summaryrefslogtreecommitdiffstats
path: root/chromium/content/browser
diff options
context:
space:
mode:
authorJocelyn Turcotte <jocelyn.turcotte@digia.com>2014-08-08 14:30:41 +0200
committerJocelyn Turcotte <jocelyn.turcotte@digia.com>2014-08-12 13:49:54 +0200
commitab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch)
tree498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/content/browser
parent4ce69f7403811819800e7c5ae1318b2647e778d1 (diff)
Update Chromium to beta version 37.0.2062.68
Change-Id: I188e3b5aff1bec75566014291b654eb19f5bc8ca Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Diffstat (limited to 'chromium/content/browser')
-rw-r--r--chromium/content/browser/BUILD.gn370
-rw-r--r--chromium/content/browser/DEPS12
-rw-r--r--chromium/content/browser/accessibility/OWNERS1
-rw-r--r--chromium/content/browser/accessibility/accessibility_mode_browsertest.cc103
-rw-r--r--chromium/content/browser/accessibility/accessibility_mode_helper.cc56
-rw-r--r--chromium/content/browser/accessibility/accessibility_mode_helper.h28
-rw-r--r--chromium/content/browser/accessibility/accessibility_mode_helper_unittest.cc53
-rw-r--r--chromium/content/browser/accessibility/accessibility_tree_formatter.cc23
-rw-r--r--chromium/content/browser/accessibility/accessibility_tree_formatter_android.cc9
-rw-r--r--chromium/content/browser/accessibility/accessibility_tree_formatter_gtk.cc109
-rw-r--r--chromium/content/browser/accessibility/accessibility_tree_formatter_mac.mm81
-rw-r--r--chromium/content/browser/accessibility/accessibility_tree_formatter_utils_win.cc10
-rw-r--r--chromium/content/browser/accessibility/accessibility_tree_formatter_win.cc17
-rw-r--r--chromium/content/browser/accessibility/accessibility_ui.cc72
-rw-r--r--chromium/content/browser/accessibility/accessibility_ui.h1
-rw-r--r--chromium/content/browser/accessibility/accessibility_win_browsertest.cc218
-rw-r--r--chromium/content/browser/accessibility/android_hit_testing_browsertest.cc81
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility.cc482
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility.h185
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_android.cc310
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_android.h8
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_cocoa.h26
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_cocoa.mm689
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_delegate_mac.h27
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_gtk.cc518
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_gtk.h92
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_mac.h14
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_mac.mm36
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_mac_unittest.mm63
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_manager.cc455
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_manager.h128
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_manager_android.cc265
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_manager_android.h34
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_manager_gtk.cc82
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_manager_gtk.h48
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_manager_mac.h13
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_manager_mac.mm120
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_manager_unittest.cc436
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_manager_win.cc297
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_manager_win.h35
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_state_impl.cc100
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_state_impl.h18
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_win.cc789
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_win.h35
-rw-r--r--chromium/content/browser/accessibility/browser_accessibility_win_unittest.cc231
-rw-r--r--chromium/content/browser/accessibility/cross_platform_accessibility_browsertest.cc380
-rw-r--r--chromium/content/browser/accessibility/dump_accessibility_tree_browsertest.cc80
-rw-r--r--chromium/content/browser/android/child_process_launcher_android.cc95
-rw-r--r--chromium/content/browser/android/child_process_launcher_android.h18
-rw-r--r--chromium/content/browser/android/content_readback_handler.cc125
-rw-r--r--chromium/content/browser/android/content_readback_handler.h62
-rw-r--r--chromium/content/browser/android/content_settings.cc2
-rw-r--r--chromium/content/browser/android/content_settings.h4
-rw-r--r--chromium/content/browser/android/content_startup_flags.cc38
-rw-r--r--chromium/content/browser/android/content_video_view.cc83
-rw-r--r--chromium/content/browser/android/content_video_view.h22
-rw-r--r--chromium/content/browser/android/content_view_core_impl.cc964
-rw-r--r--chromium/content/browser/android/content_view_core_impl.h180
-rw-r--r--chromium/content/browser/android/content_view_render_view.cc96
-rw-r--r--chromium/content/browser/android/content_view_render_view.h30
-rw-r--r--chromium/content/browser/android/content_view_statics.cc84
-rw-r--r--chromium/content/browser/android/date_time_chooser_android.cc8
-rw-r--r--chromium/content/browser/android/date_time_chooser_android.h2
-rw-r--r--chromium/content/browser/android/download_controller_android_impl.cc10
-rw-r--r--chromium/content/browser/android/download_controller_android_impl.h2
-rw-r--r--chromium/content/browser/android/edge_effect.cc130
-rw-r--r--chromium/content/browser/android/edge_effect.h9
-rw-r--r--chromium/content/browser/android/gesture_event_type.h19
-rw-r--r--chromium/content/browser/android/gesture_event_type_list.h33
-rw-r--r--chromium/content/browser/android/in_process/synchronous_compositor_factory_impl.cc267
-rw-r--r--chromium/content/browser/android/in_process/synchronous_compositor_factory_impl.h86
-rw-r--r--chromium/content/browser/android/in_process/synchronous_compositor_impl.cc360
-rw-r--r--chromium/content/browser/android/in_process/synchronous_compositor_impl.h35
-rw-r--r--chromium/content/browser/android/in_process/synchronous_compositor_output_surface.cc126
-rw-r--r--chromium/content/browser/android/in_process/synchronous_compositor_output_surface.h29
-rw-r--r--chromium/content/browser/android/in_process/synchronous_input_event_filter.cc11
-rw-r--r--chromium/content/browser/android/in_process/synchronous_input_event_filter.h5
-rw-r--r--chromium/content/browser/android/interstitial_page_delegate_android.h2
-rw-r--r--chromium/content/browser/android/load_url_params.cc2
-rw-r--r--chromium/content/browser/android/overscroll_glow.cc152
-rw-r--r--chromium/content/browser/android/overscroll_glow.h44
-rw-r--r--chromium/content/browser/android/surface_texture_peer_browser_impl.cc56
-rw-r--r--chromium/content/browser/android/surface_texture_peer_browser_impl.h2
-rw-r--r--chromium/content/browser/android/touch_point.cc123
-rw-r--r--chromium/content/browser/android/touch_point.h30
-rw-r--r--chromium/content/browser/android/tracing_controller_android.cc50
-rw-r--r--chromium/content/browser/android/tracing_controller_android.h12
-rw-r--r--chromium/content/browser/android/ui_resource_provider_impl.cc60
-rw-r--r--chromium/content/browser/android/ui_resource_provider_impl.h46
-rw-r--r--chromium/content/browser/android/web_contents_observer_android.cc67
-rw-r--r--chromium/content/browser/android/web_contents_observer_android.h9
-rw-r--r--chromium/content/browser/appcache/appcache_database_unittest.cc1216
-rw-r--r--chromium/content/browser/appcache/appcache_disk_cache_unittest.cc188
-rw-r--r--chromium/content/browser/appcache/appcache_dispatcher_host.cc16
-rw-r--r--chromium/content/browser/appcache/appcache_dispatcher_host.h5
-rw-r--r--chromium/content/browser/appcache/appcache_frontend_proxy.cc14
-rw-r--r--chromium/content/browser/appcache/appcache_frontend_proxy.h9
-rw-r--r--chromium/content/browser/appcache/appcache_group_unittest.cc309
-rw-r--r--chromium/content/browser/appcache/appcache_host_unittest.cc554
-rw-r--r--chromium/content/browser/appcache/appcache_interceptor.cc128
-rw-r--r--chromium/content/browser/appcache/appcache_interceptor.h82
-rw-r--r--chromium/content/browser/appcache/appcache_quota_client_unittest.cc437
-rw-r--r--chromium/content/browser/appcache/appcache_request_handler_unittest.cc1014
-rw-r--r--chromium/content/browser/appcache/appcache_response_unittest.cc722
-rw-r--r--chromium/content/browser/appcache/appcache_service_unittest.cc386
-rw-r--r--chromium/content/browser/appcache/appcache_storage_impl_unittest.cc2050
-rw-r--r--chromium/content/browser/appcache/appcache_storage_unittest.cc171
-rw-r--r--chromium/content/browser/appcache/appcache_unittest.cc719
-rw-r--r--chromium/content/browser/appcache/appcache_update_job_unittest.cc3720
-rw-r--r--chromium/content/browser/appcache/appcache_url_request_job_unittest.cc869
-rw-r--r--chromium/content/browser/appcache/chrome_appcache_service.cc11
-rw-r--r--chromium/content/browser/appcache/chrome_appcache_service.h13
-rw-r--r--chromium/content/browser/appcache/chrome_appcache_service_unittest.cc27
-rw-r--r--chromium/content/browser/appcache/manifest_parser_unittest.cc524
-rw-r--r--chromium/content/browser/appcache/mock_appcache_policy.cc28
-rw-r--r--chromium/content/browser/appcache/mock_appcache_policy.h31
-rw-r--r--chromium/content/browser/appcache/mock_appcache_service.cc27
-rw-r--r--chromium/content/browser/appcache/mock_appcache_service.h50
-rw-r--r--chromium/content/browser/appcache/mock_appcache_storage.cc551
-rw-r--r--chromium/content/browser/appcache/mock_appcache_storage.h254
-rw-r--r--chromium/content/browser/appcache/mock_appcache_storage_unittest.cc646
-rw-r--r--chromium/content/browser/appcache/view_appcache_internals_job.cc681
-rw-r--r--chromium/content/browser/appcache/view_appcache_internals_job.h35
-rw-r--r--chromium/content/browser/aura/image_transport_factory.cc49
-rw-r--r--chromium/content/browser/aura/image_transport_factory_browsertest.cc69
-rw-r--r--chromium/content/browser/aura/no_transport_image_transport_factory.cc96
-rw-r--r--chromium/content/browser/aura/owned_mailbox.h41
-rw-r--r--chromium/content/browser/aura/reflector_impl.cc182
-rw-r--r--chromium/content/browser/aura/software_output_device_ozone.cc73
-rw-r--r--chromium/content/browser/aura/software_output_device_x11.cc87
-rw-r--r--chromium/content/browser/battery_status/OWNERS1
-rw-r--r--chromium/content/browser/battery_status/battery_status_browsertest.cc167
-rw-r--r--chromium/content/browser/battery_status/battery_status_manager.h57
-rw-r--r--chromium/content/browser/battery_status/battery_status_manager_android.cc56
-rw-r--r--chromium/content/browser/battery_status/battery_status_manager_default.cc31
-rw-r--r--chromium/content/browser/battery_status/battery_status_message_filter.cc56
-rw-r--r--chromium/content/browser/battery_status/battery_status_message_filter.h36
-rw-r--r--chromium/content/browser/battery_status/battery_status_service.cc106
-rw-r--r--chromium/content/browser/battery_status/battery_status_service.h66
-rw-r--r--chromium/content/browser/battery_status/battery_status_service_unittest.cc193
-rw-r--r--chromium/content/browser/bookmarklet_browsertest.cc6
-rw-r--r--chromium/content/browser/bootstrap_sandbox_mac.cc149
-rw-r--r--chromium/content/browser/bootstrap_sandbox_mac.h25
-rw-r--r--chromium/content/browser/browser.gni15
-rw-r--r--chromium/content/browser/browser_child_process_host_impl.cc37
-rw-r--r--chromium/content/browser/browser_child_process_host_impl.h32
-rw-r--r--chromium/content/browser/browser_context.cc48
-rw-r--r--chromium/content/browser/browser_main_loop.cc311
-rw-r--r--chromium/content/browser/browser_main_loop.h28
-rw-r--r--chromium/content/browser/browser_main_runner.cc21
-rw-r--r--chromium/content/browser/browser_plugin/browser_plugin_embedder.cc212
-rw-r--r--chromium/content/browser/browser_plugin/browser_plugin_embedder.h69
-rw-r--r--chromium/content/browser/browser_plugin/browser_plugin_geolocation_permission_context.cc94
-rw-r--r--chromium/content/browser/browser_plugin/browser_plugin_geolocation_permission_context.h44
-rw-r--r--chromium/content/browser/browser_plugin/browser_plugin_guest.cc1312
-rw-r--r--chromium/content/browser/browser_plugin/browser_plugin_guest.h371
-rw-r--r--chromium/content/browser/browser_plugin/browser_plugin_guest_manager.cc244
-rw-r--r--chromium/content/browser/browser_plugin/browser_plugin_guest_manager.h138
-rw-r--r--chromium/content/browser/browser_plugin/browser_plugin_host_browsertest.cc1039
-rw-r--r--chromium/content/browser/browser_plugin/browser_plugin_host_factory.h41
-rw-r--r--chromium/content/browser/browser_plugin/browser_plugin_message_filter.cc69
-rw-r--r--chromium/content/browser/browser_plugin/browser_plugin_message_filter.h13
-rw-r--r--chromium/content/browser/browser_plugin/test_browser_plugin_embedder.cc40
-rw-r--r--chromium/content/browser/browser_plugin/test_browser_plugin_embedder.h47
-rw-r--r--chromium/content/browser/browser_plugin/test_browser_plugin_guest.cc254
-rw-r--r--chromium/content/browser/browser_plugin/test_browser_plugin_guest.h115
-rw-r--r--chromium/content/browser/browser_plugin/test_browser_plugin_guest_delegate.cc68
-rw-r--r--chromium/content/browser/browser_plugin/test_browser_plugin_guest_delegate.h52
-rw-r--r--chromium/content/browser/browser_plugin/test_browser_plugin_guest_manager.cc35
-rw-r--r--chromium/content/browser/browser_plugin/test_browser_plugin_guest_manager.h51
-rw-r--r--chromium/content/browser/browser_thread_impl.cc130
-rw-r--r--chromium/content/browser/browser_url_handler_impl.cc42
-rw-r--r--chromium/content/browser/browsing_instance.cc4
-rw-r--r--chromium/content/browser/browsing_instance.h15
-rw-r--r--chromium/content/browser/byte_stream.cc6
-rw-r--r--chromium/content/browser/child_process_launcher.cc123
-rw-r--r--chromium/content/browser/child_process_launcher.h12
-rw-r--r--chromium/content/browser/child_process_security_policy_browsertest.cc4
-rw-r--r--chromium/content/browser/child_process_security_policy_impl.cc49
-rw-r--r--chromium/content/browser/child_process_security_policy_impl.h7
-rw-r--r--chromium/content/browser/child_process_security_policy_unittest.cc149
-rw-r--r--chromium/content/browser/compositor/OWNERS (renamed from chromium/content/browser/aura/OWNERS)0
-rw-r--r--chromium/content/browser/compositor/browser_compositor_output_surface.cc (renamed from chromium/content/browser/aura/browser_compositor_output_surface.cc)69
-rw-r--r--chromium/content/browser/compositor/browser_compositor_output_surface.h (renamed from chromium/content/browser/aura/browser_compositor_output_surface.h)38
-rw-r--r--chromium/content/browser/compositor/browser_compositor_output_surface_proxy.cc (renamed from chromium/content/browser/aura/browser_compositor_output_surface_proxy.cc)8
-rw-r--r--chromium/content/browser/compositor/browser_compositor_output_surface_proxy.h (renamed from chromium/content/browser/aura/browser_compositor_output_surface_proxy.h)8
-rw-r--r--chromium/content/browser/compositor/browser_compositor_view_mac.h64
-rw-r--r--chromium/content/browser/compositor/browser_compositor_view_mac.mm256
-rw-r--r--chromium/content/browser/compositor/delegated_frame_host.cc852
-rw-r--r--chromium/content/browser/compositor/delegated_frame_host.h285
-rw-r--r--chromium/content/browser/compositor/gpu_browser_compositor_output_surface.cc (renamed from chromium/content/browser/aura/gpu_browser_compositor_output_surface.cc)27
-rw-r--r--chromium/content/browser/compositor/gpu_browser_compositor_output_surface.h (renamed from chromium/content/browser/aura/gpu_browser_compositor_output_surface.h)22
-rw-r--r--chromium/content/browser/compositor/gpu_process_transport_factory.cc (renamed from chromium/content/browser/aura/gpu_process_transport_factory.cc)394
-rw-r--r--chromium/content/browser/compositor/gpu_process_transport_factory.h (renamed from chromium/content/browser/aura/gpu_process_transport_factory.h)42
-rw-r--r--chromium/content/browser/compositor/image_transport_factory.cc62
-rw-r--r--chromium/content/browser/compositor/image_transport_factory.h (renamed from chromium/content/browser/aura/image_transport_factory.h)39
-rw-r--r--chromium/content/browser/compositor/image_transport_factory_browsertest.cc110
-rw-r--r--chromium/content/browser/compositor/no_transport_image_transport_factory.cc53
-rw-r--r--chromium/content/browser/compositor/no_transport_image_transport_factory.h (renamed from chromium/content/browser/aura/no_transport_image_transport_factory.h)29
-rw-r--r--chromium/content/browser/compositor/onscreen_display_client.cc31
-rw-r--r--chromium/content/browser/compositor/onscreen_display_client.h46
-rw-r--r--chromium/content/browser/compositor/overlay_candidate_validator_ozone.cc57
-rw-r--r--chromium/content/browser/compositor/overlay_candidate_validator_ozone.h39
-rw-r--r--chromium/content/browser/compositor/owned_mailbox.cc (renamed from chromium/content/browser/aura/owned_mailbox.cc)32
-rw-r--r--chromium/content/browser/compositor/owned_mailbox.h49
-rw-r--r--chromium/content/browser/compositor/reflector_impl.cc245
-rw-r--r--chromium/content/browser/compositor/reflector_impl.h (renamed from chromium/content/browser/aura/reflector_impl.h)99
-rw-r--r--chromium/content/browser/compositor/resize_lock.cc (renamed from chromium/content/browser/aura/resize_lock.cc)4
-rw-r--r--chromium/content/browser/compositor/resize_lock.h (renamed from chromium/content/browser/aura/resize_lock.h)8
-rw-r--r--chromium/content/browser/compositor/software_browser_compositor_output_surface.cc (renamed from chromium/content/browser/aura/software_browser_compositor_output_surface.cc)22
-rw-r--r--chromium/content/browser/compositor/software_browser_compositor_output_surface.h (renamed from chromium/content/browser/aura/software_browser_compositor_output_surface.h)22
-rw-r--r--chromium/content/browser/compositor/software_browser_compositor_output_surface_unittest.cc (renamed from chromium/content/browser/aura/software_browser_compositor_output_surface_unittest.cc)25
-rw-r--r--chromium/content/browser/compositor/software_output_device_mac.h35
-rw-r--r--chromium/content/browser/compositor/software_output_device_mac.mm30
-rw-r--r--chromium/content/browser/compositor/software_output_device_ozone.cc60
-rw-r--r--chromium/content/browser/compositor/software_output_device_ozone.h (renamed from chromium/content/browser/aura/software_output_device_ozone.h)16
-rw-r--r--chromium/content/browser/compositor/software_output_device_ozone_unittest.cc (renamed from chromium/content/browser/aura/software_output_device_ozone_unittest.cc)134
-rw-r--r--chromium/content/browser/compositor/software_output_device_win.cc (renamed from chromium/content/browser/aura/software_output_device_win.cc)32
-rw-r--r--chromium/content/browser/compositor/software_output_device_win.h (renamed from chromium/content/browser/aura/software_output_device_win.h)17
-rw-r--r--chromium/content/browser/compositor/software_output_device_x11.cc138
-rw-r--r--chromium/content/browser/compositor/software_output_device_x11.h (renamed from chromium/content/browser/aura/software_output_device_x11.h)18
-rw-r--r--chromium/content/browser/compositor/surface_display_output_surface.cc49
-rw-r--r--chromium/content/browser/compositor/surface_display_output_surface.h41
-rw-r--r--chromium/content/browser/context_factory.cc16
-rw-r--r--chromium/content/browser/cross_site_transfer_browsertest.cc466
-rw-r--r--chromium/content/browser/database_browsertest.cc4
-rw-r--r--chromium/content/browser/database_quota_client_unittest.cc289
-rw-r--r--chromium/content/browser/database_tracker_unittest.cc875
-rw-r--r--chromium/content/browser/database_util_unittest.cc79
-rw-r--r--chromium/content/browser/databases_table_unittest.cc152
-rw-r--r--chromium/content/browser/device_monitor_mac.h12
-rw-r--r--chromium/content/browser/device_monitor_mac.mm246
-rw-r--r--chromium/content/browser/device_orientation/device_data.h43
-rw-r--r--chromium/content/browser/device_orientation/device_inertial_sensor_browsertest.cc186
-rw-r--r--chromium/content/browser/device_sensors/DEPS (renamed from chromium/content/browser/device_orientation/DEPS)0
-rw-r--r--chromium/content/browser/device_sensors/OWNERS (renamed from chromium/content/browser/device_orientation/OWNERS)0
-rw-r--r--chromium/content/browser/device_sensors/data_fetcher_shared_memory.h (renamed from chromium/content/browser/device_orientation/data_fetcher_shared_memory.h)14
-rw-r--r--chromium/content/browser/device_sensors/data_fetcher_shared_memory_android.cc (renamed from chromium/content/browser/device_orientation/data_fetcher_shared_memory_android.cc)19
-rw-r--r--chromium/content/browser/device_sensors/data_fetcher_shared_memory_base.cc (renamed from chromium/content/browser/device_orientation/data_fetcher_shared_memory_base.cc)13
-rw-r--r--chromium/content/browser/device_sensors/data_fetcher_shared_memory_base.h (renamed from chromium/content/browser/device_orientation/data_fetcher_shared_memory_base.h)13
-rw-r--r--chromium/content/browser/device_sensors/data_fetcher_shared_memory_base_unittest.cc (renamed from chromium/content/browser/device_orientation/data_fetcher_shared_memory_base_unittest.cc)11
-rw-r--r--chromium/content/browser/device_sensors/data_fetcher_shared_memory_default.cc (renamed from chromium/content/browser/device_orientation/data_fetcher_shared_memory_default.cc)5
-rw-r--r--chromium/content/browser/device_sensors/data_fetcher_shared_memory_mac.cc (renamed from chromium/content/browser/device_orientation/data_fetcher_shared_memory_mac.cc)39
-rw-r--r--chromium/content/browser/device_sensors/data_fetcher_shared_memory_win.cc (renamed from chromium/content/browser/device_orientation/data_fetcher_shared_memory_win.cc)4
-rw-r--r--chromium/content/browser/device_sensors/device_inertial_sensor_browsertest.cc289
-rw-r--r--chromium/content/browser/device_sensors/device_inertial_sensor_service.cc (renamed from chromium/content/browser/device_orientation/device_inertial_sensor_service.cc)13
-rw-r--r--chromium/content/browser/device_sensors/device_inertial_sensor_service.h (renamed from chromium/content/browser/device_orientation/device_inertial_sensor_service.h)19
-rw-r--r--chromium/content/browser/device_sensors/device_motion_message_filter.cc (renamed from chromium/content/browser/device_orientation/device_motion_message_filter.cc)21
-rw-r--r--chromium/content/browser/device_sensors/device_motion_message_filter.h (renamed from chromium/content/browser/device_orientation/device_motion_message_filter.h)15
-rw-r--r--chromium/content/browser/device_sensors/device_orientation_message_filter.cc (renamed from chromium/content/browser/device_orientation/device_orientation_message_filter.cc)20
-rw-r--r--chromium/content/browser/device_sensors/device_orientation_message_filter.h (renamed from chromium/content/browser/device_orientation/device_orientation_message_filter.h)14
-rw-r--r--chromium/content/browser/device_sensors/inertial_sensor_consts.h (renamed from chromium/content/browser/device_orientation/inertial_sensor_consts.h)8
-rw-r--r--chromium/content/browser/device_sensors/sensor_manager_android.cc (renamed from chromium/content/browser/device_orientation/data_fetcher_impl_android.cc)68
-rw-r--r--chromium/content/browser/device_sensors/sensor_manager_android.h (renamed from chromium/content/browser/device_orientation/data_fetcher_impl_android.h)53
-rw-r--r--chromium/content/browser/device_sensors/sensor_manager_android_unittest.cc (renamed from chromium/content/browser/device_orientation/data_fetcher_impl_android_unittest.cc)89
-rw-r--r--chromium/content/browser/devtools/BUILD.gn71
-rw-r--r--chromium/content/browser/devtools/browser_protocol.json3
-rw-r--r--chromium/content/browser/devtools/devtools_agent_host_impl.cc12
-rw-r--r--chromium/content/browser/devtools/devtools_agent_host_impl.h2
-rw-r--r--chromium/content/browser/devtools/devtools_browser_target.cc53
-rw-r--r--chromium/content/browser/devtools/devtools_browser_target.h8
-rw-r--r--chromium/content/browser/devtools/devtools_external_agent_proxy_impl.cc73
-rw-r--r--chromium/content/browser/devtools/devtools_external_agent_proxy_impl.h35
-rw-r--r--chromium/content/browser/devtools/devtools_http_handler_impl.cc112
-rw-r--r--chromium/content/browser/devtools/devtools_http_handler_impl.h11
-rw-r--r--chromium/content/browser/devtools/devtools_http_handler_unittest.cc50
-rw-r--r--chromium/content/browser/devtools/devtools_manager_impl.cc13
-rw-r--r--chromium/content/browser/devtools/devtools_manager_impl.h9
-rw-r--r--chromium/content/browser/devtools/devtools_manager_unittest.cc10
-rw-r--r--chromium/content/browser/devtools/devtools_netlog_observer.cc134
-rw-r--r--chromium/content/browser/devtools/devtools_netlog_observer.h13
-rw-r--r--chromium/content/browser/devtools/devtools_power_handler.cc83
-rw-r--r--chromium/content/browser/devtools/devtools_power_handler.h38
-rw-r--r--chromium/content/browser/devtools/devtools_protocol.cc43
-rw-r--r--chromium/content/browser/devtools/devtools_protocol.h18
-rw-r--r--chromium/content/browser/devtools/devtools_protocol_constants.cc293
-rw-r--r--chromium/content/browser/devtools/devtools_protocol_constants.h296
-rw-r--r--chromium/content/browser/devtools/devtools_resources.gyp40
-rw-r--r--chromium/content/browser/devtools/devtools_system_info_handler.cc7
-rw-r--r--chromium/content/browser/devtools/devtools_tracing_handler.cc103
-rw-r--r--chromium/content/browser/devtools/devtools_tracing_handler.h23
-rw-r--r--chromium/content/browser/devtools/embedded_worker_devtools_manager.cc379
-rw-r--r--chromium/content/browser/devtools/embedded_worker_devtools_manager.h138
-rw-r--r--chromium/content/browser/devtools/embedded_worker_devtools_manager_unittest.cc272
-rw-r--r--chromium/content/browser/devtools/forwarding_agent_host.cc41
-rw-r--r--chromium/content/browser/devtools/forwarding_agent_host.h39
-rw-r--r--chromium/content/browser/devtools/ipc_devtools_agent_host.cc7
-rw-r--r--chromium/content/browser/devtools/render_view_devtools_agent_host.cc138
-rw-r--r--chromium/content/browser/devtools/render_view_devtools_agent_host.h16
-rw-r--r--chromium/content/browser/devtools/renderer_overrides_handler.cc168
-rw-r--r--chromium/content/browser/devtools/renderer_overrides_handler.h12
-rw-r--r--chromium/content/browser/devtools/renderer_overrides_handler_browsertest.cc11
-rw-r--r--chromium/content/browser/devtools/tethering_handler.cc2
-rw-r--r--chromium/content/browser/devtools/worker_devtools_manager.cc38
-rw-r--r--chromium/content/browser/devtools/worker_devtools_manager.h12
-rw-r--r--chromium/content/browser/devtools/worker_devtools_message_filter.cc11
-rw-r--r--chromium/content/browser/devtools/worker_devtools_message_filter.h3
-rw-r--r--chromium/content/browser/dom_storage/dom_storage_area.cc5
-rw-r--r--chromium/content/browser/dom_storage/dom_storage_area_unittest.cc3
-rw-r--r--chromium/content/browser/dom_storage/dom_storage_browsertest.cc4
-rw-r--r--chromium/content/browser/dom_storage/dom_storage_context_impl.cc11
-rw-r--r--chromium/content/browser/dom_storage/dom_storage_context_impl.h1
-rw-r--r--chromium/content/browser/dom_storage/dom_storage_context_impl_unittest.cc9
-rw-r--r--chromium/content/browser/dom_storage/dom_storage_context_wrapper.cc8
-rw-r--r--chromium/content/browser/dom_storage/dom_storage_context_wrapper.h3
-rw-r--r--chromium/content/browser/dom_storage/dom_storage_database.cc4
-rw-r--r--chromium/content/browser/dom_storage/dom_storage_database_unittest.cc4
-rw-r--r--chromium/content/browser/dom_storage/dom_storage_host.cc24
-rw-r--r--chromium/content/browser/dom_storage/dom_storage_host.h2
-rw-r--r--chromium/content/browser/dom_storage/dom_storage_message_filter.cc14
-rw-r--r--chromium/content/browser/dom_storage/dom_storage_message_filter.h5
-rw-r--r--chromium/content/browser/dom_storage/dom_storage_namespace.cc8
-rw-r--r--chromium/content/browser/dom_storage/session_storage_database.cc76
-rw-r--r--chromium/content/browser/dom_storage/session_storage_database.h11
-rw-r--r--chromium/content/browser/dom_storage/session_storage_database_unittest.cc21
-rw-r--r--chromium/content/browser/download/base_file.cc111
-rw-r--r--chromium/content/browser/download/base_file.h38
-rw-r--r--chromium/content/browser/download/base_file_posix.cc2
-rw-r--r--chromium/content/browser/download/base_file_unittest.cc92
-rw-r--r--chromium/content/browser/download/download_browsertest.cc225
-rw-r--r--chromium/content/browser/download/download_create_info.h6
-rw-r--r--chromium/content/browser/download/download_file_factory.cc8
-rw-r--r--chromium/content/browser/download/download_file_impl.cc11
-rw-r--r--chromium/content/browser/download/download_file_impl.h5
-rw-r--r--chromium/content/browser/download/download_file_unittest.cc10
-rw-r--r--chromium/content/browser/download/download_interrupt_reasons_impl.cc2
-rw-r--r--chromium/content/browser/download/download_item_factory.h2
-rw-r--r--chromium/content/browser/download/download_item_impl.cc94
-rw-r--r--chromium/content/browser/download/download_item_impl.h17
-rw-r--r--chromium/content/browser/download/download_item_impl_unittest.cc31
-rw-r--r--chromium/content/browser/download/download_manager_impl.cc16
-rw-r--r--chromium/content/browser/download/download_manager_impl.h3
-rw-r--r--chromium/content/browser/download/download_manager_impl_unittest.cc23
-rw-r--r--chromium/content/browser/download/download_net_log_parameters.cc6
-rw-r--r--chromium/content/browser/download/download_resource_handler.cc117
-rw-r--r--chromium/content/browser/download/download_resource_handler.h53
-rw-r--r--chromium/content/browser/download/drag_download_file.cc19
-rw-r--r--chromium/content/browser/download/drag_download_file.h14
-rw-r--r--chromium/content/browser/download/drag_download_file_browsertest.cc9
-rw-r--r--chromium/content/browser/download/drag_download_util.cc29
-rw-r--r--chromium/content/browser/download/drag_download_util.h11
-rw-r--r--chromium/content/browser/download/mhtml_generation_browsertest.cc4
-rw-r--r--chromium/content/browser/download/mhtml_generation_manager.cc168
-rw-r--r--chromium/content/browser/download/mhtml_generation_manager.h49
-rw-r--r--chromium/content/browser/download/save_file.cc3
-rw-r--r--chromium/content/browser/download/save_file_manager.cc7
-rw-r--r--chromium/content/browser/download/save_file_resource_handler.cc38
-rw-r--r--chromium/content/browser/download/save_file_resource_handler.h35
-rw-r--r--chromium/content/browser/download/save_package.cc14
-rw-r--r--chromium/content/browser/download/save_package_browsertest.cc4
-rw-r--r--chromium/content/browser/download/save_package_unittest.cc14
-rw-r--r--chromium/content/browser/fileapi/DEPS6
-rw-r--r--chromium/content/browser/fileapi/blob_storage_context_unittest.cc237
-rw-r--r--chromium/content/browser/fileapi/blob_storage_host.cc117
-rw-r--r--chromium/content/browser/fileapi/blob_storage_host.h76
-rw-r--r--chromium/content/browser/fileapi/blob_url_request_job_unittest.cc166
-rw-r--r--chromium/content/browser/fileapi/browser_file_system_helper.cc16
-rw-r--r--chromium/content/browser/fileapi/chrome_blob_storage_context.cc42
-rw-r--r--chromium/content/browser/fileapi/chrome_blob_storage_context.h5
-rw-r--r--chromium/content/browser/fileapi/copy_or_move_file_validator_unittest.cc119
-rw-r--r--chromium/content/browser/fileapi/copy_or_move_operation_delegate_unittest.cc317
-rw-r--r--chromium/content/browser/fileapi/dragged_file_util_unittest.cc139
-rw-r--r--chromium/content/browser/fileapi/external_mount_points_unittest.cc515
-rw-r--r--chromium/content/browser/fileapi/file_system_browsertest.cc4
-rw-r--r--chromium/content/browser/fileapi/file_system_context_unittest.cc112
-rw-r--r--chromium/content/browser/fileapi/file_system_dir_url_request_job_unittest.cc237
-rw-r--r--chromium/content/browser/fileapi/file_system_file_stream_reader_unittest.cc35
-rw-r--r--chromium/content/browser/fileapi/file_system_operation_impl_unittest.cc268
-rw-r--r--chromium/content/browser/fileapi/file_system_operation_impl_write_unittest.cc90
-rw-r--r--chromium/content/browser/fileapi/file_system_operation_runner_unittest.cc48
-rw-r--r--chromium/content/browser/fileapi/file_system_quota_client_unittest.cc51
-rw-r--r--chromium/content/browser/fileapi/file_system_url_request_job_unittest.cc198
-rw-r--r--chromium/content/browser/fileapi/file_system_url_unittest.cc221
-rw-r--r--chromium/content/browser/fileapi/file_system_usage_cache_unittest.cc160
-rw-r--r--chromium/content/browser/fileapi/file_writer_delegate_unittest.cc128
-rw-r--r--chromium/content/browser/fileapi/fileapi_message_filter.cc156
-rw-r--r--chromium/content/browser/fileapi/fileapi_message_filter.h44
-rw-r--r--chromium/content/browser/fileapi/fileapi_message_filter_unittest.cc37
-rw-r--r--chromium/content/browser/fileapi/isolated_context_unittest.cc360
-rw-r--r--chromium/content/browser/fileapi/local_file_stream_reader_unittest.cc258
-rw-r--r--chromium/content/browser/fileapi/local_file_stream_writer_unittest.cc180
-rw-r--r--chromium/content/browser/fileapi/local_file_util_unittest.cc147
-rw-r--r--chromium/content/browser/fileapi/mock_file_change_observer.cc51
-rw-r--r--chromium/content/browser/fileapi/mock_file_change_observer.h103
-rw-r--r--chromium/content/browser/fileapi/mock_url_request_delegate.cc72
-rw-r--r--chromium/content/browser/fileapi/mock_url_request_delegate.h38
-rw-r--r--chromium/content/browser/fileapi/native_file_util_unittest.cc409
-rw-r--r--chromium/content/browser/fileapi/obfuscated_file_util_unittest.cc1212
-rw-r--r--chromium/content/browser/fileapi/plugin_private_file_system_backend_unittest.cc58
-rw-r--r--chromium/content/browser/fileapi/recursive_operation_delegate_unittest.cc51
-rw-r--r--chromium/content/browser/fileapi/sandbox_database_test_helper.cc93
-rw-r--r--chromium/content/browser/fileapi/sandbox_database_test_helper.h28
-rw-r--r--chromium/content/browser/fileapi/sandbox_directory_database_unittest.cc675
-rw-r--r--chromium/content/browser/fileapi/sandbox_file_system_backend_delegate_unittest.cc47
-rw-r--r--chromium/content/browser/fileapi/sandbox_file_system_backend_unittest.cc59
-rw-r--r--chromium/content/browser/fileapi/sandbox_isolated_origin_database_unittest.cc43
-rw-r--r--chromium/content/browser/fileapi/sandbox_origin_database_unittest.cc305
-rw-r--r--chromium/content/browser/fileapi/sandbox_prioritized_origin_database_unittest.cc217
-rw-r--r--chromium/content/browser/fileapi/timed_task_helper_unittest.cc85
-rw-r--r--chromium/content/browser/fileapi/transient_file_util_unittest.cc39
-rw-r--r--chromium/content/browser/fileapi/upload_file_system_file_element_reader.cc117
-rw-r--r--chromium/content/browser/fileapi/upload_file_system_file_element_reader.h66
-rw-r--r--chromium/content/browser/fileapi/upload_file_system_file_element_reader_unittest.cc27
-rw-r--r--chromium/content/browser/frame_host/OWNERS2
-rw-r--r--chromium/content/browser/frame_host/cross_process_frame_connector.cc184
-rw-r--r--chromium/content/browser/frame_host/cross_process_frame_connector.h123
-rw-r--r--chromium/content/browser/frame_host/cross_site_transferring_request.cc46
-rw-r--r--chromium/content/browser/frame_host/cross_site_transferring_request.h40
-rw-r--r--chromium/content/browser/frame_host/debug_urls.cc92
-rw-r--r--chromium/content/browser/frame_host/debug_urls.h5
-rw-r--r--chromium/content/browser/frame_host/frame_tree.cc250
-rw-r--r--chromium/content/browser/frame_host/frame_tree.h117
-rw-r--r--chromium/content/browser/frame_host/frame_tree_browsertest.cc158
-rw-r--r--chromium/content/browser/frame_host/frame_tree_node.cc68
-rw-r--r--chromium/content/browser/frame_host/frame_tree_node.h88
-rw-r--r--chromium/content/browser/frame_host/frame_tree_unittest.cc273
-rw-r--r--chromium/content/browser/frame_host/interstitial_page_impl.cc224
-rw-r--r--chromium/content/browser/frame_host/interstitial_page_impl.h43
-rw-r--r--chromium/content/browser/frame_host/interstitial_page_navigator_impl.cc18
-rw-r--r--chromium/content/browser/frame_host/interstitial_page_navigator_impl.h13
-rw-r--r--chromium/content/browser/frame_host/navigation_controller_delegate.h10
-rw-r--r--chromium/content/browser/frame_host/navigation_controller_impl.cc173
-rw-r--r--chromium/content/browser/frame_host/navigation_controller_impl.h48
-rw-r--r--chromium/content/browser/frame_host/navigation_controller_impl_browsertest.cc34
-rw-r--r--chromium/content/browser/frame_host/navigation_controller_impl_unittest.cc769
-rw-r--r--chromium/content/browser/frame_host/navigation_entry_impl.cc15
-rw-r--r--chromium/content/browser/frame_host/navigation_entry_impl.h16
-rw-r--r--chromium/content/browser/frame_host/navigation_entry_impl_unittest.cc5
-rw-r--r--chromium/content/browser/frame_host/navigation_entry_screenshot_manager.cc36
-rw-r--r--chromium/content/browser/frame_host/navigator.cc25
-rw-r--r--chromium/content/browser/frame_host/navigator.h93
-rw-r--r--chromium/content/browser/frame_host/navigator_delegate.cc17
-rw-r--r--chromium/content/browser/frame_host/navigator_delegate.h83
-rw-r--r--chromium/content/browser/frame_host/navigator_impl.cc562
-rw-r--r--chromium/content/browser/frame_host/navigator_impl.h57
-rw-r--r--chromium/content/browser/frame_host/render_frame_host_delegate.cc18
-rw-r--r--chromium/content/browser/frame_host/render_frame_host_delegate.h76
-rw-r--r--chromium/content/browser/frame_host/render_frame_host_impl.cc837
-rw-r--r--chromium/content/browser/frame_host/render_frame_host_impl.h229
-rw-r--r--chromium/content/browser/frame_host/render_frame_host_manager.cc1108
-rw-r--r--chromium/content/browser/frame_host/render_frame_host_manager.h293
-rw-r--r--chromium/content/browser/frame_host/render_frame_host_manager_browsertest.cc472
-rw-r--r--chromium/content/browser/frame_host/render_frame_host_manager_unittest.cc1044
-rw-r--r--chromium/content/browser/frame_host/render_frame_message_filter.cc36
-rw-r--r--chromium/content/browser/frame_host/render_frame_message_filter.h7
-rw-r--r--chromium/content/browser/frame_host/render_frame_proxy_host.cc85
-rw-r--r--chromium/content/browser/frame_host/render_frame_proxy_host.h124
-rw-r--r--chromium/content/browser/frame_host/render_widget_host_view_child_frame.cc339
-rw-r--r--chromium/content/browser/frame_host/render_widget_host_view_child_frame.h174
-rw-r--r--chromium/content/browser/frame_host/render_widget_host_view_child_frame_unittest.cc80
-rw-r--r--chromium/content/browser/frame_host/render_widget_host_view_guest.cc (renamed from chromium/content/browser/renderer_host/render_widget_host_view_guest.cc)371
-rw-r--r--chromium/content/browser/frame_host/render_widget_host_view_guest.h (renamed from chromium/content/browser/renderer_host/render_widget_host_view_guest.h)124
-rw-r--r--chromium/content/browser/frame_host/render_widget_host_view_guest_unittest.cc (renamed from chromium/content/browser/renderer_host/render_widget_host_view_guest_unittest.cc)4
-rw-r--r--chromium/content/browser/gamepad/OWNERS1
-rw-r--r--chromium/content/browser/gamepad/canonical_axis_index_list.h16
-rw-r--r--chromium/content/browser/gamepad/canonical_button_index_list.h28
-rw-r--r--chromium/content/browser/gamepad/gamepad_consumer.h29
-rw-r--r--chromium/content/browser/gamepad/gamepad_platform_data_fetcher.h10
-rw-r--r--chromium/content/browser/gamepad/gamepad_platform_data_fetcher_android.cc149
-rw-r--r--chromium/content/browser/gamepad/gamepad_platform_data_fetcher_android.h40
-rw-r--r--chromium/content/browser/gamepad/gamepad_platform_data_fetcher_linux.cc16
-rw-r--r--chromium/content/browser/gamepad/gamepad_platform_data_fetcher_mac.h2
-rw-r--r--chromium/content/browser/gamepad/gamepad_platform_data_fetcher_mac.mm105
-rw-r--r--chromium/content/browser/gamepad/gamepad_platform_data_fetcher_win.cc340
-rw-r--r--chromium/content/browser/gamepad/gamepad_platform_data_fetcher_win.h28
-rw-r--r--chromium/content/browser/gamepad/gamepad_provider.cc135
-rw-r--r--chromium/content/browser/gamepad/gamepad_provider.h41
-rw-r--r--chromium/content/browser/gamepad/gamepad_provider_unittest.cc12
-rw-r--r--chromium/content/browser/gamepad/gamepad_service.cc100
-rw-r--r--chromium/content/browser/gamepad/gamepad_service.h58
-rw-r--r--chromium/content/browser/gamepad/gamepad_standard_mappings.cc60
-rw-r--r--chromium/content/browser/gamepad/gamepad_standard_mappings.h16
-rw-r--r--chromium/content/browser/gamepad/gamepad_standard_mappings_linux.cc72
-rw-r--r--chromium/content/browser/gamepad/gamepad_standard_mappings_mac.mm106
-rw-r--r--chromium/content/browser/gamepad/gamepad_standard_mappings_win.cc148
-rw-r--r--chromium/content/browser/gamepad/raw_input_data_fetcher_win.cc505
-rw-r--r--chromium/content/browser/gamepad/raw_input_data_fetcher_win.h139
-rw-r--r--chromium/content/browser/gamepad/xbox_data_fetcher_mac.cc278
-rw-r--r--chromium/content/browser/gamepad/xbox_data_fetcher_mac.h27
-rw-r--r--chromium/content/browser/geolocation/geolocation_dispatcher_host.cc397
-rw-r--r--chromium/content/browser/geolocation/geolocation_dispatcher_host.h89
-rw-r--r--chromium/content/browser/geolocation/geolocation_provider_impl.cc128
-rw-r--r--chromium/content/browser/geolocation/geolocation_provider_impl.h37
-rw-r--r--chromium/content/browser/geolocation/geolocation_provider_unittest.cc35
-rw-r--r--chromium/content/browser/geolocation/location_api_adapter_android.h2
-rw-r--r--chromium/content/browser/geolocation/location_arbitrator_impl.h2
-rw-r--r--chromium/content/browser/geolocation/location_provider_base.h2
-rw-r--r--chromium/content/browser/geolocation/network_location_provider.cc2
-rw-r--r--chromium/content/browser/geolocation/network_location_provider_unittest.cc10
-rw-r--r--chromium/content/browser/geolocation/network_location_request.cc2
-rw-r--r--chromium/content/browser/geolocation/wifi_data_provider_chromeos.cc4
-rw-r--r--chromium/content/browser/geolocation/wifi_data_provider_chromeos_unittest.cc3
-rw-r--r--chromium/content/browser/geolocation/wifi_data_provider_common.cc14
-rw-r--r--chromium/content/browser/geolocation/wifi_data_provider_common_unittest.cc4
-rw-r--r--chromium/content/browser/geolocation/wifi_data_provider_common_win.cc6
-rw-r--r--chromium/content/browser/geolocation/wifi_data_provider_linux.cc6
-rw-r--r--chromium/content/browser/geolocation/wifi_data_provider_linux_unittest.cc5
-rw-r--r--chromium/content/browser/geolocation/wifi_data_provider_mac.cc7
-rw-r--r--chromium/content/browser/geolocation/wifi_data_provider_win.cc18
-rw-r--r--chromium/content/browser/gpu/browser_gpu_channel_host_factory.cc252
-rw-r--r--chromium/content/browser/gpu/browser_gpu_channel_host_factory.h99
-rw-r--r--chromium/content/browser/gpu/compositor_util.cc339
-rw-r--r--chromium/content/browser/gpu/compositor_util.h21
-rw-r--r--chromium/content/browser/gpu/compositor_util_browsertest.cc8
-rw-r--r--chromium/content/browser/gpu/gpu_data_manager_impl.cc18
-rw-r--r--chromium/content/browser/gpu/gpu_data_manager_impl.h24
-rw-r--r--chromium/content/browser/gpu/gpu_data_manager_impl_private.cc322
-rw-r--r--chromium/content/browser/gpu/gpu_data_manager_impl_private.h22
-rw-r--r--chromium/content/browser/gpu/gpu_data_manager_impl_private_unittest.cc128
-rw-r--r--chromium/content/browser/gpu/gpu_internals_ui.cc37
-rw-r--r--chromium/content/browser/gpu/gpu_ipc_browsertests.cc80
-rw-r--r--chromium/content/browser/gpu/gpu_process_host.cc377
-rw-r--r--chromium/content/browser/gpu/gpu_process_host.h39
-rw-r--r--chromium/content/browser/gpu/gpu_process_host_ui_shim.cc119
-rw-r--r--chromium/content/browser/gpu/gpu_process_host_ui_shim.h5
-rw-r--r--chromium/content/browser/gpu/gpu_surface_tracker.cc52
-rw-r--r--chromium/content/browser/gpu/shader_disk_cache.cc4
-rw-r--r--chromium/content/browser/gpu/test_support_gpu.gypi7
-rw-r--r--chromium/content/browser/histogram_controller.cc11
-rw-r--r--chromium/content/browser/histogram_internals_request_job.cc5
-rw-r--r--chromium/content/browser/histogram_message_filter.cc10
-rw-r--r--chromium/content/browser/histogram_message_filter.h3
-rw-r--r--chromium/content/browser/host_zoom_map_impl.cc264
-rw-r--r--chromium/content/browser/host_zoom_map_impl.h74
-rw-r--r--chromium/content/browser/host_zoom_map_impl_unittest.cc25
-rw-r--r--chromium/content/browser/indexed_db/OWNERS3
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_active_blob_registry.cc147
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_active_blob_registry.h74
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_active_blob_registry_unittest.cc274
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_backing_store.cc2688
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_backing_store.h436
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_backing_store_unittest.cc965
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_blob_info.cc93
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_blob_info.h76
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_browsertest.cc296
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_callbacks.cc356
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_callbacks.h32
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_class_factory.cc30
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_class_factory.h36
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_cleanup_on_io_error_unittest.cc99
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_connection.cc2
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_connection.h2
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_context_impl.cc92
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_context_impl.h16
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_cursor.cc63
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_cursor.h6
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_database.cc877
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_database.h74
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_database_callbacks.h2
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_database_error.h7
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_database_unittest.cc292
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_dispatcher_host.cc235
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_dispatcher_host.h60
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_factory.cc257
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_factory.h57
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_factory_unittest.cc322
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_fake_backing_store.cc177
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_fake_backing_store.h166
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_index_writer.cc67
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_index_writer.h5
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_internals_ui.cc24
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_internals_ui.h6
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_leveldb_coding.cc247
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_leveldb_coding.h67
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_leveldb_coding_unittest.cc198
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_metadata.h2
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_pending_connection.cc23
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_pending_connection.h36
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_quota_client.h26
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_quota_client_unittest.cc6
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_tracing.h2
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_transaction.cc112
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_transaction.h26
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_transaction_coordinator.cc15
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_transaction_coordinator.h6
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_transaction_unittest.cc296
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_unittest.cc121
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_value.cc20
-rw-r--r--chromium/content/browser/indexed_db/indexed_db_value.h44
-rw-r--r--chromium/content/browser/indexed_db/leveldb/leveldb_comparator.h3
-rw-r--r--chromium/content/browser/indexed_db/leveldb/leveldb_database.cc135
-rw-r--r--chromium/content/browser/indexed_db/leveldb/leveldb_database.h45
-rw-r--r--chromium/content/browser/indexed_db/leveldb/leveldb_iterator.h9
-rw-r--r--chromium/content/browser/indexed_db/leveldb/leveldb_transaction.cc133
-rw-r--r--chromium/content/browser/indexed_db/leveldb/leveldb_transaction.h57
-rw-r--r--chromium/content/browser/indexed_db/leveldb/leveldb_unittest.cc95
-rw-r--r--chromium/content/browser/indexed_db/list_set_unittest.cc32
-rw-r--r--chromium/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.cc168
-rw-r--r--chromium/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.h52
-rw-r--r--chromium/content/browser/indexed_db/mock_indexed_db_callbacks.cc5
-rw-r--r--chromium/content/browser/indexed_db/mock_indexed_db_callbacks.h8
-rw-r--r--chromium/content/browser/loader/OWNERS3
-rw-r--r--chromium/content/browser/loader/async_resource_handler.cc102
-rw-r--r--chromium/content/browser/loader/async_resource_handler.h37
-rw-r--r--chromium/content/browser/loader/buffered_resource_handler.cc91
-rw-r--r--chromium/content/browser/loader/buffered_resource_handler.h17
-rw-r--r--chromium/content/browser/loader/certificate_resource_handler.cc50
-rw-r--r--chromium/content/browser/loader/certificate_resource_handler.h29
-rw-r--r--chromium/content/browser/loader/cross_site_resource_handler.cc204
-rw-r--r--chromium/content/browser/loader/cross_site_resource_handler.h39
-rw-r--r--chromium/content/browser/loader/detachable_resource_handler.cc57
-rw-r--r--chromium/content/browser/loader/detachable_resource_handler.h23
-rw-r--r--chromium/content/browser/loader/layered_resource_handler.cc42
-rw-r--r--chromium/content/browser/loader/layered_resource_handler.h22
-rw-r--r--chromium/content/browser/loader/offline_policy.cc96
-rw-r--r--chromium/content/browser/loader/offline_policy.h60
-rw-r--r--chromium/content/browser/loader/offline_policy_unittest.cc96
-rw-r--r--chromium/content/browser/loader/redirect_to_file_resource_handler.cc233
-rw-r--r--chromium/content/browser/loader/redirect_to_file_resource_handler.h86
-rw-r--r--chromium/content/browser/loader/resource_dispatcher_host_browsertest.cc16
-rw-r--r--chromium/content/browser/loader/resource_dispatcher_host_impl.cc465
-rw-r--r--chromium/content/browser/loader/resource_dispatcher_host_impl.h52
-rw-r--r--chromium/content/browser/loader/resource_dispatcher_host_unittest.cc820
-rw-r--r--chromium/content/browser/loader/resource_handler.h54
-rw-r--r--chromium/content/browser/loader/resource_loader.cc112
-rw-r--r--chromium/content/browser/loader/resource_loader.h7
-rw-r--r--chromium/content/browser/loader/resource_loader_delegate.h7
-rw-r--r--chromium/content/browser/loader/resource_loader_unittest.cc565
-rw-r--r--chromium/content/browser/loader/resource_message_delegate.h3
-rw-r--r--chromium/content/browser/loader/resource_message_filter.cc13
-rw-r--r--chromium/content/browser/loader/resource_message_filter.h10
-rw-r--r--chromium/content/browser/loader/resource_request_info_impl.cc63
-rw-r--r--chromium/content/browser/loader/resource_request_info_impl.h20
-rw-r--r--chromium/content/browser/loader/resource_scheduler.cc649
-rw-r--r--chromium/content/browser/loader/resource_scheduler.h42
-rw-r--r--chromium/content/browser/loader/resource_scheduler_filter.cc20
-rw-r--r--chromium/content/browser/loader/resource_scheduler_filter.h3
-rw-r--r--chromium/content/browser/loader/resource_scheduler_unittest.cc89
-rw-r--r--chromium/content/browser/loader/stream_resource_handler.cc46
-rw-r--r--chromium/content/browser/loader/stream_resource_handler.h28
-rw-r--r--chromium/content/browser/loader/sync_resource_handler.cc35
-rw-r--r--chromium/content/browser/loader/sync_resource_handler.h28
-rw-r--r--chromium/content/browser/loader/temporary_file_stream.cc63
-rw-r--r--chromium/content/browser/loader/temporary_file_stream.h45
-rw-r--r--chromium/content/browser/loader/temporary_file_stream_unittest.cc120
-rw-r--r--chromium/content/browser/loader/throttling_resource_handler.cc64
-rw-r--r--chromium/content/browser/loader/throttling_resource_handler.h11
-rw-r--r--chromium/content/browser/loader/upload_data_stream_builder.cc7
-rw-r--r--chromium/content/browser/loader/upload_data_stream_builder.h8
-rw-r--r--chromium/content/browser/loader/upload_data_stream_builder_unittest.cc376
-rw-r--r--chromium/content/browser/mach_broker_mac.h7
-rw-r--r--chromium/content/browser/mach_broker_mac.mm109
-rw-r--r--chromium/content/browser/media/OWNERS7
-rw-r--r--chromium/content/browser/media/android/browser_demuxer_android.cc13
-rw-r--r--chromium/content/browser/media/android/browser_demuxer_android.h3
-rw-r--r--chromium/content/browser/media/android/browser_media_player_manager.cc645
-rw-r--r--chromium/content/browser/media/android/browser_media_player_manager.h166
-rw-r--r--chromium/content/browser/media/android/media_drm_credential_manager.cc42
-rw-r--r--chromium/content/browser/media/android/media_drm_credential_manager.h22
-rw-r--r--chromium/content/browser/media/android/media_resource_getter_impl.cc180
-rw-r--r--chromium/content/browser/media/android/media_resource_getter_impl.h19
-rw-r--r--chromium/content/browser/media/capture/DEPS3
-rw-r--r--chromium/content/browser/media/capture/OWNERS4
-rw-r--r--chromium/content/browser/media/capture/audio_mirroring_manager.cc (renamed from chromium/content/browser/renderer_host/media/audio_mirroring_manager.cc)2
-rw-r--r--chromium/content/browser/media/capture/audio_mirroring_manager.h (renamed from chromium/content/browser/renderer_host/media/audio_mirroring_manager.h)6
-rw-r--r--chromium/content/browser/media/capture/audio_mirroring_manager_unittest.cc (renamed from chromium/content/browser/renderer_host/media/audio_mirroring_manager_unittest.cc)7
-rw-r--r--chromium/content/browser/media/capture/content_video_capture_device_core.cc (renamed from chromium/content/browser/renderer_host/media/video_capture_device_impl.cc)171
-rw-r--r--chromium/content/browser/media/capture/content_video_capture_device_core.h (renamed from chromium/content/browser/renderer_host/media/video_capture_device_impl.h)87
-rw-r--r--chromium/content/browser/media/capture/desktop_capture_device.cc (renamed from chromium/content/browser/renderer_host/media/desktop_capture_device.cc)159
-rw-r--r--chromium/content/browser/media/capture/desktop_capture_device.h (renamed from chromium/content/browser/renderer_host/media/desktop_capture_device.h)24
-rw-r--r--chromium/content/browser/media/capture/desktop_capture_device_aura.cc (renamed from chromium/content/browser/renderer_host/media/desktop_capture_device_aura.cc)223
-rw-r--r--chromium/content/browser/media/capture/desktop_capture_device_aura.h (renamed from chromium/content/browser/renderer_host/media/desktop_capture_device_aura.h)10
-rw-r--r--chromium/content/browser/media/capture/desktop_capture_device_aura_unittest.cc (renamed from chromium/content/browser/renderer_host/media/desktop_capture_device_aura_unittest.cc)37
-rw-r--r--chromium/content/browser/media/capture/desktop_capture_device_uma_types.cc20
-rw-r--r--chromium/content/browser/media/capture/desktop_capture_device_uma_types.h30
-rw-r--r--chromium/content/browser/media/capture/desktop_capture_device_unittest.cc (renamed from chromium/content/browser/renderer_host/media/desktop_capture_device_unittest.cc)98
-rw-r--r--chromium/content/browser/media/capture/video_capture_oracle.cc (renamed from chromium/content/browser/renderer_host/media/video_capture_oracle.cc)23
-rw-r--r--chromium/content/browser/media/capture/video_capture_oracle.h (renamed from chromium/content/browser/renderer_host/media/video_capture_oracle.h)22
-rw-r--r--chromium/content/browser/media/capture/video_capture_oracle_unittest.cc (renamed from chromium/content/browser/renderer_host/media/video_capture_oracle_unittest.cc)49
-rw-r--r--chromium/content/browser/media/capture/web_contents_audio_input_stream.cc (renamed from chromium/content/browser/renderer_host/media/web_contents_audio_input_stream.cc)12
-rw-r--r--chromium/content/browser/media/capture/web_contents_audio_input_stream.h (renamed from chromium/content/browser/renderer_host/media/web_contents_audio_input_stream.h)16
-rw-r--r--chromium/content/browser/media/capture/web_contents_audio_input_stream_unittest.cc (renamed from chromium/content/browser/renderer_host/media/web_contents_audio_input_stream_unittest.cc)18
-rw-r--r--chromium/content/browser/media/capture/web_contents_capture_util.cc (renamed from chromium/content/browser/renderer_host/media/web_contents_capture_util.cc)2
-rw-r--r--chromium/content/browser/media/capture/web_contents_capture_util.h (renamed from chromium/content/browser/renderer_host/media/web_contents_capture_util.h)6
-rw-r--r--chromium/content/browser/media/capture/web_contents_tracker.cc (renamed from chromium/content/browser/renderer_host/media/web_contents_tracker.cc)4
-rw-r--r--chromium/content/browser/media/capture/web_contents_tracker.h (renamed from chromium/content/browser/renderer_host/media/web_contents_tracker.h)8
-rw-r--r--chromium/content/browser/media/capture/web_contents_video_capture_device.cc (renamed from chromium/content/browser/renderer_host/media/web_contents_video_capture_device.cc)271
-rw-r--r--chromium/content/browser/media/capture/web_contents_video_capture_device.h (renamed from chromium/content/browser/renderer_host/media/web_contents_video_capture_device.h)12
-rw-r--r--chromium/content/browser/media/capture/web_contents_video_capture_device_unittest.cc (renamed from chromium/content/browser/renderer_host/media/web_contents_video_capture_device_unittest.cc)61
-rw-r--r--chromium/content/browser/media/cdm/browser_cdm_manager.cc286
-rw-r--r--chromium/content/browser/media/cdm/browser_cdm_manager.h128
-rw-r--r--chromium/content/browser/media/encrypted_media_browsertest.cc4
-rw-r--r--chromium/content/browser/media/media_browsertest.cc41
-rw-r--r--chromium/content/browser/media/media_browsertest.h4
-rw-r--r--chromium/content/browser/media/media_canplaytype_browsertest.cc1065
-rw-r--r--chromium/content/browser/media/media_devices_monitor.cc28
-rw-r--r--chromium/content/browser/media/media_internals.cc43
-rw-r--r--chromium/content/browser/media/media_internals_handler.cc6
-rw-r--r--chromium/content/browser/media/media_internals_unittest.cc16
-rw-r--r--chromium/content/browser/media/media_source_browsertest.cc10
-rw-r--r--chromium/content/browser/media/media_web_contents_observer.cc206
-rw-r--r--chromium/content/browser/media/media_web_contents_observer.h85
-rw-r--r--chromium/content/browser/media/midi_dispatcher_host.cc123
-rw-r--r--chromium/content/browser/media/midi_dispatcher_host.h60
-rw-r--r--chromium/content/browser/media/midi_host.cc (renamed from chromium/content/browser/renderer_host/media/midi_host.cc)128
-rw-r--r--chromium/content/browser/media/midi_host.h (renamed from chromium/content/browser/renderer_host/media/midi_host.h)41
-rw-r--r--chromium/content/browser/media/midi_host_unittest.cc (renamed from chromium/content/browser/renderer_host/media/midi_host_unittest.cc)35
-rw-r--r--chromium/content/browser/media/webrtc_browsertest.cc678
-rw-r--r--chromium/content/browser/media/webrtc_getusermedia_browsertest.cc607
-rw-r--r--chromium/content/browser/media/webrtc_identity_store_backend.cc74
-rw-r--r--chromium/content/browser/media/webrtc_identity_store_unittest.cc33
-rw-r--r--chromium/content/browser/media/webrtc_internals.cc174
-rw-r--r--chromium/content/browser/media/webrtc_internals.h83
-rw-r--r--chromium/content/browser/media/webrtc_internals_browsertest.cc179
-rw-r--r--chromium/content/browser/media/webrtc_internals_message_handler.cc61
-rw-r--r--chromium/content/browser/media/webrtc_internals_message_handler.h7
-rw-r--r--chromium/content/browser/media/webrtc_internals_ui_observer.h3
-rw-r--r--chromium/content/browser/media/webrtc_internals_unittest.cc202
-rw-r--r--chromium/content/browser/message_port_message_filter.cc26
-rw-r--r--chromium/content/browser/message_port_message_filter.h18
-rw-r--r--chromium/content/browser/mime_registry_message_filter.cc8
-rw-r--r--chromium/content/browser/mime_registry_message_filter.h3
-rw-r--r--chromium/content/browser/mojo/mojo_application_host.cc71
-rw-r--r--chromium/content/browser/mojo/mojo_application_host.h70
-rw-r--r--chromium/content/browser/net/browser_online_state_observer.cc3
-rw-r--r--chromium/content/browser/net/sqlite_persistent_cookie_store.cc130
-rw-r--r--chromium/content/browser/net/sqlite_persistent_cookie_store.h2
-rw-r--r--chromium/content/browser/net/sqlite_persistent_cookie_store_perftest.cc6
-rw-r--r--chromium/content/browser/net/sqlite_persistent_cookie_store_unittest.cc13
-rw-r--r--chromium/content/browser/net/view_blob_internals_job_factory.cc2
-rw-r--r--chromium/content/browser/net/view_http_cache_job_factory.cc2
-rw-r--r--chromium/content/browser/plugin_browsertest.cc12
-rw-r--r--chromium/content/browser/plugin_data_remover_impl.cc4
-rw-r--r--chromium/content/browser/plugin_data_remover_impl_browsertest.cc2
-rw-r--r--chromium/content/browser/plugin_loader_posix.cc77
-rw-r--r--chromium/content/browser/plugin_loader_posix.h25
-rw-r--r--chromium/content/browser/plugin_loader_posix_unittest.cc56
-rw-r--r--chromium/content/browser/plugin_process_host.cc69
-rw-r--r--chromium/content/browser/plugin_process_host.h4
-rw-r--r--chromium/content/browser/plugin_service_impl.cc55
-rw-r--r--chromium/content/browser/plugin_service_impl.h15
-rw-r--r--chromium/content/browser/plugin_service_impl_browsertest.cc4
-rw-r--r--chromium/content/browser/power_monitor_message_broadcaster.h6
-rw-r--r--chromium/content/browser/power_profiler/power_data_provider.h38
-rw-r--r--chromium/content/browser/power_profiler/power_data_provider_dummy.cc13
-rw-r--r--chromium/content/browser/power_profiler/power_data_provider_ia_win.cc100
-rw-r--r--chromium/content/browser/power_profiler/power_data_provider_ia_win.h35
-rw-r--r--chromium/content/browser/power_profiler/power_event.cc17
-rw-r--r--chromium/content/browser/power_profiler/power_event.h42
-rw-r--r--chromium/content/browser/power_profiler/power_profiler_observer.h33
-rw-r--r--chromium/content/browser/power_profiler/power_profiler_service.cc109
-rw-r--r--chromium/content/browser/power_profiler/power_profiler_service.h77
-rw-r--r--chromium/content/browser/power_profiler/power_profiler_service_unittest.cc143
-rw-r--r--chromium/content/browser/power_save_blocker_android.cc2
-rw-r--r--chromium/content/browser/power_save_blocker_win.cc2
-rw-r--r--chromium/content/browser/power_save_blocker_x11.cc8
-rw-r--r--chromium/content/browser/ppapi_plugin_process_host.cc68
-rw-r--r--chromium/content/browser/profiler_controller_impl.cc13
-rw-r--r--chromium/content/browser/profiler_message_filter.cc10
-rw-r--r--chromium/content/browser/profiler_message_filter.h3
-rw-r--r--chromium/content/browser/push_messaging_message_filter.cc97
-rw-r--r--chromium/content/browser/push_messaging_message_filter.h54
-rw-r--r--chromium/content/browser/quota/DEPS4
-rw-r--r--chromium/content/browser/quota/mock_quota_manager.cc147
-rw-r--r--chromium/content/browser/quota/mock_quota_manager.h152
-rw-r--r--chromium/content/browser/quota/mock_quota_manager_proxy.cc61
-rw-r--r--chromium/content/browser/quota/mock_quota_manager_proxy.h87
-rw-r--r--chromium/content/browser/quota/mock_quota_manager_unittest.cc226
-rw-r--r--chromium/content/browser/quota/quota_backend_impl_unittest.cc273
-rw-r--r--chromium/content/browser/quota/quota_database_unittest.cc567
-rw-r--r--chromium/content/browser/quota/quota_manager_unittest.cc2190
-rw-r--r--chromium/content/browser/quota/quota_reservation_manager_unittest.cc367
-rw-r--r--chromium/content/browser/quota/quota_temporary_storage_evictor_unittest.cc414
-rw-r--r--chromium/content/browser/quota/storage_monitor_unittest.cc707
-rw-r--r--chromium/content/browser/quota/usage_tracker_unittest.cc336
-rw-r--r--chromium/content/browser/quota_dispatcher_host.cc132
-rw-r--r--chromium/content/browser/quota_dispatcher_host.h11
-rw-r--r--chromium/content/browser/renderer_data_memoizing_store.h3
-rw-r--r--chromium/content/browser/renderer_host/DEPS18
-rw-r--r--chromium/content/browser/renderer_host/OWNERS6
-rw-r--r--chromium/content/browser/renderer_host/backing_store.cc21
-rw-r--r--chromium/content/browser/renderer_host/backing_store.h93
-rw-r--r--chromium/content/browser/renderer_host/backing_store_aura.cc176
-rw-r--r--chromium/content/browser/renderer_host/backing_store_aura.h66
-rw-r--r--chromium/content/browser/renderer_host/backing_store_gtk.cc692
-rw-r--r--chromium/content/browser/renderer_host/backing_store_gtk.h107
-rw-r--r--chromium/content/browser/renderer_host/backing_store_mac.h76
-rw-r--r--chromium/content/browser/renderer_host/backing_store_mac.mm297
-rw-r--r--chromium/content/browser/renderer_host/backing_store_manager.cc282
-rw-r--r--chromium/content/browser/renderer_host/backing_store_manager.h86
-rw-r--r--chromium/content/browser/renderer_host/backing_store_win.cc186
-rw-r--r--chromium/content/browser/renderer_host/backing_store_win.h60
-rw-r--r--chromium/content/browser/renderer_host/clipboard_message_filter.cc89
-rw-r--r--chromium/content/browser/renderer_host/clipboard_message_filter.h8
-rw-r--r--chromium/content/browser/renderer_host/compositing_iosurface_context_mac.h43
-rw-r--r--chromium/content/browser/renderer_host/compositing_iosurface_context_mac.mm138
-rw-r--r--chromium/content/browser/renderer_host/compositing_iosurface_layer_mac.h55
-rw-r--r--chromium/content/browser/renderer_host/compositing_iosurface_layer_mac.mm237
-rw-r--r--chromium/content/browser/renderer_host/compositing_iosurface_mac.h115
-rw-r--r--chromium/content/browser/renderer_host/compositing_iosurface_mac.mm375
-rw-r--r--chromium/content/browser/renderer_host/compositing_iosurface_shader_programs_mac.cc2
-rw-r--r--chromium/content/browser/renderer_host/compositing_iosurface_transformer_mac_unittest.cc2
-rw-r--r--chromium/content/browser/renderer_host/compositor_impl_android.cc610
-rw-r--r--chromium/content/browser/renderer_host/compositor_impl_android.h139
-rw-r--r--chromium/content/browser/renderer_host/compositor_resize_lock_aura.cc (renamed from chromium/content/browser/aura/compositor_resize_lock.cc)26
-rw-r--r--chromium/content/browser/renderer_host/compositor_resize_lock_aura.h (renamed from chromium/content/browser/aura/compositor_resize_lock.h)29
-rw-r--r--chromium/content/browser/renderer_host/database_message_filter.cc93
-rw-r--r--chromium/content/browser/renderer_host/database_message_filter.h3
-rw-r--r--chromium/content/browser/renderer_host/delegated_frame_evictor.cc21
-rw-r--r--chromium/content/browser/renderer_host/delegated_frame_evictor.h3
-rw-r--r--chromium/content/browser/renderer_host/dip_util.cc21
-rw-r--r--chromium/content/browser/renderer_host/dip_util.h8
-rw-r--r--chromium/content/browser/renderer_host/display_link_mac.cc160
-rw-r--r--chromium/content/browser/renderer_host/display_link_mac.h76
-rw-r--r--chromium/content/browser/renderer_host/event_with_latency_info.h59
-rw-r--r--chromium/content/browser/renderer_host/file_utilities_message_filter.cc19
-rw-r--r--chromium/content/browser/renderer_host/file_utilities_message_filter.h15
-rw-r--r--chromium/content/browser/renderer_host/gamepad_browser_message_filter.cc51
-rw-r--r--chromium/content/browser/renderer_host/gamepad_browser_message_filter.h16
-rw-r--r--chromium/content/browser/renderer_host/gpu_message_filter.cc86
-rw-r--r--chromium/content/browser/renderer_host/gpu_message_filter.h13
-rw-r--r--chromium/content/browser/renderer_host/gtk_im_context_wrapper.cc665
-rw-r--r--chromium/content/browser/renderer_host/gtk_im_context_wrapper.h201
-rw-r--r--chromium/content/browser/renderer_host/gtk_key_bindings_handler.cc293
-rw-r--r--chromium/content/browser/renderer_host/gtk_key_bindings_handler.h132
-rw-r--r--chromium/content/browser/renderer_host/gtk_key_bindings_handler_unittest.cc226
-rw-r--r--chromium/content/browser/renderer_host/gtk_plugin_container.cc89
-rw-r--r--chromium/content/browser/renderer_host/gtk_plugin_container.h30
-rw-r--r--chromium/content/browser/renderer_host/gtk_plugin_container_manager.cc161
-rw-r--r--chromium/content/browser/renderer_host/gtk_plugin_container_manager.h58
-rw-r--r--chromium/content/browser/renderer_host/gtk_window_utils.cc89
-rw-r--r--chromium/content/browser/renderer_host/gtk_window_utils.h23
-rw-r--r--chromium/content/browser/renderer_host/image_transport_factory_android.cc61
-rw-r--r--chromium/content/browser/renderer_host/image_transport_factory_android.h14
-rw-r--r--chromium/content/browser/renderer_host/ime_adapter_android.cc188
-rw-r--r--chromium/content/browser/renderer_host/ime_adapter_android.h15
-rw-r--r--chromium/content/browser/renderer_host/input/gesture_event_queue.cc (renamed from chromium/content/browser/renderer_host/input/gesture_event_filter.cc)263
-rw-r--r--chromium/content/browser/renderer_host/input/gesture_event_queue.h (renamed from chromium/content/browser/renderer_host/input/gesture_event_filter.h)128
-rw-r--r--chromium/content/browser/renderer_host/input/gesture_event_queue_unittest.cc (renamed from chromium/content/browser/renderer_host/input/gesture_event_filter_unittest.cc)481
-rw-r--r--chromium/content/browser/renderer_host/input/input_ack_handler.h4
-rw-r--r--chromium/content/browser/renderer_host/input/input_router.h6
-rw-r--r--chromium/content/browser/renderer_host/input/input_router_client.h15
-rw-r--r--chromium/content/browser/renderer_host/input/input_router_config_helper.cc140
-rw-r--r--chromium/content/browser/renderer_host/input/input_router_config_helper.h18
-rw-r--r--chromium/content/browser/renderer_host/input/input_router_impl.cc518
-rw-r--r--chromium/content/browser/renderer_host/input/input_router_impl.h96
-rw-r--r--chromium/content/browser/renderer_host/input/input_router_impl_perftest.cc387
-rw-r--r--chromium/content/browser/renderer_host/input/input_router_impl_unittest.cc1181
-rw-r--r--chromium/content/browser/renderer_host/input/mock_input_ack_handler.cc22
-rw-r--r--chromium/content/browser/renderer_host/input/mock_input_ack_handler.h6
-rw-r--r--chromium/content/browser/renderer_host/input/mock_input_router_client.cc30
-rw-r--r--chromium/content/browser/renderer_host/input/mock_input_router_client.h14
-rw-r--r--chromium/content/browser/renderer_host/input/motion_event_android.cc330
-rw-r--r--chromium/content/browser/renderer_host/input/motion_event_android.h125
-rw-r--r--chromium/content/browser/renderer_host/input/motion_event_web.cc160
-rw-r--r--chromium/content/browser/renderer_host/input/motion_event_web.h57
-rw-r--r--chromium/content/browser/renderer_host/input/synthetic_gesture.cc6
-rw-r--r--chromium/content/browser/renderer_host/input/synthetic_gesture.h10
-rw-r--r--chromium/content/browser/renderer_host/input/synthetic_gesture_controller.cc73
-rw-r--r--chromium/content/browser/renderer_host/input/synthetic_gesture_controller.h46
-rw-r--r--chromium/content/browser/renderer_host/input/synthetic_gesture_controller_unittest.cc705
-rw-r--r--chromium/content/browser/renderer_host/input/synthetic_gesture_target.h23
-rw-r--r--chromium/content/browser/renderer_host/input/synthetic_gesture_target_android.cc32
-rw-r--r--chromium/content/browser/renderer_host/input/synthetic_gesture_target_android.h17
-rw-r--r--chromium/content/browser/renderer_host/input/synthetic_gesture_target_aura.cc80
-rw-r--r--chromium/content/browser/renderer_host/input/synthetic_gesture_target_aura.h18
-rw-r--r--chromium/content/browser/renderer_host/input/synthetic_gesture_target_base.cc103
-rw-r--r--chromium/content/browser/renderer_host/input/synthetic_gesture_target_base.h15
-rw-r--r--chromium/content/browser/renderer_host/input/synthetic_pinch_gesture.cc169
-rw-r--r--chromium/content/browser/renderer_host/input/synthetic_pinch_gesture.h40
-rw-r--r--chromium/content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.cc230
-rw-r--r--chromium/content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.h53
-rw-r--r--chromium/content/browser/renderer_host/input/synthetic_tap_gesture.cc52
-rw-r--r--chromium/content/browser/renderer_host/input/synthetic_tap_gesture.h14
-rw-r--r--chromium/content/browser/renderer_host/input/tap_suppression_controller.cc30
-rw-r--r--chromium/content/browser/renderer_host/input/tap_suppression_controller.h22
-rw-r--r--chromium/content/browser/renderer_host/input/tap_suppression_controller_client.h8
-rw-r--r--chromium/content/browser/renderer_host/input/tap_suppression_controller_unittest.cc103
-rw-r--r--chromium/content/browser/renderer_host/input/touch_action_browsertest.cc216
-rw-r--r--chromium/content/browser/renderer_host/input/touch_action_filter.cc172
-rw-r--r--chromium/content/browser/renderer_host/input/touch_action_filter.h31
-rw-r--r--chromium/content/browser/renderer_host/input/touch_action_filter_unittest.cc631
-rw-r--r--chromium/content/browser/renderer_host/input/touch_emulator.cc389
-rw-r--r--chromium/content/browser/renderer_host/input/touch_emulator.h100
-rw-r--r--chromium/content/browser/renderer_host/input/touch_emulator_client.h26
-rw-r--r--chromium/content/browser/renderer_host/input/touch_emulator_unittest.cc349
-rw-r--r--chromium/content/browser/renderer_host/input/touch_event_queue.cc644
-rw-r--r--chromium/content/browser/renderer_host/input/touch_event_queue.h156
-rw-r--r--chromium/content/browser/renderer_host/input/touch_event_queue_unittest.cc1185
-rw-r--r--chromium/content/browser/renderer_host/input/touch_input_browsertest.cc80
-rw-r--r--chromium/content/browser/renderer_host/input/touchpad_tap_suppression_controller.cc40
-rw-r--r--chromium/content/browser/renderer_host/input/touchpad_tap_suppression_controller.h13
-rw-r--r--chromium/content/browser/renderer_host/input/touchpad_tap_suppression_controller_aura.cc58
-rw-r--r--chromium/content/browser/renderer_host/input/touchscreen_tap_suppression_controller.cc87
-rw-r--r--chromium/content/browser/renderer_host/input/touchscreen_tap_suppression_controller.h30
-rw-r--r--chromium/content/browser/renderer_host/input/touchscreen_tap_suppression_controller_stub.cc52
-rw-r--r--chromium/content/browser/renderer_host/input/web_input_event_builders_android.cc9
-rw-r--r--chromium/content/browser/renderer_host/input/web_input_event_builders_android.h38
-rw-r--r--chromium/content/browser/renderer_host/input/web_input_event_builders_gtk.cc603
-rw-r--r--chromium/content/browser/renderer_host/input/web_input_event_builders_gtk.h44
-rw-r--r--chromium/content/browser/renderer_host/input/web_input_event_builders_gtk_unittest.cc171
-rw-r--r--chromium/content/browser/renderer_host/input/web_input_event_builders_win.cc47
-rw-r--r--chromium/content/browser/renderer_host/input/web_input_event_builders_win.h21
-rw-r--r--chromium/content/browser/renderer_host/input/web_input_event_util.cc196
-rw-r--r--chromium/content/browser/renderer_host/input/web_input_event_util.h18
-rw-r--r--chromium/content/browser/renderer_host/java/OWNERS2
-rw-r--r--chromium/content/browser/renderer_host/java/gin_java_bound_object.cc192
-rw-r--r--chromium/content/browser/renderer_host/java/gin_java_bound_object.h96
-rw-r--r--chromium/content/browser/renderer_host/java/gin_java_bound_object_delegate.cc43
-rw-r--r--chromium/content/browser/renderer_host/java/gin_java_bound_object_delegate.h38
-rw-r--r--chromium/content/browser/renderer_host/java/gin_java_method_invocation_helper.cc341
-rw-r--r--chromium/content/browser/renderer_host/java/gin_java_method_invocation_helper.h116
-rw-r--r--chromium/content/browser/renderer_host/java/gin_java_method_invocation_helper_unittest.cc285
-rw-r--r--chromium/content/browser/renderer_host/java/gin_java_script_to_java_types_coercion.cc705
-rw-r--r--chromium/content/browser/renderer_host/java/gin_java_script_to_java_types_coercion.h33
-rw-r--r--chromium/content/browser/renderer_host/java/java_bound_object.cc172
-rw-r--r--chromium/content/browser/renderer_host/java/java_bound_object.h25
-rw-r--r--chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host.cc20
-rw-r--r--chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host.h17
-rw-r--r--chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.cc87
-rw-r--r--chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.h44
-rw-r--r--chromium/content/browser/renderer_host/java/java_method.cc9
-rw-r--r--chromium/content/browser/renderer_host/java/java_method.h2
-rw-r--r--chromium/content/browser/renderer_host/legacy_render_widget_host_win.cc333
-rw-r--r--chromium/content/browser/renderer_host/legacy_render_widget_host_win.h151
-rw-r--r--chromium/content/browser/renderer_host/media/DEPS6
-rw-r--r--chromium/content/browser/renderer_host/media/OWNERS20
-rw-r--r--chromium/content/browser/renderer_host/media/audio_input_device_manager.cc83
-rw-r--r--chromium/content/browser/renderer_host/media/audio_input_device_manager.h10
-rw-r--r--chromium/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc59
-rw-r--r--chromium/content/browser/renderer_host/media/audio_input_renderer_host.cc167
-rw-r--r--chromium/content/browser/renderer_host/media/audio_input_renderer_host.h65
-rw-r--r--chromium/content/browser/renderer_host/media/audio_input_sync_writer.cc70
-rw-r--r--chromium/content/browser/renderer_host/media/audio_input_sync_writer.h34
-rw-r--r--chromium/content/browser/renderer_host/media/audio_renderer_host.cc216
-rw-r--r--chromium/content/browser/renderer_host/media/audio_renderer_host.h18
-rw-r--r--chromium/content/browser/renderer_host/media/audio_renderer_host_unittest.cc36
-rw-r--r--chromium/content/browser/renderer_host/media/audio_sync_reader.cc28
-rw-r--r--chromium/content/browser/renderer_host/media/audio_sync_reader.h17
-rw-r--r--chromium/content/browser/renderer_host/media/device_request_message_filter.cc47
-rw-r--r--chromium/content/browser/renderer_host/media/device_request_message_filter.h14
-rw-r--r--chromium/content/browser/renderer_host/media/device_request_message_filter_unittest.cc72
-rw-r--r--chromium/content/browser/renderer_host/media/media_capture_devices_impl.cc99
-rw-r--r--chromium/content/browser/renderer_host/media/media_capture_devices_impl.h49
-rw-r--r--chromium/content/browser/renderer_host/media/media_stream_dispatcher_host.cc88
-rw-r--r--chromium/content/browser/renderer_host/media/media_stream_dispatcher_host.h26
-rw-r--r--chromium/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc267
-rw-r--r--chromium/content/browser/renderer_host/media/media_stream_manager.cc736
-rw-r--r--chromium/content/browser/renderer_host/media/media_stream_manager.h105
-rw-r--r--chromium/content/browser/renderer_host/media/media_stream_manager_unittest.cc12
-rw-r--r--chromium/content/browser/renderer_host/media/media_stream_provider.h15
-rw-r--r--chromium/content/browser/renderer_host/media/media_stream_requester.h6
-rw-r--r--chromium/content/browser/renderer_host/media/media_stream_track_metrics_host.cc86
-rw-r--r--chromium/content/browser/renderer_host/media/media_stream_track_metrics_host.h63
-rw-r--r--chromium/content/browser/renderer_host/media/media_stream_ui_controller_unittest.cc2
-rw-r--r--chromium/content/browser/renderer_host/media/media_stream_ui_proxy.cc106
-rw-r--r--chromium/content/browser/renderer_host/media/media_stream_ui_proxy.h21
-rw-r--r--chromium/content/browser/renderer_host/media/media_stream_ui_proxy_unittest.cc78
-rw-r--r--chromium/content/browser/renderer_host/media/midi_dispatcher_host.cc79
-rw-r--r--chromium/content/browser/renderer_host/media/midi_dispatcher_host.h51
-rw-r--r--chromium/content/browser/renderer_host/media/mock_media_observer.h15
-rw-r--r--chromium/content/browser/renderer_host/media/peer_connection_tracker_host.cc26
-rw-r--r--chromium/content/browser/renderer_host/media/peer_connection_tracker_host.h8
-rw-r--r--chromium/content/browser/renderer_host/media/video_capture_controller.cc310
-rw-r--r--chromium/content/browser/renderer_host/media/video_capture_controller.h21
-rw-r--r--chromium/content/browser/renderer_host/media/video_capture_controller_event_handler.h25
-rw-r--r--chromium/content/browser/renderer_host/media/video_capture_controller_unittest.cc218
-rw-r--r--chromium/content/browser/renderer_host/media/video_capture_host.cc163
-rw-r--r--chromium/content/browser/renderer_host/media/video_capture_host.h56
-rw-r--r--chromium/content/browser/renderer_host/media/video_capture_host_unittest.cc66
-rw-r--r--chromium/content/browser/renderer_host/media/video_capture_manager.cc380
-rw-r--r--chromium/content/browser/renderer_host/media/video_capture_manager.h127
-rw-r--r--chromium/content/browser/renderer_host/media/video_capture_manager_unittest.cc204
-rw-r--r--chromium/content/browser/renderer_host/media/webrtc_identity_service_host.cc10
-rw-r--r--chromium/content/browser/renderer_host/media/webrtc_identity_service_host.h3
-rw-r--r--chromium/content/browser/renderer_host/media/webrtc_identity_service_host_unittest.cc15
-rw-r--r--chromium/content/browser/renderer_host/memory_benchmark_message_filter.cc10
-rw-r--r--chromium/content/browser/renderer_host/memory_benchmark_message_filter.h3
-rw-r--r--chromium/content/browser/renderer_host/native_web_keyboard_event_aura.cc14
-rw-r--r--chromium/content/browser/renderer_host/native_web_keyboard_event_gtk.cc75
-rw-r--r--chromium/content/browser/renderer_host/native_web_keyboard_event_win.cc49
-rw-r--r--chromium/content/browser/renderer_host/overscroll_controller.cc31
-rw-r--r--chromium/content/browser/renderer_host/overscroll_controller.h17
-rw-r--r--chromium/content/browser/renderer_host/p2p/socket_dispatcher_host.cc118
-rw-r--r--chromium/content/browser/renderer_host/p2p/socket_dispatcher_host.h37
-rw-r--r--chromium/content/browser/renderer_host/p2p/socket_host.cc506
-rw-r--r--chromium/content/browser/renderer_host/p2p/socket_host.h62
-rw-r--r--chromium/content/browser/renderer_host/p2p/socket_host_tcp.cc86
-rw-r--r--chromium/content/browser/renderer_host/p2p/socket_host_tcp.h24
-rw-r--r--chromium/content/browser/renderer_host/p2p/socket_host_tcp_server.cc26
-rw-r--r--chromium/content/browser/renderer_host/p2p/socket_host_tcp_server.h11
-rw-r--r--chromium/content/browser/renderer_host/p2p/socket_host_tcp_server_unittest.cc9
-rw-r--r--chromium/content/browser/renderer_host/p2p/socket_host_tcp_unittest.cc91
-rw-r--r--chromium/content/browser/renderer_host/p2p/socket_host_test_utils.cc9
-rw-r--r--chromium/content/browser/renderer_host/p2p/socket_host_test_utils.h4
-rw-r--r--chromium/content/browser/renderer_host/p2p/socket_host_udp.cc89
-rw-r--r--chromium/content/browser/renderer_host/p2p/socket_host_udp.h15
-rw-r--r--chromium/content/browser/renderer_host/p2p/socket_host_udp_unittest.cc55
-rw-r--r--chromium/content/browser/renderer_host/p2p/socket_host_unittest.cc376
-rw-r--r--chromium/content/browser/renderer_host/pepper/browser_ppapi_host_impl.cc84
-rw-r--r--chromium/content/browser/renderer_host/pepper/browser_ppapi_host_impl.h51
-rw-r--r--chromium/content/browser/renderer_host/pepper/browser_ppapi_host_test.cc22
-rw-r--r--chromium/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.cc85
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_browser_font_singleton_host.cc13
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_external_file_ref_backend.cc39
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_external_file_ref_backend.h16
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_file_io_host.cc430
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_file_io_host.h70
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_file_ref_host.cc79
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_file_ref_host.h7
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_file_system_browser_host.cc248
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_file_system_browser_host.h57
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_file_system_browser_host_unittest.cc16
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_flash_file_message_filter.cc161
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_flash_file_message_filter.h3
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_gamepad_host.cc14
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_gamepad_host.h13
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_gamepad_host_unittest.cc38
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_host_resolver_message_filter.cc46
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_host_resolver_message_filter.h2
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_internal_file_ref_backend.cc81
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_internal_file_ref_backend.h18
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_lookup_request.h2
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_message_filter.cc19
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_message_filter.h3
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_network_monitor_host.cc49
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_network_monitor_host.h7
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_network_proxy_host.cc67
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_network_proxy_host.h2
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_print_settings_manager.cc45
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_printing_host.cc14
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_printing_host.h8
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_printing_host_unittest.cc52
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_renderer_connection.cc43
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_renderer_connection.h3
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_security_helper.cc21
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_socket_utils.cc75
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_socket_utils.h10
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.cc72
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.h11
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_tcp_socket.cc159
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.cc261
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h25
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_truetype_font_list_host.cc26
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_truetype_font_list_win.cc23
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.cc129
-rw-r--r--chromium/content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.h9
-rw-r--r--chromium/content/browser/renderer_host/pepper/quota_reservation.cc81
-rw-r--r--chromium/content/browser/renderer_host/pepper/quota_reservation.h24
-rw-r--r--chromium/content/browser/renderer_host/pepper/quota_reservation_unittest.cc150
-rw-r--r--chromium/content/browser/renderer_host/pepper/ssl_context_helper.cc6
-rw-r--r--chromium/content/browser/renderer_host/popup_menu_helper_mac.h10
-rw-r--r--chromium/content/browser/renderer_host/popup_menu_helper_mac.mm39
-rw-r--r--chromium/content/browser/renderer_host/render_message_filter.cc294
-rw-r--r--chromium/content/browser/renderer_host/render_message_filter.h57
-rw-r--r--chromium/content/browser/renderer_host/render_process_host_browsertest.cc126
-rw-r--r--chromium/content/browser/renderer_host/render_process_host_impl.cc1149
-rw-r--r--chromium/content/browser/renderer_host/render_process_host_impl.h178
-rw-r--r--chromium/content/browser/renderer_host/render_process_host_unittest.cc2
-rw-r--r--chromium/content/browser/renderer_host/render_sandbox_host_linux.cc710
-rw-r--r--chromium/content/browser/renderer_host/render_sandbox_host_linux.h17
-rw-r--r--chromium/content/browser/renderer_host/render_view_host_browsertest.cc68
-rw-r--r--chromium/content/browser/renderer_host/render_view_host_delegate.cc24
-rw-r--r--chromium/content/browser/renderer_host/render_view_host_delegate.h181
-rw-r--r--chromium/content/browser/renderer_host/render_view_host_delegate_view.h82
-rw-r--r--chromium/content/browser/renderer_host/render_view_host_factory.cc10
-rw-r--r--chromium/content/browser/renderer_host/render_view_host_factory.h2
-rw-r--r--chromium/content/browser/renderer_host/render_view_host_impl.cc1361
-rw-r--r--chromium/content/browser/renderer_host/render_view_host_impl.h409
-rw-r--r--chromium/content/browser/renderer_host/render_view_host_unittest.cc93
-rw-r--r--chromium/content/browser/renderer_host/render_widget_helper.cc22
-rw-r--r--chromium/content/browser/renderer_host/render_widget_helper.h13
-rw-r--r--chromium/content/browser/renderer_host/render_widget_helper_mac.mm64
-rw-r--r--chromium/content/browser/renderer_host/render_widget_host_browsertest.cc53
-rw-r--r--chromium/content/browser/renderer_host/render_widget_host_delegate.cc14
-rw-r--r--chromium/content/browser/renderer_host/render_widget_host_delegate.h19
-rw-r--r--chromium/content/browser/renderer_host/render_widget_host_impl.cc1114
-rw-r--r--chromium/content/browser/renderer_host/render_widget_host_impl.h317
-rw-r--r--chromium/content/browser/renderer_host/render_widget_host_unittest.cc1885
-rw-r--r--chromium/content/browser/renderer_host/render_widget_host_view_android.cc1118
-rw-r--r--chromium/content/browser/renderer_host/render_widget_host_view_android.h195
-rw-r--r--chromium/content/browser/renderer_host/render_widget_host_view_aura.cc2230
-rw-r--r--chromium/content/browser/renderer_host/render_widget_host_view_aura.h457
-rw-r--r--chromium/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc1899
-rw-r--r--chromium/content/browser/renderer_host/render_widget_host_view_base.cc106
-rw-r--r--chromium/content/browser/renderer_host/render_widget_host_view_base.h377
-rw-r--r--chromium/content/browser/renderer_host/render_widget_host_view_browsertest.cc263
-rw-r--r--chromium/content/browser/renderer_host/render_widget_host_view_gtk.cc1608
-rw-r--r--chromium/content/browser/renderer_host/render_widget_host_view_gtk.h340
-rw-r--r--chromium/content/browser/renderer_host/render_widget_host_view_mac.h278
-rw-r--r--chromium/content/browser/renderer_host/render_widget_host_view_mac.mm2084
-rw-r--r--chromium/content/browser/renderer_host/render_widget_host_view_mac_dictionary_helper.h4
-rw-r--r--chromium/content/browser/renderer_host/render_widget_host_view_mac_dictionary_helper.mm2
-rw-r--r--chromium/content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper_unittest.mm49
-rw-r--r--chromium/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm92
-rw-r--r--chromium/content/browser/renderer_host/render_widget_host_view_win.cc3225
-rw-r--r--chromium/content/browser/renderer_host/render_widget_host_view_win.h616
-rw-r--r--chromium/content/browser/renderer_host/renderer_frame_manager.cc88
-rw-r--r--chromium/content/browser/renderer_host/renderer_frame_manager.h18
-rw-r--r--chromium/content/browser/renderer_host/sandbox_ipc_linux.cc650
-rw-r--r--chromium/content/browser/renderer_host/sandbox_ipc_linux.h87
-rw-r--r--chromium/content/browser/renderer_host/socket_stream_dispatcher_host.cc51
-rw-r--r--chromium/content/browser/renderer_host/socket_stream_dispatcher_host.h5
-rw-r--r--chromium/content/browser/renderer_host/socket_stream_host.cc17
-rw-r--r--chromium/content/browser/renderer_host/socket_stream_host.h8
-rw-r--r--chromium/content/browser/renderer_host/software_frame_manager.cc96
-rw-r--r--chromium/content/browser/renderer_host/software_frame_manager_unittest.cc21
-rw-r--r--chromium/content/browser/renderer_host/software_layer_mac.h22
-rw-r--r--chromium/content/browser/renderer_host/software_layer_mac.mm66
-rw-r--r--chromium/content/browser/renderer_host/text_input_client_mac.h22
-rw-r--r--chromium/content/browser/renderer_host/text_input_client_mac.mm18
-rw-r--r--chromium/content/browser/renderer_host/text_input_client_mac_unittest.mm13
-rw-r--r--chromium/content/browser/renderer_host/text_input_client_message_filter.h7
-rw-r--r--chromium/content/browser/renderer_host/text_input_client_message_filter.mm23
-rw-r--r--chromium/content/browser/renderer_host/ui_events_helper.cc31
-rw-r--r--chromium/content/browser/renderer_host/ui_events_helper.h6
-rw-r--r--chromium/content/browser/renderer_host/web_input_event_aura.cc55
-rw-r--r--chromium/content/browser/renderer_host/web_input_event_aurawin.cc17
-rw-r--r--chromium/content/browser/renderer_host/web_input_event_aurax11.cc2
-rw-r--r--chromium/content/browser/renderer_host/webmenurunner_mac.h3
-rw-r--r--chromium/content/browser/renderer_host/webmenurunner_mac.mm4
-rw-r--r--chromium/content/browser/renderer_host/websocket_dispatcher_host.cc45
-rw-r--r--chromium/content/browser/renderer_host/websocket_dispatcher_host.h35
-rw-r--r--chromium/content/browser/renderer_host/websocket_dispatcher_host_unittest.cc50
-rw-r--r--chromium/content/browser/renderer_host/websocket_host.cc230
-rw-r--r--chromium/content/browser/renderer_host/websocket_host.h18
-rw-r--r--chromium/content/browser/resolve_proxy_msg_helper.cc11
-rw-r--r--chromium/content/browser/resolve_proxy_msg_helper.h3
-rw-r--r--chromium/content/browser/resolve_proxy_msg_helper_unittest.cc3
-rw-r--r--chromium/content/browser/resources/accessibility/accessibility.js27
-rw-r--r--chromium/content/browser/resources/devtools/devtools_pinch_cursor.pngbin0 -> 139 bytes
-rw-r--r--chromium/content/browser/resources/devtools/devtools_pinch_cursor_2x.pngbin0 -> 198 bytes
-rw-r--r--chromium/content/browser/resources/devtools/devtools_touch_cursor.pngbin0 -> 271 bytes
-rw-r--r--chromium/content/browser/resources/devtools/devtools_touch_cursor_2x.pngbin0 -> 518 bytes
-rw-r--r--chromium/content/browser/resources/gpu/OWNERS2
-rw-r--r--chromium/content/browser/resources/gpu/gpu_internals.html1
-rw-r--r--chromium/content/browser/resources/gpu/info_view.css6
-rw-r--r--chromium/content/browser/resources/gpu/info_view.html15
-rw-r--r--chromium/content/browser/resources/gpu/info_view.js155
-rw-r--r--chromium/content/browser/resources/indexed_db/indexeddb_internals.css6
-rw-r--r--chromium/content/browser/resources/indexed_db/indexeddb_internals.html2
-rw-r--r--chromium/content/browser/resources/media/OWNERS1
-rw-r--r--chromium/content/browser/resources/media/data_series.js3
-rw-r--r--chromium/content/browser/resources/media/dump_creator.js131
-rw-r--r--chromium/content/browser/resources/media/stats_graph_helper.js101
-rw-r--r--chromium/content/browser/resources/media/stats_table.js5
-rw-r--r--chromium/content/browser/resources/media/tab_view.js2
-rw-r--r--chromium/content/browser/resources/media/timeline_graph_view.js83
-rw-r--r--chromium/content/browser/resources/media/webrtc_internals.css17
-rw-r--r--chromium/content/browser/resources/media/webrtc_internals.js60
-rw-r--r--chromium/content/browser/resources/service_worker/OWNERS5
-rw-r--r--chromium/content/browser/resources/service_worker/serviceworker_internals.css44
-rw-r--r--chromium/content/browser/resources/service_worker/serviceworker_internals.html141
-rw-r--r--chromium/content/browser/resources/service_worker/serviceworker_internals.js304
-rw-r--r--chromium/content/browser/safe_util_win.cc2
-rw-r--r--chromium/content/browser/screen_orientation/OWNERS1
-rw-r--r--chromium/content/browser/screen_orientation/screen_orientation_dispatcher_host.cc85
-rw-r--r--chromium/content/browser/screen_orientation/screen_orientation_dispatcher_host.h50
-rw-r--r--chromium/content/browser/screen_orientation/screen_orientation_provider.h28
-rw-r--r--chromium/content/browser/screen_orientation/screen_orientation_provider_android.cc51
-rw-r--r--chromium/content/browser/screen_orientation/screen_orientation_provider_android.h34
-rw-r--r--chromium/content/browser/security_exploit_browsertest.cc19
-rw-r--r--chromium/content/browser/service_worker/BUILD.gn12
-rw-r--r--chromium/content/browser/service_worker/DEPS3
-rw-r--r--chromium/content/browser/service_worker/OWNERS13
-rw-r--r--chromium/content/browser/service_worker/embedded_worker_instance.cc301
-rw-r--r--chromium/content/browser/service_worker/embedded_worker_instance.h130
-rw-r--r--chromium/content/browser/service_worker/embedded_worker_instance_unittest.cc166
-rw-r--r--chromium/content/browser/service_worker/embedded_worker_registry.cc193
-rw-r--r--chromium/content/browser/service_worker/embedded_worker_registry.h61
-rw-r--r--chromium/content/browser/service_worker/embedded_worker_test_helper.cc241
-rw-r--r--chromium/content/browser/service_worker/embedded_worker_test_helper.h135
-rw-r--r--chromium/content/browser/service_worker/service_worker_browsertest.cc710
-rw-r--r--chromium/content/browser/service_worker/service_worker_context.h28
-rw-r--r--chromium/content/browser/service_worker/service_worker_context_core.cc287
-rw-r--r--chromium/content/browser/service_worker/service_worker_context_core.h140
-rw-r--r--chromium/content/browser/service_worker/service_worker_context_observer.h70
-rw-r--r--chromium/content/browser/service_worker/service_worker_context_request_handler.cc90
-rw-r--r--chromium/content/browser/service_worker/service_worker_context_request_handler.h42
-rw-r--r--chromium/content/browser/service_worker/service_worker_context_unittest.cc301
-rw-r--r--chromium/content/browser/service_worker/service_worker_context_wrapper.cc148
-rw-r--r--chromium/content/browser/service_worker/service_worker_context_wrapper.h41
-rw-r--r--chromium/content/browser/service_worker/service_worker_controllee_request_handler.cc121
-rw-r--r--chromium/content/browser/service_worker/service_worker_controllee_request_handler.h57
-rw-r--r--chromium/content/browser/service_worker/service_worker_database.cc1113
-rw-r--r--chromium/content/browser/service_worker/service_worker_database.h324
-rw-r--r--chromium/content/browser/service_worker/service_worker_database.proto31
-rw-r--r--chromium/content/browser/service_worker/service_worker_database_unittest.cc904
-rw-r--r--chromium/content/browser/service_worker/service_worker_disk_cache.cc22
-rw-r--r--chromium/content/browser/service_worker/service_worker_disk_cache.h55
-rw-r--r--chromium/content/browser/service_worker/service_worker_dispatcher_host.cc380
-rw-r--r--chromium/content/browser/service_worker/service_worker_dispatcher_host.h101
-rw-r--r--chromium/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc252
-rw-r--r--chromium/content/browser/service_worker/service_worker_fetch_dispatcher.cc78
-rw-r--r--chromium/content/browser/service_worker/service_worker_fetch_dispatcher.h57
-rw-r--r--chromium/content/browser/service_worker/service_worker_handle.cc126
-rw-r--r--chromium/content/browser/service_worker/service_worker_handle.h92
-rw-r--r--chromium/content/browser/service_worker/service_worker_handle_unittest.cc114
-rw-r--r--chromium/content/browser/service_worker/service_worker_histograms.cc30
-rw-r--r--chromium/content/browser/service_worker/service_worker_histograms.h38
-rw-r--r--chromium/content/browser/service_worker/service_worker_info.cc57
-rw-r--r--chromium/content/browser/service_worker/service_worker_info.h56
-rw-r--r--chromium/content/browser/service_worker/service_worker_internals_ui.cc675
-rw-r--r--chromium/content/browser/service_worker/service_worker_internals_ui.h74
-rw-r--r--chromium/content/browser/service_worker/service_worker_job_coordinator.cc112
-rw-r--r--chromium/content/browser/service_worker/service_worker_job_coordinator.h85
-rw-r--r--chromium/content/browser/service_worker/service_worker_job_unittest.cc702
-rw-r--r--chromium/content/browser/service_worker/service_worker_process_manager.cc193
-rw-r--r--chromium/content/browser/service_worker/service_worker_process_manager.h118
-rw-r--r--chromium/content/browser/service_worker/service_worker_proto.gyp17
-rw-r--r--chromium/content/browser/service_worker/service_worker_provider_host.cc165
-rw-r--r--chromium/content/browser/service_worker/service_worker_provider_host.h101
-rw-r--r--chromium/content/browser/service_worker/service_worker_provider_host_unittest.cc283
-rw-r--r--chromium/content/browser/service_worker/service_worker_read_from_cache_job.cc191
-rw-r--r--chromium/content/browser/service_worker/service_worker_read_from_cache_job.h73
-rw-r--r--chromium/content/browser/service_worker/service_worker_register_job.cc538
-rw-r--r--chromium/content/browser/service_worker/service_worker_register_job.h177
-rw-r--r--chromium/content/browser/service_worker/service_worker_register_job_base.h37
-rw-r--r--chromium/content/browser/service_worker/service_worker_registration.cc52
-rw-r--r--chromium/content/browser/service_worker/service_worker_registration.h30
-rw-r--r--chromium/content/browser/service_worker/service_worker_registration_status.cc42
-rw-r--r--chromium/content/browser/service_worker/service_worker_registration_status.h13
-rw-r--r--chromium/content/browser/service_worker/service_worker_registration_unittest.cc63
-rw-r--r--chromium/content/browser/service_worker/service_worker_request_handler.cc111
-rw-r--r--chromium/content/browser/service_worker/service_worker_request_handler.h83
-rw-r--r--chromium/content/browser/service_worker/service_worker_script_cache_map.cc72
-rw-r--r--chromium/content/browser/service_worker/service_worker_script_cache_map.h62
-rw-r--r--chromium/content/browser/service_worker/service_worker_storage.cc1057
-rw-r--r--chromium/content/browser/service_worker/service_worker_storage.h322
-rw-r--r--chromium/content/browser/service_worker/service_worker_storage_unittest.cc899
-rw-r--r--chromium/content/browser/service_worker/service_worker_test_utils.h39
-rw-r--r--chromium/content/browser/service_worker/service_worker_unregister_job.cc90
-rw-r--r--chromium/content/browser/service_worker/service_worker_unregister_job.h64
-rw-r--r--chromium/content/browser/service_worker/service_worker_url_request_job.cc304
-rw-r--r--chromium/content/browser/service_worker/service_worker_url_request_job.h137
-rw-r--r--chromium/content/browser/service_worker/service_worker_url_request_job_unittest.cc238
-rw-r--r--chromium/content/browser/service_worker/service_worker_utils.cc65
-rw-r--r--chromium/content/browser/service_worker/service_worker_utils.h55
-rw-r--r--chromium/content/browser/service_worker/service_worker_utils_unittest.cc103
-rw-r--r--chromium/content/browser/service_worker/service_worker_version.cc627
-rw-r--r--chromium/content/browser/service_worker/service_worker_version.h280
-rw-r--r--chromium/content/browser/service_worker/service_worker_version_unittest.cc355
-rw-r--r--chromium/content/browser/service_worker/service_worker_write_to_cache_job.cc327
-rw-r--r--chromium/content/browser/service_worker/service_worker_write_to_cache_job.h117
-rw-r--r--chromium/content/browser/session_history_browsertest.cc26
-rw-r--r--chromium/content/browser/shared_worker/OWNERS3
-rw-r--r--chromium/content/browser/shared_worker/shared_worker_host.cc336
-rw-r--r--chromium/content/browser/shared_worker/shared_worker_host.h135
-rw-r--r--chromium/content/browser/shared_worker/shared_worker_instance.cc68
-rw-r--r--chromium/content/browser/shared_worker/shared_worker_instance.h70
-rw-r--r--chromium/content/browser/shared_worker/shared_worker_instance_unittest.cc83
-rw-r--r--chromium/content/browser/shared_worker/shared_worker_message_filter.cc168
-rw-r--r--chromium/content/browser/shared_worker/shared_worker_message_filter.h78
-rw-r--r--chromium/content/browser/shared_worker/shared_worker_service_impl.cc657
-rw-r--r--chromium/content/browser/shared_worker/shared_worker_service_impl.h176
-rw-r--r--chromium/content/browser/shared_worker/shared_worker_service_impl_unittest.cc905
-rw-r--r--chromium/content/browser/site_instance_impl.cc19
-rw-r--r--chromium/content/browser/site_instance_impl.h12
-rw-r--r--chromium/content/browser/site_instance_impl_unittest.cc18
-rw-r--r--chromium/content/browser/site_per_process_browsertest.cc546
-rw-r--r--chromium/content/browser/speech/endpointer/energy_endpointer.cc2
-rw-r--r--chromium/content/browser/speech/google_one_shot_remote_engine.cc10
-rw-r--r--chromium/content/browser/speech/google_one_shot_remote_engine_unittest.cc6
-rw-r--r--chromium/content/browser/speech/google_streaming_remote_engine.cc39
-rw-r--r--chromium/content/browser/speech/google_streaming_remote_engine_unittest.cc24
-rw-r--r--chromium/content/browser/speech/input_tag_speech_browsertest.cc136
-rw-r--r--chromium/content/browser/speech/input_tag_speech_dispatcher_host.cc227
-rw-r--r--chromium/content/browser/speech/input_tag_speech_dispatcher_host.h93
-rw-r--r--chromium/content/browser/speech/proto/BUILD.gn11
-rw-r--r--chromium/content/browser/speech/proto/speech_proto.gyp4
-rw-r--r--chromium/content/browser/speech/speech_recognition_browsertest.cc17
-rw-r--r--chromium/content/browser/speech/speech_recognition_dispatcher_host.cc43
-rw-r--r--chromium/content/browser/speech/speech_recognition_dispatcher_host.h6
-rw-r--r--chromium/content/browser/speech/speech_recognition_manager_impl.cc10
-rw-r--r--chromium/content/browser/speech/speech_recognizer_impl.cc40
-rw-r--r--chromium/content/browser/speech/speech_recognizer_impl.h13
-rw-r--r--chromium/content/browser/speech/speech_recognizer_impl_unittest.cc60
-rw-r--r--chromium/content/browser/ssl/ssl_cert_error_handler.cc4
-rw-r--r--chromium/content/browser/ssl/ssl_cert_error_handler.h2
-rw-r--r--chromium/content/browser/ssl/ssl_client_auth_handler.cc12
-rw-r--r--chromium/content/browser/ssl/ssl_client_auth_handler.h2
-rw-r--r--chromium/content/browser/ssl/ssl_error_handler.cc13
-rw-r--r--chromium/content/browser/ssl/ssl_error_handler.h6
-rw-r--r--chromium/content/browser/ssl/ssl_manager.cc6
-rw-r--r--chromium/content/browser/ssl/ssl_manager.h2
-rw-r--r--chromium/content/browser/ssl/ssl_policy.cc13
-rw-r--r--chromium/content/browser/storage_partition_impl.cc55
-rw-r--r--chromium/content/browser/storage_partition_impl.h7
-rw-r--r--chromium/content/browser/storage_partition_impl_map.cc55
-rw-r--r--chromium/content/browser/storage_partition_impl_map.h4
-rw-r--r--chromium/content/browser/storage_partition_impl_map_unittest.cc37
-rw-r--r--chromium/content/browser/storage_partition_impl_unittest.cc460
-rw-r--r--chromium/content/browser/streams/stream.cc11
-rw-r--r--chromium/content/browser/streams/stream.h7
-rw-r--r--chromium/content/browser/streams/stream_context.cc1
-rw-r--r--chromium/content/browser/streams/stream_context.h1
-rw-r--r--chromium/content/browser/streams/stream_handle_impl.cc36
-rw-r--r--chromium/content/browser/streams/stream_handle_impl.h14
-rw-r--r--chromium/content/browser/streams/stream_url_request_job.cc2
-rw-r--r--chromium/content/browser/streams/stream_url_request_job_unittest.cc8
-rw-r--r--chromium/content/browser/theme_helper_mac.h6
-rw-r--r--chromium/content/browser/theme_helper_mac.mm83
-rw-r--r--chromium/content/browser/time_zone_monitor.cc31
-rw-r--r--chromium/content/browser/time_zone_monitor.h51
-rw-r--r--chromium/content/browser/time_zone_monitor_android.cc38
-rw-r--r--chromium/content/browser/time_zone_monitor_android.h37
-rw-r--r--chromium/content/browser/time_zone_monitor_chromeos.cc37
-rw-r--r--chromium/content/browser/time_zone_monitor_linux.cc166
-rw-r--r--chromium/content/browser/time_zone_monitor_mac.mm40
-rw-r--r--chromium/content/browser/time_zone_monitor_win.cc46
-rw-r--r--chromium/content/browser/tracing/BUILD.gn66
-rw-r--r--chromium/content/browser/tracing/etw_system_event_consumer_win.cc215
-rw-r--r--chromium/content/browser/tracing/etw_system_event_consumer_win.h75
-rwxr-xr-xchromium/content/browser/tracing/generate_trace_viewer_grd.py1
-rw-r--r--chromium/content/browser/tracing/trace_message_filter.cc25
-rw-r--r--chromium/content/browser/tracing/trace_message_filter.h3
-rw-r--r--chromium/content/browser/tracing/tracing_controller_browsertest.cc50
-rw-r--r--chromium/content/browser/tracing/tracing_controller_impl.cc427
-rw-r--r--chromium/content/browser/tracing/tracing_controller_impl.h50
-rw-r--r--chromium/content/browser/tracing/tracing_resources.gyp8
-rw-r--r--chromium/content/browser/tracing/tracing_ui.cc161
-rw-r--r--chromium/content/browser/tracing/tracing_ui.h4
-rw-r--r--chromium/content/browser/user_metrics.cc47
-rw-r--r--chromium/content/browser/utility_process_host_impl.cc92
-rw-r--r--chromium/content/browser/utility_process_host_impl.h13
-rw-r--r--chromium/content/browser/vibration/vibration_message_filter.cc18
-rw-r--r--chromium/content/browser/vibration/vibration_message_filter.h3
-rw-r--r--chromium/content/browser/vibration/vibration_provider_android.cc1
-rw-r--r--chromium/content/browser/vibration/vibration_provider_android.h2
-rw-r--r--chromium/content/browser/web_contents/OWNERS4
-rw-r--r--chromium/content/browser/web_contents/aura/gesture_nav_simple.cc232
-rw-r--r--chromium/content/browser/web_contents/aura/gesture_nav_simple.h55
-rw-r--r--chromium/content/browser/web_contents/aura/image_window_delegate.cc8
-rw-r--r--chromium/content/browser/web_contents/aura/image_window_delegate.h9
-rw-r--r--chromium/content/browser/web_contents/aura/overscroll_navigation_overlay.cc302
-rw-r--r--chromium/content/browser/web_contents/aura/overscroll_navigation_overlay.h128
-rw-r--r--chromium/content/browser/web_contents/aura/overscroll_navigation_overlay_unittest.cc197
-rw-r--r--chromium/content/browser/web_contents/aura/shadow_layer_delegate.cc2
-rw-r--r--chromium/content/browser/web_contents/aura/window_slider.cc129
-rw-r--r--chromium/content/browser/web_contents/aura/window_slider.h58
-rw-r--r--chromium/content/browser/web_contents/aura/window_slider_unittest.cc329
-rw-r--r--chromium/content/browser/web_contents/drag_utils_gtk.cc38
-rw-r--r--chromium/content/browser/web_contents/drag_utils_gtk.h24
-rw-r--r--chromium/content/browser/web_contents/opened_by_dom_browsertest.cc139
-rw-r--r--chromium/content/browser/web_contents/touch_editable_impl_aura.cc117
-rw-r--r--chromium/content/browser/web_contents/touch_editable_impl_aura.h17
-rw-r--r--chromium/content/browser/web_contents/touch_editable_impl_aura_browsertest.cc522
-rw-r--r--chromium/content/browser/web_contents/web_contents_android.cc11
-rw-r--r--chromium/content/browser/web_contents/web_contents_android.h5
-rw-r--r--chromium/content/browser/web_contents/web_contents_drag_win.cc443
-rw-r--r--chromium/content/browser/web_contents/web_contents_drag_win.h122
-rw-r--r--chromium/content/browser/web_contents/web_contents_impl.cc2484
-rw-r--r--chromium/content/browser/web_contents/web_contents_impl.h524
-rw-r--r--chromium/content/browser/web_contents/web_contents_impl_browsertest.cc262
-rw-r--r--chromium/content/browser/web_contents/web_contents_impl_unittest.cc637
-rw-r--r--chromium/content/browser/web_contents/web_contents_view.h137
-rw-r--r--chromium/content/browser/web_contents/web_contents_view_android.cc60
-rw-r--r--chromium/content/browser/web_contents/web_contents_view_android.h26
-rw-r--r--chromium/content/browser/web_contents/web_contents_view_aura.cc673
-rw-r--r--chromium/content/browser/web_contents/web_contents_view_aura.h57
-rw-r--r--chromium/content/browser/web_contents/web_contents_view_aura_browsertest.cc244
-rw-r--r--chromium/content/browser/web_contents/web_contents_view_gtk.cc431
-rw-r--r--chromium/content/browser/web_contents/web_contents_view_gtk.h147
-rw-r--r--chromium/content/browser/web_contents/web_contents_view_guest.cc101
-rw-r--r--chromium/content/browser/web_contents/web_contents_view_guest.h49
-rw-r--r--chromium/content/browser/web_contents/web_contents_view_mac.h21
-rw-r--r--chromium/content/browser/web_contents/web_contents_view_mac.mm68
-rw-r--r--chromium/content/browser/web_contents/web_contents_view_mac_unittest.mm2
-rw-r--r--chromium/content/browser/web_contents/web_contents_view_overscroll_animator_mac.h61
-rw-r--r--chromium/content/browser/web_contents/web_contents_view_overscroll_animator_slider_mac.h59
-rw-r--r--chromium/content/browser/web_contents/web_contents_view_overscroll_animator_slider_mac.mm259
-rw-r--r--chromium/content/browser/web_contents/web_contents_view_win.cc465
-rw-r--r--chromium/content/browser/web_contents/web_contents_view_win.h147
-rw-r--r--chromium/content/browser/web_contents/web_drag_dest_gtk.cc339
-rw-r--r--chromium/content/browser/web_contents/web_drag_dest_gtk.h114
-rw-r--r--chromium/content/browser/web_contents/web_drag_dest_mac.mm9
-rw-r--r--chromium/content/browser/web_contents/web_drag_dest_mac_unittest.mm4
-rw-r--r--chromium/content/browser/web_contents/web_drag_dest_win.cc286
-rw-r--r--chromium/content/browser/web_contents/web_drag_dest_win.h89
-rw-r--r--chromium/content/browser/web_contents/web_drag_source_gtk.cc403
-rw-r--r--chromium/content/browser/web_contents/web_drag_source_gtk.h115
-rw-r--r--chromium/content/browser/web_contents/web_drag_source_mac.h3
-rw-r--r--chromium/content/browser/web_contents/web_drag_source_mac.mm51
-rw-r--r--chromium/content/browser/web_contents/web_drag_source_win.cc130
-rw-r--r--chromium/content/browser/web_contents/web_drag_source_win.h79
-rw-r--r--chromium/content/browser/webkit_browsertest.cc19
-rw-r--r--chromium/content/browser/webui/content_web_ui_controller_factory.cc17
-rw-r--r--chromium/content/browser/webui/generic_handler.cc2
-rw-r--r--chromium/content/browser/webui/shared_resources_data_source.cc2
-rw-r--r--chromium/content/browser/webui/shared_resources_data_source.h2
-rw-r--r--chromium/content/browser/webui/url_data_manager_backend.cc164
-rw-r--r--chromium/content/browser/webui/url_data_manager_backend.h10
-rw-r--r--chromium/content/browser/webui/web_ui_controller_factory_registry.cc19
-rw-r--r--chromium/content/browser/webui/web_ui_controller_factory_registry.h3
-rw-r--r--chromium/content/browser/webui/web_ui_data_source_impl.cc32
-rw-r--r--chromium/content/browser/webui/web_ui_data_source_impl.h2
-rw-r--r--chromium/content/browser/webui/web_ui_data_source_unittest.cc32
-rw-r--r--chromium/content/browser/webui/web_ui_impl.cc98
-rw-r--r--chromium/content/browser/webui/web_ui_impl.h24
-rw-r--r--chromium/content/browser/webui/web_ui_message_handler.cc7
-rw-r--r--chromium/content/browser/webui/web_ui_message_handler_unittest.cc16
-rw-r--r--chromium/content/browser/webui/web_ui_mojo_browsertest.cc218
-rw-r--r--chromium/content/browser/worker_host/OWNERS2
-rw-r--r--chromium/content/browser/worker_host/worker_document_set.cc28
-rw-r--r--chromium/content/browser/worker_host/worker_document_set.h28
-rw-r--r--chromium/content/browser/worker_host/worker_message_filter.cc32
-rw-r--r--chromium/content/browser/worker_host/worker_message_filter.h8
-rw-r--r--chromium/content/browser/worker_host/worker_process_host.cc385
-rw-r--r--chromium/content/browser/worker_host/worker_process_host.h78
-rw-r--r--chromium/content/browser/worker_host/worker_service_impl.cc310
-rw-r--r--chromium/content/browser/worker_host/worker_service_impl.h44
-rw-r--r--chromium/content/browser/worker_host/worker_storage_partition.cc38
-rw-r--r--chromium/content/browser/worker_host/worker_storage_partition.h34
-rw-r--r--chromium/content/browser/zygote_host/zygote_host_impl_linux.cc191
-rw-r--r--chromium/content/browser/zygote_host/zygote_host_impl_linux.h1
1405 files changed, 126936 insertions, 62619 deletions
diff --git a/chromium/content/browser/BUILD.gn b/chromium/content/browser/BUILD.gn
new file mode 100644
index 00000000000..d76f458c7c6
--- /dev/null
+++ b/chromium/content/browser/BUILD.gn
@@ -0,0 +1,370 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/features.gni")
+import("//build/config/ui.gni")
+import("//content/browser/browser.gni")
+
+source_set("browser") {
+ # Only targets in the content tree can depend directly on this target.
+ visibility = [ "//content/*" ]
+
+ defines = []
+ libs = []
+ ldflags = []
+
+ # Shared deps. See also non-iOS deps below.
+ deps = [
+ "//base",
+ "//content:resources",
+ "//content/browser/service_worker:database_proto",
+ "//content/browser/speech/proto",
+ "//crypto",
+ "//google_apis",
+ "//net",
+ "//skia",
+ "//sql",
+ "//third_party/re2",
+ "//third_party/WebKit/public:blink_headers",
+ "//third_party/zlib",
+ "//third_party/zlib:zip",
+ "//ui/accessibility",
+ "//ui/accessibility:ax_gen",
+ "//ui/base",
+ "//ui/events",
+ "//ui/events:gesture_detection",
+ "//ui/gfx",
+ "//ui/gfx/geometry",
+ "//ui/resources",
+ "//ui/snapshot",
+ ]
+
+ 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/webkit/grit/devtools_resources.h",
+ "$root_gen_dir/webkit/grit/devtools_resources_map.cc",
+ "$root_gen_dir/webkit/grit/devtools_resources_map.h",
+ "$root_gen_dir/content/browser/tracing/grit/tracing_resources.h",
+ "$root_gen_dir/ui/ui_resources/grit/webui_resources_map.cc",
+ "$root_gen_dir/content/browser/devtools/devtools_protocol_constants.cc",
+ "$root_gen_dir/content/browser/devtools/devtools_protocol_constants.h",
+ ], ".")
+
+ # Non-iOS deps.
+ deps += [
+ "//content/browser/devtools:resources",
+ "//content/common:mojo_bindings",
+ "//cc",
+ "//cc:cc_surfaces",
+ "//mojo/public/cpp/bindings",
+ "//mojo/public/interfaces/service_provider:service_provider",
+ "//mojo/public/js/bindings",
+ "//net:http_server",
+ "//third_party/icu",
+ "//third_party/leveldatabase",
+ "//third_party/libyuv",
+ "//ui/resources",
+ "//ui/surface",
+ "//ui/webui/resources",
+ "//webkit:resources",
+ "//webkit:strings",
+ "//webkit/browser:storage",
+ "//webkit/common",
+ "//webkit/common:storage",
+ # TODO(GYP)
+ #"//third_party/angle:commit_id",
+ ]
+ }
+
+ configs += [
+ "//content:content_implementation",
+ "//content:libjingle_stub_config", # TODO(GYP) remove when libjingle is ready.
+ "//content:webrtc_stub_config", # TODO(GYP) remove when webrtc is ready.
+ ]
+
+ if (toolkit_views) {
+ deps += [ "//ui/events" ]
+ }
+
+ if (is_win) {
+ sources += [
+ "power_profiler/power_data_provider_ia_win.cc",
+ "power_profiler/power_data_provider_ia_win.h",
+ ]
+ deps += [ "//third_party/power_gadget" ]
+ } else {
+ sources += [ "power_profiler/power_data_provider_dummy.cc" ]
+ sources -= [ "renderer_host/web_input_event_aurawin.cc" ]
+ }
+
+ if (!is_win && !is_mac && (!is_linux || !use_udev)) {
+ sources += [ "gamepad/gamepad_platform_data_fetcher.cc" ]
+ }
+
+ if (enable_printing != 0) {
+ deps += [ "//printing" ]
+ }
+
+# TODO(GYP)
+# ['OS!="ios" and chrome_multiple_dll!=1', {
+# 'dependencies': [
+# '../third_party/WebKit/public/blink.gyp:blink',
+# ],
+# }],
+ if (!is_mac && !is_ios) {
+ deps += [ "//sandbox" ]
+ }
+ if (!is_android && !is_ios) {
+ deps += [ "//content/browser/tracing:resources" ]
+ }
+
+ if (enable_webrtc) {
+ sources += rebase_path(content_browser_gypi_values.webrtc_browser_sources,
+ ".", "//content")
+ # TODO(GYP)
+ #deps += [ "//jingle:glue" ]
+ if (is_linux) {
+ # TODO(GYP)
+ #deps += [ "//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_aura.cc",
+ "media/capture/desktop_capture_device_aura.h",
+ "media/capture/desktop_capture_device_uma_types.cc",
+ "media/capture/desktop_capture_device_uma_types.h",
+ ]
+ defines += [ "ENABLE_SCREEN_CAPTURE=1" ]
+ # TODO(GYP)
+ #deps += [ "//third_party/webrtc/modules:desktop_capture" ]
+ }
+ }
+
+ if (is_win) {
+ sources -= [
+ "device_sensors/data_fetcher_shared_memory_default.cc",
+ "geolocation/empty_wifi_data_provider.cc",
+ ]
+ defines += [
+ # This prevents the inclusion of atlhost.h which paired
+ # with the windows 8 sdk it does the wrong thing.
+ "__ATLHOST_H__",
+ ]
+ deps += [
+ "//third_party/iaccessible2",
+ "//third_party/isimpledom",
+ ]
+ libs += [
+ "comctl32.lib",
+ "dinput8.lib",
+ "dwmapi.lib",
+ "dxguid.lib",
+ "sensorsapi.lib",
+ "portabledeviceguids.lib",
+ ]
+ # TODI(GYP)
+# 'msvs_settings': {
+# 'VCLinkerTool': {
+# 'DelayLoadDLLs': [
+# 'dinput8.dll',
+# 'user32.dll',
+# 'dwmapi.dll',
+# ],
+ }
+
+ if (is_linux) {
+ deps += [ "//sandbox/linux:libc_urandom_override" ]
+ }
+
+ if (use_udev) {
+ configs += [ "//build/config/linux:udev" ]
+ } else {
+ # Remove udev-specific sources.
+ sources -= [
+ "device_monitor_udev.cc",
+ "device_monitor_udev.h",
+ ]
+ if (is_linux) {
+ # Already filtered out on non-Linux.
+ sources -= [
+ "gamepad/gamepad_platform_data_fetcher_linux.cc",
+ "udev_linux.cc",
+ "udev_linux.h",
+ ]
+ }
+ }
+
+ if (enable_plugins) {
+ sources += rebase_path(content_browser_gypi_values.plugin_browser_sources,
+ ".", "//content")
+ deps += [
+ "//ppapi:ppapi_ipc",
+ "//ppapi:ppapi_shared",
+ ]
+ if (!use_ozone || use_pango) {
+ sources -= [ "renderer_host/pepper/pepper_truetype_font_list_ozone.cc" ]
+ }
+ }
+
+ if (is_linux && use_aura) {
+ configs += [ "//build/config/linux:fontconfig" ]
+ }
+
+ if (use_x11) {
+ configs += [ "//build/config/linux:x11" ]
+ } else {
+ sources -= [
+ "power_save_blocker_x11.cc",
+ "renderer_host/web_input_event_aurax11.cc",
+ ]
+ }
+
+ if (use_pango) {
+ configs += [ "//build/config/linux:pangocairo" ]
+ }
+
+ if (is_android) {
+ sources += rebase_path(content_browser_gypi_values.android_browser_sources,
+ ".", "//content")
+ sources -= [
+ "battery_status/battery_status_manager_default.cc",
+ "browser_ipc_logging.cc",
+ "device_sensors/data_fetcher_shared_memory_default.cc",
+ "font_list_async.cc",
+ "geolocation/device_data_provider.cc",
+ "geolocation/empty_device_data_provider.cc",
+ "geolocation/network_location_provider.cc",
+ "geolocation/network_location_provider.h",
+ "geolocation/network_location_request.cc",
+ "geolocation/network_location_request.h",
+ "geolocation/wifi_data_provider_common.cc",
+ "renderer_host/native_web_keyboard_event.cc",
+ "tracing/tracing_ui.cc",
+ "tracing/tracing_ui.h",
+
+ # Android skips most, but not all, of the speech code.
+ "speech/audio_buffer.cc",
+ "speech/audio_buffer.h",
+ "speech/audio_encoder.cc",
+ "speech/audio_encoder.h",
+ "speech/chunked_byte_buffer.cc",
+ "speech/chunked_byte_buffer.h",
+ "speech/endpointer/endpointer.cc",
+ "speech/endpointer/endpointer.h",
+ "speech/endpointer/energy_endpointer.cc",
+ "speech/endpointer/energy_endpointer.h",
+ "speech/endpointer/energy_endpointer_params.cc",
+ "speech/endpointer/energy_endpointer_params.h",
+ "speech/google_one_shot_remote_engine.cc",
+ "speech/google_one_shot_remote_engine.h",
+ "speech/google_streaming_remote_engine.cc",
+ "speech/google_streaming_remote_engine.h",
+ "speech/speech_recognition_engine.cc",
+ "speech/speech_recognition_engine.h",
+ "speech/speech_recognizer_impl.cc",
+ "speech/speech_recognizer_impl.h",
+ ]
+ deps += [
+ #"//content:jni_headers", TODO(GYP)
+ #"//media", TODO(GYP)
+ ]
+ libs += [ "jnigraphics" ]
+ }
+
+ if (is_mac) {
+ sources -= [
+ "device_sensors/data_fetcher_shared_memory_default.cc",
+ "geolocation/empty_wifi_data_provider.cc",
+ "geolocation/empty_wifi_data_provider.h",
+ ]
+ libs += [ "bsm" ]
+ }
+
+ if (is_chromeos) {
+ sources -= [
+ "geolocation/wifi_data_provider_linux.cc",
+ "power_save_blocker_ozone.cc",
+ "power_save_blocker_x11.cc",
+ ]
+ deps += [ "//chromeos:power_manager_proto" ]
+ }
+
+ if (use_aura) {
+ deps += [
+ "//ui/aura",
+ "//ui/strings",
+ ]
+ } else { # Not aura.
+ sources -= [
+ "renderer_host/render_widget_host_view_aura.cc",
+ "renderer_host/render_widget_host_view_aura.h",
+ "web_contents/touch_editable_impl_aura.cc",
+ "web_contents/touch_editable_impl_aura.h",
+ "renderer_host/ui_events_helper.cc",
+ "renderer_host/ui_events_helper.h",
+ ]
+ }
+
+ if (use_aura || is_mac) {
+ sources += rebase_path(
+ content_browser_gypi_values.compositor_browser_sources,
+ ".", "//content")
+ if (!use_x11) {
+ sources -= [
+ "compositor/software_output_device_x11.cc",
+ "compositor/software_output_device_x11.h",
+ ]
+ }
+ deps += [ "//ui/compositor" ]
+ }
+
+ if (enable_speech_input) {
+ deps += [
+ "//third_party/flac",
+ "//third_party/speex",
+ ]
+ }
+
+ if (is_linux) {
+ if (use_dbus) {
+ sources -= [
+ "geolocation/empty_wifi_data_provider.cc",
+ ]
+ deps += [ "//dbus" ]
+ } else {
+ # This will already have gotten removed for all non-Linux cases.
+ sources -= [ "geolocation/wifi_data_provider_linux.cc" ]
+ }
+ }
+}
diff --git a/chromium/content/browser/DEPS b/chromium/content/browser/DEPS
index 6e6295235ac..1af4237d1f0 100644
--- a/chromium/content/browser/DEPS
+++ b/chromium/content/browser/DEPS
@@ -1,7 +1,5 @@
include_rules = [
- "+content/port/browser",
"+content/public/browser",
- "+content/child/webkitplatformsupport_impl.h", # For in-process webkit
"+media/audio", # For audio input for speech input feature.
"+media/base", # For Android JNI registration.
"+media/midi", # For Web MIDI API
@@ -19,6 +17,7 @@ include_rules = [
"+third_party/iaccessible2",
"+third_party/isimpledom",
"+third_party/khronos", # For enum definitions only
+ "+third_party/power_gadget",
"+third_party/speex",
"+third_party/re2",
@@ -32,19 +31,27 @@ 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/WebBatteryStatus.h",
"+third_party/WebKit/public/platform/WebCursorInfo.h",
+ "+third_party/WebKit/public/platform/WebGamepad.h",
"+third_party/WebKit/public/platform/WebGamepads.h",
"+third_party/WebKit/public/platform/WebGraphicsContext3D.h",
"+third_party/WebKit/public/platform/WebIDBDatabaseException.h",
"+third_party/WebKit/public/platform/WebIDBTypes.h",
"+third_party/WebKit/public/platform/WebReferrerPolicy.h",
+ "+third_party/WebKit/public/platform/WebScreenOrientationLockType.h",
+ "+third_party/WebKit/public/platform/WebScreenOrientationType.h",
"+third_party/WebKit/public/platform/WebScreenInfo.h",
"+third_party/WebKit/public/platform/WebServiceWorkerError.h",
+ "+third_party/WebKit/public/platform/WebServiceWorkerEventResult.h",
+ "+third_party/WebKit/public/platform/WebServiceWorkerState.h",
"+third_party/WebKit/public/platform/WebString.h",
"+third_party/WebKit/public/platform/WebVibration.h",
+ "+third_party/WebKit/public/web/mac/WebScrollbarTheme.h",
"+third_party/WebKit/public/web/WebAXEnums.h",
"+third_party/WebKit/public/web/WebCompositionUnderline.h",
"+third_party/WebKit/public/web/WebConsoleMessage.h",
+ "+third_party/WebKit/public/web/WebContentSecurityPolicy.h",
"+third_party/WebKit/public/web/WebDragOperation.h",
"+third_party/WebKit/public/web/WebDragStatus.h",
"+third_party/WebKit/public/web/WebFindOptions.h",
@@ -60,6 +67,7 @@ include_rules = [
# These should be burned down. http://crbug.com/237267
"!third_party/WebKit/public/web/mac/WebInputEventFactory.h",
+ "+ui/ozone/ozone_platform.h",
"+ui/ozone/ozone_switches.h",
# DO NOT ADD ANY CHROME OR COMPONENTS INCLUDES HERE!!!
diff --git a/chromium/content/browser/accessibility/OWNERS b/chromium/content/browser/accessibility/OWNERS
index 11e8fd837ee..c50d5b87619 100644
--- a/chromium/content/browser/accessibility/OWNERS
+++ b/chromium/content/browser/accessibility/OWNERS
@@ -1,2 +1,3 @@
+aboxhall@chromium.org
dmazzoni@chromium.org
dtseng@chromium.org
diff --git a/chromium/content/browser/accessibility/accessibility_mode_browsertest.cc b/chromium/content/browser/accessibility/accessibility_mode_browsertest.cc
new file mode 100644
index 00000000000..5e191bd0b0e
--- /dev/null
+++ b/chromium/content/browser/accessibility/accessibility_mode_browsertest.cc
@@ -0,0 +1,103 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/render_view_host_impl.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_view_host.h"
+#include "content/public/browser/render_widget_host.h"
+#include "content/public/browser/render_widget_host_view.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/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 {
+
+const char kMinimalPageDataURL[] =
+ "data:text/html,<html><head></head><body>Hello, world</body></html>";
+
+class AccessibilityModeTest : public ContentBrowserTest {
+ protected:
+ WebContents* web_contents() {
+ return shell()->web_contents();
+ }
+
+ RenderWidgetHostImpl* rwhi() {
+ RenderWidgetHost* rwh =
+ web_contents()->GetRenderWidgetHostView()->GetRenderWidgetHost();
+ return RenderWidgetHostImpl::From(rwh);
+ }
+
+ RenderWidgetHostViewBase* host_view() {
+ return static_cast<RenderWidgetHostViewBase*>(
+ shell()->web_contents()->GetRenderWidgetHostView());
+ }
+
+ void ExpectBrowserAccessibilityManager(bool expect_bam,
+ std::string message = "") {
+ if (expect_bam) {
+ EXPECT_NE((BrowserAccessibilityManager*)NULL,
+ host_view()->GetBrowserAccessibilityManager()) << message;
+ } else {
+ EXPECT_EQ((BrowserAccessibilityManager*)NULL,
+ host_view()->GetBrowserAccessibilityManager()) << message;
+ }
+ }
+};
+
+IN_PROC_BROWSER_TEST_F(AccessibilityModeTest, AccessibilityModeOff) {
+ NavigateToURL(shell(), GURL(kMinimalPageDataURL));
+
+ EXPECT_EQ(AccessibilityModeOff, rwhi()->accessibility_mode());
+ ExpectBrowserAccessibilityManager(false);
+}
+
+IN_PROC_BROWSER_TEST_F(AccessibilityModeTest, AccessibilityModeComplete) {
+ NavigateToURL(shell(), GURL(kMinimalPageDataURL));
+ ASSERT_EQ(AccessibilityModeOff, rwhi()->accessibility_mode());
+
+ AccessibilityNotificationWaiter waiter(shell());
+ rwhi()->AddAccessibilityMode(AccessibilityModeComplete);
+ EXPECT_EQ(AccessibilityModeComplete, rwhi()->accessibility_mode());
+ waiter.WaitForNotification();
+ ExpectBrowserAccessibilityManager(true);
+}
+
+IN_PROC_BROWSER_TEST_F(AccessibilityModeTest, AccessibilityModeTreeOnly) {
+ NavigateToURL(shell(), GURL(kMinimalPageDataURL));
+ ASSERT_EQ(AccessibilityModeOff, rwhi()->accessibility_mode());
+
+ AccessibilityNotificationWaiter waiter(shell());
+ rwhi()->AddAccessibilityMode(AccessibilityModeTreeOnly);
+ EXPECT_EQ(AccessibilityModeTreeOnly, rwhi()->accessibility_mode());
+ waiter.WaitForNotification();
+ // No BrowserAccessibilityManager expected for AccessibilityModeTreeOnly
+ ExpectBrowserAccessibilityManager(false);
+}
+
+IN_PROC_BROWSER_TEST_F(AccessibilityModeTest, AddingModes) {
+ NavigateToURL(shell(), GURL(kMinimalPageDataURL));
+
+ AccessibilityNotificationWaiter waiter(shell());
+ rwhi()->AddAccessibilityMode(AccessibilityModeTreeOnly);
+ EXPECT_EQ(AccessibilityModeTreeOnly, rwhi()->accessibility_mode());
+ waiter.WaitForNotification();
+ ExpectBrowserAccessibilityManager(false,
+ "Should be no BrowserAccessibilityManager "
+ "for AccessibilityModeTreeOnly");
+
+ AccessibilityNotificationWaiter waiter2(shell());
+ rwhi()->AddAccessibilityMode(AccessibilityModeComplete);
+ EXPECT_EQ(AccessibilityModeComplete, rwhi()->accessibility_mode());
+ waiter2.WaitForNotification();
+ ExpectBrowserAccessibilityManager(true,
+ "Should be a BrowserAccessibilityManager "
+ "for AccessibilityModeComplete");
+}
+
+} // namespace content
diff --git a/chromium/content/browser/accessibility/accessibility_mode_helper.cc b/chromium/content/browser/accessibility/accessibility_mode_helper.cc
new file mode 100644
index 00000000000..7988595aa21
--- /dev/null
+++ b/chromium/content/browser/accessibility/accessibility_mode_helper.cc
@@ -0,0 +1,56 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/accessibility/accessibility_mode_helper.h"
+
+#if defined(OS_WIN)
+#include "base/command_line.h"
+#include "base/win/windows_version.h"
+#include "content/public/common/content_switches.h"
+#endif
+
+namespace content {
+
+namespace {
+
+AccessibilityMode CastToAccessibilityMode(unsigned int int_mode) {
+ AccessibilityMode mode = static_cast<AccessibilityMode>(int_mode);
+ switch (mode) {
+ case AccessibilityModeOff:
+ case AccessibilityModeComplete:
+ case AccessibilityModeEditableTextOnly:
+ case AccessibilityModeTreeOnly:
+ return mode;
+ }
+ DCHECK(false) << "Could not convert to AccessibilityMode: " << int_mode;
+ return AccessibilityModeOff;
+}
+
+} // namespace
+
+AccessibilityMode AddAccessibilityModeTo(AccessibilityMode to,
+ AccessibilityMode mode_to_add) {
+ return CastToAccessibilityMode(to | mode_to_add);
+}
+
+AccessibilityMode RemoveAccessibilityModeFrom(
+ AccessibilityMode from,
+ AccessibilityMode mode_to_remove) {
+ unsigned int new_mode = from ^ (mode_to_remove & from);
+#if defined(OS_WIN)
+ // On Windows 8, always enable accessibility for editable text controls
+ // so we can show the virtual keyboard when one is enabled.
+ if (base::win::GetVersion() >= base::win::VERSION_WIN8 &&
+ !CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableRendererAccessibility)) {
+ if ((from & AccessibilityModeEditableTextOnly) ==
+ AccessibilityModeEditableTextOnly)
+ new_mode |= AccessibilityModeEditableTextOnly;
+ }
+#endif // defined(OS_WIN)
+
+ return CastToAccessibilityMode(new_mode);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/accessibility/accessibility_mode_helper.h b/chromium/content/browser/accessibility/accessibility_mode_helper.h
new file mode 100644
index 00000000000..c12c2069d19
--- /dev/null
+++ b/chromium/content/browser/accessibility/accessibility_mode_helper.h
@@ -0,0 +1,28 @@
+// 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_ACCESSIBILITY_ACCESSIBILITY_MODE_HELPER_H_
+#define CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_MODE_HELPER_H_
+
+#include "content/common/content_export.h"
+#include "content/common/view_message_enums.h"
+
+namespace content {
+
+// Adds the given accessibility mode constant to the given accessibility mode
+// bitmap.
+CONTENT_EXPORT AccessibilityMode
+ AddAccessibilityModeTo(AccessibilityMode to, AccessibilityMode mode_to_add);
+
+// Removes the given accessibility mode constant from the given accessibility
+// mode bitmap, managing the bits that are shared with other modes such that a
+// bit will only be turned off when all modes that depend on it have been
+// removed.
+CONTENT_EXPORT AccessibilityMode
+ RemoveAccessibilityModeFrom(AccessibilityMode to,
+ AccessibilityMode mode_to_remove);
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_MODE_HELPER_H_
diff --git a/chromium/content/browser/accessibility/accessibility_mode_helper_unittest.cc b/chromium/content/browser/accessibility/accessibility_mode_helper_unittest.cc
new file mode 100644
index 00000000000..73388a19e9f
--- /dev/null
+++ b/chromium/content/browser/accessibility/accessibility_mode_helper_unittest.cc
@@ -0,0 +1,53 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/accessibility/accessibility_mode_helper.h"
+#include "content/common/view_message_enums.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_WIN)
+#include "base/win/windows_version.h"
+#endif
+
+namespace content {
+
+TEST(AccessibilityModeHelperTest, TestNoOpRemove) {
+ EXPECT_EQ(AccessibilityModeComplete,
+ RemoveAccessibilityModeFrom(AccessibilityModeComplete,
+ AccessibilityModeOff));
+}
+
+TEST(AccessibilityModeHelperTest, TestRemoveSelf) {
+ AccessibilityMode kOffMode = AccessibilityModeOff;
+#if defined(OS_WIN)
+ // Always preserve AccessibilityModeEditableTextOnly on Windows 8,
+ // see RemoveAccessibilityModeFrom() implementation.
+ // Test won't pass if switches::kDisableRendererAccessibility is set.
+ if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
+ kOffMode = AccessibilityModeEditableTextOnly;
+ }
+#endif // defined(OS_WIN)
+
+ EXPECT_EQ(kOffMode,
+ RemoveAccessibilityModeFrom(AccessibilityModeComplete,
+ AccessibilityModeComplete));
+
+ EXPECT_EQ(
+ kOffMode,
+ RemoveAccessibilityModeFrom(AccessibilityModeEditableTextOnly,
+ AccessibilityModeEditableTextOnly));
+}
+
+TEST(AccessibilityModeHelperTest, TestAddMode) {
+ EXPECT_EQ(
+ AccessibilityModeComplete,
+ AddAccessibilityModeTo(AccessibilityModeEditableTextOnly,
+ AccessibilityModeComplete));
+ EXPECT_EQ(
+ AccessibilityModeComplete,
+ AddAccessibilityModeTo(AccessibilityModeEditableTextOnly,
+ AccessibilityModeTreeOnly));
+}
+
+} // namespace content
diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter.cc b/chromium/content/browser/accessibility/accessibility_tree_formatter.cc
index af33a42a7c3..cca61a5b5c6 100644
--- a/chromium/content/browser/accessibility/accessibility_tree_formatter.cc
+++ b/chromium/content/browser/accessibility/accessibility_tree_formatter.cc
@@ -11,7 +11,7 @@
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "content/browser/accessibility/browser_accessibility_manager.h"
-#include "content/port/browser/render_widget_host_view_port.h"
+#include "content/browser/renderer_host/render_widget_host_view_base.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
@@ -31,7 +31,7 @@ AccessibilityTreeFormatter::AccessibilityTreeFormatter(
// static
AccessibilityTreeFormatter* AccessibilityTreeFormatter::Create(
RenderViewHost* rvh) {
- RenderWidgetHostViewPort* host_view = static_cast<RenderWidgetHostViewPort*>(
+ RenderWidgetHostViewBase* host_view = static_cast<RenderWidgetHostViewBase*>(
WebContents::FromRenderViewHost(rvh)->GetRenderWidgetHostView());
BrowserAccessibilityManager* manager =
@@ -68,7 +68,7 @@ void AccessibilityTreeFormatter::RecursiveBuildAccessibilityTree(
dict->Set(kChildrenDictAttr, children);
for (size_t i = 0; i < node.PlatformChildCount(); ++i) {
- BrowserAccessibility* child_node = node.children()[i];
+ BrowserAccessibility* child_node = node.InternalGetChild(i);
base::DictionaryValue* child_dict = new base::DictionaryValue;
children->Append(child_dict);
RecursiveBuildAccessibilityTree(*child_node, child_dict);
@@ -79,7 +79,7 @@ void AccessibilityTreeFormatter::RecursiveFormatAccessibilityTree(
const base::DictionaryValue& dict, base::string16* contents, int depth) {
base::string16 line =
ToString(dict, base::string16(depth * kIndentSpaces, ' '));
- if (line.find(ASCIIToUTF16(kSkipString)) != base::string16::npos)
+ if (line.find(base::ASCIIToUTF16(kSkipString)) != base::string16::npos)
return;
*contents += line;
@@ -92,11 +92,10 @@ void AccessibilityTreeFormatter::RecursiveFormatAccessibilityTree(
}
}
-#if (!defined(OS_WIN) && !defined(OS_MACOSX) && !defined(OS_ANDROID) && \
- !defined(TOOLKIT_GTK))
+#if (!defined(OS_WIN) && !defined(OS_MACOSX) && !defined(OS_ANDROID))
void AccessibilityTreeFormatter::AddProperties(const BrowserAccessibility& node,
base::DictionaryValue* dict) {
- dict->SetInteger("id", node.renderer_id());
+ dict->SetInteger("id", node.GetId());
}
base::string16 AccessibilityTreeFormatter::ToString(
@@ -105,7 +104,7 @@ base::string16 AccessibilityTreeFormatter::ToString(
int id_value;
node.GetInteger("id", &id_value);
return indent + base::IntToString16(id_value) +
- ASCIIToUTF16("\n");
+ base::ASCIIToUTF16("\n");
}
void AccessibilityTreeFormatter::Initialize() {}
@@ -152,7 +151,7 @@ bool AccessibilityTreeFormatter::MatchesFilters(
if (iter->type == Filter::ALLOW_EMPTY)
allow = true;
else if (iter->type == Filter::ALLOW)
- allow = (!MatchPattern(text, UTF8ToUTF16("*=''")));
+ allow = (!MatchPattern(text, base::UTF8ToUTF16("*=''")));
else
allow = false;
}
@@ -168,12 +167,12 @@ base::string16 AccessibilityTreeFormatter::FormatCoordinates(
value.GetInteger(y_name, &y);
std::string xy_str(base::StringPrintf("%s=(%d, %d)", name, x, y));
- return UTF8ToUTF16(xy_str);
+ return base::UTF8ToUTF16(xy_str);
}
void AccessibilityTreeFormatter::WriteAttribute(
bool include_by_default, const std::string& attr, base::string16* line) {
- WriteAttribute(include_by_default, UTF8ToUTF16(attr), line);
+ WriteAttribute(include_by_default, base::UTF8ToUTF16(attr), line);
}
void AccessibilityTreeFormatter::WriteAttribute(
@@ -183,7 +182,7 @@ void AccessibilityTreeFormatter::WriteAttribute(
if (!MatchesFilters(attr, include_by_default))
return;
if (!line->empty())
- *line += ASCIIToUTF16(" ");
+ *line += base::ASCIIToUTF16(" ");
*line += attr;
}
diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter_android.cc b/chromium/content/browser/accessibility/accessibility_tree_formatter_android.cc
index 8c625a3eae2..65f8fa3d0fa 100644
--- a/chromium/content/browser/accessibility/accessibility_tree_formatter_android.cc
+++ b/chromium/content/browser/accessibility/accessibility_tree_formatter_android.cc
@@ -14,7 +14,6 @@
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "content/browser/accessibility/browser_accessibility_android.h"
-#include "content/common/accessibility_node_data.h"
using base::StringPrintf;
@@ -68,7 +67,7 @@ void AccessibilityTreeFormatter::Initialize() {
}
void AccessibilityTreeFormatter::AddProperties(
- const BrowserAccessibility& node, DictionaryValue* dict) {
+ const BrowserAccessibility& node, base::DictionaryValue* dict) {
const BrowserAccessibilityAndroid* android_node =
static_cast<const BrowserAccessibilityAndroid*>(&node);
@@ -116,13 +115,13 @@ void AccessibilityTreeFormatter::AddProperties(
}
base::string16 AccessibilityTreeFormatter::ToString(
- const DictionaryValue& dict,
+ const base::DictionaryValue& dict,
const base::string16& indent) {
base::string16 line;
base::string16 class_value;
dict.GetString("class", &class_value);
- WriteAttribute(true, UTF16ToUTF8(class_value), &line);
+ WriteAttribute(true, base::UTF16ToUTF8(class_value), &line);
for (unsigned i = 0; i < arraysize(BOOL_ATTRIBUTES); i++) {
const char* attribute_name = BOOL_ATTRIBUTES[i];
@@ -151,7 +150,7 @@ base::string16 AccessibilityTreeFormatter::ToString(
&line);
}
- return indent + line + ASCIIToUTF16("\n");
+ return indent + line + base::ASCIIToUTF16("\n");
}
// static
diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter_gtk.cc b/chromium/content/browser/accessibility/accessibility_tree_formatter_gtk.cc
deleted file mode 100644
index 0dc21ce1247..00000000000
--- a/chromium/content/browser/accessibility/accessibility_tree_formatter_gtk.cc
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/accessibility/accessibility_tree_formatter.h"
-
-#include <atk/atk.h>
-
-#include "base/logging.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/browser/accessibility/browser_accessibility_gtk.h"
-
-namespace content {
-
-void AccessibilityTreeFormatter::AddProperties(const BrowserAccessibility& node,
- base::DictionaryValue* dict) {
- BrowserAccessibilityGtk* node_gtk =
- const_cast<BrowserAccessibility*>(&node)->ToBrowserAccessibilityGtk();
- AtkObject* atk_object = node_gtk->GetAtkObject();
- AtkRole role = atk_object_get_role(atk_object);
- if (role != ATK_ROLE_UNKNOWN)
- dict->SetString("role", atk_role_get_name(role));
- dict->SetString("name", atk_object_get_name(atk_object));
- dict->SetString("description", atk_object_get_description(atk_object));
- AtkStateSet* state_set =
- atk_object_ref_state_set(atk_object);
- ListValue* states = new base::ListValue;
- for (int i = ATK_STATE_INVALID; i < ATK_STATE_LAST_DEFINED; i++) {
- AtkStateType state_type = static_cast<AtkStateType>(i);
- if (atk_state_set_contains_state(state_set, state_type))
- states->AppendString(atk_state_type_get_name(state_type));
- }
- dict->Set("states", states);
- dict->SetInteger("id", node.renderer_id());
-}
-
-base::string16 AccessibilityTreeFormatter::ToString(
- const base::DictionaryValue& node,
- const base::string16& indent) {
- base::string16 line;
- std::string role_value;
- node.GetString("role", &role_value);
- if (!role_value.empty())
- WriteAttribute(true, base::StringPrintf("[%s]", role_value.c_str()), &line);
-
- std::string name_value;
- node.GetString("name", &name_value);
- WriteAttribute(true, base::StringPrintf("name='%s'", name_value.c_str()),
- &line);
-
- std::string description_value;
- node.GetString("description", &description_value);
- WriteAttribute(false,
- base::StringPrintf("description='%s'",
- description_value.c_str()),
- &line);
-
- const base::ListValue* states_value;
- node.GetList("states", &states_value);
- for (base::ListValue::const_iterator it = states_value->begin();
- it != states_value->end();
- ++it) {
- std::string state_value;
- if ((*it)->GetAsString(&state_value))
- WriteAttribute(true, state_value, &line);
- }
-
- int id_value;
- node.GetInteger("id", &id_value);
- WriteAttribute(false,
- base::StringPrintf("id=%d", id_value),
- &line);
-
- return indent + line + ASCIIToUTF16("\n");
-}
-
-void AccessibilityTreeFormatter::Initialize() {}
-
-// static
-const base::FilePath::StringType
-AccessibilityTreeFormatter::GetActualFileSuffix() {
- return FILE_PATH_LITERAL("-actual-gtk.txt");
-}
-
-// static
-const base::FilePath::StringType
-AccessibilityTreeFormatter::GetExpectedFileSuffix() {
- return FILE_PATH_LITERAL("-expected-gtk.txt");
-}
-
-// static
-const std::string AccessibilityTreeFormatter::GetAllowEmptyString() {
- return "@GTK-ALLOW-EMPTY:";
-}
-
-// static
-const std::string AccessibilityTreeFormatter::GetAllowString() {
- return "@GTK-ALLOW:";
-}
-
-// static
-const std::string AccessibilityTreeFormatter::GetDenyString() {
- return "@GTK-DENY:";
-}
-
-}
diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter_mac.mm b/chromium/content/browser/accessibility/accessibility_tree_formatter_mac.mm
index 021b5fdc419..e037d4d01bf 100644
--- a/chromium/content/browser/accessibility/accessibility_tree_formatter_mac.mm
+++ b/chromium/content/browser/accessibility/accessibility_tree_formatter_mac.mm
@@ -83,6 +83,49 @@ bool IsRangeValue(id value) {
return 0 == strcmp([value objCType], @encode(NSRange));
}
+scoped_ptr<base::Value> PopulateObject(id value);
+
+scoped_ptr<base::ListValue> PopulateArray(NSArray* array) {
+ scoped_ptr<base::ListValue> list(new base::ListValue);
+ for (NSUInteger i = 0; i < [array count]; i++)
+ list->Append(PopulateObject([array objectAtIndex:i]).release());
+ return list.Pass();
+}
+
+scoped_ptr<base::StringValue> StringForBrowserAccessibility(
+ BrowserAccessibilityCocoa* obj) {
+ NSString* description = [obj role];
+ id value = [obj value];
+ id roleDescription = [obj roleDescription];
+ if (value && ![value isEqual:@""]) {
+ description = [NSString stringWithFormat:@"%@ %@", description, value];
+ } else if ([description isEqualToString:NSAccessibilityGroupRole] &&
+ roleDescription != nil &&
+ ![roleDescription isEqualToString:@""]) {
+ description = [NSString stringWithFormat:@"%@ %@",
+ description, roleDescription];
+ }
+ return scoped_ptr<base::StringValue>(
+ new base::StringValue(SysNSStringToUTF16(description))).Pass();
+}
+
+scoped_ptr<base::Value> PopulateObject(id value) {
+ if ([value isKindOfClass:[NSArray class]])
+ return scoped_ptr<base::Value>(PopulateArray((NSArray*) value));
+ if (IsRangeValue(value))
+ return scoped_ptr<base::Value>(PopulateRange([value rangeValue]));
+ if ([value isKindOfClass:[BrowserAccessibilityCocoa class]]) {
+ std::string str;
+ StringForBrowserAccessibility(value)->GetAsString(&str);
+ return scoped_ptr<base::Value>(StringForBrowserAccessibility(
+ (BrowserAccessibilityCocoa*) value));
+ }
+
+ return scoped_ptr<base::Value>(
+ new base::StringValue(
+ SysNSStringToUTF16([NSString stringWithFormat:@"%@", value]))).Pass();
+}
+
NSArray* BuildAllAttributesArray() {
NSArray* array = [NSArray arrayWithObjects:
NSAccessibilityRoleDescriptionAttribute,
@@ -111,9 +154,11 @@ NSArray* BuildAllAttributesArray() {
NSAccessibilityOrientationAttribute,
@"AXRequired",
NSAccessibilityRowIndexRangeAttribute,
+ NSAccessibilityTitleUIElementAttribute,
NSAccessibilityURLAttribute,
NSAccessibilityVisibleCharacterRangeAttribute,
@"AXVisited",
+ @"AXLinkedUIElements",
nil];
return [array retain];
}
@@ -143,18 +188,13 @@ void AccessibilityTreeFormatter::AddProperties(const BrowserAccessibility& node,
CR_DEFINE_STATIC_LOCAL(NSArray*, all_attributes, (BuildAllAttributesArray()));
for (NSString* requestedAttribute in all_attributes) {
- if (![supportedAttributes containsObject:requestedAttribute]) {
+ if (![supportedAttributes containsObject:requestedAttribute])
continue;
- }
id value = [cocoa_node accessibilityAttributeValue:requestedAttribute];
- if (IsRangeValue(value)) {
+ if (value != nil) {
dict->Set(
SysNSStringToUTF8(requestedAttribute),
- PopulateRange([value rangeValue]).release());
- } else if (value != nil) {
- dict->SetString(
- SysNSStringToUTF8(requestedAttribute),
- SysNSStringToUTF16([NSString stringWithFormat:@"%@", value]));
+ PopulateObject(value).release());
}
}
dict->Set(kPositionDictAttr, PopulatePosition(node).release());
@@ -171,7 +211,7 @@ base::string16 AccessibilityTreeFormatter::ToString(
nil];
string s_value;
dict.GetString(SysNSStringToUTF8(NSAccessibilityRoleAttribute), &s_value);
- WriteAttribute(true, UTF8ToUTF16(s_value), &line);
+ WriteAttribute(true, base::UTF8ToUTF16(s_value), &line);
string subroleAttribute = SysNSStringToUTF8(NSAccessibilitySubroleAttribute);
if (dict.GetString(subroleAttribute, &s_value)) {
@@ -184,10 +224,18 @@ base::string16 AccessibilityTreeFormatter::ToString(
CR_DEFINE_STATIC_LOCAL(NSArray*, all_attributes, (BuildAllAttributesArray()));
for (NSString* requestedAttribute in all_attributes) {
string requestedAttributeUTF8 = SysNSStringToUTF8(requestedAttribute);
- const base::DictionaryValue* d_value;
- if (dict.GetDictionary(requestedAttributeUTF8, &d_value)) {
+ if (dict.GetString(requestedAttributeUTF8, &s_value)) {
+ WriteAttribute([defaultAttributes containsObject:requestedAttribute],
+ StringPrintf("%s='%s'",
+ requestedAttributeUTF8.c_str(),
+ s_value.c_str()),
+ &line);
+ continue;
+ }
+ const base::Value* value;
+ if (dict.Get(requestedAttributeUTF8, &value)) {
std::string json_value;
- base::JSONWriter::Write(d_value, &json_value);
+ base::JSONWriter::Write(value, &json_value);
WriteAttribute(
[defaultAttributes containsObject:requestedAttribute],
StringPrintf("%s=%s",
@@ -195,13 +243,6 @@ base::string16 AccessibilityTreeFormatter::ToString(
json_value.c_str()),
&line);
}
- if (!dict.GetString(requestedAttributeUTF8, &s_value))
- continue;
- WriteAttribute([defaultAttributes containsObject:requestedAttribute],
- StringPrintf("%s='%s'",
- requestedAttributeUTF8.c_str(),
- s_value.c_str()),
- &line);
}
const base::DictionaryValue* d_value = NULL;
if (dict.GetDictionary(kPositionDictAttr, &d_value)) {
@@ -218,7 +259,7 @@ base::string16 AccessibilityTreeFormatter::ToString(
&line);
}
- return indent + line + ASCIIToUTF16("\n");
+ return indent + line + base::ASCIIToUTF16("\n");
}
// static
diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter_utils_win.cc b/chromium/content/browser/accessibility/accessibility_tree_formatter_utils_win.cc
index e2cebc126c2..5e1d97351e1 100644
--- a/chromium/content/browser/accessibility/accessibility_tree_formatter_utils_win.cc
+++ b/chromium/content/browser/accessibility/accessibility_tree_formatter_utils_win.cc
@@ -42,11 +42,11 @@ AccessibilityRoleStateMap* AccessibilityRoleStateMap::GetInstance() {
AccessibilityRoleStateMap::AccessibilityRoleStateMap() {
// Convenience macros for generating readable strings.
-#define IA_ROLE_MAP(x) ia_role_string_map[x] = L#x; \
- ia2_role_string_map[x] = L#x;
-#define IA2_ROLE_MAP(x) ia2_role_string_map[x] = L#x;
-#define IA_STATE_MAP(x) ia_state_string_map[STATE_SYSTEM_##x] = L#x;
-#define IA2_STATE_MAP(x) ia2_state_string_map[x] = L#x;
+#define IA_ROLE_MAP(x) ia_role_string_map[x] = L###x; \
+ ia2_role_string_map[x] = L###x;
+#define IA2_ROLE_MAP(x) ia2_role_string_map[x] = L###x;
+#define IA_STATE_MAP(x) ia_state_string_map[STATE_SYSTEM_##x] = L###x;
+#define IA2_STATE_MAP(x) ia2_state_string_map[x] = L###x;
// MSAA / IAccessible roles. Each one of these is also a valid
// IAccessible2 role, the IA_ROLE_MAP macro adds it to both.
diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter_win.cc b/chromium/content/browser/accessibility/accessibility_tree_formatter_win.cc
index c50b52630d9..76db849ab55 100644
--- a/chromium/content/browser/accessibility/accessibility_tree_formatter_win.cc
+++ b/chromium/content/browser/accessibility/accessibility_tree_formatter_win.cc
@@ -15,7 +15,6 @@
#include "content/browser/accessibility/accessibility_tree_formatter_utils_win.h"
#include "content/browser/accessibility/browser_accessibility_manager.h"
#include "content/browser/accessibility/browser_accessibility_win.h"
-#include "content/common/accessibility_node_data.h"
#include "third_party/iaccessible2/ia2_api_all.h"
#include "ui/base/win/atl_module.h"
@@ -90,7 +89,7 @@ void AccessibilityTreeFormatter::AddProperties(
for (std::vector<base::string16>::const_iterator it = state_strings.begin();
it != state_strings.end();
++it) {
- states->AppendString(UTF16ToUTF8(*it));
+ states->AppendString(base::UTF16ToUTF8(*it));
}
dict->Set("states", states);
@@ -99,7 +98,7 @@ void AccessibilityTreeFormatter::AddProperties(
for (std::vector<base::string16>::const_iterator it = ia2_attributes.begin();
it != ia2_attributes.end();
++it) {
- attributes->AppendString(UTF16ToUTF8(*it));
+ attributes->AppendString(base::UTF16ToUTF8(*it));
}
dict->Set("attributes", attributes);
@@ -206,7 +205,7 @@ base::string16 AccessibilityTreeFormatter::ToString(
base::string16 role_value;
dict.GetString("role", &role_value);
- WriteAttribute(true, UTF16ToUTF8(role_value), &line);
+ WriteAttribute(true, base::UTF16ToUTF8(role_value), &line);
base::string16 name_value;
dict.GetString("name", &name_value);
@@ -225,7 +224,7 @@ base::string16 AccessibilityTreeFormatter::ToString(
value->GetAsString(&string_value);
WriteAttribute(false,
StringPrintf(L"%ls='%ls'",
- UTF8ToUTF16(attribute_name).c_str(),
+ base::UTF8ToUTF16(attribute_name).c_str(),
string_value.c_str()),
&line);
break;
@@ -235,7 +234,8 @@ base::string16 AccessibilityTreeFormatter::ToString(
value->GetAsInteger(&int_value);
WriteAttribute(false,
base::StringPrintf(L"%ls=%d",
- UTF8ToUTF16(attribute_name).c_str(),
+ base::UTF8ToUTF16(
+ attribute_name).c_str(),
int_value),
&line);
break;
@@ -245,7 +245,8 @@ base::string16 AccessibilityTreeFormatter::ToString(
value->GetAsDouble(&double_value);
WriteAttribute(false,
base::StringPrintf(L"%ls=%.2f",
- UTF8ToUTF16(attribute_name).c_str(),
+ base::UTF8ToUTF16(
+ attribute_name).c_str(),
double_value),
&line);
break;
@@ -287,7 +288,7 @@ base::string16 AccessibilityTreeFormatter::ToString(
}
}
- return indent + line + ASCIIToUTF16("\n");
+ return indent + line + base::ASCIIToUTF16("\n");
}
// static
diff --git a/chromium/content/browser/accessibility/accessibility_ui.cc b/chromium/content/browser/accessibility/accessibility_ui.cc
index 5203af6ade7..1f4df798f46 100644
--- a/chromium/content/browser/accessibility/accessibility_ui.cc
+++ b/chromium/content/browser/accessibility/accessibility_ui.cc
@@ -14,9 +14,8 @@
#include "content/browser/accessibility/browser_accessibility_manager.h"
#include "content/browser/accessibility/browser_accessibility_state_impl.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "content/browser/renderer_host/render_widget_host_view_base.h"
#include "content/common/view_message_enums.h"
-#include "content/port/browser/render_widget_host_view_port.h"
-#include "content/public/browser/browser_thread.h"
#include "content/public/browser/favicon_status.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/render_process_host.h"
@@ -75,7 +74,7 @@ base::DictionaryValue* BuildTargetDescriptor(RenderViewHost* rvh) {
// TODO(nasko): Fix the following code to use a consistent set of data
// across the URL, title, and favicon.
url = web_contents->GetURL();
- title = UTF16ToUTF8(web_contents->GetTitle());
+ title = base::UTF16ToUTF8(web_contents->GetTitle());
NavigationController& controller = web_contents->GetController();
NavigationEntry* entry = controller.GetVisibleEntry();
if (entry != NULL && entry->GetURL().is_valid())
@@ -90,18 +89,26 @@ base::DictionaryValue* BuildTargetDescriptor(RenderViewHost* rvh) {
accessibility_mode);
}
-void SendTargetsData(
- const WebUIDataSource::GotDataCallback& callback) {
+bool HandleRequestCallback(BrowserContext* current_context,
+ const std::string& path,
+ const WebUIDataSource::GotDataCallback& callback) {
+ if (path != kDataFile)
+ return false;
scoped_ptr<base::ListValue> rvh_list(new base::ListValue());
scoped_ptr<RenderWidgetHostIterator> widgets(
RenderWidgetHost::GetRenderWidgetHosts());
+
while (RenderWidgetHost* widget = widgets->GetNextHost()) {
// Ignore processes that don't have a connection, such as crashed tabs.
if (!widget->GetProcess()->HasConnection())
continue;
if (!widget->IsRenderView())
continue;
+ RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(widget);
+ BrowserContext* context = rwhi->GetProcess()->GetBrowserContext();
+ if (context != current_context)
+ continue;
RenderViewHost* rvh = RenderViewHost::From(widget);
rvh_list->Append(BuildTargetDescriptor(rvh));
@@ -109,7 +116,7 @@ void SendTargetsData(
scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue());
data->Set("list", rvh_list.release());
- scoped_ptr<base::FundamentalValue> a11y_mode(new base::FundamentalValue(
+ scoped_ptr<base::FundamentalValue> a11y_mode(base::Value::CreateIntegerValue(
BrowserAccessibilityStateImpl::GetInstance()->accessibility_mode()));
data->Set("global_a11y_mode", a11y_mode.release());
@@ -117,22 +124,12 @@ void SendTargetsData(
base::JSONWriter::Write(data.get(), &json_string);
callback.Run(base::RefCountedString::TakeString(&json_string));
-}
-
-bool HandleRequestCallback(
- const std::string& path,
- const WebUIDataSource::GotDataCallback& callback) {
- if (path != kDataFile)
- return false;
-
- SendTargetsData(callback);
return true;
}
} // namespace
-AccessibilityUI::AccessibilityUI(WebUI* web_ui)
- : WebUIController(web_ui) {
+AccessibilityUI::AccessibilityUI(WebUI* web_ui) : WebUIController(web_ui) {
// Set up the chrome://accessibility source.
WebUIDataSource* html_source =
WebUIDataSource::Create(kChromeUIAccessibilityHost);
@@ -141,11 +138,11 @@ AccessibilityUI::AccessibilityUI(WebUI* web_ui)
web_ui->RegisterMessageCallback(
"toggleAccessibility",
base::Bind(&AccessibilityUI::ToggleAccessibility,
- base::Unretained(this)));
+ base::Unretained(this)));
web_ui->RegisterMessageCallback(
"toggleGlobalAccessibility",
base::Bind(&AccessibilityUI::ToggleGlobalAccessibility,
- base::Unretained(this)));
+ base::Unretained(this)));
web_ui->RegisterMessageCallback(
"requestAccessibilityTree",
base::Bind(&AccessibilityUI::RequestAccessibilityTree,
@@ -156,26 +153,26 @@ AccessibilityUI::AccessibilityUI(WebUI* web_ui)
html_source->AddResourcePath("accessibility.css", IDR_ACCESSIBILITY_CSS);
html_source->AddResourcePath("accessibility.js", IDR_ACCESSIBILITY_JS);
html_source->SetDefaultResource(IDR_ACCESSIBILITY_HTML);
- html_source->SetRequestFilter(base::Bind(&HandleRequestCallback));
+ html_source->SetRequestFilter(
+ base::Bind(&HandleRequestCallback,
+ web_ui->GetWebContents()->GetBrowserContext()));
BrowserContext* browser_context =
- web_ui->GetWebContents()->GetBrowserContext();
+ web_ui->GetWebContents()->GetBrowserContext();
WebUIDataSource::Add(browser_context, html_source);
}
-AccessibilityUI::~AccessibilityUI() {
-}
+AccessibilityUI::~AccessibilityUI() {}
void AccessibilityUI::ToggleAccessibility(const base::ListValue* args) {
std::string process_id_str;
std::string route_id_str;
int process_id;
int route_id;
- CHECK(args->GetSize() == 2);
+ CHECK_EQ(2U, args->GetSize());
CHECK(args->GetString(0, &process_id_str));
CHECK(args->GetString(1, &route_id_str));
- CHECK(base::StringToInt(process_id_str,
- &process_id));
+ CHECK(base::StringToInt(process_id_str, &process_id));
CHECK(base::StringToInt(route_id_str, &route_id));
RenderViewHost* rvh = RenderViewHost::FromID(process_id, route_id);
@@ -185,20 +182,20 @@ void AccessibilityUI::ToggleAccessibility(const base::ListValue* args) {
if (!rwhi)
return;
AccessibilityMode mode = rwhi->accessibility_mode();
- if (mode == AccessibilityModeOff)
- rwhi->SetAccessibilityMode(AccessibilityModeComplete);
+ if ((mode & AccessibilityModeComplete) != AccessibilityModeComplete)
+ rwhi->AddAccessibilityMode(AccessibilityModeComplete);
else
- rwhi->SetAccessibilityMode(AccessibilityModeOff);
+ rwhi->ResetAccessibilityMode();
}
void AccessibilityUI::ToggleGlobalAccessibility(const base::ListValue* args) {
BrowserAccessibilityStateImpl* state =
BrowserAccessibilityStateImpl::GetInstance();
AccessibilityMode mode = state->accessibility_mode();
- AccessibilityMode new_mode = (mode == AccessibilityModeOff
- ? AccessibilityModeComplete
- : AccessibilityModeOff);
- state->SetAccessibilityMode(new_mode);
+ if ((mode & AccessibilityModeComplete) != AccessibilityModeComplete)
+ state->EnableAccessibility();
+ else
+ state->DisableAccessibility();
}
void AccessibilityUI::RequestAccessibilityTree(const base::ListValue* args) {
@@ -206,7 +203,7 @@ void AccessibilityUI::RequestAccessibilityTree(const base::ListValue* args) {
std::string route_id_str;
int process_id;
int route_id;
- CHECK(args->GetSize() == 2);
+ CHECK_EQ(2U, args->GetSize());
CHECK(args->GetString(0, &process_id_str));
CHECK(args->GetString(1, &route_id_str));
CHECK(base::StringToInt(process_id_str, &process_id));
@@ -223,7 +220,7 @@ void AccessibilityUI::RequestAccessibilityTree(const base::ListValue* args) {
}
scoped_ptr<base::DictionaryValue> result(BuildTargetDescriptor(rvh));
- RenderWidgetHostViewPort* host_view = static_cast<RenderWidgetHostViewPort*>(
+ RenderWidgetHostViewBase* host_view = static_cast<RenderWidgetHostViewBase*>(
WebContents::FromRenderViewHost(rvh)->GetRenderWidgetHostView());
if (!host_view) {
result->Set("error",
@@ -244,13 +241,14 @@ void AccessibilityUI::RequestAccessibilityTree(const base::ListValue* args) {
}
std::vector<AccessibilityTreeFormatter::Filter> filters;
filters.push_back(AccessibilityTreeFormatter::Filter(
- ASCIIToUTF16("*"),
+ base::ASCIIToUTF16("*"),
AccessibilityTreeFormatter::Filter::ALLOW));
formatter->SetFilters(filters);
formatter->FormatAccessibilityTree(&accessibility_contents_utf16);
result->Set("tree",
- new base::StringValue(UTF16ToUTF8(accessibility_contents_utf16)));
+ new base::StringValue(
+ base::UTF16ToUTF8(accessibility_contents_utf16)));
web_ui()->CallJavascriptFunction("accessibility.showTree", *(result.get()));
}
diff --git a/chromium/content/browser/accessibility/accessibility_ui.h b/chromium/content/browser/accessibility/accessibility_ui.h
index 1b239f8033b..49227283038 100644
--- a/chromium/content/browser/accessibility/accessibility_ui.h
+++ b/chromium/content/browser/accessibility/accessibility_ui.h
@@ -6,6 +6,7 @@
#define CHROME_BROWSER_UI_WEBUI_ACCESSIBILITY_UI_H_
#include "content/public/browser/web_ui_controller.h"
+#include "content/public/browser/web_ui_data_source.h"
namespace base {
class ListValue;
diff --git a/chromium/content/browser/accessibility/accessibility_win_browsertest.cc b/chromium/content/browser/accessibility/accessibility_win_browsertest.cc
index bb35348ba76..a11c026f8ad 100644
--- a/chromium/content/browser/accessibility/accessibility_win_browsertest.cc
+++ b/chromium/content/browser/accessibility/accessibility_win_browsertest.cc
@@ -13,29 +13,23 @@
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
-#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.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 "content/test/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
#include "third_party/iaccessible2/ia2_api_all.h"
#include "third_party/isimpledom/ISimpleDOMNode.h"
-
-// TODO(dmazzoni): Disabled accessibility tests on Win64. crbug.com/179717
-#if defined(ARCH_CPU_X86_64)
-#define MAYBE(x) DISABLED_##x
-#else
-#define MAYBE(x) x
-#endif
+#include "ui/aura/window.h"
+#include "ui/aura/window_tree_host.h"
namespace content {
namespace {
-
// Helpers --------------------------------------------------------------------
base::win::ScopedComPtr<IAccessible> GetAccessibleFromResultVariant(
@@ -91,7 +85,9 @@ void RecursiveFindNodeInAccessibilityTree(IAccessible* node,
// on the bots, this is really helpful in figuring out why.
for (int i = 0; i < depth; i++)
printf(" ");
- printf("role=%d name=%s\n", V_I4(&role), WideToUTF8(name).c_str());
+ printf("role=%s name=%s\n",
+ base::WideToUTF8(IAccessibleRoleToString(V_I4(&role))).c_str(),
+ base::WideToUTF8(name).c_str());
if (expected_role == V_I4(&role) && expected_name == name) {
*found = true;
@@ -149,7 +145,7 @@ void AccessibilityWinBrowserTest::LoadInitialAccessibilityTreeFromHtml(
const std::string& html) {
AccessibilityNotificationWaiter waiter(
shell(), AccessibilityModeComplete,
- blink::WebAXEventLoadComplete);
+ ui::AX_EVENT_LOAD_COMPLETE);
GURL html_data_url("data:text/html," + html);
NavigateToURL(shell(), html_data_url);
waiter.WaitForNotification();
@@ -158,29 +154,12 @@ void AccessibilityWinBrowserTest::LoadInitialAccessibilityTreeFromHtml(
// Retrieve the MSAA client accessibility object for the Render Widget Host View
// of the selected tab.
IAccessible* AccessibilityWinBrowserTest::GetRendererAccessible() {
- HWND hwnd_render_widget_host_view =
- shell()->web_contents()->GetRenderWidgetHostView()->GetNativeView();
-
- // Invoke windows screen reader detection by sending the WM_GETOBJECT message
- // with kIdCustom as the LPARAM.
- const int32 kIdCustom = 1;
- SendMessage(
- hwnd_render_widget_host_view, WM_GETOBJECT, OBJID_CLIENT, kIdCustom);
-
- IAccessible* accessible;
- HRESULT hr = AccessibleObjectFromWindow(
- hwnd_render_widget_host_view, OBJID_CLIENT,
- IID_IAccessible, reinterpret_cast<void**>(&accessible));
-
- EXPECT_EQ(S_OK, hr);
- EXPECT_NE(accessible, reinterpret_cast<IAccessible*>(NULL));
-
- return accessible;
+ content::WebContents* web_contents = shell()->web_contents();
+ return web_contents->GetRenderWidgetHostView()->GetNativeViewAccessible();
}
void AccessibilityWinBrowserTest::ExecuteScript(const std::wstring& script) {
- shell()->web_contents()->GetRenderViewHost()->ExecuteJavascriptInWebFrame(
- std::wstring(), script);
+ shell()->web_contents()->GetMainFrame()->ExecuteJavaScript(script);
}
@@ -286,7 +265,8 @@ void AccessibleChecker::AppendExpectedChild(
}
void AccessibleChecker::CheckAccessible(IAccessible* accessible) {
- SCOPED_TRACE("while checking " + UTF16ToUTF8(RoleVariantToString(role_)));
+ SCOPED_TRACE("while checking " +
+ base::UTF16ToUTF8(RoleVariantToString(role_)));
CheckAccessibleName(accessible);
CheckAccessibleRole(accessible);
CheckIA2Role(accessible);
@@ -418,8 +398,8 @@ base::string16 AccessibleChecker::RoleVariantToString(
// Tests ----------------------------------------------------------------------
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
- MAYBE(TestBusyAccessibilityTree)) {
- NavigateToURL(shell(), GURL(kAboutBlankURL));
+ TestBusyAccessibilityTree) {
+ NavigateToURL(shell(), GURL(url::kAboutBlankURL));
// The initial accessible returned should have state STATE_SYSTEM_BUSY while
// the accessibility tree is being requested from the renderer.
@@ -431,49 +411,6 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
document1_checker.CheckAccessible(GetRendererAccessible());
}
-// Flaky, http://crbug.com/167320 .
-IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
- DISABLED_TestRendererAccessibilityTree) {
- LoadInitialAccessibilityTreeFromHtml(
- "<html><head><title>Accessibility Win Test</title></head>"
- "<body><input type='button' value='push' /><input type='checkbox' />"
- "</body></html>");
-
- // Check the browser's copy of the renderer accessibility tree.
- AccessibleChecker button_checker(L"push", ROLE_SYSTEM_PUSHBUTTON,
- std::wstring());
- AccessibleChecker checkbox_checker(std::wstring(), ROLE_SYSTEM_CHECKBUTTON,
- std::wstring());
- AccessibleChecker body_checker(std::wstring(), L"body", IA2_ROLE_SECTION,
- std::wstring());
- AccessibleChecker document2_checker(L"Accessibility Win Test",
- ROLE_SYSTEM_DOCUMENT, std::wstring());
- body_checker.AppendExpectedChild(&button_checker);
- body_checker.AppendExpectedChild(&checkbox_checker);
- document2_checker.AppendExpectedChild(&body_checker);
- document2_checker.CheckAccessible(GetRendererAccessible());
-
- // Check that document accessible has a parent accessible.
- base::win::ScopedComPtr<IAccessible> document_accessible(
- GetRendererAccessible());
- ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL));
- base::win::ScopedComPtr<IDispatch> parent_dispatch;
- HRESULT hr = document_accessible->get_accParent(parent_dispatch.Receive());
- EXPECT_EQ(S_OK, hr);
- EXPECT_NE(parent_dispatch, reinterpret_cast<IDispatch*>(NULL));
-
- // Navigate to another page.
- NavigateToURL(shell(), GURL(kAboutBlankURL));
-
- // Verify that the IAccessible reference still points to a valid object and
- // that calls to its methods fail since the tree is no longer valid after
- // the page navagation.
- base::win::ScopedBstr name;
- base::win::ScopedVariant childid_self(CHILDID_SELF);
- hr = document_accessible->get_accName(childid_self, name.Receive());
- ASSERT_EQ(E_FAIL, hr);
-}
-
// Periodically failing. See crbug.com/145537
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
DISABLED_TestNotificationActiveDescendantChanged) {
@@ -504,7 +441,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
scoped_ptr<AccessibilityNotificationWaiter> waiter(
new AccessibilityNotificationWaiter(
shell(), AccessibilityModeComplete,
- blink::WebAXEventFocus));
+ ui::AX_EVENT_FOCUS));
ExecuteScript(L"document.body.children[0].focus()");
waiter->WaitForNotification();
@@ -516,7 +453,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
// Set the active descendant of the radio group
waiter.reset(new AccessibilityNotificationWaiter(
shell(), AccessibilityModeComplete,
- blink::WebAXEventFocus));
+ ui::AX_EVENT_FOCUS));
ExecuteScript(
L"document.body.children[0].setAttribute('aria-activedescendant', 'li')");
waiter->WaitForNotification();
@@ -529,7 +466,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
}
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
- MAYBE(TestNotificationCheckedStateChanged)) {
+ TestNotificationCheckedStateChanged) {
LoadInitialAccessibilityTreeFromHtml(
"<body><input type='checkbox' /></body>");
@@ -549,7 +486,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
scoped_ptr<AccessibilityNotificationWaiter> waiter(
new AccessibilityNotificationWaiter(
shell(), AccessibilityModeComplete,
- blink::WebAXEventCheckedStateChanged));
+ ui::AX_EVENT_CHECKED_STATE_CHANGED));
ExecuteScript(L"document.body.children[0].checked=true");
waiter->WaitForNotification();
@@ -560,7 +497,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
}
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
- MAYBE(TestNotificationChildrenChanged)) {
+ TestNotificationChildrenChanged) {
// The role attribute causes the node to be in the accessibility tree.
LoadInitialAccessibilityTreeFromHtml("<body role=group></body>");
@@ -577,18 +514,19 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
new AccessibilityNotificationWaiter(
shell(),
AccessibilityModeComplete,
- blink::WebAXEventChildrenChanged));
+ ui::AX_EVENT_CHILDREN_CHANGED));
ExecuteScript(L"document.body.innerHTML='<b>new text</b>'");
waiter->WaitForNotification();
// Check that the accessibility tree of the browser has been updated.
- AccessibleChecker text_checker(L"new text", ROLE_SYSTEM_TEXT, std::wstring());
+ AccessibleChecker text_checker(
+ L"new text", ROLE_SYSTEM_STATICTEXT, std::wstring());
group_checker.AppendExpectedChild(&text_checker);
document_checker.CheckAccessible(GetRendererAccessible());
}
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
- MAYBE(TestNotificationChildrenChanged2)) {
+ TestNotificationChildrenChanged2) {
// The role attribute causes the node to be in the accessibility tree.
LoadInitialAccessibilityTreeFromHtml(
"<div role=group style='visibility: hidden'>text</div>");
@@ -602,12 +540,12 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
scoped_ptr<AccessibilityNotificationWaiter> waiter(
new AccessibilityNotificationWaiter(
shell(), AccessibilityModeComplete,
- blink::WebAXEventChildrenChanged));
+ ui::AX_EVENT_CHILDREN_CHANGED));
ExecuteScript(L"document.body.children[0].style.visibility='visible'");
waiter->WaitForNotification();
// Check that the accessibility tree of the browser has been updated.
- AccessibleChecker static_text_checker(L"text", ROLE_SYSTEM_TEXT,
+ AccessibleChecker static_text_checker(L"text", ROLE_SYSTEM_STATICTEXT,
std::wstring());
AccessibleChecker group_checker(std::wstring(), ROLE_SYSTEM_GROUPING,
std::wstring());
@@ -617,7 +555,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
}
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
- MAYBE(TestNotificationFocusChanged)) {
+ TestNotificationFocusChanged) {
// The role attribute causes the node to be in the accessibility tree.
LoadInitialAccessibilityTreeFromHtml("<div role=group tabindex='-1'></div>");
@@ -635,7 +573,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
scoped_ptr<AccessibilityNotificationWaiter> waiter(
new AccessibilityNotificationWaiter(
shell(), AccessibilityModeComplete,
- blink::WebAXEventFocus));
+ ui::AX_EVENT_FOCUS));
ExecuteScript(L"document.body.children[0].focus()");
waiter->WaitForNotification();
@@ -649,7 +587,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
waiter.reset(
new AccessibilityNotificationWaiter(
shell(), AccessibilityModeComplete,
- blink::WebAXEventBlur));
+ ui::AX_EVENT_BLUR));
base::win::ScopedComPtr<IAccessible> document_accessible(
GetRendererAccessible());
ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL));
@@ -665,7 +603,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
}
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
- MAYBE(TestNotificationValueChanged)) {
+ TestNotificationValueChanged) {
LoadInitialAccessibilityTreeFromHtml(
"<body><input type='text' value='old value'/></body>");
@@ -685,7 +623,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
scoped_ptr<AccessibilityNotificationWaiter> waiter(
new AccessibilityNotificationWaiter(
shell(), AccessibilityModeComplete,
- blink::WebAXEventValueChanged));
+ ui::AX_EVENT_VALUE_CHANGED));
ExecuteScript(L"document.body.children[0].value='new value'");
waiter->WaitForNotification();
@@ -703,16 +641,21 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
// that wraps the tab contents returns the IAccessible implementation
// provided by RenderWidgetHostViewWin in GetNativeViewAccessible().
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
- MAYBE(ContainsRendererAccessibilityTree)) {
+ ContainsRendererAccessibilityTree) {
LoadInitialAccessibilityTreeFromHtml(
"<html><head><title>MyDocument</title></head>"
"<body>Content</body></html>");
- // Get the accessibility object for the browser window.
- HWND browser_hwnd = shell()->window();
+ // Get the accessibility object for the window tree host.
+ aura::Window* window = shell()->window();
+ CHECK(window);
+ aura::WindowTreeHost* window_tree_host = window->GetHost();
+ CHECK(window_tree_host);
+ HWND hwnd = window_tree_host->GetAcceleratedWidget();
+ CHECK(hwnd);
base::win::ScopedComPtr<IAccessible> browser_accessible;
HRESULT hr = AccessibleObjectFromWindow(
- browser_hwnd,
+ hwnd,
OBJID_WINDOW,
IID_IAccessible,
reinterpret_cast<void**>(browser_accessible.Receive()));
@@ -724,83 +667,8 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
ASSERT_EQ(found, true);
}
-// Disabled because of http://crbug.com/144390.
-IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
- DISABLED_TestToggleButtonRoleAndStates) {
- AccessibleChecker* button_checker;
- std::string button_html("data:text/html,");
- AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
- std::wstring());
- AccessibleChecker body_checker(std::wstring(), L"body", IA2_ROLE_SECTION,
- std::wstring());
- document_checker.AppendExpectedChild(&body_checker);
-
-// Temporary macro
-#define ADD_BUTTON(html, ia2_role, state) \
- button_html += html; \
- button_checker = new AccessibleChecker(L"x", ROLE_SYSTEM_PUSHBUTTON, \
- ia2_role, std::wstring()); \
- button_checker->SetExpectedState(state); \
- body_checker.AppendExpectedChild(button_checker)
-
- // If aria-pressed is 'undefined', empty or not present, use PUSHBUTTON
- // Otherwise use TOGGLE_BUTTON, even if the value is invalid.
- // The spec does this in an attempt future-proof in case new values are added.
- ADD_BUTTON("<span role='button' aria-pressed='false'>x</span>",
- IA2_ROLE_TOGGLE_BUTTON, 0);
- ADD_BUTTON("<span role='button' aria-pressed='true'>x</span>",
- IA2_ROLE_TOGGLE_BUTTON, STATE_SYSTEM_PRESSED);
- ADD_BUTTON("<span role='button' aria-pressed='mixed'>x</span>",
- IA2_ROLE_TOGGLE_BUTTON, STATE_SYSTEM_MIXED);
- ADD_BUTTON("<span role='button' aria-pressed='xyz'>x</span>",
- IA2_ROLE_TOGGLE_BUTTON, 0);
- ADD_BUTTON("<span role='button' aria-pressed=''>x</span>",
- ROLE_SYSTEM_PUSHBUTTON, 0);
- ADD_BUTTON("<span role='button' aria-pressed>x</span>",
- ROLE_SYSTEM_PUSHBUTTON, 0);
- ADD_BUTTON("<span role='button' aria-pressed='undefined'>x</span>",
- ROLE_SYSTEM_PUSHBUTTON, 0);
- ADD_BUTTON("<span role='button'>x</span>", ROLE_SYSTEM_PUSHBUTTON, 0);
- ADD_BUTTON("<input type='button' aria-pressed='true' value='x'/>",
- IA2_ROLE_TOGGLE_BUTTON, STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_PRESSED);
- ADD_BUTTON("<input type='button' aria-pressed='false' value='x'/>",
- IA2_ROLE_TOGGLE_BUTTON, STATE_SYSTEM_FOCUSABLE);
- ADD_BUTTON("<input type='button' aria-pressed='mixed' value='x'>",
- IA2_ROLE_TOGGLE_BUTTON, STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_MIXED);
- ADD_BUTTON("<input type='button' aria-pressed='xyz' value='x'/>",
- IA2_ROLE_TOGGLE_BUTTON, STATE_SYSTEM_FOCUSABLE);
- ADD_BUTTON("<input type='button' aria-pressed='' value='x'/>",
- ROLE_SYSTEM_PUSHBUTTON, STATE_SYSTEM_FOCUSABLE);
- ADD_BUTTON("<input type='button' aria-pressed value='x'>",
- ROLE_SYSTEM_PUSHBUTTON, STATE_SYSTEM_FOCUSABLE);
- ADD_BUTTON("<input type='button' aria-pressed='undefined' value='x'>",
- ROLE_SYSTEM_PUSHBUTTON, STATE_SYSTEM_FOCUSABLE);
- ADD_BUTTON("<input type='button' value='x'>",
- ROLE_SYSTEM_PUSHBUTTON, STATE_SYSTEM_FOCUSABLE);
- ADD_BUTTON("<button aria-pressed='true'>x</button>",
- IA2_ROLE_TOGGLE_BUTTON, STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_PRESSED);
- ADD_BUTTON("<button aria-pressed='false'>x</button>",
- IA2_ROLE_TOGGLE_BUTTON, STATE_SYSTEM_FOCUSABLE);
- ADD_BUTTON("<button aria-pressed='mixed'>x</button>", IA2_ROLE_TOGGLE_BUTTON,
- STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_MIXED);
- ADD_BUTTON("<button aria-pressed='xyz'>x</button>", IA2_ROLE_TOGGLE_BUTTON,
- STATE_SYSTEM_FOCUSABLE);
- ADD_BUTTON("<button aria-pressed=''>x</button>",
- ROLE_SYSTEM_PUSHBUTTON, STATE_SYSTEM_FOCUSABLE);
- ADD_BUTTON("<button aria-pressed>x</button>",
- ROLE_SYSTEM_PUSHBUTTON, STATE_SYSTEM_FOCUSABLE);
- ADD_BUTTON("<button aria-pressed='undefined'>x</button>",
- ROLE_SYSTEM_PUSHBUTTON, STATE_SYSTEM_FOCUSABLE);
- ADD_BUTTON("<button>x</button>", ROLE_SYSTEM_PUSHBUTTON,
- STATE_SYSTEM_FOCUSABLE);
-#undef ADD_BUTTON // Temporary macro
-
- LoadInitialAccessibilityTreeFromHtml(button_html);
- document_checker.CheckAccessible(GetRendererAccessible());
-}
-
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
- MAYBE(SupportsISimpleDOM)) {
+ SupportsISimpleDOM) {
LoadInitialAccessibilityTreeFromHtml(
"<body><input type='checkbox' /></body>");
@@ -864,7 +732,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
EXPECT_EQ(0, num_children);
}
-IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, MAYBE(TestRoleGroup)) {
+IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestRoleGroup) {
LoadInitialAccessibilityTreeFromHtml(
"<fieldset></fieldset><div role=group></div>");
diff --git a/chromium/content/browser/accessibility/android_hit_testing_browsertest.cc b/chromium/content/browser/accessibility/android_hit_testing_browsertest.cc
new file mode 100644
index 00000000000..1070f6e63f2
--- /dev/null
+++ b/chromium/content/browser/accessibility/android_hit_testing_browsertest.cc
@@ -0,0 +1,81 @@
+// 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 <set>
+#include <string>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.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/renderer_host/render_view_host_impl.h"
+#include "content/browser/renderer_host/render_widget_host_view_base.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() {}
+ virtual ~AndroidHitTestingBrowserTest() {}
+};
+
+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,"
+ "<!doctype html>"
+ "<html><head><title>Accessibility Test</title></head>"
+ "<body>"
+ "<a href='#'>"
+ "This is some text in a link"
+ "</a>"
+ "</body></html>";
+ GURL url(url_str);
+ NavigateToURL(shell(), url);
+ waiter.WaitForNotification();
+
+ // Get the BrowserAccessibilityManager.
+ RenderWidgetHostViewBase* host_view = static_cast<RenderWidgetHostViewBase*>(
+ shell()->web_contents()->GetRenderWidgetHostView());
+ BrowserAccessibilityManager* manager =
+ host_view->GetBrowserAccessibilityManager();
+
+ // 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 223cd8df559..fe71fef965a 100644
--- a/chromium/content/browser/accessibility/browser_accessibility.cc
+++ b/chromium/content/browser/accessibility/browser_accessibility.cc
@@ -13,17 +13,11 @@
namespace content {
-typedef AccessibilityNodeData::BoolAttribute BoolAttribute;
-typedef AccessibilityNodeData::FloatAttribute FloatAttribute;
-typedef AccessibilityNodeData::IntAttribute IntAttribute;
-typedef AccessibilityNodeData::StringAttribute StringAttribute;
-
#if !defined(OS_MACOSX) && \
!defined(OS_WIN) && \
- !defined(TOOLKIT_GTK) && \
!defined(OS_ANDROID)
-// We have subclassess of BrowserAccessibility on Mac, Linux/GTK,
-// and Win. For any other platform, instantiate the base class.
+// We have subclassess of BrowserAccessibility on Mac and Win. For any other
+// platform, instantiate the base class.
// static
BrowserAccessibility* BrowserAccessibility::Create() {
return new BrowserAccessibility();
@@ -32,89 +26,56 @@ BrowserAccessibility* BrowserAccessibility::Create() {
BrowserAccessibility::BrowserAccessibility()
: manager_(NULL),
- parent_(NULL),
- index_in_parent_(0),
- renderer_id_(0),
- role_(0),
- state_(0),
- instance_active_(false) {
+ node_(NULL) {
}
BrowserAccessibility::~BrowserAccessibility() {
}
-bool BrowserAccessibility::PlatformIsLeaf() const {
- return role_ == blink::WebAXRoleStaticText || child_count() == 0;
+void BrowserAccessibility::Init(BrowserAccessibilityManager* manager,
+ ui::AXNode* node) {
+ manager_ = manager;
+ node_ = node;
}
-uint32 BrowserAccessibility::PlatformChildCount() const {
- return PlatformIsLeaf() ? 0 : children_.size();
+void BrowserAccessibility::OnDataChanged() {
+ GetStringAttribute(ui::AX_ATTR_NAME, &name_);
+ GetStringAttribute(ui::AX_ATTR_VALUE, &value_);
}
-void BrowserAccessibility::DetachTree(
- std::vector<BrowserAccessibility*>* nodes) {
- nodes->push_back(this);
- for (size_t i = 0; i < children_.size(); ++i)
- children_[i]->DetachTree(nodes);
- children_.clear();
- parent_ = NULL;
-}
+bool BrowserAccessibility::PlatformIsLeaf() const {
+ if (InternalChildCount() == 0)
+ return true;
-void BrowserAccessibility::InitializeTreeStructure(
- BrowserAccessibilityManager* manager,
- BrowserAccessibility* parent,
- int32 renderer_id,
- int32 index_in_parent) {
- manager_ = manager;
- parent_ = parent;
- renderer_id_ = renderer_id;
- index_in_parent_ = index_in_parent;
+ // All of these roles may have children that we use as internal
+ // implementation details, but we want to expose them as leaves
+ // to platform accessibility APIs.
+ switch (GetRole()) {
+ case ui::AX_ROLE_EDITABLE_TEXT:
+ case ui::AX_ROLE_SLIDER:
+ case ui::AX_ROLE_STATIC_TEXT:
+ case ui::AX_ROLE_TEXT_AREA:
+ case ui::AX_ROLE_TEXT_FIELD:
+ return true;
+ default:
+ return false;
+ }
}
-void BrowserAccessibility::InitializeData(const AccessibilityNodeData& src) {
- DCHECK_EQ(renderer_id_, src.id);
- role_ = src.role;
- state_ = src.state;
- string_attributes_ = src.string_attributes;
- int_attributes_ = src.int_attributes;
- float_attributes_ = src.float_attributes;
- bool_attributes_ = src.bool_attributes;
- intlist_attributes_ = src.intlist_attributes;
- html_attributes_ = src.html_attributes;
- location_ = src.location;
- instance_active_ = true;
-
- GetStringAttribute(AccessibilityNodeData::ATTR_NAME, &name_);
- GetStringAttribute(AccessibilityNodeData::ATTR_VALUE, &value_);
-
- PreInitialize();
+uint32 BrowserAccessibility::PlatformChildCount() const {
+ return PlatformIsLeaf() ? 0 : InternalChildCount();
}
bool BrowserAccessibility::IsNative() const {
return false;
}
-void BrowserAccessibility::SwapChildren(
- std::vector<BrowserAccessibility*>& children) {
- children.swap(children_);
-}
-
-void BrowserAccessibility::UpdateParent(BrowserAccessibility* parent,
- int index_in_parent) {
- parent_ = parent;
- index_in_parent_ = index_in_parent;
-}
-
-void BrowserAccessibility::SetLocation(const gfx::Rect& new_location) {
- location_ = new_location;
-}
-
bool BrowserAccessibility::IsDescendantOf(
BrowserAccessibility* ancestor) {
if (this == ancestor) {
return true;
- } else if (parent_) {
- return parent_->IsDescendantOf(ancestor);
+ } else if (GetParent()) {
+ return GetParent()->IsDescendantOf(ancestor);
}
return false;
@@ -122,63 +83,117 @@ bool BrowserAccessibility::IsDescendantOf(
BrowserAccessibility* BrowserAccessibility::PlatformGetChild(
uint32 child_index) const {
- DCHECK(child_index < children_.size());
- return children_[child_index];
+ DCHECK(child_index < InternalChildCount());
+ return InternalGetChild(child_index);
}
BrowserAccessibility* BrowserAccessibility::GetPreviousSibling() {
- if (parent_ && index_in_parent_ > 0)
- return parent_->children_[index_in_parent_ - 1];
+ if (GetParent() && GetIndexInParent() > 0)
+ return GetParent()->InternalGetChild(GetIndexInParent() - 1);
return NULL;
}
BrowserAccessibility* BrowserAccessibility::GetNextSibling() {
- if (parent_ &&
- index_in_parent_ >= 0 &&
- index_in_parent_ < static_cast<int>(parent_->children_.size() - 1)) {
- return parent_->children_[index_in_parent_ + 1];
+ if (GetParent() &&
+ GetIndexInParent() >= 0 &&
+ GetIndexInParent() < static_cast<int>(
+ GetParent()->InternalChildCount() - 1)) {
+ return GetParent()->InternalGetChild(GetIndexInParent() + 1);
}
return NULL;
}
+uint32 BrowserAccessibility::InternalChildCount() const {
+ if (!node_ || !manager_)
+ return 0;
+ return static_cast<uint32>(node_->child_count());
+}
+
+BrowserAccessibility* BrowserAccessibility::InternalGetChild(
+ uint32 child_index) const {
+ if (!node_ || !manager_)
+ return NULL;
+ return manager_->GetFromAXNode(node_->children()[child_index]);
+}
+
+BrowserAccessibility* BrowserAccessibility::GetParent() const {
+ if (!node_ || !manager_)
+ return NULL;
+ ui::AXNode* parent = node_->parent();
+ return parent ? manager_->GetFromAXNode(parent) : NULL;
+}
+
+int32 BrowserAccessibility::GetIndexInParent() const {
+ return node_ ? node_->index_in_parent() : -1;
+}
+
+int32 BrowserAccessibility::GetId() const {
+ return node_ ? node_->id() : -1;
+}
+
+const ui::AXNodeData& BrowserAccessibility::GetData() const {
+ CR_DEFINE_STATIC_LOCAL(ui::AXNodeData, empty_data, ());
+ if (node_)
+ return node_->data();
+ else
+ return empty_data;
+}
+
+gfx::Rect BrowserAccessibility::GetLocation() const {
+ return GetData().location;
+}
+
+int32 BrowserAccessibility::GetRole() const {
+ return GetData().role;
+}
+
+int32 BrowserAccessibility::GetState() const {
+ return GetData().state;
+}
+
+const std::vector<std::pair<std::string, std::string> >&
+BrowserAccessibility::GetHtmlAttributes() const {
+ return GetData().html_attributes;
+}
+
gfx::Rect BrowserAccessibility::GetLocalBoundsRect() const {
- gfx::Rect bounds = location_;
+ gfx::Rect bounds = GetLocation();
// 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 = parent_;
+ BrowserAccessibility* parent = GetParent();
bool need_to_offset_web_area =
- (role_ == blink::WebAXRoleWebArea ||
- role_ == blink::WebAXRoleRootWebArea);
+ (GetRole() == ui::AX_ROLE_WEB_AREA ||
+ GetRole() == ui::AX_ROLE_ROOT_WEB_AREA);
while (parent) {
if (need_to_offset_web_area &&
- parent->location().width() > 0 &&
- parent->location().height() > 0) {
- bounds.Offset(parent->location().x(), parent->location().y());
+ 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->role() == blink::WebAXRoleRootWebArea &&
+ if (parent->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA &&
!manager()->UseRootScrollOffsetsWhenComputingBounds()) {
break;
}
- if (parent->role() == blink::WebAXRoleWebArea ||
- parent->role() == blink::WebAXRoleRootWebArea) {
+ if (parent->GetRole() == ui::AX_ROLE_WEB_AREA ||
+ parent->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA) {
int sx = 0;
int sy = 0;
- if (parent->GetIntAttribute(AccessibilityNodeData::ATTR_SCROLL_X, &sx) &&
- parent->GetIntAttribute(AccessibilityNodeData::ATTR_SCROLL_Y, &sy)) {
+ if (parent->GetIntAttribute(ui::AX_ATTR_SCROLL_X, &sx) &&
+ parent->GetIntAttribute(ui::AX_ATTR_SCROLL_Y, &sy)) {
bounds.Offset(-sx, -sy);
}
need_to_offset_web_area = true;
}
- parent = parent->parent();
+ parent = parent->GetParent();
}
return bounds;
@@ -196,17 +211,34 @@ gfx::Rect BrowserAccessibility::GetGlobalBoundsRect() const {
gfx::Rect BrowserAccessibility::GetLocalBoundsForRange(int start, int len)
const {
- DCHECK_EQ(role_, blink::WebAXRoleStaticText);
+ if (GetRole() != ui::AX_ROLE_STATIC_TEXT) {
+ // Apply recursively to all static text descendants. For example, if
+ // you call it on a div with two text node children, it just calls
+ // GetLocalBoundsForRange on each of the two children (adjusting
+ // |start| for each one) and unions the resulting rects.
+ gfx::Rect bounds;
+ for (size_t i = 0; i < InternalChildCount(); ++i) {
+ BrowserAccessibility* child = InternalGetChild(i);
+ int child_len = child->GetStaticTextLenRecursive();
+ if (start < child_len && start + len > 0) {
+ gfx::Rect child_rect = child->GetLocalBoundsForRange(start, len);
+ bounds.Union(child_rect);
+ }
+ start -= child_len;
+ }
+ return bounds;
+ }
+
int end = start + len;
int child_start = 0;
int child_end = 0;
gfx::Rect bounds;
- for (size_t i = 0; i < children_.size() && child_end < start + len; ++i) {
- BrowserAccessibility* child = children_[i];
- DCHECK_EQ(child->role(), blink::WebAXRoleInlineTextBox);
+ for (size_t i = 0; i < InternalChildCount() && child_end < start + len; ++i) {
+ BrowserAccessibility* child = InternalGetChild(i);
+ DCHECK_EQ(child->GetRole(), ui::AX_ROLE_INLINE_TEXT_BOX);
std::string child_text;
- child->GetStringAttribute(AccessibilityNodeData::ATTR_VALUE, &child_text);
+ child->GetStringAttribute(ui::AX_ATTR_VALUE, &child_text);
int child_len = static_cast<int>(child_text.size());
child_start = child_end;
child_end += child_len;
@@ -220,11 +252,11 @@ gfx::Rect BrowserAccessibility::GetLocalBoundsForRange(int start, int len)
int local_start = overlap_start - child_start;
int local_end = overlap_end - child_start;
- gfx::Rect child_rect = child->location();
+ gfx::Rect child_rect = child->GetLocation();
int text_direction = child->GetIntAttribute(
- AccessibilityNodeData::ATTR_TEXT_DIRECTION);
+ ui::AX_ATTR_TEXT_DIRECTION);
const std::vector<int32>& character_offsets = child->GetIntListAttribute(
- AccessibilityNodeData::ATTR_CHARACTER_OFFSETS);
+ ui::AX_ATTR_CHARACTER_OFFSETS);
int start_pixel_offset =
local_start > 0 ? character_offsets[local_start - 1] : 0;
int end_pixel_offset =
@@ -232,28 +264,29 @@ gfx::Rect BrowserAccessibility::GetLocalBoundsForRange(int start, int len)
gfx::Rect child_overlap_rect;
switch (text_direction) {
- case blink::WebAXTextDirectionLR: {
+ case ui::AX_TEXT_DIRECTION_NONE:
+ case ui::AX_TEXT_DIRECTION_LR: {
int left = child_rect.x() + start_pixel_offset;
int right = child_rect.x() + end_pixel_offset;
child_overlap_rect = gfx::Rect(left, child_rect.y(),
right - left, child_rect.height());
break;
}
- case blink::WebAXTextDirectionRL: {
+ case ui::AX_TEXT_DIRECTION_RL: {
int right = child_rect.right() - start_pixel_offset;
int left = child_rect.right() - end_pixel_offset;
child_overlap_rect = gfx::Rect(left, child_rect.y(),
right - left, child_rect.height());
break;
}
- case blink::WebAXTextDirectionTB: {
+ case ui::AX_TEXT_DIRECTION_TB: {
int top = child_rect.y() + start_pixel_offset;
int bottom = child_rect.y() + end_pixel_offset;
child_overlap_rect = gfx::Rect(child_rect.x(), top,
child_rect.width(), bottom - top);
break;
}
- case blink::WebAXTextDirectionBT: {
+ case ui::AX_TEXT_DIRECTION_BT: {
int bottom = child_rect.bottom() - start_pixel_offset;
int top = child_rect.bottom() - end_pixel_offset;
child_overlap_rect = gfx::Rect(child_rect.x(), top,
@@ -286,34 +319,58 @@ gfx::Rect BrowserAccessibility::GetGlobalBoundsForRange(int start, int len)
BrowserAccessibility* BrowserAccessibility::BrowserAccessibilityForPoint(
const gfx::Point& point) {
+ // The best result found that's a child of this object.
+ BrowserAccessibility* child_result = NULL;
+ // The best result that's an indirect descendant like grandchild, etc.
+ BrowserAccessibility* descendant_result = NULL;
+
// Walk the children recursively looking for the BrowserAccessibility that
- // most tightly encloses the specified point.
+ // most tightly encloses the specified point. Walk backwards so that in
+ // the absence of any other information, we assume the object that occurs
+ // later in the tree is on top of one that comes before it.
for (int i = static_cast<int>(PlatformChildCount()) - 1; i >= 0; --i) {
BrowserAccessibility* child = PlatformGetChild(i);
- if (child->GetGlobalBoundsRect().Contains(point))
- return child->BrowserAccessibilityForPoint(point);
+
+ // Skip table columns because cells are only contained in rows,
+ // not columns.
+ if (child->GetRole() == ui::AX_ROLE_COLUMN)
+ continue;
+
+ if (child->GetGlobalBoundsRect().Contains(point)) {
+ BrowserAccessibility* result = child->BrowserAccessibilityForPoint(point);
+ if (result == child && !child_result)
+ child_result = result;
+ if (result != child && !descendant_result)
+ descendant_result = result;
+ }
+
+ if (child_result && descendant_result)
+ break;
}
+
+ // Explanation of logic: it's possible that this point overlaps more than
+ // one child of this object. If so, as a heuristic we prefer if the point
+ // overlaps a descendant of one of the two children and not the other.
+ // As an example, suppose you have two rows of buttons - the buttons don't
+ // overlap, but the rows do. Without this heuristic, we'd greedily only
+ // consider one of the containers.
+ if (descendant_result)
+ return descendant_result;
+ if (child_result)
+ return child_result;
+
return this;
}
void BrowserAccessibility::Destroy() {
- for (std::vector<BrowserAccessibility*>::iterator iter = children_.begin();
- iter != children_.end();
- ++iter) {
- (*iter)->Destroy();
- }
- children_.clear();
-
// Allow the object to fire a TextRemoved notification.
name_.clear();
value_.clear();
- PostInitialize();
- manager_->NotifyAccessibilityEvent(
- blink::WebAXEventHide, this);
+ manager_->NotifyAccessibilityEvent(ui::AX_EVENT_HIDE, this);
+ node_ = NULL;
+ manager_ = NULL;
- instance_active_ = false;
- manager_->RemoveNode(this);
NativeReleaseReference();
}
@@ -321,9 +378,11 @@ void BrowserAccessibility::NativeReleaseReference() {
delete this;
}
-bool BrowserAccessibility::HasBoolAttribute(BoolAttribute attribute) const {
- for (size_t i = 0; i < bool_attributes_.size(); ++i) {
- if (bool_attributes_[i].first == attribute)
+bool BrowserAccessibility::HasBoolAttribute(
+ ui::AXBoolAttribute attribute) const {
+ const ui::AXNodeData& data = GetData();
+ for (size_t i = 0; i < data.bool_attributes.size(); ++i) {
+ if (data.bool_attributes[i].first == attribute)
return true;
}
@@ -331,20 +390,23 @@ bool BrowserAccessibility::HasBoolAttribute(BoolAttribute attribute) const {
}
-bool BrowserAccessibility::GetBoolAttribute(BoolAttribute attribute) const {
- for (size_t i = 0; i < bool_attributes_.size(); ++i) {
- if (bool_attributes_[i].first == attribute)
- return bool_attributes_[i].second;
+bool BrowserAccessibility::GetBoolAttribute(
+ ui::AXBoolAttribute attribute) const {
+ const ui::AXNodeData& data = GetData();
+ for (size_t i = 0; i < data.bool_attributes.size(); ++i) {
+ if (data.bool_attributes[i].first == attribute)
+ return data.bool_attributes[i].second;
}
return false;
}
bool BrowserAccessibility::GetBoolAttribute(
- BoolAttribute attribute, bool* value) const {
- for (size_t i = 0; i < bool_attributes_.size(); ++i) {
- if (bool_attributes_[i].first == attribute) {
- *value = bool_attributes_[i].second;
+ ui::AXBoolAttribute attribute, bool* value) const {
+ const ui::AXNodeData& data = GetData();
+ for (size_t i = 0; i < data.bool_attributes.size(); ++i) {
+ if (data.bool_attributes[i].first == attribute) {
+ *value = data.bool_attributes[i].second;
return true;
}
}
@@ -352,29 +414,34 @@ bool BrowserAccessibility::GetBoolAttribute(
return false;
}
-bool BrowserAccessibility::HasFloatAttribute(FloatAttribute attribute) const {
- for (size_t i = 0; i < float_attributes_.size(); ++i) {
- if (float_attributes_[i].first == attribute)
+bool BrowserAccessibility::HasFloatAttribute(
+ ui::AXFloatAttribute attribute) const {
+ const ui::AXNodeData& data = GetData();
+ for (size_t i = 0; i < data.float_attributes.size(); ++i) {
+ if (data.float_attributes[i].first == attribute)
return true;
}
return false;
}
-float BrowserAccessibility::GetFloatAttribute(FloatAttribute attribute) const {
- for (size_t i = 0; i < float_attributes_.size(); ++i) {
- if (float_attributes_[i].first == attribute)
- return float_attributes_[i].second;
+float BrowserAccessibility::GetFloatAttribute(
+ ui::AXFloatAttribute attribute) const {
+ const ui::AXNodeData& data = GetData();
+ for (size_t i = 0; i < data.float_attributes.size(); ++i) {
+ if (data.float_attributes[i].first == attribute)
+ return data.float_attributes[i].second;
}
return 0.0;
}
bool BrowserAccessibility::GetFloatAttribute(
- FloatAttribute attribute, float* value) const {
- for (size_t i = 0; i < float_attributes_.size(); ++i) {
- if (float_attributes_[i].first == attribute) {
- *value = float_attributes_[i].second;
+ ui::AXFloatAttribute attribute, float* value) const {
+ const ui::AXNodeData& data = GetData();
+ for (size_t i = 0; i < data.float_attributes.size(); ++i) {
+ if (data.float_attributes[i].first == attribute) {
+ *value = data.float_attributes[i].second;
return true;
}
}
@@ -382,29 +449,33 @@ bool BrowserAccessibility::GetFloatAttribute(
return false;
}
-bool BrowserAccessibility::HasIntAttribute(IntAttribute attribute) const {
- for (size_t i = 0; i < int_attributes_.size(); ++i) {
- if (int_attributes_[i].first == attribute)
+bool BrowserAccessibility::HasIntAttribute(
+ ui::AXIntAttribute attribute) const {
+ const ui::AXNodeData& data = GetData();
+ for (size_t i = 0; i < data.int_attributes.size(); ++i) {
+ if (data.int_attributes[i].first == attribute)
return true;
}
return false;
}
-int BrowserAccessibility::GetIntAttribute(IntAttribute attribute) const {
- for (size_t i = 0; i < int_attributes_.size(); ++i) {
- if (int_attributes_[i].first == attribute)
- return int_attributes_[i].second;
+int BrowserAccessibility::GetIntAttribute(ui::AXIntAttribute attribute) const {
+ const ui::AXNodeData& data = GetData();
+ for (size_t i = 0; i < data.int_attributes.size(); ++i) {
+ if (data.int_attributes[i].first == attribute)
+ return data.int_attributes[i].second;
}
return 0;
}
bool BrowserAccessibility::GetIntAttribute(
- IntAttribute attribute, int* value) const {
- for (size_t i = 0; i < int_attributes_.size(); ++i) {
- if (int_attributes_[i].first == attribute) {
- *value = int_attributes_[i].second;
+ ui::AXIntAttribute attribute, int* value) const {
+ const ui::AXNodeData& data = GetData();
+ for (size_t i = 0; i < data.int_attributes.size(); ++i) {
+ if (data.int_attributes[i].first == attribute) {
+ *value = data.int_attributes[i].second;
return true;
}
}
@@ -412,9 +483,11 @@ bool BrowserAccessibility::GetIntAttribute(
return false;
}
-bool BrowserAccessibility::HasStringAttribute(StringAttribute attribute) const {
- for (size_t i = 0; i < string_attributes_.size(); ++i) {
- if (string_attributes_[i].first == attribute)
+bool BrowserAccessibility::HasStringAttribute(
+ ui::AXStringAttribute attribute) const {
+ const ui::AXNodeData& data = GetData();
+ for (size_t i = 0; i < data.string_attributes.size(); ++i) {
+ if (data.string_attributes[i].first == attribute)
return true;
}
@@ -422,21 +495,23 @@ bool BrowserAccessibility::HasStringAttribute(StringAttribute attribute) const {
}
const std::string& BrowserAccessibility::GetStringAttribute(
- StringAttribute attribute) const {
+ ui::AXStringAttribute attribute) const {
+ const ui::AXNodeData& data = GetData();
CR_DEFINE_STATIC_LOCAL(std::string, empty_string, ());
- for (size_t i = 0; i < string_attributes_.size(); ++i) {
- if (string_attributes_[i].first == attribute)
- return string_attributes_[i].second;
+ for (size_t i = 0; i < data.string_attributes.size(); ++i) {
+ if (data.string_attributes[i].first == attribute)
+ return data.string_attributes[i].second;
}
return empty_string;
}
bool BrowserAccessibility::GetStringAttribute(
- StringAttribute attribute, std::string* value) const {
- for (size_t i = 0; i < string_attributes_.size(); ++i) {
- if (string_attributes_[i].first == attribute) {
- *value = string_attributes_[i].second;
+ ui::AXStringAttribute attribute, std::string* value) const {
+ const ui::AXNodeData& data = GetData();
+ for (size_t i = 0; i < data.string_attributes.size(); ++i) {
+ if (data.string_attributes[i].first == attribute) {
+ *value = data.string_attributes[i].second;
return true;
}
}
@@ -445,39 +520,46 @@ bool BrowserAccessibility::GetStringAttribute(
}
base::string16 BrowserAccessibility::GetString16Attribute(
- StringAttribute attribute) const {
+ ui::AXStringAttribute attribute) const {
std::string value_utf8;
if (!GetStringAttribute(attribute, &value_utf8))
return base::string16();
- return UTF8ToUTF16(value_utf8);
+ return base::UTF8ToUTF16(value_utf8);
}
bool BrowserAccessibility::GetString16Attribute(
- StringAttribute attribute,
+ ui::AXStringAttribute attribute,
base::string16* value) const {
std::string value_utf8;
if (!GetStringAttribute(attribute, &value_utf8))
return false;
- *value = UTF8ToUTF16(value_utf8);
+ *value = base::UTF8ToUTF16(value_utf8);
return true;
}
void BrowserAccessibility::SetStringAttribute(
- StringAttribute attribute, const std::string& value) {
- for (size_t i = 0; i < string_attributes_.size(); ++i) {
- if (string_attributes_[i].first == attribute) {
- string_attributes_[i].second = value;
+ ui::AXStringAttribute attribute, const std::string& value) {
+ if (!node_)
+ return;
+ ui::AXNodeData data = GetData();
+ for (size_t i = 0; i < data.string_attributes.size(); ++i) {
+ if (data.string_attributes[i].first == attribute) {
+ data.string_attributes[i].second = value;
+ node_->SetData(data);
return;
}
}
- if (!value.empty())
- string_attributes_.push_back(std::make_pair(attribute, value));
+ if (!value.empty()) {
+ data.string_attributes.push_back(std::make_pair(attribute, value));
+ node_->SetData(data);
+ }
}
bool BrowserAccessibility::HasIntListAttribute(
- AccessibilityNodeData::IntListAttribute attribute) const {
- for (size_t i = 0; i < intlist_attributes_.size(); ++i) {
- if (intlist_attributes_[i].first == attribute)
+ ui::AXIntListAttribute attribute) const {
+ const ui::AXNodeData& data = GetData();
+ for (size_t i = 0; i < data.intlist_attributes.size(); ++i) {
+ if (data.intlist_attributes[i].first == attribute)
return true;
}
@@ -485,22 +567,24 @@ bool BrowserAccessibility::HasIntListAttribute(
}
const std::vector<int32>& BrowserAccessibility::GetIntListAttribute(
- AccessibilityNodeData::IntListAttribute attribute) const {
+ ui::AXIntListAttribute attribute) const {
+ const ui::AXNodeData& data = GetData();
CR_DEFINE_STATIC_LOCAL(std::vector<int32>, empty_vector, ());
- for (size_t i = 0; i < intlist_attributes_.size(); ++i) {
- if (intlist_attributes_[i].first == attribute)
- return intlist_attributes_[i].second;
+ for (size_t i = 0; i < data.intlist_attributes.size(); ++i) {
+ if (data.intlist_attributes[i].first == attribute)
+ return data.intlist_attributes[i].second;
}
return empty_vector;
}
bool BrowserAccessibility::GetIntListAttribute(
- AccessibilityNodeData::IntListAttribute attribute,
+ ui::AXIntListAttribute attribute,
std::vector<int32>* value) const {
- for (size_t i = 0; i < intlist_attributes_.size(); ++i) {
- if (intlist_attributes_[i].first == attribute) {
- *value = intlist_attributes_[i].second;
+ const ui::AXNodeData& data = GetData();
+ for (size_t i = 0; i < data.intlist_attributes.size(); ++i) {
+ if (data.intlist_attributes[i].first == attribute) {
+ *value = data.intlist_attributes[i].second;
return true;
}
}
@@ -510,10 +594,10 @@ bool BrowserAccessibility::GetIntListAttribute(
bool BrowserAccessibility::GetHtmlAttribute(
const char* html_attr, std::string* value) const {
- for (size_t i = 0; i < html_attributes_.size(); ++i) {
- const std::string& attr = html_attributes_[i].first;
+ for (size_t i = 0; i < GetHtmlAttributes().size(); ++i) {
+ const std::string& attr = GetHtmlAttributes()[i].first;
if (LowerCaseEqualsASCII(attr, html_attr)) {
- *value = html_attributes_[i].second;
+ *value = GetHtmlAttributes()[i].second;
return true;
}
}
@@ -526,7 +610,7 @@ bool BrowserAccessibility::GetHtmlAttribute(
std::string value_utf8;
if (!GetHtmlAttribute(html_attr, &value_utf8))
return false;
- *value = UTF8ToUTF16(value_utf8);
+ *value = base::UTF8ToUTF16(value_utf8);
return true;
}
@@ -555,24 +639,24 @@ bool BrowserAccessibility::GetAriaTristate(
return false; // Not set
}
-bool BrowserAccessibility::HasState(blink::WebAXState state_enum) const {
- return (state_ >> state_enum) & 1;
+bool BrowserAccessibility::HasState(ui::AXState state_enum) const {
+ return (GetState() >> state_enum) & 1;
}
bool BrowserAccessibility::IsEditableText() const {
// These roles don't have readonly set, but they're not editable text.
- if (role_ == blink::WebAXRoleScrollArea ||
- role_ == blink::WebAXRoleColumn ||
- role_ == blink::WebAXRoleTableHeaderContainer) {
+ if (GetRole() == ui::AX_ROLE_SCROLL_AREA ||
+ GetRole() == ui::AX_ROLE_COLUMN ||
+ GetRole() == ui::AX_ROLE_TABLE_HEADER_CONTAINER) {
return false;
}
// Note: WebAXStateReadonly being false means it's either a text control,
// or contenteditable. We also check for editable text roles to cover
// another element that has role=textbox set on it.
- return (!HasState(blink::WebAXStateReadonly) ||
- role_ == blink::WebAXRoleTextField ||
- role_ == blink::WebAXRoleTextArea);
+ return (!HasState(ui::AX_STATE_READ_ONLY) ||
+ GetRole() == ui::AX_ROLE_TEXT_FIELD ||
+ GetRole() == ui::AX_ROLE_TEXT_AREA);
}
std::string BrowserAccessibility::GetTextRecursive() const {
@@ -586,4 +670,14 @@ std::string BrowserAccessibility::GetTextRecursive() const {
return result;
}
+int BrowserAccessibility::GetStaticTextLenRecursive() const {
+ if (GetRole() == ui::AX_ROLE_STATIC_TEXT)
+ return static_cast<int>(GetStringAttribute(ui::AX_ATTR_VALUE).size());
+
+ int len = 0;
+ for (size_t i = 0; i < InternalChildCount(); ++i)
+ len += InternalGetChild(i)->GetStaticTextLenRecursive();
+ return len;
+}
+
} // namespace content
diff --git a/chromium/content/browser/accessibility/browser_accessibility.h b/chromium/content/browser/accessibility/browser_accessibility.h
index 1258803e376..17f70caf675 100644
--- a/chromium/content/browser/accessibility/browser_accessibility.h
+++ b/chromium/content/browser/accessibility/browser_accessibility.h
@@ -12,9 +12,10 @@
#include "base/basictypes.h"
#include "base/strings/string16.h"
#include "build/build_config.h"
-#include "content/common/accessibility_node_data.h"
#include "content/common/content_export.h"
#include "third_party/WebKit/public/web/WebAXEnums.h"
+#include "ui/accessibility/ax_node.h"
+#include "ui/accessibility/ax_node_data.h"
#if defined(OS_MACOSX) && __OBJC__
@class BrowserAccessibilityCocoa;
@@ -24,8 +25,6 @@ namespace content {
class BrowserAccessibilityManager;
#if defined(OS_WIN)
class BrowserAccessibilityWin;
-#elif defined(TOOLKIT_GTK)
-class BrowserAccessibilityGtk;
#endif
////////////////////////////////////////////////////////////////////////////////
@@ -48,47 +47,29 @@ class CONTENT_EXPORT BrowserAccessibility {
virtual ~BrowserAccessibility();
- // Detach all descendants of this subtree and push all of the node pointers,
- // including this node, onto the end of |nodes|.
- virtual void DetachTree(std::vector<BrowserAccessibility*>* nodes);
+ // 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.
+ virtual void Init(BrowserAccessibilityManager* manager, ui::AXNode* node);
- // Perform platform-specific initialization. This can be called multiple times
- // during the lifetime of this instance after the members of this base object
- // have been reset with new values from the renderer process.
- // Child dependent initialization can be done here.
- virtual void PostInitialize() {}
+ // Called after the object is first initialized and again every time
+ // its data changes.
+ virtual void OnDataChanged();
+
+ // Called after an atomic update to the tree finished and this object
+ // was created or changed in this update.
+ virtual void OnUpdateFinished() {}
// Returns true if this is a native platform-specific object, vs a
// cross-platform generic object.
virtual bool IsNative() const;
- // Initialize the tree structure of this object.
- void InitializeTreeStructure(
- BrowserAccessibilityManager* manager,
- BrowserAccessibility* parent,
- int32 renderer_id,
- int32 index_in_parent);
-
- // Initialize this object's data.
- void InitializeData(const AccessibilityNodeData& src);
-
- virtual void SwapChildren(std::vector<BrowserAccessibility*>& children);
-
- // Update the parent and index in parent if this node has been moved.
- void UpdateParent(BrowserAccessibility* parent, int index_in_parent);
-
- // Update this node's location, leaving everything else the same.
- virtual void SetLocation(const gfx::Rect& new_location);
+ // Called when the location changed.
+ virtual void OnLocationChanged() const {}
// Return true if this object is equal to or a descendant of |ancestor|.
bool IsDescendantOf(BrowserAccessibility* ancestor);
- // Returns the parent of this object, or NULL if it's the root.
- BrowserAccessibility* parent() const { return parent_; }
-
- // Returns the number of children of this object.
- uint32 child_count() const { return children_.size(); }
-
// Returns true if this is a leaf node on this platform, meaning any
// children should not be exposed to this platform's native accessibility
// layer. Each platform subclass should implement this itself.
@@ -134,14 +115,14 @@ class CONTENT_EXPORT BrowserAccessibility {
BrowserAccessibility* BrowserAccessibilityForPoint(const gfx::Point& point);
// Marks this object for deletion, releases our reference to it, and
- // recursively calls Destroy() on its children. May not delete
- // immediately due to reference counting.
+ // nulls out the pointer to the underlying AXNode. May not delete
+ // the object immediately due to reference counting.
//
// Reference counting is used on some platforms because the
// operating system may hold onto a reference to a BrowserAccessibility
// object even after we're through with it. When a BrowserAccessibility
// has had Destroy() called but its reference count is not yet zero,
- // queries on this object return failure
+ // instance_active() returns false and queries on this object return failure.
virtual void Destroy();
// Subclasses should override this to support platform reference counting.
@@ -154,32 +135,37 @@ class CONTENT_EXPORT BrowserAccessibility {
// Accessors
//
- const std::vector<BrowserAccessibility*>& children() const {
- return children_;
- }
- const std::vector<std::pair<std::string, std::string> >&
- html_attributes() const {
- return html_attributes_;
- }
- int32 index_in_parent() const { return index_in_parent_; }
- gfx::Rect location() const { return location_; }
BrowserAccessibilityManager* manager() const { return manager_; }
+ bool instance_active() const { return node_ != NULL; }
+ ui::AXNode* node() const { return node_; }
const std::string& name() const { return name_; }
const std::string& value() const { return value_; }
- int32 renderer_id() const { return renderer_id_; }
- int32 role() const { return role_; }
- int32 state() const { return state_; }
- bool instance_active() const { return instance_active_; }
-
void set_name(const std::string& name) { name_ = name; }
void set_value(const std::string& value) { value_ = value; }
+ // These access the internal accessibility tree, which doesn't necessarily
+ // reflect the accessibility tree that should be exposed on each platform.
+ // Use PlatformChildCount and PlatformGetChild to implement platform
+ // accessibility APIs.
+ uint32 InternalChildCount() const;
+ BrowserAccessibility* InternalGetChild(uint32 child_index) const;
+
+ BrowserAccessibility* GetParent() const;
+ int32 GetIndexInParent() const;
+
+ int32 GetId() const;
+ const ui::AXNodeData& GetData() const;
+ gfx::Rect GetLocation() const;
+ int32 GetRole() const;
+ int32 GetState() const;
+
+ typedef std::vector<std::pair<std::string, std::string> > HtmlAttributes;
+ const HtmlAttributes& GetHtmlAttributes() const;
+
#if defined(OS_MACOSX) && __OBJC__
BrowserAccessibilityCocoa* ToBrowserAccessibilityCocoa();
#elif defined(OS_WIN)
BrowserAccessibilityWin* ToBrowserAccessibilityWin();
-#elif defined(TOOLKIT_GTK)
- BrowserAccessibilityGtk* ToBrowserAccessibilityGtk();
#endif
// Accessing accessibility attributes:
@@ -197,43 +183,37 @@ class CONTENT_EXPORT BrowserAccessibility {
// attribute is not present. In addition, strings can be returned as
// either std::string or base::string16, for convenience.
- bool HasBoolAttribute(AccessibilityNodeData::BoolAttribute attr) const;
- bool GetBoolAttribute(AccessibilityNodeData::BoolAttribute attr) const;
- bool GetBoolAttribute(AccessibilityNodeData::BoolAttribute attr,
- bool* value) const;
+ bool HasBoolAttribute(ui::AXBoolAttribute attr) const;
+ bool GetBoolAttribute(ui::AXBoolAttribute attr) const;
+ bool GetBoolAttribute(ui::AXBoolAttribute attr, bool* value) const;
- bool HasFloatAttribute(AccessibilityNodeData::FloatAttribute attr) const;
- float GetFloatAttribute(AccessibilityNodeData::FloatAttribute attr) const;
- bool GetFloatAttribute(AccessibilityNodeData::FloatAttribute attr,
- float* value) const;
+ bool HasFloatAttribute(ui::AXFloatAttribute attr) const;
+ float GetFloatAttribute(ui::AXFloatAttribute attr) const;
+ bool GetFloatAttribute(ui::AXFloatAttribute attr, float* value) const;
- bool HasIntAttribute(AccessibilityNodeData::IntAttribute attribute) const;
- int GetIntAttribute(AccessibilityNodeData::IntAttribute attribute) const;
- bool GetIntAttribute(AccessibilityNodeData::IntAttribute attribute,
- int* value) const;
+ bool HasIntAttribute(ui::AXIntAttribute attribute) const;
+ int GetIntAttribute(ui::AXIntAttribute attribute) const;
+ bool GetIntAttribute(ui::AXIntAttribute attribute, int* value) const;
bool HasStringAttribute(
- AccessibilityNodeData::StringAttribute attribute) const;
- const std::string& GetStringAttribute(
- AccessibilityNodeData::StringAttribute attribute) const;
- bool GetStringAttribute(AccessibilityNodeData::StringAttribute attribute,
+ ui::AXStringAttribute attribute) const;
+ const std::string& GetStringAttribute(ui::AXStringAttribute attribute) const;
+ bool GetStringAttribute(ui::AXStringAttribute attribute,
std::string* value) const;
- bool GetString16Attribute(AccessibilityNodeData::StringAttribute attribute,
+ bool GetString16Attribute(ui::AXStringAttribute attribute,
base::string16* value) const;
base::string16 GetString16Attribute(
- AccessibilityNodeData::StringAttribute attribute) const;
+ ui::AXStringAttribute attribute) const;
- bool HasIntListAttribute(
- AccessibilityNodeData::IntListAttribute attribute) const;
+ bool HasIntListAttribute(ui::AXIntListAttribute attribute) const;
const std::vector<int32>& GetIntListAttribute(
- AccessibilityNodeData::IntListAttribute attribute) const;
- bool GetIntListAttribute(AccessibilityNodeData::IntListAttribute attribute,
+ ui::AXIntListAttribute attribute) const;
+ bool GetIntListAttribute(ui::AXIntListAttribute attribute,
std::vector<int32>* value) const;
- void SetStringAttribute(
- AccessibilityNodeData::StringAttribute attribute,
- const std::string& value);
+ void SetStringAttribute(ui::AXStringAttribute attribute,
+ const std::string& value);
// Retrieve the value of a html attribute from the attribute map and
// returns true if found.
@@ -257,7 +237,7 @@ class CONTENT_EXPORT BrowserAccessibility {
bool* is_mixed) const;
// Returns true if the bit corresponding to the given state enum is 1.
- bool HasState(blink::WebAXState state_enum) const;
+ bool HasState(ui::AXState state_enum) const;
// Returns true if this node is an editable text field of any kind.
bool IsEditableText() const;
@@ -266,56 +246,21 @@ class CONTENT_EXPORT BrowserAccessibility {
std::string GetTextRecursive() const;
protected:
- // Perform platform specific initialization. This can be called multiple times
- // during the lifetime of this instance after the members of this base object
- // have been reset with new values from the renderer process.
- // Perform child independent initialization in this method.
- virtual void PreInitialize() {}
-
BrowserAccessibility();
- // The manager of this tree of accessibility objects; needed for
- // global operations like focus tracking.
+ // The manager of this tree of accessibility objects.
BrowserAccessibilityManager* manager_;
- // The parent of this object, may be NULL if we're the root object.
- BrowserAccessibility* parent_;
+ // The underlying node.
+ ui::AXNode* node_;
private:
- // The index of this within its parent object.
- int32 index_in_parent_;
-
- // The ID of this object in the renderer process.
- int32 renderer_id_;
-
- // The children of this object.
- std::vector<BrowserAccessibility*> children_;
+ // Return the sum of the lengths of all static text descendants,
+ // including this object if it's static text.
+ int GetStaticTextLenRecursive() const;
- // Accessibility metadata from the renderer
std::string name_;
std::string value_;
- std::vector<std::pair<
- AccessibilityNodeData::BoolAttribute, bool> > bool_attributes_;
- std::vector<std::pair<
- AccessibilityNodeData::FloatAttribute, float> > float_attributes_;
- std::vector<std::pair<
- AccessibilityNodeData::IntAttribute, int> > int_attributes_;
- std::vector<std::pair<
- AccessibilityNodeData::StringAttribute, std::string> > string_attributes_;
- std::vector<std::pair<
- AccessibilityNodeData::IntListAttribute, std::vector<int32> > >
- intlist_attributes_;
- std::vector<std::pair<std::string, std::string> > html_attributes_;
- int32 role_;
- int32 state_;
- gfx::Rect location_;
-
- // BrowserAccessibility objects are reference-counted on some platforms.
- // When we're done with this object and it's removed from our accessibility
- // tree, a client may still be holding onto a pointer to this object, so
- // we mark it as inactive so that calls to any of this object's methods
- // immediately return failure.
- bool instance_active_;
private:
DISALLOW_COPY_AND_ASSIGN(BrowserAccessibility);
diff --git a/chromium/content/browser/accessibility/browser_accessibility_android.cc b/chromium/content/browser/accessibility/browser_accessibility_android.cc
index 444aee29656..8a8514a0d80 100644
--- a/chromium/content/browser/accessibility/browser_accessibility_android.cc
+++ b/chromium/content/browser/accessibility/browser_accessibility_android.cc
@@ -7,7 +7,6 @@
#include "base/strings/utf_string_conversions.h"
#include "content/browser/accessibility/browser_accessibility_manager_android.h"
#include "content/common/accessibility_messages.h"
-#include "content/common/accessibility_node_data.h"
namespace {
@@ -57,13 +56,13 @@ bool BrowserAccessibilityAndroid::IsNative() const {
}
bool BrowserAccessibilityAndroid::PlatformIsLeaf() const {
- if (child_count() == 0)
+ if (InternalChildCount() == 0)
return true;
// Iframes are always allowed to contain children.
if (IsIframe() ||
- role() == blink::WebAXRoleRootWebArea ||
- role() == blink::WebAXRoleWebArea) {
+ GetRole() == ui::AX_ROLE_ROOT_WEB_AREA ||
+ GetRole() == ui::AX_ROLE_WEB_AREA) {
return false;
}
@@ -73,11 +72,11 @@ bool BrowserAccessibilityAndroid::PlatformIsLeaf() const {
// Headings with text can drop their children.
base::string16 name = GetText();
- if (role() == blink::WebAXRoleHeading && !name.empty())
+ if (GetRole() == ui::AX_ROLE_HEADING && !name.empty())
return true;
// Focusable nodes with text can drop their children.
- if (HasState(blink::WebAXStateFocusable) && !name.empty())
+ if (HasState(ui::AX_STATE_FOCUSABLE) && !name.empty())
return true;
// Nodes with only static text as children can drop their children.
@@ -92,18 +91,18 @@ bool BrowserAccessibilityAndroid::IsCheckable() const {
bool is_aria_pressed_defined;
bool is_mixed;
GetAriaTristate("aria-pressed", &is_aria_pressed_defined, &is_mixed);
- if (role() == blink::WebAXRoleCheckBox ||
- role() == blink::WebAXRoleRadioButton ||
+ if (GetRole() == ui::AX_ROLE_CHECK_BOX ||
+ GetRole() == ui::AX_ROLE_RADIO_BUTTON ||
is_aria_pressed_defined) {
checkable = true;
}
- if (HasState(blink::WebAXStateChecked))
+ if (HasState(ui::AX_STATE_CHECKED))
checkable = true;
return checkable;
}
bool BrowserAccessibilityAndroid::IsChecked() const {
- return HasState(blink::WebAXStateChecked);
+ return HasState(ui::AX_STATE_CHECKED);
}
bool BrowserAccessibilityAndroid::IsClickable() const {
@@ -111,21 +110,21 @@ bool BrowserAccessibilityAndroid::IsClickable() const {
}
bool BrowserAccessibilityAndroid::IsCollection() const {
- return (role() == blink::WebAXRoleGrid ||
- role() == blink::WebAXRoleList ||
- role() == blink::WebAXRoleListBox ||
- role() == blink::WebAXRoleTable ||
- role() == blink::WebAXRoleTree);
+ return (GetRole() == ui::AX_ROLE_GRID ||
+ GetRole() == ui::AX_ROLE_LIST ||
+ GetRole() == ui::AX_ROLE_LIST_BOX ||
+ GetRole() == ui::AX_ROLE_TABLE ||
+ GetRole() == ui::AX_ROLE_TREE);
}
bool BrowserAccessibilityAndroid::IsCollectionItem() const {
- return (role() == blink::WebAXRoleCell ||
- role() == blink::WebAXRoleColumnHeader ||
- role() == blink::WebAXRoleDescriptionListTerm ||
- role() == blink::WebAXRoleListBoxOption ||
- role() == blink::WebAXRoleListItem ||
- role() == blink::WebAXRoleRowHeader ||
- role() == blink::WebAXRoleTreeItem);
+ return (GetRole() == ui::AX_ROLE_CELL ||
+ GetRole() == ui::AX_ROLE_COLUMN_HEADER ||
+ GetRole() == ui::AX_ROLE_DESCRIPTION_LIST_TERM ||
+ GetRole() == ui::AX_ROLE_LIST_BOX_OPTION ||
+ GetRole() == ui::AX_ROLE_LIST_ITEM ||
+ GetRole() == ui::AX_ROLE_ROW_HEADER ||
+ GetRole() == ui::AX_ROLE_TREE_ITEM);
}
bool BrowserAccessibilityAndroid::IsContentInvalid() const {
@@ -138,13 +137,13 @@ bool BrowserAccessibilityAndroid::IsDismissable() const {
}
bool BrowserAccessibilityAndroid::IsEnabled() const {
- return HasState(blink::WebAXStateEnabled);
+ return HasState(ui::AX_STATE_ENABLED);
}
bool BrowserAccessibilityAndroid::IsFocusable() const {
- bool focusable = HasState(blink::WebAXStateFocusable);
+ bool focusable = HasState(ui::AX_STATE_FOCUSABLE);
if (IsIframe() ||
- role() == blink::WebAXRoleWebArea) {
+ GetRole() == ui::AX_ROLE_WEB_AREA) {
focusable = false;
}
return focusable;
@@ -155,98 +154,106 @@ bool BrowserAccessibilityAndroid::IsFocused() const {
}
bool BrowserAccessibilityAndroid::IsHeading() const {
- return (role() == blink::WebAXRoleColumnHeader ||
- role() == blink::WebAXRoleHeading ||
- role() == blink::WebAXRoleRowHeader);
+ return (GetRole() == ui::AX_ROLE_COLUMN_HEADER ||
+ GetRole() == ui::AX_ROLE_HEADING ||
+ GetRole() == ui::AX_ROLE_ROW_HEADER);
}
bool BrowserAccessibilityAndroid::IsHierarchical() const {
- return (role() == blink::WebAXRoleList ||
- role() == blink::WebAXRoleTree);
+ return (GetRole() == ui::AX_ROLE_LIST ||
+ GetRole() == ui::AX_ROLE_TREE);
+}
+
+bool BrowserAccessibilityAndroid::IsLink() const {
+ return GetRole() == ui::AX_ROLE_LINK ||
+ GetRole() == ui::AX_ROLE_IMAGE_MAP_LINK;
}
bool BrowserAccessibilityAndroid::IsMultiLine() const {
- return role() == blink::WebAXRoleTextArea;
+ return GetRole() == ui::AX_ROLE_TEXT_AREA;
}
bool BrowserAccessibilityAndroid::IsPassword() const {
- return HasState(blink::WebAXStateProtected);
+ return HasState(ui::AX_STATE_PROTECTED);
}
bool BrowserAccessibilityAndroid::IsRangeType() const {
- return (role() == blink::WebAXRoleProgressIndicator ||
- role() == blink::WebAXRoleScrollBar ||
- role() == blink::WebAXRoleSlider);
+ return (GetRole() == ui::AX_ROLE_PROGRESS_INDICATOR ||
+ GetRole() == ui::AX_ROLE_SCROLL_BAR ||
+ GetRole() == ui::AX_ROLE_SLIDER);
}
bool BrowserAccessibilityAndroid::IsScrollable() const {
int dummy;
- return GetIntAttribute(AccessibilityNodeData::ATTR_SCROLL_X_MAX, &dummy);
+ return GetIntAttribute(ui::AX_ATTR_SCROLL_X_MAX, &dummy);
}
bool BrowserAccessibilityAndroid::IsSelected() const {
- return HasState(blink::WebAXStateSelected);
+ return HasState(ui::AX_STATE_SELECTED);
}
bool BrowserAccessibilityAndroid::IsVisibleToUser() const {
- return !HasState(blink::WebAXStateInvisible);
+ return !HasState(ui::AX_STATE_INVISIBLE);
}
bool BrowserAccessibilityAndroid::CanOpenPopup() const {
- return HasState(blink::WebAXStateHaspopup);
+ return HasState(ui::AX_STATE_HASPOPUP);
}
const char* BrowserAccessibilityAndroid::GetClassName() const {
const char* class_name = NULL;
- switch(role()) {
- case blink::WebAXRoleEditableText:
- case blink::WebAXRoleSpinButton:
- case blink::WebAXRoleTextArea:
- case blink::WebAXRoleTextField:
+ switch(GetRole()) {
+ case ui::AX_ROLE_EDITABLE_TEXT:
+ case ui::AX_ROLE_SPIN_BUTTON:
+ case ui::AX_ROLE_TEXT_AREA:
+ case ui::AX_ROLE_TEXT_FIELD:
class_name = "android.widget.EditText";
break;
- case blink::WebAXRoleSlider:
+ case ui::AX_ROLE_SLIDER:
class_name = "android.widget.SeekBar";
break;
- case blink::WebAXRoleComboBox:
+ case ui::AX_ROLE_COMBO_BOX:
class_name = "android.widget.Spinner";
break;
- case blink::WebAXRoleButton:
- case blink::WebAXRoleMenuButton:
- case blink::WebAXRolePopUpButton:
+ case ui::AX_ROLE_BUTTON:
+ case ui::AX_ROLE_MENU_BUTTON:
+ case ui::AX_ROLE_POP_UP_BUTTON:
class_name = "android.widget.Button";
break;
- case blink::WebAXRoleCheckBox:
+ case ui::AX_ROLE_CHECK_BOX:
class_name = "android.widget.CheckBox";
break;
- case blink::WebAXRoleRadioButton:
+ case ui::AX_ROLE_RADIO_BUTTON:
class_name = "android.widget.RadioButton";
break;
- case blink::WebAXRoleToggleButton:
+ case ui::AX_ROLE_TOGGLE_BUTTON:
class_name = "android.widget.ToggleButton";
break;
- case blink::WebAXRoleCanvas:
- case blink::WebAXRoleImage:
+ case ui::AX_ROLE_CANVAS:
+ case ui::AX_ROLE_IMAGE:
class_name = "android.widget.Image";
break;
- case blink::WebAXRoleProgressIndicator:
+ case ui::AX_ROLE_PROGRESS_INDICATOR:
class_name = "android.widget.ProgressBar";
break;
- case blink::WebAXRoleTabList:
+ case ui::AX_ROLE_TAB_LIST:
class_name = "android.widget.TabWidget";
break;
- case blink::WebAXRoleGrid:
- case blink::WebAXRoleTable:
+ case ui::AX_ROLE_GRID:
+ case ui::AX_ROLE_TABLE:
class_name = "android.widget.GridView";
break;
- case blink::WebAXRoleList:
- case blink::WebAXRoleListBox:
+ case ui::AX_ROLE_LIST:
+ case ui::AX_ROLE_LIST_BOX:
class_name = "android.widget.ListView";
break;
- case blink::WebAXRoleDialog:
+ case ui::AX_ROLE_DIALOG:
class_name = "android.app.Dialog";
break;
+ case ui::AX_ROLE_ROOT_WEB_AREA:
+ class_name = "android.webkit.WebView";
+ break;
default:
class_name = "android.view.View";
break;
@@ -257,59 +264,89 @@ const char* BrowserAccessibilityAndroid::GetClassName() const {
base::string16 BrowserAccessibilityAndroid::GetText() const {
if (IsIframe() ||
- role() == blink::WebAXRoleWebArea) {
+ GetRole() == ui::AX_ROLE_WEB_AREA) {
return base::string16();
}
- base::string16 description = GetString16Attribute(
- AccessibilityNodeData::ATTR_DESCRIPTION);
+ // See comment in browser_accessibility_win.cc for details.
+ // The difference here is that we can only expose one accessible
+ // name on Android, not 2 or 3 like on Windows or Mac.
+ //
+ // The basic rule is: prefer description (aria-labelledby or aria-label),
+ // then help (title), then name (inner text), then value (control value).
+ // However, if title_elem_id is set, that means there's a label element
+ // supplying the name and then name takes precedence over help.
+ // TODO(dmazzoni): clean this up by providing more granular labels in
+ // Blink, making the platform-specific mapping to accessible text simpler.
+ base::string16 description = GetString16Attribute(ui::AX_ATTR_DESCRIPTION);
+ base::string16 help = GetString16Attribute(ui::AX_ATTR_HELP);
+ int title_elem_id = GetIntAttribute(
+ ui::AX_ATTR_TITLE_UI_ELEMENT);
base::string16 text;
- if (!name().empty())
- text = base::UTF8ToUTF16(name());
- else if (!description.empty())
+ if (!description.empty())
text = description;
+ else if (title_elem_id && !name().empty())
+ text = base::UTF8ToUTF16(name());
+ else if (!help.empty())
+ text = help;
+ else if (!name().empty())
+ text = base::UTF8ToUTF16(name());
else if (!value().empty())
text = base::UTF8ToUTF16(value());
// This is called from PlatformIsLeaf, so don't call PlatformChildCount
// from within this!
if (text.empty() && HasOnlyStaticTextChildren()) {
- for (uint32 i = 0; i < child_count(); i++) {
- BrowserAccessibility* child = children()[i];
+ for (uint32 i = 0; i < InternalChildCount(); i++) {
+ BrowserAccessibility* child = InternalGetChild(i);
text += static_cast<BrowserAccessibilityAndroid*>(child)->GetText();
}
}
- switch(role()) {
- case blink::WebAXRoleImageMapLink:
- case blink::WebAXRoleLink:
- if (!text.empty())
- text += ASCIIToUTF16(" ");
- text += ASCIIToUTF16("Link");
- break;
- case blink::WebAXRoleHeading:
+ switch(GetRole()) {
+ case ui::AX_ROLE_HEADING:
// Only append "heading" if this node already has text.
if (!text.empty())
- text += ASCIIToUTF16(" Heading");
+ text += base::ASCIIToUTF16(" Heading");
break;
}
+ if (text.empty() && IsLink()) {
+ base::string16 url = GetString16Attribute(ui::AX_ATTR_URL);
+ // Given a url like http://foo.com/bar/baz.png, just return the
+ // base name, e.g., "baz".
+ int trailing_slashes = 0;
+ while (url.size() - trailing_slashes > 0 &&
+ url[url.size() - trailing_slashes - 1] == '/') {
+ trailing_slashes++;
+ }
+ if (trailing_slashes)
+ url = url.substr(0, url.size() - trailing_slashes);
+ size_t slash_index = url.rfind('/');
+ if (slash_index != std::string::npos)
+ url = url.substr(slash_index + 1);
+ size_t dot_index = url.rfind('.');
+ if (dot_index != std::string::npos)
+ url = url.substr(0, dot_index);
+ text = url;
+ }
+
return text;
}
int BrowserAccessibilityAndroid::GetItemIndex() const {
int index = 0;
- switch(role()) {
- case blink::WebAXRoleListItem:
- case blink::WebAXRoleListBoxOption:
- case blink::WebAXRoleTreeItem:
- index = index_in_parent();
+ switch(GetRole()) {
+ case ui::AX_ROLE_LIST_ITEM:
+ case ui::AX_ROLE_LIST_BOX_OPTION:
+ case ui::AX_ROLE_TREE_ITEM:
+ index = GetIndexInParent();
break;
- case blink::WebAXRoleSlider:
- case blink::WebAXRoleProgressIndicator: {
+ case ui::AX_ROLE_SLIDER:
+ case ui::AX_ROLE_PROGRESS_INDICATOR: {
float value_for_range;
if (GetFloatAttribute(
- AccessibilityNodeData::ATTR_VALUE_FOR_RANGE, &value_for_range)) {
+ ui::AX_ATTR_VALUE_FOR_RANGE, &value_for_range)) {
index = static_cast<int>(value_for_range);
}
break;
@@ -320,15 +357,15 @@ int BrowserAccessibilityAndroid::GetItemIndex() const {
int BrowserAccessibilityAndroid::GetItemCount() const {
int count = 0;
- switch(role()) {
- case blink::WebAXRoleList:
- case blink::WebAXRoleListBox:
+ switch(GetRole()) {
+ case ui::AX_ROLE_LIST:
+ case ui::AX_ROLE_LIST_BOX:
count = PlatformChildCount();
break;
- case blink::WebAXRoleSlider:
- case blink::WebAXRoleProgressIndicator: {
+ case ui::AX_ROLE_SLIDER:
+ case ui::AX_ROLE_PROGRESS_INDICATOR: {
float max_value_for_range;
- if (GetFloatAttribute(AccessibilityNodeData::ATTR_MAX_VALUE_FOR_RANGE,
+ if (GetFloatAttribute(ui::AX_ATTR_MAX_VALUE_FOR_RANGE,
&max_value_for_range)) {
count = static_cast<int>(max_value_for_range);
}
@@ -340,25 +377,25 @@ int BrowserAccessibilityAndroid::GetItemCount() const {
int BrowserAccessibilityAndroid::GetScrollX() const {
int value = 0;
- GetIntAttribute(AccessibilityNodeData::ATTR_SCROLL_X, &value);
+ GetIntAttribute(ui::AX_ATTR_SCROLL_X, &value);
return value;
}
int BrowserAccessibilityAndroid::GetScrollY() const {
int value = 0;
- GetIntAttribute(AccessibilityNodeData::ATTR_SCROLL_Y, &value);
+ GetIntAttribute(ui::AX_ATTR_SCROLL_Y, &value);
return value;
}
int BrowserAccessibilityAndroid::GetMaxScrollX() const {
int value = 0;
- GetIntAttribute(AccessibilityNodeData::ATTR_SCROLL_X_MAX, &value);
+ GetIntAttribute(ui::AX_ATTR_SCROLL_X_MAX, &value);
return value;
}
int BrowserAccessibilityAndroid::GetMaxScrollY() const {
int value = 0;
- GetIntAttribute(AccessibilityNodeData::ATTR_SCROLL_Y_MAX, &value);
+ GetIntAttribute(ui::AX_ATTR_SCROLL_Y_MAX, &value);
return value;
}
@@ -414,13 +451,13 @@ base::string16 BrowserAccessibilityAndroid::GetTextChangeBeforeText() const {
int BrowserAccessibilityAndroid::GetSelectionStart() const {
int sel_start = 0;
- GetIntAttribute(AccessibilityNodeData::ATTR_TEXT_SEL_START, &sel_start);
+ GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START, &sel_start);
return sel_start;
}
int BrowserAccessibilityAndroid::GetSelectionEnd() const {
int sel_end = 0;
- GetIntAttribute(AccessibilityNodeData::ATTR_TEXT_SEL_END, &sel_end);
+ GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &sel_end);
return sel_end;
}
@@ -430,7 +467,7 @@ int BrowserAccessibilityAndroid::GetEditableTextLength() const {
int BrowserAccessibilityAndroid::AndroidInputType() const {
std::string html_tag = GetStringAttribute(
- AccessibilityNodeData::ATTR_HTML_TAG);
+ ui::AX_ATTR_HTML_TAG);
if (html_tag != "input")
return ANDROID_TEXT_INPUTTYPE_TYPE_NULL;
@@ -466,7 +503,7 @@ int BrowserAccessibilityAndroid::AndroidInputType() const {
int BrowserAccessibilityAndroid::AndroidLiveRegionType() const {
std::string live = GetStringAttribute(
- AccessibilityNodeData::ATTR_LIVE_STATUS);
+ ui::AX_ATTR_LIVE_STATUS);
if (live == "polite")
return ANDROID_VIEW_VIEW_ACCESSIBILITY_LIVE_REGION_POLITE;
else if (live == "assertive")
@@ -479,14 +516,14 @@ int BrowserAccessibilityAndroid::AndroidRangeType() const {
}
int BrowserAccessibilityAndroid::RowCount() const {
- if (role() == blink::WebAXRoleGrid ||
- role() == blink::WebAXRoleTable) {
- return CountChildrenWithRole(blink::WebAXRoleRow);
+ if (GetRole() == ui::AX_ROLE_GRID ||
+ GetRole() == ui::AX_ROLE_TABLE) {
+ return CountChildrenWithRole(ui::AX_ROLE_ROW);
}
- if (role() == blink::WebAXRoleList ||
- role() == blink::WebAXRoleListBox ||
- role() == blink::WebAXRoleTree) {
+ if (GetRole() == ui::AX_ROLE_LIST ||
+ GetRole() == ui::AX_ROLE_LIST_BOX ||
+ GetRole() == ui::AX_ROLE_TREE) {
return PlatformChildCount();
}
@@ -494,53 +531,53 @@ int BrowserAccessibilityAndroid::RowCount() const {
}
int BrowserAccessibilityAndroid::ColumnCount() const {
- if (role() == blink::WebAXRoleGrid ||
- role() == blink::WebAXRoleTable) {
- return CountChildrenWithRole(blink::WebAXRoleColumn);
+ if (GetRole() == ui::AX_ROLE_GRID ||
+ GetRole() == ui::AX_ROLE_TABLE) {
+ return CountChildrenWithRole(ui::AX_ROLE_COLUMN);
}
return 0;
}
int BrowserAccessibilityAndroid::RowIndex() const {
- if (role() == blink::WebAXRoleListItem ||
- role() == blink::WebAXRoleListBoxOption ||
- role() == blink::WebAXRoleTreeItem) {
- return index_in_parent();
+ if (GetRole() == ui::AX_ROLE_LIST_ITEM ||
+ GetRole() == ui::AX_ROLE_LIST_BOX_OPTION ||
+ GetRole() == ui::AX_ROLE_TREE_ITEM) {
+ return GetIndexInParent();
}
- return GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_CELL_ROW_INDEX);
+ return GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX);
}
int BrowserAccessibilityAndroid::RowSpan() const {
- return GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_CELL_ROW_SPAN);
+ return GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_SPAN);
}
int BrowserAccessibilityAndroid::ColumnIndex() const {
- return GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX);
+ return GetIntAttribute(ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX);
}
int BrowserAccessibilityAndroid::ColumnSpan() const {
- return GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_SPAN);
+ return GetIntAttribute(ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN);
}
float BrowserAccessibilityAndroid::RangeMin() const {
- return GetFloatAttribute(AccessibilityNodeData::ATTR_MIN_VALUE_FOR_RANGE);
+ return GetFloatAttribute(ui::AX_ATTR_MIN_VALUE_FOR_RANGE);
}
float BrowserAccessibilityAndroid::RangeMax() const {
- return GetFloatAttribute(AccessibilityNodeData::ATTR_MAX_VALUE_FOR_RANGE);
+ return GetFloatAttribute(ui::AX_ATTR_MAX_VALUE_FOR_RANGE);
}
float BrowserAccessibilityAndroid::RangeCurrentValue() const {
- return GetFloatAttribute(AccessibilityNodeData::ATTR_VALUE_FOR_RANGE);
+ return GetFloatAttribute(ui::AX_ATTR_VALUE_FOR_RANGE);
}
bool BrowserAccessibilityAndroid::HasFocusableChild() const {
// This is called from PlatformIsLeaf, so don't call PlatformChildCount
// from within this!
- for (uint32 i = 0; i < child_count(); i++) {
- BrowserAccessibility* child = children()[i];
- if (child->HasState(blink::WebAXStateFocusable))
+ for (uint32 i = 0; i < InternalChildCount(); i++) {
+ BrowserAccessibility* child = InternalGetChild(i);
+ if (child->HasState(ui::AX_STATE_FOCUSABLE))
return true;
if (static_cast<BrowserAccessibilityAndroid*>(child)->HasFocusableChild())
return true;
@@ -551,9 +588,9 @@ bool BrowserAccessibilityAndroid::HasFocusableChild() const {
bool BrowserAccessibilityAndroid::HasOnlyStaticTextChildren() const {
// This is called from PlatformIsLeaf, so don't call PlatformChildCount
// from within this!
- for (uint32 i = 0; i < child_count(); i++) {
- BrowserAccessibility* child = children()[i];
- if (child->role() != blink::WebAXRoleStaticText)
+ for (uint32 i = 0; i < InternalChildCount(); i++) {
+ BrowserAccessibility* child = InternalGetChild(i);
+ if (child->GetRole() != ui::AX_ROLE_STATIC_TEXT)
return false;
}
return true;
@@ -561,12 +598,12 @@ bool BrowserAccessibilityAndroid::HasOnlyStaticTextChildren() const {
bool BrowserAccessibilityAndroid::IsIframe() const {
base::string16 html_tag = GetString16Attribute(
- AccessibilityNodeData::ATTR_HTML_TAG);
- return html_tag == ASCIIToUTF16("iframe");
+ ui::AX_ATTR_HTML_TAG);
+ return html_tag == base::ASCIIToUTF16("iframe");
}
-void BrowserAccessibilityAndroid::PostInitialize() {
- BrowserAccessibility::PostInitialize();
+void BrowserAccessibilityAndroid::OnDataChanged() {
+ BrowserAccessibility::OnDataChanged();
if (IsEditableText()) {
if (base::UTF8ToUTF16(value()) != new_value_) {
@@ -575,12 +612,12 @@ void BrowserAccessibilityAndroid::PostInitialize() {
}
}
- if (role() == blink::WebAXRoleAlert && first_time_)
- manager()->NotifyAccessibilityEvent(blink::WebAXEventAlert, this);
+ if (GetRole() == ui::AX_ROLE_ALERT && first_time_)
+ manager()->NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, this);
base::string16 live;
if (GetString16Attribute(
- AccessibilityNodeData::ATTR_CONTAINER_LIVE_STATUS, &live)) {
+ ui::AX_ATTR_CONTAINER_LIVE_STATUS, &live)) {
NotifyLiveRegionUpdate(live);
}
@@ -596,18 +633,17 @@ void BrowserAccessibilityAndroid::NotifyLiveRegionUpdate(
base::string16 text = GetText();
if (cached_text_ != text) {
if (!text.empty()) {
- manager()->NotifyAccessibilityEvent(blink::WebAXEventShow,
+ manager()->NotifyAccessibilityEvent(ui::AX_EVENT_SHOW,
this);
}
cached_text_ = text;
}
}
-int BrowserAccessibilityAndroid::CountChildrenWithRole(
- blink::WebAXRole role) const {
+int BrowserAccessibilityAndroid::CountChildrenWithRole(ui::AXRole role) const {
int count = 0;
for (uint32 i = 0; i < PlatformChildCount(); i++) {
- if (PlatformGetChild(i)->role() == role)
+ if (PlatformGetChild(i)->GetRole() == role)
count++;
}
return count;
diff --git a/chromium/content/browser/accessibility/browser_accessibility_android.h b/chromium/content/browser/accessibility/browser_accessibility_android.h
index 2968184b53c..f78a96dd54d 100644
--- a/chromium/content/browser/accessibility/browser_accessibility_android.h
+++ b/chromium/content/browser/accessibility/browser_accessibility_android.h
@@ -13,7 +13,7 @@ namespace content {
class BrowserAccessibilityAndroid : public BrowserAccessibility {
public:
// Overrides from BrowserAccessibility.
- virtual void PostInitialize() OVERRIDE;
+ virtual void OnDataChanged() OVERRIDE;
virtual bool IsNative() const OVERRIDE;
virtual bool PlatformIsLeaf() const OVERRIDE;
@@ -30,6 +30,7 @@ class BrowserAccessibilityAndroid : public BrowserAccessibility {
bool IsFocused() const;
bool IsHeading() const;
bool IsHierarchical() const;
+ bool IsLink() const;
bool IsMultiLine() const;
bool IsPassword() const;
bool IsRangeType() const;
@@ -39,6 +40,8 @@ class BrowserAccessibilityAndroid : public BrowserAccessibility {
bool CanOpenPopup() const;
+ bool HasFocusableChild() const;
+
const char* GetClassName() const;
base::string16 GetText() const;
@@ -81,13 +84,12 @@ class BrowserAccessibilityAndroid : public BrowserAccessibility {
BrowserAccessibilityAndroid();
- bool HasFocusableChild() const;
bool HasOnlyStaticTextChildren() const;
bool IsIframe() const;
void NotifyLiveRegionUpdate(base::string16& aria_live);
- int CountChildrenWithRole(blink::WebAXRole role) const;
+ int CountChildrenWithRole(ui::AXRole role) const;
base::string16 cached_text_;
bool first_time_;
diff --git a/chromium/content/browser/accessibility/browser_accessibility_cocoa.h b/chromium/content/browser/accessibility/browser_accessibility_cocoa.h
index 9ac751fdb41..5fc708f867b 100644
--- a/chromium/content/browser/accessibility/browser_accessibility_cocoa.h
+++ b/chromium/content/browser/accessibility/browser_accessibility_cocoa.h
@@ -9,8 +9,7 @@
#import "base/mac/scoped_nsobject.h"
#include "content/browser/accessibility/browser_accessibility.h"
-#import "content/browser/accessibility/browser_accessibility_delegate_mac.h"
-#include "third_party/WebKit/public/web/WebAXEnums.h"
+#include "content/browser/accessibility/browser_accessibility_manager.h"
// BrowserAccessibilityCocoa is a cocoa wrapper around the BrowserAccessibility
// object. The renderer converts webkit's accessibility tree into a
@@ -20,15 +19,11 @@
@private
content::BrowserAccessibility* browserAccessibility_;
base::scoped_nsobject<NSMutableArray> children_;
- id<BrowserAccessibilityDelegateCocoa> delegate_;
}
// This creates a cocoa browser accessibility object around
-// the cross platform BrowserAccessibility object. The delegate is
-// used to communicate with the host renderer. None of these
-// parameters can be null.
-- (id)initWithObject:(content::BrowserAccessibility*)accessibility
- delegate:(id<BrowserAccessibilityDelegateCocoa>)delegate;
+// the cross platform BrowserAccessibility object, which can't be null.
+- (id)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
@@ -40,11 +35,22 @@
// Convenience method to get the internal, cross-platform role
// from browserAccessibility_.
-- (blink::WebAXRole)internalRole;
+- (ui::AXRole)internalRole;
+
+// Convenience method to get the BrowserAccessibilityDelegate from
+// the manager.
+- (content::BrowserAccessibilityDelegate*)delegate;
+
+// Convert the local objet's origin to a global point.
+- (NSPoint)pointInScreen:(NSPoint)origin
+ size:(NSSize)size;
// Return the method name for the given attribute. For testing only.
- (NSString*)methodNameForAttribute:(NSString*)attribute;
+// Swap the children array with the given scoped_nsobject.
+- (void)swapChildren:(base::scoped_nsobject<NSMutableArray>*)other;
+
// Internally-used method.
@property(nonatomic, readonly) NSPoint origin;
@@ -98,7 +104,7 @@
@property(nonatomic, readonly) NSArray* tabs;
@property(nonatomic, readonly) NSString* title;
@property(nonatomic, readonly) id titleUIElement;
-@property(nonatomic, readonly) NSString* url;
+@property(nonatomic, readonly) NSURL* url;
@property(nonatomic, readonly) NSString* value;
@property(nonatomic, readonly) NSString* valueDescription;
@property(nonatomic, readonly) NSValue* visibleCharacterRange;
diff --git a/chromium/content/browser/accessibility/browser_accessibility_cocoa.mm b/chromium/content/browser/accessibility/browser_accessibility_cocoa.mm
index 9364dbbfc00..232e4827750 100644
--- a/chromium/content/browser/accessibility/browser_accessibility_cocoa.mm
+++ b/chromium/content/browser/accessibility/browser_accessibility_cocoa.mm
@@ -22,16 +22,16 @@
// this object.
extern "C" void NSAccessibilityUnregisterUniqueIdForUIElement(id element);
-using content::AccessibilityNodeData;
+using ui::AXNodeData;
using content::BrowserAccessibility;
using content::BrowserAccessibilityManager;
using content::BrowserAccessibilityManagerMac;
using content::ContentClient;
-typedef AccessibilityNodeData::StringAttribute StringAttribute;
+typedef ui::AXStringAttribute StringAttribute;
namespace {
-// Returns an autoreleased copy of the AccessibilityNodeData's attribute.
+// Returns an autoreleased copy of the AXNodeData's attribute.
NSString* NSStringForStringAttribute(
BrowserAccessibility* browserAccessibility,
StringAttribute attribute) {
@@ -40,129 +40,127 @@ NSString* NSStringForStringAttribute(
}
struct MapEntry {
- blink::WebAXRole webKitValue;
+ ui::AXRole webKitValue;
NSString* nativeValue;
};
-typedef std::map<blink::WebAXRole, NSString*> RoleMap;
+typedef std::map<ui::AXRole, NSString*> RoleMap;
-// GetState checks the bitmask used in AccessibilityNodeData to check
+// GetState checks the bitmask used in AXNodeData to check
// if the given state was set on the accessibility object.
-bool GetState(BrowserAccessibility* accessibility, blink::WebAXState state) {
- return ((accessibility->state() >> state) & 1);
+bool GetState(BrowserAccessibility* accessibility, ui::AXState state) {
+ return ((accessibility->GetState() >> state) & 1);
}
RoleMap BuildRoleMap() {
const MapEntry roles[] = {
- { blink::WebAXRoleAlert, NSAccessibilityGroupRole },
- { blink::WebAXRoleAlertDialog, NSAccessibilityGroupRole },
- { blink::WebAXRoleAnnotation, NSAccessibilityUnknownRole },
- { blink::WebAXRoleApplication, NSAccessibilityGroupRole },
- { blink::WebAXRoleArticle, NSAccessibilityGroupRole },
- { blink::WebAXRoleBrowser, NSAccessibilityBrowserRole },
- { blink::WebAXRoleBusyIndicator, NSAccessibilityBusyIndicatorRole },
- { blink::WebAXRoleButton, NSAccessibilityButtonRole },
- { blink::WebAXRoleCanvas, NSAccessibilityImageRole },
- { blink::WebAXRoleCell, @"AXCell" },
- { blink::WebAXRoleCheckBox, NSAccessibilityCheckBoxRole },
- { blink::WebAXRoleColorWell, NSAccessibilityColorWellRole },
- { blink::WebAXRoleComboBox, NSAccessibilityComboBoxRole },
- { blink::WebAXRoleColumn, NSAccessibilityColumnRole },
- { blink::WebAXRoleColumnHeader, @"AXCell" },
- { blink::WebAXRoleDefinition, NSAccessibilityGroupRole },
- { blink::WebAXRoleDescriptionListDetail, NSAccessibilityGroupRole },
- { blink::WebAXRoleDescriptionListTerm, NSAccessibilityGroupRole },
- { blink::WebAXRoleDialog, NSAccessibilityGroupRole },
- { blink::WebAXRoleDirectory, NSAccessibilityListRole },
- { blink::WebAXRoleDisclosureTriangle,
- NSAccessibilityDisclosureTriangleRole },
- { blink::WebAXRoleDiv, NSAccessibilityGroupRole },
- { blink::WebAXRoleDocument, NSAccessibilityGroupRole },
- { blink::WebAXRoleDrawer, NSAccessibilityDrawerRole },
- { blink::WebAXRoleEditableText, NSAccessibilityTextFieldRole },
- { blink::WebAXRoleFooter, NSAccessibilityGroupRole },
- { blink::WebAXRoleForm, NSAccessibilityGroupRole },
- { blink::WebAXRoleGrid, NSAccessibilityGridRole },
- { blink::WebAXRoleGroup, NSAccessibilityGroupRole },
- { blink::WebAXRoleGrowArea, NSAccessibilityGrowAreaRole },
- { blink::WebAXRoleHeading, @"AXHeading" },
- { blink::WebAXRoleHelpTag, NSAccessibilityHelpTagRole },
- { blink::WebAXRoleHorizontalRule, NSAccessibilityGroupRole },
- { blink::WebAXRoleIgnored, NSAccessibilityUnknownRole },
- { blink::WebAXRoleImage, NSAccessibilityImageRole },
- { blink::WebAXRoleImageMap, NSAccessibilityGroupRole },
- { blink::WebAXRoleImageMapLink, NSAccessibilityLinkRole },
- { blink::WebAXRoleIncrementor, NSAccessibilityIncrementorRole },
- { blink::WebAXRoleLabel, NSAccessibilityGroupRole },
- { blink::WebAXRoleApplication, NSAccessibilityGroupRole },
- { blink::WebAXRoleBanner, NSAccessibilityGroupRole },
- { blink::WebAXRoleComplementary, NSAccessibilityGroupRole },
- { blink::WebAXRoleContentInfo, NSAccessibilityGroupRole },
- { blink::WebAXRoleMain, NSAccessibilityGroupRole },
- { blink::WebAXRoleNavigation, NSAccessibilityGroupRole },
- { blink::WebAXRoleSearch, NSAccessibilityGroupRole },
- { blink::WebAXRoleLink, NSAccessibilityLinkRole },
- { blink::WebAXRoleList, NSAccessibilityListRole },
- { blink::WebAXRoleListItem, NSAccessibilityGroupRole },
- { blink::WebAXRoleListMarker, @"AXListMarker" },
- { blink::WebAXRoleListBox, NSAccessibilityListRole },
- { blink::WebAXRoleListBoxOption, NSAccessibilityStaticTextRole },
- { blink::WebAXRoleLog, NSAccessibilityGroupRole },
- { blink::WebAXRoleMarquee, NSAccessibilityGroupRole },
- { blink::WebAXRoleMath, NSAccessibilityGroupRole },
- { blink::WebAXRoleMatte, NSAccessibilityMatteRole },
- { blink::WebAXRoleMenu, NSAccessibilityMenuRole },
- { blink::WebAXRoleMenuBar, NSAccessibilityMenuBarRole },
- { blink::WebAXRoleMenuItem, NSAccessibilityMenuItemRole },
- { blink::WebAXRoleMenuButton, NSAccessibilityButtonRole },
- { blink::WebAXRoleMenuListOption, NSAccessibilityMenuItemRole },
- { blink::WebAXRoleMenuListPopup, NSAccessibilityUnknownRole },
- { blink::WebAXRoleNote, NSAccessibilityGroupRole },
- { blink::WebAXRoleOutline, NSAccessibilityOutlineRole },
- { blink::WebAXRoleParagraph, NSAccessibilityGroupRole },
- { blink::WebAXRolePopUpButton, NSAccessibilityPopUpButtonRole },
- { blink::WebAXRolePresentational, NSAccessibilityGroupRole },
- { blink::WebAXRoleProgressIndicator,
- NSAccessibilityProgressIndicatorRole },
- { blink::WebAXRoleRadioButton, NSAccessibilityRadioButtonRole },
- { blink::WebAXRoleRadioGroup, NSAccessibilityRadioGroupRole },
- { blink::WebAXRoleRegion, NSAccessibilityGroupRole },
- { blink::WebAXRoleRootWebArea, @"AXWebArea" },
- { blink::WebAXRoleRow, NSAccessibilityRowRole },
- { blink::WebAXRoleRowHeader, @"AXCell" },
- { blink::WebAXRoleRuler, NSAccessibilityRulerRole },
- { blink::WebAXRoleRulerMarker, NSAccessibilityRulerMarkerRole },
+ { ui::AX_ROLE_ALERT, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_ALERT_DIALOG, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_ANNOTATION, NSAccessibilityUnknownRole },
+ { ui::AX_ROLE_APPLICATION, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_ARTICLE, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_BANNER, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_BROWSER, NSAccessibilityBrowserRole },
+ { ui::AX_ROLE_BUSY_INDICATOR, NSAccessibilityBusyIndicatorRole },
+ { ui::AX_ROLE_BUTTON, NSAccessibilityButtonRole },
+ { ui::AX_ROLE_CANVAS, NSAccessibilityImageRole },
+ { ui::AX_ROLE_CELL, @"AXCell" },
+ { ui::AX_ROLE_CHECK_BOX, NSAccessibilityCheckBoxRole },
+ { ui::AX_ROLE_COLOR_WELL, NSAccessibilityColorWellRole },
+ { ui::AX_ROLE_COLUMN, NSAccessibilityColumnRole },
+ { ui::AX_ROLE_COLUMN_HEADER, @"AXCell" },
+ { ui::AX_ROLE_COMBO_BOX, NSAccessibilityComboBoxRole },
+ { ui::AX_ROLE_COMPLEMENTARY, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_CONTENT_INFO, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_DEFINITION, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_DESCRIPTION_LIST_DETAIL, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_DESCRIPTION_LIST_TERM, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_DIALOG, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_DIRECTORY, NSAccessibilityListRole },
+ { ui::AX_ROLE_DISCLOSURE_TRIANGLE, NSAccessibilityDisclosureTriangleRole },
+ { ui::AX_ROLE_DIV, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_DOCUMENT, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_DRAWER, NSAccessibilityDrawerRole },
+ { ui::AX_ROLE_EDITABLE_TEXT, NSAccessibilityTextFieldRole },
+ { ui::AX_ROLE_FOOTER, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_FORM, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_GRID, NSAccessibilityGridRole },
+ { ui::AX_ROLE_GROUP, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_GROW_AREA, NSAccessibilityGrowAreaRole },
+ { ui::AX_ROLE_HEADING, @"AXHeading" },
+ { ui::AX_ROLE_HELP_TAG, NSAccessibilityHelpTagRole },
+ { ui::AX_ROLE_HORIZONTAL_RULE, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_IFRAME, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_IGNORED, NSAccessibilityUnknownRole },
+ { ui::AX_ROLE_IMAGE, NSAccessibilityImageRole },
+ { ui::AX_ROLE_IMAGE_MAP, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_IMAGE_MAP_LINK, NSAccessibilityLinkRole },
+ { ui::AX_ROLE_INCREMENTOR, NSAccessibilityIncrementorRole },
+ { ui::AX_ROLE_LABEL_TEXT, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_LINK, NSAccessibilityLinkRole },
+ { ui::AX_ROLE_LIST, NSAccessibilityListRole },
+ { ui::AX_ROLE_LIST_BOX, NSAccessibilityListRole },
+ { ui::AX_ROLE_LIST_BOX_OPTION, NSAccessibilityStaticTextRole },
+ { ui::AX_ROLE_LIST_ITEM, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_LIST_MARKER, @"AXListMarker" },
+ { ui::AX_ROLE_LOG, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_MAIN, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_MARQUEE, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_MATH, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_MATTE, NSAccessibilityMatteRole },
+ { ui::AX_ROLE_MENU, NSAccessibilityMenuRole },
+ { ui::AX_ROLE_MENU_BAR, NSAccessibilityMenuBarRole },
+ { ui::AX_ROLE_MENU_BUTTON, NSAccessibilityButtonRole },
+ { ui::AX_ROLE_MENU_ITEM, NSAccessibilityMenuItemRole },
+ { ui::AX_ROLE_MENU_LIST_OPTION, NSAccessibilityMenuItemRole },
+ { ui::AX_ROLE_MENU_LIST_POPUP, NSAccessibilityUnknownRole },
+ { ui::AX_ROLE_NAVIGATION, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_NOTE, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_OUTLINE, NSAccessibilityOutlineRole },
+ { ui::AX_ROLE_PARAGRAPH, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_POP_UP_BUTTON, NSAccessibilityPopUpButtonRole },
+ { ui::AX_ROLE_PRESENTATIONAL, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_PROGRESS_INDICATOR, NSAccessibilityProgressIndicatorRole },
+ { ui::AX_ROLE_RADIO_BUTTON, NSAccessibilityRadioButtonRole },
+ { ui::AX_ROLE_RADIO_GROUP, NSAccessibilityRadioGroupRole },
+ { ui::AX_ROLE_REGION, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_ROOT_WEB_AREA, @"AXWebArea" },
+ { ui::AX_ROLE_ROW, NSAccessibilityRowRole },
+ { ui::AX_ROLE_ROW_HEADER, @"AXCell" },
+ { ui::AX_ROLE_RULER, NSAccessibilityRulerRole },
+ { ui::AX_ROLE_RULER_MARKER, NSAccessibilityRulerMarkerRole },
+ { ui::AX_ROLE_SCROLL_BAR, NSAccessibilityScrollBarRole },
+ { ui::AX_ROLE_SEARCH, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_SHEET, NSAccessibilitySheetRole },
+ { ui::AX_ROLE_SLIDER, NSAccessibilitySliderRole },
+ { ui::AX_ROLE_SLIDER_THUMB, NSAccessibilityValueIndicatorRole },
+ { ui::AX_ROLE_SPIN_BUTTON, NSAccessibilitySliderRole },
+ { ui::AX_ROLE_SPLITTER, NSAccessibilitySplitterRole },
+ { ui::AX_ROLE_SPLIT_GROUP, NSAccessibilitySplitGroupRole },
+ { ui::AX_ROLE_STATIC_TEXT, NSAccessibilityStaticTextRole },
+ { ui::AX_ROLE_STATUS, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_SVG_ROOT, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_SYSTEM_WIDE, NSAccessibilityUnknownRole },
+ { ui::AX_ROLE_TAB, NSAccessibilityRadioButtonRole },
+ { ui::AX_ROLE_TABLE, NSAccessibilityTableRole },
+ { ui::AX_ROLE_TABLE_HEADER_CONTAINER, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_TAB_LIST, NSAccessibilityTabGroupRole },
+ { ui::AX_ROLE_TAB_PANEL, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_TEXT_AREA, NSAccessibilityTextAreaRole },
+ { ui::AX_ROLE_TEXT_FIELD, NSAccessibilityTextFieldRole },
+ { ui::AX_ROLE_TIMER, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_TOGGLE_BUTTON, NSAccessibilityCheckBoxRole },
+ { ui::AX_ROLE_TOOLBAR, NSAccessibilityToolbarRole },
+ { ui::AX_ROLE_TOOLTIP, NSAccessibilityGroupRole },
+ { ui::AX_ROLE_TREE, NSAccessibilityOutlineRole },
+ { ui::AX_ROLE_TREE_GRID, NSAccessibilityTableRole },
+ { ui::AX_ROLE_TREE_ITEM, NSAccessibilityRowRole },
+ { ui::AX_ROLE_VALUE_INDICATOR, NSAccessibilityValueIndicatorRole },
+ { ui::AX_ROLE_WEB_AREA, @"AXWebArea" },
+ { ui::AX_ROLE_WINDOW, NSAccessibilityWindowRole },
+
// TODO(dtseng): we don't correctly support the attributes for these roles.
- // { blink::WebAXRoleScrollArea, NSAccessibilityScrollAreaRole },
- { blink::WebAXRoleScrollBar, NSAccessibilityScrollBarRole },
- { blink::WebAXRoleSheet, NSAccessibilitySheetRole },
- { blink::WebAXRoleSlider, NSAccessibilitySliderRole },
- { blink::WebAXRoleSliderThumb, NSAccessibilityValueIndicatorRole },
- { blink::WebAXRoleSpinButton, NSAccessibilitySliderRole },
- { blink::WebAXRoleSplitter, NSAccessibilitySplitterRole },
- { blink::WebAXRoleSplitGroup, NSAccessibilitySplitGroupRole },
- { blink::WebAXRoleStaticText, NSAccessibilityStaticTextRole },
- { blink::WebAXRoleStatus, NSAccessibilityGroupRole },
- { blink::WebAXRoleSVGRoot, NSAccessibilityGroupRole },
- { blink::WebAXRoleSystemWide, NSAccessibilityUnknownRole },
- { blink::WebAXRoleTab, NSAccessibilityRadioButtonRole },
- { blink::WebAXRoleTabList, NSAccessibilityTabGroupRole },
- { blink::WebAXRoleTabPanel, NSAccessibilityGroupRole },
- { blink::WebAXRoleTable, NSAccessibilityTableRole },
- { blink::WebAXRoleTableHeaderContainer, NSAccessibilityGroupRole },
- { blink::WebAXRoleTextArea, NSAccessibilityTextAreaRole },
- { blink::WebAXRoleTextField, NSAccessibilityTextFieldRole },
- { blink::WebAXRoleTimer, NSAccessibilityGroupRole },
- { blink::WebAXRoleToggleButton, NSAccessibilityButtonRole },
- { blink::WebAXRoleToolbar, NSAccessibilityToolbarRole },
- { blink::WebAXRoleUserInterfaceTooltip, NSAccessibilityGroupRole },
- { blink::WebAXRoleTree, NSAccessibilityOutlineRole },
- { blink::WebAXRoleTreeGrid, NSAccessibilityTableRole },
- { blink::WebAXRoleTreeItem, NSAccessibilityRowRole },
- { blink::WebAXRoleValueIndicator, NSAccessibilityValueIndicatorRole },
- { blink::WebAXRoleLink, NSAccessibilityLinkRole },
- { blink::WebAXRoleWebArea, @"AXWebArea" },
- { blink::WebAXRoleWindow, NSAccessibilityWindowRole },
+ // { ui::AX_ROLE_SCROLL_AREA, NSAccessibilityScrollAreaRole },
};
RoleMap role_map;
@@ -172,8 +170,8 @@ RoleMap BuildRoleMap() {
}
// A mapping of webkit roles to native roles.
-NSString* NativeRoleFromAccessibilityNodeDataRole(
- const blink::WebAXRole& role) {
+NSString* NativeRoleFromAXRole(
+ const ui::AXRole& role) {
CR_DEFINE_STATIC_LOCAL(RoleMap, web_accessibility_to_native_role,
(BuildRoleMap()));
RoleMap::iterator it = web_accessibility_to_native_role.find(role);
@@ -185,32 +183,33 @@ NSString* NativeRoleFromAccessibilityNodeDataRole(
RoleMap BuildSubroleMap() {
const MapEntry subroles[] = {
- { blink::WebAXRoleAlert, @"AXApplicationAlert" },
- { blink::WebAXRoleAlertDialog, @"AXApplicationAlertDialog" },
- { blink::WebAXRoleArticle, @"AXDocumentArticle" },
- { blink::WebAXRoleDefinition, @"AXDefinition" },
- { blink::WebAXRoleDescriptionListDetail, @"AXDescription" },
- { blink::WebAXRoleDescriptionListTerm, @"AXTerm" },
- { blink::WebAXRoleDialog, @"AXApplicationDialog" },
- { blink::WebAXRoleDocument, @"AXDocument" },
- { blink::WebAXRoleFooter, @"AXLandmarkContentInfo" },
- { blink::WebAXRoleApplication, @"AXLandmarkApplication" },
- { blink::WebAXRoleBanner, @"AXLandmarkBanner" },
- { blink::WebAXRoleComplementary, @"AXLandmarkComplementary" },
- { blink::WebAXRoleContentInfo, @"AXLandmarkContentInfo" },
- { blink::WebAXRoleMain, @"AXLandmarkMain" },
- { blink::WebAXRoleNavigation, @"AXLandmarkNavigation" },
- { blink::WebAXRoleSearch, @"AXLandmarkSearch" },
- { blink::WebAXRoleLog, @"AXApplicationLog" },
- { blink::WebAXRoleMarquee, @"AXApplicationMarquee" },
- { blink::WebAXRoleMath, @"AXDocumentMath" },
- { blink::WebAXRoleNote, @"AXDocumentNote" },
- { blink::WebAXRoleRegion, @"AXDocumentRegion" },
- { blink::WebAXRoleStatus, @"AXApplicationStatus" },
- { blink::WebAXRoleTabPanel, @"AXTabPanel" },
- { blink::WebAXRoleTimer, @"AXApplicationTimer" },
- { blink::WebAXRoleUserInterfaceTooltip, @"AXUserInterfaceTooltip" },
- { blink::WebAXRoleTreeItem, NSAccessibilityOutlineRowSubrole },
+ { ui::AX_ROLE_ALERT, @"AXApplicationAlert" },
+ { ui::AX_ROLE_ALERT_DIALOG, @"AXApplicationAlertDialog" },
+ { ui::AX_ROLE_ARTICLE, @"AXDocumentArticle" },
+ { ui::AX_ROLE_DEFINITION, @"AXDefinition" },
+ { ui::AX_ROLE_DESCRIPTION_LIST_DETAIL, @"AXDescription" },
+ { ui::AX_ROLE_DESCRIPTION_LIST_TERM, @"AXTerm" },
+ { ui::AX_ROLE_DIALOG, @"AXApplicationDialog" },
+ { ui::AX_ROLE_DOCUMENT, @"AXDocument" },
+ { ui::AX_ROLE_FOOTER, @"AXLandmarkContentInfo" },
+ { ui::AX_ROLE_APPLICATION, @"AXLandmarkApplication" },
+ { ui::AX_ROLE_BANNER, @"AXLandmarkBanner" },
+ { ui::AX_ROLE_COMPLEMENTARY, @"AXLandmarkComplementary" },
+ { ui::AX_ROLE_CONTENT_INFO, @"AXLandmarkContentInfo" },
+ { ui::AX_ROLE_MAIN, @"AXLandmarkMain" },
+ { ui::AX_ROLE_NAVIGATION, @"AXLandmarkNavigation" },
+ { ui::AX_ROLE_SEARCH, @"AXLandmarkSearch" },
+ { ui::AX_ROLE_LOG, @"AXApplicationLog" },
+ { ui::AX_ROLE_MARQUEE, @"AXApplicationMarquee" },
+ { ui::AX_ROLE_MATH, @"AXDocumentMath" },
+ { ui::AX_ROLE_NOTE, @"AXDocumentNote" },
+ { ui::AX_ROLE_REGION, @"AXDocumentRegion" },
+ { ui::AX_ROLE_STATUS, @"AXApplicationStatus" },
+ { ui::AX_ROLE_TAB_PANEL, @"AXTabPanel" },
+ { ui::AX_ROLE_TIMER, @"AXApplicationTimer" },
+ { ui::AX_ROLE_TOGGLE_BUTTON, @"AXToggleButton" },
+ { ui::AX_ROLE_TOOLTIP, @"AXUserInterfaceTooltip" },
+ { ui::AX_ROLE_TREE_ITEM, NSAccessibilityOutlineRowSubrole },
};
RoleMap subrole_map;
@@ -220,8 +219,8 @@ RoleMap BuildSubroleMap() {
}
// A mapping of webkit roles to native subroles.
-NSString* NativeSubroleFromAccessibilityNodeDataRole(
- const blink::WebAXRole& role) {
+NSString* NativeSubroleFromAXRole(
+ const ui::AXRole& role) {
CR_DEFINE_STATIC_LOCAL(RoleMap, web_accessibility_to_native_subrole,
(BuildSubroleMap()));
RoleMap::iterator it = web_accessibility_to_native_subrole.find(role);
@@ -258,6 +257,7 @@ NSDictionary* attributeToMethodNameMap = nil;
{ NSAccessibilityHeaderAttribute, @"header" },
{ NSAccessibilityHelpAttribute, @"help" },
{ NSAccessibilityIndexAttribute, @"index" },
+ { NSAccessibilityLinkedUIElementsAttribute, @"linkedUIElements" },
{ NSAccessibilityMaxValueAttribute, @"maxValue" },
{ NSAccessibilityMinValueAttribute, @"minValue" },
{ NSAccessibilityNumberOfCharactersAttribute, @"numberOfCharacters" },
@@ -269,6 +269,7 @@ NSDictionary* attributeToMethodNameMap = nil;
{ NSAccessibilityRowHeaderUIElementsAttribute, @"rowHeaders" },
{ NSAccessibilityRowIndexRangeAttribute, @"rowIndexRange" },
{ NSAccessibilityRowsAttribute, @"rows" },
+ // TODO(aboxhall): expose NSAccessibilityServesAsTitleForUIElementsAttribute
{ NSAccessibilitySizeAttribute, @"size" },
{ NSAccessibilitySubroleAttribute, @"subrole" },
{ NSAccessibilityTabsAttribute, @"tabs" },
@@ -306,12 +307,9 @@ NSDictionary* attributeToMethodNameMap = nil;
dict = nil;
}
-- (id)initWithObject:(BrowserAccessibility*)accessibility
- delegate:(id<BrowserAccessibilityDelegateCocoa>)delegate {
- if ((self = [super init])) {
+- (id)initWithObject:(BrowserAccessibility*)accessibility {
+ if ((self = [super init]))
browserAccessibility_ = accessibility;
- delegate_ = delegate;
- }
return self;
}
@@ -324,29 +322,29 @@ NSDictionary* attributeToMethodNameMap = nil;
- (NSString*)accessKey {
return NSStringForStringAttribute(
- browserAccessibility_, AccessibilityNodeData::ATTR_ACCESS_KEY);
+ browserAccessibility_, ui::AX_ATTR_ACCESS_KEY);
}
- (NSNumber*)ariaAtomic {
bool boolValue = browserAccessibility_->GetBoolAttribute(
- AccessibilityNodeData::ATTR_LIVE_ATOMIC);
+ ui::AX_ATTR_LIVE_ATOMIC);
return [NSNumber numberWithBool:boolValue];
}
- (NSNumber*)ariaBusy {
bool boolValue = browserAccessibility_->GetBoolAttribute(
- AccessibilityNodeData::ATTR_LIVE_BUSY);
+ ui::AX_ATTR_LIVE_BUSY);
return [NSNumber numberWithBool:boolValue];
}
- (NSString*)ariaLive {
return NSStringForStringAttribute(
- browserAccessibility_, AccessibilityNodeData::ATTR_LIVE_STATUS);
+ browserAccessibility_, ui::AX_ATTR_LIVE_STATUS);
}
- (NSString*)ariaRelevant {
return NSStringForStringAttribute(
- browserAccessibility_, AccessibilityNodeData::ATTR_LIVE_RELEVANT);
+ browserAccessibility_, ui::AX_ATTR_LIVE_RELEVANT);
}
// Returns an array of BrowserAccessibilityCocoa objects, representing the
@@ -368,11 +366,11 @@ NSDictionary* attributeToMethodNameMap = nil;
// Also, add indirect children (if any).
const std::vector<int32>& indirectChildIds =
browserAccessibility_->GetIntListAttribute(
- AccessibilityNodeData::ATTR_INDIRECT_CHILD_IDS);
+ ui::AX_ATTR_INDIRECT_CHILD_IDS);
for (uint32 i = 0; i < indirectChildIds.size(); ++i) {
int32 child_id = indirectChildIds[i];
BrowserAccessibility* child =
- browserAccessibility_->manager()->GetFromRendererID(child_id);
+ browserAccessibility_->manager()->GetFromID(child_id);
// This only became necessary as a result of crbug.com/93095. It should be
// a DCHECK in the future.
@@ -390,41 +388,41 @@ NSDictionary* attributeToMethodNameMap = nil;
if (![self isIgnored]) {
children_.reset();
} else {
- [browserAccessibility_->parent()->ToBrowserAccessibilityCocoa()
+ [browserAccessibility_->GetParent()->ToBrowserAccessibilityCocoa()
childrenChanged];
}
}
- (NSArray*)columnHeaders {
- if ([self internalRole] != blink::WebAXRoleTable &&
- [self internalRole] != blink::WebAXRoleGrid) {
+ if ([self internalRole] != ui::AX_ROLE_TABLE &&
+ [self internalRole] != ui::AX_ROLE_GRID) {
return nil;
}
NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
const std::vector<int32>& uniqueCellIds =
browserAccessibility_->GetIntListAttribute(
- AccessibilityNodeData::ATTR_UNIQUE_CELL_IDS);
+ ui::AX_ATTR_UNIQUE_CELL_IDS);
for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
int id = uniqueCellIds[i];
BrowserAccessibility* cell =
- browserAccessibility_->manager()->GetFromRendererID(id);
- if (cell && cell->role() == blink::WebAXRoleColumnHeader)
+ browserAccessibility_->manager()->GetFromID(id);
+ if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER)
[ret addObject:cell->ToBrowserAccessibilityCocoa()];
}
return ret;
}
- (NSValue*)columnIndexRange {
- if ([self internalRole] != blink::WebAXRoleCell)
+ if ([self internalRole] != ui::AX_ROLE_CELL)
return nil;
int column = -1;
int colspan = -1;
browserAccessibility_->GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX, &column);
+ ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column);
browserAccessibility_->GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_SPAN, &colspan);
+ ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan);
if (column >= 0 && colspan >= 1)
return [NSValue valueWithRange:NSMakeRange(column, colspan)];
return nil;
@@ -442,7 +440,7 @@ NSDictionary* attributeToMethodNameMap = nil;
- (NSString*)description {
std::string description;
if (browserAccessibility_->GetStringAttribute(
- AccessibilityNodeData::ATTR_DESCRIPTION, &description)) {
+ ui::AX_ATTR_DESCRIPTION, &description)) {
return base::SysUTF8ToNSString(description);
}
@@ -451,7 +449,7 @@ NSDictionary* attributeToMethodNameMap = nil;
if (![[self role] isEqualToString:NSAccessibilityImageRole])
return @"";
if (browserAccessibility_->HasStringAttribute(
- AccessibilityNodeData::ATTR_NAME)) {
+ ui::AX_ATTR_NAME)) {
return @"";
}
if ([self titleUIElement])
@@ -461,7 +459,7 @@ NSDictionary* attributeToMethodNameMap = nil;
// Return the base part of the filename as the description.
std::string url;
if (browserAccessibility_->GetStringAttribute(
- AccessibilityNodeData::ATTR_URL, &url)) {
+ ui::AX_ATTR_URL, &url)) {
// Given a url like http://foo.com/bar/baz.png, just return the
// base name, e.g., "baz.png".
size_t leftIndex = url.rfind('/');
@@ -474,9 +472,9 @@ NSDictionary* attributeToMethodNameMap = nil;
}
- (NSNumber*)disclosing {
- if ([self internalRole] == blink::WebAXRoleTreeItem) {
+ if ([self internalRole] == ui::AX_ROLE_TREE_ITEM) {
return [NSNumber numberWithBool:
- GetState(browserAccessibility_, blink::WebAXStateExpanded)];
+ GetState(browserAccessibility_, ui::AX_STATE_EXPANDED)];
} else {
return nil;
}
@@ -489,11 +487,11 @@ NSDictionary* attributeToMethodNameMap = nil;
}
- (NSNumber*)disclosureLevel {
- blink::WebAXRole role = [self internalRole];
- if (role == blink::WebAXRoleRow ||
- role == blink::WebAXRoleTreeItem) {
+ ui::AXRole role = [self internalRole];
+ if (role == ui::AX_ROLE_ROW ||
+ role == ui::AX_ROLE_TREE_ITEM) {
int level = browserAccessibility_->GetIntAttribute(
- AccessibilityNodeData::ATTR_HIERARCHICAL_LEVEL);
+ ui::AX_ATTR_HIERARCHICAL_LEVEL);
// Mac disclosureLevel is 0-based, but web levels are 1-based.
if (level > 0)
level--;
@@ -510,7 +508,7 @@ NSDictionary* attributeToMethodNameMap = nil;
- (NSNumber*)enabled {
return [NSNumber numberWithBool:
- GetState(browserAccessibility_, blink::WebAXStateEnabled)];
+ GetState(browserAccessibility_, ui::AX_STATE_ENABLED)];
}
- (NSNumber*)focused {
@@ -522,21 +520,21 @@ NSDictionary* attributeToMethodNameMap = nil;
- (id)header {
int headerElementId = -1;
- if ([self internalRole] == blink::WebAXRoleTable ||
- [self internalRole] == blink::WebAXRoleGrid) {
+ if ([self internalRole] == ui::AX_ROLE_TABLE ||
+ [self internalRole] == ui::AX_ROLE_GRID) {
browserAccessibility_->GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_HEADER_ID, &headerElementId);
- } else if ([self internalRole] == blink::WebAXRoleColumn) {
+ ui::AX_ATTR_TABLE_HEADER_ID, &headerElementId);
+ } else if ([self internalRole] == ui::AX_ROLE_COLUMN) {
browserAccessibility_->GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_COLUMN_HEADER_ID, &headerElementId);
- } else if ([self internalRole] == blink::WebAXRoleRow) {
+ ui::AX_ATTR_TABLE_COLUMN_HEADER_ID, &headerElementId);
+ } else if ([self internalRole] == ui::AX_ROLE_ROW) {
browserAccessibility_->GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_ROW_HEADER_ID, &headerElementId);
+ ui::AX_ATTR_TABLE_ROW_HEADER_ID, &headerElementId);
}
if (headerElementId > 0) {
BrowserAccessibility* headerObject =
- browserAccessibility_->manager()->GetFromRendererID(headerElementId);
+ browserAccessibility_->manager()->GetFromID(headerElementId);
if (headerObject)
return headerObject->ToBrowserAccessibilityCocoa();
}
@@ -545,17 +543,17 @@ NSDictionary* attributeToMethodNameMap = nil;
- (NSString*)help {
return NSStringForStringAttribute(
- browserAccessibility_, AccessibilityNodeData::ATTR_HELP);
+ browserAccessibility_, ui::AX_ATTR_HELP);
}
- (NSNumber*)index {
- if ([self internalRole] == blink::WebAXRoleColumn) {
+ if ([self internalRole] == ui::AX_ROLE_COLUMN) {
int columnIndex = browserAccessibility_->GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_COLUMN_INDEX);
+ ui::AX_ATTR_TABLE_COLUMN_INDEX);
return [NSNumber numberWithInt:columnIndex];
- } else if ([self internalRole] == blink::WebAXRoleRow) {
+ } else if ([self internalRole] == ui::AX_ROLE_ROW) {
int rowIndex = browserAccessibility_->GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_ROW_INDEX);
+ ui::AX_ATTR_TABLE_ROW_INDEX);
return [NSNumber numberWithInt:rowIndex];
}
@@ -580,35 +578,57 @@ NSDictionary* attributeToMethodNameMap = nil;
return invalid;
}
+- (void)addLinkedUIElementsFromAttribute:(ui::AXIntListAttribute)attribute
+ addTo:(NSMutableArray*)outArray {
+ const std::vector<int32>& attributeValues =
+ browserAccessibility_->GetIntListAttribute(attribute);
+ for (size_t i = 0; i < attributeValues.size(); ++i) {
+ BrowserAccessibility* element =
+ browserAccessibility_->manager()->GetFromID(attributeValues[i]);
+ if (element)
+ [outArray addObject:element->ToBrowserAccessibilityCocoa()];
+ }
+}
+
+- (NSArray*)linkedUIElements {
+ NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
+ [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_OWNS_IDS addTo:ret];
+ [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_CONTROLS_IDS addTo:ret];
+ [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_FLOWTO_IDS addTo:ret];
+ if ([ret count] == 0)
+ return nil;
+ return ret;
+}
+
- (NSNumber*)loaded {
return [NSNumber numberWithBool:YES];
}
- (NSNumber*)loadingProgress {
float floatValue = browserAccessibility_->GetFloatAttribute(
- AccessibilityNodeData::ATTR_DOC_LOADING_PROGRESS);
+ ui::AX_ATTR_DOC_LOADING_PROGRESS);
return [NSNumber numberWithFloat:floatValue];
}
- (NSNumber*)maxValue {
float floatValue = browserAccessibility_->GetFloatAttribute(
- AccessibilityNodeData::ATTR_MAX_VALUE_FOR_RANGE);
+ ui::AX_ATTR_MAX_VALUE_FOR_RANGE);
return [NSNumber numberWithFloat:floatValue];
}
- (NSNumber*)minValue {
float floatValue = browserAccessibility_->GetFloatAttribute(
- AccessibilityNodeData::ATTR_MIN_VALUE_FOR_RANGE);
+ ui::AX_ATTR_MIN_VALUE_FOR_RANGE);
return [NSNumber numberWithFloat:floatValue];
}
- (NSString*)orientation {
// We present a spin button as a vertical slider, with a role description
// of "spin button".
- if ([self internalRole] == blink::WebAXRoleSpinButton)
+ if ([self internalRole] == ui::AX_ROLE_SPIN_BUTTON)
return NSAccessibilityVerticalOrientationValue;
- if (GetState(browserAccessibility_, blink::WebAXStateVertical))
+ if (GetState(browserAccessibility_, ui::AX_STATE_VERTICAL))
return NSAccessibilityVerticalOrientationValue;
else
return NSAccessibilityHorizontalOrientationValue;
@@ -628,9 +648,9 @@ NSDictionary* attributeToMethodNameMap = nil;
- (id)parent {
// A nil parent means we're the root.
- if (browserAccessibility_->parent()) {
+ if (browserAccessibility_->GetParent()) {
return NSAccessibilityUnignoredAncestor(
- browserAccessibility_->parent()->ToBrowserAccessibilityCocoa());
+ browserAccessibility_->GetParent()->ToBrowserAccessibilityCocoa());
} else {
// Hook back up to RenderWidgetHostViewCocoa.
BrowserAccessibilityManagerMac* manager =
@@ -643,30 +663,56 @@ NSDictionary* attributeToMethodNameMap = nil;
- (NSValue*)position {
NSPoint origin = [self origin];
NSSize size = [[self size] sizeValue];
- NSPoint pointInScreen =
- [delegate_ accessibilityPointInScreen:origin size:size];
+ NSPoint pointInScreen = [self pointInScreen:origin size:size];
return [NSValue valueWithPoint:pointInScreen];
}
- (NSNumber*)required {
return [NSNumber numberWithBool:
- GetState(browserAccessibility_, blink::WebAXStateRequired)];
+ GetState(browserAccessibility_, ui::AX_STATE_REQUIRED)];
}
// Returns an enum indicating the role from browserAccessibility_.
-- (blink::WebAXRole)internalRole {
- return static_cast<blink::WebAXRole>(browserAccessibility_->role());
+- (ui::AXRole)internalRole {
+ return static_cast<ui::AXRole>(browserAccessibility_->GetRole());
+}
+
+- (content::BrowserAccessibilityDelegate*)delegate {
+ return browserAccessibility_->manager() ?
+ browserAccessibility_->manager()->delegate() :
+ nil;
+}
+
+- (NSPoint)pointInScreen:(NSPoint)origin
+ size:(NSSize)size {
+ if (!browserAccessibility_)
+ return NSZeroPoint;
+
+ gfx::Rect bounds(origin.x, origin.y, size.width, size.height);
+ gfx::Point point = [self delegate]->AccessibilityOriginInScreen(bounds);
+ return NSMakePoint(point.x(), point.y());
}
// Returns a string indicating the NSAccessibility role of this object.
- (NSString*)role {
- blink::WebAXRole role = [self internalRole];
- if (role == blink::WebAXRoleCanvas &&
+ ui::AXRole role = [self internalRole];
+ if (role == ui::AX_ROLE_CANVAS &&
browserAccessibility_->GetBoolAttribute(
- AccessibilityNodeData::ATTR_CANVAS_HAS_FALLBACK)) {
+ ui::AX_ATTR_CANVAS_HAS_FALLBACK)) {
return NSAccessibilityGroupRole;
}
- return NativeRoleFromAccessibilityNodeDataRole(role);
+ if (role == ui::AX_ROLE_BUTTON || role == ui::AX_ROLE_TOGGLE_BUTTON) {
+ bool isAriaPressedDefined;
+ bool isMixed;
+ browserAccessibility_->GetAriaTristate("aria-pressed",
+ &isAriaPressedDefined,
+ &isMixed);
+ if (isAriaPressedDefined)
+ return NSAccessibilityCheckBoxRole;
+ else
+ return NSAccessibilityButtonRole;
+ }
+ return NativeRoleFromAXRole(role);
}
// Returns a string indicating the role description of this object.
@@ -695,10 +741,10 @@ NSDictionary* attributeToMethodNameMap = nil;
[role isEqualToString:NSAccessibilityRadioButtonRole]) {
std::string role;
if (browserAccessibility_->GetHtmlAttribute("role", &role)) {
- blink::WebAXRole internalRole = [self internalRole];
- if ((internalRole != blink::WebAXRoleGroup &&
- internalRole != blink::WebAXRoleListItem) ||
- internalRole == blink::WebAXRoleTab) {
+ ui::AXRole internalRole = [self internalRole];
+ if ((internalRole != ui::AX_ROLE_GROUP &&
+ internalRole != ui::AX_ROLE_LIST_ITEM) ||
+ internalRole == ui::AX_ROLE_TAB) {
// TODO(dtseng): This is not localized; see crbug/84814.
return base::SysUTF8ToNSString(role);
}
@@ -706,13 +752,16 @@ NSDictionary* attributeToMethodNameMap = nil;
}
switch([self internalRole]) {
- case blink::WebAXRoleFooter:
+ case ui::AX_ROLE_FOOTER:
return base::SysUTF16ToNSString(content_client->GetLocalizedString(
IDS_AX_ROLE_FOOTER));
- case blink::WebAXRoleSpinButton:
+ case ui::AX_ROLE_SPIN_BUTTON:
// This control is similar to what VoiceOver calls a "stepper".
return base::SysUTF16ToNSString(content_client->GetLocalizedString(
IDS_AX_ROLE_STEPPER));
+ case ui::AX_ROLE_TOGGLE_BUTTON:
+ return base::SysUTF16ToNSString(content_client->GetLocalizedString(
+ IDS_AX_ROLE_TOGGLE_BUTTON));
default:
break;
}
@@ -721,35 +770,35 @@ NSDictionary* attributeToMethodNameMap = nil;
}
- (NSArray*)rowHeaders {
- if ([self internalRole] != blink::WebAXRoleTable &&
- [self internalRole] != blink::WebAXRoleGrid) {
+ if ([self internalRole] != ui::AX_ROLE_TABLE &&
+ [self internalRole] != ui::AX_ROLE_GRID) {
return nil;
}
NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
const std::vector<int32>& uniqueCellIds =
browserAccessibility_->GetIntListAttribute(
- AccessibilityNodeData::ATTR_UNIQUE_CELL_IDS);
+ ui::AX_ATTR_UNIQUE_CELL_IDS);
for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
int id = uniqueCellIds[i];
BrowserAccessibility* cell =
- browserAccessibility_->manager()->GetFromRendererID(id);
- if (cell && cell->role() == blink::WebAXRoleRowHeader)
+ browserAccessibility_->manager()->GetFromID(id);
+ if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER)
[ret addObject:cell->ToBrowserAccessibilityCocoa()];
}
return ret;
}
- (NSValue*)rowIndexRange {
- if ([self internalRole] != blink::WebAXRoleCell)
+ if ([self internalRole] != ui::AX_ROLE_CELL)
return nil;
int row = -1;
int rowspan = -1;
browserAccessibility_->GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_CELL_ROW_INDEX, &row);
+ ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row);
browserAccessibility_->GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_CELL_ROW_SPAN, &rowspan);
+ ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan);
if (row >= 0 && rowspan >= 1)
return [NSValue valueWithRange:NSMakeRange(row, rowspan)];
return nil;
@@ -758,20 +807,20 @@ NSDictionary* attributeToMethodNameMap = nil;
- (NSArray*)rows {
NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
- if ([self internalRole] == blink::WebAXRoleTable||
- [self internalRole] == blink::WebAXRoleGrid) {
+ if ([self internalRole] == ui::AX_ROLE_TABLE||
+ [self internalRole] == ui::AX_ROLE_GRID) {
for (BrowserAccessibilityCocoa* child in [self children]) {
if ([[child role] isEqualToString:NSAccessibilityRowRole])
[ret addObject:child];
}
- } else if ([self internalRole] == blink::WebAXRoleColumn) {
+ } else if ([self internalRole] == ui::AX_ROLE_COLUMN) {
const std::vector<int32>& indirectChildIds =
browserAccessibility_->GetIntListAttribute(
- AccessibilityNodeData::ATTR_INDIRECT_CHILD_IDS);
+ ui::AX_ATTR_INDIRECT_CHILD_IDS);
for (uint32 i = 0; i < indirectChildIds.size(); ++i) {
int id = indirectChildIds[i];
BrowserAccessibility* rowElement =
- browserAccessibility_->manager()->GetFromRendererID(id);
+ browserAccessibility_->manager()->GetFromID(id);
if (rowElement)
[ret addObject:rowElement->ToBrowserAccessibilityCocoa()];
}
@@ -788,32 +837,31 @@ NSDictionary* attributeToMethodNameMap = nil;
// Returns a subrole based upon the role.
- (NSString*) subrole {
- blink::WebAXRole browserAccessibilityRole = [self internalRole];
- if (browserAccessibilityRole == blink::WebAXRoleTextField &&
- GetState(browserAccessibility_, blink::WebAXStateProtected)) {
+ ui::AXRole browserAccessibilityRole = [self internalRole];
+ if (browserAccessibilityRole == ui::AX_ROLE_TEXT_FIELD &&
+ GetState(browserAccessibility_, ui::AX_STATE_PROTECTED)) {
return @"AXSecureTextField";
}
NSString* htmlTag = NSStringForStringAttribute(
- browserAccessibility_, AccessibilityNodeData::ATTR_HTML_TAG);
+ browserAccessibility_, ui::AX_ATTR_HTML_TAG);
- if (browserAccessibilityRole == blink::WebAXRoleList) {
- if ([htmlTag isEqualToString:@"ul"] ||
- [htmlTag isEqualToString:@"ol"]) {
- return @"AXContentList";
- } else if ([htmlTag isEqualToString:@"dl"]) {
+ if (browserAccessibilityRole == ui::AX_ROLE_LIST) {
+ if ([htmlTag isEqualToString:@"dl"]) {
return @"AXDescriptionList";
+ } else {
+ return @"AXContentList";
}
}
- return NativeSubroleFromAccessibilityNodeDataRole(browserAccessibilityRole);
+ return NativeSubroleFromAXRole(browserAccessibilityRole);
}
// Returns all tabs in this subtree.
- (NSArray*)tabs {
NSMutableArray* tabSubtree = [[[NSMutableArray alloc] init] autorelease];
- if ([self internalRole] == blink::WebAXRoleTab)
+ if ([self internalRole] == ui::AX_ROLE_TAB)
[tabSubtree addObject:self];
for (uint i=0; i < [[self children] count]; ++i) {
@@ -827,27 +875,41 @@ NSDictionary* attributeToMethodNameMap = nil;
- (NSString*)title {
return NSStringForStringAttribute(
- browserAccessibility_, AccessibilityNodeData::ATTR_NAME);
+ browserAccessibility_, ui::AX_ATTR_NAME);
}
- (id)titleUIElement {
int titleElementId;
if (browserAccessibility_->GetIntAttribute(
- AccessibilityNodeData::ATTR_TITLE_UI_ELEMENT, &titleElementId)) {
+ ui::AX_ATTR_TITLE_UI_ELEMENT, &titleElementId)) {
+ BrowserAccessibility* titleElement =
+ browserAccessibility_->manager()->GetFromID(titleElementId);
+ if (titleElement)
+ return titleElement->ToBrowserAccessibilityCocoa();
+ }
+ std::vector<int32> labelledby_ids =
+ browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS);
+ if (labelledby_ids.size() == 1) {
BrowserAccessibility* titleElement =
- browserAccessibility_->manager()->GetFromRendererID(titleElementId);
+ browserAccessibility_->manager()->GetFromID(labelledby_ids[0]);
if (titleElement)
return titleElement->ToBrowserAccessibilityCocoa();
}
+
return nil;
}
-- (NSString*)url {
+- (NSURL*)url {
StringAttribute urlAttribute =
[[self role] isEqualToString:@"AXWebArea"] ?
- AccessibilityNodeData::ATTR_DOC_URL :
- AccessibilityNodeData::ATTR_URL;
- return NSStringForStringAttribute(browserAccessibility_, urlAttribute);
+ ui::AX_ATTR_DOC_URL :
+ ui::AX_ATTR_URL;
+
+ std::string urlStr = browserAccessibility_->GetStringAttribute(urlAttribute);
+ if (urlStr.empty())
+ return nil;
+
+ return [NSURL URLWithString:(base::SysUTF8ToNSString(urlStr))];
}
- (id)value {
@@ -858,24 +920,36 @@ NSDictionary* attributeToMethodNameMap = nil;
if ([role isEqualToString:@"AXHeading"]) {
int level = 0;
if (browserAccessibility_->GetIntAttribute(
- AccessibilityNodeData::ATTR_HIERARCHICAL_LEVEL, &level)) {
+ ui::AX_ATTR_HIERARCHICAL_LEVEL, &level)) {
return [NSNumber numberWithInt:level];
}
} else if ([role isEqualToString:NSAccessibilityButtonRole]) {
// AXValue does not make sense for pure buttons.
return @"";
+ } else if ([self internalRole] == ui::AX_ROLE_TOGGLE_BUTTON) {
+ int value = 0;
+ bool isAriaPressedDefined;
+ bool isMixed;
+ value = browserAccessibility_->GetAriaTristate(
+ "aria-pressed", &isAriaPressedDefined, &isMixed) ? 1 : 0;
+
+ if (isMixed)
+ value = 2;
+
+ return [NSNumber numberWithInt:value];
+
} else if ([role isEqualToString:NSAccessibilityCheckBoxRole] ||
[role isEqualToString:NSAccessibilityRadioButtonRole]) {
int value = 0;
value = GetState(
- browserAccessibility_, blink::WebAXStateChecked) ? 1 : 0;
+ browserAccessibility_, ui::AX_STATE_CHECKED) ? 1 : 0;
value = GetState(
- browserAccessibility_, blink::WebAXStateSelected) ?
+ browserAccessibility_, ui::AX_STATE_SELECTED) ?
1 :
value;
if (browserAccessibility_->GetBoolAttribute(
- AccessibilityNodeData::ATTR_BUTTON_MIXED)) {
+ ui::AX_ATTR_BUTTON_MIXED)) {
value = 2;
}
return [NSNumber numberWithInt:value];
@@ -884,28 +958,28 @@ NSDictionary* attributeToMethodNameMap = nil;
[role isEqualToString:NSAccessibilityScrollBarRole]) {
float floatValue;
if (browserAccessibility_->GetFloatAttribute(
- AccessibilityNodeData::ATTR_VALUE_FOR_RANGE, &floatValue)) {
+ ui::AX_ATTR_VALUE_FOR_RANGE, &floatValue)) {
return [NSNumber numberWithFloat:floatValue];
}
} else if ([role isEqualToString:NSAccessibilityColorWellRole]) {
int r = browserAccessibility_->GetIntAttribute(
- AccessibilityNodeData::ATTR_COLOR_VALUE_RED);
+ ui::AX_ATTR_COLOR_VALUE_RED);
int g = browserAccessibility_->GetIntAttribute(
- AccessibilityNodeData::ATTR_COLOR_VALUE_GREEN);
+ ui::AX_ATTR_COLOR_VALUE_GREEN);
int b = browserAccessibility_->GetIntAttribute(
- AccessibilityNodeData::ATTR_COLOR_VALUE_BLUE);
+ ui::AX_ATTR_COLOR_VALUE_BLUE);
// This string matches the one returned by a native Mac color well.
return [NSString stringWithFormat:@"rgb %7.5f %7.5f %7.5f 1",
r / 255., g / 255., b / 255.];
}
return NSStringForStringAttribute(
- browserAccessibility_, AccessibilityNodeData::ATTR_VALUE);
+ browserAccessibility_, ui::AX_ATTR_VALUE);
}
- (NSString*)valueDescription {
return NSStringForStringAttribute(
- browserAccessibility_, AccessibilityNodeData::ATTR_VALUE);
+ browserAccessibility_, ui::AX_ATTR_VALUE);
}
- (NSValue*)visibleCharacterRange {
@@ -917,11 +991,11 @@ NSDictionary* attributeToMethodNameMap = nil;
NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
const std::vector<int32>& uniqueCellIds =
browserAccessibility_->GetIntListAttribute(
- AccessibilityNodeData::ATTR_UNIQUE_CELL_IDS);
+ ui::AX_ATTR_UNIQUE_CELL_IDS);
for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
int id = uniqueCellIds[i];
BrowserAccessibility* cell =
- browserAccessibility_->manager()->GetFromRendererID(id);
+ browserAccessibility_->manager()->GetFromID(id);
if (cell)
[ret addObject:cell->ToBrowserAccessibilityCocoa()];
}
@@ -938,17 +1012,27 @@ NSDictionary* attributeToMethodNameMap = nil;
- (NSNumber*)visited {
return [NSNumber numberWithBool:
- GetState(browserAccessibility_, blink::WebAXStateVisited)];
+ GetState(browserAccessibility_, ui::AX_STATE_VISITED)];
}
- (id)window {
- return [delegate_ window];
+ if (!browserAccessibility_)
+ return nil;
+
+ BrowserAccessibilityManagerMac* manager =
+ static_cast<BrowserAccessibilityManagerMac*>(
+ browserAccessibility_->manager());
+ return [manager->parent_view() window];
}
- (NSString*)methodNameForAttribute:(NSString*)attribute {
return [attributeToMethodNameMap objectForKey:attribute];
}
+- (void)swapChildren:(base::scoped_nsobject<NSMutableArray>*)other {
+ children_.swap(*other);
+}
+
// Returns the accessibility value for the given attribute. If the value isn't
// supported this will return nil.
- (id)accessibilityAttributeValue:(NSString*)attribute {
@@ -963,9 +1047,9 @@ NSDictionary* attributeToMethodNameMap = nil;
// TODO(dtseng): refactor remaining attributes.
int selStart, selEnd;
if (browserAccessibility_->GetIntAttribute(
- AccessibilityNodeData::ATTR_TEXT_SEL_START, &selStart) &&
+ ui::AX_ATTR_TEXT_SEL_START, &selStart) &&
browserAccessibility_->
- GetIntAttribute(AccessibilityNodeData::ATTR_TEXT_SEL_END, &selEnd)) {
+ GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &selEnd)) {
if (selStart > selEnd)
std::swap(selStart, selEnd);
int selLength = selEnd - selStart;
@@ -973,7 +1057,7 @@ NSDictionary* attributeToMethodNameMap = nil;
NSAccessibilityInsertionPointLineNumberAttribute]) {
const std::vector<int32>& line_breaks =
browserAccessibility_->GetIntListAttribute(
- AccessibilityNodeData::ATTR_LINE_BREAKS);
+ ui::AX_ATTR_LINE_BREAKS);
for (int i = 0; i < static_cast<int>(line_breaks.size()); ++i) {
if (line_breaks[i] > selStart)
return [NSNumber numberWithInt:i];
@@ -982,7 +1066,7 @@ NSDictionary* attributeToMethodNameMap = nil;
}
if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) {
std::string value = browserAccessibility_->GetStringAttribute(
- AccessibilityNodeData::ATTR_VALUE);
+ ui::AX_ATTR_VALUE);
return base::SysUTF8ToNSString(value.substr(selStart, selLength));
}
if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
@@ -1001,14 +1085,14 @@ NSDictionary* attributeToMethodNameMap = nil;
const std::vector<int32>& line_breaks =
browserAccessibility_->GetIntListAttribute(
- AccessibilityNodeData::ATTR_LINE_BREAKS);
+ ui::AX_ATTR_LINE_BREAKS);
int len = static_cast<int>(browserAccessibility_->value().size());
if ([attribute isEqualToString:
NSAccessibilityStringForRangeParameterizedAttribute]) {
NSRange range = [(NSValue*)parameter rangeValue];
std::string value = browserAccessibility_->GetStringAttribute(
- AccessibilityNodeData::ATTR_VALUE);
+ ui::AX_ATTR_VALUE);
return base::SysUTF8ToNSString(value.substr(range.location, range.length));
}
@@ -1036,8 +1120,8 @@ NSDictionary* attributeToMethodNameMap = nil;
if ([attribute isEqualToString:
NSAccessibilityCellForColumnAndRowParameterizedAttribute]) {
- if ([self internalRole] != blink::WebAXRoleTable &&
- [self internalRole] != blink::WebAXRoleGrid) {
+ if ([self internalRole] != ui::AX_ROLE_TABLE &&
+ [self internalRole] != ui::AX_ROLE_GRID) {
return nil;
}
if (![parameter isKindOfClass:[NSArray self]])
@@ -1046,9 +1130,9 @@ NSDictionary* attributeToMethodNameMap = nil;
int column = [[array objectAtIndex:0] intValue];
int row = [[array objectAtIndex:1] intValue];
int num_columns = browserAccessibility_->GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT);
+ ui::AX_ATTR_TABLE_COLUMN_COUNT);
int num_rows = browserAccessibility_->GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_ROW_COUNT);
+ ui::AX_ATTR_TABLE_ROW_COUNT);
if (column < 0 || column >= num_columns ||
row < 0 || row >= num_rows) {
return nil;
@@ -1057,11 +1141,11 @@ NSDictionary* attributeToMethodNameMap = nil;
i < browserAccessibility_->PlatformChildCount();
++i) {
BrowserAccessibility* child = browserAccessibility_->PlatformGetChild(i);
- if (child->role() != blink::WebAXRoleRow)
+ if (child->GetRole() != ui::AX_ROLE_ROW)
continue;
int rowIndex;
if (!child->GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_ROW_INDEX, &rowIndex)) {
+ ui::AX_ATTR_TABLE_ROW_INDEX, &rowIndex)) {
continue;
}
if (rowIndex < row)
@@ -1072,11 +1156,11 @@ NSDictionary* attributeToMethodNameMap = nil;
j < child->PlatformChildCount();
++j) {
BrowserAccessibility* cell = child->PlatformGetChild(j);
- if (cell->role() != blink::WebAXRoleCell)
+ if (cell->GetRole() != ui::AX_ROLE_CELL)
continue;
int colIndex;
if (!cell->GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX,
+ ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX,
&colIndex)) {
continue;
}
@@ -1091,15 +1175,14 @@ NSDictionary* attributeToMethodNameMap = nil;
if ([attribute isEqualToString:
NSAccessibilityBoundsForRangeParameterizedAttribute]) {
- if ([self internalRole] != blink::WebAXRoleStaticText)
+ if ([self internalRole] != ui::AX_ROLE_STATIC_TEXT)
return nil;
NSRange range = [(NSValue*)parameter rangeValue];
gfx::Rect rect = browserAccessibility_->GetGlobalBoundsForRange(
range.location, range.length);
NSPoint origin = NSMakePoint(rect.x(), rect.y());
NSSize size = NSMakeSize(rect.width(), rect.height());
- NSPoint pointInScreen =
- [delegate_ accessibilityPointInScreen:origin size:size];
+ NSPoint pointInScreen = [self pointInScreen:origin size:size];
NSRect nsrect = NSMakeRect(
pointInScreen.x, pointInScreen.y, rect.width(), rect.height());
return [NSValue valueWithRect:nsrect];
@@ -1144,7 +1227,7 @@ NSDictionary* attributeToMethodNameMap = nil;
NSAccessibilityStyleRangeForIndexParameterizedAttribute,
nil];
}
- if ([self internalRole] == blink::WebAXRoleStaticText) {
+ if ([self internalRole] == ui::AX_ROLE_STATIC_TEXT) {
return [NSArray arrayWithObjects:
NSAccessibilityBoundsForRangeParameterizedAttribute,
nil];
@@ -1218,6 +1301,7 @@ NSDictionary* attributeToMethodNameMap = nil;
NSAccessibilityEnabledAttribute,
NSAccessibilityFocusedAttribute,
NSAccessibilityHelpAttribute,
+ NSAccessibilityLinkedUIElementsAttribute,
NSAccessibilityParentAttribute,
NSAccessibilityPositionAttribute,
NSAccessibilityRoleAttribute,
@@ -1228,7 +1312,6 @@ NSDictionary* attributeToMethodNameMap = nil;
NSAccessibilityTopLevelUIElementAttribute,
NSAccessibilityValueAttribute,
NSAccessibilityWindowAttribute,
- NSAccessibilityURLAttribute,
@"AXAccessKey",
@"AXInvalid",
@"AXRequired",
@@ -1295,11 +1378,11 @@ NSDictionary* attributeToMethodNameMap = nil;
NSAccessibilityDisclosedRowsAttribute,
nil]];
} else if ([role isEqualToString:NSAccessibilityRowRole]) {
- if (browserAccessibility_->parent()) {
+ if (browserAccessibility_->GetParent()) {
base::string16 parentRole;
- browserAccessibility_->parent()->GetHtmlAttribute(
+ browserAccessibility_->GetParent()->GetHtmlAttribute(
"role", &parentRole);
- const base::string16 treegridRole(ASCIIToUTF16("treegrid"));
+ const base::string16 treegridRole(base::ASCIIToUTF16("treegrid"));
if (parentRole == treegridRole) {
[ret addObjectsFromArray:[NSArray arrayWithObjects:
NSAccessibilityDisclosingAttribute,
@@ -1315,16 +1398,23 @@ NSDictionary* attributeToMethodNameMap = nil;
}
}
+ // Add the url attribute only if it has a valid url.
+ if ([self url] != nil) {
+ [ret addObjectsFromArray:[NSArray arrayWithObjects:
+ NSAccessibilityURLAttribute,
+ nil]];
+ }
+
// Live regions.
if (browserAccessibility_->HasStringAttribute(
- AccessibilityNodeData::ATTR_LIVE_STATUS)) {
+ ui::AX_ATTR_LIVE_STATUS)) {
[ret addObjectsFromArray:[NSArray arrayWithObjects:
@"AXARIALive",
@"AXARIARelevant",
nil]];
}
if (browserAccessibility_->HasStringAttribute(
- AccessibilityNodeData::ATTR_CONTAINER_LIVE_STATUS)) {
+ ui::AX_ATTR_CONTAINER_LIVE_STATUS)) {
[ret addObjectsFromArray:[NSArray arrayWithObjects:
@"AXARIAAtomic",
@"AXARIABusy",
@@ -1332,12 +1422,16 @@ NSDictionary* attributeToMethodNameMap = nil;
}
// Title UI Element.
- if (browserAccessibility_->HasIntAttribute(
- AccessibilityNodeData::ATTR_TITLE_UI_ELEMENT)) {
+ if (browserAccessibility_->HasIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT) ||
+ (browserAccessibility_->HasIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS) &&
+ browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS)
+ .size() == 1)) {
[ret addObjectsFromArray:[NSArray arrayWithObjects:
NSAccessibilityTitleUIElementAttribute,
nil]];
}
+ // TODO(aboxhall): expose NSAccessibilityServesAsTitleForUIElementsAttribute
+ // for elements which are referred to by labelledby or are labels
return ret;
}
@@ -1364,10 +1458,10 @@ NSDictionary* attributeToMethodNameMap = nil;
if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
return GetState(browserAccessibility_,
- blink::WebAXStateFocusable);
+ ui::AX_STATE_FOCUSABLE);
if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
return browserAccessibility_->GetBoolAttribute(
- AccessibilityNodeData::ATTR_CAN_SET_VALUE);
+ ui::AX_ATTR_CAN_SET_VALUE);
}
if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute] &&
([[self role] isEqualToString:NSAccessibilityTextFieldRole] ||
@@ -1392,11 +1486,13 @@ NSDictionary* attributeToMethodNameMap = nil;
if (!browserAccessibility_)
return;
- // TODO(feldstein): Support more actions.
- if ([action isEqualToString:NSAccessibilityPressAction])
- [delegate_ doDefaultAction:browserAccessibility_->renderer_id()];
- else if ([action isEqualToString:NSAccessibilityShowMenuAction])
- [delegate_ performShowMenuAction:self];
+ // TODO(dmazzoni): Support more actions.
+ if ([action isEqualToString:NSAccessibilityPressAction]) {
+ [self delegate]->AccessibilityDoDefaultAction(
+ browserAccessibility_->GetId());
+ } else if ([action isEqualToString:NSAccessibilityShowMenuAction]) {
+ [self delegate]->AccessibilityShowMenu(browserAccessibility_->GetId());
+ }
}
// Returns the description of the given action.
@@ -1422,15 +1518,14 @@ NSDictionary* attributeToMethodNameMap = nil;
if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
NSNumber* focusedNumber = value;
BOOL focused = [focusedNumber intValue];
- [delegate_ setAccessibilityFocus:focused
- accessibilityId:browserAccessibility_->renderer_id()];
+ if (focused)
+ [self delegate]->AccessibilitySetFocus(browserAccessibility_->GetId());
}
if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
NSRange range = [(NSValue*)value rangeValue];
- [delegate_
- accessibilitySetTextSelection:browserAccessibility_->renderer_id()
- startOffset:range.location
- endOffset:range.location + range.length];
+ [self delegate]->AccessibilitySetTextSelection(
+ browserAccessibility_->GetId(),
+ range.location, range.location + range.length);
}
}
@@ -1473,7 +1568,7 @@ NSDictionary* attributeToMethodNameMap = nil;
// Potentially called during dealloc.
if (!browserAccessibility_)
return [super hash];
- return browserAccessibility_->renderer_id();
+ return browserAccessibility_->GetId();
}
- (BOOL)accessibilityShouldUseUniqueId {
diff --git a/chromium/content/browser/accessibility/browser_accessibility_delegate_mac.h b/chromium/content/browser/accessibility/browser_accessibility_delegate_mac.h
deleted file mode 100644
index 054c255e7fc..00000000000
--- a/chromium/content/browser/accessibility/browser_accessibility_delegate_mac.h
+++ /dev/null
@@ -1,27 +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_ACCESSIBILITY_BROWSER_ACCESSIBILITY_DELEGATE_MAC_H_
-#define CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_DELEGATE_MAC_H_
-
-@class BrowserAccessibilityCocoa;
-@class NSWindow;
-
-// This protocol is used by the BrowserAccessibility objects to pass messages
-// to, or otherwise communicate with, their underlying WebAccessibility
-// objects over the IPC boundary.
-@protocol BrowserAccessibilityDelegateCocoa
-- (NSPoint)accessibilityPointInScreen:(NSPoint)origin
- size:(NSSize)size;
-- (void)doDefaultAction:(int32)accessibilityObjectId;
-- (void)accessibilitySetTextSelection:(int32)accId
- startOffset:(int32)startOffset
- endOffset:(int32)endOffset;
-- (void)performShowMenuAction:(BrowserAccessibilityCocoa*)accessibility;
-- (void)setAccessibilityFocus:(BOOL)focus
- accessibilityId:(int32)accessibilityObjectId;
-- (NSWindow*)window;
-@end
-
-#endif // CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_DELEGATE_MAC_H_
diff --git a/chromium/content/browser/accessibility/browser_accessibility_gtk.cc b/chromium/content/browser/accessibility/browser_accessibility_gtk.cc
deleted file mode 100644
index 06a164cb920..00000000000
--- a/chromium/content/browser/accessibility/browser_accessibility_gtk.cc
+++ /dev/null
@@ -1,518 +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/accessibility/browser_accessibility_gtk.h"
-
-#include <gtk/gtk.h>
-
-#include "base/strings/utf_string_conversions.h"
-#include "content/browser/accessibility/browser_accessibility_manager_gtk.h"
-#include "content/common/accessibility_messages.h"
-
-namespace content {
-
-static gpointer browser_accessibility_parent_class = NULL;
-
-static BrowserAccessibilityGtk* ToBrowserAccessibilityGtk(
- BrowserAccessibilityAtk* atk_object) {
- if (!atk_object)
- return NULL;
-
- return atk_object->m_object;
-}
-
-//
-// AtkComponent interface.
-//
-
-static BrowserAccessibilityGtk* ToBrowserAccessibilityGtk(
- AtkComponent* atk_object) {
- if (!IS_BROWSER_ACCESSIBILITY(atk_object))
- return NULL;
-
- return ToBrowserAccessibilityGtk(BROWSER_ACCESSIBILITY(atk_object));
-}
-
-static AtkObject* browser_accessibility_accessible_at_point(
- AtkComponent* component, gint x, gint y, AtkCoordType coord_type) {
- BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(component);
- if (!obj)
- return NULL;
-
- gfx::Point point(x, y);
- if (!obj->GetGlobalBoundsRect().Contains(point))
- return NULL;
-
- BrowserAccessibility* result = obj->BrowserAccessibilityForPoint(point);
- if (!result)
- return NULL;
-
- AtkObject* atk_result = result->ToBrowserAccessibilityGtk()->GetAtkObject();
- g_object_ref(atk_result);
- return atk_result;
-}
-
-static void browser_accessibility_get_extents(
- AtkComponent* component, gint* x, gint* y, gint* width, gint* height,
- AtkCoordType coord_type) {
- BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(component);
- if (!obj)
- return;
-
- gfx::Rect bounds = obj->GetGlobalBoundsRect();
- *x = bounds.x();
- *y = bounds.y();
- *width = bounds.width();
- *height = bounds.height();
-}
-
-static gboolean browser_accessibility_grab_focus(AtkComponent* component) {
- BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(component);
- if (!obj)
- return false;
-
- obj->manager()->SetFocus(obj, true);
- return true;
-}
-
-static void ComponentInterfaceInit(AtkComponentIface* iface) {
- iface->ref_accessible_at_point = browser_accessibility_accessible_at_point;
- iface->get_extents = browser_accessibility_get_extents;
- iface->grab_focus = browser_accessibility_grab_focus;
-}
-
-static const GInterfaceInfo ComponentInfo = {
- reinterpret_cast<GInterfaceInitFunc>(ComponentInterfaceInit), 0, 0
-};
-
-//
-// AtkValue interface.
-//
-
-static BrowserAccessibilityGtk* ToBrowserAccessibilityGtk(
- AtkValue* atk_object) {
- if (!IS_BROWSER_ACCESSIBILITY(atk_object))
- return NULL;
-
- return ToBrowserAccessibilityGtk(BROWSER_ACCESSIBILITY(atk_object));
-}
-
-static void browser_accessibility_get_current_value(
- AtkValue* atk_object, GValue* value) {
- BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
- if (!obj)
- return;
-
- float float_val;
- if (obj->GetFloatAttribute(AccessibilityNodeData::ATTR_VALUE_FOR_RANGE,
- &float_val)) {
- memset(value, 0, sizeof(*value));
- g_value_init(value, G_TYPE_FLOAT);
- g_value_set_float(value, float_val);
- }
-}
-
-static void browser_accessibility_get_minimum_value(
- AtkValue* atk_object, GValue* value) {
- BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
- if (!obj)
- return;
-
- float float_val;
- if (obj->GetFloatAttribute(AccessibilityNodeData::ATTR_MIN_VALUE_FOR_RANGE,
- &float_val)) {
- memset(value, 0, sizeof(*value));
- g_value_init(value, G_TYPE_FLOAT);
- g_value_set_float(value, float_val);
- }
-}
-
-static void browser_accessibility_get_maximum_value(
- AtkValue* atk_object, GValue* value) {
- BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
- if (!obj)
- return;
-
- float float_val;
- if (obj->GetFloatAttribute(AccessibilityNodeData::ATTR_MAX_VALUE_FOR_RANGE,
- &float_val)) {
- memset(value, 0, sizeof(*value));
- g_value_init(value, G_TYPE_FLOAT);
- g_value_set_float(value, float_val);
- }
-}
-
-static void browser_accessibility_get_minimum_increment(
- AtkValue* atk_object, GValue* value) {
- // TODO(dmazzoni): get the correct value from an <input type=range>.
- memset(value, 0, sizeof(*value));
- g_value_init(value, G_TYPE_FLOAT);
- g_value_set_float(value, 1.0);
-}
-
-static void ValueInterfaceInit(AtkValueIface* iface) {
- iface->get_current_value = browser_accessibility_get_current_value;
- iface->get_minimum_value = browser_accessibility_get_minimum_value;
- iface->get_maximum_value = browser_accessibility_get_maximum_value;
- iface->get_minimum_increment = browser_accessibility_get_minimum_increment;
-}
-
-static const GInterfaceInfo ValueInfo = {
- reinterpret_cast<GInterfaceInitFunc>(ValueInterfaceInit), 0, 0
-};
-
-//
-// AtkObject interface
-//
-
-static BrowserAccessibilityGtk* ToBrowserAccessibilityGtk(
- AtkObject* atk_object) {
- if (!IS_BROWSER_ACCESSIBILITY(atk_object))
- return NULL;
-
- return ToBrowserAccessibilityGtk(BROWSER_ACCESSIBILITY(atk_object));
-}
-
-static const gchar* browser_accessibility_get_name(AtkObject* atk_object) {
- BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
- if (!obj)
- return NULL;
-
- return obj->GetStringAttribute(AccessibilityNodeData::ATTR_NAME).c_str();
-}
-
-static const gchar* browser_accessibility_get_description(
- AtkObject* atk_object) {
- BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
- if (!obj)
- return NULL;
-
- return obj->GetStringAttribute(
- AccessibilityNodeData::ATTR_DESCRIPTION).c_str();
-}
-
-static AtkObject* browser_accessibility_get_parent(AtkObject* atk_object) {
- BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
- if (!obj)
- return NULL;
- if (obj->parent())
- return obj->parent()->ToBrowserAccessibilityGtk()->GetAtkObject();
-
- BrowserAccessibilityManagerGtk* manager =
- static_cast<BrowserAccessibilityManagerGtk*>(obj->manager());
- return gtk_widget_get_accessible(manager->parent_widget());
-}
-
-static gint browser_accessibility_get_n_children(AtkObject* atk_object) {
- BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
- if (!obj)
- return 0;
-
- return obj->PlatformChildCount();
-}
-
-static AtkObject* browser_accessibility_ref_child(
- AtkObject* atk_object, gint index) {
- BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
- if (!obj)
- return NULL;
-
- if (index < 0 || index >= static_cast<gint>(obj->PlatformChildCount()))
- return NULL;
-
- AtkObject* result =
- obj->children()[index]->ToBrowserAccessibilityGtk()->GetAtkObject();
- g_object_ref(result);
- return result;
-}
-
-static gint browser_accessibility_get_index_in_parent(AtkObject* atk_object) {
- BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
- if (!obj)
- return 0;
- return obj->index_in_parent();
-}
-
-static AtkAttributeSet* browser_accessibility_get_attributes(
- AtkObject* atk_object) {
- return NULL;
-}
-
-static AtkRole browser_accessibility_get_role(AtkObject* atk_object) {
- BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
- if (!obj)
- return ATK_ROLE_INVALID;
- return obj->atk_role();
-}
-
-static AtkStateSet* browser_accessibility_ref_state_set(AtkObject* atk_object) {
- BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
- if (!obj)
- return NULL;
- AtkStateSet* state_set =
- ATK_OBJECT_CLASS(browser_accessibility_parent_class)->
- ref_state_set(atk_object);
- int32 state = obj->state();
-
- if (state & (1 << blink::WebAXStateFocusable))
- atk_state_set_add_state(state_set, ATK_STATE_FOCUSABLE);
- if (obj->manager()->GetFocus(NULL) == obj)
- atk_state_set_add_state(state_set, ATK_STATE_FOCUSED);
- if (state & (1 << blink::WebAXStateEnabled))
- atk_state_set_add_state(state_set, ATK_STATE_ENABLED);
-
- return state_set;
-}
-
-static AtkRelationSet* browser_accessibility_ref_relation_set(
- AtkObject* atk_object) {
- AtkRelationSet* relation_set =
- ATK_OBJECT_CLASS(browser_accessibility_parent_class)
- ->ref_relation_set(atk_object);
- return relation_set;
-}
-
-//
-// The rest of the BrowserAccessibilityGtk code, not specific to one
-// of the Atk* interfaces.
-//
-
-static void browser_accessibility_init(AtkObject* atk_object, gpointer data) {
- if (ATK_OBJECT_CLASS(browser_accessibility_parent_class)->initialize) {
- ATK_OBJECT_CLASS(browser_accessibility_parent_class)->initialize(
- atk_object, data);
- }
-
- BROWSER_ACCESSIBILITY(atk_object)->m_object =
- reinterpret_cast<BrowserAccessibilityGtk*>(data);
-}
-
-static void browser_accessibility_finalize(GObject* atk_object) {
- G_OBJECT_CLASS(browser_accessibility_parent_class)->finalize(atk_object);
-}
-
-static void browser_accessibility_class_init(AtkObjectClass* klass) {
- GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
- browser_accessibility_parent_class = g_type_class_peek_parent(klass);
-
- gobject_class->finalize = browser_accessibility_finalize;
- klass->initialize = browser_accessibility_init;
- klass->get_name = browser_accessibility_get_name;
- klass->get_description = browser_accessibility_get_description;
- klass->get_parent = browser_accessibility_get_parent;
- klass->get_n_children = browser_accessibility_get_n_children;
- klass->ref_child = browser_accessibility_ref_child;
- klass->get_role = browser_accessibility_get_role;
- klass->ref_state_set = browser_accessibility_ref_state_set;
- klass->get_index_in_parent = browser_accessibility_get_index_in_parent;
- klass->get_attributes = browser_accessibility_get_attributes;
- klass->ref_relation_set = browser_accessibility_ref_relation_set;
-}
-
-GType browser_accessibility_get_type() {
- static volatile gsize type_volatile = 0;
-
- if (g_once_init_enter(&type_volatile)) {
- static const GTypeInfo tinfo = {
- sizeof(BrowserAccessibilityAtkClass),
- (GBaseInitFunc) 0,
- (GBaseFinalizeFunc) 0,
- (GClassInitFunc) browser_accessibility_class_init,
- (GClassFinalizeFunc) 0,
- 0, /* class data */
- sizeof(BrowserAccessibilityAtk), /* instance size */
- 0, /* nb preallocs */
- (GInstanceInitFunc) 0,
- 0 /* value table */
- };
-
- GType type = g_type_register_static(
- ATK_TYPE_OBJECT, "BrowserAccessibility", &tinfo, GTypeFlags(0));
- g_once_init_leave(&type_volatile, type);
- }
-
- return type_volatile;
-}
-
-static const char* GetUniqueAccessibilityTypeName(int interface_mask)
-{
- // 20 characters is enough for "Chrome%x" with any integer value.
- static char name[20];
- snprintf(name, sizeof(name), "Chrome%x", interface_mask);
- return name;
-}
-
-enum AtkInterfaces {
- ATK_ACTION_INTERFACE,
- ATK_COMPONENT_INTERFACE,
- ATK_DOCUMENT_INTERFACE,
- ATK_EDITABLE_TEXT_INTERFACE,
- ATK_HYPERLINK_INTERFACE,
- ATK_HYPERTEXT_INTERFACE,
- ATK_IMAGE_INTERFACE,
- ATK_SELECTION_INTERFACE,
- ATK_TABLE_INTERFACE,
- ATK_TEXT_INTERFACE,
- ATK_VALUE_INTERFACE,
-};
-
-static int GetInterfaceMaskFromObject(BrowserAccessibilityGtk* obj) {
- int interface_mask = 0;
-
- // Component interface is always supported.
- interface_mask |= 1 << ATK_COMPONENT_INTERFACE;
-
- int role = obj->role();
- if (role == blink::WebAXRoleProgressIndicator ||
- role == blink::WebAXRoleScrollBar ||
- role == blink::WebAXRoleSlider) {
- interface_mask |= 1 << ATK_VALUE_INTERFACE;
- }
-
- return interface_mask;
-}
-
-static GType GetAccessibilityTypeFromObject(BrowserAccessibilityGtk* obj) {
- static const GTypeInfo type_info = {
- sizeof(BrowserAccessibilityAtkClass),
- (GBaseInitFunc) 0,
- (GBaseFinalizeFunc) 0,
- (GClassInitFunc) 0,
- (GClassFinalizeFunc) 0,
- 0, /* class data */
- sizeof(BrowserAccessibilityAtk), /* instance size */
- 0, /* nb preallocs */
- (GInstanceInitFunc) 0,
- 0 /* value table */
- };
-
- int interface_mask = GetInterfaceMaskFromObject(obj);
- const char* atk_type_name = GetUniqueAccessibilityTypeName(interface_mask);
- GType type = g_type_from_name(atk_type_name);
- if (type)
- return type;
-
- type = g_type_register_static(BROWSER_ACCESSIBILITY_TYPE,
- atk_type_name,
- &type_info,
- GTypeFlags(0));
- if (interface_mask & (1 << ATK_COMPONENT_INTERFACE))
- g_type_add_interface_static(type, ATK_TYPE_COMPONENT, &ComponentInfo);
- if (interface_mask & (1 << ATK_VALUE_INTERFACE))
- g_type_add_interface_static(type, ATK_TYPE_VALUE, &ValueInfo);
-
- return type;
-}
-
-BrowserAccessibilityAtk* browser_accessibility_new(
- BrowserAccessibilityGtk* obj) {
- GType type = GetAccessibilityTypeFromObject(obj);
- AtkObject* atk_object = static_cast<AtkObject*>(g_object_new(type, 0));
-
- atk_object_initialize(atk_object, obj);
-
- return BROWSER_ACCESSIBILITY(atk_object);
-}
-
-void browser_accessibility_detach(BrowserAccessibilityAtk* atk_object) {
- atk_object->m_object = NULL;
-}
-
-// static
-BrowserAccessibility* BrowserAccessibility::Create() {
- return new BrowserAccessibilityGtk();
-}
-
-BrowserAccessibilityGtk* BrowserAccessibility::ToBrowserAccessibilityGtk() {
- return static_cast<BrowserAccessibilityGtk*>(this);
-}
-
-BrowserAccessibilityGtk::BrowserAccessibilityGtk()
- : atk_object_(NULL) {
-}
-
-BrowserAccessibilityGtk::~BrowserAccessibilityGtk() {
- browser_accessibility_detach(BROWSER_ACCESSIBILITY(atk_object_));
- if (atk_object_)
- g_object_unref(atk_object_);
-}
-
-AtkObject* BrowserAccessibilityGtk::GetAtkObject() const {
- if (!G_IS_OBJECT(atk_object_))
- return NULL;
- return atk_object_;
-}
-
-void BrowserAccessibilityGtk::PreInitialize() {
- BrowserAccessibility::PreInitialize();
- InitRoleAndState();
-
- if (atk_object_) {
- // If the object's role changes and that causes its
- // interface mask to change, we need to create a new
- // AtkObject for it.
- int interface_mask = GetInterfaceMaskFromObject(this);
- if (interface_mask != interface_mask_) {
- g_object_unref(atk_object_);
- atk_object_ = NULL;
- }
- }
-
- if (!atk_object_) {
- interface_mask_ = GetInterfaceMaskFromObject(this);
- atk_object_ = ATK_OBJECT(browser_accessibility_new(this));
- if (this->parent()) {
- atk_object_set_parent(
- atk_object_,
- this->parent()->ToBrowserAccessibilityGtk()->GetAtkObject());
- }
- }
-}
-
-bool BrowserAccessibilityGtk::IsNative() const {
- return true;
-}
-
-void BrowserAccessibilityGtk::InitRoleAndState() {
- switch(role()) {
- case blink::WebAXRoleDocument:
- case blink::WebAXRoleRootWebArea:
- case blink::WebAXRoleWebArea:
- atk_role_ = ATK_ROLE_DOCUMENT_WEB;
- break;
- case blink::WebAXRoleGroup:
- case blink::WebAXRoleDiv:
- atk_role_ = ATK_ROLE_SECTION;
- break;
- case blink::WebAXRoleButton:
- atk_role_ = ATK_ROLE_PUSH_BUTTON;
- break;
- case blink::WebAXRoleCheckBox:
- atk_role_ = ATK_ROLE_CHECK_BOX;
- break;
- case blink::WebAXRoleComboBox:
- atk_role_ = ATK_ROLE_COMBO_BOX;
- break;
- case blink::WebAXRoleLink:
- atk_role_ = ATK_ROLE_LINK;
- break;
- case blink::WebAXRoleRadioButton:
- atk_role_ = ATK_ROLE_RADIO_BUTTON;
- break;
- case blink::WebAXRoleStaticText:
- atk_role_ = ATK_ROLE_TEXT;
- break;
- case blink::WebAXRoleTextArea:
- atk_role_ = ATK_ROLE_ENTRY;
- break;
- case blink::WebAXRoleTextField:
- atk_role_ = ATK_ROLE_ENTRY;
- break;
- default:
- atk_role_ = ATK_ROLE_UNKNOWN;
- break;
- }
-}
-
-} // namespace content
diff --git a/chromium/content/browser/accessibility/browser_accessibility_gtk.h b/chromium/content/browser/accessibility/browser_accessibility_gtk.h
deleted file mode 100644
index c8895e358d1..00000000000
--- a/chromium/content/browser/accessibility/browser_accessibility_gtk.h
+++ /dev/null
@@ -1,92 +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_ACCESSIBILITY_BROWSER_ACCESSIBILITY_GTK_H_
-#define CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_GTK_H_
-
-#include <atk/atk.h>
-
-#include "base/compiler_specific.h"
-#include "content/browser/accessibility/browser_accessibility.h"
-
-namespace content {
-
-class BrowserAccessibilityGtk;
-class BrowserAccessibilityManagerGtk;
-
-G_BEGIN_DECLS
-
-#define BROWSER_ACCESSIBILITY_TYPE (browser_accessibility_get_type())
-#define BROWSER_ACCESSIBILITY(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST( \
- (obj), BROWSER_ACCESSIBILITY_TYPE, BrowserAccessibilityAtk))
-#define BROWSER_ACCESSIBILITY_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_CAST( \
- (klass), BROWSER_ACCESSIBILITY_TYPE, BrowserAccessibilityAtkClass))
-#define IS_BROWSER_ACCESSIBILITY(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE((obj), BROWSER_ACCESSIBILITY_TYPE))
-#define IS_BROWSER_ACCESSIBILITY_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_TYPE((klass), BROWSER_ACCESSIBILITY_TYPE))
-#define BROWSER_ACCESSIBILITY_GET_CLASS(obj) \
- (G_TYPE_INSTANCE_GET_CLASS( \
- (obj), BROWSER_ACCESSIBILITY_TYPE, BrowserAccessibilityAtkClass))
-
-typedef struct _BrowserAccessibilityAtk BrowserAccessibilityAtk;
-typedef struct _BrowserAccessibilityAtkClass BrowserAccessibilityAtkClass;
-
-struct _BrowserAccessibilityAtk {
- AtkObject parent;
- BrowserAccessibilityGtk* m_object;
-};
-
-struct _BrowserAccessibilityAtkClass {
- AtkObjectClass parent_class;
-};
-
-GType browser_accessibility_get_type (void) G_GNUC_CONST;
-
-BrowserAccessibilityAtk* browser_accessibility_new(
- BrowserAccessibilityGtk* object);
-
-BrowserAccessibilityGtk* browser_accessibility_get_object(
- BrowserAccessibilityAtk* atk_object);
-
-void browser_accessibility_detach (BrowserAccessibilityAtk* atk_object);
-
-AtkObject* browser_accessibility_get_focused_element(
- BrowserAccessibilityAtk* atk_object);
-
-G_END_DECLS
-
-class BrowserAccessibilityGtk : public BrowserAccessibility {
- public:
- BrowserAccessibilityGtk();
-
- virtual ~BrowserAccessibilityGtk();
-
- AtkObject* GetAtkObject() const;
-
- AtkRole atk_role() { return atk_role_; }
-
- // BrowserAccessibility methods.
- virtual void PreInitialize() OVERRIDE;
- virtual bool IsNative() const OVERRIDE;
-
- private:
- virtual void InitRoleAndState();
-
- // Give BrowserAccessibility::Create access to our constructor.
- friend class BrowserAccessibility;
-
- AtkObject* atk_object_;
- AtkRole atk_role_;
- int interface_mask_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(BrowserAccessibilityGtk);
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_GTK_H_
diff --git a/chromium/content/browser/accessibility/browser_accessibility_mac.h b/chromium/content/browser/accessibility/browser_accessibility_mac.h
index 63a9d007d3e..e1b5e384dc1 100644
--- a/chromium/content/browser/accessibility/browser_accessibility_mac.h
+++ b/chromium/content/browser/accessibility/browser_accessibility_mac.h
@@ -17,21 +17,21 @@ namespace content {
class BrowserAccessibilityMac : public BrowserAccessibility {
public:
- // Implementation of BrowserAccessibility.
- virtual void PreInitialize() OVERRIDE;
+ // BrowserAccessibility overrides.
virtual void NativeReleaseReference() OVERRIDE;
virtual bool IsNative() const OVERRIDE;
-
- // Overrides from BrowserAccessibility.
- virtual void DetachTree(std::vector<BrowserAccessibility*>* nodes) OVERRIDE;
- virtual void SwapChildren(std::vector<BrowserAccessibility*>& children)
- OVERRIDE;
+ virtual void OnDataChanged() OVERRIDE;
// The BrowserAccessibilityCocoa associated with us.
BrowserAccessibilityCocoa* native_view() const {
return browser_accessibility_cocoa_;
}
+ // Detach the BrowserAccessibilityCocoa object and then recreate it.
+ // This is only used to work around VoiceOver bugs by forcing VoiceOver
+ // to rebuild its internal state.
+ void RecreateNativeObject();
+
private:
// This gives BrowserAccessibility::Create access to the class constructor.
friend class BrowserAccessibility;
diff --git a/chromium/content/browser/accessibility/browser_accessibility_mac.mm b/chromium/content/browser/accessibility/browser_accessibility_mac.mm
index 11595a10e28..1d4c2f92a7f 100644
--- a/chromium/content/browser/accessibility/browser_accessibility_mac.mm
+++ b/chromium/content/browser/accessibility/browser_accessibility_mac.mm
@@ -7,7 +7,6 @@
#import "content/browser/accessibility/browser_accessibility_mac.h"
#import "content/browser/accessibility/browser_accessibility_cocoa.h"
-#import "content/browser/accessibility/browser_accessibility_delegate_mac.h"
#include "content/browser/accessibility/browser_accessibility_manager_mac.h"
namespace content {
@@ -21,19 +20,17 @@ BrowserAccessibilityMac::BrowserAccessibilityMac()
: browser_accessibility_cocoa_(NULL) {
}
-void BrowserAccessibilityMac::PreInitialize() {
- BrowserAccessibility::PreInitialize();
+void BrowserAccessibilityMac::OnDataChanged() {
+ BrowserAccessibility::OnDataChanged();
- if (browser_accessibility_cocoa_)
+ if (browser_accessibility_cocoa_) {
+ [browser_accessibility_cocoa_ childrenChanged];
return;
+ }
// We take ownership of the cocoa obj here.
- BrowserAccessibilityManagerMac* manager =
- static_cast<BrowserAccessibilityManagerMac*>(manager_);
browser_accessibility_cocoa_ = [[BrowserAccessibilityCocoa alloc]
- initWithObject:this
- delegate:
- (id<BrowserAccessibilityDelegateCocoa>)manager->parent_view()];
+ initWithObject:this];
}
void BrowserAccessibilityMac::NativeReleaseReference() {
@@ -51,16 +48,19 @@ bool BrowserAccessibilityMac::IsNative() const {
return true;
}
-void BrowserAccessibilityMac::DetachTree(
- std::vector<BrowserAccessibility*>* nodes) {
- [browser_accessibility_cocoa_ childrenChanged];
- BrowserAccessibility::DetachTree(nodes);
-}
+void BrowserAccessibilityMac::RecreateNativeObject() {
+ if (!browser_accessibility_cocoa_)
+ return;
-void BrowserAccessibilityMac::SwapChildren(
- std::vector<BrowserAccessibility*>& children) {
- [browser_accessibility_cocoa_ childrenChanged];
- BrowserAccessibility::SwapChildren(children);
+ // Preserve the children so that recreating the native object doesn't
+ // end up recreating the whole subtree.
+ base::scoped_nsobject<NSMutableArray> children;
+ [browser_accessibility_cocoa_ swapChildren:&children];
+ [browser_accessibility_cocoa_ detach];
+ [browser_accessibility_cocoa_ release];
+ browser_accessibility_cocoa_ = [[BrowserAccessibilityCocoa alloc]
+ initWithObject:this];
+ [browser_accessibility_cocoa_ swapChildren:&children];
}
BrowserAccessibilityCocoa* BrowserAccessibility::ToBrowserAccessibilityCocoa() {
diff --git a/chromium/content/browser/accessibility/browser_accessibility_mac_unittest.mm b/chromium/content/browser/accessibility/browser_accessibility_mac_unittest.mm
index 5ff0a3864f9..11388c8c027 100644
--- a/chromium/content/browser/accessibility/browser_accessibility_mac_unittest.mm
+++ b/chromium/content/browser/accessibility/browser_accessibility_mac_unittest.mm
@@ -12,46 +12,7 @@
#include "content/browser/accessibility/browser_accessibility_manager_mac.h"
#include "testing/gtest/include/gtest/gtest.h"
#import "testing/gtest_mac.h"
-#import "ui/base/test/ui_cocoa_test_helper.h"
-
-@interface MockAccessibilityDelegate :
- NSView<BrowserAccessibilityDelegateCocoa>
-
-- (NSPoint)accessibilityPointInScreen:(NSPoint)origin
- size:(NSSize)size;
-- (void)doDefaultAction:(int32)accessibilityObjectId;
-- (void)accessibilitySetTextSelection:(int32)accId
- startOffset:(int32)startOffset
- endOffset:(int32)endOffset;
-- (void)performShowMenuAction:(BrowserAccessibilityCocoa*)accessibility;
-- (void)setAccessibilityFocus:(BOOL)focus
- accessibilityId:(int32)accessibilityObjectId;
-- (NSWindow*)window;
-
-@end
-
-@implementation MockAccessibilityDelegate
-
-- (NSPoint)accessibilityPointInScreen:(NSPoint)origin
- size:(NSSize)size {
- return NSZeroPoint;
-}
-- (void)doDefaultAction:(int32)accessibilityObjectId {
-}
-- (void)accessibilitySetTextSelection:(int32)accId
- startOffset:(int32)startOffset
- endOffset:(int32)endOffset {
-}
-- (void)performShowMenuAction:(BrowserAccessibilityCocoa*)accessibility {
-}
-- (void)setAccessibilityFocus:(BOOL)focus
- accessibilityId:(int32)accessibilityObjectId {
-}
-- (NSWindow*)window {
- return nil;
-}
-
-@end
+#import "ui/gfx/test/ui_cocoa_test_helper.h"
namespace content {
@@ -64,38 +25,38 @@ class BrowserAccessibilityTest : public ui::CocoaTest {
protected:
void RebuildAccessibilityTree() {
- AccessibilityNodeData root;
+ ui::AXNodeData root;
root.id = 1000;
root.location.set_width(500);
root.location.set_height(100);
- root.role = blink::WebAXRoleRootWebArea;
- root.AddStringAttribute(AccessibilityNodeData::ATTR_HELP, "HelpText");
+ root.role = ui::AX_ROLE_ROOT_WEB_AREA;
+ root.AddStringAttribute(ui::AX_ATTR_HELP, "HelpText");
root.child_ids.push_back(1001);
root.child_ids.push_back(1002);
- AccessibilityNodeData child1;
+ ui::AXNodeData child1;
child1.id = 1001;
child1.SetName("Child1");
child1.location.set_width(250);
child1.location.set_height(100);
- child1.role = blink::WebAXRoleButton;
+ child1.role = ui::AX_ROLE_BUTTON;
- AccessibilityNodeData child2;
+ ui::AXNodeData child2;
child2.id = 1002;
child2.location.set_x(250);
child2.location.set_width(250);
child2.location.set_height(100);
- child2.role = blink::WebAXRoleHeading;
+ child2.role = ui::AX_ROLE_HEADING;
- delegate_.reset([[MockAccessibilityDelegate alloc] init]);
manager_.reset(
- new BrowserAccessibilityManagerMac(delegate_, root, NULL));
- manager_->UpdateNodesForTesting(child1, child2);
+ new BrowserAccessibilityManagerMac(
+ nil,
+ MakeAXTreeUpdate(root, child1, child2),
+ NULL));
accessibility_.reset([manager_->GetRoot()->ToBrowserAccessibilityCocoa()
retain]);
}
- base::scoped_nsobject<MockAccessibilityDelegate> delegate_;
base::scoped_nsobject<BrowserAccessibilityCocoa> accessibility_;
scoped_ptr<BrowserAccessibilityManager> manager_;
};
diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager.cc b/chromium/content/browser/accessibility/browser_accessibility_manager.cc
index 88338300c7c..6bdce2d907a 100644
--- a/chromium/content/browser/accessibility/browser_accessibility_manager.cc
+++ b/chromium/content/browser/accessibility/browser_accessibility_manager.cc
@@ -10,22 +10,55 @@
namespace content {
+ui::AXTreeUpdate MakeAXTreeUpdate(
+ const ui::AXNodeData& node1,
+ const ui::AXNodeData& node2 /* = ui::AXNodeData() */,
+ const ui::AXNodeData& node3 /* = ui::AXNodeData() */,
+ const ui::AXNodeData& node4 /* = ui::AXNodeData() */,
+ const ui::AXNodeData& node5 /* = ui::AXNodeData() */,
+ const ui::AXNodeData& node6 /* = ui::AXNodeData() */,
+ const ui::AXNodeData& node7 /* = ui::AXNodeData() */,
+ const ui::AXNodeData& node8 /* = ui::AXNodeData() */,
+ const ui::AXNodeData& node9 /* = ui::AXNodeData() */) {
+ CR_DEFINE_STATIC_LOCAL(ui::AXNodeData, empty_data, ());
+ int32 no_id = empty_data.id;
+
+ ui::AXTreeUpdate update;
+ update.nodes.push_back(node1);
+ if (node2.id != no_id)
+ update.nodes.push_back(node2);
+ if (node3.id != no_id)
+ update.nodes.push_back(node3);
+ if (node4.id != no_id)
+ update.nodes.push_back(node4);
+ if (node5.id != no_id)
+ update.nodes.push_back(node5);
+ if (node6.id != no_id)
+ update.nodes.push_back(node6);
+ if (node7.id != no_id)
+ update.nodes.push_back(node7);
+ if (node8.id != no_id)
+ update.nodes.push_back(node8);
+ if (node9.id != no_id)
+ update.nodes.push_back(node9);
+ return update;
+}
+
BrowserAccessibility* BrowserAccessibilityFactory::Create() {
return BrowserAccessibility::Create();
}
#if !defined(OS_MACOSX) && \
!defined(OS_WIN) && \
- !defined(TOOLKIT_GTK) && \
!defined(OS_ANDROID) \
-// We have subclassess of BrowserAccessibilityManager on Mac, Linux/GTK,
-// and Win. For any other platform, instantiate the base class.
+// We have subclassess of BrowserAccessibilityManager on Mac, and Win. For any
+// other platform, instantiate the base class.
// static
BrowserAccessibilityManager* BrowserAccessibilityManager::Create(
- const AccessibilityNodeData& src,
+ const ui::AXTreeUpdate& initial_tree,
BrowserAccessibilityDelegate* delegate,
BrowserAccessibilityFactory* factory) {
- return new BrowserAccessibilityManager(src, delegate, factory);
+ return new BrowserAccessibilityManager(initial_tree, delegate, factory);
}
#endif
@@ -34,96 +67,90 @@ BrowserAccessibilityManager::BrowserAccessibilityManager(
BrowserAccessibilityFactory* factory)
: delegate_(delegate),
factory_(factory),
- root_(NULL),
+ tree_(new ui::AXTree()),
focus_(NULL),
osk_state_(OSK_ALLOWED) {
+ tree_->SetDelegate(this);
}
BrowserAccessibilityManager::BrowserAccessibilityManager(
- const AccessibilityNodeData& src,
+ const ui::AXTreeUpdate& initial_tree,
BrowserAccessibilityDelegate* delegate,
BrowserAccessibilityFactory* factory)
: delegate_(delegate),
factory_(factory),
- root_(NULL),
+ tree_(new ui::AXTree()),
focus_(NULL),
osk_state_(OSK_ALLOWED) {
- Initialize(src);
+ tree_->SetDelegate(this);
+ Initialize(initial_tree);
}
BrowserAccessibilityManager::~BrowserAccessibilityManager() {
- if (root_)
- root_->Destroy();
+ tree_.reset(NULL);
}
-void BrowserAccessibilityManager::Initialize(const AccessibilityNodeData src) {
- std::vector<AccessibilityNodeData> nodes;
- nodes.push_back(src);
- if (!UpdateNodes(nodes))
- return;
+void BrowserAccessibilityManager::Initialize(
+ const ui::AXTreeUpdate& initial_tree) {
+ if (!tree_->Unserialize(initial_tree)) {
+ if (delegate_) {
+ LOG(ERROR) << tree_->error();
+ delegate_->AccessibilityFatalError();
+ } else {
+ LOG(FATAL) << tree_->error();
+ }
+ }
+
if (!focus_)
- SetFocus(root_, false);
+ SetFocus(tree_->GetRoot(), false);
}
// static
-AccessibilityNodeData BrowserAccessibilityManager::GetEmptyDocument() {
- AccessibilityNodeData empty_document;
+ui::AXTreeUpdate BrowserAccessibilityManager::GetEmptyDocument() {
+ ui::AXNodeData empty_document;
empty_document.id = 0;
- empty_document.role = blink::WebAXRoleRootWebArea;
- return empty_document;
+ empty_document.role = ui::AX_ROLE_ROOT_WEB_AREA;
+ ui::AXTreeUpdate update;
+ update.nodes.push_back(empty_document);
+ return update;
}
BrowserAccessibility* BrowserAccessibilityManager::GetRoot() {
- return root_;
+ return GetFromAXNode(tree_->GetRoot());
+}
+
+BrowserAccessibility* BrowserAccessibilityManager::GetFromAXNode(
+ ui::AXNode* node) {
+ return GetFromID(node->id());
}
-BrowserAccessibility* BrowserAccessibilityManager::GetFromRendererID(
- int32 renderer_id) {
+BrowserAccessibility* BrowserAccessibilityManager::GetFromID(int32 id) {
base::hash_map<int32, BrowserAccessibility*>::iterator iter =
- renderer_id_map_.find(renderer_id);
- if (iter != renderer_id_map_.end())
+ id_wrapper_map_.find(id);
+ if (iter != id_wrapper_map_.end())
return iter->second;
return NULL;
}
-void BrowserAccessibilityManager::GotFocus(bool touch_event_context) {
- if (!touch_event_context)
- osk_state_ = OSK_DISALLOWED_BECAUSE_TAB_JUST_APPEARED;
-
- if (!focus_)
- return;
-
- NotifyAccessibilityEvent(blink::WebAXEventFocus, focus_);
+void BrowserAccessibilityManager::OnWindowFocused() {
+ if (focus_)
+ NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetFromAXNode(focus_));
}
-void BrowserAccessibilityManager::WasHidden() {
- osk_state_ = OSK_DISALLOWED_BECAUSE_TAB_HIDDEN;
+void BrowserAccessibilityManager::OnWindowBlurred() {
+ if (focus_)
+ NotifyAccessibilityEvent(ui::AX_EVENT_BLUR, GetFromAXNode(focus_));
}
void BrowserAccessibilityManager::GotMouseDown() {
osk_state_ = OSK_ALLOWED_WITHIN_FOCUSED_OBJECT;
- NotifyAccessibilityEvent(blink::WebAXEventFocus, focus_);
-}
-
-bool BrowserAccessibilityManager::IsOSKAllowed(const gfx::Rect& bounds) {
- if (!delegate_ || !delegate_->HasFocus())
- return false;
-
- gfx::Point touch_point = delegate_->GetLastTouchEventLocation();
- return bounds.Contains(touch_point);
+ NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetFromAXNode(focus_));
}
bool BrowserAccessibilityManager::UseRootScrollOffsetsWhenComputingBounds() {
return true;
}
-void BrowserAccessibilityManager::RemoveNode(BrowserAccessibility* node) {
- if (node == focus_)
- SetFocus(root_, false);
- int renderer_id = node->renderer_id();
- renderer_id_map_.erase(renderer_id);
-}
-
void BrowserAccessibilityManager::OnAccessibilityEvents(
const std::vector<AccessibilityHostMsg_EventParams>& params) {
bool should_send_initial_focus = false;
@@ -131,22 +158,28 @@ void BrowserAccessibilityManager::OnAccessibilityEvents(
// Process all changes to the accessibility tree first.
for (uint32 index = 0; index < params.size(); index++) {
const AccessibilityHostMsg_EventParams& param = params[index];
- if (!UpdateNodes(param.nodes))
+ if (!tree_->Unserialize(param.update)) {
+ if (delegate_) {
+ LOG(ERROR) << tree_->error();
+ delegate_->AccessibilityFatalError();
+ } else {
+ CHECK(false) << tree_->error();
+ }
return;
+ }
- // Set initial focus when a page is loaded.
- blink::WebAXEvent event_type = param.event_type;
- if (event_type == blink::WebAXEventLoadComplete) {
- if (!focus_) {
- SetFocus(root_, false);
- should_send_initial_focus = true;
- }
+ // Set focus to the root if it's not anywhere else.
+ if (!focus_) {
+ SetFocus(tree_->GetRoot(), false);
+ should_send_initial_focus = true;
}
}
+ OnTreeUpdateFinished();
+
if (should_send_initial_focus &&
- (!delegate_ || delegate_->HasFocus())) {
- NotifyAccessibilityEvent(blink::WebAXEventFocus, focus_);
+ (!delegate_ || delegate_->AccessibilityViewHasFocus())) {
+ NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetFromAXNode(focus_));
}
// Now iterate over the events again and fire the events.
@@ -155,13 +188,13 @@ void BrowserAccessibilityManager::OnAccessibilityEvents(
// Find the node corresponding to the id that's the target of the
// event (which may not be the root of the update tree).
- BrowserAccessibility* node = GetFromRendererID(param.id);
+ ui::AXNode* node = tree_->GetFromId(param.id);
if (!node)
continue;
- blink::WebAXEvent event_type = param.event_type;
- if (event_type == blink::WebAXEventFocus ||
- event_type == blink::WebAXEventBlur) {
+ ui::AXEvent event_type = param.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 &&
@@ -170,54 +203,83 @@ void BrowserAccessibilityManager::OnAccessibilityEvents(
// Don't send a native focus event if the window itself doesn't
// have focus.
- if (delegate_ && !delegate_->HasFocus())
+ if (delegate_ && !delegate_->AccessibilityViewHasFocus())
continue;
}
// Send the event event to the operating system.
- NotifyAccessibilityEvent(event_type, node);
+ NotifyAccessibilityEvent(event_type, GetFromAXNode(node));
}
}
+void BrowserAccessibilityManager::OnLocationChanges(
+ const std::vector<AccessibilityHostMsg_LocationChangeParams>& params) {
+ for (size_t i = 0; i < params.size(); ++i) {
+ BrowserAccessibility* obj = GetFromID(params[i].id);
+ if (!obj)
+ continue;
+ ui::AXNode* node = obj->node();
+ node->SetLocation(params[i].new_location);
+ obj->OnLocationChanged();
+ }
+}
+
+BrowserAccessibility* BrowserAccessibilityManager::GetActiveDescendantFocus(
+ BrowserAccessibility* root) {
+ BrowserAccessibility* node = BrowserAccessibilityManager::GetFocus(root);
+ if (!node)
+ return NULL;
+
+ int active_descendant_id;
+ if (node->GetIntAttribute(ui::AX_ATTR_ACTIVEDESCENDANT_ID,
+ &active_descendant_id)) {
+ BrowserAccessibility* active_descendant =
+ node->manager()->GetFromID(active_descendant_id);
+ if (active_descendant)
+ return active_descendant;
+ }
+ return node;
+}
+
BrowserAccessibility* BrowserAccessibilityManager::GetFocus(
BrowserAccessibility* root) {
- if (focus_ && (!root || focus_->IsDescendantOf(root)))
- return focus_;
+ if (focus_ && (!root || focus_->IsDescendantOf(root->node())))
+ return GetFromAXNode(focus_);
return NULL;
}
-void BrowserAccessibilityManager::SetFocus(
- BrowserAccessibility* node, bool notify) {
+void BrowserAccessibilityManager::SetFocus(ui::AXNode* node, bool notify) {
if (focus_ != node)
focus_ = node;
if (notify && node && delegate_)
- delegate_->SetAccessibilityFocus(node->renderer_id());
+ delegate_->AccessibilitySetFocus(node->id());
}
-void BrowserAccessibilityManager::SetRoot(BrowserAccessibility* node) {
- root_ = node;
- NotifyRootChanged();
+void BrowserAccessibilityManager::SetFocus(
+ BrowserAccessibility* obj, bool notify) {
+ if (obj->node())
+ SetFocus(obj->node(), notify);
}
void BrowserAccessibilityManager::DoDefaultAction(
const BrowserAccessibility& node) {
if (delegate_)
- delegate_->AccessibilityDoDefaultAction(node.renderer_id());
+ delegate_->AccessibilityDoDefaultAction(node.GetId());
}
void BrowserAccessibilityManager::ScrollToMakeVisible(
const BrowserAccessibility& node, gfx::Rect subfocus) {
if (delegate_) {
- delegate_->AccessibilityScrollToMakeVisible(node.renderer_id(), subfocus);
+ delegate_->AccessibilityScrollToMakeVisible(node.GetId(), subfocus);
}
}
void BrowserAccessibilityManager::ScrollToPoint(
const BrowserAccessibility& node, gfx::Point point) {
if (delegate_) {
- delegate_->AccessibilityScrollToPoint(node.renderer_id(), point);
+ delegate_->AccessibilityScrollToPoint(node.GetId(), point);
}
}
@@ -225,209 +287,80 @@ void BrowserAccessibilityManager::SetTextSelection(
const BrowserAccessibility& node, int start_offset, int end_offset) {
if (delegate_) {
delegate_->AccessibilitySetTextSelection(
- node.renderer_id(), start_offset, end_offset);
+ node.GetId(), start_offset, end_offset);
}
}
gfx::Rect BrowserAccessibilityManager::GetViewBounds() {
if (delegate_)
- return delegate_->GetViewBounds();
+ return delegate_->AccessibilityGetViewBounds();
return gfx::Rect();
}
-void BrowserAccessibilityManager::UpdateNodesForTesting(
- const AccessibilityNodeData& node1,
- const AccessibilityNodeData& node2 /* = AccessibilityNodeData() */,
- const AccessibilityNodeData& node3 /* = AccessibilityNodeData() */,
- const AccessibilityNodeData& node4 /* = AccessibilityNodeData() */,
- const AccessibilityNodeData& node5 /* = AccessibilityNodeData() */,
- const AccessibilityNodeData& node6 /* = AccessibilityNodeData() */,
- const AccessibilityNodeData& node7 /* = AccessibilityNodeData() */) {
- std::vector<AccessibilityNodeData> nodes;
- nodes.push_back(node1);
- if (node2.id != AccessibilityNodeData().id)
- nodes.push_back(node2);
- if (node3.id != AccessibilityNodeData().id)
- nodes.push_back(node3);
- if (node4.id != AccessibilityNodeData().id)
- nodes.push_back(node4);
- if (node5.id != AccessibilityNodeData().id)
- nodes.push_back(node5);
- if (node6.id != AccessibilityNodeData().id)
- nodes.push_back(node6);
- if (node7.id != AccessibilityNodeData().id)
- nodes.push_back(node7);
- UpdateNodes(nodes);
-}
-
-bool BrowserAccessibilityManager::UpdateNodes(
- const std::vector<AccessibilityNodeData>& nodes) {
- bool success = true;
-
- // First, update all of the nodes in the tree.
- for (size_t i = 0; i < nodes.size() && success; i++) {
- if (!UpdateNode(nodes[i]))
- success = false;
- }
-
- // In a second pass, call PostInitialize on each one - this must
- // be called after all of each node's children are initialized too.
- for (size_t i = 0; i < nodes.size() && success; i++) {
- // Note: it's not a bug for nodes[i].id to not be found in the tree.
- // Consider this example:
- // Before:
- // A
- // B
- // C
- // D
- // E
- // F
- // After:
- // A
- // B
- // C
- // F
- // D
- // In this example, F is being reparented. The renderer scans the tree
- // in order. If can't update "C" to add "F" as a child, when "F" is still
- // a child of "E". So it first updates "E", to remove "F" as a child.
- // Later, it ends up deleting "E". So when we get here, "E" was updated as
- // part of this sequence but it no longer exists in the final tree, so
- // there's nothing to postinitialize.
- BrowserAccessibility* instance = GetFromRendererID(nodes[i].id);
- if (instance)
- instance->PostInitialize();
- }
-
- if (!success) {
- // A bad accessibility tree could lead to memory corruption.
- // Ask the delegate to crash the renderer, or if not available,
- // crash the browser.
- if (delegate_)
- delegate_->FatalAccessibilityTreeError();
- else
- CHECK(false);
+BrowserAccessibility* BrowserAccessibilityManager::NextInTreeOrder(
+ BrowserAccessibility* node) {
+ if (!node)
+ return NULL;
+
+ if (node->PlatformChildCount() > 0)
+ return node->PlatformGetChild(0);
+ while (node) {
+ if (node->GetParent() &&
+ node->GetIndexInParent() <
+ static_cast<int>(node->GetParent()->PlatformChildCount()) - 1) {
+ return node->GetParent()->PlatformGetChild(node->GetIndexInParent() + 1);
+ }
+ node = node->GetParent();
}
- return success;
-}
-
-BrowserAccessibility* BrowserAccessibilityManager::CreateNode(
- BrowserAccessibility* parent,
- int32 renderer_id,
- int32 index_in_parent) {
- BrowserAccessibility* node = factory_->Create();
- node->InitializeTreeStructure(
- this, parent, renderer_id, index_in_parent);
- AddNodeToMap(node);
- return node;
-}
-
-void BrowserAccessibilityManager::AddNodeToMap(BrowserAccessibility* node) {
- renderer_id_map_[node->renderer_id()] = node;
+ return NULL;
}
-bool BrowserAccessibilityManager::UpdateNode(const AccessibilityNodeData& src) {
- // This method updates one node in the tree based on serialized data
- // received from the renderer.
+BrowserAccessibility* BrowserAccessibilityManager::PreviousInTreeOrder(
+ BrowserAccessibility* node) {
+ if (!node)
+ return NULL;
- // Create a set of child ids in |src| for fast lookup. If a duplicate id is
- // found, exit now with a fatal error before changing anything else.
- std::set<int32> new_child_ids;
- for (size_t i = 0; i < src.child_ids.size(); ++i) {
- if (new_child_ids.find(src.child_ids[i]) != new_child_ids.end())
- return false;
- new_child_ids.insert(src.child_ids[i]);
+ if (node->GetParent() && node->GetIndexInParent() > 0) {
+ node = node->GetParent()->PlatformGetChild(node->GetIndexInParent() - 1);
+ while (node->PlatformChildCount() > 0)
+ node = node->PlatformGetChild(node->PlatformChildCount() - 1);
+ return node;
}
- // Look up the node by id. If it's not found, then either the root
- // of the tree is being swapped, or we're out of sync with the renderer
- // and this is a serious error.
- BrowserAccessibility* instance = GetFromRendererID(src.id);
- if (!instance) {
- if (src.role != blink::WebAXRoleRootWebArea)
- return false;
- instance = CreateNode(NULL, src.id, 0);
- }
+ return node->GetParent();
+}
- // TODO(dmazzoni): avoid a linear scan here.
- for (size_t i = 0; i < src.bool_attributes.size(); i++) {
- if (src.bool_attributes[i].first ==
- AccessibilityNodeData::ATTR_UPDATE_LOCATION_ONLY) {
- instance->SetLocation(src.location);
- return true;
- }
+void BrowserAccessibilityManager::OnNodeWillBeDeleted(ui::AXNode* node) {
+ if (node == focus_ && tree_) {
+ if (node != tree_->GetRoot())
+ SetFocus(tree_->GetRoot(), false);
+ else
+ focus_ = NULL;
}
+ if (id_wrapper_map_.find(node->id()) == id_wrapper_map_.end())
+ return;
+ GetFromAXNode(node)->Destroy();
+ id_wrapper_map_.erase(node->id());
+}
- // Update all of the node-specific data, like its role, state, name, etc.
- instance->InitializeData(src);
-
- //
- // Update the children in three steps:
- //
- // 1. Iterate over the old children and delete nodes that are no longer
- // in the tree.
- // 2. Build up a vector of new children, reusing children that haven't
- // changed (but may have been reordered) and adding new empty
- // objects for new children.
- // 3. Swap in the new children vector for the old one.
-
- // Delete any previous children of this instance that are no longer
- // children first. We make a deletion-only pass first to prevent a
- // node that's being reparented from being the child of both its old
- // parent and new parent, which could lead to a double-free.
- // If a node is reparented, the renderer will always send us a fresh
- // copy of the node.
- const std::vector<BrowserAccessibility*>& old_children = instance->children();
- for (size_t i = 0; i < old_children.size(); ++i) {
- int old_id = old_children[i]->renderer_id();
- if (new_child_ids.find(old_id) == new_child_ids.end())
- old_children[i]->Destroy();
- }
+void BrowserAccessibilityManager::OnNodeCreated(ui::AXNode* node) {
+ BrowserAccessibility* wrapper = factory_->Create();
+ wrapper->Init(this, node);
+ id_wrapper_map_[node->id()] = wrapper;
+ wrapper->OnDataChanged();
+}
- // Now build a vector of new children, reusing objects that were already
- // children of this node before.
- std::vector<BrowserAccessibility*> new_children;
- bool success = true;
- for (size_t i = 0; i < src.child_ids.size(); i++) {
- int32 child_renderer_id = src.child_ids[i];
- int32 index_in_parent = static_cast<int32>(i);
- BrowserAccessibility* child = GetFromRendererID(child_renderer_id);
- if (child) {
- if (child->parent() != instance) {
- // This is a serious error - nodes should never be reparented.
- // If this case occurs, continue so this node isn't left in an
- // inconsistent state, but return failure at the end.
- success = false;
- continue;
- }
- child->UpdateParent(instance, index_in_parent);
- } else {
- child = CreateNode(instance, child_renderer_id, index_in_parent);
- }
- new_children.push_back(child);
- }
+void BrowserAccessibilityManager::OnNodeChanged(ui::AXNode* node) {
+ GetFromAXNode(node)->OnDataChanged();
+}
- // Finally, swap in the new children vector for the old.
- instance->SwapChildren(new_children);
-
- // Handle the case where this node is the new root of the tree.
- if (src.role == blink::WebAXRoleRootWebArea &&
- (!root_ || root_->renderer_id() != src.id)) {
- if (root_)
- root_->Destroy();
- if (focus_ == root_)
- SetFocus(instance, false);
- SetRoot(instance);
- }
+void BrowserAccessibilityManager::OnNodeCreationFinished(ui::AXNode* node) {
+ GetFromAXNode(node)->OnUpdateFinished();
+}
- // Keep track of what node is focused.
- if (src.role != blink::WebAXRoleRootWebArea &&
- src.role != blink::WebAXRoleWebArea &&
- (src.state >> blink::WebAXStateFocused & 1)) {
- SetFocus(instance, false);
- }
- return success;
+void BrowserAccessibilityManager::OnNodeChangeFinished(ui::AXNode* node) {
+ GetFromAXNode(node)->OnUpdateFinished();
}
} // namespace content
diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager.h b/chromium/content/browser/accessibility/browser_accessibility_manager.h
index 716ee405ff5..ae0bc30b31f 100644
--- a/chromium/content/browser/accessibility/browser_accessibility_manager.h
+++ b/chromium/content/browser/accessibility/browser_accessibility_manager.h
@@ -10,12 +10,15 @@
#include "base/containers/hash_tables.h"
#include "base/memory/scoped_ptr.h"
#include "build/build_config.h"
-#include "content/common/accessibility_node_data.h"
#include "content/common/content_export.h"
#include "third_party/WebKit/public/web/WebAXEnums.h"
+#include "ui/accessibility/ax_node_data.h"
+#include "ui/accessibility/ax_tree.h"
+#include "ui/accessibility/ax_tree_update.h"
#include "ui/gfx/native_widget_types.h"
struct AccessibilityHostMsg_EventParams;
+struct AccessibilityHostMsg_LocationChangeParams;
namespace content {
class BrowserAccessibility;
@@ -26,22 +29,38 @@ class BrowserAccessibilityManagerAndroid;
class BrowserAccessibilityManagerWin;
#endif
+// For testing.
+CONTENT_EXPORT ui::AXTreeUpdate MakeAXTreeUpdate(
+ const ui::AXNodeData& node,
+ const ui::AXNodeData& node2 = ui::AXNodeData(),
+ const ui::AXNodeData& node3 = ui::AXNodeData(),
+ const ui::AXNodeData& node4 = ui::AXNodeData(),
+ const ui::AXNodeData& node5 = ui::AXNodeData(),
+ const ui::AXNodeData& node6 = ui::AXNodeData(),
+ const ui::AXNodeData& node7 = ui::AXNodeData(),
+ const ui::AXNodeData& node8 = ui::AXNodeData(),
+ const ui::AXNodeData& node9 = ui::AXNodeData());
+
// Class that can perform actions on behalf of the BrowserAccessibilityManager.
class CONTENT_EXPORT BrowserAccessibilityDelegate {
public:
virtual ~BrowserAccessibilityDelegate() {}
- virtual void SetAccessibilityFocus(int acc_obj_id) = 0;
+ virtual void AccessibilitySetFocus(int acc_obj_id) = 0;
virtual void AccessibilityDoDefaultAction(int acc_obj_id) = 0;
+ virtual void AccessibilityShowMenu(int acc_obj_id) = 0;
virtual void AccessibilityScrollToMakeVisible(
int acc_obj_id, gfx::Rect subfocus) = 0;
virtual void AccessibilityScrollToPoint(
int acc_obj_id, gfx::Point point) = 0;
virtual void AccessibilitySetTextSelection(
int acc_obj_id, int start_offset, int end_offset) = 0;
- virtual bool HasFocus() const = 0;
- virtual gfx::Rect GetViewBounds() const = 0;
- virtual gfx::Point GetLastTouchEventLocation() const = 0;
- virtual void FatalAccessibilityTreeError() = 0;
+ virtual bool AccessibilityViewHasFocus() const = 0;
+ virtual gfx::Rect AccessibilityGetViewBounds() const = 0;
+ virtual gfx::Point AccessibilityOriginInScreen(
+ const gfx::Rect& bounds) const = 0;
+ virtual void AccessibilityHitTest(
+ const gfx::Point& point) = 0;
+ virtual void AccessibilityFatalError() = 0;
};
class CONTENT_EXPORT BrowserAccessibilityFactory {
@@ -54,44 +73,41 @@ class CONTENT_EXPORT BrowserAccessibilityFactory {
};
// Manages a tree of BrowserAccessibility objects.
-class CONTENT_EXPORT BrowserAccessibilityManager {
+class CONTENT_EXPORT BrowserAccessibilityManager : public ui::AXTreeDelegate {
public:
// Creates the platform-specific BrowserAccessibilityManager, but
// with no parent window pointer. Only useful for unit tests.
static BrowserAccessibilityManager* Create(
- const AccessibilityNodeData& src,
+ const ui::AXTreeUpdate& initial_tree,
BrowserAccessibilityDelegate* delegate,
BrowserAccessibilityFactory* factory = new BrowserAccessibilityFactory());
virtual ~BrowserAccessibilityManager();
- void Initialize(const AccessibilityNodeData src);
+ void Initialize(const ui::AXTreeUpdate& initial_tree);
- static AccessibilityNodeData GetEmptyDocument();
+ static ui::AXTreeUpdate GetEmptyDocument();
virtual void NotifyAccessibilityEvent(
- blink::WebAXEvent event_type, BrowserAccessibility* node) { }
+ ui::AXEvent event_type, BrowserAccessibility* node) { }
// Return a pointer to the root of the tree, does not make a new reference.
BrowserAccessibility* GetRoot();
- // Removes a node from the manager.
- virtual void RemoveNode(BrowserAccessibility* node);
+ // Returns a pointer to the BrowserAccessibility object for a given AXNode.
+ BrowserAccessibility* GetFromAXNode(ui::AXNode* node);
- // Return a pointer to the object corresponding to the given renderer_id,
+ // Return a pointer to the object corresponding to the given id,
// does not make a new reference.
- BrowserAccessibility* GetFromRendererID(int32 renderer_id);
+ BrowserAccessibility* GetFromID(int32 id);
// Called to notify the accessibility manager that its associated native
- // view got focused. This implies that it is shown (opposite of WasHidden,
- // below).
- // The touch_event_context parameter indicates that we were called in the
- // context of a touch event.
- void GotFocus(bool touch_event_context);
+ // view got focused.
+ virtual void OnWindowFocused();
// Called to notify the accessibility manager that its associated native
- // view was hidden. When it's no longer hidden, GotFocus will be called.
- void WasHidden();
+ // view lost focus.
+ virtual void OnWindowBlurred();
// Called to notify the accessibility manager that a mouse down event
// occurred in the tab.
@@ -100,6 +116,7 @@ class CONTENT_EXPORT BrowserAccessibilityManager {
// 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);
// Tell the renderer to do the default action for this node.
@@ -125,10 +142,14 @@ class CONTENT_EXPORT BrowserAccessibilityManager {
gfx::Rect GetViewBounds();
// Called when the renderer process has notified us of about tree changes.
- // Send a notification to MSAA clients of the change.
void OnAccessibilityEvents(
const std::vector<AccessibilityHostMsg_EventParams>& params);
+ // Called when the renderer process updates the location of accessibility
+ // objects.
+ void OnLocationChanges(
+ const std::vector<AccessibilityHostMsg_LocationChangeParams>& params);
+
#if defined(OS_WIN)
BrowserAccessibilityManagerWin* ToBrowserAccessibilityManagerWin();
#endif
@@ -139,28 +160,29 @@ class CONTENT_EXPORT BrowserAccessibilityManager {
// Return the object that has focus, if it's a descandant of the
// given root (inclusive). Does not make a new reference.
- BrowserAccessibility* GetFocus(BrowserAccessibility* root);
+ virtual BrowserAccessibility* GetFocus(BrowserAccessibility* root);
- // Is the on-screen keyboard allowed to be shown, in response to a
- // focus event on a text box?
- bool IsOSKAllowed(const gfx::Rect& bounds);
+ // Return the descentant of the given root that has focus, or that object's
+ // active descendant if it has one.
+ BrowserAccessibility* GetActiveDescendantFocus(BrowserAccessibility* root);
// True by default, but some platforms want to treat the root
// scroll offsets separately.
virtual bool UseRootScrollOffsetsWhenComputingBounds();
- // For testing only: update the given nodes as if they were
- // received from the renderer process in OnAccessibilityEvents.
- // Takes up to 7 nodes at once so tests don't need to create a vector
- // each time.
- void UpdateNodesForTesting(
- const AccessibilityNodeData& node,
- const AccessibilityNodeData& node2 = AccessibilityNodeData(),
- const AccessibilityNodeData& node3 = AccessibilityNodeData(),
- const AccessibilityNodeData& node4 = AccessibilityNodeData(),
- const AccessibilityNodeData& node5 = AccessibilityNodeData(),
- const AccessibilityNodeData& node6 = AccessibilityNodeData(),
- const AccessibilityNodeData& node7 = AccessibilityNodeData());
+ // Walk the tree.
+ BrowserAccessibility* NextInTreeOrder(BrowserAccessibility* node);
+ BrowserAccessibility* PreviousInTreeOrder(BrowserAccessibility* node);
+
+ // AXTreeDelegate implementation.
+ virtual void OnNodeWillBeDeleted(ui::AXNode* node) OVERRIDE;
+ virtual void OnNodeCreated(ui::AXNode* node) OVERRIDE;
+ virtual void OnNodeChanged(ui::AXNode* node) OVERRIDE;
+ virtual void OnNodeCreationFinished(ui::AXNode* node) OVERRIDE;
+ virtual void OnNodeChangeFinished(ui::AXNode* node) OVERRIDE;
+ virtual void OnRootChanged(ui::AXNode* new_root) OVERRIDE {}
+
+ BrowserAccessibilityDelegate* delegate() const { return delegate_; }
protected:
BrowserAccessibilityManager(
@@ -168,13 +190,12 @@ class CONTENT_EXPORT BrowserAccessibilityManager {
BrowserAccessibilityFactory* factory);
BrowserAccessibilityManager(
- const AccessibilityNodeData& src,
+ const ui::AXTreeUpdate& initial_tree,
BrowserAccessibilityDelegate* delegate,
BrowserAccessibilityFactory* factory);
- virtual void AddNodeToMap(BrowserAccessibility* node);
-
- virtual void NotifyRootChanged() {}
+ // Called at the end of updating the tree.
+ virtual void OnTreeUpdateFinished() {}
private:
// The following states keep track of whether or not the
@@ -201,17 +222,17 @@ class CONTENT_EXPORT BrowserAccessibilityManager {
// Update a set of nodes using data received from the renderer
// process.
- bool UpdateNodes(const std::vector<AccessibilityNodeData>& nodes);
+ bool UpdateNodes(const std::vector<ui::AXNodeData>& nodes);
// Update one node from the tree using data received from the renderer
// process. Returns true on success, false on fatal error.
- bool UpdateNode(const AccessibilityNodeData& src);
+ bool UpdateNode(const ui::AXNodeData& src);
void SetRoot(BrowserAccessibility* root);
BrowserAccessibility* CreateNode(
BrowserAccessibility* parent,
- int32 renderer_id,
+ int32 id,
int32 index_in_parent);
protected:
@@ -221,17 +242,18 @@ class CONTENT_EXPORT BrowserAccessibilityManager {
// Factory to create BrowserAccessibility objects (for dependency injection).
scoped_ptr<BrowserAccessibilityFactory> factory_;
- // The root of the tree of accessible objects and the element that
- // currently has focus, if any.
- BrowserAccessibility* root_;
- BrowserAccessibility* focus_;
+ // The underlying tree of accessibility objects.
+ scoped_ptr<ui::AXTree> 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<int32, BrowserAccessibility*> id_wrapper_map_;
// The on-screen keyboard state.
OnScreenKeyboardState osk_state_;
- // A mapping from renderer IDs to BrowserAccessibility objects.
- base::hash_map<int32, BrowserAccessibility*> renderer_id_map_;
-
DISALLOW_COPY_AND_ASSIGN(BrowserAccessibilityManager);
};
diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_android.cc b/chromium/content/browser/accessibility/browser_accessibility_manager_android.cc
index 17538e9b347..07f04e71b45 100644
--- a/chromium/content/browser/accessibility/browser_accessibility_manager_android.cc
+++ b/chromium/content/browser/accessibility/browser_accessibility_manager_android.cc
@@ -26,9 +26,24 @@ enum {
ANDROID_ACCESSIBILITY_EVENT_TYPE_VIEW_TEXT_SELECTION_CHANGED = 8192
};
-// Restricts |val| to the range [min, max].
-int Clamp(int val, int min, int max) {
- return std::min(std::max(val, min), max);
+enum AndroidHtmlElementType {
+ HTML_ELEMENT_TYPE_SECTION,
+ HTML_ELEMENT_TYPE_LIST,
+ HTML_ELEMENT_TYPE_CONTROL,
+ HTML_ELEMENT_TYPE_ANY
+};
+
+// 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;
}
} // anonymous namespace
@@ -42,11 +57,11 @@ namespace aria_strings {
// static
BrowserAccessibilityManager* BrowserAccessibilityManager::Create(
- const AccessibilityNodeData& src,
+ const ui::AXTreeUpdate& initial_tree,
BrowserAccessibilityDelegate* delegate,
BrowserAccessibilityFactory* factory) {
- return new BrowserAccessibilityManagerAndroid(ScopedJavaLocalRef<jobject>(),
- src, delegate, factory);
+ return new BrowserAccessibilityManagerAndroid(
+ ScopedJavaLocalRef<jobject>(), initial_tree, delegate, factory);
}
BrowserAccessibilityManagerAndroid*
@@ -56,10 +71,10 @@ BrowserAccessibilityManager::ToBrowserAccessibilityManagerAndroid() {
BrowserAccessibilityManagerAndroid::BrowserAccessibilityManagerAndroid(
ScopedJavaLocalRef<jobject> content_view_core,
- const AccessibilityNodeData& src,
+ const ui::AXTreeUpdate& initial_tree,
BrowserAccessibilityDelegate* delegate,
BrowserAccessibilityFactory* factory)
- : BrowserAccessibilityManager(src, delegate, factory) {
+ : BrowserAccessibilityManager(initial_tree, delegate, factory) {
SetContentViewCore(content_view_core);
}
@@ -73,12 +88,15 @@ BrowserAccessibilityManagerAndroid::~BrowserAccessibilityManagerAndroid() {
}
// static
-AccessibilityNodeData BrowserAccessibilityManagerAndroid::GetEmptyDocument() {
- AccessibilityNodeData empty_document;
+ui::AXTreeUpdate BrowserAccessibilityManagerAndroid::GetEmptyDocument() {
+ ui::AXNodeData empty_document;
empty_document.id = 0;
- empty_document.role = blink::WebAXRoleRootWebArea;
- empty_document.state = 1 << blink::WebAXStateReadonly;
- return empty_document;
+ empty_document.role = ui::AX_ROLE_ROOT_WEB_AREA;
+ empty_document.state = 1 << ui::AX_STATE_READ_ONLY;
+
+ ui::AXTreeUpdate update;
+ update.nodes.push_back(empty_document);
+ return update;
}
void BrowserAccessibilityManagerAndroid::SetContentViewCore(
@@ -94,43 +112,52 @@ void BrowserAccessibilityManagerAndroid::SetContentViewCore(
}
void BrowserAccessibilityManagerAndroid::NotifyAccessibilityEvent(
- blink::WebAXEvent event_type,
+ ui::AXEvent event_type,
BrowserAccessibility* node) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
if (obj.is_null())
return;
- if (event_type == blink::WebAXEventHide)
+ if (event_type == ui::AX_EVENT_HIDE)
+ return;
+
+ if (event_type == ui::AX_EVENT_HOVER) {
+ HandleHoverEvent(node);
return;
+ }
// Always send AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED to notify
// the Android system that the accessibility hierarchy rooted at this
// node has changed.
Java_BrowserAccessibilityManager_handleContentChanged(
- env, obj.obj(), node->renderer_id());
+ env, obj.obj(), node->GetId());
switch (event_type) {
- case blink::WebAXEventLoadComplete:
+ case ui::AX_EVENT_LOAD_COMPLETE:
Java_BrowserAccessibilityManager_handlePageLoaded(
- env, obj.obj(), focus_->renderer_id());
+ env, obj.obj(), focus_->id());
break;
- case blink::WebAXEventFocus:
+ case ui::AX_EVENT_FOCUS:
Java_BrowserAccessibilityManager_handleFocusChanged(
- env, obj.obj(), node->renderer_id());
+ env, obj.obj(), node->GetId());
break;
- case blink::WebAXEventCheckedStateChanged:
+ case ui::AX_EVENT_CHECKED_STATE_CHANGED:
Java_BrowserAccessibilityManager_handleCheckStateChanged(
- env, obj.obj(), node->renderer_id());
+ env, obj.obj(), node->GetId());
break;
- case blink::WebAXEventScrolledToAnchor:
+ case ui::AX_EVENT_SCROLL_POSITION_CHANGED:
+ Java_BrowserAccessibilityManager_handleScrollPositionChanged(
+ env, obj.obj(), node->GetId());
+ break;
+ case ui::AX_EVENT_SCROLLED_TO_ANCHOR:
Java_BrowserAccessibilityManager_handleScrolledToAnchor(
- env, obj.obj(), node->renderer_id());
+ env, obj.obj(), node->GetId());
break;
- case blink::WebAXEventAlert:
+ case ui::AX_EVENT_ALERT:
// An alert is a special case of live region. Fall through to the
// next case to handle it.
- case blink::WebAXEventShow: {
+ case ui::AX_EVENT_SHOW: {
// This event is fired when an object appears in a live region.
// Speak its text.
BrowserAccessibilityAndroid* android_node =
@@ -141,16 +168,16 @@ void BrowserAccessibilityManagerAndroid::NotifyAccessibilityEvent(
env, android_node->GetText()).obj());
break;
}
- case blink::WebAXEventSelectedTextChanged:
+ case ui::AX_EVENT_SELECTED_TEXT_CHANGED:
Java_BrowserAccessibilityManager_handleTextSelectionChanged(
- env, obj.obj(), node->renderer_id());
+ env, obj.obj(), node->GetId());
break;
- case blink::WebAXEventChildrenChanged:
- case blink::WebAXEventTextChanged:
- case blink::WebAXEventValueChanged:
+ case ui::AX_EVENT_CHILDREN_CHANGED:
+ case ui::AX_EVENT_TEXT_CHANGED:
+ case ui::AX_EVENT_VALUE_CHANGED:
if (node->IsEditableText()) {
Java_BrowserAccessibilityManager_handleEditableTextChanged(
- env, obj.obj(), node->renderer_id());
+ env, obj.obj(), node->GetId());
}
break;
default:
@@ -161,49 +188,34 @@ void BrowserAccessibilityManagerAndroid::NotifyAccessibilityEvent(
}
jint BrowserAccessibilityManagerAndroid::GetRootId(JNIEnv* env, jobject obj) {
- return static_cast<jint>(root_->renderer_id());
+ return static_cast<jint>(GetRoot()->GetId());
}
jboolean BrowserAccessibilityManagerAndroid::IsNodeValid(
JNIEnv* env, jobject obj, jint id) {
- return GetFromRendererID(id) != NULL;
+ return GetFromID(id) != NULL;
}
-jint BrowserAccessibilityManagerAndroid::HitTest(
+void BrowserAccessibilityManagerAndroid::HitTest(
JNIEnv* env, jobject obj, jint x, jint y) {
- BrowserAccessibilityAndroid* result =
- static_cast<BrowserAccessibilityAndroid*>(
- root_->BrowserAccessibilityForPoint(gfx::Point(x, y)));
-
- if (!result)
- return root_->renderer_id();
-
- if (result->IsFocusable())
- return result->renderer_id();
-
- // Examine the children of |result| to find the nearest accessibility focus
- // candidate
- BrowserAccessibility* nearest_node = FuzzyHitTest(x, y, result);
- if (nearest_node)
- return nearest_node->renderer_id();
-
- return root_->renderer_id();
+ if (delegate())
+ delegate()->AccessibilityHitTest(gfx::Point(x, y));
}
jboolean BrowserAccessibilityManagerAndroid::PopulateAccessibilityNodeInfo(
JNIEnv* env, jobject obj, jobject info, jint id) {
BrowserAccessibilityAndroid* node = static_cast<BrowserAccessibilityAndroid*>(
- GetFromRendererID(id));
+ GetFromID(id));
if (!node)
return false;
- if (node->parent()) {
+ if (node->GetParent()) {
Java_BrowserAccessibilityManager_setAccessibilityNodeInfoParent(
- env, obj, info, node->parent()->renderer_id());
+ env, obj, info, node->GetParent()->GetId());
}
for (unsigned i = 0; i < node->PlatformChildCount(); ++i) {
Java_BrowserAccessibilityManager_addAccessibilityNodeInfoChild(
- env, obj, info, node->children()[i]->renderer_id());
+ env, obj, info, node->InternalGetChild(i)->GetId());
}
Java_BrowserAccessibilityManager_setAccessibilityNodeInfoBooleanAttributes(
env, obj, info,
@@ -218,18 +230,21 @@ jboolean BrowserAccessibilityManagerAndroid::PopulateAccessibilityNodeInfo(
node->IsScrollable(),
node->IsSelected(),
node->IsVisibleToUser());
- Java_BrowserAccessibilityManager_setAccessibilityNodeInfoStringAttributes(
+ Java_BrowserAccessibilityManager_setAccessibilityNodeInfoClassName(
env, obj, info,
- base::android::ConvertUTF8ToJavaString(env, node->GetClassName()).obj(),
- base::android::ConvertUTF16ToJavaString(env, node->GetText()).obj());
+ base::android::ConvertUTF8ToJavaString(env, node->GetClassName()).obj());
+ Java_BrowserAccessibilityManager_setAccessibilityNodeInfoContentDescription(
+ env, obj, info,
+ base::android::ConvertUTF16ToJavaString(env, node->GetText()).obj(),
+ node->IsLink());
gfx::Rect absolute_rect = node->GetLocalBoundsRect();
gfx::Rect parent_relative_rect = absolute_rect;
- if (node->parent()) {
- gfx::Rect parent_rect = node->parent()->GetLocalBoundsRect();
+ if (node->GetParent()) {
+ gfx::Rect parent_rect = node->GetParent()->GetLocalBoundsRect();
parent_relative_rect.Offset(-parent_rect.OffsetFromOrigin());
}
- bool is_root = node->parent() == NULL;
+ bool is_root = node->GetParent() == NULL;
Java_BrowserAccessibilityManager_setAccessibilityNodeInfoLocation(
env, obj, info,
absolute_rect.x(), absolute_rect.y(),
@@ -277,7 +292,7 @@ jboolean BrowserAccessibilityManagerAndroid::PopulateAccessibilityNodeInfo(
jboolean BrowserAccessibilityManagerAndroid::PopulateAccessibilityEvent(
JNIEnv* env, jobject obj, jobject event, jint id, jint event_type) {
BrowserAccessibilityAndroid* node = static_cast<BrowserAccessibilityAndroid*>(
- GetFromRendererID(id));
+ GetFromID(id));
if (!node)
return false;
@@ -363,74 +378,110 @@ jboolean BrowserAccessibilityManagerAndroid::PopulateAccessibilityEvent(
void BrowserAccessibilityManagerAndroid::Click(
JNIEnv* env, jobject obj, jint id) {
- BrowserAccessibility* node = GetFromRendererID(id);
+ BrowserAccessibility* node = GetFromID(id);
if (node)
DoDefaultAction(*node);
}
void BrowserAccessibilityManagerAndroid::Focus(
JNIEnv* env, jobject obj, jint id) {
- BrowserAccessibility* node = GetFromRendererID(id);
+ BrowserAccessibility* node = GetFromID(id);
if (node)
SetFocus(node, true);
}
void BrowserAccessibilityManagerAndroid::Blur(JNIEnv* env, jobject obj) {
- SetFocus(root_, true);
+ SetFocus(GetRoot(), true);
}
-BrowserAccessibility* BrowserAccessibilityManagerAndroid::FuzzyHitTest(
- int x, int y, BrowserAccessibility* start_node) {
- BrowserAccessibility* nearest_node = NULL;
- int min_distance = INT_MAX;
- FuzzyHitTestImpl(x, y, start_node, &nearest_node, &min_distance);
- return nearest_node;
+void BrowserAccessibilityManagerAndroid::ScrollToMakeNodeVisible(
+ JNIEnv* env, jobject obj, jint id) {
+ BrowserAccessibility* node = GetFromID(id);
+ if (node)
+ ScrollToMakeVisible(*node, gfx::Rect(node->GetLocation().size()));
}
-// static
-void BrowserAccessibilityManagerAndroid::FuzzyHitTestImpl(
- int x, int y, BrowserAccessibility* start_node,
- BrowserAccessibility** nearest_candidate, int* nearest_distance) {
- BrowserAccessibilityAndroid* node =
- static_cast<BrowserAccessibilityAndroid*>(start_node);
- int distance = CalculateDistanceSquared(x, y, node);
-
- if (node->IsFocusable()) {
- if (distance < *nearest_distance) {
- *nearest_candidate = node;
- *nearest_distance = distance;
- }
- // Don't examine any more children of focusable node
- // TODO(aboxhall): what about focusable children?
+void BrowserAccessibilityManagerAndroid::HandleHoverEvent(
+ BrowserAccessibility* node) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
+ if (obj.is_null())
return;
- }
- if (!node->GetText().empty()) {
- if (distance < *nearest_distance) {
- *nearest_candidate = node;
- *nearest_distance = distance;
+ BrowserAccessibilityAndroid* ancestor =
+ static_cast<BrowserAccessibilityAndroid*>(node->GetParent());
+ while (ancestor) {
+ if (ancestor->PlatformIsLeaf() ||
+ (ancestor->IsFocusable() && !ancestor->HasFocusableChild())) {
+ node = ancestor;
+ // Don't break - we want the highest ancestor that's focusable or a
+ // leaf node.
}
- return;
+ ancestor = static_cast<BrowserAccessibilityAndroid*>(ancestor->GetParent());
}
- for (uint32 i = 0; i < node->PlatformChildCount(); i++) {
- BrowserAccessibility* child = node->PlatformGetChild(i);
- FuzzyHitTestImpl(x, y, child, nearest_candidate, nearest_distance);
- }
+ Java_BrowserAccessibilityManager_handleHover(
+ env, obj.obj(), node->GetId());
}
-// static
-int BrowserAccessibilityManagerAndroid::CalculateDistanceSquared(
- int x, int y, BrowserAccessibility* node) {
- gfx::Rect node_bounds = node->GetLocalBoundsRect();
- int nearest_x = Clamp(x, node_bounds.x(), node_bounds.right());
- int nearest_y = Clamp(y, node_bounds.y(), node_bounds.bottom());
- int dx = std::abs(x - nearest_x);
- int dy = std::abs(y - nearest_y);
- return dx * dx + dy * dy;
+jint BrowserAccessibilityManagerAndroid::FindElementType(
+ JNIEnv* env, jobject obj, jint start_id, jstring element_type_str,
+ jboolean forwards) {
+ BrowserAccessibility* node = GetFromID(start_id);
+ if (!node)
+ return 0;
+
+ AndroidHtmlElementType element_type = HtmlElementTypeFromString(
+ base::android::ConvertJavaStringToUTF16(env, element_type_str));
+
+ 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<BrowserAccessibilityAndroid*>(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<BrowserAccessibilityAndroid*>(node)->IsClickable())
+ return node->GetId();
+ break;
+ }
+
+ node = forwards ? NextInTreeOrder(node) : PreviousInTreeOrder(node);
+ }
+
+ return 0;
}
-void BrowserAccessibilityManagerAndroid::NotifyRootChanged() {
+void BrowserAccessibilityManagerAndroid::OnRootChanged(ui::AXNode* new_root) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
if (obj.is_null())
diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_android.h b/chromium/content/browser/accessibility/browser_accessibility_manager_android.h
index 6a9f38d8fb8..a40f7e7cf88 100644
--- a/chromium/content/browser/accessibility/browser_accessibility_manager_android.h
+++ b/chromium/content/browser/accessibility/browser_accessibility_manager_android.h
@@ -21,20 +21,20 @@ class CONTENT_EXPORT BrowserAccessibilityManagerAndroid
public:
BrowserAccessibilityManagerAndroid(
base::android::ScopedJavaLocalRef<jobject> content_view_core,
- const AccessibilityNodeData& src,
+ const ui::AXTreeUpdate& initial_tree,
BrowserAccessibilityDelegate* delegate,
BrowserAccessibilityFactory* factory = new BrowserAccessibilityFactory());
virtual ~BrowserAccessibilityManagerAndroid();
- static AccessibilityNodeData GetEmptyDocument();
+ static ui::AXTreeUpdate GetEmptyDocument();
void SetContentViewCore(
base::android::ScopedJavaLocalRef<jobject> content_view_core);
// Implementation of BrowserAccessibilityManager.
virtual void NotifyAccessibilityEvent(
- blink::WebAXEvent event_type, BrowserAccessibility* node) OVERRIDE;
+ ui::AXEvent event_type, BrowserAccessibility* node) OVERRIDE;
// --------------------------------------------------------------------------
// Methods called from Java via JNI
@@ -43,7 +43,7 @@ class CONTENT_EXPORT BrowserAccessibilityManagerAndroid
// Tree methods.
jint GetRootId(JNIEnv* env, jobject obj);
jboolean IsNodeValid(JNIEnv* env, jobject obj, jint id);
- jint HitTest(JNIEnv* env, jobject obj, jint x, jint y);
+ void HitTest(JNIEnv* env, jobject obj, jint x, jint y);
// Populate Java accessibility data structures with info about a node.
jboolean PopulateAccessibilityNodeInfo(
@@ -55,9 +55,19 @@ class CONTENT_EXPORT BrowserAccessibilityManagerAndroid
void Click(JNIEnv* env, jobject obj, jint id);
void Focus(JNIEnv* env, jobject obj, jint id);
void Blur(JNIEnv* env, jobject obj);
+ void ScrollToMakeNodeVisible(JNIEnv* env, jobject obj, int id);
+
+ // Return the id of the next node in tree order in the direction given by
+ // |forwards|, starting with |start_id|, that matches |element_type|,
+ // where |element_type| is a special uppercase string from TalkBack or
+ // BrailleBack indicating general categories of web content like
+ // "SECTION" or "CONTROL". Return 0 if not found.
+ jint FindElementType(JNIEnv* env, jobject obj, jint start_id,
+ jstring element_type, jboolean forwards);
protected:
- virtual void NotifyRootChanged() OVERRIDE;
+ // AXTreeDelegate overrides.
+ virtual void OnRootChanged(ui::AXNode* new_root) OVERRIDE;
virtual bool UseRootScrollOffsetsWhenComputingBounds() OVERRIDE;
@@ -73,18 +83,8 @@ class CONTENT_EXPORT BrowserAccessibilityManagerAndroid
// ContentViewCore.
JavaObjectWeakGlobalRef java_ref_;
- // Searches through the children of start_node to find the nearest
- // accessibility focus candidate for a touch which has not landed directly on
- // an accessibility focus candidate.
- BrowserAccessibility* FuzzyHitTest(
- int x, int y, BrowserAccessibility* start_node);
-
- static void FuzzyHitTestImpl(int x, int y, BrowserAccessibility* start_node,
- BrowserAccessibility** nearest_candidate, int* min_distance);
-
- // Calculates the distance from the point (x, y) to the nearest point on the
- // edge of |node|.
- static int CalculateDistanceSquared(int x, int y, BrowserAccessibility* node);
+ // Handle a hover event from the renderer process.
+ void HandleHoverEvent(BrowserAccessibility* node);
DISALLOW_COPY_AND_ASSIGN(BrowserAccessibilityManagerAndroid);
};
diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_gtk.cc b/chromium/content/browser/accessibility/browser_accessibility_manager_gtk.cc
deleted file mode 100644
index e97f8b2635b..00000000000
--- a/chromium/content/browser/accessibility/browser_accessibility_manager_gtk.cc
+++ /dev/null
@@ -1,82 +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/accessibility/browser_accessibility_manager_gtk.h"
-
-#include "content/browser/accessibility/browser_accessibility_gtk.h"
-#include "content/common/accessibility_messages.h"
-
-namespace content {
-
-// static
-BrowserAccessibilityManager* BrowserAccessibilityManager::Create(
- const AccessibilityNodeData& src,
- BrowserAccessibilityDelegate* delegate,
- BrowserAccessibilityFactory* factory) {
- return new BrowserAccessibilityManagerGtk(
- NULL,
- src,
- delegate,
- factory);
-}
-
-BrowserAccessibilityManagerGtk::BrowserAccessibilityManagerGtk(
- GtkWidget* parent_widget,
- const AccessibilityNodeData& src,
- BrowserAccessibilityDelegate* delegate,
- BrowserAccessibilityFactory* factory)
- : BrowserAccessibilityManager(delegate, factory),
- parent_widget_(parent_widget) {
- Initialize(src);
-}
-
-BrowserAccessibilityManagerGtk::~BrowserAccessibilityManagerGtk() {
-}
-
-// static
-AccessibilityNodeData BrowserAccessibilityManagerGtk::GetEmptyDocument() {
- AccessibilityNodeData empty_document;
- empty_document.id = 0;
- empty_document.role = blink::WebAXRoleRootWebArea;
- empty_document.state =
- 1 << blink::WebAXStateReadonly;
- return empty_document;
-}
-
-void BrowserAccessibilityManagerGtk::NotifyAccessibilityEvent(
- blink::WebAXEvent event_type,
- BrowserAccessibility* node) {
- if (!node->IsNative())
- return;
- AtkObject* atk_object = node->ToBrowserAccessibilityGtk()->GetAtkObject();
-
- switch (event_type) {
- case blink::WebAXEventChildrenChanged:
- RecursivelySendChildrenChanged(GetRoot()->ToBrowserAccessibilityGtk());
- break;
- case blink::WebAXEventFocus:
- // Note: the focus-event was deprecated in ATK 2.9.4
- // See https://bugzilla.gnome.org/show_bug.cgi?id=649575#c8
- g_signal_emit_by_name(atk_object, "focus-event", true);
- break;
- default:
- break;
- }
-}
-
-void BrowserAccessibilityManagerGtk::RecursivelySendChildrenChanged(
- BrowserAccessibilityGtk* node) {
- AtkObject* atkObject = node->ToBrowserAccessibilityGtk()->GetAtkObject();
- for (unsigned int i = 0; i < node->children().size(); ++i) {
- BrowserAccessibilityGtk* child =
- node->children()[i]->ToBrowserAccessibilityGtk();
- g_signal_emit_by_name(atkObject,
- "children-changed::add",
- i,
- child->GetAtkObject());
- RecursivelySendChildrenChanged(child);
- }
-}
-
-} // namespace content
diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_gtk.h b/chromium/content/browser/accessibility/browser_accessibility_manager_gtk.h
deleted file mode 100644
index 97d99d36f7b..00000000000
--- a/chromium/content/browser/accessibility/browser_accessibility_manager_gtk.h
+++ /dev/null
@@ -1,48 +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_ACCESSIBILITY_BROWSER_ACCESSIBILITY_MANAGER_GTK_H_
-#define CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_MANAGER_GTK_H_
-
-#include "content/browser/accessibility/browser_accessibility_manager.h"
-
-struct ViewHostMsg_AccessibilityNotification_Params;
-
-namespace content {
-class BrowserAccessibilityGtk;
-
-// Manages a tree of BrowserAccessibilityGtk objects.
-class CONTENT_EXPORT BrowserAccessibilityManagerGtk
- : public BrowserAccessibilityManager {
- public:
- BrowserAccessibilityManagerGtk(
- GtkWidget* parent_widget,
- const AccessibilityNodeData& src,
- BrowserAccessibilityDelegate* delegate,
- BrowserAccessibilityFactory* factory = new BrowserAccessibilityFactory());
-
- virtual ~BrowserAccessibilityManagerGtk();
-
- static AccessibilityNodeData GetEmptyDocument();
-
- // BrowserAccessibilityManager methods
- virtual void NotifyAccessibilityEvent(
- blink::WebAXEvent event_type, BrowserAccessibility* node) OVERRIDE;
-
- GtkWidget* parent_widget() { return parent_widget_; }
-
- private:
- void RecursivelySendChildrenChanged(BrowserAccessibilityGtk* node);
-
- GtkWidget* parent_widget_;
-
- // Give BrowserAccessibilityManager::Create access to our constructor.
- friend class BrowserAccessibilityManager;
-
- DISALLOW_COPY_AND_ASSIGN(BrowserAccessibilityManagerGtk);
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_MANAGER_GTK_H_
diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_mac.h b/chromium/content/browser/accessibility/browser_accessibility_manager_mac.h
index 3b00f051121..fb2a0eb47b0 100644
--- a/chromium/content/browser/accessibility/browser_accessibility_manager_mac.h
+++ b/chromium/content/browser/accessibility/browser_accessibility_manager_mac.h
@@ -16,25 +16,32 @@ class CONTENT_EXPORT BrowserAccessibilityManagerMac
public:
BrowserAccessibilityManagerMac(
NSView* parent_view,
- const AccessibilityNodeData& src,
+ const ui::AXTreeUpdate& initial_tree,
BrowserAccessibilityDelegate* delegate,
BrowserAccessibilityFactory* factory = new BrowserAccessibilityFactory());
- static AccessibilityNodeData GetEmptyDocument();
+ static ui::AXTreeUpdate GetEmptyDocument();
+
+ virtual BrowserAccessibility* GetFocus(BrowserAccessibility* root) OVERRIDE;
// Implementation of BrowserAccessibilityManager.
virtual void NotifyAccessibilityEvent(
- blink::WebAXEvent event_type, BrowserAccessibility* node) OVERRIDE;
+ ui::AXEvent event_type, BrowserAccessibility* node) OVERRIDE;
NSView* parent_view() { return parent_view_; }
private:
+ virtual void OnNodeCreationFinished(ui::AXNode* node) OVERRIDE;
+ virtual void OnTreeUpdateFinished() OVERRIDE;
+
// This gives BrowserAccessibilityManager::Create access to the class
// constructor.
friend class BrowserAccessibilityManager;
NSView* parent_view_;
+ bool created_live_region_;
+
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 8f07f31959f..53d776bc287 100644
--- a/chromium/content/browser/accessibility/browser_accessibility_manager_mac.mm
+++ b/chromium/content/browser/accessibility/browser_accessibility_manager_mac.mm
@@ -6,39 +6,50 @@
#import "base/logging.h"
#import "content/browser/accessibility/browser_accessibility_cocoa.h"
+#import "content/browser/accessibility/browser_accessibility_mac.h"
#include "content/common/accessibility_messages.h"
namespace content {
// static
BrowserAccessibilityManager* BrowserAccessibilityManager::Create(
- const AccessibilityNodeData& src,
+ const ui::AXTreeUpdate& initial_tree,
BrowserAccessibilityDelegate* delegate,
BrowserAccessibilityFactory* factory) {
- return new BrowserAccessibilityManagerMac(NULL, src, delegate, factory);
+ return new BrowserAccessibilityManagerMac(
+ NULL, initial_tree, delegate, factory);
}
BrowserAccessibilityManagerMac::BrowserAccessibilityManagerMac(
NSView* parent_view,
- const AccessibilityNodeData& src,
+ const ui::AXTreeUpdate& initial_tree,
BrowserAccessibilityDelegate* delegate,
BrowserAccessibilityFactory* factory)
- : BrowserAccessibilityManager(src, delegate, factory),
- parent_view_(parent_view) {
+ : BrowserAccessibilityManager(initial_tree, delegate, factory),
+ parent_view_(parent_view),
+ created_live_region_(false) {
}
// static
-AccessibilityNodeData BrowserAccessibilityManagerMac::GetEmptyDocument() {
- AccessibilityNodeData empty_document;
+ui::AXTreeUpdate BrowserAccessibilityManagerMac::GetEmptyDocument() {
+ ui::AXNodeData empty_document;
empty_document.id = 0;
- empty_document.role = blink::WebAXRoleRootWebArea;
+ empty_document.role = ui::AX_ROLE_ROOT_WEB_AREA;
empty_document.state =
- 1 << blink::WebAXStateReadonly;
- return empty_document;
+ 1 << ui::AX_STATE_READ_ONLY;
+ ui::AXTreeUpdate update;
+ update.nodes.push_back(empty_document);
+ return update;
+}
+
+BrowserAccessibility* BrowserAccessibilityManagerMac::GetFocus(
+ BrowserAccessibility* root) {
+ BrowserAccessibility* node = GetActiveDescendantFocus(root);
+ return node;
}
void BrowserAccessibilityManagerMac::NotifyAccessibilityEvent(
- blink::WebAXEvent event_type,
+ ui::AXEvent event_type,
BrowserAccessibility* node) {
if (!node->IsNative())
return;
@@ -46,88 +57,94 @@ void BrowserAccessibilityManagerMac::NotifyAccessibilityEvent(
// Refer to AXObjectCache.mm (webkit).
NSString* event_id = @"";
switch (event_type) {
- case blink::WebAXEventActiveDescendantChanged:
- if (node->role() == blink::WebAXRoleTree)
+ case ui::AX_EVENT_ACTIVEDESCENDANTCHANGED:
+ if (node->GetRole() == ui::AX_ROLE_TREE) {
event_id = NSAccessibilitySelectedRowsChangedNotification;
- else
+ } else {
event_id = NSAccessibilityFocusedUIElementChangedNotification;
+ BrowserAccessibility* active_descendant_focus =
+ GetActiveDescendantFocus(GetRoot());
+ if (active_descendant_focus)
+ node = active_descendant_focus;
+ }
+
break;
- case blink::WebAXEventAlert:
+ case ui::AX_EVENT_ALERT:
// Not used on Mac.
return;
- case blink::WebAXEventBlur:
+ case ui::AX_EVENT_BLUR:
// A no-op on Mac.
return;
- case blink::WebAXEventCheckedStateChanged:
+ case ui::AX_EVENT_CHECKED_STATE_CHANGED:
// Not used on Mac.
return;
- case blink::WebAXEventChildrenChanged:
+ case ui::AX_EVENT_CHILDREN_CHANGED:
// TODO(dtseng): no clear equivalent on Mac.
return;
- case blink::WebAXEventFocus:
+ case ui::AX_EVENT_FOCUS:
event_id = NSAccessibilityFocusedUIElementChangedNotification;
break;
- case blink::WebAXEventLayoutComplete:
+ case ui::AX_EVENT_LAYOUT_COMPLETE:
event_id = @"AXLayoutComplete";
break;
- case blink::WebAXEventLiveRegionChanged:
+ case ui::AX_EVENT_LIVE_REGION_CHANGED:
event_id = @"AXLiveRegionChanged";
break;
- case blink::WebAXEventLoadComplete:
+ case ui::AX_EVENT_LOAD_COMPLETE:
event_id = @"AXLoadComplete";
break;
- case blink::WebAXEventMenuListValueChanged:
+ case ui::AX_EVENT_MENU_LIST_VALUE_CHANGED:
// Not used on Mac.
return;
- case blink::WebAXEventShow:
+ case ui::AX_EVENT_SHOW:
// Not used on Mac.
return;
- case blink::WebAXEventHide:
+ case ui::AX_EVENT_HIDE:
// Not used on Mac.
return;
- case blink::WebAXEventRowCountChanged:
+ case ui::AX_EVENT_ROW_COUNT_CHANGED:
event_id = NSAccessibilityRowCountChangedNotification;
break;
- case blink::WebAXEventRowCollapsed:
+ case ui::AX_EVENT_ROW_COLLAPSED:
event_id = @"AXRowCollapsed";
break;
- case blink::WebAXEventRowExpanded:
+ case ui::AX_EVENT_ROW_EXPANDED:
event_id = @"AXRowExpanded";
break;
- case blink::WebAXEventScrolledToAnchor:
+ case ui::AX_EVENT_SCROLLED_TO_ANCHOR:
// Not used on Mac.
return;
- case blink::WebAXEventSelectedChildrenChanged:
+ case ui::AX_EVENT_SELECTED_CHILDREN_CHANGED:
event_id = NSAccessibilitySelectedChildrenChangedNotification;
break;
- case blink::WebAXEventSelectedTextChanged:
+ case ui::AX_EVENT_SELECTED_TEXT_CHANGED:
event_id = NSAccessibilitySelectedTextChangedNotification;
break;
- case blink::WebAXEventTextInserted:
+ case ui::AX_EVENT_TEXT_INSERTED:
// Not used on Mac.
return;
- case blink::WebAXEventTextRemoved:
+ case ui::AX_EVENT_TEXT_REMOVED:
// Not used on Mac.
return;
- case blink::WebAXEventValueChanged:
+ case ui::AX_EVENT_VALUE_CHANGED:
event_id = NSAccessibilityValueChangedNotification;
break;
- case blink::WebAXEventAriaAttributeChanged:
+ case ui::AX_EVENT_ARIA_ATTRIBUTE_CHANGED:
// Not used on Mac.
return;
- case blink::WebAXEventAutocorrectionOccured:
+ case ui::AX_EVENT_AUTOCORRECTION_OCCURED:
// Not used on Mac.
return;
- case blink::WebAXEventInvalidStatusChanged:
+ case ui::AX_EVENT_INVALID_STATUS_CHANGED:
// Not used on Mac.
return;
- case blink::WebAXEventLocationChanged:
+ case ui::AX_EVENT_LOCATION_CHANGED:
// Not used on Mac.
return;
- case blink::WebAXEventMenuListItemSelected:
+ case ui::AX_EVENT_MENU_LIST_ITEM_SELECTED:
// Not used on Mac.
return;
- case blink::WebAXEventTextChanged:
+ case ui::AX_EVENT_TEXT_CHANGED:
// Not used on Mac.
return;
default:
@@ -139,4 +156,27 @@ void BrowserAccessibilityManagerMac::NotifyAccessibilityEvent(
NSAccessibilityPostNotification(native_node, event_id);
}
+void BrowserAccessibilityManagerMac::OnNodeCreationFinished(ui::AXNode* node) {
+ BrowserAccessibility* obj = GetFromAXNode(node);
+ if (obj && obj->HasStringAttribute(ui::AX_ATTR_LIVE_STATUS))
+ created_live_region_ = true;
+}
+
+void BrowserAccessibilityManagerMac::OnTreeUpdateFinished() {
+ if (!created_live_region_)
+ return;
+
+ // This code is to work around a bug in VoiceOver, where a new live
+ // region that gets added is ignored. VoiceOver seems to only scan the
+ // page for live regions once. By recreating the NSAccessibility
+ // object for the root of the tree, we force VoiceOver to clear out its
+ // internal state and find newly-added live regions this time.
+ BrowserAccessibilityMac* root =
+ static_cast<BrowserAccessibilityMac*>(GetRoot());
+ root->RecreateNativeObject();
+ NotifyAccessibilityEvent(ui::AX_EVENT_CHILDREN_CHANGED, root);
+
+ created_live_region_ = false;
+}
+
} // 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 b0d48363989..ddff85fd804 100644
--- a/chromium/content/browser/accessibility/browser_accessibility_manager_unittest.cc
+++ b/chromium/content/browser/accessibility/browser_accessibility_manager_unittest.cc
@@ -6,8 +6,10 @@
#include "base/strings/utf_string_conversions.h"
#include "content/browser/accessibility/browser_accessibility.h"
#include "content/browser/accessibility/browser_accessibility_manager.h"
+#if defined(OS_WIN)
+#include "content/browser/accessibility/browser_accessibility_win.h"
+#endif
#include "content/common/accessibility_messages.h"
-#include "content/common/accessibility_node_data.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
@@ -36,6 +38,15 @@ class CountedBrowserAccessibility : public BrowserAccessibility {
int native_ref_count_;
static int global_obj_count_;
+
+#if defined(OS_WIN)
+ // Adds some padding to prevent a heap-buffer-overflow when an instance of
+ // this class is casted into a BrowserAccessibilityWin pointer.
+ // http://crbug.com/235508
+ // TODO(dmazzoni): Fix this properly.
+ static const size_t kDataSize = sizeof(int) + sizeof(BrowserAccessibility);
+ uint8 padding_[sizeof(BrowserAccessibilityWin) - kDataSize];
+#endif
};
int CountedBrowserAccessibility::global_obj_count_ = 0;
@@ -56,24 +67,27 @@ class TestBrowserAccessibilityDelegate
TestBrowserAccessibilityDelegate()
: got_fatal_error_(false) {}
- virtual void SetAccessibilityFocus(int acc_obj_id) OVERRIDE {}
+ virtual void AccessibilitySetFocus(int acc_obj_id) OVERRIDE {}
virtual void AccessibilityDoDefaultAction(int acc_obj_id) OVERRIDE {}
+ virtual void AccessibilityShowMenu(int acc_obj_id) OVERRIDE {}
virtual void AccessibilityScrollToMakeVisible(
int acc_obj_id, gfx::Rect subfocus) OVERRIDE {}
virtual void AccessibilityScrollToPoint(
int acc_obj_id, gfx::Point point) OVERRIDE {}
virtual void AccessibilitySetTextSelection(
int acc_obj_id, int start_offset, int end_offset) OVERRIDE {}
- virtual bool HasFocus() const OVERRIDE {
+ virtual bool AccessibilityViewHasFocus() const OVERRIDE {
return false;
}
- virtual gfx::Rect GetViewBounds() const OVERRIDE {
+ virtual gfx::Rect AccessibilityGetViewBounds() const OVERRIDE {
return gfx::Rect();
}
- virtual gfx::Point GetLastTouchEventLocation() const OVERRIDE {
+ virtual gfx::Point AccessibilityOriginInScreen(
+ const gfx::Rect& bounds) const OVERRIDE {
return gfx::Point();
}
- virtual void FatalAccessibilityTreeError() OVERRIDE {
+ virtual void AccessibilityHitTest(const gfx::Point& point) OVERRIDE {}
+ virtual void AccessibilityFatalError() OVERRIDE {
got_fatal_error_ = true;
}
@@ -87,40 +101,39 @@ private:
} // anonymous namespace
TEST(BrowserAccessibilityManagerTest, TestNoLeaks) {
- // Create AccessibilityNodeData objects for a simple document tree,
+ // Create ui::AXNodeData objects for a simple document tree,
// representing the accessibility information used to initialize
// BrowserAccessibilityManager.
- AccessibilityNodeData button;
+ ui::AXNodeData button;
button.id = 2;
button.SetName("Button");
- button.role = blink::WebAXRoleButton;
+ button.role = ui::AX_ROLE_BUTTON;
button.state = 0;
- AccessibilityNodeData checkbox;
+ ui::AXNodeData checkbox;
checkbox.id = 3;
checkbox.SetName("Checkbox");
- checkbox.role = blink::WebAXRoleCheckBox;
+ checkbox.role = ui::AX_ROLE_CHECK_BOX;
checkbox.state = 0;
- AccessibilityNodeData root;
+ ui::AXNodeData root;
root.id = 1;
root.SetName("Document");
- root.role = blink::WebAXRoleRootWebArea;
+ root.role = ui::AX_ROLE_ROOT_WEB_AREA;
root.state = 0;
root.child_ids.push_back(2);
root.child_ids.push_back(3);
// Construct a BrowserAccessibilityManager with this
- // AccessibilityNodeData tree and a factory for an instance-counting
+ // ui::AXNodeData tree and a factory for an instance-counting
// BrowserAccessibility, and ensure that exactly 3 instances were
// created. Note that the manager takes ownership of the factory.
CountedBrowserAccessibility::global_obj_count_ = 0;
BrowserAccessibilityManager* manager =
BrowserAccessibilityManager::Create(
- root,
+ MakeAXTreeUpdate(root, button, checkbox),
NULL,
new CountedBrowserAccessibilityFactory());
- manager->UpdateNodesForTesting(button, checkbox);
ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_);
@@ -132,10 +145,9 @@ TEST(BrowserAccessibilityManagerTest, TestNoLeaks) {
// the three nodes in the tree.
manager =
BrowserAccessibilityManager::Create(
- root,
+ MakeAXTreeUpdate(root, button, checkbox),
NULL,
new CountedBrowserAccessibilityFactory());
- manager->UpdateNodesForTesting(button, checkbox);
ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_);
CountedBrowserAccessibility* root_accessible =
@@ -169,28 +181,28 @@ TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects) {
// child2
// child3
- AccessibilityNodeData tree1_child1;
+ ui::AXNodeData tree1_child1;
tree1_child1.id = 2;
tree1_child1.SetName("Child1");
- tree1_child1.role = blink::WebAXRoleButton;
+ tree1_child1.role = ui::AX_ROLE_BUTTON;
tree1_child1.state = 0;
- AccessibilityNodeData tree1_child2;
+ ui::AXNodeData tree1_child2;
tree1_child2.id = 3;
tree1_child2.SetName("Child2");
- tree1_child2.role = blink::WebAXRoleButton;
+ tree1_child2.role = ui::AX_ROLE_BUTTON;
tree1_child2.state = 0;
- AccessibilityNodeData tree1_child3;
+ ui::AXNodeData tree1_child3;
tree1_child3.id = 4;
tree1_child3.SetName("Child3");
- tree1_child3.role = blink::WebAXRoleButton;
+ tree1_child3.role = ui::AX_ROLE_BUTTON;
tree1_child3.state = 0;
- AccessibilityNodeData tree1_root;
+ ui::AXNodeData tree1_root;
tree1_root.id = 1;
tree1_root.SetName("Document");
- tree1_root.role = blink::WebAXRoleRootWebArea;
+ tree1_root.role = ui::AX_ROLE_ROOT_WEB_AREA;
tree1_root.state = 0;
tree1_root.child_ids.push_back(2);
tree1_root.child_ids.push_back(3);
@@ -204,16 +216,16 @@ TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects) {
// child2
// <-- child3 deleted
- AccessibilityNodeData tree2_child0;
+ ui::AXNodeData tree2_child0;
tree2_child0.id = 5;
tree2_child0.SetName("Child0");
- tree2_child0.role = blink::WebAXRoleButton;
+ tree2_child0.role = ui::AX_ROLE_BUTTON;
tree2_child0.state = 0;
- AccessibilityNodeData tree2_root;
+ ui::AXNodeData tree2_root;
tree2_root.id = 1;
tree2_root.SetName("DocumentChanged");
- tree2_root.role = blink::WebAXRoleRootWebArea;
+ tree2_root.role = ui::AX_ROLE_ROOT_WEB_AREA;
tree2_root.state = 0;
tree2_root.child_ids.push_back(5);
tree2_root.child_ids.push_back(2);
@@ -223,10 +235,10 @@ TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects) {
CountedBrowserAccessibility::global_obj_count_ = 0;
BrowserAccessibilityManager* manager =
BrowserAccessibilityManager::Create(
- tree1_root,
+ MakeAXTreeUpdate(tree1_root,
+ tree1_child1, tree1_child2, tree1_child3),
NULL,
new CountedBrowserAccessibilityFactory());
- manager->UpdateNodesForTesting(tree1_child1, tree1_child2, tree1_child3);
ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
// Save references to all of the objects.
@@ -247,17 +259,17 @@ TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects) {
child3_accessible->NativeAddReference();
// Check the index in parent.
- EXPECT_EQ(0, child1_accessible->index_in_parent());
- EXPECT_EQ(1, child2_accessible->index_in_parent());
- EXPECT_EQ(2, child3_accessible->index_in_parent());
+ EXPECT_EQ(0, child1_accessible->GetIndexInParent());
+ EXPECT_EQ(1, child2_accessible->GetIndexInParent());
+ EXPECT_EQ(2, child3_accessible->GetIndexInParent());
// Process a notification containing the changed subtree.
std::vector<AccessibilityHostMsg_EventParams> params;
params.push_back(AccessibilityHostMsg_EventParams());
AccessibilityHostMsg_EventParams* msg = &params[0];
- msg->event_type = blink::WebAXEventChildrenChanged;
- msg->nodes.push_back(tree2_root);
- msg->nodes.push_back(tree2_child0);
+ msg->event_type = ui::AX_EVENT_CHILDREN_CHANGED;
+ msg->update.nodes.push_back(tree2_root);
+ msg->update.nodes.push_back(tree2_child0);
msg->id = tree2_root.id;
manager->OnAccessibilityEvents(params);
@@ -273,8 +285,8 @@ TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects) {
EXPECT_FALSE(child3_accessible->instance_active());
// Check that the index in parent has been updated.
- EXPECT_EQ(1, child1_accessible->index_in_parent());
- EXPECT_EQ(2, child2_accessible->index_in_parent());
+ EXPECT_EQ(1, child1_accessible->GetIndexInParent());
+ EXPECT_EQ(2, child2_accessible->GetIndexInParent());
// Release our references. The object count should only decrease by 1
// for child3.
@@ -304,58 +316,58 @@ TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects2) {
// child3
// grandchild3
- AccessibilityNodeData tree1_grandchild1;
+ ui::AXNodeData tree1_grandchild1;
tree1_grandchild1.id = 4;
tree1_grandchild1.SetName("GrandChild1");
- tree1_grandchild1.role = blink::WebAXRoleButton;
+ tree1_grandchild1.role = ui::AX_ROLE_BUTTON;
tree1_grandchild1.state = 0;
- AccessibilityNodeData tree1_child1;
+ ui::AXNodeData tree1_child1;
tree1_child1.id = 3;
tree1_child1.SetName("Child1");
- tree1_child1.role = blink::WebAXRoleButton;
+ tree1_child1.role = ui::AX_ROLE_BUTTON;
tree1_child1.state = 0;
tree1_child1.child_ids.push_back(4);
- AccessibilityNodeData tree1_grandchild2;
+ ui::AXNodeData tree1_grandchild2;
tree1_grandchild2.id = 6;
tree1_grandchild2.SetName("GrandChild1");
- tree1_grandchild2.role = blink::WebAXRoleButton;
+ tree1_grandchild2.role = ui::AX_ROLE_BUTTON;
tree1_grandchild2.state = 0;
- AccessibilityNodeData tree1_child2;
+ ui::AXNodeData tree1_child2;
tree1_child2.id = 5;
tree1_child2.SetName("Child2");
- tree1_child2.role = blink::WebAXRoleButton;
+ tree1_child2.role = ui::AX_ROLE_BUTTON;
tree1_child2.state = 0;
tree1_child2.child_ids.push_back(6);
- AccessibilityNodeData tree1_grandchild3;
+ ui::AXNodeData tree1_grandchild3;
tree1_grandchild3.id = 8;
tree1_grandchild3.SetName("GrandChild3");
- tree1_grandchild3.role = blink::WebAXRoleButton;
+ tree1_grandchild3.role = ui::AX_ROLE_BUTTON;
tree1_grandchild3.state = 0;
- AccessibilityNodeData tree1_child3;
+ ui::AXNodeData tree1_child3;
tree1_child3.id = 7;
tree1_child3.SetName("Child3");
- tree1_child3.role = blink::WebAXRoleButton;
+ tree1_child3.role = ui::AX_ROLE_BUTTON;
tree1_child3.state = 0;
tree1_child3.child_ids.push_back(8);
- AccessibilityNodeData tree1_container;
+ ui::AXNodeData tree1_container;
tree1_container.id = 2;
tree1_container.SetName("Container");
- tree1_container.role = blink::WebAXRoleGroup;
+ tree1_container.role = ui::AX_ROLE_GROUP;
tree1_container.state = 0;
tree1_container.child_ids.push_back(3);
tree1_container.child_ids.push_back(5);
tree1_container.child_ids.push_back(7);
- AccessibilityNodeData tree1_root;
+ ui::AXNodeData tree1_root;
tree1_root.id = 1;
tree1_root.SetName("Document");
- tree1_root.role = blink::WebAXRoleRootWebArea;
+ tree1_root.role = ui::AX_ROLE_ROOT_WEB_AREA;
tree1_root.state = 0;
tree1_root.child_ids.push_back(2);
@@ -371,23 +383,23 @@ TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects2) {
// grandchild2
// <-- child3 (and grandchild3) deleted
- AccessibilityNodeData tree2_grandchild0;
+ ui::AXNodeData tree2_grandchild0;
tree2_grandchild0.id = 9;
tree2_grandchild0.SetName("GrandChild0");
- tree2_grandchild0.role = blink::WebAXRoleButton;
+ tree2_grandchild0.role = ui::AX_ROLE_BUTTON;
tree2_grandchild0.state = 0;
- AccessibilityNodeData tree2_child0;
+ ui::AXNodeData tree2_child0;
tree2_child0.id = 10;
tree2_child0.SetName("Child0");
- tree2_child0.role = blink::WebAXRoleButton;
+ tree2_child0.role = ui::AX_ROLE_BUTTON;
tree2_child0.state = 0;
tree2_child0.child_ids.push_back(9);
- AccessibilityNodeData tree2_container;
+ ui::AXNodeData tree2_container;
tree2_container.id = 2;
tree2_container.SetName("Container");
- tree2_container.role = blink::WebAXRoleGroup;
+ tree2_container.role = ui::AX_ROLE_GROUP;
tree2_container.state = 0;
tree2_container.child_ids.push_back(10);
tree2_container.child_ids.push_back(3);
@@ -397,13 +409,12 @@ TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects2) {
CountedBrowserAccessibility::global_obj_count_ = 0;
BrowserAccessibilityManager* manager =
BrowserAccessibilityManager::Create(
- tree1_root,
+ MakeAXTreeUpdate(tree1_root, tree1_container,
+ tree1_child1, tree1_grandchild1,
+ tree1_child2, tree1_grandchild2,
+ tree1_child3, tree1_grandchild3),
NULL,
new CountedBrowserAccessibilityFactory());
- manager->UpdateNodesForTesting(tree1_container,
- tree1_child1, tree1_grandchild1,
- tree1_child2, tree1_grandchild2,
- tree1_child3, tree1_grandchild3);
ASSERT_EQ(8, CountedBrowserAccessibility::global_obj_count_);
// Save references to some objects.
@@ -424,18 +435,18 @@ TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects2) {
child3_accessible->NativeAddReference();
// Check the index in parent.
- EXPECT_EQ(1, child2_accessible->index_in_parent());
- EXPECT_EQ(2, child3_accessible->index_in_parent());
+ EXPECT_EQ(1, child2_accessible->GetIndexInParent());
+ EXPECT_EQ(2, child3_accessible->GetIndexInParent());
// Process a notification containing the changed subtree rooted at
// the container.
std::vector<AccessibilityHostMsg_EventParams> params;
params.push_back(AccessibilityHostMsg_EventParams());
AccessibilityHostMsg_EventParams* msg = &params[0];
- msg->event_type = blink::WebAXEventChildrenChanged;
- msg->nodes.push_back(tree2_container);
- msg->nodes.push_back(tree2_child0);
- msg->nodes.push_back(tree2_grandchild0);
+ msg->event_type = ui::AX_EVENT_CHILDREN_CHANGED;
+ msg->update.nodes.push_back(tree2_container);
+ msg->update.nodes.push_back(tree2_child0);
+ msg->update.nodes.push_back(tree2_grandchild0);
msg->id = tree2_container.id;
manager->OnAccessibilityEvents(params);
@@ -451,11 +462,11 @@ TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects2) {
EXPECT_FALSE(child3_accessible->instance_active());
// Ensure that we retain the parent of the detached subtree.
- EXPECT_EQ(root_accessible, container_accessible->parent());
- EXPECT_EQ(0, container_accessible->index_in_parent());
+ EXPECT_EQ(root_accessible, container_accessible->GetParent());
+ EXPECT_EQ(0, container_accessible->GetIndexInParent());
// Check that the index in parent has been updated.
- EXPECT_EQ(2, child2_accessible->index_in_parent());
+ EXPECT_EQ(2, child2_accessible->GetIndexInParent());
// Release our references. The object count should only decrease by 1
// for child3.
@@ -479,22 +490,22 @@ TEST(BrowserAccessibilityManagerTest, TestMoveChildUp) {
// 3
// 4
- AccessibilityNodeData tree1_4;
+ ui::AXNodeData tree1_4;
tree1_4.id = 4;
tree1_4.state = 0;
- AccessibilityNodeData tree1_3;
+ ui::AXNodeData tree1_3;
tree1_3.id = 3;
tree1_3.state = 0;
tree1_3.child_ids.push_back(4);
- AccessibilityNodeData tree1_2;
+ ui::AXNodeData tree1_2;
tree1_2.id = 2;
tree1_2.state = 0;
- AccessibilityNodeData tree1_1;
+ ui::AXNodeData tree1_1;
tree1_1.id = 1;
- tree1_1.role = blink::WebAXRoleRootWebArea;
+ tree1_1.role = ui::AX_ROLE_ROOT_WEB_AREA;
tree1_1.state = 0;
tree1_1.child_ids.push_back(2);
tree1_1.child_ids.push_back(3);
@@ -506,20 +517,20 @@ TEST(BrowserAccessibilityManagerTest, TestMoveChildUp) {
// 6 <-- new
// 5 <-- new
- AccessibilityNodeData tree2_6;
+ ui::AXNodeData tree2_6;
tree2_6.id = 6;
tree2_6.state = 0;
- AccessibilityNodeData tree2_5;
+ ui::AXNodeData tree2_5;
tree2_5.id = 5;
tree2_5.state = 0;
- AccessibilityNodeData tree2_4;
+ ui::AXNodeData tree2_4;
tree2_4.id = 4;
tree2_4.state = 0;
tree2_4.child_ids.push_back(6);
- AccessibilityNodeData tree2_1;
+ ui::AXNodeData tree2_1;
tree2_1.id = 1;
tree2_1.state = 0;
tree2_1.child_ids.push_back(4);
@@ -529,21 +540,20 @@ TEST(BrowserAccessibilityManagerTest, TestMoveChildUp) {
CountedBrowserAccessibility::global_obj_count_ = 0;
BrowserAccessibilityManager* manager =
BrowserAccessibilityManager::Create(
- tree1_1,
+ MakeAXTreeUpdate(tree1_1, tree1_2, tree1_3, tree1_4),
NULL,
new CountedBrowserAccessibilityFactory());
- manager->UpdateNodesForTesting(tree1_2, tree1_3, tree1_4);
ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
// Process a notification containing the changed subtree.
std::vector<AccessibilityHostMsg_EventParams> params;
params.push_back(AccessibilityHostMsg_EventParams());
AccessibilityHostMsg_EventParams* msg = &params[0];
- msg->event_type = blink::WebAXEventChildrenChanged;
- msg->nodes.push_back(tree2_1);
- msg->nodes.push_back(tree2_4);
- msg->nodes.push_back(tree2_5);
- msg->nodes.push_back(tree2_6);
+ msg->event_type = ui::AX_EVENT_CHILDREN_CHANGED;
+ msg->update.nodes.push_back(tree2_1);
+ msg->update.nodes.push_back(tree2_4);
+ msg->update.nodes.push_back(tree2_5);
+ msg->update.nodes.push_back(tree2_6);
msg->id = tree2_1.id;
manager->OnAccessibilityEvents(params);
@@ -555,20 +565,14 @@ TEST(BrowserAccessibilityManagerTest, TestMoveChildUp) {
ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
}
-// Crashes on Windows. http://crbug.com/304130
-#if defined(OS_WIN)
-#define MAYBE_TestFatalError DISABLED_TestFatalError
-#else
-#define MAYBE_TestFatalError TestFatalError
-#endif
TEST(BrowserAccessibilityManagerTest, TestFatalError) {
// Test that BrowserAccessibilityManager raises a fatal error
// (which will crash the renderer) if the same id is used in
// two places in the tree.
- AccessibilityNodeData root;
+ ui::AXNodeData root;
root.id = 1;
- root.role = blink::WebAXRoleRootWebArea;
+ root.role = ui::AX_ROLE_ROOT_WEB_AREA;
root.child_ids.push_back(2);
root.child_ids.push_back(2);
@@ -579,57 +583,65 @@ TEST(BrowserAccessibilityManagerTest, TestFatalError) {
scoped_ptr<BrowserAccessibilityManager> manager;
ASSERT_FALSE(delegate->got_fatal_error());
manager.reset(BrowserAccessibilityManager::Create(
- root,
+ MakeAXTreeUpdate(root),
delegate.get(),
factory));
ASSERT_TRUE(delegate->got_fatal_error());
- AccessibilityNodeData root2;
+ ui::AXNodeData root2;
root2.id = 1;
- root2.role = blink::WebAXRoleRootWebArea;
+ root2.role = ui::AX_ROLE_ROOT_WEB_AREA;
root2.child_ids.push_back(2);
root2.child_ids.push_back(3);
- AccessibilityNodeData child1;
+ ui::AXNodeData child1;
child1.id = 2;
child1.child_ids.push_back(4);
child1.child_ids.push_back(5);
- AccessibilityNodeData child2;
+ ui::AXNodeData child2;
child2.id = 3;
child2.child_ids.push_back(6);
child2.child_ids.push_back(5); // Duplicate
+ ui::AXNodeData grandchild4;
+ grandchild4.id = 4;
+
+ ui::AXNodeData grandchild5;
+ grandchild5.id = 5;
+
+ ui::AXNodeData grandchild6;
+ grandchild6.id = 6;
+
delegate->reset_got_fatal_error();
factory = new CountedBrowserAccessibilityFactory();
manager.reset(BrowserAccessibilityManager::Create(
- root2,
+ MakeAXTreeUpdate(root2, child1, child2,
+ grandchild4, grandchild5, grandchild6),
delegate.get(),
factory));
- ASSERT_FALSE(delegate->got_fatal_error());
- manager->UpdateNodesForTesting(child1, child2);
ASSERT_TRUE(delegate->got_fatal_error());
}
TEST(BrowserAccessibilityManagerTest, BoundsForRange) {
- AccessibilityNodeData root;
+ ui::AXNodeData root;
root.id = 1;
- root.role = blink::WebAXRoleRootWebArea;
+ root.role = ui::AX_ROLE_ROOT_WEB_AREA;
- AccessibilityNodeData static_text;
+ ui::AXNodeData static_text;
static_text.id = 2;
static_text.SetValue("Hello, world.");
- static_text.role = blink::WebAXRoleStaticText;
+ static_text.role = ui::AX_ROLE_STATIC_TEXT;
static_text.location = gfx::Rect(100, 100, 29, 18);
root.child_ids.push_back(2);
- AccessibilityNodeData inline_text1;
+ ui::AXNodeData inline_text1;
inline_text1.id = 3;
inline_text1.SetValue("Hello, ");
- inline_text1.role = blink::WebAXRoleInlineTextBox;
+ inline_text1.role = ui::AX_ROLE_INLINE_TEXT_BOX;
inline_text1.location = gfx::Rect(100, 100, 29, 9);
- inline_text1.AddIntAttribute(AccessibilityNodeData::ATTR_TEXT_DIRECTION,
- blink::WebAXTextDirectionLR);
+ inline_text1.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
+ ui::AX_TEXT_DIRECTION_LR);
std::vector<int32> character_offsets1;
character_offsets1.push_back(6); // 0
character_offsets1.push_back(11); // 1
@@ -639,16 +651,16 @@ TEST(BrowserAccessibilityManagerTest, BoundsForRange) {
character_offsets1.push_back(29); // 5
character_offsets1.push_back(29); // 6 (note that the space has no width)
inline_text1.AddIntListAttribute(
- AccessibilityNodeData::ATTR_CHARACTER_OFFSETS, character_offsets1);
+ ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets1);
static_text.child_ids.push_back(3);
- AccessibilityNodeData inline_text2;
+ ui::AXNodeData inline_text2;
inline_text2.id = 4;
inline_text2.SetValue("world.");
- inline_text2.role = blink::WebAXRoleInlineTextBox;
+ inline_text2.role = ui::AX_ROLE_INLINE_TEXT_BOX;
inline_text2.location = gfx::Rect(100, 109, 28, 9);
- inline_text2.AddIntAttribute(AccessibilityNodeData::ATTR_TEXT_DIRECTION,
- blink::WebAXTextDirectionLR);
+ inline_text2.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
+ ui::AX_TEXT_DIRECTION_LR);
std::vector<int32> character_offsets2;
character_offsets2.push_back(5);
character_offsets2.push_back(10);
@@ -657,15 +669,14 @@ TEST(BrowserAccessibilityManagerTest, BoundsForRange) {
character_offsets2.push_back(25);
character_offsets2.push_back(28);
inline_text2.AddIntListAttribute(
- AccessibilityNodeData::ATTR_CHARACTER_OFFSETS, character_offsets2);
+ ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets2);
static_text.child_ids.push_back(4);
scoped_ptr<BrowserAccessibilityManager> manager(
BrowserAccessibilityManager::Create(
- root,
+ MakeAXTreeUpdate(root, static_text, inline_text1, inline_text2),
NULL,
new CountedBrowserAccessibilityFactory()));
- manager->UpdateNodesForTesting(static_text, inline_text1, inline_text2);
BrowserAccessibility* root_accessible = manager->GetRoot();
BrowserAccessibility* static_text_accessible =
@@ -692,6 +703,11 @@ TEST(BrowserAccessibilityManagerTest, BoundsForRange) {
// Test range that's beyond the text.
EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
static_text_accessible->GetLocalBoundsForRange(-1, 999).ToString());
+
+ // Test that we can call bounds for range on the parent element, too,
+ // and it still works.
+ EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
+ root_accessible->GetLocalBoundsForRange(0, 13).ToString());
}
TEST(BrowserAccessibilityManagerTest, BoundsForRangeBiDi) {
@@ -703,53 +719,52 @@ TEST(BrowserAccessibilityManagerTest, BoundsForRangeBiDi) {
// tests that if something like that were to occur, GetLocalBoundsForRange
// returns the correct bounds for different ranges.
- AccessibilityNodeData root;
+ ui::AXNodeData root;
root.id = 1;
- root.role = blink::WebAXRoleRootWebArea;
+ root.role = ui::AX_ROLE_ROOT_WEB_AREA;
- AccessibilityNodeData static_text;
+ ui::AXNodeData static_text;
static_text.id = 2;
static_text.SetValue("123abc");
- static_text.role = blink::WebAXRoleStaticText;
+ static_text.role = ui::AX_ROLE_STATIC_TEXT;
static_text.location = gfx::Rect(100, 100, 60, 20);
root.child_ids.push_back(2);
- AccessibilityNodeData inline_text1;
+ ui::AXNodeData inline_text1;
inline_text1.id = 3;
inline_text1.SetValue("123");
- inline_text1.role = blink::WebAXRoleInlineTextBox;
+ inline_text1.role = ui::AX_ROLE_INLINE_TEXT_BOX;
inline_text1.location = gfx::Rect(100, 100, 30, 20);
- inline_text1.AddIntAttribute(AccessibilityNodeData::ATTR_TEXT_DIRECTION,
- blink::WebAXTextDirectionLR);
+ inline_text1.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
+ ui::AX_TEXT_DIRECTION_LR);
std::vector<int32> character_offsets1;
character_offsets1.push_back(10); // 0
character_offsets1.push_back(20); // 1
character_offsets1.push_back(30); // 2
inline_text1.AddIntListAttribute(
- AccessibilityNodeData::ATTR_CHARACTER_OFFSETS, character_offsets1);
+ ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets1);
static_text.child_ids.push_back(3);
- AccessibilityNodeData inline_text2;
+ ui::AXNodeData inline_text2;
inline_text2.id = 4;
inline_text2.SetValue("abc");
- inline_text2.role = blink::WebAXRoleInlineTextBox;
+ inline_text2.role = ui::AX_ROLE_INLINE_TEXT_BOX;
inline_text2.location = gfx::Rect(130, 100, 30, 20);
- inline_text2.AddIntAttribute(AccessibilityNodeData::ATTR_TEXT_DIRECTION,
- blink::WebAXTextDirectionRL);
+ inline_text2.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
+ ui::AX_TEXT_DIRECTION_RL);
std::vector<int32> character_offsets2;
character_offsets2.push_back(10);
character_offsets2.push_back(20);
character_offsets2.push_back(30);
inline_text2.AddIntListAttribute(
- AccessibilityNodeData::ATTR_CHARACTER_OFFSETS, character_offsets2);
+ ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets2);
static_text.child_ids.push_back(4);
scoped_ptr<BrowserAccessibilityManager> manager(
BrowserAccessibilityManager::Create(
- root,
+ MakeAXTreeUpdate(root, static_text, inline_text1, inline_text2),
NULL,
new CountedBrowserAccessibilityFactory()));
- manager->UpdateNodesForTesting(static_text, inline_text1, inline_text2);
BrowserAccessibility* root_accessible = manager->GetRoot();
BrowserAccessibility* static_text_accessible =
@@ -776,4 +791,145 @@ TEST(BrowserAccessibilityManagerTest, BoundsForRangeBiDi) {
static_text_accessible->GetLocalBoundsForRange(2, 2).ToString());
}
+#if defined(OS_WIN)
+#define MAYBE_BoundsForRangeOnParentElement \
+ DISABLED_BoundsForRangeOnParentElement
+#else
+#define MAYBE_BoundsForRangeOnParentElement BoundsForRangeOnParentElement
+#endif
+TEST(BrowserAccessibilityManagerTest, MAYBE_BoundsForRangeOnParentElement) {
+ ui::AXNodeData root;
+ root.id = 1;
+ root.role = ui::AX_ROLE_ROOT_WEB_AREA;
+ root.child_ids.push_back(2);
+
+ ui::AXNodeData div;
+ div.id = 2;
+ div.role = ui::AX_ROLE_DIV;
+ div.location = gfx::Rect(100, 100, 100, 20);
+ div.child_ids.push_back(3);
+ div.child_ids.push_back(4);
+ div.child_ids.push_back(5);
+
+ ui::AXNodeData static_text1;
+ static_text1.id = 3;
+ static_text1.SetValue("AB");
+ static_text1.role = ui::AX_ROLE_STATIC_TEXT;
+ static_text1.location = gfx::Rect(100, 100, 40, 20);
+ static_text1.child_ids.push_back(6);
+
+ ui::AXNodeData img;
+ img.id = 4;
+ img.role = ui::AX_ROLE_IMAGE;
+ img.location = gfx::Rect(140, 100, 20, 20);
+
+ ui::AXNodeData static_text2;
+ static_text2.id = 5;
+ static_text2.SetValue("CD");
+ static_text2.role = ui::AX_ROLE_STATIC_TEXT;
+ static_text2.location = gfx::Rect(160, 100, 40, 20);
+ static_text2.child_ids.push_back(7);
+
+ ui::AXNodeData inline_text1;
+ inline_text1.id = 6;
+ inline_text1.SetValue("AB");
+ inline_text1.role = ui::AX_ROLE_INLINE_TEXT_BOX;
+ inline_text1.location = gfx::Rect(100, 100, 40, 20);
+ inline_text1.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
+ ui::AX_TEXT_DIRECTION_LR);
+ std::vector<int32> character_offsets1;
+ character_offsets1.push_back(20); // 0
+ character_offsets1.push_back(40); // 1
+ inline_text1.AddIntListAttribute(
+ ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets1);
+
+ ui::AXNodeData inline_text2;
+ inline_text2.id = 7;
+ inline_text2.SetValue("CD");
+ inline_text2.role = ui::AX_ROLE_INLINE_TEXT_BOX;
+ inline_text2.location = gfx::Rect(160, 100, 40, 20);
+ inline_text2.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
+ ui::AX_TEXT_DIRECTION_LR);
+ std::vector<int32> character_offsets2;
+ character_offsets2.push_back(20); // 0
+ character_offsets2.push_back(40); // 1
+ inline_text2.AddIntListAttribute(
+ ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets2);
+
+ scoped_ptr<BrowserAccessibilityManager> manager(
+ BrowserAccessibilityManager::Create(
+ MakeAXTreeUpdate(
+ root, div, static_text1, img,
+ static_text2, inline_text1, inline_text2),
+ NULL,
+ new CountedBrowserAccessibilityFactory()));
+ BrowserAccessibility* root_accessible = manager->GetRoot();
+
+ EXPECT_EQ(gfx::Rect(100, 100, 20, 20).ToString(),
+ root_accessible->GetLocalBoundsForRange(0, 1).ToString());
+
+ EXPECT_EQ(gfx::Rect(100, 100, 40, 20).ToString(),
+ root_accessible->GetLocalBoundsForRange(0, 2).ToString());
+
+ EXPECT_EQ(gfx::Rect(100, 100, 80, 20).ToString(),
+ root_accessible->GetLocalBoundsForRange(0, 3).ToString());
+
+ EXPECT_EQ(gfx::Rect(120, 100, 60, 20).ToString(),
+ root_accessible->GetLocalBoundsForRange(1, 2).ToString());
+
+ EXPECT_EQ(gfx::Rect(120, 100, 80, 20).ToString(),
+ root_accessible->GetLocalBoundsForRange(1, 3).ToString());
+
+ EXPECT_EQ(gfx::Rect(100, 100, 100, 20).ToString(),
+ root_accessible->GetLocalBoundsForRange(0, 4).ToString());
+}
+
+TEST(BrowserAccessibilityManagerTest, NextPreviousInTreeOrder) {
+ ui::AXNodeData root;
+ root.id = 1;
+ root.role = ui::AX_ROLE_ROOT_WEB_AREA;
+
+ ui::AXNodeData node2;
+ node2.id = 2;
+ root.child_ids.push_back(2);
+
+ ui::AXNodeData node3;
+ node3.id = 3;
+ root.child_ids.push_back(3);
+
+ ui::AXNodeData node4;
+ node4.id = 4;
+ node3.child_ids.push_back(4);
+
+ ui::AXNodeData node5;
+ node5.id = 5;
+ root.child_ids.push_back(5);
+
+ scoped_ptr<BrowserAccessibilityManager> manager(
+ BrowserAccessibilityManager::Create(
+ MakeAXTreeUpdate(root, node2, node3, node4, node5),
+ NULL,
+ new CountedBrowserAccessibilityFactory()));
+
+ BrowserAccessibility* root_accessible = manager->GetRoot();
+ BrowserAccessibility* node2_accessible = root_accessible->PlatformGetChild(0);
+ BrowserAccessibility* node3_accessible = root_accessible->PlatformGetChild(1);
+ BrowserAccessibility* node4_accessible =
+ node3_accessible->PlatformGetChild(0);
+ BrowserAccessibility* node5_accessible = root_accessible->PlatformGetChild(2);
+
+ ASSERT_EQ(NULL, manager->NextInTreeOrder(NULL));
+ ASSERT_EQ(node2_accessible, manager->NextInTreeOrder(root_accessible));
+ ASSERT_EQ(node3_accessible, manager->NextInTreeOrder(node2_accessible));
+ ASSERT_EQ(node4_accessible, manager->NextInTreeOrder(node3_accessible));
+ ASSERT_EQ(node5_accessible, manager->NextInTreeOrder(node4_accessible));
+ ASSERT_EQ(NULL, manager->NextInTreeOrder(node5_accessible));
+
+ ASSERT_EQ(NULL, manager->PreviousInTreeOrder(NULL));
+ ASSERT_EQ(node4_accessible, manager->PreviousInTreeOrder(node5_accessible));
+ ASSERT_EQ(node3_accessible, manager->PreviousInTreeOrder(node4_accessible));
+ ASSERT_EQ(node2_accessible, manager->PreviousInTreeOrder(node3_accessible));
+ ASSERT_EQ(root_accessible, manager->PreviousInTreeOrder(node2_accessible));
+}
+
} // 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 4e5f8dc9abc..4e7153ab75a 100644
--- a/chromium/content/browser/accessibility/browser_accessibility_manager_win.cc
+++ b/chromium/content/browser/accessibility/browser_accessibility_manager_win.cc
@@ -4,104 +4,25 @@
#include "content/browser/accessibility/browser_accessibility_manager_win.h"
-#include <atlbase.h>
-#include <atlapp.h>
-#include <atlcom.h>
-#include <atlcrack.h>
-#include <oleacc.h>
-
#include "base/command_line.h"
#include "base/win/scoped_comptr.h"
#include "base/win/windows_version.h"
#include "content/browser/accessibility/browser_accessibility_state_impl.h"
#include "content/browser/accessibility/browser_accessibility_win.h"
+#include "content/browser/renderer_host/legacy_render_widget_host_win.h"
#include "content/common/accessibility_messages.h"
+#include "ui/base/win/atl_module.h"
namespace content {
-// Some screen readers expect every tab / every unique web content container
-// to be in its own HWND, like it was before Aura, but with Aura there's just
-// one main HWND for a frame, or even for the whole desktop. So, we need a
-// fake HWND as the root of the accessibility tree for each tab.
-// We should get rid of this code when the latest two versions of all
-// supported screen readers no longer make this assumption.
-//
-// This class implements a child HWND with zero size, that delegates its
-// accessibility implementation to the root of the BrowserAccessibilityManager
-// tree. This HWND is hooked up as the parent of the root object in the
-// BrowserAccessibilityManager tree, so when any accessibility client
-// calls ::WindowFromAccessibleObject, they get this HWND instead of the
-// DesktopRootWindowHostWin.
-class AccessibleHWND
- : public ATL::CWindowImpl<AccessibleHWND,
- ATL::CWindow,
- ATL::CWinTraits<WS_CHILD> > {
- public:
- // Unfortunately, some screen readers look for this exact window class
- // to enable certain features. It'd be great to remove this.
- DECLARE_WND_CLASS_EX(L"Chrome_RenderWidgetHostHWND", CS_DBLCLKS, 0);
-
- BEGIN_MSG_MAP_EX(AccessibleHWND)
- MESSAGE_HANDLER_EX(WM_GETOBJECT, OnGetObject)
- END_MSG_MAP()
-
- AccessibleHWND(HWND parent, BrowserAccessibilityManagerWin* manager)
- : manager_(manager) {
- Create(parent);
- ShowWindow(true);
- MoveWindow(0, 0, 0, 0);
-
- HRESULT hr = ::CreateStdAccessibleObject(
- hwnd(), OBJID_WINDOW, IID_IAccessible,
- reinterpret_cast<void **>(window_accessible_.Receive()));
- DCHECK(SUCCEEDED(hr));
- }
-
- HWND hwnd() {
- DCHECK(::IsWindow(m_hWnd));
- return m_hWnd;
- }
-
- IAccessible* window_accessible() { return window_accessible_; }
-
- void OnManagerDeleted() {
- manager_ = NULL;
- }
-
- protected:
- virtual void OnFinalMessage(HWND hwnd) OVERRIDE {
- if (manager_)
- manager_->OnAccessibleHwndDeleted();
- delete this;
- }
-
- private:
- LRESULT OnGetObject(UINT message,
- WPARAM w_param,
- LPARAM l_param) {
- if (OBJID_CLIENT != l_param || !manager_)
- return static_cast<LRESULT>(0L);
-
- base::win::ScopedComPtr<IAccessible> root(
- manager_->GetRoot()->ToBrowserAccessibilityWin());
- return LresultFromObject(IID_IAccessible, w_param,
- static_cast<IAccessible*>(root.Detach()));
- }
-
- BrowserAccessibilityManagerWin* manager_;
- base::win::ScopedComPtr<IAccessible> window_accessible_;
-
- DISALLOW_COPY_AND_ASSIGN(AccessibleHWND);
-};
-
-
// static
BrowserAccessibilityManager* BrowserAccessibilityManager::Create(
- const AccessibilityNodeData& src,
+ const ui::AXTreeUpdate& initial_tree,
BrowserAccessibilityDelegate* delegate,
BrowserAccessibilityFactory* factory) {
return new BrowserAccessibilityManagerWin(
- GetDesktopWindow(), NULL, src, delegate, factory);
+ content::LegacyRenderWidgetHostHWND::Create(GetDesktopWindow()).get(),
+ NULL, initial_tree, delegate, factory);
}
BrowserAccessibilityManagerWin*
@@ -110,18 +31,22 @@ BrowserAccessibilityManager::ToBrowserAccessibilityManagerWin() {
}
BrowserAccessibilityManagerWin::BrowserAccessibilityManagerWin(
- HWND parent_hwnd,
+ LegacyRenderWidgetHostHWND* accessible_hwnd,
IAccessible* parent_iaccessible,
- const AccessibilityNodeData& src,
+ const ui::AXTreeUpdate& initial_tree,
BrowserAccessibilityDelegate* delegate,
BrowserAccessibilityFactory* factory)
- : BrowserAccessibilityManager(src, delegate, factory),
- parent_hwnd_(parent_hwnd),
+ : BrowserAccessibilityManager(initial_tree, delegate, factory),
+ parent_hwnd_(NULL),
parent_iaccessible_(parent_iaccessible),
tracked_scroll_object_(NULL),
- is_chrome_frame_(
- CommandLine::ForCurrentProcess()->HasSwitch("chrome-frame")),
- accessible_hwnd_(NULL) {
+ accessible_hwnd_(accessible_hwnd),
+ focus_event_on_root_needed_(false) {
+ ui::win::CreateATLModuleIfNeeded();
+ if (accessible_hwnd_) {
+ accessible_hwnd_->set_browser_accessibility_manager(this);
+ parent_hwnd_ = accessible_hwnd_->GetParent();
+ }
}
BrowserAccessibilityManagerWin::~BrowserAccessibilityManagerWin() {
@@ -134,132 +59,192 @@ BrowserAccessibilityManagerWin::~BrowserAccessibilityManagerWin() {
}
// static
-AccessibilityNodeData BrowserAccessibilityManagerWin::GetEmptyDocument() {
- AccessibilityNodeData empty_document;
+ui::AXTreeUpdate BrowserAccessibilityManagerWin::GetEmptyDocument() {
+ ui::AXNodeData empty_document;
empty_document.id = 0;
- empty_document.role = blink::WebAXRoleRootWebArea;
+ empty_document.role = ui::AX_ROLE_ROOT_WEB_AREA;
empty_document.state =
- (1 << blink::WebAXStateEnabled) |
- (1 << blink::WebAXStateReadonly) |
- (1 << blink::WebAXStateBusy);
- return empty_document;
+ (1 << ui::AX_STATE_ENABLED) |
+ (1 << ui::AX_STATE_READ_ONLY) |
+ (1 << ui::AX_STATE_BUSY);
+
+ ui::AXTreeUpdate update;
+ update.nodes.push_back(empty_document);
+ return update;
+}
+
+void BrowserAccessibilityManagerWin::SetAccessibleHWND(
+ LegacyRenderWidgetHostHWND* accessible_hwnd) {
+ accessible_hwnd_ = accessible_hwnd;
+ if (accessible_hwnd_) {
+ accessible_hwnd_->set_browser_accessibility_manager(this);
+ parent_hwnd_ = accessible_hwnd_->GetParent();
+ }
}
void BrowserAccessibilityManagerWin::MaybeCallNotifyWinEvent(DWORD event,
LONG child_id) {
- // Don't fire events if this view isn't hooked up to its parent.
- if (!parent_iaccessible())
- return;
-
-#if defined(USE_AURA)
- // If this is an Aura build on Win 7 and complete accessibility is
- // enabled, create a fake child HWND to use as the root of the
- // accessibility tree. See comments above AccessibleHWND for details.
- if (BrowserAccessibilityStateImpl::GetInstance()->IsAccessibleBrowser() &&
- !is_chrome_frame_ &&
- !accessible_hwnd_) {
- accessible_hwnd_ = new AccessibleHWND(parent_hwnd_, this);
+ // If on Win 7 and complete accessibility is enabled, use the fake child HWND
+ // to use as the root of the accessibility tree. See comments above
+ // LegacyRenderWidgetHostHWND for details.
+ if (accessible_hwnd_ &&
+ BrowserAccessibilityStateImpl::GetInstance()->IsAccessibleBrowser()) {
parent_hwnd_ = accessible_hwnd_->hwnd();
parent_iaccessible_ = accessible_hwnd_->window_accessible();
}
-#endif
- ::NotifyWinEvent(event, parent_hwnd(), OBJID_CLIENT, child_id);
+ // Only fire events if this view is hooked up to its parent.
+ if (parent_iaccessible() && parent_hwnd())
+ ::NotifyWinEvent(event, parent_hwnd(), OBJID_CLIENT, child_id);
}
-void BrowserAccessibilityManagerWin::AddNodeToMap(BrowserAccessibility* node) {
- BrowserAccessibilityManager::AddNodeToMap(node);
- LONG unique_id_win = node->ToBrowserAccessibilityWin()->unique_id_win();
- unique_id_to_renderer_id_map_[unique_id_win] = node->renderer_id();
+
+void BrowserAccessibilityManagerWin::OnNodeCreated(ui::AXNode* node) {
+ BrowserAccessibilityManager::OnNodeCreated(node);
+ BrowserAccessibility* obj = GetFromAXNode(node);
+ LONG unique_id_win = obj->ToBrowserAccessibilityWin()->unique_id_win();
+ unique_id_to_ax_id_map_[unique_id_win] = obj->GetId();
}
-void BrowserAccessibilityManagerWin::RemoveNode(BrowserAccessibility* node) {
- unique_id_to_renderer_id_map_.erase(
- node->ToBrowserAccessibilityWin()->unique_id_win());
- BrowserAccessibilityManager::RemoveNode(node);
- if (node == tracked_scroll_object_) {
+void BrowserAccessibilityManagerWin::OnNodeWillBeDeleted(ui::AXNode* node) {
+ BrowserAccessibilityManager::OnNodeWillBeDeleted(node);
+ BrowserAccessibility* obj = GetFromAXNode(node);
+ if (!obj)
+ return;
+ unique_id_to_ax_id_map_.erase(
+ obj->ToBrowserAccessibilityWin()->unique_id_win());
+ if (obj == tracked_scroll_object_) {
tracked_scroll_object_->Release();
tracked_scroll_object_ = NULL;
}
}
+void BrowserAccessibilityManagerWin::OnWindowFocused() {
+ // 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 (!delegate_ || !delegate_->AccessibilityViewHasFocus())
+ 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_->GetRoot())
+ NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetRoot());
+ BrowserAccessibilityManager::OnWindowFocused();
+}
+
void BrowserAccessibilityManagerWin::NotifyAccessibilityEvent(
- blink::WebAXEvent event_type,
+ ui::AXEvent event_type,
BrowserAccessibility* node) {
- if (node->role() == blink::WebAXRoleInlineTextBox)
+ if (node->GetRole() == ui::AX_ROLE_INLINE_TEXT_BOX)
return;
+ // Don't fire focus, blur, or load complete notifications if the
+ // window isn't focused, because that can confuse screen readers into
+ // entering their "browse" mode.
+ if ((event_type == ui::AX_EVENT_FOCUS ||
+ event_type == ui::AX_EVENT_BLUR ||
+ event_type == ui::AX_EVENT_LOAD_COMPLETE) &&
+ (!delegate_ || !delegate_->AccessibilityViewHasFocus())) {
+ 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->GetBoolAttribute(ui::AX_ATTR_DOC_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 blink::WebAXEventActiveDescendantChanged:
+ case ui::AX_EVENT_ACTIVEDESCENDANTCHANGED:
event_id = IA2_EVENT_ACTIVE_DESCENDANT_CHANGED;
break;
- case blink::WebAXEventAlert:
+ case ui::AX_EVENT_ALERT:
event_id = EVENT_SYSTEM_ALERT;
break;
- case blink::WebAXEventAriaAttributeChanged:
+ case ui::AX_EVENT_ARIA_ATTRIBUTE_CHANGED:
event_id = IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED;
break;
- case blink::WebAXEventAutocorrectionOccured:
+ case ui::AX_EVENT_AUTOCORRECTION_OCCURED:
event_id = IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED;
break;
- case blink::WebAXEventBlur:
+ case ui::AX_EVENT_BLUR:
// Equivalent to focus on the root.
event_id = EVENT_OBJECT_FOCUS;
node = GetRoot();
break;
- case blink::WebAXEventCheckedStateChanged:
+ case ui::AX_EVENT_CHECKED_STATE_CHANGED:
event_id = EVENT_OBJECT_STATECHANGE;
break;
- case blink::WebAXEventChildrenChanged:
+ case ui::AX_EVENT_CHILDREN_CHANGED:
event_id = EVENT_OBJECT_REORDER;
break;
- case blink::WebAXEventFocus:
+ case ui::AX_EVENT_FOCUS:
event_id = EVENT_OBJECT_FOCUS;
break;
- case blink::WebAXEventInvalidStatusChanged:
+ case ui::AX_EVENT_INVALID_STATUS_CHANGED:
event_id = EVENT_OBJECT_STATECHANGE;
break;
- case blink::WebAXEventLiveRegionChanged:
- // TODO: try not firing a native notification at all, since
- // on Windows, each individual item in a live region that changes
- // already gets its own notification.
- event_id = EVENT_OBJECT_REORDER;
+ case ui::AX_EVENT_LIVE_REGION_CHANGED:
+ if (node->GetBoolAttribute(ui::AX_ATTR_CONTAINER_LIVE_BUSY))
+ return;
+ event_id = EVENT_OBJECT_LIVEREGIONCHANGED;
break;
- case blink::WebAXEventLoadComplete:
+ case ui::AX_EVENT_LOAD_COMPLETE:
event_id = IA2_EVENT_DOCUMENT_LOAD_COMPLETE;
break;
- case blink::WebAXEventMenuListItemSelected:
+ case ui::AX_EVENT_MENU_LIST_ITEM_SELECTED:
event_id = EVENT_OBJECT_FOCUS;
break;
- case blink::WebAXEventMenuListValueChanged:
+ case ui::AX_EVENT_MENU_LIST_VALUE_CHANGED:
event_id = EVENT_OBJECT_VALUECHANGE;
break;
- case blink::WebAXEventHide:
+ case ui::AX_EVENT_HIDE:
event_id = EVENT_OBJECT_HIDE;
break;
- case blink::WebAXEventShow:
+ case ui::AX_EVENT_SHOW:
event_id = EVENT_OBJECT_SHOW;
break;
- case blink::WebAXEventScrolledToAnchor:
+ case ui::AX_EVENT_SCROLL_POSITION_CHANGED:
+ event_id = EVENT_SYSTEM_SCROLLINGEND;
+ break;
+ case ui::AX_EVENT_SCROLLED_TO_ANCHOR:
event_id = EVENT_SYSTEM_SCROLLINGSTART;
break;
- case blink::WebAXEventSelectedChildrenChanged:
+ case ui::AX_EVENT_SELECTED_CHILDREN_CHANGED:
event_id = EVENT_OBJECT_SELECTIONWITHIN;
break;
- case blink::WebAXEventSelectedTextChanged:
+ case ui::AX_EVENT_SELECTED_TEXT_CHANGED:
event_id = IA2_EVENT_TEXT_CARET_MOVED;
break;
- case blink::WebAXEventTextChanged:
+ case ui::AX_EVENT_TEXT_CHANGED:
event_id = EVENT_OBJECT_NAMECHANGE;
break;
- case blink::WebAXEventTextInserted:
+ case ui::AX_EVENT_TEXT_INSERTED:
event_id = IA2_EVENT_TEXT_INSERTED;
break;
- case blink::WebAXEventTextRemoved:
+ case ui::AX_EVENT_TEXT_REMOVED:
event_id = IA2_EVENT_TEXT_REMOVED;
break;
- case blink::WebAXEventValueChanged:
+ case ui::AX_EVENT_VALUE_CHANGED:
event_id = EVENT_OBJECT_VALUECHANGE;
break;
default:
@@ -280,7 +265,7 @@ void BrowserAccessibilityManagerWin::NotifyAccessibilityEvent(
// If this is a layout complete notification (sent when a container scrolls)
// and there is a descendant tracked object, send a notification on it.
// TODO(dmazzoni): remove once http://crbug.com/113483 is fixed.
- if (event_type == blink::WebAXEventLayoutComplete &&
+ if (event_type == ui::AX_EVENT_LAYOUT_COMPLETE &&
tracked_scroll_object_ &&
tracked_scroll_object_->IsDescendantOf(node)) {
MaybeCallNotifyWinEvent(
@@ -291,6 +276,12 @@ void BrowserAccessibilityManagerWin::NotifyAccessibilityEvent(
}
}
+void BrowserAccessibilityManagerWin::OnRootChanged(ui::AXNode* new_root) {
+ // In order to make screen readers aware of the new accessibility root,
+ // we need to fire a focus event on it.
+ OnWindowFocused();
+}
+
void BrowserAccessibilityManagerWin::TrackScrollingObject(
BrowserAccessibilityWin* node) {
if (tracked_scroll_object_)
@@ -302,9 +293,9 @@ void BrowserAccessibilityManagerWin::TrackScrollingObject(
BrowserAccessibilityWin* BrowserAccessibilityManagerWin::GetFromUniqueIdWin(
LONG unique_id_win) {
base::hash_map<LONG, int32>::iterator iter =
- unique_id_to_renderer_id_map_.find(unique_id_win);
- if (iter != unique_id_to_renderer_id_map_.end()) {
- BrowserAccessibility* result = GetFromRendererID(iter->second);
+ unique_id_to_ax_id_map_.find(unique_id_win);
+ if (iter != unique_id_to_ax_id_map_.end()) {
+ BrowserAccessibility* result = GetFromID(iter->second);
if (result)
return result->ToBrowserAccessibilityWin();
}
diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_win.h b/chromium/content/browser/accessibility/browser_accessibility_manager_win.h
index cbfc877909e..26c82b793c0 100644
--- a/chromium/content/browser/accessibility/browser_accessibility_manager_win.h
+++ b/chromium/content/browser/accessibility/browser_accessibility_manager_win.h
@@ -14,22 +14,22 @@
namespace content {
class BrowserAccessibilityWin;
-class AccessibleHWND;
+class LegacyRenderWidgetHostHWND;
// Manages a tree of BrowserAccessibilityWin objects.
class CONTENT_EXPORT BrowserAccessibilityManagerWin
: public BrowserAccessibilityManager {
public:
BrowserAccessibilityManagerWin(
- HWND parent_hwnd,
+ content::LegacyRenderWidgetHostHWND* accessible_hwnd,
IAccessible* parent_iaccessible,
- const AccessibilityNodeData& src,
+ const ui::AXTreeUpdate& initial_tree,
BrowserAccessibilityDelegate* delegate,
BrowserAccessibilityFactory* factory = new BrowserAccessibilityFactory());
virtual ~BrowserAccessibilityManagerWin();
- static AccessibilityNodeData GetEmptyDocument();
+ static ui::AXTreeUpdate GetEmptyDocument();
// Get the closest containing HWND.
HWND parent_hwnd() { return parent_hwnd_; }
@@ -40,14 +40,19 @@ class CONTENT_EXPORT BrowserAccessibilityManagerWin
parent_iaccessible_ = parent_iaccessible;
}
+ void SetAccessibleHWND(LegacyRenderWidgetHostHWND* accessible_hwnd);
+
// Calls NotifyWinEvent if the parent window's IAccessible pointer is known.
void MaybeCallNotifyWinEvent(DWORD event, LONG child_id);
+ // AXTree methods
+ virtual void OnNodeWillBeDeleted(ui::AXNode* node) OVERRIDE;
+ virtual void OnNodeCreated(ui::AXNode* node) OVERRIDE;
+
// BrowserAccessibilityManager methods
- virtual void AddNodeToMap(BrowserAccessibility* node);
- virtual void RemoveNode(BrowserAccessibility* node) OVERRIDE;
+ virtual void OnWindowFocused() OVERRIDE;
virtual void NotifyAccessibilityEvent(
- blink::WebAXEvent event_type, BrowserAccessibility* node) OVERRIDE;
+ ui::AXEvent event_type, BrowserAccessibility* node) OVERRIDE;
// Track this object and post a VISIBLE_DATA_CHANGED notification when
// its container scrolls.
@@ -61,6 +66,10 @@ class CONTENT_EXPORT BrowserAccessibilityManagerWin
// Called when |accessible_hwnd_| is deleted by its parent.
void OnAccessibleHwndDeleted();
+ protected:
+ // BrowserAccessibilityManager methods
+ virtual void OnRootChanged(ui::AXNode* new_root) OVERRIDE;
+
private:
// The closest ancestor HWND.
HWND parent_hwnd_;
@@ -77,13 +86,15 @@ class CONTENT_EXPORT BrowserAccessibilityManagerWin
BrowserAccessibilityWin* tracked_scroll_object_;
// A mapping from the Windows-specific unique IDs (unique within the
- // browser process) to renderer ids within this page.
- base::hash_map<long, int32> unique_id_to_renderer_id_map_;
-
- bool is_chrome_frame_;
+ // browser process) to accessibility ids within this page.
+ base::hash_map<long, int32> unique_id_to_ax_id_map_;
// Owned by its parent; OnAccessibleHwndDeleted gets called upon deletion.
- AccessibleHWND* accessible_hwnd_;
+ LegacyRenderWidgetHostHWND* accessible_hwnd_;
+
+ // Set to true if we need to fire a focus event on the root as soon as
+ // possible.
+ bool focus_event_on_root_needed_;
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 3dee7d62b33..f45c31d2776 100644
--- a/chromium/content/browser/accessibility/browser_accessibility_state_impl.cc
+++ b/chromium/content/browser/accessibility/browser_accessibility_state_impl.cc
@@ -7,6 +7,7 @@
#include "base/command_line.h"
#include "base/metrics/histogram.h"
#include "base/timer/timer.h"
+#include "content/browser/accessibility/accessibility_mode_helper.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
@@ -37,21 +38,7 @@ BrowserAccessibilityStateImpl* BrowserAccessibilityStateImpl::GetInstance() {
BrowserAccessibilityStateImpl::BrowserAccessibilityStateImpl()
: BrowserAccessibilityState(),
accessibility_mode_(AccessibilityModeOff) {
-#if defined(OS_WIN)
- // On Windows 8, always enable accessibility for editable text controls
- // so we can show the virtual keyboard when one is enabled.
- if (base::win::GetVersion() >= base::win::VERSION_WIN8 &&
- !CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kDisableRendererAccessibility)) {
- accessibility_mode_ = AccessibilityModeEditableTextOnly;
- }
-#endif // defined(OS_WIN)
-
- if (CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kForceRendererAccessibility)) {
- accessibility_mode_ = AccessibilityModeComplete;
- }
-
+ ResetAccessibilityModeValue();
#if defined(OS_WIN)
// On Windows, UpdateHistograms calls some system functions with unknown
// runtime, so call it on the file thread to ensure there's no jank.
@@ -80,20 +67,57 @@ void BrowserAccessibilityStateImpl::OnScreenReaderDetected() {
switches::kDisableRendererAccessibility)) {
return;
}
- SetAccessibilityMode(AccessibilityModeComplete);
+ EnableAccessibility();
}
void BrowserAccessibilityStateImpl::EnableAccessibility() {
- // We may want to do something different with this later.
- SetAccessibilityMode(AccessibilityModeComplete);
+ AddAccessibilityMode(AccessibilityModeComplete);
}
void BrowserAccessibilityStateImpl::DisableAccessibility() {
- SetAccessibilityMode(AccessibilityModeOff);
+ ResetAccessibilityMode();
+}
+
+void BrowserAccessibilityStateImpl::ResetAccessibilityModeValue() {
+ accessibility_mode_ = AccessibilityModeOff;
+#if defined(OS_WIN)
+ // On Windows 8, always enable accessibility for editable text controls
+ // so we can show the virtual keyboard when one is enabled.
+ if (base::win::GetVersion() >= base::win::VERSION_WIN8 &&
+ !CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableRendererAccessibility)) {
+ accessibility_mode_ = AccessibilityModeEditableTextOnly;
+ }
+#endif // defined(OS_WIN)
+
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kForceRendererAccessibility)) {
+ accessibility_mode_ = AccessibilityModeComplete;
+ }
+}
+
+void BrowserAccessibilityStateImpl::ResetAccessibilityMode() {
+ ResetAccessibilityModeValue();
+
+ // Iterate over all RenderWidgetHosts, even swapped out ones in case
+ // they become active again.
+ scoped_ptr<RenderWidgetHostIterator> widgets(
+ RenderWidgetHostImpl::GetAllRenderWidgetHosts());
+ while (RenderWidgetHost* widget = widgets->GetNextHost()) {
+ // Ignore processes that don't have a connection, such as crashed tabs.
+ if (!widget->GetProcess()->HasConnection())
+ continue;
+ if (!widget->IsRenderView())
+ continue;
+
+ RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(widget);
+ rwhi->ResetAccessibilityMode();
+ }
}
bool BrowserAccessibilityStateImpl::IsAccessibleBrowser() {
- return (accessibility_mode_ == AccessibilityModeComplete);
+ return ((accessibility_mode_ & AccessibilityModeComplete) ==
+ AccessibilityModeComplete);
}
void BrowserAccessibilityStateImpl::AddHistogramCallback(
@@ -124,12 +148,36 @@ void BrowserAccessibilityStateImpl::UpdatePlatformSpecificHistograms() {
}
#endif
-void BrowserAccessibilityStateImpl::SetAccessibilityMode(
+void BrowserAccessibilityStateImpl::AddAccessibilityMode(
AccessibilityMode mode) {
- if (accessibility_mode_ == mode)
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableRendererAccessibility)) {
return;
- accessibility_mode_ = mode;
+ }
+
+ accessibility_mode_ =
+ content::AddAccessibilityModeTo(accessibility_mode_, mode);
+
+ AddOrRemoveFromRenderWidgets(mode, true);
+}
+
+void BrowserAccessibilityStateImpl::RemoveAccessibilityMode(
+ AccessibilityMode mode) {
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kForceRendererAccessibility) &&
+ mode == AccessibilityModeComplete) {
+ return;
+ }
+
+ accessibility_mode_ =
+ content::RemoveAccessibilityModeFrom(accessibility_mode_, mode);
+
+ AddOrRemoveFromRenderWidgets(mode, false);
+}
+void BrowserAccessibilityStateImpl::AddOrRemoveFromRenderWidgets(
+ AccessibilityMode mode,
+ bool add) {
// Iterate over all RenderWidgetHosts, even swapped out ones in case
// they become active again.
scoped_ptr<RenderWidgetHostIterator> widgets(
@@ -141,7 +189,11 @@ void BrowserAccessibilityStateImpl::SetAccessibilityMode(
if (!widget->IsRenderView())
continue;
- RenderWidgetHostImpl::From(widget)->SetAccessibilityMode(mode);
+ RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(widget);
+ if (add)
+ rwhi->AddAccessibilityMode(mode);
+ else
+ rwhi->RemoveAccessibilityMode(mode);
}
}
diff --git a/chromium/content/browser/accessibility/browser_accessibility_state_impl.h b/chromium/content/browser/accessibility/browser_accessibility_state_impl.h
index 3098341ef25..2a1b1c6bb4b 100644
--- a/chromium/content/browser/accessibility/browser_accessibility_state_impl.h
+++ b/chromium/content/browser/accessibility/browser_accessibility_state_impl.h
@@ -42,6 +42,7 @@ class CONTENT_EXPORT BrowserAccessibilityStateImpl
virtual void EnableAccessibility() OVERRIDE;
virtual void DisableAccessibility() OVERRIDE;
+ virtual void ResetAccessibilityMode() OVERRIDE;
virtual void OnScreenReaderDetected() OVERRIDE;
virtual bool IsAccessibleBrowser() OVERRIDE;
virtual void AddHistogramCallback(base::Closure callback) OVERRIDE;
@@ -49,12 +50,23 @@ class CONTENT_EXPORT BrowserAccessibilityStateImpl
virtual void UpdateHistogramsForTesting() OVERRIDE;
AccessibilityMode accessibility_mode() const { return accessibility_mode_; };
- void SetAccessibilityMode(AccessibilityMode mode);
+
+ // Adds the given accessibility mode to the current accessibility mode bitmap.
+ void AddAccessibilityMode(AccessibilityMode mode);
+
+ // Removes the given accessibility mode from the current accessibility mode
+ // bitmap, managing the bits that are shared with other modes such that a
+ // bit will only be turned off when all modes that depend on it have been
+ // removed.
+ void RemoveAccessibilityMode(AccessibilityMode mode);
private:
friend class base::RefCountedThreadSafe<BrowserAccessibilityStateImpl>;
friend struct DefaultSingletonTraits<BrowserAccessibilityStateImpl>;
+ // Resets accessibility_mode_ to the default value.
+ void ResetAccessibilityModeValue();
+
// Called a short while after startup to allow time for the accessibility
// state to be determined. Updates histograms with the current state.
void UpdateHistograms();
@@ -64,6 +76,10 @@ class CONTENT_EXPORT BrowserAccessibilityStateImpl
void UpdatePlatformSpecificHistograms();
+ // Updates the accessibility mode of all render widgets, including swapped out
+ // ones. |add| specifies whether the mode should be added or removed.
+ void AddOrRemoveFromRenderWidgets(AccessibilityMode mode, bool add);
+
AccessibilityMode accessibility_mode_;
std::vector<base::Closure> histogram_callbacks_;
diff --git a/chromium/content/browser/accessibility/browser_accessibility_win.cc b/chromium/content/browser/accessibility/browser_accessibility_win.cc
index 2c0150322b9..c5fa35898ff 100644
--- a/chromium/content/browser/accessibility/browser_accessibility_win.cc
+++ b/chromium/content/browser/accessibility/browser_accessibility_win.cc
@@ -18,7 +18,7 @@
#include "content/browser/accessibility/browser_accessibility_state_impl.h"
#include "content/common/accessibility_messages.h"
#include "content/public/common/content_client.h"
-#include "ui/base/accessibility/accessible_text_utils.h"
+#include "ui/accessibility/ax_text_utils.h"
#include "ui/base/win/accessibility_ids_win.h"
#include "ui/base/win/accessibility_misc_utils.h"
@@ -34,7 +34,7 @@ const GUID GUID_IAccessibleContentDocument = {
0xa5d8e1f3, 0x3571, 0x4d8f,
0x95, 0x21, 0x07, 0xed, 0x28, 0xfb, 0x07, 0x2e};
-const char16 BrowserAccessibilityWin::kEmbeddedCharacter[] = L"\xfffc";
+const base::char16 BrowserAccessibilityWin::kEmbeddedCharacter[] = L"\xfffc";
// static
LONG BrowserAccessibilityWin::next_unique_id_win_ =
@@ -114,7 +114,7 @@ STDMETHODIMP BrowserAccessibilityRelation::get_nTargets(long* n_targets) {
BrowserAccessibilityManager* manager = owner_->manager();
for (long i = *n_targets - 1; i >= 0; --i) {
- BrowserAccessibility* result = manager->GetFromRendererID(target_ids_[i]);
+ BrowserAccessibility* result = manager->GetFromID(target_ids_[i]);
if (!result || !result->instance_active()) {
*n_targets = 0;
break;
@@ -138,7 +138,7 @@ STDMETHODIMP BrowserAccessibilityRelation::get_target(long target_index,
BrowserAccessibilityManager* manager = owner_->manager();
BrowserAccessibility* result =
- manager->GetFromRendererID(target_ids_[target_index]);
+ manager->GetFromID(target_ids_[target_index]);
if (!result || !result->instance_active())
return E_FAIL;
@@ -380,7 +380,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_accDefaultAction(VARIANT var_id,
return E_INVALIDARG;
return target->GetStringAttributeAsBstr(
- AccessibilityNodeData::ATTR_SHORTCUT, def_action);
+ ui::AX_ATTR_SHORTCUT, def_action);
}
STDMETHODIMP BrowserAccessibilityWin::get_accDescription(VARIANT var_id,
@@ -396,7 +396,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_accDescription(VARIANT var_id,
return E_INVALIDARG;
return target->GetStringAttributeAsBstr(
- AccessibilityNodeData::ATTR_DESCRIPTION, desc);
+ ui::AX_ATTR_DESCRIPTION, desc);
}
STDMETHODIMP BrowserAccessibilityWin::get_accFocus(VARIANT* focus_child) {
@@ -433,7 +433,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_accHelp(VARIANT var_id, BSTR* help) {
return E_INVALIDARG;
return target->GetStringAttributeAsBstr(
- AccessibilityNodeData::ATTR_HELP, help);
+ ui::AX_ATTR_HELP, help);
}
STDMETHODIMP BrowserAccessibilityWin::get_accKeyboardShortcut(VARIANT var_id,
@@ -449,7 +449,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_accKeyboardShortcut(VARIANT var_id,
return E_INVALIDARG;
return target->GetStringAttributeAsBstr(
- AccessibilityNodeData::ATTR_SHORTCUT, acc_key);
+ ui::AX_ATTR_SHORTCUT, acc_key);
}
STDMETHODIMP BrowserAccessibilityWin::get_accName(VARIANT var_id, BSTR* name) {
@@ -468,10 +468,10 @@ STDMETHODIMP BrowserAccessibilityWin::get_accName(VARIANT var_id, BSTR* name) {
// If the name is empty, see if it's labeled by another element.
if (name_str.empty()) {
int title_elem_id;
- if (target->GetIntAttribute(AccessibilityNodeData::ATTR_TITLE_UI_ELEMENT,
+ if (target->GetIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT,
&title_elem_id)) {
BrowserAccessibility* title_elem =
- manager()->GetFromRendererID(title_elem_id);
+ manager()->GetFromID(title_elem_id);
if (title_elem)
name_str = title_elem->GetTextRecursive();
}
@@ -480,7 +480,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_accName(VARIANT var_id, BSTR* name) {
if (name_str.empty())
return S_FALSE;
- *name = SysAllocString(UTF8ToUTF16(name_str).c_str());
+ *name = SysAllocString(base::UTF8ToUTF16(name_str).c_str());
DCHECK(*name);
return S_OK;
@@ -493,19 +493,24 @@ STDMETHODIMP BrowserAccessibilityWin::get_accParent(IDispatch** disp_parent) {
if (!disp_parent)
return E_INVALIDARG;
- IAccessible* parent_obj = parent()->ToBrowserAccessibilityWin();
+ IAccessible* parent_obj = GetParent()->ToBrowserAccessibilityWin();
if (parent_obj == NULL) {
// This happens if we're the root of the tree;
// return the IAccessible for the window.
parent_obj =
manager()->ToBrowserAccessibilityManagerWin()->parent_iaccessible();
-
// |parent| can only be NULL if the manager was created before the parent
// IAccessible was known and it wasn't subsequently set before a client
- // requested it. Crash hard if this happens so that we get crash reports.
- CHECK(parent_obj);
+ // requested it. This has been fixed. |parent| may also be NULL during
+ // destruction. Possible cases where this could occur include tabs being
+ // dragged to a new window, etc.
+ if (!parent_obj) {
+ DVLOG(1) << "In Function: "
+ << __FUNCTION__
+ << ". Parent IAccessible interface is NULL. Returning failure";
+ return E_FAIL;
+ }
}
-
parent_obj->AddRef();
*disp_parent = parent_obj;
return S_OK;
@@ -577,11 +582,11 @@ STDMETHODIMP BrowserAccessibilityWin::get_accValue(VARIANT var_id,
// Expose color well value.
if (target->ia2_role() == IA2_ROLE_COLOR_CHOOSER) {
int r = target->GetIntAttribute(
- AccessibilityNodeData::ATTR_COLOR_VALUE_RED);
+ ui::AX_ATTR_COLOR_VALUE_RED);
int g = target->GetIntAttribute(
- AccessibilityNodeData::ATTR_COLOR_VALUE_GREEN);
+ ui::AX_ATTR_COLOR_VALUE_GREEN);
int b = target->GetIntAttribute(
- AccessibilityNodeData::ATTR_COLOR_VALUE_BLUE);
+ ui::AX_ATTR_COLOR_VALUE_BLUE);
base::string16 value_text;
value_text = base::IntToString16((r * 100) / 255) + L"% red " +
base::IntToString16((g * 100) / 255) + L"% green " +
@@ -591,7 +596,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_accValue(VARIANT var_id,
return S_OK;
}
- *value = SysAllocString(UTF8ToUTF16(target->value()).c_str());
+ *value = SysAllocString(base::UTF8ToUTF16(target->value()).c_str());
DCHECK(*value);
return S_OK;
}
@@ -606,12 +611,12 @@ STDMETHODIMP BrowserAccessibilityWin::get_accSelection(VARIANT* selected) {
if (!instance_active())
return E_FAIL;
- if (blink_role() != blink::WebAXRoleListBox)
+ if (GetRole() != ui::AX_ROLE_LIST_BOX)
return E_NOTIMPL;
unsigned long selected_count = 0;
- for (size_t i = 0; i < children().size(); ++i) {
- if (children()[i]->HasState(blink::WebAXStateSelected))
+ for (size_t i = 0; i < InternalChildCount(); ++i) {
+ if (InternalGetChild(i)->HasState(ui::AX_STATE_SELECTED))
++selected_count;
}
@@ -621,11 +626,11 @@ STDMETHODIMP BrowserAccessibilityWin::get_accSelection(VARIANT* selected) {
}
if (selected_count == 1) {
- for (size_t i = 0; i < children().size(); ++i) {
- if (children()[i]->HasState(blink::WebAXStateSelected)) {
+ for (size_t i = 0; i < InternalChildCount(); ++i) {
+ if (InternalGetChild(i)->HasState(ui::AX_STATE_SELECTED)) {
selected->vt = VT_DISPATCH;
selected->pdispVal =
- children()[i]->ToBrowserAccessibilityWin()->NewReference();
+ InternalGetChild(i)->ToBrowserAccessibilityWin()->NewReference();
return S_OK;
}
}
@@ -636,11 +641,11 @@ STDMETHODIMP BrowserAccessibilityWin::get_accSelection(VARIANT* selected) {
new base::win::EnumVariant(selected_count);
enum_variant->AddRef();
unsigned long index = 0;
- for (size_t i = 0; i < children().size(); ++i) {
- if (children()[i]->HasState(blink::WebAXStateSelected)) {
+ for (size_t i = 0; i < InternalChildCount(); ++i) {
+ if (InternalGetChild(i)->HasState(ui::AX_STATE_SELECTED)) {
enum_variant->ItemAt(index)->vt = VT_DISPATCH;
enum_variant->ItemAt(index)->pdispVal =
- children()[i]->ToBrowserAccessibilityWin()->NewReference();
+ InternalGetChild(i)->ToBrowserAccessibilityWin()->NewReference();
++index;
}
}
@@ -734,6 +739,9 @@ STDMETHODIMP BrowserAccessibilityWin::get_windowHandle(HWND* window_handle) {
return E_INVALIDARG;
*window_handle = manager()->ToBrowserAccessibilityManagerWin()->parent_hwnd();
+ if (!*window_handle)
+ return E_FAIL;
+
return S_OK;
}
@@ -744,7 +752,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_indexInParent(LONG* index_in_parent) {
if (!index_in_parent)
return E_INVALIDARG;
- *index_in_parent = this->index_in_parent();
+ *index_in_parent = this->GetIndexInParent();
return S_OK;
}
@@ -805,7 +813,7 @@ STDMETHODIMP BrowserAccessibilityWin::scrollTo(enum IA2ScrollType scroll_type) {
if (!instance_active())
return E_FAIL;
- gfx::Rect r = location();
+ gfx::Rect r = GetLocation();
switch(scroll_type) {
case IA2_SCROLL_TYPE_TOP_LEFT:
manager()->ScrollToMakeVisible(*this, gfx::Rect(r.x(), r.y(), 0, 0));
@@ -853,8 +861,8 @@ STDMETHODIMP BrowserAccessibilityWin::scrollToPoint(
if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) {
scroll_to -= manager()->GetViewBounds().OffsetFromOrigin();
} else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) {
- if (parent())
- scroll_to += parent()->location().OffsetFromOrigin();
+ if (GetParent())
+ scroll_to += GetParent()->GetLocation().OffsetFromOrigin();
} else {
return E_INVALIDARG;
}
@@ -875,12 +883,12 @@ STDMETHODIMP BrowserAccessibilityWin::get_groupPosition(
if (!group_level || !similar_items_in_group || !position_in_group)
return E_INVALIDARG;
- if (blink_role() == blink::WebAXRoleListBoxOption &&
- parent() &&
- parent()->role() == blink::WebAXRoleListBox) {
+ if (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION &&
+ GetParent() &&
+ GetParent()->GetRole() == ui::AX_ROLE_LIST_BOX) {
*group_level = 0;
- *similar_items_in_group = parent()->PlatformChildCount();
- *position_in_group = index_in_parent() + 1;
+ *similar_items_in_group = GetParent()->PlatformChildCount();
+ *position_in_group = GetIndexInParent() + 1;
return S_OK;
}
@@ -905,7 +913,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_appName(BSTR* app_name) {
DCHECK_EQ(2U, product_components.size());
if (product_components.size() != 2)
return E_FAIL;
- *app_name = SysAllocString(UTF8ToUTF16(product_components[0]).c_str());
+ *app_name = SysAllocString(base::UTF8ToUTF16(product_components[0]).c_str());
DCHECK(*app_name);
return *app_name ? S_OK : E_FAIL;
}
@@ -924,7 +932,8 @@ STDMETHODIMP BrowserAccessibilityWin::get_appVersion(BSTR* app_version) {
DCHECK_EQ(2U, product_components.size());
if (product_components.size() != 2)
return E_FAIL;
- *app_version = SysAllocString(UTF8ToUTF16(product_components[1]).c_str());
+ *app_version =
+ SysAllocString(base::UTF8ToUTF16(product_components[1]).c_str());
DCHECK(*app_version);
return *app_version ? S_OK : E_FAIL;
}
@@ -953,7 +962,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_toolkitVersion(
return E_INVALIDARG;
std::string user_agent = GetContentClient()->GetUserAgent();
- *toolkit_version = SysAllocString(UTF8ToUTF16(user_agent).c_str());
+ *toolkit_version = SysAllocString(base::UTF8ToUTF16(user_agent).c_str());
DCHECK(*toolkit_version);
return *toolkit_version ? S_OK : E_FAIL;
}
@@ -970,7 +979,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_description(BSTR* desc) {
return E_INVALIDARG;
return GetStringAttributeAsBstr(
- AccessibilityNodeData::ATTR_DESCRIPTION, desc);
+ ui::AX_ATTR_DESCRIPTION, desc);
}
STDMETHODIMP BrowserAccessibilityWin::get_imagePosition(
@@ -986,16 +995,18 @@ STDMETHODIMP BrowserAccessibilityWin::get_imagePosition(
if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) {
HWND parent_hwnd =
manager()->ToBrowserAccessibilityManagerWin()->parent_hwnd();
+ if (!parent_hwnd)
+ return E_FAIL;
POINT top_left = {0, 0};
::ClientToScreen(parent_hwnd, &top_left);
- *x = location().x() + top_left.x;
- *y = location().y() + top_left.y;
+ *x = GetLocation().x() + top_left.x;
+ *y = GetLocation().y() + top_left.y;
} else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) {
- *x = location().x();
- *y = location().y();
- if (parent()) {
- *x -= parent()->location().x();
- *y -= parent()->location().y();
+ *x = GetLocation().x();
+ *y = GetLocation().y();
+ if (GetParent()) {
+ *x -= GetParent()->GetLocation().x();
+ *y -= GetParent()->GetLocation().y();
}
} else {
return E_INVALIDARG;
@@ -1011,8 +1022,8 @@ STDMETHODIMP BrowserAccessibilityWin::get_imageSize(LONG* height, LONG* width) {
if (!height || !width)
return E_INVALIDARG;
- *height = location().height();
- *width = location().width();
+ *height = GetLocation().height();
+ *width = GetLocation().width();
return S_OK;
}
@@ -1033,9 +1044,9 @@ STDMETHODIMP BrowserAccessibilityWin::get_accessibleAt(
int columns;
int rows;
if (!GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns) ||
+ ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
!GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows) ||
+ ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
columns <= 0 ||
rows <= 0) {
return S_FALSE;
@@ -1045,11 +1056,11 @@ STDMETHODIMP BrowserAccessibilityWin::get_accessibleAt(
return E_INVALIDARG;
const std::vector<int32>& cell_ids = GetIntListAttribute(
- AccessibilityNodeData::ATTR_CELL_IDS);
+ ui::AX_ATTR_CELL_IDS);
DCHECK_EQ(columns * rows, static_cast<int>(cell_ids.size()));
int cell_id = cell_ids[row * columns + column];
- BrowserAccessibilityWin* cell = GetFromRendererID(cell_id);
+ BrowserAccessibilityWin* cell = GetFromID(cell_id);
if (cell) {
*accessible = static_cast<IAccessible*>(cell->NewReference());
return S_OK;
@@ -1082,9 +1093,9 @@ STDMETHODIMP BrowserAccessibilityWin::get_childIndex(long row,
int columns;
int rows;
if (!GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns) ||
+ ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
!GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows) ||
+ ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
columns <= 0 ||
rows <= 0) {
return S_FALSE;
@@ -1094,9 +1105,9 @@ STDMETHODIMP BrowserAccessibilityWin::get_childIndex(long row,
return E_INVALIDARG;
const std::vector<int32>& cell_ids = GetIntListAttribute(
- AccessibilityNodeData::ATTR_CELL_IDS);
+ ui::AX_ATTR_CELL_IDS);
const std::vector<int32>& unique_cell_ids = GetIntListAttribute(
- AccessibilityNodeData::ATTR_UNIQUE_CELL_IDS);
+ ui::AX_ATTR_UNIQUE_CELL_IDS);
DCHECK_EQ(columns * rows, static_cast<int>(cell_ids.size()));
int cell_id = cell_ids[row * columns + column];
for (size_t i = 0; i < unique_cell_ids.size(); ++i) {
@@ -1120,8 +1131,8 @@ STDMETHODIMP BrowserAccessibilityWin::get_columnDescription(long column,
int columns;
int rows;
if (!GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns) ||
- !GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows) ||
+ ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
+ !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
columns <= 0 ||
rows <= 0) {
return S_FALSE;
@@ -1131,21 +1142,21 @@ STDMETHODIMP BrowserAccessibilityWin::get_columnDescription(long column,
return E_INVALIDARG;
const std::vector<int32>& cell_ids = GetIntListAttribute(
- AccessibilityNodeData::ATTR_CELL_IDS);
+ ui::AX_ATTR_CELL_IDS);
for (int i = 0; i < rows; ++i) {
int cell_id = cell_ids[i * columns + column];
BrowserAccessibilityWin* cell = static_cast<BrowserAccessibilityWin*>(
- manager()->GetFromRendererID(cell_id));
- if (cell && cell->blink_role() == blink::WebAXRoleColumnHeader) {
+ manager()->GetFromID(cell_id));
+ if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER) {
base::string16 cell_name = cell->GetString16Attribute(
- AccessibilityNodeData::ATTR_NAME);
+ ui::AX_ATTR_NAME);
if (cell_name.size() > 0) {
*description = SysAllocString(cell_name.c_str());
return S_OK;
}
return cell->GetStringAttributeAsBstr(
- AccessibilityNodeData::ATTR_DESCRIPTION, description);
+ ui::AX_ATTR_DESCRIPTION, description);
}
}
@@ -1165,8 +1176,8 @@ STDMETHODIMP BrowserAccessibilityWin::get_columnExtentAt(
int columns;
int rows;
if (!GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns) ||
- !GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows) ||
+ ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
+ !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
columns <= 0 ||
rows <= 0) {
return S_FALSE;
@@ -1176,14 +1187,14 @@ STDMETHODIMP BrowserAccessibilityWin::get_columnExtentAt(
return E_INVALIDARG;
const std::vector<int32>& cell_ids = GetIntListAttribute(
- AccessibilityNodeData::ATTR_CELL_IDS);
+ ui::AX_ATTR_CELL_IDS);
int cell_id = cell_ids[row * columns + column];
BrowserAccessibilityWin* cell = static_cast<BrowserAccessibilityWin*>(
- manager()->GetFromRendererID(cell_id));
+ manager()->GetFromID(cell_id));
int colspan;
if (cell &&
cell->GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) &&
+ ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) &&
colspan >= 1) {
*n_columns_spanned = colspan;
return S_OK;
@@ -1208,7 +1219,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_columnIndex(long cell_index,
return E_INVALIDARG;
const std::vector<int32>& unique_cell_ids = GetIntListAttribute(
- AccessibilityNodeData::ATTR_UNIQUE_CELL_IDS);
+ ui::AX_ATTR_UNIQUE_CELL_IDS);
int cell_id_count = static_cast<int>(unique_cell_ids.size());
if (cell_index < 0)
return E_INVALIDARG;
@@ -1217,11 +1228,11 @@ STDMETHODIMP BrowserAccessibilityWin::get_columnIndex(long cell_index,
int cell_id = unique_cell_ids[cell_index];
BrowserAccessibilityWin* cell =
- manager()->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin();
+ manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
int col_index;
if (cell &&
cell->GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX, &col_index)) {
+ ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &col_index)) {
*column_index = col_index;
return S_OK;
}
@@ -1238,7 +1249,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_nColumns(long* column_count) {
int columns;
if (GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns)) {
+ ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns)) {
*column_count = columns;
return S_OK;
}
@@ -1254,7 +1265,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_nRows(long* row_count) {
return E_INVALIDARG;
int rows;
- if (GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows)) {
+ if (GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows)) {
*row_count = rows;
return S_OK;
}
@@ -1307,8 +1318,8 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowDescription(long row,
int columns;
int rows;
if (!GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns) ||
- !GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows) ||
+ ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
+ !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
columns <= 0 ||
rows <= 0) {
return S_FALSE;
@@ -1318,21 +1329,21 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowDescription(long row,
return E_INVALIDARG;
const std::vector<int32>& cell_ids = GetIntListAttribute(
- AccessibilityNodeData::ATTR_CELL_IDS);
+ ui::AX_ATTR_CELL_IDS);
for (int i = 0; i < columns; ++i) {
int cell_id = cell_ids[row * columns + i];
BrowserAccessibilityWin* cell =
- manager()->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin();
- if (cell && cell->blink_role() == blink::WebAXRoleRowHeader) {
+ manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
+ if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER) {
base::string16 cell_name = cell->GetString16Attribute(
- AccessibilityNodeData::ATTR_NAME);
+ ui::AX_ATTR_NAME);
if (cell_name.size() > 0) {
*description = SysAllocString(cell_name.c_str());
return S_OK;
}
return cell->GetStringAttributeAsBstr(
- AccessibilityNodeData::ATTR_DESCRIPTION, description);
+ ui::AX_ATTR_DESCRIPTION, description);
}
}
@@ -1351,8 +1362,8 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowExtentAt(long row,
int columns;
int rows;
if (!GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns) ||
- !GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows) ||
+ ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
+ !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
columns <= 0 ||
rows <= 0) {
return S_FALSE;
@@ -1362,14 +1373,14 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowExtentAt(long row,
return E_INVALIDARG;
const std::vector<int32>& cell_ids = GetIntListAttribute(
- AccessibilityNodeData::ATTR_CELL_IDS);
+ ui::AX_ATTR_CELL_IDS);
int cell_id = cell_ids[row * columns + column];
BrowserAccessibilityWin* cell =
- manager()->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin();
+ manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
int rowspan;
if (cell &&
cell->GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
+ ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
rowspan >= 1) {
*n_rows_spanned = rowspan;
return S_OK;
@@ -1394,7 +1405,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowIndex(long cell_index,
return E_INVALIDARG;
const std::vector<int32>& unique_cell_ids = GetIntListAttribute(
- AccessibilityNodeData::ATTR_UNIQUE_CELL_IDS);
+ ui::AX_ATTR_UNIQUE_CELL_IDS);
int cell_id_count = static_cast<int>(unique_cell_ids.size());
if (cell_index < 0)
return E_INVALIDARG;
@@ -1403,11 +1414,11 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowIndex(long cell_index,
int cell_id = unique_cell_ids[cell_index];
BrowserAccessibilityWin* cell =
- manager()->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin();
+ manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
int cell_row_index;
if (cell &&
cell->GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_CELL_ROW_INDEX, &cell_row_index)) {
+ ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &cell_row_index)) {
*row_index = cell_row_index;
return S_OK;
}
@@ -1523,7 +1534,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowColumnExtentsAtIndex(
return E_INVALIDARG;
const std::vector<int32>& unique_cell_ids = GetIntListAttribute(
- AccessibilityNodeData::ATTR_UNIQUE_CELL_IDS);
+ ui::AX_ATTR_UNIQUE_CELL_IDS);
int cell_id_count = static_cast<int>(unique_cell_ids.size());
if (index < 0)
return E_INVALIDARG;
@@ -1532,14 +1543,14 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowColumnExtentsAtIndex(
int cell_id = unique_cell_ids[index];
BrowserAccessibilityWin* cell =
- manager()->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin();
+ manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
int rowspan;
int colspan;
if (cell &&
cell->GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
+ ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
cell->GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) &&
+ ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) &&
rowspan >= 1 &&
colspan >= 1) {
*row_extents = rowspan;
@@ -1619,7 +1630,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_columnExtent(
int colspan;
if (GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) &&
+ ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) &&
colspan >= 1) {
*n_columns_spanned = colspan;
return S_OK;
@@ -1641,13 +1652,13 @@ STDMETHODIMP BrowserAccessibilityWin::get_columnHeaderCells(
int column;
if (!GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX, &column)) {
+ ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column)) {
return S_FALSE;
}
- BrowserAccessibility* table = parent();
- while (table && table->role() != blink::WebAXRoleTable)
- table = table->parent();
+ BrowserAccessibility* table = GetParent();
+ while (table && table->GetRole() != ui::AX_ROLE_TABLE)
+ table = table->GetParent();
if (!table) {
NOTREACHED();
return S_FALSE;
@@ -1656,22 +1667,22 @@ STDMETHODIMP BrowserAccessibilityWin::get_columnHeaderCells(
int columns;
int rows;
if (!table->GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns) ||
+ ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
!table->GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows)) {
+ ui::AX_ATTR_TABLE_ROW_COUNT, &rows)) {
return S_FALSE;
}
if (columns <= 0 || rows <= 0 || column < 0 || column >= columns)
return S_FALSE;
const std::vector<int32>& cell_ids = table->GetIntListAttribute(
- AccessibilityNodeData::ATTR_CELL_IDS);
+ ui::AX_ATTR_CELL_IDS);
for (int i = 0; i < rows; ++i) {
int cell_id = cell_ids[i * columns + column];
BrowserAccessibilityWin* cell =
- manager()->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin();
- if (cell && cell->blink_role() == blink::WebAXRoleColumnHeader)
+ manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
+ if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER)
(*n_column_header_cells)++;
}
@@ -1680,8 +1691,8 @@ STDMETHODIMP BrowserAccessibilityWin::get_columnHeaderCells(
int index = 0;
for (int i = 0; i < rows; ++i) {
int cell_id = cell_ids[i * columns + column];
- BrowserAccessibility* cell = manager()->GetFromRendererID(cell_id);
- if (cell && cell->role() == blink::WebAXRoleColumnHeader) {
+ BrowserAccessibility* cell = manager()->GetFromID(cell_id);
+ if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER) {
(*cell_accessibles)[index] = static_cast<IAccessible*>(
cell->ToBrowserAccessibilityWin()->NewReference());
++index;
@@ -1700,7 +1711,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_columnIndex(long* column_index) {
int column;
if (GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX, &column)) {
+ ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column)) {
*column_index = column;
return S_OK;
}
@@ -1717,7 +1728,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowExtent(long* n_rows_spanned) {
int rowspan;
if (GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
+ ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
rowspan >= 1) {
*n_rows_spanned = rowspan;
return S_OK;
@@ -1739,13 +1750,13 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowHeaderCells(
int row;
if (!GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_CELL_ROW_INDEX, &row)) {
+ ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row)) {
return S_FALSE;
}
- BrowserAccessibility* table = parent();
- while (table && table->role() != blink::WebAXRoleTable)
- table = table->parent();
+ BrowserAccessibility* table = GetParent();
+ while (table && table->GetRole() != ui::AX_ROLE_TABLE)
+ table = table->GetParent();
if (!table) {
NOTREACHED();
return S_FALSE;
@@ -1754,21 +1765,21 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowHeaderCells(
int columns;
int rows;
if (!table->GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns) ||
+ ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
!table->GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows)) {
+ ui::AX_ATTR_TABLE_ROW_COUNT, &rows)) {
return S_FALSE;
}
if (columns <= 0 || rows <= 0 || row < 0 || row >= rows)
return S_FALSE;
const std::vector<int32>& cell_ids = table->GetIntListAttribute(
- AccessibilityNodeData::ATTR_CELL_IDS);
+ ui::AX_ATTR_CELL_IDS);
for (int i = 0; i < columns; ++i) {
int cell_id = cell_ids[row * columns + i];
- BrowserAccessibility* cell = manager()->GetFromRendererID(cell_id);
- if (cell && cell->role() == blink::WebAXRoleRowHeader)
+ BrowserAccessibility* cell = manager()->GetFromID(cell_id);
+ if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER)
(*n_row_header_cells)++;
}
@@ -1777,8 +1788,8 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowHeaderCells(
int index = 0;
for (int i = 0; i < columns; ++i) {
int cell_id = cell_ids[row * columns + i];
- BrowserAccessibility* cell = manager()->GetFromRendererID(cell_id);
- if (cell && cell->role() == blink::WebAXRoleRowHeader) {
+ BrowserAccessibility* cell = manager()->GetFromID(cell_id);
+ if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER) {
(*cell_accessibles)[index] = static_cast<IAccessible*>(
cell->ToBrowserAccessibilityWin()->NewReference());
++index;
@@ -1796,7 +1807,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowIndex(long* row_index) {
return E_INVALIDARG;
int row;
- if (GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_CELL_ROW_INDEX, &row)) {
+ if (GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row)) {
*row_index = row;
return S_OK;
}
@@ -1835,13 +1846,13 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowColumnExtents(
int column;
int rowspan;
int colspan;
- if (GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_CELL_ROW_INDEX, &row) &&
+ if (GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row) &&
GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX, &column) &&
+ ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column) &&
GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
+ ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
GetIntAttribute(
- AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_SPAN, &colspan)) {
+ ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan)) {
*row_index = row;
*column_index = column;
*row_extents = rowspan;
@@ -1863,12 +1874,12 @@ STDMETHODIMP BrowserAccessibilityWin::get_table(IUnknown** table) {
int row;
int column;
- GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_CELL_ROW_INDEX, &row);
- GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX, &column);
+ GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row);
+ GetIntAttribute(ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column);
- BrowserAccessibility* find_table = parent();
- while (find_table && find_table->role() != blink::WebAXRoleTable)
- find_table = find_table->parent();
+ BrowserAccessibility* find_table = GetParent();
+ while (find_table && find_table->GetRole() != ui::AX_ROLE_TABLE)
+ find_table = find_table->GetParent();
if (!find_table) {
NOTREACHED();
return S_FALSE;
@@ -1903,10 +1914,10 @@ STDMETHODIMP BrowserAccessibilityWin::get_caretOffset(LONG* offset) {
return E_INVALIDARG;
*offset = 0;
- if (blink_role() == blink::WebAXRoleTextField ||
- blink_role() == blink::WebAXRoleTextArea) {
+ if (GetRole() == ui::AX_ROLE_TEXT_FIELD ||
+ GetRole() == ui::AX_ROLE_TEXT_AREA) {
int sel_start = 0;
- if (GetIntAttribute(AccessibilityNodeData::ATTR_TEXT_SEL_START,
+ if (GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START,
&sel_start))
*offset = sel_start;
}
@@ -1933,15 +1944,12 @@ STDMETHODIMP BrowserAccessibilityWin::get_characterExtents(
if (offset < 0 || offset > static_cast<LONG>(text_str.size()))
return E_INVALIDARG;
- if (blink_role() != blink::WebAXRoleStaticText)
- return E_FAIL;
-
gfx::Rect character_bounds;
if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) {
character_bounds = GetGlobalBoundsForRange(offset, 1);
} else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) {
character_bounds = GetLocalBoundsForRange(offset, 1);
- character_bounds -= location().OffsetFromOrigin();
+ character_bounds -= GetLocation().OffsetFromOrigin();
} else {
return E_INVALIDARG;
}
@@ -1962,13 +1970,13 @@ STDMETHODIMP BrowserAccessibilityWin::get_nSelections(LONG* n_selections) {
return E_INVALIDARG;
*n_selections = 0;
- if (blink_role() == blink::WebAXRoleTextField ||
- blink_role() == blink::WebAXRoleTextArea) {
+ if (GetRole() == ui::AX_ROLE_TEXT_FIELD ||
+ GetRole() == ui::AX_ROLE_TEXT_AREA) {
int sel_start = 0;
int sel_end = 0;
- if (GetIntAttribute(AccessibilityNodeData::ATTR_TEXT_SEL_START,
+ if (GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START,
&sel_start) &&
- GetIntAttribute(AccessibilityNodeData::ATTR_TEXT_SEL_END, &sel_end) &&
+ GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &sel_end) &&
sel_start != sel_end)
*n_selections = 1;
}
@@ -1987,13 +1995,13 @@ STDMETHODIMP BrowserAccessibilityWin::get_selection(LONG selection_index,
*start_offset = 0;
*end_offset = 0;
- if (blink_role() == blink::WebAXRoleTextField ||
- blink_role() == blink::WebAXRoleTextArea) {
+ if (GetRole() == ui::AX_ROLE_TEXT_FIELD ||
+ GetRole() == ui::AX_ROLE_TEXT_AREA) {
int sel_start = 0;
int sel_end = 0;
if (GetIntAttribute(
- AccessibilityNodeData::ATTR_TEXT_SEL_START, &sel_start) &&
- GetIntAttribute(AccessibilityNodeData::ATTR_TEXT_SEL_END, &sel_end)) {
+ ui::AX_ATTR_TEXT_SEL_START, &sel_start) &&
+ GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &sel_end)) {
*start_offset = sel_start;
*end_offset = sel_end;
}
@@ -2272,7 +2280,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_hyperlink(
}
BrowserAccessibilityWin* child =
- children()[hyperlinks_[index]]->ToBrowserAccessibilityWin();
+ InternalGetChild(hyperlinks_[index])->ToBrowserAccessibilityWin();
*hyperlink = static_cast<IAccessibleHyperlink*>(child->NewReference());
return S_OK;
}
@@ -2313,7 +2321,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_currentValue(VARIANT* value) {
float float_val;
if (GetFloatAttribute(
- AccessibilityNodeData::ATTR_VALUE_FOR_RANGE, &float_val)) {
+ ui::AX_ATTR_VALUE_FOR_RANGE, &float_val)) {
value->vt = VT_R8;
value->dblVal = float_val;
return S_OK;
@@ -2331,7 +2339,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_minimumValue(VARIANT* value) {
return E_INVALIDARG;
float float_val;
- if (GetFloatAttribute(AccessibilityNodeData::ATTR_MIN_VALUE_FOR_RANGE,
+ if (GetFloatAttribute(ui::AX_ATTR_MIN_VALUE_FOR_RANGE,
&float_val)) {
value->vt = VT_R8;
value->dblVal = float_val;
@@ -2350,7 +2358,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_maximumValue(VARIANT* value) {
return E_INVALIDARG;
float float_val;
- if (GetFloatAttribute(AccessibilityNodeData::ATTR_MAX_VALUE_FOR_RANGE,
+ if (GetFloatAttribute(ui::AX_ATTR_MAX_VALUE_FOR_RANGE,
&float_val)) {
value->vt = VT_R8;
value->dblVal = float_val;
@@ -2377,7 +2385,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_URL(BSTR* url) {
if (!url)
return E_INVALIDARG;
- return GetStringAttributeAsBstr(AccessibilityNodeData::ATTR_DOC_URL, url);
+ return GetStringAttributeAsBstr(ui::AX_ATTR_DOC_URL, url);
}
STDMETHODIMP BrowserAccessibilityWin::get_title(BSTR* title) {
@@ -2387,7 +2395,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_title(BSTR* title) {
if (!title)
return E_INVALIDARG;
- return GetStringAttributeAsBstr(AccessibilityNodeData::ATTR_DOC_TITLE, title);
+ return GetStringAttributeAsBstr(ui::AX_ATTR_DOC_TITLE, title);
}
STDMETHODIMP BrowserAccessibilityWin::get_mimeType(BSTR* mime_type) {
@@ -2398,7 +2406,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_mimeType(BSTR* mime_type) {
return E_INVALIDARG;
return GetStringAttributeAsBstr(
- AccessibilityNodeData::ATTR_DOC_MIMETYPE, mime_type);
+ ui::AX_ATTR_DOC_MIMETYPE, mime_type);
}
STDMETHODIMP BrowserAccessibilityWin::get_docType(BSTR* doc_type) {
@@ -2409,7 +2417,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_docType(BSTR* doc_type) {
return E_INVALIDARG;
return GetStringAttributeAsBstr(
- AccessibilityNodeData::ATTR_DOC_DOCTYPE, doc_type);
+ ui::AX_ATTR_DOC_DOCTYPE, doc_type);
}
//
@@ -2432,13 +2440,13 @@ STDMETHODIMP BrowserAccessibilityWin::get_nodeInfo(
}
base::string16 tag;
- if (GetString16Attribute(AccessibilityNodeData::ATTR_HTML_TAG, &tag))
+ if (GetString16Attribute(ui::AX_ATTR_HTML_TAG, &tag))
*node_name = SysAllocString(tag.c_str());
else
*node_name = NULL;
*name_space_id = 0;
- *node_value = SysAllocString(UTF8ToUTF16(value()).c_str());
+ *node_value = SysAllocString(base::UTF8ToUTF16(value()).c_str());
*num_children = PlatformChildCount();
*unique_id = unique_id_win_;
@@ -2467,15 +2475,15 @@ STDMETHODIMP BrowserAccessibilityWin::get_attributes(
return E_INVALIDARG;
*num_attribs = max_attribs;
- if (*num_attribs > html_attributes().size())
- *num_attribs = html_attributes().size();
+ if (*num_attribs > GetHtmlAttributes().size())
+ *num_attribs = GetHtmlAttributes().size();
for (unsigned short i = 0; i < *num_attribs; ++i) {
attrib_names[i] = SysAllocString(
- UTF8ToUTF16(html_attributes()[i].first).c_str());
+ base::UTF8ToUTF16(GetHtmlAttributes()[i].first).c_str());
name_space_id[i] = 0;
attrib_values[i] = SysAllocString(
- UTF8ToUTF16(html_attributes()[i].second).c_str());
+ base::UTF8ToUTF16(GetHtmlAttributes()[i].second).c_str());
}
return S_OK;
}
@@ -2494,11 +2502,11 @@ STDMETHODIMP BrowserAccessibilityWin::get_attributesForNames(
for (unsigned short i = 0; i < num_attribs; ++i) {
name_space_id[i] = 0;
bool found = false;
- std::string name = UTF16ToUTF8((LPCWSTR)attrib_names[i]);
- for (unsigned int j = 0; j < html_attributes().size(); ++j) {
- if (html_attributes()[j].first == name) {
+ std::string name = base::UTF16ToUTF8((LPCWSTR)attrib_names[i]);
+ for (unsigned int j = 0; j < GetHtmlAttributes().size(); ++j) {
+ if (GetHtmlAttributes()[j].first == name) {
attrib_values[i] = SysAllocString(
- UTF8ToUTF16(html_attributes()[j].second).c_str());
+ base::UTF8ToUTF16(GetHtmlAttributes()[j].second).c_str());
found = true;
break;
}
@@ -2526,7 +2534,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_computedStyle(
base::string16 display;
if (max_style_properties == 0 ||
- !GetString16Attribute(AccessibilityNodeData::ATTR_DISPLAY, &display)) {
+ !GetString16Attribute(ui::AX_ATTR_DISPLAY, &display)) {
*num_style_properties = 0;
return S_OK;
}
@@ -2556,7 +2564,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_computedStyleForProperties(
StringToLowerASCII(&name);
if (name == L"display") {
base::string16 display = GetString16Attribute(
- AccessibilityNodeData::ATTR_DISPLAY);
+ ui::AX_ATTR_DISPLAY);
style_values[i] = SysAllocString(display.c_str());
} else {
style_values[i] = NULL;
@@ -2578,7 +2586,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_parentNode(ISimpleDOMNode** node) {
if (!node)
return E_INVALIDARG;
- *node = parent()->ToBrowserAccessibilityWin()->NewReference();
+ *node = GetParent()->ToBrowserAccessibilityWin()->NewReference();
return S_OK;
}
@@ -2623,12 +2631,12 @@ STDMETHODIMP BrowserAccessibilityWin::get_previousSibling(
if (!node)
return E_INVALIDARG;
- if (!parent() || index_in_parent() <= 0) {
+ if (!GetParent() || GetIndexInParent() <= 0) {
*node = NULL;
return S_FALSE;
}
- *node = parent()->children()[index_in_parent() - 1]->
+ *node = GetParent()->InternalGetChild(GetIndexInParent() - 1)->
ToBrowserAccessibilityWin()->NewReference();
return S_OK;
}
@@ -2640,14 +2648,15 @@ STDMETHODIMP BrowserAccessibilityWin::get_nextSibling(ISimpleDOMNode** node) {
if (!node)
return E_INVALIDARG;
- if (!parent() ||
- index_in_parent() < 0 ||
- index_in_parent() >= static_cast<int>(parent()->children().size()) - 1) {
+ if (!GetParent() ||
+ GetIndexInParent() < 0 ||
+ GetIndexInParent() >= static_cast<int>(
+ GetParent()->InternalChildCount()) - 1) {
*node = NULL;
return S_FALSE;
}
- *node = parent()->children()[index_in_parent() + 1]->
+ *node = GetParent()->InternalGetChild(GetIndexInParent() + 1)->
ToBrowserAccessibilityWin()->NewReference();
return S_OK;
}
@@ -2686,7 +2695,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_domText(BSTR* dom_text) {
return E_INVALIDARG;
return GetStringAttributeAsBstr(
- AccessibilityNodeData::ATTR_NAME, dom_text);
+ ui::AX_ATTR_NAME, dom_text);
}
STDMETHODIMP BrowserAccessibilityWin::get_clippedSubstringBounds(
@@ -2722,9 +2731,6 @@ STDMETHODIMP BrowserAccessibilityWin::get_unclippedSubstringBounds(
return E_INVALIDARG;
}
- if (blink_role() != blink::WebAXRoleStaticText)
- return E_FAIL;
-
gfx::Rect bounds = GetGlobalBoundsForRange(
start_index, end_index - start_index);
*out_x = bounds.x();
@@ -2818,13 +2824,9 @@ STDMETHODIMP BrowserAccessibilityWin::GetPatternProvider(PATTERNID id,
<< id;
if (id == UIA_ValuePatternId || id == UIA_TextPatternId) {
if (IsEditableText()) {
- // The BrowserAccessibilityManager keeps track of instances when
- // we don't want to show the on-screen keyboard.
- if (!manager()->IsOSKAllowed(GetGlobalBoundsRect()))
- return E_NOTIMPL;
-
DVLOG(1) << "Returning UIA text provider";
- base::win::UIATextProvider::CreateTextProvider(true, provider);
+ base::win::UIATextProvider::CreateTextProvider(
+ GetValueText(), true, provider);
return S_OK;
}
}
@@ -2896,28 +2898,28 @@ HRESULT WINAPI BrowserAccessibilityWin::InternalQueryInterface(
// Private methods.
//
-// Initialize this object and mark it as active.
-void BrowserAccessibilityWin::PreInitialize() {
- BrowserAccessibility::PreInitialize();
+// Called every time this node's data changes.
+void BrowserAccessibilityWin::OnDataChanged() {
+ BrowserAccessibility::OnDataChanged();
InitRoleAndState();
// Expose the "display" and "tag" attributes.
- StringAttributeToIA2(AccessibilityNodeData::ATTR_DISPLAY, "display");
- StringAttributeToIA2(AccessibilityNodeData::ATTR_HTML_TAG, "tag");
- StringAttributeToIA2(AccessibilityNodeData::ATTR_ROLE, "xml-roles");
+ StringAttributeToIA2(ui::AX_ATTR_DISPLAY, "display");
+ StringAttributeToIA2(ui::AX_ATTR_HTML_TAG, "tag");
+ StringAttributeToIA2(ui::AX_ATTR_ROLE, "xml-roles");
// Expose "level" attribute for headings, trees, etc.
- IntAttributeToIA2(AccessibilityNodeData::ATTR_HIERARCHICAL_LEVEL, "level");
+ IntAttributeToIA2(ui::AX_ATTR_HIERARCHICAL_LEVEL, "level");
// Expose the set size and position in set for listbox options.
- if (blink_role() == blink::WebAXRoleListBoxOption &&
- parent() &&
- parent()->role() == blink::WebAXRoleListBox) {
+ if (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION &&
+ GetParent() &&
+ GetParent()->GetRole() == ui::AX_ROLE_LIST_BOX) {
ia2_attributes_.push_back(
- L"setsize:" + base::IntToString16(parent()->PlatformChildCount()));
+ L"setsize:" + base::IntToString16(GetParent()->PlatformChildCount()));
ia2_attributes_.push_back(
- L"setsize:" + base::IntToString16(index_in_parent() + 1));
+ L"setsize:" + base::IntToString16(GetIndexInParent() + 1));
}
if (ia_role_ == ROLE_SYSTEM_CHECKBUTTON ||
@@ -2927,19 +2929,19 @@ void BrowserAccessibilityWin::PreInitialize() {
}
// Expose live region attributes.
- StringAttributeToIA2(AccessibilityNodeData::ATTR_LIVE_STATUS, "live");
- StringAttributeToIA2(AccessibilityNodeData::ATTR_LIVE_RELEVANT, "relevant");
- BoolAttributeToIA2(AccessibilityNodeData::ATTR_LIVE_ATOMIC, "atomic");
- BoolAttributeToIA2(AccessibilityNodeData::ATTR_LIVE_BUSY, "busy");
+ StringAttributeToIA2(ui::AX_ATTR_LIVE_STATUS, "live");
+ StringAttributeToIA2(ui::AX_ATTR_LIVE_RELEVANT, "relevant");
+ BoolAttributeToIA2(ui::AX_ATTR_LIVE_ATOMIC, "atomic");
+ BoolAttributeToIA2(ui::AX_ATTR_LIVE_BUSY, "busy");
// Expose container live region attributes.
- StringAttributeToIA2(AccessibilityNodeData::ATTR_CONTAINER_LIVE_STATUS,
+ StringAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_STATUS,
"container-live");
- StringAttributeToIA2(AccessibilityNodeData::ATTR_CONTAINER_LIVE_RELEVANT,
+ StringAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_RELEVANT,
"container-relevant");
- BoolAttributeToIA2(AccessibilityNodeData::ATTR_CONTAINER_LIVE_ATOMIC,
+ BoolAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_ATOMIC,
"container-atomic");
- BoolAttributeToIA2(AccessibilityNodeData::ATTR_CONTAINER_LIVE_BUSY,
+ BoolAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_BUSY,
"container-busy");
// Expose slider value.
@@ -2951,14 +2953,14 @@ void BrowserAccessibilityWin::PreInitialize() {
// Expose table cell index.
if (ia_role_ == ROLE_SYSTEM_CELL) {
- BrowserAccessibility* table = parent();
- while (table && table->role() != blink::WebAXRoleTable)
- table = table->parent();
+ BrowserAccessibility* table = GetParent();
+ while (table && table->GetRole() != ui::AX_ROLE_TABLE)
+ table = table->GetParent();
if (table) {
const std::vector<int32>& unique_cell_ids = table->GetIntListAttribute(
- AccessibilityNodeData::ATTR_UNIQUE_CELL_IDS);
+ ui::AX_ATTR_UNIQUE_CELL_IDS);
for (size_t i = 0; i < unique_cell_ids.size(); ++i) {
- if (unique_cell_ids[i] == renderer_id()) {
+ if (unique_cell_ids[i] == GetId()) {
ia2_attributes_.push_back(
base::string16(L"table-cell-index:") + base::IntToString16(i));
}
@@ -2988,10 +2990,10 @@ void BrowserAccessibilityWin::PreInitialize() {
// if any, in "description".
int title_elem_id = GetIntAttribute(
- AccessibilityNodeData::ATTR_TITLE_UI_ELEMENT);
- std::string help = GetStringAttribute(AccessibilityNodeData::ATTR_HELP);
+ ui::AX_ATTR_TITLE_UI_ELEMENT);
+ std::string help = GetStringAttribute(ui::AX_ATTR_HELP);
std::string description = GetStringAttribute(
- AccessibilityNodeData::ATTR_DESCRIPTION);
+ ui::AX_ATTR_DESCRIPTION);
// WebKit annoyingly puts the title in the description if there's no other
// description, which just confuses the rest of the logic. Put it back.
@@ -3022,8 +3024,8 @@ void BrowserAccessibilityWin::PreInitialize() {
// If it's a text field, also consider the placeholder.
std::string placeholder;
- if (blink_role() == blink::WebAXRoleTextField &&
- HasState(blink::WebAXStateFocusable) &&
+ if (GetRole() == ui::AX_ROLE_TEXT_FIELD &&
+ HasState(ui::AX_STATE_FOCUSABLE) &&
GetHtmlAttribute("placeholder", &placeholder)) {
if (name().empty() && !title_elem_id) {
set_name(placeholder);
@@ -3032,22 +3034,22 @@ void BrowserAccessibilityWin::PreInitialize() {
}
}
- SetStringAttribute(AccessibilityNodeData::ATTR_DESCRIPTION, description);
- SetStringAttribute(AccessibilityNodeData::ATTR_HELP, help);
+ SetStringAttribute(ui::AX_ATTR_DESCRIPTION, description);
+ SetStringAttribute(ui::AX_ATTR_HELP, help);
// On Windows, the value of a document should be its url.
- if (blink_role() == blink::WebAXRoleRootWebArea ||
- blink_role() == blink::WebAXRoleWebArea) {
- set_value(GetStringAttribute(AccessibilityNodeData::ATTR_DOC_URL));
+ if (GetRole() == ui::AX_ROLE_ROOT_WEB_AREA ||
+ GetRole() == ui::AX_ROLE_WEB_AREA) {
+ set_value(GetStringAttribute(ui::AX_ATTR_DOC_URL));
}
// For certain roles (listbox option, static text, and list marker)
// WebKit stores the main accessible text in the "value" - swap it so
// that it's the "name".
if (name().empty() &&
- (blink_role() == blink::WebAXRoleListBoxOption ||
- blink_role() == blink::WebAXRoleStaticText ||
- blink_role() == blink::WebAXRoleListMarker)) {
+ (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION ||
+ GetRole() == ui::AX_ROLE_STATIC_TEXT ||
+ GetRole() == ui::AX_ROLE_LIST_MARKER)) {
std::string tmp = value();
set_value(name());
set_name(tmp);
@@ -3056,7 +3058,7 @@ void BrowserAccessibilityWin::PreInitialize() {
// If this doesn't have a value and is linked then set its value to the url
// attribute. This allows screen readers to read an empty link's destination.
if (value().empty() && (ia_state_ & STATE_SYSTEM_LINKED))
- set_value(GetStringAttribute(AccessibilityNodeData::ATTR_URL));
+ set_value(GetStringAttribute(ui::AX_ATTR_URL));
// Clear any old relationships between this node and other nodes.
for (size_t i = 0; i < relations_.size(); ++i)
@@ -3077,17 +3079,15 @@ void BrowserAccessibilityWin::PreInitialize() {
}
}
-void BrowserAccessibilityWin::PostInitialize() {
- BrowserAccessibility::PostInitialize();
-
+void BrowserAccessibilityWin::OnUpdateFinished() {
// Construct the hypertext for this node.
hyperlink_offset_to_index_.clear();
hyperlinks_.clear();
hypertext_.clear();
for (unsigned int i = 0; i < PlatformChildCount(); ++i) {
BrowserAccessibility* child = PlatformGetChild(i);
- if (child->role() == blink::WebAXRoleStaticText) {
- hypertext_ += UTF8ToUTF16(child->name());
+ if (child->GetRole() == ui::AX_ROLE_STATIC_TEXT) {
+ hypertext_ += base::UTF8ToUTF16(child->name());
} else {
hyperlink_offset_to_index_[hypertext_.size()] = hyperlinks_.size();
hypertext_ += kEmbeddedCharacter;
@@ -3097,15 +3097,15 @@ void BrowserAccessibilityWin::PostInitialize() {
DCHECK_EQ(hyperlink_offset_to_index_.size(), hyperlinks_.size());
// Fire an event when an alert first appears.
- if (blink_role() == blink::WebAXRoleAlert && first_time_)
- manager()->NotifyAccessibilityEvent(blink::WebAXEventAlert, this);
+ if (GetRole() == ui::AX_ROLE_ALERT && first_time_)
+ manager()->NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, this);
// Fire events if text has changed.
base::string16 text = TextForIAccessibleText();
if (previous_text_ != text) {
if (!previous_text_.empty() && !text.empty()) {
manager()->NotifyAccessibilityEvent(
- blink::WebAXEventShow, this);
+ ui::AX_EVENT_SHOW, this);
}
// TODO(dmazzoni): Look into HIDE events, too.
@@ -3123,7 +3123,7 @@ void BrowserAccessibilityWin::PostInitialize() {
// focus for managed descendants is platform-specific.
// Fire a focus event if the focused descendant in a multi-select
// list box changes.
- if (blink_role() == blink::WebAXRoleListBoxOption &&
+ if (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION &&
(ia_state_ & STATE_SYSTEM_FOCUSABLE) &&
(ia_state_ & STATE_SYSTEM_SELECTABLE) &&
(ia_state_ & STATE_SYSTEM_FOCUSED) &&
@@ -3147,8 +3147,8 @@ void BrowserAccessibilityWin::PostInitialize() {
// Fire an event if this container object has scrolled.
int sx = 0;
int sy = 0;
- if (GetIntAttribute(AccessibilityNodeData::ATTR_SCROLL_X, &sx) &&
- GetIntAttribute(AccessibilityNodeData::ATTR_SCROLL_Y, &sy)) {
+ if (GetIntAttribute(ui::AX_ATTR_SCROLL_X, &sx) &&
+ GetIntAttribute(ui::AX_ATTR_SCROLL_Y, &sy)) {
if (!first_time_ &&
(sx != previous_scroll_x_ || sy != previous_scroll_y_)) {
manager->MaybeCallNotifyWinEvent(EVENT_SYSTEM_SCROLLINGEND,
@@ -3173,8 +3173,7 @@ bool BrowserAccessibilityWin::IsNative() const {
return true;
}
-void BrowserAccessibilityWin::SetLocation(const gfx::Rect& new_location) {
- BrowserAccessibility::SetLocation(new_location);
+void BrowserAccessibilityWin::OnLocationChanged() const {
manager()->ToBrowserAccessibilityManagerWin()->MaybeCallNotifyWinEvent(
EVENT_OBJECT_LOCATIONCHANGE, unique_id_win());
}
@@ -3201,7 +3200,7 @@ BrowserAccessibilityWin* BrowserAccessibilityWin::GetTargetFromChildID(
}
HRESULT BrowserAccessibilityWin::GetStringAttributeAsBstr(
- AccessibilityNodeData::StringAttribute attribute,
+ ui::AXStringAttribute attribute,
BSTR* value_bstr) {
base::string16 str;
@@ -3218,49 +3217,49 @@ HRESULT BrowserAccessibilityWin::GetStringAttributeAsBstr(
}
void BrowserAccessibilityWin::StringAttributeToIA2(
- AccessibilityNodeData::StringAttribute attribute,
+ ui::AXStringAttribute attribute,
const char* ia2_attr) {
base::string16 value;
if (GetString16Attribute(attribute, &value))
- ia2_attributes_.push_back(ASCIIToUTF16(ia2_attr) + L":" + value);
+ ia2_attributes_.push_back(base::ASCIIToUTF16(ia2_attr) + L":" + value);
}
void BrowserAccessibilityWin::BoolAttributeToIA2(
- AccessibilityNodeData::BoolAttribute attribute,
+ ui::AXBoolAttribute attribute,
const char* ia2_attr) {
bool value;
if (GetBoolAttribute(attribute, &value)) {
- ia2_attributes_.push_back((ASCIIToUTF16(ia2_attr) + L":") +
+ ia2_attributes_.push_back((base::ASCIIToUTF16(ia2_attr) + L":") +
(value ? L"true" : L"false"));
}
}
void BrowserAccessibilityWin::IntAttributeToIA2(
- AccessibilityNodeData::IntAttribute attribute,
+ ui::AXIntAttribute attribute,
const char* ia2_attr) {
int value;
if (GetIntAttribute(attribute, &value)) {
- ia2_attributes_.push_back(ASCIIToUTF16(ia2_attr) + L":" +
+ ia2_attributes_.push_back(base::ASCIIToUTF16(ia2_attr) + L":" +
base::IntToString16(value));
}
}
base::string16 BrowserAccessibilityWin::GetValueText() {
float fval;
- base::string16 value = UTF8ToUTF16(this->value());
+ base::string16 value = base::UTF8ToUTF16(this->value());
if (value.empty() &&
- GetFloatAttribute(AccessibilityNodeData::ATTR_VALUE_FOR_RANGE, &fval)) {
- value = UTF8ToUTF16(base::DoubleToString(fval));
+ GetFloatAttribute(ui::AX_ATTR_VALUE_FOR_RANGE, &fval)) {
+ value = base::UTF8ToUTF16(base::DoubleToString(fval));
}
return value;
}
base::string16 BrowserAccessibilityWin::TextForIAccessibleText() {
if (IsEditableText())
- return UTF8ToUTF16(value());
- return (blink_role() == blink::WebAXRoleStaticText) ?
- UTF8ToUTF16(name()) : hypertext_;
+ return base::UTF8ToUTF16(value());
+ return (GetRole() == ui::AX_ROLE_STATIC_TEXT) ?
+ base::UTF8ToUTF16(name()) : hypertext_;
}
void BrowserAccessibilityWin::HandleSpecialTextOffset(
@@ -3295,14 +3294,13 @@ LONG BrowserAccessibilityWin::FindBoundary(
HandleSpecialTextOffset(text, &start_offset);
ui::TextBoundaryType boundary = IA2TextBoundaryToTextBoundary(ia2_boundary);
const std::vector<int32>& line_breaks = GetIntListAttribute(
- AccessibilityNodeData::ATTR_LINE_BREAKS);
+ ui::AX_ATTR_LINE_BREAKS);
return ui::FindAccessibleTextBoundary(
text, line_breaks, boundary, start_offset, direction);
}
-BrowserAccessibilityWin* BrowserAccessibilityWin::GetFromRendererID(
- int32 renderer_id) {
- return manager()->GetFromRendererID(renderer_id)->ToBrowserAccessibilityWin();
+BrowserAccessibilityWin* BrowserAccessibilityWin::GetFromID(int32 id) {
+ return manager()->GetFromID(id)->ToBrowserAccessibilityWin();
}
void BrowserAccessibilityWin::InitRoleAndState() {
@@ -3310,95 +3308,95 @@ void BrowserAccessibilityWin::InitRoleAndState() {
ia2_state_ = IA2_STATE_OPAQUE;
ia2_attributes_.clear();
- if (HasState(blink::WebAXStateBusy))
+ if (HasState(ui::AX_STATE_BUSY))
ia_state_ |= STATE_SYSTEM_BUSY;
- if (HasState(blink::WebAXStateChecked))
+ if (HasState(ui::AX_STATE_CHECKED))
ia_state_ |= STATE_SYSTEM_CHECKED;
- if (HasState(blink::WebAXStateCollapsed))
+ if (HasState(ui::AX_STATE_COLLAPSED))
ia_state_ |= STATE_SYSTEM_COLLAPSED;
- if (HasState(blink::WebAXStateExpanded))
+ if (HasState(ui::AX_STATE_EXPANDED))
ia_state_ |= STATE_SYSTEM_EXPANDED;
- if (HasState(blink::WebAXStateFocusable))
+ if (HasState(ui::AX_STATE_FOCUSABLE))
ia_state_ |= STATE_SYSTEM_FOCUSABLE;
- if (HasState(blink::WebAXStateHaspopup))
+ if (HasState(ui::AX_STATE_HASPOPUP))
ia_state_ |= STATE_SYSTEM_HASPOPUP;
- if (HasState(blink::WebAXStateHovered))
+ if (HasState(ui::AX_STATE_HOVERED))
ia_state_ |= STATE_SYSTEM_HOTTRACKED;
- if (HasState(blink::WebAXStateIndeterminate))
+ if (HasState(ui::AX_STATE_INDETERMINATE))
ia_state_ |= STATE_SYSTEM_INDETERMINATE;
- if (HasState(blink::WebAXStateInvisible))
+ if (HasState(ui::AX_STATE_INVISIBLE))
ia_state_ |= STATE_SYSTEM_INVISIBLE;
- if (HasState(blink::WebAXStateLinked))
+ if (HasState(ui::AX_STATE_LINKED))
ia_state_ |= STATE_SYSTEM_LINKED;
- if (HasState(blink::WebAXStateMultiselectable)) {
+ if (HasState(ui::AX_STATE_MULTISELECTABLE)) {
ia_state_ |= STATE_SYSTEM_EXTSELECTABLE;
ia_state_ |= STATE_SYSTEM_MULTISELECTABLE;
}
// TODO(ctguil): Support STATE_SYSTEM_EXTSELECTABLE/accSelect.
- if (HasState(blink::WebAXStateOffscreen))
+ if (HasState(ui::AX_STATE_OFFSCREEN))
ia_state_ |= STATE_SYSTEM_OFFSCREEN;
- if (HasState(blink::WebAXStatePressed))
+ if (HasState(ui::AX_STATE_PRESSED))
ia_state_ |= STATE_SYSTEM_PRESSED;
- if (HasState(blink::WebAXStateProtected))
+ if (HasState(ui::AX_STATE_PROTECTED))
ia_state_ |= STATE_SYSTEM_PROTECTED;
- if (HasState(blink::WebAXStateRequired))
+ if (HasState(ui::AX_STATE_REQUIRED))
ia2_state_ |= IA2_STATE_REQUIRED;
- if (HasState(blink::WebAXStateSelectable))
+ if (HasState(ui::AX_STATE_SELECTABLE))
ia_state_ |= STATE_SYSTEM_SELECTABLE;
- if (HasState(blink::WebAXStateSelected))
+ if (HasState(ui::AX_STATE_SELECTED))
ia_state_ |= STATE_SYSTEM_SELECTED;
- if (HasState(blink::WebAXStateVisited))
+ if (HasState(ui::AX_STATE_VISITED))
ia_state_ |= STATE_SYSTEM_TRAVERSED;
- if (!HasState(blink::WebAXStateEnabled))
+ if (!HasState(ui::AX_STATE_ENABLED))
ia_state_ |= STATE_SYSTEM_UNAVAILABLE;
- if (HasState(blink::WebAXStateVertical)) {
+ if (HasState(ui::AX_STATE_VERTICAL)) {
ia2_state_ |= IA2_STATE_VERTICAL;
} else {
ia2_state_ |= IA2_STATE_HORIZONTAL;
}
- if (HasState(blink::WebAXStateVisited))
+ if (HasState(ui::AX_STATE_VISITED))
ia_state_ |= STATE_SYSTEM_TRAVERSED;
// WebKit marks everything as readonly unless it's editable text, so if it's
// not readonly, mark it as editable now. The final computation of the
// READONLY state for MSAA is below, after the switch.
- if (!HasState(blink::WebAXStateReadonly))
+ if (!HasState(ui::AX_STATE_READ_ONLY))
ia2_state_ |= IA2_STATE_EDITABLE;
base::string16 invalid;
if (GetHtmlAttribute("aria-invalid", &invalid))
ia2_state_ |= IA2_STATE_INVALID_ENTRY;
- if (GetBoolAttribute(AccessibilityNodeData::ATTR_BUTTON_MIXED))
+ if (GetBoolAttribute(ui::AX_ATTR_BUTTON_MIXED))
ia_state_ |= STATE_SYSTEM_MIXED;
- if (GetBoolAttribute(AccessibilityNodeData::ATTR_CAN_SET_VALUE))
+ if (GetBoolAttribute(ui::AX_ATTR_CAN_SET_VALUE))
ia2_state_ |= IA2_STATE_EDITABLE;
base::string16 html_tag = GetString16Attribute(
- AccessibilityNodeData::ATTR_HTML_TAG);
+ ui::AX_ATTR_HTML_TAG);
ia_role_ = 0;
ia2_role_ = 0;
- switch (blink_role()) {
- case blink::WebAXRoleAlert:
+ switch (GetRole()) {
+ case ui::AX_ROLE_ALERT:
ia_role_ = ROLE_SYSTEM_ALERT;
break;
- case blink::WebAXRoleAlertDialog:
+ case ui::AX_ROLE_ALERT_DIALOG:
ia_role_ = ROLE_SYSTEM_DIALOG;
break;
- case blink::WebAXRoleApplication:
+ case ui::AX_ROLE_APPLICATION:
ia_role_ = ROLE_SYSTEM_APPLICATION;
break;
- case blink::WebAXRoleArticle:
+ case ui::AX_ROLE_ARTICLE:
ia_role_ = ROLE_SYSTEM_GROUPING;
ia2_role_ = IA2_ROLE_SECTION;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleBusyIndicator:
+ case ui::AX_ROLE_BUSY_INDICATOR:
ia_role_ = ROLE_SYSTEM_ANIMATION;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleButton:
+ case ui::AX_ROLE_BUTTON:
ia_role_ = ROLE_SYSTEM_PUSHBUTTON;
bool is_aria_pressed_defined;
bool is_mixed;
@@ -3409,88 +3407,88 @@ void BrowserAccessibilityWin::InitRoleAndState() {
if (is_mixed)
ia_state_ |= STATE_SYSTEM_MIXED;
break;
- case blink::WebAXRoleCanvas:
- if (GetBoolAttribute(AccessibilityNodeData::ATTR_CANVAS_HAS_FALLBACK)) {
+ case ui::AX_ROLE_CANVAS:
+ if (GetBoolAttribute(ui::AX_ATTR_CANVAS_HAS_FALLBACK)) {
role_name_ = L"canvas";
ia2_role_ = IA2_ROLE_CANVAS;
} else {
ia_role_ = ROLE_SYSTEM_GRAPHIC;
}
break;
- case blink::WebAXRoleCell:
+ case ui::AX_ROLE_CELL:
ia_role_ = ROLE_SYSTEM_CELL;
break;
- case blink::WebAXRoleCheckBox:
+ case ui::AX_ROLE_CHECK_BOX:
ia_role_ = ROLE_SYSTEM_CHECKBUTTON;
break;
- case blink::WebAXRoleColorWell:
+ case ui::AX_ROLE_COLOR_WELL:
ia_role_ = ROLE_SYSTEM_CLIENT;
ia2_role_ = IA2_ROLE_COLOR_CHOOSER;
break;
- case blink::WebAXRoleColumn:
+ case ui::AX_ROLE_COLUMN:
ia_role_ = ROLE_SYSTEM_COLUMN;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleColumnHeader:
+ case ui::AX_ROLE_COLUMN_HEADER:
ia_role_ = ROLE_SYSTEM_COLUMNHEADER;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleComboBox:
+ case ui::AX_ROLE_COMBO_BOX:
ia_role_ = ROLE_SYSTEM_COMBOBOX;
break;
- case blink::WebAXRoleDiv:
+ case ui::AX_ROLE_DIV:
role_name_ = L"div";
ia2_role_ = IA2_ROLE_SECTION;
break;
- case blink::WebAXRoleDefinition:
+ case ui::AX_ROLE_DEFINITION:
role_name_ = html_tag;
ia2_role_ = IA2_ROLE_PARAGRAPH;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleDescriptionListDetail:
+ case ui::AX_ROLE_DESCRIPTION_LIST_DETAIL:
role_name_ = html_tag;
ia2_role_ = IA2_ROLE_PARAGRAPH;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleDescriptionListTerm:
+ case ui::AX_ROLE_DESCRIPTION_LIST_TERM:
ia_role_ = ROLE_SYSTEM_LISTITEM;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleDialog:
+ case ui::AX_ROLE_DIALOG:
ia_role_ = ROLE_SYSTEM_DIALOG;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleDisclosureTriangle:
+ case ui::AX_ROLE_DISCLOSURE_TRIANGLE:
ia_role_ = ROLE_SYSTEM_OUTLINEBUTTON;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleDocument:
- case blink::WebAXRoleRootWebArea:
- case blink::WebAXRoleWebArea:
+ case ui::AX_ROLE_DOCUMENT:
+ case ui::AX_ROLE_ROOT_WEB_AREA:
+ case ui::AX_ROLE_WEB_AREA:
ia_role_ = ROLE_SYSTEM_DOCUMENT;
ia_state_ |= STATE_SYSTEM_READONLY;
ia_state_ |= STATE_SYSTEM_FOCUSABLE;
break;
- case blink::WebAXRoleEditableText:
+ case ui::AX_ROLE_EDITABLE_TEXT:
ia_role_ = ROLE_SYSTEM_TEXT;
ia2_state_ |= IA2_STATE_SINGLE_LINE;
ia2_state_ |= IA2_STATE_EDITABLE;
break;
- case blink::WebAXRoleForm:
+ case ui::AX_ROLE_FORM:
role_name_ = L"form";
ia2_role_ = IA2_ROLE_FORM;
break;
- case blink::WebAXRoleFooter:
+ case ui::AX_ROLE_FOOTER:
ia_role_ = IA2_ROLE_FOOTER;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleGrid:
+ case ui::AX_ROLE_GRID:
ia_role_ = ROLE_SYSTEM_TABLE;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleGroup: {
+ case ui::AX_ROLE_GROUP: {
base::string16 aria_role = GetString16Attribute(
- AccessibilityNodeData::ATTR_ROLE);
+ ui::AX_ATTR_ROLE);
if (aria_role == L"group" || html_tag == L"fieldset") {
ia_role_ = ROLE_SYSTEM_GROUPING;
} else if (html_tag == L"li") {
@@ -3505,190 +3503,191 @@ void BrowserAccessibilityWin::InitRoleAndState() {
ia_state_ |= STATE_SYSTEM_READONLY;
break;
}
- case blink::WebAXRoleGrowArea:
+ case ui::AX_ROLE_GROW_AREA:
ia_role_ = ROLE_SYSTEM_GRIP;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleHeading:
+ case ui::AX_ROLE_HEADING:
role_name_ = html_tag;
ia2_role_ = IA2_ROLE_HEADING;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleHorizontalRule:
+ case ui::AX_ROLE_HORIZONTAL_RULE:
ia_role_ = ROLE_SYSTEM_SEPARATOR;
break;
- case blink::WebAXRoleImage:
+ case ui::AX_ROLE_IFRAME:
+ ia_role_ = ROLE_SYSTEM_CLIENT;
+ ia2_role_ = IA2_ROLE_INTERNAL_FRAME;
+ break;
+ case ui::AX_ROLE_IMAGE:
ia_role_ = ROLE_SYSTEM_GRAPHIC;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleImageMap:
+ case ui::AX_ROLE_IMAGE_MAP:
role_name_ = html_tag;
ia2_role_ = IA2_ROLE_IMAGE_MAP;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleImageMapLink:
+ case ui::AX_ROLE_IMAGE_MAP_LINK:
ia_role_ = ROLE_SYSTEM_LINK;
ia_state_ |= STATE_SYSTEM_LINKED;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleLabel:
+ case ui::AX_ROLE_LABEL_TEXT:
ia_role_ = ROLE_SYSTEM_TEXT;
ia2_role_ = IA2_ROLE_LABEL;
break;
- case blink::WebAXRoleBanner:
- case blink::WebAXRoleComplementary:
- case blink::WebAXRoleContentInfo:
- case blink::WebAXRoleMain:
- case blink::WebAXRoleNavigation:
- case blink::WebAXRoleSearch:
+ 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:
ia_role_ = ROLE_SYSTEM_GROUPING;
ia2_role_ = IA2_ROLE_SECTION;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleLink:
+ case ui::AX_ROLE_LINK:
ia_role_ = ROLE_SYSTEM_LINK;
ia_state_ |= STATE_SYSTEM_LINKED;
break;
- case blink::WebAXRoleList:
+ case ui::AX_ROLE_LIST:
ia_role_ = ROLE_SYSTEM_LIST;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleListBox:
+ case ui::AX_ROLE_LIST_BOX:
ia_role_ = ROLE_SYSTEM_LIST;
break;
- case blink::WebAXRoleListBoxOption:
+ case ui::AX_ROLE_LIST_BOX_OPTION:
ia_role_ = ROLE_SYSTEM_LISTITEM;
if (ia_state_ & STATE_SYSTEM_SELECTABLE) {
ia_state_ |= STATE_SYSTEM_FOCUSABLE;
- if (HasState(blink::WebAXStateFocused))
+ if (HasState(ui::AX_STATE_FOCUSED))
ia_state_ |= STATE_SYSTEM_FOCUSED;
}
break;
- case blink::WebAXRoleListItem:
+ case ui::AX_ROLE_LIST_ITEM:
ia_role_ = ROLE_SYSTEM_LISTITEM;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleListMarker:
- ia_role_ = ROLE_SYSTEM_TEXT;
- ia_state_ |= STATE_SYSTEM_READONLY;
- break;
- case blink::WebAXRoleMath:
+ case ui::AX_ROLE_MATH_ELEMENT:
ia_role_ = ROLE_SYSTEM_EQUATION;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleMenu:
- case blink::WebAXRoleMenuButton:
+ case ui::AX_ROLE_MENU:
+ case ui::AX_ROLE_MENU_BUTTON:
ia_role_ = ROLE_SYSTEM_MENUPOPUP;
break;
- case blink::WebAXRoleMenuBar:
+ case ui::AX_ROLE_MENU_BAR:
ia_role_ = ROLE_SYSTEM_MENUBAR;
break;
- case blink::WebAXRoleMenuItem:
+ case ui::AX_ROLE_MENU_ITEM:
ia_role_ = ROLE_SYSTEM_MENUITEM;
break;
- case blink::WebAXRoleMenuListPopup:
+ case ui::AX_ROLE_MENU_LIST_POPUP:
ia_role_ = ROLE_SYSTEM_CLIENT;
break;
- case blink::WebAXRoleMenuListOption:
+ case ui::AX_ROLE_MENU_LIST_OPTION:
ia_role_ = ROLE_SYSTEM_LISTITEM;
if (ia_state_ & STATE_SYSTEM_SELECTABLE) {
ia_state_ |= STATE_SYSTEM_FOCUSABLE;
- if (HasState(blink::WebAXStateFocused))
+ if (HasState(ui::AX_STATE_FOCUSED))
ia_state_ |= STATE_SYSTEM_FOCUSED;
}
break;
- case blink::WebAXRoleNote:
+ case ui::AX_ROLE_NOTE:
ia_role_ = ROLE_SYSTEM_GROUPING;
ia2_role_ = IA2_ROLE_NOTE;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleOutline:
+ case ui::AX_ROLE_OUTLINE:
ia_role_ = ROLE_SYSTEM_OUTLINE;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleParagraph:
+ case ui::AX_ROLE_PARAGRAPH:
role_name_ = L"P";
ia2_role_ = IA2_ROLE_PARAGRAPH;
break;
- case blink::WebAXRolePopUpButton:
+ case ui::AX_ROLE_POP_UP_BUTTON:
if (html_tag == L"select") {
ia_role_ = ROLE_SYSTEM_COMBOBOX;
} else {
ia_role_ = ROLE_SYSTEM_BUTTONMENU;
}
break;
- case blink::WebAXRoleProgressIndicator:
+ case ui::AX_ROLE_PROGRESS_INDICATOR:
ia_role_ = ROLE_SYSTEM_PROGRESSBAR;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleRadioButton:
+ case ui::AX_ROLE_RADIO_BUTTON:
ia_role_ = ROLE_SYSTEM_RADIOBUTTON;
break;
- case blink::WebAXRoleRadioGroup:
+ case ui::AX_ROLE_RADIO_GROUP:
ia_role_ = ROLE_SYSTEM_GROUPING;
ia2_role_ = IA2_ROLE_SECTION;
break;
- case blink::WebAXRoleRegion:
+ case ui::AX_ROLE_REGION:
ia_role_ = ROLE_SYSTEM_GROUPING;
ia2_role_ = IA2_ROLE_SECTION;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleRow:
+ case ui::AX_ROLE_ROW:
ia_role_ = ROLE_SYSTEM_ROW;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleRowHeader:
+ case ui::AX_ROLE_ROW_HEADER:
ia_role_ = ROLE_SYSTEM_ROWHEADER;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleRuler:
+ case ui::AX_ROLE_RULER:
ia_role_ = ROLE_SYSTEM_CLIENT;
ia2_role_ = IA2_ROLE_RULER;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleScrollArea:
+ case ui::AX_ROLE_SCROLL_AREA:
ia_role_ = ROLE_SYSTEM_CLIENT;
ia2_role_ = IA2_ROLE_SCROLL_PANE;
ia_state_ |= STATE_SYSTEM_READONLY;
+ ia2_state_ &= ~(IA2_STATE_EDITABLE);
break;
- case blink::WebAXRoleScrollBar:
+ case ui::AX_ROLE_SCROLL_BAR:
ia_role_ = ROLE_SYSTEM_SCROLLBAR;
break;
- case blink::WebAXRoleSlider:
+ case ui::AX_ROLE_SLIDER:
ia_role_ = ROLE_SYSTEM_SLIDER;
break;
- case blink::WebAXRoleSpinButton:
+ case ui::AX_ROLE_SPIN_BUTTON:
ia_role_ = ROLE_SYSTEM_SPINBUTTON;
break;
- case blink::WebAXRoleSpinButtonPart:
+ case ui::AX_ROLE_SPIN_BUTTON_PART:
ia_role_ = ROLE_SYSTEM_PUSHBUTTON;
break;
- case blink::WebAXRoleSplitGroup:
+ case ui::AX_ROLE_SPLIT_GROUP:
ia_role_ = ROLE_SYSTEM_CLIENT;
ia2_role_ = IA2_ROLE_SPLIT_PANE;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleAnnotation:
- case blink::WebAXRoleStaticText:
- ia_role_ = ROLE_SYSTEM_TEXT;
- ia_state_ |= STATE_SYSTEM_READONLY;
+ case ui::AX_ROLE_ANNOTATION:
+ case ui::AX_ROLE_LIST_MARKER:
+ case ui::AX_ROLE_STATIC_TEXT:
+ ia_role_ = ROLE_SYSTEM_STATICTEXT;
break;
- case blink::WebAXRoleStatus:
+ case ui::AX_ROLE_STATUS:
ia_role_ = ROLE_SYSTEM_STATUSBAR;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleSplitter:
+ case ui::AX_ROLE_SPLITTER:
ia_role_ = ROLE_SYSTEM_SEPARATOR;
break;
- case blink::WebAXRoleSVGRoot:
+ case ui::AX_ROLE_SVG_ROOT:
ia_role_ = ROLE_SYSTEM_GRAPHIC;
break;
- case blink::WebAXRoleTab:
+ case ui::AX_ROLE_TAB:
ia_role_ = ROLE_SYSTEM_PAGETAB;
break;
- case blink::WebAXRoleTable: {
+ case ui::AX_ROLE_TABLE: {
base::string16 aria_role = GetString16Attribute(
- AccessibilityNodeData::ATTR_ROLE);
+ ui::AX_ATTR_ROLE);
if (aria_role == L"treegrid") {
ia_role_ = ROLE_SYSTEM_OUTLINE;
} else {
@@ -3697,77 +3696,77 @@ void BrowserAccessibilityWin::InitRoleAndState() {
}
break;
}
- case blink::WebAXRoleTableHeaderContainer:
+ case ui::AX_ROLE_TABLE_HEADER_CONTAINER:
ia_role_ = ROLE_SYSTEM_GROUPING;
ia2_role_ = IA2_ROLE_SECTION;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleTabList:
+ case ui::AX_ROLE_TAB_LIST:
ia_role_ = ROLE_SYSTEM_PAGETABLIST;
break;
- case blink::WebAXRoleTabPanel:
+ case ui::AX_ROLE_TAB_PANEL:
ia_role_ = ROLE_SYSTEM_PROPERTYPAGE;
break;
- case blink::WebAXRoleToggleButton:
+ case ui::AX_ROLE_TOGGLE_BUTTON:
ia_role_ = ROLE_SYSTEM_PUSHBUTTON;
ia2_role_ = IA2_ROLE_TOGGLE_BUTTON;
break;
- case blink::WebAXRoleTextArea:
+ case ui::AX_ROLE_TEXT_AREA:
ia_role_ = ROLE_SYSTEM_TEXT;
ia2_state_ |= IA2_STATE_MULTI_LINE;
ia2_state_ |= IA2_STATE_EDITABLE;
ia2_state_ |= IA2_STATE_SELECTABLE_TEXT;
break;
- case blink::WebAXRoleTextField:
+ case ui::AX_ROLE_TEXT_FIELD:
ia_role_ = ROLE_SYSTEM_TEXT;
ia2_state_ |= IA2_STATE_SINGLE_LINE;
ia2_state_ |= IA2_STATE_EDITABLE;
ia2_state_ |= IA2_STATE_SELECTABLE_TEXT;
break;
- case blink::WebAXRoleTimer:
+ case ui::AX_ROLE_TIMER:
ia_role_ = ROLE_SYSTEM_CLOCK;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleToolbar:
+ case ui::AX_ROLE_TOOLBAR:
ia_role_ = ROLE_SYSTEM_TOOLBAR;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleUserInterfaceTooltip:
+ case ui::AX_ROLE_TOOLTIP:
ia_role_ = ROLE_SYSTEM_TOOLTIP;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleTree:
+ case ui::AX_ROLE_TREE:
ia_role_ = ROLE_SYSTEM_OUTLINE;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleTreeGrid:
+ case ui::AX_ROLE_TREE_GRID:
ia_role_ = ROLE_SYSTEM_OUTLINE;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleTreeItem:
+ case ui::AX_ROLE_TREE_ITEM:
ia_role_ = ROLE_SYSTEM_OUTLINEITEM;
ia_state_ |= STATE_SYSTEM_READONLY;
break;
- case blink::WebAXRoleWindow:
+ case ui::AX_ROLE_WINDOW:
ia_role_ = ROLE_SYSTEM_WINDOW;
break;
// TODO(dmazzoni): figure out the proper MSAA role for all of these.
- case blink::WebAXRoleBrowser:
- case blink::WebAXRoleDirectory:
- case blink::WebAXRoleDrawer:
- case blink::WebAXRoleHelpTag:
- case blink::WebAXRoleIgnored:
- case blink::WebAXRoleIncrementor:
- case blink::WebAXRoleLog:
- case blink::WebAXRoleMarquee:
- case blink::WebAXRoleMatte:
- case blink::WebAXRolePresentational:
- case blink::WebAXRoleRulerMarker:
- case blink::WebAXRoleSheet:
- case blink::WebAXRoleSliderThumb:
- case blink::WebAXRoleSystemWide:
- case blink::WebAXRoleValueIndicator:
+ case ui::AX_ROLE_BROWSER:
+ case ui::AX_ROLE_DIRECTORY:
+ case ui::AX_ROLE_DRAWER:
+ case ui::AX_ROLE_HELP_TAG:
+ case ui::AX_ROLE_IGNORED:
+ case ui::AX_ROLE_INCREMENTOR:
+ case ui::AX_ROLE_LOG:
+ case ui::AX_ROLE_MARQUEE:
+ case ui::AX_ROLE_MATTE:
+ case ui::AX_ROLE_PRESENTATIONAL:
+ case ui::AX_ROLE_RULER_MARKER:
+ case ui::AX_ROLE_SHEET:
+ case ui::AX_ROLE_SLIDER_THUMB:
+ case ui::AX_ROLE_SYSTEM_WIDE:
+ case ui::AX_ROLE_VALUE_INDICATOR:
default:
ia_role_ = ROLE_SYSTEM_CLIENT;
break;
@@ -3779,13 +3778,13 @@ void BrowserAccessibilityWin::InitRoleAndState() {
// aria-readonly attribute and for a few roles (in the switch above).
// We clear the READONLY state on focusable controls and on a document.
// Everything else, the majority of objects, do not have this state set.
- if (HasState(blink::WebAXStateFocusable) &&
+ if (HasState(ui::AX_STATE_FOCUSABLE) &&
ia_role_ != ROLE_SYSTEM_DOCUMENT) {
ia_state_ &= ~(STATE_SYSTEM_READONLY);
}
- if (!HasState(blink::WebAXStateReadonly))
+ if (!HasState(ui::AX_STATE_READ_ONLY))
ia_state_ &= ~(STATE_SYSTEM_READONLY);
- if (GetBoolAttribute(AccessibilityNodeData::ATTR_ARIA_READONLY))
+ if (GetBoolAttribute(ui::AX_ATTR_ARIA_READONLY))
ia_state_ |= STATE_SYSTEM_READONLY;
// The role should always be set.
diff --git a/chromium/content/browser/accessibility/browser_accessibility_win.h b/chromium/content/browser/accessibility/browser_accessibility_win.h
index 69d6efb7631..8dca2a8a9c8 100644
--- a/chromium/content/browser/accessibility/browser_accessibility_win.h
+++ b/chromium/content/browser/accessibility/browser_accessibility_win.h
@@ -60,29 +60,29 @@ BrowserAccessibilityWin
public:
BEGIN_COM_MAP(BrowserAccessibilityWin)
COM_INTERFACE_ENTRY2(IDispatch, IAccessible2)
- COM_INTERFACE_ENTRY2(IAccessible, IAccessible2)
- COM_INTERFACE_ENTRY2(IAccessibleText, IAccessibleHypertext)
+ COM_INTERFACE_ENTRY(IAccessible)
COM_INTERFACE_ENTRY(IAccessible2)
COM_INTERFACE_ENTRY(IAccessibleApplication)
+ COM_INTERFACE_ENTRY(IAccessibleEx)
COM_INTERFACE_ENTRY(IAccessibleHyperlink)
COM_INTERFACE_ENTRY(IAccessibleHypertext)
COM_INTERFACE_ENTRY(IAccessibleImage)
COM_INTERFACE_ENTRY(IAccessibleTable)
COM_INTERFACE_ENTRY(IAccessibleTable2)
COM_INTERFACE_ENTRY(IAccessibleTableCell)
+ COM_INTERFACE_ENTRY(IAccessibleText)
COM_INTERFACE_ENTRY(IAccessibleValue)
+ COM_INTERFACE_ENTRY(IRawElementProviderSimple)
COM_INTERFACE_ENTRY(IServiceProvider)
COM_INTERFACE_ENTRY(ISimpleDOMDocument)
COM_INTERFACE_ENTRY(ISimpleDOMNode)
COM_INTERFACE_ENTRY(ISimpleDOMText)
- COM_INTERFACE_ENTRY(IAccessibleEx)
- COM_INTERFACE_ENTRY(IRawElementProviderSimple)
END_COM_MAP()
// Represents a non-static text node in IAccessibleHypertext. This character
// is embedded in the response to IAccessibleText::get_text, indicating the
// position where a non-static text child object appears.
- CONTENT_EXPORT static const char16 kEmbeddedCharacter[];
+ CONTENT_EXPORT static const base::char16 kEmbeddedCharacter[];
// Mappings from roles and states to human readable strings. Initialize
// with |InitializeStringMaps|.
@@ -100,13 +100,12 @@ BrowserAccessibilityWin
//
// BrowserAccessibility methods.
//
- CONTENT_EXPORT virtual void PreInitialize() OVERRIDE;
- CONTENT_EXPORT virtual void PostInitialize() OVERRIDE;
+ CONTENT_EXPORT virtual void OnDataChanged() OVERRIDE;
+ CONTENT_EXPORT virtual void OnUpdateFinished() OVERRIDE;
CONTENT_EXPORT virtual void NativeAddReference() OVERRIDE;
CONTENT_EXPORT virtual void NativeReleaseReference() OVERRIDE;
CONTENT_EXPORT virtual bool IsNative() const OVERRIDE;
- CONTENT_EXPORT virtual void SetLocation(const gfx::Rect& new_location)
- OVERRIDE;
+ CONTENT_EXPORT virtual void OnLocationChanged() const OVERRIDE;
//
// IAccessible methods.
@@ -772,10 +771,6 @@ BrowserAccessibilityWin
return ia2_attributes_;
}
- // BrowserAccessibility::role is shadowed by IAccessible2::role, so
- // we provide an alias for it.
- int32 blink_role() const { return BrowserAccessibility::role(); }
-
private:
// Add one to the reference count and return the same object. Always
// use this method when returning a BrowserAccessibilityWin object as
@@ -790,29 +785,29 @@ BrowserAccessibilityWin
BrowserAccessibilityWin* GetTargetFromChildID(const VARIANT& var_id);
// Initialize the role and state metadata from the role enum and state
- // bitmasks defined in AccessibilityNodeData.
+ // bitmasks defined in ui::AXNodeData.
void InitRoleAndState();
// Retrieve the value of an attribute from the string attribute map and
// if found and nonempty, allocate a new BSTR (with SysAllocString)
// and return S_OK. If not found or empty, return S_FALSE.
HRESULT GetStringAttributeAsBstr(
- AccessibilityNodeData::StringAttribute attribute,
+ ui::AXStringAttribute attribute,
BSTR* value_bstr);
// If the string attribute |attribute| is present, add its value as an
// IAccessible2 attribute with the name |ia2_attr|.
- void StringAttributeToIA2(AccessibilityNodeData::StringAttribute attribute,
+ void StringAttributeToIA2(ui::AXStringAttribute attribute,
const char* ia2_attr);
// If the bool attribute |attribute| is present, add its value as an
// IAccessible2 attribute with the name |ia2_attr|.
- void BoolAttributeToIA2(AccessibilityNodeData::BoolAttribute attribute,
+ void BoolAttributeToIA2(ui::AXBoolAttribute attribute,
const char* ia2_attr);
// If the int attribute |attribute| is present, add its value as an
// IAccessible2 attribute with the name |ia2_attr|.
- void IntAttributeToIA2(AccessibilityNodeData::IntAttribute attribute,
+ void IntAttributeToIA2(ui::AXIntAttribute attribute,
const char* ia2_attr);
// Get the value text, which might come from the floating-point
@@ -838,9 +833,9 @@ BrowserAccessibilityWin
LONG start_offset,
ui::TextBoundaryDirection direction);
- // Return a pointer to the object corresponding to the given renderer_id,
+ // Return a pointer to the object corresponding to the given id,
// does not make a new reference.
- BrowserAccessibilityWin* GetFromRendererID(int32 renderer_id);
+ BrowserAccessibilityWin* GetFromID(int32 id);
// Windows-specific unique ID (unique within the browser process),
// used for get_accChild, NotifyWinEvent, and as the unique ID for
diff --git a/chromium/content/browser/accessibility/browser_accessibility_win_unittest.cc b/chromium/content/browser/accessibility/browser_accessibility_win_unittest.cc
index 408fb4df361..eca0d911711 100644
--- a/chromium/content/browser/accessibility/browser_accessibility_win_unittest.cc
+++ b/chromium/content/browser/accessibility/browser_accessibility_win_unittest.cc
@@ -11,6 +11,7 @@
#include "content/browser/accessibility/browser_accessibility_manager_win.h"
#include "content/browser/accessibility/browser_accessibility_state_impl.h"
#include "content/browser/accessibility/browser_accessibility_win.h"
+#include "content/browser/renderer_host/legacy_render_widget_host_win.h"
#include "content/common/accessibility_messages.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/win/atl_module.h"
@@ -110,38 +111,38 @@ void BrowserAccessibilityTest::SetUp() {
// Test that BrowserAccessibilityManager correctly releases the tree of
// BrowserAccessibility instances upon delete.
TEST_F(BrowserAccessibilityTest, TestNoLeaks) {
- // Create AccessibilityNodeData objects for a simple document tree,
+ // Create ui::AXNodeData objects for a simple document tree,
// representing the accessibility information used to initialize
// BrowserAccessibilityManager.
- AccessibilityNodeData button;
+ ui::AXNodeData button;
button.id = 2;
button.SetName("Button");
- button.role = blink::WebAXRoleButton;
+ button.role = ui::AX_ROLE_BUTTON;
button.state = 0;
- AccessibilityNodeData checkbox;
+ ui::AXNodeData checkbox;
checkbox.id = 3;
checkbox.SetName("Checkbox");
- checkbox.role = blink::WebAXRoleCheckBox;
+ checkbox.role = ui::AX_ROLE_CHECK_BOX;
checkbox.state = 0;
- AccessibilityNodeData root;
+ ui::AXNodeData root;
root.id = 1;
root.SetName("Document");
- root.role = blink::WebAXRoleRootWebArea;
+ root.role = ui::AX_ROLE_ROOT_WEB_AREA;
root.state = 0;
root.child_ids.push_back(2);
root.child_ids.push_back(3);
// Construct a BrowserAccessibilityManager with this
- // AccessibilityNodeData tree and a factory for an instance-counting
+ // ui::AXNodeData tree and a factory for an instance-counting
// BrowserAccessibility, and ensure that exactly 3 instances were
// created. Note that the manager takes ownership of the factory.
CountedBrowserAccessibility::reset();
scoped_ptr<BrowserAccessibilityManager> manager(
BrowserAccessibilityManager::Create(
- root, NULL, new CountedBrowserAccessibilityFactory()));
- manager->UpdateNodesForTesting(button, checkbox);
+ MakeAXTreeUpdate(root, button, checkbox),
+ NULL, new CountedBrowserAccessibilityFactory()));
ASSERT_EQ(3, CountedBrowserAccessibility::num_instances());
// Delete the manager and test that all 3 instances are deleted.
@@ -151,8 +152,8 @@ TEST_F(BrowserAccessibilityTest, TestNoLeaks) {
// Construct a manager again, and this time use the IAccessible interface
// to get new references to two of the three nodes in the tree.
manager.reset(BrowserAccessibilityManager::Create(
- root, NULL, new CountedBrowserAccessibilityFactory()));
- manager->UpdateNodesForTesting(button, checkbox);
+ MakeAXTreeUpdate(root, button, checkbox),
+ NULL, new CountedBrowserAccessibilityFactory()));
ASSERT_EQ(3, CountedBrowserAccessibility::num_instances());
IAccessible* root_accessible =
manager->GetRoot()->ToBrowserAccessibilityWin();
@@ -179,30 +180,30 @@ TEST_F(BrowserAccessibilityTest, TestNoLeaks) {
}
TEST_F(BrowserAccessibilityTest, TestChildrenChange) {
- // Create AccessibilityNodeData objects for a simple document tree,
+ // Create ui::AXNodeData objects for a simple document tree,
// representing the accessibility information used to initialize
// BrowserAccessibilityManager.
- AccessibilityNodeData text;
+ ui::AXNodeData text;
text.id = 2;
- text.role = blink::WebAXRoleStaticText;
+ text.role = ui::AX_ROLE_STATIC_TEXT;
text.SetName("old text");
text.state = 0;
- AccessibilityNodeData root;
+ ui::AXNodeData root;
root.id = 1;
root.SetName("Document");
- root.role = blink::WebAXRoleRootWebArea;
+ root.role = ui::AX_ROLE_ROOT_WEB_AREA;
root.state = 0;
root.child_ids.push_back(2);
// Construct a BrowserAccessibilityManager with this
- // AccessibilityNodeData tree and a factory for an instance-counting
+ // ui::AXNodeData tree and a factory for an instance-counting
// BrowserAccessibility.
CountedBrowserAccessibility::reset();
scoped_ptr<BrowserAccessibilityManager> manager(
BrowserAccessibilityManager::Create(
- root, NULL, new CountedBrowserAccessibilityFactory()));
- manager->UpdateNodesForTesting(text);
+ MakeAXTreeUpdate(root, text),
+ NULL, new CountedBrowserAccessibilityFactory()));
// Query for the text IAccessible and verify that it returns "old text" as its
// value.
@@ -227,14 +228,14 @@ TEST_F(BrowserAccessibilityTest, TestChildrenChange) {
text_accessible.Release();
// Notify the BrowserAccessibilityManager that the text child has changed.
- AccessibilityNodeData text2;
+ ui::AXNodeData text2;
text2.id = 2;
- text2.role = blink::WebAXRoleStaticText;
+ text2.role = ui::AX_ROLE_STATIC_TEXT;
text2.SetName("new text");
text2.SetName("old text");
AccessibilityHostMsg_EventParams param;
- param.event_type = blink::WebAXEventChildrenChanged;
- param.nodes.push_back(text2);
+ param.event_type = ui::AX_EVENT_CHILDREN_CHANGED;
+ param.update.nodes.push_back(text2);
param.id = text2.id;
std::vector<AccessibilityHostMsg_EventParams> events;
events.push_back(param);
@@ -263,50 +264,50 @@ TEST_F(BrowserAccessibilityTest, TestChildrenChange) {
}
TEST_F(BrowserAccessibilityTest, TestChildrenChangeNoLeaks) {
- // Create AccessibilityNodeData objects for a simple document tree,
+ // Create ui::AXNodeData objects for a simple document tree,
// representing the accessibility information used to initialize
// BrowserAccessibilityManager.
- AccessibilityNodeData div;
+ ui::AXNodeData div;
div.id = 2;
- div.role = blink::WebAXRoleGroup;
+ div.role = ui::AX_ROLE_GROUP;
div.state = 0;
- AccessibilityNodeData text3;
+ ui::AXNodeData text3;
text3.id = 3;
- text3.role = blink::WebAXRoleStaticText;
+ text3.role = ui::AX_ROLE_STATIC_TEXT;
text3.state = 0;
- AccessibilityNodeData text4;
+ ui::AXNodeData text4;
text4.id = 4;
- text4.role = blink::WebAXRoleStaticText;
+ text4.role = ui::AX_ROLE_STATIC_TEXT;
text4.state = 0;
div.child_ids.push_back(3);
div.child_ids.push_back(4);
- AccessibilityNodeData root;
+ ui::AXNodeData root;
root.id = 1;
- root.role = blink::WebAXRoleRootWebArea;
+ root.role = ui::AX_ROLE_ROOT_WEB_AREA;
root.state = 0;
root.child_ids.push_back(2);
// Construct a BrowserAccessibilityManager with this
- // AccessibilityNodeData tree and a factory for an instance-counting
+ // ui::AXNodeData tree and a factory for an instance-counting
// BrowserAccessibility and ensure that exactly 4 instances were
// created. Note that the manager takes ownership of the factory.
CountedBrowserAccessibility::reset();
scoped_ptr<BrowserAccessibilityManager> manager(
BrowserAccessibilityManager::Create(
- root, NULL, new CountedBrowserAccessibilityFactory()));
- manager->UpdateNodesForTesting(div, text3, text4);
+ MakeAXTreeUpdate(root, div, text3, text4),
+ NULL, new CountedBrowserAccessibilityFactory()));
ASSERT_EQ(4, CountedBrowserAccessibility::num_instances());
// Notify the BrowserAccessibilityManager that the div node and its children
// were removed and ensure that only one BrowserAccessibility instance exists.
root.child_ids.clear();
AccessibilityHostMsg_EventParams param;
- param.event_type = blink::WebAXEventChildrenChanged;
- param.nodes.push_back(root);
+ param.event_type = ui::AX_EVENT_CHILDREN_CHANGED;
+ param.update.nodes.push_back(root);
param.id = root.id;
std::vector<AccessibilityHostMsg_EventParams> events;
events.push_back(param);
@@ -322,27 +323,27 @@ TEST_F(BrowserAccessibilityTest, TestChildrenChangeNoLeaks) {
TEST_F(BrowserAccessibilityTest, TestTextBoundaries) {
std::string text1_value = "One two three.\nFour five six.";
- AccessibilityNodeData text1;
+ ui::AXNodeData text1;
text1.id = 11;
- text1.role = blink::WebAXRoleTextField;
+ text1.role = ui::AX_ROLE_TEXT_FIELD;
text1.state = 0;
- text1.AddStringAttribute(AccessibilityNodeData::ATTR_VALUE, text1_value);
+ text1.AddStringAttribute(ui::AX_ATTR_VALUE, text1_value);
std::vector<int32> line_breaks;
line_breaks.push_back(15);
text1.AddIntListAttribute(
- AccessibilityNodeData::ATTR_LINE_BREAKS, line_breaks);
+ ui::AX_ATTR_LINE_BREAKS, line_breaks);
- AccessibilityNodeData root;
+ ui::AXNodeData root;
root.id = 1;
- root.role = blink::WebAXRoleRootWebArea;
+ root.role = ui::AX_ROLE_ROOT_WEB_AREA;
root.state = 0;
root.child_ids.push_back(11);
CountedBrowserAccessibility::reset();
scoped_ptr<BrowserAccessibilityManager> manager(
BrowserAccessibilityManager::Create(
- root, NULL, new CountedBrowserAccessibilityFactory()));
- manager->UpdateNodesForTesting(text1);
+ MakeAXTreeUpdate(root, text1),
+ NULL, new CountedBrowserAccessibilityFactory()));
ASSERT_EQ(2, CountedBrowserAccessibility::num_instances());
BrowserAccessibilityWin* root_obj =
@@ -419,30 +420,30 @@ TEST_F(BrowserAccessibilityTest, TestSimpleHypertext) {
const std::string text1_name = "One two three.";
const std::string text2_name = " Four five six.";
- AccessibilityNodeData text1;
+ ui::AXNodeData text1;
text1.id = 11;
- text1.role = blink::WebAXRoleStaticText;
- text1.state = 1 << blink::WebAXStateReadonly;
+ text1.role = ui::AX_ROLE_STATIC_TEXT;
+ text1.state = 1 << ui::AX_STATE_READ_ONLY;
text1.SetName(text1_name);
- AccessibilityNodeData text2;
+ ui::AXNodeData text2;
text2.id = 12;
- text2.role = blink::WebAXRoleStaticText;
- text2.state = 1 << blink::WebAXStateReadonly;
+ text2.role = ui::AX_ROLE_STATIC_TEXT;
+ text2.state = 1 << ui::AX_STATE_READ_ONLY;
text2.SetName(text2_name);
- AccessibilityNodeData root;
+ ui::AXNodeData root;
root.id = 1;
- root.role = blink::WebAXRoleRootWebArea;
- root.state = 1 << blink::WebAXStateReadonly;
+ root.role = ui::AX_ROLE_ROOT_WEB_AREA;
+ root.state = 1 << ui::AX_STATE_READ_ONLY;
root.child_ids.push_back(11);
root.child_ids.push_back(12);
CountedBrowserAccessibility::reset();
scoped_ptr<BrowserAccessibilityManager> manager(
BrowserAccessibilityManager::Create(
- root, NULL, new CountedBrowserAccessibilityFactory()));
- manager->UpdateNodesForTesting(root, text1, text2);
+ MakeAXTreeUpdate(root, root, text1, text2),
+ NULL, new CountedBrowserAccessibilityFactory()));
ASSERT_EQ(3, CountedBrowserAccessibility::num_instances());
BrowserAccessibilityWin* root_obj =
@@ -487,42 +488,42 @@ TEST_F(BrowserAccessibilityTest, TestComplexHypertext) {
const std::string button1_text_name = "red";
const std::string link1_text_name = "blue";
- AccessibilityNodeData text1;
+ ui::AXNodeData text1;
text1.id = 11;
- text1.role = blink::WebAXRoleStaticText;
- text1.state = 1 << blink::WebAXStateReadonly;
+ text1.role = ui::AX_ROLE_STATIC_TEXT;
+ text1.state = 1 << ui::AX_STATE_READ_ONLY;
text1.SetName(text1_name);
- AccessibilityNodeData text2;
+ ui::AXNodeData text2;
text2.id = 12;
- text2.role = blink::WebAXRoleStaticText;
- text2.state = 1 << blink::WebAXStateReadonly;
+ text2.role = ui::AX_ROLE_STATIC_TEXT;
+ text2.state = 1 << ui::AX_STATE_READ_ONLY;
text2.SetName(text2_name);
- AccessibilityNodeData button1, button1_text;
+ ui::AXNodeData button1, button1_text;
button1.id = 13;
button1_text.id = 15;
button1_text.SetName(button1_text_name);
- button1.role = blink::WebAXRoleButton;
- button1_text.role = blink::WebAXRoleStaticText;
- button1.state = 1 << blink::WebAXStateReadonly;
- button1_text.state = 1 << blink::WebAXStateReadonly;
+ button1.role = ui::AX_ROLE_BUTTON;
+ button1_text.role = ui::AX_ROLE_STATIC_TEXT;
+ button1.state = 1 << ui::AX_STATE_READ_ONLY;
+ button1_text.state = 1 << ui::AX_STATE_READ_ONLY;
button1.child_ids.push_back(15);
- AccessibilityNodeData link1, link1_text;
+ ui::AXNodeData link1, link1_text;
link1.id = 14;
link1_text.id = 16;
link1_text.SetName(link1_text_name);
- link1.role = blink::WebAXRoleLink;
- link1_text.role = blink::WebAXRoleStaticText;
- link1.state = 1 << blink::WebAXStateReadonly;
- link1_text.state = 1 << blink::WebAXStateReadonly;
+ link1.role = ui::AX_ROLE_LINK;
+ link1_text.role = ui::AX_ROLE_STATIC_TEXT;
+ link1.state = 1 << ui::AX_STATE_READ_ONLY;
+ link1_text.state = 1 << ui::AX_STATE_READ_ONLY;
link1.child_ids.push_back(16);
- AccessibilityNodeData root;
+ ui::AXNodeData root;
root.id = 1;
- root.role = blink::WebAXRoleRootWebArea;
- root.state = 1 << blink::WebAXStateReadonly;
+ root.role = ui::AX_ROLE_ROOT_WEB_AREA;
+ root.state = 1 << ui::AX_STATE_READ_ONLY;
root.child_ids.push_back(11);
root.child_ids.push_back(13);
root.child_ids.push_back(12);
@@ -531,11 +532,10 @@ TEST_F(BrowserAccessibilityTest, TestComplexHypertext) {
CountedBrowserAccessibility::reset();
scoped_ptr<BrowserAccessibilityManager> manager(
BrowserAccessibilityManager::Create(
- root, NULL, new CountedBrowserAccessibilityFactory()));
- manager->UpdateNodesForTesting(root,
- text1, button1, button1_text,
- text2, link1, link1_text);
-
+ MakeAXTreeUpdate(root,
+ text1, button1, button1_text,
+ text2, link1, link1_text),
+ NULL, new CountedBrowserAccessibilityFactory()));
ASSERT_EQ(7, CountedBrowserAccessibility::num_instances());
BrowserAccessibilityWin* root_obj =
@@ -549,7 +549,7 @@ TEST_F(BrowserAccessibilityTest, TestComplexHypertext) {
const std::string embed = base::UTF16ToUTF8(
BrowserAccessibilityWin::kEmbeddedCharacter);
EXPECT_EQ(text1_name + embed + text2_name + embed,
- UTF16ToUTF8(base::string16(text)));
+ base::UTF16ToUTF8(base::string16(text)));
text.Reset();
long hyperlink_count;
@@ -602,12 +602,14 @@ TEST_F(BrowserAccessibilityTest, TestCreateEmptyDocument) {
// Try creating an empty document with busy state. Readonly is
// set automatically.
CountedBrowserAccessibility::reset();
- const int32 busy_state = 1 << blink::WebAXStateBusy;
- const int32 readonly_state = 1 << blink::WebAXStateReadonly;
- const int32 enabled_state = 1 << blink::WebAXStateEnabled;
+ const int32 busy_state = 1 << ui::AX_STATE_BUSY;
+ const int32 readonly_state = 1 << ui::AX_STATE_READ_ONLY;
+ const int32 enabled_state = 1 << ui::AX_STATE_ENABLED;
+ scoped_ptr<content::LegacyRenderWidgetHostHWND> accessible_hwnd(
+ content::LegacyRenderWidgetHostHWND::Create(GetDesktopWindow()));
scoped_ptr<BrowserAccessibilityManager> manager(
new BrowserAccessibilityManagerWin(
- GetDesktopWindow(),
+ accessible_hwnd.get(),
NULL,
BrowserAccessibilityManagerWin::GetEmptyDocument(),
NULL,
@@ -615,73 +617,72 @@ TEST_F(BrowserAccessibilityTest, TestCreateEmptyDocument) {
// Verify the root is as we expect by default.
BrowserAccessibility* root = manager->GetRoot();
- EXPECT_EQ(0, root->renderer_id());
- EXPECT_EQ(blink::WebAXRoleRootWebArea, root->role());
- EXPECT_EQ(busy_state | readonly_state | enabled_state, root->state());
+ EXPECT_EQ(0, root->GetId());
+ EXPECT_EQ(ui::AX_ROLE_ROOT_WEB_AREA, root->GetRole());
+ EXPECT_EQ(busy_state | readonly_state | enabled_state, root->GetState());
// Tree with a child textfield.
- AccessibilityNodeData tree1_1;
+ ui::AXNodeData tree1_1;
tree1_1.id = 1;
- tree1_1.role = blink::WebAXRoleRootWebArea;
+ tree1_1.role = ui::AX_ROLE_ROOT_WEB_AREA;
tree1_1.child_ids.push_back(2);
- AccessibilityNodeData tree1_2;
+ ui::AXNodeData tree1_2;
tree1_2.id = 2;
- tree1_2.role = blink::WebAXRoleTextField;
+ tree1_2.role = ui::AX_ROLE_TEXT_FIELD;
// Process a load complete.
std::vector<AccessibilityHostMsg_EventParams> params;
params.push_back(AccessibilityHostMsg_EventParams());
AccessibilityHostMsg_EventParams* msg = &params[0];
- msg->event_type = blink::WebAXEventLoadComplete;
- msg->nodes.push_back(tree1_1);
- msg->nodes.push_back(tree1_2);
+ msg->event_type = ui::AX_EVENT_LOAD_COMPLETE;
+ msg->update.nodes.push_back(tree1_1);
+ msg->update.nodes.push_back(tree1_2);
msg->id = tree1_1.id;
manager->OnAccessibilityEvents(params);
// Save for later comparison.
- BrowserAccessibility* acc1_2 = manager->GetFromRendererID(2);
+ BrowserAccessibility* acc1_2 = manager->GetFromID(2);
// Verify the root has changed.
EXPECT_NE(root, manager->GetRoot());
// And the proper child remains.
- EXPECT_EQ(blink::WebAXRoleTextField, acc1_2->role());
- EXPECT_EQ(2, acc1_2->renderer_id());
+ EXPECT_EQ(ui::AX_ROLE_TEXT_FIELD, acc1_2->GetRole());
+ EXPECT_EQ(2, acc1_2->GetId());
// Tree with a child button.
- AccessibilityNodeData tree2_1;
+ ui::AXNodeData tree2_1;
tree2_1.id = 1;
- tree2_1.role = blink::WebAXRoleRootWebArea;
+ tree2_1.role = ui::AX_ROLE_ROOT_WEB_AREA;
tree2_1.child_ids.push_back(3);
- AccessibilityNodeData tree2_2;
+ ui::AXNodeData tree2_2;
tree2_2.id = 3;
- tree2_2.role = blink::WebAXRoleButton;
+ tree2_2.role = ui::AX_ROLE_BUTTON;
- msg->nodes.clear();
- msg->nodes.push_back(tree2_1);
- msg->nodes.push_back(tree2_2);
+ msg->update.nodes.clear();
+ msg->update.nodes.push_back(tree2_1);
+ msg->update.nodes.push_back(tree2_2);
msg->id = tree2_1.id;
// Fire another load complete.
manager->OnAccessibilityEvents(params);
- BrowserAccessibility* acc2_2 = manager->GetFromRendererID(3);
+ BrowserAccessibility* acc2_2 = manager->GetFromID(3);
// Verify the root has changed.
EXPECT_NE(root, manager->GetRoot());
// And the new child exists.
- EXPECT_EQ(blink::WebAXRoleButton, acc2_2->role());
- EXPECT_EQ(3, acc2_2->renderer_id());
+ EXPECT_EQ(ui::AX_ROLE_BUTTON, acc2_2->GetRole());
+ EXPECT_EQ(3, acc2_2->GetId());
// Ensure we properly cleaned up.
manager.reset();
ASSERT_EQ(0, CountedBrowserAccessibility::num_instances());
}
-#if defined(USE_AURA)
TEST(BrowserAccessibilityManagerWinTest, TestAccessibleHWND) {
HWND desktop_hwnd = GetDesktopWindow();
base::win::ScopedComPtr<IAccessible> desktop_hwnd_iaccessible;
@@ -690,9 +691,12 @@ TEST(BrowserAccessibilityManagerWinTest, TestAccessibleHWND) {
IID_IAccessible,
reinterpret_cast<void**>(desktop_hwnd_iaccessible.Receive())));
+ scoped_ptr<content::LegacyRenderWidgetHostHWND> accessible_hwnd(
+ content::LegacyRenderWidgetHostHWND::Create(GetDesktopWindow()));
+
scoped_ptr<BrowserAccessibilityManagerWin> manager(
new BrowserAccessibilityManagerWin(
- desktop_hwnd,
+ accessible_hwnd.get(),
desktop_hwnd_iaccessible,
BrowserAccessibilityManagerWin::GetEmptyDocument(),
NULL));
@@ -715,9 +719,11 @@ TEST(BrowserAccessibilityManagerWinTest, TestAccessibleHWND) {
ASSERT_EQ(NULL, manager->parent_hwnd());
// Now create it again.
+ accessible_hwnd = content::LegacyRenderWidgetHostHWND::Create(
+ GetDesktopWindow());
manager.reset(
new BrowserAccessibilityManagerWin(
- desktop_hwnd,
+ accessible_hwnd.get(),
desktop_hwnd_iaccessible,
BrowserAccessibilityManagerWin::GetEmptyDocument(),
NULL));
@@ -729,6 +735,5 @@ TEST(BrowserAccessibilityManagerWinTest, TestAccessibleHWND) {
// crash on destruction (to be caught by SyzyASAN or other tools).
manager.reset(NULL);
}
-#endif
} // namespace content
diff --git a/chromium/content/browser/accessibility/cross_platform_accessibility_browsertest.cc b/chromium/content/browser/accessibility/cross_platform_accessibility_browsertest.cc
index f0aa13ff921..06b72962fd2 100644
--- a/chromium/content/browser/accessibility/cross_platform_accessibility_browsertest.cc
+++ b/chromium/content/browser/accessibility/cross_platform_accessibility_browsertest.cc
@@ -10,10 +10,12 @@
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_widget_host_view.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 "content/test/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
+#include "ui/accessibility/ax_node.h"
+#include "ui/accessibility/ax_tree.h"
#if defined(OS_WIN)
#include <atlbase.h>
@@ -37,21 +39,21 @@ class CrossPlatformAccessibilityBrowserTest : public ContentBrowserTest {
// Tell the renderer to send an accessibility tree, then wait for the
// notification that it's been received.
- const AccessibilityNodeDataTreeNode& GetAccessibilityNodeDataTree(
+ const ui::AXTree& GetAXTree(
AccessibilityMode accessibility_mode = AccessibilityModeComplete) {
AccessibilityNotificationWaiter waiter(
- shell(), accessibility_mode, blink::WebAXEventLayoutComplete);
+ shell(), accessibility_mode, ui::AX_EVENT_LAYOUT_COMPLETE);
waiter.WaitForNotification();
- return waiter.GetAccessibilityNodeDataTree();
+ return waiter.GetAXTree();
}
// Make sure each node in the tree has an unique id.
void RecursiveAssertUniqueIds(
- const AccessibilityNodeDataTreeNode& node, base::hash_set<int>* ids) {
- ASSERT_TRUE(ids->find(node.id) == ids->end());
- ids->insert(node.id);
- for (size_t i = 0; i < node.children.size(); i++)
- RecursiveAssertUniqueIds(node.children[i], ids);
+ const ui::AXNode* node, base::hash_set<int>* ids) {
+ ASSERT_TRUE(ids->find(node->id()) == ids->end());
+ ids->insert(node->id());
+ for (int i = 0; i < node->child_count(); i++)
+ RecursiveAssertUniqueIds(node->ChildAtIndex(i), ids);
}
// ContentBrowserTest
@@ -59,12 +61,12 @@ class CrossPlatformAccessibilityBrowserTest : public ContentBrowserTest {
virtual void TearDownInProcessBrowserTestFixture() OVERRIDE;
protected:
- std::string GetAttr(const AccessibilityNodeData& node,
- const AccessibilityNodeData::StringAttribute attr);
- int GetIntAttr(const AccessibilityNodeData& node,
- const AccessibilityNodeData::IntAttribute attr);
- bool GetBoolAttr(const AccessibilityNodeData& node,
- const AccessibilityNodeData::BoolAttribute attr);
+ std::string GetAttr(const ui::AXNode* node,
+ const ui::AXStringAttribute attr);
+ int GetIntAttr(const ui::AXNode* node,
+ const ui::AXIntAttribute attr);
+ bool GetBoolAttr(const ui::AXNode* node,
+ const ui::AXBoolAttribute attr);
private:
#if defined(OS_WIN)
@@ -88,38 +90,41 @@ CrossPlatformAccessibilityBrowserTest::TearDownInProcessBrowserTestFixture() {
#endif
}
-// Convenience method to get the value of a particular AccessibilityNodeData
-// node attribute as a UTF-8 string.
+// Convenience method to get the value of a particular AXNode
+// attribute as a UTF-8 string.
std::string CrossPlatformAccessibilityBrowserTest::GetAttr(
- const AccessibilityNodeData& node,
- const AccessibilityNodeData::StringAttribute attr) {
- for (size_t i = 0; i < node.string_attributes.size(); ++i) {
- if (node.string_attributes[i].first == attr)
- return node.string_attributes[i].second;
+ const ui::AXNode* node,
+ const ui::AXStringAttribute attr) {
+ const ui::AXNodeData& data = node->data();
+ for (size_t i = 0; i < data.string_attributes.size(); ++i) {
+ if (data.string_attributes[i].first == attr)
+ return data.string_attributes[i].second;
}
return std::string();
}
-// Convenience method to get the value of a particular AccessibilityNodeData
-// node integer attribute.
+// Convenience method to get the value of a particular AXNode
+// integer attribute.
int CrossPlatformAccessibilityBrowserTest::GetIntAttr(
- const AccessibilityNodeData& node,
- const AccessibilityNodeData::IntAttribute attr) {
- for (size_t i = 0; i < node.int_attributes.size(); ++i) {
- if (node.int_attributes[i].first == attr)
- return node.int_attributes[i].second;
+ const ui::AXNode* node,
+ const ui::AXIntAttribute attr) {
+ const ui::AXNodeData& data = node->data();
+ for (size_t i = 0; i < data.int_attributes.size(); ++i) {
+ if (data.int_attributes[i].first == attr)
+ return data.int_attributes[i].second;
}
return -1;
}
-// Convenience method to get the value of a particular AccessibilityNodeData
-// node boolean attribute.
+// Convenience method to get the value of a particular AXNode
+// boolean attribute.
bool CrossPlatformAccessibilityBrowserTest::GetBoolAttr(
- const AccessibilityNodeData& node,
- const AccessibilityNodeData::BoolAttribute attr) {
- for (size_t i = 0; i < node.bool_attributes.size(); ++i) {
- if (node.bool_attributes[i].first == attr)
- return node.bool_attributes[i].second;
+ const ui::AXNode* node,
+ const ui::AXBoolAttribute attr) {
+ const ui::AXNodeData& data = node->data();
+ for (size_t i = 0; i < data.bool_attributes.size(); ++i) {
+ if (data.bool_attributes[i].first == attr)
+ return data.bool_attributes[i].second;
}
return false;
}
@@ -136,62 +141,63 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
"</body></html>";
GURL url(url_str);
NavigateToURL(shell(), url);
- const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
+ const ui::AXTree& tree = GetAXTree();
+ const ui::AXNode* root = tree.GetRoot();
// Check properties of the root element of the tree.
EXPECT_STREQ(url_str,
- GetAttr(tree, AccessibilityNodeData::ATTR_DOC_URL).c_str());
+ GetAttr(root, ui::AX_ATTR_DOC_URL).c_str());
EXPECT_STREQ(
"Accessibility Test",
- GetAttr(tree, AccessibilityNodeData::ATTR_DOC_TITLE).c_str());
+ GetAttr(root, ui::AX_ATTR_DOC_TITLE).c_str());
EXPECT_STREQ(
- "html", GetAttr(tree, AccessibilityNodeData::ATTR_DOC_DOCTYPE).c_str());
+ "html", GetAttr(root, ui::AX_ATTR_DOC_DOCTYPE).c_str());
EXPECT_STREQ(
"text/html",
- GetAttr(tree, AccessibilityNodeData::ATTR_DOC_MIMETYPE).c_str());
+ GetAttr(root, ui::AX_ATTR_DOC_MIMETYPE).c_str());
EXPECT_STREQ(
"Accessibility Test",
- GetAttr(tree, AccessibilityNodeData::ATTR_NAME).c_str());
- EXPECT_EQ(blink::WebAXRoleRootWebArea, tree.role);
+ GetAttr(root, ui::AX_ATTR_NAME).c_str());
+ EXPECT_EQ(ui::AX_ROLE_ROOT_WEB_AREA, root->data().role);
// Check properites of the BODY element.
- ASSERT_EQ(1U, tree.children.size());
- const AccessibilityNodeDataTreeNode& body = tree.children[0];
- EXPECT_EQ(blink::WebAXRoleGroup, body.role);
+ ASSERT_EQ(1, root->child_count());
+ const ui::AXNode* body = root->ChildAtIndex(0);
+ EXPECT_EQ(ui::AX_ROLE_GROUP, body->data().role);
EXPECT_STREQ("body",
- GetAttr(body, AccessibilityNodeData::ATTR_HTML_TAG).c_str());
+ GetAttr(body, ui::AX_ATTR_HTML_TAG).c_str());
EXPECT_STREQ("block",
- GetAttr(body, AccessibilityNodeData::ATTR_DISPLAY).c_str());
+ GetAttr(body, ui::AX_ATTR_DISPLAY).c_str());
// Check properties of the two children of the BODY element.
- ASSERT_EQ(2U, body.children.size());
+ ASSERT_EQ(2, body->child_count());
- const AccessibilityNodeDataTreeNode& button = body.children[0];
- EXPECT_EQ(blink::WebAXRoleButton, button.role);
+ const ui::AXNode* button = body->ChildAtIndex(0);
+ EXPECT_EQ(ui::AX_ROLE_BUTTON, button->data().role);
EXPECT_STREQ(
- "input", GetAttr(button, AccessibilityNodeData::ATTR_HTML_TAG).c_str());
+ "input", GetAttr(button, ui::AX_ATTR_HTML_TAG).c_str());
EXPECT_STREQ(
"push",
- GetAttr(button, AccessibilityNodeData::ATTR_NAME).c_str());
+ GetAttr(button, ui::AX_ATTR_NAME).c_str());
EXPECT_STREQ(
"inline-block",
- GetAttr(button, AccessibilityNodeData::ATTR_DISPLAY).c_str());
- ASSERT_EQ(2U, button.html_attributes.size());
- EXPECT_STREQ("type", button.html_attributes[0].first.c_str());
- EXPECT_STREQ("button", button.html_attributes[0].second.c_str());
- EXPECT_STREQ("value", button.html_attributes[1].first.c_str());
- EXPECT_STREQ("push", button.html_attributes[1].second.c_str());
-
- const AccessibilityNodeDataTreeNode& checkbox = body.children[1];
- EXPECT_EQ(blink::WebAXRoleCheckBox, checkbox.role);
+ GetAttr(button, ui::AX_ATTR_DISPLAY).c_str());
+ ASSERT_EQ(2U, button->data().html_attributes.size());
+ EXPECT_STREQ("type", button->data().html_attributes[0].first.c_str());
+ EXPECT_STREQ("button", button->data().html_attributes[0].second.c_str());
+ EXPECT_STREQ("value", button->data().html_attributes[1].first.c_str());
+ EXPECT_STREQ("push", button->data().html_attributes[1].second.c_str());
+
+ const ui::AXNode* checkbox = body->ChildAtIndex(1);
+ EXPECT_EQ(ui::AX_ROLE_CHECK_BOX, checkbox->data().role);
EXPECT_STREQ(
- "input", GetAttr(checkbox, AccessibilityNodeData::ATTR_HTML_TAG).c_str());
+ "input", GetAttr(checkbox, ui::AX_ATTR_HTML_TAG).c_str());
EXPECT_STREQ(
"inline-block",
- GetAttr(checkbox, AccessibilityNodeData::ATTR_DISPLAY).c_str());
- ASSERT_EQ(1U, checkbox.html_attributes.size());
- EXPECT_STREQ("type", checkbox.html_attributes[0].first.c_str());
- EXPECT_STREQ("checkbox", checkbox.html_attributes[0].second.c_str());
+ GetAttr(checkbox, ui::AX_ATTR_DISPLAY).c_str());
+ ASSERT_EQ(1U, checkbox->data().html_attributes.size());
+ EXPECT_STREQ("type", checkbox->data().html_attributes[0].first.c_str());
+ EXPECT_STREQ("checkbox", checkbox->data().html_attributes[0].second.c_str());
}
IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
@@ -206,19 +212,20 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
GURL url(url_str);
NavigateToURL(shell(), url);
- const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
- ASSERT_EQ(1U, tree.children.size());
- const AccessibilityNodeDataTreeNode& body = tree.children[0];
- ASSERT_EQ(1U, body.children.size());
- const AccessibilityNodeDataTreeNode& text = body.children[0];
- EXPECT_EQ(blink::WebAXRoleTextField, text.role);
+ const ui::AXTree& tree = GetAXTree();
+ const ui::AXNode* root = tree.GetRoot();
+ ASSERT_EQ(1, root->child_count());
+ const ui::AXNode* body = root->ChildAtIndex(0);
+ ASSERT_EQ(1, body->child_count());
+ const ui::AXNode* text = body->ChildAtIndex(0);
+ EXPECT_EQ(ui::AX_ROLE_TEXT_FIELD, text->data().role);
EXPECT_STREQ(
- "input", GetAttr(text, AccessibilityNodeData::ATTR_HTML_TAG).c_str());
- EXPECT_EQ(0, GetIntAttr(text, AccessibilityNodeData::ATTR_TEXT_SEL_START));
- EXPECT_EQ(0, GetIntAttr(text, AccessibilityNodeData::ATTR_TEXT_SEL_END));
+ "input", GetAttr(text, ui::AX_ATTR_HTML_TAG).c_str());
+ EXPECT_EQ(0, GetIntAttr(text, ui::AX_ATTR_TEXT_SEL_START));
+ EXPECT_EQ(0, GetIntAttr(text, ui::AX_ATTR_TEXT_SEL_END));
EXPECT_STREQ(
"Hello, world.",
- GetAttr(text, AccessibilityNodeData::ATTR_VALUE).c_str());
+ GetAttr(text, ui::AX_ATTR_VALUE).c_str());
// TODO(dmazzoni): as soon as more accessibility code is cross-platform,
// this code should test that the accessible info is dynamically updated
@@ -237,26 +244,27 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
GURL url(url_str);
NavigateToURL(shell(), url);
- const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
- ASSERT_EQ(1U, tree.children.size());
- const AccessibilityNodeDataTreeNode& body = tree.children[0];
- ASSERT_EQ(1U, body.children.size());
- const AccessibilityNodeDataTreeNode& text = body.children[0];
- EXPECT_EQ(blink::WebAXRoleTextField, text.role);
+ const ui::AXTree& tree = GetAXTree();
+ const ui::AXNode* root = tree.GetRoot();
+ ASSERT_EQ(1, root->child_count());
+ const ui::AXNode* body = root->ChildAtIndex(0);
+ ASSERT_EQ(1, body->child_count());
+ const ui::AXNode* text = body->ChildAtIndex(0);
+ EXPECT_EQ(ui::AX_ROLE_TEXT_FIELD, text->data().role);
EXPECT_STREQ(
- "input", GetAttr(text, AccessibilityNodeData::ATTR_HTML_TAG).c_str());
- EXPECT_EQ(0, GetIntAttr(text, AccessibilityNodeData::ATTR_TEXT_SEL_START));
- EXPECT_EQ(13, GetIntAttr(text, AccessibilityNodeData::ATTR_TEXT_SEL_END));
+ "input", GetAttr(text, ui::AX_ATTR_HTML_TAG).c_str());
+ EXPECT_EQ(0, GetIntAttr(text, ui::AX_ATTR_TEXT_SEL_START));
+ EXPECT_EQ(13, GetIntAttr(text, ui::AX_ATTR_TEXT_SEL_END));
EXPECT_STREQ(
"Hello, world.",
- GetAttr(text, AccessibilityNodeData::ATTR_VALUE).c_str());
+ GetAttr(text, ui::AX_ATTR_VALUE).c_str());
}
IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
MultipleInheritanceAccessibility) {
// In a WebKit accessibility render tree for a table, each cell is a
// child of both a row and a column, so it appears to use multiple
- // inheritance. Make sure that the AccessibilityNodeDataObject tree only
+ // inheritance. Make sure that the ui::AXNodeDataObject tree only
// keeps one copy of each cell, and uses an indirect child id for the
// additional reference to it.
const char url_str[] =
@@ -266,35 +274,36 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
GURL url(url_str);
NavigateToURL(shell(), url);
- const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
- ASSERT_EQ(1U, tree.children.size());
- const AccessibilityNodeDataTreeNode& table = tree.children[0];
- EXPECT_EQ(blink::WebAXRoleTable, table.role);
- const AccessibilityNodeDataTreeNode& row = table.children[0];
- EXPECT_EQ(blink::WebAXRoleRow, row.role);
- const AccessibilityNodeDataTreeNode& cell1 = row.children[0];
- EXPECT_EQ(blink::WebAXRoleCell, cell1.role);
- const AccessibilityNodeDataTreeNode& cell2 = row.children[1];
- EXPECT_EQ(blink::WebAXRoleCell, cell2.role);
- const AccessibilityNodeDataTreeNode& column1 = table.children[1];
- EXPECT_EQ(blink::WebAXRoleColumn, column1.role);
- EXPECT_EQ(0U, column1.children.size());
- EXPECT_EQ(1U, column1.intlist_attributes.size());
- EXPECT_EQ(AccessibilityNodeData::ATTR_INDIRECT_CHILD_IDS,
- column1.intlist_attributes[0].first);
+ const ui::AXTree& tree = GetAXTree();
+ const ui::AXNode* root = tree.GetRoot();
+ ASSERT_EQ(1, root->child_count());
+ const ui::AXNode* table = root->ChildAtIndex(0);
+ EXPECT_EQ(ui::AX_ROLE_TABLE, table->data().role);
+ const ui::AXNode* row = table->ChildAtIndex(0);
+ EXPECT_EQ(ui::AX_ROLE_ROW, row->data().role);
+ const ui::AXNode* cell1 = row->ChildAtIndex(0);
+ EXPECT_EQ(ui::AX_ROLE_CELL, cell1->data().role);
+ const ui::AXNode* cell2 = row->ChildAtIndex(1);
+ EXPECT_EQ(ui::AX_ROLE_CELL, cell2->data().role);
+ const ui::AXNode* column1 = table->ChildAtIndex(1);
+ EXPECT_EQ(ui::AX_ROLE_COLUMN, column1->data().role);
+ EXPECT_EQ(0, column1->child_count());
+ EXPECT_EQ(1U, column1->data().intlist_attributes.size());
+ EXPECT_EQ(ui::AX_ATTR_INDIRECT_CHILD_IDS,
+ column1->data().intlist_attributes[0].first);
const std::vector<int32> column1_indirect_child_ids =
- column1.intlist_attributes[0].second;
+ column1->data().intlist_attributes[0].second;
EXPECT_EQ(1U, column1_indirect_child_ids.size());
- EXPECT_EQ(cell1.id, column1_indirect_child_ids[0]);
- const AccessibilityNodeDataTreeNode& column2 = table.children[2];
- EXPECT_EQ(blink::WebAXRoleColumn, column2.role);
- EXPECT_EQ(0U, column2.children.size());
- EXPECT_EQ(AccessibilityNodeData::ATTR_INDIRECT_CHILD_IDS,
- column2.intlist_attributes[0].first);
+ EXPECT_EQ(cell1->id(), column1_indirect_child_ids[0]);
+ const ui::AXNode* column2 = table->ChildAtIndex(2);
+ EXPECT_EQ(ui::AX_ROLE_COLUMN, column2->data().role);
+ EXPECT_EQ(0, column2->child_count());
+ EXPECT_EQ(ui::AX_ATTR_INDIRECT_CHILD_IDS,
+ column2->data().intlist_attributes[0].first);
const std::vector<int32> column2_indirect_child_ids =
- column2.intlist_attributes[0].second;
+ column2->data().intlist_attributes[0].second;
EXPECT_EQ(1U, column2_indirect_child_ids.size());
- EXPECT_EQ(cell2.id, column2_indirect_child_ids[0]);
+ EXPECT_EQ(cell2->id(), column2_indirect_child_ids[0]);
}
IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
@@ -314,13 +323,15 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
GURL url(url_str);
NavigateToURL(shell(), url);
- const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
+ const ui::AXTree& tree = GetAXTree();
+ const ui::AXNode* root = tree.GetRoot();
base::hash_set<int> ids;
- RecursiveAssertUniqueIds(tree, &ids);
+ RecursiveAssertUniqueIds(root, &ids);
}
+// TODO(dmazzoni): Needs to be rebaselined. http://crbug.com/347464
IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
- IframeAccessibility) {
+ DISABLED_IframeAccessibility) {
// Create a data url and load it.
const char url_str[] =
"data:text/html,"
@@ -334,42 +345,43 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
GURL url(url_str);
NavigateToURL(shell(), url);
- const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
- ASSERT_EQ(1U, tree.children.size());
- const AccessibilityNodeDataTreeNode& body = tree.children[0];
- ASSERT_EQ(3U, body.children.size());
+ const ui::AXTree& tree = GetAXTree();
+ const ui::AXNode* root = tree.GetRoot();
+ ASSERT_EQ(1, root->child_count());
+ const ui::AXNode* body = root->ChildAtIndex(0);
+ ASSERT_EQ(3, body->child_count());
- const AccessibilityNodeDataTreeNode& button1 = body.children[0];
- EXPECT_EQ(blink::WebAXRoleButton, button1.role);
+ const ui::AXNode* button1 = body->ChildAtIndex(0);
+ EXPECT_EQ(ui::AX_ROLE_BUTTON, button1->data().role);
EXPECT_STREQ(
"Button 1",
- GetAttr(button1, AccessibilityNodeData::ATTR_NAME).c_str());
+ GetAttr(button1, ui::AX_ATTR_NAME).c_str());
- const AccessibilityNodeDataTreeNode& iframe = body.children[1];
+ const ui::AXNode* iframe = body->ChildAtIndex(1);
EXPECT_STREQ("iframe",
- GetAttr(iframe, AccessibilityNodeData::ATTR_HTML_TAG).c_str());
- ASSERT_EQ(1U, iframe.children.size());
+ GetAttr(iframe, ui::AX_ATTR_HTML_TAG).c_str());
+ ASSERT_EQ(1, iframe->child_count());
- const AccessibilityNodeDataTreeNode& scroll_area = iframe.children[0];
- EXPECT_EQ(blink::WebAXRoleScrollArea, scroll_area.role);
- ASSERT_EQ(1U, scroll_area.children.size());
+ const ui::AXNode* scroll_area = iframe->ChildAtIndex(0);
+ EXPECT_EQ(ui::AX_ROLE_SCROLL_AREA, scroll_area->data().role);
+ ASSERT_EQ(1, scroll_area->child_count());
- const AccessibilityNodeDataTreeNode& sub_document = scroll_area.children[0];
- EXPECT_EQ(blink::WebAXRoleWebArea, sub_document.role);
- ASSERT_EQ(1U, sub_document.children.size());
+ const ui::AXNode* sub_document = scroll_area->ChildAtIndex(0);
+ EXPECT_EQ(ui::AX_ROLE_WEB_AREA, sub_document->data().role);
+ ASSERT_EQ(1, sub_document->child_count());
- const AccessibilityNodeDataTreeNode& sub_body = sub_document.children[0];
- ASSERT_EQ(1U, sub_body.children.size());
+ const ui::AXNode* sub_body = sub_document->ChildAtIndex(0);
+ ASSERT_EQ(1, sub_body->child_count());
- const AccessibilityNodeDataTreeNode& button2 = sub_body.children[0];
- EXPECT_EQ(blink::WebAXRoleButton, button2.role);
+ const ui::AXNode* button2 = sub_body->ChildAtIndex(0);
+ EXPECT_EQ(ui::AX_ROLE_BUTTON, button2->data().role);
EXPECT_STREQ("Button 2",
- GetAttr(button2, AccessibilityNodeData::ATTR_NAME).c_str());
+ GetAttr(button2, ui::AX_ATTR_NAME).c_str());
- const AccessibilityNodeDataTreeNode& button3 = body.children[2];
- EXPECT_EQ(blink::WebAXRoleButton, button3.role);
+ const ui::AXNode* button3 = body->ChildAtIndex(2);
+ EXPECT_EQ(ui::AX_ROLE_BUTTON, button3->data().role);
EXPECT_STREQ("Button 3",
- GetAttr(button3, AccessibilityNodeData::ATTR_NAME).c_str());
+ GetAttr(button3, ui::AX_ATTR_NAME).c_str());
}
IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
@@ -384,9 +396,10 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
GURL url(url_str);
NavigateToURL(shell(), url);
- const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
+ const ui::AXTree& tree = GetAXTree();
+ const ui::AXNode* root = tree.GetRoot();
base::hash_set<int> ids;
- RecursiveAssertUniqueIds(tree, &ids);
+ RecursiveAssertUniqueIds(root, &ids);
}
IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
@@ -411,56 +424,57 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
GURL url(url_str);
NavigateToURL(shell(), url);
- const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
- const AccessibilityNodeDataTreeNode& table = tree.children[0];
- EXPECT_EQ(blink::WebAXRoleTable, table.role);
- ASSERT_GE(table.children.size(), 5U);
- EXPECT_EQ(blink::WebAXRoleRow, table.children[0].role);
- EXPECT_EQ(blink::WebAXRoleRow, table.children[1].role);
- EXPECT_EQ(blink::WebAXRoleColumn, table.children[2].role);
- EXPECT_EQ(blink::WebAXRoleColumn, table.children[3].role);
- EXPECT_EQ(blink::WebAXRoleColumn, table.children[4].role);
+ const ui::AXTree& tree = GetAXTree();
+ const ui::AXNode* root = tree.GetRoot();
+ const ui::AXNode* table = root->ChildAtIndex(0);
+ EXPECT_EQ(ui::AX_ROLE_TABLE, table->data().role);
+ ASSERT_GE(table->child_count(), 5);
+ EXPECT_EQ(ui::AX_ROLE_ROW, table->ChildAtIndex(0)->data().role);
+ EXPECT_EQ(ui::AX_ROLE_ROW, table->ChildAtIndex(1)->data().role);
+ EXPECT_EQ(ui::AX_ROLE_COLUMN, table->ChildAtIndex(2)->data().role);
+ EXPECT_EQ(ui::AX_ROLE_COLUMN, table->ChildAtIndex(3)->data().role);
+ EXPECT_EQ(ui::AX_ROLE_COLUMN, table->ChildAtIndex(4)->data().role);
EXPECT_EQ(3,
- GetIntAttr(table, AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT));
- EXPECT_EQ(2, GetIntAttr(table, AccessibilityNodeData::ATTR_TABLE_ROW_COUNT));
+ GetIntAttr(table, ui::AX_ATTR_TABLE_COLUMN_COUNT));
+ EXPECT_EQ(2, GetIntAttr(table, ui::AX_ATTR_TABLE_ROW_COUNT));
- const AccessibilityNodeDataTreeNode& cell1 = table.children[0].children[0];
- const AccessibilityNodeDataTreeNode& cell2 = table.children[0].children[1];
- const AccessibilityNodeDataTreeNode& cell3 = table.children[1].children[0];
- const AccessibilityNodeDataTreeNode& cell4 = table.children[1].children[1];
+ const ui::AXNode* cell1 = table->ChildAtIndex(0)->ChildAtIndex(0);
+ const ui::AXNode* cell2 = table->ChildAtIndex(0)->ChildAtIndex(1);
+ const ui::AXNode* cell3 = table->ChildAtIndex(1)->ChildAtIndex(0);
+ const ui::AXNode* cell4 = table->ChildAtIndex(1)->ChildAtIndex(1);
- ASSERT_EQ(AccessibilityNodeData::ATTR_CELL_IDS,
- table.intlist_attributes[0].first);
+ ASSERT_EQ(ui::AX_ATTR_CELL_IDS,
+ table->data().intlist_attributes[0].first);
const std::vector<int32>& table_cell_ids =
- table.intlist_attributes[0].second;
+ table->data().intlist_attributes[0].second;
ASSERT_EQ(6U, table_cell_ids.size());
- EXPECT_EQ(cell1.id, table_cell_ids[0]);
- EXPECT_EQ(cell1.id, table_cell_ids[1]);
- EXPECT_EQ(cell2.id, table_cell_ids[2]);
- EXPECT_EQ(cell3.id, table_cell_ids[3]);
- EXPECT_EQ(cell4.id, table_cell_ids[4]);
- EXPECT_EQ(cell4.id, table_cell_ids[5]);
+ EXPECT_EQ(cell1->id(), table_cell_ids[0]);
+ EXPECT_EQ(cell1->id(), table_cell_ids[1]);
+ EXPECT_EQ(cell2->id(), table_cell_ids[2]);
+ EXPECT_EQ(cell3->id(), table_cell_ids[3]);
+ EXPECT_EQ(cell4->id(), table_cell_ids[4]);
+ EXPECT_EQ(cell4->id(), table_cell_ids[5]);
EXPECT_EQ(0, GetIntAttr(cell1,
- AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX));
+ ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX));
EXPECT_EQ(0, GetIntAttr(cell1,
- AccessibilityNodeData::ATTR_TABLE_CELL_ROW_INDEX));
+ ui::AX_ATTR_TABLE_CELL_ROW_INDEX));
EXPECT_EQ(2, GetIntAttr(cell1,
- AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_SPAN));
+ ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN));
EXPECT_EQ(1, GetIntAttr(cell1,
- AccessibilityNodeData::ATTR_TABLE_CELL_ROW_SPAN));
+ ui::AX_ATTR_TABLE_CELL_ROW_SPAN));
EXPECT_EQ(2, GetIntAttr(cell2,
- AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX));
+ ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX));
EXPECT_EQ(1, GetIntAttr(cell2,
- AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_SPAN));
+ ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN));
EXPECT_EQ(0, GetIntAttr(cell3,
- AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX));
+ ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX));
EXPECT_EQ(1, GetIntAttr(cell3,
- AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_SPAN));
+ ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN));
EXPECT_EQ(1, GetIntAttr(cell4,
- AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX));
+ ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX));
EXPECT_EQ(2, GetIntAttr(cell4,
- AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_SPAN));
+ ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN));
}
IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
@@ -473,13 +487,11 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
"</div>";
GURL url(url_str);
NavigateToURL(shell(), url);
- const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
-
- ASSERT_EQ(1U, tree.children.size());
- const AccessibilityNodeDataTreeNode& textbox = tree.children[0];
-
- EXPECT_EQ(
- true, GetBoolAttr(textbox, AccessibilityNodeData::ATTR_CAN_SET_VALUE));
+ const ui::AXTree& tree = GetAXTree();
+ const ui::AXNode* root = tree.GetRoot();
+ ASSERT_EQ(1, root->child_count());
+ const ui::AXNode* textbox = root->ChildAtIndex(0);
+ EXPECT_EQ(true, GetBoolAttr(textbox, ui::AX_ATTR_CAN_SET_VALUE));
}
} // namespace content
diff --git a/chromium/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/chromium/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index 8cceea7bc5f..d373f7b8757 100644
--- a/chromium/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/chromium/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -18,19 +18,19 @@
#include "content/browser/accessibility/browser_accessibility.h"
#include "content/browser/accessibility/browser_accessibility_manager.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
-#include "content/port/browser/render_widget_host_view_port.h"
+#include "content/browser/renderer_host/render_widget_host_view_base.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 "content/test/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
-// TODO(dmazzoni): Disabled accessibility tests on Win64. crbug.com/179717
-#if defined(OS_WIN) && defined(ARCH_CPU_X86_64)
+// TODO(aboxhall): Create expectations on Android for these
+#if defined(OS_ANDROID)
#define MAYBE(x) DISABLED_##x
#else
#define MAYBE(x) x
@@ -88,9 +88,9 @@ class DumpAccessibilityTreeTest : public ContentBrowserTest {
}
void AddDefaultFilters(std::vector<Filter>* filters) {
- filters->push_back(Filter(ASCIIToUTF16("FOCUSABLE"), Filter::ALLOW));
- filters->push_back(Filter(ASCIIToUTF16("READONLY"), Filter::ALLOW));
- filters->push_back(Filter(ASCIIToUTF16("*=''"), Filter::DENY));
+ filters->push_back(Filter(base::ASCIIToUTF16("FOCUSABLE"), Filter::ALLOW));
+ filters->push_back(Filter(base::ASCIIToUTF16("READONLY"), Filter::ALLOW));
+ filters->push_back(Filter(base::ASCIIToUTF16("*=''"), Filter::DENY));
}
void ParseFilters(const std::string& test_html,
@@ -109,13 +109,15 @@ class DumpAccessibilityTreeTest : public ContentBrowserTest {
AccessibilityTreeFormatter::GetDenyString();
if (StartsWithASCII(line, allow_empty_str, true)) {
filters->push_back(
- Filter(UTF8ToUTF16(line.substr(allow_empty_str.size())),
+ Filter(base::UTF8ToUTF16(line.substr(allow_empty_str.size())),
Filter::ALLOW_EMPTY));
} else if (StartsWithASCII(line, allow_str, true)) {
- filters->push_back(Filter(UTF8ToUTF16(line.substr(allow_str.size())),
+ filters->push_back(Filter(base::UTF8ToUTF16(
+ line.substr(allow_str.size())),
Filter::ALLOW));
} else if (StartsWithASCII(line, deny_str, true)) {
- filters->push_back(Filter(UTF8ToUTF16(line.substr(deny_str.size())),
+ filters->push_back(Filter(base::UTF8ToUTF16(
+ line.substr(deny_str.size())),
Filter::DENY));
}
}
@@ -133,7 +135,7 @@ class DumpAccessibilityTreeTest : public ContentBrowserTest {
void DumpAccessibilityTreeTest::RunTest(
const base::FilePath::CharType* file_path) {
- NavigateToURL(shell(), GURL(kAboutBlankURL));
+ NavigateToURL(shell(), GURL(url::kAboutBlankURL));
// Setup test paths.
base::FilePath dir_test_data;
@@ -170,16 +172,16 @@ void DumpAccessibilityTreeTest::RunTest(
// Load the page.
base::string16 html_contents16;
- html_contents16 = UTF8ToUTF16(html_contents);
+ html_contents16 = base::UTF8ToUTF16(html_contents);
GURL url = GetTestUrl("accessibility",
html_file.BaseName().MaybeAsASCII().c_str());
AccessibilityNotificationWaiter waiter(
shell(), AccessibilityModeComplete,
- blink::WebAXEventLoadComplete);
+ ui::AX_EVENT_LOAD_COMPLETE);
NavigateToURL(shell(), url);
waiter.WaitForNotification();
- RenderWidgetHostViewPort* host_view = RenderWidgetHostViewPort::FromRWHV(
+ RenderWidgetHostViewBase* host_view = static_cast<RenderWidgetHostViewBase*>(
shell()->web_contents()->GetRenderWidgetHostView());
AccessibilityTreeFormatter formatter(
host_view->GetBrowserAccessibilityManager()->GetRoot());
@@ -193,7 +195,7 @@ void DumpAccessibilityTreeTest::RunTest(
// Perform a diff (or write the initial baseline).
base::string16 actual_contents_utf16;
formatter.FormatAccessibilityTree(&actual_contents_utf16);
- std::string actual_contents = UTF16ToUTF8(actual_contents_utf16);
+ std::string actual_contents = base::UTF16ToUTF8(actual_contents_utf16);
std::vector<std::string> actual_lines, expected_lines;
Tokenize(actual_contents, "\n", &actual_lines);
Tokenize(expected_contents, "\n", &expected_lines);
@@ -231,7 +233,7 @@ void DumpAccessibilityTreeTest::RunTest(
base::FilePath(html_file.RemoveExtension().value() +
AccessibilityTreeFormatter::GetActualFileSuffix());
- EXPECT_TRUE(file_util::WriteFile(
+ EXPECT_TRUE(base::WriteFile(
actual_file, actual_contents.c_str(), actual_contents.size()));
ADD_FAILURE() << "No expectation found. Create it by doing:\n"
@@ -252,6 +254,10 @@ IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAName) {
RunTest(FILE_PATH_LITERAL("a-name.html"));
}
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityANoText) {
+ RunTest(FILE_PATH_LITERAL("a-no-text.html"));
+}
+
IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAOnclick) {
RunTest(FILE_PATH_LITERAL("a-onclick.html"));
}
@@ -266,18 +272,35 @@ IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
RunTest(FILE_PATH_LITERAL("aria-autocomplete.html"));
}
-IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAriaCombobox) {
+// crbug.com/98976 will cause new elements to be added to the Blink a11y tree
+// Re-baseline after the Blink change goes in
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+ DISABLED_AccessibilityAriaCombobox) {
RunTest(FILE_PATH_LITERAL("aria-combobox.html"));
}
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+ MAYBE(AccessibilityAriaFlowto)) {
+ RunTest(FILE_PATH_LITERAL("aria-flowto.html"));
+}
+
IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAriaInvalid) {
RunTest(FILE_PATH_LITERAL("aria-invalid.html"));
}
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+ AccessibilityAriaLabelledByHeading) {
+ RunTest(FILE_PATH_LITERAL("aria-labelledby-heading.html"));
+}
+
IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAriaLevel) {
RunTest(FILE_PATH_LITERAL("aria-level.html"));
}
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAriaList) {
+ RunTest(FILE_PATH_LITERAL("aria-list.html"));
+}
+
IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAriaMenu) {
RunTest(FILE_PATH_LITERAL("aria-menu.html"));
}
@@ -378,9 +401,8 @@ IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityHR) {
RunTest(FILE_PATH_LITERAL("hr.html"));
}
-// crbug.com/179717 and crbug.com/224659
IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
- DISABLED_AccessibilityIframeCoordinates) {
+ AccessibilityIframeCoordinates) {
RunTest(FILE_PATH_LITERAL("iframe-coordinates.html"));
}
@@ -411,7 +433,10 @@ IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
RunTest(FILE_PATH_LITERAL("input-text-name-calc.html"));
}
-IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityInputTypes) {
+// crbug.com/98976 will cause new elements to be added to the Blink a11y tree
+// Re-baseline after the Blink change goes in
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+ DISABLED_AccessibilityInputTypes) {
RunTest(FILE_PATH_LITERAL("input-types.html"));
}
@@ -419,6 +444,10 @@ IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityLabel) {
RunTest(FILE_PATH_LITERAL("label.html"));
}
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityLandmark) {
+ RunTest(FILE_PATH_LITERAL("landmark.html"));
+}
+
IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityListMarkers) {
RunTest(FILE_PATH_LITERAL("list-markers.html"));
}
@@ -452,6 +481,10 @@ IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityP) {
RunTest(FILE_PATH_LITERAL("p.html"));
}
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityRegion) {
+ RunTest(FILE_PATH_LITERAL("region.html"));
+}
+
IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilitySelect) {
RunTest(FILE_PATH_LITERAL("select.html"));
}
@@ -495,4 +528,9 @@ IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityWbr) {
RunTest(FILE_PATH_LITERAL("wbr.html"));
}
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+ AccessibilityAriaActivedescendant) {
+ RunTest(FILE_PATH_LITERAL("aria-activedescendant.html"));
+}
+
} // namespace content
diff --git a/chromium/content/browser/android/child_process_launcher_android.cc b/chromium/content/browser/android/child_process_launcher_android.cc
index cf915e4a42a..baac2b0a715 100644
--- a/chromium/content/browser/android/child_process_launcher_android.cc
+++ b/chromium/content/browser/android/child_process_launcher_android.cc
@@ -8,7 +8,9 @@
#include "base/android/jni_array.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.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/media_web_contents_observer.h"
#include "content/browser/renderer_host/compositor_impl_android.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/public/browser/browser_thread.h"
@@ -29,34 +31,52 @@ namespace content {
namespace {
// Pass a java surface object to the MediaPlayerAndroid object
-// identified by render process handle, render view ID and player ID.
+// identified by render process handle, render frame ID and player ID.
static void SetSurfacePeer(
const base::android::JavaRef<jobject>& surface,
base::ProcessHandle render_process_handle,
- int render_view_id,
+ int render_frame_id,
int player_id) {
- int renderer_id = 0;
+ int render_process_id = 0;
RenderProcessHost::iterator it = RenderProcessHost::AllHostsIterator();
while (!it.IsAtEnd()) {
if (it.GetCurrentValue()->GetHandle() == render_process_handle) {
- renderer_id = it.GetCurrentValue()->GetID();
+ render_process_id = it.GetCurrentValue()->GetID();
break;
}
it.Advance();
}
+ if (!render_process_id) {
+ DVLOG(1) << "Cannot find render process for render_process_handle "
+ << render_process_handle;
+ return;
+ }
- if (renderer_id) {
- RenderViewHostImpl* host = RenderViewHostImpl::FromID(
- renderer_id, render_view_id);
- if (host) {
- media::MediaPlayerAndroid* player =
- host->media_player_manager()->GetPlayer(player_id);
- if (player &&
- player != host->media_player_manager()->GetFullscreenPlayer()) {
- gfx::ScopedJavaSurface scoped_surface(surface);
- player->SetVideoSurface(scoped_surface.Pass());
- }
- }
+ RenderFrameHostImpl* frame =
+ RenderFrameHostImpl::FromID(render_process_id, render_frame_id);
+ if (!frame) {
+ DVLOG(1) << "Cannot find frame for render_frame_id " << render_frame_id;
+ return;
+ }
+
+ RenderViewHostImpl* view =
+ static_cast<RenderViewHostImpl*>(frame->GetRenderViewHost());
+ BrowserMediaPlayerManager* player_manager =
+ view->media_web_contents_observer()->GetMediaPlayerManager(frame);
+ if (!player_manager) {
+ DVLOG(1) << "Cannot find the media player manager for frame " << frame;
+ return;
+ }
+
+ media::MediaPlayerAndroid* player = player_manager->GetPlayer(player_id);
+ if (!player) {
+ DVLOG(1) << "Cannot find media player for player_id " << player_id;
+ return;
+ }
+
+ if (player != player_manager->GetFullscreenPlayer()) {
+ gfx::ScopedJavaSurface scoped_surface(surface);
+ player->SetVideoSurface(scoped_surface.Pass());
}
}
@@ -81,6 +101,7 @@ static void OnChildProcessStarted(JNIEnv*,
void StartChildProcess(
const CommandLine::StringVector& argv,
+ int child_process_id,
const std::vector<content::FileDescriptorInfo>& files_to_register,
const StartChildProcessCallback& callback) {
JNIEnv* env = AttachCurrentThread();
@@ -119,6 +140,7 @@ void StartChildProcess(
Java_ChildProcessLauncher_start(env,
base::android::GetApplicationContext(),
j_argv.obj(),
+ child_process_id,
j_file_ids.obj(),
j_file_fds.obj(),
j_file_auto_close.obj(),
@@ -138,6 +160,14 @@ bool IsChildProcessOomProtected(base::ProcessHandle handle) {
static_cast<jint>(handle));
}
+void SetChildProcessInForeground(base::ProcessHandle handle,
+ bool in_foreground) {
+ JNIEnv* env = AttachCurrentThread();
+ DCHECK(env);
+ return Java_ChildProcessLauncher_setInForeground(env,
+ static_cast<jint>(handle), static_cast<jboolean>(in_foreground));
+}
+
void EstablishSurfacePeer(
JNIEnv* env, jclass clazz,
jint pid, jobject surface, jint primary_id, jint secondary_id) {
@@ -151,12 +181,33 @@ void EstablishSurfacePeer(
&SetSurfacePeer, jsurface, pid, primary_id, secondary_id));
}
-jobject GetViewSurface(JNIEnv* env, jclass clazz, jint surface_id) {
- // This is a synchronous call from the GPU process and is expected to be
- // handled on a binder thread. Handling this on the UI thread will lead
- // to deadlocks.
- DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
- return CompositorImpl::GetSurface(surface_id);
+void RegisterViewSurface(int surface_id, jobject j_surface) {
+ JNIEnv* env = AttachCurrentThread();
+ DCHECK(env);
+ Java_ChildProcessLauncher_registerViewSurface(env, surface_id, j_surface);
+}
+
+void UnregisterViewSurface(int surface_id) {
+ JNIEnv* env = AttachCurrentThread();
+ DCHECK(env);
+ Java_ChildProcessLauncher_unregisterViewSurface(env, surface_id);
+}
+
+void RegisterChildProcessSurfaceTexture(int surface_texture_id,
+ int child_process_id,
+ jobject j_surface_texture) {
+ JNIEnv* env = AttachCurrentThread();
+ DCHECK(env);
+ Java_ChildProcessLauncher_registerSurfaceTexture(
+ env, surface_texture_id, child_process_id, j_surface_texture);
+}
+
+void UnregisterChildProcessSurfaceTexture(int surface_texture_id,
+ int child_process_id) {
+ JNIEnv* env = AttachCurrentThread();
+ DCHECK(env);
+ Java_ChildProcessLauncher_unregisterSurfaceTexture(
+ env, surface_texture_id, child_process_id);
}
jboolean IsSingleProcess(JNIEnv* env, jclass clazz) {
diff --git a/chromium/content/browser/android/child_process_launcher_android.h b/chromium/content/browser/android/child_process_launcher_android.h
index 71a6f04c712..747649fc7ac 100644
--- a/chromium/content/browser/android/child_process_launcher_android.h
+++ b/chromium/content/browser/android/child_process_launcher_android.h
@@ -9,7 +9,6 @@
#include "base/callback.h"
#include "base/command_line.h"
-#include "base/platform_file.h"
#include "base/process/process.h"
#include "content/public/browser/file_descriptor_info.h"
@@ -21,7 +20,8 @@ typedef base::Callback<void(base::ProcessHandle)> StartChildProcessCallback;
// The created process handle is returned to the |callback| on success, 0 is
// retuned if the process could not be created.
void StartChildProcess(
- const CommandLine::StringVector& argv,
+ const base::CommandLine::StringVector& argv,
+ int child_process_id,
const std::vector<FileDescriptorInfo>& files_to_register,
const StartChildProcessCallback& callback);
@@ -31,6 +31,20 @@ void StopChildProcess(base::ProcessHandle handle);
bool IsChildProcessOomProtected(base::ProcessHandle handle);
+void SetChildProcessInForeground(base::ProcessHandle handle,
+ bool in_foreground);
+
+void RegisterViewSurface(int surface_id, jobject j_surface);
+
+void UnregisterViewSurface(int surface_id);
+
+void RegisterChildProcessSurfaceTexture(int surface_texture_id,
+ int child_process_id,
+ jobject j_surface_texture);
+
+void UnregisterChildProcessSurfaceTexture(int surface_texture_id,
+ int child_process_id);
+
bool RegisterChildProcessLauncher(JNIEnv* env);
} // namespace content
diff --git a/chromium/content/browser/android/content_readback_handler.cc b/chromium/content/browser/android/content_readback_handler.cc
new file mode 100644
index 00000000000..841ef5f051a
--- /dev/null
+++ b/chromium/content/browser/android/content_readback_handler.cc
@@ -0,0 +1,125 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/android/content_readback_handler.h"
+
+#include "base/android/jni_android.h"
+#include "base/bind.h"
+#include "cc/output/copy_output_request.h"
+#include "cc/output/copy_output_result.h"
+#include "content/browser/android/content_view_core_impl.h"
+#include "jni/ContentReadbackHandler_jni.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/base/android/window_android.h"
+#include "ui/base/android/window_android_compositor.h"
+#include "ui/gfx/android/java_bitmap.h"
+#include "ui/gfx/rect.h"
+
+namespace content {
+
+namespace {
+
+typedef base::Callback<void(bool, const SkBitmap&)> ResultCallback;
+
+void OnFinishCopyOutputRequest(
+ const ResultCallback& result_callback,
+ scoped_ptr<cc::CopyOutputResult> copy_output_result) {
+ if (!copy_output_result->HasBitmap()) {
+ result_callback.Run(false, SkBitmap());
+ return;
+ }
+
+ scoped_ptr<SkBitmap> bitmap = copy_output_result->TakeBitmap();
+ result_callback.Run(true, *bitmap.Pass());
+}
+
+} // anonymous namespace
+
+// static
+bool ContentReadbackHandler::RegisterContentReadbackHandler(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+ContentReadbackHandler::ContentReadbackHandler(JNIEnv* env, jobject obj)
+ : weak_factory_(this) {
+ java_obj_.Reset(env, obj);
+}
+
+void ContentReadbackHandler::Destroy(JNIEnv* env, jobject obj) {
+ delete this;
+}
+
+void ContentReadbackHandler::GetContentBitmap(JNIEnv* env,
+ jobject obj,
+ jint readback_id,
+ jfloat scale,
+ jobject config,
+ jfloat x,
+ jfloat y,
+ jfloat width,
+ jfloat height,
+ jobject content_view_core) {
+ ContentViewCore* view =
+ ContentViewCore::GetNativeContentViewCore(env, content_view_core);
+ DCHECK(view);
+
+ ResultCallback result_callback =
+ base::Bind(&ContentReadbackHandler::OnFinishReadback,
+ weak_factory_.GetWeakPtr(),
+ readback_id);
+
+ view->GetScaledContentBitmap(
+ scale, config, gfx::Rect(x, y, width, height), result_callback);
+}
+
+void ContentReadbackHandler::GetCompositorBitmap(JNIEnv* env,
+ jobject obj,
+ jint readback_id,
+ jlong native_window_android) {
+ ui::WindowAndroid* window_android =
+ reinterpret_cast<ui::WindowAndroid*>(native_window_android);
+ DCHECK(window_android);
+
+ ResultCallback result_callback =
+ base::Bind(&ContentReadbackHandler::OnFinishReadback,
+ weak_factory_.GetWeakPtr(),
+ readback_id);
+
+ base::Callback<void(scoped_ptr<cc::CopyOutputResult>)> copy_output_callback =
+ base::Bind(&OnFinishCopyOutputRequest,
+ result_callback);
+
+ ui::WindowAndroidCompositor* compositor = window_android->GetCompositor();
+
+ if (!compositor) {
+ copy_output_callback.Run(cc::CopyOutputResult::CreateEmptyResult());
+ return;
+ }
+
+ compositor->RequestCopyOfOutputOnRootLayer(
+ cc::CopyOutputRequest::CreateBitmapRequest(copy_output_callback));
+}
+
+ContentReadbackHandler::~ContentReadbackHandler() {}
+
+void ContentReadbackHandler::OnFinishReadback(int readback_id,
+ bool success,
+ const SkBitmap& bitmap) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> java_bitmap;
+ if (success)
+ java_bitmap = gfx::ConvertToJavaBitmap(&bitmap);
+
+ Java_ContentReadbackHandler_notifyGetBitmapFinished(
+ env, java_obj_.obj(), readback_id, success, java_bitmap.obj());
+}
+
+// static
+static jlong Init(JNIEnv* env, jobject obj) {
+ ContentReadbackHandler* content_readback_handler =
+ new ContentReadbackHandler(env, obj);
+ return reinterpret_cast<intptr_t>(content_readback_handler);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/android/content_readback_handler.h b/chromium/content/browser/android/content_readback_handler.h
new file mode 100644
index 00000000000..403bdd0a7af
--- /dev/null
+++ b/chromium/content/browser/android/content_readback_handler.h
@@ -0,0 +1,62 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_ANDROID_CONTENT_READBACK_HANDLER_H_
+#define CONTENT_BROWSER_ANDROID_CONTENT_READBACK_HANDLER_H_
+
+#include <jni.h>
+
+#include "base/android/jni_weak_ref.h"
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+
+class SkBitmap;
+
+namespace cc {
+class CopyOutputResult;
+}
+
+namespace content {
+
+// Native side of the ContentReadbackHandler.java, which issues content
+// readbacks from the Java side.
+class ContentReadbackHandler {
+ public:
+ // Registers the JNI methods for ContentViewRender.
+ static bool RegisterContentReadbackHandler(JNIEnv* env);
+
+ // Methods called from Java via JNI -----------------------------------------
+ ContentReadbackHandler(JNIEnv* env, jobject obj);
+ void Destroy(JNIEnv* env, jobject obj);
+ void GetContentBitmap(JNIEnv* env,
+ jobject obj,
+ jint readback_id,
+ jfloat scale,
+ jobject config,
+ jfloat x,
+ jfloat y,
+ jfloat width,
+ jfloat height,
+ jobject content_view_core);
+ void GetCompositorBitmap(JNIEnv* env,
+ jobject obj,
+ jint readback_id,
+ jlong native_window_android);
+
+ private:
+ virtual ~ContentReadbackHandler();
+
+ void OnFinishReadback(int readback_id,
+ bool success,
+ const SkBitmap& bitmap);
+
+ base::android::ScopedJavaGlobalRef<jobject> java_obj_;
+ base::WeakPtrFactory<ContentReadbackHandler> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ContentReadbackHandler);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_ANDROID_CONTENT_READBACK_HANDLER_H_
diff --git a/chromium/content/browser/android/content_settings.cc b/chromium/content/browser/android/content_settings.cc
index 7c6407fd177..a83169c2ce9 100644
--- a/chromium/content/browser/android/content_settings.cc
+++ b/chromium/content/browser/android/content_settings.cc
@@ -42,7 +42,7 @@ bool ContentSettings::GetJavaScriptEnabled(JNIEnv* env, jobject obj) {
return render_view_host->GetDelegate()->GetWebkitPrefs().javascript_enabled;
}
-void ContentSettings::WebContentsDestroyed(WebContents* web_contents) {
+void ContentSettings::WebContentsDestroyed() {
delete this;
}
diff --git a/chromium/content/browser/android/content_settings.h b/chromium/content/browser/android/content_settings.h
index 5c0f9a025ba..0728a6afabb 100644
--- a/chromium/content/browser/android/content_settings.h
+++ b/chromium/content/browser/android/content_settings.h
@@ -7,7 +7,7 @@
#include <jni.h>
-#include "base/android/jni_helper.h"
+#include "base/android/jni_weak_ref.h"
#include "content/public/browser/web_contents_observer.h"
namespace content {
@@ -26,7 +26,7 @@ class ContentSettings : public WebContentsObserver {
virtual ~ContentSettings();
// WebContentsObserver overrides:
- virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE;
+ virtual void WebContentsDestroyed() OVERRIDE;
// The Java counterpart to this class.
JavaObjectWeakGlobalRef content_settings_;
diff --git a/chromium/content/browser/android/content_startup_flags.cc b/chromium/content/browser/android/content_startup_flags.cc
index 7d950343443..a771daa8170 100644
--- a/chromium/content/browser/android/content_startup_flags.cc
+++ b/chromium/content/browser/android/content_startup_flags.cc
@@ -14,6 +14,7 @@
#include "content/public/common/content_switches.h"
#include "gpu/command_buffer/service/gpu_switches.h"
#include "ui/base/ui_base_switches.h"
+#include "ui/native_theme/native_theme_switches.h"
namespace content {
@@ -27,48 +28,45 @@ void SetContentCommandLineFlags(int max_render_process_count,
CommandLine* parsed_command_line = CommandLine::ForCurrentProcess();
+ int command_line_renderer_limit = -1;
if (parsed_command_line->HasSwitch(switches::kRendererProcessLimit)) {
std::string limit = parsed_command_line->GetSwitchValueASCII(
switches::kRendererProcessLimit);
int value;
- if (base::StringToInt(limit, &value))
- max_render_process_count = value;
+ if (base::StringToInt(limit, &value)) {
+ command_line_renderer_limit = value;
+ if (value <= 0)
+ max_render_process_count = 0;
+ }
}
- if (max_render_process_count <= 0) {
+ if (command_line_renderer_limit > 0) {
+ int limit = std::min(command_line_renderer_limit,
+ static_cast<int>(kMaxRendererProcessCount));
+ RenderProcessHost::SetMaxRendererProcessCount(limit);
+ } else if (max_render_process_count <= 0) {
// Need to ensure the command line flag is consistent as a lot of chrome
// internal code checks this directly, but it wouldn't normally get set when
// we are implementing an embedded WebView.
parsed_command_line->AppendSwitch(switches::kSingleProcess);
} else {
- max_render_process_count =
- std::min(max_render_process_count,
- static_cast<int>(content::kMaxRendererProcessCount));
- content::RenderProcessHost::SetMaxRendererProcessCount(
- max_render_process_count);
+ int default_maximum = RenderProcessHost::GetMaxRendererProcessCount();
+ DCHECK(default_maximum <= static_cast<int>(kMaxRendererProcessCount));
+ if (max_render_process_count < default_maximum)
+ RenderProcessHost::SetMaxRendererProcessCount(max_render_process_count);
}
- parsed_command_line->AppendSwitch(switches::kForceCompositingMode);
- parsed_command_line->AppendSwitch(switches::kAllowWebUICompositing);
parsed_command_line->AppendSwitch(switches::kEnableThreadedCompositing);
parsed_command_line->AppendSwitch(
switches::kEnableCompositingForFixedPosition);
parsed_command_line->AppendSwitch(switches::kEnableAcceleratedOverflowScroll);
- parsed_command_line->AppendSwitch(
- switches::kEnableAcceleratedScrollableFrames);
- parsed_command_line->AppendSwitch(
- switches::kEnableCompositedScrollingForFrames);
parsed_command_line->AppendSwitch(switches::kEnableBeginFrameScheduling);
- parsed_command_line->AppendSwitch(switches::kEnableDeadlineScheduling);
- parsed_command_line->AppendSwitch(switches::kDisableGestureDebounce);
parsed_command_line->AppendSwitch(switches::kEnableGestureTapHighlight);
parsed_command_line->AppendSwitch(switches::kEnablePinch);
parsed_command_line->AppendSwitch(switches::kEnableOverlayFullscreenVideo);
- parsed_command_line->AppendSwitch(switches::kEnableOverlayScrollbars);
+ parsed_command_line->AppendSwitch(switches::kEnableOverlayScrollbar);
parsed_command_line->AppendSwitch(switches::kEnableOverscrollNotifications);
- parsed_command_line->AppendSwitchASCII(switches::kTouchAckTimeoutDelayMs,
- "200");
// Run the GPU service as a thread in the browser instead of as a
// standalone process.
@@ -86,6 +84,8 @@ void SetContentCommandLineFlags(int max_render_process_count,
parsed_command_line->AppendSwitch(switches::kUIPrioritizeInGpuProcess);
+ parsed_command_line->AppendSwitch(switches::kEnableDelegatedRenderer);
+
if (!plugin_descriptor.empty()) {
parsed_command_line->AppendSwitchNative(
switches::kRegisterPepperPlugins, plugin_descriptor);
diff --git a/chromium/content/browser/android/content_video_view.cc b/chromium/content/browser/android/content_video_view.cc
index 9ee541aab0d..5407032441b 100644
--- a/chromium/content/browser/android/content_video_view.cc
+++ b/chromium/content/browser/android/content_video_view.cc
@@ -6,6 +6,8 @@
#include "base/command_line.h"
#include "base/logging.h"
+#include "base/message_loop/message_loop.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"
@@ -36,27 +38,29 @@ bool ContentVideoView::RegisterContentVideoView(JNIEnv* env) {
return RegisterNativesImpl(env);
}
-bool ContentVideoView::HasContentVideoView() {
+ContentVideoView* ContentVideoView::GetInstance() {
return g_content_video_view;
}
ContentVideoView::ContentVideoView(
- const ScopedJavaLocalRef<jobject>& context,
- const ScopedJavaLocalRef<jobject>& client,
BrowserMediaPlayerManager* manager)
- : manager_(manager) {
+ : manager_(manager),
+ weak_factory_(this) {
DCHECK(!g_content_video_view);
- JNIEnv *env = AttachCurrentThread();
- j_content_video_view_ = JavaObjectWeakGlobalRef(env,
- Java_ContentVideoView_createContentVideoView(env, context.obj(),
- reinterpret_cast<intptr_t>(this), client.obj()).obj());
+ j_content_video_view_ = CreateJavaObject();
g_content_video_view = this;
CreatePowerSaveBlocker();
}
ContentVideoView::~ContentVideoView() {
DCHECK(g_content_video_view);
- DestroyContentVideoView(true);
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> content_video_view = GetJavaObject(env);
+ if (!content_video_view.is_null()) {
+ Java_ContentVideoView_destroyContentVideoView(env,
+ content_video_view.obj(), true);
+ j_content_video_view_.reset();
+ }
g_content_video_view = NULL;
}
@@ -107,14 +111,26 @@ void ContentVideoView::OnPlaybackComplete() {
}
void ContentVideoView::OnExitFullscreen() {
- DestroyContentVideoView(false);
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> content_video_view = GetJavaObject(env);
+ if (!content_video_view.is_null())
+ Java_ContentVideoView_onExitFullscreen(env, content_video_view.obj());
}
void ContentVideoView::UpdateMediaMetadata() {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> content_video_view = GetJavaObject(env);
- if (!content_video_view.is_null())
- UpdateMediaMetadata(env, content_video_view.obj());
+ 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<int>(player->GetDuration().InMilliseconds()),
+ player->CanPause(),player->CanSeekForward(), player->CanSeekBackward());
+ }
}
int ContentVideoView::GetVideoWidth(JNIEnv*, jobject obj) const {
@@ -169,13 +185,11 @@ void ContentVideoView::SetSurface(JNIEnv* env, jobject obj,
gfx::ScopedJavaSurface::AcquireExternalSurface(surface));
}
-void ContentVideoView::UpdateMediaMetadata(JNIEnv* env, jobject obj) {
- media::MediaPlayerAndroid* player = manager_->GetFullscreenPlayer();
- if (player && player->IsPlayerReady())
- Java_ContentVideoView_onUpdateMediaMetadata(
- env, obj, player->GetVideoWidth(), player->GetVideoHeight(),
- player->GetDuration().InMilliseconds(), player->CanPause(),
- player->CanSeekForward(), player->CanSeekBackward());
+void ContentVideoView::RequestMediaMetadata(JNIEnv* env, jobject obj) {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&ContentVideoView::UpdateMediaMetadata,
+ weak_factory_.GetWeakPtr()));
}
ScopedJavaLocalRef<jobject> ContentVideoView::GetJavaObject(JNIEnv* env) {
@@ -194,7 +208,27 @@ gfx::NativeView ContentVideoView::GetNativeView() {
}
+JavaObjectWeakGlobalRef ContentVideoView::CreateJavaObject() {
+ ContentViewCoreImpl* content_view_core = manager_->GetContentViewCore();
+ JNIEnv* env = AttachCurrentThread();
+ bool legacyMode = CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableOverlayFullscreenVideoSubtitle);
+ return JavaObjectWeakGlobalRef(
+ env,
+ Java_ContentVideoView_createContentVideoView(
+ env,
+ content_view_core->GetContext().obj(),
+ reinterpret_cast<intptr_t>(this),
+ content_view_core->GetContentVideoViewClient().obj(),
+ legacyMode).obj());
+}
+
void ContentVideoView::CreatePowerSaveBlocker() {
+ if (!CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableOverlayFullscreenVideoSubtitle)) {
+ return;
+ }
+
if (power_save_blocker_) return;
power_save_blocker_ = PowerSaveBlocker::Create(
@@ -203,15 +237,4 @@ void ContentVideoView::CreatePowerSaveBlocker() {
static_cast<PowerSaveBlockerImpl*>(power_save_blocker_.get())->
InitDisplaySleepBlocker(GetNativeView());
}
-
-void ContentVideoView::DestroyContentVideoView(bool native_view_destroyed) {
- JNIEnv* env = AttachCurrentThread();
- ScopedJavaLocalRef<jobject> content_video_view = GetJavaObject(env);
- if (!content_video_view.is_null()) {
- j_content_video_view_.reset();
- Java_ContentVideoView_destroyContentVideoView(env,
- content_video_view.obj(), native_view_destroyed);
- j_content_video_view_.reset();
- }
-}
} // namespace content
diff --git a/chromium/content/browser/android/content_video_view.h b/chromium/content/browser/android/content_video_view.h
index 633b9031fbf..faea292b5f0 100644
--- a/chromium/content/browser/android/content_video_view.h
+++ b/chromium/content/browser/android/content_video_view.h
@@ -7,11 +7,12 @@
#include <jni.h>
-#include "base/android/jni_helper.h"
+#include "base/android/jni_weak_ref.h"
#include "base/android/scoped_java_ref.h"
#include "base/basictypes.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
#include "base/timer/timer.h"
#include "ui/gfx/native_widget_types.h"
@@ -28,10 +29,7 @@ class ContentVideoView {
public:
// Construct a ContentVideoView object. The |manager| will handle all the
// playback controls from the Java class.
- ContentVideoView(
- const base::android::ScopedJavaLocalRef<jobject>& context,
- const base::android::ScopedJavaLocalRef<jobject>& client,
- BrowserMediaPlayerManager* manager);
+ explicit ContentVideoView(BrowserMediaPlayerManager* manager);
~ContentVideoView();
@@ -41,8 +39,8 @@ class ContentVideoView {
static bool RegisterContentVideoView(JNIEnv* env);
static void KeepScreenOn(bool screen_on);
- // Return true if there is existing ContentVideoView object.
- static bool HasContentVideoView();
+ // Return the singleton object or NULL.
+ static ContentVideoView* GetInstance();
// Getter method called by the Java class to get the media information.
int GetVideoWidth(JNIEnv*, jobject obj) const;
@@ -50,7 +48,7 @@ class ContentVideoView {
int GetDurationInMilliSeconds(JNIEnv*, jobject obj) const;
int GetCurrentPosition(JNIEnv*, jobject obj) const;
bool IsPlaying(JNIEnv*, jobject obj);
- void UpdateMediaMetadata(JNIEnv*, jobject obj);
+ void RequestMediaMetadata(JNIEnv*, jobject obj);
// Called when the Java fullscreen view is destroyed. If
// |release_media_player| is true, |manager_| needs to release the player
@@ -78,9 +76,8 @@ class ContentVideoView {
base::android::ScopedJavaLocalRef<jobject> GetJavaObject(JNIEnv* env);
private:
- // Destroy the |j_content_video_view_|. If |native_view_destroyed| is true,
- // no further calls to the native object is allowed.
- void DestroyContentVideoView(bool native_view_destroyed);
+ // Creates the corresponding ContentVideoView Java object.
+ JavaObjectWeakGlobalRef CreateJavaObject();
// Returns the associated NativeView
gfx::NativeView GetNativeView();
@@ -102,6 +99,9 @@ class ContentVideoView {
// Weak reference of corresponding Java object.
JavaObjectWeakGlobalRef j_content_video_view_;
+ // Weak pointer for posting tasks.
+ base::WeakPtrFactory<ContentVideoView> weak_factory_;
+
DISALLOW_COPY_AND_ASSIGN(ContentVideoView);
};
diff --git a/chromium/content/browser/android/content_view_core_impl.cc b/chromium/content/browser/android/content_view_core_impl.cc
index f5450f45110..fc0af4c922b 100644
--- a/chromium/content/browser/android/content_view_core_impl.cc
+++ b/chromium/content/browser/android/content_view_core_impl.cc
@@ -15,37 +15,42 @@
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "cc/layers/layer.h"
+#include "cc/layers/solid_color_layer.h"
#include "cc/output/begin_frame_args.h"
+#include "content/browser/android/gesture_event_type.h"
#include "content/browser/android/interstitial_page_delegate_android.h"
#include "content/browser/android/load_url_params.h"
-#include "content/browser/android/touch_point.h"
#include "content/browser/frame_host/interstitial_page_impl.h"
#include "content/browser/frame_host/navigation_controller_impl.h"
#include "content/browser/frame_host/navigation_entry_impl.h"
-#include "content/browser/media/android/browser_media_player_manager.h"
+#include "content/browser/geolocation/geolocation_dispatcher_host.h"
+#include "content/browser/media/media_web_contents_observer.h"
#include "content/browser/renderer_host/compositor_impl_android.h"
+#include "content/browser/renderer_host/input/motion_event_android.h"
#include "content/browser/renderer_host/input/web_input_event_builders_android.h"
+#include "content/browser/renderer_host/input/web_input_event_util.h"
#include "content/browser/renderer_host/java/java_bound_object.h"
#include "content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_view_android.h"
+#include "content/browser/screen_orientation/screen_orientation_dispatcher_host.h"
#include "content/browser/ssl/ssl_host_state.h"
#include "content/browser/web_contents/web_contents_view_android.h"
+#include "content/common/frame_messages.h"
+#include "content/common/input/web_input_event_traits.h"
#include "content/common/input_messages.h"
#include "content/common/view_messages.h"
#include "content/public/browser/browser_accessibility_state.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/favicon_status.h"
-#include "content/public/browser/notification_details.h"
-#include "content/public/browser/notification_service.h"
-#include "content/public/browser/notification_source.h"
-#include "content/public/browser/notification_types.h"
+#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/menu_item.h"
#include "content/public/common/page_transition_types.h"
+#include "content/public/common/user_agent.h"
#include "jni/ContentViewCore_jni.h"
#include "third_party/WebKit/public/web/WebBindings.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
@@ -55,7 +60,6 @@
#include "ui/gfx/screen.h"
#include "ui/gfx/size_conversions.h"
#include "ui/gfx/size_f.h"
-#include "webkit/common/user_agent/user_agent_util.h"
using base::android::AttachCurrentThread;
using base::android::ConvertJavaStringToUTF16;
@@ -82,10 +86,6 @@ namespace content {
namespace {
-const unsigned int kDefaultVSyncIntervalMicros = 16666u;
-// TODO(brianderson): Use adaptive draw-time estimation.
-const float kDefaultBrowserCompositeVSyncFraction = 1.0f / 3;
-
const void* kContentViewUserDataKey = &kContentViewUserDataKey;
int GetRenderProcessIdFromRenderViewHost(RenderViewHost* host) {
@@ -109,6 +109,55 @@ ScopedJavaLocalRef<jobject> CreateJavaRect(
static_cast<int>(rect.bottom())));
}
+int ToGestureEventType(WebInputEvent::Type type) {
+ switch (type) {
+ case WebInputEvent::GestureScrollBegin:
+ return SCROLL_START;
+ case WebInputEvent::GestureScrollEnd:
+ return SCROLL_END;
+ case WebInputEvent::GestureScrollUpdate:
+ return SCROLL_BY;
+ case WebInputEvent::GestureFlingStart:
+ return FLING_START;
+ case WebInputEvent::GestureFlingCancel:
+ return FLING_CANCEL;
+ case WebInputEvent::GestureShowPress:
+ return SHOW_PRESS;
+ case WebInputEvent::GestureTap:
+ return SINGLE_TAP_CONFIRMED;
+ case WebInputEvent::GestureTapUnconfirmed:
+ return SINGLE_TAP_UNCONFIRMED;
+ case WebInputEvent::GestureTapDown:
+ return TAP_DOWN;
+ case WebInputEvent::GestureTapCancel:
+ return TAP_CANCEL;
+ case WebInputEvent::GestureDoubleTap:
+ return DOUBLE_TAP;
+ case WebInputEvent::GestureLongPress:
+ return LONG_PRESS;
+ case WebInputEvent::GestureLongTap:
+ return LONG_TAP;
+ case WebInputEvent::GesturePinchBegin:
+ return PINCH_BEGIN;
+ case WebInputEvent::GesturePinchEnd:
+ return PINCH_END;
+ case WebInputEvent::GesturePinchUpdate:
+ return PINCH_BY;
+ case WebInputEvent::GestureTwoFingerTap:
+ case WebInputEvent::GestureScrollUpdateWithoutPropagation:
+ default:
+ NOTREACHED() << "Invalid source gesture type: "
+ << WebInputEventTraits::GetName(type);
+ return -1;
+ };
+}
+
+float GetPrimaryDisplayDeviceScaleFactor() {
+ const gfx::Display& display =
+ gfx::Screen::GetNativeScreen()->GetPrimaryDisplay();
+ return display.device_scale_factor();
+}
+
} // namespace
// Enables a callback when the underlying WebContents is destroyed, to enable
@@ -159,31 +208,31 @@ ContentViewCore* ContentViewCore::GetNativeContentViewCore(JNIEnv* env,
Java_ContentViewCore_getNativeContentViewCore(env, obj));
}
-ContentViewCoreImpl::ContentViewCoreImpl(JNIEnv* env, jobject obj,
- bool hardware_accelerated,
- WebContents* web_contents,
- ui::ViewAndroid* view_android,
- ui::WindowAndroid* window_android)
+ContentViewCoreImpl::ContentViewCoreImpl(
+ JNIEnv* env,
+ jobject obj,
+ WebContents* web_contents,
+ ui::ViewAndroid* view_android,
+ ui::WindowAndroid* window_android,
+ jobject java_bridge_retained_object_set)
: WebContentsObserver(web_contents),
java_ref_(env, obj),
web_contents_(static_cast<WebContentsImpl*>(web_contents)),
- root_layer_(cc::Layer::Create()),
- vsync_interval_(base::TimeDelta::FromMicroseconds(
- kDefaultVSyncIntervalMicros)),
- expected_browser_composite_time_(base::TimeDelta::FromMicroseconds(
- kDefaultVSyncIntervalMicros * kDefaultBrowserCompositeVSyncFraction)),
+ root_layer_(cc::SolidColorLayer::Create()),
+ dpi_scale_(GetPrimaryDisplayDeviceScaleFactor()),
view_android_(view_android),
window_android_(window_android),
device_orientation_(0),
- geolocation_needs_pause_(false) {
+ accessibility_enabled_(false) {
CHECK(web_contents) <<
"A ContentViewCoreImpl should be created with a valid WebContents.";
- // TODO(leandrogracia): make use of the hardware_accelerated argument.
-
- const gfx::Display& display =
- gfx::Screen::GetNativeScreen()->GetPrimaryDisplay();
- dpi_scale_ = display.device_scale_factor();
+ root_layer_->SetBackgroundColor(GetBackgroundColor(env, obj));
+ gfx::Size physical_size(
+ Java_ContentViewCore_getPhysicalBackingWidthPix(env, obj),
+ Java_ContentViewCore_getPhysicalBackingHeightPix(env, obj));
+ root_layer_->SetBounds(physical_size);
+ root_layer_->SetIsDrawable(true);
// Currently, the only use case we have for overriding a user agent involves
// spoofing a desktop Linux user agent for "Request desktop site".
@@ -192,9 +241,13 @@ ContentViewCoreImpl::ContentViewCoreImpl(JNIEnv* env, jobject obj,
const char kLinuxInfoStr[] = "X11; Linux x86_64";
std::string product = content::GetContentClient()->GetProduct();
std::string spoofed_ua =
- webkit_glue::BuildUserAgentFromOSAndProduct(kLinuxInfoStr, product);
+ BuildUserAgentFromOSAndProduct(kLinuxInfoStr, product);
web_contents->SetUserAgentOverride(spoofed_ua);
+ java_bridge_dispatcher_host_manager_.reset(
+ new JavaBridgeDispatcherHostManager(web_contents,
+ java_bridge_retained_object_set));
+
InitWebContents();
}
@@ -206,9 +259,6 @@ ContentViewCoreImpl::~ContentViewCoreImpl() {
Java_ContentViewCore_onNativeContentViewCoreDestroyed(
env, j_obj.obj(), reinterpret_cast<intptr_t>(this));
}
- // Make sure nobody calls back into this object while we are tearing things
- // down.
- notification_registrar_.RemoveAll();
}
base::android::ScopedJavaLocalRef<jobject>
@@ -224,91 +274,54 @@ void ContentViewCoreImpl::OnJavaContentViewCoreDestroyed(JNIEnv* env,
void ContentViewCoreImpl::InitWebContents() {
DCHECK(web_contents_);
- notification_registrar_.Add(
- this, NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
- Source<WebContents>(web_contents_));
- notification_registrar_.Add(
- this, NOTIFICATION_RENDERER_PROCESS_CREATED,
- content::NotificationService::AllBrowserContextsAndSources());
- notification_registrar_.Add(
- this, NOTIFICATION_WEB_CONTENTS_CONNECTED,
- Source<WebContents>(web_contents_));
-
- static_cast<WebContentsViewAndroid*>(web_contents_->GetView())->
- SetContentViewCore(this);
+ static_cast<WebContentsViewAndroid*>(
+ static_cast<WebContentsImpl*>(web_contents_)->GetView())->
+ SetContentViewCore(this);
DCHECK(!web_contents_->GetUserData(kContentViewUserDataKey));
web_contents_->SetUserData(kContentViewUserDataKey,
new ContentViewUserData(this));
}
-void ContentViewCoreImpl::Observe(int type,
- const NotificationSource& source,
- const NotificationDetails& details) {
- switch (type) {
- case NOTIFICATION_RENDER_VIEW_HOST_CHANGED: {
- std::pair<RenderViewHost*, RenderViewHost*>* switched_details =
- Details<std::pair<RenderViewHost*, RenderViewHost*> >(details).ptr();
- int old_pid = 0;
- if (switched_details->first) {
- old_pid = GetRenderProcessIdFromRenderViewHost(
- switched_details->first);
-
- RenderWidgetHostViewAndroid* view =
- static_cast<RenderWidgetHostViewAndroid*>(
- switched_details->first->GetView());
- if (view)
- view->SetContentViewCore(NULL);
- }
- int new_pid = GetRenderProcessIdFromRenderViewHost(
- web_contents_->GetRenderViewHost());
- if (new_pid != old_pid) {
- // Notify the Java side of the change of the current renderer process.
- JNIEnv* env = AttachCurrentThread();
- ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
- if (!obj.is_null()) {
- Java_ContentViewCore_onRenderProcessSwap(
- env, obj.obj(), old_pid, new_pid);
- }
- }
- SetFocusInternal(HasFocus());
- if (geolocation_needs_pause_)
- PauseOrResumeGeolocation(true);
- break;
- }
- case NOTIFICATION_RENDERER_PROCESS_CREATED: {
- // Notify the Java side of the current renderer process.
- RenderProcessHost* source_process_host =
- Source<RenderProcessHost>(source).ptr();
- RenderProcessHost* current_process_host =
- web_contents_->GetRenderViewHost()->GetProcess();
-
- if (source_process_host == current_process_host) {
- int pid = GetRenderProcessIdFromRenderViewHost(
- web_contents_->GetRenderViewHost());
- JNIEnv* env = AttachCurrentThread();
- ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
- if (!obj.is_null()) {
- Java_ContentViewCore_onRenderProcessSwap(env, obj.obj(), 0, pid);
- }
- }
- break;
- }
- case NOTIFICATION_WEB_CONTENTS_CONNECTED: {
- JNIEnv* env = AttachCurrentThread();
- ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
- if (!obj.is_null()) {
- Java_ContentViewCore_onWebContentsConnected(env, obj.obj());
- }
- break;
- }
- }
-}
-
void ContentViewCoreImpl::RenderViewReady() {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
+ if (!obj.is_null())
+ Java_ContentViewCore_onRenderProcessChange(env, obj.obj());
+
if (device_orientation_ != 0)
SendOrientationChangeEventInternal();
}
+void ContentViewCoreImpl::RenderViewHostChanged(RenderViewHost* old_host,
+ RenderViewHost* new_host) {
+ int old_pid = 0;
+ if (old_host) {
+ old_pid = GetRenderProcessIdFromRenderViewHost(old_host);
+
+ RenderWidgetHostViewAndroid* view =
+ static_cast<RenderWidgetHostViewAndroid*>(old_host->GetView());
+ if (view)
+ view->SetContentViewCore(NULL);
+
+ view = static_cast<RenderWidgetHostViewAndroid*>(new_host->GetView());
+ if (view)
+ view->SetContentViewCore(this);
+ }
+ int new_pid = GetRenderProcessIdFromRenderViewHost(
+ web_contents_->GetRenderViewHost());
+ if (new_pid != old_pid) {
+ // Notify the Java side that the renderer process changed.
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
+ if (!obj.is_null()) {
+ Java_ContentViewCore_onRenderProcessChange(env, obj.obj());
+ }
+ }
+
+ SetFocusInternal(HasFocus());
+ SetAccessibilityEnabledInternal(accessibility_enabled_);
+}
+
RenderWidgetHostViewAndroid*
ContentViewCoreImpl::GetRenderWidgetHostViewAndroid() {
RenderWidgetHostView* rwhv = NULL;
@@ -353,37 +366,14 @@ void ContentViewCoreImpl::Hide() {
}
void ContentViewCoreImpl::PauseVideo() {
- RenderViewHost* host = web_contents_->GetRenderViewHost();
- if (host)
- host->Send(new ViewMsg_PauseVideo(host->GetRoutingID()));
+ RenderViewHostImpl* rvhi = static_cast<RenderViewHostImpl*>(
+ web_contents_->GetRenderViewHost());
+ if (rvhi)
+ rvhi->media_web_contents_observer()->PauseVideo();
}
void ContentViewCoreImpl::PauseOrResumeGeolocation(bool should_pause) {
- geolocation_needs_pause_ = should_pause;
- RenderViewHostImpl* rvh =
- static_cast<RenderViewHostImpl*>(web_contents_->GetRenderViewHost());
- if (rvh) {
- scoped_refptr<GeolocationDispatcherHost> geolocation_dispatcher =
- static_cast<RenderProcessHostImpl*>(
- web_contents_->GetRenderProcessHost())->
- geolocation_dispatcher_host();
- if (geolocation_dispatcher.get()) {
- BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
- base::Bind(&GeolocationDispatcherHost::PauseOrResume,
- geolocation_dispatcher,
- rvh->GetRoutingID(),
- should_pause));
- geolocation_needs_pause_ = false;
- }
- }
-}
-
-void ContentViewCoreImpl::OnTabCrashed() {
- JNIEnv* env = AttachCurrentThread();
- ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
- if (obj.is_null())
- return;
- Java_ContentViewCore_resetVSyncNotification(env, obj.obj());
+ web_contents_->geolocation_dispatcher_host()->PauseOrResume(should_pause);
}
// All positions and sizes are in CSS pixels.
@@ -404,11 +394,8 @@ void ContentViewCoreImpl::UpdateFrameInfo(
return;
if (window_android_) {
- gfx::Vector2dF window_offset(
- Java_ContentViewCore_getLocationInWindowX(env, obj.obj()),
- Java_ContentViewCore_getLocationInWindowY(env, obj.obj()));
window_android_->set_content_offset(
- gfx::ScaleVector2d(content_offset, dpi_scale_) + window_offset);
+ gfx::ScaleVector2d(content_offset, dpi_scale_));
}
Java_ContentViewCore_updateFrameInfo(
@@ -433,11 +420,13 @@ void ContentViewCoreImpl::SetTitle(const base::string16& title) {
if (obj.is_null())
return;
ScopedJavaLocalRef<jstring> jtitle =
- ConvertUTF8ToJavaString(env, UTF16ToUTF8(title));
+ ConvertUTF8ToJavaString(env, base::UTF16ToUTF8(title));
Java_ContentViewCore_setTitle(env, obj.obj(), jtitle.obj());
}
void ContentViewCoreImpl::OnBackgroundColorChanged(SkColor color) {
+ root_layer_->SetBackgroundColor(color);
+
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
if (obj.is_null())
@@ -445,13 +434,15 @@ void ContentViewCoreImpl::OnBackgroundColorChanged(SkColor color) {
Java_ContentViewCore_onBackgroundColorChanged(env, obj.obj(), color);
}
-void ContentViewCoreImpl::ShowSelectPopupMenu(
+void ContentViewCoreImpl::ShowSelectPopupMenu(const gfx::Rect& bounds,
const std::vector<MenuItem>& items, int selected_item, bool multiple) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> j_obj = java_ref_.get(env);
if (j_obj.is_null())
return;
+ ScopedJavaLocalRef<jobject> bounds_rect(CreateJavaRect(env, bounds));
+
// For multi-select list popups we find the list of previous selections by
// iterating through the items. But for single selection popups we take the
// given |selected_item| as is.
@@ -489,43 +480,105 @@ void ContentViewCoreImpl::ShowSelectPopupMenu(
ScopedJavaLocalRef<jobjectArray> items_array(
base::android::ToJavaArrayOfStrings(env, labels));
Java_ContentViewCore_showSelectPopup(env, j_obj.obj(),
- items_array.obj(), enabled_array.obj(),
- multiple, selected_array.obj());
+ bounds_rect.obj(),
+ items_array.obj(),
+ enabled_array.obj(),
+ multiple,
+ selected_array.obj());
}
-void ContentViewCoreImpl::ConfirmTouchEvent(InputEventAckState ack_result) {
+void ContentViewCoreImpl::HideSelectPopupMenu() {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> j_obj = java_ref_.get(env);
- if (j_obj.is_null())
- return;
- Java_ContentViewCore_confirmTouchEvent(env, j_obj.obj(),
- static_cast<jint>(ack_result));
+ if (!j_obj.is_null())
+ Java_ContentViewCore_hideSelectPopup(env, j_obj.obj());
}
-void ContentViewCoreImpl::UnhandledFlingStartEvent() {
+void ContentViewCoreImpl::OnGestureEventAck(const blink::WebGestureEvent& event,
+ InputEventAckState ack_result) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> j_obj = java_ref_.get(env);
if (j_obj.is_null())
return;
- Java_ContentViewCore_unhandledFlingStartEvent(env, j_obj.obj());
-}
-void ContentViewCoreImpl::OnScrollUpdateGestureConsumed() {
- JNIEnv* env = AttachCurrentThread();
- ScopedJavaLocalRef<jobject> j_obj = java_ref_.get(env);
- if (j_obj.is_null())
- return;
- Java_ContentViewCore_onScrollUpdateGestureConsumed(env, j_obj.obj());
+ switch (event.type) {
+ case WebInputEvent::GestureFlingStart:
+ if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED) {
+ // The view expects the fling velocity in pixels/s.
+ Java_ContentViewCore_onFlingStartEventConsumed(env, j_obj.obj(),
+ event.data.flingStart.velocityX * dpi_scale(),
+ event.data.flingStart.velocityY * dpi_scale());
+ } else {
+ // If a scroll ends with a fling, a SCROLL_END event is never sent.
+ // However, if that fling went unconsumed, we still need to let the
+ // listeners know that scrolling has ended.
+ Java_ContentViewCore_onScrollEndEventAck(env, j_obj.obj());
+ }
+
+ if (ack_result == INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS) {
+ // The view expects the fling velocity in pixels/s.
+ Java_ContentViewCore_onFlingStartEventHadNoConsumer(env, j_obj.obj(),
+ event.data.flingStart.velocityX * dpi_scale(),
+ event.data.flingStart.velocityY * dpi_scale());
+ }
+ break;
+ case WebInputEvent::GestureFlingCancel:
+ Java_ContentViewCore_onFlingCancelEventAck(env, j_obj.obj());
+ break;
+ case WebInputEvent::GestureScrollBegin:
+ Java_ContentViewCore_onScrollBeginEventAck(env, j_obj.obj());
+ break;
+ case WebInputEvent::GestureScrollUpdate:
+ if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED)
+ Java_ContentViewCore_onScrollUpdateGestureConsumed(env, j_obj.obj());
+ break;
+ case WebInputEvent::GestureScrollEnd:
+ Java_ContentViewCore_onScrollEndEventAck(env, j_obj.obj());
+ break;
+ case WebInputEvent::GesturePinchBegin:
+ Java_ContentViewCore_onPinchBeginEventAck(env, j_obj.obj());
+ break;
+ case WebInputEvent::GesturePinchEnd:
+ Java_ContentViewCore_onPinchEndEventAck(env, j_obj.obj());
+ break;
+ case WebInputEvent::GestureTap:
+ Java_ContentViewCore_onSingleTapEventAck(
+ env,
+ j_obj.obj(),
+ ack_result == INPUT_EVENT_ACK_STATE_CONSUMED,
+ event.x * dpi_scale(),
+ event.y * dpi_scale());
+ break;
+ case WebInputEvent::GestureDoubleTap:
+ Java_ContentViewCore_onDoubleTapEventAck(env, j_obj.obj());
+ break;
+ default:
+ break;
+ }
}
-void ContentViewCoreImpl::HasTouchEventHandlers(bool need_touch_events) {
+bool ContentViewCoreImpl::FilterInputEvent(const blink::WebInputEvent& event) {
+ if (event.type != WebInputEvent::GestureTap &&
+ event.type != WebInputEvent::GestureDoubleTap &&
+ event.type != WebInputEvent::GestureLongTap &&
+ event.type != WebInputEvent::GestureLongPress)
+ return false;
+
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> j_obj = java_ref_.get(env);
if (j_obj.is_null())
- return;
- Java_ContentViewCore_hasTouchEventHandlers(env,
- j_obj.obj(),
- need_touch_events);
+ return false;
+
+ const blink::WebGestureEvent& gesture =
+ static_cast<const blink::WebGestureEvent&>(event);
+ int gesture_type = ToGestureEventType(event.type);
+ return Java_ContentViewCore_filterTapOrPressEvent(env,
+ j_obj.obj(),
+ gesture_type,
+ gesture.x * dpi_scale(),
+ gesture.y * dpi_scale());
+
+ // TODO(jdduke): Also report double-tap UMA, crbug/347568.
}
bool ContentViewCoreImpl::HasFocus() {
@@ -573,14 +626,19 @@ void ContentViewCoreImpl::ShowPastePopup(int x_dip, int y_dip) {
static_cast<jint>(y_dip));
}
-unsigned int ContentViewCoreImpl::GetScaledContentTexture(
+void ContentViewCoreImpl::GetScaledContentBitmap(
float scale,
- gfx::Size* out_size) {
+ jobject jbitmap_config,
+ gfx::Rect src_subrect,
+ const base::Callback<void(bool, const SkBitmap&)>& result_callback) {
RenderWidgetHostViewAndroid* view = GetRenderWidgetHostViewAndroid();
- if (!view)
- return 0;
-
- return view->GetScaledContentTexture(scale, out_size);
+ if (!view) {
+ result_callback.Run(false, SkBitmap());
+ return;
+ }
+ SkBitmap::Config skbitmap_format = gfx::ConvertToSkiaConfig(jbitmap_config);
+ view->GetScaledContentBitmap(scale, skbitmap_format, src_subrect,
+ result_callback);
}
void ContentViewCoreImpl::StartContentIntent(const GURL& content_url) {
@@ -625,25 +683,6 @@ ScopedJavaLocalRef<jobject> ContentViewCoreImpl::CreateTouchEventSynthesizer() {
return Java_ContentViewCore_createTouchEventSynthesizer(env, obj.obj());
}
-void ContentViewCoreImpl::NotifyExternalSurface(
- int player_id, bool is_request, const gfx::RectF& rect) {
- JNIEnv* env = AttachCurrentThread();
-
- ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
- if (obj.is_null())
- return;
-
- Java_ContentViewCore_notifyExternalSurface(
- env,
- obj.obj(),
- static_cast<jint>(player_id),
- static_cast<jboolean>(is_request),
- static_cast<jfloat>(rect.x()),
- static_cast<jfloat>(rect.y()),
- static_cast<jfloat>(rect.width()),
- static_cast<jfloat>(rect.height()));
-}
-
ScopedJavaLocalRef<jobject> ContentViewCoreImpl::GetContentVideoViewClient() {
JNIEnv* env = AttachCurrentThread();
@@ -675,6 +714,21 @@ bool ContentViewCoreImpl::ShouldBlockMediaRequest(const GURL& url) {
j_url.obj());
}
+void ContentViewCoreImpl::DidStopFlinging() {
+ JNIEnv* env = AttachCurrentThread();
+
+ ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
+ if (!obj.is_null())
+ Java_ContentViewCore_onNativeFlingStopped(env, obj.obj());
+}
+
+gfx::Size ContentViewCoreImpl::GetViewSize() const {
+ gfx::Size size = GetViewportSizeDip();
+ gfx::Size offset = GetViewportSizeOffsetDip();
+ size.Enlarge(-offset.width(), -offset.height());
+ return size;
+}
+
gfx::Size ContentViewCoreImpl::GetPhysicalBackingSize() const {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> j_obj = java_ref_.get(env);
@@ -707,12 +761,12 @@ gfx::Size ContentViewCoreImpl::GetViewportSizeOffsetPix() const {
gfx::Size ContentViewCoreImpl::GetViewportSizeDip() const {
return gfx::ToCeiledSize(
- gfx::ScaleSize(GetViewportSizePix(), 1.0f / GetDpiScale()));
+ gfx::ScaleSize(GetViewportSizePix(), 1.0f / dpi_scale()));
}
gfx::Size ContentViewCoreImpl::GetViewportSizeOffsetDip() const {
return gfx::ToCeiledSize(
- gfx::ScaleSize(GetViewportSizeOffsetPix(), 1.0f / GetDpiScale()));
+ gfx::ScaleSize(GetViewportSizeOffsetPix(), 1.0f / dpi_scale()));
}
float ContentViewCoreImpl::GetOverdrawBottomHeightDip() const {
@@ -721,15 +775,19 @@ float ContentViewCoreImpl::GetOverdrawBottomHeightDip() const {
if (j_obj.is_null())
return 0.f;
return Java_ContentViewCore_getOverdrawBottomHeightPix(env, j_obj.obj())
- / GetDpiScale();
+ / dpi_scale();
}
void ContentViewCoreImpl::AttachLayer(scoped_refptr<cc::Layer> layer) {
root_layer_->AddChild(layer);
+ root_layer_->SetIsDrawable(false);
}
void ContentViewCoreImpl::RemoveLayer(scoped_refptr<cc::Layer> layer) {
layer->RemoveFromParent();
+
+ if (!root_layer_->children().size())
+ root_layer_->SetIsDrawable(true);
}
void ContentViewCoreImpl::LoadUrl(
@@ -737,30 +795,6 @@ void ContentViewCoreImpl::LoadUrl(
GetWebContents()->GetController().LoadURLWithParams(params);
}
-void ContentViewCoreImpl::AddBeginFrameSubscriber() {
- JNIEnv* env = AttachCurrentThread();
- ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
- if (obj.is_null())
- return;
- Java_ContentViewCore_addVSyncSubscriber(env, obj.obj());
-}
-
-void ContentViewCoreImpl::RemoveBeginFrameSubscriber() {
- JNIEnv* env = AttachCurrentThread();
- ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
- if (obj.is_null())
- return;
- Java_ContentViewCore_removeVSyncSubscriber(env, obj.obj());
-}
-
-void ContentViewCoreImpl::SetNeedsAnimate() {
- JNIEnv* env = AttachCurrentThread();
- ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
- if (obj.is_null())
- return;
- Java_ContentViewCore_setNeedsAnimate(env, obj.obj());
-}
-
ui::ViewAndroid* ContentViewCoreImpl::GetViewAndroid() const {
// view_android_ should never be null for Chrome.
DCHECK(view_android_);
@@ -805,12 +839,15 @@ void ContentViewCoreImpl::LoadUrl(
jstring url,
jint load_url_type,
jint transition_type,
+ jstring j_referrer_url,
+ jint referrer_policy,
jint ua_override_option,
jstring extra_headers,
jbyteArray post_data,
jstring base_url_for_data_url,
jstring virtual_url_for_data_url,
- jboolean can_load_local_resources) {
+ jboolean can_load_local_resources,
+ jboolean is_renderer_initiated) {
DCHECK(url);
NavigationController::LoadURLParams params(
GURL(ConvertJavaStringToUTF8(env, url)));
@@ -843,13 +880,15 @@ void ContentViewCoreImpl::LoadUrl(
}
params.can_load_local_resources = can_load_local_resources;
+ if (j_referrer_url) {
+ params.referrer = content::Referrer(
+ GURL(ConvertJavaStringToUTF8(env, j_referrer_url)),
+ static_cast<blink::WebReferrerPolicy>(referrer_policy));
+ }
- LoadUrl(params);
-}
+ params.is_renderer_initiated = is_renderer_initiated;
-jint ContentViewCoreImpl::GetCurrentRenderProcessId(JNIEnv* env, jobject obj) {
- return GetRenderProcessIdFromRenderViewHost(
- web_contents_->GetRenderViewHost());
+ LoadUrl(params);
}
ScopedJavaLocalRef<jstring> ContentViewCoreImpl::GetURL(
@@ -857,11 +896,6 @@ ScopedJavaLocalRef<jstring> ContentViewCoreImpl::GetURL(
return ConvertUTF8ToJavaString(env, GetWebContents()->GetURL().spec());
}
-ScopedJavaLocalRef<jstring> ContentViewCoreImpl::GetTitle(
- JNIEnv* env, jobject obj) const {
- return ConvertUTF16ToJavaString(env, GetWebContents()->GetTitle());
-}
-
jboolean ContentViewCoreImpl::IsIncognito(JNIEnv* env, jobject obj) {
return GetWebContents()->GetBrowserContext()->IsOffTheRecord();
}
@@ -893,39 +927,55 @@ void ContentViewCoreImpl::SendOrientationChangeEvent(JNIEnv* env,
}
}
-jboolean ContentViewCoreImpl::SendTouchEvent(JNIEnv* env,
- jobject obj,
- jlong time_ms,
- jint type,
- jobjectArray pts) {
+jboolean ContentViewCoreImpl::OnTouchEvent(JNIEnv* env,
+ jobject obj,
+ jobject motion_event,
+ jlong time_ms,
+ jint android_action,
+ jint pointer_count,
+ jint history_size,
+ jint action_index,
+ jfloat pos_x_0,
+ jfloat pos_y_0,
+ jfloat pos_x_1,
+ jfloat pos_y_1,
+ jint pointer_id_0,
+ jint pointer_id_1,
+ jfloat touch_major_0,
+ jfloat touch_major_1,
+ jfloat raw_pos_x,
+ jfloat raw_pos_y) {
RenderWidgetHostViewAndroid* rwhv = GetRenderWidgetHostViewAndroid();
- if (rwhv) {
- using blink::WebTouchEvent;
- blink::WebTouchEvent event;
- TouchPoint::BuildWebTouchEvent(env, type, time_ms, GetDpiScale(), pts,
- event);
- rwhv->SendTouchEvent(event);
- return true;
- }
- return false;
-}
+ // Avoid synthesizing a touch event if it cannot be forwarded.
+ if (!rwhv)
+ return false;
-float ContentViewCoreImpl::GetTouchPaddingDip() {
- return 48.0f / GetDpiScale();
+ MotionEventAndroid event(1.f / dpi_scale(),
+ env,
+ motion_event,
+ time_ms,
+ android_action,
+ pointer_count,
+ history_size,
+ action_index,
+ pos_x_0,
+ pos_y_0,
+ pos_x_1,
+ pos_y_1,
+ pointer_id_0,
+ pointer_id_1,
+ touch_major_0,
+ touch_major_1,
+ raw_pos_x,
+ raw_pos_y);
+
+ return rwhv->OnTouchEvent(event);
}
float ContentViewCoreImpl::GetDpiScale() const {
return dpi_scale_;
}
-void ContentViewCoreImpl::RequestContentClipping(
- const gfx::Rect& clipping,
- const gfx::Size& content_size) {
- RenderWidgetHostViewAndroid* rwhv = GetRenderWidgetHostViewAndroid();
- if (rwhv)
- rwhv->RequestContentClipping(clipping, content_size);
-}
-
jboolean ContentViewCoreImpl::SendMouseMoveEvent(JNIEnv* env,
jobject obj,
jlong time_ms,
@@ -938,7 +988,7 @@ jboolean ContentViewCoreImpl::SendMouseMoveEvent(JNIEnv* env,
blink::WebMouseEvent event = WebMouseEventBuilder::Build(
WebInputEvent::MouseMove,
blink::WebMouseEvent::ButtonNone,
- time_ms / 1000.0, x / GetDpiScale(), y / GetDpiScale(), 0, 1);
+ time_ms / 1000.0, x / dpi_scale(), y / dpi_scale(), 0, 1);
rwhv->SendMouseEvent(event);
return true;
@@ -963,7 +1013,7 @@ jboolean ContentViewCoreImpl::SendMouseWheelEvent(JNIEnv* env,
return false;
}
blink::WebMouseWheelEvent event = WebMouseWheelEventBuilder::Build(
- direction, time_ms / 1000.0, x / GetDpiScale(), y / GetDpiScale());
+ direction, time_ms / 1000.0, x / dpi_scale(), y / dpi_scale());
rwhv->SendMouseWheelEvent(event);
return true;
@@ -972,7 +1022,7 @@ jboolean ContentViewCoreImpl::SendMouseWheelEvent(JNIEnv* env,
WebGestureEvent ContentViewCoreImpl::MakeGestureEvent(
WebInputEvent::Type type, int64 time_ms, float x, float y) const {
return WebGestureEventBuilder::Build(
- type, time_ms / 1000.0, x / GetDpiScale(), y / GetDpiScale());
+ type, time_ms / 1000.0, x / dpi_scale(), y / dpi_scale());
}
void ContentViewCoreImpl::SendGestureEvent(
@@ -982,10 +1032,18 @@ void ContentViewCoreImpl::SendGestureEvent(
rwhv->SendGestureEvent(event);
}
-void ContentViewCoreImpl::ScrollBegin(JNIEnv* env, jobject obj, jlong time_ms,
- jfloat x, jfloat y) {
+void ContentViewCoreImpl::ScrollBegin(JNIEnv* env,
+ jobject obj,
+ jlong time_ms,
+ jfloat x,
+ jfloat y,
+ jfloat hintx,
+ jfloat hinty) {
WebGestureEvent event = MakeGestureEvent(
WebInputEvent::GestureScrollBegin, time_ms, x, y);
+ event.data.scrollBegin.deltaXHint = hintx / dpi_scale();
+ event.data.scrollBegin.deltaYHint = hinty / dpi_scale();
+
SendGestureEvent(event);
}
@@ -999,8 +1057,8 @@ void ContentViewCoreImpl::ScrollBy(JNIEnv* env, jobject obj, jlong time_ms,
jfloat x, jfloat y, jfloat dx, jfloat dy) {
WebGestureEvent event = MakeGestureEvent(
WebInputEvent::GestureScrollUpdate, time_ms, x, y);
- event.data.scrollUpdate.deltaX = -dx / GetDpiScale();
- event.data.scrollUpdate.deltaY = -dy / GetDpiScale();
+ event.data.scrollUpdate.deltaX = -dx / dpi_scale();
+ event.data.scrollUpdate.deltaY = -dy / dpi_scale();
SendGestureEvent(event);
}
@@ -1009,11 +1067,8 @@ void ContentViewCoreImpl::FlingStart(JNIEnv* env, jobject obj, jlong time_ms,
jfloat x, jfloat y, jfloat vx, jfloat vy) {
WebGestureEvent event = MakeGestureEvent(
WebInputEvent::GestureFlingStart, time_ms, x, y);
-
- // Velocity should not be scaled by DIP since that interacts poorly with the
- // deceleration constants. The DIP scaling is done on the renderer.
- event.data.flingStart.velocityX = vx;
- event.data.flingStart.velocityY = vy;
+ event.data.flingStart.velocityX = vx / dpi_scale();
+ event.data.flingStart.velocityY = vy / dpi_scale();
SendGestureEvent(event);
}
@@ -1025,59 +1080,11 @@ void ContentViewCoreImpl::FlingCancel(JNIEnv* env, jobject obj, jlong time_ms) {
}
void ContentViewCoreImpl::SingleTap(JNIEnv* env, jobject obj, jlong time_ms,
- jfloat x, jfloat y,
- jboolean disambiguation_popup_tap) {
+ jfloat x, jfloat y) {
WebGestureEvent event = MakeGestureEvent(
WebInputEvent::GestureTap, time_ms, x, y);
-
event.data.tap.tapCount = 1;
- if (!disambiguation_popup_tap) {
- const float touch_padding_dip = GetTouchPaddingDip();
- event.data.tap.width = touch_padding_dip;
- event.data.tap.height = touch_padding_dip;
- }
-
- SendGestureEvent(event);
-}
-
-void ContentViewCoreImpl::SingleTapUnconfirmed(JNIEnv* env, jobject obj,
- jlong time_ms,
- jfloat x, jfloat y) {
- WebGestureEvent event = MakeGestureEvent(
- WebInputEvent::GestureTapUnconfirmed, time_ms, x, y);
-
- event.data.tap.tapCount = 1;
-
- const float touch_padding_dip = GetTouchPaddingDip();
- event.data.tap.width = touch_padding_dip;
- event.data.tap.height = touch_padding_dip;
-
- SendGestureEvent(event);
-}
-void ContentViewCoreImpl::ShowPressState(JNIEnv* env, jobject obj,
- jlong time_ms,
- jfloat x, jfloat y) {
- WebGestureEvent event = MakeGestureEvent(
- WebInputEvent::GestureShowPress, time_ms, x, y);
- SendGestureEvent(event);
-}
-
-void ContentViewCoreImpl::TapCancel(JNIEnv* env,
- jobject obj,
- jlong time_ms,
- jfloat x,
- jfloat y) {
- WebGestureEvent event = MakeGestureEvent(
- WebInputEvent::GestureTapCancel, time_ms, x, y);
- SendGestureEvent(event);
-}
-
-void ContentViewCoreImpl::TapDown(JNIEnv* env, jobject obj,
- jlong time_ms,
- jfloat x, jfloat y) {
- WebGestureEvent event = MakeGestureEvent(
- WebInputEvent::GestureTapDown, time_ms, x, y);
SendGestureEvent(event);
}
@@ -1085,36 +1092,18 @@ void ContentViewCoreImpl::DoubleTap(JNIEnv* env, jobject obj, jlong time_ms,
jfloat x, jfloat y) {
WebGestureEvent event = MakeGestureEvent(
WebInputEvent::GestureDoubleTap, time_ms, x, y);
+ // Set the tap count to 1 even for DoubleTap, in order to be consistent with
+ // double tap behavior on a mobile viewport. See crbug.com/234986 for context.
+ event.data.tap.tapCount = 1;
+
SendGestureEvent(event);
}
void ContentViewCoreImpl::LongPress(JNIEnv* env, jobject obj, jlong time_ms,
- jfloat x, jfloat y,
- jboolean disambiguation_popup_tap) {
+ jfloat x, jfloat y) {
WebGestureEvent event = MakeGestureEvent(
WebInputEvent::GestureLongPress, time_ms, x, y);
- if (!disambiguation_popup_tap) {
- const float touch_padding_dip = GetTouchPaddingDip();
- event.data.longPress.width = touch_padding_dip;
- event.data.longPress.height = touch_padding_dip;
- }
-
- SendGestureEvent(event);
-}
-
-void ContentViewCoreImpl::LongTap(JNIEnv* env, jobject obj, jlong time_ms,
- jfloat x, jfloat y,
- jboolean disambiguation_popup_tap) {
- WebGestureEvent event = MakeGestureEvent(
- WebInputEvent::GestureLongTap, time_ms, x, y);
-
- if (!disambiguation_popup_tap) {
- const float touch_padding_dip = GetTouchPaddingDip();
- event.data.longPress.width = touch_padding_dip;
- event.data.longPress.height = touch_padding_dip;
- }
-
SendGestureEvent(event);
}
@@ -1144,21 +1133,44 @@ void ContentViewCoreImpl::PinchBy(JNIEnv* env, jobject obj, jlong time_ms,
void ContentViewCoreImpl::SelectBetweenCoordinates(JNIEnv* env, jobject obj,
jfloat x1, jfloat y1,
jfloat x2, jfloat y2) {
- if (GetRenderWidgetHostViewAndroid()) {
- GetRenderWidgetHostViewAndroid()->SelectRange(
- gfx::Point(x1 / GetDpiScale(), y1 / GetDpiScale()),
- gfx::Point(x2 / GetDpiScale(), y2 / GetDpiScale()));
- }
+ if (!web_contents_)
+ return;
+
+ web_contents_->SelectRange(
+ gfx::Point(x1 / dpi_scale(), y1 / dpi_scale()),
+ gfx::Point(x2 / dpi_scale(), y2 / dpi_scale()));
}
void ContentViewCoreImpl::MoveCaret(JNIEnv* env, jobject obj,
jfloat x, jfloat y) {
if (GetRenderWidgetHostViewAndroid()) {
GetRenderWidgetHostViewAndroid()->MoveCaret(
- gfx::Point(x / GetDpiScale(), y / GetDpiScale()));
+ gfx::Point(x / dpi_scale(), y / dpi_scale()));
}
}
+void ContentViewCoreImpl::ResetGestureDetection(JNIEnv* env, jobject obj) {
+ RenderWidgetHostViewAndroid* rwhv = GetRenderWidgetHostViewAndroid();
+ if (rwhv)
+ rwhv->ResetGestureDetection();
+}
+
+void ContentViewCoreImpl::SetDoubleTapSupportEnabled(JNIEnv* env,
+ jobject obj,
+ jboolean enabled) {
+ RenderWidgetHostViewAndroid* rwhv = GetRenderWidgetHostViewAndroid();
+ if (rwhv)
+ rwhv->SetDoubleTapSupportEnabled(enabled);
+}
+
+void ContentViewCoreImpl::SetMultiTouchZoomSupportEnabled(JNIEnv* env,
+ jobject obj,
+ jboolean enabled) {
+ RenderWidgetHostViewAndroid* rwhv = GetRenderWidgetHostViewAndroid();
+ if (rwhv)
+ rwhv->SetMultiTouchZoomSupportEnabled(enabled);
+}
+
void ContentViewCoreImpl::LoadIfNecessary(JNIEnv* env, jobject obj) {
web_contents_->GetController().LoadIfNecessary();
}
@@ -1167,10 +1179,6 @@ void ContentViewCoreImpl::RequestRestoreLoad(JNIEnv* env, jobject obj) {
web_contents_->GetController().SetNeedsReload();
}
-void ContentViewCoreImpl::StopLoading(JNIEnv* env, jobject obj) {
- web_contents_->Stop();
-}
-
void ContentViewCoreImpl::Reload(JNIEnv* env,
jobject obj,
jboolean check_for_repost) {
@@ -1200,102 +1208,68 @@ void ContentViewCoreImpl::ClearHistory(JNIEnv* env, jobject obj) {
web_contents_->GetController().PruneAllButLastCommitted();
}
+void ContentViewCoreImpl::AddStyleSheetByURL(
+ JNIEnv* env, jobject obj, jstring url) {
+ if (!web_contents_)
+ return;
+
+ web_contents_->GetMainFrame()->Send(new FrameMsg_AddStyleSheetByURL(
+ web_contents_->GetMainFrame()->GetRoutingID(),
+ ConvertJavaStringToUTF8(env, url)));
+}
+
+void ContentViewCoreImpl::SetAllowJavascriptInterfacesInspection(
+ JNIEnv* env,
+ jobject obj,
+ jboolean allow) {
+ java_bridge_dispatcher_host_manager_->SetAllowObjectContentsInspection(allow);
+}
+
void ContentViewCoreImpl::AddJavascriptInterface(
JNIEnv* env,
jobject /* obj */,
jobject object,
jstring name,
- jclass safe_annotation_clazz,
- jobject retained_object_set) {
+ jclass safe_annotation_clazz) {
ScopedJavaLocalRef<jobject> scoped_object(env, object);
ScopedJavaLocalRef<jclass> scoped_clazz(env, safe_annotation_clazz);
- JavaObjectWeakGlobalRef weak_retained_object_set(env, retained_object_set);
// JavaBoundObject creates the NPObject with a ref count of 1, and
// JavaBridgeDispatcherHostManager takes its own ref.
- JavaBridgeDispatcherHostManager* java_bridge =
- web_contents_->java_bridge_dispatcher_host_manager();
- java_bridge->SetRetainedObjectSet(weak_retained_object_set);
- NPObject* bound_object = JavaBoundObject::Create(scoped_object, scoped_clazz,
- java_bridge->AsWeakPtr());
- java_bridge->AddNamedObject(ConvertJavaStringToUTF16(env, name),
- bound_object);
+ NPObject* bound_object = JavaBoundObject::Create(
+ scoped_object,
+ scoped_clazz,
+ java_bridge_dispatcher_host_manager_->AsWeakPtr(),
+ java_bridge_dispatcher_host_manager_->GetAllowObjectContentsInspection());
+ java_bridge_dispatcher_host_manager_->AddNamedObject(
+ ConvertJavaStringToUTF16(env, name), bound_object);
blink::WebBindings::releaseObject(bound_object);
}
void ContentViewCoreImpl::RemoveJavascriptInterface(JNIEnv* env,
jobject /* obj */,
jstring name) {
- web_contents_->java_bridge_dispatcher_host_manager()->RemoveNamedObject(
+ java_bridge_dispatcher_host_manager_->RemoveNamedObject(
ConvertJavaStringToUTF16(env, name));
}
-void ContentViewCoreImpl::UpdateVSyncParameters(JNIEnv* env, jobject /* obj */,
- jlong timebase_micros,
- jlong interval_micros) {
- RenderWidgetHostViewAndroid* view = GetRenderWidgetHostViewAndroid();
- if (!view)
- return;
-
- RenderWidgetHostImpl* host = RenderWidgetHostImpl::From(
- view->GetRenderWidgetHost());
-
- host->UpdateVSyncParameters(
- base::TimeTicks::FromInternalValue(timebase_micros),
- base::TimeDelta::FromMicroseconds(interval_micros));
-
- vsync_interval_ =
- base::TimeDelta::FromMicroseconds(interval_micros);
- expected_browser_composite_time_ =
- vsync_interval_ * kDefaultBrowserCompositeVSyncFraction;
-}
-
-void ContentViewCoreImpl::OnVSync(JNIEnv* env, jobject /* obj */,
- jlong frame_time_micros) {
- base::TimeTicks frame_time =
- base::TimeTicks::FromInternalValue(frame_time_micros);
- SendBeginFrame(frame_time);
-}
-
-void ContentViewCoreImpl::SendBeginFrame(base::TimeTicks frame_time) {
- RenderWidgetHostViewAndroid* view = GetRenderWidgetHostViewAndroid();
- if (!view)
- return;
-
- base::TimeTicks display_time = frame_time + vsync_interval_;
- base::TimeTicks deadline = display_time - expected_browser_composite_time_;
-
- view->SendBeginFrame(
- cc::BeginFrameArgs::Create(frame_time, deadline, vsync_interval_));
-}
-
-jboolean ContentViewCoreImpl::OnAnimate(JNIEnv* env, jobject /* obj */,
- jlong frame_time_micros) {
- RenderWidgetHostViewAndroid* view = GetRenderWidgetHostViewAndroid();
- if (!view)
- return false;
-
- return view->Animate(base::TimeTicks::FromInternalValue(frame_time_micros));
-}
-
-jboolean ContentViewCoreImpl::PopulateBitmapFromCompositor(JNIEnv* env,
- jobject obj,
- jobject jbitmap) {
- RenderWidgetHostViewAndroid* view = GetRenderWidgetHostViewAndroid();
- if (!view)
- return false;
-
- return view->PopulateBitmapWithContents(jbitmap);
-}
-
void ContentViewCoreImpl::WasResized(JNIEnv* env, jobject obj) {
RenderWidgetHostViewAndroid* view = GetRenderWidgetHostViewAndroid();
- if (view)
+ gfx::Size physical_size(
+ Java_ContentViewCore_getPhysicalBackingWidthPix(env, obj),
+ Java_ContentViewCore_getPhysicalBackingHeightPix(env, obj));
+ root_layer_->SetBounds(physical_size);
+
+ if (view) {
+ RenderWidgetHostImpl* host = RenderWidgetHostImpl::From(
+ view->GetRenderWidgetHost());
+ host->SendScreenRects();
view->WasResized();
+ }
}
void ContentViewCoreImpl::ShowInterstitialPage(
- JNIEnv* env, jobject obj, jstring jurl, jint delegate_ptr) {
+ JNIEnv* env, jobject obj, jstring jurl, jlong delegate_ptr) {
GURL url(base::android::ConvertJavaStringToUTF8(env, jurl));
InterstitialPageDelegateAndroid* delegate =
reinterpret_cast<InterstitialPageDelegateAndroid*>(delegate_ptr);
@@ -1310,33 +1284,6 @@ jboolean ContentViewCoreImpl::IsShowingInterstitialPage(JNIEnv* env,
return web_contents_->ShowingInterstitialPage();
}
-void ContentViewCoreImpl::AttachExternalVideoSurface(JNIEnv* env,
- jobject obj,
- jint player_id,
- jobject jsurface) {
-#if defined(VIDEO_HOLE)
- RenderViewHostImpl* rvhi = static_cast<RenderViewHostImpl*>(
- web_contents_->GetRenderViewHost());
- if (rvhi && rvhi->media_player_manager()) {
- rvhi->media_player_manager()->AttachExternalVideoSurface(
- static_cast<int>(player_id), jsurface);
- }
-#endif // defined(VIDEO_HOLE)
-}
-
-void ContentViewCoreImpl::DetachExternalVideoSurface(JNIEnv* env,
- jobject obj,
- jint player_id) {
-#if defined(VIDEO_HOLE)
- RenderViewHostImpl* rvhi = static_cast<RenderViewHostImpl*>(
- web_contents_->GetRenderViewHost());
- if (rvhi && rvhi->media_player_manager()) {
- rvhi->media_player_manager()->DetachExternalVideoSurface(
- static_cast<int>(player_id));
- }
-#endif // defined(VIDEO_HOLE)
-}
-
jboolean ContentViewCoreImpl::IsRenderWidgetHostViewReady(JNIEnv* env,
jobject obj) {
RenderWidgetHostViewAndroid* view = GetRenderWidgetHostViewAndroid();
@@ -1376,6 +1323,13 @@ void ContentViewCoreImpl::ScrollFocusedEditableNodeIntoView(JNIEnv* env,
host->GetRoutingID(), gfx::Rect()));
}
+void ContentViewCoreImpl::SelectWordAroundCaret(JNIEnv* env, jobject obj) {
+ RenderViewHost* host = web_contents_->GetRenderViewHost();
+ if (!host)
+ return;
+ host->SelectWordAroundCaret();
+}
+
namespace {
static void AddNavigationEntryToHistory(JNIEnv* env, jobject obj,
@@ -1449,21 +1403,13 @@ ContentViewCoreImpl::GetOriginalUrlForActiveNavigationEntry(JNIEnv* env,
return ConvertUTF8ToJavaString(env, entry->GetOriginalRequestURL().spec());
}
-int ContentViewCoreImpl::GetNativeImeAdapter(JNIEnv* env, jobject obj) {
+long ContentViewCoreImpl::GetNativeImeAdapter(JNIEnv* env, jobject obj) {
RenderWidgetHostViewAndroid* rwhva = GetRenderWidgetHostViewAndroid();
if (!rwhva)
return 0;
return rwhva->GetNativeImeAdapter();
}
-void ContentViewCoreImpl::UndoScrollFocusedEditableNodeIntoView(
- JNIEnv* env,
- jobject obj) {
- RenderViewHost* host = web_contents_->GetRenderViewHost();
- host->Send(
- new ViewMsg_UndoScrollFocusedEditableNodeIntoView(host->GetRoutingID()));
-}
-
namespace {
void JavaScriptResultCallback(const ScopedJavaGlobalRef<jobject>& callback,
const base::Value* result) {
@@ -1494,8 +1440,8 @@ void ContentViewCoreImpl::EvaluateJavaScript(JNIEnv* env,
if (!callback) {
// No callback requested.
- rvh->ExecuteJavascriptInWebFrame(base::string16(), // frame_xpath
- ConvertJavaStringToUTF16(env, script));
+ web_contents_->GetMainFrame()->ExecuteJavaScript(
+ ConvertJavaStringToUTF16(env, script));
return;
}
@@ -1503,11 +1449,10 @@ void ContentViewCoreImpl::EvaluateJavaScript(JNIEnv* env,
// base::Callback.
ScopedJavaGlobalRef<jobject> j_callback;
j_callback.Reset(env, callback);
- content::RenderViewHost::JavascriptResultCallback c_callback =
+ content::RenderFrameHost::JavaScriptResultCallback c_callback =
base::Bind(&JavaScriptResultCallback, j_callback);
- rvh->ExecuteJavascriptInWebFrameCallbackResult(
- base::string16(), // frame_xpath
+ web_contents_->GetMainFrame()->ExecuteJavaScript(
ConvertJavaStringToUTF16(env, script),
c_callback);
}
@@ -1518,7 +1463,7 @@ bool ContentViewCoreImpl::GetUseDesktopUserAgent(
return entry && entry->GetIsOverridingUserAgent();
}
-void ContentViewCoreImpl::UpdateImeAdapter(int native_ime_adapter,
+void ContentViewCoreImpl::UpdateImeAdapter(long native_ime_adapter,
int text_input_type,
const std::string& text,
int selection_start,
@@ -1526,7 +1471,7 @@ void ContentViewCoreImpl::UpdateImeAdapter(int native_ime_adapter,
int composition_start,
int composition_end,
bool show_ime_if_needed,
- bool require_ack) {
+ bool is_non_ime_change) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
if (obj.is_null())
@@ -1538,7 +1483,7 @@ void ContentViewCoreImpl::UpdateImeAdapter(int native_ime_adapter,
jstring_text.obj(),
selection_start, selection_end,
composition_start, composition_end,
- show_ime_if_needed, require_ack);
+ show_ime_if_needed, is_non_ime_change);
}
void ContentViewCoreImpl::ClearSslPreferences(JNIEnv* env, jobject obj) {
@@ -1575,6 +1520,11 @@ void ContentViewCoreImpl::SetUseDesktopUserAgent(
void ContentViewCoreImpl::SetAccessibilityEnabled(JNIEnv* env, jobject obj,
bool enabled) {
+ SetAccessibilityEnabledInternal(enabled);
+}
+
+void ContentViewCoreImpl::SetAccessibilityEnabledInternal(bool enabled) {
+ accessibility_enabled_ = enabled;
RenderWidgetHostViewAndroid* host_view = GetRenderWidgetHostViewAndroid();
if (!host_view)
return;
@@ -1588,47 +1538,18 @@ void ContentViewCoreImpl::SetAccessibilityEnabled(JNIEnv* env, jobject obj,
accessibility_state->OnScreenReaderDetected();
// If it was actually enabled globally, enable it for this RenderWidget now.
if (accessibility_state->IsAccessibleBrowser() && host_impl)
- host_impl->SetAccessibilityMode(AccessibilityModeComplete);
+ host_impl->AddAccessibilityMode(AccessibilityModeComplete);
} else {
- accessibility_state->DisableAccessibility();
+ accessibility_state->ResetAccessibilityMode();
if (host_impl)
- host_impl->SetAccessibilityMode(AccessibilityModeOff);
- }
-}
-
-void ContentViewCoreImpl::SendSingleTapUma(JNIEnv* env,
- jobject obj,
- jint type,
- jint count) {
- UMA_HISTOGRAM_ENUMERATION("Event.SingleTapType", type, count);
-}
-
-void ContentViewCoreImpl::SendActionAfterDoubleTapUma(JNIEnv* env,
- jobject obj,
- jint type,
- jboolean has_delay,
- jint count) {
- // This UMA stat tracks a user's action after a double tap within
- // k seconds (where k == 5 currently). This UMA will tell us if
- // removing the tap gesture delay will lead to significantly more
- // accidental navigations after a double tap.
- if (has_delay) {
- UMA_HISTOGRAM_ENUMERATION("Event.ActionAfterDoubleTapWithDelay", type,
- count);
- } else {
- UMA_HISTOGRAM_ENUMERATION("Event.ActionAfterDoubleTapNoDelay", type,
- count);
+ host_impl->ResetAccessibilityMode();
}
}
void ContentViewCoreImpl::SendOrientationChangeEventInternal() {
RenderWidgetHostViewAndroid* rwhv = GetRenderWidgetHostViewAndroid();
if (rwhv)
- rwhv->UpdateScreenInfo(rwhv->GetNativeView());
-
- RenderViewHostImpl* rvhi = static_cast<RenderViewHostImpl*>(
- web_contents_->GetRenderViewHost());
- rvhi->SendOrientationChangeEvent(device_orientation_);
+ rwhv->UpdateScreenInfo(GetViewAndroid());
}
void ContentViewCoreImpl::ExtractSmartClipData(JNIEnv* env,
@@ -1638,17 +1559,46 @@ void ContentViewCoreImpl::ExtractSmartClipData(JNIEnv* env,
jint width,
jint height) {
gfx::Rect rect(
- static_cast<int>(x / GetDpiScale()),
- static_cast<int>(y / GetDpiScale()),
- static_cast<int>((width > 0 && width < GetDpiScale()) ?
- 1 : (int)(width / GetDpiScale())),
- static_cast<int>((height > 0 && height < GetDpiScale()) ?
- 1 : (int)(height / GetDpiScale())));
+ static_cast<int>(x / dpi_scale()),
+ static_cast<int>(y / dpi_scale()),
+ static_cast<int>((width > 0 && width < dpi_scale()) ?
+ 1 : (int)(width / dpi_scale())),
+ static_cast<int>((height > 0 && height < dpi_scale()) ?
+ 1 : (int)(height / dpi_scale())));
GetWebContents()->Send(new ViewMsg_ExtractSmartClipData(
GetWebContents()->GetRoutingID(), rect));
}
-void ContentViewCoreImpl::OnSmartClipDataExtracted(const string16& result) {
+jint ContentViewCoreImpl::GetCurrentRenderProcessId(JNIEnv* env, jobject obj) {
+ return GetRenderProcessIdFromRenderViewHost(
+ web_contents_->GetRenderViewHost());
+}
+
+void ContentViewCoreImpl::SetBackgroundOpaque(JNIEnv* env, jobject jobj,
+ jboolean opaque) {
+ if (GetRenderWidgetHostViewAndroid())
+ GetRenderWidgetHostViewAndroid()->SetBackgroundOpaque(opaque);
+}
+
+void ContentViewCoreImpl::RequestTextSurroundingSelection(
+ int max_length,
+ const base::Callback<
+ void(const base::string16& content, int start_offset, int end_offset)>&
+ callback) {
+ DCHECK(!callback.is_null());
+ RenderFrameHost* focused_frame = web_contents_->GetFocusedFrame();
+ if (!focused_frame)
+ return;
+ if (GetRenderWidgetHostViewAndroid()) {
+ GetRenderWidgetHostViewAndroid()->SetTextSurroundingSelectionCallback(
+ callback);
+ focused_frame->Send(new FrameMsg_TextSurroundingSelectionRequest(
+ focused_frame->GetRoutingID(), max_length));
+ }
+}
+
+void ContentViewCoreImpl::OnSmartClipDataExtracted(
+ const base::string16& result) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
if (obj.is_null())
@@ -1658,24 +1608,26 @@ void ContentViewCoreImpl::OnSmartClipDataExtracted(const string16& result) {
env, obj.obj(), jresult.obj());
}
-void ContentViewCoreImpl::WebContentsDestroyed(WebContents* web_contents) {
- WebContentsViewAndroid* wcva =
- static_cast<WebContentsViewAndroid*>(web_contents->GetView());
+void ContentViewCoreImpl::WebContentsDestroyed() {
+ WebContentsViewAndroid* wcva = static_cast<WebContentsViewAndroid*>(
+ static_cast<WebContentsImpl*>(web_contents())->GetView());
DCHECK(wcva);
wcva->SetContentViewCore(NULL);
}
// This is called for each ContentView.
-jlong Init(JNIEnv* env, jobject obj,
- jboolean hardware_accelerated,
+jlong Init(JNIEnv* env,
+ jobject obj,
jlong native_web_contents,
jlong view_android,
- jlong window_android) {
+ jlong window_android,
+ jobject retained_objects_set) {
ContentViewCoreImpl* view = new ContentViewCoreImpl(
- env, obj, hardware_accelerated,
+ env, obj,
reinterpret_cast<WebContents*>(native_web_contents),
reinterpret_cast<ui::ViewAndroid*>(view_android),
- reinterpret_cast<ui::WindowAndroid*>(window_android));
+ reinterpret_cast<ui::WindowAndroid*>(window_android),
+ retained_objects_set);
return reinterpret_cast<intptr_t>(view);
}
diff --git a/chromium/content/browser/android/content_view_core_impl.h b/chromium/content/browser/android/content_view_core_impl.h
index 5c61ae60b6d..3bb12913975 100644
--- a/chromium/content/browser/android/content_view_core_impl.h
+++ b/chromium/content/browser/android/content_view_core_impl.h
@@ -8,8 +8,7 @@
#include <vector>
#include "base/android/jni_android.h"
-#include "base/android/jni_helper.h"
-#include "base/basictypes.h"
+#include "base/android/jni_weak_ref.h"
#include "base/compiler_specific.h"
#include "base/i18n/rtl.h"
#include "base/memory/scoped_ptr.h"
@@ -17,8 +16,6 @@
#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"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/web_contents_observer.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
#include "ui/gfx/rect.h"
@@ -31,21 +28,21 @@ class WindowAndroid;
}
namespace content {
+class JavaBridgeDispatcherHostManager;
class RenderWidgetHostViewAndroid;
struct MenuItem;
// TODO(jrg): this is a shell. Upstream the rest.
class ContentViewCoreImpl : public ContentViewCore,
- public NotificationObserver,
public WebContentsObserver {
public:
static ContentViewCoreImpl* FromWebContents(WebContents* web_contents);
ContentViewCoreImpl(JNIEnv* env,
jobject obj,
- bool hardware_accelerated,
WebContents* web_contents,
ui::ViewAndroid* view_android,
- ui::WindowAndroid* window_android);
+ ui::WindowAndroid* window_android,
+ jobject java_bridge_retained_object_set);
// ContentViewCore implementation.
virtual base::android::ScopedJavaLocalRef<jobject> GetJavaObject() OVERRIDE;
@@ -54,16 +51,21 @@ class ContentViewCoreImpl : public ContentViewCore,
virtual ui::WindowAndroid* GetWindowAndroid() const OVERRIDE;
virtual scoped_refptr<cc::Layer> GetLayer() const OVERRIDE;
virtual void LoadUrl(NavigationController::LoadURLParams& params) OVERRIDE;
- virtual jint GetCurrentRenderProcessId(JNIEnv* env, jobject obj) OVERRIDE;
virtual void ShowPastePopup(int x, int y) OVERRIDE;
- virtual unsigned int GetScaledContentTexture(
+ virtual void GetScaledContentBitmap(
float scale,
- gfx::Size* out_size) OVERRIDE;
+ jobject bitmap_config,
+ gfx::Rect src_subrect,
+ const base::Callback<void(bool, const SkBitmap&)>& result_callback)
+ OVERRIDE;
virtual float GetDpiScale() const OVERRIDE;
- virtual void RequestContentClipping(const gfx::Rect& clipping,
- const gfx::Size& content_size) OVERRIDE;
virtual void PauseVideo() OVERRIDE;
virtual void PauseOrResumeGeolocation(bool should_pause) OVERRIDE;
+ virtual void RequestTextSurroundingSelection(
+ int max_length,
+ const base::Callback<void(const base::string16& content,
+ int start_offset,
+ int end_offset)>& callback) OVERRIDE;
// --------------------------------------------------------------------------
// Methods called from Java via JNI
@@ -83,22 +85,36 @@ class ContentViewCoreImpl : public ContentViewCore,
jstring url,
jint load_url_type,
jint transition_type,
+ jstring j_referrer_url,
+ jint referrer_policy,
jint ua_override_option,
jstring extra_headers,
jbyteArray post_data,
jstring base_url_for_data_url,
jstring virtual_url_for_data_url,
- jboolean can_load_local_resources);
+ jboolean can_load_local_resources,
+ jboolean is_renderer_initiated);
base::android::ScopedJavaLocalRef<jstring> GetURL(JNIEnv* env, jobject) const;
- base::android::ScopedJavaLocalRef<jstring> GetTitle(
- JNIEnv* env, jobject obj) const;
jboolean IsIncognito(JNIEnv* env, jobject obj);
void SendOrientationChangeEvent(JNIEnv* env, jobject obj, jint orientation);
- jboolean SendTouchEvent(JNIEnv* env,
- jobject obj,
- jlong time_ms,
- jint type,
- jobjectArray pts);
+ jboolean OnTouchEvent(JNIEnv* env,
+ jobject obj,
+ jobject motion_event,
+ jlong time_ms,
+ jint android_action,
+ jint pointer_count,
+ jint history_size,
+ jint action_index,
+ jfloat pos_x_0,
+ jfloat pos_y_0,
+ jfloat pos_x_1,
+ jfloat pos_y_1,
+ jint pointer_id_0,
+ jint pointer_id_1,
+ jfloat touch_major_0,
+ jfloat touch_major_1,
+ jfloat raw_pos_x,
+ jfloat raw_pos_y);
jboolean SendMouseMoveEvent(JNIEnv* env,
jobject obj,
jlong time_ms,
@@ -110,7 +126,8 @@ class ContentViewCoreImpl : public ContentViewCore,
jfloat x,
jfloat y,
jfloat vertical_axis);
- void ScrollBegin(JNIEnv* env, jobject obj, jlong time_ms, jfloat x, jfloat y);
+ void ScrollBegin(JNIEnv* env, jobject obj, jlong time_ms,
+ jfloat x, jfloat y, jfloat hintx, jfloat hinty);
void ScrollEnd(JNIEnv* env, jobject obj, jlong time_ms);
void ScrollBy(JNIEnv* env, jobject obj, jlong time_ms,
jfloat x, jfloat y, jfloat dx, jfloat dy);
@@ -118,24 +135,11 @@ class ContentViewCoreImpl : public ContentViewCore,
jfloat x, jfloat y, jfloat vx, jfloat vy);
void FlingCancel(JNIEnv* env, jobject obj, jlong time_ms);
void SingleTap(JNIEnv* env, jobject obj, jlong time_ms,
- jfloat x, jfloat y,
- jboolean disambiguation_popup_tap);
- void SingleTapUnconfirmed(JNIEnv* env, jobject obj, jlong time_ms,
- jfloat x, jfloat y);
- void ShowPressState(JNIEnv* env, jobject obj, jlong time_ms,
- jfloat x, jfloat y);
- void TapCancel(JNIEnv* env, jobject obj, jlong time_ms,
jfloat x, jfloat y);
- void TapDown(JNIEnv* env, jobject obj, jlong time_ms,
- jfloat x, jfloat y);
void DoubleTap(JNIEnv* env, jobject obj, jlong time_ms,
jfloat x, jfloat y) ;
void LongPress(JNIEnv* env, jobject obj, jlong time_ms,
- jfloat x, jfloat y,
- jboolean disambiguation_popup_tap);
- void LongTap(JNIEnv* env, jobject obj, jlong time_ms,
- jfloat x, jfloat y,
- jboolean disambiguation_popup_tap);
+ jfloat x, jfloat y);
void PinchBegin(JNIEnv* env, jobject obj, jlong time_ms, jfloat x, jfloat y);
void PinchEnd(JNIEnv* env, jobject obj, jlong time_ms);
void PinchBy(JNIEnv* env, jobject obj, jlong time_ms,
@@ -145,23 +149,29 @@ class ContentViewCoreImpl : public ContentViewCore,
jfloat x2, jfloat y2);
void MoveCaret(JNIEnv* env, jobject obj, jfloat x, jfloat y);
+ void ResetGestureDetection(JNIEnv* env, jobject obj);
+ void SetDoubleTapSupportEnabled(JNIEnv* env, jobject obj, jboolean enabled);
+ void SetMultiTouchZoomSupportEnabled(JNIEnv* env,
+ jobject obj,
+ jboolean enabled);
+
void LoadIfNecessary(JNIEnv* env, jobject obj);
void RequestRestoreLoad(JNIEnv* env, jobject obj);
- void StopLoading(JNIEnv* env, jobject obj);
void Reload(JNIEnv* env, jobject obj, jboolean check_for_repost);
void ReloadIgnoringCache(JNIEnv* env, jobject obj, jboolean check_for_repost);
void CancelPendingReload(JNIEnv* env, jobject obj);
void ContinuePendingReload(JNIEnv* env, jobject obj);
+ void AddStyleSheetByURL(JNIEnv* env, jobject obj, jstring url);
void ClearHistory(JNIEnv* env, jobject obj);
void EvaluateJavaScript(JNIEnv* env,
jobject obj,
jstring script,
jobject callback,
jboolean start_renderer);
- int GetNativeImeAdapter(JNIEnv* env, jobject obj);
+ long GetNativeImeAdapter(JNIEnv* env, jobject obj);
void SetFocus(JNIEnv* env, jobject obj, jboolean focused);
void ScrollFocusedEditableNodeIntoView(JNIEnv* env, jobject obj);
- void UndoScrollFocusedEditableNodeIntoView(JNIEnv* env, jobject obj);
+ void SelectWordAroundCaret(JNIEnv* env, jobject obj);
jint GetBackgroundColor(JNIEnv* env, jobject obj);
void SetBackgroundColor(JNIEnv* env, jobject obj, jint color);
@@ -175,12 +185,14 @@ class ContentViewCoreImpl : public ContentViewCore,
bool GetUseDesktopUserAgent(JNIEnv* env, jobject /* obj */);
void Show();
void Hide();
+ void SetAllowJavascriptInterfacesInspection(JNIEnv* env,
+ jobject obj,
+ jboolean allow);
void AddJavascriptInterface(JNIEnv* env,
jobject obj,
jobject object,
jstring name,
- jclass safe_annotation_clazz,
- jobject retained_object_set);
+ jclass safe_annotation_clazz);
void RemoveJavascriptInterface(JNIEnv* env, jobject obj, jstring name);
int GetNavigationHistory(JNIEnv* env, jobject obj, jobject history);
void GetDirectedNavigationHistory(JNIEnv* env,
@@ -190,13 +202,6 @@ class ContentViewCoreImpl : public ContentViewCore,
jint max_entries);
base::android::ScopedJavaLocalRef<jstring>
GetOriginalUrlForActiveNavigationEntry(JNIEnv* env, jobject obj);
- void UpdateVSyncParameters(JNIEnv* env, jobject obj, jlong timebase_micros,
- jlong interval_micros);
- void OnVSync(JNIEnv* env, jobject /* obj */, jlong frame_time_micros);
- jboolean OnAnimate(JNIEnv* env, jobject /* obj */, jlong frame_time_micros);
- jboolean PopulateBitmapFromCompositor(JNIEnv* env,
- jobject obj,
- jobject jbitmap);
void WasResized(JNIEnv* env, jobject obj);
jboolean IsRenderWidgetHostViewReady(JNIEnv* env, jobject obj);
void ExitFullscreen(JNIEnv* env, jobject obj);
@@ -210,21 +215,10 @@ class ContentViewCoreImpl : public ContentViewCore,
void ShowInterstitialPage(JNIEnv* env,
jobject obj,
jstring jurl,
- jint delegate);
+ jlong delegate);
jboolean IsShowingInterstitialPage(JNIEnv* env, jobject obj);
- void AttachExternalVideoSurface(JNIEnv* env,
- jobject obj,
- jint player_id,
- jobject jsurface);
- void DetachExternalVideoSurface(JNIEnv* env, jobject obj, jint player_id);
void SetAccessibilityEnabled(JNIEnv* env, jobject obj, bool enabled);
- void SendActionAfterDoubleTapUma(JNIEnv* env,
- jobject obj,
- jint type,
- jboolean has_delay,
- jint count);
- void SendSingleTapUma(JNIEnv* env, jobject obj, jint type, jint count);
void ExtractSmartClipData(JNIEnv* env,
jobject obj,
@@ -232,21 +226,27 @@ class ContentViewCoreImpl : public ContentViewCore,
jint y,
jint width,
jint height);
+
+ void SetBackgroundOpaque(JNIEnv* env, jobject jobj, jboolean opaque);
+
+ jint GetCurrentRenderProcessId(JNIEnv* env, jobject obj);
+
// --------------------------------------------------------------------------
// Public methods that call to Java via JNI
// --------------------------------------------------------------------------
- void OnSmartClipDataExtracted(const string16& result);
+ void OnSmartClipDataExtracted(const base::string16& result);
// Creates a popup menu with |items|.
// |multiple| defines if it should support multi-select.
// If not |multiple|, |selected_item| sets the initially selected item.
// Otherwise, item's "checked" flag selects it.
- void ShowSelectPopupMenu(const std::vector<MenuItem>& items,
+ void ShowSelectPopupMenu(const gfx::Rect& bounds,
+ const std::vector<MenuItem>& items,
int selected_item,
bool multiple);
-
- void OnTabCrashed();
+ // Hides a visible popup menu.
+ void HideSelectPopupMenu();
// All sizes and offsets are in CSS pixels as cached by the renderer.
void UpdateFrameInfo(const gfx::Vector2dF& scroll_offset,
@@ -258,19 +258,18 @@ class ContentViewCoreImpl : public ContentViewCore,
const gfx::Vector2dF& content_offset,
float overdraw_bottom_height);
- void UpdateImeAdapter(int native_ime_adapter, int text_input_type,
+ void UpdateImeAdapter(long native_ime_adapter, int text_input_type,
const std::string& text,
int selection_start, int selection_end,
int composition_start, int composition_end,
- bool show_ime_if_needed, bool require_ack);
+ bool show_ime_if_needed, bool is_non_ime_change);
void SetTitle(const base::string16& title);
void OnBackgroundColorChanged(SkColor color);
bool HasFocus();
- void ConfirmTouchEvent(InputEventAckState ack_result);
- void UnhandledFlingStartEvent();
- void OnScrollUpdateGestureConsumed();
- void HasTouchEventHandlers(bool need_touch_events);
+ void OnGestureEventAck(const blink::WebGestureEvent& event,
+ InputEventAckState ack_result);
+ bool FilterInputEvent(const blink::WebInputEvent& event);
void OnSelectionChanged(const std::string& text);
void OnSelectionBoundsChanged(
const ViewHostMsg_SelectionBounds_Params& params);
@@ -287,11 +286,6 @@ class ContentViewCoreImpl : public ContentViewCore,
// testing/benchmarking purposes
base::android::ScopedJavaLocalRef<jobject> CreateTouchEventSynthesizer();
- // Notifies the java object about the external surface, requesting for one if
- // necessary.
- void NotifyExternalSurface(
- int player_id, bool is_request, const gfx::RectF& rect);
-
base::android::ScopedJavaLocalRef<jobject> GetContentVideoViewClient();
// Returns the context that the ContentViewCore was created with, it would
@@ -301,6 +295,13 @@ class ContentViewCoreImpl : public ContentViewCore,
// Returns True if the given media should be blocked to load.
bool ShouldBlockMediaRequest(const GURL& url);
+ void DidStopFlinging();
+
+ // Returns the viewport size after accounting for the viewport offset.
+ gfx::Size GetViewSize() const;
+
+ void SetAccessibilityEnabledInternal(bool enabled);
+
// --------------------------------------------------------------------------
// Methods called from native code
// --------------------------------------------------------------------------
@@ -312,9 +313,6 @@ class ContentViewCoreImpl : public ContentViewCore,
void AttachLayer(scoped_refptr<cc::Layer> layer);
void RemoveLayer(scoped_refptr<cc::Layer> layer);
- void AddBeginFrameSubscriber();
- void RemoveBeginFrameSubscriber();
- void SetNeedsAnimate();
private:
class ContentViewUserData;
@@ -322,14 +320,11 @@ class ContentViewCoreImpl : public ContentViewCore,
friend class ContentViewUserData;
virtual ~ContentViewCoreImpl();
- // NotificationObserver implementation.
- virtual void Observe(int type,
- const NotificationSource& source,
- const NotificationDetails& details) OVERRIDE;
-
// WebContentsObserver implementation.
virtual void RenderViewReady() OVERRIDE;
- virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE;
+ virtual void RenderViewHostChanged(RenderViewHost* old_host,
+ RenderViewHost* new_host) OVERRIDE;
+ virtual void WebContentsDestroyed() OVERRIDE;
// --------------------------------------------------------------------------
// Other private methods and data
@@ -339,18 +334,15 @@ class ContentViewCoreImpl : public ContentViewCore,
RenderWidgetHostViewAndroid* GetRenderWidgetHostViewAndroid();
- float GetTouchPaddingDip();
-
blink::WebGestureEvent MakeGestureEvent(
blink::WebInputEvent::Type type, int64 time_ms, float x, float y) const;
- void SendBeginFrame(base::TimeTicks frame_time);
-
gfx::Size GetViewportSizePix() const;
gfx::Size GetViewportSizeOffsetPix() const;
void DeleteScaledSnapshotTexture();
+ bool OnMotionEvent(const ui::MotionEvent& event);
void SendGestureEvent(const blink::WebGestureEvent& event);
// Update focus state of the RenderWidgetHostView.
@@ -359,11 +351,11 @@ class ContentViewCoreImpl : public ContentViewCore,
// Send device_orientation_ to renderer.
void SendOrientationChangeEventInternal();
+ float dpi_scale() const { return dpi_scale_; }
+
// A weak reference to the Java ContentViewCore object.
JavaObjectWeakGlobalRef java_ref_;
- NotificationRegistrar notification_registrar_;
-
// Reference to the current WebContents used to determine how and what to
// display in the ContentViewCore.
WebContentsImpl* web_contents_;
@@ -374,10 +366,6 @@ class ContentViewCoreImpl : public ContentViewCore,
// Device scale factor.
float dpi_scale_;
- // Variables used to keep track of frame timestamps and deadlines.
- base::TimeDelta vsync_interval_;
- base::TimeDelta expected_browser_composite_time_;
-
// The Android view that can be used to add and remove decoration layers
// like AutofillPopup.
ui::ViewAndroid* view_android_;
@@ -389,7 +377,11 @@ class ContentViewCoreImpl : public ContentViewCore,
// will be sent to Renderer once it is ready.
int device_orientation_;
- bool geolocation_needs_pause_;
+ bool accessibility_enabled_;
+
+ // Manages injecting Java objects.
+ scoped_ptr<JavaBridgeDispatcherHostManager>
+ java_bridge_dispatcher_host_manager_;
DISALLOW_COPY_AND_ASSIGN(ContentViewCoreImpl);
};
diff --git a/chromium/content/browser/android/content_view_render_view.cc b/chromium/content/browser/android/content_view_render_view.cc
index 639deae5ca6..06590fa7513 100644
--- a/chromium/content/browser/android/content_view_render_view.cc
+++ b/chromium/content/browser/android/content_view_render_view.cc
@@ -15,6 +15,7 @@
#include "content/browser/android/content_view_core_impl.h"
#include "content/public/browser/android/compositor.h"
#include "content/public/browser/android/content_view_layer_renderer.h"
+#include "content/public/browser/android/layer_tree_build_helper.h"
#include "jni/ContentViewRenderView_jni.h"
#include "ui/gfx/android/java_bitmap.h"
#include "ui/gfx/size.h"
@@ -26,6 +27,24 @@ using base::android::ScopedJavaLocalRef;
namespace content {
+namespace {
+
+class LayerTreeBuildHelperImpl : public LayerTreeBuildHelper {
+ public:
+ LayerTreeBuildHelperImpl() {}
+ virtual ~LayerTreeBuildHelperImpl() {}
+
+ virtual scoped_refptr<cc::Layer> GetLayerTree(
+ scoped_refptr<cc::Layer> content_root_layer) OVERRIDE {
+ return content_root_layer;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LayerTreeBuildHelperImpl);
+};
+
+} // anonymous namespace
+
// static
bool ContentViewRenderView::RegisterContentViewRenderView(JNIEnv* env) {
return RegisterNativesImpl(env);
@@ -34,16 +53,28 @@ bool ContentViewRenderView::RegisterContentViewRenderView(JNIEnv* env) {
ContentViewRenderView::ContentViewRenderView(JNIEnv* env,
jobject obj,
gfx::NativeWindow root_window)
- : buffers_swapped_during_composite_(false),
- root_window_(root_window) {
+ : layer_tree_build_helper_(new LayerTreeBuildHelperImpl()),
+ root_window_(root_window),
+ current_surface_format_(0) {
java_obj_.Reset(env, obj);
}
ContentViewRenderView::~ContentViewRenderView() {
}
+void ContentViewRenderView::SetLayerTreeBuildHelper(JNIEnv* env,
+ jobject obj,
+ jlong native_build_helper) {
+ CHECK(native_build_helper);
+
+ LayerTreeBuildHelper* build_helper =
+ reinterpret_cast<LayerTreeBuildHelper*>(native_build_helper);
+ layer_tree_build_helper_.reset(build_helper);
+}
// static
-static jlong Init(JNIEnv* env, jobject obj, jlong native_root_window) {
+static jlong Init(JNIEnv* env,
+ jobject obj,
+ jlong native_root_window) {
gfx::NativeWindow root_window =
reinterpret_cast<gfx::NativeWindow>(native_root_window);
ContentViewRenderView* content_view_render_view =
@@ -55,60 +86,48 @@ void ContentViewRenderView::Destroy(JNIEnv* env, jobject obj) {
delete this;
}
-void ContentViewRenderView::SetCurrentContentView(
- JNIEnv* env, jobject obj, int native_content_view) {
+void ContentViewRenderView::SetCurrentContentViewCore(
+ JNIEnv* env, jobject obj, jlong native_content_view_core) {
InitCompositor();
- ContentViewCoreImpl* content_view =
- reinterpret_cast<ContentViewCoreImpl*>(native_content_view);
- if (content_view)
- compositor_->SetRootLayer(content_view->GetLayer());
- else
- compositor_->SetRootLayer(cc::Layer::Create());
+ ContentViewCoreImpl* content_view_core =
+ reinterpret_cast<ContentViewCoreImpl*>(native_content_view_core);
+ compositor_->SetRootLayer(content_view_core
+ ? layer_tree_build_helper_->GetLayerTree(
+ content_view_core->GetLayer())
+ : scoped_refptr<cc::Layer>());
}
void ContentViewRenderView::SurfaceCreated(
- JNIEnv* env, jobject obj, jobject jsurface) {
+ JNIEnv* env, jobject obj) {
+ current_surface_format_ = 0;
InitCompositor();
- compositor_->SetSurface(jsurface);
}
void ContentViewRenderView::SurfaceDestroyed(JNIEnv* env, jobject obj) {
compositor_->SetSurface(NULL);
+ current_surface_format_ = 0;
}
-void ContentViewRenderView::SurfaceSetSize(
- JNIEnv* env, jobject obj, jint width, jint height) {
+void ContentViewRenderView::SurfaceChanged(JNIEnv* env, jobject obj,
+ jint format, jint width, jint height, jobject surface) {
+ if (current_surface_format_ != format) {
+ current_surface_format_ = format;
+ compositor_->SetSurface(surface);
+ }
compositor_->SetWindowBounds(gfx::Size(width, height));
}
-jboolean ContentViewRenderView::Composite(JNIEnv* env, jobject obj) {
- if (!compositor_)
- return false;
-
- buffers_swapped_during_composite_ = false;
- compositor_->Composite();
- return buffers_swapped_during_composite_;
-}
-
-jboolean ContentViewRenderView::CompositeToBitmap(JNIEnv* env, jobject obj,
- jobject java_bitmap) {
- gfx::JavaBitmap bitmap(java_bitmap);
- if (!compositor_ || bitmap.format() != ANDROID_BITMAP_FORMAT_RGBA_8888)
- return false;
- return compositor_->CompositeAndReadback(bitmap.pixels(),
- gfx::Rect(bitmap.size()));
+void ContentViewRenderView::SetOverlayVideoMode(
+ JNIEnv* env, jobject obj, bool enabled) {
+ compositor_->SetHasTransparentBackground(enabled);
}
-void ContentViewRenderView::ScheduleComposite() {
+void ContentViewRenderView::Layout() {
JNIEnv* env = base::android::AttachCurrentThread();
- Java_ContentViewRenderView_requestRender(env, java_obj_.obj());
+ Java_ContentViewRenderView_onCompositorLayout(env, java_obj_.obj());
}
-void ContentViewRenderView::OnSwapBuffersPosted() {
- buffers_swapped_during_composite_ = true;
-}
-
-void ContentViewRenderView::OnSwapBuffersCompleted() {
+void ContentViewRenderView::OnSwapBuffersCompleted(int pending_swap_buffers) {
JNIEnv* env = base::android::AttachCurrentThread();
Java_ContentViewRenderView_onSwapBuffersCompleted(env, java_obj_.obj());
}
@@ -117,5 +136,4 @@ void ContentViewRenderView::InitCompositor() {
if (!compositor_)
compositor_.reset(Compositor::Create(this, root_window_));
}
-
} // 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 1bd3899ac3c..2f1bcc1e7c7 100644
--- a/chromium/content/browser/android/content_view_render_view.h
+++ b/chromium/content/browser/android/content_view_render_view.h
@@ -5,15 +5,20 @@
#ifndef CONTENT_BROWSER_ANDROID_CONTENT_VIEW_RENDER_VIEW_H_
#define CONTENT_BROWSER_ANDROID_CONTENT_VIEW_RENDER_VIEW_H_
-#include "base/android/jni_helper.h"
+#include "base/android/jni_weak_ref.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "content/public/browser/android/compositor_client.h"
#include "ui/gfx/native_widget_types.h"
+namespace cc {
+class Layer;
+}
+
namespace content {
class Compositor;
+class LayerTreeBuildHelper;
class ContentViewRenderView : public CompositorClient {
public:
@@ -26,30 +31,33 @@ class ContentViewRenderView : public CompositorClient {
// Methods called from Java via JNI -----------------------------------------
void Destroy(JNIEnv* env, jobject obj);
- void SetCurrentContentView(JNIEnv* env, jobject obj, int native_content_view);
- void SurfaceCreated(JNIEnv* env, jobject obj, jobject jsurface);
+ void SetCurrentContentViewCore(JNIEnv* env, jobject obj,
+ jlong native_content_view_core);
+ void SetLayerTreeBuildHelper(
+ JNIEnv* env, jobject obj, jlong native_build_helper);
+ void SurfaceCreated(JNIEnv* env, jobject obj);
void SurfaceDestroyed(JNIEnv* env, jobject obj);
- void SurfaceSetSize(JNIEnv* env, jobject obj, jint width, jint height);
+ void SurfaceChanged(JNIEnv* env, jobject obj,
+ jint format, jint width, jint height, jobject surface);
jboolean Composite(JNIEnv* env, jobject obj);
- jboolean CompositeToBitmap(JNIEnv* env, jobject obj, jobject java_bitmap);
+ void SetOverlayVideoMode(JNIEnv* env, jobject obj, bool enabled);
- // CompositorClient ---------------------------------------------------------
- virtual void ScheduleComposite() OVERRIDE;
- virtual void OnSwapBuffersPosted() OVERRIDE;
- virtual void OnSwapBuffersCompleted() OVERRIDE;
+ // CompositorClient implementation
+ virtual void Layout() OVERRIDE;
+ virtual void OnSwapBuffersCompleted(int pending_swap_buffers) OVERRIDE;
private:
virtual ~ContentViewRenderView();
void InitCompositor();
- bool buffers_swapped_during_composite_;
-
base::android::ScopedJavaGlobalRef<jobject> java_obj_;
+ scoped_ptr<LayerTreeBuildHelper> layer_tree_build_helper_;
scoped_ptr<content::Compositor> compositor_;
gfx::NativeWindow root_window_;
+ int current_surface_format_;
DISALLOW_COPY_AND_ASSIGN(ContentViewRenderView);
};
diff --git a/chromium/content/browser/android/content_view_statics.cc b/chromium/content/browser/android/content_view_statics.cc
index e176564c4a0..00b5600493c 100644
--- a/chromium/content/browser/android/content_view_statics.cc
+++ b/chromium/content/browser/android/content_view_statics.cc
@@ -15,6 +15,7 @@
#include "content/common/android/address_parser.h"
#include "content/common/view_messages.h"
#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_process_host_observer.h"
#include "jni/ContentViewStatics_jni.h"
using base::android::ConvertJavaStringToUTF16;
@@ -33,30 +34,66 @@ namespace {
// suspended as opposed to all the current renderer processes because the
// suspend calls are refcounted within WebKitPlatformSupport and it expects a
// perfectly matched number of resume calls.
-// Note that this vector is only accessed from the UI thread.
-base::LazyInstance<std::vector<int /* process id */> > g_suspended_processes =
- LAZY_INSTANCE_INITIALIZER;
+// Note that this class is only accessed from the UI thread.
+class SuspendedProcessWatcher : public content::RenderProcessHostObserver {
+ public:
-// Suspends timers in all current render processes.
-void SuspendWebKitSharedTimers(std::vector<int>* suspended_processes) {
- for (content::RenderProcessHost::iterator i(
- content::RenderProcessHost::AllHostsIterator());
- !i.IsAtEnd(); i.Advance()) {
- content::RenderProcessHost* host = i.GetCurrentValue();
- suspended_processes->push_back(host->GetID());
- host->Send(new ViewMsg_SetWebKitSharedTimersSuspended(true));
+ // If the process crashes, stop watching the corresponding RenderProcessHost
+ // and ensure it doesn't get over-resumed.
+ virtual void RenderProcessExited(content::RenderProcessHost* host,
+ base::ProcessHandle handle,
+ base::TerminationStatus status,
+ int exit_code) OVERRIDE {
+ StopWatching(host);
+ }
+
+ virtual void RenderProcessHostDestroyed(
+ content::RenderProcessHost* host) OVERRIDE {
+ StopWatching(host);
+ }
+
+ // Suspends timers in all current render processes.
+ void SuspendWebKitSharedTimers() {
+ DCHECK(suspended_processes_.empty());
+
+ for (content::RenderProcessHost::iterator i(
+ content::RenderProcessHost::AllHostsIterator());
+ !i.IsAtEnd(); i.Advance()) {
+ content::RenderProcessHost* host = i.GetCurrentValue();
+ host->AddObserver(this);
+ host->Send(new ViewMsg_SetWebKitSharedTimersSuspended(true));
+ suspended_processes_.push_back(host->GetID());
+ }
}
-}
-// Resumes timers in processes that were previously stopped.
-void ResumeWebkitSharedTimers(const std::vector<int>& suspended_processes) {
- for (std::vector<int>::const_iterator it = suspended_processes.begin();
- it != suspended_processes.end(); ++it) {
- content::RenderProcessHost* host = content::RenderProcessHost::FromID(*it);
- if (host) // The process might have been killed since it was suspended.
+ // Resumes timers in processes that were previously stopped.
+ void ResumeWebkitSharedTimers() {
+ for (std::vector<int>::const_iterator it = suspended_processes_.begin();
+ it != suspended_processes_.end(); ++it) {
+ content::RenderProcessHost* host =
+ content::RenderProcessHost::FromID(*it);
+ DCHECK(host);
+ host->RemoveObserver(this);
host->Send(new ViewMsg_SetWebKitSharedTimersSuspended(false));
+ }
+ suspended_processes_.clear();
}
-}
+
+ private:
+ void StopWatching(content::RenderProcessHost* host) {
+ std::vector<int>::iterator pos = std::find(suspended_processes_.begin(),
+ suspended_processes_.end(),
+ host->GetID());
+ DCHECK_NE(pos, suspended_processes_.end());
+ host->RemoveObserver(this);
+ suspended_processes_.erase(pos);
+ }
+
+ std::vector<int /* RenderProcessHost id */> suspended_processes_;
+};
+
+base::LazyInstance<SuspendedProcessWatcher> g_suspended_processes_watcher =
+ LAZY_INSTANCE_INITIALIZER;
} // namespace
@@ -72,20 +109,17 @@ static jstring FindAddress(JNIEnv* env, jclass clazz, jstring addr) {
static void SetWebKitSharedTimersSuspended(JNIEnv* env,
jclass obj,
jboolean suspend) {
- std::vector<int>* suspended_processes = g_suspended_processes.Pointer();
if (suspend) {
- DCHECK(suspended_processes->empty());
- SuspendWebKitSharedTimers(suspended_processes);
+ g_suspended_processes_watcher.Pointer()->SuspendWebKitSharedTimers();
} else {
- ResumeWebkitSharedTimers(*suspended_processes);
- suspended_processes->clear();
+ g_suspended_processes_watcher.Pointer()->ResumeWebkitSharedTimers();
}
}
namespace content {
bool RegisterWebViewStatics(JNIEnv* env) {
- return RegisterNativesImpl(env) >= 0;
+ return RegisterNativesImpl(env);
}
} // namespace content
diff --git a/chromium/content/browser/android/date_time_chooser_android.cc b/chromium/content/browser/android/date_time_chooser_android.cc
index f6b50c9da4b..58133ba22d9 100644
--- a/chromium/content/browser/android/date_time_chooser_android.cc
+++ b/chromium/content/browser/android/date_time_chooser_android.cc
@@ -23,8 +23,8 @@ using base::android::ConvertUTF16ToJavaString;
namespace {
-string16 SanitizeSuggestionString(const string16& string) {
- string16 trimmed = string.substr(0, 255);
+base::string16 SanitizeSuggestionString(const base::string16& string) {
+ base::string16 trimmed = string.substr(0, 255);
icu::UnicodeString sanitized;
base::i18n::UTF16CharIterator sanitized_iterator(&trimmed);
while (!sanitized_iterator.end()) {
@@ -33,8 +33,8 @@ string16 SanitizeSuggestionString(const string16& string) {
sanitized.append(c);
sanitized_iterator.Advance();
}
- return string16(sanitized.getBuffer(),
- static_cast<size_t>(sanitized.length()));
+ return base::string16(sanitized.getBuffer(),
+ static_cast<size_t>(sanitized.length()));
}
} // namespace
diff --git a/chromium/content/browser/android/date_time_chooser_android.h b/chromium/content/browser/android/date_time_chooser_android.h
index c02ecf56fe2..b47ed5ec7c5 100644
--- a/chromium/content/browser/android/date_time_chooser_android.h
+++ b/chromium/content/browser/android/date_time_chooser_android.h
@@ -8,7 +8,7 @@
#include <string>
#include <vector>
-#include "base/android/jni_helper.h"
+#include "base/android/jni_weak_ref.h"
#include "base/memory/scoped_ptr.h"
#include "ui/base/ime/text_input_type.h"
diff --git a/chromium/content/browser/android/download_controller_android_impl.cc b/chromium/content/browser/android/download_controller_android_impl.cc
index f3a274a3ef4..2d042353036 100644
--- a/chromium/content/browser/android/download_controller_android_impl.cc
+++ b/chromium/content/browser/android/download_controller_android_impl.cc
@@ -22,11 +22,11 @@
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/download_url_parameters.h"
#include "content/public/browser/global_request_id.h"
-#include "content/public/browser/web_contents_view.h"
#include "content/public/common/referrer.h"
#include "jni/DownloadController_jni.h"
#include "net/cookies/cookie_options.h"
#include "net/cookies/cookie_store.h"
+#include "net/http/http_content_disposition.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_response_headers.h"
#include "net/url_request/url_request.h"
@@ -221,10 +221,16 @@ void DownloadControllerAndroidImpl::StartAndroidDownload(
ScopedJavaLocalRef<jstring> 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, "");
+ ScopedJavaLocalRef<jstring> jfilename =
+ ConvertUTF8ToJavaString(env, header.filename());
+
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.total_bytes);
+ jcookie.obj(), jreferer.obj(), jfilename.obj(), info.total_bytes);
}
void DownloadControllerAndroidImpl::OnDownloadStarted(
diff --git a/chromium/content/browser/android/download_controller_android_impl.h b/chromium/content/browser/android/download_controller_android_impl.h
index db9b9cf99bb..26bc2a0c4f9 100644
--- a/chromium/content/browser/android/download_controller_android_impl.h
+++ b/chromium/content/browser/android/download_controller_android_impl.h
@@ -21,7 +21,7 @@
#include <string>
-#include "base/android/jni_helper.h"
+#include "base/android/jni_weak_ref.h"
#include "base/android/scoped_java_ref.h"
#include "base/callback.h"
#include "base/memory/singleton.h"
diff --git a/chromium/content/browser/android/edge_effect.cc b/chromium/content/browser/android/edge_effect.cc
index 41fa1a171e1..94e5b512e20 100644
--- a/chromium/content/browser/android/edge_effect.cc
+++ b/chromium/content/browser/android/edge_effect.cc
@@ -5,7 +5,6 @@
#include "content/browser/android/edge_effect.h"
#include "cc/layers/layer.h"
-#include "ui/gfx/screen.h"
namespace content {
@@ -42,6 +41,8 @@ const float kMaxVelocity = 10000.f;
const float kEpsilon = 0.001f;
+const float kGlowHeightToWidthRatio = 0.25f;
+
// How much dragging should effect the height of the edge image.
// Number determined by user testing.
const int kPullDistanceEdgeFactor = 7;
@@ -76,21 +77,44 @@ T Damp(T input, T factor) {
}
gfx::Transform ComputeTransform(EdgeEffect::Edge edge,
- gfx::SizeF size, int height) {
+ const gfx::SizeF& window_size,
+ int offset,
+ int height) {
+ // Edge effects that require rotation are translated to the center about which
+ // the layer should be rotated to align with the corresponding edge.
switch (edge) {
- default:
case EdgeEffect::EDGE_TOP:
- return gfx::Transform(1, 0, 0, 1, 0, 0);
+ return gfx::Transform(1, 0, 0, 1, 0, offset);
case EdgeEffect::EDGE_LEFT:
return gfx::Transform(0, 1, -1, 0,
- (-size.width() + height) / 2 ,
- (size.width() - height) / 2);
+ (-window_size.height() + height) / 2.f + offset,
+ (window_size.height() - height) / 2.f);
case EdgeEffect::EDGE_BOTTOM:
- return gfx::Transform(-1, 0, 0, -1, 0, size.height() - height);
+ return gfx::Transform(-1, 0, 0, -1,
+ 0, window_size.height() - height + offset);
case EdgeEffect::EDGE_RIGHT:
return gfx::Transform(0, -1, 1, 0,
- (-size.width() - height) / 2 + size.height(),
- (size.width() - height) / 2);
+ (-window_size.height() - height) / 2.f + window_size.width() + offset,
+ (window_size.height() - height) / 2.f);
+ default:
+ NOTREACHED() << "Invalid edge: " << edge;
+ return gfx::Transform();
+ };
+}
+
+gfx::Size ComputeBounds(EdgeEffect::Edge edge,
+ const gfx::SizeF& window_size,
+ int height) {
+ switch (edge) {
+ case EdgeEffect::EDGE_TOP:
+ case EdgeEffect::EDGE_BOTTOM:
+ return gfx::Size(window_size.width(), height);
+ case EdgeEffect::EDGE_LEFT:
+ case EdgeEffect::EDGE_RIGHT:
+ return gfx::Size(window_size.height(), height);
+ default:
+ NOTREACHED() << "Invalid edge: " << edge;
+ return gfx::Size();
};
}
@@ -103,13 +127,17 @@ void DisableLayer(cc::Layer* layer) {
void UpdateLayer(cc::Layer* layer,
EdgeEffect::Edge edge,
- gfx::SizeF size,
+ const gfx::SizeF& window_size,
+ int offset,
int height,
float opacity) {
DCHECK(layer);
layer->SetIsDrawable(true);
- layer->SetTransform(ComputeTransform(edge, size, height));
- layer->SetBounds(gfx::Size(size.width(), height));
+ gfx::Size bounds = ComputeBounds(edge, window_size, height);
+ layer->SetTransformOrigin(
+ gfx::Point3F(bounds.width() * 0.5f, bounds.height() * 0.5f, 0));
+ layer->SetTransform(ComputeTransform(edge, window_size, offset, height));
+ layer->SetBounds(bounds);
layer->SetOpacity(Clamp(opacity, 0.f, 1.f));
}
@@ -132,14 +160,10 @@ EdgeEffect::EdgeEffect(scoped_refptr<cc::Layer> edge,
, glow_scale_y_start_(0)
, glow_scale_y_finish_(0)
, state_(STATE_IDLE)
- , pull_distance_(0)
- , dpi_scale_(1) {
+ , pull_distance_(0) {
// Prevent the provided layers from drawing until the effect is activated.
DisableLayer(edge_.get());
DisableLayer(glow_.get());
-
- dpi_scale_ =
- gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().device_scale_factor();
}
EdgeEffect::~EdgeEffect() { }
@@ -167,7 +191,6 @@ void EdgeEffect::Pull(base::TimeTicks current_time, float delta_distance) {
start_time_ = current_time;
duration_ = base::TimeDelta::FromMilliseconds(kPullTime);
- delta_distance *= dpi_scale_;
float abs_delta_distance = std::abs(delta_distance);
pull_distance_ += delta_distance;
float distance = std::abs(pull_distance_);
@@ -220,8 +243,7 @@ void EdgeEffect::Release(base::TimeTicks current_time) {
void EdgeEffect::Absorb(base::TimeTicks current_time, float velocity) {
state_ = STATE_ABSORB;
- float scaled_velocity =
- dpi_scale_ * Clamp(std::abs(velocity), kMinVelocity, kMaxVelocity);
+ velocity = Clamp(std::abs(velocity), kMinVelocity, kMaxVelocity);
start_time_ = current_time;
// This should never be less than 1 millisecond.
@@ -238,9 +260,9 @@ void EdgeEffect::Absorb(base::TimeTicks current_time, float velocity) {
// Factor the velocity by 8. Testing on device shows this works best to
// reflect the strength of the user's scrolling.
- edge_alpha_finish_ = Clamp(scaled_velocity * kVelocityEdgeFactor, 0.f, 1.f);
+ edge_alpha_finish_ = Clamp(velocity * kVelocityEdgeFactor, 0.f, 1.f);
// Edge should never get larger than the size of its asset.
- edge_scale_y_finish_ = Clamp(scaled_velocity * kVelocityEdgeFactor,
+ edge_scale_y_finish_ = Clamp(velocity * kVelocityEdgeFactor,
kHeldEdgeScaleY, 1.f);
// Growth for the size of the glow should be quadratic to properly
@@ -248,10 +270,10 @@ void EdgeEffect::Absorb(base::TimeTicks current_time, float velocity) {
// to a user's scrolling speed. The faster the scrolling speed, the more
// intense the effect should be for both the size and the saturation.
glow_scale_y_finish_ = std::min(
- 0.025f + (scaled_velocity * (scaled_velocity / 100) * 0.00015f), 1.75f);
+ 0.025f + (velocity * (velocity / 100) * 0.00015f), 1.75f);
// Alpha should change for the glow as well as size.
glow_alpha_finish_ = Clamp(glow_alpha_start_,
- scaled_velocity * kVelocityGlowFactor * .00001f,
+ velocity * kVelocityGlowFactor * .00001f,
kMaxAlpha);
}
@@ -302,18 +324,16 @@ bool EdgeEffect::Update(base::TimeTicks current_time) {
glow_alpha_finish_ = 0.f;
glow_scale_y_finish_ = 0.f;
break;
- case STATE_PULL_DECAY:
- {
- // When receding, we want edge to decrease more slowly
- // than the glow.
- float factor = glow_scale_y_finish_ != 0 ?
- 1 / (glow_scale_y_finish_ * glow_scale_y_finish_) :
- std::numeric_limits<float>::max();
- edge_scale_y_ = edge_scale_y_start_ +
- (edge_scale_y_finish_ - edge_scale_y_start_) * interp * factor;
- state_ = STATE_RECEDE;
- }
- break;
+ case STATE_PULL_DECAY: {
+ // When receding, we want edge to decrease more slowly
+ // than the glow.
+ const float factor = glow_scale_y_finish_ != 0 ?
+ 1 / (glow_scale_y_finish_ * glow_scale_y_finish_) :
+ std::numeric_limits<float>::max();
+ edge_scale_y_ = edge_scale_y_start_ +
+ (edge_scale_y_finish_ - edge_scale_y_start_) * interp * factor;
+ state_ = STATE_RECEDE;
+ } break;
case STATE_RECEDE:
Finish();
break;
@@ -328,41 +348,33 @@ bool EdgeEffect::Update(base::TimeTicks current_time) {
return !IsFinished();
}
-void EdgeEffect::ApplyToLayers(gfx::SizeF size, Edge edge) {
+void EdgeEffect::ApplyToLayers(gfx::SizeF window_size,
+ Edge edge,
+ float edge_height,
+ float glow_height,
+ float offset) {
if (IsFinished())
return;
- // An empty effect size, while meaningless, is also relatively harmless, and
+ // An empty window size, while meaningless, is also relatively harmless, and
// will simply prevent any drawing of the layers.
- if (size.IsEmpty()) {
+ if (window_size.IsEmpty()) {
DisableLayer(edge_.get());
DisableLayer(glow_.get());
return;
}
- float dummy_scale_x, dummy_scale_y;
-
// Glow
- gfx::Size glow_image_bounds;
- glow_->CalculateContentsScale(1.f, 1.f, 1.f, false,
- &dummy_scale_x, &dummy_scale_y,
- &glow_image_bounds);
- const int glow_height = glow_image_bounds.height();
- const int glow_width = glow_image_bounds.width();
- const int glow_bottom = static_cast<int>(std::min(
- glow_height * glow_scale_y_ * glow_height / glow_width * 0.6f,
- glow_height * kMaxGlowHeight) * dpi_scale_ + 0.5f);
- UpdateLayer(glow_.get(), edge, size, glow_bottom, glow_alpha_);
+ const int scaled_glow_height = static_cast<int>(
+ std::min(glow_height * glow_scale_y_ * kGlowHeightToWidthRatio * 0.6f,
+ glow_height * kMaxGlowHeight) + 0.5f);
+ UpdateLayer(
+ glow_.get(), edge, window_size, offset, scaled_glow_height, glow_alpha_);
// Edge
- gfx::Size edge_image_bounds;
- edge_->CalculateContentsScale(1.f, 1.f, 1.f, false,
- &dummy_scale_x, &dummy_scale_y,
- &edge_image_bounds);
- const int edge_height = edge_image_bounds.height();
- const int edge_bottom = static_cast<int>(
- edge_height * edge_scale_y_ * dpi_scale_);
- UpdateLayer(edge_.get(), edge, size, edge_bottom, edge_alpha_);
+ const int scaled_edge_height = static_cast<int>(edge_height * edge_scale_y_);
+ UpdateLayer(
+ edge_.get(), edge, window_size, offset, scaled_edge_height, edge_alpha_);
}
} // namespace content
diff --git a/chromium/content/browser/android/edge_effect.h b/chromium/content/browser/android/edge_effect.h
index f47a9b1a0e9..b8febba22d2 100644
--- a/chromium/content/browser/android/edge_effect.h
+++ b/chromium/content/browser/android/edge_effect.h
@@ -21,6 +21,7 @@ namespace content {
* resources directly, while this version simply applies the effect to
* existing resources. Conscious tradeoffs were made to align this as closely
* as possible with the original Android java version.
+ * All coordinates and dimensions are in device pixels.
*/
class EdgeEffect {
public:
@@ -43,7 +44,11 @@ public:
void Finish();
bool IsFinished() const;
- void ApplyToLayers(gfx::SizeF size, Edge edge);
+ void ApplyToLayers(gfx::SizeF window_size,
+ Edge edge,
+ float edge_height,
+ float glow_height,
+ float offset);
private:
@@ -79,8 +84,6 @@ private:
float pull_distance_;
- float dpi_scale_;
-
DISALLOW_COPY_AND_ASSIGN(EdgeEffect);
};
diff --git a/chromium/content/browser/android/gesture_event_type.h b/chromium/content/browser/android/gesture_event_type.h
new file mode 100644
index 00000000000..d41311c7598
--- /dev/null
+++ b/chromium/content/browser/android/gesture_event_type.h
@@ -0,0 +1,19 @@
+// 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_ANDROID_GESTURE_EVENT_TYPE_
+#define CONTENT_BROWSER_ANDROID_GESTURE_EVENT_TYPE_
+
+namespace content {
+
+enum GestureEventType {
+#define DEFINE_GESTURE_EVENT_TYPE(name, value) name = value,
+#include "content/browser/android/gesture_event_type_list.h"
+#undef DEFINE_GESTURE_EVENT_TYPE
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_ANDROID_GESTURE_EVENT_TYPE_
+
diff --git a/chromium/content/browser/android/gesture_event_type_list.h b/chromium/content/browser/android/gesture_event_type_list.h
new file mode 100644
index 00000000000..85af616cb47
--- /dev/null
+++ b/chromium/content/browser/android/gesture_event_type_list.h
@@ -0,0 +1,33 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file intentionally does not have header guards because this file
+// is meant to be included inside a macro to generate enum values.
+
+// This file contains a list of GestureEventType's usable by ContentViewCore,
+// providing a direct mapping to and from their corresponding
+// blink::WebGestureEvent types.
+
+#ifndef DEFINE_GESTURE_EVENT_TYPE
+#error "Please define DEFINE_GESTURE_EVENT_TYPE before including this file."
+#endif
+
+DEFINE_GESTURE_EVENT_TYPE(SHOW_PRESS, 0)
+DEFINE_GESTURE_EVENT_TYPE(DOUBLE_TAP, 1)
+DEFINE_GESTURE_EVENT_TYPE(SINGLE_TAP_UP, 2)
+DEFINE_GESTURE_EVENT_TYPE(SINGLE_TAP_CONFIRMED, 3)
+DEFINE_GESTURE_EVENT_TYPE(SINGLE_TAP_UNCONFIRMED, 4)
+DEFINE_GESTURE_EVENT_TYPE(LONG_PRESS, 5)
+DEFINE_GESTURE_EVENT_TYPE(SCROLL_START, 6)
+DEFINE_GESTURE_EVENT_TYPE(SCROLL_BY, 7)
+DEFINE_GESTURE_EVENT_TYPE(SCROLL_END, 8)
+DEFINE_GESTURE_EVENT_TYPE(FLING_START, 9)
+DEFINE_GESTURE_EVENT_TYPE(FLING_CANCEL, 10)
+DEFINE_GESTURE_EVENT_TYPE(FLING_END, 11)
+DEFINE_GESTURE_EVENT_TYPE(PINCH_BEGIN, 12)
+DEFINE_GESTURE_EVENT_TYPE(PINCH_BY, 13)
+DEFINE_GESTURE_EVENT_TYPE(PINCH_END, 14)
+DEFINE_GESTURE_EVENT_TYPE(TAP_CANCEL, 15)
+DEFINE_GESTURE_EVENT_TYPE(LONG_TAP, 16)
+DEFINE_GESTURE_EVENT_TYPE(TAP_DOWN, 17)
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
new file mode 100644
index 00000000000..c19173cd3cc
--- /dev/null
+++ b/chromium/content/browser/android/in_process/synchronous_compositor_factory_impl.cc
@@ -0,0 +1,267 @@
+// 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/android/in_process/synchronous_compositor_factory_impl.h"
+
+#include "content/browser/android/in_process/synchronous_compositor_output_surface.h"
+#include "content/public/browser/browser_thread.h"
+#include "gpu/command_buffer/client/gl_in_process_context.h"
+#include "ui/gl/android/surface_texture.h"
+#include "ui/gl/gl_surface.h"
+#include "ui/gl/gl_surface_stub.h"
+#include "webkit/common/gpu/context_provider_in_process.h"
+#include "webkit/common/gpu/webgraphicscontext3d_in_process_command_buffer_impl.h"
+
+using webkit::gpu::ContextProviderWebContext;
+
+namespace content {
+
+namespace {
+
+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;
+}
+
+using webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl;
+
+scoped_ptr<gpu::GLInProcessContext> CreateOffscreenContext(
+ const blink::WebGraphicsContext3D::Attributes& attributes) {
+ const gfx::GpuPreference gpu_preference = gfx::PreferDiscreteGpu;
+
+ gpu::GLInProcessContextAttribs in_process_attribs;
+ WebGraphicsContext3DInProcessCommandBufferImpl::ConvertAttributes(
+ attributes, &in_process_attribs);
+ in_process_attribs.lose_context_when_out_of_memory = 1;
+
+ scoped_ptr<gpu::GLInProcessContext> context(
+ gpu::GLInProcessContext::Create(NULL /* service */,
+ NULL /* surface */,
+ true /* is_offscreen */,
+ gfx::kNullAcceleratedWidget,
+ gfx::Size(1, 1),
+ NULL /* share_context */,
+ false /* share_resources */,
+ in_process_attribs,
+ gpu_preference));
+ return context.Pass();
+}
+
+scoped_ptr<gpu::GLInProcessContext> CreateContext(
+ scoped_refptr<gpu::InProcessCommandBuffer::Service> service,
+ gpu::GLInProcessContext* share_context) {
+ const gfx::GpuPreference gpu_preference = gfx::PreferDiscreteGpu;
+ gpu::GLInProcessContextAttribs in_process_attribs;
+ WebGraphicsContext3DInProcessCommandBufferImpl::ConvertAttributes(
+ GetDefaultAttribs(), &in_process_attribs);
+ in_process_attribs.lose_context_when_out_of_memory = 1;
+
+ scoped_ptr<gpu::GLInProcessContext> context(
+ gpu::GLInProcessContext::Create(service,
+ NULL /* surface */,
+ false /* is_offscreen */,
+ gfx::kNullAcceleratedWidget,
+ gfx::Size(1, 1),
+ share_context,
+ false /* share_resources */,
+ in_process_attribs,
+ gpu_preference));
+ return context.Pass();
+}
+
+scoped_ptr<WebGraphicsContext3DInProcessCommandBufferImpl> WrapContext(
+ scoped_ptr<gpu::GLInProcessContext> context) {
+ if (!context.get())
+ return scoped_ptr<WebGraphicsContext3DInProcessCommandBufferImpl>();
+
+ return scoped_ptr<WebGraphicsContext3DInProcessCommandBufferImpl>(
+ WebGraphicsContext3DInProcessCommandBufferImpl::WrapContext(
+ context.Pass(), GetDefaultAttribs()));
+}
+
+scoped_ptr<WebGraphicsContext3DInProcessCommandBufferImpl>
+WrapContextWithAttributes(
+ scoped_ptr<gpu::GLInProcessContext> context,
+ const blink::WebGraphicsContext3D::Attributes& attributes) {
+ if (!context.get())
+ return scoped_ptr<WebGraphicsContext3DInProcessCommandBufferImpl>();
+
+ return scoped_ptr<WebGraphicsContext3DInProcessCommandBufferImpl>(
+ WebGraphicsContext3DInProcessCommandBufferImpl::WrapContext(
+ context.Pass(), attributes));
+}
+
+class VideoContextProvider
+ : public StreamTextureFactorySynchronousImpl::ContextProvider {
+ public:
+ VideoContextProvider(
+ scoped_ptr<gpu::GLInProcessContext> gl_in_process_context)
+ : gl_in_process_context_(gl_in_process_context.get()) {
+
+ context_provider_ = webkit::gpu::ContextProviderInProcess::Create(
+ WrapContext(gl_in_process_context.Pass()),
+ "Video-Offscreen-main-thread");
+ context_provider_->BindToCurrentThread();
+ }
+
+ virtual scoped_refptr<gfx::SurfaceTexture> GetSurfaceTexture(
+ uint32 stream_id) OVERRIDE {
+ return gl_in_process_context_->GetSurfaceTexture(stream_id);
+ }
+
+ virtual gpu::gles2::GLES2Interface* ContextGL() OVERRIDE {
+ return context_provider_->ContextGL();
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<VideoContextProvider>;
+ virtual ~VideoContextProvider() {}
+
+ scoped_refptr<cc::ContextProvider> context_provider_;
+ gpu::GLInProcessContext* gl_in_process_context_;
+
+ DISALLOW_COPY_AND_ASSIGN(VideoContextProvider);
+};
+
+} // namespace
+
+using webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl;
+
+SynchronousCompositorFactoryImpl::SynchronousCompositorFactoryImpl()
+ : record_full_layer_(true),
+ num_hardware_compositors_(0) {
+ SynchronousCompositorFactory::SetInstance(this);
+}
+
+SynchronousCompositorFactoryImpl::~SynchronousCompositorFactoryImpl() {}
+
+scoped_refptr<base::MessageLoopProxy>
+SynchronousCompositorFactoryImpl::GetCompositorMessageLoop() {
+ return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
+}
+
+bool
+SynchronousCompositorFactoryImpl::RecordFullLayer() {
+ return record_full_layer_;
+}
+
+scoped_ptr<cc::OutputSurface>
+SynchronousCompositorFactoryImpl::CreateOutputSurface(int routing_id) {
+ scoped_ptr<SynchronousCompositorOutputSurface> output_surface(
+ new SynchronousCompositorOutputSurface(routing_id));
+ return output_surface.PassAs<cc::OutputSurface>();
+}
+
+InputHandlerManagerClient*
+SynchronousCompositorFactoryImpl::GetInputHandlerManagerClient() {
+ return synchronous_input_event_filter();
+}
+
+scoped_refptr<ContextProviderWebContext> SynchronousCompositorFactoryImpl::
+ GetSharedOffscreenContextProviderForMainThread() {
+ bool failed = false;
+ if ((!offscreen_context_for_main_thread_.get() ||
+ offscreen_context_for_main_thread_->DestroyedOnMainThread())) {
+ scoped_ptr<gpu::GLInProcessContext> context =
+ CreateOffscreenContext(GetDefaultAttribs());
+ offscreen_context_for_main_thread_ =
+ webkit::gpu::ContextProviderInProcess::Create(
+ WrapContext(context.Pass()),
+ "Compositor-Offscreen-main-thread");
+ failed = !offscreen_context_for_main_thread_.get() ||
+ !offscreen_context_for_main_thread_->BindToCurrentThread();
+ }
+
+ if (failed) {
+ offscreen_context_for_main_thread_ = NULL;
+ }
+ return offscreen_context_for_main_thread_;
+}
+
+scoped_refptr<cc::ContextProvider> SynchronousCompositorFactoryImpl::
+ CreateOnscreenContextProviderForCompositorThread() {
+ DCHECK(service_);
+
+ if (!share_context_.get())
+ share_context_ = CreateContext(service_, NULL);
+ return webkit::gpu::ContextProviderInProcess::Create(
+ WrapContext(CreateContext(service_, share_context_.get())),
+ "Child-Compositor");
+}
+
+gpu::GLInProcessContext* SynchronousCompositorFactoryImpl::GetShareContext() {
+ DCHECK(share_context_.get());
+ return share_context_.get();
+}
+
+scoped_refptr<StreamTextureFactory>
+SynchronousCompositorFactoryImpl::CreateStreamTextureFactory(int frame_id) {
+ scoped_refptr<StreamTextureFactorySynchronousImpl> factory(
+ StreamTextureFactorySynchronousImpl::Create(
+ base::Bind(
+ &SynchronousCompositorFactoryImpl::TryCreateStreamTextureFactory,
+ base::Unretained(this)),
+ frame_id));
+ return factory;
+}
+
+blink::WebGraphicsContext3D*
+SynchronousCompositorFactoryImpl::CreateOffscreenGraphicsContext3D(
+ const blink::WebGraphicsContext3D::Attributes& attributes) {
+ return WrapContextWithAttributes(CreateOffscreenContext(attributes),
+ attributes).release();
+}
+
+void SynchronousCompositorFactoryImpl::CompositorInitializedHardwareDraw() {
+ base::AutoLock lock(num_hardware_compositor_lock_);
+ num_hardware_compositors_++;
+}
+
+void SynchronousCompositorFactoryImpl::CompositorReleasedHardwareDraw() {
+ base::AutoLock lock(num_hardware_compositor_lock_);
+ DCHECK_GT(num_hardware_compositors_, 0u);
+ num_hardware_compositors_--;
+}
+
+bool SynchronousCompositorFactoryImpl::CanCreateMainThreadContext() {
+ base::AutoLock lock(num_hardware_compositor_lock_);
+ return num_hardware_compositors_ > 0;
+}
+
+scoped_refptr<StreamTextureFactorySynchronousImpl::ContextProvider>
+SynchronousCompositorFactoryImpl::TryCreateStreamTextureFactory() {
+ scoped_refptr<StreamTextureFactorySynchronousImpl::ContextProvider>
+ context_provider;
+ // This check only guarantees the main thread context is created after
+ // a compositor did successfully initialize hardware draw in the past.
+ // In particular this does not guarantee that the main thread context
+ // will fail creation when all compositors release hardware draw.
+ if (CanCreateMainThreadContext() && !video_context_provider_) {
+ DCHECK(service_);
+ DCHECK(share_context_.get());
+
+ video_context_provider_ = new VideoContextProvider(
+ CreateContext(service_, share_context_.get()));
+ }
+ return video_context_provider_;
+}
+
+void SynchronousCompositorFactoryImpl::SetDeferredGpuService(
+ scoped_refptr<gpu::InProcessCommandBuffer::Service> service) {
+ DCHECK(!service_);
+ service_ = service;
+}
+
+void SynchronousCompositorFactoryImpl::SetRecordFullDocument(
+ bool record_full_document) {
+ record_full_layer_ = record_full_document;
+}
+
+} // 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
new file mode 100644
index 00000000000..ddaef409a3d
--- /dev/null
+++ b/chromium/content/browser/android/in_process/synchronous_compositor_factory_impl.h
@@ -0,0 +1,86 @@
+// 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_ANDROID_IN_PROCESS_SYNCHRONOUS_COMPOSITOR_FACTORY_IMPL_H_
+#define CONTENT_BROWSER_ANDROID_IN_PROCESS_SYNCHRONOUS_COMPOSITOR_FACTORY_IMPL_H_
+
+#include "base/synchronization/lock.h"
+#include "content/browser/android/in_process/synchronous_input_event_filter.h"
+#include "content/renderer/android/synchronous_compositor_factory.h"
+#include "content/renderer/media/android/stream_texture_factory_synchronous_impl.h"
+#include "gpu/command_buffer/service/in_process_command_buffer.h"
+#include "webkit/common/gpu/context_provider_web_context.h"
+
+namespace gpu {
+class GLInProcessContext;
+}
+
+namespace webkit {
+namespace gpu {
+class WebGraphicsContext3DInProcessCommandBufferImpl;
+}
+}
+
+namespace content {
+
+class SynchronousCompositorFactoryImpl : public SynchronousCompositorFactory {
+ public:
+ SynchronousCompositorFactoryImpl();
+ virtual ~SynchronousCompositorFactoryImpl();
+
+ // SynchronousCompositorFactory
+ virtual scoped_refptr<base::MessageLoopProxy> GetCompositorMessageLoop()
+ OVERRIDE;
+ virtual bool RecordFullLayer() OVERRIDE;
+ virtual scoped_ptr<cc::OutputSurface> CreateOutputSurface(int routing_id)
+ OVERRIDE;
+ virtual InputHandlerManagerClient* GetInputHandlerManagerClient() OVERRIDE;
+ virtual scoped_refptr<webkit::gpu::ContextProviderWebContext>
+ GetSharedOffscreenContextProviderForMainThread() OVERRIDE;
+ virtual scoped_refptr<StreamTextureFactory> CreateStreamTextureFactory(
+ int view_id) OVERRIDE;
+ virtual blink::WebGraphicsContext3D* CreateOffscreenGraphicsContext3D(
+ const blink::WebGraphicsContext3D::Attributes& attributes) OVERRIDE;
+
+
+ SynchronousInputEventFilter* synchronous_input_event_filter() {
+ return &synchronous_input_event_filter_;
+ }
+
+ void SetDeferredGpuService(
+ scoped_refptr<gpu::InProcessCommandBuffer::Service> service);
+ void SetRecordFullDocument(bool record_full_document);
+ void CompositorInitializedHardwareDraw();
+ void CompositorReleasedHardwareDraw();
+
+ scoped_refptr<cc::ContextProvider>
+ CreateOnscreenContextProviderForCompositorThread();
+ gpu::GLInProcessContext* GetShareContext();
+
+ private:
+ bool CanCreateMainThreadContext();
+ scoped_refptr<StreamTextureFactorySynchronousImpl::ContextProvider>
+ TryCreateStreamTextureFactory();
+
+ SynchronousInputEventFilter synchronous_input_event_filter_;
+
+ scoped_refptr<webkit::gpu::ContextProviderWebContext>
+ offscreen_context_for_main_thread_;
+
+ scoped_refptr<gpu::InProcessCommandBuffer::Service> service_;
+ scoped_ptr<gpu::GLInProcessContext> share_context_;
+ scoped_refptr<StreamTextureFactorySynchronousImpl::ContextProvider>
+ video_context_provider_;
+
+ bool record_full_layer_;
+
+ // |num_hardware_compositor_lock_| is updated on UI thread only but can be
+ // read on renderer main thread.
+ base::Lock num_hardware_compositor_lock_;
+ unsigned int num_hardware_compositors_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_ANDROID_IN_PROCESS_SYNCHRONOUS_COMPOSITOR_FACTORY_IMPL_H_
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 230b1e92dc6..1b5ccb74ab5 100644
--- a/chromium/content/browser/android/in_process/synchronous_compositor_impl.cc
+++ b/chromium/content/browser/android/in_process/synchronous_compositor_impl.cc
@@ -6,30 +6,21 @@
#include "base/lazy_instance.h"
#include "base/message_loop/message_loop.h"
-#include "base/synchronization/lock.h"
#include "cc/input/input_handler.h"
-#include "cc/input/layer_scroll_offset_delegate.h"
+#include "content/browser/android/in_process/synchronous_compositor_factory_impl.h"
#include "content/browser/android/in_process/synchronous_input_event_filter.h"
#include "content/browser/renderer_host/render_widget_host_view_android.h"
+#include "content/common/input/did_overscroll_params.h"
#include "content/public/browser/android/synchronous_compositor_client.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
-#include "content/renderer/android/synchronous_compositor_factory.h"
-#include "content/renderer/media/android/stream_texture_factory_android_synchronous_impl.h"
-#include "gpu/command_buffer/client/gl_in_process_context.h"
-#include "gpu/command_buffer/service/stream_texture_manager_in_process_android.h"
-#include "ui/gl/android/surface_texture.h"
#include "ui/gl/gl_surface.h"
-#include "webkit/common/gpu/context_provider_in_process.h"
-#include "webkit/common/gpu/webgraphicscontext3d_in_process_command_buffer_impl.h"
namespace content {
namespace {
-using webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl;
-
int GetInProcessRendererId() {
content::RenderProcessHost::iterator it =
content::RenderProcessHost::AllHostsIterator();
@@ -45,223 +36,6 @@ int GetInProcessRendererId() {
return id;
}
-class VideoContextProvider
- : public StreamTextureFactorySynchronousImpl::ContextProvider {
- public:
- VideoContextProvider(
- const scoped_refptr<cc::ContextProvider>& context_provider,
- gpu::GLInProcessContext* gl_in_process_context)
- : context_provider_(context_provider),
- gl_in_process_context_(gl_in_process_context) {}
-
- virtual scoped_refptr<gfx::SurfaceTexture> GetSurfaceTexture(
- uint32 stream_id) OVERRIDE {
- return gl_in_process_context_->GetSurfaceTexture(stream_id);
- }
-
- virtual blink::WebGraphicsContext3D* Context3d() OVERRIDE {
- return context_provider_->Context3d();
- }
-
- private:
- friend class base::RefCountedThreadSafe<VideoContextProvider>;
- virtual ~VideoContextProvider() {}
-
- scoped_refptr<cc::ContextProvider> context_provider_;
- gpu::GLInProcessContext* gl_in_process_context_;
-
- DISALLOW_COPY_AND_ASSIGN(VideoContextProvider);
-};
-
-class SynchronousCompositorFactoryImpl : public SynchronousCompositorFactory {
- public:
- SynchronousCompositorFactoryImpl()
- : wrapped_gl_context_for_main_thread_(NULL),
- num_hardware_compositors_(0) {
- SynchronousCompositorFactory::SetInstance(this);
- }
-
- // SynchronousCompositorFactory
- virtual scoped_refptr<base::MessageLoopProxy>
- GetCompositorMessageLoop() OVERRIDE {
- return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
- }
-
- virtual scoped_ptr<cc::OutputSurface> CreateOutputSurface(
- int routing_id) OVERRIDE {
- scoped_ptr<SynchronousCompositorOutputSurface> output_surface(
- new SynchronousCompositorOutputSurface(routing_id));
- return output_surface.PassAs<cc::OutputSurface>();
- }
-
- virtual InputHandlerManagerClient* GetInputHandlerManagerClient() OVERRIDE {
- return synchronous_input_event_filter();
- }
-
- SynchronousInputEventFilter* synchronous_input_event_filter() {
- return &synchronous_input_event_filter_;
- }
-
- scoped_ptr<WebGraphicsContext3DInProcessCommandBufferImpl>
- CreateOffscreenContext() {
- if (!gfx::GLSurface::InitializeOneOff())
- return scoped_ptr<WebGraphicsContext3DInProcessCommandBufferImpl>();
-
- const gfx::GpuPreference gpu_preference = gfx::PreferDiscreteGpu;
-
- blink::WebGraphicsContext3D::Attributes attributes;
- attributes.antialias = false;
- attributes.shareResources = true;
- attributes.noAutomaticFlushes = true;
-
- gpu::GLInProcessContextAttribs in_process_attribs;
- WebGraphicsContext3DInProcessCommandBufferImpl::ConvertAttributes(
- attributes, &in_process_attribs);
- scoped_ptr<gpu::GLInProcessContext> context(
- gpu::GLInProcessContext::CreateContext(true,
- NULL,
- gfx::Size(1, 1),
- attributes.shareResources,
- in_process_attribs,
- gpu_preference));
-
- wrapped_gl_context_for_main_thread_ = context.get();
- if (!context.get())
- return scoped_ptr<WebGraphicsContext3DInProcessCommandBufferImpl>();
-
- return scoped_ptr<WebGraphicsContext3DInProcessCommandBufferImpl>(
- WebGraphicsContext3DInProcessCommandBufferImpl::WrapContext(
- context.Pass(), attributes));
- }
-
- virtual scoped_refptr<cc::ContextProvider>
- GetOffscreenContextProviderForMainThread() OVERRIDE {
- // This check only guarantees the main thread context is created after
- // a compositor did successfully initialize hardware draw in the past.
- // In particular this does not guarantee that the main thread context
- // will fail creation when all compositors release hardware draw.
- bool failed = !CanCreateMainThreadContext();
- if (!failed &&
- (!offscreen_context_for_main_thread_.get() ||
- offscreen_context_for_main_thread_->DestroyedOnMainThread())) {
- offscreen_context_for_main_thread_ =
- webkit::gpu::ContextProviderInProcess::Create(
- CreateOffscreenContext(),
- "Compositor-Offscreen");
- failed = !offscreen_context_for_main_thread_.get() ||
- !offscreen_context_for_main_thread_->BindToCurrentThread();
- }
-
- if (failed) {
- offscreen_context_for_main_thread_ = NULL;
- wrapped_gl_context_for_main_thread_ = NULL;
- }
- return offscreen_context_for_main_thread_;
- }
-
- // This is called on both renderer main thread (offscreen context creation
- // path shared between cross-process and in-process platforms) and renderer
- // compositor impl thread (InitializeHwDraw) in order to support Android
- // WebView synchronously enable and disable hardware mode multiple times in
- // the same task. This is ok because in-process WGC3D creation may happen on
- // any thread and is lightweight.
- virtual scoped_refptr<cc::ContextProvider>
- GetOffscreenContextProviderForCompositorThread() OVERRIDE {
- base::AutoLock lock(offscreen_context_for_compositor_thread_lock_);
- if (!offscreen_context_for_compositor_thread_.get() ||
- offscreen_context_for_compositor_thread_->DestroyedOnMainThread()) {
- offscreen_context_for_compositor_thread_ =
- webkit::gpu::ContextProviderInProcess::CreateOffscreen();
- }
- return offscreen_context_for_compositor_thread_;
- }
-
- virtual scoped_ptr<StreamTextureFactory> CreateStreamTextureFactory(
- int view_id) OVERRIDE {
- scoped_ptr<StreamTextureFactorySynchronousImpl> factory(
- new StreamTextureFactorySynchronousImpl(
- base::Bind(&SynchronousCompositorFactoryImpl::
- TryCreateStreamTextureFactory,
- base::Unretained(this)),
- view_id));
- return factory.PassAs<StreamTextureFactory>();
- }
-
- void CompositorInitializedHardwareDraw(SynchronousCompositorImpl* compositor);
- void CompositorReleasedHardwareDraw(SynchronousCompositorImpl* compositor);
-
- private:
- void ReleaseGlobalHardwareResources();
- bool CanCreateMainThreadContext();
- scoped_refptr<StreamTextureFactorySynchronousImpl::ContextProvider>
- TryCreateStreamTextureFactory();
-
- SynchronousInputEventFilter synchronous_input_event_filter_;
-
- // Only guards construction and destruction of
- // |offscreen_context_for_compositor_thread_|, not usage.
- base::Lock offscreen_context_for_compositor_thread_lock_;
- scoped_refptr<cc::ContextProvider> offscreen_context_for_main_thread_;
- // This is a pointer to the context owned by
- // |offscreen_context_for_main_thread_|.
- gpu::GLInProcessContext* wrapped_gl_context_for_main_thread_;
- scoped_refptr<cc::ContextProvider> offscreen_context_for_compositor_thread_;
-
- // |num_hardware_compositor_lock_| is updated on UI thread only but can be
- // read on renderer main thread.
- base::Lock num_hardware_compositor_lock_;
- unsigned int num_hardware_compositors_;
-};
-
-void SynchronousCompositorFactoryImpl::CompositorInitializedHardwareDraw(
- SynchronousCompositorImpl* compositor) {
- base::AutoLock lock(num_hardware_compositor_lock_);
- num_hardware_compositors_++;
-}
-
-void SynchronousCompositorFactoryImpl::CompositorReleasedHardwareDraw(
- SynchronousCompositorImpl* compositor) {
- bool should_release_resources = false;
- {
- base::AutoLock lock(num_hardware_compositor_lock_);
- DCHECK_GT(num_hardware_compositors_, 0u);
- num_hardware_compositors_--;
- should_release_resources = num_hardware_compositors_ == 0u;
- }
- if (should_release_resources)
- ReleaseGlobalHardwareResources();
-}
-
-void SynchronousCompositorFactoryImpl::ReleaseGlobalHardwareResources() {
- {
- base::AutoLock lock(offscreen_context_for_compositor_thread_lock_);
- offscreen_context_for_compositor_thread_ = NULL;
- }
-
- // TODO(boliu): Properly clean up command buffer server of main thread
- // context here.
-}
-
-bool SynchronousCompositorFactoryImpl::CanCreateMainThreadContext() {
- base::AutoLock lock(num_hardware_compositor_lock_);
- return num_hardware_compositors_ > 0;
-}
-
-scoped_refptr<StreamTextureFactorySynchronousImpl::ContextProvider>
-SynchronousCompositorFactoryImpl::TryCreateStreamTextureFactory() {
- scoped_refptr<StreamTextureFactorySynchronousImpl::ContextProvider>
- context_provider;
- if (CanCreateMainThreadContext() &&
- GetOffscreenContextProviderForMainThread()) {
- DCHECK(offscreen_context_for_main_thread_);
- DCHECK(wrapped_gl_context_for_main_thread_);
- context_provider =
- new VideoContextProvider(offscreen_context_for_main_thread_,
- wrapped_gl_context_for_main_thread_);
- }
- return context_provider;
-}
-
base::LazyInstance<SynchronousCompositorFactoryImpl>::Leaky g_factory =
LAZY_INSTANCE_INITIALIZER;
@@ -292,7 +66,8 @@ SynchronousCompositorImpl::SynchronousCompositorImpl(WebContents* contents)
: compositor_client_(NULL),
output_surface_(NULL),
contents_(contents),
- input_handler_(NULL) {
+ input_handler_(NULL),
+ weak_ptr_factory_(this) {
DCHECK(contents);
}
@@ -308,15 +83,28 @@ void SynchronousCompositorImpl::SetClient(
compositor_client_ = compositor_client;
}
-bool SynchronousCompositorImpl::InitializeHwDraw(
- scoped_refptr<gfx::GLSurface> surface) {
+// static
+void SynchronousCompositor::SetGpuService(
+ scoped_refptr<gpu::InProcessCommandBuffer::Service> service) {
+ g_factory.Get().SetDeferredGpuService(service);
+}
+
+// static
+void SynchronousCompositor::SetRecordFullDocument(bool record_full_document) {
+ g_factory.Get().SetRecordFullDocument(record_full_document);
+}
+
+bool SynchronousCompositorImpl::InitializeHwDraw() {
DCHECK(CalledOnValidThread());
DCHECK(output_surface_);
- bool success = output_surface_->InitializeHwDraw(
- surface,
- g_factory.Get().GetOffscreenContextProviderForCompositorThread());
+
+ scoped_refptr<cc::ContextProvider> onscreen_context =
+ g_factory.Get().CreateOnscreenContextProviderForCompositorThread();
+
+ bool success = output_surface_->InitializeHwDraw(onscreen_context);
+
if (success)
- g_factory.Get().CompositorInitializedHardwareDraw(this);
+ g_factory.Get().CompositorInitializedHardwareDraw();
return success;
}
@@ -324,27 +112,51 @@ void SynchronousCompositorImpl::ReleaseHwDraw() {
DCHECK(CalledOnValidThread());
DCHECK(output_surface_);
output_surface_->ReleaseHwDraw();
- g_factory.Get().CompositorReleasedHardwareDraw(this);
+ g_factory.Get().CompositorReleasedHardwareDraw();
}
-bool SynchronousCompositorImpl::DemandDrawHw(
- gfx::Size surface_size,
- const gfx::Transform& transform,
- gfx::Rect viewport,
- gfx::Rect clip,
- bool stencil_enabled) {
+gpu::GLInProcessContext* SynchronousCompositorImpl::GetShareContext() {
+ DCHECK(CalledOnValidThread());
+ return g_factory.Get().GetShareContext();
+}
+
+scoped_ptr<cc::CompositorFrame> SynchronousCompositorImpl::DemandDrawHw(
+ gfx::Size surface_size,
+ const gfx::Transform& transform,
+ gfx::Rect viewport,
+ gfx::Rect clip) {
DCHECK(CalledOnValidThread());
DCHECK(output_surface_);
- return output_surface_->DemandDrawHw(
- surface_size, transform, viewport, clip, stencil_enabled);
+ scoped_ptr<cc::CompositorFrame> frame =
+ output_surface_->DemandDrawHw(surface_size, transform, viewport, clip);
+ if (frame.get())
+ UpdateFrameMetaData(frame->metadata);
+ return frame.Pass();
+}
+
+void SynchronousCompositorImpl::ReturnResources(
+ const cc::CompositorFrameAck& frame_ack) {
+ DCHECK(CalledOnValidThread());
+ output_surface_->ReturnResources(frame_ack);
}
bool SynchronousCompositorImpl::DemandDrawSw(SkCanvas* canvas) {
DCHECK(CalledOnValidThread());
DCHECK(output_surface_);
- return output_surface_->DemandDrawSw(canvas);
+ scoped_ptr<cc::CompositorFrame> frame = output_surface_->DemandDrawSw(canvas);
+ if (frame.get())
+ UpdateFrameMetaData(frame->metadata);
+ return !!frame.get();
+}
+
+void SynchronousCompositorImpl::UpdateFrameMetaData(
+ const cc::CompositorFrameMetadata& frame_metadata) {
+ RenderWidgetHostViewAndroid* rwhv = static_cast<RenderWidgetHostViewAndroid*>(
+ contents_->GetRenderWidgetHostView());
+ if (rwhv)
+ rwhv->SynchronousFrameMetadata(frame_metadata);
}
void SynchronousCompositorImpl::SetMemoryPolicy(
@@ -352,7 +164,7 @@ void SynchronousCompositorImpl::SetMemoryPolicy(
DCHECK(CalledOnValidThread());
DCHECK(output_surface_);
- return output_surface_->SetMemoryPolicy(policy);
+ output_surface_->SetMemoryPolicy(policy);
}
void SynchronousCompositorImpl::DidChangeRootLayerScrollOffset() {
@@ -396,7 +208,7 @@ void SynchronousCompositorImpl::SetInputHandler(
}
void SynchronousCompositorImpl::DidOverscroll(
- const cc::DidOverscrollParams& params) {
+ const DidOverscrollParams& params) {
if (compositor_client_) {
compositor_client_->DidOverscroll(params.accumulated_overscroll,
params.latest_overscroll_delta,
@@ -404,6 +216,13 @@ void SynchronousCompositorImpl::DidOverscroll(
}
}
+void SynchronousCompositorImpl::DidStopFlinging() {
+ RenderWidgetHostViewAndroid* rwhv = static_cast<RenderWidgetHostViewAndroid*>(
+ contents_->GetRenderWidgetHostView());
+ if (rwhv)
+ rwhv->DidStopFlinging();
+}
+
void SynchronousCompositorImpl::SetContinuousInvalidate(bool enable) {
DCHECK(CalledOnValidThread());
if (compositor_client_)
@@ -417,32 +236,11 @@ InputEventAckState SynchronousCompositorImpl::HandleInputEvent(
contents_->GetRoutingID(), input_event);
}
-void SynchronousCompositorImpl::UpdateFrameMetaData(
- const cc::CompositorFrameMetadata& frame_metadata) {
- RenderWidgetHostViewAndroid* rwhv = static_cast<RenderWidgetHostViewAndroid*>(
- contents_->GetRenderWidgetHostView());
- if (rwhv)
- rwhv->SynchronousFrameMetadata(frame_metadata);
-}
-
void SynchronousCompositorImpl::DidActivatePendingTree() {
if (compositor_client_)
compositor_client_->DidUpdateContent();
}
-void SynchronousCompositorImpl::SetMaxScrollOffset(
- gfx::Vector2dF max_scroll_offset) {
- DCHECK(CalledOnValidThread());
- if (compositor_client_)
- compositor_client_->SetMaxRootLayerScrollOffset(max_scroll_offset);
-}
-
-void SynchronousCompositorImpl::SetTotalScrollOffset(gfx::Vector2dF new_value) {
- DCHECK(CalledOnValidThread());
- if (compositor_client_)
- compositor_client_->SetTotalRootLayerScrollOffset(new_value);
-}
-
gfx::Vector2dF SynchronousCompositorImpl::GetTotalScrollOffset() {
DCHECK(CalledOnValidThread());
if (compositor_client_)
@@ -457,17 +255,23 @@ bool SynchronousCompositorImpl::IsExternalFlingActive() const {
return false;
}
-void SynchronousCompositorImpl::SetTotalPageScaleFactor(
- float page_scale_factor) {
+void SynchronousCompositorImpl::UpdateRootLayerState(
+ const gfx::Vector2dF& total_scroll_offset,
+ const gfx::Vector2dF& max_scroll_offset,
+ const gfx::SizeF& scrollable_size,
+ float page_scale_factor,
+ float min_page_scale_factor,
+ float max_page_scale_factor) {
DCHECK(CalledOnValidThread());
- if (compositor_client_)
- compositor_client_->SetRootLayerPageScaleFactor(page_scale_factor);
-}
-
-void SynchronousCompositorImpl::SetScrollableSize(gfx::SizeF scrollable_size) {
- DCHECK(CalledOnValidThread());
- if (compositor_client_)
- compositor_client_->SetRootLayerScrollableSize(scrollable_size);
+ if (!compositor_client_)
+ return;
+
+ compositor_client_->UpdateRootLayerState(total_scroll_offset,
+ max_scroll_offset,
+ scrollable_size,
+ page_scale_factor,
+ min_page_scale_factor,
+ max_page_scale_factor);
}
// Not using base::NonThreadSafe as we want to enforce a more exacting threading
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 f23d920a555..6ac80cb92e6 100644
--- a/chromium/content/browser/android/in_process/synchronous_compositor_impl.h
+++ b/chromium/content/browser/android/in_process/synchronous_compositor_impl.h
@@ -10,13 +10,12 @@
#include "base/memory/scoped_ptr.h"
#include "cc/input/layer_scroll_offset_delegate.h"
#include "content/browser/android/in_process/synchronous_compositor_output_surface.h"
-#include "content/port/common/input_event_ack_state.h"
+#include "content/common/input/input_event_ack_state.h"
#include "content/public/browser/android/synchronous_compositor.h"
#include "content/public/browser/web_contents_user_data.h"
namespace cc {
class InputHandler;
-struct DidOverscrollParams;
}
namespace blink {
@@ -25,6 +24,7 @@ class WebInputEvent;
namespace content {
class InputHandlerManager;
+struct DidOverscrollParams;
// The purpose of this class is to act as the intermediary between the various
// components that make up the 'synchronous compositor mode' implementation and
@@ -48,16 +48,17 @@ class SynchronousCompositorImpl
// SynchronousCompositor
virtual void SetClient(SynchronousCompositorClient* compositor_client)
OVERRIDE;
- virtual bool InitializeHwDraw(
- scoped_refptr<gfx::GLSurface> surface) OVERRIDE;
+ virtual bool InitializeHwDraw() OVERRIDE;
virtual void ReleaseHwDraw() OVERRIDE;
- virtual bool DemandDrawHw(
+ virtual gpu::GLInProcessContext* GetShareContext() OVERRIDE;
+ virtual scoped_ptr<cc::CompositorFrame> DemandDrawHw(
gfx::Size surface_size,
const gfx::Transform& transform,
gfx::Rect viewport,
- gfx::Rect clip,
- bool stencil_enabled) OVERRIDE;
+ gfx::Rect clip) OVERRIDE;
virtual bool DemandDrawSw(SkCanvas* canvas) OVERRIDE;
+ virtual void ReturnResources(
+ const cc::CompositorFrameAck& frame_ack) OVERRIDE;
virtual void SetMemoryPolicy(
const SynchronousCompositorMemoryPolicy& policy) OVERRIDE;
virtual void DidChangeRootLayerScrollOffset() OVERRIDE;
@@ -68,28 +69,28 @@ class SynchronousCompositorImpl
virtual void DidDestroySynchronousOutputSurface(
SynchronousCompositorOutputSurface* output_surface) OVERRIDE;
virtual void SetContinuousInvalidate(bool enable) OVERRIDE;
- virtual void UpdateFrameMetaData(
- const cc::CompositorFrameMetadata& frame_info) OVERRIDE;
virtual void DidActivatePendingTree() OVERRIDE;
// LayerScrollOffsetDelegate
- virtual void SetMaxScrollOffset(gfx::Vector2dF max_scroll_offset) OVERRIDE;
- virtual void SetTotalScrollOffset(gfx::Vector2dF new_value) OVERRIDE;
virtual gfx::Vector2dF GetTotalScrollOffset() OVERRIDE;
+ virtual void UpdateRootLayerState(const gfx::Vector2dF& total_scroll_offset,
+ const gfx::Vector2dF& max_scroll_offset,
+ const gfx::SizeF& scrollable_size,
+ float page_scale_factor,
+ float min_page_scale_factor,
+ float max_page_scale_factor) OVERRIDE;
virtual bool IsExternalFlingActive() const OVERRIDE;
- virtual void SetTotalPageScaleFactor(float page_scale_factor) OVERRIDE;
- virtual void SetScrollableSize(gfx::SizeF scrollable_size) OVERRIDE;
void SetInputHandler(cc::InputHandler* input_handler);
- void DidOverscroll(const cc::DidOverscrollParams& params);
+ void DidOverscroll(const DidOverscrollParams& params);
+ void DidStopFlinging();
private:
explicit SynchronousCompositorImpl(WebContents* contents);
virtual ~SynchronousCompositorImpl();
friend class WebContentsUserData<SynchronousCompositorImpl>;
- void DidCreateSynchronousOutputSurface(
- SynchronousCompositorOutputSurface* output_surface);
+ void UpdateFrameMetaData(const cc::CompositorFrameMetadata& frame_info);
bool CalledOnValidThread() const;
SynchronousCompositorClient* compositor_client_;
@@ -97,6 +98,8 @@ class SynchronousCompositorImpl
WebContents* contents_;
cc::InputHandler* input_handler_;
+ base::WeakPtrFactory<SynchronousCompositorImpl> weak_ptr_factory_;
+
DISALLOW_COPY_AND_ASSIGN(SynchronousCompositorImpl);
};
diff --git a/chromium/content/browser/android/in_process/synchronous_compositor_output_surface.cc b/chromium/content/browser/android/in_process/synchronous_compositor_output_surface.cc
index 2d37659723c..041de4ddfec 100644
--- a/chromium/content/browser/android/in_process/synchronous_compositor_output_surface.cc
+++ b/chromium/content/browser/android/in_process/synchronous_compositor_output_surface.cc
@@ -12,51 +12,19 @@
#include "cc/output/output_surface_client.h"
#include "cc/output/software_output_device.h"
#include "content/browser/android/in_process/synchronous_compositor_impl.h"
+#include "content/browser/gpu/compositor_util.h"
#include "content/public/browser/browser_thread.h"
-#include "gpu/command_buffer/client/gl_in_process_context.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
#include "gpu/command_buffer/common/gpu_memory_allocation.h"
-#include "third_party/skia/include/core/SkBitmapDevice.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "ui/gfx/rect_conversions.h"
#include "ui/gfx/skia_util.h"
#include "ui/gfx/transform.h"
-#include "ui/gl/gl_surface.h"
-#include "webkit/common/gpu/context_provider_in_process.h"
-#include "webkit/common/gpu/webgraphicscontext3d_in_process_command_buffer_impl.h"
namespace content {
namespace {
-scoped_ptr<webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl>
-CreateWebGraphicsContext3D(scoped_refptr<gfx::GLSurface> surface) {
- using webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl;
- if (!gfx::GLSurface::InitializeOneOff())
- return scoped_ptr<WebGraphicsContext3DInProcessCommandBufferImpl>();
-
- const gfx::GpuPreference gpu_preference = gfx::PreferDiscreteGpu;
-
- blink::WebGraphicsContext3D::Attributes attributes;
- attributes.antialias = false;
- attributes.shareResources = true;
- attributes.noAutomaticFlushes = true;
-
- gpu::GLInProcessContextAttribs in_process_attribs;
- WebGraphicsContext3DInProcessCommandBufferImpl::ConvertAttributes(
- attributes, &in_process_attribs);
- scoped_ptr<gpu::GLInProcessContext> context(
- gpu::GLInProcessContext::CreateWithSurface(surface,
- attributes.shareResources,
- in_process_attribs,
- gpu_preference));
-
- if (!context.get())
- return scoped_ptr<WebGraphicsContext3DInProcessCommandBufferImpl>();
-
- return WebGraphicsContext3DInProcessCommandBufferImpl::WrapContext(
- context.Pass(), attributes).Pass();
-}
-
void DidActivatePendingTree(int routing_id) {
SynchronousCompositorOutputSurfaceDelegate* delegate =
SynchronousCompositorImpl::FromRoutingID(routing_id);
@@ -70,31 +38,29 @@ class SynchronousCompositorOutputSurface::SoftwareDevice
: public cc::SoftwareOutputDevice {
public:
SoftwareDevice(SynchronousCompositorOutputSurface* surface)
- : surface_(surface),
- null_device_(SkBitmap::kARGB_8888_Config, 1, 1),
- null_canvas_(&null_device_) {
+ : surface_(surface) {
}
- virtual void Resize(gfx::Size size) OVERRIDE {
+ virtual void Resize(const gfx::Size& pixel_size,
+ float scale_factor) OVERRIDE {
// Intentional no-op: canvas size is controlled by the embedder.
}
- virtual SkCanvas* BeginPaint(gfx::Rect damage_rect) OVERRIDE {
+ virtual SkCanvas* BeginPaint(const gfx::Rect& damage_rect) OVERRIDE {
if (!surface_->current_sw_canvas_) {
NOTREACHED() << "BeginPaint with no canvas set";
return &null_canvas_;
}
- LOG_IF(WARNING, surface_->did_swap_buffer_)
+ LOG_IF(WARNING, surface_->frame_holder_.get())
<< "Mutliple calls to BeginPaint per frame";
return surface_->current_sw_canvas_;
}
virtual void EndPaint(cc::SoftwareFrameData* frame_data) OVERRIDE {
}
- virtual void CopyToBitmap(gfx::Rect rect, SkBitmap* output) OVERRIDE {
+ virtual void CopyToPixels(const gfx::Rect& rect, void* pixels) OVERRIDE {
NOTIMPLEMENTED();
}
private:
SynchronousCompositorOutputSurface* surface_;
- SkBitmapDevice null_device_;
SkCanvas null_canvas_;
DISALLOW_COPY_AND_ASSIGN(SoftwareDevice);
@@ -105,15 +71,16 @@ SynchronousCompositorOutputSurface::SynchronousCompositorOutputSurface(
: cc::OutputSurface(
scoped_ptr<cc::SoftwareOutputDevice>(new SoftwareDevice(this))),
routing_id_(routing_id),
- needs_begin_impl_frame_(false),
+ needs_begin_frame_(false),
invoking_composite_(false),
- did_swap_buffer_(false),
current_sw_canvas_(NULL),
memory_policy_(0),
output_surface_client_(NULL) {
capabilities_.deferred_gl_initialization = true;
capabilities_.draw_and_swap_full_viewport_every_frame = true;
capabilities_.adjust_deadline_for_parent = false;
+ capabilities_.delegated_rendering = true;
+ capabilities_.max_frames_pending = 1;
// Cannot call out to GetDelegate() here as the output surface is not
// constructed on the correct thread.
@@ -155,33 +122,26 @@ bool SynchronousCompositorOutputSurface::BindToClient(
}
void SynchronousCompositorOutputSurface::Reshape(
- gfx::Size size, float scale_factor) {
+ const gfx::Size& size, float scale_factor) {
// Intentional no-op: surface size is controlled by the embedder.
}
-void SynchronousCompositorOutputSurface::SetNeedsBeginImplFrame(
- bool enable) {
+void SynchronousCompositorOutputSurface::SetNeedsBeginFrame(bool enable) {
DCHECK(CalledOnValidThread());
- cc::OutputSurface::SetNeedsBeginImplFrame(enable);
- needs_begin_impl_frame_ = enable;
+ needs_begin_frame_ = enable;
SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
- if (delegate)
- delegate->SetContinuousInvalidate(needs_begin_impl_frame_);
+ if (delegate && !invoking_composite_)
+ delegate->SetContinuousInvalidate(needs_begin_frame_);
}
void SynchronousCompositorOutputSurface::SwapBuffers(
cc::CompositorFrame* frame) {
DCHECK(CalledOnValidThread());
- if (!ForcedDrawToSoftwareDevice()) {
- DCHECK(context_provider_);
- context_provider_->Context3d()->shallowFlushCHROMIUM();
- }
- SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
- if (delegate)
- delegate->UpdateFrameMetaData(frame->metadata);
- did_swap_buffer_ = true;
- DidSwapBuffers();
+ frame_holder_.reset(new cc::CompositorFrame);
+ frame->AssignTo(frame_holder_.get());
+
+ client_->DidSwapBuffers();
}
namespace {
@@ -192,18 +152,12 @@ void AdjustTransform(gfx::Transform* transform, gfx::Rect viewport) {
} // namespace
bool SynchronousCompositorOutputSurface::InitializeHwDraw(
- scoped_refptr<gfx::GLSurface> surface,
- scoped_refptr<cc::ContextProvider> offscreen_context_provider) {
+ scoped_refptr<cc::ContextProvider> onscreen_context_provider) {
DCHECK(CalledOnValidThread());
DCHECK(HasClient());
DCHECK(!context_provider_);
- DCHECK(surface);
- scoped_refptr<cc::ContextProvider> onscreen_context_provider =
- webkit::gpu::ContextProviderInProcess::Create(
- CreateWebGraphicsContext3D(surface), "SynchronousCompositor");
- return InitializeAndSetContext3d(onscreen_context_provider,
- offscreen_context_provider);
+ return InitializeAndSetContext3d(onscreen_context_provider);
}
void SynchronousCompositorOutputSurface::ReleaseHwDraw() {
@@ -211,24 +165,24 @@ void SynchronousCompositorOutputSurface::ReleaseHwDraw() {
cc::OutputSurface::ReleaseGL();
}
-bool SynchronousCompositorOutputSurface::DemandDrawHw(
+scoped_ptr<cc::CompositorFrame>
+SynchronousCompositorOutputSurface::DemandDrawHw(
gfx::Size surface_size,
const gfx::Transform& transform,
gfx::Rect viewport,
- gfx::Rect clip,
- bool stencil_enabled) {
+ gfx::Rect clip) {
DCHECK(CalledOnValidThread());
DCHECK(HasClient());
DCHECK(context_provider_);
surface_size_ = surface_size;
- SetExternalStencilTest(stencil_enabled);
InvokeComposite(transform, viewport, clip, true);
- return did_swap_buffer_;
+ return frame_holder_.Pass();
}
-bool SynchronousCompositorOutputSurface::DemandDrawSw(SkCanvas* canvas) {
+scoped_ptr<cc::CompositorFrame>
+SynchronousCompositorOutputSurface::DemandDrawSw(SkCanvas* canvas) {
DCHECK(CalledOnValidThread());
DCHECK(canvas);
DCHECK(!current_sw_canvas_);
@@ -243,11 +197,10 @@ bool SynchronousCompositorOutputSurface::DemandDrawSw(SkCanvas* canvas) {
surface_size_ = gfx::Size(canvas->getDeviceSize().width(),
canvas->getDeviceSize().height());
- SetExternalStencilTest(false);
InvokeComposite(transform, clip, clip, false);
- return did_swap_buffer_;
+ return frame_holder_.Pass();
}
void SynchronousCompositorOutputSurface::InvokeComposite(
@@ -256,17 +209,15 @@ void SynchronousCompositorOutputSurface::InvokeComposite(
gfx::Rect clip,
bool valid_for_tile_management) {
DCHECK(!invoking_composite_);
+ DCHECK(!frame_holder_.get());
base::AutoReset<bool> invoking_composite_resetter(&invoking_composite_, true);
- did_swap_buffer_ = false;
gfx::Transform adjusted_transform = transform;
AdjustTransform(&adjusted_transform, viewport);
SetExternalDrawConstraints(
adjusted_transform, viewport, clip, valid_for_tile_management);
SetNeedsRedrawRect(gfx::Rect(viewport.size()));
-
- if (needs_begin_impl_frame_)
- BeginImplFrame(cc::BeginFrameArgs::CreateForSynchronousCompositor());
+ client_->BeginFrame(cc::BeginFrameArgs::CreateForSynchronousCompositor());
// After software draws (which might move the viewport arbitrarily), restore
// the previous hardware viewport to allow CC's tile manager to prioritize
@@ -280,14 +231,17 @@ void SynchronousCompositorOutputSurface::InvokeComposite(
cached_hw_transform_, cached_hw_viewport_, cached_hw_clip_, true);
}
- if (did_swap_buffer_)
- OnSwapBuffersComplete();
+ if (frame_holder_.get())
+ client_->DidSwapBuffersComplete();
+
+ SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
+ if (delegate)
+ delegate->SetContinuousInvalidate(needs_begin_frame_);
}
-void
-SynchronousCompositorOutputSurface::PostCheckForRetroactiveBeginImplFrame() {
- // Synchronous compositor cannot perform retroactive BeginImplFrames, so
- // intentionally no-op here.
+void SynchronousCompositorOutputSurface::ReturnResources(
+ const cc::CompositorFrameAck& frame_ack) {
+ ReclaimResources(&frame_ack);
}
void SynchronousCompositorOutputSurface::SetMemoryPolicy(
diff --git a/chromium/content/browser/android/in_process/synchronous_compositor_output_surface.h b/chromium/content/browser/android/in_process/synchronous_compositor_output_surface.h
index 2879de00c04..3a456a433f0 100644
--- a/chromium/content/browser/android/in_process/synchronous_compositor_output_surface.h
+++ b/chromium/content/browser/android/in_process/synchronous_compositor_output_surface.h
@@ -9,6 +9,7 @@
#include "base/compiler_specific.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
+#include "cc/output/compositor_frame.h"
#include "cc/output/managed_memory_policy.h"
#include "cc/output/output_surface.h"
#include "content/public/browser/android/synchronous_compositor.h"
@@ -32,8 +33,6 @@ class SynchronousCompositorOutputSurfaceDelegate {
virtual void DidDestroySynchronousOutputSurface(
SynchronousCompositorOutputSurface* output_surface) = 0;
virtual void SetContinuousInvalidate(bool enable) = 0;
- virtual void UpdateFrameMetaData(
- const cc::CompositorFrameMetadata& frame_metadata) = 0;
virtual void DidActivatePendingTree() = 0;
protected:
@@ -58,30 +57,26 @@ class SynchronousCompositorOutputSurface
// OutputSurface.
virtual bool ForcedDrawToSoftwareDevice() const OVERRIDE;
virtual bool BindToClient(cc::OutputSurfaceClient* surface_client) OVERRIDE;
- virtual void Reshape(gfx::Size size, float scale_factor) OVERRIDE;
- virtual void SetNeedsBeginImplFrame(bool enable) OVERRIDE;
+ virtual void Reshape(const gfx::Size& size, float scale_factor) OVERRIDE;
+ virtual void SetNeedsBeginFrame(bool enable) OVERRIDE;
virtual void SwapBuffers(cc::CompositorFrame* frame) OVERRIDE;
// Partial SynchronousCompositor API implementation.
bool InitializeHwDraw(
- scoped_refptr<gfx::GLSurface> surface,
- scoped_refptr<cc::ContextProvider> offscreen_context_provider);
+ scoped_refptr<cc::ContextProvider> onscreen_context_provider);
void ReleaseHwDraw();
- bool DemandDrawHw(gfx::Size surface_size,
- const gfx::Transform& transform,
- gfx::Rect viewport,
- gfx::Rect clip,
- bool stencil_enabled);
- bool DemandDrawSw(SkCanvas* canvas);
+ scoped_ptr<cc::CompositorFrame> DemandDrawHw(gfx::Size surface_size,
+ const gfx::Transform& transform,
+ gfx::Rect viewport,
+ gfx::Rect clip);
+ void ReturnResources(const cc::CompositorFrameAck& frame_ack);
+ scoped_ptr<cc::CompositorFrame> DemandDrawSw(SkCanvas* canvas);
void SetMemoryPolicy(const SynchronousCompositorMemoryPolicy& policy);
private:
class SoftwareDevice;
friend class SoftwareDevice;
- // Private OutputSurface overrides.
- virtual void PostCheckForRetroactiveBeginImplFrame() OVERRIDE;
-
void InvokeComposite(const gfx::Transform& transform,
gfx::Rect viewport,
gfx::Rect clip,
@@ -90,9 +85,8 @@ class SynchronousCompositorOutputSurface
SynchronousCompositorOutputSurfaceDelegate* GetDelegate();
int routing_id_;
- bool needs_begin_impl_frame_;
+ bool needs_begin_frame_;
bool invoking_composite_;
- bool did_swap_buffer_;
gfx::Transform cached_hw_transform_;
gfx::Rect cached_hw_viewport_;
@@ -104,6 +98,7 @@ class SynchronousCompositorOutputSurface
cc::ManagedMemoryPolicy memory_policy_;
cc::OutputSurfaceClient* output_surface_client_;
+ scoped_ptr<cc::CompositorFrame> frame_holder_;
DISALLOW_COPY_AND_ASSIGN(SynchronousCompositorOutputSurface);
};
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 b8ba0ea3352..2122a90f36b 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
@@ -67,7 +67,7 @@ void SynchronousInputEventFilter::DidRemoveInputHandler(int routing_id) {
void SynchronousInputEventFilter::DidOverscroll(
int routing_id,
- const cc::DidOverscrollParams& params) {
+ const DidOverscrollParams& params) {
// The SynchronusCompositorImpl can be NULL if the WebContents that it's
// bound to has already been deleted.
SynchronousCompositorImpl* compositor =
@@ -76,4 +76,13 @@ void SynchronousInputEventFilter::DidOverscroll(
compositor->DidOverscroll(params);
}
+void SynchronousInputEventFilter::DidStopFlinging(int routing_id) {
+ // The SynchronusCompositorImpl can be NULL if the WebContents that it's
+ // bound to has already been deleted.
+ SynchronousCompositorImpl* compositor =
+ SynchronousCompositorImpl::FromRoutingID(routing_id);
+ if (compositor)
+ compositor->DidStopFlinging();
+}
+
} // 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 a6fa68fcfda..3b81fa6f889 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
@@ -8,7 +8,7 @@
#include "base/basictypes.h"
#include "base/callback.h"
#include "base/compiler_specific.h"
-#include "content/port/common/input_event_ack_state.h"
+#include "content/common/input/input_event_ack_state.h"
#include "content/renderer/input/input_handler_manager_client.h"
#include "ui/gfx/vector2d_f.h"
@@ -37,7 +37,8 @@ class SynchronousInputEventFilter : public InputHandlerManagerClient {
cc::InputHandler* input_handler) OVERRIDE;
virtual void DidRemoveInputHandler(int routing_id) OVERRIDE;
virtual void DidOverscroll(int routing_id,
- const cc::DidOverscrollParams& params) OVERRIDE;
+ const DidOverscrollParams& params) OVERRIDE;
+ virtual void DidStopFlinging(int routing_id) OVERRIDE;
private:
void SetBoundHandlerOnUIThread(const Handler& handler);
diff --git a/chromium/content/browser/android/interstitial_page_delegate_android.h b/chromium/content/browser/android/interstitial_page_delegate_android.h
index f1ce12cb95e..208717e8787 100644
--- a/chromium/content/browser/android/interstitial_page_delegate_android.h
+++ b/chromium/content/browser/android/interstitial_page_delegate_android.h
@@ -8,7 +8,7 @@
#include <jni.h>
#include <string>
-#include "base/android/jni_helper.h"
+#include "base/android/jni_weak_ref.h"
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "content/common/content_export.h"
diff --git a/chromium/content/browser/android/load_url_params.cc b/chromium/content/browser/android/load_url_params.cc
index 57eff2d5fae..d276622f55a 100644
--- a/chromium/content/browser/android/load_url_params.cc
+++ b/chromium/content/browser/android/load_url_params.cc
@@ -39,7 +39,7 @@ bool RegisterLoadUrlParams(JNIEnv* env) {
jboolean IsDataScheme(JNIEnv* env, jclass clazz, jstring jurl) {
GURL url(base::android::ConvertJavaStringToUTF8(env, jurl));
- return url.SchemeIs(chrome::kDataScheme);
+ return url.SchemeIs(url::kDataScheme);
}
} // namespace content
diff --git a/chromium/content/browser/android/overscroll_glow.cc b/chromium/content/browser/android/overscroll_glow.cc
index 8e08de45eca..fb491f3ebaa 100644
--- a/chromium/content/browser/android/overscroll_glow.cc
+++ b/chromium/content/browser/android/overscroll_glow.cc
@@ -9,6 +9,7 @@
#include "base/threading/worker_pool.h"
#include "cc/layers/image_layer.h"
#include "content/browser/android/edge_effect.h"
+#include "skia/ext/image_operations.h"
#include "ui/gfx/android/java_bitmap.h"
using std::max;
@@ -19,21 +20,39 @@ namespace content {
namespace {
const float kEpsilon = 1e-3f;
+const int kScaledEdgeHeight = 12;
+const int kScaledGlowHeight = 64;
+const float kEdgeHeightAtMdpi = 12.f;
+const float kGlowHeightAtMdpi = 128.f;
+
+SkBitmap CreateSkBitmapFromAndroidResource(const char* name, gfx::Size size) {
+ base::android::ScopedJavaLocalRef<jobject> jobj =
+ gfx::CreateJavaBitmapFromAndroidResource(name, size);
+ if (jobj.is_null())
+ return SkBitmap();
+
+ SkBitmap bitmap = CreateSkBitmapFromJavaBitmap(gfx::JavaBitmap(jobj.obj()));
+ if (bitmap.isNull())
+ return bitmap;
+
+ return skia::ImageOperations::Resize(
+ bitmap, skia::ImageOperations::RESIZE_BOX, size.width(), size.height());
+}
class OverscrollResources {
public:
OverscrollResources() {
TRACE_EVENT0("browser", "OverscrollResources::Create");
edge_bitmap_ =
- gfx::CreateSkBitmapFromResource("android:drawable/overscroll_edge",
- gfx::Size(128, 12));
+ CreateSkBitmapFromAndroidResource("android:drawable/overscroll_edge",
+ gfx::Size(128, kScaledEdgeHeight));
glow_bitmap_ =
- gfx::CreateSkBitmapFromResource("android:drawable/overscroll_glow",
- gfx::Size(128, 64));
+ CreateSkBitmapFromAndroidResource("android:drawable/overscroll_glow",
+ gfx::Size(128, kScaledGlowHeight));
}
- const SkBitmap& edge_bitmap() { return edge_bitmap_; }
- const SkBitmap& glow_bitmap() { return glow_bitmap_; }
+ const SkBitmap& edge_bitmap() const { return edge_bitmap_; }
+ const SkBitmap& glow_bitmap() const { return glow_bitmap_; }
private:
SkBitmap edge_bitmap_;
@@ -82,10 +101,7 @@ scoped_ptr<OverscrollGlow> OverscrollGlow::Create(bool enabled) {
}
OverscrollGlow::OverscrollGlow(bool enabled)
- : enabled_(enabled),
- initialized_(false),
- horizontal_overscroll_enabled_(true),
- vertical_overscroll_enabled_(true) {}
+ : enabled_(enabled), initialized_(false) {}
OverscrollGlow::~OverscrollGlow() {
Detach();
@@ -108,7 +124,8 @@ void OverscrollGlow::Disable() {
bool OverscrollGlow::OnOverscrolled(cc::Layer* overscrolling_layer,
base::TimeTicks current_time,
- gfx::Vector2dF overscroll,
+ gfx::Vector2dF accumulated_overscroll,
+ gfx::Vector2dF overscroll_delta,
gfx::Vector2dF velocity) {
DCHECK(overscrolling_layer);
@@ -117,23 +134,12 @@ bool OverscrollGlow::OnOverscrolled(cc::Layer* overscrolling_layer,
// The size of the glow determines the relative effect of the inputs; an
// empty-sized effect is effectively disabled.
- if (size_.IsEmpty())
+ if (display_params_.size.IsEmpty())
return false;
- if (!horizontal_overscroll_enabled_) {
- overscroll.set_x(0);
- velocity.set_x(0);
- }
- if (!vertical_overscroll_enabled_) {
- overscroll.set_y(0);
- velocity.set_y(0);
- }
-
// Ignore sufficiently small values that won't meaningfuly affect animation.
- overscroll = ZeroSmallComponents(overscroll);
- velocity = ZeroSmallComponents(velocity);
-
- if (overscroll.IsZero()) {
+ overscroll_delta = ZeroSmallComponents(overscroll_delta);
+ if (overscroll_delta.IsZero()) {
if (initialized_) {
Release(current_time);
UpdateLayerAttachment(overscrolling_layer);
@@ -144,28 +150,22 @@ bool OverscrollGlow::OnOverscrolled(cc::Layer* overscrolling_layer,
if (!InitializeIfNecessary())
return false;
- if (!velocity.IsZero()) {
- // Release effects if scrolling has changed directions.
- if (velocity.x() * old_velocity_.x() < 0)
- ReleaseAxis(AXIS_X, current_time);
- if (velocity.y() * old_velocity_.y() < 0)
- ReleaseAxis(AXIS_Y, current_time);
-
- Absorb(current_time, velocity, overscroll, old_overscroll_);
- } else {
- // Release effects when overscroll accumulation violates monotonicity.
- if (overscroll.x() * old_overscroll_.x() < 0 ||
- std::abs(overscroll.x()) < std::abs(old_overscroll_.x()))
- ReleaseAxis(AXIS_X, current_time);
- if (overscroll.y() * old_overscroll_.y() < 0 ||
- std::abs(overscroll.y()) < std::abs(old_overscroll_.y()))
- ReleaseAxis(AXIS_Y, current_time);
-
- Pull(current_time, overscroll - old_overscroll_);
- }
+ gfx::Vector2dF old_overscroll = accumulated_overscroll - overscroll_delta;
+ bool x_overscroll_started =
+ !IsApproxZero(overscroll_delta.x()) && IsApproxZero(old_overscroll.x());
+ bool y_overscroll_started =
+ !IsApproxZero(overscroll_delta.y()) && IsApproxZero(old_overscroll.y());
- old_velocity_ = velocity;
- old_overscroll_ = overscroll;
+ if (x_overscroll_started)
+ ReleaseAxis(AXIS_X, current_time);
+ if (y_overscroll_started)
+ ReleaseAxis(AXIS_Y, current_time);
+
+ velocity = ZeroSmallComponents(velocity);
+ if (!velocity.IsZero())
+ Absorb(current_time, velocity, x_overscroll_started, y_overscroll_started);
+ else
+ Pull(current_time, overscroll_delta);
UpdateLayerAttachment(overscrolling_layer);
return NeedsAnimate();
@@ -177,15 +177,14 @@ bool OverscrollGlow::Animate(base::TimeTicks current_time) {
return false;
}
- const gfx::SizeF sizes[EdgeEffect::EDGE_COUNT] = {
- size_, gfx::SizeF(size_.height(), size_.width()),
- size_, gfx::SizeF(size_.height(), size_.width())
- };
-
for (size_t i = 0; i < EdgeEffect::EDGE_COUNT; ++i) {
if (edge_effects_[i]->Update(current_time)) {
- edge_effects_[i]->ApplyToLayers(sizes[i],
- static_cast<EdgeEffect::Edge>(i));
+ edge_effects_[i]->ApplyToLayers(
+ display_params_.size,
+ static_cast<EdgeEffect::Edge>(i),
+ kEdgeHeightAtMdpi * display_params_.device_scale_factor,
+ kGlowHeightAtMdpi * display_params_.device_scale_factor,
+ display_params_.edge_offsets[i]);
}
}
@@ -197,6 +196,10 @@ bool OverscrollGlow::Animate(base::TimeTicks current_time) {
return true;
}
+void OverscrollGlow::UpdateDisplayParameters(const DisplayParameters& params) {
+ display_params_ = params;
+}
+
bool OverscrollGlow::NeedsAnimate() const {
if (!enabled_ || !initialized_)
return false;
@@ -259,14 +262,15 @@ void OverscrollGlow::Pull(base::TimeTicks current_time,
if (overscroll_delta.IsZero())
return;
- gfx::Vector2dF overscroll_pull = gfx::ScaleVector2d(overscroll_delta,
- 1.f / size_.width(),
- 1.f / size_.height());
+ gfx::Vector2dF overscroll_pull =
+ gfx::ScaleVector2d(overscroll_delta,
+ 1.f / display_params_.size.width(),
+ 1.f / display_params_.size.height());
float edge_overscroll_pull[EdgeEffect::EDGE_COUNT] = {
- min(overscroll_pull.y(), 0.f), // Top
- min(overscroll_pull.x(), 0.f), // Left
- max(overscroll_pull.y(), 0.f), // Bottom
- max(overscroll_pull.x(), 0.f) // Right
+ min(overscroll_pull.y(), 0.f), // Top
+ min(overscroll_pull.x(), 0.f), // Left
+ max(overscroll_pull.y(), 0.f), // Bottom
+ max(overscroll_pull.x(), 0.f) // Right
};
for (size_t i = 0; i < EdgeEffect::EDGE_COUNT; ++i) {
@@ -280,18 +284,18 @@ void OverscrollGlow::Pull(base::TimeTicks current_time,
void OverscrollGlow::Absorb(base::TimeTicks current_time,
gfx::Vector2dF velocity,
- gfx::Vector2dF overscroll,
- gfx::Vector2dF old_overscroll) {
+ bool x_overscroll_started,
+ bool y_overscroll_started) {
DCHECK(enabled_ && initialized_);
- if (overscroll.IsZero() || velocity.IsZero())
+ if (velocity.IsZero())
return;
// Only trigger on initial overscroll at a non-zero velocity
const float overscroll_velocities[EdgeEffect::EDGE_COUNT] = {
- old_overscroll.y() >= 0 && overscroll.y() < 0 ? min(velocity.y(), 0.f) : 0,
- old_overscroll.x() >= 0 && overscroll.x() < 0 ? min(velocity.x(), 0.f) : 0,
- old_overscroll.y() <= 0 && overscroll.y() > 0 ? max(velocity.y(), 0.f) : 0,
- old_overscroll.x() <= 0 && overscroll.x() > 0 ? max(velocity.x(), 0.f) : 0
+ y_overscroll_started ? min(velocity.y(), 0.f) : 0, // Top
+ x_overscroll_started ? min(velocity.x(), 0.f) : 0, // Left
+ y_overscroll_started ? max(velocity.y(), 0.f) : 0, // Bottom
+ x_overscroll_started ? max(velocity.x(), 0.f) : 0 // Right
};
for (size_t i = 0; i < EdgeEffect::EDGE_COUNT; ++i) {
@@ -305,10 +309,8 @@ void OverscrollGlow::Absorb(base::TimeTicks current_time,
void OverscrollGlow::Release(base::TimeTicks current_time) {
DCHECK(initialized_);
- for (size_t i = 0; i < EdgeEffect::EDGE_COUNT; ++i) {
+ for (size_t i = 0; i < EdgeEffect::EDGE_COUNT; ++i)
edge_effects_[i]->Release(current_time);
- }
- old_overscroll_ = old_velocity_ = gfx::Vector2dF();
}
void OverscrollGlow::ReleaseAxis(Axis axis, base::TimeTicks current_time) {
@@ -317,14 +319,10 @@ void OverscrollGlow::ReleaseAxis(Axis axis, base::TimeTicks current_time) {
case AXIS_X:
edge_effects_[EdgeEffect::EDGE_LEFT]->Release(current_time);
edge_effects_[EdgeEffect::EDGE_RIGHT]->Release(current_time);
- old_overscroll_.set_x(0);
- old_velocity_.set_x(0);
break;
case AXIS_Y:
edge_effects_[EdgeEffect::EDGE_TOP]->Release(current_time);
edge_effects_[EdgeEffect::EDGE_BOTTOM]->Release(current_time);
- old_overscroll_.set_y(0);
- old_velocity_.set_y(0);
break;
};
}
@@ -334,5 +332,9 @@ EdgeEffect* OverscrollGlow::GetOppositeEdge(int edge_index) {
return edge_effects_[(edge_index + 2) % EdgeEffect::EDGE_COUNT].get();
}
-} // namespace content
+OverscrollGlow::DisplayParameters::DisplayParameters()
+ : device_scale_factor(1) {
+ edge_offsets[0] = edge_offsets[1] = edge_offsets[2] = edge_offsets[3] = 0.f;
+}
+} // namespace content
diff --git a/chromium/content/browser/android/overscroll_glow.h b/chromium/content/browser/android/overscroll_glow.h
index 3a13fb3c4f1..0a555d41b7a 100644
--- a/chromium/content/browser/android/overscroll_glow.h
+++ b/chromium/content/browser/android/overscroll_glow.h
@@ -22,7 +22,7 @@ namespace content {
/* |OverscrollGlow| mirrors its Android counterpart, OverscrollGlow.java.
* Conscious tradeoffs were made to align this as closely as possible with the
- * original Android java version.
+ * original Android Java version.
*/
class OverscrollGlow {
public:
@@ -43,30 +43,29 @@ class OverscrollGlow {
void Disable();
// Effect layers will be attached to |overscrolling_layer| if necessary.
- // |overscroll| is the accumulated overscroll for the current gesture.
- // |velocity| is the instantaneous velocity for the overscroll.
+ // |accumulated_overscroll| and |overscroll_delta| are in device pixels, while
+ // |velocity| is in device pixels / second.
// Returns true if the effect still needs animation ticks.
bool OnOverscrolled(cc::Layer* overscrolling_layer,
base::TimeTicks current_time,
- gfx::Vector2dF overscroll,
+ gfx::Vector2dF accumulated_overscroll,
+ gfx::Vector2dF overscroll_delta,
gfx::Vector2dF velocity);
// Returns true if the effect still needs animation ticks.
// Note: The effect will detach itself when no further animation is required.
bool Animate(base::TimeTicks current_time);
- // Horizontal overscroll will be ignored when false.
- void set_horizontal_overscroll_enabled(bool enabled) {
- horizontal_overscroll_enabled_ = enabled;
- }
- // Vertical overscroll will be ignored when false.
- void set_vertical_overscroll_enabled(bool enabled) {
- vertical_overscroll_enabled_ = enabled;
- }
- // The size of the layer for which edges will be animated.
- void set_size(gfx::SizeF size) {
- size_ = size;
- }
+ // Update the effect according to the most recent display parameters,
+ // Note: All dimensions are in device pixels.
+ struct DisplayParameters {
+ DisplayParameters();
+ gfx::SizeF size;
+ float edge_offsets[EdgeEffect::EDGE_COUNT];
+ float device_scale_factor;
+ };
+ void UpdateDisplayParameters(const DisplayParameters& params);
+
private:
enum Axis { AXIS_X, AXIS_Y };
@@ -78,12 +77,11 @@ class OverscrollGlow {
bool NeedsAnimate() const;
void UpdateLayerAttachment(cc::Layer* parent);
void Detach();
- void Pull(base::TimeTicks current_time,
- gfx::Vector2dF added_overscroll);
+ void Pull(base::TimeTicks current_time, gfx::Vector2dF overscroll_delta);
void Absorb(base::TimeTicks current_time,
gfx::Vector2dF velocity,
- gfx::Vector2dF overscroll,
- gfx::Vector2dF old_overscroll);
+ bool x_overscroll_started,
+ bool y_overscroll_started);
void Release(base::TimeTicks current_time);
void ReleaseAxis(Axis axis, base::TimeTicks current_time);
@@ -91,13 +89,9 @@ class OverscrollGlow {
scoped_ptr<EdgeEffect> edge_effects_[EdgeEffect::EDGE_COUNT];
+ DisplayParameters display_params_;
bool enabled_;
bool initialized_;
- gfx::SizeF size_;
- gfx::Vector2dF old_overscroll_;
- gfx::Vector2dF old_velocity_;
- bool horizontal_overscroll_enabled_;
- bool vertical_overscroll_enabled_;
scoped_refptr<cc::Layer> root_layer_;
diff --git a/chromium/content/browser/android/surface_texture_peer_browser_impl.cc b/chromium/content/browser/android/surface_texture_peer_browser_impl.cc
index 7935c9ab409..4f61887f880 100644
--- a/chromium/content/browser/android/surface_texture_peer_browser_impl.cc
+++ b/chromium/content/browser/android/surface_texture_peer_browser_impl.cc
@@ -4,7 +4,9 @@
#include "content/browser/android/surface_texture_peer_browser_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/media_web_contents_observer.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
@@ -16,34 +18,52 @@ namespace content {
namespace {
// Pass a java surface object to the MediaPlayerAndroid object
-// identified by render process handle, render view ID and player ID.
+// identified by render process handle, render frame ID and player ID.
static void SetSurfacePeer(
scoped_refptr<gfx::SurfaceTexture> surface_texture,
base::ProcessHandle render_process_handle,
- int render_view_id,
+ int render_frame_id,
int player_id) {
- int renderer_id = 0;
+ int render_process_id = 0;
RenderProcessHost::iterator it = RenderProcessHost::AllHostsIterator();
while (!it.IsAtEnd()) {
if (it.GetCurrentValue()->GetHandle() == render_process_handle) {
- renderer_id = it.GetCurrentValue()->GetID();
+ render_process_id = it.GetCurrentValue()->GetID();
break;
}
it.Advance();
}
+ if (!render_process_id) {
+ DVLOG(1) << "Cannot find render process for render_process_handle "
+ << render_process_handle;
+ return;
+ }
- if (renderer_id) {
- RenderViewHostImpl* host = RenderViewHostImpl::FromID(
- renderer_id, render_view_id);
- if (host) {
- media::MediaPlayerAndroid* player =
- host->media_player_manager()->GetPlayer(player_id);
- if (player &&
- player != host->media_player_manager()->GetFullscreenPlayer()) {
- gfx::ScopedJavaSurface surface(surface_texture.get());
- player->SetVideoSurface(surface.Pass());
- }
- }
+ RenderFrameHostImpl* frame =
+ RenderFrameHostImpl::FromID(render_process_id, render_frame_id);
+ if (!frame) {
+ DVLOG(1) << "Cannot find frame for render_frame_id " << render_frame_id;
+ return;
+ }
+
+ RenderViewHostImpl* view =
+ static_cast<RenderViewHostImpl*>(frame->GetRenderViewHost());
+ BrowserMediaPlayerManager* player_manager =
+ view->media_web_contents_observer()->GetMediaPlayerManager(frame);
+ if (!player_manager) {
+ DVLOG(1) << "Cannot find the media player manager for frame " << frame;
+ return;
+ }
+
+ media::MediaPlayerAndroid* player = player_manager->GetPlayer(player_id);
+ if (!player) {
+ DVLOG(1) << "Cannot find media player for player_id " << player_id;
+ return;
+ }
+
+ if (player != player_manager->GetFullscreenPlayer()) {
+ gfx::ScopedJavaSurface scoped_surface(surface_texture);
+ player->SetVideoSurface(scoped_surface.Pass());
}
}
@@ -58,14 +78,14 @@ SurfaceTexturePeerBrowserImpl::~SurfaceTexturePeerBrowserImpl() {
void SurfaceTexturePeerBrowserImpl::EstablishSurfaceTexturePeer(
base::ProcessHandle render_process_handle,
scoped_refptr<gfx::SurfaceTexture> surface_texture,
- int render_view_id,
+ int render_frame_id,
int player_id) {
if (!surface_texture.get())
return;
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
&SetSurfacePeer, surface_texture, render_process_handle,
- render_view_id, player_id));
+ render_frame_id, player_id));
}
} // namespace content
diff --git a/chromium/content/browser/android/surface_texture_peer_browser_impl.h b/chromium/content/browser/android/surface_texture_peer_browser_impl.h
index 4aa85634dc2..ea3fd046553 100644
--- a/chromium/content/browser/android/surface_texture_peer_browser_impl.h
+++ b/chromium/content/browser/android/surface_texture_peer_browser_impl.h
@@ -25,7 +25,7 @@ class SurfaceTexturePeerBrowserImpl : public SurfaceTexturePeer {
virtual void EstablishSurfaceTexturePeer(
base::ProcessHandle render_process_handle,
scoped_refptr<gfx::SurfaceTexture> surface_texture,
- int render_view_id,
+ int render_frame_id,
int player_id) OVERRIDE;
private:
diff --git a/chromium/content/browser/android/touch_point.cc b/chromium/content/browser/android/touch_point.cc
deleted file mode 100644
index ffe2e50507c..00000000000
--- a/chromium/content/browser/android/touch_point.cc
+++ /dev/null
@@ -1,123 +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/android/touch_point.h"
-
-#include "base/debug/debugger.h"
-#include "base/logging.h"
-#include "base/time/time.h"
-
-#include "jni/TouchPoint_jni.h"
-
-using blink::WebTouchEvent;
-using blink::WebTouchPoint;
-
-namespace {
-
-void MaybeAddTouchPoint(JNIEnv* env,
- jobject pt,
- float dpi_scale,
- blink::WebTouchEvent& event) {
- WebTouchPoint::State state = static_cast<WebTouchPoint::State>(
- Java_TouchPoint_getState(env, pt));
- if (state == WebTouchPoint::StateUndefined)
- return;
-
- // When generating a cancel event from an event of a different type, the
- // touch points are out of sync, so we ensure the points are marked as
- // canceled as well.
- if (event.type == WebTouchEvent::TouchCancel)
- state = WebTouchPoint::StateCancelled;
-
- // Record the current number of points in the WebTouchEvent
- const int idx = event.touchesLength;
- DCHECK_LT(idx, blink::WebTouchEvent::touchesLengthCap);
-
- WebTouchPoint wtp;
- wtp.id = Java_TouchPoint_getId(env, pt);
- wtp.state = state;
- wtp.position.x = Java_TouchPoint_getX(env, pt) / dpi_scale;
- wtp.position.y = Java_TouchPoint_getY(env, pt) / dpi_scale;
- // TODO(joth): Raw event co-ordinates.
- wtp.screenPosition = wtp.position;
- wtp.force = Java_TouchPoint_getPressure(env, pt);
-
- const int radiusMajor = static_cast<int>(
- Java_TouchPoint_getTouchMajor(env, pt) * 0.5f / dpi_scale);
- const int radiusMinor = static_cast<int>(
- Java_TouchPoint_getTouchMinor(env, pt) * 0.5f / dpi_scale);
- const float majorAngleInRadiansClockwiseFromVertical =
- Java_TouchPoint_getOrientation(env, pt);
- const float majorAngleInDegreesClockwiseFromVertical =
- std::isnan(majorAngleInRadiansClockwiseFromVertical)
- ? 0.f : (majorAngleInRadiansClockwiseFromVertical * 180.f) / M_PI;
- // Android provides a major axis orientation clockwise with respect to the
- // vertical of [-90, 90], while the W3C specifies a range of [0, 90].
- if (majorAngleInDegreesClockwiseFromVertical >= 0) {
- wtp.radiusX = radiusMinor;
- wtp.radiusY = radiusMajor;
- wtp.rotationAngle = majorAngleInDegreesClockwiseFromVertical;
- } else {
- wtp.radiusX = radiusMajor;
- wtp.radiusY = radiusMinor;
- wtp.rotationAngle = majorAngleInDegreesClockwiseFromVertical + 90.f;
- }
- DCHECK_GE(wtp.rotationAngle, 0.f);
- DCHECK_LE(wtp.rotationAngle, 90.f);
-
- // Add the newly created WebTouchPoint to the event
- event.touches[idx] = wtp;
- ++(event.touchesLength);
-}
-
-} // namespace
-
-namespace content {
-
-void TouchPoint::BuildWebTouchEvent(JNIEnv* env,
- jint type,
- jlong time_ms,
- float dpi_scale,
- jobjectArray pts,
- blink::WebTouchEvent& event) {
- event.type = static_cast<WebTouchEvent::Type>(type);
- event.timeStampSeconds =
- static_cast<double>(time_ms) / base::Time::kMillisecondsPerSecond;
- int arrayLength = env->GetArrayLength(pts);
- // Loop until either all of the input points have been consumed or the output
- // array has been filled
- for (int i = 0; i < arrayLength; i++) {
- jobject pt = env->GetObjectArrayElement(pts, i);
- MaybeAddTouchPoint(env, pt, dpi_scale, event);
- if (event.touchesLength >= event.touchesLengthCap)
- break;
- }
- DCHECK_GT(event.touchesLength, 0U);
-}
-
-static void RegisterConstants(JNIEnv* env) {
- Java_TouchPoint_initializeConstants(
- env,
- blink::WebTouchEvent::TouchStart,
- blink::WebTouchEvent::TouchMove,
- blink::WebTouchEvent::TouchEnd,
- blink::WebTouchEvent::TouchCancel,
- blink::WebTouchPoint::StateUndefined,
- blink::WebTouchPoint::StateReleased,
- blink::WebTouchPoint::StatePressed,
- blink::WebTouchPoint::StateMoved,
- blink::WebTouchPoint::StateStationary,
- blink::WebTouchPoint::StateCancelled);
-}
-
-bool RegisterTouchPoint(JNIEnv* env) {
- if (!RegisterNativesImpl(env))
- return false;
-
- RegisterConstants(env);
-
- return true;
-}
-
-} // namespace content
diff --git a/chromium/content/browser/android/touch_point.h b/chromium/content/browser/android/touch_point.h
deleted file mode 100644
index d7caac08764..00000000000
--- a/chromium/content/browser/android/touch_point.h
+++ /dev/null
@@ -1,30 +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_ANDROID_TOUCH_POINT_H_
-#define CONTENT_BROWSER_ANDROID_TOUCH_POINT_H_
-
-#include <jni.h>
-
-#include "third_party/WebKit/public/web/WebInputEvent.h"
-
-namespace content {
-
-// This class provides a helper method to convert a java object array of touch
-// events (in physical pixdels) into a blink::WebTouchEvent (in dip).
-class TouchPoint {
- public:
- static void BuildWebTouchEvent(JNIEnv* env,
- jint type,
- jlong time_ms,
- float dpi_scale,
- jobjectArray pts,
- blink::WebTouchEvent& event);
-};
-
-bool RegisterTouchPoint(JNIEnv* env);
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_ANDROID_CHROME_VIEW_H_
diff --git a/chromium/content/browser/android/tracing_controller_android.cc b/chromium/content/browser/android/tracing_controller_android.cc
index 75ac6d7c238..a9c5b408147 100644
--- a/chromium/content/browser/android/tracing_controller_android.cc
+++ b/chromium/content/browser/android/tracing_controller_android.cc
@@ -7,6 +7,7 @@
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/debug/trace_event.h"
+#include "base/json/json_writer.h"
#include "base/logging.h"
#include "content/public/browser/tracing_controller.h"
#include "jni/TracingControllerAndroid_jni.h"
@@ -30,16 +31,13 @@ void TracingControllerAndroid::Destroy(JNIEnv* env, jobject obj) {
bool TracingControllerAndroid::StartTracing(JNIEnv* env,
jobject obj,
- jstring jfilename,
jstring jcategories,
jboolean record_continuously) {
- file_path_ = base::FilePath(
- base::android::ConvertJavaStringToUTF8(env, jfilename));
std::string categories =
base::android::ConvertJavaStringToUTF8(env, jcategories);
// This log is required by adb_profile_chrome.py.
- LOG(WARNING) << "Logging performance trace to file: " << file_path_.value();
+ LOG(WARNING) << "Logging performance trace to file";
return TracingController::GetInstance()->EnableRecording(
categories,
@@ -48,16 +46,29 @@ bool TracingControllerAndroid::StartTracing(JNIEnv* env,
TracingController::EnableRecordingDoneCallback());
}
-void TracingControllerAndroid::StopTracing(JNIEnv* env, jobject obj) {
+void TracingControllerAndroid::StopTracing(JNIEnv* env,
+ jobject obj,
+ jstring jfilepath) {
+ base::FilePath file_path(
+ base::android::ConvertJavaStringToUTF8(env, jfilepath));
if (!TracingController::GetInstance()->DisableRecording(
- file_path_,
+ file_path,
base::Bind(&TracingControllerAndroid::OnTracingStopped,
weak_factory_.GetWeakPtr()))) {
LOG(ERROR) << "EndTracingAsync failed, forcing an immediate stop";
- OnTracingStopped(file_path_);
+ OnTracingStopped(file_path);
}
}
+void TracingControllerAndroid::GenerateTracingFilePath(
+ base::FilePath* file_path) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ ScopedJavaLocalRef<jstring> jfilename =
+ Java_TracingControllerAndroid_generateTracingFilePath(env);
+ *file_path = base::FilePath(
+ base::android::ConvertJavaStringToUTF8(env, jfilename.obj()));
+}
+
void TracingControllerAndroid::OnTracingStopped(
const base::FilePath& file_path) {
JNIEnv* env = base::android::AttachCurrentThread();
@@ -66,6 +77,31 @@ void TracingControllerAndroid::OnTracingStopped(
Java_TracingControllerAndroid_onTracingStopped(env, obj.obj());
}
+bool TracingControllerAndroid::GetKnownCategoryGroupsAsync(JNIEnv* env,
+ jobject obj) {
+ if (!TracingController::GetInstance()->GetCategories(
+ base::Bind(&TracingControllerAndroid::OnKnownCategoriesReceived,
+ weak_factory_.GetWeakPtr()))) {
+ return false;
+ }
+ return true;
+}
+
+void TracingControllerAndroid::OnKnownCategoriesReceived(
+ const std::set<std::string>& categories_received) {
+ scoped_ptr<base::ListValue> category_list(new base::ListValue());
+ for (std::set<std::string>::const_iterator it = categories_received.begin();
+ it != categories_received.end();
+ ++it) {
+ category_list->AppendString(*it);
+ }
+ std::string received_category_list;
+ base::JSONWriter::Write(category_list.get(), &received_category_list);
+
+ // This log is required by adb_profile_chrome.py.
+ LOG(WARNING) << "{\"traceCategoriesList\": " << received_category_list << "}";
+}
+
static jstring GetDefaultCategories(JNIEnv* env, jobject obj) {
return base::android::ConvertUTF8ToJavaString(env,
base::debug::CategoryFilter::kDefaultCategoryFilterString).Release();
diff --git a/chromium/content/browser/android/tracing_controller_android.h b/chromium/content/browser/android/tracing_controller_android.h
index 4d70e7b75ef..ab426d78997 100644
--- a/chromium/content/browser/android/tracing_controller_android.h
+++ b/chromium/content/browser/android/tracing_controller_android.h
@@ -5,7 +5,9 @@
#ifndef CONTENT_BROWSER_ANDROID_TRACING_CONTROLLER_ANDROID_H_
#define CONTENT_BROWSER_ANDROID_TRACING_CONTROLLER_ANDROID_H_
-#include "base/android/jni_helper.h"
+#include <set>
+
+#include "base/android/jni_weak_ref.h"
#include "base/files/file_path.h"
#include "base/memory/weak_ptr.h"
@@ -19,17 +21,19 @@ class TracingControllerAndroid {
bool StartTracing(JNIEnv* env,
jobject obj,
- jstring filename,
jstring categories,
jboolean record_continuously);
- void StopTracing(JNIEnv* env, jobject obj);
+ void StopTracing(JNIEnv* env, jobject obj, jstring jfilepath);
+ bool GetKnownCategoryGroupsAsync(JNIEnv* env, jobject obj);
+ static void GenerateTracingFilePath(base::FilePath* file_path);
private:
~TracingControllerAndroid();
void OnTracingStopped(const base::FilePath& file_path);
+ void OnKnownCategoriesReceived(
+ const std::set<std::string>& categories_received);
JavaObjectWeakGlobalRef weak_java_object_;
- base::FilePath file_path_;
base::WeakPtrFactory<TracingControllerAndroid> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(TracingControllerAndroid);
diff --git a/chromium/content/browser/android/ui_resource_provider_impl.cc b/chromium/content/browser/android/ui_resource_provider_impl.cc
new file mode 100644
index 00000000000..f97975cc81e
--- /dev/null
+++ b/chromium/content/browser/android/ui_resource_provider_impl.cc
@@ -0,0 +1,60 @@
+// 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/android/ui_resource_provider_impl.h"
+
+#include "cc/resources/ui_resource_client.h"
+#include "cc/trees/layer_tree_host.h"
+#include "content/public/browser/android/ui_resource_client_android.h"
+
+namespace content {
+
+UIResourceProviderImpl::UIResourceProviderImpl() : host_(NULL) {
+}
+
+UIResourceProviderImpl::~UIResourceProviderImpl() {
+ SetLayerTreeHost(NULL);
+}
+
+void UIResourceProviderImpl::SetLayerTreeHost(cc::LayerTreeHost* host) {
+ if (host_ == host)
+ return;
+ host_ = host;
+ UIResourcesAreInvalid();
+}
+
+void UIResourceProviderImpl::UIResourcesAreInvalid() {
+ UIResourceClientMap client_map = ui_resource_client_map_;
+ ui_resource_client_map_.clear();
+ for (UIResourceClientMap::iterator iter = client_map.begin();
+ iter != client_map.end();
+ iter++) {
+ iter->second->UIResourceIsInvalid();
+ }
+}
+
+cc::UIResourceId UIResourceProviderImpl::CreateUIResource(
+ UIResourceClientAndroid* client) {
+ if (!host_)
+ return 0;
+ cc::UIResourceId id = host_->CreateUIResource(client);
+ DCHECK(ui_resource_client_map_.find(id) == ui_resource_client_map_.end());
+
+ ui_resource_client_map_[id] = client;
+ return id;
+}
+
+void UIResourceProviderImpl::DeleteUIResource(cc::UIResourceId ui_resource_id) {
+ UIResourceClientMap::iterator iter =
+ ui_resource_client_map_.find(ui_resource_id);
+ DCHECK(iter != ui_resource_client_map_.end());
+
+ ui_resource_client_map_.erase(iter);
+
+ if (!host_)
+ return;
+ host_->DeleteUIResource(ui_resource_id);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/android/ui_resource_provider_impl.h b/chromium/content/browser/android/ui_resource_provider_impl.h
new file mode 100644
index 00000000000..79c23014d2d
--- /dev/null
+++ b/chromium/content/browser/android/ui_resource_provider_impl.h
@@ -0,0 +1,46 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_ANDROID_UI_RESOURCE_PROVIDER_IMPL_H_
+#define CONTENT_BROWSER_ANDROID_UI_RESOURCE_PROVIDER_IMPL_H_
+
+#include "base/containers/hash_tables.h"
+#include "content/public/browser/android/ui_resource_provider.h"
+
+namespace cc {
+class LayerTreeHost;
+}
+
+namespace content {
+
+class UIResourceClientAndroid;
+
+class UIResourceProviderImpl : public UIResourceProvider {
+ public:
+ UIResourceProviderImpl();
+
+ virtual ~UIResourceProviderImpl();
+
+ void SetLayerTreeHost(cc::LayerTreeHost* host);
+
+ void UIResourcesAreInvalid();
+
+ virtual cc::UIResourceId CreateUIResource(
+ UIResourceClientAndroid* client) OVERRIDE;
+
+ virtual void DeleteUIResource(cc::UIResourceId resource_id) OVERRIDE;
+
+ private:
+ typedef base::hash_map<cc::UIResourceId, UIResourceClientAndroid*>
+ UIResourceClientMap;
+ UIResourceClientMap ui_resource_client_map_;
+
+ cc::LayerTreeHost* host_;
+
+ DISALLOW_COPY_AND_ASSIGN(UIResourceProviderImpl);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_ANDROID_UI_RESOURCE_PROVIDER_IMPL_H_
diff --git a/chromium/content/browser/android/web_contents_observer_android.cc b/chromium/content/browser/android/web_contents_observer_android.cc
index 797ea3b1af9..0aa9c0b3dee 100644
--- a/chromium/content/browser/android/web_contents_observer_android.cc
+++ b/chromium/content/browser/android/web_contents_observer_android.cc
@@ -11,7 +11,6 @@
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
-#include "content/browser/android/content_view_core_impl.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/navigation_details.h"
@@ -36,11 +35,13 @@ WebContentsObserverAndroid::WebContentsObserverAndroid(
WebContentsObserverAndroid::~WebContentsObserverAndroid() {
}
-jlong Init(JNIEnv* env, jobject obj, jlong native_content_view_core) {
- ContentViewCore* content_view_core =
- reinterpret_cast<ContentViewCore*>(native_content_view_core);
+jlong Init(JNIEnv* env, jobject obj, jobject java_web_contents) {
+ content::WebContents* web_contents =
+ content::WebContents::FromJavaWebContents(java_web_contents);
+ CHECK(web_contents);
+
WebContentsObserverAndroid* native_observer = new WebContentsObserverAndroid(
- env, obj, content_view_core->GetWebContents());
+ env, obj, web_contents);
return reinterpret_cast<intptr_t>(native_observer);
}
@@ -48,8 +49,7 @@ void WebContentsObserverAndroid::Destroy(JNIEnv* env, jobject obj) {
delete this;
}
-void WebContentsObserverAndroid::WebContentsDestroyed(
- WebContents* web_contents) {
+void WebContentsObserverAndroid::WebContentsDestroyed() {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj(weak_java_observer_.get(env));
if (obj.is_null()) {
@@ -130,16 +130,23 @@ void WebContentsObserverAndroid::DidNavigateMainFrame(
ConvertUTF8ToJavaString(env, params.url.spec()));
ScopedJavaLocalRef<jstring> jstring_base_url(
ConvertUTF8ToJavaString(env, params.base_url.spec()));
+
// See http://crbug.com/251330 for why it's determined this way.
- bool in_page_navigation =
- details.type == NAVIGATION_TYPE_IN_PAGE || details.is_in_page;
- // TODO(mkosiba): delete once downstream rolls.
- Java_WebContentsObserverAndroid_didNavigateMainFrameV_JLS_JLS_Z(
- env, obj.obj(), jstring_url.obj(), jstring_base_url.obj(),
- details.is_navigation_to_different_page());
- Java_WebContentsObserverAndroid_didNavigateMainFrameV_JLS_JLS_Z_Z(
+ url::Replacements<char> replacements;
+ replacements.ClearRef();
+ bool urls_same_ignoring_fragment =
+ params.url.ReplaceComponents(replacements) ==
+ details.previous_url.ReplaceComponents(replacements);
+
+ // is_fragment_navigation is indicative of the intent of this variable.
+ // However, there isn't sufficient information here to determine whether this
+ // is actually a fragment navigation, or a history API navigation to a URL
+ // that would also be valid for a fragment navigation.
+ bool is_fragment_navigation = urls_same_ignoring_fragment &&
+ (details.type == NAVIGATION_TYPE_IN_PAGE || details.is_in_page);
+ Java_WebContentsObserverAndroid_didNavigateMainFrame(
env, obj.obj(), jstring_url.obj(), jstring_base_url.obj(),
- details.is_navigation_to_different_page(), in_page_navigation);
+ details.is_navigation_to_different_page(), is_fragment_navigation);
}
void WebContentsObserverAndroid::DidNavigateAnyFrame(
@@ -221,21 +228,24 @@ void WebContentsObserverAndroid::DidFinishLoad(
env, obj.obj(), frame_id, jstring_url.obj(), is_main_frame);
}
-void WebContentsObserverAndroid::NavigationEntryCommitted(
- const LoadCommittedDetails& load_details) {
+void WebContentsObserverAndroid::DocumentLoadedInFrame(
+ int64 frame_id,
+ RenderViewHost* render_view_host) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj(weak_java_observer_.get(env));
if (obj.is_null())
return;
- Java_WebContentsObserverAndroid_navigationEntryCommitted(env, obj.obj());
+ Java_WebContentsObserverAndroid_documentLoadedInFrame(
+ env, obj.obj(), frame_id);
}
-void WebContentsObserverAndroid::DidChangeVisibleSSLState() {
+void WebContentsObserverAndroid::NavigationEntryCommitted(
+ const LoadCommittedDetails& load_details) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj(weak_java_observer_.get(env));
if (obj.is_null())
return;
- Java_WebContentsObserverAndroid_didChangeVisibleSSLState(env, obj.obj());
+ Java_WebContentsObserverAndroid_navigationEntryCommitted(env, obj.obj());
}
void WebContentsObserverAndroid::DidAttachInterstitialPage() {
@@ -254,6 +264,14 @@ void WebContentsObserverAndroid::DidDetachInterstitialPage() {
Java_WebContentsObserverAndroid_didDetachInterstitialPage(env, obj.obj());
}
+void WebContentsObserverAndroid::DidChangeThemeColor(SkColor color) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj(weak_java_observer_.get(env));
+ if (obj.is_null())
+ return;
+ Java_WebContentsObserverAndroid_didChangeThemeColor(env, obj.obj(), color);
+}
+
void WebContentsObserverAndroid::DidFailLoadInternal(
bool is_provisional_load,
bool is_main_frame,
@@ -277,6 +295,15 @@ void WebContentsObserverAndroid::DidFailLoadInternal(
jstring_error_description.obj(), jstring_url.obj());
}
+void WebContentsObserverAndroid::DidFirstVisuallyNonEmptyPaint() {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj(weak_java_observer_.get(env));
+ if (obj.is_null())
+ return;
+ Java_WebContentsObserverAndroid_didFirstVisuallyNonEmptyPaint(
+ env, obj.obj());
+}
+
bool RegisterWebContentsObserverAndroid(JNIEnv* env) {
return RegisterNativesImpl(env);
}
diff --git a/chromium/content/browser/android/web_contents_observer_android.h b/chromium/content/browser/android/web_contents_observer_android.h
index 15a136390f8..e13606c3812 100644
--- a/chromium/content/browser/android/web_contents_observer_android.h
+++ b/chromium/content/browser/android/web_contents_observer_android.h
@@ -7,7 +7,7 @@
#include <jni.h>
-#include "base/android/jni_helper.h"
+#include "base/android/jni_weak_ref.h"
#include "base/basictypes.h"
#include "base/process/kill.h"
#include "content/browser/web_contents/web_contents_impl.h"
@@ -54,6 +54,7 @@ class WebContentsObserverAndroid : public WebContentsObserver {
const FrameNavigateParams& params) OVERRIDE;
virtual void DidNavigateAnyFrame(const LoadCommittedDetails& details,
const FrameNavigateParams& params) OVERRIDE;
+ virtual void DidFirstVisuallyNonEmptyPaint() OVERRIDE;
virtual void DidStartProvisionalLoadForFrame(
int64 frame_id,
int64 parent_frame_id,
@@ -73,12 +74,14 @@ class WebContentsObserverAndroid : public WebContentsObserver {
const GURL& validated_url,
bool is_main_frame,
RenderViewHost* render_view_host) OVERRIDE;
+ virtual void DocumentLoadedInFrame(int64 frame_id,
+ RenderViewHost* render_view_host) OVERRIDE;
virtual void NavigationEntryCommitted(
const LoadCommittedDetails& load_details) OVERRIDE;
- virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE;
- virtual void DidChangeVisibleSSLState() OVERRIDE;
+ virtual void WebContentsDestroyed() OVERRIDE;
virtual void DidAttachInterstitialPage() OVERRIDE;
virtual void DidDetachInterstitialPage() OVERRIDE;
+ virtual void DidChangeThemeColor(SkColor color) OVERRIDE;
void DidFailLoadInternal(bool is_provisional_load,
bool is_main_frame,
diff --git a/chromium/content/browser/appcache/appcache_database_unittest.cc b/chromium/content/browser/appcache/appcache_database_unittest.cc
new file mode 100644
index 00000000000..92fa6a57d91
--- /dev/null
+++ b/chromium/content/browser/appcache/appcache_database_unittest.cc
@@ -0,0 +1,1216 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/strings/stringprintf.h"
+#include "sql/connection.h"
+#include "sql/meta_table.h"
+#include "sql/statement.h"
+#include "sql/test/scoped_error_ignorer.h"
+#include "sql/test/test_helpers.h"
+#include "sql/transaction.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/sqlite/sqlite3.h"
+#include "webkit/browser/appcache/appcache_database.h"
+#include "webkit/browser/appcache/appcache_entry.h"
+
+using appcache::AppCacheDatabase;
+using appcache::AppCacheEntry;
+using appcache::APPCACHE_FALLBACK_NAMESPACE;
+using appcache::APPCACHE_INTERCEPT_NAMESPACE;
+using appcache::APPCACHE_NETWORK_NAMESPACE;
+
+namespace {
+
+const base::Time kZeroTime;
+
+} // namespace
+
+namespace content {
+
+class AppCacheDatabaseTest {};
+
+TEST(AppCacheDatabaseTest, LazyOpen) {
+ // Use an empty file path to use an in-memory sqlite database.
+ const base::FilePath kEmptyPath;
+ AppCacheDatabase db(kEmptyPath);
+
+ EXPECT_FALSE(db.LazyOpen(false));
+ EXPECT_TRUE(db.LazyOpen(true));
+
+ int64 group_id, cache_id, response_id, deleteable_response_rowid;
+ group_id = cache_id = response_id = deleteable_response_rowid = 0;
+ EXPECT_TRUE(db.FindLastStorageIds(&group_id, &cache_id, &response_id,
+ &deleteable_response_rowid));
+ EXPECT_EQ(0, group_id);
+ EXPECT_EQ(0, cache_id);
+ EXPECT_EQ(0, response_id);
+ EXPECT_EQ(0, deleteable_response_rowid);
+
+ std::set<GURL> origins;
+ EXPECT_TRUE(db.FindOriginsWithGroups(&origins));
+ EXPECT_TRUE(origins.empty());
+}
+
+TEST(AppCacheDatabaseTest, ReCreate) {
+ // Real files on disk for this test.
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ const base::FilePath kDbFile = temp_dir.path().AppendASCII("appcache.db");
+ const base::FilePath kNestedDir = temp_dir.path().AppendASCII("nested");
+ const base::FilePath kOtherFile = kNestedDir.AppendASCII("other_file");
+ EXPECT_TRUE(base::CreateDirectory(kNestedDir));
+ EXPECT_EQ(3, base::WriteFile(kOtherFile, "foo", 3));
+
+ AppCacheDatabase db(kDbFile);
+ EXPECT_FALSE(db.LazyOpen(false));
+ EXPECT_TRUE(db.LazyOpen(true));
+
+ EXPECT_TRUE(base::PathExists(kDbFile));
+ EXPECT_TRUE(base::DirectoryExists(kNestedDir));
+ EXPECT_TRUE(base::PathExists(kOtherFile));
+
+ EXPECT_TRUE(db.DeleteExistingAndCreateNewDatabase());
+
+ EXPECT_TRUE(base::PathExists(kDbFile));
+ EXPECT_FALSE(base::DirectoryExists(kNestedDir));
+ EXPECT_FALSE(base::PathExists(kOtherFile));
+}
+
+#ifdef NDEBUG
+// Only run in release builds because sql::Connection and familiy
+// crank up DLOG(FATAL)'ness and this test presents it with
+// intentionally bad data which causes debug builds to exit instead
+// of run to completion. In release builds, errors the are delivered
+// to the consumer so we can test the error handling of the consumer.
+// TODO: crbug/328576
+TEST(AppCacheDatabaseTest, QuickIntegrityCheck) {
+ // Real files on disk for this test too, a corrupt database file.
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ base::FilePath mock_dir = temp_dir.path().AppendASCII("mock");
+ ASSERT_TRUE(base::CreateDirectory(mock_dir));
+
+ const base::FilePath kDbFile = mock_dir.AppendASCII("appcache.db");
+ const base::FilePath kOtherFile = mock_dir.AppendASCII("other_file");
+ EXPECT_EQ(3, base::WriteFile(kOtherFile, "foo", 3));
+
+ // First create a valid db file.
+ {
+ AppCacheDatabase db(kDbFile);
+ EXPECT_TRUE(db.LazyOpen(true));
+ EXPECT_TRUE(base::PathExists(kOtherFile));
+ EXPECT_TRUE(base::PathExists(kDbFile));
+ }
+
+ // Break it.
+ ASSERT_TRUE(sql::test::CorruptSizeInHeader(kDbFile));
+
+ // Reopening will notice the corruption and delete/recreate the directory.
+ {
+ sql::ScopedErrorIgnorer ignore_errors;
+ ignore_errors.IgnoreError(SQLITE_CORRUPT);
+ AppCacheDatabase db(kDbFile);
+ EXPECT_TRUE(db.LazyOpen(true));
+ EXPECT_FALSE(base::PathExists(kOtherFile));
+ EXPECT_TRUE(base::PathExists(kDbFile));
+ EXPECT_TRUE(ignore_errors.CheckIgnoredErrors());
+ }
+}
+#endif // NDEBUG
+
+TEST(AppCacheDatabaseTest, WasCorrutionDetected) {
+ // Real files on disk for this test too, a corrupt database file.
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ const base::FilePath kDbFile = temp_dir.path().AppendASCII("appcache.db");
+
+ // First create a valid db file.
+ AppCacheDatabase db(kDbFile);
+ EXPECT_TRUE(db.LazyOpen(true));
+ EXPECT_TRUE(base::PathExists(kDbFile));
+ EXPECT_FALSE(db.was_corruption_detected());
+
+ // Break it.
+ ASSERT_TRUE(sql::test::CorruptSizeInHeader(kDbFile));
+
+ // See the the corruption is detected and reported.
+ {
+ sql::ScopedErrorIgnorer ignore_errors;
+ ignore_errors.IgnoreError(SQLITE_CORRUPT);
+ std::map<GURL, int64> usage_map;
+ EXPECT_FALSE(db.GetAllOriginUsage(&usage_map));
+ EXPECT_TRUE(db.was_corruption_detected());
+ EXPECT_TRUE(base::PathExists(kDbFile));
+ EXPECT_TRUE(ignore_errors.CheckIgnoredErrors());
+ }
+}
+
+TEST(AppCacheDatabaseTest, ExperimentalFlags) {
+ const char kExperimentFlagsKey[] = "ExperimentFlags";
+ std::string kInjectedFlags("exp1,exp2");
+
+ // Real files on disk for this test.
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ const base::FilePath kDbFile = temp_dir.path().AppendASCII("appcache.db");
+ const base::FilePath kOtherFile = temp_dir.path().AppendASCII("other_file");
+ EXPECT_EQ(3, base::WriteFile(kOtherFile, "foo", 3));
+ EXPECT_TRUE(base::PathExists(kOtherFile));
+
+ // Inject a non empty flags value, and verify it got there.
+ {
+ AppCacheDatabase db(kDbFile);
+ EXPECT_TRUE(db.LazyOpen(true));
+ EXPECT_TRUE(db.meta_table_->SetValue(kExperimentFlagsKey, kInjectedFlags));
+ std::string flags;
+ EXPECT_TRUE(db.meta_table_->GetValue(kExperimentFlagsKey, &flags));
+ EXPECT_EQ(kInjectedFlags, flags);
+ }
+
+ // If flags don't match the expected value, empty string by default,
+ // the database should be recreated and other files should be cleared out.
+ {
+ AppCacheDatabase db(kDbFile);
+ EXPECT_TRUE(db.LazyOpen(false));
+ std::string flags;
+ EXPECT_TRUE(db.meta_table_->GetValue(kExperimentFlagsKey, &flags));
+ EXPECT_TRUE(flags.empty());
+ EXPECT_FALSE(base::PathExists(kOtherFile));
+ }
+}
+
+TEST(AppCacheDatabaseTest, EntryRecords) {
+ const base::FilePath kEmptyPath;
+ AppCacheDatabase db(kEmptyPath);
+ EXPECT_TRUE(db.LazyOpen(true));
+
+ sql::ScopedErrorIgnorer ignore_errors;
+ // TODO(shess): Suppressing SQLITE_CONSTRAINT because the code
+ // expects that and handles the resulting error. Consider revising
+ // the code to use INSERT OR IGNORE (which would not throw
+ // SQLITE_CONSTRAINT) and then check ChangeCount() to see if any
+ // changes were made.
+ ignore_errors.IgnoreError(SQLITE_CONSTRAINT);
+
+ AppCacheDatabase::EntryRecord entry;
+
+ entry.cache_id = 1;
+ entry.url = GURL("http://blah/1");
+ entry.flags = AppCacheEntry::MASTER;
+ entry.response_id = 1;
+ entry.response_size = 100;
+ EXPECT_TRUE(db.InsertEntry(&entry));
+ EXPECT_FALSE(db.InsertEntry(&entry));
+
+ entry.cache_id = 2;
+ entry.url = GURL("http://blah/2");
+ entry.flags = AppCacheEntry::EXPLICIT;
+ entry.response_id = 2;
+ entry.response_size = 200;
+ EXPECT_TRUE(db.InsertEntry(&entry));
+
+ entry.cache_id = 2;
+ entry.url = GURL("http://blah/3");
+ entry.flags = AppCacheEntry::MANIFEST;
+ entry.response_id = 3;
+ entry.response_size = 300;
+ EXPECT_TRUE(db.InsertEntry(&entry));
+
+ std::vector<AppCacheDatabase::EntryRecord> found;
+
+ EXPECT_TRUE(db.FindEntriesForCache(1, &found));
+ EXPECT_EQ(1U, found.size());
+ EXPECT_EQ(1, found[0].cache_id);
+ EXPECT_EQ(GURL("http://blah/1"), found[0].url);
+ EXPECT_EQ(AppCacheEntry::MASTER, found[0].flags);
+ EXPECT_EQ(1, found[0].response_id);
+ EXPECT_EQ(100, found[0].response_size);
+ found.clear();
+
+ EXPECT_TRUE(db.AddEntryFlags(GURL("http://blah/1"), 1,
+ AppCacheEntry::FOREIGN));
+ EXPECT_TRUE(db.FindEntriesForCache(1, &found));
+ EXPECT_EQ(1U, found.size());
+ EXPECT_EQ(AppCacheEntry::MASTER | AppCacheEntry::FOREIGN, found[0].flags);
+ found.clear();
+
+ EXPECT_TRUE(db.FindEntriesForCache(2, &found));
+ EXPECT_EQ(2U, found.size());
+ EXPECT_EQ(2, found[0].cache_id);
+ EXPECT_EQ(GURL("http://blah/2"), found[0].url);
+ EXPECT_EQ(AppCacheEntry::EXPLICIT, found[0].flags);
+ EXPECT_EQ(2, found[0].response_id);
+ EXPECT_EQ(200, found[0].response_size);
+ EXPECT_EQ(2, found[1].cache_id);
+ EXPECT_EQ(GURL("http://blah/3"), found[1].url);
+ EXPECT_EQ(AppCacheEntry::MANIFEST, found[1].flags);
+ EXPECT_EQ(3, found[1].response_id);
+ EXPECT_EQ(300, found[1].response_size);
+ found.clear();
+
+ EXPECT_TRUE(db.DeleteEntriesForCache(2));
+ EXPECT_TRUE(db.FindEntriesForCache(2, &found));
+ EXPECT_TRUE(found.empty());
+ found.clear();
+
+ EXPECT_TRUE(db.DeleteEntriesForCache(1));
+ EXPECT_FALSE(db.AddEntryFlags(GURL("http://blah/1"), 1,
+ AppCacheEntry::FOREIGN));
+
+ ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
+}
+
+TEST(AppCacheDatabaseTest, CacheRecords) {
+ const base::FilePath kEmptyPath;
+ AppCacheDatabase db(kEmptyPath);
+ EXPECT_TRUE(db.LazyOpen(true));
+
+ sql::ScopedErrorIgnorer ignore_errors;
+ // TODO(shess): See EntryRecords test.
+ ignore_errors.IgnoreError(SQLITE_CONSTRAINT);
+
+ const AppCacheDatabase::CacheRecord kZeroRecord;
+ AppCacheDatabase::CacheRecord record;
+ EXPECT_FALSE(db.FindCache(1, &record));
+
+ record.cache_id = 1;
+ record.group_id = 1;
+ record.online_wildcard = true;
+ record.update_time = kZeroTime;
+ record.cache_size = 100;
+ EXPECT_TRUE(db.InsertCache(&record));
+ EXPECT_FALSE(db.InsertCache(&record));
+
+ record = kZeroRecord;
+ EXPECT_TRUE(db.FindCache(1, &record));
+ EXPECT_EQ(1, record.cache_id);
+ EXPECT_EQ(1, record.group_id);
+ EXPECT_TRUE(record.online_wildcard);
+ EXPECT_TRUE(kZeroTime == record.update_time);
+ EXPECT_EQ(100, record.cache_size);
+
+ record = kZeroRecord;
+ EXPECT_TRUE(db.FindCacheForGroup(1, &record));
+ EXPECT_EQ(1, record.cache_id);
+ EXPECT_EQ(1, record.group_id);
+ EXPECT_TRUE(record.online_wildcard);
+ EXPECT_TRUE(kZeroTime == record.update_time);
+ EXPECT_EQ(100, record.cache_size);
+
+ EXPECT_TRUE(db.DeleteCache(1));
+ EXPECT_FALSE(db.FindCache(1, &record));
+ EXPECT_FALSE(db.FindCacheForGroup(1, &record));
+
+ EXPECT_TRUE(db.DeleteCache(1));
+
+ ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
+}
+
+TEST(AppCacheDatabaseTest, GroupRecords) {
+ const base::FilePath kEmptyPath;
+ AppCacheDatabase db(kEmptyPath);
+ EXPECT_TRUE(db.LazyOpen(true));
+
+ sql::ScopedErrorIgnorer ignore_errors;
+ // TODO(shess): See EntryRecords test.
+ ignore_errors.IgnoreError(SQLITE_CONSTRAINT);
+
+ const GURL kManifestUrl("http://blah/manifest");
+ const GURL kOrigin(kManifestUrl.GetOrigin());
+ const base::Time kLastAccessTime = base::Time::Now();
+ const base::Time kCreationTime =
+ kLastAccessTime - base::TimeDelta::FromDays(7);
+
+ const AppCacheDatabase::GroupRecord kZeroRecord;
+ AppCacheDatabase::GroupRecord record;
+ std::vector<AppCacheDatabase::GroupRecord> records;
+
+ // Behavior with an empty table
+ EXPECT_FALSE(db.FindGroup(1, &record));
+ EXPECT_FALSE(db.FindGroupForManifestUrl(kManifestUrl, &record));
+ EXPECT_TRUE(db.DeleteGroup(1));
+ EXPECT_TRUE(db.FindGroupsForOrigin(kOrigin, &records));
+ EXPECT_TRUE(records.empty());
+ EXPECT_FALSE(db.FindGroupForCache(1, &record));
+
+ record.group_id = 1;
+ record.manifest_url = kManifestUrl;
+ record.origin = kOrigin;
+ record.last_access_time = kLastAccessTime;
+ record.creation_time = kCreationTime;
+ EXPECT_TRUE(db.InsertGroup(&record));
+ EXPECT_FALSE(db.InsertGroup(&record));
+
+ record.group_id = 2;
+ EXPECT_FALSE(db.InsertGroup(&record));
+
+ record = kZeroRecord;
+ EXPECT_TRUE(db.FindGroup(1, &record));
+ EXPECT_EQ(1, record.group_id);
+ EXPECT_EQ(kManifestUrl, record.manifest_url);
+ EXPECT_EQ(kOrigin, record.origin);
+ EXPECT_EQ(kCreationTime.ToInternalValue(),
+ record.creation_time.ToInternalValue());
+ EXPECT_EQ(kLastAccessTime.ToInternalValue(),
+ record.last_access_time.ToInternalValue());
+
+ record = kZeroRecord;
+ EXPECT_TRUE(db.FindGroupForManifestUrl(kManifestUrl, &record));
+ EXPECT_EQ(1, record.group_id);
+ EXPECT_EQ(kManifestUrl, record.manifest_url);
+ EXPECT_EQ(kOrigin, record.origin);
+ EXPECT_EQ(kCreationTime.ToInternalValue(),
+ record.creation_time.ToInternalValue());
+ EXPECT_EQ(kLastAccessTime.ToInternalValue(),
+ record.last_access_time.ToInternalValue());
+
+ record.group_id = 2;
+ record.manifest_url = kOrigin;
+ record.origin = kOrigin;
+ record.last_access_time = kLastAccessTime;
+ record.creation_time = kCreationTime;
+ EXPECT_TRUE(db.InsertGroup(&record));
+
+ record = kZeroRecord;
+ EXPECT_TRUE(db.FindGroupForManifestUrl(kOrigin, &record));
+ EXPECT_EQ(2, record.group_id);
+ EXPECT_EQ(kOrigin, record.manifest_url);
+ EXPECT_EQ(kOrigin, record.origin);
+ EXPECT_EQ(kCreationTime.ToInternalValue(),
+ record.creation_time.ToInternalValue());
+ EXPECT_EQ(kLastAccessTime.ToInternalValue(),
+ record.last_access_time.ToInternalValue());
+
+ EXPECT_TRUE(db.FindGroupsForOrigin(kOrigin, &records));
+ EXPECT_EQ(2U, records.size());
+ EXPECT_EQ(1, records[0].group_id);
+ EXPECT_EQ(kManifestUrl, records[0].manifest_url);
+ EXPECT_EQ(kOrigin, records[0].origin);
+ EXPECT_EQ(2, records[1].group_id);
+ EXPECT_EQ(kOrigin, records[1].manifest_url);
+ EXPECT_EQ(kOrigin, records[1].origin);
+
+ EXPECT_TRUE(db.DeleteGroup(1));
+
+ records.clear();
+ EXPECT_TRUE(db.FindGroupsForOrigin(kOrigin, &records));
+ EXPECT_EQ(1U, records.size());
+ EXPECT_EQ(2, records[0].group_id);
+ EXPECT_EQ(kOrigin, records[0].manifest_url);
+ EXPECT_EQ(kOrigin, records[0].origin);
+ EXPECT_EQ(kCreationTime.ToInternalValue(),
+ record.creation_time.ToInternalValue());
+ EXPECT_EQ(kLastAccessTime.ToInternalValue(),
+ record.last_access_time.ToInternalValue());
+
+ std::set<GURL> origins;
+ EXPECT_TRUE(db.FindOriginsWithGroups(&origins));
+ EXPECT_EQ(1U, origins.size());
+ EXPECT_EQ(kOrigin, *(origins.begin()));
+
+ const GURL kManifest2("http://blah2/manifest");
+ const GURL kOrigin2(kManifest2.GetOrigin());
+ record.group_id = 1;
+ record.manifest_url = kManifest2;
+ record.origin = kOrigin2;
+ EXPECT_TRUE(db.InsertGroup(&record));
+
+ origins.clear();
+ EXPECT_TRUE(db.FindOriginsWithGroups(&origins));
+ EXPECT_EQ(2U, origins.size());
+ EXPECT_TRUE(origins.end() != origins.find(kOrigin));
+ EXPECT_TRUE(origins.end() != origins.find(kOrigin2));
+
+ AppCacheDatabase::CacheRecord cache_record;
+ cache_record.cache_id = 1;
+ cache_record.group_id = 1;
+ cache_record.online_wildcard = true;
+ cache_record.update_time = kZeroTime;
+ EXPECT_TRUE(db.InsertCache(&cache_record));
+
+ record = kZeroRecord;
+ EXPECT_TRUE(db.FindGroupForCache(1, &record));
+ EXPECT_EQ(1, record.group_id);
+ EXPECT_EQ(kManifest2, record.manifest_url);
+ EXPECT_EQ(kOrigin2, record.origin);
+
+ ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
+}
+
+TEST(AppCacheDatabaseTest, NamespaceRecords) {
+ const base::FilePath kEmptyPath;
+ AppCacheDatabase db(kEmptyPath);
+ EXPECT_TRUE(db.LazyOpen(true));
+
+ sql::ScopedErrorIgnorer ignore_errors;
+ // TODO(shess): See EntryRecords test.
+ ignore_errors.IgnoreError(SQLITE_CONSTRAINT);
+
+ const GURL kFooNameSpace1("http://foo/namespace1");
+ const GURL kFooNameSpace2("http://foo/namespace2");
+ const GURL kFooFallbackEntry("http://foo/entry");
+ const GURL kFooOrigin(kFooNameSpace1.GetOrigin());
+ const GURL kBarNameSpace1("http://bar/namespace1");
+ const GURL kBarNameSpace2("http://bar/namespace2");
+ const GURL kBarFallbackEntry("http://bar/entry");
+ const GURL kBarOrigin(kBarNameSpace1.GetOrigin());
+
+ const AppCacheDatabase::NamespaceRecord kZeroRecord;
+ AppCacheDatabase::NamespaceRecord record;
+ std::vector<AppCacheDatabase::NamespaceRecord> intercepts;
+ std::vector<AppCacheDatabase::NamespaceRecord> fallbacks;
+
+ // Behavior with an empty table
+ EXPECT_TRUE(db.FindNamespacesForCache(1, &intercepts, &fallbacks));
+ EXPECT_TRUE(fallbacks.empty());
+ EXPECT_TRUE(db.FindNamespacesForOrigin(kFooOrigin, &intercepts, &fallbacks));
+ EXPECT_TRUE(fallbacks.empty());
+ EXPECT_TRUE(db.DeleteNamespacesForCache(1));
+
+ // Two records for two differenent caches in the Foo origin.
+ record.cache_id = 1;
+ record.origin = kFooOrigin;
+ record.namespace_.namespace_url = kFooNameSpace1;
+ record.namespace_.target_url = kFooFallbackEntry;
+ EXPECT_TRUE(db.InsertNamespace(&record));
+ EXPECT_FALSE(db.InsertNamespace(&record));
+
+ record.cache_id = 2;
+ record.origin = kFooOrigin;
+ record.namespace_.namespace_url = kFooNameSpace2;
+ record.namespace_.target_url = kFooFallbackEntry;
+ EXPECT_TRUE(db.InsertNamespace(&record));
+
+ fallbacks.clear();
+ EXPECT_TRUE(db.FindNamespacesForCache(1, &intercepts, &fallbacks));
+ EXPECT_EQ(1U, fallbacks.size());
+ EXPECT_EQ(1, fallbacks[0].cache_id);
+ EXPECT_EQ(kFooOrigin, fallbacks[0].origin);
+ EXPECT_EQ(kFooNameSpace1, fallbacks[0].namespace_.namespace_url);
+ EXPECT_EQ(kFooFallbackEntry, fallbacks[0].namespace_.target_url);
+ EXPECT_FALSE(fallbacks[0].namespace_.is_pattern);
+
+ fallbacks.clear();
+ EXPECT_TRUE(db.FindNamespacesForCache(2, &intercepts, &fallbacks));
+ EXPECT_EQ(1U, fallbacks.size());
+ EXPECT_EQ(2, fallbacks[0].cache_id);
+ EXPECT_EQ(kFooOrigin, fallbacks[0].origin);
+ EXPECT_EQ(kFooNameSpace2, fallbacks[0].namespace_.namespace_url);
+ EXPECT_EQ(kFooFallbackEntry, fallbacks[0].namespace_.target_url);
+ EXPECT_FALSE(fallbacks[0].namespace_.is_pattern);
+
+ fallbacks.clear();
+ EXPECT_TRUE(db.FindNamespacesForOrigin(kFooOrigin, &intercepts, &fallbacks));
+ EXPECT_EQ(2U, fallbacks.size());
+ EXPECT_EQ(1, fallbacks[0].cache_id);
+ EXPECT_EQ(kFooOrigin, fallbacks[0].origin);
+ EXPECT_EQ(kFooNameSpace1, fallbacks[0].namespace_.namespace_url);
+ EXPECT_EQ(kFooFallbackEntry, fallbacks[0].namespace_.target_url);
+ EXPECT_FALSE(fallbacks[0].namespace_.is_pattern);
+ EXPECT_EQ(2, fallbacks[1].cache_id);
+ EXPECT_EQ(kFooOrigin, fallbacks[1].origin);
+ EXPECT_EQ(kFooNameSpace2, fallbacks[1].namespace_.namespace_url);
+ EXPECT_EQ(kFooFallbackEntry, fallbacks[1].namespace_.target_url);
+ EXPECT_FALSE(fallbacks[1].namespace_.is_pattern);
+
+ EXPECT_TRUE(db.DeleteNamespacesForCache(1));
+ fallbacks.clear();
+ EXPECT_TRUE(db.FindNamespacesForOrigin(kFooOrigin, &intercepts, &fallbacks));
+ EXPECT_EQ(1U, fallbacks.size());
+ EXPECT_EQ(2, fallbacks[0].cache_id);
+ EXPECT_EQ(kFooOrigin, fallbacks[0].origin);
+ EXPECT_EQ(kFooNameSpace2, fallbacks[0].namespace_.namespace_url);
+ EXPECT_EQ(kFooFallbackEntry, fallbacks[0].namespace_.target_url);
+ EXPECT_FALSE(fallbacks[0].namespace_.is_pattern);
+
+ // Two more records for the same cache in the Bar origin.
+ record.cache_id = 3;
+ record.origin = kBarOrigin;
+ record.namespace_.namespace_url = kBarNameSpace1;
+ record.namespace_.target_url = kBarFallbackEntry;
+ record.namespace_.is_pattern = true;
+ EXPECT_TRUE(db.InsertNamespace(&record));
+
+ record.cache_id = 3;
+ record.origin = kBarOrigin;
+ record.namespace_.namespace_url = kBarNameSpace2;
+ record.namespace_.target_url = kBarFallbackEntry;
+ record.namespace_.is_pattern = true;
+ EXPECT_TRUE(db.InsertNamespace(&record));
+
+ fallbacks.clear();
+ EXPECT_TRUE(db.FindNamespacesForCache(3, &intercepts, &fallbacks));
+ EXPECT_EQ(2U, fallbacks.size());
+ EXPECT_TRUE(fallbacks[0].namespace_.is_pattern);
+ EXPECT_TRUE(fallbacks[1].namespace_.is_pattern);
+
+ fallbacks.clear();
+ EXPECT_TRUE(db.FindNamespacesForOrigin(kBarOrigin, &intercepts, &fallbacks));
+ EXPECT_EQ(2U, fallbacks.size());
+ EXPECT_TRUE(fallbacks[0].namespace_.is_pattern);
+ EXPECT_TRUE(fallbacks[1].namespace_.is_pattern);
+
+ ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
+}
+
+TEST(AppCacheDatabaseTest, OnlineWhiteListRecords) {
+ const base::FilePath kEmptyPath;
+ AppCacheDatabase db(kEmptyPath);
+ EXPECT_TRUE(db.LazyOpen(true));
+
+ const GURL kFooNameSpace1("http://foo/namespace1");
+ const GURL kFooNameSpace2("http://foo/namespace2");
+ const GURL kBarNameSpace1("http://bar/namespace1");
+
+ const AppCacheDatabase::OnlineWhiteListRecord kZeroRecord;
+ AppCacheDatabase::OnlineWhiteListRecord record;
+ std::vector<AppCacheDatabase::OnlineWhiteListRecord> records;
+
+ // Behavior with an empty table
+ EXPECT_TRUE(db.FindOnlineWhiteListForCache(1, &records));
+ EXPECT_TRUE(records.empty());
+ EXPECT_TRUE(db.DeleteOnlineWhiteListForCache(1));
+
+ record.cache_id = 1;
+ record.namespace_url = kFooNameSpace1;
+ EXPECT_TRUE(db.InsertOnlineWhiteList(&record));
+ record.namespace_url = kFooNameSpace2;
+ record.is_pattern = true;
+ EXPECT_TRUE(db.InsertOnlineWhiteList(&record));
+ records.clear();
+ EXPECT_TRUE(db.FindOnlineWhiteListForCache(1, &records));
+ EXPECT_EQ(2U, records.size());
+ EXPECT_EQ(1, records[0].cache_id);
+ EXPECT_EQ(kFooNameSpace1, records[0].namespace_url);
+ EXPECT_FALSE(records[0].is_pattern);
+ EXPECT_EQ(1, records[1].cache_id);
+ EXPECT_EQ(kFooNameSpace2, records[1].namespace_url);
+ EXPECT_TRUE(records[1].is_pattern);
+
+ record.cache_id = 2;
+ record.namespace_url = kBarNameSpace1;
+ EXPECT_TRUE(db.InsertOnlineWhiteList(&record));
+ records.clear();
+ EXPECT_TRUE(db.FindOnlineWhiteListForCache(2, &records));
+ EXPECT_EQ(1U, records.size());
+
+ EXPECT_TRUE(db.DeleteOnlineWhiteListForCache(1));
+ records.clear();
+ EXPECT_TRUE(db.FindOnlineWhiteListForCache(1, &records));
+ EXPECT_TRUE(records.empty());
+}
+
+TEST(AppCacheDatabaseTest, DeletableResponseIds) {
+ const base::FilePath kEmptyPath;
+ AppCacheDatabase db(kEmptyPath);
+ EXPECT_TRUE(db.LazyOpen(true));
+
+ sql::ScopedErrorIgnorer ignore_errors;
+ // TODO(shess): See EntryRecords test.
+ ignore_errors.IgnoreError(SQLITE_CONSTRAINT);
+
+ std::vector<int64> ids;
+
+ EXPECT_TRUE(db.GetDeletableResponseIds(&ids, kint64max, 100));
+ EXPECT_TRUE(ids.empty());
+ ids.push_back(0);
+ EXPECT_TRUE(db.DeleteDeletableResponseIds(ids));
+ EXPECT_TRUE(db.InsertDeletableResponseIds(ids));
+
+ ids.clear();
+ EXPECT_TRUE(db.GetDeletableResponseIds(&ids, kint64max, 100));
+ EXPECT_EQ(1U, ids.size());
+ EXPECT_EQ(0, ids[0]);
+
+ int64 unused, deleteable_response_rowid;
+ unused = deleteable_response_rowid = 0;
+ EXPECT_TRUE(db.FindLastStorageIds(&unused, &unused, &unused,
+ &deleteable_response_rowid));
+ EXPECT_EQ(1, deleteable_response_rowid);
+
+
+ // Expected to fail due to the duplicate id, 0 is already in the table.
+ ids.clear();
+ ids.push_back(0);
+ ids.push_back(1);
+ EXPECT_FALSE(db.InsertDeletableResponseIds(ids));
+
+ ids.clear();
+ for (int i = 1; i < 10; ++i)
+ ids.push_back(i);
+ EXPECT_TRUE(db.InsertDeletableResponseIds(ids));
+ EXPECT_TRUE(db.FindLastStorageIds(&unused, &unused, &unused,
+ &deleteable_response_rowid));
+ EXPECT_EQ(10, deleteable_response_rowid);
+
+ ids.clear();
+ EXPECT_TRUE(db.GetDeletableResponseIds(&ids, kint64max, 100));
+ EXPECT_EQ(10U, ids.size());
+ for (int i = 0; i < 10; ++i)
+ EXPECT_EQ(i, ids[i]);
+
+ // Ensure the limit is respected.
+ ids.clear();
+ EXPECT_TRUE(db.GetDeletableResponseIds(&ids, kint64max, 5));
+ EXPECT_EQ(5U, ids.size());
+ for (int i = 0; i < static_cast<int>(ids.size()); ++i)
+ EXPECT_EQ(i, ids[i]);
+
+ // Ensure the max_rowid is respected (the first rowid is 1).
+ ids.clear();
+ EXPECT_TRUE(db.GetDeletableResponseIds(&ids, 5, 100));
+ EXPECT_EQ(5U, ids.size());
+ for (int i = 0; i < static_cast<int>(ids.size()); ++i)
+ EXPECT_EQ(i, ids[i]);
+
+ // Ensure that we can delete from the table.
+ EXPECT_TRUE(db.DeleteDeletableResponseIds(ids));
+ ids.clear();
+ EXPECT_TRUE(db.GetDeletableResponseIds(&ids, kint64max, 100));
+ EXPECT_EQ(5U, ids.size());
+ for (int i = 0; i < static_cast<int>(ids.size()); ++i)
+ EXPECT_EQ(i + 5, ids[i]);
+
+ ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
+}
+
+TEST(AppCacheDatabaseTest, OriginUsage) {
+ const GURL kManifestUrl("http://blah/manifest");
+ const GURL kManifestUrl2("http://blah/manifest2");
+ const GURL kOrigin(kManifestUrl.GetOrigin());
+ const GURL kOtherOriginManifestUrl("http://other/manifest");
+ const GURL kOtherOrigin(kOtherOriginManifestUrl.GetOrigin());
+
+ const base::FilePath kEmptyPath;
+ AppCacheDatabase db(kEmptyPath);
+ EXPECT_TRUE(db.LazyOpen(true));
+
+ std::vector<AppCacheDatabase::CacheRecord> cache_records;
+ EXPECT_EQ(0, db.GetOriginUsage(kOrigin));
+ EXPECT_TRUE(db.FindCachesForOrigin(kOrigin, &cache_records));
+ EXPECT_TRUE(cache_records.empty());
+
+ AppCacheDatabase::GroupRecord group_record;
+ group_record.group_id = 1;
+ group_record.manifest_url = kManifestUrl;
+ group_record.origin = kOrigin;
+ EXPECT_TRUE(db.InsertGroup(&group_record));
+ AppCacheDatabase::CacheRecord cache_record;
+ cache_record.cache_id = 1;
+ cache_record.group_id = 1;
+ cache_record.online_wildcard = true;
+ cache_record.update_time = kZeroTime;
+ cache_record.cache_size = 100;
+ EXPECT_TRUE(db.InsertCache(&cache_record));
+
+ EXPECT_EQ(100, db.GetOriginUsage(kOrigin));
+
+ group_record.group_id = 2;
+ group_record.manifest_url = kManifestUrl2;
+ group_record.origin = kOrigin;
+ EXPECT_TRUE(db.InsertGroup(&group_record));
+ cache_record.cache_id = 2;
+ cache_record.group_id = 2;
+ cache_record.online_wildcard = true;
+ cache_record.update_time = kZeroTime;
+ cache_record.cache_size = 1000;
+ EXPECT_TRUE(db.InsertCache(&cache_record));
+
+ EXPECT_EQ(1100, db.GetOriginUsage(kOrigin));
+
+ group_record.group_id = 3;
+ group_record.manifest_url = kOtherOriginManifestUrl;
+ group_record.origin = kOtherOrigin;
+ EXPECT_TRUE(db.InsertGroup(&group_record));
+ cache_record.cache_id = 3;
+ cache_record.group_id = 3;
+ cache_record.online_wildcard = true;
+ cache_record.update_time = kZeroTime;
+ cache_record.cache_size = 5000;
+ EXPECT_TRUE(db.InsertCache(&cache_record));
+
+ EXPECT_EQ(5000, db.GetOriginUsage(kOtherOrigin));
+
+ EXPECT_TRUE(db.FindCachesForOrigin(kOrigin, &cache_records));
+ EXPECT_EQ(2U, cache_records.size());
+ cache_records.clear();
+ EXPECT_TRUE(db.FindCachesForOrigin(kOtherOrigin, &cache_records));
+ EXPECT_EQ(1U, cache_records.size());
+
+ std::map<GURL, int64> usage_map;
+ EXPECT_TRUE(db.GetAllOriginUsage(&usage_map));
+ EXPECT_EQ(2U, usage_map.size());
+ EXPECT_EQ(1100, usage_map[kOrigin]);
+ EXPECT_EQ(5000, usage_map[kOtherOrigin]);
+}
+
+#if defined(APPCACHE_USE_SIMPLE_CACHE)
+// There is no such upgrade path in this case.
+#else
+TEST(AppCacheDatabaseTest, UpgradeSchema3to5) {
+ // Real file on disk for this test.
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ const base::FilePath kDbFile = temp_dir.path().AppendASCII("upgrade3.db");
+
+ const GURL kMockOrigin("http://mockorigin/");
+ const char kNamespaceUrlFormat[] = "namespace%d";
+ const char kTargetUrlFormat[] = "target%d";
+ const int kNumNamespaces = 10;
+
+ // Create a v3 schema based database containing some fallback records.
+ {
+ const int kVersion3 = 3;
+ const char kGroupsTable[] = "Groups";
+ const char kCachesTable[] = "Caches";
+ const char kEntriesTable[] = "Entries";
+ const char kFallbackNameSpacesTable[] = "FallbackNameSpaces";
+ const char kOnlineWhiteListsTable[] = "OnlineWhiteLists";
+ const char kDeletableResponseIdsTable[] = "DeletableResponseIds";
+
+ const struct {
+ const char* table_name;
+ const char* columns;
+ } kTables3[] = {
+ { kGroupsTable,
+ "(group_id INTEGER PRIMARY KEY,"
+ " origin TEXT,"
+ " manifest_url TEXT,"
+ " creation_time INTEGER,"
+ " last_access_time INTEGER)" },
+
+ { kCachesTable,
+ "(cache_id INTEGER PRIMARY KEY,"
+ " group_id INTEGER,"
+ " online_wildcard INTEGER CHECK(online_wildcard IN (0, 1)),"
+ " update_time INTEGER,"
+ " cache_size INTEGER)" }, // intentionally not normalized
+
+ { kEntriesTable,
+ "(cache_id INTEGER,"
+ " url TEXT,"
+ " flags INTEGER,"
+ " response_id INTEGER,"
+ " response_size INTEGER)" },
+
+ { kFallbackNameSpacesTable,
+ "(cache_id INTEGER,"
+ " origin TEXT," // intentionally not normalized
+ " namespace_url TEXT,"
+ " fallback_entry_url TEXT)" },
+
+ { kOnlineWhiteListsTable,
+ "(cache_id INTEGER,"
+ " namespace_url TEXT)" },
+
+ { kDeletableResponseIdsTable,
+ "(response_id INTEGER NOT NULL)" },
+ };
+
+ const struct {
+ const char* index_name;
+ const char* table_name;
+ const char* columns;
+ bool unique;
+ } kIndexes3[] = {
+ { "GroupsOriginIndex",
+ kGroupsTable,
+ "(origin)",
+ false },
+
+ { "GroupsManifestIndex",
+ kGroupsTable,
+ "(manifest_url)",
+ true },
+
+ { "CachesGroupIndex",
+ kCachesTable,
+ "(group_id)",
+ false },
+
+ { "EntriesCacheIndex",
+ kEntriesTable,
+ "(cache_id)",
+ false },
+
+ { "EntriesCacheAndUrlIndex",
+ kEntriesTable,
+ "(cache_id, url)",
+ true },
+
+ { "EntriesResponseIdIndex",
+ kEntriesTable,
+ "(response_id)",
+ true },
+
+ { "FallbackNameSpacesCacheIndex",
+ kFallbackNameSpacesTable,
+ "(cache_id)",
+ false },
+
+ { "FallbackNameSpacesOriginIndex",
+ kFallbackNameSpacesTable,
+ "(origin)",
+ false },
+
+ { "FallbackNameSpacesCacheAndUrlIndex",
+ kFallbackNameSpacesTable,
+ "(cache_id, namespace_url)",
+ true },
+
+ { "OnlineWhiteListCacheIndex",
+ kOnlineWhiteListsTable,
+ "(cache_id)",
+ false },
+
+ { "DeletableResponsesIdIndex",
+ kDeletableResponseIdsTable,
+ "(response_id)",
+ true },
+ };
+
+ const int kTableCount3 = ARRAYSIZE_UNSAFE(kTables3);
+ const int kIndexCount3 = ARRAYSIZE_UNSAFE(kIndexes3);
+
+ sql::Connection connection;
+ EXPECT_TRUE(connection.Open(kDbFile));
+
+ sql::Transaction transaction(&connection);
+ EXPECT_TRUE(transaction.Begin());
+
+ sql::MetaTable meta_table;
+ EXPECT_TRUE(meta_table.Init(&connection, kVersion3, kVersion3));
+
+ for (int i = 0; i < kTableCount3; ++i) {
+ std::string sql("CREATE TABLE ");
+ sql += kTables3[i].table_name;
+ sql += kTables3[i].columns;
+ EXPECT_TRUE(connection.Execute(sql.c_str()));
+ }
+
+ for (int i = 0; i < kIndexCount3; ++i) {
+ std::string sql;
+ if (kIndexes3[i].unique)
+ sql += "CREATE UNIQUE INDEX ";
+ else
+ sql += "CREATE INDEX ";
+ sql += kIndexes3[i].index_name;
+ sql += " ON ";
+ sql += kIndexes3[i].table_name;
+ sql += kIndexes3[i].columns;
+ EXPECT_TRUE(connection.Execute(sql.c_str()));
+ }
+
+ const char* kSql =
+ "INSERT INTO FallbackNameSpaces"
+ " (cache_id, origin, namespace_url, fallback_entry_url)"
+ " VALUES (?, ?, ?, ?)";
+
+ sql::Statement statement;
+ statement.Assign(connection.GetUniqueStatement(kSql));
+ EXPECT_TRUE(statement.is_valid());
+ for (int i = 0; i < kNumNamespaces; ++i) {
+ GURL namespace_url(
+ kMockOrigin.Resolve(base::StringPrintf(kNamespaceUrlFormat, i)));
+ GURL target_url(
+ kMockOrigin.Resolve(base::StringPrintf(kTargetUrlFormat, i)));
+ statement.BindInt64(0, i);
+ statement.BindString(1, kMockOrigin.spec().c_str());
+ statement.BindString(2, namespace_url.spec().c_str());
+ statement.BindString(3, target_url.spec().c_str());
+ ASSERT_TRUE(statement.Run());
+ statement.Reset(true);
+ }
+
+ EXPECT_TRUE(transaction.Commit());
+ }
+
+ // Open that database and verify that it got updated.
+ AppCacheDatabase db(kDbFile);
+ EXPECT_TRUE(db.LazyOpen(true));
+
+ EXPECT_FALSE(db.db_->DoesTableExist("FallbackNameSpaces"));
+ EXPECT_FALSE(db.db_->DoesIndexExist("FallbackNamesSpacesCacheIndex"));
+ EXPECT_FALSE(db.db_->DoesIndexExist("FallbackNameSpacesOriginIndex"));
+ EXPECT_FALSE(db.db_->DoesIndexExist("FallbackNameSpacesCacheAndUrlIndex"));
+
+ EXPECT_TRUE(db.db_->DoesTableExist("Namespaces"));
+ EXPECT_TRUE(db.db_->DoesIndexExist("NamespacesCacheIndex"));
+ EXPECT_TRUE(db.db_->DoesIndexExist("NamespacesOriginIndex"));
+ EXPECT_TRUE(db.db_->DoesIndexExist("NamespacesCacheAndUrlIndex"));
+ EXPECT_TRUE(db.db_->DoesColumnExist("Namespaces", "is_pattern"));
+ EXPECT_TRUE(db.db_->DoesColumnExist("OnlineWhiteLists", "is_pattern"));
+
+ EXPECT_EQ(5, db.meta_table_->GetVersionNumber());
+ EXPECT_EQ(5, db.meta_table_->GetCompatibleVersionNumber());
+
+ std::vector<AppCacheDatabase::NamespaceRecord> intercepts;
+ std::vector<AppCacheDatabase::NamespaceRecord> fallbacks;
+ EXPECT_TRUE(db.FindNamespacesForOrigin(kMockOrigin, &intercepts,
+ &fallbacks));
+ EXPECT_TRUE(intercepts.empty());
+ EXPECT_EQ(kNumNamespaces, static_cast<int>(fallbacks.size()));
+
+ for (int i = 0; i < kNumNamespaces; ++i) {
+ GURL expected_namespace_url(
+ kMockOrigin.Resolve(base::StringPrintf(kNamespaceUrlFormat, i)));
+ GURL expected_target_url(
+ kMockOrigin.Resolve(base::StringPrintf(kTargetUrlFormat, i)));
+
+ EXPECT_EQ(i, fallbacks[i].cache_id);
+ EXPECT_EQ(APPCACHE_FALLBACK_NAMESPACE, fallbacks[i].namespace_.type);
+ EXPECT_EQ(kMockOrigin, fallbacks[i].origin);
+ EXPECT_EQ(expected_namespace_url, fallbacks[i].namespace_.namespace_url);
+ EXPECT_EQ(expected_target_url, fallbacks[i].namespace_.target_url);
+ EXPECT_FALSE(fallbacks[i].namespace_.is_pattern);
+ }
+}
+#endif // !APPCACHE_USE_SIMPLE_CACHE
+
+#if defined(APPCACHE_USE_SIMPLE_CACHE)
+// There is no such upgrade path in this case.
+#else
+TEST(AppCacheDatabaseTest, UpgradeSchema4to5) {
+ // Real file on disk for this test.
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ const base::FilePath kDbFile = temp_dir.path().AppendASCII("upgrade4.db");
+
+ const GURL kMockOrigin("http://mockorigin/");
+ const char kNamespaceUrlFormat[] = "namespace%d";
+ const char kWhitelistUrlFormat[] = "whitelist%d";
+ const char kTargetUrlFormat[] = "target%d";
+ const int kNumNamespaces = 10;
+ const int kWhitelistCacheId = 1;
+
+ // Create a v4 schema based database containing some fallback records.
+ {
+ const int kVersion4 = 4;
+ const char kGroupsTable[] = "Groups";
+ const char kCachesTable[] = "Caches";
+ const char kEntriesTable[] = "Entries";
+ const char kNamespacesTable[] = "Namespaces";
+ const char kOnlineWhiteListsTable[] = "OnlineWhiteLists";
+ const char kDeletableResponseIdsTable[] = "DeletableResponseIds";
+
+ struct TableInfo {
+ const char* table_name;
+ const char* columns;
+ };
+
+ struct IndexInfo {
+ const char* index_name;
+ const char* table_name;
+ const char* columns;
+ bool unique;
+ };
+
+ const TableInfo kTables4[] = {
+ { kGroupsTable,
+ "(group_id INTEGER PRIMARY KEY,"
+ " origin TEXT,"
+ " manifest_url TEXT,"
+ " creation_time INTEGER,"
+ " last_access_time INTEGER)" },
+
+ { kCachesTable,
+ "(cache_id INTEGER PRIMARY KEY,"
+ " group_id INTEGER,"
+ " online_wildcard INTEGER CHECK(online_wildcard IN (0, 1)),"
+ " update_time INTEGER,"
+ " cache_size INTEGER)" }, // intentionally not normalized
+
+ { kEntriesTable,
+ "(cache_id INTEGER,"
+ " url TEXT,"
+ " flags INTEGER,"
+ " response_id INTEGER,"
+ " response_size INTEGER)" },
+
+ { kNamespacesTable,
+ "(cache_id INTEGER,"
+ " origin TEXT," // intentionally not normalized
+ " type INTEGER,"
+ " namespace_url TEXT,"
+ " target_url TEXT)" },
+
+ { kOnlineWhiteListsTable,
+ "(cache_id INTEGER,"
+ " namespace_url TEXT)" },
+
+ { kDeletableResponseIdsTable,
+ "(response_id INTEGER NOT NULL)" },
+ };
+
+ const IndexInfo kIndexes4[] = {
+ { "GroupsOriginIndex",
+ kGroupsTable,
+ "(origin)",
+ false },
+
+ { "GroupsManifestIndex",
+ kGroupsTable,
+ "(manifest_url)",
+ true },
+
+ { "CachesGroupIndex",
+ kCachesTable,
+ "(group_id)",
+ false },
+
+ { "EntriesCacheIndex",
+ kEntriesTable,
+ "(cache_id)",
+ false },
+
+ { "EntriesCacheAndUrlIndex",
+ kEntriesTable,
+ "(cache_id, url)",
+ true },
+
+ { "EntriesResponseIdIndex",
+ kEntriesTable,
+ "(response_id)",
+ true },
+
+ { "NamespacesCacheIndex",
+ kNamespacesTable,
+ "(cache_id)",
+ false },
+
+ { "NamespacesOriginIndex",
+ kNamespacesTable,
+ "(origin)",
+ false },
+
+ { "NamespacesCacheAndUrlIndex",
+ kNamespacesTable,
+ "(cache_id, namespace_url)",
+ true },
+
+ { "OnlineWhiteListCacheIndex",
+ kOnlineWhiteListsTable,
+ "(cache_id)",
+ false },
+
+ { "DeletableResponsesIdIndex",
+ kDeletableResponseIdsTable,
+ "(response_id)",
+ true },
+ };
+
+ const int kTableCount4 = ARRAYSIZE_UNSAFE(kTables4);
+ const int kIndexCount4 = ARRAYSIZE_UNSAFE(kIndexes4);
+
+ sql::Connection connection;
+ EXPECT_TRUE(connection.Open(kDbFile));
+
+ sql::Transaction transaction(&connection);
+ EXPECT_TRUE(transaction.Begin());
+
+ sql::MetaTable meta_table;
+ EXPECT_TRUE(meta_table.Init(&connection, kVersion4, kVersion4));
+
+ for (int i = 0; i < kTableCount4; ++i) {
+ std::string sql("CREATE TABLE ");
+ sql += kTables4[i].table_name;
+ sql += kTables4[i].columns;
+ EXPECT_TRUE(connection.Execute(sql.c_str()));
+ }
+
+ for (int i = 0; i < kIndexCount4; ++i) {
+ std::string sql;
+ if (kIndexes4[i].unique)
+ sql += "CREATE UNIQUE INDEX ";
+ else
+ sql += "CREATE INDEX ";
+ sql += kIndexes4[i].index_name;
+ sql += " ON ";
+ sql += kIndexes4[i].table_name;
+ sql += kIndexes4[i].columns;
+ EXPECT_TRUE(connection.Execute(sql.c_str()));
+ }
+
+ const char* kNamespacesSql =
+ "INSERT INTO Namespaces"
+ " (cache_id, origin, type, namespace_url, target_url)"
+ " VALUES (?, ?, ?, ?, ?)";
+ sql::Statement statement;
+ statement.Assign(connection.GetUniqueStatement(kNamespacesSql));
+ EXPECT_TRUE(statement.is_valid());
+ for (int i = 0; i < kNumNamespaces; ++i) {
+ GURL namespace_url(
+ kMockOrigin.Resolve(base::StringPrintf(kNamespaceUrlFormat, i)));
+ GURL target_url(
+ kMockOrigin.Resolve(base::StringPrintf(kTargetUrlFormat, i)));
+ statement.BindInt64(0, i);
+ statement.BindString(1, kMockOrigin.spec().c_str());
+ statement.BindInt(2, APPCACHE_FALLBACK_NAMESPACE);
+ statement.BindString(3, namespace_url.spec().c_str());
+ statement.BindString(4, target_url.spec().c_str());
+ ASSERT_TRUE(statement.Run());
+ statement.Reset(true);
+ }
+
+ const char* kWhitelistsSql =
+ "INSERT INTO OnlineWhiteLists"
+ " (cache_id, namespace_url)"
+ " VALUES (?, ?)";
+ statement.Assign(connection.GetUniqueStatement(kWhitelistsSql));
+ EXPECT_TRUE(statement.is_valid());
+ for (int i = 0; i < kNumNamespaces; ++i) {
+ GURL namespace_url(
+ kMockOrigin.Resolve(base::StringPrintf(kWhitelistUrlFormat, i)));
+ statement.BindInt64(0, kWhitelistCacheId);
+ statement.BindString(1, namespace_url.spec().c_str());
+ ASSERT_TRUE(statement.Run());
+ statement.Reset(true);
+ }
+
+ EXPECT_TRUE(transaction.Commit());
+ }
+
+ // Open that database and verify that it got upgraded to v5.
+ AppCacheDatabase db(kDbFile);
+ EXPECT_TRUE(db.LazyOpen(true));
+ EXPECT_TRUE(db.db_->DoesColumnExist("Namespaces", "is_pattern"));
+ EXPECT_TRUE(db.db_->DoesColumnExist("OnlineWhiteLists", "is_pattern"));
+ EXPECT_EQ(5, db.meta_table_->GetVersionNumber());
+ EXPECT_EQ(5, db.meta_table_->GetCompatibleVersionNumber());
+
+ std::vector<AppCacheDatabase::NamespaceRecord> intercepts;
+ std::vector<AppCacheDatabase::NamespaceRecord> fallbacks;
+ EXPECT_TRUE(db.FindNamespacesForOrigin(kMockOrigin, &intercepts,
+ &fallbacks));
+ EXPECT_TRUE(intercepts.empty());
+ EXPECT_EQ(kNumNamespaces, static_cast<int>(fallbacks.size()));
+
+ std::vector<AppCacheDatabase::OnlineWhiteListRecord> whitelists;
+ EXPECT_TRUE(db.FindOnlineWhiteListForCache(kWhitelistCacheId, &whitelists));
+ EXPECT_EQ(kNumNamespaces, static_cast<int>(whitelists.size()));
+
+ for (int i = 0; i < kNumNamespaces; ++i) {
+ GURL expected_namespace_url(
+ kMockOrigin.Resolve(base::StringPrintf(kNamespaceUrlFormat, i)));
+ GURL expected_target_url(
+ kMockOrigin.Resolve(base::StringPrintf(kTargetUrlFormat, i)));
+ GURL expected_whitelist_url(
+ kMockOrigin.Resolve(base::StringPrintf(kWhitelistUrlFormat, i)));
+
+ EXPECT_EQ(i, fallbacks[i].cache_id);
+ EXPECT_EQ(APPCACHE_FALLBACK_NAMESPACE, fallbacks[i].namespace_.type);
+ EXPECT_EQ(kMockOrigin, fallbacks[i].origin);
+ EXPECT_EQ(expected_namespace_url, fallbacks[i].namespace_.namespace_url);
+ EXPECT_EQ(expected_target_url, fallbacks[i].namespace_.target_url);
+ EXPECT_FALSE(fallbacks[i].namespace_.is_pattern);
+ EXPECT_EQ(expected_whitelist_url, whitelists[i].namespace_url);
+ EXPECT_FALSE(whitelists[i].is_pattern);
+ }
+}
+#endif // !APPCACHE_USE_SIMPLE_CACHE
+
+} // namespace content
diff --git a/chromium/content/browser/appcache/appcache_disk_cache_unittest.cc b/chromium/content/browser/appcache/appcache_disk_cache_unittest.cc
new file mode 100644
index 00000000000..5e1e083ebdc
--- /dev/null
+++ b/chromium/content/browser/appcache/appcache_disk_cache_unittest.cc
@@ -0,0 +1,188 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/run_loop.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/appcache/appcache_disk_cache.h"
+
+using appcache::AppCacheDiskCache;
+
+namespace content {
+
+class AppCacheDiskCacheTest : public testing::Test {
+ public:
+ AppCacheDiskCacheTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ // Use the current thread for the DiskCache's cache_thread.
+ message_loop_.reset(new base::MessageLoopForIO());
+ cache_thread_ = base::MessageLoopProxy::current();
+ ASSERT_TRUE(directory_.CreateUniqueTempDir());
+ completion_callback_ = base::Bind(
+ &AppCacheDiskCacheTest::OnComplete,
+ base::Unretained(this));
+ }
+
+ virtual void TearDown() OVERRIDE {
+ base::RunLoop().RunUntilIdle();
+ message_loop_.reset(NULL);
+ }
+
+ void FlushCacheTasks() {
+ base::RunLoop().RunUntilIdle();
+ }
+
+ void OnComplete(int err) {
+ completion_results_.push_back(err);
+ }
+
+ base::ScopedTempDir directory_;
+ scoped_ptr<base::MessageLoop> message_loop_;
+ scoped_refptr<base::MessageLoopProxy> cache_thread_;
+ net::CompletionCallback completion_callback_;
+ std::vector<int> completion_results_;
+
+ static const int k10MBytes = 10 * 1024 * 1024;
+};
+
+TEST_F(AppCacheDiskCacheTest, DisablePriorToInitCompletion) {
+ AppCacheDiskCache::Entry* entry = NULL;
+
+ // Create an instance and start it initializing, queue up
+ // one of each kind of "entry" function.
+ scoped_ptr<AppCacheDiskCache> disk_cache(new AppCacheDiskCache);
+ EXPECT_FALSE(disk_cache->is_disabled());
+ disk_cache->InitWithDiskBackend(
+ directory_.path(), k10MBytes, false, cache_thread_,
+ completion_callback_);
+ disk_cache->CreateEntry(1, &entry, completion_callback_);
+ disk_cache->OpenEntry(2, &entry, completion_callback_);
+ disk_cache->DoomEntry(3, completion_callback_);
+
+ // Pull the plug on all that.
+ EXPECT_FALSE(disk_cache->is_disabled());
+ disk_cache->Disable();
+ EXPECT_TRUE(disk_cache->is_disabled());
+
+ FlushCacheTasks();
+
+ EXPECT_EQ(NULL, entry);
+ EXPECT_EQ(4u, completion_results_.size());
+ for (std::vector<int>::const_iterator iter = completion_results_.begin();
+ iter < completion_results_.end(); ++iter) {
+ EXPECT_EQ(net::ERR_ABORTED, *iter);
+ }
+
+ // Ensure the directory can be deleted at this point.
+ EXPECT_TRUE(base::DirectoryExists(directory_.path()));
+ EXPECT_FALSE(base::IsDirectoryEmpty(directory_.path()));
+ EXPECT_TRUE(base::DeleteFile(directory_.path(), true));
+ EXPECT_FALSE(base::DirectoryExists(directory_.path()));
+}
+
+TEST_F(AppCacheDiskCacheTest, DisableAfterInitted) {
+ // Create an instance and let it fully init.
+ scoped_ptr<AppCacheDiskCache> disk_cache(new AppCacheDiskCache);
+ EXPECT_FALSE(disk_cache->is_disabled());
+ disk_cache->InitWithDiskBackend(
+ directory_.path(), k10MBytes, false, cache_thread_,
+ completion_callback_);
+ FlushCacheTasks();
+ EXPECT_EQ(1u, completion_results_.size());
+ EXPECT_EQ(net::OK, completion_results_[0]);
+
+ // Pull the plug
+ disk_cache->Disable();
+ FlushCacheTasks();
+
+ // Ensure the directory can be deleted at this point.
+ EXPECT_TRUE(base::DirectoryExists(directory_.path()));
+ EXPECT_FALSE(base::IsDirectoryEmpty(directory_.path()));
+ EXPECT_TRUE(base::DeleteFile(directory_.path(), true));
+ EXPECT_FALSE(base::DirectoryExists(directory_.path()));
+
+ // Methods should return immediately when disabled and not invoke
+ // the callback at all.
+ AppCacheDiskCache::Entry* entry = NULL;
+ completion_results_.clear();
+ EXPECT_EQ(net::ERR_ABORTED,
+ disk_cache->CreateEntry(1, &entry, completion_callback_));
+ EXPECT_EQ(net::ERR_ABORTED,
+ disk_cache->OpenEntry(2, &entry, completion_callback_));
+ EXPECT_EQ(net::ERR_ABORTED,
+ disk_cache->DoomEntry(3, completion_callback_));
+ FlushCacheTasks();
+ EXPECT_TRUE(completion_results_.empty());
+}
+
+// Flaky on Android: http://crbug.com/339534
+TEST_F(AppCacheDiskCacheTest, DISABLED_DisableWithEntriesOpen) {
+ // Create an instance and let it fully init.
+ scoped_ptr<AppCacheDiskCache> disk_cache(new AppCacheDiskCache);
+ EXPECT_FALSE(disk_cache->is_disabled());
+ disk_cache->InitWithDiskBackend(
+ directory_.path(), k10MBytes, false, cache_thread_,
+ completion_callback_);
+ FlushCacheTasks();
+ EXPECT_EQ(1u, completion_results_.size());
+ EXPECT_EQ(net::OK, completion_results_[0]);
+
+ // Note: We don't have detailed expectations of the DiskCache
+ // operations because on android it's really SimpleCache which
+ // does behave differently.
+ //
+ // What matters for the corruption handling and service reinitiazation
+ // is that the directory can be deleted after the calling Disable() method,
+ // and we do have expectations about that.
+
+ // Create/open some entries.
+ AppCacheDiskCache::Entry* entry1 = NULL;
+ AppCacheDiskCache::Entry* entry2 = NULL;
+ disk_cache->CreateEntry(1, &entry1, completion_callback_);
+ disk_cache->CreateEntry(2, &entry2, completion_callback_);
+ FlushCacheTasks();
+ EXPECT_TRUE(entry1);
+ EXPECT_TRUE(entry2);
+
+ // Write something to one of the entries and flush it.
+ const char* kData = "Hello";
+ const int kDataLen = strlen(kData) + 1;
+ scoped_refptr<net::IOBuffer> write_buf(new net::WrappedIOBuffer(kData));
+ entry1->Write(0, 0, write_buf, kDataLen, completion_callback_);
+ FlushCacheTasks();
+
+ // Queue up a read and a write.
+ scoped_refptr<net::IOBuffer> read_buf = new net::IOBuffer(kDataLen);
+ entry1->Read(0, 0, read_buf.get(), kDataLen, completion_callback_);
+ entry2->Write(0, 0, write_buf.get(), kDataLen, completion_callback_);
+
+ // Pull the plug
+ disk_cache->Disable();
+ FlushCacheTasks();
+
+ // Ensure the directory can be deleted at this point.
+ EXPECT_TRUE(base::DirectoryExists(directory_.path()));
+ EXPECT_FALSE(base::IsDirectoryEmpty(directory_.path()));
+ EXPECT_TRUE(base::DeleteFile(directory_.path(), true));
+ EXPECT_FALSE(base::DirectoryExists(directory_.path()));
+
+ disk_cache.reset(NULL);
+
+ // Also, new IO operations should fail immediately.
+ EXPECT_EQ(
+ net::ERR_ABORTED,
+ entry1->Read(0, 0, read_buf.get(), kDataLen, completion_callback_));
+ entry1->Close();
+ entry2->Close();
+
+ FlushCacheTasks();
+}
+
+} // namespace content
diff --git a/chromium/content/browser/appcache/appcache_dispatcher_host.cc b/chromium/content/browser/appcache/appcache_dispatcher_host.cc
index 25e33cf209a..64e46b188a5 100644
--- a/chromium/content/browser/appcache/appcache_dispatcher_host.cc
+++ b/chromium/content/browser/appcache/appcache_dispatcher_host.cc
@@ -15,7 +15,8 @@ namespace content {
AppCacheDispatcherHost::AppCacheDispatcherHost(
ChromeAppCacheService* appcache_service,
int process_id)
- : appcache_service_(appcache_service),
+ : BrowserMessageFilter(AppCacheMsgStart),
+ appcache_service_(appcache_service),
frontend_proxy_(this),
process_id_(process_id) {
}
@@ -36,10 +37,9 @@ void AppCacheDispatcherHost::OnChannelConnected(int32 peer_pid) {
}
}
-bool AppCacheDispatcherHost::OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) {
+bool AppCacheDispatcherHost::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(AppCacheDispatcherHost, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(AppCacheDispatcherHost, message)
IPC_MESSAGE_HANDLER(AppCacheHostMsg_RegisterHost, OnRegisterHost)
IPC_MESSAGE_HANDLER(AppCacheHostMsg_UnregisterHost, OnUnregisterHost)
IPC_MESSAGE_HANDLER(AppCacheHostMsg_SetSpawningHostId, OnSetSpawningHostId)
@@ -55,7 +55,7 @@ bool AppCacheDispatcherHost::OnMessageReceived(const IPC::Message& message,
IPC_MESSAGE_HANDLER_DELAY_REPLY(AppCacheHostMsg_StartUpdate, OnStartUpdate)
IPC_MESSAGE_HANDLER_DELAY_REPLY(AppCacheHostMsg_SwapCache, OnSwapCache)
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return handled;
}
@@ -63,7 +63,7 @@ bool AppCacheDispatcherHost::OnMessageReceived(const IPC::Message& message,
AppCacheDispatcherHost::~AppCacheDispatcherHost() {}
void AppCacheDispatcherHost::BadMessageReceived() {
- RecordAction(UserMetricsAction("BadMessageTerminate_ACDH"));
+ RecordAction(base::UserMetricsAction("BadMessageTerminate_ACDH"));
BrowserMessageFilter::BadMessageReceived();
}
@@ -162,7 +162,7 @@ void AppCacheDispatcherHost::OnGetStatus(int host_id, IPC::Message* reply_msg) {
return;
}
- GetStatusCallback(appcache::UNCACHED, reply_msg);
+ GetStatusCallback(appcache::APPCACHE_STATUS_UNCACHED, reply_msg);
}
void AppCacheDispatcherHost::OnStartUpdate(int host_id,
@@ -205,7 +205,7 @@ void AppCacheDispatcherHost::OnSwapCache(int host_id, IPC::Message* reply_msg) {
}
void AppCacheDispatcherHost::GetStatusCallback(
- appcache::Status status, void* param) {
+ appcache::AppCacheStatus status, void* param) {
IPC::Message* reply_msg = reinterpret_cast<IPC::Message*>(param);
DCHECK_EQ(pending_reply_msg_.get(), reply_msg);
AppCacheHostMsg_GetStatus::WriteReplyParams(reply_msg, status);
diff --git a/chromium/content/browser/appcache/appcache_dispatcher_host.h b/chromium/content/browser/appcache/appcache_dispatcher_host.h
index 29ed69ecf8b..8bad796d29e 100644
--- a/chromium/content/browser/appcache/appcache_dispatcher_host.h
+++ b/chromium/content/browser/appcache/appcache_dispatcher_host.h
@@ -28,8 +28,7 @@ class AppCacheDispatcherHost : public BrowserMessageFilter {
// BrowserIOMessageFilter implementation
virtual void OnChannelConnected(int32 peer_pid) OVERRIDE;
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
protected:
virtual ~AppCacheDispatcherHost();
@@ -56,7 +55,7 @@ class AppCacheDispatcherHost : public BrowserMessageFilter {
void OnGetResourceList(
int host_id,
std::vector<appcache::AppCacheResourceInfo>* resource_infos);
- void GetStatusCallback(appcache::Status status, void* param);
+ void GetStatusCallback(appcache::AppCacheStatus status, void* param);
void StartUpdateCallback(bool result, void* param);
void SwapCacheCallback(bool result, void* param);
diff --git a/chromium/content/browser/appcache/appcache_frontend_proxy.cc b/chromium/content/browser/appcache/appcache_frontend_proxy.cc
index 0ef1e32b1b8..ea693e9d71e 100644
--- a/chromium/content/browser/appcache/appcache_frontend_proxy.cc
+++ b/chromium/content/browser/appcache/appcache_frontend_proxy.cc
@@ -18,13 +18,14 @@ void AppCacheFrontendProxy::OnCacheSelected(
}
void AppCacheFrontendProxy::OnStatusChanged(const std::vector<int>& host_ids,
- appcache::Status status) {
+ appcache::AppCacheStatus status) {
sender_->Send(new AppCacheMsg_StatusChanged(host_ids, status));
}
void AppCacheFrontendProxy::OnEventRaised(const std::vector<int>& host_ids,
- appcache::EventID event_id) {
- DCHECK_NE(appcache::PROGRESS_EVENT, event_id); // See OnProgressEventRaised.
+ appcache::AppCacheEventID event_id) {
+ DCHECK_NE(appcache::APPCACHE_PROGRESS_EVENT,
+ event_id); // See OnProgressEventRaised.
sender_->Send(new AppCacheMsg_EventRaised(host_ids, event_id));
}
@@ -37,13 +38,12 @@ void AppCacheFrontendProxy::OnProgressEventRaised(
void AppCacheFrontendProxy::OnErrorEventRaised(
const std::vector<int>& host_ids,
- const std::string& message) {
- sender_->Send(new AppCacheMsg_ErrorEventRaised(
- host_ids, message));
+ const appcache::AppCacheErrorDetails& details) {
+ sender_->Send(new AppCacheMsg_ErrorEventRaised(host_ids, details));
}
void AppCacheFrontendProxy::OnLogMessage(int host_id,
- appcache::LogLevel log_level,
+ appcache::AppCacheLogLevel log_level,
const std::string& message) {
sender_->Send(new AppCacheMsg_LogMessage(host_id, log_level, message));
}
diff --git a/chromium/content/browser/appcache/appcache_frontend_proxy.h b/chromium/content/browser/appcache/appcache_frontend_proxy.h
index 2576e05d508..904c8aed5c1 100644
--- a/chromium/content/browser/appcache/appcache_frontend_proxy.h
+++ b/chromium/content/browser/appcache/appcache_frontend_proxy.h
@@ -22,15 +22,16 @@ class AppCacheFrontendProxy : public appcache::AppCacheFrontend {
virtual void OnCacheSelected(int host_id,
const appcache::AppCacheInfo& info) OVERRIDE;
virtual void OnStatusChanged(const std::vector<int>& host_ids,
- appcache::Status status) OVERRIDE;
+ appcache::AppCacheStatus status) OVERRIDE;
virtual void OnEventRaised(const std::vector<int>& host_ids,
- appcache::EventID event_id) OVERRIDE;
+ appcache::AppCacheEventID event_id) OVERRIDE;
virtual void OnProgressEventRaised(const std::vector<int>& host_ids,
const GURL& url,
int num_total, int num_complete) OVERRIDE;
virtual void OnErrorEventRaised(const std::vector<int>& host_ids,
- const std::string& message) OVERRIDE;
- virtual void OnLogMessage(int host_id, appcache::LogLevel log_level,
+ const appcache::AppCacheErrorDetails& details)
+ OVERRIDE;
+ virtual void OnLogMessage(int host_id, appcache::AppCacheLogLevel log_level,
const std::string& message) OVERRIDE;
virtual void OnContentBlocked(int host_id,
const GURL& manifest_url) OVERRIDE;
diff --git a/chromium/content/browser/appcache/appcache_group_unittest.cc b/chromium/content/browser/appcache/appcache_group_unittest.cc
new file mode 100644
index 00000000000..cec20fa8c62
--- /dev/null
+++ b/chromium/content/browser/appcache/appcache_group_unittest.cc
@@ -0,0 +1,309 @@
+// 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 <string>
+
+#include "base/message_loop/message_loop.h"
+#include "content/browser/appcache/mock_appcache_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/appcache/appcache.h"
+#include "webkit/browser/appcache/appcache_group.h"
+#include "webkit/browser/appcache/appcache_host.h"
+#include "webkit/browser/appcache/appcache_update_job.h"
+#include "webkit/common/appcache/appcache_interfaces.h"
+
+using appcache::AppCache;
+using appcache::AppCacheFrontend;
+using appcache::AppCacheGroup;
+using appcache::AppCacheHost;
+using appcache::AppCacheServiceImpl;
+using appcache::AppCacheUpdateJob;
+
+namespace {
+
+class TestAppCacheFrontend : public appcache::AppCacheFrontend {
+ public:
+ TestAppCacheFrontend()
+ : last_host_id_(-1), last_cache_id_(-1),
+ last_status_(appcache::APPCACHE_STATUS_OBSOLETE) {
+ }
+
+ virtual void OnCacheSelected(
+ int host_id, const appcache::AppCacheInfo& info) OVERRIDE {
+ last_host_id_ = host_id;
+ last_cache_id_ = info.cache_id;
+ last_status_ = info.status;
+ }
+
+ virtual void OnStatusChanged(const std::vector<int>& host_ids,
+ appcache::AppCacheStatus status) OVERRIDE {
+ }
+
+ virtual void OnEventRaised(const std::vector<int>& host_ids,
+ appcache::AppCacheEventID event_id) OVERRIDE {
+ }
+
+ virtual void OnErrorEventRaised(const std::vector<int>& host_ids,
+ const appcache::AppCacheErrorDetails& details)
+ OVERRIDE {}
+
+ virtual void OnProgressEventRaised(const std::vector<int>& host_ids,
+ const GURL& url,
+ int num_total, int num_complete) OVERRIDE {
+ }
+
+ virtual void OnLogMessage(int host_id, appcache::AppCacheLogLevel log_level,
+ const std::string& message) OVERRIDE {
+ }
+
+ virtual void OnContentBlocked(int host_id,
+ const GURL& manifest_url) OVERRIDE {
+ }
+
+ int last_host_id_;
+ int64 last_cache_id_;
+ appcache::AppCacheStatus last_status_;
+};
+
+} // namespace anon
+
+namespace content {
+
+class TestUpdateObserver : public AppCacheGroup::UpdateObserver {
+ public:
+ TestUpdateObserver() : update_completed_(false), group_has_cache_(false) {
+ }
+
+ virtual void OnUpdateComplete(AppCacheGroup* group) OVERRIDE {
+ update_completed_ = true;
+ group_has_cache_ = group->HasCache();
+ }
+
+ virtual void OnContentBlocked(AppCacheGroup* group) {
+ }
+
+ bool update_completed_;
+ bool group_has_cache_;
+};
+
+class TestAppCacheHost : public AppCacheHost {
+ public:
+ TestAppCacheHost(int host_id, AppCacheFrontend* frontend,
+ AppCacheServiceImpl* service)
+ : AppCacheHost(host_id, frontend, service),
+ update_completed_(false) {
+ }
+
+ virtual void OnUpdateComplete(AppCacheGroup* group) OVERRIDE {
+ update_completed_ = true;
+ }
+
+ bool update_completed_;
+};
+
+class AppCacheGroupTest : public testing::Test {
+ private:
+ base::MessageLoop message_loop_;
+};
+
+TEST_F(AppCacheGroupTest, AddRemoveCache) {
+ MockAppCacheService service;
+ scoped_refptr<AppCacheGroup> group(
+ new AppCacheGroup(service.storage(), GURL("http://foo.com"), 111));
+
+ base::Time now = base::Time::Now();
+
+ scoped_refptr<AppCache> cache1(new AppCache(service.storage(), 111));
+ cache1->set_complete(true);
+ cache1->set_update_time(now);
+ group->AddCache(cache1.get());
+ EXPECT_EQ(cache1, group->newest_complete_cache());
+
+ // Adding older cache does not change newest complete cache.
+ scoped_refptr<AppCache> cache2(new AppCache(service.storage(), 222));
+ cache2->set_complete(true);
+ cache2->set_update_time(now - base::TimeDelta::FromDays(1));
+ group->AddCache(cache2.get());
+ EXPECT_EQ(cache1, group->newest_complete_cache());
+
+ // Adding newer cache does change newest complete cache.
+ scoped_refptr<AppCache> cache3(new AppCache(service.storage(), 333));
+ cache3->set_complete(true);
+ cache3->set_update_time(now + base::TimeDelta::FromDays(1));
+ group->AddCache(cache3.get());
+ EXPECT_EQ(cache3, group->newest_complete_cache());
+
+ // Adding cache with same update time uses one with larger ID.
+ scoped_refptr<AppCache> cache4(new AppCache(service.storage(), 444));
+ cache4->set_complete(true);
+ cache4->set_update_time(now + base::TimeDelta::FromDays(1)); // same as 3
+ group->AddCache(cache4.get());
+ EXPECT_EQ(cache4, group->newest_complete_cache());
+
+ // smaller id
+ scoped_refptr<AppCache> cache5(new AppCache(service.storage(), 55));
+ cache5->set_complete(true);
+ cache5->set_update_time(now + base::TimeDelta::FromDays(1)); // same as 4
+ group->AddCache(cache5.get());
+ EXPECT_EQ(cache4, group->newest_complete_cache()); // no change
+
+ // Old caches can always be removed.
+ group->RemoveCache(cache1.get());
+ EXPECT_FALSE(cache1->owning_group());
+ EXPECT_EQ(cache4, group->newest_complete_cache()); // newest unchanged
+
+ // Remove rest of caches.
+ group->RemoveCache(cache2.get());
+ EXPECT_FALSE(cache2->owning_group());
+ EXPECT_EQ(cache4, group->newest_complete_cache()); // newest unchanged
+ group->RemoveCache(cache3.get());
+ EXPECT_FALSE(cache3->owning_group());
+ EXPECT_EQ(cache4, group->newest_complete_cache()); // newest unchanged
+ group->RemoveCache(cache5.get());
+ EXPECT_FALSE(cache5->owning_group());
+ EXPECT_EQ(cache4, group->newest_complete_cache()); // newest unchanged
+ group->RemoveCache(cache4.get()); // newest removed
+ EXPECT_FALSE(cache4->owning_group());
+ EXPECT_FALSE(group->newest_complete_cache()); // no more newest cache
+
+ // Can remove newest cache if there are older caches.
+ group->AddCache(cache1.get());
+ EXPECT_EQ(cache1, group->newest_complete_cache());
+ group->AddCache(cache4.get());
+ EXPECT_EQ(cache4, group->newest_complete_cache());
+ group->RemoveCache(cache4.get()); // remove newest
+ EXPECT_FALSE(cache4->owning_group());
+ EXPECT_FALSE(group->newest_complete_cache()); // newest removed
+}
+
+TEST_F(AppCacheGroupTest, CleanupUnusedGroup) {
+ MockAppCacheService service;
+ TestAppCacheFrontend frontend;
+ AppCacheGroup* group =
+ new AppCacheGroup(service.storage(), GURL("http://foo.com"), 111);
+
+ AppCacheHost host1(1, &frontend, &service);
+ AppCacheHost host2(2, &frontend, &service);
+
+ base::Time now = base::Time::Now();
+
+ AppCache* cache1 = new AppCache(service.storage(), 111);
+ cache1->set_complete(true);
+ cache1->set_update_time(now);
+ group->AddCache(cache1);
+ EXPECT_EQ(cache1, group->newest_complete_cache());
+
+ host1.AssociateCompleteCache(cache1);
+ EXPECT_EQ(frontend.last_host_id_, host1.host_id());
+ EXPECT_EQ(frontend.last_cache_id_, cache1->cache_id());
+ EXPECT_EQ(frontend.last_status_, appcache::APPCACHE_STATUS_IDLE);
+
+ host2.AssociateCompleteCache(cache1);
+ EXPECT_EQ(frontend.last_host_id_, host2.host_id());
+ EXPECT_EQ(frontend.last_cache_id_, cache1->cache_id());
+ EXPECT_EQ(frontend.last_status_, appcache::APPCACHE_STATUS_IDLE);
+
+ AppCache* cache2 = new AppCache(service.storage(), 222);
+ cache2->set_complete(true);
+ cache2->set_update_time(now + base::TimeDelta::FromDays(1));
+ group->AddCache(cache2);
+ EXPECT_EQ(cache2, group->newest_complete_cache());
+
+ // Unassociate all hosts from older cache.
+ host1.AssociateNoCache(GURL());
+ host2.AssociateNoCache(GURL());
+ EXPECT_EQ(frontend.last_host_id_, host2.host_id());
+ EXPECT_EQ(frontend.last_cache_id_, appcache::kAppCacheNoCacheId);
+ EXPECT_EQ(frontend.last_status_, appcache::APPCACHE_STATUS_UNCACHED);
+}
+
+TEST_F(AppCacheGroupTest, StartUpdate) {
+ MockAppCacheService service;
+ scoped_refptr<AppCacheGroup> group(
+ new AppCacheGroup(service.storage(), GURL("http://foo.com"), 111));
+
+ // Set state to checking to prevent update job from executing fetches.
+ group->update_status_ = AppCacheGroup::CHECKING;
+ group->StartUpdate();
+ AppCacheUpdateJob* update = group->update_job_;
+ EXPECT_TRUE(update != NULL);
+
+ // Start another update, check that same update job is in use.
+ group->StartUpdateWithHost(NULL);
+ EXPECT_EQ(update, group->update_job_);
+
+ // Deleting the update should restore the group to APPCACHE_STATUS_IDLE.
+ delete update;
+ EXPECT_TRUE(group->update_job_ == NULL);
+ EXPECT_EQ(AppCacheGroup::IDLE, group->update_status());
+}
+
+TEST_F(AppCacheGroupTest, CancelUpdate) {
+ MockAppCacheService service;
+ scoped_refptr<AppCacheGroup> group(
+ new AppCacheGroup(service.storage(), GURL("http://foo.com"), 111));
+
+ // Set state to checking to prevent update job from executing fetches.
+ group->update_status_ = AppCacheGroup::CHECKING;
+ group->StartUpdate();
+ AppCacheUpdateJob* update = group->update_job_;
+ EXPECT_TRUE(update != NULL);
+
+ // Deleting the group should cancel the update.
+ TestUpdateObserver observer;
+ group->AddUpdateObserver(&observer);
+ group = NULL; // causes group to be deleted
+ EXPECT_TRUE(observer.update_completed_);
+ EXPECT_FALSE(observer.group_has_cache_);
+}
+
+TEST_F(AppCacheGroupTest, QueueUpdate) {
+ MockAppCacheService service;
+ scoped_refptr<AppCacheGroup> group(
+ new AppCacheGroup(service.storage(), GURL("http://foo.com"), 111));
+
+ // Set state to checking to prevent update job from executing fetches.
+ group->update_status_ = AppCacheGroup::CHECKING;
+ group->StartUpdate();
+ EXPECT_TRUE(group->update_job_);
+
+ // Pretend group's update job is terminating so that next update is queued.
+ group->update_job_->internal_state_ = AppCacheUpdateJob::REFETCH_MANIFEST;
+ EXPECT_TRUE(group->update_job_->IsTerminating());
+
+ TestAppCacheFrontend frontend;
+ TestAppCacheHost host(1, &frontend, &service);
+ host.new_master_entry_url_ = GURL("http://foo.com/bar.txt");
+ group->StartUpdateWithNewMasterEntry(&host, host.new_master_entry_url_);
+ EXPECT_FALSE(group->queued_updates_.empty());
+
+ group->AddUpdateObserver(&host);
+ EXPECT_FALSE(group->FindObserver(&host, group->observers_));
+ EXPECT_TRUE(group->FindObserver(&host, group->queued_observers_));
+
+ // Delete update to cause it to complete. Verify no update complete notice
+ // sent to host.
+ delete group->update_job_;
+ EXPECT_EQ(AppCacheGroup::IDLE, group->update_status_);
+ EXPECT_FALSE(group->restart_update_task_.IsCancelled());
+ EXPECT_FALSE(host.update_completed_);
+
+ // Start another update. Cancels task and will run queued updates.
+ group->update_status_ = AppCacheGroup::CHECKING; // prevent actual fetches
+ group->StartUpdate();
+ EXPECT_TRUE(group->update_job_);
+ EXPECT_TRUE(group->restart_update_task_.IsCancelled());
+ EXPECT_TRUE(group->queued_updates_.empty());
+ EXPECT_FALSE(group->update_job_->pending_master_entries_.empty());
+ EXPECT_FALSE(group->FindObserver(&host, group->queued_observers_));
+ EXPECT_TRUE(group->FindObserver(&host, group->observers_));
+
+ // Delete update to cause it to complete. Verify host is notified.
+ delete group->update_job_;
+ EXPECT_EQ(AppCacheGroup::IDLE, group->update_status_);
+ EXPECT_TRUE(group->restart_update_task_.IsCancelled());
+ EXPECT_TRUE(host.update_completed_);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/appcache/appcache_host_unittest.cc b/chromium/content/browser/appcache/appcache_host_unittest.cc
new file mode 100644
index 00000000000..6b9970dc4ff
--- /dev/null
+++ b/chromium/content/browser/appcache/appcache_host_unittest.cc
@@ -0,0 +1,554 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "content/browser/appcache/mock_appcache_policy.h"
+#include "content/browser/appcache/mock_appcache_service.h"
+#include "net/url_request/url_request.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/appcache/appcache.h"
+#include "webkit/browser/appcache/appcache_backend_impl.h"
+#include "webkit/browser/appcache/appcache_group.h"
+#include "webkit/browser/appcache/appcache_host.h"
+#include "webkit/browser/quota/quota_manager.h"
+
+using appcache::AppCache;
+using appcache::AppCacheBackendImpl;
+using appcache::AppCacheEntry;
+using appcache::AppCacheFrontend;
+using appcache::AppCacheGroup;
+using appcache::AppCacheHost;
+using appcache::kAppCacheNoCacheId;
+using appcache::APPCACHE_ERROR_EVENT;
+using appcache::APPCACHE_STATUS_OBSOLETE;
+using appcache::APPCACHE_OBSOLETE_EVENT;
+using appcache::APPCACHE_PROGRESS_EVENT;
+using appcache::AppCacheStatus;
+using appcache::APPCACHE_STATUS_UNCACHED;
+
+namespace content {
+
+class AppCacheHostTest : public testing::Test {
+ public:
+ AppCacheHostTest() {
+ get_status_callback_ =
+ base::Bind(&AppCacheHostTest::GetStatusCallback,
+ base::Unretained(this));
+ start_update_callback_ =
+ base::Bind(&AppCacheHostTest::StartUpdateCallback,
+ base::Unretained(this));
+ swap_cache_callback_ =
+ base::Bind(&AppCacheHostTest::SwapCacheCallback,
+ base::Unretained(this));
+ }
+
+ class MockFrontend : public AppCacheFrontend {
+ public:
+ MockFrontend()
+ : last_host_id_(-222), last_cache_id_(-222),
+ last_status_(appcache::APPCACHE_STATUS_OBSOLETE),
+ last_status_changed_(appcache::APPCACHE_STATUS_OBSOLETE),
+ last_event_id_(appcache::APPCACHE_OBSOLETE_EVENT),
+ content_blocked_(false) {
+ }
+
+ virtual void OnCacheSelected(
+ int host_id, const appcache::AppCacheInfo& info) OVERRIDE {
+ last_host_id_ = host_id;
+ last_cache_id_ = info.cache_id;
+ last_status_ = info.status;
+ }
+
+ virtual void OnStatusChanged(const std::vector<int>& host_ids,
+ appcache::AppCacheStatus status) OVERRIDE {
+ last_status_changed_ = status;
+ }
+
+ virtual void OnEventRaised(const std::vector<int>& host_ids,
+ appcache::AppCacheEventID event_id) OVERRIDE {
+ last_event_id_ = event_id;
+ }
+
+ virtual void OnErrorEventRaised(
+ const std::vector<int>& host_ids,
+ const appcache::AppCacheErrorDetails& details) OVERRIDE {
+ last_event_id_ = APPCACHE_ERROR_EVENT;
+ }
+
+ virtual void OnProgressEventRaised(const std::vector<int>& host_ids,
+ const GURL& url,
+ int num_total,
+ int num_complete) OVERRIDE {
+ last_event_id_ = APPCACHE_PROGRESS_EVENT;
+ }
+
+ virtual void OnLogMessage(int host_id,
+ appcache::AppCacheLogLevel log_level,
+ const std::string& message) OVERRIDE {
+ }
+
+ virtual void OnContentBlocked(int host_id,
+ const GURL& manifest_url) OVERRIDE {
+ content_blocked_ = true;
+ }
+
+ int last_host_id_;
+ int64 last_cache_id_;
+ appcache::AppCacheStatus last_status_;
+ appcache::AppCacheStatus last_status_changed_;
+ appcache::AppCacheEventID last_event_id_;
+ bool content_blocked_;
+ };
+
+ class MockQuotaManagerProxy : public quota::QuotaManagerProxy {
+ public:
+ MockQuotaManagerProxy() : QuotaManagerProxy(NULL, NULL) {}
+
+ // Not needed for our tests.
+ virtual void RegisterClient(quota::QuotaClient* client) OVERRIDE {}
+ virtual void NotifyStorageAccessed(quota::QuotaClient::ID client_id,
+ const GURL& origin,
+ quota::StorageType type) OVERRIDE {}
+ virtual void NotifyStorageModified(quota::QuotaClient::ID client_id,
+ const GURL& origin,
+ quota::StorageType type,
+ int64 delta) OVERRIDE {}
+ virtual void SetUsageCacheEnabled(quota::QuotaClient::ID client_id,
+ const GURL& origin,
+ quota::StorageType type,
+ bool enabled) OVERRIDE {}
+ virtual void GetUsageAndQuota(
+ base::SequencedTaskRunner* original_task_runner,
+ const GURL& origin,
+ quota::StorageType type,
+ const GetUsageAndQuotaCallback& callback) OVERRIDE {}
+
+ virtual void NotifyOriginInUse(const GURL& origin) OVERRIDE {
+ inuse_[origin] += 1;
+ }
+
+ virtual void NotifyOriginNoLongerInUse(const GURL& origin) OVERRIDE {
+ inuse_[origin] -= 1;
+ }
+
+ int GetInUseCount(const GURL& origin) {
+ return inuse_[origin];
+ }
+
+ void reset() {
+ inuse_.clear();
+ }
+
+ // Map from origin to count of inuse notifications.
+ std::map<GURL, int> inuse_;
+
+ protected:
+ virtual ~MockQuotaManagerProxy() {}
+ };
+
+ void GetStatusCallback(AppCacheStatus status, void* param) {
+ last_status_result_ = status;
+ last_callback_param_ = param;
+ }
+
+ void StartUpdateCallback(bool result, void* param) {
+ last_start_result_ = result;
+ last_callback_param_ = param;
+ }
+
+ void SwapCacheCallback(bool result, void* param) {
+ last_swap_result_ = result;
+ last_callback_param_ = param;
+ }
+
+ base::MessageLoop message_loop_;
+
+ // Mock classes for the 'host' to work with
+ MockAppCacheService service_;
+ MockFrontend mock_frontend_;
+
+ // Mock callbacks we expect to receive from the 'host'
+ appcache::GetStatusCallback get_status_callback_;
+ appcache::StartUpdateCallback start_update_callback_;
+ appcache::SwapCacheCallback swap_cache_callback_;
+
+ AppCacheStatus last_status_result_;
+ bool last_swap_result_;
+ bool last_start_result_;
+ void* last_callback_param_;
+};
+
+TEST_F(AppCacheHostTest, Basic) {
+ // Construct a host and test what state it appears to be in.
+ AppCacheHost host(1, &mock_frontend_, &service_);
+ EXPECT_EQ(1, host.host_id());
+ EXPECT_EQ(&service_, host.service());
+ EXPECT_EQ(&mock_frontend_, host.frontend());
+ EXPECT_EQ(NULL, host.associated_cache());
+ EXPECT_FALSE(host.is_selection_pending());
+
+ // See that the callbacks are delivered immediately
+ // and respond as if there is no cache selected.
+ last_status_result_ = APPCACHE_STATUS_OBSOLETE;
+ host.GetStatusWithCallback(get_status_callback_, reinterpret_cast<void*>(1));
+ EXPECT_EQ(APPCACHE_STATUS_UNCACHED, last_status_result_);
+ EXPECT_EQ(reinterpret_cast<void*>(1), last_callback_param_);
+
+ last_start_result_ = true;
+ host.StartUpdateWithCallback(start_update_callback_,
+ reinterpret_cast<void*>(2));
+ EXPECT_FALSE(last_start_result_);
+ EXPECT_EQ(reinterpret_cast<void*>(2), last_callback_param_);
+
+ last_swap_result_ = true;
+ host.SwapCacheWithCallback(swap_cache_callback_, reinterpret_cast<void*>(3));
+ EXPECT_FALSE(last_swap_result_);
+ EXPECT_EQ(reinterpret_cast<void*>(3), last_callback_param_);
+}
+
+TEST_F(AppCacheHostTest, SelectNoCache) {
+ scoped_refptr<MockQuotaManagerProxy> mock_quota_proxy(
+ new MockQuotaManagerProxy);
+ service_.set_quota_manager_proxy(mock_quota_proxy.get());
+
+ // Reset our mock frontend
+ mock_frontend_.last_cache_id_ = -333;
+ mock_frontend_.last_host_id_ = -333;
+ mock_frontend_.last_status_ = APPCACHE_STATUS_OBSOLETE;
+
+ const GURL kDocAndOriginUrl(GURL("http://whatever/").GetOrigin());
+ {
+ AppCacheHost host(1, &mock_frontend_, &service_);
+ host.SelectCache(kDocAndOriginUrl, kAppCacheNoCacheId, GURL());
+ EXPECT_EQ(1, mock_quota_proxy->GetInUseCount(kDocAndOriginUrl));
+
+ // We should have received an OnCacheSelected msg
+ EXPECT_EQ(1, mock_frontend_.last_host_id_);
+ EXPECT_EQ(kAppCacheNoCacheId, mock_frontend_.last_cache_id_);
+ EXPECT_EQ(APPCACHE_STATUS_UNCACHED, mock_frontend_.last_status_);
+
+ // Otherwise, see that it respond as if there is no cache selected.
+ EXPECT_EQ(1, host.host_id());
+ EXPECT_EQ(&service_, host.service());
+ EXPECT_EQ(&mock_frontend_, host.frontend());
+ EXPECT_EQ(NULL, host.associated_cache());
+ EXPECT_FALSE(host.is_selection_pending());
+ EXPECT_TRUE(host.preferred_manifest_url().is_empty());
+ }
+ EXPECT_EQ(0, mock_quota_proxy->GetInUseCount(kDocAndOriginUrl));
+ service_.set_quota_manager_proxy(NULL);
+}
+
+TEST_F(AppCacheHostTest, ForeignEntry) {
+ // Reset our mock frontend
+ mock_frontend_.last_cache_id_ = -333;
+ mock_frontend_.last_host_id_ = -333;
+ mock_frontend_.last_status_ = APPCACHE_STATUS_OBSOLETE;
+
+ // Precondition, a cache with an entry that is not marked as foreign.
+ const int kCacheId = 22;
+ const GURL kDocumentURL("http://origin/document");
+ scoped_refptr<AppCache> cache = new AppCache(service_.storage(), kCacheId);
+ cache->AddEntry(kDocumentURL, AppCacheEntry(AppCacheEntry::EXPLICIT));
+
+ AppCacheHost host(1, &mock_frontend_, &service_);
+ host.MarkAsForeignEntry(kDocumentURL, kCacheId);
+
+ // We should have received an OnCacheSelected msg for kAppCacheNoCacheId.
+ EXPECT_EQ(1, mock_frontend_.last_host_id_);
+ EXPECT_EQ(kAppCacheNoCacheId, mock_frontend_.last_cache_id_);
+ EXPECT_EQ(APPCACHE_STATUS_UNCACHED, mock_frontend_.last_status_);
+
+ // See that it respond as if there is no cache selected.
+ EXPECT_EQ(1, host.host_id());
+ EXPECT_EQ(&service_, host.service());
+ EXPECT_EQ(&mock_frontend_, host.frontend());
+ EXPECT_EQ(NULL, host.associated_cache());
+ EXPECT_FALSE(host.is_selection_pending());
+
+ // See that the entry was marked as foreign.
+ EXPECT_TRUE(cache->GetEntry(kDocumentURL)->IsForeign());
+}
+
+TEST_F(AppCacheHostTest, ForeignFallbackEntry) {
+ // Reset our mock frontend
+ mock_frontend_.last_cache_id_ = -333;
+ mock_frontend_.last_host_id_ = -333;
+ mock_frontend_.last_status_ = APPCACHE_STATUS_OBSOLETE;
+
+ // Precondition, a cache with a fallback entry that is not marked as foreign.
+ const int kCacheId = 22;
+ const GURL kFallbackURL("http://origin/fallback_resource");
+ scoped_refptr<AppCache> cache = new AppCache(service_.storage(), kCacheId);
+ cache->AddEntry(kFallbackURL, AppCacheEntry(AppCacheEntry::FALLBACK));
+
+ AppCacheHost host(1, &mock_frontend_, &service_);
+ host.NotifyMainResourceIsNamespaceEntry(kFallbackURL);
+ host.MarkAsForeignEntry(GURL("http://origin/missing_document"), kCacheId);
+
+ // We should have received an OnCacheSelected msg for kAppCacheNoCacheId.
+ EXPECT_EQ(1, mock_frontend_.last_host_id_);
+ EXPECT_EQ(kAppCacheNoCacheId, mock_frontend_.last_cache_id_);
+ EXPECT_EQ(APPCACHE_STATUS_UNCACHED, mock_frontend_.last_status_);
+
+ // See that the fallback entry was marked as foreign.
+ EXPECT_TRUE(cache->GetEntry(kFallbackURL)->IsForeign());
+}
+
+TEST_F(AppCacheHostTest, FailedCacheLoad) {
+ // Reset our mock frontend
+ mock_frontend_.last_cache_id_ = -333;
+ mock_frontend_.last_host_id_ = -333;
+ mock_frontend_.last_status_ = APPCACHE_STATUS_OBSOLETE;
+
+ AppCacheHost host(1, &mock_frontend_, &service_);
+ EXPECT_FALSE(host.is_selection_pending());
+
+ const int kMockCacheId = 333;
+
+ // Put it in a state where we're waiting on a cache
+ // load prior to finishing cache selection.
+ host.pending_selected_cache_id_ = kMockCacheId;
+ EXPECT_TRUE(host.is_selection_pending());
+
+ // The callback should not occur until we finish cache selection.
+ last_status_result_ = APPCACHE_STATUS_OBSOLETE;
+ last_callback_param_ = reinterpret_cast<void*>(-1);
+ host.GetStatusWithCallback(get_status_callback_, reinterpret_cast<void*>(1));
+ EXPECT_EQ(APPCACHE_STATUS_OBSOLETE, last_status_result_);
+ EXPECT_EQ(reinterpret_cast<void*>(-1), last_callback_param_);
+
+ // Satisfy the load with NULL, a failure.
+ host.OnCacheLoaded(NULL, kMockCacheId);
+
+ // Cache selection should have finished
+ EXPECT_FALSE(host.is_selection_pending());
+ EXPECT_EQ(1, mock_frontend_.last_host_id_);
+ EXPECT_EQ(kAppCacheNoCacheId, mock_frontend_.last_cache_id_);
+ EXPECT_EQ(APPCACHE_STATUS_UNCACHED, mock_frontend_.last_status_);
+
+ // Callback should have fired upon completing the cache load too.
+ EXPECT_EQ(APPCACHE_STATUS_UNCACHED, last_status_result_);
+ EXPECT_EQ(reinterpret_cast<void*>(1), last_callback_param_);
+}
+
+TEST_F(AppCacheHostTest, FailedGroupLoad) {
+ AppCacheHost host(1, &mock_frontend_, &service_);
+
+ const GURL kMockManifestUrl("http://foo.bar/baz");
+
+ // Put it in a state where we're waiting on a cache
+ // load prior to finishing cache selection.
+ host.pending_selected_manifest_url_ = kMockManifestUrl;
+ EXPECT_TRUE(host.is_selection_pending());
+
+ // The callback should not occur until we finish cache selection.
+ last_status_result_ = APPCACHE_STATUS_OBSOLETE;
+ last_callback_param_ = reinterpret_cast<void*>(-1);
+ host.GetStatusWithCallback(get_status_callback_, reinterpret_cast<void*>(1));
+ EXPECT_EQ(APPCACHE_STATUS_OBSOLETE, last_status_result_);
+ EXPECT_EQ(reinterpret_cast<void*>(-1), last_callback_param_);
+
+ // Satisfy the load will NULL, a failure.
+ host.OnGroupLoaded(NULL, kMockManifestUrl);
+
+ // Cache selection should have finished
+ EXPECT_FALSE(host.is_selection_pending());
+ EXPECT_EQ(1, mock_frontend_.last_host_id_);
+ EXPECT_EQ(kAppCacheNoCacheId, mock_frontend_.last_cache_id_);
+ EXPECT_EQ(APPCACHE_STATUS_UNCACHED, mock_frontend_.last_status_);
+
+ // Callback should have fired upon completing the group load.
+ EXPECT_EQ(APPCACHE_STATUS_UNCACHED, last_status_result_);
+ EXPECT_EQ(reinterpret_cast<void*>(1), last_callback_param_);
+}
+
+TEST_F(AppCacheHostTest, SetSwappableCache) {
+ AppCacheHost host(1, &mock_frontend_, &service_);
+ host.SetSwappableCache(NULL);
+ EXPECT_FALSE(host.swappable_cache_.get());
+
+ scoped_refptr<AppCacheGroup> group1(new AppCacheGroup(
+ service_.storage(), GURL(), service_.storage()->NewGroupId()));
+ host.SetSwappableCache(group1.get());
+ EXPECT_FALSE(host.swappable_cache_.get());
+
+ AppCache* cache1 = new AppCache(service_.storage(), 111);
+ cache1->set_complete(true);
+ group1->AddCache(cache1);
+ host.SetSwappableCache(group1.get());
+ EXPECT_EQ(cache1, host.swappable_cache_.get());
+
+ mock_frontend_.last_host_id_ = -222; // to verify we received OnCacheSelected
+
+ host.AssociateCompleteCache(cache1);
+ EXPECT_FALSE(host.swappable_cache_.get()); // was same as associated cache
+ EXPECT_EQ(appcache::APPCACHE_STATUS_IDLE, host.GetStatus());
+ // verify OnCacheSelected was called
+ EXPECT_EQ(host.host_id(), mock_frontend_.last_host_id_);
+ EXPECT_EQ(cache1->cache_id(), mock_frontend_.last_cache_id_);
+ EXPECT_EQ(appcache::APPCACHE_STATUS_IDLE, mock_frontend_.last_status_);
+
+ AppCache* cache2 = new AppCache(service_.storage(), 222);
+ cache2->set_complete(true);
+ group1->AddCache(cache2);
+ EXPECT_EQ(cache2, host.swappable_cache_.get()); // updated to newest
+
+ scoped_refptr<AppCacheGroup> group2(
+ new AppCacheGroup(service_.storage(), GURL("http://foo.com"),
+ service_.storage()->NewGroupId()));
+ AppCache* cache3 = new AppCache(service_.storage(), 333);
+ cache3->set_complete(true);
+ group2->AddCache(cache3);
+
+ AppCache* cache4 = new AppCache(service_.storage(), 444);
+ cache4->set_complete(true);
+ group2->AddCache(cache4);
+ EXPECT_EQ(cache2, host.swappable_cache_.get()); // unchanged
+
+ host.AssociateCompleteCache(cache3);
+ EXPECT_EQ(cache4, host.swappable_cache_.get()); // newest cache in group2
+ EXPECT_FALSE(group1->HasCache()); // both caches in group1 have refcount 0
+
+ host.AssociateNoCache(GURL());
+ EXPECT_FALSE(host.swappable_cache_.get());
+ EXPECT_FALSE(group2->HasCache()); // both caches in group2 have refcount 0
+
+ // Host adds reference to newest cache when an update is complete.
+ AppCache* cache5 = new AppCache(service_.storage(), 555);
+ cache5->set_complete(true);
+ group2->AddCache(cache5);
+ host.group_being_updated_ = group2;
+ host.OnUpdateComplete(group2.get());
+ EXPECT_FALSE(host.group_being_updated_.get());
+ EXPECT_EQ(cache5, host.swappable_cache_.get());
+
+ group2->RemoveCache(cache5);
+ EXPECT_FALSE(group2->HasCache());
+ host.group_being_updated_ = group2;
+ host.OnUpdateComplete(group2.get());
+ EXPECT_FALSE(host.group_being_updated_.get());
+ EXPECT_FALSE(host.swappable_cache_.get()); // group2 had no newest cache
+}
+
+TEST_F(AppCacheHostTest, ForDedicatedWorker) {
+ const int kMockProcessId = 1;
+ const int kParentHostId = 1;
+ const int kWorkerHostId = 2;
+
+ AppCacheBackendImpl backend_impl;
+ backend_impl.Initialize(&service_, &mock_frontend_, kMockProcessId);
+ backend_impl.RegisterHost(kParentHostId);
+ backend_impl.RegisterHost(kWorkerHostId);
+
+ AppCacheHost* parent_host = backend_impl.GetHost(kParentHostId);
+ EXPECT_FALSE(parent_host->is_for_dedicated_worker());
+
+ AppCacheHost* worker_host = backend_impl.GetHost(kWorkerHostId);
+ worker_host->SelectCacheForWorker(kParentHostId, kMockProcessId);
+ EXPECT_TRUE(worker_host->is_for_dedicated_worker());
+ EXPECT_EQ(parent_host, worker_host->GetParentAppCacheHost());
+
+ // We should have received an OnCacheSelected msg for the worker_host.
+ // The host for workers always indicates 'no cache selected' regardless
+ // of its parent's state. This is OK because the worker cannot access
+ // the scriptable interface, the only function available is resource
+ // loading (see appcache_request_handler_unittests those tests).
+ EXPECT_EQ(kWorkerHostId, mock_frontend_.last_host_id_);
+ EXPECT_EQ(kAppCacheNoCacheId, mock_frontend_.last_cache_id_);
+ EXPECT_EQ(APPCACHE_STATUS_UNCACHED, mock_frontend_.last_status_);
+
+ // Simulate the parent being torn down.
+ backend_impl.UnregisterHost(kParentHostId);
+ parent_host = NULL;
+ EXPECT_EQ(NULL, backend_impl.GetHost(kParentHostId));
+ EXPECT_EQ(NULL, worker_host->GetParentAppCacheHost());
+}
+
+TEST_F(AppCacheHostTest, SelectCacheAllowed) {
+ scoped_refptr<MockQuotaManagerProxy> mock_quota_proxy(
+ new MockQuotaManagerProxy);
+ MockAppCachePolicy mock_appcache_policy;
+ mock_appcache_policy.can_create_return_value_ = true;
+ service_.set_quota_manager_proxy(mock_quota_proxy.get());
+ service_.set_appcache_policy(&mock_appcache_policy);
+
+ // Reset our mock frontend
+ mock_frontend_.last_cache_id_ = -333;
+ mock_frontend_.last_host_id_ = -333;
+ mock_frontend_.last_status_ = APPCACHE_STATUS_OBSOLETE;
+ mock_frontend_.last_event_id_ = APPCACHE_OBSOLETE_EVENT;
+ mock_frontend_.content_blocked_ = false;
+
+ const GURL kDocAndOriginUrl(GURL("http://whatever/").GetOrigin());
+ const GURL kManifestUrl(GURL("http://whatever/cache.manifest"));
+ {
+ AppCacheHost host(1, &mock_frontend_, &service_);
+ host.first_party_url_ = kDocAndOriginUrl;
+ host.SelectCache(kDocAndOriginUrl, kAppCacheNoCacheId, kManifestUrl);
+ EXPECT_EQ(1, mock_quota_proxy->GetInUseCount(kDocAndOriginUrl));
+
+ // MockAppCacheService::LoadOrCreateGroup is asynchronous, so we shouldn't
+ // have received an OnCacheSelected msg yet.
+ EXPECT_EQ(-333, mock_frontend_.last_host_id_);
+ EXPECT_EQ(-333, mock_frontend_.last_cache_id_);
+ EXPECT_EQ(APPCACHE_STATUS_OBSOLETE, mock_frontend_.last_status_);
+ // No error events either
+ EXPECT_EQ(APPCACHE_OBSOLETE_EVENT, mock_frontend_.last_event_id_);
+ EXPECT_FALSE(mock_frontend_.content_blocked_);
+
+ EXPECT_TRUE(host.is_selection_pending());
+ }
+ EXPECT_EQ(0, mock_quota_proxy->GetInUseCount(kDocAndOriginUrl));
+ service_.set_quota_manager_proxy(NULL);
+}
+
+TEST_F(AppCacheHostTest, SelectCacheBlocked) {
+ scoped_refptr<MockQuotaManagerProxy> mock_quota_proxy(
+ new MockQuotaManagerProxy);
+ MockAppCachePolicy mock_appcache_policy;
+ mock_appcache_policy.can_create_return_value_ = false;
+ service_.set_quota_manager_proxy(mock_quota_proxy.get());
+ service_.set_appcache_policy(&mock_appcache_policy);
+
+ // Reset our mock frontend
+ mock_frontend_.last_cache_id_ = -333;
+ mock_frontend_.last_host_id_ = -333;
+ mock_frontend_.last_status_ = APPCACHE_STATUS_OBSOLETE;
+ mock_frontend_.last_event_id_ = APPCACHE_OBSOLETE_EVENT;
+ mock_frontend_.content_blocked_ = false;
+
+ const GURL kDocAndOriginUrl(GURL("http://whatever/").GetOrigin());
+ const GURL kManifestUrl(GURL("http://whatever/cache.manifest"));
+ {
+ AppCacheHost host(1, &mock_frontend_, &service_);
+ host.first_party_url_ = kDocAndOriginUrl;
+ host.SelectCache(kDocAndOriginUrl, kAppCacheNoCacheId, kManifestUrl);
+ EXPECT_EQ(1, mock_quota_proxy->GetInUseCount(kDocAndOriginUrl));
+
+ // We should have received an OnCacheSelected msg
+ EXPECT_EQ(1, mock_frontend_.last_host_id_);
+ EXPECT_EQ(kAppCacheNoCacheId, mock_frontend_.last_cache_id_);
+ EXPECT_EQ(APPCACHE_STATUS_UNCACHED, mock_frontend_.last_status_);
+
+ // Also, an error event was raised
+ EXPECT_EQ(APPCACHE_ERROR_EVENT, mock_frontend_.last_event_id_);
+ EXPECT_TRUE(mock_frontend_.content_blocked_);
+
+ // Otherwise, see that it respond as if there is no cache selected.
+ EXPECT_EQ(1, host.host_id());
+ EXPECT_EQ(&service_, host.service());
+ EXPECT_EQ(&mock_frontend_, host.frontend());
+ EXPECT_EQ(NULL, host.associated_cache());
+ EXPECT_FALSE(host.is_selection_pending());
+ EXPECT_TRUE(host.preferred_manifest_url().is_empty());
+ }
+ EXPECT_EQ(0, mock_quota_proxy->GetInUseCount(kDocAndOriginUrl));
+ service_.set_quota_manager_proxy(NULL);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/appcache/appcache_interceptor.cc b/chromium/content/browser/appcache/appcache_interceptor.cc
new file mode 100644
index 00000000000..ac7dbc3b607
--- /dev/null
+++ b/chromium/content/browser/appcache/appcache_interceptor.cc
@@ -0,0 +1,128 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/appcache/appcache_interceptor.h"
+
+#include "webkit/browser/appcache/appcache_backend_impl.h"
+#include "webkit/browser/appcache/appcache_host.h"
+#include "webkit/browser/appcache/appcache_request_handler.h"
+#include "webkit/browser/appcache/appcache_service_impl.h"
+#include "webkit/browser/appcache/appcache_url_request_job.h"
+#include "webkit/common/appcache/appcache_interfaces.h"
+
+using appcache::AppCacheBackendImpl;
+using appcache::AppCacheHost;
+using appcache::AppCacheRequestHandler;
+using appcache::AppCacheServiceImpl;
+using appcache::kAppCacheNoCacheId;
+using appcache::kAppCacheNoHostId;
+
+namespace content {
+
+// static
+AppCacheInterceptor* AppCacheInterceptor::GetInstance() {
+ return Singleton<AppCacheInterceptor>::get();
+}
+
+void AppCacheInterceptor::SetHandler(
+ net::URLRequest* request, AppCacheRequestHandler* handler) {
+ request->SetUserData(GetInstance(), handler); // request takes ownership
+}
+
+AppCacheRequestHandler* AppCacheInterceptor::GetHandler(
+ net::URLRequest* request) {
+ return reinterpret_cast<AppCacheRequestHandler*>(
+ request->GetUserData(GetInstance()));
+}
+
+void AppCacheInterceptor::SetExtraRequestInfo(
+ net::URLRequest* request, AppCacheServiceImpl* service, int process_id,
+ int host_id, ResourceType::Type resource_type) {
+ if (!service || (host_id == kAppCacheNoHostId))
+ return;
+
+ AppCacheBackendImpl* backend = service->GetBackend(process_id);
+ if (!backend)
+ return;
+
+ // TODO(michaeln): An invalid host id is indicative of bad data
+ // from a child process. How should we handle that here?
+ AppCacheHost* host = backend->GetHost(host_id);
+ if (!host)
+ return;
+
+ // Create a handler for this request and associate it with the request.
+ AppCacheRequestHandler* handler =
+ host->CreateRequestHandler(request, resource_type);
+ if (handler)
+ SetHandler(request, handler);
+}
+
+void AppCacheInterceptor::GetExtraResponseInfo(net::URLRequest* request,
+ int64* cache_id,
+ GURL* manifest_url) {
+ DCHECK(*cache_id == kAppCacheNoCacheId);
+ DCHECK(manifest_url->is_empty());
+ AppCacheRequestHandler* handler = GetHandler(request);
+ if (handler)
+ handler->GetExtraResponseInfo(cache_id, manifest_url);
+}
+
+void AppCacheInterceptor::PrepareForCrossSiteTransfer(
+ net::URLRequest* request,
+ int old_process_id) {
+ AppCacheRequestHandler* handler = GetHandler(request);
+ if (!handler)
+ return;
+ handler->PrepareForCrossSiteTransfer(old_process_id);
+}
+
+void AppCacheInterceptor::CompleteCrossSiteTransfer(
+ net::URLRequest* request,
+ int new_process_id,
+ int new_host_id) {
+ AppCacheRequestHandler* handler = GetHandler(request);
+ if (!handler)
+ return;
+ DCHECK_NE(kAppCacheNoHostId, new_host_id);
+ handler->CompleteCrossSiteTransfer(new_process_id,
+ new_host_id);
+}
+
+AppCacheInterceptor::AppCacheInterceptor() {
+ net::URLRequest::Deprecated::RegisterRequestInterceptor(this);
+}
+
+AppCacheInterceptor::~AppCacheInterceptor() {
+ net::URLRequest::Deprecated::UnregisterRequestInterceptor(this);
+}
+
+net::URLRequestJob* AppCacheInterceptor::MaybeIntercept(
+ net::URLRequest* request, net::NetworkDelegate* network_delegate) {
+ AppCacheRequestHandler* handler = GetHandler(request);
+ if (!handler)
+ return NULL;
+ return handler->MaybeLoadResource(request, network_delegate);
+}
+
+net::URLRequestJob* AppCacheInterceptor::MaybeInterceptRedirect(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate,
+ const GURL& location) {
+ AppCacheRequestHandler* handler = GetHandler(request);
+ if (!handler)
+ return NULL;
+ return handler->MaybeLoadFallbackForRedirect(
+ request, network_delegate, location);
+}
+
+net::URLRequestJob* AppCacheInterceptor::MaybeInterceptResponse(
+ net::URLRequest* request, net::NetworkDelegate* network_delegate) {
+ AppCacheRequestHandler* handler = GetHandler(request);
+ if (!handler)
+ return NULL;
+ return handler->MaybeLoadFallbackForResponse(request, network_delegate);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/appcache/appcache_interceptor.h b/chromium/content/browser/appcache/appcache_interceptor.h
new file mode 100644
index 00000000000..d8da18190f4
--- /dev/null
+++ b/chromium/content/browser/appcache/appcache_interceptor.h
@@ -0,0 +1,82 @@
+// 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_APPCACHE_APPCACHE_INTERCEPTOR_H_
+#define CONTENT_BROWSER_APPCACHE_APPCACHE_INTERCEPTOR_H_
+
+#include "base/memory/singleton.h"
+#include "content/common/content_export.h"
+#include "net/url_request/url_request.h"
+#include "url/gurl.h"
+#include "webkit/common/resource_type.h"
+
+namespace appcache {
+class AppCacheRequestHandler;
+class AppCacheServiceImpl;
+}
+
+namespace content {
+
+// An interceptor to hijack requests and potentially service them out of
+// the appcache.
+class CONTENT_EXPORT AppCacheInterceptor
+ : public net::URLRequest::Interceptor {
+ public:
+ // Registers a singleton instance with the net library.
+ // Should be called early in the IO thread prior to initiating requests.
+ static void EnsureRegistered() {
+ CHECK(GetInstance());
+ }
+
+ // Must be called to make a request eligible for retrieval from an appcache.
+ static void SetExtraRequestInfo(net::URLRequest* request,
+ appcache::AppCacheServiceImpl* service,
+ int process_id,
+ int host_id,
+ ResourceType::Type resource_type);
+
+ // May be called after response headers are complete to retrieve extra
+ // info about the response.
+ static void GetExtraResponseInfo(net::URLRequest* request,
+ int64* cache_id,
+ GURL* manifest_url);
+
+ // Methods to support cross site navigations.
+ static void PrepareForCrossSiteTransfer(net::URLRequest* request,
+ int old_process_id);
+ static void CompleteCrossSiteTransfer(net::URLRequest* request,
+ int new_process_id,
+ int new_host_id);
+
+ static AppCacheInterceptor* GetInstance();
+
+ protected:
+ // Override from net::URLRequest::Interceptor:
+ virtual net::URLRequestJob* MaybeIntercept(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) OVERRIDE;
+ virtual net::URLRequestJob* MaybeInterceptResponse(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) OVERRIDE;
+ virtual net::URLRequestJob* MaybeInterceptRedirect(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate,
+ const GURL& location) OVERRIDE;
+
+ private:
+ friend struct DefaultSingletonTraits<AppCacheInterceptor>;
+
+ AppCacheInterceptor();
+ virtual ~AppCacheInterceptor();
+
+ static void SetHandler(net::URLRequest* request,
+ appcache::AppCacheRequestHandler* handler);
+ static appcache::AppCacheRequestHandler* GetHandler(net::URLRequest* request);
+
+ DISALLOW_COPY_AND_ASSIGN(AppCacheInterceptor);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_APPCACHE_APPCACHE_INTERCEPTOR_H_
diff --git a/chromium/content/browser/appcache/appcache_quota_client_unittest.cc b/chromium/content/browser/appcache/appcache_quota_client_unittest.cc
new file mode 100644
index 00000000000..3a32e95dd2f
--- /dev/null
+++ b/chromium/content/browser/appcache/appcache_quota_client_unittest.cc
@@ -0,0 +1,437 @@
+// 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 <map>
+#include <set>
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/run_loop.h"
+#include "content/browser/appcache/mock_appcache_service.h"
+#include "net/base/net_errors.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/appcache/appcache_quota_client.h"
+
+using appcache::AppCacheQuotaClient;
+
+namespace content {
+
+// Declared to shorten the line lengths.
+static const quota::StorageType kTemp = quota::kStorageTypeTemporary;
+static const quota::StorageType kPerm = quota::kStorageTypePersistent;
+
+// Base class for our test fixtures.
+class AppCacheQuotaClientTest : public testing::Test {
+ public:
+ const GURL kOriginA;
+ const GURL kOriginB;
+ const GURL kOriginOther;
+
+ AppCacheQuotaClientTest()
+ : kOriginA("http://host"),
+ kOriginB("http://host:8000"),
+ kOriginOther("http://other"),
+ usage_(0),
+ delete_status_(quota::kQuotaStatusUnknown),
+ num_get_origin_usage_completions_(0),
+ num_get_origins_completions_(0),
+ num_delete_origins_completions_(0),
+ weak_factory_(this) {
+ }
+
+ int64 GetOriginUsage(
+ quota::QuotaClient* client,
+ const GURL& origin,
+ quota::StorageType type) {
+ usage_ = -1;
+ AsyncGetOriginUsage(client, origin, type);
+ base::RunLoop().RunUntilIdle();
+ return usage_;
+ }
+
+ const std::set<GURL>& GetOriginsForType(
+ quota::QuotaClient* client,
+ quota::StorageType type) {
+ origins_.clear();
+ AsyncGetOriginsForType(client, type);
+ base::RunLoop().RunUntilIdle();
+ return origins_;
+ }
+
+ const std::set<GURL>& GetOriginsForHost(
+ quota::QuotaClient* client,
+ quota::StorageType type,
+ const std::string& host) {
+ origins_.clear();
+ AsyncGetOriginsForHost(client, type, host);
+ base::RunLoop().RunUntilIdle();
+ return origins_;
+ }
+
+ quota::QuotaStatusCode DeleteOriginData(
+ quota::QuotaClient* client,
+ quota::StorageType type,
+ const GURL& origin) {
+ delete_status_ = quota::kQuotaStatusUnknown;
+ AsyncDeleteOriginData(client, type, origin);
+ base::RunLoop().RunUntilIdle();
+ return delete_status_;
+ }
+
+ void AsyncGetOriginUsage(
+ quota::QuotaClient* client,
+ const GURL& origin,
+ quota::StorageType type) {
+ client->GetOriginUsage(
+ origin, type,
+ base::Bind(&AppCacheQuotaClientTest::OnGetOriginUsageComplete,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void AsyncGetOriginsForType(
+ quota::QuotaClient* client,
+ quota::StorageType type) {
+ client->GetOriginsForType(
+ type,
+ base::Bind(&AppCacheQuotaClientTest::OnGetOriginsComplete,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void AsyncGetOriginsForHost(
+ quota::QuotaClient* client,
+ quota::StorageType type,
+ const std::string& host) {
+ client->GetOriginsForHost(
+ type, host,
+ base::Bind(&AppCacheQuotaClientTest::OnGetOriginsComplete,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void AsyncDeleteOriginData(
+ quota::QuotaClient* client,
+ quota::StorageType type,
+ const GURL& origin) {
+ client->DeleteOriginData(
+ origin, type,
+ base::Bind(&AppCacheQuotaClientTest::OnDeleteOriginDataComplete,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void SetUsageMapEntry(const GURL& origin, int64 usage) {
+ mock_service_.storage()->usage_map_[origin] = usage;
+ }
+
+ AppCacheQuotaClient* CreateClient() {
+ return new AppCacheQuotaClient(&mock_service_);
+ }
+
+ void Call_NotifyAppCacheReady(AppCacheQuotaClient* client) {
+ client->NotifyAppCacheReady();
+ }
+
+ void Call_NotifyAppCacheDestroyed(AppCacheQuotaClient* client) {
+ client->NotifyAppCacheDestroyed();
+ }
+
+ void Call_OnQuotaManagerDestroyed(AppCacheQuotaClient* client) {
+ client->OnQuotaManagerDestroyed();
+ }
+
+ protected:
+ void OnGetOriginUsageComplete(int64 usage) {
+ ++num_get_origin_usage_completions_;
+ usage_ = usage;
+ }
+
+ void OnGetOriginsComplete(const std::set<GURL>& origins) {
+ ++num_get_origins_completions_;
+ origins_ = origins;
+ }
+
+ void OnDeleteOriginDataComplete(quota::QuotaStatusCode status) {
+ ++num_delete_origins_completions_;
+ delete_status_ = status;
+ }
+
+ base::MessageLoop message_loop_;
+ int64 usage_;
+ std::set<GURL> origins_;
+ quota::QuotaStatusCode delete_status_;
+ int num_get_origin_usage_completions_;
+ int num_get_origins_completions_;
+ int num_delete_origins_completions_;
+ MockAppCacheService mock_service_;
+ base::WeakPtrFactory<AppCacheQuotaClientTest> weak_factory_;
+};
+
+
+TEST_F(AppCacheQuotaClientTest, BasicCreateDestroy) {
+ AppCacheQuotaClient* client = CreateClient();
+ Call_NotifyAppCacheReady(client);
+ Call_OnQuotaManagerDestroyed(client);
+ Call_NotifyAppCacheDestroyed(client);
+}
+
+TEST_F(AppCacheQuotaClientTest, EmptyService) {
+ AppCacheQuotaClient* client = CreateClient();
+ Call_NotifyAppCacheReady(client);
+
+ EXPECT_EQ(0, GetOriginUsage(client, kOriginA, kTemp));
+ EXPECT_EQ(0, GetOriginUsage(client, kOriginA, kPerm));
+ EXPECT_TRUE(GetOriginsForType(client, kTemp).empty());
+ EXPECT_TRUE(GetOriginsForType(client, kPerm).empty());
+ EXPECT_TRUE(GetOriginsForHost(client, kTemp, kOriginA.host()).empty());
+ EXPECT_TRUE(GetOriginsForHost(client, kPerm, kOriginA.host()).empty());
+ EXPECT_EQ(quota::kQuotaStatusOk, DeleteOriginData(client, kTemp, kOriginA));
+ EXPECT_EQ(quota::kQuotaStatusOk, DeleteOriginData(client, kPerm, kOriginA));
+
+ Call_NotifyAppCacheDestroyed(client);
+ Call_OnQuotaManagerDestroyed(client);
+}
+
+TEST_F(AppCacheQuotaClientTest, NoService) {
+ AppCacheQuotaClient* client = CreateClient();
+ Call_NotifyAppCacheReady(client);
+ Call_NotifyAppCacheDestroyed(client);
+
+ EXPECT_EQ(0, GetOriginUsage(client, kOriginA, kTemp));
+ EXPECT_EQ(0, GetOriginUsage(client, kOriginA, kPerm));
+ EXPECT_TRUE(GetOriginsForType(client, kTemp).empty());
+ EXPECT_TRUE(GetOriginsForType(client, kPerm).empty());
+ EXPECT_TRUE(GetOriginsForHost(client, kTemp, kOriginA.host()).empty());
+ EXPECT_TRUE(GetOriginsForHost(client, kPerm, kOriginA.host()).empty());
+ EXPECT_EQ(quota::kQuotaErrorAbort,
+ DeleteOriginData(client, kTemp, kOriginA));
+ EXPECT_EQ(quota::kQuotaErrorAbort,
+ DeleteOriginData(client, kPerm, kOriginA));
+
+ Call_OnQuotaManagerDestroyed(client);
+}
+
+TEST_F(AppCacheQuotaClientTest, GetOriginUsage) {
+ AppCacheQuotaClient* client = CreateClient();
+ Call_NotifyAppCacheReady(client);
+
+ SetUsageMapEntry(kOriginA, 1000);
+ EXPECT_EQ(1000, GetOriginUsage(client, kOriginA, kTemp));
+ EXPECT_EQ(0, GetOriginUsage(client, kOriginA, kPerm));
+
+ Call_NotifyAppCacheDestroyed(client);
+ Call_OnQuotaManagerDestroyed(client);
+}
+
+TEST_F(AppCacheQuotaClientTest, GetOriginsForHost) {
+ AppCacheQuotaClient* client = CreateClient();
+ Call_NotifyAppCacheReady(client);
+
+ EXPECT_EQ(kOriginA.host(), kOriginB.host());
+ EXPECT_NE(kOriginA.host(), kOriginOther.host());
+
+ std::set<GURL> origins = GetOriginsForHost(client, kTemp, kOriginA.host());
+ EXPECT_TRUE(origins.empty());
+
+ SetUsageMapEntry(kOriginA, 1000);
+ SetUsageMapEntry(kOriginB, 10);
+ SetUsageMapEntry(kOriginOther, 500);
+
+ origins = GetOriginsForHost(client, kTemp, kOriginA.host());
+ EXPECT_EQ(2ul, origins.size());
+ EXPECT_TRUE(origins.find(kOriginA) != origins.end());
+ EXPECT_TRUE(origins.find(kOriginB) != origins.end());
+
+ origins = GetOriginsForHost(client, kTemp, kOriginOther.host());
+ EXPECT_EQ(1ul, origins.size());
+ EXPECT_TRUE(origins.find(kOriginOther) != origins.end());
+
+ origins = GetOriginsForHost(client, kPerm, kOriginA.host());
+ EXPECT_TRUE(origins.empty());
+
+ Call_NotifyAppCacheDestroyed(client);
+ Call_OnQuotaManagerDestroyed(client);
+}
+
+TEST_F(AppCacheQuotaClientTest, GetOriginsForType) {
+ AppCacheQuotaClient* client = CreateClient();
+ Call_NotifyAppCacheReady(client);
+
+ EXPECT_TRUE(GetOriginsForType(client, kTemp).empty());
+ EXPECT_TRUE(GetOriginsForType(client, kPerm).empty());
+
+ SetUsageMapEntry(kOriginA, 1000);
+ SetUsageMapEntry(kOriginB, 10);
+
+ std::set<GURL> origins = GetOriginsForType(client, kTemp);
+ EXPECT_EQ(2ul, origins.size());
+ EXPECT_TRUE(origins.find(kOriginA) != origins.end());
+ EXPECT_TRUE(origins.find(kOriginB) != origins.end());
+
+ EXPECT_TRUE(GetOriginsForType(client, kPerm).empty());
+
+ Call_NotifyAppCacheDestroyed(client);
+ Call_OnQuotaManagerDestroyed(client);
+}
+
+TEST_F(AppCacheQuotaClientTest, DeleteOriginData) {
+ AppCacheQuotaClient* client = CreateClient();
+ Call_NotifyAppCacheReady(client);
+
+ // Perm deletions are short circuited in the Client and
+ // should not reach the AppCacheServiceImpl.
+ EXPECT_EQ(quota::kQuotaStatusOk,
+ DeleteOriginData(client, kPerm, kOriginA));
+ EXPECT_EQ(0, mock_service_.delete_called_count());
+
+ EXPECT_EQ(quota::kQuotaStatusOk,
+ DeleteOriginData(client, kTemp, kOriginA));
+ EXPECT_EQ(1, mock_service_.delete_called_count());
+
+ mock_service_.set_mock_delete_appcaches_for_origin_result(
+ net::ERR_ABORTED);
+ EXPECT_EQ(quota::kQuotaErrorAbort,
+ DeleteOriginData(client, kTemp, kOriginA));
+ EXPECT_EQ(2, mock_service_.delete_called_count());
+
+ Call_OnQuotaManagerDestroyed(client);
+ Call_NotifyAppCacheDestroyed(client);
+}
+
+TEST_F(AppCacheQuotaClientTest, PendingRequests) {
+ AppCacheQuotaClient* client = CreateClient();
+
+ SetUsageMapEntry(kOriginA, 1000);
+ SetUsageMapEntry(kOriginB, 10);
+ SetUsageMapEntry(kOriginOther, 500);
+
+ // Queue up some reqeusts.
+ AsyncGetOriginUsage(client, kOriginA, kPerm);
+ AsyncGetOriginUsage(client, kOriginB, kTemp);
+ AsyncGetOriginsForType(client, kPerm);
+ AsyncGetOriginsForType(client, kTemp);
+ AsyncGetOriginsForHost(client, kTemp, kOriginA.host());
+ AsyncGetOriginsForHost(client, kTemp, kOriginOther.host());
+ AsyncDeleteOriginData(client, kTemp, kOriginA);
+ AsyncDeleteOriginData(client, kPerm, kOriginA);
+ AsyncDeleteOriginData(client, kTemp, kOriginB);
+
+ EXPECT_EQ(0, num_get_origin_usage_completions_);
+ EXPECT_EQ(0, num_get_origins_completions_);
+ EXPECT_EQ(0, num_delete_origins_completions_);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, num_get_origin_usage_completions_);
+ EXPECT_EQ(0, num_get_origins_completions_);
+ EXPECT_EQ(0, num_delete_origins_completions_);
+
+ // Pending requests should get serviced when the appcache is ready.
+ Call_NotifyAppCacheReady(client);
+ EXPECT_EQ(2, num_get_origin_usage_completions_);
+ EXPECT_EQ(4, num_get_origins_completions_);
+ EXPECT_EQ(0, num_delete_origins_completions_);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(3, num_delete_origins_completions_); // deletes are really async
+
+ // They should be serviced in order requested.
+ EXPECT_EQ(10, usage_);
+ EXPECT_EQ(1ul, origins_.size());
+ EXPECT_TRUE(origins_.find(kOriginOther) != origins_.end());
+
+ Call_NotifyAppCacheDestroyed(client);
+ Call_OnQuotaManagerDestroyed(client);
+}
+
+TEST_F(AppCacheQuotaClientTest, DestroyServiceWithPending) {
+ AppCacheQuotaClient* client = CreateClient();
+
+ SetUsageMapEntry(kOriginA, 1000);
+ SetUsageMapEntry(kOriginB, 10);
+ SetUsageMapEntry(kOriginOther, 500);
+
+ // Queue up some reqeusts prior to being ready.
+ AsyncGetOriginUsage(client, kOriginA, kPerm);
+ AsyncGetOriginUsage(client, kOriginB, kTemp);
+ AsyncGetOriginsForType(client, kPerm);
+ AsyncGetOriginsForType(client, kTemp);
+ AsyncGetOriginsForHost(client, kTemp, kOriginA.host());
+ AsyncGetOriginsForHost(client, kTemp, kOriginOther.host());
+ AsyncDeleteOriginData(client, kTemp, kOriginA);
+ AsyncDeleteOriginData(client, kPerm, kOriginA);
+ AsyncDeleteOriginData(client, kTemp, kOriginB);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, num_get_origin_usage_completions_);
+ EXPECT_EQ(0, num_get_origins_completions_);
+ EXPECT_EQ(0, num_delete_origins_completions_);
+
+ // Kill the service.
+ Call_NotifyAppCacheDestroyed(client);
+
+ // All should have been aborted and called completion.
+ EXPECT_EQ(2, num_get_origin_usage_completions_);
+ EXPECT_EQ(4, num_get_origins_completions_);
+ EXPECT_EQ(3, num_delete_origins_completions_);
+ EXPECT_EQ(0, usage_);
+ EXPECT_TRUE(origins_.empty());
+ EXPECT_EQ(quota::kQuotaErrorAbort, delete_status_);
+
+ Call_OnQuotaManagerDestroyed(client);
+}
+
+TEST_F(AppCacheQuotaClientTest, DestroyQuotaManagerWithPending) {
+ AppCacheQuotaClient* client = CreateClient();
+
+ SetUsageMapEntry(kOriginA, 1000);
+ SetUsageMapEntry(kOriginB, 10);
+ SetUsageMapEntry(kOriginOther, 500);
+
+ // Queue up some reqeusts prior to being ready.
+ AsyncGetOriginUsage(client, kOriginA, kPerm);
+ AsyncGetOriginUsage(client, kOriginB, kTemp);
+ AsyncGetOriginsForType(client, kPerm);
+ AsyncGetOriginsForType(client, kTemp);
+ AsyncGetOriginsForHost(client, kTemp, kOriginA.host());
+ AsyncGetOriginsForHost(client, kTemp, kOriginOther.host());
+ AsyncDeleteOriginData(client, kTemp, kOriginA);
+ AsyncDeleteOriginData(client, kPerm, kOriginA);
+ AsyncDeleteOriginData(client, kTemp, kOriginB);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, num_get_origin_usage_completions_);
+ EXPECT_EQ(0, num_get_origins_completions_);
+ EXPECT_EQ(0, num_delete_origins_completions_);
+
+ // Kill the quota manager.
+ Call_OnQuotaManagerDestroyed(client);
+ Call_NotifyAppCacheReady(client);
+
+ // Callbacks should be deleted and not called.
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, num_get_origin_usage_completions_);
+ EXPECT_EQ(0, num_get_origins_completions_);
+ EXPECT_EQ(0, num_delete_origins_completions_);
+
+ Call_NotifyAppCacheDestroyed(client);
+}
+
+TEST_F(AppCacheQuotaClientTest, DestroyWithDeleteInProgress) {
+ AppCacheQuotaClient* client = CreateClient();
+ Call_NotifyAppCacheReady(client);
+
+ // Start an async delete.
+ AsyncDeleteOriginData(client, kTemp, kOriginB);
+ EXPECT_EQ(0, num_delete_origins_completions_);
+
+ // Kill the service.
+ Call_NotifyAppCacheDestroyed(client);
+
+ // Should have been aborted.
+ EXPECT_EQ(1, num_delete_origins_completions_);
+ EXPECT_EQ(quota::kQuotaErrorAbort, delete_status_);
+
+ // A real completion callback from the service should
+ // be dropped if it comes in after NotifyAppCacheDestroyed.
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(1, num_delete_origins_completions_);
+ EXPECT_EQ(quota::kQuotaErrorAbort, delete_status_);
+
+ Call_OnQuotaManagerDestroyed(client);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/appcache/appcache_request_handler_unittest.cc b/chromium/content/browser/appcache/appcache_request_handler_unittest.cc
new file mode 100644
index 00000000000..df6fb330d24
--- /dev/null
+++ b/chromium/content/browser/appcache/appcache_request_handler_unittest.cc
@@ -0,0 +1,1014 @@
+// 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 <stack>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/message_loop/message_loop.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "content/browser/appcache/mock_appcache_policy.h"
+#include "content/browser/appcache/mock_appcache_service.h"
+#include "net/base/net_errors.h"
+#include "net/base/request_priority.h"
+#include "net/http/http_response_headers.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_error_job.h"
+#include "net/url_request/url_request_job_factory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/appcache/appcache.h"
+#include "webkit/browser/appcache/appcache_backend_impl.h"
+#include "webkit/browser/appcache/appcache_request_handler.h"
+#include "webkit/browser/appcache/appcache_url_request_job.h"
+
+using appcache::AppCache;
+using appcache::AppCacheBackendImpl;
+using appcache::AppCacheEntry;
+using appcache::AppCacheFrontend;
+using appcache::AppCacheGroup;
+using appcache::AppCacheHost;
+using appcache::AppCacheInfo;
+using appcache::AppCacheRequestHandler;
+using appcache::AppCacheURLRequestJob;
+using appcache::kAppCacheNoCacheId;
+
+namespace content {
+
+static const int kMockProcessId = 1;
+
+class AppCacheRequestHandlerTest : public testing::Test {
+ public:
+ class MockFrontend : public AppCacheFrontend {
+ public:
+ virtual void OnCacheSelected(
+ int host_id, const appcache::AppCacheInfo& info) OVERRIDE {}
+
+ virtual void OnStatusChanged(const std::vector<int>& host_ids,
+ appcache::AppCacheStatus status) OVERRIDE {}
+
+ virtual void OnEventRaised(const std::vector<int>& host_ids,
+ appcache::AppCacheEventID event_id) OVERRIDE {}
+
+ virtual void OnErrorEventRaised(
+ const std::vector<int>& host_ids,
+ const appcache::AppCacheErrorDetails& details) OVERRIDE {}
+
+ virtual void OnProgressEventRaised(const std::vector<int>& host_ids,
+ const GURL& url,
+ int num_total,
+ int num_complete) OVERRIDE {
+ }
+
+ virtual void OnLogMessage(int host_id,
+ appcache::AppCacheLogLevel log_level,
+ const std::string& message) OVERRIDE {
+ }
+
+ virtual void OnContentBlocked(int host_id,
+ const GURL& manifest_url) OVERRIDE {}
+ };
+
+ // Helper callback to run a test on our io_thread. The io_thread is spun up
+ // once and reused for all tests.
+ template <class Method>
+ void MethodWrapper(Method method) {
+ SetUpTest();
+ (this->*method)();
+ }
+
+ // Subclasses to simulate particular responses so test cases can
+ // exercise fallback code paths.
+
+ class MockURLRequestDelegate : public net::URLRequest::Delegate {
+ virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE {}
+ virtual void OnReadCompleted(net::URLRequest* request,
+ int bytes_read) OVERRIDE {
+ }
+ };
+
+ class MockURLRequestJob : public net::URLRequestJob {
+ public:
+ MockURLRequestJob(net::URLRequest* request,
+ net::NetworkDelegate* network_delegate,
+ int response_code)
+ : net::URLRequestJob(request, network_delegate),
+ response_code_(response_code),
+ has_response_info_(false) {}
+ MockURLRequestJob(net::URLRequest* request,
+ net::NetworkDelegate* network_delegate,
+ const net::HttpResponseInfo& info)
+ : net::URLRequestJob(request, network_delegate),
+ response_code_(info.headers->response_code()),
+ has_response_info_(true),
+ response_info_(info) {}
+
+ protected:
+ virtual ~MockURLRequestJob() {}
+ virtual void Start() OVERRIDE {
+ NotifyHeadersComplete();
+ }
+ virtual int GetResponseCode() const OVERRIDE {
+ return response_code_;
+ }
+ virtual void GetResponseInfo(
+ net::HttpResponseInfo* info) OVERRIDE {
+ if (!has_response_info_)
+ return;
+ *info = response_info_;
+ }
+
+ private:
+ int response_code_;
+ bool has_response_info_;
+ net::HttpResponseInfo response_info_;
+ };
+
+ class MockURLRequestJobFactory : public net::URLRequestJobFactory {
+ public:
+ MockURLRequestJobFactory() : job_(NULL) {
+ }
+
+ virtual ~MockURLRequestJobFactory() {
+ DCHECK(!job_);
+ }
+
+ void SetJob(net::URLRequestJob* job) {
+ job_ = job;
+ }
+
+ virtual net::URLRequestJob* MaybeCreateJobWithProtocolHandler(
+ const std::string& scheme,
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) const OVERRIDE {
+ if (job_) {
+ net::URLRequestJob* temp = job_;
+ job_ = NULL;
+ return temp;
+ } else {
+ // Some of these tests trigger UpdateJobs which start URLRequests.
+ // We short circuit those be returning error jobs.
+ return new net::URLRequestErrorJob(request,
+ network_delegate,
+ net::ERR_INTERNET_DISCONNECTED);
+ }
+ }
+
+ virtual bool IsHandledProtocol(const std::string& scheme) const OVERRIDE {
+ return scheme == "http";
+ };
+
+ virtual bool IsHandledURL(const GURL& url) const OVERRIDE {
+ return url.SchemeIs("http");
+ }
+
+ virtual bool IsSafeRedirectTarget(const GURL& location) const OVERRIDE {
+ return false;
+ }
+
+ private:
+ mutable net::URLRequestJob* job_;
+ };
+
+ class MockURLRequest : public net::URLRequest {
+ public:
+ MockURLRequest(const GURL& url, net::URLRequestContext* context)
+ : net::URLRequest(url, net::DEFAULT_PRIORITY, NULL, context) {}
+
+
+ MockURLRequestDelegate delegate_;
+ };
+
+ static void SetUpTestCase() {
+ io_thread_.reset(new base::Thread("AppCacheRequestHandlerTest Thread"));
+ base::Thread::Options options(base::MessageLoop::TYPE_IO, 0);
+ io_thread_->StartWithOptions(options);
+ }
+
+ static void TearDownTestCase() {
+ io_thread_.reset(NULL);
+ }
+
+ // Test harness --------------------------------------------------
+
+ AppCacheRequestHandlerTest() : host_(NULL) {}
+
+ template <class Method>
+ void RunTestOnIOThread(Method method) {
+ test_finished_event_ .reset(new base::WaitableEvent(false, false));
+ io_thread_->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&AppCacheRequestHandlerTest::MethodWrapper<Method>,
+ base::Unretained(this), method));
+ test_finished_event_->Wait();
+ }
+
+ void SetUpTest() {
+ DCHECK(base::MessageLoop::current() == io_thread_->message_loop());
+ mock_service_.reset(new MockAppCacheService);
+ mock_service_->set_request_context(&empty_context_);
+ mock_policy_.reset(new MockAppCachePolicy);
+ mock_service_->set_appcache_policy(mock_policy_.get());
+ mock_frontend_.reset(new MockFrontend);
+ backend_impl_.reset(new AppCacheBackendImpl);
+ backend_impl_->Initialize(mock_service_.get(), mock_frontend_.get(),
+ kMockProcessId);
+ const int kHostId = 1;
+ backend_impl_->RegisterHost(kHostId);
+ host_ = backend_impl_->GetHost(kHostId);
+ job_factory_.reset(new MockURLRequestJobFactory());
+ empty_context_.set_job_factory(job_factory_.get());
+ }
+
+ void TearDownTest() {
+ DCHECK(base::MessageLoop::current() == io_thread_->message_loop());
+ job_ = NULL;
+ handler_.reset();
+ request_.reset();
+ backend_impl_.reset();
+ mock_frontend_.reset();
+ mock_service_.reset();
+ mock_policy_.reset();
+ job_factory_.reset();
+ host_ = NULL;
+ }
+
+ void TestFinished() {
+ // We unwind the stack prior to finishing up to let stack
+ // based objects get deleted.
+ DCHECK(base::MessageLoop::current() == io_thread_->message_loop());
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&AppCacheRequestHandlerTest::TestFinishedUnwound,
+ base::Unretained(this)));
+ }
+
+ void TestFinishedUnwound() {
+ TearDownTest();
+ test_finished_event_->Signal();
+ }
+
+ void PushNextTask(const base::Closure& task) {
+ task_stack_.push(task);
+ }
+
+ void ScheduleNextTask() {
+ DCHECK(base::MessageLoop::current() == io_thread_->message_loop());
+ if (task_stack_.empty()) {
+ TestFinished();
+ return;
+ }
+ base::MessageLoop::current()->PostTask(FROM_HERE, task_stack_.top());
+ task_stack_.pop();
+ }
+
+ // MainResource_Miss --------------------------------------------------
+
+ void MainResource_Miss() {
+ PushNextTask(
+ base::Bind(&AppCacheRequestHandlerTest::Verify_MainResource_Miss,
+ base::Unretained(this)));
+
+ request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_));
+ handler_.reset(host_->CreateRequestHandler(request_.get(),
+ ResourceType::MAIN_FRAME));
+ EXPECT_TRUE(handler_.get());
+
+ job_ = handler_->MaybeLoadResource(request_.get(),
+ request_->context()->network_delegate());
+ EXPECT_TRUE(job_.get());
+ EXPECT_TRUE(job_->is_waiting());
+
+ // We have to wait for completion of storage->FindResponseForMainRequest.
+ ScheduleNextTask();
+ }
+
+ void Verify_MainResource_Miss() {
+ EXPECT_FALSE(job_->is_waiting());
+ EXPECT_TRUE(job_->is_delivering_network_response());
+
+ int64 cache_id = kAppCacheNoCacheId;
+ GURL manifest_url;
+ handler_->GetExtraResponseInfo(&cache_id, &manifest_url);
+ EXPECT_EQ(kAppCacheNoCacheId, cache_id);
+ EXPECT_EQ(GURL(), manifest_url);
+ EXPECT_EQ(0, handler_->found_group_id_);
+
+ AppCacheURLRequestJob* fallback_job;
+ fallback_job = handler_->MaybeLoadFallbackForRedirect(
+ request_.get(),
+ request_->context()->network_delegate(),
+ GURL("http://blah/redirect"));
+ EXPECT_FALSE(fallback_job);
+ fallback_job = handler_->MaybeLoadFallbackForResponse(
+ request_.get(), request_->context()->network_delegate());
+ EXPECT_FALSE(fallback_job);
+
+ EXPECT_TRUE(host_->preferred_manifest_url().is_empty());
+
+ TestFinished();
+ }
+
+ // MainResource_Hit --------------------------------------------------
+
+ void MainResource_Hit() {
+ PushNextTask(
+ base::Bind(&AppCacheRequestHandlerTest::Verify_MainResource_Hit,
+ base::Unretained(this)));
+
+ request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_));
+ handler_.reset(host_->CreateRequestHandler(request_.get(),
+ ResourceType::MAIN_FRAME));
+ EXPECT_TRUE(handler_.get());
+
+ mock_storage()->SimulateFindMainResource(
+ AppCacheEntry(AppCacheEntry::EXPLICIT, 1),
+ GURL(), AppCacheEntry(),
+ 1, 2, GURL("http://blah/manifest/"));
+
+ job_ = handler_->MaybeLoadResource(request_.get(),
+ request_->context()->network_delegate());
+ EXPECT_TRUE(job_.get());
+ EXPECT_TRUE(job_->is_waiting());
+
+ // We have to wait for completion of storage->FindResponseForMainRequest.
+ ScheduleNextTask();
+ }
+
+ void Verify_MainResource_Hit() {
+ EXPECT_FALSE(job_->is_waiting());
+ EXPECT_TRUE(job_->is_delivering_appcache_response());
+
+ int64 cache_id = kAppCacheNoCacheId;
+ GURL manifest_url;
+ handler_->GetExtraResponseInfo(&cache_id, &manifest_url);
+ EXPECT_EQ(1, cache_id);
+ EXPECT_EQ(GURL("http://blah/manifest/"), manifest_url);
+ EXPECT_EQ(2, handler_->found_group_id_);
+
+ AppCacheURLRequestJob* fallback_job;
+ fallback_job = handler_->MaybeLoadFallbackForResponse(
+ request_.get(), request_->context()->network_delegate());
+ EXPECT_FALSE(fallback_job);
+
+ EXPECT_EQ(GURL("http://blah/manifest/"),
+ host_->preferred_manifest_url());
+
+ TestFinished();
+ }
+
+ // MainResource_Fallback --------------------------------------------------
+
+ void MainResource_Fallback() {
+ PushNextTask(
+ base::Bind(&AppCacheRequestHandlerTest::Verify_MainResource_Fallback,
+ base::Unretained(this)));
+
+ request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_));
+ handler_.reset(host_->CreateRequestHandler(request_.get(),
+ ResourceType::MAIN_FRAME));
+ EXPECT_TRUE(handler_.get());
+
+ mock_storage()->SimulateFindMainResource(
+ AppCacheEntry(),
+ GURL("http://blah/fallbackurl"),
+ AppCacheEntry(AppCacheEntry::EXPLICIT, 1),
+ 1, 2, GURL("http://blah/manifest/"));
+
+ job_ = handler_->MaybeLoadResource(request_.get(),
+ request_->context()->network_delegate());
+ EXPECT_TRUE(job_.get());
+ EXPECT_TRUE(job_->is_waiting());
+
+ // We have to wait for completion of storage->FindResponseForMainRequest.
+ ScheduleNextTask();
+ }
+
+ void SimulateResponseCode(int response_code) {
+ job_factory_->SetJob(
+ new MockURLRequestJob(
+ request_.get(),
+ request_->context()->network_delegate(),
+ response_code));
+ request_->Start();
+ // All our simulation needs to satisfy are the following two DCHECKs
+ DCHECK(request_->status().is_success());
+ DCHECK_EQ(response_code, request_->GetResponseCode());
+ }
+
+ void SimulateResponseInfo(const net::HttpResponseInfo& info) {
+ job_factory_->SetJob(
+ new MockURLRequestJob(
+ request_.get(),
+ request_->context()->network_delegate(), info));
+ request_->set_delegate(&request_->delegate_);
+ request_->Start();
+ }
+
+ void Verify_MainResource_Fallback() {
+ EXPECT_FALSE(job_->is_waiting());
+ EXPECT_TRUE(job_->is_delivering_network_response());
+
+ // When the request is restarted, the existing job is dropped so a
+ // real network job gets created. We expect NULL here which will cause
+ // the net library to create a real job.
+ job_ = handler_->MaybeLoadResource(request_.get(),
+ request_->context()->network_delegate());
+ EXPECT_FALSE(job_.get());
+
+ // Simulate an http error of the real network job.
+ SimulateResponseCode(500);
+
+ job_ = handler_->MaybeLoadFallbackForResponse(
+ request_.get(), request_->context()->network_delegate());
+ EXPECT_TRUE(job_.get());
+ EXPECT_TRUE(job_->is_delivering_appcache_response());
+
+ int64 cache_id = kAppCacheNoCacheId;
+ GURL manifest_url;
+ handler_->GetExtraResponseInfo(&cache_id, &manifest_url);
+ EXPECT_EQ(1, cache_id);
+ EXPECT_EQ(GURL("http://blah/manifest/"), manifest_url);
+ EXPECT_TRUE(host_->main_resource_was_namespace_entry_);
+ EXPECT_EQ(GURL("http://blah/fallbackurl"), host_->namespace_entry_url_);
+
+ EXPECT_EQ(GURL("http://blah/manifest/"),
+ host_->preferred_manifest_url());
+
+ TestFinished();
+ }
+
+ // MainResource_FallbackOverride --------------------------------------------
+
+ void MainResource_FallbackOverride() {
+ PushNextTask(base::Bind(
+ &AppCacheRequestHandlerTest::Verify_MainResource_FallbackOverride,
+ base::Unretained(this)));
+
+ request_.reset(new MockURLRequest(GURL("http://blah/fallback-override"),
+ &empty_context_));
+ handler_.reset(host_->CreateRequestHandler(request_.get(),
+ ResourceType::MAIN_FRAME));
+ EXPECT_TRUE(handler_.get());
+
+ mock_storage()->SimulateFindMainResource(
+ AppCacheEntry(),
+ GURL("http://blah/fallbackurl"),
+ AppCacheEntry(AppCacheEntry::EXPLICIT, 1),
+ 1, 2, GURL("http://blah/manifest/"));
+
+ job_ = handler_->MaybeLoadResource(request_.get(),
+ request_->context()->network_delegate());
+ EXPECT_TRUE(job_.get());
+ EXPECT_TRUE(job_->is_waiting());
+
+ // We have to wait for completion of storage->FindResponseForMainRequest.
+ ScheduleNextTask();
+ }
+
+ void Verify_MainResource_FallbackOverride() {
+ EXPECT_FALSE(job_->is_waiting());
+ EXPECT_TRUE(job_->is_delivering_network_response());
+
+ // When the request is restarted, the existing job is dropped so a
+ // real network job gets created. We expect NULL here which will cause
+ // the net library to create a real job.
+ job_ = handler_->MaybeLoadResource(request_.get(),
+ request_->context()->network_delegate());
+ EXPECT_FALSE(job_.get());
+
+ // Simulate an http error of the real network job, but with custom
+ // headers that override the fallback behavior.
+ const char kOverrideHeaders[] =
+ "HTTP/1.1 404 BOO HOO\0"
+ "x-chromium-appcache-fallback-override: disallow-fallback\0"
+ "\0";
+ net::HttpResponseInfo info;
+ info.headers = new net::HttpResponseHeaders(
+ std::string(kOverrideHeaders, arraysize(kOverrideHeaders)));
+ SimulateResponseInfo(info);
+
+ job_ = handler_->MaybeLoadFallbackForResponse(
+ request_.get(), request_->context()->network_delegate());
+ EXPECT_FALSE(job_.get());
+
+ TestFinished();
+ }
+
+ // SubResource_Miss_WithNoCacheSelected ----------------------------------
+
+ void SubResource_Miss_WithNoCacheSelected() {
+ request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_));
+ handler_.reset(host_->CreateRequestHandler(request_.get(),
+ ResourceType::SUB_RESOURCE));
+
+ // We avoid creating handler when possible, sub-resource requests are not
+ // subject to retrieval from an appcache when there's no associated cache.
+ EXPECT_FALSE(handler_.get());
+
+ TestFinished();
+ }
+
+ // SubResource_Miss_WithCacheSelected ----------------------------------
+
+ void SubResource_Miss_WithCacheSelected() {
+ // A sub-resource load where the resource is not in an appcache, or
+ // in a network or fallback namespace, should result in a failed request.
+ host_->AssociateCompleteCache(MakeNewCache());
+
+ request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_));
+ handler_.reset(host_->CreateRequestHandler(request_.get(),
+ ResourceType::SUB_RESOURCE));
+ EXPECT_TRUE(handler_.get());
+
+ job_ = handler_->MaybeLoadResource(request_.get(),
+ request_->context()->network_delegate());
+ EXPECT_TRUE(job_.get());
+ EXPECT_TRUE(job_->is_delivering_error_response());
+
+ AppCacheURLRequestJob* fallback_job;
+ fallback_job = handler_->MaybeLoadFallbackForRedirect(
+ request_.get(),
+ request_->context()->network_delegate(),
+ GURL("http://blah/redirect"));
+ EXPECT_FALSE(fallback_job);
+ fallback_job = handler_->MaybeLoadFallbackForResponse(
+ request_.get(), request_->context()->network_delegate());
+ EXPECT_FALSE(fallback_job);
+
+ TestFinished();
+ }
+
+ // SubResource_Miss_WithWaitForCacheSelection -----------------------------
+
+ void SubResource_Miss_WithWaitForCacheSelection() {
+ // Precondition, the host is waiting on cache selection.
+ scoped_refptr<AppCache> cache(MakeNewCache());
+ host_->pending_selected_cache_id_ = cache->cache_id();
+ host_->set_preferred_manifest_url(cache->owning_group()->manifest_url());
+
+ request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_));
+ handler_.reset(host_->CreateRequestHandler(request_.get(),
+ ResourceType::SUB_RESOURCE));
+ EXPECT_TRUE(handler_.get());
+ job_ = handler_->MaybeLoadResource(request_.get(),
+ request_->context()->network_delegate());
+ EXPECT_TRUE(job_.get());
+ EXPECT_TRUE(job_->is_waiting());
+
+ host_->FinishCacheSelection(cache.get(), NULL);
+ EXPECT_FALSE(job_->is_waiting());
+ EXPECT_TRUE(job_->is_delivering_error_response());
+
+ AppCacheURLRequestJob* fallback_job;
+ fallback_job = handler_->MaybeLoadFallbackForRedirect(
+ request_.get(),
+ request_->context()->network_delegate(),
+ GURL("http://blah/redirect"));
+ EXPECT_FALSE(fallback_job);
+ fallback_job = handler_->MaybeLoadFallbackForResponse(
+ request_.get(), request_->context()->network_delegate());
+ EXPECT_FALSE(fallback_job);
+
+ TestFinished();
+ }
+
+ // SubResource_Hit -----------------------------
+
+ void SubResource_Hit() {
+ host_->AssociateCompleteCache(MakeNewCache());
+
+ mock_storage()->SimulateFindSubResource(
+ AppCacheEntry(AppCacheEntry::EXPLICIT, 1), AppCacheEntry(), false);
+
+ request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_));
+ handler_.reset(host_->CreateRequestHandler(request_.get(),
+ ResourceType::SUB_RESOURCE));
+ EXPECT_TRUE(handler_.get());
+ job_ = handler_->MaybeLoadResource(request_.get(),
+ request_->context()->network_delegate());
+ EXPECT_TRUE(job_.get());
+ EXPECT_TRUE(job_->is_delivering_appcache_response());
+
+ AppCacheURLRequestJob* fallback_job;
+ fallback_job = handler_->MaybeLoadFallbackForRedirect(
+ request_.get(),
+ request_->context()->network_delegate(),
+ GURL("http://blah/redirect"));
+ EXPECT_FALSE(fallback_job);
+ fallback_job = handler_->MaybeLoadFallbackForResponse(
+ request_.get(), request_->context()->network_delegate());
+ EXPECT_FALSE(fallback_job);
+
+ TestFinished();
+ }
+
+ // SubResource_RedirectFallback -----------------------------
+
+ void SubResource_RedirectFallback() {
+ // Redirects to resources in the a different origin are subject to
+ // fallback namespaces.
+ host_->AssociateCompleteCache(MakeNewCache());
+
+ mock_storage()->SimulateFindSubResource(
+ AppCacheEntry(), AppCacheEntry(AppCacheEntry::EXPLICIT, 1), false);
+
+ request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_));
+ handler_.reset(host_->CreateRequestHandler(request_.get(),
+ ResourceType::SUB_RESOURCE));
+ EXPECT_TRUE(handler_.get());
+ job_ = handler_->MaybeLoadResource(request_.get(),
+ request_->context()->network_delegate());
+ EXPECT_FALSE(job_.get());
+
+ job_ = handler_->MaybeLoadFallbackForRedirect(
+ request_.get(),
+ request_->context()->network_delegate(),
+ GURL("http://not_blah/redirect"));
+ EXPECT_TRUE(job_.get());
+ EXPECT_TRUE(job_->is_delivering_appcache_response());
+
+ AppCacheURLRequestJob* fallback_job;
+ fallback_job = handler_->MaybeLoadFallbackForResponse(
+ request_.get(), request_->context()->network_delegate());
+ EXPECT_FALSE(fallback_job);
+
+ TestFinished();
+ }
+
+ // SubResource_NoRedirectFallback -----------------------------
+
+ void SubResource_NoRedirectFallback() {
+ // Redirects to resources in the same-origin are not subject to
+ // fallback namespaces.
+ host_->AssociateCompleteCache(MakeNewCache());
+
+ mock_storage()->SimulateFindSubResource(
+ AppCacheEntry(), AppCacheEntry(AppCacheEntry::EXPLICIT, 1), false);
+
+ request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_));
+ handler_.reset(host_->CreateRequestHandler(request_.get(),
+ ResourceType::SUB_RESOURCE));
+ EXPECT_TRUE(handler_.get());
+ job_ = handler_->MaybeLoadResource(request_.get(),
+ request_->context()->network_delegate());
+ EXPECT_FALSE(job_.get());
+
+ AppCacheURLRequestJob* fallback_job;
+ fallback_job = handler_->MaybeLoadFallbackForRedirect(
+ request_.get(),
+ request_->context()->network_delegate(),
+ GURL("http://blah/redirect"));
+ EXPECT_FALSE(fallback_job);
+
+ SimulateResponseCode(200);
+ fallback_job = handler_->MaybeLoadFallbackForResponse(
+ request_.get(), request_->context()->network_delegate());
+ EXPECT_FALSE(fallback_job);
+
+ TestFinished();
+ }
+
+ // SubResource_Network -----------------------------
+
+ void SubResource_Network() {
+ // A sub-resource load where the resource is in a network namespace,
+ // should result in the system using a 'real' job to do the network
+ // retrieval.
+ host_->AssociateCompleteCache(MakeNewCache());
+
+ mock_storage()->SimulateFindSubResource(
+ AppCacheEntry(), AppCacheEntry(), true);
+
+ request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_));
+ handler_.reset(host_->CreateRequestHandler(request_.get(),
+ ResourceType::SUB_RESOURCE));
+ EXPECT_TRUE(handler_.get());
+ job_ = handler_->MaybeLoadResource(request_.get(),
+ request_->context()->network_delegate());
+ EXPECT_FALSE(job_.get());
+
+ AppCacheURLRequestJob* fallback_job;
+ fallback_job = handler_->MaybeLoadFallbackForRedirect(
+ request_.get(),
+ request_->context()->network_delegate(),
+ GURL("http://blah/redirect"));
+ EXPECT_FALSE(fallback_job);
+ fallback_job = handler_->MaybeLoadFallbackForResponse(
+ request_.get(), request_->context()->network_delegate());
+ EXPECT_FALSE(fallback_job);
+
+ TestFinished();
+ }
+
+ // DestroyedHost -----------------------------
+
+ void DestroyedHost() {
+ host_->AssociateCompleteCache(MakeNewCache());
+
+ mock_storage()->SimulateFindSubResource(
+ AppCacheEntry(AppCacheEntry::EXPLICIT, 1), AppCacheEntry(), false);
+
+ request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_));
+ handler_.reset(host_->CreateRequestHandler(request_.get(),
+ ResourceType::SUB_RESOURCE));
+ EXPECT_TRUE(handler_.get());
+
+ backend_impl_->UnregisterHost(1);
+ host_ = NULL;
+
+ EXPECT_FALSE(handler_->MaybeLoadResource(
+ request_.get(), request_->context()->network_delegate()));
+ EXPECT_FALSE(handler_->MaybeLoadFallbackForRedirect(
+ request_.get(),
+ request_->context()->network_delegate(),
+ GURL("http://blah/redirect")));
+ EXPECT_FALSE(handler_->MaybeLoadFallbackForResponse(
+ request_.get(), request_->context()->network_delegate()));
+
+ TestFinished();
+ }
+
+ // DestroyedHostWithWaitingJob -----------------------------
+
+ void DestroyedHostWithWaitingJob() {
+ // Precondition, the host is waiting on cache selection.
+ host_->pending_selected_cache_id_ = 1;
+
+ request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_));
+ handler_.reset(host_->CreateRequestHandler(request_.get(),
+ ResourceType::SUB_RESOURCE));
+ EXPECT_TRUE(handler_.get());
+
+ job_ = handler_->MaybeLoadResource(request_.get(),
+ request_->context()->network_delegate());
+ EXPECT_TRUE(job_.get());
+ EXPECT_TRUE(job_->is_waiting());
+
+ backend_impl_->UnregisterHost(1);
+ host_ = NULL;
+ EXPECT_TRUE(job_->has_been_killed());
+
+ EXPECT_FALSE(handler_->MaybeLoadResource(
+ request_.get(), request_->context()->network_delegate()));
+ EXPECT_FALSE(handler_->MaybeLoadFallbackForRedirect(
+ request_.get(),
+ request_->context()->network_delegate(),
+ GURL("http://blah/redirect")));
+ EXPECT_FALSE(handler_->MaybeLoadFallbackForResponse(
+ request_.get(), request_->context()->network_delegate()));
+
+ TestFinished();
+ }
+
+ // UnsupportedScheme -----------------------------
+
+ void UnsupportedScheme() {
+ // Precondition, the host is waiting on cache selection.
+ host_->pending_selected_cache_id_ = 1;
+
+ request_.reset(new MockURLRequest(GURL("ftp://blah/"), &empty_context_));
+ handler_.reset(host_->CreateRequestHandler(request_.get(),
+ ResourceType::SUB_RESOURCE));
+ EXPECT_TRUE(handler_.get()); // we could redirect to http (conceivably)
+
+ EXPECT_FALSE(handler_->MaybeLoadResource(
+ request_.get(), request_->context()->network_delegate()));
+ EXPECT_FALSE(handler_->MaybeLoadFallbackForRedirect(
+ request_.get(),
+ request_->context()->network_delegate(),
+ GURL("ftp://blah/redirect")));
+ EXPECT_FALSE(handler_->MaybeLoadFallbackForResponse(
+ request_.get(), request_->context()->network_delegate()));
+
+ TestFinished();
+ }
+
+ // CanceledRequest -----------------------------
+
+ void CanceledRequest() {
+ request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_));
+ handler_.reset(host_->CreateRequestHandler(request_.get(),
+ ResourceType::MAIN_FRAME));
+ EXPECT_TRUE(handler_.get());
+
+ job_ = handler_->MaybeLoadResource(request_.get(),
+ request_->context()->network_delegate());
+ EXPECT_TRUE(job_.get());
+ EXPECT_TRUE(job_->is_waiting());
+ EXPECT_FALSE(job_->has_been_started());
+
+ job_factory_->SetJob(job_);
+ request_->Start();
+ EXPECT_TRUE(job_->has_been_started());
+
+ request_->Cancel();
+ EXPECT_TRUE(job_->has_been_killed());
+
+ EXPECT_FALSE(handler_->MaybeLoadFallbackForResponse(
+ request_.get(), request_->context()->network_delegate()));
+
+ TestFinished();
+ }
+
+ // WorkerRequest -----------------------------
+
+ void WorkerRequest() {
+ EXPECT_TRUE(AppCacheRequestHandler::IsMainResourceType(
+ ResourceType::MAIN_FRAME));
+ EXPECT_TRUE(AppCacheRequestHandler::IsMainResourceType(
+ ResourceType::SUB_FRAME));
+ EXPECT_TRUE(AppCacheRequestHandler::IsMainResourceType(
+ ResourceType::SHARED_WORKER));
+ EXPECT_FALSE(AppCacheRequestHandler::IsMainResourceType(
+ ResourceType::WORKER));
+
+ request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_));
+
+ const int kParentHostId = host_->host_id();
+ const int kWorkerHostId = 2;
+ const int kAbandonedWorkerHostId = 3;
+ const int kNonExsitingHostId = 700;
+
+ backend_impl_->RegisterHost(kWorkerHostId);
+ AppCacheHost* worker_host = backend_impl_->GetHost(kWorkerHostId);
+ worker_host->SelectCacheForWorker(kParentHostId, kMockProcessId);
+ handler_.reset(worker_host->CreateRequestHandler(
+ request_.get(), ResourceType::SHARED_WORKER));
+ EXPECT_TRUE(handler_.get());
+ // Verify that the handler is associated with the parent host.
+ EXPECT_EQ(host_, handler_->host_);
+
+ // Create a new worker host, but associate it with a parent host that
+ // does not exists to simulate the host having been torn down.
+ backend_impl_->UnregisterHost(kWorkerHostId);
+ backend_impl_->RegisterHost(kAbandonedWorkerHostId);
+ worker_host = backend_impl_->GetHost(kAbandonedWorkerHostId);
+ EXPECT_EQ(NULL, backend_impl_->GetHost(kNonExsitingHostId));
+ worker_host->SelectCacheForWorker(kNonExsitingHostId, kMockProcessId);
+ handler_.reset(worker_host->CreateRequestHandler(
+ request_.get(), ResourceType::SHARED_WORKER));
+ EXPECT_FALSE(handler_.get());
+
+ TestFinished();
+ }
+
+ // MainResource_Blocked --------------------------------------------------
+
+ void MainResource_Blocked() {
+ PushNextTask(
+ base::Bind(&AppCacheRequestHandlerTest::Verify_MainResource_Blocked,
+ base::Unretained(this)));
+
+ request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_));
+ handler_.reset(host_->CreateRequestHandler(request_.get(),
+ ResourceType::MAIN_FRAME));
+ EXPECT_TRUE(handler_.get());
+
+ mock_policy_->can_load_return_value_ = false;
+ mock_storage()->SimulateFindMainResource(
+ AppCacheEntry(AppCacheEntry::EXPLICIT, 1),
+ GURL(), AppCacheEntry(),
+ 1, 2, GURL("http://blah/manifest/"));
+
+ job_ = handler_->MaybeLoadResource(request_.get(),
+ request_->context()->network_delegate());
+ EXPECT_TRUE(job_.get());
+ EXPECT_TRUE(job_->is_waiting());
+
+ // We have to wait for completion of storage->FindResponseForMainRequest.
+ ScheduleNextTask();
+ }
+
+ void Verify_MainResource_Blocked() {
+ EXPECT_FALSE(job_->is_waiting());
+ EXPECT_FALSE(job_->is_delivering_appcache_response());
+
+ EXPECT_EQ(0, handler_->found_cache_id_);
+ EXPECT_EQ(0, handler_->found_group_id_);
+ EXPECT_TRUE(handler_->found_manifest_url_.is_empty());
+ EXPECT_TRUE(host_->preferred_manifest_url().is_empty());
+ EXPECT_TRUE(host_->main_resource_blocked_);
+ EXPECT_TRUE(host_->blocked_manifest_url_ == GURL("http://blah/manifest/"));
+
+ TestFinished();
+ }
+
+ // Test case helpers --------------------------------------------------
+
+ AppCache* MakeNewCache() {
+ AppCache* cache = new AppCache(
+ mock_storage(), mock_storage()->NewCacheId());
+ cache->set_complete(true);
+ AppCacheGroup* group = new AppCacheGroup(
+ mock_storage(), GURL("http://blah/manifest"),
+ mock_storage()->NewGroupId());
+ group->AddCache(cache);
+ return cache;
+ }
+
+ MockAppCacheStorage* mock_storage() {
+ return reinterpret_cast<MockAppCacheStorage*>(mock_service_->storage());
+ }
+
+ // Data members --------------------------------------------------
+
+ scoped_ptr<base::WaitableEvent> test_finished_event_;
+ std::stack<base::Closure> task_stack_;
+ scoped_ptr<MockAppCacheService> mock_service_;
+ scoped_ptr<AppCacheBackendImpl> backend_impl_;
+ scoped_ptr<MockFrontend> mock_frontend_;
+ scoped_ptr<MockAppCachePolicy> mock_policy_;
+ AppCacheHost* host_;
+ net::URLRequestContext empty_context_;
+ scoped_ptr<MockURLRequestJobFactory> job_factory_;
+ scoped_ptr<MockURLRequest> request_;
+ scoped_ptr<AppCacheRequestHandler> handler_;
+ scoped_refptr<AppCacheURLRequestJob> job_;
+
+ static scoped_ptr<base::Thread> io_thread_;
+};
+
+// static
+scoped_ptr<base::Thread> AppCacheRequestHandlerTest::io_thread_;
+
+TEST_F(AppCacheRequestHandlerTest, MainResource_Miss) {
+ RunTestOnIOThread(&AppCacheRequestHandlerTest::MainResource_Miss);
+}
+
+TEST_F(AppCacheRequestHandlerTest, MainResource_Hit) {
+ RunTestOnIOThread(&AppCacheRequestHandlerTest::MainResource_Hit);
+}
+
+TEST_F(AppCacheRequestHandlerTest, MainResource_Fallback) {
+ RunTestOnIOThread(&AppCacheRequestHandlerTest::MainResource_Fallback);
+}
+
+TEST_F(AppCacheRequestHandlerTest, MainResource_FallbackOverride) {
+ RunTestOnIOThread(
+ &AppCacheRequestHandlerTest::MainResource_FallbackOverride);
+}
+
+TEST_F(AppCacheRequestHandlerTest, SubResource_Miss_WithNoCacheSelected) {
+ RunTestOnIOThread(
+ &AppCacheRequestHandlerTest::SubResource_Miss_WithNoCacheSelected);
+}
+
+TEST_F(AppCacheRequestHandlerTest, SubResource_Miss_WithCacheSelected) {
+ RunTestOnIOThread(
+ &AppCacheRequestHandlerTest::SubResource_Miss_WithCacheSelected);
+}
+
+TEST_F(AppCacheRequestHandlerTest,
+ SubResource_Miss_WithWaitForCacheSelection) {
+ RunTestOnIOThread(
+ &AppCacheRequestHandlerTest::SubResource_Miss_WithWaitForCacheSelection);
+}
+
+TEST_F(AppCacheRequestHandlerTest, SubResource_Hit) {
+ RunTestOnIOThread(&AppCacheRequestHandlerTest::SubResource_Hit);
+}
+
+TEST_F(AppCacheRequestHandlerTest, SubResource_RedirectFallback) {
+ RunTestOnIOThread(&AppCacheRequestHandlerTest::SubResource_RedirectFallback);
+}
+
+TEST_F(AppCacheRequestHandlerTest, SubResource_NoRedirectFallback) {
+ RunTestOnIOThread(
+ &AppCacheRequestHandlerTest::SubResource_NoRedirectFallback);
+}
+
+TEST_F(AppCacheRequestHandlerTest, SubResource_Network) {
+ RunTestOnIOThread(&AppCacheRequestHandlerTest::SubResource_Network);
+}
+
+TEST_F(AppCacheRequestHandlerTest, DestroyedHost) {
+ RunTestOnIOThread(&AppCacheRequestHandlerTest::DestroyedHost);
+}
+
+TEST_F(AppCacheRequestHandlerTest, DestroyedHostWithWaitingJob) {
+ RunTestOnIOThread(&AppCacheRequestHandlerTest::DestroyedHostWithWaitingJob);
+}
+
+TEST_F(AppCacheRequestHandlerTest, UnsupportedScheme) {
+ RunTestOnIOThread(&AppCacheRequestHandlerTest::UnsupportedScheme);
+}
+
+TEST_F(AppCacheRequestHandlerTest, CanceledRequest) {
+ RunTestOnIOThread(&AppCacheRequestHandlerTest::CanceledRequest);
+}
+
+TEST_F(AppCacheRequestHandlerTest, WorkerRequest) {
+ RunTestOnIOThread(&AppCacheRequestHandlerTest::WorkerRequest);
+}
+
+TEST_F(AppCacheRequestHandlerTest, MainResource_Blocked) {
+ RunTestOnIOThread(&AppCacheRequestHandlerTest::MainResource_Blocked);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/appcache/appcache_response_unittest.cc b/chromium/content/browser/appcache/appcache_response_unittest.cc
new file mode 100644
index 00000000000..935fd7cd773
--- /dev/null
+++ b/chromium/content/browser/appcache/appcache_response_unittest.cc
@@ -0,0 +1,722 @@
+// 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 <stack>
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/pickle.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "content/browser/appcache/mock_appcache_service.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_response_headers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/appcache/appcache_response.h"
+
+using appcache::AppCacheStorage;
+using appcache::AppCacheResponseInfo;
+using appcache::AppCacheResponseReader;
+using appcache::AppCacheResponseWriter;
+using appcache::HttpResponseInfoIOBuffer;
+using net::IOBuffer;
+using net::WrappedIOBuffer;
+
+namespace content {
+
+static const int kNumBlocks = 4;
+static const int kBlockSize = 1024;
+static const int kNoSuchResponseId = 123;
+
+class AppCacheResponseTest : public testing::Test {
+ public:
+
+ // Test Harness -------------------------------------------------------------
+
+ // Helper class used to verify test results
+ class MockStorageDelegate : public AppCacheStorage::Delegate {
+ public:
+ explicit MockStorageDelegate(AppCacheResponseTest* test)
+ : loaded_info_id_(0), test_(test) {
+ }
+
+ virtual void OnResponseInfoLoaded(AppCacheResponseInfo* info,
+ int64 response_id) OVERRIDE {
+ loaded_info_ = info;
+ loaded_info_id_ = response_id;
+ test_->ScheduleNextTask();
+ }
+
+ scoped_refptr<AppCacheResponseInfo> loaded_info_;
+ int64 loaded_info_id_;
+ AppCacheResponseTest* test_;
+ };
+
+ // Helper callback to run a test on our io_thread. The io_thread is spun up
+ // once and reused for all tests.
+ template <class Method>
+ void MethodWrapper(Method method) {
+ SetUpTest();
+ (this->*method)();
+ }
+
+ static void SetUpTestCase() {
+ io_thread_.reset(new base::Thread("AppCacheResponseTest Thread"));
+ base::Thread::Options options(base::MessageLoop::TYPE_IO, 0);
+ io_thread_->StartWithOptions(options);
+ }
+
+ static void TearDownTestCase() {
+ io_thread_.reset(NULL);
+ }
+
+ AppCacheResponseTest() {}
+
+ template <class Method>
+ void RunTestOnIOThread(Method method) {
+ test_finished_event_ .reset(new base::WaitableEvent(false, false));
+ io_thread_->message_loop()->PostTask(
+ FROM_HERE, base::Bind(&AppCacheResponseTest::MethodWrapper<Method>,
+ base::Unretained(this), method));
+ test_finished_event_->Wait();
+ }
+
+ void SetUpTest() {
+ DCHECK(base::MessageLoop::current() == io_thread_->message_loop());
+ DCHECK(task_stack_.empty());
+ storage_delegate_.reset(new MockStorageDelegate(this));
+ service_.reset(new MockAppCacheService());
+ expected_read_result_ = 0;
+ expected_write_result_ = 0;
+ written_response_id_ = 0;
+ should_delete_reader_in_completion_callback_ = false;
+ should_delete_writer_in_completion_callback_ = false;
+ reader_deletion_count_down_ = 0;
+ writer_deletion_count_down_ = 0;
+ read_callback_was_called_ = false;
+ write_callback_was_called_ = false;
+ }
+
+ void TearDownTest() {
+ DCHECK(base::MessageLoop::current() == io_thread_->message_loop());
+ while (!task_stack_.empty())
+ task_stack_.pop();
+
+ reader_.reset();
+ read_buffer_ = NULL;
+ read_info_buffer_ = NULL;
+ writer_.reset();
+ write_buffer_ = NULL;
+ write_info_buffer_ = NULL;
+ storage_delegate_.reset();
+ service_.reset();
+ }
+
+ void TestFinished() {
+ // We unwind the stack prior to finishing up to let stack
+ // based objects get deleted.
+ DCHECK(base::MessageLoop::current() == io_thread_->message_loop());
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&AppCacheResponseTest::TestFinishedUnwound,
+ base::Unretained(this)));
+ }
+
+ void TestFinishedUnwound() {
+ TearDownTest();
+ test_finished_event_->Signal();
+ }
+
+ void PushNextTask(const base::Closure& task) {
+ task_stack_.push(std::pair<base::Closure, bool>(task, false));
+ }
+
+ void PushNextTaskAsImmediate(const base::Closure& task) {
+ task_stack_.push(std::pair<base::Closure, bool>(task, true));
+ }
+
+ void ScheduleNextTask() {
+ DCHECK(base::MessageLoop::current() == io_thread_->message_loop());
+ if (task_stack_.empty()) {
+ TestFinished();
+ return;
+ }
+ base::Closure task = task_stack_.top().first;
+ bool immediate = task_stack_.top().second;
+ task_stack_.pop();
+ if (immediate)
+ task.Run();
+ else
+ base::MessageLoop::current()->PostTask(FROM_HERE, task);
+ }
+
+ // Wrappers to call AppCacheResponseReader/Writer Read and Write methods
+
+ void WriteBasicResponse() {
+ static const char kHttpHeaders[] =
+ "HTTP/1.0 200 OK\0Content-Length: 5\0\0";
+ static const char* kHttpBody = "Hello";
+ scoped_refptr<IOBuffer> body(new WrappedIOBuffer(kHttpBody));
+ std::string raw_headers(kHttpHeaders, arraysize(kHttpHeaders));
+ WriteResponse(
+ MakeHttpResponseInfo(raw_headers), body.get(), strlen(kHttpBody));
+ }
+
+ int basic_response_size() { return 5; } // should match kHttpBody above
+
+ void WriteResponse(net::HttpResponseInfo* head,
+ IOBuffer* body, int body_len) {
+ DCHECK(body);
+ scoped_refptr<IOBuffer> body_ref(body);
+ PushNextTask(base::Bind(&AppCacheResponseTest::WriteResponseBody,
+ base::Unretained(this), body_ref, body_len));
+ WriteResponseHead(head);
+ }
+
+ void WriteResponseHead(net::HttpResponseInfo* head) {
+ EXPECT_FALSE(writer_->IsWritePending());
+ expected_write_result_ = GetHttpResponseInfoSize(head);
+ write_info_buffer_ = new HttpResponseInfoIOBuffer(head);
+ writer_->WriteInfo(write_info_buffer_.get(),
+ base::Bind(&AppCacheResponseTest::OnWriteInfoComplete,
+ base::Unretained(this)));
+ }
+
+ void WriteResponseBody(scoped_refptr<IOBuffer> io_buffer, int buf_len) {
+ EXPECT_FALSE(writer_->IsWritePending());
+ write_buffer_ = io_buffer;
+ expected_write_result_ = buf_len;
+ writer_->WriteData(write_buffer_.get(),
+ buf_len,
+ base::Bind(&AppCacheResponseTest::OnWriteComplete,
+ base::Unretained(this)));
+ }
+
+ void ReadResponseBody(scoped_refptr<IOBuffer> io_buffer, int buf_len) {
+ EXPECT_FALSE(reader_->IsReadPending());
+ read_buffer_ = io_buffer;
+ expected_read_result_ = buf_len;
+ reader_->ReadData(read_buffer_.get(),
+ buf_len,
+ base::Bind(&AppCacheResponseTest::OnReadComplete,
+ base::Unretained(this)));
+ }
+
+ // AppCacheResponseReader / Writer completion callbacks
+
+ void OnWriteInfoComplete(int result) {
+ EXPECT_FALSE(writer_->IsWritePending());
+ EXPECT_EQ(expected_write_result_, result);
+ ScheduleNextTask();
+ }
+
+ void OnWriteComplete(int result) {
+ EXPECT_FALSE(writer_->IsWritePending());
+ write_callback_was_called_ = true;
+ EXPECT_EQ(expected_write_result_, result);
+ if (should_delete_writer_in_completion_callback_ &&
+ --writer_deletion_count_down_ == 0) {
+ writer_.reset();
+ }
+ ScheduleNextTask();
+ }
+
+ void OnReadInfoComplete(int result) {
+ EXPECT_FALSE(reader_->IsReadPending());
+ EXPECT_EQ(expected_read_result_, result);
+ ScheduleNextTask();
+ }
+
+ void OnReadComplete(int result) {
+ EXPECT_FALSE(reader_->IsReadPending());
+ read_callback_was_called_ = true;
+ EXPECT_EQ(expected_read_result_, result);
+ if (should_delete_reader_in_completion_callback_ &&
+ --reader_deletion_count_down_ == 0) {
+ reader_.reset();
+ }
+ ScheduleNextTask();
+ }
+
+ // Helpers to work with HttpResponseInfo objects
+
+ net::HttpResponseInfo* MakeHttpResponseInfo(const std::string& raw_headers) {
+ net::HttpResponseInfo* info = new net::HttpResponseInfo;
+ info->request_time = base::Time::Now();
+ info->response_time = base::Time::Now();
+ info->was_cached = false;
+ info->headers = new net::HttpResponseHeaders(raw_headers);
+ return info;
+ }
+
+ int GetHttpResponseInfoSize(const net::HttpResponseInfo* info) {
+ Pickle pickle;
+ return PickleHttpResonseInfo(&pickle, info);
+ }
+
+ bool CompareHttpResponseInfos(const net::HttpResponseInfo* info1,
+ const net::HttpResponseInfo* info2) {
+ Pickle pickle1;
+ Pickle pickle2;
+ PickleHttpResonseInfo(&pickle1, info1);
+ PickleHttpResonseInfo(&pickle2, info2);
+ return (pickle1.size() == pickle2.size()) &&
+ (0 == memcmp(pickle1.data(), pickle2.data(), pickle1.size()));
+ }
+
+ int PickleHttpResonseInfo(Pickle* pickle, const net::HttpResponseInfo* info) {
+ const bool kSkipTransientHeaders = true;
+ const bool kTruncated = false;
+ info->Persist(pickle, kSkipTransientHeaders, kTruncated);
+ return pickle->size();
+ }
+
+ // Helpers to fill and verify blocks of memory with a value
+
+ void FillData(char value, char* data, int data_len) {
+ memset(data, value, data_len);
+ }
+
+ bool CheckData(char value, const char* data, int data_len) {
+ for (int i = 0; i < data_len; ++i, ++data) {
+ if (*data != value)
+ return false;
+ }
+ return true;
+ }
+
+ // Individual Tests ---------------------------------------------------------
+ // Most of the individual tests involve multiple async steps. Each test
+ // is delineated with a section header.
+
+
+ // ReadNonExistentResponse -------------------------------------------
+ void ReadNonExistentResponse() {
+ // 1. Attempt to ReadInfo
+ // 2. Attempt to ReadData
+
+ reader_.reset(service_->storage()->CreateResponseReader(
+ GURL(), 0, kNoSuchResponseId));
+
+ // Push tasks in reverse order
+ PushNextTask(base::Bind(&AppCacheResponseTest::ReadNonExistentData,
+ base::Unretained(this)));
+ PushNextTask(base::Bind(&AppCacheResponseTest::ReadNonExistentInfo,
+ base::Unretained(this)));
+ ScheduleNextTask();
+ }
+
+ void ReadNonExistentInfo() {
+ EXPECT_FALSE(reader_->IsReadPending());
+ read_info_buffer_ = new HttpResponseInfoIOBuffer();
+ reader_->ReadInfo(read_info_buffer_.get(),
+ base::Bind(&AppCacheResponseTest::OnReadInfoComplete,
+ base::Unretained(this)));
+ EXPECT_TRUE(reader_->IsReadPending());
+ expected_read_result_ = net::ERR_CACHE_MISS;
+ }
+
+ void ReadNonExistentData() {
+ EXPECT_FALSE(reader_->IsReadPending());
+ read_buffer_ = new IOBuffer(kBlockSize);
+ reader_->ReadData(read_buffer_.get(),
+ kBlockSize,
+ base::Bind(&AppCacheResponseTest::OnReadComplete,
+ base::Unretained(this)));
+ EXPECT_TRUE(reader_->IsReadPending());
+ expected_read_result_ = net::ERR_CACHE_MISS;
+ }
+
+ // LoadResponseInfo_Miss ----------------------------------------------------
+ void LoadResponseInfo_Miss() {
+ PushNextTask(base::Bind(&AppCacheResponseTest::LoadResponseInfo_Miss_Verify,
+ base::Unretained(this)));
+ service_->storage()->LoadResponseInfo(GURL(), 0, kNoSuchResponseId,
+ storage_delegate_.get());
+ }
+
+ void LoadResponseInfo_Miss_Verify() {
+ EXPECT_EQ(kNoSuchResponseId, storage_delegate_->loaded_info_id_);
+ EXPECT_TRUE(!storage_delegate_->loaded_info_.get());
+ TestFinished();
+ }
+
+ // LoadResponseInfo_Hit ----------------------------------------------------
+ void LoadResponseInfo_Hit() {
+ // This tests involves multiple async steps.
+ // 1. Write a response headers and body to storage
+ // a. headers
+ // b. body
+ // 2. Use LoadResponseInfo to read the response headers back out
+ PushNextTask(base::Bind(&AppCacheResponseTest::LoadResponseInfo_Hit_Step2,
+ base::Unretained(this)));
+ writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0));
+ written_response_id_ = writer_->response_id();
+ WriteBasicResponse();
+ }
+
+ void LoadResponseInfo_Hit_Step2() {
+ writer_.reset();
+ PushNextTask(base::Bind(&AppCacheResponseTest::LoadResponseInfo_Hit_Verify,
+ base::Unretained(this)));
+ service_->storage()->LoadResponseInfo(GURL(), 0, written_response_id_,
+ storage_delegate_.get());
+ }
+
+ void LoadResponseInfo_Hit_Verify() {
+ EXPECT_EQ(written_response_id_, storage_delegate_->loaded_info_id_);
+ EXPECT_TRUE(storage_delegate_->loaded_info_.get());
+ EXPECT_TRUE(CompareHttpResponseInfos(
+ write_info_buffer_->http_info.get(),
+ storage_delegate_->loaded_info_->http_response_info()));
+ EXPECT_EQ(basic_response_size(),
+ storage_delegate_->loaded_info_->response_data_size());
+ TestFinished();
+ }
+
+ // AmountWritten ----------------------------------------------------
+
+ void AmountWritten() {
+ static const char kHttpHeaders[] =
+ "HTTP/1.0 200 OK\0\0";
+ std::string raw_headers(kHttpHeaders, arraysize(kHttpHeaders));
+ net::HttpResponseInfo* head = MakeHttpResponseInfo(raw_headers);
+ int expected_amount_written =
+ GetHttpResponseInfoSize(head) + kNumBlocks * kBlockSize;
+
+ // Push tasks in reverse order.
+ PushNextTask(base::Bind(&AppCacheResponseTest::Verify_AmountWritten,
+ base::Unretained(this), expected_amount_written));
+ for (int i = 0; i < kNumBlocks; ++i) {
+ PushNextTask(base::Bind(&AppCacheResponseTest::WriteOneBlock,
+ base::Unretained(this), kNumBlocks - i));
+ }
+ PushNextTask(base::Bind(&AppCacheResponseTest::WriteResponseHead,
+ base::Unretained(this), head));
+
+ writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0));
+ written_response_id_ = writer_->response_id();
+ ScheduleNextTask();
+ }
+
+ void Verify_AmountWritten(int expected_amount_written) {
+ EXPECT_EQ(expected_amount_written, writer_->amount_written());
+ TestFinished();
+ }
+
+
+ // WriteThenVariouslyReadResponse -------------------------------------------
+
+ void WriteThenVariouslyReadResponse() {
+ // This tests involves multiple async steps.
+ // 1. First, write a large body using multiple writes, we don't bother
+ // with a response head for this test.
+ // 2. Read the entire body, using multiple reads
+ // 3. Read the entire body, using one read.
+ // 4. Attempt to read beyond the EOF.
+ // 5. Read just a range.
+ // 6. Attempt to read beyond EOF of a range.
+
+ // Push tasks in reverse order
+ PushNextTask(base::Bind(&AppCacheResponseTest::ReadRangeFullyBeyondEOF,
+ base::Unretained(this)));
+ PushNextTask(base::Bind(&AppCacheResponseTest::ReadRangePartiallyBeyondEOF,
+ base::Unretained(this)));
+ PushNextTask(base::Bind(&AppCacheResponseTest::ReadPastEOF,
+ base::Unretained(this)));
+ PushNextTask(base::Bind(&AppCacheResponseTest::ReadRange,
+ base::Unretained(this)));
+ PushNextTask(base::Bind(&AppCacheResponseTest::ReadPastEOF,
+ base::Unretained(this)));
+ PushNextTask(base::Bind(&AppCacheResponseTest::ReadAllAtOnce,
+ base::Unretained(this)));
+ PushNextTask(base::Bind(&AppCacheResponseTest::ReadInBlocks,
+ base::Unretained(this)));
+ PushNextTask(base::Bind(&AppCacheResponseTest::WriteOutBlocks,
+ base::Unretained(this)));
+
+ // Get them going.
+ ScheduleNextTask();
+ }
+
+ void WriteOutBlocks() {
+ writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0));
+ written_response_id_ = writer_->response_id();
+ for (int i = 0; i < kNumBlocks; ++i) {
+ PushNextTask(base::Bind(&AppCacheResponseTest::WriteOneBlock,
+ base::Unretained(this), kNumBlocks - i));
+ }
+ ScheduleNextTask();
+ }
+
+ void WriteOneBlock(int block_number) {
+ scoped_refptr<IOBuffer> io_buffer(
+ new IOBuffer(kBlockSize));
+ FillData(block_number, io_buffer->data(), kBlockSize);
+ WriteResponseBody(io_buffer, kBlockSize);
+ }
+
+ void ReadInBlocks() {
+ writer_.reset();
+ reader_.reset(service_->storage()->CreateResponseReader(
+ GURL(), 0, written_response_id_));
+ for (int i = 0; i < kNumBlocks; ++i) {
+ PushNextTask(base::Bind(&AppCacheResponseTest::ReadOneBlock,
+ base::Unretained(this), kNumBlocks - i));
+ }
+ ScheduleNextTask();
+ }
+
+ void ReadOneBlock(int block_number) {
+ PushNextTask(base::Bind(&AppCacheResponseTest::VerifyOneBlock,
+ base::Unretained(this), block_number));
+ ReadResponseBody(new IOBuffer(kBlockSize), kBlockSize);
+ }
+
+ void VerifyOneBlock(int block_number) {
+ EXPECT_TRUE(CheckData(block_number, read_buffer_->data(), kBlockSize));
+ ScheduleNextTask();
+ }
+
+ void ReadAllAtOnce() {
+ PushNextTask(base::Bind(&AppCacheResponseTest::VerifyAllAtOnce,
+ base::Unretained(this)));
+ reader_.reset(service_->storage()->CreateResponseReader(
+ GURL(), 0, written_response_id_));
+ int big_size = kNumBlocks * kBlockSize;
+ ReadResponseBody(new IOBuffer(big_size), big_size);
+ }
+
+ void VerifyAllAtOnce() {
+ char* p = read_buffer_->data();
+ for (int i = 0; i < kNumBlocks; ++i, p += kBlockSize)
+ EXPECT_TRUE(CheckData(i + 1, p, kBlockSize));
+ ScheduleNextTask();
+ }
+
+ void ReadPastEOF() {
+ EXPECT_FALSE(reader_->IsReadPending());
+ read_buffer_ = new IOBuffer(kBlockSize);
+ expected_read_result_ = 0;
+ reader_->ReadData(read_buffer_.get(),
+ kBlockSize,
+ base::Bind(&AppCacheResponseTest::OnReadComplete,
+ base::Unretained(this)));
+ }
+
+ void ReadRange() {
+ PushNextTask(base::Bind(&AppCacheResponseTest::VerifyRange,
+ base::Unretained(this)));
+ reader_.reset(service_->storage()->CreateResponseReader(
+ GURL(), 0, written_response_id_));
+ reader_->SetReadRange(kBlockSize, kBlockSize);
+ ReadResponseBody(new IOBuffer(kBlockSize), kBlockSize);
+ }
+
+ void VerifyRange() {
+ EXPECT_TRUE(CheckData(2, read_buffer_->data(), kBlockSize));
+ ScheduleNextTask(); // ReadPastEOF is scheduled next
+ }
+
+ void ReadRangePartiallyBeyondEOF() {
+ PushNextTask(base::Bind(&AppCacheResponseTest::VerifyRangeBeyondEOF,
+ base::Unretained(this)));
+ reader_.reset(service_->storage()->CreateResponseReader(
+ GURL(), 0, written_response_id_));
+ reader_->SetReadRange(kBlockSize, kNumBlocks * kBlockSize);
+ ReadResponseBody(new IOBuffer(kNumBlocks * kBlockSize),
+ kNumBlocks * kBlockSize);
+ expected_read_result_ = (kNumBlocks - 1) * kBlockSize;
+ }
+
+ void VerifyRangeBeyondEOF() {
+ // Just verify the first 1k
+ VerifyRange();
+ }
+
+ void ReadRangeFullyBeyondEOF() {
+ reader_.reset(service_->storage()->CreateResponseReader(
+ GURL(), 0, written_response_id_));
+ reader_->SetReadRange((kNumBlocks * kBlockSize) + 1, kBlockSize);
+ ReadResponseBody(new IOBuffer(kBlockSize), kBlockSize);
+ expected_read_result_ = 0;
+ }
+
+ // IOChaining -------------------------------------------
+ void IOChaining() {
+ // 1. Write several blocks out initiating the subsequent write
+ // from within the completion callback of the previous write.
+ // 2. Read and verify several blocks in similarly chaining reads.
+
+ // Push tasks in reverse order
+ PushNextTaskAsImmediate(
+ base::Bind(&AppCacheResponseTest::ReadInBlocksImmediately,
+ base::Unretained(this)));
+ PushNextTaskAsImmediate(
+ base::Bind(&AppCacheResponseTest::WriteOutBlocksImmediately,
+ base::Unretained(this)));
+
+ // Get them going.
+ ScheduleNextTask();
+ }
+
+ void WriteOutBlocksImmediately() {
+ writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0));
+ written_response_id_ = writer_->response_id();
+ for (int i = 0; i < kNumBlocks; ++i) {
+ PushNextTaskAsImmediate(
+ base::Bind(&AppCacheResponseTest::WriteOneBlock,
+ base::Unretained(this), kNumBlocks - i));
+ }
+ ScheduleNextTask();
+ }
+
+ void ReadInBlocksImmediately() {
+ writer_.reset();
+ reader_.reset(service_->storage()->CreateResponseReader(
+ GURL(), 0, written_response_id_));
+ for (int i = 0; i < kNumBlocks; ++i) {
+ PushNextTaskAsImmediate(
+ base::Bind(&AppCacheResponseTest::ReadOneBlockImmediately,
+ base::Unretained(this),
+ kNumBlocks - i));
+ }
+ ScheduleNextTask();
+ }
+
+ void ReadOneBlockImmediately(int block_number) {
+ PushNextTaskAsImmediate(base::Bind(&AppCacheResponseTest::VerifyOneBlock,
+ base::Unretained(this), block_number));
+ ReadResponseBody(new IOBuffer(kBlockSize), kBlockSize);
+ }
+
+ // DeleteWithinCallbacks -------------------------------------------
+ void DeleteWithinCallbacks() {
+ // 1. Write out a few blocks normally, and upon
+ // completion of the last write, delete the writer.
+ // 2. Read in a few blocks normally, and upon completion
+ // of the last read, delete the reader.
+
+ should_delete_reader_in_completion_callback_ = true;
+ reader_deletion_count_down_ = kNumBlocks;
+ should_delete_writer_in_completion_callback_ = true;
+ writer_deletion_count_down_ = kNumBlocks;
+
+ PushNextTask(base::Bind(&AppCacheResponseTest::ReadInBlocks,
+ base::Unretained(this)));
+ PushNextTask(base::Bind(&AppCacheResponseTest::WriteOutBlocks,
+ base::Unretained(this)));
+ ScheduleNextTask();
+ }
+
+ // DeleteWithIOPending -------------------------------------------
+ void DeleteWithIOPending() {
+ // 1. Write a few blocks normally.
+ // 2. Start a write, delete with it pending.
+ // 3. Start a read, delete with it pending.
+ PushNextTask(base::Bind(&AppCacheResponseTest::ReadThenDelete,
+ base::Unretained(this)));
+ PushNextTask(base::Bind(&AppCacheResponseTest::WriteThenDelete,
+ base::Unretained(this)));
+ PushNextTask(base::Bind(&AppCacheResponseTest::WriteOutBlocks,
+ base::Unretained(this)));
+ ScheduleNextTask();
+ }
+
+ void WriteThenDelete() {
+ write_callback_was_called_ = false;
+ WriteOneBlock(5);
+ EXPECT_TRUE(writer_->IsWritePending());
+ writer_.reset();
+ ScheduleNextTask();
+ }
+
+ void ReadThenDelete() {
+ read_callback_was_called_ = false;
+ reader_.reset(service_->storage()->CreateResponseReader(
+ GURL(), 0, written_response_id_));
+ ReadResponseBody(new IOBuffer(kBlockSize), kBlockSize);
+ EXPECT_TRUE(reader_->IsReadPending());
+ reader_.reset();
+
+ // Wait a moment to verify no callbacks.
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE, base::Bind(&AppCacheResponseTest::VerifyNoCallbacks,
+ base::Unretained(this)),
+ base::TimeDelta::FromMilliseconds(10));
+ }
+
+ void VerifyNoCallbacks() {
+ EXPECT_TRUE(!write_callback_was_called_);
+ EXPECT_TRUE(!read_callback_was_called_);
+ TestFinished();
+ }
+
+ // Data members
+
+ scoped_ptr<base::WaitableEvent> test_finished_event_;
+ scoped_ptr<MockStorageDelegate> storage_delegate_;
+ scoped_ptr<MockAppCacheService> service_;
+ std::stack<std::pair<base::Closure, bool> > task_stack_;
+
+ scoped_ptr<AppCacheResponseReader> reader_;
+ scoped_refptr<HttpResponseInfoIOBuffer> read_info_buffer_;
+ scoped_refptr<IOBuffer> read_buffer_;
+ int expected_read_result_;
+ bool should_delete_reader_in_completion_callback_;
+ int reader_deletion_count_down_;
+ bool read_callback_was_called_;
+
+ int64 written_response_id_;
+ scoped_ptr<AppCacheResponseWriter> writer_;
+ scoped_refptr<HttpResponseInfoIOBuffer> write_info_buffer_;
+ scoped_refptr<IOBuffer> write_buffer_;
+ int expected_write_result_;
+ bool should_delete_writer_in_completion_callback_;
+ int writer_deletion_count_down_;
+ bool write_callback_was_called_;
+
+ static scoped_ptr<base::Thread> io_thread_;
+};
+
+// static
+scoped_ptr<base::Thread> AppCacheResponseTest::io_thread_;
+
+TEST_F(AppCacheResponseTest, ReadNonExistentResponse) {
+ RunTestOnIOThread(&AppCacheResponseTest::ReadNonExistentResponse);
+}
+
+TEST_F(AppCacheResponseTest, LoadResponseInfo_Miss) {
+ RunTestOnIOThread(&AppCacheResponseTest::LoadResponseInfo_Miss);
+}
+
+TEST_F(AppCacheResponseTest, LoadResponseInfo_Hit) {
+ RunTestOnIOThread(&AppCacheResponseTest::LoadResponseInfo_Hit);
+}
+
+TEST_F(AppCacheResponseTest, AmountWritten) {
+ RunTestOnIOThread(&AppCacheResponseTest::AmountWritten);
+}
+
+TEST_F(AppCacheResponseTest, WriteThenVariouslyReadResponse) {
+ RunTestOnIOThread(&AppCacheResponseTest::WriteThenVariouslyReadResponse);
+}
+
+TEST_F(AppCacheResponseTest, IOChaining) {
+ RunTestOnIOThread(&AppCacheResponseTest::IOChaining);
+}
+
+TEST_F(AppCacheResponseTest, DeleteWithinCallbacks) {
+ RunTestOnIOThread(&AppCacheResponseTest::DeleteWithinCallbacks);
+}
+
+TEST_F(AppCacheResponseTest, DeleteWithIOPending) {
+ RunTestOnIOThread(&AppCacheResponseTest::DeleteWithIOPending);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/appcache/appcache_service_unittest.cc b/chromium/content/browser/appcache/appcache_service_unittest.cc
new file mode 100644
index 00000000000..80016a94784
--- /dev/null
+++ b/chromium/content/browser/appcache/appcache_service_unittest.cc
@@ -0,0 +1,386 @@
+// 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 <string>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/pickle.h"
+#include "base/run_loop.h"
+#include "content/browser/appcache/mock_appcache_storage.h"
+#include "net/base/completion_callback.h"
+#include "net/base/io_buffer.h"
+#include "net/http/http_response_headers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/appcache/appcache_response.h"
+#include "webkit/browser/appcache/appcache_service_impl.h"
+
+using appcache::AppCache;
+using appcache::AppCacheEntry;
+using appcache::AppCacheGroup;
+using appcache::AppCacheInfo;
+using appcache::AppCacheInfoCollection;
+using appcache::AppCacheInfoVector;
+using appcache::AppCacheResponseReader;
+using appcache::AppCacheServiceImpl;
+using appcache::HttpResponseInfoIOBuffer;
+
+namespace content {
+namespace {
+
+const int64 kMockGroupId = 1;
+const int64 kMockCacheId = 1;
+const int64 kMockResponseId = 1;
+const int64 kMissingCacheId = 5;
+const int64 kMissingResponseId = 5;
+const char kMockHeaders[] =
+ "HTTP/1.0 200 OK\0Content-Length: 5\0\0";
+const char kMockBody[] = "Hello";
+const int kMockBodySize = 5;
+
+class MockResponseReader : public AppCacheResponseReader {
+ public:
+ MockResponseReader(int64 response_id,
+ net::HttpResponseInfo* info, int info_size,
+ const char* data, int data_size)
+ : AppCacheResponseReader(response_id, 0, NULL),
+ info_(info), info_size_(info_size),
+ data_(data), data_size_(data_size) {
+ }
+ virtual void ReadInfo(HttpResponseInfoIOBuffer* info_buf,
+ const net::CompletionCallback& callback) OVERRIDE {
+ info_buffer_ = info_buf;
+ callback_ = callback; // Cleared on completion.
+
+ int rv = info_.get() ? info_size_ : net::ERR_FAILED;
+ info_buffer_->http_info.reset(info_.release());
+ info_buffer_->response_data_size = data_size_;
+ ScheduleUserCallback(rv);
+ }
+ virtual void ReadData(net::IOBuffer* buf, int buf_len,
+ const net::CompletionCallback& callback) OVERRIDE {
+ buffer_ = buf;
+ buffer_len_ = buf_len;
+ callback_ = callback; // Cleared on completion.
+
+ if (!data_) {
+ ScheduleUserCallback(net::ERR_CACHE_READ_FAILURE);
+ return;
+ }
+ DCHECK(buf_len >= data_size_);
+ memcpy(buf->data(), data_, data_size_);
+ ScheduleUserCallback(data_size_);
+ data_size_ = 0;
+ }
+
+ private:
+ void ScheduleUserCallback(int result) {
+ base::MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(&MockResponseReader::InvokeUserCompletionCallback,
+ weak_factory_.GetWeakPtr(), result));
+ }
+
+ scoped_ptr<net::HttpResponseInfo> info_;
+ int info_size_;
+ const char* data_;
+ int data_size_;
+};
+
+} // namespace
+
+
+class AppCacheServiceImplTest : public testing::Test {
+ public:
+ AppCacheServiceImplTest()
+ : kOrigin("http://hello/"),
+ kManifestUrl(kOrigin.Resolve("manifest")),
+ service_(new AppCacheServiceImpl(NULL)),
+ delete_result_(net::OK), delete_completion_count_(0),
+ deletion_callback_(
+ base::Bind(&AppCacheServiceImplTest::OnDeleteAppCachesComplete,
+ base::Unretained(this))) {
+ // Setup to use mock storage.
+ service_->storage_.reset(new MockAppCacheStorage(service_.get()));
+ }
+
+ void OnDeleteAppCachesComplete(int result) {
+ delete_result_ = result;
+ ++delete_completion_count_;
+ }
+
+ MockAppCacheStorage* mock_storage() {
+ return static_cast<MockAppCacheStorage*>(service_->storage());
+ }
+
+ void ResetStorage() {
+ service_->storage_.reset(new MockAppCacheStorage(service_.get()));
+ }
+
+ bool IsGroupStored(const GURL& manifest_url) {
+ return mock_storage()->IsGroupForManifestStored(manifest_url);
+ }
+
+ int CountPendingHelpers() {
+ return service_->pending_helpers_.size();
+ }
+
+ void SetupMockGroup() {
+ scoped_ptr<net::HttpResponseInfo> info(MakeMockResponseInfo());
+ const int kMockInfoSize = GetResponseInfoSize(info.get());
+
+ // Create a mock group, cache, and entry and stuff them into mock storage.
+ scoped_refptr<AppCacheGroup> group(
+ new AppCacheGroup(service_->storage(), kManifestUrl, kMockGroupId));
+ scoped_refptr<AppCache> cache(
+ new AppCache(service_->storage(), kMockCacheId));
+ cache->AddEntry(
+ kManifestUrl,
+ AppCacheEntry(AppCacheEntry::MANIFEST, kMockResponseId,
+ kMockInfoSize + kMockBodySize));
+ cache->set_complete(true);
+ group->AddCache(cache.get());
+ mock_storage()->AddStoredGroup(group.get());
+ mock_storage()->AddStoredCache(cache.get());
+ }
+
+ void SetupMockReader(
+ bool valid_info, bool valid_data, bool valid_size) {
+ net::HttpResponseInfo* info = valid_info ? MakeMockResponseInfo() : NULL;
+ int info_size = info ? GetResponseInfoSize(info) : 0;
+ const char* data = valid_data ? kMockBody : NULL;
+ int data_size = valid_size ? kMockBodySize : 3;
+ mock_storage()->SimulateResponseReader(
+ new MockResponseReader(kMockResponseId, info, info_size,
+ data, data_size));
+ }
+
+ net::HttpResponseInfo* MakeMockResponseInfo() {
+ net::HttpResponseInfo* info = new net::HttpResponseInfo;
+ info->request_time = base::Time::Now();
+ info->response_time = base::Time::Now();
+ info->was_cached = false;
+ info->headers = new net::HttpResponseHeaders(
+ std::string(kMockHeaders, arraysize(kMockHeaders)));
+ return info;
+ }
+
+ int GetResponseInfoSize(const net::HttpResponseInfo* info) {
+ Pickle pickle;
+ return PickleResponseInfo(&pickle, info);
+ }
+
+ int PickleResponseInfo(Pickle* pickle, const net::HttpResponseInfo* info) {
+ const bool kSkipTransientHeaders = true;
+ const bool kTruncated = false;
+ info->Persist(pickle, kSkipTransientHeaders, kTruncated);
+ return pickle->size();
+ }
+
+ const GURL kOrigin;
+ const GURL kManifestUrl;
+
+ scoped_ptr<AppCacheServiceImpl> service_;
+ int delete_result_;
+ int delete_completion_count_;
+ net::CompletionCallback deletion_callback_;
+
+ private:
+ base::MessageLoop message_loop_;
+};
+
+TEST_F(AppCacheServiceImplTest, DeleteAppCachesForOrigin) {
+ // Without giving mock storage simiulated info, should fail.
+ service_->DeleteAppCachesForOrigin(kOrigin, deletion_callback_);
+ EXPECT_EQ(0, delete_completion_count_);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(1, delete_completion_count_);
+ EXPECT_EQ(net::ERR_FAILED, delete_result_);
+ delete_completion_count_ = 0;
+
+ // Should succeed given an empty info collection.
+ mock_storage()->SimulateGetAllInfo(new AppCacheInfoCollection);
+ service_->DeleteAppCachesForOrigin(kOrigin, deletion_callback_);
+ EXPECT_EQ(0, delete_completion_count_);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(1, delete_completion_count_);
+ EXPECT_EQ(net::OK, delete_result_);
+ delete_completion_count_ = 0;
+
+ scoped_refptr<AppCacheInfoCollection> info(new AppCacheInfoCollection);
+
+ // Should succeed given a non-empty info collection.
+ AppCacheInfo mock_manifest_1;
+ AppCacheInfo mock_manifest_2;
+ AppCacheInfo mock_manifest_3;
+ mock_manifest_1.manifest_url = kOrigin.Resolve("manifest1");
+ mock_manifest_2.manifest_url = kOrigin.Resolve("manifest2");
+ mock_manifest_3.manifest_url = kOrigin.Resolve("manifest3");
+ AppCacheInfoVector info_vector;
+ info_vector.push_back(mock_manifest_1);
+ info_vector.push_back(mock_manifest_2);
+ info_vector.push_back(mock_manifest_3);
+ info->infos_by_origin[kOrigin] = info_vector;
+ mock_storage()->SimulateGetAllInfo(info.get());
+ service_->DeleteAppCachesForOrigin(kOrigin, deletion_callback_);
+ EXPECT_EQ(0, delete_completion_count_);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(1, delete_completion_count_);
+ EXPECT_EQ(net::OK, delete_result_);
+ delete_completion_count_ = 0;
+
+ // Should fail if storage fails to delete.
+ info->infos_by_origin[kOrigin] = info_vector;
+ mock_storage()->SimulateGetAllInfo(info.get());
+ mock_storage()->SimulateMakeGroupObsoleteFailure();
+ service_->DeleteAppCachesForOrigin(kOrigin, deletion_callback_);
+ EXPECT_EQ(0, delete_completion_count_);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(1, delete_completion_count_);
+ EXPECT_EQ(net::ERR_FAILED, delete_result_);
+ delete_completion_count_ = 0;
+
+ // Should complete with abort error if the service is deleted
+ // prior to a delete completion.
+ service_->DeleteAppCachesForOrigin(kOrigin, deletion_callback_);
+ EXPECT_EQ(0, delete_completion_count_);
+ service_.reset(); // kill it
+ EXPECT_EQ(1, delete_completion_count_);
+ EXPECT_EQ(net::ERR_ABORTED, delete_result_);
+ delete_completion_count_ = 0;
+
+ // Let any tasks lingering from the sudden deletion run and verify
+ // no other completion calls occur.
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, delete_completion_count_);
+}
+
+TEST_F(AppCacheServiceImplTest, CheckAppCacheResponse) {
+ // Check a non-existing manifest.
+ EXPECT_FALSE(IsGroupStored(kManifestUrl));
+ service_->CheckAppCacheResponse(kManifestUrl, 1, 1);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, CountPendingHelpers());
+ EXPECT_FALSE(IsGroupStored(kManifestUrl));
+ ResetStorage();
+
+ // Check a response that looks good.
+ // Nothing should be deleted.
+ SetupMockGroup();
+ EXPECT_TRUE(IsGroupStored(kManifestUrl));
+ SetupMockReader(true, true, true);
+ service_->CheckAppCacheResponse(kManifestUrl, kMockCacheId, kMockResponseId);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, CountPendingHelpers());
+ EXPECT_TRUE(IsGroupStored(kManifestUrl));
+ ResetStorage();
+
+ // Check a response for which there is no cache entry.
+ // The group should get deleted.
+ SetupMockGroup();
+ service_->CheckAppCacheResponse(kManifestUrl, kMockCacheId,
+ kMissingResponseId);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, CountPendingHelpers());
+ EXPECT_FALSE(IsGroupStored(kManifestUrl));
+ ResetStorage();
+
+ // Check a response for which there is no manifest entry in a newer version
+ // of the cache. Nothing should get deleted in this case.
+ SetupMockGroup();
+ service_->CheckAppCacheResponse(kManifestUrl, kMissingCacheId,
+ kMissingResponseId);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, CountPendingHelpers());
+ EXPECT_TRUE(IsGroupStored(kManifestUrl));
+ ResetStorage();
+
+ // Check a response with bad headers.
+ SetupMockGroup();
+ service_->CheckAppCacheResponse(kManifestUrl, kMockCacheId, kMockResponseId);
+ SetupMockReader(false, true, true);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, CountPendingHelpers());
+ EXPECT_FALSE(IsGroupStored(kManifestUrl));
+ ResetStorage();
+
+ // Check a response with bad data.
+ SetupMockGroup();
+ service_->CheckAppCacheResponse(kManifestUrl, kMockCacheId, kMockResponseId);
+ SetupMockReader(true, false, true);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, CountPendingHelpers());
+ EXPECT_FALSE(IsGroupStored(kManifestUrl));
+ ResetStorage();
+
+ // Check a response with truncated data.
+ SetupMockGroup();
+ service_->CheckAppCacheResponse(kManifestUrl, kMockCacheId, kMockResponseId);
+ SetupMockReader(true, true, false);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, CountPendingHelpers());
+ EXPECT_FALSE(IsGroupStored(kManifestUrl));
+ ResetStorage();
+
+ service_.reset(); // Clean up.
+ base::RunLoop().RunUntilIdle();
+}
+
+// Just tests the backoff scheduling function, not the actual reinit function.
+TEST_F(AppCacheServiceImplTest, ScheduleReinitialize) {
+ const base::TimeDelta kNoDelay;
+ const base::TimeDelta kOneSecond(base::TimeDelta::FromSeconds(1));
+ const base::TimeDelta k30Seconds(base::TimeDelta::FromSeconds(30));
+ const base::TimeDelta kOneHour(base::TimeDelta::FromHours(1));
+
+ // Do things get initialized as expected?
+ scoped_ptr<AppCacheServiceImpl> service(new AppCacheServiceImpl(NULL));
+ EXPECT_TRUE(service->last_reinit_time_.is_null());
+ EXPECT_FALSE(service->reinit_timer_.IsRunning());
+ EXPECT_EQ(kNoDelay, service->next_reinit_delay_);
+
+ // Do we see artifacts of the timer pending and such?
+ service->ScheduleReinitialize();
+ EXPECT_TRUE(service->reinit_timer_.IsRunning());
+ EXPECT_EQ(kNoDelay, service->reinit_timer_.GetCurrentDelay());
+ EXPECT_EQ(k30Seconds, service->next_reinit_delay_);
+
+ // Nothing should change if already scheduled
+ service->ScheduleReinitialize();
+ EXPECT_TRUE(service->reinit_timer_.IsRunning());
+ EXPECT_EQ(kNoDelay, service->reinit_timer_.GetCurrentDelay());
+ EXPECT_EQ(k30Seconds, service->next_reinit_delay_);
+
+ // Does the delay increase as expected?
+ service->reinit_timer_.Stop();
+ service->last_reinit_time_ = base::Time::Now() - kOneSecond;
+ service->ScheduleReinitialize();
+ EXPECT_TRUE(service->reinit_timer_.IsRunning());
+ EXPECT_EQ(k30Seconds, service->reinit_timer_.GetCurrentDelay());
+ EXPECT_EQ(k30Seconds + k30Seconds, service->next_reinit_delay_);
+
+ // Does the delay reset as expected?
+ service->reinit_timer_.Stop();
+ service->last_reinit_time_ = base::Time::Now() -
+ base::TimeDelta::FromHours(2);
+ service->ScheduleReinitialize();
+ EXPECT_TRUE(service->reinit_timer_.IsRunning());
+ EXPECT_EQ(kNoDelay, service->reinit_timer_.GetCurrentDelay());
+ EXPECT_EQ(k30Seconds, service->next_reinit_delay_);
+
+ // Does the delay max out as expected?
+ service->reinit_timer_.Stop();
+ service->last_reinit_time_ = base::Time::Now() - kOneSecond;
+ service->next_reinit_delay_ = kOneHour;
+ service->ScheduleReinitialize();
+ EXPECT_TRUE(service->reinit_timer_.IsRunning());
+ EXPECT_EQ(kOneHour, service->reinit_timer_.GetCurrentDelay());
+ EXPECT_EQ(kOneHour, service->next_reinit_delay_);
+
+ // Fine to delete while pending.
+ service.reset(NULL);
+}
+
+
+
+} // namespace content
diff --git a/chromium/content/browser/appcache/appcache_storage_impl_unittest.cc b/chromium/content/browser/appcache/appcache_storage_impl_unittest.cc
new file mode 100644
index 00000000000..4c467a1a2dc
--- /dev/null
+++ b/chromium/content/browser/appcache/appcache_storage_impl_unittest.cc
@@ -0,0 +1,2050 @@
+// 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 <stack>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "content/browser/appcache/appcache_interceptor.h"
+#include "net/base/net_errors.h"
+#include "net/base/request_priority.h"
+#include "net/http/http_response_headers.h"
+#include "net/url_request/url_request_error_job.h"
+#include "net/url_request/url_request_job_factory_impl.h"
+#include "net/url_request/url_request_test_job.h"
+#include "net/url_request/url_request_test_util.h"
+#include "sql/test/test_helpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/appcache/appcache.h"
+#include "webkit/browser/appcache/appcache_backend_impl.h"
+#include "webkit/browser/appcache/appcache_database.h"
+#include "webkit/browser/appcache/appcache_entry.h"
+#include "webkit/browser/appcache/appcache_group.h"
+#include "webkit/browser/appcache/appcache_host.h"
+#include "webkit/browser/appcache/appcache_request_handler.h"
+#include "webkit/browser/appcache/appcache_service_impl.h"
+#include "webkit/browser/appcache/appcache_storage_impl.h"
+#include "webkit/browser/quota/quota_manager.h"
+
+using appcache::APPCACHE_FALLBACK_NAMESPACE;
+using appcache::APPCACHE_NETWORK_NAMESPACE;
+using appcache::AppCacheBackendImpl;
+using appcache::AppCacheDatabase;
+using appcache::AppCacheEntry;
+using appcache::AppCacheFrontend;
+using appcache::AppCacheHost;
+using appcache::AppCacheInfo;
+using appcache::AppCacheGroup;
+using appcache::AppCacheServiceImpl;
+using appcache::AppCacheStorage;
+using appcache::AppCacheStorageImpl;
+using appcache::AppCacheStorageReference;
+using appcache::AppCache;
+using appcache::AppCacheErrorDetails;
+using appcache::AppCacheEventID;
+using appcache::kAppCacheNoCacheId;
+using appcache::kAppCacheNoResponseId;
+using appcache::APPCACHE_INTERCEPT_NAMESPACE;
+using appcache::AppCacheLogLevel;
+using appcache::Namespace;
+using appcache::AppCacheStatus;
+
+namespace content {
+
+namespace {
+
+const base::Time kZeroTime;
+const GURL kManifestUrl("http://blah/manifest");
+const GURL kManifestUrl2("http://blah/manifest2");
+const GURL kManifestUrl3("http://blah/manifest3");
+const GURL kEntryUrl("http://blah/entry");
+const GURL kEntryUrl2("http://blah/entry2");
+const GURL kFallbackNamespace("http://blah/fallback_namespace/");
+const GURL kFallbackNamespace2("http://blah/fallback_namespace/longer");
+const GURL kFallbackTestUrl("http://blah/fallback_namespace/longer/test");
+const GURL kOnlineNamespace("http://blah/online_namespace");
+const GURL kOnlineNamespaceWithinFallback(
+ "http://blah/fallback_namespace/online/");
+const GURL kInterceptNamespace("http://blah/intercept_namespace/");
+const GURL kInterceptNamespace2("http://blah/intercept_namespace/longer/");
+const GURL kInterceptTestUrl("http://blah/intercept_namespace/longer/test");
+const GURL kInterceptPatternNamespace("http://blah/intercept_pattern/*/bar");
+const GURL kInterceptPatternTestPositiveUrl(
+ "http://blah/intercept_pattern/foo/bar");
+const GURL kInterceptPatternTestNegativeUrl(
+ "http://blah/intercept_pattern/foo/not_bar");
+const GURL kFallbackPatternNamespace("http://blah/fallback_pattern/*/bar");
+const GURL kFallbackPatternTestPositiveUrl(
+ "http://blah/fallback_pattern/foo/bar");
+const GURL kFallbackPatternTestNegativeUrl(
+ "http://blah/fallback_pattern/foo/not_bar");
+const GURL kOrigin(kManifestUrl.GetOrigin());
+
+const int kManifestEntryIdOffset = 100;
+const int kFallbackEntryIdOffset = 1000;
+
+const GURL kDefaultEntryUrl("http://blah/makecacheandgroup_default_entry");
+const int kDefaultEntrySize = 10;
+const int kDefaultEntryIdOffset = 12345;
+
+const int kMockQuota = 5000;
+
+// The Reinitialize test needs some http accessible resources to run,
+// we mock stuff inprocess for that.
+class MockHttpServer {
+ public:
+ static GURL GetMockUrl(const std::string& path) {
+ return GURL("http://mockhost/" + path);
+ }
+
+ static net::URLRequestJob* CreateJob(
+ net::URLRequest* request, net::NetworkDelegate* network_delegate) {
+ if (request->url().host() != "mockhost")
+ return new net::URLRequestErrorJob(request, network_delegate, -100);
+
+ std::string headers, body;
+ GetMockResponse(request->url().path(), &headers, &body);
+ return new net::URLRequestTestJob(
+ request, network_delegate, headers, body, true);
+ }
+
+ private:
+ static void GetMockResponse(const std::string& path,
+ std::string* headers,
+ std::string* body) {
+ const char manifest_headers[] =
+ "HTTP/1.1 200 OK\0"
+ "Content-type: text/cache-manifest\0"
+ "\0";
+ const char page_headers[] =
+ "HTTP/1.1 200 OK\0"
+ "Content-type: text/html\0"
+ "\0";
+ const char not_found_headers[] =
+ "HTTP/1.1 404 NOT FOUND\0"
+ "\0";
+
+ if (path == "/manifest") {
+ (*headers) = std::string(manifest_headers, arraysize(manifest_headers));
+ (*body) = "CACHE MANIFEST\n";
+ } else if (path == "/empty.html") {
+ (*headers) = std::string(page_headers, arraysize(page_headers));
+ (*body) = "";
+ } else {
+ (*headers) = std::string(not_found_headers,
+ arraysize(not_found_headers));
+ (*body) = "";
+ }
+ }
+};
+
+class MockHttpServerJobFactory
+ : public net::URLRequestJobFactory::ProtocolHandler {
+ public:
+ virtual net::URLRequestJob* MaybeCreateJob(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) const OVERRIDE {
+ return MockHttpServer::CreateJob(request, network_delegate);
+ }
+};
+
+class IOThread : public base::Thread {
+ public:
+ explicit IOThread(const char* name)
+ : base::Thread(name) {
+ }
+
+ virtual ~IOThread() {
+ Stop();
+ }
+
+ net::URLRequestContext* request_context() {
+ return request_context_.get();
+ }
+
+ virtual void Init() OVERRIDE {
+ scoped_ptr<net::URLRequestJobFactoryImpl> factory(
+ new net::URLRequestJobFactoryImpl());
+ factory->SetProtocolHandler("http", new MockHttpServerJobFactory);
+ job_factory_ = factory.Pass();
+ request_context_.reset(new net::TestURLRequestContext());
+ request_context_->set_job_factory(job_factory_.get());
+ AppCacheInterceptor::EnsureRegistered();
+ }
+
+ virtual void CleanUp() OVERRIDE {
+ request_context_.reset();
+ job_factory_.reset();
+ }
+
+ private:
+ scoped_ptr<net::URLRequestJobFactory> job_factory_;
+ scoped_ptr<net::URLRequestContext> request_context_;
+};
+
+scoped_ptr<IOThread> io_thread;
+scoped_ptr<base::Thread> db_thread;
+
+} // namespace
+
+class AppCacheStorageImplTest : public testing::Test {
+ public:
+ class MockStorageDelegate : public AppCacheStorage::Delegate {
+ public:
+ explicit MockStorageDelegate(AppCacheStorageImplTest* test)
+ : loaded_cache_id_(0), stored_group_success_(false),
+ would_exceed_quota_(false), obsoleted_success_(false),
+ found_cache_id_(kAppCacheNoCacheId), test_(test) {
+ }
+
+ virtual void OnCacheLoaded(AppCache* cache, int64 cache_id) OVERRIDE {
+ loaded_cache_ = cache;
+ loaded_cache_id_ = cache_id;
+ test_->ScheduleNextTask();
+ }
+
+ virtual void OnGroupLoaded(AppCacheGroup* group,
+ const GURL& manifest_url) OVERRIDE {
+ loaded_group_ = group;
+ loaded_manifest_url_ = manifest_url;
+ loaded_groups_newest_cache_ = group ? group->newest_complete_cache()
+ : NULL;
+ test_->ScheduleNextTask();
+ }
+
+ virtual void OnGroupAndNewestCacheStored(
+ AppCacheGroup* group, AppCache* newest_cache, bool success,
+ bool would_exceed_quota) OVERRIDE {
+ stored_group_ = group;
+ stored_group_success_ = success;
+ would_exceed_quota_ = would_exceed_quota;
+ test_->ScheduleNextTask();
+ }
+
+ virtual void OnGroupMadeObsolete(AppCacheGroup* group,
+ bool success,
+ int response_code) OVERRIDE {
+ obsoleted_group_ = group;
+ obsoleted_success_ = success;
+ test_->ScheduleNextTask();
+ }
+
+ virtual void OnMainResponseFound(const GURL& url,
+ const AppCacheEntry& entry,
+ const GURL& namespace_entry_url,
+ const AppCacheEntry& fallback_entry,
+ int64 cache_id,
+ int64 group_id,
+ const GURL& manifest_url) OVERRIDE {
+ found_url_ = url;
+ found_entry_ = entry;
+ found_namespace_entry_url_ = namespace_entry_url;
+ found_fallback_entry_ = fallback_entry;
+ found_cache_id_ = cache_id;
+ found_group_id_ = group_id;
+ found_manifest_url_ = manifest_url;
+ test_->ScheduleNextTask();
+ }
+
+ scoped_refptr<AppCache> loaded_cache_;
+ int64 loaded_cache_id_;
+ scoped_refptr<AppCacheGroup> loaded_group_;
+ GURL loaded_manifest_url_;
+ scoped_refptr<AppCache> loaded_groups_newest_cache_;
+ scoped_refptr<AppCacheGroup> stored_group_;
+ bool stored_group_success_;
+ bool would_exceed_quota_;
+ scoped_refptr<AppCacheGroup> obsoleted_group_;
+ bool obsoleted_success_;
+ GURL found_url_;
+ AppCacheEntry found_entry_;
+ GURL found_namespace_entry_url_;
+ AppCacheEntry found_fallback_entry_;
+ int64 found_cache_id_;
+ int64 found_group_id_;
+ GURL found_manifest_url_;
+ AppCacheStorageImplTest* test_;
+ };
+
+ class MockQuotaManager : public quota::QuotaManager {
+ public:
+ MockQuotaManager()
+ : QuotaManager(true /* is_incognito */,
+ base::FilePath(),
+ io_thread->message_loop_proxy().get(),
+ db_thread->message_loop_proxy().get(),
+ NULL),
+ async_(false) {}
+
+ virtual void GetUsageAndQuota(
+ const GURL& origin,
+ quota::StorageType type,
+ const GetUsageAndQuotaCallback& callback) OVERRIDE {
+ EXPECT_EQ(quota::kStorageTypeTemporary, type);
+ if (async_) {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&MockQuotaManager::CallCallback,
+ base::Unretained(this),
+ callback));
+ return;
+ }
+ CallCallback(callback);
+ }
+
+ void CallCallback(const GetUsageAndQuotaCallback& callback) {
+ callback.Run(quota::kQuotaStatusOk, 0, kMockQuota);
+ }
+
+ bool async_;
+
+ protected:
+ virtual ~MockQuotaManager() {}
+ };
+
+ class MockQuotaManagerProxy : public quota::QuotaManagerProxy {
+ public:
+ MockQuotaManagerProxy()
+ : QuotaManagerProxy(NULL, NULL),
+ notify_storage_accessed_count_(0),
+ notify_storage_modified_count_(0),
+ last_delta_(0),
+ mock_manager_(new MockQuotaManager) {
+ manager_ = mock_manager_.get();
+ }
+
+ virtual void NotifyStorageAccessed(quota::QuotaClient::ID client_id,
+ const GURL& origin,
+ quota::StorageType type) OVERRIDE {
+ EXPECT_EQ(quota::QuotaClient::kAppcache, client_id);
+ EXPECT_EQ(quota::kStorageTypeTemporary, type);
+ ++notify_storage_accessed_count_;
+ last_origin_ = origin;
+ }
+
+ virtual void NotifyStorageModified(quota::QuotaClient::ID client_id,
+ const GURL& origin,
+ quota::StorageType type,
+ int64 delta) OVERRIDE {
+ EXPECT_EQ(quota::QuotaClient::kAppcache, client_id);
+ EXPECT_EQ(quota::kStorageTypeTemporary, type);
+ ++notify_storage_modified_count_;
+ last_origin_ = origin;
+ last_delta_ = delta;
+ }
+
+ // Not needed for our tests.
+ virtual void RegisterClient(quota::QuotaClient* client) OVERRIDE {}
+ virtual void NotifyOriginInUse(const GURL& origin) OVERRIDE {}
+ virtual void NotifyOriginNoLongerInUse(const GURL& origin) OVERRIDE {}
+ virtual void SetUsageCacheEnabled(quota::QuotaClient::ID client_id,
+ const GURL& origin,
+ quota::StorageType type,
+ bool enabled) OVERRIDE {}
+ virtual void GetUsageAndQuota(
+ base::SequencedTaskRunner* original_task_runner,
+ const GURL& origin,
+ quota::StorageType type,
+ const GetUsageAndQuotaCallback& callback) OVERRIDE {}
+
+ int notify_storage_accessed_count_;
+ int notify_storage_modified_count_;
+ GURL last_origin_;
+ int last_delta_;
+ scoped_refptr<MockQuotaManager> mock_manager_;
+
+ protected:
+ virtual ~MockQuotaManagerProxy() {}
+ };
+
+ template <class Method>
+ void RunMethod(Method method) {
+ (this->*method)();
+ }
+
+ // Helper callback to run a test on our io_thread. The io_thread is spun up
+ // once and reused for all tests.
+ template <class Method>
+ void MethodWrapper(Method method) {
+ SetUpTest();
+
+ // Ensure InitTask execution prior to conducting a test.
+ FlushDbThreadTasks();
+
+ // We also have to wait for InitTask completion call to be performed
+ // on the IO thread prior to running the test. Its guaranteed to be
+ // queued by this time.
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&AppCacheStorageImplTest::RunMethod<Method>,
+ base::Unretained(this),
+ method));
+ }
+
+ static void SetUpTestCase() {
+ // We start both threads as TYPE_IO because we also use the db_thead
+ // for the disk_cache which needs to be of TYPE_IO.
+ base::Thread::Options options(base::MessageLoop::TYPE_IO, 0);
+ io_thread.reset(new IOThread("AppCacheTest.IOThread"));
+ ASSERT_TRUE(io_thread->StartWithOptions(options));
+ db_thread.reset(new base::Thread("AppCacheTest::DBThread"));
+ ASSERT_TRUE(db_thread->StartWithOptions(options));
+ }
+
+ static void TearDownTestCase() {
+ io_thread.reset(NULL);
+ db_thread.reset(NULL);
+ }
+
+ // Test harness --------------------------------------------------
+
+ AppCacheStorageImplTest() {
+ }
+
+ template <class Method>
+ void RunTestOnIOThread(Method method) {
+ test_finished_event_ .reset(new base::WaitableEvent(false, false));
+ io_thread->message_loop()->PostTask(
+ FROM_HERE, base::Bind(&AppCacheStorageImplTest::MethodWrapper<Method>,
+ base::Unretained(this), method));
+ test_finished_event_->Wait();
+ }
+
+ void SetUpTest() {
+ DCHECK(base::MessageLoop::current() == io_thread->message_loop());
+ service_.reset(new AppCacheServiceImpl(NULL));
+ service_->Initialize(
+ base::FilePath(), db_thread->message_loop_proxy().get(), NULL);
+ mock_quota_manager_proxy_ = new MockQuotaManagerProxy();
+ service_->quota_manager_proxy_ = mock_quota_manager_proxy_;
+ delegate_.reset(new MockStorageDelegate(this));
+ }
+
+ void TearDownTest() {
+ DCHECK(base::MessageLoop::current() == io_thread->message_loop());
+ storage()->CancelDelegateCallbacks(delegate());
+ group_ = NULL;
+ cache_ = NULL;
+ cache2_ = NULL;
+ mock_quota_manager_proxy_ = NULL;
+ delegate_.reset();
+ service_.reset();
+ FlushDbThreadTasks();
+ }
+
+ void TestFinished() {
+ // We unwind the stack prior to finishing up to let stack
+ // based objects get deleted.
+ DCHECK(base::MessageLoop::current() == io_thread->message_loop());
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&AppCacheStorageImplTest::TestFinishedUnwound,
+ base::Unretained(this)));
+ }
+
+ void TestFinishedUnwound() {
+ TearDownTest();
+ test_finished_event_->Signal();
+ }
+
+ void PushNextTask(const base::Closure& task) {
+ task_stack_.push(task);
+ }
+
+ void ScheduleNextTask() {
+ DCHECK(base::MessageLoop::current() == io_thread->message_loop());
+ if (task_stack_.empty()) {
+ return;
+ }
+ base::MessageLoop::current()->PostTask(FROM_HERE, task_stack_.top());
+ task_stack_.pop();
+ }
+
+ static void SignalEvent(base::WaitableEvent* event) {
+ event->Signal();
+ }
+
+ void FlushDbThreadTasks() {
+ // We pump a task thru the db thread to ensure any tasks previously
+ // scheduled on that thread have been performed prior to return.
+ base::WaitableEvent event(false, false);
+ db_thread->message_loop()->PostTask(
+ FROM_HERE, base::Bind(&AppCacheStorageImplTest::SignalEvent, &event));
+ event.Wait();
+ }
+
+ // LoadCache_Miss ----------------------------------------------------
+
+ void LoadCache_Miss() {
+ // Attempt to load a cache that doesn't exist. Should
+ // complete asynchronously.
+ PushNextTask(base::Bind(&AppCacheStorageImplTest::Verify_LoadCache_Miss,
+ base::Unretained(this)));
+
+ storage()->LoadCache(111, delegate());
+ EXPECT_NE(111, delegate()->loaded_cache_id_);
+ }
+
+ void Verify_LoadCache_Miss() {
+ EXPECT_EQ(111, delegate()->loaded_cache_id_);
+ EXPECT_FALSE(delegate()->loaded_cache_.get());
+ EXPECT_EQ(0, mock_quota_manager_proxy_->notify_storage_accessed_count_);
+ EXPECT_EQ(0, mock_quota_manager_proxy_->notify_storage_modified_count_);
+ TestFinished();
+ }
+
+ // LoadCache_NearHit -------------------------------------------------
+
+ void LoadCache_NearHit() {
+ // Attempt to load a cache that is currently in use
+ // and does not require loading from storage. This
+ // load should complete syncly.
+
+ // Setup some preconditions. Make an 'unstored' cache for
+ // us to load. The ctor should put it in the working set.
+ int64 cache_id = storage()->NewCacheId();
+ scoped_refptr<AppCache> cache(new AppCache(storage(), cache_id));
+
+ // Conduct the test.
+ storage()->LoadCache(cache_id, delegate());
+ EXPECT_EQ(cache_id, delegate()->loaded_cache_id_);
+ EXPECT_EQ(cache.get(), delegate()->loaded_cache_.get());
+ EXPECT_EQ(0, mock_quota_manager_proxy_->notify_storage_accessed_count_);
+ EXPECT_EQ(0, mock_quota_manager_proxy_->notify_storage_modified_count_);
+ TestFinished();
+ }
+
+ // CreateGroup --------------------------------------------
+
+ void CreateGroupInEmptyOrigin() {
+ // Attempt to load a group that doesn't exist, one should
+ // be created for us, but not stored.
+
+ // Since the origin has no groups, the storage class will respond
+ // syncly.
+ storage()->LoadOrCreateGroup(kManifestUrl, delegate());
+ Verify_CreateGroup();
+ }
+
+ void CreateGroupInPopulatedOrigin() {
+ // Attempt to load a group that doesn't exist, one should
+ // be created for us, but not stored.
+ PushNextTask(base::Bind(&AppCacheStorageImplTest::Verify_CreateGroup,
+ base::Unretained(this)));
+
+ // Since the origin has groups, storage class will have to
+ // consult the database and completion will be async.
+ storage()->usage_map_[kOrigin] = kDefaultEntrySize;
+
+ storage()->LoadOrCreateGroup(kManifestUrl, delegate());
+ EXPECT_FALSE(delegate()->loaded_group_.get());
+ }
+
+ void Verify_CreateGroup() {
+ EXPECT_EQ(kManifestUrl, delegate()->loaded_manifest_url_);
+ EXPECT_TRUE(delegate()->loaded_group_.get());
+ EXPECT_TRUE(delegate()->loaded_group_->HasOneRef());
+ EXPECT_FALSE(delegate()->loaded_group_->newest_complete_cache());
+
+ // Should not have been stored in the database.
+ AppCacheDatabase::GroupRecord record;
+ EXPECT_FALSE(database()->FindGroup(
+ delegate()->loaded_group_->group_id(), &record));
+
+ EXPECT_EQ(0, mock_quota_manager_proxy_->notify_storage_accessed_count_);
+ EXPECT_EQ(0, mock_quota_manager_proxy_->notify_storage_modified_count_);
+
+ TestFinished();
+ }
+
+ // LoadGroupAndCache_FarHit --------------------------------------
+
+ void LoadGroupAndCache_FarHit() {
+ // Attempt to load a cache that is not currently in use
+ // and does require loading from disk. This
+ // load should complete asynchronously.
+ PushNextTask(base::Bind(&AppCacheStorageImplTest::Verify_LoadCache_Far_Hit,
+ base::Unretained(this)));
+
+ // Setup some preconditions. Create a group and newest cache that
+ // appear to be "stored" and "not currently in use".
+ MakeCacheAndGroup(kManifestUrl, 1, 1, true);
+ group_ = NULL;
+ cache_ = NULL;
+
+ // Conduct the cache load test, completes async
+ storage()->LoadCache(1, delegate());
+ }
+
+ void Verify_LoadCache_Far_Hit() {
+ EXPECT_TRUE(delegate()->loaded_cache_.get());
+ EXPECT_TRUE(delegate()->loaded_cache_->HasOneRef());
+ EXPECT_EQ(1, delegate()->loaded_cache_id_);
+
+ // The group should also have been loaded.
+ EXPECT_TRUE(delegate()->loaded_cache_->owning_group());
+ EXPECT_TRUE(delegate()->loaded_cache_->owning_group()->HasOneRef());
+ EXPECT_EQ(1, delegate()->loaded_cache_->owning_group()->group_id());
+
+ EXPECT_EQ(1, mock_quota_manager_proxy_->notify_storage_accessed_count_);
+ EXPECT_EQ(0, mock_quota_manager_proxy_->notify_storage_modified_count_);
+
+ // Drop things from the working set.
+ delegate()->loaded_cache_ = NULL;
+ EXPECT_FALSE(delegate()->loaded_group_.get());
+
+ // Conduct the group load test, also complete asynchronously.
+ PushNextTask(base::Bind(&AppCacheStorageImplTest::Verify_LoadGroup_Far_Hit,
+ base::Unretained(this)));
+
+ storage()->LoadOrCreateGroup(kManifestUrl, delegate());
+ }
+
+ void Verify_LoadGroup_Far_Hit() {
+ EXPECT_TRUE(delegate()->loaded_group_.get());
+ EXPECT_EQ(kManifestUrl, delegate()->loaded_manifest_url_);
+ EXPECT_TRUE(delegate()->loaded_group_->newest_complete_cache());
+ delegate()->loaded_groups_newest_cache_ = NULL;
+ EXPECT_TRUE(delegate()->loaded_group_->HasOneRef());
+ EXPECT_EQ(2, mock_quota_manager_proxy_->notify_storage_accessed_count_);
+ EXPECT_EQ(0, mock_quota_manager_proxy_->notify_storage_modified_count_);
+ TestFinished();
+ }
+
+ // StoreNewGroup --------------------------------------
+
+ void StoreNewGroup() {
+ // Store a group and its newest cache. Should complete asynchronously.
+ PushNextTask(base::Bind(&AppCacheStorageImplTest::Verify_StoreNewGroup,
+ base::Unretained(this)));
+
+ // Setup some preconditions. Create a group and newest cache that
+ // appear to be "unstored".
+ group_ = new AppCacheGroup(
+ storage(), kManifestUrl, storage()->NewGroupId());
+ cache_ = new AppCache(storage(), storage()->NewCacheId());
+ cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::EXPLICIT, 1,
+ kDefaultEntrySize));
+ // Hold a ref to the cache simulate the UpdateJob holding that ref,
+ // and hold a ref to the group to simulate the CacheHost holding that ref.
+
+ // Have the quota manager retrun asynchronously for this test.
+ mock_quota_manager_proxy_->mock_manager_->async_ = true;
+
+ // Conduct the store test.
+ storage()->StoreGroupAndNewestCache(group_.get(), cache_.get(), delegate());
+ EXPECT_FALSE(delegate()->stored_group_success_);
+ }
+
+ void Verify_StoreNewGroup() {
+ EXPECT_TRUE(delegate()->stored_group_success_);
+ EXPECT_EQ(group_.get(), delegate()->stored_group_.get());
+ EXPECT_EQ(cache_.get(), group_->newest_complete_cache());
+ EXPECT_TRUE(cache_->is_complete());
+
+ // Should have been stored in the database.
+ AppCacheDatabase::GroupRecord group_record;
+ AppCacheDatabase::CacheRecord cache_record;
+ EXPECT_TRUE(database()->FindGroup(group_->group_id(), &group_record));
+ EXPECT_TRUE(database()->FindCache(cache_->cache_id(), &cache_record));
+
+ // Verify quota bookkeeping
+ EXPECT_EQ(kDefaultEntrySize, storage()->usage_map_[kOrigin]);
+ EXPECT_EQ(1, mock_quota_manager_proxy_->notify_storage_modified_count_);
+ EXPECT_EQ(kOrigin, mock_quota_manager_proxy_->last_origin_);
+ EXPECT_EQ(kDefaultEntrySize, mock_quota_manager_proxy_->last_delta_);
+
+ TestFinished();
+ }
+
+ // StoreExistingGroup --------------------------------------
+
+ void StoreExistingGroup() {
+ // Store a group and its newest cache. Should complete asynchronously.
+ PushNextTask(base::Bind(&AppCacheStorageImplTest::Verify_StoreExistingGroup,
+ base::Unretained(this)));
+
+ // Setup some preconditions. Create a group and old complete cache
+ // that appear to be "stored"
+ MakeCacheAndGroup(kManifestUrl, 1, 1, true);
+ EXPECT_EQ(kDefaultEntrySize, storage()->usage_map_[kOrigin]);
+
+ // And a newest unstored complete cache.
+ cache2_ = new AppCache(storage(), 2);
+ cache2_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::MASTER, 1,
+ kDefaultEntrySize + 100));
+
+ // Conduct the test.
+ storage()->StoreGroupAndNewestCache(
+ group_.get(), cache2_.get(), delegate());
+ EXPECT_FALSE(delegate()->stored_group_success_);
+ }
+
+ void Verify_StoreExistingGroup() {
+ EXPECT_TRUE(delegate()->stored_group_success_);
+ EXPECT_EQ(group_.get(), delegate()->stored_group_.get());
+ EXPECT_EQ(cache2_.get(), group_->newest_complete_cache());
+ EXPECT_TRUE(cache2_->is_complete());
+
+ // The new cache should have been stored in the database.
+ AppCacheDatabase::GroupRecord group_record;
+ AppCacheDatabase::CacheRecord cache_record;
+ EXPECT_TRUE(database()->FindGroup(1, &group_record));
+ EXPECT_TRUE(database()->FindCache(2, &cache_record));
+
+ // The old cache should have been deleted
+ EXPECT_FALSE(database()->FindCache(1, &cache_record));
+
+ // Verify quota bookkeeping
+ EXPECT_EQ(kDefaultEntrySize + 100, storage()->usage_map_[kOrigin]);
+ EXPECT_EQ(1, mock_quota_manager_proxy_->notify_storage_modified_count_);
+ EXPECT_EQ(kOrigin, mock_quota_manager_proxy_->last_origin_);
+ EXPECT_EQ(100, mock_quota_manager_proxy_->last_delta_);
+
+ TestFinished();
+ }
+
+ // StoreExistingGroupExistingCache -------------------------------
+
+ void StoreExistingGroupExistingCache() {
+ // Store a group with updates to its existing newest complete cache.
+ // Setup some preconditions. Create a group and a complete cache that
+ // appear to be "stored".
+
+ // Setup some preconditions. Create a group and old complete cache
+ // that appear to be "stored"
+ MakeCacheAndGroup(kManifestUrl, 1, 1, true);
+ EXPECT_EQ(kDefaultEntrySize, storage()->usage_map_[kOrigin]);
+
+ // Change the cache.
+ base::Time now = base::Time::Now();
+ cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::MASTER, 1, 100));
+ cache_->set_update_time(now);
+
+ PushNextTask(base::Bind(
+ &AppCacheStorageImplTest::Verify_StoreExistingGroupExistingCache,
+ base::Unretained(this), now));
+
+ // Conduct the test.
+ EXPECT_EQ(cache_, group_->newest_complete_cache());
+ storage()->StoreGroupAndNewestCache(group_.get(), cache_.get(), delegate());
+ EXPECT_FALSE(delegate()->stored_group_success_);
+ }
+
+ void Verify_StoreExistingGroupExistingCache(
+ base::Time expected_update_time) {
+ EXPECT_TRUE(delegate()->stored_group_success_);
+ EXPECT_EQ(cache_, group_->newest_complete_cache());
+
+ AppCacheDatabase::CacheRecord cache_record;
+ EXPECT_TRUE(database()->FindCache(1, &cache_record));
+ EXPECT_EQ(1, cache_record.cache_id);
+ EXPECT_EQ(1, cache_record.group_id);
+ EXPECT_FALSE(cache_record.online_wildcard);
+ EXPECT_TRUE(expected_update_time == cache_record.update_time);
+ EXPECT_EQ(100 + kDefaultEntrySize, cache_record.cache_size);
+
+ std::vector<AppCacheDatabase::EntryRecord> entry_records;
+ EXPECT_TRUE(database()->FindEntriesForCache(1, &entry_records));
+ EXPECT_EQ(2U, entry_records.size());
+ if (entry_records[0].url == kDefaultEntryUrl)
+ entry_records.erase(entry_records.begin());
+ EXPECT_EQ(1 , entry_records[0].cache_id);
+ EXPECT_EQ(kEntryUrl, entry_records[0].url);
+ EXPECT_EQ(AppCacheEntry::MASTER, entry_records[0].flags);
+ EXPECT_EQ(1, entry_records[0].response_id);
+ EXPECT_EQ(100, entry_records[0].response_size);
+
+ // Verify quota bookkeeping
+ EXPECT_EQ(100 + kDefaultEntrySize, storage()->usage_map_[kOrigin]);
+ EXPECT_EQ(1, mock_quota_manager_proxy_->notify_storage_modified_count_);
+ EXPECT_EQ(kOrigin, mock_quota_manager_proxy_->last_origin_);
+ EXPECT_EQ(100, mock_quota_manager_proxy_->last_delta_);
+
+ TestFinished();
+ }
+
+ // FailStoreGroup --------------------------------------
+
+ void FailStoreGroup() {
+ // Store a group and its newest cache. Should complete asynchronously.
+ PushNextTask(base::Bind(&AppCacheStorageImplTest::Verify_FailStoreGroup,
+ base::Unretained(this)));
+
+ // Setup some preconditions. Create a group and newest cache that
+ // appear to be "unstored" and big enough to exceed the 5M limit.
+ const int64 kTooBig = 10 * 1024 * 1024; // 10M
+ group_ = new AppCacheGroup(
+ storage(), kManifestUrl, storage()->NewGroupId());
+ cache_ = new AppCache(storage(), storage()->NewCacheId());
+ cache_->AddEntry(kManifestUrl,
+ AppCacheEntry(AppCacheEntry::MANIFEST, 1, kTooBig));
+ // Hold a ref to the cache simulate the UpdateJob holding that ref,
+ // and hold a ref to the group to simulate the CacheHost holding that ref.
+
+ // Conduct the store test.
+ storage()->StoreGroupAndNewestCache(group_.get(), cache_.get(), delegate());
+ EXPECT_FALSE(delegate()->stored_group_success_); // Expected to be async.
+ }
+
+ void Verify_FailStoreGroup() {
+ EXPECT_FALSE(delegate()->stored_group_success_);
+ EXPECT_TRUE(delegate()->would_exceed_quota_);
+
+ // Should not have been stored in the database.
+ AppCacheDatabase::GroupRecord group_record;
+ AppCacheDatabase::CacheRecord cache_record;
+ EXPECT_FALSE(database()->FindGroup(group_->group_id(), &group_record));
+ EXPECT_FALSE(database()->FindCache(cache_->cache_id(), &cache_record));
+
+ EXPECT_EQ(0, mock_quota_manager_proxy_->notify_storage_accessed_count_);
+ EXPECT_EQ(0, mock_quota_manager_proxy_->notify_storage_modified_count_);
+
+ TestFinished();
+ }
+
+ // MakeGroupObsolete -------------------------------
+
+ void MakeGroupObsolete() {
+ // Make a group obsolete, should complete asynchronously.
+ PushNextTask(base::Bind(&AppCacheStorageImplTest::Verify_MakeGroupObsolete,
+ base::Unretained(this)));
+
+ // Setup some preconditions. Create a group and newest cache that
+ // appears to be "stored" and "currently in use".
+ MakeCacheAndGroup(kManifestUrl, 1, 1, true);
+ EXPECT_EQ(kDefaultEntrySize, storage()->usage_map_[kOrigin]);
+
+ // Also insert some related records.
+ AppCacheDatabase::EntryRecord entry_record;
+ entry_record.cache_id = 1;
+ entry_record.flags = AppCacheEntry::FALLBACK;
+ entry_record.response_id = 1;
+ entry_record.url = kEntryUrl;
+ EXPECT_TRUE(database()->InsertEntry(&entry_record));
+
+ AppCacheDatabase::NamespaceRecord fallback_namespace_record;
+ fallback_namespace_record.cache_id = 1;
+ fallback_namespace_record.namespace_.target_url = kEntryUrl;
+ fallback_namespace_record.namespace_.namespace_url = kFallbackNamespace;
+ fallback_namespace_record.origin = kManifestUrl.GetOrigin();
+ EXPECT_TRUE(database()->InsertNamespace(&fallback_namespace_record));
+
+ AppCacheDatabase::OnlineWhiteListRecord online_whitelist_record;
+ online_whitelist_record.cache_id = 1;
+ online_whitelist_record.namespace_url = kOnlineNamespace;
+ EXPECT_TRUE(database()->InsertOnlineWhiteList(&online_whitelist_record));
+
+ // Conduct the test.
+ storage()->MakeGroupObsolete(group_.get(), delegate(), 0);
+ EXPECT_FALSE(group_->is_obsolete());
+ }
+
+ void Verify_MakeGroupObsolete() {
+ EXPECT_TRUE(delegate()->obsoleted_success_);
+ EXPECT_EQ(group_.get(), delegate()->obsoleted_group_.get());
+ EXPECT_TRUE(group_->is_obsolete());
+ EXPECT_TRUE(storage()->usage_map_.empty());
+
+ // The cache and group have been deleted from the database.
+ AppCacheDatabase::GroupRecord group_record;
+ AppCacheDatabase::CacheRecord cache_record;
+ EXPECT_FALSE(database()->FindGroup(1, &group_record));
+ EXPECT_FALSE(database()->FindCache(1, &cache_record));
+
+ // The related records should have been deleted too.
+ std::vector<AppCacheDatabase::EntryRecord> entry_records;
+ database()->FindEntriesForCache(1, &entry_records);
+ EXPECT_TRUE(entry_records.empty());
+ std::vector<AppCacheDatabase::NamespaceRecord> intercept_records;
+ std::vector<AppCacheDatabase::NamespaceRecord> fallback_records;
+ database()->FindNamespacesForCache(
+ 1, &intercept_records, &fallback_records);
+ EXPECT_TRUE(fallback_records.empty());
+ std::vector<AppCacheDatabase::OnlineWhiteListRecord> whitelist_records;
+ database()->FindOnlineWhiteListForCache(1, &whitelist_records);
+ EXPECT_TRUE(whitelist_records.empty());
+
+ // Verify quota bookkeeping
+ EXPECT_TRUE(storage()->usage_map_.empty());
+ EXPECT_EQ(1, mock_quota_manager_proxy_->notify_storage_modified_count_);
+ EXPECT_EQ(kOrigin, mock_quota_manager_proxy_->last_origin_);
+ EXPECT_EQ(-kDefaultEntrySize, mock_quota_manager_proxy_->last_delta_);
+
+ TestFinished();
+ }
+
+ // MarkEntryAsForeign -------------------------------
+
+ void MarkEntryAsForeign() {
+ // Setup some preconditions. Create a cache with an entry
+ // in storage and in the working set.
+ MakeCacheAndGroup(kManifestUrl, 1, 1, true);
+ cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::EXPLICIT));
+ AppCacheDatabase::EntryRecord entry_record;
+ entry_record.cache_id = 1;
+ entry_record.url = kEntryUrl;
+ entry_record.flags = AppCacheEntry::EXPLICIT;
+ entry_record.response_id = 0;
+ EXPECT_TRUE(database()->InsertEntry(&entry_record));
+ EXPECT_FALSE(cache_->GetEntry(kEntryUrl)->IsForeign());
+
+ // Conduct the test.
+ storage()->MarkEntryAsForeign(kEntryUrl, 1);
+
+ // The entry in the working set should have been updated syncly.
+ EXPECT_TRUE(cache_->GetEntry(kEntryUrl)->IsForeign());
+ EXPECT_TRUE(cache_->GetEntry(kEntryUrl)->IsExplicit());
+
+ // And the entry in storage should also be updated, but that
+ // happens asynchronously on the db thread.
+ FlushDbThreadTasks();
+ AppCacheDatabase::EntryRecord entry_record2;
+ EXPECT_TRUE(database()->FindEntry(1, kEntryUrl, &entry_record2));
+ EXPECT_EQ(AppCacheEntry::EXPLICIT | AppCacheEntry::FOREIGN,
+ entry_record2.flags);
+ TestFinished();
+ }
+
+ // MarkEntryAsForeignWithLoadInProgress -------------------------------
+
+ void MarkEntryAsForeignWithLoadInProgress() {
+ PushNextTask(base::Bind(
+ &AppCacheStorageImplTest::Verify_MarkEntryAsForeignWithLoadInProgress,
+ base::Unretained(this)));
+
+ // Setup some preconditions. Create a cache with an entry
+ // in storage, but not in the working set.
+ MakeCacheAndGroup(kManifestUrl, 1, 1, true);
+ cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::EXPLICIT));
+ AppCacheDatabase::EntryRecord entry_record;
+ entry_record.cache_id = 1;
+ entry_record.url = kEntryUrl;
+ entry_record.flags = AppCacheEntry::EXPLICIT;
+ entry_record.response_id = 0;
+ EXPECT_TRUE(database()->InsertEntry(&entry_record));
+ EXPECT_FALSE(cache_->GetEntry(kEntryUrl)->IsForeign());
+ EXPECT_TRUE(cache_->HasOneRef());
+ cache_ = NULL;
+ group_ = NULL;
+
+ // Conduct the test, start a cache load, and prior to completion
+ // of that load, mark the entry as foreign.
+ storage()->LoadCache(1, delegate());
+ storage()->MarkEntryAsForeign(kEntryUrl, 1);
+ }
+
+ void Verify_MarkEntryAsForeignWithLoadInProgress() {
+ EXPECT_EQ(1, delegate()->loaded_cache_id_);
+ EXPECT_TRUE(delegate()->loaded_cache_.get());
+
+ // The entry in the working set should have been updated upon load.
+ EXPECT_TRUE(delegate()->loaded_cache_->GetEntry(kEntryUrl)->IsForeign());
+ EXPECT_TRUE(delegate()->loaded_cache_->GetEntry(kEntryUrl)->IsExplicit());
+
+ // And the entry in storage should also be updated.
+ FlushDbThreadTasks();
+ AppCacheDatabase::EntryRecord entry_record;
+ EXPECT_TRUE(database()->FindEntry(1, kEntryUrl, &entry_record));
+ EXPECT_EQ(AppCacheEntry::EXPLICIT | AppCacheEntry::FOREIGN,
+ entry_record.flags);
+ TestFinished();
+ }
+
+ // FindNoMainResponse -------------------------------
+
+ void FindNoMainResponse() {
+ PushNextTask(base::Bind(&AppCacheStorageImplTest::Verify_FindNoMainResponse,
+ base::Unretained(this)));
+
+ // Conduct the test.
+ storage()->FindResponseForMainRequest(kEntryUrl, GURL(), delegate());
+ EXPECT_NE(kEntryUrl, delegate()->found_url_);
+ }
+
+ void Verify_FindNoMainResponse() {
+ EXPECT_EQ(kEntryUrl, delegate()->found_url_);
+ EXPECT_TRUE(delegate()->found_manifest_url_.is_empty());
+ EXPECT_EQ(kAppCacheNoCacheId, delegate()->found_cache_id_);
+ EXPECT_EQ(kAppCacheNoResponseId, delegate()->found_entry_.response_id());
+ EXPECT_EQ(kAppCacheNoResponseId,
+ delegate()->found_fallback_entry_.response_id());
+ EXPECT_TRUE(delegate()->found_namespace_entry_url_.is_empty());
+ EXPECT_EQ(0, delegate()->found_entry_.types());
+ EXPECT_EQ(0, delegate()->found_fallback_entry_.types());
+ TestFinished();
+ }
+
+ // BasicFindMainResponse -------------------------------
+
+ void BasicFindMainResponseInDatabase() {
+ BasicFindMainResponse(true);
+ }
+
+ void BasicFindMainResponseInWorkingSet() {
+ BasicFindMainResponse(false);
+ }
+
+ void BasicFindMainResponse(bool drop_from_working_set) {
+ PushNextTask(base::Bind(
+ &AppCacheStorageImplTest::Verify_BasicFindMainResponse,
+ base::Unretained(this)));
+
+ // Setup some preconditions. Create a complete cache with an entry
+ // in storage.
+ MakeCacheAndGroup(kManifestUrl, 2, 1, true);
+ cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::EXPLICIT, 1));
+ AppCacheDatabase::EntryRecord entry_record;
+ entry_record.cache_id = 1;
+ entry_record.url = kEntryUrl;
+ entry_record.flags = AppCacheEntry::EXPLICIT;
+ entry_record.response_id = 1;
+ EXPECT_TRUE(database()->InsertEntry(&entry_record));
+
+ // Optionally drop the cache/group pair from the working set.
+ if (drop_from_working_set) {
+ EXPECT_TRUE(cache_->HasOneRef());
+ cache_ = NULL;
+ EXPECT_TRUE(group_->HasOneRef());
+ group_ = NULL;
+ }
+
+ // Conduct the test.
+ storage()->FindResponseForMainRequest(kEntryUrl, GURL(), delegate());
+ EXPECT_NE(kEntryUrl, delegate()->found_url_);
+ }
+
+ void Verify_BasicFindMainResponse() {
+ EXPECT_EQ(kEntryUrl, delegate()->found_url_);
+ EXPECT_EQ(kManifestUrl, delegate()->found_manifest_url_);
+ EXPECT_EQ(1, delegate()->found_cache_id_);
+ EXPECT_EQ(2, delegate()->found_group_id_);
+ EXPECT_EQ(1, delegate()->found_entry_.response_id());
+ EXPECT_TRUE(delegate()->found_entry_.IsExplicit());
+ EXPECT_FALSE(delegate()->found_fallback_entry_.has_response_id());
+ TestFinished();
+ }
+
+ // BasicFindMainFallbackResponse -------------------------------
+
+ void BasicFindMainFallbackResponseInDatabase() {
+ BasicFindMainFallbackResponse(true);
+ }
+
+ void BasicFindMainFallbackResponseInWorkingSet() {
+ BasicFindMainFallbackResponse(false);
+ }
+
+ void BasicFindMainFallbackResponse(bool drop_from_working_set) {
+ PushNextTask(base::Bind(
+ &AppCacheStorageImplTest::Verify_BasicFindMainFallbackResponse,
+ base::Unretained(this)));
+
+ // Setup some preconditions. Create a complete cache with a
+ // fallback namespace and entry.
+ MakeCacheAndGroup(kManifestUrl, 2, 1, true);
+ cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::FALLBACK, 1));
+ cache_->AddEntry(kEntryUrl2, AppCacheEntry(AppCacheEntry::FALLBACK, 2));
+ cache_->fallback_namespaces_.push_back(
+ Namespace(APPCACHE_FALLBACK_NAMESPACE,
+ kFallbackNamespace2,
+ kEntryUrl2,
+ false));
+ cache_->fallback_namespaces_.push_back(
+ Namespace(APPCACHE_FALLBACK_NAMESPACE,
+ kFallbackNamespace,
+ kEntryUrl,
+ false));
+ AppCacheDatabase::CacheRecord cache_record;
+ std::vector<AppCacheDatabase::EntryRecord> entries;
+ std::vector<AppCacheDatabase::NamespaceRecord> intercepts;
+ std::vector<AppCacheDatabase::NamespaceRecord> fallbacks;
+ std::vector<AppCacheDatabase::OnlineWhiteListRecord> whitelists;
+ cache_->ToDatabaseRecords(group_.get(),
+ &cache_record,
+ &entries,
+ &intercepts,
+ &fallbacks,
+ &whitelists);
+
+ std::vector<AppCacheDatabase::EntryRecord>::const_iterator iter =
+ entries.begin();
+ while (iter != entries.end()) {
+ // MakeCacheAndGroup has inserted the default entry record already.
+ if (iter->url != kDefaultEntryUrl)
+ EXPECT_TRUE(database()->InsertEntry(&(*iter)));
+ ++iter;
+ }
+
+ EXPECT_TRUE(database()->InsertNamespaceRecords(fallbacks));
+ EXPECT_TRUE(database()->InsertOnlineWhiteListRecords(whitelists));
+ if (drop_from_working_set) {
+ EXPECT_TRUE(cache_->HasOneRef());
+ cache_ = NULL;
+ EXPECT_TRUE(group_->HasOneRef());
+ group_ = NULL;
+ }
+
+ // Conduct the test. The test url is in both fallback namespace urls,
+ // but should match the longer of the two.
+ storage()->FindResponseForMainRequest(kFallbackTestUrl, GURL(), delegate());
+ EXPECT_NE(kFallbackTestUrl, delegate()->found_url_);
+ }
+
+ void Verify_BasicFindMainFallbackResponse() {
+ EXPECT_EQ(kFallbackTestUrl, delegate()->found_url_);
+ EXPECT_EQ(kManifestUrl, delegate()->found_manifest_url_);
+ EXPECT_EQ(1, delegate()->found_cache_id_);
+ EXPECT_EQ(2, delegate()->found_group_id_);
+ EXPECT_FALSE(delegate()->found_entry_.has_response_id());
+ EXPECT_EQ(2, delegate()->found_fallback_entry_.response_id());
+ EXPECT_EQ(kEntryUrl2, delegate()->found_namespace_entry_url_);
+ EXPECT_TRUE(delegate()->found_fallback_entry_.IsFallback());
+ TestFinished();
+ }
+
+ // BasicFindMainInterceptResponse -------------------------------
+
+ void BasicFindMainInterceptResponseInDatabase() {
+ BasicFindMainInterceptResponse(true);
+ }
+
+ void BasicFindMainInterceptResponseInWorkingSet() {
+ BasicFindMainInterceptResponse(false);
+ }
+
+ void BasicFindMainInterceptResponse(bool drop_from_working_set) {
+ PushNextTask(base::Bind(
+ &AppCacheStorageImplTest::Verify_BasicFindMainInterceptResponse,
+ base::Unretained(this)));
+
+ // Setup some preconditions. Create a complete cache with an
+ // intercept namespace and entry.
+ MakeCacheAndGroup(kManifestUrl, 2, 1, true);
+ cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::INTERCEPT, 1));
+ cache_->AddEntry(kEntryUrl2, AppCacheEntry(AppCacheEntry::INTERCEPT, 2));
+ cache_->intercept_namespaces_.push_back(
+ Namespace(APPCACHE_INTERCEPT_NAMESPACE, kInterceptNamespace2,
+ kEntryUrl2, false));
+ cache_->intercept_namespaces_.push_back(
+ Namespace(APPCACHE_INTERCEPT_NAMESPACE, kInterceptNamespace,
+ kEntryUrl, false));
+ AppCacheDatabase::CacheRecord cache_record;
+ std::vector<AppCacheDatabase::EntryRecord> entries;
+ std::vector<AppCacheDatabase::NamespaceRecord> intercepts;
+ std::vector<AppCacheDatabase::NamespaceRecord> fallbacks;
+ std::vector<AppCacheDatabase::OnlineWhiteListRecord> whitelists;
+ cache_->ToDatabaseRecords(group_.get(),
+ &cache_record,
+ &entries,
+ &intercepts,
+ &fallbacks,
+ &whitelists);
+
+ std::vector<AppCacheDatabase::EntryRecord>::const_iterator iter =
+ entries.begin();
+ while (iter != entries.end()) {
+ // MakeCacheAndGroup has inserted the default entry record already
+ if (iter->url != kDefaultEntryUrl)
+ EXPECT_TRUE(database()->InsertEntry(&(*iter)));
+ ++iter;
+ }
+
+ EXPECT_TRUE(database()->InsertNamespaceRecords(intercepts));
+ EXPECT_TRUE(database()->InsertOnlineWhiteListRecords(whitelists));
+ if (drop_from_working_set) {
+ EXPECT_TRUE(cache_->HasOneRef());
+ cache_ = NULL;
+ EXPECT_TRUE(group_->HasOneRef());
+ group_ = NULL;
+ }
+
+ // Conduct the test. The test url is in both intercept namespaces,
+ // but should match the longer of the two.
+ storage()->FindResponseForMainRequest(
+ kInterceptTestUrl, GURL(), delegate());
+ EXPECT_NE(kInterceptTestUrl, delegate()->found_url_);
+ }
+
+ void Verify_BasicFindMainInterceptResponse() {
+ EXPECT_EQ(kInterceptTestUrl, delegate()->found_url_);
+ EXPECT_EQ(kManifestUrl, delegate()->found_manifest_url_);
+ EXPECT_EQ(1, delegate()->found_cache_id_);
+ EXPECT_EQ(2, delegate()->found_group_id_);
+ EXPECT_EQ(2, delegate()->found_entry_.response_id());
+ EXPECT_TRUE(delegate()->found_entry_.IsIntercept());
+ EXPECT_EQ(kEntryUrl2, delegate()->found_namespace_entry_url_);
+ EXPECT_FALSE(delegate()->found_fallback_entry_.has_response_id());
+ TestFinished();
+ }
+
+ // FindInterceptPatternMatch ----------------------------------------
+
+ void FindInterceptPatternMatchInDatabase() {
+ FindInterceptPatternMatch(true);
+ }
+
+ void FindInterceptPatternMatchInWorkingSet() {
+ FindInterceptPatternMatch(false);
+ }
+
+ void FindInterceptPatternMatch(bool drop_from_working_set) {
+ // Setup some preconditions. Create a complete cache with an
+ // pattern matching intercept namespace and entry.
+ MakeCacheAndGroup(kManifestUrl, 2, 1, true);
+ cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::INTERCEPT, 1));
+ cache_->intercept_namespaces_.push_back(
+ Namespace(APPCACHE_INTERCEPT_NAMESPACE, kInterceptPatternNamespace,
+ kEntryUrl, true));
+ AppCacheDatabase::CacheRecord cache_record;
+ std::vector<AppCacheDatabase::EntryRecord> entries;
+ std::vector<AppCacheDatabase::NamespaceRecord> intercepts;
+ std::vector<AppCacheDatabase::NamespaceRecord> fallbacks;
+ std::vector<AppCacheDatabase::OnlineWhiteListRecord> whitelists;
+ cache_->ToDatabaseRecords(group_.get(),
+ &cache_record,
+ &entries,
+ &intercepts,
+ &fallbacks,
+ &whitelists);
+
+ std::vector<AppCacheDatabase::EntryRecord>::const_iterator iter =
+ entries.begin();
+ while (iter != entries.end()) {
+ // MakeCacheAndGroup has inserted the default entry record already
+ if (iter->url != kDefaultEntryUrl)
+ EXPECT_TRUE(database()->InsertEntry(&(*iter)));
+ ++iter;
+ }
+
+ EXPECT_TRUE(database()->InsertNamespaceRecords(intercepts));
+ if (drop_from_working_set) {
+ EXPECT_TRUE(cache_->HasOneRef());
+ cache_ = NULL;
+ EXPECT_TRUE(group_->HasOneRef());
+ group_ = NULL;
+ }
+
+ // First test something that does not match the pattern.
+ PushNextTask(base::Bind(
+ &AppCacheStorageImplTest::Verify_FindInterceptPatternMatchNegative,
+ base::Unretained(this)));
+ storage()->FindResponseForMainRequest(
+ kInterceptPatternTestNegativeUrl, GURL(), delegate());
+ EXPECT_EQ(GURL(), delegate()->found_url_); // Is always async.
+ }
+
+ void Verify_FindInterceptPatternMatchNegative() {
+ EXPECT_EQ(kInterceptPatternTestNegativeUrl, delegate()->found_url_);
+ EXPECT_TRUE(delegate()->found_manifest_url_.is_empty());
+ EXPECT_EQ(kAppCacheNoCacheId, delegate()->found_cache_id_);
+ EXPECT_EQ(kAppCacheNoResponseId, delegate()->found_entry_.response_id());
+ EXPECT_EQ(kAppCacheNoResponseId,
+ delegate()->found_fallback_entry_.response_id());
+ EXPECT_TRUE(delegate()->found_namespace_entry_url_.is_empty());
+ EXPECT_EQ(0, delegate()->found_entry_.types());
+ EXPECT_EQ(0, delegate()->found_fallback_entry_.types());
+
+ // Then test something that matches.
+ PushNextTask(base::Bind(
+ &AppCacheStorageImplTest::Verify_FindInterceptPatternMatchPositive,
+ base::Unretained(this)));
+ storage()->FindResponseForMainRequest(
+ kInterceptPatternTestPositiveUrl, GURL(), delegate());
+ }
+
+ void Verify_FindInterceptPatternMatchPositive() {
+ EXPECT_EQ(kInterceptPatternTestPositiveUrl, delegate()->found_url_);
+ EXPECT_EQ(kManifestUrl, delegate()->found_manifest_url_);
+ EXPECT_EQ(1, delegate()->found_cache_id_);
+ EXPECT_EQ(2, delegate()->found_group_id_);
+ EXPECT_EQ(1, delegate()->found_entry_.response_id());
+ EXPECT_TRUE(delegate()->found_entry_.IsIntercept());
+ EXPECT_EQ(kEntryUrl, delegate()->found_namespace_entry_url_);
+ EXPECT_FALSE(delegate()->found_fallback_entry_.has_response_id());
+ TestFinished();
+ }
+
+ // FindFallbackPatternMatch -------------------------------
+
+ void FindFallbackPatternMatchInDatabase() {
+ FindFallbackPatternMatch(true);
+ }
+
+ void FindFallbackPatternMatchInWorkingSet() {
+ FindFallbackPatternMatch(false);
+ }
+
+ void FindFallbackPatternMatch(bool drop_from_working_set) {
+ // Setup some preconditions. Create a complete cache with a
+ // pattern matching fallback namespace and entry.
+ MakeCacheAndGroup(kManifestUrl, 2, 1, true);
+ cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::FALLBACK, 1));
+ cache_->fallback_namespaces_.push_back(
+ Namespace(APPCACHE_FALLBACK_NAMESPACE, kFallbackPatternNamespace,
+ kEntryUrl, true));
+ AppCacheDatabase::CacheRecord cache_record;
+ std::vector<AppCacheDatabase::EntryRecord> entries;
+ std::vector<AppCacheDatabase::NamespaceRecord> intercepts;
+ std::vector<AppCacheDatabase::NamespaceRecord> fallbacks;
+ std::vector<AppCacheDatabase::OnlineWhiteListRecord> whitelists;
+ cache_->ToDatabaseRecords(group_.get(),
+ &cache_record,
+ &entries,
+ &intercepts,
+ &fallbacks,
+ &whitelists);
+
+ std::vector<AppCacheDatabase::EntryRecord>::const_iterator iter =
+ entries.begin();
+ while (iter != entries.end()) {
+ // MakeCacheAndGroup has inserted the default entry record already.
+ if (iter->url != kDefaultEntryUrl)
+ EXPECT_TRUE(database()->InsertEntry(&(*iter)));
+ ++iter;
+ }
+
+ EXPECT_TRUE(database()->InsertNamespaceRecords(fallbacks));
+ if (drop_from_working_set) {
+ EXPECT_TRUE(cache_->HasOneRef());
+ cache_ = NULL;
+ EXPECT_TRUE(group_->HasOneRef());
+ group_ = NULL;
+ }
+
+ // First test something that does not match the pattern.
+ PushNextTask(base::Bind(
+ &AppCacheStorageImplTest::Verify_FindFallbackPatternMatchNegative,
+ base::Unretained(this)));
+ storage()->FindResponseForMainRequest(
+ kFallbackPatternTestNegativeUrl, GURL(), delegate());
+ EXPECT_EQ(GURL(), delegate()->found_url_); // Is always async.
+ }
+
+ void Verify_FindFallbackPatternMatchNegative() {
+ EXPECT_EQ(kFallbackPatternTestNegativeUrl, delegate()->found_url_);
+ EXPECT_TRUE(delegate()->found_manifest_url_.is_empty());
+ EXPECT_EQ(kAppCacheNoCacheId, delegate()->found_cache_id_);
+ EXPECT_EQ(kAppCacheNoResponseId, delegate()->found_entry_.response_id());
+ EXPECT_EQ(kAppCacheNoResponseId,
+ delegate()->found_fallback_entry_.response_id());
+ EXPECT_TRUE(delegate()->found_namespace_entry_url_.is_empty());
+ EXPECT_EQ(0, delegate()->found_entry_.types());
+ EXPECT_EQ(0, delegate()->found_fallback_entry_.types());
+
+ // Then test something that matches.
+ PushNextTask(base::Bind(
+ &AppCacheStorageImplTest::Verify_FindFallbackPatternMatchPositive,
+ base::Unretained(this)));
+ storage()->FindResponseForMainRequest(
+ kFallbackPatternTestPositiveUrl, GURL(), delegate());
+ }
+
+ void Verify_FindFallbackPatternMatchPositive() {
+ EXPECT_EQ(kFallbackPatternTestPositiveUrl, delegate()->found_url_);
+ EXPECT_EQ(kManifestUrl, delegate()->found_manifest_url_);
+ EXPECT_EQ(1, delegate()->found_cache_id_);
+ EXPECT_EQ(2, delegate()->found_group_id_);
+ EXPECT_EQ(1, delegate()->found_fallback_entry_.response_id());
+ EXPECT_TRUE(delegate()->found_fallback_entry_.IsFallback());
+ EXPECT_EQ(kEntryUrl, delegate()->found_namespace_entry_url_);
+ EXPECT_FALSE(delegate()->found_entry_.has_response_id());
+ TestFinished();
+ }
+
+ // FindMainResponseWithMultipleHits -------------------------------
+
+ void FindMainResponseWithMultipleHits() {
+ PushNextTask(base::Bind(
+ &AppCacheStorageImplTest::Verify_FindMainResponseWithMultipleHits,
+ base::Unretained(this)));
+
+ // Setup some preconditions, create a few caches with an identical set
+ // of entries and fallback namespaces. Only the last one remains in
+ // the working set to simulate appearing as "in use".
+ MakeMultipleHitCacheAndGroup(kManifestUrl, 1);
+ MakeMultipleHitCacheAndGroup(kManifestUrl2, 2);
+ MakeMultipleHitCacheAndGroup(kManifestUrl3, 3);
+
+ // Conduct the test, we should find the response from the last cache
+ // since it's "in use".
+ storage()->FindResponseForMainRequest(kEntryUrl, GURL(), delegate());
+ EXPECT_NE(kEntryUrl, delegate()->found_url_);
+ }
+
+ void MakeMultipleHitCacheAndGroup(const GURL& manifest_url, int id) {
+ MakeCacheAndGroup(manifest_url, id, id, true);
+ AppCacheDatabase::EntryRecord entry_record;
+
+ // Add an entry for kEntryUrl
+ entry_record.cache_id = id;
+ entry_record.url = kEntryUrl;
+ entry_record.flags = AppCacheEntry::EXPLICIT;
+ entry_record.response_id = id;
+ EXPECT_TRUE(database()->InsertEntry(&entry_record));
+ cache_->AddEntry(
+ entry_record.url,
+ AppCacheEntry(entry_record.flags, entry_record.response_id));
+
+ // Add an entry for the manifestUrl
+ entry_record.cache_id = id;
+ entry_record.url = manifest_url;
+ entry_record.flags = AppCacheEntry::MANIFEST;
+ entry_record.response_id = id + kManifestEntryIdOffset;
+ EXPECT_TRUE(database()->InsertEntry(&entry_record));
+ cache_->AddEntry(
+ entry_record.url,
+ AppCacheEntry(entry_record.flags, entry_record.response_id));
+
+ // Add a fallback entry and namespace
+ entry_record.cache_id = id;
+ entry_record.url = kEntryUrl2;
+ entry_record.flags = AppCacheEntry::FALLBACK;
+ entry_record.response_id = id + kFallbackEntryIdOffset;
+ EXPECT_TRUE(database()->InsertEntry(&entry_record));
+ cache_->AddEntry(
+ entry_record.url,
+ AppCacheEntry(entry_record.flags, entry_record.response_id));
+ AppCacheDatabase::NamespaceRecord fallback_namespace_record;
+ fallback_namespace_record.cache_id = id;
+ fallback_namespace_record.namespace_.target_url = entry_record.url;
+ fallback_namespace_record.namespace_.namespace_url = kFallbackNamespace;
+ fallback_namespace_record.origin = manifest_url.GetOrigin();
+ EXPECT_TRUE(database()->InsertNamespace(&fallback_namespace_record));
+ cache_->fallback_namespaces_.push_back(
+ Namespace(APPCACHE_FALLBACK_NAMESPACE,
+ kFallbackNamespace,
+ kEntryUrl2,
+ false));
+ }
+
+ void Verify_FindMainResponseWithMultipleHits() {
+ EXPECT_EQ(kEntryUrl, delegate()->found_url_);
+ EXPECT_EQ(kManifestUrl3, delegate()->found_manifest_url_);
+ EXPECT_EQ(3, delegate()->found_cache_id_);
+ EXPECT_EQ(3, delegate()->found_group_id_);
+ EXPECT_EQ(3, delegate()->found_entry_.response_id());
+ EXPECT_TRUE(delegate()->found_entry_.IsExplicit());
+ EXPECT_FALSE(delegate()->found_fallback_entry_.has_response_id());
+
+ // Conduct another test perferring kManifestUrl
+ delegate_.reset(new MockStorageDelegate(this));
+ PushNextTask(base::Bind(
+ &AppCacheStorageImplTest::Verify_FindMainResponseWithMultipleHits2,
+ base::Unretained(this)));
+ storage()->FindResponseForMainRequest(kEntryUrl, kManifestUrl, delegate());
+ EXPECT_NE(kEntryUrl, delegate()->found_url_);
+ }
+
+ void Verify_FindMainResponseWithMultipleHits2() {
+ EXPECT_EQ(kEntryUrl, delegate()->found_url_);
+ EXPECT_EQ(kManifestUrl, delegate()->found_manifest_url_);
+ EXPECT_EQ(1, delegate()->found_cache_id_);
+ EXPECT_EQ(1, delegate()->found_group_id_);
+ EXPECT_EQ(1, delegate()->found_entry_.response_id());
+ EXPECT_TRUE(delegate()->found_entry_.IsExplicit());
+ EXPECT_FALSE(delegate()->found_fallback_entry_.has_response_id());
+
+ // Conduct the another test perferring kManifestUrl2
+ delegate_.reset(new MockStorageDelegate(this));
+ PushNextTask(base::Bind(
+ &AppCacheStorageImplTest::Verify_FindMainResponseWithMultipleHits3,
+ base::Unretained(this)));
+ storage()->FindResponseForMainRequest(kEntryUrl, kManifestUrl2, delegate());
+ EXPECT_NE(kEntryUrl, delegate()->found_url_);
+ }
+
+ void Verify_FindMainResponseWithMultipleHits3() {
+ EXPECT_EQ(kEntryUrl, delegate()->found_url_);
+ EXPECT_EQ(kManifestUrl2, delegate()->found_manifest_url_);
+ EXPECT_EQ(2, delegate()->found_cache_id_);
+ EXPECT_EQ(2, delegate()->found_group_id_);
+ EXPECT_EQ(2, delegate()->found_entry_.response_id());
+ EXPECT_TRUE(delegate()->found_entry_.IsExplicit());
+ EXPECT_FALSE(delegate()->found_fallback_entry_.has_response_id());
+
+ // Conduct another test with no preferred manifest that hits the fallback.
+ delegate_.reset(new MockStorageDelegate(this));
+ PushNextTask(base::Bind(
+ &AppCacheStorageImplTest::Verify_FindMainResponseWithMultipleHits4,
+ base::Unretained(this)));
+ storage()->FindResponseForMainRequest(
+ kFallbackTestUrl, GURL(), delegate());
+ EXPECT_NE(kFallbackTestUrl, delegate()->found_url_);
+ }
+
+ void Verify_FindMainResponseWithMultipleHits4() {
+ EXPECT_EQ(kFallbackTestUrl, delegate()->found_url_);
+ EXPECT_EQ(kManifestUrl3, delegate()->found_manifest_url_);
+ EXPECT_EQ(3, delegate()->found_cache_id_);
+ EXPECT_EQ(3, delegate()->found_group_id_);
+ EXPECT_FALSE(delegate()->found_entry_.has_response_id());
+ EXPECT_EQ(3 + kFallbackEntryIdOffset,
+ delegate()->found_fallback_entry_.response_id());
+ EXPECT_TRUE(delegate()->found_fallback_entry_.IsFallback());
+ EXPECT_EQ(kEntryUrl2, delegate()->found_namespace_entry_url_);
+
+ // Conduct another test preferring kManifestUrl2 that hits the fallback.
+ delegate_.reset(new MockStorageDelegate(this));
+ PushNextTask(base::Bind(
+ &AppCacheStorageImplTest::Verify_FindMainResponseWithMultipleHits5,
+ base::Unretained(this)));
+ storage()->FindResponseForMainRequest(
+ kFallbackTestUrl, kManifestUrl2, delegate());
+ EXPECT_NE(kFallbackTestUrl, delegate()->found_url_);
+ }
+
+ void Verify_FindMainResponseWithMultipleHits5() {
+ EXPECT_EQ(kFallbackTestUrl, delegate()->found_url_);
+ EXPECT_EQ(kManifestUrl2, delegate()->found_manifest_url_);
+ EXPECT_EQ(2, delegate()->found_cache_id_);
+ EXPECT_EQ(2, delegate()->found_group_id_);
+ EXPECT_FALSE(delegate()->found_entry_.has_response_id());
+ EXPECT_EQ(2 + kFallbackEntryIdOffset,
+ delegate()->found_fallback_entry_.response_id());
+ EXPECT_TRUE(delegate()->found_fallback_entry_.IsFallback());
+ EXPECT_EQ(kEntryUrl2, delegate()->found_namespace_entry_url_);
+
+ TestFinished();
+ }
+
+ // FindMainResponseExclusions -------------------------------
+
+ void FindMainResponseExclusionsInDatabase() {
+ FindMainResponseExclusions(true);
+ }
+
+ void FindMainResponseExclusionsInWorkingSet() {
+ FindMainResponseExclusions(false);
+ }
+
+ void FindMainResponseExclusions(bool drop_from_working_set) {
+ // Setup some preconditions. Create a complete cache with a
+ // foreign entry, an online namespace, and a second online
+ // namespace nested within a fallback namespace.
+ MakeCacheAndGroup(kManifestUrl, 1, 1, true);
+ cache_->AddEntry(kEntryUrl,
+ AppCacheEntry(AppCacheEntry::EXPLICIT | AppCacheEntry::FOREIGN, 1));
+ cache_->AddEntry(kEntryUrl2, AppCacheEntry(AppCacheEntry::FALLBACK, 2));
+ cache_->fallback_namespaces_.push_back(
+ Namespace(APPCACHE_FALLBACK_NAMESPACE,
+ kFallbackNamespace,
+ kEntryUrl2,
+ false));
+ cache_->online_whitelist_namespaces_.push_back(
+ Namespace(APPCACHE_NETWORK_NAMESPACE, kOnlineNamespace,
+ GURL(), false));
+ cache_->online_whitelist_namespaces_.push_back(
+ Namespace(APPCACHE_NETWORK_NAMESPACE, kOnlineNamespaceWithinFallback,
+ GURL(), false));
+
+ AppCacheDatabase::EntryRecord entry_record;
+ entry_record.cache_id = 1;
+ entry_record.url = kEntryUrl;
+ entry_record.flags = AppCacheEntry::EXPLICIT | AppCacheEntry::FOREIGN;
+ entry_record.response_id = 1;
+ EXPECT_TRUE(database()->InsertEntry(&entry_record));
+ AppCacheDatabase::OnlineWhiteListRecord whitelist_record;
+ whitelist_record.cache_id = 1;
+ whitelist_record.namespace_url = kOnlineNamespace;
+ EXPECT_TRUE(database()->InsertOnlineWhiteList(&whitelist_record));
+ AppCacheDatabase::NamespaceRecord fallback_namespace_record;
+ fallback_namespace_record.cache_id = 1;
+ fallback_namespace_record.namespace_.target_url = kEntryUrl2;
+ fallback_namespace_record.namespace_.namespace_url = kFallbackNamespace;
+ fallback_namespace_record.origin = kManifestUrl.GetOrigin();
+ EXPECT_TRUE(database()->InsertNamespace(&fallback_namespace_record));
+ whitelist_record.cache_id = 1;
+ whitelist_record.namespace_url = kOnlineNamespaceWithinFallback;
+ EXPECT_TRUE(database()->InsertOnlineWhiteList(&whitelist_record));
+ if (drop_from_working_set) {
+ cache_ = NULL;
+ group_ = NULL;
+ }
+
+ // We should not find anything for the foreign entry.
+ PushNextTask(base::Bind(&AppCacheStorageImplTest::Verify_ExclusionNotFound,
+ base::Unretained(this), kEntryUrl, 1));
+ storage()->FindResponseForMainRequest(kEntryUrl, GURL(), delegate());
+ }
+
+ void Verify_ExclusionNotFound(GURL expected_url, int phase) {
+ EXPECT_EQ(expected_url, delegate()->found_url_);
+ EXPECT_TRUE(delegate()->found_manifest_url_.is_empty());
+ EXPECT_EQ(kAppCacheNoCacheId, delegate()->found_cache_id_);
+ EXPECT_EQ(0, delegate()->found_group_id_);
+ EXPECT_EQ(kAppCacheNoResponseId, delegate()->found_entry_.response_id());
+ EXPECT_EQ(kAppCacheNoResponseId,
+ delegate()->found_fallback_entry_.response_id());
+ EXPECT_TRUE(delegate()->found_namespace_entry_url_.is_empty());
+ EXPECT_EQ(0, delegate()->found_entry_.types());
+ EXPECT_EQ(0, delegate()->found_fallback_entry_.types());
+
+ if (phase == 1) {
+ // We should not find anything for the online namespace.
+ PushNextTask(
+ base::Bind(&AppCacheStorageImplTest::Verify_ExclusionNotFound,
+ base::Unretained(this), kOnlineNamespace, 2));
+ storage()->FindResponseForMainRequest(
+ kOnlineNamespace, GURL(), delegate());
+ return;
+ }
+ if (phase == 2) {
+ // We should not find anything for the online namespace nested within
+ // the fallback namespace.
+ PushNextTask(base::Bind(
+ &AppCacheStorageImplTest::Verify_ExclusionNotFound,
+ base::Unretained(this), kOnlineNamespaceWithinFallback, 3));
+ storage()->FindResponseForMainRequest(
+ kOnlineNamespaceWithinFallback, GURL(), delegate());
+ return;
+ }
+
+ TestFinished();
+ }
+
+ // Reinitialize -------------------------------
+ // These tests are somewhat of a system integration test.
+ // They rely on running a mock http server on our IO thread,
+ // and involves other appcache classes to get some code
+ // coverage thruout when Reinitialize happens.
+
+ class MockServiceObserver : public AppCacheServiceImpl::Observer {
+ public:
+ explicit MockServiceObserver(AppCacheStorageImplTest* test)
+ : test_(test) {}
+
+ virtual void OnServiceReinitialized(
+ AppCacheStorageReference* old_storage_ref) OVERRIDE {
+ observed_old_storage_ = old_storage_ref;
+ test_->ScheduleNextTask();
+ }
+
+ scoped_refptr<AppCacheStorageReference> observed_old_storage_;
+ AppCacheStorageImplTest* test_;
+ };
+
+ class MockAppCacheFrontend : public AppCacheFrontend {
+ public:
+ MockAppCacheFrontend() : error_event_was_raised_(false) {}
+
+ virtual void OnCacheSelected(
+ int host_id, const AppCacheInfo& info) OVERRIDE {}
+ virtual void OnStatusChanged(const std::vector<int>& host_ids,
+ AppCacheStatus status) OVERRIDE {}
+ virtual void OnEventRaised(const std::vector<int>& host_ids,
+ AppCacheEventID event_id) OVERRIDE {}
+ virtual void OnProgressEventRaised(
+ const std::vector<int>& host_ids,
+ const GURL& url,
+ int num_total, int num_complete) OVERRIDE {}
+ virtual void OnErrorEventRaised(const std::vector<int>& host_ids,
+ const AppCacheErrorDetails& details)
+ OVERRIDE {
+ error_event_was_raised_ = true;
+ }
+ virtual void OnLogMessage(int host_id, AppCacheLogLevel log_level,
+ const std::string& message) OVERRIDE {}
+ virtual void OnContentBlocked(
+ int host_id, const GURL& manifest_url) OVERRIDE {}
+
+ bool error_event_was_raised_;
+ };
+
+ enum ReinitTestCase {
+ CORRUPT_CACHE_ON_INSTALL,
+ CORRUPT_CACHE_ON_LOAD_EXISTING,
+ CORRUPT_SQL_ON_INSTALL
+ };
+
+ void Reinitialize1() {
+ // Recover from a corrupt disk cache discovered while
+ // installing a new appcache.
+ Reinitialize(CORRUPT_CACHE_ON_INSTALL);
+ }
+
+ void Reinitialize2() {
+ // Recover from a corrupt disk cache discovered while
+ // trying to load a resource from an existing appcache.
+ Reinitialize(CORRUPT_CACHE_ON_LOAD_EXISTING);
+ }
+
+ void Reinitialize3() {
+ // Recover from a corrupt sql database discovered while
+ // installing a new appcache.
+ Reinitialize(CORRUPT_SQL_ON_INSTALL);
+ }
+
+ void Reinitialize(ReinitTestCase test_case) {
+ // Unlike all of the other tests, this one actually read/write files.
+ ASSERT_TRUE(temp_directory_.CreateUniqueTempDir());
+
+ AppCacheDatabase db(temp_directory_.path().AppendASCII("Index"));
+ EXPECT_TRUE(db.LazyOpen(true));
+
+ if (test_case == CORRUPT_CACHE_ON_INSTALL ||
+ test_case == CORRUPT_CACHE_ON_LOAD_EXISTING) {
+ // Create a corrupt/unopenable disk_cache index file.
+ const std::string kCorruptData("deadbeef");
+ base::FilePath disk_cache_directory =
+ temp_directory_.path().AppendASCII("Cache");
+ ASSERT_TRUE(base::CreateDirectory(disk_cache_directory));
+ base::FilePath index_file = disk_cache_directory.AppendASCII("index");
+ EXPECT_EQ(static_cast<int>(kCorruptData.length()),
+ base::WriteFile(
+ index_file, kCorruptData.data(), kCorruptData.length()));
+ }
+
+ // Create records for a degenerate cached manifest that only contains
+ // one entry for the manifest file resource.
+ if (test_case == CORRUPT_CACHE_ON_LOAD_EXISTING) {
+ AppCacheDatabase db(temp_directory_.path().AppendASCII("Index"));
+ GURL manifest_url = MockHttpServer::GetMockUrl("manifest");
+
+ AppCacheDatabase::GroupRecord group_record;
+ group_record.group_id = 1;
+ group_record.manifest_url = manifest_url;
+ group_record.origin = manifest_url.GetOrigin();
+ EXPECT_TRUE(db.InsertGroup(&group_record));
+ AppCacheDatabase::CacheRecord cache_record;
+ cache_record.cache_id = 1;
+ cache_record.group_id = 1;
+ cache_record.online_wildcard = false;
+ cache_record.update_time = kZeroTime;
+ cache_record.cache_size = kDefaultEntrySize;
+ EXPECT_TRUE(db.InsertCache(&cache_record));
+ AppCacheDatabase::EntryRecord entry_record;
+ entry_record.cache_id = 1;
+ entry_record.url = manifest_url;
+ entry_record.flags = AppCacheEntry::MANIFEST;
+ entry_record.response_id = 1;
+ entry_record.response_size = kDefaultEntrySize;
+ EXPECT_TRUE(db.InsertEntry(&entry_record));
+ }
+
+ // Recreate the service to point at the db and corruption on disk.
+ service_.reset(new AppCacheServiceImpl(NULL));
+ service_->set_request_context(io_thread->request_context());
+ service_->Initialize(
+ temp_directory_.path(),
+ db_thread->message_loop_proxy().get(),
+ db_thread->message_loop_proxy().get());
+ mock_quota_manager_proxy_ = new MockQuotaManagerProxy();
+ service_->quota_manager_proxy_ = mock_quota_manager_proxy_;
+ delegate_.reset(new MockStorageDelegate(this));
+
+ // Additional setup to observe reinitailize happens.
+ observer_.reset(new MockServiceObserver(this));
+ service_->AddObserver(observer_.get());
+
+ // We continue after the init task is complete including the callback
+ // on the current thread.
+ FlushDbThreadTasks();
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&AppCacheStorageImplTest::Continue_Reinitialize,
+ base::Unretained(this),
+ test_case));
+ }
+
+ void Continue_Reinitialize(ReinitTestCase test_case) {
+ const int kMockProcessId = 1;
+ backend_.reset(new AppCacheBackendImpl);
+ backend_->Initialize(service_.get(), &frontend_, kMockProcessId);
+
+ if (test_case == CORRUPT_SQL_ON_INSTALL) {
+ // Break the db file
+ EXPECT_FALSE(database()->was_corruption_detected());
+ ASSERT_TRUE(sql::test::CorruptSizeInHeader(
+ temp_directory_.path().AppendASCII("Index")));
+ }
+
+ if (test_case == CORRUPT_CACHE_ON_INSTALL ||
+ test_case == CORRUPT_SQL_ON_INSTALL) {
+ // Try to create a new appcache, the resulting update job will
+ // eventually fail when it gets to disk cache initialization.
+ backend_->RegisterHost(1);
+ AppCacheHost* host1 = backend_->GetHost(1);
+ const GURL kEmptyPageUrl(MockHttpServer::GetMockUrl("empty.html"));
+ host1->first_party_url_ = kEmptyPageUrl;
+ host1->SelectCache(kEmptyPageUrl,
+ kAppCacheNoCacheId,
+ MockHttpServer::GetMockUrl("manifest"));
+ } else {
+ ASSERT_EQ(CORRUPT_CACHE_ON_LOAD_EXISTING, test_case);
+ // Try to access the existing cache manifest.
+ // The URLRequestJob will eventually fail when it gets to disk
+ // cache initialization.
+ backend_->RegisterHost(2);
+ AppCacheHost* host2 = backend_->GetHost(2);
+ GURL manifest_url = MockHttpServer::GetMockUrl("manifest");
+ request_ = service()->request_context()->CreateRequest(
+ manifest_url, net::DEFAULT_PRIORITY, NULL, NULL);
+ AppCacheInterceptor::SetExtraRequestInfo(
+ request_.get(), service_.get(),
+ backend_->process_id(), host2->host_id(),
+ ResourceType::MAIN_FRAME);
+ request_->Start();
+ }
+
+ PushNextTask(base::Bind(
+ &AppCacheStorageImplTest::Verify_Reinitialized,
+ base::Unretained(this),
+ test_case));
+ }
+
+ void Verify_Reinitialized(ReinitTestCase test_case) {
+ // Verify we got notified of reinit and a new storage instance is created,
+ // and that the old data has been deleted.
+ EXPECT_TRUE(observer_->observed_old_storage_.get());
+ EXPECT_TRUE(observer_->observed_old_storage_->storage() != storage());
+ EXPECT_FALSE(PathExists(
+ temp_directory_.path().AppendASCII("Cache").AppendASCII("index")));
+ EXPECT_FALSE(PathExists(
+ temp_directory_.path().AppendASCII("Index")));
+
+ if (test_case == CORRUPT_SQL_ON_INSTALL) {
+ AppCacheStorageImpl* storage = static_cast<AppCacheStorageImpl*>(
+ observer_->observed_old_storage_->storage());
+ EXPECT_TRUE(storage->database_->was_corruption_detected());
+ }
+
+ // Verify that the hosts saw appropriate events.
+ if (test_case == CORRUPT_CACHE_ON_INSTALL ||
+ test_case == CORRUPT_SQL_ON_INSTALL) {
+ EXPECT_TRUE(frontend_.error_event_was_raised_);
+ AppCacheHost* host1 = backend_->GetHost(1);
+ EXPECT_FALSE(host1->associated_cache());
+ EXPECT_FALSE(host1->group_being_updated_);
+ EXPECT_TRUE(host1->disabled_storage_reference_.get());
+ } else {
+ ASSERT_EQ(CORRUPT_CACHE_ON_LOAD_EXISTING, test_case);
+ AppCacheHost* host2 = backend_->GetHost(2);
+ EXPECT_EQ(1, host2->main_resource_cache_->cache_id());
+ EXPECT_TRUE(host2->disabled_storage_reference_.get());
+ }
+
+ // Cleanup and claim victory.
+ service_->RemoveObserver(observer_.get());
+ request_.reset();
+ backend_.reset();
+ observer_.reset();
+ TestFinished();
+ }
+
+ // Test case helpers --------------------------------------------------
+
+ AppCacheServiceImpl* service() {
+ return service_.get();
+ }
+
+ AppCacheStorageImpl* storage() {
+ return static_cast<AppCacheStorageImpl*>(service()->storage());
+ }
+
+ AppCacheDatabase* database() {
+ return storage()->database_;
+ }
+
+ MockStorageDelegate* delegate() {
+ return delegate_.get();
+ }
+
+ void MakeCacheAndGroup(
+ const GURL& manifest_url, int64 group_id, int64 cache_id,
+ bool add_to_database) {
+ AppCacheEntry default_entry(
+ AppCacheEntry::EXPLICIT, cache_id + kDefaultEntryIdOffset,
+ kDefaultEntrySize);
+ group_ = new AppCacheGroup(storage(), manifest_url, group_id);
+ cache_ = new AppCache(storage(), cache_id);
+ cache_->AddEntry(kDefaultEntryUrl, default_entry);
+ cache_->set_complete(true);
+ group_->AddCache(cache_.get());
+ if (add_to_database) {
+ AppCacheDatabase::GroupRecord group_record;
+ group_record.group_id = group_id;
+ group_record.manifest_url = manifest_url;
+ group_record.origin = manifest_url.GetOrigin();
+ EXPECT_TRUE(database()->InsertGroup(&group_record));
+ AppCacheDatabase::CacheRecord cache_record;
+ cache_record.cache_id = cache_id;
+ cache_record.group_id = group_id;
+ cache_record.online_wildcard = false;
+ cache_record.update_time = kZeroTime;
+ cache_record.cache_size = kDefaultEntrySize;
+ EXPECT_TRUE(database()->InsertCache(&cache_record));
+ AppCacheDatabase::EntryRecord entry_record;
+ entry_record.cache_id = cache_id;
+ entry_record.url = kDefaultEntryUrl;
+ entry_record.flags = default_entry.types();
+ entry_record.response_id = default_entry.response_id();
+ entry_record.response_size = default_entry.response_size();
+ EXPECT_TRUE(database()->InsertEntry(&entry_record));
+
+ storage()->usage_map_[manifest_url.GetOrigin()] =
+ default_entry.response_size();
+ }
+ }
+
+ // Data members --------------------------------------------------
+
+ scoped_ptr<base::WaitableEvent> test_finished_event_;
+ std::stack<base::Closure> task_stack_;
+ scoped_ptr<AppCacheServiceImpl> service_;
+ scoped_ptr<MockStorageDelegate> delegate_;
+ scoped_refptr<MockQuotaManagerProxy> mock_quota_manager_proxy_;
+ scoped_refptr<AppCacheGroup> group_;
+ scoped_refptr<AppCache> cache_;
+ scoped_refptr<AppCache> cache2_;
+
+ // Specifically for the Reinitalize test.
+ base::ScopedTempDir temp_directory_;
+ scoped_ptr<MockServiceObserver> observer_;
+ MockAppCacheFrontend frontend_;
+ scoped_ptr<AppCacheBackendImpl> backend_;
+ scoped_ptr<net::URLRequest> request_;
+};
+
+
+TEST_F(AppCacheStorageImplTest, LoadCache_Miss) {
+ RunTestOnIOThread(&AppCacheStorageImplTest::LoadCache_Miss);
+}
+
+TEST_F(AppCacheStorageImplTest, LoadCache_NearHit) {
+ RunTestOnIOThread(&AppCacheStorageImplTest::LoadCache_NearHit);
+}
+
+TEST_F(AppCacheStorageImplTest, CreateGroupInEmptyOrigin) {
+ RunTestOnIOThread(&AppCacheStorageImplTest::CreateGroupInEmptyOrigin);
+}
+
+TEST_F(AppCacheStorageImplTest, CreateGroupInPopulatedOrigin) {
+ RunTestOnIOThread(&AppCacheStorageImplTest::CreateGroupInPopulatedOrigin);
+}
+
+TEST_F(AppCacheStorageImplTest, LoadGroupAndCache_FarHit) {
+ RunTestOnIOThread(&AppCacheStorageImplTest::LoadGroupAndCache_FarHit);
+}
+
+TEST_F(AppCacheStorageImplTest, StoreNewGroup) {
+ RunTestOnIOThread(&AppCacheStorageImplTest::StoreNewGroup);
+}
+
+TEST_F(AppCacheStorageImplTest, StoreExistingGroup) {
+ RunTestOnIOThread(&AppCacheStorageImplTest::StoreExistingGroup);
+}
+
+TEST_F(AppCacheStorageImplTest, StoreExistingGroupExistingCache) {
+ RunTestOnIOThread(&AppCacheStorageImplTest::StoreExistingGroupExistingCache);
+}
+
+TEST_F(AppCacheStorageImplTest, FailStoreGroup) {
+ RunTestOnIOThread(&AppCacheStorageImplTest::FailStoreGroup);
+}
+
+TEST_F(AppCacheStorageImplTest, MakeGroupObsolete) {
+ RunTestOnIOThread(&AppCacheStorageImplTest::MakeGroupObsolete);
+}
+
+TEST_F(AppCacheStorageImplTest, MarkEntryAsForeign) {
+ RunTestOnIOThread(&AppCacheStorageImplTest::MarkEntryAsForeign);
+}
+
+TEST_F(AppCacheStorageImplTest, MarkEntryAsForeignWithLoadInProgress) {
+ RunTestOnIOThread(
+ &AppCacheStorageImplTest::MarkEntryAsForeignWithLoadInProgress);
+}
+
+TEST_F(AppCacheStorageImplTest, FindNoMainResponse) {
+ RunTestOnIOThread(&AppCacheStorageImplTest::FindNoMainResponse);
+}
+
+TEST_F(AppCacheStorageImplTest, BasicFindMainResponseInDatabase) {
+ RunTestOnIOThread(
+ &AppCacheStorageImplTest::BasicFindMainResponseInDatabase);
+}
+
+TEST_F(AppCacheStorageImplTest, BasicFindMainResponseInWorkingSet) {
+ RunTestOnIOThread(
+ &AppCacheStorageImplTest::BasicFindMainResponseInWorkingSet);
+}
+
+TEST_F(AppCacheStorageImplTest, BasicFindMainFallbackResponseInDatabase) {
+ RunTestOnIOThread(
+ &AppCacheStorageImplTest::BasicFindMainFallbackResponseInDatabase);
+}
+
+TEST_F(AppCacheStorageImplTest, BasicFindMainFallbackResponseInWorkingSet) {
+ RunTestOnIOThread(
+ &AppCacheStorageImplTest::BasicFindMainFallbackResponseInWorkingSet);
+}
+
+TEST_F(AppCacheStorageImplTest, BasicFindMainInterceptResponseInDatabase) {
+ RunTestOnIOThread(
+ &AppCacheStorageImplTest::BasicFindMainInterceptResponseInDatabase);
+}
+
+TEST_F(AppCacheStorageImplTest, BasicFindMainInterceptResponseInWorkingSet) {
+ RunTestOnIOThread(
+ &AppCacheStorageImplTest::BasicFindMainInterceptResponseInWorkingSet);
+}
+
+TEST_F(AppCacheStorageImplTest, FindMainResponseWithMultipleHits) {
+ RunTestOnIOThread(
+ &AppCacheStorageImplTest::FindMainResponseWithMultipleHits);
+}
+
+TEST_F(AppCacheStorageImplTest, FindMainResponseExclusionsInDatabase) {
+ RunTestOnIOThread(
+ &AppCacheStorageImplTest::FindMainResponseExclusionsInDatabase);
+}
+
+TEST_F(AppCacheStorageImplTest, FindMainResponseExclusionsInWorkingSet) {
+ RunTestOnIOThread(
+ &AppCacheStorageImplTest::FindMainResponseExclusionsInWorkingSet);
+}
+
+TEST_F(AppCacheStorageImplTest, FindInterceptPatternMatchInWorkingSet) {
+ RunTestOnIOThread(
+ &AppCacheStorageImplTest::FindInterceptPatternMatchInWorkingSet);
+}
+
+TEST_F(AppCacheStorageImplTest, FindInterceptPatternMatchInDatabase) {
+ RunTestOnIOThread(
+ &AppCacheStorageImplTest::FindInterceptPatternMatchInDatabase);
+}
+
+TEST_F(AppCacheStorageImplTest, FindFallbackPatternMatchInWorkingSet) {
+ RunTestOnIOThread(
+ &AppCacheStorageImplTest::FindFallbackPatternMatchInWorkingSet);
+}
+
+TEST_F(AppCacheStorageImplTest, FindFallbackPatternMatchInDatabase) {
+ RunTestOnIOThread(
+ &AppCacheStorageImplTest::FindFallbackPatternMatchInDatabase);
+}
+
+TEST_F(AppCacheStorageImplTest, Reinitialize1) {
+ RunTestOnIOThread(&AppCacheStorageImplTest::Reinitialize1);
+}
+
+TEST_F(AppCacheStorageImplTest, Reinitialize2) {
+ RunTestOnIOThread(&AppCacheStorageImplTest::Reinitialize2);
+}
+
+TEST_F(AppCacheStorageImplTest, Reinitialize3) {
+ RunTestOnIOThread(&AppCacheStorageImplTest::Reinitialize3);
+}
+
+// That's all folks!
+
+} // namespace content
diff --git a/chromium/content/browser/appcache/appcache_storage_unittest.cc b/chromium/content/browser/appcache/appcache_storage_unittest.cc
new file mode 100644
index 00000000000..fa696968e72
--- /dev/null
+++ b/chromium/content/browser/appcache/appcache_storage_unittest.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 "base/message_loop/message_loop.h"
+#include "content/browser/appcache/mock_appcache_service.h"
+#include "content/browser/quota/mock_quota_manager_proxy.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/appcache/appcache.h"
+#include "webkit/browser/appcache/appcache_group.h"
+#include "webkit/browser/appcache/appcache_response.h"
+#include "webkit/browser/appcache/appcache_storage.h"
+
+using appcache::AppCache;
+using appcache::AppCacheGroup;
+using appcache::AppCacheResponseInfo;
+using appcache::AppCacheStorage;
+using appcache::kUnkownResponseDataSize;
+
+namespace content {
+
+namespace {
+const quota::StorageType kTemp = quota::kStorageTypeTemporary;
+}
+
+class AppCacheStorageTest : public testing::Test {
+ public:
+ class MockStorageDelegate : public AppCacheStorage::Delegate {
+ public:
+ };
+};
+
+TEST_F(AppCacheStorageTest, AddRemoveCache) {
+ MockAppCacheService service;
+ scoped_refptr<AppCache> cache(new AppCache(service.storage(), 111));
+
+ EXPECT_EQ(cache.get(),
+ service.storage()->working_set()->GetCache(111));
+
+ service.storage()->working_set()->RemoveCache(cache.get());
+
+ EXPECT_TRUE(!service.storage()->working_set()->GetCache(111));
+
+ // Removing non-existing cache from service should not fail.
+ MockAppCacheService dummy;
+ dummy.storage()->working_set()->RemoveCache(cache.get());
+}
+
+TEST_F(AppCacheStorageTest, AddRemoveGroup) {
+ MockAppCacheService service;
+ scoped_refptr<AppCacheGroup> group(
+ new AppCacheGroup(service.storage(), GURL(), 111));
+
+ EXPECT_EQ(group.get(), service.storage()->working_set()->GetGroup(GURL()));
+
+ service.storage()->working_set()->RemoveGroup(group.get());
+
+ EXPECT_TRUE(!service.storage()->working_set()->GetGroup(GURL()));
+
+ // Removing non-existing group from service should not fail.
+ MockAppCacheService dummy;
+ dummy.storage()->working_set()->RemoveGroup(group.get());
+}
+
+TEST_F(AppCacheStorageTest, AddRemoveResponseInfo) {
+ MockAppCacheService service;
+ scoped_refptr<AppCacheResponseInfo> info(
+ new AppCacheResponseInfo(service.storage(), GURL(),
+ 111, new net::HttpResponseInfo,
+ kUnkownResponseDataSize));
+
+ EXPECT_EQ(info.get(),
+ service.storage()->working_set()->GetResponseInfo(111));
+
+ service.storage()->working_set()->RemoveResponseInfo(info.get());
+
+ EXPECT_TRUE(!service.storage()->working_set()->GetResponseInfo(111));
+
+ // Removing non-existing info from service should not fail.
+ MockAppCacheService dummy;
+ dummy.storage()->working_set()->RemoveResponseInfo(info.get());
+}
+
+TEST_F(AppCacheStorageTest, DelegateReferences) {
+ typedef scoped_refptr<AppCacheStorage::DelegateReference>
+ ScopedDelegateReference;
+ MockAppCacheService service;
+ MockStorageDelegate delegate;
+ ScopedDelegateReference delegate_reference1;
+ ScopedDelegateReference delegate_reference2;
+
+ EXPECT_FALSE(service.storage()->GetDelegateReference(&delegate));
+
+ delegate_reference1 =
+ service.storage()->GetOrCreateDelegateReference(&delegate);
+ EXPECT_TRUE(delegate_reference1.get());
+ EXPECT_TRUE(delegate_reference1->HasOneRef());
+ EXPECT_TRUE(service.storage()->GetDelegateReference(&delegate));
+ EXPECT_EQ(&delegate,
+ service.storage()->GetDelegateReference(&delegate)->delegate);
+ EXPECT_EQ(service.storage()->GetDelegateReference(&delegate),
+ service.storage()->GetOrCreateDelegateReference(&delegate));
+ delegate_reference1 = NULL;
+ EXPECT_FALSE(service.storage()->GetDelegateReference(&delegate));
+
+ delegate_reference1 =
+ service.storage()->GetOrCreateDelegateReference(&delegate);
+ service.storage()->CancelDelegateCallbacks(&delegate);
+ EXPECT_TRUE(delegate_reference1.get());
+ EXPECT_TRUE(delegate_reference1->HasOneRef());
+ EXPECT_FALSE(delegate_reference1->delegate);
+ EXPECT_FALSE(service.storage()->GetDelegateReference(&delegate));
+
+ delegate_reference2 =
+ service.storage()->GetOrCreateDelegateReference(&delegate);
+ EXPECT_TRUE(delegate_reference2.get());
+ EXPECT_TRUE(delegate_reference2->HasOneRef());
+ EXPECT_EQ(&delegate, delegate_reference2->delegate);
+ EXPECT_NE(delegate_reference1.get(), delegate_reference2.get());
+}
+
+TEST_F(AppCacheStorageTest, UsageMap) {
+ const GURL kOrigin("http://origin/");
+ const GURL kOrigin2("http://origin2/");
+
+ MockAppCacheService service;
+ scoped_refptr<MockQuotaManagerProxy> mock_proxy(
+ new MockQuotaManagerProxy(NULL, NULL));
+ service.set_quota_manager_proxy(mock_proxy.get());
+
+ service.storage()->UpdateUsageMapAndNotify(kOrigin, 0);
+ EXPECT_EQ(0, mock_proxy->notify_storage_modified_count());
+
+ service.storage()->UpdateUsageMapAndNotify(kOrigin, 10);
+ EXPECT_EQ(1, mock_proxy->notify_storage_modified_count());
+ EXPECT_EQ(10, mock_proxy->last_notified_delta());
+ EXPECT_EQ(kOrigin, mock_proxy->last_notified_origin());
+ EXPECT_EQ(kTemp, mock_proxy->last_notified_type());
+
+ service.storage()->UpdateUsageMapAndNotify(kOrigin, 100);
+ EXPECT_EQ(2, mock_proxy->notify_storage_modified_count());
+ EXPECT_EQ(90, mock_proxy->last_notified_delta());
+ EXPECT_EQ(kOrigin, mock_proxy->last_notified_origin());
+ EXPECT_EQ(kTemp, mock_proxy->last_notified_type());
+
+ service.storage()->UpdateUsageMapAndNotify(kOrigin, 0);
+ EXPECT_EQ(3, mock_proxy->notify_storage_modified_count());
+ EXPECT_EQ(-100, mock_proxy->last_notified_delta());
+ EXPECT_EQ(kOrigin, mock_proxy->last_notified_origin());
+ EXPECT_EQ(kTemp, mock_proxy->last_notified_type());
+
+ service.storage()->NotifyStorageAccessed(kOrigin2);
+ EXPECT_EQ(0, mock_proxy->notify_storage_accessed_count());
+
+ service.storage()->usage_map_[kOrigin2] = 1;
+ service.storage()->NotifyStorageAccessed(kOrigin2);
+ EXPECT_EQ(1, mock_proxy->notify_storage_accessed_count());
+ EXPECT_EQ(kOrigin2, mock_proxy->last_notified_origin());
+ EXPECT_EQ(kTemp, mock_proxy->last_notified_type());
+
+ service.storage()->usage_map_.clear();
+ service.storage()->usage_map_[kOrigin] = 5000;
+ service.storage()->ClearUsageMapAndNotify();
+ EXPECT_EQ(4, mock_proxy->notify_storage_modified_count());
+ EXPECT_EQ(-5000, mock_proxy->last_notified_delta());
+ EXPECT_EQ(kOrigin, mock_proxy->last_notified_origin());
+ EXPECT_EQ(kTemp, mock_proxy->last_notified_type());
+ EXPECT_TRUE(service.storage()->usage_map_.empty());
+}
+
+} // namespace content
diff --git a/chromium/content/browser/appcache/appcache_unittest.cc b/chromium/content/browser/appcache/appcache_unittest.cc
new file mode 100644
index 00000000000..32c4e89dee0
--- /dev/null
+++ b/chromium/content/browser/appcache/appcache_unittest.cc
@@ -0,0 +1,719 @@
+// 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/appcache/mock_appcache_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/appcache/appcache.h"
+#include "webkit/browser/appcache/appcache_host.h"
+
+using appcache::AppCache;
+using appcache::AppCacheDatabase;
+using appcache::AppCacheEntry;
+using appcache::AppCacheFrontend;
+using appcache::AppCacheGroup;
+using appcache::AppCacheHost;
+using appcache::AppCacheInfo;
+using appcache::AppCacheErrorDetails;
+using appcache::AppCacheEventID;
+using appcache::APPCACHE_FALLBACK_NAMESPACE;
+using appcache::APPCACHE_INTERCEPT_NAMESPACE;
+using appcache::AppCacheLogLevel;
+using appcache::Manifest;
+using appcache::Namespace;
+using appcache::NamespaceVector;
+using appcache::APPCACHE_NETWORK_NAMESPACE;
+using appcache::PARSE_MANIFEST_ALLOWING_INTERCEPTS;
+using appcache::PARSE_MANIFEST_PER_STANDARD;
+using appcache::AppCacheStatus;
+
+namespace content {
+
+namespace {
+
+class MockAppCacheFrontend : public AppCacheFrontend {
+ public:
+ virtual void OnCacheSelected(int host_id, const AppCacheInfo& info) OVERRIDE {
+ }
+ virtual void OnStatusChanged(const std::vector<int>& host_ids,
+ AppCacheStatus status) OVERRIDE {}
+ virtual void OnEventRaised(const std::vector<int>& host_ids,
+ AppCacheEventID event_id) OVERRIDE {}
+ virtual void OnProgressEventRaised(
+ const std::vector<int>& host_ids,
+ const GURL& url,
+ int num_total, int num_complete) OVERRIDE {}
+ virtual void OnErrorEventRaised(
+ const std::vector<int>& host_ids,
+ const AppCacheErrorDetails& details) OVERRIDE {}
+ virtual void OnLogMessage(int host_id, AppCacheLogLevel log_level,
+ const std::string& message) OVERRIDE {}
+ virtual void OnContentBlocked(
+ int host_id, const GURL& manifest_url) OVERRIDE {}
+};
+
+} // namespace
+
+class AppCacheTest : public testing::Test {
+};
+
+TEST(AppCacheTest, CleanupUnusedCache) {
+ MockAppCacheService service;
+ MockAppCacheFrontend frontend;
+ scoped_refptr<AppCache> cache(new AppCache(service.storage(), 111));
+ cache->set_complete(true);
+ scoped_refptr<AppCacheGroup> group(
+ new AppCacheGroup(service.storage(), GURL("http://blah/manifest"), 111));
+ group->AddCache(cache.get());
+
+ AppCacheHost host1(1, &frontend, &service);
+ AppCacheHost host2(2, &frontend, &service);
+
+ host1.AssociateCompleteCache(cache.get());
+ host2.AssociateCompleteCache(cache.get());
+
+ host1.AssociateNoCache(GURL());
+ host2.AssociateNoCache(GURL());
+}
+
+TEST(AppCacheTest, AddModifyRemoveEntry) {
+ MockAppCacheService service;
+ scoped_refptr<AppCache> cache(new AppCache(service.storage(), 111));
+
+ EXPECT_TRUE(cache->entries().empty());
+ EXPECT_EQ(0L, cache->cache_size());
+
+ const GURL kFooUrl("http://foo.com");
+ const int64 kFooResponseId = 1;
+ const int64 kFooSize = 100;
+ AppCacheEntry entry1(AppCacheEntry::MASTER, kFooResponseId, kFooSize);
+ cache->AddEntry(kFooUrl, entry1);
+ EXPECT_EQ(entry1.types(), cache->GetEntry(kFooUrl)->types());
+ EXPECT_EQ(1UL, cache->entries().size());
+ EXPECT_EQ(kFooSize, cache->cache_size());
+
+ const GURL kBarUrl("http://bar.com");
+ const int64 kBarResponseId = 2;
+ const int64 kBarSize = 200;
+ AppCacheEntry entry2(AppCacheEntry::FALLBACK, kBarResponseId, kBarSize);
+ EXPECT_TRUE(cache->AddOrModifyEntry(kBarUrl, entry2));
+ EXPECT_EQ(entry2.types(), cache->GetEntry(kBarUrl)->types());
+ EXPECT_EQ(2UL, cache->entries().size());
+ EXPECT_EQ(kFooSize + kBarSize, cache->cache_size());
+
+ // Expected to return false when an existing entry is modified.
+ AppCacheEntry entry3(AppCacheEntry::EXPLICIT);
+ EXPECT_FALSE(cache->AddOrModifyEntry(kFooUrl, entry3));
+ EXPECT_EQ((AppCacheEntry::MASTER | AppCacheEntry::EXPLICIT),
+ cache->GetEntry(kFooUrl)->types());
+ // Only the type should be modified.
+ EXPECT_EQ(kFooResponseId, cache->GetEntry(kFooUrl)->response_id());
+ EXPECT_EQ(kFooSize, cache->GetEntry(kFooUrl)->response_size());
+ EXPECT_EQ(kFooSize + kBarSize, cache->cache_size());
+
+ EXPECT_EQ(entry2.types(), cache->GetEntry(kBarUrl)->types()); // unchanged
+
+ cache->RemoveEntry(kBarUrl);
+ EXPECT_EQ(kFooSize, cache->cache_size());
+ cache->RemoveEntry(kFooUrl);
+ EXPECT_EQ(0L, cache->cache_size());
+ EXPECT_TRUE(cache->entries().empty());
+}
+
+TEST(AppCacheTest, InitializeWithManifest) {
+ MockAppCacheService service;
+
+ scoped_refptr<AppCache> cache(new AppCache(service.storage(), 1234));
+ EXPECT_TRUE(cache->fallback_namespaces_.empty());
+ EXPECT_TRUE(cache->online_whitelist_namespaces_.empty());
+ EXPECT_FALSE(cache->online_whitelist_all_);
+
+ Manifest manifest;
+ manifest.explicit_urls.insert("http://one.com");
+ manifest.explicit_urls.insert("http://two.com");
+ manifest.fallback_namespaces.push_back(
+ Namespace(APPCACHE_FALLBACK_NAMESPACE, GURL("http://fb1.com"),
+ GURL("http://fbone.com"), true));
+ manifest.online_whitelist_namespaces.push_back(
+ Namespace(APPCACHE_NETWORK_NAMESPACE, GURL("http://w1.com"), GURL(), false));
+ manifest.online_whitelist_namespaces.push_back(
+ Namespace(APPCACHE_NETWORK_NAMESPACE, GURL("http://w2.com"), GURL(), false));
+ manifest.online_whitelist_all = true;
+
+ cache->InitializeWithManifest(&manifest);
+ const std::vector<Namespace>& fallbacks =
+ cache->fallback_namespaces_;
+ size_t expected = 1;
+ EXPECT_EQ(expected, fallbacks.size());
+ EXPECT_EQ(GURL("http://fb1.com"), fallbacks[0].namespace_url);
+ EXPECT_EQ(GURL("http://fbone.com"), fallbacks[0].target_url);
+ EXPECT_TRUE(fallbacks[0].is_pattern);
+ const NamespaceVector& whitelist = cache->online_whitelist_namespaces_;
+ expected = 2;
+ EXPECT_EQ(expected, whitelist.size());
+ EXPECT_EQ(GURL("http://w1.com"), whitelist[0].namespace_url);
+ EXPECT_EQ(GURL("http://w2.com"), whitelist[1].namespace_url);
+ EXPECT_TRUE(cache->online_whitelist_all_);
+
+ // Ensure collections in manifest were taken over by the cache rather than
+ // copied.
+ EXPECT_TRUE(manifest.fallback_namespaces.empty());
+ EXPECT_TRUE(manifest.online_whitelist_namespaces.empty());
+}
+
+TEST(AppCacheTest, FindResponseForRequest) {
+ MockAppCacheService service;
+
+ const GURL kOnlineNamespaceUrl("http://blah/online_namespace");
+ const GURL kFallbackEntryUrl1("http://blah/fallback_entry1");
+ const GURL kFallbackNamespaceUrl1("http://blah/fallback_namespace/");
+ const GURL kFallbackEntryUrl2("http://blah/fallback_entry2");
+ const GURL kFallbackNamespaceUrl2("http://blah/fallback_namespace/longer");
+ const GURL kManifestUrl("http://blah/manifest");
+ const GURL kForeignExplicitEntryUrl("http://blah/foreign");
+ const GURL kInOnlineNamespaceUrl(
+ "http://blah/online_namespace/network");
+ const GURL kExplicitInOnlineNamespaceUrl(
+ "http://blah/online_namespace/explicit");
+ const GURL kFallbackTestUrl1("http://blah/fallback_namespace/1");
+ const GURL kFallbackTestUrl2("http://blah/fallback_namespace/longer2");
+ const GURL kInterceptNamespace("http://blah/intercept_namespace/");
+ const GURL kInterceptNamespaceWithinFallback(
+ "http://blah/fallback_namespace/intercept_namespace/");
+ const GURL kInterceptNamespaceEntry("http://blah/intercept_entry");
+ const GURL kOnlineNamespaceWithinOtherNamespaces(
+ "http://blah/fallback_namespace/intercept_namespace/1/online");
+
+ const int64 kFallbackResponseId1 = 1;
+ const int64 kFallbackResponseId2 = 2;
+ const int64 kManifestResponseId = 3;
+ const int64 kForeignExplicitResponseId = 4;
+ const int64 kExplicitInOnlineNamespaceResponseId = 5;
+ const int64 kInterceptResponseId = 6;
+
+ Manifest manifest;
+ manifest.online_whitelist_namespaces.push_back(
+ Namespace(APPCACHE_NETWORK_NAMESPACE, kOnlineNamespaceUrl,
+ GURL(), false));
+ manifest.online_whitelist_namespaces.push_back(
+ Namespace(APPCACHE_NETWORK_NAMESPACE,
+ kOnlineNamespaceWithinOtherNamespaces,
+ GURL(), false));
+ manifest.fallback_namespaces.push_back(
+ Namespace(APPCACHE_FALLBACK_NAMESPACE, kFallbackNamespaceUrl1,
+ kFallbackEntryUrl1, false));
+ manifest.fallback_namespaces.push_back(
+ Namespace(APPCACHE_FALLBACK_NAMESPACE, kFallbackNamespaceUrl2,
+ kFallbackEntryUrl2, false));
+ manifest.intercept_namespaces.push_back(
+ Namespace(APPCACHE_INTERCEPT_NAMESPACE, kInterceptNamespace,
+ kInterceptNamespaceEntry, false));
+ manifest.intercept_namespaces.push_back(
+ Namespace(APPCACHE_INTERCEPT_NAMESPACE, kInterceptNamespaceWithinFallback,
+ kInterceptNamespaceEntry, false));
+
+ // Create a cache with some namespaces and entries.
+ scoped_refptr<AppCache> cache(new AppCache(service.storage(), 1234));
+ cache->InitializeWithManifest(&manifest);
+ cache->AddEntry(
+ kFallbackEntryUrl1,
+ AppCacheEntry(AppCacheEntry::FALLBACK, kFallbackResponseId1));
+ cache->AddEntry(
+ kFallbackEntryUrl2,
+ AppCacheEntry(AppCacheEntry::FALLBACK, kFallbackResponseId2));
+ cache->AddEntry(
+ kManifestUrl,
+ AppCacheEntry(AppCacheEntry::MANIFEST, kManifestResponseId));
+ cache->AddEntry(
+ kForeignExplicitEntryUrl,
+ AppCacheEntry(AppCacheEntry::EXPLICIT | AppCacheEntry::FOREIGN,
+ kForeignExplicitResponseId));
+ cache->AddEntry(
+ kExplicitInOnlineNamespaceUrl,
+ AppCacheEntry(AppCacheEntry::EXPLICIT,
+ kExplicitInOnlineNamespaceResponseId));
+ cache->AddEntry(
+ kInterceptNamespaceEntry,
+ AppCacheEntry(AppCacheEntry::INTERCEPT, kInterceptResponseId));
+ cache->set_complete(true);
+
+ // See that we get expected results from FindResponseForRequest
+
+ bool found = false;
+ AppCacheEntry entry;
+ AppCacheEntry fallback_entry;
+ GURL intercept_namespace;
+ GURL fallback_namespace;
+ bool network_namespace = false;
+
+ found = cache->FindResponseForRequest(GURL("http://blah/miss"),
+ &entry, &intercept_namespace,
+ &fallback_entry, &fallback_namespace,
+ &network_namespace);
+ EXPECT_FALSE(found);
+
+ found = cache->FindResponseForRequest(kForeignExplicitEntryUrl,
+ &entry, &intercept_namespace,
+ &fallback_entry, &fallback_namespace,
+ &network_namespace);
+ EXPECT_TRUE(found);
+ EXPECT_EQ(kForeignExplicitResponseId, entry.response_id());
+ EXPECT_FALSE(fallback_entry.has_response_id());
+ EXPECT_FALSE(network_namespace);
+
+ entry = AppCacheEntry(); // reset
+
+ found = cache->FindResponseForRequest(kManifestUrl,
+ &entry, &intercept_namespace,
+ &fallback_entry, &fallback_namespace,
+ &network_namespace);
+ EXPECT_TRUE(found);
+ EXPECT_EQ(kManifestResponseId, entry.response_id());
+ EXPECT_FALSE(fallback_entry.has_response_id());
+ EXPECT_FALSE(network_namespace);
+
+ entry = AppCacheEntry(); // reset
+
+ found = cache->FindResponseForRequest(kInOnlineNamespaceUrl,
+ &entry, &intercept_namespace,
+ &fallback_entry, &fallback_namespace,
+ &network_namespace);
+ EXPECT_TRUE(found);
+ EXPECT_FALSE(entry.has_response_id());
+ EXPECT_FALSE(fallback_entry.has_response_id());
+ EXPECT_TRUE(network_namespace);
+
+ network_namespace = false; // reset
+
+ found = cache->FindResponseForRequest(kExplicitInOnlineNamespaceUrl,
+ &entry, &intercept_namespace,
+ &fallback_entry, &fallback_namespace,
+ &network_namespace);
+ EXPECT_TRUE(found);
+ EXPECT_EQ(kExplicitInOnlineNamespaceResponseId, entry.response_id());
+ EXPECT_FALSE(fallback_entry.has_response_id());
+ EXPECT_FALSE(network_namespace);
+
+ entry = AppCacheEntry(); // reset
+
+ found = cache->FindResponseForRequest(kFallbackTestUrl1,
+ &entry, &intercept_namespace,
+ &fallback_entry, &fallback_namespace,
+ &network_namespace);
+ EXPECT_TRUE(found);
+ EXPECT_FALSE(entry.has_response_id());
+ EXPECT_EQ(kFallbackResponseId1, fallback_entry.response_id());
+ EXPECT_EQ(kFallbackEntryUrl1,
+ cache->GetFallbackEntryUrl(fallback_namespace));
+ EXPECT_FALSE(network_namespace);
+
+ fallback_entry = AppCacheEntry(); // reset
+
+ found = cache->FindResponseForRequest(kFallbackTestUrl2,
+ &entry, &intercept_namespace,
+ &fallback_entry, &fallback_namespace,
+ &network_namespace);
+ EXPECT_TRUE(found);
+ EXPECT_FALSE(entry.has_response_id());
+ EXPECT_EQ(kFallbackResponseId2, fallback_entry.response_id());
+ EXPECT_EQ(kFallbackEntryUrl2,
+ cache->GetFallbackEntryUrl(fallback_namespace));
+ EXPECT_FALSE(network_namespace);
+
+ fallback_entry = AppCacheEntry(); // reset
+
+ found = cache->FindResponseForRequest(kOnlineNamespaceWithinOtherNamespaces,
+ &entry, &intercept_namespace,
+ &fallback_entry, &fallback_namespace,
+ &network_namespace);
+ EXPECT_TRUE(found);
+ EXPECT_FALSE(entry.has_response_id());
+ EXPECT_FALSE(fallback_entry.has_response_id());
+ EXPECT_TRUE(network_namespace);
+
+ fallback_entry = AppCacheEntry(); // reset
+
+ found = cache->FindResponseForRequest(
+ kOnlineNamespaceWithinOtherNamespaces.Resolve("online_resource"),
+ &entry, &intercept_namespace,
+ &fallback_entry, &fallback_namespace,
+ &network_namespace);
+ EXPECT_TRUE(found);
+ EXPECT_FALSE(entry.has_response_id());
+ EXPECT_FALSE(fallback_entry.has_response_id());
+ EXPECT_TRUE(network_namespace);
+
+ fallback_namespace = GURL();
+
+ found = cache->FindResponseForRequest(
+ kInterceptNamespace.Resolve("intercept_me"),
+ &entry, &intercept_namespace,
+ &fallback_entry, &fallback_namespace,
+ &network_namespace);
+ EXPECT_TRUE(found);
+ EXPECT_EQ(kInterceptResponseId, entry.response_id());
+ EXPECT_EQ(kInterceptNamespaceEntry,
+ cache->GetInterceptEntryUrl(intercept_namespace));
+ EXPECT_FALSE(fallback_entry.has_response_id());
+ EXPECT_TRUE(fallback_namespace.is_empty());
+ EXPECT_FALSE(network_namespace);
+
+ entry = AppCacheEntry(); // reset
+
+ found = cache->FindResponseForRequest(
+ kInterceptNamespaceWithinFallback.Resolve("intercept_me"),
+ &entry, &intercept_namespace,
+ &fallback_entry, &fallback_namespace,
+ &network_namespace);
+ EXPECT_TRUE(found);
+ EXPECT_EQ(kInterceptResponseId, entry.response_id());
+ EXPECT_EQ(kInterceptNamespaceEntry,
+ cache->GetInterceptEntryUrl(intercept_namespace));
+ EXPECT_FALSE(fallback_entry.has_response_id());
+ EXPECT_TRUE(fallback_namespace.is_empty());
+ EXPECT_FALSE(network_namespace);
+}
+
+TEST(AppCacheTest, FindInterceptPatternResponseForRequest) {
+ MockAppCacheService service;
+
+ // Setup an appcache with an intercept namespace that uses pattern matching.
+ const GURL kInterceptNamespaceBase("http://blah/intercept_namespace/");
+ const GURL kInterceptPatternNamespace(
+ kInterceptNamespaceBase.Resolve("*.hit*"));
+ const GURL kInterceptNamespaceEntry("http://blah/intercept_resource");
+ const int64 kInterceptResponseId = 1;
+ Manifest manifest;
+ manifest.intercept_namespaces.push_back(
+ Namespace(APPCACHE_INTERCEPT_NAMESPACE, kInterceptPatternNamespace,
+ kInterceptNamespaceEntry, true));
+ scoped_refptr<AppCache> cache(new AppCache(service.storage(), 1234));
+ cache->InitializeWithManifest(&manifest);
+ cache->AddEntry(
+ kInterceptNamespaceEntry,
+ AppCacheEntry(AppCacheEntry::INTERCEPT, kInterceptResponseId));
+ cache->set_complete(true);
+
+ // See that the pattern match works.
+ bool found = false;
+ AppCacheEntry entry;
+ AppCacheEntry fallback_entry;
+ GURL intercept_namespace;
+ GURL fallback_namespace;
+ bool network_namespace = false;
+
+ found = cache->FindResponseForRequest(
+ GURL("http://blah/miss"),
+ &entry, &intercept_namespace,
+ &fallback_entry, &fallback_namespace,
+ &network_namespace);
+ EXPECT_FALSE(found);
+
+ found = cache->FindResponseForRequest(
+ GURL("http://blah/intercept_namespace/another_miss"),
+ &entry, &intercept_namespace,
+ &fallback_entry, &fallback_namespace,
+ &network_namespace);
+ EXPECT_FALSE(found);
+
+ found = cache->FindResponseForRequest(
+ GURL("http://blah/intercept_namespace/path.hit"),
+ &entry, &intercept_namespace,
+ &fallback_entry, &fallback_namespace,
+ &network_namespace);
+ EXPECT_TRUE(found);
+ EXPECT_EQ(kInterceptResponseId, entry.response_id());
+ EXPECT_EQ(kInterceptNamespaceEntry,
+ cache->GetInterceptEntryUrl(intercept_namespace));
+ EXPECT_FALSE(fallback_entry.has_response_id());
+ EXPECT_TRUE(fallback_namespace.is_empty());
+ EXPECT_FALSE(network_namespace);
+
+ entry = AppCacheEntry(); // reset
+
+ found = cache->FindResponseForRequest(
+ GURL("http://blah/intercept_namespace/longer/path.hit?arg=ok"),
+ &entry, &intercept_namespace,
+ &fallback_entry, &fallback_namespace,
+ &network_namespace);
+ EXPECT_TRUE(found);
+ EXPECT_EQ(kInterceptResponseId, entry.response_id());
+ EXPECT_EQ(kInterceptNamespaceEntry,
+ cache->GetInterceptEntryUrl(intercept_namespace));
+ EXPECT_FALSE(fallback_entry.has_response_id());
+ EXPECT_TRUE(fallback_namespace.is_empty());
+ EXPECT_FALSE(network_namespace);
+}
+
+TEST(AppCacheTest, FindFallbackPatternResponseForRequest) {
+ MockAppCacheService service;
+
+ // Setup an appcache with a fallback namespace that uses pattern matching.
+ const GURL kFallbackNamespaceBase("http://blah/fallback_namespace/");
+ const GURL kFallbackPatternNamespace(
+ kFallbackNamespaceBase.Resolve("*.hit*"));
+ const GURL kFallbackNamespaceEntry("http://blah/fallback_resource");
+ const int64 kFallbackResponseId = 1;
+ Manifest manifest;
+ manifest.fallback_namespaces.push_back(
+ Namespace(APPCACHE_FALLBACK_NAMESPACE, kFallbackPatternNamespace,
+ kFallbackNamespaceEntry, true));
+ scoped_refptr<AppCache> cache(new AppCache(service.storage(), 1234));
+ cache->InitializeWithManifest(&manifest);
+ cache->AddEntry(
+ kFallbackNamespaceEntry,
+ AppCacheEntry(AppCacheEntry::FALLBACK, kFallbackResponseId));
+ cache->set_complete(true);
+
+ // See that the pattern match works.
+ bool found = false;
+ AppCacheEntry entry;
+ AppCacheEntry fallback_entry;
+ GURL intercept_namespace;
+ GURL fallback_namespace;
+ bool network_namespace = false;
+
+ found = cache->FindResponseForRequest(
+ GURL("http://blah/miss"),
+ &entry, &intercept_namespace,
+ &fallback_entry, &fallback_namespace,
+ &network_namespace);
+ EXPECT_FALSE(found);
+
+ found = cache->FindResponseForRequest(
+ GURL("http://blah/fallback_namespace/another_miss"),
+ &entry, &intercept_namespace,
+ &fallback_entry, &fallback_namespace,
+ &network_namespace);
+ EXPECT_FALSE(found);
+
+ found = cache->FindResponseForRequest(
+ GURL("http://blah/fallback_namespace/path.hit"),
+ &entry, &intercept_namespace,
+ &fallback_entry, &fallback_namespace,
+ &network_namespace);
+ EXPECT_TRUE(found);
+ EXPECT_FALSE(entry.has_response_id());
+ EXPECT_EQ(kFallbackResponseId, fallback_entry.response_id());
+ EXPECT_EQ(kFallbackNamespaceEntry,
+ cache->GetFallbackEntryUrl(fallback_namespace));
+ EXPECT_FALSE(network_namespace);
+
+ fallback_entry = AppCacheEntry();
+ fallback_namespace = GURL();
+
+ found = cache->FindResponseForRequest(
+ GURL("http://blah/fallback_namespace/longer/path.hit?arg=ok"),
+ &entry, &intercept_namespace,
+ &fallback_entry, &fallback_namespace,
+ &network_namespace);
+ EXPECT_TRUE(found);
+ EXPECT_FALSE(entry.has_response_id());
+ EXPECT_EQ(kFallbackResponseId, fallback_entry.response_id());
+ EXPECT_EQ(kFallbackNamespaceEntry,
+ cache->GetFallbackEntryUrl(fallback_namespace));
+ EXPECT_TRUE(intercept_namespace.is_empty());
+ EXPECT_FALSE(network_namespace);
+}
+
+
+TEST(AppCacheTest, FindNetworkNamespacePatternResponseForRequest) {
+ MockAppCacheService service;
+
+ // Setup an appcache with a network namespace that uses pattern matching.
+ const GURL kNetworkNamespaceBase("http://blah/network_namespace/");
+ const GURL kNetworkPatternNamespace(
+ kNetworkNamespaceBase.Resolve("*.hit*"));
+ Manifest manifest;
+ manifest.online_whitelist_namespaces.push_back(
+ Namespace(APPCACHE_NETWORK_NAMESPACE, kNetworkPatternNamespace,
+ GURL(), true));
+ manifest.online_whitelist_all = false;
+ scoped_refptr<AppCache> cache(new AppCache(service.storage(), 1234));
+ cache->InitializeWithManifest(&manifest);
+ cache->set_complete(true);
+
+ // See that the pattern match works.
+ bool found = false;
+ AppCacheEntry entry;
+ AppCacheEntry fallback_entry;
+ GURL intercept_namespace;
+ GURL fallback_namespace;
+ bool network_namespace = false;
+
+ found = cache->FindResponseForRequest(
+ GURL("http://blah/miss"),
+ &entry, &intercept_namespace,
+ &fallback_entry, &fallback_namespace,
+ &network_namespace);
+ EXPECT_FALSE(found);
+
+ found = cache->FindResponseForRequest(
+ GURL("http://blah/network_namespace/path.hit"),
+ &entry, &intercept_namespace,
+ &fallback_entry, &fallback_namespace,
+ &network_namespace);
+ EXPECT_TRUE(found);
+ EXPECT_TRUE(network_namespace);
+ EXPECT_FALSE(entry.has_response_id());
+ EXPECT_FALSE(fallback_entry.has_response_id());
+}
+
+TEST(AppCacheTest, ToFromDatabaseRecords) {
+ // Setup a cache with some entries.
+ const int64 kCacheId = 1234;
+ const int64 kGroupId = 4321;
+ const GURL kManifestUrl("http://foo.com/manifest");
+ const GURL kInterceptUrl("http://foo.com/intercept.html");
+ const GURL kFallbackUrl("http://foo.com/fallback.html");
+ const GURL kWhitelistUrl("http://foo.com/whitelist*");
+ const std::string kData(
+ "CACHE MANIFEST\r"
+ "CHROMIUM-INTERCEPT:\r"
+ "/intercept return /intercept.html\r"
+ "FALLBACK:\r"
+ "/ /fallback.html\r"
+ "NETWORK:\r"
+ "/whitelist* isPattern\r"
+ "*\r");
+ MockAppCacheService service;
+ scoped_refptr<AppCacheGroup> group =
+ new AppCacheGroup(service.storage(), kManifestUrl, kGroupId);
+ scoped_refptr<AppCache> cache(new AppCache(service.storage(), kCacheId));
+ Manifest manifest;
+ EXPECT_TRUE(ParseManifest(kManifestUrl, kData.c_str(), kData.length(),
+ PARSE_MANIFEST_ALLOWING_INTERCEPTS, manifest));
+ cache->InitializeWithManifest(&manifest);
+ EXPECT_EQ(APPCACHE_NETWORK_NAMESPACE,
+ cache->online_whitelist_namespaces_[0].type);
+ EXPECT_TRUE(cache->online_whitelist_namespaces_[0].is_pattern);
+ EXPECT_EQ(kWhitelistUrl,
+ cache->online_whitelist_namespaces_[0].namespace_url);
+ cache->AddEntry(
+ kManifestUrl,
+ AppCacheEntry(AppCacheEntry::MANIFEST, 1, 1));
+ cache->AddEntry(
+ kInterceptUrl,
+ AppCacheEntry(AppCacheEntry::INTERCEPT, 3, 3));
+ cache->AddEntry(
+ kFallbackUrl,
+ AppCacheEntry(AppCacheEntry::FALLBACK, 2, 2));
+
+ // Get it to produce database records and verify them.
+ AppCacheDatabase::CacheRecord cache_record;
+ std::vector<AppCacheDatabase::EntryRecord> entries;
+ std::vector<AppCacheDatabase::NamespaceRecord> intercepts;
+ std::vector<AppCacheDatabase::NamespaceRecord> fallbacks;
+ std::vector<AppCacheDatabase::OnlineWhiteListRecord> whitelists;
+ cache->ToDatabaseRecords(group.get(),
+ &cache_record,
+ &entries,
+ &intercepts,
+ &fallbacks,
+ &whitelists);
+ EXPECT_EQ(kCacheId, cache_record.cache_id);
+ EXPECT_EQ(kGroupId, cache_record.group_id);
+ EXPECT_TRUE(cache_record.online_wildcard);
+ EXPECT_EQ(1 + 2 + 3, cache_record.cache_size);
+ EXPECT_EQ(3u, entries.size());
+ EXPECT_EQ(1u, intercepts.size());
+ EXPECT_EQ(1u, fallbacks.size());
+ EXPECT_EQ(1u, whitelists.size());
+ cache = NULL;
+
+ // Create a new AppCache and populate it with those records and verify.
+ cache = new AppCache(service.storage(), kCacheId);
+ cache->InitializeWithDatabaseRecords(
+ cache_record, entries, intercepts,
+ fallbacks, whitelists);
+ EXPECT_TRUE(cache->online_whitelist_all_);
+ EXPECT_EQ(3u, cache->entries().size());
+ EXPECT_TRUE(cache->GetEntry(kManifestUrl));
+ EXPECT_TRUE(cache->GetEntry(kInterceptUrl));
+ EXPECT_TRUE(cache->GetEntry(kFallbackUrl));
+ EXPECT_EQ(kInterceptUrl,
+ cache->GetInterceptEntryUrl(GURL("http://foo.com/intercept")));
+ EXPECT_EQ(kFallbackUrl,
+ cache->GetFallbackEntryUrl(GURL("http://foo.com/")));
+ EXPECT_EQ(1 + 2 + 3, cache->cache_size());
+ EXPECT_EQ(APPCACHE_NETWORK_NAMESPACE,
+ cache->online_whitelist_namespaces_[0].type);
+ EXPECT_TRUE(cache->online_whitelist_namespaces_[0].is_pattern);
+ EXPECT_EQ(kWhitelistUrl,
+ cache->online_whitelist_namespaces_[0].namespace_url);
+}
+
+TEST(AppCacheTest, IsNamespaceMatch) {
+ Namespace prefix;
+ prefix.namespace_url = GURL("http://foo.com/prefix");
+ prefix.is_pattern = false;
+ EXPECT_TRUE(prefix.IsMatch(
+ GURL("http://foo.com/prefix_and_anothing_goes")));
+ EXPECT_FALSE(prefix.IsMatch(
+ GURL("http://foo.com/nope")));
+
+ Namespace bar_no_star;
+ bar_no_star.namespace_url = GURL("http://foo.com/bar");
+ bar_no_star.is_pattern = true;
+ EXPECT_TRUE(bar_no_star.IsMatch(
+ GURL("http://foo.com/bar")));
+ EXPECT_FALSE(bar_no_star.IsMatch(
+ GURL("http://foo.com/bar/nope")));
+
+ Namespace bar_star;
+ bar_star.namespace_url = GURL("http://foo.com/bar/*");
+ bar_star.is_pattern = true;
+ EXPECT_TRUE(bar_star.IsMatch(
+ GURL("http://foo.com/bar/")));
+ EXPECT_TRUE(bar_star.IsMatch(
+ GURL("http://foo.com/bar/should_match")));
+ EXPECT_FALSE(bar_star.IsMatch(
+ GURL("http://foo.com/not_bar/should_not_match")));
+
+ Namespace star_bar_star;
+ star_bar_star.namespace_url = GURL("http://foo.com/*/bar/*");
+ star_bar_star.is_pattern = true;
+ EXPECT_TRUE(star_bar_star.IsMatch(
+ GURL("http://foo.com/any/bar/should_match")));
+ EXPECT_TRUE(star_bar_star.IsMatch(
+ GURL("http://foo.com/any/bar/")));
+ EXPECT_FALSE(star_bar_star.IsMatch(
+ GURL("http://foo.com/any/not_bar/no_match")));
+
+ Namespace query_star_edit;
+ query_star_edit.namespace_url = GURL("http://foo.com/query?id=*&verb=edit*");
+ query_star_edit.is_pattern = true;
+ EXPECT_TRUE(query_star_edit.IsMatch(
+ GURL("http://foo.com/query?id=1234&verb=edit&option=blue")));
+ EXPECT_TRUE(query_star_edit.IsMatch(
+ GURL("http://foo.com/query?id=12345&option=blue&verb=edit")));
+ EXPECT_FALSE(query_star_edit.IsMatch(
+ GURL("http://foo.com/query?id=12345&option=blue&verb=print")));
+ EXPECT_TRUE(query_star_edit.IsMatch(
+ GURL("http://foo.com/query?id=123&verb=print&verb=edit")));
+
+ Namespace star_greediness;
+ star_greediness.namespace_url = GURL("http://foo.com/*/b");
+ star_greediness.is_pattern = true;
+ EXPECT_TRUE(star_greediness.IsMatch(
+ GURL("http://foo.com/a/b")));
+ EXPECT_TRUE(star_greediness.IsMatch(
+ GURL("http://foo.com/a/wxy/z/b")));
+ EXPECT_TRUE(star_greediness.IsMatch(
+ GURL("http://foo.com/a/b/b")));
+ EXPECT_TRUE(star_greediness.IsMatch(
+ GURL("http://foo.com/b/b")));
+ EXPECT_TRUE(star_greediness.IsMatch(
+ GURL("http://foo.com/a/b/b/b/b/b")));
+ EXPECT_TRUE(star_greediness.IsMatch(
+ GURL("http://foo.com/a/b/b/b/a/b")));
+ EXPECT_TRUE(star_greediness.IsMatch(
+ GURL("http://foo.com/a/b/01234567890abcdef/b")));
+ EXPECT_TRUE(star_greediness.IsMatch(
+ GURL("http://foo.com/a/b/01234567890abcdef/b01234567890abcdef/b")));
+ EXPECT_TRUE(star_greediness.IsMatch(
+ GURL("http://foo.com/a/b/01234567890abcdef_eat_some_more_characters_"
+ "/and_even_more_for_the_heck_of_it/01234567890abcdef/b")));
+}
+
+} // namespace content
diff --git a/chromium/content/browser/appcache/appcache_update_job_unittest.cc b/chromium/content/browser/appcache/appcache_update_job_unittest.cc
new file mode 100644
index 00000000000..bc3957e6bea
--- /dev/null
+++ b/chromium/content/browser/appcache/appcache_update_job_unittest.cc
@@ -0,0 +1,3720 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/stl_util.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "content/browser/appcache/mock_appcache_service.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_response_headers.h"
+#include "net/url_request/url_request_error_job.h"
+#include "net/url_request/url_request_job_factory_impl.h"
+#include "net/url_request/url_request_test_job.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/appcache/appcache_group.h"
+#include "webkit/browser/appcache/appcache_host.h"
+#include "webkit/browser/appcache/appcache_response.h"
+#include "webkit/browser/appcache/appcache_update_job.h"
+
+using appcache::AppCache;
+using appcache::AppCacheEntry;
+using appcache::AppCacheFrontend;
+using appcache::AppCacheHost;
+using appcache::AppCacheGroup;
+using appcache::AppCacheResponseInfo;
+using appcache::AppCacheUpdateJob;
+using appcache::AppCacheResponseWriter;
+using appcache::APPCACHE_CACHED_EVENT;
+using appcache::APPCACHE_CHECKING_EVENT;
+using appcache::APPCACHE_DOWNLOADING_EVENT;
+using appcache::APPCACHE_ERROR_EVENT;
+using appcache::AppCacheEventID;
+using appcache::APPCACHE_FALLBACK_NAMESPACE;
+using appcache::HttpResponseInfoIOBuffer;
+using appcache::kAppCacheNoCacheId;
+using appcache::kAppCacheNoResponseId;
+using appcache::Namespace;
+using appcache::APPCACHE_NETWORK_NAMESPACE;
+using appcache::APPCACHE_NO_UPDATE_EVENT;
+using appcache::APPCACHE_OBSOLETE_EVENT;
+using appcache::APPCACHE_PROGRESS_EVENT;
+using appcache::APPCACHE_UPDATE_READY_EVENT;
+using appcache::AppCacheStatus;
+
+namespace content {
+class AppCacheUpdateJobTest;
+
+namespace {
+
+const char kManifest1Contents[] =
+ "CACHE MANIFEST\n"
+ "explicit1\n"
+ "FALLBACK:\n"
+ "fallback1 fallback1a\n"
+ "NETWORK:\n"
+ "*\n";
+
+// There are a handful of http accessible resources that we need to conduct
+// these tests. Instead of running a seperate server to host these resources,
+// we mock them up.
+class MockHttpServer {
+ public:
+ static GURL GetMockUrl(const std::string& path) {
+ return GURL("http://mockhost/" + path);
+ }
+
+ static GURL GetMockHttpsUrl(const std::string& path) {
+ return GURL("https://mockhost/" + path);
+ }
+
+ static GURL GetMockCrossOriginHttpsUrl(const std::string& path) {
+ return GURL("https://cross_origin_host/" + path);
+ }
+
+ static net::URLRequestJob* JobFactory(
+ net::URLRequest* request, net::NetworkDelegate* network_delegate) {
+ if (request->url().host() != "mockhost" &&
+ request->url().host() != "cross_origin_host")
+ return new net::URLRequestErrorJob(request, network_delegate, -100);
+
+ std::string headers, body;
+ GetMockResponse(request->url().path(), &headers, &body);
+ return new net::URLRequestTestJob(
+ request, network_delegate, headers, body, true);
+ }
+
+ private:
+ static void GetMockResponse(const std::string& path,
+ std::string* headers,
+ std::string* body) {
+ const char ok_headers[] =
+ "HTTP/1.1 200 OK\0"
+ "\0";
+ const char error_headers[] =
+ "HTTP/1.1 500 BOO HOO\0"
+ "\0";
+ const char manifest_headers[] =
+ "HTTP/1.1 200 OK\0"
+ "Content-type: text/cache-manifest\0"
+ "\0";
+ const char not_modified_headers[] =
+ "HTTP/1.1 304 NOT MODIFIED\0"
+ "\0";
+ const char gone_headers[] =
+ "HTTP/1.1 410 GONE\0"
+ "\0";
+ const char not_found_headers[] =
+ "HTTP/1.1 404 NOT FOUND\0"
+ "\0";
+ const char no_store_headers[] =
+ "HTTP/1.1 200 OK\0"
+ "Cache-Control: no-store\0"
+ "\0";
+
+ if (path == "/files/missing-mime-manifest") {
+ (*headers) = std::string(ok_headers, arraysize(ok_headers));
+ (*body) = "CACHE MANIFEST\n";
+ } else if (path == "/files/bad-manifest") {
+ (*headers) = std::string(manifest_headers, arraysize(manifest_headers));
+ (*body) = "BAD CACHE MANIFEST";
+ } else if (path == "/files/empty1") {
+ (*headers) = std::string(ok_headers, arraysize(ok_headers));
+ (*body) = "";
+ } else if (path == "/files/empty-file-manifest") {
+ (*headers) = std::string(manifest_headers, arraysize(manifest_headers));
+ (*body) = "CACHE MANIFEST\n"
+ "empty1\n";
+ } else if (path == "/files/empty-manifest") {
+ (*headers) = std::string(manifest_headers, arraysize(manifest_headers));
+ (*body) = "CACHE MANIFEST\n";
+ } else if (path == "/files/explicit1") {
+ (*headers) = std::string(ok_headers, arraysize(ok_headers));
+ (*body) = "explicit1";
+ } else if (path == "/files/explicit2") {
+ (*headers) = std::string(ok_headers, arraysize(ok_headers));
+ (*body) = "explicit2";
+ } else if (path == "/files/fallback1a") {
+ (*headers) = std::string(ok_headers, arraysize(ok_headers));
+ (*body) = "fallback1a";
+ } else if (path == "/files/intercept1a") {
+ (*headers) = std::string(ok_headers, arraysize(ok_headers));
+ (*body) = "intercept1a";
+ } else if (path == "/files/gone") {
+ (*headers) = std::string(gone_headers, arraysize(gone_headers));
+ (*body) = "";
+ } else if (path == "/files/manifest1") {
+ (*headers) = std::string(manifest_headers, arraysize(manifest_headers));
+ (*body) = kManifest1Contents;
+ } else if (path == "/files/manifest1-with-notmodified") {
+ (*headers) = std::string(manifest_headers, arraysize(manifest_headers));
+ (*body) = kManifest1Contents;
+ (*body).append("CACHE:\n"
+ "notmodified\n");
+ } else if (path == "/files/manifest-fb-404") {
+ (*headers) = std::string(manifest_headers, arraysize(manifest_headers));
+ (*body) = "CACHE MANIFEST\n"
+ "explicit1\n"
+ "FALLBACK:\n"
+ "fallback1 fallback1a\n"
+ "fallback404 fallback-404\n"
+ "NETWORK:\n"
+ "online1\n";
+ } else if (path == "/files/manifest-merged-types") {
+ (*headers) = std::string(manifest_headers, arraysize(manifest_headers));
+ (*body) = "CACHE MANIFEST\n"
+ "explicit1\n"
+ "# manifest is also an explicit entry\n"
+ "manifest-merged-types\n"
+ "FALLBACK:\n"
+ "# fallback is also explicit entry\n"
+ "fallback1 explicit1\n"
+ "NETWORK:\n"
+ "online1\n";
+ } else if (path == "/files/manifest-with-404") {
+ (*headers) = std::string(manifest_headers, arraysize(manifest_headers));
+ (*body) = "CACHE MANIFEST\n"
+ "explicit-404\n"
+ "explicit1\n"
+ "explicit2\n"
+ "explicit3\n"
+ "FALLBACK:\n"
+ "fallback1 fallback1a\n"
+ "NETWORK:\n"
+ "online1\n";
+ } else if (path == "/files/manifest-with-intercept") {
+ (*headers) = std::string(manifest_headers, arraysize(manifest_headers));
+ (*body) = "CACHE MANIFEST\n"
+ "CHROMIUM-INTERCEPT:\n"
+ "intercept1 return intercept1a\n";
+ } else if (path == "/files/notmodified") {
+ (*headers) = std::string(not_modified_headers,
+ arraysize(not_modified_headers));
+ (*body) = "";
+ } else if (path == "/files/servererror") {
+ (*headers) = std::string(error_headers,
+ arraysize(error_headers));
+ (*body) = "error";
+ } else if (path == "/files/valid_cross_origin_https_manifest") {
+ (*headers) = std::string(manifest_headers, arraysize(manifest_headers));
+ (*body) = "CACHE MANIFEST\n"
+ "https://cross_origin_host/files/explicit1\n";
+ } else if (path == "/files/invalid_cross_origin_https_manifest") {
+ (*headers) = std::string(manifest_headers, arraysize(manifest_headers));
+ (*body) = "CACHE MANIFEST\n"
+ "https://cross_origin_host/files/no-store-headers\n";
+ } else if (path == "/files/no-store-headers") {
+ (*headers) = std::string(no_store_headers, arraysize(no_store_headers));
+ (*body) = "no-store";
+ } else {
+ (*headers) = std::string(not_found_headers,
+ arraysize(not_found_headers));
+ (*body) = "";
+ }
+ }
+};
+
+class MockHttpServerJobFactory
+ : public net::URLRequestJobFactory::ProtocolHandler {
+ public:
+ virtual net::URLRequestJob* MaybeCreateJob(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) const OVERRIDE {
+ return MockHttpServer::JobFactory(request, network_delegate);
+ }
+};
+
+inline bool operator==(const Namespace& lhs, const Namespace& rhs) {
+ return lhs.type == rhs.type &&
+ lhs.namespace_url == rhs.namespace_url &&
+ lhs.target_url == rhs.target_url;
+}
+
+} // namespace
+
+class MockFrontend : public AppCacheFrontend {
+ public:
+ MockFrontend()
+ : ignore_progress_events_(false), verify_progress_events_(false),
+ last_progress_total_(-1), last_progress_complete_(-1),
+ start_update_trigger_(APPCACHE_CHECKING_EVENT), update_(NULL) {
+ }
+
+ virtual void OnCacheSelected(
+ int host_id, const appcache::AppCacheInfo& info) OVERRIDE {
+ }
+
+ virtual void OnStatusChanged(const std::vector<int>& host_ids,
+ AppCacheStatus status) OVERRIDE {
+ }
+
+ virtual void OnEventRaised(const std::vector<int>& host_ids,
+ AppCacheEventID event_id) OVERRIDE {
+ raised_events_.push_back(RaisedEvent(host_ids, event_id));
+
+ // Trigger additional updates if requested.
+ if (event_id == start_update_trigger_ && update_) {
+ for (std::vector<AppCacheHost*>::iterator it = update_hosts_.begin();
+ it != update_hosts_.end(); ++it) {
+ AppCacheHost* host = *it;
+ update_->StartUpdate(host,
+ (host ? host->pending_master_entry_url() : GURL()));
+ }
+ update_hosts_.clear(); // only trigger once
+ }
+ }
+
+ virtual void OnErrorEventRaised(const std::vector<int>& host_ids,
+ const appcache::AppCacheErrorDetails& details)
+ OVERRIDE {
+ error_message_ = details.message;
+ OnEventRaised(host_ids, APPCACHE_ERROR_EVENT);
+ }
+
+ virtual void OnProgressEventRaised(const std::vector<int>& host_ids,
+ const GURL& url,
+ int num_total,
+ int num_complete) OVERRIDE {
+ if (!ignore_progress_events_)
+ OnEventRaised(host_ids, APPCACHE_PROGRESS_EVENT);
+
+ if (verify_progress_events_) {
+ EXPECT_GE(num_total, num_complete);
+ EXPECT_GE(num_complete, 0);
+
+ if (last_progress_total_ == -1) {
+ // Should start at zero.
+ EXPECT_EQ(0, num_complete);
+ } else {
+ // Total should be stable and complete should bump up by one at a time.
+ EXPECT_EQ(last_progress_total_, num_total);
+ EXPECT_EQ(last_progress_complete_ + 1, num_complete);
+ }
+
+ // Url should be valid for all except the 'final' event.
+ if (num_total == num_complete)
+ EXPECT_TRUE(url.is_empty());
+ else
+ EXPECT_TRUE(url.is_valid());
+
+ last_progress_total_ = num_total;
+ last_progress_complete_ = num_complete;
+ }
+ }
+
+ virtual void OnLogMessage(int host_id,
+ appcache::AppCacheLogLevel log_level,
+ const std::string& message) OVERRIDE {
+ }
+
+ virtual void OnContentBlocked(int host_id,
+ const GURL& manifest_url) OVERRIDE {
+ }
+
+ void AddExpectedEvent(const std::vector<int>& host_ids,
+ AppCacheEventID event_id) {
+ DCHECK(!ignore_progress_events_ || event_id != APPCACHE_PROGRESS_EVENT);
+ expected_events_.push_back(RaisedEvent(host_ids, event_id));
+ }
+
+ void SetIgnoreProgressEvents(bool ignore) {
+ // Some tests involve joining new hosts to an already running update job
+ // or intentionally failing. The timing and sequencing of the progress
+ // events generated by an update job are dependent on the behavior of
+ // an external HTTP server. For jobs that do not run fully till completion,
+ // due to either joining late or early exit, we skip monitoring the
+ // progress events to avoid flakiness.
+ ignore_progress_events_ = ignore;
+ }
+
+ void SetVerifyProgressEvents(bool verify) {
+ verify_progress_events_ = verify;
+ }
+
+ void TriggerAdditionalUpdates(AppCacheEventID trigger_event,
+ AppCacheUpdateJob* update) {
+ start_update_trigger_ = trigger_event;
+ update_ = update;
+ }
+
+ void AdditionalUpdateHost(AppCacheHost* host) {
+ update_hosts_.push_back(host);
+ }
+
+ typedef std::vector<int> HostIds;
+ typedef std::pair<HostIds, AppCacheEventID> RaisedEvent;
+ typedef std::vector<RaisedEvent> RaisedEvents;
+ RaisedEvents raised_events_;
+ std::string error_message_;
+
+ // Set the expected events if verification needs to happen asynchronously.
+ RaisedEvents expected_events_;
+ std::string expected_error_message_;
+
+ bool ignore_progress_events_;
+
+ bool verify_progress_events_;
+ int last_progress_total_;
+ int last_progress_complete_;
+
+ // Add ability for frontend to add master entries to an inprogress update.
+ AppCacheEventID start_update_trigger_;
+ AppCacheUpdateJob* update_;
+ std::vector<AppCacheHost*> update_hosts_;
+};
+
+// Helper factories to simulate redirected URL responses for tests.
+class RedirectFactory : public net::URLRequestJobFactory::ProtocolHandler {
+ public:
+ virtual net::URLRequestJob* MaybeCreateJob(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) const OVERRIDE {
+ return new net::URLRequestTestJob(
+ request,
+ network_delegate,
+ net::URLRequestTestJob::test_redirect_headers(),
+ net::URLRequestTestJob::test_data_1(),
+ true);
+ }
+};
+
+// Helper class to simulate a URL that returns retry or success.
+class RetryRequestTestJob : public net::URLRequestTestJob {
+ public:
+ enum RetryHeader {
+ NO_RETRY_AFTER,
+ NONZERO_RETRY_AFTER,
+ RETRY_AFTER_0,
+ };
+
+ static const GURL kRetryUrl;
+
+ // Call this at the start of each retry test.
+ static void Initialize(int num_retry_responses, RetryHeader header,
+ int expected_requests) {
+ num_requests_ = 0;
+ num_retries_ = num_retry_responses;
+ retry_after_ = header;
+ expected_requests_ = expected_requests;
+ }
+
+ // Verifies results at end of test and resets counters.
+ static void Verify() {
+ EXPECT_EQ(expected_requests_, num_requests_);
+ num_requests_ = 0;
+ expected_requests_ = 0;
+ }
+
+ static net::URLRequestJob* RetryFactory(
+ net::URLRequest* request, net::NetworkDelegate* network_delegate) {
+ ++num_requests_;
+ if (num_retries_ > 0 && request->original_url() == kRetryUrl) {
+ --num_retries_;
+ return new RetryRequestTestJob(
+ request, network_delegate, RetryRequestTestJob::retry_headers(), 503);
+ } else {
+ return new RetryRequestTestJob(
+ request,
+ network_delegate,
+ RetryRequestTestJob::manifest_headers(), 200);
+ }
+ }
+
+ virtual int GetResponseCode() const OVERRIDE { return response_code_; }
+
+ private:
+ virtual ~RetryRequestTestJob() {}
+
+ static std::string retry_headers() {
+ const char no_retry_after[] =
+ "HTTP/1.1 503 BOO HOO\0"
+ "\0";
+ const char nonzero[] =
+ "HTTP/1.1 503 BOO HOO\0"
+ "Retry-After: 60\0"
+ "\0";
+ const char retry_after_0[] =
+ "HTTP/1.1 503 BOO HOO\0"
+ "Retry-After: 0\0"
+ "\0";
+
+ switch (retry_after_) {
+ case NO_RETRY_AFTER:
+ return std::string(no_retry_after, arraysize(no_retry_after));
+ case NONZERO_RETRY_AFTER:
+ return std::string(nonzero, arraysize(nonzero));
+ case RETRY_AFTER_0:
+ default:
+ return std::string(retry_after_0, arraysize(retry_after_0));
+ }
+ }
+
+ static std::string manifest_headers() {
+ const char headers[] =
+ "HTTP/1.1 200 OK\0"
+ "Content-type: text/cache-manifest\0"
+ "\0";
+ return std::string(headers, arraysize(headers));
+ }
+
+ static std::string data() {
+ return std::string("CACHE MANIFEST\r"
+ "http://retry\r"); // must be same as kRetryUrl
+ }
+
+ RetryRequestTestJob(net::URLRequest* request,
+ net::NetworkDelegate* network_delegate,
+ const std::string& headers,
+ int response_code)
+ : net::URLRequestTestJob(
+ request, network_delegate, headers, data(), true),
+ response_code_(response_code) {
+ }
+
+ int response_code_;
+
+ static int num_requests_;
+ static int num_retries_;
+ static RetryHeader retry_after_;
+ static int expected_requests_;
+};
+
+class RetryRequestTestJobFactory
+ : public net::URLRequestJobFactory::ProtocolHandler {
+ public:
+ virtual net::URLRequestJob* MaybeCreateJob(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) const OVERRIDE {
+ return RetryRequestTestJob::RetryFactory(request, network_delegate);
+ }
+};
+
+// static
+const GURL RetryRequestTestJob::kRetryUrl("http://retry");
+int RetryRequestTestJob::num_requests_ = 0;
+int RetryRequestTestJob::num_retries_;
+RetryRequestTestJob::RetryHeader RetryRequestTestJob::retry_after_;
+int RetryRequestTestJob::expected_requests_ = 0;
+
+// Helper class to check for certain HTTP headers.
+class HttpHeadersRequestTestJob : public net::URLRequestTestJob {
+ public:
+ // Call this at the start of each HTTP header-related test.
+ static void Initialize(const std::string& expect_if_modified_since,
+ const std::string& expect_if_none_match) {
+ expect_if_modified_since_ = expect_if_modified_since;
+ expect_if_none_match_ = expect_if_none_match;
+ }
+
+ // Verifies results at end of test and resets class.
+ static void Verify() {
+ if (!expect_if_modified_since_.empty())
+ EXPECT_TRUE(saw_if_modified_since_);
+ if (!expect_if_none_match_.empty())
+ EXPECT_TRUE(saw_if_none_match_);
+
+ // Reset.
+ expect_if_modified_since_.clear();
+ saw_if_modified_since_ = false;
+ expect_if_none_match_.clear();
+ saw_if_none_match_ = false;
+ already_checked_ = false;
+ }
+
+ static net::URLRequestJob* IfModifiedSinceFactory(
+ net::URLRequest* request, net::NetworkDelegate* network_delegate) {
+ if (!already_checked_) {
+ already_checked_ = true; // only check once for a test
+ const net::HttpRequestHeaders& extra_headers =
+ request->extra_request_headers();
+ std::string header_value;
+ saw_if_modified_since_ =
+ extra_headers.GetHeader(
+ net::HttpRequestHeaders::kIfModifiedSince, &header_value) &&
+ header_value == expect_if_modified_since_;
+
+ saw_if_none_match_ =
+ extra_headers.GetHeader(
+ net::HttpRequestHeaders::kIfNoneMatch, &header_value) &&
+ header_value == expect_if_none_match_;
+ }
+ return MockHttpServer::JobFactory(request, network_delegate);
+ }
+
+ protected:
+ virtual ~HttpHeadersRequestTestJob() {}
+
+ private:
+ static std::string expect_if_modified_since_;
+ static bool saw_if_modified_since_;
+ static std::string expect_if_none_match_;
+ static bool saw_if_none_match_;
+ static bool already_checked_;
+};
+
+// static
+std::string HttpHeadersRequestTestJob::expect_if_modified_since_;
+bool HttpHeadersRequestTestJob::saw_if_modified_since_ = false;
+std::string HttpHeadersRequestTestJob::expect_if_none_match_;
+bool HttpHeadersRequestTestJob::saw_if_none_match_ = false;
+bool HttpHeadersRequestTestJob::already_checked_ = false;
+
+class IfModifiedSinceJobFactory
+ : public net::URLRequestJobFactory::ProtocolHandler {
+ public:
+ virtual net::URLRequestJob* MaybeCreateJob(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) const OVERRIDE {
+ return HttpHeadersRequestTestJob::IfModifiedSinceFactory(
+ request, network_delegate);
+ }
+};
+
+class IOThread : public base::Thread {
+ public:
+ explicit IOThread(const char* name)
+ : base::Thread(name) {
+ }
+
+ virtual ~IOThread() {
+ Stop();
+ }
+
+ net::URLRequestContext* request_context() {
+ return request_context_.get();
+ }
+
+ void SetNewJobFactory(net::URLRequestJobFactory* job_factory) {
+ DCHECK(job_factory);
+ job_factory_.reset(job_factory);
+ request_context_->set_job_factory(job_factory_.get());
+ }
+
+ virtual void Init() OVERRIDE {
+ scoped_ptr<net::URLRequestJobFactoryImpl> factory(
+ new net::URLRequestJobFactoryImpl());
+ factory->SetProtocolHandler("http", new MockHttpServerJobFactory);
+ factory->SetProtocolHandler("https", new MockHttpServerJobFactory);
+ job_factory_ = factory.Pass();
+ request_context_.reset(new net::TestURLRequestContext());
+ request_context_->set_job_factory(job_factory_.get());
+ }
+
+ virtual void CleanUp() OVERRIDE {
+ request_context_.reset();
+ job_factory_.reset();
+ }
+
+ private:
+ scoped_ptr<net::URLRequestJobFactory> job_factory_;
+ scoped_ptr<net::URLRequestContext> request_context_;
+};
+
+class AppCacheUpdateJobTest : public testing::Test,
+ public AppCacheGroup::UpdateObserver {
+ public:
+ AppCacheUpdateJobTest()
+ : do_checks_after_update_finished_(false),
+ expect_group_obsolete_(false),
+ expect_group_has_cache_(false),
+ expect_group_is_being_deleted_(false),
+ expect_old_cache_(NULL),
+ expect_newest_cache_(NULL),
+ expect_non_null_update_time_(false),
+ tested_manifest_(NONE),
+ tested_manifest_path_override_(NULL) {
+ io_thread_.reset(new IOThread("AppCacheUpdateJob IO test thread"));
+ base::Thread::Options options(base::MessageLoop::TYPE_IO, 0);
+ io_thread_->StartWithOptions(options);
+ }
+
+ // Use a separate IO thread to run a test. Thread will be destroyed
+ // when it goes out of scope.
+ template <class Method>
+ void RunTestOnIOThread(Method method) {
+ event_.reset(new base::WaitableEvent(false, false));
+ io_thread_->message_loop()->PostTask(
+ FROM_HERE, base::Bind(method, base::Unretained(this)));
+
+ // Wait until task is done before exiting the test.
+ event_->Wait();
+ }
+
+ void StartCacheAttemptTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ group_ = new AppCacheGroup(service_->storage(), GURL("http://failme"),
+ service_->storage()->NewGroupId());
+
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ MockFrontend mock_frontend;
+ AppCacheHost host(1, &mock_frontend, service_.get());
+
+ update->StartUpdate(&host, GURL());
+
+ // Verify state.
+ EXPECT_EQ(AppCacheUpdateJob::CACHE_ATTEMPT, update->update_type_);
+ EXPECT_EQ(AppCacheUpdateJob::FETCH_MANIFEST, update->internal_state_);
+ EXPECT_EQ(AppCacheGroup::CHECKING, group_->update_status());
+
+ // Verify notifications.
+ MockFrontend::RaisedEvents& events = mock_frontend.raised_events_;
+ size_t expected = 1;
+ EXPECT_EQ(expected, events.size());
+ EXPECT_EQ(expected, events[0].first.size());
+ EXPECT_EQ(host.host_id(), events[0].first[0]);
+ EXPECT_EQ(APPCACHE_CHECKING_EVENT, events[0].second);
+
+ // Abort as we're not testing actual URL fetches in this test.
+ delete update;
+ UpdateFinished();
+ }
+
+ void StartUpgradeAttemptTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ {
+ MakeService();
+ group_ = new AppCacheGroup(service_->storage(), GURL("http://failme"),
+ service_->storage()->NewGroupId());
+
+ // Give the group some existing caches.
+ AppCache* cache1 = MakeCacheForGroup(1, 111);
+ AppCache* cache2 = MakeCacheForGroup(2, 222);
+
+ // Associate some hosts with caches in the group.
+ MockFrontend mock_frontend1;
+ MockFrontend mock_frontend2;
+ MockFrontend mock_frontend3;
+
+ AppCacheHost host1(1, &mock_frontend1, service_.get());
+ host1.AssociateCompleteCache(cache1);
+
+ AppCacheHost host2(2, &mock_frontend2, service_.get());
+ host2.AssociateCompleteCache(cache2);
+
+ AppCacheHost host3(3, &mock_frontend1, service_.get());
+ host3.AssociateCompleteCache(cache1);
+
+ AppCacheHost host4(4, &mock_frontend3, service_.get());
+
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+ update->StartUpdate(&host4, GURL());
+
+ // Verify state after starting an update.
+ EXPECT_EQ(AppCacheUpdateJob::UPGRADE_ATTEMPT, update->update_type_);
+ EXPECT_EQ(AppCacheUpdateJob::FETCH_MANIFEST, update->internal_state_);
+ EXPECT_EQ(AppCacheGroup::CHECKING, group_->update_status());
+
+ // Verify notifications.
+ MockFrontend::RaisedEvents& events = mock_frontend1.raised_events_;
+ size_t expected = 1;
+ EXPECT_EQ(expected, events.size());
+ expected = 2; // 2 hosts using frontend1
+ EXPECT_EQ(expected, events[0].first.size());
+ MockFrontend::HostIds& host_ids = events[0].first;
+ EXPECT_TRUE(std::find(host_ids.begin(), host_ids.end(), host1.host_id())
+ != host_ids.end());
+ EXPECT_TRUE(std::find(host_ids.begin(), host_ids.end(), host3.host_id())
+ != host_ids.end());
+ EXPECT_EQ(APPCACHE_CHECKING_EVENT, events[0].second);
+
+ events = mock_frontend2.raised_events_;
+ expected = 1;
+ EXPECT_EQ(expected, events.size());
+ EXPECT_EQ(expected, events[0].first.size()); // 1 host using frontend2
+ EXPECT_EQ(host2.host_id(), events[0].first[0]);
+ EXPECT_EQ(APPCACHE_CHECKING_EVENT, events[0].second);
+
+ events = mock_frontend3.raised_events_;
+ EXPECT_TRUE(events.empty());
+
+ // Abort as we're not testing actual URL fetches in this test.
+ delete update;
+ }
+ UpdateFinished();
+ }
+
+ void CacheAttemptFetchManifestFailTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ group_ = new AppCacheGroup(service_->storage(), GURL("http://failme"),
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ MockFrontend* frontend = MakeMockFrontend();
+ AppCacheHost* host = MakeHost(1, frontend);
+ update->StartUpdate(host, GURL());
+ EXPECT_TRUE(update->manifest_fetcher_ != NULL);
+
+ update->manifest_fetcher_->request()->CancelWithError(-100);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = false;
+ frontend->AddExpectedEvent(MockFrontend::HostIds(1, host->host_id()),
+ APPCACHE_CHECKING_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void UpgradeFetchManifestFailTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ group_ = new AppCacheGroup(service_->storage(), GURL("http://failme"),
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ AppCache* cache = MakeCacheForGroup(1, 111);
+ MockFrontend* frontend1 = MakeMockFrontend();
+ MockFrontend* frontend2 = MakeMockFrontend();
+ AppCacheHost* host1 = MakeHost(1, frontend1);
+ AppCacheHost* host2 = MakeHost(2, frontend2);
+ host1->AssociateCompleteCache(cache);
+ host2->AssociateCompleteCache(cache);
+
+ update->StartUpdate(NULL, GURL());
+ EXPECT_TRUE(update->manifest_fetcher_ != NULL);
+
+ update->manifest_fetcher_->request()->CancelWithError(-100);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ expect_newest_cache_ = cache; // newest cache unaffected by update
+ MockFrontend::HostIds ids1(1, host1->host_id());
+ frontend1->AddExpectedEvent(ids1, APPCACHE_CHECKING_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_ERROR_EVENT);
+ MockFrontend::HostIds ids2(1, host2->host_id());
+ frontend2->AddExpectedEvent(ids2, APPCACHE_CHECKING_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_ERROR_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void ManifestRedirectTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ net::URLRequestJobFactoryImpl* new_factory(
+ new net::URLRequestJobFactoryImpl);
+ new_factory->SetProtocolHandler("http", new RedirectFactory);
+ io_thread_->SetNewJobFactory(new_factory);
+
+ MakeService();
+ group_ = new AppCacheGroup(service_->storage(), GURL("http://testme"),
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ MockFrontend* frontend = MakeMockFrontend();
+ AppCacheHost* host = MakeHost(1, frontend);
+ update->StartUpdate(host, GURL());
+ EXPECT_TRUE(update->manifest_fetcher_ != NULL);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = false; // redirect is like a failed request
+ frontend->AddExpectedEvent(MockFrontend::HostIds(1, host->host_id()),
+ APPCACHE_CHECKING_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void ManifestMissingMimeTypeTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_->storage(),
+ MockHttpServer::GetMockUrl("files/missing-mime-manifest"),
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), 33);
+ MockFrontend* frontend = MakeMockFrontend();
+ AppCacheHost* host = MakeHost(1, frontend);
+ host->AssociateCompleteCache(cache);
+
+ frontend->SetVerifyProgressEvents(true);
+
+ update->StartUpdate(NULL, GURL());
+ EXPECT_TRUE(update->manifest_fetcher_ != NULL);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ expect_old_cache_ = cache;
+ tested_manifest_ = EMPTY_MANIFEST;
+ tested_manifest_path_override_ = "files/missing-mime-manifest";
+ MockFrontend::HostIds ids(1, host->host_id());
+ frontend->AddExpectedEvent(ids, APPCACHE_CHECKING_EVENT);
+ frontend->AddExpectedEvent(ids, APPCACHE_DOWNLOADING_EVENT);
+ frontend->AddExpectedEvent(ids, APPCACHE_PROGRESS_EVENT); // final
+ frontend->AddExpectedEvent(ids, APPCACHE_UPDATE_READY_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void ManifestNotFoundTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_->storage(), MockHttpServer::GetMockUrl("files/nosuchfile"),
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ AppCache* cache = MakeCacheForGroup(1, 111);
+ MockFrontend* frontend1 = MakeMockFrontend();
+ MockFrontend* frontend2 = MakeMockFrontend();
+ AppCacheHost* host1 = MakeHost(1, frontend1);
+ AppCacheHost* host2 = MakeHost(2, frontend2);
+ host1->AssociateCompleteCache(cache);
+ host2->AssociateCompleteCache(cache);
+
+ update->StartUpdate(NULL, GURL());
+ EXPECT_TRUE(update->manifest_fetcher_ != NULL);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = true;
+ expect_group_has_cache_ = true;
+ expect_newest_cache_ = cache; // newest cache unaffected by update
+ MockFrontend::HostIds ids1(1, host1->host_id());
+ frontend1->AddExpectedEvent(ids1, APPCACHE_CHECKING_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_OBSOLETE_EVENT);
+ MockFrontend::HostIds ids2(1, host2->host_id());
+ frontend2->AddExpectedEvent(ids2, APPCACHE_CHECKING_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_OBSOLETE_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void ManifestGoneTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_->storage(), MockHttpServer::GetMockUrl("files/gone"),
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ MockFrontend* frontend = MakeMockFrontend();
+ AppCacheHost* host = MakeHost(1, frontend);
+ update->StartUpdate(host, GURL());
+ EXPECT_TRUE(update->manifest_fetcher_ != NULL);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = false;
+ frontend->AddExpectedEvent(MockFrontend::HostIds(1, host->host_id()),
+ APPCACHE_CHECKING_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void CacheAttemptNotModifiedTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_->storage(), MockHttpServer::GetMockUrl("files/notmodified"),
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ MockFrontend* frontend = MakeMockFrontend();
+ AppCacheHost* host = MakeHost(1, frontend);
+ update->StartUpdate(host, GURL());
+ EXPECT_TRUE(update->manifest_fetcher_ != NULL);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = false; // treated like cache failure
+ frontend->AddExpectedEvent(MockFrontend::HostIds(1, host->host_id()),
+ APPCACHE_CHECKING_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void UpgradeNotModifiedTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_->storage(), MockHttpServer::GetMockUrl("files/notmodified"),
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ AppCache* cache = MakeCacheForGroup(1, 111);
+ MockFrontend* frontend1 = MakeMockFrontend();
+ MockFrontend* frontend2 = MakeMockFrontend();
+ AppCacheHost* host1 = MakeHost(1, frontend1);
+ AppCacheHost* host2 = MakeHost(2, frontend2);
+ host1->AssociateCompleteCache(cache);
+ host2->AssociateCompleteCache(cache);
+
+ update->StartUpdate(NULL, GURL());
+ EXPECT_TRUE(update->manifest_fetcher_ != NULL);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ expect_newest_cache_ = cache; // newest cache unaffected by update
+ MockFrontend::HostIds ids1(1, host1->host_id());
+ frontend1->AddExpectedEvent(ids1, APPCACHE_CHECKING_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_NO_UPDATE_EVENT);
+ MockFrontend::HostIds ids2(1, host2->host_id());
+ frontend2->AddExpectedEvent(ids2, APPCACHE_CHECKING_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_NO_UPDATE_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void UpgradeManifestDataUnchangedTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_->storage(), MockHttpServer::GetMockUrl("files/manifest1"),
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ // Create response writer to get a response id.
+ response_writer_.reset(
+ service_->storage()->CreateResponseWriter(group_->manifest_url(),
+ group_->group_id()));
+
+ AppCache* cache = MakeCacheForGroup(1, response_writer_->response_id());
+ MockFrontend* frontend1 = MakeMockFrontend();
+ MockFrontend* frontend2 = MakeMockFrontend();
+ AppCacheHost* host1 = MakeHost(1, frontend1);
+ AppCacheHost* host2 = MakeHost(2, frontend2);
+ host1->AssociateCompleteCache(cache);
+ host2->AssociateCompleteCache(cache);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ expect_newest_cache_ = cache; // newest cache unaffected by update
+ MockFrontend::HostIds ids1(1, host1->host_id());
+ frontend1->AddExpectedEvent(ids1, APPCACHE_CHECKING_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_NO_UPDATE_EVENT);
+ MockFrontend::HostIds ids2(1, host2->host_id());
+ frontend2->AddExpectedEvent(ids2, APPCACHE_CHECKING_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_NO_UPDATE_EVENT);
+
+ // Seed storage with expected manifest data.
+ const std::string seed_data(kManifest1Contents);
+ scoped_refptr<net::StringIOBuffer> io_buffer(
+ new net::StringIOBuffer(seed_data));
+ response_writer_->WriteData(
+ io_buffer.get(),
+ seed_data.length(),
+ base::Bind(&AppCacheUpdateJobTest::StartUpdateAfterSeedingStorageData,
+ base::Unretained(this)));
+
+ // Start update after data write completes asynchronously.
+ }
+
+ // See http://code.google.com/p/chromium/issues/detail?id=95101
+ void Bug95101Test() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_->storage(), MockHttpServer::GetMockUrl("files/empty-manifest"),
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ // Create a malformed cache with a missing manifest entry.
+ GURL wrong_manifest_url =
+ MockHttpServer::GetMockUrl("files/missing-mime-manifest");
+ AppCache* cache = MakeCacheForGroup(1, wrong_manifest_url, 111);
+ MockFrontend* frontend = MakeMockFrontend();
+ AppCacheHost* host = MakeHost(1, frontend);
+ host->AssociateCompleteCache(cache);
+
+ update->StartUpdate(NULL, GURL());
+ EXPECT_TRUE(update->manifest_fetcher_ != NULL);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_is_being_deleted_ = true;
+ expect_group_has_cache_ = true;
+ expect_newest_cache_ = cache; // newest cache unaffected by update
+ MockFrontend::HostIds id(1, host->host_id());
+ frontend->AddExpectedEvent(id, APPCACHE_CHECKING_EVENT);
+ frontend->AddExpectedEvent(id, APPCACHE_ERROR_EVENT);
+ frontend->expected_error_message_ =
+ "Manifest entry not found in existing cache";
+ WaitForUpdateToFinish();
+ }
+
+ void StartUpdateAfterSeedingStorageData(int result) {
+ ASSERT_GT(result, 0);
+ response_writer_.reset();
+
+ AppCacheUpdateJob* update = group_->update_job_;
+ update->StartUpdate(NULL, GURL());
+ EXPECT_TRUE(update->manifest_fetcher_ != NULL);
+
+ WaitForUpdateToFinish();
+ }
+
+ void BasicCacheAttemptSuccessTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ GURL manifest_url = MockHttpServer::GetMockUrl("files/manifest1");
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_->storage(), manifest_url,
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ MockFrontend* frontend = MakeMockFrontend();
+ AppCacheHost* host = MakeHost(1, frontend);
+ update->StartUpdate(host, GURL());
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ tested_manifest_ = MANIFEST1;
+ frontend->AddExpectedEvent(MockFrontend::HostIds(1, host->host_id()),
+ APPCACHE_CHECKING_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void DownloadInterceptEntriesTest() {
+ // Ensures we download intercept entries too.
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+ GURL manifest_url =
+ MockHttpServer::GetMockUrl("files/manifest-with-intercept");
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_->storage(), manifest_url,
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ MockFrontend* frontend = MakeMockFrontend();
+ AppCacheHost* host = MakeHost(1, frontend);
+ update->StartUpdate(host, GURL());
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ tested_manifest_ = MANIFEST_WITH_INTERCEPT;
+ frontend->AddExpectedEvent(MockFrontend::HostIds(1, host->host_id()),
+ APPCACHE_CHECKING_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void BasicUpgradeSuccessTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_->storage(), MockHttpServer::GetMockUrl("files/manifest1"),
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ // Create a response writer to get a response id.
+ response_writer_.reset(
+ service_->storage()->CreateResponseWriter(group_->manifest_url(),
+ group_->group_id()));
+
+ AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(),
+ response_writer_->response_id());
+ MockFrontend* frontend1 = MakeMockFrontend();
+ MockFrontend* frontend2 = MakeMockFrontend();
+ AppCacheHost* host1 = MakeHost(1, frontend1);
+ AppCacheHost* host2 = MakeHost(2, frontend2);
+ host1->AssociateCompleteCache(cache);
+ host2->AssociateCompleteCache(cache);
+ frontend1->SetVerifyProgressEvents(true);
+ frontend2->SetVerifyProgressEvents(true);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ expect_old_cache_ = cache;
+ tested_manifest_ = MANIFEST1;
+ MockFrontend::HostIds ids1(1, host1->host_id());
+ frontend1->AddExpectedEvent(ids1, APPCACHE_CHECKING_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_DOWNLOADING_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT); // final
+ frontend1->AddExpectedEvent(ids1, APPCACHE_UPDATE_READY_EVENT);
+ MockFrontend::HostIds ids2(1, host2->host_id());
+ frontend2->AddExpectedEvent(ids2, APPCACHE_CHECKING_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_DOWNLOADING_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_PROGRESS_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_PROGRESS_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_PROGRESS_EVENT); // final
+ frontend2->AddExpectedEvent(ids2, APPCACHE_UPDATE_READY_EVENT);
+
+ // Seed storage with expected manifest data different from manifest1.
+ const std::string seed_data("different");
+ scoped_refptr<net::StringIOBuffer> io_buffer(
+ new net::StringIOBuffer(seed_data));
+ response_writer_->WriteData(
+ io_buffer.get(),
+ seed_data.length(),
+ base::Bind(&AppCacheUpdateJobTest::StartUpdateAfterSeedingStorageData,
+ base::Unretained(this)));
+
+ // Start update after data write completes asynchronously.
+ }
+
+ void UpgradeLoadFromNewestCacheTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_->storage(), MockHttpServer::GetMockUrl("files/manifest1"),
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), 42);
+ MockFrontend* frontend = MakeMockFrontend();
+ AppCacheHost* host = MakeHost(1, frontend);
+ host->AssociateCompleteCache(cache);
+
+ // Give the newest cache an entry that is in storage.
+ response_writer_.reset(
+ service_->storage()->CreateResponseWriter(group_->manifest_url(),
+ group_->group_id()));
+ cache->AddEntry(MockHttpServer::GetMockUrl("files/explicit1"),
+ AppCacheEntry(AppCacheEntry::EXPLICIT,
+ response_writer_->response_id()));
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ expect_old_cache_ = cache;
+ expect_response_ids_.insert(
+ std::map<GURL, int64>::value_type(
+ MockHttpServer::GetMockUrl("files/explicit1"),
+ response_writer_->response_id()));
+ tested_manifest_ = MANIFEST1;
+ MockFrontend::HostIds ids(1, host->host_id());
+ frontend->AddExpectedEvent(ids, APPCACHE_CHECKING_EVENT);
+ frontend->AddExpectedEvent(ids, APPCACHE_DOWNLOADING_EVENT);
+ frontend->AddExpectedEvent(ids, APPCACHE_PROGRESS_EVENT);
+ frontend->AddExpectedEvent(ids, APPCACHE_PROGRESS_EVENT);
+ frontend->AddExpectedEvent(ids, APPCACHE_PROGRESS_EVENT); // final
+ frontend->AddExpectedEvent(ids, APPCACHE_UPDATE_READY_EVENT);
+
+ // Seed storage with expected http response info for entry. Allow reuse.
+ const char data[] =
+ "HTTP/1.1 200 OK\0"
+ "Cache-Control: max-age=8675309\0"
+ "\0";
+ net::HttpResponseHeaders* headers =
+ new net::HttpResponseHeaders(std::string(data, arraysize(data)));
+ net::HttpResponseInfo* response_info = new net::HttpResponseInfo();
+ response_info->request_time = base::Time::Now();
+ response_info->response_time = base::Time::Now();
+ response_info->headers = headers; // adds ref to headers
+ scoped_refptr<HttpResponseInfoIOBuffer> io_buffer(
+ new HttpResponseInfoIOBuffer(response_info)); // adds ref to info
+ response_writer_->WriteInfo(
+ io_buffer.get(),
+ base::Bind(&AppCacheUpdateJobTest::StartUpdateAfterSeedingStorageData,
+ base::Unretained(this)));
+
+ // Start update after data write completes asynchronously.
+ }
+
+ void UpgradeNoLoadFromNewestCacheTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_->storage(), MockHttpServer::GetMockUrl("files/manifest1"),
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), 42);
+ MockFrontend* frontend = MakeMockFrontend();
+ AppCacheHost* host = MakeHost(1, frontend);
+ host->AssociateCompleteCache(cache);
+
+ // Give the newest cache an entry that is in storage.
+ response_writer_.reset(
+ service_->storage()->CreateResponseWriter(group_->manifest_url(),
+ group_->group_id()));
+ cache->AddEntry(MockHttpServer::GetMockUrl("files/explicit1"),
+ AppCacheEntry(AppCacheEntry::EXPLICIT,
+ response_writer_->response_id()));
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ expect_old_cache_ = cache;
+ tested_manifest_ = MANIFEST1;
+ MockFrontend::HostIds ids(1, host->host_id());
+ frontend->AddExpectedEvent(ids, APPCACHE_CHECKING_EVENT);
+ frontend->AddExpectedEvent(ids, APPCACHE_DOWNLOADING_EVENT);
+ frontend->AddExpectedEvent(ids, APPCACHE_PROGRESS_EVENT);
+ frontend->AddExpectedEvent(ids, APPCACHE_PROGRESS_EVENT);
+ frontend->AddExpectedEvent(ids, APPCACHE_PROGRESS_EVENT); // final
+ frontend->AddExpectedEvent(ids, APPCACHE_UPDATE_READY_EVENT);
+
+ // Seed storage with expected http response info for entry. Do NOT
+ // allow reuse by setting an expires header in the past.
+ const char data[] =
+ "HTTP/1.1 200 OK\0"
+ "Expires: Thu, 01 Dec 1994 16:00:00 GMT\0"
+ "\0";
+ net::HttpResponseHeaders* headers =
+ new net::HttpResponseHeaders(std::string(data, arraysize(data)));
+ net::HttpResponseInfo* response_info = new net::HttpResponseInfo();
+ response_info->request_time = base::Time::Now();
+ response_info->response_time = base::Time::Now();
+ response_info->headers = headers; // adds ref to headers
+ scoped_refptr<HttpResponseInfoIOBuffer> io_buffer(
+ new HttpResponseInfoIOBuffer(response_info)); // adds ref to info
+ response_writer_->WriteInfo(
+ io_buffer.get(),
+ base::Bind(&AppCacheUpdateJobTest::StartUpdateAfterSeedingStorageData,
+ base::Unretained(this)));
+
+ // Start update after data write completes asynchronously.
+ }
+
+ void UpgradeLoadFromNewestCacheVaryHeaderTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_->storage(), MockHttpServer::GetMockUrl("files/manifest1"),
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), 42);
+ MockFrontend* frontend = MakeMockFrontend();
+ AppCacheHost* host = MakeHost(1, frontend);
+ host->AssociateCompleteCache(cache);
+
+ // Give the newest cache an entry that is in storage.
+ response_writer_.reset(
+ service_->storage()->CreateResponseWriter(group_->manifest_url(),
+ group_->group_id()));
+ cache->AddEntry(MockHttpServer::GetMockUrl("files/explicit1"),
+ AppCacheEntry(AppCacheEntry::EXPLICIT,
+ response_writer_->response_id()));
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ expect_old_cache_ = cache;
+ tested_manifest_ = MANIFEST1;
+ MockFrontend::HostIds ids(1, host->host_id());
+ frontend->AddExpectedEvent(ids, APPCACHE_CHECKING_EVENT);
+ frontend->AddExpectedEvent(ids, APPCACHE_DOWNLOADING_EVENT);
+ frontend->AddExpectedEvent(ids, APPCACHE_PROGRESS_EVENT);
+ frontend->AddExpectedEvent(ids, APPCACHE_PROGRESS_EVENT);
+ frontend->AddExpectedEvent(ids, APPCACHE_PROGRESS_EVENT); // final
+ frontend->AddExpectedEvent(ids, APPCACHE_UPDATE_READY_EVENT);
+
+ // Seed storage with expected http response info for entry: a vary header.
+ const char data[] =
+ "HTTP/1.1 200 OK\0"
+ "Cache-Control: max-age=8675309\0"
+ "Vary: blah\0"
+ "\0";
+ net::HttpResponseHeaders* headers =
+ new net::HttpResponseHeaders(std::string(data, arraysize(data)));
+ net::HttpResponseInfo* response_info = new net::HttpResponseInfo();
+ response_info->request_time = base::Time::Now();
+ response_info->response_time = base::Time::Now();
+ response_info->headers = headers; // adds ref to headers
+ scoped_refptr<HttpResponseInfoIOBuffer> io_buffer(
+ new HttpResponseInfoIOBuffer(response_info)); // adds ref to info
+ response_writer_->WriteInfo(
+ io_buffer.get(),
+ base::Bind(&AppCacheUpdateJobTest::StartUpdateAfterSeedingStorageData,
+ base::Unretained(this)));
+
+ // Start update after data write completes asynchronously.
+ }
+
+ void UpgradeSuccessMergedTypesTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ group_ = new AppCacheGroup(service_->storage(),
+ MockHttpServer::GetMockUrl("files/manifest-merged-types"),
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), 42);
+ MockFrontend* frontend1 = MakeMockFrontend();
+ MockFrontend* frontend2 = MakeMockFrontend();
+ AppCacheHost* host1 = MakeHost(1, frontend1);
+ AppCacheHost* host2 = MakeHost(2, frontend2);
+ host1->AssociateCompleteCache(cache);
+ host2->AssociateCompleteCache(cache);
+
+ // Give the newest cache a master entry that is also one of the explicit
+ // entries in the manifest.
+ cache->AddEntry(MockHttpServer::GetMockUrl("files/explicit1"),
+ AppCacheEntry(AppCacheEntry::MASTER, 111));
+
+ update->StartUpdate(NULL, GURL());
+ EXPECT_TRUE(update->manifest_fetcher_ != NULL);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ expect_old_cache_ = cache;
+ tested_manifest_ = MANIFEST_MERGED_TYPES;
+ MockFrontend::HostIds ids1(1, host1->host_id());
+ frontend1->AddExpectedEvent(ids1, APPCACHE_CHECKING_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_DOWNLOADING_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT); // explicit1
+ frontend1->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT); // manifest
+ frontend1->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT); // final
+ frontend1->AddExpectedEvent(ids1, APPCACHE_UPDATE_READY_EVENT);
+ MockFrontend::HostIds ids2(1, host2->host_id());
+ frontend2->AddExpectedEvent(ids2, APPCACHE_CHECKING_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_DOWNLOADING_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_PROGRESS_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_PROGRESS_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_PROGRESS_EVENT); // final
+ frontend2->AddExpectedEvent(ids2, APPCACHE_UPDATE_READY_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void CacheAttemptFailUrlFetchTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ group_ = new AppCacheGroup(service_->storage(),
+ MockHttpServer::GetMockUrl("files/manifest-with-404"),
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ MockFrontend* frontend = MakeMockFrontend();
+ AppCacheHost* host = MakeHost(1, frontend);
+ update->StartUpdate(host, GURL());
+ EXPECT_TRUE(update->manifest_fetcher_ != NULL);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = false; // 404 explicit url is cache failure
+ frontend->AddExpectedEvent(MockFrontend::HostIds(1, host->host_id()),
+ APPCACHE_CHECKING_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void UpgradeFailUrlFetchTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ group_ = new AppCacheGroup(service_->storage(),
+ MockHttpServer::GetMockUrl("files/manifest-fb-404"),
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), 99);
+ MockFrontend* frontend1 = MakeMockFrontend();
+ MockFrontend* frontend2 = MakeMockFrontend();
+ frontend1->SetIgnoreProgressEvents(true);
+ frontend2->SetIgnoreProgressEvents(true);
+ AppCacheHost* host1 = MakeHost(1, frontend1);
+ AppCacheHost* host2 = MakeHost(2, frontend2);
+ host1->AssociateCompleteCache(cache);
+ host2->AssociateCompleteCache(cache);
+
+ update->StartUpdate(NULL, GURL());
+ EXPECT_TRUE(update->manifest_fetcher_ != NULL);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ expect_newest_cache_ = cache; // newest cache unaffectd by failed update
+ MockFrontend::HostIds ids1(1, host1->host_id());
+ frontend1->AddExpectedEvent(ids1, APPCACHE_CHECKING_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_DOWNLOADING_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_ERROR_EVENT);
+ MockFrontend::HostIds ids2(1, host2->host_id());
+ frontend2->AddExpectedEvent(ids2, APPCACHE_CHECKING_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_DOWNLOADING_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_ERROR_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void UpgradeFailMasterUrlFetchTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ tested_manifest_path_override_ = "files/manifest1-with-notmodified";
+
+ MakeService();
+ const GURL kManifestUrl =
+ MockHttpServer::GetMockUrl(tested_manifest_path_override_);
+ group_ = new AppCacheGroup(
+ service_->storage(), kManifestUrl,
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), 25);
+ MockFrontend* frontend1 = MakeMockFrontend();
+ MockFrontend* frontend2 = MakeMockFrontend();
+ AppCacheHost* host1 = MakeHost(1, frontend1);
+ AppCacheHost* host2 = MakeHost(2, frontend2);
+ host1->AssociateCompleteCache(cache);
+ host2->AssociateCompleteCache(cache);
+
+ // Give the newest cache some existing entries; one will fail with a 404.
+ cache->AddEntry(
+ MockHttpServer::GetMockUrl("files/notfound"),
+ AppCacheEntry(AppCacheEntry::MASTER, 222));
+ cache->AddEntry(
+ MockHttpServer::GetMockUrl("files/explicit2"),
+ AppCacheEntry(AppCacheEntry::MASTER | AppCacheEntry::FOREIGN, 333));
+ cache->AddEntry(
+ MockHttpServer::GetMockUrl("files/servererror"),
+ AppCacheEntry(AppCacheEntry::MASTER, 444));
+ cache->AddEntry(
+ MockHttpServer::GetMockUrl("files/notmodified"),
+ AppCacheEntry(AppCacheEntry::EXPLICIT, 555));
+
+ // Seed the response_info working set with canned data for
+ // files/servererror and for files/notmodified to test that the
+ // existing entries for those resource are reused by the update job.
+ const char kData[] =
+ "HTTP/1.1 200 OK\0"
+ "Last-Modified: Sat, 29 Oct 1994 19:43:31 GMT\0"
+ "\0";
+ const std::string kRawHeaders(kData, arraysize(kData));
+ MakeAppCacheResponseInfo(kManifestUrl, 444, kRawHeaders);
+ MakeAppCacheResponseInfo(kManifestUrl, 555, kRawHeaders);
+
+ update->StartUpdate(NULL, GURL());
+ EXPECT_TRUE(update->manifest_fetcher_ != NULL);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ expect_old_cache_ = cache;
+ tested_manifest_ = MANIFEST1;
+ expect_extra_entries_.insert(AppCache::EntryMap::value_type(
+ MockHttpServer::GetMockUrl("files/explicit2"),
+ AppCacheEntry(AppCacheEntry::MASTER))); // foreign flag is dropped
+ expect_extra_entries_.insert(AppCache::EntryMap::value_type(
+ MockHttpServer::GetMockUrl("files/servererror"),
+ AppCacheEntry(AppCacheEntry::MASTER)));
+ expect_extra_entries_.insert(AppCache::EntryMap::value_type(
+ MockHttpServer::GetMockUrl("files/notmodified"),
+ AppCacheEntry(AppCacheEntry::EXPLICIT)));
+ expect_response_ids_.insert(std::map<GURL, int64>::value_type(
+ MockHttpServer::GetMockUrl("files/servererror"), 444)); // copied
+ expect_response_ids_.insert(std::map<GURL, int64>::value_type(
+ MockHttpServer::GetMockUrl("files/notmodified"), 555)); // copied
+ MockFrontend::HostIds ids1(1, host1->host_id());
+ frontend1->AddExpectedEvent(ids1, APPCACHE_CHECKING_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_DOWNLOADING_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT); // explicit1
+ frontend1->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT); // fallback1a
+ frontend1->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT); // notfound
+ frontend1->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT); // explicit2
+ frontend1->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT); // servererror
+ frontend1->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT); // notmodified
+ frontend1->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT); // final
+ frontend1->AddExpectedEvent(ids1, APPCACHE_UPDATE_READY_EVENT);
+ MockFrontend::HostIds ids2(1, host2->host_id());
+ frontend2->AddExpectedEvent(ids2, APPCACHE_CHECKING_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_DOWNLOADING_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_PROGRESS_EVENT); // explicit1
+ frontend2->AddExpectedEvent(ids2, APPCACHE_PROGRESS_EVENT); // fallback1a
+ frontend2->AddExpectedEvent(ids2, APPCACHE_PROGRESS_EVENT); // notfound
+ frontend2->AddExpectedEvent(ids2, APPCACHE_PROGRESS_EVENT); // explicit2
+ frontend2->AddExpectedEvent(ids2, APPCACHE_PROGRESS_EVENT); // servererror
+ frontend2->AddExpectedEvent(ids2, APPCACHE_PROGRESS_EVENT); // notmodified
+ frontend2->AddExpectedEvent(ids2, APPCACHE_PROGRESS_EVENT); // final
+ frontend2->AddExpectedEvent(ids2, APPCACHE_UPDATE_READY_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void EmptyManifestTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_->storage(), MockHttpServer::GetMockUrl("files/empty-manifest"),
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), 33);
+ MockFrontend* frontend1 = MakeMockFrontend();
+ MockFrontend* frontend2 = MakeMockFrontend();
+ AppCacheHost* host1 = MakeHost(1, frontend1);
+ AppCacheHost* host2 = MakeHost(2, frontend2);
+ host1->AssociateCompleteCache(cache);
+ host2->AssociateCompleteCache(cache);
+
+ frontend1->SetVerifyProgressEvents(true);
+
+ update->StartUpdate(NULL, GURL());
+ EXPECT_TRUE(update->manifest_fetcher_ != NULL);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ expect_old_cache_ = cache;
+ tested_manifest_ = EMPTY_MANIFEST;
+ MockFrontend::HostIds ids1(1, host1->host_id());
+ frontend1->AddExpectedEvent(ids1, APPCACHE_CHECKING_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_DOWNLOADING_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT); // final
+ frontend1->AddExpectedEvent(ids1, APPCACHE_UPDATE_READY_EVENT);
+ MockFrontend::HostIds ids2(1, host2->host_id());
+ frontend2->AddExpectedEvent(ids2, APPCACHE_CHECKING_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_DOWNLOADING_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_PROGRESS_EVENT); // final
+ frontend2->AddExpectedEvent(ids2, APPCACHE_UPDATE_READY_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void EmptyFileTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ group_ = new AppCacheGroup(service_->storage(),
+ MockHttpServer::GetMockUrl("files/empty-file-manifest"),
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), 22);
+ MockFrontend* frontend = MakeMockFrontend();
+ AppCacheHost* host = MakeHost(1, frontend);
+ host->AssociateCompleteCache(cache);
+ frontend->SetVerifyProgressEvents(true);
+
+ update->StartUpdate(host, GURL());
+ EXPECT_TRUE(update->manifest_fetcher_ != NULL);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ tested_manifest_ = EMPTY_FILE_MANIFEST;
+ MockFrontend::HostIds ids1(1, host->host_id());
+ frontend->AddExpectedEvent(ids1, APPCACHE_CHECKING_EVENT);
+ frontend->AddExpectedEvent(ids1, APPCACHE_DOWNLOADING_EVENT);
+ frontend->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT);
+ frontend->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT); // final
+ frontend->AddExpectedEvent(ids1, APPCACHE_UPDATE_READY_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void RetryRequestTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ // Set some large number of times to return retry.
+ // Expect 1 manifest fetch and 3 retries.
+ RetryRequestTestJob::Initialize(5, RetryRequestTestJob::RETRY_AFTER_0, 4);
+ net::URLRequestJobFactoryImpl* new_factory(
+ new net::URLRequestJobFactoryImpl);
+ new_factory->SetProtocolHandler("http", new RetryRequestTestJobFactory);
+ io_thread_->SetNewJobFactory(new_factory);
+
+ MakeService();
+ group_ = new AppCacheGroup(service_->storage(),
+ RetryRequestTestJob::kRetryUrl,
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ MockFrontend* frontend = MakeMockFrontend();
+ AppCacheHost* host = MakeHost(1, frontend);
+ update->StartUpdate(host, GURL());
+ EXPECT_TRUE(update->manifest_fetcher_ != NULL);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = false;
+ frontend->AddExpectedEvent(MockFrontend::HostIds(1, host->host_id()),
+ APPCACHE_CHECKING_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void RetryNoRetryAfterTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ // Set some large number of times to return retry.
+ // Expect 1 manifest fetch and 0 retries.
+ RetryRequestTestJob::Initialize(5, RetryRequestTestJob::NO_RETRY_AFTER, 1);
+ net::URLRequestJobFactoryImpl* new_factory(
+ new net::URLRequestJobFactoryImpl);
+ new_factory->SetProtocolHandler("http", new RetryRequestTestJobFactory);
+ io_thread_->SetNewJobFactory(new_factory);
+
+ MakeService();
+ group_ = new AppCacheGroup(service_->storage(),
+ RetryRequestTestJob::kRetryUrl,
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ MockFrontend* frontend = MakeMockFrontend();
+ AppCacheHost* host = MakeHost(1, frontend);
+ update->StartUpdate(host, GURL());
+ EXPECT_TRUE(update->manifest_fetcher_ != NULL);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = false;
+ frontend->AddExpectedEvent(MockFrontend::HostIds(1, host->host_id()),
+ APPCACHE_CHECKING_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void RetryNonzeroRetryAfterTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ // Set some large number of times to return retry.
+ // Expect 1 request and 0 retry attempts.
+ RetryRequestTestJob::Initialize(
+ 5, RetryRequestTestJob::NONZERO_RETRY_AFTER, 1);
+ net::URLRequestJobFactoryImpl* new_factory(
+ new net::URLRequestJobFactoryImpl);
+ new_factory->SetProtocolHandler("http", new RetryRequestTestJobFactory);
+ io_thread_->SetNewJobFactory(new_factory);
+
+ MakeService();
+ group_ = new AppCacheGroup(service_->storage(),
+ RetryRequestTestJob::kRetryUrl,
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ MockFrontend* frontend = MakeMockFrontend();
+ AppCacheHost* host = MakeHost(1, frontend);
+ update->StartUpdate(host, GURL());
+ EXPECT_TRUE(update->manifest_fetcher_ != NULL);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = false;
+ frontend->AddExpectedEvent(MockFrontend::HostIds(1, host->host_id()),
+ APPCACHE_CHECKING_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void RetrySuccessTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ // Set 2 as the retry limit (does not exceed the max).
+ // Expect 1 manifest fetch, 2 retries, 1 url fetch, 1 manifest refetch.
+ RetryRequestTestJob::Initialize(2, RetryRequestTestJob::RETRY_AFTER_0, 5);
+ net::URLRequestJobFactoryImpl* new_factory(
+ new net::URLRequestJobFactoryImpl);
+ new_factory->SetProtocolHandler("http", new RetryRequestTestJobFactory);
+ io_thread_->SetNewJobFactory(new_factory);
+
+ MakeService();
+ group_ = new AppCacheGroup(service_->storage(),
+ RetryRequestTestJob::kRetryUrl,
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ MockFrontend* frontend = MakeMockFrontend();
+ AppCacheHost* host = MakeHost(1, frontend);
+ update->StartUpdate(host, GURL());
+ EXPECT_TRUE(update->manifest_fetcher_ != NULL);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ frontend->AddExpectedEvent(MockFrontend::HostIds(1, host->host_id()),
+ APPCACHE_CHECKING_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void RetryUrlTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ // Set 1 as the retry limit (does not exceed the max).
+ // Expect 1 manifest fetch, 1 url fetch, 1 url retry, 1 manifest refetch.
+ RetryRequestTestJob::Initialize(1, RetryRequestTestJob::RETRY_AFTER_0, 4);
+ net::URLRequestJobFactoryImpl* new_factory(
+ new net::URLRequestJobFactoryImpl);
+ new_factory->SetProtocolHandler("http", new RetryRequestTestJobFactory);
+ io_thread_->SetNewJobFactory(new_factory);
+
+ MakeService();
+ group_ = new AppCacheGroup(service_->storage(), GURL("http://retryurl"),
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ MockFrontend* frontend = MakeMockFrontend();
+ AppCacheHost* host = MakeHost(1, frontend);
+ update->StartUpdate(host, GURL());
+ EXPECT_TRUE(update->manifest_fetcher_ != NULL);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ frontend->AddExpectedEvent(MockFrontend::HostIds(1, host->host_id()),
+ APPCACHE_CHECKING_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void FailStoreNewestCacheTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ MockAppCacheStorage* storage =
+ reinterpret_cast<MockAppCacheStorage*>(service_->storage());
+ storage->SimulateStoreGroupAndNewestCacheFailure();
+
+ group_ = new AppCacheGroup(
+ service_->storage(), MockHttpServer::GetMockUrl("files/manifest1"),
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ MockFrontend* frontend = MakeMockFrontend();
+ AppCacheHost* host = MakeHost(1, frontend);
+ update->StartUpdate(host, GURL());
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = false; // storage failed
+ frontend->AddExpectedEvent(MockFrontend::HostIds(1, host->host_id()),
+ APPCACHE_CHECKING_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void UpgradeFailStoreNewestCacheTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ MockAppCacheStorage* storage =
+ reinterpret_cast<MockAppCacheStorage*>(service_->storage());
+ storage->SimulateStoreGroupAndNewestCacheFailure();
+
+ group_ = new AppCacheGroup(
+ service_->storage(), MockHttpServer::GetMockUrl("files/manifest1"),
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), 11);
+ MockFrontend* frontend1 = MakeMockFrontend();
+ MockFrontend* frontend2 = MakeMockFrontend();
+ AppCacheHost* host1 = MakeHost(1, frontend1);
+ AppCacheHost* host2 = MakeHost(2, frontend2);
+ host1->AssociateCompleteCache(cache);
+ host2->AssociateCompleteCache(cache);
+
+ update->StartUpdate(NULL, GURL());
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ expect_newest_cache_ = cache; // unchanged
+ MockFrontend::HostIds ids1(1, host1->host_id());
+ frontend1->AddExpectedEvent(ids1, APPCACHE_CHECKING_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_DOWNLOADING_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_ERROR_EVENT);
+ MockFrontend::HostIds ids2(1, host2->host_id());
+ frontend2->AddExpectedEvent(ids2, APPCACHE_CHECKING_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_DOWNLOADING_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_PROGRESS_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_PROGRESS_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_ERROR_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void MasterEntryFailStoreNewestCacheTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ MockAppCacheStorage* storage =
+ reinterpret_cast<MockAppCacheStorage*>(service_->storage());
+ storage->SimulateStoreGroupAndNewestCacheFailure();
+
+ const GURL kManifestUrl = MockHttpServer::GetMockUrl("files/notmodified");
+ const int64 kManifestResponseId = 11;
+
+ // Seed the response_info working set with canned data for
+ // files/servererror and for files/notmodified to test that the
+ // existing entries for those resource are reused by the update job.
+ const char kData[] =
+ "HTTP/1.1 200 OK\0"
+ "Content-type: text/cache-manifest\0"
+ "Last-Modified: Sat, 29 Oct 1994 19:43:31 GMT\0"
+ "\0";
+ const std::string kRawHeaders(kData, arraysize(kData));
+ MakeAppCacheResponseInfo(kManifestUrl, kManifestResponseId, kRawHeaders);
+
+ group_ = new AppCacheGroup(
+ service_->storage(), kManifestUrl,
+ service_->storage()->NewGroupId());
+ scoped_refptr<AppCache> cache(
+ MakeCacheForGroup(service_->storage()->NewCacheId(),
+ kManifestResponseId));
+
+ MockFrontend* frontend = MakeMockFrontend();
+ AppCacheHost* host = MakeHost(1, frontend);
+ host->first_party_url_ = kManifestUrl;
+ host->SelectCache(MockHttpServer::GetMockUrl("files/empty1"),
+ kAppCacheNoCacheId, kManifestUrl);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ tested_manifest_ = EMPTY_MANIFEST;
+ tested_manifest_path_override_ = "files/notmodified";
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ expect_newest_cache_ = cache.get(); // unchanged
+ MockFrontend::HostIds ids1(1, host->host_id());
+ frontend->AddExpectedEvent(ids1, APPCACHE_ERROR_EVENT);
+ frontend->expected_error_message_ =
+ "Failed to commit new cache to storage";
+
+ WaitForUpdateToFinish();
+ }
+
+ void UpgradeFailMakeGroupObsoleteTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ MockAppCacheStorage* storage =
+ reinterpret_cast<MockAppCacheStorage*>(service_->storage());
+ storage->SimulateMakeGroupObsoleteFailure();
+
+ group_ = new AppCacheGroup(
+ service_->storage(), MockHttpServer::GetMockUrl("files/nosuchfile"),
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ AppCache* cache = MakeCacheForGroup(1, 111);
+ MockFrontend* frontend1 = MakeMockFrontend();
+ MockFrontend* frontend2 = MakeMockFrontend();
+ AppCacheHost* host1 = MakeHost(1, frontend1);
+ AppCacheHost* host2 = MakeHost(2, frontend2);
+ host1->AssociateCompleteCache(cache);
+ host2->AssociateCompleteCache(cache);
+
+ update->StartUpdate(NULL, GURL());
+ EXPECT_TRUE(update->manifest_fetcher_ != NULL);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ expect_newest_cache_ = cache; // newest cache unaffected by update
+ MockFrontend::HostIds ids1(1, host1->host_id());
+ frontend1->AddExpectedEvent(ids1, APPCACHE_CHECKING_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_ERROR_EVENT);
+ MockFrontend::HostIds ids2(1, host2->host_id());
+ frontend2->AddExpectedEvent(ids2, APPCACHE_CHECKING_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_ERROR_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void MasterEntryFetchManifestFailTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ group_ = new AppCacheGroup(service_->storage(), GURL("http://failme"), 111);
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ MockFrontend* frontend = MakeMockFrontend();
+ AppCacheHost* host = MakeHost(1, frontend);
+ host->new_master_entry_url_ = GURL("http://failme/blah");
+ update->StartUpdate(host, host->new_master_entry_url_);
+ EXPECT_TRUE(update->manifest_fetcher_ != NULL);
+
+ update->manifest_fetcher_->request()->CancelWithError(-100);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = false;
+ MockFrontend::HostIds ids1(1, host->host_id());
+ frontend->AddExpectedEvent(ids1, APPCACHE_CHECKING_EVENT);
+ frontend->AddExpectedEvent(ids1, APPCACHE_ERROR_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void MasterEntryBadManifestTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ group_ = new AppCacheGroup(service_->storage(),
+ MockHttpServer::GetMockUrl("files/bad-manifest"), 111);
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ MockFrontend* frontend = MakeMockFrontend();
+ AppCacheHost* host = MakeHost(1, frontend);
+ host->new_master_entry_url_ = MockHttpServer::GetMockUrl("files/blah");
+ update->StartUpdate(host, host->new_master_entry_url_);
+ EXPECT_TRUE(update->manifest_fetcher_ != NULL);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = false;
+ MockFrontend::HostIds ids1(1, host->host_id());
+ frontend->AddExpectedEvent(ids1, APPCACHE_CHECKING_EVENT);
+ frontend->AddExpectedEvent(ids1, APPCACHE_ERROR_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void MasterEntryManifestNotFoundTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_->storage(),
+ MockHttpServer::GetMockUrl("files/nosuchfile"),
+ 111);
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ MockFrontend* frontend = MakeMockFrontend();
+ AppCacheHost* host = MakeHost(1, frontend);
+ host->new_master_entry_url_ = MockHttpServer::GetMockUrl("files/blah");
+
+ update->StartUpdate(host, host->new_master_entry_url_);
+ EXPECT_TRUE(update->manifest_fetcher_ != NULL);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = false;
+ MockFrontend::HostIds ids1(1, host->host_id());
+ frontend->AddExpectedEvent(ids1, APPCACHE_CHECKING_EVENT);
+ frontend->AddExpectedEvent(ids1, APPCACHE_ERROR_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void MasterEntryFailUrlFetchTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ group_ = new AppCacheGroup(service_->storage(),
+ MockHttpServer::GetMockUrl("files/manifest-fb-404"), 111);
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ MockFrontend* frontend = MakeMockFrontend();
+ frontend->SetIgnoreProgressEvents(true);
+ AppCacheHost* host = MakeHost(1, frontend);
+ host->new_master_entry_url_ =
+ MockHttpServer::GetMockUrl("files/explicit1");
+
+ update->StartUpdate(host, host->new_master_entry_url_);
+ EXPECT_TRUE(update->manifest_fetcher_ != NULL);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = false; // 404 fallback url is cache failure
+ MockFrontend::HostIds ids1(1, host->host_id());
+ frontend->AddExpectedEvent(ids1, APPCACHE_CHECKING_EVENT);
+ frontend->AddExpectedEvent(ids1, APPCACHE_DOWNLOADING_EVENT);
+ frontend->AddExpectedEvent(ids1, APPCACHE_ERROR_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void MasterEntryAllFailTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_->storage(),
+ MockHttpServer::GetMockUrl("files/manifest1"),
+ 111);
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ MockFrontend* frontend1 = MakeMockFrontend();
+ frontend1->SetIgnoreProgressEvents(true);
+ AppCacheHost* host1 = MakeHost(1, frontend1);
+ host1->new_master_entry_url_ =
+ MockHttpServer::GetMockUrl("files/nosuchfile");
+ update->StartUpdate(host1, host1->new_master_entry_url_);
+
+ MockFrontend* frontend2 = MakeMockFrontend();
+ frontend2->SetIgnoreProgressEvents(true);
+ AppCacheHost* host2 = MakeHost(2, frontend2);
+ host2->new_master_entry_url_ =
+ MockHttpServer::GetMockUrl("files/servererror");
+ update->StartUpdate(host2, host2->new_master_entry_url_);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = false; // all pending masters failed
+ MockFrontend::HostIds ids1(1, host1->host_id());
+ frontend1->AddExpectedEvent(ids1, APPCACHE_CHECKING_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_DOWNLOADING_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_ERROR_EVENT);
+ MockFrontend::HostIds ids2(1, host2->host_id());
+ frontend2->AddExpectedEvent(ids2, APPCACHE_CHECKING_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_DOWNLOADING_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_ERROR_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void UpgradeMasterEntryAllFailTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_->storage(),
+ MockHttpServer::GetMockUrl("files/manifest1"),
+ 111);
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), 42);
+ MockFrontend* frontend1 = MakeMockFrontend();
+ AppCacheHost* host1 = MakeHost(1, frontend1);
+ host1->AssociateCompleteCache(cache);
+
+ MockFrontend* frontend2 = MakeMockFrontend();
+ frontend2->SetIgnoreProgressEvents(true);
+ AppCacheHost* host2 = MakeHost(2, frontend2);
+ host2->new_master_entry_url_ =
+ MockHttpServer::GetMockUrl("files/nosuchfile");
+ update->StartUpdate(host2, host2->new_master_entry_url_);
+
+ MockFrontend* frontend3 = MakeMockFrontend();
+ frontend3->SetIgnoreProgressEvents(true);
+ AppCacheHost* host3 = MakeHost(3, frontend3);
+ host3->new_master_entry_url_ =
+ MockHttpServer::GetMockUrl("files/servererror");
+ update->StartUpdate(host3, host3->new_master_entry_url_);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ expect_old_cache_ = cache;
+ tested_manifest_ = MANIFEST1;
+ MockFrontend::HostIds ids1(1, host1->host_id());
+ frontend1->AddExpectedEvent(ids1, APPCACHE_CHECKING_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_DOWNLOADING_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT); // final
+ frontend1->AddExpectedEvent(ids1, APPCACHE_UPDATE_READY_EVENT);
+ MockFrontend::HostIds ids2(1, host2->host_id());
+ frontend2->AddExpectedEvent(ids2, APPCACHE_DOWNLOADING_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_ERROR_EVENT);
+ MockFrontend::HostIds ids3(1, host3->host_id());
+ frontend3->AddExpectedEvent(ids3, APPCACHE_CHECKING_EVENT);
+ frontend3->AddExpectedEvent(ids3, APPCACHE_DOWNLOADING_EVENT);
+ frontend3->AddExpectedEvent(ids3, APPCACHE_ERROR_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void MasterEntrySomeFailTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_->storage(),
+ MockHttpServer::GetMockUrl("files/manifest1"),
+ 111);
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ MockFrontend* frontend1 = MakeMockFrontend();
+ frontend1->SetIgnoreProgressEvents(true);
+ AppCacheHost* host1 = MakeHost(1, frontend1);
+ host1->new_master_entry_url_ =
+ MockHttpServer::GetMockUrl("files/nosuchfile");
+ update->StartUpdate(host1, host1->new_master_entry_url_);
+
+ MockFrontend* frontend2 = MakeMockFrontend();
+ AppCacheHost* host2 = MakeHost(2, frontend2);
+ host2->new_master_entry_url_ =
+ MockHttpServer::GetMockUrl("files/explicit2");
+ update->StartUpdate(host2, host2->new_master_entry_url_);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true; // as long as one pending master succeeds
+ tested_manifest_ = MANIFEST1;
+ expect_extra_entries_.insert(AppCache::EntryMap::value_type(
+ MockHttpServer::GetMockUrl("files/explicit2"),
+ AppCacheEntry(AppCacheEntry::MASTER)));
+ MockFrontend::HostIds ids1(1, host1->host_id());
+ frontend1->AddExpectedEvent(ids1, APPCACHE_CHECKING_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_DOWNLOADING_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_ERROR_EVENT);
+ MockFrontend::HostIds ids2(1, host2->host_id());
+ frontend2->AddExpectedEvent(ids2, APPCACHE_CHECKING_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_DOWNLOADING_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_PROGRESS_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_PROGRESS_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_PROGRESS_EVENT); // final
+ frontend2->AddExpectedEvent(ids2, APPCACHE_CACHED_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void UpgradeMasterEntrySomeFailTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_->storage(),
+ MockHttpServer::GetMockUrl("files/manifest1"),
+ 111);
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), 42);
+ MockFrontend* frontend1 = MakeMockFrontend();
+ AppCacheHost* host1 = MakeHost(1, frontend1);
+ host1->AssociateCompleteCache(cache);
+
+ MockFrontend* frontend2 = MakeMockFrontend();
+ frontend2->SetIgnoreProgressEvents(true);
+ AppCacheHost* host2 = MakeHost(2, frontend2);
+ host2->new_master_entry_url_ =
+ MockHttpServer::GetMockUrl("files/nosuchfile");
+ update->StartUpdate(host2, host2->new_master_entry_url_);
+
+ MockFrontend* frontend3 = MakeMockFrontend();
+ AppCacheHost* host3 = MakeHost(3, frontend3);
+ host3->new_master_entry_url_ =
+ MockHttpServer::GetMockUrl("files/explicit2");
+ update->StartUpdate(host3, host3->new_master_entry_url_);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ expect_old_cache_ = cache;
+ tested_manifest_ = MANIFEST1;
+ expect_extra_entries_.insert(AppCache::EntryMap::value_type(
+ MockHttpServer::GetMockUrl("files/explicit2"),
+ AppCacheEntry(AppCacheEntry::MASTER)));
+ MockFrontend::HostIds ids1(1, host1->host_id());
+ frontend1->AddExpectedEvent(ids1, APPCACHE_CHECKING_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_DOWNLOADING_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT); // final
+ frontend1->AddExpectedEvent(ids1, APPCACHE_UPDATE_READY_EVENT);
+ MockFrontend::HostIds ids2(1, host2->host_id());
+ frontend2->AddExpectedEvent(ids2, APPCACHE_DOWNLOADING_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_ERROR_EVENT);
+ MockFrontend::HostIds ids3(1, host3->host_id());
+ frontend3->AddExpectedEvent(ids3, APPCACHE_CHECKING_EVENT);
+ frontend3->AddExpectedEvent(ids3, APPCACHE_DOWNLOADING_EVENT);
+ frontend3->AddExpectedEvent(ids3, APPCACHE_PROGRESS_EVENT);
+ frontend3->AddExpectedEvent(ids3, APPCACHE_PROGRESS_EVENT);
+ frontend3->AddExpectedEvent(ids3, APPCACHE_PROGRESS_EVENT); // final
+ frontend3->AddExpectedEvent(ids3, APPCACHE_UPDATE_READY_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void MasterEntryNoUpdateTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ group_ = new AppCacheGroup(service_->storage(),
+ MockHttpServer::GetMockUrl("files/notmodified"), 111);
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ AppCache* cache = MakeCacheForGroup(1, 111);
+ MockFrontend* frontend1 = MakeMockFrontend();
+ AppCacheHost* host1 = MakeHost(1, frontend1);
+ host1->AssociateCompleteCache(cache);
+
+ // Give cache an existing entry that can also be fetched.
+ cache->AddEntry(MockHttpServer::GetMockUrl("files/explicit2"),
+ AppCacheEntry(AppCacheEntry::EXPLICIT, 222));
+
+ // Reset the update time to null so we can verify it gets
+ // modified in this test case by the UpdateJob.
+ cache->set_update_time(base::Time());
+
+ MockFrontend* frontend2 = MakeMockFrontend();
+ AppCacheHost* host2 = MakeHost(2, frontend2);
+ host2->new_master_entry_url_ =
+ MockHttpServer::GetMockUrl("files/explicit1");
+ update->StartUpdate(host2, host2->new_master_entry_url_);
+
+ AppCacheHost* host3 = MakeHost(3, frontend2); // same frontend as host2
+ host3->new_master_entry_url_ =
+ MockHttpServer::GetMockUrl("files/explicit2");
+ update->StartUpdate(host3, host3->new_master_entry_url_);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ expect_newest_cache_ = cache; // newest cache still the same cache
+ expect_non_null_update_time_ = true;
+ tested_manifest_ = PENDING_MASTER_NO_UPDATE;
+ MockFrontend::HostIds ids1(1, host1->host_id());
+ frontend1->AddExpectedEvent(ids1, APPCACHE_CHECKING_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_NO_UPDATE_EVENT);
+ MockFrontend::HostIds ids3(1, host3->host_id());
+ frontend2->AddExpectedEvent(ids3, APPCACHE_CHECKING_EVENT);
+ MockFrontend::HostIds ids2and3;
+ ids2and3.push_back(host2->host_id());
+ ids2and3.push_back(host3->host_id());
+ frontend2->AddExpectedEvent(ids2and3, APPCACHE_NO_UPDATE_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void StartUpdateMidCacheAttemptTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_->storage(), MockHttpServer::GetMockUrl("files/manifest1"),
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ MockFrontend* frontend1 = MakeMockFrontend();
+ AppCacheHost* host1 = MakeHost(1, frontend1);
+ host1->new_master_entry_url_ =
+ MockHttpServer::GetMockUrl("files/explicit2");
+ update->StartUpdate(host1, host1->new_master_entry_url_);
+ EXPECT_TRUE(update->manifest_fetcher_ != NULL);
+
+ // Set up additional updates to be started while update is in progress.
+ MockFrontend* frontend2 = MakeMockFrontend();
+ frontend2->SetIgnoreProgressEvents(true);
+ AppCacheHost* host2 = MakeHost(2, frontend2);
+ host2->new_master_entry_url_ =
+ MockHttpServer::GetMockUrl("files/nosuchfile");
+
+ MockFrontend* frontend3 = MakeMockFrontend();
+ AppCacheHost* host3 = MakeHost(3, frontend3);
+ host3->new_master_entry_url_ =
+ MockHttpServer::GetMockUrl("files/explicit1");
+
+ MockFrontend* frontend4 = MakeMockFrontend();
+ AppCacheHost* host4 = MakeHost(4, frontend4);
+ host4->new_master_entry_url_ =
+ MockHttpServer::GetMockUrl("files/explicit2");
+
+ MockFrontend* frontend5 = MakeMockFrontend();
+ AppCacheHost* host5 = MakeHost(5, frontend5); // no master entry url
+
+ frontend1->TriggerAdditionalUpdates(APPCACHE_DOWNLOADING_EVENT, update);
+ frontend1->AdditionalUpdateHost(host2); // fetch will fail
+ frontend1->AdditionalUpdateHost(host3); // same as an explicit entry
+ frontend1->AdditionalUpdateHost(host4); // same as another master entry
+ frontend1->AdditionalUpdateHost(NULL); // no host
+ frontend1->AdditionalUpdateHost(host5); // no master entry url
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ tested_manifest_ = MANIFEST1;
+ expect_extra_entries_.insert(AppCache::EntryMap::value_type(
+ MockHttpServer::GetMockUrl("files/explicit2"),
+ AppCacheEntry(AppCacheEntry::MASTER)));
+ MockFrontend::HostIds ids1(1, host1->host_id());
+ frontend1->AddExpectedEvent(ids1, APPCACHE_CHECKING_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_DOWNLOADING_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT); // final
+ frontend1->AddExpectedEvent(ids1, APPCACHE_CACHED_EVENT);
+ MockFrontend::HostIds ids2(1, host2->host_id());
+ frontend2->AddExpectedEvent(ids2, APPCACHE_CHECKING_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_DOWNLOADING_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_ERROR_EVENT);
+ MockFrontend::HostIds ids3(1, host3->host_id());
+ frontend3->AddExpectedEvent(ids3, APPCACHE_CHECKING_EVENT);
+ frontend3->AddExpectedEvent(ids3, APPCACHE_DOWNLOADING_EVENT);
+ frontend3->AddExpectedEvent(ids3, APPCACHE_PROGRESS_EVENT);
+ frontend3->AddExpectedEvent(ids3, APPCACHE_PROGRESS_EVENT);
+ frontend3->AddExpectedEvent(ids3, APPCACHE_PROGRESS_EVENT); // final
+ frontend3->AddExpectedEvent(ids3, APPCACHE_CACHED_EVENT);
+ MockFrontend::HostIds ids4(1, host4->host_id());
+ frontend4->AddExpectedEvent(ids4, APPCACHE_CHECKING_EVENT);
+ frontend4->AddExpectedEvent(ids4, APPCACHE_DOWNLOADING_EVENT);
+ frontend4->AddExpectedEvent(ids4, APPCACHE_PROGRESS_EVENT);
+ frontend4->AddExpectedEvent(ids4, APPCACHE_PROGRESS_EVENT);
+ frontend4->AddExpectedEvent(ids4, APPCACHE_PROGRESS_EVENT); // final
+ frontend4->AddExpectedEvent(ids4, APPCACHE_CACHED_EVENT);
+
+ // Host 5 is not associated with cache so no progress/cached events.
+ MockFrontend::HostIds ids5(1, host5->host_id());
+ frontend5->AddExpectedEvent(ids5, APPCACHE_CHECKING_EVENT);
+ frontend5->AddExpectedEvent(ids5, APPCACHE_DOWNLOADING_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void StartUpdateMidNoUpdateTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_->storage(), MockHttpServer::GetMockUrl("files/notmodified"),
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ AppCache* cache = MakeCacheForGroup(1, 111);
+ MockFrontend* frontend1 = MakeMockFrontend();
+ AppCacheHost* host1 = MakeHost(1, frontend1);
+ host1->AssociateCompleteCache(cache);
+
+ // Give cache an existing entry.
+ cache->AddEntry(MockHttpServer::GetMockUrl("files/explicit2"),
+ AppCacheEntry(AppCacheEntry::EXPLICIT, 222));
+
+ // Start update with a pending master entry that will fail to give us an
+ // event to trigger other updates.
+ MockFrontend* frontend2 = MakeMockFrontend();
+ AppCacheHost* host2 = MakeHost(2, frontend2);
+ host2->new_master_entry_url_ =
+ MockHttpServer::GetMockUrl("files/nosuchfile");
+ update->StartUpdate(host2, host2->new_master_entry_url_);
+ EXPECT_TRUE(update->manifest_fetcher_ != NULL);
+
+ // Set up additional updates to be started while update is in progress.
+ MockFrontend* frontend3 = MakeMockFrontend();
+ AppCacheHost* host3 = MakeHost(3, frontend3);
+ host3->new_master_entry_url_ =
+ MockHttpServer::GetMockUrl("files/explicit1");
+
+ MockFrontend* frontend4 = MakeMockFrontend();
+ AppCacheHost* host4 = MakeHost(4, frontend4); // no master entry url
+
+ MockFrontend* frontend5 = MakeMockFrontend();
+ AppCacheHost* host5 = MakeHost(5, frontend5);
+ host5->new_master_entry_url_ =
+ MockHttpServer::GetMockUrl("files/explicit2"); // existing entry
+
+ MockFrontend* frontend6 = MakeMockFrontend();
+ AppCacheHost* host6 = MakeHost(6, frontend6);
+ host6->new_master_entry_url_ =
+ MockHttpServer::GetMockUrl("files/explicit1");
+
+ frontend2->TriggerAdditionalUpdates(APPCACHE_ERROR_EVENT, update);
+ frontend2->AdditionalUpdateHost(host3);
+ frontend2->AdditionalUpdateHost(NULL); // no host
+ frontend2->AdditionalUpdateHost(host4); // no master entry url
+ frontend2->AdditionalUpdateHost(host5); // same as existing cache entry
+ frontend2->AdditionalUpdateHost(host6); // same as another master entry
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ expect_newest_cache_ = cache; // newest cache unaffected by update
+ tested_manifest_ = PENDING_MASTER_NO_UPDATE;
+ MockFrontend::HostIds ids1(1, host1->host_id()); // prior associated host
+ frontend1->AddExpectedEvent(ids1, APPCACHE_CHECKING_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_NO_UPDATE_EVENT);
+ MockFrontend::HostIds ids2(1, host2->host_id());
+ frontend2->AddExpectedEvent(ids2, APPCACHE_ERROR_EVENT);
+ MockFrontend::HostIds ids3(1, host3->host_id());
+ frontend3->AddExpectedEvent(ids3, APPCACHE_CHECKING_EVENT);
+ frontend3->AddExpectedEvent(ids3, APPCACHE_NO_UPDATE_EVENT);
+ MockFrontend::HostIds ids4(1, host4->host_id()); // unassociated w/cache
+ frontend4->AddExpectedEvent(ids4, APPCACHE_CHECKING_EVENT);
+ MockFrontend::HostIds ids5(1, host5->host_id());
+ frontend5->AddExpectedEvent(ids5, APPCACHE_CHECKING_EVENT);
+ frontend5->AddExpectedEvent(ids5, APPCACHE_NO_UPDATE_EVENT);
+ MockFrontend::HostIds ids6(1, host6->host_id());
+ frontend6->AddExpectedEvent(ids6, APPCACHE_CHECKING_EVENT);
+ frontend6->AddExpectedEvent(ids6, APPCACHE_NO_UPDATE_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void StartUpdateMidDownloadTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_->storage(),
+ MockHttpServer::GetMockUrl("files/manifest1"),
+ 111);
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), 42);
+ MockFrontend* frontend1 = MakeMockFrontend();
+ AppCacheHost* host1 = MakeHost(1, frontend1);
+ host1->AssociateCompleteCache(cache);
+
+ update->StartUpdate(NULL, GURL());
+
+ // Set up additional updates to be started while update is in progress.
+ MockFrontend* frontend2 = MakeMockFrontend();
+ AppCacheHost* host2 = MakeHost(2, frontend2);
+ host2->new_master_entry_url_ =
+ MockHttpServer::GetMockUrl("files/explicit1");
+
+ MockFrontend* frontend3 = MakeMockFrontend();
+ AppCacheHost* host3 = MakeHost(3, frontend3);
+ host3->new_master_entry_url_ =
+ MockHttpServer::GetMockUrl("files/explicit2");
+
+ MockFrontend* frontend4 = MakeMockFrontend();
+ AppCacheHost* host4 = MakeHost(4, frontend4); // no master entry url
+
+ MockFrontend* frontend5 = MakeMockFrontend();
+ AppCacheHost* host5 = MakeHost(5, frontend5);
+ host5->new_master_entry_url_ =
+ MockHttpServer::GetMockUrl("files/explicit2");
+
+ frontend1->TriggerAdditionalUpdates(APPCACHE_PROGRESS_EVENT, update);
+ frontend1->AdditionalUpdateHost(host2); // same as entry in manifest
+ frontend1->AdditionalUpdateHost(NULL); // no host
+ frontend1->AdditionalUpdateHost(host3); // new master entry
+ frontend1->AdditionalUpdateHost(host4); // no master entry url
+ frontend1->AdditionalUpdateHost(host5); // same as another master entry
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ tested_manifest_ = MANIFEST1;
+ expect_extra_entries_.insert(AppCache::EntryMap::value_type(
+ MockHttpServer::GetMockUrl("files/explicit2"),
+ AppCacheEntry(AppCacheEntry::MASTER)));
+ MockFrontend::HostIds ids1(1, host1->host_id()); // prior associated host
+ frontend1->AddExpectedEvent(ids1, APPCACHE_CHECKING_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_DOWNLOADING_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT);
+ frontend1->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT); // final
+ frontend1->AddExpectedEvent(ids1, APPCACHE_UPDATE_READY_EVENT);
+ MockFrontend::HostIds ids2(1, host2->host_id());
+ frontend2->AddExpectedEvent(ids2, APPCACHE_CHECKING_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_DOWNLOADING_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_PROGRESS_EVENT);
+ frontend2->AddExpectedEvent(ids2, APPCACHE_PROGRESS_EVENT); // final
+ frontend2->AddExpectedEvent(ids2, APPCACHE_UPDATE_READY_EVENT);
+ MockFrontend::HostIds ids3(1, host3->host_id());
+ frontend3->AddExpectedEvent(ids3, APPCACHE_CHECKING_EVENT);
+ frontend3->AddExpectedEvent(ids3, APPCACHE_DOWNLOADING_EVENT);
+ frontend3->AddExpectedEvent(ids3, APPCACHE_PROGRESS_EVENT);
+ frontend3->AddExpectedEvent(ids3, APPCACHE_PROGRESS_EVENT); // final
+ frontend3->AddExpectedEvent(ids3, APPCACHE_UPDATE_READY_EVENT);
+ MockFrontend::HostIds ids4(1, host4->host_id()); // unassociated w/cache
+ frontend4->AddExpectedEvent(ids4, APPCACHE_CHECKING_EVENT);
+ frontend4->AddExpectedEvent(ids4, APPCACHE_DOWNLOADING_EVENT);
+ MockFrontend::HostIds ids5(1, host5->host_id());
+ frontend5->AddExpectedEvent(ids5, APPCACHE_CHECKING_EVENT);
+ frontend5->AddExpectedEvent(ids5, APPCACHE_DOWNLOADING_EVENT);
+ frontend5->AddExpectedEvent(ids5, APPCACHE_PROGRESS_EVENT);
+ frontend5->AddExpectedEvent(ids5, APPCACHE_PROGRESS_EVENT); // final
+ frontend5->AddExpectedEvent(ids5, APPCACHE_UPDATE_READY_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void QueueMasterEntryTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_->storage(),
+ MockHttpServer::GetMockUrl("files/manifest1"),
+ 111);
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ // Pretend update job has been running and is about to terminate.
+ group_->update_status_ = AppCacheGroup::DOWNLOADING;
+ update->internal_state_ = AppCacheUpdateJob::REFETCH_MANIFEST;
+ EXPECT_TRUE(update->IsTerminating());
+
+ // Start an update. Should be queued.
+ MockFrontend* frontend = MakeMockFrontend();
+ AppCacheHost* host = MakeHost(1, frontend);
+ host->new_master_entry_url_ =
+ MockHttpServer::GetMockUrl("files/explicit2");
+ update->StartUpdate(host, host->new_master_entry_url_);
+ EXPECT_TRUE(update->pending_master_entries_.empty());
+ EXPECT_FALSE(group_->queued_updates_.empty());
+
+ // Delete update, causing it to finish, which should trigger a new update
+ // for the queued host and master entry after a delay.
+ delete update;
+ EXPECT_FALSE(group_->restart_update_task_.IsCancelled());
+
+ // Set up checks for when queued update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ tested_manifest_ = MANIFEST1;
+ expect_extra_entries_.insert(AppCache::EntryMap::value_type(
+ host->new_master_entry_url_, AppCacheEntry(AppCacheEntry::MASTER)));
+ MockFrontend::HostIds ids1(1, host->host_id());
+ frontend->AddExpectedEvent(ids1, APPCACHE_CHECKING_EVENT);
+ frontend->AddExpectedEvent(ids1, APPCACHE_DOWNLOADING_EVENT);
+ frontend->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT);
+ frontend->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT);
+ frontend->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT); // final
+ frontend->AddExpectedEvent(ids1, APPCACHE_CACHED_EVENT);
+
+ // Group status will be APPCACHE_STATUS_IDLE so cannot call
+ // WaitForUpdateToFinish.
+ group_->AddUpdateObserver(this);
+ }
+
+ void IfModifiedSinceTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ net::URLRequestJobFactoryImpl* new_factory(
+ new net::URLRequestJobFactoryImpl);
+ new_factory->SetProtocolHandler("http", new IfModifiedSinceJobFactory);
+ io_thread_->SetNewJobFactory(new_factory);
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_->storage(), GURL("http://headertest"), 111);
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ // First test against a cache attempt. Will start manifest fetch
+ // synchronously.
+ HttpHeadersRequestTestJob::Initialize(std::string(), std::string());
+ MockFrontend mock_frontend;
+ AppCacheHost host(1, &mock_frontend, service_.get());
+ update->StartUpdate(&host, GURL());
+ HttpHeadersRequestTestJob::Verify();
+ delete update;
+
+ // Now simulate a refetch manifest request. Will start fetch request
+ // synchronously.
+ const char data[] =
+ "HTTP/1.1 200 OK\0"
+ "\0";
+ net::HttpResponseHeaders* headers =
+ new net::HttpResponseHeaders(std::string(data, arraysize(data)));
+ net::HttpResponseInfo* response_info = new net::HttpResponseInfo();
+ response_info->headers = headers; // adds ref to headers
+
+ HttpHeadersRequestTestJob::Initialize(std::string(), std::string());
+ update = new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+ group_->update_status_ = AppCacheGroup::DOWNLOADING;
+ update->manifest_response_info_.reset(response_info);
+ update->internal_state_ = AppCacheUpdateJob::REFETCH_MANIFEST;
+ update->FetchManifest(false); // not first request
+ HttpHeadersRequestTestJob::Verify();
+ delete update;
+
+ // Change the headers to include a Last-Modified header. Manifest refetch
+ // should include If-Modified-Since header.
+ const char data2[] =
+ "HTTP/1.1 200 OK\0"
+ "Last-Modified: Sat, 29 Oct 1994 19:43:31 GMT\0"
+ "\0";
+ net::HttpResponseHeaders* headers2 =
+ new net::HttpResponseHeaders(std::string(data2, arraysize(data2)));
+ response_info = new net::HttpResponseInfo();
+ response_info->headers = headers2;
+
+ HttpHeadersRequestTestJob::Initialize("Sat, 29 Oct 1994 19:43:31 GMT",
+ std::string());
+ update = new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+ group_->update_status_ = AppCacheGroup::DOWNLOADING;
+ update->manifest_response_info_.reset(response_info);
+ update->internal_state_ = AppCacheUpdateJob::REFETCH_MANIFEST;
+ update->FetchManifest(false); // not first request
+ HttpHeadersRequestTestJob::Verify();
+ delete update;
+
+ UpdateFinished();
+ }
+
+ void IfModifiedSinceUpgradeTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ HttpHeadersRequestTestJob::Initialize("Sat, 29 Oct 1994 19:43:31 GMT",
+ std::string());
+ net::URLRequestJobFactoryImpl* new_factory(
+ new net::URLRequestJobFactoryImpl);
+ new_factory->SetProtocolHandler("http", new IfModifiedSinceJobFactory);
+ io_thread_->SetNewJobFactory(new_factory);
+
+ MakeService();
+ group_ =new AppCacheGroup(
+ service_->storage(),
+ MockHttpServer::GetMockUrl("files/manifest1"),
+ 111);
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ // Give the newest cache a manifest enry that is in storage.
+ response_writer_.reset(
+ service_->storage()->CreateResponseWriter(group_->manifest_url(),
+ group_->group_id()));
+
+ AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(),
+ response_writer_->response_id());
+ MockFrontend* frontend = MakeMockFrontend();
+ AppCacheHost* host = MakeHost(1, frontend);
+ host->AssociateCompleteCache(cache);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ expect_old_cache_ = cache;
+ tested_manifest_ = MANIFEST1;
+ MockFrontend::HostIds ids1(1, host->host_id());
+ frontend->AddExpectedEvent(ids1, APPCACHE_CHECKING_EVENT);
+ frontend->AddExpectedEvent(ids1, APPCACHE_DOWNLOADING_EVENT);
+ frontend->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT);
+ frontend->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT);
+ frontend->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT); // final
+ frontend->AddExpectedEvent(ids1, APPCACHE_UPDATE_READY_EVENT);
+
+ // Seed storage with expected manifest response info that will cause
+ // an If-Modified-Since header to be put in the manifest fetch request.
+ const char data[] =
+ "HTTP/1.1 200 OK\0"
+ "Last-Modified: Sat, 29 Oct 1994 19:43:31 GMT\0"
+ "\0";
+ net::HttpResponseHeaders* headers =
+ new net::HttpResponseHeaders(std::string(data, arraysize(data)));
+ net::HttpResponseInfo* response_info = new net::HttpResponseInfo();
+ response_info->headers = headers; // adds ref to headers
+ scoped_refptr<HttpResponseInfoIOBuffer> io_buffer(
+ new HttpResponseInfoIOBuffer(response_info)); // adds ref to info
+ response_writer_->WriteInfo(
+ io_buffer.get(),
+ base::Bind(&AppCacheUpdateJobTest::StartUpdateAfterSeedingStorageData,
+ base::Unretained(this)));
+
+ // Start update after data write completes asynchronously.
+ }
+
+ void IfNoneMatchUpgradeTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ HttpHeadersRequestTestJob::Initialize(std::string(), "\"LadeDade\"");
+ net::URLRequestJobFactoryImpl* new_factory(
+ new net::URLRequestJobFactoryImpl);
+ new_factory->SetProtocolHandler("http", new IfModifiedSinceJobFactory);
+ io_thread_->SetNewJobFactory(new_factory);
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_->storage(),
+ MockHttpServer::GetMockUrl("files/manifest1"),
+ 111);
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ // Give the newest cache a manifest enry that is in storage.
+ response_writer_.reset(
+ service_->storage()->CreateResponseWriter(group_->manifest_url(),
+ group_->group_id()));
+
+ AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(),
+ response_writer_->response_id());
+ MockFrontend* frontend = MakeMockFrontend();
+ AppCacheHost* host = MakeHost(1, frontend);
+ host->AssociateCompleteCache(cache);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ expect_old_cache_ = cache;
+ tested_manifest_ = MANIFEST1;
+ MockFrontend::HostIds ids1(1, host->host_id());
+ frontend->AddExpectedEvent(ids1, APPCACHE_CHECKING_EVENT);
+ frontend->AddExpectedEvent(ids1, APPCACHE_DOWNLOADING_EVENT);
+ frontend->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT);
+ frontend->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT);
+ frontend->AddExpectedEvent(ids1, APPCACHE_PROGRESS_EVENT); // final
+ frontend->AddExpectedEvent(ids1, APPCACHE_UPDATE_READY_EVENT);
+
+ // Seed storage with expected manifest response info that will cause
+ // an If-None-Match header to be put in the manifest fetch request.
+ const char data[] =
+ "HTTP/1.1 200 OK\0"
+ "ETag: \"LadeDade\"\0"
+ "\0";
+ net::HttpResponseHeaders* headers =
+ new net::HttpResponseHeaders(std::string(data, arraysize(data)));
+ net::HttpResponseInfo* response_info = new net::HttpResponseInfo();
+ response_info->headers = headers; // adds ref to headers
+ scoped_refptr<HttpResponseInfoIOBuffer> io_buffer(
+ new HttpResponseInfoIOBuffer(response_info)); // adds ref to info
+ response_writer_->WriteInfo(
+ io_buffer.get(),
+ base::Bind(&AppCacheUpdateJobTest::StartUpdateAfterSeedingStorageData,
+ base::Unretained(this)));
+
+ // Start update after data write completes asynchronously.
+ }
+
+ void IfNoneMatchRefetchTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ HttpHeadersRequestTestJob::Initialize(std::string(), "\"LadeDade\"");
+ net::URLRequestJobFactoryImpl* new_factory(
+ new net::URLRequestJobFactoryImpl);
+ new_factory->SetProtocolHandler("http", new IfModifiedSinceJobFactory);
+ io_thread_->SetNewJobFactory(new_factory);
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_->storage(), GURL("http://headertest"), 111);
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ // Simulate a refetch manifest request that uses an ETag header.
+ const char data[] =
+ "HTTP/1.1 200 OK\0"
+ "ETag: \"LadeDade\"\0"
+ "\0";
+ net::HttpResponseHeaders* headers =
+ new net::HttpResponseHeaders(std::string(data, arraysize(data)));
+ net::HttpResponseInfo* response_info = new net::HttpResponseInfo();
+ response_info->headers = headers; // adds ref to headers
+
+ group_->update_status_ = AppCacheGroup::DOWNLOADING;
+ update->manifest_response_info_.reset(response_info);
+ update->internal_state_ = AppCacheUpdateJob::REFETCH_MANIFEST;
+ update->FetchManifest(false); // not first request
+ HttpHeadersRequestTestJob::Verify();
+ delete update;
+
+ UpdateFinished();
+ }
+
+ void MultipleHeadersRefetchTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ // Verify that code is correct when building multiple extra headers.
+ HttpHeadersRequestTestJob::Initialize(
+ "Sat, 29 Oct 1994 19:43:31 GMT", "\"LadeDade\"");
+ net::URLRequestJobFactoryImpl* new_factory(
+ new net::URLRequestJobFactoryImpl);
+ new_factory->SetProtocolHandler("http", new IfModifiedSinceJobFactory);
+ io_thread_->SetNewJobFactory(new_factory);
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_->storage(), GURL("http://headertest"), 111);
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ // Simulate a refetch manifest request that uses an ETag header.
+ const char data[] =
+ "HTTP/1.1 200 OK\0"
+ "Last-Modified: Sat, 29 Oct 1994 19:43:31 GMT\0"
+ "ETag: \"LadeDade\"\0"
+ "\0";
+ net::HttpResponseHeaders* headers =
+ new net::HttpResponseHeaders(std::string(data, arraysize(data)));
+ net::HttpResponseInfo* response_info = new net::HttpResponseInfo();
+ response_info->headers = headers; // adds ref to headers
+
+ group_->update_status_ = AppCacheGroup::DOWNLOADING;
+ update->manifest_response_info_.reset(response_info);
+ update->internal_state_ = AppCacheUpdateJob::REFETCH_MANIFEST;
+ update->FetchManifest(false); // not first request
+ HttpHeadersRequestTestJob::Verify();
+ delete update;
+
+ UpdateFinished();
+ }
+
+ void CrossOriginHttpsSuccessTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ GURL manifest_url = MockHttpServer::GetMockHttpsUrl(
+ "files/valid_cross_origin_https_manifest");
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_->storage(), manifest_url, service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ MockFrontend* frontend = MakeMockFrontend();
+ AppCacheHost* host = MakeHost(1, frontend);
+ update->StartUpdate(host, GURL());
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ tested_manifest_ = NONE;
+ MockFrontend::HostIds host_ids(1, host->host_id());
+ frontend->AddExpectedEvent(host_ids, APPCACHE_CHECKING_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void CrossOriginHttpsDeniedTest() {
+ ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+
+ GURL manifest_url = MockHttpServer::GetMockHttpsUrl(
+ "files/invalid_cross_origin_https_manifest");
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_->storage(), manifest_url, service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update =
+ new AppCacheUpdateJob(service_.get(), group_.get());
+ group_->update_job_ = update;
+
+ MockFrontend* frontend = MakeMockFrontend();
+ AppCacheHost* host = MakeHost(1, frontend);
+ update->StartUpdate(host, GURL());
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = false;
+ tested_manifest_ = NONE;
+ MockFrontend::HostIds host_ids(1, host->host_id());
+ frontend->AddExpectedEvent(host_ids, APPCACHE_CHECKING_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void WaitForUpdateToFinish() {
+ if (group_->update_status() == AppCacheGroup::IDLE)
+ UpdateFinished();
+ else
+ group_->AddUpdateObserver(this);
+ }
+
+ virtual void OnUpdateComplete(AppCacheGroup* group) OVERRIDE {
+ ASSERT_EQ(group_, group);
+ protect_newest_cache_ = group->newest_complete_cache();
+ UpdateFinished();
+ }
+
+ void UpdateFinished() {
+ // We unwind the stack prior to finishing up to let stack-based objects
+ // get deleted.
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&AppCacheUpdateJobTest::UpdateFinishedUnwound,
+ base::Unretained(this)));
+ }
+
+ void UpdateFinishedUnwound() {
+ EXPECT_EQ(AppCacheGroup::IDLE, group_->update_status());
+ EXPECT_TRUE(group_->update_job() == NULL);
+ if (do_checks_after_update_finished_)
+ VerifyExpectations();
+
+ // Clean up everything that was created on the IO thread.
+ protect_newest_cache_ = NULL;
+ group_ = NULL;
+ STLDeleteContainerPointers(hosts_.begin(), hosts_.end());
+ STLDeleteContainerPointers(frontends_.begin(), frontends_.end());
+ response_infos_.clear();
+ service_.reset(NULL);
+
+ event_->Signal();
+ }
+
+ void MakeService() {
+ service_.reset(new MockAppCacheService());
+ service_->set_request_context(io_thread_->request_context());
+ }
+
+ AppCache* MakeCacheForGroup(int64 cache_id, int64 manifest_response_id) {
+ return MakeCacheForGroup(cache_id, group_->manifest_url(),
+ manifest_response_id);
+ }
+
+ AppCache* MakeCacheForGroup(int64 cache_id, const GURL& manifest_entry_url,
+ int64 manifest_response_id) {
+ AppCache* cache = new AppCache(service_->storage(), cache_id);
+ cache->set_complete(true);
+ cache->set_update_time(base::Time::Now());
+ group_->AddCache(cache);
+
+ // Add manifest entry to cache.
+ cache->AddEntry(manifest_entry_url,
+ AppCacheEntry(AppCacheEntry::MANIFEST, manifest_response_id));
+
+ return cache;
+ }
+
+ AppCacheHost* MakeHost(int host_id, AppCacheFrontend* frontend) {
+ AppCacheHost* host = new AppCacheHost(host_id, frontend, service_.get());
+ hosts_.push_back(host);
+ return host;
+ }
+
+ AppCacheResponseInfo* MakeAppCacheResponseInfo(
+ const GURL& manifest_url, int64 response_id,
+ const std::string& raw_headers) {
+ net::HttpResponseInfo* http_info = new net::HttpResponseInfo();
+ http_info->headers = new net::HttpResponseHeaders(raw_headers);
+ scoped_refptr<AppCacheResponseInfo> info(
+ new AppCacheResponseInfo(service_->storage(), manifest_url,
+ response_id, http_info, 0));
+ response_infos_.push_back(info);
+ return info.get();
+ }
+
+ MockFrontend* MakeMockFrontend() {
+ MockFrontend* frontend = new MockFrontend();
+ frontends_.push_back(frontend);
+ return frontend;
+ }
+
+ // Verifies conditions about the group and notifications after an update
+ // has finished. Cannot verify update job internals as update is deleted.
+ void VerifyExpectations() {
+ RetryRequestTestJob::Verify();
+ HttpHeadersRequestTestJob::Verify();
+
+ EXPECT_EQ(expect_group_obsolete_, group_->is_obsolete());
+ EXPECT_EQ(expect_group_is_being_deleted_, group_->is_being_deleted());
+
+ if (expect_group_has_cache_) {
+ EXPECT_TRUE(group_->newest_complete_cache() != NULL);
+
+ if (expect_non_null_update_time_)
+ EXPECT_TRUE(!group_->newest_complete_cache()->update_time().is_null());
+
+ if (expect_old_cache_) {
+ EXPECT_NE(expect_old_cache_, group_->newest_complete_cache());
+ EXPECT_TRUE(group_->old_caches().end() !=
+ std::find(group_->old_caches().begin(),
+ group_->old_caches().end(), expect_old_cache_));
+ }
+ if (expect_newest_cache_) {
+ EXPECT_EQ(expect_newest_cache_, group_->newest_complete_cache());
+ EXPECT_TRUE(group_->old_caches().end() ==
+ std::find(group_->old_caches().begin(),
+ group_->old_caches().end(), expect_newest_cache_));
+ } else {
+ // Tests that don't know which newest cache to expect contain updates
+ // that succeed (because the update creates a new cache whose pointer
+ // is unknown to the test). Check group and newest cache were stored
+ // when update succeeds.
+ MockAppCacheStorage* storage =
+ reinterpret_cast<MockAppCacheStorage*>(service_->storage());
+ EXPECT_TRUE(storage->IsGroupStored(group_.get()));
+ EXPECT_TRUE(storage->IsCacheStored(group_->newest_complete_cache()));
+
+ // Check that all entries in the newest cache were stored.
+ const AppCache::EntryMap& entries =
+ group_->newest_complete_cache()->entries();
+ for (AppCache::EntryMap::const_iterator it = entries.begin();
+ it != entries.end(); ++it) {
+ EXPECT_NE(kAppCacheNoResponseId, it->second.response_id());
+
+ // Check that any copied entries have the expected response id
+ // and that entries that are not copied have a different response id.
+ std::map<GURL, int64>::iterator found =
+ expect_response_ids_.find(it->first);
+ if (found != expect_response_ids_.end()) {
+ EXPECT_EQ(found->second, it->second.response_id());
+ } else if (expect_old_cache_) {
+ AppCacheEntry* old_entry = expect_old_cache_->GetEntry(it->first);
+ if (old_entry)
+ EXPECT_NE(old_entry->response_id(), it->second.response_id());
+ }
+ }
+ }
+ } else {
+ EXPECT_TRUE(group_->newest_complete_cache() == NULL);
+ }
+
+ // Check expected events.
+ for (size_t i = 0; i < frontends_.size(); ++i) {
+ MockFrontend* frontend = frontends_[i];
+
+ MockFrontend::RaisedEvents& expected_events = frontend->expected_events_;
+ MockFrontend::RaisedEvents& actual_events = frontend->raised_events_;
+ EXPECT_EQ(expected_events.size(), actual_events.size());
+
+ // Check each expected event.
+ for (size_t j = 0;
+ j < expected_events.size() && j < actual_events.size(); ++j) {
+ EXPECT_EQ(expected_events[j].second, actual_events[j].second);
+
+ MockFrontend::HostIds& expected_ids = expected_events[j].first;
+ MockFrontend::HostIds& actual_ids = actual_events[j].first;
+ EXPECT_EQ(expected_ids.size(), actual_ids.size());
+
+ for (size_t k = 0; k < expected_ids.size(); ++k) {
+ int id = expected_ids[k];
+ EXPECT_TRUE(std::find(actual_ids.begin(), actual_ids.end(), id) !=
+ actual_ids.end());
+ }
+ }
+
+ if (!frontend->expected_error_message_.empty()) {
+ EXPECT_EQ(frontend->expected_error_message_,
+ frontend->error_message_);
+ }
+ }
+
+ // Verify expected cache contents last as some checks are asserts
+ // and will abort the test if they fail.
+ if (tested_manifest_) {
+ AppCache* cache = group_->newest_complete_cache();
+ ASSERT_TRUE(cache != NULL);
+ EXPECT_EQ(group_, cache->owning_group());
+ EXPECT_TRUE(cache->is_complete());
+
+ switch (tested_manifest_) {
+ case MANIFEST1:
+ VerifyManifest1(cache);
+ break;
+ case MANIFEST_MERGED_TYPES:
+ VerifyManifestMergedTypes(cache);
+ break;
+ case EMPTY_MANIFEST:
+ VerifyEmptyManifest(cache);
+ break;
+ case EMPTY_FILE_MANIFEST:
+ VerifyEmptyFileManifest(cache);
+ break;
+ case PENDING_MASTER_NO_UPDATE:
+ VerifyMasterEntryNoUpdate(cache);
+ break;
+ case MANIFEST_WITH_INTERCEPT:
+ VerifyManifestWithIntercept(cache);
+ break;
+ case NONE:
+ default:
+ break;
+ }
+ }
+ }
+
+ void VerifyManifest1(AppCache* cache) {
+ size_t expected = 3 + expect_extra_entries_.size();
+ EXPECT_EQ(expected, cache->entries().size());
+ const char* kManifestPath = tested_manifest_path_override_ ?
+ tested_manifest_path_override_ :
+ "files/manifest1";
+ AppCacheEntry* entry =
+ cache->GetEntry(MockHttpServer::GetMockUrl(kManifestPath));
+ ASSERT_TRUE(entry);
+ EXPECT_EQ(AppCacheEntry::MANIFEST, entry->types());
+ entry = cache->GetEntry(MockHttpServer::GetMockUrl("files/explicit1"));
+ ASSERT_TRUE(entry);
+ EXPECT_TRUE(entry->IsExplicit());
+ entry = cache->GetEntry(
+ MockHttpServer::GetMockUrl("files/fallback1a"));
+ ASSERT_TRUE(entry);
+ EXPECT_EQ(AppCacheEntry::FALLBACK, entry->types());
+
+ for (AppCache::EntryMap::iterator i = expect_extra_entries_.begin();
+ i != expect_extra_entries_.end(); ++i) {
+ entry = cache->GetEntry(i->first);
+ ASSERT_TRUE(entry);
+ EXPECT_EQ(i->second.types(), entry->types());
+ }
+
+ expected = 1;
+ ASSERT_EQ(expected, cache->fallback_namespaces_.size());
+ EXPECT_TRUE(cache->fallback_namespaces_[0] ==
+ Namespace(
+ APPCACHE_FALLBACK_NAMESPACE,
+ MockHttpServer::GetMockUrl("files/fallback1"),
+ MockHttpServer::GetMockUrl("files/fallback1a"),
+ false));
+
+ EXPECT_TRUE(cache->online_whitelist_namespaces_.empty());
+ EXPECT_TRUE(cache->online_whitelist_all_);
+
+ EXPECT_TRUE(cache->update_time_ > base::Time());
+ }
+
+ void VerifyManifestMergedTypes(AppCache* cache) {
+ size_t expected = 2;
+ EXPECT_EQ(expected, cache->entries().size());
+ AppCacheEntry* entry = cache->GetEntry(
+ MockHttpServer::GetMockUrl("files/manifest-merged-types"));
+ ASSERT_TRUE(entry);
+ EXPECT_EQ(AppCacheEntry::EXPLICIT | AppCacheEntry::MANIFEST,
+ entry->types());
+ entry = cache->GetEntry(MockHttpServer::GetMockUrl("files/explicit1"));
+ ASSERT_TRUE(entry);
+ EXPECT_EQ(AppCacheEntry::EXPLICIT | AppCacheEntry::FALLBACK |
+ AppCacheEntry::MASTER, entry->types());
+
+ expected = 1;
+ ASSERT_EQ(expected, cache->fallback_namespaces_.size());
+ EXPECT_TRUE(cache->fallback_namespaces_[0] ==
+ Namespace(
+ APPCACHE_FALLBACK_NAMESPACE,
+ MockHttpServer::GetMockUrl("files/fallback1"),
+ MockHttpServer::GetMockUrl("files/explicit1"),
+ false));
+
+ EXPECT_EQ(expected, cache->online_whitelist_namespaces_.size());
+ EXPECT_TRUE(cache->online_whitelist_namespaces_[0] ==
+ Namespace(
+ APPCACHE_NETWORK_NAMESPACE,
+ MockHttpServer::GetMockUrl("files/online1"),
+ GURL(), false));
+ EXPECT_FALSE(cache->online_whitelist_all_);
+
+ EXPECT_TRUE(cache->update_time_ > base::Time());
+ }
+
+ void VerifyEmptyManifest(AppCache* cache) {
+ const char* kManifestPath = tested_manifest_path_override_ ?
+ tested_manifest_path_override_ :
+ "files/empty-manifest";
+ size_t expected = 1;
+ EXPECT_EQ(expected, cache->entries().size());
+ AppCacheEntry* entry = cache->GetEntry(
+ MockHttpServer::GetMockUrl(kManifestPath));
+ ASSERT_TRUE(entry);
+ EXPECT_EQ(AppCacheEntry::MANIFEST, entry->types());
+
+ EXPECT_TRUE(cache->fallback_namespaces_.empty());
+ EXPECT_TRUE(cache->online_whitelist_namespaces_.empty());
+ EXPECT_FALSE(cache->online_whitelist_all_);
+
+ EXPECT_TRUE(cache->update_time_ > base::Time());
+ }
+
+ void VerifyEmptyFileManifest(AppCache* cache) {
+ EXPECT_EQ(size_t(2), cache->entries().size());
+ AppCacheEntry* entry = cache->GetEntry(
+ MockHttpServer::GetMockUrl("files/empty-file-manifest"));
+ ASSERT_TRUE(entry);
+ EXPECT_EQ(AppCacheEntry::MANIFEST, entry->types());
+
+ entry = cache->GetEntry(
+ MockHttpServer::GetMockUrl("files/empty1"));
+ ASSERT_TRUE(entry);
+ EXPECT_EQ(AppCacheEntry::EXPLICIT, entry->types());
+ EXPECT_TRUE(entry->has_response_id());
+
+ EXPECT_TRUE(cache->fallback_namespaces_.empty());
+ EXPECT_TRUE(cache->online_whitelist_namespaces_.empty());
+ EXPECT_FALSE(cache->online_whitelist_all_);
+
+ EXPECT_TRUE(cache->update_time_ > base::Time());
+ }
+
+ void VerifyMasterEntryNoUpdate(AppCache* cache) {
+ EXPECT_EQ(size_t(3), cache->entries().size());
+ AppCacheEntry* entry = cache->GetEntry(
+ MockHttpServer::GetMockUrl("files/notmodified"));
+ ASSERT_TRUE(entry);
+ EXPECT_EQ(AppCacheEntry::MANIFEST, entry->types());
+
+ entry = cache->GetEntry(
+ MockHttpServer::GetMockUrl("files/explicit1"));
+ ASSERT_TRUE(entry);
+ EXPECT_EQ(AppCacheEntry::MASTER, entry->types());
+ EXPECT_TRUE(entry->has_response_id());
+
+ entry = cache->GetEntry(
+ MockHttpServer::GetMockUrl("files/explicit2"));
+ ASSERT_TRUE(entry);
+ EXPECT_EQ(AppCacheEntry::EXPLICIT | AppCacheEntry::MASTER, entry->types());
+ EXPECT_TRUE(entry->has_response_id());
+
+ EXPECT_TRUE(cache->fallback_namespaces_.empty());
+ EXPECT_TRUE(cache->online_whitelist_namespaces_.empty());
+ EXPECT_FALSE(cache->online_whitelist_all_);
+
+ EXPECT_TRUE(cache->update_time_ > base::Time());
+ }
+
+ void VerifyManifestWithIntercept(AppCache* cache) {
+ EXPECT_EQ(2u, cache->entries().size());
+ const char* kManifestPath = "files/manifest-with-intercept";
+ AppCacheEntry* entry =
+ cache->GetEntry(MockHttpServer::GetMockUrl(kManifestPath));
+ ASSERT_TRUE(entry);
+ EXPECT_EQ(AppCacheEntry::MANIFEST, entry->types());
+ entry = cache->GetEntry(MockHttpServer::GetMockUrl("files/intercept1a"));
+ ASSERT_TRUE(entry);
+ EXPECT_TRUE(entry->IsIntercept());
+ EXPECT_TRUE(cache->online_whitelist_namespaces_.empty());
+ EXPECT_FALSE(cache->online_whitelist_all_);
+ EXPECT_TRUE(cache->update_time_ > base::Time());
+ }
+
+ private:
+ // Various manifest files used in this test.
+ enum TestedManifest {
+ NONE,
+ MANIFEST1,
+ MANIFEST_MERGED_TYPES,
+ EMPTY_MANIFEST,
+ EMPTY_FILE_MANIFEST,
+ PENDING_MASTER_NO_UPDATE,
+ MANIFEST_WITH_INTERCEPT
+ };
+
+ scoped_ptr<IOThread> io_thread_;
+
+ scoped_ptr<MockAppCacheService> service_;
+ scoped_refptr<AppCacheGroup> group_;
+ scoped_refptr<AppCache> protect_newest_cache_;
+ scoped_ptr<base::WaitableEvent> event_;
+
+ scoped_ptr<AppCacheResponseWriter> response_writer_;
+
+ // Hosts used by an async test that need to live until update job finishes.
+ // Otherwise, test can put host on the stack instead of here.
+ std::vector<AppCacheHost*> hosts_;
+
+ // Response infos used by an async test that need to live until update job
+ // finishes.
+ std::vector<scoped_refptr<AppCacheResponseInfo> > response_infos_;
+
+ // Flag indicating if test cares to verify the update after update finishes.
+ bool do_checks_after_update_finished_;
+ bool expect_group_obsolete_;
+ bool expect_group_has_cache_;
+ bool expect_group_is_being_deleted_;
+ AppCache* expect_old_cache_;
+ AppCache* expect_newest_cache_;
+ bool expect_non_null_update_time_;
+ std::vector<MockFrontend*> frontends_; // to check expected events
+ TestedManifest tested_manifest_;
+ const char* tested_manifest_path_override_;
+ AppCache::EntryMap expect_extra_entries_;
+ std::map<GURL, int64> expect_response_ids_;
+};
+
+TEST_F(AppCacheUpdateJobTest, AlreadyChecking) {
+ MockAppCacheService service;
+ scoped_refptr<AppCacheGroup> group(
+ new AppCacheGroup(service.storage(), GURL("http://manifesturl.com"),
+ service.storage()->NewGroupId()));
+
+ AppCacheUpdateJob update(&service, group.get());
+
+ // Pretend group is in checking state.
+ group->update_job_ = &update;
+ group->update_status_ = AppCacheGroup::CHECKING;
+
+ update.StartUpdate(NULL, GURL());
+ EXPECT_EQ(AppCacheGroup::CHECKING, group->update_status());
+
+ MockFrontend mock_frontend;
+ AppCacheHost host(1, &mock_frontend, &service);
+ update.StartUpdate(&host, GURL());
+
+ MockFrontend::RaisedEvents events = mock_frontend.raised_events_;
+ size_t expected = 1;
+ EXPECT_EQ(expected, events.size());
+ EXPECT_EQ(expected, events[0].first.size());
+ EXPECT_EQ(host.host_id(), events[0].first[0]);
+ EXPECT_EQ(APPCACHE_CHECKING_EVENT, events[0].second);
+ EXPECT_EQ(AppCacheGroup::CHECKING, group->update_status());
+}
+
+TEST_F(AppCacheUpdateJobTest, AlreadyDownloading) {
+ MockAppCacheService service;
+ scoped_refptr<AppCacheGroup> group(
+ new AppCacheGroup(service.storage(), GURL("http://manifesturl.com"),
+ service.storage()->NewGroupId()));
+
+ AppCacheUpdateJob update(&service, group.get());
+
+ // Pretend group is in downloading state.
+ group->update_job_ = &update;
+ group->update_status_ = AppCacheGroup::DOWNLOADING;
+
+ update.StartUpdate(NULL, GURL());
+ EXPECT_EQ(AppCacheGroup::DOWNLOADING, group->update_status());
+
+ MockFrontend mock_frontend;
+ AppCacheHost host(1, &mock_frontend, &service);
+ update.StartUpdate(&host, GURL());
+
+ MockFrontend::RaisedEvents events = mock_frontend.raised_events_;
+ size_t expected = 2;
+ EXPECT_EQ(expected, events.size());
+ expected = 1;
+ EXPECT_EQ(expected, events[0].first.size());
+ EXPECT_EQ(host.host_id(), events[0].first[0]);
+ EXPECT_EQ(APPCACHE_CHECKING_EVENT, events[0].second);
+
+ EXPECT_EQ(expected, events[1].first.size());
+ EXPECT_EQ(host.host_id(), events[1].first[0]);
+ EXPECT_EQ(appcache::APPCACHE_DOWNLOADING_EVENT, events[1].second);
+
+ EXPECT_EQ(AppCacheGroup::DOWNLOADING, group->update_status());
+}
+
+TEST_F(AppCacheUpdateJobTest, StartCacheAttempt) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::StartCacheAttemptTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, StartUpgradeAttempt) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::StartUpgradeAttemptTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, CacheAttemptFetchManifestFail) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::CacheAttemptFetchManifestFailTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, UpgradeFetchManifestFail) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::UpgradeFetchManifestFailTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, ManifestRedirect) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::ManifestRedirectTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, ManifestMissingMimeTypeTest) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::ManifestMissingMimeTypeTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, ManifestNotFound) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::ManifestNotFoundTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, ManifestGone) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::ManifestGoneTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, CacheAttemptNotModified) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::CacheAttemptNotModifiedTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, UpgradeNotModified) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::UpgradeNotModifiedTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, UpgradeManifestDataUnchanged) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::UpgradeManifestDataUnchangedTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, Bug95101Test) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::Bug95101Test);
+}
+
+TEST_F(AppCacheUpdateJobTest, BasicCacheAttemptSuccess) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::BasicCacheAttemptSuccessTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, DownloadInterceptEntriesTest) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::DownloadInterceptEntriesTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, BasicUpgradeSuccess) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::BasicUpgradeSuccessTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, UpgradeLoadFromNewestCache) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::UpgradeLoadFromNewestCacheTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, UpgradeNoLoadFromNewestCache) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::UpgradeNoLoadFromNewestCacheTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, UpgradeLoadFromNewestCacheVaryHeader) {
+ RunTestOnIOThread(
+ &AppCacheUpdateJobTest::UpgradeLoadFromNewestCacheVaryHeaderTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, UpgradeSuccessMergedTypes) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::UpgradeSuccessMergedTypesTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, CacheAttemptFailUrlFetch) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::CacheAttemptFailUrlFetchTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, UpgradeFailUrlFetch) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::UpgradeFailUrlFetchTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, UpgradeFailMasterUrlFetch) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::UpgradeFailMasterUrlFetchTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, EmptyManifest) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::EmptyManifestTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, EmptyFile) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::EmptyFileTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, RetryRequest) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::RetryRequestTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, RetryNoRetryAfter) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::RetryNoRetryAfterTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, RetryNonzeroRetryAfter) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::RetryNonzeroRetryAfterTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, RetrySuccess) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::RetrySuccessTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, RetryUrl) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::RetryUrlTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, FailStoreNewestCache) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::FailStoreNewestCacheTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, MasterEntryFailStoreNewestCacheTest) {
+ RunTestOnIOThread(
+ &AppCacheUpdateJobTest::MasterEntryFailStoreNewestCacheTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, UpgradeFailStoreNewestCache) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::UpgradeFailStoreNewestCacheTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, UpgradeFailMakeGroupObsolete) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::UpgradeFailMakeGroupObsoleteTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, MasterEntryFetchManifestFail) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::MasterEntryFetchManifestFailTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, MasterEntryBadManifest) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::MasterEntryBadManifestTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, MasterEntryManifestNotFound) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::MasterEntryManifestNotFoundTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, MasterEntryFailUrlFetch) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::MasterEntryFailUrlFetchTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, MasterEntryAllFail) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::MasterEntryAllFailTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, UpgradeMasterEntryAllFail) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::UpgradeMasterEntryAllFailTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, MasterEntrySomeFail) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::MasterEntrySomeFailTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, UpgradeMasterEntrySomeFail) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::UpgradeMasterEntrySomeFailTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, MasterEntryNoUpdate) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::MasterEntryNoUpdateTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, StartUpdateMidCacheAttempt) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::StartUpdateMidCacheAttemptTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, StartUpdateMidNoUpdate) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::StartUpdateMidNoUpdateTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, StartUpdateMidDownload) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::StartUpdateMidDownloadTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, QueueMasterEntry) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::QueueMasterEntryTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, IfModifiedSince) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::IfModifiedSinceTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, IfModifiedSinceUpgrade) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::IfModifiedSinceUpgradeTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, IfNoneMatchUpgrade) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::IfNoneMatchUpgradeTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, IfNoneMatchRefetch) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::IfNoneMatchRefetchTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, MultipleHeadersRefetch) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::MultipleHeadersRefetchTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, CrossOriginHttpsSuccess) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::CrossOriginHttpsSuccessTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, CrossOriginHttpsDenied) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::CrossOriginHttpsDeniedTest);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/appcache/appcache_url_request_job_unittest.cc b/chromium/content/browser/appcache/appcache_url_request_job_unittest.cc
new file mode 100644
index 00000000000..edaa07e8e38
--- /dev/null
+++ b/chromium/content/browser/appcache/appcache_url_request_job_unittest.cc
@@ -0,0 +1,869 @@
+// 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 <stack>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/pickle.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "content/browser/appcache/mock_appcache_service.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/base/request_priority.h"
+#include "net/http/http_response_headers.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_error_job.h"
+#include "net/url_request/url_request_job_factory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/appcache/appcache_response.h"
+#include "webkit/browser/appcache/appcache_url_request_job.h"
+
+using appcache::AppCacheEntry;
+using appcache::AppCacheStorage;
+using appcache::AppCacheResponseInfo;
+using appcache::AppCacheResponseReader;
+using appcache::AppCacheResponseWriter;
+using appcache::AppCacheURLRequestJob;
+using appcache::HttpResponseInfoIOBuffer;
+using appcache::kAppCacheNoCacheId;
+using net::IOBuffer;
+using net::WrappedIOBuffer;
+
+namespace content {
+
+namespace {
+
+const char kHttpBasicHeaders[] = "HTTP/1.0 200 OK\0Content-Length: 5\0\0";
+const char kHttpBasicBody[] = "Hello";
+
+const int kNumBlocks = 4;
+const int kBlockSize = 1024;
+
+class MockURLRequestJobFactory : public net::URLRequestJobFactory {
+ public:
+ MockURLRequestJobFactory() : job_(NULL) {
+ }
+
+ virtual ~MockURLRequestJobFactory() {
+ DCHECK(!job_);
+ }
+
+ void SetJob(net::URLRequestJob* job) {
+ job_ = job;
+ }
+
+ bool has_job() const {
+ return job_ != NULL;
+ }
+
+ // net::URLRequestJobFactory implementation.
+ virtual net::URLRequestJob* MaybeCreateJobWithProtocolHandler(
+ const std::string& scheme,
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) const OVERRIDE {
+ if (job_) {
+ net::URLRequestJob* temp = job_;
+ job_ = NULL;
+ return temp;
+ } else {
+ return new net::URLRequestErrorJob(request,
+ network_delegate,
+ net::ERR_INTERNET_DISCONNECTED);
+ }
+ }
+
+ virtual bool IsHandledProtocol(const std::string& scheme) const OVERRIDE {
+ return scheme == "http";
+ };
+
+ virtual bool IsHandledURL(const GURL& url) const OVERRIDE {
+ return url.SchemeIs("http");
+ }
+
+ virtual bool IsSafeRedirectTarget(const GURL& location) const OVERRIDE {
+ return false;
+ }
+
+ private:
+ mutable net::URLRequestJob* job_;
+};
+
+class AppCacheURLRequestJobTest : public testing::Test {
+ public:
+
+ // Test Harness -------------------------------------------------------------
+ // TODO(michaeln): share this test harness with AppCacheResponseTest
+
+ class MockStorageDelegate : public AppCacheStorage::Delegate {
+ public:
+ explicit MockStorageDelegate(AppCacheURLRequestJobTest* test)
+ : loaded_info_id_(0), test_(test) {
+ }
+
+ virtual void OnResponseInfoLoaded(AppCacheResponseInfo* info,
+ int64 response_id) OVERRIDE {
+ loaded_info_ = info;
+ loaded_info_id_ = response_id;
+ test_->ScheduleNextTask();
+ }
+
+ scoped_refptr<AppCacheResponseInfo> loaded_info_;
+ int64 loaded_info_id_;
+ AppCacheURLRequestJobTest* test_;
+ };
+
+ class MockURLRequestDelegate : public net::URLRequest::Delegate {
+ public:
+ explicit MockURLRequestDelegate(AppCacheURLRequestJobTest* test)
+ : test_(test),
+ received_data_(new net::IOBuffer(kNumBlocks * kBlockSize)),
+ did_receive_headers_(false), amount_received_(0),
+ kill_after_amount_received_(0), kill_with_io_pending_(false) {
+ }
+
+ virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE {
+ amount_received_ = 0;
+ did_receive_headers_ = false;
+ if (request->status().is_success()) {
+ EXPECT_TRUE(request->response_headers());
+ did_receive_headers_ = true;
+ received_info_ = request->response_info();
+ ReadSome(request);
+ } else {
+ RequestComplete();
+ }
+ }
+
+ virtual void OnReadCompleted(net::URLRequest* request,
+ int bytes_read) OVERRIDE {
+ if (bytes_read > 0) {
+ amount_received_ += bytes_read;
+
+ if (kill_after_amount_received_ && !kill_with_io_pending_) {
+ if (amount_received_ >= kill_after_amount_received_) {
+ request->Cancel();
+ return;
+ }
+ }
+
+ ReadSome(request);
+
+ if (kill_after_amount_received_ && kill_with_io_pending_) {
+ if (amount_received_ >= kill_after_amount_received_) {
+ request->Cancel();
+ return;
+ }
+ }
+ } else {
+ RequestComplete();
+ }
+ }
+
+ void ReadSome(net::URLRequest* request) {
+ DCHECK(amount_received_ + kBlockSize <= kNumBlocks * kBlockSize);
+ scoped_refptr<IOBuffer> wrapped_buffer(
+ new net::WrappedIOBuffer(received_data_->data() + amount_received_));
+ int bytes_read = 0;
+ EXPECT_FALSE(
+ request->Read(wrapped_buffer.get(), kBlockSize, &bytes_read));
+ EXPECT_EQ(0, bytes_read);
+ }
+
+ void RequestComplete() {
+ test_->ScheduleNextTask();
+ }
+
+ AppCacheURLRequestJobTest* test_;
+ net::HttpResponseInfo received_info_;
+ scoped_refptr<net::IOBuffer> received_data_;
+ bool did_receive_headers_;
+ int amount_received_;
+ int kill_after_amount_received_;
+ bool kill_with_io_pending_;
+ };
+
+ // Helper callback to run a test on our io_thread. The io_thread is spun up
+ // once and reused for all tests.
+ template <class Method>
+ void MethodWrapper(Method method) {
+ SetUpTest();
+ (this->*method)();
+ }
+
+ static void SetUpTestCase() {
+ io_thread_.reset(new base::Thread("AppCacheURLRequestJobTest Thread"));
+ base::Thread::Options options(base::MessageLoop::TYPE_IO, 0);
+ io_thread_->StartWithOptions(options);
+ }
+
+ static void TearDownTestCase() {
+ io_thread_.reset(NULL);
+ }
+
+ AppCacheURLRequestJobTest() {}
+
+ template <class Method>
+ void RunTestOnIOThread(Method method) {
+ test_finished_event_ .reset(new base::WaitableEvent(false, false));
+ io_thread_->message_loop()->PostTask(
+ FROM_HERE, base::Bind(&AppCacheURLRequestJobTest::MethodWrapper<Method>,
+ base::Unretained(this), method));
+ test_finished_event_->Wait();
+ }
+
+ void SetUpTest() {
+ DCHECK(base::MessageLoop::current() == io_thread_->message_loop());
+ DCHECK(task_stack_.empty());
+
+ storage_delegate_.reset(new MockStorageDelegate(this));
+ service_.reset(new MockAppCacheService());
+ expected_read_result_ = 0;
+ expected_write_result_ = 0;
+ written_response_id_ = 0;
+ reader_deletion_count_down_ = 0;
+ writer_deletion_count_down_ = 0;
+
+ url_request_delegate_.reset(new MockURLRequestDelegate(this));
+ job_factory_.reset(new MockURLRequestJobFactory());
+ empty_context_.reset(new net::URLRequestContext());
+ empty_context_->set_job_factory(job_factory_.get());
+ }
+
+ void TearDownTest() {
+ DCHECK(base::MessageLoop::current() == io_thread_->message_loop());
+ request_.reset();
+
+ while (!task_stack_.empty())
+ task_stack_.pop();
+
+ reader_.reset();
+ read_buffer_ = NULL;
+ read_info_buffer_ = NULL;
+ writer_.reset();
+ write_buffer_ = NULL;
+ write_info_buffer_ = NULL;
+ storage_delegate_.reset();
+ service_.reset();
+
+ DCHECK(!job_factory_->has_job());
+ empty_context_.reset();
+ job_factory_.reset();
+ url_request_delegate_.reset();
+ }
+
+ void TestFinished() {
+ // We unwind the stack prior to finishing up to let stack
+ // based objects get deleted.
+ DCHECK(base::MessageLoop::current() == io_thread_->message_loop());
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&AppCacheURLRequestJobTest::TestFinishedUnwound,
+ base::Unretained(this)));
+ }
+
+ void TestFinishedUnwound() {
+ TearDownTest();
+ test_finished_event_->Signal();
+ }
+
+ void PushNextTask(const base::Closure& task) {
+ task_stack_.push(std::pair<base::Closure, bool>(task, false));
+ }
+
+ void PushNextTaskAsImmediate(const base::Closure& task) {
+ task_stack_.push(std::pair<base::Closure, bool>(task, true));
+ }
+
+ void ScheduleNextTask() {
+ DCHECK(base::MessageLoop::current() == io_thread_->message_loop());
+ if (task_stack_.empty()) {
+ TestFinished();
+ return;
+ }
+ base::Closure task =task_stack_.top().first;
+ bool immediate = task_stack_.top().second;
+ task_stack_.pop();
+ if (immediate)
+ task.Run();
+ else
+ base::MessageLoop::current()->PostTask(FROM_HERE, task);
+ }
+
+ // Wrappers to call AppCacheResponseReader/Writer Read and Write methods
+
+ void WriteBasicResponse() {
+ scoped_refptr<IOBuffer> body(new WrappedIOBuffer(kHttpBasicBody));
+ std::string raw_headers(kHttpBasicHeaders, arraysize(kHttpBasicHeaders));
+ WriteResponse(
+ MakeHttpResponseInfo(raw_headers), body.get(), strlen(kHttpBasicBody));
+ }
+
+ void WriteResponse(net::HttpResponseInfo* head,
+ IOBuffer* body, int body_len) {
+ DCHECK(body);
+ scoped_refptr<IOBuffer> body_ref(body);
+ PushNextTask(base::Bind(&AppCacheURLRequestJobTest::WriteResponseBody,
+ base::Unretained(this), body_ref, body_len));
+ WriteResponseHead(head);
+ }
+
+ void WriteResponseHead(net::HttpResponseInfo* head) {
+ EXPECT_FALSE(writer_->IsWritePending());
+ expected_write_result_ = GetHttpResponseInfoSize(head);
+ write_info_buffer_ = new HttpResponseInfoIOBuffer(head);
+ writer_->WriteInfo(
+ write_info_buffer_.get(),
+ base::Bind(&AppCacheURLRequestJobTest::OnWriteInfoComplete,
+ base::Unretained(this)));
+ }
+
+ void WriteResponseBody(scoped_refptr<IOBuffer> io_buffer, int buf_len) {
+ EXPECT_FALSE(writer_->IsWritePending());
+ write_buffer_ = io_buffer;
+ expected_write_result_ = buf_len;
+ writer_->WriteData(write_buffer_.get(),
+ buf_len,
+ base::Bind(&AppCacheURLRequestJobTest::OnWriteComplete,
+ base::Unretained(this)));
+ }
+
+ void ReadResponseBody(scoped_refptr<IOBuffer> io_buffer, int buf_len) {
+ EXPECT_FALSE(reader_->IsReadPending());
+ read_buffer_ = io_buffer;
+ expected_read_result_ = buf_len;
+ reader_->ReadData(read_buffer_.get(),
+ buf_len,
+ base::Bind(&AppCacheURLRequestJobTest::OnReadComplete,
+ base::Unretained(this)));
+ }
+
+ // AppCacheResponseReader / Writer completion callbacks
+
+ void OnWriteInfoComplete(int result) {
+ EXPECT_FALSE(writer_->IsWritePending());
+ EXPECT_EQ(expected_write_result_, result);
+ ScheduleNextTask();
+ }
+
+ void OnWriteComplete(int result) {
+ EXPECT_FALSE(writer_->IsWritePending());
+ EXPECT_EQ(expected_write_result_, result);
+ ScheduleNextTask();
+ }
+
+ void OnReadInfoComplete(int result) {
+ EXPECT_FALSE(reader_->IsReadPending());
+ EXPECT_EQ(expected_read_result_, result);
+ ScheduleNextTask();
+ }
+
+ void OnReadComplete(int result) {
+ EXPECT_FALSE(reader_->IsReadPending());
+ EXPECT_EQ(expected_read_result_, result);
+ ScheduleNextTask();
+ }
+
+ // Helpers to work with HttpResponseInfo objects
+
+ net::HttpResponseInfo* MakeHttpResponseInfo(const std::string& raw_headers) {
+ net::HttpResponseInfo* info = new net::HttpResponseInfo;
+ info->request_time = base::Time::Now();
+ info->response_time = base::Time::Now();
+ info->was_cached = false;
+ info->headers = new net::HttpResponseHeaders(raw_headers);
+ return info;
+ }
+
+ int GetHttpResponseInfoSize(const net::HttpResponseInfo* info) {
+ Pickle pickle;
+ return PickleHttpResonseInfo(&pickle, info);
+ }
+
+ bool CompareHttpResponseInfos(const net::HttpResponseInfo* info1,
+ const net::HttpResponseInfo* info2) {
+ Pickle pickle1;
+ Pickle pickle2;
+ PickleHttpResonseInfo(&pickle1, info1);
+ PickleHttpResonseInfo(&pickle2, info2);
+ return (pickle1.size() == pickle2.size()) &&
+ (0 == memcmp(pickle1.data(), pickle2.data(), pickle1.size()));
+ }
+
+ int PickleHttpResonseInfo(Pickle* pickle, const net::HttpResponseInfo* info) {
+ const bool kSkipTransientHeaders = true;
+ const bool kTruncated = false;
+ info->Persist(pickle, kSkipTransientHeaders, kTruncated);
+ return pickle->size();
+ }
+
+ // Helpers to fill and verify blocks of memory with a value
+
+ void FillData(char value, char* data, int data_len) {
+ memset(data, value, data_len);
+ }
+
+ bool CheckData(char value, const char* data, int data_len) {
+ for (int i = 0; i < data_len; ++i, ++data) {
+ if (*data != value)
+ return false;
+ }
+ return true;
+ }
+
+ // Individual Tests ---------------------------------------------------------
+ // Some of the individual tests involve multiple async steps. Each test
+ // is delineated with a section header.
+
+ // Basic -------------------------------------------------------------------
+ void Basic() {
+ AppCacheStorage* storage = service_->storage();
+ net::URLRequest request(GURL("http://blah/"), net::DEFAULT_PRIORITY, NULL,
+ empty_context_.get());
+ scoped_refptr<AppCacheURLRequestJob> job;
+
+ // Create an instance and see that it looks as expected.
+
+ job = new AppCacheURLRequestJob(
+ &request, NULL, storage, NULL, false);
+ EXPECT_TRUE(job->is_waiting());
+ EXPECT_FALSE(job->is_delivering_appcache_response());
+ EXPECT_FALSE(job->is_delivering_network_response());
+ EXPECT_FALSE(job->is_delivering_error_response());
+ EXPECT_FALSE(job->has_been_started());
+ EXPECT_FALSE(job->has_been_killed());
+ EXPECT_EQ(GURL(), job->manifest_url());
+ EXPECT_EQ(kAppCacheNoCacheId, job->cache_id());
+ EXPECT_FALSE(job->entry().has_response_id());
+
+ TestFinished();
+ }
+
+ // DeliveryOrders -----------------------------------------------------
+ void DeliveryOrders() {
+ AppCacheStorage* storage = service_->storage();
+ net::URLRequest request(GURL("http://blah/"), net::DEFAULT_PRIORITY, NULL,
+ empty_context_.get());
+ scoped_refptr<AppCacheURLRequestJob> job;
+
+ // Create an instance, give it a delivery order and see that
+ // it looks as expected.
+
+ job = new AppCacheURLRequestJob(&request, NULL, storage, NULL, false);
+ job->DeliverErrorResponse();
+ EXPECT_TRUE(job->is_delivering_error_response());
+ EXPECT_FALSE(job->has_been_started());
+
+ job = new AppCacheURLRequestJob(&request, NULL, storage, NULL, false);
+ job->DeliverNetworkResponse();
+ EXPECT_TRUE(job->is_delivering_network_response());
+ EXPECT_FALSE(job->has_been_started());
+
+ job = new AppCacheURLRequestJob(&request, NULL, storage, NULL, false);
+ const GURL kManifestUrl("http://blah/");
+ const int64 kCacheId(1);
+ const int64 kGroupId(1);
+ const AppCacheEntry kEntry(AppCacheEntry::EXPLICIT, 1);
+ job->DeliverAppCachedResponse(kManifestUrl, kCacheId, kGroupId,
+ kEntry, false);
+ EXPECT_FALSE(job->is_waiting());
+ EXPECT_TRUE(job->is_delivering_appcache_response());
+ EXPECT_FALSE(job->has_been_started());
+ EXPECT_EQ(kManifestUrl, job->manifest_url());
+ EXPECT_EQ(kCacheId, job->cache_id());
+ EXPECT_EQ(kGroupId, job->group_id());
+ EXPECT_EQ(kEntry.types(), job->entry().types());
+ EXPECT_EQ(kEntry.response_id(), job->entry().response_id());
+
+ TestFinished();
+ }
+
+ // DeliverNetworkResponse --------------------------------------------------
+
+ void DeliverNetworkResponse() {
+ // This test has async steps.
+ PushNextTask(
+ base::Bind(&AppCacheURLRequestJobTest::VerifyDeliverNetworkResponse,
+ base::Unretained(this)));
+
+ AppCacheStorage* storage = service_->storage();
+ request_ = empty_context_->CreateRequest(GURL("http://blah/"),
+ net::DEFAULT_PRIORITY,
+ url_request_delegate_.get(),
+ NULL);
+
+ // Setup to create an AppCacheURLRequestJob with orders to deliver
+ // a network response.
+ AppCacheURLRequestJob* mock_job = new AppCacheURLRequestJob(
+ request_.get(), NULL, storage, NULL, false);
+ job_factory_->SetJob(mock_job);
+ mock_job->DeliverNetworkResponse();
+ EXPECT_TRUE(mock_job->is_delivering_network_response());
+ EXPECT_FALSE(mock_job->has_been_started());
+
+ // Start the request.
+ request_->Start();
+
+ // The job should have been picked up.
+ EXPECT_FALSE(job_factory_->has_job());
+ // Completion is async.
+ }
+
+ void VerifyDeliverNetworkResponse() {
+ EXPECT_EQ(request_->status().error(),
+ net::ERR_INTERNET_DISCONNECTED);
+ TestFinished();
+ }
+
+ // DeliverErrorResponse --------------------------------------------------
+
+ void DeliverErrorResponse() {
+ // This test has async steps.
+ PushNextTask(
+ base::Bind(&AppCacheURLRequestJobTest::VerifyDeliverErrorResponse,
+ base::Unretained(this)));
+
+ AppCacheStorage* storage = service_->storage();
+ request_ = empty_context_->CreateRequest(GURL("http://blah/"),
+ net::DEFAULT_PRIORITY,
+ url_request_delegate_.get(),
+ NULL);
+
+ // Setup to create an AppCacheURLRequestJob with orders to deliver
+ // a network response.
+ AppCacheURLRequestJob* mock_job = new AppCacheURLRequestJob(
+ request_.get(), NULL, storage, NULL, false);
+ job_factory_->SetJob(mock_job);
+ mock_job->DeliverErrorResponse();
+ EXPECT_TRUE(mock_job->is_delivering_error_response());
+ EXPECT_FALSE(mock_job->has_been_started());
+
+ // Start the request.
+ request_->Start();
+
+ // The job should have been picked up.
+ EXPECT_FALSE(job_factory_->has_job());
+ // Completion is async.
+ }
+
+ void VerifyDeliverErrorResponse() {
+ EXPECT_EQ(request_->status().error(), net::ERR_FAILED);
+ TestFinished();
+ }
+
+ // DeliverSmallAppCachedResponse --------------------------------------
+ // "Small" being small enough to read completely in a single
+ // request->Read call.
+
+ void DeliverSmallAppCachedResponse() {
+ // This test has several async steps.
+ // 1. Write a small response to response storage.
+ // 2. Use net::URLRequest to retrieve it.
+ // 3. Verify we received what we expected to receive.
+
+ PushNextTask(base::Bind(
+ &AppCacheURLRequestJobTest::VerifyDeliverSmallAppCachedResponse,
+ base::Unretained(this)));
+ PushNextTask(
+ base::Bind(&AppCacheURLRequestJobTest::RequestAppCachedResource,
+ base::Unretained(this), false));
+
+ writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0));
+ written_response_id_ = writer_->response_id();
+ WriteBasicResponse();
+ // Continues async
+ }
+
+ void RequestAppCachedResource(bool start_after_delivery_orders) {
+ AppCacheStorage* storage = service_->storage();
+ request_ = empty_context_->CreateRequest(GURL("http://blah/"),
+ net::DEFAULT_PRIORITY,
+ url_request_delegate_.get(),
+ NULL);
+
+ // Setup to create an AppCacheURLRequestJob with orders to deliver
+ // a network response.
+ scoped_refptr<AppCacheURLRequestJob> job(new AppCacheURLRequestJob(
+ request_.get(), NULL, storage, NULL, false));
+
+ if (start_after_delivery_orders) {
+ job->DeliverAppCachedResponse(
+ GURL(), 0, 111,
+ AppCacheEntry(AppCacheEntry::EXPLICIT, written_response_id_),
+ false);
+ EXPECT_TRUE(job->is_delivering_appcache_response());
+ }
+
+ // Start the request.
+ EXPECT_FALSE(job->has_been_started());
+ job_factory_->SetJob(job.get());
+ request_->Start();
+ EXPECT_FALSE(job_factory_->has_job());
+ EXPECT_TRUE(job->has_been_started());
+
+ if (!start_after_delivery_orders) {
+ job->DeliverAppCachedResponse(
+ GURL(), 0, 111,
+ AppCacheEntry(AppCacheEntry::EXPLICIT, written_response_id_),
+ false);
+ EXPECT_TRUE(job->is_delivering_appcache_response());
+ }
+
+ // Completion is async.
+ }
+
+ void VerifyDeliverSmallAppCachedResponse() {
+ EXPECT_TRUE(request_->status().is_success());
+ EXPECT_TRUE(CompareHttpResponseInfos(
+ write_info_buffer_->http_info.get(),
+ &url_request_delegate_->received_info_));
+ EXPECT_EQ(5, url_request_delegate_->amount_received_);
+ EXPECT_EQ(0, memcmp(kHttpBasicBody,
+ url_request_delegate_->received_data_->data(),
+ strlen(kHttpBasicBody)));
+ TestFinished();
+ }
+
+ // DeliverLargeAppCachedResponse --------------------------------------
+ // "Large" enough to require multiple calls to request->Read to complete.
+
+ void DeliverLargeAppCachedResponse() {
+ // This test has several async steps.
+ // 1. Write a large response to response storage.
+ // 2. Use net::URLRequest to retrieve it.
+ // 3. Verify we received what we expected to receive.
+
+ PushNextTask(base::Bind(
+ &AppCacheURLRequestJobTest::VerifyDeliverLargeAppCachedResponse,
+ base::Unretained(this)));
+ PushNextTask(base::Bind(
+ &AppCacheURLRequestJobTest::RequestAppCachedResource,
+ base::Unretained(this), true));
+
+ writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0));
+ written_response_id_ = writer_->response_id();
+ WriteLargeResponse();
+ // Continues async
+ }
+
+ void WriteLargeResponse() {
+ // 3, 1k blocks
+ static const char kHttpHeaders[] =
+ "HTTP/1.0 200 OK\0Content-Length: 3072\0\0";
+ scoped_refptr<IOBuffer> body(new IOBuffer(kBlockSize * 3));
+ char* p = body->data();
+ for (int i = 0; i < 3; ++i, p += kBlockSize)
+ FillData(i + 1, p, kBlockSize);
+ std::string raw_headers(kHttpHeaders, arraysize(kHttpHeaders));
+ WriteResponse(
+ MakeHttpResponseInfo(raw_headers), body.get(), kBlockSize * 3);
+ }
+
+ void VerifyDeliverLargeAppCachedResponse() {
+ EXPECT_TRUE(request_->status().is_success());
+ EXPECT_TRUE(CompareHttpResponseInfos(
+ write_info_buffer_->http_info.get(),
+ &url_request_delegate_->received_info_));
+ EXPECT_EQ(3072, url_request_delegate_->amount_received_);
+ char* p = url_request_delegate_->received_data_->data();
+ for (int i = 0; i < 3; ++i, p += kBlockSize)
+ EXPECT_TRUE(CheckData(i + 1, p, kBlockSize));
+ TestFinished();
+ }
+
+ // DeliverPartialResponse --------------------------------------
+
+ void DeliverPartialResponse() {
+ // This test has several async steps.
+ // 1. Write a small response to response storage.
+ // 2. Use net::URLRequest to retrieve it a subset using a range request
+ // 3. Verify we received what we expected to receive.
+ PushNextTask(base::Bind(
+ &AppCacheURLRequestJobTest::VerifyDeliverPartialResponse,
+ base::Unretained(this)));
+ PushNextTask(base::Bind(
+ &AppCacheURLRequestJobTest::MakeRangeRequest, base::Unretained(this)));
+ writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0));
+ written_response_id_ = writer_->response_id();
+ WriteBasicResponse();
+ // Continues async
+ }
+
+ void MakeRangeRequest() {
+ AppCacheStorage* storage = service_->storage();
+ request_ = empty_context_->CreateRequest(GURL("http://blah/"),
+ net::DEFAULT_PRIORITY,
+ url_request_delegate_.get(),
+ NULL);
+
+ // Request a range, the 3 middle chars out of 'Hello'
+ net::HttpRequestHeaders extra_headers;
+ extra_headers.SetHeader("Range", "bytes= 1-3");
+ request_->SetExtraRequestHeaders(extra_headers);
+
+ // Create job with orders to deliver an appcached entry.
+ scoped_refptr<AppCacheURLRequestJob> job(new AppCacheURLRequestJob(
+ request_.get(), NULL, storage, NULL, false));
+ job->DeliverAppCachedResponse(
+ GURL(), 0, 111,
+ AppCacheEntry(AppCacheEntry::EXPLICIT, written_response_id_),
+ false);
+ EXPECT_TRUE(job->is_delivering_appcache_response());
+
+ // Start the request.
+ EXPECT_FALSE(job->has_been_started());
+ job_factory_->SetJob(job.get());
+ request_->Start();
+ EXPECT_FALSE(job_factory_->has_job());
+ EXPECT_TRUE(job->has_been_started());
+ // Completion is async.
+ }
+
+ void VerifyDeliverPartialResponse() {
+ EXPECT_TRUE(request_->status().is_success());
+ EXPECT_EQ(3, url_request_delegate_->amount_received_);
+ EXPECT_EQ(0, memcmp(kHttpBasicBody + 1,
+ url_request_delegate_->received_data_->data(),
+ 3));
+ net::HttpResponseHeaders* headers =
+ url_request_delegate_->received_info_.headers.get();
+ EXPECT_EQ(206, headers->response_code());
+ EXPECT_EQ(3, headers->GetContentLength());
+ int64 range_start, range_end, object_size;
+ EXPECT_TRUE(
+ headers->GetContentRange(&range_start, &range_end, &object_size));
+ EXPECT_EQ(1, range_start);
+ EXPECT_EQ(3, range_end);
+ EXPECT_EQ(5, object_size);
+ TestFinished();
+ }
+
+ // CancelRequest --------------------------------------
+
+ void CancelRequest() {
+ // This test has several async steps.
+ // 1. Write a large response to response storage.
+ // 2. Use net::URLRequest to retrieve it.
+ // 3. Cancel the request after data starts coming in.
+
+ PushNextTask(base::Bind(
+ &AppCacheURLRequestJobTest::VerifyCancel, base::Unretained(this)));
+ PushNextTask(base::Bind(
+ &AppCacheURLRequestJobTest::RequestAppCachedResource,
+ base::Unretained(this), true));
+
+ writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0));
+ written_response_id_ = writer_->response_id();
+ WriteLargeResponse();
+
+ url_request_delegate_->kill_after_amount_received_ = kBlockSize;
+ url_request_delegate_->kill_with_io_pending_ = false;
+ // Continues async
+ }
+
+ void VerifyCancel() {
+ EXPECT_EQ(net::URLRequestStatus::CANCELED,
+ request_->status().status());
+ TestFinished();
+ }
+
+ // CancelRequestWithIOPending --------------------------------------
+
+ void CancelRequestWithIOPending() {
+ // This test has several async steps.
+ // 1. Write a large response to response storage.
+ // 2. Use net::URLRequest to retrieve it.
+ // 3. Cancel the request after data starts coming in.
+
+ PushNextTask(base::Bind(
+ &AppCacheURLRequestJobTest::VerifyCancel, base::Unretained(this)));
+ PushNextTask(base::Bind(
+ &AppCacheURLRequestJobTest::RequestAppCachedResource,
+ base::Unretained(this), true));
+
+ writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0));
+ written_response_id_ = writer_->response_id();
+ WriteLargeResponse();
+
+ url_request_delegate_->kill_after_amount_received_ = kBlockSize;
+ url_request_delegate_->kill_with_io_pending_ = true;
+ // Continues async
+ }
+
+
+ // Data members --------------------------------------------------------
+
+ scoped_ptr<base::WaitableEvent> test_finished_event_;
+ scoped_ptr<MockStorageDelegate> storage_delegate_;
+ scoped_ptr<MockAppCacheService> service_;
+ std::stack<std::pair<base::Closure, bool> > task_stack_;
+
+ scoped_ptr<AppCacheResponseReader> reader_;
+ scoped_refptr<HttpResponseInfoIOBuffer> read_info_buffer_;
+ scoped_refptr<IOBuffer> read_buffer_;
+ int expected_read_result_;
+ int reader_deletion_count_down_;
+
+ int64 written_response_id_;
+ scoped_ptr<AppCacheResponseWriter> writer_;
+ scoped_refptr<HttpResponseInfoIOBuffer> write_info_buffer_;
+ scoped_refptr<IOBuffer> write_buffer_;
+ int expected_write_result_;
+ int writer_deletion_count_down_;
+
+ scoped_ptr<MockURLRequestJobFactory> job_factory_;
+ scoped_ptr<net::URLRequestContext> empty_context_;
+ scoped_ptr<net::URLRequest> request_;
+ scoped_ptr<MockURLRequestDelegate> url_request_delegate_;
+
+ static scoped_ptr<base::Thread> io_thread_;
+};
+
+// static
+scoped_ptr<base::Thread> AppCacheURLRequestJobTest::io_thread_;
+
+TEST_F(AppCacheURLRequestJobTest, Basic) {
+ RunTestOnIOThread(&AppCacheURLRequestJobTest::Basic);
+}
+
+TEST_F(AppCacheURLRequestJobTest, DeliveryOrders) {
+ RunTestOnIOThread(&AppCacheURLRequestJobTest::DeliveryOrders);
+}
+
+TEST_F(AppCacheURLRequestJobTest, DeliverNetworkResponse) {
+ RunTestOnIOThread(&AppCacheURLRequestJobTest::DeliverNetworkResponse);
+}
+
+TEST_F(AppCacheURLRequestJobTest, DeliverErrorResponse) {
+ RunTestOnIOThread(&AppCacheURLRequestJobTest::DeliverErrorResponse);
+}
+
+TEST_F(AppCacheURLRequestJobTest, DeliverSmallAppCachedResponse) {
+ RunTestOnIOThread(&AppCacheURLRequestJobTest::DeliverSmallAppCachedResponse);
+}
+
+TEST_F(AppCacheURLRequestJobTest, DeliverLargeAppCachedResponse) {
+ RunTestOnIOThread(&AppCacheURLRequestJobTest::DeliverLargeAppCachedResponse);
+}
+
+TEST_F(AppCacheURLRequestJobTest, DeliverPartialResponse) {
+ RunTestOnIOThread(&AppCacheURLRequestJobTest::DeliverPartialResponse);
+}
+
+TEST_F(AppCacheURLRequestJobTest, CancelRequest) {
+ RunTestOnIOThread(&AppCacheURLRequestJobTest::CancelRequest);
+}
+
+TEST_F(AppCacheURLRequestJobTest, CancelRequestWithIOPending) {
+ RunTestOnIOThread(&AppCacheURLRequestJobTest::CancelRequestWithIOPending);
+}
+
+} // namespace
+
+} // namespace content
diff --git a/chromium/content/browser/appcache/chrome_appcache_service.cc b/chromium/content/browser/appcache/chrome_appcache_service.cc
index 1d913b938b5..c72c21812ac 100644
--- a/chromium/content/browser/appcache/chrome_appcache_service.cc
+++ b/chromium/content/browser/appcache/chrome_appcache_service.cc
@@ -17,7 +17,7 @@ namespace content {
ChromeAppCacheService::ChromeAppCacheService(
quota::QuotaManagerProxy* quota_manager_proxy)
- : AppCacheService(quota_manager_proxy),
+ : AppCacheServiceImpl(quota_manager_proxy),
resource_context_(NULL) {
}
@@ -68,12 +68,15 @@ bool ChromeAppCacheService::CanCreateAppCache(
ChromeAppCacheService::~ChromeAppCacheService() {}
void ChromeAppCacheService::DeleteOnCorrectThread() const {
- if (BrowserThread::IsMessageLoopValid(BrowserThread::IO) &&
- !BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+ if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+ delete this;
+ return;
+ }
+ if (BrowserThread::IsMessageLoopValid(BrowserThread::IO)) {
BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, this);
return;
}
- delete this;
+ // Better to leak than crash on shutdown.
}
} // namespace content
diff --git a/chromium/content/browser/appcache/chrome_appcache_service.h b/chromium/content/browser/appcache/chrome_appcache_service.h
index 3c787e5360c..2f33d9b0f24 100644
--- a/chromium/content/browser/appcache/chrome_appcache_service.h
+++ b/chromium/content/browser/appcache/chrome_appcache_service.h
@@ -10,7 +10,7 @@
#include "base/sequenced_task_runner_helpers.h"
#include "content/common/content_export.h"
#include "webkit/browser/appcache/appcache_policy.h"
-#include "webkit/browser/appcache/appcache_service.h"
+#include "webkit/browser/appcache/appcache_service_impl.h"
#include "webkit/browser/quota/special_storage_policy.h"
namespace base {
@@ -26,7 +26,7 @@ class ResourceContext;
struct ChromeAppCacheServiceDeleter;
-// An AppCacheService subclass used by the chrome. There is an instance
+// An AppCacheServiceImpl subclass used by the chrome. There is an instance
// associated with each BrowserContext. This derivation adds refcounting
// semantics since a browser context has multiple URLRequestContexts which refer
// to the same object, and those URLRequestContexts are refcounted independently
@@ -35,18 +35,19 @@ struct ChromeAppCacheServiceDeleter;
// All methods, except the ctor, are expected to be called on
// the IO thread (unless specifically called out in doc comments).
//
-// TODO(dpranke): Fix dependencies on AppCacheService so that we don't have
-// to worry about clients calling AppCacheService methods.
+// TODO(dpranke): Fix dependencies on AppCacheServiceImpl so that we don't have
+// to worry about clients calling AppCacheServiceImpl methods.
class CONTENT_EXPORT ChromeAppCacheService
: public base::RefCountedThreadSafe<ChromeAppCacheService,
ChromeAppCacheServiceDeleter>,
- NON_EXPORTED_BASE(public appcache::AppCacheService),
+ NON_EXPORTED_BASE(public appcache::AppCacheServiceImpl),
NON_EXPORTED_BASE(public appcache::AppCachePolicy) {
public:
explicit ChromeAppCacheService(quota::QuotaManagerProxy* proxy);
+ // If |cache_path| is empty we will use in-memory structs.
void InitializeOnIOThread(
- const base::FilePath& cache_path, // May be empty to use in-memory structs.
+ const base::FilePath& cache_path,
ResourceContext* resource_context,
net::URLRequestContextGetter* request_context_getter,
scoped_refptr<quota::SpecialStoragePolicy> special_storage_policy);
diff --git a/chromium/content/browser/appcache/chrome_appcache_service_unittest.cc b/chromium/content/browser/appcache/chrome_appcache_service_unittest.cc
index a4e895c6dd0..1800d0bf328 100644
--- a/chromium/content/browser/appcache/chrome_appcache_service_unittest.cc
+++ b/chromium/content/browser/appcache/chrome_appcache_service_unittest.cc
@@ -10,18 +10,16 @@
#include "content/browser/appcache/chrome_appcache_service.h"
#include "content/browser/browser_thread_impl.h"
#include "content/public/browser/resource_context.h"
+#include "content/public/test/mock_special_storage_policy.h"
#include "content/public/test/test_browser_context.h"
+#include "content/test/appcache_test_helper.h"
#include "net/url_request/url_request_context_getter.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "webkit/browser/appcache/appcache_database.h"
#include "webkit/browser/appcache/appcache_storage_impl.h"
-#include "webkit/browser/appcache/appcache_test_helper.h"
-#include "webkit/browser/quota/mock_special_storage_policy.h"
#include <set>
-using appcache::AppCacheTestHelper;
-
namespace content {
namespace {
const base::FilePath::CharType kTestingAppCacheDirname[] =
@@ -63,8 +61,7 @@ class MockURLRequestContextGetter : public net::URLRequestContextGetter {
class ChromeAppCacheServiceTest : public testing::Test {
public:
ChromeAppCacheServiceTest()
- : message_loop_(base::MessageLoop::TYPE_IO),
- kProtectedManifestURL(kProtectedManifest),
+ : kProtectedManifestURL(kProtectedManifest),
kNormalManifestURL(kNormalManifest),
kSessionOnlyManifestURL(kSessionOnlyManifest),
file_thread_(BrowserThread::FILE, &message_loop_),
@@ -74,12 +71,12 @@ class ChromeAppCacheServiceTest : public testing::Test {
io_thread_(BrowserThread::IO, &message_loop_) {}
protected:
- scoped_refptr<ChromeAppCacheService> CreateAppCacheService(
+ scoped_refptr<ChromeAppCacheService> CreateAppCacheServiceImpl(
const base::FilePath& appcache_path,
bool init_storage);
void InsertDataIntoAppCache(ChromeAppCacheService* appcache_service);
- base::MessageLoop message_loop_;
+ base::MessageLoopForIO message_loop_;
base::ScopedTempDir temp_dir_;
const GURL kProtectedManifestURL;
const GURL kNormalManifestURL;
@@ -94,13 +91,13 @@ class ChromeAppCacheServiceTest : public testing::Test {
};
scoped_refptr<ChromeAppCacheService>
-ChromeAppCacheServiceTest::CreateAppCacheService(
+ChromeAppCacheServiceTest::CreateAppCacheServiceImpl(
const base::FilePath& appcache_path,
bool init_storage) {
scoped_refptr<ChromeAppCacheService> appcache_service =
new ChromeAppCacheService(NULL);
- scoped_refptr<quota::MockSpecialStoragePolicy> mock_policy =
- new quota::MockSpecialStoragePolicy;
+ scoped_refptr<MockSpecialStoragePolicy> mock_policy =
+ new MockSpecialStoragePolicy;
mock_policy->AddProtected(kProtectedManifestURL.GetOrigin());
mock_policy->AddSessionOnly(kSessionOnlyManifestURL.GetOrigin());
scoped_refptr<MockURLRequestContextGetter> mock_request_context_getter =
@@ -153,7 +150,7 @@ TEST_F(ChromeAppCacheServiceTest, KeepOnDestruction) {
// Create a ChromeAppCacheService and insert data into it
scoped_refptr<ChromeAppCacheService> appcache_service =
- CreateAppCacheService(appcache_path, true);
+ CreateAppCacheServiceImpl(appcache_path, true);
ASSERT_TRUE(base::PathExists(appcache_path));
ASSERT_TRUE(base::PathExists(appcache_path.AppendASCII("Index")));
InsertDataIntoAppCache(appcache_service.get());
@@ -163,7 +160,7 @@ TEST_F(ChromeAppCacheServiceTest, KeepOnDestruction) {
message_loop_.RunUntilIdle();
// Recreate the appcache (for reading the data back)
- appcache_service = CreateAppCacheService(appcache_path, false);
+ appcache_service = CreateAppCacheServiceImpl(appcache_path, false);
// The directory is still there
ASSERT_TRUE(base::PathExists(appcache_path));
@@ -190,7 +187,7 @@ TEST_F(ChromeAppCacheServiceTest, SaveSessionState) {
// Create a ChromeAppCacheService and insert data into it
scoped_refptr<ChromeAppCacheService> appcache_service =
- CreateAppCacheService(appcache_path, true);
+ CreateAppCacheServiceImpl(appcache_path, true);
ASSERT_TRUE(base::PathExists(appcache_path));
ASSERT_TRUE(base::PathExists(appcache_path.AppendASCII("Index")));
InsertDataIntoAppCache(appcache_service.get());
@@ -203,7 +200,7 @@ TEST_F(ChromeAppCacheServiceTest, SaveSessionState) {
message_loop_.RunUntilIdle();
// Recreate the appcache (for reading the data back)
- appcache_service = CreateAppCacheService(appcache_path, false);
+ appcache_service = CreateAppCacheServiceImpl(appcache_path, false);
// The directory is still there
ASSERT_TRUE(base::PathExists(appcache_path));
diff --git a/chromium/content/browser/appcache/manifest_parser_unittest.cc b/chromium/content/browser/appcache/manifest_parser_unittest.cc
new file mode 100644
index 00000000000..8a44376d774
--- /dev/null
+++ b/chromium/content/browser/appcache/manifest_parser_unittest.cc
@@ -0,0 +1,524 @@
+// 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 <string>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+#include "webkit/browser/appcache/manifest_parser.h"
+
+using appcache::Manifest;
+using appcache::NamespaceVector;
+using appcache::APPCACHE_FALLBACK_NAMESPACE;
+using appcache::APPCACHE_INTERCEPT_NAMESPACE;
+using appcache::APPCACHE_NETWORK_NAMESPACE;
+using appcache::PARSE_MANIFEST_ALLOWING_INTERCEPTS;
+using appcache::PARSE_MANIFEST_PER_STANDARD;
+
+namespace content {
+
+class AppCacheManifestParserTest : public testing::Test {
+};
+
+TEST(AppCacheManifestParserTest, NoData) {
+ GURL url;
+ Manifest manifest;
+ EXPECT_FALSE(ParseManifest(url, "", 0,
+ PARSE_MANIFEST_ALLOWING_INTERCEPTS, manifest));
+ EXPECT_FALSE(ParseManifest(url, "CACHE MANIFEST\r", 0, // Len is 0.
+ PARSE_MANIFEST_ALLOWING_INTERCEPTS, manifest));
+}
+
+TEST(AppCacheManifestParserTest, CheckSignature) {
+ GURL url;
+ Manifest manifest;
+
+ const std::string kBadSignatures[] = {
+ "foo",
+ "CACHE MANIFEST;V2\r", // not followed by whitespace
+ "CACHE MANIFEST#bad\r", // no whitespace before comment
+ "cache manifest ", // wrong case
+ "#CACHE MANIFEST\r", // comment
+ "xCACHE MANIFEST\n", // bad first char
+ " CACHE MANIFEST\r", // begins with whitespace
+ "\xEF\xBE\xBF" "CACHE MANIFEST\r", // bad UTF-8 BOM value
+ };
+
+ for (size_t i = 0; i < arraysize(kBadSignatures); ++i) {
+ const std::string bad = kBadSignatures[i];
+ EXPECT_FALSE(ParseManifest(url, bad.c_str(), bad.length(),
+ PARSE_MANIFEST_ALLOWING_INTERCEPTS, manifest));
+ }
+
+ const std::string kGoodSignatures[] = {
+ "CACHE MANIFEST",
+ "CACHE MANIFEST ",
+ "CACHE MANIFEST\r",
+ "CACHE MANIFEST\n",
+ "CACHE MANIFEST\r\n",
+ "CACHE MANIFEST\t# ignore me\r",
+ "CACHE MANIFEST ignore\r\n",
+ "CHROMIUM CACHE MANIFEST\r\n",
+ "\xEF\xBB\xBF" "CACHE MANIFEST \r\n", // BOM present
+ };
+
+ for (size_t i = 0; i < arraysize(kGoodSignatures); ++i) {
+ const std::string good = kGoodSignatures[i];
+ EXPECT_TRUE(ParseManifest(url, good.c_str(), good.length(),
+ PARSE_MANIFEST_ALLOWING_INTERCEPTS, manifest));
+ }
+}
+
+TEST(AppCacheManifestParserTest, NoManifestUrl) {
+ Manifest manifest;
+ const std::string kData("CACHE MANIFEST\r"
+ "relative/tobase.com\r"
+ "http://absolute.com/addme.com");
+ const GURL kUrl;
+ EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(),
+ PARSE_MANIFEST_ALLOWING_INTERCEPTS, manifest));
+ EXPECT_TRUE(manifest.explicit_urls.empty());
+ EXPECT_TRUE(manifest.fallback_namespaces.empty());
+ EXPECT_TRUE(manifest.online_whitelist_namespaces.empty());
+ EXPECT_FALSE(manifest.online_whitelist_all);
+}
+
+TEST(AppCacheManifestParserTest, ExplicitUrls) {
+ Manifest manifest;
+ const GURL kUrl("http://www.foo.com");
+ const std::string kData("CACHE MANIFEST\r"
+ "relative/one\r"
+ "# some comment\r"
+ "http://www.foo.com/two#strip\r\n"
+ "NETWORK:\r"
+ " \t CACHE:\r"
+ "HTTP://www.diff.com/three\r"
+ "FALLBACK:\r"
+ " \t # another comment with leading whitespace\n"
+ "IGNORE:\r"
+ "http://www.foo.com/ignore\r"
+ "CACHE: \r"
+ "garbage:#!@\r"
+ "https://www.foo.com/diffscheme \t \r"
+ " \t relative/four#stripme\n\r"
+ "*\r");
+
+ EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(),
+ PARSE_MANIFEST_ALLOWING_INTERCEPTS, manifest));
+ EXPECT_TRUE(manifest.fallback_namespaces.empty());
+ EXPECT_TRUE(manifest.online_whitelist_namespaces.empty());
+ EXPECT_FALSE(manifest.online_whitelist_all);
+
+ base::hash_set<std::string> urls = manifest.explicit_urls;
+ const size_t kExpected = 5;
+ ASSERT_EQ(kExpected, urls.size());
+ EXPECT_TRUE(urls.find("http://www.foo.com/relative/one") != urls.end());
+ EXPECT_TRUE(urls.find("http://www.foo.com/two") != urls.end());
+ EXPECT_TRUE(urls.find("http://www.diff.com/three") != urls.end());
+ EXPECT_TRUE(urls.find("http://www.foo.com/relative/four") != urls.end());
+
+ // Wildcard is treated as a relative URL in explicit section.
+ EXPECT_TRUE(urls.find("http://www.foo.com/*") != urls.end());
+
+ // We should get the same results with intercepts disallowed.
+ manifest = Manifest();
+ EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(),
+ PARSE_MANIFEST_PER_STANDARD, manifest));
+ EXPECT_TRUE(manifest.fallback_namespaces.empty());
+ EXPECT_TRUE(manifest.online_whitelist_namespaces.empty());
+ EXPECT_FALSE(manifest.online_whitelist_all);
+
+ urls = manifest.explicit_urls;
+ ASSERT_EQ(kExpected, urls.size());
+ EXPECT_TRUE(urls.find("http://www.foo.com/relative/one") != urls.end());
+ EXPECT_TRUE(urls.find("http://www.foo.com/two") != urls.end());
+ EXPECT_TRUE(urls.find("http://www.diff.com/three") != urls.end());
+ EXPECT_TRUE(urls.find("http://www.foo.com/relative/four") != urls.end());
+
+ // Wildcard is treated as a relative URL in explicit section.
+ EXPECT_TRUE(urls.find("http://www.foo.com/*") != urls.end());
+}
+
+TEST(AppCacheManifestParserTest, WhitelistUrls) {
+ Manifest manifest;
+ const GURL kUrl("http://www.bar.com");
+ const std::string kData("CACHE MANIFEST\r"
+ "NETWORK:\r"
+ "relative/one\r"
+ "# a comment\r"
+ "http://www.bar.com/two\r"
+ "HTTP://www.diff.com/three#strip\n\r"
+ "FALLBACK:\r"
+ "garbage\r"
+ "UNKNOWN:\r"
+ "http://www.bar.com/ignore\r"
+ "CACHE:\r"
+ "NETWORK:\r"
+ "https://www.wrongscheme.com\n"
+ "relative/four#stripref \t \r"
+ "http://www.five.com\r\n"
+ "*foo\r");
+
+ EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(),
+ PARSE_MANIFEST_ALLOWING_INTERCEPTS, manifest));
+ EXPECT_TRUE(manifest.explicit_urls.empty());
+ EXPECT_TRUE(manifest.fallback_namespaces.empty());
+ EXPECT_TRUE(manifest.intercept_namespaces.empty());
+ EXPECT_FALSE(manifest.online_whitelist_all);
+
+ const NamespaceVector& online = manifest.online_whitelist_namespaces;
+ const size_t kExpected = 6;
+ ASSERT_EQ(kExpected, online.size());
+ EXPECT_EQ(APPCACHE_NETWORK_NAMESPACE, online[0].type);
+ EXPECT_FALSE(online[0].is_pattern);
+ EXPECT_TRUE(online[0].target_url.is_empty());
+ EXPECT_EQ(GURL("http://www.bar.com/relative/one"), online[0].namespace_url);
+ EXPECT_EQ(GURL("http://www.bar.com/two"), online[1].namespace_url);
+ EXPECT_EQ(GURL("http://www.diff.com/three"), online[2].namespace_url);
+ EXPECT_EQ(GURL("http://www.bar.com/relative/four"), online[3].namespace_url);
+ EXPECT_EQ(GURL("http://www.five.com"), online[4].namespace_url);
+ EXPECT_EQ(GURL("http://www.bar.com/*foo"), online[5].namespace_url);
+}
+
+TEST(AppCacheManifestParserTest, FallbackUrls) {
+ Manifest manifest;
+ const GURL kUrl("http://glorp.com");
+ const std::string kData("CACHE MANIFEST\r"
+ "# a comment\r"
+ "CACHE:\r"
+ "NETWORK:\r"
+ "UNKNOWN:\r"
+ "FALLBACK:\r"
+ "relative/one \t \t http://glorp.com/onefb \t \r"
+ "*\r"
+ "https://glorp.com/wrong http://glorp.com/wrongfb\r"
+ "http://glorp.com/two#strip relative/twofb\r"
+ "HTTP://glorp.com/three relative/threefb#strip\n"
+ "http://glorp.com/three http://glorp.com/three-dup\r"
+ "http://glorp.com/solo \t \r\n"
+ "http://diff.com/ignore http://glorp.com/wronghost\r"
+ "http://glorp.com/wronghost http://diff.com/ohwell\r"
+ "relative/badscheme ftp://glorp.com/ignored\r"
+ "garbage\r\n"
+ "CACHE:\r"
+ "# only fallback urls in this test\r"
+ "FALLBACK:\n"
+ "relative/four#strip relative/fourfb#strip\r"
+ "http://www.glorp.com/notsame relative/skipped\r");
+
+ EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(),
+ PARSE_MANIFEST_ALLOWING_INTERCEPTS, manifest));
+ EXPECT_TRUE(manifest.explicit_urls.empty());
+ EXPECT_TRUE(manifest.online_whitelist_namespaces.empty());
+ EXPECT_FALSE(manifest.online_whitelist_all);
+
+ const NamespaceVector& fallbacks = manifest.fallback_namespaces;
+ const size_t kExpected = 5;
+ ASSERT_EQ(kExpected, fallbacks.size());
+ EXPECT_EQ(APPCACHE_FALLBACK_NAMESPACE, fallbacks[0].type);
+ EXPECT_EQ(APPCACHE_FALLBACK_NAMESPACE, fallbacks[1].type);
+ EXPECT_EQ(APPCACHE_FALLBACK_NAMESPACE, fallbacks[2].type);
+ EXPECT_EQ(APPCACHE_FALLBACK_NAMESPACE, fallbacks[3].type);
+ EXPECT_EQ(APPCACHE_FALLBACK_NAMESPACE, fallbacks[4].type);
+ EXPECT_EQ(GURL("http://glorp.com/relative/one"),
+ fallbacks[0].namespace_url);
+ EXPECT_EQ(GURL("http://glorp.com/onefb"),
+ fallbacks[0].target_url);
+ EXPECT_EQ(GURL("http://glorp.com/two"),
+ fallbacks[1].namespace_url);
+ EXPECT_EQ(GURL("http://glorp.com/relative/twofb"),
+ fallbacks[1].target_url);
+ EXPECT_EQ(GURL("http://glorp.com/three"),
+ fallbacks[2].namespace_url);
+ EXPECT_EQ(GURL("http://glorp.com/relative/threefb"),
+ fallbacks[2].target_url);
+ EXPECT_EQ(GURL("http://glorp.com/three"), // duplicates are stored
+ fallbacks[3].namespace_url);
+ EXPECT_EQ(GURL("http://glorp.com/three-dup"),
+ fallbacks[3].target_url);
+ EXPECT_EQ(GURL("http://glorp.com/relative/four"),
+ fallbacks[4].namespace_url);
+ EXPECT_EQ(GURL("http://glorp.com/relative/fourfb"),
+ fallbacks[4].target_url);
+
+ EXPECT_TRUE(manifest.intercept_namespaces.empty());
+}
+
+TEST(AppCacheManifestParserTest, FallbackUrlsWithPort) {
+ Manifest manifest;
+ const GURL kUrl("http://www.portme.com:1234");
+ const std::string kData("CACHE MANIFEST\r"
+ "FALLBACK:\r"
+ "http://www.portme.com:1234/one relative/onefb\r"
+ "HTTP://www.portme.com:9876/wrong http://www.portme.com:1234/ignore\r"
+ "http://www.portme.com:1234/stillwrong http://www.portme.com:42/boo\r"
+ "relative/two relative/twofb\r"
+ "http://www.portme.com:1234/three HTTP://www.portme.com:1234/threefb\r"
+ "http://www.portme.com/noport http://www.portme.com:1234/skipped\r"
+ "http://www.portme.com:1234/skipme http://www.portme.com/noport\r");
+
+ EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(),
+ PARSE_MANIFEST_ALLOWING_INTERCEPTS, manifest));
+ EXPECT_TRUE(manifest.explicit_urls.empty());
+ EXPECT_TRUE(manifest.online_whitelist_namespaces.empty());
+ EXPECT_FALSE(manifest.online_whitelist_all);
+
+ const NamespaceVector& fallbacks = manifest.fallback_namespaces;
+ const size_t kExpected = 3;
+ ASSERT_EQ(kExpected, fallbacks.size());
+ EXPECT_EQ(APPCACHE_FALLBACK_NAMESPACE, fallbacks[0].type);
+ EXPECT_EQ(APPCACHE_FALLBACK_NAMESPACE, fallbacks[1].type);
+ EXPECT_EQ(APPCACHE_FALLBACK_NAMESPACE, fallbacks[2].type);
+ EXPECT_EQ(GURL("http://www.portme.com:1234/one"),
+ fallbacks[0].namespace_url);
+ EXPECT_EQ(GURL("http://www.portme.com:1234/relative/onefb"),
+ fallbacks[0].target_url);
+ EXPECT_EQ(GURL("http://www.portme.com:1234/relative/two"),
+ fallbacks[1].namespace_url);
+ EXPECT_EQ(GURL("http://www.portme.com:1234/relative/twofb"),
+ fallbacks[1].target_url);
+ EXPECT_EQ(GURL("http://www.portme.com:1234/three"),
+ fallbacks[2].namespace_url);
+ EXPECT_EQ(GURL("http://www.portme.com:1234/threefb"),
+ fallbacks[2].target_url);
+
+ EXPECT_TRUE(manifest.intercept_namespaces.empty());
+}
+
+TEST(AppCacheManifestParserTest, InterceptUrls) {
+ Manifest manifest;
+ const GURL kUrl("http://www.portme.com:1234");
+ const std::string kData("CHROMIUM CACHE MANIFEST\r"
+ "CHROMIUM-INTERCEPT:\r"
+ "http://www.portme.com:1234/one return relative/int1\r"
+ "HTTP://www.portme.com:9/wrong return http://www.portme.com:1234/ignore\r"
+ "http://www.portme.com:1234/wrong return http://www.portme.com:9/boo\r"
+ "relative/two return relative/int2\r"
+ "relative/three wrong relative/threefb\r"
+ "http://www.portme.com:1234/three return HTTP://www.portme.com:1234/int3\r"
+ "http://www.portme.com/noport return http://www.portme.com:1234/skipped\r"
+ "http://www.portme.com:1234/skipme return http://www.portme.com/noport\r"
+ "relative/wrong/again missing/intercept_type\r");
+
+ EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(),
+ PARSE_MANIFEST_ALLOWING_INTERCEPTS, manifest));
+ EXPECT_TRUE(manifest.fallback_namespaces.empty());
+ EXPECT_TRUE(manifest.explicit_urls.empty());
+ EXPECT_TRUE(manifest.online_whitelist_namespaces.empty());
+ EXPECT_FALSE(manifest.online_whitelist_all);
+
+ const NamespaceVector& intercepts = manifest.intercept_namespaces;
+ const size_t kExpected = 3;
+ ASSERT_EQ(kExpected, intercepts.size());
+ EXPECT_EQ(APPCACHE_INTERCEPT_NAMESPACE, intercepts[0].type);
+ EXPECT_EQ(APPCACHE_INTERCEPT_NAMESPACE, intercepts[1].type);
+ EXPECT_EQ(APPCACHE_INTERCEPT_NAMESPACE, intercepts[2].type);
+ EXPECT_EQ(GURL("http://www.portme.com:1234/one"),
+ intercepts[0].namespace_url);
+ EXPECT_EQ(GURL("http://www.portme.com:1234/relative/int1"),
+ intercepts[0].target_url);
+ EXPECT_EQ(GURL("http://www.portme.com:1234/relative/two"),
+ intercepts[1].namespace_url);
+ EXPECT_EQ(GURL("http://www.portme.com:1234/relative/int2"),
+ intercepts[1].target_url);
+ EXPECT_EQ(GURL("http://www.portme.com:1234/three"),
+ intercepts[2].namespace_url);
+ EXPECT_EQ(GURL("http://www.portme.com:1234/int3"),
+ intercepts[2].target_url);
+
+ // Disallow intercepts ths time.
+ manifest = Manifest();
+ EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(),
+ PARSE_MANIFEST_PER_STANDARD, manifest));
+ EXPECT_TRUE(manifest.fallback_namespaces.empty());
+ EXPECT_TRUE(manifest.explicit_urls.empty());
+ EXPECT_TRUE(manifest.online_whitelist_namespaces.empty());
+ EXPECT_TRUE(manifest.intercept_namespaces.empty());
+ EXPECT_FALSE(manifest.online_whitelist_all);
+}
+
+TEST(AppCacheManifestParserTest, ComboUrls) {
+ Manifest manifest;
+ const GURL kUrl("http://combo.com:42");
+ const std::string kData("CACHE MANIFEST\r"
+ "relative/explicit-1\r"
+ "# some comment\r"
+ "http://combo.com:99/explicit-2#strip\r"
+ "NETWORK:\r"
+ "http://combo.com/whitelist-1\r"
+ "HTTP://www.diff.com/whitelist-2#strip\r"
+ "*\r"
+ "CACHE:\n\r"
+ "http://www.diff.com/explicit-3\r"
+ "FALLBACK:\r"
+ "http://combo.com:42/fallback-1 http://combo.com:42/fallback-1b\r"
+ "relative/fallback-2 relative/fallback-2b\r"
+ "UNKNOWN:\r\n"
+ "http://combo.com/ignoreme\r"
+ "relative/still-ignored\r"
+ "NETWORK:\r\n"
+ "relative/whitelist-3#strip\r"
+ "http://combo.com:99/whitelist-4\r");
+ EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(),
+ PARSE_MANIFEST_ALLOWING_INTERCEPTS, manifest));
+ EXPECT_TRUE(manifest.online_whitelist_all);
+
+ base::hash_set<std::string> urls = manifest.explicit_urls;
+ size_t expected = 3;
+ ASSERT_EQ(expected, urls.size());
+ EXPECT_TRUE(urls.find("http://combo.com:42/relative/explicit-1") !=
+ urls.end());
+ EXPECT_TRUE(urls.find("http://combo.com:99/explicit-2") != urls.end());
+ EXPECT_TRUE(urls.find("http://www.diff.com/explicit-3") != urls.end());
+
+ const NamespaceVector& online = manifest.online_whitelist_namespaces;
+ expected = 4;
+ ASSERT_EQ(expected, online.size());
+ EXPECT_EQ(GURL("http://combo.com/whitelist-1"),
+ online[0].namespace_url);
+ EXPECT_EQ(GURL("http://www.diff.com/whitelist-2"),
+ online[1].namespace_url);
+ EXPECT_EQ(GURL("http://combo.com:42/relative/whitelist-3"),
+ online[2].namespace_url);
+ EXPECT_EQ(GURL("http://combo.com:99/whitelist-4"),
+ online[3].namespace_url);
+
+ const NamespaceVector& fallbacks = manifest.fallback_namespaces;
+ expected = 2;
+ ASSERT_EQ(expected, fallbacks.size());
+ EXPECT_EQ(APPCACHE_FALLBACK_NAMESPACE, fallbacks[0].type);
+ EXPECT_EQ(APPCACHE_FALLBACK_NAMESPACE, fallbacks[1].type);
+ EXPECT_EQ(GURL("http://combo.com:42/fallback-1"),
+ fallbacks[0].namespace_url);
+ EXPECT_EQ(GURL("http://combo.com:42/fallback-1b"),
+ fallbacks[0].target_url);
+ EXPECT_EQ(GURL("http://combo.com:42/relative/fallback-2"),
+ fallbacks[1].namespace_url);
+ EXPECT_EQ(GURL("http://combo.com:42/relative/fallback-2b"),
+ fallbacks[1].target_url);
+
+ EXPECT_TRUE(manifest.intercept_namespaces.empty());
+}
+
+TEST(AppCacheManifestParserTest, UnusualUtf8) {
+ Manifest manifest;
+ const GURL kUrl("http://bad.com");
+ const std::string kData("CACHE MANIFEST\r"
+ "\xC0" "invalidutf8\r"
+ "nonbmp" "\xF1\x84\xAB\xBC\r");
+ EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(),
+ PARSE_MANIFEST_ALLOWING_INTERCEPTS, manifest));
+ base::hash_set<std::string> urls = manifest.explicit_urls;
+ EXPECT_TRUE(urls.find("http://bad.com/%EF%BF%BDinvalidutf8") != urls.end());
+ EXPECT_TRUE(urls.find("http://bad.com/nonbmp%F1%84%AB%BC") != urls.end());
+}
+
+TEST(AppCacheManifestParserTest, IgnoreAfterSpace) {
+ Manifest manifest;
+ const GURL kUrl("http://smorg.borg");
+ const std::string kData(
+ "CACHE MANIFEST\r"
+ "resource.txt this stuff after the white space should be ignored\r");
+ EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(),
+ PARSE_MANIFEST_ALLOWING_INTERCEPTS, manifest));
+
+ base::hash_set<std::string> urls = manifest.explicit_urls;
+ EXPECT_TRUE(urls.find("http://smorg.borg/resource.txt") != urls.end());
+}
+
+TEST(AppCacheManifestParserTest, DifferentOriginUrlWithSecureScheme) {
+ Manifest manifest;
+ const GURL kUrl("https://www.foo.com");
+ const std::string kData("CACHE MANIFEST\r"
+ "CACHE: \r"
+ "relative/secureschemesameorigin\r"
+ "https://www.foo.com/secureschemesameorigin\r"
+ "http://www.xyz.com/secureschemedifforigin\r"
+ "https://www.xyz.com/secureschemedifforigin\r");
+
+ EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(),
+ PARSE_MANIFEST_ALLOWING_INTERCEPTS, manifest));
+ EXPECT_TRUE(manifest.fallback_namespaces.empty());
+ EXPECT_TRUE(manifest.online_whitelist_namespaces.empty());
+
+ base::hash_set<std::string> urls = manifest.explicit_urls;
+ const size_t kExpected = 3;
+ ASSERT_EQ(kExpected, urls.size());
+ EXPECT_TRUE(urls.find("https://www.foo.com/relative/secureschemesameorigin")
+ != urls.end());
+ EXPECT_TRUE(urls.find("https://www.foo.com/secureschemesameorigin") !=
+ urls.end());
+ EXPECT_FALSE(urls.find("http://www.xyz.com/secureschemedifforigin") !=
+ urls.end());
+ EXPECT_TRUE(urls.find("https://www.xyz.com/secureschemedifforigin") !=
+ urls.end());
+}
+
+TEST(AppCacheManifestParserTest, PatternMatching) {
+ const GURL kUrl("http://foo.com/manifest");
+ const std::string kManifestBody(
+ "CACHE MANIFEST\r"
+ "CACHE: \r"
+ "http://foo.com/page.html\r"
+ "CHROMIUM-INTERCEPT:\r"
+ "http://foo.com/intercept_prefix return /prefix\r"
+ "http://foo.com/intercept_pattern return /pattern isPattern\r"
+ "http://foo.com/*/intercept_pattern?query return /pattern isPattern\r"
+ "FALLBACK:\r"
+ "http://foo.com/fallback_prefix /prefix wrongAnnotation\r"
+ "http://foo.com/fallback_pattern* /pattern\tisPattern \r"
+ "NETWORK:\r"
+ "*\r"
+ "isPattern\r" // should not be interpretted as a pattern
+ "http://foo.com/network_pattern* isPattern\r");
+
+
+ Manifest manifest;
+ EXPECT_TRUE(ParseManifest(kUrl, kManifestBody.c_str(),
+ kManifestBody.length(),
+ PARSE_MANIFEST_ALLOWING_INTERCEPTS, manifest));
+ EXPECT_TRUE(manifest.online_whitelist_all);
+ EXPECT_EQ(1u, manifest.explicit_urls.size());
+ EXPECT_EQ(3u, manifest.intercept_namespaces.size());
+ EXPECT_EQ(2u, manifest.fallback_namespaces.size());
+ EXPECT_EQ(2u, manifest.online_whitelist_namespaces.size());
+ EXPECT_EQ(APPCACHE_INTERCEPT_NAMESPACE,
+ manifest.intercept_namespaces[0].type);
+ EXPECT_EQ(APPCACHE_FALLBACK_NAMESPACE, manifest.fallback_namespaces[0].type);
+ EXPECT_EQ(APPCACHE_NETWORK_NAMESPACE,
+ manifest.online_whitelist_namespaces[0].type);
+ EXPECT_FALSE(manifest.intercept_namespaces[0].is_pattern);
+ EXPECT_TRUE(manifest.intercept_namespaces[1].is_pattern);
+ EXPECT_TRUE(manifest.intercept_namespaces[2].is_pattern);
+ EXPECT_FALSE(manifest.fallback_namespaces[0].is_pattern);
+ EXPECT_TRUE(manifest.fallback_namespaces[1].is_pattern);
+ EXPECT_FALSE(manifest.online_whitelist_namespaces[0].is_pattern);
+ EXPECT_TRUE(manifest.online_whitelist_namespaces[1].is_pattern);
+ EXPECT_EQ(
+ GURL("http://foo.com/*/intercept_pattern?query"),
+ manifest.intercept_namespaces[2].namespace_url);
+ EXPECT_EQ(
+ GURL("http://foo.com/pattern"),
+ manifest.intercept_namespaces[2].target_url);
+ EXPECT_EQ(
+ GURL("http://foo.com/fallback_pattern*"),
+ manifest.fallback_namespaces[1].namespace_url);
+ EXPECT_EQ(
+ GURL("http://foo.com/pattern"),
+ manifest.fallback_namespaces[1].target_url);
+ EXPECT_EQ(
+ GURL("http://foo.com/isPattern"),
+ manifest.online_whitelist_namespaces[0].namespace_url);
+ EXPECT_EQ(
+ GURL(),
+ manifest.online_whitelist_namespaces[0].target_url);
+ EXPECT_EQ(
+ GURL("http://foo.com/network_pattern*"),
+ manifest.online_whitelist_namespaces[1].namespace_url);
+ EXPECT_EQ(
+ GURL(),
+ manifest.online_whitelist_namespaces[1].target_url);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/appcache/mock_appcache_policy.cc b/chromium/content/browser/appcache/mock_appcache_policy.cc
new file mode 100644
index 00000000000..2fdfa9c9c6a
--- /dev/null
+++ b/chromium/content/browser/appcache/mock_appcache_policy.cc
@@ -0,0 +1,28 @@
+// 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/appcache/mock_appcache_policy.h"
+
+namespace content {
+
+MockAppCachePolicy::MockAppCachePolicy()
+ : can_load_return_value_(true), can_create_return_value_(true) {
+}
+
+MockAppCachePolicy::~MockAppCachePolicy() {
+}
+
+bool MockAppCachePolicy::CanLoadAppCache(const GURL& manifest_url,
+ const GURL& first_party) {
+ requested_manifest_url_ = manifest_url;
+ return can_load_return_value_;
+}
+
+bool MockAppCachePolicy::CanCreateAppCache(const GURL& manifest_url,
+ const GURL& first_party) {
+ requested_manifest_url_ = manifest_url;
+ return can_create_return_value_;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/appcache/mock_appcache_policy.h b/chromium/content/browser/appcache/mock_appcache_policy.h
new file mode 100644
index 00000000000..bc7576412b9
--- /dev/null
+++ b/chromium/content/browser/appcache/mock_appcache_policy.h
@@ -0,0 +1,31 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_APPCACHE_MOCK_APPCACHE_POLICY_H_
+#define CONTENT_BROWSER_APPCACHE_MOCK_APPCACHE_POLICY_H_
+
+#include "base/compiler_specific.h"
+#include "url/gurl.h"
+#include "webkit/browser/appcache/appcache_policy.h"
+
+namespace content {
+
+class MockAppCachePolicy : public appcache::AppCachePolicy {
+ public:
+ MockAppCachePolicy();
+ virtual ~MockAppCachePolicy();
+
+ virtual bool CanLoadAppCache(const GURL& manifest_url,
+ const GURL& first_party) OVERRIDE;
+ virtual bool CanCreateAppCache(const GURL& manifest_url,
+ const GURL& first_party) OVERRIDE;
+
+ bool can_load_return_value_;
+ bool can_create_return_value_;
+ GURL requested_manifest_url_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_APPCACHE_MOCK_APPCACHE_POLICY_H_
diff --git a/chromium/content/browser/appcache/mock_appcache_service.cc b/chromium/content/browser/appcache/mock_appcache_service.cc
new file mode 100644
index 00000000000..549d20bc393
--- /dev/null
+++ b/chromium/content/browser/appcache/mock_appcache_service.cc
@@ -0,0 +1,27 @@
+// 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/appcache/mock_appcache_service.h"
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+
+namespace content {
+
+static void DeferredCallCallback(
+ const net::CompletionCallback& callback, int rv) {
+ callback.Run(rv);
+}
+
+void MockAppCacheService::DeleteAppCachesForOrigin(
+ const GURL& origin, const net::CompletionCallback& callback) {
+ ++delete_called_count_;
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&DeferredCallCallback,
+ callback,
+ mock_delete_appcaches_for_origin_result_));
+}
+
+} // namespace content
diff --git a/chromium/content/browser/appcache/mock_appcache_service.h b/chromium/content/browser/appcache/mock_appcache_service.h
new file mode 100644
index 00000000000..691736baa60
--- /dev/null
+++ b/chromium/content/browser/appcache/mock_appcache_service.h
@@ -0,0 +1,50 @@
+// 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_APPCACHE_MOCK_APPCACHE_SERVICE_H_
+#define CONTENT_BROWSER_APPCACHE_MOCK_APPCACHE_SERVICE_H_
+
+#include "base/compiler_specific.h"
+#include "content/browser/appcache/mock_appcache_storage.h"
+#include "webkit/browser/appcache/appcache_service_impl.h"
+#include "webkit/browser/quota/quota_manager.h"
+
+using appcache::AppCacheServiceImpl;
+
+namespace content {
+
+// For use by unit tests.
+class MockAppCacheService : public AppCacheServiceImpl {
+ public:
+ MockAppCacheService()
+ : AppCacheServiceImpl(NULL),
+ mock_delete_appcaches_for_origin_result_(net::OK),
+ delete_called_count_(0) {
+ storage_.reset(new MockAppCacheStorage(this));
+ }
+
+ // Just returns a canned completion code without actually
+ // removing groups and caches in our mock storage instance.
+ virtual void DeleteAppCachesForOrigin(
+ const GURL& origin,
+ const net::CompletionCallback& callback) OVERRIDE;
+
+ void set_quota_manager_proxy(quota::QuotaManagerProxy* proxy) {
+ quota_manager_proxy_ = proxy;
+ }
+
+ void set_mock_delete_appcaches_for_origin_result(int rv) {
+ mock_delete_appcaches_for_origin_result_ = rv;
+ }
+
+ int delete_called_count() const { return delete_called_count_; }
+
+ private:
+ int mock_delete_appcaches_for_origin_result_;
+ int delete_called_count_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_APPCACHE_MOCK_APPCACHE_SERVICE_H_
diff --git a/chromium/content/browser/appcache/mock_appcache_storage.cc b/chromium/content/browser/appcache/mock_appcache_storage.cc
new file mode 100644
index 00000000000..ea9673df497
--- /dev/null
+++ b/chromium/content/browser/appcache/mock_appcache_storage.cc
@@ -0,0 +1,551 @@
+// 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/appcache/mock_appcache_storage.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/stl_util.h"
+#include "webkit/browser/appcache/appcache.h"
+#include "webkit/browser/appcache/appcache_entry.h"
+#include "webkit/browser/appcache/appcache_group.h"
+#include "webkit/browser/appcache/appcache_response.h"
+#include "webkit/browser/appcache/appcache_service_impl.h"
+
+// This is a quick and easy 'mock' implementation of the storage interface
+// that doesn't put anything to disk.
+//
+// We simply add an extra reference to objects when they're put in storage,
+// and remove the extra reference when they are removed from storage.
+// Responses are never really removed from the in-memory disk cache.
+// Delegate callbacks are made asyncly to appropiately mimic what will
+// happen with a real disk-backed storage impl that involves IO on a
+// background thread.
+
+using appcache::AppCacheResponseWriter;
+using appcache::AppCacheServiceImpl;
+using appcache::APPCACHE_FALLBACK_NAMESPACE;
+using appcache::APPCACHE_INTERCEPT_NAMESPACE;
+using appcache::kAppCacheNoCacheId;
+using appcache::AppCacheNamespaceType;
+
+namespace content {
+
+MockAppCacheStorage::MockAppCacheStorage(AppCacheServiceImpl* service)
+ : AppCacheStorage(service),
+ simulate_make_group_obsolete_failure_(false),
+ simulate_store_group_and_newest_cache_failure_(false),
+ simulate_find_main_resource_(false),
+ simulate_find_sub_resource_(false),
+ simulated_found_cache_id_(kAppCacheNoCacheId),
+ simulated_found_group_id_(0),
+ simulated_found_network_namespace_(false),
+ weak_factory_(this) {
+ last_cache_id_ = 0;
+ last_group_id_ = 0;
+ last_response_id_ = 0;
+}
+
+MockAppCacheStorage::~MockAppCacheStorage() {
+}
+
+void MockAppCacheStorage::GetAllInfo(Delegate* delegate) {
+ ScheduleTask(
+ base::Bind(&MockAppCacheStorage::ProcessGetAllInfo,
+ weak_factory_.GetWeakPtr(),
+ make_scoped_refptr(GetOrCreateDelegateReference(delegate))));
+}
+
+void MockAppCacheStorage::LoadCache(int64 id, Delegate* delegate) {
+ DCHECK(delegate);
+ AppCache* cache = working_set_.GetCache(id);
+ if (ShouldCacheLoadAppearAsync(cache)) {
+ ScheduleTask(
+ base::Bind(&MockAppCacheStorage::ProcessLoadCache,
+ weak_factory_.GetWeakPtr(), id,
+ make_scoped_refptr(GetOrCreateDelegateReference(delegate))));
+ return;
+ }
+ ProcessLoadCache(id, GetOrCreateDelegateReference(delegate));
+}
+
+void MockAppCacheStorage::LoadOrCreateGroup(
+ const GURL& manifest_url, Delegate* delegate) {
+ DCHECK(delegate);
+ AppCacheGroup* group = working_set_.GetGroup(manifest_url);
+ if (ShouldGroupLoadAppearAsync(group)) {
+ ScheduleTask(
+ base::Bind(&MockAppCacheStorage::ProcessLoadOrCreateGroup,
+ weak_factory_.GetWeakPtr(), manifest_url,
+ make_scoped_refptr(GetOrCreateDelegateReference(delegate))));
+ return;
+ }
+ ProcessLoadOrCreateGroup(
+ manifest_url, GetOrCreateDelegateReference(delegate));
+}
+
+void MockAppCacheStorage::StoreGroupAndNewestCache(
+ AppCacheGroup* group, AppCache* newest_cache, Delegate* delegate) {
+ DCHECK(group && delegate && newest_cache);
+
+ // Always make this operation look async.
+ ScheduleTask(
+ base::Bind(&MockAppCacheStorage::ProcessStoreGroupAndNewestCache,
+ weak_factory_.GetWeakPtr(), make_scoped_refptr(group),
+ make_scoped_refptr(newest_cache),
+ make_scoped_refptr(GetOrCreateDelegateReference(delegate))));
+}
+
+void MockAppCacheStorage::FindResponseForMainRequest(
+ const GURL& url, const GURL& preferred_manifest_url, Delegate* delegate) {
+ DCHECK(delegate);
+
+ // Note: MockAppCacheStorage does not respect the preferred_manifest_url.
+
+ // Always make this operation look async.
+ ScheduleTask(
+ base::Bind(&MockAppCacheStorage::ProcessFindResponseForMainRequest,
+ weak_factory_.GetWeakPtr(), url,
+ make_scoped_refptr(GetOrCreateDelegateReference(delegate))));
+}
+
+void MockAppCacheStorage::FindResponseForSubRequest(
+ AppCache* cache, const GURL& url,
+ AppCacheEntry* found_entry, AppCacheEntry* found_fallback_entry,
+ bool* found_network_namespace) {
+ DCHECK(cache && cache->is_complete());
+
+ // This layer of indirection is here to facilitate testing.
+ if (simulate_find_sub_resource_) {
+ *found_entry = simulated_found_entry_;
+ *found_fallback_entry = simulated_found_fallback_entry_;
+ *found_network_namespace = simulated_found_network_namespace_;
+ simulate_find_sub_resource_ = false;
+ return;
+ }
+
+ GURL fallback_namespace_not_used;
+ GURL intercept_namespace_not_used;
+ cache->FindResponseForRequest(
+ url, found_entry, &intercept_namespace_not_used,
+ found_fallback_entry, &fallback_namespace_not_used,
+ found_network_namespace);
+}
+
+void MockAppCacheStorage::MarkEntryAsForeign(
+ const GURL& entry_url, int64 cache_id) {
+ AppCache* cache = working_set_.GetCache(cache_id);
+ if (cache) {
+ AppCacheEntry* entry = cache->GetEntry(entry_url);
+ DCHECK(entry);
+ if (entry)
+ entry->add_types(AppCacheEntry::FOREIGN);
+ }
+}
+
+void MockAppCacheStorage::MakeGroupObsolete(AppCacheGroup* group,
+ Delegate* delegate,
+ int response_code) {
+ DCHECK(group && delegate);
+
+ // Always make this method look async.
+ ScheduleTask(
+ base::Bind(&MockAppCacheStorage::ProcessMakeGroupObsolete,
+ weak_factory_.GetWeakPtr(),
+ make_scoped_refptr(group),
+ make_scoped_refptr(GetOrCreateDelegateReference(delegate)),
+ response_code));
+}
+
+AppCacheResponseReader* MockAppCacheStorage::CreateResponseReader(
+ const GURL& manifest_url, int64 group_id, int64 response_id) {
+ if (simulated_reader_)
+ return simulated_reader_.release();
+ return new AppCacheResponseReader(response_id, group_id, disk_cache());
+}
+
+AppCacheResponseWriter* MockAppCacheStorage::CreateResponseWriter(
+ const GURL& manifest_url, int64 group_id) {
+ return new AppCacheResponseWriter(NewResponseId(), group_id, disk_cache());
+}
+
+void MockAppCacheStorage::DoomResponses(
+ const GURL& manifest_url, const std::vector<int64>& response_ids) {
+ DeleteResponses(manifest_url, response_ids);
+}
+
+void MockAppCacheStorage::DeleteResponses(
+ const GURL& manifest_url, const std::vector<int64>& response_ids) {
+ // We don't bother with actually removing responses from the disk-cache,
+ // just keep track of which ids have been doomed or deleted
+ std::vector<int64>::const_iterator it = response_ids.begin();
+ while (it != response_ids.end()) {
+ doomed_response_ids_.insert(*it);
+ ++it;
+ }
+}
+
+void MockAppCacheStorage::ProcessGetAllInfo(
+ scoped_refptr<DelegateReference> delegate_ref) {
+ if (delegate_ref->delegate)
+ delegate_ref->delegate->OnAllInfo(simulated_appcache_info_.get());
+}
+
+void MockAppCacheStorage::ProcessLoadCache(
+ int64 id, scoped_refptr<DelegateReference> delegate_ref) {
+ AppCache* cache = working_set_.GetCache(id);
+ if (delegate_ref->delegate)
+ delegate_ref->delegate->OnCacheLoaded(cache, id);
+}
+
+void MockAppCacheStorage::ProcessLoadOrCreateGroup(
+ const GURL& manifest_url, scoped_refptr<DelegateReference> delegate_ref) {
+ scoped_refptr<AppCacheGroup> group(working_set_.GetGroup(manifest_url));
+
+ // Newly created groups are not put in the stored_groups collection
+ // until StoreGroupAndNewestCache is called.
+ if (!group.get())
+ group = new AppCacheGroup(service_->storage(), manifest_url, NewGroupId());
+
+ if (delegate_ref->delegate)
+ delegate_ref->delegate->OnGroupLoaded(group.get(), manifest_url);
+}
+
+void MockAppCacheStorage::ProcessStoreGroupAndNewestCache(
+ scoped_refptr<AppCacheGroup> group,
+ scoped_refptr<AppCache> newest_cache,
+ scoped_refptr<DelegateReference> delegate_ref) {
+ Delegate* delegate = delegate_ref->delegate;
+ if (simulate_store_group_and_newest_cache_failure_) {
+ if (delegate)
+ delegate->OnGroupAndNewestCacheStored(
+ group.get(), newest_cache.get(), false, false);
+ return;
+ }
+
+ AddStoredGroup(group.get());
+ if (newest_cache.get() != group->newest_complete_cache()) {
+ newest_cache->set_complete(true);
+ group->AddCache(newest_cache.get());
+ AddStoredCache(newest_cache.get());
+
+ // Copy the collection prior to removal, on final release
+ // of a cache the group's collection will change.
+ AppCacheGroup::Caches copy = group->old_caches();
+ RemoveStoredCaches(copy);
+ }
+
+ if (delegate)
+ delegate->OnGroupAndNewestCacheStored(
+ group.get(), newest_cache.get(), true, false);
+}
+
+namespace {
+
+struct FoundCandidate {
+ GURL namespace_entry_url;
+ AppCacheEntry entry;
+ int64 cache_id;
+ int64 group_id;
+ GURL manifest_url;
+ bool is_cache_in_use;
+
+ FoundCandidate()
+ : cache_id(kAppCacheNoCacheId), group_id(0), is_cache_in_use(false) {}
+};
+
+void MaybeTakeNewNamespaceEntry(
+ AppCacheNamespaceType namespace_type,
+ const AppCacheEntry &entry,
+ const GURL& namespace_url,
+ bool cache_is_in_use,
+ FoundCandidate* best_candidate,
+ GURL* best_candidate_namespace,
+ AppCache* cache,
+ AppCacheGroup* group) {
+ DCHECK(entry.has_response_id());
+
+ bool take_new_entry = true;
+
+ // Does the new candidate entry trump our current best candidate?
+ if (best_candidate->entry.has_response_id()) {
+ // Longer namespace prefix matches win.
+ size_t candidate_length =
+ namespace_url.spec().length();
+ size_t best_length =
+ best_candidate_namespace->spec().length();
+
+ if (candidate_length > best_length) {
+ take_new_entry = true;
+ } else if (candidate_length == best_length &&
+ cache_is_in_use && !best_candidate->is_cache_in_use) {
+ take_new_entry = true;
+ } else {
+ take_new_entry = false;
+ }
+ }
+
+ if (take_new_entry) {
+ if (namespace_type == APPCACHE_FALLBACK_NAMESPACE) {
+ best_candidate->namespace_entry_url =
+ cache->GetFallbackEntryUrl(namespace_url);
+ } else {
+ best_candidate->namespace_entry_url =
+ cache->GetInterceptEntryUrl(namespace_url);
+ }
+ best_candidate->entry = entry;
+ best_candidate->cache_id = cache->cache_id();
+ best_candidate->group_id = group->group_id();
+ best_candidate->manifest_url = group->manifest_url();
+ best_candidate->is_cache_in_use = cache_is_in_use;
+ *best_candidate_namespace = namespace_url;
+ }
+}
+} // namespace
+
+void MockAppCacheStorage::ProcessFindResponseForMainRequest(
+ const GURL& url, scoped_refptr<DelegateReference> delegate_ref) {
+ if (simulate_find_main_resource_) {
+ simulate_find_main_resource_ = false;
+ if (delegate_ref->delegate) {
+ delegate_ref->delegate->OnMainResponseFound(
+ url, simulated_found_entry_,
+ simulated_found_fallback_url_, simulated_found_fallback_entry_,
+ simulated_found_cache_id_, simulated_found_group_id_,
+ simulated_found_manifest_url_);
+ }
+ return;
+ }
+
+ // This call has no persistent side effects, if the delegate has gone
+ // away, we can just bail out early.
+ if (!delegate_ref->delegate)
+ return;
+
+ // TODO(michaeln): The heuristics around choosing amoungst
+ // multiple candidates is under specified, and just plain
+ // not fully understood. Refine these over time. In particular,
+ // * prefer candidates from newer caches
+ // * take into account the cache associated with the document
+ // that initiated the navigation
+ // * take into account the cache associated with the document
+ // currently residing in the frame being navigated
+ FoundCandidate found_candidate;
+ GURL found_intercept_candidate_namespace;
+ FoundCandidate found_fallback_candidate;
+ GURL found_fallback_candidate_namespace;
+
+ for (StoredGroupMap::const_iterator it = stored_groups_.begin();
+ it != stored_groups_.end(); ++it) {
+ AppCacheGroup* group = it->second.get();
+ AppCache* cache = group->newest_complete_cache();
+ if (group->is_obsolete() || !cache ||
+ (url.GetOrigin() != group->manifest_url().GetOrigin())) {
+ continue;
+ }
+
+ AppCacheEntry found_entry;
+ AppCacheEntry found_fallback_entry;
+ GURL found_intercept_namespace;
+ GURL found_fallback_namespace;
+ bool ignore_found_network_namespace = false;
+ bool found = cache->FindResponseForRequest(
+ url, &found_entry, &found_intercept_namespace,
+ &found_fallback_entry, &found_fallback_namespace,
+ &ignore_found_network_namespace);
+
+ // 6.11.1 Navigating across documents, Step 10.
+ // Network namespacing doesn't apply to main resource loads,
+ // and foreign entries are excluded.
+ if (!found || ignore_found_network_namespace ||
+ (found_entry.has_response_id() && found_entry.IsForeign()) ||
+ (found_fallback_entry.has_response_id() &&
+ found_fallback_entry.IsForeign())) {
+ continue;
+ }
+
+ // We have a bias for hits from caches that are in use.
+ bool is_in_use = IsCacheStored(cache) && !cache->HasOneRef();
+
+ if (found_entry.has_response_id() &&
+ found_intercept_namespace.is_empty()) {
+ found_candidate.namespace_entry_url = GURL();
+ found_candidate.entry = found_entry;
+ found_candidate.cache_id = cache->cache_id();
+ found_candidate.group_id = group->group_id();
+ found_candidate.manifest_url = group->manifest_url();
+ found_candidate.is_cache_in_use = is_in_use;
+ if (is_in_use)
+ break; // We break out of the loop with this direct hit.
+ } else if (found_entry.has_response_id() &&
+ !found_intercept_namespace.is_empty()) {
+ MaybeTakeNewNamespaceEntry(
+ APPCACHE_INTERCEPT_NAMESPACE,
+ found_entry, found_intercept_namespace, is_in_use,
+ &found_candidate, &found_intercept_candidate_namespace,
+ cache, group);
+ } else {
+ DCHECK(found_fallback_entry.has_response_id());
+ MaybeTakeNewNamespaceEntry(
+ APPCACHE_FALLBACK_NAMESPACE,
+ found_fallback_entry, found_fallback_namespace, is_in_use,
+ &found_fallback_candidate, &found_fallback_candidate_namespace,
+ cache, group);
+ }
+ }
+
+ // Found a direct hit or an intercept namespace hit.
+ if (found_candidate.entry.has_response_id()) {
+ delegate_ref->delegate->OnMainResponseFound(
+ url, found_candidate.entry, found_candidate.namespace_entry_url,
+ AppCacheEntry(), found_candidate.cache_id, found_candidate.group_id,
+ found_candidate.manifest_url);
+ return;
+ }
+
+ // Found a fallback namespace.
+ if (found_fallback_candidate.entry.has_response_id()) {
+ delegate_ref->delegate->OnMainResponseFound(
+ url, AppCacheEntry(),
+ found_fallback_candidate.namespace_entry_url,
+ found_fallback_candidate.entry,
+ found_fallback_candidate.cache_id,
+ found_fallback_candidate.group_id,
+ found_fallback_candidate.manifest_url);
+ return;
+ }
+
+ // Didn't find anything.
+ delegate_ref->delegate->OnMainResponseFound(
+ url, AppCacheEntry(), GURL(), AppCacheEntry(), kAppCacheNoCacheId, 0,
+ GURL());
+}
+
+void MockAppCacheStorage::ProcessMakeGroupObsolete(
+ scoped_refptr<AppCacheGroup> group,
+ scoped_refptr<DelegateReference> delegate_ref,
+ int response_code) {
+ if (simulate_make_group_obsolete_failure_) {
+ if (delegate_ref->delegate)
+ delegate_ref->delegate->OnGroupMadeObsolete(
+ group.get(), false, response_code);
+ return;
+ }
+
+ RemoveStoredGroup(group.get());
+ if (group->newest_complete_cache())
+ RemoveStoredCache(group->newest_complete_cache());
+
+ // Copy the collection prior to removal, on final release
+ // of a cache the group's collection will change.
+ AppCacheGroup::Caches copy = group->old_caches();
+ RemoveStoredCaches(copy);
+
+ group->set_obsolete(true);
+
+ // Also remove from the working set, caches for an 'obsolete' group
+ // may linger in use, but the group itself cannot be looked up by
+ // 'manifest_url' in the working set any longer.
+ working_set()->RemoveGroup(group.get());
+
+ if (delegate_ref->delegate)
+ delegate_ref->delegate->OnGroupMadeObsolete(
+ group.get(), true, response_code);
+}
+
+void MockAppCacheStorage::ScheduleTask(const base::Closure& task) {
+ pending_tasks_.push_back(task);
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&MockAppCacheStorage::RunOnePendingTask,
+ weak_factory_.GetWeakPtr()));
+}
+
+void MockAppCacheStorage::RunOnePendingTask() {
+ DCHECK(!pending_tasks_.empty());
+ base::Closure task = pending_tasks_.front();
+ pending_tasks_.pop_front();
+ task.Run();
+}
+
+void MockAppCacheStorage::AddStoredCache(AppCache* cache) {
+ int64 cache_id = cache->cache_id();
+ if (stored_caches_.find(cache_id) == stored_caches_.end()) {
+ stored_caches_.insert(
+ StoredCacheMap::value_type(cache_id, make_scoped_refptr(cache)));
+ }
+}
+
+void MockAppCacheStorage::RemoveStoredCache(AppCache* cache) {
+ // Do not remove from the working set, active caches are still usable
+ // and may be looked up by id until they fall out of use.
+ stored_caches_.erase(cache->cache_id());
+}
+
+void MockAppCacheStorage::RemoveStoredCaches(
+ const AppCacheGroup::Caches& caches) {
+ AppCacheGroup::Caches::const_iterator it = caches.begin();
+ while (it != caches.end()) {
+ RemoveStoredCache(*it);
+ ++it;
+ }
+}
+
+void MockAppCacheStorage::AddStoredGroup(AppCacheGroup* group) {
+ const GURL& url = group->manifest_url();
+ if (stored_groups_.find(url) == stored_groups_.end()) {
+ stored_groups_.insert(
+ StoredGroupMap::value_type(url, make_scoped_refptr(group)));
+ }
+}
+
+void MockAppCacheStorage::RemoveStoredGroup(AppCacheGroup* group) {
+ stored_groups_.erase(group->manifest_url());
+}
+
+bool MockAppCacheStorage::ShouldGroupLoadAppearAsync(
+ const AppCacheGroup* group) {
+ // We'll have to query the database to see if a group for the
+ // manifest_url exists on disk. So return true for async.
+ if (!group)
+ return true;
+
+ // Groups without a newest cache can't have been put to disk yet, so
+ // we can synchronously return a reference we have in the working set.
+ if (!group->newest_complete_cache())
+ return false;
+
+ // The LoadGroup interface implies also loading the newest cache, so
+ // if loading the newest cache should appear async, so too must the
+ // loading of this group.
+ if (!ShouldCacheLoadAppearAsync(group->newest_complete_cache()))
+ return false;
+
+
+ // If any of the old caches are "in use", then the group must also
+ // be memory resident and not require async loading.
+ const AppCacheGroup::Caches& old_caches = group->old_caches();
+ AppCacheGroup::Caches::const_iterator it = old_caches.begin();
+ while (it != old_caches.end()) {
+ // "in use" caches don't require async loading
+ if (!ShouldCacheLoadAppearAsync(*it))
+ return false;
+ ++it;
+ }
+
+ return true;
+}
+
+bool MockAppCacheStorage::ShouldCacheLoadAppearAsync(const AppCache* cache) {
+ if (!cache)
+ return true;
+
+ // If the 'stored' ref is the only ref, real storage will have to load from
+ // the database.
+ return IsCacheStored(cache) && cache->HasOneRef();
+}
+
+} // namespace content
diff --git a/chromium/content/browser/appcache/mock_appcache_storage.h b/chromium/content/browser/appcache/mock_appcache_storage.h
new file mode 100644
index 00000000000..5d31748da43
--- /dev/null
+++ b/chromium/content/browser/appcache/mock_appcache_storage.h
@@ -0,0 +1,254 @@
+// 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_APPCACHE_MOCK_APPCACHE_STORAGE_H_
+#define CONTENT_BROWSER_APPCACHE_MOCK_APPCACHE_STORAGE_H_
+
+#include <deque>
+#include <map>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/containers/hash_tables.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "webkit/browser/appcache/appcache.h"
+#include "webkit/browser/appcache/appcache_disk_cache.h"
+#include "webkit/browser/appcache/appcache_group.h"
+#include "webkit/browser/appcache/appcache_response.h"
+#include "webkit/browser/appcache/appcache_storage.h"
+
+using appcache::AppCache;
+using appcache::AppCacheDiskCache;
+using appcache::AppCacheEntry;
+using appcache::AppCacheGroup;
+using appcache::AppCacheInfoCollection;
+using appcache::AppCacheResponseReader;
+using appcache::AppCacheResponseWriter;
+using appcache::AppCacheServiceImpl;
+using appcache::AppCacheStorage;
+using appcache::kAppCacheNoCacheId;
+
+namespace content {
+FORWARD_DECLARE_TEST(AppCacheServiceImplTest, DeleteAppCachesForOrigin);
+FORWARD_DECLARE_TEST(MockAppCacheStorageTest, BasicFindMainResponse);
+FORWARD_DECLARE_TEST(MockAppCacheStorageTest,
+ BasicFindMainFallbackResponse);
+FORWARD_DECLARE_TEST(MockAppCacheStorageTest, CreateGroup);
+FORWARD_DECLARE_TEST(MockAppCacheStorageTest, FindMainResponseExclusions);
+FORWARD_DECLARE_TEST(MockAppCacheStorageTest,
+ FindMainResponseWithMultipleCandidates);
+FORWARD_DECLARE_TEST(MockAppCacheStorageTest, LoadCache_FarHit);
+FORWARD_DECLARE_TEST(MockAppCacheStorageTest, LoadGroupAndCache_FarHit);
+FORWARD_DECLARE_TEST(MockAppCacheStorageTest, MakeGroupObsolete);
+FORWARD_DECLARE_TEST(MockAppCacheStorageTest, StoreNewGroup);
+FORWARD_DECLARE_TEST(MockAppCacheStorageTest, StoreExistingGroup);
+FORWARD_DECLARE_TEST(MockAppCacheStorageTest,
+ StoreExistingGroupExistingCache);
+class AppCacheRequestHandlerTest;
+class AppCacheServiceImplTest;
+class MockAppCacheStorageTest;
+
+// For use in unit tests.
+// Note: This class is also being used to bootstrap our development efforts.
+// We can get layout tests up and running, and back fill with real storage
+// somewhat in parallel.
+class MockAppCacheStorage : public AppCacheStorage {
+ public:
+ explicit MockAppCacheStorage(AppCacheServiceImpl* service);
+ virtual ~MockAppCacheStorage();
+
+ virtual void GetAllInfo(Delegate* delegate) OVERRIDE;
+ virtual void LoadCache(int64 id, Delegate* delegate) OVERRIDE;
+ virtual void LoadOrCreateGroup(const GURL& manifest_url,
+ Delegate* delegate) OVERRIDE;
+ virtual void StoreGroupAndNewestCache(AppCacheGroup* group,
+ AppCache* newest_cache,
+ Delegate* delegate) OVERRIDE;
+ virtual void FindResponseForMainRequest(const GURL& url,
+ const GURL& preferred_manifest_url,
+ Delegate* delegate) OVERRIDE;
+ virtual void FindResponseForSubRequest(
+ AppCache* cache, const GURL& url,
+ AppCacheEntry* found_entry, AppCacheEntry* found_fallback_entry,
+ bool * found_network_namespace) OVERRIDE;
+ virtual void MarkEntryAsForeign(const GURL& entry_url,
+ int64 cache_id) OVERRIDE;
+ virtual void MakeGroupObsolete(AppCacheGroup* group,
+ Delegate* delegate,
+ int response_code) OVERRIDE;
+ virtual AppCacheResponseReader* CreateResponseReader(
+ const GURL& manifest_url, int64 group_id, int64 response_id) OVERRIDE;
+ virtual AppCacheResponseWriter* CreateResponseWriter(
+ const GURL& manifest_url, int64 group_id) OVERRIDE;
+ virtual void DoomResponses(
+ const GURL& manifest_url,
+ const std::vector<int64>& response_ids) OVERRIDE;
+ virtual void DeleteResponses(
+ const GURL& manifest_url,
+ const std::vector<int64>& response_ids) OVERRIDE;
+
+ private:
+ friend class AppCacheRequestHandlerTest;
+ friend class AppCacheServiceImplTest;
+ friend class AppCacheUpdateJobTest;
+ friend class MockAppCacheStorageTest;
+
+ typedef base::hash_map<int64, scoped_refptr<AppCache> > StoredCacheMap;
+ typedef std::map<GURL, scoped_refptr<AppCacheGroup> > StoredGroupMap;
+ typedef std::set<int64> DoomedResponseIds;
+
+ void ProcessGetAllInfo(scoped_refptr<DelegateReference> delegate_ref);
+ void ProcessLoadCache(
+ int64 id, scoped_refptr<DelegateReference> delegate_ref);
+ void ProcessLoadOrCreateGroup(
+ const GURL& manifest_url, scoped_refptr<DelegateReference> delegate_ref);
+ void ProcessStoreGroupAndNewestCache(
+ scoped_refptr<AppCacheGroup> group, scoped_refptr<AppCache> newest_cache,
+ scoped_refptr<DelegateReference> delegate_ref);
+ void ProcessMakeGroupObsolete(scoped_refptr<AppCacheGroup> group,
+ scoped_refptr<DelegateReference> delegate_ref,
+ int response_code);
+ void ProcessFindResponseForMainRequest(
+ const GURL& url, scoped_refptr<DelegateReference> delegate_ref);
+
+ void ScheduleTask(const base::Closure& task);
+ void RunOnePendingTask();
+
+ void AddStoredCache(AppCache* cache);
+ void RemoveStoredCache(AppCache* cache);
+ void RemoveStoredCaches(const AppCacheGroup::Caches& caches);
+ bool IsCacheStored(const AppCache* cache) {
+ return stored_caches_.find(cache->cache_id()) != stored_caches_.end();
+ }
+
+ void AddStoredGroup(AppCacheGroup* group);
+ void RemoveStoredGroup(AppCacheGroup* group);
+ bool IsGroupStored(const AppCacheGroup* group) {
+ return IsGroupForManifestStored(group->manifest_url());
+ }
+ bool IsGroupForManifestStored(const GURL& manifest_url) {
+ return stored_groups_.find(manifest_url) != stored_groups_.end();
+ }
+
+ // These helpers determine when certain operations should complete
+ // asynchronously vs synchronously to faithfully mimic, or mock,
+ // the behavior of the real implemenation of the AppCacheStorage
+ // interface.
+ bool ShouldGroupLoadAppearAsync(const AppCacheGroup* group);
+ bool ShouldCacheLoadAppearAsync(const AppCache* cache);
+
+ // Lazily constructed in-memory disk cache.
+ AppCacheDiskCache* disk_cache() {
+ if (!disk_cache_) {
+ const int kMaxCacheSize = 10 * 1024 * 1024;
+ disk_cache_.reset(new AppCacheDiskCache);
+ disk_cache_->InitWithMemBackend(kMaxCacheSize, net::CompletionCallback());
+ }
+ return disk_cache_.get();
+ }
+
+ // Simulate failures for testing. Once set all subsequent calls
+ // to MakeGroupObsolete or StorageGroupAndNewestCache will fail.
+ void SimulateMakeGroupObsoleteFailure() {
+ simulate_make_group_obsolete_failure_ = true;
+ }
+ void SimulateStoreGroupAndNewestCacheFailure() {
+ simulate_store_group_and_newest_cache_failure_ = true;
+ }
+
+ // Simulate FindResponseFor results for testing. These
+ // provided values will be return on the next call to
+ // the corresponding Find method, subsequent calls are
+ // unaffected.
+ void SimulateFindMainResource(
+ const AppCacheEntry& entry,
+ const GURL& fallback_url,
+ const AppCacheEntry& fallback_entry,
+ int64 cache_id,
+ int64 group_id,
+ const GURL& manifest_url) {
+ simulate_find_main_resource_ = true;
+ simulate_find_sub_resource_ = false;
+ simulated_found_entry_ = entry;
+ simulated_found_fallback_url_ = fallback_url;
+ simulated_found_fallback_entry_ = fallback_entry;
+ simulated_found_cache_id_ = cache_id;
+ simulated_found_group_id_ = group_id;
+ simulated_found_manifest_url_ = manifest_url,
+ simulated_found_network_namespace_ = false; // N/A to main resource loads
+ }
+ void SimulateFindSubResource(
+ const AppCacheEntry& entry,
+ const AppCacheEntry& fallback_entry,
+ bool network_namespace) {
+ simulate_find_main_resource_ = false;
+ simulate_find_sub_resource_ = true;
+ simulated_found_entry_ = entry;
+ simulated_found_fallback_entry_ = fallback_entry;
+ simulated_found_cache_id_ = kAppCacheNoCacheId; // N/A to sub resource loads
+ simulated_found_manifest_url_ = GURL(); // N/A to sub resource loads
+ simulated_found_group_id_ = 0; // N/A to sub resource loads
+ simulated_found_network_namespace_ = network_namespace;
+ }
+
+ void SimulateGetAllInfo(AppCacheInfoCollection* info) {
+ simulated_appcache_info_ = info;
+ }
+
+ void SimulateResponseReader(AppCacheResponseReader* reader) {
+ simulated_reader_.reset(reader);
+ }
+
+ StoredCacheMap stored_caches_;
+ StoredGroupMap stored_groups_;
+ DoomedResponseIds doomed_response_ids_;
+ scoped_ptr<AppCacheDiskCache> disk_cache_;
+ std::deque<base::Closure> pending_tasks_;
+
+ bool simulate_make_group_obsolete_failure_;
+ bool simulate_store_group_and_newest_cache_failure_;
+
+ bool simulate_find_main_resource_;
+ bool simulate_find_sub_resource_;
+ AppCacheEntry simulated_found_entry_;
+ AppCacheEntry simulated_found_fallback_entry_;
+ int64 simulated_found_cache_id_;
+ int64 simulated_found_group_id_;
+ GURL simulated_found_fallback_url_;
+ GURL simulated_found_manifest_url_;
+ bool simulated_found_network_namespace_;
+ scoped_refptr<AppCacheInfoCollection> simulated_appcache_info_;
+ scoped_ptr<AppCacheResponseReader> simulated_reader_;
+
+ base::WeakPtrFactory<MockAppCacheStorage> weak_factory_;
+
+ FRIEND_TEST_ALL_PREFIXES(MockAppCacheStorageTest,
+ BasicFindMainResponse);
+ FRIEND_TEST_ALL_PREFIXES(MockAppCacheStorageTest,
+ BasicFindMainFallbackResponse);
+ FRIEND_TEST_ALL_PREFIXES(MockAppCacheStorageTest, CreateGroup);
+ FRIEND_TEST_ALL_PREFIXES(MockAppCacheStorageTest,
+ FindMainResponseExclusions);
+ FRIEND_TEST_ALL_PREFIXES(MockAppCacheStorageTest,
+ FindMainResponseWithMultipleCandidates);
+ FRIEND_TEST_ALL_PREFIXES(MockAppCacheStorageTest, LoadCache_FarHit);
+ FRIEND_TEST_ALL_PREFIXES(MockAppCacheStorageTest,
+ LoadGroupAndCache_FarHit);
+ FRIEND_TEST_ALL_PREFIXES(MockAppCacheStorageTest, MakeGroupObsolete);
+ FRIEND_TEST_ALL_PREFIXES(MockAppCacheStorageTest, StoreNewGroup);
+ FRIEND_TEST_ALL_PREFIXES(MockAppCacheStorageTest,
+ StoreExistingGroup);
+ FRIEND_TEST_ALL_PREFIXES(MockAppCacheStorageTest,
+ StoreExistingGroupExistingCache);
+ FRIEND_TEST_ALL_PREFIXES(AppCacheServiceImplTest,
+ DeleteAppCachesForOrigin);
+
+ DISALLOW_COPY_AND_ASSIGN(MockAppCacheStorage);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_APPCACHE_MOCK_APPCACHE_STORAGE_H_
diff --git a/chromium/content/browser/appcache/mock_appcache_storage_unittest.cc b/chromium/content/browser/appcache/mock_appcache_storage_unittest.cc
new file mode 100644
index 00000000000..f1c9149102c
--- /dev/null
+++ b/chromium/content/browser/appcache/mock_appcache_storage_unittest.cc
@@ -0,0 +1,646 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/run_loop.h"
+#include "content/browser/appcache/mock_appcache_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/appcache/appcache.h"
+#include "webkit/browser/appcache/appcache_group.h"
+#include "webkit/browser/appcache/appcache_response.h"
+#include "webkit/browser/appcache/appcache_storage.h"
+
+using appcache::AppCache;
+using appcache::AppCacheEntry;
+using appcache::AppCacheGroup;
+using appcache::AppCacheStorage;
+using appcache::APPCACHE_FALLBACK_NAMESPACE;
+using appcache::APPCACHE_INTERCEPT_NAMESPACE;
+using appcache::kAppCacheNoCacheId;
+using appcache::kAppCacheNoResponseId;
+using appcache::Manifest;
+using appcache::Namespace;
+using appcache::APPCACHE_NETWORK_NAMESPACE;
+
+namespace content {
+
+class MockAppCacheStorageTest : public testing::Test {
+ public:
+ class MockStorageDelegate : public AppCacheStorage::Delegate {
+ public:
+ explicit MockStorageDelegate()
+ : loaded_cache_id_(0), stored_group_success_(false),
+ obsoleted_success_(false), found_cache_id_(kAppCacheNoCacheId) {
+ }
+
+ virtual void OnCacheLoaded(AppCache* cache, int64 cache_id) OVERRIDE {
+ loaded_cache_ = cache;
+ loaded_cache_id_ = cache_id;
+ }
+
+ virtual void OnGroupLoaded(AppCacheGroup* group,
+ const GURL& manifest_url) OVERRIDE {
+ loaded_group_ = group;
+ loaded_manifest_url_ = manifest_url;
+ }
+
+ virtual void OnGroupAndNewestCacheStored(
+ AppCacheGroup* group, AppCache* newest_cache, bool success,
+ bool would_exceed_quota) OVERRIDE {
+ stored_group_ = group;
+ stored_group_success_ = success;
+ }
+
+ virtual void OnGroupMadeObsolete(AppCacheGroup* group,
+ bool success,
+ int response_code) OVERRIDE {
+ obsoleted_group_ = group;
+ obsoleted_success_ = success;
+ }
+
+ virtual void OnMainResponseFound(const GURL& url,
+ const AppCacheEntry& entry,
+ const GURL& fallback_url,
+ const AppCacheEntry& fallback_entry,
+ int64 cache_id,
+ int64 group_id,
+ const GURL& manifest_url) OVERRIDE {
+ found_url_ = url;
+ found_entry_ = entry;
+ found_fallback_url_ = fallback_url;
+ found_fallback_entry_ = fallback_entry;
+ found_cache_id_ = cache_id;
+ found_manifest_url_ = manifest_url;
+ }
+
+ scoped_refptr<AppCache> loaded_cache_;
+ int64 loaded_cache_id_;
+ scoped_refptr<AppCacheGroup> loaded_group_;
+ GURL loaded_manifest_url_;
+ scoped_refptr<AppCacheGroup> stored_group_;
+ bool stored_group_success_;
+ scoped_refptr<AppCacheGroup> obsoleted_group_;
+ bool obsoleted_success_;
+ GURL found_url_;
+ AppCacheEntry found_entry_;
+ GURL found_fallback_url_;
+ AppCacheEntry found_fallback_entry_;
+ int64 found_cache_id_;
+ GURL found_manifest_url_;
+ };
+
+ private:
+ base::MessageLoop message_loop_;
+};
+
+TEST_F(MockAppCacheStorageTest, LoadCache_Miss) {
+ // Attempt to load a cache that doesn't exist. Should
+ // complete asyncly.
+ MockAppCacheService service;
+ MockStorageDelegate delegate;
+ service.storage()->LoadCache(111, &delegate);
+ EXPECT_NE(111, delegate.loaded_cache_id_);
+ base::RunLoop().RunUntilIdle(); // Do async task execution.
+ EXPECT_EQ(111, delegate.loaded_cache_id_);
+ EXPECT_FALSE(delegate.loaded_cache_.get());
+}
+
+TEST_F(MockAppCacheStorageTest, LoadCache_NearHit) {
+ // Attempt to load a cache that is currently in use
+ // and does not require loading from disk. This
+ // load should complete syncly.
+ MockAppCacheService service;
+
+ // Setup some preconditions. Make an 'unstored' cache for
+ // us to load. The ctor should put it in the working set.
+ int64 cache_id = service.storage()->NewCacheId();
+ scoped_refptr<AppCache> cache(new AppCache(service.storage(), cache_id));
+
+ // Conduct the test.
+ MockStorageDelegate delegate;
+ service.storage()->LoadCache(cache_id, &delegate);
+ EXPECT_EQ(cache_id, delegate.loaded_cache_id_);
+ EXPECT_EQ(cache.get(), delegate.loaded_cache_.get());
+}
+
+TEST_F(MockAppCacheStorageTest, CreateGroup) {
+ // Attempt to load/create a group that doesn't exist.
+ // Should complete asyncly.
+ MockAppCacheService service;
+ MockAppCacheStorage* storage =
+ reinterpret_cast<MockAppCacheStorage*>(service.storage());
+ MockStorageDelegate delegate;
+ GURL manifest_url("http://blah/");
+ service.storage()->LoadOrCreateGroup(manifest_url, &delegate);
+ EXPECT_NE(manifest_url, delegate.loaded_manifest_url_);
+ EXPECT_FALSE(delegate.loaded_group_.get());
+ base::RunLoop().RunUntilIdle(); // Do async task execution.
+ EXPECT_EQ(manifest_url, delegate.loaded_manifest_url_);
+ EXPECT_TRUE(delegate.loaded_group_.get());
+ EXPECT_TRUE(delegate.loaded_group_->HasOneRef());
+ EXPECT_FALSE(delegate.loaded_group_->newest_complete_cache());
+ EXPECT_TRUE(storage->stored_groups_.empty());
+}
+
+TEST_F(MockAppCacheStorageTest, LoadGroup_NearHit) {
+ // Attempt to load a group that is currently in use
+ // and does not require loading from disk. This
+ // load should complete syncly.
+ MockAppCacheService service;
+ MockStorageDelegate delegate;
+
+ // Setup some preconditions. Create a group that appears
+ // to be "unstored" and "currently in use".
+ GURL manifest_url("http://blah/");
+ service.storage()->LoadOrCreateGroup(manifest_url, &delegate);
+ base::RunLoop().RunUntilIdle(); // Do async task execution.
+ EXPECT_EQ(manifest_url, delegate.loaded_manifest_url_);
+ EXPECT_TRUE(delegate.loaded_group_.get());
+
+ // Reset our delegate, and take a reference to the new group.
+ scoped_refptr<AppCacheGroup> group;
+ group.swap(delegate.loaded_group_);
+ delegate.loaded_manifest_url_ = GURL();
+
+ // Conduct the test.
+ service.storage()->LoadOrCreateGroup(manifest_url, &delegate);
+ EXPECT_EQ(manifest_url, delegate.loaded_manifest_url_);
+ EXPECT_EQ(group.get(), delegate.loaded_group_.get());
+}
+
+TEST_F(MockAppCacheStorageTest, LoadGroupAndCache_FarHit) {
+ // Attempt to load a cache that is not currently in use
+ // and does require loading from disk. This
+ // load should complete asyncly.
+ MockAppCacheService service;
+ MockAppCacheStorage* storage =
+ reinterpret_cast<MockAppCacheStorage*>(service.storage());
+
+ // Setup some preconditions. Create a group and newest cache that
+ // appears to be "stored" and "not currently in use".
+ GURL manifest_url("http://blah/");
+ scoped_refptr<AppCacheGroup> group(
+ new AppCacheGroup(service.storage(), manifest_url, 111));
+ int64 cache_id = storage->NewCacheId();
+ scoped_refptr<AppCache> cache(new AppCache(service.storage(), cache_id));
+ cache->set_complete(true);
+ group->AddCache(cache.get());
+ storage->AddStoredGroup(group.get());
+ storage->AddStoredCache(cache.get());
+
+ // Drop the references from above so the only refs to these
+ // objects are from within the storage class. This is to make
+ // these objects appear as "not currently in use".
+ AppCache* cache_ptr = cache.get();
+ AppCacheGroup* group_ptr = group.get();
+ cache = NULL;
+ group = NULL;
+
+ // Setup a delegate to receive completion callbacks.
+ MockStorageDelegate delegate;
+
+ // Conduct the cache load test.
+ EXPECT_NE(cache_id, delegate.loaded_cache_id_);
+ EXPECT_NE(cache_ptr, delegate.loaded_cache_.get());
+ storage->LoadCache(cache_id, &delegate);
+ EXPECT_NE(cache_id, delegate.loaded_cache_id_);
+ EXPECT_NE(cache_ptr, delegate.loaded_cache_.get());
+ base::RunLoop().RunUntilIdle(); // Do async task execution.
+ EXPECT_EQ(cache_id, delegate.loaded_cache_id_);
+ EXPECT_EQ(cache_ptr, delegate.loaded_cache_.get());
+ delegate.loaded_cache_ = NULL;
+
+ // Conduct the group load test.
+ EXPECT_NE(manifest_url, delegate.loaded_manifest_url_);
+ EXPECT_FALSE(delegate.loaded_group_.get());
+ storage->LoadOrCreateGroup(manifest_url, &delegate);
+ EXPECT_NE(manifest_url, delegate.loaded_manifest_url_);
+ EXPECT_FALSE(delegate.loaded_group_.get());
+ base::RunLoop().RunUntilIdle(); // Do async task execution.
+ EXPECT_EQ(manifest_url, delegate.loaded_manifest_url_);
+ EXPECT_EQ(group_ptr, delegate.loaded_group_.get());
+}
+
+TEST_F(MockAppCacheStorageTest, StoreNewGroup) {
+ // Store a group and its newest cache. Should complete asyncly.
+ MockAppCacheService service;
+ MockAppCacheStorage* storage =
+ reinterpret_cast<MockAppCacheStorage*>(service.storage());
+
+ // Setup some preconditions. Create a group and newest cache that
+ // appears to be "unstored".
+ GURL manifest_url("http://blah/");
+ scoped_refptr<AppCacheGroup> group(
+ new AppCacheGroup(service.storage(), manifest_url, 111));
+ int64 cache_id = storage->NewCacheId();
+ scoped_refptr<AppCache> cache(new AppCache(service.storage(), cache_id));
+ // Hold a ref to the cache simulate the UpdateJob holding that ref,
+ // and hold a ref to the group to simulate the CacheHost holding that ref.
+
+ // Conduct the store test.
+ MockStorageDelegate delegate;
+ EXPECT_TRUE(storage->stored_caches_.empty());
+ EXPECT_TRUE(storage->stored_groups_.empty());
+ storage->StoreGroupAndNewestCache(group.get(), cache.get(), &delegate);
+ EXPECT_FALSE(delegate.stored_group_success_);
+ EXPECT_TRUE(storage->stored_caches_.empty());
+ EXPECT_TRUE(storage->stored_groups_.empty());
+ base::RunLoop().RunUntilIdle(); // Do async task execution.
+ EXPECT_TRUE(delegate.stored_group_success_);
+ EXPECT_FALSE(storage->stored_caches_.empty());
+ EXPECT_FALSE(storage->stored_groups_.empty());
+ EXPECT_EQ(cache, group->newest_complete_cache());
+ EXPECT_TRUE(cache->is_complete());
+}
+
+TEST_F(MockAppCacheStorageTest, StoreExistingGroup) {
+ // Store a group and its newest cache. Should complete asyncly.
+ MockAppCacheService service;
+ MockAppCacheStorage* storage =
+ reinterpret_cast<MockAppCacheStorage*>(service.storage());
+
+ // Setup some preconditions. Create a group and old complete cache
+ // that appear to be "stored", and a newest unstored complete cache.
+ GURL manifest_url("http://blah/");
+ scoped_refptr<AppCacheGroup> group(
+ new AppCacheGroup(service.storage(), manifest_url, 111));
+ int64 old_cache_id = storage->NewCacheId();
+ scoped_refptr<AppCache> old_cache(
+ new AppCache(service.storage(), old_cache_id));
+ old_cache->set_complete(true);
+ group->AddCache(old_cache.get());
+ storage->AddStoredGroup(group.get());
+ storage->AddStoredCache(old_cache.get());
+ int64 new_cache_id = storage->NewCacheId();
+ scoped_refptr<AppCache> new_cache(
+ new AppCache(service.storage(), new_cache_id));
+ // Hold our refs to simulate the UpdateJob holding these refs.
+
+ // Conduct the test.
+ MockStorageDelegate delegate;
+ EXPECT_EQ(size_t(1), storage->stored_caches_.size());
+ EXPECT_EQ(size_t(1), storage->stored_groups_.size());
+ EXPECT_TRUE(storage->IsCacheStored(old_cache.get()));
+ EXPECT_FALSE(storage->IsCacheStored(new_cache.get()));
+ storage->StoreGroupAndNewestCache(group.get(), new_cache.get(), &delegate);
+ EXPECT_FALSE(delegate.stored_group_success_);
+ EXPECT_EQ(size_t(1), storage->stored_caches_.size());
+ EXPECT_EQ(size_t(1), storage->stored_groups_.size());
+ EXPECT_TRUE(storage->IsCacheStored(old_cache.get()));
+ EXPECT_FALSE(storage->IsCacheStored(new_cache.get()));
+ base::RunLoop().RunUntilIdle(); // Do async task execution.
+ EXPECT_TRUE(delegate.stored_group_success_);
+ EXPECT_EQ(size_t(1), storage->stored_caches_.size());
+ EXPECT_EQ(size_t(1), storage->stored_groups_.size());
+ EXPECT_FALSE(storage->IsCacheStored(old_cache.get()));
+ EXPECT_TRUE(storage->IsCacheStored(new_cache.get()));
+ EXPECT_EQ(new_cache.get(), group->newest_complete_cache());
+ EXPECT_TRUE(new_cache->is_complete());
+}
+
+TEST_F(MockAppCacheStorageTest, StoreExistingGroupExistingCache) {
+ // Store a group with updates to its existing newest complete cache.
+ MockAppCacheService service;
+ MockAppCacheStorage* storage =
+ reinterpret_cast<MockAppCacheStorage*>(service.storage());
+
+ // Setup some preconditions. Create a group and a complete cache that
+ // appear to be "stored".
+ GURL manifest_url("http://blah");
+ scoped_refptr<AppCacheGroup> group(
+ new AppCacheGroup(service.storage(), manifest_url, 111));
+ int64 cache_id = storage->NewCacheId();
+ scoped_refptr<AppCache> cache(new AppCache(service.storage(), cache_id));
+ cache->set_complete(true);
+ group->AddCache(cache.get());
+ storage->AddStoredGroup(group.get());
+ storage->AddStoredCache(cache.get());
+ // Hold our refs to simulate the UpdateJob holding these refs.
+
+ // Change the group's newest cache.
+ EXPECT_EQ(cache, group->newest_complete_cache());
+ GURL entry_url("http://blah/blah");
+ cache->AddEntry(entry_url, AppCacheEntry(AppCacheEntry::MASTER));
+
+ // Conduct the test.
+ MockStorageDelegate delegate;
+ EXPECT_EQ(size_t(1), storage->stored_caches_.size());
+ EXPECT_EQ(size_t(1), storage->stored_groups_.size());
+ EXPECT_TRUE(storage->IsCacheStored(cache.get()));
+ storage->StoreGroupAndNewestCache(group.get(), cache.get(), &delegate);
+ EXPECT_FALSE(delegate.stored_group_success_);
+ EXPECT_EQ(size_t(1), storage->stored_caches_.size());
+ EXPECT_EQ(size_t(1), storage->stored_groups_.size());
+ base::RunLoop().RunUntilIdle(); // Do async task execution.
+ EXPECT_TRUE(delegate.stored_group_success_);
+ EXPECT_EQ(size_t(1), storage->stored_caches_.size());
+ EXPECT_EQ(size_t(1), storage->stored_groups_.size());
+ EXPECT_TRUE(storage->IsCacheStored(cache.get()));
+ EXPECT_EQ(cache, group->newest_complete_cache());
+ EXPECT_TRUE(cache->GetEntry(entry_url));
+}
+
+TEST_F(MockAppCacheStorageTest, MakeGroupObsolete) {
+ // Make a group obsolete, should complete asyncly.
+ MockAppCacheService service;
+ MockAppCacheStorage* storage =
+ reinterpret_cast<MockAppCacheStorage*>(service.storage());
+
+ // Setup some preconditions. Create a group and newest cache that
+ // appears to be "stored" and "currently in use".
+ GURL manifest_url("http://blah/");
+ scoped_refptr<AppCacheGroup> group(
+ new AppCacheGroup(service.storage(), manifest_url, 111));
+ int64 cache_id = storage->NewCacheId();
+ scoped_refptr<AppCache> cache(new AppCache(service.storage(), cache_id));
+ cache->set_complete(true);
+ group->AddCache(cache.get());
+ storage->AddStoredGroup(group.get());
+ storage->AddStoredCache(cache.get());
+ // Hold our refs to simulate the UpdateJob holding these refs.
+
+ // Conduct the test.
+ MockStorageDelegate delegate;
+ EXPECT_FALSE(group->is_obsolete());
+ EXPECT_EQ(size_t(1), storage->stored_caches_.size());
+ EXPECT_EQ(size_t(1), storage->stored_groups_.size());
+ EXPECT_FALSE(cache->HasOneRef());
+ EXPECT_FALSE(group->HasOneRef());
+ storage->MakeGroupObsolete(group.get(), &delegate, 0);
+ EXPECT_FALSE(group->is_obsolete());
+ EXPECT_EQ(size_t(1), storage->stored_caches_.size());
+ EXPECT_EQ(size_t(1), storage->stored_groups_.size());
+ EXPECT_FALSE(cache->HasOneRef());
+ EXPECT_FALSE(group->HasOneRef());
+ base::RunLoop().RunUntilIdle(); // Do async task execution.
+ EXPECT_TRUE(delegate.obsoleted_success_);
+ EXPECT_EQ(group.get(), delegate.obsoleted_group_.get());
+ EXPECT_TRUE(group->is_obsolete());
+ EXPECT_TRUE(storage->stored_caches_.empty());
+ EXPECT_TRUE(storage->stored_groups_.empty());
+ EXPECT_TRUE(cache->HasOneRef());
+ EXPECT_FALSE(group->HasOneRef());
+ delegate.obsoleted_group_ = NULL;
+ cache = NULL;
+ EXPECT_TRUE(group->HasOneRef());
+}
+
+TEST_F(MockAppCacheStorageTest, MarkEntryAsForeign) {
+ // Should complete syncly.
+ MockAppCacheService service;
+ MockAppCacheStorage* storage =
+ reinterpret_cast<MockAppCacheStorage*>(service.storage());
+
+ // Setup some preconditions. Create a cache with an entry.
+ GURL entry_url("http://blah/entry");
+ int64 cache_id = storage->NewCacheId();
+ scoped_refptr<AppCache> cache(new AppCache(service.storage(), cache_id));
+ cache->AddEntry(entry_url, AppCacheEntry(AppCacheEntry::EXPLICIT));
+
+ // Conduct the test.
+ MockStorageDelegate delegate;
+ EXPECT_FALSE(cache->GetEntry(entry_url)->IsForeign());
+ storage->MarkEntryAsForeign(entry_url, cache_id);
+ EXPECT_TRUE(cache->GetEntry(entry_url)->IsForeign());
+ EXPECT_TRUE(cache->GetEntry(entry_url)->IsExplicit());
+}
+
+TEST_F(MockAppCacheStorageTest, FindNoMainResponse) {
+ // Should complete asyncly.
+ MockAppCacheService service;
+ MockAppCacheStorage* storage =
+ reinterpret_cast<MockAppCacheStorage*>(service.storage());
+
+ // Conduct the test.
+ MockStorageDelegate delegate;
+ GURL url("http://blah/some_url");
+ EXPECT_NE(url, delegate.found_url_);
+ storage->FindResponseForMainRequest(url, GURL(), &delegate);
+ EXPECT_NE(url, delegate.found_url_);
+ base::RunLoop().RunUntilIdle(); // Do async task execution.
+ EXPECT_EQ(url, delegate.found_url_);
+ EXPECT_TRUE(delegate.found_manifest_url_.is_empty());
+ EXPECT_EQ(kAppCacheNoCacheId, delegate.found_cache_id_);
+ EXPECT_EQ(kAppCacheNoResponseId, delegate.found_entry_.response_id());
+ EXPECT_EQ(kAppCacheNoResponseId,
+ delegate.found_fallback_entry_.response_id());
+ EXPECT_TRUE(delegate.found_fallback_url_.is_empty());
+ EXPECT_EQ(0, delegate.found_entry_.types());
+ EXPECT_EQ(0, delegate.found_fallback_entry_.types());
+}
+
+TEST_F(MockAppCacheStorageTest, BasicFindMainResponse) {
+ // Should complete asyncly.
+ MockAppCacheService service;
+ MockAppCacheStorage* storage =
+ reinterpret_cast<MockAppCacheStorage*>(service.storage());
+
+ // Setup some preconditions. Create a complete cache with an entry.
+ const int64 kCacheId = storage->NewCacheId();
+ const GURL kEntryUrl("http://blah/entry");
+ const GURL kManifestUrl("http://blah/manifest");
+ const int64 kResponseId = 1;
+ scoped_refptr<AppCache> cache(new AppCache(service.storage(), kCacheId));
+ cache->AddEntry(
+ kEntryUrl, AppCacheEntry(AppCacheEntry::EXPLICIT, kResponseId));
+ cache->set_complete(true);
+ scoped_refptr<AppCacheGroup> group(
+ new AppCacheGroup(service.storage(), kManifestUrl, 111));
+ group->AddCache(cache.get());
+ storage->AddStoredGroup(group.get());
+ storage->AddStoredCache(cache.get());
+
+ // Conduct the test.
+ MockStorageDelegate delegate;
+ EXPECT_NE(kEntryUrl, delegate.found_url_);
+ storage->FindResponseForMainRequest(kEntryUrl, GURL(), &delegate);
+ EXPECT_NE(kEntryUrl, delegate.found_url_);
+ base::RunLoop().RunUntilIdle(); // Do async task execution.
+ EXPECT_EQ(kEntryUrl, delegate.found_url_);
+ EXPECT_EQ(kManifestUrl, delegate.found_manifest_url_);
+ EXPECT_EQ(kCacheId, delegate.found_cache_id_);
+ EXPECT_EQ(kResponseId, delegate.found_entry_.response_id());
+ EXPECT_TRUE(delegate.found_entry_.IsExplicit());
+ EXPECT_FALSE(delegate.found_fallback_entry_.has_response_id());
+}
+
+TEST_F(MockAppCacheStorageTest, BasicFindMainFallbackResponse) {
+ // Should complete asyncly.
+ MockAppCacheService service;
+ MockAppCacheStorage* storage =
+ reinterpret_cast<MockAppCacheStorage*>(service.storage());
+
+ // Setup some preconditions. Create a complete cache with a
+ // fallback namespace and entry.
+ const int64 kCacheId = storage->NewCacheId();
+ const GURL kFallbackEntryUrl1("http://blah/fallback_entry1");
+ const GURL kFallbackNamespaceUrl1("http://blah/fallback_namespace/");
+ const GURL kFallbackEntryUrl2("http://blah/fallback_entry2");
+ const GURL kFallbackNamespaceUrl2("http://blah/fallback_namespace/longer");
+ const GURL kManifestUrl("http://blah/manifest");
+ const int64 kResponseId1 = 1;
+ const int64 kResponseId2 = 2;
+
+ Manifest manifest;
+ manifest.fallback_namespaces.push_back(
+ Namespace(APPCACHE_FALLBACK_NAMESPACE, kFallbackNamespaceUrl1,
+ kFallbackEntryUrl1, false));
+ manifest.fallback_namespaces.push_back(
+ Namespace(APPCACHE_FALLBACK_NAMESPACE, kFallbackNamespaceUrl2,
+ kFallbackEntryUrl2, false));
+
+ scoped_refptr<AppCache> cache(new AppCache(service.storage(), kCacheId));
+ cache->InitializeWithManifest(&manifest);
+ cache->AddEntry(kFallbackEntryUrl1,
+ AppCacheEntry(AppCacheEntry::FALLBACK, kResponseId1));
+ cache->AddEntry(kFallbackEntryUrl2,
+ AppCacheEntry(AppCacheEntry::FALLBACK, kResponseId2));
+ cache->set_complete(true);
+
+ scoped_refptr<AppCacheGroup> group(
+ new AppCacheGroup(service.storage(), kManifestUrl, 111));
+ group->AddCache(cache.get());
+ storage->AddStoredGroup(group.get());
+ storage->AddStoredCache(cache.get());
+
+ // The test url is in both fallback namespace urls, but should match
+ // the longer of the two.
+ const GURL kTestUrl("http://blah/fallback_namespace/longer/test");
+
+ // Conduct the test.
+ MockStorageDelegate delegate;
+ EXPECT_NE(kTestUrl, delegate.found_url_);
+ storage->FindResponseForMainRequest(kTestUrl, GURL(), &delegate);
+ EXPECT_NE(kTestUrl, delegate.found_url_);
+ base::RunLoop().RunUntilIdle(); // Do async task execution.
+ EXPECT_EQ(kTestUrl, delegate.found_url_);
+ EXPECT_EQ(kManifestUrl, delegate.found_manifest_url_);
+ EXPECT_EQ(kCacheId, delegate.found_cache_id_);
+ EXPECT_FALSE(delegate.found_entry_.has_response_id());
+ EXPECT_EQ(kResponseId2, delegate.found_fallback_entry_.response_id());
+ EXPECT_EQ(kFallbackEntryUrl2, delegate.found_fallback_url_);
+ EXPECT_TRUE(delegate.found_fallback_entry_.IsFallback());
+}
+
+TEST_F(MockAppCacheStorageTest, FindMainResponseWithMultipleCandidates) {
+ // Should complete asyncly.
+ MockAppCacheService service;
+ MockAppCacheStorage* storage =
+ reinterpret_cast<MockAppCacheStorage*>(service.storage());
+
+ // Setup some preconditions. Create 2 complete caches with an entry
+ // for the same url.
+
+ const GURL kEntryUrl("http://blah/entry");
+ const int64 kCacheId1 = storage->NewCacheId();
+ const int64 kCacheId2 = storage->NewCacheId();
+ const GURL kManifestUrl1("http://blah/manifest1");
+ const GURL kManifestUrl2("http://blah/manifest2");
+ const int64 kResponseId1 = 1;
+ const int64 kResponseId2 = 2;
+
+ // The first cache.
+ scoped_refptr<AppCache> cache(new AppCache(service.storage(), kCacheId1));
+ cache->AddEntry(
+ kEntryUrl, AppCacheEntry(AppCacheEntry::EXPLICIT, kResponseId1));
+ cache->set_complete(true);
+ scoped_refptr<AppCacheGroup> group(
+ new AppCacheGroup(service.storage(), kManifestUrl1, 111));
+ group->AddCache(cache.get());
+ storage->AddStoredGroup(group.get());
+ storage->AddStoredCache(cache.get());
+ // Drop our references to cache1 so it appears as "not in use".
+ cache = NULL;
+ group = NULL;
+
+ // The second cache.
+ cache = new AppCache(service.storage(), kCacheId2);
+ cache->AddEntry(
+ kEntryUrl, AppCacheEntry(AppCacheEntry::EXPLICIT, kResponseId2));
+ cache->set_complete(true);
+ group = new AppCacheGroup(service.storage(), kManifestUrl2, 222);
+ group->AddCache(cache.get());
+ storage->AddStoredGroup(group.get());
+ storage->AddStoredCache(cache.get());
+
+ // Conduct the test, we should find the response from the second cache
+ // since it's "in use".
+ MockStorageDelegate delegate;
+ EXPECT_NE(kEntryUrl, delegate.found_url_);
+ storage->FindResponseForMainRequest(kEntryUrl, GURL(), &delegate);
+ EXPECT_NE(kEntryUrl, delegate.found_url_);
+ base::RunLoop().RunUntilIdle(); // Do async task execution.
+ EXPECT_EQ(kEntryUrl, delegate.found_url_);
+ EXPECT_EQ(kManifestUrl2, delegate.found_manifest_url_);
+ EXPECT_EQ(kCacheId2, delegate.found_cache_id_);
+ EXPECT_EQ(kResponseId2, delegate.found_entry_.response_id());
+ EXPECT_TRUE(delegate.found_entry_.IsExplicit());
+ EXPECT_FALSE(delegate.found_fallback_entry_.has_response_id());
+}
+
+TEST_F(MockAppCacheStorageTest, FindMainResponseExclusions) {
+ // Should complete asyncly.
+ MockAppCacheService service;
+ MockAppCacheStorage* storage =
+ reinterpret_cast<MockAppCacheStorage*>(service.storage());
+
+ // Setup some preconditions. Create a complete cache with a
+ // foreign entry and an online namespace.
+
+ const int64 kCacheId = storage->NewCacheId();
+ const GURL kEntryUrl("http://blah/entry");
+ const GURL kManifestUrl("http://blah/manifest");
+ const GURL kOnlineNamespaceUrl("http://blah/online_namespace");
+ const int64 kResponseId = 1;
+
+ Manifest manifest;
+ manifest.online_whitelist_namespaces.push_back(
+ Namespace(APPCACHE_NETWORK_NAMESPACE, kOnlineNamespaceUrl,
+ GURL(), false));
+ scoped_refptr<AppCache> cache(new AppCache(service.storage(), kCacheId));
+ cache->InitializeWithManifest(&manifest);
+ cache->AddEntry(
+ kEntryUrl,
+ AppCacheEntry(AppCacheEntry::EXPLICIT | AppCacheEntry::FOREIGN,
+ kResponseId));
+ cache->set_complete(true);
+ scoped_refptr<AppCacheGroup> group(
+ new AppCacheGroup(service.storage(), kManifestUrl, 111));
+ group->AddCache(cache.get());
+ storage->AddStoredGroup(group.get());
+ storage->AddStoredCache(cache.get());
+
+ MockStorageDelegate delegate;
+
+ // We should not find anything for the foreign entry.
+ EXPECT_NE(kEntryUrl, delegate.found_url_);
+ storage->FindResponseForMainRequest(kEntryUrl, GURL(), &delegate);
+ EXPECT_NE(kEntryUrl, delegate.found_url_);
+ base::RunLoop().RunUntilIdle(); // Do async task execution.
+ EXPECT_EQ(kEntryUrl, delegate.found_url_);
+ EXPECT_TRUE(delegate.found_manifest_url_.is_empty());
+ EXPECT_EQ(kAppCacheNoCacheId, delegate.found_cache_id_);
+ EXPECT_EQ(kAppCacheNoResponseId, delegate.found_entry_.response_id());
+ EXPECT_EQ(kAppCacheNoResponseId,
+ delegate.found_fallback_entry_.response_id());
+ EXPECT_TRUE(delegate.found_fallback_url_.is_empty());
+ EXPECT_EQ(0, delegate.found_entry_.types());
+ EXPECT_EQ(0, delegate.found_fallback_entry_.types());
+
+ // We should not find anything for the online namespace.
+ EXPECT_NE(kOnlineNamespaceUrl, delegate.found_url_);
+ storage->FindResponseForMainRequest(kOnlineNamespaceUrl, GURL(), &delegate);
+ EXPECT_NE(kOnlineNamespaceUrl, delegate.found_url_);
+ base::RunLoop().RunUntilIdle(); // Do async task execution.
+ EXPECT_EQ(kOnlineNamespaceUrl, delegate.found_url_);
+ EXPECT_TRUE(delegate.found_manifest_url_.is_empty());
+ EXPECT_EQ(kAppCacheNoCacheId, delegate.found_cache_id_);
+ EXPECT_EQ(kAppCacheNoResponseId, delegate.found_entry_.response_id());
+ EXPECT_EQ(kAppCacheNoResponseId,
+ delegate.found_fallback_entry_.response_id());
+ EXPECT_TRUE(delegate.found_fallback_url_.is_empty());
+ EXPECT_EQ(0, delegate.found_entry_.types());
+ EXPECT_EQ(0, delegate.found_fallback_entry_.types());
+}
+
+} // namespace content
diff --git a/chromium/content/browser/appcache/view_appcache_internals_job.cc b/chromium/content/browser/appcache/view_appcache_internals_job.cc
new file mode 100644
index 00000000000..0cc64c361f0
--- /dev/null
+++ b/chromium/content/browser/appcache/view_appcache_internals_job.cc
@@ -0,0 +1,681 @@
+// 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/appcache/view_appcache_internals_job.h"
+
+#include <algorithm>
+#include <string>
+
+#include "base/base64.h"
+#include "base/bind.h"
+#include "base/format_macros.h"
+#include "base/i18n/time_formatting.h"
+#include "base/logging.h"
+#include "base/memory/weak_ptr.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 "net/base/escape.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_response_headers.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_simple_job.h"
+#include "net/url_request/view_cache_helper.h"
+#include "webkit/browser/appcache/appcache.h"
+#include "webkit/browser/appcache/appcache_group.h"
+#include "webkit/browser/appcache/appcache_policy.h"
+#include "webkit/browser/appcache/appcache_response.h"
+#include "webkit/browser/appcache/appcache_service_impl.h"
+#include "webkit/browser/appcache/appcache_storage.h"
+
+using appcache::AppCacheGroup;
+using appcache::AppCacheInfo;
+using appcache::AppCacheInfoCollection;
+using appcache::AppCacheInfoVector;
+using appcache::AppCacheServiceImpl;
+using appcache::AppCacheStorage;
+using appcache::AppCacheStorageReference;
+using appcache::AppCacheResourceInfo;
+using appcache::AppCacheResourceInfoVector;
+using appcache::AppCacheResponseInfo;
+using appcache::AppCacheResponseReader;
+
+namespace content {
+namespace {
+
+const char kErrorMessage[] = "Error in retrieving Application Caches.";
+const char kEmptyAppCachesMessage[] = "No available Application Caches.";
+const char kManifestNotFoundMessage[] = "Manifest not found.";
+const char kManifest[] = "Manifest: ";
+const char kSize[] = "Size: ";
+const char kCreationTime[] = "Creation Time: ";
+const char kLastAccessTime[] = "Last Access Time: ";
+const char kLastUpdateTime[] = "Last Update Time: ";
+const char kFormattedDisabledAppCacheMsg[] =
+ "<b><i><font color=\"FF0000\">"
+ "This Application Cache is disabled by policy.</font></i></b><br/>";
+const char kRemoveCacheLabel[] = "Remove";
+const char kViewCacheLabel[] = "View Entries";
+const char kRemoveCacheCommand[] = "remove-cache";
+const char kViewCacheCommand[] = "view-cache";
+const char kViewEntryCommand[] = "view-entry";
+
+void EmitPageStart(std::string* out) {
+ out->append(
+ "<!DOCTYPE HTML>\n"
+ "<html><title>AppCache Internals</title>\n"
+ "<meta http-equiv=\"Content-Security-Policy\""
+ " content=\"object-src 'none'; script-src 'none'\">\n"
+ "<style>\n"
+ "body { font-family: sans-serif; font-size: 0.8em; }\n"
+ "tt, code, pre { font-family: WebKitHack, monospace; }\n"
+ "form { display: inline; }\n"
+ ".subsection_body { margin: 10px 0 10px 2em; }\n"
+ ".subsection_title { font-weight: bold; }\n"
+ "</style>\n"
+ "</head><body>\n");
+}
+
+void EmitPageEnd(std::string* out) {
+ out->append("</body></html>\n");
+}
+
+void EmitListItem(const std::string& label,
+ const std::string& data,
+ std::string* out) {
+ out->append("<li>");
+ out->append(net::EscapeForHTML(label));
+ out->append(net::EscapeForHTML(data));
+ out->append("</li>\n");
+}
+
+void EmitAnchor(const std::string& url, const std::string& text,
+ std::string* out) {
+ out->append("<a href=\"");
+ out->append(net::EscapeForHTML(url));
+ out->append("\">");
+ out->append(net::EscapeForHTML(text));
+ out->append("</a>");
+}
+
+void EmitCommandAnchor(const char* label,
+ const GURL& base_url,
+ const char* command,
+ const char* param,
+ std::string* out) {
+ std::string query(command);
+ query.push_back('=');
+ query.append(param);
+ GURL::Replacements replacements;
+ replacements.SetQuery(query.data(), url::Component(0, query.length()));
+ GURL command_url = base_url.ReplaceComponents(replacements);
+ EmitAnchor(command_url.spec(), label, out);
+}
+
+void EmitAppCacheInfo(const GURL& base_url,
+ AppCacheServiceImpl* service,
+ const AppCacheInfo* info,
+ std::string* out) {
+ std::string manifest_url_base64;
+ base::Base64Encode(info->manifest_url.spec(), &manifest_url_base64);
+
+ out->append("\n<p>");
+ out->append(kManifest);
+ EmitAnchor(info->manifest_url.spec(), info->manifest_url.spec(), out);
+ out->append("<br/>\n");
+ if (!service->appcache_policy()->CanLoadAppCache(
+ info->manifest_url, info->manifest_url)) {
+ out->append(kFormattedDisabledAppCacheMsg);
+ }
+ out->append("\n<br/>\n");
+ EmitCommandAnchor(kRemoveCacheLabel, base_url,
+ kRemoveCacheCommand, manifest_url_base64.c_str(), out);
+ out->append("&nbsp;&nbsp;");
+ EmitCommandAnchor(kViewCacheLabel, base_url,
+ kViewCacheCommand, manifest_url_base64.c_str(), out);
+ out->append("\n<br/>\n");
+ out->append("<ul>");
+ EmitListItem(
+ kSize,
+ base::UTF16ToUTF8(FormatBytesUnlocalized(info->size)),
+ out);
+ EmitListItem(
+ kCreationTime,
+ base::UTF16ToUTF8(TimeFormatFriendlyDateAndTime(info->creation_time)),
+ out);
+ EmitListItem(
+ kLastUpdateTime,
+ base::UTF16ToUTF8(TimeFormatFriendlyDateAndTime(info->last_update_time)),
+ out);
+ EmitListItem(
+ kLastAccessTime,
+ base::UTF16ToUTF8(TimeFormatFriendlyDateAndTime(info->last_access_time)),
+ out);
+ out->append("</ul></p></br>\n");
+}
+
+void EmitAppCacheInfoVector(
+ const GURL& base_url,
+ AppCacheServiceImpl* service,
+ const AppCacheInfoVector& appcaches,
+ std::string* out) {
+ for (std::vector<AppCacheInfo>::const_iterator info =
+ appcaches.begin();
+ info != appcaches.end(); ++info) {
+ EmitAppCacheInfo(base_url, service, &(*info), out);
+ }
+}
+
+void EmitTableData(const std::string& data, bool align_right, bool bold,
+ std::string* out) {
+ if (align_right)
+ out->append("<td align='right'>");
+ else
+ out->append("<td>");
+ if (bold)
+ out->append("<b>");
+ out->append(data);
+ if (bold)
+ out->append("</b>");
+ out->append("</td>");
+}
+
+std::string FormFlagsString(const AppCacheResourceInfo& info) {
+ std::string str;
+ if (info.is_manifest)
+ str.append("Manifest, ");
+ if (info.is_master)
+ str.append("Master, ");
+ if (info.is_intercept)
+ str.append("Intercept, ");
+ if (info.is_fallback)
+ str.append("Fallback, ");
+ if (info.is_explicit)
+ str.append("Explicit, ");
+ if (info.is_foreign)
+ str.append("Foreign, ");
+ return str;
+}
+
+std::string FormViewEntryAnchor(const GURL& base_url,
+ const GURL& manifest_url, const GURL& entry_url,
+ int64 response_id,
+ int64 group_id) {
+ std::string manifest_url_base64;
+ std::string entry_url_base64;
+ std::string response_id_string;
+ std::string group_id_string;
+ base::Base64Encode(manifest_url.spec(), &manifest_url_base64);
+ base::Base64Encode(entry_url.spec(), &entry_url_base64);
+ response_id_string = base::Int64ToString(response_id);
+ group_id_string = base::Int64ToString(group_id);
+
+ std::string query(kViewEntryCommand);
+ query.push_back('=');
+ query.append(manifest_url_base64);
+ query.push_back('|');
+ query.append(entry_url_base64);
+ query.push_back('|');
+ query.append(response_id_string);
+ query.push_back('|');
+ query.append(group_id_string);
+
+ GURL::Replacements replacements;
+ replacements.SetQuery(query.data(), url::Component(0, query.length()));
+ GURL view_entry_url = base_url.ReplaceComponents(replacements);
+
+ std::string anchor;
+ EmitAnchor(view_entry_url.spec(), entry_url.spec(), &anchor);
+ return anchor;
+}
+
+void EmitAppCacheResourceInfoVector(
+ const GURL& base_url,
+ const GURL& manifest_url,
+ const AppCacheResourceInfoVector& resource_infos,
+ int64 group_id,
+ std::string* out) {
+ out->append("<table border='0'>\n");
+ out->append("<tr>");
+ EmitTableData("Flags", false, true, out);
+ EmitTableData("URL", false, true, out);
+ EmitTableData("Size (headers and data)", true, true, out);
+ out->append("</tr>\n");
+ for (AppCacheResourceInfoVector::const_iterator
+ iter = resource_infos.begin();
+ iter != resource_infos.end(); ++iter) {
+ out->append("<tr>");
+ EmitTableData(FormFlagsString(*iter), false, false, out);
+ EmitTableData(FormViewEntryAnchor(base_url, manifest_url,
+ iter->url, iter->response_id,
+ group_id),
+ false, false, out);
+ EmitTableData(base::UTF16ToUTF8(FormatBytesUnlocalized(iter->size)),
+ true, false, out);
+ out->append("</tr>\n");
+ }
+ out->append("</table>\n");
+}
+
+void EmitResponseHeaders(net::HttpResponseHeaders* headers, std::string* out) {
+ out->append("<hr><pre>");
+ out->append(net::EscapeForHTML(headers->GetStatusLine()));
+ out->push_back('\n');
+
+ void* iter = NULL;
+ std::string name, value;
+ while (headers->EnumerateHeaderLines(&iter, &name, &value)) {
+ out->append(net::EscapeForHTML(name));
+ out->append(": ");
+ out->append(net::EscapeForHTML(value));
+ out->push_back('\n');
+ }
+ out->append("</pre>");
+}
+
+void EmitHexDump(const char *buf, size_t buf_len, size_t total_len,
+ std::string* out) {
+ out->append("<hr><pre>");
+ base::StringAppendF(out, "Showing %d of %d bytes\n\n",
+ static_cast<int>(buf_len), static_cast<int>(total_len));
+ net::ViewCacheHelper::HexDump(buf, buf_len, out);
+ if (buf_len < total_len)
+ out->append("\nNote: data is truncated...");
+ out->append("</pre>");
+}
+
+GURL DecodeBase64URL(const std::string& base64) {
+ std::string url;
+ base::Base64Decode(base64, &url);
+ return GURL(url);
+}
+
+bool ParseQuery(const std::string& query,
+ std::string* command, std::string* value) {
+ size_t position = query.find("=");
+ if (position == std::string::npos)
+ return false;
+ *command = query.substr(0, position);
+ *value = query.substr(position + 1);
+ return !command->empty() && !value->empty();
+}
+
+bool SortByManifestUrl(const AppCacheInfo& lhs,
+ const AppCacheInfo& rhs) {
+ return lhs.manifest_url.spec() < rhs.manifest_url.spec();
+}
+
+bool SortByResourceUrl(const AppCacheResourceInfo& lhs,
+ const AppCacheResourceInfo& rhs) {
+ return lhs.url.spec() < rhs.url.spec();
+}
+
+GURL ClearQuery(const GURL& url) {
+ GURL::Replacements replacements;
+ replacements.ClearQuery();
+ return url.ReplaceComponents(replacements);
+}
+
+// Simple base class for the job subclasses defined here.
+class BaseInternalsJob : public net::URLRequestSimpleJob,
+ public AppCacheServiceImpl::Observer {
+ protected:
+ BaseInternalsJob(net::URLRequest* request,
+ net::NetworkDelegate* network_delegate,
+ AppCacheServiceImpl* service)
+ : URLRequestSimpleJob(request, network_delegate),
+ appcache_service_(service),
+ appcache_storage_(service->storage()) {
+ appcache_service_->AddObserver(this);
+ }
+
+ virtual ~BaseInternalsJob() {
+ appcache_service_->RemoveObserver(this);
+ }
+
+ virtual void OnServiceReinitialized(
+ AppCacheStorageReference* old_storage_ref) OVERRIDE {
+ if (old_storage_ref->storage() == appcache_storage_)
+ disabled_storage_reference_ = old_storage_ref;
+ }
+
+ AppCacheServiceImpl* appcache_service_;
+ AppCacheStorage* appcache_storage_;
+ scoped_refptr<AppCacheStorageReference> disabled_storage_reference_;
+};
+
+// Job that lists all appcaches in the system.
+class MainPageJob : public BaseInternalsJob {
+ public:
+ MainPageJob(net::URLRequest* request,
+ net::NetworkDelegate* network_delegate,
+ AppCacheServiceImpl* service)
+ : BaseInternalsJob(request, network_delegate, service),
+ weak_factory_(this) {
+ }
+
+ virtual void Start() OVERRIDE {
+ DCHECK(request_);
+ info_collection_ = new AppCacheInfoCollection;
+ appcache_service_->GetAllAppCacheInfo(
+ info_collection_.get(),
+ base::Bind(&MainPageJob::OnGotInfoComplete,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ // Produces a page containing the listing
+ virtual int GetData(std::string* mime_type,
+ std::string* charset,
+ std::string* out,
+ const net::CompletionCallback& callback) const OVERRIDE {
+ mime_type->assign("text/html");
+ charset->assign("UTF-8");
+
+ out->clear();
+ EmitPageStart(out);
+ if (!info_collection_.get()) {
+ out->append(kErrorMessage);
+ } else if (info_collection_->infos_by_origin.empty()) {
+ out->append(kEmptyAppCachesMessage);
+ } else {
+ typedef std::map<GURL, AppCacheInfoVector> InfoByOrigin;
+ AppCacheInfoVector appcaches;
+ for (InfoByOrigin::const_iterator origin =
+ info_collection_->infos_by_origin.begin();
+ origin != info_collection_->infos_by_origin.end(); ++origin) {
+ appcaches.insert(appcaches.end(),
+ origin->second.begin(), origin->second.end());
+ }
+ std::sort(appcaches.begin(), appcaches.end(), SortByManifestUrl);
+
+ GURL base_url = ClearQuery(request_->url());
+ EmitAppCacheInfoVector(base_url, appcache_service_, appcaches, out);
+ }
+ EmitPageEnd(out);
+ return net::OK;
+ }
+
+ private:
+ virtual ~MainPageJob() {}
+
+ void OnGotInfoComplete(int rv) {
+ if (rv != net::OK)
+ info_collection_ = NULL;
+ StartAsync();
+ }
+
+ scoped_refptr<AppCacheInfoCollection> info_collection_;
+ base::WeakPtrFactory<MainPageJob> weak_factory_;
+ DISALLOW_COPY_AND_ASSIGN(MainPageJob);
+};
+
+// Job that redirects back to the main appcache internals page.
+class RedirectToMainPageJob : public BaseInternalsJob {
+ public:
+ RedirectToMainPageJob(net::URLRequest* request,
+ net::NetworkDelegate* network_delegate,
+ AppCacheServiceImpl* service)
+ : BaseInternalsJob(request, network_delegate, service) {}
+
+ virtual int GetData(std::string* mime_type,
+ std::string* charset,
+ std::string* data,
+ const net::CompletionCallback& callback) const OVERRIDE {
+ return net::OK; // IsRedirectResponse induces a redirect.
+ }
+
+ virtual bool IsRedirectResponse(GURL* location,
+ int* http_status_code) OVERRIDE {
+ *location = ClearQuery(request_->url());
+ *http_status_code = 307;
+ return true;
+ }
+
+ protected:
+ virtual ~RedirectToMainPageJob() {}
+};
+
+// Job that removes an appcache and then redirects back to the main page.
+class RemoveAppCacheJob : public RedirectToMainPageJob {
+ public:
+ RemoveAppCacheJob(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate,
+ AppCacheServiceImpl* service,
+ const GURL& manifest_url)
+ : RedirectToMainPageJob(request, network_delegate, service),
+ manifest_url_(manifest_url),
+ weak_factory_(this) {
+ }
+
+ virtual void Start() OVERRIDE {
+ DCHECK(request_);
+
+ appcache_service_->DeleteAppCacheGroup(
+ manifest_url_,base::Bind(&RemoveAppCacheJob::OnDeleteAppCacheComplete,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ private:
+ virtual ~RemoveAppCacheJob() {}
+
+ void OnDeleteAppCacheComplete(int rv) {
+ StartAsync(); // Causes the base class to redirect.
+ }
+
+ GURL manifest_url_;
+ base::WeakPtrFactory<RemoveAppCacheJob> weak_factory_;
+};
+
+
+// Job shows the details of a particular manifest url.
+class ViewAppCacheJob : public BaseInternalsJob,
+ public AppCacheStorage::Delegate {
+ public:
+ ViewAppCacheJob(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate,
+ AppCacheServiceImpl* service,
+ const GURL& manifest_url)
+ : BaseInternalsJob(request, network_delegate, service),
+ manifest_url_(manifest_url) {}
+
+ virtual void Start() OVERRIDE {
+ DCHECK(request_);
+ appcache_storage_->LoadOrCreateGroup(manifest_url_, this);
+ }
+
+ // Produces a page containing the entries listing.
+ virtual int GetData(std::string* mime_type,
+ std::string* charset,
+ std::string* out,
+ const net::CompletionCallback& callback) const OVERRIDE {
+ mime_type->assign("text/html");
+ charset->assign("UTF-8");
+ out->clear();
+ EmitPageStart(out);
+ if (appcache_info_.manifest_url.is_empty()) {
+ out->append(kManifestNotFoundMessage);
+ } else {
+ GURL base_url = ClearQuery(request_->url());
+ EmitAppCacheInfo(base_url, appcache_service_, &appcache_info_, out);
+ EmitAppCacheResourceInfoVector(base_url,
+ manifest_url_,
+ resource_infos_,
+ appcache_info_.group_id,
+ out);
+ }
+ EmitPageEnd(out);
+ return net::OK;
+ }
+
+ private:
+ virtual ~ViewAppCacheJob() {
+ appcache_storage_->CancelDelegateCallbacks(this);
+ }
+
+ // AppCacheStorage::Delegate override
+ virtual void OnGroupLoaded(
+ AppCacheGroup* group, const GURL& manifest_url) OVERRIDE {
+ DCHECK_EQ(manifest_url_, manifest_url);
+ if (group && group->newest_complete_cache()) {
+ appcache_info_.manifest_url = manifest_url;
+ appcache_info_.group_id = group->group_id();
+ appcache_info_.size = group->newest_complete_cache()->cache_size();
+ appcache_info_.creation_time = group->creation_time();
+ appcache_info_.last_update_time =
+ group->newest_complete_cache()->update_time();
+ appcache_info_.last_access_time = base::Time::Now();
+ group->newest_complete_cache()->ToResourceInfoVector(&resource_infos_);
+ std::sort(resource_infos_.begin(), resource_infos_.end(),
+ SortByResourceUrl);
+ }
+ StartAsync();
+ }
+
+ GURL manifest_url_;
+ AppCacheInfo appcache_info_;
+ AppCacheResourceInfoVector resource_infos_;
+ DISALLOW_COPY_AND_ASSIGN(ViewAppCacheJob);
+};
+
+// Job that shows the details of a particular cached resource.
+class ViewEntryJob : public BaseInternalsJob,
+ public AppCacheStorage::Delegate {
+ public:
+ ViewEntryJob(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate,
+ AppCacheServiceImpl* service,
+ const GURL& manifest_url,
+ const GURL& entry_url,
+ int64 response_id, int64 group_id)
+ : BaseInternalsJob(request, network_delegate, service),
+ manifest_url_(manifest_url), entry_url_(entry_url),
+ response_id_(response_id), group_id_(group_id), amount_read_(0) {
+ }
+
+ virtual void Start() OVERRIDE {
+ DCHECK(request_);
+ appcache_storage_->LoadResponseInfo(
+ manifest_url_, group_id_, response_id_, this);
+ }
+
+ // Produces a page containing the response headers and data.
+ virtual int GetData(std::string* mime_type,
+ std::string* charset,
+ std::string* out,
+ const net::CompletionCallback& callback) const OVERRIDE {
+ mime_type->assign("text/html");
+ charset->assign("UTF-8");
+ out->clear();
+ EmitPageStart(out);
+ EmitAnchor(entry_url_.spec(), entry_url_.spec(), out);
+ out->append("<br/>\n");
+ if (response_info_.get()) {
+ if (response_info_->http_response_info())
+ EmitResponseHeaders(response_info_->http_response_info()->headers.get(),
+ out);
+ else
+ out->append("Failed to read response headers.<br>");
+
+ if (response_data_.get()) {
+ EmitHexDump(response_data_->data(),
+ amount_read_,
+ response_info_->response_data_size(),
+ out);
+ } else {
+ out->append("Failed to read response data.<br>");
+ }
+ } else {
+ out->append("Failed to read response headers and data.<br>");
+ }
+ EmitPageEnd(out);
+ return net::OK;
+ }
+
+ private:
+ virtual ~ViewEntryJob() {
+ appcache_storage_->CancelDelegateCallbacks(this);
+ }
+
+ virtual void OnResponseInfoLoaded(
+ AppCacheResponseInfo* response_info, int64 response_id) OVERRIDE {
+ if (!response_info) {
+ StartAsync();
+ return;
+ }
+ response_info_ = response_info;
+
+ // Read the response data, truncating if its too large.
+ const int64 kLimit = 100 * 1000;
+ int64 amount_to_read =
+ std::min(kLimit, response_info->response_data_size());
+ response_data_ = new net::IOBuffer(amount_to_read);
+
+ reader_.reset(appcache_storage_->CreateResponseReader(
+ manifest_url_, group_id_, response_id_));
+ reader_->ReadData(
+ response_data_.get(),
+ amount_to_read,
+ base::Bind(&ViewEntryJob::OnReadComplete, base::Unretained(this)));
+ }
+
+ void OnReadComplete(int result) {
+ reader_.reset();
+ amount_read_ = result;
+ if (result < 0)
+ response_data_ = NULL;
+ StartAsync();
+ }
+
+ GURL manifest_url_;
+ GURL entry_url_;
+ int64 response_id_;
+ int64 group_id_;
+ scoped_refptr<AppCacheResponseInfo> response_info_;
+ scoped_refptr<net::IOBuffer> response_data_;
+ int amount_read_;
+ scoped_ptr<AppCacheResponseReader> reader_;
+};
+
+} // namespace
+
+net::URLRequestJob* ViewAppCacheInternalsJobFactory::CreateJobForRequest(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate,
+ AppCacheServiceImpl* service) {
+ if (!request->url().has_query())
+ return new MainPageJob(request, network_delegate, service);
+
+ std::string command;
+ std::string param;
+ ParseQuery(request->url().query(), &command, &param);
+
+ if (command == kRemoveCacheCommand)
+ return new RemoveAppCacheJob(request, network_delegate, service,
+ DecodeBase64URL(param));
+
+ if (command == kViewCacheCommand)
+ return new ViewAppCacheJob(request, network_delegate, service,
+ DecodeBase64URL(param));
+
+ std::vector<std::string> tokens;
+ int64 response_id;
+ int64 group_id;
+ if (command == kViewEntryCommand && Tokenize(param, "|", &tokens) == 4u &&
+ base::StringToInt64(tokens[2], &response_id) &&
+ base::StringToInt64(tokens[3], &group_id)) {
+ return new ViewEntryJob(request, network_delegate, service,
+ DecodeBase64URL(tokens[0]), // manifest url
+ DecodeBase64URL(tokens[1]), // entry url
+ response_id, group_id);
+ }
+
+ return new RedirectToMainPageJob(request, network_delegate, service);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/appcache/view_appcache_internals_job.h b/chromium/content/browser/appcache/view_appcache_internals_job.h
new file mode 100644
index 00000000000..f11b68e5875
--- /dev/null
+++ b/chromium/content/browser/appcache/view_appcache_internals_job.h
@@ -0,0 +1,35 @@
+// 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_APPCACHE_VIEW_APPCACHE_INTERNALS_JOB_H_
+#define CONTENT_BROWSER_APPCACHE_VIEW_APPCACHE_INTERNALS_JOB_H_
+
+#include "base/basictypes.h"
+
+namespace net {
+class NetworkDelegate;
+class URLRequest;
+class URLRequestJob;
+}
+
+namespace appcache {
+class AppCacheServiceImpl;
+}
+
+namespace content {
+
+class ViewAppCacheInternalsJobFactory {
+ public:
+ static net::URLRequestJob* CreateJobForRequest(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate,
+ appcache::AppCacheServiceImpl* service);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ViewAppCacheInternalsJobFactory);
+};
+
+} // namespace appcache
+
+#endif // CONTENT_BROWSER_APPCACHE_VIEW_APPCACHE_INTERNALS_JOB_H_
diff --git a/chromium/content/browser/aura/image_transport_factory.cc b/chromium/content/browser/aura/image_transport_factory.cc
deleted file mode 100644
index 4a20e7d9af1..00000000000
--- a/chromium/content/browser/aura/image_transport_factory.cc
+++ /dev/null
@@ -1,49 +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/aura/image_transport_factory.h"
-
-#include "content/browser/aura/gpu_process_transport_factory.h"
-#include "content/browser/aura/no_transport_image_transport_factory.h"
-#include "ui/compositor/compositor.h"
-
-namespace content {
-
-namespace {
-ImageTransportFactory* g_factory = NULL;
-bool g_initialized_for_unit_tests = false;
-}
-
-// static
-void ImageTransportFactory::Initialize() {
- DCHECK(!g_factory || g_initialized_for_unit_tests);
- if (g_initialized_for_unit_tests)
- return;
- g_factory = new GpuProcessTransportFactory;
- ui::ContextFactory::SetInstance(g_factory->AsContextFactory());
-}
-
-void ImageTransportFactory::InitializeForUnitTests(
- scoped_ptr<ui::ContextFactory> test_factory) {
- DCHECK(!g_factory);
- DCHECK(!g_initialized_for_unit_tests);
- g_initialized_for_unit_tests = true;
- g_factory = new NoTransportImageTransportFactory(test_factory.Pass());
- ui::ContextFactory::SetInstance(g_factory->AsContextFactory());
-}
-
-// static
-void ImageTransportFactory::Terminate() {
- ui::ContextFactory::SetInstance(NULL);
- delete g_factory;
- g_factory = NULL;
- g_initialized_for_unit_tests = false;
-}
-
-// static
-ImageTransportFactory* ImageTransportFactory::GetInstance() {
- return g_factory;
-}
-
-} // namespace content
diff --git a/chromium/content/browser/aura/image_transport_factory_browsertest.cc b/chromium/content/browser/aura/image_transport_factory_browsertest.cc
deleted file mode 100644
index bf770a7e857..00000000000
--- a/chromium/content/browser/aura/image_transport_factory_browsertest.cc
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/run_loop.h"
-#include "cc/output/context_provider.h"
-#include "content/browser/aura/image_transport_factory.h"
-#include "content/public/browser/gpu_data_manager.h"
-#include "content/test/content_browser_test.h"
-#include "gpu/GLES2/gl2extchromium.h"
-#include "gpu/command_buffer/client/gles2_interface.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "ui/compositor/compositor.h"
-
-namespace content {
-namespace {
-
-class ImageTransportFactoryBrowserTest : public ContentBrowserTest {
- public:
- ImageTransportFactoryBrowserTest() {}
-
- virtual void SetUp() OVERRIDE {
- UseRealGLContexts();
- ContentBrowserTest::SetUp();
- }
-};
-
-class MockImageTransportFactoryObserver : public ImageTransportFactoryObserver {
- public:
- MOCK_METHOD0(OnLostResources, void());
-};
-
-// Checks that upon context loss, the observer is called and the created
-// resources are reset.
-IN_PROC_BROWSER_TEST_F(ImageTransportFactoryBrowserTest, TestLostContext) {
- // This test doesn't make sense in software compositing mode.
- if (!GpuDataManager::GetInstance()->CanUseGpuBrowserCompositor())
- return;
-
- ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
- scoped_refptr<ui::Texture> texture = factory->CreateTransportClient(1.f);
- ASSERT_TRUE(texture.get());
-
- MockImageTransportFactoryObserver observer;
- factory->AddObserver(&observer);
-
- base::RunLoop run_loop;
- EXPECT_CALL(observer, OnLostResources())
- .WillOnce(testing::InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
-
- ui::ContextFactory* context_factory = ui::ContextFactory::GetInstance();
-
- gpu::gles2::GLES2Interface* gl =
- context_factory->SharedMainThreadContextProvider()->ContextGL();
- gl->LoseContextCHROMIUM(GL_GUILTY_CONTEXT_RESET_ARB,
- GL_INNOCENT_CONTEXT_RESET_ARB);
-
- // We have to flush to make sure that the client side gets a chance to notice
- // the context is gone.
- gl->Flush();
-
- run_loop.Run();
- EXPECT_EQ(0u, texture->PrepareTexture());
-
- factory->RemoveObserver(&observer);
-}
-
-} // anonymous namespace
-} // namespace content
diff --git a/chromium/content/browser/aura/no_transport_image_transport_factory.cc b/chromium/content/browser/aura/no_transport_image_transport_factory.cc
deleted file mode 100644
index 7f68e02edaa..00000000000
--- a/chromium/content/browser/aura/no_transport_image_transport_factory.cc
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/aura/no_transport_image_transport_factory.h"
-
-#include "cc/output/context_provider.h"
-#include "content/common/gpu/client/gl_helper.h"
-#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
-#include "ui/compositor/compositor.h"
-
-namespace content {
-
-namespace {
-
-class FakeTexture : public ui::Texture {
- public:
- FakeTexture(scoped_refptr<cc::ContextProvider> context_provider,
- float device_scale_factor)
- : ui::Texture(false, gfx::Size(), device_scale_factor),
- context_provider_(context_provider),
- texture_(context_provider_->Context3d()->createTexture()) {}
-
- virtual unsigned int PrepareTexture() OVERRIDE { return texture_; }
-
- virtual void Consume(const std::string& mailbox_name,
- const gfx::Size& new_size) OVERRIDE {
- size_ = new_size;
- }
-
- private:
- virtual ~FakeTexture() {
- context_provider_->Context3d()->deleteTexture(texture_);
- }
-
- scoped_refptr<cc::ContextProvider> context_provider_;
- unsigned texture_;
- DISALLOW_COPY_AND_ASSIGN(FakeTexture);
-};
-
-} // anonymous namespace
-
-NoTransportImageTransportFactory::NoTransportImageTransportFactory(
- scoped_ptr<ui::ContextFactory> context_factory)
- : context_factory_(context_factory.Pass()) {}
-
-NoTransportImageTransportFactory::~NoTransportImageTransportFactory() {}
-
-ui::ContextFactory* NoTransportImageTransportFactory::AsContextFactory() {
- return context_factory_.get();
-}
-
-gfx::GLSurfaceHandle
-NoTransportImageTransportFactory::CreateSharedSurfaceHandle() {
- return gfx::GLSurfaceHandle();
-}
-
-void NoTransportImageTransportFactory::DestroySharedSurfaceHandle(
- gfx::GLSurfaceHandle surface) {}
-
-scoped_refptr<ui::Texture>
-NoTransportImageTransportFactory::CreateTransportClient(
- float device_scale_factor) {
- return new FakeTexture(context_factory_->SharedMainThreadContextProvider(),
- device_scale_factor);
-}
-
-scoped_refptr<ui::Texture> NoTransportImageTransportFactory::CreateOwnedTexture(
- const gfx::Size& size,
- float device_scale_factor,
- unsigned int texture_id) {
- return NULL;
-}
-
-GLHelper* NoTransportImageTransportFactory::GetGLHelper() {
- if (!gl_helper_) {
- context_provider_ = context_factory_->SharedMainThreadContextProvider();
- gl_helper_.reset(new GLHelper(context_provider_->Context3d(),
- context_provider_->ContextSupport()));
- }
- return gl_helper_.get();
-}
-
-uint32 NoTransportImageTransportFactory::InsertSyncPoint() { return 0; }
-
-void NoTransportImageTransportFactory::WaitSyncPoint(uint32 sync_point) {}
-
-// We don't generate lost context events, so we don't need to keep track of
-// observers
-void NoTransportImageTransportFactory::AddObserver(
- ImageTransportFactoryObserver* observer) {}
-
-void NoTransportImageTransportFactory::RemoveObserver(
- ImageTransportFactoryObserver* observer) {}
-
-} // namespace content
diff --git a/chromium/content/browser/aura/owned_mailbox.h b/chromium/content/browser/aura/owned_mailbox.h
deleted file mode 100644
index ea1c242bf74..00000000000
--- a/chromium/content/browser/aura/owned_mailbox.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/memory/ref_counted.h"
-#include "content/browser/aura/image_transport_factory.h"
-#include "gpu/command_buffer/common/mailbox.h"
-
-namespace content {
-
-class GLHelper;
-
-// This class holds a texture id and gpu::Mailbox, and deletes the texture
-// id when the object itself is destroyed. Should only be created if a GLHelper
-// exists on the ImageTransportFactory.
-class OwnedMailbox : public base::RefCounted<OwnedMailbox>,
- public ImageTransportFactoryObserver {
- public:
- explicit OwnedMailbox(GLHelper* gl_helper);
-
- uint32 texture_id() const { return texture_id_; }
- uint32 sync_point() const { return sync_point_; }
- const gpu::Mailbox& mailbox() const { return mailbox_; }
-
- void UpdateSyncPoint(uint32 sync_point);
-
- protected:
- virtual ~OwnedMailbox();
-
- virtual void OnLostResources() OVERRIDE;
-
- private:
- friend class base::RefCounted<OwnedMailbox>;
-
- uint32 texture_id_;
- gpu::Mailbox mailbox_;
- uint32 sync_point_;
- GLHelper* gl_helper_;
-};
-
-} // namespace content
diff --git a/chromium/content/browser/aura/reflector_impl.cc b/chromium/content/browser/aura/reflector_impl.cc
deleted file mode 100644
index 08f7b0d11ff..00000000000
--- a/chromium/content/browser/aura/reflector_impl.cc
+++ /dev/null
@@ -1,182 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/aura/reflector_impl.h"
-
-#include "base/bind.h"
-#include "base/location.h"
-#include "content/browser/aura/browser_compositor_output_surface.h"
-#include "content/common/gpu/client/gl_helper.h"
-#include "ui/compositor/layer.h"
-
-namespace content {
-
-ReflectorImpl::ReflectorImpl(
- ui::Compositor* mirrored_compositor,
- ui::Layer* mirroring_layer,
- IDMap<BrowserCompositorOutputSurface>* output_surface_map,
- int surface_id)
- : texture_id_(0),
- texture_size_(mirrored_compositor->size()),
- output_surface_map_(output_surface_map),
- mirrored_compositor_(mirrored_compositor),
- mirroring_compositor_(mirroring_layer->GetCompositor()),
- mirroring_layer_(mirroring_layer),
- impl_message_loop_(ui::Compositor::GetCompositorMessageLoop()),
- main_message_loop_(base::MessageLoopProxy::current()),
- surface_id_(surface_id) {
- CreateSharedTexture();
- impl_message_loop_->PostTask(
- FROM_HERE,
- base::Bind(&ReflectorImpl::InitOnImplThread, this));
-}
-
-void ReflectorImpl::InitOnImplThread() {
- // Ignore if the reflector was shutdown before
- // initialized, or it's already initialized.
- if (!output_surface_map_ || gl_helper_.get())
- return;
-
- BrowserCompositorOutputSurface* source_surface =
- output_surface_map_->Lookup(surface_id_);
- // Skip if the source surface isn't ready yet. This will be
- // initiailze when the source surface becomes ready.
- if (!source_surface)
- return;
-
- AttachToOutputSurface(source_surface);
- gl_helper_->CopyTextureFullImage(texture_id_, texture_size_);
- // The shared texture doesn't have the data, so invokes full redraw
- // now.
- main_message_loop_->PostTask(
- FROM_HERE,
- base::Bind(&ReflectorImpl::FullRedrawContentOnMainThread,
- scoped_refptr<ReflectorImpl>(this)));
-}
-
-void ReflectorImpl::OnSourceSurfaceReady(int surface_id) {
- DCHECK_EQ(surface_id_, surface_id);
- InitOnImplThread();
-}
-
-void ReflectorImpl::Shutdown() {
- mirroring_compositor_ = NULL;
- mirroring_layer_ = NULL;
- shared_texture_ = NULL;
- impl_message_loop_->PostTask(
- FROM_HERE,
- base::Bind(&ReflectorImpl::ShutdownOnImplThread, this));
-}
-
-void ReflectorImpl::ShutdownOnImplThread() {
- BrowserCompositorOutputSurface* output_surface =
- output_surface_map_->Lookup(surface_id_);
- if (output_surface)
- output_surface->SetReflector(NULL);
- output_surface_map_ = NULL;
- gl_helper_.reset();
- // The instance must be deleted on main thread.
- main_message_loop_->PostTask(
- FROM_HERE,
- base::Bind(&ReflectorImpl::DeleteOnMainThread,
- scoped_refptr<ReflectorImpl>(this)));
-}
-
-// This must be called on ImplThread, or before the surface is passed to
-// ImplThread.
-void ReflectorImpl::AttachToOutputSurface(
- BrowserCompositorOutputSurface* output_surface) {
- gl_helper_.reset(
- new GLHelper(output_surface->context_provider()->Context3d(),
- output_surface->context_provider()->ContextSupport()));
- output_surface->SetReflector(this);
-}
-
-void ReflectorImpl::OnMirroringCompositorResized() {
- mirroring_compositor_->ScheduleFullRedraw();
-}
-
-void ReflectorImpl::OnLostResources() {
- shared_texture_ = NULL;
- mirroring_layer_->SetShowPaintedContent();
-}
-
-void ReflectorImpl::OnReshape(gfx::Size size) {
- if (texture_size_ == size)
- return;
- texture_size_ = size;
- DCHECK(texture_id_);
- gl_helper_->ResizeTexture(texture_id_, size);
- main_message_loop_->PostTask(
- FROM_HERE,
- base::Bind(&ReflectorImpl::UpdateTextureSizeOnMainThread,
- this->AsWeakPtr(),
- texture_size_));
-}
-
-void ReflectorImpl::OnSwapBuffers() {
- DCHECK(texture_id_);
- gl_helper_->CopyTextureFullImage(texture_id_, texture_size_);
- main_message_loop_->PostTask(
- FROM_HERE,
- base::Bind(&ReflectorImpl::FullRedrawOnMainThread,
- this->AsWeakPtr(),
- texture_size_));
-}
-
-void ReflectorImpl::OnPostSubBuffer(gfx::Rect rect) {
- DCHECK(texture_id_);
- gl_helper_->CopyTextureSubImage(texture_id_, rect);
- main_message_loop_->PostTask(
- FROM_HERE,
- base::Bind(&ReflectorImpl::UpdateSubBufferOnMainThread,
- this->AsWeakPtr(),
- texture_size_,
- rect));
-}
-
-void ReflectorImpl::CreateSharedTexture() {
- texture_id_ =
- ImageTransportFactory::GetInstance()->GetGLHelper()->CreateTexture();
- shared_texture_ =
- ImageTransportFactory::GetInstance()->CreateOwnedTexture(
- texture_size_, 1.0f, texture_id_);
- mirroring_layer_->SetExternalTexture(shared_texture_.get());
-}
-
-ReflectorImpl::~ReflectorImpl() {
- // Make sure the reflector is deleted on main thread.
- DCHECK_EQ(main_message_loop_.get(),
- base::MessageLoopProxy::current().get());
-}
-
-void ReflectorImpl::UpdateTextureSizeOnMainThread(gfx::Size size) {
- if (!mirroring_layer_)
- return;
- mirroring_layer_->SetBounds(gfx::Rect(size));
-}
-
-void ReflectorImpl::FullRedrawOnMainThread(gfx::Size size) {
- if (!mirroring_compositor_)
- return;
- UpdateTextureSizeOnMainThread(size);
- mirroring_compositor_->ScheduleFullRedraw();
-}
-
-void ReflectorImpl::UpdateSubBufferOnMainThread(gfx::Size size,
- gfx::Rect rect) {
- if (!mirroring_compositor_)
- return;
- UpdateTextureSizeOnMainThread(size);
- // Flip the coordinates to compositor's one.
- int y = size.height() - rect.y() - rect.height();
- gfx::Rect new_rect(rect.x(), y, rect.width(), rect.height());
- mirroring_layer_->SchedulePaint(new_rect);
-}
-
-void ReflectorImpl::FullRedrawContentOnMainThread() {
- mirrored_compositor_->ScheduleFullRedraw();
-}
-
-} // namespace content
diff --git a/chromium/content/browser/aura/software_output_device_ozone.cc b/chromium/content/browser/aura/software_output_device_ozone.cc
deleted file mode 100644
index 661ab50716d..00000000000
--- a/chromium/content/browser/aura/software_output_device_ozone.cc
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/aura/software_output_device_ozone.h"
-#include "third_party/skia/include/core/SkBitmapDevice.h"
-#include "third_party/skia/include/core/SkDevice.h"
-#include "ui/compositor/compositor.h"
-#include "ui/gfx/ozone/surface_factory_ozone.h"
-#include "ui/gfx/skia_util.h"
-#include "ui/gfx/vsync_provider.h"
-
-namespace content {
-
-SoftwareOutputDeviceOzone::SoftwareOutputDeviceOzone(ui::Compositor* compositor)
- : compositor_(compositor), realized_widget_(gfx::kNullAcceleratedWidget) {
- gfx::SurfaceFactoryOzone* factory = gfx::SurfaceFactoryOzone::GetInstance();
-
- if (factory->InitializeHardware() != gfx::SurfaceFactoryOzone::INITIALIZED)
- LOG(FATAL) << "Failed to initialize hardware in OZONE";
-
- realized_widget_ = factory->RealizeAcceleratedWidget(compositor_->widget());
-
- if (realized_widget_ == gfx::kNullAcceleratedWidget)
- LOG(FATAL) << "Failed to get a realized AcceleratedWidget";
-
- vsync_provider_.reset(factory->GetVSyncProvider(realized_widget_));
-}
-
-SoftwareOutputDeviceOzone::~SoftwareOutputDeviceOzone() {
-}
-
-void SoftwareOutputDeviceOzone::Resize(gfx::Size viewport_size) {
- if (viewport_size_ == viewport_size)
- return;
-
- viewport_size_ = viewport_size;
- gfx::Rect bounds(viewport_size_);
-
- gfx::SurfaceFactoryOzone* factory = gfx::SurfaceFactoryOzone::GetInstance();
- factory->AttemptToResizeAcceleratedWidget(compositor_->widget(),
- bounds);
-
- canvas_ = skia::SharePtr(factory->GetCanvasForWidget(realized_widget_));
- device_ = skia::SharePtr(canvas_->getDevice());
-}
-
-SkCanvas* SoftwareOutputDeviceOzone::BeginPaint(gfx::Rect damage_rect) {
- DCHECK(gfx::Rect(viewport_size_).Contains(damage_rect));
-
- canvas_->clipRect(gfx::RectToSkRect(damage_rect), SkRegion::kReplace_Op);
- // Save the current state so we can restore once we're done drawing. This is
- // saved after the clip since we want to keep the clip information after we're
- // done drawing such that OZONE knows what was updated.
- canvas_->save();
-
- return SoftwareOutputDevice::BeginPaint(damage_rect);
-}
-
-void SoftwareOutputDeviceOzone::EndPaint(cc::SoftwareFrameData* frame_data) {
- SoftwareOutputDevice::EndPaint(frame_data);
-
- canvas_->restore();
-
- if (damage_rect_.IsEmpty())
- return;
-
- bool scheduled = gfx::SurfaceFactoryOzone::GetInstance()->SchedulePageFlip(
- compositor_->widget());
- DCHECK(scheduled) << "Failed to schedule pageflip";
-}
-
-} // namespace content
diff --git a/chromium/content/browser/aura/software_output_device_x11.cc b/chromium/content/browser/aura/software_output_device_x11.cc
deleted file mode 100644
index abdb116ed34..00000000000
--- a/chromium/content/browser/aura/software_output_device_x11.cc
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/aura/software_output_device_x11.h"
-
-#include <X11/Xlib.h>
-#include <X11/Xutil.h>
-
-#include "content/public/browser/browser_thread.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "third_party/skia/include/core/SkDevice.h"
-#include "ui/compositor/compositor.h"
-
-namespace content {
-
-SoftwareOutputDeviceX11::SoftwareOutputDeviceX11(ui::Compositor* compositor)
- : compositor_(compositor),
- display_(gfx::GetXDisplay()),
- gc_(NULL),
- image_(NULL) {
- // TODO(skaslev) Remove this when crbug.com/180702 is fixed.
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- gc_ = XCreateGC(display_, compositor_->widget(), 0, NULL);
-}
-
-SoftwareOutputDeviceX11::~SoftwareOutputDeviceX11() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- XFreeGC(display_, gc_);
- ClearImage();
-}
-
-void SoftwareOutputDeviceX11::ClearImage() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- if (image_) {
- // XDestroyImage deletes the data referenced by the image which
- // is actually owned by the device_. So we have to reset data here.
- image_->data = NULL;
- XDestroyImage(image_);
- image_ = NULL;
- }
-}
-
-void SoftwareOutputDeviceX11::Resize(gfx::Size viewport_size) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- cc::SoftwareOutputDevice::Resize(viewport_size);
-
- ClearImage();
- if (!device_)
- return;
-
- const SkBitmap& bitmap = device_->accessBitmap(false);
- image_ = XCreateImage(display_, CopyFromParent,
- DefaultDepth(display_, DefaultScreen(display_)),
- ZPixmap, 0,
- static_cast<char*>(bitmap.getPixels()),
- viewport_size_.width(), viewport_size_.height(),
- 32, 4 * viewport_size_.width());
-}
-
-void SoftwareOutputDeviceX11::EndPaint(cc::SoftwareFrameData* frame_data) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DCHECK(device_);
- DCHECK(frame_data);
-
- if (!device_)
- return;
-
- SoftwareOutputDevice::EndPaint(frame_data);
-
- gfx::Rect rect = damage_rect_;
- rect.Intersect(gfx::Rect(viewport_size_));
- if (rect.IsEmpty())
- return;
-
- // TODO(skaslev): Maybe switch XShmPutImage since it's async.
- XPutImage(display_, compositor_->widget(), gc_, image_,
- rect.x(), rect.y(),
- rect.x(), rect.y(),
- rect.width(), rect.height());
-}
-
-} // namespace content
diff --git a/chromium/content/browser/battery_status/OWNERS b/chromium/content/browser/battery_status/OWNERS
new file mode 100644
index 00000000000..1fd89e0e2eb
--- /dev/null
+++ b/chromium/content/browser/battery_status/OWNERS
@@ -0,0 +1 @@
+timvolodine@chromium.org
diff --git a/chromium/content/browser/battery_status/battery_status_browsertest.cc b/chromium/content/browser/battery_status/battery_status_browsertest.cc
new file mode 100644
index 00000000000..76878315054
--- /dev/null
+++ b/chromium/content/browser/battery_status/battery_status_browsertest.cc
@@ -0,0 +1,167 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/command_line.h"
+#include "base/synchronization/waitable_event.h"
+#include "content/browser/battery_status/battery_status_manager.h"
+#include "content/browser/battery_status/battery_status_service.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/content_browser_test.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "content/public/test/test_utils.h"
+#include "content/shell/browser/shell.h"
+
+namespace content {
+
+namespace {
+
+class FakeBatteryManager : public BatteryStatusManager {
+ public:
+ explicit FakeBatteryManager(
+ const BatteryStatusService::BatteryUpdateCallback& callback)
+ : battery_status_available_(true),
+ started_(false) {
+ callback_ = callback;
+ }
+ virtual ~FakeBatteryManager() { }
+
+ // Methods from BatteryStatusManager.
+ virtual bool StartListeningBatteryChange() OVERRIDE {
+ started_ = true;
+ if (battery_status_available_)
+ InvokeUpdateCallback();
+ return battery_status_available_;
+ }
+
+ virtual void StopListeningBatteryChange() OVERRIDE { }
+
+ void InvokeUpdateCallback() {
+ callback_.Run(status_);
+ }
+
+ void set_battery_status(const blink::WebBatteryStatus& status) {
+ status_ = status;
+ }
+
+ void set_battery_status_available(bool value) {
+ battery_status_available_ = value;
+ }
+
+ bool started() {
+ return started_;
+ }
+
+ private:
+ bool battery_status_available_;
+ bool started_;
+ blink::WebBatteryStatus status_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeBatteryManager);
+};
+
+class BatteryStatusBrowserTest : public ContentBrowserTest {
+ public:
+ BatteryStatusBrowserTest()
+ : battery_manager_(0),
+ battery_service_(0),
+ io_loop_finished_event_(false, false) {
+ }
+
+ virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
+ command_line->AppendSwitch(
+ switches::kEnableExperimentalWebPlatformFeatures);
+ }
+
+ virtual void SetUpOnMainThread() OVERRIDE {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&BatteryStatusBrowserTest::SetUpOnIOThread, this));
+ io_loop_finished_event_.Wait();
+ }
+
+ void SetUpOnIOThread() {
+ battery_service_ = BatteryStatusService::GetInstance();
+ battery_manager_ = new FakeBatteryManager(
+ battery_service_->GetUpdateCallbackForTesting());
+ battery_service_->SetBatteryManagerForTesting(battery_manager_);
+ io_loop_finished_event_.Signal();
+ }
+
+ virtual void TearDown() OVERRIDE {
+ battery_service_->SetBatteryManagerForTesting(0);
+ }
+
+ FakeBatteryManager* battery_manager() {
+ return battery_manager_;
+ }
+
+ private:
+ FakeBatteryManager* battery_manager_;
+ BatteryStatusService* battery_service_;
+ base::WaitableEvent io_loop_finished_event_;
+
+ DISALLOW_COPY_AND_ASSIGN(BatteryStatusBrowserTest);
+};
+
+IN_PROC_BROWSER_TEST_F(BatteryStatusBrowserTest, BatteryManagerDefaultValues) {
+ // Set the fake battery manager to return false on start. From JavaScript
+ // request a promise for the battery status information and once it resolves
+ // check the default values and navigate to #pass.
+ battery_manager()->set_battery_status_available(false);
+ GURL test_url = GetTestUrl(
+ "battery_status", "battery_status_default_test.html");
+ NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 2);
+ EXPECT_EQ("pass", shell()->web_contents()->GetLastCommittedURL().ref());
+ EXPECT_TRUE(battery_manager()->started());
+}
+
+IN_PROC_BROWSER_TEST_F(BatteryStatusBrowserTest, BatteryManagerResolvePromise) {
+ // Set the fake battery manager to return predefined battery status values.
+ // From JavaScript request a promise for the battery status information and
+ // once it resolves check the values and navigate to #pass.
+ blink::WebBatteryStatus status;
+ status.charging = true;
+ status.chargingTime = 100;
+ status.dischargingTime = std::numeric_limits<double>::infinity();
+ status.level = 0.5;
+ battery_manager()->set_battery_status(status);
+
+ GURL test_url = GetTestUrl(
+ "battery_status", "battery_status_promise_resolution_test.html");
+ NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 2);
+ EXPECT_EQ("pass", shell()->web_contents()->GetLastCommittedURL().ref());
+ EXPECT_TRUE(battery_manager()->started());
+}
+
+IN_PROC_BROWSER_TEST_F(BatteryStatusBrowserTest,
+ BatteryManagerWithEventListener) {
+ // Set the fake battery manager to return default battery status values.
+ // From JavaScript request a promise for the battery status information.
+ // Once it resolves add an event listener for battery level change. Set
+ // battery level to 0.6 and invoke update. Check that the event listener
+ // is invoked with the correct value for level and navigate to #pass.
+ blink::WebBatteryStatus status;
+ battery_manager()->set_battery_status(status);
+
+ TestNavigationObserver same_tab_observer(shell()->web_contents(), 2);
+ GURL test_url = GetTestUrl(
+ "battery_status", "battery_status_event_listener_test.html");
+ shell()->LoadURL(test_url);
+ same_tab_observer.Wait();
+ EXPECT_EQ("resolved", shell()->web_contents()->GetLastCommittedURL().ref());
+
+ TestNavigationObserver same_tab_observer2(shell()->web_contents(), 1);
+ status.level = 0.6;
+ battery_manager()->set_battery_status(status);
+ battery_manager()->InvokeUpdateCallback();
+ same_tab_observer2.Wait();
+ EXPECT_EQ("pass", shell()->web_contents()->GetLastCommittedURL().ref());
+ EXPECT_TRUE(battery_manager()->started());
+}
+
+} // namespace
+
+} // namespace content
diff --git a/chromium/content/browser/battery_status/battery_status_manager.h b/chromium/content/browser/battery_status/battery_status_manager.h
new file mode 100644
index 00000000000..20e71edfbb1
--- /dev/null
+++ b/chromium/content/browser/battery_status/battery_status_manager.h
@@ -0,0 +1,57 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_BATTERY_STATUS_BATTERY_STATUS_MANAGER_H_
+#define CHROME_BROWSER_BATTERY_STATUS_BATTERY_STATUS_MANAGER_H_
+
+#include "build/build_config.h"
+
+#if defined(OS_ANDROID)
+#include "base/android/scoped_java_ref.h"
+#endif
+
+#include "content/browser/battery_status/battery_status_service.h"
+
+namespace content {
+
+// Platform specific manager class for fetching battery status data.
+class CONTENT_EXPORT BatteryStatusManager {
+ public:
+ explicit BatteryStatusManager(
+ const BatteryStatusService::BatteryUpdateCallback& callback);
+ virtual ~BatteryStatusManager();
+
+ // Start listening for battery status changes. New updates are signalled
+ // by invoking the callback provided at construction time.
+ virtual bool StartListeningBatteryChange();
+
+ // Stop listening for battery status changes.
+ virtual void StopListeningBatteryChange();
+
+#if defined(OS_ANDROID)
+ // Must be called at startup.
+ static bool Register(JNIEnv* env);
+
+ // Called from Java via JNI.
+ void GotBatteryStatus(JNIEnv*, jobject, jboolean charging,
+ jdouble charging_time, jdouble discharging_time,
+ jdouble level);
+#endif
+
+ protected:
+ BatteryStatusManager();
+ BatteryStatusService::BatteryUpdateCallback callback_;
+
+ private:
+#if defined(OS_ANDROID)
+ // Java provider of battery status info.
+ base::android::ScopedJavaGlobalRef<jobject> j_manager_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(BatteryStatusManager);
+};
+
+} // namespace content
+
+#endif // CHROME_BROWSER_BATTERY_STATUS_BATTERY_STATUS_MANAGER_H_
diff --git a/chromium/content/browser/battery_status/battery_status_manager_android.cc b/chromium/content/browser/battery_status/battery_status_manager_android.cc
new file mode 100644
index 00000000000..a8438b85572
--- /dev/null
+++ b/chromium/content/browser/battery_status/battery_status_manager_android.cc
@@ -0,0 +1,56 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/battery_status/battery_status_manager.h"
+
+#include "base/android/jni_android.h"
+#include "base/metrics/histogram.h"
+#include "jni/BatteryStatusManager_jni.h"
+
+using base::android::AttachCurrentThread;
+
+namespace content {
+
+BatteryStatusManager::BatteryStatusManager(
+ const BatteryStatusService::BatteryUpdateCallback& callback)
+ : callback_(callback) {
+ j_manager_.Reset(
+ Java_BatteryStatusManager_getInstance(
+ AttachCurrentThread(), base::android::GetApplicationContext()));
+}
+
+BatteryStatusManager::BatteryStatusManager() {
+}
+
+BatteryStatusManager::~BatteryStatusManager() {
+}
+
+bool BatteryStatusManager::Register(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+void BatteryStatusManager::GotBatteryStatus(JNIEnv*, jobject,
+ jboolean charging, jdouble charging_time, jdouble discharging_time,
+ jdouble level) {
+ blink::WebBatteryStatus status;
+ status.charging = charging;
+ status.chargingTime = charging_time;
+ status.dischargingTime = discharging_time;
+ status.level = level;
+ callback_.Run(status);
+}
+
+bool BatteryStatusManager::StartListeningBatteryChange() {
+ bool result = Java_BatteryStatusManager_start(AttachCurrentThread(),
+ j_manager_.obj(), reinterpret_cast<intptr_t>(this));
+ UMA_HISTOGRAM_BOOLEAN("BatteryStatus.StartAndroid", result);
+ return result;
+}
+
+void BatteryStatusManager::StopListeningBatteryChange() {
+ Java_BatteryStatusManager_stop(
+ AttachCurrentThread(), j_manager_.obj());
+}
+
+} // namespace content
diff --git a/chromium/content/browser/battery_status/battery_status_manager_default.cc b/chromium/content/browser/battery_status/battery_status_manager_default.cc
new file mode 100644
index 00000000000..a8d066bdefd
--- /dev/null
+++ b/chromium/content/browser/battery_status/battery_status_manager_default.cc
@@ -0,0 +1,31 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/battery_status/battery_status_manager.h"
+
+#include "base/logging.h"
+
+namespace content {
+
+BatteryStatusManager::BatteryStatusManager(
+ const BatteryStatusService::BatteryUpdateCallback& callback)
+ : callback_(callback) {
+}
+
+BatteryStatusManager::BatteryStatusManager() {
+}
+
+BatteryStatusManager::~BatteryStatusManager() {
+}
+
+bool BatteryStatusManager::StartListeningBatteryChange() {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+void BatteryStatusManager::StopListeningBatteryChange() {
+ NOTIMPLEMENTED();
+}
+
+} // namespace content
diff --git a/chromium/content/browser/battery_status/battery_status_message_filter.cc b/chromium/content/browser/battery_status/battery_status_message_filter.cc
new file mode 100644
index 00000000000..19a21b80787
--- /dev/null
+++ b/chromium/content/browser/battery_status/battery_status_message_filter.cc
@@ -0,0 +1,56 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/battery_status/battery_status_message_filter.h"
+
+#include "content/common/battery_status_messages.h"
+
+namespace content {
+
+BatteryStatusMessageFilter::BatteryStatusMessageFilter()
+ : BrowserMessageFilter(BatteryStatusMsgStart),
+ is_started_(false) {
+ callback_ = base::Bind(&BatteryStatusMessageFilter::SendBatteryChange,
+ base::Unretained(this));
+}
+
+BatteryStatusMessageFilter::~BatteryStatusMessageFilter() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ if (is_started_)
+ subscription_.reset();
+}
+
+bool BatteryStatusMessageFilter::OnMessageReceived(
+ const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(BatteryStatusMessageFilter, message)
+ IPC_MESSAGE_HANDLER(BatteryStatusHostMsg_Start, OnBatteryStatusStart)
+ IPC_MESSAGE_HANDLER(BatteryStatusHostMsg_Stop, OnBatteryStatusStop)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void BatteryStatusMessageFilter::OnBatteryStatusStart() {
+ DCHECK(!is_started_);
+ if (is_started_)
+ return;
+ is_started_ = true;
+ subscription_ = BatteryStatusService::GetInstance()->AddCallback(callback_);
+}
+
+void BatteryStatusMessageFilter::OnBatteryStatusStop() {
+ DCHECK(is_started_);
+ if (!is_started_)
+ return;
+ is_started_ = false;
+ subscription_.reset();
+}
+
+void BatteryStatusMessageFilter::SendBatteryChange(
+ const blink::WebBatteryStatus& status) {
+ Send(new BatteryStatusMsg_DidChange(status));
+}
+
+} // namespace content
diff --git a/chromium/content/browser/battery_status/battery_status_message_filter.h b/chromium/content/browser/battery_status/battery_status_message_filter.h
new file mode 100644
index 00000000000..9484114c576
--- /dev/null
+++ b/chromium/content/browser/battery_status/battery_status_message_filter.h
@@ -0,0 +1,36 @@
+// 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_BATTERY_STATUS_BATTERY_STATUS_MESSAGE_FILTER_H_
+#define CONTENT_BROWSER_BATTERY_STATUS_BATTERY_STATUS_MESSAGE_FILTER_H_
+
+#include "content/browser/battery_status/battery_status_service.h"
+#include "content/public/browser/browser_message_filter.h"
+
+namespace content {
+
+class BatteryStatusMessageFilter : public BrowserMessageFilter {
+ public:
+ BatteryStatusMessageFilter();
+
+ // BrowserMessageFilter implementation.
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+
+ private:
+ virtual ~BatteryStatusMessageFilter();
+
+ void OnBatteryStatusStart();
+ void OnBatteryStatusStop();
+ void SendBatteryChange(const blink::WebBatteryStatus& status);
+
+ BatteryStatusService::BatteryUpdateCallback callback_;
+ scoped_ptr<BatteryStatusService::BatteryUpdateSubscription> subscription_;
+ bool is_started_;
+
+ DISALLOW_COPY_AND_ASSIGN(BatteryStatusMessageFilter);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_BATTERY_STATUS_BATTERY_STATUS_MESSAGE_FILTER_H_
diff --git a/chromium/content/browser/battery_status/battery_status_service.cc b/chromium/content/browser/battery_status/battery_status_service.cc
new file mode 100644
index 00000000000..21d59d3780d
--- /dev/null
+++ b/chromium/content/browser/battery_status/battery_status_service.cc
@@ -0,0 +1,106 @@
+// 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/battery_status/battery_status_service.h"
+
+#include "base/bind.h"
+#include "content/browser/battery_status/battery_status_manager.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace content {
+
+BatteryStatusService::BatteryStatusService()
+ : update_callback_(base::Bind(&BatteryStatusService::UpdateBatteryStatus,
+ base::Unretained(this))),
+ status_updated_(false),
+ is_shutdown_(false) {
+ callback_list_.set_removal_callback(
+ base::Bind(&BatteryStatusService::ConsumersChanged,
+ base::Unretained(this)));
+}
+
+BatteryStatusService::~BatteryStatusService() {
+}
+
+BatteryStatusService* BatteryStatusService::GetInstance() {
+ return Singleton<BatteryStatusService,
+ LeakySingletonTraits<BatteryStatusService> >::get();
+}
+
+scoped_ptr<BatteryStatusService::BatteryUpdateSubscription>
+BatteryStatusService::AddCallback(const BatteryUpdateCallback& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK(!is_shutdown_);
+
+ if (!battery_fetcher_)
+ battery_fetcher_.reset(new BatteryStatusManager(update_callback_));
+
+ if (callback_list_.empty()) {
+ bool success = battery_fetcher_->StartListeningBatteryChange();
+ if (!success) {
+ // Make sure the promise resolves with the default values in Blink.
+ callback.Run(blink::WebBatteryStatus());
+ }
+ }
+
+ if (status_updated_) {
+ // Send recent status to the new callback if already available.
+ callback.Run(status_);
+ }
+
+ return callback_list_.Add(callback);
+}
+
+void BatteryStatusService::ConsumersChanged() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK(!is_shutdown_);
+
+ if (callback_list_.empty()) {
+ battery_fetcher_->StopListeningBatteryChange();
+ status_updated_ = false;
+ }
+}
+
+void BatteryStatusService::UpdateBatteryStatus(
+ const blink::WebBatteryStatus& status) {
+ DCHECK(!is_shutdown_);
+ BrowserThread::PostTask(BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&BatteryStatusService::NotifyConsumers,
+ base::Unretained(this), status));
+}
+
+void BatteryStatusService::NotifyConsumers(
+ const blink::WebBatteryStatus& status) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ if (callback_list_.empty())
+ return;
+
+ status_ = status;
+ status_updated_ = true;
+ callback_list_.Notify(status);
+}
+
+void BatteryStatusService::Shutdown() {
+ if (!callback_list_.empty())
+ battery_fetcher_->StopListeningBatteryChange();
+ battery_fetcher_.reset();
+ is_shutdown_ = true;
+}
+
+const BatteryStatusService::BatteryUpdateCallback&
+BatteryStatusService::GetUpdateCallbackForTesting() const {
+ return update_callback_;
+}
+
+void BatteryStatusService::SetBatteryManagerForTesting(
+ BatteryStatusManager* test_battery_manager) {
+ battery_fetcher_.reset(test_battery_manager);
+ blink::WebBatteryStatus status;
+ status_ = status;
+ status_updated_ = false;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/battery_status/battery_status_service.h b/chromium/content/browser/battery_status/battery_status_service.h
new file mode 100644
index 00000000000..1c1e3648363
--- /dev/null
+++ b/chromium/content/browser/battery_status/battery_status_service.h
@@ -0,0 +1,66 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_BATTERY_STATUS_BATTERY_STATUS_SERVICE_H_
+#define CONTENT_BROWSER_BATTERY_STATUS_BATTERY_STATUS_SERVICE_H_
+
+#include "base/callback_list.h"
+#include "base/memory/singleton.h"
+#include "content/common/content_export.h"
+#include "third_party/WebKit/public/platform/WebBatteryStatus.h"
+
+namespace content {
+class BatteryStatusManager;
+
+class CONTENT_EXPORT BatteryStatusService {
+ public:
+ typedef base::Callback<void(const blink::WebBatteryStatus&)>
+ BatteryUpdateCallback;
+ typedef base::CallbackList<void(const blink::WebBatteryStatus&)>
+ BatteryUpdateCallbackList;
+ typedef BatteryUpdateCallbackList::Subscription BatteryUpdateSubscription;
+
+ // Returns the BatteryStatusService singleton.
+ static BatteryStatusService* GetInstance();
+
+ // Adds a callback to receive battery status updates.
+ // Must be called on the I/O thread.
+ scoped_ptr<BatteryUpdateSubscription> AddCallback(
+ const BatteryUpdateCallback& callback);
+
+ // Gracefully clean-up.
+ void Shutdown();
+
+ // Injects a custom battery status manager for testing purposes.
+ // This class takes ownership of the injected object.
+ void SetBatteryManagerForTesting(BatteryStatusManager* test_battery_manager);
+
+ // Returns callback to invoke when battery is changed. Used for testing.
+ const BatteryUpdateCallback& GetUpdateCallbackForTesting() const;
+
+ private:
+ friend struct DefaultSingletonTraits<BatteryStatusService>;
+
+ BatteryStatusService();
+ virtual ~BatteryStatusService();
+
+ // Updates current battery status and sends new status to interested
+ // render processes. Can be called on any thread via a callback.
+ void UpdateBatteryStatus(const blink::WebBatteryStatus& status);
+ void NotifyConsumers(const blink::WebBatteryStatus& status);
+ void ConsumersChanged();
+
+ scoped_ptr<BatteryStatusManager> battery_fetcher_;
+ BatteryUpdateCallbackList callback_list_;
+ BatteryUpdateCallback update_callback_;
+ blink::WebBatteryStatus status_;
+ bool status_updated_;
+ bool is_shutdown_;
+
+ DISALLOW_COPY_AND_ASSIGN(BatteryStatusService);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_BATTERY_STATUS_BATTERY_STATUS_SERVICE_H_
diff --git a/chromium/content/browser/battery_status/battery_status_service_unittest.cc b/chromium/content/browser/battery_status/battery_status_service_unittest.cc
new file mode 100644
index 00000000000..f92c8766fc8
--- /dev/null
+++ b/chromium/content/browser/battery_status/battery_status_service_unittest.cc
@@ -0,0 +1,193 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/battery_status/battery_status_service.h"
+
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "content/browser/battery_status/battery_status_manager.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+
+namespace content {
+
+namespace {
+
+class FakeBatteryManager : public BatteryStatusManager {
+ public:
+ explicit FakeBatteryManager(
+ const BatteryStatusService::BatteryUpdateCallback& callback)
+ : start_invoked_count_(0),
+ stop_invoked_count_(0) {
+ callback_ = callback;
+ }
+ virtual ~FakeBatteryManager() { }
+
+ // Methods from Battery Status Manager
+ virtual bool StartListeningBatteryChange() OVERRIDE {
+ start_invoked_count_++;
+ return true;
+ }
+
+ virtual void StopListeningBatteryChange() OVERRIDE {
+ stop_invoked_count_++;
+ }
+
+ void InvokeUpdateCallback(const blink::WebBatteryStatus& status) {
+ callback_.Run(status);
+ }
+
+ int start_invoked_count() const { return start_invoked_count_; }
+ int stop_invoked_count() const { return stop_invoked_count_; }
+
+ private:
+ int start_invoked_count_;
+ int stop_invoked_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeBatteryManager);
+};
+
+class BatteryStatusServiceTest : public testing::Test {
+ public:
+ BatteryStatusServiceTest()
+ : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
+ battery_service_(0),
+ battery_manager_(0),
+ callback1_invoked_count_(0),
+ callback2_invoked_count_(0) {
+ }
+ virtual ~BatteryStatusServiceTest() { }
+
+ protected:
+ typedef BatteryStatusService::BatteryUpdateSubscription BatterySubscription;
+
+ virtual void SetUp() OVERRIDE {
+ callback1_ = base::Bind(&BatteryStatusServiceTest::Callback1,
+ base::Unretained(this));
+ callback2_ = base::Bind(&BatteryStatusServiceTest::Callback2,
+ base::Unretained(this));
+ battery_service_ = BatteryStatusService::GetInstance();
+ battery_manager_ = new FakeBatteryManager(
+ battery_service_->GetUpdateCallbackForTesting());
+ battery_service_->SetBatteryManagerForTesting(battery_manager_);
+ }
+
+ virtual void TearDown() OVERRIDE {
+ base::RunLoop().RunUntilIdle();
+ battery_service_->SetBatteryManagerForTesting(0);
+ }
+
+ FakeBatteryManager* battery_manager() {
+ return battery_manager_;
+ }
+
+ scoped_ptr<BatterySubscription> AddCallback(
+ const BatteryStatusService::BatteryUpdateCallback& callback) {
+ return battery_service_->AddCallback(callback);
+ }
+
+ int callback1_invoked_count() const {
+ return callback1_invoked_count_;
+ }
+
+ int callback2_invoked_count() const {
+ return callback2_invoked_count_;
+ }
+
+ const blink::WebBatteryStatus& battery_status() const {
+ return battery_status_;
+ }
+
+ const BatteryStatusService::BatteryUpdateCallback& callback1() const {
+ return callback1_;
+ }
+
+ const BatteryStatusService::BatteryUpdateCallback& callback2() const {
+ return callback2_;
+ }
+
+ private:
+ void Callback1(const blink::WebBatteryStatus& status) {
+ callback1_invoked_count_++;
+ battery_status_ = status;
+ }
+
+ void Callback2(const blink::WebBatteryStatus& status) {
+ callback2_invoked_count_++;
+ battery_status_ = status;
+ }
+
+ content::TestBrowserThreadBundle thread_bundle_;
+ BatteryStatusService* battery_service_;
+ FakeBatteryManager* battery_manager_;
+ BatteryStatusService::BatteryUpdateCallback callback1_;
+ BatteryStatusService::BatteryUpdateCallback callback2_;
+ int callback1_invoked_count_;
+ int callback2_invoked_count_;
+ blink::WebBatteryStatus battery_status_;
+
+ DISALLOW_COPY_AND_ASSIGN(BatteryStatusServiceTest);
+};
+
+TEST_F(BatteryStatusServiceTest, AddFirstCallback) {
+ scoped_ptr<BatterySubscription> subscription1 = AddCallback(callback1());
+ EXPECT_EQ(1, battery_manager()->start_invoked_count());
+ EXPECT_EQ(0, battery_manager()->stop_invoked_count());
+ subscription1.reset();
+ EXPECT_EQ(1, battery_manager()->start_invoked_count());
+ EXPECT_EQ(1, battery_manager()->stop_invoked_count());
+}
+
+TEST_F(BatteryStatusServiceTest, AddCallbackAfterUpdate) {
+ scoped_ptr<BatterySubscription> subscription1 = AddCallback(callback1());
+ blink::WebBatteryStatus status;
+ battery_manager()->InvokeUpdateCallback(status);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(1, callback1_invoked_count());
+ EXPECT_EQ(0, callback2_invoked_count());
+
+ scoped_ptr<BatterySubscription> subscription2 = AddCallback(callback2());
+ EXPECT_EQ(1, callback1_invoked_count());
+ EXPECT_EQ(1, callback2_invoked_count());
+}
+
+TEST_F(BatteryStatusServiceTest, TwoCallbacksUpdate) {
+ scoped_ptr<BatterySubscription> subscription1 = AddCallback(callback1());
+ scoped_ptr<BatterySubscription> subscription2 = AddCallback(callback2());
+
+ blink::WebBatteryStatus status;
+ status.charging = true;
+ status.chargingTime = 100;
+ status.dischargingTime = 200;
+ status.level = 0.5;
+ battery_manager()->InvokeUpdateCallback(status);
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(1, callback1_invoked_count());
+ EXPECT_EQ(1, callback2_invoked_count());
+ EXPECT_EQ(status.charging, battery_status().charging);
+ EXPECT_EQ(status.chargingTime, battery_status().chargingTime);
+ EXPECT_EQ(status.dischargingTime, battery_status().dischargingTime);
+ EXPECT_EQ(status.level, battery_status().level);
+}
+
+TEST_F(BatteryStatusServiceTest, RemoveOneCallback) {
+ scoped_ptr<BatterySubscription> subscription1 = AddCallback(callback1());
+ scoped_ptr<BatterySubscription> subscription2 = AddCallback(callback2());
+
+ blink::WebBatteryStatus status;
+ battery_manager()->InvokeUpdateCallback(status);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(1, callback1_invoked_count());
+ EXPECT_EQ(1, callback2_invoked_count());
+
+ subscription1.reset();
+ battery_manager()->InvokeUpdateCallback(status);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(1, callback1_invoked_count());
+ EXPECT_EQ(2, callback2_invoked_count());
+}
+
+} // namespace
+
+} // namespace content
diff --git a/chromium/content/browser/bookmarklet_browsertest.cc b/chromium/content/browser/bookmarklet_browsertest.cc
index 35523ee63dc..5ec10d505a5 100644
--- a/chromium/content/browser/bookmarklet_browsertest.cc
+++ b/chromium/content/browser/bookmarklet_browsertest.cc
@@ -5,10 +5,10 @@
#include "base/strings/string_util.h"
#include "content/public/browser/web_contents.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/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
@@ -70,6 +70,4 @@ IN_PROC_BROWSER_TEST_F(BookmarkletTest, DocumentWrite) {
EXPECT_EQ("hello world", GetBodyText());
}
-
} // namespace content
-
diff --git a/chromium/content/browser/bootstrap_sandbox_mac.cc b/chromium/content/browser/bootstrap_sandbox_mac.cc
new file mode 100644
index 00000000000..040a23783ea
--- /dev/null
+++ b/chromium/content/browser/bootstrap_sandbox_mac.cc
@@ -0,0 +1,149 @@
+// 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/bootstrap_sandbox_mac.h"
+
+#include "base/logging.h"
+#include "base/mac/mac_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/singleton.h"
+#include "content/browser/mach_broker_mac.h"
+#include "content/common/sandbox_init_mac.h"
+#include "content/public/browser/browser_child_process_observer.h"
+#include "content/public/browser/child_process_data.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/common/sandbox_type_mac.h"
+#include "sandbox/mac/bootstrap_sandbox.h"
+
+namespace content {
+
+namespace {
+
+// This class is responsible for creating the BootstrapSandbox global
+// singleton, as well as registering all associated policies with it.
+class BootstrapSandboxPolicy : public BrowserChildProcessObserver,
+ public NotificationObserver {
+ public:
+ static BootstrapSandboxPolicy* GetInstance();
+
+ sandbox::BootstrapSandbox* sandbox() const {
+ return sandbox_.get();
+ }
+
+ // BrowserChildProcessObserver:
+ virtual void BrowserChildProcessHostDisconnected(
+ const ChildProcessData& data) OVERRIDE;
+ virtual void BrowserChildProcessCrashed(
+ const ChildProcessData& data) OVERRIDE;
+
+ // NotificationObserver:
+ virtual void Observe(int type,
+ const NotificationSource& source,
+ const NotificationDetails& details) OVERRIDE;
+
+ private:
+ friend struct DefaultSingletonTraits<BootstrapSandboxPolicy>;
+ BootstrapSandboxPolicy();
+ virtual ~BootstrapSandboxPolicy();
+
+ void RegisterSandboxPolicies();
+ void RegisterRendererPolicy();
+
+ void AddBaselinePolicy(sandbox::BootstrapSandboxPolicy* policy);
+
+ NotificationRegistrar notification_registrar_;
+
+ scoped_ptr<sandbox::BootstrapSandbox> sandbox_;
+};
+
+BootstrapSandboxPolicy* BootstrapSandboxPolicy::GetInstance() {
+ return Singleton<BootstrapSandboxPolicy>::get();
+}
+
+void BootstrapSandboxPolicy::BrowserChildProcessHostDisconnected(
+ const ChildProcessData& data) {
+ sandbox()->ChildDied(data.handle);
+}
+
+void BootstrapSandboxPolicy::BrowserChildProcessCrashed(
+ const ChildProcessData& data) {
+ sandbox()->ChildDied(data.handle);
+}
+
+void BootstrapSandboxPolicy::Observe(int type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ switch (type) {
+ case NOTIFICATION_RENDERER_PROCESS_CLOSED:
+ sandbox()->ChildDied(
+ Details<RenderProcessHost::RendererClosedDetails>(details)->handle);
+ break;
+ default:
+ NOTREACHED() << "Unexpected notification " << type;
+ break;
+ }
+}
+
+BootstrapSandboxPolicy::BootstrapSandboxPolicy()
+ : sandbox_(sandbox::BootstrapSandbox::Create()) {
+ CHECK(sandbox_.get());
+ BrowserChildProcessObserver::Add(this);
+ notification_registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_CLOSED,
+ NotificationService::AllBrowserContextsAndSources());
+ RegisterSandboxPolicies();
+}
+
+BootstrapSandboxPolicy::~BootstrapSandboxPolicy() {
+ BrowserChildProcessObserver::Remove(this);
+}
+
+void BootstrapSandboxPolicy::RegisterSandboxPolicies() {
+ RegisterRendererPolicy();
+}
+
+void BootstrapSandboxPolicy::RegisterRendererPolicy() {
+ sandbox::BootstrapSandboxPolicy policy;
+ AddBaselinePolicy(&policy);
+
+ // Permit font queries.
+ policy.rules["com.apple.FontServer"] = sandbox::Rule(sandbox::POLICY_ALLOW);
+ policy.rules["com.apple.FontObjectsServer"] =
+ sandbox::Rule(sandbox::POLICY_ALLOW);
+
+ // Allow access to the windowserver. This is needed to get the colorspace
+ // during sandbox warmup. Since NSColorSpace conforms to NSCoding, this
+ // should be plumbed over IPC instead <http://crbug.com/265709>.
+ policy.rules["com.apple.windowserver.active"] =
+ sandbox::Rule(sandbox::POLICY_ALLOW);
+
+ sandbox_->RegisterSandboxPolicy(SANDBOX_TYPE_RENDERER, policy);
+}
+
+void BootstrapSandboxPolicy::AddBaselinePolicy(
+ sandbox::BootstrapSandboxPolicy* policy) {
+ auto& rules = policy->rules;
+
+ // Allow the child to send its task port to the MachBroker.
+ rules[MachBroker::GetMachPortName()] = sandbox::Rule(sandbox::POLICY_ALLOW);
+
+ // Allow logging to the syslog.
+ rules["com.apple.system.logger"] = sandbox::Rule(sandbox::POLICY_ALLOW);
+}
+
+} // namespace
+
+bool ShouldEnableBootstrapSandbox() {
+ return false;
+}
+
+sandbox::BootstrapSandbox* GetBootstrapSandbox() {
+ return BootstrapSandboxPolicy::GetInstance()->sandbox();
+}
+
+} // namespace content
diff --git a/chromium/content/browser/bootstrap_sandbox_mac.h b/chromium/content/browser/bootstrap_sandbox_mac.h
new file mode 100644
index 00000000000..ffb6a6160c1
--- /dev/null
+++ b/chromium/content/browser/bootstrap_sandbox_mac.h
@@ -0,0 +1,25 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_BOOTSTRAP_SANDBOX_MAC_H_
+#define CONTENT_BROWSER_BOOTSTRAP_SANDBOX_MAC_H_
+
+namespace sandbox {
+class BootstrapSandbox;
+}
+
+namespace content {
+
+// Whether or not the bootstrap sandbox should be enabled.
+bool ShouldEnableBootstrapSandbox();
+
+// Returns the singleton instance of the BootstrapSandox. The returned object
+// is thread-safe.
+// On the first call to this function, the sandbox will be created and all
+// the policies will be registered with it.
+sandbox::BootstrapSandbox* GetBootstrapSandbox();
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_BOOTSTRAP_SANDBOX_MAC_H_
diff --git a/chromium/content/browser/browser.gni b/chromium/content/browser/browser.gni
new file mode 100644
index 00000000000..b7e8d7f1d52
--- /dev/null
+++ b/chromium/content/browser/browser.gni
@@ -0,0 +1,15 @@
+# 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.
+
+# This file defines the content browser gypi values. This file is read once and
+# cached, which is a performance optimization that allows us to share the
+# results of parsing the .gypi file between the public and private BUILD.gn
+# files. It also saves us from duplicating this exec_script call.
+content_browser_gypi_values = exec_script(
+ "//build/gypi_to_gn.py",
+ [ rebase_path("../content_browser.gypi"),
+ "--replace=<(SHARED_INTERMEDIATE_DIR)=$root_gen_dir" ],
+ "scope",
+ [ "../content_browser.gypi" ])
+
diff --git a/chromium/content/browser/browser_child_process_host_impl.cc b/chromium/content/browser/browser_child_process_host_impl.cc
index 42615723344..c2d31b8a697 100644
--- a/chromium/content/browser/browser_child_process_host_impl.cc
+++ b/chromium/content/browser/browser_child_process_host_impl.cc
@@ -129,12 +129,7 @@ void BrowserChildProcessHostImpl::TerminateAll() {
}
void BrowserChildProcessHostImpl::Launch(
-#if defined(OS_WIN)
SandboxedProcessLauncherDelegate* delegate,
-#elif defined(OS_POSIX)
- bool use_zygote,
- const base::EnvironmentMap& environ,
-#endif
CommandLine* cmd_line) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
@@ -144,15 +139,12 @@ void BrowserChildProcessHostImpl::Launch(
const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
static const char* kForwardSwitches[] = {
switches::kDisableLogging,
- switches::kEnableDCHECK,
switches::kEnableLogging,
+ switches::kIPCConnectionTimeout,
switches::kLoggingLevel,
switches::kTraceToConsole,
switches::kV,
switches::kVModule,
-#if defined(OS_POSIX)
- switches::kChildCleanExit,
-#endif
#if defined(OS_WIN)
switches::kEnableHighResolutionTime,
#endif
@@ -161,13 +153,7 @@ void BrowserChildProcessHostImpl::Launch(
arraysize(kForwardSwitches));
child_process_.reset(new ChildProcessLauncher(
-#if defined(OS_WIN)
delegate,
-#elif defined(OS_POSIX)
- use_zygote,
- environ,
- child_process_host_->TakeClientFileDescriptor(),
-#endif
cmd_line,
data_.id,
this));
@@ -229,6 +215,12 @@ void BrowserChildProcessHostImpl::NotifyProcessInstanceCreated(
BrowserChildProcessInstanceCreated(data));
}
+void BrowserChildProcessHostImpl::HistogramBadMessageTerminated(
+ int process_type) {
+ UMA_HISTOGRAM_ENUMERATION("ChildProcess.BadMessgeTerminated", process_type,
+ PROCESS_TYPE_MAX);
+}
+
base::TerminationStatus BrowserChildProcessHostImpl::GetTerminationStatus(
bool known_dead, int* exit_code) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
@@ -262,6 +254,16 @@ void BrowserChildProcessHostImpl::OnChannelError() {
delegate_->OnChannelError();
}
+void BrowserChildProcessHostImpl::OnBadMessageReceived(
+ const IPC::Message& message) {
+ HistogramBadMessageTerminated(data_.process_type);
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableKillAfterBadIPC)) {
+ return;
+ }
+ base::KillProcess(GetHandle(), RESULT_CODE_KILLED_BAD_MESSAGE, false);
+}
+
bool BrowserChildProcessHostImpl::CanShutdown() {
return delegate_->CanShutdown();
}
@@ -313,6 +315,11 @@ bool BrowserChildProcessHostImpl::Send(IPC::Message* message) {
return child_process_host_->Send(message);
}
+void BrowserChildProcessHostImpl::OnProcessLaunchFailed() {
+ delegate_->OnProcessLaunchFailed();
+ delete delegate_; // Will delete us
+}
+
void BrowserChildProcessHostImpl::OnProcessLaunched() {
base::ProcessHandle handle = child_process_->GetHandle();
if (!handle) {
diff --git a/chromium/content/browser/browser_child_process_host_impl.h b/chromium/content/browser/browser_child_process_host_impl.h
index 428da3ddaf5..b196551532e 100644
--- a/chromium/content/browser/browser_child_process_host_impl.h
+++ b/chromium/content/browser/browser_child_process_host_impl.h
@@ -17,6 +17,10 @@
#include "content/public/browser/child_process_data.h"
#include "content/public/common/child_process_host_delegate.h"
+namespace base {
+class CommandLine;
+}
+
namespace content {
class BrowserChildProcessHostIterator;
@@ -43,13 +47,8 @@ class CONTENT_EXPORT BrowserChildProcessHostImpl
// BrowserChildProcessHost implementation:
virtual bool Send(IPC::Message* message) OVERRIDE;
virtual void Launch(
-#if defined(OS_WIN)
SandboxedProcessLauncherDelegate* delegate,
-#elif defined(OS_POSIX)
- bool use_zygote,
- const base::EnvironmentMap& environ,
-#endif
- CommandLine* cmd_line) OVERRIDE;
+ base::CommandLine* cmd_line) OVERRIDE;
virtual const ChildProcessData& GetData() const OVERRIDE;
virtual ChildProcessHost* GetHost() const OVERRIDE;
virtual base::TerminationStatus GetTerminationStatus(
@@ -57,9 +56,14 @@ class CONTENT_EXPORT BrowserChildProcessHostImpl
virtual void SetName(const base::string16& name) OVERRIDE;
virtual void SetHandle(base::ProcessHandle handle) OVERRIDE;
- // Returns the handle of the child process. This can be called only after
- // OnProcessLaunched is called or it will be invalid and may crash.
- base::ProcessHandle GetHandle() const;
+ // ChildProcessHostDelegate implementation:
+ virtual bool CanShutdown() OVERRIDE;
+ virtual void OnChildDisconnected() OVERRIDE;
+ virtual base::ProcessHandle GetHandle() const OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+ virtual void OnChannelConnected(int32 peer_pid) OVERRIDE;
+ virtual void OnChannelError() OVERRIDE;
+ virtual void OnBadMessageReceived(const IPC::Message& message) OVERRIDE;
// Removes this host from the host list. Calls ChildProcessHost::ForceShutdown
void ForceShutdown();
@@ -77,6 +81,8 @@ class CONTENT_EXPORT BrowserChildProcessHostImpl
// 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_; }
typedef std::list<BrowserChildProcessHostImpl*> BrowserChildProcessList;
@@ -89,15 +95,9 @@ class CONTENT_EXPORT BrowserChildProcessHostImpl
static void AddObserver(BrowserChildProcessObserver* observer);
static void RemoveObserver(BrowserChildProcessObserver* observer);
- // ChildProcessHostDelegate implementation:
- virtual bool CanShutdown() OVERRIDE;
- virtual void OnChildDisconnected() OVERRIDE;
- virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
- virtual void OnChannelConnected(int32 peer_pid) OVERRIDE;
- virtual void OnChannelError() OVERRIDE;
-
// ChildProcessLauncher::Client implementation.
virtual void OnProcessLaunched() OVERRIDE;
+ virtual void OnProcessLaunchFailed() OVERRIDE;
#if defined(OS_WIN)
void DeleteProcessWaitableEvent(base::WaitableEvent* event);
diff --git a/chromium/content/browser/browser_context.cc b/chromium/content/browser/browser_context.cc
index b720ae48014..dbc5949317a 100644
--- a/chromium/content/browser/browser_context.cc
+++ b/chromium/content/browser/browser_context.cc
@@ -5,14 +5,13 @@
#include "content/public/browser/browser_context.h"
#if !defined(OS_IOS)
-#include "content/browser/appcache/chrome_appcache_service.h"
-#include "content/browser/dom_storage/dom_storage_context_wrapper.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"
#include "content/browser/loader/resource_dispatcher_host_impl.h"
-#include "content/browser/storage_partition_impl.h"
#include "content/browser/storage_partition_impl_map.h"
#include "content/common/child_process_host_impl.h"
+#include "content/public/browser/blob_handle.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/site_instance.h"
@@ -68,15 +67,9 @@ StoragePartition* GetStoragePartitionFromConfig(
return partition_map->Get(partition_domain, partition_name, in_memory);
}
-// Run |callback| on each DOMStorageContextWrapper in |browser_context|.
-void PurgeDOMStorageContextInPartition(StoragePartition* storage_partition) {
- static_cast<StoragePartitionImpl*>(storage_partition)->
- GetDOMStorageContext()->PurgeMemory();
-}
-
void SaveSessionStateOnIOThread(
const scoped_refptr<net::URLRequestContextGetter>& context_getter,
- appcache::AppCacheService* appcache_service) {
+ appcache::AppCacheServiceImpl* appcache_service) {
net::URLRequestContext* context = context_getter->GetURLRequestContext();
context->cookie_store()->GetCookieMonster()->
SetForceKeepSessionState();
@@ -90,10 +83,6 @@ void SaveSessionStateOnIndexedDBThread(
indexed_db_context->SetForceKeepSessionState();
}
-void PurgeMemoryOnIOThread(appcache::AppCacheService* appcache_service) {
- appcache_service->PurgeMemory();
-}
-
} // namespace
// static
@@ -209,6 +198,20 @@ StoragePartition* BrowserContext::GetDefaultStoragePartition(
return GetStoragePartition(browser_context, NULL);
}
+void BrowserContext::CreateMemoryBackedBlob(BrowserContext* browser_context,
+ const char* data, size_t length,
+ const BlobCallback& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ ChromeBlobStorageContext* blob_context =
+ ChromeBlobStorageContext::GetFor(browser_context);
+ BrowserThread::PostTaskAndReplyWithResult(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&ChromeBlobStorageContext::CreateMemoryBackedBlob,
+ make_scoped_refptr(blob_context), data, length),
+ callback);
+}
+
void BrowserContext::EnsureResourceContextInitialized(BrowserContext* context) {
// This will be enough to tickle initialization of BrowserContext if
// necessary, which initializes ResourceContext. The reason we don't call
@@ -233,7 +236,8 @@ void BrowserContext::SaveSessionState(BrowserContext* browser_context) {
base::Bind(
&SaveSessionStateOnIOThread,
make_scoped_refptr(browser_context->GetRequestContext()),
- storage_partition->GetAppCacheService()));
+ static_cast<appcache::AppCacheServiceImpl*>(
+ storage_partition->GetAppCacheService())));
}
DOMStorageContextWrapper* dom_storage_context_proxy =
@@ -253,20 +257,6 @@ void BrowserContext::SaveSessionState(BrowserContext* browser_context) {
}
}
-void BrowserContext::PurgeMemory(BrowserContext* browser_context) {
- if (BrowserThread::IsMessageLoopValid(BrowserThread::IO)) {
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::Bind(
- &PurgeMemoryOnIOThread,
- BrowserContext::GetDefaultStoragePartition(browser_context)->
- GetAppCacheService()));
- }
-
- ForEachStoragePartition(browser_context,
- base::Bind(&PurgeDOMStorageContextInPartition));
-}
-
#endif // !OS_IOS
BrowserContext::~BrowserContext() {
diff --git a/chromium/content/browser/browser_main_loop.cc b/chromium/content/browser/browser_main_loop.cc
index 1930bc083a7..8bcc3d33b9d 100644
--- a/chromium/content/browser/browser_main_loop.cc
+++ b/chromium/content/browser/browser_main_loop.cc
@@ -23,8 +23,9 @@
#include "base/thread_task_runner_handle.h"
#include "base/threading/thread_restrictions.h"
#include "base/timer/hi_res_timer_manager.h"
+#include "content/browser/battery_status/battery_status_service.h"
#include "content/browser/browser_thread_impl.h"
-#include "content/browser/device_orientation/device_inertial_sensor_service.h"
+#include "content/browser/device_sensors/device_inertial_sensor_service.h"
#include "content/browser/download/save_file_manager.h"
#include "content/browser/gamepad/gamepad_service.h"
#include "content/browser/gpu/browser_gpu_channel_host_factory.h"
@@ -34,14 +35,14 @@
#include "content/browser/gpu/gpu_process_host_ui_shim.h"
#include "content/browser/histogram_synchronizer.h"
#include "content/browser/loader/resource_dispatcher_host_impl.h"
+#include "content/browser/media/capture/audio_mirroring_manager.h"
#include "content/browser/media/media_internals.h"
#include "content/browser/net/browser_online_state_observer.h"
#include "content/browser/plugin_service_impl.h"
-#include "content/browser/renderer_host/media/audio_mirroring_manager.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/webui/content_web_ui_controller_factory.h"
#include "content/browser/webui/url_data_manager.h"
#include "content/public/browser/browser_main_parts.h"
@@ -62,17 +63,29 @@
#include "net/ssl/ssl_config_service.h"
#include "ui/base/clipboard/clipboard.h"
+#if defined(USE_AURA) || (defined(OS_MACOSX) && !defined(OS_IOS))
+#include "content/browser/compositor/image_transport_factory.h"
+#endif
+
#if defined(USE_AURA)
-#include "content/browser/aura/image_transport_factory.h"
+#include "content/public/browser/context_factory.h"
+#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 "content/browser/android/browser_startup_controller.h"
#include "content/browser/android/surface_texture_peer_browser_impl.h"
+#include "content/browser/android/tracing_controller_android.h"
+#include "ui/gl/gl_surface.h"
#endif
-#if defined(OS_MACOSX)
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+#include "content/browser/bootstrap_sandbox_mac.h"
#include "content/browser/theme_helper_mac.h"
#endif
@@ -81,7 +94,6 @@
#include <commctrl.h>
#include <shellapi.h>
-#include "base/win/text_services_message_filter.h"
#include "content/browser/system_message_window_win.h"
#include "content/common/sandbox_win.h"
#include "net/base/winsock_init.h"
@@ -98,15 +110,10 @@
#include "content/browser/device_monitor_mac.h"
#endif
-#if defined(TOOLKIT_GTK)
-#include "ui/gfx/gtk_util.h"
-#endif
-
#if defined(OS_POSIX) && !defined(OS_MACOSX)
-#include <sys/stat.h>
-
#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_client.h"
#endif
#if defined(TCMALLOC_TRACE_MEMORY_SUPPORTED)
@@ -114,7 +121,8 @@
#endif
#if defined(USE_X11)
-#include <X11/Xlib.h>
+#include "ui/gfx/x/x11_connection.h"
+#include "ui/gfx/x/x11_types.h"
#endif
// One of the linux specific headers defines this as a macro.
@@ -128,55 +136,33 @@ namespace {
#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
void SetupSandbox(const CommandLine& parsed_command_line) {
TRACE_EVENT0("startup", "SetupSandbox");
- // TODO(evanm): move this into SandboxWrapper; I'm just trying to move this
- // code en masse out of chrome_main for now.
base::FilePath sandbox_binary;
- bool env_chrome_devel_sandbox_set = false;
- struct stat st;
+
+ scoped_ptr<sandbox::SetuidSandboxClient> setuid_sandbox_client(
+ sandbox::SetuidSandboxClient::Create());
const bool want_setuid_sandbox =
!parsed_command_line.HasSwitch(switches::kNoSandbox) &&
- !parsed_command_line.HasSwitch(switches::kDisableSetuidSandbox);
+ !parsed_command_line.HasSwitch(switches::kDisableSetuidSandbox) &&
+ !setuid_sandbox_client->IsDisabledViaEnvironment();
+ static const char no_suid_error[] =
+ "Running without the SUID sandbox! See "
+ "https://code.google.com/p/chromium/wiki/LinuxSUIDSandboxDevelopment "
+ "for more information on developing with the sandbox on.";
if (want_setuid_sandbox) {
- base::FilePath exe_dir;
- if (PathService::Get(base::DIR_EXE, &exe_dir)) {
- base::FilePath sandbox_candidate = exe_dir.AppendASCII("chrome-sandbox");
- if (base::PathExists(sandbox_candidate))
- sandbox_binary = sandbox_candidate;
- }
-
- // In user-managed builds, including development builds, an environment
- // variable is required to enable the sandbox. See
- // http://code.google.com/p/chromium/wiki/LinuxSUIDSandboxDevelopment
- if (sandbox_binary.empty() &&
- stat(base::kProcSelfExe, &st) == 0 && st.st_uid == getuid()) {
- const char* devel_sandbox_path = getenv("CHROME_DEVEL_SANDBOX");
- if (devel_sandbox_path) {
- env_chrome_devel_sandbox_set = true;
- sandbox_binary = base::FilePath(devel_sandbox_path);
- }
- }
-
- static const char no_suid_error[] = "Running without the SUID sandbox! See "
- "https://code.google.com/p/chromium/wiki/LinuxSUIDSandboxDevelopment "
- "for more information on developing with the sandbox on.";
+ sandbox_binary = setuid_sandbox_client->GetSandboxBinaryPath();
if (sandbox_binary.empty()) {
- if (!env_chrome_devel_sandbox_set) {
- // This needs to be fatal. Talk to security@chromium.org if you feel
- // otherwise.
- LOG(FATAL) << no_suid_error;
- }
-
- // TODO(jln): an empty CHROME_DEVEL_SANDBOX environment variable (as
- // opposed to a non existing one) is not fatal yet. This is needed
- // because of existing bots and scripts. Fix it (crbug.com/245376).
- LOG(ERROR) << no_suid_error;
+ // This needs to be fatal. Talk to security@chromium.org if you feel
+ // otherwise.
+ LOG(FATAL) << no_suid_error;
}
+ } else {
+ LOG(ERROR) << no_suid_error;
}
// Tickle the sandbox host and zygote host so they fork now.
- RenderSandboxHostLinux::GetInstance()->Init(sandbox_binary.value());
+ RenderSandboxHostLinux::GetInstance()->Init();
ZygoteHostImpl::GetInstance()->Init(sandbox_binary.value());
}
#endif
@@ -191,18 +177,7 @@ static void GLibLogHandler(const gchar* log_domain,
if (!message)
message = "<no message>";
- if (strstr(message, "Loading IM context type") ||
- strstr(message, "wrong ELF class: ELFCLASS64")) {
- // http://crbug.com/9643
- // Until we have a real 64-bit build or all of these 32-bit package issues
- // are sorted out, don't fatal on ELF 32/64-bit mismatch warnings and don't
- // spam the user with more than one of them.
- static bool alerted = false;
- if (!alerted) {
- LOG(ERROR) << "Bug 9643: " << log_domain << ": " << message;
- alerted = true;
- }
- } else if (strstr(message, "Unable to retrieve the file info for")) {
+ if (strstr(message, "Unable to retrieve the file info for")) {
LOG(ERROR) << "GTK File code error: " << message;
} else if (strstr(message, "Could not find the icon") &&
strstr(log_domain, "Gtk")) {
@@ -215,8 +190,6 @@ static void GLibLogHandler(const gchar* log_domain,
} else if (strstr(message, "Unable to create Ubuntu Menu Proxy") &&
strstr(log_domain, "<unknown>")) {
LOG(ERROR) << "GTK menu proxy create failed";
- } else if (strstr(message, "gtk_drag_dest_leave: assertion")) {
- LOG(ERROR) << "Drag destination deleted: http://crbug.com/18557";
} else if (strstr(message, "Out of memory") &&
strstr(log_domain, "<unknown>")) {
LOG(ERROR) << "DBus call timeout or out of memory: "
@@ -225,11 +198,11 @@ static void GLibLogHandler(const gchar* log_domain,
strstr(log_domain, "<unknown>")) {
LOG(ERROR) << "DConf settings backend could not connect to session bus: "
<< "http://crbug.com/179797";
- } else if (strstr(message, "XDG_RUNTIME_DIR variable not set")) {
- LOG(ERROR) << message << " (http://bugs.chromium.org/97293)";
} else if (strstr(message, "Attempting to store changes into") ||
strstr(message, "Attempting to set the permissions of")) {
LOG(ERROR) << message << " (http://bugs.chromium.org/161366)";
+ } else if (strstr(message, "drawable is not a native X11 window")) {
+ LOG(ERROR) << message << " (http://bugs.chromium.org/329991)";
} else {
LOG(DFATAL) << log_domain << ": " << message;
}
@@ -251,6 +224,20 @@ static void SetUpGLibLogHandler() {
}
#endif
+void OnStoppedStartupTracing(const base::FilePath& trace_file) {
+ VLOG(0) << "Completed startup tracing to " << trace_file.value();
+}
+
+#if defined(USE_AURA)
+bool ShouldInitializeBrowserGpuChannelAndTransportSurface() {
+ return true;
+}
+#elif defined(OS_MACOSX) && !defined(OS_IOS)
+bool ShouldInitializeBrowserGpuChannelAndTransportSurface() {
+ return IsDelegatedRendererEnabled();
+}
+#endif
+
} // namespace
// The currently-running BrowserMainLoop. There can be one or zero.
@@ -322,7 +309,8 @@ BrowserMainLoop::BrowserMainLoop(const MainFunctionParams& parameters)
created_threads_(false),
// ContentMainRunner should have enabled tracing of the browser process
// when kTraceStartup is in the command line.
- is_tracing_startup_(base::debug::TraceLog::GetInstance()->IsEnabled()) {
+ is_tracing_startup_(
+ parameters.command_line.HasSwitch(switches::kTraceStartup)) {
DCHECK(!g_current_browser_main_loop);
g_current_browser_main_loop = this;
}
@@ -336,7 +324,7 @@ BrowserMainLoop::~BrowserMainLoop() {
}
void BrowserMainLoop::Init() {
- TRACE_EVENT0("startup", "BrowserMainLoop::Init")
+ TRACE_EVENT0("startup", "BrowserMainLoop::Init");
parts_.reset(
GetContentClient()->browser()->CreateBrowserMainParts(parameters_));
}
@@ -355,7 +343,7 @@ void BrowserMainLoop::EarlyInitialization() {
#if defined(USE_X11)
if (parsed_command_line_.HasSwitch(switches::kSingleProcess) ||
parsed_command_line_.HasSwitch(switches::kInProcessGPU)) {
- if (!XInitThreads()) {
+ if (!gfx::InitializeThreadedX11()) {
LOG(ERROR) << "Failed to put Xlib into threaded mode.";
}
}
@@ -377,16 +365,18 @@ void BrowserMainLoop::EarlyInitialization() {
g_type_init();
#endif
-#if !defined(USE_AURA)
- gfx::GtkInitFromCommandLine(parsed_command_line_);
-#endif
-
SetUpGLibLogHandler();
#endif
if (parts_)
parts_->PreEarlyInitialization();
+#if defined(OS_MACOSX)
+ // We use quite a few file descriptors for our IPC, and the default limit on
+ // the Mac is low (256), so bump it up.
+ base::SetFdLimit(1024);
+#endif
+
#if defined(OS_WIN)
net::EnsureWinsockInit();
#endif
@@ -396,9 +386,6 @@ void BrowserMainLoop::EarlyInitialization() {
crypto::EnsureNSPRInit();
#endif // !defined(USE_OPENSSL)
- if (parsed_command_line_.HasSwitch(switches::kEnableSSLCachedInfo))
- net::SSLConfigService::EnableCachedInfo();
-
#if !defined(OS_IOS)
if (parsed_command_line_.HasSwitch(switches::kRendererProcessLimit)) {
std::string limit_string = parsed_command_line_.GetSwitchValueASCII(
@@ -415,7 +402,7 @@ void BrowserMainLoop::EarlyInitialization() {
}
void BrowserMainLoop::MainMessageLoopStart() {
- TRACE_EVENT0("startup", "BrowserMainLoop::MainMessageLoopStart")
+ TRACE_EVENT0("startup", "BrowserMainLoop::MainMessageLoopStart");
if (parts_) {
TRACE_EVENT0("startup",
"BrowserMainLoop::MainMessageLoopStart:PreMainMessageLoopStart");
@@ -433,62 +420,56 @@ void BrowserMainLoop::MainMessageLoopStart() {
// Create a MessageLoop if one does not already exist for the current thread.
if (!base::MessageLoop::current())
- main_message_loop_.reset(new base::MessageLoop(base::MessageLoop::TYPE_UI));
+ main_message_loop_.reset(new base::MessageLoopForUI);
InitializeMainThread();
{
- TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:SystemMonitor")
+ TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:SystemMonitor");
system_monitor_.reset(new base::SystemMonitor);
}
{
- TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:PowerMonitor")
+ TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:PowerMonitor");
scoped_ptr<base::PowerMonitorSource> power_monitor_source(
new base::PowerMonitorDeviceSource());
power_monitor_.reset(new base::PowerMonitor(power_monitor_source.Pass()));
}
{
- TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:HighResTimerManager")
+ TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:HighResTimerManager");
hi_res_timer_manager_.reset(new base::HighResolutionTimerManager);
}
{
- TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:NetworkChangeNotifier")
+ TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:NetworkChangeNotifier");
network_change_notifier_.reset(net::NetworkChangeNotifier::Create());
}
#if !defined(OS_IOS)
{
- TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:MediaFeatures")
+ TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:MediaFeatures");
media::InitializeCPUSpecificMediaFeatures();
}
{
- TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:AudioMan")
+ TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:AudioMan");
audio_manager_.reset(media::AudioManager::Create(
MediaInternals::GetInstance()));
}
{
- TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:MIDIManager")
- midi_manager_.reset(media::MIDIManager::Create());
+ TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:MidiManager");
+ midi_manager_.reset(media::MidiManager::Create());
}
{
- TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:ContentWebUIController")
+ TRACE_EVENT0("startup",
+ "BrowserMainLoop::Subsystem:ContentWebUIController");
WebUIControllerFactory::RegisterFactory(
ContentWebUIControllerFactory::GetInstance());
}
{
- TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:AudioMirroringManager")
+ TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:AudioMirroringManager");
audio_mirroring_manager_.reset(new AudioMirroringManager());
}
-
- // Start tracing to a file if needed.
- if (is_tracing_startup_) {
- TRACE_EVENT0("startup", "BrowserMainLoop::InitStartupTracing")
- InitStartupTracing(parsed_command_line_);
- }
-
{
- TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:OnlineStateObserver")
+ TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:OnlineStateObserver");
online_state_observer_.reset(new BrowserOnlineStateObserver);
}
@@ -500,31 +481,30 @@ void BrowserMainLoop::MainMessageLoopStart() {
#if defined(OS_WIN)
system_message_window_.reset(new SystemMessageWindowWin);
-
- if (base::win::IsTSFAwareRequired()) {
- // Create a TSF message filter for the message loop. MessageLoop takes
- // ownership of the filter.
- scoped_ptr<base::win::TextServicesMessageFilter> tsf_message_filter(
- new base::win::TextServicesMessageFilter);
- if (tsf_message_filter->Init()) {
- base::MessageLoopForUI::current()->SetMessageFilter(
- tsf_message_filter.PassAs<base::MessageLoopForUI::MessageFilter>());
- }
- }
#endif
if (parts_)
parts_->PostMainMessageLoopStart();
+#if !defined(OS_IOS)
+ // Start tracing to a file if needed. Only do this after starting the main
+ // message loop to avoid calling MessagePumpForUI::ScheduleWork() before
+ // MessagePumpForUI::Start() as it will crash the browser.
+ if (is_tracing_startup_) {
+ TRACE_EVENT0("startup", "BrowserMainLoop::InitStartupTracing");
+ InitStartupTracing(parsed_command_line_);
+ }
+#endif // !defined(OS_IOS)
+
#if defined(OS_ANDROID)
{
- TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:SurfaceTexturePeer")
+ TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:SurfaceTexturePeer");
SurfaceTexturePeer::InitInstance(new SurfaceTexturePeerBrowserImpl());
}
#endif
if (parsed_command_line_.HasSwitch(switches::kMemoryMetrics)) {
- TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:MemoryObserver")
+ TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:MemoryObserver");
memory_observer_.reset(new MemoryObserver());
base::MessageLoop::current()->AddTaskObserver(memory_observer_.get());
}
@@ -539,7 +519,6 @@ void BrowserMainLoop::MainMessageLoopStart() {
}
int BrowserMainLoop::PreCreateThreads() {
-
if (parts_) {
TRACE_EVENT0("startup",
"BrowserMainLoop::CreateThreads:PreCreateThreads");
@@ -552,7 +531,7 @@ int BrowserMainLoop::PreCreateThreads() {
// but must be created on the main thread. The service ctor is
// inexpensive and does not invoke the io_thread() accessor.
{
- TRACE_EVENT0("startup", "BrowserMainLoop::CreateThreads:PluginService")
+ TRACE_EVENT0("startup", "BrowserMainLoop::CreateThreads:PluginService");
PluginService::GetInstance()->Init();
}
#endif
@@ -700,7 +679,6 @@ int BrowserMainLoop::CreateThreads() {
}
TRACE_EVENT_END0("startup", "BrowserMainLoop::CreateThreads:start");
-
}
created_threads_ = true;
return result_code_;
@@ -738,7 +716,7 @@ void BrowserMainLoop::ShutdownThreadsAndCleanUp() {
// Called early, nothing to do
return;
}
- TRACE_EVENT0("shutdown", "BrowserMainLoop::ShutdownThreadsAndCleanUp")
+ TRACE_EVENT0("shutdown", "BrowserMainLoop::ShutdownThreadsAndCleanUp");
// Teardown may start in PostMainMessageLoopRun, and during teardown we
// need to be able to perform IO.
@@ -748,8 +726,10 @@ void BrowserMainLoop::ShutdownThreadsAndCleanUp() {
base::Bind(base::IgnoreResult(&base::ThreadRestrictions::SetIOAllowed),
true));
+#if !defined(OS_IOS)
if (RenderProcessHost::run_renderer_in_process())
RenderProcessHostImpl::ShutDownInProcessRenderer();
+#endif
if (parts_) {
TRACE_EVENT0("shutdown",
@@ -776,8 +756,8 @@ void BrowserMainLoop::ShutdownThreadsAndCleanUp() {
resource_dispatcher_host_.get()->Shutdown();
}
-#if defined(USE_AURA)
- {
+#if defined(USE_AURA) || defined(OS_MACOSX)
+ if (ShouldInitializeBrowserGpuChannelAndTransportSurface()) {
TRACE_EVENT0("shutdown",
"BrowserMainLoop::Subsystem:ImageTransportFactory");
ImageTransportFactory::Terminate();
@@ -905,6 +885,10 @@ void BrowserMainLoop::ShutdownThreadsAndCleanUp() {
DeviceInertialSensorService::GetInstance()->Shutdown();
}
{
+ TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:BatteryStatusService");
+ BatteryStatusService::GetInstance()->Shutdown();
+ }
+ {
TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:DeleteDataSources");
URLDataManager::DeleteDataSources();
}
@@ -917,7 +901,7 @@ void BrowserMainLoop::ShutdownThreadsAndCleanUp() {
}
void BrowserMainLoop::InitializeMainThread() {
- TRACE_EVENT0("startup", "BrowserMainLoop::InitializeMainThread")
+ TRACE_EVENT0("startup", "BrowserMainLoop::InitializeMainThread");
const char* kThreadName = "CrBrowserMain";
base::PlatformThread::SetName(kThreadName);
if (main_message_loop_)
@@ -929,7 +913,7 @@ void BrowserMainLoop::InitializeMainThread() {
}
int BrowserMainLoop::BrowserThreadsStarted() {
- TRACE_EVENT0("startup", "BrowserMainLoop::BrowserThreadsStarted")
+ TRACE_EVENT0("startup", "BrowserMainLoop::BrowserThreadsStarted");
#if !defined(OS_IOS)
indexed_db_thread_.reset(new base::Thread("IndexedDB"));
@@ -948,27 +932,42 @@ int BrowserMainLoop::BrowserThreadsStarted() {
#if !defined(OS_IOS)
HistogramSynchronizer::GetInstance();
+ bool initialize_gpu_data_manager = true;
+#if defined(OS_ANDROID)
+ // On Android, GLSurface::InitializeOneOff() must be called before initalizing
+ // the GpuDataManagerImpl as it uses the GL bindings. crbug.com/326295
+ if (!gfx::GLSurface::InitializeOneOff()) {
+ LOG(ERROR) << "GLSurface::InitializeOneOff failed";
+ initialize_gpu_data_manager = false;
+ }
+#endif
+
// Initialize the GpuDataManager before we set up the MessageLoops because
// otherwise we'll trigger the assertion about doing IO on the UI thread.
- GpuDataManagerImpl::GetInstance()->Initialize();
+ if (initialize_gpu_data_manager)
+ GpuDataManagerImpl::GetInstance()->Initialize();
- bool always_uses_gpu = IsForceCompositingModeEnabled();
+ bool always_uses_gpu = true;
bool established_gpu_channel = false;
-#if defined(USE_AURA) || defined(OS_ANDROID)
- established_gpu_channel =
- !parsed_command_line_.HasSwitch(switches::kDisableGpuProcessPrelaunch) ||
- parsed_command_line_.HasSwitch(switches::kSingleProcess) ||
- parsed_command_line_.HasSwitch(switches::kInProcessGPU);
+#if defined(USE_AURA) || defined(OS_MACOSX)
+ if (ShouldInitializeBrowserGpuChannelAndTransportSurface()) {
+ established_gpu_channel = true;
+ if (!GpuDataManagerImpl::GetInstance()->CanUseGpuBrowserCompositor()) {
+ established_gpu_channel = always_uses_gpu = false;
+ }
+ BrowserGpuChannelHostFactory::Initialize(established_gpu_channel);
+ ImageTransportFactory::Initialize();
#if defined(USE_AURA)
- if (!GpuDataManagerImpl::GetInstance()->CanUseGpuBrowserCompositor()) {
- established_gpu_channel = always_uses_gpu = false;
+ if (aura::Env::GetInstance()) {
+ aura::Env::GetInstance()->set_context_factory(
+ content::GetContextFactory());
+ }
+#endif
}
- BrowserGpuChannelHostFactory::Initialize(established_gpu_channel);
- ImageTransportFactory::Initialize();
#elif defined(OS_ANDROID)
+ established_gpu_channel = true;
BrowserGpuChannelHostFactory::Initialize(established_gpu_channel);
#endif
-#endif
#if defined(OS_LINUX) && defined(USE_UDEV)
device_monitor_linux_.reset(new DeviceMonitorLinux());
@@ -1005,6 +1004,12 @@ int BrowserMainLoop::BrowserThreadsStarted() {
io_thread_->message_loop_proxy(), main_thread_->message_loop_proxy());
}
+ {
+ TRACE_EVENT0("startup",
+ "BrowserMainLoop::BrowserThreadsStarted::TimeZoneMonitor");
+ time_zone_monitor_ = TimeZoneMonitor::Create();
+ }
+
// Alert the clipboard class to which threads are allowed to access the
// clipboard:
std::vector<base::PlatformThreadId> allowed_clipboard_threads;
@@ -1023,7 +1028,6 @@ int BrowserMainLoop::BrowserThreadsStarted() {
if (GpuDataManagerImpl::GetInstance()->GpuAccessAllowed(NULL) &&
!established_gpu_channel &&
always_uses_gpu &&
- !parsed_command_line_.HasSwitch(switches::kDisableGpuProcessPrelaunch) &&
!parsed_command_line_.HasSwitch(switches::kSingleProcess) &&
!parsed_command_line_.HasSwitch(switches::kInProcessGPU)) {
TRACE_EVENT_INSTANT0("gpu", "Post task to launch GPU process",
@@ -1034,16 +1038,23 @@ int BrowserMainLoop::BrowserThreadsStarted() {
GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED,
CAUSE_FOR_GPU_LAUNCH_BROWSER_STARTUP));
}
-#endif // !defined(OS_IOS)
#if defined(OS_MACOSX)
ThemeHelperMac::GetInstance();
-#endif
+ if (ShouldEnableBootstrapSandbox()) {
+ TRACE_EVENT0("startup",
+ "BrowserMainLoop::BrowserThreadsStarted:BootstrapSandbox");
+ CHECK(GetBootstrapSandbox());
+ }
+#endif // defined(OS_MACOSX)
+
+#endif // !defined(OS_IOS)
+
return result_code_;
}
-void BrowserMainLoop::InitializeToolkit() {
- TRACE_EVENT0("startup", "BrowserMainLoop::InitializeToolkit")
+bool BrowserMainLoop::InitializeToolkit() {
+ TRACE_EVENT0("startup", "BrowserMainLoop::InitializeToolkit");
// TODO(evan): this function is rather subtle, due to the variety
// of intersecting ifdefs we have. To keep it easy to follow, there
// are no #else branches on any #ifs.
@@ -1051,23 +1062,31 @@ void BrowserMainLoop::InitializeToolkit() {
// (Need to add InitializeToolkit stage to BrowserParts).
// See also GTK setup in EarlyInitialization, above, and associated comments.
-#if defined(TOOLKIT_GTK)
- // It is important for this to happen before the first run dialog, as it
- // styles the dialog as well.
- gfx::InitRCStyles();
-#endif
-
#if defined(OS_WIN)
// Init common control sex.
INITCOMMONCONTROLSEX config;
config.dwSize = sizeof(config);
config.dwICC = ICC_WIN95_CLASSES;
if (!InitCommonControlsEx(&config))
- LOG_GETLASTERROR(FATAL);
+ PLOG(FATAL);
#endif
+#if defined(USE_AURA)
+
+#if defined(USE_X11)
+ if (!gfx::GetXDisplay())
+ return false;
+#endif
+
+ // Env creates the compositor. Aura widgets need the compositor to be created
+ // before they can be initialized by the browser.
+ aura::Env::CreateInstance(true);
+#endif // defined(USE_AURA)
+
if (parts_)
parts_->ToolkitInitialized();
+
+ return true;
}
void BrowserMainLoop::MainMessageLoopRun() {
@@ -1075,7 +1094,7 @@ void BrowserMainLoop::MainMessageLoopRun() {
// Android's main message loop is the Java message loop.
NOTREACHED();
#else
- DCHECK_EQ(base::MessageLoop::TYPE_UI, base::MessageLoop::current()->type());
+ DCHECK(base::MessageLoopForUI::IsCurrent());
if (parameters_.ui_task)
base::MessageLoopForUI::current()->PostTask(FROM_HERE,
*parameters_.ui_task);
@@ -1097,8 +1116,12 @@ void BrowserMainLoop::InitStartupTracing(const CommandLine& command_line) {
return;
if (trace_file.empty()) {
+#if defined(OS_ANDROID)
+ TracingControllerAndroid::GenerateTracingFilePath(&trace_file);
+#else
// Default to saving the startup trace into the current dir.
trace_file = base::FilePath().AppendASCII("chrometrace.log");
+#endif
}
std::string delay_str = command_line.GetSwitchValueASCII(
@@ -1120,7 +1143,7 @@ void BrowserMainLoop::InitStartupTracing(const CommandLine& command_line) {
void BrowserMainLoop::EndStartupTracing(const base::FilePath& trace_file) {
is_tracing_startup_ = false;
TracingController::GetInstance()->DisableRecording(
- trace_file, TracingController::TracingFileResultCallback());
+ trace_file, base::Bind(&OnStoppedStartupTracing));
}
} // namespace content
diff --git a/chromium/content/browser/browser_main_loop.h b/chromium/content/browser/browser_main_loop.h
index 445d99050ef..f38ac4234ab 100644
--- a/chromium/content/browser/browser_main_loop.h
+++ b/chromium/content/browser/browser_main_loop.h
@@ -11,9 +11,8 @@
#include "content/browser/browser_process_sub_thread.h"
#include "content/public/browser/browser_main_runner.h"
-class CommandLine;
-
namespace base {
+class CommandLine;
class FilePath;
class HighResolutionTimerManager;
class MessageLoop;
@@ -27,7 +26,7 @@ class TraceEventSystemStatsMonitor;
namespace media {
class AudioManager;
-class MIDIManager;
+class MidiManager;
class UserInputMonitor;
} // namespace media
@@ -45,13 +44,15 @@ class MediaStreamManager;
class ResourceDispatcherHostImpl;
class SpeechRecognitionManagerImpl;
class StartupTaskRunner;
-class SystemMessageWindowWin;
+class TimeZoneMonitor;
struct MainFunctionParams;
#if defined(OS_LINUX)
class DeviceMonitorLinux;
#elif defined(OS_MACOSX)
class DeviceMonitorMac;
+#elif defined(OS_WIN)
+class SystemMessageWindowWin;
#endif
// Implements the main browser loop stages called from BrowserMainRunner.
@@ -68,7 +69,9 @@ class CONTENT_EXPORT BrowserMainLoop {
void Init();
void EarlyInitialization();
- void InitializeToolkit();
+ // Initializes the toolkit. Returns whether the toolkit initialization was
+ // successful or not.
+ bool InitializeToolkit();
void MainMessageLoopStart();
// Create and start running the tasks we need to complete startup. Note that
@@ -96,11 +99,17 @@ class CONTENT_EXPORT BrowserMainLoop {
media::UserInputMonitor* user_input_monitor() const {
return user_input_monitor_.get();
}
- media::MIDIManager* midi_manager() const { return midi_manager_.get(); }
+ media::MidiManager* midi_manager() const { return midi_manager_.get(); }
base::Thread* indexed_db_thread() const { return indexed_db_thread_.get(); }
bool is_tracing_startup() const { return is_tracing_startup_; }
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+ DeviceMonitorMac* device_monitor_mac() const {
+ return device_monitor_mac_.get();
+ }
+#endif
+
private:
class MemoryObserver;
// For ShutdownThreadsAndCleanUp.
@@ -121,12 +130,12 @@ class CONTENT_EXPORT BrowserMainLoop {
void MainMessageLoopRun();
- void InitStartupTracing(const CommandLine& command_line);
+ void InitStartupTracing(const base::CommandLine& command_line);
void EndStartupTracing(const base::FilePath& trace_file);
// Members initialized on construction ---------------------------------------
const MainFunctionParams& parameters_;
- const CommandLine& parsed_command_line_;
+ const base::CommandLine& parsed_command_line_;
int result_code_;
// True if the non-UI threads were created.
bool created_threads_;
@@ -140,7 +149,7 @@ class CONTENT_EXPORT BrowserMainLoop {
// user_input_monitor_ has to outlive audio_manager_, so declared first.
scoped_ptr<media::UserInputMonitor> user_input_monitor_;
scoped_ptr<media::AudioManager> audio_manager_;
- scoped_ptr<media::MIDIManager> midi_manager_;
+ scoped_ptr<media::MidiManager> midi_manager_;
scoped_ptr<AudioMirroringManager> audio_mirroring_manager_;
scoped_ptr<MediaStreamManager> media_stream_manager_;
// Per-process listener for online state changes.
@@ -166,6 +175,7 @@ class CONTENT_EXPORT BrowserMainLoop {
// Members initialized in |BrowserThreadsStarted()| --------------------------
scoped_ptr<ResourceDispatcherHostImpl> resource_dispatcher_host_;
scoped_ptr<SpeechRecognitionManagerImpl> speech_recognition_manager_;
+ scoped_ptr<TimeZoneMonitor> time_zone_monitor_;
// Members initialized in |RunMainMessageLoopParts()| ------------------------
scoped_ptr<BrowserProcessSubThread> db_thread_;
diff --git a/chromium/content/browser/browser_main_runner.cc b/chromium/content/browser/browser_main_runner.cc
index b2610b751af..c41fc9541c9 100644
--- a/chromium/content/browser/browser_main_runner.cc
+++ b/chromium/content/browser/browser_main_runner.cc
@@ -20,7 +20,6 @@
#include "ui/base/ime/input_method_initializer.h"
#if defined(OS_WIN)
-#include "base/win/metro.h"
#include "base/win/windows_version.h"
#include "ui/base/win/scoped_ole_initializer.h"
#endif
@@ -54,17 +53,13 @@ class BrowserMainRunnerImpl : public BrowserMainRunner {
#endif
#if defined(OS_WIN)
- if (parameters.command_line.HasSwitch(
- switches::kEnableTextServicesFramework)) {
- base::win::SetForceToUseTSF();
- } else if (base::win::GetVersion() < base::win::VERSION_VISTA) {
+ if (base::win::GetVersion() < base::win::VERSION_VISTA) {
// When "Extend support of advanced text services to all programs"
// (a.k.a. Cicero Unaware Application Support; CUAS) is enabled on
// Windows XP and handwriting modules shipped with Office 2003 are
// installed, "penjpn.dll" and "skchui.dll" will be loaded and then
- // crash
- // unless a user installs Office 2003 SP3. To prevent these modules from
- // being loaded, disable TSF entirely. crbug/160914.
+ // crash unless a user installs Office 2003 SP3. To prevent these
+ // modules from being loaded, disable TSF entirely. crbug.com/160914.
// TODO(yukawa): Add a high-level wrapper for this instead of calling
// Win32 API here directly.
ImmDisableTextFrameService(static_cast<DWORD>(-1));
@@ -89,7 +84,8 @@ class BrowserMainRunnerImpl : public BrowserMainRunner {
main_loop_->EarlyInitialization();
// Must happen before we try to use a message loop or display any UI.
- main_loop_->InitializeToolkit();
+ if (!main_loop_->InitializeToolkit())
+ return 1;
main_loop_->MainMessageLoopStart();
@@ -153,7 +149,12 @@ class BrowserMainRunnerImpl : public BrowserMainRunner {
#if defined(OS_WIN)
ole_initializer_.reset(NULL);
#endif
-
+ #if defined(OS_ANDROID)
+ // Forcefully terminates the RunLoop inside MessagePumpForUI, ensuring
+ // proper shutdown for content_browsertests. Shutdown() is not used by
+ // the actual browser.
+ base::MessageLoop::current()->QuitNow();
+ #endif
main_loop_.reset(NULL);
notification_service_.reset(NULL);
diff --git a/chromium/content/browser/browser_plugin/browser_plugin_embedder.cc b/chromium/content/browser/browser_plugin/browser_plugin_embedder.cc
index 18bfc25c3b4..30b47890802 100644
--- a/chromium/content/browser/browser_plugin/browser_plugin_embedder.cc
+++ b/chromium/content/browser/browser_plugin/browser_plugin_embedder.cc
@@ -6,8 +6,6 @@
#include "base/values.h"
#include "content/browser/browser_plugin/browser_plugin_guest.h"
-#include "content/browser/browser_plugin/browser_plugin_guest_manager.h"
-#include "content/browser/browser_plugin/browser_plugin_host_factory.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/browser_plugin/browser_plugin_constants.h"
@@ -15,6 +13,7 @@
#include "content/common/drag_messages.h"
#include "content/common/gpu/gpu_messages.h"
#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_plugin_guest_manager.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/native_web_keyboard_event.h"
#include "content/public/browser/render_view_host.h"
@@ -27,23 +26,17 @@
namespace content {
-// static
-BrowserPluginHostFactory* BrowserPluginEmbedder::factory_ = NULL;
-
BrowserPluginEmbedder::BrowserPluginEmbedder(WebContentsImpl* web_contents)
: WebContentsObserver(web_contents),
- next_get_render_view_request_id_(0) {
+ weak_ptr_factory_(this) {
}
BrowserPluginEmbedder::~BrowserPluginEmbedder() {
- CleanUp();
}
// static
BrowserPluginEmbedder* BrowserPluginEmbedder::Create(
WebContentsImpl* web_contents) {
- if (factory_)
- return factory_->CreateBrowserPluginEmbedder(web_contents);
return new BrowserPluginEmbedder(web_contents);
}
@@ -63,90 +56,34 @@ void BrowserPluginEmbedder::StartDrag(BrowserPluginGuest* guest) {
guest_started_drag_ = guest->AsWeakPtr();
}
-void BrowserPluginEmbedder::StopDrag(BrowserPluginGuest* guest) {
- if (guest_started_drag_.get() == guest) {
- guest_started_drag_.reset();
- }
+WebContentsImpl* BrowserPluginEmbedder::GetWebContents() const {
+ return static_cast<WebContentsImpl*>(web_contents());
}
-void BrowserPluginEmbedder::GetRenderViewHostAtPosition(
- int x, int y, const WebContents::GetRenderViewHostCallback& callback) {
- // Store the callback so we can call it later when we have the response.
- pending_get_render_view_callbacks_.insert(
- std::make_pair(next_get_render_view_request_id_, callback));
- Send(new BrowserPluginMsg_PluginAtPositionRequest(
- routing_id(),
- next_get_render_view_request_id_,
- gfx::Point(x, y)));
- ++next_get_render_view_request_id_;
+BrowserPluginGuestManager*
+BrowserPluginEmbedder::GetBrowserPluginGuestManager() const {
+ return GetWebContents()->GetBrowserContext()->GetGuestManager();
}
bool BrowserPluginEmbedder::DidSendScreenRectsCallback(
- BrowserPluginGuest* guest) {
+ WebContents* guest_web_contents) {
static_cast<RenderViewHostImpl*>(
- guest->GetWebContents()->GetRenderViewHost())->SendScreenRects();
+ guest_web_contents->GetRenderViewHost())->SendScreenRects();
// Not handled => Iterate over all guests.
return false;
}
void BrowserPluginEmbedder::DidSendScreenRects() {
- WebContentsImpl* embedder =
- static_cast<WebContentsImpl*>(web_contents());
- GetBrowserPluginGuestManager()->ForEachGuest(embedder, base::Bind(
- &BrowserPluginEmbedder::DidSendScreenRectsCallback,
- base::Unretained(this)));
-}
-
-bool BrowserPluginEmbedder::UnlockMouseIfNecessaryCallback(
- const NativeWebKeyboardEvent& event,
- BrowserPluginGuest* guest) {
- return guest->UnlockMouseIfNecessary(event);
-}
-
-bool BrowserPluginEmbedder::HandleKeyboardEvent(
- const NativeWebKeyboardEvent& event) {
- if ((event.type != blink::WebInputEvent::RawKeyDown) ||
- (event.windowsKeyCode != ui::VKEY_ESCAPE) ||
- (event.modifiers & blink::WebInputEvent::InputModifiers)) {
- return false;
- }
-
- WebContentsImpl* embedder =
- static_cast<WebContentsImpl*>(web_contents());
- return GetBrowserPluginGuestManager()->ForEachGuest(embedder, base::Bind(
- &BrowserPluginEmbedder::UnlockMouseIfNecessaryCallback,
- base::Unretained(this),
- event));
-}
-
-bool BrowserPluginEmbedder::SetZoomLevelCallback(
- double level, BrowserPluginGuest* guest) {
- guest->GetWebContents()->SetZoomLevel(level);
- // Not handled => Iterate over all guests.
- return false;
-}
-
-void BrowserPluginEmbedder::SetZoomLevel(double level) {
- WebContentsImpl* embedder =
- static_cast<WebContentsImpl*>(web_contents());
- GetBrowserPluginGuestManager()->ForEachGuest(embedder, base::Bind(
- &BrowserPluginEmbedder::SetZoomLevelCallback,
- base::Unretained(this),
- level));
-}
-
-void BrowserPluginEmbedder::RenderProcessGone(base::TerminationStatus status) {
- CleanUp();
+ GetBrowserPluginGuestManager()->ForEachGuest(
+ GetWebContents(), base::Bind(
+ &BrowserPluginEmbedder::DidSendScreenRectsCallback,
+ base::Unretained(this)));
}
bool BrowserPluginEmbedder::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(BrowserPluginEmbedder, message)
- IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_AllocateInstanceID,
- OnAllocateInstanceID)
IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_Attach, OnAttach)
- IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_PluginAtPositionResponse,
- OnPluginAtPositionResponse)
IPC_MESSAGE_HANDLER_GENERIC(DragHostMsg_UpdateDragCursor,
OnUpdateDragCursor(&handled));
IPC_MESSAGE_UNHANDLED(handled = false)
@@ -164,19 +101,11 @@ void BrowserPluginEmbedder::DragSourceEndedAt(int client_x, int client_y,
}
}
-void BrowserPluginEmbedder::DragSourceMovedTo(int client_x, int client_y,
- int screen_x, int screen_y) {
- if (guest_started_drag_.get()) {
- gfx::Point guest_offset =
- guest_started_drag_->GetScreenCoordinates(gfx::Point());
- guest_started_drag_->DragSourceMovedTo(client_x - guest_offset.x(),
- client_y - guest_offset.y(), screen_x, screen_y);
- }
-}
-
void BrowserPluginEmbedder::SystemDragEnded() {
- if (guest_started_drag_.get() &&
- (guest_started_drag_.get() != guest_dragging_over_.get()))
+ // When the embedder's drag/drop operation ends, we need to pass the message
+ // to the guest that initiated the drag/drop operation. This will ensure that
+ // the guest's RVH state is reset properly.
+ if (guest_started_drag_.get())
guest_started_drag_->EndSystemDrag();
guest_started_drag_.reset();
guest_dragging_over_.reset();
@@ -186,93 +115,42 @@ void BrowserPluginEmbedder::OnUpdateDragCursor(bool* handled) {
*handled = (guest_dragging_over_.get() != NULL);
}
-void BrowserPluginEmbedder::CleanUp() {
- // CleanUp gets called when BrowserPluginEmbedder's WebContents goes away
- // or the associated RenderViewHost is destroyed or swapped out. Therefore we
- // don't need to care about the pending callbacks anymore.
- pending_get_render_view_callbacks_.clear();
-}
-
-BrowserPluginGuestManager*
- BrowserPluginEmbedder::GetBrowserPluginGuestManager() {
- BrowserPluginGuestManager* guest_manager = static_cast<WebContentsImpl*>(
- web_contents())->GetBrowserPluginGuestManager();
- if (!guest_manager) {
- guest_manager = BrowserPluginGuestManager::Create();
- web_contents()->GetBrowserContext()->SetUserData(
- browser_plugin::kBrowserPluginGuestManagerKeyName, guest_manager);
+void BrowserPluginEmbedder::OnGuestCallback(
+ int instance_id,
+ const BrowserPluginHostMsg_Attach_Params& params,
+ const base::DictionaryValue* extra_params,
+ WebContents* guest_web_contents) {
+ BrowserPluginGuest* guest = guest_web_contents ?
+ static_cast<WebContentsImpl*>(guest_web_contents)->
+ GetBrowserPluginGuest() : NULL;
+ if (!guest) {
+ scoped_ptr<base::DictionaryValue> copy_extra_params(
+ extra_params->DeepCopy());
+ guest_web_contents = GetBrowserPluginGuestManager()->CreateGuest(
+ GetWebContents()->GetSiteInstance(),
+ instance_id,
+ copy_extra_params.Pass());
+ guest = guest_web_contents
+ ? static_cast<WebContentsImpl*>(guest_web_contents)
+ ->GetBrowserPluginGuest()
+ : NULL;
}
- return guest_manager;
-}
-void BrowserPluginEmbedder::OnAllocateInstanceID(int request_id) {
- int instance_id = GetBrowserPluginGuestManager()->get_next_instance_id();
- Send(new BrowserPluginMsg_AllocateInstanceID_ACK(
- routing_id(), request_id, instance_id));
+ if (guest)
+ guest->Attach(GetWebContents(), params, *extra_params);
}
void BrowserPluginEmbedder::OnAttach(
int instance_id,
const BrowserPluginHostMsg_Attach_Params& params,
const base::DictionaryValue& extra_params) {
- if (!GetBrowserPluginGuestManager()->CanEmbedderAccessInstanceIDMaybeKill(
- web_contents()->GetRenderProcessHost()->GetID(), instance_id))
- return;
-
- BrowserPluginGuest* guest =
- GetBrowserPluginGuestManager()->GetGuestByInstanceID(
- instance_id, web_contents()->GetRenderProcessHost()->GetID());
-
- if (guest) {
- // There is an implicit order expectation here:
- // 1. The content embedder is made aware of the attachment.
- // 2. BrowserPluginGuest::Attach is called.
- // 3. The content embedder issues queued events if any that happened
- // prior to attachment.
- GetContentClient()->browser()->GuestWebContentsAttached(
- guest->GetWebContents(),
- web_contents(),
- extra_params);
- guest->Attach(
- static_cast<WebContentsImpl*>(web_contents()), params, extra_params);
- return;
- }
-
- scoped_ptr<base::DictionaryValue> copy_extra_params(extra_params.DeepCopy());
- guest = GetBrowserPluginGuestManager()->CreateGuest(
- web_contents()->GetSiteInstance(),
- instance_id, params,
- copy_extra_params.Pass());
- if (guest) {
- GetContentClient()->browser()->GuestWebContentsAttached(
- guest->GetWebContents(),
- web_contents(),
- extra_params);
- guest->Initialize(params, static_cast<WebContentsImpl*>(web_contents()));
- }
-}
-
-void BrowserPluginEmbedder::OnPluginAtPositionResponse(
- int instance_id, int request_id, const gfx::Point& position) {
- const std::map<int, WebContents::GetRenderViewHostCallback>::iterator
- callback_iter = pending_get_render_view_callbacks_.find(request_id);
- if (callback_iter == pending_get_render_view_callbacks_.end())
- return;
-
- RenderViewHost* render_view_host;
- BrowserPluginGuest* guest = NULL;
- if (instance_id != browser_plugin::kInstanceIDNone) {
- guest = GetBrowserPluginGuestManager()->GetGuestByInstanceID(
- instance_id, web_contents()->GetRenderProcessHost()->GetID());
- }
-
- if (guest)
- render_view_host = guest->GetWebContents()->GetRenderViewHost();
- else // No plugin, use embedder's RenderViewHost.
- render_view_host = web_contents()->GetRenderViewHost();
-
- callback_iter->second.Run(render_view_host, position.x(), position.y());
- pending_get_render_view_callbacks_.erase(callback_iter);
+ GetBrowserPluginGuestManager()->MaybeGetGuestByInstanceIDOrKill(
+ instance_id, GetWebContents()->GetRenderProcessHost()->GetID(),
+ base::Bind(&BrowserPluginEmbedder::OnGuestCallback,
+ base::Unretained(this),
+ instance_id,
+ params,
+ &extra_params));
}
} // namespace content
diff --git a/chromium/content/browser/browser_plugin/browser_plugin_embedder.h b/chromium/content/browser/browser_plugin/browser_plugin_embedder.h
index 107fa618686..3c35bef17a7 100644
--- a/chromium/content/browser/browser_plugin/browser_plugin_embedder.h
+++ b/chromium/content/browser/browser_plugin/browser_plugin_embedder.h
@@ -33,7 +33,6 @@ namespace content {
class BrowserPluginGuest;
class BrowserPluginGuestManager;
-class BrowserPluginHostFactory;
class RenderWidgetHostImpl;
class WebContentsImpl;
struct NativeWebKeyboardEvent;
@@ -44,42 +43,18 @@ class CONTENT_EXPORT BrowserPluginEmbedder : public WebContentsObserver {
static BrowserPluginEmbedder* Create(WebContentsImpl* web_contents);
- // Returns the RenderViewHost at a point (|x|, |y|) asynchronously via
- // |callback|. We need a roundtrip to renderer process to get this
- // information.
- void GetRenderViewHostAtPosition(
- int x,
- int y,
- const WebContents::GetRenderViewHostCallback& callback);
+ // Returns this embedder's WebContentsImpl.
+ WebContentsImpl* GetWebContents() const;
// Called when embedder's |rwh| has sent screen rects to renderer.
void DidSendScreenRects();
- // Called when embedder's WebContentsImpl has unhandled keyboard input.
- // Returns whether the BrowserPlugin has handled the keyboard event.
- // Currently we are only interested in checking for the escape key to
- // unlock hte guest's pointer lock.
- bool HandleKeyboardEvent(const NativeWebKeyboardEvent& event);
-
- // Overrides factory for testing. Default (NULL) value indicates regular
- // (non-test) environment.
- static void set_factory_for_testing(BrowserPluginHostFactory* factory) {
- factory_ = factory;
- }
-
- // Sets the zoom level for all guests within this embedder.
- void SetZoomLevel(double level);
-
// WebContentsObserver implementation.
- virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE;
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
void DragSourceEndedAt(int client_x, int client_y, int screen_x,
int screen_y, blink::WebDragOperation operation);
- void DragSourceMovedTo(int client_x, int client_y,
- int screen_x, int screen_y);
-
void OnUpdateDragCursor(bool* handled);
void DragEnteredGuest(BrowserPluginGuest* guest);
@@ -88,29 +63,31 @@ class CONTENT_EXPORT BrowserPluginEmbedder : public WebContentsObserver {
void StartDrag(BrowserPluginGuest* guest);
- void StopDrag(BrowserPluginGuest* guest);
-
+ // Sends EndSystemDrag message to the guest that initiated the last drag/drop
+ // operation, if there's any.
void SystemDragEnded();
private:
- friend class TestBrowserPluginEmbedder;
-
- BrowserPluginEmbedder(WebContentsImpl* web_contents);
+ explicit BrowserPluginEmbedder(WebContentsImpl* web_contents);
- void CleanUp();
+ BrowserPluginGuestManager* GetBrowserPluginGuestManager() const;
- BrowserPluginGuestManager* GetBrowserPluginGuestManager();
+ bool DidSendScreenRectsCallback(WebContents* guest_web_contents);
- bool DidSendScreenRectsCallback(BrowserPluginGuest* guest);
-
- bool SetZoomLevelCallback(double level, BrowserPluginGuest* guest);
+ bool SetZoomLevelCallback(double level, WebContents* guest_web_contents);
bool UnlockMouseIfNecessaryCallback(const NativeWebKeyboardEvent& event,
- BrowserPluginGuest* guest);
+ WebContents* guest);
+
+ // Called by the content embedder when a guest exists with the provided
+ // |instance_id|.
+ void OnGuestCallback(int instance_id,
+ const BrowserPluginHostMsg_Attach_Params& params,
+ const base::DictionaryValue* extra_params,
+ WebContents* guest_web_contents);
// Message handlers.
- void OnAllocateInstanceID(int request_id);
void OnAttach(int instance_id,
const BrowserPluginHostMsg_Attach_Params& params,
const base::DictionaryValue& extra_params);
@@ -118,18 +95,6 @@ class CONTENT_EXPORT BrowserPluginEmbedder : public WebContentsObserver {
int request_id,
const gfx::Point& position);
- // Static factory instance (always NULL for non-test).
- static BrowserPluginHostFactory* factory_;
-
- // Map that contains outstanding queries to |GetRenderViewHostAtPosition|.
- // We need a roundtrip to the renderer process to retrieve the answer,
- // so we store these callbacks until we hear back from the renderer.
- typedef std::map<int, WebContents::GetRenderViewHostCallback>
- GetRenderViewHostCallbackMap;
- GetRenderViewHostCallbackMap pending_get_render_view_callbacks_;
- // Next request id for BrowserPluginMsg_PluginAtPositionRequest query.
- int next_get_render_view_request_id_;
-
// Used to correctly update the cursor when dragging over a guest, and to
// handle a race condition when dropping onto the guest that started the drag
// (the race is that the dragend message arrives before the drop message so
@@ -141,6 +106,8 @@ class CONTENT_EXPORT BrowserPluginEmbedder : public WebContentsObserver {
// status messages to the correct guest.
base::WeakPtr<BrowserPluginGuest> guest_started_drag_;
+ base::WeakPtrFactory<BrowserPluginEmbedder> weak_ptr_factory_;
+
DISALLOW_COPY_AND_ASSIGN(BrowserPluginEmbedder);
};
diff --git a/chromium/content/browser/browser_plugin/browser_plugin_geolocation_permission_context.cc b/chromium/content/browser/browser_plugin/browser_plugin_geolocation_permission_context.cc
deleted file mode 100644
index 8a803c47120..00000000000
--- a/chromium/content/browser/browser_plugin/browser_plugin_geolocation_permission_context.cc
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/browser_plugin/browser_plugin_geolocation_permission_context.h"
-
-#include "base/bind.h"
-#include "content/browser/browser_plugin/browser_plugin_guest.h"
-#include "content/browser/web_contents/web_contents_impl.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/render_process_host.h"
-#include "content/public/browser/render_view_host.h"
-
-namespace content {
-
-BrowserPluginGeolocationPermissionContext::
- BrowserPluginGeolocationPermissionContext() {
-}
-
-BrowserPluginGeolocationPermissionContext::
- ~BrowserPluginGeolocationPermissionContext() {
-}
-
-void BrowserPluginGeolocationPermissionContext::RequestGeolocationPermission(
- int render_process_id,
- int render_view_id,
- int bridge_id,
- const GURL& requesting_frame,
- base::Callback<void(bool)> callback) {
- if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
- BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- base::Bind(
- &BrowserPluginGeolocationPermissionContext::
- RequestGeolocationPermission,
- this,
- render_process_id,
- render_view_id,
- bridge_id,
- requesting_frame,
- callback));
- return;
- }
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- // Note that callback.Run(true) allows geolocation access, callback.Run(false)
- // denies geolocation access.
- // We need to go to the renderer to ask embedder's js if we are allowed to
- // have geolocation access.
- RenderViewHost* rvh = RenderViewHost::FromID(render_process_id,
- render_view_id);
- if (rvh) {
- DCHECK(rvh->GetProcess()->IsGuest());
- WebContentsImpl* guest_web_contents =
- static_cast<WebContentsImpl*>(rvh->GetDelegate()->GetAsWebContents());
- BrowserPluginGuest* guest = guest_web_contents->GetBrowserPluginGuest();
- guest->AskEmbedderForGeolocationPermission(bridge_id,
- requesting_frame,
- callback);
- }
-}
-
-void BrowserPluginGeolocationPermissionContext::
- CancelGeolocationPermissionRequest(int render_process_id,
- int render_view_id,
- int bridge_id,
- const GURL& requesting_frame) {
- if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
- BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- base::Bind(
- &BrowserPluginGeolocationPermissionContext::
- CancelGeolocationPermissionRequest,
- this,
- render_process_id,
- render_view_id,
- bridge_id,
- requesting_frame));
- return;
- }
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- RenderViewHost* rvh = RenderViewHost::FromID(render_process_id,
- render_view_id);
- if (rvh) {
- DCHECK(rvh->GetProcess()->IsGuest());
- WebContentsImpl* guest_web_contents =
- static_cast<WebContentsImpl*>(rvh->GetDelegate()->GetAsWebContents());
- BrowserPluginGuest* guest = guest_web_contents->GetBrowserPluginGuest();
- if (guest)
- guest->CancelGeolocationRequest(bridge_id);
- }
-}
-
-} // namespace content
diff --git a/chromium/content/browser/browser_plugin/browser_plugin_geolocation_permission_context.h b/chromium/content/browser/browser_plugin/browser_plugin_geolocation_permission_context.h
deleted file mode 100644
index 27cc9e3e4a7..00000000000
--- a/chromium/content/browser/browser_plugin/browser_plugin_geolocation_permission_context.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.
-
-#ifndef CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_GEOLOCATION_PERMISSION_CONTEXT_H_
-#define CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_GEOLOCATION_PERMISSION_CONTEXT_H_
-
-#include "content/public/browser/geolocation_permission_context.h"
-
-namespace content {
-
-// Browser plugin specific implementation of GeolocationPermissionContext.
-// It manages Geolocation permissions flow for BrowserPluginGuest. When a guest
-// requests gelocation permission, it delegates the request to embedder though
-// embedder's javascript api.
-// This runs on the I/O thread. We have to return to UI thread to talk to a
-// BrowserPluginGuest.
-class BrowserPluginGeolocationPermissionContext :
- public GeolocationPermissionContext {
- public:
- BrowserPluginGeolocationPermissionContext();
-
- // GeolocationPermissionContext implementation:
- virtual void RequestGeolocationPermission(
- int render_process_id,
- int render_view_id,
- int bridge_id,
- const GURL& requesting_frame,
- base::Callback<void(bool)> callback) OVERRIDE;
- virtual void CancelGeolocationPermissionRequest(
- int render_process_id,
- int render_view_id,
- int bridge_id,
- const GURL& requesting_frame) OVERRIDE;
-
- private:
- virtual ~BrowserPluginGeolocationPermissionContext();
-
- DISALLOW_COPY_AND_ASSIGN(BrowserPluginGeolocationPermissionContext);
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_GEOLOCATION_PERMISSION_CONTEXT_H_
diff --git a/chromium/content/browser/browser_plugin/browser_plugin_guest.cc b/chromium/content/browser/browser_plugin/browser_plugin_guest.cc
index 9668d227c51..b3bd05a6b21 100644
--- a/chromium/content/browser/browser_plugin/browser_plugin_guest.cc
+++ b/chromium/content/browser/browser_plugin/browser_plugin_guest.cc
@@ -7,47 +7,31 @@
#include <algorithm>
#include "base/message_loop/message_loop.h"
-#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "content/browser/browser_plugin/browser_plugin_embedder.h"
-#include "content/browser/browser_plugin/browser_plugin_guest_manager.h"
-#include "content/browser/browser_plugin/browser_plugin_host_factory.h"
#include "content/browser/browser_thread_impl.h"
#include "content/browser/child_process_security_policy_impl.h"
+#include "content/browser/frame_host/render_frame_host_impl.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"
#include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "content/browser/renderer_host/render_widget_host_view_base.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/browser/web_contents/web_contents_view_guest.h"
-#include "content/common/browser_plugin/browser_plugin_constants.h"
#include "content/common/browser_plugin/browser_plugin_messages.h"
#include "content/common/content_constants_internal.h"
#include "content/common/drag_messages.h"
-#include "content/common/gpu/gpu_messages.h"
#include "content/common/input_messages.h"
#include "content/common/view_messages.h"
-#include "content/port/browser/render_view_host_delegate_view.h"
-#include "content/port/browser/render_widget_host_view_port.h"
#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_plugin_guest_manager.h"
#include "content/public/browser/content_browser_client.h"
-#include "content/public/browser/geolocation_permission_context.h"
-#include "content/public/browser/navigation_controller.h"
-#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_widget_host_view.h"
-#include "content/public/browser/resource_request_details.h"
#include "content/public/browser/user_metrics.h"
#include "content/public/browser/web_contents_observer.h"
-#include "content/public/browser/web_contents_view.h"
#include "content/public/common/drop_data.h"
-#include "content/public/common/media_stream_request.h"
-#include "content/public/common/result_codes.h"
-#include "content/public/common/url_constants.h"
-#include "content/public/common/url_utils.h"
-#include "net/url_request/url_request.h"
#include "third_party/WebKit/public/platform/WebCursorInfo.h"
-#include "ui/events/keycodes/keyboard_codes.h"
-#include "ui/surface/transport_dib.h"
-#include "webkit/common/resource_type.h"
#if defined(OS_MACOSX)
#include "content/browser/browser_plugin/browser_plugin_popup_menu_helper_mac.h"
@@ -55,254 +39,6 @@
namespace content {
-// static
-BrowserPluginHostFactory* BrowserPluginGuest::factory_ = NULL;
-
-// Parent class for the various types of permission requests, each of which
-// should be able to handle the response to their permission request.
-class BrowserPluginGuest::PermissionRequest :
- public base::RefCounted<BrowserPluginGuest::PermissionRequest> {
- public:
- virtual void Respond(bool should_allow, const std::string& user_input) = 0;
- virtual bool AllowedByDefault() const {
- return false;
- }
- protected:
- PermissionRequest() {
- RecordAction(UserMetricsAction("BrowserPlugin.Guest.PermissionRequest"));
- }
- virtual ~PermissionRequest() {}
- // Friend RefCounted so that the dtor can be non-public.
- friend class base::RefCounted<BrowserPluginGuest::PermissionRequest>;
-};
-
-class BrowserPluginGuest::DownloadRequest : public PermissionRequest {
- public:
- explicit DownloadRequest(base::Callback<void(bool)> callback)
- : callback_(callback) {
- RecordAction(
- UserMetricsAction("BrowserPlugin.Guest.PermissionRequest.Download"));
- }
- virtual void Respond(bool should_allow,
- const std::string& user_input) OVERRIDE {
- callback_.Run(should_allow);
- }
-
- private:
- virtual ~DownloadRequest() {}
- base::Callback<void(bool)> callback_;
-};
-
-class BrowserPluginGuest::GeolocationRequest : public PermissionRequest {
- public:
- GeolocationRequest(GeolocationCallback callback,
- int bridge_id,
- base::WeakPtrFactory<BrowserPluginGuest>* weak_ptr_factory)
- : callback_(callback),
- bridge_id_(bridge_id),
- weak_ptr_factory_(weak_ptr_factory) {
- RecordAction(
- UserMetricsAction("BrowserPlugin.Guest.PermissionRequest.Geolocation"));
- }
-
- virtual void Respond(bool should_allow,
- const std::string& user_input) OVERRIDE {
- base::WeakPtr<BrowserPluginGuest> guest(weak_ptr_factory_->GetWeakPtr());
-
- WebContents* web_contents = guest->embedder_web_contents();
- if (should_allow && web_contents) {
- // If renderer side embedder decides to allow gelocation, we need to check
- // if the app/embedder itself has geolocation access.
- BrowserContext* browser_context = web_contents->GetBrowserContext();
- if (browser_context) {
- GeolocationPermissionContext* geolocation_context =
- browser_context->GetGeolocationPermissionContext();
- if (geolocation_context) {
- base::Callback<void(bool)> geolocation_callback = base::Bind(
- &BrowserPluginGuest::SetGeolocationPermission,
- guest,
- callback_,
- bridge_id_);
- geolocation_context->RequestGeolocationPermission(
- web_contents->GetRenderProcessHost()->GetID(),
- web_contents->GetRoutingID(),
- // The geolocation permission request here is not initiated
- // through WebGeolocationPermissionRequest. We are only interested
- // in the fact whether the embedder/app has geolocation
- // permission. Therefore we use an invalid |bridge_id|.
- -1 /* bridge_id */,
- web_contents->GetLastCommittedURL(),
- geolocation_callback);
- return;
- }
- }
- }
- guest->SetGeolocationPermission(callback_, bridge_id_, false);
- }
-
- private:
- virtual ~GeolocationRequest() {}
- base::Callback<void(bool)> callback_;
- int bridge_id_;
- base::WeakPtrFactory<BrowserPluginGuest>* weak_ptr_factory_;
-};
-
-class BrowserPluginGuest::MediaRequest : public PermissionRequest {
- public:
- MediaRequest(const MediaStreamRequest& request,
- const MediaResponseCallback& callback,
- BrowserPluginGuest* guest)
- : request_(request),
- callback_(callback),
- guest_(guest) {
- RecordAction(
- UserMetricsAction("BrowserPlugin.Guest.PermissionRequest.Media"));
- }
-
- virtual void Respond(bool should_allow,
- const std::string& user_input) OVERRIDE {
- WebContentsImpl* web_contents = guest_->embedder_web_contents();
- if (should_allow && web_contents) {
- // Re-route the request to the embedder's WebContents; the guest gets the
- // permission this way.
- web_contents->RequestMediaAccessPermission(request_, callback_);
- } else {
- // Deny the request.
- callback_.Run(MediaStreamDevices(), scoped_ptr<MediaStreamUI>());
- }
- }
-
- private:
- virtual ~MediaRequest() {}
- MediaStreamRequest request_;
- MediaResponseCallback callback_;
- BrowserPluginGuest* guest_;
-};
-
-class BrowserPluginGuest::NewWindowRequest : public PermissionRequest {
- public:
- NewWindowRequest(int instance_id, BrowserPluginGuest* guest)
- : instance_id_(instance_id),
- guest_(guest) {
- RecordAction(
- UserMetricsAction("BrowserPlugin.Guest.PermissionRequest.NewWindow"));
- }
-
- virtual void Respond(bool should_allow,
- const std::string& user_input) OVERRIDE {
- int embedder_render_process_id =
- guest_->embedder_web_contents()->GetRenderProcessHost()->GetID();
- BrowserPluginGuest* guest =
- guest_->GetWebContents()->GetBrowserPluginGuestManager()->
- GetGuestByInstanceID(instance_id_, embedder_render_process_id);
- if (!guest) {
- VLOG(0) << "Guest not found. Instance ID: " << instance_id_;
- return;
- }
-
- // If we do not destroy the guest then we allow the new window.
- if (!should_allow)
- guest->Destroy();
- }
-
- private:
- virtual ~NewWindowRequest() {}
- int instance_id_;
- BrowserPluginGuest* guest_;
-};
-
-class BrowserPluginGuest::JavaScriptDialogRequest : public PermissionRequest {
- public:
- JavaScriptDialogRequest(const DialogClosedCallback& callback)
- : callback_(callback) {
- RecordAction(
- UserMetricsAction("BrowserPlugin.Guest.PermissionRequest.JSDialog"));
- }
-
- virtual void Respond(bool should_allow,
- const std::string& user_input) OVERRIDE {
- callback_.Run(should_allow, UTF8ToUTF16(user_input));
- }
-
- private:
- virtual ~JavaScriptDialogRequest() {}
- DialogClosedCallback callback_;
-};
-
-class BrowserPluginGuest::PointerLockRequest : public PermissionRequest {
- public:
- PointerLockRequest(BrowserPluginGuest* guest)
- : guest_(guest) {
- RecordAction(
- UserMetricsAction("BrowserPlugin.Guest.PermissionRequest.PointerLock"));
- }
-
- virtual void Respond(bool should_allow,
- const std::string& user_input) OVERRIDE {
- guest_->SendMessageToEmbedder(
- new BrowserPluginMsg_SetMouseLock(guest_->instance_id(), should_allow));
- }
-
- private:
- virtual ~PointerLockRequest() {}
- BrowserPluginGuest* guest_;
-};
-
-namespace {
-std::string WindowOpenDispositionToString(
- WindowOpenDisposition window_open_disposition) {
- switch (window_open_disposition) {
- case IGNORE_ACTION:
- return "ignore";
- case SAVE_TO_DISK:
- return "save_to_disk";
- case CURRENT_TAB:
- return "current_tab";
- case NEW_BACKGROUND_TAB:
- return "new_background_tab";
- case NEW_FOREGROUND_TAB:
- return "new_foreground_tab";
- case NEW_WINDOW:
- return "new_window";
- case NEW_POPUP:
- return "new_popup";
- default:
- NOTREACHED() << "Unknown Window Open Disposition";
- return "ignore";
- }
-}
-
-std::string JavaScriptMessageTypeToString(JavaScriptMessageType message_type) {
- switch (message_type) {
- case JAVASCRIPT_MESSAGE_TYPE_ALERT:
- return "alert";
- case JAVASCRIPT_MESSAGE_TYPE_CONFIRM:
- return "confirm";
- case JAVASCRIPT_MESSAGE_TYPE_PROMPT:
- return "prompt";
- default:
- NOTREACHED() << "Unknown JavaScript Message Type.";
- return "unknown";
- }
-}
-
-// Called on IO thread.
-static std::string RetrieveDownloadURLFromRequestId(
- RenderViewHost* render_view_host,
- int url_request_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-
- int render_process_id = render_view_host->GetProcess()->GetID();
- GlobalRequestID global_id(render_process_id, url_request_id);
- net::URLRequest* url_request =
- ResourceDispatcherHostImpl::Get()->GetURLRequest(global_id);
- if (url_request)
- return url_request->url().possibly_invalid_spec();
- return "";
-}
-
-} // namespace
-
class BrowserPluginGuest::EmbedderWebContentsObserver
: public WebContentsObserver {
public:
@@ -314,11 +50,7 @@ class BrowserPluginGuest::EmbedderWebContentsObserver
virtual ~EmbedderWebContentsObserver() {
}
- // WebContentsObserver:
- virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE {
- browser_plugin_guest_->EmbedderDestroyed();
- }
-
+ // WebContentsObserver implementation.
virtual void WasShown() OVERRIDE {
browser_plugin_guest_->EmbedderVisibilityChanged(true);
}
@@ -336,179 +68,59 @@ class BrowserPluginGuest::EmbedderWebContentsObserver
BrowserPluginGuest::BrowserPluginGuest(
int instance_id,
bool has_render_view,
- WebContentsImpl* web_contents,
- BrowserPluginGuest* opener)
+ WebContentsImpl* web_contents)
: WebContentsObserver(web_contents),
- weak_ptr_factory_(this),
embedder_web_contents_(NULL),
instance_id_(instance_id),
- damage_buffer_sequence_id_(0),
- damage_buffer_size_(0),
- damage_buffer_scale_factor_(1.0f),
guest_device_scale_factor_(1.0f),
- guest_hang_timeout_(
- base::TimeDelta::FromMilliseconds(kHungRendererDelayMs)),
focused_(false),
mouse_locked_(false),
pending_lock_request_(false),
+ guest_visible_(false),
+ guest_opaque_(true),
embedder_visible_(true),
+ auto_size_enabled_(false),
copy_request_id_(0),
- next_permission_request_id_(browser_plugin::kInvalidPermissionRequestID),
has_render_view_(has_render_view),
last_seen_auto_size_enabled_(false),
- is_in_destruction_(false) {
+ is_in_destruction_(false),
+ last_text_input_type_(ui::TEXT_INPUT_TYPE_NONE),
+ last_input_mode_(ui::TEXT_INPUT_MODE_DEFAULT),
+ last_can_compose_inline_(true),
+ delegate_(NULL),
+ weak_ptr_factory_(this) {
DCHECK(web_contents);
- web_contents->SetDelegate(this);
- if (opener)
- opener_ = opener->AsWeakPtr();
- GetWebContents()->GetBrowserPluginGuestManager()->AddGuest(instance_id_,
- GetWebContents());
-}
-
-bool BrowserPluginGuest::AddMessageToConsole(WebContents* source,
- int32 level,
- const base::string16& message,
- int32 line_no,
- const base::string16& source_id) {
- if (!delegate_)
- return false;
-
- delegate_->AddMessageToConsole(level, message, line_no, source_id);
- return true;
}
-void BrowserPluginGuest::DestroyUnattachedWindows() {
- // Destroy() reaches in and removes the BrowserPluginGuest from its opener's
- // pending_new_windows_ set. To avoid mutating the set while iterating, we
- // create a copy of the pending new windows set and iterate over the copy.
- PendingWindowMap pending_new_windows(pending_new_windows_);
- // Clean up unattached new windows opened by this guest.
- for (PendingWindowMap::const_iterator it = pending_new_windows.begin();
- it != pending_new_windows.end(); ++it) {
- it->first->Destroy();
- }
- // All pending windows should be removed from the set after Destroy() is
- // called on all of them.
- DCHECK(pending_new_windows_.empty());
-}
-
-void BrowserPluginGuest::LoadURLWithParams(const GURL& url,
- const Referrer& referrer,
- PageTransition transition_type,
- WebContents* web_contents) {
- NavigationController::LoadURLParams load_url_params(url);
- load_url_params.referrer = referrer;
- load_url_params.transition_type = transition_type;
- load_url_params.extra_headers = std::string();
- if (delegate_ && delegate_->IsOverridingUserAgent()) {
- load_url_params.override_user_agent =
- NavigationController::UA_OVERRIDE_TRUE;
- }
- web_contents->GetController().LoadURLWithParams(load_url_params);
-}
-
-void BrowserPluginGuest::RespondToPermissionRequest(
- int request_id,
- bool should_allow,
- const std::string& user_input) {
- RequestMap::iterator request_itr = permission_request_map_.find(request_id);
- if (request_itr == permission_request_map_.end()) {
- VLOG(0) << "Not a valid request ID.";
- return;
- }
- request_itr->second->Respond(should_allow, user_input);
- permission_request_map_.erase(request_itr);
+void BrowserPluginGuest::WillDestroy() {
+ is_in_destruction_ = true;
+ embedder_web_contents_ = NULL;
+ delegate_ = NULL;
}
-int BrowserPluginGuest::RequestPermission(
- BrowserPluginPermissionType permission_type,
- scoped_refptr<BrowserPluginGuest::PermissionRequest> request,
- const base::DictionaryValue& request_info) {
- if (!delegate_) {
- request->Respond(false, "");
- return browser_plugin::kInvalidPermissionRequestID;
- }
-
- int request_id = ++next_permission_request_id_;
- permission_request_map_[request_id] = request;
-
- BrowserPluginGuestDelegate::PermissionResponseCallback callback =
- base::Bind(&BrowserPluginGuest::RespondToPermissionRequest,
- AsWeakPtr(),
- request_id);
- // If BrowserPluginGuestDelegate hasn't handled the permission then we simply
- // perform the default action (which is one of allow or reject) immediately.
- if (!delegate_->RequestPermission(
- permission_type, request_info, callback, request->AllowedByDefault())) {
- callback.Run(request->AllowedByDefault(), "");
- return browser_plugin::kInvalidPermissionRequestID;
- }
-
- return request_id;
+base::WeakPtr<BrowserPluginGuest> BrowserPluginGuest::AsWeakPtr() {
+ return weak_ptr_factory_.GetWeakPtr();
}
-BrowserPluginGuest* BrowserPluginGuest::CreateNewGuestWindow(
- const OpenURLParams& params) {
- BrowserPluginGuestManager* guest_manager =
- GetWebContents()->GetBrowserPluginGuestManager();
-
- // Allocate a new instance ID for the new guest.
- int instance_id = guest_manager->get_next_instance_id();
-
- // Set the attach params to use the same partition as the opener.
- // We pull the partition information from the site's URL, which is of the form
- // guest://site/{persist}?{partition_name}.
- const GURL& site_url = GetWebContents()->GetSiteInstance()->GetSiteURL();
- BrowserPluginHostMsg_Attach_Params attach_params;
- attach_params.storage_partition_id = site_url.query();
- attach_params.persist_storage =
- site_url.path().find("persist") != std::string::npos;
-
- // The new guest gets a copy of this guest's extra params so that the content
- // embedder exposes the same API for this guest as its opener.
- scoped_ptr<base::DictionaryValue> extra_params(
- extra_attach_params_->DeepCopy());
- BrowserPluginGuest* new_guest =
- GetWebContents()->GetBrowserPluginGuestManager()->CreateGuest(
- GetWebContents()->GetSiteInstance(), instance_id,
- attach_params, extra_params.Pass());
- new_guest->opener_ = AsWeakPtr();
-
- // Take ownership of |new_guest|.
- pending_new_windows_.insert(
- std::make_pair(new_guest, NewWindowInfo(params.url, std::string())));
-
- // Request permission to show the new window.
- RequestNewWindowPermission(params.disposition, gfx::Rect(),
- params.user_gesture, new_guest->GetWebContents());
-
- return new_guest;
-}
+bool BrowserPluginGuest::LockMouse(bool allowed) {
+ if (!attached() || (mouse_locked_ == allowed))
+ return false;
-void BrowserPluginGuest::EmbedderDestroyed() {
- embedder_web_contents_ = NULL;
- if (delegate_)
- delegate_->EmbedderDestroyed();
- Destroy();
+ return embedder_web_contents()->GotResponseToLockMouseRequest(allowed);
}
void BrowserPluginGuest::Destroy() {
- is_in_destruction_ = true;
- if (!attached() && opener())
- opener()->pending_new_windows_.erase(this);
- DestroyUnattachedWindows();
- GetWebContents()->GetBrowserPluginGuestManager()->RemoveGuest(instance_id_);
- delete GetWebContents();
+ if (!delegate_)
+ return;
+ delegate_->Destroy();
}
bool BrowserPluginGuest::OnMessageReceivedFromEmbedder(
const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(BrowserPluginGuest, message)
- IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_BuffersSwappedACK,
- OnSwapBuffersACK)
- IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_CompositorFrameACK,
- OnCompositorFrameACK)
+ IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_CompositorFrameSwappedACK,
+ OnCompositorFrameSwappedACK)
IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_CopyFromCompositingSurfaceAck,
OnCopyFromCompositingSurfaceAck)
IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_DragStatusUpdate,
@@ -524,22 +136,19 @@ bool BrowserPluginGuest::OnMessageReceivedFromEmbedder(
IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_ImeSetComposition,
OnImeSetComposition)
IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_LockMouse_ACK, OnLockMouseAck)
- IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_NavigateGuest, OnNavigateGuest)
IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_PluginDestroyed, OnPluginDestroyed)
IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_ReclaimCompositorResources,
OnReclaimCompositorResources)
IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_ResizeGuest, OnResizeGuest)
- IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_SetAutoSize, OnSetSize)
+ IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_SetAutoSize, OnSetAutoSize)
IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_SetEditCommandsForNextKeyEvent,
OnSetEditCommandsForNextKeyEvent)
IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_SetFocus, OnSetFocus)
- IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_SetName, OnSetName)
IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_SetContentsOpaque,
OnSetContentsOpaque)
IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_SetVisibility, OnSetVisibility)
IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_UnlockMouse_ACK, OnUnlockMouseAck)
IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_UpdateGeometry, OnUpdateGeometry)
- IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_UpdateRect_ACK, OnUpdateRectACK)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
@@ -547,14 +156,14 @@ bool BrowserPluginGuest::OnMessageReceivedFromEmbedder(
void BrowserPluginGuest::Initialize(
const BrowserPluginHostMsg_Attach_Params& params,
- WebContentsImpl* embedder_web_contents) {
+ WebContentsImpl* embedder_web_contents,
+ const base::DictionaryValue& extra_params) {
focused_ = params.focused;
guest_visible_ = params.visible;
guest_opaque_ = params.opaque;
- guest_window_rect_ = params.resize_guest_params.view_rect;
+ guest_window_rect_ = gfx::Rect(params.origin,
+ params.resize_guest_params.view_size);
- if (!params.name.empty())
- name_ = params.name;
auto_size_enabled_ = params.auto_size_params.enable;
max_auto_size_ = params.auto_size_params.max_size;
min_auto_size_ = params.auto_size_params.min_size;
@@ -591,7 +200,8 @@ void BrowserPluginGuest::Initialize(
embedder_web_contents_observer_.reset(new EmbedderWebContentsObserver(this));
- OnSetSize(instance_id_, params.auto_size_params, params.resize_guest_params);
+ OnSetAutoSize(
+ instance_id_, params.auto_size_params, params.resize_guest_params);
// Create a swapped out RenderView for the guest in the embedder render
// process, so that the embedder can access the guest's window object.
@@ -602,19 +212,9 @@ void BrowserPluginGuest::Initialize(
new BrowserPluginMsg_GuestContentWindowReady(instance_id_,
guest_routing_id));
- if (!params.src.empty()) {
- // params.src will be validated in BrowserPluginGuest::OnNavigateGuest.
- OnNavigateGuest(instance_id_, params.src);
- }
-
- has_render_view_ = true;
-
- if (!embedder_web_contents_->
- GetWebkitPrefs().accelerated_compositing_enabled) {
- WebPreferences prefs = GetWebContents()->GetWebkitPrefs();
- prefs.accelerated_compositing_enabled = false;
- GetWebContents()->GetRenderViewHost()->UpdateWebkitPreferences(prefs);
- }
+ WebPreferences prefs = GetWebContents()->GetWebkitPrefs();
+ prefs.navigate_on_drag_drop = false;
+ GetWebContents()->GetRenderViewHost()->UpdateWebkitPreferences(prefs);
// Enable input method for guest if it's enabled for the embedder.
if (static_cast<RenderViewHostImpl*>(
@@ -624,27 +224,11 @@ void BrowserPluginGuest::Initialize(
guest_rvh->SetInputMethodActive(true);
}
- // Inform the embedder of the guest's information.
- // We pull the partition information from the site's URL, which is of the form
- // guest://site/{persist}?{partition_name}.
- const GURL& site_url = GetWebContents()->GetSiteInstance()->GetSiteURL();
- BrowserPluginMsg_Attach_ACK_Params ack_params;
- ack_params.storage_partition_id = site_url.query();
- ack_params.persist_storage =
- site_url.path().find("persist") != std::string::npos;
- ack_params.name = name_;
- SendMessageToEmbedder(
- new BrowserPluginMsg_Attach_ACK(instance_id_, ack_params));
-
- if (delegate_)
- delegate_->DidAttach();
+ // Inform the embedder of the guest's attachment.
+ SendMessageToEmbedder(new BrowserPluginMsg_Attach_ACK(instance_id_));
}
BrowserPluginGuest::~BrowserPluginGuest() {
- while (!pending_messages_.empty()) {
- delete pending_messages_.front();
- pending_messages_.pop();
- }
}
// static
@@ -652,43 +236,49 @@ BrowserPluginGuest* BrowserPluginGuest::Create(
int instance_id,
SiteInstance* guest_site_instance,
WebContentsImpl* web_contents,
- scoped_ptr<base::DictionaryValue> extra_params) {
- RecordAction(UserMetricsAction("BrowserPlugin.Guest.Create"));
- BrowserPluginGuest* guest = NULL;
- if (factory_) {
- guest = factory_->CreateBrowserPluginGuest(instance_id, web_contents);
- } else {
- guest = new BrowserPluginGuest(instance_id, false, web_contents, NULL);
- }
- guest->extra_attach_params_.reset(extra_params->DeepCopy());
+ scoped_ptr<base::DictionaryValue> extra_params,
+ BrowserPluginGuest* opener) {
+ RecordAction(base::UserMetricsAction("BrowserPlugin.Guest.Create"));
+ BrowserPluginGuest* guest = new BrowserPluginGuest(
+ instance_id, web_contents->opener() != NULL, web_contents);
web_contents->SetBrowserPluginGuest(guest);
+ WebContents* opener_web_contents = NULL;
+ if (opener) {
+ opener_web_contents = opener->GetWebContents();
+ guest_site_instance = opener_web_contents->GetSiteInstance();
+ }
BrowserPluginGuestDelegate* delegate = NULL;
GetContentClient()->browser()->GuestWebContentsCreated(
- guest_site_instance, web_contents, NULL, &delegate, extra_params.Pass());
- guest->SetDelegate(delegate);
+ instance_id,
+ guest_site_instance,
+ web_contents,
+ opener_web_contents,
+ &delegate,
+ extra_params.Pass());
+ if (delegate) {
+ delegate->RegisterDestructionCallback(
+ base::Bind(&BrowserPluginGuest::WillDestroy,
+ base::Unretained(guest)));
+ guest->set_delegate(delegate);
+ }
return guest;
}
// static
-BrowserPluginGuest* BrowserPluginGuest::CreateWithOpener(
- int instance_id,
- bool has_render_view,
- WebContentsImpl* web_contents,
- BrowserPluginGuest* opener) {
- BrowserPluginGuest* guest =
- new BrowserPluginGuest(
- instance_id, has_render_view, web_contents, opener);
- web_contents->SetBrowserPluginGuest(guest);
- BrowserPluginGuestDelegate* delegate = NULL;
- GetContentClient()->browser()->GuestWebContentsCreated(
- opener->GetWebContents()->GetSiteInstance(),
- web_contents, opener->GetWebContents(), &delegate,
- scoped_ptr<base::DictionaryValue>());
- guest->SetDelegate(delegate);
- return guest;
+bool BrowserPluginGuest::IsGuest(WebContentsImpl* web_contents) {
+ return web_contents && web_contents->GetBrowserPluginGuest();
+}
+
+// static
+bool BrowserPluginGuest::IsGuest(RenderViewHostImpl* render_view_host) {
+ return render_view_host && IsGuest(
+ static_cast<WebContentsImpl*>(WebContents::FromRenderViewHost(
+ render_view_host)));
}
RenderWidgetHostView* BrowserPluginGuest::GetEmbedderRenderWidgetHostView() {
+ if (!attached())
+ return NULL;
return embedder_web_contents_->GetRenderWidgetHostView();
}
@@ -706,6 +296,11 @@ void BrowserPluginGuest::CopyFromCompositingSurface(
copy_request_id_, src_subrect, dst_size));
}
+BrowserPluginGuestManager*
+BrowserPluginGuest::GetBrowserPluginGuestManager() const {
+ return GetWebContents()->GetBrowserContext()->GetGuestManager();
+}
+
// screen.
gfx::Rect BrowserPluginGuest::ToGuestRect(const gfx::Rect& bounds) {
gfx::Rect guest_rect(bounds);
@@ -718,196 +313,15 @@ void BrowserPluginGuest::EmbedderVisibilityChanged(bool visible) {
UpdateVisibility();
}
-void BrowserPluginGuest::AddNewContents(WebContents* source,
- WebContents* new_contents,
- WindowOpenDisposition disposition,
- const gfx::Rect& initial_pos,
- bool user_gesture,
- bool* was_blocked) {
- if (was_blocked)
- *was_blocked = false;
- RequestNewWindowPermission(disposition, initial_pos, user_gesture,
- static_cast<WebContentsImpl*>(new_contents));
-}
-
-void BrowserPluginGuest::CanDownload(
- RenderViewHost* render_view_host,
- int request_id,
- const std::string& request_method,
- const base::Callback<void(bool)>& callback) {
- BrowserThread::PostTaskAndReplyWithResult(
- BrowserThread::IO, FROM_HERE,
- base::Bind(&RetrieveDownloadURLFromRequestId,
- render_view_host, request_id),
- base::Bind(&BrowserPluginGuest::DidRetrieveDownloadURLFromRequestId,
- weak_ptr_factory_.GetWeakPtr(),
- request_method,
- callback));
-}
-
-void BrowserPluginGuest::LoadProgressChanged(WebContents* contents,
- double progress) {
- if (delegate_)
- delegate_->LoadProgressed(progress);
-}
-
-void BrowserPluginGuest::CloseContents(WebContents* source) {
- if (!delegate_)
- return;
-
- delegate_->Close();
-}
-
-JavaScriptDialogManager* BrowserPluginGuest::GetJavaScriptDialogManager() {
- return this;
-}
-
-bool BrowserPluginGuest::HandleContextMenu(const ContextMenuParams& params) {
- // TODO(fsamuel): We show the regular page context menu handler for now until
- // we implement the Apps Context Menu API for Browser Plugin (see
- // http://crbug.com/140315).
- return false; // Will be handled by WebContentsViewGuest.
-}
-
-void BrowserPluginGuest::HandleKeyboardEvent(
- WebContents* source,
- const NativeWebKeyboardEvent& event) {
- if (!attached())
- return;
-
- if (UnlockMouseIfNecessary(event))
- return;
-
- if (delegate_ && delegate_->HandleKeyboardEvent(event))
- return;
-
- if (!embedder_web_contents_->GetDelegate())
- return;
-
- // Send the unhandled keyboard events back to the embedder to reprocess them.
- // TODO(fsamuel): This introduces the possibility of out-of-order keyboard
- // events because the guest may be arbitrarily delayed when responding to
- // keyboard events. In that time, the embedder may have received and processed
- // additional key events. This needs to be fixed as soon as possible.
- // See http://crbug.com/229882.
- embedder_web_contents_->GetDelegate()->HandleKeyboardEvent(
- web_contents(), event);
-}
-
-WebContents* BrowserPluginGuest::OpenURLFromTab(WebContents* source,
- const OpenURLParams& params) {
- // If the guest wishes to navigate away prior to attachment then we save the
- // navigation to perform upon attachment. Navigation initializes a lot of
- // state that assumes an embedder exists, such as RenderWidgetHostViewGuest.
- // Navigation also resumes resource loading which we don't want to allow
- // until attachment.
- if (!attached()) {
- PendingWindowMap::iterator it = opener()->pending_new_windows_.find(this);
- if (it == opener()->pending_new_windows_.end())
- return NULL;
- const NewWindowInfo& old_target_url = it->second;
- NewWindowInfo new_window_info(params.url, old_target_url.name);
- new_window_info.changed = new_window_info.url != old_target_url.url;
- it->second = new_window_info;
- return NULL;
- }
- if (params.disposition == CURRENT_TAB) {
- // This can happen for cross-site redirects.
- LoadURLWithParams(params.url, params.referrer, params.transition, source);
- return source;
- }
-
- return CreateNewGuestWindow(params)->GetWebContents();
-}
-
-void BrowserPluginGuest::WebContentsCreated(WebContents* source_contents,
- int64 source_frame_id,
- const base::string16& frame_name,
- const GURL& target_url,
- WebContents* new_contents) {
- WebContentsImpl* new_contents_impl =
- static_cast<WebContentsImpl*>(new_contents);
- BrowserPluginGuest* guest = new_contents_impl->GetBrowserPluginGuest();
- guest->opener_ = AsWeakPtr();
- std::string guest_name = UTF16ToUTF8(frame_name);
- guest->name_ = guest_name;
- // Take ownership of the new guest until it is attached to the embedder's DOM
- // tree to avoid leaking a guest if this guest is destroyed before attaching
- // the new guest.
- pending_new_windows_.insert(
- std::make_pair(guest, NewWindowInfo(target_url, guest_name)));
-}
-
-void BrowserPluginGuest::RendererUnresponsive(WebContents* source) {
- RecordAction(UserMetricsAction("BrowserPlugin.Guest.Hung"));
- if (!delegate_)
- return;
- delegate_->RendererUnresponsive();
-}
-
-void BrowserPluginGuest::RendererResponsive(WebContents* source) {
- RecordAction(UserMetricsAction("BrowserPlugin.Guest.Responsive"));
- if (!delegate_)
- return;
- delegate_->RendererResponsive();
-}
-
-void BrowserPluginGuest::RunFileChooser(WebContents* web_contents,
- const FileChooserParams& params) {
- if (!attached())
- return;
-
- if (!embedder_web_contents_->GetDelegate())
- return;
-
- embedder_web_contents_->GetDelegate()->RunFileChooser(web_contents, params);
-}
-
-bool BrowserPluginGuest::ShouldFocusPageAfterCrash() {
- // Rather than managing focus in WebContentsImpl::RenderViewReady, we will
- // manage the focus ourselves.
- return false;
+void BrowserPluginGuest::PointerLockPermissionResponse(bool allow) {
+ SendMessageToEmbedder(
+ new BrowserPluginMsg_SetMouseLock(instance_id(), allow));
}
-WebContentsImpl* BrowserPluginGuest::GetWebContents() {
+WebContentsImpl* BrowserPluginGuest::GetWebContents() const {
return static_cast<WebContentsImpl*>(web_contents());
}
-base::SharedMemory* BrowserPluginGuest::GetDamageBufferFromEmbedder(
- const BrowserPluginHostMsg_ResizeGuest_Params& params) {
- if (!attached()) {
- LOG(WARNING) << "Attempting to map a damage buffer prior to attachment.";
- return NULL;
- }
-#if defined(OS_WIN)
- base::ProcessHandle handle =
- embedder_web_contents_->GetRenderProcessHost()->GetHandle();
- scoped_ptr<base::SharedMemory> shared_buf(
- new base::SharedMemory(params.damage_buffer_handle, false, handle));
-#elif defined(OS_POSIX)
- scoped_ptr<base::SharedMemory> shared_buf(
- new base::SharedMemory(params.damage_buffer_handle, false));
-#endif
- if (!shared_buf->Map(params.damage_buffer_size)) {
- LOG(WARNING) << "Unable to map the embedder's damage buffer.";
- return NULL;
- }
- return shared_buf.release();
-}
-
-void BrowserPluginGuest::SetDamageBuffer(
- const BrowserPluginHostMsg_ResizeGuest_Params& params) {
- damage_buffer_.reset(GetDamageBufferFromEmbedder(params));
- // Sanity check: Verify that we've correctly shared the damage buffer memory
- // between the embedder and browser processes.
- DCHECK(!damage_buffer_ ||
- *static_cast<unsigned int*>(damage_buffer_->memory()) == 0xdeadbeef);
- damage_buffer_sequence_id_ = params.damage_buffer_sequence_id;
- damage_buffer_size_ = params.damage_buffer_size;
- damage_view_size_ = params.view_rect.size();
- damage_buffer_scale_factor_ = params.scale_factor;
-}
-
gfx::Point BrowserPluginGuest::GetScreenCoordinates(
const gfx::Point& relative_position) const {
gfx::Point screen_pos(relative_position);
@@ -920,46 +334,6 @@ bool BrowserPluginGuest::InAutoSizeBounds(const gfx::Size& size) const {
size.height() <= max_auto_size_.height();
}
-void BrowserPluginGuest::RequestNewWindowPermission(
- WindowOpenDisposition disposition,
- const gfx::Rect& initial_bounds,
- bool user_gesture,
- WebContentsImpl* new_contents) {
- BrowserPluginGuest* guest = new_contents->GetBrowserPluginGuest();
- PendingWindowMap::iterator it = pending_new_windows_.find(guest);
- if (it == pending_new_windows_.end())
- return;
- const NewWindowInfo& new_window_info = it->second;
-
- base::DictionaryValue request_info;
- request_info.Set(browser_plugin::kInitialHeight,
- base::Value::CreateIntegerValue(initial_bounds.height()));
- request_info.Set(browser_plugin::kInitialWidth,
- base::Value::CreateIntegerValue(initial_bounds.width()));
- request_info.Set(browser_plugin::kTargetURL,
- base::Value::CreateStringValue(new_window_info.url.spec()));
- request_info.Set(browser_plugin::kName,
- base::Value::CreateStringValue(new_window_info.name));
- request_info.Set(browser_plugin::kWindowID,
- base::Value::CreateIntegerValue(guest->instance_id()));
- request_info.Set(browser_plugin::kWindowOpenDisposition,
- base::Value::CreateStringValue(
- WindowOpenDispositionToString(disposition)));
-
- RequestPermission(BROWSER_PLUGIN_PERMISSION_TYPE_NEW_WINDOW,
- new NewWindowRequest(guest->instance_id(), this),
- request_info);
-}
-
-bool BrowserPluginGuest::UnlockMouseIfNecessary(
- const NativeWebKeyboardEvent& event) {
- if (!mouse_locked_)
- return false;
-
- embedder_web_contents()->GotResponseToLockMouseRequest(false);
- return true;
-}
-
void BrowserPluginGuest::SendMessageToEmbedder(IPC::Message* msg) {
if (!attached()) {
// Some pages such as data URLs, javascript URLs, and about:blank
@@ -967,7 +341,7 @@ void BrowserPluginGuest::SendMessageToEmbedder(IPC::Message* msg) {
// As a result, we must save all these IPCs until attachment and then
// forward them so that the embedder gets a chance to see and process
// the load events.
- pending_messages_.push(msg);
+ pending_messages_.push_back(linked_ptr<IPC::Message>(msg));
return;
}
msg->set_routing_id(embedder_web_contents_->GetRoutingID());
@@ -980,71 +354,10 @@ void BrowserPluginGuest::DragSourceEndedAt(int client_x, int client_y,
screen_x, screen_y, operation);
}
-void BrowserPluginGuest::DragSourceMovedTo(int client_x, int client_y,
- int screen_x, int screen_y) {
- web_contents()->GetRenderViewHost()->DragSourceMovedTo(client_x, client_y,
- screen_x, screen_y);
-}
-
void BrowserPluginGuest::EndSystemDrag() {
RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>(
GetWebContents()->GetRenderViewHost());
guest_rvh->DragSourceSystemDragEnded();
- // Issue a MouseUp event to get out of a selection state.
- blink::WebMouseEvent mouse_event;
- mouse_event.type = blink::WebInputEvent::MouseUp;
- mouse_event.button = blink::WebMouseEvent::ButtonLeft;
- guest_rvh->ForwardMouseEvent(mouse_event);
-}
-
-void BrowserPluginGuest::SetDelegate(BrowserPluginGuestDelegate* delegate) {
- DCHECK(!delegate_);
- delegate_.reset(delegate);
-}
-
-void BrowserPluginGuest::AskEmbedderForGeolocationPermission(
- int bridge_id,
- const GURL& requesting_frame,
- const GeolocationCallback& callback) {
- base::DictionaryValue request_info;
- request_info.Set(browser_plugin::kURL,
- base::Value::CreateStringValue(requesting_frame.spec()));
-
- int request_id =
- RequestPermission(BROWSER_PLUGIN_PERMISSION_TYPE_GEOLOCATION,
- new GeolocationRequest(
- callback, bridge_id, &weak_ptr_factory_),
- request_info);
-
- DCHECK(bridge_id_to_request_id_map_.find(bridge_id) ==
- bridge_id_to_request_id_map_.end());
- bridge_id_to_request_id_map_[bridge_id] = request_id;
-}
-
-int BrowserPluginGuest::RemoveBridgeID(int bridge_id) {
- std::map<int, int>::iterator bridge_itr =
- bridge_id_to_request_id_map_.find(bridge_id);
- if (bridge_itr == bridge_id_to_request_id_map_.end())
- return browser_plugin::kInvalidPermissionRequestID;
-
- int request_id = bridge_itr->second;
- bridge_id_to_request_id_map_.erase(bridge_itr);
- return request_id;
-}
-
-void BrowserPluginGuest::CancelGeolocationRequest(int bridge_id) {
- int request_id = RemoveBridgeID(bridge_id);
- RequestMap::iterator request_itr = permission_request_map_.find(request_id);
- if (request_itr == permission_request_map_.end())
- return;
- permission_request_map_.erase(request_itr);
-}
-
-void BrowserPluginGuest::SetGeolocationPermission(GeolocationCallback callback,
- int bridge_id,
- bool allowed) {
- callback.Run(allowed);
- RemoveBridgeID(bridge_id);
}
void BrowserPluginGuest::SendQueuedMessages() {
@@ -1052,9 +365,9 @@ void BrowserPluginGuest::SendQueuedMessages() {
return;
while (!pending_messages_.empty()) {
- IPC::Message* message = pending_messages_.front();
- pending_messages_.pop();
- SendMessageToEmbedder(message);
+ linked_ptr<IPC::Message> message_ptr = pending_messages_.front();
+ pending_messages_.pop_front();
+ SendMessageToEmbedder(message_ptr.release());
}
}
@@ -1065,27 +378,11 @@ void BrowserPluginGuest::DidCommitProvisionalLoadForFrame(
const GURL& url,
PageTransition transition_type,
RenderViewHost* render_view_host) {
- RecordAction(UserMetricsAction("BrowserPlugin.Guest.DidNavigate"));
-}
-
-void BrowserPluginGuest::DidStopLoading(RenderViewHost* render_view_host) {
- bool enable_dragdrop = delegate_ && delegate_->IsDragAndDropEnabled();
- if (!enable_dragdrop) {
- // Initiating a drag from inside a guest is currently not supported without
- // the kEnableBrowserPluginDragDrop flag on a linux platform. So inject some
- // JS to disable it. http://crbug.com/161112
- const char script[] = "window.addEventListener('dragstart', function() { "
- " window.event.preventDefault(); "
- "});";
- render_view_host->ExecuteJavascriptInWebFrame(base::string16(),
- ASCIIToUTF16(script));
- }
+ RecordAction(base::UserMetricsAction("BrowserPlugin.Guest.DidNavigate"));
}
void BrowserPluginGuest::RenderViewReady() {
RenderViewHost* rvh = GetWebContents()->GetRenderViewHost();
- // The guest RenderView should always live in a guest process.
- CHECK(rvh->GetProcess()->IsGuest());
// TODO(fsamuel): Investigate whether it's possible to update state earlier
// here (see http://crbug.com/158151).
Send(new InputMsg_SetFocus(routing_id(), focused_));
@@ -1093,56 +390,37 @@ void BrowserPluginGuest::RenderViewReady() {
if (auto_size_enabled_)
rvh->EnableAutoResize(min_auto_size_, max_auto_size_);
else
- rvh->DisableAutoResize(damage_view_size_);
+ rvh->DisableAutoResize(full_size_);
- Send(new ViewMsg_SetName(routing_id(), name_));
OnSetContentsOpaque(instance_id_, guest_opaque_);
- RenderWidgetHostImpl::From(rvh)->
- set_hung_renderer_delay_ms(guest_hang_timeout_);
+ RenderWidgetHostImpl::From(rvh)->set_hung_renderer_delay_ms(
+ base::TimeDelta::FromMilliseconds(kHungRendererDelayMs));
}
void BrowserPluginGuest::RenderProcessGone(base::TerminationStatus status) {
SendMessageToEmbedder(new BrowserPluginMsg_GuestGone(instance_id()));
switch (status) {
case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
- RecordAction(UserMetricsAction("BrowserPlugin.Guest.Killed"));
+ RecordAction(base::UserMetricsAction("BrowserPlugin.Guest.Killed"));
break;
case base::TERMINATION_STATUS_PROCESS_CRASHED:
- RecordAction(UserMetricsAction("BrowserPlugin.Guest.Crashed"));
+ RecordAction(base::UserMetricsAction("BrowserPlugin.Guest.Crashed"));
break;
case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
- RecordAction(UserMetricsAction("BrowserPlugin.Guest.AbnormalDeath"));
+ RecordAction(
+ base::UserMetricsAction("BrowserPlugin.Guest.AbnormalDeath"));
break;
default:
break;
}
- // TODO(fsamuel): Consider whether we should be clearing
- // |permission_request_map_| here.
- if (delegate_)
- delegate_->GuestProcessGone(status);
-}
-
-// static
-void BrowserPluginGuest::AcknowledgeBufferPresent(
- int route_id,
- int gpu_host_id,
- const std::string& mailbox_name,
- uint32 sync_point) {
- AcceleratedSurfaceMsg_BufferPresented_Params ack_params;
- ack_params.mailbox_name = mailbox_name;
- ack_params.sync_point = sync_point;
- RenderWidgetHostImpl::AcknowledgeBufferPresent(route_id,
- gpu_host_id,
- ack_params);
}
// static
bool BrowserPluginGuest::ShouldForwardToBrowserPluginGuest(
const IPC::Message& message) {
switch (message.type()) {
- case BrowserPluginHostMsg_BuffersSwappedACK::ID:
- case BrowserPluginHostMsg_CompositorFrameACK::ID:
+ case BrowserPluginHostMsg_CompositorFrameSwappedACK::ID:
case BrowserPluginHostMsg_CopyFromCompositingSurfaceAck::ID:
case BrowserPluginHostMsg_DragStatusUpdate::ID:
case BrowserPluginHostMsg_ExecuteEditCommand::ID:
@@ -1151,19 +429,16 @@ bool BrowserPluginGuest::ShouldForwardToBrowserPluginGuest(
case BrowserPluginHostMsg_ImeConfirmComposition::ID:
case BrowserPluginHostMsg_ImeSetComposition::ID:
case BrowserPluginHostMsg_LockMouse_ACK::ID:
- case BrowserPluginHostMsg_NavigateGuest::ID:
case BrowserPluginHostMsg_PluginDestroyed::ID:
case BrowserPluginHostMsg_ReclaimCompositorResources::ID:
case BrowserPluginHostMsg_ResizeGuest::ID:
case BrowserPluginHostMsg_SetAutoSize::ID:
case BrowserPluginHostMsg_SetEditCommandsForNextKeyEvent::ID:
case BrowserPluginHostMsg_SetFocus::ID:
- case BrowserPluginHostMsg_SetName::ID:
case BrowserPluginHostMsg_SetContentsOpaque::ID:
case BrowserPluginHostMsg_SetVisibility::ID:
case BrowserPluginHostMsg_UnlockMouse_ACK::ID:
case BrowserPluginHostMsg_UpdateGeometry::ID:
- case BrowserPluginHostMsg_UpdateRect_ACK::ID:
return true;
default:
return false;
@@ -1185,16 +460,15 @@ bool BrowserPluginGuest::OnMessageReceived(const IPC::Message& message) {
#endif
IPC_MESSAGE_HANDLER(ViewHostMsg_ShowWidget, OnShowWidget)
IPC_MESSAGE_HANDLER(ViewHostMsg_TakeFocus, OnTakeFocus)
- IPC_MESSAGE_HANDLER(ViewHostMsg_TextInputTypeChanged,
- OnTextInputTypeChanged)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_TextInputStateChanged,
+ OnTextInputStateChanged)
IPC_MESSAGE_HANDLER(ViewHostMsg_ImeCancelComposition,
OnImeCancelComposition)
-#if defined(OS_MACOSX) || defined(OS_WIN) || defined(USE_AURA)
+#if defined(OS_MACOSX) || defined(USE_AURA)
IPC_MESSAGE_HANDLER(ViewHostMsg_ImeCompositionRangeChanged,
OnImeCompositionRangeChanged)
#endif
IPC_MESSAGE_HANDLER(ViewHostMsg_UnlockMouse, OnUnlockMouse)
- IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateFrameName, OnUpdateFrameName)
IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateRect, OnUpdateRect)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
@@ -1203,17 +477,13 @@ bool BrowserPluginGuest::OnMessageReceived(const IPC::Message& message) {
void BrowserPluginGuest::Attach(
WebContentsImpl* embedder_web_contents,
- BrowserPluginHostMsg_Attach_Params params,
+ const BrowserPluginHostMsg_Attach_Params& params,
const base::DictionaryValue& extra_params) {
if (attached())
return;
- extra_attach_params_.reset(extra_params.DeepCopy());
-
- // Clear parameters that get inherited from the opener.
- params.storage_partition_id.clear();
- params.persist_storage = false;
- params.src.clear();
+ if (delegate_)
+ delegate_->WillAttach(embedder_web_contents, extra_params);
// 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
@@ -1226,46 +496,23 @@ void BrowserPluginGuest::Attach(
new_view->CreateViewForWidget(web_contents()->GetRenderViewHost());
}
- // We need to do a navigation here if the target URL has changed between
- // the time the WebContents was created and the time it was attached.
- // We also need to do an initial navigation if a RenderView was never
- // created for the new window in cases where there is no referrer.
- PendingWindowMap::iterator it = opener()->pending_new_windows_.find(this);
- if (it != opener()->pending_new_windows_.end()) {
- const NewWindowInfo& new_window_info = it->second;
- if (new_window_info.changed || !has_render_view_)
- params.src = it->second.url.spec();
- } else {
- NOTREACHED();
- }
-
- // Once a new guest is attached to the DOM of the embedder page, then the
- // lifetime of the new guest is no longer managed by the opener guest.
- opener()->pending_new_windows_.erase(this);
-
- // The guest's frame name takes precedence over the BrowserPlugin's name.
- // The guest's frame name is assigned in
- // BrowserPluginGuest::WebContentsCreated.
- if (!name_.empty())
- params.name.clear();
-
- Initialize(params, embedder_web_contents);
+ Initialize(params, embedder_web_contents, extra_params);
SendQueuedMessages();
- RecordAction(UserMetricsAction("BrowserPlugin.Guest.Attached"));
+ if (delegate_)
+ delegate_->DidAttach();
+
+ RecordAction(base::UserMetricsAction("BrowserPlugin.Guest.Attached"));
}
-void BrowserPluginGuest::OnCompositorFrameACK(
+void BrowserPluginGuest::OnCompositorFrameSwappedACK(
int instance_id,
- int route_id,
- uint32 output_surface_id,
- int renderer_host_id,
- const cc::CompositorFrameAck& ack) {
- RenderWidgetHostImpl::SendSwapCompositorFrameAck(route_id,
- output_surface_id,
- renderer_host_id,
- ack);
+ const FrameHostMsg_CompositorFrameSwappedACK_Params& params) {
+ RenderWidgetHostImpl::SendSwapCompositorFrameAck(params.producing_route_id,
+ params.output_surface_id,
+ params.producing_host_id,
+ params.ack);
}
void BrowserPluginGuest::OnDragStatusUpdate(int instance_id,
@@ -1308,7 +555,7 @@ void BrowserPluginGuest::OnImeSetComposition(
int selection_start,
int selection_end) {
Send(new ViewMsg_ImeSetComposition(routing_id(),
- UTF8ToUTF16(text), underlines,
+ base::UTF8ToUTF16(text), underlines,
selection_start, selection_end));
}
@@ -1317,7 +564,7 @@ void BrowserPluginGuest::OnImeConfirmComposition(
const std::string& text,
bool keep_selection) {
Send(new ViewMsg_ImeConfirmComposition(routing_id(),
- UTF8ToUTF16(text),
+ base::UTF8ToUTF16(text),
gfx::Range::InvalidRange(),
keep_selection));
}
@@ -1326,19 +573,19 @@ void BrowserPluginGuest::OnExtendSelectionAndDelete(
int instance_id,
int before,
int after) {
- Send(new ViewMsg_ExtendSelectionAndDelete(routing_id(), before, after));
+ RenderFrameHostImpl* rfh = static_cast<RenderFrameHostImpl*>(
+ web_contents()->GetFocusedFrame());
+ if (rfh)
+ rfh->ExtendSelectionAndDelete(before, after);
}
void BrowserPluginGuest::OnReclaimCompositorResources(
int instance_id,
- int route_id,
- uint32 output_surface_id,
- int renderer_host_id,
- const cc::CompositorFrameAck& ack) {
- RenderWidgetHostImpl::SendReclaimCompositorResources(route_id,
- output_surface_id,
- renderer_host_id,
- ack);
+ const FrameHostMsg_ReclaimCompositorResources_Params& params) {
+ RenderWidgetHostImpl::SendReclaimCompositorResources(params.route_id,
+ params.output_surface_id,
+ params.renderer_host_id,
+ params.ack);
}
void BrowserPluginGuest::OnHandleInputEvent(
@@ -1405,19 +652,17 @@ void BrowserPluginGuest::OnLockMouse(bool user_gesture,
Send(new ViewMsg_LockMouse_ACK(routing_id(), false));
return;
}
+
+ if (!delegate_)
+ return;
+
pending_lock_request_ = true;
- base::DictionaryValue request_info;
- request_info.Set(browser_plugin::kUserGesture,
- base::Value::CreateBooleanValue(user_gesture));
- request_info.Set(browser_plugin::kLastUnlockedBySelf,
- base::Value::CreateBooleanValue(last_unlocked_by_target));
- request_info.Set(browser_plugin::kURL,
- base::Value::CreateStringValue(
- web_contents()->GetLastCommittedURL().spec()));
-
- RequestPermission(BROWSER_PLUGIN_PERMISSION_TYPE_POINTER_LOCK,
- new PointerLockRequest(this),
- request_info);
+
+ delegate_->RequestPointerLockPermission(
+ user_gesture,
+ last_unlocked_by_target,
+ base::Bind(&BrowserPluginGuest::PointerLockPermissionResponse,
+ weak_ptr_factory_.GetWeakPtr()));
}
void BrowserPluginGuest::OnLockMouseAck(int instance_id, bool succeeded) {
@@ -1427,50 +672,6 @@ void BrowserPluginGuest::OnLockMouseAck(int instance_id, bool succeeded) {
mouse_locked_ = true;
}
-void BrowserPluginGuest::OnNavigateGuest(
- int instance_id,
- const std::string& src) {
- GURL url = delegate_ ? delegate_->ResolveURL(src) : GURL(src);
- // We do not load empty urls in web_contents.
- // If a guest sets empty src attribute after it has navigated to some
- // non-empty page, the action is considered no-op. This empty src navigation
- // should never be sent to BrowserPluginGuest (browser process).
- DCHECK(!src.empty());
- if (src.empty())
- return;
-
- // Do not allow navigating a guest to schemes other than known safe schemes.
- // This will block the embedder trying to load unwanted schemes, e.g.
- // chrome://settings.
- bool scheme_is_blocked =
- (!ChildProcessSecurityPolicyImpl::GetInstance()->IsWebSafeScheme(
- url.scheme()) &&
- !ChildProcessSecurityPolicyImpl::GetInstance()->IsPseudoScheme(
- url.scheme())) ||
- url.SchemeIs(kJavaScriptScheme);
- if (scheme_is_blocked || !url.is_valid()) {
- if (delegate_) {
- std::string error_type;
- base::RemoveChars(net::ErrorToString(net::ERR_ABORTED), "net::",
- &error_type);
- delegate_->LoadAbort(true /* is_top_level */, url, error_type);
- }
- return;
- }
-
- GURL validated_url(url);
- RenderViewHost::FilterURL(
- GetWebContents()->GetRenderProcessHost(),
- false,
- &validated_url);
- // As guests do not swap processes on navigation, only navigations to
- // normal web URLs are supported. No protocol handlers are installed for
- // other schemes (e.g., WebUI or extensions), and no permissions or bindings
- // can be granted to the guest process.
- LoadURLWithParams(validated_url, Referrer(), PAGE_TRANSITION_AUTO_TOPLEVEL,
- GetWebContents());
-}
-
void BrowserPluginGuest::OnPluginDestroyed(int instance_id) {
Destroy();
}
@@ -1497,37 +698,37 @@ void BrowserPluginGuest::OnResizeGuest(
// When autosize is turned off and as a result there is a layout change, we
// send a sizechanged event.
if (!auto_size_enabled_ && last_seen_auto_size_enabled_ &&
- !params.view_rect.size().IsEmpty() && delegate_) {
- delegate_->SizeChanged(last_seen_view_size_, params.view_rect.size());
+ !params.view_size.IsEmpty() && delegate_) {
+ delegate_->SizeChanged(last_seen_view_size_, params.view_size);
last_seen_auto_size_enabled_ = false;
}
- // Invalid damage buffer means we are in HW compositing mode,
- // so just resize the WebContents and repaint if needed.
- if (base::SharedMemory::IsHandleValid(params.damage_buffer_handle))
- SetDamageBuffer(params);
- if (!params.view_rect.size().IsEmpty())
- GetWebContents()->GetView()->SizeContents(params.view_rect.size());
+ // Just resize the WebContents and repaint if needed.
+ full_size_ = params.view_size;
+ if (!params.view_size.IsEmpty())
+ GetWebContents()->GetView()->SizeContents(params.view_size);
if (params.repaint)
- Send(new ViewMsg_Repaint(routing_id(), params.view_rect.size()));
+ Send(new ViewMsg_Repaint(routing_id(), params.view_size));
}
void BrowserPluginGuest::OnSetFocus(int instance_id, bool focused) {
- if (focused_ == focused)
- return;
focused_ = focused;
Send(new InputMsg_SetFocus(routing_id(), focused));
if (!focused && mouse_locked_)
OnUnlockMouse();
-}
-void BrowserPluginGuest::OnSetName(int instance_id, const std::string& name) {
- if (name == name_)
- return;
- name_ = name;
- Send(new ViewMsg_SetName(routing_id(), name));
+ // Restore the last seen state of text input to the view.
+ RenderWidgetHostViewBase* rwhv = static_cast<RenderWidgetHostViewBase*>(
+ web_contents()->GetRenderWidgetHostView());
+ if (rwhv) {
+ ViewHostMsg_TextInputState_Params params;
+ params.type = last_text_input_type_;
+ params.mode = last_input_mode_;
+ params.can_compose_inline = last_can_compose_inline_;
+ rwhv->TextInputStateChanged(params);
+ }
}
-void BrowserPluginGuest::OnSetSize(
+void BrowserPluginGuest::OnSetAutoSize(
int instance_id,
const BrowserPluginHostMsg_AutoSize_Params& auto_size_params,
const BrowserPluginHostMsg_ResizeGuest_Params& resize_guest_params) {
@@ -1540,20 +741,20 @@ void BrowserPluginGuest::OnSetSize(
if (auto_size_enabled_ && (!old_auto_size_enabled ||
(old_max_size != max_auto_size_) ||
(old_min_size != min_auto_size_))) {
- RecordAction(UserMetricsAction("BrowserPlugin.Guest.EnableAutoResize"));
+ RecordAction(
+ base::UserMetricsAction("BrowserPlugin.Guest.EnableAutoResize"));
GetWebContents()->GetRenderViewHost()->EnableAutoResize(
min_auto_size_, max_auto_size_);
// TODO(fsamuel): If we're changing autosize parameters, then we force
- // the guest to completely repaint itself, because BrowserPlugin has
- // allocated a new damage buffer and expects a full frame of pixels.
- // Ideally, we shouldn't need to do this because we shouldn't need to
- // allocate a new damage buffer unless |max_auto_size_| has changed.
+ // the guest to completely repaint itself.
+ // Ideally, we shouldn't need to do this unless |max_auto_size_| has
+ // changed.
// However, even in that case, layout may not change and so we may
// not get a full frame worth of pixels.
Send(new ViewMsg_Repaint(routing_id(), max_auto_size_));
} else if (!auto_size_enabled_ && old_auto_size_enabled) {
GetWebContents()->GetRenderViewHost()->DisableAutoResize(
- resize_guest_params.view_rect.size());
+ resize_guest_params.view_size);
}
OnResizeGuest(instance_id_, resize_guest_params);
}
@@ -1567,14 +768,7 @@ void BrowserPluginGuest::OnSetEditCommandsForNextKeyEvent(
void BrowserPluginGuest::OnSetContentsOpaque(int instance_id, bool opaque) {
guest_opaque_ = opaque;
-
- SkBitmap background;
- if (!guest_opaque_) {
- background.setConfig(SkBitmap::kARGB_8888_Config, 1, 1);
- unsigned int color = 0;
- background.setPixels(&color);
- }
- Send(new ViewMsg_SetBackground(routing_id(), background));
+ Send(new ViewMsg_SetBackgroundOpaque(routing_id(), guest_opaque_));
}
void BrowserPluginGuest::OnSetVisibility(int instance_id, bool visible) {
@@ -1585,22 +779,6 @@ void BrowserPluginGuest::OnSetVisibility(int instance_id, bool visible) {
GetWebContents()->WasHidden();
}
-void BrowserPluginGuest::OnSwapBuffersACK(int instance_id,
- int route_id,
- int gpu_host_id,
- const std::string& mailbox_name,
- uint32 sync_point) {
- AcknowledgeBufferPresent(route_id, gpu_host_id, mailbox_name, sync_point);
-
-// This is only relevant on MACOSX and WIN when threaded compositing
-// is not enabled. In threaded mode, above ACK is sufficient.
-#if defined(OS_MACOSX) || defined(OS_WIN)
- RenderWidgetHostImpl* render_widget_host =
- RenderWidgetHostImpl::From(GetWebContents()->GetRenderViewHost());
- render_widget_host->AcknowledgeSwapBuffersToRenderer();
-#endif // defined(OS_MACOSX) || defined(OS_WIN)
-}
-
void BrowserPluginGuest::OnUnlockMouse() {
SendMessageToEmbedder(
new BrowserPluginMsg_SetMouseLock(instance_id(), false));
@@ -1615,17 +793,6 @@ void BrowserPluginGuest::OnUnlockMouseAck(int instance_id) {
mouse_locked_ = false;
}
-void BrowserPluginGuest::OnUpdateRectACK(
- int instance_id,
- bool needs_ack,
- const BrowserPluginHostMsg_AutoSize_Params& auto_size_params,
- const BrowserPluginHostMsg_ResizeGuest_Params& resize_guest_params) {
- // Only the software path expects an ACK.
- if (needs_ack)
- Send(new ViewMsg_UpdateRect_ACK(routing_id()));
- OnSetSize(instance_id_, auto_size_params, resize_guest_params);
-}
-
void BrowserPluginGuest::OnCopyFromCompositingSurfaceAck(
int instance_id,
int request_id,
@@ -1686,83 +853,6 @@ void BrowserPluginGuest::OnTakeFocus(bool reverse) {
new BrowserPluginMsg_AdvanceFocus(instance_id(), reverse));
}
-void BrowserPluginGuest::OnUpdateFrameName(int frame_id,
- bool is_top_level,
- const std::string& name) {
- if (!is_top_level)
- return;
-
- name_ = name;
- SendMessageToEmbedder(new BrowserPluginMsg_UpdatedName(instance_id_, name));
-}
-
-void BrowserPluginGuest::RequestMediaAccessPermission(
- WebContents* web_contents,
- const MediaStreamRequest& request,
- const MediaResponseCallback& callback) {
- base::DictionaryValue request_info;
- request_info.Set(
- browser_plugin::kURL,
- base::Value::CreateStringValue(request.security_origin.spec()));
-
- RequestPermission(BROWSER_PLUGIN_PERMISSION_TYPE_MEDIA,
- new MediaRequest(request, callback, this),
- request_info);
-}
-
-void BrowserPluginGuest::RunJavaScriptDialog(
- WebContents* web_contents,
- const GURL& origin_url,
- const std::string& accept_lang,
- JavaScriptMessageType javascript_message_type,
- const base::string16& message_text,
- const base::string16& default_prompt_text,
- const DialogClosedCallback& callback,
- bool* did_suppress_message) {
- base::DictionaryValue request_info;
- request_info.Set(
- browser_plugin::kDefaultPromptText,
- base::Value::CreateStringValue(UTF16ToUTF8(default_prompt_text)));
- request_info.Set(
- browser_plugin::kMessageText,
- base::Value::CreateStringValue(UTF16ToUTF8(message_text)));
- request_info.Set(
- browser_plugin::kMessageType,
- base::Value::CreateStringValue(
- JavaScriptMessageTypeToString(javascript_message_type)));
- request_info.Set(
- browser_plugin::kURL,
- base::Value::CreateStringValue(origin_url.spec()));
-
- RequestPermission(BROWSER_PLUGIN_PERMISSION_TYPE_JAVASCRIPT_DIALOG,
- new JavaScriptDialogRequest(callback),
- request_info);
-}
-
-void BrowserPluginGuest::RunBeforeUnloadDialog(
- WebContents* web_contents,
- const base::string16& message_text,
- bool is_reload,
- const DialogClosedCallback& callback) {
- // This is called if the guest has a beforeunload event handler.
- // This callback allows navigation to proceed.
- callback.Run(true, base::string16());
-}
-
-bool BrowserPluginGuest::HandleJavaScriptDialog(
- WebContents* web_contents,
- bool accept,
- const base::string16* prompt_override) {
- return false;
-}
-
-void BrowserPluginGuest::CancelActiveAndPendingDialogs(
- WebContents* web_contents) {
-}
-
-void BrowserPluginGuest::WebContentsDestroyed(WebContents* web_contents) {
-}
-
void BrowserPluginGuest::OnUpdateRect(
const ViewHostMsg_UpdateRect_Params& params) {
BrowserPluginMsg_UpdateRect_Params relay_params;
@@ -1770,7 +860,6 @@ void BrowserPluginGuest::OnUpdateRect(
relay_params.scale_factor = params.scale_factor;
relay_params.is_resize_ack = ViewHostMsg_UpdateRect_Flags::is_resize_ack(
params.flags);
- relay_params.needs_ack = params.needs_ack;
bool size_changed = last_seen_view_size_ != params.view_size;
gfx::Size old_size = last_seen_view_size_;
@@ -1782,91 +871,34 @@ void BrowserPluginGuest::OnUpdateRect(
}
last_seen_auto_size_enabled_ = auto_size_enabled_;
- // HW accelerated case, acknowledge resize only
- if (!params.needs_ack || !damage_buffer_) {
- relay_params.damage_buffer_sequence_id = 0;
- SendMessageToEmbedder(
- new BrowserPluginMsg_UpdateRect(instance_id(), relay_params));
- return;
- }
-
- // Only copy damage if the guest is in autosize mode and the guest's view size
- // is less than the maximum size or the guest's view size is equal to the
- // damage buffer's size and the guest's scale factor is equal to the damage
- // buffer's scale factor.
- // The scaling change can happen due to asynchronous updates of the DPI on a
- // resolution change.
- if (((auto_size_enabled_ && InAutoSizeBounds(params.view_size)) ||
- (params.view_size == damage_view_size())) &&
- params.scale_factor == damage_buffer_scale_factor()) {
- TransportDIB* dib = GetWebContents()->GetRenderProcessHost()->
- GetTransportDIB(params.bitmap);
- if (dib) {
- size_t guest_damage_buffer_size =
-#if defined(OS_WIN)
- params.bitmap_rect.width() *
- params.bitmap_rect.height() * 4;
-#else
- dib->size();
-#endif
- size_t embedder_damage_buffer_size = damage_buffer_size_;
- void* guest_memory = dib->memory();
- void* embedder_memory = damage_buffer_->memory();
- size_t size = std::min(guest_damage_buffer_size,
- embedder_damage_buffer_size);
- memcpy(embedder_memory, guest_memory, size);
- }
- }
- relay_params.damage_buffer_sequence_id = damage_buffer_sequence_id_;
- relay_params.bitmap_rect = params.bitmap_rect;
- relay_params.scroll_delta = params.scroll_delta;
- relay_params.scroll_rect = params.scroll_rect;
- relay_params.copy_rects = params.copy_rects;
-
SendMessageToEmbedder(
new BrowserPluginMsg_UpdateRect(instance_id(), relay_params));
}
-void BrowserPluginGuest::OnTextInputTypeChanged(ui::TextInputType type,
- ui::TextInputMode input_mode,
- bool can_compose_inline) {
- RenderWidgetHostViewPort::FromRWHV(
- web_contents()->GetRenderWidgetHostView())->TextInputTypeChanged(
- type, input_mode, can_compose_inline);
+void BrowserPluginGuest::OnTextInputStateChanged(
+ const ViewHostMsg_TextInputState_Params& params) {
+ // Save the state of text input so we can restore it on focus.
+ last_text_input_type_ = params.type;
+ last_input_mode_ = params.mode;
+ last_can_compose_inline_ = params.can_compose_inline;
+
+ static_cast<RenderWidgetHostViewBase*>(
+ web_contents()->GetRenderWidgetHostView())->TextInputStateChanged(params);
}
void BrowserPluginGuest::OnImeCancelComposition() {
- RenderWidgetHostViewPort::FromRWHV(
+ static_cast<RenderWidgetHostViewBase*>(
web_contents()->GetRenderWidgetHostView())->ImeCancelComposition();
}
-#if defined(OS_MACOSX) || defined(OS_WIN) || defined(USE_AURA)
+#if defined(OS_MACOSX) || defined(USE_AURA)
void BrowserPluginGuest::OnImeCompositionRangeChanged(
const gfx::Range& range,
const std::vector<gfx::Rect>& character_bounds) {
- RenderWidgetHostViewPort::FromRWHV(
+ static_cast<RenderWidgetHostViewBase*>(
web_contents()->GetRenderWidgetHostView())->ImeCompositionRangeChanged(
range, character_bounds);
}
#endif
-void BrowserPluginGuest::DidRetrieveDownloadURLFromRequestId(
- const std::string& request_method,
- const base::Callback<void(bool)>& callback,
- const std::string& url) {
- if (url.empty()) {
- callback.Run(false);
- return;
- }
-
- base::DictionaryValue request_info;
- request_info.Set(browser_plugin::kRequestMethod,
- base::Value::CreateStringValue(request_method));
- request_info.Set(browser_plugin::kURL, base::Value::CreateStringValue(url));
-
- RequestPermission(BROWSER_PLUGIN_PERMISSION_TYPE_DOWNLOAD,
- new DownloadRequest(callback),
- request_info);
-}
-
} // namespace content
diff --git a/chromium/content/browser/browser_plugin/browser_plugin_guest.h b/chromium/content/browser/browser_plugin/browser_plugin_guest.h
index 46c95a40da8..d1bd4ba36c7 100644
--- a/chromium/content/browser/browser_plugin/browser_plugin_guest.h
+++ b/chromium/content/browser/browser_plugin/browser_plugin_guest.h
@@ -10,9 +10,9 @@
// messages about the guest render process that the embedder might be interested
// in receiving should be listened for here.
//
-// BrowserPluginGuest is a WebContentsDelegate and WebContentsObserver for the
-// guest WebContents. BrowserPluginGuest operates under the assumption that the
-// guest will be accessible through only one RenderViewHost for the lifetime of
+// BrowserPluginGuest is a WebContentsObserver for the guest WebContents.
+// BrowserPluginGuest operates under the assumption that the guest will be
+// accessible through only one RenderViewHost for the lifetime of
// the guest WebContents. Thus, cross-process navigation is not supported.
#ifndef CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_GUEST_H_
@@ -22,17 +22,13 @@
#include <queue>
#include "base/compiler_specific.h"
-#include "base/id_map.h"
-#include "base/memory/shared_memory.h"
+#include "base/memory/linked_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/values.h"
#include "content/common/edit_command.h"
-#include "content/port/common/input_event_ack_state.h"
+#include "content/common/input/input_event_ack_state.h"
#include "content/public/browser/browser_plugin_guest_delegate.h"
-#include "content/public/browser/javascript_dialog_manager.h"
-#include "content/public/browser/web_contents_delegate.h"
#include "content/public/browser/web_contents_observer.h"
-#include "content/public/common/browser_plugin_permission_type.h"
#include "third_party/WebKit/public/web/WebCompositionUnderline.h"
#include "third_party/WebKit/public/web/WebDragOperation.h"
#include "third_party/WebKit/public/web/WebDragStatus.h"
@@ -40,21 +36,18 @@
#include "ui/base/ime/text_input_mode.h"
#include "ui/base/ime/text_input_type.h"
#include "ui/gfx/rect.h"
-#include "ui/surface/transport_dib.h"
+class SkBitmap;
struct BrowserPluginHostMsg_AutoSize_Params;
struct BrowserPluginHostMsg_Attach_Params;
struct BrowserPluginHostMsg_ResizeGuest_Params;
-struct ViewHostMsg_CreateWindow_Params;
+struct FrameHostMsg_CompositorFrameSwappedACK_Params;
+struct FrameHostMsg_ReclaimCompositorResources_Params;
#if defined(OS_MACOSX)
struct ViewHostMsg_ShowPopup_Params;
#endif
+struct ViewHostMsg_TextInputState_Params;
struct ViewHostMsg_UpdateRect_Params;
-class WebCursor;
-
-namespace cc {
-class CompositorFrameAck;
-}
namespace blink {
class WebInputEvent;
@@ -66,14 +59,12 @@ class Range;
namespace content {
-class BrowserPluginHostFactory;
-class BrowserPluginEmbedder;
class BrowserPluginGuestManager;
-class RenderProcessHost;
+class RenderViewHostImpl;
class RenderWidgetHostView;
class SiteInstance;
+class WebCursor;
struct DropData;
-struct MediaStreamRequest;
// A browser plugin guest provides functionality for WebContents to operate in
// the guest role and implements guest-specific overrides for ViewHostMsg_*
@@ -85,13 +76,8 @@ struct MediaStreamRequest;
// A BrowserPluginGuest can also create a new unattached guest via
// CreateNewWindow. The newly created guest will live in the same partition,
// which means it can share storage and can script this guest.
-class CONTENT_EXPORT BrowserPluginGuest
- : public JavaScriptDialogManager,
- public WebContentsDelegate,
- public WebContentsObserver,
- public base::SupportsWeakPtr<BrowserPluginGuest> {
+class CONTENT_EXPORT BrowserPluginGuest : public WebContentsObserver {
public:
- typedef base::Callback<void(bool)> GeolocationCallback;
virtual ~BrowserPluginGuest();
// The WebContents passed into the factory method here has not been
@@ -105,17 +91,21 @@ class CONTENT_EXPORT BrowserPluginGuest
int instance_id,
SiteInstance* guest_site_instance,
WebContentsImpl* web_contents,
- scoped_ptr<base::DictionaryValue> extra_params);
-
- static BrowserPluginGuest* CreateWithOpener(
- int instance_id,
- bool has_render_view,
- WebContentsImpl* web_contents,
+ scoped_ptr<base::DictionaryValue> extra_params,
BrowserPluginGuest* opener);
- // Called when the embedder WebContents is destroyed to give the
- // BrowserPluginGuest an opportunity to clean up after itself.
- void EmbedderDestroyed();
+ // Returns whether the given WebContents is a BrowserPlugin guest.
+ static bool IsGuest(WebContentsImpl* web_contents);
+
+ // Returns whether the given RenderviewHost is a BrowserPlugin guest.
+ static bool IsGuest(RenderViewHostImpl* render_view_host);
+
+ // Returns a WeakPtr to this BrowserPluginGuest.
+ base::WeakPtr<BrowserPluginGuest> AsWeakPtr();
+
+ // Sets the lock state of the pointer. Returns true if |allowed| is true and
+ // the mouse has been successfully locked.
+ bool LockMouse(bool allowed);
// Called when the embedder WebContents changes visibility.
void EmbedderVisibilityChanged(bool visible);
@@ -130,24 +120,20 @@ class CONTENT_EXPORT BrowserPluginGuest
bool OnMessageReceivedFromEmbedder(const IPC::Message& message);
- void Initialize(const BrowserPluginHostMsg_Attach_Params& params,
- WebContentsImpl* embedder_web_contents);
-
WebContentsImpl* embedder_web_contents() const {
return embedder_web_contents_;
}
+ // Returns the embedder's RenderWidgetHostView if it is available.
+ // Returns NULL otherwise.
RenderWidgetHostView* GetEmbedderRenderWidgetHostView();
bool focused() const { return focused_; }
bool visible() const { return guest_visible_; }
- void clear_damage_buffer() { damage_buffer_.reset(); }
bool is_in_destruction() { return is_in_destruction_; }
- BrowserPluginGuest* opener() const { return opener_.get(); }
-
- // Returns whether the mouse pointer was unlocked.
- bool UnlockMouseIfNecessary(const NativeWebKeyboardEvent& event);
+ // Returns the BrowserPluginGuest that created this guest, if any.
+ BrowserPluginGuest* GetOpener() const;
void UpdateVisibility();
@@ -156,6 +142,8 @@ class CONTENT_EXPORT BrowserPluginGuest
gfx::Size dst_size,
const base::Callback<void(bool, const SkBitmap&)>& callback);
+ BrowserPluginGuestManager* GetBrowserPluginGuestManager() const;
+
// WebContentsObserver implementation.
virtual void DidCommitProvisionalLoadForFrame(
int64 frame_id,
@@ -164,93 +152,19 @@ class CONTENT_EXPORT BrowserPluginGuest
const GURL& url,
PageTransition transition_type,
RenderViewHost* render_view_host) OVERRIDE;
- virtual void DidStopLoading(RenderViewHost* render_view_host) OVERRIDE;
virtual void RenderViewReady() OVERRIDE;
virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE;
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
- // WebContentsDelegate implementation.
- virtual bool AddMessageToConsole(WebContents* source,
- int32 level,
- const base::string16& message,
- int32 line_no,
- const base::string16& source_id) OVERRIDE;
- // If a new window is created with target="_blank" and rel="noreferrer", then
- // this method is called, indicating that the new WebContents is ready to be
- // attached.
- virtual void AddNewContents(WebContents* source,
- WebContents* new_contents,
- WindowOpenDisposition disposition,
- const gfx::Rect& initial_pos,
- bool user_gesture,
- bool* was_blocked) OVERRIDE;
- virtual void CanDownload(RenderViewHost* render_view_host,
- int request_id,
- const std::string& request_method,
- const base::Callback<void(bool)>& callback) OVERRIDE;
- virtual void LoadProgressChanged(WebContents* source,
- double progress) OVERRIDE;
- virtual void CloseContents(WebContents* source) OVERRIDE;
- virtual JavaScriptDialogManager* GetJavaScriptDialogManager() OVERRIDE;
- virtual bool HandleContextMenu(const ContextMenuParams& params) OVERRIDE;
- virtual void HandleKeyboardEvent(
- WebContents* source,
- const NativeWebKeyboardEvent& event) OVERRIDE;
- virtual WebContents* OpenURLFromTab(WebContents* source,
- const OpenURLParams& params) OVERRIDE;
- virtual void WebContentsCreated(WebContents* source_contents,
- int64 source_frame_id,
- const base::string16& frame_name,
- const GURL& target_url,
- WebContents* new_contents) OVERRIDE;
- virtual void RendererUnresponsive(WebContents* source) OVERRIDE;
- virtual void RendererResponsive(WebContents* source) OVERRIDE;
- virtual void RunFileChooser(WebContents* web_contents,
- const FileChooserParams& params) OVERRIDE;
- virtual bool ShouldFocusPageAfterCrash() OVERRIDE;
- virtual void RequestMediaAccessPermission(
- WebContents* web_contents,
- const MediaStreamRequest& request,
- const MediaResponseCallback& callback) OVERRIDE;
-
- // JavaScriptDialogManager implementation.
- virtual void RunJavaScriptDialog(
- WebContents* web_contents,
- const GURL& origin_url,
- const std::string& accept_lang,
- JavaScriptMessageType javascript_message_type,
- const base::string16& message_text,
- const base::string16& default_prompt_text,
- const DialogClosedCallback& callback,
- bool* did_suppress_message) OVERRIDE;
- virtual void RunBeforeUnloadDialog(
- WebContents* web_contents,
- const base::string16& message_text,
- bool is_reload,
- const DialogClosedCallback& callback) OVERRIDE;
- virtual bool HandleJavaScriptDialog(
- WebContents* web_contents,
- bool accept,
- const base::string16* prompt_override) OVERRIDE;
- virtual void CancelActiveAndPendingDialogs(
- WebContents* web_contents) OVERRIDE;
- virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE;
-
// Exposes the protected web_contents() from WebContentsObserver.
- WebContentsImpl* GetWebContents();
-
- // Overridden in tests.
- virtual void SetDamageBuffer(
- const BrowserPluginHostMsg_ResizeGuest_Params& params);
+ WebContentsImpl* GetWebContents() const;
gfx::Point GetScreenCoordinates(const gfx::Point& relative_position) const;
// Helper to send messages to embedder. This methods fills the message with
// the correct routing id.
- // Overridden in test implementation since we want to intercept certain
- // messages for testing.
- virtual void SendMessageToEmbedder(IPC::Message* msg);
+ void SendMessageToEmbedder(IPC::Message* msg);
// Returns whether the guest is attached to an embedder.
bool attached() const { return embedder_web_contents_ != NULL; }
@@ -262,23 +176,9 @@ class CONTENT_EXPORT BrowserPluginGuest
// parameters passed into BrowserPlugin from JavaScript to be forwarded to
// the content embedder.
void Attach(WebContentsImpl* embedder_web_contents,
- BrowserPluginHostMsg_Attach_Params params,
+ const BrowserPluginHostMsg_Attach_Params& params,
const base::DictionaryValue& extra_params);
- // Requests geolocation permission through Embedder JavaScript API.
- void AskEmbedderForGeolocationPermission(int bridge_id,
- const GURL& requesting_frame,
- const GeolocationCallback& callback);
- // Cancels pending geolocation request.
- void CancelGeolocationRequest(int bridge_id);
-
- // Allow the embedder to call this for unhandled messages when
- // BrowserPluginGuest is already destroyed.
- static void AcknowledgeBufferPresent(int route_id,
- int gpu_host_id,
- const std::string& mailbox_name,
- uint32 sync_point);
-
// Returns whether BrowserPluginGuest is interested in receiving the given
// |message|.
static bool ShouldForwardToBrowserPluginGuest(const IPC::Message& message);
@@ -287,106 +187,42 @@ class CONTENT_EXPORT BrowserPluginGuest
void DragSourceEndedAt(int client_x, int client_y, int screen_x,
int screen_y, blink::WebDragOperation operation);
- void DragSourceMovedTo(int client_x, int client_y,
- int screen_x, int screen_y);
-
// Called when the drag started by this guest ends at an OS-level.
void EndSystemDrag();
- // |this| takes ownership of |delegate|.
- void SetDelegate(BrowserPluginGuestDelegate* delegate);
+ void set_delegate(BrowserPluginGuestDelegate* delegate) {
+ DCHECK(!delegate_);
+ delegate_ = delegate;
+ }
void RespondToPermissionRequest(int request_id,
bool should_allow,
const std::string& user_input);
- // Overrides factory for testing. Default (NULL) value indicates regular
- // (non-test) environment.
- static void set_factory_for_testing(BrowserPluginHostFactory* factory) {
- BrowserPluginGuest::factory_ = factory;
- }
+ void PointerLockPermissionResponse(bool allow);
private:
class EmbedderWebContentsObserver;
- friend class TestBrowserPluginGuest;
-
- class DownloadRequest;
- class GeolocationRequest;
- class JavaScriptDialogRequest;
- // MediaRequest because of naming conflicts with MediaStreamRequest.
- class MediaRequest;
- class NewWindowRequest;
- class PermissionRequest;
- class PointerLockRequest;
-
- // Tracks the name, and target URL of the new window and whether or not it has
- // changed since the WebContents has been created and before the new window
- // has been attached to a BrowserPlugin. Once the first navigation commits, we
- // no longer track this information.
- struct NewWindowInfo {
- bool changed;
- GURL url;
- std::string name;
- NewWindowInfo(const GURL& url, const std::string& name) :
- changed(false),
- url(url),
- name(name) {}
- };
// BrowserPluginGuest is a WebContentsObserver of |web_contents| and
// |web_contents| has to stay valid for the lifetime of BrowserPluginGuest.
BrowserPluginGuest(int instance_id,
bool has_render_view,
- WebContentsImpl* web_contents,
- BrowserPluginGuest* opener);
-
- // Destroy unattached new windows that have been opened by this
- // BrowserPluginGuest.
- void DestroyUnattachedWindows();
-
- void LoadURLWithParams(const GURL& url,
- const Referrer& referrer,
- PageTransition transition_type,
- WebContents* web_contents);
-
- // Bridge IDs correspond to a geolocation request. This method will remove
- // the bookkeeping for a particular geolocation request associated with the
- // provided |bridge_id|. It returns the request ID of the geolocation request.
- int RemoveBridgeID(int bridge_id);
-
- // Returns the |request_id| generated for the |request| provided.
- int RequestPermission(
- BrowserPluginPermissionType permission_type,
- scoped_refptr<BrowserPluginGuest::PermissionRequest> request,
- const base::DictionaryValue& request_info);
-
- // Creates a new guest window, and BrowserPluginGuest that is owned by this
- // BrowserPluginGuest.
- BrowserPluginGuest* CreateNewGuestWindow(const OpenURLParams& params);
-
- base::SharedMemory* damage_buffer() const { return damage_buffer_.get(); }
- const gfx::Size& damage_view_size() const { return damage_view_size_; }
- float damage_buffer_scale_factor() const {
- return damage_buffer_scale_factor_;
- }
- // Returns the damage buffer corresponding to the handle in resize |params|.
- base::SharedMemory* GetDamageBufferFromEmbedder(
- const BrowserPluginHostMsg_ResizeGuest_Params& params);
+ WebContentsImpl* web_contents);
- bool InAutoSizeBounds(const gfx::Size& size) const;
+ void WillDestroy();
+
+ void Initialize(const BrowserPluginHostMsg_Attach_Params& params,
+ WebContentsImpl* embedder_web_contents,
+ const base::DictionaryValue& extra_params);
- void RequestNewWindowPermission(WindowOpenDisposition disposition,
- const gfx::Rect& initial_bounds,
- bool user_gesture,
- WebContentsImpl* new_contents);
+ bool InAutoSizeBounds(const gfx::Size& size) const;
// Message handlers for messages from embedder.
- void OnCompositorFrameACK(int instance_id,
- int route_id,
- uint32 output_surface_id,
- int renderer_host_id,
- const cc::CompositorFrameAck& ack);
+ void OnCompositorFrameSwappedACK(
+ int instance_id,
+ const FrameHostMsg_CompositorFrameSwappedACK_Params& params);
void OnCopyFromCompositingSurfaceAck(int instance_id,
int request_id,
const SkBitmap& bitmap);
@@ -405,33 +241,27 @@ class CONTENT_EXPORT BrowserPluginGuest
const std::string& command);
// Returns compositor resources reclaimed in the embedder to the guest.
- void OnReclaimCompositorResources(int instance_id,
- int route_id,
- uint32 output_surface_id,
- int renderer_host_id,
- const cc::CompositorFrameAck& ack);
-
- // Overriden in tests.
- virtual void OnHandleInputEvent(int instance_id,
+ void OnReclaimCompositorResources(
+ int instance_id,
+ const FrameHostMsg_ReclaimCompositorResources_Params& params);
+
+ void OnHandleInputEvent(int instance_id,
const gfx::Rect& guest_window_rect,
const blink::WebInputEvent* event);
void OnLockMouse(bool user_gesture,
bool last_unlocked_by_target,
bool privileged);
void OnLockMouseAck(int instance_id, bool succeeded);
- void OnNavigateGuest(int instance_id, const std::string& src);
void OnPluginDestroyed(int instance_id);
- // Grab the new damage buffer from the embedder, and resize the guest's
- // web contents.
- void OnResizeGuest(int instance_id,
- const BrowserPluginHostMsg_ResizeGuest_Params& params);
- // Overriden in tests.
- virtual void OnSetFocus(int instance_id, bool focused);
+ // Resizes the guest's web contents.
+ void OnResizeGuest(
+ int instance_id, const BrowserPluginHostMsg_ResizeGuest_Params& params);
+ void OnSetFocus(int instance_id, bool focused);
// Sets the name of the guest so that other guests in the same partition can
// access it.
void OnSetName(int instance_id, const std::string& name);
// Updates the size state of the guest.
- void OnSetSize(
+ void OnSetAutoSize(
int instance_id,
const BrowserPluginHostMsg_AutoSize_Params& auto_size_params,
const BrowserPluginHostMsg_ResizeGuest_Params& resize_guest_params);
@@ -455,24 +285,13 @@ class CONTENT_EXPORT BrowserPluginGuest
// collection. See RenderThreadImpl::IdleHandler (executed when hidden) and
// RenderThreadImpl::IdleHandlerInForegroundTab (executed when visible).
void OnSetVisibility(int instance_id, bool visible);
- // Message from embedder acknowledging last HW buffer.
- void OnSwapBuffersACK(int instance_id,
- int route_id,
- int gpu_host_id,
- const std::string& mailbox_name,
- uint32 sync_point);
void OnUnlockMouse();
void OnUnlockMouseAck(int instance_id);
void OnUpdateGeometry(int instance_id, const gfx::Rect& view_rect);
- void OnUpdateRectACK(
- int instance_id,
- bool needs_ack,
- const BrowserPluginHostMsg_AutoSize_Params& auto_size_params,
- const BrowserPluginHostMsg_ResizeGuest_Params& resize_guest_params);
- void OnTextInputTypeChanged(ui::TextInputType type,
- ui::TextInputMode input_mode,
- bool can_compose_inline);
+ void OnTextInputStateChanged(
+ const ViewHostMsg_TextInputState_Params& params);
+
void OnImeSetComposition(
int instance_id,
const std::string& text,
@@ -484,9 +303,8 @@ class CONTENT_EXPORT BrowserPluginGuest
const std::string& text,
bool keep_selection);
void OnExtendSelectionAndDelete(int instance_id, int before, int after);
- // Overridden in tests.
- virtual void OnImeCancelComposition();
-#if defined(OS_MACOSX) || defined(OS_WIN) || defined(USE_AURA)
+ void OnImeCancelComposition();
+#if defined(OS_MACOSX) || defined(USE_AURA)
void OnImeCompositionRangeChanged(
const gfx::Range& range,
const std::vector<gfx::Rect>& character_bounds);
@@ -506,52 +324,24 @@ class CONTENT_EXPORT BrowserPluginGuest
void OnShowPopup(const ViewHostMsg_ShowPopup_Params& params);
#endif
void OnShowWidget(int route_id, const gfx::Rect& initial_pos);
- // Overriden in tests.
- virtual void OnTakeFocus(bool reverse);
+ void OnTakeFocus(bool reverse);
void OnUpdateFrameName(int frame_id,
bool is_top_level,
const std::string& name);
void OnUpdateRect(const ViewHostMsg_UpdateRect_Params& params);
- // Requests download permission through embedder JavaScript API after
- // retrieving url information from IO thread.
- void DidRetrieveDownloadURLFromRequestId(
- const std::string& request_method,
- const base::Callback<void(bool)>& callback,
- const std::string& url);
-
- // Embedder sets permission to allow or deny geolocation request.
- void SetGeolocationPermission(
- GeolocationCallback callback, int bridge_id, bool allowed);
-
// Forwards all messages from the |pending_messages_| queue to the embedder.
void SendQueuedMessages();
- // Weak pointer used to ask GeolocationPermissionContext about geolocation
- // permission.
- base::WeakPtrFactory<BrowserPluginGuest> weak_ptr_factory_;
-
- // Static factory instance (always NULL for non-test).
- static BrowserPluginHostFactory* factory_;
-
scoped_ptr<EmbedderWebContentsObserver> embedder_web_contents_observer_;
WebContentsImpl* embedder_web_contents_;
- std::map<int, int> bridge_id_to_request_id_map_;
-
// An identifier that uniquely identifies a browser plugin guest within an
// embedder.
int instance_id_;
- scoped_ptr<base::SharedMemory> damage_buffer_;
- // An identifier that uniquely identifies a damage buffer.
- uint32 damage_buffer_sequence_id_;
- size_t damage_buffer_size_;
- gfx::Size damage_view_size_;
- float damage_buffer_scale_factor_;
float guest_device_scale_factor_;
gfx::Rect guest_window_rect_;
gfx::Rect guest_screen_rect_;
- base::TimeDelta guest_hang_timeout_;
bool focused_;
bool mouse_locked_;
bool pending_lock_request_;
@@ -562,6 +352,7 @@ class CONTENT_EXPORT BrowserPluginGuest
bool auto_size_enabled_;
gfx::Size max_auto_size_;
gfx::Size min_auto_size_;
+ gfx::Size full_size_;
// Each copy-request is identified by a unique number. The unique number is
// used to keep track of the right callback.
@@ -570,20 +361,11 @@ class CONTENT_EXPORT BrowserPluginGuest
typedef std::map<int, const CopyRequestCallback> CopyRequestMap;
CopyRequestMap copy_request_callbacks_;
- typedef std::map<BrowserPluginGuest*, NewWindowInfo> PendingWindowMap;
- PendingWindowMap pending_new_windows_;
- base::WeakPtr<BrowserPluginGuest> opener_;
- // A counter to generate a unique request id for a permission request.
- // We only need the ids to be unique for a given BrowserPluginGuest.
- int next_permission_request_id_;
-
- // A map to store relevant info for a request keyed by the request's id.
- typedef std::map<int, scoped_refptr<PermissionRequest> > RequestMap;
- RequestMap permission_request_map_;
-
// Indicates that this BrowserPluginGuest has associated renderer-side state.
// This is used to determine whether or not to create a new RenderView when
- // this guest is attached.
+ // this guest is attached. A BrowserPluginGuest would have renderer-side state
+ // prior to attachment if it is created via a call to window.open and
+ // maintains a JavaScript reference to its opener.
bool has_render_view_;
// Last seen size of guest contents (by OnUpdateRect).
@@ -593,15 +375,20 @@ class CONTENT_EXPORT BrowserPluginGuest
bool is_in_destruction_;
+ // Text input type states.
+ ui::TextInputType last_text_input_type_;
+ ui::TextInputMode last_input_mode_;
+ bool last_can_compose_inline_;
+
// 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::queue<IPC::Message*> pending_messages_;
+ std::deque<linked_ptr<IPC::Message> > pending_messages_;
- scoped_ptr<BrowserPluginGuestDelegate> delegate_;
+ BrowserPluginGuestDelegate* delegate_;
- // These are parameters passed from JavaScript on attachment to the content
- // embedder.
- scoped_ptr<base::DictionaryValue> extra_attach_params_;
+ // Weak pointer used to ask GeolocationPermissionContext about geolocation
+ // permission.
+ base::WeakPtrFactory<BrowserPluginGuest> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(BrowserPluginGuest);
};
diff --git a/chromium/content/browser/browser_plugin/browser_plugin_guest_manager.cc b/chromium/content/browser/browser_plugin/browser_plugin_guest_manager.cc
deleted file mode 100644
index ed91c1f51ad..00000000000
--- a/chromium/content/browser/browser_plugin/browser_plugin_guest_manager.cc
+++ /dev/null
@@ -1,244 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/browser_plugin/browser_plugin_guest_manager.h"
-
-#include "content/browser/browser_plugin/browser_plugin_guest.h"
-#include "content/browser/browser_plugin/browser_plugin_host_factory.h"
-#include "content/browser/renderer_host/render_view_host_impl.h"
-#include "content/browser/web_contents/web_contents_impl.h"
-#include "content/common/browser_plugin/browser_plugin_constants.h"
-#include "content/common/browser_plugin/browser_plugin_messages.h"
-#include "content/common/content_export.h"
-#include "content/public/browser/user_metrics.h"
-#include "content/public/common/result_codes.h"
-#include "content/public/common/url_constants.h"
-#include "content/public/common/url_utils.h"
-#include "net/base/escape.h"
-
-namespace content {
-
-// static
-BrowserPluginHostFactory* BrowserPluginGuestManager::factory_ = NULL;
-
-BrowserPluginGuestManager::BrowserPluginGuestManager()
- : next_instance_id_(browser_plugin::kInstanceIDNone) {
-}
-
-BrowserPluginGuestManager::~BrowserPluginGuestManager() {
-}
-
-// static
-BrowserPluginGuestManager* BrowserPluginGuestManager::Create() {
- if (factory_)
- return factory_->CreateBrowserPluginGuestManager();
- return new BrowserPluginGuestManager();
-}
-
-BrowserPluginGuest* BrowserPluginGuestManager::CreateGuest(
- SiteInstance* embedder_site_instance,
- int instance_id,
- const BrowserPluginHostMsg_Attach_Params& params,
- scoped_ptr<base::DictionaryValue> extra_params) {
- RenderProcessHost* embedder_process_host =
- embedder_site_instance->GetProcess();
- // Validate that the partition id coming from the renderer is valid UTF-8,
- // since we depend on this in other parts of the code, such as FilePath
- // creation. If the validation fails, treat it as a bad message and kill the
- // renderer process.
- if (!IsStringUTF8(params.storage_partition_id)) {
- content::RecordAction(UserMetricsAction("BadMessageTerminate_BPGM"));
- base::KillProcess(
- embedder_process_host->GetHandle(),
- content::RESULT_CODE_KILLED_BAD_MESSAGE, false);
- return NULL;
- }
-
- // We usually require BrowserPlugins to be hosted by a storage isolated
- // extension. We treat WebUI pages as a special case if they host the
- // BrowserPlugin in a component extension iframe. In that case, we use the
- // iframe's URL to determine the extension.
- const GURL& embedder_site_url = embedder_site_instance->GetSiteURL();
- GURL validated_frame_url(params.embedder_frame_url);
- RenderViewHost::FilterURL(
- embedder_process_host, false, &validated_frame_url);
- const std::string& host = content::HasWebUIScheme(embedder_site_url) ?
- validated_frame_url.host() : embedder_site_url.host();
-
- std::string url_encoded_partition = net::EscapeQueryParamValue(
- params.storage_partition_id, false);
- // The SiteInstance of a given webview tag is based on the fact that it's
- // a guest process in addition to which platform application the tag
- // belongs to and what storage partition is in use, rather than the URL
- // that the tag is being navigated to.
- GURL guest_site(base::StringPrintf("%s://%s/%s?%s",
- kGuestScheme,
- host.c_str(),
- params.persist_storage ? "persist" : "",
- url_encoded_partition.c_str()));
-
- // If we already have a webview tag in the same app using the same storage
- // partition, we should use the same SiteInstance so the existing tag and
- // the new tag can script each other.
- SiteInstance* guest_site_instance = GetGuestSiteInstance(guest_site);
- if (!guest_site_instance) {
- // Create the SiteInstance in a new BrowsingInstance, which will ensure
- // that webview tags are also not allowed to send messages across
- // different partitions.
- guest_site_instance = SiteInstance::CreateForURL(
- embedder_site_instance->GetBrowserContext(), guest_site);
- }
-
- return WebContentsImpl::CreateGuest(
- embedder_site_instance->GetBrowserContext(),
- guest_site_instance,
- instance_id,
- extra_params.Pass());
-}
-
-BrowserPluginGuest* BrowserPluginGuestManager::GetGuestByInstanceID(
- int instance_id,
- int embedder_render_process_id) const {
- if (!CanEmbedderAccessInstanceIDMaybeKill(embedder_render_process_id,
- instance_id)) {
- return NULL;
- }
- GuestInstanceMap::const_iterator it =
- guest_web_contents_by_instance_id_.find(instance_id);
- if (it == guest_web_contents_by_instance_id_.end())
- return NULL;
- return static_cast<WebContentsImpl*>(it->second)->GetBrowserPluginGuest();
-}
-
-void BrowserPluginGuestManager::AddGuest(int instance_id,
- WebContentsImpl* guest_web_contents) {
- DCHECK(guest_web_contents_by_instance_id_.find(instance_id) ==
- guest_web_contents_by_instance_id_.end());
- guest_web_contents_by_instance_id_[instance_id] = guest_web_contents;
-}
-
-void BrowserPluginGuestManager::RemoveGuest(int instance_id) {
- DCHECK(guest_web_contents_by_instance_id_.find(instance_id) !=
- guest_web_contents_by_instance_id_.end());
- guest_web_contents_by_instance_id_.erase(instance_id);
-}
-
-bool BrowserPluginGuestManager::CanEmbedderAccessInstanceIDMaybeKill(
- int embedder_render_process_id,
- int instance_id) const {
- if (!CanEmbedderAccessInstanceID(embedder_render_process_id, instance_id)) {
- // The embedder process is trying to access a guest it does not own.
- content::RecordAction(UserMetricsAction("BadMessageTerminate_BPGM"));
- base::KillProcess(
- RenderProcessHost::FromID(embedder_render_process_id)->GetHandle(),
- content::RESULT_CODE_KILLED_BAD_MESSAGE, false);
- return false;
- }
- return true;
-}
-
-void BrowserPluginGuestManager::OnMessageReceived(const IPC::Message& message,
- int render_process_id) {
- if (BrowserPluginGuest::ShouldForwardToBrowserPluginGuest(message)) {
- int instance_id = 0;
- // All allowed messages must have instance_id as their first parameter.
- PickleIterator iter(message);
- bool success = iter.ReadInt(&instance_id);
- DCHECK(success);
- BrowserPluginGuest* guest =
- GetGuestByInstanceID(instance_id, render_process_id);
- if (guest && guest->OnMessageReceivedFromEmbedder(message))
- return;
- }
- IPC_BEGIN_MESSAGE_MAP(BrowserPluginGuestManager, message)
- IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_BuffersSwappedACK,
- OnUnhandledSwapBuffersACK)
- IPC_END_MESSAGE_MAP()
-}
-
-// static
-bool BrowserPluginGuestManager::CanEmbedderAccessGuest(
- int embedder_render_process_id,
- BrowserPluginGuest* guest) {
- // The embedder can access the guest if it has not been attached and its
- // opener's embedder lives in the same process as the given embedder.
- if (!guest->attached()) {
- if (!guest->opener())
- return false;
-
- return embedder_render_process_id ==
- guest->opener()->embedder_web_contents()->GetRenderProcessHost()->
- GetID();
- }
-
- return embedder_render_process_id ==
- guest->embedder_web_contents()->GetRenderProcessHost()->GetID();
-}
-
-bool BrowserPluginGuestManager::CanEmbedderAccessInstanceID(
- int embedder_render_process_id,
- int instance_id) const {
- // The embedder is trying to access a guest with a negative or zero
- // instance ID.
- if (instance_id <= browser_plugin::kInstanceIDNone)
- return false;
-
- // The embedder is trying to access an instance ID that has not yet been
- // allocated by BrowserPluginGuestManager. This could cause instance ID
- // collisions in the future, and potentially give one embedder access to a
- // guest it does not own.
- if (instance_id > next_instance_id_)
- return false;
-
- GuestInstanceMap::const_iterator it =
- guest_web_contents_by_instance_id_.find(instance_id);
- if (it == guest_web_contents_by_instance_id_.end())
- return true;
- BrowserPluginGuest* guest =
- static_cast<WebContentsImpl*>(it->second)->GetBrowserPluginGuest();
-
- return CanEmbedderAccessGuest(embedder_render_process_id, guest);
-}
-
-SiteInstance* BrowserPluginGuestManager::GetGuestSiteInstance(
- const GURL& guest_site) {
- for (GuestInstanceMap::const_iterator it =
- guest_web_contents_by_instance_id_.begin();
- it != guest_web_contents_by_instance_id_.end(); ++it) {
- if (it->second->GetSiteInstance()->GetSiteURL() == guest_site)
- return it->second->GetSiteInstance();
- }
- return NULL;
-}
-
-// We only get here during teardown if we have one last buffer pending,
-// otherwise the ACK is handled by the guest.
-void BrowserPluginGuestManager::OnUnhandledSwapBuffersACK(
- int instance_id,
- int route_id,
- int gpu_host_id,
- const std::string& mailbox_name,
- uint32 sync_point) {
- BrowserPluginGuest::AcknowledgeBufferPresent(route_id,
- gpu_host_id,
- mailbox_name,
- sync_point);
-}
-
-bool BrowserPluginGuestManager::ForEachGuest(
- WebContentsImpl* embedder_web_contents, const GuestCallback& callback) {
- for (GuestInstanceMap::iterator it =
- guest_web_contents_by_instance_id_.begin();
- it != guest_web_contents_by_instance_id_.end(); ++it) {
- BrowserPluginGuest* guest = it->second->GetBrowserPluginGuest();
- if (embedder_web_contents != guest->embedder_web_contents())
- continue;
-
- if (callback.Run(guest))
- return true;
- }
- return false;
-}
-
-} // namespace content
diff --git a/chromium/content/browser/browser_plugin/browser_plugin_guest_manager.h b/chromium/content/browser/browser_plugin/browser_plugin_guest_manager.h
deleted file mode 100644
index 5f3b3b9c686..00000000000
--- a/chromium/content/browser/browser_plugin/browser_plugin_guest_manager.h
+++ /dev/null
@@ -1,138 +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.
-
-// A BrowserPluginGuestManager serves as a message router to BrowserPluginGuests
-// for all guests within a given profile.
-// Messages are routed to a particular guest instance based on an instance_id.
-
-#ifndef CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_GUEST_MANAGER_H_
-#define CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_GUEST_MANAGER_H_
-
-#include "base/basictypes.h"
-#include "base/supports_user_data.h"
-#include "base/values.h"
-#include "content/common/content_export.h"
-#include "ipc/ipc_message.h"
-
-struct BrowserPluginHostMsg_Attach_Params;
-struct BrowserPluginHostMsg_ResizeGuest_Params;
-class GURL;
-
-namespace gfx {
-class Point;
-}
-
-namespace IPC {
-class Message;
-} // namespace IPC
-
-namespace content {
-
-class BrowserPluginGuest;
-class BrowserPluginHostFactory;
-class RenderProcessHostImpl;
-class RenderWidgetHostImpl;
-class SiteInstance;
-class WebContents;
-class WebContentsImpl;
-
-// WARNING: All APIs should be guarded with a process ID check like
-// CanEmbedderAccessInstanceIDMaybeKill, to prevent abuse by normal renderer
-// processes.
-class CONTENT_EXPORT BrowserPluginGuestManager :
- public base::SupportsUserData::Data {
- public:
- virtual ~BrowserPluginGuestManager();
-
- static BrowserPluginGuestManager* Create();
-
- // Overrides factory for testing. Default (NULL) value indicates regular
- // (non-test) environment.
- static void set_factory_for_testing(BrowserPluginHostFactory* factory) {
- content::BrowserPluginGuestManager::factory_ = factory;
- }
-
- // Gets the next available instance id.
- int get_next_instance_id() { return ++next_instance_id_; }
-
- // Creates a guest WebContents with the provided |instance_id| and |params|.
- // If params.src is present, the new guest will also be navigated to the
- // provided URL. Optionally, the new guest may be attached to a
- // |guest_opener|, and may be attached to a pre-selected |routing_id|.
- BrowserPluginGuest* CreateGuest(
- SiteInstance* embedder_site_instance,
- int instance_id,
- const BrowserPluginHostMsg_Attach_Params& params,
- scoped_ptr<base::DictionaryValue> extra_params);
-
- // Returns a BrowserPluginGuest given an |instance_id|. Returns NULL if the
- // guest wasn't found. If the embedder is not permitted to access the given
- // |instance_id|, the embedder is killed, and NULL is returned.
- BrowserPluginGuest* GetGuestByInstanceID(
- int instance_id,
- int embedder_render_process_id) const;
-
- // Adds a new |guest_web_contents| to the embedder (overridable in test).
- virtual void AddGuest(int instance_id, WebContentsImpl* guest_web_contents);
-
- // Removes the guest with the given |instance_id| from this
- // BrowserPluginGuestManager.
- void RemoveGuest(int instance_id);
-
- // Returns whether the specified embedder is permitted to access the given
- // |instance_id|, and kills the embedder if not.
- bool CanEmbedderAccessInstanceIDMaybeKill(int embedder_render_process_id,
- int instance_id) const;
-
- typedef base::Callback<bool(BrowserPluginGuest*)> GuestCallback;
- bool ForEachGuest(WebContentsImpl* embedder_web_contents,
- const GuestCallback& callback);
-
- void OnMessageReceived(const IPC::Message& message, int render_process_id);
-
- private:
- friend class TestBrowserPluginGuestManager;
-
- BrowserPluginGuestManager();
-
- // Returns whether the given embedder process is allowed to access the
- // provided |guest|.
- static bool CanEmbedderAccessGuest(int embedder_render_process_id,
- BrowserPluginGuest* guest);
-
- // Returns whether the given embedder process is allowed to use the provided
- // |instance_id| or access the guest associated with the |instance_id|. If the
- // embedder can, the method returns true. If the guest does not exist but the
- // embedder can use that |instance_id|, then it returns true. If the embedder
- // is not permitted to use that instance ID or access the associated guest,
- // then it returns false.
- bool CanEmbedderAccessInstanceID(int embedder_render_process_id,
- int instance_id) const;
-
- // Returns an existing SiteInstance if the current profile has a guest of the
- // given |guest_site|.
- SiteInstance* GetGuestSiteInstance(const GURL& guest_site);
-
- // Message handlers.
- void OnUnhandledSwapBuffersACK(int instance_id,
- int route_id,
- int gpu_host_id,
- const std::string& mailbox_name,
- uint32 sync_point);
-
- // Static factory instance (always NULL outside of tests).
- static BrowserPluginHostFactory* factory_;
-
- // Contains guests' WebContents, mapping from their instance ids.
- typedef std::map<int, WebContentsImpl*> GuestInstanceMap;
- GuestInstanceMap guest_web_contents_by_instance_id_;
- int next_instance_id_;
-
- DISALLOW_COPY_AND_ASSIGN(BrowserPluginGuestManager);
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_GUEST_MANAGER_H_
-
diff --git a/chromium/content/browser/browser_plugin/browser_plugin_host_browsertest.cc b/chromium/content/browser/browser_plugin/browser_plugin_host_browsertest.cc
deleted file mode 100644
index 9e48a49647d..00000000000
--- a/chromium/content/browser/browser_plugin/browser_plugin_host_browsertest.cc
+++ /dev/null
@@ -1,1039 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/command_line.h"
-#include "base/memory/singleton.h"
-#include "base/run_loop.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/test/test_timeouts.h"
-#include "content/browser/browser_plugin/browser_plugin_guest.h"
-#include "content/browser/browser_plugin/browser_plugin_host_factory.h"
-#include "content/browser/browser_plugin/test_browser_plugin_embedder.h"
-#include "content/browser/browser_plugin/test_browser_plugin_guest.h"
-#include "content/browser/browser_plugin/test_browser_plugin_guest_delegate.h"
-#include "content/browser/browser_plugin/test_browser_plugin_guest_manager.h"
-#include "content/browser/child_process_security_policy_impl.h"
-#include "content/browser/renderer_host/render_view_host_impl.h"
-#include "content/browser/web_contents/web_contents_impl.h"
-#include "content/common/browser_plugin/browser_plugin_messages.h"
-#include "content/common/view_messages.h"
-#include "content/public/browser/notification_service.h"
-#include "content/public/browser/notification_types.h"
-#include "content/public/browser/render_widget_host_view.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "content/public/common/content_switches.h"
-#include "content/public/common/drop_data.h"
-#include "content/public/common/url_constants.h"
-#include "content/public/test/browser_test_utils.h"
-#include "content/public/test/test_utils.h"
-#include "content/shell/browser/shell.h"
-#include "content/test/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
-#include "net/base/net_util.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
-#include "net/test/embedded_test_server/http_request.h"
-#include "net/test/embedded_test_server/http_response.h"
-#include "net/test/spawned_test_server/spawned_test_server.h"
-#include "third_party/WebKit/public/web/WebInputEvent.h"
-
-using blink::WebInputEvent;
-using blink::WebMouseEvent;
-using content::BrowserPluginEmbedder;
-using content::BrowserPluginGuest;
-using content::BrowserPluginHostFactory;
-using content::WebContentsImpl;
-
-const char kHTMLForGuest[] =
- "data:text/html,<html><body>hello world</body></html>";
-
-const char kHTMLForGuestTouchHandler[] =
- "data:text/html,<html><body><div id=\"touch\">With touch</div></body>"
- "<script type=\"text/javascript\">"
- "function handler() {}"
- "function InstallTouchHandler() { "
- " document.getElementById(\"touch\").addEventListener(\"touchstart\", "
- " handler);"
- "}"
- "function UninstallTouchHandler() { "
- " document.getElementById(\"touch\").removeEventListener(\"touchstart\", "
- " handler);"
- "}"
- "</script></html>";
-
-const char kHTMLForGuestAcceptDrag[] =
- "data:text/html,<html><body>"
- "<script>"
- "function dropped() {"
- " document.title = \"DROPPED\";"
- "}"
- "</script>"
- "<textarea id=\"text\" style=\"width:100%; height: 100%\""
- " ondrop=\"dropped();\">"
- "</textarea>"
- "</body></html>";
-
-const char kHTMLForGuestWithSize[] =
- "data:text/html,"
- "<html>"
- "<body style=\"margin: 0px;\">"
- "<img style=\"width: 100%; height: 400px;\"/>"
- "</body>"
- "</html>";
-
-namespace content {
-
-// Test factory for creating test instances of BrowserPluginEmbedder and
-// BrowserPluginGuest.
-class TestBrowserPluginHostFactory : public BrowserPluginHostFactory {
- public:
- virtual BrowserPluginGuestManager*
- CreateBrowserPluginGuestManager() OVERRIDE {
- guest_manager_instance_count_++;
- if (message_loop_runner_.get())
- message_loop_runner_->Quit();
- return new TestBrowserPluginGuestManager();
- }
-
- virtual BrowserPluginGuest* CreateBrowserPluginGuest(
- int instance_id,
- WebContentsImpl* web_contents) OVERRIDE {
- return new TestBrowserPluginGuest(instance_id, web_contents);
- }
-
- // Also keeps track of number of instances created.
- virtual BrowserPluginEmbedder* CreateBrowserPluginEmbedder(
- WebContentsImpl* web_contents) OVERRIDE {
-
- return new TestBrowserPluginEmbedder(web_contents);
- }
-
- // Singleton getter.
- static TestBrowserPluginHostFactory* GetInstance() {
- return Singleton<TestBrowserPluginHostFactory>::get();
- }
-
- // Waits for at least one embedder to be created in the test. Returns true if
- // we have a guest, false if waiting times out.
- void WaitForGuestManagerCreation() {
- // Check if already have created an instance.
- if (guest_manager_instance_count_ > 0)
- return;
- // Wait otherwise.
- message_loop_runner_ = new MessageLoopRunner();
- message_loop_runner_->Run();
- }
-
- protected:
- TestBrowserPluginHostFactory() : guest_manager_instance_count_(0) {}
- virtual ~TestBrowserPluginHostFactory() {}
-
- private:
- // For Singleton.
- friend struct DefaultSingletonTraits<TestBrowserPluginHostFactory>;
-
- scoped_refptr<MessageLoopRunner> message_loop_runner_;
- int guest_manager_instance_count_;
-
- DISALLOW_COPY_AND_ASSIGN(TestBrowserPluginHostFactory);
-};
-
-// Test factory class for browser plugin that creates guests with short hang
-// timeout.
-class TestShortHangTimeoutGuestFactory : public TestBrowserPluginHostFactory {
- public:
- virtual BrowserPluginGuest* CreateBrowserPluginGuest(
- int instance_id, WebContentsImpl* web_contents) OVERRIDE {
- TestBrowserPluginGuest* guest =
- new TestBrowserPluginGuest(instance_id, web_contents);
- guest->set_guest_hang_timeout(TestTimeouts::tiny_timeout());
- return guest;
- }
-
- // Singleton getter.
- static TestShortHangTimeoutGuestFactory* GetInstance() {
- return Singleton<TestShortHangTimeoutGuestFactory>::get();
- }
-
- protected:
- TestShortHangTimeoutGuestFactory() {}
- virtual ~TestShortHangTimeoutGuestFactory() {}
-
- private:
- // For Singleton.
- friend struct DefaultSingletonTraits<TestShortHangTimeoutGuestFactory>;
-
- DISALLOW_COPY_AND_ASSIGN(TestShortHangTimeoutGuestFactory);
-};
-
-// A transparent observer that can be used to verify that a RenderViewHost
-// received a specific message.
-class MessageObserver : public WebContentsObserver {
- public:
- MessageObserver(WebContents* web_contents, uint32 message_id)
- : WebContentsObserver(web_contents),
- message_id_(message_id),
- message_received_(false) {
- }
-
- virtual ~MessageObserver() {}
-
- void WaitUntilMessageReceived() {
- if (message_received_)
- return;
- message_loop_runner_ = new MessageLoopRunner();
- message_loop_runner_->Run();
- }
-
- void ResetState() {
- message_received_ = false;
- }
-
- // IPC::Listener implementation.
- virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
- if (message.type() == message_id_) {
- message_received_ = true;
- if (message_loop_runner_.get())
- message_loop_runner_->Quit();
- }
- return false;
- }
-
- private:
- scoped_refptr<MessageLoopRunner> message_loop_runner_;
- uint32 message_id_;
- bool message_received_;
-
- DISALLOW_COPY_AND_ASSIGN(MessageObserver);
-};
-
-class BrowserPluginHostTest : public ContentBrowserTest {
- public:
- BrowserPluginHostTest()
- : test_embedder_(NULL),
- test_guest_(NULL),
- test_guest_manager_(NULL) {}
-
- virtual void SetUp() OVERRIDE {
- // Override factory to create tests instances of BrowserPlugin*.
- content::BrowserPluginEmbedder::set_factory_for_testing(
- TestBrowserPluginHostFactory::GetInstance());
- content::BrowserPluginGuest::set_factory_for_testing(
- TestBrowserPluginHostFactory::GetInstance());
- content::BrowserPluginGuestManager::set_factory_for_testing(
- TestBrowserPluginHostFactory::GetInstance());
-
- // On legacy windows, the AcceptDragEvents test needs this to pass.
-#if defined(OS_WIN) && !defined(USE_AURA)
- UseRealGLBindings();
-#endif
- // We need real contexts, otherwise the embedder doesn't composite, but the
- // guest does, and that isn't an expected configuration.
- UseRealGLContexts();
-
- ContentBrowserTest::SetUp();
- }
- virtual void TearDown() OVERRIDE {
- content::BrowserPluginEmbedder::set_factory_for_testing(NULL);
- content::BrowserPluginGuest::set_factory_for_testing(NULL);
-
- ContentBrowserTest::TearDown();
- }
-
- virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
- // Enable browser plugin in content_shell for running test.
- command_line->AppendSwitch(switches::kEnableBrowserPluginForAllViewTypes);
- }
-
- static void SimulateSpaceKeyPress(WebContents* web_contents) {
- SimulateKeyPress(web_contents,
- ui::VKEY_SPACE,
- false, // control.
- false, // shift.
- false, // alt.
- false); // command.
- }
-
- static void SimulateTabKeyPress(WebContents* web_contents) {
- SimulateKeyPress(web_contents,
- ui::VKEY_TAB,
- false, // control.
- false, // shift.
- false, // alt.
- false); // command.
- }
-
- // Executes the javascript synchronously and makes sure the returned value is
- // freed properly.
- void ExecuteSyncJSFunction(RenderViewHost* rvh, const std::string& jscript) {
- scoped_ptr<base::Value> value =
- content::ExecuteScriptAndGetValue(rvh, jscript);
- }
-
- bool IsAttributeNull(RenderViewHost* rvh, const std::string& attribute) {
- scoped_ptr<base::Value> value = content::ExecuteScriptAndGetValue(rvh,
- "document.getElementById('plugin').getAttribute('" + attribute + "');");
- return value->GetType() == Value::TYPE_NULL;
- }
-
- // Removes all attributes in the comma-delimited string |attributes|.
- void RemoveAttributes(RenderViewHost* rvh, const std::string& attributes) {
- std::vector<std::string> attributes_list;
- base::SplitString(attributes, ',', &attributes_list);
- std::vector<std::string>::const_iterator itr;
- for (itr = attributes_list.begin(); itr != attributes_list.end(); ++itr) {
- ExecuteSyncJSFunction(rvh, "document.getElementById('plugin')"
- "." + *itr + " = null;");
- }
- }
-
- // This helper method does the following:
- // 1. Start the test server and navigate the shell to |embedder_url|.
- // 2. Execute custom pre-navigation |embedder_code| if provided.
- // 3. Navigate the guest to the |guest_url|.
- // 4. Verify that the guest has been created and has completed loading.
- void StartBrowserPluginTest(const std::string& embedder_url,
- const std::string& guest_url,
- bool is_guest_data_url,
- const std::string& embedder_code) {
- ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
- GURL test_url(embedded_test_server()->GetURL(embedder_url));
- NavigateToURL(shell(), test_url);
-
- WebContentsImpl* embedder_web_contents = static_cast<WebContentsImpl*>(
- shell()->web_contents());
- RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
- embedder_web_contents->GetRenderViewHost());
- // Focus the embedder.
- rvh->Focus();
- // Activative IME.
- rvh->SetInputMethodActive(true);
-
- // Allow the test to do some operations on the embedder before we perform
- // the first navigation of the guest.
- if (!embedder_code.empty())
- ExecuteSyncJSFunction(rvh, embedder_code);
-
- if (!is_guest_data_url) {
- test_url = embedded_test_server()->GetURL(guest_url);
- ExecuteSyncJSFunction(
- rvh, base::StringPrintf("SetSrc('%s');", test_url.spec().c_str()));
- } else {
- ExecuteSyncJSFunction(
- rvh, base::StringPrintf("SetSrc('%s');", guest_url.c_str()));
- }
-
- // Wait to make sure embedder is created/attached to WebContents.
- TestBrowserPluginHostFactory::GetInstance()->WaitForGuestManagerCreation();
-
- test_embedder_ = static_cast<TestBrowserPluginEmbedder*>(
- embedder_web_contents->GetBrowserPluginEmbedder());
- ASSERT_TRUE(test_embedder_);
-
- test_guest_manager_ = static_cast<TestBrowserPluginGuestManager*>(
- embedder_web_contents->GetBrowserPluginGuestManager());
- ASSERT_TRUE(test_guest_manager_);
-
- test_guest_manager_->WaitForGuestAdded();
-
- // Verify that we have exactly one guest.
- const TestBrowserPluginGuestManager::GuestInstanceMap& instance_map =
- test_guest_manager_->guest_web_contents_for_testing();
- EXPECT_EQ(1u, instance_map.size());
-
- WebContentsImpl* test_guest_web_contents = static_cast<WebContentsImpl*>(
- instance_map.begin()->second);
- test_guest_ = static_cast<TestBrowserPluginGuest*>(
- test_guest_web_contents->GetBrowserPluginGuest());
- test_guest_->WaitForLoadStop();
- }
-
- TestBrowserPluginEmbedder* test_embedder() const { return test_embedder_; }
- TestBrowserPluginGuest* test_guest() const { return test_guest_; }
- TestBrowserPluginGuestManager* test_guest_manager() const {
- return test_guest_manager_;
- }
-
- private:
- TestBrowserPluginEmbedder* test_embedder_;
- TestBrowserPluginGuest* test_guest_;
- TestBrowserPluginGuestManager* test_guest_manager_;
- DISALLOW_COPY_AND_ASSIGN(BrowserPluginHostTest);
-};
-
-// This test ensures that if guest isn't there and we resize the guest (from
-// js), it remembers the size correctly.
-//
-// Initially we load an embedder with a guest without a src attribute (which has
-// dimension 640x480), resize it to 100x200, and then we set the source to a
-// sample guest. In the end we verify that the correct size has been set.
-IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, NavigateAfterResize) {
- const gfx::Size nxt_size = gfx::Size(100, 200);
- const std::string embedder_code = base::StringPrintf(
- "SetSize(%d, %d);", nxt_size.width(), nxt_size.height());
- const char kEmbedderURL[] = "/browser_plugin_embedder.html";
- StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, embedder_code);
-
- // Wait for the guest to receive a damage buffer of size 100x200.
- // This means the guest will be painted properly at that size.
- test_guest()->WaitForDamageBufferWithSize(nxt_size);
-}
-
-IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, AdvanceFocus) {
- const char kEmbedderURL[] = "/browser_plugin_focus.html";
- const char* kGuestURL = "/browser_plugin_focus_child.html";
- StartBrowserPluginTest(kEmbedderURL, kGuestURL, false, std::string());
-
- SimulateMouseClick(test_embedder()->web_contents(), 0,
- blink::WebMouseEvent::ButtonLeft);
- BrowserPluginHostTest::SimulateTabKeyPress(test_embedder()->web_contents());
- // Wait until we focus into the guest.
- test_guest()->WaitForFocus();
-
- // TODO(fsamuel): A third Tab key press should not be necessary.
- // The browser plugin will take keyboard focus but it will not
- // focus an initial element. The initial element is dependent
- // upon tab direction which WebKit does not propagate to the plugin.
- // See http://crbug.com/147644.
- BrowserPluginHostTest::SimulateTabKeyPress(test_embedder()->web_contents());
- BrowserPluginHostTest::SimulateTabKeyPress(test_embedder()->web_contents());
- BrowserPluginHostTest::SimulateTabKeyPress(test_embedder()->web_contents());
- test_guest()->WaitForAdvanceFocus();
-}
-
-// This test opens a page in http and then opens another page in https, forcing
-// a RenderViewHost swap in the web_contents. We verify that the embedder in the
-// web_contents gets cleared properly.
-IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, EmbedderChangedAfterSwap) {
- net::SpawnedTestServer https_server(
- net::SpawnedTestServer::TYPE_HTTPS,
- net::SpawnedTestServer::kLocalhost,
- base::FilePath(FILE_PATH_LITERAL("content/test/data")));
- ASSERT_TRUE(https_server.Start());
-
- // 1. Load an embedder page with one guest in it.
- const char kEmbedderURL[] = "/browser_plugin_embedder.html";
- StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
-
- // 2. Navigate to a URL in https, so we trigger a RenderViewHost swap.
- GURL test_https_url(https_server.GetURL(
- "files/browser_plugin_title_change.html"));
- content::WindowedNotificationObserver swap_observer(
- content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
- content::Source<WebContents>(test_embedder()->web_contents()));
- NavigateToURL(shell(), test_https_url);
- swap_observer.Wait();
-
- TestBrowserPluginEmbedder* test_embedder_after_swap =
- static_cast<TestBrowserPluginEmbedder*>(
- static_cast<WebContentsImpl*>(shell()->web_contents())->
- GetBrowserPluginEmbedder());
- // Verify we have a no embedder in web_contents (since the new page doesn't
- // have any browser plugin).
- ASSERT_TRUE(!test_embedder_after_swap);
- ASSERT_NE(test_embedder(), test_embedder_after_swap);
-}
-
-// This test opens two pages in http and there is no RenderViewHost swap,
-// therefore the embedder created on first page navigation stays the same in
-// web_contents.
-// Failing flakily on Windows: crbug.com/308405
-#if defined(OS_WIN)
-#define MAYBE_EmbedderSameAfterNav DISABLED_EmbedderSameAfterNav
-#else
-#define MAYBE_EmbedderSameAfterNav EmbedderSameAfterNav
-#endif
-IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, MAYBE_EmbedderSameAfterNav) {
- const char kEmbedderURL[] = "/browser_plugin_embedder.html";
- StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
- WebContentsImpl* embedder_web_contents = test_embedder()->web_contents();
-
- // Navigate to another page in same host and port, so RenderViewHost swap
- // does not happen and existing embedder doesn't change in web_contents.
- GURL test_url_new(embedded_test_server()->GetURL(
- "/browser_plugin_title_change.html"));
- const base::string16 expected_title = ASCIIToUTF16("done");
- content::TitleWatcher title_watcher(shell()->web_contents(), expected_title);
- NavigateToURL(shell(), test_url_new);
- VLOG(0) << "Start waiting for title";
- base::string16 actual_title = title_watcher.WaitAndGetTitle();
- EXPECT_EQ(expected_title, actual_title);
- VLOG(0) << "Done navigating to second page";
-
- TestBrowserPluginEmbedder* test_embedder_after_nav =
- static_cast<TestBrowserPluginEmbedder*>(
- embedder_web_contents->GetBrowserPluginEmbedder());
- // Embedder must not change in web_contents.
- ASSERT_EQ(test_embedder_after_nav, test_embedder());
-}
-
-// This test verifies that hiding the embedder also hides the guest.
-IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, BrowserPluginVisibilityChanged) {
- const char kEmbedderURL[] = "/browser_plugin_embedder.html";
- StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
-
- // Hide the Browser Plugin.
- RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
- test_embedder()->web_contents()->GetRenderViewHost());
- ExecuteSyncJSFunction(
- rvh, "document.getElementById('plugin').style.visibility = 'hidden'");
-
- // Make sure that the guest is hidden.
- test_guest()->WaitUntilHidden();
-}
-
-IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, EmbedderVisibilityChanged) {
- const char kEmbedderURL[] = "/browser_plugin_embedder.html";
- StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
-
- // Hide the embedder.
- test_embedder()->web_contents()->WasHidden();
-
- // Make sure that hiding the embedder also hides the guest.
- test_guest()->WaitUntilHidden();
-}
-
-// Verifies that installing/uninstalling touch-event handlers in the guest
-// plugin correctly updates the touch-event handling state in the embedder.
-IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, AcceptTouchEvents) {
- const char kEmbedderURL[] = "/browser_plugin_embedder.html";
- StartBrowserPluginTest(
- kEmbedderURL, kHTMLForGuestTouchHandler, true, std::string());
-
- RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
- test_embedder()->web_contents()->GetRenderViewHost());
- // The embedder should not have any touch event handlers at this point.
- EXPECT_FALSE(rvh->has_touch_handler());
-
- // Install the touch handler in the guest. This should cause the embedder to
- // start listening for touch events too.
- MessageObserver observer(test_embedder()->web_contents(),
- ViewHostMsg_HasTouchEventHandlers::ID);
- ExecuteSyncJSFunction(test_guest()->web_contents()->GetRenderViewHost(),
- "InstallTouchHandler();");
- observer.WaitUntilMessageReceived();
- EXPECT_TRUE(rvh->has_touch_handler());
-
- // Uninstalling the touch-handler in guest should cause the embedder to stop
- // listening for touch events.
- observer.ResetState();
- ExecuteSyncJSFunction(test_guest()->web_contents()->GetRenderViewHost(),
- "UninstallTouchHandler();");
- observer.WaitUntilMessageReceived();
- EXPECT_FALSE(rvh->has_touch_handler());
-}
-
-// This tests verifies that reloading the embedder does not crash the browser
-// and that the guest is reset.
-IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, ReloadEmbedder) {
- const char kEmbedderURL[] = "/browser_plugin_embedder.html";
- StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
- RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
- test_embedder()->web_contents()->GetRenderViewHost());
-
- // Change the title of the page to 'modified' so that we know that
- // the page has successfully reloaded when it goes back to 'embedder'
- // in the next step.
- {
- const base::string16 expected_title = ASCIIToUTF16("modified");
- content::TitleWatcher title_watcher(test_embedder()->web_contents(),
- expected_title);
-
- ExecuteSyncJSFunction(rvh,
- base::StringPrintf("SetTitle('%s');", "modified"));
-
- base::string16 actual_title = title_watcher.WaitAndGetTitle();
- EXPECT_EQ(expected_title, actual_title);
- }
-
- // Reload the embedder page, and verify that the reload was successful.
- // Then navigate the guest to verify that the browser process does not crash.
- {
- const base::string16 expected_title = ASCIIToUTF16("embedder");
- content::TitleWatcher title_watcher(test_embedder()->web_contents(),
- expected_title);
-
- test_embedder()->web_contents()->GetController().Reload(false);
- base::string16 actual_title = title_watcher.WaitAndGetTitle();
- EXPECT_EQ(expected_title, actual_title);
-
- ExecuteSyncJSFunction(
- test_embedder()->web_contents()->GetRenderViewHost(),
- base::StringPrintf("SetSrc('%s');", kHTMLForGuest));
- test_guest_manager()->WaitForGuestAdded();
-
- const TestBrowserPluginGuestManager::GuestInstanceMap& instance_map =
- test_guest_manager()->guest_web_contents_for_testing();
- WebContentsImpl* test_guest_web_contents = static_cast<WebContentsImpl*>(
- instance_map.begin()->second);
- TestBrowserPluginGuest* new_test_guest =
- static_cast<TestBrowserPluginGuest*>(
- test_guest_web_contents->GetBrowserPluginGuest());
- ASSERT_TRUE(new_test_guest != NULL);
-
- // Wait for the guest to send an UpdateRectMsg, meaning it is ready.
- new_test_guest->WaitForUpdateRectMsg();
- }
-}
-
-// Always failing in the win7_aura try bot. See http://crbug.com/181107.
-// Times out on the Mac. See http://crbug.com/297576.
-#if (defined(OS_WIN) && defined(USE_AURA)) || defined(OS_MACOSX)
-#define MAYBE_AcceptDragEvents DISABLED_AcceptDragEvents
-#else
-#define MAYBE_AcceptDragEvents AcceptDragEvents
-#endif
-
-// Tests that a drag-n-drop over the browser plugin in the embedder happens
-// correctly.
-IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, MAYBE_AcceptDragEvents) {
- const char kEmbedderURL[] = "/browser_plugin_dragging.html";
- StartBrowserPluginTest(
- kEmbedderURL, kHTMLForGuestAcceptDrag, true, std::string());
-
- RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
- test_embedder()->web_contents()->GetRenderViewHost());
-
- // Get a location in the embedder outside of the plugin.
- base::ListValue *start, *end;
- scoped_ptr<base::Value> value =
- content::ExecuteScriptAndGetValue(rvh, "dragLocation()");
- ASSERT_TRUE(value->GetAsList(&start) && start->GetSize() == 2);
- double start_x, start_y;
- ASSERT_TRUE(start->GetDouble(0, &start_x) && start->GetDouble(1, &start_y));
-
- // Get a location in the embedder that falls inside the plugin.
- value = content::ExecuteScriptAndGetValue(rvh, "dropLocation()");
- ASSERT_TRUE(value->GetAsList(&end) && end->GetSize() == 2);
- double end_x, end_y;
- ASSERT_TRUE(end->GetDouble(0, &end_x) && end->GetDouble(1, &end_y));
-
- DropData drop_data;
- GURL url = GURL("https://www.domain.com/index.html");
- drop_data.url = url;
-
- // Pretend that the URL is being dragged over the embedder. Start the drag
- // from outside the plugin, then move the drag inside the plugin and drop.
- // This should trigger appropriate messages from the embedder to the guest,
- // and end with a drop on the guest. The guest changes title when a drop
- // happens.
- const base::string16 expected_title = ASCIIToUTF16("DROPPED");
- content::TitleWatcher title_watcher(test_guest()->web_contents(),
- expected_title);
-
- rvh->DragTargetDragEnter(drop_data, gfx::Point(start_x, start_y),
- gfx::Point(start_x, start_y), blink::WebDragOperationEvery, 0);
- rvh->DragTargetDragOver(gfx::Point(end_x, end_y), gfx::Point(end_x, end_y),
- blink::WebDragOperationEvery, 0);
- rvh->DragTargetDrop(gfx::Point(end_x, end_y), gfx::Point(end_x, end_y), 0);
-
- base::string16 actual_title = title_watcher.WaitAndGetTitle();
- EXPECT_EQ(expected_title, actual_title);
-}
-
-// This test verifies that round trip postMessage works as expected.
-// 1. The embedder posts a message 'testing123' to the guest.
-// 2. The guest receives and replies to the message using the event object's
-// source object: event.source.postMessage('foobar', '*')
-// 3. The embedder receives the message and uses the event's source
-// object to do one final reply: 'stop'
-// 4. The guest receives the final 'stop' message.
-// 5. The guest acks the 'stop' message with a 'stop_ack' message.
-// 6. The embedder changes its title to 'main guest' when it sees the 'stop_ack'
-// message.
-IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, PostMessage) {
- const char* kTesting = "testing123";
- const char* kEmbedderURL = "/browser_plugin_embedder.html";
- const char* kGuestURL = "/browser_plugin_post_message_guest.html";
- StartBrowserPluginTest(kEmbedderURL, kGuestURL, false, std::string());
- RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
- test_embedder()->web_contents()->GetRenderViewHost());
- {
- const base::string16 expected_title = ASCIIToUTF16("main guest");
- content::TitleWatcher title_watcher(test_embedder()->web_contents(),
- expected_title);
-
- // By the time we get here 'contentWindow' should be ready because the
- // guest has completed loading.
- ExecuteSyncJSFunction(
- rvh, base::StringPrintf("PostMessage('%s, false');", kTesting));
-
- // The title will be updated to "main guest" at the last stage of the
- // process described above.
- base::string16 actual_title = title_watcher.WaitAndGetTitle();
- EXPECT_EQ(expected_title, actual_title);
- }
-}
-
-// This is the same as BrowserPluginHostTest.PostMessage but also
-// posts a message to an iframe.
-// TODO(fsamuel): This test should replace the previous test once postMessage
-// iframe targeting is fixed (see http://crbug.com/153701).
-IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, DISABLED_PostMessageToIFrame) {
- const char* kTesting = "testing123";
- const char* kEmbedderURL = "/browser_plugin_embedder.html";
- const char* kGuestURL = "/browser_plugin_post_message_guest.html";
- StartBrowserPluginTest(kEmbedderURL, kGuestURL, false, std::string());
- RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
- test_embedder()->web_contents()->GetRenderViewHost());
- {
- const base::string16 expected_title = ASCIIToUTF16("main guest");
- content::TitleWatcher title_watcher(test_embedder()->web_contents(),
- expected_title);
-
- ExecuteSyncJSFunction(
- rvh, base::StringPrintf("PostMessage('%s, false');", kTesting));
-
- // The title will be updated to "main guest" at the last stage of the
- // process described above.
- base::string16 actual_title = title_watcher.WaitAndGetTitle();
- EXPECT_EQ(expected_title, actual_title);
- }
- {
- content::TitleWatcher ready_watcher(test_embedder()->web_contents(),
- ASCIIToUTF16("ready"));
-
- RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>(
- test_guest()->web_contents()->GetRenderViewHost());
- GURL test_url = embedded_test_server()->GetURL(
- "/browser_plugin_post_message_guest.html");
- ExecuteSyncJSFunction(
- guest_rvh,
- base::StringPrintf(
- "CreateChildFrame('%s');", test_url.spec().c_str()));
-
- base::string16 actual_title = ready_watcher.WaitAndGetTitle();
- EXPECT_EQ(ASCIIToUTF16("ready"), actual_title);
-
- content::TitleWatcher iframe_watcher(test_embedder()->web_contents(),
- ASCIIToUTF16("iframe"));
- ExecuteSyncJSFunction(
- rvh, base::StringPrintf("PostMessage('%s', true);", kTesting));
-
- // The title will be updated to "iframe" at the last stage of the
- // process described above.
- actual_title = iframe_watcher.WaitAndGetTitle();
- EXPECT_EQ(ASCIIToUTF16("iframe"), actual_title);
- }
-}
-
-// This test verifies that if a browser plugin is hidden before navigation,
-// the guest starts off hidden.
-IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, HiddenBeforeNavigation) {
- const char* kEmbedderURL = "/browser_plugin_embedder.html";
- const std::string embedder_code =
- "document.getElementById('plugin').style.visibility = 'hidden'";
- StartBrowserPluginTest(
- kEmbedderURL, kHTMLForGuest, true, embedder_code);
- EXPECT_FALSE(test_guest()->visible());
-}
-
-// This test verifies that if a browser plugin is focused before navigation then
-// the guest starts off focused.
-IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, FocusBeforeNavigation) {
- const char* kEmbedderURL = "/browser_plugin_embedder.html";
- const std::string embedder_code =
- "document.getElementById('plugin').focus();";
- StartBrowserPluginTest(
- kEmbedderURL, kHTMLForGuest, true, embedder_code);
- RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>(
- test_guest()->web_contents()->GetRenderViewHost());
- // Verify that the guest is focused.
- scoped_ptr<base::Value> value =
- content::ExecuteScriptAndGetValue(guest_rvh, "document.hasFocus()");
- bool result = false;
- ASSERT_TRUE(value->GetAsBoolean(&result));
- EXPECT_TRUE(result);
-}
-
-IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, FocusTracksEmbedder) {
- const char* kEmbedderURL = "/browser_plugin_embedder.html";
- StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
- // Blur the embedder.
- test_embedder()->web_contents()->GetRenderViewHost()->Blur();
- // Ensure that the guest is also blurred.
- test_guest()->WaitForBlur();
-}
-
-// Test for regression http://crbug.com/162961.
-IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, GetRenderViewHostAtPositionTest) {
- const char kEmbedderURL[] = "/browser_plugin_embedder.html";
- const std::string embedder_code =
- base::StringPrintf("SetSize(%d, %d);", 100, 100);
- StartBrowserPluginTest(kEmbedderURL, kHTMLForGuestWithSize, true,
- embedder_code);
- // Check for render view host at position (150, 150) that is outside the
- // bounds of our guest, so this would respond with the render view host of the
- // embedder.
- test_embedder()->WaitForRenderViewHostAtPosition(150, 150);
- ASSERT_EQ(test_embedder()->web_contents()->GetRenderViewHost(),
- test_embedder()->last_rvh_at_position_response());
-}
-
-// This test verifies that if IME is enabled in the embedder, it is also enabled
-// in the guest.
-IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, VerifyInputMethodActive) {
- const char* kEmbedderURL = "/browser_plugin_embedder.html";
- StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
- RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
- test_guest()->web_contents()->GetRenderViewHost());
- EXPECT_TRUE(rvh->input_method_active());
-}
-
-// Verify that navigating to an invalid URL (e.g. 'http:') doesn't cause
-// a crash.
-IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, DoNotCrashOnInvalidNavigation) {
- const char kEmbedderURL[] = "/browser_plugin_embedder.html";
- StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
- TestBrowserPluginGuestDelegate* delegate =
- new TestBrowserPluginGuestDelegate();
- test_guest()->SetDelegate(delegate);
-
- const char kValidSchemeWithEmptyURL[] = "http:";
- ExecuteSyncJSFunction(
- test_embedder()->web_contents()->GetRenderViewHost(),
- base::StringPrintf("SetSrc('%s');", kValidSchemeWithEmptyURL));
- EXPECT_TRUE(delegate->load_aborted());
- EXPECT_FALSE(delegate->load_aborted_url().is_valid());
- EXPECT_EQ(kValidSchemeWithEmptyURL,
- delegate->load_aborted_url().possibly_invalid_spec());
-
- delegate->ResetStates();
-
- // Attempt a navigation to chrome-guest://abc123, which is a valid URL. But it
- // should be blocked because the scheme isn't web-safe or a pseudo-scheme.
- ExecuteSyncJSFunction(
- test_embedder()->web_contents()->GetRenderViewHost(),
- base::StringPrintf("SetSrc('%s://abc123');", kGuestScheme));
- EXPECT_TRUE(delegate->load_aborted());
- EXPECT_TRUE(delegate->load_aborted_url().is_valid());
-}
-
-// Tests involving the threaded compositor.
-class BrowserPluginThreadedCompositorTest : public BrowserPluginHostTest {
- public:
- BrowserPluginThreadedCompositorTest() {}
- virtual ~BrowserPluginThreadedCompositorTest() {}
-
- protected:
- virtual void SetUpCommandLine(CommandLine* cmd) OVERRIDE {
- BrowserPluginHostTest::SetUpCommandLine(cmd);
- cmd->AppendSwitch(switches::kEnableThreadedCompositing);
-
- // http://crbug.com/327035
- cmd->AppendSwitch(switches::kDisableDelegatedRenderer);
- }
-};
-
-static void CompareSkBitmaps(const SkBitmap& expected_bitmap,
- const SkBitmap& bitmap) {
- EXPECT_EQ(expected_bitmap.width(), bitmap.width());
- if (expected_bitmap.width() != bitmap.width())
- return;
- EXPECT_EQ(expected_bitmap.height(), bitmap.height());
- if (expected_bitmap.height() != bitmap.height())
- return;
- EXPECT_EQ(expected_bitmap.config(), bitmap.config());
- if (expected_bitmap.config() != bitmap.config())
- return;
-
- SkAutoLockPixels expected_bitmap_lock(expected_bitmap);
- SkAutoLockPixels bitmap_lock(bitmap);
- int fails = 0;
- const int kAllowableError = 2;
- for (int i = 0; i < bitmap.width() && fails < 10; ++i) {
- for (int j = 0; j < bitmap.height() && fails < 10; ++j) {
- SkColor expected_color = expected_bitmap.getColor(i, j);
- SkColor color = bitmap.getColor(i, j);
- int expected_alpha = SkColorGetA(expected_color);
- int alpha = SkColorGetA(color);
- int expected_red = SkColorGetR(expected_color);
- int red = SkColorGetR(color);
- int expected_green = SkColorGetG(expected_color);
- int green = SkColorGetG(color);
- int expected_blue = SkColorGetB(expected_color);
- int blue = SkColorGetB(color);
- EXPECT_NEAR(expected_alpha, alpha, kAllowableError)
- << "expected_color: " << std::hex << expected_color
- << " color: " << color
- << " Failed at " << std::dec << i << ", " << j
- << " Failure " << ++fails;
- EXPECT_NEAR(expected_red, red, kAllowableError)
- << "expected_color: " << std::hex << expected_color
- << " color: " << color
- << " Failed at " << std::dec << i << ", " << j
- << " Failure " << ++fails;
- EXPECT_NEAR(expected_green, green, kAllowableError)
- << "expected_color: " << std::hex << expected_color
- << " color: " << color
- << " Failed at " << std::dec << i << ", " << j
- << " Failure " << ++fails;
- EXPECT_NEAR(expected_blue, blue, kAllowableError)
- << "expected_color: " << std::hex << expected_color
- << " color: " << color
- << " Failed at " << std::dec << i << ", " << j
- << " Failure " << ++fails;
- }
- }
- EXPECT_LT(fails, 10);
-}
-
-static void CompareSkBitmapAndRun(const base::Closure& callback,
- const SkBitmap& expected_bitmap,
- bool *result,
- bool succeed,
- const SkBitmap& bitmap) {
- *result = succeed;
- if (succeed)
- CompareSkBitmaps(expected_bitmap, bitmap);
- callback.Run();
-}
-
-// http://crbug.com/171744
-#if defined(OS_MACOSX)
-#define MAYBE_GetBackingStore DISABLED_GetBackingStore
-#else
-#define MAYBE_GetBackingStore GetBackingStore
-#endif
-IN_PROC_BROWSER_TEST_F(BrowserPluginThreadedCompositorTest,
- MAYBE_GetBackingStore) {
- const char kEmbedderURL[] = "/browser_plugin_embedder.html";
- const char kHTMLForGuest[] =
- "data:text/html,<html><style>body { background-color: red; }</style>"
- "<body></body></html>";
- StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true,
- std::string("SetSize(50, 60);"));
-
- WebContentsImpl* guest_contents = test_guest()->web_contents();
- RenderWidgetHostImpl* guest_widget_host =
- RenderWidgetHostImpl::From(guest_contents->GetRenderViewHost());
-
- SkBitmap expected_bitmap;
- expected_bitmap.setConfig(SkBitmap::kARGB_8888_Config, 50, 60);
- expected_bitmap.allocPixels();
- expected_bitmap.eraseARGB(255, 255, 0, 0); // #f00
- bool result = false;
- while (!result) {
- base::RunLoop loop;
- guest_widget_host->CopyFromBackingStore(gfx::Rect(),
- guest_widget_host->GetView()->GetViewBounds().size(),
- base::Bind(&CompareSkBitmapAndRun, loop.QuitClosure(), expected_bitmap,
- &result));
- loop.Run();
- }
-}
-
-// Tests input method.
-IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, InputMethod) {
- const char kEmbedderURL[] = "/browser_plugin_embedder.html";
- const char kGuestHTML[] = "data:text/html,"
- "<html><body><input id=\"input1\">"
- "<input id=\"input2\"></body>"
- "<script>"
- "var i = document.getElementById(\"input1\");"
- "i.oninput = function() {"
- " document.title = i.value;"
- "}"
- "</script>"
- "</html>";
- StartBrowserPluginTest(kEmbedderURL, kGuestHTML, true,
- "document.getElementById(\"plugin\").focus();");
-
- RenderViewHostImpl* embedder_rvh = static_cast<RenderViewHostImpl*>(
- test_embedder()->web_contents()->GetRenderViewHost());
- RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>(
- test_guest()->web_contents()->GetRenderViewHost());
-
- std::vector<blink::WebCompositionUnderline> underlines;
-
- // An input field in browser plugin guest gets focus and given some user
- // input via IME.
- {
- ExecuteSyncJSFunction(guest_rvh,
- "document.getElementById('input1').focus();");
- string16 expected_title = UTF8ToUTF16("InputTest123");
- content::TitleWatcher title_watcher(test_guest()->web_contents(),
- expected_title);
- embedder_rvh->Send(
- new ViewMsg_ImeSetComposition(
- test_embedder()->web_contents()->GetRoutingID(),
- expected_title,
- underlines,
- 12, 12));
- base::string16 actual_title = title_watcher.WaitAndGetTitle();
- EXPECT_EQ(expected_title, actual_title);
- }
- // A composition is committed via IME.
- {
- string16 expected_title = UTF8ToUTF16("InputTest456");
- content::TitleWatcher title_watcher(test_guest()->web_contents(),
- expected_title);
- embedder_rvh->Send(
- new ViewMsg_ImeConfirmComposition(
- test_embedder()->web_contents()->GetRoutingID(),
- expected_title,
- gfx::Range(),
- true));
- base::string16 actual_title = title_watcher.WaitAndGetTitle();
- EXPECT_EQ(expected_title, actual_title);
- }
- // IME composition starts, but focus moves out, then the composition will
- // be committed and get cancel msg.
- {
- ExecuteSyncJSFunction(guest_rvh,
- "document.getElementById('input1').value = '';");
- string16 composition = UTF8ToUTF16("InputTest789");
- content::TitleWatcher title_watcher(test_guest()->web_contents(),
- composition);
- embedder_rvh->Send(
- new ViewMsg_ImeSetComposition(
- test_embedder()->web_contents()->GetRoutingID(),
- composition,
- underlines,
- 12, 12));
- base::string16 actual_title = title_watcher.WaitAndGetTitle();
- EXPECT_EQ(composition, actual_title);
- // Moving focus causes IME cancel, and the composition will be committed
- // in input1, not in input2.
- ExecuteSyncJSFunction(guest_rvh,
- "document.getElementById('input2').focus();");
- test_guest()->WaitForImeCancel();
- scoped_ptr<base::Value> value =
- content::ExecuteScriptAndGetValue(
- guest_rvh, "document.getElementById('input1').value");
- std::string result;
- ASSERT_TRUE(value->GetAsString(&result));
- EXPECT_EQ(UTF16ToUTF8(composition), result);
- }
- // Tests ExtendSelectionAndDelete message works in browser_plugin.
- {
- // Set 'InputTestABC' in input1 and put caret at 6 (after 'T').
- ExecuteSyncJSFunction(guest_rvh,
- "var i = document.getElementById('input1');"
- "i.focus();"
- "i.value = 'InputTestABC';"
- "i.selectionStart=6;"
- "i.selectionEnd=6;");
- string16 expected_value = UTF8ToUTF16("InputABC");
- content::TitleWatcher title_watcher(test_guest()->web_contents(),
- expected_value);
- // Delete 'Test' in 'InputTestABC', as the caret is after 'T':
- // delete before 1 character ('T') and after 3 characters ('est').
- embedder_rvh->Send(
- new ViewMsg_ExtendSelectionAndDelete(
- test_embedder()->web_contents()->GetRoutingID(),
- 1, 3));
- base::string16 actual_title = title_watcher.WaitAndGetTitle();
- EXPECT_EQ(expected_value, actual_title);
- scoped_ptr<base::Value> value =
- content::ExecuteScriptAndGetValue(
- guest_rvh, "document.getElementById('input1').value");
- std::string actual_value;
- ASSERT_TRUE(value->GetAsString(&actual_value));
- EXPECT_EQ(UTF16ToUTF8(expected_value), actual_value);
- }
-}
-
-} // namespace content
diff --git a/chromium/content/browser/browser_plugin/browser_plugin_host_factory.h b/chromium/content/browser/browser_plugin/browser_plugin_host_factory.h
deleted file mode 100644
index 9bfc1e2d740..00000000000
--- a/chromium/content/browser/browser_plugin/browser_plugin_host_factory.h
+++ /dev/null
@@ -1,41 +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_BROWSER_PLUGIN_BROWSER_PLUGIN_HOST_FACTORY_H_
-#define CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_HOST_FACTORY_H_
-
-#include "base/base_export.h"
-#include "base/basictypes.h"
-#include "base/compiler_specific.h"
-#include "base/strings/string16.h"
-#include "content/common/content_export.h"
-
-struct BrowserPluginHostMsg_CreateGuest_Params;
-
-namespace content {
-
-class BrowserPluginEmbedder;
-class BrowserPluginGuest;
-class RenderViewHost;
-class WebContentsImpl;
-
-// Factory to create BrowserPlugin embedder and guest.
-class CONTENT_EXPORT BrowserPluginHostFactory {
- public:
- virtual BrowserPluginGuestManager* CreateBrowserPluginGuestManager() = 0;
-
- virtual BrowserPluginGuest* CreateBrowserPluginGuest(
- int instance_id,
- WebContentsImpl* web_contents) = 0;
-
- virtual BrowserPluginEmbedder* CreateBrowserPluginEmbedder(
- WebContentsImpl* web_contents) = 0;
-
- protected:
- virtual ~BrowserPluginHostFactory() {}
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_HOST_FACTORY_H_
diff --git a/chromium/content/browser/browser_plugin/browser_plugin_message_filter.cc b/chromium/content/browser/browser_plugin/browser_plugin_message_filter.cc
index 031797a46c4..e82fb7a031a 100644
--- a/chromium/content/browser/browser_plugin/browser_plugin_message_filter.cc
+++ b/chromium/content/browser/browser_plugin/browser_plugin_message_filter.cc
@@ -6,21 +6,22 @@
#include "base/supports_user_data.h"
#include "content/browser/browser_plugin/browser_plugin_guest.h"
-#include "content/browser/browser_plugin/browser_plugin_guest_manager.h"
+#include "content/browser/gpu/gpu_process_host.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/browser_plugin/browser_plugin_constants.h"
#include "content/common/browser_plugin/browser_plugin_messages.h"
+#include "content/common/gpu/gpu_messages.h"
#include "content/common/view_messages.h"
#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_plugin_guest_manager.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_view_host.h"
namespace content {
-BrowserPluginMessageFilter::BrowserPluginMessageFilter(int render_process_id,
- bool is_guest)
- : render_process_id_(render_process_id),
- is_guest_(is_guest) {
+BrowserPluginMessageFilter::BrowserPluginMessageFilter(int render_process_id)
+ : BrowserMessageFilter(BrowserPluginMsgStart),
+ render_process_id_(render_process_id) {
}
BrowserPluginMessageFilter::~BrowserPluginMessageFilter() {
@@ -28,21 +29,24 @@ BrowserPluginMessageFilter::~BrowserPluginMessageFilter() {
}
bool BrowserPluginMessageFilter::OnMessageReceived(
- const IPC::Message& message,
- bool* message_was_ok) {
+ const IPC::Message& message) {
// Any message requested by a BrowserPluginGuest should be routed through
// a BrowserPluginGuestManager.
if (BrowserPluginGuest::ShouldForwardToBrowserPluginGuest(message)) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- BrowserPluginGuestManager* guest_manager = GetBrowserPluginGuestManager();
- if (guest_manager)
- guest_manager->OnMessageReceived(message, render_process_id_);
+ ForwardMessageToGuest(message);
// We always swallow messages destined for BrowserPluginGuestManager because
// we're on the UI thread and fallback code is expected to be run on the IO
// thread.
return true;
}
- return false;
+ bool handled = true;
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ IPC_BEGIN_MESSAGE_MAP(BrowserPluginMessageFilter, message)
+ IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_BuffersSwappedACK,
+ OnSwapBuffersACK)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
}
void BrowserPluginMessageFilter::OnDestruct() const {
@@ -55,18 +59,45 @@ void BrowserPluginMessageFilter::OverrideThreadForMessage(
*thread = BrowserThread::UI;
}
-BrowserPluginGuestManager*
- BrowserPluginMessageFilter::GetBrowserPluginGuestManager() {
+static void BrowserPluginGuestMessageCallback(const IPC::Message& message,
+ WebContents* guest_web_contents) {
+ if (!guest_web_contents)
+ return;
+ static_cast<WebContentsImpl*>(guest_web_contents)->GetBrowserPluginGuest()->
+ OnMessageReceivedFromEmbedder(message);
+}
+
+void BrowserPluginMessageFilter::ForwardMessageToGuest(
+ const IPC::Message& message) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
RenderProcessHostImpl* host = static_cast<RenderProcessHostImpl*>(
RenderProcessHost::FromID(render_process_id_));
if (!host)
- return NULL;
+ return;
+
+ int instance_id = 0;
+ // All allowed messages must have instance_id as their first parameter.
+ PickleIterator iter(message);
+ bool success = iter.ReadInt(&instance_id);
+ DCHECK(success);
+ host->GetBrowserContext()->GetGuestManager()->
+ MaybeGetGuestByInstanceIDOrKill(
+ instance_id,
+ render_process_id_,
+ base::Bind(&BrowserPluginGuestMessageCallback,
+ message));
+}
- BrowserContext* browser_context = host->GetBrowserContext();
- return static_cast<BrowserPluginGuestManager*>(
- browser_context->GetUserData(
- browser_plugin::kBrowserPluginGuestManagerKeyName));
+void BrowserPluginMessageFilter::OnSwapBuffersACK(
+ const FrameHostMsg_BuffersSwappedACK_Params& params) {
+ GpuProcessHost* gpu_host = GpuProcessHost::FromID(params.gpu_host_id);
+ if (!gpu_host)
+ return;
+ AcceleratedSurfaceMsg_BufferPresented_Params ack_params;
+ ack_params.mailbox = params.mailbox;
+ ack_params.sync_point = params.sync_point;
+ gpu_host->Send(new AcceleratedSurfaceMsg_BufferPresented(params.gpu_route_id,
+ ack_params));
}
} // namespace content
diff --git a/chromium/content/browser/browser_plugin/browser_plugin_message_filter.h b/chromium/content/browser/browser_plugin/browser_plugin_message_filter.h
index 829c1badcbf..136c6cc58e6 100644
--- a/chromium/content/browser/browser_plugin/browser_plugin_message_filter.h
+++ b/chromium/content/browser/browser_plugin/browser_plugin_message_filter.h
@@ -7,23 +7,23 @@
#include "content/public/browser/browser_message_filter.h"
+struct FrameHostMsg_BuffersSwappedACK_Params;
+
namespace content {
class BrowserContext;
-class BrowserPluginGuestManager;
// This class filters out incoming IPC messages for the guest renderer process
// on the IPC thread before other message filters handle them.
class BrowserPluginMessageFilter : public BrowserMessageFilter {
public:
- BrowserPluginMessageFilter(int render_process_id, bool is_guest);
+ BrowserPluginMessageFilter(int render_process_id);
// BrowserMessageFilter implementation.
virtual void OverrideThreadForMessage(
const IPC::Message& message,
BrowserThread::ID* thread) OVERRIDE;
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
virtual void OnDestruct() const OVERRIDE;
private:
@@ -32,10 +32,11 @@ class BrowserPluginMessageFilter : public BrowserMessageFilter {
virtual ~BrowserPluginMessageFilter();
- BrowserPluginGuestManager* GetBrowserPluginGuestManager();
+ void ForwardMessageToGuest(const IPC::Message& message);
+
+ void OnSwapBuffersACK(const FrameHostMsg_BuffersSwappedACK_Params& params);
int render_process_id_;
- int is_guest_;
DISALLOW_COPY_AND_ASSIGN(BrowserPluginMessageFilter);
};
diff --git a/chromium/content/browser/browser_plugin/test_browser_plugin_embedder.cc b/chromium/content/browser/browser_plugin/test_browser_plugin_embedder.cc
deleted file mode 100644
index a513d0fd4be..00000000000
--- a/chromium/content/browser/browser_plugin/test_browser_plugin_embedder.cc
+++ /dev/null
@@ -1,40 +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/browser_plugin/test_browser_plugin_embedder.h"
-
-#include "content/browser/browser_plugin/browser_plugin_embedder.h"
-#include "content/browser/web_contents/web_contents_impl.h"
-
-namespace content {
-
-TestBrowserPluginEmbedder::TestBrowserPluginEmbedder(
- WebContentsImpl* web_contents)
- : BrowserPluginEmbedder(web_contents),
- last_rvh_at_position_response_(NULL) {
-}
-
-TestBrowserPluginEmbedder::~TestBrowserPluginEmbedder() {
-}
-
-void TestBrowserPluginEmbedder::GetRenderViewHostCallback(
- RenderViewHost* rvh, int x, int y) {
- last_rvh_at_position_response_ = rvh;
- if (message_loop_runner_.get())
- message_loop_runner_->Quit();
-}
-
-void TestBrowserPluginEmbedder::WaitForRenderViewHostAtPosition(int x, int y) {
- GetRenderViewHostAtPosition(x, y,
- base::Bind(&TestBrowserPluginEmbedder::GetRenderViewHostCallback,
- base::Unretained(this)));
- message_loop_runner_ = new MessageLoopRunner();
- message_loop_runner_->Run();
-}
-
-WebContentsImpl* TestBrowserPluginEmbedder::web_contents() const {
- return static_cast<WebContentsImpl*>(BrowserPluginEmbedder::web_contents());
-}
-
-} // namespace content
diff --git a/chromium/content/browser/browser_plugin/test_browser_plugin_embedder.h b/chromium/content/browser/browser_plugin/test_browser_plugin_embedder.h
deleted file mode 100644
index 4673f926305..00000000000
--- a/chromium/content/browser/browser_plugin/test_browser_plugin_embedder.h
+++ /dev/null
@@ -1,47 +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_BROWSER_PLUGIN_TEST_BROWSER_PLUGIN_EMBEDDER_H_
-#define CONTENT_BROWSER_BROWSER_PLUGIN_TEST_BROWSER_PLUGIN_EMBEDDER_H_
-
-#include "base/compiler_specific.h"
-#include "content/browser/browser_plugin/browser_plugin_embedder.h"
-#include "content/public/test/test_utils.h"
-
-namespace content {
-
-class BrowserPluginGuest;
-class RenderViewHost;
-class WebContentsImpl;
-
-// Test class for BrowserPluginEmbedder.
-//
-// Provides utilities to wait for certain state/messages in
-// BrowserPluginEmbedder to be used in tests.
-class TestBrowserPluginEmbedder : public BrowserPluginEmbedder {
- public:
- TestBrowserPluginEmbedder(WebContentsImpl* web_contents);
- virtual ~TestBrowserPluginEmbedder();
-
- // Asks the renderer process for RenderViewHost at (|x|, |y|) and waits until
- // the response arrives.
- void WaitForRenderViewHostAtPosition(int x, int y);
- RenderViewHost* last_rvh_at_position_response() {
- return last_rvh_at_position_response_;
- }
-
- WebContentsImpl* web_contents() const;
-
- private:
- void GetRenderViewHostCallback(RenderViewHost* rvh, int x, int y);
-
- scoped_refptr<MessageLoopRunner> message_loop_runner_;
- RenderViewHost* last_rvh_at_position_response_;
-
- DISALLOW_COPY_AND_ASSIGN(TestBrowserPluginEmbedder);
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_BROWSER_PLUGIN_TEST_BROWSER_PLUGIN_EMBEDDER_H_
diff --git a/chromium/content/browser/browser_plugin/test_browser_plugin_guest.cc b/chromium/content/browser/browser_plugin/test_browser_plugin_guest.cc
deleted file mode 100644
index 91ba2909d21..00000000000
--- a/chromium/content/browser/browser_plugin/test_browser_plugin_guest.cc
+++ /dev/null
@@ -1,254 +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/browser_plugin/test_browser_plugin_guest.h"
-
-#include "base/test/test_timeouts.h"
-#include "content/browser/renderer_host/render_view_host_impl.h"
-#include "content/browser/web_contents/web_contents_impl.h"
-#include "content/common/browser_plugin/browser_plugin_messages.h"
-
-namespace content {
-
-class BrowserPluginGuest;
-
-TestBrowserPluginGuest::TestBrowserPluginGuest(
- int instance_id,
- WebContentsImpl* web_contents)
- : BrowserPluginGuest(instance_id, false, web_contents, NULL),
- update_rect_count_(0),
- damage_buffer_call_count_(0),
- exit_observed_(false),
- focus_observed_(false),
- blur_observed_(false),
- advance_focus_observed_(false),
- was_hidden_observed_(false),
- set_damage_buffer_observed_(false),
- input_observed_(false),
- load_stop_observed_(false),
- ime_cancel_observed_(false),
- waiting_for_damage_buffer_with_size_(false),
- last_damage_buffer_size_(gfx::Size()) {
-}
-
-TestBrowserPluginGuest::~TestBrowserPluginGuest() {
-}
-
-WebContentsImpl* TestBrowserPluginGuest::web_contents() const {
- return static_cast<WebContentsImpl*>(BrowserPluginGuest::web_contents());
-}
-
-void TestBrowserPluginGuest::SendMessageToEmbedder(IPC::Message* msg) {
- if (msg->type() == BrowserPluginMsg_UpdateRect::ID) {
- update_rect_count_++;
- int instance_id = 0;
- BrowserPluginMsg_UpdateRect_Params params;
- BrowserPluginMsg_UpdateRect::Read(msg, &instance_id, &params);
- last_view_size_observed_ = params.view_size;
- if (!expected_auto_view_size_.IsEmpty() &&
- expected_auto_view_size_ == params.view_size) {
- if (auto_view_size_message_loop_runner_.get())
- auto_view_size_message_loop_runner_->Quit();
- }
- if (send_message_loop_runner_.get())
- send_message_loop_runner_->Quit();
- }
- BrowserPluginGuest::SendMessageToEmbedder(msg);
-}
-
-void TestBrowserPluginGuest::WaitForUpdateRectMsg() {
- // Check if we already got any UpdateRect message.
- if (update_rect_count_ > 0)
- return;
- send_message_loop_runner_ = new MessageLoopRunner();
- send_message_loop_runner_->Run();
-}
-
-void TestBrowserPluginGuest::ResetUpdateRectCount() {
- update_rect_count_ = 0;
-}
-
-void TestBrowserPluginGuest::WaitForDamageBufferWithSize(
- const gfx::Size& size) {
- if (damage_buffer_call_count_ > 0 && last_damage_buffer_size_ == size)
- return;
-
- expected_damage_buffer_size_ = size;
- waiting_for_damage_buffer_with_size_ = true;
- damage_buffer_message_loop_runner_ = new MessageLoopRunner();
- damage_buffer_message_loop_runner_->Run();
-}
-
-void TestBrowserPluginGuest::RenderProcessGone(base::TerminationStatus status) {
- exit_observed_ = true;
- if (status != base::TERMINATION_STATUS_NORMAL_TERMINATION &&
- status != base::TERMINATION_STATUS_STILL_RUNNING)
- VLOG(0) << "Guest crashed status: " << status;
- if (crash_message_loop_runner_.get())
- crash_message_loop_runner_->Quit();
- BrowserPluginGuest::RenderProcessGone(status);
-}
-
-void TestBrowserPluginGuest::OnHandleInputEvent(
- int instance_id,
- const gfx::Rect& guest_window_rect,
- const blink::WebInputEvent* event) {
- BrowserPluginGuest::OnHandleInputEvent(instance_id,
- guest_window_rect,
- event);
- input_observed_ = true;
- if (input_message_loop_runner_.get())
- input_message_loop_runner_->Quit();
-}
-
-void TestBrowserPluginGuest::WaitForExit() {
- // Check if we already observed a guest crash, return immediately if so.
- if (exit_observed_)
- return;
-
- crash_message_loop_runner_ = new MessageLoopRunner();
- crash_message_loop_runner_->Run();
-}
-
-void TestBrowserPluginGuest::WaitForFocus() {
- if (focus_observed_) {
- focus_observed_ = false;
- return;
- }
- focus_message_loop_runner_ = new MessageLoopRunner();
- focus_message_loop_runner_->Run();
- focus_observed_ = false;
-}
-
-void TestBrowserPluginGuest::WaitForBlur() {
- if (blur_observed_) {
- blur_observed_ = false;
- return;
- }
- blur_message_loop_runner_ = new MessageLoopRunner();
- blur_message_loop_runner_->Run();
- blur_observed_ = false;
-}
-
-void TestBrowserPluginGuest::WaitForAdvanceFocus() {
- if (advance_focus_observed_)
- return;
- advance_focus_message_loop_runner_ = new MessageLoopRunner();
- advance_focus_message_loop_runner_->Run();
-}
-
-void TestBrowserPluginGuest::WaitUntilHidden() {
- if (was_hidden_observed_) {
- was_hidden_observed_ = false;
- return;
- }
- was_hidden_message_loop_runner_ = new MessageLoopRunner();
- was_hidden_message_loop_runner_->Run();
- was_hidden_observed_ = false;
-}
-
-void TestBrowserPluginGuest::WaitForInput() {
- if (input_observed_) {
- input_observed_ = false;
- return;
- }
-
- input_message_loop_runner_ = new MessageLoopRunner();
- input_message_loop_runner_->Run();
- input_observed_ = false;
-}
-
-void TestBrowserPluginGuest::WaitForLoadStop() {
- if (load_stop_observed_) {
- load_stop_observed_ = false;
- return;
- }
-
- load_stop_message_loop_runner_ = new MessageLoopRunner();
- load_stop_message_loop_runner_->Run();
- load_stop_observed_ = false;
-}
-
-void TestBrowserPluginGuest::WaitForViewSize(const gfx::Size& view_size) {
- if (last_view_size_observed_ == view_size) {
- last_view_size_observed_ = gfx::Size();
- return;
- }
-
- expected_auto_view_size_ = view_size;
- auto_view_size_message_loop_runner_ = new MessageLoopRunner();
- auto_view_size_message_loop_runner_->Run();
- last_view_size_observed_ = gfx::Size();
-}
-
-void TestBrowserPluginGuest::WaitForImeCancel() {
- if (ime_cancel_observed_) {
- ime_cancel_observed_ = false;
- return;
- }
-
- ime_cancel_message_loop_runner_ = new MessageLoopRunner();
- ime_cancel_message_loop_runner_->Run();
- ime_cancel_observed_ = false;
-}
-
-void TestBrowserPluginGuest::OnSetFocus(int instance_id, bool focused) {
- if (focused) {
- focus_observed_ = true;
- if (focus_message_loop_runner_.get())
- focus_message_loop_runner_->Quit();
- } else {
- blur_observed_ = true;
- if (blur_message_loop_runner_.get())
- blur_message_loop_runner_->Quit();
- }
- BrowserPluginGuest::OnSetFocus(instance_id, focused);
-}
-
-void TestBrowserPluginGuest::OnTakeFocus(bool reverse) {
- advance_focus_observed_ = true;
- if (advance_focus_message_loop_runner_.get())
- advance_focus_message_loop_runner_->Quit();
- BrowserPluginGuest::OnTakeFocus(reverse);
-}
-
-void TestBrowserPluginGuest::SetDamageBuffer(
- const BrowserPluginHostMsg_ResizeGuest_Params& params) {
- ++damage_buffer_call_count_;
- last_damage_buffer_size_ = params.view_rect.size();
-
- if (waiting_for_damage_buffer_with_size_ &&
- expected_damage_buffer_size_ == params.view_rect.size() &&
- damage_buffer_message_loop_runner_.get()) {
- damage_buffer_message_loop_runner_->Quit();
- waiting_for_damage_buffer_with_size_ = false;
- }
-
- BrowserPluginGuest::SetDamageBuffer(params);
-}
-
-void TestBrowserPluginGuest::DidStopLoading(
- RenderViewHost* render_view_host) {
- BrowserPluginGuest::DidStopLoading(render_view_host);
- load_stop_observed_ = true;
- if (load_stop_message_loop_runner_.get())
- load_stop_message_loop_runner_->Quit();
-}
-
-void TestBrowserPluginGuest::OnImeCancelComposition() {
- if (!ime_cancel_observed_) {
- ime_cancel_observed_ = true;
- if (ime_cancel_message_loop_runner_.get())
- ime_cancel_message_loop_runner_->Quit();
- }
- BrowserPluginGuest::OnImeCancelComposition();
-}
-
-void TestBrowserPluginGuest::WasHidden() {
- was_hidden_observed_ = true;
- if (was_hidden_message_loop_runner_.get())
- was_hidden_message_loop_runner_->Quit();
-}
-
-} // namespace content
diff --git a/chromium/content/browser/browser_plugin/test_browser_plugin_guest.h b/chromium/content/browser/browser_plugin/test_browser_plugin_guest.h
deleted file mode 100644
index 1ef7f0dbecf..00000000000
--- a/chromium/content/browser/browser_plugin/test_browser_plugin_guest.h
+++ /dev/null
@@ -1,115 +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_BROWSER_PLUGIN_TEST_BROWSER_PLUGIN_GUEST_H_
-#define CONTENT_BROWSER_BROWSER_PLUGIN_TEST_BROWSER_PLUGIN_GUEST_H_
-
-#include "base/compiler_specific.h"
-#include "content/browser/browser_plugin/browser_plugin_guest.h"
-#include "content/public/test/test_utils.h"
-#include "ui/gfx/size.h"
-
-namespace content {
-
-class RenderProcessHost;
-class RenderViewHost;
-class WebContentsImpl;
-
-// Test class for BrowserPluginGuest.
-//
-// Provides utilities to wait for certain state/messages in BrowserPluginGuest
-// to be used in tests.
-class TestBrowserPluginGuest : public BrowserPluginGuest {
- public:
- TestBrowserPluginGuest(int instance_id, WebContentsImpl* web_contents);
- virtual ~TestBrowserPluginGuest();
-
- WebContentsImpl* web_contents() const;
-
- // Overridden methods from BrowserPluginGuest to intercept in test objects.
- virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE;
- virtual void OnHandleInputEvent(int instance_id,
- const gfx::Rect& guest_window_rect,
- const blink::WebInputEvent* event) OVERRIDE;
- virtual void OnSetFocus(int instance_id, bool focused) OVERRIDE;
- virtual void OnTakeFocus(bool reverse) OVERRIDE;
- virtual void SetDamageBuffer(
- const BrowserPluginHostMsg_ResizeGuest_Params& params) OVERRIDE;
- virtual void DidStopLoading(RenderViewHost* render_view_host) OVERRIDE;
- virtual void OnImeCancelComposition() OVERRIDE;
-
- // Overridden from WebContentsObserver.
- virtual void WasHidden() OVERRIDE;
-
- // Test utilities to wait for a event we are interested in.
- // Waits until UpdateRect message is sent from the guest, meaning it is
- // ready/rendered.
- void WaitForUpdateRectMsg();
- void ResetUpdateRectCount();
- // Waits until a guest receives a damage buffer of the specified |size|.
- void WaitForDamageBufferWithSize(const gfx::Size& size);
- // Waits for focus to reach this guest.
- void WaitForFocus();
- // Waits for blur to reach this guest.
- void WaitForBlur();
- // Waits for focus to move out of this guest.
- void WaitForAdvanceFocus();
- // Waits until the guest is hidden.
- void WaitUntilHidden();
- // Waits until guest exits.
- void WaitForExit();
- // Waits until input is observed.
- void WaitForInput();
- // Waits until 'loadstop' is observed.
- void WaitForLoadStop();
- // Waits until UpdateRect with a particular |view_size| is observed.
- void WaitForViewSize(const gfx::Size& view_size);
- // Waits until IME cancellation is observed.
- void WaitForImeCancel();
-
- void set_guest_hang_timeout(const base::TimeDelta& timeout) {
- guest_hang_timeout_ = timeout;
- }
-
- private:
- // Overridden methods from BrowserPluginGuest to intercept in test objects.
- virtual void SendMessageToEmbedder(IPC::Message* msg) OVERRIDE;
-
- int update_rect_count_;
- int damage_buffer_call_count_;
- bool exit_observed_;
- bool focus_observed_;
- bool blur_observed_;
- bool advance_focus_observed_;
- bool was_hidden_observed_;
- bool set_damage_buffer_observed_;
- bool input_observed_;
- bool load_stop_observed_;
- bool ime_cancel_observed_;
- gfx::Size last_view_size_observed_;
- gfx::Size expected_auto_view_size_;
-
- // For WaitForDamageBufferWithSize().
- bool waiting_for_damage_buffer_with_size_;
- gfx::Size expected_damage_buffer_size_;
- gfx::Size last_damage_buffer_size_;
-
- scoped_refptr<MessageLoopRunner> send_message_loop_runner_;
- scoped_refptr<MessageLoopRunner> crash_message_loop_runner_;
- scoped_refptr<MessageLoopRunner> focus_message_loop_runner_;
- scoped_refptr<MessageLoopRunner> blur_message_loop_runner_;
- scoped_refptr<MessageLoopRunner> advance_focus_message_loop_runner_;
- scoped_refptr<MessageLoopRunner> was_hidden_message_loop_runner_;
- scoped_refptr<MessageLoopRunner> damage_buffer_message_loop_runner_;
- scoped_refptr<MessageLoopRunner> input_message_loop_runner_;
- scoped_refptr<MessageLoopRunner> load_stop_message_loop_runner_;
- scoped_refptr<MessageLoopRunner> auto_view_size_message_loop_runner_;
- scoped_refptr<MessageLoopRunner> ime_cancel_message_loop_runner_;
-
- DISALLOW_COPY_AND_ASSIGN(TestBrowserPluginGuest);
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_BROWSER_PLUGIN_TEST_BROWSER_PLUGIN_GUEST_H_
diff --git a/chromium/content/browser/browser_plugin/test_browser_plugin_guest_delegate.cc b/chromium/content/browser/browser_plugin/test_browser_plugin_guest_delegate.cc
deleted file mode 100644
index 59ae63555d9..00000000000
--- a/chromium/content/browser/browser_plugin/test_browser_plugin_guest_delegate.cc
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/browser_plugin/test_browser_plugin_guest_delegate.h"
-
-namespace content {
-
-TestBrowserPluginGuestDelegate::TestBrowserPluginGuestDelegate()
- : load_aborted_(false) {
-}
-
-TestBrowserPluginGuestDelegate::~TestBrowserPluginGuestDelegate() {
-}
-
-void TestBrowserPluginGuestDelegate::ResetStates() {
- load_aborted_ = false;
- load_aborted_url_ = GURL();
-}
-
-void TestBrowserPluginGuestDelegate::AddMessageToConsole(
- int32 level,
- const base::string16& message,
- int32 line_no,
- const base::string16& source_id) {
-}
-
-void TestBrowserPluginGuestDelegate::Close() {
-}
-
-void TestBrowserPluginGuestDelegate::GuestProcessGone(
- base::TerminationStatus status) {
-}
-
-bool TestBrowserPluginGuestDelegate::HandleKeyboardEvent(
- const NativeWebKeyboardEvent& event) {
- return BrowserPluginGuestDelegate::HandleKeyboardEvent(event);
-}
-
-void TestBrowserPluginGuestDelegate::LoadAbort(bool is_top_level,
- const GURL& url,
- const std::string& error_type) {
- load_aborted_ = true;
- load_aborted_url_ = url;
-}
-
-void TestBrowserPluginGuestDelegate::RendererResponsive() {
-}
-
-void TestBrowserPluginGuestDelegate::RendererUnresponsive() {
-}
-
-bool TestBrowserPluginGuestDelegate::RequestPermission(
- BrowserPluginPermissionType permission_type,
- const base::DictionaryValue& request_info,
- const PermissionResponseCallback& callback,
- bool allowed_by_default) {
- return BrowserPluginGuestDelegate::RequestPermission(permission_type,
- request_info,
- callback,
- allowed_by_default);
-}
-
-void TestBrowserPluginGuestDelegate::SizeChanged(const gfx::Size& old_size,
- const gfx::Size& new_size) {
-}
-
-} // namespace content
diff --git a/chromium/content/browser/browser_plugin/test_browser_plugin_guest_delegate.h b/chromium/content/browser/browser_plugin/test_browser_plugin_guest_delegate.h
deleted file mode 100644
index 4bfae4c622f..00000000000
--- a/chromium/content/browser/browser_plugin/test_browser_plugin_guest_delegate.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_BROWSER_PLUGIN_TEST_BROWSER_PLUGIN_GUEST_DELEGATE_H_
-#define CONTENT_BROWSER_BROWSER_PLUGIN_TEST_BROWSER_PLUGIN_GUEST_DELEGATE_H_
-
-#include "content/public/browser/browser_plugin_guest_delegate.h"
-
-namespace content {
-
-class TestBrowserPluginGuestDelegate : public BrowserPluginGuestDelegate {
- public:
- TestBrowserPluginGuestDelegate();
- virtual ~TestBrowserPluginGuestDelegate();
-
- void ResetStates();
-
- bool load_aborted() const { return load_aborted_; }
- const GURL& load_aborted_url() const { return load_aborted_url_; }
-
- private:
- // Overridden from BrowserPluginGuestDelegate:
- virtual void AddMessageToConsole(int32 level,
- const base::string16& message,
- int32 line_no,
- const base::string16& source_id) OVERRIDE;
- virtual void Close() OVERRIDE;
- virtual void GuestProcessGone(base::TerminationStatus status) OVERRIDE;
- virtual bool HandleKeyboardEvent(
- const NativeWebKeyboardEvent& event) OVERRIDE;
- virtual void LoadAbort(bool is_top_level,
- const GURL& url,
- const std::string& error_type) OVERRIDE;
- virtual void RendererResponsive() OVERRIDE;
- virtual void RendererUnresponsive() OVERRIDE;
- virtual bool RequestPermission(
- BrowserPluginPermissionType permission_type,
- const base::DictionaryValue& request_info,
- const PermissionResponseCallback& callback,
- bool allowed_by_default) OVERRIDE;
- virtual void SizeChanged(const gfx::Size& old_size,
- const gfx::Size& new_size) OVERRIDE;
-
- bool load_aborted_;
- GURL load_aborted_url_;
-
- DISALLOW_COPY_AND_ASSIGN(TestBrowserPluginGuestDelegate);
-};
-
-} // namespace content
-#endif // CONTENT_BROWSER_BROWSER_PLUGIN_TEST_BROWSER_PLUGIN_GUEST_DELEGATE_H_
diff --git a/chromium/content/browser/browser_plugin/test_browser_plugin_guest_manager.cc b/chromium/content/browser/browser_plugin/test_browser_plugin_guest_manager.cc
deleted file mode 100644
index 7d0b5ac23dd..00000000000
--- a/chromium/content/browser/browser_plugin/test_browser_plugin_guest_manager.cc
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/browser_plugin/test_browser_plugin_guest_manager.h"
-
-#include "content/browser/web_contents/web_contents_impl.h"
-#include "content/public/test/test_utils.h"
-
-namespace content {
-
-TestBrowserPluginGuestManager::TestBrowserPluginGuestManager() {
-}
-
-TestBrowserPluginGuestManager::~TestBrowserPluginGuestManager() {
-}
-
-void TestBrowserPluginGuestManager::AddGuest(
- int instance_id,
- WebContentsImpl* guest_web_contents) {
- BrowserPluginGuestManager::AddGuest(instance_id, guest_web_contents);
- if (message_loop_runner_.get())
- message_loop_runner_->Quit();
-}
-
-void TestBrowserPluginGuestManager::WaitForGuestAdded() {
- // Check if guests were already created.
- if (guest_web_contents_by_instance_id_.size() > 0)
- return;
- // Wait otherwise.
- message_loop_runner_ = new MessageLoopRunner();
- message_loop_runner_->Run();
-}
-
-} // namespace content
diff --git a/chromium/content/browser/browser_plugin/test_browser_plugin_guest_manager.h b/chromium/content/browser/browser_plugin/test_browser_plugin_guest_manager.h
deleted file mode 100644
index d7683cf7ae9..00000000000
--- a/chromium/content/browser/browser_plugin/test_browser_plugin_guest_manager.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_BROWSER_PLUGIN_TEST_BROWSER_PLUGIN_GUEST_MANAGER_H_
-#define CONTENT_BROWSER_BROWSER_PLUGIN_TEST_BROWSER_PLUGIN_GUEST_MANAGER_H_
-
-#include "base/compiler_specific.h"
-#include "base/gtest_prod_util.h"
-#include "content/browser/browser_plugin/browser_plugin_guest_manager.h"
-#include "content/public/test/test_utils.h"
-
-FORWARD_DECLARE_TEST(BrowserPluginHostTest, ReloadEmbedder);
-
-namespace content {
-
-class WebContentsImpl;
-
-// Test class for BrowserPluginGuestManager.
-//
-// Provides utilities to wait for certain state/messages in
-// BrowserPluginGuestManager to be used in tests.
-class TestBrowserPluginGuestManager : public BrowserPluginGuestManager {
- public:
- typedef BrowserPluginGuestManager::GuestInstanceMap GuestInstanceMap;
-
- TestBrowserPluginGuestManager();
- virtual ~TestBrowserPluginGuestManager();
-
- const GuestInstanceMap& guest_web_contents_for_testing() const {
- return guest_web_contents_by_instance_id_;
- }
-
- // Waits until at least one guest is added to the guest manager.
- void WaitForGuestAdded();
-
- private:
- // BrowserPluginHostTest.ReloadEmbedder needs access to the GuestInstanceMap.
- FRIEND_TEST_ALL_PREFIXES(BrowserPluginHostTest, ReloadEmbedder);
-
- // Overriden to intercept in test.
- virtual void AddGuest(int instance_id,
- WebContentsImpl* guest_web_contents) OVERRIDE;
-
- scoped_refptr<MessageLoopRunner> message_loop_runner_;
- DISALLOW_COPY_AND_ASSIGN(TestBrowserPluginGuestManager);
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_BROWSER_PLUGIN_TEST_BROWSER_PLUGIN_GUEST_MANAGER_H_
diff --git a/chromium/content/browser/browser_thread_impl.cc b/chromium/content/browser/browser_thread_impl.cc
index 099ec3b72a4..641056ffb78 100644
--- a/chromium/content/browser/browser_thread_impl.cc
+++ b/chromium/content/browser/browser_thread_impl.cc
@@ -16,6 +16,10 @@
#include "base/threading/thread_restrictions.h"
#include "content/public/browser/browser_thread_delegate.h"
+#if defined(OS_ANDROID)
+#include "base/android/jni_android.h"
+#endif
+
namespace content {
namespace {
@@ -31,6 +35,57 @@ static const char* g_browser_thread_names[BrowserThread::ID_COUNT] = {
"Chrome_IOThread", // IO
};
+// An implementation of MessageLoopProxy to be used in conjunction
+// with BrowserThread.
+class BrowserThreadMessageLoopProxy : public base::MessageLoopProxy {
+ public:
+ explicit BrowserThreadMessageLoopProxy(BrowserThread::ID identifier)
+ : id_(identifier) {
+ }
+
+ // MessageLoopProxy implementation.
+ virtual bool PostDelayedTask(
+ const tracked_objects::Location& from_here,
+ const base::Closure& task, base::TimeDelta delay) OVERRIDE {
+ return BrowserThread::PostDelayedTask(id_, from_here, task, delay);
+ }
+
+ virtual bool PostNonNestableDelayedTask(
+ const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta delay) OVERRIDE {
+ return BrowserThread::PostNonNestableDelayedTask(id_, from_here, task,
+ delay);
+ }
+
+ virtual bool RunsTasksOnCurrentThread() const OVERRIDE {
+ return BrowserThread::CurrentlyOn(id_);
+ }
+
+ protected:
+ virtual ~BrowserThreadMessageLoopProxy() {}
+
+ private:
+ BrowserThread::ID id_;
+ DISALLOW_COPY_AND_ASSIGN(BrowserThreadMessageLoopProxy);
+};
+
+// A separate helper is used just for the proxies, in order to avoid needing
+// to initialize the globals to create a proxy.
+struct BrowserThreadProxies {
+ BrowserThreadProxies() {
+ for (int i = 0; i < BrowserThread::ID_COUNT; ++i) {
+ proxies[i] =
+ new BrowserThreadMessageLoopProxy(static_cast<BrowserThread::ID>(i));
+ }
+ }
+
+ scoped_refptr<base::MessageLoopProxy> proxies[BrowserThread::ID_COUNT];
+};
+
+base::LazyInstance<BrowserThreadProxies>::Leaky
+ g_proxies = LAZY_INSTANCE_INITIALIZER;
+
struct BrowserThreadGlobals {
BrowserThreadGlobals()
: blocking_pool(new base::SequencedWorkerPool(3, "BrowserBlocking")) {
@@ -69,7 +124,7 @@ BrowserThreadImpl::BrowserThreadImpl(ID identifier)
BrowserThreadImpl::BrowserThreadImpl(ID identifier,
base::MessageLoop* message_loop)
- : Thread(message_loop->thread_name().c_str()), identifier_(identifier) {
+ : Thread(message_loop->thread_name()), identifier_(identifier) {
set_message_loop(message_loop);
Initialize();
}
@@ -167,6 +222,15 @@ MSVC_POP_WARNING()
MSVC_ENABLE_OPTIMIZE();
void BrowserThreadImpl::Run(base::MessageLoop* message_loop) {
+#if defined(OS_ANDROID)
+ // Not to reset thread name to "Thread-???" by VM, attach VM with thread name.
+ // Though it may create unnecessary VM thread objects, keeping thread name
+ // gives more benefit in debugging in the platform.
+ if (!thread_name().empty()) {
+ base::android::AttachCurrentThreadWithName(thread_name());
+ }
+#endif
+
BrowserThread::ID thread_id = ID_COUNT;
if (!GetCurrentThreadIdentifier(&thread_id))
return Thread::Run(message_loop);
@@ -274,41 +338,6 @@ bool BrowserThreadImpl::PostTaskHelper(
return !!message_loop;
}
-// An implementation of MessageLoopProxy to be used in conjunction
-// with BrowserThread.
-class BrowserThreadMessageLoopProxy : public base::MessageLoopProxy {
- public:
- explicit BrowserThreadMessageLoopProxy(BrowserThread::ID identifier)
- : id_(identifier) {
- }
-
- // MessageLoopProxy implementation.
- virtual bool PostDelayedTask(
- const tracked_objects::Location& from_here,
- const base::Closure& task, base::TimeDelta delay) OVERRIDE {
- return BrowserThread::PostDelayedTask(id_, from_here, task, delay);
- }
-
- virtual bool PostNonNestableDelayedTask(
- const tracked_objects::Location& from_here,
- const base::Closure& task,
- base::TimeDelta delay) OVERRIDE {
- return BrowserThread::PostNonNestableDelayedTask(id_, from_here, task,
- delay);
- }
-
- virtual bool RunsTasksOnCurrentThread() const OVERRIDE {
- return BrowserThread::CurrentlyOn(id_);
- }
-
- protected:
- virtual ~BrowserThreadMessageLoopProxy() {}
-
- private:
- BrowserThread::ID id_;
- DISALLOW_COPY_AND_ASSIGN(BrowserThreadMessageLoopProxy);
-};
-
// static
bool BrowserThread::PostBlockingPoolTask(
const tracked_objects::Location& from_here,
@@ -365,6 +394,33 @@ bool BrowserThread::CurrentlyOn(ID identifier) {
base::MessageLoop::current();
}
+static const char* GetThreadName(BrowserThread::ID thread) {
+ if (BrowserThread::UI < thread && thread < BrowserThread::ID_COUNT)
+ return g_browser_thread_names[thread];
+ if (thread == BrowserThread::UI)
+ return "Chrome_UIThread";
+ return "Unknown Thread";
+}
+
+// static
+std::string BrowserThread::GetDCheckCurrentlyOnErrorMessage(ID expected) {
+ const std::string& message_loop_name =
+ base::MessageLoop::current()->thread_name();
+ ID actual_browser_thread;
+ const char* actual_name = "Unknown Thread";
+ if (!message_loop_name.empty()) {
+ actual_name = message_loop_name.c_str();
+ } else if (GetCurrentThreadIdentifier(&actual_browser_thread)) {
+ actual_name = GetThreadName(actual_browser_thread);
+ }
+ std::string result = "Must be called on ";
+ result += GetThreadName(expected);
+ result += "; actually called on ";
+ result += actual_name;
+ result += ".";
+ return result;
+}
+
// static
bool BrowserThread::IsMessageLoopValid(ID identifier) {
if (g_globals == NULL)
@@ -450,7 +506,7 @@ bool BrowserThread::GetCurrentThreadIdentifier(ID* identifier) {
// static
scoped_refptr<base::MessageLoopProxy>
BrowserThread::GetMessageLoopProxyForThread(ID identifier) {
- return make_scoped_refptr(new BrowserThreadMessageLoopProxy(identifier));
+ return g_proxies.Get().proxies[identifier];
}
// static
diff --git a/chromium/content/browser/browser_url_handler_impl.cc b/chromium/content/browser/browser_url_handler_impl.cc
index 05cc72df474..1b7050f8ddf 100644
--- a/chromium/content/browser/browser_url_handler_impl.cc
+++ b/chromium/content/browser/browser_url_handler_impl.cc
@@ -6,10 +6,10 @@
#include "base/command_line.h"
#include "base/strings/string_util.h"
+#include "cc/base/switches.h"
#include "content/browser/frame_host/debug_urls.h"
#include "content/browser/webui/web_ui_impl.h"
#include "content/public/browser/content_browser_client.h"
-#include "content/public/common/content_switches.h"
#include "content/public/common/url_constants.h"
#include "url/gurl.h"
@@ -23,22 +23,34 @@ static bool HandleViewSource(GURL* url, BrowserContext* browser_context) {
// Bug 26129: limit view-source to view the content and not any
// other kind of 'active' url scheme like 'javascript' or 'data'.
- static const char* const allowed_sub_schemes[] = {
- kHttpScheme, kHttpsScheme, kFtpScheme,
- chrome::kChromeDevToolsScheme, chrome::kChromeUIScheme,
- chrome::kFileScheme, chrome::kFileSystemScheme
+ static const char* const default_allowed_sub_schemes[] = {
+ url::kHttpScheme,
+ url::kHttpsScheme,
+ url::kFtpScheme,
+ kChromeDevToolsScheme,
+ kChromeUIScheme,
+ url::kFileScheme,
+ url::kFileSystemScheme
};
+ // Merge all the schemes for which view-source is allowed by default, with
+ // the WebUI schemes defined by the ContentBrowserClient.
+ std::vector<std::string> all_allowed_sub_schemes;
+ for (size_t i = 0; i < arraysize(default_allowed_sub_schemes); ++i)
+ all_allowed_sub_schemes.push_back(default_allowed_sub_schemes[i]);
+ GetContentClient()->browser()->GetAdditionalWebUISchemes(
+ &all_allowed_sub_schemes);
+
bool is_sub_scheme_allowed = false;
- for (size_t i = 0; i < arraysize(allowed_sub_schemes); i++) {
- if (url->SchemeIs(allowed_sub_schemes[i])) {
+ for (size_t i = 0; i < all_allowed_sub_schemes.size(); ++i) {
+ if (url->SchemeIs(all_allowed_sub_schemes[i].c_str())) {
is_sub_scheme_allowed = true;
break;
}
}
if (!is_sub_scheme_allowed) {
- *url = GURL(kAboutBlankURL);
+ *url = GURL(url::kAboutBlankURL);
return false;
}
@@ -53,11 +65,10 @@ static bool ReverseViewSource(GURL* url, BrowserContext* browser_context) {
if (url->SchemeIs(kViewSourceScheme))
return false;
- url_canon::Replacements<char> repl;
+ url::Replacements<char> repl;
repl.SetScheme(kViewSourceScheme,
- url_parse::Component(0, strlen(kViewSourceScheme)));
- repl.SetPath(url->spec().c_str(),
- url_parse::Component(0, url->spec().size()));
+ url::Component(0, strlen(kViewSourceScheme)));
+ repl.SetPath(url->spec().c_str(), url::Component(0, url->spec().size()));
*url = url->ReplaceComponents(repl);
return true;
}
@@ -68,17 +79,14 @@ static bool DebugURLHandler(GURL* url, BrowserContext* browser_context) {
// chrome:// scheme, since the about: scheme won't be rewritten in
// this code path.
if (CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kEnableGpuBenchmarking)) {
+ cc::switches::kEnableGpuBenchmarking)) {
if (HandleDebugURL(*url, PAGE_TRANSITION_FROM_ADDRESS_BAR)) {
return true;
}
}
// Circumvent processing URLs that the renderer process will handle.
- return *url == GURL(kChromeUICrashURL) ||
- *url == GURL(kChromeUIHangURL) ||
- *url == GURL(kChromeUIKillURL) ||
- *url == GURL(kChromeUIShorthangURL);
+ return IsRendererDebugURL(*url);
}
// static
diff --git a/chromium/content/browser/browsing_instance.cc b/chromium/content/browser/browsing_instance.cc
index c70f38b9daa..1d5f8953769 100644
--- a/chromium/content/browser/browsing_instance.cc
+++ b/chromium/content/browser/browsing_instance.cc
@@ -15,7 +15,8 @@
namespace content {
BrowsingInstance::BrowsingInstance(BrowserContext* browser_context)
- : browser_context_(browser_context) {
+ : browser_context_(browser_context),
+ active_contents_count_(0u) {
}
bool BrowsingInstance::HasSiteInstance(const GURL& url) {
@@ -84,6 +85,7 @@ BrowsingInstance::~BrowsingInstance() {
// We should only be deleted when all of the SiteInstances that refer to
// us are gone.
DCHECK(site_instance_map_.empty());
+ DCHECK_EQ(0u, active_contents_count_);
}
} // namespace content
diff --git a/chromium/content/browser/browsing_instance.h b/chromium/content/browser/browsing_instance.h
index 60aa47b19dc..60518b07c4d 100644
--- a/chromium/content/browser/browsing_instance.h
+++ b/chromium/content/browser/browsing_instance.h
@@ -7,6 +7,7 @@
#include "base/containers/hash_tables.h"
#include "base/lazy_instance.h"
+#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "content/common/content_export.h"
#include "content/public/browser/browser_context.h"
@@ -79,6 +80,14 @@ class CONTENT_EXPORT BrowsingInstance
// BrowsingInstance.
void UnregisterSiteInstance(SiteInstance* site_instance);
+ // Tracks the number of WebContents currently in this BrowsingInstance.
+ size_t active_contents_count() const { return active_contents_count_; }
+ void increment_active_contents_count() { active_contents_count_++; }
+ void decrement_active_contents_count() {
+ DCHECK_LT(0u, active_contents_count_);
+ active_contents_count_--;
+ }
+
friend class SiteInstanceImpl;
friend class SiteInstance;
@@ -89,6 +98,7 @@ class CONTENT_EXPORT BrowsingInstance
private:
// Map of site to SiteInstance, to ensure we only have one SiteInstance per
+ // site.
typedef base::hash_map<std::string, SiteInstance*> SiteInstanceMap;
// Common browser context to which all SiteInstances in this BrowsingInstance
@@ -100,8 +110,13 @@ class CONTENT_EXPORT BrowsingInstance
// obtained with SiteInstanceImpl::GetSiteForURL. Note that this map may not
// contain every active SiteInstance, because a race exists where two
// SiteInstances can be assigned to the same site. This is ok in rare cases.
+ // It also does not contain SiteInstances which have not yet been assigned a
+ // site, such as about:blank. See NavigatorImpl::ShouldAssignSiteForURL.
SiteInstanceMap site_instance_map_;
+ // Number of WebContentses currently using this BrowsingInstance.
+ size_t active_contents_count_;
+
DISALLOW_COPY_AND_ASSIGN(BrowsingInstance);
};
diff --git a/chromium/content/browser/byte_stream.cc b/chromium/content/browser/byte_stream.cc
index 7b0f9fb0f9a..0cd3cd8c1b2 100644
--- a/chromium/content/browser/byte_stream.cc
+++ b/chromium/content/browser/byte_stream.cc
@@ -194,6 +194,9 @@ ByteStreamWriterImpl::ByteStreamWriterImpl(
}
ByteStreamWriterImpl::~ByteStreamWriterImpl() {
+ // No RunsTasksOnCurrentThread() check to allow deleting a created writer
+ // before we start using it. Once started, should be deleted on the specified
+ // task runner.
my_lifetime_flag_->is_alive = false;
}
@@ -323,6 +326,9 @@ ByteStreamReaderImpl::ByteStreamReaderImpl(
}
ByteStreamReaderImpl::~ByteStreamReaderImpl() {
+ // No RunsTasksOnCurrentThread() check to allow deleting a created writer
+ // before we start using it. Once started, should be deleted on the specified
+ // task runner.
my_lifetime_flag_->is_alive = false;
}
diff --git a/chromium/content/browser/child_process_launcher.cc b/chromium/content/browser/child_process_launcher.cc
index 3a162119bd5..5e65042301c 100644
--- a/chromium/content/browser/child_process_launcher.cc
+++ b/chromium/content/browser/child_process_launcher.cc
@@ -9,6 +9,7 @@
#include "base/bind.h"
#include "base/command_line.h"
#include "base/file_util.h"
+#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/metrics/histogram.h"
@@ -20,14 +21,16 @@
#include "content/public/common/content_descriptors.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/result_codes.h"
+#include "content/public/common/sandboxed_process_launcher_delegate.h"
#if defined(OS_WIN)
#include "base/files/file_path.h"
#include "content/common/sandbox_win.h"
#include "content/public/common/sandbox_init.h"
-#include "content/public/common/sandboxed_process_launcher_delegate.h"
#elif defined(OS_MACOSX)
+#include "content/browser/bootstrap_sandbox_mac.h"
#include "content/browser/mach_broker_mac.h"
+#include "sandbox/mac/bootstrap_sandbox.h"
#elif defined(OS_ANDROID)
#include "base/android/jni_android.h"
#include "content/browser/android/child_process_launcher_android.h"
@@ -57,29 +60,22 @@ class ChildProcessLauncher::Context
client_thread_id_(BrowserThread::UI),
termination_status_(base::TERMINATION_STATUS_NORMAL_TERMINATION),
exit_code_(RESULT_CODE_NORMAL_EXIT),
- starting_(true)
+ starting_(true),
+ // TODO(earthdok): Re-enable on CrOS http://crbug.com/360622
+#if (defined(ADDRESS_SANITIZER) || defined(LEAK_SANITIZER) || \
+ defined(THREAD_SANITIZER)) && !defined(OS_CHROMEOS)
+ terminate_child_on_shutdown_(false)
+#else
+ terminate_child_on_shutdown_(true)
+#endif
#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
, zygote_(false)
#endif
{
-#if defined(OS_POSIX)
- terminate_child_on_shutdown_ = !CommandLine::ForCurrentProcess()->
- HasSwitch(switches::kChildCleanExit);
-#else
- terminate_child_on_shutdown_ = true;
-#endif
}
void Launch(
-#if defined(OS_WIN)
SandboxedProcessLauncherDelegate* delegate,
-#elif defined(OS_ANDROID)
- int ipcfd,
-#elif defined(OS_POSIX)
- bool use_zygote,
- const base::EnvironmentMap& environ,
- int ipcfd,
-#endif
CommandLine* cmd_line,
int child_process_id,
Client* client) {
@@ -91,7 +87,7 @@ class ChildProcessLauncher::Context
// We need to close the client end of the IPC channel to reliably detect
// child termination. We will close this fd after we create the child
// process which is asynchronous on Android.
- ipcfd_ = ipcfd;
+ ipcfd_ = delegate->GetIpcFd();
#endif
BrowserThread::PostTask(
BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
@@ -100,15 +96,7 @@ class ChildProcessLauncher::Context
make_scoped_refptr(this),
client_thread_id_,
child_process_id,
-#if defined(OS_WIN)
delegate,
-#elif defined(OS_ANDROID)
- ipcfd,
-#elif defined(OS_POSIX)
- use_zygote,
- environ,
- ipcfd,
-#endif
cmd_line));
}
@@ -183,22 +171,33 @@ class ChildProcessLauncher::Context
scoped_refptr<Context> this_object,
BrowserThread::ID client_thread_id,
int child_process_id,
-#if defined(OS_WIN)
SandboxedProcessLauncherDelegate* delegate,
+ CommandLine* cmd_line) {
+ scoped_ptr<SandboxedProcessLauncherDelegate> delegate_deleter(delegate);
+#if defined(OS_WIN)
+ bool launch_elevated = delegate->ShouldLaunchElevated();
#elif defined(OS_ANDROID)
- int ipcfd,
+ int ipcfd = delegate->GetIpcFd();
+#elif defined(OS_MACOSX)
+ base::EnvironmentMap env = delegate->GetEnvironment();
+ int ipcfd = delegate->GetIpcFd();
#elif defined(OS_POSIX)
- bool use_zygote,
- const base::EnvironmentMap& env,
- int ipcfd,
+ bool use_zygote = delegate->ShouldUseZygote();
+ base::EnvironmentMap env = delegate->GetEnvironment();
+ int ipcfd = delegate->GetIpcFd();
#endif
- CommandLine* cmd_line) {
scoped_ptr<CommandLine> cmd_line_deleter(cmd_line);
base::TimeTicks begin_launch_time = base::TimeTicks::Now();
#if defined(OS_WIN)
- scoped_ptr<SandboxedProcessLauncherDelegate> delegate_deleter(delegate);
- base::ProcessHandle handle = StartSandboxedProcess(delegate, cmd_line);
+ base::ProcessHandle handle = base::kNullProcessHandle;
+ if (launch_elevated) {
+ base::LaunchOptions options;
+ options.start_hidden = true;
+ base::LaunchElevatedProcess(*cmd_line, options, &handle);
+ } else {
+ handle = StartSandboxedProcess(delegate, cmd_line);
+ }
#elif defined(OS_POSIX)
std::string process_type =
cmd_line->GetSwitchValueASCII(switches::kProcessType);
@@ -225,7 +224,7 @@ class ChildProcessLauncher::Context
GetAdditionalMappedFilesForChildProcess(*cmd_line, child_process_id,
&files_to_register);
- StartChildProcess(cmd_line->argv(), files_to_register,
+ StartChildProcess(cmd_line->argv(), child_process_id, files_to_register,
base::Bind(&ChildProcessLauncher::Context::OnChildProcessStarted,
this_object, client_thread_id, begin_launch_time));
@@ -233,7 +232,7 @@ class ChildProcessLauncher::Context
base::ProcessHandle handle = base::kNullProcessHandle;
// We need to close the client end of the IPC channel to reliably detect
// child termination.
- file_util::ScopedFD ipcfd_closer(&ipcfd);
+ base::ScopedFD ipcfd_closer(ipcfd);
#if !defined(OS_MACOSX)
GetContentClient()->browser()->
@@ -285,11 +284,27 @@ class ChildProcessLauncher::Context
// Make sure the MachBroker is running, and inform it to expect a
// check-in from the new process.
broker->EnsureRunning();
+
+ const int bootstrap_sandbox_policy = delegate->GetSandboxType();
+ if (ShouldEnableBootstrapSandbox() &&
+ bootstrap_sandbox_policy != SANDBOX_TYPE_INVALID) {
+ options.replacement_bootstrap_name =
+ GetBootstrapSandbox()->server_bootstrap_name();
+ GetBootstrapSandbox()->PrepareToForkWithPolicy(
+ bootstrap_sandbox_policy);
+ }
#endif // defined(OS_MACOSX)
bool launched = base::LaunchProcess(*cmd_line, options, &handle);
+ if (!launched)
+ handle = base::kNullProcessHandle;
#if defined(OS_MACOSX)
+ if (ShouldEnableBootstrapSandbox() &&
+ bootstrap_sandbox_policy != SANDBOX_TYPE_INVALID) {
+ GetBootstrapSandbox()->FinishedFork(handle);
+ }
+
if (launched)
broker->AddPlaceholderForPid(handle);
@@ -297,9 +312,6 @@ class ChildProcessLauncher::Context
// messasge be processed on the broker's thread.
broker->GetLock().Release();
#endif // defined(OS_MACOSX)
-
- if (!launched)
- handle = base::kNullProcessHandle;
}
#endif // else defined(OS_POSIX)
#if !defined(OS_ANDROID)
@@ -324,7 +336,7 @@ class ChildProcessLauncher::Context
base::ProcessHandle handle) {
#if defined(OS_ANDROID)
// Finally close the ipcfd
- file_util::ScopedFD ipcfd_closer(&ipcfd_);
+ base::ScopedFD ipcfd_closer(ipcfd_);
#endif
starting_ = false;
process_.set_handle(handle);
@@ -335,7 +347,11 @@ class ChildProcessLauncher::Context
zygote_ = zygote;
#endif
if (client_) {
- client_->OnProcessLaunched();
+ if (handle) {
+ client_->OnProcessLaunched();
+ } else {
+ client_->OnProcessLaunchFailed();
+ }
} else {
Terminate();
}
@@ -365,6 +381,9 @@ class ChildProcessLauncher::Context
bool background) {
base::Process process(handle);
process.SetProcessBackgrounded(background);
+#if defined(OS_ANDROID)
+ SetChildProcessInForeground(handle, !background);
+#endif
}
static void TerminateInternal(
@@ -416,27 +435,13 @@ class ChildProcessLauncher::Context
ChildProcessLauncher::ChildProcessLauncher(
-#if defined(OS_WIN)
SandboxedProcessLauncherDelegate* delegate,
-#elif defined(OS_POSIX)
- bool use_zygote,
- const base::EnvironmentMap& environ,
- int ipcfd,
-#endif
CommandLine* cmd_line,
int child_process_id,
Client* client) {
context_ = new Context();
context_->Launch(
-#if defined(OS_WIN)
delegate,
-#elif defined(OS_ANDROID)
- ipcfd,
-#elif defined(OS_POSIX)
- use_zygote,
- environ,
- ipcfd,
-#endif
cmd_line,
child_process_id,
client);
@@ -506,14 +511,14 @@ base::TerminationStatus ChildProcessLauncher::GetChildTerminationStatus(
void ChildProcessLauncher::SetProcessBackgrounded(bool background) {
BrowserThread::PostTask(
- BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
- base::Bind(
- &ChildProcessLauncher::Context::SetProcessBackgrounded,
- GetHandle(), background));
+ BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
+ base::Bind(
+ &ChildProcessLauncher::Context::SetProcessBackgrounded,
+ GetHandle(), background));
}
void ChildProcessLauncher::SetTerminateChildOnShutdown(
- bool terminate_on_shutdown) {
+ bool terminate_on_shutdown) {
if (context_.get())
context_->set_terminate_child_on_shutdown(terminate_on_shutdown);
}
diff --git a/chromium/content/browser/child_process_launcher.h b/chromium/content/browser/child_process_launcher.h
index 7ce780551f4..cc8b3f52cef 100644
--- a/chromium/content/browser/child_process_launcher.h
+++ b/chromium/content/browser/child_process_launcher.h
@@ -11,7 +11,9 @@
#include "base/process/launch.h"
#include "content/common/content_export.h"
+namespace base {
class CommandLine;
+}
namespace content {
class SandboxedProcessLauncherDelegate;
@@ -27,6 +29,8 @@ class CONTENT_EXPORT ChildProcessLauncher {
// constructed on.
virtual void OnProcessLaunched() = 0;
+ virtual void OnProcessLaunchFailed() {};
+
protected:
virtual ~Client() {}
};
@@ -37,14 +41,8 @@ class CONTENT_EXPORT ChildProcessLauncher {
// this object destructs, it will be terminated.
// Takes ownership of cmd_line.
ChildProcessLauncher(
-#if defined(OS_WIN)
SandboxedProcessLauncherDelegate* delegate,
-#elif defined(OS_POSIX)
- bool use_zygote,
- const base::EnvironmentMap& environ,
- int ipcfd,
-#endif
- CommandLine* cmd_line,
+ base::CommandLine* cmd_line,
int child_process_id,
Client* client);
~ChildProcessLauncher();
diff --git a/chromium/content/browser/child_process_security_policy_browsertest.cc b/chromium/content/browser/child_process_security_policy_browsertest.cc
index c7cfe39c167..67d36e4eabf 100644
--- a/chromium/content/browser/child_process_security_policy_browsertest.cc
+++ b/chromium/content/browser/child_process_security_policy_browsertest.cc
@@ -10,9 +10,9 @@
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/common/result_codes.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/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
diff --git a/chromium/content/browser/child_process_security_policy_impl.cc b/chromium/content/browser/child_process_security_policy_impl.cc
index 0667964f3e2..68f4e81fbef 100644
--- a/chromium/content/browser/child_process_security_policy_impl.cc
+++ b/chromium/content/browser/child_process_security_policy_impl.cc
@@ -8,7 +8,6 @@
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
-#include "base/platform_file.h"
#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "content/browser/plugin_process_host.h"
@@ -19,7 +18,7 @@
#include "content/public/common/bindings_policy.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/url_constants.h"
-#include "net/base/net_util.h"
+#include "net/base/filename_util.h"
#include "net/url_request/url_request.h"
#include "url/gurl.h"
#include "webkit/browser/fileapi/file_permission_policy.h"
@@ -163,7 +162,7 @@ class ChildProcessSecurityPolicyImpl::SecurityState {
can_read_raw_cookies_ = false;
}
- void GrantPermissionForMIDISysEx() {
+ void GrantPermissionForMidiSysEx() {
can_send_midi_sysex_ = true;
}
@@ -176,7 +175,7 @@ class ChildProcessSecurityPolicyImpl::SecurityState {
// file:// URLs are more granular. The child may have been given
// permission to a specific file but not the file:// scheme in general.
- if (url.SchemeIs(chrome::kFileScheme)) {
+ if (url.SchemeIs(url::kFileScheme)) {
base::FilePath path;
if (net::FileURLToFilePath(url, &path))
return ContainsKey(request_file_set_, path);
@@ -274,7 +273,7 @@ class ChildProcessSecurityPolicyImpl::SecurityState {
private:
typedef std::map<std::string, bool> SchemeMap;
- typedef int FilePermissionFlags; // bit-set of PlatformFileFlags
+ typedef int FilePermissionFlags; // bit-set of base::File::Flags
typedef std::map<base::FilePath, FilePermissionFlags> FileMap;
typedef std::map<std::string, FilePermissionFlags> FileSystemMap;
typedef std::set<base::FilePath> FileSet;
@@ -308,17 +307,17 @@ class ChildProcessSecurityPolicyImpl::SecurityState {
ChildProcessSecurityPolicyImpl::ChildProcessSecurityPolicyImpl() {
// We know about these schemes and believe them to be safe.
- RegisterWebSafeScheme(kHttpScheme);
- RegisterWebSafeScheme(kHttpsScheme);
- RegisterWebSafeScheme(kFtpScheme);
- RegisterWebSafeScheme(chrome::kDataScheme);
+ RegisterWebSafeScheme(url::kHttpScheme);
+ RegisterWebSafeScheme(url::kHttpsScheme);
+ RegisterWebSafeScheme(url::kFtpScheme);
+ RegisterWebSafeScheme(url::kDataScheme);
RegisterWebSafeScheme("feed");
- RegisterWebSafeScheme(chrome::kBlobScheme);
- RegisterWebSafeScheme(chrome::kFileSystemScheme);
+ RegisterWebSafeScheme(url::kBlobScheme);
+ RegisterWebSafeScheme(url::kFileSystemScheme);
// We know about the following pseudo schemes and treat them specially.
- RegisterPseudoScheme(chrome::kAboutScheme);
- RegisterPseudoScheme(kJavaScriptScheme);
+ RegisterPseudoScheme(url::kAboutScheme);
+ RegisterPseudoScheme(url::kJavaScriptScheme);
RegisterPseudoScheme(kViewSourceScheme);
}
@@ -434,7 +433,7 @@ void ChildProcessSecurityPolicyImpl::GrantRequestURL(
void ChildProcessSecurityPolicyImpl::GrantRequestSpecificFileURL(
int child_id,
const GURL& url) {
- if (!url.SchemeIs(chrome::kFileScheme))
+ if (!url.SchemeIs(url::kFileScheme))
return;
{
@@ -461,6 +460,16 @@ void ChildProcessSecurityPolicyImpl::GrantCreateReadWriteFile(
GrantPermissionsForFile(child_id, file, CREATE_READ_WRITE_FILE_GRANT);
}
+void ChildProcessSecurityPolicyImpl::GrantCopyInto(int child_id,
+ const base::FilePath& dir) {
+ GrantPermissionsForFile(child_id, dir, COPY_INTO_FILE_GRANT);
+}
+
+void ChildProcessSecurityPolicyImpl::GrantDeleteFrom(
+ int child_id, const base::FilePath& dir) {
+ GrantPermissionsForFile(child_id, dir, DELETE_FILE_GRANT);
+}
+
void ChildProcessSecurityPolicyImpl::GrantPermissionsForFile(
int child_id, const base::FilePath& file, int permissions) {
base::AutoLock lock(lock_);
@@ -514,14 +523,14 @@ void ChildProcessSecurityPolicyImpl::GrantDeleteFromFileSystem(
GrantPermissionsForFileSystem(child_id, filesystem_id, DELETE_FILE_GRANT);
}
-void ChildProcessSecurityPolicyImpl::GrantSendMIDISysExMessage(int child_id) {
+void ChildProcessSecurityPolicyImpl::GrantSendMidiSysExMessage(int child_id) {
base::AutoLock lock(lock_);
SecurityStateMap::iterator state = security_state_.find(child_id);
if (state == security_state_.end())
return;
- state->second->GrantPermissionForMIDISysEx();
+ state->second->GrantPermissionForMidiSysEx();
}
void ChildProcessSecurityPolicyImpl::GrantScheme(int child_id,
@@ -545,10 +554,10 @@ void ChildProcessSecurityPolicyImpl::GrantWebUIBindings(int child_id) {
state->second->GrantBindings(BINDINGS_POLICY_WEB_UI);
// Web UI bindings need the ability to request chrome: URLs.
- state->second->GrantScheme(chrome::kChromeUIScheme);
+ state->second->GrantScheme(kChromeUIScheme);
// Web UI pages can contain links to file:// URLs.
- state->second->GrantScheme(chrome::kFileScheme);
+ state->second->GrantScheme(url::kFileScheme);
}
void ChildProcessSecurityPolicyImpl::GrantReadRawCookies(int child_id) {
@@ -608,7 +617,7 @@ bool ChildProcessSecurityPolicyImpl::CanRequestURL(
return CanRequestURL(child_id, child_url);
}
- if (LowerCaseEqualsASCII(url.spec(), kAboutBlankURL))
+ if (LowerCaseEqualsASCII(url.spec(), url::kAboutBlankURL))
return true; // Every child process can request <about:blank>.
// URLs like <about:memory> and <about:crash> shouldn't be requestable by
@@ -871,7 +880,7 @@ void ChildProcessSecurityPolicyImpl::RegisterFileSystemPermissionPolicy(
file_system_policy_map_[type] = policy;
}
-bool ChildProcessSecurityPolicyImpl::CanSendMIDISysExMessage(int child_id) {
+bool ChildProcessSecurityPolicyImpl::CanSendMidiSysExMessage(int child_id) {
base::AutoLock lock(lock_);
SecurityStateMap::iterator state = security_state_.find(child_id);
diff --git a/chromium/content/browser/child_process_security_policy_impl.h b/chromium/content/browser/child_process_security_policy_impl.h
index 93480ad6b32..005292cdf2b 100644
--- a/chromium/content/browser/child_process_security_policy_impl.h
+++ b/chromium/content/browser/child_process_security_policy_impl.h
@@ -45,6 +45,9 @@ class CONTENT_EXPORT ChildProcessSecurityPolicyImpl
virtual void GrantReadFile(int child_id, const base::FilePath& file) OVERRIDE;
virtual void GrantCreateReadWriteFile(int child_id,
const base::FilePath& file) OVERRIDE;
+ virtual void GrantCopyInto(int child_id, const base::FilePath& dir) OVERRIDE;
+ virtual void GrantDeleteFrom(int child_id,
+ const base::FilePath& dir) OVERRIDE;
virtual void GrantReadFileSystem(
int child_id,
const std::string& filesystem_id) OVERRIDE;
@@ -124,7 +127,7 @@ class CONTENT_EXPORT ChildProcessSecurityPolicyImpl
void RevokeReadRawCookies(int child_id);
// Grants permission to send system exclusive message to any MIDI devices.
- void GrantSendMIDISysExMessage(int child_id);
+ void GrantSendMidiSysExMessage(int child_id);
// Before servicing a child process's request for a URL, the browser should
// call this method to determine whether the process has the capability to
@@ -184,7 +187,7 @@ class CONTENT_EXPORT ChildProcessSecurityPolicyImpl
int policy);
// Returns true if sending system exclusive messages is allowed.
- bool CanSendMIDISysExMessage(int child_id);
+ bool CanSendMidiSysExMessage(int child_id);
private:
friend class ChildProcessSecurityPolicyInProcessBrowserTest;
diff --git a/chromium/content/browser/child_process_security_policy_unittest.cc b/chromium/content/browser/child_process_security_policy_unittest.cc
index c4d51b3eb38..97c1889b3d7 100644
--- a/chromium/content/browser/child_process_security_policy_unittest.cc
+++ b/chromium/content/browser/child_process_security_policy_unittest.cc
@@ -7,7 +7,6 @@
#include "base/basictypes.h"
#include "base/files/file_path.h"
-#include "base/platform_file.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/public/common/url_constants.h"
#include "content/test/test_content_browser_client.h"
@@ -63,13 +62,13 @@ class ChildProcessSecurityPolicyTest : public testing::Test {
// Claim to always handle chrome:// URLs because the CPSP's notion of
// allowing WebUI bindings is hard-wired to this particular scheme.
- test_browser_client_.AddScheme(chrome::kChromeUIScheme);
+ test_browser_client_.AddScheme(kChromeUIScheme);
// Claim to always handle file:// URLs like the browser would.
// net::URLRequest::IsHandledURL() no longer claims support for default
// protocols as this is the responsibility of the browser (which is
// responsible for adding the appropriate ProtocolHandler).
- test_browser_client_.AddScheme(chrome::kFileScheme);
+ test_browser_client_.AddScheme(url::kFileScheme);
}
virtual void TearDown() {
@@ -120,34 +119,34 @@ TEST_F(ChildProcessSecurityPolicyTest, IsWebSafeSchemeTest) {
ChildProcessSecurityPolicyImpl* p =
ChildProcessSecurityPolicyImpl::GetInstance();
- EXPECT_TRUE(p->IsWebSafeScheme(kHttpScheme));
- EXPECT_TRUE(p->IsWebSafeScheme(kHttpsScheme));
- EXPECT_TRUE(p->IsWebSafeScheme(kFtpScheme));
- EXPECT_TRUE(p->IsWebSafeScheme(chrome::kDataScheme));
+ EXPECT_TRUE(p->IsWebSafeScheme(url::kHttpScheme));
+ EXPECT_TRUE(p->IsWebSafeScheme(url::kHttpsScheme));
+ EXPECT_TRUE(p->IsWebSafeScheme(url::kFtpScheme));
+ EXPECT_TRUE(p->IsWebSafeScheme(url::kDataScheme));
EXPECT_TRUE(p->IsWebSafeScheme("feed"));
- EXPECT_TRUE(p->IsWebSafeScheme(chrome::kBlobScheme));
- EXPECT_TRUE(p->IsWebSafeScheme(chrome::kFileSystemScheme));
+ EXPECT_TRUE(p->IsWebSafeScheme(url::kBlobScheme));
+ EXPECT_TRUE(p->IsWebSafeScheme(url::kFileSystemScheme));
EXPECT_FALSE(p->IsWebSafeScheme("registered-web-safe-scheme"));
p->RegisterWebSafeScheme("registered-web-safe-scheme");
EXPECT_TRUE(p->IsWebSafeScheme("registered-web-safe-scheme"));
- EXPECT_FALSE(p->IsWebSafeScheme(chrome::kChromeUIScheme));
+ EXPECT_FALSE(p->IsWebSafeScheme(kChromeUIScheme));
}
TEST_F(ChildProcessSecurityPolicyTest, IsPseudoSchemeTest) {
ChildProcessSecurityPolicyImpl* p =
ChildProcessSecurityPolicyImpl::GetInstance();
- EXPECT_TRUE(p->IsPseudoScheme(chrome::kAboutScheme));
- EXPECT_TRUE(p->IsPseudoScheme(kJavaScriptScheme));
+ EXPECT_TRUE(p->IsPseudoScheme(url::kAboutScheme));
+ EXPECT_TRUE(p->IsPseudoScheme(url::kJavaScriptScheme));
EXPECT_TRUE(p->IsPseudoScheme(kViewSourceScheme));
EXPECT_FALSE(p->IsPseudoScheme("registered-pseudo-scheme"));
p->RegisterPseudoScheme("registered-pseudo-scheme");
EXPECT_TRUE(p->IsPseudoScheme("registered-pseudo-scheme"));
- EXPECT_FALSE(p->IsPseudoScheme(chrome::kChromeUIScheme));
+ EXPECT_FALSE(p->IsPseudoScheme(kChromeUIScheme));
}
TEST_F(ChildProcessSecurityPolicyTest, StandardSchemesTest) {
@@ -467,130 +466,130 @@ TEST_F(ChildProcessSecurityPolicyTest, FilePermissions) {
// Grant permissions for a file.
p->Add(kRendererID);
EXPECT_FALSE(p->HasPermissionsForFile(kRendererID, granted_file,
- base::PLATFORM_FILE_OPEN));
+ base::File::FLAG_OPEN));
GrantPermissionsForFile(p, kRendererID, granted_file,
- base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_OPEN_TRUNCATED |
- base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_WRITE);
+ base::File::FLAG_OPEN |
+ base::File::FLAG_OPEN_TRUNCATED |
+ base::File::FLAG_READ |
+ base::File::FLAG_WRITE);
EXPECT_TRUE(p->HasPermissionsForFile(kRendererID, granted_file,
- base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_OPEN_TRUNCATED |
- base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_WRITE));
+ base::File::FLAG_OPEN |
+ base::File::FLAG_OPEN_TRUNCATED |
+ base::File::FLAG_READ |
+ base::File::FLAG_WRITE));
EXPECT_TRUE(p->HasPermissionsForFile(kRendererID, granted_file,
- base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ));
+ base::File::FLAG_OPEN |
+ base::File::FLAG_READ));
EXPECT_FALSE(p->HasPermissionsForFile(kRendererID, granted_file,
- base::PLATFORM_FILE_CREATE));
+ base::File::FLAG_CREATE));
EXPECT_FALSE(p->HasPermissionsForFile(kRendererID, granted_file, 0));
EXPECT_FALSE(p->HasPermissionsForFile(kRendererID, granted_file,
- base::PLATFORM_FILE_CREATE |
- base::PLATFORM_FILE_OPEN_TRUNCATED |
- base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_WRITE));
+ base::File::FLAG_CREATE |
+ base::File::FLAG_OPEN_TRUNCATED |
+ base::File::FLAG_READ |
+ base::File::FLAG_WRITE));
EXPECT_FALSE(p->HasPermissionsForFile(kRendererID, sibling_file,
- base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ));
+ base::File::FLAG_OPEN |
+ base::File::FLAG_READ));
EXPECT_FALSE(p->HasPermissionsForFile(kRendererID, parent_file,
- base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ));
+ base::File::FLAG_OPEN |
+ base::File::FLAG_READ));
EXPECT_TRUE(p->HasPermissionsForFile(kRendererID, child_file,
- base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ));
+ base::File::FLAG_OPEN |
+ base::File::FLAG_READ));
EXPECT_TRUE(p->HasPermissionsForFile(kRendererID, child_traversal1,
- base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ));
+ base::File::FLAG_OPEN |
+ base::File::FLAG_READ));
EXPECT_TRUE(p->HasPermissionsForFile(kRendererID, child_traversal2,
- base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ));
+ base::File::FLAG_OPEN |
+ base::File::FLAG_READ));
EXPECT_FALSE(p->HasPermissionsForFile(kRendererID, evil_traversal1,
- base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ));
+ base::File::FLAG_OPEN |
+ base::File::FLAG_READ));
EXPECT_FALSE(p->HasPermissionsForFile(kRendererID, evil_traversal2,
- base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ));
+ base::File::FLAG_OPEN |
+ base::File::FLAG_READ));
// CPSP doesn't allow this case for the sake of simplicity.
EXPECT_FALSE(p->HasPermissionsForFile(kRendererID, self_traversal,
- base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ));
+ base::File::FLAG_OPEN |
+ base::File::FLAG_READ));
p->Remove(kRendererID);
// Grant permissions for the directory the file is in.
p->Add(kRendererID);
EXPECT_FALSE(p->HasPermissionsForFile(kRendererID, granted_file,
- base::PLATFORM_FILE_OPEN));
+ base::File::FLAG_OPEN));
GrantPermissionsForFile(p, kRendererID, parent_file,
- base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ);
+ base::File::FLAG_OPEN |
+ base::File::FLAG_READ);
EXPECT_TRUE(p->HasPermissionsForFile(kRendererID, granted_file,
- base::PLATFORM_FILE_OPEN));
+ base::File::FLAG_OPEN));
EXPECT_FALSE(p->HasPermissionsForFile(kRendererID, granted_file,
- base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_WRITE));
+ base::File::FLAG_READ |
+ base::File::FLAG_WRITE));
p->Remove(kRendererID);
// Grant permissions for the directory the file is in (with trailing '/').
p->Add(kRendererID);
EXPECT_FALSE(p->HasPermissionsForFile(kRendererID, granted_file,
- base::PLATFORM_FILE_OPEN));
+ base::File::FLAG_OPEN));
GrantPermissionsForFile(p, kRendererID, parent_slash_file,
- base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ);
+ base::File::FLAG_OPEN |
+ base::File::FLAG_READ);
EXPECT_TRUE(p->HasPermissionsForFile(kRendererID, granted_file,
- base::PLATFORM_FILE_OPEN));
+ base::File::FLAG_OPEN));
EXPECT_FALSE(p->HasPermissionsForFile(kRendererID, granted_file,
- base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_WRITE));
+ base::File::FLAG_READ |
+ base::File::FLAG_WRITE));
// Grant permissions for the file (should overwrite the permissions granted
// for the directory).
GrantPermissionsForFile(p, kRendererID, granted_file,
- base::PLATFORM_FILE_TEMPORARY);
+ base::File::FLAG_TEMPORARY);
EXPECT_FALSE(p->HasPermissionsForFile(kRendererID, granted_file,
- base::PLATFORM_FILE_OPEN));
+ base::File::FLAG_OPEN));
EXPECT_TRUE(p->HasPermissionsForFile(kRendererID, granted_file,
- base::PLATFORM_FILE_TEMPORARY));
+ base::File::FLAG_TEMPORARY));
// Revoke all permissions for the file (it should inherit its permissions
// from the directory again).
p->RevokeAllPermissionsForFile(kRendererID, granted_file);
EXPECT_TRUE(p->HasPermissionsForFile(kRendererID, granted_file,
- base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ));
+ base::File::FLAG_OPEN |
+ base::File::FLAG_READ));
EXPECT_FALSE(p->HasPermissionsForFile(kRendererID, granted_file,
- base::PLATFORM_FILE_TEMPORARY));
+ base::File::FLAG_TEMPORARY));
p->Remove(kRendererID);
// Grant file permissions for the file to main thread renderer process,
// make sure its worker thread renderer process inherits those.
p->Add(kRendererID);
GrantPermissionsForFile(p, kRendererID, granted_file,
- base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ);
+ base::File::FLAG_OPEN |
+ base::File::FLAG_READ);
EXPECT_TRUE(p->HasPermissionsForFile(kRendererID, granted_file,
- base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ));
+ base::File::FLAG_OPEN |
+ base::File::FLAG_READ));
EXPECT_FALSE(p->HasPermissionsForFile(kRendererID, granted_file,
- base::PLATFORM_FILE_WRITE));
+ base::File::FLAG_WRITE));
p->AddWorker(kWorkerRendererID, kRendererID);
EXPECT_TRUE(p->HasPermissionsForFile(kWorkerRendererID, granted_file,
- base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ));
+ base::File::FLAG_OPEN |
+ base::File::FLAG_READ));
EXPECT_FALSE(p->HasPermissionsForFile(kWorkerRendererID, granted_file,
- base::PLATFORM_FILE_WRITE));
+ base::File::FLAG_WRITE));
p->Remove(kRendererID);
EXPECT_FALSE(p->HasPermissionsForFile(kWorkerRendererID, granted_file,
- base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ));
+ base::File::FLAG_OPEN |
+ base::File::FLAG_READ));
p->Remove(kWorkerRendererID);
p->Add(kRendererID);
GrantPermissionsForFile(p, kRendererID, relative_file,
- base::PLATFORM_FILE_OPEN);
+ base::File::FLAG_OPEN);
EXPECT_FALSE(p->HasPermissionsForFile(kRendererID, relative_file,
- base::PLATFORM_FILE_OPEN));
+ base::File::FLAG_OPEN));
p->Remove(kRendererID);
}
diff --git a/chromium/content/browser/aura/OWNERS b/chromium/content/browser/compositor/OWNERS
index 3b61cda8291..3b61cda8291 100644
--- a/chromium/content/browser/aura/OWNERS
+++ b/chromium/content/browser/compositor/OWNERS
diff --git a/chromium/content/browser/aura/browser_compositor_output_surface.cc b/chromium/content/browser/compositor/browser_compositor_output_surface.cc
index 2125d3845f4..40576b2aeaf 100644
--- a/chromium/content/browser/aura/browser_compositor_output_surface.cc
+++ b/chromium/content/browser/compositor/browser_compositor_output_surface.cc
@@ -1,17 +1,15 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// 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/aura/browser_compositor_output_surface.h"
+#include "content/browser/compositor/browser_compositor_output_surface.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/location.h"
-#include "base/message_loop/message_loop_proxy.h"
#include "base/strings/string_number_conversions.h"
-#include "content/browser/aura/reflector_impl.h"
+#include "content/browser/compositor/reflector_impl.h"
#include "content/common/gpu/client/context_provider_command_buffer.h"
-#include "ui/compositor/compositor.h"
#include "ui/compositor/compositor_switches.h"
namespace content {
@@ -20,13 +18,11 @@ BrowserCompositorOutputSurface::BrowserCompositorOutputSurface(
const scoped_refptr<ContextProviderCommandBuffer>& context_provider,
int surface_id,
IDMap<BrowserCompositorOutputSurface>* output_surface_map,
- base::MessageLoopProxy* compositor_message_loop,
- base::WeakPtr<ui::Compositor> compositor)
+ const scoped_refptr<ui::CompositorVSyncManager>& vsync_manager)
: OutputSurface(context_provider),
surface_id_(surface_id),
output_surface_map_(output_surface_map),
- compositor_message_loop_(compositor_message_loop),
- compositor_(compositor) {
+ vsync_manager_(vsync_manager) {
Initialize();
}
@@ -34,34 +30,27 @@ BrowserCompositorOutputSurface::BrowserCompositorOutputSurface(
scoped_ptr<cc::SoftwareOutputDevice> software_device,
int surface_id,
IDMap<BrowserCompositorOutputSurface>* output_surface_map,
- base::MessageLoopProxy* compositor_message_loop,
- base::WeakPtr<ui::Compositor> compositor)
+ const scoped_refptr<ui::CompositorVSyncManager>& vsync_manager)
: OutputSurface(software_device.Pass()),
surface_id_(surface_id),
output_surface_map_(output_surface_map),
- compositor_message_loop_(compositor_message_loop),
- compositor_(compositor) {
+ vsync_manager_(vsync_manager) {
Initialize();
}
BrowserCompositorOutputSurface::~BrowserCompositorOutputSurface() {
DCHECK(CalledOnValidThread());
+ if (reflector_)
+ reflector_->DetachFromOutputSurface();
+ DCHECK(!reflector_);
if (!HasClient())
return;
output_surface_map_->Remove(surface_id_);
+ vsync_manager_->RemoveObserver(this);
}
void BrowserCompositorOutputSurface::Initialize() {
- CommandLine* command_line = CommandLine::ForCurrentProcess();
- if (command_line->HasSwitch(switches::kUIMaxFramesPending)) {
- std::string string_value = command_line->GetSwitchValueASCII(
- switches::kUIMaxFramesPending);
- int int_value;
- if (base::StringToInt(string_value, &int_value))
- capabilities_.max_frames_pending = int_value;
- else
- LOG(ERROR) << "Trouble parsing --" << switches::kUIMaxFramesPending;
- }
+ capabilities_.max_frames_pending = 1;
capabilities_.adjust_deadline_for_parent = false;
DetachFromThread();
@@ -76,15 +65,17 @@ bool BrowserCompositorOutputSurface::BindToClient(
output_surface_map_->AddWithID(this, surface_id_);
if (reflector_)
- reflector_->OnSourceSurfaceReady(surface_id_);
+ reflector_->OnSourceSurfaceReady(this);
+ vsync_manager_->AddObserver(this);
return true;
}
-void BrowserCompositorOutputSurface::Reshape(gfx::Size size,
- float scale_factor) {
- OutputSurface::Reshape(size, scale_factor);
- if (reflector_.get())
- reflector_->OnReshape(size);
+void BrowserCompositorOutputSurface::OnSwapBuffersComplete() {
+ // On Mac, delay acknowledging the swap to the output surface client until
+ // it has been drawn.
+#if !defined(OS_MACOSX)
+ cc::OutputSurface::OnSwapBuffersComplete();
+#endif
}
void BrowserCompositorOutputSurface::OnUpdateVSyncParameters(
@@ -92,15 +83,25 @@ void BrowserCompositorOutputSurface::OnUpdateVSyncParameters(
base::TimeDelta interval) {
DCHECK(CalledOnValidThread());
DCHECK(HasClient());
- OnVSyncParametersChanged(timebase, interval);
- compositor_message_loop_->PostTask(
- FROM_HERE,
- base::Bind(&ui::Compositor::OnUpdateVSyncParameters,
- compositor_, timebase, interval));
+ CommitVSyncParameters(timebase, interval);
+}
+
+void BrowserCompositorOutputSurface::OnUpdateVSyncParametersFromGpu(
+ base::TimeTicks timebase,
+ base::TimeDelta interval) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(HasClient());
+ vsync_manager_->UpdateVSyncParameters(timebase, interval);
}
void BrowserCompositorOutputSurface::SetReflector(ReflectorImpl* reflector) {
reflector_ = reflector;
}
+#if defined(OS_MACOSX)
+void BrowserCompositorOutputSurface::OnSurfaceDisplayed() {
+ cc::OutputSurface::OnSwapBuffersComplete();
+}
+#endif
+
} // namespace content
diff --git a/chromium/content/browser/aura/browser_compositor_output_surface.h b/chromium/content/browser/compositor/browser_compositor_output_surface.h
index 79f757a56a2..e622f41c497 100644
--- a/chromium/content/browser/aura/browser_compositor_output_surface.h
+++ b/chromium/content/browser/compositor/browser_compositor_output_surface.h
@@ -1,24 +1,20 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// 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_AURA_BROWSER_COMPOSITOR_OUTPUT_SURFACE_H_
-#define CONTENT_BROWSER_AURA_BROWSER_COMPOSITOR_OUTPUT_SURFACE_H_
+#ifndef CONTENT_BROWSER_COMPOSITOR_BROWSER_COMPOSITOR_OUTPUT_SURFACE_H_
+#define CONTENT_BROWSER_COMPOSITOR_BROWSER_COMPOSITOR_OUTPUT_SURFACE_H_
#include "base/id_map.h"
-#include "base/memory/weak_ptr.h"
#include "base/threading/non_thread_safe.h"
#include "cc/output/output_surface.h"
#include "content/common/content_export.h"
-
-namespace base { class MessageLoopProxy; }
+#include "ui/compositor/compositor_vsync_manager.h"
namespace cc {
class SoftwareOutputDevice;
}
-namespace ui { class Compositor; }
-
namespace content {
class ContextProviderCommandBuffer;
class ReflectorImpl;
@@ -26,41 +22,47 @@ class WebGraphicsContext3DCommandBufferImpl;
class CONTENT_EXPORT BrowserCompositorOutputSurface
: public cc::OutputSurface,
+ public ui::CompositorVSyncManager::Observer,
public base::NonThreadSafe {
public:
virtual ~BrowserCompositorOutputSurface();
// cc::OutputSurface implementation.
virtual bool BindToClient(cc::OutputSurfaceClient* client) OVERRIDE;
- virtual void Reshape(gfx::Size size, float scale_factor) OVERRIDE;
+ virtual void OnSwapBuffersComplete() OVERRIDE;
- void OnUpdateVSyncParameters(base::TimeTicks timebase,
- base::TimeDelta interval);
+ // ui::CompositorOutputSurface::Observer implementation.
+ virtual void OnUpdateVSyncParameters(base::TimeTicks timebase,
+ base::TimeDelta interval) OVERRIDE;
+
+ void OnUpdateVSyncParametersFromGpu(base::TimeTicks tiembase,
+ base::TimeDelta interval);
void SetReflector(ReflectorImpl* reflector);
+#if defined(OS_MACOSX)
+ void OnSurfaceDisplayed();
+#endif
+
protected:
// Constructor used by the accelerated implementation.
BrowserCompositorOutputSurface(
const scoped_refptr<ContextProviderCommandBuffer>& context,
int surface_id,
IDMap<BrowserCompositorOutputSurface>* output_surface_map,
- base::MessageLoopProxy* compositor_message_loop,
- base::WeakPtr<ui::Compositor> compositor);
+ const scoped_refptr<ui::CompositorVSyncManager>& vsync_manager);
// Constructor used by the software implementation.
BrowserCompositorOutputSurface(
scoped_ptr<cc::SoftwareOutputDevice> software_device,
int surface_id,
IDMap<BrowserCompositorOutputSurface>* output_surface_map,
- base::MessageLoopProxy* compositor_message_loop,
- base::WeakPtr<ui::Compositor> compositor);
+ const scoped_refptr<ui::CompositorVSyncManager>& vsync_manager);
int surface_id_;
IDMap<BrowserCompositorOutputSurface>* output_surface_map_;
- scoped_refptr<base::MessageLoopProxy> compositor_message_loop_;
- base::WeakPtr<ui::Compositor> compositor_;
+ scoped_refptr<ui::CompositorVSyncManager> vsync_manager_;
scoped_refptr<ReflectorImpl> reflector_;
private:
@@ -71,4 +73,4 @@ class CONTENT_EXPORT BrowserCompositorOutputSurface
} // namespace content
-#endif // CONTENT_BROWSER_AURA_BROWSER_COMPOSITOR_OUTPUT_SURFACE_H_
+#endif // CONTENT_BROWSER_COMPOSITOR_BROWSER_COMPOSITOR_OUTPUT_SURFACE_H_
diff --git a/chromium/content/browser/aura/browser_compositor_output_surface_proxy.cc b/chromium/content/browser/compositor/browser_compositor_output_surface_proxy.cc
index e86e5b8c69c..27fe55a7ea9 100644
--- a/chromium/content/browser/aura/browser_compositor_output_surface_proxy.cc
+++ b/chromium/content/browser/compositor/browser_compositor_output_surface_proxy.cc
@@ -1,11 +1,11 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// 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/aura/browser_compositor_output_surface_proxy.h"
+#include "content/browser/compositor/browser_compositor_output_surface_proxy.h"
#include "base/bind.h"
-#include "content/browser/aura/browser_compositor_output_surface.h"
+#include "content/browser/compositor/browser_compositor_output_surface.h"
#include "content/browser/gpu/browser_gpu_channel_host_factory.h"
#include "content/common/gpu/gpu_messages.h"
@@ -53,6 +53,6 @@ BrowserCompositorOutputSurfaceProxy::OnUpdateVSyncParametersOnCompositorThread(
base::TimeDelta interval) {
BrowserCompositorOutputSurface* surface = surface_map_->Lookup(surface_id);
if (surface)
- surface->OnUpdateVSyncParameters(timebase, interval);
+ surface->OnUpdateVSyncParametersFromGpu(timebase, interval);
}
} // namespace content
diff --git a/chromium/content/browser/aura/browser_compositor_output_surface_proxy.h b/chromium/content/browser/compositor/browser_compositor_output_surface_proxy.h
index 0c5ffcd6740..644516ea665 100644
--- a/chromium/content/browser/aura/browser_compositor_output_surface_proxy.h
+++ b/chromium/content/browser/compositor/browser_compositor_output_surface_proxy.h
@@ -1,9 +1,9 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// 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_AURA_BROWSER_COMPOSITOR_OUTPUT_SURFACE_PROXY_H_
-#define CONTENT_BROWSER_AURA_BROWSER_COMPOSITOR_OUTPUT_SURFACE_PROXY_H_
+#ifndef CONTENT_BROWSER_COMPOSITOR_BROWSER_COMPOSITOR_OUTPUT_SURFACE_PROXY_H_
+#define CONTENT_BROWSER_COMPOSITOR_BROWSER_COMPOSITOR_OUTPUT_SURFACE_PROXY_H_
#include "base/id_map.h"
#include "base/memory/ref_counted.h"
@@ -48,4 +48,4 @@ class CONTENT_EXPORT BrowserCompositorOutputSurfaceProxy
} // namespace content
-#endif // CONTENT_BROWSER_AURA_BROWSER_COMPOSITOR_OUTPUT_SURFACE_PROXY_H_
+#endif // CONTENT_BROWSER_COMPOSITOR_BROWSER_COMPOSITOR_OUTPUT_SURFACE_PROXY_H_
diff --git a/chromium/content/browser/compositor/browser_compositor_view_mac.h b/chromium/content/browser/compositor/browser_compositor_view_mac.h
new file mode 100644
index 00000000000..5d22b873dae
--- /dev/null
+++ b/chromium/content/browser/compositor/browser_compositor_view_mac.h
@@ -0,0 +1,64 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_COMPOSITOR_BROWSER_COMPOSITOR_VIEW_MAC_H_
+#define CONTENT_BROWSER_COMPOSITOR_BROWSER_COMPOSITOR_VIEW_MAC_H_
+
+#import <Cocoa/Cocoa.h>
+#include <IOSurface/IOSurfaceAPI.h>
+
+#include "base/mac/scoped_nsobject.h"
+#include "cc/output/software_frame_data.h"
+#include "content/browser/renderer_host/compositing_iosurface_layer_mac.h"
+#include "content/browser/renderer_host/software_layer_mac.h"
+#include "skia/ext/platform_canvas.h"
+#include "ui/compositor/compositor.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace content {
+class BrowserCompositorViewMacHelper;
+} // namespace content
+
+// Additions to the NSView interface for compositor frames.
+@interface NSView (BrowserCompositorView)
+- (void)gotAcceleratedIOSurfaceFrame:(IOSurfaceID)surface_handle
+ withOutputSurfaceID:(int)surface_id
+ withPixelSize:(gfx::Size)pixel_size
+ withScaleFactor:(float)scale_factor;
+
+- (void)gotSoftwareFrame:(cc::SoftwareFrameData*)frame_data
+ withScaleFactor:(float)scale_factor
+ withCanvas:(SkCanvas*)canvas;
+@end // NSView (BrowserCompositorView)
+
+// NSView drawn by a ui::Compositor. The superview of this view is responsible
+// for changing the ui::Compositor SizeAndScale and calling layoutLayers when
+// the size of the parent view may change. This interface is patterned after
+// the needs of RenderWidgetHostViewCocoa, and could change.
+@interface BrowserCompositorViewMac : NSView {
+ scoped_ptr<ui::Compositor> compositor_;
+
+ base::scoped_nsobject<CALayer> background_layer_;
+ base::scoped_nsobject<CompositingIOSurfaceLayer> accelerated_layer_;
+ int accelerated_layer_output_surface_id_;
+ base::scoped_nsobject<SoftwareLayer> software_layer_;
+
+ scoped_ptr<content::BrowserCompositorViewMacHelper> helper_;
+}
+
+// Initialize to render the content of a specific superview.
+- (id)initWithSuperview:(NSView*)view;
+
+// Re-position the layers to the correct place when this view's superview
+// changes size, or when the accelerated or software content changes.
+- (void)layoutLayers;
+
+// Disallow further access to the client.
+- (void)resetClient;
+
+// Access the underlying ui::Compositor for this view.
+- (ui::Compositor*)compositor;
+@end // BrowserCompositorViewMac
+
+#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
new file mode 100644
index 00000000000..0a89e691075
--- /dev/null
+++ b/chromium/content/browser/compositor/browser_compositor_view_mac.mm
@@ -0,0 +1,256 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/compositor/browser_compositor_view_mac.h"
+
+#include "base/debug/trace_event.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "content/browser/compositor/gpu_process_transport_factory.h"
+#include "content/browser/renderer_host/compositing_iosurface_context_mac.h"
+#include "content/browser/renderer_host/compositing_iosurface_mac.h"
+#include "content/browser/renderer_host/software_layer_mac.h"
+#include "content/public/browser/context_factory.h"
+#include "ui/base/cocoa/animation_utils.h"
+#include "ui/gl/scoped_cgl.h"
+
+@interface BrowserCompositorViewMac (Private)
+- (void)layerDidDrawFrame;
+- (void)gotAcceleratedLayerError;
+@end // BrowserCompositorViewMac (Private)
+
+namespace content {
+
+// The CompositingIOSurfaceLayerClient interface needs to be implemented as a
+// C++ class to operate on, rather than Objective C class. This helper class
+// provides a bridge between the two.
+class BrowserCompositorViewMacHelper : public CompositingIOSurfaceLayerClient {
+ public:
+ BrowserCompositorViewMacHelper(BrowserCompositorViewMac* view)
+ : view_(view) {}
+ virtual ~BrowserCompositorViewMacHelper() {}
+
+ private:
+ // CompositingIOSurfaceLayerClient implementation:
+ virtual void AcceleratedLayerDidDrawFrame(bool succeeded) OVERRIDE {
+ [view_ layerDidDrawFrame];
+ if (!succeeded)
+ [view_ gotAcceleratedLayerError];
+ }
+
+ BrowserCompositorViewMac* view_;
+};
+
+} // namespace content
+
+
+// The default implementation of additions to the NSView interface for browser
+// compositing should never be called. Log an error if they are.
+@implementation NSView (BrowserCompositorView)
+
+- (void)gotAcceleratedIOSurfaceFrame:(IOSurfaceID)surface_handle
+ withOutputSurfaceID:(int)surface_id
+ withPixelSize:(gfx::Size)pixel_size
+ withScaleFactor:(float)scale_factor {
+ DLOG(ERROR) << "-[NSView gotAcceleratedIOSurfaceFrame] called on "
+ << "non-overriden class.";
+}
+
+- (void)gotSoftwareFrame:(cc::SoftwareFrameData*)frame_data
+ withScaleFactor:(float)scale_factor
+ withCanvas:(SkCanvas*)canvas {
+ DLOG(ERROR) << "-[NSView gotSoftwareFrame] called on non-overridden class.";
+}
+
+@end // NSView (BrowserCompositorView)
+
+@implementation BrowserCompositorViewMac : NSView
+
+- (id)initWithSuperview:(NSView*)view {
+ if (self = [super init]) {
+ accelerated_layer_output_surface_id_ = 0;
+ helper_.reset(new content::BrowserCompositorViewMacHelper(self));
+
+ // Disable the fade-in animation as the layer and view are added.
+ ScopedCAActionDisabler disabler;
+
+ // Make this view host a transparent layer.
+ background_layer_.reset([[CALayer alloc] init]);
+ [background_layer_ setContentsGravity:kCAGravityTopLeft];
+ [self setLayer:background_layer_];
+ [self setWantsLayer:YES];
+
+ compositor_.reset(new ui::Compositor(self, content::GetContextFactory()));
+ [view addSubview:self];
+ }
+ return self;
+}
+
+- (void)gotAcceleratedLayerError {
+ if (!accelerated_layer_)
+ return;
+
+ [accelerated_layer_ context]->PoisonContextAndSharegroup();
+ compositor_->ScheduleFullRedraw();
+}
+
+// This function closely mirrors RenderWidgetHostViewMac::LayoutLayers. When
+// only delegated rendering is supported, only one copy of this code will
+// need to exist.
+- (void)layoutLayers {
+ // Disable animation of the layers' resizing or repositioning.
+ ScopedCAActionDisabler disabler;
+
+ NSSize superview_frame_size = [[self superview] frame].size;
+ [self setFrame:NSMakeRect(
+ 0, 0, superview_frame_size.width, superview_frame_size.height)];
+
+ CGRect new_background_frame = CGRectMake(
+ 0,
+ 0,
+ superview_frame_size.width,
+ superview_frame_size.height);
+ [background_layer_ setFrame:new_background_frame];
+
+ // The bounds of the accelerated layer determine the size of the GL surface
+ // that will be drawn to. Make sure that this is big enough to draw the
+ // IOSurface.
+ if (accelerated_layer_) {
+ CGRect layer_bounds = CGRectMake(
+ 0,
+ 0,
+ [accelerated_layer_ iosurface]->dip_io_surface_size().width(),
+ [accelerated_layer_ iosurface]->dip_io_surface_size().height());
+ CGPoint layer_position = CGPointMake(
+ 0,
+ CGRectGetHeight(new_background_frame) - CGRectGetHeight(layer_bounds));
+ bool bounds_changed = !CGRectEqualToRect(
+ layer_bounds, [accelerated_layer_ bounds]);
+ [accelerated_layer_ setBounds:layer_bounds];
+ [accelerated_layer_ setPosition:layer_position];
+ if (bounds_changed) {
+ [accelerated_layer_ setNeedsDisplay];
+ [accelerated_layer_ displayIfNeeded];
+ }
+ }
+
+ // The content area of the software layer is the size of the image provided.
+ // Make the bounds of the layer match the superview's bounds, to ensure that
+ // the visible contents are drawn.
+ [software_layer_ setBounds:new_background_frame];
+}
+
+- (void)resetClient {
+ [accelerated_layer_ resetClient];
+}
+
+- (ui::Compositor*)compositor {
+ return compositor_.get();
+}
+
+- (void)gotAcceleratedIOSurfaceFrame:(IOSurfaceID)surface_handle
+ withOutputSurfaceID:(int)surface_id
+ withPixelSize:(gfx::Size)pixel_size
+ withScaleFactor:(float)scale_factor {
+ DCHECK(!accelerated_layer_output_surface_id_);
+ accelerated_layer_output_surface_id_ = surface_id;
+
+ ScopedCAActionDisabler disabler;
+
+ // If there is already an accelerated layer, but it has the wrong scale
+ // factor or it was poisoned, remove the old layer and replace it.
+ base::scoped_nsobject<CompositingIOSurfaceLayer> old_accelerated_layer;
+ if (accelerated_layer_ && (
+ [accelerated_layer_ context]->HasBeenPoisoned() ||
+ [accelerated_layer_ iosurface]->scale_factor() != scale_factor)) {
+ old_accelerated_layer = accelerated_layer_;
+ accelerated_layer_.reset();
+ }
+
+ // If there is not a layer for accelerated frames, create one.
+ if (!accelerated_layer_) {
+ // Disable the fade-in animation as the layer is added.
+ ScopedCAActionDisabler disabler;
+ scoped_refptr<content::CompositingIOSurfaceMac> iosurface =
+ content::CompositingIOSurfaceMac::Create();
+ accelerated_layer_.reset([[CompositingIOSurfaceLayer alloc]
+ initWithIOSurface:iosurface
+ withScaleFactor:scale_factor
+ withClient:helper_.get()]);
+ [[self layer] addSublayer:accelerated_layer_];
+ }
+
+ {
+ bool result = true;
+ gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
+ [accelerated_layer_ context]->cgl_context());
+ result = [accelerated_layer_ iosurface]->SetIOSurfaceWithContextCurrent(
+ [accelerated_layer_ context], surface_handle, pixel_size, scale_factor);
+ if (!result)
+ LOG(ERROR) << "Failed SetIOSurface on CompositingIOSurfaceMac";
+ }
+ [accelerated_layer_ gotNewFrame];
+ [self layoutLayers];
+
+ // If there was a software layer or an old accelerated layer, remove it.
+ // Disable the fade-out animation as the layer is removed.
+ {
+ ScopedCAActionDisabler disabler;
+ [software_layer_ removeFromSuperlayer];
+ software_layer_.reset();
+ [old_accelerated_layer resetClient];
+ [old_accelerated_layer removeFromSuperlayer];
+ old_accelerated_layer.reset();
+ }
+}
+
+- (void)gotSoftwareFrame:(cc::SoftwareFrameData*)frame_data
+ withScaleFactor:(float)scale_factor
+ withCanvas:(SkCanvas*)canvas {
+ if (!frame_data || !canvas)
+ return;
+
+ // If there is not a layer for software frames, create one.
+ if (!software_layer_) {
+ // Disable the fade-in animation as the layer is added.
+ ScopedCAActionDisabler disabler;
+ software_layer_.reset([[SoftwareLayer alloc] init]);
+ [[self layer] addSublayer:software_layer_];
+ }
+
+ SkImageInfo info;
+ size_t row_bytes;
+ const void* pixels = canvas->peekPixels(&info, &row_bytes);
+ [software_layer_ setContentsToData:pixels
+ withRowBytes:row_bytes
+ withPixelSize:gfx::Size(info.fWidth, info.fHeight)
+ withScaleFactor:scale_factor];
+ [self layoutLayers];
+
+ // If there was an accelerated layer, remove it.
+ // Disable the fade-out animation as the layer is removed.
+ {
+ ScopedCAActionDisabler disabler;
+ [accelerated_layer_ resetClient];
+ [accelerated_layer_ removeFromSuperlayer];
+ accelerated_layer_.reset();
+ }
+
+ // This call can be nested insider ui::Compositor commit calls, and can also
+ // make additional ui::Compositor commit calls. Avoid the potential recursion
+ // by acknowledging the frame asynchronously.
+ [self performSelector:@selector(layerDidDrawFrame)
+ withObject:nil
+ afterDelay:0];
+}
+
+- (void)layerDidDrawFrame {
+ if (!accelerated_layer_output_surface_id_)
+ return;
+
+ content::ImageTransportFactory::GetInstance()->OnSurfaceDisplayed(
+ accelerated_layer_output_surface_id_);
+ accelerated_layer_output_surface_id_ = 0;
+}
+
+@end // BrowserCompositorViewMac
diff --git a/chromium/content/browser/compositor/delegated_frame_host.cc b/chromium/content/browser/compositor/delegated_frame_host.cc
new file mode 100644
index 00000000000..5db69b70b4c
--- /dev/null
+++ b/chromium/content/browser/compositor/delegated_frame_host.cc
@@ -0,0 +1,852 @@
+// 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 "base/callback_helpers.h"
+#include "base/command_line.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 "content/browser/compositor/resize_lock.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"
+
+namespace content {
+
+////////////////////////////////////////////////////////////////////////////////
+// DelegatedFrameHostClient
+
+bool DelegatedFrameHostClient::ShouldCreateResizeLock() {
+ // On Windows and Linux, holding pointer moves will not help throttling
+ // resizes.
+ // TODO(piman): on Windows we need to block (nested message loop?) the
+ // WM_SIZE event. On Linux we need to throttle at the WM level using
+ // _NET_WM_SYNC_REQUEST.
+ // TODO(ccameron): Mac browser window resizing is incompletely implemented.
+#if !defined(OS_CHROMEOS)
+ return false;
+#else
+ return GetDelegatedFrameHost()->ShouldCreateResizeLock();
+#endif
+}
+
+void DelegatedFrameHostClient::RequestCopyOfOutput(
+ scoped_ptr<cc::CopyOutputRequest> request) {
+ GetDelegatedFrameHost()->RequestCopyOfOutput(request.Pass());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// DelegatedFrameHost
+
+DelegatedFrameHost::DelegatedFrameHost(DelegatedFrameHostClient* client)
+ : client_(client),
+ last_output_surface_id_(0),
+ pending_delegated_ack_count_(0),
+ skipped_frames_(false),
+ can_lock_compositor_(YES_CAN_LOCK),
+ delegated_frame_evictor_(new DelegatedFrameEvictor(this)) {
+ ImageTransportFactory::GetInstance()->AddObserver(this);
+}
+
+void DelegatedFrameHost::WasShown() {
+ delegated_frame_evictor_->SetVisible(true);
+
+ if (!released_front_lock_.get()) {
+ ui::Compositor* compositor = client_->GetCompositor();
+ if (compositor)
+ released_front_lock_ = compositor->GetCompositorLock();
+ }
+}
+
+void DelegatedFrameHost::WasHidden() {
+ delegated_frame_evictor_->SetVisible(false);
+ released_front_lock_ = NULL;
+}
+
+void DelegatedFrameHost::MaybeCreateResizeLock() {
+ if (!client_->ShouldCreateResizeLock())
+ return;
+ DCHECK(client_->GetCompositor());
+
+ // Listen to changes in the compositor lock state.
+ ui::Compositor* compositor = client_->GetCompositor();
+ if (!compositor->HasObserver(this))
+ compositor->AddObserver(this);
+
+ 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_->CreateResizeLock(defer_compositor_lock);
+}
+
+bool DelegatedFrameHost::ShouldCreateResizeLock() {
+ RenderWidgetHostImpl* host = client_->GetHost();
+
+ if (resize_lock_)
+ return false;
+
+ if (host->should_auto_resize())
+ return false;
+
+ gfx::Size desired_size = client_->DesiredFrameSize();
+ if (desired_size == current_frame_size_in_dip_ || desired_size.IsEmpty())
+ return false;
+
+ ui::Compositor* compositor = client_->GetCompositor();
+ if (!compositor)
+ return false;
+
+ return true;
+}
+
+void DelegatedFrameHost::RequestCopyOfOutput(
+ scoped_ptr<cc::CopyOutputRequest> request) {
+ client_->GetLayer()->RequestCopyOfOutput(request.Pass());
+}
+
+void DelegatedFrameHost::CopyFromCompositingSurface(
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ const base::Callback<void(bool, const SkBitmap&)>& callback,
+ const SkBitmap::Config config) {
+ // Only ARGB888 and RGB565 supported as of now.
+ bool format_support = ((config == SkBitmap::kRGB_565_Config) ||
+ (config == SkBitmap::kARGB_8888_Config));
+ DCHECK(format_support);
+ if (!CanCopyToBitmap()) {
+ callback.Run(false, SkBitmap());
+ return;
+ }
+
+ const gfx::Size& dst_size_in_pixel =
+ client_->ConvertViewSizeToPixel(dst_size);
+ scoped_ptr<cc::CopyOutputRequest> request =
+ cc::CopyOutputRequest::CreateRequest(base::Bind(
+ &DelegatedFrameHost::CopyFromCompositingSurfaceHasResult,
+ dst_size_in_pixel,
+ config,
+ callback));
+ gfx::Rect src_subrect_in_pixel =
+ ConvertRectToPixel(client_->CurrentDeviceScaleFactor(), src_subrect);
+ request->set_area(src_subrect_in_pixel);
+ client_->RequestCopyOfOutput(request.Pass());
+}
+
+void DelegatedFrameHost::CopyFromCompositingSurfaceToVideoFrame(
+ const gfx::Rect& src_subrect,
+ const scoped_refptr<media::VideoFrame>& target,
+ const base::Callback<void(bool)>& callback) {
+ if (!CanCopyToVideoFrame()) {
+ callback.Run(false);
+ return;
+ }
+
+ // Try get a texture to reuse.
+ scoped_refptr<OwnedMailbox> subscriber_texture;
+ if (frame_subscriber_) {
+ 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<cc::CopyOutputRequest> request =
+ cc::CopyOutputRequest::CreateRequest(base::Bind(
+ &DelegatedFrameHost::
+ CopyFromCompositingSurfaceHasResultForVideo,
+ AsWeakPtr(), // For caching the ReadbackYUVInterface on this class.
+ subscriber_texture,
+ target,
+ callback));
+ gfx::Rect src_subrect_in_pixel =
+ ConvertRectToPixel(client_->CurrentDeviceScaleFactor(), src_subrect);
+ request->set_area(src_subrect_in_pixel);
+ if (subscriber_texture.get()) {
+ request->SetTextureMailbox(
+ cc::TextureMailbox(subscriber_texture->mailbox(),
+ subscriber_texture->target(),
+ subscriber_texture->sync_point()));
+ }
+ client_->RequestCopyOfOutput(request.Pass());
+}
+
+bool DelegatedFrameHost::CanCopyToBitmap() const {
+ return client_->GetCompositor() &&
+ client_->GetLayer()->has_external_content();
+}
+
+bool DelegatedFrameHost::CanCopyToVideoFrame() const {
+ return client_->GetCompositor() &&
+ client_->GetLayer()->has_external_content();
+}
+
+bool DelegatedFrameHost::CanSubscribeFrame() const {
+ return true;
+}
+
+void DelegatedFrameHost::BeginFrameSubscription(
+ scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) {
+ frame_subscriber_ = subscriber.Pass();
+}
+
+void DelegatedFrameHost::EndFrameSubscription() {
+ idle_frame_subscriber_textures_.clear();
+ frame_subscriber_.reset();
+}
+
+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() {
+ MaybeCreateResizeLock();
+}
+
+gfx::Size DelegatedFrameHost::GetRequestedRendererSize() const {
+ if (resize_lock_)
+ return resize_lock_->expected_size();
+ else
+ return client_->DesiredFrameSize();
+}
+
+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();
+ ui::Compositor* compositor = client_->GetCompositor();
+ if (compositor) {
+ if (!compositor->HasObserver(this))
+ compositor->AddObserver(this);
+ }
+}
+
+void DelegatedFrameHost::DidReceiveFrameFromRenderer() {
+ if (frame_subscriber() && CanCopyToVideoFrame()) {
+ const base::TimeTicks present_time = base::TimeTicks::Now();
+ scoped_refptr<media::VideoFrame> frame;
+ RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback callback;
+ if (frame_subscriber()->ShouldCaptureFrame(present_time,
+ &frame, &callback)) {
+ CopyFromCompositingSurfaceToVideoFrame(
+ gfx::Rect(current_frame_size_in_dip_),
+ frame,
+ base::Bind(callback, present_time));
+ }
+ }
+}
+
+void DelegatedFrameHost::SwapDelegatedFrame(
+ uint32 output_surface_id,
+ scoped_ptr<cc::DelegatedFrameData> frame_data,
+ float frame_device_scale_factor,
+ const std::vector<ui::LatencyInfo>& latency_info) {
+ RenderWidgetHostImpl* host = client_->GetHost();
+ DCHECK(!frame_data->render_pass_list.empty());
+
+ cc::RenderPass* root_pass = frame_data->render_pass_list.back();
+
+ gfx::Size frame_size = root_pass->output_rect.size();
+ gfx::Size frame_size_in_dip =
+ ConvertSizeToDIP(frame_device_scale_factor, frame_size);
+
+ gfx::Rect damage_rect = gfx::ToEnclosingRect(root_pass->damage_rect);
+ damage_rect.Intersect(gfx::Rect(frame_size));
+ gfx::Rect damage_rect_in_dip =
+ 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(),
+ latency_info.begin(), latency_info.end());
+
+ RenderWidgetHostImpl::SendSwapCompositorFrameAck(
+ host->GetRoutingID(), output_surface_id,
+ host->GetProcess()->GetID(), 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();
+ 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();
+
+ // 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;
+ }
+ if (frame_size.IsEmpty()) {
+ DCHECK(frame_data->resource_list.empty());
+ EvictDelegatedFrame();
+ } else {
+ if (!resource_collection_) {
+ 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(), frame_data.Pass());
+ client_->GetLayer()->SetShowDelegatedContent(frame_provider_.get(),
+ frame_size_in_dip);
+ } else {
+ frame_provider_->SetFrameData(frame_data.Pass());
+ }
+ }
+ released_front_lock_ = NULL;
+ current_frame_size_in_dip_ = frame_size_in_dip;
+ CheckResizeLock();
+
+ client_->SchedulePaintInRect(damage_rect_in_dip);
+
+ pending_delegated_ack_count_++;
+
+ ui::Compositor* compositor = client_->GetCompositor();
+ if (!compositor) {
+ SendDelegatedFrameAck(output_surface_id);
+ } else {
+ std::vector<ui::LatencyInfo>::const_iterator it;
+ for (it = latency_info.begin(); it != 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));
+ }
+ DidReceiveFrameFromRenderer();
+ if (frame_provider_.get())
+ delegated_frame_evictor_->SwappedFrame(!host->is_hidden());
+ // Note: the frame may have been evicted immediately.
+}
+
+void DelegatedFrameHost::SendDelegatedFrameAck(uint32 output_surface_id) {
+ RenderWidgetHostImpl* host = client_->GetHost();
+ cc::CompositorFrameAck ack;
+ if (resource_collection_)
+ resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources);
+ RenderWidgetHostImpl::SendSwapCompositorFrameAck(host->GetRoutingID(),
+ output_surface_id,
+ host->GetProcess()->GetID(),
+ ack);
+ DCHECK_GT(pending_delegated_ack_count_, 0);
+ pending_delegated_ack_count_--;
+}
+
+void DelegatedFrameHost::UnusedResourcesAreAvailable() {
+ if (pending_delegated_ack_count_)
+ return;
+
+ SendReturnedDelegatedResources(last_output_surface_id_);
+}
+
+void DelegatedFrameHost::SendReturnedDelegatedResources(
+ uint32 output_surface_id) {
+ RenderWidgetHostImpl* host = client_->GetHost();
+ DCHECK(resource_collection_);
+
+ cc::CompositorFrameAck ack;
+ resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources);
+ DCHECK(!ack.resources.empty());
+
+ RenderWidgetHostImpl::SendReclaimCompositorResources(
+ host->GetRoutingID(),
+ output_surface_id,
+ host->GetProcess()->GetID(),
+ ack);
+}
+
+void DelegatedFrameHost::EvictDelegatedFrame() {
+ client_->GetLayer()->SetShowPaintedContent();
+ frame_provider_ = NULL;
+ delegated_frame_evictor_->DiscardedFrame();
+}
+
+// static
+void DelegatedFrameHost::CopyFromCompositingSurfaceHasResult(
+ const gfx::Size& dst_size_in_pixel,
+ const SkBitmap::Config config,
+ const base::Callback<void(bool, const SkBitmap&)>& callback,
+ scoped_ptr<cc::CopyOutputResult> result) {
+ if (result->IsEmpty() || result->size().IsEmpty()) {
+ callback.Run(false, SkBitmap());
+ return;
+ }
+
+ if (result->HasTexture()) {
+ PrepareTextureCopyOutputResult(dst_size_in_pixel, config,
+ callback,
+ result.Pass());
+ return;
+ }
+
+ DCHECK(result->HasBitmap());
+ PrepareBitmapCopyOutputResult(dst_size_in_pixel, config, callback,
+ result.Pass());
+}
+
+static void CopyFromCompositingSurfaceFinished(
+ const base::Callback<void(bool, const SkBitmap&)>& callback,
+ scoped_ptr<cc::SingleReleaseCallback> release_callback,
+ scoped_ptr<SkBitmap> bitmap,
+ scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock,
+ bool result) {
+ bitmap_pixels_lock.reset();
+
+ uint32 sync_point = 0;
+ if (result) {
+ GLHelper* gl_helper = ImageTransportFactory::GetInstance()->GetGLHelper();
+ sync_point = gl_helper->InsertSyncPoint();
+ }
+ bool lost_resource = sync_point == 0;
+ release_callback->Run(sync_point, lost_resource);
+
+ callback.Run(result, *bitmap);
+}
+
+// static
+void DelegatedFrameHost::PrepareTextureCopyOutputResult(
+ const gfx::Size& dst_size_in_pixel,
+ const SkBitmap::Config config,
+ const base::Callback<void(bool, const SkBitmap&)>& callback,
+ scoped_ptr<cc::CopyOutputResult> result) {
+ DCHECK(result->HasTexture());
+ base::ScopedClosureRunner scoped_callback_runner(
+ base::Bind(callback, false, SkBitmap()));
+
+ scoped_ptr<SkBitmap> bitmap(new SkBitmap);
+ bitmap->setConfig(config,
+ dst_size_in_pixel.width(), dst_size_in_pixel.height(),
+ 0, kOpaque_SkAlphaType);
+ if (!bitmap->allocPixels())
+ return;
+
+ ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
+ GLHelper* gl_helper = factory->GetGLHelper();
+ if (!gl_helper)
+ return;
+
+ scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock(
+ new SkAutoLockPixels(*bitmap));
+ uint8* pixels = static_cast<uint8*>(bitmap->getPixels());
+
+ cc::TextureMailbox texture_mailbox;
+ scoped_ptr<cc::SingleReleaseCallback> 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_point(),
+ result->size(),
+ gfx::Rect(result->size()),
+ dst_size_in_pixel,
+ pixels,
+ config,
+ base::Bind(&CopyFromCompositingSurfaceFinished,
+ callback,
+ base::Passed(&release_callback),
+ base::Passed(&bitmap),
+ base::Passed(&bitmap_pixels_lock)),
+ GLHelper::SCALER_QUALITY_FAST);
+}
+
+// static
+void DelegatedFrameHost::PrepareBitmapCopyOutputResult(
+ const gfx::Size& dst_size_in_pixel,
+ const SkBitmap::Config config,
+ const base::Callback<void(bool, const SkBitmap&)>& callback,
+ scoped_ptr<cc::CopyOutputResult> result) {
+ if (config != SkBitmap::kARGB_8888_Config) {
+ NOTIMPLEMENTED();
+ callback.Run(false, SkBitmap());
+ return;
+ }
+ DCHECK(result->HasBitmap());
+ scoped_ptr<SkBitmap> source = result->TakeBitmap();
+ DCHECK(source);
+ SkBitmap bitmap = skia::ImageOperations::Resize(
+ *source,
+ skia::ImageOperations::RESIZE_BEST,
+ dst_size_in_pixel.width(),
+ dst_size_in_pixel.height());
+ callback.Run(true, bitmap);
+}
+
+// static
+void DelegatedFrameHost::ReturnSubscriberTexture(
+ base::WeakPtr<DelegatedFrameHost> dfh,
+ scoped_refptr<OwnedMailbox> subscriber_texture,
+ uint32 sync_point) {
+ if (!subscriber_texture.get())
+ return;
+ if (!dfh)
+ return;
+
+ subscriber_texture->UpdateSyncPoint(sync_point);
+
+ if (dfh->frame_subscriber_ && subscriber_texture->texture_id())
+ dfh->idle_frame_subscriber_textures_.push_back(subscriber_texture);
+}
+
+void DelegatedFrameHost::CopyFromCompositingSurfaceFinishedForVideo(
+ base::WeakPtr<DelegatedFrameHost> dfh,
+ const base::Callback<void(bool)>& callback,
+ scoped_refptr<OwnedMailbox> subscriber_texture,
+ scoped_ptr<cc::SingleReleaseCallback> release_callback,
+ bool result) {
+ callback.Run(result);
+
+ uint32 sync_point = 0;
+ if (result) {
+ GLHelper* gl_helper = ImageTransportFactory::GetInstance()->GetGLHelper();
+ sync_point = gl_helper->InsertSyncPoint();
+ }
+ if (release_callback) {
+ // A release callback means the texture came from the compositor, so there
+ // should be no |subscriber_texture|.
+ DCHECK(!subscriber_texture);
+ bool lost_resource = sync_point == 0;
+ release_callback->Run(sync_point, lost_resource);
+ }
+ ReturnSubscriberTexture(dfh, subscriber_texture, sync_point);
+}
+
+// static
+void DelegatedFrameHost::CopyFromCompositingSurfaceHasResultForVideo(
+ base::WeakPtr<DelegatedFrameHost> dfh,
+ scoped_refptr<OwnedMailbox> subscriber_texture,
+ scoped_refptr<media::VideoFrame> video_frame,
+ const base::Callback<void(bool)>& callback,
+ scoped_ptr<cc::CopyOutputResult> result) {
+ base::ScopedClosureRunner scoped_callback_runner(base::Bind(callback, false));
+ base::ScopedClosureRunner scoped_return_subscriber_texture(
+ base::Bind(&ReturnSubscriberTexture, dfh, subscriber_texture, 0));
+
+ 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 coded_size() and the result's size() are both physical
+ // pixels.
+ gfx::Rect region_in_frame =
+ media::ComputeLetterboxRegion(gfx::Rect(video_frame->coded_size()),
+ 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<SkBitmap> bitmap = result->TakeBitmap();
+ // Scale the bitmap to the required size, if necessary.
+ SkBitmap scaled_bitmap;
+ if (result->size().width() != region_in_frame.width() ||
+ result->size().height() != region_in_frame.height()) {
+ 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<uint8*>(scaled_bitmap.getPixels()),
+ scaled_bitmap.rowBytes(),
+ region_in_frame,
+ video_frame.get());
+ }
+ ignore_result(scoped_callback_runner.Release());
+ callback.Run(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<cc::SingleReleaseCallback> 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 =
+ 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,
+ video_frame->coded_size(),
+ region_in_frame,
+ 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<void(bool result)> finished_callback = base::Bind(
+ &DelegatedFrameHost::CopyFromCompositingSurfaceFinishedForVideo,
+ dfh->AsWeakPtr(),
+ callback,
+ subscriber_texture,
+ base::Passed(&release_callback));
+ yuv_readback_pipeline->ReadbackYUV(texture_mailbox.mailbox(),
+ texture_mailbox.sync_point(),
+ video_frame.get(),
+ finished_callback);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// DelegatedFrameHost, ui::CompositorObserver implementation:
+
+void DelegatedFrameHost::OnCompositingDidCommit(
+ ui::Compositor* compositor) {
+ RenderWidgetHostImpl* host = client_->GetHost();
+ 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();
+ host->WasResized();
+ // 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::OnUpdateVSyncParameters(
+ base::TimeTicks timebase,
+ base::TimeDelta interval) {
+ RenderWidgetHostImpl* host = client_->GetHost();
+ if (client_->IsVisible())
+ host->UpdateVSyncParameters(timebase, interval);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// RenderWidgetHostViewAura, ImageTransportFactoryObserver implementation:
+
+void DelegatedFrameHost::OnLostResources() {
+ RenderWidgetHostImpl* host = client_->GetHost();
+ if (frame_provider_.get())
+ EvictDelegatedFrame();
+ idle_frame_subscriber_textures_.clear();
+ yuv_readback_pipeline_.reset();
+
+ host->ScheduleComposite();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// DelegatedFrameHost, private:
+
+DelegatedFrameHost::~DelegatedFrameHost() {
+ ImageTransportFactory::GetInstance()->RemoveObserver(this);
+
+ if (resource_collection_.get())
+ resource_collection_->SetClient(NULL);
+
+ DCHECK(!vsync_manager_);
+}
+
+void DelegatedFrameHost::RunOnCommitCallbacks() {
+ for (std::vector<base::Closure>::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) {
+ ui::Compositor* compositor = client_->GetCompositor();
+ DCHECK(compositor);
+
+ if (!compositor->HasObserver(this))
+ compositor->AddObserver(this);
+
+ can_lock_compositor_ = NO_PENDING_COMMIT;
+ on_compositing_did_commit_callbacks_.push_back(callback);
+}
+
+void DelegatedFrameHost::AddedToWindow() {
+ ui::Compositor* compositor = client_->GetCompositor();
+ if (compositor) {
+ DCHECK(!vsync_manager_);
+ vsync_manager_ = compositor->vsync_manager();
+ vsync_manager_->AddObserver(this);
+ }
+}
+
+void DelegatedFrameHost::RemovingFromWindow() {
+ RunOnCommitCallbacks();
+ resize_lock_.reset();
+ client_->GetHost()->WasResized();
+ ui::Compositor* compositor = client_->GetCompositor();
+ if (compositor && compositor->HasObserver(this))
+ compositor->RemoveObserver(this);
+
+ if (vsync_manager_) {
+ vsync_manager_->RemoveObserver(this);
+ vsync_manager_ = NULL;
+ }
+}
+
+void DelegatedFrameHost::LockResources() {
+ DCHECK(frame_provider_);
+ delegated_frame_evictor_->LockFrame();
+}
+
+void DelegatedFrameHost::UnlockResources() {
+ DCHECK(frame_provider_);
+ 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_);
+ }
+}
+
+} // namespace content
+
diff --git a/chromium/content/browser/compositor/delegated_frame_host.h b/chromium/content/browser/compositor/delegated_frame_host.h
new file mode 100644
index 00000000000..bcbc758ffad
--- /dev/null
+++ b/chromium/content/browser/compositor/delegated_frame_host.h
@@ -0,0 +1,285 @@
+// 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 "cc/layers/delegated_frame_provider.h"
+#include "cc/layers/delegated_frame_resource_collection.h"
+#include "cc/output/copy_output_result.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/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/gfx/rect_conversions.h"
+
+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::Compositor* GetCompositor() const = 0;
+ virtual ui::Layer* GetLayer() = 0;
+ virtual RenderWidgetHostImpl* GetHost() = 0;
+ virtual void SchedulePaintInRect(const gfx::Rect& damage_rect_in_dip) = 0;
+ virtual bool IsVisible() = 0;
+ virtual scoped_ptr<ResizeLock> CreateResizeLock(
+ bool defer_compositor_lock) = 0;
+ virtual gfx::Size DesiredFrameSize() = 0;
+
+ // TODO(ccameron): It is likely that at least one of these two functions is
+ // redundant. Find which one, and delete it.
+ virtual float CurrentDeviceScaleFactor() = 0;
+ virtual gfx::Size ConvertViewSizeToPixel(const gfx::Size& size) = 0;
+
+ // These are to be overridden for testing only.
+ // TODO(ccameron): This is convoluted. Make the tests that need to override
+ // these functions test DelegatedFrameHost directly (rather than do it
+ // through RenderWidgetHostViewAura).
+ virtual DelegatedFrameHost* GetDelegatedFrameHost() const = 0;
+ virtual bool ShouldCreateResizeLock();
+ virtual void RequestCopyOfOutput(scoped_ptr<cc::CopyOutputRequest> request);
+};
+
+// 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 base::SupportsWeakPtr<DelegatedFrameHost> {
+ public:
+ DelegatedFrameHost(DelegatedFrameHostClient* client);
+ virtual ~DelegatedFrameHost();
+
+ bool CanCopyToBitmap() const;
+
+ // Public interface exposed to RenderWidgetHostView.
+ void SwapDelegatedFrame(
+ uint32 output_surface_id,
+ scoped_ptr<cc::DelegatedFrameData> frame_data,
+ float frame_device_scale_factor,
+ const std::vector<ui::LatencyInfo>& latency_info);
+ void WasHidden();
+ void WasShown();
+ void WasResized();
+ gfx::Size GetRequestedRendererSize() const;
+ void AddedToWindow();
+ void RemovingFromWindow();
+ void CopyFromCompositingSurface(
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ const base::Callback<void(bool, const SkBitmap&)>& callback,
+ const SkBitmap::Config config);
+ void CopyFromCompositingSurfaceToVideoFrame(
+ const gfx::Rect& src_subrect,
+ const scoped_refptr<media::VideoFrame>& target,
+ const base::Callback<void(bool)>& callback);
+ bool CanCopyToVideoFrame() const;
+ bool CanSubscribeFrame() const;
+ void BeginFrameSubscription(
+ scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber);
+ void EndFrameSubscription();
+
+ // Exposed for tests.
+ cc::DelegatedFrameProvider* FrameProviderForTesting() const {
+ return frame_provider_.get();
+ }
+ void OnCompositingDidCommitForTesting(ui::Compositor* compositor) {
+ OnCompositingDidCommit(compositor);
+ }
+ bool ShouldCreateResizeLockForTesting() { return ShouldCreateResizeLock(); }
+
+ private:
+ friend class DelegatedFrameHostClient;
+ FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest,
+ SkippedDelegatedFrames);
+ FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest,
+ DiscardDelegatedFramesWithLocking);
+ FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraCopyRequestTest,
+ DestroyedAfterCopyRequest);
+
+ RenderWidgetHostViewFrameSubscriber* frame_subscriber() const {
+ return frame_subscriber_.get();
+ }
+ bool ShouldCreateResizeLock();
+ void RequestCopyOfOutput(scoped_ptr<cc::CopyOutputRequest> request);
+
+ void LockResources();
+ void UnlockResources();
+
+ // Overridden from ui::CompositorObserver:
+ virtual void OnCompositingDidCommit(ui::Compositor* compositor) OVERRIDE;
+ virtual void OnCompositingStarted(ui::Compositor* compositor,
+ base::TimeTicks start_time) OVERRIDE;
+ virtual void OnCompositingEnded(ui::Compositor* compositor) OVERRIDE;
+ virtual void OnCompositingAborted(ui::Compositor* compositor) OVERRIDE;
+ virtual void OnCompositingLockStateChanged(
+ ui::Compositor* compositor) OVERRIDE;
+
+ // Overridden from ui::CompositorVSyncManager::Observer:
+ virtual void OnUpdateVSyncParameters(base::TimeTicks timebase,
+ base::TimeDelta interval) OVERRIDE;
+
+ // Overridden from ui::LayerOwnerObserver:
+ virtual void OnLayerRecreated(ui::Layer* old_layer,
+ ui::Layer* new_layer) OVERRIDE;
+
+ // Overridden from ImageTransportFactoryObserver:
+ virtual void OnLostResources() OVERRIDE;
+
+ 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 SkBitmap::Config config,
+ const base::Callback<void(bool, const SkBitmap&)>& callback,
+ scoped_ptr<cc::CopyOutputResult> result);
+ static void PrepareTextureCopyOutputResult(
+ const gfx::Size& dst_size_in_pixel,
+ const SkBitmap::Config config,
+ const base::Callback<void(bool, const SkBitmap&)>& callback,
+ scoped_ptr<cc::CopyOutputResult> result);
+ static void PrepareBitmapCopyOutputResult(
+ const gfx::Size& dst_size_in_pixel,
+ const SkBitmap::Config config,
+ const base::Callback<void(bool, const SkBitmap&)>& callback,
+ scoped_ptr<cc::CopyOutputResult> result);
+ static void CopyFromCompositingSurfaceHasResultForVideo(
+ base::WeakPtr<DelegatedFrameHost> rwhva,
+ scoped_refptr<OwnedMailbox> subscriber_texture,
+ scoped_refptr<media::VideoFrame> video_frame,
+ const base::Callback<void(bool)>& callback,
+ scoped_ptr<cc::CopyOutputResult> result);
+ static void CopyFromCompositingSurfaceFinishedForVideo(
+ base::WeakPtr<DelegatedFrameHost> rwhva,
+ const base::Callback<void(bool)>& callback,
+ scoped_refptr<OwnedMailbox> subscriber_texture,
+ scoped_ptr<cc::SingleReleaseCallback> release_callback,
+ bool result);
+ static void ReturnSubscriberTexture(
+ base::WeakPtr<DelegatedFrameHost> rwhva,
+ scoped_refptr<OwnedMailbox> subscriber_texture,
+ uint32 sync_point);
+
+ void SendDelegatedFrameAck(uint32 output_surface_id);
+ void SendReturnedDelegatedResources(uint32 output_surface_id);
+
+ // DelegatedFrameEvictorClient implementation.
+ virtual void EvictDelegatedFrame() OVERRIDE;
+
+ // cc::DelegatedFrameProviderClient implementation.
+ virtual void UnusedResourcesAreAvailable() OVERRIDE;
+
+ void DidReceiveFrameFromRenderer();
+
+ DelegatedFrameHostClient* client_;
+
+ std::vector<base::Closure> on_compositing_did_commit_callbacks_;
+
+ // The vsync manager we are observing for changes, if any.
+ scoped_refptr<ui::CompositorVSyncManager> vsync_manager_;
+
+ // With delegated renderer, this is the last output surface, used to
+ // disambiguate resources with the same id coming from different output
+ // surfaces.
+ uint32 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<ui::LatencyInfo> 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<cc::DelegatedFrameResourceCollection> resource_collection_;
+
+ // Provides delegated frame updates to the cc::DelegatedRendererLayer.
+ scoped_refptr<cc::DelegatedFrameProvider> frame_provider_;
+
+ // 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<ResizeLock> 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<ui::CompositorLock> 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<RenderWidgetHostViewFrameSubscriber> frame_subscriber_;
+ std::vector<scoped_refptr<OwnedMailbox> > idle_frame_subscriber_textures_;
+
+ // YUV readback pipeline.
+ scoped_ptr<content::ReadbackYUVInterface>
+ yuv_readback_pipeline_;
+
+ scoped_ptr<DelegatedFrameEvictor> delegated_frame_evictor_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_COMPOSITOR_DELEGATED_FRAME_HOST_H_
diff --git a/chromium/content/browser/aura/gpu_browser_compositor_output_surface.cc b/chromium/content/browser/compositor/gpu_browser_compositor_output_surface.cc
index 8e3c30ca529..3b79d546d42 100644
--- a/chromium/content/browser/aura/gpu_browser_compositor_output_surface.cc
+++ b/chromium/content/browser/compositor/gpu_browser_compositor_output_surface.cc
@@ -1,12 +1,13 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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/aura/gpu_browser_compositor_output_surface.h"
+#include "content/browser/compositor/gpu_browser_compositor_output_surface.h"
#include "cc/output/compositor_frame.h"
-#include "content/browser/aura/reflector_impl.h"
-#include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h"
+#include "content/browser/compositor/reflector_impl.h"
+#include "content/common/gpu/client/context_provider_command_buffer.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
namespace content {
@@ -14,13 +15,14 @@ GpuBrowserCompositorOutputSurface::GpuBrowserCompositorOutputSurface(
const scoped_refptr<ContextProviderCommandBuffer>& context,
int surface_id,
IDMap<BrowserCompositorOutputSurface>* output_surface_map,
- base::MessageLoopProxy* compositor_message_loop,
- base::WeakPtr<ui::Compositor> compositor)
+ const scoped_refptr<ui::CompositorVSyncManager>& vsync_manager,
+ scoped_ptr<cc::OverlayCandidateValidator> overlay_candidate_validator)
: BrowserCompositorOutputSurface(context,
surface_id,
output_surface_map,
- compositor_message_loop,
- compositor) {}
+ vsync_manager) {
+ overlay_candidate_validator_ = overlay_candidate_validator.Pass();
+}
GpuBrowserCompositorOutputSurface::~GpuBrowserCompositorOutputSurface() {}
@@ -28,13 +30,12 @@ void GpuBrowserCompositorOutputSurface::SwapBuffers(
cc::CompositorFrame* frame) {
DCHECK(frame->gl_frame_data);
- WebGraphicsContext3DCommandBufferImpl* command_buffer_context =
- static_cast<WebGraphicsContext3DCommandBufferImpl*>(
- context_provider_->Context3d());
+ ContextProviderCommandBuffer* provider_command_buffer =
+ static_cast<ContextProviderCommandBuffer*>(context_provider_.get());
CommandBufferProxyImpl* command_buffer_proxy =
- command_buffer_context->GetCommandBufferProxy();
+ provider_command_buffer->GetCommandBufferProxy();
DCHECK(command_buffer_proxy);
- context_provider_->Context3d()->shallowFlushCHROMIUM();
+ context_provider_->ContextGL()->ShallowFlushCHROMIUM();
command_buffer_proxy->SetLatencyInfo(frame->metadata.latency_info);
if (reflector_.get()) {
diff --git a/chromium/content/browser/aura/gpu_browser_compositor_output_surface.h b/chromium/content/browser/compositor/gpu_browser_compositor_output_surface.h
index 76b9724d4a6..dd1ac3f693d 100644
--- a/chromium/content/browser/aura/gpu_browser_compositor_output_surface.h
+++ b/chromium/content/browser/compositor/gpu_browser_compositor_output_surface.h
@@ -1,11 +1,19 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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_AURA_GPU_BROWSER_COMPOSITOR_OUTPUT_SURFACE_H_
-#define CONTENT_BROWSER_AURA_GPU_BROWSER_COMPOSITOR_OUTPUT_SURFACE_H_
+#ifndef CONTENT_BROWSER_COMPOSITOR_GPU_BROWSER_COMPOSITOR_OUTPUT_SURFACE_H_
+#define CONTENT_BROWSER_COMPOSITOR_GPU_BROWSER_COMPOSITOR_OUTPUT_SURFACE_H_
-#include "content/browser/aura/browser_compositor_output_surface.h"
+#include "content/browser/compositor/browser_compositor_output_surface.h"
+
+namespace ui {
+class CompositorVSyncManager;
+}
+
+namespace cc {
+class OverlayCandidateValidator;
+}
namespace content {
@@ -19,8 +27,8 @@ class GpuBrowserCompositorOutputSurface
const scoped_refptr<ContextProviderCommandBuffer>& context,
int surface_id,
IDMap<BrowserCompositorOutputSurface>* output_surface_map,
- base::MessageLoopProxy* compositor_message_loop,
- base::WeakPtr<ui::Compositor> compositor);
+ const scoped_refptr<ui::CompositorVSyncManager>& vsync_manager,
+ scoped_ptr<cc::OverlayCandidateValidator> overlay_candidate_validator);
virtual ~GpuBrowserCompositorOutputSurface();
@@ -33,4 +41,4 @@ class GpuBrowserCompositorOutputSurface
} // namespace content
-#endif // CONTENT_BROWSER_AURA_GPU_BROWSER_COMPOSITOR_OUTPUT_SURFACE_H_
+#endif // CONTENT_BROWSER_COMPOSITOR_GPU_BROWSER_COMPOSITOR_OUTPUT_SURFACE_H_
diff --git a/chromium/content/browser/aura/gpu_process_transport_factory.cc b/chromium/content/browser/compositor/gpu_process_transport_factory.cc
index 47efce7555f..bdf7cb4904a 100644
--- a/chromium/content/browser/aura/gpu_process_transport_factory.cc
+++ b/chromium/content/browser/compositor/gpu_process_transport_factory.cc
@@ -1,8 +1,8 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// 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/aura/gpu_process_transport_factory.h"
+#include "content/browser/compositor/gpu_process_transport_factory.h"
#include <string>
@@ -11,13 +11,17 @@
#include "base/location.h"
#include "base/message_loop/message_loop.h"
#include "base/metrics/histogram.h"
+#include "base/threading/thread.h"
#include "cc/output/compositor_frame.h"
#include "cc/output/output_surface.h"
-#include "content/browser/aura/browser_compositor_output_surface.h"
-#include "content/browser/aura/browser_compositor_output_surface_proxy.h"
-#include "content/browser/aura/gpu_browser_compositor_output_surface.h"
-#include "content/browser/aura/reflector_impl.h"
-#include "content/browser/aura/software_browser_compositor_output_surface.h"
+#include "cc/surfaces/surface_manager.h"
+#include "content/browser/compositor/browser_compositor_output_surface.h"
+#include "content/browser/compositor/browser_compositor_output_surface_proxy.h"
+#include "content/browser/compositor/gpu_browser_compositor_output_surface.h"
+#include "content/browser/compositor/onscreen_display_client.h"
+#include "content/browser/compositor/reflector_impl.h"
+#include "content/browser/compositor/software_browser_compositor_output_surface.h"
+#include "content/browser/compositor/surface_display_output_surface.h"
#include "content/browser/gpu/browser_gpu_channel_host_factory.h"
#include "content/browser/gpu/gpu_data_manager_impl.h"
#include "content/browser/gpu/gpu_surface_tracker.h"
@@ -27,8 +31,11 @@
#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/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 "third_party/khronos/GLES2/gl2.h"
#include "ui/compositor/compositor.h"
#include "ui/compositor/compositor_constants.h"
@@ -37,12 +44,15 @@
#include "ui/gfx/size.h"
#if defined(OS_WIN)
-#include "content/browser/aura/software_output_device_win.h"
-#include "ui/surface/accelerated_surface_win.h"
+#include "content/browser/compositor/software_output_device_win.h"
#elif defined(USE_OZONE)
-#include "content/browser/aura/software_output_device_ozone.h"
+#include "content/browser/compositor/overlay_candidate_validator_ozone.h"
+#include "content/browser/compositor/software_output_device_ozone.h"
+#include "ui/ozone/public/surface_factory_ozone.h"
#elif defined(USE_X11)
-#include "content/browser/aura/software_output_device_x11.h"
+#include "content/browser/compositor/software_output_device_x11.h"
+#elif defined(OS_MACOSX)
+#include "content/browser/compositor/software_output_device_mac.h"
#endif
using cc::ContextProvider;
@@ -52,101 +62,28 @@ namespace content {
struct GpuProcessTransportFactory::PerCompositorData {
int surface_id;
-#if defined(OS_WIN)
- scoped_ptr<AcceleratedSurface> accelerated_surface;
-#endif
scoped_refptr<ReflectorImpl> reflector;
-};
-
-class OwnedTexture : public ui::Texture, ImageTransportFactoryObserver {
- public:
- OwnedTexture(const scoped_refptr<ContextProvider>& provider,
- const gfx::Size& size,
- float device_scale_factor,
- GLuint texture_id)
- : ui::Texture(true, size, device_scale_factor),
- provider_(provider),
- texture_id_(texture_id) {
- ImageTransportFactory::GetInstance()->AddObserver(this);
- }
-
- // ui::Texture overrides:
- virtual unsigned int PrepareTexture() OVERRIDE {
- // It's possible that we may have lost the context owning our
- // texture but not yet fired the OnLostResources callback, so poll to see if
- // it's still valid.
- if (provider_ && provider_->IsContextLost())
- texture_id_ = 0u;
- return texture_id_;
- }
-
- // ImageTransportFactory overrides:
- virtual void OnLostResources() OVERRIDE {
- DeleteTexture();
- provider_ = NULL;
- }
-
- protected:
- virtual ~OwnedTexture() {
- ImageTransportFactory::GetInstance()->RemoveObserver(this);
- DeleteTexture();
- }
-
- protected:
- void DeleteTexture() {
- if (texture_id_) {
- provider_->ContextGL()->DeleteTextures(1, &texture_id_);
- texture_id_ = 0;
- }
- }
-
- scoped_refptr<cc::ContextProvider> provider_;
- GLuint texture_id_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(OwnedTexture);
-};
-
-class ImageTransportClientTexture : public OwnedTexture {
- public:
- ImageTransportClientTexture(const scoped_refptr<ContextProvider>& provider,
- float device_scale_factor,
- GLuint texture_id)
- : OwnedTexture(provider,
- gfx::Size(0, 0),
- device_scale_factor,
- texture_id) {}
-
- virtual void Consume(const std::string& mailbox_name,
- const gfx::Size& new_size) OVERRIDE {
- DCHECK(mailbox_name.size() == GL_MAILBOX_SIZE_CHROMIUM);
- mailbox_name_ = mailbox_name;
- if (mailbox_name.empty())
- return;
-
- DCHECK(provider_ && texture_id_);
- GLES2Interface* gl = provider_->ContextGL();
- gl->BindTexture(GL_TEXTURE_2D, texture_id_);
- gl->ConsumeTextureCHROMIUM(
- GL_TEXTURE_2D, reinterpret_cast<const GLbyte*>(mailbox_name.c_str()));
- size_ = new_size;
- gl->ShallowFlushCHROMIUM();
- }
-
- virtual std::string Produce() OVERRIDE { return mailbox_name_; }
-
- protected:
- virtual ~ImageTransportClientTexture() {}
-
- private:
- std::string mailbox_name_;
- DISALLOW_COPY_AND_ASSIGN(ImageTransportClientTexture);
+ scoped_ptr<OnscreenDisplayClient> display_client;
};
GpuProcessTransportFactory::GpuProcessTransportFactory()
: callback_factory_(this) {
output_surface_proxy_ = new BrowserCompositorOutputSurfaceProxy(
&output_surface_map_);
+#if defined(OS_CHROMEOS)
+ bool use_thread = !CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kUIDisableThreadedCompositing);
+#else
+ bool use_thread = false;
+#endif
+ if (use_thread) {
+ compositor_thread_.reset(new base::Thread("Browser Compositor"));
+ compositor_thread_->Start();
+ }
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kUseSurfaces)) {
+ surface_manager_ = make_scoped_ptr(new cc::SurfaceManager);
+ }
}
GpuProcessTransportFactory::~GpuProcessTransportFactory() {
@@ -172,10 +109,27 @@ scoped_ptr<cc::SoftwareOutputDevice> CreateSoftwareOutputDevice(
#elif defined(USE_X11)
return scoped_ptr<cc::SoftwareOutputDevice>(new SoftwareOutputDeviceX11(
compositor));
-#endif
-
+#elif defined(OS_MACOSX)
+ return scoped_ptr<cc::SoftwareOutputDevice>(
+ new SoftwareOutputDeviceMac(compositor));
+#else
NOTREACHED();
return scoped_ptr<cc::SoftwareOutputDevice>();
+#endif
+}
+
+scoped_ptr<cc::OverlayCandidateValidator> CreateOverlayCandidateValidator(
+ gfx::AcceleratedWidget widget) {
+#if defined(USE_OZONE)
+ ui::OverlayCandidatesOzone* overlay_candidates =
+ ui::SurfaceFactoryOzone::GetInstance()->GetOverlayCandidates(widget);
+ if (overlay_candidates && CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableHardwareOverlays)) {
+ return scoped_ptr<cc::OverlayCandidateValidator>(
+ new OverlayCandidateValidatorOzone(widget, overlay_candidates));
+ }
+#endif
+ return scoped_ptr<cc::OverlayCandidateValidator>();
}
scoped_ptr<cc::OutputSurface> GpuProcessTransportFactory::CreateOutputSurface(
@@ -184,24 +138,20 @@ scoped_ptr<cc::OutputSurface> GpuProcessTransportFactory::CreateOutputSurface(
if (!data)
data = CreatePerCompositorData(compositor);
- bool force_software_renderer = false;
-#if defined(OS_WIN)
+ bool create_software_renderer = software_fallback;
+#if defined(OS_CHROMEOS)
+ // Software fallback does not happen on Chrome OS.
+ create_software_renderer = false;
+#elif defined(OS_WIN)
if (::GetProp(compositor->widget(), kForceSoftwareCompositor)) {
- force_software_renderer = reinterpret_cast<bool>(
- ::RemoveProp(compositor->widget(), kForceSoftwareCompositor));
+ if (::RemoveProp(compositor->widget(), kForceSoftwareCompositor))
+ create_software_renderer = true;
}
#endif
scoped_refptr<ContextProviderCommandBuffer> context_provider;
- // Software fallback does not happen on Chrome OS.
-#if defined(OS_CHROMEOS)
- software_fallback = false;
-#endif
-
- CommandLine* command_line = CommandLine::ForCurrentProcess();
- if (!command_line->HasSwitch(switches::kUIEnableSoftwareCompositing) &&
- !force_software_renderer && !software_fallback) {
+ if (!create_software_renderer) {
context_provider = ContextProviderCommandBuffer::Create(
GpuProcessTransportFactory::CreateContextCommon(data->surface_id),
"Compositor");
@@ -209,8 +159,43 @@ scoped_ptr<cc::OutputSurface> GpuProcessTransportFactory::CreateOutputSurface(
UMA_HISTOGRAM_BOOLEAN("Aura.CreatedGpuBrowserCompositor", !!context_provider);
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kUseSurfaces)) {
+ // This gets a bit confusing. Here we have a ContextProvider configured to
+ // render directly to this widget. We need to make an OnscreenDisplayClient
+ // associated with this context, then return a SurfaceDisplayOutputSurface
+ // set up to draw to the display's surface.
+ cc::SurfaceManager* manager = surface_manager_.get();
+ scoped_ptr<cc::OutputSurface> software_surface;
+ if (!context_provider) {
+ software_surface =
+ make_scoped_ptr(new SoftwareBrowserCompositorOutputSurface(
+ output_surface_proxy_,
+ CreateSoftwareOutputDevice(compositor),
+ per_compositor_data_[compositor]->surface_id,
+ &output_surface_map_,
+ compositor->vsync_manager()));
+ }
+ scoped_ptr<OnscreenDisplayClient> display_client(new OnscreenDisplayClient(
+ context_provider, software_surface.Pass(), manager));
+ // TODO(jamesr): Need to set up filtering for the
+ // GpuHostMsg_UpdateVSyncParameters message.
+
+ scoped_refptr<cc::ContextProvider> offscreen_context_provider;
+ if (context_provider) {
+ offscreen_context_provider = ContextProviderCommandBuffer::Create(
+ GpuProcessTransportFactory::CreateOffscreenCommandBufferContext(),
+ "Offscreen-Compositor");
+ }
+ scoped_ptr<SurfaceDisplayOutputSurface> output_surface(
+ new SurfaceDisplayOutputSurface(
+ display_client->display(), manager, offscreen_context_provider));
+ data->display_client = display_client.Pass();
+ return output_surface.PassAs<cc::OutputSurface>();
+ }
+
if (!context_provider.get()) {
- if (ui::Compositor::WasInitializedWithThread()) {
+ if (compositor_thread_.get()) {
LOG(FATAL) << "Failed to create UI context, but can't use software"
" compositing with browser threaded compositing. Aborting.";
}
@@ -221,13 +206,12 @@ scoped_ptr<cc::OutputSurface> GpuProcessTransportFactory::CreateOutputSurface(
CreateSoftwareOutputDevice(compositor),
per_compositor_data_[compositor]->surface_id,
&output_surface_map_,
- base::MessageLoopProxy::current().get(),
- compositor->AsWeakPtr()));
+ compositor->vsync_manager()));
return surface.PassAs<cc::OutputSurface>();
}
scoped_refptr<base::SingleThreadTaskRunner> compositor_thread_task_runner =
- ui::Compositor::GetCompositorMessageLoop();
+ GetCompositorMessageLoop();
if (!compositor_thread_task_runner.get())
compositor_thread_task_runner = base::MessageLoopProxy::current();
@@ -241,12 +225,10 @@ scoped_ptr<cc::OutputSurface> GpuProcessTransportFactory::CreateOutputSurface(
context_provider,
per_compositor_data_[compositor]->surface_id,
&output_surface_map_,
- base::MessageLoopProxy::current().get(),
- compositor->AsWeakPtr()));
- if (data->reflector.get()) {
- data->reflector->CreateSharedTexture();
- data->reflector->AttachToOutputSurface(surface.get());
- }
+ compositor->vsync_manager(),
+ CreateOverlayCandidateValidator(compositor->widget())));
+ if (data->reflector.get())
+ data->reflector->ReattachToOutputSurfaceFromMainThread(surface.get());
return surface.PassAs<cc::OutputSurface>();
}
@@ -257,12 +239,11 @@ scoped_refptr<ui::Reflector> GpuProcessTransportFactory::CreateReflector(
PerCompositorData* data = per_compositor_data_[source];
DCHECK(data);
- if (data->reflector.get())
- RemoveObserver(data->reflector.get());
-
- data->reflector = new ReflectorImpl(
- source, target, &output_surface_map_, data->surface_id);
- AddObserver(data->reflector.get());
+ data->reflector = new ReflectorImpl(source,
+ target,
+ &output_surface_map_,
+ GetCompositorMessageLoop(),
+ data->surface_id);
return data->reflector;
}
@@ -273,7 +254,6 @@ void GpuProcessTransportFactory::RemoveReflector(
PerCompositorData* data =
per_compositor_data_[reflector_impl->mirrored_compositor()];
DCHECK(data);
- RemoveObserver(reflector_impl);
data->reflector->Shutdown();
data->reflector = NULL;
}
@@ -287,88 +267,60 @@ void GpuProcessTransportFactory::RemoveCompositor(ui::Compositor* compositor) {
GpuSurfaceTracker::Get()->RemoveSurface(data->surface_id);
delete data;
per_compositor_data_.erase(it);
- if (per_compositor_data_.empty())
- gl_helper_.reset();
+ if (per_compositor_data_.empty()) {
+ // Destroying the GLHelper may cause some async actions to be cancelled,
+ // causing things to request a new GLHelper. Due to crbug.com/176091 the
+ // GLHelper created in this case would be lost/leaked if we just reset()
+ // on the |gl_helper_| variable directly. So instead we call reset() on a
+ // local scoped_ptr.
+ scoped_ptr<GLHelper> helper = gl_helper_.Pass();
+
+ // If there are any observer left at this point, make sure they clean up
+ // before we destroy the GLHelper.
+ FOR_EACH_OBSERVER(
+ ImageTransportFactoryObserver, observer_list_, OnLostResources());
+
+ helper.reset();
+ DCHECK(!gl_helper_) << "Destroying the GLHelper should not cause a new "
+ "GLHelper to be created.";
+ }
}
bool GpuProcessTransportFactory::DoesCreateTestContexts() { return false; }
-ui::ContextFactory* GpuProcessTransportFactory::AsContextFactory() {
+cc::SharedBitmapManager* GpuProcessTransportFactory::GetSharedBitmapManager() {
+ return HostSharedBitmapManager::current();
+}
+
+ui::ContextFactory* GpuProcessTransportFactory::GetContextFactory() {
return this;
}
-gfx::GLSurfaceHandle GpuProcessTransportFactory::CreateSharedSurfaceHandle() {
- scoped_refptr<cc::ContextProvider> provider =
- SharedMainThreadContextProvider();
- if (!provider.get())
- return gfx::GLSurfaceHandle();
- typedef WebGraphicsContext3DCommandBufferImpl WGC3DCBI;
- WGC3DCBI* context = static_cast<WGC3DCBI*>(provider->Context3d());
+base::MessageLoopProxy* GpuProcessTransportFactory::GetCompositorMessageLoop() {
+ if (!compositor_thread_)
+ return NULL;
+ return compositor_thread_->message_loop_proxy();
+}
+
+gfx::GLSurfaceHandle GpuProcessTransportFactory::GetSharedSurfaceHandle() {
gfx::GLSurfaceHandle handle = gfx::GLSurfaceHandle(
gfx::kNullPluginWindow, gfx::TEXTURE_TRANSPORT);
- handle.parent_gpu_process_id = context->GetGPUProcessID();
handle.parent_client_id =
BrowserGpuChannelHostFactory::instance()->GetGpuChannelId();
return handle;
}
-void GpuProcessTransportFactory::DestroySharedSurfaceHandle(
- gfx::GLSurfaceHandle surface) {}
-
-scoped_refptr<ui::Texture> GpuProcessTransportFactory::CreateTransportClient(
- float device_scale_factor) {
- scoped_refptr<cc::ContextProvider> provider =
- SharedMainThreadContextProvider();
- if (!provider.get())
- return NULL;
- GLuint texture_id = 0;
- provider->ContextGL()->GenTextures(1, &texture_id);
- scoped_refptr<ImageTransportClientTexture> image(
- new ImageTransportClientTexture(
- provider, device_scale_factor, texture_id));
- return image;
-}
-
-scoped_refptr<ui::Texture> GpuProcessTransportFactory::CreateOwnedTexture(
- const gfx::Size& size,
- float device_scale_factor,
- unsigned int texture_id) {
- scoped_refptr<cc::ContextProvider> provider =
- SharedMainThreadContextProvider();
- if (!provider.get())
- return NULL;
- scoped_refptr<OwnedTexture> image(new OwnedTexture(
- provider, size, device_scale_factor, texture_id));
- return image;
-}
-
GLHelper* GpuProcessTransportFactory::GetGLHelper() {
- if (!gl_helper_) {
+ if (!gl_helper_ && !per_compositor_data_.empty()) {
scoped_refptr<cc::ContextProvider> provider =
SharedMainThreadContextProvider();
if (provider.get())
- gl_helper_.reset(new GLHelper(provider->Context3d(),
+ gl_helper_.reset(new GLHelper(provider->ContextGL(),
provider->ContextSupport()));
}
return gl_helper_.get();
}
-uint32 GpuProcessTransportFactory::InsertSyncPoint() {
- scoped_refptr<cc::ContextProvider> provider =
- SharedMainThreadContextProvider();
- if (!provider.get())
- return 0;
- return provider->Context3d()->insertSyncPoint();
-}
-
-void GpuProcessTransportFactory::WaitSyncPoint(uint32 sync_point) {
- scoped_refptr<cc::ContextProvider> provider =
- SharedMainThreadContextProvider();
- if (!provider.get())
- return;
- provider->Context3d()->waitSyncPoint(sync_point);
-}
-
void GpuProcessTransportFactory::AddObserver(
ImageTransportFactoryObserver* observer) {
observer_list_.AddObserver(observer);
@@ -379,51 +331,35 @@ void GpuProcessTransportFactory::RemoveObserver(
observer_list_.RemoveObserver(observer);
}
-scoped_refptr<cc::ContextProvider>
-GpuProcessTransportFactory::OffscreenCompositorContextProvider() {
- // Don't check for DestroyedOnMainThread() here. We hear about context
- // loss for this context through the lost context callback. If the context
- // is lost, we want to leave this ContextProvider available until the lost
- // context notification is sent to the ImageTransportFactoryObserver clients.
- if (offscreen_compositor_contexts_.get())
- return offscreen_compositor_contexts_;
-
- offscreen_compositor_contexts_ = ContextProviderCommandBuffer::Create(
- GpuProcessTransportFactory::CreateOffscreenCommandBufferContext(),
- "Compositor-Offscreen");
-
- return offscreen_compositor_contexts_;
+#if defined(OS_MACOSX)
+void GpuProcessTransportFactory::OnSurfaceDisplayed(int surface_id) {
+ BrowserCompositorOutputSurface* surface = output_surface_map_.Lookup(
+ surface_id);
+ if (surface)
+ surface->OnSurfaceDisplayed();
}
+#endif
scoped_refptr<cc::ContextProvider>
GpuProcessTransportFactory::SharedMainThreadContextProvider() {
if (shared_main_thread_contexts_.get())
return shared_main_thread_contexts_;
- if (ui::Compositor::WasInitializedWithThread()) {
- // In threaded compositing mode, we have to create our own context for the
- // main thread since the compositor's context will be bound to the
- // compositor thread.
- shared_main_thread_contexts_ = ContextProviderCommandBuffer::Create(
- GpuProcessTransportFactory::CreateOffscreenCommandBufferContext(),
- "Offscreen-MainThread");
- } else {
- // In single threaded compositing mode, we can just reuse the compositor's
- // shared context.
- shared_main_thread_contexts_ =
- static_cast<ContextProviderCommandBuffer*>(
- OffscreenCompositorContextProvider().get());
- }
+ // In threaded compositing mode, we have to create our own context for the
+ // main thread since the compositor's context will be bound to the
+ // compositor thread. When not in threaded mode, we still need a separate
+ // context so that skia and gl_helper don't step on each other.
+ shared_main_thread_contexts_ = ContextProviderCommandBuffer::Create(
+ GpuProcessTransportFactory::CreateOffscreenCommandBufferContext(),
+ "Offscreen-MainThread");
if (shared_main_thread_contexts_) {
shared_main_thread_contexts_->SetLostContextCallback(
base::Bind(&GpuProcessTransportFactory::
OnLostMainThreadSharedContextInsideCallback,
callback_factory_.GetWeakPtr()));
- if (!shared_main_thread_contexts_->BindToCurrentThread()) {
+ if (!shared_main_thread_contexts_->BindToCurrentThread())
shared_main_thread_contexts_ = NULL;
- offscreen_compositor_contexts_ = NULL;
- }
}
return shared_main_thread_contexts_;
}
@@ -438,10 +374,6 @@ GpuProcessTransportFactory::CreatePerCompositorData(
PerCompositorData* data = new PerCompositorData;
data->surface_id = tracker->AddSurfaceForNativeWidget(widget);
-#if defined(OS_WIN)
- if (GpuDataManagerImpl::GetInstance()->IsUsingAcceleratedSurface())
- data->accelerated_surface.reset(new AcceleratedSurface(widget));
-#endif
tracker->SetSurfaceHandle(
data->surface_id,
gfx::GLSurfaceHandle(widget, gfx::NATIVE_DIRECT));
@@ -461,12 +393,15 @@ GpuProcessTransportFactory::CreateContextCommon(int surface_id) {
attrs.stencil = false;
attrs.antialias = false;
attrs.noAutomaticFlushes = true;
+ bool lose_context_when_out_of_memory = true;
CauseForGpuLaunch cause =
CAUSE_FOR_GPU_LAUNCH_WEBGRAPHICSCONTEXT3DCOMMANDBUFFERIMPL_INITIALIZE;
scoped_refptr<GpuChannelHost> gpu_channel_host(
BrowserGpuChannelHostFactory::instance()->EstablishGpuChannelSync(cause));
- if (!gpu_channel_host)
+ if (!gpu_channel_host) {
+ LOG(ERROR) << "Failed to establish GPU channel.";
return scoped_ptr<WebGraphicsContext3DCommandBufferImpl>();
+ }
GURL url("chrome://gpu/GpuProcessTransportFactory::CreateContextCommon");
scoped_ptr<WebGraphicsContext3DCommandBufferImpl> context(
new WebGraphicsContext3DCommandBufferImpl(
@@ -474,8 +409,9 @@ GpuProcessTransportFactory::CreateContextCommon(int surface_id) {
url,
gpu_channel_host.get(),
attrs,
- false,
- WebGraphicsContext3DCommandBufferImpl::SharedMemoryLimits()));
+ lose_context_when_out_of_memory,
+ WebGraphicsContext3DCommandBufferImpl::SharedMemoryLimits(),
+ NULL));
return context.Pass();
}
@@ -493,11 +429,8 @@ void GpuProcessTransportFactory::OnLostMainThreadSharedContext() {
// new resources are created if needed.
// Kill shared contexts for both threads in tandem so they are always in
// the same share group.
- scoped_refptr<cc::ContextProvider> lost_offscreen_compositor_contexts =
- offscreen_compositor_contexts_;
scoped_refptr<cc::ContextProvider> lost_shared_main_thread_contexts =
shared_main_thread_contexts_;
- offscreen_compositor_contexts_ = NULL;
shared_main_thread_contexts_ = NULL;
scoped_ptr<GLHelper> lost_gl_helper = gl_helper_.Pass();
@@ -508,7 +441,6 @@ void GpuProcessTransportFactory::OnLostMainThreadSharedContext() {
// Kill things that use the shared context before killing the shared context.
lost_gl_helper.reset();
- lost_offscreen_compositor_contexts = NULL;
lost_shared_main_thread_contexts = NULL;
}
diff --git a/chromium/content/browser/aura/gpu_process_transport_factory.h b/chromium/content/browser/compositor/gpu_process_transport_factory.h
index 13da92b2539..b9c1bee1209 100644
--- a/chromium/content/browser/aura/gpu_process_transport_factory.h
+++ b/chromium/content/browser/compositor/gpu_process_transport_factory.h
@@ -1,9 +1,9 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// 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_AURA_GPU_PROCESS_TRANSPORT_FACTORY_H_
-#define CONTENT_BROWSER_AURA_GPU_PROCESS_TRANSPORT_FACTORY_H_
+#ifndef CONTENT_BROWSER_COMPOSITOR_GPU_PROCESS_TRANSPORT_FACTORY_H_
+#define CONTENT_BROWSER_COMPOSITOR_GPU_PROCESS_TRANSPORT_FACTORY_H_
#include <map>
@@ -12,9 +12,17 @@
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
-#include "content/browser/aura/image_transport_factory.h"
+#include "content/browser/compositor/image_transport_factory.h"
#include "ui/compositor/compositor.h"
+namespace base {
+class Thread;
+}
+
+namespace cc {
+class SurfaceManager;
+}
+
namespace content {
class BrowserCompositorOutputSurface;
class BrowserCompositorOutputSurfaceProxy;
@@ -44,28 +52,21 @@ class GpuProcessTransportFactory
scoped_refptr<ui::Reflector> reflector) OVERRIDE;
virtual void RemoveCompositor(ui::Compositor* compositor) OVERRIDE;
virtual scoped_refptr<cc::ContextProvider>
- OffscreenCompositorContextProvider() OVERRIDE;
- virtual scoped_refptr<cc::ContextProvider>
SharedMainThreadContextProvider() OVERRIDE;
virtual bool DoesCreateTestContexts() OVERRIDE;
+ virtual cc::SharedBitmapManager* GetSharedBitmapManager() OVERRIDE;
+ virtual base::MessageLoopProxy* GetCompositorMessageLoop() OVERRIDE;
// ImageTransportFactory implementation.
- virtual ui::ContextFactory* AsContextFactory() OVERRIDE;
- virtual gfx::GLSurfaceHandle CreateSharedSurfaceHandle() OVERRIDE;
- virtual void DestroySharedSurfaceHandle(
- gfx::GLSurfaceHandle surface) OVERRIDE;
- virtual scoped_refptr<ui::Texture> CreateTransportClient(
- float device_scale_factor) OVERRIDE;
- virtual scoped_refptr<ui::Texture> CreateOwnedTexture(
- const gfx::Size& size,
- float device_scale_factor,
- unsigned int texture_id) OVERRIDE;
+ virtual ui::ContextFactory* GetContextFactory() OVERRIDE;
+ virtual gfx::GLSurfaceHandle GetSharedSurfaceHandle() OVERRIDE;
virtual GLHelper* GetGLHelper() OVERRIDE;
- virtual uint32 InsertSyncPoint() OVERRIDE;
- virtual void WaitSyncPoint(uint32 sync_point) OVERRIDE;
virtual void AddObserver(ImageTransportFactoryObserver* observer) OVERRIDE;
virtual void RemoveObserver(
ImageTransportFactoryObserver* observer) OVERRIDE;
+#if defined(OS_MACOSX)
+ virtual void OnSurfaceDisplayed(int surface_id) OVERRIDE;
+#endif
private:
struct PerCompositorData;
@@ -78,12 +79,13 @@ class GpuProcessTransportFactory
void OnLostMainThreadSharedContext();
typedef std::map<ui::Compositor*, PerCompositorData*> PerCompositorDataMap;
+ scoped_ptr<base::Thread> compositor_thread_;
PerCompositorDataMap per_compositor_data_;
- scoped_refptr<ContextProviderCommandBuffer> offscreen_compositor_contexts_;
scoped_refptr<ContextProviderCommandBuffer> shared_main_thread_contexts_;
scoped_ptr<GLHelper> gl_helper_;
ObserverList<ImageTransportFactoryObserver> observer_list_;
base::WeakPtrFactory<GpuProcessTransportFactory> callback_factory_;
+ scoped_ptr<cc::SurfaceManager> surface_manager_;
// The contents of this map and its methods may only be used on the compositor
// thread.
@@ -96,4 +98,4 @@ class GpuProcessTransportFactory
} // namespace content
-#endif // CONTENT_BROWSER_AURA_GPU_PROCESS_TRANSPORT_FACTORY_H_
+#endif // CONTENT_BROWSER_COMPOSITOR_GPU_PROCESS_TRANSPORT_FACTORY_H_
diff --git a/chromium/content/browser/compositor/image_transport_factory.cc b/chromium/content/browser/compositor/image_transport_factory.cc
new file mode 100644
index 00000000000..66d484f51eb
--- /dev/null
+++ b/chromium/content/browser/compositor/image_transport_factory.cc
@@ -0,0 +1,62 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/compositor/image_transport_factory.h"
+
+#include "base/command_line.h"
+#include "content/browser/compositor/gpu_process_transport_factory.h"
+#include "content/browser/compositor/no_transport_image_transport_factory.h"
+#include "ui/compositor/compositor.h"
+#include "ui/compositor/compositor_switches.h"
+#include "ui/gl/gl_implementation.h"
+
+namespace content {
+
+namespace {
+ImageTransportFactory* g_factory = NULL;
+bool g_initialized_for_unit_tests = false;
+static gfx::DisableNullDrawGLBindings* g_disable_null_draw = NULL;
+
+void SetFactory(ImageTransportFactory* factory) {
+ g_factory = factory;
+}
+
+}
+
+// static
+void ImageTransportFactory::Initialize() {
+ DCHECK(!g_factory || g_initialized_for_unit_tests);
+ if (g_initialized_for_unit_tests)
+ return;
+ SetFactory(new GpuProcessTransportFactory);
+}
+
+void ImageTransportFactory::InitializeForUnitTests(
+ scoped_ptr<ui::ContextFactory> test_factory) {
+ DCHECK(!g_factory);
+ DCHECK(!g_initialized_for_unit_tests);
+ g_initialized_for_unit_tests = true;
+
+ CommandLine* command_line = CommandLine::ForCurrentProcess();
+ if (command_line->HasSwitch(switches::kEnablePixelOutputInTests))
+ g_disable_null_draw = new gfx::DisableNullDrawGLBindings;
+
+ SetFactory(new NoTransportImageTransportFactory(test_factory.Pass()));
+}
+
+// static
+void ImageTransportFactory::Terminate() {
+ delete g_factory;
+ g_factory = NULL;
+ delete g_disable_null_draw;
+ g_disable_null_draw = NULL;
+ g_initialized_for_unit_tests = false;
+}
+
+// static
+ImageTransportFactory* ImageTransportFactory::GetInstance() {
+ return g_factory;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/aura/image_transport_factory.h b/chromium/content/browser/compositor/image_transport_factory.h
index fecb4426edf..f53c4a7028f 100644
--- a/chromium/content/browser/aura/image_transport_factory.h
+++ b/chromium/content/browser/compositor/image_transport_factory.h
@@ -1,9 +1,9 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// 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_AURA_IMAGE_TRANSPORT_FACTORY_H_
-#define CONTENT_BROWSER_AURA_IMAGE_TRANSPORT_FACTORY_H_
+#ifndef CONTENT_BROWSER_COMPOSITOR_IMAGE_TRANSPORT_FACTORY_H_
+#define CONTENT_BROWSER_COMPOSITOR_IMAGE_TRANSPORT_FACTORY_H_
#include <string>
@@ -65,42 +65,23 @@ class CONTENT_EXPORT ImageTransportFactory {
static ImageTransportFactory* GetInstance();
// Gets the image transport factory as a context factory for the compositor.
- virtual ui::ContextFactory* AsContextFactory() = 0;
+ virtual ui::ContextFactory* GetContextFactory() = 0;
- // Creates a shared surface handle.
- // Note: the handle may get lost at any time, a state that an
- // ImageTransportFactoryObserver gets notified of.
- virtual gfx::GLSurfaceHandle CreateSharedSurfaceHandle() = 0;
-
- // Destroys a shared surface handle.
- virtual void DestroySharedSurfaceHandle(gfx::GLSurfaceHandle surface) = 0;
-
- // Creates a transport texture for a given scale factor.
- virtual scoped_refptr<ui::Texture> CreateTransportClient(
- float device_scale_factor) = 0;
-
- // Variant of CreateTransportClient() that deletes the texture on the GPU when
- // the returned value is deleted.
- virtual scoped_refptr<ui::Texture> CreateOwnedTexture(
- const gfx::Size& size,
- float device_scale_factor,
- unsigned int texture_id) = 0;
+ virtual gfx::GLSurfaceHandle GetSharedSurfaceHandle() = 0;
// Gets a GLHelper instance, associated with the shared context. This
// GLHelper will get destroyed whenever the shared context is lost
// (ImageTransportFactoryObserver::OnLostResources is called).
virtual GLHelper* GetGLHelper() = 0;
- // Inserts a SyncPoint into the shared context.
- virtual uint32 InsertSyncPoint() = 0;
-
- // Blocks waiting for the sync point on the service side.
- virtual void WaitSyncPoint(uint32 sync_point) = 0;
-
virtual void AddObserver(ImageTransportFactoryObserver* observer) = 0;
virtual void RemoveObserver(ImageTransportFactoryObserver* observer) = 0;
+
+#if defined(OS_MACOSX)
+ virtual void OnSurfaceDisplayed(int surface_id) = 0;
+#endif
};
} // namespace content
-#endif // CONTENT_BROWSER_AURA_IMAGE_TRANSPORT_FACTORY_H_
+#endif // CONTENT_BROWSER_COMPOSITOR_IMAGE_TRANSPORT_FACTORY_H_
diff --git a/chromium/content/browser/compositor/image_transport_factory_browsertest.cc b/chromium/content/browser/compositor/image_transport_factory_browsertest.cc
new file mode 100644
index 00000000000..61f1bbf107f
--- /dev/null
+++ b/chromium/content/browser/compositor/image_transport_factory_browsertest.cc
@@ -0,0 +1,110 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/compositor/image_transport_factory.h"
+
+#include "base/run_loop.h"
+#include "cc/output/context_provider.h"
+#include "content/browser/compositor/owned_mailbox.h"
+#include "content/public/browser/gpu_data_manager.h"
+#include "content/public/test/content_browser_test.h"
+#include "gpu/GLES2/gl2extchromium.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "ui/compositor/compositor.h"
+
+namespace content {
+namespace {
+
+typedef ContentBrowserTest ImageTransportFactoryBrowserTest;
+
+class MockImageTransportFactoryObserver : public ImageTransportFactoryObserver {
+ public:
+ MOCK_METHOD0(OnLostResources, void());
+};
+
+// This crashes on Mac ASAN
+// http://crbug.com/335083
+#if defined(OS_MACOSX)
+#define MAYBE_TestLostContext DISABLED_TestLostContext
+#else
+#define MAYBE_TestLostContext TestLostContext
+#endif
+// Checks that upon context loss, the observer is called and the created
+// resources are reset.
+IN_PROC_BROWSER_TEST_F(ImageTransportFactoryBrowserTest,
+ MAYBE_TestLostContext) {
+ // This test doesn't make sense in software compositing mode.
+ if (!GpuDataManager::GetInstance()->CanUseGpuBrowserCompositor())
+ return;
+
+ ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
+
+ scoped_refptr<OwnedMailbox> mailbox =
+ new OwnedMailbox(factory->GetGLHelper());
+ EXPECT_FALSE(mailbox->mailbox().IsZero());
+
+ MockImageTransportFactoryObserver observer;
+ factory->AddObserver(&observer);
+
+ base::RunLoop run_loop;
+ EXPECT_CALL(observer, OnLostResources())
+ .WillOnce(testing::InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
+
+ ui::ContextFactory* context_factory = factory->GetContextFactory();
+ gpu::gles2::GLES2Interface* gl =
+ context_factory->SharedMainThreadContextProvider()->ContextGL();
+ gl->LoseContextCHROMIUM(GL_GUILTY_CONTEXT_RESET_ARB,
+ GL_INNOCENT_CONTEXT_RESET_ARB);
+
+ // We have to flush to make sure that the client side gets a chance to notice
+ // the context is gone.
+ gl->Flush();
+
+ run_loop.Run();
+ EXPECT_TRUE(mailbox->mailbox().IsZero());
+
+ factory->RemoveObserver(&observer);
+}
+
+class ImageTransportFactoryTearDownBrowserTest : public ContentBrowserTest {
+ public:
+ ImageTransportFactoryTearDownBrowserTest() {}
+
+ virtual void TearDown() {
+ if (mailbox_)
+ EXPECT_TRUE(mailbox_->mailbox().IsZero());
+ ContentBrowserTest::TearDown();
+ }
+
+ protected:
+ scoped_refptr<OwnedMailbox> mailbox_;
+};
+
+// This crashes on Mac. ImageTransportFactory is NULL unless
+// --enable-delegated-renderer is passed, and after that, we'd need to spawn a
+// renderer and get a frame before we create a browser compositor, necessary for
+// the GLHelper to not be NULL.
+// http://crbug.com/335083
+#if defined(OS_MACOSX)
+#define MAYBE_LoseOnTearDown DISABLED_LoseOnTearDown
+#else
+#define MAYBE_LoseOnTearDown LoseOnTearDown
+#endif
+// Checks that upon destruction of the ImageTransportFactory, the observer is
+// called and the created resources are reset.
+IN_PROC_BROWSER_TEST_F(ImageTransportFactoryTearDownBrowserTest,
+ MAYBE_LoseOnTearDown) {
+ // This test doesn't make sense in software compositing mode.
+ if (!GpuDataManager::GetInstance()->CanUseGpuBrowserCompositor())
+ return;
+ ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
+ GLHelper* helper = factory->GetGLHelper();
+ ASSERT_TRUE(helper);
+ mailbox_ = new OwnedMailbox(helper);
+ EXPECT_FALSE(mailbox_->mailbox().IsZero());
+}
+
+} // anonymous namespace
+} // namespace content
diff --git a/chromium/content/browser/compositor/no_transport_image_transport_factory.cc b/chromium/content/browser/compositor/no_transport_image_transport_factory.cc
new file mode 100644
index 00000000000..03289c0f685
--- /dev/null
+++ b/chromium/content/browser/compositor/no_transport_image_transport_factory.cc
@@ -0,0 +1,53 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/compositor/no_transport_image_transport_factory.h"
+
+#include "cc/output/context_provider.h"
+#include "content/common/gpu/client/gl_helper.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "ui/compositor/compositor.h"
+
+namespace content {
+
+NoTransportImageTransportFactory::NoTransportImageTransportFactory(
+ scoped_ptr<ui::ContextFactory> context_factory)
+ : context_factory_(context_factory.Pass()) {}
+
+NoTransportImageTransportFactory::~NoTransportImageTransportFactory() {
+ scoped_ptr<GLHelper> lost_gl_helper = gl_helper_.Pass();
+ FOR_EACH_OBSERVER(ImageTransportFactoryObserver,
+ observer_list_,
+ OnLostResources());
+}
+
+ui::ContextFactory* NoTransportImageTransportFactory::GetContextFactory() {
+ return context_factory_.get();
+}
+
+gfx::GLSurfaceHandle
+NoTransportImageTransportFactory::GetSharedSurfaceHandle() {
+ return gfx::GLSurfaceHandle();
+}
+
+GLHelper* NoTransportImageTransportFactory::GetGLHelper() {
+ if (!gl_helper_) {
+ context_provider_ = context_factory_->SharedMainThreadContextProvider();
+ gl_helper_.reset(new GLHelper(context_provider_->ContextGL(),
+ context_provider_->ContextSupport()));
+ }
+ return gl_helper_.get();
+}
+
+void NoTransportImageTransportFactory::AddObserver(
+ ImageTransportFactoryObserver* observer) {
+ observer_list_.AddObserver(observer);
+}
+
+void NoTransportImageTransportFactory::RemoveObserver(
+ ImageTransportFactoryObserver* observer) {
+ observer_list_.RemoveObserver(observer);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/aura/no_transport_image_transport_factory.h b/chromium/content/browser/compositor/no_transport_image_transport_factory.h
index dd551d97d53..fd5f6bf1be5 100644
--- a/chromium/content/browser/aura/no_transport_image_transport_factory.h
+++ b/chromium/content/browser/compositor/no_transport_image_transport_factory.h
@@ -1,12 +1,13 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// 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_AURA_NO_TRANSPORT_IMAGE_TRANSPORT_FACTORY_H_
-#define CONTENT_BROWSER_AURA_NO_TRANSPORT_IMAGE_TRANSPORT_FACTORY_H_
+#ifndef CONTENT_BROWSER_COMPOSITOR_NO_TRANSPORT_IMAGE_TRANSPORT_FACTORY_H_
+#define CONTENT_BROWSER_COMPOSITOR_NO_TRANSPORT_IMAGE_TRANSPORT_FACTORY_H_
#include "base/memory/scoped_ptr.h"
-#include "content/browser/aura/image_transport_factory.h"
+#include "base/observer_list.h"
+#include "content/browser/compositor/image_transport_factory.h"
namespace cc {
class ContextProvider;
@@ -22,30 +23,24 @@ class NoTransportImageTransportFactory : public ImageTransportFactory {
virtual ~NoTransportImageTransportFactory();
// ImageTransportFactory implementation.
- virtual ui::ContextFactory* AsContextFactory() OVERRIDE;
- virtual gfx::GLSurfaceHandle CreateSharedSurfaceHandle() OVERRIDE;
- virtual void DestroySharedSurfaceHandle(gfx::GLSurfaceHandle surface)
- OVERRIDE;
- virtual scoped_refptr<ui::Texture> CreateTransportClient(
- float device_scale_factor) OVERRIDE;
- virtual scoped_refptr<ui::Texture> CreateOwnedTexture(
- const gfx::Size& size,
- float device_scale_factor,
- unsigned int texture_id) OVERRIDE;
+ virtual ui::ContextFactory* GetContextFactory() OVERRIDE;
+ virtual gfx::GLSurfaceHandle GetSharedSurfaceHandle() OVERRIDE;
virtual GLHelper* GetGLHelper() OVERRIDE;
- virtual uint32 InsertSyncPoint() OVERRIDE;
- virtual void WaitSyncPoint(uint32 sync_point) OVERRIDE;
virtual void AddObserver(ImageTransportFactoryObserver* observer) OVERRIDE;
virtual void RemoveObserver(ImageTransportFactoryObserver* observer) OVERRIDE;
+#if defined(OS_MACOSX)
+ virtual void OnSurfaceDisplayed(int surface_id) OVERRIDE {}
+#endif
private:
scoped_ptr<ui::ContextFactory> context_factory_;
scoped_refptr<cc::ContextProvider> context_provider_;
scoped_ptr<GLHelper> gl_helper_;
+ ObserverList<ImageTransportFactoryObserver> observer_list_;
DISALLOW_COPY_AND_ASSIGN(NoTransportImageTransportFactory);
};
} // namespace content
-#endif // CONTENT_BROWSER_AURA_NO_TRANSPORT_IMAGE_TRANSPORT_FACTORY_H_
+#endif // CONTENT_BROWSER_COMPOSITOR_NO_TRANSPORT_IMAGE_TRANSPORT_FACTORY_H_
diff --git a/chromium/content/browser/compositor/onscreen_display_client.cc b/chromium/content/browser/compositor/onscreen_display_client.cc
new file mode 100644
index 00000000000..814f0fcc53e
--- /dev/null
+++ b/chromium/content/browser/compositor/onscreen_display_client.cc
@@ -0,0 +1,31 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/compositor/onscreen_display_client.h"
+
+#include "cc/output/output_surface.h"
+#include "content/common/host_shared_bitmap_manager.h"
+
+namespace content {
+
+OnscreenDisplayClient::OnscreenDisplayClient(
+ const scoped_refptr<cc::ContextProvider>& onscreen_context_provider,
+ scoped_ptr<cc::OutputSurface> software_surface,
+ cc::SurfaceManager* manager)
+ : onscreen_context_provider_(onscreen_context_provider),
+ software_surface_(software_surface.Pass()),
+ display_(this, manager, HostSharedBitmapManager::current()) {
+}
+
+OnscreenDisplayClient::~OnscreenDisplayClient() {
+}
+
+scoped_ptr<cc::OutputSurface> OnscreenDisplayClient::CreateOutputSurface() {
+ if (!onscreen_context_provider_)
+ return software_surface_.Pass();
+ return make_scoped_ptr(new cc::OutputSurface(onscreen_context_provider_))
+ .Pass();
+}
+
+} // namespace content
diff --git a/chromium/content/browser/compositor/onscreen_display_client.h b/chromium/content/browser/compositor/onscreen_display_client.h
new file mode 100644
index 00000000000..7ab6ff7d02b
--- /dev/null
+++ b/chromium/content/browser/compositor/onscreen_display_client.h
@@ -0,0 +1,46 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_COMPOSITOR_ONSCREEN_DISPLAY_CLIENT_H_
+#define CONTENT_BROWSER_COMPOSITOR_ONSCREEN_DISPLAY_CLIENT_H_
+
+#include "cc/surfaces/display_client.h"
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "cc/surfaces/display.h"
+
+namespace cc {
+class ContextProvider;
+class SurfaceManager;
+}
+
+namespace content {
+
+// This class provides a DisplayClient implementation for drawing directly to an
+// onscreen context.
+class OnscreenDisplayClient : cc::DisplayClient {
+ public:
+ OnscreenDisplayClient(
+ const scoped_refptr<cc::ContextProvider>& onscreen_context_provider,
+ scoped_ptr<cc::OutputSurface> software_surface,
+ cc::SurfaceManager* manager);
+ virtual ~OnscreenDisplayClient();
+
+ cc::Display* display() { return &display_; }
+
+ // cc::DisplayClient implementation.
+ virtual scoped_ptr<cc::OutputSurface> CreateOutputSurface() OVERRIDE;
+
+ private:
+ scoped_refptr<cc::ContextProvider> onscreen_context_provider_;
+ scoped_ptr<cc::OutputSurface> software_surface_;
+ cc::Display display_;
+
+ DISALLOW_COPY_AND_ASSIGN(OnscreenDisplayClient);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_COMPOSITOR_ONSCREEN_DISPLAY_CLIENT_H_
diff --git a/chromium/content/browser/compositor/overlay_candidate_validator_ozone.cc b/chromium/content/browser/compositor/overlay_candidate_validator_ozone.cc
new file mode 100644
index 00000000000..b4a3cfeace0
--- /dev/null
+++ b/chromium/content/browser/compositor/overlay_candidate_validator_ozone.cc
@@ -0,0 +1,57 @@
+// 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/overlay_candidate_validator_ozone.h"
+
+#include "ui/ozone/public/overlay_candidates_ozone.h"
+
+namespace content {
+
+static ui::SurfaceFactoryOzone::BufferFormat GetOzoneFormat(
+ cc::ResourceFormat overlay_format) {
+ switch (overlay_format) {
+ case cc::RGBA_8888:
+ return ui::SurfaceFactoryOzone::RGBA_8888;
+ case cc::RGBA_4444:
+ case cc::BGRA_8888:
+ case cc::LUMINANCE_8:
+ case cc::RGB_565:
+ case cc::ETC1:
+ break;
+ }
+ NOTREACHED();
+ return ui::SurfaceFactoryOzone::UNKNOWN;
+}
+
+OverlayCandidateValidatorOzone::OverlayCandidateValidatorOzone(
+ gfx::AcceleratedWidget widget,
+ ui::OverlayCandidatesOzone* overlay_candidates)
+ : widget_(widget), overlay_candidates_(overlay_candidates) {
+}
+
+OverlayCandidateValidatorOzone::~OverlayCandidateValidatorOzone() {}
+
+void OverlayCandidateValidatorOzone::CheckOverlaySupport(
+ cc::OverlayCandidateList* surfaces) {
+ DCHECK_GE(2U, surfaces->size());
+ ui::OverlayCandidatesOzone::OverlaySurfaceCandidateList ozone_surface_list;
+ ozone_surface_list.resize(surfaces->size());
+
+ for (size_t i = 0; i < surfaces->size(); i++) {
+ ozone_surface_list.at(i).transform = surfaces->at(i).transform;
+ ozone_surface_list.at(i).format = GetOzoneFormat(surfaces->at(i).format);
+ ozone_surface_list.at(i).display_rect = surfaces->at(i).display_rect;
+ ozone_surface_list.at(i).crop_rect = surfaces->at(i).uv_rect;
+ ozone_surface_list.at(i).plane_z_order = surfaces->at(i).plane_z_order;
+ }
+
+ overlay_candidates_->CheckOverlaySupport(&ozone_surface_list);
+ DCHECK_EQ(surfaces->size(), ozone_surface_list.size());
+
+ for (size_t i = 0; i < surfaces->size(); i++) {
+ surfaces->at(i).overlay_handled = ozone_surface_list.at(i).overlay_handled;
+ }
+}
+
+} // namespace content
diff --git a/chromium/content/browser/compositor/overlay_candidate_validator_ozone.h b/chromium/content/browser/compositor/overlay_candidate_validator_ozone.h
new file mode 100644
index 00000000000..8fb690b5196
--- /dev/null
+++ b/chromium/content/browser/compositor/overlay_candidate_validator_ozone.h
@@ -0,0 +1,39 @@
+// 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_OVERLAY_CANDIDATE_VALIDATOR_OZONE_H_
+#define CONTENT_BROWSER_COMPOSITOR_OVERLAY_CANDIDATE_VALIDATOR_OZONE_H_
+
+#include "cc/output/overlay_candidate_validator.h"
+
+#include "content/common/content_export.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace ui {
+class OverlayCandidatesOzone;
+}
+
+namespace content {
+
+class CONTENT_EXPORT OverlayCandidateValidatorOzone
+ : public cc::OverlayCandidateValidator {
+ public:
+ OverlayCandidateValidatorOzone(
+ gfx::AcceleratedWidget widget,
+ ui::OverlayCandidatesOzone* overlay_candidates);
+ virtual ~OverlayCandidateValidatorOzone();
+
+ // cc::OverlayCandidateValidator implementation.
+ virtual void CheckOverlaySupport(cc::OverlayCandidateList* surfaces) OVERRIDE;
+
+ private:
+ gfx::AcceleratedWidget widget_;
+ ui::OverlayCandidatesOzone* overlay_candidates_;
+
+ DISALLOW_COPY_AND_ASSIGN(OverlayCandidateValidatorOzone);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_COMPOSITOR_OVERLAY_CANDIDATE_VALIDATOR_OZONE_H_
diff --git a/chromium/content/browser/aura/owned_mailbox.cc b/chromium/content/browser/compositor/owned_mailbox.cc
index 1a99d335ac2..9c7b37861f4 100644
--- a/chromium/content/browser/aura/owned_mailbox.cc
+++ b/chromium/content/browser/compositor/owned_mailbox.cc
@@ -1,42 +1,44 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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/aura/owned_mailbox.h"
+#include "content/browser/compositor/owned_mailbox.h"
#include "base/logging.h"
-#include "content/browser/aura/image_transport_factory.h"
+#include "content/browser/compositor/image_transport_factory.h"
#include "content/common/gpu/client/gl_helper.h"
namespace content {
OwnedMailbox::OwnedMailbox(GLHelper* gl_helper)
- : texture_id_(0), sync_point_(0), gl_helper_(gl_helper) {
+ : texture_id_(0), gl_helper_(gl_helper) {
texture_id_ = gl_helper_->CreateTexture();
- mailbox_ = gl_helper_->ProduceMailboxFromTexture(texture_id_, &sync_point_);
+ mailbox_holder_ = gl_helper_->ProduceMailboxHolderFromTexture(texture_id_);
ImageTransportFactory::GetInstance()->AddObserver(this);
}
OwnedMailbox::~OwnedMailbox() {
- ImageTransportFactory::GetInstance()->RemoveObserver(this);
- if (gl_helper_) {
- gl_helper_->WaitSyncPoint(sync_point_);
- gl_helper_->DeleteTexture(texture_id_);
- }
+ if (gl_helper_)
+ Destroy();
}
void OwnedMailbox::UpdateSyncPoint(uint32 sync_point) {
if (sync_point)
- sync_point_ = sync_point;
+ mailbox_holder_.sync_point = sync_point;
}
-void OwnedMailbox::OnLostResources() {
- gl_helper_->WaitSyncPoint(sync_point_);
+void OwnedMailbox::Destroy() {
+ ImageTransportFactory::GetInstance()->RemoveObserver(this);
+ gl_helper_->WaitSyncPoint(mailbox_holder_.sync_point);
gl_helper_->DeleteTexture(texture_id_);
texture_id_ = 0;
- mailbox_ = gpu::Mailbox();
- sync_point_ = 0;
+ mailbox_holder_ = gpu::MailboxHolder();
gl_helper_ = NULL;
}
+void OwnedMailbox::OnLostResources() {
+ if (gl_helper_)
+ Destroy();
+}
+
} // namespace content
diff --git a/chromium/content/browser/compositor/owned_mailbox.h b/chromium/content/browser/compositor/owned_mailbox.h
new file mode 100644
index 00000000000..3926c81e0fd
--- /dev/null
+++ b/chromium/content/browser/compositor/owned_mailbox.h
@@ -0,0 +1,49 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_COMPOSITOR_OWNED_MAILBOX_H_
+#define CONTENT_BROWSER_COMPOSITOR_OWNED_MAILBOX_H_
+
+#include "base/memory/ref_counted.h"
+#include "content/browser/compositor/image_transport_factory.h"
+#include "content/common/content_export.h"
+#include "gpu/command_buffer/common/mailbox_holder.h"
+
+namespace content {
+
+class GLHelper;
+
+// This class holds a texture id and gpu::Mailbox, and deletes the texture
+// id when the object itself is destroyed. Should only be created if a GLHelper
+// exists on the ImageTransportFactory.
+class CONTENT_EXPORT OwnedMailbox : public base::RefCounted<OwnedMailbox>,
+ public ImageTransportFactoryObserver {
+ public:
+ explicit OwnedMailbox(GLHelper* gl_helper);
+
+ const gpu::MailboxHolder& holder() const { return mailbox_holder_; }
+ const gpu::Mailbox& mailbox() const { return mailbox_holder_.mailbox; }
+ uint32 texture_id() const { return texture_id_; }
+ uint32 target() const { return mailbox_holder_.texture_target; }
+ uint32 sync_point() const { return mailbox_holder_.sync_point; }
+ void UpdateSyncPoint(uint32 sync_point);
+ void Destroy();
+
+ protected:
+ virtual ~OwnedMailbox();
+
+ // ImageTransportFactoryObserver implementation.
+ virtual void OnLostResources() OVERRIDE;
+
+ private:
+ friend class base::RefCounted<OwnedMailbox>;
+
+ uint32 texture_id_;
+ gpu::MailboxHolder mailbox_holder_;
+ GLHelper* gl_helper_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_COMPOSITOR_OWNED_MAILBOX_H_
diff --git a/chromium/content/browser/compositor/reflector_impl.cc b/chromium/content/browser/compositor/reflector_impl.cc
new file mode 100644
index 00000000000..2ccded325fe
--- /dev/null
+++ b/chromium/content/browser/compositor/reflector_impl.cc
@@ -0,0 +1,245 @@
+// 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/reflector_impl.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "content/browser/compositor/browser_compositor_output_surface.h"
+#include "content/browser/compositor/owned_mailbox.h"
+#include "content/common/gpu/client/gl_helper.h"
+#include "ui/compositor/layer.h"
+
+namespace content {
+
+ReflectorImpl::ReflectorImpl(
+ ui::Compositor* mirrored_compositor,
+ ui::Layer* mirroring_layer,
+ IDMap<BrowserCompositorOutputSurface>* output_surface_map,
+ base::MessageLoopProxy* compositor_thread_loop,
+ int surface_id)
+ : impl_unsafe_(output_surface_map),
+ main_unsafe_(mirrored_compositor, mirroring_layer),
+ impl_message_loop_(compositor_thread_loop),
+ main_message_loop_(base::MessageLoopProxy::current()),
+ surface_id_(surface_id) {
+ GLHelper* helper = ImageTransportFactory::GetInstance()->GetGLHelper();
+ MainThreadData& main = GetMain();
+ main.mailbox = new OwnedMailbox(helper);
+ impl_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &ReflectorImpl::InitOnImplThread, this, main.mailbox->holder()));
+}
+
+ReflectorImpl::MainThreadData::MainThreadData(
+ ui::Compositor* mirrored_compositor,
+ ui::Layer* mirroring_layer)
+ : needs_set_mailbox(true),
+ mirrored_compositor(mirrored_compositor),
+ mirroring_layer(mirroring_layer) {}
+
+ReflectorImpl::MainThreadData::~MainThreadData() {}
+
+ReflectorImpl::ImplThreadData::ImplThreadData(
+ IDMap<BrowserCompositorOutputSurface>* output_surface_map)
+ : output_surface_map(output_surface_map),
+ output_surface(NULL),
+ texture_id(0) {}
+
+ReflectorImpl::ImplThreadData::~ImplThreadData() {}
+
+ReflectorImpl::ImplThreadData& ReflectorImpl::GetImpl() {
+ DCHECK(impl_message_loop_->BelongsToCurrentThread());
+ return impl_unsafe_;
+}
+
+ReflectorImpl::MainThreadData& ReflectorImpl::GetMain() {
+ DCHECK(main_message_loop_->BelongsToCurrentThread());
+ return main_unsafe_;
+}
+
+void ReflectorImpl::InitOnImplThread(const gpu::MailboxHolder& mailbox_holder) {
+ ImplThreadData& impl = GetImpl();
+ // Ignore if the reflector was shutdown before
+ // initialized, or it's already initialized.
+ if (!impl.output_surface_map || impl.gl_helper.get())
+ return;
+
+ impl.mailbox_holder = mailbox_holder;
+
+ BrowserCompositorOutputSurface* source_surface =
+ impl.output_surface_map->Lookup(surface_id_);
+ // Skip if the source surface isn't ready yet. This will be
+ // initialized when the source surface becomes ready.
+ if (!source_surface)
+ return;
+
+ AttachToOutputSurfaceOnImplThread(impl.mailbox_holder, source_surface);
+}
+
+void ReflectorImpl::OnSourceSurfaceReady(
+ BrowserCompositorOutputSurface* source_surface) {
+ ImplThreadData& impl = GetImpl();
+ AttachToOutputSurfaceOnImplThread(impl.mailbox_holder, source_surface);
+}
+
+void ReflectorImpl::Shutdown() {
+ MainThreadData& main = GetMain();
+ main.mailbox = NULL;
+ main.mirroring_layer->SetShowPaintedContent();
+ main.mirroring_layer = NULL;
+ impl_message_loop_->PostTask(
+ FROM_HERE, base::Bind(&ReflectorImpl::ShutdownOnImplThread, this));
+}
+
+void ReflectorImpl::DetachFromOutputSurface() {
+ ImplThreadData& impl = GetImpl();
+ DCHECK(impl.output_surface);
+ impl.output_surface->SetReflector(NULL);
+ DCHECK(impl.texture_id);
+ impl.gl_helper->DeleteTexture(impl.texture_id);
+ impl.texture_id = 0;
+ impl.gl_helper.reset();
+ impl.output_surface = NULL;
+}
+
+void ReflectorImpl::ShutdownOnImplThread() {
+ ImplThreadData& impl = GetImpl();
+ if (impl.output_surface)
+ DetachFromOutputSurface();
+ impl.output_surface_map = NULL;
+ // The instance must be deleted on main thread.
+ main_message_loop_->PostTask(FROM_HERE,
+ base::Bind(&ReflectorImpl::DeleteOnMainThread,
+ scoped_refptr<ReflectorImpl>(this)));
+}
+
+void ReflectorImpl::ReattachToOutputSurfaceFromMainThread(
+ BrowserCompositorOutputSurface* output_surface) {
+ MainThreadData& main = GetMain();
+ GLHelper* helper = ImageTransportFactory::GetInstance()->GetGLHelper();
+ main.mailbox = new OwnedMailbox(helper);
+ main.needs_set_mailbox = true;
+ main.mirroring_layer->SetShowPaintedContent();
+ impl_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&ReflectorImpl::AttachToOutputSurfaceOnImplThread,
+ this,
+ main.mailbox->holder(),
+ output_surface));
+}
+
+void ReflectorImpl::OnMirroringCompositorResized() {
+ MainThreadData& main = GetMain();
+ main.mirroring_layer->SchedulePaint(main.mirroring_layer->bounds());
+}
+
+void ReflectorImpl::OnSwapBuffers() {
+ ImplThreadData& impl = GetImpl();
+ gfx::Size size = impl.output_surface->SurfaceSize();
+ if (impl.texture_id) {
+ impl.gl_helper->CopyTextureFullImage(impl.texture_id, size);
+ impl.gl_helper->Flush();
+ }
+ main_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &ReflectorImpl::FullRedrawOnMainThread, this->AsWeakPtr(), size));
+}
+
+void ReflectorImpl::OnPostSubBuffer(gfx::Rect rect) {
+ ImplThreadData& impl = GetImpl();
+ if (impl.texture_id) {
+ impl.gl_helper->CopyTextureSubImage(impl.texture_id, rect);
+ impl.gl_helper->Flush();
+ }
+ main_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&ReflectorImpl::UpdateSubBufferOnMainThread,
+ this->AsWeakPtr(),
+ impl.output_surface->SurfaceSize(),
+ rect));
+}
+
+ReflectorImpl::~ReflectorImpl() {
+ // Make sure the reflector is deleted on main thread.
+ DCHECK_EQ(main_message_loop_.get(), base::MessageLoopProxy::current().get());
+}
+
+static void ReleaseMailbox(scoped_refptr<OwnedMailbox> mailbox,
+ unsigned int sync_point,
+ bool is_lost) {
+ mailbox->UpdateSyncPoint(sync_point);
+}
+
+void ReflectorImpl::AttachToOutputSurfaceOnImplThread(
+ const gpu::MailboxHolder& mailbox_holder,
+ BrowserCompositorOutputSurface* output_surface) {
+ ImplThreadData& impl = GetImpl();
+ if (output_surface == impl.output_surface)
+ return;
+ if (impl.output_surface)
+ DetachFromOutputSurface();
+ impl.output_surface = output_surface;
+ output_surface->context_provider()->BindToCurrentThread();
+ impl.gl_helper.reset(
+ new GLHelper(output_surface->context_provider()->ContextGL(),
+ output_surface->context_provider()->ContextSupport()));
+ impl.texture_id = impl.gl_helper->ConsumeMailboxToTexture(
+ mailbox_holder.mailbox, mailbox_holder.sync_point);
+ impl.gl_helper->ResizeTexture(impl.texture_id, output_surface->SurfaceSize());
+ impl.gl_helper->Flush();
+ output_surface->SetReflector(this);
+ // The texture doesn't have the data, so invokes full redraw now.
+ main_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&ReflectorImpl::FullRedrawContentOnMainThread,
+ scoped_refptr<ReflectorImpl>(this)));
+}
+
+void ReflectorImpl::UpdateTextureSizeOnMainThread(gfx::Size size) {
+ MainThreadData& main = GetMain();
+ if (!main.mirroring_layer || !main.mailbox ||
+ main.mailbox->mailbox().IsZero())
+ return;
+ if (main.needs_set_mailbox) {
+ main.mirroring_layer->SetTextureMailbox(
+ cc::TextureMailbox(main.mailbox->holder()),
+ cc::SingleReleaseCallback::Create(
+ base::Bind(ReleaseMailbox, main.mailbox)),
+ size);
+ main.needs_set_mailbox = false;
+ } else {
+ main.mirroring_layer->SetTextureSize(size);
+ }
+ main.mirroring_layer->SetBounds(gfx::Rect(size));
+}
+
+void ReflectorImpl::FullRedrawOnMainThread(gfx::Size size) {
+ MainThreadData& main = GetMain();
+ if (!main.mirroring_layer)
+ return;
+ UpdateTextureSizeOnMainThread(size);
+ main.mirroring_layer->SchedulePaint(main.mirroring_layer->bounds());
+}
+
+void ReflectorImpl::UpdateSubBufferOnMainThread(gfx::Size size,
+ gfx::Rect rect) {
+ MainThreadData& main = GetMain();
+ if (!main.mirroring_layer)
+ return;
+ UpdateTextureSizeOnMainThread(size);
+ // Flip the coordinates to compositor's one.
+ int y = size.height() - rect.y() - rect.height();
+ gfx::Rect new_rect(rect.x(), y, rect.width(), rect.height());
+ main.mirroring_layer->SchedulePaint(new_rect);
+}
+
+void ReflectorImpl::FullRedrawContentOnMainThread() {
+ MainThreadData& main = GetMain();
+ main.mirrored_compositor->ScheduleFullRedraw();
+}
+
+} // namespace content
diff --git a/chromium/content/browser/aura/reflector_impl.h b/chromium/content/browser/compositor/reflector_impl.h
index e5b5128accf..0a9af98ca61 100644
--- a/chromium/content/browser/aura/reflector_impl.h
+++ b/chromium/content/browser/compositor/reflector_impl.h
@@ -1,14 +1,16 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// 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_AURA_REFLECTOR_IMPL_H_
-#define CONTENT_BROWSER_AURA_REFLECTOR_IMPL_H_
+#ifndef CONTENT_BROWSER_COMPOSITOR_REFLECTOR_IMPL_H_
+#define CONTENT_BROWSER_COMPOSITOR_REFLECTOR_IMPL_H_
#include "base/id_map.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
-#include "content/browser/aura/image_transport_factory.h"
+#include "base/synchronization/lock.h"
+#include "content/browser/compositor/image_transport_factory.h"
+#include "gpu/command_buffer/common/mailbox_holder.h"
#include "ui/compositor/reflector.h"
#include "ui/gfx/size.h"
@@ -23,42 +25,36 @@ class Layer;
namespace content {
+class OwnedMailbox;
class BrowserCompositorOutputSurface;
// A reflector implementation that copies the framebuffer content
// to the texture, then draw it onto the mirroring compositor.
-class ReflectorImpl : public ImageTransportFactoryObserver,
- public base::SupportsWeakPtr<ReflectorImpl>,
+class ReflectorImpl : public base::SupportsWeakPtr<ReflectorImpl>,
public ui::Reflector {
public:
ReflectorImpl(
ui::Compositor* mirrored_compositor,
ui::Layer* mirroring_layer,
IDMap<BrowserCompositorOutputSurface>* output_surface_map,
+ base::MessageLoopProxy* compositor_thread_loop,
int surface_id);
ui::Compositor* mirrored_compositor() {
- return mirrored_compositor_;
+ return GetMain().mirrored_compositor;
}
- void InitOnImplThread();
+ void InitOnImplThread(const gpu::MailboxHolder& mailbox_holder);
void Shutdown();
void ShutdownOnImplThread();
- // This must be called on ImplThread, or before the surface is passed to
- // ImplThread.
- void AttachToOutputSurface(BrowserCompositorOutputSurface* surface);
+ // Post a task to attach the reflector to the output surface onto ImplThread.
+ void ReattachToOutputSurfaceFromMainThread(
+ BrowserCompositorOutputSurface* surface);
// ui::Reflector implementation.
virtual void OnMirroringCompositorResized() OVERRIDE;
- // ImageTransportFactoryObsever implementation.
- virtual void OnLostResources() OVERRIDE;
-
- // Called when the output surface's size has changed.
- // This must be called on ImplThread.
- void OnReshape(gfx::Size size);
-
// Called in |BrowserCompositorOutputSurface::SwapBuffers| to copy
// the full screen image to the |texture_id_|. This must be called
// on ImplThread.
@@ -70,18 +66,45 @@ class ReflectorImpl : public ImageTransportFactoryObserver,
void OnPostSubBuffer(gfx::Rect rect);
// Create a shared texture that will be used to copy the content of
- // mirrored compositor to the mirroring compositor. This must be
- // called before the reflector is attached to OutputSurface to avoid
- // race with ImplThread accessing |texture_id_|.
- void CreateSharedTexture();
+ // mirrored compositor to the mirroring compositor. This should
+ // be posted to the main thread when the output is attached in
+ // impl thread.
+ void CreateSharedTextureOnMainThread(gfx::Size size);
// Called when the source surface is bound and available. This must
// be called on ImplThread.
- void OnSourceSurfaceReady(int surface_id);
+ void OnSourceSurfaceReady(BrowserCompositorOutputSurface* surface);
+
+ void DetachFromOutputSurface();
private:
+ struct MainThreadData {
+ MainThreadData(ui::Compositor* mirrored_compositor,
+ ui::Layer* mirroring_layer);
+ ~MainThreadData();
+ scoped_refptr<OwnedMailbox> mailbox;
+ bool needs_set_mailbox;
+ ui::Compositor* mirrored_compositor;
+ ui::Layer* mirroring_layer;
+ };
+
+ struct ImplThreadData {
+ explicit ImplThreadData(
+ IDMap<BrowserCompositorOutputSurface>* output_surface_map);
+ ~ImplThreadData();
+ IDMap<BrowserCompositorOutputSurface>* output_surface_map;
+ BrowserCompositorOutputSurface* output_surface;
+ scoped_ptr<GLHelper> gl_helper;
+ unsigned texture_id;
+ gpu::MailboxHolder mailbox_holder;
+ };
+
virtual ~ReflectorImpl();
+ void AttachToOutputSurfaceOnImplThread(
+ const gpu::MailboxHolder& mailbox_holder,
+ BrowserCompositorOutputSurface* surface);
+
void UpdateTextureSizeOnMainThread(gfx::Size size);
// Request full redraw on mirroring compositor.
@@ -97,24 +120,16 @@ class ReflectorImpl : public ImageTransportFactoryObserver,
// so the ReflectorImpl gets deleted when the function returns.
static void DeleteOnMainThread(scoped_refptr<ReflectorImpl> reflector) {}
- // These variables are initialized on MainThread before
- // the reflector is attached to the output surface. Once
- // attached, they must be accessed only on ImplThraed unless
- // the context is lost. When the context is lost, these
- // will be re-ininitiailzed when the new output-surface
- // is created on MainThread.
- int texture_id_;
- gfx::Size texture_size_;
-
- // Must be accessed only on ImplThread.
- IDMap<BrowserCompositorOutputSurface>* output_surface_map_;
- scoped_ptr<GLHelper> gl_helper_;
-
- // Must be accessed only on MainThread.
- ui::Compositor* mirrored_compositor_;
- ui::Compositor* mirroring_compositor_;
- ui::Layer* mirroring_layer_;
- scoped_refptr<ui::Texture> shared_texture_;
+ MainThreadData& GetMain();
+ ImplThreadData& GetImpl();
+
+ // Must be accessed only on ImplThread, through GetImpl().
+ ImplThreadData impl_unsafe_;
+
+ // Must be accessed only on MainThread, through GetMain().
+ MainThreadData main_unsafe_;
+
+ // Can be accessed on both.
scoped_refptr<base::MessageLoopProxy> impl_message_loop_;
scoped_refptr<base::MessageLoopProxy> main_message_loop_;
int surface_id_;
@@ -122,4 +137,4 @@ class ReflectorImpl : public ImageTransportFactoryObserver,
} // namespace content
-#endif // CONTENT_BROWSER_AURA_REFLECTOR_IMPL_H_
+#endif // CONTENT_BROWSER_COMPOSITOR_REFLECTOR_IMPL_H_
diff --git a/chromium/content/browser/aura/resize_lock.cc b/chromium/content/browser/compositor/resize_lock.cc
index 4c8cd95a67f..ddbc4437872 100644
--- a/chromium/content/browser/aura/resize_lock.cc
+++ b/chromium/content/browser/compositor/resize_lock.cc
@@ -1,8 +1,8 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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/aura/resize_lock.h"
+#include "content/browser/compositor/resize_lock.h"
namespace content {
diff --git a/chromium/content/browser/aura/resize_lock.h b/chromium/content/browser/compositor/resize_lock.h
index cff98224c7f..07acff33cf1 100644
--- a/chromium/content/browser/aura/resize_lock.h
+++ b/chromium/content/browser/compositor/resize_lock.h
@@ -1,9 +1,9 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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_AURA_RESIZE_LOCK_H_
-#define CONTENT_BROWSER_AURA_RESIZE_LOCK_H_
+#ifndef CONTENT_BROWSER_COMPOSITOR_RESIZE_LOCK_H_
+#define CONTENT_BROWSER_COMPOSITOR_RESIZE_LOCK_H_
#include "base/basictypes.h"
#include "content/common/content_export.h"
@@ -34,4 +34,4 @@ class CONTENT_EXPORT ResizeLock {
} // namespace content
-#endif // CONTENT_BROWSER_AURA_RESIZE_LOCK_H_
+#endif // CONTENT_BROWSER_COMPOSITOR_RESIZE_LOCK_H_
diff --git a/chromium/content/browser/aura/software_browser_compositor_output_surface.cc b/chromium/content/browser/compositor/software_browser_compositor_output_surface.cc
index 070a6365d9f..369fe930dbd 100644
--- a/chromium/content/browser/aura/software_browser_compositor_output_surface.cc
+++ b/chromium/content/browser/compositor/software_browser_compositor_output_surface.cc
@@ -1,15 +1,15 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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/aura/software_browser_compositor_output_surface.h"
+#include "content/browser/compositor/software_browser_compositor_output_surface.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop.h"
#include "base/time/time.h"
#include "cc/output/compositor_frame.h"
#include "cc/output/software_output_device.h"
-#include "content/browser/aura/browser_compositor_output_surface_proxy.h"
+#include "content/browser/compositor/browser_compositor_output_surface_proxy.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "ui/events/latency_info.h"
#include "ui/gfx/vsync_provider.h"
@@ -21,13 +21,11 @@ SoftwareBrowserCompositorOutputSurface::SoftwareBrowserCompositorOutputSurface(
scoped_ptr<cc::SoftwareOutputDevice> software_device,
int surface_id,
IDMap<BrowserCompositorOutputSurface>* output_surface_map,
- base::MessageLoopProxy* compositor_message_loop,
- base::WeakPtr<ui::Compositor> compositor)
+ const scoped_refptr<ui::CompositorVSyncManager>& vsync_manager)
: BrowserCompositorOutputSurface(software_device.Pass(),
surface_id,
output_surface_map,
- compositor_message_loop,
- compositor),
+ vsync_manager),
output_surface_proxy_(surface_proxy) {}
SoftwareBrowserCompositorOutputSurface::
@@ -35,15 +33,15 @@ SoftwareBrowserCompositorOutputSurface::
void SoftwareBrowserCompositorOutputSurface::SwapBuffers(
cc::CompositorFrame* frame) {
- ui::LatencyInfo latency_info = frame->metadata.latency_info;
- latency_info.AddLatencyNumber(
- ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0);
-
+ for (size_t i = 0; i < frame->metadata.latency_info.size(); i++) {
+ frame->metadata.latency_info[i].AddLatencyNumber(
+ ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0);
+ }
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(
&RenderWidgetHostImpl::CompositorFrameDrawn,
- latency_info));
+ frame->metadata.latency_info));
gfx::VSyncProvider* vsync_provider = software_device()->GetVSyncProvider();
if (vsync_provider) {
diff --git a/chromium/content/browser/aura/software_browser_compositor_output_surface.h b/chromium/content/browser/compositor/software_browser_compositor_output_surface.h
index 79c7901f055..788d9daa9e6 100644
--- a/chromium/content/browser/aura/software_browser_compositor_output_surface.h
+++ b/chromium/content/browser/compositor/software_browser_compositor_output_surface.h
@@ -1,23 +1,22 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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_AURA_SOFTWARE_BROWSER_COMPOSITOR_OUTPUT_SURFACE_H_
-#define CONTENT_BROWSER_AURA_SOFTWARE_BROWSER_COMPOSITOR_OUTPUT_SURFACE_H_
+#ifndef CONTENT_BROWSER_COMPOSITOR_SOFTWARE_BROWSER_COMPOSITOR_OUTPUT_SURFACE_H_
+#define CONTENT_BROWSER_COMPOSITOR_SOFTWARE_BROWSER_COMPOSITOR_OUTPUT_SURFACE_H_
#include "base/memory/weak_ptr.h"
-#include "content/browser/aura/browser_compositor_output_surface.h"
+#include "content/browser/compositor/browser_compositor_output_surface.h"
#include "content/common/content_export.h"
-#include "ui/compositor/compositor.h"
-
-namespace base {
-class MessageLoopProxy;
-}
namespace cc {
class SoftwareOutputDevice;
}
+namespace ui {
+class CompositorVSyncManager;
+}
+
namespace content {
class BrowserCompositorOutputSurfaceProxy;
@@ -30,8 +29,7 @@ class CONTENT_EXPORT SoftwareBrowserCompositorOutputSurface
scoped_ptr<cc::SoftwareOutputDevice> software_device,
int surface_id,
IDMap<BrowserCompositorOutputSurface>* output_surface_map,
- base::MessageLoopProxy* compositor_message_loop,
- base::WeakPtr<ui::Compositor> compositor);
+ const scoped_refptr<ui::CompositorVSyncManager>& vsync_manager);
virtual ~SoftwareBrowserCompositorOutputSurface();
@@ -47,4 +45,4 @@ class CONTENT_EXPORT SoftwareBrowserCompositorOutputSurface
} // namespace content
-#endif // CONTENT_BROWSER_AURA_SOFTWARE_BROWSER_COMPOSITOR_OUTPUT_SURFACE_H_
+#endif // CONTENT_BROWSER_COMPOSITOR_SOFTWARE_BROWSER_COMPOSITOR_OUTPUT_SURFACE_H_
diff --git a/chromium/content/browser/aura/software_browser_compositor_output_surface_unittest.cc b/chromium/content/browser/compositor/software_browser_compositor_output_surface_unittest.cc
index 1d05295cb81..79c3364d890 100644
--- a/chromium/content/browser/aura/software_browser_compositor_output_surface_unittest.cc
+++ b/chromium/content/browser/compositor/software_browser_compositor_output_surface_unittest.cc
@@ -1,15 +1,15 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/message_loop/message_loop.h"
#include "cc/output/compositor_frame.h"
-#include "content/browser/aura/browser_compositor_output_surface_proxy.h"
-#include "content/browser/aura/software_browser_compositor_output_surface.h"
+#include "content/browser/compositor/browser_compositor_output_surface_proxy.h"
+#include "content/browser/compositor/software_browser_compositor_output_surface.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/compositor/compositor.h"
#include "ui/compositor/test/context_factories_for_test.h"
#include "ui/gfx/vsync_provider.h"
-#include "ui/gl/gl_implementation.h"
namespace {
@@ -80,18 +80,21 @@ class SoftwareBrowserCompositorOutputSurfaceTest : public testing::Test {
SoftwareBrowserCompositorOutputSurfaceTest::
SoftwareBrowserCompositorOutputSurfaceTest() {
- CHECK(gfx::InitializeGLBindings(gfx::kGLImplementationOSMesaGL));
- message_loop_.reset(new base::MessageLoop(base::MessageLoop::TYPE_UI));
+ // |message_loop_| is not used, but the main thread still has to exist for the
+ // compositor to use.
+ message_loop_.reset(new base::MessageLoopForUI);
}
SoftwareBrowserCompositorOutputSurfaceTest::
~SoftwareBrowserCompositorOutputSurfaceTest() {}
void SoftwareBrowserCompositorOutputSurfaceTest::SetUp() {
- ui::InitializeContextFactoryForTests(false);
- ui::Compositor::Initialize();
+ bool enable_pixel_output = false;
+ ui::ContextFactory* context_factory =
+ ui::InitializeContextFactoryForTests(enable_pixel_output);
- compositor_.reset(new ui::Compositor(gfx::kNullAcceleratedWidget));
+ compositor_.reset(new ui::Compositor(gfx::kNullAcceleratedWidget,
+ context_factory));
surface_proxy_ =
new content::BrowserCompositorOutputSurfaceProxy(&surface_map_);
}
@@ -104,7 +107,6 @@ void SoftwareBrowserCompositorOutputSurfaceTest::TearDown() {
surface_map_.Clear();
ui::TerminateContextFactoryForTests();
- ui::Compositor::Terminate();
}
scoped_ptr<content::BrowserCompositorOutputSurface>
@@ -116,8 +118,7 @@ SoftwareBrowserCompositorOutputSurfaceTest::CreateSurface(
device.Pass(),
1,
&surface_map_,
- compositor_->GetCompositorMessageLoop(),
- compositor_->AsWeakPtr()));
+ compositor_->vsync_manager()));
}
TEST_F(SoftwareBrowserCompositorOutputSurfaceTest, NoVSyncProvider) {
diff --git a/chromium/content/browser/compositor/software_output_device_mac.h b/chromium/content/browser/compositor/software_output_device_mac.h
new file mode 100644
index 00000000000..8220a3bd14f
--- /dev/null
+++ b/chromium/content/browser/compositor/software_output_device_mac.h
@@ -0,0 +1,35 @@
+// 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_SOFTWARE_OUTPUT_DEVICE_MAC_H_
+#define CONTENT_BROWSER_COMPOSITOR_SOFTWARE_OUTPUT_DEVICE_MAC_H_
+
+#include "cc/output/software_output_device.h"
+
+namespace gfx {
+class Canvas;
+}
+
+namespace ui {
+class Compositor;
+}
+
+namespace content {
+
+class SoftwareOutputDeviceMac : public cc::SoftwareOutputDevice {
+ public:
+ explicit SoftwareOutputDeviceMac(ui::Compositor* compositor);
+ virtual ~SoftwareOutputDeviceMac();
+
+ virtual void EndPaint(cc::SoftwareFrameData* frame_data) OVERRIDE;
+
+ private:
+ ui::Compositor* compositor_;
+
+ DISALLOW_COPY_AND_ASSIGN(SoftwareOutputDeviceMac);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_COMPOSITOR_SOFTWARE_OUTPUT_DEVICE_MAC_H_
diff --git a/chromium/content/browser/compositor/software_output_device_mac.mm b/chromium/content/browser/compositor/software_output_device_mac.mm
new file mode 100644
index 00000000000..ab734078f73
--- /dev/null
+++ b/chromium/content/browser/compositor/software_output_device_mac.mm
@@ -0,0 +1,30 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <Cocoa/Cocoa.h>
+
+#include "content/browser/compositor/software_output_device_mac.h"
+
+#include "content/browser/compositor/browser_compositor_view_mac.h"
+#include "ui/compositor/compositor.h"
+
+namespace content {
+
+SoftwareOutputDeviceMac::SoftwareOutputDeviceMac(ui::Compositor* compositor)
+ : compositor_(compositor) {
+}
+
+SoftwareOutputDeviceMac::~SoftwareOutputDeviceMac() {
+}
+
+void SoftwareOutputDeviceMac::EndPaint(cc::SoftwareFrameData* frame_data) {
+ SoftwareOutputDevice::EndPaint(frame_data);
+
+ NSView* view = compositor_->widget();
+ [view gotSoftwareFrame:frame_data
+ withScaleFactor:scale_factor_
+ withCanvas:canvas_.get()];
+}
+
+} // namespace content
diff --git a/chromium/content/browser/compositor/software_output_device_ozone.cc b/chromium/content/browser/compositor/software_output_device_ozone.cc
new file mode 100644
index 00000000000..b97b8b746a2
--- /dev/null
+++ b/chromium/content/browser/compositor/software_output_device_ozone.cc
@@ -0,0 +1,60 @@
+// 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/software_output_device_ozone.h"
+#include "third_party/skia/include/core/SkDevice.h"
+#include "ui/compositor/compositor.h"
+#include "ui/gfx/skia_util.h"
+#include "ui/gfx/vsync_provider.h"
+#include "ui/ozone/public/surface_factory_ozone.h"
+#include "ui/ozone/public/surface_ozone_canvas.h"
+
+namespace content {
+
+SoftwareOutputDeviceOzone::SoftwareOutputDeviceOzone(ui::Compositor* compositor)
+ : compositor_(compositor) {
+ ui::SurfaceFactoryOzone* factory = ui::SurfaceFactoryOzone::GetInstance();
+
+ if (factory->InitializeHardware() != ui::SurfaceFactoryOzone::INITIALIZED)
+ LOG(FATAL) << "Failed to initialize hardware in OZONE";
+
+ surface_ozone_ = factory->CreateCanvasForWidget(compositor_->widget());
+
+ if (!surface_ozone_)
+ LOG(FATAL) << "Failed to initialize canvas";
+
+ vsync_provider_ = surface_ozone_->CreateVSyncProvider();
+}
+
+SoftwareOutputDeviceOzone::~SoftwareOutputDeviceOzone() {
+}
+
+void SoftwareOutputDeviceOzone::Resize(const gfx::Size& viewport_pixel_size,
+ float scale_factor) {
+ scale_factor_ = scale_factor;
+
+ if (viewport_pixel_size_ == viewport_pixel_size)
+ return;
+
+ viewport_pixel_size_ = viewport_pixel_size;
+
+ surface_ozone_->ResizeCanvas(viewport_pixel_size_);
+}
+
+SkCanvas* SoftwareOutputDeviceOzone::BeginPaint(const gfx::Rect& damage_rect) {
+ DCHECK(gfx::Rect(viewport_pixel_size_).Contains(damage_rect));
+
+ // Get canvas for next frame.
+ canvas_ = surface_ozone_->GetCanvas();
+
+ return SoftwareOutputDevice::BeginPaint(damage_rect);
+}
+
+void SoftwareOutputDeviceOzone::EndPaint(cc::SoftwareFrameData* frame_data) {
+ SoftwareOutputDevice::EndPaint(frame_data);
+
+ surface_ozone_->PresentCanvas(damage_rect_);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/aura/software_output_device_ozone.h b/chromium/content/browser/compositor/software_output_device_ozone.h
index 7ebf430f323..600138ff96e 100644
--- a/chromium/content/browser/aura/software_output_device_ozone.h
+++ b/chromium/content/browser/compositor/software_output_device_ozone.h
@@ -1,9 +1,9 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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_AURA_SOFTWARE_OUTPUT_DEVICE_OZONE_H_
-#define CONTENT_BROWSER_AURA_SOFTWARE_OUTPUT_DEVICE_OZONE_H_
+#ifndef CONTENT_BROWSER_COMPOSITOR_SOFTWARE_OUTPUT_DEVICE_OZONE_H_
+#define CONTENT_BROWSER_COMPOSITOR_SOFTWARE_OUTPUT_DEVICE_OZONE_H_
#include "cc/output/software_output_device.h"
#include "content/common/content_export.h"
@@ -11,6 +11,7 @@
namespace ui {
class Compositor;
+class SurfaceOzoneCanvas;
}
namespace content {
@@ -24,18 +25,19 @@ class CONTENT_EXPORT SoftwareOutputDeviceOzone
explicit SoftwareOutputDeviceOzone(ui::Compositor* compositor);
virtual ~SoftwareOutputDeviceOzone();
- virtual void Resize(gfx::Size viewport_size) OVERRIDE;
- virtual SkCanvas* BeginPaint(gfx::Rect damage_rect) OVERRIDE;
+ virtual void Resize(const gfx::Size& viewport_pixel_size,
+ float scale_factor) OVERRIDE;
+ virtual SkCanvas* BeginPaint(const gfx::Rect& damage_rect) OVERRIDE;
virtual void EndPaint(cc::SoftwareFrameData* frame_data) OVERRIDE;
private:
ui::Compositor* compositor_;
- gfx::AcceleratedWidget realized_widget_;
+ scoped_ptr<ui::SurfaceOzoneCanvas> surface_ozone_;
DISALLOW_COPY_AND_ASSIGN(SoftwareOutputDeviceOzone);
};
} // namespace content
-#endif // CONTENT_BROWSER_AURA_SOFTWARE_OUTPUT_DEVICE_OZONE_H_
+#endif // CONTENT_BROWSER_COMPOSITOR_SOFTWARE_OUTPUT_DEVICE_OZONE_H_
diff --git a/chromium/content/browser/aura/software_output_device_ozone_unittest.cc b/chromium/content/browser/compositor/software_output_device_ozone_unittest.cc
index 6469995f6ec..a2176a6ffc0 100644
--- a/chromium/content/browser/aura/software_output_device_ozone_unittest.cc
+++ b/chromium/content/browser/compositor/software_output_device_ozone_unittest.cc
@@ -1,23 +1,49 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "cc/output/software_frame_data.h"
-#include "content/browser/aura/software_output_device_ozone.h"
+#include "content/browser/compositor/software_output_device_ozone.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/skia/include/core/SkBitmapDevice.h"
+#include "third_party/skia/include/core/SkSurface.h"
#include "ui/compositor/compositor.h"
#include "ui/compositor/test/context_factories_for_test.h"
-#include "ui/gfx/ozone/surface_factory_ozone.h"
#include "ui/gfx/size.h"
#include "ui/gfx/skia_util.h"
+#include "ui/gfx/vsync_provider.h"
#include "ui/gl/gl_implementation.h"
+#include "ui/ozone/public/surface_factory_ozone.h"
+#include "ui/ozone/public/surface_ozone_canvas.h"
namespace {
-class MockSurfaceFactoryOzone : public gfx::SurfaceFactoryOzone {
+class MockSurfaceOzone : public ui::SurfaceOzoneCanvas {
+ public:
+ MockSurfaceOzone() {}
+ virtual ~MockSurfaceOzone() {}
+
+ // ui::SurfaceOzoneCanvas overrides:
+ virtual void ResizeCanvas(const gfx::Size& size) OVERRIDE {
+ surface_ = skia::AdoptRef(SkSurface::NewRaster(
+ SkImageInfo::MakeN32Premul(size.width(), size.height())));
+ }
+ virtual skia::RefPtr<SkCanvas> GetCanvas() OVERRIDE {
+ return skia::SharePtr(surface_->getCanvas());
+ }
+ virtual void PresentCanvas(const gfx::Rect& damage) OVERRIDE {}
+ virtual scoped_ptr<gfx::VSyncProvider> CreateVSyncProvider() OVERRIDE {
+ return scoped_ptr<gfx::VSyncProvider>();
+ }
+
+ private:
+ skia::RefPtr<SkSurface> surface_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockSurfaceOzone);
+};
+
+class MockSurfaceFactoryOzone : public ui::SurfaceFactoryOzone {
public:
MockSurfaceFactoryOzone() {}
virtual ~MockSurfaceFactoryOzone() {}
@@ -28,33 +54,17 @@ class MockSurfaceFactoryOzone : public gfx::SurfaceFactoryOzone {
virtual void ShutdownHardware() OVERRIDE {}
virtual gfx::AcceleratedWidget GetAcceleratedWidget() OVERRIDE { return 1; }
- virtual gfx::AcceleratedWidget RealizeAcceleratedWidget(
- gfx::AcceleratedWidget w) OVERRIDE { return w; }
virtual bool LoadEGLGLES2Bindings(
AddGLLibraryCallback add_gl_library,
SetGLGetProcAddressProcCallback set_gl_get_proc_address) OVERRIDE {
return false;
}
- virtual bool AttemptToResizeAcceleratedWidget(
- gfx::AcceleratedWidget w, const gfx::Rect& bounds) OVERRIDE {
- device_ = skia::AdoptRef(new SkBitmapDevice(SkBitmap::kARGB_8888_Config,
- bounds.width(),
- bounds.height(),
- true));
- canvas_ = skia::AdoptRef(new SkCanvas(device_.get()));
- return true;
- }
- virtual SkCanvas* GetCanvasForWidget(gfx::AcceleratedWidget w) OVERRIDE {
- return canvas_.get();
+ virtual scoped_ptr<ui::SurfaceOzoneCanvas> CreateCanvasForWidget(
+ gfx::AcceleratedWidget widget) OVERRIDE {
+ return make_scoped_ptr<ui::SurfaceOzoneCanvas>(new MockSurfaceOzone());
}
- virtual gfx::VSyncProvider* GetVSyncProvider(
- gfx::AcceleratedWidget w) OVERRIDE {
- return NULL;
- }
- private:
- skia::RefPtr<SkBitmapDevice> device_;
- skia::RefPtr<SkCanvas> canvas_;
+ private:
DISALLOW_COPY_AND_ASSIGN(MockSurfaceFactoryOzone);
};
@@ -70,38 +80,39 @@ class SoftwareOutputDeviceOzoneTest : public testing::Test {
protected:
scoped_ptr<content::SoftwareOutputDeviceOzone> output_device_;
+ bool enable_pixel_output_;
private:
scoped_ptr<ui::Compositor> compositor_;
scoped_ptr<base::MessageLoop> message_loop_;
- scoped_ptr<gfx::SurfaceFactoryOzone> surface_factory_;
+ scoped_ptr<ui::SurfaceFactoryOzone> surface_factory_;
DISALLOW_COPY_AND_ASSIGN(SoftwareOutputDeviceOzoneTest);
};
-SoftwareOutputDeviceOzoneTest::SoftwareOutputDeviceOzoneTest() {
- CHECK(gfx::InitializeGLBindings(gfx::kGLImplementationOSMesaGL));
- message_loop_.reset(new base::MessageLoop(base::MessageLoop::TYPE_UI));
+SoftwareOutputDeviceOzoneTest::SoftwareOutputDeviceOzoneTest()
+ : enable_pixel_output_(false) {
+ message_loop_.reset(new base::MessageLoopForUI);
}
SoftwareOutputDeviceOzoneTest::~SoftwareOutputDeviceOzoneTest() {
}
void SoftwareOutputDeviceOzoneTest::SetUp() {
- ui::InitializeContextFactoryForTests(false);
- ui::Compositor::Initialize();
+ ui::ContextFactory* context_factory =
+ ui::InitializeContextFactoryForTests(enable_pixel_output_);
surface_factory_.reset(new MockSurfaceFactoryOzone());
- gfx::SurfaceFactoryOzone::SetInstance(surface_factory_.get());
const gfx::Size size(500, 400);
compositor_.reset(new ui::Compositor(
- gfx::SurfaceFactoryOzone::GetInstance()->GetAcceleratedWidget()));
+ ui::SurfaceFactoryOzone::GetInstance()->GetAcceleratedWidget(),
+ context_factory));
compositor_->SetScaleAndSize(1.0f, size);
output_device_.reset(new content::SoftwareOutputDeviceOzone(
compositor_.get()));
- output_device_->Resize(size);
+ output_device_->Resize(size, 1.f);
}
void SoftwareOutputDeviceOzoneTest::TearDown() {
@@ -109,39 +120,24 @@ void SoftwareOutputDeviceOzoneTest::TearDown() {
compositor_.reset();
surface_factory_.reset();
ui::TerminateContextFactoryForTests();
- ui::Compositor::Terminate();
}
-TEST_F(SoftwareOutputDeviceOzoneTest, CheckClipAfterBeginPaint) {
- gfx::Rect damage(10, 10, 100, 100);
- SkCanvas* canvas = output_device_->BeginPaint(damage);
-
- SkIRect sk_bounds;
- canvas->getClipDeviceBounds(&sk_bounds);
-
- EXPECT_EQ(damage.ToString(), gfx::SkIRectToRect(sk_bounds).ToString());
-}
-
-TEST_F(SoftwareOutputDeviceOzoneTest, CheckClipAfterSecondBeginPaint) {
- gfx::Rect damage(10, 10, 100, 100);
- SkCanvas* canvas = output_device_->BeginPaint(damage);
-
- cc::SoftwareFrameData frame;
- output_device_->EndPaint(&frame);
-
- damage = gfx::Rect(100, 100, 100, 100);
- canvas = output_device_->BeginPaint(damage);
- SkIRect sk_bounds;
- canvas->getClipDeviceBounds(&sk_bounds);
+class SoftwareOutputDeviceOzonePixelTest
+ : public SoftwareOutputDeviceOzoneTest {
+ protected:
+ virtual void SetUp() OVERRIDE;
+};
- EXPECT_EQ(damage.ToString(), gfx::SkIRectToRect(sk_bounds).ToString());
+void SoftwareOutputDeviceOzonePixelTest::SetUp() {
+ enable_pixel_output_ = true;
+ SoftwareOutputDeviceOzoneTest::SetUp();
}
TEST_F(SoftwareOutputDeviceOzoneTest, CheckCorrectResizeBehavior) {
gfx::Rect damage(0, 0, 100, 100);
gfx::Size size(200, 100);
// Reduce size.
- output_device_->Resize(size);
+ output_device_->Resize(size, 1.f);
SkCanvas* canvas = output_device_->BeginPaint(damage);
gfx::Size canvas_size(canvas->getDeviceSize().width(),
@@ -150,7 +146,7 @@ TEST_F(SoftwareOutputDeviceOzoneTest, CheckCorrectResizeBehavior) {
size.SetSize(1000, 500);
// Increase size.
- output_device_->Resize(size);
+ output_device_->Resize(size, 1.f);
canvas = output_device_->BeginPaint(damage);
canvas_size.SetSize(canvas->getDeviceSize().width(),
@@ -159,9 +155,11 @@ TEST_F(SoftwareOutputDeviceOzoneTest, CheckCorrectResizeBehavior) {
}
-TEST_F(SoftwareOutputDeviceOzoneTest, CheckCopyToBitmap) {
- const gfx::Rect area(6, 4);
- output_device_->Resize(area.size());
+TEST_F(SoftwareOutputDeviceOzonePixelTest, CheckCopyToBitmap) {
+ const int width = 6;
+ const int height = 4;
+ const gfx::Rect area(width, height);
+ output_device_->Resize(area.size(), 1.f);
SkCanvas* canvas = output_device_->BeginPaint(area);
// Clear the background to black.
@@ -173,23 +171,25 @@ TEST_F(SoftwareOutputDeviceOzoneTest, CheckCopyToBitmap) {
// Draw a white rectangle.
gfx::Rect damage(area.width() / 2, area.height() / 2);
canvas = output_device_->BeginPaint(damage);
+ canvas->clipRect(gfx::RectToSkRect(damage), SkRegion::kReplace_Op);
canvas->drawColor(SK_ColorWHITE);
output_device_->EndPaint(&frame);
- SkBitmap bitmap;
- output_device_->CopyToBitmap(area, &bitmap);
+ SkPMColor pixels[width * height];
+ output_device_->CopyToPixels(area, pixels);
- SkAutoLockPixels pixel_lock(bitmap);
// Check that the copied bitmap contains the same pixel values as what we
// painted.
+ const SkPMColor white = SkPreMultiplyColor(SK_ColorWHITE);
+ const SkPMColor black = SkPreMultiplyColor(SK_ColorBLACK);
for (int i = 0; i < area.height(); ++i) {
for (int j = 0; j < area.width(); ++j) {
if (j < damage.width() && i < damage.height())
- EXPECT_EQ(SK_ColorWHITE, bitmap.getColor(j, i));
+ EXPECT_EQ(white, pixels[i * area.width() + j]);
else
- EXPECT_EQ(SK_ColorBLACK, bitmap.getColor(j, i));
+ EXPECT_EQ(black, pixels[i * area.width() + j]);
}
}
}
diff --git a/chromium/content/browser/aura/software_output_device_win.cc b/chromium/content/browser/compositor/software_output_device_win.cc
index f8d6d9b6d70..1a6b5f64b40 100644
--- a/chromium/content/browser/aura/software_output_device_win.cc
+++ b/chromium/content/browser/compositor/software_output_device_win.cc
@@ -1,8 +1,8 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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/aura/software_output_device_win.h"
+#include "content/browser/compositor/software_output_device_win.h"
#include "content/public/browser/browser_thread.h"
#include "third_party/skia/include/core/SkBitmap.h"
@@ -29,20 +29,24 @@ SoftwareOutputDeviceWin::~SoftwareOutputDeviceWin() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
}
-void SoftwareOutputDeviceWin::Resize(gfx::Size viewport_size) {
+void SoftwareOutputDeviceWin::Resize(const gfx::Size& viewport_pixel_size,
+ float scale_factor) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- if (viewport_size_ == viewport_size)
+ scale_factor_ = scale_factor;
+
+ if (viewport_pixel_size_ == viewport_pixel_size)
return;
- viewport_size_ = viewport_size;
- contents_.reset(new gfx::Canvas(viewport_size, 1.0f, true));
+ viewport_pixel_size_ = viewport_pixel_size;
+ contents_.reset(new gfx::Canvas(viewport_pixel_size, 1.0f, true));
memset(&bitmap_info_, 0, sizeof(bitmap_info_));
- gfx::CreateBitmapHeader(viewport_size_.width(), viewport_size_.height(),
+ gfx::CreateBitmapHeader(viewport_pixel_size_.width(),
+ viewport_pixel_size_.height(),
&bitmap_info_.bmiHeader);
}
-SkCanvas* SoftwareOutputDeviceWin::BeginPaint(gfx::Rect damage_rect) {
+SkCanvas* SoftwareOutputDeviceWin::BeginPaint(const gfx::Rect& damage_rect) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(contents_);
@@ -61,7 +65,7 @@ void SoftwareOutputDeviceWin::EndPaint(cc::SoftwareFrameData* frame_data) {
SoftwareOutputDevice::EndPaint(frame_data);
gfx::Rect rect = damage_rect_;
- rect.Intersect(gfx::Rect(viewport_size_));
+ rect.Intersect(gfx::Rect(viewport_pixel_size_));
if (rect.IsEmpty())
return;
@@ -92,12 +96,12 @@ void SoftwareOutputDeviceWin::EndPaint(cc::SoftwareFrameData* frame_data) {
}
}
-void SoftwareOutputDeviceWin::CopyToBitmap(
- gfx::Rect rect, SkBitmap* output) {
+void SoftwareOutputDeviceWin::CopyToPixels(const gfx::Rect& rect,
+ void* pixels) {
DCHECK(contents_);
- SkBaseDevice* device = contents_->sk_canvas()->getDevice();
- const SkBitmap& bitmap = device->accessBitmap(false);
- bitmap.extractSubset(output, gfx::RectToSkIRect(rect));
+ SkImageInfo info = SkImageInfo::MakeN32Premul(rect.width(), rect.height());
+ contents_->sk_canvas()->readPixels(
+ info, pixels, info.minRowBytes(), rect.x(), rect.y());
}
} // namespace content
diff --git a/chromium/content/browser/aura/software_output_device_win.h b/chromium/content/browser/compositor/software_output_device_win.h
index 9c0655b6f67..3724f0217f5 100644
--- a/chromium/content/browser/aura/software_output_device_win.h
+++ b/chromium/content/browser/compositor/software_output_device_win.h
@@ -1,9 +1,9 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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_AURA_SOFTWARE_OUTPUT_DEVICE_WIN_H_
-#define CONTENT_BROWSER_AURA_SOFTWARE_OUTPUT_DEVICE_WIN_H_
+#ifndef CONTENT_BROWSER_COMPOSITOR_SOFTWARE_OUTPUT_DEVICE_WIN_H_
+#define CONTENT_BROWSER_COMPOSITOR_SOFTWARE_OUTPUT_DEVICE_WIN_H_
#include "base/memory/scoped_ptr.h"
#include "cc/output/software_output_device.h"
@@ -25,18 +25,21 @@ class SoftwareOutputDeviceWin : public cc::SoftwareOutputDevice {
explicit SoftwareOutputDeviceWin(ui::Compositor* compositor);
virtual ~SoftwareOutputDeviceWin();
- virtual void Resize(gfx::Size viewport_size) OVERRIDE;
- virtual SkCanvas* BeginPaint(gfx::Rect damage_rect) OVERRIDE;
+ virtual void Resize(const gfx::Size& viewport_pixel_size,
+ float scale_factor) OVERRIDE;
+ virtual SkCanvas* BeginPaint(const gfx::Rect& damage_rect) OVERRIDE;
virtual void EndPaint(cc::SoftwareFrameData* frame_data) OVERRIDE;
- virtual void CopyToBitmap(gfx::Rect rect, SkBitmap* output) OVERRIDE;
+ virtual void CopyToPixels(const gfx::Rect& rect, void* pixels) OVERRIDE;
private:
HWND hwnd_;
BITMAPINFO bitmap_info_;
scoped_ptr<gfx::Canvas> contents_;
bool is_hwnd_composited_;
+
+ DISALLOW_COPY_AND_ASSIGN(SoftwareOutputDeviceWin);
};
} // namespace content
-#endif // CONTENT_BROWSER_AURA_SOFTWARE_OUTPUT_DEVICE_WIN_H_
+#endif // CONTENT_BROWSER_COMPOSITOR_SOFTWARE_OUTPUT_DEVICE_WIN_H_
diff --git a/chromium/content/browser/compositor/software_output_device_x11.cc b/chromium/content/browser/compositor/software_output_device_x11.cc
new file mode 100644
index 00000000000..ebaefd7550e
--- /dev/null
+++ b/chromium/content/browser/compositor/software_output_device_x11.cc
@@ -0,0 +1,138 @@
+// 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/software_output_device_x11.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+#include "content/public/browser/browser_thread.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkDevice.h"
+#include "ui/base/x/x11_util.h"
+#include "ui/base/x/x11_util_internal.h"
+#include "ui/compositor/compositor.h"
+#include "ui/gfx/x/x11_types.h"
+
+namespace content {
+
+SoftwareOutputDeviceX11::SoftwareOutputDeviceX11(ui::Compositor* compositor)
+ : compositor_(compositor), display_(gfx::GetXDisplay()), gc_(NULL) {
+ // TODO(skaslev) Remove this when crbug.com/180702 is fixed.
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ gc_ = XCreateGC(display_, compositor_->widget(), 0, NULL);
+ if (!XGetWindowAttributes(display_, compositor_->widget(), &attributes_)) {
+ LOG(ERROR) << "XGetWindowAttributes failed for window "
+ << compositor_->widget();
+ return;
+ }
+}
+
+SoftwareOutputDeviceX11::~SoftwareOutputDeviceX11() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ XFreeGC(display_, gc_);
+}
+
+void SoftwareOutputDeviceX11::EndPaint(cc::SoftwareFrameData* frame_data) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(canvas_);
+ DCHECK(frame_data);
+
+ if (!canvas_)
+ return;
+
+ SoftwareOutputDevice::EndPaint(frame_data);
+
+ gfx::Rect rect = damage_rect_;
+ rect.Intersect(gfx::Rect(viewport_pixel_size_));
+ if (rect.IsEmpty())
+ return;
+
+ int bpp = gfx::BitsPerPixelForPixmapDepth(display_, attributes_.depth);
+
+ if (bpp != 32 && bpp != 16 && ui::QueryRenderSupport(display_)) {
+ // gfx::PutARGBImage only supports 16 and 32 bpp, but Xrender can do other
+ // conversions.
+ Pixmap pixmap = XCreatePixmap(
+ display_, compositor_->widget(), rect.width(), rect.height(), 32);
+ GC gc = XCreateGC(display_, pixmap, 0, NULL);
+ XImage image;
+ memset(&image, 0, sizeof(image));
+
+ SkImageInfo info;
+ size_t rowBytes;
+ const void* addr = canvas_->peekPixels(&info, &rowBytes);
+ image.width = viewport_pixel_size_.width();
+ image.height = viewport_pixel_size_.height();
+ image.depth = 32;
+ image.bits_per_pixel = 32;
+ image.format = ZPixmap;
+ image.byte_order = LSBFirst;
+ image.bitmap_unit = 8;
+ image.bitmap_bit_order = LSBFirst;
+ image.bytes_per_line = rowBytes;
+ image.red_mask = 0xff;
+ image.green_mask = 0xff00;
+ image.blue_mask = 0xff0000;
+ image.data = const_cast<char*>(static_cast<const char*>(addr));
+
+ XPutImage(display_,
+ pixmap,
+ gc,
+ &image,
+ rect.x(),
+ rect.y() /* source x, y */,
+ 0,
+ 0 /* dest x, y */,
+ rect.width(),
+ rect.height());
+ XFreeGC(display_, gc);
+ Picture picture = XRenderCreatePicture(
+ display_, pixmap, ui::GetRenderARGB32Format(display_), 0, NULL);
+ XRenderPictFormat* pictformat =
+ XRenderFindVisualFormat(display_, attributes_.visual);
+ Picture dest_picture = XRenderCreatePicture(
+ display_, compositor_->widget(), pictformat, 0, NULL);
+ XRenderComposite(display_,
+ PictOpSrc, // op
+ picture, // src
+ 0, // mask
+ dest_picture, // dest
+ 0, // src_x
+ 0, // src_y
+ 0, // mask_x
+ 0, // mask_y
+ rect.x(), // dest_x
+ rect.y(), // dest_y
+ rect.width(), // width
+ rect.height()); // height
+ XRenderFreePicture(display_, picture);
+ XRenderFreePicture(display_, dest_picture);
+ XFreePixmap(display_, pixmap);
+ return;
+ }
+
+ // TODO(jbauman): Switch to XShmPutImage since it's async.
+ SkImageInfo info;
+ size_t rowBytes;
+ const void* addr = canvas_->peekPixels(&info, &rowBytes);
+ gfx::PutARGBImage(display_,
+ attributes_.visual,
+ attributes_.depth,
+ compositor_->widget(),
+ gc_,
+ static_cast<const uint8*>(addr),
+ viewport_pixel_size_.width(),
+ viewport_pixel_size_.height(),
+ rect.x(),
+ rect.y(),
+ rect.x(),
+ rect.y(),
+ rect.width(),
+ rect.height());
+}
+
+} // namespace content
diff --git a/chromium/content/browser/aura/software_output_device_x11.h b/chromium/content/browser/compositor/software_output_device_x11.h
index 782afeee7ec..20ae6428e63 100644
--- a/chromium/content/browser/aura/software_output_device_x11.h
+++ b/chromium/content/browser/compositor/software_output_device_x11.h
@@ -1,9 +1,11 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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_AURA_SOFTWARE_OUTPUT_DEVICE_X11_H_
-#define CONTENT_BROWSER_AURA_SOFTWARE_OUTPUT_DEVICE_X11_H_
+#ifndef CONTENT_BROWSER_COMPOSITOR_SOFTWARE_OUTPUT_DEVICE_X11_H_
+#define CONTENT_BROWSER_COMPOSITOR_SOFTWARE_OUTPUT_DEVICE_X11_H_
+
+#include <X11/Xlib.h>
#include "cc/output/software_output_device.h"
#include "ui/gfx/x/x11_types.h"
@@ -20,19 +22,17 @@ class SoftwareOutputDeviceX11 : public cc::SoftwareOutputDevice {
virtual ~SoftwareOutputDeviceX11();
- virtual void Resize(gfx::Size viewport_size) OVERRIDE;
-
virtual void EndPaint(cc::SoftwareFrameData* frame_data) OVERRIDE;
private:
- void ClearImage();
-
ui::Compositor* compositor_;
XDisplay* display_;
GC gc_;
- XImage* image_;
+ XWindowAttributes attributes_;
+
+ DISALLOW_COPY_AND_ASSIGN(SoftwareOutputDeviceX11);
};
} // namespace content
-#endif // CONTENT_BROWSER_AURA_SOFTWARE_OUTPUT_DEVICE_X11_H_
+#endif // CONTENT_BROWSER_COMPOSITOR_SOFTWARE_OUTPUT_DEVICE_X11_H_
diff --git a/chromium/content/browser/compositor/surface_display_output_surface.cc b/chromium/content/browser/compositor/surface_display_output_surface.cc
new file mode 100644
index 00000000000..46c4ad75f95
--- /dev/null
+++ b/chromium/content/browser/compositor/surface_display_output_surface.cc
@@ -0,0 +1,49 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/compositor/surface_display_output_surface.h"
+
+#include "cc/output/compositor_frame.h"
+#include "cc/surfaces/display.h"
+#include "cc/surfaces/surface.h"
+#include "cc/surfaces/surface_manager.h"
+
+namespace content {
+
+SurfaceDisplayOutputSurface::SurfaceDisplayOutputSurface(
+ cc::Display* display,
+ cc::SurfaceManager* surface_manager,
+ const scoped_refptr<cc::ContextProvider>& context_provider)
+ : cc::OutputSurface(context_provider,
+ scoped_ptr<cc::SoftwareOutputDevice>()),
+ display_(display),
+ surface_manager_(surface_manager) {
+ capabilities_.delegated_rendering = true;
+ capabilities_.max_frames_pending = 1;
+}
+
+SurfaceDisplayOutputSurface::~SurfaceDisplayOutputSurface() {
+}
+
+void SurfaceDisplayOutputSurface::SwapBuffers(cc::CompositorFrame* frame) {
+ gfx::Size frame_size =
+ frame->delegated_frame_data->render_pass_list.back()->output_rect.size();
+ display_->Resize(frame_size);
+ cc::SurfaceId surface_id = display_->CurrentSurfaceId();
+ cc::Surface* surface = surface_manager_->GetSurfaceForId(surface_id);
+ if (!surface)
+ return;
+
+ scoped_ptr<cc::CompositorFrame> frame_copy(new cc::CompositorFrame());
+ frame->AssignTo(frame_copy.get());
+ surface->QueueFrame(frame_copy.Pass());
+
+ if (!display_->Draw())
+ return;
+
+ client_->DidSwapBuffers();
+ client_->DidSwapBuffersComplete();
+}
+
+} // namespace content
diff --git a/chromium/content/browser/compositor/surface_display_output_surface.h b/chromium/content/browser/compositor/surface_display_output_surface.h
new file mode 100644
index 00000000000..185e0ffc1d2
--- /dev/null
+++ b/chromium/content/browser/compositor/surface_display_output_surface.h
@@ -0,0 +1,41 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_COMPOSITOR_SURFACE_DISPLAY_OUTPUT_SURFACE_H_
+#define CONTENT_BROWSER_COMPOSITOR_SURFACE_DISPLAY_OUTPUT_SURFACE_H_
+
+#include "cc/output/output_surface.h"
+
+namespace cc {
+class Display;
+class SurfaceManager;
+}
+
+namespace content {
+
+// This class is maps a compositor OutputSurface to the surface system's Display
+// concept, allowing a compositor client to submit frames for a native root
+// window or physical display.
+class SurfaceDisplayOutputSurface : public cc::OutputSurface {
+ public:
+ // The underlying cc::Display and cc::SurfaceManager must outlive this class.
+ SurfaceDisplayOutputSurface(
+ cc::Display* display,
+ cc::SurfaceManager* surface_manager,
+ const scoped_refptr<cc::ContextProvider>& context_provider);
+ virtual ~SurfaceDisplayOutputSurface();
+
+ // cc::OutputSurface implementation.
+ virtual void SwapBuffers(cc::CompositorFrame* frame) OVERRIDE;
+
+ private:
+ cc::Display* display_;
+ cc::SurfaceManager* surface_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(SurfaceDisplayOutputSurface);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_COMPOSITOR_SURFACE_DISPLAY_OUTPUT_SURFACE_H_
diff --git a/chromium/content/browser/context_factory.cc b/chromium/content/browser/context_factory.cc
new file mode 100644
index 00000000000..b9dfacc0dec
--- /dev/null
+++ b/chromium/content/browser/context_factory.cc
@@ -0,0 +1,16 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/public/browser/context_factory.h"
+
+#include "content/browser/compositor/image_transport_factory.h"
+
+namespace content {
+
+ui::ContextFactory* GetContextFactory() {
+ DCHECK(ImageTransportFactory::GetInstance());
+ return ImageTransportFactory::GetInstance()->GetContextFactory();
+}
+
+} // namespace content
diff --git a/chromium/content/browser/cross_site_transfer_browsertest.cc b/chromium/content/browser/cross_site_transfer_browsertest.cc
new file mode 100644
index 00000000000..fff180d72c4
--- /dev/null
+++ b/chromium/content/browser/cross_site_transfer_browsertest.cc
@@ -0,0 +1,466 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/command_line.h"
+#include "base/strings/stringprintf.h"
+#include "content/browser/loader/resource_dispatcher_host_impl.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/resource_dispatcher_host_delegate.h"
+#include "content/public/browser/resource_throttle.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/content_browser_test.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "content/shell/browser/shell.h"
+#include "content/shell/browser/shell_content_browser_client.h"
+#include "content/shell/browser/shell_resource_dispatcher_host_delegate.h"
+#include "net/base/escape.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_status.h"
+#include "url/gurl.h"
+
+namespace content {
+
+// Tracks a single request for a specified URL, and allows waiting until the
+// request is destroyed, and then inspecting whether it completed successfully.
+class TrackingResourceDispatcherHostDelegate
+ : public ShellResourceDispatcherHostDelegate {
+ public:
+ TrackingResourceDispatcherHostDelegate() : throttle_created_(false) {
+ }
+
+ virtual void RequestBeginning(
+ net::URLRequest* request,
+ ResourceContext* resource_context,
+ appcache::AppCacheService* appcache_service,
+ ResourceType::Type resource_type,
+ int child_id,
+ int route_id,
+ ScopedVector<ResourceThrottle>* throttles) OVERRIDE {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ ShellResourceDispatcherHostDelegate::RequestBeginning(
+ request, resource_context, appcache_service, resource_type, child_id,
+ route_id, throttles);
+ // Expect only a single request for the tracked url.
+ ASSERT_FALSE(throttle_created_);
+ // If this is a request for the tracked URL, add a throttle to track it.
+ if (request->url() == tracked_url_)
+ throttles->push_back(new TrackingThrottle(request, this));
+ }
+
+ // Starts tracking a URL. The request for previously tracked URL, if any,
+ // must have been made and deleted before calling this function.
+ void SetTrackedURL(const GURL& tracked_url) {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ // Should not currently be tracking any URL.
+ ASSERT_FALSE(run_loop_);
+
+ // Create a RunLoop that will be stopped once the request for the tracked
+ // URL has been destroyed, to allow tracking the URL while also waiting for
+ // other events.
+ run_loop_.reset(new base::RunLoop());
+
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(
+ &TrackingResourceDispatcherHostDelegate::SetTrackedURLOnIOThread,
+ base::Unretained(this),
+ tracked_url));
+ }
+
+ // Waits until the tracked URL has been requests, and the request for it has
+ // been destroyed.
+ bool WaitForTrackedURLAndGetCompleted() {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ run_loop_->Run();
+ run_loop_.reset();
+ return tracked_request_completed_;
+ }
+
+ private:
+ // ResourceThrottle attached to request for the tracked URL. On destruction,
+ // passes the final URLRequestStatus back to the delegate.
+ class TrackingThrottle : public ResourceThrottle {
+ public:
+ TrackingThrottle(net::URLRequest* request,
+ TrackingResourceDispatcherHostDelegate* tracker)
+ : request_(request), tracker_(tracker) {
+ }
+
+ virtual ~TrackingThrottle() {
+ // If the request is deleted without being cancelled, its status will
+ // indicate it succeeded, so have to check if the request is still pending
+ // as well.
+ tracker_->OnTrackedRequestDestroyed(
+ !request_->is_pending() && request_->status().is_success());
+ }
+
+ // ResourceThrottle implementation:
+ virtual const char* GetNameForLogging() const OVERRIDE {
+ return "TrackingThrottle";
+ }
+
+ private:
+ net::URLRequest* request_;
+ TrackingResourceDispatcherHostDelegate* tracker_;
+
+ DISALLOW_COPY_AND_ASSIGN(TrackingThrottle);
+ };
+
+ void SetTrackedURLOnIOThread(const GURL& tracked_url) {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ throttle_created_ = false;
+ tracked_url_ = tracked_url;
+ }
+
+ void OnTrackedRequestDestroyed(bool completed) {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ tracked_request_completed_ = completed;
+ tracked_url_ = GURL();
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE, run_loop_->QuitClosure());
+ }
+
+ // These live on the IO thread.
+ GURL tracked_url_;
+ bool throttle_created_;
+
+ // This is created and destroyed on the UI thread, but stopped on the IO
+ // thread.
+ scoped_ptr<base::RunLoop> run_loop_;
+
+ // Set on the IO thread while |run_loop_| is non-NULL, read on the UI thread
+ // after deleting run_loop_.
+ bool tracked_request_completed_;
+
+ DISALLOW_COPY_AND_ASSIGN(TrackingResourceDispatcherHostDelegate);
+};
+
+// WebContentsDelegate that fails to open a URL when there's a request that
+// needs to be transferred between renderers.
+class NoTransferRequestDelegate : public WebContentsDelegate {
+ public:
+ NoTransferRequestDelegate() {}
+
+ virtual WebContents* OpenURLFromTab(WebContents* source,
+ const OpenURLParams& params) OVERRIDE {
+ bool is_transfer =
+ (params.transferred_global_request_id != GlobalRequestID());
+ if (is_transfer)
+ return NULL;
+ NavigationController::LoadURLParams load_url_params(params.url);
+ load_url_params.referrer = params.referrer;
+ load_url_params.frame_tree_node_id = params.frame_tree_node_id;
+ load_url_params.transition_type = params.transition;
+ load_url_params.extra_headers = params.extra_headers;
+ load_url_params.should_replace_current_entry =
+ params.should_replace_current_entry;
+ load_url_params.is_renderer_initiated = true;
+ source->GetController().LoadURLWithParams(load_url_params);
+ return source;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NoTransferRequestDelegate);
+};
+
+class CrossSiteTransferTest : public ContentBrowserTest {
+ public:
+ CrossSiteTransferTest() : old_delegate_(NULL) {
+ }
+
+ // ContentBrowserTest implementation:
+ virtual void SetUpOnMainThread() OVERRIDE {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(
+ &CrossSiteTransferTest::InjectResourceDisptcherHostDelegate,
+ base::Unretained(this)));
+ }
+
+ virtual void TearDownOnMainThread() OVERRIDE {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(
+ &CrossSiteTransferTest::RestoreResourceDisptcherHostDelegate,
+ base::Unretained(this)));
+ }
+
+ protected:
+ void NavigateToURLContentInitiated(Shell* window,
+ const GURL& url,
+ bool should_replace_current_entry,
+ bool should_wait_for_navigation) {
+ std::string script;
+ if (should_replace_current_entry)
+ script = base::StringPrintf("location.replace('%s')", url.spec().c_str());
+ else
+ script = base::StringPrintf("location.href = '%s'", url.spec().c_str());
+ TestNavigationObserver load_observer(shell()->web_contents(), 1);
+ bool result = ExecuteScript(window->web_contents(), script);
+ EXPECT_TRUE(result);
+ if (should_wait_for_navigation)
+ load_observer.Wait();
+ }
+
+ virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
+ // Use --site-per-process to force process swaps for cross-site transfers.
+ command_line->AppendSwitch(switches::kSitePerProcess);
+ }
+
+ void InjectResourceDisptcherHostDelegate() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ old_delegate_ = ResourceDispatcherHostImpl::Get()->delegate();
+ ResourceDispatcherHostImpl::Get()->SetDelegate(&tracking_delegate_);
+ }
+
+ void RestoreResourceDisptcherHostDelegate() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ ResourceDispatcherHostImpl::Get()->SetDelegate(old_delegate_);
+ old_delegate_ = NULL;
+ }
+
+ TrackingResourceDispatcherHostDelegate& tracking_delegate() {
+ return tracking_delegate_;
+ }
+
+ private:
+ TrackingResourceDispatcherHostDelegate tracking_delegate_;
+ ResourceDispatcherHostDelegate* old_delegate_;
+};
+
+// The following tests crash in the ThreadSanitizer runtime,
+// http://crbug.com/356758.
+#if defined(THREAD_SANITIZER)
+#define MAYBE_ReplaceEntryCrossProcessThenTransfer \
+ DISABLED_ReplaceEntryCrossProcessThenTransfer
+#define MAYBE_ReplaceEntryCrossProcessTwice \
+ DISABLED_ReplaceEntryCrossProcessTwice
+#else
+#define MAYBE_ReplaceEntryCrossProcessThenTransfer \
+ ReplaceEntryCrossProcessThenTransfer
+#define MAYBE_ReplaceEntryCrossProcessTwice ReplaceEntryCrossProcessTwice
+#endif
+// Tests that the |should_replace_current_entry| flag persists correctly across
+// request transfers that began with a cross-process navigation.
+IN_PROC_BROWSER_TEST_F(CrossSiteTransferTest,
+ MAYBE_ReplaceEntryCrossProcessThenTransfer) {
+ const NavigationController& controller =
+ shell()->web_contents()->GetController();
+ host_resolver()->AddRule("*", "127.0.0.1");
+ ASSERT_TRUE(test_server()->Start());
+
+ // These must all stay in scope with replace_host.
+ GURL::Replacements replace_host;
+ std::string a_com("A.com");
+ std::string b_com("B.com");
+
+ // Navigate to a starting URL, so there is a history entry to replace.
+ GURL url1 = test_server()->GetURL("files/site_isolation/blank.html?1");
+ NavigateToURL(shell(), url1);
+
+ // Force all future navigations to transfer. Note that this includes same-site
+ // navigiations which may cause double process swaps (via OpenURL and then via
+ // transfer). This test intentionally exercises that case.
+ ShellContentBrowserClient::SetSwapProcessesForRedirect(true);
+
+ // Navigate to a page on A.com with entry replacement. This navigation is
+ // cross-site, so the renderer will send it to the browser via OpenURL to give
+ // to a new process. It will then be transferred into yet another process due
+ // to the call above.
+ GURL url2 = test_server()->GetURL("files/site_isolation/blank.html?2");
+ replace_host.SetHostStr(a_com);
+ url2 = url2.ReplaceComponents(replace_host);
+ // Used to make sure the request for url2 succeeds, and there was only one of
+ // them.
+ tracking_delegate().SetTrackedURL(url2);
+ NavigateToURLContentInitiated(shell(), url2, true, true);
+
+ // There should be one history entry. url2 should have replaced url1.
+ EXPECT_TRUE(controller.GetPendingEntry() == NULL);
+ EXPECT_EQ(1, controller.GetEntryCount());
+ EXPECT_EQ(0, controller.GetCurrentEntryIndex());
+ EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL());
+ // Make sure the request succeeded.
+ EXPECT_TRUE(tracking_delegate().WaitForTrackedURLAndGetCompleted());
+
+ // Now navigate as before to a page on B.com, but normally (without
+ // replacement). This will still perform a double process-swap as above, via
+ // OpenURL and then transfer.
+ GURL url3 = test_server()->GetURL("files/site_isolation/blank.html?3");
+ replace_host.SetHostStr(b_com);
+ url3 = url3.ReplaceComponents(replace_host);
+ // Used to make sure the request for url3 succeeds, and there was only one of
+ // them.
+ tracking_delegate().SetTrackedURL(url3);
+ NavigateToURLContentInitiated(shell(), url3, false, true);
+
+ // There should be two history entries. url2 should have replaced url1. url2
+ // should not have replaced url3.
+ EXPECT_TRUE(controller.GetPendingEntry() == NULL);
+ EXPECT_EQ(2, controller.GetEntryCount());
+ EXPECT_EQ(1, controller.GetCurrentEntryIndex());
+ EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL());
+ EXPECT_EQ(url3, controller.GetEntryAtIndex(1)->GetURL());
+
+ // Make sure the request succeeded.
+ EXPECT_TRUE(tracking_delegate().WaitForTrackedURLAndGetCompleted());
+}
+
+// Tests that the |should_replace_current_entry| flag persists correctly across
+// request transfers that began with a content-initiated in-process
+// navigation. This test is the same as the test above, except transfering from
+// in-process.
+IN_PROC_BROWSER_TEST_F(CrossSiteTransferTest,
+ ReplaceEntryInProcessThenTranfers) {
+ const NavigationController& controller =
+ shell()->web_contents()->GetController();
+ ASSERT_TRUE(test_server()->Start());
+
+ // Navigate to a starting URL, so there is a history entry to replace.
+ GURL url = test_server()->GetURL("files/site_isolation/blank.html?1");
+ NavigateToURL(shell(), url);
+
+ // Force all future navigations to transfer. Note that this includes same-site
+ // navigiations which may cause double process swaps (via OpenURL and then via
+ // transfer). All navigations in this test are same-site, so it only swaps
+ // processes via request transfer.
+ ShellContentBrowserClient::SetSwapProcessesForRedirect(true);
+
+ // Navigate in-process with entry replacement. It will then be transferred
+ // into a new one due to the call above.
+ GURL url2 = test_server()->GetURL("files/site_isolation/blank.html?2");
+ NavigateToURLContentInitiated(shell(), url2, true, true);
+
+ // There should be one history entry. url2 should have replaced url1.
+ EXPECT_TRUE(controller.GetPendingEntry() == NULL);
+ EXPECT_EQ(1, controller.GetEntryCount());
+ EXPECT_EQ(0, controller.GetCurrentEntryIndex());
+ EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL());
+
+ // Now navigate as before, but without replacement.
+ GURL url3 = test_server()->GetURL("files/site_isolation/blank.html?3");
+ NavigateToURLContentInitiated(shell(), url3, false, true);
+
+ // There should be two history entries. url2 should have replaced url1. url2
+ // should not have replaced url3.
+ EXPECT_TRUE(controller.GetPendingEntry() == NULL);
+ EXPECT_EQ(2, controller.GetEntryCount());
+ EXPECT_EQ(1, controller.GetCurrentEntryIndex());
+ EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL());
+ EXPECT_EQ(url3, controller.GetEntryAtIndex(1)->GetURL());
+}
+
+// Tests that the |should_replace_current_entry| flag persists correctly across
+// request transfers that cross processes twice from renderer policy.
+IN_PROC_BROWSER_TEST_F(CrossSiteTransferTest,
+ MAYBE_ReplaceEntryCrossProcessTwice) {
+ const NavigationController& controller =
+ shell()->web_contents()->GetController();
+ host_resolver()->AddRule("*", "127.0.0.1");
+ ASSERT_TRUE(test_server()->Start());
+
+ // These must all stay in scope with replace_host.
+ GURL::Replacements replace_host;
+ std::string a_com("A.com");
+ std::string b_com("B.com");
+
+ // Navigate to a starting URL, so there is a history entry to replace.
+ GURL url1 = test_server()->GetURL("files/site_isolation/blank.html?1");
+ NavigateToURL(shell(), url1);
+
+ // Navigate to a page on A.com which redirects to B.com with entry
+ // replacement. This will switch processes via OpenURL twice. First to A.com,
+ // and second in response to the server redirect to B.com. The second swap is
+ // also renderer-initiated via OpenURL because decidePolicyForNavigation is
+ // currently applied on redirects.
+ GURL url2b = test_server()->GetURL("files/site_isolation/blank.html?2");
+ replace_host.SetHostStr(b_com);
+ url2b = url2b.ReplaceComponents(replace_host);
+ GURL url2a = test_server()->GetURL(
+ "server-redirect?" + net::EscapeQueryParamValue(url2b.spec(), false));
+ replace_host.SetHostStr(a_com);
+ url2a = url2a.ReplaceComponents(replace_host);
+ NavigateToURLContentInitiated(shell(), url2a, true, true);
+
+ // There should be one history entry. url2b should have replaced url1.
+ EXPECT_TRUE(controller.GetPendingEntry() == NULL);
+ EXPECT_EQ(1, controller.GetEntryCount());
+ EXPECT_EQ(0, controller.GetCurrentEntryIndex());
+ EXPECT_EQ(url2b, controller.GetEntryAtIndex(0)->GetURL());
+
+ // Now repeat without replacement.
+ GURL url3b = test_server()->GetURL("files/site_isolation/blank.html?3");
+ replace_host.SetHostStr(b_com);
+ url3b = url3b.ReplaceComponents(replace_host);
+ GURL url3a = test_server()->GetURL(
+ "server-redirect?" + net::EscapeQueryParamValue(url3b.spec(), false));
+ replace_host.SetHostStr(a_com);
+ url3a = url3a.ReplaceComponents(replace_host);
+ NavigateToURLContentInitiated(shell(), url3a, false, true);
+
+ // There should be two history entries. url2b should have replaced url1. url2b
+ // should not have replaced url3b.
+ EXPECT_TRUE(controller.GetPendingEntry() == NULL);
+ EXPECT_EQ(2, controller.GetEntryCount());
+ EXPECT_EQ(1, controller.GetCurrentEntryIndex());
+ EXPECT_EQ(url2b, controller.GetEntryAtIndex(0)->GetURL());
+ EXPECT_EQ(url3b, controller.GetEntryAtIndex(1)->GetURL());
+}
+
+// Tests that the request is destroyed when a cross process navigation is
+// cancelled.
+IN_PROC_BROWSER_TEST_F(CrossSiteTransferTest, NoLeakOnCrossSiteCancel) {
+ const NavigationController& controller =
+ shell()->web_contents()->GetController();
+ host_resolver()->AddRule("*", "127.0.0.1");
+ ASSERT_TRUE(test_server()->Start());
+
+ // These must all stay in scope with replace_host.
+ GURL::Replacements replace_host;
+ std::string a_com("A.com");
+ std::string b_com("B.com");
+
+ // Navigate to a starting URL, so there is a history entry to replace.
+ GURL url1 = test_server()->GetURL("files/site_isolation/blank.html?1");
+ NavigateToURL(shell(), url1);
+
+ // Force all future navigations to transfer.
+ ShellContentBrowserClient::SetSwapProcessesForRedirect(true);
+
+ NoTransferRequestDelegate no_transfer_request_delegate;
+ WebContentsDelegate* old_delegate = shell()->web_contents()->GetDelegate();
+ shell()->web_contents()->SetDelegate(&no_transfer_request_delegate);
+
+ // Navigate to a page on A.com with entry replacement. This navigation is
+ // cross-site, so the renderer will send it to the browser via OpenURL to give
+ // to a new process. It will then be transferred into yet another process due
+ // to the call above.
+ GURL url2 = test_server()->GetURL("files/site_isolation/blank.html?2");
+ replace_host.SetHostStr(a_com);
+ url2 = url2.ReplaceComponents(replace_host);
+ // Used to make sure the second request is cancelled, and there is only one
+ // request for url2.
+ tracking_delegate().SetTrackedURL(url2);
+
+ // Don't wait for the navigation to complete, since that never happens in
+ // this case.
+ NavigateToURLContentInitiated(shell(), url2, false, false);
+
+ // There should be one history entry, with url1.
+ EXPECT_EQ(1, controller.GetEntryCount());
+ EXPECT_EQ(0, controller.GetCurrentEntryIndex());
+ EXPECT_EQ(url1, controller.GetEntryAtIndex(0)->GetURL());
+
+ // Make sure the request for url2 did not complete.
+ EXPECT_FALSE(tracking_delegate().WaitForTrackedURLAndGetCompleted());
+
+ shell()->web_contents()->SetDelegate(old_delegate);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/database_browsertest.cc b/chromium/content/browser/database_browsertest.cc
index 373a06f2a70..d3cc669d256 100644
--- a/chromium/content/browser/database_browsertest.cc
+++ b/chromium/content/browser/database_browsertest.cc
@@ -10,10 +10,10 @@
#include "content/public/browser/notification_types.h"
#include "content/public/browser/web_contents.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/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
#include "content/test/net/url_request_mock_http_job.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromium/content/browser/database_quota_client_unittest.cc b/chromium/content/browser/database_quota_client_unittest.cc
new file mode 100644
index 00000000000..4ac6cb3cb2d
--- /dev/null
+++ b/chromium/content/browser/database_quota_client_unittest.cc
@@ -0,0 +1,289 @@
+// 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 <map>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "net/base/completion_callback.h"
+#include "net/base/net_errors.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/database/database_quota_client.h"
+#include "webkit/browser/database/database_tracker.h"
+#include "webkit/browser/database/database_util.h"
+#include "webkit/common/database/database_identifier.h"
+
+using webkit_database::DatabaseQuotaClient;
+using webkit_database::DatabaseTracker;
+using webkit_database::OriginInfo;
+
+namespace content {
+
+// Declared to shorten the line lengths.
+static const quota::StorageType kTemp = quota::kStorageTypeTemporary;
+static const quota::StorageType kPerm = quota::kStorageTypePersistent;
+
+// Mock tracker class the mocks up those methods of the tracker
+// that are used by the QuotaClient.
+class MockDatabaseTracker : public DatabaseTracker {
+ public:
+ MockDatabaseTracker()
+ : DatabaseTracker(base::FilePath(), false, NULL, NULL, NULL),
+ delete_called_count_(0),
+ async_delete_(false) {}
+
+ virtual bool GetOriginInfo(
+ const std::string& origin_identifier,
+ OriginInfo* info) OVERRIDE {
+ std::map<GURL, MockOriginInfo>::const_iterator found =
+ mock_origin_infos_.find(
+ webkit_database::GetOriginFromIdentifier(origin_identifier));
+ if (found == mock_origin_infos_.end())
+ return false;
+ *info = OriginInfo(found->second);
+ return true;
+ }
+
+ virtual bool GetAllOriginIdentifiers(
+ std::vector<std::string>* origins_identifiers) OVERRIDE {
+ std::map<GURL, MockOriginInfo>::const_iterator iter;
+ for (iter = mock_origin_infos_.begin();
+ iter != mock_origin_infos_.end();
+ ++iter) {
+ origins_identifiers->push_back(iter->second.GetOriginIdentifier());
+ }
+ return true;
+ }
+
+ virtual bool GetAllOriginsInfo(
+ std::vector<OriginInfo>* origins_info) OVERRIDE {
+ std::map<GURL, MockOriginInfo>::const_iterator iter;
+ for (iter = mock_origin_infos_.begin();
+ iter != mock_origin_infos_.end();
+ ++iter) {
+ origins_info->push_back(OriginInfo(iter->second));
+ }
+ return true;
+ }
+
+ virtual int DeleteDataForOrigin(
+ const std::string& origin_identifier,
+ const net::CompletionCallback& callback) OVERRIDE {
+ ++delete_called_count_;
+ if (async_delete()) {
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&MockDatabaseTracker::AsyncDeleteDataForOrigin, this,
+ callback));
+ return net::ERR_IO_PENDING;
+ }
+ return net::OK;
+ }
+
+ void AsyncDeleteDataForOrigin(const net::CompletionCallback& callback) {
+ callback.Run(net::OK);
+ }
+
+ void AddMockDatabase(const GURL& origin, const char* name, int size) {
+ MockOriginInfo& info = mock_origin_infos_[origin];
+ info.set_origin(webkit_database::GetIdentifierFromOrigin(origin));
+ info.AddMockDatabase(base::ASCIIToUTF16(name), size);
+ }
+
+ int delete_called_count() { return delete_called_count_; }
+ bool async_delete() { return async_delete_; }
+ void set_async_delete(bool async) { async_delete_ = async; }
+
+ protected:
+ virtual ~MockDatabaseTracker() {}
+
+ private:
+ class MockOriginInfo : public OriginInfo {
+ public:
+ void set_origin(const std::string& origin_identifier) {
+ origin_identifier_ = origin_identifier;
+ }
+
+ void AddMockDatabase(const base::string16& name, int size) {
+ EXPECT_TRUE(database_info_.find(name) == database_info_.end());
+ database_info_[name].first = size;
+ total_size_ += size;
+ }
+ };
+
+ int delete_called_count_;
+ bool async_delete_;
+ std::map<GURL, MockOriginInfo> mock_origin_infos_;
+};
+
+
+// Base class for our test fixtures.
+class DatabaseQuotaClientTest : public testing::Test {
+ public:
+ const GURL kOriginA;
+ const GURL kOriginB;
+ const GURL kOriginOther;
+
+ DatabaseQuotaClientTest()
+ : kOriginA("http://host"),
+ kOriginB("http://host:8000"),
+ kOriginOther("http://other"),
+ usage_(0),
+ mock_tracker_(new MockDatabaseTracker),
+ weak_factory_(this) {
+ }
+
+ int64 GetOriginUsage(
+ quota::QuotaClient* client,
+ const GURL& origin,
+ quota::StorageType type) {
+ usage_ = 0;
+ client->GetOriginUsage(
+ origin, type,
+ base::Bind(&DatabaseQuotaClientTest::OnGetOriginUsageComplete,
+ weak_factory_.GetWeakPtr()));
+ base::RunLoop().RunUntilIdle();
+ return usage_;
+ }
+
+ const std::set<GURL>& GetOriginsForType(
+ quota::QuotaClient* client,
+ quota::StorageType type) {
+ origins_.clear();
+ client->GetOriginsForType(
+ type,
+ base::Bind(&DatabaseQuotaClientTest::OnGetOriginsComplete,
+ weak_factory_.GetWeakPtr()));
+ base::RunLoop().RunUntilIdle();
+ return origins_;
+ }
+
+ const std::set<GURL>& GetOriginsForHost(
+ quota::QuotaClient* client,
+ quota::StorageType type,
+ const std::string& host) {
+ origins_.clear();
+ client->GetOriginsForHost(
+ type, host,
+ base::Bind(&DatabaseQuotaClientTest::OnGetOriginsComplete,
+ weak_factory_.GetWeakPtr()));
+ base::RunLoop().RunUntilIdle();
+ return origins_;
+ }
+
+ bool DeleteOriginData(
+ quota::QuotaClient* client,
+ quota::StorageType type,
+ const GURL& origin) {
+ delete_status_ = quota::kQuotaStatusUnknown;
+ client->DeleteOriginData(
+ origin, type,
+ base::Bind(&DatabaseQuotaClientTest::OnDeleteOriginDataComplete,
+ weak_factory_.GetWeakPtr()));
+ base::RunLoop().RunUntilIdle();
+ return delete_status_ == quota::kQuotaStatusOk;
+ }
+
+ MockDatabaseTracker* mock_tracker() { return mock_tracker_.get(); }
+
+
+ private:
+ void OnGetOriginUsageComplete(int64 usage) {
+ usage_ = usage;
+ }
+
+ void OnGetOriginsComplete(const std::set<GURL>& origins) {
+ origins_ = origins;
+ }
+
+ void OnDeleteOriginDataComplete(quota::QuotaStatusCode status) {
+ delete_status_ = status;
+ }
+
+ base::MessageLoop message_loop_;
+ int64 usage_;
+ std::set<GURL> origins_;
+ quota::QuotaStatusCode delete_status_;
+ scoped_refptr<MockDatabaseTracker> mock_tracker_;
+ base::WeakPtrFactory<DatabaseQuotaClientTest> weak_factory_;
+};
+
+
+TEST_F(DatabaseQuotaClientTest, GetOriginUsage) {
+ DatabaseQuotaClient client(base::MessageLoopProxy::current().get(),
+ mock_tracker());
+
+ EXPECT_EQ(0, GetOriginUsage(&client, kOriginA, kTemp));
+ EXPECT_EQ(0, GetOriginUsage(&client, kOriginA, kPerm));
+
+ mock_tracker()->AddMockDatabase(kOriginA, "fooDB", 1000);
+ EXPECT_EQ(1000, GetOriginUsage(&client, kOriginA, kTemp));
+ EXPECT_EQ(0, GetOriginUsage(&client, kOriginA, kPerm));
+
+ EXPECT_EQ(0, GetOriginUsage(&client, kOriginB, kPerm));
+ EXPECT_EQ(0, GetOriginUsage(&client, kOriginB, kTemp));
+}
+
+TEST_F(DatabaseQuotaClientTest, GetOriginsForHost) {
+ DatabaseQuotaClient client(base::MessageLoopProxy::current().get(),
+ mock_tracker());
+
+ EXPECT_EQ(kOriginA.host(), kOriginB.host());
+ EXPECT_NE(kOriginA.host(), kOriginOther.host());
+
+ std::set<GURL> origins = GetOriginsForHost(&client, kTemp, kOriginA.host());
+ EXPECT_TRUE(origins.empty());
+
+ mock_tracker()->AddMockDatabase(kOriginA, "fooDB", 1000);
+ origins = GetOriginsForHost(&client, kTemp, kOriginA.host());
+ EXPECT_EQ(origins.size(), 1ul);
+ EXPECT_TRUE(origins.find(kOriginA) != origins.end());
+
+ mock_tracker()->AddMockDatabase(kOriginB, "barDB", 1000);
+ origins = GetOriginsForHost(&client, kTemp, kOriginA.host());
+ EXPECT_EQ(origins.size(), 2ul);
+ EXPECT_TRUE(origins.find(kOriginA) != origins.end());
+ EXPECT_TRUE(origins.find(kOriginB) != origins.end());
+
+ EXPECT_TRUE(GetOriginsForHost(&client, kPerm, kOriginA.host()).empty());
+ EXPECT_TRUE(GetOriginsForHost(&client, kTemp, kOriginOther.host()).empty());
+}
+
+TEST_F(DatabaseQuotaClientTest, GetOriginsForType) {
+ DatabaseQuotaClient client(base::MessageLoopProxy::current().get(),
+ mock_tracker());
+
+ EXPECT_TRUE(GetOriginsForType(&client, kTemp).empty());
+ EXPECT_TRUE(GetOriginsForType(&client, kPerm).empty());
+
+ mock_tracker()->AddMockDatabase(kOriginA, "fooDB", 1000);
+ std::set<GURL> origins = GetOriginsForType(&client, kTemp);
+ EXPECT_EQ(origins.size(), 1ul);
+ EXPECT_TRUE(origins.find(kOriginA) != origins.end());
+
+ EXPECT_TRUE(GetOriginsForType(&client, kPerm).empty());
+}
+
+TEST_F(DatabaseQuotaClientTest, DeleteOriginData) {
+ DatabaseQuotaClient client(base::MessageLoopProxy::current().get(),
+ mock_tracker());
+
+ // Perm deletions are short circuited in the Client and
+ // should not reach the DatabaseTracker.
+ EXPECT_TRUE(DeleteOriginData(&client, kPerm, kOriginA));
+ EXPECT_EQ(0, mock_tracker()->delete_called_count());
+
+ mock_tracker()->set_async_delete(false);
+ EXPECT_TRUE(DeleteOriginData(&client, kTemp, kOriginA));
+ EXPECT_EQ(1, mock_tracker()->delete_called_count());
+
+ mock_tracker()->set_async_delete(true);
+ EXPECT_TRUE(DeleteOriginData(&client, kTemp, kOriginA));
+ EXPECT_EQ(2, mock_tracker()->delete_called_count());
+}
+
+} // namespace content
diff --git a/chromium/content/browser/database_tracker_unittest.cc b/chromium/content/browser/database_tracker_unittest.cc
new file mode 100644
index 00000000000..baece0a4bfd
--- /dev/null
+++ b/chromium/content/browser/database_tracker_unittest.cc
@@ -0,0 +1,875 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/file_util.h"
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "content/public/test/mock_special_storage_policy.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/sqlite/sqlite3.h"
+#include "webkit/browser/database/database_tracker.h"
+#include "webkit/browser/quota/quota_manager_proxy.h"
+#include "webkit/common/database/database_identifier.h"
+
+using base::ASCIIToUTF16;
+using webkit_database::DatabaseConnections;
+using webkit_database::DatabaseTracker;
+using webkit_database::OriginInfo;
+
+namespace {
+
+const char kOrigin1Url[] = "http://origin1";
+const char kOrigin2Url[] = "http://protected_origin2";
+
+class TestObserver : public webkit_database::DatabaseTracker::Observer {
+ public:
+ TestObserver()
+ : new_notification_received_(false),
+ observe_size_changes_(true),
+ observe_scheduled_deletions_(true) {
+ }
+ TestObserver(bool observe_size_changes, bool observe_scheduled_deletions)
+ : new_notification_received_(false),
+ observe_size_changes_(observe_size_changes),
+ observe_scheduled_deletions_(observe_scheduled_deletions) {
+ }
+
+ virtual ~TestObserver() {}
+ virtual void OnDatabaseSizeChanged(const std::string& origin_identifier,
+ const base::string16& database_name,
+ int64 database_size) OVERRIDE {
+ if (!observe_size_changes_)
+ return;
+ new_notification_received_ = true;
+ origin_identifier_ = origin_identifier;
+ database_name_ = database_name;
+ database_size_ = database_size;
+ }
+ virtual void OnDatabaseScheduledForDeletion(
+ const std::string& origin_identifier,
+ const base::string16& database_name) OVERRIDE {
+ if (!observe_scheduled_deletions_)
+ return;
+ new_notification_received_ = true;
+ origin_identifier_ = origin_identifier;
+ database_name_ = database_name;
+ }
+ bool DidReceiveNewNotification() {
+ bool temp_new_notification_received = new_notification_received_;
+ new_notification_received_ = false;
+ return temp_new_notification_received;
+ }
+ std::string GetNotificationOriginIdentifier() {
+ return origin_identifier_;
+ }
+ base::string16 GetNotificationDatabaseName() { return database_name_; }
+ int64 GetNotificationDatabaseSize() { return database_size_; }
+
+ private:
+ bool new_notification_received_;
+ bool observe_size_changes_;
+ bool observe_scheduled_deletions_;
+ std::string origin_identifier_;
+ base::string16 database_name_;
+ int64 database_size_;
+};
+
+void CheckNotificationReceived(TestObserver* observer,
+ const std::string& expected_origin_identifier,
+ const base::string16& expected_database_name,
+ int64 expected_database_size) {
+ EXPECT_TRUE(observer->DidReceiveNewNotification());
+ EXPECT_EQ(expected_origin_identifier,
+ observer->GetNotificationOriginIdentifier());
+ EXPECT_EQ(expected_database_name,
+ observer->GetNotificationDatabaseName());
+ EXPECT_EQ(expected_database_size,
+ observer->GetNotificationDatabaseSize());
+}
+
+class TestQuotaManagerProxy : public quota::QuotaManagerProxy {
+ public:
+ TestQuotaManagerProxy()
+ : QuotaManagerProxy(NULL, NULL),
+ registered_client_(NULL) {
+ }
+
+ virtual void RegisterClient(quota::QuotaClient* client) OVERRIDE {
+ EXPECT_FALSE(registered_client_);
+ registered_client_ = client;
+ }
+
+ virtual void NotifyStorageAccessed(quota::QuotaClient::ID client_id,
+ const GURL& origin,
+ quota::StorageType type) OVERRIDE {
+ EXPECT_EQ(quota::QuotaClient::kDatabase, client_id);
+ EXPECT_EQ(quota::kStorageTypeTemporary, type);
+ accesses_[origin] += 1;
+ }
+
+ virtual void NotifyStorageModified(quota::QuotaClient::ID client_id,
+ const GURL& origin,
+ quota::StorageType type,
+ int64 delta) OVERRIDE {
+ EXPECT_EQ(quota::QuotaClient::kDatabase, client_id);
+ EXPECT_EQ(quota::kStorageTypeTemporary, type);
+ modifications_[origin].first += 1;
+ modifications_[origin].second += delta;
+ }
+
+ // Not needed for our tests.
+ virtual void NotifyOriginInUse(const GURL& origin) OVERRIDE {}
+ virtual void NotifyOriginNoLongerInUse(const GURL& origin) OVERRIDE {}
+ virtual void SetUsageCacheEnabled(quota::QuotaClient::ID client_id,
+ const GURL& origin,
+ quota::StorageType type,
+ bool enabled) OVERRIDE {}
+ virtual void GetUsageAndQuota(
+ base::SequencedTaskRunner* original_task_runner,
+ const GURL& origin,
+ quota::StorageType type,
+ const GetUsageAndQuotaCallback& callback) OVERRIDE {}
+
+ void SimulateQuotaManagerDestroyed() {
+ if (registered_client_) {
+ registered_client_->OnQuotaManagerDestroyed();
+ registered_client_ = NULL;
+ }
+ }
+
+ bool WasAccessNotified(const GURL& origin) {
+ return accesses_[origin] != 0;
+ }
+
+ bool WasModificationNotified(const GURL& origin, int64 amount) {
+ return modifications_[origin].first != 0 &&
+ modifications_[origin].second == amount;
+ }
+
+ void reset() {
+ accesses_.clear();
+ modifications_.clear();
+ }
+
+ quota::QuotaClient* registered_client_;
+
+ // Map from origin to count of access notifications.
+ std::map<GURL, int> accesses_;
+
+ // Map from origin to <count, sum of deltas>
+ std::map<GURL, std::pair<int, int64> > modifications_;
+
+ protected:
+ virtual ~TestQuotaManagerProxy() {
+ EXPECT_FALSE(registered_client_);
+ }
+};
+
+
+bool EnsureFileOfSize(const base::FilePath& file_path, int64 length) {
+ base::File file(file_path,
+ base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_WRITE);
+ if (!file.IsValid())
+ return false;
+ return file.SetLength(length);
+}
+
+} // namespace
+
+namespace content {
+
+// We declare a helper class, and make it a friend of DatabaseTracker using
+// the FORWARD_DECLARE_TEST macro, and we implement all tests we want to run as
+// static methods of this class. Then we make our TEST() targets call these
+// static functions. This allows us to run each test in normal mode and
+// incognito mode without writing the same code twice.
+class DatabaseTracker_TestHelper_Test {
+ public:
+ static void TestDeleteOpenDatabase(bool incognito_mode) {
+ // Initialize the tracker database.
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ scoped_refptr<MockSpecialStoragePolicy> special_storage_policy =
+ new MockSpecialStoragePolicy;
+ special_storage_policy->AddProtected(GURL(kOrigin2Url));
+ scoped_refptr<DatabaseTracker> tracker(
+ new DatabaseTracker(temp_dir.path(),
+ incognito_mode,
+ special_storage_policy.get(),
+ NULL,
+ NULL));
+
+ // Create and open three databases.
+ int64 database_size = 0;
+ const std::string kOrigin1 =
+ webkit_database::GetIdentifierFromOrigin(GURL(kOrigin1Url));
+ const std::string kOrigin2 =
+ webkit_database::GetIdentifierFromOrigin(GURL(kOrigin2Url));
+ const base::string16 kDB1 = ASCIIToUTF16("db1");
+ const base::string16 kDB2 = ASCIIToUTF16("db2");
+ const base::string16 kDB3 = ASCIIToUTF16("db3");
+ const base::string16 kDescription = ASCIIToUTF16("database_description");
+
+ tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0,
+ &database_size);
+ tracker->DatabaseOpened(kOrigin2, kDB2, kDescription, 0,
+ &database_size);
+ tracker->DatabaseOpened(kOrigin2, kDB3, kDescription, 0,
+ &database_size);
+
+ EXPECT_TRUE(base::CreateDirectory(
+ tracker->DatabaseDirectory().Append(base::FilePath::FromUTF16Unsafe(
+ tracker->GetOriginDirectory(kOrigin1)))));
+ EXPECT_TRUE(base::CreateDirectory(
+ tracker->DatabaseDirectory().Append(base::FilePath::FromUTF16Unsafe(
+ tracker->GetOriginDirectory(kOrigin2)))));
+ EXPECT_EQ(1, base::WriteFile(
+ tracker->GetFullDBFilePath(kOrigin1, kDB1), "a", 1));
+ EXPECT_EQ(2, base::WriteFile(
+ tracker->GetFullDBFilePath(kOrigin2, kDB2), "aa", 2));
+ EXPECT_EQ(3, base::WriteFile(
+ tracker->GetFullDBFilePath(kOrigin2, kDB3), "aaa", 3));
+ tracker->DatabaseModified(kOrigin1, kDB1);
+ tracker->DatabaseModified(kOrigin2, kDB2);
+ tracker->DatabaseModified(kOrigin2, kDB3);
+
+ // Delete db1. Should also delete origin1.
+ TestObserver observer;
+ tracker->AddObserver(&observer);
+ net::TestCompletionCallback callback;
+ int result = tracker->DeleteDatabase(kOrigin1, kDB1, callback.callback());
+ EXPECT_EQ(net::ERR_IO_PENDING, result);
+ ASSERT_FALSE(callback.have_result());
+ EXPECT_TRUE(observer.DidReceiveNewNotification());
+ EXPECT_EQ(kOrigin1, observer.GetNotificationOriginIdentifier());
+ EXPECT_EQ(kDB1, observer.GetNotificationDatabaseName());
+ tracker->DatabaseClosed(kOrigin1, kDB1);
+ result = callback.GetResult(result);
+ EXPECT_EQ(net::OK, result);
+ EXPECT_FALSE(base::PathExists(
+ tracker->DatabaseDirectory().AppendASCII(kOrigin1)));
+
+ // Recreate db1.
+ tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0,
+ &database_size);
+ EXPECT_TRUE(base::CreateDirectory(
+ tracker->DatabaseDirectory().Append(base::FilePath::FromUTF16Unsafe(
+ tracker->GetOriginDirectory(kOrigin1)))));
+ EXPECT_EQ(1, base::WriteFile(
+ tracker->GetFullDBFilePath(kOrigin1, kDB1), "a", 1));
+ tracker->DatabaseModified(kOrigin1, kDB1);
+
+ // Setup file modification times. db1 and db2 are modified now, db3 three
+ // days ago.
+ base::Time now = base::Time::Now();
+ EXPECT_TRUE(base::TouchFile(tracker->GetFullDBFilePath(kOrigin1, kDB1),
+ now, now));
+ EXPECT_TRUE(base::TouchFile(tracker->GetFullDBFilePath(kOrigin2, kDB2),
+ now, now));
+ base::Time three_days_ago = now - base::TimeDelta::FromDays(3);
+ EXPECT_TRUE(base::TouchFile(tracker->GetFullDBFilePath(kOrigin2, kDB3),
+ three_days_ago, three_days_ago));
+
+ // Delete databases modified since yesterday. db2 is whitelisted.
+ base::Time yesterday = base::Time::Now();
+ yesterday -= base::TimeDelta::FromDays(1);
+ result = tracker->DeleteDataModifiedSince(
+ yesterday, callback.callback());
+ EXPECT_EQ(net::ERR_IO_PENDING, result);
+ ASSERT_FALSE(callback.have_result());
+ EXPECT_TRUE(observer.DidReceiveNewNotification());
+ tracker->DatabaseClosed(kOrigin1, kDB1);
+ tracker->DatabaseClosed(kOrigin2, kDB2);
+ result = callback.GetResult(result);
+ EXPECT_EQ(net::OK, result);
+ EXPECT_FALSE(base::PathExists(
+ tracker->DatabaseDirectory().AppendASCII(kOrigin1)));
+ EXPECT_TRUE(
+ base::PathExists(tracker->GetFullDBFilePath(kOrigin2, kDB2)));
+ EXPECT_TRUE(
+ base::PathExists(tracker->GetFullDBFilePath(kOrigin2, kDB3)));
+
+ tracker->DatabaseClosed(kOrigin2, kDB3);
+ tracker->RemoveObserver(&observer);
+ }
+
+ static void TestDatabaseTracker(bool incognito_mode) {
+ // Initialize the tracker database.
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ scoped_refptr<MockSpecialStoragePolicy> special_storage_policy =
+ new MockSpecialStoragePolicy;
+ special_storage_policy->AddProtected(GURL(kOrigin2Url));
+ scoped_refptr<DatabaseTracker> tracker(
+ new DatabaseTracker(temp_dir.path(),
+ incognito_mode,
+ special_storage_policy.get(),
+ NULL,
+ NULL));
+
+ // Add two observers.
+ TestObserver observer1;
+ TestObserver observer2;
+ tracker->AddObserver(&observer1);
+ tracker->AddObserver(&observer2);
+
+ // Open three new databases.
+ int64 database_size = 0;
+ const std::string kOrigin1 =
+ webkit_database::GetIdentifierFromOrigin(GURL(kOrigin1Url));
+ const std::string kOrigin2 =
+ webkit_database::GetIdentifierFromOrigin(GURL(kOrigin2Url));
+ const base::string16 kDB1 = ASCIIToUTF16("db1");
+ const base::string16 kDB2 = ASCIIToUTF16("db2");
+ const base::string16 kDB3 = ASCIIToUTF16("db3");
+ const base::string16 kDescription = ASCIIToUTF16("database_description");
+
+ // Get the info for kOrigin1 and kOrigin2
+ DatabaseTracker::CachedOriginInfo* origin1_info =
+ tracker->GetCachedOriginInfo(kOrigin1);
+ DatabaseTracker::CachedOriginInfo* origin2_info =
+ tracker->GetCachedOriginInfo(kOrigin1);
+ EXPECT_TRUE(origin1_info);
+ EXPECT_TRUE(origin2_info);
+
+
+ tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0,
+ &database_size);
+ EXPECT_EQ(0, database_size);
+ tracker->DatabaseOpened(kOrigin2, kDB2, kDescription, 0,
+ &database_size);
+ EXPECT_EQ(0, database_size);
+ tracker->DatabaseOpened(kOrigin1, kDB3, kDescription, 0,
+ &database_size);
+ EXPECT_EQ(0, database_size);
+
+ // Write some data to each file and check that the listeners are
+ // called with the appropriate values.
+ EXPECT_TRUE(base::CreateDirectory(
+ tracker->DatabaseDirectory().Append(base::FilePath::FromUTF16Unsafe(
+ tracker->GetOriginDirectory(kOrigin1)))));
+ EXPECT_TRUE(base::CreateDirectory(
+ tracker->DatabaseDirectory().Append(base::FilePath::FromUTF16Unsafe(
+ tracker->GetOriginDirectory(kOrigin2)))));
+ EXPECT_EQ(1, base::WriteFile(
+ tracker->GetFullDBFilePath(kOrigin1, kDB1), "a", 1));
+ EXPECT_EQ(2, base::WriteFile(
+ tracker->GetFullDBFilePath(kOrigin2, kDB2), "aa", 2));
+ EXPECT_EQ(4, base::WriteFile(
+ tracker->GetFullDBFilePath(kOrigin1, kDB3), "aaaa", 4));
+ tracker->DatabaseModified(kOrigin1, kDB1);
+ CheckNotificationReceived(&observer1, kOrigin1, kDB1, 1);
+ CheckNotificationReceived(&observer2, kOrigin1, kDB1, 1);
+ tracker->DatabaseModified(kOrigin2, kDB2);
+ CheckNotificationReceived(&observer1, kOrigin2, kDB2, 2);
+ CheckNotificationReceived(&observer2, kOrigin2, kDB2, 2);
+ tracker->DatabaseModified(kOrigin1, kDB3);
+ CheckNotificationReceived(&observer1, kOrigin1, kDB3, 4);
+ CheckNotificationReceived(&observer2, kOrigin1, kDB3, 4);
+
+ // Close all databases
+ tracker->DatabaseClosed(kOrigin1, kDB1);
+ tracker->DatabaseClosed(kOrigin2, kDB2);
+ tracker->DatabaseClosed(kOrigin1, kDB3);
+
+ // Open an existing database and check the reported size
+ tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0,
+ &database_size);
+ EXPECT_EQ(1, database_size);
+ tracker->DatabaseClosed(kOrigin1, kDB1);
+
+ // Remove an observer; this should clear all caches.
+ tracker->RemoveObserver(&observer2);
+
+ // Close the tracker database and clear all caches.
+ // Then make sure that DatabaseOpened() still returns the correct result.
+ tracker->CloseTrackerDatabaseAndClearCaches();
+ tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0,
+ &database_size);
+ EXPECT_EQ(1, database_size);
+ tracker->DatabaseClosed(kOrigin1, kDB1);
+
+ // Remove all observers.
+ tracker->RemoveObserver(&observer1);
+
+ // Trying to delete a database in use should fail
+ tracker->DatabaseOpened(kOrigin1, kDB3, kDescription, 0,
+ &database_size);
+ EXPECT_FALSE(tracker->DeleteClosedDatabase(kOrigin1, kDB3));
+ origin1_info = tracker->GetCachedOriginInfo(kOrigin1);
+ EXPECT_TRUE(origin1_info);
+ EXPECT_EQ(4, origin1_info->GetDatabaseSize(kDB3));
+ tracker->DatabaseClosed(kOrigin1, kDB3);
+
+ // Delete a database and make sure the space used by that origin is updated
+ EXPECT_TRUE(tracker->DeleteClosedDatabase(kOrigin1, kDB3));
+ origin1_info = tracker->GetCachedOriginInfo(kOrigin1);
+ EXPECT_TRUE(origin1_info);
+ EXPECT_EQ(1, origin1_info->GetDatabaseSize(kDB1));
+ EXPECT_EQ(0, origin1_info->GetDatabaseSize(kDB3));
+
+ // Get all data for all origins
+ std::vector<OriginInfo> origins_info;
+ EXPECT_TRUE(tracker->GetAllOriginsInfo(&origins_info));
+ EXPECT_EQ(size_t(2), origins_info.size());
+ EXPECT_EQ(kOrigin1, origins_info[0].GetOriginIdentifier());
+ EXPECT_EQ(1, origins_info[0].TotalSize());
+ EXPECT_EQ(1, origins_info[0].GetDatabaseSize(kDB1));
+ EXPECT_EQ(0, origins_info[0].GetDatabaseSize(kDB3));
+
+ EXPECT_EQ(kOrigin2, origins_info[1].GetOriginIdentifier());
+ EXPECT_EQ(2, origins_info[1].TotalSize());
+
+ // Trying to delete an origin with databases in use should fail
+ tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0,
+ &database_size);
+ EXPECT_FALSE(tracker->DeleteOrigin(kOrigin1, false));
+ origin1_info = tracker->GetCachedOriginInfo(kOrigin1);
+ EXPECT_TRUE(origin1_info);
+ EXPECT_EQ(1, origin1_info->GetDatabaseSize(kDB1));
+ tracker->DatabaseClosed(kOrigin1, kDB1);
+
+ // Delete an origin that doesn't have any database in use
+ EXPECT_TRUE(tracker->DeleteOrigin(kOrigin1, false));
+ origins_info.clear();
+ EXPECT_TRUE(tracker->GetAllOriginsInfo(&origins_info));
+ EXPECT_EQ(size_t(1), origins_info.size());
+ EXPECT_EQ(kOrigin2, origins_info[0].GetOriginIdentifier());
+
+ origin1_info = tracker->GetCachedOriginInfo(kOrigin1);
+ EXPECT_TRUE(origin1_info);
+ EXPECT_EQ(0, origin1_info->TotalSize());
+ }
+
+ static void DatabaseTrackerQuotaIntegration() {
+ const GURL kOrigin(kOrigin1Url);
+ const std::string kOriginId =
+ webkit_database::GetIdentifierFromOrigin(kOrigin);
+ const base::string16 kName = ASCIIToUTF16("name");
+ const base::string16 kDescription = ASCIIToUTF16("description");
+
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ // Initialize the tracker with a QuotaManagerProxy
+ scoped_refptr<TestQuotaManagerProxy> test_quota_proxy(
+ new TestQuotaManagerProxy);
+ scoped_refptr<DatabaseTracker> tracker(
+ new DatabaseTracker(temp_dir.path(),
+ false /* incognito */,
+ NULL,
+ test_quota_proxy.get(),
+ NULL));
+ EXPECT_TRUE(test_quota_proxy->registered_client_);
+
+ // Create a database and modify it a couple of times, close it,
+ // then delete it. Observe the tracker notifies accordingly.
+
+ int64 database_size = 0;
+ tracker->DatabaseOpened(kOriginId, kName, kDescription, 0,
+ &database_size);
+ EXPECT_TRUE(test_quota_proxy->WasAccessNotified(kOrigin));
+ test_quota_proxy->reset();
+
+ base::FilePath db_file(tracker->GetFullDBFilePath(kOriginId, kName));
+ EXPECT_TRUE(base::CreateDirectory(db_file.DirName()));
+ EXPECT_TRUE(EnsureFileOfSize(db_file, 10));
+ tracker->DatabaseModified(kOriginId, kName);
+ EXPECT_TRUE(test_quota_proxy->WasModificationNotified(kOrigin, 10));
+ test_quota_proxy->reset();
+
+ EXPECT_TRUE(EnsureFileOfSize(db_file, 100));
+ tracker->DatabaseModified(kOriginId, kName);
+ EXPECT_TRUE(test_quota_proxy->WasModificationNotified(kOrigin, 90));
+ test_quota_proxy->reset();
+
+ tracker->DatabaseClosed(kOriginId, kName);
+ EXPECT_TRUE(test_quota_proxy->WasAccessNotified(kOrigin));
+ EXPECT_EQ(net::OK, tracker->DeleteDatabase(
+ kOriginId, kName, net::CompletionCallback()));
+ EXPECT_TRUE(test_quota_proxy->WasModificationNotified(kOrigin, -100));
+ test_quota_proxy->reset();
+
+ // Create a database and modify it, try to delete it while open,
+ // then close it (at which time deletion will actually occur).
+ // Observe the tracker notifies accordingly.
+
+ tracker->DatabaseOpened(kOriginId, kName, kDescription, 0,
+ &database_size);
+ EXPECT_TRUE(test_quota_proxy->WasAccessNotified(kOrigin));
+ test_quota_proxy->reset();
+
+ db_file = tracker->GetFullDBFilePath(kOriginId, kName);
+ EXPECT_TRUE(base::CreateDirectory(db_file.DirName()));
+ EXPECT_TRUE(EnsureFileOfSize(db_file, 100));
+ tracker->DatabaseModified(kOriginId, kName);
+ EXPECT_TRUE(test_quota_proxy->WasModificationNotified(kOrigin, 100));
+ test_quota_proxy->reset();
+
+ EXPECT_EQ(net::ERR_IO_PENDING,
+ tracker->DeleteDatabase(kOriginId, kName,
+ net::CompletionCallback()));
+ EXPECT_FALSE(test_quota_proxy->WasModificationNotified(kOrigin, -100));
+
+ tracker->DatabaseClosed(kOriginId, kName);
+ EXPECT_TRUE(test_quota_proxy->WasAccessNotified(kOrigin));
+ EXPECT_TRUE(test_quota_proxy->WasModificationNotified(kOrigin, -100));
+ test_quota_proxy->reset();
+
+ // Create a database and up the file size without telling
+ // the tracker about the modification, than simulate a
+ // a renderer crash.
+ // Observe the tracker notifies accordingly.
+
+ tracker->DatabaseOpened(kOriginId, kName, kDescription, 0,
+ &database_size);
+ EXPECT_TRUE(test_quota_proxy->WasAccessNotified(kOrigin));
+ test_quota_proxy->reset();
+ db_file = tracker->GetFullDBFilePath(kOriginId, kName);
+ EXPECT_TRUE(base::CreateDirectory(db_file.DirName()));
+ EXPECT_TRUE(EnsureFileOfSize(db_file, 100));
+ DatabaseConnections crashed_renderer_connections;
+ crashed_renderer_connections.AddConnection(kOriginId, kName);
+ EXPECT_FALSE(test_quota_proxy->WasModificationNotified(kOrigin, 100));
+ tracker->CloseDatabases(crashed_renderer_connections);
+ EXPECT_TRUE(test_quota_proxy->WasModificationNotified(kOrigin, 100));
+
+ // Cleanup.
+ crashed_renderer_connections.RemoveAllConnections();
+ test_quota_proxy->SimulateQuotaManagerDestroyed();
+ }
+
+ static void DatabaseTrackerClearSessionOnlyDatabasesOnExit() {
+ int64 database_size = 0;
+ const std::string kOrigin1 =
+ webkit_database::GetIdentifierFromOrigin(GURL(kOrigin1Url));
+ const std::string kOrigin2 =
+ webkit_database::GetIdentifierFromOrigin(GURL(kOrigin2Url));
+ const base::string16 kDB1 = ASCIIToUTF16("db1");
+ const base::string16 kDB2 = ASCIIToUTF16("db2");
+ const base::string16 kDescription = ASCIIToUTF16("database_description");
+
+ // Initialize the tracker database.
+ base::MessageLoop message_loop;
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ base::FilePath origin1_db_dir;
+ base::FilePath origin2_db_dir;
+ {
+ scoped_refptr<MockSpecialStoragePolicy> special_storage_policy =
+ new MockSpecialStoragePolicy;
+ special_storage_policy->AddSessionOnly(GURL(kOrigin2Url));
+ scoped_refptr<DatabaseTracker> tracker(
+ new DatabaseTracker(temp_dir.path(),
+ false,
+ special_storage_policy.get(),
+ NULL,
+ base::MessageLoopProxy::current().get()));
+
+ // Open two new databases.
+ tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0,
+ &database_size);
+ EXPECT_EQ(0, database_size);
+ tracker->DatabaseOpened(kOrigin2, kDB2, kDescription, 0,
+ &database_size);
+ EXPECT_EQ(0, database_size);
+
+ // Write some data to each file.
+ base::FilePath db_file;
+ db_file = tracker->GetFullDBFilePath(kOrigin1, kDB1);
+ EXPECT_TRUE(base::CreateDirectory(db_file.DirName()));
+ EXPECT_TRUE(EnsureFileOfSize(db_file, 1));
+
+ db_file = tracker->GetFullDBFilePath(kOrigin2, kDB2);
+ EXPECT_TRUE(base::CreateDirectory(db_file.DirName()));
+ EXPECT_TRUE(EnsureFileOfSize(db_file, 2));
+
+ // Store the origin database directories as long as they still exist.
+ origin1_db_dir = tracker->GetFullDBFilePath(kOrigin1, kDB1).DirName();
+ origin2_db_dir = tracker->GetFullDBFilePath(kOrigin2, kDB2).DirName();
+
+ tracker->DatabaseModified(kOrigin1, kDB1);
+ tracker->DatabaseModified(kOrigin2, kDB2);
+
+ // Close all databases.
+ tracker->DatabaseClosed(kOrigin1, kDB1);
+ tracker->DatabaseClosed(kOrigin2, kDB2);
+
+ tracker->Shutdown();
+ }
+
+ // At this point, the database tracker should be gone. Create a new one.
+ scoped_refptr<DatabaseTracker> tracker(
+ new DatabaseTracker(temp_dir.path(), false, NULL, NULL, NULL));
+
+ // Get all data for all origins.
+ std::vector<OriginInfo> origins_info;
+ EXPECT_TRUE(tracker->GetAllOriginsInfo(&origins_info));
+ // kOrigin1 was not session-only, so it survived. kOrigin2 was session-only
+ // and it got deleted.
+ EXPECT_EQ(size_t(1), origins_info.size());
+ EXPECT_EQ(kOrigin1, origins_info[0].GetOriginIdentifier());
+ EXPECT_TRUE(
+ base::PathExists(tracker->GetFullDBFilePath(kOrigin1, kDB1)));
+ EXPECT_EQ(base::FilePath(), tracker->GetFullDBFilePath(kOrigin2, kDB2));
+
+ // The origin directory of kOrigin1 remains, but the origin directory of
+ // kOrigin2 is deleted.
+ EXPECT_TRUE(base::PathExists(origin1_db_dir));
+ EXPECT_FALSE(base::PathExists(origin2_db_dir));
+ }
+
+ static void DatabaseTrackerSetForceKeepSessionState() {
+ int64 database_size = 0;
+ const std::string kOrigin1 =
+ webkit_database::GetIdentifierFromOrigin(GURL(kOrigin1Url));
+ const std::string kOrigin2 =
+ webkit_database::GetIdentifierFromOrigin(GURL(kOrigin2Url));
+ const base::string16 kDB1 = ASCIIToUTF16("db1");
+ const base::string16 kDB2 = ASCIIToUTF16("db2");
+ const base::string16 kDescription = ASCIIToUTF16("database_description");
+
+ // Initialize the tracker database.
+ base::MessageLoop message_loop;
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ base::FilePath origin1_db_dir;
+ base::FilePath origin2_db_dir;
+ {
+ scoped_refptr<MockSpecialStoragePolicy> special_storage_policy =
+ new MockSpecialStoragePolicy;
+ special_storage_policy->AddSessionOnly(GURL(kOrigin2Url));
+ scoped_refptr<DatabaseTracker> tracker(
+ new DatabaseTracker(temp_dir.path(),
+ false,
+ special_storage_policy.get(),
+ NULL,
+ base::MessageLoopProxy::current().get()));
+ tracker->SetForceKeepSessionState();
+
+ // Open two new databases.
+ tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0,
+ &database_size);
+ EXPECT_EQ(0, database_size);
+ tracker->DatabaseOpened(kOrigin2, kDB2, kDescription, 0,
+ &database_size);
+ EXPECT_EQ(0, database_size);
+
+ // Write some data to each file.
+ base::FilePath db_file;
+ db_file = tracker->GetFullDBFilePath(kOrigin1, kDB1);
+ EXPECT_TRUE(base::CreateDirectory(db_file.DirName()));
+ EXPECT_TRUE(EnsureFileOfSize(db_file, 1));
+
+ db_file = tracker->GetFullDBFilePath(kOrigin2, kDB2);
+ EXPECT_TRUE(base::CreateDirectory(db_file.DirName()));
+ EXPECT_TRUE(EnsureFileOfSize(db_file, 2));
+
+ // Store the origin database directories as long as they still exist.
+ origin1_db_dir = tracker->GetFullDBFilePath(kOrigin1, kDB1).DirName();
+ origin2_db_dir = tracker->GetFullDBFilePath(kOrigin2, kDB2).DirName();
+
+ tracker->DatabaseModified(kOrigin1, kDB1);
+ tracker->DatabaseModified(kOrigin2, kDB2);
+
+ // Close all databases.
+ tracker->DatabaseClosed(kOrigin1, kDB1);
+ tracker->DatabaseClosed(kOrigin2, kDB2);
+
+ tracker->Shutdown();
+ }
+
+ // At this point, the database tracker should be gone. Create a new one.
+ scoped_refptr<DatabaseTracker> tracker(
+ new DatabaseTracker(temp_dir.path(), false, NULL, NULL, NULL));
+
+ // Get all data for all origins.
+ std::vector<OriginInfo> origins_info;
+ EXPECT_TRUE(tracker->GetAllOriginsInfo(&origins_info));
+ // No origins were deleted.
+ EXPECT_EQ(size_t(2), origins_info.size());
+ EXPECT_TRUE(
+ base::PathExists(tracker->GetFullDBFilePath(kOrigin1, kDB1)));
+ EXPECT_TRUE(
+ base::PathExists(tracker->GetFullDBFilePath(kOrigin2, kDB2)));
+
+ EXPECT_TRUE(base::PathExists(origin1_db_dir));
+ EXPECT_TRUE(base::PathExists(origin2_db_dir));
+ }
+
+ static void EmptyDatabaseNameIsValid() {
+ const GURL kOrigin(kOrigin1Url);
+ const std::string kOriginId =
+ webkit_database::GetIdentifierFromOrigin(kOrigin);
+ const base::string16 kEmptyName;
+ const base::string16 kDescription(ASCIIToUTF16("description"));
+ const base::string16 kChangedDescription(
+ ASCIIToUTF16("changed_description"));
+
+ // Initialize a tracker database, no need to put it on disk.
+ const bool kUseInMemoryTrackerDatabase = true;
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ scoped_refptr<DatabaseTracker> tracker(
+ new DatabaseTracker(temp_dir.path(), kUseInMemoryTrackerDatabase,
+ NULL, NULL, NULL));
+
+ // Starts off with no databases.
+ std::vector<OriginInfo> infos;
+ EXPECT_TRUE(tracker->GetAllOriginsInfo(&infos));
+ EXPECT_TRUE(infos.empty());
+
+ // Create a db with an empty name.
+ int64 database_size = -1;
+ tracker->DatabaseOpened(kOriginId, kEmptyName, kDescription, 0,
+ &database_size);
+ EXPECT_EQ(0, database_size);
+ tracker->DatabaseModified(kOriginId, kEmptyName);
+ EXPECT_TRUE(tracker->GetAllOriginsInfo(&infos));
+ EXPECT_EQ(1u, infos.size());
+ EXPECT_EQ(kDescription, infos[0].GetDatabaseDescription(kEmptyName));
+ EXPECT_FALSE(tracker->GetFullDBFilePath(kOriginId, kEmptyName).empty());
+ tracker->DatabaseOpened(kOriginId, kEmptyName, kChangedDescription, 0,
+ &database_size);
+ infos.clear();
+ EXPECT_TRUE(tracker->GetAllOriginsInfo(&infos));
+ EXPECT_EQ(1u, infos.size());
+ EXPECT_EQ(kChangedDescription, infos[0].GetDatabaseDescription(kEmptyName));
+ tracker->DatabaseClosed(kOriginId, kEmptyName);
+ tracker->DatabaseClosed(kOriginId, kEmptyName);
+
+ // Deleting it should return to the initial state.
+ EXPECT_EQ(net::OK, tracker->DeleteDatabase(kOriginId, kEmptyName,
+ net::CompletionCallback()));
+ infos.clear();
+ EXPECT_TRUE(tracker->GetAllOriginsInfo(&infos));
+ EXPECT_TRUE(infos.empty());
+ }
+
+ static void HandleSqliteError() {
+ const GURL kOrigin(kOrigin1Url);
+ const std::string kOriginId =
+ webkit_database::GetIdentifierFromOrigin(kOrigin);
+ const base::string16 kName(ASCIIToUTF16("name"));
+ const base::string16 kDescription(ASCIIToUTF16("description"));
+
+ // Initialize a tracker database, no need to put it on disk.
+ const bool kUseInMemoryTrackerDatabase = true;
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ scoped_refptr<DatabaseTracker> tracker(
+ new DatabaseTracker(temp_dir.path(), kUseInMemoryTrackerDatabase,
+ NULL, NULL, NULL));
+
+ // Setup to observe OnScheduledForDelete notifications.
+ TestObserver observer(false, true);
+ tracker->AddObserver(&observer);
+
+ // Verify does no harm when there is no such database.
+ tracker->HandleSqliteError(kOriginId, kName, SQLITE_CORRUPT);
+ EXPECT_FALSE(tracker->IsDatabaseScheduledForDeletion(kOriginId, kName));
+ EXPECT_FALSE(observer.DidReceiveNewNotification());
+
+ // --------------------------------------------------------
+ // Create a record of a database in the tracker db and create
+ // a spoof_db_file on disk in the expected location.
+ int64 database_size = 0;
+ tracker->DatabaseOpened(kOriginId, kName, kDescription, 0,
+ &database_size);
+ base::FilePath spoof_db_file = tracker->GetFullDBFilePath(kOriginId, kName);
+ EXPECT_FALSE(tracker->GetFullDBFilePath(kOriginId, kName).empty());
+ EXPECT_TRUE(base::CreateDirectory(spoof_db_file.DirName()));
+ EXPECT_TRUE(EnsureFileOfSize(spoof_db_file, 1));
+
+ // Verify does no harm with a non-error is reported.
+ tracker->HandleSqliteError(kOriginId, kName, SQLITE_OK);
+ EXPECT_FALSE(tracker->IsDatabaseScheduledForDeletion(kOriginId, kName));
+ EXPECT_FALSE(observer.DidReceiveNewNotification());
+
+ // Verify that with a connection open, the db is scheduled for deletion,
+ // but that the file still exists.
+ tracker->HandleSqliteError(kOriginId, kName, SQLITE_CORRUPT);
+ EXPECT_TRUE(tracker->IsDatabaseScheduledForDeletion(kOriginId, kName));
+ EXPECT_TRUE(observer.DidReceiveNewNotification());
+ EXPECT_TRUE(base::PathExists(spoof_db_file));
+
+ // Verify that once closed, the file is deleted and the record in the
+ // tracker db is removed.
+ tracker->DatabaseClosed(kOriginId, kName);
+ EXPECT_FALSE(base::PathExists(spoof_db_file));
+ EXPECT_TRUE(tracker->GetFullDBFilePath(kOriginId, kName).empty());
+
+ // --------------------------------------------------------
+ // Create another record of a database in the tracker db and create
+ // a spoof_db_file on disk in the expected location.
+ tracker->DatabaseOpened(kOriginId, kName, kDescription, 0,
+ &database_size);
+ base::FilePath spoof_db_file2 = tracker->GetFullDBFilePath(kOriginId,
+ kName);
+ EXPECT_FALSE(tracker->GetFullDBFilePath(kOriginId, kName).empty());
+ EXPECT_NE(spoof_db_file, spoof_db_file2);
+ EXPECT_TRUE(base::CreateDirectory(spoof_db_file2.DirName()));
+ EXPECT_TRUE(EnsureFileOfSize(spoof_db_file2, 1));
+
+ // Verify that with no connection open, the db is deleted immediately.
+ tracker->DatabaseClosed(kOriginId, kName);
+ tracker->HandleSqliteError(kOriginId, kName, SQLITE_CORRUPT);
+ EXPECT_FALSE(tracker->IsDatabaseScheduledForDeletion(kOriginId, kName));
+ EXPECT_FALSE(observer.DidReceiveNewNotification());
+ EXPECT_TRUE(tracker->GetFullDBFilePath(kOriginId, kName).empty());
+ EXPECT_FALSE(base::PathExists(spoof_db_file2));
+
+ tracker->RemoveObserver(&observer);
+ }
+};
+
+TEST(DatabaseTrackerTest, DeleteOpenDatabase) {
+ DatabaseTracker_TestHelper_Test::TestDeleteOpenDatabase(false);
+}
+
+TEST(DatabaseTrackerTest, DeleteOpenDatabaseIncognitoMode) {
+ DatabaseTracker_TestHelper_Test::TestDeleteOpenDatabase(true);
+}
+
+TEST(DatabaseTrackerTest, DatabaseTracker) {
+ DatabaseTracker_TestHelper_Test::TestDatabaseTracker(false);
+}
+
+TEST(DatabaseTrackerTest, DatabaseTrackerIncognitoMode) {
+ DatabaseTracker_TestHelper_Test::TestDatabaseTracker(true);
+}
+
+TEST(DatabaseTrackerTest, DatabaseTrackerQuotaIntegration) {
+ // There is no difference in behavior between incognito and not.
+ DatabaseTracker_TestHelper_Test::DatabaseTrackerQuotaIntegration();
+}
+
+TEST(DatabaseTrackerTest, DatabaseTrackerClearSessionOnlyDatabasesOnExit) {
+ // Only works for regular mode.
+ DatabaseTracker_TestHelper_Test::
+ DatabaseTrackerClearSessionOnlyDatabasesOnExit();
+}
+
+TEST(DatabaseTrackerTest, DatabaseTrackerSetForceKeepSessionState) {
+ // Only works for regular mode.
+ DatabaseTracker_TestHelper_Test::DatabaseTrackerSetForceKeepSessionState();
+}
+
+TEST(DatabaseTrackerTest, EmptyDatabaseNameIsValid) {
+ DatabaseTracker_TestHelper_Test::EmptyDatabaseNameIsValid();
+}
+
+TEST(DatabaseTrackerTest, HandleSqliteError) {
+ DatabaseTracker_TestHelper_Test::HandleSqliteError();
+}
+
+} // namespace content
diff --git a/chromium/content/browser/database_util_unittest.cc b/chromium/content/browser/database_util_unittest.cc
new file mode 100644
index 00000000000..23f0a47900d
--- /dev/null
+++ b/chromium/content/browser/database_util_unittest.cc
@@ -0,0 +1,79 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/string_piece.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/database/database_util.h"
+#include "webkit/common/database/database_identifier.h"
+
+using base::ASCIIToUTF16;
+using webkit_database::DatabaseUtil;
+
+static void TestVfsFilePath(bool expected_result,
+ const char* vfs_file_name,
+ const char* expected_origin_identifier = "",
+ const char* expected_database_name = "",
+ const char* expected_sqlite_suffix = "") {
+ std::string origin_identifier;
+ base::string16 database_name;
+ base::string16 sqlite_suffix;
+ EXPECT_EQ(expected_result,
+ DatabaseUtil::CrackVfsFileName(ASCIIToUTF16(vfs_file_name),
+ &origin_identifier,
+ &database_name,
+ &sqlite_suffix));
+ EXPECT_EQ(expected_origin_identifier, origin_identifier);
+ EXPECT_EQ(ASCIIToUTF16(expected_database_name), database_name);
+ EXPECT_EQ(ASCIIToUTF16(expected_sqlite_suffix), sqlite_suffix);
+}
+
+static GURL ToAndFromOriginIdentifier(const GURL origin_url) {
+ std::string id = webkit_database::GetIdentifierFromOrigin(origin_url);
+ return webkit_database::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.
+TEST(DatabaseUtilTest, CrackVfsFilePathTest) {
+ TestVfsFilePath(true, "http_origin_0/#", "http_origin_0", "", "");
+ TestVfsFilePath(true,
+ "http_origin_0/#suffix", "http_origin_0", "", "suffix");
+ TestVfsFilePath(true,
+ "http_origin_0/db_name#", "http_origin_0", "db_name", "");
+ TestVfsFilePath(true,
+ "http_origin_0/db_name#suffix", "http_origin_0", "db_name", "suffix");
+ TestVfsFilePath(false, "http_origin_0db_name#");
+ TestVfsFilePath(false, "http_origin_0db_name#suffix");
+ TestVfsFilePath(false, "http_origin_0/db_name");
+ TestVfsFilePath(false, "http_origin_0#db_name/suffix");
+ TestVfsFilePath(false, "/db_name#");
+ 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/databases_table_unittest.cc b/chromium/content/browser/databases_table_unittest.cc
new file mode 100644
index 00000000000..b82176fe247
--- /dev/null
+++ b/chromium/content/browser/databases_table_unittest.cc
@@ -0,0 +1,152 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "sql/connection.h"
+#include "sql/statement.h"
+#include "sql/test/scoped_error_ignorer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/sqlite/sqlite3.h"
+#include "webkit/browser/database/databases_table.h"
+
+using base::ASCIIToUTF16;
+using webkit_database::DatabaseDetails;
+using webkit_database::DatabasesTable;
+
+namespace content {
+
+static void CheckDetailsAreEqual(const DatabaseDetails& d1,
+ const DatabaseDetails& d2) {
+ EXPECT_EQ(d1.origin_identifier, d2.origin_identifier);
+ EXPECT_EQ(d1.database_name, d2.database_name);
+ EXPECT_EQ(d1.description, d2.description);
+ EXPECT_EQ(d1.estimated_size, d2.estimated_size);
+}
+
+static bool DatabasesTableIsEmpty(sql::Connection* db) {
+ sql::Statement statement(db->GetCachedStatement(
+ SQL_FROM_HERE, "SELECT COUNT(*) FROM Databases"));
+ return (statement.is_valid() && statement.Step() && !statement.ColumnInt(0));
+}
+
+TEST(DatabasesTableTest, TestIt) {
+ // Initialize the 'Databases' table.
+ sql::Connection db;
+
+ sql::ScopedErrorIgnorer ignore_errors;
+ // TODO(shess): Suppressing SQLITE_CONSTRAINT because the code
+ // expects that and handles the resulting error. Consider revising
+ // the code to use INSERT OR IGNORE (which would not throw
+ // SQLITE_CONSTRAINT) and then check ChangeCount() to see if any
+ // changes were made.
+ ignore_errors.IgnoreError(SQLITE_CONSTRAINT);
+
+ // Initialize the temp dir and the 'Databases' table.
+ EXPECT_TRUE(db.OpenInMemory());
+ DatabasesTable databases_table(&db);
+ EXPECT_TRUE(databases_table.Init());
+
+ // The 'Databases' table should be empty.
+ EXPECT_TRUE(DatabasesTableIsEmpty(&db));
+
+ // Create the details for a databases.
+ DatabaseDetails details_in1;
+ DatabaseDetails details_out1;
+ details_in1.origin_identifier = "origin1";
+ details_in1.database_name = ASCIIToUTF16("db1");
+ details_in1.description = ASCIIToUTF16("description_db1");
+ details_in1.estimated_size = 100;
+
+ // Updating details for this database should fail.
+ EXPECT_FALSE(databases_table.UpdateDatabaseDetails(details_in1));
+ EXPECT_FALSE(databases_table.GetDatabaseDetails(
+ details_in1.origin_identifier,
+ details_in1.database_name,
+ &details_out1));
+
+ // Inserting details for this database should pass.
+ EXPECT_TRUE(databases_table.InsertDatabaseDetails(details_in1));
+ EXPECT_TRUE(databases_table.GetDatabaseDetails(
+ details_in1.origin_identifier,
+ details_in1.database_name,
+ &details_out1));
+ EXPECT_EQ(1, databases_table.GetDatabaseID(details_in1.origin_identifier,
+ details_in1.database_name));
+
+ // Check that the details were correctly written to the database.
+ CheckDetailsAreEqual(details_in1, details_out1);
+
+ // Check that inserting a duplicate row fails.
+ EXPECT_FALSE(databases_table.InsertDatabaseDetails(details_in1));
+
+ // Insert details for another database with the same origin.
+ DatabaseDetails details_in2;
+ details_in2.origin_identifier = "origin1";
+ details_in2.database_name = ASCIIToUTF16("db2");
+ details_in2.description = ASCIIToUTF16("description_db2");
+ details_in2.estimated_size = 200;
+ EXPECT_TRUE(databases_table.InsertDatabaseDetails(details_in2));
+ EXPECT_EQ(2, databases_table.GetDatabaseID(details_in2.origin_identifier,
+ details_in2.database_name));
+
+ // Insert details for a third database with a different origin.
+ DatabaseDetails details_in3;
+ details_in3.origin_identifier = "origin2";
+ details_in3.database_name = ASCIIToUTF16("db3");
+ details_in3.description = ASCIIToUTF16("description_db3");
+ details_in3.estimated_size = 300;
+ EXPECT_TRUE(databases_table.InsertDatabaseDetails(details_in3));
+ EXPECT_EQ(3, databases_table.GetDatabaseID(details_in3.origin_identifier,
+ details_in3.database_name));
+
+ // There should be no database with origin "origin3".
+ std::vector<DatabaseDetails> details_out_origin3;
+ EXPECT_TRUE(databases_table.GetAllDatabaseDetailsForOriginIdentifier(
+ "origin3", &details_out_origin3));
+ EXPECT_TRUE(details_out_origin3.empty());
+
+ // There should be only two databases with origin "origin1".
+ std::vector<DatabaseDetails> details_out_origin1;
+ EXPECT_TRUE(databases_table.GetAllDatabaseDetailsForOriginIdentifier(
+ details_in1.origin_identifier, &details_out_origin1));
+ EXPECT_EQ(size_t(2), details_out_origin1.size());
+ CheckDetailsAreEqual(details_in1, details_out_origin1[0]);
+ CheckDetailsAreEqual(details_in2, details_out_origin1[1]);
+
+ // Get the list of all origins: should be "origin1" and "origin2".
+ std::vector<std::string> origins_out;
+ EXPECT_TRUE(databases_table.GetAllOriginIdentifiers(&origins_out));
+ EXPECT_EQ(size_t(2), origins_out.size());
+ EXPECT_EQ(details_in1.origin_identifier, origins_out[0]);
+ EXPECT_EQ(details_in3.origin_identifier, origins_out[1]);
+
+ // Delete an origin and check that it's no longer in the table.
+ origins_out.clear();
+ EXPECT_TRUE(databases_table.DeleteOriginIdentifier(
+ details_in3.origin_identifier));
+ EXPECT_TRUE(databases_table.GetAllOriginIdentifiers(&origins_out));
+ EXPECT_EQ(size_t(1), origins_out.size());
+ EXPECT_EQ(details_in1.origin_identifier, origins_out[0]);
+
+ // Deleting an origin that doesn't have any record in this table should fail.
+ EXPECT_FALSE(databases_table.DeleteOriginIdentifier("unknown_origin"));
+
+ // Delete the details for 'db1' and check that they're no longer there.
+ EXPECT_TRUE(databases_table.DeleteDatabaseDetails(
+ details_in1.origin_identifier, details_in1.database_name));
+ EXPECT_FALSE(databases_table.GetDatabaseDetails(
+ details_in1.origin_identifier,
+ details_in1.database_name,
+ &details_out1));
+
+ // Check that trying to delete a record that doesn't exist fails.
+ EXPECT_FALSE(databases_table.DeleteDatabaseDetails(
+ "unknown_origin", ASCIIToUTF16("unknown_database")));
+
+ ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
+}
+
+} // namespace content
diff --git a/chromium/content/browser/device_monitor_mac.h b/chromium/content/browser/device_monitor_mac.h
index 6def493a81f..b3d3f9f6dc3 100644
--- a/chromium/content/browser/device_monitor_mac.h
+++ b/chromium/content/browser/device_monitor_mac.h
@@ -7,6 +7,7 @@
#include "base/basictypes.h"
#include "base/system_monitor/system_monitor.h"
+#include "base/threading/thread_checker.h"
namespace {
class DeviceMonitorMacImpl;
@@ -22,6 +23,13 @@ class DeviceMonitorMac {
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<base::SingleThreadTaskRunner>& 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
@@ -31,6 +39,10 @@ class DeviceMonitorMac {
private:
scoped_ptr<DeviceMonitorMacImpl> 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);
};
diff --git a/chromium/content/browser/device_monitor_mac.mm b/chromium/content/browser/device_monitor_mac.mm
index ebc0f77de02..eb429afe640 100644
--- a/chromium/content/browser/device_monitor_mac.mm
+++ b/chromium/content/browser/device_monitor_mac.mm
@@ -6,7 +6,13 @@
#import <QTKit/QTKit.h>
+#include <set>
+
+#include "base/bind_helpers.h"
#include "base/logging.h"
+#include "base/mac/scoped_nsobject.h"
+#include "base/threading/thread_checker.h"
+#include "content/public/browser/browser_thread.h"
#import "media/video/capture/mac/avfoundation_glue.h"
namespace {
@@ -203,17 +209,142 @@ void QTKitMonitorImpl::OnDeviceChanged() {
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 Device Thread by SuspendedObserverDelegate.
+@interface CrAVFoundationDeviceObserver : NSObject {
+ @private
+ SuspendObserverDelegate* receiver_; // weak
+ // Member to keep track of the devices we are already monitoring.
+ std::set<CrAVCaptureDevice*> monitoredDevices_;
+}
+
+- (id)initWithChangeReceiver:(SuspendObserverDelegate*)receiver;
+- (void)startObserving:(CrAVCaptureDevice*)device;
+- (void)stopObserving:(CrAVCaptureDevice*)device;
+
+@end
+
+namespace {
+
+// This class owns and manages the lifetime of a CrAVFoundationDeviceObserver.
+// Provides a callback for this device observer to indicate that there has been
+// a device change of some kind. Created by AVFoundationMonitorImpl in UI thread
+// but living in Device Thread.
+class SuspendObserverDelegate :
+ public base::RefCountedThreadSafe<SuspendObserverDelegate> {
+ public:
+ explicit SuspendObserverDelegate(DeviceMonitorMacImpl* monitor)
+ : avfoundation_monitor_impl_(monitor) {
+ device_thread_checker_.DetachFromThread();
+ }
+
+ void OnDeviceChanged();
+ void StartObserver();
+ void ResetDeviceMonitorOnUIThread();
+
+ private:
+ friend class base::RefCountedThreadSafe<SuspendObserverDelegate>;
+
+ virtual ~SuspendObserverDelegate() {}
+
+ void OnDeviceChangedOnUIThread(
+ const std::vector<DeviceInfo>& snapshot_devices);
+
+ base::ThreadChecker device_thread_checker_;
+ base::scoped_nsobject<CrAVFoundationDeviceObserver> suspend_observer_;
+ DeviceMonitorMacImpl* avfoundation_monitor_impl_;
+};
+
+void SuspendObserverDelegate::OnDeviceChanged() {
+ DCHECK(device_thread_checker_.CalledOnValidThread());
+ NSArray* devices = [AVCaptureDeviceGlue devices];
+ std::vector<DeviceInfo> snapshot_devices;
+ for (CrAVCaptureDevice* device in devices) {
+ [suspend_observer_ startObserving:device];
+ 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));
+ }
+ // Post the consolidation of enumerated devices to be done on UI thread.
+ content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
+ base::Bind(&SuspendObserverDelegate::OnDeviceChangedOnUIThread,
+ this, snapshot_devices));
+}
+
+void SuspendObserverDelegate::StartObserver() {
+ DCHECK(device_thread_checker_.CalledOnValidThread());
+ suspend_observer_.reset([[CrAVFoundationDeviceObserver alloc]
+ initWithChangeReceiver:this]);
+ for (CrAVCaptureDevice* device in [AVCaptureDeviceGlue devices])
+ [suspend_observer_ startObserving:device];
+}
+
+void SuspendObserverDelegate::ResetDeviceMonitorOnUIThread() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ avfoundation_monitor_impl_ = NULL;
+}
+
+void SuspendObserverDelegate::OnDeviceChangedOnUIThread(
+ const std::vector<DeviceInfo>& snapshot_devices) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ // |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. Owns a SuspendObserverDelegate living in |device_task_runner_|
+// and gets notified when a device is suspended/resumed. This class is created
+// and lives in UI thread;
class AVFoundationMonitorImpl : public DeviceMonitorMacImpl {
public:
- explicit AVFoundationMonitorImpl(content::DeviceMonitorMac* monitor);
+ AVFoundationMonitorImpl(
+ content::DeviceMonitorMac* monitor,
+ const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner);
virtual ~AVFoundationMonitorImpl();
virtual void OnDeviceChanged() OVERRIDE;
+
+ private:
+ base::ThreadChecker thread_checker_;
+
+ // {Video,AudioInput}DeviceManager's "Device" thread task runner used for
+ // posting tasks to |suspend_observer_delegate_|; valid after
+ // MediaStreamManager calls StartMonitoring().
+ const scoped_refptr<base::SingleThreadTaskRunner> device_task_runner_;
+
+ scoped_refptr<SuspendObserverDelegate> suspend_observer_delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(AVFoundationMonitorImpl);
};
AVFoundationMonitorImpl::AVFoundationMonitorImpl(
- content::DeviceMonitorMac* monitor)
- : DeviceMonitorMacImpl(monitor) {
+ content::DeviceMonitorMac* monitor,
+ const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner)
+ : DeviceMonitorMacImpl(monitor),
+ device_task_runner_(device_task_runner),
+ suspend_observer_delegate_(new SuspendObserverDelegate(this)) {
NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
device_arrival_ =
[nc addObserverForName:AVFoundationGlue::
@@ -229,56 +360,119 @@ AVFoundationMonitorImpl::AVFoundationMonitorImpl(
queue:nil
usingBlock:^(NSNotification* notification) {
OnDeviceChanged();}];
+ device_task_runner_->PostTask(FROM_HERE,
+ base::Bind(&SuspendObserverDelegate::StartObserver,
+ suspend_observer_delegate_));
}
AVFoundationMonitorImpl::~AVFoundationMonitorImpl() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ suspend_observer_delegate_->ResetDeviceMonitorOnUIThread();
NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
[nc removeObserver:device_arrival_];
[nc removeObserver:device_removal_];
}
void AVFoundationMonitorImpl::OnDeviceChanged() {
- std::vector<DeviceInfo> snapshot_devices;
+ DCHECK(thread_checker_.CalledOnValidThread());
+ device_task_runner_->PostTask(FROM_HERE,
+ base::Bind(&SuspendObserverDelegate::OnDeviceChanged,
+ suspend_observer_delegate_));
+}
- NSArray* devices = [AVCaptureDeviceGlue devices];
- for (CrAVCaptureDevice* device in devices) {
- DeviceInfo::DeviceType device_type = DeviceInfo::kUnknown;
- if ([device hasMediaType:AVFoundationGlue::AVMediaTypeVideo()]) {
- device_type = DeviceInfo::kVideo;
- } else if ([device hasMediaType:AVFoundationGlue::AVMediaTypeMuxed()]) {
- device_type = DeviceInfo::kMuxed;
- } else if ([device hasMediaType:AVFoundationGlue::AVMediaTypeAudio()]) {
- device_type = DeviceInfo::kAudio;
- }
- snapshot_devices.push_back(DeviceInfo([[device uniqueID] UTF8String],
- device_type));
+} // namespace
+
+@implementation CrAVFoundationDeviceObserver
+
+- (id)initWithChangeReceiver:(SuspendObserverDelegate*)receiver {
+ if ((self = [super init])) {
+ DCHECK(receiver != NULL);
+ receiver_ = receiver;
}
- ConsolidateDevicesListAndNotify(snapshot_devices);
+ return self;
}
-} // namespace
+- (void)dealloc {
+ std::set<CrAVCaptureDevice*>::iterator it = monitoredDevices_.begin();
+ while (it != monitoredDevices_.end())
+ [self stopObserving:*it++];
+ [super dealloc];
+}
+
+- (void)startObserving:(CrAVCaptureDevice*)device {
+ 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];
+ [device addObserver:self
+ forKeyPath:@"connected"
+ options:0
+ context:device];
+ monitoredDevices_.insert(device);
+}
+
+- (void)stopObserving:(CrAVCaptureDevice*)device {
+ DCHECK(device != nil);
+ std::set<CrAVCaptureDevice*>::iterator found =
+ std::find(monitoredDevices_.begin(), monitoredDevices_.end(), device);
+ DCHECK(found != monitoredDevices_.end());
+ // Every so seldom, |device| might be gone when getting here, in that case
+ // removing the observer causes a crash. Try to avoid it by checking sanity of
+ // the |device| via its -observationInfo. http://crbug.com/371271.
+ if ([device observationInfo]) {
+ [device removeObserver:self
+ forKeyPath:@"suspended"];
+ [device removeObserver:self
+ forKeyPath:@"connected"];
+ }
+ monitoredDevices_.erase(found);
+}
+
+- (void)observeValueForKeyPath:(NSString*)keyPath
+ ofObject:(id)object
+ change:(NSDictionary*)change
+ context:(void*)context {
+ if ([keyPath isEqual:@"suspended"])
+ receiver_->OnDeviceChanged();
+ if ([keyPath isEqual:@"connected"])
+ [self stopObserving:static_cast<CrAVCaptureDevice*>(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<base::SingleThreadTaskRunner>& device_task_runner) {
+ DCHECK(thread_checker_.CalledOnValidThread());
if (AVFoundationGlue::IsAVFoundationSupported()) {
DVLOG(1) << "Monitoring via AVFoundation";
- device_monitor_impl_.reset(new AVFoundationMonitorImpl(this));
- // For the AVFoundation to start sending connect/disconnect notifications,
- // the AVFoundation NSBundle has to be loaded and the devices enumerated.
- // This operation seems to take in the range of hundred of ms. so should be
- // moved to the point when is needed, and that is during
- // DeviceVideoCaptureMac +getDeviceNames.
+ device_monitor_impl_.reset(new AVFoundationMonitorImpl(this,
+ device_task_runner));
} else {
DVLOG(1) << "Monitoring via QTKit";
device_monitor_impl_.reset(new QTKitMonitorImpl(this));
}
}
-DeviceMonitorMac::~DeviceMonitorMac() {}
-
void DeviceMonitorMac::NotifyDeviceChanged(
base::SystemMonitor::DeviceType type) {
+ DCHECK(thread_checker_.CalledOnValidThread());
// TODO(xians): Remove the global variable for SystemMonitor.
base::SystemMonitor::Get()->ProcessDevicesChanged(type);
}
diff --git a/chromium/content/browser/device_orientation/device_data.h b/chromium/content/browser/device_orientation/device_data.h
deleted file mode 100644
index e8c79add64c..00000000000
--- a/chromium/content/browser/device_orientation/device_data.h
+++ /dev/null
@@ -1,43 +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_ORIENTATION_DEVICE_DATA_H_
-#define CONTENT_BROWSER_DEVICE_ORIENTATION_DEVICE_DATA_H_
-
-#include "base/memory/ref_counted.h"
-#include "content/common/content_export.h"
-
-namespace IPC {
-class Message;
-}
-
-namespace content {
-
-class CONTENT_EXPORT DeviceData :
- public base::RefCountedThreadSafe<DeviceData> {
- public:
- // TODO(timvolodine): move the DeviceData::Type enum to the service class
- // once it is implemented.
- enum Type {
- kTypeOrientation = 0,
- kTypeMotion = 1,
- kTypeTest = 100
- };
-
- virtual IPC::Message* CreateIPCMessage(int render_view_id) const = 0;
- virtual bool ShouldFireEvent(const DeviceData* other) const = 0;
-
- protected:
- DeviceData() {}
- virtual ~DeviceData() {}
-
- private:
- friend class base::RefCountedThreadSafe<DeviceData>;
-
- DISALLOW_COPY_AND_ASSIGN(DeviceData);
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_DEVICE_ORIENTATION_DEVICE_DATA_H_
diff --git a/chromium/content/browser/device_orientation/device_inertial_sensor_browsertest.cc b/chromium/content/browser/device_orientation/device_inertial_sensor_browsertest.cc
deleted file mode 100644
index 86f20f46728..00000000000
--- a/chromium/content/browser/device_orientation/device_inertial_sensor_browsertest.cc
+++ /dev/null
@@ -1,186 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/command_line.h"
-#include "base/synchronization/waitable_event.h"
-#include "content/browser/device_orientation/data_fetcher_shared_memory.h"
-#include "content/browser/device_orientation/device_inertial_sensor_service.h"
-#include "content/common/device_orientation/device_motion_hardware_buffer.h"
-#include "content/common/device_orientation/device_orientation_hardware_buffer.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/common/content_switches.h"
-#include "content/shell/browser/shell.h"
-#include "content/test/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
-
-namespace content {
-
-namespace {
-
-class FakeDataFetcher : public DataFetcherSharedMemory {
- public:
- FakeDataFetcher()
- : started_orientation_(false, false),
- stopped_orientation_(false, false),
- started_motion_(false, false),
- stopped_motion_(false, false) {
- }
- virtual ~FakeDataFetcher() { }
-
- virtual bool Start(ConsumerType consumer_type, void* buffer) OVERRIDE {
- EXPECT_TRUE(buffer);
-
- switch (consumer_type) {
- case CONSUMER_TYPE_MOTION:
- UpdateMotion(static_cast<DeviceMotionHardwareBuffer*>(buffer));
- started_motion_.Signal();
- break;
- case CONSUMER_TYPE_ORIENTATION:
- UpdateOrientation(
- static_cast<DeviceOrientationHardwareBuffer*>(buffer));
- started_orientation_.Signal();
- break;
- default:
- return false;
- }
- return true;
- }
-
- virtual bool Stop(ConsumerType consumer_type) OVERRIDE {
- switch (consumer_type) {
- case CONSUMER_TYPE_MOTION:
- stopped_motion_.Signal();
- break;
- case CONSUMER_TYPE_ORIENTATION:
- stopped_orientation_.Signal();
- break;
- default:
- return false;
- }
- return true;
- }
-
- virtual void Fetch(unsigned consumer_bitmask) OVERRIDE {
- FAIL() << "fetch should not be called";
- }
-
- virtual FetcherType GetType() const OVERRIDE {
- return FETCHER_TYPE_DEFAULT;
- }
-
- void UpdateMotion(DeviceMotionHardwareBuffer* buffer) {
- buffer->seqlock.WriteBegin();
- buffer->data.accelerationX = 1;
- buffer->data.hasAccelerationX = true;
- buffer->data.accelerationY = 2;
- buffer->data.hasAccelerationY = true;
- buffer->data.accelerationZ = 3;
- buffer->data.hasAccelerationZ = true;
-
- buffer->data.accelerationIncludingGravityX = 4;
- buffer->data.hasAccelerationIncludingGravityX = true;
- buffer->data.accelerationIncludingGravityY = 5;
- buffer->data.hasAccelerationIncludingGravityY = true;
- buffer->data.accelerationIncludingGravityZ = 6;
- buffer->data.hasAccelerationIncludingGravityZ = true;
-
- buffer->data.rotationRateAlpha = 7;
- buffer->data.hasRotationRateAlpha = true;
- buffer->data.rotationRateBeta = 8;
- buffer->data.hasRotationRateBeta = true;
- buffer->data.rotationRateGamma = 9;
- buffer->data.hasRotationRateGamma = true;
-
- buffer->data.interval = 100;
- buffer->data.allAvailableSensorsAreActive = true;
- buffer->seqlock.WriteEnd();
- }
-
- void UpdateOrientation(DeviceOrientationHardwareBuffer* buffer) {
- buffer->seqlock.WriteBegin();
- buffer->data.alpha = 1;
- buffer->data.hasAlpha = true;
- buffer->data.beta = 2;
- buffer->data.hasBeta = true;
- buffer->data.gamma = 3;
- buffer->data.hasGamma = true;
- buffer->data.allAvailableSensorsAreActive = true;
- buffer->seqlock.WriteEnd();
- }
-
- base::WaitableEvent started_orientation_;
- base::WaitableEvent stopped_orientation_;
- base::WaitableEvent started_motion_;
- base::WaitableEvent stopped_motion_;
-
- private:
-
- DISALLOW_COPY_AND_ASSIGN(FakeDataFetcher);
-};
-
-
-class DeviceInertialSensorBrowserTest : public ContentBrowserTest {
- public:
- DeviceInertialSensorBrowserTest()
- : fetcher_(NULL),
- io_loop_finished_event_(false, false) {
- }
-
- // From ContentBrowserTest.
- virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
- EXPECT_TRUE(!command_line->HasSwitch(switches::kDisableDeviceOrientation));
- EXPECT_TRUE(!command_line->HasSwitch(switches::kDisableDeviceMotion));
- }
-
- virtual void SetUpOnMainThread() OVERRIDE {
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::Bind(&DeviceInertialSensorBrowserTest::SetUpOnIOThread, this));
- io_loop_finished_event_.Wait();
- }
-
- void SetUpOnIOThread() {
- fetcher_ = new FakeDataFetcher();
- DeviceInertialSensorService::GetInstance()->
- SetDataFetcherForTests(fetcher_);
- io_loop_finished_event_.Signal();
- }
-
- FakeDataFetcher* fetcher_;
-
- private:
- base::WaitableEvent io_loop_finished_event_;
-};
-
-
-IN_PROC_BROWSER_TEST_F(DeviceInertialSensorBrowserTest, OrientationTest) {
- // The test page will register an event handler for orientation events,
- // expects to get an event with fake values, then removes the event
- // handler and navigates to #pass.
- GURL test_url = GetTestUrl(
- "device_orientation", "device_orientation_test.html");
- NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 2);
-
- EXPECT_EQ("pass", shell()->web_contents()->GetLastCommittedURL().ref());
- fetcher_->started_orientation_.Wait();
- fetcher_->stopped_orientation_.Wait();
-}
-
-IN_PROC_BROWSER_TEST_F(DeviceInertialSensorBrowserTest, MotionTest) {
- // The test page will register an event handler for motion events,
- // expects to get an event with fake values, then removes the event
- // handler and navigates to #pass.
- GURL test_url = GetTestUrl(
- "device_orientation", "device_motion_test.html");
- NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 2);
-
- EXPECT_EQ("pass", shell()->web_contents()->GetLastCommittedURL().ref());
- fetcher_->started_motion_.Wait();
- fetcher_->stopped_motion_.Wait();
-}
-
-} // namespace
-
-} // namespace content
diff --git a/chromium/content/browser/device_orientation/DEPS b/chromium/content/browser/device_sensors/DEPS
index e118614c16a..e118614c16a 100644
--- a/chromium/content/browser/device_orientation/DEPS
+++ b/chromium/content/browser/device_sensors/DEPS
diff --git a/chromium/content/browser/device_orientation/OWNERS b/chromium/content/browser/device_sensors/OWNERS
index d523461a382..d523461a382 100644
--- a/chromium/content/browser/device_orientation/OWNERS
+++ b/chromium/content/browser/device_sensors/OWNERS
diff --git a/chromium/content/browser/device_orientation/data_fetcher_shared_memory.h b/chromium/content/browser/device_sensors/data_fetcher_shared_memory.h
index 16a55fe4cd0..ec6e1a2bfbc 100644
--- a/chromium/content/browser/device_orientation/data_fetcher_shared_memory.h
+++ b/chromium/content/browser/device_sensors/data_fetcher_shared_memory.h
@@ -1,15 +1,15 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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_DEVICE_ORIENTATION_DATA_FETCHER_SHARED_MEMORY_H_
-#define CONTENT_BROWSER_DEVICE_ORIENTATION_DATA_FETCHER_SHARED_MEMORY_H_
+#ifndef CONTENT_BROWSER_DEVICE_SENSORS_DATA_FETCHER_SHARED_MEMORY_H_
+#define CONTENT_BROWSER_DEVICE_SENSORS_DATA_FETCHER_SHARED_MEMORY_H_
-#include "content/browser/device_orientation/data_fetcher_shared_memory_base.h"
+#include "content/browser/device_sensors/data_fetcher_shared_memory_base.h"
#if !defined(OS_ANDROID)
-#include "content/common/device_orientation/device_motion_hardware_buffer.h"
-#include "content/common/device_orientation/device_orientation_hardware_buffer.h"
+#include "content/common/device_sensors/device_motion_hardware_buffer.h"
+#include "content/common/device_sensors/device_orientation_hardware_buffer.h"
#endif
#if defined(OS_MACOSX)
@@ -63,4 +63,4 @@ class CONTENT_EXPORT DataFetcherSharedMemory
} // namespace content
-#endif // CONTENT_BROWSER_DEVICE_ORIENTATION_DATA_FETCHER_SHARED_MEMORY_H_
+#endif // CONTENT_BROWSER_DEVICE_SENSORS_DATA_FETCHER_SHARED_MEMORY_H_
diff --git a/chromium/content/browser/device_orientation/data_fetcher_shared_memory_android.cc b/chromium/content/browser/device_sensors/data_fetcher_shared_memory_android.cc
index 194c0c21b8d..2ed5f7fc0f9 100644
--- a/chromium/content/browser/device_orientation/data_fetcher_shared_memory_android.cc
+++ b/chromium/content/browser/device_sensors/data_fetcher_shared_memory_android.cc
@@ -1,13 +1,13 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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/device_orientation/data_fetcher_shared_memory.h"
+#include "content/browser/device_sensors/data_fetcher_shared_memory.h"
#include "base/logging.h"
-#include "content/browser/device_orientation/data_fetcher_impl_android.h"
-#include "content/common/device_orientation/device_motion_hardware_buffer.h"
-#include "content/common/device_orientation/device_orientation_hardware_buffer.h"
+#include "content/browser/device_sensors/sensor_manager_android.h"
+#include "content/common/device_sensors/device_motion_hardware_buffer.h"
+#include "content/common/device_sensors/device_orientation_hardware_buffer.h"
namespace content {
@@ -22,11 +22,11 @@ bool DataFetcherSharedMemory::Start(ConsumerType consumer_type, void* buffer) {
switch (consumer_type) {
case CONSUMER_TYPE_MOTION:
- return DataFetcherImplAndroid::GetInstance()->
+ return SensorManagerAndroid::GetInstance()->
StartFetchingDeviceMotionData(
static_cast<DeviceMotionHardwareBuffer*>(buffer));
case CONSUMER_TYPE_ORIENTATION:
- return DataFetcherImplAndroid::GetInstance()->
+ return SensorManagerAndroid::GetInstance()->
StartFetchingDeviceOrientationData(
static_cast<DeviceOrientationHardwareBuffer*>(buffer));
default:
@@ -38,11 +38,10 @@ bool DataFetcherSharedMemory::Start(ConsumerType consumer_type, void* buffer) {
bool DataFetcherSharedMemory::Stop(ConsumerType consumer_type) {
switch (consumer_type) {
case CONSUMER_TYPE_MOTION:
- DataFetcherImplAndroid::GetInstance()->StopFetchingDeviceMotionData();
+ SensorManagerAndroid::GetInstance()->StopFetchingDeviceMotionData();
return true;
case CONSUMER_TYPE_ORIENTATION:
- DataFetcherImplAndroid::GetInstance()->
- StopFetchingDeviceOrientationData();
+ SensorManagerAndroid::GetInstance()->StopFetchingDeviceOrientationData();
return true;
default:
NOTREACHED();
diff --git a/chromium/content/browser/device_orientation/data_fetcher_shared_memory_base.cc b/chromium/content/browser/device_sensors/data_fetcher_shared_memory_base.cc
index 690d1f8aa7f..6a9b7d287d3 100644
--- a/chromium/content/browser/device_orientation/data_fetcher_shared_memory_base.cc
+++ b/chromium/content/browser/device_sensors/data_fetcher_shared_memory_base.cc
@@ -1,16 +1,16 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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/device_orientation/data_fetcher_shared_memory_base.h"
+#include "content/browser/device_sensors/data_fetcher_shared_memory_base.h"
#include "base/bind.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/threading/thread.h"
#include "base/timer/timer.h"
-#include "content/common/device_orientation/device_motion_hardware_buffer.h"
-#include "content/common/device_orientation/device_orientation_hardware_buffer.h"
+#include "content/common/device_sensors/device_motion_hardware_buffer.h"
+#include "content/common/device_sensors/device_orientation_hardware_buffer.h"
namespace content {
@@ -28,7 +28,7 @@ static size_t GetConsumerSharedMemoryBufferSize(ConsumerType consumer_type) {
return 0;
}
-}
+} // namespace
class DataFetcherSharedMemoryBase::PollingThread : public base::Thread {
public:
@@ -42,7 +42,6 @@ class DataFetcherSharedMemoryBase::PollingThread : public base::Thread {
bool IsTimerRunning() const { return timer_ ? timer_->IsRunning() : false; }
private:
-
void DoPoll();
unsigned consumers_bitmask_;
@@ -89,7 +88,7 @@ void DataFetcherSharedMemoryBase::PollingThread::RemoveConsumer(
consumers_bitmask_ ^= consumer_type;
if (!consumers_bitmask_)
- timer_.reset(); // will also stop the timer.
+ timer_.reset(); // will also stop the timer.
}
void DataFetcherSharedMemoryBase::PollingThread::DoPoll() {
diff --git a/chromium/content/browser/device_orientation/data_fetcher_shared_memory_base.h b/chromium/content/browser/device_sensors/data_fetcher_shared_memory_base.h
index e0ac543f217..c959e8254fd 100644
--- a/chromium/content/browser/device_orientation/data_fetcher_shared_memory_base.h
+++ b/chromium/content/browser/device_sensors/data_fetcher_shared_memory_base.h
@@ -1,16 +1,16 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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_DEVICE_ORIENTATION_DATA_FETCHER_SHARED_MEMORY_BASE_H_
-#define CONTENT_BROWSER_DEVICE_ORIENTATION_DATA_FETCHER_SHARED_MEMORY_BASE_H_
+#ifndef CONTENT_BROWSER_DEVICE_SENSORS_DATA_FETCHER_SHARED_MEMORY_BASE_H_
+#define CONTENT_BROWSER_DEVICE_SENSORS_DATA_FETCHER_SHARED_MEMORY_BASE_H_
#include <map>
#include "base/memory/scoped_ptr.h"
#include "base/memory/shared_memory.h"
#include "base/message_loop/message_loop.h"
-#include "content/browser/device_orientation/inertial_sensor_consts.h"
+#include "content/browser/device_sensors/inertial_sensor_consts.h"
#include "content/common/content_export.h"
namespace content {
@@ -22,7 +22,6 @@ namespace content {
// polling thread to fetch data at regular intervals.
class CONTENT_EXPORT DataFetcherSharedMemoryBase {
public:
-
// Starts updating the shared memory buffer with sensor data at
// regular intervals. Returns true if the relevant sensors could
// be successfully activated.
@@ -94,6 +93,6 @@ class CONTENT_EXPORT DataFetcherSharedMemoryBase {
DISALLOW_COPY_AND_ASSIGN(DataFetcherSharedMemoryBase);
};
-}
+} // namespace content
-#endif // CONTENT_BROWSER_DEVICE_ORIENTATION_DATA_FETCHER_SHARED_MEMORY_BASE_H_
+#endif // CONTENT_BROWSER_DEVICE_SENSORS_DATA_FETCHER_SHARED_MEMORY_BASE_H_
diff --git a/chromium/content/browser/device_orientation/data_fetcher_shared_memory_base_unittest.cc b/chromium/content/browser/device_sensors/data_fetcher_shared_memory_base_unittest.cc
index dd452a073d4..3a86108b7b5 100644
--- a/chromium/content/browser/device_orientation/data_fetcher_shared_memory_base_unittest.cc
+++ b/chromium/content/browser/device_sensors/data_fetcher_shared_memory_base_unittest.cc
@@ -1,15 +1,15 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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/device_orientation/data_fetcher_shared_memory_base.h"
+#include "content/browser/device_sensors/data_fetcher_shared_memory_base.h"
#include "base/logging.h"
#include "base/process/process_handle.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
-#include "content/common/device_orientation/device_motion_hardware_buffer.h"
-#include "content/common/device_orientation/device_orientation_hardware_buffer.h"
+#include "content/common/device_sensors/device_motion_hardware_buffer.h"
+#include "content/common/device_sensors/device_orientation_hardware_buffer.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
@@ -167,7 +167,6 @@ class FakeNonPollingDataFetcher : public FakeDataFetcher {
}
private:
-
DISALLOW_COPY_AND_ASSIGN(FakeNonPollingDataFetcher);
};
@@ -225,7 +224,6 @@ class FakePollingDataFetcher : public FakeDataFetcher {
}
private:
-
DISALLOW_COPY_AND_ASSIGN(FakePollingDataFetcher);
};
@@ -280,7 +278,6 @@ class FakeZeroDelayPollingDataFetcher : public FakeDataFetcher {
}
private:
-
DISALLOW_COPY_AND_ASSIGN(FakeZeroDelayPollingDataFetcher);
};
diff --git a/chromium/content/browser/device_orientation/data_fetcher_shared_memory_default.cc b/chromium/content/browser/device_sensors/data_fetcher_shared_memory_default.cc
index ce7b5676e66..457fd8d5b47 100644
--- a/chromium/content/browser/device_orientation/data_fetcher_shared_memory_default.cc
+++ b/chromium/content/browser/device_sensors/data_fetcher_shared_memory_default.cc
@@ -1,4 +1,4 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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.
@@ -29,7 +29,7 @@ static bool SetOrientationBuffer(
return true;
}
-}
+} // namespace
namespace content {
@@ -61,7 +61,6 @@ bool DataFetcherSharedMemory::Start(ConsumerType consumer_type, void* buffer) {
}
bool DataFetcherSharedMemory::Stop(ConsumerType consumer_type) {
-
switch (consumer_type) {
case CONSUMER_TYPE_MOTION:
return SetMotionBuffer(motion_buffer_, false);
diff --git a/chromium/content/browser/device_orientation/data_fetcher_shared_memory_mac.cc b/chromium/content/browser/device_sensors/data_fetcher_shared_memory_mac.cc
index 91ffd3e1fc4..7f4934dd342 100644
--- a/chromium/content/browser/device_orientation/data_fetcher_shared_memory_mac.cc
+++ b/chromium/content/browser/device_sensors/data_fetcher_shared_memory_mac.cc
@@ -1,4 +1,4 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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.
@@ -64,9 +64,6 @@ void FetchOrientation(SuddenMotionSensor* sensor,
double beta = kRad2deg * atan2(-axis_value[1], axis_value[2]);
double gamma = kRad2deg * asin(axis_value[0]);
- // TODO(aousterh): should absolute_ be set to false here?
- // See crbug.com/136010.
-
// Make sure that the interval boundaries comply with the specification. At
// this point, beta is [-180, 180] and gamma is [-90, 90], but the spec has
// the upper bound open on both.
@@ -120,22 +117,40 @@ bool DataFetcherSharedMemory::Start(ConsumerType consumer_type, void* buffer) {
DCHECK(base::MessageLoop::current() == GetPollingMessageLoop());
DCHECK(buffer);
+ if (!sudden_motion_sensor_)
+ sudden_motion_sensor_.reset(SuddenMotionSensor::Create());
+ bool sudden_motion_sensor_available = sudden_motion_sensor_.get() != NULL;
+
switch (consumer_type) {
case CONSUMER_TYPE_MOTION:
motion_buffer_ = static_cast<DeviceMotionHardwareBuffer*>(buffer);
- if (!sudden_motion_sensor_)
- sudden_motion_sensor_.reset(SuddenMotionSensor::Create());
UMA_HISTOGRAM_BOOLEAN("InertialSensor.MotionMacAvailable",
- sudden_motion_sensor_.get() != NULL);
- return sudden_motion_sensor_.get() != NULL;
+ sudden_motion_sensor_available);
+ if (!sudden_motion_sensor_available) {
+ // No motion sensor available, fire an all-null event.
+ motion_buffer_->seqlock.WriteBegin();
+ motion_buffer_->data.allAvailableSensorsAreActive = true;
+ motion_buffer_->seqlock.WriteEnd();
+ }
+ return sudden_motion_sensor_available;
case CONSUMER_TYPE_ORIENTATION:
orientation_buffer_ =
static_cast<DeviceOrientationHardwareBuffer*>(buffer);
- if (!sudden_motion_sensor_)
- sudden_motion_sensor_.reset(SuddenMotionSensor::Create());
UMA_HISTOGRAM_BOOLEAN("InertialSensor.OrientationMacAvailable",
- sudden_motion_sensor_.get() != NULL);
- return sudden_motion_sensor_.get() != NULL;
+ sudden_motion_sensor_available);
+ if (sudden_motion_sensor_available) {
+ // 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.
+ orientation_buffer_->seqlock.WriteBegin();
+ orientation_buffer_->data.allAvailableSensorsAreActive = true;
+ orientation_buffer_->seqlock.WriteEnd();
+ }
+ return sudden_motion_sensor_available;
default:
NOTREACHED();
}
diff --git a/chromium/content/browser/device_orientation/data_fetcher_shared_memory_win.cc b/chromium/content/browser/device_sensors/data_fetcher_shared_memory_win.cc
index d5cf46a97e0..96f4a14215e 100644
--- a/chromium/content/browser/device_orientation/data_fetcher_shared_memory_win.cc
+++ b/chromium/content/browser/device_sensors/data_fetcher_shared_memory_win.cc
@@ -1,4 +1,4 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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.
@@ -384,12 +384,14 @@ void DataFetcherSharedMemory::SetBufferAvailableState(
orientation_buffer_->data.allAvailableSensorsAreActive = enabled;
orientation_buffer_->seqlock.WriteEnd();
}
+ break;
case CONSUMER_TYPE_MOTION:
if (motion_buffer_) {
motion_buffer_->seqlock.WriteBegin();
motion_buffer_->data.allAvailableSensorsAreActive = enabled;
motion_buffer_->seqlock.WriteEnd();
}
+ break;
default:
NOTREACHED();
}
diff --git a/chromium/content/browser/device_sensors/device_inertial_sensor_browsertest.cc b/chromium/content/browser/device_sensors/device_inertial_sensor_browsertest.cc
new file mode 100644
index 00000000000..ff65895e5ac
--- /dev/null
+++ b/chromium/content/browser/device_sensors/device_inertial_sensor_browsertest.cc
@@ -0,0 +1,289 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/command_line.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/platform_thread.h"
+#include "content/browser/device_sensors/data_fetcher_shared_memory.h"
+#include "content/browser/device_sensors/device_inertial_sensor_service.h"
+#include "content/common/device_sensors/device_motion_hardware_buffer.h"
+#include "content/common/device_sensors/device_orientation_hardware_buffer.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/content_browser_test.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "content/public/test/test_utils.h"
+#include "content/shell/browser/shell.h"
+#include "content/shell/browser/shell_javascript_dialog_manager.h"
+
+namespace content {
+
+namespace {
+
+class FakeDataFetcher : public DataFetcherSharedMemory {
+ public:
+ FakeDataFetcher()
+ : started_orientation_(false, false),
+ stopped_orientation_(false, false),
+ started_motion_(false, false),
+ stopped_motion_(false, false),
+ sensor_data_available_(true) {
+ }
+ virtual ~FakeDataFetcher() { }
+
+ virtual bool Start(ConsumerType consumer_type, void* buffer) OVERRIDE {
+ EXPECT_TRUE(buffer);
+
+ switch (consumer_type) {
+ case CONSUMER_TYPE_MOTION:
+ {
+ DeviceMotionHardwareBuffer* motion_buffer =
+ static_cast<DeviceMotionHardwareBuffer*>(buffer);
+ if (sensor_data_available_)
+ UpdateMotion(motion_buffer);
+ SetMotionBufferReady(motion_buffer);
+ started_motion_.Signal();
+ }
+ break;
+ case CONSUMER_TYPE_ORIENTATION:
+ {
+ DeviceOrientationHardwareBuffer* orientation_buffer =
+ static_cast<DeviceOrientationHardwareBuffer*>(buffer);
+ if (sensor_data_available_)
+ UpdateOrientation(orientation_buffer);
+ SetOrientationBufferReady(orientation_buffer);
+ started_orientation_.Signal();
+ }
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+
+ virtual bool Stop(ConsumerType consumer_type) OVERRIDE {
+ switch (consumer_type) {
+ case CONSUMER_TYPE_MOTION:
+ stopped_motion_.Signal();
+ break;
+ case CONSUMER_TYPE_ORIENTATION:
+ stopped_orientation_.Signal();
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+
+ virtual void Fetch(unsigned consumer_bitmask) OVERRIDE {
+ FAIL() << "fetch should not be called";
+ }
+
+ virtual FetcherType GetType() const OVERRIDE {
+ return FETCHER_TYPE_DEFAULT;
+ }
+
+ void SetSensorDataAvailable(bool available) {
+ sensor_data_available_ = available;
+ }
+
+ void SetMotionBufferReady(DeviceMotionHardwareBuffer* buffer) {
+ buffer->seqlock.WriteBegin();
+ buffer->data.allAvailableSensorsAreActive = true;
+ buffer->seqlock.WriteEnd();
+ }
+
+ void SetOrientationBufferReady(DeviceOrientationHardwareBuffer* buffer) {
+ buffer->seqlock.WriteBegin();
+ buffer->data.allAvailableSensorsAreActive = true;
+ buffer->seqlock.WriteEnd();
+ }
+
+ void UpdateMotion(DeviceMotionHardwareBuffer* buffer) {
+ buffer->seqlock.WriteBegin();
+ buffer->data.accelerationX = 1;
+ buffer->data.hasAccelerationX = true;
+ buffer->data.accelerationY = 2;
+ buffer->data.hasAccelerationY = true;
+ buffer->data.accelerationZ = 3;
+ buffer->data.hasAccelerationZ = true;
+
+ buffer->data.accelerationIncludingGravityX = 4;
+ buffer->data.hasAccelerationIncludingGravityX = true;
+ buffer->data.accelerationIncludingGravityY = 5;
+ buffer->data.hasAccelerationIncludingGravityY = true;
+ buffer->data.accelerationIncludingGravityZ = 6;
+ buffer->data.hasAccelerationIncludingGravityZ = true;
+
+ buffer->data.rotationRateAlpha = 7;
+ buffer->data.hasRotationRateAlpha = true;
+ buffer->data.rotationRateBeta = 8;
+ buffer->data.hasRotationRateBeta = true;
+ buffer->data.rotationRateGamma = 9;
+ buffer->data.hasRotationRateGamma = true;
+
+ buffer->data.interval = 100;
+ buffer->data.allAvailableSensorsAreActive = true;
+ buffer->seqlock.WriteEnd();
+ }
+
+ void UpdateOrientation(DeviceOrientationHardwareBuffer* buffer) {
+ buffer->seqlock.WriteBegin();
+ buffer->data.alpha = 1;
+ buffer->data.hasAlpha = true;
+ buffer->data.beta = 2;
+ buffer->data.hasBeta = true;
+ buffer->data.gamma = 3;
+ buffer->data.hasGamma = true;
+ buffer->data.allAvailableSensorsAreActive = true;
+ buffer->seqlock.WriteEnd();
+ }
+
+ base::WaitableEvent started_orientation_;
+ base::WaitableEvent stopped_orientation_;
+ base::WaitableEvent started_motion_;
+ base::WaitableEvent stopped_motion_;
+ bool sensor_data_available_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FakeDataFetcher);
+};
+
+
+class DeviceInertialSensorBrowserTest : public ContentBrowserTest {
+ public:
+ DeviceInertialSensorBrowserTest()
+ : fetcher_(NULL),
+ io_loop_finished_event_(false, false) {
+ }
+
+ virtual void SetUpOnMainThread() OVERRIDE {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&DeviceInertialSensorBrowserTest::SetUpOnIOThread, this));
+ io_loop_finished_event_.Wait();
+ }
+
+ void SetUpOnIOThread() {
+ fetcher_ = new FakeDataFetcher();
+ DeviceInertialSensorService::GetInstance()->
+ SetDataFetcherForTesting(fetcher_);
+ io_loop_finished_event_.Signal();
+ }
+
+ void DelayAndQuit(base::TimeDelta delay) {
+ base::PlatformThread::Sleep(delay);
+ base::MessageLoop::current()->QuitWhenIdle();
+ }
+
+ void WaitForAlertDialogAndQuitAfterDelay(base::TimeDelta delay) {
+ ShellJavaScriptDialogManager* dialog_manager=
+ static_cast<ShellJavaScriptDialogManager*>(
+ shell()->GetJavaScriptDialogManager());
+
+ scoped_refptr<MessageLoopRunner> runner = new MessageLoopRunner();
+ dialog_manager->set_dialog_request_callback(
+ base::Bind(&DeviceInertialSensorBrowserTest::DelayAndQuit, this,
+ delay));
+ runner->Run();
+ }
+
+ FakeDataFetcher* fetcher_;
+
+ private:
+ base::WaitableEvent io_loop_finished_event_;
+};
+
+
+IN_PROC_BROWSER_TEST_F(DeviceInertialSensorBrowserTest, OrientationTest) {
+ // The test page will register an event handler for orientation events,
+ // expects to get an event with fake values, then removes the event
+ // handler and navigates to #pass.
+ GURL test_url = GetTestUrl(
+ "device_orientation", "device_orientation_test.html");
+ NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 2);
+
+ EXPECT_EQ("pass", shell()->web_contents()->GetLastCommittedURL().ref());
+ fetcher_->started_orientation_.Wait();
+ fetcher_->stopped_orientation_.Wait();
+}
+
+IN_PROC_BROWSER_TEST_F(DeviceInertialSensorBrowserTest, MotionTest) {
+ // The test page will register an event handler for motion events,
+ // expects to get an event with fake values, then removes the event
+ // handler and navigates to #pass.
+ GURL test_url = GetTestUrl(
+ "device_orientation", "device_motion_test.html");
+ NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 2);
+
+ EXPECT_EQ("pass", shell()->web_contents()->GetLastCommittedURL().ref());
+ fetcher_->started_motion_.Wait();
+ fetcher_->stopped_motion_.Wait();
+}
+
+// Flaking in the android try bot. See http://crbug.com/360578.
+#if defined(OS_ANDROID)
+#define MAYBE_OrientationNullTestWithAlert DISABLED_OrientationNullTestWithAlert
+#else
+#define MAYBE_OrientationNullTestWithAlert OrientationNullTestWithAlert
+#endif
+IN_PROC_BROWSER_TEST_F(DeviceInertialSensorBrowserTest,
+ MAYBE_OrientationNullTestWithAlert) {
+ // The test page will register an event handler for orientation events,
+ // expects to get an event with null values. The test raises a modal alert
+ // dialog with a delay to test that the one-off null-event still propagates
+ // to window after the alert is dismissed and the callback is invoked which
+ // navigates to #pass.
+ fetcher_->SetSensorDataAvailable(false);
+ TestNavigationObserver same_tab_observer(shell()->web_contents(), 2);
+
+ GURL test_url = GetTestUrl(
+ "device_orientation", "device_orientation_null_test_with_alert.html");
+ shell()->LoadURL(test_url);
+
+ // TODO(timvolodine): investigate if it is possible to test this without
+ // delay, crbug.com/360044.
+ WaitForAlertDialogAndQuitAfterDelay(base::TimeDelta::FromMilliseconds(1000));
+
+ fetcher_->started_orientation_.Wait();
+ fetcher_->stopped_orientation_.Wait();
+ same_tab_observer.Wait();
+ EXPECT_EQ("pass", shell()->web_contents()->GetLastCommittedURL().ref());
+}
+
+// Flaking in the android try bot. See http://crbug.com/360578.
+#if defined(OS_ANDROID)
+#define MAYBE_MotionNullTestWithAlert DISABLED_MotionNullTestWithAlert
+#else
+#define MAYBE_MotionNullTestWithAlert MotionNullTestWithAlert
+#endif
+IN_PROC_BROWSER_TEST_F(DeviceInertialSensorBrowserTest,
+ MAYBE_MotionNullTestWithAlert) {
+ // The test page will register an event handler for motion events,
+ // expects to get an event with null values. The test raises a modal alert
+ // dialog with a delay to test that the one-off null-event still propagates
+ // to window after the alert is dismissed and the callback is invoked which
+ // navigates to #pass.
+ fetcher_->SetSensorDataAvailable(false);
+ TestNavigationObserver same_tab_observer(shell()->web_contents(), 2);
+
+ GURL test_url = GetTestUrl(
+ "device_orientation", "device_motion_null_test_with_alert.html");
+ shell()->LoadURL(test_url);
+
+ // TODO(timvolodine): investigate if it is possible to test this without
+ // delay, crbug.com/360044.
+ WaitForAlertDialogAndQuitAfterDelay(base::TimeDelta::FromMilliseconds(1000));
+
+ fetcher_->started_motion_.Wait();
+ fetcher_->stopped_motion_.Wait();
+ same_tab_observer.Wait();
+ EXPECT_EQ("pass", shell()->web_contents()->GetLastCommittedURL().ref());
+}
+
+} // namespace
+
+} // namespace content
diff --git a/chromium/content/browser/device_orientation/device_inertial_sensor_service.cc b/chromium/content/browser/device_sensors/device_inertial_sensor_service.cc
index cb8bbf300b2..cf96820649e 100644
--- a/chromium/content/browser/device_orientation/device_inertial_sensor_service.cc
+++ b/chromium/content/browser/device_sensors/device_inertial_sensor_service.cc
@@ -1,14 +1,13 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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/device_orientation/device_inertial_sensor_service.h"
+#include "content/browser/device_sensors/device_inertial_sensor_service.h"
#include "base/bind.h"
#include "base/logging.h"
#include "base/memory/singleton.h"
-#include "content/browser/device_orientation/data_fetcher_shared_memory.h"
-#include "content/public/browser/render_process_host.h"
+#include "content/browser/device_sensors/data_fetcher_shared_memory.h"
namespace content {
@@ -54,11 +53,11 @@ bool DeviceInertialSensorService::ChangeNumberConsumers(
switch (consumer_type) {
case CONSUMER_TYPE_MOTION:
num_motion_readers_ += delta;
- DCHECK(num_motion_readers_ >= 0);
+ DCHECK_GE(num_motion_readers_, 0);
return true;
case CONSUMER_TYPE_ORIENTATION:
num_orientation_readers_ += delta;
- DCHECK(num_orientation_readers_ >= 0);
+ DCHECK_GE(num_orientation_readers_ , 0);
return true;
default:
NOTREACHED();
@@ -91,7 +90,7 @@ void DeviceInertialSensorService::Shutdown() {
is_shutdown_ = true;
}
-void DeviceInertialSensorService::SetDataFetcherForTests(
+void DeviceInertialSensorService::SetDataFetcherForTesting(
DataFetcherSharedMemory* test_data_fetcher) {
data_fetcher_.reset(test_data_fetcher);
}
diff --git a/chromium/content/browser/device_orientation/device_inertial_sensor_service.h b/chromium/content/browser/device_sensors/device_inertial_sensor_service.h
index 1c7c162f23f..1d8cfd14497 100644
--- a/chromium/content/browser/device_orientation/device_inertial_sensor_service.h
+++ b/chromium/content/browser/device_sensors/device_inertial_sensor_service.h
@@ -1,9 +1,9 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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_DEVICE_ORIENTATION_DEVICE_INERTIAL_SENSOR_SERVICE_H_
-#define CONTENT_BROWSER_DEVICE_ORIENTATION_DEVICE_INERTIAL_SENSOR_SERVICE_H_
+#ifndef CONTENT_BROWSER_DEVICE_SENSORS_DEVICE_INERTIAL_SENSOR_SERVICE_H_
+#define CONTENT_BROWSER_DEVICE_SENSORS_DEVICE_INERTIAL_SENSOR_SERVICE_H_
#include "base/basictypes.h"
#include "base/callback_forward.h"
@@ -11,17 +11,16 @@
#include "base/memory/shared_memory.h"
#include "base/memory/singleton.h"
#include "base/threading/thread_checker.h"
-#include "content/browser/device_orientation/inertial_sensor_consts.h"
+#include "content/browser/device_sensors/inertial_sensor_consts.h"
#include "content/common/content_export.h"
namespace content {
class DataFetcherSharedMemory;
-class RenderProcessHost;
-// Owns the DeviceMotionProvider (the background polling thread) and keeps
-// track of the number of consumers currently using the data (and pausing
-// the provider when not in use).
+// Owns the data fetcher for Device Motion and Orientation and keeps track of
+// the number of consumers currently using the data. The data fetcher is stopped
+// when there are no consumers.
class CONTENT_EXPORT DeviceInertialSensorService {
public:
// Returns the DeviceInertialSensorService singleton.
@@ -46,7 +45,7 @@ class CONTENT_EXPORT DeviceInertialSensorService {
// Injects a custom data fetcher for testing purposes. This class takes
// ownership of the injected object.
- void SetDataFetcherForTests(DataFetcherSharedMemory* test_data_fetcher);
+ void SetDataFetcherForTesting(DataFetcherSharedMemory* test_data_fetcher);
private:
friend struct DefaultSingletonTraits<DeviceInertialSensorService>;
@@ -69,4 +68,4 @@ class CONTENT_EXPORT DeviceInertialSensorService {
} // namespace content
-#endif // CONTENT_BROWSER_DEVICE_ORIENTATION_DEVICE_INERTIAL_SENSOR_SERVICE_H_
+#endif // CONTENT_BROWSER_DEVICE_SENSORS_DEVICE_INERTIAL_SENSOR_SERVICE_H_
diff --git a/chromium/content/browser/device_orientation/device_motion_message_filter.cc b/chromium/content/browser/device_sensors/device_motion_message_filter.cc
index bc33cac3912..2994c383f15 100644
--- a/chromium/content/browser/device_orientation/device_motion_message_filter.cc
+++ b/chromium/content/browser/device_sensors/device_motion_message_filter.cc
@@ -1,16 +1,17 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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/device_orientation/device_motion_message_filter.h"
+#include "content/browser/device_sensors/device_motion_message_filter.h"
-#include "content/browser/device_orientation/device_inertial_sensor_service.h"
-#include "content/common/device_orientation/device_motion_messages.h"
+#include "content/browser/device_sensors/device_inertial_sensor_service.h"
+#include "content/common/device_sensors/device_motion_messages.h"
namespace content {
DeviceMotionMessageFilter::DeviceMotionMessageFilter()
- : is_started_(false) {
+ : BrowserMessageFilter(DeviceMotionMsgStart),
+ is_started_(false) {
}
DeviceMotionMessageFilter::~DeviceMotionMessageFilter() {
@@ -20,19 +21,15 @@ DeviceMotionMessageFilter::~DeviceMotionMessageFilter() {
CONSUMER_TYPE_MOTION);
}
-bool DeviceMotionMessageFilter::OnMessageReceived(
- const IPC::Message& message,
- bool* message_was_ok) {
+bool DeviceMotionMessageFilter::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(DeviceMotionMessageFilter,
- message,
- *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(DeviceMotionMessageFilter, message)
IPC_MESSAGE_HANDLER(DeviceMotionHostMsg_StartPolling,
OnDeviceMotionStartPolling)
IPC_MESSAGE_HANDLER(DeviceMotionHostMsg_StopPolling,
OnDeviceMotionStopPolling)
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return handled;
}
diff --git a/chromium/content/browser/device_orientation/device_motion_message_filter.h b/chromium/content/browser/device_sensors/device_motion_message_filter.h
index 3d078e335cc..cd4859472d5 100644
--- a/chromium/content/browser/device_orientation/device_motion_message_filter.h
+++ b/chromium/content/browser/device_sensors/device_motion_message_filter.h
@@ -1,25 +1,20 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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_RENDERER_HOST_DEVICE_MOTION_MESSAGE_FILTER_H_
-#define CONTENT_BROWSER_RENDERER_HOST_DEVICE_MOTION_MESSAGE_FILTER_H_
+#ifndef CONTENT_BROWSER_DEVICE_SENSORS_DEVICE_MOTION_MESSAGE_FILTER_H_
+#define CONTENT_BROWSER_DEVICE_SENSORS_DEVICE_MOTION_MESSAGE_FILTER_H_
-#include "base/compiler_specific.h"
#include "content/public/browser/browser_message_filter.h"
namespace content {
-class DeviceMotionService;
-class RenderProcessHost;
-
class DeviceMotionMessageFilter : public BrowserMessageFilter {
public:
DeviceMotionMessageFilter();
// BrowserMessageFilter implementation.
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
private:
virtual ~DeviceMotionMessageFilter();
@@ -35,4 +30,4 @@ class DeviceMotionMessageFilter : public BrowserMessageFilter {
} // namespace content
-#endif // CONTENT_BROWSER_RENDERER_HOST_DEVICE_MOTION_MESSAGE_FILTER_H_
+#endif // CONTENT_BROWSER_DEVICE_SENSORS_DEVICE_MOTION_MESSAGE_FILTER_H_
diff --git a/chromium/content/browser/device_orientation/device_orientation_message_filter.cc b/chromium/content/browser/device_sensors/device_orientation_message_filter.cc
index d00f2b8ccfd..402aa67a40f 100644
--- a/chromium/content/browser/device_orientation/device_orientation_message_filter.cc
+++ b/chromium/content/browser/device_sensors/device_orientation_message_filter.cc
@@ -1,16 +1,17 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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/device_orientation/device_orientation_message_filter.h"
+#include "content/browser/device_sensors/device_orientation_message_filter.h"
-#include "content/browser/device_orientation/device_inertial_sensor_service.h"
-#include "content/common/device_orientation/device_orientation_messages.h"
+#include "content/browser/device_sensors/device_inertial_sensor_service.h"
+#include "content/common/device_sensors/device_orientation_messages.h"
namespace content {
DeviceOrientationMessageFilter::DeviceOrientationMessageFilter()
- : is_started_(false) {
+ : BrowserMessageFilter(DeviceOrientationMsgStart),
+ is_started_(false) {
}
DeviceOrientationMessageFilter::~DeviceOrientationMessageFilter() {
@@ -21,18 +22,15 @@ DeviceOrientationMessageFilter::~DeviceOrientationMessageFilter() {
}
bool DeviceOrientationMessageFilter::OnMessageReceived(
- const IPC::Message& message,
- bool* message_was_ok) {
+ const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(DeviceOrientationMessageFilter,
- message,
- *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(DeviceOrientationMessageFilter, message)
IPC_MESSAGE_HANDLER(DeviceOrientationHostMsg_StartPolling,
OnDeviceOrientationStartPolling)
IPC_MESSAGE_HANDLER(DeviceOrientationHostMsg_StopPolling,
OnDeviceOrientationStopPolling)
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return handled;
}
diff --git a/chromium/content/browser/device_orientation/device_orientation_message_filter.h b/chromium/content/browser/device_sensors/device_orientation_message_filter.h
index b8ccb84a49c..56893c83a16 100644
--- a/chromium/content/browser/device_orientation/device_orientation_message_filter.h
+++ b/chromium/content/browser/device_sensors/device_orientation_message_filter.h
@@ -1,24 +1,20 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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_DEVICE_ORIENTATION_DEVICE_ORIENTATION_MESSAGE_FILTER_H_
-#define CONTENT_BROWSER_DEVICE_ORIENTATION_DEVICE_ORIENTATION_MESSAGE_FILTER_H_
+#ifndef CONTENT_BROWSER_DEVICE_SENSORS_DEVICE_ORIENTATION_MESSAGE_FILTER_H_
+#define CONTENT_BROWSER_DEVICE_SENSORS_DEVICE_ORIENTATION_MESSAGE_FILTER_H_
-#include "base/compiler_specific.h"
#include "content/public/browser/browser_message_filter.h"
namespace content {
-class RenderProcessHost;
-
class DeviceOrientationMessageFilter : public BrowserMessageFilter {
public:
DeviceOrientationMessageFilter();
// BrowserMessageFilter implementation.
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
private:
virtual ~DeviceOrientationMessageFilter();
@@ -34,4 +30,4 @@ class DeviceOrientationMessageFilter : public BrowserMessageFilter {
} // namespace content
-#endif // CONTENT_BROWSER_DEVICE_ORIENTATION_DEVICE_ORIENTATION_MESSAGE_FILTER_H_
+#endif // CONTENT_BROWSER_DEVICE_SENSORS_DEVICE_ORIENTATION_MESSAGE_FILTER_H_
diff --git a/chromium/content/browser/device_orientation/inertial_sensor_consts.h b/chromium/content/browser/device_sensors/inertial_sensor_consts.h
index 6a37fc82a4e..6ca6a98e96a 100644
--- a/chromium/content/browser/device_orientation/inertial_sensor_consts.h
+++ b/chromium/content/browser/device_sensors/inertial_sensor_consts.h
@@ -1,9 +1,9 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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_DEVICE_ORIENTATION_INERTIAL_SENSOR_CONSTS_H_
-#define CONTENT_BROWSER_DEVICE_ORIENTATION_INERTIAL_SENSOR_CONSTS_H_
+#ifndef CONTENT_BROWSER_DEVICE_SENSORS_INERTIAL_SENSOR_CONSTS_H_
+#define CONTENT_BROWSER_DEVICE_SENSORS_INERTIAL_SENSOR_CONSTS_H_
namespace content {
@@ -22,4 +22,4 @@ const int kInertialSensorIntervalMillis = 50;
} // namespace content
-#endif // CONTENT_BROWSER_DEVICE_ORIENTATION_INERTIAL_SENSOR_CONSTS_H_
+#endif // CONTENT_BROWSER_DEVICE_SENSORS_INERTIAL_SENSOR_CONSTS_H_
diff --git a/chromium/content/browser/device_orientation/data_fetcher_impl_android.cc b/chromium/content/browser/device_sensors/sensor_manager_android.cc
index c103029f62b..400adfa355b 100644
--- a/chromium/content/browser/device_orientation/data_fetcher_impl_android.cc
+++ b/chromium/content/browser/device_sensors/sensor_manager_android.cc
@@ -1,16 +1,16 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// 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/device_orientation/data_fetcher_impl_android.h"
+#include "content/browser/device_sensors/sensor_manager_android.h"
#include <string.h>
#include "base/android/jni_android.h"
#include "base/memory/singleton.h"
#include "base/metrics/histogram.h"
-#include "content/browser/device_orientation/inertial_sensor_consts.h"
-#include "jni/DeviceMotionAndOrientation_jni.h"
+#include "content/browser/device_sensors/inertial_sensor_consts.h"
+#include "jni/DeviceSensors_jni.h"
using base::android::AttachCurrentThread;
@@ -24,7 +24,7 @@ static void updateRotationVectorHistogram(bool value) {
namespace content {
-DataFetcherImplAndroid::DataFetcherImplAndroid()
+SensorManagerAndroid::SensorManagerAndroid()
: number_active_device_motion_sensors_(0),
device_motion_buffer_(NULL),
device_orientation_buffer_(NULL),
@@ -32,22 +32,24 @@ DataFetcherImplAndroid::DataFetcherImplAndroid()
is_orientation_buffer_ready_(false) {
memset(received_motion_data_, 0, sizeof(received_motion_data_));
device_orientation_.Reset(
- Java_DeviceMotionAndOrientation_getInstance(AttachCurrentThread()));
+ Java_DeviceSensors_getInstance(
+ AttachCurrentThread(),
+ base::android::GetApplicationContext()));
}
-DataFetcherImplAndroid::~DataFetcherImplAndroid() {
+SensorManagerAndroid::~SensorManagerAndroid() {
}
-bool DataFetcherImplAndroid::Register(JNIEnv* env) {
+bool SensorManagerAndroid::Register(JNIEnv* env) {
return RegisterNativesImpl(env);
}
-DataFetcherImplAndroid* DataFetcherImplAndroid::GetInstance() {
- return Singleton<DataFetcherImplAndroid,
- LeakySingletonTraits<DataFetcherImplAndroid> >::get();
+SensorManagerAndroid* SensorManagerAndroid::GetInstance() {
+ return Singleton<SensorManagerAndroid,
+ LeakySingletonTraits<SensorManagerAndroid> >::get();
}
-void DataFetcherImplAndroid::GotOrientation(
+void SensorManagerAndroid::GotOrientation(
JNIEnv*, jobject, double alpha, double beta, double gamma) {
base::AutoLock autolock(orientation_buffer_lock_);
@@ -69,7 +71,7 @@ void DataFetcherImplAndroid::GotOrientation(
}
}
-void DataFetcherImplAndroid::GotAcceleration(
+void SensorManagerAndroid::GotAcceleration(
JNIEnv*, jobject, double x, double y, double z) {
base::AutoLock autolock(motion_buffer_lock_);
@@ -91,7 +93,7 @@ void DataFetcherImplAndroid::GotAcceleration(
}
}
-void DataFetcherImplAndroid::GotAccelerationIncludingGravity(
+void SensorManagerAndroid::GotAccelerationIncludingGravity(
JNIEnv*, jobject, double x, double y, double z) {
base::AutoLock autolock(motion_buffer_lock_);
@@ -113,7 +115,7 @@ void DataFetcherImplAndroid::GotAccelerationIncludingGravity(
}
}
-void DataFetcherImplAndroid::GotRotationRate(
+void SensorManagerAndroid::GotRotationRate(
JNIEnv*, jobject, double alpha, double beta, double gamma) {
base::AutoLock autolock(motion_buffer_lock_);
@@ -135,24 +137,24 @@ void DataFetcherImplAndroid::GotRotationRate(
}
}
-bool DataFetcherImplAndroid::Start(DeviceData::Type event_type) {
+bool SensorManagerAndroid::Start(EventType event_type) {
DCHECK(!device_orientation_.is_null());
- return Java_DeviceMotionAndOrientation_start(
+ return Java_DeviceSensors_start(
AttachCurrentThread(), device_orientation_.obj(),
reinterpret_cast<intptr_t>(this), static_cast<jint>(event_type),
kInertialSensorIntervalMillis);
}
-void DataFetcherImplAndroid::Stop(DeviceData::Type event_type) {
+void SensorManagerAndroid::Stop(EventType event_type) {
DCHECK(!device_orientation_.is_null());
- Java_DeviceMotionAndOrientation_stop(
+ Java_DeviceSensors_stop(
AttachCurrentThread(), device_orientation_.obj(),
static_cast<jint>(event_type));
}
-int DataFetcherImplAndroid::GetNumberActiveDeviceMotionSensors() {
+int SensorManagerAndroid::GetNumberActiveDeviceMotionSensors() {
DCHECK(!device_orientation_.is_null());
- return Java_DeviceMotionAndOrientation_getNumberActiveDeviceMotionSensors(
+ return Java_DeviceSensors_getNumberActiveDeviceMotionSensors(
AttachCurrentThread(), device_orientation_.obj());
}
@@ -161,7 +163,7 @@ int DataFetcherImplAndroid::GetNumberActiveDeviceMotionSensors() {
// --- Device Motion
-bool DataFetcherImplAndroid::StartFetchingDeviceMotionData(
+bool SensorManagerAndroid::StartFetchingDeviceMotionData(
DeviceMotionHardwareBuffer* buffer) {
DCHECK(buffer);
{
@@ -169,7 +171,7 @@ bool DataFetcherImplAndroid::StartFetchingDeviceMotionData(
device_motion_buffer_ = buffer;
ClearInternalMotionBuffers();
}
- bool success = Start(DeviceData::kTypeMotion);
+ bool success = Start(kTypeMotion);
// If no motion data can ever be provided, the number of active device motion
// sensors will be zero. In that case flag the shared memory buffer
@@ -182,8 +184,8 @@ bool DataFetcherImplAndroid::StartFetchingDeviceMotionData(
return success;
}
-void DataFetcherImplAndroid::StopFetchingDeviceMotionData() {
- Stop(DeviceData::kTypeMotion);
+void SensorManagerAndroid::StopFetchingDeviceMotionData() {
+ Stop(kTypeMotion);
{
base::AutoLock autolock(motion_buffer_lock_);
if (device_motion_buffer_) {
@@ -193,7 +195,7 @@ void DataFetcherImplAndroid::StopFetchingDeviceMotionData() {
}
}
-void DataFetcherImplAndroid::CheckMotionBufferReadyToRead() {
+void SensorManagerAndroid::CheckMotionBufferReadyToRead() {
if (received_motion_data_[RECEIVED_MOTION_DATA_ACCELERATION] +
received_motion_data_[RECEIVED_MOTION_DATA_ACCELERATION_INCL_GRAVITY] +
received_motion_data_[RECEIVED_MOTION_DATA_ROTATION_RATE] ==
@@ -214,14 +216,14 @@ void DataFetcherImplAndroid::CheckMotionBufferReadyToRead() {
}
}
-void DataFetcherImplAndroid::SetMotionBufferReadyStatus(bool ready) {
+void SensorManagerAndroid::SetMotionBufferReadyStatus(bool ready) {
device_motion_buffer_->seqlock.WriteBegin();
device_motion_buffer_->data.allAvailableSensorsAreActive = ready;
device_motion_buffer_->seqlock.WriteEnd();
is_motion_buffer_ready_ = ready;
}
-void DataFetcherImplAndroid::ClearInternalMotionBuffers() {
+void SensorManagerAndroid::ClearInternalMotionBuffers() {
memset(received_motion_data_, 0, sizeof(received_motion_data_));
number_active_device_motion_sensors_ = 0;
SetMotionBufferReadyStatus(false);
@@ -229,7 +231,7 @@ void DataFetcherImplAndroid::ClearInternalMotionBuffers() {
// --- Device Orientation
-void DataFetcherImplAndroid::SetOrientationBufferReadyStatus(bool ready) {
+void SensorManagerAndroid::SetOrientationBufferReadyStatus(bool ready) {
device_orientation_buffer_->seqlock.WriteBegin();
device_orientation_buffer_->data.absolute = ready;
device_orientation_buffer_->data.hasAbsolute = ready;
@@ -238,14 +240,14 @@ void DataFetcherImplAndroid::SetOrientationBufferReadyStatus(bool ready) {
is_orientation_buffer_ready_ = ready;
}
-bool DataFetcherImplAndroid::StartFetchingDeviceOrientationData(
+bool SensorManagerAndroid::StartFetchingDeviceOrientationData(
DeviceOrientationHardwareBuffer* buffer) {
DCHECK(buffer);
{
base::AutoLock autolock(orientation_buffer_lock_);
device_orientation_buffer_ = buffer;
}
- bool success = Start(DeviceData::kTypeOrientation);
+ bool success = Start(kTypeOrientation);
{
base::AutoLock autolock(orientation_buffer_lock_);
@@ -260,8 +262,8 @@ bool DataFetcherImplAndroid::StartFetchingDeviceOrientationData(
return success;
}
-void DataFetcherImplAndroid::StopFetchingDeviceOrientationData() {
- Stop(DeviceData::kTypeOrientation);
+void SensorManagerAndroid::StopFetchingDeviceOrientationData() {
+ Stop(kTypeOrientation);
{
base::AutoLock autolock(orientation_buffer_lock_);
if (device_orientation_buffer_) {
diff --git a/chromium/content/browser/device_orientation/data_fetcher_impl_android.h b/chromium/content/browser/device_sensors/sensor_manager_android.h
index 9332fa2e20a..f2a552ecf5c 100644
--- a/chromium/content/browser/device_orientation/data_fetcher_impl_android.h
+++ b/chromium/content/browser/device_sensors/sensor_manager_android.h
@@ -1,17 +1,16 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CHROME_BROWSER_DEVICE_ORIENTATION_DATA_FETCHER_IMPL_ANDROID_H_
-#define CHROME_BROWSER_DEVICE_ORIENTATION_DATA_FETCHER_IMPL_ANDROID_H_
+#ifndef CONTENT_BROWSER_DEVICE_SENSORS_SENSOR_MANAGER_ANDROID_H_
+#define CONTENT_BROWSER_DEVICE_SENSORS_SENSOR_MANAGER_ANDROID_H_
#include "base/android/scoped_java_ref.h"
#include "base/memory/scoped_ptr.h"
#include "base/synchronization/lock.h"
-#include "content/browser/device_orientation/device_data.h"
#include "content/common/content_export.h"
-#include "content/common/device_orientation/device_motion_hardware_buffer.h"
-#include "content/common/device_orientation/device_orientation_hardware_buffer.h"
+#include "content/common/device_sensors/device_motion_hardware_buffer.h"
+#include "content/common/device_sensors/device_orientation_hardware_buffer.h"
template<typename T> struct DefaultSingletonTraits;
@@ -22,16 +21,13 @@ namespace content {
// Android's SensorManager has a push API, so when Got*() methods are called
// by the system the browser process puts the received data into a shared
// memory buffer, which is read by the renderer processes.
-//
-
-// TODO(timvolodine): rename this class to SensorManagerAndroid.
-class CONTENT_EXPORT DataFetcherImplAndroid {
+class CONTENT_EXPORT SensorManagerAndroid {
public:
// Must be called at startup, before GetInstance().
static bool Register(JNIEnv* env);
// Needs to be thread-safe, because accessed from different threads.
- static DataFetcherImplAndroid* GetInstance();
+ static SensorManagerAndroid* GetInstance();
// Called from Java via JNI.
void GotOrientation(JNIEnv*, jobject,
@@ -43,9 +39,6 @@ class CONTENT_EXPORT DataFetcherImplAndroid {
void GotRotationRate(JNIEnv*, jobject,
double alpha, double beta, double gamma);
- virtual bool Start(DeviceData::Type event_type);
- virtual void Stop(DeviceData::Type event_type);
-
// Shared memory related methods.
bool StartFetchingDeviceMotionData(DeviceMotionHardwareBuffer* buffer);
void StopFetchingDeviceMotionData();
@@ -55,19 +48,23 @@ class CONTENT_EXPORT DataFetcherImplAndroid {
void StopFetchingDeviceOrientationData();
protected:
- DataFetcherImplAndroid();
- virtual ~DataFetcherImplAndroid();
+ enum EventType {
+ // These constants should match DEVICE_ORIENTATION and DEVICE_MOTION
+ // constants in content/public/android/java/src/org/chromium/content/
+ // browser/DeviceMotionAndOrientation.java
+ kTypeOrientation = 0,
+ kTypeMotion = 1
+ };
+
+ SensorManagerAndroid();
+ virtual ~SensorManagerAndroid();
+ virtual bool Start(EventType event_type);
+ virtual void Stop(EventType event_type);
virtual int GetNumberActiveDeviceMotionSensors();
private:
- friend struct DefaultSingletonTraits<DataFetcherImplAndroid>;
-
- void CheckMotionBufferReadyToRead();
- void SetMotionBufferReadyStatus(bool ready);
- void ClearInternalMotionBuffers();
-
- void SetOrientationBufferReadyStatus(bool ready);
+ friend struct DefaultSingletonTraits<SensorManagerAndroid>;
enum {
RECEIVED_MOTION_DATA_ACCELERATION = 0,
@@ -76,6 +73,12 @@ class CONTENT_EXPORT DataFetcherImplAndroid {
RECEIVED_MOTION_DATA_MAX = 3,
};
+ void CheckMotionBufferReadyToRead();
+ void SetMotionBufferReadyStatus(bool ready);
+ void ClearInternalMotionBuffers();
+
+ void SetOrientationBufferReadyStatus(bool ready);
+
// The Java provider of orientation info.
base::android::ScopedJavaGlobalRef<jobject> device_orientation_;
int number_active_device_motion_sensors_;
@@ -88,9 +91,9 @@ class CONTENT_EXPORT DataFetcherImplAndroid {
base::Lock motion_buffer_lock_;
base::Lock orientation_buffer_lock_;
- DISALLOW_COPY_AND_ASSIGN(DataFetcherImplAndroid);
+ DISALLOW_COPY_AND_ASSIGN(SensorManagerAndroid);
};
} // namespace content
-#endif // CHROME_BROWSER_DEVICE_ORIENTATION_DATA_FETCHER_IMPL_ANDROID_H_
+#endif // CONTENT_BROWSER_DEVICE_SENSORS_SENSOR_MANAGER_ANDROID_H_
diff --git a/chromium/content/browser/device_orientation/data_fetcher_impl_android_unittest.cc b/chromium/content/browser/device_sensors/sensor_manager_android_unittest.cc
index 3d36222cf0c..8299164c910 100644
--- a/chromium/content/browser/device_orientation/data_fetcher_impl_android_unittest.cc
+++ b/chromium/content/browser/device_sensors/sensor_manager_android_unittest.cc
@@ -1,30 +1,23 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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/device_orientation/data_fetcher_impl_android.h"
+#include "content/browser/device_sensors/sensor_manager_android.h"
#include "base/android/jni_android.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
-#include "content/browser/device_orientation/inertial_sensor_consts.h"
+#include "content/browser/device_sensors/inertial_sensor_consts.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
namespace {
-class FakeDataFetcherImplAndroid : public DataFetcherImplAndroid {
+class FakeSensorManagerAndroid : public SensorManagerAndroid {
public:
- FakeDataFetcherImplAndroid() { }
- virtual ~FakeDataFetcherImplAndroid() { }
-
- virtual bool Start(DeviceData::Type event_type) OVERRIDE {
- return true;
- }
-
- virtual void Stop(DeviceData::Type event_type) OVERRIDE {
- }
+ FakeSensorManagerAndroid() { }
+ virtual ~FakeSensorManagerAndroid() { }
virtual int GetNumberActiveDeviceMotionSensors() OVERRIDE {
return number_active_sensors_;
@@ -34,13 +27,21 @@ class FakeDataFetcherImplAndroid : public DataFetcherImplAndroid {
number_active_sensors_ = number_active_sensors;
}
+ protected:
+ virtual bool Start(EventType event_type) OVERRIDE {
+ return true;
+ }
+
+ virtual void Stop(EventType event_type) OVERRIDE {
+ }
+
private:
int number_active_sensors_;
};
-class AndroidDataFetcherTest : public testing::Test {
+class AndroidSensorManagerTest : public testing::Test {
protected:
- AndroidDataFetcherTest() {
+ AndroidSensorManagerTest() {
motion_buffer_.reset(new DeviceMotionHardwareBuffer);
orientation_buffer_.reset(new DeviceOrientationHardwareBuffer);
}
@@ -49,15 +50,15 @@ class AndroidDataFetcherTest : public testing::Test {
scoped_ptr<DeviceOrientationHardwareBuffer> orientation_buffer_;
};
-TEST_F(AndroidDataFetcherTest, ThreeDeviceMotionSensorsActive) {
- FakeDataFetcherImplAndroid::Register(base::android::AttachCurrentThread());
- FakeDataFetcherImplAndroid fetcher;
- fetcher.SetNumberActiveDeviceMotionSensors(3);
+TEST_F(AndroidSensorManagerTest, ThreeDeviceMotionSensorsActive) {
+ FakeSensorManagerAndroid::Register(base::android::AttachCurrentThread());
+ FakeSensorManagerAndroid sensorManager;
+ sensorManager.SetNumberActiveDeviceMotionSensors(3);
- fetcher.StartFetchingDeviceMotionData(motion_buffer_.get());
+ sensorManager.StartFetchingDeviceMotionData(motion_buffer_.get());
ASSERT_FALSE(motion_buffer_->data.allAvailableSensorsAreActive);
- fetcher.GotAcceleration(0, 0, 1, 2, 3);
+ sensorManager.GotAcceleration(0, 0, 1, 2, 3);
ASSERT_FALSE(motion_buffer_->data.allAvailableSensorsAreActive);
ASSERT_EQ(1, motion_buffer_->data.accelerationX);
ASSERT_TRUE(motion_buffer_->data.hasAccelerationX);
@@ -66,7 +67,7 @@ TEST_F(AndroidDataFetcherTest, ThreeDeviceMotionSensorsActive) {
ASSERT_EQ(3, motion_buffer_->data.accelerationZ);
ASSERT_TRUE(motion_buffer_->data.hasAccelerationZ);
- fetcher.GotAccelerationIncludingGravity(0, 0, 4, 5, 6);
+ sensorManager.GotAccelerationIncludingGravity(0, 0, 4, 5, 6);
ASSERT_FALSE(motion_buffer_->data.allAvailableSensorsAreActive);
ASSERT_EQ(4, motion_buffer_->data.accelerationIncludingGravityX);
ASSERT_TRUE(motion_buffer_->data.hasAccelerationIncludingGravityX);
@@ -75,7 +76,7 @@ TEST_F(AndroidDataFetcherTest, ThreeDeviceMotionSensorsActive) {
ASSERT_EQ(6, motion_buffer_->data.accelerationIncludingGravityZ);
ASSERT_TRUE(motion_buffer_->data.hasAccelerationIncludingGravityZ);
- fetcher.GotRotationRate(0, 0, 7, 8, 9);
+ sensorManager.GotRotationRate(0, 0, 7, 8, 9);
ASSERT_TRUE(motion_buffer_->data.allAvailableSensorsAreActive);
ASSERT_EQ(7, motion_buffer_->data.rotationRateAlpha);
ASSERT_TRUE(motion_buffer_->data.hasRotationRateAlpha);
@@ -85,50 +86,50 @@ TEST_F(AndroidDataFetcherTest, ThreeDeviceMotionSensorsActive) {
ASSERT_TRUE(motion_buffer_->data.hasRotationRateGamma);
ASSERT_EQ(kInertialSensorIntervalMillis, motion_buffer_->data.interval);
- fetcher.StopFetchingDeviceMotionData();
+ sensorManager.StopFetchingDeviceMotionData();
ASSERT_FALSE(motion_buffer_->data.allAvailableSensorsAreActive);
}
-TEST_F(AndroidDataFetcherTest, TwoDeviceMotionSensorsActive) {
- FakeDataFetcherImplAndroid::Register(base::android::AttachCurrentThread());
- FakeDataFetcherImplAndroid fetcher;
- fetcher.SetNumberActiveDeviceMotionSensors(2);
+TEST_F(AndroidSensorManagerTest, TwoDeviceMotionSensorsActive) {
+ FakeSensorManagerAndroid::Register(base::android::AttachCurrentThread());
+ FakeSensorManagerAndroid sensorManager;
+ sensorManager.SetNumberActiveDeviceMotionSensors(2);
- fetcher.StartFetchingDeviceMotionData(motion_buffer_.get());
+ sensorManager.StartFetchingDeviceMotionData(motion_buffer_.get());
ASSERT_FALSE(motion_buffer_->data.allAvailableSensorsAreActive);
- fetcher.GotAcceleration(0, 0, 1, 2, 3);
+ sensorManager.GotAcceleration(0, 0, 1, 2, 3);
ASSERT_FALSE(motion_buffer_->data.allAvailableSensorsAreActive);
- fetcher.GotAccelerationIncludingGravity(0, 0, 1, 2, 3);
+ sensorManager.GotAccelerationIncludingGravity(0, 0, 1, 2, 3);
ASSERT_TRUE(motion_buffer_->data.allAvailableSensorsAreActive);
ASSERT_EQ(kInertialSensorIntervalMillis, motion_buffer_->data.interval);
- fetcher.StopFetchingDeviceMotionData();
+ sensorManager.StopFetchingDeviceMotionData();
ASSERT_FALSE(motion_buffer_->data.allAvailableSensorsAreActive);
}
-TEST_F(AndroidDataFetcherTest, ZeroDeviceMotionSensorsActive) {
- FakeDataFetcherImplAndroid::Register(base::android::AttachCurrentThread());
- FakeDataFetcherImplAndroid fetcher;
- fetcher.SetNumberActiveDeviceMotionSensors(0);
+TEST_F(AndroidSensorManagerTest, ZeroDeviceMotionSensorsActive) {
+ FakeSensorManagerAndroid::Register(base::android::AttachCurrentThread());
+ FakeSensorManagerAndroid sensorManager;
+ sensorManager.SetNumberActiveDeviceMotionSensors(0);
- fetcher.StartFetchingDeviceMotionData(motion_buffer_.get());
+ sensorManager.StartFetchingDeviceMotionData(motion_buffer_.get());
ASSERT_TRUE(motion_buffer_->data.allAvailableSensorsAreActive);
ASSERT_EQ(kInertialSensorIntervalMillis, motion_buffer_->data.interval);
- fetcher.StopFetchingDeviceMotionData();
+ sensorManager.StopFetchingDeviceMotionData();
ASSERT_FALSE(motion_buffer_->data.allAvailableSensorsAreActive);
}
-TEST_F(AndroidDataFetcherTest, DeviceOrientationSensorsActive) {
- FakeDataFetcherImplAndroid::Register(base::android::AttachCurrentThread());
- FakeDataFetcherImplAndroid fetcher;
+TEST_F(AndroidSensorManagerTest, DeviceOrientationSensorsActive) {
+ FakeSensorManagerAndroid::Register(base::android::AttachCurrentThread());
+ FakeSensorManagerAndroid sensorManager;
- fetcher.StartFetchingDeviceOrientationData(orientation_buffer_.get());
+ sensorManager.StartFetchingDeviceOrientationData(orientation_buffer_.get());
ASSERT_FALSE(orientation_buffer_->data.allAvailableSensorsAreActive);
- fetcher.GotOrientation(0, 0, 1, 2, 3);
+ sensorManager.GotOrientation(0, 0, 1, 2, 3);
ASSERT_TRUE(orientation_buffer_->data.allAvailableSensorsAreActive);
ASSERT_EQ(1, orientation_buffer_->data.alpha);
ASSERT_TRUE(orientation_buffer_->data.hasAlpha);
@@ -137,7 +138,7 @@ TEST_F(AndroidDataFetcherTest, DeviceOrientationSensorsActive) {
ASSERT_EQ(3, orientation_buffer_->data.gamma);
ASSERT_TRUE(orientation_buffer_->data.hasGamma);
- fetcher.StopFetchingDeviceOrientationData();
+ sensorManager.StopFetchingDeviceOrientationData();
ASSERT_FALSE(orientation_buffer_->data.allAvailableSensorsAreActive);
}
diff --git a/chromium/content/browser/devtools/BUILD.gn b/chromium/content/browser/devtools/BUILD.gn
new file mode 100644
index 00000000000..4aab2bab11e
--- /dev/null
+++ b/chromium/content/browser/devtools/BUILD.gn
@@ -0,0 +1,71 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//tools/grit/grit_rule.gni")
+
+# In GYP: devtools_resources target.
+group("resources") {
+ deps = [
+ ":devtools_resources",
+ ":devtools_protocol_constants",
+ ]
+}
+
+# In GYP: devtools_resources action in the devtools_resources target.
+action("devtools_resources") {
+ visibility = ":resources"
+
+ # This can't use grit_rule.gni because the grd file is generated at build
+ # time, so the trick of using grit_info to get the real inputs/outputs at GYP
+ # time isn't possible.
+ script = "//tools/grit/grit.py"
+
+ grdfile = "$root_gen_dir/devtools/devtools_resources.grd"
+
+ source_prereqs = [ grdfile ] +
+ rebase_path(exec_script("//tools/grit/grit_info.py", [ "--inputs" ],
+ "list lines"),
+ ".", "//")
+
+ out_dir = "$root_gen_dir/webkit"
+ outputs = [
+ "$out_dir/grit/devtools_resources.h",
+ "$out_dir/devtools_resources.pak",
+ "$out_dir/grit/devtools_resources_map.cc",
+ "$out_dir/grit/devtools_resources_map.h",
+ ]
+
+ args = [
+ "-i", rebase_path(grdfile, root_build_dir), "build",
+ "-f", rebase_path("//tools/gritsettings/resource_ids", root_build_dir),
+ "-o", rebase_path(out_dir, root_build_dir),
+ "-D", "SHARED_INTERMEDIATE_DIR=" +
+ rebase_path(root_gen_dir, root_build_dir),
+ ] + grit_defines
+
+ deps = [
+ # This is the action that generates out .grd input file.
+ "//third_party/WebKit/public:blink_generate_devtools_grd",
+ ]
+}
+
+action("devtools_protocol_constants") {
+ visibility = ":resources"
+
+ script = "//content/public/browser/devtools_protocol_constants_generator.py"
+
+ blink_protocol = "//third_party/WebKit/Source/devtools/protocol.json"
+ browser_protocol = "browser_protocol.json"
+ source_prereqs = [ blink_protocol, browser_protocol ]
+
+ outputs = [
+ "$target_gen_dir/devtools_protocol_constants.cc",
+ "$target_gen_dir/devtools_protocol_constants.h",
+ ]
+
+ args = [ "content" ] + rebase_path(outputs, root_build_dir) + [
+ rebase_path(blink_protocol, root_build_dir),
+ rebase_path(browser_protocol, root_build_dir),
+ ]
+}
diff --git a/chromium/content/browser/devtools/browser_protocol.json b/chromium/content/browser/devtools/browser_protocol.json
index 59e73c0c590..dfb462febf2 100644
--- a/chromium/content/browser/devtools/browser_protocol.json
+++ b/chromium/content/browser/devtools/browser_protocol.json
@@ -22,7 +22,8 @@
"properties": [
{ "name": "devices", "type": "array", "items": { "$ref": "GPUDevice" }, "description": "The graphics devices on the system. Element 0 is the primary GPU." },
{ "name": "auxAttributes", "type": "object", "optional": "true", "description": "An optional dictionary of additional GPU related attributes." },
- { "name": "featureStatus", "type": "object", "optional": "true", "description": "An optional dictionary of graphics features and their status." }
+ { "name": "featureStatus", "type": "object", "optional": "true", "description": "An optional dictionary of graphics features and their status." },
+ { "name": "driverBugWorkarounds", "type": "array", "items": { "type": "string" }, "description": "An optional array of GPU driver bug workarounds." }
],
"description": "Provides information about the GPU(s) on the system."
},
diff --git a/chromium/content/browser/devtools/devtools_agent_host_impl.cc b/chromium/content/browser/devtools/devtools_agent_host_impl.cc
index 087004296d5..bcb60b55b78 100644
--- a/chromium/content/browser/devtools/devtools_agent_host_impl.cc
+++ b/chromium/content/browser/devtools/devtools_agent_host_impl.cc
@@ -10,6 +10,7 @@
#include "base/guid.h"
#include "base/lazy_instance.h"
#include "content/browser/devtools/devtools_manager_impl.h"
+#include "content/browser/devtools/forwarding_agent_host.h"
#include "content/public/browser/browser_thread.h"
namespace content {
@@ -31,6 +32,7 @@ DevToolsAgentHostImpl::~DevToolsAgentHostImpl() {
g_instances.Get().erase(g_instances.Get().find(id_));
}
+//static
scoped_refptr<DevToolsAgentHost> DevToolsAgentHost::GetForId(
const std::string& id) {
if (g_instances == NULL)
@@ -41,6 +43,12 @@ scoped_refptr<DevToolsAgentHost> DevToolsAgentHost::GetForId(
return it->second;
}
+//static
+scoped_refptr<DevToolsAgentHost> DevToolsAgentHost::Create(
+ DevToolsExternalAgentProxyDelegate* delegate) {
+ return new ForwardingAgentHost(delegate);
+}
+
bool DevToolsAgentHostImpl::IsAttached() {
return !!DevToolsManagerImpl::GetInstance()->GetDevToolsClientHostFor(this);
}
@@ -60,6 +68,10 @@ void DevToolsAgentHostImpl::DisconnectRenderViewHost() {}
void DevToolsAgentHostImpl::ConnectRenderViewHost(RenderViewHost* rvh) {}
+bool DevToolsAgentHostImpl::IsWorker() const {
+ return false;
+}
+
void DevToolsAgentHostImpl::NotifyCloseListener() {
if (close_listener_) {
scoped_refptr<DevToolsAgentHostImpl> protect(this);
diff --git a/chromium/content/browser/devtools/devtools_agent_host_impl.h b/chromium/content/browser/devtools/devtools_agent_host_impl.h
index 9bd800dc398..d0fad668b49 100644
--- a/chromium/content/browser/devtools/devtools_agent_host_impl.h
+++ b/chromium/content/browser/devtools/devtools_agent_host_impl.h
@@ -53,6 +53,8 @@ class CONTENT_EXPORT DevToolsAgentHostImpl : public DevToolsAgentHost {
virtual void ConnectRenderViewHost(RenderViewHost* rvh) OVERRIDE;
+ virtual bool IsWorker() const OVERRIDE;
+
protected:
DevToolsAgentHostImpl();
virtual ~DevToolsAgentHostImpl();
diff --git a/chromium/content/browser/devtools/devtools_browser_target.cc b/chromium/content/browser/devtools/devtools_browser_target.cc
index a782462ba4c..b050152d2e4 100644
--- a/chromium/content/browser/devtools/devtools_browser_target.cc
+++ b/chromium/content/browser/devtools/devtools_browser_target.cc
@@ -5,9 +5,12 @@
#include "content/browser/devtools/devtools_browser_target.h"
#include "base/bind.h"
+#include "base/lazy_instance.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/message_loop/message_loop_proxy.h"
+#include "base/stl_util.h"
+#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "content/public/browser/browser_thread.h"
#include "net/server/http_server.h"
@@ -15,13 +18,11 @@
namespace content {
DevToolsBrowserTarget::DevToolsBrowserTarget(
- base::MessageLoopProxy* message_loop_proxy,
net::HttpServer* http_server,
int connection_id)
- : message_loop_proxy_(message_loop_proxy),
+ : message_loop_proxy_(base::MessageLoopProxy::current()),
http_server_(http_server),
connection_id_(connection_id),
- handlers_deleter_(&handlers_),
weak_factory_(this) {
}
@@ -29,6 +30,8 @@ void DevToolsBrowserTarget::RegisterDomainHandler(
const std::string& domain,
DevToolsProtocol::Handler* handler,
bool handle_on_ui_thread) {
+ DCHECK_EQ(message_loop_proxy_, base::MessageLoopProxy::current());
+
DCHECK(handlers_.find(domain) == handlers_.end());
handlers_[domain] = handler;
if (handle_on_ui_thread) {
@@ -41,7 +44,11 @@ void DevToolsBrowserTarget::RegisterDomainHandler(
}
}
+typedef std::map<std::string, DevToolsBrowserTarget*> DomainMap;
+base::LazyInstance<DomainMap>::Leaky g_used_domains = LAZY_INSTANCE_INITIALIZER;
+
void DevToolsBrowserTarget::HandleMessage(const std::string& data) {
+ DCHECK_EQ(message_loop_proxy_, base::MessageLoopProxy::current());
std::string error_response;
scoped_refptr<DevToolsProtocol::Command> command =
DevToolsProtocol::ParseCommand(data, &error_response);
@@ -55,9 +62,21 @@ void DevToolsBrowserTarget::HandleMessage(const std::string& data) {
Respond(command->NoSuchMethodErrorResponse()->Serialize());
return;
}
+ DomainMap& used_domains(g_used_domains.Get());
+ std::string domain = command->domain();
+ DomainMap::iterator jt = used_domains.find(domain);
+ if (jt == used_domains.end()) {
+ used_domains[domain] = this;
+ } else if (jt->second != this) {
+ std::string message =
+ base::StringPrintf("'%s' is held by another connection",
+ domain.c_str());
+ Respond(command->ServerErrorResponse(message)->Serialize());
+ return;
+ }
DevToolsProtocol::Handler* handler = it->second;
- bool handle_directly = handle_on_ui_thread_.find(command->domain()) ==
+ bool handle_directly = handle_on_ui_thread_.find(domain) ==
handle_on_ui_thread_.end();
if (handle_directly) {
scoped_refptr<DevToolsProtocol::Response> response =
@@ -81,9 +100,23 @@ void DevToolsBrowserTarget::HandleMessage(const std::string& data) {
}
void DevToolsBrowserTarget::Detach() {
- message_loop_proxy_ = NULL;
+ DCHECK_EQ(message_loop_proxy_, base::MessageLoopProxy::current());
+ DCHECK(http_server_);
+
http_server_ = NULL;
+ DomainMap& used_domains(g_used_domains.Get());
+ for (DomainMap::iterator it = used_domains.begin();
+ it != used_domains.end();) {
+ if (it->second == this) {
+ DomainMap::iterator to_erase = it;
+ ++it;
+ used_domains.erase(to_erase);
+ } else {
+ ++it;
+ }
+ }
+
std::vector<DevToolsProtocol::Handler*> ui_handlers;
for (std::set<std::string>::iterator domain_it = handle_on_ui_thread_.begin();
domain_it != handle_on_ui_thread_.end();
@@ -94,6 +127,8 @@ void DevToolsBrowserTarget::Detach() {
handlers_.erase(handler_it);
}
+ STLDeleteValues(&handlers_);
+
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
@@ -103,11 +138,14 @@ void DevToolsBrowserTarget::Detach() {
}
DevToolsBrowserTarget::~DevToolsBrowserTarget() {
+ // DCHECK that Detach has been called or no handler has ever been registered.
+ DCHECK(handlers_.empty());
}
void DevToolsBrowserTarget::HandleCommandOnUIThread(
DevToolsProtocol::Handler* handler,
scoped_refptr<DevToolsProtocol::Command> command) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
scoped_refptr<DevToolsProtocol::Response> response =
handler->HandleCommand(command);
if (response && response->is_async_promise())
@@ -121,18 +159,19 @@ void DevToolsBrowserTarget::HandleCommandOnUIThread(
void DevToolsBrowserTarget::DeleteHandlersOnUIThread(
std::vector<DevToolsProtocol::Handler*> handlers) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
STLDeleteElements(&handlers);
}
void DevToolsBrowserTarget::Respond(const std::string& message) {
+ DCHECK_EQ(message_loop_proxy_, base::MessageLoopProxy::current());
if (!http_server_)
return;
http_server_->SendOverWebSocket(connection_id_, message);
}
void DevToolsBrowserTarget::RespondFromUIThread(const std::string& message) {
- if (!message_loop_proxy_)
- return;
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
message_loop_proxy_->PostTask(
FROM_HERE,
base::Bind(&DevToolsBrowserTarget::Respond, this, message));
diff --git a/chromium/content/browser/devtools/devtools_browser_target.h b/chromium/content/browser/devtools/devtools_browser_target.h
index 46ed291cfd7..0dda59b2ad4 100644
--- a/chromium/content/browser/devtools/devtools_browser_target.h
+++ b/chromium/content/browser/devtools/devtools_browser_target.h
@@ -13,7 +13,6 @@
#include "base/compiler_specific.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
-#include "base/stl_util.h"
#include "content/browser/devtools/devtools_protocol.h"
namespace base {
@@ -32,9 +31,7 @@ namespace content {
class DevToolsBrowserTarget
: public base::RefCountedThreadSafe<DevToolsBrowserTarget> {
public:
- DevToolsBrowserTarget(base::MessageLoopProxy* message_loop_proxy,
- net::HttpServer* server,
- int connection_id);
+ DevToolsBrowserTarget(net::HttpServer* server, int connection_id);
int connection_id() const { return connection_id_; }
@@ -61,13 +58,12 @@ class DevToolsBrowserTarget
void Respond(const std::string& message);
void RespondFromUIThread(const std::string& message);
- base::MessageLoopProxy* message_loop_proxy_;
+ scoped_refptr<base::MessageLoopProxy> message_loop_proxy_;
net::HttpServer* http_server_;
const int connection_id_;
typedef std::map<std::string, DevToolsProtocol::Handler*> DomainHandlerMap;
DomainHandlerMap handlers_;
- STLValueDeleter<DomainHandlerMap> handlers_deleter_;
std::set<std::string> handle_on_ui_thread_;
base::WeakPtrFactory<DevToolsBrowserTarget> weak_factory_;
diff --git a/chromium/content/browser/devtools/devtools_external_agent_proxy_impl.cc b/chromium/content/browser/devtools/devtools_external_agent_proxy_impl.cc
deleted file mode 100644
index 0513dc238b1..00000000000
--- a/chromium/content/browser/devtools/devtools_external_agent_proxy_impl.cc
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/devtools/devtools_external_agent_proxy_impl.h"
-
-#include "content/browser/devtools/devtools_agent_host_impl.h"
-#include "content/browser/devtools/devtools_manager_impl.h"
-#include "content/public/browser/devtools_external_agent_proxy_delegate.h"
-
-namespace content {
-
-class DevToolsExternalAgentProxyImpl::ForwardingAgentHost
- : public DevToolsAgentHostImpl {
- public:
- ForwardingAgentHost(DevToolsExternalAgentProxyDelegate* delegate)
- : delegate_(delegate) {
- }
-
- void ConnectionClosed() {
- NotifyCloseListener();
- }
-
- private:
- virtual ~ForwardingAgentHost() {
- }
-
- // DevToolsAgentHostImpl implementation.
- virtual void Attach() OVERRIDE {
- delegate_->Attach();
- };
-
- virtual void Detach() OVERRIDE {
- delegate_->Detach();
- };
-
- virtual void DispatchOnInspectorBackend(const std::string& message) OVERRIDE {
- delegate_->SendMessageToBackend(message);
- }
-
- DevToolsExternalAgentProxyDelegate* delegate_;
-};
-
-//static
-DevToolsExternalAgentProxy* DevToolsExternalAgentProxy::Create(
- DevToolsExternalAgentProxyDelegate* delegate) {
- return new DevToolsExternalAgentProxyImpl(delegate);
-}
-
-DevToolsExternalAgentProxyImpl::DevToolsExternalAgentProxyImpl(
- DevToolsExternalAgentProxyDelegate* delegate)
- : agent_host_(new ForwardingAgentHost(delegate)) {
-}
-
-DevToolsExternalAgentProxyImpl::~DevToolsExternalAgentProxyImpl() {
-}
-
-scoped_refptr<DevToolsAgentHost> DevToolsExternalAgentProxyImpl::
- GetAgentHost() {
- return agent_host_;
-}
-
-void DevToolsExternalAgentProxyImpl::DispatchOnClientHost(
- const std::string& message) {
- DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(
- agent_host_.get(), message);
-}
-
-void DevToolsExternalAgentProxyImpl::ConnectionClosed() {
- agent_host_->ConnectionClosed();
-}
-
-} // content
diff --git a/chromium/content/browser/devtools/devtools_external_agent_proxy_impl.h b/chromium/content/browser/devtools/devtools_external_agent_proxy_impl.h
deleted file mode 100644
index cf025ec7b4f..00000000000
--- a/chromium/content/browser/devtools/devtools_external_agent_proxy_impl.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_EXTERNAL_AGENT_PROXY_IMPL_H
-#define CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_EXTERNAL_AGENT_PROXY_IMPL_H
-
-#include "base/memory/ref_counted.h"
-#include "content/public/browser/devtools_external_agent_proxy.h"
-
-namespace content {
-
-class DevToolsExternalAgentProxyImpl
- : public DevToolsExternalAgentProxy {
- public:
- explicit DevToolsExternalAgentProxyImpl(
- DevToolsExternalAgentProxyDelegate* delegate);
- virtual ~DevToolsExternalAgentProxyImpl();
-
- // DevToolsExternalAgentProxy implementation.
- virtual scoped_refptr<DevToolsAgentHost> GetAgentHost() OVERRIDE;
- virtual void DispatchOnClientHost(const std::string& message) OVERRIDE;
- virtual void ConnectionClosed() OVERRIDE;
-
- private:
- class ForwardingAgentHost;
-
- scoped_refptr<ForwardingAgentHost> agent_host_;
-
- DISALLOW_COPY_AND_ASSIGN(DevToolsExternalAgentProxyImpl);
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_EXTERNAL_AGENT_PROXY_IMPL_H
diff --git a/chromium/content/browser/devtools/devtools_http_handler_impl.cc b/chromium/content/browser/devtools/devtools_http_handler_impl.cc
index 1d0fca1e282..d1b5b1e6724 100644
--- a/chromium/content/browser/devtools/devtools_http_handler_impl.cc
+++ b/chromium/content/browser/devtools/devtools_http_handler_impl.cc
@@ -14,6 +14,7 @@
#include "base/logging.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
#include "base/threading/thread.h"
#include "base/values.h"
#include "content/browser/devtools/devtools_browser_target.h"
@@ -31,14 +32,15 @@
#include "content/public/browser/devtools_target.h"
#include "content/public/common/content_client.h"
#include "content/public/common/url_constants.h"
+#include "content/public/common/user_agent.h"
+#include "content/public/common/user_agent.h"
#include "grit/devtools_resources_map.h"
#include "net/base/escape.h"
#include "net/base/io_buffer.h"
#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
#include "net/server/http_server_request_info.h"
#include "net/server/http_server_response_info.h"
-#include "webkit/common/user_agent/user_agent.h"
-#include "webkit/common/user_agent/user_agent_util.h"
#if defined(OS_ANDROID)
#include "base/android/build_info.h"
@@ -48,7 +50,8 @@ namespace content {
namespace {
-const char kProtocolVersion[] = "1.0";
+const base::FilePath::CharType kDevToolsActivePortFileName[] =
+ FILE_PATH_LITERAL("DevToolsActivePort");
const char kDevToolsHandlerThreadName[] = "Chrome_DevToolsHandlerThread";
@@ -56,6 +59,7 @@ const char kThumbUrlPrefix[] = "/thumb/";
const char kPageUrlPrefix[] = "/devtools/page/";
const char kTargetIdField[] = "id";
+const char kTargetParentIdField[] = "parentId";
const char kTargetTypeField[] = "type";
const char kTargetTitleField[] = "title";
const char kTargetDescriptionField[] = "description";
@@ -135,7 +139,7 @@ static bool TimeComparator(const DevToolsTarget* target1,
// static
bool DevToolsHttpHandler::IsSupportedProtocolVersion(
const std::string& version) {
- return version == kProtocolVersion;
+ return devtools::IsSupportedProtocolVersion(version);
}
// static
@@ -151,11 +155,13 @@ int DevToolsHttpHandler::GetFrontendResourceId(const std::string& name) {
DevToolsHttpHandler* DevToolsHttpHandler::Start(
const net::StreamListenSocketFactory* socket_factory,
const std::string& frontend_url,
- DevToolsHttpHandlerDelegate* delegate) {
+ DevToolsHttpHandlerDelegate* delegate,
+ const base::FilePath& active_port_output_directory) {
DevToolsHttpHandlerImpl* http_handler =
new DevToolsHttpHandlerImpl(socket_factory,
frontend_url,
- delegate);
+ delegate,
+ active_port_output_directory);
http_handler->Start();
return http_handler;
}
@@ -215,8 +221,7 @@ GURL DevToolsHttpHandlerImpl::GetFrontendURL() {
net::IPEndPoint ip_address;
if (server_->GetLocalAddress(&ip_address))
return GURL();
- return GURL(std::string("http://") + ip_address.ToString() +
- overridden_frontend_url_);
+ return GURL(std::string("http://") + ip_address.ToString() + frontend_url_);
}
static std::string PathWithoutParams(const std::string& path) {
@@ -237,7 +242,12 @@ static std::string GetMimeType(const std::string& filename) {
return "image/png";
} else if (EndsWith(filename, ".gif", false)) {
return "image/gif";
+ } else if (EndsWith(filename, ".json", false)) {
+ return "application/json";
}
+ LOG(ERROR) << "GetMimeType doesn't know mime type for: "
+ << filename
+ << " text/plain will be returned";
NOTREACHED();
return "text/plain";
}
@@ -262,7 +272,7 @@ void DevToolsHttpHandlerImpl::OnHttpRequest(
DevToolsTarget* target = GetTarget(target_id);
GURL page_url;
if (target)
- page_url = target->GetUrl();
+ page_url = target->GetURL();
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
@@ -318,24 +328,21 @@ void DevToolsHttpHandlerImpl::OnWebSocketRequest(
std::string browser_prefix = "/devtools/browser";
size_t browser_pos = request.path.find(browser_prefix);
if (browser_pos == 0) {
- if (browser_target_) {
- server_->Send500(connection_id, "Another client already attached");
- return;
- }
- browser_target_ = new DevToolsBrowserTarget(
- thread_->message_loop_proxy().get(), server_.get(), connection_id);
- browser_target_->RegisterDomainHandler(
+ scoped_refptr<DevToolsBrowserTarget> browser_target =
+ new DevToolsBrowserTarget(server_.get(), connection_id);
+ browser_target->RegisterDomainHandler(
devtools::Tracing::kName,
- new DevToolsTracingHandler(),
+ new DevToolsTracingHandler(DevToolsTracingHandler::Browser),
true /* handle on UI thread */);
- browser_target_->RegisterDomainHandler(
+ browser_target->RegisterDomainHandler(
TetheringHandler::kDomain,
new TetheringHandler(delegate_.get()),
false /* handle on this thread */);
- browser_target_->RegisterDomainHandler(
+ browser_target->RegisterDomainHandler(
devtools::SystemInfo::kName,
new DevToolsSystemInfoHandler(),
true /* handle on UI thread */);
+ browser_targets_[connection_id] = browser_target;
server_->AcceptWebSocket(connection_id, request);
return;
@@ -354,8 +361,9 @@ void DevToolsHttpHandlerImpl::OnWebSocketRequest(
void DevToolsHttpHandlerImpl::OnWebSocketMessage(
int connection_id,
const std::string& data) {
- if (browser_target_ && connection_id == browser_target_->connection_id()) {
- browser_target_->HandleMessage(data);
+ BrowserTargets::iterator it = browser_targets_.find(connection_id);
+ if (it != browser_targets_.end()) {
+ it->second->HandleMessage(data);
return;
}
@@ -370,9 +378,10 @@ void DevToolsHttpHandlerImpl::OnWebSocketMessage(
}
void DevToolsHttpHandlerImpl::OnClose(int connection_id) {
- if (browser_target_ && browser_target_->connection_id() == connection_id) {
- browser_target_->Detach();
- browser_target_ = NULL;
+ BrowserTargets::iterator it = browser_targets_.find(connection_id);
+ if (it != browser_targets_.end()) {
+ it->second->Detach();
+ browser_targets_.erase(it);
return;
}
@@ -390,8 +399,8 @@ std::string DevToolsHttpHandlerImpl::GetFrontendURLInternal(
const std::string& host) {
return base::StringPrintf(
"%s%sws=%s%s%s",
- overridden_frontend_url_.c_str(),
- overridden_frontend_url_.find("?") == std::string::npos ? "?" : "&",
+ frontend_url_.c_str(),
+ frontend_url_.find("?") == std::string::npos ? "?" : "&",
host.c_str(),
kPageUrlPrefix,
id.c_str());
@@ -452,11 +461,10 @@ void DevToolsHttpHandlerImpl::OnJsonRequestUI(
if (command == "version") {
base::DictionaryValue version;
- version.SetString("Protocol-Version", kProtocolVersion);
- version.SetString("WebKit-Version", webkit_glue::GetWebKitVersion());
- version.SetString("Browser", content::GetContentClient()->GetProduct());
- version.SetString("User-Agent",
- webkit_glue::GetUserAgent(GURL(kAboutBlankURL)));
+ version.SetString("Protocol-Version", devtools::kProtocolVersion);
+ version.SetString("WebKit-Version", GetWebKitVersion());
+ version.SetString("Browser", GetContentClient()->GetProduct());
+ version.SetString("User-Agent", GetContentClient()->GetUserAgent());
#if defined(OS_ANDROID)
version.SetString("Android-Package",
base::android::BuildInfo::GetInstance()->package_name());
@@ -478,7 +486,7 @@ void DevToolsHttpHandlerImpl::OnJsonRequestUI(
GURL url(net::UnescapeURLComponent(
query, net::UnescapeRule::URL_SPECIAL_CHARS));
if (!url.is_valid())
- url = GURL(kAboutBlankURL);
+ url = GURL(url::kAboutBlankURL);
scoped_ptr<DevToolsTarget> target(delegate_->CreateNewTarget(url));
if (!target) {
SendJson(connection_id,
@@ -641,12 +649,14 @@ void DevToolsHttpHandlerImpl::OnCloseUI(int connection_id) {
DevToolsHttpHandlerImpl::DevToolsHttpHandlerImpl(
const net::StreamListenSocketFactory* socket_factory,
const std::string& frontend_url,
- DevToolsHttpHandlerDelegate* delegate)
- : overridden_frontend_url_(frontend_url),
+ DevToolsHttpHandlerDelegate* delegate,
+ const base::FilePath& active_port_output_directory)
+ : frontend_url_(frontend_url),
socket_factory_(socket_factory),
- delegate_(delegate) {
- if (overridden_frontend_url_.empty())
- overridden_frontend_url_ = "/devtools/devtools.html";
+ delegate_(delegate),
+ active_port_output_directory_(active_port_output_directory) {
+ if (frontend_url_.empty())
+ frontend_url_ = "/devtools/devtools.html";
// Balanced in ResetHandlerThreadAndRelease().
AddRef();
@@ -655,6 +665,8 @@ DevToolsHttpHandlerImpl::DevToolsHttpHandlerImpl(
// Runs on the handler thread
void DevToolsHttpHandlerImpl::Init() {
server_ = new net::HttpServer(*socket_factory_.get(), this);
+ if (!active_port_output_directory_.empty())
+ WriteActivePortToUserProfile();
}
// Runs on the handler thread
@@ -674,6 +686,25 @@ void DevToolsHttpHandlerImpl::StopHandlerThread() {
thread_->Stop();
}
+void DevToolsHttpHandlerImpl::WriteActivePortToUserProfile() {
+ DCHECK(!active_port_output_directory_.empty());
+ net::IPEndPoint endpoint;
+ int err;
+ if ((err = server_->GetLocalAddress(&endpoint)) != net::OK) {
+ LOG(ERROR) << "Error " << err << " getting local address";
+ return;
+ }
+
+ // Write this port to a well-known file in the profile directory
+ // so Telemetry can pick it up.
+ base::FilePath path = active_port_output_directory_.Append(
+ kDevToolsActivePortFileName);
+ std::string port_string = base::IntToString(endpoint.port());
+ if (base::WriteFile(path, port_string.c_str(), port_string.length()) < 0) {
+ LOG(ERROR) << "Error writing DevTools active port to file";
+ }
+}
+
void DevToolsHttpHandlerImpl::SendJson(int connection_id,
net::HttpStatusCode status_code,
base::Value* value,
@@ -753,15 +784,18 @@ base::DictionaryValue* DevToolsHttpHandlerImpl::SerializeTarget(
std::string id = target.GetId();
dictionary->SetString(kTargetIdField, id);
+ std::string parent_id = target.GetParentId();
+ if (!parent_id.empty())
+ dictionary->SetString(kTargetParentIdField, parent_id);
dictionary->SetString(kTargetTypeField, target.GetType());
dictionary->SetString(kTargetTitleField,
net::EscapeForHTML(target.GetTitle()));
dictionary->SetString(kTargetDescriptionField, target.GetDescription());
- GURL url = target.GetUrl();
+ GURL url = target.GetURL();
dictionary->SetString(kTargetUrlField, url.spec());
- GURL favicon_url = target.GetFaviconUrl();
+ GURL favicon_url = target.GetFaviconURL();
if (favicon_url.is_valid())
dictionary->SetString(kTargetFaviconUrlField, favicon_url.spec());
diff --git a/chromium/content/browser/devtools/devtools_http_handler_impl.h b/chromium/content/browser/devtools/devtools_http_handler_impl.h
index 4e40db07f11..0d08d9897aa 100644
--- a/chromium/content/browser/devtools/devtools_http_handler_impl.h
+++ b/chromium/content/browser/devtools/devtools_http_handler_impl.h
@@ -46,7 +46,8 @@ class DevToolsHttpHandlerImpl
// Takes ownership over |socket_factory|.
DevToolsHttpHandlerImpl(const net::StreamListenSocketFactory* socket_factory,
const std::string& frontend_url,
- DevToolsHttpHandlerDelegate* delegate);
+ DevToolsHttpHandlerDelegate* delegate,
+ const base::FilePath& active_port_output_directory);
virtual ~DevToolsHttpHandlerImpl();
void Start();
@@ -90,6 +91,8 @@ class DevToolsHttpHandlerImpl
void StartHandlerThread();
void StopHandlerThread();
+ void WriteActivePortToUserProfile();
+
void SendJson(int connection_id,
net::HttpStatusCode status_code,
base::Value* value,
@@ -113,15 +116,17 @@ class DevToolsHttpHandlerImpl
// The thread used by the devtools handler to run server socket.
scoped_ptr<base::Thread> thread_;
- std::string overridden_frontend_url_;
+ std::string frontend_url_;
scoped_ptr<const net::StreamListenSocketFactory> socket_factory_;
scoped_refptr<net::HttpServer> server_;
typedef std::map<int, DevToolsClientHost*> ConnectionToClientHostMap;
ConnectionToClientHostMap connection_to_client_host_ui_;
scoped_ptr<DevToolsHttpHandlerDelegate> delegate_;
+ base::FilePath active_port_output_directory_;
typedef std::map<std::string, DevToolsTarget*> TargetMap;
TargetMap target_map_;
- scoped_refptr<DevToolsBrowserTarget> browser_target_;
+ typedef std::map<int, scoped_refptr<DevToolsBrowserTarget> > BrowserTargets;
+ BrowserTargets browser_targets_;
DISALLOW_COPY_AND_ASSIGN(DevToolsHttpHandlerImpl);
};
diff --git a/chromium/content/browser/devtools/devtools_http_handler_unittest.cc b/chromium/content/browser/devtools/devtools_http_handler_unittest.cc
index f3179f0f140..88710924c2a 100644
--- a/chromium/content/browser/devtools/devtools_http_handler_unittest.cc
+++ b/chromium/content/browser/devtools/devtools_http_handler_unittest.cc
@@ -2,25 +2,34 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
+#include "base/strings/string_number_conversions.h"
#include "content/browser/browser_thread_impl.h"
#include "content/public/browser/devtools_http_handler.h"
#include "content/public/browser/devtools_http_handler_delegate.h"
#include "content/public/browser/devtools_target.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
#include "net/socket/stream_listen_socket.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
namespace {
+const int kDummyPort = 4321;
+const base::FilePath::CharType kDevToolsActivePortFileName[] =
+ FILE_PATH_LITERAL("DevToolsActivePort");
+
using net::StreamListenSocket;
class DummyListenSocket : public StreamListenSocket,
public StreamListenSocket::Delegate {
public:
DummyListenSocket()
- : StreamListenSocket(0, this) {}
+ : StreamListenSocket(net::kInvalidSocket, this) {}
// StreamListenSocket::Delegate "implementation"
virtual void DidAccept(StreamListenSocket* server,
@@ -32,6 +41,12 @@ class DummyListenSocket : public StreamListenSocket,
protected:
virtual ~DummyListenSocket() {}
virtual void Accept() OVERRIDE {}
+ virtual int GetLocalAddress(net::IPEndPoint* address) OVERRIDE {
+ net::IPAddressNumber number;
+ EXPECT_TRUE(net::ParseIPLiteralToNumber("127.0.0.1", &number));
+ *address = net::IPEndPoint(number, kDummyPort);
+ return net::OK;
+ }
};
class DummyListenSocketFactory : public net::StreamListenSocketFactory {
@@ -106,7 +121,8 @@ TEST_F(DevToolsHttpHandlerTest, TestStartStop) {
new DummyListenSocketFactory(run_loop.QuitClosure(),
run_loop_2.QuitClosure()),
std::string(),
- new DummyDelegate());
+ new DummyDelegate(),
+ base::FilePath());
// Our dummy socket factory will post a quit message once the server will
// become ready.
run_loop.Run();
@@ -115,4 +131,34 @@ TEST_F(DevToolsHttpHandlerTest, TestStartStop) {
run_loop_2.Run();
}
+TEST_F(DevToolsHttpHandlerTest, TestDevToolsActivePort) {
+ base::RunLoop run_loop, run_loop_2;
+ base::ScopedTempDir temp_dir;
+ EXPECT_TRUE(temp_dir.CreateUniqueTempDir());
+ content::DevToolsHttpHandler* devtools_http_handler_ =
+ content::DevToolsHttpHandler::Start(
+ new DummyListenSocketFactory(run_loop.QuitClosure(),
+ run_loop_2.QuitClosure()),
+ std::string(),
+ new DummyDelegate(),
+ temp_dir.path());
+ // Our dummy socket factory will post a quit message once the server will
+ // become ready.
+ run_loop.Run();
+ devtools_http_handler_->Stop();
+ // Make sure the handler actually stops.
+ run_loop_2.Run();
+
+ // Now make sure the DevToolsActivePort was written into the
+ // temporary directory and its contents are as expected.
+ base::FilePath active_port_file = temp_dir.path().Append(
+ kDevToolsActivePortFileName);
+ EXPECT_TRUE(base::PathExists(active_port_file));
+ std::string file_contents;
+ EXPECT_TRUE(base::ReadFileToString(active_port_file, &file_contents));
+ int port = 0;
+ EXPECT_TRUE(base::StringToInt(file_contents, &port));
+ EXPECT_EQ(kDummyPort, port);
+}
+
} // namespace content
diff --git a/chromium/content/browser/devtools/devtools_manager_impl.cc b/chromium/content/browser/devtools/devtools_manager_impl.cc
index 56b2dd7d2f4..3aafd1c2e2e 100644
--- a/chromium/content/browser/devtools/devtools_manager_impl.cc
+++ b/chromium/content/browser/devtools/devtools_manager_impl.cc
@@ -13,7 +13,9 @@
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/devtools_client_host.h"
+#include "content/public/browser/devtools_manager_delegate.h"
namespace content {
@@ -27,7 +29,8 @@ DevToolsManagerImpl* DevToolsManagerImpl::GetInstance() {
return Singleton<DevToolsManagerImpl>::get();
}
-DevToolsManagerImpl::DevToolsManagerImpl() {
+DevToolsManagerImpl::DevToolsManagerImpl()
+ : delegate_(GetContentClient()->browser()->GetDevToolsManagerDelegate()) {
}
DevToolsManagerImpl::~DevToolsManagerImpl() {
@@ -35,6 +38,12 @@ DevToolsManagerImpl::~DevToolsManagerImpl() {
DCHECK(client_to_agent_host_.empty());
}
+void DevToolsManagerImpl::Inspect(BrowserContext* browser_context,
+ DevToolsAgentHost* agent_host) {
+ if (delegate_)
+ delegate_->Inspect(browser_context, agent_host);
+}
+
DevToolsClientHost* DevToolsManagerImpl::GetDevToolsClientHostFor(
DevToolsAgentHostImpl* agent_host_impl) {
AgentToClientHostMap::iterator it =
@@ -184,6 +193,8 @@ void DevToolsManagerImpl::RemoveAgentStateCallback(const Callback& callback) {
void DevToolsManagerImpl::NotifyObservers(DevToolsAgentHost* agent_host,
bool attached) {
CallbackContainer copy(callbacks_);
+ if (delegate_)
+ delegate_->DevToolsAgentStateChanged(agent_host, attached);
for (CallbackContainer::iterator it = copy.begin(); it != copy.end(); ++it)
(*it)->Run(agent_host, attached);
}
diff --git a/chromium/content/browser/devtools/devtools_manager_impl.h b/chromium/content/browser/devtools/devtools_manager_impl.h
index a14b2f1ca11..19b6918518b 100644
--- a/chromium/content/browser/devtools/devtools_manager_impl.h
+++ b/chromium/content/browser/devtools/devtools_manager_impl.h
@@ -24,6 +24,8 @@ class Message;
namespace content {
+class BrowserContext;
+class DevToolsManagerDelegate;
class RenderViewHost;
// This class is a singleton that manages DevToolsClientHost instances and
@@ -43,9 +45,14 @@ class CONTENT_EXPORT DevToolsManagerImpl
DevToolsManagerImpl();
virtual ~DevToolsManagerImpl();
+ // Opens the inspector for |agent_host|.
+ void Inspect(BrowserContext* browser_context, DevToolsAgentHost* agent_host);
+
void DispatchOnInspectorFrontend(DevToolsAgentHost* agent_host,
const std::string& message);
+ DevToolsManagerDelegate* delegate() const { return delegate_.get(); }
+
// DevToolsManager implementation
virtual bool DispatchOnInspectorBackend(DevToolsClientHost* from,
const std::string& message) OVERRIDE;
@@ -96,6 +103,8 @@ class CONTENT_EXPORT DevToolsManagerImpl
typedef std::vector<const Callback*> CallbackContainer;
CallbackContainer callbacks_;
+ scoped_ptr<DevToolsManagerDelegate> delegate_;
+
DISALLOW_COPY_AND_ASSIGN(DevToolsManagerImpl);
};
diff --git a/chromium/content/browser/devtools/devtools_manager_unittest.cc b/chromium/content/browser/devtools/devtools_manager_unittest.cc
index aac9b6cbb92..75c527388b4 100644
--- a/chromium/content/browser/devtools/devtools_manager_unittest.cc
+++ b/chromium/content/browser/devtools/devtools_manager_unittest.cc
@@ -216,7 +216,7 @@ class TestExternalAgentDelegate: public DevToolsExternalAgentProxyDelegate {
EXPECT_EQ(count, event_counter_[name]);
}
- virtual void Attach() OVERRIDE {
+ virtual void Attach(DevToolsExternalAgentProxy* proxy) OVERRIDE {
recordEvent("Attach");
};
@@ -239,12 +239,10 @@ class TestExternalAgentDelegate: public DevToolsExternalAgentProxyDelegate {
};
TEST_F(DevToolsManagerTest, TestExternalProxy) {
- TestExternalAgentDelegate delegate;
+ TestExternalAgentDelegate* delegate = new TestExternalAgentDelegate();
- scoped_ptr<DevToolsExternalAgentProxy> proxy(
- DevToolsExternalAgentProxy::Create(&delegate));
-
- scoped_refptr<DevToolsAgentHost> agent_host = proxy->GetAgentHost();
+ scoped_refptr<DevToolsAgentHost> agent_host =
+ DevToolsAgentHost::Create(delegate);
EXPECT_EQ(agent_host, DevToolsAgentHost::GetForId(agent_host->GetId()));
DevToolsManager* manager = DevToolsManager::GetInstance();
diff --git a/chromium/content/browser/devtools/devtools_netlog_observer.cc b/chromium/content/browser/devtools/devtools_netlog_observer.cc
index 21a835b9e99..27ce58ae49b 100644
--- a/chromium/content/browser/devtools/devtools_netlog_observer.cc
+++ b/chromium/content/browser/devtools/devtools_netlog_observer.cc
@@ -42,10 +42,6 @@ void DevToolsNetLogObserver::OnAddEntry(const net::NetLog::Entry& entry) {
if (entry.source().type == net::NetLog::SOURCE_URL_REQUEST)
OnAddURLRequestEntry(entry);
- else if (entry.source().type == net::NetLog::SOURCE_HTTP_STREAM_JOB)
- OnAddHTTPStreamJobEntry(entry);
- else if (entry.source().type == net::NetLog::SOURCE_SOCKET)
- OnAddSocketEntry(entry);
}
void DevToolsNetLogObserver::OnAddURLRequestEntry(
@@ -74,22 +70,12 @@ void DevToolsNetLogObserver::OnAddURLRequestEntry(
}
request_to_info_[entry.source().id] = new ResourceInfo();
-
- if (request_to_encoded_data_length_.size() > kMaxNumEntries) {
- LOG(WARNING) << "The encoded data length observer url request count "
- "has grown larger than expected, resetting";
- request_to_encoded_data_length_.clear();
- }
-
- request_to_encoded_data_length_[entry.source().id] = 0;
}
return;
} else if (entry.type() == net::NetLog::TYPE_REQUEST_ALIVE) {
// Cleanup records based on the TYPE_REQUEST_ALIVE entry.
- if (is_end) {
+ if (is_end)
request_to_info_.erase(entry.source().id);
- request_to_encoded_data_length_.erase(entry.source().id);
- }
return;
}
@@ -162,35 +148,15 @@ void DevToolsNetLogObserver::OnAddURLRequestEntry(
response_headers->EnumerateHeaderLines(&it, &name, &value); ) {
info->response_headers.push_back(std::make_pair(name, value));
}
- info->response_headers_text =
- net::HttpUtil::ConvertHeadersBackToHTTPResponse(
- response_headers->raw_headers());
- break;
- }
- case net::NetLog::TYPE_HTTP_STREAM_REQUEST_BOUND_TO_JOB: {
- scoped_ptr<base::Value> event_params(entry.ParametersToValue());
- net::NetLog::Source http_stream_job_source;
- if (!net::NetLog::Source::FromEventParameters(event_params.get(),
- &http_stream_job_source)) {
- NOTREACHED();
- break;
- }
- uint32 http_stream_job_id = http_stream_job_source.id;
- HTTPStreamJobToSocketMap::iterator it =
- http_stream_job_to_socket_.find(http_stream_job_id);
- if (it == http_stream_job_to_socket_.end())
- return;
- uint32 socket_id = it->second;
-
- if (socket_to_request_.size() > kMaxNumEntries) {
- LOG(WARNING) << "The url request observer socket count has grown "
- "larger than expected, resetting";
- socket_to_request_.clear();
+ if (!info->request_headers_text.empty()) {
+ info->response_headers_text =
+ net::HttpUtil::ConvertHeadersBackToHTTPResponse(
+ response_headers->raw_headers());
+ } else {
+ // SPDY request.
+ info->response_headers_text = "";
}
-
- socket_to_request_[socket_id] = entry.source().id;
- http_stream_job_to_socket_.erase(http_stream_job_id);
break;
}
default:
@@ -198,68 +164,6 @@ void DevToolsNetLogObserver::OnAddURLRequestEntry(
}
}
-void DevToolsNetLogObserver::OnAddHTTPStreamJobEntry(
- const net::NetLog::Entry& entry) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-
- if (entry.type() == net::NetLog::TYPE_SOCKET_POOL_BOUND_TO_SOCKET) {
- scoped_ptr<base::Value> event_params(entry.ParametersToValue());
- net::NetLog::Source socket_source;
- if (!net::NetLog::Source::FromEventParameters(event_params.get(),
- &socket_source)) {
- NOTREACHED();
- return;
- }
-
- // Prevents us from passively growing the memory unbounded in
- // case something went wrong. Should not happen.
- if (http_stream_job_to_socket_.size() > kMaxNumEntries) {
- LOG(WARNING) << "The load timing observer http stream job count "
- "has grown larger than expected, resetting";
- http_stream_job_to_socket_.clear();
- }
- http_stream_job_to_socket_[entry.source().id] = socket_source.id;
- }
-}
-
-void DevToolsNetLogObserver::OnAddSocketEntry(
- const net::NetLog::Entry& entry) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-
- bool is_end = entry.phase() == net::NetLog::PHASE_END;
-
- SocketToRequestMap::iterator it =
- socket_to_request_.find(entry.source().id);
- if (it == socket_to_request_.end())
- return;
- uint32 request_id = it->second;
-
- if (entry.type() == net::NetLog::TYPE_SOCKET_IN_USE) {
- if (is_end)
- socket_to_request_.erase(entry.source().id);
- return;
- }
-
- RequestToEncodedDataLengthMap::iterator encoded_data_length_it =
- request_to_encoded_data_length_.find(request_id);
- if (encoded_data_length_it == request_to_encoded_data_length_.end())
- return;
-
- if (net::NetLog::TYPE_SOCKET_BYTES_RECEIVED == entry.type()) {
- int byte_count = 0;
- scoped_ptr<base::Value> value(entry.ParametersToValue());
- if (!value->IsType(base::Value::TYPE_DICTIONARY))
- return;
-
- base::DictionaryValue* dValue =
- static_cast<base::DictionaryValue*>(value.get());
- if (!dValue->GetInteger("byte_count", &byte_count))
- return;
-
- encoded_data_length_it->second += byte_count;
- }
-}
-
void DevToolsNetLogObserver::Attach() {
DCHECK(!instance_);
net::NetLog* net_log = GetContentClient()->browser()->GetNetLog();
@@ -303,26 +207,4 @@ void DevToolsNetLogObserver::PopulateResponseInfo(
dev_tools_net_log_observer->GetResourceInfo(source_id);
}
-// static
-int DevToolsNetLogObserver::GetAndResetEncodedDataLength(
- net::URLRequest* request) {
- if (!(request->load_flags() & net::LOAD_REPORT_RAW_HEADERS))
- return -1;
-
- uint32 source_id = request->net_log().source().id;
- DevToolsNetLogObserver* dev_tools_net_log_observer =
- DevToolsNetLogObserver::GetInstance();
- if (dev_tools_net_log_observer == NULL)
- return -1;
-
- RequestToEncodedDataLengthMap::iterator it =
- dev_tools_net_log_observer->request_to_encoded_data_length_.find(
- source_id);
- if (it == dev_tools_net_log_observer->request_to_encoded_data_length_.end())
- return -1;
- int encoded_data_length = it->second;
- it->second = 0;
- return encoded_data_length;
-}
-
} // namespace content
diff --git a/chromium/content/browser/devtools/devtools_netlog_observer.h b/chromium/content/browser/devtools/devtools_netlog_observer.h
index d1d478c3791..f063a3d432c 100644
--- a/chromium/content/browser/devtools/devtools_netlog_observer.h
+++ b/chromium/content/browser/devtools/devtools_netlog_observer.h
@@ -7,8 +7,8 @@
#include "base/containers/hash_tables.h"
#include "base/memory/ref_counted.h"
+#include "content/public/common/resource_devtools_info.h"
#include "net/base/net_log.h"
-#include "webkit/common/resource_devtools_info.h"
namespace net {
class URLRequest;
@@ -25,15 +25,13 @@ struct ResourceResponse;
// IO Thread, it must also reside on the IO Thread. Only OnAddEntry can be
// called from other threads.
class DevToolsNetLogObserver : public net::NetLog::ThreadSafeObserver {
- typedef webkit_glue::ResourceDevToolsInfo ResourceInfo;
+ typedef ResourceDevToolsInfo ResourceInfo;
public:
// net::NetLog::ThreadSafeObserver implementation:
virtual void OnAddEntry(const net::NetLog::Entry& entry) OVERRIDE;
void OnAddURLRequestEntry(const net::NetLog::Entry& entry);
- void OnAddHTTPStreamJobEntry(const net::NetLog::Entry& entry);
- void OnAddSocketEntry(const net::NetLog::Entry& entry);
static void Attach();
static void Detach();
@@ -43,7 +41,6 @@ class DevToolsNetLogObserver : public net::NetLog::ThreadSafeObserver {
static DevToolsNetLogObserver* GetInstance();
static void PopulateResponseInfo(net::URLRequest*,
ResourceResponse*);
- static int GetAndResetEncodedDataLength(net::URLRequest* request);
private:
static DevToolsNetLogObserver* instance_;
@@ -54,13 +51,7 @@ class DevToolsNetLogObserver : public net::NetLog::ThreadSafeObserver {
ResourceInfo* GetResourceInfo(uint32 id);
typedef base::hash_map<uint32, scoped_refptr<ResourceInfo> > RequestToInfoMap;
- typedef base::hash_map<uint32, int> RequestToEncodedDataLengthMap;
- typedef base::hash_map<uint32, uint32> HTTPStreamJobToSocketMap;
- typedef base::hash_map<uint32, uint32> SocketToRequestMap;
RequestToInfoMap request_to_info_;
- RequestToEncodedDataLengthMap request_to_encoded_data_length_;
- HTTPStreamJobToSocketMap http_stream_job_to_socket_;
- SocketToRequestMap socket_to_request_;
DISALLOW_COPY_AND_ASSIGN(DevToolsNetLogObserver);
};
diff --git a/chromium/content/browser/devtools/devtools_power_handler.cc b/chromium/content/browser/devtools/devtools_power_handler.cc
new file mode 100644
index 00000000000..43772b47607
--- /dev/null
+++ b/chromium/content/browser/devtools/devtools_power_handler.cc
@@ -0,0 +1,83 @@
+// 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/devtools/devtools_power_handler.h"
+
+#include "base/bind.h"
+#include "base/values.h"
+#include "content/browser/devtools/devtools_protocol_constants.h"
+#include "content/browser/power_profiler/power_profiler_service.h"
+
+namespace content {
+
+DevToolsPowerHandler::DevToolsPowerHandler() {
+ RegisterCommandHandler(devtools::Power::start::kName,
+ base::Bind(&DevToolsPowerHandler::OnStart,
+ base::Unretained(this)));
+ RegisterCommandHandler(devtools::Power::end::kName,
+ base::Bind(&DevToolsPowerHandler::OnEnd,
+ base::Unretained(this)));
+ RegisterCommandHandler(devtools::Power::canProfilePower::kName,
+ base::Bind(&DevToolsPowerHandler::OnCanProfilePower,
+ base::Unretained(this)));
+}
+
+DevToolsPowerHandler::~DevToolsPowerHandler() {
+ PowerProfilerService::GetInstance()->RemoveObserver(this);
+}
+
+void DevToolsPowerHandler::OnPowerEvent(const PowerEventVector& events) {
+ base::DictionaryValue* params = new base::DictionaryValue();
+ base::ListValue* event_list = new base::ListValue();
+
+ std::vector<PowerEvent>::const_iterator iter;
+ for (iter = events.begin(); iter != events.end(); ++iter) {
+ base::DictionaryValue* event_body = new base::DictionaryValue();
+
+ DCHECK(iter->type < PowerEvent::ID_COUNT);
+ event_body->SetString("type", kPowerTypeNames[iter->type]);
+ // Use internal value to be consistent with Blink's
+ // monotonicallyIncreasingTime.
+ event_body->SetDouble("timestamp", iter->time.ToInternalValue() /
+ static_cast<double>(base::Time::kMicrosecondsPerMillisecond));
+ event_body->SetDouble("value", iter->value);
+ event_list->Append(event_body);
+ }
+
+ params->Set(devtools::Power::dataAvailable::kParamValue, event_list);
+ SendNotification(devtools::Power::dataAvailable::kName, params);
+}
+
+scoped_refptr<DevToolsProtocol::Response>
+DevToolsPowerHandler::OnStart(
+ scoped_refptr<DevToolsProtocol::Command> command) {
+ if (PowerProfilerService::GetInstance()->IsAvailable()) {
+ PowerProfilerService::GetInstance()->AddObserver(this);
+ return command->SuccessResponse(NULL);
+ }
+
+ return command->InternalErrorResponse("Power profiler service unavailable");
+}
+
+scoped_refptr<DevToolsProtocol::Response>
+DevToolsPowerHandler::OnEnd(scoped_refptr<DevToolsProtocol::Command> command) {
+ if (PowerProfilerService::GetInstance()->IsAvailable()) {
+ PowerProfilerService::GetInstance()->RemoveObserver(this);
+ return command->SuccessResponse(NULL);
+ }
+
+ return command->InternalErrorResponse("Power profiler service unavailable");
+}
+
+scoped_refptr<DevToolsProtocol::Response>
+DevToolsPowerHandler::OnCanProfilePower(
+ scoped_refptr<DevToolsProtocol::Command> command) {
+ base::DictionaryValue* result = new base::DictionaryValue();
+ result->SetBoolean(devtools::kResult,
+ PowerProfilerService::GetInstance()->IsAvailable());
+
+ return command->SuccessResponse(result);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/devtools/devtools_power_handler.h b/chromium/content/browser/devtools/devtools_power_handler.h
new file mode 100644
index 00000000000..f645392a199
--- /dev/null
+++ b/chromium/content/browser/devtools/devtools_power_handler.h
@@ -0,0 +1,38 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_POWER_HANDLER_H_
+#define CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_POWER_HANDLER_H_
+
+#include "base/time/time.h"
+#include "content/browser/devtools/devtools_protocol.h"
+#include "content/browser/power_profiler/power_profiler_observer.h"
+
+namespace content {
+
+// This class provides power information to DevTools.
+class DevToolsPowerHandler
+ : public DevToolsProtocol::Handler,
+ public PowerProfilerObserver {
+ public:
+ DevToolsPowerHandler();
+ virtual ~DevToolsPowerHandler();
+
+ // PowerProfilerObserver override.
+ virtual void OnPowerEvent(const PowerEventVector&) OVERRIDE;
+
+ private:
+ scoped_refptr<DevToolsProtocol::Response> OnStart(
+ scoped_refptr<DevToolsProtocol::Command> command);
+ scoped_refptr<DevToolsProtocol::Response> OnEnd(
+ scoped_refptr<DevToolsProtocol::Command> command);
+ scoped_refptr<DevToolsProtocol::Response> OnCanProfilePower(
+ scoped_refptr<DevToolsProtocol::Command> command);
+
+ DISALLOW_COPY_AND_ASSIGN(DevToolsPowerHandler);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_POWER_HANDLER_H_
diff --git a/chromium/content/browser/devtools/devtools_protocol.cc b/chromium/content/browser/devtools/devtools_protocol.cc
index e0e544e54c4..5e127f1dcba 100644
--- a/chromium/content/browser/devtools/devtools_protocol.cc
+++ b/chromium/content/browser/devtools/devtools_protocol.cc
@@ -27,7 +27,8 @@ enum Error {
kErrorInvalidRequest = -32600,
kErrorNoSuchMethod = -32601,
kErrorInvalidParams = -32602,
- kErrorInternalError = -32603
+ kErrorInternalError = -32603,
+ kErrorServerError = -32000
};
} // namespace
@@ -84,6 +85,11 @@ DevToolsProtocol::Command::NoSuchMethodErrorResponse() {
}
scoped_refptr<DevToolsProtocol::Response>
+DevToolsProtocol::Command::ServerErrorResponse(const std::string& message) {
+ return new Response(id_, kErrorServerError, message);
+}
+
+scoped_refptr<DevToolsProtocol::Response>
DevToolsProtocol::Command::AsyncResponsePromise() {
scoped_refptr<DevToolsProtocol::Response> promise =
new DevToolsProtocol::Response(0, NULL);
@@ -216,13 +222,20 @@ scoped_refptr<DevToolsProtocol::Command> DevToolsProtocol::ParseCommand(
std::string* error_response) {
scoped_ptr<base::DictionaryValue> command_dict(
ParseMessage(json, error_response));
+ return ParseCommand(command_dict.get(), error_response);
+}
+
+// static
+scoped_refptr<DevToolsProtocol::Command> DevToolsProtocol::ParseCommand(
+ base::DictionaryValue* command_dict,
+ std::string* error_response) {
if (!command_dict)
return NULL;
int id;
std::string method;
bool ok = command_dict->GetInteger(kIdParam, &id) && id >= 0;
- ok = ok && ParseMethod(command_dict.get(), &method);
+ ok = ok && ParseMethod(command_dict, &method);
if (!ok) {
scoped_refptr<Response> response =
new Response(kNoId, kErrorInvalidRequest, "No such method");
@@ -244,6 +257,28 @@ DevToolsProtocol::CreateCommand(
return new Command(id, method, params);
}
+//static
+scoped_refptr<DevToolsProtocol::Response>
+DevToolsProtocol::ParseResponse(
+ base::DictionaryValue* response_dict) {
+ int id;
+ if (!response_dict->GetInteger(kIdParam, &id))
+ id = kNoId;
+
+ const base::DictionaryValue* error_dict;
+ if (response_dict->GetDictionary(kErrorParam, &error_dict)) {
+ int error_code = kErrorInternalError;
+ response_dict->GetInteger(kErrorCodeParam, &error_code);
+ std::string error_message;
+ response_dict->GetString(kErrorMessageParam, &error_message);
+ return new Response(id, error_code, error_message);
+ }
+
+ const base::DictionaryValue* result = NULL;
+ response_dict->GetDictionary(kResultParam, &result);
+ return new Response(id, result ? result->DeepCopy() : NULL);
+}
+
// static
scoped_refptr<DevToolsProtocol::Notification>
DevToolsProtocol::ParseNotification(const std::string& json) {
@@ -275,11 +310,11 @@ base::DictionaryValue* DevToolsProtocol::ParseMessage(
std::string* error_response) {
int parse_error_code;
std::string error_message;
- scoped_ptr<Value> message(
+ scoped_ptr<base::Value> message(
base::JSONReader::ReadAndReturnError(
json, 0, &parse_error_code, &error_message));
- if (!message || !message->IsType(Value::TYPE_DICTIONARY)) {
+ if (!message || !message->IsType(base::Value::TYPE_DICTIONARY)) {
scoped_refptr<Response> response =
new Response(0, kErrorParseError, error_message);
if (error_response)
diff --git a/chromium/content/browser/devtools/devtools_protocol.h b/chromium/content/browser/devtools/devtools_protocol.h
index 15b0a8087be..cbc075b3715 100644
--- a/chromium/content/browser/devtools/devtools_protocol.h
+++ b/chromium/content/browser/devtools/devtools_protocol.h
@@ -64,6 +64,9 @@ class DevToolsProtocol {
// Creates error response.
scoped_refptr<Response> NoSuchMethodErrorResponse();
+ // Creates error response.
+ scoped_refptr<Response> ServerErrorResponse(const std::string& message);
+
// Creates async response promise.
scoped_refptr<Response> AsyncResponsePromise();
@@ -157,25 +160,32 @@ class DevToolsProtocol {
DISALLOW_COPY_AND_ASSIGN(Handler);
};
+ CONTENT_EXPORT static base::DictionaryValue* ParseMessage(
+ const std::string& json,
+ std::string* error_response);
+
CONTENT_EXPORT static scoped_refptr<Command> ParseCommand(
const std::string& json,
std::string* error_response);
+ CONTENT_EXPORT static scoped_refptr<Command> ParseCommand(
+ base::DictionaryValue* command_dict,
+ std::string* error_response);
+
CONTENT_EXPORT static scoped_refptr<Command> CreateCommand(
int id,
const std::string& method,
base::DictionaryValue* params);
+ CONTENT_EXPORT static scoped_refptr<Response> ParseResponse(
+ base::DictionaryValue* response_dict);
+
static scoped_refptr<Notification> ParseNotification(
const std::string& json);
static scoped_refptr<Notification> CreateNotification(
const std::string& method, base::DictionaryValue* params);
- private:
- static base::DictionaryValue* ParseMessage(const std::string& json,
- std::string* error_response);
-
DevToolsProtocol() {}
~DevToolsProtocol() {}
};
diff --git a/chromium/content/browser/devtools/devtools_protocol_constants.cc b/chromium/content/browser/devtools/devtools_protocol_constants.cc
deleted file mode 100644
index af5ecd1f580..00000000000
--- a/chromium/content/browser/devtools/devtools_protocol_constants.cc
+++ /dev/null
@@ -1,293 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// THIS FILE IS AUTOGENERATED.
-// If you need change something in this file, please see
-// protocol.json and browser_protocol.json
-
-#include "content/browser/devtools/devtools_protocol_constants.h"
-
-namespace content {
-namespace devtools {
-
-const char kResult[] = "result";
-
-namespace DOM {
- const char kName[] = "DOM";
-
- namespace Rect {
- const char kParamHeight[] = "height";
- const char kParamWidth[] = "width";
- const char kParamX[] = "x";
- const char kParamY[] = "y";
- } // Rect
-
- namespace setFileInputFiles {
- const char kName[] = "DOM.setFileInputFiles";
- const char kParamFiles[] = "files";
- const char kParamNodeId[] = "nodeId";
- } // setFileInputFiles
-} // DOM
-
-namespace Input {
- const char kName[] = "Input";
-
- namespace dispatchGestureEvent {
- const char kName[] = "Input.dispatchGestureEvent";
- const char kParamDeltaX[] = "deltaX";
- const char kParamDeltaY[] = "deltaY";
- const char kParamPinchScale[] = "pinchScale";
- const char kParamTimestamp[] = "timestamp";
- const char kParamType[] = "type";
- const char kParamX[] = "x";
- const char kParamY[] = "y";
-
- namespace Type {
- const char kEnumPinchBegin[] = "pinchBegin";
- const char kEnumPinchEnd[] = "pinchEnd";
- const char kEnumPinchUpdate[] = "pinchUpdate";
- const char kEnumScrollBegin[] = "scrollBegin";
- const char kEnumScrollEnd[] = "scrollEnd";
- const char kEnumScrollUpdate[] = "scrollUpdate";
- const char kEnumTap[] = "tap";
- const char kEnumTapDown[] = "tapDown";
- } // Type
- } // dispatchGestureEvent
-
- namespace dispatchMouseEvent {
- const char kName[] = "Input.dispatchMouseEvent";
- const char kParamButton[] = "button";
- const char kParamClickCount[] = "clickCount";
- const char kParamDeviceSpace[] = "deviceSpace";
- const char kParamModifiers[] = "modifiers";
- const char kParamTimestamp[] = "timestamp";
- const char kParamType[] = "type";
- const char kParamX[] = "x";
- const char kParamY[] = "y";
-
- namespace Button {
- const char kEnumLeft[] = "left";
- const char kEnumMiddle[] = "middle";
- const char kEnumNone[] = "none";
- const char kEnumRight[] = "right";
- } // Button
-
- namespace Type {
- const char kEnumMouseMoved[] = "mouseMoved";
- const char kEnumMousePressed[] = "mousePressed";
- const char kEnumMouseReleased[] = "mouseReleased";
- } // Type
- } // dispatchMouseEvent
-} // Input
-
-namespace Inspector {
- const char kName[] = "Inspector";
-
- namespace detached {
- const char kName[] = "Inspector.detached";
- const char kParamReason[] = "reason";
- } // detached
-
- namespace targetCrashed {
- const char kName[] = "Inspector.targetCrashed";
- } // targetCrashed
-} // Inspector
-
-namespace Page {
- const char kName[] = "Page";
-
- namespace NavigationEntry {
- const char kParamId[] = "id";
- const char kParamTitle[] = "title";
- const char kParamUrl[] = "url";
- } // NavigationEntry
-
- namespace Quota {
- const char kParamPersistent[] = "persistent";
- const char kParamTemporary[] = "temporary";
- } // Quota
-
- namespace ScreencastFrameMetadata {
- const char kParamDeviceScaleFactor[] = "deviceScaleFactor";
- const char kParamOffsetBottom[] = "offsetBottom";
- const char kParamOffsetTop[] = "offsetTop";
- const char kParamPageScaleFactor[] = "pageScaleFactor";
- const char kParamPageScaleFactorMax[] = "pageScaleFactorMax";
- const char kParamPageScaleFactorMin[] = "pageScaleFactorMin";
- const char kParamViewport[] = "viewport";
- } // ScreencastFrameMetadata
-
- namespace Usage {
- const char kParamPersistent[] = "persistent";
- const char kParamSyncable[] = "syncable";
- const char kParamTemporary[] = "temporary";
- } // Usage
-
- namespace UsageItem {
- const char kParamId[] = "id";
- const char kParamValue[] = "value";
-
- namespace Id {
- const char kEnumAppcache[] = "appcache";
- const char kEnumDatabase[] = "database";
- const char kEnumFilesystem[] = "filesystem";
- const char kEnumIndexeddatabase[] = "indexeddatabase";
- } // Id
- } // UsageItem
-
- namespace canScreencast {
- const char kName[] = "Page.canScreencast";
- const char kResponseResult[] = "result";
- } // canScreencast
-
- namespace captureScreenshot {
- const char kName[] = "Page.captureScreenshot";
- const char kParamFormat[] = "format";
- const char kParamMaxHeight[] = "maxHeight";
- const char kParamMaxWidth[] = "maxWidth";
- const char kParamQuality[] = "quality";
- const char kResponseData[] = "data";
- const char kResponseMetadata[] = "metadata";
-
- namespace Format {
- const char kEnumJpeg[] = "jpeg";
- const char kEnumPng[] = "png";
- } // Format
- } // captureScreenshot
-
- namespace disable {
- const char kName[] = "Page.disable";
- } // disable
-
- namespace getNavigationHistory {
- const char kName[] = "Page.getNavigationHistory";
- const char kResponseCurrentIndex[] = "currentIndex";
- const char kResponseEntries[] = "entries";
- } // getNavigationHistory
-
- namespace handleJavaScriptDialog {
- const char kName[] = "Page.handleJavaScriptDialog";
- const char kParamAccept[] = "accept";
- const char kParamPromptText[] = "promptText";
- } // handleJavaScriptDialog
-
- namespace navigate {
- const char kName[] = "Page.navigate";
- const char kParamUrl[] = "url";
- } // navigate
-
- namespace navigateToHistoryEntry {
- const char kName[] = "Page.navigateToHistoryEntry";
- const char kParamEntryId[] = "entryId";
- } // navigateToHistoryEntry
-
- namespace queryUsageAndQuota {
- const char kName[] = "Page.queryUsageAndQuota";
- const char kParamSecurityOrigin[] = "securityOrigin";
- const char kResponseQuota[] = "quota";
- const char kResponseUsage[] = "usage";
- } // queryUsageAndQuota
-
- namespace reload {
- const char kName[] = "Page.reload";
- const char kParamIgnoreCache[] = "ignoreCache";
- const char kParamScriptPreprocessor[] = "scriptPreprocessor";
- const char kParamScriptToEvaluateOnLoad[] = "scriptToEvaluateOnLoad";
- } // reload
-
- namespace screencastFrame {
- const char kName[] = "Page.screencastFrame";
- const char kParamData[] = "data";
- const char kParamMetadata[] = "metadata";
- } // screencastFrame
-
- namespace screencastVisibilityChanged {
- const char kName[] = "Page.screencastVisibilityChanged";
- const char kParamVisible[] = "visible";
- } // screencastVisibilityChanged
-
- namespace startScreencast {
- const char kName[] = "Page.startScreencast";
- const char kParamFormat[] = "format";
- const char kParamMaxHeight[] = "maxHeight";
- const char kParamMaxWidth[] = "maxWidth";
- const char kParamQuality[] = "quality";
-
- namespace Format {
- const char kEnumJpeg[] = "jpeg";
- const char kEnumPng[] = "png";
- } // Format
- } // startScreencast
-
- namespace stopScreencast {
- const char kName[] = "Page.stopScreencast";
- } // stopScreencast
-} // Page
-
-namespace SystemInfo {
- const char kName[] = "SystemInfo";
-
- namespace GPUDevice {
- const char kParamDeviceId[] = "deviceId";
- const char kParamDeviceString[] = "deviceString";
- const char kParamVendorId[] = "vendorId";
- const char kParamVendorString[] = "vendorString";
- } // GPUDevice
-
- namespace GPUInfo {
- const char kParamAuxAttributes[] = "auxAttributes";
- const char kParamDevices[] = "devices";
- const char kParamFeatureStatus[] = "featureStatus";
- } // GPUInfo
-
- namespace SystemInfo {
- const char kParamGpu[] = "gpu";
- const char kParamModelName[] = "modelName";
- } // SystemInfo
-
- namespace getInfo {
- const char kName[] = "SystemInfo.getInfo";
- const char kResponseInfo[] = "info";
- } // getInfo
-} // SystemInfo
-
-namespace Tracing {
- const char kName[] = "Tracing";
-
- namespace dataCollected {
- const char kName[] = "Tracing.dataCollected";
- const char kParamValue[] = "value";
- } // dataCollected
-
- namespace end {
- const char kName[] = "Tracing.end";
- } // end
-
- namespace start {
- const char kName[] = "Tracing.start";
- const char kParamCategories[] = "categories";
- const char kParamOptions[] = "options";
- } // start
-
- namespace tracingComplete {
- const char kName[] = "Tracing.tracingComplete";
- } // tracingComplete
-} // Tracing
-
-namespace Worker {
- const char kName[] = "Worker";
-
- namespace disconnectFromWorker {
- const char kName[] = "Worker.disconnectFromWorker";
- const char kParamWorkerId[] = "workerId";
- } // disconnectFromWorker
-
- namespace disconnectedFromWorker {
- const char kName[] = "Worker.disconnectedFromWorker";
- } // disconnectedFromWorker
-} // Worker
-
-
-} // devtools
-} // content
diff --git a/chromium/content/browser/devtools/devtools_protocol_constants.h b/chromium/content/browser/devtools/devtools_protocol_constants.h
deleted file mode 100644
index 8ed159bc0df..00000000000
--- a/chromium/content/browser/devtools/devtools_protocol_constants.h
+++ /dev/null
@@ -1,296 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_PROTOCOL_CONSTANTSH_
-#define CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_PROTOCOL_CONSTANTSH_
-
-// THIS FILE IS AUTOGENERATED.
-// If you need change something in this file, please see
-// protocol.json and browser_protocol.json
-
-namespace content {
-namespace devtools {
-
-extern const char kResult[];
-
-namespace DOM {
- extern const char kName[];
-
- namespace Rect {
- extern const char kParamHeight[];
- extern const char kParamWidth[];
- extern const char kParamX[];
- extern const char kParamY[];
- } // Rect
-
- namespace setFileInputFiles {
- extern const char kName[];
- extern const char kParamFiles[];
- extern const char kParamNodeId[];
- } // setFileInputFiles
-} // DOM
-
-namespace Input {
- extern const char kName[];
-
- namespace dispatchGestureEvent {
- extern const char kName[];
- extern const char kParamDeltaX[];
- extern const char kParamDeltaY[];
- extern const char kParamPinchScale[];
- extern const char kParamTimestamp[];
- extern const char kParamType[];
- extern const char kParamX[];
- extern const char kParamY[];
-
- namespace Type {
- extern const char kEnumPinchBegin[];
- extern const char kEnumPinchEnd[];
- extern const char kEnumPinchUpdate[];
- extern const char kEnumScrollBegin[];
- extern const char kEnumScrollEnd[];
- extern const char kEnumScrollUpdate[];
- extern const char kEnumTap[];
- extern const char kEnumTapDown[];
- } // Type
- } // dispatchGestureEvent
-
- namespace dispatchMouseEvent {
- extern const char kName[];
- extern const char kParamButton[];
- extern const char kParamClickCount[];
- extern const char kParamDeviceSpace[];
- extern const char kParamModifiers[];
- extern const char kParamTimestamp[];
- extern const char kParamType[];
- extern const char kParamX[];
- extern const char kParamY[];
-
- namespace Button {
- extern const char kEnumLeft[];
- extern const char kEnumMiddle[];
- extern const char kEnumNone[];
- extern const char kEnumRight[];
- } // Button
-
- namespace Type {
- extern const char kEnumMouseMoved[];
- extern const char kEnumMousePressed[];
- extern const char kEnumMouseReleased[];
- } // Type
- } // dispatchMouseEvent
-} // Input
-
-namespace Inspector {
- extern const char kName[];
-
- namespace detached {
- extern const char kName[];
- extern const char kParamReason[];
- } // detached
-
- namespace targetCrashed {
- extern const char kName[];
- } // targetCrashed
-} // Inspector
-
-namespace Page {
- extern const char kName[];
-
- namespace NavigationEntry {
- extern const char kParamId[];
- extern const char kParamTitle[];
- extern const char kParamUrl[];
- } // NavigationEntry
-
- namespace Quota {
- extern const char kParamPersistent[];
- extern const char kParamTemporary[];
- } // Quota
-
- namespace ScreencastFrameMetadata {
- extern const char kParamDeviceScaleFactor[];
- extern const char kParamOffsetBottom[];
- extern const char kParamOffsetTop[];
- extern const char kParamPageScaleFactor[];
- extern const char kParamPageScaleFactorMax[];
- extern const char kParamPageScaleFactorMin[];
- extern const char kParamViewport[];
- } // ScreencastFrameMetadata
-
- namespace Usage {
- extern const char kParamPersistent[];
- extern const char kParamSyncable[];
- extern const char kParamTemporary[];
- } // Usage
-
- namespace UsageItem {
- extern const char kParamId[];
- extern const char kParamValue[];
-
- namespace Id {
- extern const char kEnumAppcache[];
- extern const char kEnumDatabase[];
- extern const char kEnumFilesystem[];
- extern const char kEnumIndexeddatabase[];
- } // Id
- } // UsageItem
-
- namespace canScreencast {
- extern const char kName[];
- extern const char kResponseResult[];
- } // canScreencast
-
- namespace captureScreenshot {
- extern const char kName[];
- extern const char kParamFormat[];
- extern const char kParamMaxHeight[];
- extern const char kParamMaxWidth[];
- extern const char kParamQuality[];
- extern const char kResponseData[];
- extern const char kResponseMetadata[];
-
- namespace Format {
- extern const char kEnumJpeg[];
- extern const char kEnumPng[];
- } // Format
- } // captureScreenshot
-
- namespace disable {
- extern const char kName[];
- } // disable
-
- namespace getNavigationHistory {
- extern const char kName[];
- extern const char kResponseCurrentIndex[];
- extern const char kResponseEntries[];
- } // getNavigationHistory
-
- namespace handleJavaScriptDialog {
- extern const char kName[];
- extern const char kParamAccept[];
- extern const char kParamPromptText[];
- } // handleJavaScriptDialog
-
- namespace navigate {
- extern const char kName[];
- extern const char kParamUrl[];
- } // navigate
-
- namespace navigateToHistoryEntry {
- extern const char kName[];
- extern const char kParamEntryId[];
- } // navigateToHistoryEntry
-
- namespace queryUsageAndQuota {
- extern const char kName[];
- extern const char kParamSecurityOrigin[];
- extern const char kResponseQuota[];
- extern const char kResponseUsage[];
- } // queryUsageAndQuota
-
- namespace reload {
- extern const char kName[];
- extern const char kParamIgnoreCache[];
- extern const char kParamScriptPreprocessor[];
- extern const char kParamScriptToEvaluateOnLoad[];
- } // reload
-
- namespace screencastFrame {
- extern const char kName[];
- extern const char kParamData[];
- extern const char kParamMetadata[];
- } // screencastFrame
-
- namespace screencastVisibilityChanged {
- extern const char kName[];
- extern const char kParamVisible[];
- } // screencastVisibilityChanged
-
- namespace startScreencast {
- extern const char kName[];
- extern const char kParamFormat[];
- extern const char kParamMaxHeight[];
- extern const char kParamMaxWidth[];
- extern const char kParamQuality[];
-
- namespace Format {
- extern const char kEnumJpeg[];
- extern const char kEnumPng[];
- } // Format
- } // startScreencast
-
- namespace stopScreencast {
- extern const char kName[];
- } // stopScreencast
-} // Page
-
-namespace SystemInfo {
- extern const char kName[];
-
- namespace GPUDevice {
- extern const char kParamDeviceId[];
- extern const char kParamDeviceString[];
- extern const char kParamVendorId[];
- extern const char kParamVendorString[];
- } // GPUDevice
-
- namespace GPUInfo {
- extern const char kParamAuxAttributes[];
- extern const char kParamDevices[];
- extern const char kParamFeatureStatus[];
- } // GPUInfo
-
- namespace SystemInfo {
- extern const char kParamGpu[];
- extern const char kParamModelName[];
- } // SystemInfo
-
- namespace getInfo {
- extern const char kName[];
- extern const char kResponseInfo[];
- } // getInfo
-} // SystemInfo
-
-namespace Tracing {
- extern const char kName[];
-
- namespace dataCollected {
- extern const char kName[];
- extern const char kParamValue[];
- } // dataCollected
-
- namespace end {
- extern const char kName[];
- } // end
-
- namespace start {
- extern const char kName[];
- extern const char kParamCategories[];
- extern const char kParamOptions[];
- } // start
-
- namespace tracingComplete {
- extern const char kName[];
- } // tracingComplete
-} // Tracing
-
-namespace Worker {
- extern const char kName[];
-
- namespace disconnectFromWorker {
- extern const char kName[];
- extern const char kParamWorkerId[];
- } // disconnectFromWorker
-
- namespace disconnectedFromWorker {
- extern const char kName[];
- } // disconnectedFromWorker
-} // Worker
-
-
-} // devtools
-} // content
-
-#endif // CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_PROTOCOL_CONSTANTSH_
diff --git a/chromium/content/browser/devtools/devtools_resources.gyp b/chromium/content/browser/devtools/devtools_resources.gyp
index 30b42209453..72ae7060549 100644
--- a/chromium/content/browser/devtools/devtools_resources.gyp
+++ b/chromium/content/browser/devtools/devtools_resources.gyp
@@ -22,6 +22,7 @@
'variables': {
'grit_cmd': ['python', '../../../tools/grit/grit.py'],
'grit_grd_file': '<(SHARED_INTERMEDIATE_DIR)/devtools/devtools_resources.grd',
+ 'grit_rc_header_format%': '',
},
'inputs': [
'<(grit_grd_file)',
@@ -35,14 +36,47 @@
],
'action': ['<@(grit_cmd)',
'-i', '<(grit_grd_file)', 'build',
- '-f', 'GRIT_DIR/../gritsettings/resource_ids',
+ '-f', '<(DEPTH)/tools/gritsettings/resource_ids',
'-o', '<(grit_out_dir)',
'-D', 'SHARED_INTERMEDIATE_DIR=<(SHARED_INTERMEDIATE_DIR)',
- '<@(grit_defines)' ],
+ '<@(grit_defines)',
+ '<@(grit_rc_header_format)'],
'message': 'Generating resources from <(grit_grd_file)',
- 'msvs_cygwin_shell': 1,
+ },
+ {
+ 'action_name': 'devtools_protocol_constants',
+ 'variables': {
+ 'blink_protocol': '../../../third_party/WebKit/Source/devtools/protocol.json',
+ 'browser_protocol': 'browser_protocol.json',
+ 'generator': '../../public/browser/devtools_protocol_constants_generator.py',
+ 'package': 'content'
+ },
+ 'inputs': [
+ '<(blink_protocol)',
+ '<(browser_protocol)',
+ '<(generator)',
+ ],
+ 'outputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/<(package)/browser/devtools/devtools_protocol_constants.cc',
+ '<(SHARED_INTERMEDIATE_DIR)/<(package)/browser/devtools/devtools_protocol_constants.h'
+ ],
+ 'action':[
+ 'python',
+ '<(generator)',
+ '<(package)',
+ '<(SHARED_INTERMEDIATE_DIR)/<(package)/browser/devtools/devtools_protocol_constants.cc',
+ '<(SHARED_INTERMEDIATE_DIR)/<(package)/browser/devtools/devtools_protocol_constants.h',
+ '<(blink_protocol)',
+ '<(browser_protocol)',
+ ],
+ 'message': 'Generating DevTools protocol constants from <(blink_protocol)'
}
],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '<(SHARED_INTERMEDIATE_DIR)',
+ ]
+ },
'includes': [ '../../../build/grit_target.gypi' ],
},
],
diff --git a/chromium/content/browser/devtools/devtools_system_info_handler.cc b/chromium/content/browser/devtools/devtools_system_info_handler.cc
index 4ade158c2bc..6e0336d2c08 100644
--- a/chromium/content/browser/devtools/devtools_system_info_handler.cc
+++ b/chromium/content/browser/devtools/devtools_system_info_handler.cc
@@ -20,9 +20,11 @@ const char kAuxAttributes[] = "auxAttributes";
const char kDeviceId[] = "deviceId";
const char kDeviceString[] = "deviceString";
const char kDevices[] = "devices";
+const char kDriverBugWorkarounds[] = "driverBugWorkarounds";
const char kFeatureStatus[] = "featureStatus";
const char kGPU[] = "gpu";
const char kModelName[] = "modelName";
+const char kModelVersion[] = "modelVersion";
const char kVendorId[] = "vendorId";
const char kVendorString[] = "vendorString";
@@ -118,8 +120,11 @@ DevToolsSystemInfoHandler::OnGetInfo(
gpu_dict->Set(kFeatureStatus, GetFeatureStatus());
+ gpu_dict->Set(kDriverBugWorkarounds, GetDriverBugWorkarounds());
+
base::DictionaryValue* system_dict = new base::DictionaryValue;
- system_dict->SetString(kModelName, gpu_info.machine_model);
+ system_dict->SetString(kModelName, gpu_info.machine_model_name);
+ system_dict->SetString(kModelVersion, gpu_info.machine_model_version);
system_dict->Set(kGPU, gpu_dict);
return command->SuccessResponse(system_dict);
}
diff --git a/chromium/content/browser/devtools/devtools_tracing_handler.cc b/chromium/content/browser/devtools/devtools_tracing_handler.cc
index e9f6cf10b29..7d308642212 100644
--- a/chromium/content/browser/devtools/devtools_tracing_handler.cc
+++ b/chromium/content/browser/devtools/devtools_tracing_handler.cc
@@ -4,6 +4,8 @@
#include "content/browser/devtools/devtools_tracing_handler.h"
+#include <cmath>
+
#include "base/bind.h"
#include "base/callback.h"
#include "base/file_util.h"
@@ -13,6 +15,8 @@
#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/values.h"
#include "content/browser/devtools/devtools_http_handler_impl.h"
#include "content/browser/devtools/devtools_protocol_constants.h"
@@ -42,14 +46,18 @@ void ReadFile(
} // namespace
-DevToolsTracingHandler::DevToolsTracingHandler()
- : weak_factory_(this) {
+DevToolsTracingHandler::DevToolsTracingHandler(
+ DevToolsTracingHandler::Target target)
+ : weak_factory_(this), target_(target) {
RegisterCommandHandler(devtools::Tracing::start::kName,
base::Bind(&DevToolsTracingHandler::OnStart,
base::Unretained(this)));
RegisterCommandHandler(devtools::Tracing::end::kName,
base::Bind(&DevToolsTracingHandler::OnEnd,
base::Unretained(this)));
+ RegisterCommandHandler(devtools::Tracing::getCategories::kName,
+ base::Bind(&DevToolsTracingHandler::OnGetCategories,
+ base::Unretained(this)));
}
DevToolsTracingHandler::~DevToolsTracingHandler() {
@@ -69,10 +77,10 @@ void DevToolsTracingHandler::ReadRecordingResult(
if (trace_data->data().size()) {
scoped_ptr<base::Value> trace_value(base::JSONReader::Read(
trace_data->data()));
- DictionaryValue* dictionary = NULL;
+ base::DictionaryValue* dictionary = NULL;
bool ok = trace_value->GetAsDictionary(&dictionary);
DCHECK(ok);
- ListValue* list = NULL;
+ base::ListValue* list = NULL;
ok = dictionary->GetList("traceEvents", &list);
DCHECK(ok);
std::string buffer;
@@ -142,20 +150,99 @@ DevToolsTracingHandler::OnStart(
options = TraceOptionsFromString(options_param);
}
+ if (params && params->HasKey(
+ devtools::Tracing::start::kParamBufferUsageReportingInterval)) {
+ double usage_reporting_interval = 0.0;
+ params->GetDouble(
+ devtools::Tracing::start::kParamBufferUsageReportingInterval,
+ &usage_reporting_interval);
+ if (usage_reporting_interval > 0) {
+ base::TimeDelta interval = base::TimeDelta::FromMilliseconds(
+ std::ceil(usage_reporting_interval));
+ buffer_usage_poll_timer_.reset(new base::Timer(
+ FROM_HERE,
+ interval,
+ base::Bind(
+ base::IgnoreResult(&TracingController::GetTraceBufferPercentFull),
+ base::Unretained(TracingController::GetInstance()),
+ base::Bind(&DevToolsTracingHandler::OnBufferUsage,
+ weak_factory_.GetWeakPtr())),
+ true));
+ buffer_usage_poll_timer_->Reset();
+ }
+ }
+
+ // If inspected target is a render process Tracing.start will be handled by
+ // tracing agent in the renderer.
+ if (target_ == Renderer) {
+ TracingController::GetInstance()->EnableRecording(
+ categories, options, TracingController::EnableRecordingDoneCallback());
+ return NULL;
+ }
+
TracingController::GetInstance()->EnableRecording(
categories, options,
- TracingController::EnableRecordingDoneCallback());
- return command->SuccessResponse(NULL);
+ base::Bind(&DevToolsTracingHandler::OnTracingStarted,
+ weak_factory_.GetWeakPtr(),
+ command));
+
+ return command->AsyncResponsePromise();
+}
+
+void DevToolsTracingHandler::OnTracingStarted(
+ scoped_refptr<DevToolsProtocol::Command> command) {
+ SendAsyncResponse(command->SuccessResponse(NULL));
+}
+
+void DevToolsTracingHandler::OnBufferUsage(float usage) {
+ base::DictionaryValue* params = new base::DictionaryValue();
+ params->SetDouble(devtools::Tracing::bufferUsage::kParamValue, usage);
+ SendNotification(devtools::Tracing::bufferUsage::kName, params);
}
scoped_refptr<DevToolsProtocol::Response>
DevToolsTracingHandler::OnEnd(
scoped_refptr<DevToolsProtocol::Command> command) {
- TracingController::GetInstance()->DisableRecording(
- base::FilePath(),
+ DisableRecording(
base::Bind(&DevToolsTracingHandler::BeginReadingRecordingResult,
weak_factory_.GetWeakPtr()));
return command->SuccessResponse(NULL);
}
+void DevToolsTracingHandler::DisableRecording(
+ const TracingController::TracingFileResultCallback& callback) {
+ buffer_usage_poll_timer_.reset();
+ TracingController::GetInstance()->DisableRecording(base::FilePath(),
+ callback);
+}
+
+void DevToolsTracingHandler::OnClientDetached() {
+ DisableRecording();
+}
+
+scoped_refptr<DevToolsProtocol::Response>
+DevToolsTracingHandler::OnGetCategories(
+ scoped_refptr<DevToolsProtocol::Command> command) {
+ TracingController::GetInstance()->GetCategories(
+ base::Bind(&DevToolsTracingHandler::OnCategoriesReceived,
+ weak_factory_.GetWeakPtr(),
+ command));
+ return command->AsyncResponsePromise();
+}
+
+void DevToolsTracingHandler::OnCategoriesReceived(
+ scoped_refptr<DevToolsProtocol::Command> command,
+ const std::set<std::string>& category_set) {
+ base::DictionaryValue* response = new base::DictionaryValue;
+ base::ListValue* category_list = new base::ListValue;
+ for (std::set<std::string>::const_iterator it = category_set.begin();
+ it != category_set.end(); ++it) {
+ category_list->AppendString(*it);
+ }
+
+ response->Set(devtools::Tracing::getCategories::kResponseCategories,
+ category_list);
+ SendAsyncResponse(command->SuccessResponse(response));
+}
+
} // namespace content
diff --git a/chromium/content/browser/devtools/devtools_tracing_handler.h b/chromium/content/browser/devtools/devtools_tracing_handler.h
index b551e7579e2..7c3b8e645e4 100644
--- a/chromium/content/browser/devtools/devtools_tracing_handler.h
+++ b/chromium/content/browser/devtools/devtools_tracing_handler.h
@@ -5,12 +5,16 @@
#ifndef CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_TRACING_HANDLER_H_
#define CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_TRACING_HANDLER_H_
+#include <set>
+#include <string>
+
#include "base/memory/weak_ptr.h"
#include "content/browser/devtools/devtools_protocol.h"
#include "content/public/browser/tracing_controller.h"
namespace base {
class RefCountedString;
+class Timer;
}
namespace content {
@@ -19,23 +23,38 @@ namespace content {
// infrastructure.
class DevToolsTracingHandler : public DevToolsProtocol::Handler {
public:
- DevToolsTracingHandler();
+ enum Target { Browser, Renderer };
+ explicit DevToolsTracingHandler(Target target);
virtual ~DevToolsTracingHandler();
+ void OnClientDetached();
+
private:
void BeginReadingRecordingResult(const base::FilePath& path);
void ReadRecordingResult(const scoped_refptr<base::RefCountedString>& result);
void OnTraceDataCollected(const std::string& trace_fragment);
+ void OnTracingStarted(scoped_refptr<DevToolsProtocol::Command> command);
+ void OnBufferUsage(float usage);
scoped_refptr<DevToolsProtocol::Response> OnStart(
scoped_refptr<DevToolsProtocol::Command> command);
scoped_refptr<DevToolsProtocol::Response> OnEnd(
scoped_refptr<DevToolsProtocol::Command> command);
+ scoped_refptr<DevToolsProtocol::Response> OnGetCategories(
+ scoped_refptr<DevToolsProtocol::Command> command);
+ void OnCategoriesReceived(scoped_refptr<DevToolsProtocol::Command> command,
+ const std::set<std::string>& category_set);
+
TracingController::Options TraceOptionsFromString(const std::string& options);
- base::WeakPtrFactory<DevToolsTracingHandler> weak_factory_;
+ void DisableRecording(
+ const TracingController::TracingFileResultCallback& callback =
+ TracingController::TracingFileResultCallback());
+ base::WeakPtrFactory<DevToolsTracingHandler> weak_factory_;
+ scoped_ptr<base::Timer> buffer_usage_poll_timer_;
+ Target target_;
DISALLOW_COPY_AND_ASSIGN(DevToolsTracingHandler);
};
diff --git a/chromium/content/browser/devtools/embedded_worker_devtools_manager.cc b/chromium/content/browser/devtools/embedded_worker_devtools_manager.cc
new file mode 100644
index 00000000000..3ba61aafeea
--- /dev/null
+++ b/chromium/content/browser/devtools/embedded_worker_devtools_manager.cc
@@ -0,0 +1,379 @@
+// 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/devtools/embedded_worker_devtools_manager.h"
+
+#include "content/browser/devtools/devtools_manager_impl.h"
+#include "content/browser/devtools/devtools_protocol.h"
+#include "content/browser/devtools/devtools_protocol_constants.h"
+#include "content/browser/devtools/ipc_devtools_agent_host.h"
+#include "content/browser/shared_worker/shared_worker_instance.h"
+#include "content/common/devtools_messages.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/worker_service.h"
+#include "ipc/ipc_listener.h"
+
+namespace content {
+
+namespace {
+
+bool SendMessageToWorker(
+ const EmbeddedWorkerDevToolsManager::WorkerId& worker_id,
+ IPC::Message* message) {
+ RenderProcessHost* host = RenderProcessHost::FromID(worker_id.first);
+ if (!host) {
+ delete message;
+ return false;
+ }
+ message->set_routing_id(worker_id.second);
+ host->Send(message);
+ return true;
+}
+
+} // namespace
+
+EmbeddedWorkerDevToolsManager::ServiceWorkerIdentifier::ServiceWorkerIdentifier(
+ const ServiceWorkerContextCore* const service_worker_context,
+ int64 service_worker_version_id)
+ : service_worker_context_(service_worker_context),
+ service_worker_version_id_(service_worker_version_id) {
+}
+
+EmbeddedWorkerDevToolsManager::ServiceWorkerIdentifier::ServiceWorkerIdentifier(
+ const ServiceWorkerIdentifier& other)
+ : service_worker_context_(other.service_worker_context_),
+ service_worker_version_id_(other.service_worker_version_id_) {
+}
+
+bool EmbeddedWorkerDevToolsManager::ServiceWorkerIdentifier::Matches(
+ const ServiceWorkerIdentifier& other) const {
+ return service_worker_context_ == other.service_worker_context_ &&
+ service_worker_version_id_ == other.service_worker_version_id_;
+}
+
+EmbeddedWorkerDevToolsManager::WorkerInfo::WorkerInfo(
+ const SharedWorkerInstance& instance)
+ : shared_worker_instance_(new SharedWorkerInstance(instance)),
+ state_(WORKER_UNINSPECTED),
+ agent_host_(NULL) {
+}
+
+EmbeddedWorkerDevToolsManager::WorkerInfo::WorkerInfo(
+ const ServiceWorkerIdentifier& service_worker_id)
+ : service_worker_id_(new ServiceWorkerIdentifier(service_worker_id)),
+ state_(WORKER_UNINSPECTED),
+ agent_host_(NULL) {
+}
+
+bool EmbeddedWorkerDevToolsManager::WorkerInfo::Matches(
+ const SharedWorkerInstance& other) {
+ if (!shared_worker_instance_)
+ return false;
+ return shared_worker_instance_->Matches(other);
+}
+
+bool EmbeddedWorkerDevToolsManager::WorkerInfo::Matches(
+ const ServiceWorkerIdentifier& other) {
+ if (!service_worker_id_)
+ return false;
+ return service_worker_id_->Matches(other);
+}
+
+EmbeddedWorkerDevToolsManager::WorkerInfo::~WorkerInfo() {
+}
+
+class EmbeddedWorkerDevToolsManager::EmbeddedWorkerDevToolsAgentHost
+ : public IPCDevToolsAgentHost,
+ public IPC::Listener {
+ public:
+ explicit EmbeddedWorkerDevToolsAgentHost(WorkerId worker_id)
+ : worker_id_(worker_id), worker_attached_(false) {
+ AttachToWorker();
+ }
+
+ // DevToolsAgentHost override.
+ virtual bool IsWorker() const OVERRIDE { return true; }
+
+ // IPCDevToolsAgentHost implementation.
+ virtual void SendMessageToAgent(IPC::Message* message) OVERRIDE {
+ if (worker_attached_)
+ SendMessageToWorker(worker_id_, message);
+ else
+ delete message;
+ }
+ virtual void Attach() OVERRIDE {
+ AttachToWorker();
+ IPCDevToolsAgentHost::Attach();
+ }
+ virtual void OnClientAttached() OVERRIDE {}
+ virtual void OnClientDetached() OVERRIDE { DetachFromWorker(); }
+
+ // IPC::Listener implementation.
+ virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(EmbeddedWorkerDevToolsAgentHost, msg)
+ IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend,
+ OnDispatchOnInspectorFrontend)
+ IPC_MESSAGE_HANDLER(DevToolsHostMsg_SaveAgentRuntimeState,
+ OnSaveAgentRuntimeState)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+ }
+
+ void ReattachToWorker(WorkerId worker_id) {
+ CHECK(!worker_attached_);
+ worker_id_ = worker_id;
+ if (!IsAttached())
+ return;
+ AttachToWorker();
+ Reattach(state_);
+ }
+
+ void DetachFromWorker() {
+ if (!worker_attached_)
+ return;
+ worker_attached_ = false;
+ if (RenderProcessHost* host = RenderProcessHost::FromID(worker_id_.first))
+ host->RemoveRoute(worker_id_.second);
+ Release();
+ }
+
+ WorkerId worker_id() const { return worker_id_; }
+
+ private:
+ virtual ~EmbeddedWorkerDevToolsAgentHost() {
+ CHECK(!worker_attached_);
+ EmbeddedWorkerDevToolsManager::GetInstance()->RemoveInspectedWorkerData(
+ this);
+ }
+
+ void OnDispatchOnInspectorFrontend(const std::string& message) {
+ DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(this,
+ message);
+ }
+
+ void OnSaveAgentRuntimeState(const std::string& state) { state_ = state; }
+
+ void AttachToWorker() {
+ if (worker_attached_)
+ return;
+ worker_attached_ = true;
+ AddRef();
+ if (RenderProcessHost* host = RenderProcessHost::FromID(worker_id_.first))
+ host->AddRoute(worker_id_.second, this);
+ }
+
+ WorkerId worker_id_;
+ bool worker_attached_;
+ std::string state_;
+ DISALLOW_COPY_AND_ASSIGN(EmbeddedWorkerDevToolsAgentHost);
+};
+
+// static
+EmbeddedWorkerDevToolsManager* EmbeddedWorkerDevToolsManager::GetInstance() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ return Singleton<EmbeddedWorkerDevToolsManager>::get();
+}
+
+DevToolsAgentHost* EmbeddedWorkerDevToolsManager::GetDevToolsAgentHostForWorker(
+ int worker_process_id,
+ int worker_route_id) {
+ WorkerId id(worker_process_id, worker_route_id);
+
+ WorkerInfoMap::iterator it = workers_.find(id);
+ if (it == workers_.end())
+ return NULL;
+
+ WorkerInfo* info = it->second;
+ if (info->state() != WORKER_UNINSPECTED &&
+ info->state() != WORKER_PAUSED_FOR_DEBUG_ON_START) {
+ return info->agent_host();
+ }
+
+ EmbeddedWorkerDevToolsAgentHost* agent_host =
+ new EmbeddedWorkerDevToolsAgentHost(id);
+ info->set_agent_host(agent_host);
+ info->set_state(WORKER_INSPECTED);
+ return agent_host;
+}
+
+DevToolsAgentHost*
+EmbeddedWorkerDevToolsManager::GetDevToolsAgentHostForServiceWorker(
+ const ServiceWorkerIdentifier& service_worker_id) {
+ WorkerInfoMap::iterator it = FindExistingServiceWorkerInfo(service_worker_id);
+ if (it == workers_.end())
+ return NULL;
+ return GetDevToolsAgentHostForWorker(it->first.first, it->first.second);
+}
+
+EmbeddedWorkerDevToolsManager::EmbeddedWorkerDevToolsManager()
+ : debug_service_worker_on_start_(false) {
+}
+
+EmbeddedWorkerDevToolsManager::~EmbeddedWorkerDevToolsManager() {
+}
+
+bool EmbeddedWorkerDevToolsManager::SharedWorkerCreated(
+ int worker_process_id,
+ int worker_route_id,
+ const SharedWorkerInstance& instance) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ const WorkerId id(worker_process_id, worker_route_id);
+ WorkerInfoMap::iterator it = FindExistingSharedWorkerInfo(instance);
+ if (it == workers_.end()) {
+ scoped_ptr<WorkerInfo> info(new WorkerInfo(instance));
+ workers_.set(id, info.Pass());
+ return false;
+ }
+ MoveToPausedState(id, it);
+ return true;
+}
+
+bool EmbeddedWorkerDevToolsManager::ServiceWorkerCreated(
+ int worker_process_id,
+ int worker_route_id,
+ const ServiceWorkerIdentifier& service_worker_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ const WorkerId id(worker_process_id, worker_route_id);
+ WorkerInfoMap::iterator it = FindExistingServiceWorkerInfo(service_worker_id);
+ if (it == workers_.end()) {
+ scoped_ptr<WorkerInfo> info(new WorkerInfo(service_worker_id));
+ if (debug_service_worker_on_start_)
+ info->set_state(WORKER_PAUSED_FOR_DEBUG_ON_START);
+ workers_.set(id, info.Pass());
+ return debug_service_worker_on_start_;
+ }
+ MoveToPausedState(id, it);
+ return true;
+}
+
+void EmbeddedWorkerDevToolsManager::WorkerDestroyed(int worker_process_id,
+ int worker_route_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ const WorkerId id(worker_process_id, worker_route_id);
+ WorkerInfoMap::iterator it = workers_.find(id);
+ DCHECK(it != workers_.end());
+ WorkerInfo* info = it->second;
+ switch (info->state()) {
+ case WORKER_UNINSPECTED:
+ case WORKER_PAUSED_FOR_DEBUG_ON_START:
+ workers_.erase(it);
+ break;
+ case WORKER_INSPECTED: {
+ EmbeddedWorkerDevToolsAgentHost* agent_host = info->agent_host();
+ info->set_state(WORKER_TERMINATED);
+ if (!agent_host->IsAttached()) {
+ agent_host->DetachFromWorker();
+ return;
+ }
+ // Client host is debugging this worker agent host.
+ std::string notification =
+ DevToolsProtocol::CreateNotification(
+ devtools::Worker::disconnectedFromWorker::kName, NULL)
+ ->Serialize();
+ DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(
+ agent_host, notification);
+ agent_host->DetachFromWorker();
+ break;
+ }
+ case WORKER_TERMINATED:
+ NOTREACHED();
+ break;
+ case WORKER_PAUSED_FOR_REATTACH: {
+ scoped_ptr<WorkerInfo> worker_info = workers_.take_and_erase(it);
+ worker_info->set_state(WORKER_TERMINATED);
+ const WorkerId old_id = worker_info->agent_host()->worker_id();
+ workers_.set(old_id, worker_info.Pass());
+ break;
+ }
+ }
+}
+
+void EmbeddedWorkerDevToolsManager::WorkerContextStarted(int worker_process_id,
+ int worker_route_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ const WorkerId id(worker_process_id, worker_route_id);
+ WorkerInfoMap::iterator it = workers_.find(id);
+ DCHECK(it != workers_.end());
+ WorkerInfo* info = it->second;
+ if (info->state() == WORKER_PAUSED_FOR_DEBUG_ON_START) {
+ RenderProcessHost* rph = RenderProcessHost::FromID(worker_process_id);
+ scoped_refptr<DevToolsAgentHost> agent_host(
+ GetDevToolsAgentHostForWorker(worker_process_id, worker_route_id));
+ DevToolsManagerImpl::GetInstance()->Inspect(rph->GetBrowserContext(),
+ agent_host.get());
+ } else if (info->state() == WORKER_PAUSED_FOR_REATTACH) {
+ info->agent_host()->ReattachToWorker(id);
+ info->set_state(WORKER_INSPECTED);
+ }
+}
+
+void EmbeddedWorkerDevToolsManager::RemoveInspectedWorkerData(
+ EmbeddedWorkerDevToolsAgentHost* agent_host) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ const WorkerId id(agent_host->worker_id());
+ scoped_ptr<WorkerInfo> worker_info = workers_.take_and_erase(id);
+ if (worker_info) {
+ DCHECK_EQ(worker_info->agent_host(), agent_host);
+ if (worker_info->state() == WORKER_TERMINATED)
+ return;
+ DCHECK_EQ(worker_info->state(), WORKER_INSPECTED);
+ worker_info->set_agent_host(NULL);
+ worker_info->set_state(WORKER_UNINSPECTED);
+ workers_.set(id, worker_info.Pass());
+ return;
+ }
+ for (WorkerInfoMap::iterator it = workers_.begin(); it != workers_.end();
+ ++it) {
+ if (it->second->agent_host() == agent_host) {
+ DCHECK_EQ(WORKER_PAUSED_FOR_REATTACH, it->second->state());
+ SendMessageToWorker(
+ it->first,
+ new DevToolsAgentMsg_ResumeWorkerContext(it->first.second));
+ it->second->set_agent_host(NULL);
+ it->second->set_state(WORKER_UNINSPECTED);
+ return;
+ }
+ }
+}
+
+EmbeddedWorkerDevToolsManager::WorkerInfoMap::iterator
+EmbeddedWorkerDevToolsManager::FindExistingSharedWorkerInfo(
+ const SharedWorkerInstance& instance) {
+ WorkerInfoMap::iterator it = workers_.begin();
+ for (; it != workers_.end(); ++it) {
+ if (it->second->Matches(instance))
+ break;
+ }
+ return it;
+}
+
+EmbeddedWorkerDevToolsManager::WorkerInfoMap::iterator
+EmbeddedWorkerDevToolsManager::FindExistingServiceWorkerInfo(
+ const ServiceWorkerIdentifier& service_worker_id) {
+ WorkerInfoMap::iterator it = workers_.begin();
+ for (; it != workers_.end(); ++it) {
+ if (it->second->Matches(service_worker_id))
+ break;
+ }
+ return it;
+}
+
+void EmbeddedWorkerDevToolsManager::MoveToPausedState(
+ const WorkerId& id,
+ const WorkerInfoMap::iterator& it) {
+ DCHECK_EQ(WORKER_TERMINATED, it->second->state());
+ scoped_ptr<WorkerInfo> info = workers_.take_and_erase(it);
+ info->set_state(WORKER_PAUSED_FOR_REATTACH);
+ workers_.set(id, info.Pass());
+}
+
+void EmbeddedWorkerDevToolsManager::ResetForTesting() {
+ workers_.clear();
+}
+
+} // namespace content
diff --git a/chromium/content/browser/devtools/embedded_worker_devtools_manager.h b/chromium/content/browser/devtools/embedded_worker_devtools_manager.h
new file mode 100644
index 00000000000..3b15c8f2099
--- /dev/null
+++ b/chromium/content/browser/devtools/embedded_worker_devtools_manager.h
@@ -0,0 +1,138 @@
+// 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_DEVTOOLS_EMBEDDED_WORKER_DEVTOOLS_MANAGER_H_
+#define CONTENT_BROWSER_DEVTOOLS_EMBEDDED_WORKER_DEVTOOLS_MANAGER_H_
+
+#include "base/basictypes.h"
+#include "base/containers/scoped_ptr_hash_map.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_vector.h"
+#include "base/memory/singleton.h"
+#include "base/strings/string16.h"
+#include "content/browser/shared_worker/shared_worker_instance.h"
+#include "content/common/content_export.h"
+
+namespace content {
+
+class DevToolsAgentHost;
+class ServiceWorkerContextCore;
+
+// EmbeddedWorkerDevToolsManager is used instead of WorkerDevToolsManager when
+// "enable-embedded-shared-worker" flag is set.
+// This class lives on UI thread.
+class CONTENT_EXPORT EmbeddedWorkerDevToolsManager {
+ public:
+ typedef std::pair<int, int> WorkerId;
+ class EmbeddedWorkerDevToolsAgentHost;
+
+ class ServiceWorkerIdentifier {
+ public:
+ ServiceWorkerIdentifier(
+ const ServiceWorkerContextCore* const service_worker_context,
+ int64 service_worker_version_id);
+ explicit ServiceWorkerIdentifier(const ServiceWorkerIdentifier& other);
+ ~ServiceWorkerIdentifier() {}
+
+ bool Matches(const ServiceWorkerIdentifier& other) const;
+
+ private:
+ const ServiceWorkerContextCore* const service_worker_context_;
+ const int64 service_worker_version_id_;
+ };
+
+ // Returns the EmbeddedWorkerDevToolsManager singleton.
+ static EmbeddedWorkerDevToolsManager* GetInstance();
+
+ DevToolsAgentHost* GetDevToolsAgentHostForWorker(int worker_process_id,
+ int worker_route_id);
+ DevToolsAgentHost* GetDevToolsAgentHostForServiceWorker(
+ const ServiceWorkerIdentifier& service_worker_id);
+
+ // Returns true when the worker must be paused on start because a DevTool
+ // window for the same former SharedWorkerInstance is still opened.
+ bool SharedWorkerCreated(int worker_process_id,
+ int worker_route_id,
+ const SharedWorkerInstance& instance);
+ // Returns true when the worker must be paused on start because a DevTool
+ // window for the same former ServiceWorkerIdentifier is still opened or
+ // debug-on-start is enabled in chrome://serviceworker-internals.
+ bool ServiceWorkerCreated(int worker_process_id,
+ int worker_route_id,
+ const ServiceWorkerIdentifier& service_worker_id);
+ void WorkerContextStarted(int worker_process_id, int worker_route_id);
+ void WorkerDestroyed(int worker_process_id, int worker_route_id);
+
+ void set_debug_service_worker_on_start(bool debug_on_start) {
+ debug_service_worker_on_start_ = debug_on_start;
+ }
+ bool debug_service_worker_on_start() const {
+ return debug_service_worker_on_start_;
+ }
+
+ private:
+ friend struct DefaultSingletonTraits<EmbeddedWorkerDevToolsManager>;
+ friend class EmbeddedWorkerDevToolsManagerTest;
+ FRIEND_TEST_ALL_PREFIXES(EmbeddedWorkerDevToolsManagerTest, BasicTest);
+ FRIEND_TEST_ALL_PREFIXES(EmbeddedWorkerDevToolsManagerTest, AttachTest);
+
+ enum WorkerState {
+ WORKER_UNINSPECTED,
+ WORKER_INSPECTED,
+ WORKER_TERMINATED,
+ WORKER_PAUSED_FOR_DEBUG_ON_START,
+ WORKER_PAUSED_FOR_REATTACH,
+ };
+
+ class WorkerInfo {
+ public:
+ // Creates WorkerInfo for SharedWorker.
+ explicit WorkerInfo(const SharedWorkerInstance& instance);
+ // Creates WorkerInfo for ServiceWorker.
+ explicit WorkerInfo(const ServiceWorkerIdentifier& service_worker_id);
+ ~WorkerInfo();
+
+ WorkerState state() { return state_; }
+ void set_state(WorkerState new_state) { state_ = new_state; }
+ EmbeddedWorkerDevToolsAgentHost* agent_host() { return agent_host_; }
+ void set_agent_host(EmbeddedWorkerDevToolsAgentHost* agent_host) {
+ agent_host_ = agent_host;
+ }
+ bool Matches(const SharedWorkerInstance& other);
+ bool Matches(const ServiceWorkerIdentifier& other);
+
+ private:
+ scoped_ptr<SharedWorkerInstance> shared_worker_instance_;
+ scoped_ptr<ServiceWorkerIdentifier> service_worker_id_;
+ WorkerState state_;
+ EmbeddedWorkerDevToolsAgentHost* agent_host_;
+ };
+
+ typedef base::ScopedPtrHashMap<WorkerId, WorkerInfo> WorkerInfoMap;
+
+ EmbeddedWorkerDevToolsManager();
+ virtual ~EmbeddedWorkerDevToolsManager();
+
+ void RemoveInspectedWorkerData(EmbeddedWorkerDevToolsAgentHost* agent_host);
+
+ WorkerInfoMap::iterator FindExistingSharedWorkerInfo(
+ const SharedWorkerInstance& instance);
+ WorkerInfoMap::iterator FindExistingServiceWorkerInfo(
+ const ServiceWorkerIdentifier& service_worker_id);
+
+ void MoveToPausedState(const WorkerId& id, const WorkerInfoMap::iterator& it);
+
+ // Resets to its initial state as if newly created.
+ void ResetForTesting();
+
+ WorkerInfoMap workers_;
+
+ bool debug_service_worker_on_start_;
+
+ DISALLOW_COPY_AND_ASSIGN(EmbeddedWorkerDevToolsManager);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_DEVTOOLS_EMBEDDED_WORKER_DEVTOOLS_MANAGER_H_
diff --git a/chromium/content/browser/devtools/embedded_worker_devtools_manager_unittest.cc b/chromium/content/browser/devtools/embedded_worker_devtools_manager_unittest.cc
new file mode 100644
index 00000000000..69b23052425
--- /dev/null
+++ b/chromium/content/browser/devtools/embedded_worker_devtools_manager_unittest.cc
@@ -0,0 +1,272 @@
+// 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/devtools/embedded_worker_devtools_manager.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "content/browser/browser_thread_impl.h"
+#include "content/browser/devtools/devtools_manager_impl.h"
+#include "content/browser/shared_worker/shared_worker_instance.h"
+#include "content/browser/worker_host/worker_storage_partition.h"
+#include "content/public/browser/devtools_agent_host.h"
+#include "content/public/browser/devtools_client_host.h"
+#include "content/public/test/test_browser_context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+namespace {
+
+class TestDevToolsClientHost : public DevToolsClientHost {
+ public:
+ TestDevToolsClientHost() {}
+ virtual ~TestDevToolsClientHost() {}
+ virtual void DispatchOnInspectorFrontend(
+ const std::string& message) OVERRIDE {}
+ virtual void InspectedContentsClosing() OVERRIDE {}
+ virtual void ReplacedWithAnotherClient() OVERRIDE {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestDevToolsClientHost);
+};
+}
+
+class EmbeddedWorkerDevToolsManagerTest : public testing::Test {
+ public:
+ EmbeddedWorkerDevToolsManagerTest()
+ : ui_thread_(BrowserThread::UI, &message_loop_),
+ browser_context_(new TestBrowserContext()),
+ partition_(
+ new WorkerStoragePartition(browser_context_->GetRequestContext(),
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL)),
+ partition_id_(*partition_.get()) {}
+
+ protected:
+ virtual void SetUp() OVERRIDE {
+ manager_ = EmbeddedWorkerDevToolsManager::GetInstance();
+ }
+ virtual void TearDown() OVERRIDE {
+ EmbeddedWorkerDevToolsManager::GetInstance()->ResetForTesting();
+ }
+
+ void CheckWorkerState(int worker_process_id,
+ int worker_route_id,
+ EmbeddedWorkerDevToolsManager::WorkerState state) {
+ const EmbeddedWorkerDevToolsManager::WorkerId id(worker_process_id,
+ worker_route_id);
+ EmbeddedWorkerDevToolsManager::WorkerInfoMap::iterator it =
+ manager_->workers_.find(id);
+ EXPECT_TRUE(manager_->workers_.end() != it);
+ EXPECT_EQ(state, it->second->state());
+ }
+
+ void CheckWorkerNotExist(int worker_process_id, int worker_route_id) {
+ const EmbeddedWorkerDevToolsManager::WorkerId id(worker_process_id,
+ worker_route_id);
+ EXPECT_TRUE(manager_->workers_.end() == manager_->workers_.find(id));
+ }
+
+ void CheckWorkerCount(size_t size) {
+ EXPECT_EQ(size, manager_->workers_.size());
+ }
+
+ void RegisterDevToolsClientHostFor(DevToolsAgentHost* agent_host,
+ DevToolsClientHost* client_host) {
+ DevToolsManagerImpl::GetInstance()->RegisterDevToolsClientHostFor(
+ agent_host, client_host);
+ }
+
+ void ClientHostClosing(DevToolsClientHost* client_host) {
+ DevToolsManagerImpl::GetInstance()->ClientHostClosing(client_host);
+ }
+
+ base::MessageLoopForIO message_loop_;
+ BrowserThreadImpl ui_thread_;
+ scoped_ptr<TestBrowserContext> browser_context_;
+ scoped_ptr<WorkerStoragePartition> partition_;
+ const WorkerStoragePartitionId partition_id_;
+ EmbeddedWorkerDevToolsManager* manager_;
+};
+
+TEST_F(EmbeddedWorkerDevToolsManagerTest, BasicTest) {
+ scoped_refptr<DevToolsAgentHost> agent_host;
+
+ SharedWorkerInstance instance1(GURL("http://example.com/w.js"),
+ base::string16(),
+ base::string16(),
+ blink::WebContentSecurityPolicyTypeReport,
+ browser_context_->GetResourceContext(),
+ partition_id_);
+
+ agent_host = manager_->GetDevToolsAgentHostForWorker(1, 1);
+ EXPECT_FALSE(agent_host.get());
+
+ // Created -> Started -> Destroyed
+ CheckWorkerNotExist(1, 1);
+ manager_->SharedWorkerCreated(1, 1, instance1);
+ CheckWorkerState(1, 1, EmbeddedWorkerDevToolsManager::WORKER_UNINSPECTED);
+ manager_->WorkerContextStarted(1, 1);
+ CheckWorkerState(1, 1, EmbeddedWorkerDevToolsManager::WORKER_UNINSPECTED);
+ manager_->WorkerDestroyed(1, 1);
+ CheckWorkerNotExist(1, 1);
+
+ // Created -> GetDevToolsAgentHost -> Started -> Destroyed
+ CheckWorkerNotExist(1, 2);
+ manager_->SharedWorkerCreated(1, 2, instance1);
+ CheckWorkerState(1, 2, EmbeddedWorkerDevToolsManager::WORKER_UNINSPECTED);
+ agent_host = manager_->GetDevToolsAgentHostForWorker(1, 2);
+ EXPECT_TRUE(agent_host.get());
+ CheckWorkerState(1, 2, EmbeddedWorkerDevToolsManager::WORKER_INSPECTED);
+ EXPECT_EQ(agent_host.get(), manager_->GetDevToolsAgentHostForWorker(1, 2));
+ manager_->WorkerContextStarted(1, 2);
+ CheckWorkerState(1, 2, EmbeddedWorkerDevToolsManager::WORKER_INSPECTED);
+ manager_->WorkerDestroyed(1, 2);
+ CheckWorkerState(1, 2, EmbeddedWorkerDevToolsManager::WORKER_TERMINATED);
+ agent_host = NULL;
+ CheckWorkerNotExist(1, 2);
+
+ // Created -> Started -> GetDevToolsAgentHost -> Destroyed
+ CheckWorkerNotExist(1, 3);
+ manager_->SharedWorkerCreated(1, 3, instance1);
+ CheckWorkerState(1, 3, EmbeddedWorkerDevToolsManager::WORKER_UNINSPECTED);
+ manager_->WorkerContextStarted(1, 3);
+ CheckWorkerState(1, 3, EmbeddedWorkerDevToolsManager::WORKER_UNINSPECTED);
+ agent_host = manager_->GetDevToolsAgentHostForWorker(1, 3);
+ EXPECT_TRUE(agent_host.get());
+ CheckWorkerState(1, 3, EmbeddedWorkerDevToolsManager::WORKER_INSPECTED);
+ manager_->WorkerDestroyed(1, 3);
+ CheckWorkerState(1, 3, EmbeddedWorkerDevToolsManager::WORKER_TERMINATED);
+ agent_host = NULL;
+ CheckWorkerNotExist(1, 3);
+
+ // Created -> Destroyed
+ CheckWorkerNotExist(1, 4);
+ manager_->SharedWorkerCreated(1, 4, instance1);
+ CheckWorkerState(1, 4, EmbeddedWorkerDevToolsManager::WORKER_UNINSPECTED);
+ manager_->WorkerDestroyed(1, 4);
+ CheckWorkerNotExist(1, 4);
+
+ // Created -> GetDevToolsAgentHost -> Destroyed
+ CheckWorkerNotExist(1, 5);
+ manager_->SharedWorkerCreated(1, 5, instance1);
+ CheckWorkerState(1, 5, EmbeddedWorkerDevToolsManager::WORKER_UNINSPECTED);
+ agent_host = manager_->GetDevToolsAgentHostForWorker(1, 5);
+ EXPECT_TRUE(agent_host.get());
+ CheckWorkerState(1, 5, EmbeddedWorkerDevToolsManager::WORKER_INSPECTED);
+ manager_->WorkerDestroyed(1, 5);
+ CheckWorkerState(1, 5, EmbeddedWorkerDevToolsManager::WORKER_TERMINATED);
+ agent_host = NULL;
+ CheckWorkerNotExist(1, 5);
+
+ // Created -> GetDevToolsAgentHost -> Free agent_host -> Destroyed
+ CheckWorkerNotExist(1, 6);
+ manager_->SharedWorkerCreated(1, 6, instance1);
+ CheckWorkerState(1, 6, EmbeddedWorkerDevToolsManager::WORKER_UNINSPECTED);
+ agent_host = manager_->GetDevToolsAgentHostForWorker(1, 6);
+ EXPECT_TRUE(agent_host.get());
+ CheckWorkerState(1, 6, EmbeddedWorkerDevToolsManager::WORKER_INSPECTED);
+ agent_host = NULL;
+ manager_->WorkerDestroyed(1, 6);
+ CheckWorkerNotExist(1, 6);
+}
+
+TEST_F(EmbeddedWorkerDevToolsManagerTest, AttachTest) {
+ scoped_refptr<DevToolsAgentHost> agent_host1;
+ scoped_refptr<DevToolsAgentHost> agent_host2;
+
+ SharedWorkerInstance instance1(GURL("http://example.com/w1.js"),
+ base::string16(),
+ base::string16(),
+ blink::WebContentSecurityPolicyTypeReport,
+ browser_context_->GetResourceContext(),
+ partition_id_);
+ SharedWorkerInstance instance2(GURL("http://example.com/w2.js"),
+ base::string16(),
+ base::string16(),
+ blink::WebContentSecurityPolicyTypeReport,
+ browser_context_->GetResourceContext(),
+ partition_id_);
+
+ // Created -> GetDevToolsAgentHost -> Register -> Started -> Destroyed
+ scoped_ptr<TestDevToolsClientHost> client_host1(new TestDevToolsClientHost());
+ CheckWorkerNotExist(2, 1);
+ manager_->SharedWorkerCreated(2, 1, instance1);
+ CheckWorkerState(2, 1, EmbeddedWorkerDevToolsManager::WORKER_UNINSPECTED);
+ agent_host1 = manager_->GetDevToolsAgentHostForWorker(2, 1);
+ EXPECT_TRUE(agent_host1.get());
+ CheckWorkerState(2, 1, EmbeddedWorkerDevToolsManager::WORKER_INSPECTED);
+ EXPECT_EQ(agent_host1.get(), manager_->GetDevToolsAgentHostForWorker(2, 1));
+ RegisterDevToolsClientHostFor(agent_host1.get(), client_host1.get());
+ CheckWorkerState(2, 1, EmbeddedWorkerDevToolsManager::WORKER_INSPECTED);
+ manager_->WorkerContextStarted(2, 1);
+ CheckWorkerState(2, 1, EmbeddedWorkerDevToolsManager::WORKER_INSPECTED);
+ manager_->WorkerDestroyed(2, 1);
+ CheckWorkerState(2, 1, EmbeddedWorkerDevToolsManager::WORKER_TERMINATED);
+ EXPECT_EQ(agent_host1.get(), manager_->GetDevToolsAgentHostForWorker(2, 1));
+
+ // Created -> Started -> GetDevToolsAgentHost -> Register -> Destroyed
+ scoped_ptr<TestDevToolsClientHost> client_host2(new TestDevToolsClientHost());
+ manager_->SharedWorkerCreated(2, 2, instance2);
+ CheckWorkerState(2, 2, EmbeddedWorkerDevToolsManager::WORKER_UNINSPECTED);
+ manager_->WorkerContextStarted(2, 2);
+ CheckWorkerState(2, 2, EmbeddedWorkerDevToolsManager::WORKER_UNINSPECTED);
+ agent_host2 = manager_->GetDevToolsAgentHostForWorker(2, 2);
+ EXPECT_TRUE(agent_host2.get());
+ EXPECT_NE(agent_host1.get(), agent_host2.get());
+ EXPECT_EQ(agent_host2.get(), manager_->GetDevToolsAgentHostForWorker(2, 2));
+ CheckWorkerState(2, 2, EmbeddedWorkerDevToolsManager::WORKER_INSPECTED);
+ RegisterDevToolsClientHostFor(agent_host2.get(), client_host2.get());
+ CheckWorkerState(2, 2, EmbeddedWorkerDevToolsManager::WORKER_INSPECTED);
+ manager_->WorkerDestroyed(2, 2);
+ CheckWorkerState(2, 2, EmbeddedWorkerDevToolsManager::WORKER_TERMINATED);
+ EXPECT_EQ(agent_host2.get(), manager_->GetDevToolsAgentHostForWorker(2, 2));
+
+ // Re-created -> Started -> ClientHostClosing -> Destroyed
+ CheckWorkerState(2, 1, EmbeddedWorkerDevToolsManager::WORKER_TERMINATED);
+ manager_->SharedWorkerCreated(2, 3, instance1);
+ CheckWorkerNotExist(2, 1);
+ CheckWorkerState(
+ 2, 3, EmbeddedWorkerDevToolsManager::WORKER_PAUSED_FOR_REATTACH);
+ EXPECT_EQ(agent_host1.get(), manager_->GetDevToolsAgentHostForWorker(2, 3));
+ manager_->WorkerContextStarted(2, 3);
+ CheckWorkerState(2, 3, EmbeddedWorkerDevToolsManager::WORKER_INSPECTED);
+ ClientHostClosing(client_host1.get());
+ manager_->WorkerDestroyed(2, 3);
+ CheckWorkerState(2, 3, EmbeddedWorkerDevToolsManager::WORKER_TERMINATED);
+ agent_host1 = NULL;
+ CheckWorkerNotExist(2, 3);
+
+ // Re-created -> Destroyed
+ CheckWorkerState(2, 2, EmbeddedWorkerDevToolsManager::WORKER_TERMINATED);
+ manager_->SharedWorkerCreated(2, 4, instance2);
+ CheckWorkerNotExist(2, 2);
+ CheckWorkerState(
+ 2, 4, EmbeddedWorkerDevToolsManager::WORKER_PAUSED_FOR_REATTACH);
+ EXPECT_EQ(agent_host2.get(), manager_->GetDevToolsAgentHostForWorker(2, 4));
+ manager_->WorkerDestroyed(2, 4);
+ CheckWorkerNotExist(2, 4);
+ CheckWorkerState(2, 2, EmbeddedWorkerDevToolsManager::WORKER_TERMINATED);
+
+ // Re-created -> ClientHostClosing -> Destroyed
+ manager_->SharedWorkerCreated(2, 5, instance2);
+ CheckWorkerNotExist(2, 2);
+ CheckWorkerState(
+ 2, 5, EmbeddedWorkerDevToolsManager::WORKER_PAUSED_FOR_REATTACH);
+ EXPECT_EQ(agent_host2.get(), manager_->GetDevToolsAgentHostForWorker(2, 5));
+ ClientHostClosing(client_host2.get());
+ CheckWorkerCount(1);
+ agent_host2 = NULL;
+ CheckWorkerCount(1);
+ manager_->WorkerDestroyed(2, 5);
+ CheckWorkerCount(0);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/devtools/forwarding_agent_host.cc b/chromium/content/browser/devtools/forwarding_agent_host.cc
new file mode 100644
index 00000000000..f1524e1b7e1
--- /dev/null
+++ b/chromium/content/browser/devtools/forwarding_agent_host.cc
@@ -0,0 +1,41 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/devtools/forwarding_agent_host.h"
+
+#include "content/browser/devtools/devtools_manager_impl.h"
+
+namespace content {
+
+ForwardingAgentHost::ForwardingAgentHost(
+ DevToolsExternalAgentProxyDelegate* delegate)
+ : delegate_(delegate) {
+}
+
+ForwardingAgentHost::~ForwardingAgentHost() {
+}
+
+void ForwardingAgentHost::DispatchOnClientHost(const std::string& message) {
+ DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(
+ this, message);
+}
+
+void ForwardingAgentHost::ConnectionClosed() {
+ NotifyCloseListener();
+}
+
+void ForwardingAgentHost::Attach() {
+ delegate_->Attach(this);
+}
+
+void ForwardingAgentHost::Detach() {
+ delegate_->Detach();
+}
+
+void ForwardingAgentHost::DispatchOnInspectorBackend(
+ const std::string& message) {
+ delegate_->SendMessageToBackend(message);
+}
+
+} // content
diff --git a/chromium/content/browser/devtools/forwarding_agent_host.h b/chromium/content/browser/devtools/forwarding_agent_host.h
new file mode 100644
index 00000000000..81e4aed5419
--- /dev/null
+++ b/chromium/content/browser/devtools/forwarding_agent_host.h
@@ -0,0 +1,39 @@
+// 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_DEVTOOLS_FORWARDING_AGENT_HOST_H
+#define CONTENT_BROWSER_DEVTOOLS_FORWARDING_AGENT_HOST_H
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "content/browser/devtools/devtools_agent_host_impl.h"
+#include "content/public/browser/devtools_external_agent_proxy.h"
+#include "content/public/browser/devtools_external_agent_proxy_delegate.h"
+
+namespace content {
+
+class ForwardingAgentHost
+ : public DevToolsAgentHostImpl,
+ public DevToolsExternalAgentProxy {
+ public:
+ ForwardingAgentHost(DevToolsExternalAgentProxyDelegate* delegate);
+
+ private:
+ virtual ~ForwardingAgentHost();
+
+ // DevToolsExternalAgentProxy implementation.
+ virtual void DispatchOnClientHost(const std::string& message) OVERRIDE;
+ virtual void ConnectionClosed() OVERRIDE;
+
+ // DevToolsAgentHostImpl implementation.
+ virtual void Attach() OVERRIDE;
+ virtual void Detach() OVERRIDE;
+ virtual void DispatchOnInspectorBackend(const std::string& message) OVERRIDE;
+
+ scoped_ptr<DevToolsExternalAgentProxyDelegate> delegate_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_DEVTOOLS_FORWARDING_AGENT_HOST_H
diff --git a/chromium/content/browser/devtools/ipc_devtools_agent_host.cc b/chromium/content/browser/devtools/ipc_devtools_agent_host.cc
index c7cd3c386f8..294a679c7f6 100644
--- a/chromium/content/browser/devtools/ipc_devtools_agent_host.cc
+++ b/chromium/content/browser/devtools/ipc_devtools_agent_host.cc
@@ -9,7 +9,7 @@
namespace content {
void IPCDevToolsAgentHost::Attach() {
- SendMessageToAgent(new DevToolsAgentMsg_Attach(MSG_ROUTING_NONE));
+ SendMessageToAgent(new DevToolsAgentMsg_Attach(MSG_ROUTING_NONE, GetId()));
OnClientAttached();
}
@@ -26,7 +26,7 @@ void IPCDevToolsAgentHost::DispatchOnInspectorBackend(
void IPCDevToolsAgentHost::InspectElement(int x, int y) {
SendMessageToAgent(new DevToolsAgentMsg_InspectElement(MSG_ROUTING_NONE,
- x, y));
+ GetId(), x, y));
}
IPCDevToolsAgentHost::~IPCDevToolsAgentHost() {
@@ -34,8 +34,7 @@ IPCDevToolsAgentHost::~IPCDevToolsAgentHost() {
void IPCDevToolsAgentHost::Reattach(const std::string& saved_agent_state) {
SendMessageToAgent(new DevToolsAgentMsg_Reattach(
- MSG_ROUTING_NONE,
- saved_agent_state));
+ MSG_ROUTING_NONE, GetId(), saved_agent_state));
OnClientAttached();
}
diff --git a/chromium/content/browser/devtools/render_view_devtools_agent_host.cc b/chromium/content/browser/devtools/render_view_devtools_agent_host.cc
index 39ced5eb581..82df7ed2f0d 100644
--- a/chromium/content/browser/devtools/render_view_devtools_agent_host.cc
+++ b/chromium/content/browser/devtools/render_view_devtools_agent_host.cc
@@ -8,6 +8,7 @@
#include "base/lazy_instance.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/devtools/devtools_manager_impl.h"
+#include "content/browser/devtools/devtools_power_handler.h"
#include "content/browser/devtools/devtools_protocol.h"
#include "content/browser/devtools/devtools_protocol_constants.h"
#include "content/browser/devtools/devtools_tracing_handler.h"
@@ -19,6 +20,7 @@
#include "content/common/devtools_messages.h"
#include "content/common/view_messages.h"
#include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/devtools_manager_delegate.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_widget_host_iterator.h"
@@ -35,6 +37,22 @@ typedef std::vector<RenderViewDevToolsAgentHost*> Instances;
namespace {
base::LazyInstance<Instances>::Leaky g_instances = LAZY_INSTANCE_INITIALIZER;
+//Returns RenderViewDevToolsAgentHost attached to any of RenderViewHost
+//instances associated with |web_contents|
+static RenderViewDevToolsAgentHost* FindAgentHost(WebContents* web_contents) {
+ if (g_instances == NULL)
+ return NULL;
+ RenderViewHostDelegate* delegate =
+ static_cast<WebContentsImpl*>(web_contents);
+ for (Instances::iterator it = g_instances.Get().begin();
+ it != g_instances.Get().end(); ++it) {
+ RenderViewHost* rvh = (*it)->render_view_host();
+ if (rvh && rvh->GetDelegate() == delegate)
+ return *it;
+ }
+ return NULL;
+}
+
static RenderViewDevToolsAgentHost* FindAgentHost(RenderViewHost* rvh) {
if (g_instances == NULL)
return NULL;
@@ -48,6 +66,14 @@ static RenderViewDevToolsAgentHost* FindAgentHost(RenderViewHost* rvh) {
} // namespace
+scoped_refptr<DevToolsAgentHost>
+DevToolsAgentHost::GetOrCreateFor(WebContents* web_contents) {
+ RenderViewDevToolsAgentHost* result = FindAgentHost(web_contents);
+ if (!result)
+ result = new RenderViewDevToolsAgentHost(web_contents->GetRenderViewHost());
+ return result;
+}
+
// static
scoped_refptr<DevToolsAgentHost>
DevToolsAgentHost::GetOrCreateFor(RenderViewHost* rvh) {
@@ -96,10 +122,20 @@ std::vector<RenderViewHost*> DevToolsAgentHost::GetValidRenderViewHosts() {
RenderViewHost* rvh = RenderViewHost::From(widget);
WebContents* web_contents = WebContents::FromRenderViewHost(rvh);
+ if (!web_contents)
+ continue;
+
// Don't report a RenderViewHost if it is not the current RenderViewHost
- // for some WebContents.
- if (!web_contents || rvh != web_contents->GetRenderViewHost())
+ // for some WebContents (this filters out pre-render RVHs and similar).
+ // However report a RenderViewHost created for an out of process iframe.
+ // TODO (kaznacheev): Revisit this when it is clear how OOP iframes
+ // interact with pre-rendering.
+ // TODO (kaznacheev): GetMainFrame() call is a temporary hack. Iterate over
+ // all RenderFrameHost instances when multiple OOP frames are supported.
+ if (rvh != web_contents->GetRenderViewHost() &&
+ !rvh->GetMainFrame()->IsCrossProcessSubframe()) {
continue;
+ }
result.push_back(rvh);
}
@@ -117,18 +153,28 @@ void RenderViewDevToolsAgentHost::OnCancelPendingNavigation(
agent_host->ConnectRenderViewHost(current);
}
-RenderViewDevToolsAgentHost::RenderViewDevToolsAgentHost(
- RenderViewHost* rvh)
+// static
+bool RenderViewDevToolsAgentHost::DispatchIPCMessage(
+ RenderViewHost* source,
+ const IPC::Message& message) {
+ RenderViewDevToolsAgentHost* agent_host = FindAgentHost(source);
+ return agent_host && agent_host->DispatchIPCMessage(message);
+}
+
+RenderViewDevToolsAgentHost::RenderViewDevToolsAgentHost(RenderViewHost* rvh)
: render_view_host_(NULL),
overrides_handler_(new RendererOverridesHandler(this)),
- tracing_handler_(new DevToolsTracingHandler())
- {
+ tracing_handler_(
+ new DevToolsTracingHandler(DevToolsTracingHandler::Renderer)),
+ power_handler_(new DevToolsPowerHandler()),
+ reattaching_(false) {
SetRenderViewHost(rvh);
DevToolsProtocol::Notifier notifier(base::Bind(
&RenderViewDevToolsAgentHost::OnDispatchOnInspectorFrontend,
base::Unretained(this)));
overrides_handler_->SetNotifier(notifier);
tracing_handler_->SetNotifier(notifier);
+ power_handler_->SetNotifier(notifier);
g_instances.Get().push_back(this);
AddRef(); // Balanced in RenderViewHostDestroyed.
}
@@ -140,14 +186,30 @@ RenderViewHost* RenderViewDevToolsAgentHost::GetRenderViewHost() {
void RenderViewDevToolsAgentHost::DispatchOnInspectorBackend(
const std::string& message) {
std::string error_message;
+
+ scoped_ptr<base::DictionaryValue> message_dict(
+ DevToolsProtocol::ParseMessage(message, &error_message));
scoped_refptr<DevToolsProtocol::Command> command =
- DevToolsProtocol::ParseCommand(message, &error_message);
+ DevToolsProtocol::ParseCommand(message_dict.get(), &error_message);
if (command) {
- scoped_refptr<DevToolsProtocol::Response> overridden_response =
- overrides_handler_->HandleCommand(command);
+ scoped_refptr<DevToolsProtocol::Response> overridden_response;
+
+ DevToolsManagerDelegate* delegate =
+ DevToolsManagerImpl::GetInstance()->delegate();
+ if (delegate) {
+ scoped_ptr<base::DictionaryValue> overridden_response_value(
+ delegate->HandleCommand(this, message_dict.get()));
+ if (overridden_response_value)
+ overridden_response = DevToolsProtocol::ParseResponse(
+ overridden_response_value.get());
+ }
+ if (!overridden_response)
+ overridden_response = overrides_handler_->HandleCommand(command);
if (!overridden_response)
overridden_response = tracing_handler_->HandleCommand(command);
+ if (!overridden_response)
+ overridden_response = power_handler_->HandleCommand(command);
if (overridden_response) {
if (!overridden_response->is_async_promise())
OnDispatchOnInspectorFrontend(overridden_response->Serialize());
@@ -169,12 +231,17 @@ void RenderViewDevToolsAgentHost::OnClientAttached() {
if (!render_view_host_)
return;
- ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadRawCookies(
- render_view_host_->GetProcess()->GetID());
+ InnerOnClientAttached();
// TODO(kaznacheev): Move this call back to DevToolsManagerImpl when
// extensions::ProcessManager no longer relies on this notification.
- DevToolsManagerImpl::GetInstance()->NotifyObservers(this, true);
+ if (!reattaching_)
+ DevToolsManagerImpl::GetInstance()->NotifyObservers(this, true);
+}
+
+void RenderViewDevToolsAgentHost::InnerOnClientAttached() {
+ ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadRawCookies(
+ render_view_host_->GetProcess()->GetID());
#if defined(OS_ANDROID)
power_save_blocker_.reset(
@@ -194,6 +261,7 @@ void RenderViewDevToolsAgentHost::OnClientDetached() {
power_save_blocker_.reset();
#endif
overrides_handler_->OnClientDetached();
+ tracing_handler_->OnClientDetached();
ClientDetachedFromRenderer();
}
@@ -201,6 +269,15 @@ void RenderViewDevToolsAgentHost::ClientDetachedFromRenderer() {
if (!render_view_host_)
return;
+ InnerClientDetachedFromRenderer();
+
+ // TODO(kaznacheev): Move this call back to DevToolsManagerImpl when
+ // extensions::ProcessManager no longer relies on this notification.
+ if (!reattaching_)
+ DevToolsManagerImpl::GetInstance()->NotifyObservers(this, false);
+}
+
+void RenderViewDevToolsAgentHost::InnerClientDetachedFromRenderer() {
bool process_has_agents = false;
RenderProcessHost* render_process_host = render_view_host_->GetProcess();
for (Instances::iterator it = g_instances.Get().begin();
@@ -217,10 +294,6 @@ void RenderViewDevToolsAgentHost::ClientDetachedFromRenderer() {
ChildProcessSecurityPolicyImpl::GetInstance()->RevokeReadRawCookies(
render_process_host->GetID());
}
-
- // TODO(kaznacheev): Move this call back to DevToolsManagerImpl when
- // extensions::ProcessManager no longer relies on this notification.
- DevToolsManagerImpl::GetInstance()->NotifyObservers(this, false);
}
RenderViewDevToolsAgentHost::~RenderViewDevToolsAgentHost() {
@@ -240,8 +313,7 @@ void RenderViewDevToolsAgentHost::AboutToNavigateRenderView(
render_view_host_)->render_view_termination_status() ==
base::TERMINATION_STATUS_STILL_RUNNING)
return;
- DisconnectRenderViewHost();
- ConnectRenderViewHost(dest_rvh);
+ ReattachToRenderViewHost(dest_rvh);
}
void RenderViewDevToolsAgentHost::RenderViewHostChanged(
@@ -250,11 +322,19 @@ void RenderViewDevToolsAgentHost::RenderViewHostChanged(
if (new_host != render_view_host_) {
// AboutToNavigateRenderView was not called for renderer-initiated
// navigation.
- DisconnectRenderViewHost();
- ConnectRenderViewHost(new_host);
+ ReattachToRenderViewHost(new_host);
}
}
+void
+RenderViewDevToolsAgentHost::ReattachToRenderViewHost(RenderViewHost* rvh) {
+ DCHECK(!reattaching_);
+ reattaching_ = true;
+ DisconnectRenderViewHost();
+ ConnectRenderViewHost(rvh);
+ reattaching_ = false;
+}
+
void RenderViewDevToolsAgentHost::RenderViewDeleted(RenderViewHost* rvh) {
if (rvh != render_view_host_)
return;
@@ -272,6 +352,9 @@ void RenderViewDevToolsAgentHost::RenderProcessGone(
case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
case base::TERMINATION_STATUS_PROCESS_CRASHED:
+#if defined(OS_ANDROID)
+ case base::TERMINATION_STATUS_OOM_PROTECTED:
+#endif
RenderViewCrashed();
break;
default:
@@ -341,7 +424,7 @@ void RenderViewDevToolsAgentHost::RenderViewCrashed() {
DispatchOnInspectorFrontend(this, notification->Serialize());
}
-bool RenderViewDevToolsAgentHost::OnMessageReceived(
+bool RenderViewDevToolsAgentHost::DispatchIPCMessage(
const IPC::Message& msg) {
if (!render_view_host_)
return false;
@@ -352,9 +435,6 @@ bool RenderViewDevToolsAgentHost::OnMessageReceived(
OnDispatchOnInspectorFrontend)
IPC_MESSAGE_HANDLER(DevToolsHostMsg_SaveAgentRuntimeState,
OnSaveAgentRuntimeState)
- IPC_MESSAGE_HANDLER(DevToolsHostMsg_ClearBrowserCache, OnClearBrowserCache)
- IPC_MESSAGE_HANDLER(DevToolsHostMsg_ClearBrowserCookies,
- OnClearBrowserCookies)
IPC_MESSAGE_HANDLER_GENERIC(ViewHostMsg_SwapCompositorFrame,
handled = false; OnSwapCompositorFrame(msg))
IPC_MESSAGE_UNHANDLED(handled = false)
@@ -392,14 +472,4 @@ void RenderViewDevToolsAgentHost::OnDispatchOnInspectorFrontend(
this, message);
}
-void RenderViewDevToolsAgentHost::OnClearBrowserCache() {
- if (render_view_host_)
- GetContentClient()->browser()->ClearCache(render_view_host_);
-}
-
-void RenderViewDevToolsAgentHost::OnClearBrowserCookies() {
- if (render_view_host_)
- GetContentClient()->browser()->ClearCookies(render_view_host_);
-}
-
} // namespace content
diff --git a/chromium/content/browser/devtools/render_view_devtools_agent_host.h b/chromium/content/browser/devtools/render_view_devtools_agent_host.h
index 88ba70cc81a..f4e9baf4a5f 100644
--- a/chromium/content/browser/devtools/render_view_devtools_agent_host.h
+++ b/chromium/content/browser/devtools/render_view_devtools_agent_host.h
@@ -22,6 +22,7 @@ class CompositorFrameMetadata;
namespace content {
+class DevToolsPowerHandler;
class DevToolsTracingHandler;
class RendererOverridesHandler;
class RenderViewHost;
@@ -38,6 +39,9 @@ class CONTENT_EXPORT RenderViewDevToolsAgentHost
static void OnCancelPendingNavigation(RenderViewHost* pending,
RenderViewHost* current);
+ static bool DispatchIPCMessage(RenderViewHost* source,
+ const IPC::Message& message);
+
RenderViewDevToolsAgentHost(RenderViewHost*);
RenderViewHost* render_view_host() { return render_view_host_; }
@@ -68,13 +72,16 @@ class CONTENT_EXPORT RenderViewDevToolsAgentHost
virtual void RenderViewDeleted(RenderViewHost* rvh) OVERRIDE;
virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE;
virtual void DidAttachInterstitialPage() OVERRIDE;
- virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
// NotificationObserver overrides:
virtual void Observe(int type,
const NotificationSource& source,
const NotificationDetails& details) OVERRIDE;
+ void ReattachToRenderViewHost(RenderViewHost* rvh);
+
+ bool DispatchIPCMessage(const IPC::Message& message);
+
void SetRenderViewHost(RenderViewHost* rvh);
void ClearRenderViewHost();
@@ -83,19 +90,22 @@ class CONTENT_EXPORT RenderViewDevToolsAgentHost
void OnDispatchOnInspectorFrontend(const std::string& message);
void OnSaveAgentRuntimeState(const std::string& state);
- void OnClearBrowserCache();
- void OnClearBrowserCookies();
void ClientDetachedFromRenderer();
+ void InnerOnClientAttached();
+ void InnerClientDetachedFromRenderer();
+
RenderViewHost* render_view_host_;
scoped_ptr<RendererOverridesHandler> overrides_handler_;
scoped_ptr<DevToolsTracingHandler> tracing_handler_;
+ scoped_ptr<DevToolsPowerHandler> power_handler_;
#if defined(OS_ANDROID)
scoped_ptr<PowerSaveBlockerImpl> power_save_blocker_;
#endif
std::string state_;
NotificationRegistrar registrar_;
+ bool reattaching_;
DISALLOW_COPY_AND_ASSIGN(RenderViewDevToolsAgentHost);
};
diff --git a/chromium/content/browser/devtools/renderer_overrides_handler.cc b/chromium/content/browser/devtools/renderer_overrides_handler.cc
index 4901f34cc12..7370275cd45 100644
--- a/chromium/content/browser/devtools/renderer_overrides_handler.cc
+++ b/chromium/content/browser/devtools/renderer_overrides_handler.cc
@@ -13,6 +13,7 @@
#include "base/bind_helpers.h"
#include "base/files/file_path.h"
#include "base/strings/string16.h"
+#include "base/thread_task_runner_handle.h"
#include "base/values.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/devtools/devtools_protocol_constants.h"
@@ -20,9 +21,10 @@
#include "content/browser/renderer_host/dip_util.h"
#include "content/browser/renderer_host/render_view_host_delegate.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
+#include "content/browser/renderer_host/render_widget_host_view_base.h"
#include "content/common/view_messages.h"
-#include "content/port/browser/render_widget_host_view_port.h"
#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/javascript_dialog_manager.h"
#include "content/public/browser/navigation_controller.h"
@@ -41,6 +43,8 @@
#include "third_party/WebKit/public/web/WebInputEvent.h"
#include "ui/gfx/codec/jpeg_codec.h"
#include "ui/gfx/codec/png_codec.h"
+#include "ui/gfx/display.h"
+#include "ui/gfx/screen.h"
#include "ui/gfx/size_conversions.h"
#include "ui/snapshot/snapshot.h"
#include "url/gurl.h"
@@ -91,6 +95,16 @@ RendererOverridesHandler::RendererOverridesHandler(DevToolsAgentHost* agent)
&RendererOverridesHandler::GrantPermissionsForSetFileInputFiles,
base::Unretained(this)));
RegisterCommandHandler(
+ devtools::Network::clearBrowserCache::kName,
+ base::Bind(
+ &RendererOverridesHandler::ClearBrowserCache,
+ base::Unretained(this)));
+ RegisterCommandHandler(
+ devtools::Network::clearBrowserCookies::kName,
+ base::Bind(
+ &RendererOverridesHandler::ClearBrowserCookies,
+ base::Unretained(this)));
+ RegisterCommandHandler(
devtools::Page::disable::kName,
base::Bind(
&RendererOverridesHandler::PageDisable, base::Unretained(this)));
@@ -192,19 +206,22 @@ void RendererOverridesHandler::InnerSwapCompositorFrame() {
double scale = 1;
ParseCaptureParameters(screencast_command_.get(), &format, &quality, &scale);
- RenderWidgetHostViewPort* view_port =
- RenderWidgetHostViewPort::FromRWHV(host->GetView());
+ const gfx::Display& display =
+ gfx::Screen::GetNativeScreen()->GetPrimaryDisplay();
+ float device_scale_factor = display.device_scale_factor();
gfx::Rect view_bounds = host->GetView()->GetViewBounds();
- gfx::Size snapshot_size = gfx::ToFlooredSize(
- gfx::ScaleSize(view_bounds.size(), scale));
+ gfx::Size snapshot_size(gfx::ToCeiledSize(
+ gfx::ScaleSize(view_bounds.size(), scale / device_scale_factor)));
- view_port->CopyFromCompositingSurface(
+ RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
+ host->GetView());
+ view->CopyFromCompositingSurface(
view_bounds, snapshot_size,
- base::Bind(&RendererOverridesHandler::ScreenshotCaptured,
+ base::Bind(&RendererOverridesHandler::ScreencastFrameCaptured,
weak_factory_.GetWeakPtr(),
- scoped_refptr<DevToolsProtocol::Command>(), format, quality,
- last_compositor_frame_metadata_));
+ format, quality, last_compositor_frame_metadata_),
+ SkBitmap::kARGB_8888_Config);
}
void RendererOverridesHandler::ParseCaptureParameters(
@@ -218,13 +235,13 @@ void RendererOverridesHandler::ParseCaptureParameters(
double max_height = -1;
base::DictionaryValue* params = command->params();
if (params) {
- params->GetString(devtools::Page::captureScreenshot::kParamFormat,
+ params->GetString(devtools::Page::startScreencast::kParamFormat,
format);
- params->GetInteger(devtools::Page::captureScreenshot::kParamQuality,
+ params->GetInteger(devtools::Page::startScreencast::kParamQuality,
quality);
- params->GetDouble(devtools::Page::captureScreenshot::kParamMaxWidth,
+ params->GetDouble(devtools::Page::startScreencast::kParamMaxWidth,
&max_width);
- params->GetDouble(devtools::Page::captureScreenshot::kParamMaxHeight,
+ params->GetDouble(devtools::Page::startScreencast::kParamMaxHeight,
&max_height);
}
@@ -246,6 +263,20 @@ void RendererOverridesHandler::ParseCaptureParameters(
*scale = 5;
}
+base::DictionaryValue* RendererOverridesHandler::CreateScreenshotResponse(
+ const std::vector<unsigned char>& png_data) {
+ std::string base_64_data;
+ base::Base64Encode(
+ base::StringPiece(reinterpret_cast<const char*>(&png_data[0]),
+ png_data.size()),
+ &base_64_data);
+
+ base::DictionaryValue* response = new base::DictionaryValue();
+ response->SetString(
+ devtools::Page::captureScreenshot::kResponseData, base_64_data);
+ return response;
+}
+
// DOM agent handlers --------------------------------------------------------
scoped_refptr<DevToolsProtocol::Response>
@@ -272,6 +303,23 @@ RendererOverridesHandler::GrantPermissionsForSetFileInputFiles(
}
+// Network agent handlers ----------------------------------------------------
+
+scoped_refptr<DevToolsProtocol::Response>
+RendererOverridesHandler::ClearBrowserCache(
+ scoped_refptr<DevToolsProtocol::Command> command) {
+ GetContentClient()->browser()->ClearCache(agent_->GetRenderViewHost());
+ return command->SuccessResponse(NULL);
+}
+
+scoped_refptr<DevToolsProtocol::Response>
+RendererOverridesHandler::ClearBrowserCookies(
+ scoped_refptr<DevToolsProtocol::Command> command) {
+ GetContentClient()->browser()->ClearCookies(agent_->GetRenderViewHost());
+ return command->SuccessResponse(NULL);
+}
+
+
// Page agent handlers -------------------------------------------------------
scoped_refptr<DevToolsProtocol::Response>
@@ -306,7 +354,7 @@ RendererOverridesHandler::PageHandleJavaScriptDialog(
web_contents->GetDelegate()->GetJavaScriptDialogManager();
if (manager && manager->HandleJavaScriptDialog(
web_contents, accept, prompt_override_ptr)) {
- return NULL;
+ return command->SuccessResponse(new base::DictionaryValue());
}
}
}
@@ -367,7 +415,7 @@ RendererOverridesHandler::PageGetNavigationHistory(
result->SetInteger(
devtools::Page::getNavigationHistory::kResponseCurrentIndex,
controller.GetCurrentEntryIndex());
- ListValue* entries = new ListValue();
+ base::ListValue* entries = new base::ListValue();
for (int i = 0; i != controller.GetEntryCount(); ++i) {
const NavigationEntry* entry = controller.GetEntryAtIndex(i);
base::DictionaryValue* entry_value = new base::DictionaryValue();
@@ -426,44 +474,41 @@ RendererOverridesHandler::PageCaptureScreenshot(
if (!host->GetView())
return command->InternalErrorResponse("Unable to access the view");
- std::string format;
- int quality = kDefaultScreenshotQuality;
- double scale = 1;
- ParseCaptureParameters(command.get(), &format, &quality, &scale);
-
gfx::Rect view_bounds = host->GetView()->GetViewBounds();
- gfx::Size snapshot_size = gfx::ToFlooredSize(
- gfx::ScaleSize(view_bounds.size(), scale));
-
- // Grab screen pixels if available for current platform.
- // TODO(pfeldman): support format, scale and quality in ui::GrabViewSnapshot.
- std::vector<unsigned char> png;
- bool is_unscaled_png = scale == 1 && format == kPng;
- if (is_unscaled_png && ui::GrabViewSnapshot(host->GetView()->GetNativeView(),
- &png,
- gfx::Rect(snapshot_size))) {
- std::string base64_data;
- base::Base64Encode(
- base::StringPiece(reinterpret_cast<char*>(&*png.begin()), png.size()),
- &base64_data);
- base::DictionaryValue* result = new base::DictionaryValue();
- result->SetString(
- devtools::Page::captureScreenshot::kResponseData, base64_data);
- return command->SuccessResponse(result);
+ gfx::Rect snapshot_bounds(view_bounds.size());
+ gfx::Size snapshot_size = snapshot_bounds.size();
+
+ std::vector<unsigned char> png_data;
+ if (ui::GrabViewSnapshot(host->GetView()->GetNativeView(),
+ &png_data,
+ snapshot_bounds)) {
+ if (png_data.size())
+ return command->SuccessResponse(CreateScreenshotResponse(png_data));
+ else
+ return command->InternalErrorResponse("Unable to capture screenshot");
}
- // Fallback to copying from compositing surface.
- RenderWidgetHostViewPort* view_port =
- RenderWidgetHostViewPort::FromRWHV(host->GetView());
-
- view_port->CopyFromCompositingSurface(
- view_bounds, snapshot_size,
+ ui::GrabViewSnapshotAsync(
+ host->GetView()->GetNativeView(),
+ snapshot_bounds,
+ base::ThreadTaskRunnerHandle::Get(),
base::Bind(&RendererOverridesHandler::ScreenshotCaptured,
- weak_factory_.GetWeakPtr(), command, format, quality,
- last_compositor_frame_metadata_));
+ weak_factory_.GetWeakPtr(), command));
return command->AsyncResponsePromise();
}
+void RendererOverridesHandler::ScreenshotCaptured(
+ scoped_refptr<DevToolsProtocol::Command> command,
+ scoped_refptr<base::RefCountedBytes> png_data) {
+ if (png_data) {
+ SendAsyncResponse(
+ command->SuccessResponse(CreateScreenshotResponse(png_data->data())));
+ } else {
+ SendAsyncResponse(
+ command->InternalErrorResponse("Unable to capture screenshot"));
+ }
+}
+
scoped_refptr<DevToolsProtocol::Response>
RendererOverridesHandler::PageCanScreencast(
scoped_refptr<DevToolsProtocol::Command> command) {
@@ -497,18 +542,14 @@ RendererOverridesHandler::PageStopScreencast(
return command->SuccessResponse(NULL);
}
-void RendererOverridesHandler::ScreenshotCaptured(
- scoped_refptr<DevToolsProtocol::Command> command,
+void RendererOverridesHandler::ScreencastFrameCaptured(
const std::string& format,
int quality,
const cc::CompositorFrameMetadata& metadata,
bool success,
const SkBitmap& bitmap) {
if (!success) {
- if (command) {
- SendAsyncResponse(
- command->InternalErrorResponse("Unable to capture screenshot"));
- } else if (capture_retry_count_) {
+ if (capture_retry_count_) {
--capture_retry_count_;
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
@@ -541,13 +582,8 @@ void RendererOverridesHandler::ScreenshotCaptured(
encoded = false;
}
- if (!encoded) {
- if (command) {
- SendAsyncResponse(
- command->InternalErrorResponse("Unable to encode screenshot"));
- }
+ if (!encoded)
return;
- }
std::string base_64_data;
base::Base64Encode(
@@ -593,20 +629,11 @@ void RendererOverridesHandler::ScreenshotCaptured(
response_metadata->Set(
devtools::Page::ScreencastFrameMetadata::kParamViewport, viewport);
- if (command) {
- response->Set(devtools::Page::captureScreenshot::kResponseMetadata,
- response_metadata);
- } else {
- response->Set(devtools::Page::screencastFrame::kParamMetadata,
- response_metadata);
- }
+ response->Set(devtools::Page::screencastFrame::kParamMetadata,
+ response_metadata);
}
- if (command) {
- SendAsyncResponse(command->SuccessResponse(response));
- } else {
- SendNotification(devtools::Page::screencastFrame::kName, response);
- }
+ SendNotification(devtools::Page::screencastFrame::kName, response);
}
// Quota and Usage ------------------------------------------
@@ -906,6 +933,7 @@ RendererOverridesHandler::InputDispatchGestureEvent(
agent_->GetRenderViewHost());
blink::WebGestureEvent event;
ParseGenericInputParams(params, &event);
+ event.sourceDevice = blink::WebGestureDeviceTouchscreen;
std::string type;
if (params->GetString(devtools::Input::dispatchGestureEvent::kParamType,
diff --git a/chromium/content/browser/devtools/renderer_overrides_handler.h b/chromium/content/browser/devtools/renderer_overrides_handler.h
index cce32767c39..0f7f8bc6cba 100644
--- a/chromium/content/browser/devtools/renderer_overrides_handler.h
+++ b/chromium/content/browser/devtools/renderer_overrides_handler.h
@@ -7,6 +7,7 @@
#include "base/basictypes.h"
#include "base/compiler_specific.h"
+#include "base/memory/ref_counted_memory.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
@@ -43,12 +44,20 @@ class CONTENT_EXPORT RendererOverridesHandler
void ParseCaptureParameters(DevToolsProtocol::Command* command,
std::string* format, int* quality,
double* scale);
+ base::DictionaryValue* CreateScreenshotResponse(
+ const std::vector<unsigned char>& png_data);
// DOM domain.
scoped_refptr<DevToolsProtocol::Response>
GrantPermissionsForSetFileInputFiles(
scoped_refptr<DevToolsProtocol::Command> command);
+ // Network domain.
+ scoped_refptr<DevToolsProtocol::Response> ClearBrowserCache(
+ scoped_refptr<DevToolsProtocol::Command> command);
+ scoped_refptr<DevToolsProtocol::Response> ClearBrowserCookies(
+ scoped_refptr<DevToolsProtocol::Command> command);
+
// Page domain.
scoped_refptr<DevToolsProtocol::Response> PageDisable(
scoped_refptr<DevToolsProtocol::Command> command);
@@ -75,6 +84,9 @@ class CONTENT_EXPORT RendererOverridesHandler
void ScreenshotCaptured(
scoped_refptr<DevToolsProtocol::Command> command,
+ scoped_refptr<base::RefCountedBytes> png_data);
+
+ void ScreencastFrameCaptured(
const std::string& format,
int quality,
const cc::CompositorFrameMetadata& metadata,
diff --git a/chromium/content/browser/devtools/renderer_overrides_handler_browsertest.cc b/chromium/content/browser/devtools/renderer_overrides_handler_browsertest.cc
index 24dd9621341..d869d9d6e6c 100644
--- a/chromium/content/browser/devtools/renderer_overrides_handler_browsertest.cc
+++ b/chromium/content/browser/devtools/renderer_overrides_handler_browsertest.cc
@@ -8,9 +8,9 @@
#include "content/browser/devtools/renderer_overrides_handler.h"
#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/web_contents.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/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
namespace content {
@@ -18,14 +18,15 @@ class RendererOverridesHandlerTest : public ContentBrowserTest {
protected:
scoped_refptr<DevToolsProtocol::Response> SendCommand(
const std::string& method,
- DictionaryValue* params) {
+ base::DictionaryValue* params) {
scoped_ptr<RendererOverridesHandler> handler(CreateHandler());
scoped_refptr<DevToolsProtocol::Command> command(
DevToolsProtocol::CreateCommand(1, method, params));
return handler->HandleCommand(command);
}
- void SendAsyncCommand(const std::string& method, DictionaryValue* params) {
+ void SendAsyncCommand(const std::string& method,
+ base::DictionaryValue* params) {
scoped_ptr<RendererOverridesHandler> handler(CreateHandler());
scoped_refptr<DevToolsProtocol::Command> command(
DevToolsProtocol::CreateCommand(1, method, params));
@@ -84,7 +85,7 @@ class RendererOverridesHandlerTest : public ContentBrowserTest {
};
IN_PROC_BROWSER_TEST_F(RendererOverridesHandlerTest, QueryUsageAndQuota) {
- DictionaryValue* params = new DictionaryValue();
+ base::DictionaryValue* params = new base::DictionaryValue();
params->SetString("securityOrigin", "http://example.com");
SendAsyncCommand("Page.queryUsageAndQuota", params);
diff --git a/chromium/content/browser/devtools/tethering_handler.cc b/chromium/content/browser/devtools/tethering_handler.cc
index d3e787d0c01..9b3b7fa005a 100644
--- a/chromium/content/browser/devtools/tethering_handler.cc
+++ b/chromium/content/browser/devtools/tethering_handler.cc
@@ -149,7 +149,7 @@ class SocketPump : public net::StreamListenSocket::Delegate {
}
void SelfDestruct() {
- if (wire_buffer_->offset() != wire_buffer_size_) {
+ if (wire_buffer_ && wire_buffer_->offset() != wire_buffer_size_) {
pending_destruction_ = true;
return;
}
diff --git a/chromium/content/browser/devtools/worker_devtools_manager.cc b/chromium/content/browser/devtools/worker_devtools_manager.cc
index f0608815f33..0658c353c64 100644
--- a/chromium/content/browser/devtools/worker_devtools_manager.cc
+++ b/chromium/content/browser/devtools/worker_devtools_manager.cc
@@ -12,6 +12,7 @@
#include "content/browser/devtools/devtools_manager_impl.h"
#include "content/browser/devtools/devtools_protocol.h"
#include "content/browser/devtools/devtools_protocol_constants.h"
+#include "content/browser/devtools/embedded_worker_devtools_manager.h"
#include "content/browser/devtools/ipc_devtools_agent_host.h"
#include "content/browser/devtools/worker_devtools_message_filter.h"
#include "content/browser/worker_host/worker_service_impl.h"
@@ -27,19 +28,13 @@ namespace content {
scoped_refptr<DevToolsAgentHost> DevToolsAgentHost::GetForWorker(
int worker_process_id,
int worker_route_id) {
- return WorkerDevToolsManager::GetDevToolsAgentHostForWorker(
- worker_process_id,
- worker_route_id);
-}
-
-// Called on the UI thread.
-// static
-bool DevToolsAgentHost::HasForWorker(
- int worker_process_id,
- int worker_route_id) {
- return WorkerDevToolsManager::HasDevToolsAgentHostForWorker(
- worker_process_id,
- worker_route_id);
+ if (WorkerService::EmbeddedSharedWorkerEnabled()) {
+ return EmbeddedWorkerDevToolsManager::GetInstance()
+ ->GetDevToolsAgentHostForWorker(worker_process_id, worker_route_id);
+ } else {
+ return WorkerDevToolsManager::GetDevToolsAgentHostForWorker(
+ worker_process_id, worker_route_id);
+ }
}
namespace {
@@ -217,6 +212,7 @@ struct WorkerDevToolsManager::InspectedWorker {
// static
WorkerDevToolsManager* WorkerDevToolsManager::GetInstance() {
+ DCHECK(!WorkerService::EmbeddedSharedWorkerEnabled());
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
return Singleton<WorkerDevToolsManager>::get();
}
@@ -225,6 +221,7 @@ WorkerDevToolsManager* WorkerDevToolsManager::GetInstance() {
DevToolsAgentHost* WorkerDevToolsManager::GetDevToolsAgentHostForWorker(
int worker_process_id,
int worker_route_id) {
+ DCHECK(!WorkerService::EmbeddedSharedWorkerEnabled());
WorkerId id(worker_process_id, worker_route_id);
AgentHosts::iterator it = g_agent_map.Get().find(id);
if (it == g_agent_map.Get().end())
@@ -232,21 +229,13 @@ DevToolsAgentHost* WorkerDevToolsManager::GetDevToolsAgentHostForWorker(
return it->second;
}
-// static
-bool WorkerDevToolsManager::HasDevToolsAgentHostForWorker(
- int worker_process_id,
- int worker_route_id) {
- WorkerId id(worker_process_id, worker_route_id);
- return g_agent_map.Get().find(id) != g_agent_map.Get().end();
-}
-
WorkerDevToolsManager::WorkerDevToolsManager() {
}
WorkerDevToolsManager::~WorkerDevToolsManager() {
}
-void WorkerDevToolsManager::WorkerCreated(
+bool WorkerDevToolsManager::WorkerCreated(
WorkerProcessHost* worker,
const WorkerProcessHost::WorkerInstance& instance) {
for (TerminatedInspectedWorkers::iterator it = terminated_workers_.begin();
@@ -254,14 +243,13 @@ void WorkerDevToolsManager::WorkerCreated(
if (instance.Matches(it->worker_url, it->worker_name,
instance.partition(),
instance.resource_context())) {
- worker->Send(new DevToolsAgentMsg_PauseWorkerContextOnStart(
- instance.worker_route_id()));
WorkerId new_worker_id(worker->GetData().id, instance.worker_route_id());
paused_workers_[new_worker_id] = it->old_worker_id;
terminated_workers_.erase(it);
- return;
+ return true;
}
}
+ return false;
}
void WorkerDevToolsManager::WorkerDestroyed(
diff --git a/chromium/content/browser/devtools/worker_devtools_manager.h b/chromium/content/browser/devtools/worker_devtools_manager.h
index 8a9a7084226..73916c30fe2 100644
--- a/chromium/content/browser/devtools/worker_devtools_manager.h
+++ b/chromium/content/browser/devtools/worker_devtools_manager.h
@@ -19,6 +19,7 @@ namespace content {
class DevToolsAgentHost;
// All methods are supposed to be called on the IO thread.
+// This class is not used when "enable-embedded-shared-worker" flag is set.
class WorkerDevToolsManager {
public:
typedef std::pair<int, int> WorkerId;
@@ -32,11 +33,6 @@ class WorkerDevToolsManager {
int worker_process_id,
int worker_route_id);
- // Called on the UI thread.
- static bool HasDevToolsAgentHostForWorker(
- int worker_process_id,
- int worker_route_id);
-
void ForwardToDevToolsClient(int worker_process_id,
int worker_route_id,
const std::string& message);
@@ -45,9 +41,9 @@ class WorkerDevToolsManager {
const std::string& state);
// Called on the IO thread.
- void WorkerCreated(
- WorkerProcessHost* process,
- const WorkerProcessHost::WorkerInstance& instance);
+ // Returns true when the worker must be paused on start.
+ bool WorkerCreated(WorkerProcessHost* process,
+ const WorkerProcessHost::WorkerInstance& instance);
void WorkerDestroyed(WorkerProcessHost* process, int worker_route_id);
void WorkerContextStarted(WorkerProcessHost* process, int worker_route_id);
diff --git a/chromium/content/browser/devtools/worker_devtools_message_filter.cc b/chromium/content/browser/devtools/worker_devtools_message_filter.cc
index 3f5553f120c..23c068b5405 100644
--- a/chromium/content/browser/devtools/worker_devtools_message_filter.cc
+++ b/chromium/content/browser/devtools/worker_devtools_message_filter.cc
@@ -12,7 +12,8 @@ namespace content {
WorkerDevToolsMessageFilter::WorkerDevToolsMessageFilter(
int worker_process_host_id)
- : worker_process_host_id_(worker_process_host_id),
+ : BrowserMessageFilter(DevToolsMsgStart),
+ worker_process_host_id_(worker_process_host_id),
current_routing_id_(0) {
}
@@ -20,18 +21,16 @@ WorkerDevToolsMessageFilter::~WorkerDevToolsMessageFilter() {
}
bool WorkerDevToolsMessageFilter::OnMessageReceived(
- const IPC::Message& message,
- bool* message_was_ok) {
+ const IPC::Message& message) {
bool handled = true;
current_routing_id_ = message.routing_id();
- IPC_BEGIN_MESSAGE_MAP_EX(WorkerDevToolsMessageFilter, message,
- *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(WorkerDevToolsMessageFilter, message)
IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend,
OnDispatchOnInspectorFrontend)
IPC_MESSAGE_HANDLER(DevToolsHostMsg_SaveAgentRuntimeState,
OnSaveAgentRumtimeState)
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return handled;
}
diff --git a/chromium/content/browser/devtools/worker_devtools_message_filter.h b/chromium/content/browser/devtools/worker_devtools_message_filter.h
index 73ba6f8442a..c80658e8dbf 100644
--- a/chromium/content/browser/devtools/worker_devtools_message_filter.h
+++ b/chromium/content/browser/devtools/worker_devtools_message_filter.h
@@ -18,8 +18,7 @@ class WorkerDevToolsMessageFilter : public BrowserMessageFilter {
virtual ~WorkerDevToolsMessageFilter();
// BrowserMessageFilter implementation.
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
// Message handlers.
void OnDispatchOnInspectorFrontend(const std::string& message);
void OnSaveAgentRumtimeState(const std::string& state);
diff --git a/chromium/content/browser/dom_storage/dom_storage_area.cc b/chromium/content/browser/dom_storage/dom_storage_area.cc
index e358301b130..b1e803a148d 100644
--- a/chromium/content/browser/dom_storage/dom_storage_area.cc
+++ b/chromium/content/browser/dom_storage/dom_storage_area.cc
@@ -361,9 +361,10 @@ void DOMStorageArea::OnCommitTimer() {
void DOMStorageArea::CommitChanges(const CommitBatch* commit_batch) {
// This method executes on the commit sequence.
DCHECK(task_runner_->IsRunningOnCommitSequence());
- bool success = backing_->CommitChanges(commit_batch->clear_all_first,
+ backing_->CommitChanges(commit_batch->clear_all_first,
commit_batch->changed_values);
- DCHECK(success); // TODO(michaeln): what if it fails?
+ // TODO(michaeln): what if CommitChanges returns false (e.g., we're trying to
+ // commit to a DB which is in an inconsistent state?)
task_runner_->PostTask(
FROM_HERE,
base::Bind(&DOMStorageArea::OnCommitComplete, this));
diff --git a/chromium/content/browser/dom_storage/dom_storage_area_unittest.cc b/chromium/content/browser/dom_storage/dom_storage_area_unittest.cc
index 697f6250b1d..c4d290c6a4d 100644
--- a/chromium/content/browser/dom_storage/dom_storage_area_unittest.cc
+++ b/chromium/content/browser/dom_storage/dom_storage_area_unittest.cc
@@ -18,8 +18,9 @@
#include "content/common/dom_storage/dom_storage_types.h"
#include "testing/gtest/include/gtest/gtest.h"
-namespace content {
+using base::ASCIIToUTF16;
+namespace content {
class DOMStorageAreaTest : public testing::Test {
public:
diff --git a/chromium/content/browser/dom_storage/dom_storage_browsertest.cc b/chromium/content/browser/dom_storage/dom_storage_browsertest.cc
index b2aa9f1592b..8d9e31d9d6c 100644
--- a/chromium/content/browser/dom_storage/dom_storage_browsertest.cc
+++ b/chromium/content/browser/dom_storage/dom_storage_browsertest.cc
@@ -7,9 +7,9 @@
#include "content/common/dom_storage/dom_storage_types.h"
#include "content/public/common/content_paths.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/shell/browser/shell.h"
-#include "content/test/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
#include "net/base/net_util.h"
namespace content {
diff --git a/chromium/content/browser/dom_storage/dom_storage_context_impl.cc b/chromium/content/browser/dom_storage/dom_storage_context_impl.cc
index 2c31bd627b3..05a65870587 100644
--- a/chromium/content/browser/dom_storage/dom_storage_context_impl.cc
+++ b/chromium/content/browser/dom_storage/dom_storage_context_impl.cc
@@ -160,17 +160,6 @@ void DOMStorageContextImpl::DeleteSessionStorage(
NotifyAreaCleared(area, usage_info.origin);
}
-void DOMStorageContextImpl::PurgeMemory() {
- // We can only purge memory from the local storage namespace
- // which is backed by disk.
- // TODO(marja): Purge sessionStorage, too. (Requires changes to the FastClear
- // functionality.)
- StorageNamespaceMap::iterator found =
- namespaces_.find(kLocalStorageNamespaceId);
- if (found != namespaces_.end())
- found->second->PurgeMemory(DOMStorageNamespace::PURGE_AGGRESSIVE);
-}
-
void DOMStorageContextImpl::Shutdown() {
is_shutdown_ = true;
StorageNamespaceMap::const_iterator it = namespaces_.begin();
diff --git a/chromium/content/browser/dom_storage/dom_storage_context_impl.h b/chromium/content/browser/dom_storage/dom_storage_context_impl.h
index a9400fbc594..d256d9c5723 100644
--- a/chromium/content/browser/dom_storage/dom_storage_context_impl.h
+++ b/chromium/content/browser/dom_storage/dom_storage_context_impl.h
@@ -117,7 +117,6 @@ class CONTENT_EXPORT DOMStorageContextImpl
void GetSessionStorageUsage(std::vector<SessionStorageUsageInfo>* infos);
void DeleteLocalStorage(const GURL& origin);
void DeleteSessionStorage(const SessionStorageUsageInfo& usage_info);
- void PurgeMemory();
// Used by content settings to alter the behavior around
// what data to keep and what data to discard at shutdown.
diff --git a/chromium/content/browser/dom_storage/dom_storage_context_impl_unittest.cc b/chromium/content/browser/dom_storage/dom_storage_context_impl_unittest.cc
index ba8d8e98bc6..d29741f62ea 100644
--- a/chromium/content/browser/dom_storage/dom_storage_context_impl_unittest.cc
+++ b/chromium/content/browser/dom_storage/dom_storage_context_impl_unittest.cc
@@ -17,8 +17,10 @@
#include "content/public/browser/local_storage_usage_info.h"
#include "content/public/browser/session_storage_namespace.h"
#include "content/public/browser/session_storage_usage_info.h"
+#include "content/public/test/mock_special_storage_policy.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "webkit/browser/quota/mock_special_storage_policy.h"
+
+using base::ASCIIToUTF16;
namespace content {
@@ -40,7 +42,7 @@ class DOMStorageContextImplTest : public testing::Test {
virtual void SetUp() {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
- storage_policy_ = new quota::MockSpecialStoragePolicy;
+ storage_policy_ = new MockSpecialStoragePolicy;
task_runner_ =
new MockDOMStorageTaskRunner(base::MessageLoopProxy::current().get());
context_ = new DOMStorageContextImpl(temp_dir_.path(),
@@ -67,7 +69,7 @@ class DOMStorageContextImplTest : public testing::Test {
protected:
base::MessageLoop message_loop_;
base::ScopedTempDir temp_dir_;
- scoped_refptr<quota::MockSpecialStoragePolicy> storage_policy_;
+ scoped_refptr<MockSpecialStoragePolicy> storage_policy_;
scoped_refptr<MockDOMStorageTaskRunner> task_runner_;
scoped_refptr<DOMStorageContextImpl> context_;
DISALLOW_COPY_AND_ASSIGN(DOMStorageContextImplTest);
@@ -80,7 +82,6 @@ TEST_F(DOMStorageContextImplTest, Basics) {
EXPECT_EQ(temp_dir_.path(), context_->localstorage_directory());
EXPECT_EQ(base::FilePath(), context_->sessionstorage_directory());
EXPECT_EQ(storage_policy_.get(), context_->special_storage_policy_.get());
- context_->PurgeMemory();
context_->DeleteLocalStorage(GURL("http://chromium.org/"));
const int kFirstSessionStorageNamespaceId = 1;
EXPECT_TRUE(context_->GetStorageNamespace(kLocalStorageNamespaceId));
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 56ea51e3d2a..47d5b05be9a 100644
--- a/chromium/content/browser/dom_storage/dom_storage_context_wrapper.cc
+++ b/chromium/content/browser/dom_storage/dom_storage_context_wrapper.cc
@@ -146,14 +146,6 @@ void DOMStorageContextWrapper::StartScavengingUnusedSessionStorage() {
context_));
}
-void DOMStorageContextWrapper::PurgeMemory() {
- DCHECK(context_.get());
- context_->task_runner()->PostShutdownBlockingTask(
- FROM_HERE,
- DOMStorageTaskRunner::PRIMARY_SEQUENCE,
- base::Bind(&DOMStorageContextImpl::PurgeMemory, context_));
-}
-
void DOMStorageContextWrapper::SetForceKeepSessionState() {
DCHECK(context_.get());
context_->task_runner()->PostShutdownBlockingTask(
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 0971f889835..0ed95076a20 100644
--- a/chromium/content/browser/dom_storage/dom_storage_context_wrapper.h
+++ b/chromium/content/browser/dom_storage/dom_storage_context_wrapper.h
@@ -44,9 +44,6 @@ class CONTENT_EXPORT DOMStorageContextWrapper :
RecreateSessionStorage(const std::string& persistent_id) OVERRIDE;
virtual void StartScavengingUnusedSessionStorage() OVERRIDE;
- // Called to free up memory that's not strictly needed.
- void PurgeMemory();
-
// Used by content settings to alter the behavior around
// what data to keep and what data to discard at shutdown.
// The policy is not so straight forward to describe, see
diff --git a/chromium/content/browser/dom_storage/dom_storage_database.cc b/chromium/content/browser/dom_storage/dom_storage_database.cc
index 690d02acf2f..aaf6bb03447 100644
--- a/chromium/content/browser/dom_storage/dom_storage_database.cc
+++ b/chromium/content/browser/dom_storage/dom_storage_database.cc
@@ -107,7 +107,7 @@ bool DOMStorageDatabase::CommitChanges(bool clear_all_first,
"INSERT INTO ItemTable VALUES (?,?)"));
statement.BindString16(0, key);
statement.BindBlob(1, value.string().data(),
- value.string().length() * sizeof(char16));
+ value.string().length() * sizeof(base::char16));
known_to_be_empty_ = false;
did_insert = true;
}
@@ -229,8 +229,6 @@ DOMStorageDatabase::SchemaVersion DOMStorageDatabase::DetectSchemaVersion() {
default:
return INVALID;
}
- NOTREACHED();
- return INVALID;
}
bool DOMStorageDatabase::CreateTableV2() {
diff --git a/chromium/content/browser/dom_storage/dom_storage_database_unittest.cc b/chromium/content/browser/dom_storage/dom_storage_database_unittest.cc
index f3ba0b64df7..e20e399ec6c 100644
--- a/chromium/content/browser/dom_storage/dom_storage_database_unittest.cc
+++ b/chromium/content/browser/dom_storage/dom_storage_database_unittest.cc
@@ -14,6 +14,8 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/sqlite/sqlite3.h"
+using base::ASCIIToUTF16;
+
namespace content {
void CreateV1Table(sql::Connection* db) {
@@ -344,7 +346,7 @@ TEST(DOMStorageDatabaseTest, TestCanOpenFileThatIsNotADatabase) {
temp_dir.path().AppendASCII("TestDOMStorageDatabase.db");
const char kData[] = "I am not a database.";
- file_util::WriteFile(file_name, kData, strlen(kData));
+ base::WriteFile(file_name, kData, strlen(kData));
{
sql::ScopedErrorIgnorer ignore_errors;
diff --git a/chromium/content/browser/dom_storage/dom_storage_host.cc b/chromium/content/browser/dom_storage/dom_storage_host.cc
index 9128f57461f..556f76d5c0f 100644
--- a/chromium/content/browser/dom_storage/dom_storage_host.cc
+++ b/chromium/content/browser/dom_storage/dom_storage_host.cc
@@ -32,12 +32,8 @@ bool DOMStorageHost::OpenStorageArea(int connection_id, int namespace_id,
return false; // Indicates the renderer gave us very bad data.
NamespaceAndArea references;
references.namespace_ = context_->GetStorageNamespace(namespace_id);
- if (!references.namespace_.get()) {
- // TODO(michaeln): Fix crbug/134003 and return false here.
- // Until then return true to avoid crashing the renderer for
- // sending a bad message.
- return true;
- }
+ if (!references.namespace_.get())
+ return false;
references.area_ = references.namespace_->OpenStorageArea(origin);
DCHECK(references.area_.get());
connections_[connection_id] = references;
@@ -56,12 +52,8 @@ bool DOMStorageHost::ExtractAreaValues(
int connection_id, DOMStorageValuesMap* map, bool* send_log_get_messages) {
map->clear();
DOMStorageArea* area = GetOpenArea(connection_id);
- if (!area) {
- // TODO(michaeln): Fix crbug/134003 and return false here.
- // Until then return true to avoid crashing the renderer
- // for sending a bad message.
- return true;
- }
+ if (!area)
+ return false;
if (!area->IsLoadedInMemory()) {
DOMStorageNamespace* ns = GetNamespace(connection_id);
DCHECK(ns);
@@ -107,12 +99,8 @@ bool DOMStorageHost::SetAreaItem(
const base::string16& value, const GURL& page_url,
base::NullableString16* old_value) {
DOMStorageArea* area = GetOpenArea(connection_id);
- if (!area) {
- // TODO(michaeln): Fix crbug/134003 and return false here.
- // Until then return true to allow the renderer to operate
- // to a limited degree out of its cache.
- return true;
- }
+ if (!area)
+ return false;
if (!area->SetItem(key, value, old_value))
return false;
if (old_value->is_null() || old_value->string() != value)
diff --git a/chromium/content/browser/dom_storage/dom_storage_host.h b/chromium/content/browser/dom_storage/dom_storage_host.h
index f4ca784b2fa..10d59198f23 100644
--- a/chromium/content/browser/dom_storage/dom_storage_host.h
+++ b/chromium/content/browser/dom_storage/dom_storage_host.h
@@ -81,6 +81,8 @@ class CONTENT_EXPORT DOMStorageHost {
scoped_refptr<DOMStorageContextImpl> context_;
AreaMap connections_;
int render_process_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(DOMStorageHost);
};
} // namespace content
diff --git a/chromium/content/browser/dom_storage/dom_storage_message_filter.cc b/chromium/content/browser/dom_storage/dom_storage_message_filter.cc
index fbc3880abb7..91d72d32762 100644
--- a/chromium/content/browser/dom_storage/dom_storage_message_filter.cc
+++ b/chromium/content/browser/dom_storage/dom_storage_message_filter.cc
@@ -23,7 +23,8 @@ namespace content {
DOMStorageMessageFilter::DOMStorageMessageFilter(
int render_process_id,
DOMStorageContextWrapper* context)
- : render_process_id_(render_process_id),
+ : BrowserMessageFilter(DOMStorageMsgStart),
+ render_process_id_(render_process_id),
context_(context->context()),
connection_dispatching_message_for_(0) {
}
@@ -46,7 +47,7 @@ void DOMStorageMessageFilter::UninitializeInSequence() {
host_.reset();
}
-void DOMStorageMessageFilter::OnFilterAdded(IPC::Channel* channel) {
+void DOMStorageMessageFilter::OnFilterAdded(IPC::Sender* sender) {
context_->task_runner()->PostShutdownBlockingTask(
FROM_HERE,
DOMStorageTaskRunner::PRIMARY_SEQUENCE,
@@ -67,15 +68,14 @@ base::TaskRunner* DOMStorageMessageFilter::OverrideTaskRunnerForMessage(
return NULL;
}
-bool DOMStorageMessageFilter::OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) {
+bool DOMStorageMessageFilter::OnMessageReceived(const IPC::Message& message) {
if (IPC_MESSAGE_CLASS(message) != DOMStorageMsgStart)
return false;
DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(host_.get());
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(DOMStorageMessageFilter, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(DOMStorageMessageFilter, message)
IPC_MESSAGE_HANDLER(DOMStorageHostMsg_OpenStorageArea, OnOpenStorageArea)
IPC_MESSAGE_HANDLER(DOMStorageHostMsg_CloseStorageArea, OnCloseStorageArea)
IPC_MESSAGE_HANDLER(DOMStorageHostMsg_LoadStorageArea, OnLoadStorageArea)
@@ -94,7 +94,7 @@ void DOMStorageMessageFilter::OnOpenStorageArea(int connection_id,
const GURL& origin) {
DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!host_->OpenStorageArea(connection_id, namespace_id, origin)) {
- RecordAction(UserMetricsAction("BadMessageTerminate_DSMF_1"));
+ RecordAction(base::UserMetricsAction("BadMessageTerminate_DSMF_1"));
BadMessageReceived();
}
}
@@ -109,7 +109,7 @@ void DOMStorageMessageFilter::OnLoadStorageArea(int connection_id,
bool* send_log_get_messages) {
DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!host_->ExtractAreaValues(connection_id, map, send_log_get_messages)) {
- RecordAction(UserMetricsAction("BadMessageTerminate_DSMF_2"));
+ RecordAction(base::UserMetricsAction("BadMessageTerminate_DSMF_2"));
BadMessageReceived();
}
Send(new DOMStorageMsg_AsyncOperationComplete(true));
diff --git a/chromium/content/browser/dom_storage/dom_storage_message_filter.h b/chromium/content/browser/dom_storage/dom_storage_message_filter.h
index 3c82418842c..346a8ff5654 100644
--- a/chromium/content/browser/dom_storage/dom_storage_message_filter.h
+++ b/chromium/content/browser/dom_storage/dom_storage_message_filter.h
@@ -40,12 +40,11 @@ class DOMStorageMessageFilter
void UninitializeInSequence();
// BrowserMessageFilter implementation
- virtual void OnFilterAdded(IPC::Channel* channel) OVERRIDE;
+ virtual void OnFilterAdded(IPC::Sender* sender) OVERRIDE;
virtual void OnFilterRemoved() OVERRIDE;
virtual base::TaskRunner* OverrideTaskRunnerForMessage(
const IPC::Message& message) OVERRIDE;
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
// Message Handlers.
void OnOpenStorageArea(int connection_id, int64 namespace_id,
diff --git a/chromium/content/browser/dom_storage/dom_storage_namespace.cc b/chromium/content/browser/dom_storage/dom_storage_namespace.cc
index ac1e2f89cd0..d36147f076e 100644
--- a/chromium/content/browser/dom_storage/dom_storage_namespace.cc
+++ b/chromium/content/browser/dom_storage/dom_storage_namespace.cc
@@ -16,8 +16,8 @@
#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_database.h"
-#include "content/common/child_process_host_impl.h"
#include "content/common/dom_storage/dom_storage_types.h"
+#include "content/public/common/child_process_host.h"
namespace content {
@@ -247,14 +247,14 @@ DOMStorageNamespace::GetAreaHolder(const GURL& origin) {
}
void DOMStorageNamespace::AddTransactionLogProcessId(int process_id) {
- DCHECK(process_id != ChildProcessHostImpl::kInvalidChildProcessId);
+ DCHECK(process_id != ChildProcessHost::kInvalidUniqueID);
DCHECK(transactions_.count(process_id) == 0);
TransactionData* transaction_data = new TransactionData;
transactions_[process_id] = transaction_data;
}
void DOMStorageNamespace::RemoveTransactionLogProcessId(int process_id) {
- DCHECK(process_id != ChildProcessHostImpl::kInvalidChildProcessId);
+ DCHECK(process_id != ChildProcessHost::kInvalidUniqueID);
DCHECK(transactions_.count(process_id) == 1);
delete transactions_[process_id];
transactions_.erase(process_id);
@@ -353,7 +353,7 @@ SessionStorageNamespace::MergeResult DOMStorageNamespace::Merge(
}
bool DOMStorageNamespace::IsLoggingRenderer(int process_id) {
- DCHECK(process_id != ChildProcessHostImpl::kInvalidChildProcessId);
+ DCHECK(process_id != ChildProcessHost::kInvalidUniqueID);
if (transactions_.count(process_id) < 1)
return false;
return !transactions_[process_id]->max_log_size_exceeded;
diff --git a/chromium/content/browser/dom_storage/session_storage_database.cc b/chromium/content/browser/dom_storage/session_storage_database.cc
index 7b8dcd0bf0a..30d3d86577e 100644
--- a/chromium/content/browser/dom_storage/session_storage_database.cc
+++ b/chromium/content/browser/dom_storage/session_storage_database.cc
@@ -51,10 +51,53 @@ enum SessionStorageUMA {
namespace content {
+// This class keeps track of ongoing operations across different threads. When
+// DB inconsistency is detected, we need to 1) make sure no new operations start
+// 2) wait until all current operations finish, and let the last one of them
+// close the DB and delete the data. The DB will remain empty for the rest of
+// the run, and will be recreated during the next run. We cannot hope to recover
+// during this run, since the upper layer will have a different idea about what
+// should be in the database.
+class SessionStorageDatabase::DBOperation {
+ public:
+ DBOperation(SessionStorageDatabase* session_storage_database)
+ : session_storage_database_(session_storage_database) {
+ base::AutoLock auto_lock(session_storage_database_->db_lock_);
+ ++session_storage_database_->operation_count_;
+ }
+
+ ~DBOperation() {
+ base::AutoLock auto_lock(session_storage_database_->db_lock_);
+ --session_storage_database_->operation_count_;
+ if ((session_storage_database_->is_inconsistent_ ||
+ session_storage_database_->db_error_) &&
+ session_storage_database_->operation_count_ == 0 &&
+ !session_storage_database_->invalid_db_deleted_) {
+ // No other operations are ongoing and the data is bad -> delete it now.
+ session_storage_database_->db_.reset();
+#if defined(OS_WIN)
+ leveldb::DestroyDB(
+ base::WideToUTF8(session_storage_database_->file_path_.value()),
+ leveldb::Options());
+#else
+ leveldb::DestroyDB(session_storage_database_->file_path_.value(),
+ leveldb::Options());
+#endif
+ session_storage_database_->invalid_db_deleted_ = true;
+ }
+ }
+
+ private:
+ SessionStorageDatabase* session_storage_database_;
+};
+
+
SessionStorageDatabase::SessionStorageDatabase(const base::FilePath& file_path)
: file_path_(file_path),
db_error_(false),
- is_inconsistent_(false) {
+ is_inconsistent_(false),
+ invalid_db_deleted_(false),
+ operation_count_(0) {
}
SessionStorageDatabase::~SessionStorageDatabase() {
@@ -67,6 +110,7 @@ void SessionStorageDatabase::ReadAreaValues(const std::string& namespace_id,
// nothing to be added to the result.
if (!LazyOpen(false))
return;
+ DBOperation operation(this);
// While ReadAreaValues is in progress, another thread can call
// CommitAreaChanges. CommitAreaChanges might update map ref count key while
@@ -92,6 +136,7 @@ bool SessionStorageDatabase::CommitAreaChanges(
// in the database, so that it can be later shallow-copied succssfully.
if (!LazyOpen(true))
return false;
+ DBOperation operation(this);
leveldb::WriteBatch batch;
// Ensure that the keys "namespace-" "namespace-N" (see the schema above)
@@ -153,6 +198,7 @@ bool SessionStorageDatabase::CloneNamespace(
if (!LazyOpen(true))
return false;
+ DBOperation operation(this);
leveldb::WriteBatch batch;
const bool kOkIfExists = false;
@@ -181,6 +227,7 @@ bool SessionStorageDatabase::DeleteArea(const std::string& namespace_id,
// No need to create the database if it doesn't exist.
return true;
}
+ DBOperation operation(this);
leveldb::WriteBatch batch;
if (!DeleteAreaHelper(namespace_id, origin.spec(), &batch))
return false;
@@ -193,6 +240,7 @@ bool SessionStorageDatabase::DeleteNamespace(const std::string& namespace_id) {
// No need to create the database if it doesn't exist.
return true;
}
+ DBOperation operation(this);
// Itereate through the areas in the namespace.
leveldb::WriteBatch batch;
std::map<std::string, std::string> areas;
@@ -213,6 +261,7 @@ bool SessionStorageDatabase::ReadNamespacesAndOrigins(
std::map<std::string, std::vector<GURL> >* namespaces_and_origins) {
if (!LazyOpen(true))
return false;
+ DBOperation operation(this);
// While ReadNamespacesAndOrigins is in progress, another thread can call
// CommitAreaChanges. To protect the reading operation, create a snapshot and
@@ -328,7 +377,7 @@ leveldb::Status SessionStorageDatabase::TryToOpen(leveldb::DB** db) {
options.max_open_files = 0; // Use minimum.
options.create_if_missing = true;
#if defined(OS_WIN)
- return leveldb::DB::Open(options, WideToUTF8(file_path_.value()), db);
+ return leveldb::DB::Open(options, base::WideToUTF8(file_path_.value()), db);
#elif defined(OS_POSIX)
return leveldb::DB::Open(options, file_path_.value(), db);
#endif
@@ -347,12 +396,11 @@ bool SessionStorageDatabase::ConsistencyCheck(bool ok) {
if (ok)
return true;
base::AutoLock auto_lock(db_lock_);
- DCHECK(false);
- is_inconsistent_ = true;
// We cannot recover the database during this run, e.g., the upper layer can
// have a different understanding of the database state (shallow and deep
- // copies).
- // TODO(marja): Error handling.
+ // copies). Make further operations fail. The next operation that finishes
+ // will delete the data, and next run will recerate the database.
+ is_inconsistent_ = true;
return false;
}
@@ -360,8 +408,9 @@ bool SessionStorageDatabase::DatabaseErrorCheck(bool ok) {
if (ok)
return true;
base::AutoLock auto_lock(db_lock_);
+ // Make further operations fail. The next operation that finishes
+ // will delete the data, and next run will recerate the database.
db_error_ = true;
- // TODO(marja): Error handling.
return false;
}
@@ -522,15 +571,16 @@ bool SessionStorageDatabase::ReadMap(const std::string& map_id,
break;
}
// Key is of the form "map-<mapid>-<key>".
- base::string16 key16 = UTF8ToUTF16(key.substr(map_start_key.length()));
+ base::string16 key16 =
+ base::UTF8ToUTF16(key.substr(map_start_key.length()));
if (only_keys) {
(*result)[key16] = base::NullableString16();
} else {
// Convert the raw data stored in std::string (it->value()) to raw data
// stored in base::string16.
- size_t len = it->value().size() / sizeof(char16);
- const char16* data_ptr =
- reinterpret_cast<const char16*>(it->value().data());
+ size_t len = it->value().size() / sizeof(base::char16);
+ const base::char16* data_ptr =
+ reinterpret_cast<const base::char16*>(it->value().data());
(*result)[key16] =
base::NullableString16(base::string16(data_ptr, len), false);
}
@@ -545,7 +595,7 @@ void SessionStorageDatabase::WriteValuesToMap(const std::string& map_id,
it != values.end();
++it) {
base::NullableString16 value = it->second;
- std::string key = MapKey(map_id, UTF16ToUTF8(it->first));
+ std::string key = MapKey(map_id, base::UTF16ToUTF8(it->first));
if (value.is_null()) {
batch->Delete(key);
} else {
@@ -607,7 +657,7 @@ bool SessionStorageDatabase::ClearMap(const std::string& map_id,
return false;
for (DOMStorageValuesMap::const_iterator it = values.begin();
it != values.end(); ++it)
- batch->Delete(MapKey(map_id, UTF16ToUTF8(it->first)));
+ batch->Delete(MapKey(map_id, base::UTF16ToUTF8(it->first)));
return true;
}
diff --git a/chromium/content/browser/dom_storage/session_storage_database.h b/chromium/content/browser/dom_storage/session_storage_database.h
index 09d4773fea1..4260e4adb56 100644
--- a/chromium/content/browser/dom_storage/session_storage_database.h
+++ b/chromium/content/browser/dom_storage/session_storage_database.h
@@ -74,6 +74,8 @@ class CONTENT_EXPORT SessionStorageDatabase :
private:
friend class base::RefCountedThreadSafe<SessionStorageDatabase>;
+ class DBOperation;
+ friend class SessionStorageDatabase::DBOperation;
friend class SessionStorageDatabaseTest;
~SessionStorageDatabase();
@@ -189,13 +191,20 @@ class CONTENT_EXPORT SessionStorageDatabase :
scoped_ptr<leveldb::DB> db_;
base::FilePath file_path_;
- // For protecting the database opening code.
+ // For protecting the database opening code. Also guards the variables below.
base::Lock db_lock_;
// True if a database error has occurred (e.g., cannot read data).
bool db_error_;
// True if the database is in an inconsistent state.
bool is_inconsistent_;
+ // True if the database is in a failed or inconsistent state, and we have
+ // already deleted it (as an attempt to recover later).
+ bool invalid_db_deleted_;
+
+ // The number of database operations in progress. We need this so that we can
+ // delete an inconsistent database at the right moment.
+ int operation_count_;
DISALLOW_COPY_AND_ASSIGN(SessionStorageDatabase);
};
diff --git a/chromium/content/browser/dom_storage/session_storage_database_unittest.cc b/chromium/content/browser/dom_storage/session_storage_database_unittest.cc
index 2f86c3b9918..71c8063ef44 100644
--- a/chromium/content/browser/dom_storage/session_storage_database_unittest.cc
+++ b/chromium/content/browser/dom_storage/session_storage_database_unittest.cc
@@ -87,13 +87,13 @@ SessionStorageDatabaseTest::SessionStorageDatabaseTest()
kNamespace1("namespace1"),
kNamespace2("namespace2"),
kNamespaceClone("wascloned"),
- kKey1(ASCIIToUTF16("key1")),
- kKey2(ASCIIToUTF16("key2")),
- kKey3(ASCIIToUTF16("key3")),
- kValue1(ASCIIToUTF16("value1"), false),
- kValue2(ASCIIToUTF16("value2"), false),
- kValue3(ASCIIToUTF16("value3"), false),
- kValue4(ASCIIToUTF16("value4"), false) { }
+ kKey1(base::ASCIIToUTF16("key1")),
+ kKey2(base::ASCIIToUTF16("key2")),
+ kKey3(base::ASCIIToUTF16("key3")),
+ kValue1(base::ASCIIToUTF16("value1"), false),
+ kValue2(base::ASCIIToUTF16("value2"), false),
+ kValue3(base::ASCIIToUTF16("value3"), false),
+ kValue4(base::ASCIIToUTF16("value4"), false) { }
SessionStorageDatabaseTest::~SessionStorageDatabaseTest() { }
@@ -303,9 +303,10 @@ void SessionStorageDatabaseTest::DumpData() const {
if (IsMapValueKey(it->key().ToString(), &dummy_map_id)) {
// Convert the value back to base::string16.
base::string16 value;
- size_t len = it->value().size() / sizeof(char16);
+ size_t len = it->value().size() / sizeof(base::char16);
value.resize(len);
- value.assign(reinterpret_cast<const char16*>(it->value().data()), len);
+ value.assign(
+ reinterpret_cast<const base::char16*>(it->value().data()), len);
LOG(WARNING) << it->key().ToString() << ": " << value;
} else {
LOG(WARNING) << it->key().ToString() << ": " << it->value().ToString();
@@ -699,7 +700,7 @@ TEST_F(SessionStorageDatabaseTest, WriteRawBytes) {
unsigned char raw_data[10] = {255, 0, 0, 0, 1, 2, 3, 4, 5, 0};
DOMStorageValuesMap changes;
base::string16 string_with_raw_data;
- string_with_raw_data.assign(reinterpret_cast<char16*>(raw_data), 5);
+ string_with_raw_data.assign(reinterpret_cast<base::char16*>(raw_data), 5);
changes[kKey1] = base::NullableString16(string_with_raw_data, false);
EXPECT_TRUE(db_->CommitAreaChanges(kNamespace1, kOrigin1, false, changes));
CheckDatabaseConsistency();
diff --git a/chromium/content/browser/download/base_file.cc b/chromium/content/browser/download/base_file.cc
index 1de3e842dd8..362f64ea1e0 100644
--- a/chromium/content/browser/download/base_file.cc
+++ b/chromium/content/browser/download/base_file.cc
@@ -6,6 +6,7 @@
#include "base/bind.h"
#include "base/file_util.h"
+#include "base/files/file.h"
#include "base/format_macros.h"
#include "base/logging.h"
#include "base/pickle.h"
@@ -17,7 +18,6 @@
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
#include "crypto/secure_hash.h"
-#include "net/base/file_stream.h"
#include "net/base/net_errors.h"
namespace content {
@@ -31,18 +31,18 @@ BaseFile::BaseFile(const base::FilePath& full_path,
int64 received_bytes,
bool calculate_hash,
const std::string& hash_state_bytes,
- scoped_ptr<net::FileStream> file_stream,
+ base::File file,
const net::BoundNetLog& bound_net_log)
: full_path_(full_path),
source_url_(source_url),
referrer_url_(referrer_url),
- file_stream_(file_stream.Pass()),
+ file_(file.Pass()),
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, kSha256HashLen);
+ 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.
@@ -67,11 +67,6 @@ DownloadInterruptReason BaseFile::Initialize(
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
DCHECK(!detached_);
- if (file_stream_) {
- file_stream_->SetBoundNetLogSource(bound_net_log_);
- file_stream_->EnableErrorStatistics();
- }
-
if (full_path_.empty()) {
base::FilePath initial_directory(default_directory);
base::FilePath temp_file;
@@ -103,7 +98,7 @@ DownloadInterruptReason BaseFile::AppendDataToFile(const char* data,
if (detached_)
RecordDownloadCount(APPEND_TO_DETACHED_FILE_COUNT);
- if (!file_stream_)
+ if (!file_.IsValid())
return LogInterruptReason("No file stream on append", 0,
DOWNLOAD_INTERRUPT_REASON_FILE_FAILED);
@@ -117,19 +112,12 @@ DownloadInterruptReason BaseFile::AppendDataToFile(const char* data,
const char* current_data = data;
while (len > 0) {
write_count++;
- int write_result =
- file_stream_->WriteSync(current_data, len);
+ int write_result = file_.WriteAtCurrentPos(current_data, len);
DCHECK_NE(0, write_result);
- // Check for errors.
- if (static_cast<size_t>(write_result) != data_len) {
- // We should never get ERR_IO_PENDING, as the Write above is synchronous.
- DCHECK_NE(net::ERR_IO_PENDING, write_result);
-
- // Report errors on file writes.
- if (write_result < 0)
- return LogNetError("Write", static_cast<net::Error>(write_result));
- }
+ // Report errors on file writes.
+ if (write_result < 0)
+ return LogSystemError("Write", logging::GetLastSystemErrorCode());
// Update status.
size_t write_size = static_cast<size_t>(write_result);
@@ -197,16 +185,17 @@ void BaseFile::Cancel() {
if (!full_path_.empty()) {
bound_net_log_.AddEvent(net::NetLog::TYPE_DOWNLOAD_FILE_DELETED);
-
base::DeleteFile(full_path_, false);
}
+
+ Detach();
}
void BaseFile::Finish() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
if (calculate_hash_)
- secure_hash_->Finish(sha256_hash_, kSha256HashLen);
+ secure_hash_->Finish(sha256_hash_, crypto::kSHA256Length);
Close();
}
@@ -243,8 +232,8 @@ std::string BaseFile::GetHashState() {
// static
bool BaseFile::IsEmptyHash(const std::string& hash) {
- return (hash.size() == kSha256HashLen &&
- 0 == memcmp(hash.data(), kEmptySha256Hash, sizeof(kSha256HashLen)));
+ return (hash.size() == crypto::kSHA256Length &&
+ 0 == memcmp(hash.data(), kEmptySha256Hash, crypto::kSHA256Length));
}
std::string BaseFile::DebugString() const {
@@ -258,11 +247,6 @@ std::string BaseFile::DebugString() const {
detached_ ? 'T' : 'F');
}
-void BaseFile::CreateFileStream() {
- file_stream_.reset(new net::FileStream(bound_net_log_.net_log()));
- file_stream_->SetBoundNetLogSource(bound_net_log_);
-}
-
DownloadInterruptReason BaseFile::Open() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
DCHECK(!detached_);
@@ -272,43 +256,36 @@ DownloadInterruptReason BaseFile::Open() {
net::NetLog::TYPE_DOWNLOAD_FILE_OPENED,
base::Bind(&FileOpenedNetLogCallback, &full_path_, bytes_so_far_));
- // Create a new file stream if it is not provided.
- if (!file_stream_) {
- CreateFileStream();
- file_stream_->EnableErrorStatistics();
- int open_result = file_stream_->OpenSync(
- full_path_,
- base::PLATFORM_FILE_OPEN_ALWAYS | base::PLATFORM_FILE_WRITE);
- if (open_result != net::OK) {
- ClearStream();
- return LogNetError("Open", static_cast<net::Error>(open_result));
- }
-
- // We may be re-opening the file after rename. Always make sure we're
- // writing at the end of the file.
- int64 seek_result = file_stream_->SeekSync(net::FROM_END, 0);
- if (seek_result < 0) {
- ClearStream();
- return LogNetError("Seek", static_cast<net::Error>(seek_result));
+ // 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);
+ if (!file_.IsValid()) {
+ return LogNetError("Open",
+ net::FileErrorToNetError(file_.error_details()));
}
- } else {
- file_stream_->SetBoundNetLogSource(bound_net_log_);
}
- int64 file_size = file_stream_->SeekSync(net::FROM_END, 0);
- if (file_size > bytes_so_far_) {
+ // We may be re-opening the file after rename. Always make sure we're
+ // writing at the end of the file.
+ int64 file_size = file_.Seek(base::File::FROM_END, 0);
+ if (file_size < 0) {
+ logging::SystemErrorCode error = logging::GetLastSystemErrorCode();
+ ClearFile();
+ return LogSystemError("Seek", 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.
// Truncate the file.
- int64 truncate_result = file_stream_->Truncate(bytes_so_far_);
- if (truncate_result < 0)
- return LogNetError("Truncate", static_cast<net::Error>(truncate_result));
-
- // If if wasn't an error, it should have truncated to the size
- // specified.
- DCHECK_EQ(bytes_so_far_, truncate_result);
+ if (!file_.SetLength(bytes_so_far_) ||
+ file_.Seek(base::File::FROM_BEGIN, bytes_so_far_) != bytes_so_far_) {
+ logging::SystemErrorCode error = logging::GetLastSystemErrorCode();
+ ClearFile();
+ return LogSystemError("Truncate", error);
+ }
} else if (file_size < bytes_so_far_) {
// The file is shorter than we expected. Our hashes won't be valid.
+ ClearFile();
return LogInterruptReason("Unable to seek to last written point", 0,
DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT);
}
@@ -321,20 +298,18 @@ void BaseFile::Close() {
bound_net_log_.AddEvent(net::NetLog::TYPE_DOWNLOAD_FILE_CLOSED);
- if (file_stream_) {
-#if defined(OS_CHROMEOS)
+ if (file_.IsValid()) {
// Currently we don't really care about the return value, since if it fails
// theres not much we can do. But we might in the future.
- file_stream_->FlushSync();
-#endif
- ClearStream();
+ file_.Flush();
+ ClearFile();
}
}
-void BaseFile::ClearStream() {
+void BaseFile::ClearFile() {
// This should only be called when we have a stream.
- DCHECK(file_stream_.get() != NULL);
- file_stream_.reset();
+ DCHECK(file_.IsValid());
+ file_.Close();
bound_net_log_.EndEvent(net::NetLog::TYPE_DOWNLOAD_FILE_OPENED);
}
@@ -349,7 +324,7 @@ DownloadInterruptReason BaseFile::LogNetError(
DownloadInterruptReason BaseFile::LogSystemError(
const char* operation,
- int os_error) {
+ logging::SystemErrorCode os_error) {
// There's no direct conversion from a system error to an interrupt reason.
net::Error net_error = net::MapSystemError(os_error);
return LogInterruptReason(
diff --git a/chromium/content/browser/download/base_file.h b/chromium/content/browser/download/base_file.h
index f1686c81fb0..a14c5daef31 100644
--- a/chromium/content/browser/download/base_file.h
+++ b/chromium/content/browser/download/base_file.h
@@ -7,14 +7,16 @@
#include <string>
+#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/gtest_prod_util.h"
+#include "base/logging.h"
#include "base/memory/linked_ptr.h"
#include "base/memory/scoped_ptr.h"
#include "base/time/time.h"
#include "content/common/content_export.h"
#include "content/public/browser/download_interrupt_reasons.h"
-#include "net/base/file_stream.h"
+#include "crypto/sha2.h"
#include "net/base/net_errors.h"
#include "net/base/net_log.h"
#include "url/gurl.h"
@@ -22,9 +24,6 @@
namespace crypto {
class SecureHash;
}
-namespace net {
-class FileStream;
-}
namespace content {
@@ -40,7 +39,7 @@ class CONTENT_EXPORT BaseFile {
int64 received_bytes,
bool calculate_hash,
const std::string& hash_state,
- scoped_ptr<net::FileStream> file_stream,
+ base::File file,
const net::BoundNetLog& bound_net_log);
virtual ~BaseFile();
@@ -81,7 +80,7 @@ class CONTENT_EXPORT BaseFile {
DownloadInterruptReason AnnotateWithSourceInformation();
base::FilePath full_path() const { return full_path_; }
- bool in_progress() const { return file_stream_.get() != NULL; }
+ bool in_progress() const { return file_.IsValid(); }
int64 bytes_so_far() const { return bytes_so_far_; }
// Fills |hash| with the hash digest for the file.
@@ -92,8 +91,8 @@ class CONTENT_EXPORT BaseFile {
virtual std::string GetHashState();
// Returns true if the given hash is considered empty. An empty hash is
- // a string of size kSha256HashLen that contains only zeros (initial value
- // for the hash).
+ // 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;
@@ -102,17 +101,14 @@ class CONTENT_EXPORT BaseFile {
friend class BaseFileTest;
FRIEND_TEST_ALL_PREFIXES(BaseFileTest, IsEmptyHash);
- // Re-initializes file_stream_ with a newly allocated net::FileStream().
- void CreateFileStream();
-
- // Creates and opens the file_stream_ if it is NULL.
+ // Creates and opens the file_ if it is NULL.
DownloadInterruptReason Open();
- // Closes and resets file_stream_.
+ // Closes and resets file_.
void Close();
- // Resets file_stream_.
- void ClearStream();
+ // Resets file_.
+ void ClearFile();
// Platform specific method that moves a file to a new path and adjusts the
// security descriptor / permissions on the file to match the defaults for the
@@ -129,7 +125,8 @@ class CONTENT_EXPORT BaseFile {
// Log the system error in |os_error| and converts it into a
// |DownloadInterruptReason|.
- DownloadInterruptReason LogSystemError(const char* operation, int os_error);
+ DownloadInterruptReason LogSystemError(const char* operation,
+ logging::SystemErrorCode os_error);
// Log a TYPE_DOWNLOAD_FILE_ERROR NetLog event with |os_error| and |reason|.
// Returns |reason|.
@@ -137,8 +134,7 @@ class CONTENT_EXPORT BaseFile {
const char* operation, int os_error,
DownloadInterruptReason reason);
- static const size_t kSha256HashLen = 32;
- static const unsigned char kEmptySha256Hash[kSha256HashLen];
+ static const unsigned char kEmptySha256Hash[crypto::kSHA256Length];
// Full path to the file including the file name.
base::FilePath full_path_;
@@ -151,8 +147,8 @@ class CONTENT_EXPORT BaseFile {
std::string client_guid_;
- // OS file stream for writing
- scoped_ptr<net::FileStream> file_stream_;
+ // OS file for writing
+ base::File file_;
// Amount of data received up so far, in bytes.
int64 bytes_so_far_;
@@ -167,7 +163,7 @@ class CONTENT_EXPORT BaseFile {
// is set.
scoped_ptr<crypto::SecureHash> secure_hash_;
- unsigned char sha256_hash_[kSha256HashLen];
+ unsigned char sha256_hash_[crypto::kSHA256Length];
// Indicates that this class no longer owns the associated file, and so
// won't delete it on destruction.
diff --git a/chromium/content/browser/download/base_file_posix.cc b/chromium/content/browser/download/base_file_posix.cc
index dd664afeb59..4cf987c8b20 100644
--- a/chromium/content/browser/download/base_file_posix.cc
+++ b/chromium/content/browser/download/base_file_posix.cc
@@ -18,7 +18,7 @@ DownloadInterruptReason BaseFile::MoveFileAndAdjustPermissions(
// First check the file existence and create an empty file if it doesn't
// exist.
if (!base::PathExists(new_path)) {
- int write_error = file_util::WriteFile(new_path, "", 0);
+ int write_error = base::WriteFile(new_path, "", 0);
if (write_error < 0)
return LogSystemError("WriteFile", errno);
}
diff --git a/chromium/content/browser/download/base_file_unittest.cc b/chromium/content/browser/download/base_file_unittest.cc
index 4ab0c2ae8f6..8b45ce9ca0f 100644
--- a/chromium/content/browser/download/base_file_unittest.cc
+++ b/chromium/content/browser/download/base_file_unittest.cc
@@ -5,6 +5,7 @@
#include "content/browser/download/base_file.h"
#include "base/file_util.h"
+#include "base/files/file.h"
#include "base/files/scoped_temp_dir.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
@@ -13,8 +14,7 @@
#include "content/browser/browser_thread_impl.h"
#include "content/public/browser/download_interrupt_reasons.h"
#include "crypto/secure_hash.h"
-#include "net/base/file_stream.h"
-#include "net/base/mock_file_stream.h"
+#include "crypto/sha2.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
@@ -36,8 +36,7 @@ const base::TimeDelta kElapsedTimeDelta = base::TimeDelta::FromSeconds(
class BaseFileTest : public testing::Test {
public:
- static const size_t kSha256HashLen = 32;
- static const unsigned char kEmptySha256Hash[kSha256HashLen];
+ static const unsigned char kEmptySha256Hash[crypto::kSHA256Length];
BaseFileTest()
: expect_file_survives_(false),
@@ -55,7 +54,7 @@ class BaseFileTest : public testing::Test {
0,
false,
std::string(),
- scoped_ptr<net::FileStream>(),
+ base::File(),
net::BoundNetLog()));
}
@@ -84,7 +83,7 @@ class BaseFileTest : public testing::Test {
void ResetHash() {
secure_hash_.reset(crypto::SecureHash::Create(crypto::SecureHash::SHA256));
- memcpy(sha256_hash_, kEmptySha256Hash, kSha256HashLen);
+ memcpy(sha256_hash_, kEmptySha256Hash, crypto::kSHA256Length);
}
void UpdateHash(const char* data, size_t length) {
@@ -93,7 +92,7 @@ class BaseFileTest : public testing::Test {
std::string GetFinalHash() {
std::string hash;
- secure_hash_->Finish(sha256_hash_, kSha256HashLen);
+ secure_hash_->Finish(sha256_hash_, crypto::kSHA256Length);
hash.assign(reinterpret_cast<const char*>(sha256_hash_),
sizeof(sha256_hash_));
return hash;
@@ -106,7 +105,7 @@ class BaseFileTest : public testing::Test {
0,
true,
std::string(),
- scoped_ptr<net::FileStream>(),
+ base::File(),
net::BoundNetLog()));
}
@@ -146,7 +145,7 @@ class BaseFileTest : public testing::Test {
0,
false,
std::string(),
- scoped_ptr<net::FileStream>(),
+ base::File(),
net::BoundNetLog());
EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
@@ -172,7 +171,7 @@ class BaseFileTest : public testing::Test {
0,
false,
std::string(),
- scoped_ptr<net::FileStream>(),
+ base::File(),
net::BoundNetLog());
EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
duplicate_file.Initialize(temp_dir_.path()));
@@ -197,8 +196,6 @@ class BaseFileTest : public testing::Test {
}
protected:
- linked_ptr<net::testing::MockFileStream> mock_file_stream_;
-
// BaseClass instance we are testing.
scoped_ptr<BaseFile> base_file_;
@@ -214,7 +211,7 @@ class BaseFileTest : public testing::Test {
// Hash calculator.
scoped_ptr<crypto::SecureHash> secure_hash_;
- unsigned char sha256_hash_[kSha256HashLen];
+ unsigned char sha256_hash_[crypto::kSHA256Length];
private:
// Keep track of what data should be saved to the disk file.
@@ -401,7 +398,7 @@ TEST_F(BaseFileTest, MultipleWritesInterruptedWithHash) {
base_file_->bytes_so_far(),
true,
hash_state,
- scoped_ptr<net::FileStream>(),
+ base::File(),
net::BoundNetLog());
ASSERT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
second_file.Initialize(base::FilePath()));
@@ -479,41 +476,29 @@ TEST_F(BaseFileTest, RenameWithError) {
base_file_->Finish();
}
-// Write data to the file multiple times.
-TEST_F(BaseFileTest, MultipleWritesWithError) {
+// Test that a failed write reports an error.
+TEST_F(BaseFileTest, WriteWithError) {
base::FilePath path;
ASSERT_TRUE(base::CreateTemporaryFile(&path));
- // Create a new file stream. scoped_ptr takes ownership and passes it to
- // BaseFile; we use the pointer anyway and rely on the BaseFile not
- // deleting the MockFileStream until the BaseFile is reset.
- net::testing::MockFileStream* mock_file_stream(
- new net::testing::MockFileStream(NULL));
- scoped_ptr<net::FileStream> mock_file_stream_scoped_ptr(mock_file_stream);
-
- ASSERT_EQ(0,
- mock_file_stream->OpenSync(
- path,
- base::PLATFORM_FILE_OPEN_ALWAYS | base::PLATFORM_FILE_WRITE));
-
- // Copy of mock_file_stream; we pass ownership and rely on the BaseFile
- // not deleting it until it is reset.
-
- base_file_.reset(new BaseFile(mock_file_stream->get_path(),
+
+ // 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(),
- mock_file_stream_scoped_ptr.Pass(),
+ file.Pass(),
net::BoundNetLog()));
ASSERT_TRUE(InitializeFile());
- ASSERT_TRUE(AppendDataToFile(kTestData1));
- ASSERT_TRUE(AppendDataToFile(kTestData2));
- mock_file_stream->set_forced_error(net::ERR_ACCESS_DENIED);
+#if defined(OS_WIN)
set_expected_error(DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED);
- ASSERT_FALSE(AppendDataToFile(kTestData3));
- std::string hash;
- EXPECT_FALSE(base_file_->GetHash(&hash));
+#elif defined (OS_POSIX)
+ set_expected_error(DOWNLOAD_INTERRUPT_REASON_FILE_FAILED);
+#endif
+ ASSERT_FALSE(AppendDataToFile(kTestData1));
base_file_->Finish();
}
@@ -551,7 +536,7 @@ TEST_F(BaseFileTest, AppendToBaseFile) {
kTestDataLength4,
false,
std::string(),
- scoped_ptr<net::FileStream>(),
+ base::File(),
net::BoundNetLog()));
ASSERT_TRUE(InitializeFile());
@@ -585,7 +570,7 @@ TEST_F(BaseFileTest, ReadonlyBaseFile) {
0,
false,
std::string(),
- scoped_ptr<net::FileStream>(),
+ base::File(),
net::BoundNetLog()));
expect_in_progress_ = false;
@@ -605,11 +590,15 @@ TEST_F(BaseFileTest, ReadonlyBaseFile) {
}
TEST_F(BaseFileTest, IsEmptyHash) {
- std::string empty(BaseFile::kSha256HashLen, '\x00');
+ std::string empty(crypto::kSHA256Length, '\x00');
EXPECT_TRUE(BaseFile::IsEmptyHash(empty));
- std::string not_empty(BaseFile::kSha256HashLen, '\x01');
+ std::string not_empty(crypto::kSHA256Length, '\x01');
EXPECT_FALSE(BaseFile::IsEmptyHash(not_empty));
EXPECT_FALSE(BaseFile::IsEmptyHash(std::string()));
+
+ std::string also_not_empty = empty;
+ also_not_empty[crypto::kSHA256Length - 1] = '\x01';
+ EXPECT_FALSE(BaseFile::IsEmptyHash(also_not_empty));
}
// Test that a temporary file is created in the default download directory.
@@ -630,4 +619,21 @@ TEST_F(BaseFileTest, CreatedInDefaultDirectory) {
base_file_->Finish();
}
+TEST_F(BaseFileTest, NoDoubleDeleteAfterCancel) {
+ ASSERT_TRUE(InitializeFile());
+ base::FilePath full_path = base_file_->full_path();
+ ASSERT_FALSE(full_path.empty());
+ ASSERT_TRUE(base::PathExists(full_path));
+
+ base_file_->Cancel();
+ ASSERT_FALSE(base::PathExists(full_path));
+
+ const char kData[] = "hello";
+ const int kDataLength = static_cast<int>(arraysize(kData) - 1);
+ ASSERT_EQ(kDataLength, base::WriteFile(full_path, kData, kDataLength));
+ // The file that we created here should stick around when the BaseFile is
+ // destroyed during TearDown.
+ expect_file_survives_ = true;
+}
+
} // namespace content
diff --git a/chromium/content/browser/download/download_browsertest.cc b/chromium/content/browser/download/download_browsertest.cc
index 80c83e81bc2..8f35b63e576 100644
--- a/chromium/content/browser/download/download_browsertest.cc
+++ b/chromium/content/browser/download/download_browsertest.cc
@@ -11,6 +11,8 @@
#include "base/files/scoped_temp_dir.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/threading/platform_thread.h"
+#include "base/time/time.h"
#include "content/browser/byte_stream.h"
#include "content/browser/download/download_file_factory.h"
#include "content/browser/download/download_file_impl.h"
@@ -23,6 +25,8 @@
#include "content/public/common/content_switches.h"
#include "content/public/common/webplugininfo.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/download_test_observer.h"
#include "content/public/test/test_file_error_injector.h"
#include "content/public/test/test_utils.h"
@@ -30,22 +34,24 @@
#include "content/shell/browser/shell_browser_context.h"
#include "content/shell/browser/shell_download_manager_delegate.h"
#include "content/shell/browser/shell_network_delegate.h"
-#include "content/test/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
#include "content/test/net/url_request_mock_http_job.h"
#include "content/test/net/url_request_slow_download_job.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
#include "net/test/spawned_test_server/spawned_test_server.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
-using ::testing::_;
+using ::net::test_server::EmbeddedTestServer;
using ::testing::AllOf;
using ::testing::Field;
using ::testing::InSequence;
using ::testing::Property;
using ::testing::Return;
using ::testing::StrictMock;
+using ::testing::_;
namespace content {
@@ -185,8 +191,7 @@ DownloadFileWithDelay::DownloadFileWithDelay(
base::WeakPtr<DownloadFileWithDelayFactory> owner)
: DownloadFileImpl(
save_info.Pass(), default_download_directory, url, referrer_url,
- calculate_hash, stream.Pass(), bound_net_log,
- power_save_blocker.Pass(), observer),
+ calculate_hash, stream.Pass(), bound_net_log, observer),
owner_(owner) {}
DownloadFileWithDelay::~DownloadFileWithDelay() {}
@@ -281,8 +286,7 @@ class CountingDownloadFile : public DownloadFileImpl {
base::WeakPtr<DownloadDestinationObserver> observer)
: DownloadFileImpl(save_info.Pass(), default_downloads_directory,
url, referrer_url, calculate_hash,
- stream.Pass(), bound_net_log,
- power_save_blocker.Pass(), observer) {}
+ stream.Pass(), bound_net_log, observer) {}
virtual ~CountingDownloadFile() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
@@ -350,6 +354,7 @@ class TestShellDownloadManagerDelegate : public ShellDownloadManagerDelegate {
public:
TestShellDownloadManagerDelegate()
: delay_download_open_(false) {}
+ virtual ~TestShellDownloadManagerDelegate() {}
virtual bool ShouldOpenDownload(
DownloadItem* item,
@@ -370,8 +375,6 @@ class TestShellDownloadManagerDelegate : public ShellDownloadManagerDelegate {
callbacks->swap(delayed_callbacks_);
}
private:
- virtual ~TestShellDownloadManagerDelegate() {}
-
bool delay_download_open_;
std::vector<DownloadOpenDelayedCallback> delayed_callbacks_;
};
@@ -504,6 +507,54 @@ bool InitialSizeFilter(int* download_size, DownloadItem* download) {
return true;
}
+// Request handler to be used with CreateRedirectHandler().
+scoped_ptr<net::test_server::HttpResponse> HandleRequestAndSendRedirectResponse(
+ const std::string& relative_url,
+ const GURL& target_url,
+ const net::test_server::HttpRequest& request) {
+ scoped_ptr<net::test_server::BasicHttpResponse> response;
+ if (request.relative_url == relative_url) {
+ response.reset(new net::test_server::BasicHttpResponse);
+ response->set_code(net::HTTP_FOUND);
+ response->AddCustomHeader("Location", target_url.spec());
+ }
+ return response.PassAs<net::test_server::HttpResponse>();
+}
+
+// Creates a request handler for EmbeddedTestServer that responds with a HTTP
+// 302 redirect if the request URL matches |relative_url|.
+EmbeddedTestServer::HandleRequestCallback CreateRedirectHandler(
+ const std::string& relative_url,
+ const GURL& target_url) {
+ return base::Bind(
+ &HandleRequestAndSendRedirectResponse, relative_url, target_url);
+}
+
+// Request handler to be used with CreateBasicResponseHandler().
+scoped_ptr<net::test_server::HttpResponse> HandleRequestAndSendBasicResponse(
+ const std::string& relative_url,
+ const std::string& content_type,
+ const std::string& body,
+ const net::test_server::HttpRequest& request) {
+ scoped_ptr<net::test_server::BasicHttpResponse> response;
+ if (request.relative_url == relative_url) {
+ response.reset(new net::test_server::BasicHttpResponse);
+ response->set_content_type(content_type);
+ response->set_content(body);
+ }
+ return response.PassAs<net::test_server::HttpResponse>();
+}
+
+// Creates a request handler for an EmbeddedTestServer that response with an
+// HTTP 200 status code, a Content-Type header and a body.
+EmbeddedTestServer::HandleRequestCallback CreateBasicResponseHandler(
+ const std::string& relative_url,
+ const std::string& content_type,
+ const std::string& body) {
+ return base::Bind(
+ &HandleRequestAndSendBasicResponse, relative_url, content_type, body);
+}
+
} // namespace
class DownloadContentTest : public ContentBrowserTest {
@@ -522,12 +573,12 @@ class DownloadContentTest : public ContentBrowserTest {
virtual void SetUpOnMainThread() OVERRIDE {
ASSERT_TRUE(downloads_directory_.CreateUniqueTempDir());
- TestShellDownloadManagerDelegate* delegate =
- new TestShellDownloadManagerDelegate();
- delegate->SetDownloadBehaviorForTesting(downloads_directory_.path());
+ test_delegate_.reset(new TestShellDownloadManagerDelegate());
+ test_delegate_->SetDownloadBehaviorForTesting(downloads_directory_.path());
DownloadManager* manager = DownloadManagerForShell(shell());
- manager->SetDelegate(delegate);
- delegate->SetDownloadManager(manager);
+ manager->GetDelegate()->Shutdown();
+ manager->SetDelegate(test_delegate_.get());
+ test_delegate_->SetDownloadManager(manager);
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
@@ -538,10 +589,8 @@ class DownloadContentTest : public ContentBrowserTest {
base::Bind(&URLRequestMockHTTPJob::AddUrlHandler, mock_base));
}
- TestShellDownloadManagerDelegate* GetDownloadManagerDelegate(
- DownloadManager* manager) {
- return static_cast<TestShellDownloadManagerDelegate*>(
- manager->GetDelegate());
+ TestShellDownloadManagerDelegate* GetDownloadManagerDelegate() {
+ return test_delegate_.get();
}
// Create a DownloadTestObserverTerminal that will wait for the
@@ -585,8 +634,10 @@ class DownloadContentTest : public ContentBrowserTest {
(CountingDownloadFile::GetNumberActiveFilesFromFileThread() == 0);
}
- void DownloadAndWait(Shell* shell, const GURL& url,
- DownloadItem::DownloadState expected_terminal_state) {
+ void NavigateToURLAndWaitForDownload(
+ Shell* shell,
+ const GURL& url,
+ DownloadItem::DownloadState expected_terminal_state) {
scoped_ptr<DownloadTestObserver> observer(CreateWaiter(shell, 1));
NavigateToURL(shell, url);
observer->WaitForFinished();
@@ -699,6 +750,7 @@ class DownloadContentTest : public ContentBrowserTest {
// Location of the downloads directory for these tests
base::ScopedTempDir downloads_directory_;
+ scoped_ptr<TestShellDownloadManagerDelegate> test_delegate_;
};
IN_PROC_BROWSER_TEST_F(DownloadContentTest, DownloadCancelled) {
@@ -748,7 +800,7 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, MultiDownload) {
base::FilePath file(FILE_PATH_LITERAL("download-test.lib"));
GURL url(URLRequestMockHTTPJob::GetMockUrl(file));
// Download the file and wait.
- DownloadAndWait(shell(), url, DownloadItem::COMPLETE);
+ NavigateToURLAndWaitForDownload(shell(), url, DownloadItem::COMPLETE);
// Should now have 2 items on the manager.
downloads.clear();
@@ -802,7 +854,7 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, DownloadOctetStream) {
// The following is served with a Content-Type of application/octet-stream.
GURL url(URLRequestMockHTTPJob::GetMockUrl(base::FilePath(kTestFilePath)));
- DownloadAndWait(shell(), url, DownloadItem::COMPLETE);
+ NavigateToURLAndWaitForDownload(shell(), url, DownloadItem::COMPLETE);
}
#endif
@@ -857,7 +909,7 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, CancelAtRelease) {
DownloadManagerImpl* download_manager(DownloadManagerForShell(shell()));
// Mark delegate for delayed open.
- GetDownloadManagerDelegate(download_manager)->SetDelayedOpen(true);
+ GetDownloadManagerDelegate()->SetDelayedOpen(true);
// Setup new factory.
DownloadFileWithDelayFactory* file_factory =
@@ -898,7 +950,7 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, CancelAtRelease) {
// Need to complete open test.
std::vector<DownloadOpenDelayedCallback> delayed_callbacks;
- GetDownloadManagerDelegate(download_manager)->GetDelayedCallbacks(
+ GetDownloadManagerDelegate()->GetDelayedCallbacks(
&delayed_callbacks);
ASSERT_EQ(1u, delayed_callbacks.size());
delayed_callbacks[0].Run(true);
@@ -944,7 +996,20 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, ShutdownInProgress) {
EXPECT_CALL(item_observer, OnDownloadDestroyed(items[0]))
.WillOnce(Return());
}
+
+ // See http://crbug.com/324525. If we have a refcount release/post task
+ // race, the second post will stall the IO thread long enough so that we'll
+ // lose the race and crash. The first stall is just to give the UI thread
+ // a chance to get the second stall onto the IO thread queue after the cancel
+ // message created by Shutdown and before the notification callback
+ // created by the IO thread in canceling the request.
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+ base::Bind(&base::PlatformThread::Sleep,
+ base::TimeDelta::FromMilliseconds(25)));
DownloadManagerForShell(shell())->Shutdown();
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+ base::Bind(&base::PlatformThread::Sleep,
+ base::TimeDelta::FromMilliseconds(25)));
items.clear();
}
@@ -954,7 +1019,7 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, ShutdownAtRelease) {
DownloadManagerImpl* download_manager(DownloadManagerForShell(shell()));
// Mark delegate for delayed open.
- GetDownloadManagerDelegate(download_manager)->SetDelayedOpen(true);
+ GetDownloadManagerDelegate()->SetDelayedOpen(true);
// Setup new factory.
DownloadFileWithDelayFactory* file_factory =
@@ -1549,7 +1614,7 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, RemoveResumingDownload) {
// single threaded. The response to this download request should follow the
// response to the previous resumption request.
GURL url2(test_server()->GetURL("rangereset?size=100&rst_limit=0&token=x"));
- DownloadAndWait(shell(), url2, DownloadItem::COMPLETE);
+ NavigateToURLAndWaitForDownload(shell(), url2, DownloadItem::COMPLETE);
EXPECT_TRUE(EnsureNoPendingDownloads());
}
@@ -1597,7 +1662,7 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, CancelResumingDownload) {
// single threaded. The response to this download request should follow the
// response to the previous resumption request.
GURL url2(test_server()->GetURL("rangereset?size=100&rst_limit=0&token=x"));
- DownloadAndWait(shell(), url2, DownloadItem::COMPLETE);
+ NavigateToURLAndWaitForDownload(shell(), url2, DownloadItem::COMPLETE);
EXPECT_TRUE(EnsureNoPendingDownloads());
}
@@ -1640,4 +1705,110 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, CookiePolicy) {
GURL(download)));
}
+// A filename suggestion specified via a @download attribute should not be
+// effective if the final download URL is in another origin from the original
+// download URL.
+IN_PROC_BROWSER_TEST_F(DownloadContentTest,
+ DownloadAttributeCrossOriginRedirect) {
+ EmbeddedTestServer origin_one;
+ EmbeddedTestServer origin_two;
+ ASSERT_TRUE(origin_one.InitializeAndWaitUntilReady());
+ ASSERT_TRUE(origin_two.InitializeAndWaitUntilReady());
+
+ // The download-attribute.html page contains an anchor element whose href is
+ // set to the value of the query parameter (specified as |target| in the URL
+ // below). The suggested filename for the anchor is 'suggested-filename'. When
+ // the page is loaded, a script simulates a click on the anchor, triggering a
+ // download of the target URL.
+ //
+ // We construct two test servers; origin_one and origin_two. Once started, the
+ // server URLs will differ by the port number. Therefore they will be in
+ // different origins.
+ GURL download_url = origin_one.GetURL("/ping");
+ GURL referrer_url = origin_one.GetURL(
+ std::string("/download-attribute.html?target=") + download_url.spec());
+
+ // <origin_one>/download-attribute.html initiates a download of
+ // <origin_one>/ping, which redirects to <origin_two>/download.
+ origin_one.ServeFilesFromDirectory(GetTestFilePath("download", ""));
+ origin_one.RegisterRequestHandler(
+ CreateRedirectHandler("/ping", origin_two.GetURL("/download")));
+ origin_two.RegisterRequestHandler(CreateBasicResponseHandler(
+ "/download", "application/octet-stream", "Hello"));
+
+ NavigateToURLAndWaitForDownload(
+ shell(), referrer_url, DownloadItem::COMPLETE);
+
+ std::vector<DownloadItem*> downloads;
+ DownloadManagerForShell(shell())->GetAllDownloads(&downloads);
+ ASSERT_EQ(1u, downloads.size());
+
+ EXPECT_EQ(FILE_PATH_LITERAL("download"),
+ downloads[0]->GetTargetFilePath().BaseName().value());
+ ASSERT_TRUE(origin_one.ShutdownAndWaitUntilComplete());
+ ASSERT_TRUE(origin_two.ShutdownAndWaitUntilComplete());
+}
+
+// A filename suggestion specified via a @download attribute should be effective
+// if the final download URL is in the same origin as the initial download URL.
+// Test that this holds even if there are cross origin redirects in the middle
+// of the redirect chain.
+IN_PROC_BROWSER_TEST_F(DownloadContentTest,
+ DownloadAttributeSameOriginRedirect) {
+ EmbeddedTestServer origin_one;
+ EmbeddedTestServer origin_two;
+ ASSERT_TRUE(origin_one.InitializeAndWaitUntilReady());
+ ASSERT_TRUE(origin_two.InitializeAndWaitUntilReady());
+
+ // The download-attribute.html page contains an anchor element whose href is
+ // set to the value of the query parameter (specified as |target| in the URL
+ // below). The suggested filename for the anchor is 'suggested-filename'. When
+ // the page is loaded, a script simulates a click on the anchor, triggering a
+ // download of the target URL.
+ //
+ // We construct two test servers; origin_one and origin_two. Once started, the
+ // server URLs will differ by the port number. Therefore they will be in
+ // different origins.
+ GURL download_url = origin_one.GetURL("/ping");
+ GURL referrer_url = origin_one.GetURL(
+ std::string("/download-attribute.html?target=") + download_url.spec());
+ origin_one.ServeFilesFromDirectory(GetTestFilePath("download", ""));
+
+ // <origin_one>/download-attribute.html initiates a download of
+ // <origin_one>/ping, which redirects to <origin_two>/pong, and then finally
+ // to <origin_one>/download.
+ origin_one.RegisterRequestHandler(
+ CreateRedirectHandler("/ping", origin_two.GetURL("/pong")));
+ origin_two.RegisterRequestHandler(
+ CreateRedirectHandler("/pong", origin_one.GetURL("/download")));
+ origin_one.RegisterRequestHandler(CreateBasicResponseHandler(
+ "/download", "application/octet-stream", "Hello"));
+
+ NavigateToURLAndWaitForDownload(
+ shell(), referrer_url, DownloadItem::COMPLETE);
+
+ std::vector<DownloadItem*> downloads;
+ DownloadManagerForShell(shell())->GetAllDownloads(&downloads);
+ ASSERT_EQ(1u, downloads.size());
+
+ EXPECT_EQ(FILE_PATH_LITERAL("suggested-filename"),
+ downloads[0]->GetTargetFilePath().BaseName().value());
+ ASSERT_TRUE(origin_one.ShutdownAndWaitUntilComplete());
+ ASSERT_TRUE(origin_two.ShutdownAndWaitUntilComplete());
+}
+
+// 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.
+IN_PROC_BROWSER_TEST_F(DownloadContentTest, DownloadGZipWithNoContent) {
+ EmbeddedTestServer test_server;
+ ASSERT_TRUE(test_server.InitializeAndWaitUntilReady());
+
+ GURL url = test_server.GetURL("/empty.bin");
+ test_server.ServeFilesFromDirectory(GetTestFilePath("download", ""));
+
+ NavigateToURLAndWaitForDownload(shell(), url, DownloadItem::COMPLETE);
+ // That's it. This should work without crashing.
+}
+
} // namespace content
diff --git a/chromium/content/browser/download/download_create_info.h b/chromium/content/browser/download/download_create_info.h
index e032fbf293d..e8e1443b93a 100644
--- a/chromium/content/browser/download/download_create_info.h
+++ b/chromium/content/browser/download/download_create_info.h
@@ -45,6 +45,12 @@ struct CONTENT_EXPORT DownloadCreateInfo {
// The URL that referred us.
GURL referrer_url;
+ // The URL of the tab that started us.
+ GURL tab_url;
+
+ // The referrer URL of the tab that started us.
+ GURL tab_referrer_url;
+
// The time when the download started.
base::Time start_time;
diff --git a/chromium/content/browser/download/download_file_factory.cc b/chromium/content/browser/download/download_file_factory.cc
index a7fe7b834a9..817bee11180 100644
--- a/chromium/content/browser/download/download_file_factory.cc
+++ b/chromium/content/browser/download/download_file_factory.cc
@@ -5,7 +5,6 @@
#include "content/browser/download/download_file_factory.h"
#include "content/browser/download/download_file_impl.h"
-#include "content/public/browser/power_save_blocker.h"
namespace content {
@@ -20,14 +19,9 @@ DownloadFile* DownloadFileFactory::CreateFile(
scoped_ptr<ByteStreamReader> stream,
const net::BoundNetLog& bound_net_log,
base::WeakPtr<DownloadDestinationObserver> observer) {
- scoped_ptr<PowerSaveBlocker> psb(
- PowerSaveBlocker::Create(
- PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension,
- "Download in progress"));
return new DownloadFileImpl(
save_info.Pass(), default_downloads_directory, url, referrer_url,
- calculate_hash, stream.Pass(), bound_net_log,
- psb.Pass(), observer);
+ calculate_hash, stream.Pass(), bound_net_log, observer);
}
} // namespace content
diff --git a/chromium/content/browser/download/download_file_impl.cc b/chromium/content/browser/download/download_file_impl.cc
index 67bee81b67a..11e98cb726b 100644
--- a/chromium/content/browser/download/download_file_impl.cc
+++ b/chromium/content/browser/download/download_file_impl.cc
@@ -18,7 +18,6 @@
#include "content/browser/download/download_stats.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/download_destination_observer.h"
-#include "content/public/browser/power_save_blocker.h"
#include "net/base/io_buffer.h"
namespace content {
@@ -36,7 +35,6 @@ DownloadFileImpl::DownloadFileImpl(
bool calculate_hash,
scoped_ptr<ByteStreamReader> stream,
const net::BoundNetLog& bound_net_log,
- scoped_ptr<PowerSaveBlocker> power_save_blocker,
base::WeakPtr<DownloadDestinationObserver> observer)
: file_(save_info->file_path,
url,
@@ -44,15 +42,14 @@ DownloadFileImpl::DownloadFileImpl(
save_info->offset,
calculate_hash,
save_info->hash_state,
- save_info->file_stream.Pass(),
+ save_info->file.Pass(),
bound_net_log),
default_download_directory_(default_download_directory),
stream_reader_(stream.Pass()),
bytes_seen_(0),
bound_net_log_(bound_net_log),
observer_(observer),
- weak_factory_(this),
- power_save_blocker_(power_save_blocker.Pass()) {
+ weak_factory_(this) {
}
DownloadFileImpl::~DownloadFileImpl() {
@@ -110,7 +107,7 @@ void DownloadFileImpl::RenameAndUniquify(
base::FilePath new_path(full_path);
- int uniquifier = file_util::GetUniquePathNumber(
+ int uniquifier = base::GetUniquePathNumber(
new_path, base::FilePath::StringType());
if (uniquifier > 0) {
new_path = new_path.InsertBeforeExtensionASCII(
@@ -297,7 +294,7 @@ void DownloadFileImpl::StreamActive() {
&DownloadDestinationObserver::DestinationCompleted,
observer_, hash));
}
- if (bound_net_log_.IsLoggingAllEvents()) {
+ if (bound_net_log_.IsLogging()) {
bound_net_log_.AddEvent(
net::NetLog::TYPE_DOWNLOAD_STREAM_DRAINED,
base::Bind(&FileStreamDrainedNetLogCallback, total_incoming_data_size,
diff --git a/chromium/content/browser/download/download_file_impl.h b/chromium/content/browser/download/download_file_impl.h
index 9a50870fb2a..2ba3d8d058e 100644
--- a/chromium/content/browser/download/download_file_impl.h
+++ b/chromium/content/browser/download/download_file_impl.h
@@ -22,7 +22,6 @@ namespace content {
class ByteStreamReader;
class DownloadDestinationObserver;
class DownloadManager;
-class PowerSaveBlocker;
struct DownloadCreateInfo;
class CONTENT_EXPORT DownloadFileImpl : virtual public DownloadFile {
@@ -43,7 +42,6 @@ class CONTENT_EXPORT DownloadFileImpl : virtual public DownloadFile {
bool calculate_hash,
scoped_ptr<ByteStreamReader> stream,
const net::BoundNetLog& bound_net_log,
- scoped_ptr<PowerSaveBlocker> power_save_blocker,
base::WeakPtr<DownloadDestinationObserver> observer);
virtual ~DownloadFileImpl();
@@ -105,9 +103,6 @@ class CONTENT_EXPORT DownloadFileImpl : virtual public DownloadFile {
base::WeakPtrFactory<DownloadFileImpl> weak_factory_;
- // RAII handle to keep the system from sleeping while we're downloading.
- scoped_ptr<PowerSaveBlocker> power_save_blocker_;
-
DISALLOW_COPY_AND_ASSIGN(DownloadFileImpl);
};
diff --git a/chromium/content/browser/download/download_file_unittest.cc b/chromium/content/browser/download/download_file_unittest.cc
index 141808685f7..1127908c260 100644
--- a/chromium/content/browser/download/download_file_unittest.cc
+++ b/chromium/content/browser/download/download_file_unittest.cc
@@ -14,7 +14,6 @@
#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/browser/power_save_blocker.h"
#include "content/public/test/mock_download_manager.h"
#include "net/base/file_stream.h"
#include "net/base/mock_file_stream.h"
@@ -116,7 +115,7 @@ class DownloadFileTest : public testing::Test {
*reason_p = reason;
}
- virtual bool CreateDownloadFile(int offset, bool calculate_hash) {
+ bool CreateDownloadFile(int offset, bool calculate_hash) {
// There can be only one.
DCHECK(!download_file_.get());
@@ -137,7 +136,6 @@ class DownloadFileTest : public testing::Test {
calculate_hash,
scoped_ptr<ByteStreamReader>(input_stream_),
net::BoundNetLog(),
- scoped_ptr<PowerSaveBlocker>().Pass(),
observer_factory_.GetWeakPtr()));
download_file_->SetClientGuid(
"12345678-ABCD-1234-DCBA-123456789ABC");
@@ -159,7 +157,7 @@ class DownloadFileTest : public testing::Test {
return result == DOWNLOAD_INTERRUPT_REASON_NONE;
}
- virtual void DestroyDownloadFile(int offset) {
+ void DestroyDownloadFile(int offset) {
EXPECT_FALSE(download_file_->InProgress());
// Make sure the data has been properly written to disk.
@@ -414,7 +412,7 @@ TEST_F(DownloadFileTest, RenameFileFinal) {
ASSERT_FALSE(base::PathExists(path_5));
static const char file_data[] = "xyzzy";
ASSERT_EQ(static_cast<int>(sizeof(file_data) - 1),
- file_util::WriteFile(path_5, file_data, sizeof(file_data) - 1));
+ base::WriteFile(path_5, file_data, sizeof(file_data) - 1));
ASSERT_TRUE(base::PathExists(path_5));
EXPECT_TRUE(base::ReadFileToString(path_5, &file_contents));
EXPECT_EQ(std::string(file_data), file_contents);
@@ -442,7 +440,7 @@ TEST_F(DownloadFileTest, RenameUniquifies) {
ASSERT_FALSE(base::PathExists(path_1));
static const char file_data[] = "xyzzy";
ASSERT_EQ(static_cast<int>(sizeof(file_data)),
- file_util::WriteFile(path_1, file_data, sizeof(file_data)));
+ base::WriteFile(path_1, file_data, sizeof(file_data)));
ASSERT_TRUE(base::PathExists(path_1));
EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, RenameAndUniquify(path_1, NULL));
diff --git a/chromium/content/browser/download/download_interrupt_reasons_impl.cc b/chromium/content/browser/download/download_interrupt_reasons_impl.cc
index beb56494e87..9a01f484f1d 100644
--- a/chromium/content/browser/download/download_interrupt_reasons_impl.cc
+++ b/chromium/content/browser/download/download_interrupt_reasons_impl.cc
@@ -88,7 +88,7 @@ DownloadInterruptReason ConvertNetErrorToInterruptReason(
return DOWNLOAD_INTERRUPT_REASON_NONE;
}
-std::string InterruptReasonDebugString(DownloadInterruptReason error) {
+std::string DownloadInterruptReasonToString(DownloadInterruptReason error) {
#define INTERRUPT_REASON(name, value) \
case DOWNLOAD_INTERRUPT_REASON_##name: return #name;
diff --git a/chromium/content/browser/download/download_item_factory.h b/chromium/content/browser/download/download_item_factory.h
index c5e03f5c41d..d4198cb62e1 100644
--- a/chromium/content/browser/download/download_item_factory.h
+++ b/chromium/content/browser/download/download_item_factory.h
@@ -44,6 +44,8 @@ public:
const base::FilePath& target_path,
const std::vector<GURL>& url_chain,
const GURL& referrer_url,
+ const std::string& mime_type,
+ const std::string& original_mime_type,
const base::Time& start_time,
const base::Time& end_time,
const std::string& etag,
diff --git a/chromium/content/browser/download/download_item_impl.cc b/chromium/content/browser/download/download_item_impl.cc
index b8f78ff56ef..433ac78f48b 100644
--- a/chromium/content/browser/download/download_item_impl.cc
+++ b/chromium/content/browser/download/download_item_impl.cc
@@ -57,12 +57,23 @@ namespace content {
namespace {
-void DeleteDownloadedFile(const base::FilePath& path) {
+bool DeleteDownloadedFile(const base::FilePath& path) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
// Make sure we only delete files.
- if (!base::DirectoryExists(path))
- base::DeleteFile(path, false);
+ if (base::DirectoryExists(path))
+ return true;
+ return base::DeleteFile(path, false);
+}
+
+void DeleteDownloadedFileDone(
+ base::WeakPtr<DownloadItemImpl> item,
+ const base::Callback<void(bool)>& callback,
+ bool success) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (success && item.get())
+ item->OnDownloadedFileRemoved();
+ callback.Run(success);
}
// Wrapper around DownloadFile::Detach and DownloadFile::Cancel that
@@ -102,6 +113,8 @@ DownloadItemImpl::DownloadItemImpl(DownloadItemImplDelegate* delegate,
const base::FilePath& target_path,
const std::vector<GURL>& url_chain,
const GURL& referrer_url,
+ const std::string& mime_type,
+ const std::string& original_mime_type,
const base::Time& start_time,
const base::Time& end_time,
const std::string& etag,
@@ -122,6 +135,8 @@ DownloadItemImpl::DownloadItemImpl(DownloadItemImplDelegate* delegate,
referrer_url_(referrer_url),
transition_type_(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),
@@ -164,7 +179,9 @@ DownloadItemImpl::DownloadItemImpl(
TARGET_DISPOSITION_PROMPT : TARGET_DISPOSITION_OVERWRITE),
url_chain_(info.url_chain),
referrer_url_(info.referrer_url),
- suggested_filename_(UTF16ToUTF8(info.save_info->suggested_name)),
+ tab_url_(info.tab_url),
+ tab_referrer_url_(info.tab_referrer_url),
+ suggested_filename_(base::UTF16ToUTF8(info.save_info->suggested_name)),
forced_file_path_(info.save_info->file_path),
transition_type_(info.transition_type),
has_user_gesture_(info.has_user_gesture),
@@ -407,8 +424,9 @@ void DownloadItemImpl::Cancel(bool user_cancel) {
// 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(&DeleteDownloadedFile, current_path_));
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(base::IgnoreResult(&DeleteDownloadedFile), current_path_));
current_path_.clear();
}
@@ -538,6 +556,14 @@ const GURL& DownloadItemImpl::GetReferrerUrl() const {
return referrer_url_;
}
+const GURL& DownloadItemImpl::GetTabUrl() const {
+ return tab_url_;
+}
+
+const GURL& DownloadItemImpl::GetTabReferrerUrl() const {
+ return tab_referrer_url_;
+}
+
std::string DownloadItemImpl::GetSuggestedFilename() const {
return suggested_filename_;
}
@@ -614,18 +640,29 @@ bool DownloadItemImpl::GetFileExternallyRemoved() const {
return file_externally_removed_;
}
-void DownloadItemImpl::DeleteFile() {
+void DownloadItemImpl::DeleteFile(const base::Callback<void(bool)>& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- if ((GetState() != DownloadItem::COMPLETE) ||
- file_externally_removed_) {
+ if (GetState() != DownloadItem::COMPLETE) {
+ // Pass a null WeakPtr so it doesn't call OnDownloadedFileRemoved.
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&DeleteDownloadedFileDone,
+ base::WeakPtr<DownloadItemImpl>(), callback, false));
+ return;
+ }
+ if (current_path_.empty() || file_externally_removed_) {
+ // Pass a null WeakPtr so it doesn't call OnDownloadedFileRemoved.
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&DeleteDownloadedFileDone,
+ base::WeakPtr<DownloadItemImpl>(), callback, true));
return;
}
- BrowserThread::PostTaskAndReply(
+ BrowserThread::PostTaskAndReplyWithResult(
BrowserThread::FILE, FROM_HERE,
base::Bind(&DeleteDownloadedFile, current_path_),
- base::Bind(&DownloadItemImpl::OnDownloadedFileRemoved,
- weak_ptr_factory_.GetWeakPtr()));
- current_path_.clear();
+ base::Bind(&DeleteDownloadedFileDone,
+ weak_ptr_factory_.GetWeakPtr(), callback));
}
bool DownloadItemImpl::IsDangerous() const {
@@ -805,7 +842,7 @@ std::string DownloadItemImpl::DebugString(bool verbose) const {
" target_path = \"%" PRFilePath "\"",
GetTotalBytes(),
GetReceivedBytes(),
- InterruptReasonDebugString(last_reason_).c_str(),
+ DownloadInterruptReasonToString(last_reason_).c_str(),
IsPaused() ? 'T' : 'F',
DebugResumeModeString(GetResumeMode()),
auto_resume_count_,
@@ -866,6 +903,7 @@ DownloadItemImpl::ResumeMode DownloadItemImpl::GetResumeMode() const {
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_USER_SHUTDOWN:
case DOWNLOAD_INTERRUPT_REASON_CRASH:
@@ -1014,7 +1052,7 @@ void DownloadItemImpl::DestinationUpdate(int64 bytes_so_far,
if (received_bytes_ > total_bytes_)
total_bytes_ = 0;
- if (bound_net_log_.IsLoggingAllEvents()) {
+ if (bound_net_log_.IsLogging()) {
bound_net_log_.AddEvent(
net::NetLog::TYPE_DOWNLOAD_ITEM_UPDATED,
net::NetLog::Int64Callback("bytes_so_far", received_bytes_));
@@ -1378,23 +1416,20 @@ void DownloadItemImpl::Completed() {
}
}
-void DownloadItemImpl::OnResumeRequestStarted(DownloadItem* item,
- net::Error error) {
+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(net::OK, error);
+ DCHECK_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason);
DCHECK_EQ(static_cast<DownloadItem*>(this), item);
return;
}
// Otherwise, the request failed without passing through
// DownloadResourceHandler::OnResponseStarted.
- if (error == net::OK)
- error = net::ERR_FAILED;
- DownloadInterruptReason reason =
- ConvertNetErrorToInterruptReason(error, DOWNLOAD_INTERRUPT_FROM_NETWORK);
- DCHECK_NE(DOWNLOAD_INTERRUPT_REASON_NONE, reason);
- Interrupt(reason);
+ DCHECK_NE(DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason);
+ Interrupt(interrupt_reason);
}
// **** End of Download progression cascade
@@ -1402,6 +1437,7 @@ void DownloadItemImpl::OnResumeRequestStarted(DownloadItem* item,
// An error occurred somewhere.
void DownloadItemImpl::Interrupt(DownloadInterruptReason reason) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_NE(DOWNLOAD_INTERRUPT_REASON_NONE, reason);
// Somewhat counter-intuitively, it is possible for us to receive an
// interrupt after we've already been interrupted. The generation of
@@ -1421,11 +1457,15 @@ void DownloadItemImpl::Interrupt(DownloadInterruptReason reason) {
ResumeMode resume_mode = GetResumeMode();
if (state_ == IN_PROGRESS_INTERNAL) {
- // Cancel (delete file) if we're going to restart; no point in leaving
- // data around we aren't going to use. Also cancel if resumption isn't
- // enabled for the same reason.
+ // 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());
// Cancel the originating URL request.
diff --git a/chromium/content/browser/download/download_item_impl.h b/chromium/content/browser/download/download_item_impl.h
index 804afa671b7..34916ff6fd9 100644
--- a/chromium/content/browser/download/download_item_impl.h
+++ b/chromium/content/browser/download/download_item_impl.h
@@ -19,8 +19,8 @@
#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/base/net_errors.h"
#include "net/base/net_log.h"
#include "url/gurl.h"
@@ -56,6 +56,8 @@ class CONTENT_EXPORT DownloadItemImpl
const base::FilePath& target_path,
const std::vector<GURL>& url_chain,
const GURL& referrer_url,
+ const std::string& mime_type,
+ const std::string& original_mime_type,
const base::Time& start_time,
const base::Time& end_time,
const std::string& etag,
@@ -111,6 +113,8 @@ class CONTENT_EXPORT DownloadItemImpl
virtual const std::vector<GURL>& GetUrlChain() const OVERRIDE;
virtual const GURL& GetOriginalUrl() const OVERRIDE;
virtual const GURL& GetReferrerUrl() const OVERRIDE;
+ virtual const GURL& GetTabUrl() const OVERRIDE;
+ virtual const GURL& GetTabReferrerUrl() const OVERRIDE;
virtual std::string GetSuggestedFilename() const OVERRIDE;
virtual std::string GetContentDisposition() const OVERRIDE;
virtual std::string GetMimeType() const OVERRIDE;
@@ -129,7 +133,7 @@ class CONTENT_EXPORT DownloadItemImpl
virtual const std::string& GetHash() const OVERRIDE;
virtual const std::string& GetHashState() const OVERRIDE;
virtual bool GetFileExternallyRemoved() const OVERRIDE;
- virtual void DeleteFile() OVERRIDE;
+ virtual void DeleteFile(const base::Callback<void(bool)>& callback) OVERRIDE;
virtual bool IsDangerous() const OVERRIDE;
virtual DownloadDangerType GetDangerType() const OVERRIDE;
virtual bool TimeRemaining(base::TimeDelta* remaining) const OVERRIDE;
@@ -340,7 +344,8 @@ class CONTENT_EXPORT DownloadItemImpl
void Completed();
// Callback invoked when the URLRequest for a download resumption has started.
- void OnResumeRequestStarted(DownloadItem* item, net::Error error);
+ void OnResumeRequestStarted(DownloadItem* item,
+ DownloadInterruptReason interrupt_reason);
// Helper routines -----------------------------------------------------------
@@ -416,6 +421,12 @@ class CONTENT_EXPORT DownloadItemImpl
// The URL of the page that initiated the download.
GURL referrer_url_;
+ // The URL of the tab that initiated the download.
+ GURL tab_url_;
+
+ // The URL of the referrer of the tab that initiated the download.
+ GURL tab_referrer_url_;
+
// Filename suggestion from DownloadSaveInfo. It could, among others, be the
// suggested filename in 'download' attribute of an anchor. Details:
// http://www.whatwg.org/specs/web-apps/current-work/#downloading-hyperlinks
diff --git a/chromium/content/browser/download/download_item_impl_unittest.cc b/chromium/content/browser/download/download_item_impl_unittest.cc
index 9abe3417e7e..1291ce11350 100644
--- a/chromium/content/browser/download/download_item_impl_unittest.cc
+++ b/chromium/content/browser/download/download_item_impl_unittest.cc
@@ -459,6 +459,37 @@ TEST_F(DownloadItemTest, RestartAfterInterrupted) {
CleanupItem(item, download_file, DownloadItem::INTERRUPTED);
}
+// Check we do correct cleanup for RESUME_MODE_INVALID interrupts.
+TEST_F(DownloadItemTest, UnresumableInterrupt) {
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kEnableDownloadResumption);
+
+ DownloadItemImpl* item = CreateDownloadItem();
+ MockObserver observer(item);
+ DownloadItemImplDelegate::DownloadTargetCallback callback;
+ MockDownloadFile* download_file =
+ DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS);
+
+ // 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, Cancel());
+
+ // Complete download to trigger final rename.
+ item->DestinationObserverAsWeakPtr()->DestinationCompleted(std::string());
+ RunAllPendingInMessageLoops();
+
+ ASSERT_TRUE(observer.CheckUpdated());
+ // Should not try to auto-resume.
+ ASSERT_EQ(1, observer.GetInterruptCount());
+ ASSERT_EQ(0, observer.GetResumeCount());
+
+ CleanupItem(item, download_file, DownloadItem::INTERRUPTED);
+}
+
TEST_F(DownloadItemTest, LimitRestartsAfterInterrupted) {
CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kEnableDownloadResumption);
diff --git a/chromium/content/browser/download/download_manager_impl.cc b/chromium/content/browser/download/download_manager_impl.cc
index 4f2e68a908f..2bd747d5595 100644
--- a/chromium/content/browser/download/download_manager_impl.cc
+++ b/chromium/content/browser/download/download_manager_impl.cc
@@ -56,7 +56,7 @@ void BeginDownload(scoped_ptr<DownloadUrlParameters> params,
// we must down cast. RDHI is the only subclass of RDH as of 2012 May 4.
scoped_ptr<net::URLRequest> request(
params->resource_context()->GetRequestContext()->CreateRequest(
- params->url(), net::DEFAULT_PRIORITY, NULL));
+ params->url(), net::DEFAULT_PRIORITY, NULL, NULL));
request->SetLoadFlags(request->load_flags() | params->load_flags());
request->set_method(params->method());
if (!params->post_body().empty()) {
@@ -118,7 +118,7 @@ void BeginDownload(scoped_ptr<DownloadUrlParameters> params,
save_info->offset = params->offset();
save_info->hash_state = params->hash_state();
save_info->prompt_for_save_location = params->prompt();
- save_info->file_stream = params->GetFileStream();
+ save_info->file = params->GetFile();
ResourceDispatcherHost::Get()->BeginDownload(
request.Pass(),
@@ -169,6 +169,8 @@ class DownloadItemFactoryImpl : public DownloadItemFactory {
const base::FilePath& target_path,
const std::vector<GURL>& url_chain,
const GURL& referrer_url,
+ const std::string& mime_type,
+ const std::string& original_mime_type,
const base::Time& start_time,
const base::Time& end_time,
const std::string& etag,
@@ -187,6 +189,8 @@ class DownloadItemFactoryImpl : public DownloadItemFactory {
target_path,
url_chain,
referrer_url,
+ mime_type,
+ original_mime_type,
start_time,
end_time,
etag,
@@ -393,7 +397,7 @@ void DownloadManagerImpl::StartDownloadWithId(
// while resuming, then also ignore the request.
info->request_handle.CancelRequest();
if (!on_started.is_null())
- on_started.Run(NULL, net::ERR_ABORTED);
+ on_started.Run(NULL, DOWNLOAD_INTERRUPT_REASON_USER_CANCELED);
return;
}
download = item_iterator->second;
@@ -437,7 +441,7 @@ void DownloadManagerImpl::StartDownloadWithId(
FOR_EACH_OBSERVER(Observer, observers_, OnDownloadCreated(this, download));
if (!on_started.is_null())
- on_started.Run(download, net::OK);
+ on_started.Run(download, DOWNLOAD_INTERRUPT_REASON_NONE);
}
void DownloadManagerImpl::CheckForHistoryFilesRemoval() {
@@ -619,6 +623,8 @@ DownloadItem* DownloadManagerImpl::CreateDownloadItem(
const base::FilePath& target_path,
const std::vector<GURL>& url_chain,
const GURL& referrer_url,
+ const std::string& mime_type,
+ const std::string& original_mime_type,
const base::Time& start_time,
const base::Time& end_time,
const std::string& etag,
@@ -640,6 +646,8 @@ DownloadItem* DownloadManagerImpl::CreateDownloadItem(
target_path,
url_chain,
referrer_url,
+ mime_type,
+ original_mime_type,
start_time,
end_time,
etag,
diff --git a/chromium/content/browser/download/download_manager_impl.h b/chromium/content/browser/download/download_manager_impl.h
index 6381417832e..630c83252a8 100644
--- a/chromium/content/browser/download/download_manager_impl.h
+++ b/chromium/content/browser/download/download_manager_impl.h
@@ -7,6 +7,7 @@
#include <map>
#include <set>
+#include <string>
#include "base/containers/hash_tables.h"
#include "base/memory/ref_counted.h"
@@ -78,6 +79,8 @@ class CONTENT_EXPORT DownloadManagerImpl : public DownloadManager,
const base::FilePath& target_path,
const std::vector<GURL>& url_chain,
const GURL& referrer_url,
+ const std::string& mime_type,
+ const std::string& original_mime_type,
const base::Time& start_time,
const base::Time& end_time,
const std::string& etag,
diff --git a/chromium/content/browser/download/download_manager_impl_unittest.cc b/chromium/content/browser/download/download_manager_impl_unittest.cc
index 3afc41ad5af..66430b4443a 100644
--- a/chromium/content/browser/download/download_manager_impl_unittest.cc
+++ b/chromium/content/browser/download/download_manager_impl_unittest.cc
@@ -76,6 +76,8 @@ class MockDownloadItemImpl : public DownloadItemImpl {
base::FilePath(),
std::vector<GURL>(),
GURL(),
+ "application/octet-stream",
+ "application/octet-stream",
base::Time(),
base::Time(),
std::string(),
@@ -132,6 +134,8 @@ class MockDownloadItemImpl : public DownloadItemImpl {
MOCK_CONST_METHOD0(GetURL, const GURL&());
MOCK_CONST_METHOD0(GetOriginalUrl, const GURL&());
MOCK_CONST_METHOD0(GetReferrerUrl, const GURL&());
+ MOCK_CONST_METHOD0(GetTabUrl, const GURL&());
+ MOCK_CONST_METHOD0(GetTabReferrerUrl, const GURL&());
MOCK_CONST_METHOD0(GetSuggestedFilename, std::string());
MOCK_CONST_METHOD0(GetContentDisposition, std::string());
MOCK_CONST_METHOD0(GetMimeType, std::string());
@@ -232,6 +236,8 @@ class MockDownloadItemFactory
const base::FilePath& target_path,
const std::vector<GURL>& url_chain,
const GURL& referrer_url,
+ const std::string& mime_type,
+ const std::string& original_mime_type,
const base::Time& start_time,
const base::Time& end_time,
const std::string& etag,
@@ -297,6 +303,8 @@ DownloadItemImpl* MockDownloadItemFactory::CreatePersistedItem(
const base::FilePath& target_path,
const std::vector<GURL>& url_chain,
const GURL& referrer_url,
+ const std::string& mime_type,
+ const std::string& original_mime_type,
const base::Time& start_time,
const base::Time& end_time,
const std::string& etag,
@@ -404,22 +412,11 @@ class MockBrowserContext : public BrowserContext {
MOCK_METHOD2(GetMediaRequestContextForStoragePartition,
net::URLRequestContextGetter*(
const base::FilePath& partition_path, bool in_memory));
- MOCK_METHOD5(RequestMIDISysExPermission,
- void(int render_process_id,
- int render_view_id,
- int bridge_id,
- const GURL& requesting_frame,
- const MIDISysExPermissionCallback& callback));
- MOCK_METHOD4(CancelMIDISysExPermissionRequest,
- void(int render_process_id,
- int render_view_id,
- int bridge_id,
- const GURL& requesting_frame));
MOCK_METHOD0(GetResourceContext, ResourceContext*());
MOCK_METHOD0(GetDownloadManagerDelegate, DownloadManagerDelegate*());
- MOCK_METHOD0(GetGeolocationPermissionContext,
- GeolocationPermissionContext* ());
+ MOCK_METHOD0(GetGuestManager, BrowserPluginGuestManager* ());
MOCK_METHOD0(GetSpecialStoragePolicy, quota::SpecialStoragePolicy*());
+ MOCK_METHOD0(GetPushMessagingService, PushMessagingService*());
};
class MockDownloadManagerObserver : public DownloadManager::Observer {
diff --git a/chromium/content/browser/download/download_net_log_parameters.cc b/chromium/content/browser/download/download_net_log_parameters.cc
index f464298d73e..45321362273 100644
--- a/chromium/content/browser/download/download_net_log_parameters.cc
+++ b/chromium/content/browser/download/download_net_log_parameters.cc
@@ -90,7 +90,7 @@ base::Value* ItemInterruptedNetLogCallback(DownloadInterruptReason reason,
net::NetLog::LogLevel log_level) {
base::DictionaryValue* dict = new base::DictionaryValue();
- dict->SetString("interrupt_reason", InterruptReasonDebugString(reason));
+ 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()));
@@ -106,7 +106,7 @@ base::Value* ItemResumingNetLogCallback(bool user_initiated,
base::DictionaryValue* dict = new base::DictionaryValue();
dict->SetString("user_initiated", user_initiated ? "true" : "false");
- dict->SetString("interrupt_reason", InterruptReasonDebugString(reason));
+ 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()));
@@ -200,7 +200,7 @@ base::Value* FileInterruptedNetLogCallback(const char* operation,
dict->SetString("operation", operation);
if (os_error != 0)
dict->SetInteger("os_error", os_error);
- dict->SetString("interrupt_reason", InterruptReasonDebugString(reason));
+ dict->SetString("interrupt_reason", DownloadInterruptReasonToString(reason));
return dict;
}
diff --git a/chromium/content/browser/download/download_resource_handler.cc b/chromium/content/browser/download/download_resource_handler.cc
index 9f00ef660b5..0a19163c9c3 100644
--- a/chromium/content/browser/download/download_resource_handler.cc
+++ b/chromium/content/browser/download/download_resource_handler.cc
@@ -24,6 +24,9 @@
#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/web_contents.h"
#include "content/public/common/resource_response.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
@@ -32,23 +35,30 @@
#include "net/url_request/url_request_context.h"
namespace content {
+
+struct DownloadResourceHandler::DownloadTabInfo {
+ GURL tab_url;
+ GURL tab_referrer_url;
+};
+
namespace {
void CallStartedCBOnUIThread(
const DownloadUrlParameters::OnStartedCallback& started_cb,
DownloadItem* item,
- net::Error error) {
+ DownloadInterruptReason interrupt_reason) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (started_cb.is_null())
return;
- started_cb.Run(item, error);
+ started_cb.Run(item, interrupt_reason);
}
// Static function in order to prevent any accidental accesses to
// DownloadResourceHandler members from the UI thread.
static void StartOnUIThread(
scoped_ptr<DownloadCreateInfo> info,
+ DownloadResourceHandler::DownloadTabInfo* tab_info,
scoped_ptr<ByteStreamReader> stream,
const DownloadUrlParameters::OnStartedCallback& started_cb) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
@@ -58,13 +68,35 @@ 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, net::ERR_ACCESS_DENIED);
+ 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.
+
return;
}
+ info->tab_url = tab_info->tab_url;
+ info->tab_referrer_url = tab_info->tab_referrer_url;
+
download_manager->StartDownload(info.Pass(), stream.Pass(), started_cb);
}
+void InitializeDownloadTabInfoOnUIThread(
+ const DownloadRequestHandle& request_handle,
+ DownloadResourceHandler::DownloadTabInfo* tab_info) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ WebContents* web_contents = request_handle.GetWebContents();
+ if (web_contents) {
+ NavigationEntry* entry = web_contents->GetController().GetVisibleEntry();
+ if (entry) {
+ tab_info->tab_url = entry->GetURL();
+ tab_info->tab_referrer_url = entry->GetReferrer().url;
+ }
+ }
+}
+
} // namespace
const int DownloadResourceHandler::kDownloadByteStreamSize = 100 * 1024;
@@ -84,16 +116,31 @@ DownloadResourceHandler::DownloadResourceHandler(
was_deferred_(false),
on_response_started_called_(false) {
RecordDownloadCount(UNTHROTTLED_COUNT);
+
+ // Do UI thread initialization asap after DownloadResourceHandler creation
+ // since the tab could be navigated before StartOnUIThread gets called.
+ const ResourceRequestInfoImpl* request_info = GetRequestInfo();
+ tab_info_ = new DownloadTabInfo();
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&InitializeDownloadTabInfoOnUIThread,
+ DownloadRequestHandle(AsWeakPtr(),
+ request_info->GetChildID(),
+ request_info->GetRouteID(),
+ request_info->GetRequestID()),
+ tab_info_));
+ power_save_blocker_ = PowerSaveBlocker::Create(
+ PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension,
+ "Download in progress");
}
-bool DownloadResourceHandler::OnUploadProgress(int request_id,
- uint64 position,
+bool DownloadResourceHandler::OnUploadProgress(uint64 position,
uint64 size) {
return true;
}
bool DownloadResourceHandler::OnRequestRedirected(
- int request_id,
const GURL& url,
ResourceResponse* response,
bool* defer) {
@@ -105,7 +152,6 @@ bool DownloadResourceHandler::OnRequestRedirected(
// Send the download creation information to the download thread.
bool DownloadResourceHandler::OnResponseStarted(
- int request_id,
ResourceResponse* response,
bool* defer) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
@@ -113,8 +159,7 @@ bool DownloadResourceHandler::OnResponseStarted(
DCHECK(!on_response_started_called_);
on_response_started_called_ = true;
- VLOG(20) << __FUNCTION__ << "()" << DebugString()
- << " request_id = " << request_id;
+ VLOG(20) << __FUNCTION__ << "()" << DebugString();
download_start_time_ = base::TimeTicks::Now();
// If it's a download, we don't want to poison the cache with it.
@@ -190,15 +235,26 @@ bool DownloadResourceHandler::OnResponseStarted(
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
+ // 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();
+
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&StartOnUIThread,
base::Passed(&info),
+ base::Owned(tab_info_),
base::Passed(&stream_reader),
// Pass to StartOnUIThread so that variable
// access is always on IO thread but function
// is called on UI thread.
started_cb_));
+ // Now owned by the task that was just posted.
+ tab_info_ = NULL;
// Guaranteed to be called in StartOnUIThread
started_cb_.Reset();
@@ -206,26 +262,31 @@ bool DownloadResourceHandler::OnResponseStarted(
}
void DownloadResourceHandler::CallStartedCB(
- DownloadItem* item, net::Error error) {
+ DownloadItem* item,
+ DownloadInterruptReason interrupt_reason) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (started_cb_.is_null())
return;
BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- base::Bind(&CallStartedCBOnUIThread, started_cb_, item, error));
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(
+ &CallStartedCBOnUIThread, started_cb_, item, interrupt_reason));
started_cb_.Reset();
}
-bool DownloadResourceHandler::OnWillStart(int request_id,
- const GURL& url,
- bool* defer) {
+bool DownloadResourceHandler::OnWillStart(const GURL& url, bool* defer) {
+ return true;
+}
+
+bool DownloadResourceHandler::OnBeforeNetworkStart(const GURL& url,
+ bool* defer) {
return true;
}
// Create a new buffer, which will be handed to the download thread for file
// writing and deletion.
-bool DownloadResourceHandler::OnWillRead(int request_id,
- scoped_refptr<net::IOBuffer>* buf,
+bool DownloadResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf,
int* buf_size,
int min_size) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
@@ -240,8 +301,7 @@ bool DownloadResourceHandler::OnWillRead(int request_id,
}
// Pass the buffer to the download file writer.
-bool DownloadResourceHandler::OnReadCompleted(int request_id, int bytes_read,
- bool* defer) {
+bool DownloadResourceHandler::OnReadCompleted(int bytes_read, bool* defer) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(read_buffer_.get());
@@ -281,14 +341,12 @@ bool DownloadResourceHandler::OnReadCompleted(int request_id, int bytes_read,
}
void DownloadResourceHandler::OnResponseCompleted(
- int request_id,
const net::URLRequestStatus& status,
const std::string& security_info,
bool* defer) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
int response_code = status.is_success() ? request()->GetResponseCode() : 0;
VLOG(20) << __FUNCTION__ << "()" << DebugString()
- << " request_id = " << request_id
<< " status.status() = " << status.status()
<< " status.error() = " << status.error()
<< " response_code = " << response_code;
@@ -378,7 +436,7 @@ void DownloadResourceHandler::OnResponseCompleted(
RecordNetworkBlockage(base::TimeTicks::Now() - download_start_time_,
total_pause_time_);
- CallStartedCB(NULL, error_code);
+ CallStartedCB(NULL, reason);
// Send the info down the stream. Conditional is in case we get
// OnResponseCompleted without OnResponseStarted.
@@ -397,9 +455,7 @@ void DownloadResourceHandler::OnResponseCompleted(
read_buffer_ = NULL;
}
-void DownloadResourceHandler::OnDataDownloaded(
- int request_id,
- int bytes_downloaded) {
+void DownloadResourceHandler::OnDataDownloaded(int bytes_downloaded) {
NOTREACHED();
}
@@ -435,8 +491,8 @@ void DownloadResourceHandler::CancelRequest() {
const ResourceRequestInfo* info = GetRequestInfo();
ResourceDispatcherHostImpl::Get()->CancelRequest(
info->GetChildID(),
- info->GetRequestID(),
- false);
+ info->GetRequestID());
+ // This object has been deleted.
}
std::string DownloadResourceHandler::DebugString() const {
@@ -463,12 +519,17 @@ 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(NULL, net::ERR_ACCESS_DENIED);
+ CallStartedCB(NULL, DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED);
// Remove output stream callback if a stream exists.
if (stream_writer_)
stream_writer_->RegisterCallback(base::Closure());
+ // tab_info_ must be destroyed on UI thread, since
+ // InitializeDownloadTabInfoOnUIThread might still be using it.
+ if (tab_info_)
+ BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, tab_info_);
+
UMA_HISTOGRAM_TIMES("SB2.DownloadDuration",
base::TimeTicks::Now() - download_start_time_);
}
diff --git a/chromium/content/browser/download/download_resource_handler.h b/chromium/content/browser/download/download_resource_handler.h
index 36bb0d27005..de6efef2fe6 100644
--- a/chromium/content/browser/download/download_resource_handler.h
+++ b/chromium/content/browser/download/download_resource_handler.h
@@ -11,21 +11,20 @@
#include "base/memory/scoped_ptr.h"
#include "base/timer/timer.h"
#include "content/browser/loader/resource_handler.h"
+#include "content/public/browser/download_interrupt_reasons.h"
#include "content/public/browser/download_manager.h"
#include "content/public/browser/download_save_info.h"
#include "content/public/browser/download_url_parameters.h"
-#include "content/public/browser/global_request_id.h"
-#include "net/base/net_errors.h"
-
namespace net {
class URLRequest;
} // namespace net
namespace content {
-class ByteStreamWriter;
class ByteStreamReader;
+class ByteStreamWriter;
class DownloadRequestHandle;
+class PowerSaveBlocker;
struct DownloadCreateInfo;
// Forwards data to the download thread.
@@ -33,6 +32,8 @@ class CONTENT_EXPORT DownloadResourceHandler
: public ResourceHandler,
public base::SupportsWeakPtr<DownloadResourceHandler> {
public:
+ struct DownloadTabInfo;
+
// Size of the buffer used between the DownloadResourceHandler and the
// downstream receiver of its output.
static const int kDownloadByteStreamSize;
@@ -45,45 +46,41 @@ class CONTENT_EXPORT DownloadResourceHandler
const DownloadUrlParameters::OnStartedCallback& started_cb,
scoped_ptr<DownloadSaveInfo> save_info);
- virtual bool OnUploadProgress(int request_id,
- uint64 position,
- uint64 size) OVERRIDE;
+ virtual bool OnUploadProgress(uint64 position, uint64 size) OVERRIDE;
- virtual bool OnRequestRedirected(int request_id,
- const GURL& url,
+ virtual bool OnRequestRedirected(const GURL& url,
ResourceResponse* response,
bool* defer) OVERRIDE;
// Send the download creation information to the download thread.
- virtual bool OnResponseStarted(int request_id,
- ResourceResponse* response,
+ virtual bool OnResponseStarted(ResourceResponse* response,
bool* defer) OVERRIDE;
// Pass-through implementation.
- virtual bool OnWillStart(int request_id,
- const GURL& url,
- bool* defer) OVERRIDE;
+ virtual bool OnWillStart(const GURL& url, bool* defer) OVERRIDE;
+
+ // Pass-through implementation.
+ virtual bool OnBeforeNetworkStart(const GURL& url, bool* defer) OVERRIDE;
// Create a new buffer, which will be handed to the download thread for file
// writing and deletion.
- virtual bool OnWillRead(int request_id,
- scoped_refptr<net::IOBuffer>* buf,
+ virtual bool OnWillRead(scoped_refptr<net::IOBuffer>* buf,
int* buf_size,
int min_size) OVERRIDE;
- virtual bool OnReadCompleted(int request_id, int bytes_read,
- bool* defer) OVERRIDE;
+ virtual bool OnReadCompleted(int bytes_read, bool* defer) OVERRIDE;
- virtual void OnResponseCompleted(int request_id,
- const net::URLRequestStatus& status,
+ virtual void OnResponseCompleted(const net::URLRequestStatus& status,
const std::string& security_info,
bool* defer) OVERRIDE;
// N/A to this flavor of DownloadHandler.
- virtual void OnDataDownloaded(int request_id, int bytes_downloaded) OVERRIDE;
+ virtual void OnDataDownloaded(int bytes_downloaded) OVERRIDE;
void PauseRequest();
void ResumeRequest();
+
+ // May result in this object being deleted by its owner.
void CancelRequest();
std::string DebugString() const;
@@ -94,7 +91,8 @@ class CONTENT_EXPORT DownloadResourceHandler
// 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(DownloadItem* item, net::Error error);
+ void CallStartedCB(DownloadItem* item,
+ DownloadInterruptReason interrupt_reason);
uint32 download_id_;
// This is read only on the IO thread, but may only
@@ -102,10 +100,21 @@ class CONTENT_EXPORT DownloadResourceHandler
DownloadUrlParameters::OnStartedCallback started_cb_;
scoped_ptr<DownloadSaveInfo> save_info_;
+ // Stores information about the download that must be acquired on the UI
+ // thread before StartOnUIThread is called.
+ // Created on IO thread, but only accessed on UI thread. |tab_info_| holds
+ // the pointer until we pass it off to StartOnUIThread or DeleteSoon.
+ DownloadTabInfo* tab_info_;
+
// Data flow
scoped_refptr<net::IOBuffer> read_buffer_; // From URLRequest.
scoped_ptr<ByteStreamWriter> stream_writer_; // To rest of system.
+ // Keeps the system from sleeping while this ResourceHandler is alive. If the
+ // system enters power saving mode while a request is alive, it can cause the
+ // request to fail and the associated download will be interrupted.
+ scoped_ptr<PowerSaveBlocker> power_save_blocker_;
+
// The following are used to collect stats.
base::TimeTicks download_start_time_;
base::TimeTicks last_read_time_;
diff --git a/chromium/content/browser/download/drag_download_file.cc b/chromium/content/browser/download/drag_download_file.cc
index d81b04ccf6b..29b8b5caa5c 100644
--- a/chromium/content/browser/download/drag_download_file.cc
+++ b/chromium/content/browser/download/drag_download_file.cc
@@ -5,6 +5,7 @@
#include "content/browser/download/drag_download_file.h"
#include "base/bind.h"
+#include "base/files/file.h"
#include "base/message_loop/message_loop.h"
#include "content/browser/download/download_stats.h"
#include "content/browser/web_contents/web_contents_impl.h"
@@ -13,7 +14,6 @@
#include "content/public/browser/download_item.h"
#include "content/public/browser/download_save_info.h"
#include "content/public/browser/download_url_parameters.h"
-#include "net/base/file_stream.h"
namespace content {
@@ -53,7 +53,7 @@ class DragDownloadFile::DragDownloadFileUI : public DownloadItem::Observer {
// Do not call weak_ptr_factory_.GetWeakPtr() outside the UI thread.
}
- void InitiateDownload(scoped_ptr<net::FileStream> file_stream,
+ void InitiateDownload(base::File file,
const base::FilePath& file_path) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DownloadManager* download_manager =
@@ -67,7 +67,7 @@ class DragDownloadFile::DragDownloadFileUI : public DownloadItem::Observer {
params->set_callback(base::Bind(&DragDownloadFileUI::OnDownloadStarted,
weak_ptr_factory_.GetWeakPtr()));
params->set_file_path(file_path);
- params->set_file_stream(file_stream.Pass()); // Nulls file_stream.
+ params->set_file(file.Pass()); // Nulls file.
download_manager->DownloadUrl(params.Pass());
}
@@ -89,14 +89,15 @@ class DragDownloadFile::DragDownloadFileUI : public DownloadItem::Observer {
download_item_->RemoveObserver(this);
}
- void OnDownloadStarted(DownloadItem* item, net::Error error) {
+ void OnDownloadStarted(DownloadItem* item,
+ DownloadInterruptReason interrupt_reason) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (!item) {
- DCHECK_NE(net::OK, error);
+ DCHECK_NE(DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason);
on_completed_loop_->PostTask(FROM_HERE, base::Bind(on_completed_, false));
return;
}
- DCHECK_EQ(net::OK, error);
+ DCHECK_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason);
download_item_ = item;
download_item_->AddObserver(this);
}
@@ -149,13 +150,13 @@ class DragDownloadFile::DragDownloadFileUI : public DownloadItem::Observer {
};
DragDownloadFile::DragDownloadFile(const base::FilePath& file_path,
- scoped_ptr<net::FileStream> file_stream,
+ base::File file,
const GURL& url,
const Referrer& referrer,
const std::string& referrer_encoding,
WebContents* web_contents)
: file_path_(file_path),
- file_stream_(file_stream.Pass()),
+ file_(file.Pass()),
drag_message_loop_(base::MessageLoop::current()),
state_(INITIALIZED),
drag_ui_(NULL),
@@ -196,7 +197,7 @@ void DragDownloadFile::Start(ui::DownloadFileObserver* observer) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
&DragDownloadFileUI::InitiateDownload, base::Unretained(drag_ui_),
- base::Passed(&file_stream_), file_path_));
+ base::Passed(&file_), file_path_));
}
bool DragDownloadFile::Wait() {
diff --git a/chromium/content/browser/download/drag_download_file.h b/chromium/content/browser/download/drag_download_file.h
index c281d977d87..62e4ec2aa28 100644
--- a/chromium/content/browser/download/drag_download_file.h
+++ b/chromium/content/browser/download/drag_download_file.h
@@ -6,6 +6,7 @@
#define CONTENT_BROWSER_DOWNLOAD_DRAG_DOWNLOAD_FILE_H_
#include "base/compiler_specific.h"
+#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
@@ -15,14 +16,9 @@
#include "content/public/browser/download_item.h"
#include "content/public/browser/download_manager.h"
#include "content/public/common/referrer.h"
-#include "net/base/file_stream.h"
#include "ui/base/dragdrop/download_file_interface.h"
#include "url/gurl.h"
-namespace net {
-class FileStream;
-}
-
namespace content {
class DownloadManager;
@@ -35,11 +31,11 @@ class WebContents;
class CONTENT_EXPORT DragDownloadFile : public ui::DownloadFileProvider {
public:
// On Windows, we need to download into a temporary file. On posix, we need to
- // download into a file stream that has already been created, so only the UI
- // thread is involved. |file_stream| must be null on windows but non-null on
+ // download into a file that has already been created, so only the UI
+ // thread is involved. |file| must be null on windows but non-null on
// posix systems. |file_path| is an absolute path on all systems.
DragDownloadFile(const base::FilePath& file_path,
- scoped_ptr<net::FileStream> file_stream,
+ base::File file,
const GURL& url,
const Referrer& referrer,
const std::string& referrer_encoding,
@@ -60,7 +56,7 @@ class CONTENT_EXPORT DragDownloadFile : public ui::DownloadFileProvider {
void CheckThread();
base::FilePath file_path_;
- scoped_ptr<net::FileStream> file_stream_;
+ base::File file_;
base::MessageLoop* drag_message_loop_;
State state_;
scoped_refptr<ui::DownloadFileObserver> observer_;
diff --git a/chromium/content/browser/download/drag_download_file_browsertest.cc b/chromium/content/browser/download/drag_download_file_browsertest.cc
index 5ea0e576af4..136fac3cf32 100644
--- a/chromium/content/browser/download/drag_download_file_browsertest.cc
+++ b/chromium/content/browser/download/drag_download_file_browsertest.cc
@@ -14,13 +14,13 @@
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/power_save_blocker.h"
#include "content/public/common/content_client.h"
+#include "content/public/test/content_browser_test.h"
+#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/download_test_observer.h"
#include "content/public/test/test_utils.h"
#include "content/shell/browser/shell.h"
#include "content/shell/browser/shell_browser_context.h"
#include "content/shell/browser/shell_download_manager_delegate.h"
-#include "content/test/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
#include "content/test/net/url_request_mock_http_job.h"
#include "content/test/net/url_request_slow_download_job.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -95,7 +95,7 @@ IN_PROC_BROWSER_TEST_F(DragDownloadFileTest, DragDownloadFileTest_NetError) {
Referrer referrer;
std::string referrer_encoding;
scoped_refptr<DragDownloadFile> file(
- new DragDownloadFile(name, scoped_ptr<net::FileStream>(), url, referrer,
+ new DragDownloadFile(name, base::File(), url, referrer,
referrer_encoding, shell()->web_contents()));
scoped_refptr<MockDownloadFileObserver> observer(
new MockDownloadFileObserver());
@@ -114,10 +114,9 @@ IN_PROC_BROWSER_TEST_F(DragDownloadFileTest, DragDownloadFileTest_Complete) {
"download-test.lib"))));
Referrer referrer;
std::string referrer_encoding;
- net::FileStream* stream = NULL;
SetUpServer();
scoped_refptr<DragDownloadFile> file(new DragDownloadFile(
- name, scoped_ptr<net::FileStream>(stream), url, referrer,
+ name, base::File(), url, referrer,
referrer_encoding, shell()->web_contents()));
scoped_refptr<MockDownloadFileObserver> observer(
new MockDownloadFileObserver());
diff --git a/chromium/content/browser/download/drag_download_util.cc b/chromium/content/browser/download/drag_download_util.cc
index 0850c3ba081..f259af22c6c 100644
--- a/chromium/content/browser/download/drag_download_util.cc
+++ b/chromium/content/browser/download/drag_download_util.cc
@@ -7,7 +7,7 @@
#include <string>
#include "base/bind.h"
-#include "base/file_util.h"
+#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/string_number_conversions.h"
@@ -15,19 +15,15 @@
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "content/public/browser/browser_thread.h"
-#include "net/base/file_stream.h"
-#include "net/base/net_errors.h"
#include "url/gurl.h"
-using net::FileStream;
-
namespace content {
bool ParseDownloadMetadata(const base::string16& metadata,
base::string16* mime_type,
base::FilePath* file_name,
GURL* url) {
- const char16 separator = L':';
+ const base::char16 separator = L':';
size_t mime_type_end_pos = metadata.find(separator);
if (mime_type_end_pos == base::string16::npos)
@@ -49,7 +45,7 @@ bool ParseDownloadMetadata(const base::string16& metadata,
#if defined(OS_WIN)
*file_name = base::FilePath(file_name_str);
#else
- *file_name = base::FilePath(UTF16ToUTF8(file_name_str));
+ *file_name = base::FilePath(base::UTF16ToUTF8(file_name_str));
#endif
}
if (url)
@@ -58,11 +54,9 @@ bool ParseDownloadMetadata(const base::string16& metadata,
return true;
}
-FileStream* CreateFileStreamForDrop(base::FilePath* file_path,
- net::NetLog* net_log) {
+base::File CreateFileForDrop(base::FilePath* file_path) {
DCHECK(file_path && !file_path->empty());
- scoped_ptr<FileStream> file_stream(new FileStream(net_log));
const int kMaxSeq = 99;
for (int seq = 0; seq <= kMaxSeq; seq++) {
base::FilePath new_file_path;
@@ -70,7 +64,8 @@ FileStream* CreateFileStreamForDrop(base::FilePath* file_path,
new_file_path = *file_path;
} else {
#if defined(OS_WIN)
- base::string16 suffix = ASCIIToUTF16("-") + base::IntToString16(seq);
+ base::string16 suffix =
+ base::ASCIIToUTF16("-") + base::IntToString16(seq);
#else
std::string suffix = std::string("-") + base::IntToString(seq);
#endif
@@ -80,17 +75,15 @@ FileStream* CreateFileStreamForDrop(base::FilePath* file_path,
// http://crbug.com/110709
base::ThreadRestrictions::ScopedAllowIO allow_io;
- // Explicitly (and redundantly check) for file -- despite the fact that our
- // open won't overwrite -- just to avoid log spew.
- if (!base::PathExists(new_file_path) &&
- file_stream->OpenSync(new_file_path, base::PLATFORM_FILE_CREATE |
- base::PLATFORM_FILE_WRITE) == net::OK) {
+ base::File file(
+ new_file_path, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
+ if (file.IsValid()) {
*file_path = new_file_path;
- return file_stream.release();
+ return file.Pass();
}
}
- return NULL;
+ return base::File();
}
PromiseFileFinalizer::PromiseFileFinalizer(
diff --git a/chromium/content/browser/download/drag_download_util.h b/chromium/content/browser/download/drag_download_util.h
index ca266efa622..76b4e61068c 100644
--- a/chromium/content/browser/download/drag_download_util.h
+++ b/chromium/content/browser/download/drag_download_util.h
@@ -6,6 +6,7 @@
#define CONTENT_BROWSER_DOWNLOAD_DRAG_DOWNLOAD_UTIL_H_
#include "base/basictypes.h"
+#include "base/files/file.h"
#include "base/memory/ref_counted.h"
#include "base/strings/string16.h"
#include "content/browser/download/drag_download_file.h"
@@ -17,10 +18,6 @@ namespace base {
class FilePath;
}
-namespace net {
-class FileStream;
-}
-
namespace content {
// Parse the download metadata set in DataTransfer.setData. The metadata
@@ -39,10 +36,8 @@ bool ParseDownloadMetadata(const base::string16& metadata,
// Create a new file at the specified path. If the file already exists, try to
// insert the sequential unifier to produce a new file, like foo-01.txt.
-// Return a FileStream if successful.
-// |net_log| is a NetLog for the stream.
-CONTENT_EXPORT net::FileStream* CreateFileStreamForDrop(
- base::FilePath* file_path, net::NetLog* net_log);
+// Return a File if successful.
+CONTENT_EXPORT base::File CreateFileForDrop(base::FilePath* file_path);
// Implementation of DownloadFileObserver to finalize the download process.
class PromiseFileFinalizer : public ui::DownloadFileObserver {
diff --git a/chromium/content/browser/download/mhtml_generation_browsertest.cc b/chromium/content/browser/download/mhtml_generation_browsertest.cc
index 11a08519e2d..283e53ac1e0 100644
--- a/chromium/content/browser/download/mhtml_generation_browsertest.cc
+++ b/chromium/content/browser/download/mhtml_generation_browsertest.cc
@@ -8,10 +8,10 @@
#include "base/files/scoped_temp_dir.h"
#include "base/run_loop.h"
#include "content/public/browser/web_contents.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/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromium/content/browser/download/mhtml_generation_manager.cc b/chromium/content/browser/download/mhtml_generation_manager.cc
index 7d7d4bb2645..db4ba30533f 100644
--- a/chromium/content/browser/download/mhtml_generation_manager.cc
+++ b/chromium/content/browser/download/mhtml_generation_manager.cc
@@ -5,25 +5,86 @@
#include "content/browser/download/mhtml_generation_manager.h"
#include "base/bind.h"
-#include "base/platform_file.h"
-#include "content/browser/renderer_host/render_process_host_impl.h"
+#include "base/files/file.h"
+#include "base/stl_util.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/notification_service.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_process_host_observer.h"
#include "content/public/browser/web_contents.h"
#include "content/common/view_messages.h"
-#include "content/public/browser/notification_types.h"
namespace content {
+class MHTMLGenerationManager::Job : public RenderProcessHostObserver {
+ public:
+ Job();
+ virtual ~Job();
+
+ void SetWebContents(WebContents* web_contents);
+
+ base::File browser_file() { return browser_file_.Pass(); }
+ void set_browser_file(base::File file) { browser_file_ = file.Pass(); }
+
+ int process_id() { return process_id_; }
+ int routing_id() { return routing_id_; }
+
+ GenerateMHTMLCallback callback() { return callback_; }
+ void set_callback(GenerateMHTMLCallback callback) { callback_ = callback; }
+
+ // RenderProcessHostObserver:
+ virtual void RenderProcessExited(RenderProcessHost* host,
+ base::ProcessHandle handle,
+ base::TerminationStatus status,
+ int exit_code) OVERRIDE;
+ virtual void RenderProcessHostDestroyed(RenderProcessHost* host) OVERRIDE;
+
+
+ private:
+ // The handle to the file the MHTML is saved to for the browser process.
+ base::File browser_file_;
+
+ // The IDs mapping to a specific contents.
+ int process_id_;
+ int routing_id_;
+
+ // The callback to call once generation is complete.
+ GenerateMHTMLCallback callback_;
+
+ // The RenderProcessHost being observed, or NULL if none is.
+ RenderProcessHost* host_;
+ DISALLOW_COPY_AND_ASSIGN(Job);
+};
+
MHTMLGenerationManager::Job::Job()
- : browser_file(base::kInvalidPlatformFileValue),
- renderer_file(IPC::InvalidPlatformFileForTransit()),
- process_id(-1),
- routing_id(-1) {
+ : process_id_(-1),
+ routing_id_(-1),
+ host_(NULL) {
}
MHTMLGenerationManager::Job::~Job() {
+ if (host_)
+ host_->RemoveObserver(this);
+}
+
+void MHTMLGenerationManager::Job::SetWebContents(WebContents* web_contents) {
+ process_id_ = web_contents->GetRenderProcessHost()->GetID();
+ routing_id_ = web_contents->GetRenderViewHost()->GetRoutingID();
+ host_ = web_contents->GetRenderProcessHost();
+ host_->AddObserver(this);
+}
+
+void MHTMLGenerationManager::Job::RenderProcessExited(
+ RenderProcessHost* host,
+ base::ProcessHandle handle,
+ base::TerminationStatus status,
+ int exit_code) {
+ MHTMLGenerationManager::GetInstance()->RenderProcessExited(this);
+}
+
+void MHTMLGenerationManager::Job::RenderProcessHostDestroyed(
+ RenderProcessHost* host) {
+ host_ = NULL;
}
MHTMLGenerationManager* MHTMLGenerationManager::GetInstance() {
@@ -34,6 +95,7 @@ MHTMLGenerationManager::MHTMLGenerationManager() {
}
MHTMLGenerationManager::~MHTMLGenerationManager() {
+ STLDeleteValues(&id_to_job_);
}
void MHTMLGenerationManager::SaveMHTML(WebContents* web_contents,
@@ -52,7 +114,7 @@ void MHTMLGenerationManager::SaveMHTML(WebContents* web_contents,
void MHTMLGenerationManager::StreamMHTML(
WebContents* web_contents,
- const base::PlatformFile browser_file,
+ base::File browser_file,
const GenerateMHTMLCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
@@ -61,9 +123,10 @@ void MHTMLGenerationManager::StreamMHTML(
base::ProcessHandle renderer_process =
web_contents->GetRenderProcessHost()->GetHandle();
IPC::PlatformFileForTransit renderer_file =
- IPC::GetFileHandleForProcess(browser_file, renderer_process, false);
+ IPC::GetFileHandleForProcess(browser_file.GetPlatformFile(),
+ renderer_process, false);
- FileHandleAvailable(job_id, browser_file, renderer_file);
+ FileAvailable(job_id, browser_file.Pass(), renderer_file);
}
@@ -75,32 +138,33 @@ void MHTMLGenerationManager::CreateFile(
int job_id, const base::FilePath& file_path,
base::ProcessHandle renderer_process) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
- base::PlatformFile browser_file = base::CreatePlatformFile(file_path,
- base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE,
- NULL, NULL);
- if (browser_file == base::kInvalidPlatformFileValue) {
+ base::File browser_file(
+ file_path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+ if (!browser_file.IsValid()) {
LOG(ERROR) << "Failed to create file to save MHTML at: " <<
file_path.value();
}
IPC::PlatformFileForTransit renderer_file =
- IPC::GetFileHandleForProcess(browser_file, renderer_process, false);
+ IPC::GetFileHandleForProcess(browser_file.GetPlatformFile(),
+ renderer_process, false);
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
- base::Bind(&MHTMLGenerationManager::FileHandleAvailable,
+ base::Bind(&MHTMLGenerationManager::FileAvailable,
base::Unretained(this),
job_id,
- browser_file,
+ base::Passed(&browser_file),
renderer_file));
}
-void MHTMLGenerationManager::FileHandleAvailable(int job_id,
- base::PlatformFile browser_file,
+void MHTMLGenerationManager::FileAvailable(
+ int job_id,
+ base::File browser_file,
IPC::PlatformFileForTransit renderer_file) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- if (browser_file == base::kInvalidPlatformFileValue) {
+ if (!browser_file.IsValid()) {
LOG(ERROR) << "Failed to create file";
JobFinished(job_id, -1);
return;
@@ -112,12 +176,11 @@ void MHTMLGenerationManager::FileHandleAvailable(int job_id,
return;
}
- Job& job = iter->second;
- job.browser_file = browser_file;
- job.renderer_file = renderer_file;
+ Job* job = iter->second;
+ job->set_browser_file(browser_file.Pass());
- RenderViewHostImpl* rvh = RenderViewHostImpl::FromID(
- job.process_id, job.routing_id);
+ RenderViewHost* rvh = RenderViewHost::FromID(
+ job->process_id(), job->routing_id());
if (!rvh) {
// The contents went away.
JobFinished(job_id, -1);
@@ -129,67 +192,50 @@ void MHTMLGenerationManager::FileHandleAvailable(int job_id,
}
void MHTMLGenerationManager::JobFinished(int job_id, int64 file_size) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
IDToJobMap::iterator iter = id_to_job_.find(job_id);
if (iter == id_to_job_.end()) {
NOTREACHED();
return;
}
- Job& job = iter->second;
- job.callback.Run(file_size);
+ Job* job = iter->second;
+ job->callback().Run(file_size);
BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
base::Bind(&MHTMLGenerationManager::CloseFile, base::Unretained(this),
- job.browser_file));
+ base::Passed(job->browser_file())));
id_to_job_.erase(job_id);
+ delete job;
}
-void MHTMLGenerationManager::CloseFile(base::PlatformFile file) {
+void MHTMLGenerationManager::CloseFile(base::File file) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
- base::ClosePlatformFile(file);
+ file.Close();
}
int MHTMLGenerationManager::NewJob(WebContents* web_contents,
const GenerateMHTMLCallback& callback) {
static int id_counter = 0;
int job_id = id_counter++;
- Job& job = id_to_job_[job_id];
- job.process_id = web_contents->GetRenderProcessHost()->GetID();
- job.routing_id = web_contents->GetRenderViewHost()->GetRoutingID();
- job.callback = callback;
- if (!registrar_.IsRegistered(
- this,
- NOTIFICATION_RENDERER_PROCESS_TERMINATED,
- Source<RenderProcessHost>(web_contents->GetRenderProcessHost()))) {
- registrar_.Add(
- this,
- NOTIFICATION_RENDERER_PROCESS_TERMINATED,
- Source<RenderProcessHost>(web_contents->GetRenderProcessHost()));
- }
+ Job* job = new Job();
+ id_to_job_[job_id] = job;
+ job->SetWebContents(web_contents);
+ job->set_callback(callback);
return job_id;
}
-void MHTMLGenerationManager::Observe(int type,
- const NotificationSource& source,
- const NotificationDetails& details) {
- DCHECK(type == NOTIFICATION_RENDERER_PROCESS_TERMINATED);
- RenderProcessHost* host = Source<RenderProcessHost>(source).ptr();
- registrar_.Remove(
- this,
- NOTIFICATION_RENDERER_PROCESS_TERMINATED,
- source);
- std::set<int> job_to_delete;
+void MHTMLGenerationManager::RenderProcessExited(Job* job) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
for (IDToJobMap::iterator it = id_to_job_.begin(); it != id_to_job_.end();
++it) {
- if (it->second.process_id == host->GetID())
- job_to_delete.insert(it->first);
- }
- for (std::set<int>::iterator it = job_to_delete.begin();
- it != job_to_delete.end();
- ++it) {
- JobFinished(*it, -1);
+ if (it->second == job) {
+ JobFinished(it->first, -1);
+ return;
+ }
}
+ NOTREACHED();
}
} // namespace content
diff --git a/chromium/content/browser/download/mhtml_generation_manager.h b/chromium/content/browser/download/mhtml_generation_manager.h
index 5525d99b812..bd178cb03d7 100644
--- a/chromium/content/browser/download/mhtml_generation_manager.h
+++ b/chromium/content/browser/download/mhtml_generation_manager.h
@@ -7,11 +7,9 @@
#include <map>
+#include "base/files/file.h"
#include "base/memory/singleton.h"
-#include "base/platform_file.h"
#include "base/process/process.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
#include "ipc/ipc_platform_file.h"
namespace base {
@@ -19,9 +17,10 @@ class FilePath;
}
namespace content {
+
class WebContents;
-class MHTMLGenerationManager : public NotificationObserver {
+class MHTMLGenerationManager {
public:
static MHTMLGenerationManager* GetInstance();
@@ -37,7 +36,7 @@ class MHTMLGenerationManager : public NotificationObserver {
// Instructs the render view to generate a MHTML representation of the current
// page for |web_contents|.
void StreamMHTML(WebContents* web_contents,
- const base::PlatformFile file,
+ base::File file,
const GenerateMHTMLCallback& callback);
// Notification from the renderer that the MHTML generation finished.
@@ -47,23 +46,7 @@ class MHTMLGenerationManager : public NotificationObserver {
private:
friend struct DefaultSingletonTraits<MHTMLGenerationManager>;
-
- struct Job{
- Job();
- ~Job();
-
- // The handles to file the MHTML is saved to, for the browser and renderer
- // processes.
- base::PlatformFile browser_file;
- IPC::PlatformFileForTransit renderer_file;
-
- // The IDs mapping to a specific contents.
- int process_id;
- int routing_id;
-
- // The callback to call once generation is complete.
- GenerateMHTMLCallback callback;
- };
+ class Job;
MHTMLGenerationManager();
virtual ~MHTMLGenerationManager();
@@ -74,15 +57,14 @@ class MHTMLGenerationManager : public NotificationObserver {
base::ProcessHandle renderer_process);
// Called on the UI thread when the file that should hold the MHTML data has
- // been created. This returns a handle to that file for the browser process
- // and one for the renderer process. These handles are
- // kInvalidPlatformFileValue if the file could not be opened.
- void FileHandleAvailable(int job_id,
- base::PlatformFile browser_file,
- IPC::PlatformFileForTransit renderer_file);
+ // been created. This receives a handle to that file for the browser process
+ // and one for the renderer process.
+ void FileAvailable(int job_id,
+ base::File browser_file,
+ IPC::PlatformFileForTransit renderer_file);
// Called on the file thread to close the file the MHTML was saved to.
- void CloseFile(base::PlatformFile file);
+ void CloseFile(base::File file);
// Called on the UI thread when a job has been processed (successfully or
// not). Closes the file and removes the job from the job map.
@@ -92,14 +74,11 @@ class MHTMLGenerationManager : public NotificationObserver {
// Creates an register a new job.
int NewJob(WebContents* web_contents, const GenerateMHTMLCallback& callback);
- // Implementation of NotificationObserver.
- virtual void Observe(int type,
- const NotificationSource& source,
- const NotificationDetails& details) OVERRIDE;
+ // Called when the render process connected to a job exits.
+ void RenderProcessExited(Job* job);
- typedef std::map<int, Job> IDToJobMap;
+ typedef std::map<int, Job*> IDToJobMap;
IDToJobMap id_to_job_;
- NotificationRegistrar registrar_;
DISALLOW_COPY_AND_ASSIGN(MHTMLGenerationManager);
};
diff --git a/chromium/content/browser/download/save_file.cc b/chromium/content/browser/download/save_file.cc
index e8885e4e363..4404fc866d7 100644
--- a/chromium/content/browser/download/save_file.cc
+++ b/chromium/content/browser/download/save_file.cc
@@ -6,7 +6,6 @@
#include "base/logging.h"
#include "content/public/browser/browser_thread.h"
-#include "net/base/file_stream.h"
namespace content {
@@ -21,7 +20,7 @@ SaveFile::SaveFile(const SaveFileCreateInfo* info, bool calculate_hash)
0,
calculate_hash,
std::string(),
- scoped_ptr<net::FileStream>(),
+ base::File(),
net::BoundNetLog()),
info_(info) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
diff --git a/chromium/content/browser/download/save_file_manager.cc b/chromium/content/browser/download/save_file_manager.cc
index 85651b77092..80ab95eefd0 100644
--- a/chromium/content/browser/download/save_file_manager.cc
+++ b/chromium/content/browser/download/save_file_manager.cc
@@ -18,8 +18,8 @@
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/browser_thread.h"
+#include "net/base/filename_util.h"
#include "net/base/io_buffer.h"
-#include "net/base/net_util.h"
#include "url/gurl.h"
namespace content {
@@ -389,9 +389,8 @@ void SaveFileManager::OnRequireSaveJobFromOtherSource(
void SaveFileManager::ExecuteCancelSaveRequest(int render_process_id,
int request_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- ResourceDispatcherHostImpl::Get()->CancelRequest(render_process_id,
- request_id,
- false);
+ ResourceDispatcherHostImpl::Get()->CancelRequest(
+ render_process_id, request_id);
}
// Notifications sent from the UI thread and run on the file thread.
diff --git a/chromium/content/browser/download/save_file_resource_handler.cc b/chromium/content/browser/download/save_file_resource_handler.cc
index d6405ffdb7a..e1fc32b7fc4 100644
--- a/chromium/content/browser/download/save_file_resource_handler.cc
+++ b/chromium/content/browser/download/save_file_resource_handler.cc
@@ -15,11 +15,12 @@
namespace content {
-SaveFileResourceHandler::SaveFileResourceHandler(int render_process_host_id,
+SaveFileResourceHandler::SaveFileResourceHandler(net::URLRequest* request,
+ int render_process_host_id,
int render_view_id,
const GURL& url,
SaveFileManager* manager)
- : ResourceHandler(NULL),
+ : ResourceHandler(request),
save_id_(-1),
render_process_id_(render_process_host_id),
render_view_id_(render_view_id),
@@ -31,14 +32,11 @@ SaveFileResourceHandler::SaveFileResourceHandler(int render_process_host_id,
SaveFileResourceHandler::~SaveFileResourceHandler() {
}
-bool SaveFileResourceHandler::OnUploadProgress(int request_id,
- uint64 position,
- uint64 size) {
+bool SaveFileResourceHandler::OnUploadProgress(uint64 position, uint64 size) {
return true;
}
bool SaveFileResourceHandler::OnRequestRedirected(
- int request_id,
const GURL& url,
ResourceResponse* response,
bool* defer) {
@@ -46,10 +44,8 @@ bool SaveFileResourceHandler::OnRequestRedirected(
return true;
}
-bool SaveFileResourceHandler::OnResponseStarted(
- int request_id,
- ResourceResponse* response,
- bool* defer) {
+bool SaveFileResourceHandler::OnResponseStarted(ResourceResponse* response,
+ bool* defer) {
save_id_ = save_manager_->GetNextId();
// |save_manager_| consumes (deletes):
SaveFileCreateInfo* info = new SaveFileCreateInfo;
@@ -59,7 +55,7 @@ bool SaveFileResourceHandler::OnResponseStarted(
info->save_id = save_id_;
info->render_process_id = render_process_id_;
info->render_view_id = render_view_id_;
- info->request_id = request_id;
+ info->request_id = GetRequestID();
info->content_disposition = content_disposition_;
info->save_source = SaveFileCreateInfo::SAVE_FILE_FROM_NET;
BrowserThread::PostTask(
@@ -68,14 +64,16 @@ bool SaveFileResourceHandler::OnResponseStarted(
return true;
}
-bool SaveFileResourceHandler::OnWillStart(int request_id,
- const GURL& url,
- bool* defer) {
+bool SaveFileResourceHandler::OnWillStart(const GURL& url, bool* defer) {
+ return true;
+}
+
+bool SaveFileResourceHandler::OnBeforeNetworkStart(const GURL& url,
+ bool* defer) {
return true;
}
-bool SaveFileResourceHandler::OnWillRead(int request_id,
- scoped_refptr<net::IOBuffer>* buf,
+bool SaveFileResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf,
int* buf_size,
int min_size) {
DCHECK(buf && buf_size);
@@ -87,8 +85,7 @@ bool SaveFileResourceHandler::OnWillRead(int request_id,
return true;
}
-bool SaveFileResourceHandler::OnReadCompleted(int request_id, int bytes_read,
- bool* defer) {
+bool SaveFileResourceHandler::OnReadCompleted(int bytes_read, bool* defer) {
DCHECK(read_buffer_.get());
// We are passing ownership of this buffer to the save file manager.
scoped_refptr<net::IOBuffer> buffer;
@@ -101,7 +98,6 @@ bool SaveFileResourceHandler::OnReadCompleted(int request_id, int bytes_read,
}
void SaveFileResourceHandler::OnResponseCompleted(
- int request_id,
const net::URLRequestStatus& status,
const std::string& security_info,
bool* defer) {
@@ -112,9 +108,7 @@ void SaveFileResourceHandler::OnResponseCompleted(
read_buffer_ = NULL;
}
-void SaveFileResourceHandler::OnDataDownloaded(
- int request_id,
- int bytes_downloaded) {
+void SaveFileResourceHandler::OnDataDownloaded(int bytes_downloaded) {
NOTREACHED();
}
diff --git a/chromium/content/browser/download/save_file_resource_handler.h b/chromium/content/browser/download/save_file_resource_handler.h
index 2079431f9dd..5adbc5c229a 100644
--- a/chromium/content/browser/download/save_file_resource_handler.h
+++ b/chromium/content/browser/download/save_file_resource_handler.h
@@ -11,58 +11,57 @@
#include "content/browser/loader/resource_handler.h"
#include "url/gurl.h"
+namespace net {
+class URLRequest;
+}
+
namespace content {
class SaveFileManager;
// Forwards data to the save thread.
class SaveFileResourceHandler : public ResourceHandler {
public:
- SaveFileResourceHandler(int render_process_host_id,
+ SaveFileResourceHandler(net::URLRequest* request,
+ int render_process_host_id,
int render_view_id,
const GURL& url,
SaveFileManager* manager);
virtual ~SaveFileResourceHandler();
// ResourceHandler Implementation:
- virtual bool OnUploadProgress(int request_id,
- uint64 position,
- uint64 size) OVERRIDE;
+ virtual bool OnUploadProgress(uint64 position, uint64 size) OVERRIDE;
// Saves the redirected URL to final_url_, we need to use the original
// URL to match original request.
- virtual bool OnRequestRedirected(int request_id,
- const GURL& url,
+ virtual bool OnRequestRedirected(const GURL& url,
ResourceResponse* response,
bool* defer) OVERRIDE;
// Sends the download creation information to the download thread.
- virtual bool OnResponseStarted(int request_id,
- ResourceResponse* response,
+ virtual bool OnResponseStarted(ResourceResponse* response,
bool* defer) OVERRIDE;
// Pass-through implementation.
- virtual bool OnWillStart(int request_id,
- const GURL& url,
- bool* defer) OVERRIDE;
+ virtual bool OnWillStart(const GURL& url, bool* defer) OVERRIDE;
+
+ // Pass-through implementation.
+ virtual bool OnBeforeNetworkStart(const GURL& url, bool* defer) OVERRIDE;
// Creates a new buffer, which will be handed to the download thread for file
// writing and deletion.
- virtual bool OnWillRead(int request_id,
- scoped_refptr<net::IOBuffer>* buf,
+ virtual bool OnWillRead(scoped_refptr<net::IOBuffer>* buf,
int* buf_size,
int min_size) OVERRIDE;
// Passes the buffer to the download file writer.
- virtual bool OnReadCompleted(int request_id, int bytes_read,
- bool* defer) OVERRIDE;
+ virtual bool OnReadCompleted(int bytes_read, bool* defer) OVERRIDE;
- virtual void OnResponseCompleted(int request_id,
- const net::URLRequestStatus& status,
+ virtual void OnResponseCompleted(const net::URLRequestStatus& status,
const std::string& security_info,
bool* defer) OVERRIDE;
// N/A to this flavor of SaveFileResourceHandler.
- virtual void OnDataDownloaded(int request_id, int bytes_downloaded) OVERRIDE;
+ virtual void OnDataDownloaded(int bytes_downloaded) OVERRIDE;
// If the content-length header is not present (or contains something other
// than numbers), StringToInt64 returns 0, which indicates 'unknown size' and
diff --git a/chromium/content/browser/download/save_package.cc b/chromium/content/browser/download/save_package.cc
index 5961e6bf580..90ab2a5056d 100644
--- a/chromium/content/browser/download/save_package.cc
+++ b/chromium/content/browser/download/save_package.cc
@@ -38,12 +38,12 @@
#include "content/public/browser/notification_types.h"
#include "content/public/browser/resource_context.h"
#include "content/public/browser/web_contents.h"
-#include "content/public/common/url_constants.h"
+#include "net/base/filename_util.h"
#include "net/base/io_buffer.h"
#include "net/base/mime_util.h"
-#include "net/base/net_util.h"
#include "net/url_request/url_request_context.h"
#include "third_party/WebKit/public/web/WebPageSerializerClient.h"
+#include "url/url_constants.h"
using base::Time;
using blink::WebPageSerializerClient;
@@ -259,9 +259,9 @@ GURL SavePackage::GetUrlToBeSaved() {
// "real" url of the page) from the NavigationEntry because it reflects its
// origin rather than the displayed one (returned by GetURL) which may be
// different (like having "view-source:" on the front).
- NavigationEntry* active_entry =
- web_contents()->GetController().GetActiveEntry();
- return active_entry->GetURL();
+ NavigationEntry* visible_entry =
+ web_contents()->GetController().GetVisibleEntry();
+ return visible_entry->GetURL();
}
void SavePackage::Cancel(bool user_action) {
@@ -1222,7 +1222,7 @@ base::FilePath SavePackage::GetSuggestedNameForSaveAs(
// similarly).
if (title_ == net::FormatUrl(page_url_, accept_langs)) {
std::string url_path;
- if (!page_url_.SchemeIs(chrome::kDataScheme)) {
+ if (!page_url_.SchemeIs(url::kDataScheme)) {
std::vector<std::string> url_parts;
base::SplitString(page_url_.path(), '/', &url_parts);
if (!url_parts.empty()) {
@@ -1299,7 +1299,7 @@ const base::FilePath::CharType* SavePackage::ExtensionForMimeType(
#if defined(OS_POSIX)
base::FilePath::StringType mime_type(contents_mime_type);
#elif defined(OS_WIN)
- base::FilePath::StringType mime_type(UTF8ToWide(contents_mime_type));
+ base::FilePath::StringType mime_type(base::UTF8ToWide(contents_mime_type));
#endif // OS_WIN
for (uint32 i = 0; i < ARRAYSIZE_UNSAFE(extensions); ++i) {
if (mime_type == extensions[i].mime_type)
diff --git a/chromium/content/browser/download/save_package_browsertest.cc b/chromium/content/browser/download/save_package_browsertest.cc
index 391033238cd..2c7a652dd0d 100644
--- a/chromium/content/browser/download/save_package_browsertest.cc
+++ b/chromium/content/browser/download/save_package_browsertest.cc
@@ -4,9 +4,9 @@
#include "base/files/scoped_temp_dir.h"
#include "content/browser/download/save_package.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/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
namespace content {
diff --git a/chromium/content/browser/download/save_package_unittest.cc b/chromium/content/browser/download/save_package_unittest.cc
index 47fd1b46686..b0e38c0f18d 100644
--- a/chromium/content/browser/download/save_package_unittest.cc
+++ b/chromium/content/browser/download/save_package_unittest.cc
@@ -244,7 +244,7 @@ TEST_F(SavePackageTest, MAYBE_TestLongSafePureFilename) {
const base::FilePath::StringType ext(FPL_HTML_EXTENSION);
base::FilePath::StringType filename =
#if defined(OS_WIN)
- ASCIIToWide(long_file_name);
+ base::ASCIIToWide(long_file_name);
#else
long_file_name;
#endif
@@ -352,38 +352,38 @@ static const struct SuggestedSaveNameTestCase {
} kSuggestedSaveNames[] = {
// Title overrides the URL.
{ "http://foo.com",
- ASCIIToUTF16("A page title"),
+ base::ASCIIToUTF16("A page title"),
FPL("A page title") FPL_HTML_EXTENSION,
true
},
// Extension is preserved.
{ "http://foo.com",
- ASCIIToUTF16("A page title with.ext"),
+ base::ASCIIToUTF16("A page title with.ext"),
FPL("A page title with.ext"),
false
},
// If the title matches the URL, use the last component of the URL.
{ "http://foo.com/bar",
- ASCIIToUTF16("foo.com/bar"),
+ base::ASCIIToUTF16("foo.com/bar"),
FPL("bar"),
false
},
// If the title matches the URL, but there is no "filename" component,
// use the domain.
{ "http://foo.com",
- ASCIIToUTF16("foo.com"),
+ base::ASCIIToUTF16("foo.com"),
FPL("foo.com"),
false
},
// Make sure fuzzy matching works.
{ "http://foo.com/bar",
- ASCIIToUTF16("foo.com/bar"),
+ base::ASCIIToUTF16("foo.com/bar"),
FPL("bar"),
false
},
// A URL-like title that does not match the title is respected in full.
{ "http://foo.com",
- ASCIIToUTF16("http://www.foo.com/path/title.txt"),
+ base::ASCIIToUTF16("http://www.foo.com/path/title.txt"),
FPL("http www.foo.com path title.txt"),
false
},
diff --git a/chromium/content/browser/fileapi/DEPS b/chromium/content/browser/fileapi/DEPS
new file mode 100644
index 00000000000..9d10bbaf469
--- /dev/null
+++ b/chromium/content/browser/fileapi/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+ "+third_party/leveldatabase/src/include/leveldb",
+
+ # TODO(pilgrim) jsbell says this smells funny
+ "+third_party/leveldatabase/src/db/filename.h",
+]
diff --git a/chromium/content/browser/fileapi/blob_storage_context_unittest.cc b/chromium/content/browser/fileapi/blob_storage_context_unittest.cc
new file mode 100644
index 00000000000..823679e585c
--- /dev/null
+++ b/chromium/content/browser/fileapi/blob_storage_context_unittest.cc
@@ -0,0 +1,237 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/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 "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/blob/blob_data_handle.h"
+#include "webkit/browser/blob/blob_storage_context.h"
+
+using webkit_blob::BlobDataHandle;
+
+namespace content {
+
+namespace {
+void SetupBasicBlob(BlobStorageHost* host, const std::string& id) {
+ EXPECT_TRUE(host->StartBuildingBlob(id));
+ BlobData::Item 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);
+ base::MessageLoop fake_io_message_loop;
+
+ // Build up a basic blob.
+ const std::string kId("id");
+ SetupBasicBlob(&host, kId);
+
+ // Make sure it's there, finish building implies a ref of one.
+ scoped_ptr<BlobDataHandle> blob_data_handle;
+ blob_data_handle = context.GetBlobDataFromUUID(kId);
+ EXPECT_TRUE(blob_data_handle);
+ blob_data_handle.reset();
+ { // Clean up for ASAN
+ base::RunLoop run_loop;
+ run_loop.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();
+ { // Clean up for ASAN
+ base::RunLoop run_loop;
+ run_loop.RunUntilIdle();
+ }
+
+ // Make sure it goes away in the end.
+ EXPECT_TRUE(host.DecrementBlobRefCount(kId));
+ blob_data_handle = context.GetBlobDataFromUUID(kId);
+ EXPECT_FALSE(blob_data_handle);
+ EXPECT_FALSE(host.DecrementBlobRefCount(kId));
+ EXPECT_FALSE(host.IncrementBlobRefCount(kId));
+}
+
+TEST(BlobStorageContextTest, BlobDataHandle) {
+ BlobStorageContext context;
+ BlobStorageHost host(&context);
+ 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<BlobDataHandle> blob_data_handle =
+ context.GetBlobDataFromUUID(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<BlobDataHandle> another_handle =
+ context.GetBlobDataFromUUID(kId);
+ EXPECT_TRUE(another_handle);
+
+ // Should disappear after dropping both handles.
+ blob_data_handle.reset();
+ another_handle.reset();
+ { // Clean up for ASAN
+ base::RunLoop run_loop;
+ run_loop.RunUntilIdle();
+ }
+ blob_data_handle = context.GetBlobDataFromUUID(kId);
+ EXPECT_FALSE(blob_data_handle);
+}
+
+TEST(BlobStorageContextTest, CompoundBlobs) {
+ const std::string kId1("id1");
+ const std::string kId2("id2");
+ const std::string kId2Prime("id2.prime");
+
+ base::MessageLoop fake_io_message_loop;
+
+ // Setup a set of blob data for testing.
+ base::Time time1, time2;
+ base::Time::FromString("Tue, 15 Nov 1994, 12:45:26 GMT", &time1);
+ base::Time::FromString("Mon, 14 Nov 1994, 11:30:49 GMT", &time2);
+
+ scoped_refptr<BlobData> blob_data1(new BlobData(kId1));
+ blob_data1->AppendData("Data1");
+ blob_data1->AppendData("Data2");
+ blob_data1->AppendFile(base::FilePath(FILE_PATH_LITERAL("File1.txt")),
+ 10, 1024, time1);
+
+ scoped_refptr<BlobData> blob_data2(new BlobData(kId2));
+ blob_data2->AppendData("Data3");
+ blob_data2->AppendBlob(kId1, 8, 100);
+ blob_data2->AppendFile(base::FilePath(FILE_PATH_LITERAL("File2.txt")),
+ 0, 20, time2);
+
+ scoped_refptr<BlobData> canonicalized_blob_data2(new BlobData(kId2Prime));
+ canonicalized_blob_data2->AppendData("Data3");
+ canonicalized_blob_data2->AppendData("a2___", 2);
+ canonicalized_blob_data2->AppendFile(
+ base::FilePath(FILE_PATH_LITERAL("File1.txt")),
+ 10, 98, time1);
+ canonicalized_blob_data2->AppendFile(
+ base::FilePath(FILE_PATH_LITERAL("File2.txt")), 0, 20, time2);
+
+ BlobStorageContext context;
+ scoped_ptr<BlobDataHandle> blob_data_handle;
+
+ // Test a blob referring to only data and a file.
+ blob_data_handle = context.AddFinishedBlob(blob_data1.get());
+ ASSERT_TRUE(blob_data_handle.get());
+ EXPECT_TRUE(*(blob_data_handle->data()) == *blob_data1.get());
+
+ // Test a blob composed in part with another blob.
+ blob_data_handle = context.AddFinishedBlob(blob_data2.get());
+ ASSERT_TRUE(blob_data_handle.get());
+ EXPECT_TRUE(*(blob_data_handle->data()) == *canonicalized_blob_data2.get());
+
+ blob_data_handle.reset();
+ { // Clean up for ASAN
+ base::RunLoop run_loop;
+ run_loop.RunUntilIdle();
+ }
+}
+
+TEST(BlobStorageContextTest, PublicBlobUrls) {
+ BlobStorageContext context;
+ BlobStorageHost host(&context);
+ base::MessageLoop fake_io_message_loop;
+
+ // Build up a basic blob.
+ const std::string kId("id");
+ SetupBasicBlob(&host, kId);
+
+ // Now register a url for that blob.
+ GURL kUrl("blob:id");
+ EXPECT_TRUE(host.RegisterPublicBlobURL(kUrl, kId));
+ scoped_ptr<BlobDataHandle> blob_data_handle =
+ context.GetBlobDataFromPublicURL(kUrl);
+ ASSERT_TRUE(blob_data_handle.get());
+ EXPECT_EQ(kId, blob_data_handle->data()->uuid());
+ blob_data_handle.reset();
+ { // Clean up for ASAN
+ base::RunLoop run_loop;
+ run_loop.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);
+ EXPECT_TRUE(blob_data_handle);
+ blob_data_handle.reset();
+ { // Clean up for ASAN
+ base::RunLoop run_loop;
+ run_loop.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<BlobStorageHost> 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<BlobDataHandle> handle = context.GetBlobDataFromPublicURL(kUrl);
+ EXPECT_TRUE(!handle.get());
+ handle = context.GetBlobDataFromUUID(kId);
+ EXPECT_TRUE(!handle.get());
+}
+
+TEST(BlobStorageContextTest, EarlyContextDeletion) {
+ scoped_ptr<BlobStorageContext> context(new BlobStorageContext);
+ BlobStorageHost host(context.get());
+ 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));
+ BlobData::Item 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));
+}
+
+// TODO(michaeln): tests for the depcrecated url stuff
+
+} // namespace content
diff --git a/chromium/content/browser/fileapi/blob_storage_host.cc b/chromium/content/browser/fileapi/blob_storage_host.cc
new file mode 100644
index 00000000000..cc51dbb37a7
--- /dev/null
+++ b/chromium/content/browser/fileapi/blob_storage_host.cc
@@ -0,0 +1,117 @@
+// 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 "url/gurl.h"
+#include "webkit/browser/blob/blob_data_handle.h"
+#include "webkit/browser/blob/blob_storage_context.h"
+
+using webkit_blob::BlobStorageContext;
+using webkit_blob::BlobData;
+
+namespace content {
+
+BlobStorageHost::BlobStorageHost(BlobStorageContext* context)
+ : context_(context->AsWeakPtr()) {
+}
+
+BlobStorageHost::~BlobStorageHost() {
+ if (!context_.get())
+ return;
+ for (std::set<GURL>::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 BlobData::Item& 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
new file mode 100644
index 00000000000..5d10d204dce
--- /dev/null
+++ b/chromium/content/browser/fileapi/blob_storage_host.h
@@ -0,0 +1,76 @@
+// 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_STORAGE_HOST_H_
+#define CONTENT_BROWSER_FILEAPI_STORAGE_HOST_H_
+
+#include <map>
+#include <set>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/memory/weak_ptr.h"
+#include "content/common/content_export.h"
+#include "webkit/common/blob/blob_data.h"
+
+class GURL;
+
+namespace webkit_blob {
+class BlobDataHandle;
+class BlobStorageHost;
+class BlobStorageContext;
+}
+
+using webkit_blob::BlobStorageContext;
+using webkit_blob::BlobData;
+
+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(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 BlobData::Item& 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<std::string, int> 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<GURL> public_blob_urls_;
+
+ base::WeakPtr<BlobStorageContext> context_;
+
+ DISALLOW_COPY_AND_ASSIGN(BlobStorageHost);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_FILEAPI_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 a09456b607f..5ce43121433 100644
--- a/chromium/content/browser/fileapi/blob_url_request_job_unittest.cc
+++ b/chromium/content/browser/fileapi/blob_url_request_job_unittest.cc
@@ -2,16 +2,19 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/basictypes.h"
#include "base/bind.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
+#include "base/numerics/safe_conversions.h"
#include "base/run_loop.h"
#include "base/time/time.h"
+#include "content/browser/fileapi/mock_url_request_delegate.h"
+#include "content/public/test/async_file_test_helper.h"
#include "content/public/test/test_file_system_context.h"
-#include "net/base/io_buffer.h"
#include "net/base/request_priority.h"
#include "net/http/http_byte_range.h"
#include "net/http/http_request_headers.h"
@@ -21,13 +24,15 @@
#include "net/url_request/url_request_job_factory_impl.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "webkit/browser/blob/blob_url_request_job.h"
-#include "webkit/browser/fileapi/async_file_test_helper.h"
#include "webkit/browser/fileapi/file_system_context.h"
#include "webkit/browser/fileapi/file_system_operation_context.h"
#include "webkit/browser/fileapi/file_system_url.h"
#include "webkit/common/blob/blob_data.h"
-namespace webkit_blob {
+using webkit_blob::BlobData;
+using webkit_blob::BlobURLRequestJob;
+
+namespace content {
namespace {
@@ -49,70 +54,6 @@ const fileapi::FileSystemType kFileSystemType =
class BlobURLRequestJobTest : public testing::Test {
public:
-
- // Test Harness -------------------------------------------------------------
- // TODO(jianli): share this test harness with AppCacheURLRequestJobTest
-
- class MockURLRequestDelegate : public net::URLRequest::Delegate {
- public:
- MockURLRequestDelegate()
- : received_data_(new net::IOBuffer(kBufferSize)) {}
-
- virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE {
- if (request->status().is_success()) {
- EXPECT_TRUE(request->response_headers());
- ReadSome(request);
- } else {
- RequestComplete();
- }
- }
-
- virtual void OnReadCompleted(net::URLRequest* request,
- int bytes_read) OVERRIDE {
- if (bytes_read > 0)
- ReceiveData(request, bytes_read);
- else
- RequestComplete();
- }
-
- const std::string& response_data() const { return response_data_; }
-
- private:
- void ReadSome(net::URLRequest* request) {
- if (!request->is_pending()) {
- RequestComplete();
- return;
- }
-
- int bytes_read = 0;
- if (!request->Read(received_data_.get(), kBufferSize, &bytes_read)) {
- if (!request->status().is_io_pending()) {
- RequestComplete();
- }
- return;
- }
-
- ReceiveData(request, bytes_read);
- }
-
- void ReceiveData(net::URLRequest* request, int bytes_read) {
- if (bytes_read) {
- response_data_.append(received_data_->data(),
- static_cast<size_t>(bytes_read));
- ReadSome(request);
- } else {
- RequestComplete();
- }
- }
-
- void RequestComplete() {
- base::MessageLoop::current()->Quit();
- }
-
- scoped_refptr<net::IOBuffer> received_data_;
- std::string response_data_;
- };
-
// A simple ProtocolHandler implementation to create BlobURLRequestJob.
class MockProtocolHandler :
public net::URLRequestJobFactory::ProtocolHandler {
@@ -143,17 +84,17 @@ class BlobURLRequestJobTest : public testing::Test {
temp_file1_ = temp_dir_.path().AppendASCII("BlobFile1.dat");
ASSERT_EQ(static_cast<int>(arraysize(kTestFileData1) - 1),
- file_util::WriteFile(temp_file1_, kTestFileData1,
+ base::WriteFile(temp_file1_, kTestFileData1,
arraysize(kTestFileData1) - 1));
- base::PlatformFileInfo file_info1;
+ base::File::Info file_info1;
base::GetFileInfo(temp_file1_, &file_info1);
temp_file_modification_time1_ = file_info1.last_modified;
temp_file2_ = temp_dir_.path().AppendASCII("BlobFile2.dat");
ASSERT_EQ(static_cast<int>(arraysize(kTestFileData2) - 1),
- file_util::WriteFile(temp_file2_, kTestFileData2,
+ base::WriteFile(temp_file2_, kTestFileData2,
arraysize(kTestFileData2) - 1));
- base::PlatformFileInfo file_info2;
+ base::File::Info file_info2;
base::GetFileInfo(temp_file2_, &file_info2);
temp_file_modification_time2_ = file_info2.last_modified;
@@ -167,7 +108,7 @@ class BlobURLRequestJobTest : public testing::Test {
void SetUpFileSystem() {
// Prepare file system.
- file_system_context_ = fileapi::CreateFileSystemContextForTesting(
+ file_system_context_ = CreateFileSystemContextForTesting(
NULL, temp_dir_.path());
file_system_context_->OpenFileSystem(
@@ -205,13 +146,13 @@ class BlobURLRequestJobTest : public testing::Test {
kFileSystemType,
base::FilePath().AppendASCII(filename));
- ASSERT_EQ(base::PLATFORM_FILE_OK,
- fileapi::AsyncFileTestHelper::CreateFileWithData(
+ ASSERT_EQ(base::File::FILE_OK,
+ content::AsyncFileTestHelper::CreateFileWithData(
file_system_context_, url, buf, buf_size));
- base::PlatformFileInfo file_info;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
- fileapi::AsyncFileTestHelper::GetMetadata(
+ base::File::Info file_info;
+ ASSERT_EQ(base::File::FILE_OK,
+ content::AsyncFileTestHelper::GetMetadata(
file_system_context_, url, &file_info));
if (modification_time)
*modification_time = file_info.last_modified;
@@ -219,16 +160,19 @@ class BlobURLRequestJobTest : public testing::Test {
void OnValidateFileSystem(const GURL& root,
const std::string& name,
- base::PlatformFileError result) {
- ASSERT_EQ(base::PLATFORM_FILE_OK, result);
+ base::File::Error result) {
+ ASSERT_EQ(base::File::FILE_OK, result);
ASSERT_TRUE(root.is_valid());
file_system_root_url_ = root;
}
- void TestSuccessRequest(const std::string& expected_response) {
+ void TestSuccessNonrangeRequest(const std::string& expected_response,
+ int64 expected_content_length) {
expected_status_code_ = 200;
expected_response_ = expected_response;
TestRequest("GET", net::HttpRequestHeaders());
+ EXPECT_EQ(expected_content_length,
+ request_->response_headers()->GetContentLength());
}
void TestErrorRequest(int expected_status_code) {
@@ -240,7 +184,7 @@ class BlobURLRequestJobTest : public testing::Test {
void TestRequest(const std::string& method,
const net::HttpRequestHeaders& extra_headers) {
request_ = url_request_context_.CreateRequest(
- GURL("blob:blah"), net::DEFAULT_PRIORITY, &url_request_delegate_);
+ GURL("blob:blah"), net::DEFAULT_PRIORITY, &url_request_delegate_, NULL);
request_->set_method(method);
if (!extra_headers.IsEmpty())
request_->SetExtraRequestHeaders(extra_headers);
@@ -272,6 +216,20 @@ class BlobURLRequestJobTest : public testing::Test {
*expected_result += std::string(kTestFileSystemFileData2 + 6, 7);
}
+ // This only works if all the Blob items have a definite pre-computed length.
+ // Otherwise, this will fail a CHECK.
+ int64 GetTotalBlobLength() const {
+ int64 total = 0;
+ const std::vector<BlobData::Item>& items = blob_data_->items();
+ for (std::vector<BlobData::Item>::const_iterator it = items.begin();
+ it != items.end(); ++it) {
+ int64 length = base::checked_cast<int64>(it->length());
+ CHECK(length <= kint64max - total);
+ total += length;
+ }
+ return total;
+ }
+
protected:
base::ScopedTempDir temp_dir_;
base::FilePath temp_file1_;
@@ -298,12 +256,12 @@ class BlobURLRequestJobTest : public testing::Test {
TEST_F(BlobURLRequestJobTest, TestGetSimpleDataRequest) {
blob_data_->AppendData(kTestData1);
- TestSuccessRequest(kTestData1);
+ TestSuccessNonrangeRequest(kTestData1, arraysize(kTestData1) - 1);
}
TEST_F(BlobURLRequestJobTest, TestGetSimpleFileRequest) {
blob_data_->AppendFile(temp_file1_, 0, -1, base::Time());
- TestSuccessRequest(kTestFileData1);
+ TestSuccessNonrangeRequest(kTestFileData1, arraysize(kTestFileData1) - 1);
}
TEST_F(BlobURLRequestJobTest, TestGetLargeFileRequest) {
@@ -314,10 +272,10 @@ TEST_F(BlobURLRequestJobTest, TestGetLargeFileRequest) {
for (int i = 0; i < kBufferSize * 5; ++i)
large_data.append(1, static_cast<char>(i % 256));
ASSERT_EQ(static_cast<int>(large_data.size()),
- file_util::WriteFile(large_temp_file, large_data.data(),
+ base::WriteFile(large_temp_file, large_data.data(),
large_data.size()));
blob_data_->AppendFile(large_temp_file, 0, -1, base::Time());
- TestSuccessRequest(large_data);
+ TestSuccessNonrangeRequest(large_data, large_data.size());
}
TEST_F(BlobURLRequestJobTest, TestGetNonExistentFileRequest) {
@@ -337,14 +295,15 @@ TEST_F(BlobURLRequestJobTest, TestGetChangedFileRequest) {
TEST_F(BlobURLRequestJobTest, TestGetSlicedFileRequest) {
blob_data_->AppendFile(temp_file1_, 2, 4, temp_file_modification_time1_);
std::string result(kTestFileData1 + 2, 4);
- TestSuccessRequest(result);
+ TestSuccessNonrangeRequest(result, 4);
}
TEST_F(BlobURLRequestJobTest, TestGetSimpleFileSystemFileRequest) {
SetUpFileSystem();
blob_data_->AppendFileSystemFile(temp_file_system_file1_, 0, -1,
- base::Time());
- TestSuccessRequest(kTestFileSystemFileData1);
+ base::Time());
+ TestSuccessNonrangeRequest(kTestFileSystemFileData1,
+ arraysize(kTestFileSystemFileData1) - 1);
}
TEST_F(BlobURLRequestJobTest, TestGetLargeFileSystemFileRequest) {
@@ -357,9 +316,9 @@ TEST_F(BlobURLRequestJobTest, TestGetLargeFileSystemFileRequest) {
const char kFilename[] = "LargeBlob.dat";
WriteFileSystemFile(kFilename, large_data.data(), large_data.size(), NULL);
- blob_data_->AppendFileSystemFile(GetFileSystemURL(kFilename),
- 0, -1, base::Time());
- TestSuccessRequest(large_data);
+ blob_data_->AppendFileSystemFile(GetFileSystemURL(kFilename), 0, -1,
+ base::Time());
+ TestSuccessNonrangeRequest(large_data, large_data.size());
}
TEST_F(BlobURLRequestJobTest, TestGetNonExistentFileSystemFileRequest) {
@@ -383,14 +342,14 @@ TEST_F(BlobURLRequestJobTest, TestGetSlicedFileSystemFileRequest) {
blob_data_->AppendFileSystemFile(temp_file_system_file1_, 2, 4,
temp_file_system_file_modification_time1_);
std::string result(kTestFileSystemFileData1 + 2, 4);
- TestSuccessRequest(result);
+ TestSuccessNonrangeRequest(result, 4);
}
TEST_F(BlobURLRequestJobTest, TestGetComplicatedDataAndFileRequest) {
SetUpFileSystem();
std::string result;
BuildComplicatedData(&result);
- TestSuccessRequest(result);
+ TestSuccessNonrangeRequest(result, GetTotalBlobLength());
}
TEST_F(BlobURLRequestJobTest, TestGetRangeRequest1) {
@@ -403,6 +362,15 @@ TEST_F(BlobURLRequestJobTest, TestGetRangeRequest1) {
expected_status_code_ = 206;
expected_response_ = result.substr(5, 10 - 5 + 1);
TestRequest("GET", extra_headers);
+
+ EXPECT_EQ(6, request_->response_headers()->GetContentLength());
+
+ int64 first = 0, last = 0, length = 0;
+ EXPECT_TRUE(
+ request_->response_headers()->GetContentRange(&first, &last, &length));
+ EXPECT_EQ(5, first);
+ EXPECT_EQ(10, last);
+ EXPECT_EQ(GetTotalBlobLength(), length);
}
TEST_F(BlobURLRequestJobTest, TestGetRangeRequest2) {
@@ -415,6 +383,16 @@ TEST_F(BlobURLRequestJobTest, TestGetRangeRequest2) {
expected_status_code_ = 206;
expected_response_ = result.substr(result.length() - 10);
TestRequest("GET", extra_headers);
+
+ EXPECT_EQ(10, request_->response_headers()->GetContentLength());
+
+ int64 total = GetTotalBlobLength();
+ int64 first = 0, last = 0, length = 0;
+ EXPECT_TRUE(
+ request_->response_headers()->GetContentRange(&first, &last, &length));
+ EXPECT_EQ(total - 10, first);
+ EXPECT_EQ(total - 1, last);
+ EXPECT_EQ(total, length);
}
TEST_F(BlobURLRequestJobTest, TestExtraHeaders) {
@@ -435,4 +413,4 @@ TEST_F(BlobURLRequestJobTest, TestExtraHeaders) {
EXPECT_EQ(kTestContentDisposition, content_disposition);
}
-} // namespace webkit_blob
+} // namespace content
diff --git a/chromium/content/browser/fileapi/browser_file_system_helper.cc b/chromium/content/browser/fileapi/browser_file_system_helper.cc
index 094f35ac304..d89d6cab25b 100644
--- a/chromium/content/browser/fileapi/browser_file_system_helper.cc
+++ b/chromium/content/browser/fileapi/browser_file_system_helper.cc
@@ -17,10 +17,11 @@
#include "content/public/browser/content_browser_client.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_switches.h"
-#include "content/public/common/url_constants.h"
+#include "url/url_constants.h"
#include "webkit/browser/fileapi/external_mount_points.h"
#include "webkit/browser/fileapi/file_permission_policy.h"
#include "webkit/browser/fileapi/file_system_backend.h"
+#include "webkit/browser/fileapi/file_system_context.h"
#include "webkit/browser/fileapi/file_system_operation_runner.h"
#include "webkit/browser/fileapi/file_system_options.h"
#include "webkit/browser/quota/quota_manager.h"
@@ -40,9 +41,9 @@ FileSystemOptions CreateBrowserFileSystemOptions(bool is_incognito) {
&additional_allowed_schemes);
if (CommandLine::ForCurrentProcess()->HasSwitch(
switches::kAllowFileAccessFromFiles)) {
- additional_allowed_schemes.push_back(chrome::kFileScheme);
+ additional_allowed_schemes.push_back(url::kFileScheme);
}
- return FileSystemOptions(profile_mode, additional_allowed_schemes);
+ return FileSystemOptions(profile_mode, additional_allowed_schemes, NULL);
}
} // namespace
@@ -53,7 +54,7 @@ scoped_refptr<fileapi::FileSystemContext> CreateFileSystemContext(
bool is_incognito,
quota::QuotaManagerProxy* quota_manager_proxy) {
- base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool();
+ base::SequencedWorkerPool* pool = BrowserThread::GetBlockingPool();
scoped_refptr<base::SequencedTaskRunner> file_task_runner =
pool->GetSequencedTaskRunnerWithShutdownBehavior(
pool->GetNamedSequenceToken("FileAPI"),
@@ -66,6 +67,12 @@ scoped_refptr<fileapi::FileSystemContext> CreateFileSystemContext(
profile_path,
&additional_backends);
+ // Set up the auto mount handlers for url requests.
+ std::vector<fileapi::URLRequestAutoMountHandler>
+ url_request_auto_mount_handlers;
+ GetContentClient()->browser()->GetURLRequestAutoMountHandlers(
+ &url_request_auto_mount_handlers);
+
scoped_refptr<fileapi::FileSystemContext> file_system_context =
new fileapi::FileSystemContext(
BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO).get(),
@@ -74,6 +81,7 @@ scoped_refptr<fileapi::FileSystemContext> CreateFileSystemContext(
browser_context->GetSpecialStoragePolicy(),
quota_manager_proxy,
additional_backends.Pass(),
+ url_request_auto_mount_handlers,
profile_path,
CreateBrowserFileSystemOptions(is_incognito));
diff --git a/chromium/content/browser/fileapi/chrome_blob_storage_context.cc b/chromium/content/browser/fileapi/chrome_blob_storage_context.cc
index 97151a83f67..fce0bed9066 100644
--- a/chromium/content/browser/fileapi/chrome_blob_storage_context.cc
+++ b/chromium/content/browser/fileapi/chrome_blob_storage_context.cc
@@ -5,8 +5,11 @@
#include "content/browser/fileapi/chrome_blob_storage_context.h"
#include "base/bind.h"
+#include "base/guid.h"
+#include "content/public/browser/blob_handle.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
+#include "webkit/browser/blob/blob_data_handle.h"
#include "webkit/browser/blob/blob_storage_context.h"
using base::UserDataAdapter;
@@ -14,6 +17,26 @@ using webkit_blob::BlobStorageContext;
namespace content {
+namespace {
+
+class BlobHandleImpl : public BlobHandle {
+ public:
+ BlobHandleImpl(scoped_ptr<webkit_blob::BlobDataHandle> handle)
+ : handle_(handle.Pass()) {
+ }
+
+ virtual ~BlobHandleImpl() {}
+
+ virtual std::string GetUUID() OVERRIDE {
+ return handle_->uuid();
+ }
+
+ private:
+ scoped_ptr<webkit_blob::BlobDataHandle> handle_;
+};
+
+} // namespace
+
static const char* kBlobStorageContextKeyName = "content_blob_storage_context";
ChromeBlobStorageContext::ChromeBlobStorageContext() {}
@@ -43,6 +66,25 @@ void ChromeBlobStorageContext::InitializeOnIOThread() {
context_.reset(new BlobStorageContext());
}
+scoped_ptr<BlobHandle> ChromeBlobStorageContext::CreateMemoryBackedBlob(
+ const char* data, size_t length) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ std::string uuid(base::GenerateGUID());
+ scoped_refptr<webkit_blob::BlobData> blob_data =
+ new webkit_blob::BlobData(uuid);
+ blob_data->AppendData(data, length);
+
+ scoped_ptr<webkit_blob::BlobDataHandle> blob_data_handle =
+ context_->AddFinishedBlob(blob_data.get());
+ if (!blob_data_handle)
+ return scoped_ptr<BlobHandle>();
+
+ scoped_ptr<BlobHandle> blob_handle(
+ new BlobHandleImpl(blob_data_handle.Pass()));
+ return blob_handle.Pass();
+}
+
ChromeBlobStorageContext::~ChromeBlobStorageContext() {}
void ChromeBlobStorageContext::DeleteOnCorrectThread() const {
diff --git a/chromium/content/browser/fileapi/chrome_blob_storage_context.h b/chromium/content/browser/fileapi/chrome_blob_storage_context.h
index 641e2353006..1dccd99faf0 100644
--- a/chromium/content/browser/fileapi/chrome_blob_storage_context.h
+++ b/chromium/content/browser/fileapi/chrome_blob_storage_context.h
@@ -15,6 +15,7 @@ class BlobStorageContext;
}
namespace content {
+class BlobHandle;
class BrowserContext;
struct ChromeBlobStorageContextDeleter;
@@ -40,6 +41,10 @@ class CONTENT_EXPORT ChromeBlobStorageContext
return context_.get();
}
+ // Returns a NULL scoped_ptr on failure.
+ scoped_ptr<BlobHandle> CreateMemoryBackedBlob(const char* data,
+ size_t length);
+
protected:
virtual ~ChromeBlobStorageContext();
diff --git a/chromium/content/browser/fileapi/copy_or_move_file_validator_unittest.cc b/chromium/content/browser/fileapi/copy_or_move_file_validator_unittest.cc
index 1dd3f986552..eaff4d38f54 100644
--- a/chromium/content/browser/fileapi/copy_or_move_file_validator_unittest.cc
+++ b/chromium/content/browser/fileapi/copy_or_move_file_validator_unittest.cc
@@ -7,42 +7,45 @@
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/run_loop.h"
+#include "content/public/test/async_file_test_helper.h"
+#include "content/public/test/mock_special_storage_policy.h"
#include "content/public/test/test_file_system_backend.h"
#include "content/public/test/test_file_system_context.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "webkit/browser/fileapi/async_file_test_helper.h"
#include "webkit/browser/fileapi/copy_or_move_file_validator.h"
#include "webkit/browser/fileapi/external_mount_points.h"
#include "webkit/browser/fileapi/file_system_backend.h"
#include "webkit/browser/fileapi/file_system_context.h"
#include "webkit/browser/fileapi/file_system_url.h"
#include "webkit/browser/fileapi/isolated_context.h"
-#include "webkit/browser/quota/mock_special_storage_policy.h"
#include "webkit/common/blob/shareable_file_reference.h"
#include "webkit/common/fileapi/file_system_util.h"
-namespace fileapi {
+using content::AsyncFileTestHelper;
+using fileapi::CopyOrMoveFileValidator;
+using fileapi::CopyOrMoveFileValidatorFactory;
+using fileapi::FileSystemURL;
+
+namespace content {
namespace {
-const FileSystemType kNoValidatorType = kFileSystemTypeTemporary;
-const FileSystemType kWithValidatorType = kFileSystemTypeTest;
+const fileapi::FileSystemType kNoValidatorType =
+ fileapi::kFileSystemTypeTemporary;
+const fileapi::FileSystemType kWithValidatorType = fileapi::kFileSystemTypeTest;
void ExpectOk(const GURL& origin_url,
const std::string& name,
- base::PlatformFileError error) {
- ASSERT_EQ(base::PLATFORM_FILE_OK, error);
+ base::File::Error error) {
+ ASSERT_EQ(base::File::FILE_OK, error);
}
class CopyOrMoveFileValidatorTestHelper {
public:
- CopyOrMoveFileValidatorTestHelper(
- const GURL& origin,
- FileSystemType src_type,
- FileSystemType dest_type)
- : origin_(origin),
- src_type_(src_type),
- dest_type_(dest_type) {}
+ CopyOrMoveFileValidatorTestHelper(const GURL& origin,
+ fileapi::FileSystemType src_type,
+ fileapi::FileSystemType dest_type)
+ : origin_(origin), src_type_(src_type), dest_type_(dest_type) {}
~CopyOrMoveFileValidatorTestHelper() {
file_system_context_ = NULL;
@@ -56,32 +59,32 @@ class CopyOrMoveFileValidatorTestHelper {
file_system_context_ = CreateFileSystemContextForTesting(NULL, base_dir);
// Set up TestFileSystemBackend to require CopyOrMoveFileValidator.
- FileSystemBackend* test_file_system_backend =
+ fileapi::FileSystemBackend* test_file_system_backend =
file_system_context_->GetFileSystemBackend(kWithValidatorType);
static_cast<TestFileSystemBackend*>(test_file_system_backend)->
set_require_copy_or_move_validator(true);
// Sets up source.
- FileSystemBackend* src_file_system_backend =
+ fileapi::FileSystemBackend* src_file_system_backend =
file_system_context_->GetFileSystemBackend(src_type_);
- src_file_system_backend->OpenFileSystem(
- origin_, src_type_,
- OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+ src_file_system_backend->ResolveURL(
+ FileSystemURL::CreateForTest(origin_, src_type_, base::FilePath()),
+ fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
base::Bind(&ExpectOk));
base::RunLoop().RunUntilIdle();
- ASSERT_EQ(base::PLATFORM_FILE_OK, CreateDirectory(SourceURL("")));
+ ASSERT_EQ(base::File::FILE_OK, CreateDirectory(SourceURL("")));
// Sets up dest.
DCHECK_EQ(kWithValidatorType, dest_type_);
- ASSERT_EQ(base::PLATFORM_FILE_OK, CreateDirectory(DestURL("")));
+ ASSERT_EQ(base::File::FILE_OK, CreateDirectory(DestURL("")));
copy_src_ = SourceURL("copy_src.jpg");
move_src_ = SourceURL("move_src.jpg");
copy_dest_ = DestURL("copy_dest.jpg");
move_dest_ = DestURL("move_dest.jpg");
- ASSERT_EQ(base::PLATFORM_FILE_OK, CreateFile(copy_src_, 10));
- ASSERT_EQ(base::PLATFORM_FILE_OK, CreateFile(move_src_, 10));
+ ASSERT_EQ(base::File::FILE_OK, CreateFile(copy_src_, 10));
+ ASSERT_EQ(base::File::FILE_OK, CreateFile(move_src_, 10));
ASSERT_TRUE(FileExists(copy_src_, 10));
ASSERT_TRUE(FileExists(move_src_, 10));
@@ -90,13 +93,13 @@ class CopyOrMoveFileValidatorTestHelper {
}
void SetMediaCopyOrMoveFileValidatorFactory(
- scoped_ptr<CopyOrMoveFileValidatorFactory> factory) {
+ scoped_ptr<fileapi::CopyOrMoveFileValidatorFactory> factory) {
TestFileSystemBackend* backend = static_cast<TestFileSystemBackend*>(
file_system_context_->GetFileSystemBackend(kWithValidatorType));
backend->InitializeCopyOrMoveFileValidatorFactory(factory.Pass());
}
- void CopyTest(base::PlatformFileError expected) {
+ void CopyTest(base::File::Error expected) {
ASSERT_TRUE(FileExists(copy_src_, 10));
ASSERT_FALSE(FileExists(copy_dest_, 10));
@@ -105,13 +108,13 @@ class CopyOrMoveFileValidatorTestHelper {
file_system_context_.get(), copy_src_, copy_dest_));
EXPECT_TRUE(FileExists(copy_src_, 10));
- if (expected == base::PLATFORM_FILE_OK)
+ if (expected == base::File::FILE_OK)
EXPECT_TRUE(FileExists(copy_dest_, 10));
else
EXPECT_FALSE(FileExists(copy_dest_, 10));
};
- void MoveTest(base::PlatformFileError expected) {
+ void MoveTest(base::File::Error expected) {
ASSERT_TRUE(FileExists(move_src_, 10));
ASSERT_FALSE(FileExists(move_dest_, 10));
@@ -119,7 +122,7 @@ class CopyOrMoveFileValidatorTestHelper {
AsyncFileTestHelper::Move(
file_system_context_.get(), move_src_, move_dest_));
- if (expected == base::PLATFORM_FILE_OK) {
+ if (expected == base::File::FILE_OK) {
EXPECT_FALSE(FileExists(move_src_, 10));
EXPECT_TRUE(FileExists(move_dest_, 10));
} else {
@@ -141,16 +144,16 @@ class CopyOrMoveFileValidatorTestHelper {
base::FilePath().AppendASCII("dest").AppendASCII(path));
}
- base::PlatformFileError CreateFile(const FileSystemURL& url, size_t size) {
- base::PlatformFileError result =
+ base::File::Error CreateFile(const FileSystemURL& url, size_t size) {
+ base::File::Error result =
AsyncFileTestHelper::CreateFile(file_system_context_.get(), url);
- if (result != base::PLATFORM_FILE_OK)
+ if (result != base::File::FILE_OK)
return result;
return AsyncFileTestHelper::TruncateFile(
file_system_context_.get(), url, size);
}
- base::PlatformFileError CreateDirectory(const FileSystemURL& url) {
+ base::File::Error CreateDirectory(const FileSystemURL& url) {
return AsyncFileTestHelper::CreateDirectory(file_system_context_.get(),
url);
}
@@ -164,13 +167,13 @@ class CopyOrMoveFileValidatorTestHelper {
const GURL origin_;
- const FileSystemType src_type_;
- const FileSystemType dest_type_;
+ const fileapi::FileSystemType src_type_;
+ const fileapi::FileSystemType dest_type_;
std::string src_fsid_;
std::string dest_fsid_;
base::MessageLoop message_loop_;
- scoped_refptr<FileSystemContext> file_system_context_;
+ scoped_refptr<fileapi::FileSystemContext> file_system_context_;
FileSystemURL copy_src_;
FileSystemURL copy_dest_;
@@ -188,7 +191,7 @@ enum Validity {
};
class TestCopyOrMoveFileValidatorFactory
- : public CopyOrMoveFileValidatorFactory {
+ : public fileapi::CopyOrMoveFileValidatorFactory {
public:
// A factory that creates validators that accept everything or nothing.
// TODO(gbillock): switch args to enum or something
@@ -196,7 +199,7 @@ class TestCopyOrMoveFileValidatorFactory
: validity_(validity) {}
virtual ~TestCopyOrMoveFileValidatorFactory() {}
- virtual CopyOrMoveFileValidator* CreateCopyOrMoveFileValidator(
+ virtual fileapi::CopyOrMoveFileValidator* CreateCopyOrMoveFileValidator(
const FileSystemURL& /*src_url*/,
const base::FilePath& /*platform_path*/) OVERRIDE {
return new TestCopyOrMoveFileValidator(validity_);
@@ -206,12 +209,12 @@ class TestCopyOrMoveFileValidatorFactory
class TestCopyOrMoveFileValidator : public CopyOrMoveFileValidator {
public:
explicit TestCopyOrMoveFileValidator(Validity validity)
- : result_(validity == VALID || validity == POST_WRITE_INVALID
- ? base::PLATFORM_FILE_OK
- : base::PLATFORM_FILE_ERROR_SECURITY),
- write_result_(validity == VALID || validity == PRE_WRITE_INVALID
- ? base::PLATFORM_FILE_OK
- : base::PLATFORM_FILE_ERROR_SECURITY) {
+ : result_(validity == VALID || validity == POST_WRITE_INVALID ?
+ base::File::FILE_OK :
+ base::File::FILE_ERROR_SECURITY),
+ write_result_(validity == VALID || validity == PRE_WRITE_INVALID ?
+ base::File::FILE_OK :
+ base::File::FILE_ERROR_SECURITY) {
}
virtual ~TestCopyOrMoveFileValidator() {}
@@ -231,8 +234,8 @@ class TestCopyOrMoveFileValidatorFactory
}
private:
- base::PlatformFileError result_;
- base::PlatformFileError write_result_;
+ base::File::Error result_;
+ base::File::Error write_result_;
DISALLOW_COPY_AND_ASSIGN(TestCopyOrMoveFileValidator);
};
@@ -251,8 +254,8 @@ TEST(CopyOrMoveFileValidatorTest, NoValidatorWithinSameFSType) {
kWithValidatorType,
kWithValidatorType);
helper.SetUp();
- helper.CopyTest(base::PLATFORM_FILE_OK);
- helper.MoveTest(base::PLATFORM_FILE_OK);
+ helper.CopyTest(base::File::FILE_OK);
+ helper.MoveTest(base::File::FILE_OK);
}
TEST(CopyOrMoveFileValidatorTest, MissingValidator) {
@@ -262,8 +265,8 @@ TEST(CopyOrMoveFileValidatorTest, MissingValidator) {
kNoValidatorType,
kWithValidatorType);
helper.SetUp();
- helper.CopyTest(base::PLATFORM_FILE_ERROR_SECURITY);
- helper.MoveTest(base::PLATFORM_FILE_ERROR_SECURITY);
+ helper.CopyTest(base::File::FILE_ERROR_SECURITY);
+ helper.MoveTest(base::File::FILE_ERROR_SECURITY);
}
TEST(CopyOrMoveFileValidatorTest, AcceptAll) {
@@ -275,8 +278,8 @@ TEST(CopyOrMoveFileValidatorTest, AcceptAll) {
new TestCopyOrMoveFileValidatorFactory(VALID));
helper.SetMediaCopyOrMoveFileValidatorFactory(factory.Pass());
- helper.CopyTest(base::PLATFORM_FILE_OK);
- helper.MoveTest(base::PLATFORM_FILE_OK);
+ helper.CopyTest(base::File::FILE_OK);
+ helper.MoveTest(base::File::FILE_OK);
}
TEST(CopyOrMoveFileValidatorTest, AcceptNone) {
@@ -288,8 +291,8 @@ TEST(CopyOrMoveFileValidatorTest, AcceptNone) {
new TestCopyOrMoveFileValidatorFactory(PRE_WRITE_INVALID));
helper.SetMediaCopyOrMoveFileValidatorFactory(factory.Pass());
- helper.CopyTest(base::PLATFORM_FILE_ERROR_SECURITY);
- helper.MoveTest(base::PLATFORM_FILE_ERROR_SECURITY);
+ helper.CopyTest(base::File::FILE_ERROR_SECURITY);
+ helper.MoveTest(base::File::FILE_ERROR_SECURITY);
}
TEST(CopyOrMoveFileValidatorTest, OverrideValidator) {
@@ -306,8 +309,8 @@ TEST(CopyOrMoveFileValidatorTest, OverrideValidator) {
new TestCopyOrMoveFileValidatorFactory(VALID));
helper.SetMediaCopyOrMoveFileValidatorFactory(accept_factory.Pass());
- helper.CopyTest(base::PLATFORM_FILE_ERROR_SECURITY);
- helper.MoveTest(base::PLATFORM_FILE_ERROR_SECURITY);
+ helper.CopyTest(base::File::FILE_ERROR_SECURITY);
+ helper.MoveTest(base::File::FILE_ERROR_SECURITY);
}
TEST(CopyOrMoveFileValidatorTest, RejectPostWrite) {
@@ -319,8 +322,8 @@ TEST(CopyOrMoveFileValidatorTest, RejectPostWrite) {
new TestCopyOrMoveFileValidatorFactory(POST_WRITE_INVALID));
helper.SetMediaCopyOrMoveFileValidatorFactory(factory.Pass());
- helper.CopyTest(base::PLATFORM_FILE_ERROR_SECURITY);
- helper.MoveTest(base::PLATFORM_FILE_ERROR_SECURITY);
+ helper.CopyTest(base::File::FILE_ERROR_SECURITY);
+ helper.MoveTest(base::File::FILE_ERROR_SECURITY);
}
-} // namespace fileapi
+} // namespace content
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 eb002ab0a0c..f2c00fcf9df 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
@@ -12,11 +12,14 @@
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/stl_util.h"
+#include "content/browser/quota/mock_quota_manager.h"
+#include "content/browser/quota/mock_quota_manager_proxy.h"
+#include "content/public/test/async_file_test_helper.h"
#include "content/public/test/test_file_system_backend.h"
#include "content/public/test/test_file_system_context.h"
+#include "content/test/fileapi_test_file_set.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "webkit/browser/blob/file_stream_reader.h"
-#include "webkit/browser/fileapi/async_file_test_helper.h"
#include "webkit/browser/fileapi/copy_or_move_file_validator.h"
#include "webkit/browser/fileapi/copy_or_move_operation_delegate.h"
#include "webkit/browser/fileapi/file_stream_writer.h"
@@ -24,30 +27,34 @@
#include "webkit/browser/fileapi/file_system_context.h"
#include "webkit/browser/fileapi/file_system_operation.h"
#include "webkit/browser/fileapi/file_system_url.h"
-#include "webkit/browser/fileapi/test_file_set.h"
-#include "webkit/browser/quota/mock_quota_manager.h"
#include "webkit/browser/quota/quota_manager.h"
#include "webkit/common/fileapi/file_system_util.h"
-namespace fileapi {
+using content::AsyncFileTestHelper;
+using fileapi::CopyOrMoveOperationDelegate;
+using fileapi::FileStreamWriter;
+using fileapi::FileSystemOperation;
+using fileapi::FileSystemURL;
-typedef FileSystemOperation::FileEntryList FileEntryList;
+namespace content {
+
+typedef fileapi::FileSystemOperation::FileEntryList FileEntryList;
namespace {
void ExpectOk(const GURL& origin_url,
const std::string& name,
- base::PlatformFileError error) {
- ASSERT_EQ(base::PLATFORM_FILE_OK, error);
+ base::File::Error error) {
+ ASSERT_EQ(base::File::FILE_OK, error);
}
-class TestValidatorFactory : public CopyOrMoveFileValidatorFactory {
+class TestValidatorFactory : public fileapi::CopyOrMoveFileValidatorFactory {
public:
// A factory that creates validators that accept everything or nothing.
TestValidatorFactory() {}
virtual ~TestValidatorFactory() {}
- virtual CopyOrMoveFileValidator* CreateCopyOrMoveFileValidator(
+ virtual fileapi::CopyOrMoveFileValidator* CreateCopyOrMoveFileValidator(
const FileSystemURL& /*src_url*/,
const base::FilePath& /*platform_path*/) OVERRIDE {
// Move arg management to TestValidator?
@@ -55,15 +62,15 @@ class TestValidatorFactory : public CopyOrMoveFileValidatorFactory {
}
private:
- class TestValidator : public CopyOrMoveFileValidator {
+ class TestValidator : public fileapi::CopyOrMoveFileValidator {
public:
explicit TestValidator(bool pre_copy_valid,
bool post_copy_valid,
const std::string& reject_string)
- : result_(pre_copy_valid ? base::PLATFORM_FILE_OK
- : base::PLATFORM_FILE_ERROR_SECURITY),
- write_result_(post_copy_valid ? base::PLATFORM_FILE_OK
- : base::PLATFORM_FILE_ERROR_SECURITY),
+ : result_(pre_copy_valid ? base::File::FILE_OK :
+ base::File::FILE_ERROR_SECURITY),
+ write_result_(post_copy_valid ? base::File::FILE_OK :
+ base::File::FILE_ERROR_SECURITY),
reject_string_(reject_string) {
}
virtual ~TestValidator() {}
@@ -78,10 +85,10 @@ class TestValidatorFactory : public CopyOrMoveFileValidatorFactory {
virtual void StartPostWriteValidation(
const base::FilePath& dest_platform_path,
const ResultCallback& result_callback) OVERRIDE {
- base::PlatformFileError result = write_result_;
+ base::File::Error result = write_result_;
std::string unsafe = dest_platform_path.BaseName().AsUTF8Unsafe();
if (unsafe.find(reject_string_) != std::string::npos) {
- result = base::PLATFORM_FILE_ERROR_SECURITY;
+ result = base::File::FILE_ERROR_SECURITY;
}
// Post the result since a real validator must do work asynchronously.
base::MessageLoop::current()->PostTask(
@@ -89,8 +96,8 @@ class TestValidatorFactory : public CopyOrMoveFileValidatorFactory {
}
private:
- base::PlatformFileError result_;
- base::PlatformFileError write_result_;
+ base::File::Error result_;
+ base::File::Error write_result_;
std::string reject_string_;
DISALLOW_COPY_AND_ASSIGN(TestValidator);
@@ -99,14 +106,14 @@ class TestValidatorFactory : public CopyOrMoveFileValidatorFactory {
// Records CopyProgressCallback invocations.
struct ProgressRecord {
- FileSystemOperation::CopyProgressType type;
+ fileapi::FileSystemOperation::CopyProgressType type;
FileSystemURL source_url;
FileSystemURL dest_url;
int64 size;
};
void RecordProgressCallback(std::vector<ProgressRecord>* records,
- FileSystemOperation::CopyProgressType type,
+ fileapi::FileSystemOperation::CopyProgressType type,
const FileSystemURL& source_url,
const FileSystemURL& dest_url,
int64 size) {
@@ -124,8 +131,8 @@ void RecordFileProgressCallback(std::vector<int64>* records,
}
void AssignAndQuit(base::RunLoop* run_loop,
- base::PlatformFileError* result_out,
- base::PlatformFileError result) {
+ base::File::Error* result_out,
+ base::File::Error result) {
*result_out = result;
run_loop->Quit();
}
@@ -157,13 +164,10 @@ class ScopedThreadStopper {
class CopyOrMoveOperationTestHelper {
public:
- CopyOrMoveOperationTestHelper(
- const GURL& origin,
- FileSystemType src_type,
- FileSystemType dest_type)
- : origin_(origin),
- src_type_(src_type),
- dest_type_(dest_type) {}
+ CopyOrMoveOperationTestHelper(const GURL& origin,
+ fileapi::FileSystemType src_type,
+ fileapi::FileSystemType dest_type)
+ : origin_(origin), src_type_(src_type), dest_type_(dest_type) {}
~CopyOrMoveOperationTestHelper() {
file_system_context_ = NULL;
@@ -186,45 +190,49 @@ class CopyOrMoveOperationTestHelper {
ASSERT_TRUE(base_.CreateUniqueTempDir());
base::FilePath base_dir = base_.path();
quota_manager_ =
- new quota::MockQuotaManager(false /* is_incognito */,
+ new MockQuotaManager(false /* is_incognito */,
base_dir,
base::MessageLoopProxy::current().get(),
base::MessageLoopProxy::current().get(),
NULL /* special storage policy */);
- quota_manager_proxy_ = new quota::MockQuotaManagerProxy(
+ quota_manager_proxy_ = new MockQuotaManagerProxy(
quota_manager_.get(), base::MessageLoopProxy::current().get());
file_system_context_ =
CreateFileSystemContextForTesting(quota_manager_proxy_.get(), base_dir);
// Prepare the origin's root directory.
- FileSystemBackend* backend =
+ fileapi::FileSystemBackend* backend =
file_system_context_->GetFileSystemBackend(src_type_);
- backend->OpenFileSystem(origin_, src_type_,
- OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
- base::Bind(&ExpectOk));
+ backend->ResolveURL(
+ FileSystemURL::CreateForTest(origin_, src_type_, base::FilePath()),
+ fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+ base::Bind(&ExpectOk));
backend = file_system_context_->GetFileSystemBackend(dest_type_);
- if (dest_type_ == kFileSystemTypeTest) {
+ if (dest_type_ == fileapi::kFileSystemTypeTest) {
TestFileSystemBackend* test_backend =
static_cast<TestFileSystemBackend*>(backend);
- scoped_ptr<CopyOrMoveFileValidatorFactory> factory(
+ scoped_ptr<fileapi::CopyOrMoveFileValidatorFactory> factory(
new TestValidatorFactory);
test_backend->set_require_copy_or_move_validator(
require_copy_or_move_validator);
if (init_copy_or_move_validator)
test_backend->InitializeCopyOrMoveFileValidatorFactory(factory.Pass());
}
- backend->OpenFileSystem(origin_, dest_type_,
- OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
- base::Bind(&ExpectOk));
+ backend->ResolveURL(
+ FileSystemURL::CreateForTest(origin_, dest_type_, base::FilePath()),
+ fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+ base::Bind(&ExpectOk));
base::RunLoop().RunUntilIdle();
// Grant relatively big quota initially.
- quota_manager_->SetQuota(origin_,
- FileSystemTypeToQuotaStorageType(src_type_),
- 1024 * 1024);
- quota_manager_->SetQuota(origin_,
- FileSystemTypeToQuotaStorageType(dest_type_),
- 1024 * 1024);
+ quota_manager_->SetQuota(
+ origin_,
+ fileapi::FileSystemTypeToQuotaStorageType(src_type_),
+ 1024 * 1024);
+ quota_manager_->SetQuota(
+ origin_,
+ fileapi::FileSystemTypeToQuotaStorageType(dest_type_),
+ 1024 * 1024);
}
int64 GetSourceUsage() {
@@ -249,12 +257,12 @@ class CopyOrMoveOperationTestHelper {
origin_, dest_type_, base::FilePath::FromUTF8Unsafe(path));
}
- base::PlatformFileError Copy(const FileSystemURL& src,
- const FileSystemURL& dest) {
+ base::File::Error Copy(const FileSystemURL& src,
+ const FileSystemURL& dest) {
return AsyncFileTestHelper::Copy(file_system_context_.get(), src, dest);
}
- base::PlatformFileError CopyWithProgress(
+ base::File::Error CopyWithProgress(
const FileSystemURL& src,
const FileSystemURL& dest,
const AsyncFileTestHelper::CopyProgressCallback& progress_callback) {
@@ -262,18 +270,18 @@ class CopyOrMoveOperationTestHelper {
file_system_context_.get(), src, dest, progress_callback);
}
- base::PlatformFileError Move(const FileSystemURL& src,
- const FileSystemURL& dest) {
+ base::File::Error Move(const FileSystemURL& src,
+ const FileSystemURL& dest) {
return AsyncFileTestHelper::Move(file_system_context_.get(), src, dest);
}
- base::PlatformFileError SetUpTestCaseFiles(
+ base::File::Error SetUpTestCaseFiles(
const FileSystemURL& root,
- const test::TestCaseRecord* const test_cases,
+ const FileSystemTestCaseRecord* const test_cases,
size_t test_case_size) {
- base::PlatformFileError result = base::PLATFORM_FILE_ERROR_FAILED;
+ base::File::Error result = base::File::FILE_ERROR_FAILED;
for (size_t i = 0; i < test_case_size; ++i) {
- const test::TestCaseRecord& test_case = test_cases[i];
+ const FileSystemTestCaseRecord& test_case = test_cases[i];
FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
root.origin(),
root.mount_type(),
@@ -282,8 +290,8 @@ class CopyOrMoveOperationTestHelper {
result = CreateDirectory(url);
else
result = CreateFile(url, test_case.data_file_size);
- EXPECT_EQ(base::PLATFORM_FILE_OK, result) << url.DebugString();
- if (result != base::PLATFORM_FILE_OK)
+ EXPECT_EQ(base::File::FILE_OK, result) << url.DebugString();
+ if (result != base::File::FILE_OK)
return result;
}
return result;
@@ -291,9 +299,9 @@ class CopyOrMoveOperationTestHelper {
void VerifyTestCaseFiles(
const FileSystemURL& root,
- const test::TestCaseRecord* const test_cases,
+ const FileSystemTestCaseRecord* const test_cases,
size_t test_case_size) {
- std::map<base::FilePath, const test::TestCaseRecord*> test_case_map;
+ std::map<base::FilePath, const FileSystemTestCaseRecord*> test_case_map;
for (size_t i = 0; i < test_case_size; ++i) {
test_case_map[
base::FilePath(test_cases[i].path).NormalizePathSeparators()] =
@@ -306,7 +314,7 @@ class CopyOrMoveOperationTestHelper {
while (!directories.empty()) {
FileSystemURL dir = directories.front();
directories.pop();
- ASSERT_EQ(base::PLATFORM_FILE_OK, ReadDirectory(dir, &entries));
+ ASSERT_EQ(base::File::FILE_OK, ReadDirectory(dir, &entries));
for (size_t i = 0; i < entries.size(); ++i) {
FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
dir.origin(),
@@ -327,27 +335,28 @@ class CopyOrMoveOperationTestHelper {
}
}
EXPECT_TRUE(test_case_map.empty());
- std::map<base::FilePath, const test::TestCaseRecord*>::const_iterator it;
+ std::map<base::FilePath,
+ const FileSystemTestCaseRecord*>::const_iterator it;
for (it = test_case_map.begin(); it != test_case_map.end(); ++it) {
LOG(ERROR) << "Extra entry: " << it->first.LossyDisplayName();
}
}
- base::PlatformFileError ReadDirectory(const FileSystemURL& url,
- FileEntryList* entries) {
+ base::File::Error ReadDirectory(const FileSystemURL& url,
+ FileEntryList* entries) {
return AsyncFileTestHelper::ReadDirectory(
file_system_context_.get(), url, entries);
}
- base::PlatformFileError CreateDirectory(const FileSystemURL& url) {
+ base::File::Error CreateDirectory(const FileSystemURL& url) {
return AsyncFileTestHelper::CreateDirectory(file_system_context_.get(),
url);
}
- base::PlatformFileError CreateFile(const FileSystemURL& url, size_t size) {
- base::PlatformFileError result =
+ base::File::Error CreateFile(const FileSystemURL& url, size_t size) {
+ base::File::Error result =
AsyncFileTestHelper::CreateFile(file_system_context_.get(), url);
- if (result != base::PLATFORM_FILE_OK)
+ if (result != base::File::FILE_OK)
return result;
return AsyncFileTestHelper::TruncateFile(
file_system_context_.get(), url, size);
@@ -364,7 +373,9 @@ class CopyOrMoveOperationTestHelper {
}
private:
- void GetUsageAndQuota(FileSystemType type, int64* usage, int64* quota) {
+ void GetUsageAndQuota(fileapi::FileSystemType type,
+ int64* usage,
+ int64* quota) {
quota::QuotaStatusCode status = AsyncFileTestHelper::GetUsageAndQuota(
quota_manager_.get(), origin_, type, usage, quota);
ASSERT_EQ(quota::kQuotaStatusOk, status);
@@ -374,21 +385,21 @@ class CopyOrMoveOperationTestHelper {
base::ScopedTempDir base_;
const GURL origin_;
- const FileSystemType src_type_;
- const FileSystemType dest_type_;
+ const fileapi::FileSystemType src_type_;
+ const fileapi::FileSystemType dest_type_;
base::MessageLoopForIO message_loop_;
- scoped_refptr<FileSystemContext> file_system_context_;
- scoped_refptr<quota::MockQuotaManagerProxy> quota_manager_proxy_;
- scoped_refptr<quota::MockQuotaManager> quota_manager_;
+ scoped_refptr<fileapi::FileSystemContext> file_system_context_;
+ scoped_refptr<MockQuotaManagerProxy> quota_manager_proxy_;
+ scoped_refptr<MockQuotaManager> quota_manager_;
DISALLOW_COPY_AND_ASSIGN(CopyOrMoveOperationTestHelper);
};
TEST(LocalFileSystemCopyOrMoveOperationTest, CopySingleFile) {
CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
- kFileSystemTypeTemporary,
- kFileSystemTypePersistent);
+ fileapi::kFileSystemTypeTemporary,
+ fileapi::kFileSystemTypePersistent);
helper.SetUp();
FileSystemURL src = helper.SourceURL("a");
@@ -397,11 +408,11 @@ TEST(LocalFileSystemCopyOrMoveOperationTest, CopySingleFile) {
int64 dest_initial_usage = helper.GetDestUsage();
// Set up a source file.
- ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateFile(src, 10));
+ ASSERT_EQ(base::File::FILE_OK, helper.CreateFile(src, 10));
int64 src_increase = helper.GetSourceUsage() - src_initial_usage;
// Copy it.
- ASSERT_EQ(base::PLATFORM_FILE_OK, helper.Copy(src, dest));
+ ASSERT_EQ(base::File::FILE_OK, helper.Copy(src, dest));
// Verify.
ASSERT_TRUE(helper.FileExists(src, 10));
@@ -416,8 +427,8 @@ TEST(LocalFileSystemCopyOrMoveOperationTest, CopySingleFile) {
TEST(LocalFileSystemCopyOrMoveOperationTest, MoveSingleFile) {
CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
- kFileSystemTypeTemporary,
- kFileSystemTypePersistent);
+ fileapi::kFileSystemTypeTemporary,
+ fileapi::kFileSystemTypePersistent);
helper.SetUp();
FileSystemURL src = helper.SourceURL("a");
@@ -426,11 +437,11 @@ TEST(LocalFileSystemCopyOrMoveOperationTest, MoveSingleFile) {
int64 dest_initial_usage = helper.GetDestUsage();
// Set up a source file.
- ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateFile(src, 10));
+ ASSERT_EQ(base::File::FILE_OK, helper.CreateFile(src, 10));
int64 src_increase = helper.GetSourceUsage() - src_initial_usage;
// Move it.
- ASSERT_EQ(base::PLATFORM_FILE_OK, helper.Move(src, dest));
+ ASSERT_EQ(base::File::FILE_OK, helper.Move(src, dest));
// Verify.
ASSERT_FALSE(helper.FileExists(src, AsyncFileTestHelper::kDontCheckSize));
@@ -445,8 +456,8 @@ TEST(LocalFileSystemCopyOrMoveOperationTest, MoveSingleFile) {
TEST(LocalFileSystemCopyOrMoveOperationTest, CopySingleDirectory) {
CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
- kFileSystemTypeTemporary,
- kFileSystemTypePersistent);
+ fileapi::kFileSystemTypeTemporary,
+ fileapi::kFileSystemTypePersistent);
helper.SetUp();
FileSystemURL src = helper.SourceURL("a");
@@ -455,11 +466,11 @@ TEST(LocalFileSystemCopyOrMoveOperationTest, CopySingleDirectory) {
int64 dest_initial_usage = helper.GetDestUsage();
// Set up a source directory.
- ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateDirectory(src));
+ ASSERT_EQ(base::File::FILE_OK, helper.CreateDirectory(src));
int64 src_increase = helper.GetSourceUsage() - src_initial_usage;
// Copy it.
- ASSERT_EQ(base::PLATFORM_FILE_OK, helper.Copy(src, dest));
+ ASSERT_EQ(base::File::FILE_OK, helper.Copy(src, dest));
// Verify.
ASSERT_TRUE(helper.DirectoryExists(src));
@@ -474,8 +485,8 @@ TEST(LocalFileSystemCopyOrMoveOperationTest, CopySingleDirectory) {
TEST(LocalFileSystemCopyOrMoveOperationTest, MoveSingleDirectory) {
CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
- kFileSystemTypeTemporary,
- kFileSystemTypePersistent);
+ fileapi::kFileSystemTypeTemporary,
+ fileapi::kFileSystemTypePersistent);
helper.SetUp();
FileSystemURL src = helper.SourceURL("a");
@@ -484,11 +495,11 @@ TEST(LocalFileSystemCopyOrMoveOperationTest, MoveSingleDirectory) {
int64 dest_initial_usage = helper.GetDestUsage();
// Set up a source directory.
- ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateDirectory(src));
+ ASSERT_EQ(base::File::FILE_OK, helper.CreateDirectory(src));
int64 src_increase = helper.GetSourceUsage() - src_initial_usage;
// Move it.
- ASSERT_EQ(base::PLATFORM_FILE_OK, helper.Move(src, dest));
+ ASSERT_EQ(base::File::FILE_OK, helper.Move(src, dest));
// Verify.
ASSERT_FALSE(helper.DirectoryExists(src));
@@ -503,8 +514,8 @@ TEST(LocalFileSystemCopyOrMoveOperationTest, MoveSingleDirectory) {
TEST(LocalFileSystemCopyOrMoveOperationTest, CopyDirectory) {
CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
- kFileSystemTypeTemporary,
- kFileSystemTypePersistent);
+ fileapi::kFileSystemTypeTemporary,
+ fileapi::kFileSystemTypePersistent);
helper.SetUp();
FileSystemURL src = helper.SourceURL("a");
@@ -513,15 +524,15 @@ TEST(LocalFileSystemCopyOrMoveOperationTest, CopyDirectory) {
int64 dest_initial_usage = helper.GetDestUsage();
// Set up a source directory.
- ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateDirectory(src));
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK, helper.CreateDirectory(src));
+ ASSERT_EQ(base::File::FILE_OK,
helper.SetUpTestCaseFiles(src,
- test::kRegularTestCases,
- test::kRegularTestCaseSize));
+ kRegularFileSystemTestCases,
+ kRegularFileSystemTestCaseSize));
int64 src_increase = helper.GetSourceUsage() - src_initial_usage;
// Copy it.
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
helper.CopyWithProgress(
src, dest,
AsyncFileTestHelper::CopyProgressCallback()));
@@ -531,8 +542,8 @@ TEST(LocalFileSystemCopyOrMoveOperationTest, CopyDirectory) {
ASSERT_TRUE(helper.DirectoryExists(dest));
helper.VerifyTestCaseFiles(dest,
- test::kRegularTestCases,
- test::kRegularTestCaseSize);
+ kRegularFileSystemTestCases,
+ kRegularFileSystemTestCaseSize);
int64 src_new_usage = helper.GetSourceUsage();
ASSERT_EQ(src_initial_usage + src_increase, src_new_usage);
@@ -543,8 +554,8 @@ TEST(LocalFileSystemCopyOrMoveOperationTest, CopyDirectory) {
TEST(LocalFileSystemCopyOrMoveOperationTest, MoveDirectory) {
CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
- kFileSystemTypeTemporary,
- kFileSystemTypePersistent);
+ fileapi::kFileSystemTypeTemporary,
+ fileapi::kFileSystemTypePersistent);
helper.SetUp();
FileSystemURL src = helper.SourceURL("a");
@@ -553,23 +564,23 @@ TEST(LocalFileSystemCopyOrMoveOperationTest, MoveDirectory) {
int64 dest_initial_usage = helper.GetDestUsage();
// Set up a source directory.
- ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateDirectory(src));
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK, helper.CreateDirectory(src));
+ ASSERT_EQ(base::File::FILE_OK,
helper.SetUpTestCaseFiles(src,
- test::kRegularTestCases,
- test::kRegularTestCaseSize));
+ kRegularFileSystemTestCases,
+ kRegularFileSystemTestCaseSize));
int64 src_increase = helper.GetSourceUsage() - src_initial_usage;
// Move it.
- ASSERT_EQ(base::PLATFORM_FILE_OK, helper.Move(src, dest));
+ ASSERT_EQ(base::File::FILE_OK, helper.Move(src, dest));
// Verify.
ASSERT_FALSE(helper.DirectoryExists(src));
ASSERT_TRUE(helper.DirectoryExists(dest));
helper.VerifyTestCaseFiles(dest,
- test::kRegularTestCases,
- test::kRegularTestCaseSize);
+ kRegularFileSystemTestCases,
+ kRegularFileSystemTestCaseSize);
int64 src_new_usage = helper.GetSourceUsage();
ASSERT_EQ(src_initial_usage, src_new_usage);
@@ -581,19 +592,19 @@ TEST(LocalFileSystemCopyOrMoveOperationTest, MoveDirectory) {
TEST(LocalFileSystemCopyOrMoveOperationTest,
MoveDirectoryFailPostWriteValidation) {
CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
- kFileSystemTypeTemporary,
- kFileSystemTypeTest);
+ fileapi::kFileSystemTypeTemporary,
+ fileapi::kFileSystemTypeTest);
helper.SetUp();
FileSystemURL src = helper.SourceURL("a");
FileSystemURL dest = helper.DestURL("b");
// Set up a source directory.
- ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateDirectory(src));
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK, helper.CreateDirectory(src));
+ ASSERT_EQ(base::File::FILE_OK,
helper.SetUpTestCaseFiles(src,
- test::kRegularTestCases,
- test::kRegularTestCaseSize));
+ kRegularFileSystemTestCases,
+ kRegularFileSystemTestCaseSize));
// Move it.
helper.Move(src, dest);
@@ -602,7 +613,7 @@ TEST(LocalFileSystemCopyOrMoveOperationTest,
ASSERT_TRUE(helper.DirectoryExists(src));
ASSERT_TRUE(helper.DirectoryExists(dest));
- test::TestCaseRecord kMoveDirResultCases[] = {
+ FileSystemTestCaseRecord kMoveDirResultCases[] = {
{false, FILE_PATH_LITERAL("file 0"), 38},
{false, FILE_PATH_LITERAL("file 3"), 0},
};
@@ -614,47 +625,47 @@ TEST(LocalFileSystemCopyOrMoveOperationTest,
TEST(LocalFileSystemCopyOrMoveOperationTest, CopySingleFileNoValidator) {
CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
- kFileSystemTypeTemporary,
- kFileSystemTypeTest);
+ fileapi::kFileSystemTypeTemporary,
+ fileapi::kFileSystemTypeTest);
helper.SetUpNoValidator();
FileSystemURL src = helper.SourceURL("a");
FileSystemURL dest = helper.DestURL("b");
// Set up a source file.
- ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateFile(src, 10));
+ ASSERT_EQ(base::File::FILE_OK, helper.CreateFile(src, 10));
// The copy attempt should fail with a security error -- getting
// the factory returns a security error, and the copy operation must
// respect that.
- ASSERT_EQ(base::PLATFORM_FILE_ERROR_SECURITY, helper.Copy(src, dest));
+ ASSERT_EQ(base::File::FILE_ERROR_SECURITY, helper.Copy(src, dest));
}
TEST(LocalFileSystemCopyOrMoveOperationTest, ProgressCallback) {
CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
- kFileSystemTypeTemporary,
- kFileSystemTypePersistent);
+ fileapi::kFileSystemTypeTemporary,
+ fileapi::kFileSystemTypePersistent);
helper.SetUp();
FileSystemURL src = helper.SourceURL("a");
FileSystemURL dest = helper.DestURL("b");
// Set up a source directory.
- ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateDirectory(src));
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK, helper.CreateDirectory(src));
+ ASSERT_EQ(base::File::FILE_OK,
helper.SetUpTestCaseFiles(src,
- test::kRegularTestCases,
- test::kRegularTestCaseSize));
+ kRegularFileSystemTestCases,
+ kRegularFileSystemTestCaseSize));
std::vector<ProgressRecord> records;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
helper.CopyWithProgress(src, dest,
base::Bind(&RecordProgressCallback,
base::Unretained(&records))));
// Verify progress callback.
- for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) {
- const test::TestCaseRecord& test_case = test::kRegularTestCases[i];
+ for (size_t i = 0; i < kRegularFileSystemTestCaseSize; ++i) {
+ const FileSystemTestCaseRecord& test_case = kRegularFileSystemTestCases[i];
FileSystemURL src_url = helper.SourceURL(
std::string("a/") + base::FilePath(test_case.path).AsUTF8Unsafe());
@@ -705,13 +716,10 @@ TEST(LocalFileSystemCopyOrMoveOperationTest, StreamCopyHelper) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath source_path = temp_dir.path().AppendASCII("source");
- const char kTestData[] = "abcdefghijklmnopqrstuvwxyz0123456789";
- file_util::WriteFile(source_path, kTestData,
- arraysize(kTestData) - 1); // Exclude trailing '\0'.
-
base::FilePath dest_path = temp_dir.path().AppendASCII("dest");
- // LocalFileWriter requires the file exists. So create an empty file here.
- file_util::WriteFile(dest_path, "", 0);
+ const char kTestData[] = "abcdefghijklmnopqrstuvwxyz0123456789";
+ base::WriteFile(source_path, kTestData,
+ arraysize(kTestData) - 1); // Exclude trailing '\0'.
base::MessageLoopForIO message_loop;
base::Thread file_thread("file_thread");
@@ -726,8 +734,8 @@ TEST(LocalFileSystemCopyOrMoveOperationTest, StreamCopyHelper) {
webkit_blob::FileStreamReader::CreateForLocalFile(
task_runner.get(), source_path, 0, base::Time()));
- scoped_ptr<FileStreamWriter> writer(
- FileStreamWriter::CreateForLocalFile(task_runner.get(), dest_path, 0));
+ scoped_ptr<FileStreamWriter> writer(FileStreamWriter::CreateForLocalFile(
+ task_runner.get(), dest_path, 0, FileStreamWriter::CREATE_NEW_FILE));
std::vector<int64> progress;
CopyOrMoveOperationDelegate::StreamCopyHelper helper(
@@ -737,12 +745,12 @@ TEST(LocalFileSystemCopyOrMoveOperationTest, StreamCopyHelper) {
base::Bind(&RecordFileProgressCallback, base::Unretained(&progress)),
base::TimeDelta()); // For testing, we need all the progress.
- base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
+ base::File::Error error = base::File::FILE_ERROR_FAILED;
base::RunLoop run_loop;
helper.Run(base::Bind(&AssignAndQuit, &run_loop, &error));
run_loop.Run();
- EXPECT_EQ(base::PLATFORM_FILE_OK, error);
+ EXPECT_EQ(base::File::FILE_OK, error);
ASSERT_EQ(5U, progress.size());
EXPECT_EQ(0, progress[0]);
EXPECT_EQ(10, progress[1]);
@@ -763,13 +771,11 @@ TEST(LocalFileSystemCopyOrMoveOperationTest, StreamCopyHelperWithFlush) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath source_path = temp_dir.path().AppendASCII("source");
+ base::FilePath dest_path = temp_dir.path().AppendASCII("dest");
const char kTestData[] = "abcdefghijklmnopqrstuvwxyz0123456789";
- file_util::WriteFile(source_path, kTestData,
- arraysize(kTestData) - 1); // Exclude trailing '\0'.
+ base::WriteFile(source_path, kTestData,
+ arraysize(kTestData) - 1); // Exclude trailing '\0'.
- base::FilePath dest_path = temp_dir.path().AppendASCII("dest");
- // LocalFileWriter requires the file exists. So create an empty file here.
- file_util::WriteFile(dest_path, "", 0);
base::MessageLoopForIO message_loop;
base::Thread file_thread("file_thread");
@@ -784,8 +790,8 @@ TEST(LocalFileSystemCopyOrMoveOperationTest, StreamCopyHelperWithFlush) {
webkit_blob::FileStreamReader::CreateForLocalFile(
task_runner.get(), source_path, 0, base::Time()));
- scoped_ptr<FileStreamWriter> writer(
- FileStreamWriter::CreateForLocalFile(task_runner.get(), dest_path, 0));
+ scoped_ptr<FileStreamWriter> writer(FileStreamWriter::CreateForLocalFile(
+ task_runner.get(), dest_path, 0, FileStreamWriter::CREATE_NEW_FILE));
std::vector<int64> progress;
CopyOrMoveOperationDelegate::StreamCopyHelper helper(
@@ -795,12 +801,12 @@ TEST(LocalFileSystemCopyOrMoveOperationTest, StreamCopyHelperWithFlush) {
base::Bind(&RecordFileProgressCallback, base::Unretained(&progress)),
base::TimeDelta()); // For testing, we need all the progress.
- base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
+ base::File::Error error = base::File::FILE_ERROR_FAILED;
base::RunLoop run_loop;
helper.Run(base::Bind(&AssignAndQuit, &run_loop, &error));
run_loop.Run();
- EXPECT_EQ(base::PLATFORM_FILE_OK, error);
+ EXPECT_EQ(base::File::FILE_OK, error);
ASSERT_EQ(5U, progress.size());
EXPECT_EQ(0, progress[0]);
EXPECT_EQ(10, progress[1]);
@@ -817,13 +823,10 @@ TEST(LocalFileSystemCopyOrMoveOperationTest, StreamCopyHelper_Cancel) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath source_path = temp_dir.path().AppendASCII("source");
- const char kTestData[] = "abcdefghijklmnopqrstuvwxyz0123456789";
- file_util::WriteFile(source_path, kTestData,
- arraysize(kTestData) - 1); // Exclude trailing '\0'.
-
base::FilePath dest_path = temp_dir.path().AppendASCII("dest");
- // LocalFileWriter requires the file exists. So create an empty file here.
- file_util::WriteFile(dest_path, "", 0);
+ const char kTestData[] = "abcdefghijklmnopqrstuvwxyz0123456789";
+ base::WriteFile(source_path, kTestData,
+ arraysize(kTestData) - 1); // Exclude trailing '\0'.
base::MessageLoopForIO message_loop;
base::Thread file_thread("file_thread");
@@ -838,8 +841,8 @@ TEST(LocalFileSystemCopyOrMoveOperationTest, StreamCopyHelper_Cancel) {
webkit_blob::FileStreamReader::CreateForLocalFile(
task_runner.get(), source_path, 0, base::Time()));
- scoped_ptr<FileStreamWriter> writer(
- FileStreamWriter::CreateForLocalFile(task_runner.get(), dest_path, 0));
+ scoped_ptr<FileStreamWriter> writer(FileStreamWriter::CreateForLocalFile(
+ task_runner.get(), dest_path, 0, FileStreamWriter::CREATE_NEW_FILE));
std::vector<int64> progress;
CopyOrMoveOperationDelegate::StreamCopyHelper helper(
@@ -855,12 +858,12 @@ TEST(LocalFileSystemCopyOrMoveOperationTest, StreamCopyHelper_Cancel) {
base::Bind(&CopyOrMoveOperationDelegate::StreamCopyHelper::Cancel,
base::Unretained(&helper)));
- base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
+ base::File::Error error = base::File::FILE_ERROR_FAILED;
base::RunLoop run_loop;
helper.Run(base::Bind(&AssignAndQuit, &run_loop, &error));
run_loop.Run();
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_ABORT, error);
+ EXPECT_EQ(base::File::FILE_ERROR_ABORT, error);
}
-} // namespace fileapi
+} // namespace content
diff --git a/chromium/content/browser/fileapi/dragged_file_util_unittest.cc b/chromium/content/browser/fileapi/dragged_file_util_unittest.cc
index 17b29cc6be5..6bd6e100253 100644
--- a/chromium/content/browser/fileapi/dragged_file_util_unittest.cc
+++ b/chromium/content/browser/fileapi/dragged_file_util_unittest.cc
@@ -15,18 +15,24 @@
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/time/time.h"
+#include "content/public/test/async_file_test_helper.h"
#include "content/public/test/test_file_system_context.h"
+#include "content/test/fileapi_test_file_set.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "webkit/browser/fileapi/async_file_test_helper.h"
#include "webkit/browser/fileapi/dragged_file_util.h"
#include "webkit/browser/fileapi/file_system_context.h"
#include "webkit/browser/fileapi/file_system_operation_context.h"
#include "webkit/browser/fileapi/isolated_context.h"
#include "webkit/browser/fileapi/local_file_util.h"
#include "webkit/browser/fileapi/native_file_util.h"
-#include "webkit/browser/fileapi/test_file_set.h"
-namespace fileapi {
+using content::AsyncFileTestHelper;
+using fileapi::FileSystemContext;
+using fileapi::FileSystemOperationContext;
+using fileapi::FileSystemType;
+using fileapi::FileSystemURL;
+
+namespace content {
namespace {
@@ -50,7 +56,7 @@ base::FilePath GetTopLevelPath(const base::FilePath& path) {
bool IsDirectoryEmpty(FileSystemContext* context, const FileSystemURL& url) {
FileEntryList entries;
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::ReadDirectory(context, url, &entries));
return entries.empty();
}
@@ -94,7 +100,7 @@ class DraggedFileUtilTest : public testing::Test {
virtual void SetUp() {
ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
ASSERT_TRUE(partition_dir_.CreateUniqueTempDir());
- file_util_.reset(new DraggedFileUtil());
+ file_util_.reset(new fileapi::DraggedFileUtil());
// Register the files/directories of RegularTestCases (with random
// root paths) as dropped files.
@@ -112,8 +118,8 @@ class DraggedFileUtilTest : public testing::Test {
}
protected:
- IsolatedContext* isolated_context() const {
- return IsolatedContext::GetInstance();
+ fileapi::IsolatedContext* isolated_context() const {
+ return fileapi::IsolatedContext::GetInstance();
}
const base::FilePath& root_path() const {
return data_dir_.path();
@@ -121,7 +127,7 @@ class DraggedFileUtilTest : public testing::Test {
FileSystemContext* file_system_context() const {
return file_system_context_.get();
}
- FileSystemFileUtil* file_util() const { return file_util_.get(); }
+ fileapi::FileSystemFileUtil* file_util() const { return file_util_.get(); }
std::string filesystem_id() const { return filesystem_id_; }
base::FilePath GetTestCasePlatformPath(
@@ -142,36 +148,36 @@ class DraggedFileUtilTest : public testing::Test {
filesystem_id()).Append(path);
return file_system_context_->CreateCrackedFileSystemURL(
GURL("http://example.com"),
- kFileSystemTypeIsolated,
+ fileapi::kFileSystemTypeIsolated,
virtual_path);
}
FileSystemURL GetOtherFileSystemURL(const base::FilePath& path) const {
return file_system_context()->CreateCrackedFileSystemURL(
GURL("http://example.com"),
- kFileSystemTypeTemporary,
+ fileapi::kFileSystemTypeTemporary,
base::FilePath().AppendASCII("dest").Append(path));
}
void VerifyFilesHaveSameContent(const FileSystemURL& url1,
const FileSystemURL& url2) {
// Get the file info and the platform path for url1.
- base::PlatformFileInfo info1;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ base::File::Info info1;
+ ASSERT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::GetMetadata(
file_system_context(), url1, &info1));
base::FilePath platform_path1;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::GetPlatformPath(
file_system_context(), url1, &platform_path1));
// Get the file info and the platform path for url2.
- base::PlatformFileInfo info2;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ base::File::Info info2;
+ ASSERT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::GetMetadata(
file_system_context(), url2, &info2));
base::FilePath platform_path2;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::GetPlatformPath(
file_system_context(), url2, &platform_path2));
@@ -201,7 +207,7 @@ class DraggedFileUtilTest : public testing::Test {
FileSystemURL dir = directories.front();
directories.pop();
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::ReadDirectory(
file_system_context(), dir, &entries));
for (size_t i = 0; i < entries.size(); ++i) {
@@ -220,7 +226,7 @@ class DraggedFileUtilTest : public testing::Test {
FileSystemURL dir = directories.front();
directories.pop();
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::ReadDirectory(
file_system_context(), dir, &entries));
for (size_t i = 0; i < entries.size(); ++i) {
@@ -241,9 +247,9 @@ class DraggedFileUtilTest : public testing::Test {
}
}
- scoped_ptr<FileSystemOperationContext> GetOperationContext() {
+ scoped_ptr<fileapi::FileSystemOperationContext> GetOperationContext() {
return make_scoped_ptr(
- new FileSystemOperationContext(file_system_context())).Pass();
+ new fileapi::FileSystemOperationContext(file_system_context())).Pass();
}
@@ -251,9 +257,10 @@ class DraggedFileUtilTest : public testing::Test {
void SimulateDropFiles() {
size_t root_path_index = 0;
- IsolatedContext::FileInfoSet toplevels;
- for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) {
- const test::TestCaseRecord& test_case = test::kRegularTestCases[i];
+ fileapi::IsolatedContext::FileInfoSet toplevels;
+ for (size_t i = 0; i < kRegularFileSystemTestCaseSize; ++i) {
+ const FileSystemTestCaseRecord& test_case =
+ kRegularFileSystemTestCases[i];
base::FilePath path(test_case.path);
base::FilePath toplevel = GetTopLevelPath(path);
@@ -266,7 +273,7 @@ class DraggedFileUtilTest : public testing::Test {
toplevels.AddPath(root.Append(path), NULL);
}
- test::SetUpOneTestCase(toplevel_root_map_[toplevel], test_case);
+ SetUpOneFileSystemTestCase(toplevel_root_map_[toplevel], test_case);
}
// Register the toplevel entries.
@@ -279,24 +286,25 @@ class DraggedFileUtilTest : public testing::Test {
std::string filesystem_id_;
scoped_refptr<FileSystemContext> file_system_context_;
std::map<base::FilePath, base::FilePath> toplevel_root_map_;
- scoped_ptr<DraggedFileUtil> file_util_;
+ scoped_ptr<fileapi::DraggedFileUtil> file_util_;
DISALLOW_COPY_AND_ASSIGN(DraggedFileUtilTest);
};
TEST_F(DraggedFileUtilTest, BasicTest) {
- for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) {
+ for (size_t i = 0; i < kRegularFileSystemTestCaseSize; ++i) {
SCOPED_TRACE(testing::Message() << "Testing RegularTestCases " << i);
- const test::TestCaseRecord& test_case = test::kRegularTestCases[i];
+ const FileSystemTestCaseRecord& test_case =
+ kRegularFileSystemTestCases[i];
FileSystemURL url = GetFileSystemURL(base::FilePath(test_case.path));
// See if we can query the file info via the isolated FileUtil.
// (This should succeed since we have registered all the top-level
// entries of the test cases in SetUp())
- base::PlatformFileInfo info;
+ base::File::Info info;
base::FilePath platform_path;
FileSystemOperationContext context(file_system_context());
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
file_util()->GetFileInfo(&context, url, &info, &platform_path));
// See if the obtained file info is correct.
@@ -309,7 +317,7 @@ TEST_F(DraggedFileUtilTest, BasicTest) {
}
TEST_F(DraggedFileUtilTest, UnregisteredPathsTest) {
- static const fileapi::test::TestCaseRecord kUnregisteredCases[] = {
+ static const FileSystemTestCaseRecord kUnregisteredCases[] = {
{true, FILE_PATH_LITERAL("nonexistent"), 0},
{true, FILE_PATH_LITERAL("nonexistent/dir foo"), 0},
{false, FILE_PATH_LITERAL("nonexistent/false"), 0},
@@ -319,13 +327,13 @@ TEST_F(DraggedFileUtilTest, UnregisteredPathsTest) {
for (size_t i = 0; i < arraysize(kUnregisteredCases); ++i) {
SCOPED_TRACE(testing::Message() << "Creating kUnregisteredCases " << i);
- const test::TestCaseRecord& test_case = kUnregisteredCases[i];
+ const FileSystemTestCaseRecord& test_case = kUnregisteredCases[i];
// Prepare the test file/directory.
- SetUpOneTestCase(root_path(), test_case);
+ SetUpOneFileSystemTestCase(root_path(), test_case);
// Make sure regular GetFileInfo succeeds.
- base::PlatformFileInfo info;
+ base::File::Info info;
ASSERT_TRUE(base::GetFileInfo(root_path().Append(test_case.path), &info));
if (!test_case.is_directory)
ASSERT_EQ(test_case.data_file_size, info.size);
@@ -334,7 +342,7 @@ TEST_F(DraggedFileUtilTest, UnregisteredPathsTest) {
for (size_t i = 0; i < arraysize(kUnregisteredCases); ++i) {
SCOPED_TRACE(testing::Message() << "Creating kUnregisteredCases " << i);
- const test::TestCaseRecord& test_case = kUnregisteredCases[i];
+ const FileSystemTestCaseRecord& test_case = kUnregisteredCases[i];
FileSystemURL url = GetFileSystemURL(base::FilePath(test_case.path));
// We should not be able to get the valid URL for unregistered files.
@@ -343,8 +351,9 @@ TEST_F(DraggedFileUtilTest, UnregisteredPathsTest) {
}
TEST_F(DraggedFileUtilTest, ReadDirectoryTest) {
- for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) {
- const test::TestCaseRecord& test_case = test::kRegularTestCases[i];
+ for (size_t i = 0; i < kRegularFileSystemTestCaseSize; ++i) {
+ const FileSystemTestCaseRecord& test_case =
+ kRegularFileSystemTestCases[i];
if (!test_case.is_directory)
continue;
@@ -352,7 +361,8 @@ TEST_F(DraggedFileUtilTest, ReadDirectoryTest) {
<< ": " << test_case.path);
// Read entries in the directory to construct the expected results map.
- typedef std::map<base::FilePath::StringType, DirectoryEntry> EntryMap;
+ typedef std::map<base::FilePath::StringType, fileapi::DirectoryEntry>
+ EntryMap;
EntryMap expected_entry_map;
base::FilePath dir_path = GetTestCasePlatformPath(test_case.path);
@@ -362,7 +372,7 @@ TEST_F(DraggedFileUtilTest, ReadDirectoryTest) {
base::FilePath current;
while (!(current = file_enum.Next()).empty()) {
base::FileEnumerator::FileInfo file_info = file_enum.GetInfo();
- DirectoryEntry entry;
+ fileapi::DirectoryEntry entry;
entry.is_directory = file_info.IsDirectory();
entry.name = current.BaseName().value();
entry.size = file_info.GetSize();
@@ -383,13 +393,13 @@ TEST_F(DraggedFileUtilTest, ReadDirectoryTest) {
// Perform ReadDirectory in the isolated filesystem.
FileSystemURL url = GetFileSystemURL(base::FilePath(test_case.path));
FileEntryList entries;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::ReadDirectory(
file_system_context(), url, &entries));
EXPECT_EQ(expected_entry_map.size(), entries.size());
for (size_t i = 0; i < entries.size(); ++i) {
- const DirectoryEntry& entry = entries[i];
+ const fileapi::DirectoryEntry& entry = entries[i];
EntryMap::iterator found = expected_entry_map.find(entry.name);
EXPECT_TRUE(found != expected_entry_map.end());
EXPECT_EQ(found->second.name, entry.name);
@@ -402,14 +412,15 @@ TEST_F(DraggedFileUtilTest, ReadDirectoryTest) {
}
TEST_F(DraggedFileUtilTest, GetLocalFilePathTest) {
- for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) {
- const test::TestCaseRecord& test_case = test::kRegularTestCases[i];
+ for (size_t i = 0; i < kRegularFileSystemTestCaseSize; ++i) {
+ const FileSystemTestCaseRecord& test_case =
+ kRegularFileSystemTestCases[i];
FileSystemURL url = GetFileSystemURL(base::FilePath(test_case.path));
FileSystemOperationContext context(file_system_context());
base::FilePath local_file_path;
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
file_util()->GetLocalFilePath(&context, url, &local_file_path));
EXPECT_EQ(GetTestCasePlatformPath(test_case.path).value(),
local_file_path.value());
@@ -424,14 +435,14 @@ TEST_F(DraggedFileUtilTest, CopyOutFileTest) {
std::queue<FileSystemURL> directories;
directories.push(src_root);
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::CreateDirectory(file_system_context(),
dest_root));
while (!directories.empty()) {
FileSystemURL dir = directories.front();
directories.pop();
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::ReadDirectory(file_system_context(),
dir, &entries));
for (size_t i = 0; i < entries.size(); ++i) {
@@ -441,7 +452,7 @@ TEST_F(DraggedFileUtilTest, CopyOutFileTest) {
src_root, dest_root, src_url);
if (entries[i].is_directory) {
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::CreateDirectory(file_system_context(),
dest_url));
directories.push(src_url);
@@ -449,7 +460,7 @@ TEST_F(DraggedFileUtilTest, CopyOutFileTest) {
}
SCOPED_TRACE(testing::Message() << "Testing file copy "
<< src_url.path().value());
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::Copy(file_system_context(),
src_url, dest_url));
VerifyFilesHaveSameContent(src_url, dest_url);
@@ -461,12 +472,12 @@ TEST_F(DraggedFileUtilTest, CopyOutDirectoryTest) {
FileSystemURL src_root = GetFileSystemURL(base::FilePath());
FileSystemURL dest_root = GetOtherFileSystemURL(base::FilePath());
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::CreateDirectory(file_system_context(),
dest_root));
FileEntryList entries;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::ReadDirectory(file_system_context(),
src_root, &entries));
for (size_t i = 0; i < entries.size(); ++i) {
@@ -478,7 +489,7 @@ TEST_F(DraggedFileUtilTest, CopyOutDirectoryTest) {
src_root, dest_root, src_url);
SCOPED_TRACE(testing::Message() << "Testing file copy "
<< src_url.path().value());
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::Copy(file_system_context(),
src_url, dest_url));
VerifyDirectoriesHaveSameContent(src_url, dest_url);
@@ -486,8 +497,9 @@ TEST_F(DraggedFileUtilTest, CopyOutDirectoryTest) {
}
TEST_F(DraggedFileUtilTest, TouchTest) {
- for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) {
- const test::TestCaseRecord& test_case = test::kRegularTestCases[i];
+ for (size_t i = 0; i < kRegularFileSystemTestCaseSize; ++i) {
+ const FileSystemTestCaseRecord& test_case =
+ kRegularFileSystemTestCases[i];
if (test_case.is_directory)
continue;
SCOPED_TRACE(testing::Message() << test_case.path);
@@ -496,15 +508,15 @@ TEST_F(DraggedFileUtilTest, TouchTest) {
base::Time last_access_time = base::Time::FromTimeT(1000);
base::Time last_modified_time = base::Time::FromTimeT(2000);
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
file_util()->Touch(GetOperationContext().get(), url,
last_access_time,
last_modified_time));
// Verification.
- base::PlatformFileInfo info;
+ base::File::Info info;
base::FilePath platform_path;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
file_util()->GetFileInfo(GetOperationContext().get(), url,
&info, &platform_path));
EXPECT_EQ(last_access_time.ToTimeT(), info.last_accessed.ToTimeT());
@@ -513,8 +525,9 @@ TEST_F(DraggedFileUtilTest, TouchTest) {
}
TEST_F(DraggedFileUtilTest, TruncateTest) {
- for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) {
- const test::TestCaseRecord& test_case = test::kRegularTestCases[i];
+ for (size_t i = 0; i < kRegularFileSystemTestCaseSize; ++i) {
+ const FileSystemTestCaseRecord& test_case =
+ kRegularFileSystemTestCases[i];
if (test_case.is_directory)
continue;
@@ -522,23 +535,23 @@ TEST_F(DraggedFileUtilTest, TruncateTest) {
FileSystemURL url = GetFileSystemURL(base::FilePath(test_case.path));
// Truncate to 0.
- base::PlatformFileInfo info;
+ base::File::Info info;
base::FilePath platform_path;
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
file_util()->Truncate(GetOperationContext().get(), url, 0));
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
file_util()->GetFileInfo(GetOperationContext().get(), url,
&info, &platform_path));
EXPECT_EQ(0, info.size);
// Truncate (extend) to 999.
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
file_util()->Truncate(GetOperationContext().get(), url, 999));
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
file_util()->GetFileInfo(GetOperationContext().get(), url,
&info, &platform_path));
EXPECT_EQ(999, info.size);
}
}
-} // namespace fileapi
+} // namespace content
diff --git a/chromium/content/browser/fileapi/external_mount_points_unittest.cc b/chromium/content/browser/fileapi/external_mount_points_unittest.cc
new file mode 100644
index 00000000000..fa0d1f5bbc2
--- /dev/null
+++ b/chromium/content/browser/fileapi/external_mount_points_unittest.cc
@@ -0,0 +1,515 @@
+// 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 "webkit/browser/fileapi/external_mount_points.h"
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/fileapi/file_system_url.h"
+
+#define FPL FILE_PATH_LITERAL
+
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+#define DRIVE FPL("C:")
+#else
+#define DRIVE
+#endif
+
+using fileapi::FileSystemURL;
+
+namespace content {
+
+TEST(ExternalMountPointsTest, AddMountPoint) {
+ scoped_refptr<fileapi::ExternalMountPoints> mount_points(
+ fileapi::ExternalMountPoints::CreateRefCounted());
+
+ struct TestCase {
+ // The mount point's name.
+ const char* const name;
+ // The mount point's path.
+ const base::FilePath::CharType* const path;
+ // Whether the mount point registration should succeed.
+ bool success;
+ // Path returned by GetRegisteredPath. NULL if the method is expected to
+ // fail.
+ const base::FilePath::CharType* const registered_path;
+ };
+
+ const TestCase kTestCases[] = {
+ // Valid mount point.
+ { "test", DRIVE FPL("/foo/test"), true, DRIVE FPL("/foo/test") },
+ // Valid mount point with only one path component.
+ { "bbb", DRIVE FPL("/bbb"), true, DRIVE FPL("/bbb") },
+ // Existing mount point path is substring of the mount points path.
+ { "test11", DRIVE FPL("/foo/test11"), true, DRIVE FPL("/foo/test11") },
+ // Path substring of an existing path.
+ { "test1", DRIVE FPL("/foo/test1"), true, DRIVE FPL("/foo/test1") },
+ // Empty mount point name and path.
+ { "", DRIVE FPL(""), false, NULL },
+ // Empty mount point name.
+ { "", DRIVE FPL("/ddd"), false, NULL },
+ // Empty mount point path.
+ { "empty_path", FPL(""), true, FPL("") },
+ // Name different from path's base name.
+ { "not_base_name", DRIVE FPL("/x/y/z"), true, DRIVE FPL("/x/y/z") },
+ // References parent.
+ { "invalid", DRIVE FPL("../foo/invalid"), false, NULL },
+ // Relative path.
+ { "relative", DRIVE FPL("foo/relative"), false, NULL },
+ // Existing mount point path.
+ { "path_exists", DRIVE FPL("/foo/test"), false, NULL },
+ // Mount point with the same name exists.
+ { "test", DRIVE FPL("/foo/a/test_name_exists"), false,
+ DRIVE FPL("/foo/test") },
+ // Child of an existing mount point.
+ { "a1", DRIVE FPL("/foo/test/a"), false, NULL },
+ // Parent of an existing mount point.
+ { "foo1", DRIVE FPL("/foo"), false, NULL },
+ // Bit bigger depth.
+ { "g", DRIVE FPL("/foo/a/b/c/d/e/f/g"), true,
+ DRIVE FPL("/foo/a/b/c/d/e/f/g") },
+ // Sibling mount point (with similar name) exists.
+ { "ff", DRIVE FPL("/foo/a/b/c/d/e/ff"), true,
+ DRIVE FPL("/foo/a/b/c/d/e/ff") },
+ // Lexicographically last among existing mount points.
+ { "yyy", DRIVE FPL("/zzz/yyy"), true, DRIVE FPL("/zzz/yyy") },
+ // Parent of the lexicographically last mount point.
+ { "zzz1", DRIVE FPL("/zzz"), false, NULL },
+ // Child of the lexicographically last mount point.
+ { "xxx1", DRIVE FPL("/zzz/yyy/xxx"), false, NULL },
+ // Lexicographically first among existing mount points.
+ { "b", DRIVE FPL("/a/b"), true, DRIVE FPL("/a/b") },
+ // Parent of lexicographically first mount point.
+ { "a2", DRIVE FPL("/a"), false, NULL },
+ // Child of lexicographically last mount point.
+ { "c1", DRIVE FPL("/a/b/c"), false, NULL },
+ // Parent to all of the mount points.
+ { "root", DRIVE FPL("/"), false, NULL },
+ // Path contains .. component.
+ { "funky", DRIVE FPL("/tt/fun/../funky"), false, NULL },
+ // Windows separators.
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+ { "win", DRIVE FPL("\\try\\separators\\win"), true,
+ DRIVE FPL("\\try\\separators\\win") },
+ { "win1", DRIVE FPL("\\try/separators\\win1"), true,
+ DRIVE FPL("\\try/separators\\win1") },
+ { "win2", DRIVE FPL("\\try/separators\\win"), false, NULL },
+#else
+ { "win", DRIVE FPL("\\separators\\win"), false, NULL },
+ { "win1", DRIVE FPL("\\try/separators\\win1"), false, NULL },
+#endif
+ // Win separators, but relative path.
+ { "win2", DRIVE FPL("try\\separators\\win2"), false, NULL },
+ };
+
+ // Test adding mount points.
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) {
+ EXPECT_EQ(kTestCases[i].success,
+ mount_points->RegisterFileSystem(
+ kTestCases[i].name,
+ fileapi::kFileSystemTypeNativeLocal,
+ fileapi::FileSystemMountOption(),
+ base::FilePath(kTestCases[i].path)))
+ << "Adding mount point: " << kTestCases[i].name << " with path "
+ << kTestCases[i].path;
+ }
+
+ // Test that final mount point presence state is as expected.
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) {
+ base::FilePath found_path;
+ EXPECT_EQ(kTestCases[i].registered_path != NULL,
+ mount_points->GetRegisteredPath(kTestCases[i].name, &found_path))
+ << "Test case: " << i;
+
+ if (kTestCases[i].registered_path) {
+ base::FilePath expected_path(kTestCases[i].registered_path);
+ EXPECT_EQ(expected_path.NormalizePathSeparators(), found_path);
+ }
+ }
+}
+
+TEST(ExternalMountPointsTest, GetVirtualPath) {
+ scoped_refptr<fileapi::ExternalMountPoints> mount_points(
+ fileapi::ExternalMountPoints::CreateRefCounted());
+
+ mount_points->RegisterFileSystem("c",
+ fileapi::kFileSystemTypeNativeLocal,
+ fileapi::FileSystemMountOption(),
+ base::FilePath(DRIVE FPL("/a/b/c")));
+ // Note that "/a/b/c" < "/a/b/c(1)" < "/a/b/c/".
+ mount_points->RegisterFileSystem("c(1)",
+ fileapi::kFileSystemTypeNativeLocal,
+ fileapi::FileSystemMountOption(),
+ base::FilePath(DRIVE FPL("/a/b/c(1)")));
+ mount_points->RegisterFileSystem("x",
+ fileapi::kFileSystemTypeNativeLocal,
+ fileapi::FileSystemMountOption(),
+ base::FilePath(DRIVE FPL("/z/y/x")));
+ mount_points->RegisterFileSystem("o",
+ fileapi::kFileSystemTypeNativeLocal,
+ fileapi::FileSystemMountOption(),
+ base::FilePath(DRIVE FPL("/m/n/o")));
+ // A mount point whose name does not match its path base name.
+ mount_points->RegisterFileSystem("mount",
+ fileapi::kFileSystemTypeNativeLocal,
+ fileapi::FileSystemMountOption(),
+ base::FilePath(DRIVE FPL("/root/foo")));
+ // A mount point with an empty path.
+ mount_points->RegisterFileSystem("empty_path",
+ fileapi::kFileSystemTypeNativeLocal,
+ fileapi::FileSystemMountOption(),
+ base::FilePath());
+
+ struct TestCase {
+ const base::FilePath::CharType* const local_path;
+ bool success;
+ const base::FilePath::CharType* const virtual_path;
+ };
+
+ const TestCase kTestCases[] = {
+ // Empty path.
+ { FPL(""), false, FPL("") },
+ // No registered mount point (but is parent to a mount point).
+ { DRIVE FPL("/a/b"), false, FPL("") },
+ // No registered mount point (but is parent to a mount point).
+ { DRIVE FPL("/z/y"), false, FPL("") },
+ // No registered mount point (but is parent to a mount point).
+ { DRIVE FPL("/m/n"), false, FPL("") },
+ // No registered mount point.
+ { DRIVE FPL("/foo/mount"), false, FPL("") },
+ // An existing mount point path is substring.
+ { DRIVE FPL("/a/b/c1"), false, FPL("") },
+ // No leading /.
+ { DRIVE FPL("a/b/c"), false, FPL("") },
+ // Sibling to a root path.
+ { DRIVE FPL("/a/b/d/e"), false, FPL("") },
+ // Sibling to a root path.
+ { DRIVE FPL("/z/y/v/u"), false, FPL("") },
+ // Sibling to a root path.
+ { DRIVE FPL("/m/n/p/q"), false, FPL("") },
+ // Mount point root path.
+ { DRIVE FPL("/a/b/c"), true, FPL("c") },
+ // Mount point root path.
+ { DRIVE FPL("/z/y/x"), true, FPL("x") },
+ // Mount point root path.
+ { DRIVE FPL("/m/n/o"), true, FPL("o") },
+ // Mount point child path.
+ { DRIVE FPL("/a/b/c/d/e"), true, FPL("c/d/e") },
+ // Mount point child path.
+ { DRIVE FPL("/z/y/x/v/u"), true, FPL("x/v/u") },
+ // Mount point child path.
+ { DRIVE FPL("/m/n/o/p/q"), true, FPL("o/p/q") },
+ // Name doesn't match mount point path base name.
+ { DRIVE FPL("/root/foo/a/b/c"), true, FPL("mount/a/b/c") },
+ { DRIVE FPL("/root/foo"), true, FPL("mount") },
+ // Mount point contains character whose ASCII code is smaller than file path
+ // separator's.
+ { DRIVE FPL("/a/b/c(1)/d/e"), true, FPL("c(1)/d/e") },
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+ // Path with win separators mixed in.
+ { DRIVE FPL("/a\\b\\c/d"), true, FPL("c/d") },
+#endif
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) {
+ // Initialize virtual path with a value.
+ base::FilePath virtual_path(DRIVE FPL("/mount"));
+ base::FilePath local_path(kTestCases[i].local_path);
+ EXPECT_EQ(kTestCases[i].success,
+ mount_points->GetVirtualPath(local_path, &virtual_path))
+ << "Resolving " << kTestCases[i].local_path;
+
+ // There are no guarantees for |virtual_path| value if |GetVirtualPath|
+ // fails.
+ if (!kTestCases[i].success)
+ continue;
+
+ base::FilePath expected_virtual_path(kTestCases[i].virtual_path);
+ EXPECT_EQ(expected_virtual_path.NormalizePathSeparators(), virtual_path)
+ << "Resolving " << kTestCases[i].local_path;
+ }
+}
+
+TEST(ExternalMountPointsTest, HandlesFileSystemMountType) {
+ scoped_refptr<fileapi::ExternalMountPoints> mount_points(
+ fileapi::ExternalMountPoints::CreateRefCounted());
+
+ const GURL test_origin("http://chromium.org");
+ const base::FilePath test_path(FPL("/mount"));
+
+ // Should handle External File System.
+ EXPECT_TRUE(mount_points->HandlesFileSystemMountType(
+ fileapi::kFileSystemTypeExternal));
+
+ // Shouldn't handle the rest.
+ EXPECT_FALSE(mount_points->HandlesFileSystemMountType(
+ fileapi::kFileSystemTypeIsolated));
+ EXPECT_FALSE(mount_points->HandlesFileSystemMountType(
+ fileapi::kFileSystemTypeTemporary));
+ EXPECT_FALSE(mount_points->HandlesFileSystemMountType(
+ fileapi::kFileSystemTypePersistent));
+ EXPECT_FALSE(mount_points->HandlesFileSystemMountType(
+ fileapi::kFileSystemTypeTest));
+ // Not even if it's external subtype.
+ EXPECT_FALSE(mount_points->HandlesFileSystemMountType(
+ fileapi::kFileSystemTypeNativeLocal));
+ EXPECT_FALSE(mount_points->HandlesFileSystemMountType(
+ fileapi::kFileSystemTypeRestrictedNativeLocal));
+ EXPECT_FALSE(mount_points->HandlesFileSystemMountType(
+ fileapi::kFileSystemTypeDrive));
+ EXPECT_FALSE(mount_points->HandlesFileSystemMountType(
+ fileapi::kFileSystemTypeSyncable));
+}
+
+TEST(ExternalMountPointsTest, CreateCrackedFileSystemURL) {
+ scoped_refptr<fileapi::ExternalMountPoints> mount_points(
+ fileapi::ExternalMountPoints::CreateRefCounted());
+
+ const GURL kTestOrigin("http://chromium.org");
+
+ mount_points->RegisterFileSystem("c",
+ fileapi::kFileSystemTypeNativeLocal,
+ fileapi::FileSystemMountOption(),
+ base::FilePath(DRIVE FPL("/a/b/c")));
+ mount_points->RegisterFileSystem("c(1)",
+ fileapi::kFileSystemTypeDrive,
+ fileapi::FileSystemMountOption(),
+ base::FilePath(DRIVE FPL("/a/b/c(1)")));
+ mount_points->RegisterFileSystem("empty_path",
+ fileapi::kFileSystemTypeSyncable,
+ fileapi::FileSystemMountOption(),
+ base::FilePath());
+ mount_points->RegisterFileSystem("mount",
+ fileapi::kFileSystemTypeDrive,
+ fileapi::FileSystemMountOption(),
+ base::FilePath(DRIVE FPL("/root")));
+
+ // Try cracking invalid GURL.
+ FileSystemURL invalid = mount_points->CrackURL(GURL("http://chromium.og"));
+ EXPECT_FALSE(invalid.is_valid());
+
+ // Try cracking isolated path.
+ FileSystemURL isolated = mount_points->CreateCrackedFileSystemURL(
+ kTestOrigin, fileapi::kFileSystemTypeIsolated, base::FilePath(FPL("c")));
+ EXPECT_FALSE(isolated.is_valid());
+
+ // Try native local which is not cracked.
+ FileSystemURL native_local = mount_points->CreateCrackedFileSystemURL(
+ kTestOrigin,
+ fileapi::kFileSystemTypeNativeLocal,
+ base::FilePath(FPL("c")));
+ EXPECT_FALSE(native_local.is_valid());
+
+ struct TestCase {
+ const base::FilePath::CharType* const path;
+ bool expect_valid;
+ fileapi::FileSystemType expect_type;
+ const base::FilePath::CharType* const expect_path;
+ const char* const expect_fs_id;
+ };
+
+ const TestCase kTestCases[] = {
+ { FPL("c/d/e"),
+ true, fileapi::kFileSystemTypeNativeLocal, DRIVE FPL("/a/b/c/d/e"), "c" },
+ { FPL("c(1)/d/e"),
+ true, fileapi::kFileSystemTypeDrive, DRIVE FPL("/a/b/c(1)/d/e"), "c(1)" },
+ { FPL("c(1)"),
+ true, fileapi::kFileSystemTypeDrive, DRIVE FPL("/a/b/c(1)"), "c(1)" },
+ { FPL("empty_path/a"),
+ true, fileapi::kFileSystemTypeSyncable, FPL("a"), "empty_path" },
+ { FPL("empty_path"),
+ true, fileapi::kFileSystemTypeSyncable, FPL(""), "empty_path" },
+ { FPL("mount/a/b"),
+ true, fileapi::kFileSystemTypeDrive, DRIVE FPL("/root/a/b"), "mount" },
+ { FPL("mount"),
+ true, fileapi::kFileSystemTypeDrive, DRIVE FPL("/root"), "mount" },
+ { FPL("cc"),
+ false, fileapi::kFileSystemTypeUnknown, FPL(""), "" },
+ { FPL(""),
+ false, fileapi::kFileSystemTypeUnknown, FPL(""), "" },
+ { FPL(".."),
+ false, fileapi::kFileSystemTypeUnknown, FPL(""), "" },
+ // Absolte paths.
+ { FPL("/c/d/e"),
+ false, fileapi::kFileSystemTypeUnknown, FPL(""), "" },
+ { FPL("/c(1)/d/e"),
+ false, fileapi::kFileSystemTypeUnknown, FPL(""), "" },
+ { FPL("/empty_path"),
+ false, fileapi::kFileSystemTypeUnknown, FPL(""), "" },
+ // PAth references parent.
+ { FPL("c/d/../e"),
+ false, fileapi::kFileSystemTypeUnknown, FPL(""), "" },
+ { FPL("/empty_path/a/../b"),
+ false, fileapi::kFileSystemTypeUnknown, FPL(""), "" },
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+ { FPL("c/d\\e"),
+ true, fileapi::kFileSystemTypeNativeLocal, DRIVE FPL("/a/b/c/d/e"), "c" },
+ { FPL("mount\\a\\b"),
+ true, fileapi::kFileSystemTypeDrive, DRIVE FPL("/root/a/b"), "mount" },
+#endif
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) {
+ FileSystemURL cracked = mount_points->CreateCrackedFileSystemURL(
+ kTestOrigin,
+ fileapi::kFileSystemTypeExternal,
+ base::FilePath(kTestCases[i].path));
+
+ EXPECT_EQ(kTestCases[i].expect_valid, cracked.is_valid())
+ << "Test case index: " << i;
+
+ if (!kTestCases[i].expect_valid)
+ continue;
+
+ EXPECT_EQ(kTestOrigin, cracked.origin())
+ << "Test case index: " << i;
+ EXPECT_EQ(kTestCases[i].expect_type, cracked.type())
+ << "Test case index: " << i;
+ EXPECT_EQ(base::FilePath(
+ kTestCases[i].expect_path).NormalizePathSeparators(), cracked.path())
+ << "Test case index: " << i;
+ EXPECT_EQ(base::FilePath(kTestCases[i].path).NormalizePathSeparators(),
+ cracked.virtual_path())
+ << "Test case index: " << i;
+ EXPECT_EQ(kTestCases[i].expect_fs_id, cracked.filesystem_id())
+ << "Test case index: " << i;
+ EXPECT_EQ(fileapi::kFileSystemTypeExternal, cracked.mount_type())
+ << "Test case index: " << i;
+ }
+}
+
+TEST(ExternalMountPointsTest, CrackVirtualPath) {
+ scoped_refptr<fileapi::ExternalMountPoints> mount_points(
+ fileapi::ExternalMountPoints::CreateRefCounted());
+
+ const GURL kTestOrigin("http://chromium.org");
+
+ mount_points->RegisterFileSystem("c",
+ fileapi::kFileSystemTypeNativeLocal,
+ fileapi::FileSystemMountOption(),
+ base::FilePath(DRIVE FPL("/a/b/c")));
+ mount_points->RegisterFileSystem("c(1)",
+ fileapi::kFileSystemTypeDrive,
+ fileapi::FileSystemMountOption(),
+ base::FilePath(DRIVE FPL("/a/b/c(1)")));
+ mount_points->RegisterFileSystem("empty_path",
+ fileapi::kFileSystemTypeSyncable,
+ fileapi::FileSystemMountOption(),
+ base::FilePath());
+ mount_points->RegisterFileSystem("mount",
+ fileapi::kFileSystemTypeDrive,
+ fileapi::FileSystemMountOption(),
+ base::FilePath(DRIVE FPL("/root")));
+
+ struct TestCase {
+ const base::FilePath::CharType* const path;
+ bool expect_valid;
+ fileapi::FileSystemType expect_type;
+ const base::FilePath::CharType* const expect_path;
+ const char* const expect_name;
+ };
+
+ const TestCase kTestCases[] = {
+ { FPL("c/d/e"),
+ true, fileapi::kFileSystemTypeNativeLocal, DRIVE FPL("/a/b/c/d/e"), "c" },
+ { FPL("c(1)/d/e"),
+ true, fileapi::kFileSystemTypeDrive, DRIVE FPL("/a/b/c(1)/d/e"), "c(1)" },
+ { FPL("c(1)"),
+ true, fileapi::kFileSystemTypeDrive, DRIVE FPL("/a/b/c(1)"), "c(1)" },
+ { FPL("empty_path/a"),
+ true, fileapi::kFileSystemTypeSyncable, FPL("a"), "empty_path" },
+ { FPL("empty_path"),
+ true, fileapi::kFileSystemTypeSyncable, FPL(""), "empty_path" },
+ { FPL("mount/a/b"),
+ true, fileapi::kFileSystemTypeDrive, DRIVE FPL("/root/a/b"), "mount" },
+ { FPL("mount"),
+ true, fileapi::kFileSystemTypeDrive, DRIVE FPL("/root"), "mount" },
+ { FPL("cc"),
+ false, fileapi::kFileSystemTypeUnknown, FPL(""), "" },
+ { FPL(""),
+ false, fileapi::kFileSystemTypeUnknown, FPL(""), "" },
+ { FPL(".."),
+ false, fileapi::kFileSystemTypeUnknown, FPL(""), "" },
+ // Absolte paths.
+ { FPL("/c/d/e"),
+ false, fileapi::kFileSystemTypeUnknown, FPL(""), "" },
+ { FPL("/c(1)/d/e"),
+ false, fileapi::kFileSystemTypeUnknown, FPL(""), "" },
+ { FPL("/empty_path"),
+ false, fileapi::kFileSystemTypeUnknown, FPL(""), "" },
+ // PAth references parent.
+ { FPL("c/d/../e"),
+ false, fileapi::kFileSystemTypeUnknown, FPL(""), "" },
+ { FPL("/empty_path/a/../b"),
+ false, fileapi::kFileSystemTypeUnknown, FPL(""), "" },
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+ { FPL("c/d\\e"),
+ true, fileapi::kFileSystemTypeNativeLocal, DRIVE FPL("/a/b/c/d/e"), "c" },
+ { FPL("mount\\a\\b"),
+ true, fileapi::kFileSystemTypeDrive, DRIVE FPL("/root/a/b"), "mount" },
+#endif
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) {
+ std::string cracked_name;
+ fileapi::FileSystemType cracked_type;
+ std::string cracked_id;
+ base::FilePath cracked_path;
+ fileapi::FileSystemMountOption cracked_option;
+ EXPECT_EQ(kTestCases[i].expect_valid,
+ mount_points->CrackVirtualPath(base::FilePath(kTestCases[i].path),
+ &cracked_name, &cracked_type, &cracked_id, &cracked_path,
+ &cracked_option))
+ << "Test case index: " << i;
+
+ if (!kTestCases[i].expect_valid)
+ continue;
+
+ EXPECT_EQ(kTestCases[i].expect_type, cracked_type)
+ << "Test case index: " << i;
+ EXPECT_EQ(base::FilePath(
+ kTestCases[i].expect_path).NormalizePathSeparators(), cracked_path)
+ << "Test case index: " << i;
+ EXPECT_EQ(kTestCases[i].expect_name, cracked_name)
+ << "Test case index: " << i;
+ // As of now we don't mount other filesystems with non-empty filesystem_id
+ // onto external mount points.
+ EXPECT_TRUE(cracked_id.empty()) << "Test case index: " << i;
+ }
+}
+
+TEST(ExternalMountPointsTest, MountOption) {
+ scoped_refptr<fileapi::ExternalMountPoints> mount_points(
+ fileapi::ExternalMountPoints::CreateRefCounted());
+
+ mount_points->RegisterFileSystem(
+ "nosync",
+ fileapi::kFileSystemTypeNativeLocal,
+ fileapi::FileSystemMountOption(fileapi::COPY_SYNC_OPTION_NO_SYNC),
+ base::FilePath(DRIVE FPL("/nosync")));
+ mount_points->RegisterFileSystem(
+ "sync",
+ fileapi::kFileSystemTypeNativeLocal,
+ fileapi::FileSystemMountOption(fileapi::COPY_SYNC_OPTION_SYNC),
+ base::FilePath(DRIVE FPL("/sync")));
+
+ std::string name;
+ fileapi::FileSystemType type;
+ std::string cracked_id;
+ fileapi::FileSystemMountOption option;
+ base::FilePath path;
+ EXPECT_TRUE(mount_points->CrackVirtualPath(
+ base::FilePath(FPL("nosync/file")), &name, &type, &cracked_id, &path,
+ &option));
+ EXPECT_EQ(fileapi::COPY_SYNC_OPTION_NO_SYNC, option.copy_sync_option());
+ EXPECT_TRUE(mount_points->CrackVirtualPath(
+ base::FilePath(FPL("sync/file")), &name, &type, &cracked_id, &path,
+ &option));
+ EXPECT_EQ(fileapi::COPY_SYNC_OPTION_SYNC, option.copy_sync_option());
+}
+
+} // namespace content
+
diff --git a/chromium/content/browser/fileapi/file_system_browsertest.cc b/chromium/content/browser/fileapi/file_system_browsertest.cc
index a3a83efd483..51fc4164dab 100644
--- a/chromium/content/browser/fileapi/file_system_browsertest.cc
+++ b/chromium/content/browser/fileapi/file_system_browsertest.cc
@@ -13,9 +13,9 @@
#include "content/public/browser/storage_partition.h"
#include "content/public/common/content_switches.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/shell/browser/shell.h"
-#include "content/test/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
#include "webkit/browser/quota/quota_manager.h"
using quota::QuotaManager;
diff --git a/chromium/content/browser/fileapi/file_system_context_unittest.cc b/chromium/content/browser/fileapi/file_system_context_unittest.cc
index 43003ad0dc4..e746a6a3ad3 100644
--- a/chromium/content/browser/fileapi/file_system_context_unittest.cc
+++ b/chromium/content/browser/fileapi/file_system_context_unittest.cc
@@ -7,13 +7,13 @@
#include "base/files/scoped_temp_dir.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/stringprintf.h"
+#include "content/browser/quota/mock_quota_manager.h"
+#include "content/public/test/mock_special_storage_policy.h"
#include "content/public/test/test_file_system_options.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "webkit/browser/fileapi/external_mount_points.h"
#include "webkit/browser/fileapi/file_system_backend.h"
#include "webkit/browser/fileapi/isolated_context.h"
-#include "webkit/browser/quota/mock_quota_manager.h"
-#include "webkit/browser/quota/mock_special_storage_policy.h"
#define FPL(x) FILE_PATH_LITERAL(x)
@@ -23,7 +23,14 @@
#define DRIVE
#endif
-namespace fileapi {
+using fileapi::ExternalMountPoints;
+using fileapi::FileSystemBackend;
+using fileapi::FileSystemContext;
+using fileapi::FileSystemMountOption;
+using fileapi::FileSystemURL;
+using fileapi::IsolatedContext;
+
+namespace content {
namespace {
@@ -45,10 +52,10 @@ class FileSystemContextTest : public testing::Test {
virtual void SetUp() {
ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
- storage_policy_ = new quota::MockSpecialStoragePolicy();
+ storage_policy_ = new MockSpecialStoragePolicy();
mock_quota_manager_ =
- new quota::MockQuotaManager(false /* is_incognito */,
+ new MockQuotaManager(false /* is_incognito */,
data_dir_.path(),
base::MessageLoopProxy::current().get(),
base::MessageLoopProxy::current().get(),
@@ -57,22 +64,24 @@ class FileSystemContextTest : public testing::Test {
protected:
FileSystemContext* CreateFileSystemContextForTest(
- ExternalMountPoints* external_mount_points) {
- return new FileSystemContext(base::MessageLoopProxy::current().get(),
- base::MessageLoopProxy::current().get(),
- external_mount_points,
- storage_policy_.get(),
- mock_quota_manager_->proxy(),
- ScopedVector<FileSystemBackend>(),
- data_dir_.path(),
- CreateAllowFileAccessOptions());
+ fileapi::ExternalMountPoints* external_mount_points) {
+ return new FileSystemContext(
+ base::MessageLoopProxy::current().get(),
+ base::MessageLoopProxy::current().get(),
+ external_mount_points,
+ storage_policy_.get(),
+ mock_quota_manager_->proxy(),
+ ScopedVector<FileSystemBackend>(),
+ std::vector<fileapi::URLRequestAutoMountHandler>(),
+ data_dir_.path(),
+ CreateAllowFileAccessOptions());
}
// Verifies a *valid* filesystem url has expected values.
void ExpectFileSystemURLMatches(const FileSystemURL& url,
const GURL& expect_origin,
- FileSystemType expect_mount_type,
- FileSystemType expect_type,
+ fileapi::FileSystemType expect_mount_type,
+ fileapi::FileSystemType expect_type,
const base::FilePath& expect_path,
const base::FilePath& expect_virtual_path,
const std::string& expect_filesystem_id) {
@@ -90,7 +99,7 @@ class FileSystemContextTest : public testing::Test {
base::ScopedTempDir data_dir_;
base::MessageLoop message_loop_;
scoped_refptr<quota::SpecialStoragePolicy> storage_policy_;
- scoped_refptr<quota::MockQuotaManager> mock_quota_manager_;
+ scoped_refptr<MockQuotaManager> mock_quota_manager_;
};
// It is not valid to pass NULL ExternalMountPoints to FileSystemContext on
@@ -104,13 +113,14 @@ TEST_F(FileSystemContextTest, NullExternalMountPoints) {
std::string isolated_name = "root";
std::string isolated_id =
IsolatedContext::GetInstance()->RegisterFileSystemForPath(
- kFileSystemTypeNativeLocal,
+ fileapi::kFileSystemTypeNativeLocal,
+ std::string(),
base::FilePath(DRIVE FPL("/test/isolated/root")),
&isolated_name);
// Register system external mount point.
ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
"system",
- kFileSystemTypeNativeLocal,
+ fileapi::kFileSystemTypeNativeLocal,
FileSystemMountOption(),
base::FilePath(DRIVE FPL("/test/sys/"))));
@@ -120,8 +130,8 @@ TEST_F(FileSystemContextTest, NullExternalMountPoints) {
ExpectFileSystemURLMatches(
cracked_isolated,
GURL(kTestOrigin),
- kFileSystemTypeIsolated,
- kFileSystemTypeNativeLocal,
+ fileapi::kFileSystemTypeIsolated,
+ fileapi::kFileSystemTypeNativeLocal,
base::FilePath(
DRIVE FPL("/test/isolated/root/file")).NormalizePathSeparators(),
base::FilePath::FromUTF8Unsafe(isolated_id).Append(FPL("root/file")).
@@ -134,8 +144,8 @@ TEST_F(FileSystemContextTest, NullExternalMountPoints) {
ExpectFileSystemURLMatches(
cracked_external,
GURL(kTestOrigin),
- kFileSystemTypeExternal,
- kFileSystemTypeNativeLocal,
+ fileapi::kFileSystemTypeExternal,
+ fileapi::kFileSystemTypeNativeLocal,
base::FilePath(
DRIVE FPL("/test/sys/root/file")).NormalizePathSeparators(),
base::FilePath(FPL("system/root/file")).NormalizePathSeparators(),
@@ -154,7 +164,7 @@ TEST_F(FileSystemContextTest, FileSystemContextKeepsMountPointsAlive) {
// Register system external mount point.
ASSERT_TRUE(mount_points->RegisterFileSystem(
"system",
- kFileSystemTypeNativeLocal,
+ fileapi::kFileSystemTypeNativeLocal,
FileSystemMountOption(),
base::FilePath(DRIVE FPL("/test/sys/"))));
@@ -172,8 +182,8 @@ TEST_F(FileSystemContextTest, FileSystemContextKeepsMountPointsAlive) {
ExpectFileSystemURLMatches(
cracked_external,
GURL(kTestOrigin),
- kFileSystemTypeExternal,
- kFileSystemTypeNativeLocal,
+ fileapi::kFileSystemTypeExternal,
+ fileapi::kFileSystemTypeNativeLocal,
base::FilePath(
DRIVE FPL("/test/sys/root/file")).NormalizePathSeparators(),
base::FilePath(FPL("system/root/file")).NormalizePathSeparators(),
@@ -193,32 +203,33 @@ TEST_F(FileSystemContextTest, CrackFileSystemURL) {
std::string isolated_file_system_name = "root";
const std::string kIsolatedFileSystemID =
IsolatedContext::GetInstance()->RegisterFileSystemForPath(
- kFileSystemTypeNativeLocal,
+ fileapi::kFileSystemTypeNativeLocal,
+ std::string(),
base::FilePath(DRIVE FPL("/test/isolated/root")),
&isolated_file_system_name);
// Register system external mount point.
ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
"system",
- kFileSystemTypeDrive,
+ fileapi::kFileSystemTypeDrive,
FileSystemMountOption(),
base::FilePath(DRIVE FPL("/test/sys/"))));
ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
"ext",
- kFileSystemTypeNativeLocal,
+ fileapi::kFileSystemTypeNativeLocal,
FileSystemMountOption(),
base::FilePath(DRIVE FPL("/test/ext"))));
// Register a system external mount point with the same name/id as the
// registered isolated mount point.
ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
kIsolatedFileSystemID,
- kFileSystemTypeRestrictedNativeLocal,
+ fileapi::kFileSystemTypeRestrictedNativeLocal,
FileSystemMountOption(),
base::FilePath(DRIVE FPL("/test/system/isolated"))));
// Add a mount points with the same name as a system mount point to
// FileSystemContext's external mount points.
ASSERT_TRUE(external_mount_points->RegisterFileSystem(
"ext",
- kFileSystemTypeNativeLocal,
+ fileapi::kFileSystemTypeNativeLocal,
FileSystemMountOption(),
base::FilePath(DRIVE FPL("/test/local/ext/"))));
@@ -232,8 +243,8 @@ TEST_F(FileSystemContextTest, CrackFileSystemURL) {
// Expected test results.
bool expect_is_valid;
- FileSystemType expect_mount_type;
- FileSystemType expect_type;
+ fileapi::FileSystemType expect_mount_type;
+ fileapi::FileSystemType expect_type;
const base::FilePath::CharType* expect_path;
std::string expect_filesystem_id;
};
@@ -242,40 +253,41 @@ TEST_F(FileSystemContextTest, CrackFileSystemURL) {
// Following should not be handled by the url crackers:
{
"pers_mount", "persistent", true /* is_valid */,
- kFileSystemTypePersistent, kFileSystemTypePersistent,
+ fileapi::kFileSystemTypePersistent, fileapi::kFileSystemTypePersistent,
FPL("pers_mount/root/file"),
std::string() /* filesystem id */
},
{
"temp_mount", "temporary", true /* is_valid */,
- kFileSystemTypeTemporary, kFileSystemTypeTemporary,
+ fileapi::kFileSystemTypeTemporary, fileapi::kFileSystemTypeTemporary,
FPL("temp_mount/root/file"),
std::string() /* filesystem id */
},
// Should be cracked by isolated mount points:
{
kIsolatedFileSystemID, "isolated", true /* is_valid */,
- kFileSystemTypeIsolated, kFileSystemTypeNativeLocal,
+ fileapi::kFileSystemTypeIsolated, fileapi::kFileSystemTypeNativeLocal,
DRIVE FPL("/test/isolated/root/file"),
kIsolatedFileSystemID
},
// Should be cracked by system mount points:
{
"system", "external", true /* is_valid */,
- kFileSystemTypeExternal, kFileSystemTypeDrive,
+ fileapi::kFileSystemTypeExternal, fileapi::kFileSystemTypeDrive,
DRIVE FPL("/test/sys/root/file"),
"system"
},
{
kIsolatedFileSystemID, "external", true /* is_valid */,
- kFileSystemTypeExternal, kFileSystemTypeRestrictedNativeLocal,
+ fileapi::kFileSystemTypeExternal,
+ fileapi::kFileSystemTypeRestrictedNativeLocal,
DRIVE FPL("/test/system/isolated/root/file"),
kIsolatedFileSystemID
},
// Should be cracked by FileSystemContext's ExternalMountPoints.
{
"ext", "external", true /* is_valid */,
- kFileSystemTypeExternal, kFileSystemTypeNativeLocal,
+ fileapi::kFileSystemTypeExternal, fileapi::kFileSystemTypeNativeLocal,
DRIVE FPL("/test/local/ext/root/file"),
"ext"
},
@@ -284,15 +296,15 @@ TEST_F(FileSystemContextTest, CrackFileSystemURL) {
{
"sytem", "external", false /* is_valid */,
// The rest of values will be ignored.
- kFileSystemTypeUnknown, kFileSystemTypeUnknown, FPL(""),
- std::string()
+ fileapi::kFileSystemTypeUnknown, fileapi::kFileSystemTypeUnknown,
+ FPL(""), std::string()
},
// Test for URL with non-existing filesystem id.
{
"invalid", "external", false /* is_valid */,
// The rest of values will be ignored.
- kFileSystemTypeUnknown, kFileSystemTypeUnknown, FPL(""),
- std::string()
+ fileapi::kFileSystemTypeUnknown, fileapi::kFileSystemTypeUnknown,
+ FPL(""), std::string()
},
};
@@ -339,29 +351,31 @@ TEST_F(FileSystemContextTest, CanServeURLRequest) {
// A request for a sandbox mount point should be served.
FileSystemURL cracked_url =
context->CrackURL(CreateRawFileSystemURL("persistent", "pers_mount"));
- EXPECT_EQ(kFileSystemTypePersistent, cracked_url.mount_type());
+ EXPECT_EQ(fileapi::kFileSystemTypePersistent, cracked_url.mount_type());
EXPECT_TRUE(context->CanServeURLRequest(cracked_url));
// A request for an isolated mount point should NOT be served.
std::string isolated_fs_name = "root";
std::string isolated_fs_id =
IsolatedContext::GetInstance()->RegisterFileSystemForPath(
- kFileSystemTypeNativeLocal,
+ fileapi::kFileSystemTypeNativeLocal,
+ std::string(),
base::FilePath(DRIVE FPL("/test/isolated/root")),
&isolated_fs_name);
cracked_url = context->CrackURL(
CreateRawFileSystemURL("isolated", isolated_fs_id));
- EXPECT_EQ(kFileSystemTypeIsolated, cracked_url.mount_type());
+ EXPECT_EQ(fileapi::kFileSystemTypeIsolated, cracked_url.mount_type());
EXPECT_FALSE(context->CanServeURLRequest(cracked_url));
// A request for an external mount point should be served.
const std::string kExternalMountName = "ext_mount";
ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
- kExternalMountName, kFileSystemTypeDrive, FileSystemMountOption(),
+ kExternalMountName, fileapi::kFileSystemTypeDrive,
+ FileSystemMountOption(),
base::FilePath()));
cracked_url = context->CrackURL(
CreateRawFileSystemURL("external", kExternalMountName));
- EXPECT_EQ(kFileSystemTypeExternal, cracked_url.mount_type());
+ EXPECT_EQ(fileapi::kFileSystemTypeExternal, cracked_url.mount_type());
EXPECT_TRUE(context->CanServeURLRequest(cracked_url));
ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(
@@ -371,4 +385,4 @@ TEST_F(FileSystemContextTest, CanServeURLRequest) {
} // namespace
-} // namespace fileapi
+} // namespace content
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 874e61b0203..8741cff294b 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
@@ -6,15 +6,18 @@
#include <string>
+#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/format_macros.h"
+#include "base/memory/scoped_vector.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
-#include "base/platform_file.h"
#include "base/run_loop.h"
#include "base/strings/string_piece.h"
#include "base/strings/utf_string_conversions.h"
+#include "content/public/test/mock_special_storage_policy.h"
+#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"
@@ -25,18 +28,81 @@
#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/icu/source/i18n/unicode/regex.h"
+#include "webkit/browser/fileapi/external_mount_points.h"
#include "webkit/browser/fileapi/file_system_context.h"
#include "webkit/browser/fileapi/file_system_file_util.h"
#include "webkit/browser/fileapi/file_system_operation_context.h"
#include "webkit/browser/fileapi/file_system_url.h"
-#include "webkit/browser/quota/mock_special_storage_policy.h"
-namespace fileapi {
+using fileapi::FileSystemContext;
+using fileapi::FileSystemOperationContext;
+using fileapi::FileSystemURL;
+
+namespace content {
namespace {
// We always use the TEMPORARY FileSystem in this test.
-static const char kFileSystemURLPrefix[] =
- "filesystem:http://remote/temporary/";
+const char kFileSystemURLPrefix[] = "filesystem:http://remote/temporary/";
+
+const char kValidExternalMountPoint[] = "mnt_name";
+
+// An auto mounter that will try to mount anything for |storage_domain| =
+// "automount", but will only succeed for the mount point "mnt_name".
+bool TestAutoMountForURLRequest(
+ const net::URLRequest* /*url_request*/,
+ const fileapi::FileSystemURL& filesystem_url,
+ const std::string& storage_domain,
+ const base::Callback<void(base::File::Error result)>& callback) {
+ if (storage_domain != "automount")
+ return false;
+
+ std::vector<base::FilePath::StringType> components;
+ filesystem_url.path().GetComponents(&components);
+ std::string mount_point = base::FilePath(components[0]).AsUTF8Unsafe();
+
+ if (mount_point == kValidExternalMountPoint) {
+ fileapi::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
+ kValidExternalMountPoint, fileapi::kFileSystemTypeTest,
+ fileapi::FileSystemMountOption(), base::FilePath());
+ callback.Run(base::File::FILE_OK);
+ } else {
+ callback.Run(base::File::FILE_ERROR_NOT_FOUND);
+ }
+ return true;
+}
+
+class FileSystemDirURLRequestJobFactory : public net::URLRequestJobFactory {
+ public:
+ FileSystemDirURLRequestJobFactory(const std::string& storage_domain,
+ FileSystemContext* context)
+ : storage_domain_(storage_domain), file_system_context_(context) {
+ }
+
+ virtual net::URLRequestJob* MaybeCreateJobWithProtocolHandler(
+ const std::string& scheme,
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) const OVERRIDE {
+ return new fileapi::FileSystemDirURLRequestJob(
+ request, network_delegate, storage_domain_, file_system_context_);
+ }
+
+ virtual bool IsHandledProtocol(const std::string& scheme) const OVERRIDE {
+ return true;
+ }
+
+ virtual bool IsHandledURL(const GURL& url) const OVERRIDE {
+ return true;
+ }
+
+ virtual bool IsSafeRedirectTarget(const GURL& location) const OVERRIDE {
+ return false;
+ }
+
+ private:
+ std::string storage_domain_;
+ FileSystemContext* file_system_context_;
+};
+
} // namespace
@@ -49,45 +115,55 @@ class FileSystemDirURLRequestJobTest : public testing::Test {
virtual void SetUp() OVERRIDE {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
- special_storage_policy_ = new quota::MockSpecialStoragePolicy;
+ special_storage_policy_ = new MockSpecialStoragePolicy;
file_system_context_ = CreateFileSystemContextForTesting(
NULL, temp_dir_.path());
file_system_context_->OpenFileSystem(
- GURL("http://remote/"), kFileSystemTypeTemporary,
- OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+ GURL("http://remote/"), fileapi::kFileSystemTypeTemporary,
+ fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
base::Bind(&FileSystemDirURLRequestJobTest::OnOpenFileSystem,
weak_factory_.GetWeakPtr()));
base::RunLoop().RunUntilIdle();
-
- net::URLRequest::Deprecated::RegisterProtocolFactory(
- "filesystem", &FileSystemDirURLRequestJobFactory);
}
virtual void TearDown() OVERRIDE {
// NOTE: order matters, request must die before delegate
request_.reset(NULL);
delegate_.reset(NULL);
+ }
+
+ void SetUpAutoMountContext(base::FilePath* mnt_point) {
+ *mnt_point = temp_dir_.path().AppendASCII("auto_mount_dir");
+ ASSERT_TRUE(base::CreateDirectory(*mnt_point));
- net::URLRequest::Deprecated::RegisterProtocolFactory("filesystem", NULL);
- ClearUnusedJob();
+ ScopedVector<fileapi::FileSystemBackend> additional_providers;
+ additional_providers.push_back(new TestFileSystemBackend(
+ base::MessageLoopProxy::current().get(), *mnt_point));
+
+ std::vector<fileapi::URLRequestAutoMountHandler> handlers;
+ handlers.push_back(base::Bind(&TestAutoMountForURLRequest));
+
+ file_system_context_ = CreateFileSystemContextWithAutoMountersForTesting(
+ NULL, additional_providers.Pass(), handlers, temp_dir_.path());
}
void OnOpenFileSystem(const GURL& root_url,
const std::string& name,
- base::PlatformFileError result) {
- ASSERT_EQ(base::PLATFORM_FILE_OK, result);
+ base::File::Error result) {
+ ASSERT_EQ(base::File::FILE_OK, result);
}
void TestRequestHelper(const GURL& url, bool run_to_completion,
FileSystemContext* file_system_context) {
delegate_.reset(new net::TestDelegate());
delegate_->set_quit_on_redirect(true);
- request_ = empty_context_.CreateRequest(
- url, net::DEFAULT_PRIORITY, delegate_.get());
- job_ = new FileSystemDirURLRequestJob(
- request_.get(), NULL, file_system_context);
+ job_factory_.reset(new FileSystemDirURLRequestJobFactory(
+ url.GetOrigin().host(), file_system_context));
+ empty_context_.set_job_factory(job_factory_.get());
+ request_ = empty_context_.CreateRequest(
+ url, net::DEFAULT_PRIORITY, delegate_.get(), NULL);
request_->Start();
ASSERT_TRUE(request_->is_pending()); // verify that we're starting async
if (run_to_completion)
@@ -124,7 +200,7 @@ class FileSystemDirURLRequestJobTest : public testing::Test {
void CreateDirectory(const base::StringPiece& dir_name) {
base::FilePath path = base::FilePath().AppendASCII(dir_name);
scoped_ptr<FileSystemOperationContext> context(NewOperationContext());
- ASSERT_EQ(base::PLATFORM_FILE_OK, file_util()->CreateDirectory(
+ ASSERT_EQ(base::File::FILE_OK, file_util()->CreateDirectory(
context.get(),
CreateURL(path),
false /* exclusive */,
@@ -134,26 +210,27 @@ class FileSystemDirURLRequestJobTest : public testing::Test {
void EnsureFileExists(const base::StringPiece file_name) {
base::FilePath path = base::FilePath().AppendASCII(file_name);
scoped_ptr<FileSystemOperationContext> context(NewOperationContext());
- ASSERT_EQ(base::PLATFORM_FILE_OK, file_util()->EnsureFileExists(
+ ASSERT_EQ(base::File::FILE_OK, file_util()->EnsureFileExists(
context.get(), CreateURL(path), NULL));
}
void TruncateFile(const base::StringPiece file_name, int64 length) {
base::FilePath path = base::FilePath().AppendASCII(file_name);
scoped_ptr<FileSystemOperationContext> context(NewOperationContext());
- ASSERT_EQ(base::PLATFORM_FILE_OK, file_util()->Truncate(
+ ASSERT_EQ(base::File::FILE_OK, file_util()->Truncate(
context.get(), CreateURL(path), length));
}
- base::PlatformFileError GetFileInfo(const base::FilePath& path,
- base::PlatformFileInfo* file_info,
- base::FilePath* platform_file_path) {
+ base::File::Error GetFileInfo(const base::FilePath& path,
+ base::File::Info* file_info,
+ base::FilePath* platform_file_path) {
scoped_ptr<FileSystemOperationContext> context(NewOperationContext());
return file_util()->GetFileInfo(context.get(),
CreateURL(path),
file_info, platform_file_path);
}
+ // If |size| is negative, the reported size is ignored.
void VerifyListingEntry(const std::string& entry_line,
const std::string& name,
const std::string& url,
@@ -174,13 +251,15 @@ class FileSystemDirURLRequestJobTest : public testing::Test {
EXPECT_EQ(icu::UnicodeString(url.c_str()), match.group(2, status));
EXPECT_EQ(icu::UnicodeString(is_directory ? "1" : "0"),
match.group(3, status));
- icu::UnicodeString size_string(FormatBytesUnlocalized(size).c_str());
- EXPECT_EQ(size_string, match.group(4, status));
+ if (size >= 0) {
+ icu::UnicodeString size_string(FormatBytesUnlocalized(size).c_str());
+ EXPECT_EQ(size_string, match.group(4, status));
+ }
base::Time date;
icu::UnicodeString date_ustr(match.group(5, status));
std::string date_str;
- UTF16ToUTF8(date_ustr.getBuffer(), date_ustr.length(), &date_str);
+ base::UTF16ToUTF8(date_ustr.getBuffer(), date_ustr.length(), &date_str);
EXPECT_TRUE(base::Time::FromString(date_str.c_str(), &date));
EXPECT_FALSE(date.is_null());
}
@@ -189,24 +268,7 @@ class FileSystemDirURLRequestJobTest : public testing::Test {
return GURL(kFileSystemURLPrefix + path);
}
- static net::URLRequestJob* FileSystemDirURLRequestJobFactory(
- net::URLRequest* request,
- net::NetworkDelegate* network_delegate,
- const std::string& scheme) {
- DCHECK(job_);
- net::URLRequestJob* temp = job_;
- job_ = NULL;
- return temp;
- }
-
- static void ClearUnusedJob() {
- if (job_) {
- scoped_refptr<net::URLRequestJob> deleter = job_;
- job_ = NULL;
- }
- }
-
- FileSystemFileUtil* file_util() {
+ fileapi::FileSystemFileUtil* file_util() {
return file_system_context_->sandbox_delegate()->sync_file_util();
}
@@ -219,16 +281,12 @@ class FileSystemDirURLRequestJobTest : public testing::Test {
net::URLRequestContext empty_context_;
scoped_ptr<net::TestDelegate> delegate_;
scoped_ptr<net::URLRequest> request_;
- scoped_refptr<quota::MockSpecialStoragePolicy> special_storage_policy_;
+ scoped_ptr<FileSystemDirURLRequestJobFactory> job_factory_;
+ scoped_refptr<MockSpecialStoragePolicy> special_storage_policy_;
scoped_refptr<FileSystemContext> file_system_context_;
base::WeakPtrFactory<FileSystemDirURLRequestJobTest> weak_factory_;
-
- static net::URLRequestJob* job_;
};
-// static
-net::URLRequestJob* FileSystemDirURLRequestJobTest::job_ = NULL;
-
namespace {
TEST_F(FileSystemDirURLRequestJobTest, DirectoryListing) {
@@ -248,7 +306,7 @@ TEST_F(FileSystemDirURLRequestJobTest, DirectoryListing) {
std::istringstream in(delegate_->data_received());
std::string line;
- EXPECT_TRUE(std::getline(in, line));
+ EXPECT_TRUE(!!std::getline(in, line));
#if defined(OS_WIN)
EXPECT_EQ("<script>start(\"foo\\\\bar\");</script>", line);
@@ -256,11 +314,12 @@ TEST_F(FileSystemDirURLRequestJobTest, DirectoryListing) {
EXPECT_EQ("<script>start(\"/foo/bar\");</script>", line);
#endif
- EXPECT_TRUE(std::getline(in, line));
+ EXPECT_TRUE(!!std::getline(in, line));
VerifyListingEntry(line, "hoge", "hoge", false, 10);
- EXPECT_TRUE(std::getline(in, line));
+ EXPECT_TRUE(!!std::getline(in, line));
VerifyListingEntry(line, "baz", "baz", true, 0);
+ EXPECT_FALSE(!!std::getline(in, line));
}
TEST_F(FileSystemDirURLRequestJobTest, InvalidURL) {
@@ -307,8 +366,8 @@ TEST_F(FileSystemDirURLRequestJobTest, Incognito) {
std::istringstream in(delegate_->data_received());
std::string line;
- EXPECT_TRUE(std::getline(in, line));
- EXPECT_FALSE(std::getline(in, line));
+ EXPECT_TRUE(!!std::getline(in, line));
+ EXPECT_FALSE(!!std::getline(in, line));
TestRequestWithContext(CreateFileSystemURL("foo"),
file_system_context.get());
@@ -317,5 +376,67 @@ TEST_F(FileSystemDirURLRequestJobTest, Incognito) {
EXPECT_EQ(net::ERR_FILE_NOT_FOUND, request_->status().error());
}
+TEST_F(FileSystemDirURLRequestJobTest, AutoMountDirectoryListing) {
+ base::FilePath mnt_point;
+ SetUpAutoMountContext(&mnt_point);
+ ASSERT_TRUE(base::CreateDirectory(mnt_point));
+ ASSERT_TRUE(base::CreateDirectory(mnt_point.AppendASCII("foo")));
+ ASSERT_EQ(10,
+ base::WriteFile(mnt_point.AppendASCII("bar"), "1234567890", 10));
+
+ TestRequest(GURL("filesystem:http://automount/external/mnt_name"));
+
+ ASSERT_FALSE(request_->is_pending());
+ EXPECT_EQ(1, delegate_->response_started_count());
+ EXPECT_FALSE(delegate_->received_data_before_response());
+ EXPECT_GT(delegate_->bytes_received(), 0);
+
+ std::istringstream in(delegate_->data_received());
+ std::string line;
+ EXPECT_TRUE(!!std::getline(in, line)); // |line| contains the temp dir path.
+
+ // Result order is not guaranteed, so sort the results.
+ std::vector<std::string> listing_entries;
+ while (!!std::getline(in, line))
+ listing_entries.push_back(line);
+
+ ASSERT_EQ(2U, listing_entries.size());
+ std::sort(listing_entries.begin(), listing_entries.end());
+ VerifyListingEntry(listing_entries[0], "bar", "bar", false, 10);
+ VerifyListingEntry(listing_entries[1], "foo", "foo", true, -1);
+
+ ASSERT_TRUE(
+ fileapi::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(
+ kValidExternalMountPoint));
+}
+
+TEST_F(FileSystemDirURLRequestJobTest, AutoMountInvalidRoot) {
+ base::FilePath mnt_point;
+ SetUpAutoMountContext(&mnt_point);
+ TestRequest(GURL("filesystem:http://automount/external/invalid"));
+
+ ASSERT_FALSE(request_->is_pending());
+ ASSERT_FALSE(request_->status().is_success());
+ EXPECT_EQ(net::ERR_FILE_NOT_FOUND, request_->status().error());
+
+ ASSERT_FALSE(
+ fileapi::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(
+ "invalid"));
+}
+
+TEST_F(FileSystemDirURLRequestJobTest, AutoMountNoHandler) {
+ base::FilePath mnt_point;
+ SetUpAutoMountContext(&mnt_point);
+ TestRequest(GURL("filesystem:http://noauto/external/mnt_name"));
+
+ ASSERT_FALSE(request_->is_pending());
+ ASSERT_FALSE(request_->status().is_success());
+ EXPECT_EQ(net::ERR_FILE_NOT_FOUND, request_->status().error());
+
+ ASSERT_FALSE(
+ fileapi::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(
+ kValidExternalMountPoint));
+}
+
} // namespace (anonymous)
-} // namespace fileapi
+} // namespace content
diff --git a/chromium/content/browser/fileapi/file_system_file_stream_reader_unittest.cc b/chromium/content/browser/fileapi/file_system_file_stream_reader_unittest.cc
index 3540d5b8c05..fe1d25e5ddf 100644
--- a/chromium/content/browser/fileapi/file_system_file_stream_reader_unittest.cc
+++ b/chromium/content/browser/fileapi/file_system_file_stream_reader_unittest.cc
@@ -9,19 +9,24 @@
#include "base/files/scoped_temp_dir.h"
#include "base/memory/scoped_ptr.h"
-#include "base/platform_file.h"
#include "base/run_loop.h"
+#include "content/public/test/async_file_test_helper.h"
#include "content/public/test/test_file_system_context.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/base/test_completion_callback.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "webkit/browser/fileapi/async_file_test_helper.h"
#include "webkit/browser/fileapi/external_mount_points.h"
#include "webkit/browser/fileapi/file_system_context.h"
#include "webkit/browser/fileapi/file_system_file_util.h"
-namespace fileapi {
+using content::AsyncFileTestHelper;
+using fileapi::FileSystemContext;
+using fileapi::FileSystemFileStreamReader;
+using fileapi::FileSystemType;
+using fileapi::FileSystemURL;
+
+namespace content {
namespace {
@@ -30,7 +35,7 @@ const char kTestFileName[] = "test.dat";
const char kTestData[] = "0123456789";
const int kTestDataSize = arraysize(kTestData) - 1;
-void ReadFromReader(FileSystemFileStreamReader* reader,
+void ReadFromReader(fileapi::FileSystemFileStreamReader* reader,
std::string* data,
size_t size,
int* result) {
@@ -69,8 +74,8 @@ class FileSystemFileStreamReaderTest : public testing::Test {
NULL, temp_dir_.path());
file_system_context_->OpenFileSystem(
- GURL(kURLOrigin), kFileSystemTypeTemporary,
- OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+ GURL(kURLOrigin), fileapi::kFileSystemTypeTemporary,
+ fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
base::Bind(&OnOpenFileSystem));
base::RunLoop().RunUntilIdle();
@@ -83,7 +88,7 @@ class FileSystemFileStreamReaderTest : public testing::Test {
}
protected:
- FileSystemFileStreamReader* CreateFileReader(
+ fileapi::FileSystemFileStreamReader* CreateFileReader(
const std::string& file_name,
int64 initial_offset,
const base::Time& expected_modification_time) {
@@ -103,12 +108,12 @@ class FileSystemFileStreamReaderTest : public testing::Test {
base::Time* modification_time) {
FileSystemURL url = GetFileSystemURL(file_name);
- ASSERT_EQ(base::PLATFORM_FILE_OK,
- fileapi::AsyncFileTestHelper::CreateFileWithData(
+ ASSERT_EQ(base::File::FILE_OK,
+ content::AsyncFileTestHelper::CreateFileWithData(
file_system_context_, url, buf, buf_size));
- base::PlatformFileInfo file_info;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ base::File::Info file_info;
+ ASSERT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::GetMetadata(
file_system_context_, url, &file_info));
if (modification_time)
@@ -118,14 +123,14 @@ class FileSystemFileStreamReaderTest : public testing::Test {
private:
static void OnOpenFileSystem(const GURL& root_url,
const std::string& name,
- base::PlatformFileError result) {
- ASSERT_EQ(base::PLATFORM_FILE_OK, result);
+ base::File::Error result) {
+ ASSERT_EQ(base::File::FILE_OK, result);
}
FileSystemURL GetFileSystemURL(const std::string& file_name) {
return file_system_context_->CreateCrackedFileSystemURL(
GURL(kURLOrigin),
- kFileSystemTypeTemporary,
+ fileapi::kFileSystemTypeTemporary,
base::FilePath().AppendASCII(file_name));
}
@@ -263,4 +268,4 @@ TEST_F(FileSystemFileStreamReaderTest, DeleteWithUnfinishedRead) {
reader.reset();
}
-} // namespace fileapi
+} // namespace content
diff --git a/chromium/content/browser/fileapi/file_system_operation_impl_unittest.cc b/chromium/content/browser/fileapi/file_system_operation_impl_unittest.cc
index 445657d9a01..2f8e82ec094 100644
--- a/chromium/content/browser/fileapi/file_system_operation_impl_unittest.cc
+++ b/chromium/content/browser/fileapi/file_system_operation_impl_unittest.cc
@@ -12,38 +12,45 @@
#include "base/memory/weak_ptr.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
+#include "content/browser/fileapi/mock_file_change_observer.h"
+#include "content/browser/quota/mock_quota_manager.h"
+#include "content/browser/quota/mock_quota_manager_proxy.h"
+#include "content/public/test/async_file_test_helper.h"
#include "content/public/test/sandbox_file_system_test_helper.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
-#include "webkit/browser/fileapi/async_file_test_helper.h"
#include "webkit/browser/fileapi/file_system_context.h"
#include "webkit/browser/fileapi/file_system_file_util.h"
#include "webkit/browser/fileapi/file_system_operation_context.h"
#include "webkit/browser/fileapi/file_system_operation_runner.h"
-#include "webkit/browser/fileapi/mock_file_change_observer.h"
#include "webkit/browser/fileapi/sandbox_file_system_backend.h"
-#include "webkit/browser/quota/mock_quota_manager.h"
#include "webkit/browser/quota/quota_manager.h"
+#include "webkit/browser/quota/quota_manager_proxy.h"
#include "webkit/common/blob/shareable_file_reference.h"
#include "webkit/common/fileapi/file_system_util.h"
+using content::AsyncFileTestHelper;
+using fileapi::FileSystemOperation;
+using fileapi::FileSystemOperationContext;
+using fileapi::FileSystemOperationRunner;
+using fileapi::FileSystemURL;
using quota::QuotaManager;
using quota::QuotaManagerProxy;
using webkit_blob::ShareableFileReference;
-namespace fileapi {
+namespace content {
namespace {
const int kFileOperationStatusNotSet = 1;
void AssertFileErrorEq(const tracked_objects::Location& from_here,
- base::PlatformFileError expected,
- base::PlatformFileError actual) {
+ base::File::Error expected,
+ base::File::Error actual) {
ASSERT_EQ(expected, actual) << from_here.ToString();
}
-} // namespace (anonymous)
+} // namespace
// Test class for FileSystemOperationImpl.
class FileSystemOperationImplTest
@@ -56,16 +63,17 @@ class FileSystemOperationImplTest
protected:
virtual void SetUp() OVERRIDE {
EXPECT_TRUE(base_.CreateUniqueTempDir());
- change_observers_ = MockFileChangeObserver::CreateList(&change_observer_);
+ change_observers_ = fileapi::MockFileChangeObserver::CreateList(
+ &change_observer_);
base::FilePath base_dir = base_.path().AppendASCII("filesystem");
quota_manager_ =
- new quota::MockQuotaManager(false /* is_incognito */,
+ new MockQuotaManager(false /* is_incognito */,
base_dir,
base::MessageLoopProxy::current().get(),
base::MessageLoopProxy::current().get(),
NULL /* special storage policy */);
- quota_manager_proxy_ = new quota::MockQuotaManagerProxy(
+ quota_manager_proxy_ = new MockQuotaManagerProxy(
quota_manager(), base::MessageLoopProxy::current().get());
sandbox_file_system_.SetUp(base_dir, quota_manager_proxy_.get());
sandbox_file_system_.AddFileChangeObserver(&change_observer_);
@@ -84,9 +92,9 @@ class FileSystemOperationImplTest
}
int status() const { return status_; }
- const base::PlatformFileInfo& info() const { return info_; }
+ const base::File::Info& info() const { return info_; }
const base::FilePath& path() const { return path_; }
- const std::vector<DirectoryEntry>& entries() const {
+ const std::vector<fileapi::DirectoryEntry>& entries() const {
return entries_;
}
@@ -94,20 +102,20 @@ class FileSystemOperationImplTest
return shareable_file_ref_.get();
}
- quota::MockQuotaManager* quota_manager() {
- return static_cast<quota::MockQuotaManager*>(quota_manager_.get());
+ MockQuotaManager* quota_manager() {
+ return static_cast<MockQuotaManager*>(quota_manager_.get());
}
- quota::MockQuotaManagerProxy* quota_manager_proxy() {
- return static_cast<quota::MockQuotaManagerProxy*>(
+ MockQuotaManagerProxy* quota_manager_proxy() {
+ return static_cast<MockQuotaManagerProxy*>(
quota_manager_proxy_.get());
}
- FileSystemFileUtil* file_util() {
+ fileapi::FileSystemFileUtil* file_util() {
return sandbox_file_system_.file_util();
}
- MockFileChangeObserver* change_observer() {
+ fileapi::MockFileChangeObserver* change_observer() {
return &change_observer_;
}
@@ -142,7 +150,7 @@ class FileSystemOperationImplTest
FileSystemURL CreateFile(const std::string& path) {
FileSystemURL url = URLForPath(path);
bool created = false;
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
file_util()->EnsureFileExists(NewContext().get(),
url, &created));
EXPECT_TRUE(created);
@@ -151,14 +159,14 @@ class FileSystemOperationImplTest
FileSystemURL CreateDirectory(const std::string& path) {
FileSystemURL url = URLForPath(path);
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
file_util()->CreateDirectory(NewContext().get(), url,
false /* exclusive */, true));
return url;
}
int64 GetFileSize(const std::string& path) {
- base::PlatformFileInfo info;
+ base::File::Info info;
EXPECT_TRUE(base::GetFileInfo(PlatformPath(path), &info));
return info.size;
}
@@ -185,27 +193,27 @@ class FileSystemOperationImplTest
weak_factory_.GetWeakPtr());
}
- void DidFinish(base::PlatformFileError status) {
+ void DidFinish(base::File::Error status) {
status_ = status;
}
void DidReadDirectory(
- base::PlatformFileError status,
- const std::vector<DirectoryEntry>& entries,
+ base::File::Error status,
+ const std::vector<fileapi::DirectoryEntry>& entries,
bool /* has_more */) {
entries_ = entries;
status_ = status;
}
- void DidGetMetadata(base::PlatformFileError status,
- const base::PlatformFileInfo& info) {
+ void DidGetMetadata(base::File::Error status,
+ const base::File::Info& info) {
info_ = info;
status_ = status;
}
void DidCreateSnapshotFile(
- base::PlatformFileError status,
- const base::PlatformFileInfo& info,
+ base::File::Error status,
+ const base::File::Info& info,
const base::FilePath& platform_path,
const scoped_refptr<ShareableFileReference>& shareable_file_ref) {
info_ = info;
@@ -238,7 +246,7 @@ class FileSystemOperationImplTest
sandbox_file_system_.file_system_context(), url);
operation_runner()->Remove(url, false /* recursive */,
base::Bind(&AssertFileErrorEq, FROM_HERE,
- base::PLATFORM_FILE_OK));
+ base::File::FILE_OK));
base::RunLoop().RunUntilIdle();
change_observer()->ResetCount();
@@ -269,6 +277,7 @@ class FileSystemOperationImplTest
quota + quota_delta);
}
+ private:
base::MessageLoop message_loop_;
scoped_refptr<QuotaManager> quota_manager_;
scoped_refptr<QuotaManagerProxy> quota_manager_proxy_;
@@ -280,13 +289,13 @@ class FileSystemOperationImplTest
// For post-operation status.
int status_;
- base::PlatformFileInfo info_;
+ base::File::Info info_;
base::FilePath path_;
- std::vector<DirectoryEntry> entries_;
+ std::vector<fileapi::DirectoryEntry> entries_;
scoped_refptr<ShareableFileReference> shareable_file_ref_;
- MockFileChangeObserver change_observer_;
- ChangeObserverList change_observers_;
+ fileapi::MockFileChangeObserver change_observer_;
+ fileapi::ChangeObserverList change_observers_;
base::WeakPtrFactory<FileSystemOperationImplTest> weak_factory_;
@@ -299,7 +308,7 @@ TEST_F(FileSystemOperationImplTest, TestMoveFailureSrcDoesntExist) {
FileSystemOperation::OPTION_NONE,
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status());
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, status());
EXPECT_TRUE(change_observer()->HasNoChange());
}
@@ -311,7 +320,7 @@ TEST_F(FileSystemOperationImplTest, TestMoveFailureContainsPath) {
FileSystemOperation::OPTION_NONE,
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, status());
+ EXPECT_EQ(base::File::FILE_ERROR_INVALID_OPERATION, status());
EXPECT_TRUE(change_observer()->HasNoChange());
}
@@ -325,7 +334,7 @@ TEST_F(FileSystemOperationImplTest, TestMoveFailureSrcDirExistsDestFile) {
FileSystemOperation::OPTION_NONE,
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, status());
+ EXPECT_EQ(base::File::FILE_ERROR_INVALID_OPERATION, status());
EXPECT_TRUE(change_observer()->HasNoChange());
}
@@ -340,7 +349,7 @@ TEST_F(FileSystemOperationImplTest,
FileSystemOperation::OPTION_NONE,
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_EMPTY, status());
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_EMPTY, status());
EXPECT_TRUE(change_observer()->HasNoChange());
}
@@ -354,7 +363,7 @@ TEST_F(FileSystemOperationImplTest, TestMoveFailureSrcFileExistsDestDir) {
FileSystemOperation::OPTION_NONE,
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, status());
+ EXPECT_EQ(base::File::FILE_ERROR_INVALID_OPERATION, status());
EXPECT_TRUE(change_observer()->HasNoChange());
}
@@ -365,7 +374,7 @@ TEST_F(FileSystemOperationImplTest, TestMoveFailureDestParentDoesntExist) {
FileSystemOperation::OPTION_NONE,
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status());
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, status());
EXPECT_TRUE(change_observer()->HasNoChange());
}
@@ -377,7 +386,7 @@ TEST_F(FileSystemOperationImplTest, TestMoveSuccessSrcFileAndOverwrite) {
FileSystemOperation::OPTION_NONE,
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
EXPECT_TRUE(FileExists("dest"));
EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
@@ -394,7 +403,7 @@ TEST_F(FileSystemOperationImplTest, TestMoveSuccessSrcFileAndNew) {
FileSystemOperation::OPTION_NONE,
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
EXPECT_TRUE(FileExists("new"));
EXPECT_EQ(1, change_observer()->get_and_reset_create_file_from_count());
@@ -410,7 +419,7 @@ TEST_F(FileSystemOperationImplTest, TestMoveSuccessSrcDirAndOverwrite) {
FileSystemOperation::OPTION_NONE,
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
EXPECT_FALSE(DirectoryExists("src"));
EXPECT_EQ(1, change_observer()->get_and_reset_create_directory_count());
@@ -430,7 +439,7 @@ TEST_F(FileSystemOperationImplTest, TestMoveSuccessSrcDirAndNew) {
FileSystemOperation::OPTION_NONE,
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
EXPECT_FALSE(DirectoryExists("src"));
EXPECT_TRUE(DirectoryExists("dest/new"));
@@ -450,7 +459,7 @@ TEST_F(FileSystemOperationImplTest, TestMoveSuccessSrcDirRecursive) {
FileSystemOperation::OPTION_NONE,
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
EXPECT_TRUE(DirectoryExists("dest/dir"));
EXPECT_TRUE(FileExists("dest/dir/sub"));
@@ -461,13 +470,33 @@ TEST_F(FileSystemOperationImplTest, TestMoveSuccessSrcDirRecursive) {
EXPECT_TRUE(change_observer()->HasNoChange());
}
+TEST_F(FileSystemOperationImplTest, TestMoveSuccessSamePath) {
+ FileSystemURL src_dir(CreateDirectory("src"));
+ CreateDirectory("src/dir");
+ CreateFile("src/dir/sub");
+
+ operation_runner()->Move(src_dir, src_dir,
+ FileSystemOperation::OPTION_NONE,
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::File::FILE_OK, status());
+ EXPECT_TRUE(DirectoryExists("src/dir"));
+ EXPECT_TRUE(FileExists("src/dir/sub"));
+
+ EXPECT_EQ(0, change_observer()->get_and_reset_remove_directory_count());
+ EXPECT_EQ(0, change_observer()->get_and_reset_create_directory_count());
+ EXPECT_EQ(0, change_observer()->get_and_reset_remove_file_count());
+ EXPECT_EQ(0, change_observer()->get_and_reset_create_file_from_count());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
TEST_F(FileSystemOperationImplTest, TestCopyFailureSrcDoesntExist) {
operation_runner()->Copy(URLForPath("a"), URLForPath("b"),
FileSystemOperation::OPTION_NONE,
FileSystemOperationRunner::CopyProgressCallback(),
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status());
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, status());
EXPECT_TRUE(change_observer()->HasNoChange());
}
@@ -480,7 +509,7 @@ TEST_F(FileSystemOperationImplTest, TestCopyFailureContainsPath) {
FileSystemOperationRunner::CopyProgressCallback(),
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, status());
+ EXPECT_EQ(base::File::FILE_ERROR_INVALID_OPERATION, status());
EXPECT_TRUE(change_observer()->HasNoChange());
}
@@ -495,7 +524,7 @@ TEST_F(FileSystemOperationImplTest, TestCopyFailureSrcDirExistsDestFile) {
FileSystemOperationRunner::CopyProgressCallback(),
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, status());
+ EXPECT_EQ(base::File::FILE_ERROR_INVALID_OPERATION, status());
EXPECT_TRUE(change_observer()->HasNoChange());
}
@@ -511,7 +540,7 @@ TEST_F(FileSystemOperationImplTest,
FileSystemOperationRunner::CopyProgressCallback(),
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_EMPTY, status());
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_EMPTY, status());
EXPECT_TRUE(change_observer()->HasNoChange());
}
@@ -525,7 +554,7 @@ TEST_F(FileSystemOperationImplTest, TestCopyFailureSrcFileExistsDestDir) {
FileSystemOperationRunner::CopyProgressCallback(),
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, status());
+ EXPECT_EQ(base::File::FILE_ERROR_INVALID_OPERATION, status());
EXPECT_TRUE(change_observer()->HasNoChange());
}
@@ -538,7 +567,7 @@ TEST_F(FileSystemOperationImplTest, TestCopyFailureDestParentDoesntExist) {
FileSystemOperationRunner::CopyProgressCallback(),
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status());
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, status());
EXPECT_TRUE(change_observer()->HasNoChange());
}
@@ -548,7 +577,7 @@ TEST_F(FileSystemOperationImplTest, TestCopyFailureByQuota) {
FileSystemURL dest_dir(CreateDirectory("dest"));
operation_runner()->Truncate(src_file, 6, RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
EXPECT_EQ(6, GetFileSize("src/file"));
FileSystemURL dest_file(URLForPath("dest/file"));
@@ -561,7 +590,7 @@ TEST_F(FileSystemOperationImplTest, TestCopyFailureByQuota) {
FileSystemOperationRunner::CopyProgressCallback(),
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, status());
+ EXPECT_EQ(base::File::FILE_ERROR_NO_SPACE, status());
EXPECT_FALSE(FileExists("dest/file"));
}
@@ -574,7 +603,7 @@ TEST_F(FileSystemOperationImplTest, TestCopySuccessSrcFileAndOverwrite) {
FileSystemOperationRunner::CopyProgressCallback(),
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
EXPECT_TRUE(FileExists("dest"));
EXPECT_EQ(2, quota_manager_proxy()->notify_storage_accessed_count());
@@ -590,7 +619,7 @@ TEST_F(FileSystemOperationImplTest, TestCopySuccessSrcFileAndNew) {
FileSystemOperationRunner::CopyProgressCallback(),
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
EXPECT_TRUE(FileExists("new"));
EXPECT_EQ(2, quota_manager_proxy()->notify_storage_accessed_count());
@@ -607,7 +636,7 @@ TEST_F(FileSystemOperationImplTest, TestCopySuccessSrcDirAndOverwrite) {
FileSystemOperationRunner::CopyProgressCallback(),
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
// Make sure we've overwritten but not copied the source under the |dest_dir|.
EXPECT_TRUE(DirectoryExists("dest"));
@@ -628,7 +657,7 @@ TEST_F(FileSystemOperationImplTest, TestCopySuccessSrcDirAndNew) {
FileSystemOperationRunner::CopyProgressCallback(),
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
EXPECT_TRUE(DirectoryExists("dest"));
EXPECT_GE(quota_manager_proxy()->notify_storage_accessed_count(), 2);
@@ -649,7 +678,7 @@ TEST_F(FileSystemOperationImplTest, TestCopySuccessSrcDirRecursive) {
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
EXPECT_TRUE(DirectoryExists("dest/dir"));
EXPECT_TRUE(FileExists("dest/dir/sub"));
@@ -662,12 +691,33 @@ TEST_F(FileSystemOperationImplTest, TestCopySuccessSrcDirRecursive) {
EXPECT_TRUE(change_observer()->HasNoChange());
}
+TEST_F(FileSystemOperationImplTest, TestCopySuccessSamePath) {
+ FileSystemURL src_dir(CreateDirectory("src"));
+ CreateDirectory("src/dir");
+ CreateFile("src/dir/sub");
+
+ operation_runner()->Copy(src_dir, src_dir,
+ FileSystemOperation::OPTION_NONE,
+ FileSystemOperationRunner::CopyProgressCallback(),
+ RecordStatusCallback());
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(base::File::FILE_OK, status());
+ EXPECT_TRUE(DirectoryExists("src/dir"));
+ EXPECT_TRUE(FileExists("src/dir/sub"));
+
+ EXPECT_EQ(0, change_observer()->get_and_reset_create_directory_count());
+ EXPECT_EQ(0, change_observer()->get_and_reset_remove_file_count());
+ EXPECT_EQ(0, change_observer()->get_and_reset_create_file_from_count());
+ EXPECT_TRUE(change_observer()->HasNoChange());
+}
+
TEST_F(FileSystemOperationImplTest, TestCopyInForeignFileSuccess) {
base::FilePath src_local_disk_file_path;
base::CreateTemporaryFile(&src_local_disk_file_path);
const char test_data[] = "foo";
int data_size = ARRAYSIZE_UNSAFE(test_data);
- file_util::WriteFile(src_local_disk_file_path, test_data, data_size);
+ base::WriteFile(src_local_disk_file_path, test_data, data_size);
FileSystemURL dest_dir(CreateDirectory("dest"));
@@ -681,7 +731,7 @@ TEST_F(FileSystemOperationImplTest, TestCopyInForeignFileSuccess) {
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, change_observer()->create_file_count());
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
EXPECT_TRUE(FileExists("dest/file"));
int64 after_usage;
GetUsageAndQuota(&after_usage, NULL);
@@ -699,7 +749,7 @@ TEST_F(FileSystemOperationImplTest, TestCopyInForeignFileFailureByQuota) {
base::FilePath src_local_disk_file_path;
base::CreateTemporaryFile(&src_local_disk_file_path);
const char test_data[] = "foo";
- file_util::WriteFile(src_local_disk_file_path, test_data,
+ base::WriteFile(src_local_disk_file_path, test_data,
ARRAYSIZE_UNSAFE(test_data));
FileSystemURL dest_dir(CreateDirectory("dest"));
@@ -712,7 +762,7 @@ TEST_F(FileSystemOperationImplTest, TestCopyInForeignFileFailureByQuota) {
EXPECT_FALSE(FileExists("dest/file"));
EXPECT_EQ(0, change_observer()->create_file_count());
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, status());
+ EXPECT_EQ(base::File::FILE_ERROR_NO_SPACE, status());
}
TEST_F(FileSystemOperationImplTest, TestCreateFileFailure) {
@@ -720,7 +770,7 @@ TEST_F(FileSystemOperationImplTest, TestCreateFileFailure) {
FileSystemURL file(CreateFile("file"));
operation_runner()->CreateFile(file, true, RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS, status());
+ EXPECT_EQ(base::File::FILE_ERROR_EXISTS, status());
EXPECT_TRUE(change_observer()->HasNoChange());
}
@@ -729,7 +779,7 @@ TEST_F(FileSystemOperationImplTest, TestCreateFileSuccessFileExists) {
FileSystemURL file(CreateFile("file"));
operation_runner()->CreateFile(file, false, RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
EXPECT_TRUE(FileExists("file"));
// The file was already there; did nothing.
@@ -741,7 +791,7 @@ TEST_F(FileSystemOperationImplTest, TestCreateFileSuccessExclusive) {
operation_runner()->CreateFile(URLForPath("new"), true,
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
EXPECT_TRUE(FileExists("new"));
EXPECT_EQ(1, change_observer()->get_and_reset_create_file_count());
}
@@ -751,7 +801,7 @@ TEST_F(FileSystemOperationImplTest, TestCreateFileSuccessFileDoesntExist) {
operation_runner()->CreateFile(URLForPath("nonexistent"), false,
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
EXPECT_EQ(1, change_observer()->get_and_reset_create_file_count());
}
@@ -762,7 +812,7 @@ TEST_F(FileSystemOperationImplTest,
URLForPath("nonexistent/dir"), false, false,
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status());
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, status());
EXPECT_TRUE(change_observer()->HasNoChange());
}
@@ -772,7 +822,7 @@ TEST_F(FileSystemOperationImplTest, TestCreateDirFailureDirExists) {
operation_runner()->CreateDirectory(dir, true, false,
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS, status());
+ EXPECT_EQ(base::File::FILE_ERROR_EXISTS, status());
EXPECT_TRUE(change_observer()->HasNoChange());
}
@@ -782,7 +832,7 @@ TEST_F(FileSystemOperationImplTest, TestCreateDirFailureFileExists) {
operation_runner()->CreateDirectory(file, true, false,
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS, status());
+ EXPECT_EQ(base::File::FILE_ERROR_EXISTS, status());
EXPECT_TRUE(change_observer()->HasNoChange());
}
@@ -792,14 +842,14 @@ TEST_F(FileSystemOperationImplTest, TestCreateDirSuccess) {
operation_runner()->CreateDirectory(dir, false, false,
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
EXPECT_TRUE(change_observer()->HasNoChange());
// Dir doesn't exist.
operation_runner()->CreateDirectory(URLForPath("new"), false, false,
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
EXPECT_TRUE(DirectoryExists("new"));
EXPECT_EQ(1, change_observer()->get_and_reset_create_directory_count());
}
@@ -809,7 +859,7 @@ TEST_F(FileSystemOperationImplTest, TestCreateDirSuccessExclusive) {
operation_runner()->CreateDirectory(URLForPath("new"), true, false,
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
EXPECT_TRUE(DirectoryExists("new"));
EXPECT_EQ(1, change_observer()->get_and_reset_create_directory_count());
EXPECT_TRUE(change_observer()->HasNoChange());
@@ -819,17 +869,17 @@ TEST_F(FileSystemOperationImplTest, TestExistsAndMetadataFailure) {
operation_runner()->GetMetadata(URLForPath("nonexistent"),
RecordMetadataCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status());
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, status());
operation_runner()->FileExists(URLForPath("nonexistent"),
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status());
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, status());
operation_runner()->DirectoryExists(URLForPath("nonexistent"),
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status());
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, status());
EXPECT_TRUE(change_observer()->HasNoChange());
}
@@ -840,23 +890,23 @@ TEST_F(FileSystemOperationImplTest, TestExistsAndMetadataSuccess) {
operation_runner()->DirectoryExists(dir, RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
++read_access;
operation_runner()->GetMetadata(dir, RecordMetadataCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
EXPECT_TRUE(info().is_directory);
++read_access;
operation_runner()->FileExists(file, RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
++read_access;
operation_runner()->GetMetadata(file, RecordMetadataCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
EXPECT_FALSE(info().is_directory);
++read_access;
@@ -869,12 +919,12 @@ TEST_F(FileSystemOperationImplTest, TestTypeMismatchErrors) {
FileSystemURL dir(CreateDirectory("dir"));
operation_runner()->FileExists(dir, RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_A_FILE, status());
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_A_FILE, status());
FileSystemURL file(CreateFile("file"));
operation_runner()->DirectoryExists(file, RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY, status());
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_A_DIRECTORY, status());
}
TEST_F(FileSystemOperationImplTest, TestReadDirFailure) {
@@ -882,13 +932,13 @@ TEST_F(FileSystemOperationImplTest, TestReadDirFailure) {
operation_runner()->ReadDirectory(URLForPath("nonexistent"),
RecordReadDirectoryCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status());
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, status());
// File exists.
FileSystemURL file(CreateFile("file"));
operation_runner()->ReadDirectory(file, RecordReadDirectoryCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY, status());
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_A_DIRECTORY, status());
EXPECT_TRUE(change_observer()->HasNoChange());
}
@@ -903,7 +953,7 @@ TEST_F(FileSystemOperationImplTest, TestReadDirSuccess) {
operation_runner()->ReadDirectory(parent_dir, RecordReadDirectoryCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
EXPECT_EQ(2u, entries().size());
for (size_t i = 0; i < entries().size(); ++i) {
@@ -921,7 +971,7 @@ TEST_F(FileSystemOperationImplTest, TestRemoveFailure) {
operation_runner()->Remove(URLForPath("nonexistent"), false /* recursive */,
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status());
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, status());
// It's an error to try to remove a non-empty directory if recursive flag
// is false.
@@ -936,7 +986,7 @@ TEST_F(FileSystemOperationImplTest, TestRemoveFailure) {
operation_runner()->Remove(parent_dir, false /* recursive */,
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_EMPTY, status());
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_EMPTY, status());
EXPECT_TRUE(change_observer()->HasNoChange());
}
@@ -946,7 +996,7 @@ TEST_F(FileSystemOperationImplTest, TestRemoveSuccess) {
operation_runner()->Remove(empty_dir, false /* recursive */,
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
EXPECT_FALSE(DirectoryExists("empty_dir"));
EXPECT_EQ(1, change_observer()->get_and_reset_remove_directory_count());
@@ -972,7 +1022,7 @@ TEST_F(FileSystemOperationImplTest, TestRemoveSuccessRecursive) {
operation_runner()->Remove(parent_dir, true /* recursive */,
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
EXPECT_FALSE(DirectoryExists("parent_dir"));
EXPECT_EQ(2, change_observer()->get_and_reset_remove_directory_count());
@@ -987,12 +1037,12 @@ TEST_F(FileSystemOperationImplTest, TestTruncate) {
char test_data[] = "test data";
int data_size = static_cast<int>(sizeof(test_data));
EXPECT_EQ(data_size,
- file_util::WriteFile(platform_path, test_data, data_size));
+ base::WriteFile(platform_path, test_data, data_size));
// Check that its length is the size of the data written.
operation_runner()->GetMetadata(file, RecordMetadataCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
EXPECT_FALSE(info().is_directory);
EXPECT_EQ(data_size, info().size);
@@ -1000,7 +1050,7 @@ TEST_F(FileSystemOperationImplTest, TestTruncate) {
int length = 17;
operation_runner()->Truncate(file, length, RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
EXPECT_TRUE(change_observer()->HasNoChange());
@@ -1021,7 +1071,7 @@ TEST_F(FileSystemOperationImplTest, TestTruncate) {
length = 3;
operation_runner()->Truncate(file, length, RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
EXPECT_TRUE(change_observer()->HasNoChange());
@@ -1046,7 +1096,7 @@ TEST_F(FileSystemOperationImplTest, TestTruncateFailureByQuota) {
operation_runner()->Truncate(file, 10, RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
EXPECT_TRUE(change_observer()->HasNoChange());
@@ -1054,7 +1104,7 @@ TEST_F(FileSystemOperationImplTest, TestTruncateFailureByQuota) {
operation_runner()->Truncate(file, 11, RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, status());
+ EXPECT_EQ(base::File::FILE_ERROR_NO_SPACE, status());
EXPECT_TRUE(change_observer()->HasNoChange());
EXPECT_EQ(10, GetFileSize("dir/file"));
@@ -1064,7 +1114,7 @@ TEST_F(FileSystemOperationImplTest, TestTouchFile) {
FileSystemURL file(CreateFile("file"));
base::FilePath platform_path = PlatformPath("file");
- base::PlatformFileInfo info;
+ base::File::Info info;
EXPECT_TRUE(base::GetFileInfo(platform_path, &info));
EXPECT_FALSE(info.is_directory);
EXPECT_EQ(0, info.size);
@@ -1080,7 +1130,7 @@ TEST_F(FileSystemOperationImplTest, TestTouchFile) {
operation_runner()->TouchFile(file, new_accessed_time, new_modified_time,
RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
EXPECT_TRUE(change_observer()->HasNoChange());
EXPECT_TRUE(base::GetFileInfo(platform_path, &info));
@@ -1099,7 +1149,7 @@ TEST_F(FileSystemOperationImplTest, TestCreateSnapshotFile) {
FileSystemURL file(CreateFile("dir/file"));
operation_runner()->FileExists(file, RecordStatusCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
// See if we can get a 'snapshot' file info for the file.
// Since FileSystemOperationImpl assumes the file exists in the local
@@ -1107,7 +1157,7 @@ TEST_F(FileSystemOperationImplTest, TestCreateSnapshotFile) {
// as the file itself.
operation_runner()->CreateSnapshotFile(file, RecordSnapshotFileCallback());
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
EXPECT_FALSE(info().is_directory);
EXPECT_EQ(PlatformPath("dir/file"), path());
EXPECT_TRUE(change_observer()->HasNoChange());
@@ -1134,16 +1184,16 @@ TEST_F(FileSystemOperationImplTest,
operation_runner()->Truncate(
child_file1, 5000,
- base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK));
+ base::Bind(&AssertFileErrorEq, FROM_HERE, base::File::FILE_OK));
operation_runner()->Truncate(
child_file2, 400,
- base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK));
+ base::Bind(&AssertFileErrorEq, FROM_HERE, base::File::FILE_OK));
operation_runner()->Truncate(
grandchild_file1, 30,
- base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK));
+ base::Bind(&AssertFileErrorEq, FROM_HERE, base::File::FILE_OK));
operation_runner()->Truncate(
grandchild_file2, 2,
- base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK));
+ base::Bind(&AssertFileErrorEq, FROM_HERE, base::File::FILE_OK));
base::RunLoop().RunUntilIdle();
const int64 all_file_size = 5000 + 400 + 30 + 2;
@@ -1152,7 +1202,7 @@ TEST_F(FileSystemOperationImplTest,
operation_runner()->Move(
src, dest, FileSystemOperation::OPTION_NONE,
- base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK));
+ base::Bind(&AssertFileErrorEq, FROM_HERE, base::File::FILE_OK));
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(DirectoryExists("src/dir"));
@@ -1187,16 +1237,16 @@ TEST_F(FileSystemOperationImplTest,
operation_runner()->Truncate(
child_file1, 8000,
- base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK));
+ base::Bind(&AssertFileErrorEq, FROM_HERE, base::File::FILE_OK));
operation_runner()->Truncate(
child_file2, 700,
- base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK));
+ base::Bind(&AssertFileErrorEq, FROM_HERE, base::File::FILE_OK));
operation_runner()->Truncate(
grandchild_file1, 60,
- base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK));
+ base::Bind(&AssertFileErrorEq, FROM_HERE, base::File::FILE_OK));
operation_runner()->Truncate(
grandchild_file2, 5,
- base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK));
+ base::Bind(&AssertFileErrorEq, FROM_HERE, base::File::FILE_OK));
base::RunLoop().RunUntilIdle();
const int64 child_file_size = 8000 + 700;
@@ -1212,7 +1262,7 @@ TEST_F(FileSystemOperationImplTest,
operation_runner()->Copy(
src, dest1, FileSystemOperation::OPTION_NONE,
FileSystemOperationRunner::CopyProgressCallback(),
- base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK));
+ base::Bind(&AssertFileErrorEq, FROM_HERE, base::File::FILE_OK));
base::RunLoop().RunUntilIdle();
expected_usage += all_file_size + child_path_cost + grandchild_path_cost;
@@ -1228,7 +1278,7 @@ TEST_F(FileSystemOperationImplTest,
operation_runner()->Copy(
child_dir, dest2, FileSystemOperation::OPTION_NONE,
FileSystemOperationRunner::CopyProgressCallback(),
- base::Bind(&AssertFileErrorEq, FROM_HERE, base::PLATFORM_FILE_OK));
+ base::Bind(&AssertFileErrorEq, FROM_HERE, base::File::FILE_OK));
base::RunLoop().RunUntilIdle();
expected_usage += grandchild_file_size + grandchild_path_cost;
@@ -1238,4 +1288,4 @@ TEST_F(FileSystemOperationImplTest,
EXPECT_EQ(expected_usage, usage);
}
-} // namespace fileapi
+} // namespace content
diff --git a/chromium/content/browser/fileapi/file_system_operation_impl_write_unittest.cc b/chromium/content/browser/fileapi/file_system_operation_impl_write_unittest.cc
index a92288af0fd..47b0e2d122b 100644
--- a/chromium/content/browser/fileapi/file_system_operation_impl_write_unittest.cc
+++ b/chromium/content/browser/fileapi/file_system_operation_impl_write_unittest.cc
@@ -9,6 +9,9 @@
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/run_loop.h"
+#include "content/browser/fileapi/mock_file_change_observer.h"
+#include "content/browser/quota/mock_quota_manager.h"
+#include "content/public/test/mock_blob_url_request_context.h"
#include "content/public/test/test_file_system_backend.h"
#include "content/public/test/test_file_system_context.h"
#include "net/url_request/url_request.h"
@@ -19,29 +22,29 @@
#include "url/gurl.h"
#include "webkit/browser/blob/blob_storage_context.h"
#include "webkit/browser/blob/blob_url_request_job.h"
-#include "webkit/browser/blob/mock_blob_url_request_context.h"
#include "webkit/browser/fileapi/file_system_context.h"
#include "webkit/browser/fileapi/file_system_file_util.h"
#include "webkit/browser/fileapi/file_system_operation_context.h"
#include "webkit/browser/fileapi/file_system_operation_runner.h"
#include "webkit/browser/fileapi/local_file_util.h"
-#include "webkit/browser/fileapi/mock_file_change_observer.h"
-#include "webkit/browser/quota/mock_quota_manager.h"
#include "webkit/common/blob/blob_data.h"
#include "webkit/common/fileapi/file_system_util.h"
-using webkit_blob::MockBlobURLRequestContext;
-using webkit_blob::ScopedTextBlob;
+using fileapi::FileSystemOperation;
+using fileapi::FileSystemOperationRunner;
+using fileapi::FileSystemURL;
+using content::MockBlobURLRequestContext;
+using content::ScopedTextBlob;
-namespace fileapi {
+namespace content {
namespace {
const GURL kOrigin("http://example.com");
-const FileSystemType kFileSystemType = kFileSystemTypeTest;
+const fileapi::FileSystemType kFileSystemType = fileapi::kFileSystemTypeTest;
-void AssertStatusEq(base::PlatformFileError expected,
- base::PlatformFileError actual) {
+void AssertStatusEq(base::File::Error expected,
+ base::File::Error actual) {
ASSERT_EQ(expected, actual);
}
@@ -51,19 +54,20 @@ class FileSystemOperationImplWriteTest
: public testing::Test {
public:
FileSystemOperationImplWriteTest()
- : status_(base::PLATFORM_FILE_OK),
- cancel_status_(base::PLATFORM_FILE_ERROR_FAILED),
+ : status_(base::File::FILE_OK),
+ cancel_status_(base::File::FILE_ERROR_FAILED),
bytes_written_(0),
complete_(false),
weak_factory_(this) {
- change_observers_ = MockFileChangeObserver::CreateList(&change_observer_);
+ change_observers_ = fileapi::MockFileChangeObserver::CreateList(
+ &change_observer_);
}
virtual void SetUp() {
ASSERT_TRUE(dir_.CreateUniqueTempDir());
quota_manager_ =
- new quota::MockQuotaManager(false /* is_incognito */,
+ new MockQuotaManager(false /* is_incognito */,
dir_.path(),
base::MessageLoopProxy::current().get(),
base::MessageLoopProxy::current().get(),
@@ -77,7 +81,7 @@ class FileSystemOperationImplWriteTest
file_system_context_->operation_runner()->CreateFile(
URLForPath(virtual_path_), true /* exclusive */,
- base::Bind(&AssertStatusEq, base::PLATFORM_FILE_OK));
+ base::Bind(&AssertStatusEq, base::File::FILE_OK));
static_cast<TestFileSystemBackend*>(
file_system_context_->GetFileSystemBackend(kFileSystemType))
@@ -90,8 +94,8 @@ class FileSystemOperationImplWriteTest
base::RunLoop().RunUntilIdle();
}
- base::PlatformFileError status() const { return status_; }
- base::PlatformFileError cancel_status() const { return cancel_status_; }
+ base::File::Error status() const { return status_; }
+ base::File::Error cancel_status() const { return cancel_status_; }
void add_bytes_written(int64 bytes, bool complete) {
bytes_written_ += bytes;
EXPECT_FALSE(complete_);
@@ -101,11 +105,11 @@ class FileSystemOperationImplWriteTest
bool complete() const { return complete_; }
protected:
- const ChangeObserverList& change_observers() const {
+ const fileapi::ChangeObserverList& change_observers() const {
return change_observers_;
}
- MockFileChangeObserver* change_observer() {
+ fileapi::MockFileChangeObserver* change_observer() {
return &change_observer_;
}
@@ -125,14 +129,14 @@ class FileSystemOperationImplWriteTest
weak_factory_.GetWeakPtr());
}
- void DidWrite(base::PlatformFileError status, int64 bytes, bool complete) {
- if (status == base::PLATFORM_FILE_OK) {
+ void DidWrite(base::File::Error status, int64 bytes, bool complete) {
+ if (status == base::File::FILE_OK) {
add_bytes_written(bytes, complete);
if (complete)
base::MessageLoop::current()->Quit();
} else {
EXPECT_FALSE(complete_);
- EXPECT_EQ(status_, base::PLATFORM_FILE_OK);
+ EXPECT_EQ(status_, base::File::FILE_OK);
complete_ = true;
status_ = status;
if (base::MessageLoop::current()->is_running())
@@ -140,7 +144,7 @@ class FileSystemOperationImplWriteTest
}
}
- void DidCancel(base::PlatformFileError status) {
+ void DidCancel(base::File::Error status) {
cancel_status_ = status;
}
@@ -148,8 +152,8 @@ class FileSystemOperationImplWriteTest
return *url_request_context_;
}
- scoped_refptr<FileSystemContext> file_system_context_;
- scoped_refptr<quota::MockQuotaManager> quota_manager_;
+ scoped_refptr<fileapi::FileSystemContext> file_system_context_;
+ scoped_refptr<MockQuotaManager> quota_manager_;
base::MessageLoopForIO loop_;
@@ -157,15 +161,15 @@ class FileSystemOperationImplWriteTest
base::FilePath virtual_path_;
// For post-operation status.
- base::PlatformFileError status_;
- base::PlatformFileError cancel_status_;
+ base::File::Error status_;
+ base::File::Error cancel_status_;
int64 bytes_written_;
bool complete_;
scoped_ptr<MockBlobURLRequestContext> url_request_context_;
- MockFileChangeObserver change_observer_;
- ChangeObserverList change_observers_;
+ fileapi::MockFileChangeObserver change_observer_;
+ fileapi::ChangeObserverList change_observers_;
base::WeakPtrFactory<FileSystemOperationImplWriteTest> weak_factory_;
@@ -183,7 +187,7 @@ TEST_F(FileSystemOperationImplWriteTest, TestWriteSuccess) {
base::MessageLoop::current()->Run();
EXPECT_EQ(14, bytes_written());
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
EXPECT_TRUE(complete());
EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
@@ -197,7 +201,7 @@ TEST_F(FileSystemOperationImplWriteTest, TestWriteZero) {
base::MessageLoop::current()->Run();
EXPECT_EQ(0, bytes_written());
- EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_EQ(base::File::FILE_OK, status());
EXPECT_TRUE(complete());
EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
@@ -212,7 +216,7 @@ TEST_F(FileSystemOperationImplWriteTest, TestWriteInvalidBlobUrl) {
base::MessageLoop::current()->Run();
EXPECT_EQ(0, bytes_written());
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_FAILED, status());
+ EXPECT_EQ(base::File::FILE_ERROR_FAILED, status());
EXPECT_TRUE(complete());
EXPECT_EQ(0, change_observer()->get_and_reset_modify_file_count());
@@ -228,7 +232,7 @@ TEST_F(FileSystemOperationImplWriteTest, TestWriteInvalidFile) {
base::MessageLoop::current()->Run();
EXPECT_EQ(0, bytes_written());
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status());
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, status());
EXPECT_TRUE(complete());
EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
@@ -239,7 +243,7 @@ TEST_F(FileSystemOperationImplWriteTest, TestWriteDir) {
file_system_context_->operation_runner()->CreateDirectory(
URLForPath(virtual_dir_path),
true /* exclusive */, false /* recursive */,
- base::Bind(&AssertStatusEq, base::PLATFORM_FILE_OK));
+ base::Bind(&AssertStatusEq, base::File::FILE_OK));
ScopedTextBlob blob(url_request_context(), "blob:writedir",
"It\'ll not be written, too.");
@@ -250,10 +254,10 @@ TEST_F(FileSystemOperationImplWriteTest, TestWriteDir) {
EXPECT_EQ(0, bytes_written());
// TODO(kinuko): This error code is platform- or fileutil- dependent
- // right now. Make it return PLATFORM_FILE_ERROR_NOT_A_FILE in every case.
- EXPECT_TRUE(status() == base::PLATFORM_FILE_ERROR_NOT_A_FILE ||
- status() == base::PLATFORM_FILE_ERROR_ACCESS_DENIED ||
- status() == base::PLATFORM_FILE_ERROR_FAILED);
+ // right now. Make it return File::FILE_ERROR_NOT_A_FILE in every case.
+ EXPECT_TRUE(status() == base::File::FILE_ERROR_NOT_A_FILE ||
+ status() == base::File::FILE_ERROR_ACCESS_DENIED ||
+ status() == base::File::FILE_ERROR_FAILED);
EXPECT_TRUE(complete());
EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
@@ -270,7 +274,7 @@ TEST_F(FileSystemOperationImplWriteTest, TestWriteFailureByQuota) {
base::MessageLoop::current()->Run();
EXPECT_EQ(10, bytes_written());
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, status());
+ EXPECT_EQ(base::File::FILE_ERROR_NO_SPACE, status());
EXPECT_TRUE(complete());
EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
@@ -292,8 +296,8 @@ TEST_F(FileSystemOperationImplWriteTest, TestImmediateCancelSuccessfulWrite) {
// Issued Cancel() before receiving any response from Write(),
// so nothing should have happen.
EXPECT_EQ(0, bytes_written());
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_ABORT, status());
- EXPECT_EQ(base::PLATFORM_FILE_OK, cancel_status());
+ EXPECT_EQ(base::File::FILE_ERROR_ABORT, status());
+ EXPECT_EQ(base::File::FILE_OK, cancel_status());
EXPECT_TRUE(complete());
EXPECT_EQ(0, change_observer()->get_and_reset_modify_file_count());
@@ -316,8 +320,8 @@ TEST_F(FileSystemOperationImplWriteTest, TestImmediateCancelFailingWrite) {
// Issued Cancel() before receiving any response from Write(),
// so nothing should have happen.
EXPECT_EQ(0, bytes_written());
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_ABORT, status());
- EXPECT_EQ(base::PLATFORM_FILE_OK, cancel_status());
+ EXPECT_EQ(base::File::FILE_ERROR_ABORT, status());
+ EXPECT_EQ(base::File::FILE_OK, cancel_status());
EXPECT_TRUE(complete());
EXPECT_EQ(0, change_observer()->get_and_reset_modify_file_count());
@@ -325,4 +329,4 @@ TEST_F(FileSystemOperationImplWriteTest, TestImmediateCancelFailingWrite) {
// TODO(ericu,dmikurube,kinuko): Add more tests for cancel cases.
-} // namespace fileapi
+} // namespace content
diff --git a/chromium/content/browser/fileapi/file_system_operation_runner_unittest.cc b/chromium/content/browser/fileapi/file_system_operation_runner_unittest.cc
index 1fa711b8672..af5f8e74ba9 100644
--- a/chromium/content/browser/fileapi/file_system_operation_runner_unittest.cc
+++ b/chromium/content/browser/fileapi/file_system_operation_runner_unittest.cc
@@ -5,18 +5,22 @@
#include "base/basictypes.h"
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
-#include "base/platform_file.h"
#include "base/run_loop.h"
#include "content/public/test/test_file_system_context.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "webkit/browser/fileapi/file_system_context.h"
#include "webkit/browser/fileapi/file_system_operation_runner.h"
-namespace fileapi {
+using fileapi::FileSystemContext;
+using fileapi::FileSystemOperationRunner;
+using fileapi::FileSystemType;
+using fileapi::FileSystemURL;
+
+namespace content {
void GetStatus(bool* done,
- base::PlatformFileError *status_out,
- base::PlatformFileError status) {
+ base::File::Error *status_out,
+ base::File::Error status) {
ASSERT_FALSE(*done);
*done = true;
*status_out = status;
@@ -24,8 +28,8 @@ void GetStatus(bool* done,
void GetCancelStatus(bool* operation_done,
bool* cancel_done,
- base::PlatformFileError *status_out,
- base::PlatformFileError status) {
+ base::File::Error *status_out,
+ base::File::Error status) {
// Cancel callback must be always called after the operation's callback.
ASSERT_TRUE(*operation_done);
ASSERT_FALSE(*cancel_done);
@@ -52,7 +56,7 @@ class FileSystemOperationRunnerTest : public testing::Test {
FileSystemURL URL(const std::string& path) {
return file_system_context_->CreateCrackedFileSystemURL(
- GURL("http://example.com"), kFileSystemTypeTemporary,
+ GURL("http://example.com"), fileapi::kFileSystemTypeTemporary,
base::FilePath::FromUTF8Unsafe(path));
}
@@ -70,7 +74,7 @@ class FileSystemOperationRunnerTest : public testing::Test {
TEST_F(FileSystemOperationRunnerTest, NotFoundError) {
bool done = false;
- base::PlatformFileError status = base::PLATFORM_FILE_ERROR_FAILED;
+ base::File::Error status = base::File::FILE_ERROR_FAILED;
// Regular NOT_FOUND error, which is called asynchronously.
operation_runner()->Truncate(URL("foo"), 0,
@@ -78,12 +82,12 @@ TEST_F(FileSystemOperationRunnerTest, NotFoundError) {
ASSERT_FALSE(done);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(done);
- ASSERT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status);
+ ASSERT_EQ(base::File::FILE_ERROR_NOT_FOUND, status);
}
TEST_F(FileSystemOperationRunnerTest, InvalidURLError) {
bool done = false;
- base::PlatformFileError status = base::PLATFORM_FILE_ERROR_FAILED;
+ base::File::Error status = base::File::FILE_ERROR_FAILED;
// Invalid URL error, which calls DidFinish synchronously.
operation_runner()->Truncate(FileSystemURL(), 0,
@@ -93,14 +97,14 @@ TEST_F(FileSystemOperationRunnerTest, InvalidURLError) {
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(done);
- ASSERT_EQ(base::PLATFORM_FILE_ERROR_INVALID_URL, status);
+ ASSERT_EQ(base::File::FILE_ERROR_INVALID_URL, status);
}
TEST_F(FileSystemOperationRunnerTest, NotFoundErrorAndCancel) {
bool done = false;
bool cancel_done = false;
- base::PlatformFileError status = base::PLATFORM_FILE_ERROR_FAILED;
- base::PlatformFileError cancel_status = base::PLATFORM_FILE_ERROR_FAILED;
+ base::File::Error status = base::File::FILE_ERROR_FAILED;
+ base::File::Error cancel_status = base::File::FILE_ERROR_FAILED;
// Call Truncate with non-existent URL, and try to cancel it immediately
// after that (before its callback is fired).
@@ -117,15 +121,15 @@ TEST_F(FileSystemOperationRunnerTest, NotFoundErrorAndCancel) {
ASSERT_TRUE(done);
ASSERT_TRUE(cancel_done);
- ASSERT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, status);
- ASSERT_EQ(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, cancel_status);
+ ASSERT_EQ(base::File::FILE_ERROR_NOT_FOUND, status);
+ ASSERT_EQ(base::File::FILE_ERROR_INVALID_OPERATION, cancel_status);
}
TEST_F(FileSystemOperationRunnerTest, InvalidURLErrorAndCancel) {
bool done = false;
bool cancel_done = false;
- base::PlatformFileError status = base::PLATFORM_FILE_ERROR_FAILED;
- base::PlatformFileError cancel_status = base::PLATFORM_FILE_ERROR_FAILED;
+ base::File::Error status = base::File::FILE_ERROR_FAILED;
+ base::File::Error cancel_status = base::File::FILE_ERROR_FAILED;
// Call Truncate with invalid URL, and try to cancel it immediately
// after that (before its callback is fired).
@@ -142,21 +146,21 @@ TEST_F(FileSystemOperationRunnerTest, InvalidURLErrorAndCancel) {
ASSERT_TRUE(done);
ASSERT_TRUE(cancel_done);
- ASSERT_EQ(base::PLATFORM_FILE_ERROR_INVALID_URL, status);
- ASSERT_EQ(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, cancel_status);
+ ASSERT_EQ(base::File::FILE_ERROR_INVALID_URL, status);
+ ASSERT_EQ(base::File::FILE_ERROR_INVALID_OPERATION, cancel_status);
}
TEST_F(FileSystemOperationRunnerTest, CancelWithInvalidId) {
const FileSystemOperationRunner::OperationID kInvalidId = -1;
bool done = true; // The operation is not running.
bool cancel_done = false;
- base::PlatformFileError cancel_status = base::PLATFORM_FILE_ERROR_FAILED;
+ base::File::Error cancel_status = base::File::FILE_ERROR_FAILED;
operation_runner()->Cancel(kInvalidId, base::Bind(&GetCancelStatus,
&done, &cancel_done,
&cancel_status));
ASSERT_TRUE(cancel_done);
- ASSERT_EQ(base::PLATFORM_FILE_ERROR_INVALID_OPERATION, cancel_status);
+ ASSERT_EQ(base::File::FILE_ERROR_INVALID_OPERATION, cancel_status);
}
-} // namespace fileapi
+} // namespace content
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 c4baa4f8aa6..65eaf57d164 100644
--- a/chromium/content/browser/fileapi/file_system_quota_client_unittest.cc
+++ b/chromium/content/browser/fileapi/file_system_quota_client_unittest.cc
@@ -7,12 +7,11 @@
#include "base/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/message_loop/message_loop_proxy.h"
-#include "base/platform_file.h"
#include "base/run_loop.h"
+#include "content/public/test/async_file_test_helper.h"
#include "content/public/test/test_file_system_context.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
-#include "webkit/browser/fileapi/async_file_test_helper.h"
#include "webkit/browser/fileapi/file_system_context.h"
#include "webkit/browser/fileapi/file_system_quota_client.h"
#include "webkit/browser/fileapi/file_system_usage_cache.h"
@@ -21,7 +20,11 @@
#include "webkit/common/fileapi/file_system_util.h"
#include "webkit/common/quota/quota_types.h"
-namespace fileapi {
+using content::AsyncFileTestHelper;
+using fileapi::FileSystemQuotaClient;
+using fileapi::FileSystemURL;
+
+namespace content {
namespace {
const char kDummyURL1[] = "http://www.dummy.org";
@@ -78,8 +81,9 @@ class FileSystemQuotaClientTest : public testing::Test {
return usage_;
}
- const std::set<GURL>& GetOriginsForType(FileSystemQuotaClient* quota_client,
- quota::StorageType type) {
+ const std::set<GURL>& GetOriginsForType(
+ FileSystemQuotaClient* quota_client,
+ quota::StorageType type) {
origins_.clear();
quota_client->GetOriginsForType(
type,
@@ -89,9 +93,10 @@ class FileSystemQuotaClientTest : public testing::Test {
return origins_;
}
- const std::set<GURL>& GetOriginsForHost(FileSystemQuotaClient* quota_client,
- quota::StorageType type,
- const std::string& host) {
+ const std::set<GURL>& GetOriginsForHost(
+ FileSystemQuotaClient* quota_client,
+ quota::StorageType type,
+ const std::string& host) {
origins_.clear();
quota_client->GetOriginsForHost(
type, host,
@@ -101,9 +106,10 @@ class FileSystemQuotaClientTest : public testing::Test {
return origins_;
}
- void RunAdditionalOriginUsageTask(FileSystemQuotaClient* quota_client,
- const std::string& origin_url,
- quota::StorageType type) {
+ void RunAdditionalOriginUsageTask(
+ FileSystemQuotaClient* quota_client,
+ const std::string& origin_url,
+ quota::StorageType type) {
quota_client->GetOriginUsage(
GURL(origin_url), type,
base::Bind(&FileSystemQuotaClientTest::OnGetAdditionalUsage,
@@ -113,13 +119,14 @@ class FileSystemQuotaClientTest : public testing::Test {
bool CreateFileSystemDirectory(const base::FilePath& file_path,
const std::string& origin_url,
quota::StorageType storage_type) {
- FileSystemType type = QuotaStorageTypeToFileSystemType(storage_type);
+ fileapi::FileSystemType type =
+ fileapi::QuotaStorageTypeToFileSystemType(storage_type);
FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
GURL(origin_url), type, file_path);
- base::PlatformFileError result =
+ base::File::Error result =
AsyncFileTestHelper::CreateDirectory(file_system_context_, url);
- return result == base::PLATFORM_FILE_OK;
+ return result == base::File::FILE_OK;
}
bool CreateFileSystemFile(const base::FilePath& file_path,
@@ -129,18 +136,19 @@ class FileSystemQuotaClientTest : public testing::Test {
if (file_path.empty())
return false;
- FileSystemType type = QuotaStorageTypeToFileSystemType(storage_type);
+ fileapi::FileSystemType type =
+ fileapi::QuotaStorageTypeToFileSystemType(storage_type);
FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
GURL(origin_url), type, file_path);
- base::PlatformFileError result =
+ base::File::Error result =
AsyncFileTestHelper::CreateFile(file_system_context_, url);
- if (result != base::PLATFORM_FILE_OK)
+ if (result != base::File::FILE_OK)
return false;
result = AsyncFileTestHelper::TruncateFile(
file_system_context_, url, file_size);
- return result == base::PLATFORM_FILE_OK;
+ return result == base::File::FILE_OK;
}
void InitializeOriginFiles(FileSystemQuotaClient* quota_client,
@@ -181,7 +189,8 @@ class FileSystemQuotaClientTest : public testing::Test {
GURL(files[i].origin_url) == GURL(origin_url)) {
base::FilePath path = base::FilePath().AppendASCII(files[i].name);
if (!path.empty()) {
- file_paths_cost += ObfuscatedFileUtil::ComputeFilePathCost(path);
+ file_paths_cost += fileapi::ObfuscatedFileUtil::ComputeFilePathCost(
+ path);
}
}
}
@@ -224,7 +233,7 @@ class FileSystemQuotaClientTest : public testing::Test {
base::ScopedTempDir data_dir_;
base::MessageLoop message_loop_;
- scoped_refptr<FileSystemContext> file_system_context_;
+ scoped_refptr<fileapi::FileSystemContext> file_system_context_;
base::WeakPtrFactory<FileSystemQuotaClientTest> weak_factory_;
int64 usage_;
int additional_callback_count_;
@@ -558,4 +567,4 @@ TEST_F(FileSystemQuotaClientTest, DeleteOriginTest) {
kTemporary));
}
-} // namespace fileapi
+} // namespace content
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 7edd26aa656..4f3d0abde39 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
@@ -11,15 +11,17 @@
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/format_macros.h"
+#include "base/memory/scoped_vector.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_proxy.h"
-#include "base/platform_file.h"
#include "base/rand_util.h"
#include "base/run_loop.h"
#include "base/strings/string_piece.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
+#include "content/public/test/async_file_test_helper.h"
+#include "content/public/test/test_file_system_backend.h"
#include "content/public/test/test_file_system_context.h"
#include "net/base/load_flags.h"
#include "net/base/mime_util.h"
@@ -32,12 +34,16 @@
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "webkit/browser/fileapi/async_file_test_helper.h"
#include "webkit/browser/fileapi/external_mount_points.h"
#include "webkit/browser/fileapi/file_system_context.h"
#include "webkit/browser/fileapi/file_system_file_util.h"
-namespace fileapi {
+using content::AsyncFileTestHelper;
+using fileapi::FileSystemContext;
+using fileapi::FileSystemURL;
+using fileapi::FileSystemURLRequestJob;
+
+namespace content {
namespace {
// We always use the TEMPORARY FileSystem in this test.
@@ -48,6 +54,64 @@ void FillBuffer(char* buffer, size_t len) {
base::RandBytes(buffer, len);
}
+const char kValidExternalMountPoint[] = "mnt_name";
+
+// An auto mounter that will try to mount anything for |storage_domain| =
+// "automount", but will only succeed for the mount point "mnt_name".
+bool TestAutoMountForURLRequest(
+ const net::URLRequest* /*url_request*/,
+ const fileapi::FileSystemURL& filesystem_url,
+ const std::string& storage_domain,
+ const base::Callback<void(base::File::Error result)>& callback) {
+ if (storage_domain != "automount")
+ return false;
+ std::vector<base::FilePath::StringType> components;
+ filesystem_url.path().GetComponents(&components);
+ std::string mount_point = base::FilePath(components[0]).AsUTF8Unsafe();
+
+ if (mount_point == kValidExternalMountPoint) {
+ fileapi::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
+ kValidExternalMountPoint, fileapi::kFileSystemTypeTest,
+ fileapi::FileSystemMountOption(), base::FilePath());
+ callback.Run(base::File::FILE_OK);
+ } else {
+ callback.Run(base::File::FILE_ERROR_NOT_FOUND);
+ }
+ return true;
+}
+
+class FileSystemURLRequestJobFactory : public net::URLRequestJobFactory {
+ public:
+ FileSystemURLRequestJobFactory(const std::string& storage_domain,
+ FileSystemContext* context)
+ : storage_domain_(storage_domain), file_system_context_(context) {
+ }
+
+ virtual net::URLRequestJob* MaybeCreateJobWithProtocolHandler(
+ const std::string& scheme,
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) const OVERRIDE {
+ return new fileapi::FileSystemURLRequestJob(
+ request, network_delegate, storage_domain_, file_system_context_);
+ }
+
+ virtual bool IsHandledProtocol(const std::string& scheme) const OVERRIDE {
+ return true;
+ }
+
+ virtual bool IsHandledURL(const GURL& url) const OVERRIDE {
+ return true;
+ }
+
+ virtual bool IsSafeRedirectTarget(const GURL& location) const OVERRIDE {
+ return false;
+ }
+
+ private:
+ std::string storage_domain_;
+ FileSystemContext* file_system_context_;
+};
+
} // namespace
class FileSystemURLRequestJobTest : public testing::Test {
@@ -64,31 +128,41 @@ class FileSystemURLRequestJobTest : public testing::Test {
CreateFileSystemContextForTesting(NULL, temp_dir_.path());
file_system_context_->OpenFileSystem(
- GURL("http://remote/"), kFileSystemTypeTemporary,
- OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+ GURL("http://remote/"), fileapi::kFileSystemTypeTemporary,
+ fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
base::Bind(&FileSystemURLRequestJobTest::OnOpenFileSystem,
weak_factory_.GetWeakPtr()));
base::RunLoop().RunUntilIdle();
-
- net::URLRequest::Deprecated::RegisterProtocolFactory(
- "filesystem", &FileSystemURLRequestJobFactory);
}
virtual void TearDown() OVERRIDE {
- net::URLRequest::Deprecated::RegisterProtocolFactory("filesystem", NULL);
- ClearUnusedJob();
- if (pending_job_.get()) {
- pending_job_->Kill();
- pending_job_ = NULL;
- }
// FileReader posts a task to close the file in destructor.
base::RunLoop().RunUntilIdle();
}
+ void SetUpAutoMountContext() {
+ base::FilePath mnt_point = temp_dir_.path().AppendASCII("auto_mount_dir");
+ ASSERT_TRUE(base::CreateDirectory(mnt_point));
+
+ ScopedVector<fileapi::FileSystemBackend> additional_providers;
+ additional_providers.push_back(new TestFileSystemBackend(
+ base::MessageLoopProxy::current().get(), mnt_point));
+
+ std::vector<fileapi::URLRequestAutoMountHandler> handlers;
+ handlers.push_back(base::Bind(&TestAutoMountForURLRequest));
+
+ file_system_context_ = CreateFileSystemContextWithAutoMountersForTesting(
+ NULL, additional_providers.Pass(), handlers, temp_dir_.path());
+
+ ASSERT_EQ(static_cast<int>(sizeof(kTestFileData)) - 1,
+ base::WriteFile(mnt_point.AppendASCII("foo"), kTestFileData,
+ sizeof(kTestFileData) - 1));
+ }
+
void OnOpenFileSystem(const GURL& root_url,
const std::string& name,
- base::PlatformFileError result) {
- ASSERT_EQ(base::PLATFORM_FILE_OK, result);
+ base::File::Error result) {
+ ASSERT_EQ(base::File::FILE_OK, result);
}
void TestRequestHelper(const GURL& url,
@@ -99,14 +173,15 @@ class FileSystemURLRequestJobTest : public testing::Test {
// Make delegate_ exit the MessageLoop when the request is done.
delegate_->set_quit_on_complete(true);
delegate_->set_quit_on_redirect(true);
+
+ job_factory_.reset(new FileSystemURLRequestJobFactory(
+ url.GetOrigin().host(), file_system_context));
+ empty_context_.set_job_factory(job_factory_.get());
+
request_ = empty_context_.CreateRequest(
- url, net::DEFAULT_PRIORITY, delegate_.get());
+ url, net::DEFAULT_PRIORITY, delegate_.get(), NULL);
if (headers)
request_->SetExtraRequestHeaders(*headers);
- ASSERT_TRUE(!job_);
- job_ = new FileSystemURLRequestJob(
- request_.get(), NULL, file_system_context);
- pending_job_ = job_;
request_->Start();
ASSERT_TRUE(request_->is_pending()); // verify that we're starting async
@@ -135,9 +210,9 @@ class FileSystemURLRequestJobTest : public testing::Test {
void CreateDirectory(const base::StringPiece& dir_name) {
FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
GURL("http://remote"),
- kFileSystemTypeTemporary,
+ fileapi::kFileSystemTypeTemporary,
base::FilePath().AppendASCII(dir_name));
- ASSERT_EQ(base::PLATFORM_FILE_OK, AsyncFileTestHelper::CreateDirectory(
+ ASSERT_EQ(base::File::FILE_OK, AsyncFileTestHelper::CreateDirectory(
file_system_context_, url));
}
@@ -145,9 +220,9 @@ class FileSystemURLRequestJobTest : public testing::Test {
const char* buf, int buf_size) {
FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
GURL("http://remote"),
- kFileSystemTypeTemporary,
+ fileapi::kFileSystemTypeTemporary,
base::FilePath().AppendASCII(file_name));
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::CreateFileWithData(
file_system_context_, url, buf, buf_size));
}
@@ -156,43 +231,21 @@ class FileSystemURLRequestJobTest : public testing::Test {
return GURL(kFileSystemURLPrefix + path);
}
- static net::URLRequestJob* FileSystemURLRequestJobFactory(
- net::URLRequest* request,
- net::NetworkDelegate* network_delegate,
- const std::string& scheme) {
- DCHECK(job_);
- net::URLRequestJob* temp = job_;
- job_ = NULL;
- return temp;
- }
-
- static void ClearUnusedJob() {
- if (job_) {
- scoped_refptr<net::URLRequestJob> deleter = job_;
- job_ = NULL;
- }
- }
-
// Put the message loop at the top, so that it's the last thing deleted.
base::MessageLoopForIO message_loop_;
base::ScopedTempDir temp_dir_;
- scoped_refptr<FileSystemContext> file_system_context_;
+ scoped_refptr<fileapi::FileSystemContext> file_system_context_;
base::WeakPtrFactory<FileSystemURLRequestJobTest> weak_factory_;
net::URLRequestContext empty_context_;
+ scoped_ptr<FileSystemURLRequestJobFactory> job_factory_;
// NOTE: order matters, request must die before delegate
scoped_ptr<net::TestDelegate> delegate_;
scoped_ptr<net::URLRequest> request_;
-
- scoped_refptr<net::URLRequestJob> pending_job_;
- static net::URLRequestJob* job_;
};
-// static
-net::URLRequestJob* FileSystemURLRequestJobTest::job_ = NULL;
-
namespace {
TEST_F(FileSystemURLRequestJobTest, FileTest) {
@@ -255,7 +308,6 @@ TEST_F(FileSystemURLRequestJobTest, FileTestHalfSpecifiedRange) {
EXPECT_TRUE(partial_buffer_string == delegate_->data_received());
}
-
TEST_F(FileSystemURLRequestJobTest, FileTestMultipleRangesNotSupported) {
WriteFile("file1.dat", kTestFileData, arraysize(kTestFileData) - 1);
net::HttpRequestHeaders headers;
@@ -364,5 +416,49 @@ TEST_F(FileSystemURLRequestJobTest, Incognito) {
EXPECT_EQ(200, request_->GetResponseCode());
}
+TEST_F(FileSystemURLRequestJobTest, AutoMountFileTest) {
+ SetUpAutoMountContext();
+ TestRequest(GURL("filesystem:http://automount/external/mnt_name/foo"));
+
+ ASSERT_FALSE(request_->is_pending());
+ EXPECT_EQ(1, delegate_->response_started_count());
+ EXPECT_FALSE(delegate_->received_data_before_response());
+ EXPECT_EQ(kTestFileData, delegate_->data_received());
+ EXPECT_EQ(200, request_->GetResponseCode());
+ std::string cache_control;
+ request_->GetResponseHeaderByName("cache-control", &cache_control);
+ EXPECT_EQ("no-cache", cache_control);
+
+ ASSERT_TRUE(
+ fileapi::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(
+ kValidExternalMountPoint));
+}
+
+TEST_F(FileSystemURLRequestJobTest, AutoMountInvalidRoot) {
+ SetUpAutoMountContext();
+ TestRequest(GURL("filesystem:http://automount/external/invalid/foo"));
+
+ ASSERT_FALSE(request_->is_pending());
+ EXPECT_TRUE(delegate_->request_failed());
+ EXPECT_EQ(net::ERR_FILE_NOT_FOUND, request_->status().error());
+
+ ASSERT_FALSE(
+ fileapi::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(
+ "invalid"));
+}
+
+TEST_F(FileSystemURLRequestJobTest, AutoMountNoHandler) {
+ SetUpAutoMountContext();
+ TestRequest(GURL("filesystem:http://noauto/external/mnt_name/foo"));
+
+ ASSERT_FALSE(request_->is_pending());
+ EXPECT_TRUE(delegate_->request_failed());
+ EXPECT_EQ(net::ERR_FILE_NOT_FOUND, request_->status().error());
+
+ ASSERT_FALSE(
+ fileapi::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(
+ kValidExternalMountPoint));
+}
+
} // namespace
-} // namespace fileapi
+} // namespace content
diff --git a/chromium/content/browser/fileapi/file_system_url_unittest.cc b/chromium/content/browser/fileapi/file_system_url_unittest.cc
new file mode 100644
index 00000000000..4aa23dd8ab4
--- /dev/null
+++ b/chromium/content/browser/fileapi/file_system_url_unittest.cc
@@ -0,0 +1,221 @@
+// 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 "webkit/browser/fileapi/file_system_url.h"
+
+#include "base/files/file_path.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+#include "webkit/common/fileapi/file_system_types.h"
+#include "webkit/common/fileapi/file_system_util.h"
+
+#define FPL FILE_PATH_LITERAL
+
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+#define DRIVE FPL("C:")
+#else
+#define DRIVE FPL("/a/")
+#endif
+
+using fileapi::FileSystemURL;
+using fileapi::kFileSystemTypeExternal;
+using fileapi::kFileSystemTypeIsolated;
+using fileapi::kFileSystemTypePersistent;
+using fileapi::kFileSystemTypeTemporary;
+using fileapi::VirtualPath;
+
+namespace content {
+
+namespace {
+
+FileSystemURL CreateFileSystemURL(const std::string& url_string) {
+ FileSystemURL url = FileSystemURL::CreateForTest(GURL(url_string));
+ EXPECT_TRUE(url.type() != kFileSystemTypeExternal &&
+ url.type() != kFileSystemTypeIsolated);
+ return url;
+}
+
+std::string NormalizedUTF8Path(const base::FilePath& path) {
+ return path.NormalizePathSeparators().AsUTF8Unsafe();
+}
+
+} // namespace
+
+TEST(FileSystemURLTest, ParsePersistent) {
+ FileSystemURL url = CreateFileSystemURL(
+ "filesystem:http://chromium.org/persistent/directory/file");
+ ASSERT_TRUE(url.is_valid());
+ EXPECT_EQ("http://chromium.org/", url.origin().spec());
+ EXPECT_EQ(kFileSystemTypePersistent, url.type());
+ EXPECT_EQ(FPL("file"), VirtualPath::BaseName(url.path()).value());
+ EXPECT_EQ(FPL("directory"), url.path().DirName().value());
+}
+
+TEST(FileSystemURLTest, ParseTemporary) {
+ FileSystemURL url = CreateFileSystemURL(
+ "filesystem:http://chromium.org/temporary/directory/file");
+ ASSERT_TRUE(url.is_valid());
+ EXPECT_EQ("http://chromium.org/", url.origin().spec());
+ EXPECT_EQ(kFileSystemTypeTemporary, url.type());
+ EXPECT_EQ(FPL("file"), VirtualPath::BaseName(url.path()).value());
+ EXPECT_EQ(FPL("directory"), url.path().DirName().value());
+}
+
+TEST(FileSystemURLTest, EnsureFilePathIsRelative) {
+ FileSystemURL url = CreateFileSystemURL(
+ "filesystem:http://chromium.org/temporary/////directory/file");
+ ASSERT_TRUE(url.is_valid());
+ EXPECT_EQ("http://chromium.org/", url.origin().spec());
+ EXPECT_EQ(kFileSystemTypeTemporary, url.type());
+ EXPECT_EQ(FPL("file"), VirtualPath::BaseName(url.path()).value());
+ EXPECT_EQ(FPL("directory"), url.path().DirName().value());
+ EXPECT_FALSE(url.path().IsAbsolute());
+}
+
+TEST(FileSystemURLTest, RejectBadSchemes) {
+ EXPECT_FALSE(CreateFileSystemURL("http://chromium.org/").is_valid());
+ EXPECT_FALSE(CreateFileSystemURL("https://chromium.org/").is_valid());
+ EXPECT_FALSE(CreateFileSystemURL("file:///foo/bar").is_valid());
+ EXPECT_FALSE(CreateFileSystemURL("foobar:///foo/bar").is_valid());
+}
+
+TEST(FileSystemURLTest, UnescapePath) {
+ FileSystemURL url = CreateFileSystemURL(
+ "filesystem:http://chromium.org/persistent/%7Echromium/space%20bar");
+ ASSERT_TRUE(url.is_valid());
+ EXPECT_EQ(FPL("space bar"), VirtualPath::BaseName(url.path()).value());
+ EXPECT_EQ(FPL("~chromium"), url.path().DirName().value());
+}
+
+TEST(FileSystemURLTest, RejectBadType) {
+ EXPECT_FALSE(CreateFileSystemURL(
+ "filesystem:http://c.org/foobar/file").is_valid());
+ EXPECT_FALSE(CreateFileSystemURL(
+ "filesystem:http://c.org/temporaryfoo/file").is_valid());
+}
+
+TEST(FileSystemURLTest, RejectMalformedURL) {
+ EXPECT_FALSE(CreateFileSystemURL("filesystem:///foobar/file").is_valid());
+ EXPECT_FALSE(CreateFileSystemURL("filesystem:foobar/file").is_valid());
+}
+
+TEST(FileSystemURLTest, CompareURLs) {
+ const GURL urls[] = {
+ GURL("filesystem:http://chromium.org/temporary/dir a/file a"),
+ GURL("filesystem:http://chromium.org/temporary/dir a/file a"),
+ GURL("filesystem:http://chromium.org/temporary/dir a/file b"),
+ GURL("filesystem:http://chromium.org/temporary/dir a/file aa"),
+ GURL("filesystem:http://chromium.org/temporary/dir b/file a"),
+ GURL("filesystem:http://chromium.org/temporary/dir aa/file b"),
+ GURL("filesystem:http://chromium.com/temporary/dir a/file a"),
+ GURL("filesystem:https://chromium.org/temporary/dir a/file a")
+ };
+
+ FileSystemURL::Comparator compare;
+ for (size_t i = 0; i < arraysize(urls); ++i) {
+ for (size_t j = 0; j < arraysize(urls); ++j) {
+ SCOPED_TRACE(testing::Message() << i << " < " << j);
+ EXPECT_EQ(urls[i] < urls[j],
+ compare(FileSystemURL::CreateForTest(urls[i]),
+ FileSystemURL::CreateForTest(urls[j])));
+ }
+ }
+
+ const FileSystemURL a = CreateFileSystemURL(
+ "filesystem:http://chromium.org/temporary/dir a/file a");
+ const FileSystemURL b = CreateFileSystemURL(
+ "filesystem:http://chromium.org/persistent/dir a/file a");
+ EXPECT_EQ(a.type() < b.type(), compare(a, b));
+ EXPECT_EQ(b.type() < a.type(), compare(b, a));
+}
+
+TEST(FileSystemURLTest, IsParent) {
+ const std::string root1 = GetFileSystemRootURI(
+ GURL("http://example.com"), kFileSystemTypeTemporary).spec();
+ const std::string root2 = GetFileSystemRootURI(
+ GURL("http://example.com"), kFileSystemTypePersistent).spec();
+ const std::string root3 = GetFileSystemRootURI(
+ GURL("http://chromium.org"), kFileSystemTypeTemporary).spec();
+
+ const std::string parent("dir");
+ const std::string child("dir/child");
+ const std::string other("other");
+
+ // True cases.
+ EXPECT_TRUE(CreateFileSystemURL(root1 + parent).IsParent(
+ CreateFileSystemURL(root1 + child)));
+ EXPECT_TRUE(CreateFileSystemURL(root2 + parent).IsParent(
+ CreateFileSystemURL(root2 + child)));
+
+ // False cases: the path is not a child.
+ EXPECT_FALSE(CreateFileSystemURL(root1 + parent).IsParent(
+ CreateFileSystemURL(root1 + other)));
+ EXPECT_FALSE(CreateFileSystemURL(root1 + parent).IsParent(
+ CreateFileSystemURL(root1 + parent)));
+ EXPECT_FALSE(CreateFileSystemURL(root1 + child).IsParent(
+ CreateFileSystemURL(root1 + parent)));
+
+ // False case: different types.
+ EXPECT_FALSE(CreateFileSystemURL(root1 + parent).IsParent(
+ CreateFileSystemURL(root2 + child)));
+
+ // False case: different origins.
+ EXPECT_FALSE(CreateFileSystemURL(root1 + parent).IsParent(
+ CreateFileSystemURL(root3 + child)));
+}
+
+TEST(FileSystemURLTest, ToGURL) {
+ EXPECT_TRUE(FileSystemURL().ToGURL().is_empty());
+ const char* kTestURL[] = {
+ "filesystem:http://chromium.org/persistent/directory/file0",
+ "filesystem:http://chromium.org/temporary/directory/file1",
+ "filesystem:http://chromium.org/isolated/directory/file2",
+ "filesystem:http://chromium.org/external/directory/file2",
+ "filesystem:http://chromium.org/test/directory/file3",
+ "filesystem:http://chromium.org/test/plus%2B/space%20/colon%3A",
+ };
+
+ for (size_t i = 0; i < arraysize(kTestURL); ++i) {
+ EXPECT_EQ(
+ kTestURL[i],
+ FileSystemURL::CreateForTest(GURL(kTestURL[i])).ToGURL().spec());
+ }
+}
+
+TEST(FileSystemURLTest, DebugString) {
+ const GURL kOrigin("http://example.com");
+ const base::FilePath kPath(FPL("dir/file"));
+
+ const FileSystemURL kURL1 = FileSystemURL::CreateForTest(
+ kOrigin, kFileSystemTypeTemporary, kPath);
+ EXPECT_EQ("filesystem:http://example.com/temporary/" +
+ NormalizedUTF8Path(kPath),
+ kURL1.DebugString());
+}
+
+TEST(FileSystemURLTest, IsInSameFileSystem) {
+ FileSystemURL url_foo_temp_a = FileSystemURL::CreateForTest(
+ GURL("http://foo"), kFileSystemTypeTemporary,
+ base::FilePath::FromUTF8Unsafe("a"));
+ FileSystemURL url_foo_temp_b = FileSystemURL::CreateForTest(
+ GURL("http://foo"), kFileSystemTypeTemporary,
+ base::FilePath::FromUTF8Unsafe("b"));
+ FileSystemURL url_foo_perm_a = FileSystemURL::CreateForTest(
+ GURL("http://foo"), kFileSystemTypePersistent,
+ base::FilePath::FromUTF8Unsafe("a"));
+ FileSystemURL url_bar_temp_a = FileSystemURL::CreateForTest(
+ GURL("http://bar"), kFileSystemTypeTemporary,
+ base::FilePath::FromUTF8Unsafe("a"));
+ FileSystemURL url_bar_perm_a = FileSystemURL::CreateForTest(
+ GURL("http://bar"), kFileSystemTypePersistent,
+ base::FilePath::FromUTF8Unsafe("a"));
+
+ EXPECT_TRUE(url_foo_temp_a.IsInSameFileSystem(url_foo_temp_a));
+ EXPECT_TRUE(url_foo_temp_a.IsInSameFileSystem(url_foo_temp_b));
+ EXPECT_FALSE(url_foo_temp_a.IsInSameFileSystem(url_foo_perm_a));
+ EXPECT_FALSE(url_foo_temp_a.IsInSameFileSystem(url_bar_temp_a));
+ EXPECT_FALSE(url_foo_temp_a.IsInSameFileSystem(url_bar_perm_a));
+}
+
+} // namespace content
diff --git a/chromium/content/browser/fileapi/file_system_usage_cache_unittest.cc b/chromium/content/browser/fileapi/file_system_usage_cache_unittest.cc
new file mode 100644
index 00000000000..0a54b891075
--- /dev/null
+++ b/chromium/content/browser/fileapi/file_system_usage_cache_unittest.cc
@@ -0,0 +1,160 @@
+// 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 "webkit/browser/fileapi/file_system_usage_cache.h"
+
+#include "base/basictypes.h"
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/message_loop/message_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using fileapi::FileSystemUsageCache;
+
+namespace content {
+
+class FileSystemUsageCacheTest : public testing::Test {
+ public:
+ FileSystemUsageCacheTest()
+ : usage_cache_(base::MessageLoopProxy::current().get()) {}
+
+ virtual void SetUp() {
+ ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
+ }
+
+ protected:
+ base::FilePath GetUsageFilePath() {
+ return data_dir_.path().Append(FileSystemUsageCache::kUsageFileName);
+ }
+
+ FileSystemUsageCache* usage_cache() {
+ return &usage_cache_;
+ }
+
+ private:
+ base::MessageLoop message_loop_;
+ base::ScopedTempDir data_dir_;
+ FileSystemUsageCache usage_cache_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileSystemUsageCacheTest);
+};
+
+TEST_F(FileSystemUsageCacheTest, CreateTest) {
+ base::FilePath usage_file_path = GetUsageFilePath();
+ EXPECT_TRUE(usage_cache()->UpdateUsage(usage_file_path, 0));
+}
+
+TEST_F(FileSystemUsageCacheTest, SetSizeTest) {
+ static const int64 size = 240122;
+ base::FilePath usage_file_path = GetUsageFilePath();
+ int64 usage = 0;
+ ASSERT_TRUE(usage_cache()->UpdateUsage(usage_file_path, size));
+ EXPECT_TRUE(usage_cache()->GetUsage(usage_file_path, &usage));
+ EXPECT_EQ(size, usage);
+}
+
+TEST_F(FileSystemUsageCacheTest, SetLargeSizeTest) {
+ static const int64 size = kint64max;
+ base::FilePath usage_file_path = GetUsageFilePath();
+ int64 usage = 0;
+ ASSERT_TRUE(usage_cache()->UpdateUsage(usage_file_path, size));
+ EXPECT_TRUE(usage_cache()->GetUsage(usage_file_path, &usage));
+ EXPECT_EQ(size, usage);
+}
+
+TEST_F(FileSystemUsageCacheTest, IncAndGetSizeTest) {
+ base::FilePath usage_file_path = GetUsageFilePath();
+ uint32 dirty = 0;
+ int64 usage = 0;
+ ASSERT_TRUE(usage_cache()->UpdateUsage(usage_file_path, 98214));
+ ASSERT_TRUE(usage_cache()->IncrementDirty(usage_file_path));
+ EXPECT_TRUE(usage_cache()->GetDirty(usage_file_path, &dirty));
+ EXPECT_EQ(1u, dirty);
+ EXPECT_TRUE(usage_cache()->GetUsage(usage_file_path, &usage));
+ EXPECT_EQ(98214, usage);
+}
+
+TEST_F(FileSystemUsageCacheTest, DecAndGetSizeTest) {
+ static const int64 size = 71839;
+ base::FilePath usage_file_path = GetUsageFilePath();
+ int64 usage = 0;
+ ASSERT_TRUE(usage_cache()->UpdateUsage(usage_file_path, size));
+ // DecrementDirty for dirty = 0 is invalid. It returns false.
+ ASSERT_FALSE(usage_cache()->DecrementDirty(usage_file_path));
+ EXPECT_TRUE(usage_cache()->GetUsage(usage_file_path, &usage));
+ EXPECT_EQ(size, usage);
+}
+
+TEST_F(FileSystemUsageCacheTest, IncDecAndGetSizeTest) {
+ static const int64 size = 198491;
+ base::FilePath usage_file_path = GetUsageFilePath();
+ int64 usage = 0;
+ ASSERT_TRUE(usage_cache()->UpdateUsage(usage_file_path, size));
+ ASSERT_TRUE(usage_cache()->IncrementDirty(usage_file_path));
+ ASSERT_TRUE(usage_cache()->DecrementDirty(usage_file_path));
+ EXPECT_TRUE(usage_cache()->GetUsage(usage_file_path, &usage));
+ EXPECT_EQ(size, usage);
+}
+
+TEST_F(FileSystemUsageCacheTest, DecIncAndGetSizeTest) {
+ base::FilePath usage_file_path = GetUsageFilePath();
+ uint32 dirty = 0;
+ int64 usage = 0;
+ ASSERT_TRUE(usage_cache()->UpdateUsage(usage_file_path, 854238));
+ // DecrementDirty for dirty = 0 is invalid. It returns false.
+ ASSERT_FALSE(usage_cache()->DecrementDirty(usage_file_path));
+ ASSERT_TRUE(usage_cache()->IncrementDirty(usage_file_path));
+ // It tests DecrementDirty (which returns false) has no effect, i.e
+ // does not make dirty = -1 after DecrementDirty.
+ EXPECT_TRUE(usage_cache()->GetDirty(usage_file_path, &dirty));
+ EXPECT_EQ(1u, dirty);
+ EXPECT_TRUE(usage_cache()->GetUsage(usage_file_path, &usage));
+ EXPECT_EQ(854238, usage);
+}
+
+TEST_F(FileSystemUsageCacheTest, ManyIncsSameDecsAndGetSizeTest) {
+ static const int64 size = 82412;
+ base::FilePath usage_file_path = GetUsageFilePath();
+ int64 usage = 0;
+ ASSERT_TRUE(usage_cache()->UpdateUsage(usage_file_path, size));
+ for (int i = 0; i < 20; i++)
+ ASSERT_TRUE(usage_cache()->IncrementDirty(usage_file_path));
+ for (int i = 0; i < 20; i++)
+ ASSERT_TRUE(usage_cache()->DecrementDirty(usage_file_path));
+ EXPECT_TRUE(usage_cache()->GetUsage(usage_file_path, &usage));
+ EXPECT_EQ(size, usage);
+}
+
+TEST_F(FileSystemUsageCacheTest, ManyIncsLessDecsAndGetSizeTest) {
+ uint32 dirty = 0;
+ int64 usage = 0;
+ base::FilePath usage_file_path = GetUsageFilePath();
+ ASSERT_TRUE(usage_cache()->UpdateUsage(usage_file_path, 19319));
+ for (int i = 0; i < 20; i++)
+ ASSERT_TRUE(usage_cache()->IncrementDirty(usage_file_path));
+ for (int i = 0; i < 19; i++)
+ ASSERT_TRUE(usage_cache()->DecrementDirty(usage_file_path));
+ EXPECT_TRUE(usage_cache()->GetDirty(usage_file_path, &dirty));
+ EXPECT_EQ(1u, dirty);
+ EXPECT_TRUE(usage_cache()->GetUsage(usage_file_path, &usage));
+ EXPECT_EQ(19319, usage);
+}
+
+TEST_F(FileSystemUsageCacheTest, GetSizeWithoutCacheFileTest) {
+ int64 usage = 0;
+ base::FilePath usage_file_path = GetUsageFilePath();
+ EXPECT_FALSE(usage_cache()->GetUsage(usage_file_path, &usage));
+}
+
+TEST_F(FileSystemUsageCacheTest, IncrementDirtyWithoutCacheFileTest) {
+ base::FilePath usage_file_path = GetUsageFilePath();
+ EXPECT_FALSE(usage_cache()->IncrementDirty(usage_file_path));
+}
+
+TEST_F(FileSystemUsageCacheTest, DecrementDirtyWithoutCacheFileTest) {
+ base::FilePath usage_file_path = GetUsageFilePath();
+ EXPECT_FALSE(usage_cache()->IncrementDirty(usage_file_path));
+}
+
+} // namespace content
diff --git a/chromium/content/browser/fileapi/file_writer_delegate_unittest.cc b/chromium/content/browser/fileapi/file_writer_delegate_unittest.cc
index c18877f7727..ccbc4a563df 100644
--- a/chromium/content/browser/fileapi/file_writer_delegate_unittest.cc
+++ b/chromium/content/browser/fileapi/file_writer_delegate_unittest.cc
@@ -11,27 +11,32 @@
#include "base/files/scoped_temp_dir.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
+#include "content/public/test/async_file_test_helper.h"
#include "content/public/test/test_file_system_context.h"
#include "net/base/io_buffer.h"
#include "net/base/request_priority.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_job.h"
+#include "net/url_request/url_request_job_factory.h"
#include "net/url_request/url_request_status.h"
#include "testing/platform_test.h"
#include "url/gurl.h"
-#include "webkit/browser/fileapi/async_file_test_helper.h"
#include "webkit/browser/fileapi/file_system_context.h"
#include "webkit/browser/fileapi/file_system_quota_util.h"
#include "webkit/browser/fileapi/file_writer_delegate.h"
#include "webkit/browser/fileapi/sandbox_file_stream_writer.h"
-namespace fileapi {
+using content::AsyncFileTestHelper;
+using fileapi::FileSystemURL;
+using fileapi::FileWriterDelegate;
+
+namespace content {
namespace {
const GURL kOrigin("http://example.com");
-const FileSystemType kFileSystemType = kFileSystemTypeTest;
+const fileapi::FileSystemType kFileSystemType = fileapi::kFileSystemTypeTest;
const char kData[] = "The quick brown fox jumps over the lazy dog.\n";
const int kDataSize = ARRAYSIZE_UNSAFE(kData) - 1;
@@ -39,25 +44,25 @@ const int kDataSize = ARRAYSIZE_UNSAFE(kData) - 1;
class Result {
public:
Result()
- : status_(base::PLATFORM_FILE_OK),
+ : status_(base::File::FILE_OK),
bytes_written_(0),
write_status_(FileWriterDelegate::SUCCESS_IO_PENDING) {}
- base::PlatformFileError status() const { return status_; }
+ base::File::Error status() const { return status_; }
int64 bytes_written() const { return bytes_written_; }
FileWriterDelegate::WriteProgressStatus write_status() const {
return write_status_;
}
- void DidWrite(base::PlatformFileError status, int64 bytes,
+ void DidWrite(base::File::Error status, int64 bytes,
FileWriterDelegate::WriteProgressStatus write_status) {
write_status_ = write_status;
- if (status == base::PLATFORM_FILE_OK) {
+ if (status == base::File::FILE_OK) {
bytes_written_ += bytes;
if (write_status_ != FileWriterDelegate::SUCCESS_IO_PENDING)
base::MessageLoop::current()->Quit();
} else {
- EXPECT_EQ(base::PLATFORM_FILE_OK, status_);
+ EXPECT_EQ(base::File::FILE_OK, status_);
status_ = status;
base::MessageLoop::current()->Quit();
}
@@ -65,11 +70,13 @@ class Result {
private:
// For post-operation status.
- base::PlatformFileError status_;
+ base::File::Error status_;
int64 bytes_written_;
FileWriterDelegate::WriteProgressStatus write_status_;
};
+class BlobURLRequestJobFactory;
+
} // namespace (anonymous)
class FileWriterDelegateTest : public PlatformTest {
@@ -82,7 +89,7 @@ class FileWriterDelegateTest : public PlatformTest {
int64 usage() {
return file_system_context_->GetQuotaUtil(kFileSystemType)
- ->GetOriginUsageOnFileThread(
+ ->GetOriginUsageOnFileTaskRunner(
file_system_context_.get(), kOrigin, kFileSystemType);
}
@@ -93,8 +100,8 @@ class FileWriterDelegateTest : public PlatformTest {
base::RunLoop().RunUntilIdle();
FileSystemURL url = GetFileSystemURL(test_file_path);
- base::PlatformFileInfo file_info;
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ base::File::Info file_info;
+ EXPECT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::GetMetadata(
file_system_context_, url, &file_info));
return file_info.size;
@@ -109,13 +116,16 @@ class FileWriterDelegateTest : public PlatformTest {
const char* test_file_path,
int64 offset,
int64 allowed_growth) {
- SandboxFileStreamWriter* writer = new SandboxFileStreamWriter(
- file_system_context_.get(),
- GetFileSystemURL(test_file_path),
- offset,
- *file_system_context_->GetUpdateObservers(kFileSystemType));
+ fileapi::SandboxFileStreamWriter* writer =
+ new fileapi::SandboxFileStreamWriter(
+ file_system_context_.get(),
+ GetFileSystemURL(test_file_path),
+ offset,
+ *file_system_context_->GetUpdateObservers(kFileSystemType));
writer->set_default_quota(allowed_growth);
- return new FileWriterDelegate(scoped_ptr<FileStreamWriter>(writer));
+ return new FileWriterDelegate(
+ scoped_ptr<fileapi::FileStreamWriter>(writer),
+ FileWriterDelegate::FLUSH_ON_COMPLETION);
}
FileWriterDelegate::DelegateWriteCallback GetWriteCallback(Result* result) {
@@ -131,19 +141,18 @@ class FileWriterDelegateTest : public PlatformTest {
file_writer_delegate_.reset(
CreateWriterDelegate(test_file_path, offset, allowed_growth));
request_ = empty_context_.CreateRequest(
- blob_url, net::DEFAULT_PRIORITY, file_writer_delegate_.get());
+ blob_url, net::DEFAULT_PRIORITY, file_writer_delegate_.get(), NULL);
}
- static net::URLRequest::ProtocolFactory Factory;
-
// This should be alive until the very end of this instance.
base::MessageLoopForIO loop_;
- scoped_refptr<FileSystemContext> file_system_context_;
+ scoped_refptr<fileapi::FileSystemContext> file_system_context_;
net::URLRequestContext empty_context_;
scoped_ptr<FileWriterDelegate> file_writer_delegate_;
scoped_ptr<net::URLRequest> request_;
+ scoped_ptr<BlobURLRequestJobFactory> job_factory_;
base::ScopedTempDir dir_;
@@ -201,30 +210,53 @@ class FileWriterDelegateTestJob : public net::URLRequestJob {
int cursor_;
};
-} // namespace (anonymous)
+class BlobURLRequestJobFactory : public net::URLRequestJobFactory {
+ public:
+ explicit BlobURLRequestJobFactory(const char** content_data)
+ : content_data_(content_data) {
+ }
-// static
-net::URLRequestJob* FileWriterDelegateTest::Factory(
- net::URLRequest* request,
- net::NetworkDelegate* network_delegate,
- const std::string& scheme) {
- return new FileWriterDelegateTestJob(
- request, network_delegate, FileWriterDelegateTest::content_);
-}
+ virtual net::URLRequestJob* MaybeCreateJobWithProtocolHandler(
+ const std::string& scheme,
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) const OVERRIDE {
+ return new FileWriterDelegateTestJob(
+ request, network_delegate, *content_data_);
+ }
+
+ virtual bool IsHandledProtocol(const std::string& scheme) const OVERRIDE {
+ return scheme == "blob";
+ }
+
+ virtual bool IsHandledURL(const GURL& url) const OVERRIDE {
+ return url.SchemeIs("blob");
+ }
+
+ virtual bool IsSafeRedirectTarget(const GURL& location) const OVERRIDE {
+ return true;
+ }
+
+ private:
+ const char** content_data_;
+
+ DISALLOW_COPY_AND_ASSIGN(BlobURLRequestJobFactory);
+};
+
+} // namespace (anonymous)
void FileWriterDelegateTest::SetUp() {
ASSERT_TRUE(dir_.CreateUniqueTempDir());
file_system_context_ = CreateFileSystemContextForTesting(
NULL, dir_.path());
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::CreateFile(
file_system_context_, GetFileSystemURL("test")));
- net::URLRequest::Deprecated::RegisterProtocolFactory("blob", &Factory);
+ job_factory_.reset(new BlobURLRequestJobFactory(&content_));
+ empty_context_.set_job_factory(job_factory_.get());
}
void FileWriterDelegateTest::TearDown() {
- net::URLRequest::Deprecated::RegisterProtocolFactory("blob", NULL);
file_system_context_ = NULL;
base::RunLoop().RunUntilIdle();
}
@@ -246,7 +278,7 @@ TEST_F(FileWriterDelegateTest, WriteSuccessWithoutQuotaLimit) {
ASSERT_EQ(kDataSize, usage());
EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
EXPECT_EQ(kDataSize, result.bytes_written());
- EXPECT_EQ(base::PLATFORM_FILE_OK, result.status());
+ EXPECT_EQ(base::File::FILE_OK, result.status());
}
TEST_F(FileWriterDelegateTest, WriteSuccessWithJustQuota) {
@@ -266,7 +298,7 @@ TEST_F(FileWriterDelegateTest, WriteSuccessWithJustQuota) {
EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
EXPECT_EQ(kAllowedGrowth, result.bytes_written());
- EXPECT_EQ(base::PLATFORM_FILE_OK, result.status());
+ EXPECT_EQ(base::File::FILE_OK, result.status());
}
TEST_F(FileWriterDelegateTest, DISABLED_WriteFailureByQuota) {
@@ -286,7 +318,7 @@ TEST_F(FileWriterDelegateTest, DISABLED_WriteFailureByQuota) {
EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
EXPECT_EQ(kAllowedGrowth, result.bytes_written());
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, result.status());
+ EXPECT_EQ(base::File::FILE_ERROR_NO_SPACE, result.status());
ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED, result.write_status());
}
@@ -307,7 +339,7 @@ TEST_F(FileWriterDelegateTest, WriteZeroBytesSuccessfullyWithZeroQuota) {
EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
EXPECT_EQ(kAllowedGrowth, result.bytes_written());
- EXPECT_EQ(base::PLATFORM_FILE_OK, result.status());
+ EXPECT_EQ(base::File::FILE_OK, result.status());
ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result.write_status());
}
@@ -315,7 +347,7 @@ TEST_F(FileWriterDelegateTest, WriteSuccessWithoutQuotaLimitConcurrent) {
scoped_ptr<FileWriterDelegate> file_writer_delegate2;
scoped_ptr<net::URLRequest> request2;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::CreateFile(
file_system_context_, GetFileSystemURL("test2")));
@@ -328,7 +360,7 @@ TEST_F(FileWriterDelegateTest, WriteSuccessWithoutQuotaLimitConcurrent) {
// Credate another FileWriterDelegate for concurrent write.
file_writer_delegate2.reset(CreateWriterDelegate("test2", 0, kint64max));
request2 = empty_context_.CreateRequest(
- kBlobURL2, net::DEFAULT_PRIORITY, file_writer_delegate2.get());
+ kBlobURL2, net::DEFAULT_PRIORITY, file_writer_delegate2.get(), NULL);
Result result, result2;
ASSERT_EQ(0, usage());
@@ -348,9 +380,9 @@ TEST_F(FileWriterDelegateTest, WriteSuccessWithoutQuotaLimitConcurrent) {
EXPECT_EQ(GetFileSizeOnDisk("test") + GetFileSizeOnDisk("test2"), usage());
EXPECT_EQ(kDataSize, result.bytes_written());
- EXPECT_EQ(base::PLATFORM_FILE_OK, result.status());
+ EXPECT_EQ(base::File::FILE_OK, result.status());
EXPECT_EQ(kDataSize, result2.bytes_written());
- EXPECT_EQ(base::PLATFORM_FILE_OK, result2.status());
+ EXPECT_EQ(base::File::FILE_OK, result2.status());
}
TEST_F(FileWriterDelegateTest, WritesWithQuotaAndOffset) {
@@ -374,7 +406,7 @@ TEST_F(FileWriterDelegateTest, WritesWithQuotaAndOffset) {
ASSERT_EQ(kDataSize, usage());
EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
EXPECT_EQ(kDataSize, result.bytes_written());
- EXPECT_EQ(base::PLATFORM_FILE_OK, result.status());
+ EXPECT_EQ(base::File::FILE_OK, result.status());
}
// Trying to overwrite kDataSize bytes data while allowed_growth is 20.
@@ -389,7 +421,7 @@ TEST_F(FileWriterDelegateTest, WritesWithQuotaAndOffset) {
EXPECT_EQ(kDataSize, usage());
EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
EXPECT_EQ(kDataSize, result.bytes_written());
- EXPECT_EQ(base::PLATFORM_FILE_OK, result.status());
+ EXPECT_EQ(base::File::FILE_OK, result.status());
ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result.write_status());
}
@@ -409,7 +441,7 @@ TEST_F(FileWriterDelegateTest, WritesWithQuotaAndOffset) {
EXPECT_EQ(offset + kDataSize, usage());
EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
EXPECT_EQ(kDataSize, result.bytes_written());
- EXPECT_EQ(base::PLATFORM_FILE_OK, result.status());
+ EXPECT_EQ(base::File::FILE_OK, result.status());
}
// Trying to overwrite 45 bytes data while allowed_growth is -20.
@@ -428,7 +460,7 @@ TEST_F(FileWriterDelegateTest, WritesWithQuotaAndOffset) {
EXPECT_EQ(pre_write_usage, usage());
EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
EXPECT_EQ(kDataSize, result.bytes_written());
- EXPECT_EQ(base::PLATFORM_FILE_OK, result.status());
+ EXPECT_EQ(base::File::FILE_OK, result.status());
}
// Trying to overwrite 45 bytes data with offset pre_write_usage - 20,
@@ -448,8 +480,8 @@ TEST_F(FileWriterDelegateTest, WritesWithQuotaAndOffset) {
EXPECT_EQ(pre_write_usage + allowed_growth, usage());
EXPECT_EQ(GetFileSizeOnDisk("test"), usage());
EXPECT_EQ(kOverlap + allowed_growth, result.bytes_written());
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, result.status());
+ EXPECT_EQ(base::File::FILE_ERROR_NO_SPACE, result.status());
}
}
-} // namespace fileapi
+} // namespace content
diff --git a/chromium/content/browser/fileapi/fileapi_message_filter.cc b/chromium/content/browser/fileapi/fileapi_message_filter.cc
index b6b89fb50a6..2eb601a7c6d 100644
--- a/chromium/content/browser/fileapi/fileapi_message_filter.cc
+++ b/chromium/content/browser/fileapi/fileapi_message_filter.cc
@@ -11,12 +11,12 @@
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
-#include "base/platform_file.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/string_util.h"
#include "base/threading/thread.h"
#include "base/time/time.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"
@@ -29,7 +29,6 @@
#include "net/url_request/url_request_context_getter.h"
#include "url/gurl.h"
#include "webkit/browser/blob/blob_storage_context.h"
-#include "webkit/browser/blob/blob_storage_host.h"
#include "webkit/browser/fileapi/file_observers.h"
#include "webkit/browser/fileapi/file_permission_policy.h"
#include "webkit/browser/fileapi/file_system_context.h"
@@ -47,12 +46,16 @@ using fileapi::FileSystemOperation;
using fileapi::FileSystemURL;
using webkit_blob::BlobData;
using webkit_blob::BlobStorageContext;
-using webkit_blob::BlobStorageHost;
namespace content {
namespace {
+const uint32 kFilteredMessageClasses[] = {
+ BlobMsgStart,
+ FileSystemMsgStart,
+};
+
void RevokeFilePermission(int child_id, const base::FilePath& path) {
ChildProcessSecurityPolicyImpl::GetInstance()->RevokeAllPermissionsForFile(
child_id, path);
@@ -66,7 +69,9 @@ FileAPIMessageFilter::FileAPIMessageFilter(
fileapi::FileSystemContext* file_system_context,
ChromeBlobStorageContext* blob_storage_context,
StreamContext* stream_context)
- : process_id_(process_id),
+ : BrowserMessageFilter(
+ kFilteredMessageClasses, arraysize(kFilteredMessageClasses)),
+ process_id_(process_id),
context_(file_system_context),
security_policy_(ChildProcessSecurityPolicyImpl::GetInstance()),
request_context_getter_(request_context_getter),
@@ -85,7 +90,9 @@ FileAPIMessageFilter::FileAPIMessageFilter(
fileapi::FileSystemContext* file_system_context,
ChromeBlobStorageContext* blob_storage_context,
StreamContext* stream_context)
- : process_id_(process_id),
+ : BrowserMessageFilter(
+ kFilteredMessageClasses, arraysize(kFilteredMessageClasses)),
+ process_id_(process_id),
context_(file_system_context),
security_policy_(ChildProcessSecurityPolicyImpl::GetInstance()),
request_context_(request_context),
@@ -137,11 +144,9 @@ base::TaskRunner* FileAPIMessageFilter::OverrideTaskRunnerForMessage(
return NULL;
}
-bool FileAPIMessageFilter::OnMessageReceived(
- const IPC::Message& message, bool* message_was_ok) {
- *message_was_ok = true;
+bool FileAPIMessageFilter::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(FileAPIMessageFilter, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(FileAPIMessageFilter, message)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_OpenFileSystem, OnOpenFileSystem)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_ResolveURL, OnResolveURL)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_DeleteFileSystem, OnDeleteFileSystem)
@@ -185,14 +190,14 @@ bool FileAPIMessageFilter::OnMessageReceived(
IPC_MESSAGE_HANDLER(StreamHostMsg_Clone, OnCloneStream)
IPC_MESSAGE_HANDLER(StreamHostMsg_Remove, OnRemoveStream)
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return handled;
}
FileAPIMessageFilter::~FileAPIMessageFilter() {}
void FileAPIMessageFilter::BadMessageReceived() {
- RecordAction(UserMetricsAction("BadMessageTerminate_FAMF"));
+ RecordAction(base::UserMetricsAction("BadMessageTerminate_FAMF"));
BrowserMessageFilter::BadMessageReceived();
}
@@ -201,9 +206,9 @@ void FileAPIMessageFilter::OnOpenFileSystem(int request_id,
fileapi::FileSystemType type) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (type == fileapi::kFileSystemTypeTemporary) {
- RecordAction(UserMetricsAction("OpenFileSystemTemporary"));
+ RecordAction(base::UserMetricsAction("OpenFileSystemTemporary"));
} else if (type == fileapi::kFileSystemTypePersistent) {
- RecordAction(UserMetricsAction("OpenFileSystemPersistent"));
+ RecordAction(base::UserMetricsAction("OpenFileSystemPersistent"));
}
fileapi::OpenFileSystemMode mode =
fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT;
@@ -220,7 +225,7 @@ void FileAPIMessageFilter::OnResolveURL(
return;
if (!security_policy_->CanReadFileSystemFile(process_id_, url)) {
Send(new FileSystemMsg_DidFail(request_id,
- base::PLATFORM_FILE_ERROR_SECURITY));
+ base::File::FILE_ERROR_SECURITY));
return;
}
@@ -250,7 +255,7 @@ void FileAPIMessageFilter::OnMove(
!security_policy_->CanDeleteFileSystemFile(process_id_, src_url) ||
!security_policy_->CanCreateFileSystemFile(process_id_, dest_url)) {
Send(new FileSystemMsg_DidFail(request_id,
- base::PLATFORM_FILE_ERROR_SECURITY));
+ base::File::FILE_ERROR_SECURITY));
return;
}
@@ -272,7 +277,7 @@ void FileAPIMessageFilter::OnCopy(
if (!security_policy_->CanReadFileSystemFile(process_id_, src_url) ||
!security_policy_->CanCopyIntoFileSystemFile(process_id_, dest_url)) {
Send(new FileSystemMsg_DidFail(request_id,
- base::PLATFORM_FILE_ERROR_SECURITY));
+ base::File::FILE_ERROR_SECURITY));
return;
}
@@ -291,7 +296,7 @@ void FileAPIMessageFilter::OnRemove(
return;
if (!security_policy_->CanDeleteFileSystemFile(process_id_, url)) {
Send(new FileSystemMsg_DidFail(request_id,
- base::PLATFORM_FILE_ERROR_SECURITY));
+ base::File::FILE_ERROR_SECURITY));
return;
}
@@ -308,7 +313,7 @@ void FileAPIMessageFilter::OnReadMetadata(
return;
if (!security_policy_->CanReadFileSystemFile(process_id_, url)) {
Send(new FileSystemMsg_DidFail(request_id,
- base::PLATFORM_FILE_ERROR_SECURITY));
+ base::File::FILE_ERROR_SECURITY));
return;
}
@@ -325,7 +330,7 @@ void FileAPIMessageFilter::OnCreate(
return;
if (!security_policy_->CanCreateFileSystemFile(process_id_, url)) {
Send(new FileSystemMsg_DidFail(request_id,
- base::PLATFORM_FILE_ERROR_SECURITY));
+ base::File::FILE_ERROR_SECURITY));
return;
}
@@ -348,7 +353,7 @@ void FileAPIMessageFilter::OnExists(
return;
if (!security_policy_->CanReadFileSystemFile(process_id_, url)) {
Send(new FileSystemMsg_DidFail(request_id,
- base::PLATFORM_FILE_ERROR_SECURITY));
+ base::File::FILE_ERROR_SECURITY));
return;
}
@@ -371,7 +376,7 @@ void FileAPIMessageFilter::OnReadDirectory(
return;
if (!security_policy_->CanReadFileSystemFile(process_id_, url)) {
Send(new FileSystemMsg_DidFail(request_id,
- base::PLATFORM_FILE_ERROR_SECURITY));
+ base::File::FILE_ERROR_SECURITY));
return;
}
@@ -397,7 +402,7 @@ void FileAPIMessageFilter::OnWrite(
return;
if (!security_policy_->CanWriteFileSystemFile(process_id_, url)) {
Send(new FileSystemMsg_DidFail(request_id,
- base::PLATFORM_FILE_ERROR_SECURITY));
+ base::File::FILE_ERROR_SECURITY));
return;
}
@@ -418,7 +423,7 @@ void FileAPIMessageFilter::OnTruncate(
return;
if (!security_policy_->CanWriteFileSystemFile(process_id_, url)) {
Send(new FileSystemMsg_DidFail(request_id,
- base::PLATFORM_FILE_ERROR_SECURITY));
+ base::File::FILE_ERROR_SECURITY));
return;
}
@@ -438,7 +443,7 @@ void FileAPIMessageFilter::OnTouchFile(
return;
if (!security_policy_->CanCreateFileSystemFile(process_id_, url)) {
Send(new FileSystemMsg_DidFail(request_id,
- base::PLATFORM_FILE_ERROR_SECURITY));
+ base::File::FILE_ERROR_SECURITY));
return;
}
@@ -462,7 +467,7 @@ void FileAPIMessageFilter::OnCancel(
} else {
// The write already finished; report that we failed to stop it.
Send(new FileSystemMsg_DidFail(
- request_id, base::PLATFORM_FILE_ERROR_INVALID_OPERATION));
+ request_id, base::File::FILE_ERROR_INVALID_OPERATION));
}
}
@@ -483,14 +488,22 @@ void FileAPIMessageFilter::OnCreateSnapshotFile(
return;
if (!security_policy_->CanReadFileSystemFile(process_id_, url)) {
Send(new FileSystemMsg_DidFail(request_id,
- base::PLATFORM_FILE_ERROR_SECURITY));
+ base::File::FILE_ERROR_SECURITY));
return;
}
- operations_[request_id] = operation_runner()->CreateSnapshotFile(
- url,
- base::Bind(&FileAPIMessageFilter::DidCreateSnapshot,
- this, request_id, url));
+ FileSystemBackend* backend = context_->GetFileSystemBackend(url.type());
+ if (backend->SupportsStreaming(url)) {
+ operations_[request_id] = operation_runner()->GetMetadata(
+ url,
+ base::Bind(&FileAPIMessageFilter::DidGetMetadataForStreaming,
+ this, request_id));
+ } else {
+ operations_[request_id] = operation_runner()->CreateSnapshotFile(
+ url,
+ base::Bind(&FileAPIMessageFilter::DidCreateSnapshot,
+ this, request_id, url));
+ }
}
void FileAPIMessageFilter::OnDidReceiveSnapshotFile(int request_id) {
@@ -676,8 +689,8 @@ void FileAPIMessageFilter::OnRemoveStream(const GURL& url) {
}
void FileAPIMessageFilter::DidFinish(int request_id,
- base::PlatformFileError result) {
- if (result == base::PLATFORM_FILE_OK)
+ base::File::Error result) {
+ if (result == base::File::FILE_OK)
Send(new FileSystemMsg_DidSucceed(request_id));
else
Send(new FileSystemMsg_DidFail(request_id, result));
@@ -686,32 +699,51 @@ void FileAPIMessageFilter::DidFinish(int request_id,
void FileAPIMessageFilter::DidGetMetadata(
int request_id,
- base::PlatformFileError result,
- const base::PlatformFileInfo& info) {
- if (result == base::PLATFORM_FILE_OK)
+ base::File::Error result,
+ const base::File::Info& info) {
+ if (result == base::File::FILE_OK)
Send(new FileSystemMsg_DidReadMetadata(request_id, info));
else
Send(new FileSystemMsg_DidFail(request_id, result));
operations_.erase(request_id);
}
+void FileAPIMessageFilter::DidGetMetadataForStreaming(
+ int request_id,
+ base::File::Error result,
+ const base::File::Info& info) {
+ if (result == base::File::FILE_OK) {
+ // For now, streaming Blobs are implemented as a successful snapshot file
+ // creation with an empty path.
+ Send(new FileSystemMsg_DidCreateSnapshotFile(request_id, info,
+ base::FilePath()));
+ } else {
+ Send(new FileSystemMsg_DidFail(request_id, result));
+ }
+ operations_.erase(request_id);
+}
+
void FileAPIMessageFilter::DidReadDirectory(
int request_id,
- base::PlatformFileError result,
+ base::File::Error result,
const std::vector<fileapi::DirectoryEntry>& entries,
bool has_more) {
- if (result == base::PLATFORM_FILE_OK)
- Send(new FileSystemMsg_DidReadDirectory(request_id, entries, has_more));
- else
+ if (result == base::File::FILE_OK) {
+ if (!entries.empty() || !has_more)
+ Send(new FileSystemMsg_DidReadDirectory(request_id, entries, has_more));
+ } else {
+ DCHECK(!has_more);
Send(new FileSystemMsg_DidFail(request_id, result));
- operations_.erase(request_id);
+ }
+ if (!has_more)
+ operations_.erase(request_id);
}
void FileAPIMessageFilter::DidWrite(int request_id,
- base::PlatformFileError result,
+ base::File::Error result,
int64 bytes,
bool complete) {
- if (result == base::PLATFORM_FILE_OK) {
+ if (result == base::File::FILE_OK) {
Send(new FileSystemMsg_DidWrite(request_id, bytes, complete));
if (complete)
operations_.erase(request_id);
@@ -724,9 +756,9 @@ void FileAPIMessageFilter::DidWrite(int request_id,
void FileAPIMessageFilter::DidOpenFileSystem(int request_id,
const GURL& root,
const std::string& filesystem_name,
- base::PlatformFileError result) {
+ base::File::Error result) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- if (result == base::PLATFORM_FILE_OK) {
+ if (result == base::File::FILE_OK) {
DCHECK(root.is_valid());
Send(new FileSystemMsg_DidOpenFileSystem(
request_id, filesystem_name, root));
@@ -736,16 +768,22 @@ void FileAPIMessageFilter::DidOpenFileSystem(int request_id,
// For OpenFileSystem we do not create a new operation, so no unregister here.
}
-void FileAPIMessageFilter::DidResolveURL(int request_id,
- base::PlatformFileError result,
- const fileapi::FileSystemInfo& info,
- const base::FilePath& file_path,
- bool is_directory) {
+void FileAPIMessageFilter::DidResolveURL(
+ int request_id,
+ base::File::Error result,
+ const fileapi::FileSystemInfo& info,
+ const base::FilePath& file_path,
+ fileapi::FileSystemContext::ResolvedEntryType type) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- if (result == base::PLATFORM_FILE_OK) {
+ if (result == base::File::FILE_OK &&
+ type == fileapi::FileSystemContext::RESOLVED_ENTRY_NOT_FOUND)
+ result = base::File::FILE_ERROR_NOT_FOUND;
+
+ if (result == base::File::FILE_OK) {
DCHECK(info.root_url.is_valid());
Send(new FileSystemMsg_DidResolveURL(
- request_id, info, file_path, is_directory));
+ request_id, info, file_path,
+ type == fileapi::FileSystemContext::RESOLVED_ENTRY_DIRECTORY));
} else {
Send(new FileSystemMsg_DidFail(request_id, result));
}
@@ -754,8 +792,8 @@ void FileAPIMessageFilter::DidResolveURL(int request_id,
void FileAPIMessageFilter::DidDeleteFileSystem(
int request_id,
- base::PlatformFileError result) {
- if (result == base::PLATFORM_FILE_OK)
+ base::File::Error result) {
+ if (result == base::File::FILE_OK)
Send(new FileSystemMsg_DidSucceed(request_id));
else
Send(new FileSystemMsg_DidFail(request_id, result));
@@ -766,14 +804,14 @@ void FileAPIMessageFilter::DidDeleteFileSystem(
void FileAPIMessageFilter::DidCreateSnapshot(
int request_id,
const fileapi::FileSystemURL& url,
- base::PlatformFileError result,
- const base::PlatformFileInfo& info,
+ base::File::Error result,
+ const base::File::Info& info,
const base::FilePath& platform_path,
const scoped_refptr<webkit_blob::ShareableFileReference>& /* unused */) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
operations_.erase(request_id);
- if (result != base::PLATFORM_FILE_OK) {
+ if (result != base::File::FILE_OK) {
Send(new FileSystemMsg_DidFail(request_id, result));
return;
}
@@ -809,14 +847,14 @@ void FileAPIMessageFilter::DidCreateSnapshot(
// Return the file info and platform_path.
Send(new FileSystemMsg_DidCreateSnapshotFile(
- request_id, info, platform_path));
+ request_id, info, platform_path));
}
bool FileAPIMessageFilter::ValidateFileSystemURL(
int request_id, const fileapi::FileSystemURL& url) {
if (!FileSystemURLIsValid(context_, url)) {
Send(new FileSystemMsg_DidFail(request_id,
- base::PLATFORM_FILE_ERROR_INVALID_URL));
+ base::File::FILE_ERROR_INVALID_URL));
return false;
}
@@ -825,7 +863,7 @@ bool FileAPIMessageFilter::ValidateFileSystemURL(
// validation.
if (url.type() == fileapi::kFileSystemTypePluginPrivate) {
Send(new FileSystemMsg_DidFail(request_id,
- base::PLATFORM_FILE_ERROR_SECURITY));
+ base::File::FILE_ERROR_SECURITY));
return false;
}
diff --git a/chromium/content/browser/fileapi/fileapi_message_filter.h b/chromium/content/browser/fileapi/fileapi_message_filter.h
index d734e85d217..7a98b1483c9 100644
--- a/chromium/content/browser/fileapi/fileapi_message_filter.h
+++ b/chromium/content/browser/fileapi/fileapi_message_filter.h
@@ -14,11 +14,11 @@
#include "base/files/file_util_proxy.h"
#include "base/memory/ref_counted.h"
#include "base/memory/shared_memory.h"
-#include "base/platform_file.h"
#include "content/browser/streams/stream.h"
#include "content/browser/streams/stream_context.h"
#include "content/common/content_export.h"
#include "content/public/browser/browser_message_filter.h"
+#include "webkit/browser/fileapi/file_system_context.h"
#include "webkit/browser/fileapi/file_system_operation_runner.h"
#include "webkit/common/blob/blob_data.h"
#include "webkit/common/fileapi/file_system_types.h"
@@ -33,7 +33,6 @@ class Time;
namespace fileapi {
class FileSystemURL;
-class FileSystemContext;
class FileSystemOperationRunner;
struct DirectoryEntry;
struct FileSystemInfo;
@@ -44,8 +43,11 @@ class URLRequestContext;
class URLRequestContextGetter;
} // namespace net
-namespace webkit_blob {
+namespace content {
class BlobStorageHost;
+}
+
+namespace webkit_blob {
class ShareableFileReference;
}
@@ -77,8 +79,7 @@ class CONTENT_EXPORT FileAPIMessageFilter : public BrowserMessageFilter {
virtual void OnChannelClosing() OVERRIDE;
virtual base::TaskRunner* OverrideTaskRunnerForMessage(
const IPC::Message& message) OVERRIDE;
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
protected:
virtual ~FileAPIMessageFilter();
@@ -137,7 +138,6 @@ class CONTENT_EXPORT FileAPIMessageFilter : public BrowserMessageFilter {
size_t buffer_size);
void OnFinishBuildingBlob(const std::string& uuid,
const std::string& content_type);
- void OnCancelBuildingBlob(const std::string& uuid);
void OnIncrementBlobRefCount(const std::string& uuid);
void OnDecrementBlobRefCount(const std::string& uuid);
void OnRegisterPublicBlobURL(const GURL& public_url, const std::string& uuid);
@@ -162,35 +162,37 @@ class CONTENT_EXPORT FileAPIMessageFilter : public BrowserMessageFilter {
void OnRemoveStream(const GURL& url);
// Callback functions to be used when each file operation is finished.
- void DidFinish(int request_id, base::PlatformFileError result);
- void DidCancel(int request_id, base::PlatformFileError result);
+ void DidFinish(int request_id, base::File::Error result);
void DidGetMetadata(int request_id,
- base::PlatformFileError result,
- const base::PlatformFileInfo& info);
+ base::File::Error result,
+ const base::File::Info& info);
+ void DidGetMetadataForStreaming(int request_id,
+ base::File::Error result,
+ const base::File::Info& info);
void DidReadDirectory(int request_id,
- base::PlatformFileError result,
+ base::File::Error result,
const std::vector<fileapi::DirectoryEntry>& entries,
bool has_more);
void DidWrite(int request_id,
- base::PlatformFileError result,
+ base::File::Error result,
int64 bytes,
bool complete);
void DidOpenFileSystem(int request_id,
const GURL& root,
const std::string& filesystem_name,
- base::PlatformFileError result);
+ base::File::Error result);
void DidResolveURL(int request_id,
- base::PlatformFileError result,
+ base::File::Error result,
const fileapi::FileSystemInfo& info,
const base::FilePath& file_path,
- bool is_directory);
+ fileapi::FileSystemContext::ResolvedEntryType type);
void DidDeleteFileSystem(int request_id,
- base::PlatformFileError result);
+ base::File::Error result);
void DidCreateSnapshot(
int request_id,
const fileapi::FileSystemURL& url,
- base::PlatformFileError result,
- const base::PlatformFileInfo& info,
+ base::File::Error result,
+ const base::File::Info& info,
const base::FilePath& platform_path,
const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref);
@@ -216,8 +218,8 @@ class CONTENT_EXPORT FileAPIMessageFilter : public BrowserMessageFilter {
typedef std::map<int, OperationID> OperationsMap;
OperationsMap operations_;
- // The getter holds the context until Init() can be called from the
- // IO thread, which will extract the net::URLRequestContext from it.
+ // The getter holds the context until OnChannelConnected() can be called from
+ // the IO thread, which will extract the net::URLRequestContext from it.
scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
net::URLRequestContext* request_context_;
@@ -228,7 +230,7 @@ class CONTENT_EXPORT FileAPIMessageFilter : public BrowserMessageFilter {
// Keeps track of blobs used in this process and cleans up
// when the renderer process dies.
- scoped_ptr<webkit_blob::BlobStorageHost> blob_storage_host_;
+ scoped_ptr<BlobStorageHost> blob_storage_host_;
// Keep track of stream URLs registered in this process. Need to unregister
// all of them when the renderer process dies.
diff --git a/chromium/content/browser/fileapi/fileapi_message_filter_unittest.cc b/chromium/content/browser/fileapi/fileapi_message_filter_unittest.cc
index 3dbdcc3d538..3a4c47e93eb 100644
--- a/chromium/content/browser/fileapi/fileapi_message_filter_unittest.cc
+++ b/chromium/content/browser/fileapi/fileapi_message_filter_unittest.cc
@@ -50,7 +50,7 @@ class FileAPIMessageFilterTest : public testing::Test {
protected:
virtual void SetUp() OVERRIDE {
file_system_context_ =
- fileapi::CreateFileSystemContextForTesting(NULL, base::FilePath());
+ CreateFileSystemContextForTesting(NULL, base::FilePath());
std::vector<fileapi::FileSystemType> types;
file_system_context_->GetFileSystemTypes(&types);
@@ -75,13 +75,6 @@ class FileAPIMessageFilterTest : public testing::Test {
message_loop_.RunUntilIdle();
}
- // Tests via OnMessageReceived(const IPC::Message&). The channel proxy calls
- // this method.
- bool InvokeOnMessageReceived(const IPC::Message& message) {
- bool message_was_ok;
- return filter_->OnMessageReceived(message, &message_was_ok);
- }
-
base::MessageLoop message_loop_;
TestBrowserThread io_browser_thread_;
@@ -109,8 +102,7 @@ TEST_F(FileAPIMessageFilterTest, CloseChannelWithInflightRequest) {
int request_id = 0;
const GURL kUrl("filesystem:http://example.com/temporary/foo");
FileSystemHostMsg_ReadMetadata read_metadata(request_id++, kUrl);
- bool message_was_ok;
- EXPECT_TRUE(filter->OnMessageReceived(read_metadata, &message_was_ok));
+ EXPECT_TRUE(filter->OnMessageReceived(read_metadata));
// Close the filter while it has inflight request.
filter->OnChannelClosing();
@@ -143,8 +135,7 @@ TEST_F(FileAPIMessageFilterTest, MultipleFilters) {
int request_id = 0;
const GURL kUrl("filesystem:http://example.com/temporary/foo");
FileSystemHostMsg_ReadMetadata read_metadata(request_id++, kUrl);
- bool message_was_ok;
- EXPECT_TRUE(filter1->OnMessageReceived(read_metadata, &message_was_ok));
+ EXPECT_TRUE(filter1->OnMessageReceived(read_metadata));
// Close the other filter before the request for filter1 is processed.
filter2->OnChannelClosing();
@@ -162,7 +153,7 @@ TEST_F(FileAPIMessageFilterTest, BuildEmptyStream) {
EXPECT_EQ(NULL, stream_registry->GetStream(kUrl).get());
StreamHostMsg_StartBuilding start_message(kUrl, kFakeContentType);
- EXPECT_TRUE(InvokeOnMessageReceived(start_message));
+ EXPECT_TRUE(filter_->OnMessageReceived(start_message));
const int kBufferSize = 10;
scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kBufferSize));
@@ -177,7 +168,7 @@ TEST_F(FileAPIMessageFilterTest, BuildEmptyStream) {
stream = NULL;
StreamHostMsg_FinishBuilding finish_message(kUrl);
- EXPECT_TRUE(InvokeOnMessageReceived(finish_message));
+ EXPECT_TRUE(filter_->OnMessageReceived(finish_message));
stream = stream_registry->GetStream(kUrl);
ASSERT_FALSE(stream.get() == NULL);
@@ -204,16 +195,16 @@ TEST_F(FileAPIMessageFilterTest, BuildNonEmptyStream) {
EXPECT_EQ(NULL, stream_registry->GetStream(kUrl).get());
StreamHostMsg_StartBuilding start_message(kUrl, kFakeContentType);
- EXPECT_TRUE(InvokeOnMessageReceived(start_message));
+ EXPECT_TRUE(filter_->OnMessageReceived(start_message));
webkit_blob::BlobData::Item item;
const std::string kFakeData = "foobarbaz";
item.SetToBytes(kFakeData.data(), kFakeData.size());
StreamHostMsg_AppendBlobDataItem append_message(kUrl, item);
- EXPECT_TRUE(InvokeOnMessageReceived(append_message));
+ EXPECT_TRUE(filter_->OnMessageReceived(append_message));
StreamHostMsg_FinishBuilding finish_message(kUrl);
- EXPECT_TRUE(InvokeOnMessageReceived(finish_message));
+ EXPECT_TRUE(filter_->OnMessageReceived(finish_message));
// Run loop to finish transfer and commit finalize command.
message_loop_.RunUntilIdle();
@@ -248,7 +239,7 @@ TEST_F(FileAPIMessageFilterTest, BuildStreamWithSharedMemory) {
filter_->set_peer_pid_for_testing(base::Process::Current().pid());
StreamHostMsg_StartBuilding start_message(kUrl, kFakeContentType);
- EXPECT_TRUE(InvokeOnMessageReceived(start_message));
+ EXPECT_TRUE(filter_->OnMessageReceived(start_message));
const std::string kFakeData = "foobarbaz";
@@ -257,10 +248,10 @@ TEST_F(FileAPIMessageFilterTest, BuildStreamWithSharedMemory) {
memcpy(shared_memory->memory(), kFakeData.data(), kFakeData.size());
StreamHostMsg_SyncAppendSharedMemory append_message(
kUrl, shared_memory->handle(), kFakeData.size());
- EXPECT_TRUE(InvokeOnMessageReceived(append_message));
+ EXPECT_TRUE(filter_->OnMessageReceived(append_message));
StreamHostMsg_FinishBuilding finish_message(kUrl);
- EXPECT_TRUE(InvokeOnMessageReceived(finish_message));
+ EXPECT_TRUE(filter_->OnMessageReceived(finish_message));
// Run loop to finish transfer and commit finalize command.
message_loop_.RunUntilIdle();
@@ -287,7 +278,7 @@ TEST_F(FileAPIMessageFilterTest, BuildStreamAndCallOnChannelClosing) {
const GURL kUrl(kFakeBlobInternalUrlSpec);
StreamHostMsg_StartBuilding start_message(kUrl, kFakeContentType);
- EXPECT_TRUE(InvokeOnMessageReceived(start_message));
+ EXPECT_TRUE(filter_->OnMessageReceived(start_message));
ASSERT_FALSE(stream_registry->GetStream(kUrl).get() == NULL);
@@ -303,10 +294,10 @@ TEST_F(FileAPIMessageFilterTest, CloneStream) {
const GURL kDestUrl(kFakeBlobInternalUrlSpec2);
StreamHostMsg_StartBuilding start_message(kUrl, kFakeContentType);
- EXPECT_TRUE(InvokeOnMessageReceived(start_message));
+ EXPECT_TRUE(filter_->OnMessageReceived(start_message));
StreamHostMsg_Clone clone_message(kDestUrl, kUrl);
- EXPECT_TRUE(InvokeOnMessageReceived(clone_message));
+ EXPECT_TRUE(filter_->OnMessageReceived(clone_message));
ASSERT_FALSE(stream_registry->GetStream(kUrl).get() == NULL);
ASSERT_FALSE(stream_registry->GetStream(kDestUrl).get() == NULL);
diff --git a/chromium/content/browser/fileapi/isolated_context_unittest.cc b/chromium/content/browser/fileapi/isolated_context_unittest.cc
new file mode 100644
index 00000000000..be5cc8e1c00
--- /dev/null
+++ b/chromium/content/browser/fileapi/isolated_context_unittest.cc
@@ -0,0 +1,360 @@
+// 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 <string>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/fileapi/file_system_url.h"
+#include "webkit/browser/fileapi/isolated_context.h"
+
+#define FPL(x) FILE_PATH_LITERAL(x)
+
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+#define DRIVE FPL("C:")
+#else
+#define DRIVE
+#endif
+
+using fileapi::FileSystemMountOption;
+using fileapi::FileSystemURL;
+using fileapi::IsolatedContext;
+using fileapi::kFileSystemTypeDragged;
+using fileapi::kFileSystemTypeIsolated;
+using fileapi::kFileSystemTypeNativeLocal;
+
+namespace content {
+
+typedef IsolatedContext::MountPointInfo FileInfo;
+
+namespace {
+
+const base::FilePath kTestPaths[] = {
+ base::FilePath(DRIVE FPL("/a/b.txt")),
+ base::FilePath(DRIVE FPL("/c/d/e")),
+ base::FilePath(DRIVE FPL("/h/")),
+ base::FilePath(DRIVE FPL("/")),
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+ base::FilePath(DRIVE FPL("\\foo\\bar")),
+ base::FilePath(DRIVE FPL("\\")),
+#endif
+ // For duplicated base name test.
+ base::FilePath(DRIVE FPL("/")),
+ base::FilePath(DRIVE FPL("/f/e")),
+ base::FilePath(DRIVE FPL("/f/b.txt")),
+};
+
+} // namespace
+
+class IsolatedContextTest : public testing::Test {
+ public:
+ IsolatedContextTest() {
+ for (size_t i = 0; i < arraysize(kTestPaths); ++i)
+ fileset_.insert(kTestPaths[i].NormalizePathSeparators());
+ }
+
+ virtual void SetUp() {
+ IsolatedContext::FileInfoSet files;
+ for (size_t i = 0; i < arraysize(kTestPaths); ++i) {
+ std::string name;
+ ASSERT_TRUE(
+ files.AddPath(kTestPaths[i].NormalizePathSeparators(), &name));
+ names_.push_back(name);
+ }
+ id_ = IsolatedContext::GetInstance()->RegisterDraggedFileSystem(files);
+ IsolatedContext::GetInstance()->AddReference(id_);
+ ASSERT_FALSE(id_.empty());
+ }
+
+ virtual void TearDown() {
+ IsolatedContext::GetInstance()->RemoveReference(id_);
+ }
+
+ IsolatedContext* isolated_context() const {
+ return IsolatedContext::GetInstance();
+ }
+
+ protected:
+ std::string id_;
+ std::multiset<base::FilePath> fileset_;
+ std::vector<std::string> names_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(IsolatedContextTest);
+};
+
+TEST_F(IsolatedContextTest, RegisterAndRevokeTest) {
+ // See if the returned top-level entries match with what we registered.
+ std::vector<FileInfo> toplevels;
+ ASSERT_TRUE(isolated_context()->GetDraggedFileInfo(id_, &toplevels));
+ ASSERT_EQ(fileset_.size(), toplevels.size());
+ for (size_t i = 0; i < toplevels.size(); ++i) {
+ ASSERT_TRUE(fileset_.find(toplevels[i].path) != fileset_.end());
+ }
+
+ // See if the name of each registered kTestPaths (that is what we
+ // register in SetUp() by RegisterDraggedFileSystem) is properly cracked as
+ // a valid virtual path in the isolated filesystem.
+ for (size_t i = 0; i < arraysize(kTestPaths); ++i) {
+ base::FilePath virtual_path = isolated_context()->CreateVirtualRootPath(id_)
+ .AppendASCII(names_[i]);
+ std::string cracked_id;
+ base::FilePath cracked_path;
+ std::string cracked_inner_id;
+ fileapi::FileSystemType cracked_type;
+ FileSystemMountOption cracked_option;
+ ASSERT_TRUE(isolated_context()->CrackVirtualPath(
+ virtual_path, &cracked_id, &cracked_type, &cracked_inner_id,
+ &cracked_path, &cracked_option));
+ ASSERT_EQ(kTestPaths[i].NormalizePathSeparators().value(),
+ cracked_path.value());
+ ASSERT_EQ(id_, cracked_id);
+ ASSERT_EQ(kFileSystemTypeDragged, cracked_type);
+ EXPECT_TRUE(cracked_inner_id.empty());
+ }
+
+ // Make sure GetRegisteredPath returns false for id_ since it is
+ // registered for dragged files.
+ base::FilePath path;
+ ASSERT_FALSE(isolated_context()->GetRegisteredPath(id_, &path));
+
+ // Deref the current one and registering a new one.
+ isolated_context()->RemoveReference(id_);
+
+ std::string id2 = isolated_context()->RegisterFileSystemForPath(
+ kFileSystemTypeNativeLocal, std::string(),
+ base::FilePath(DRIVE FPL("/foo")), NULL);
+
+ // Make sure the GetDraggedFileInfo returns false for both ones.
+ ASSERT_FALSE(isolated_context()->GetDraggedFileInfo(id2, &toplevels));
+ ASSERT_FALSE(isolated_context()->GetDraggedFileInfo(id_, &toplevels));
+
+ // Make sure the GetRegisteredPath returns true only for the new one.
+ ASSERT_FALSE(isolated_context()->GetRegisteredPath(id_, &path));
+ ASSERT_TRUE(isolated_context()->GetRegisteredPath(id2, &path));
+
+ // Try registering three more file systems for the same path as id2.
+ std::string id3 = isolated_context()->RegisterFileSystemForPath(
+ kFileSystemTypeNativeLocal, std::string(), path, NULL);
+ std::string id4 = isolated_context()->RegisterFileSystemForPath(
+ kFileSystemTypeNativeLocal, std::string(), path, NULL);
+ std::string id5 = isolated_context()->RegisterFileSystemForPath(
+ kFileSystemTypeNativeLocal, std::string(), path, NULL);
+
+ // Remove file system for id4.
+ isolated_context()->AddReference(id4);
+ isolated_context()->RemoveReference(id4);
+
+ // Only id4 should become invalid now.
+ ASSERT_TRUE(isolated_context()->GetRegisteredPath(id2, &path));
+ ASSERT_TRUE(isolated_context()->GetRegisteredPath(id3, &path));
+ ASSERT_FALSE(isolated_context()->GetRegisteredPath(id4, &path));
+ ASSERT_TRUE(isolated_context()->GetRegisteredPath(id5, &path));
+
+ // Revoke file system id5, after adding multiple references.
+ isolated_context()->AddReference(id5);
+ isolated_context()->AddReference(id5);
+ isolated_context()->AddReference(id5);
+ isolated_context()->RevokeFileSystem(id5);
+
+ // No matter how many references we add id5 must be invalid now.
+ ASSERT_TRUE(isolated_context()->GetRegisteredPath(id2, &path));
+ ASSERT_TRUE(isolated_context()->GetRegisteredPath(id3, &path));
+ ASSERT_FALSE(isolated_context()->GetRegisteredPath(id4, &path));
+ ASSERT_FALSE(isolated_context()->GetRegisteredPath(id5, &path));
+
+ // Revoke the file systems by path.
+ isolated_context()->RevokeFileSystemByPath(path);
+
+ // Now all the file systems associated to the path must be invalid.
+ ASSERT_FALSE(isolated_context()->GetRegisteredPath(id2, &path));
+ ASSERT_FALSE(isolated_context()->GetRegisteredPath(id3, &path));
+ ASSERT_FALSE(isolated_context()->GetRegisteredPath(id4, &path));
+ ASSERT_FALSE(isolated_context()->GetRegisteredPath(id5, &path));
+}
+
+TEST_F(IsolatedContextTest, CrackWithRelativePaths) {
+ const struct {
+ base::FilePath::StringType path;
+ bool valid;
+ } relatives[] = {
+ { FPL("foo"), true },
+ { FPL("foo/bar"), true },
+ { FPL(".."), false },
+ { FPL("foo/.."), false },
+ { FPL("foo/../bar"), false },
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+# define SHOULD_FAIL_WITH_WIN_SEPARATORS false
+#else
+# define SHOULD_FAIL_WITH_WIN_SEPARATORS true
+#endif
+ { FPL("foo\\..\\baz"), SHOULD_FAIL_WITH_WIN_SEPARATORS },
+ { FPL("foo/..\\baz"), SHOULD_FAIL_WITH_WIN_SEPARATORS },
+ };
+
+ for (size_t i = 0; i < arraysize(kTestPaths); ++i) {
+ for (size_t j = 0; j < ARRAYSIZE_UNSAFE(relatives); ++j) {
+ SCOPED_TRACE(testing::Message() << "Testing "
+ << kTestPaths[i].value() << " " << relatives[j].path);
+ base::FilePath virtual_path =
+ isolated_context()->CreateVirtualRootPath(id_).AppendASCII(
+ names_[i]).Append(relatives[j].path);
+ std::string cracked_id;
+ base::FilePath cracked_path;
+ fileapi::FileSystemType cracked_type;
+ std::string cracked_inner_id;
+ FileSystemMountOption cracked_option;
+ if (!relatives[j].valid) {
+ ASSERT_FALSE(isolated_context()->CrackVirtualPath(
+ virtual_path, &cracked_id, &cracked_type, &cracked_inner_id,
+ &cracked_path, &cracked_option));
+ continue;
+ }
+ ASSERT_TRUE(isolated_context()->CrackVirtualPath(
+ virtual_path, &cracked_id, &cracked_type, &cracked_inner_id,
+ &cracked_path, &cracked_option));
+ ASSERT_EQ(kTestPaths[i].Append(relatives[j].path)
+ .NormalizePathSeparators().value(),
+ cracked_path.value());
+ ASSERT_EQ(id_, cracked_id);
+ ASSERT_EQ(kFileSystemTypeDragged, cracked_type);
+ EXPECT_TRUE(cracked_inner_id.empty());
+ }
+ }
+}
+
+TEST_F(IsolatedContextTest, CrackURLWithRelativePaths) {
+ const struct {
+ base::FilePath::StringType path;
+ bool valid;
+ } relatives[] = {
+ { FPL("foo"), true },
+ { FPL("foo/bar"), true },
+ { FPL(".."), false },
+ { FPL("foo/.."), false },
+ { FPL("foo/../bar"), false },
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+# define SHOULD_FAIL_WITH_WIN_SEPARATORS false
+#else
+# define SHOULD_FAIL_WITH_WIN_SEPARATORS true
+#endif
+ { FPL("foo\\..\\baz"), SHOULD_FAIL_WITH_WIN_SEPARATORS },
+ { FPL("foo/..\\baz"), SHOULD_FAIL_WITH_WIN_SEPARATORS },
+ };
+
+ for (size_t i = 0; i < arraysize(kTestPaths); ++i) {
+ for (size_t j = 0; j < ARRAYSIZE_UNSAFE(relatives); ++j) {
+ SCOPED_TRACE(testing::Message() << "Testing "
+ << kTestPaths[i].value() << " " << relatives[j].path);
+ base::FilePath virtual_path =
+ isolated_context()->CreateVirtualRootPath(id_).AppendASCII(
+ names_[i]).Append(relatives[j].path);
+
+ FileSystemURL cracked = isolated_context()->CreateCrackedFileSystemURL(
+ GURL("http://chromium.org"), kFileSystemTypeIsolated, virtual_path);
+
+ ASSERT_EQ(relatives[j].valid, cracked.is_valid());
+
+ if (!relatives[j].valid)
+ continue;
+ ASSERT_EQ(GURL("http://chromium.org"), cracked.origin());
+ ASSERT_EQ(kTestPaths[i].Append(relatives[j].path)
+ .NormalizePathSeparators().value(),
+ cracked.path().value());
+ ASSERT_EQ(virtual_path.NormalizePathSeparators(), cracked.virtual_path());
+ ASSERT_EQ(id_, cracked.filesystem_id());
+ ASSERT_EQ(kFileSystemTypeDragged, cracked.type());
+ ASSERT_EQ(kFileSystemTypeIsolated, cracked.mount_type());
+ }
+ }
+}
+
+TEST_F(IsolatedContextTest, TestWithVirtualRoot) {
+ std::string cracked_id;
+ base::FilePath cracked_path;
+ FileSystemMountOption cracked_option;
+
+ // Trying to crack virtual root "/" returns true but with empty cracked path
+ // as "/" of the isolated filesystem is a pure virtual directory
+ // that has no corresponding platform directory.
+ base::FilePath virtual_path = isolated_context()->CreateVirtualRootPath(id_);
+ ASSERT_TRUE(isolated_context()->CrackVirtualPath(
+ virtual_path, &cracked_id, NULL, NULL, &cracked_path, &cracked_option));
+ ASSERT_EQ(FPL(""), cracked_path.value());
+ ASSERT_EQ(id_, cracked_id);
+
+ // Trying to crack "/foo" should fail (because "foo" is not the one
+ // included in the kTestPaths).
+ virtual_path = isolated_context()->CreateVirtualRootPath(
+ id_).AppendASCII("foo");
+ ASSERT_FALSE(isolated_context()->CrackVirtualPath(
+ virtual_path, &cracked_id, NULL, NULL, &cracked_path, &cracked_option));
+}
+
+TEST_F(IsolatedContextTest, CanHandleURL) {
+ const GURL test_origin("http://chromium.org");
+ const base::FilePath test_path(FPL("/mount"));
+
+ // Should handle isolated file system.
+ EXPECT_TRUE(isolated_context()->HandlesFileSystemMountType(
+ fileapi::kFileSystemTypeIsolated));
+
+ // Shouldn't handle the rest.
+ EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType(
+ fileapi::kFileSystemTypeExternal));
+ EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType(
+ fileapi::kFileSystemTypeTemporary));
+ EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType(
+ fileapi::kFileSystemTypePersistent));
+ EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType(
+ fileapi::kFileSystemTypeTest));
+ // Not even if it's isolated subtype.
+ EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType(
+ fileapi::kFileSystemTypeNativeLocal));
+ EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType(
+ fileapi::kFileSystemTypeDragged));
+ EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType(
+ fileapi::kFileSystemTypeNativeMedia));
+ EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType(
+ fileapi::kFileSystemTypeDeviceMedia));
+}
+
+TEST_F(IsolatedContextTest, VirtualFileSystemTests) {
+ // Should be able to register empty and non-absolute paths
+ std::string empty_fsid = isolated_context()->RegisterFileSystemForVirtualPath(
+ fileapi::kFileSystemTypeIsolated, "_", base::FilePath());
+ std::string relative_fsid =
+ isolated_context()->RegisterFileSystemForVirtualPath(
+ fileapi::kFileSystemTypeIsolated, "_",
+ base::FilePath(FPL("relpath")));
+ ASSERT_FALSE(empty_fsid.empty());
+ ASSERT_FALSE(relative_fsid.empty());
+
+ // Make sure that filesystem root is not prepended to cracked virtual paths.
+ base::FilePath database_root = base::FilePath(DRIVE FPL("/database_path"));
+ std::string database_fsid =
+ isolated_context()->RegisterFileSystemForVirtualPath(
+ fileapi::kFileSystemTypeIsolated, "_", database_root);
+
+ base::FilePath test_virtual_path =
+ base::FilePath().AppendASCII("virtualdir").AppendASCII("virtualfile.txt");
+
+ base::FilePath whole_virtual_path =
+ isolated_context()->CreateVirtualRootPath(database_fsid)
+ .AppendASCII("_").Append(test_virtual_path);
+
+ std::string cracked_id;
+ base::FilePath cracked_path;
+ std::string cracked_inner_id;
+ FileSystemMountOption cracked_option;
+ ASSERT_TRUE(isolated_context()->CrackVirtualPath(
+ whole_virtual_path, &cracked_id, NULL, &cracked_inner_id,
+ &cracked_path, &cracked_option));
+ ASSERT_EQ(database_fsid, cracked_id);
+ ASSERT_EQ(test_virtual_path, cracked_path);
+ EXPECT_TRUE(cracked_inner_id.empty());
+}
+
+} // namespace content
diff --git a/chromium/content/browser/fileapi/local_file_stream_reader_unittest.cc b/chromium/content/browser/fileapi/local_file_stream_reader_unittest.cc
new file mode 100644
index 00000000000..c9547ed770c
--- /dev/null
+++ b/chromium/content/browser/fileapi/local_file_stream_reader_unittest.cc
@@ -0,0 +1,258 @@
+// 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 "webkit/browser/blob/local_file_stream_reader.h"
+
+#include <string>
+
+#include "base/file_util.h"
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/run_loop.h"
+#include "base/threading/thread.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using webkit_blob::LocalFileStreamReader;
+
+namespace content {
+
+namespace {
+
+const char kTestData[] = "0123456789";
+const int kTestDataSize = arraysize(kTestData) - 1;
+
+void ReadFromReader(LocalFileStreamReader* reader,
+ std::string* data, size_t size,
+ int* result) {
+ ASSERT_TRUE(reader != NULL);
+ ASSERT_TRUE(result != NULL);
+ *result = net::OK;
+ net::TestCompletionCallback callback;
+ size_t total_bytes_read = 0;
+ while (total_bytes_read < size) {
+ scoped_refptr<net::IOBufferWithSize> buf(
+ new net::IOBufferWithSize(size - total_bytes_read));
+ int rv = reader->Read(buf.get(), buf->size(), callback.callback());
+ if (rv == net::ERR_IO_PENDING)
+ rv = callback.WaitForResult();
+ if (rv < 0)
+ *result = rv;
+ if (rv <= 0)
+ break;
+ total_bytes_read += rv;
+ data->append(buf->data(), rv);
+ }
+}
+
+void NeverCalled(int) { ADD_FAILURE(); }
+void EmptyCallback() {}
+
+void QuitLoop() {
+ base::MessageLoop::current()->Quit();
+}
+
+} // namespace
+
+class LocalFileStreamReaderTest : public testing::Test {
+ public:
+ LocalFileStreamReaderTest()
+ : file_thread_("FileUtilProxyTestFileThread") {}
+
+ virtual void SetUp() OVERRIDE {
+ ASSERT_TRUE(file_thread_.Start());
+ ASSERT_TRUE(dir_.CreateUniqueTempDir());
+
+ base::WriteFile(test_path(), kTestData, kTestDataSize);
+ base::File::Info info;
+ ASSERT_TRUE(base::GetFileInfo(test_path(), &info));
+ test_file_modification_time_ = info.last_modified;
+ }
+
+ virtual void TearDown() OVERRIDE {
+ // Give another chance for deleted streams to perform Close.
+ base::RunLoop().RunUntilIdle();
+ file_thread_.Stop();
+ base::RunLoop().RunUntilIdle();
+ }
+
+ protected:
+ LocalFileStreamReader* CreateFileReader(
+ const base::FilePath& path,
+ int64 initial_offset,
+ const base::Time& expected_modification_time) {
+ return new LocalFileStreamReader(
+ file_task_runner(),
+ path,
+ initial_offset,
+ expected_modification_time);
+ }
+
+ void TouchTestFile() {
+ base::Time new_modified_time =
+ test_file_modification_time() - base::TimeDelta::FromSeconds(1);
+ ASSERT_TRUE(base::TouchFile(test_path(),
+ test_file_modification_time(),
+ new_modified_time));
+ }
+
+ base::MessageLoopProxy* file_task_runner() const {
+ return file_thread_.message_loop_proxy().get();
+ }
+
+ base::FilePath test_dir() const { return dir_.path(); }
+ base::FilePath test_path() const { return dir_.path().AppendASCII("test"); }
+ base::Time test_file_modification_time() const {
+ return test_file_modification_time_;
+ }
+
+ void EnsureFileTaskFinished() {
+ file_task_runner()->PostTaskAndReply(
+ FROM_HERE, base::Bind(&EmptyCallback), base::Bind(&QuitLoop));
+ base::MessageLoop::current()->Run();
+ }
+
+ private:
+ base::MessageLoopForIO message_loop_;
+ base::Thread file_thread_;
+ base::ScopedTempDir dir_;
+ base::Time test_file_modification_time_;
+};
+
+TEST_F(LocalFileStreamReaderTest, NonExistent) {
+ base::FilePath nonexistent_path = test_dir().AppendASCII("nonexistent");
+ scoped_ptr<LocalFileStreamReader> reader(
+ CreateFileReader(nonexistent_path, 0, base::Time()));
+ int result = 0;
+ std::string data;
+ ReadFromReader(reader.get(), &data, 10, &result);
+ ASSERT_EQ(net::ERR_FILE_NOT_FOUND, result);
+ ASSERT_EQ(0U, data.size());
+}
+
+TEST_F(LocalFileStreamReaderTest, Empty) {
+ base::FilePath empty_path = test_dir().AppendASCII("empty");
+ base::File file(empty_path, base::File::FLAG_CREATE | base::File::FLAG_READ);
+ ASSERT_TRUE(file.IsValid());
+ file.Close();
+
+ scoped_ptr<LocalFileStreamReader> reader(
+ CreateFileReader(empty_path, 0, base::Time()));
+ int result = 0;
+ std::string data;
+ ReadFromReader(reader.get(), &data, 10, &result);
+ ASSERT_EQ(net::OK, result);
+ ASSERT_EQ(0U, data.size());
+
+ net::TestInt64CompletionCallback callback;
+ int64 length_result = reader->GetLength(callback.callback());
+ if (length_result == net::ERR_IO_PENDING)
+ length_result = callback.WaitForResult();
+ ASSERT_EQ(0, result);
+}
+
+TEST_F(LocalFileStreamReaderTest, GetLengthNormal) {
+ scoped_ptr<LocalFileStreamReader> reader(
+ CreateFileReader(test_path(), 0, test_file_modification_time()));
+ net::TestInt64CompletionCallback callback;
+ int64 result = reader->GetLength(callback.callback());
+ if (result == net::ERR_IO_PENDING)
+ result = callback.WaitForResult();
+ ASSERT_EQ(kTestDataSize, result);
+}
+
+TEST_F(LocalFileStreamReaderTest, GetLengthAfterModified) {
+ // Touch file so that the file's modification time becomes different
+ // from what we expect.
+ TouchTestFile();
+
+ scoped_ptr<LocalFileStreamReader> reader(
+ CreateFileReader(test_path(), 0, test_file_modification_time()));
+ net::TestInt64CompletionCallback callback;
+ int64 result = reader->GetLength(callback.callback());
+ if (result == net::ERR_IO_PENDING)
+ result = callback.WaitForResult();
+ ASSERT_EQ(net::ERR_UPLOAD_FILE_CHANGED, result);
+
+ // With NULL expected modification time this should work.
+ reader.reset(CreateFileReader(test_path(), 0, base::Time()));
+ result = reader->GetLength(callback.callback());
+ if (result == net::ERR_IO_PENDING)
+ result = callback.WaitForResult();
+ ASSERT_EQ(kTestDataSize, result);
+}
+
+TEST_F(LocalFileStreamReaderTest, GetLengthWithOffset) {
+ scoped_ptr<LocalFileStreamReader> reader(
+ CreateFileReader(test_path(), 3, base::Time()));
+ net::TestInt64CompletionCallback callback;
+ int64 result = reader->GetLength(callback.callback());
+ if (result == net::ERR_IO_PENDING)
+ result = callback.WaitForResult();
+ // Initial offset does not affect the result of GetLength.
+ ASSERT_EQ(kTestDataSize, result);
+}
+
+TEST_F(LocalFileStreamReaderTest, ReadNormal) {
+ scoped_ptr<LocalFileStreamReader> reader(
+ CreateFileReader(test_path(), 0, test_file_modification_time()));
+ int result = 0;
+ std::string data;
+ ReadFromReader(reader.get(), &data, kTestDataSize, &result);
+ ASSERT_EQ(net::OK, result);
+ ASSERT_EQ(kTestData, data);
+}
+
+TEST_F(LocalFileStreamReaderTest, ReadAfterModified) {
+ // Touch file so that the file's modification time becomes different
+ // from what we expect.
+ TouchTestFile();
+
+ scoped_ptr<LocalFileStreamReader> reader(
+ CreateFileReader(test_path(), 0, test_file_modification_time()));
+ int result = 0;
+ std::string data;
+ ReadFromReader(reader.get(), &data, kTestDataSize, &result);
+ ASSERT_EQ(net::ERR_UPLOAD_FILE_CHANGED, result);
+ ASSERT_EQ(0U, data.size());
+
+ // With NULL expected modification time this should work.
+ data.clear();
+ reader.reset(CreateFileReader(test_path(), 0, base::Time()));
+ ReadFromReader(reader.get(), &data, kTestDataSize, &result);
+ ASSERT_EQ(net::OK, result);
+ ASSERT_EQ(kTestData, data);
+}
+
+TEST_F(LocalFileStreamReaderTest, ReadWithOffset) {
+ scoped_ptr<LocalFileStreamReader> reader(
+ CreateFileReader(test_path(), 3, base::Time()));
+ int result = 0;
+ std::string data;
+ ReadFromReader(reader.get(), &data, kTestDataSize, &result);
+ ASSERT_EQ(net::OK, result);
+ ASSERT_EQ(&kTestData[3], data);
+}
+
+TEST_F(LocalFileStreamReaderTest, DeleteWithUnfinishedRead) {
+ scoped_ptr<LocalFileStreamReader> reader(
+ CreateFileReader(test_path(), 0, base::Time()));
+
+ net::TestCompletionCallback callback;
+ scoped_refptr<net::IOBufferWithSize> buf(
+ new net::IOBufferWithSize(kTestDataSize));
+ int rv = reader->Read(buf.get(), buf->size(), base::Bind(&NeverCalled));
+ ASSERT_TRUE(rv == net::ERR_IO_PENDING || rv >= 0);
+
+ // Delete immediately.
+ // Should not crash; nor should NeverCalled be callback.
+ reader.reset();
+ EnsureFileTaskFinished();
+}
+
+} // namespace content
diff --git a/chromium/content/browser/fileapi/local_file_stream_writer_unittest.cc b/chromium/content/browser/fileapi/local_file_stream_writer_unittest.cc
new file mode 100644
index 00000000000..97e5e1ecc1a
--- /dev/null
+++ b/chromium/content/browser/fileapi/local_file_stream_writer_unittest.cc
@@ -0,0 +1,180 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/browser/fileapi/local_file_stream_writer.h"
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/threading/thread.h"
+#include "net/base/io_buffer.h"
+#include "net/base/test_completion_callback.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using fileapi::FileStreamWriter;
+using fileapi::LocalFileStreamWriter;
+
+namespace content {
+
+class LocalFileStreamWriterTest : public testing::Test {
+ public:
+ LocalFileStreamWriterTest()
+ : file_thread_("FileUtilProxyTestFileThread") {}
+
+ virtual void SetUp() OVERRIDE {
+ ASSERT_TRUE(file_thread_.Start());
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ }
+
+ virtual void TearDown() OVERRIDE {
+ // Give another chance for deleted streams to perform Close.
+ base::RunLoop().RunUntilIdle();
+ file_thread_.Stop();
+ base::RunLoop().RunUntilIdle();
+ }
+
+ protected:
+ base::FilePath Path(const std::string& name) {
+ return temp_dir_.path().AppendASCII(name);
+ }
+
+ int WriteStringToWriter(LocalFileStreamWriter* writer,
+ const std::string& data) {
+ scoped_refptr<net::StringIOBuffer> buffer(new net::StringIOBuffer(data));
+ scoped_refptr<net::DrainableIOBuffer> drainable(
+ new net::DrainableIOBuffer(buffer.get(), buffer->size()));
+
+ while (drainable->BytesRemaining() > 0) {
+ net::TestCompletionCallback callback;
+ int result = writer->Write(
+ drainable.get(), drainable->BytesRemaining(), callback.callback());
+ if (result == net::ERR_IO_PENDING)
+ result = callback.WaitForResult();
+ if (result <= 0)
+ return result;
+ drainable->DidConsume(result);
+ }
+ return net::OK;
+ }
+
+ std::string GetFileContent(const base::FilePath& path) {
+ std::string content;
+ base::ReadFileToString(path, &content);
+ return content;
+ }
+
+ base::FilePath CreateFileWithContent(const std::string& name,
+ const std::string& data) {
+ base::FilePath path = Path(name);
+ base::WriteFile(path, data.c_str(), data.size());
+ return path;
+ }
+
+ base::MessageLoopProxy* file_task_runner() const {
+ return file_thread_.message_loop_proxy().get();
+ }
+
+ LocalFileStreamWriter* CreateWriter(const base::FilePath& path,
+ int64 offset) {
+ return new LocalFileStreamWriter(file_task_runner(), path, offset,
+ FileStreamWriter::OPEN_EXISTING_FILE);
+ }
+
+ private:
+ base::MessageLoopForIO message_loop_;
+ base::Thread file_thread_;
+ base::ScopedTempDir temp_dir_;
+};
+
+void NeverCalled(int unused) {
+ ADD_FAILURE();
+}
+
+TEST_F(LocalFileStreamWriterTest, Write) {
+ base::FilePath path = CreateFileWithContent("file_a", std::string());
+ scoped_ptr<LocalFileStreamWriter> writer(CreateWriter(path, 0));
+ EXPECT_EQ(net::OK, WriteStringToWriter(writer.get(), "foo"));
+ EXPECT_EQ(net::OK, WriteStringToWriter(writer.get(), "bar"));
+ writer.reset();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(base::PathExists(path));
+ EXPECT_EQ("foobar", GetFileContent(path));
+}
+
+TEST_F(LocalFileStreamWriterTest, WriteMiddle) {
+ base::FilePath path = CreateFileWithContent("file_a", "foobar");
+ scoped_ptr<LocalFileStreamWriter> writer(CreateWriter(path, 2));
+ EXPECT_EQ(net::OK, WriteStringToWriter(writer.get(), "xxx"));
+ writer.reset();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(base::PathExists(path));
+ EXPECT_EQ("foxxxr", GetFileContent(path));
+}
+
+TEST_F(LocalFileStreamWriterTest, WriteEnd) {
+ base::FilePath path = CreateFileWithContent("file_a", "foobar");
+ scoped_ptr<LocalFileStreamWriter> writer(CreateWriter(path, 6));
+ EXPECT_EQ(net::OK, WriteStringToWriter(writer.get(), "xxx"));
+ writer.reset();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(base::PathExists(path));
+ EXPECT_EQ("foobarxxx", GetFileContent(path));
+}
+
+TEST_F(LocalFileStreamWriterTest, WriteFailForNonexistingFile) {
+ base::FilePath path = Path("file_a");
+ ASSERT_FALSE(base::PathExists(path));
+ scoped_ptr<LocalFileStreamWriter> writer(CreateWriter(path, 0));
+ EXPECT_EQ(net::ERR_FILE_NOT_FOUND, WriteStringToWriter(writer.get(), "foo"));
+ writer.reset();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(base::PathExists(path));
+}
+
+TEST_F(LocalFileStreamWriterTest, CancelBeforeOperation) {
+ base::FilePath path = Path("file_a");
+ scoped_ptr<LocalFileStreamWriter> writer(CreateWriter(path, 0));
+ // Cancel immediately fails when there's no in-flight operation.
+ int cancel_result = writer->Cancel(base::Bind(&NeverCalled));
+ EXPECT_EQ(net::ERR_UNEXPECTED, cancel_result);
+}
+
+TEST_F(LocalFileStreamWriterTest, CancelAfterFinishedOperation) {
+ base::FilePath path = CreateFileWithContent("file_a", std::string());
+ scoped_ptr<LocalFileStreamWriter> writer(CreateWriter(path, 0));
+ EXPECT_EQ(net::OK, WriteStringToWriter(writer.get(), "foo"));
+
+ // Cancel immediately fails when there's no in-flight operation.
+ int cancel_result = writer->Cancel(base::Bind(&NeverCalled));
+ EXPECT_EQ(net::ERR_UNEXPECTED, cancel_result);
+
+ writer.reset();
+ base::RunLoop().RunUntilIdle();
+ // Write operation is already completed.
+ EXPECT_TRUE(base::PathExists(path));
+ EXPECT_EQ("foo", GetFileContent(path));
+}
+
+TEST_F(LocalFileStreamWriterTest, CancelWrite) {
+ base::FilePath path = CreateFileWithContent("file_a", "foobar");
+ scoped_ptr<LocalFileStreamWriter> writer(CreateWriter(path, 0));
+
+ scoped_refptr<net::StringIOBuffer> buffer(new net::StringIOBuffer("xxx"));
+ int result =
+ writer->Write(buffer.get(), buffer->size(), base::Bind(&NeverCalled));
+ ASSERT_EQ(net::ERR_IO_PENDING, result);
+
+ net::TestCompletionCallback callback;
+ writer->Cancel(callback.callback());
+ int cancel_result = callback.WaitForResult();
+ EXPECT_EQ(net::OK, cancel_result);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/fileapi/local_file_util_unittest.cc b/chromium/content/browser/fileapi/local_file_util_unittest.cc
index 40995d69310..a20b18d3bff 100644
--- a/chromium/content/browser/fileapi/local_file_util_unittest.cc
+++ b/chromium/content/browser/fileapi/local_file_util_unittest.cc
@@ -5,16 +5,16 @@
#include <string>
#include "base/file_util.h"
+#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/message_loop/message_loop_proxy.h"
-#include "base/platform_file.h"
#include "base/run_loop.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
+#include "content/public/test/async_file_test_helper.h"
#include "content/public/test/test_file_system_context.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "webkit/browser/fileapi/async_file_test_helper.h"
#include "webkit/browser/fileapi/async_file_util_adapter.h"
#include "webkit/browser/fileapi/file_system_context.h"
#include "webkit/browser/fileapi/file_system_file_util.h"
@@ -23,12 +23,19 @@
#include "webkit/browser/fileapi/native_file_util.h"
#include "webkit/common/fileapi/file_system_types.h"
-namespace fileapi {
+using content::AsyncFileTestHelper;
+using fileapi::AsyncFileUtilAdapter;
+using fileapi::FileSystemContext;
+using fileapi::FileSystemOperationContext;
+using fileapi::FileSystemURL;
+using fileapi::LocalFileUtil;
+
+namespace content {
namespace {
const GURL kOrigin("http://foo/");
-const FileSystemType kFileSystemType = kFileSystemTypeTest;
+const fileapi::FileSystemType kFileSystemType = fileapi::kFileSystemTypeTest;
} // namespace
@@ -84,30 +91,25 @@ class LocalFileUtilTest : public testing::Test {
}
int64 GetSize(const char *file_name) {
- base::PlatformFileInfo info;
+ base::File::Info info;
base::GetFileInfo(LocalPath(file_name), &info);
return info.size;
}
- base::PlatformFileError CreateFile(const char* file_name,
- base::PlatformFile* file_handle,
- bool* created) {
- int file_flags = base::PLATFORM_FILE_CREATE |
- base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_ASYNC;
+ base::File CreateFile(const char* file_name) {
+ int file_flags = base::File::FLAG_CREATE |
+ base::File::FLAG_WRITE | base::File::FLAG_ASYNC;
scoped_ptr<FileSystemOperationContext> context(NewContext());
- return file_util()->CreateOrOpen(
- context.get(),
- CreateURL(file_name),
- file_flags, file_handle, created);
+ return file_util()->CreateOrOpen(context.get(), CreateURL(file_name),
+ file_flags);
}
- base::PlatformFileError EnsureFileExists(const char* file_name,
- bool* created) {
+ base::File::Error EnsureFileExists(const char* file_name,
+ bool* created) {
scoped_ptr<FileSystemOperationContext> context(NewContext());
- return file_util()->EnsureFileExists(
- context.get(),
- CreateURL(file_name), created);
+ return file_util()->EnsureFileExists(context.get(),
+ CreateURL(file_name), created);
}
FileSystemContext* file_system_context() {
@@ -124,18 +126,14 @@ class LocalFileUtilTest : public testing::Test {
TEST_F(LocalFileUtilTest, CreateAndClose) {
const char *file_name = "test_file";
- base::PlatformFile file_handle;
- bool created;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
- CreateFile(file_name, &file_handle, &created));
- ASSERT_TRUE(created);
+ base::File file = CreateFile(file_name);
+ ASSERT_TRUE(file.IsValid());
+ ASSERT_TRUE(file.created());
EXPECT_TRUE(FileExists(file_name));
EXPECT_EQ(0, GetSize(file_name));
scoped_ptr<FileSystemOperationContext> context(NewContext());
- EXPECT_EQ(base::PLATFORM_FILE_OK,
- file_util()->Close(context.get(), file_handle));
}
// base::CreateSymbolicLink is only supported on POSIX.
@@ -143,11 +141,9 @@ TEST_F(LocalFileUtilTest, CreateAndClose) {
TEST_F(LocalFileUtilTest, CreateFailForSymlink) {
// Create symlink target file.
const char *target_name = "symlink_target";
- base::PlatformFile target_handle;
- bool symlink_target_created = false;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
- CreateFile(target_name, &target_handle, &symlink_target_created));
- ASSERT_TRUE(symlink_target_created);
+ base::File target_file = CreateFile(target_name);
+ ASSERT_TRUE(target_file.IsValid());
+ ASSERT_TRUE(target_file.created());
base::FilePath target_path = LocalPath(target_name);
// Create symlink where target must be real file.
@@ -159,74 +155,67 @@ TEST_F(LocalFileUtilTest, CreateFailForSymlink) {
// Try to open the symlink file which should fail.
scoped_ptr<FileSystemOperationContext> context(NewContext());
FileSystemURL url = CreateURL(symlink_name);
- int file_flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ;
- base::PlatformFile file_handle;
- bool created = false;
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, file_util()->CreateOrOpen(
- context.get(), url, file_flags, &file_handle, &created));
- EXPECT_FALSE(created);
+ int file_flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
+ base::File file = file_util()->CreateOrOpen(context.get(), url, file_flags);
+ ASSERT_FALSE(file.IsValid());
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, file.error_details());
}
#endif
TEST_F(LocalFileUtilTest, EnsureFileExists) {
const char *file_name = "foobar";
bool created;
- ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(file_name, &created));
+ ASSERT_EQ(base::File::FILE_OK, EnsureFileExists(file_name, &created));
ASSERT_TRUE(created);
EXPECT_TRUE(FileExists(file_name));
EXPECT_EQ(0, GetSize(file_name));
- ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(file_name, &created));
+ ASSERT_EQ(base::File::FILE_OK, EnsureFileExists(file_name, &created));
EXPECT_FALSE(created);
}
TEST_F(LocalFileUtilTest, TouchFile) {
const char *file_name = "test_file";
- base::PlatformFile file_handle;
- bool created;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
- CreateFile(file_name, &file_handle, &created));
- ASSERT_TRUE(created);
+ base::File file = CreateFile(file_name);
+ ASSERT_TRUE(file.IsValid());
+ ASSERT_TRUE(file.created());
scoped_ptr<FileSystemOperationContext> context(NewContext());
- base::PlatformFileInfo info;
+ base::File::Info info;
ASSERT_TRUE(base::GetFileInfo(LocalPath(file_name), &info));
const base::Time new_accessed =
info.last_accessed + base::TimeDelta::FromHours(10);
const base::Time new_modified =
info.last_modified + base::TimeDelta::FromHours(5);
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
file_util()->Touch(context.get(), CreateURL(file_name),
new_accessed, new_modified));
ASSERT_TRUE(base::GetFileInfo(LocalPath(file_name), &info));
EXPECT_EQ(new_accessed, info.last_accessed);
EXPECT_EQ(new_modified, info.last_modified);
-
- EXPECT_EQ(base::PLATFORM_FILE_OK,
- file_util()->Close(context.get(), file_handle));
}
TEST_F(LocalFileUtilTest, TouchDirectory) {
const char *dir_name = "test_dir";
scoped_ptr<FileSystemOperationContext> context(NewContext());
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
file_util()->CreateDirectory(context.get(),
CreateURL(dir_name),
false /* exclusive */,
false /* recursive */));
- base::PlatformFileInfo info;
+ base::File::Info info;
ASSERT_TRUE(base::GetFileInfo(LocalPath(dir_name), &info));
const base::Time new_accessed =
info.last_accessed + base::TimeDelta::FromHours(10);
const base::Time new_modified =
info.last_modified + base::TimeDelta::FromHours(5);
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
file_util()->Touch(context.get(), CreateURL(dir_name),
new_accessed, new_modified));
@@ -238,14 +227,14 @@ TEST_F(LocalFileUtilTest, TouchDirectory) {
TEST_F(LocalFileUtilTest, Truncate) {
const char *file_name = "truncated";
bool created;
- ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(file_name, &created));
+ ASSERT_EQ(base::File::FILE_OK, EnsureFileExists(file_name, &created));
ASSERT_TRUE(created);
scoped_ptr<FileSystemOperationContext> context;
context.reset(NewContext());
- ASSERT_EQ(base::PLATFORM_FILE_OK,
- file_util()->Truncate(context.get(), CreateURL(file_name), 1020));
+ ASSERT_EQ(base::File::FILE_OK,
+ file_util()->Truncate(context.get(), CreateURL(file_name), 1020));
EXPECT_TRUE(FileExists(file_name));
EXPECT_EQ(1020, GetSize(file_name));
@@ -256,24 +245,24 @@ TEST_F(LocalFileUtilTest, CopyFile) {
const char *to_file1 = "tofile1";
const char *to_file2 = "tofile2";
bool created;
- ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(from_file, &created));
+ ASSERT_EQ(base::File::FILE_OK, EnsureFileExists(from_file, &created));
ASSERT_TRUE(created);
scoped_ptr<FileSystemOperationContext> context;
context.reset(NewContext());
- ASSERT_EQ(base::PLATFORM_FILE_OK,
- file_util()->Truncate(context.get(), CreateURL(from_file), 1020));
+ ASSERT_EQ(base::File::FILE_OK,
+ file_util()->Truncate(context.get(), CreateURL(from_file), 1020));
EXPECT_TRUE(FileExists(from_file));
EXPECT_EQ(1020, GetSize(from_file));
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::Copy(file_system_context(),
CreateURL(from_file),
CreateURL(to_file1)));
context.reset(NewContext());
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::Copy(file_system_context(),
CreateURL(from_file),
CreateURL(to_file2)));
@@ -295,15 +284,15 @@ TEST_F(LocalFileUtilTest, CopyDirectory) {
scoped_ptr<FileSystemOperationContext> context;
context.reset(NewContext());
- ASSERT_EQ(base::PLATFORM_FILE_OK,
- file_util()->CreateDirectory(context.get(), CreateURL(from_dir),
- false, false));
- ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(from_file, &created));
+ ASSERT_EQ(base::File::FILE_OK,
+ file_util()->CreateDirectory(context.get(), CreateURL(from_dir),
+ false, false));
+ ASSERT_EQ(base::File::FILE_OK, EnsureFileExists(from_file, &created));
ASSERT_TRUE(created);
context.reset(NewContext());
- ASSERT_EQ(base::PLATFORM_FILE_OK,
- file_util()->Truncate(context.get(), CreateURL(from_file), 1020));
+ ASSERT_EQ(base::File::FILE_OK,
+ file_util()->Truncate(context.get(), CreateURL(from_file), 1020));
EXPECT_TRUE(DirectoryExists(from_dir));
EXPECT_TRUE(FileExists(from_file));
@@ -311,7 +300,7 @@ TEST_F(LocalFileUtilTest, CopyDirectory) {
EXPECT_FALSE(DirectoryExists(to_dir));
context.reset(NewContext());
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::Copy(file_system_context(),
CreateURL(from_dir), CreateURL(to_dir)));
@@ -327,19 +316,19 @@ TEST_F(LocalFileUtilTest, MoveFile) {
const char *from_file = "fromfile";
const char *to_file = "tofile";
bool created;
- ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(from_file, &created));
+ ASSERT_EQ(base::File::FILE_OK, EnsureFileExists(from_file, &created));
ASSERT_TRUE(created);
scoped_ptr<FileSystemOperationContext> context;
context.reset(NewContext());
- ASSERT_EQ(base::PLATFORM_FILE_OK,
- file_util()->Truncate(context.get(), CreateURL(from_file), 1020));
+ ASSERT_EQ(base::File::FILE_OK,
+ file_util()->Truncate(context.get(), CreateURL(from_file), 1020));
EXPECT_TRUE(FileExists(from_file));
EXPECT_EQ(1020, GetSize(from_file));
context.reset(NewContext());
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::Move(file_system_context(),
CreateURL(from_file),
CreateURL(to_file)));
@@ -358,15 +347,15 @@ TEST_F(LocalFileUtilTest, MoveDirectory) {
scoped_ptr<FileSystemOperationContext> context;
context.reset(NewContext());
- ASSERT_EQ(base::PLATFORM_FILE_OK,
- file_util()->CreateDirectory(context.get(), CreateURL(from_dir),
- false, false));
- ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(from_file, &created));
+ ASSERT_EQ(base::File::FILE_OK,
+ file_util()->CreateDirectory(context.get(), CreateURL(from_dir),
+ false, false));
+ ASSERT_EQ(base::File::FILE_OK, EnsureFileExists(from_file, &created));
ASSERT_TRUE(created);
context.reset(NewContext());
- ASSERT_EQ(base::PLATFORM_FILE_OK,
- file_util()->Truncate(context.get(), CreateURL(from_file), 1020));
+ ASSERT_EQ(base::File::FILE_OK,
+ file_util()->Truncate(context.get(), CreateURL(from_file), 1020));
EXPECT_TRUE(DirectoryExists(from_dir));
EXPECT_TRUE(FileExists(from_file));
@@ -374,7 +363,7 @@ TEST_F(LocalFileUtilTest, MoveDirectory) {
EXPECT_FALSE(DirectoryExists(to_dir));
context.reset(NewContext());
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::Move(file_system_context(),
CreateURL(from_dir),
CreateURL(to_dir)));
@@ -385,4 +374,4 @@ TEST_F(LocalFileUtilTest, MoveDirectory) {
EXPECT_EQ(1020, GetSize(to_file));
}
-} // namespace fileapi
+} // namespace content
diff --git a/chromium/content/browser/fileapi/mock_file_change_observer.cc b/chromium/content/browser/fileapi/mock_file_change_observer.cc
new file mode 100644
index 00000000000..c37f175d953
--- /dev/null
+++ b/chromium/content/browser/fileapi/mock_file_change_observer.cc
@@ -0,0 +1,51 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/fileapi/mock_file_change_observer.h"
+
+namespace fileapi {
+
+MockFileChangeObserver::MockFileChangeObserver()
+ : create_file_count_(0),
+ create_file_from_count_(0),
+ remove_file_count_(0),
+ modify_file_count_(0),
+ create_directory_count_(0),
+ remove_directory_count_(0) {}
+
+MockFileChangeObserver::~MockFileChangeObserver() {}
+
+// static
+ChangeObserverList MockFileChangeObserver::CreateList(
+ MockFileChangeObserver* observer) {
+ ChangeObserverList list;
+ return list.AddObserver(observer, base::MessageLoopProxy::current().get());
+}
+
+void MockFileChangeObserver::OnCreateFile(const FileSystemURL& url) {
+ create_file_count_++;
+}
+
+void MockFileChangeObserver::OnCreateFileFrom(const FileSystemURL& url,
+ const FileSystemURL& src) {
+ create_file_from_count_++;
+}
+
+void MockFileChangeObserver::OnRemoveFile(const FileSystemURL& url) {
+ remove_file_count_++;
+}
+
+void MockFileChangeObserver::OnModifyFile(const FileSystemURL& url) {
+ modify_file_count_++;
+}
+
+void MockFileChangeObserver::OnCreateDirectory(const FileSystemURL& url) {
+ create_directory_count_++;
+}
+
+void MockFileChangeObserver::OnRemoveDirectory(const FileSystemURL& url) {
+ remove_directory_count_++;
+}
+
+} // namespace fileapi
diff --git a/chromium/content/browser/fileapi/mock_file_change_observer.h b/chromium/content/browser/fileapi/mock_file_change_observer.h
new file mode 100644
index 00000000000..4c1bcb2d1a4
--- /dev/null
+++ b/chromium/content/browser/fileapi/mock_file_change_observer.h
@@ -0,0 +1,103 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_BROWSER_FILEAPI_MOCK_FILE_CHANGE_OBSERVER_H_
+#define WEBKIT_BROWSER_FILEAPI_MOCK_FILE_CHANGE_OBSERVER_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "webkit/browser/fileapi/file_observers.h"
+#include "webkit/browser/fileapi/file_system_url.h"
+#include "webkit/browser/fileapi/task_runner_bound_observer_list.h"
+
+namespace fileapi {
+
+// Mock file change observer.
+class MockFileChangeObserver : public FileChangeObserver {
+ public:
+ MockFileChangeObserver();
+ virtual ~MockFileChangeObserver();
+
+ // Creates a ChangeObserverList which only contains given |observer|.
+ static ChangeObserverList CreateList(MockFileChangeObserver* observer);
+
+ // FileChangeObserver overrides.
+ virtual void OnCreateFile(const FileSystemURL& url) OVERRIDE;
+ virtual void OnCreateFileFrom(const FileSystemURL& url,
+ const FileSystemURL& src) OVERRIDE;
+ virtual void OnRemoveFile(const FileSystemURL& url) OVERRIDE;
+ virtual void OnModifyFile(const FileSystemURL& url) OVERRIDE;
+ virtual void OnCreateDirectory(const FileSystemURL& url) OVERRIDE;
+ virtual void OnRemoveDirectory(const FileSystemURL& url) OVERRIDE;
+
+ void ResetCount() {
+ create_file_count_ = 0;
+ create_file_from_count_ = 0;
+ remove_file_count_ = 0;
+ modify_file_count_ = 0;
+ create_directory_count_ = 0;
+ remove_directory_count_ = 0;
+ }
+
+ bool HasNoChange() const {
+ return create_file_count_ == 0 &&
+ create_file_from_count_ == 0 &&
+ remove_file_count_ == 0 &&
+ modify_file_count_ == 0 &&
+ create_directory_count_ == 0 &&
+ remove_directory_count_ == 0;
+ }
+
+ int create_file_count() const { return create_file_count_; }
+ int create_file_from_count() const { return create_file_from_count_; }
+ int remove_file_count() const { return remove_file_count_; }
+ int modify_file_count() const { return modify_file_count_; }
+ int create_directory_count() const { return create_directory_count_; }
+ int remove_directory_count() const { return remove_directory_count_; }
+
+ int get_and_reset_create_file_count() {
+ int tmp = create_file_count_;
+ create_file_count_ = 0;
+ return tmp;
+ }
+ int get_and_reset_create_file_from_count() {
+ int tmp = create_file_from_count_;
+ create_file_from_count_ = 0;
+ return tmp;
+ }
+ int get_and_reset_remove_file_count() {
+ int tmp = remove_file_count_;
+ remove_file_count_ = 0;
+ return tmp;
+ }
+ int get_and_reset_modify_file_count() {
+ int tmp = modify_file_count_;
+ modify_file_count_ = 0;
+ return tmp;
+ }
+ int get_and_reset_create_directory_count() {
+ int tmp = create_directory_count_;
+ create_directory_count_ = 0;
+ return tmp;
+ }
+ int get_and_reset_remove_directory_count() {
+ int tmp = remove_directory_count_;
+ remove_directory_count_ = 0;
+ return tmp;
+ }
+
+ private:
+ int create_file_count_;
+ int create_file_from_count_;
+ int remove_file_count_;
+ int modify_file_count_;
+ int create_directory_count_;
+ int remove_directory_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockFileChangeObserver);
+};
+
+} // namespace fileapi
+
+#endif // WEBKIT_BROWSER_FILEAPI_MOCK_FILE_CHANGE_OBSERVER_H_
diff --git a/chromium/content/browser/fileapi/mock_url_request_delegate.cc b/chromium/content/browser/fileapi/mock_url_request_delegate.cc
new file mode 100644
index 00000000000..97e09f0df6f
--- /dev/null
+++ b/chromium/content/browser/fileapi/mock_url_request_delegate.cc
@@ -0,0 +1,72 @@
+// 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 "mock_url_request_delegate.h"
+
+#include "base/run_loop.h"
+#include "net/base/io_buffer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+const int kBufferSize = 1024;
+}
+
+namespace content {
+
+MockURLRequestDelegate::MockURLRequestDelegate()
+ : io_buffer_(new net::IOBuffer(kBufferSize)) {
+}
+
+MockURLRequestDelegate::~MockURLRequestDelegate() {
+}
+
+void MockURLRequestDelegate::OnResponseStarted(net::URLRequest* request) {
+ if (request->status().is_success()) {
+ EXPECT_TRUE(request->response_headers());
+ ReadSome(request);
+ } else {
+ RequestComplete();
+ }
+}
+
+void MockURLRequestDelegate::OnReadCompleted(net::URLRequest* request,
+ int bytes_read) {
+ if (bytes_read > 0)
+ ReceiveData(request, bytes_read);
+ else
+ RequestComplete();
+}
+
+void MockURLRequestDelegate::ReadSome(net::URLRequest* request) {
+ if (!request->is_pending()) {
+ RequestComplete();
+ return;
+ }
+
+ int bytes_read = 0;
+ if (!request->Read(io_buffer_.get(), kBufferSize, &bytes_read)) {
+ if (!request->status().is_io_pending())
+ RequestComplete();
+ return;
+ }
+
+ ReceiveData(request, bytes_read);
+}
+
+void MockURLRequestDelegate::ReceiveData(net::URLRequest* request,
+ int bytes_read) {
+ if (bytes_read) {
+ response_data_.append(io_buffer_->data(),
+ static_cast<size_t>(bytes_read));
+ ReadSome(request);
+ } else {
+ RequestComplete();
+ }
+}
+
+void MockURLRequestDelegate::RequestComplete() {
+ base::MessageLoop::current()->Quit();
+}
+
+} // namespace
diff --git a/chromium/content/browser/fileapi/mock_url_request_delegate.h b/chromium/content/browser/fileapi/mock_url_request_delegate.h
new file mode 100644
index 00000000000..137b11426e8
--- /dev/null
+++ b/chromium/content/browser/fileapi/mock_url_request_delegate.h
@@ -0,0 +1,38 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_FILEAPI_MOCK_URL_REQUEST_DELEGATE_H
+#define CONTENT_BROWSER_FILEAPI_MOCK_URL_REQUEST_DELEGATE_H
+
+#include "net/url_request/url_request.h"
+
+namespace net {
+class IOBuffer;
+}
+
+namespace content {
+
+// A URL request delegate that receives the response body.
+class MockURLRequestDelegate : public net::URLRequest::Delegate {
+ public:
+ MockURLRequestDelegate();
+ virtual ~MockURLRequestDelegate();
+
+ virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE;
+ virtual void OnReadCompleted(net::URLRequest* request,
+ int bytes_read) OVERRIDE;
+ const std::string& response_data() const { return response_data_; }
+
+ private:
+ void ReadSome(net::URLRequest* request);
+ void ReceiveData(net::URLRequest* request, int bytes_read);
+ void RequestComplete();
+
+ scoped_refptr<net::IOBuffer> io_buffer_;
+ std::string response_data_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_FILEAPI_MOCK_URL_REQUEST_DELEGATE_H
diff --git a/chromium/content/browser/fileapi/native_file_util_unittest.cc b/chromium/content/browser/fileapi/native_file_util_unittest.cc
new file mode 100644
index 00000000000..e20914d6610
--- /dev/null
+++ b/chromium/content/browser/fileapi/native_file_util_unittest.cc
@@ -0,0 +1,409 @@
+// 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 <set>
+
+#include "base/file_util.h"
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/fileapi/native_file_util.h"
+
+using fileapi::FileSystemFileUtil;
+using fileapi::FileSystemOperation;
+using fileapi::NativeFileUtil;
+
+namespace content {
+
+class NativeFileUtilTest : public testing::Test {
+ public:
+ NativeFileUtilTest() {}
+
+ virtual void SetUp() {
+ ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
+ }
+
+ protected:
+ base::FilePath Path() {
+ return data_dir_.path();
+ }
+
+ base::FilePath Path(const char* file_name) {
+ return data_dir_.path().AppendASCII(file_name);
+ }
+
+ bool FileExists(const base::FilePath& path) {
+ return base::PathExists(path) &&
+ !base::DirectoryExists(path);
+ }
+
+ int64 GetSize(const base::FilePath& path) {
+ base::File::Info info;
+ base::GetFileInfo(path, &info);
+ return info.size;
+ }
+
+ private:
+ base::ScopedTempDir data_dir_;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeFileUtilTest);
+};
+
+TEST_F(NativeFileUtilTest, CreateCloseAndDeleteFile) {
+ base::FilePath file_name = Path("test_file");
+ int flags = base::File::FLAG_WRITE | base::File::FLAG_ASYNC;
+ base::File file =
+ NativeFileUtil::CreateOrOpen(file_name, base::File::FLAG_CREATE | flags);
+ ASSERT_TRUE(file.IsValid());
+ ASSERT_TRUE(file.created());
+
+ EXPECT_TRUE(base::PathExists(file_name));
+ EXPECT_TRUE(NativeFileUtil::PathExists(file_name));
+ EXPECT_EQ(0, GetSize(file_name));
+ file.Close();
+
+ file = NativeFileUtil::CreateOrOpen(file_name, base::File::FLAG_OPEN | flags);
+ ASSERT_TRUE(file.IsValid());
+ ASSERT_FALSE(file.created());
+ file.Close();
+
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::DeleteFile(file_name));
+ EXPECT_FALSE(base::PathExists(file_name));
+ EXPECT_FALSE(NativeFileUtil::PathExists(file_name));
+}
+
+TEST_F(NativeFileUtilTest, EnsureFileExists) {
+ base::FilePath file_name = Path("foobar");
+ bool created = false;
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::EnsureFileExists(file_name, &created));
+ ASSERT_TRUE(created);
+
+ EXPECT_TRUE(FileExists(file_name));
+ EXPECT_EQ(0, GetSize(file_name));
+
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::EnsureFileExists(file_name, &created));
+ EXPECT_FALSE(created);
+}
+
+TEST_F(NativeFileUtilTest, CreateAndDeleteDirectory) {
+ base::FilePath dir_name = Path("test_dir");
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::CreateDirectory(dir_name,
+ false /* exclusive */,
+ false /* recursive */));
+
+ EXPECT_TRUE(NativeFileUtil::DirectoryExists(dir_name));
+ EXPECT_TRUE(base::DirectoryExists(dir_name));
+
+ ASSERT_EQ(base::File::FILE_ERROR_EXISTS,
+ NativeFileUtil::CreateDirectory(dir_name,
+ true /* exclusive */,
+ false /* recursive */));
+
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::DeleteDirectory(dir_name));
+ EXPECT_FALSE(base::DirectoryExists(dir_name));
+ EXPECT_FALSE(NativeFileUtil::DirectoryExists(dir_name));
+}
+
+TEST_F(NativeFileUtilTest, TouchFileAndGetFileInfo) {
+ base::FilePath file_name = Path("test_file");
+ base::File::Info native_info;
+ ASSERT_EQ(base::File::FILE_ERROR_NOT_FOUND,
+ NativeFileUtil::GetFileInfo(file_name, &native_info));
+
+ bool created = false;
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::EnsureFileExists(file_name, &created));
+ ASSERT_TRUE(created);
+
+ base::File::Info info;
+ ASSERT_TRUE(base::GetFileInfo(file_name, &info));
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::GetFileInfo(file_name, &native_info));
+ ASSERT_EQ(info.size, native_info.size);
+ ASSERT_EQ(info.is_directory, native_info.is_directory);
+ ASSERT_EQ(info.is_symbolic_link, native_info.is_symbolic_link);
+ ASSERT_EQ(info.last_modified, native_info.last_modified);
+ ASSERT_EQ(info.last_accessed, native_info.last_accessed);
+ ASSERT_EQ(info.creation_time, native_info.creation_time);
+
+ const base::Time new_accessed =
+ info.last_accessed + base::TimeDelta::FromHours(10);
+ const base::Time new_modified =
+ info.last_modified + base::TimeDelta::FromHours(5);
+
+ EXPECT_EQ(base::File::FILE_OK,
+ NativeFileUtil::Touch(file_name,
+ new_accessed, new_modified));
+
+ ASSERT_TRUE(base::GetFileInfo(file_name, &info));
+ EXPECT_EQ(new_accessed, info.last_accessed);
+ EXPECT_EQ(new_modified, info.last_modified);
+}
+
+TEST_F(NativeFileUtilTest, CreateFileEnumerator) {
+ base::FilePath path_1 = Path("dir1");
+ base::FilePath path_2 = Path("file1");
+ base::FilePath path_11 = Path("dir1").AppendASCII("file11");
+ base::FilePath path_12 = Path("dir1").AppendASCII("dir12");
+ base::FilePath path_121 =
+ Path("dir1").AppendASCII("dir12").AppendASCII("file121");
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::CreateDirectory(path_1, false, false));
+ bool created = false;
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::EnsureFileExists(path_2, &created));
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::EnsureFileExists(path_11, &created));
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::CreateDirectory(path_12, false, false));
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::EnsureFileExists(path_121, &created));
+
+ {
+ scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> enumerator =
+ NativeFileUtil::CreateFileEnumerator(Path(), false);
+ std::set<base::FilePath> set;
+ set.insert(path_1);
+ set.insert(path_2);
+ for (base::FilePath path = enumerator->Next(); !path.empty();
+ path = enumerator->Next())
+ EXPECT_EQ(1U, set.erase(path));
+ EXPECT_TRUE(set.empty());
+ }
+
+ {
+ scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> enumerator =
+ NativeFileUtil::CreateFileEnumerator(Path(), true);
+ std::set<base::FilePath> set;
+ set.insert(path_1);
+ set.insert(path_2);
+ set.insert(path_11);
+ set.insert(path_12);
+ set.insert(path_121);
+ for (base::FilePath path = enumerator->Next(); !path.empty();
+ path = enumerator->Next())
+ EXPECT_EQ(1U, set.erase(path));
+ EXPECT_TRUE(set.empty());
+ }
+}
+
+TEST_F(NativeFileUtilTest, Truncate) {
+ base::FilePath file_name = Path("truncated");
+ bool created = false;
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::EnsureFileExists(file_name, &created));
+ ASSERT_TRUE(created);
+
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::Truncate(file_name, 1020));
+
+ EXPECT_TRUE(FileExists(file_name));
+ EXPECT_EQ(1020, GetSize(file_name));
+}
+
+TEST_F(NativeFileUtilTest, CopyFile) {
+ base::FilePath from_file = Path("fromfile");
+ base::FilePath to_file1 = Path("tofile1");
+ base::FilePath to_file2 = Path("tofile2");
+ const NativeFileUtil::CopyOrMoveMode nosync = NativeFileUtil::COPY_NOSYNC;
+ const NativeFileUtil::CopyOrMoveMode sync = NativeFileUtil::COPY_SYNC;
+ bool created = false;
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::EnsureFileExists(from_file, &created));
+ ASSERT_TRUE(created);
+
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::Truncate(from_file, 1020));
+
+ EXPECT_TRUE(FileExists(from_file));
+ EXPECT_EQ(1020, GetSize(from_file));
+
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::CopyOrMoveFile(
+ from_file, to_file1, FileSystemOperation::OPTION_NONE, nosync));
+
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::CopyOrMoveFile(
+ from_file, to_file2, FileSystemOperation::OPTION_NONE, sync));
+
+ EXPECT_TRUE(FileExists(from_file));
+ EXPECT_EQ(1020, GetSize(from_file));
+ EXPECT_TRUE(FileExists(to_file1));
+ EXPECT_EQ(1020, GetSize(to_file1));
+ EXPECT_TRUE(FileExists(to_file2));
+ EXPECT_EQ(1020, GetSize(to_file2));
+
+ base::FilePath dir = Path("dir");
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::CreateDirectory(dir, false, false));
+ ASSERT_TRUE(base::DirectoryExists(dir));
+ base::FilePath to_dir_file = dir.AppendASCII("file");
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::CopyOrMoveFile(
+ from_file, to_dir_file,
+ FileSystemOperation::OPTION_NONE, nosync));
+ EXPECT_TRUE(FileExists(to_dir_file));
+ EXPECT_EQ(1020, GetSize(to_dir_file));
+
+ // Following tests are error checking.
+ // Source doesn't exist.
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
+ NativeFileUtil::CopyOrMoveFile(
+ Path("nonexists"), Path("file"),
+ FileSystemOperation::OPTION_NONE, nosync));
+
+ // Source is not a file.
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_A_FILE,
+ NativeFileUtil::CopyOrMoveFile(
+ dir, Path("file"), FileSystemOperation::OPTION_NONE, nosync));
+ // Destination is not a file.
+ EXPECT_EQ(base::File::FILE_ERROR_INVALID_OPERATION,
+ NativeFileUtil::CopyOrMoveFile(
+ from_file, dir, FileSystemOperation::OPTION_NONE, nosync));
+ // Destination's parent doesn't exist.
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
+ NativeFileUtil::CopyOrMoveFile(
+ from_file, Path("nodir").AppendASCII("file"),
+ FileSystemOperation::OPTION_NONE, nosync));
+ // Destination's parent is a file.
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
+ NativeFileUtil::CopyOrMoveFile(
+ from_file, Path("tofile1").AppendASCII("file"),
+ FileSystemOperation::OPTION_NONE, nosync));
+}
+
+TEST_F(NativeFileUtilTest, MoveFile) {
+ base::FilePath from_file = Path("fromfile");
+ base::FilePath to_file = Path("tofile");
+ const NativeFileUtil::CopyOrMoveMode move = NativeFileUtil::MOVE;
+ bool created = false;
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::EnsureFileExists(from_file, &created));
+ ASSERT_TRUE(created);
+
+ ASSERT_EQ(base::File::FILE_OK, NativeFileUtil::Truncate(from_file, 1020));
+
+ EXPECT_TRUE(FileExists(from_file));
+ EXPECT_EQ(1020, GetSize(from_file));
+
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::CopyOrMoveFile(
+ from_file, to_file, FileSystemOperation::OPTION_NONE, move));
+
+ EXPECT_FALSE(FileExists(from_file));
+ EXPECT_TRUE(FileExists(to_file));
+ EXPECT_EQ(1020, GetSize(to_file));
+
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::EnsureFileExists(from_file, &created));
+ ASSERT_TRUE(FileExists(from_file));
+ ASSERT_EQ(base::File::FILE_OK, NativeFileUtil::Truncate(from_file, 1020));
+
+ base::FilePath dir = Path("dir");
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::CreateDirectory(dir, false, false));
+ ASSERT_TRUE(base::DirectoryExists(dir));
+ base::FilePath to_dir_file = dir.AppendASCII("file");
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::CopyOrMoveFile(
+ from_file, to_dir_file,
+ FileSystemOperation::OPTION_NONE, move));
+ EXPECT_FALSE(FileExists(from_file));
+ EXPECT_TRUE(FileExists(to_dir_file));
+ EXPECT_EQ(1020, GetSize(to_dir_file));
+
+ // Following is error checking.
+ // Source doesn't exist.
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
+ NativeFileUtil::CopyOrMoveFile(
+ Path("nonexists"), Path("file"),
+ FileSystemOperation::OPTION_NONE, move));
+
+ // Source is not a file.
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_A_FILE,
+ NativeFileUtil::CopyOrMoveFile(
+ dir, Path("file"), FileSystemOperation::OPTION_NONE, move));
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::EnsureFileExists(from_file, &created));
+ ASSERT_TRUE(FileExists(from_file));
+ // Destination is not a file.
+ EXPECT_EQ(base::File::FILE_ERROR_INVALID_OPERATION,
+ NativeFileUtil::CopyOrMoveFile(
+ from_file, dir, FileSystemOperation::OPTION_NONE, move));
+
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::EnsureFileExists(from_file, &created));
+ ASSERT_TRUE(FileExists(from_file));
+ // Destination's parent doesn't exist.
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
+ NativeFileUtil::CopyOrMoveFile(
+ from_file, Path("nodir").AppendASCII("file"),
+ FileSystemOperation::OPTION_NONE, move));
+ // Destination's parent is a file.
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
+ NativeFileUtil::CopyOrMoveFile(
+ from_file, Path("tofile1").AppendASCII("file"),
+ FileSystemOperation::OPTION_NONE, move));
+}
+
+TEST_F(NativeFileUtilTest, PreserveLastModified) {
+ base::FilePath from_file = Path("fromfile");
+ base::FilePath to_file1 = Path("tofile1");
+ base::FilePath to_file2 = Path("tofile2");
+ base::FilePath to_file3 = Path("tofile3");
+ bool created = false;
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::EnsureFileExists(from_file, &created));
+ ASSERT_TRUE(created);
+ EXPECT_TRUE(FileExists(from_file));
+
+ base::File::Info file_info1;
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::GetFileInfo(from_file, &file_info1));
+
+ // Test for copy (nosync).
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::CopyOrMoveFile(
+ from_file, to_file1,
+ FileSystemOperation::OPTION_PRESERVE_LAST_MODIFIED,
+ NativeFileUtil::COPY_NOSYNC));
+
+ base::File::Info file_info2;
+ ASSERT_TRUE(FileExists(to_file1));
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::GetFileInfo(to_file1, &file_info2));
+ EXPECT_EQ(file_info1.last_modified, file_info2.last_modified);
+
+ // Test for copy (sync).
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::CopyOrMoveFile(
+ from_file, to_file2,
+ FileSystemOperation::OPTION_PRESERVE_LAST_MODIFIED,
+ NativeFileUtil::COPY_SYNC));
+
+ ASSERT_TRUE(FileExists(to_file2));
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::GetFileInfo(to_file1, &file_info2));
+ EXPECT_EQ(file_info1.last_modified, file_info2.last_modified);
+
+ // Test for move.
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::CopyOrMoveFile(
+ from_file, to_file3,
+ FileSystemOperation::OPTION_PRESERVE_LAST_MODIFIED,
+ NativeFileUtil::MOVE));
+
+ ASSERT_TRUE(FileExists(to_file3));
+ ASSERT_EQ(base::File::FILE_OK,
+ NativeFileUtil::GetFileInfo(to_file2, &file_info2));
+ EXPECT_EQ(file_info1.last_modified, file_info2.last_modified);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/fileapi/obfuscated_file_util_unittest.cc b/chromium/content/browser/fileapi/obfuscated_file_util_unittest.cc
index 72dd36008dd..089cef8d73d 100644
--- a/chromium/content/browser/fileapi/obfuscated_file_util_unittest.cc
+++ b/chromium/content/browser/fileapi/obfuscated_file_util_unittest.cc
@@ -8,33 +8,44 @@
#include "base/bind.h"
#include "base/file_util.h"
+#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/memory/scoped_ptr.h"
-#include "base/platform_file.h"
#include "base/run_loop.h"
+#include "content/browser/fileapi/mock_file_change_observer.h"
+#include "content/public/test/async_file_test_helper.h"
+#include "content/public/test/mock_special_storage_policy.h"
#include "content/public/test/sandbox_file_system_test_helper.h"
#include "content/public/test/test_file_system_context.h"
+#include "content/test/fileapi_test_file_set.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "webkit/browser/fileapi/async_file_test_helper.h"
#include "webkit/browser/fileapi/external_mount_points.h"
#include "webkit/browser/fileapi/file_system_backend.h"
#include "webkit/browser/fileapi/file_system_context.h"
#include "webkit/browser/fileapi/file_system_operation_context.h"
#include "webkit/browser/fileapi/file_system_usage_cache.h"
-#include "webkit/browser/fileapi/mock_file_change_observer.h"
#include "webkit/browser/fileapi/obfuscated_file_util.h"
#include "webkit/browser/fileapi/sandbox_directory_database.h"
#include "webkit/browser/fileapi/sandbox_file_system_backend_delegate.h"
#include "webkit/browser/fileapi/sandbox_isolated_origin_database.h"
#include "webkit/browser/fileapi/sandbox_origin_database.h"
-#include "webkit/browser/fileapi/test_file_set.h"
-#include "webkit/browser/quota/mock_special_storage_policy.h"
#include "webkit/browser/quota/quota_manager.h"
#include "webkit/common/database/database_identifier.h"
#include "webkit/common/quota/quota_types.h"
-namespace fileapi {
+using content::AsyncFileTestHelper;
+using fileapi::FileSystemContext;
+using fileapi::FileSystemOperation;
+using fileapi::FileSystemOperationContext;
+using fileapi::FileSystemURL;
+using fileapi::ObfuscatedFileUtil;
+using fileapi::SandboxDirectoryDatabase;
+using fileapi::SandboxIsolatedOriginDatabase;
+using fileapi::kFileSystemTypeTemporary;
+using fileapi::kFileSystemTypePersistent;
+
+namespace content {
namespace {
@@ -110,16 +121,16 @@ FileSystemURL FileSystemURLAppendUTF8(
FileSystemURL FileSystemURLDirName(const FileSystemURL& url) {
return FileSystemURL::CreateForTest(
- url.origin(), url.mount_type(), VirtualPath::DirName(url.virtual_path()));
+ url.origin(), url.mount_type(),
+ fileapi::VirtualPath::DirName(url.virtual_path()));
}
-std::string GetTypeString(FileSystemType type) {
- return SandboxFileSystemBackendDelegate::GetTypeString(type);
+std::string GetTypeString(fileapi::FileSystemType type) {
+ return fileapi::SandboxFileSystemBackendDelegate::GetTypeString(type);
}
-bool HasFileSystemType(
- ObfuscatedFileUtil::AbstractOriginEnumerator* enumerator,
- FileSystemType type) {
+bool HasFileSystemType(ObfuscatedFileUtil::AbstractOriginEnumerator* enumerator,
+ fileapi::FileSystemType type) {
return enumerator->HasTypeDirectory(GetTypeString(type));
}
@@ -133,7 +144,7 @@ class ObfuscatedFileUtilTest : public testing::Test {
public:
ObfuscatedFileUtilTest()
: origin_(GURL("http://www.example.com")),
- type_(kFileSystemTypeTemporary),
+ type_(fileapi::kFileSystemTypeTemporary),
weak_factory_(this),
sandbox_file_system_(origin_, type_),
quota_status_(quota::kQuotaStatusUnknown),
@@ -143,7 +154,7 @@ class ObfuscatedFileUtilTest : public testing::Test {
virtual void SetUp() {
ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
- storage_policy_ = new quota::MockSpecialStoragePolicy();
+ storage_policy_ = new MockSpecialStoragePolicy();
quota_manager_ =
new quota::QuotaManager(false /* is_incognito */,
@@ -162,7 +173,8 @@ class ObfuscatedFileUtilTest : public testing::Test {
sandbox_file_system_.SetUp(file_system_context_.get());
- change_observers_ = MockFileChangeObserver::CreateList(&change_observer_);
+ change_observers_ = fileapi::MockFileChangeObserver::CreateList(
+ &change_observer_);
}
virtual void TearDown() {
@@ -196,11 +208,11 @@ class ObfuscatedFileUtilTest : public testing::Test {
return context;
}
- const ChangeObserverList& change_observers() const {
+ const fileapi::ChangeObserverList& change_observers() const {
return change_observers_;
}
- MockFileChangeObserver* change_observer() {
+ fileapi::MockFileChangeObserver* change_observer() {
return &change_observer_;
}
@@ -217,6 +229,14 @@ class ObfuscatedFileUtilTest : public testing::Test {
return file_system;
}
+ scoped_ptr<ObfuscatedFileUtil> CreateObfuscatedFileUtil(
+ quota::SpecialStoragePolicy* storage_policy) {
+ return scoped_ptr<ObfuscatedFileUtil>(
+ ObfuscatedFileUtil::CreateForTesting(
+ storage_policy, data_dir_path(), NULL,
+ base::MessageLoopProxy::current().get()));
+ }
+
ObfuscatedFileUtil* ofu() {
return static_cast<ObfuscatedFileUtil*>(sandbox_file_system_.file_util());
}
@@ -271,11 +291,11 @@ class ObfuscatedFileUtilTest : public testing::Test {
bool PathExists(const FileSystemURL& url) {
scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
- base::PlatformFileInfo file_info;
+ base::File::Info file_info;
base::FilePath platform_path;
- base::PlatformFileError error = ofu()->GetFileInfo(
+ base::File::Error error = ofu()->GetFileInfo(
context.get(), url, &file_info, &platform_path);
- return error == base::PLATFORM_FILE_OK;
+ return error == base::File::FILE_OK;
}
bool DirectoryExists(const FileSystemURL& url) {
@@ -283,7 +303,7 @@ class ObfuscatedFileUtilTest : public testing::Test {
}
int64 usage() const { return usage_; }
- FileSystemUsageCache* usage_cache() {
+ fileapi::FileSystemUsageCache* usage_cache() {
return sandbox_file_system_.usage_cache();
}
@@ -299,17 +319,16 @@ class ObfuscatedFileUtilTest : public testing::Test {
return sandbox_file_system_.CreateURL(path);
}
- void CheckFileAndCloseHandle(
- const FileSystemURL& url, base::PlatformFile file_handle) {
+ void CheckFileAndCloseHandle(const FileSystemURL& url, base::File file) {
scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
base::FilePath local_path;
- EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetLocalFilePath(
- context.get(), url, &local_path));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->GetLocalFilePath(context.get(), url, &local_path));
- base::PlatformFileInfo file_info0;
+ base::File::Info file_info0;
base::FilePath data_path;
- EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetFileInfo(
- context.get(), url, &file_info0, &data_path));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->GetFileInfo(context.get(), url, &file_info0, &data_path));
EXPECT_EQ(data_path, local_path);
EXPECT_TRUE(FileExists(data_path));
EXPECT_EQ(0, GetSize(data_path));
@@ -317,26 +336,20 @@ class ObfuscatedFileUtilTest : public testing::Test {
const char data[] = "test data";
const int length = arraysize(data) - 1;
- if (base::kInvalidPlatformFileValue == file_handle) {
- bool created = true;
- base::PlatformFileError error;
- file_handle = base::CreatePlatformFile(
- data_path,
- base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE,
- &created,
- &error);
- ASSERT_NE(base::kInvalidPlatformFileValue, file_handle);
- ASSERT_EQ(base::PLATFORM_FILE_OK, error);
- EXPECT_FALSE(created);
+ if (!file.IsValid()) {
+ file.Initialize(data_path,
+ base::File::FLAG_OPEN | base::File::FLAG_WRITE);
+ ASSERT_TRUE(file.IsValid());
+ EXPECT_FALSE(file.created());
}
- ASSERT_EQ(length, base::WritePlatformFile(file_handle, 0, data, length));
- EXPECT_TRUE(base::ClosePlatformFile(file_handle));
+ ASSERT_EQ(length, file.Write(0, data, length));
+ file.Close();
- base::PlatformFileInfo file_info1;
+ base::File::Info file_info1;
EXPECT_EQ(length, GetSize(data_path));
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetFileInfo(
- context.get(), url, &file_info1, &data_path));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->GetFileInfo(context.get(), url, &file_info1, &data_path));
EXPECT_EQ(data_path, local_path);
EXPECT_FALSE(file_info0.is_directory);
@@ -348,13 +361,13 @@ class ObfuscatedFileUtilTest : public testing::Test {
EXPECT_LE(file_info0.last_modified, file_info1.last_modified);
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->Truncate(
- context.get(), url, length * 2));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->Truncate(context.get(), url, length * 2));
EXPECT_EQ(length * 2, GetSize(data_path));
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->Truncate(
- context.get(), url, 0));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->Truncate(context.get(), url, 0));
EXPECT_EQ(0, GetSize(data_path));
}
@@ -367,10 +380,10 @@ class ObfuscatedFileUtilTest : public testing::Test {
for (iter = files.begin(); iter != files.end(); ++iter) {
bool created = true;
context.reset(NewContext(NULL));
- ASSERT_EQ(base::PLATFORM_FILE_OK,
- ofu()->EnsureFileExists(
- context.get(), FileSystemURLAppend(root_url, *iter),
- &created));
+ ASSERT_EQ(base::File::FILE_OK,
+ ofu()->EnsureFileExists(context.get(),
+ FileSystemURLAppend(root_url, *iter),
+ &created));
ASSERT_FALSE(created);
}
for (iter = directories.begin(); iter != directories.end(); ++iter) {
@@ -427,10 +440,10 @@ class ObfuscatedFileUtilTest : public testing::Test {
std::set<base::FilePath::StringType>* files,
std::set<base::FilePath::StringType>* directories) {
scoped_ptr<FileSystemOperationContext> context;
- std::vector<DirectoryEntry> entries;
- EXPECT_EQ(base::PLATFORM_FILE_OK,
- AsyncFileTestHelper::ReadDirectory(
- file_system_context(), root_url, &entries));
+ std::vector<fileapi::DirectoryEntry> entries;
+ EXPECT_EQ(base::File::FILE_OK,
+ AsyncFileTestHelper::ReadDirectory(file_system_context(),
+ root_url, &entries));
EXPECT_EQ(0UL, entries.size());
files->clear();
@@ -445,22 +458,20 @@ class ObfuscatedFileUtilTest : public testing::Test {
for (iter = files->begin(); iter != files->end(); ++iter) {
bool created = false;
context.reset(NewContext(NULL));
- ASSERT_EQ(base::PLATFORM_FILE_OK,
- ofu()->EnsureFileExists(
- context.get(),
- FileSystemURLAppend(root_url, *iter),
- &created));
+ ASSERT_EQ(base::File::FILE_OK,
+ ofu()->EnsureFileExists(context.get(),
+ FileSystemURLAppend(root_url, *iter),
+ &created));
ASSERT_TRUE(created);
}
for (iter = directories->begin(); iter != directories->end(); ++iter) {
bool exclusive = true;
bool recursive = false;
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
- ofu()->CreateDirectory(
- context.get(),
- FileSystemURLAppend(root_url, *iter),
- exclusive, recursive));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->CreateDirectory(context.get(),
+ FileSystemURLAppend(root_url, *iter),
+ exclusive, recursive));
}
ValidateTestDirectory(root_url, *files, *directories);
}
@@ -471,17 +482,17 @@ class ObfuscatedFileUtilTest : public testing::Test {
FillTestDirectory(root_url, &files, &directories);
scoped_ptr<FileSystemOperationContext> context;
- std::vector<DirectoryEntry> entries;
+ std::vector<fileapi::DirectoryEntry> entries;
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::ReadDirectory(
file_system_context(), root_url, &entries));
- std::vector<DirectoryEntry>::iterator entry_iter;
+ std::vector<fileapi::DirectoryEntry>::iterator entry_iter;
EXPECT_EQ(files.size() + directories.size(), entries.size());
EXPECT_TRUE(change_observer()->HasNoChange());
for (entry_iter = entries.begin(); entry_iter != entries.end();
++entry_iter) {
- const DirectoryEntry& entry = *entry_iter;
+ const fileapi::DirectoryEntry& entry = *entry_iter;
std::set<base::FilePath::StringType>::iterator iter =
files.find(entry.name);
if (iter != files.end()) {
@@ -501,15 +512,15 @@ class ObfuscatedFileUtilTest : public testing::Test {
base::Time last_modified_time = base::Time::Now();
scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
- ofu()->Touch(
- context.get(), url, last_access_time, last_modified_time));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->Touch(context.get(), url, last_access_time,
+ last_modified_time));
// Currently we fire no change notifications for Touch.
EXPECT_TRUE(change_observer()->HasNoChange());
base::FilePath local_path;
- base::PlatformFileInfo file_info;
+ base::File::Info file_info;
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetFileInfo(
+ EXPECT_EQ(base::File::FILE_OK, ofu()->GetFileInfo(
context.get(), url, &file_info, &local_path));
// We compare as time_t here to lower our resolution, to avoid false
// negatives caused by conversion to the local filesystem's native
@@ -519,13 +530,13 @@ class ObfuscatedFileUtilTest : public testing::Test {
context.reset(NewContext(NULL));
last_modified_time += base::TimeDelta::FromHours(1);
last_access_time += base::TimeDelta::FromHours(14);
- EXPECT_EQ(base::PLATFORM_FILE_OK,
- ofu()->Touch(
- context.get(), url, last_access_time, last_modified_time));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->Touch(context.get(), url, last_access_time,
+ last_modified_time));
EXPECT_TRUE(change_observer()->HasNoChange());
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetFileInfo(
- context.get(), url, &file_info, &local_path));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->GetFileInfo(context.get(), url, &file_info, &local_path));
EXPECT_EQ(file_info.last_modified.ToTimeT(), last_modified_time.ToTimeT());
if (is_file) // Directories in OFU don't support atime.
EXPECT_EQ(file_info.last_accessed.ToTimeT(), last_access_time.ToTimeT());
@@ -539,24 +550,20 @@ class ObfuscatedFileUtilTest : public testing::Test {
FileSystemURL dest_url = CreateURLFromUTF8("new file");
int64 src_file_length = 87;
- base::PlatformFileError error_code;
- bool created = false;
- int file_flags = base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE;
- base::PlatformFile file_handle =
- base::CreatePlatformFile(
- src_file_path, file_flags, &created, &error_code);
- EXPECT_TRUE(created);
- ASSERT_EQ(base::PLATFORM_FILE_OK, error_code);
- ASSERT_NE(base::kInvalidPlatformFileValue, file_handle);
- ASSERT_TRUE(base::TruncatePlatformFile(file_handle, src_file_length));
- EXPECT_TRUE(base::ClosePlatformFile(file_handle));
+ base::File file(src_file_path,
+ base::File::FLAG_CREATE | base::File::FLAG_WRITE);
+ ASSERT_TRUE(file.IsValid());
+ EXPECT_TRUE(file.created());
+ ASSERT_TRUE(file.SetLength(src_file_length));
+ file.Close();
scoped_ptr<FileSystemOperationContext> context;
if (overwrite) {
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
- ofu()->EnsureFileExists(context.get(), dest_url, &created));
+ bool created = false;
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->EnsureFileExists(context.get(), dest_url, &created));
EXPECT_TRUE(created);
// We must have observed one (and only one) create_file_count.
@@ -570,14 +577,14 @@ class ObfuscatedFileUtilTest : public testing::Test {
// Verify that file creation requires sufficient quota for the path.
context.reset(NewContext(NULL));
context->set_allowed_bytes_growth(path_cost + src_file_length - 1);
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE,
+ EXPECT_EQ(base::File::FILE_ERROR_NO_SPACE,
ofu()->CopyInForeignFile(context.get(),
src_file_path, dest_url));
}
context.reset(NewContext(NULL));
context->set_allowed_bytes_growth(path_cost + src_file_length);
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->CopyInForeignFile(context.get(),
src_file_path, dest_url));
@@ -585,21 +592,22 @@ class ObfuscatedFileUtilTest : public testing::Test {
EXPECT_FALSE(DirectoryExists(dest_url));
context.reset(NewContext(NULL));
- base::PlatformFileInfo file_info;
+ base::File::Info file_info;
base::FilePath data_path;
- EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetFileInfo(
- context.get(), dest_url, &file_info, &data_path));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->GetFileInfo(context.get(), dest_url, &file_info,
+ &data_path));
EXPECT_NE(data_path, src_file_path);
EXPECT_TRUE(FileExists(data_path));
EXPECT_EQ(src_file_length, GetSize(data_path));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
- ofu()->DeleteFile(context.get(), dest_url));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->DeleteFile(context.get(), dest_url));
}
void ClearTimestamp(const FileSystemURL& url) {
scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->Touch(context.get(), url, base::Time(), base::Time()));
EXPECT_EQ(base::Time(), GetModifiedTime(url));
}
@@ -607,9 +615,9 @@ class ObfuscatedFileUtilTest : public testing::Test {
base::Time GetModifiedTime(const FileSystemURL& url) {
scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
base::FilePath data_path;
- base::PlatformFileInfo file_info;
+ base::File::Info file_info;
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->GetFileInfo(context.get(), url, &file_info, &data_path));
EXPECT_TRUE(change_observer()->HasNoChange());
return file_info.last_modified;
@@ -630,19 +638,19 @@ class ObfuscatedFileUtilTest : public testing::Test {
FileSystemURLAppendUTF8(dest_dir_url, "fuga"));
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->CreateDirectory(context.get(), src_dir_url, true, true));
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->CreateDirectory(context.get(), dest_dir_url, true, true));
bool created = false;
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(context.get(), src_file_url, &created));
if (overwrite) {
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(context.get(),
dest_file_url, &created));
}
@@ -650,7 +658,7 @@ class ObfuscatedFileUtilTest : public testing::Test {
ClearTimestamp(src_dir_url);
ClearTimestamp(dest_dir_url);
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->CopyOrMoveFile(context.get(),
src_file_url, dest_file_url,
FileSystemOperation::OPTION_NONE,
@@ -662,6 +670,120 @@ class ObfuscatedFileUtilTest : public testing::Test {
EXPECT_NE(base::Time(), GetModifiedTime(dest_dir_url));
}
+ void MaybeDropDatabasesAliveCaseTestBody() {
+ scoped_ptr<ObfuscatedFileUtil> file_util = CreateObfuscatedFileUtil(NULL);
+ file_util->InitOriginDatabase(GURL(), true /*create*/);
+ ASSERT_TRUE(file_util->origin_database_ != NULL);
+
+ // Callback to Drop DB is called while ObfuscatedFileUtilTest is
+ // still alive.
+ file_util->db_flush_delay_seconds_ = 0;
+ file_util->MarkUsed();
+ base::RunLoop().RunUntilIdle();
+
+ ASSERT_TRUE(file_util->origin_database_ == NULL);
+ }
+
+ void MaybeDropDatabasesAlreadyDeletedCaseTestBody() {
+ // Run message loop after OFU is already deleted to make sure callback
+ // doesn't cause a crash for use after free.
+ {
+ scoped_ptr<ObfuscatedFileUtil> file_util = CreateObfuscatedFileUtil(NULL);
+ file_util->InitOriginDatabase(GURL(), true /*create*/);
+ file_util->db_flush_delay_seconds_ = 0;
+ file_util->MarkUsed();
+ }
+
+ // At this point the callback is still in the message queue but OFU is gone.
+ base::RunLoop().RunUntilIdle();
+ }
+
+ void DestroyDirectoryDatabase_IsolatedTestBody() {
+ storage_policy_->AddIsolated(origin_);
+ scoped_ptr<ObfuscatedFileUtil> file_util = CreateObfuscatedFileUtil(
+ storage_policy_.get());
+ const FileSystemURL url = FileSystemURL::CreateForTest(
+ origin_, kFileSystemTypePersistent, base::FilePath());
+
+ // Create DirectoryDatabase for isolated origin.
+ SandboxDirectoryDatabase* db =
+ file_util->GetDirectoryDatabase(url, true /* create */);
+ ASSERT_TRUE(db != NULL);
+
+ // Destory it.
+ ASSERT_TRUE(file_util->DestroyDirectoryDatabase(
+ url.origin(), GetTypeString(url.type())));
+ ASSERT_TRUE(file_util->directories_.empty());
+ }
+
+ void GetDirectoryDatabase_IsolatedTestBody() {
+ storage_policy_->AddIsolated(origin_);
+ scoped_ptr<ObfuscatedFileUtil> file_util = CreateObfuscatedFileUtil(
+ storage_policy_.get());
+ const FileSystemURL url = FileSystemURL::CreateForTest(
+ origin_, kFileSystemTypePersistent, base::FilePath());
+
+ // Create DirectoryDatabase for isolated origin.
+ SandboxDirectoryDatabase* db =
+ file_util->GetDirectoryDatabase(url, true /* create */);
+ ASSERT_TRUE(db != NULL);
+ ASSERT_EQ(1U, file_util->directories_.size());
+
+ // Remove isolated.
+ storage_policy_->RemoveIsolated(url.origin());
+
+ // This should still get the same database.
+ SandboxDirectoryDatabase* db2 =
+ file_util->GetDirectoryDatabase(url, false /* create */);
+ ASSERT_EQ(db, db2);
+ }
+
+ void MigrationBackFromIsolatedTestBody() {
+ std::string kFakeDirectoryData("0123456789");
+ base::FilePath old_directory_db_path;
+
+ // Initialize the directory with one origin using
+ // SandboxIsolatedOriginDatabase.
+ {
+ std::string origin_string =
+ webkit_database::GetIdentifierFromOrigin(origin_);
+ SandboxIsolatedOriginDatabase database_old(
+ origin_string, data_dir_path(),
+ base::FilePath(
+ SandboxIsolatedOriginDatabase::kObsoleteOriginDirectory));
+ base::FilePath path;
+ EXPECT_TRUE(database_old.GetPathForOrigin(origin_string, &path));
+ EXPECT_FALSE(path.empty());
+
+ // Populate the origin directory with some fake data.
+ old_directory_db_path = data_dir_path().Append(path);
+ ASSERT_TRUE(base::CreateDirectory(old_directory_db_path));
+ EXPECT_EQ(static_cast<int>(kFakeDirectoryData.size()),
+ base::WriteFile(old_directory_db_path.AppendASCII("dummy"),
+ kFakeDirectoryData.data(),
+ kFakeDirectoryData.size()));
+ }
+
+ storage_policy_->AddIsolated(origin_);
+ scoped_ptr<ObfuscatedFileUtil> file_util = CreateObfuscatedFileUtil(
+ storage_policy_.get());
+ base::File::Error error = base::File::FILE_ERROR_FAILED;
+ base::FilePath origin_directory = file_util->GetDirectoryForOrigin(
+ origin_, true /* create */, &error);
+ EXPECT_EQ(base::File::FILE_OK, error);
+
+ // The database is migrated from the old one.
+ EXPECT_TRUE(base::DirectoryExists(origin_directory));
+ EXPECT_FALSE(base::DirectoryExists(old_directory_db_path));
+
+ // Check we see the same contents in the new origin directory.
+ std::string origin_db_data;
+ EXPECT_TRUE(base::PathExists(origin_directory.AppendASCII("dummy")));
+ EXPECT_TRUE(base::ReadFileToString(
+ origin_directory.AppendASCII("dummy"), &origin_db_data));
+ EXPECT_EQ(kFakeDirectoryData, origin_db_data);
+ }
+
int64 ComputeCurrentUsage() {
return sandbox_file_system_.ComputeCurrentOriginUsage() -
sandbox_file_system_.ComputeCurrentDirectoryDatabaseUsage();
@@ -678,7 +800,7 @@ class ObfuscatedFileUtilTest : public testing::Test {
protected:
base::ScopedTempDir data_dir_;
base::MessageLoop message_loop_;
- scoped_refptr<quota::MockSpecialStoragePolicy> storage_policy_;
+ scoped_refptr<MockSpecialStoragePolicy> storage_policy_;
scoped_refptr<quota::QuotaManager> quota_manager_;
scoped_refptr<FileSystemContext> file_system_context_;
GURL origin_;
@@ -687,27 +809,24 @@ class ObfuscatedFileUtilTest : public testing::Test {
SandboxFileSystemTestHelper sandbox_file_system_;
quota::QuotaStatusCode quota_status_;
int64 usage_;
- MockFileChangeObserver change_observer_;
- ChangeObserverList change_observers_;
+ fileapi::MockFileChangeObserver change_observer_;
+ fileapi::ChangeObserverList change_observers_;
private:
DISALLOW_COPY_AND_ASSIGN(ObfuscatedFileUtilTest);
};
TEST_F(ObfuscatedFileUtilTest, TestCreateAndDeleteFile) {
- base::PlatformFile file_handle = base::kInvalidPlatformFileValue;
- bool created;
FileSystemURL url = CreateURLFromUTF8("fake/file");
scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
- int file_flags = base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE;
+ int file_flags = base::File::FLAG_CREATE | base::File::FLAG_WRITE;
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
- ofu()->CreateOrOpen(
- context.get(), url, file_flags, &file_handle,
- &created));
+ base::File file = ofu()->CreateOrOpen(context.get(), url, file_flags);
+ EXPECT_FALSE(file.IsValid());
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, file.error_details());
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
ofu()->DeleteFile(context.get(), url));
url = CreateURLFromUTF8("test file");
@@ -718,38 +837,35 @@ TEST_F(ObfuscatedFileUtilTest, TestCreateAndDeleteFile) {
context.reset(NewContext(NULL));
context->set_allowed_bytes_growth(
ObfuscatedFileUtil::ComputeFilePathCost(url.path()) - 1);
- ASSERT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE,
- ofu()->CreateOrOpen(
- context.get(), url, file_flags, &file_handle, &created));
+ file = ofu()->CreateOrOpen(context.get(), url, file_flags);
+ EXPECT_FALSE(file.IsValid());
+ ASSERT_EQ(base::File::FILE_ERROR_NO_SPACE, file.error_details());
context.reset(NewContext(NULL));
context->set_allowed_bytes_growth(
ObfuscatedFileUtil::ComputeFilePathCost(url.path()));
- ASSERT_EQ(base::PLATFORM_FILE_OK,
- ofu()->CreateOrOpen(
- context.get(), url, file_flags, &file_handle, &created));
- ASSERT_TRUE(created);
+ file = ofu()->CreateOrOpen(context.get(), url, file_flags);
+ EXPECT_TRUE(file.IsValid());
+ ASSERT_TRUE(file.created());
EXPECT_EQ(1, change_observer()->get_and_reset_create_file_count());
- EXPECT_NE(base::kInvalidPlatformFileValue, file_handle);
- CheckFileAndCloseHandle(url, file_handle);
+ CheckFileAndCloseHandle(url, file.Pass());
context.reset(NewContext(NULL));
base::FilePath local_path;
- EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetLocalFilePath(
- context.get(), url, &local_path));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->GetLocalFilePath(context.get(), url, &local_path));
EXPECT_TRUE(base::PathExists(local_path));
// Verify that deleting a file isn't stopped by zero quota, and that it frees
// up quote from its path.
context.reset(NewContext(NULL));
context->set_allowed_bytes_growth(0);
- EXPECT_EQ(base::PLATFORM_FILE_OK,
- ofu()->DeleteFile(context.get(), url));
+ EXPECT_EQ(base::File::FILE_OK, ofu()->DeleteFile(context.get(), url));
EXPECT_EQ(1, change_observer()->get_and_reset_remove_file_count());
EXPECT_FALSE(base::PathExists(local_path));
EXPECT_EQ(ObfuscatedFileUtil::ComputeFilePathCost(url.path()),
- context->allowed_bytes_growth());
+ context->allowed_bytes_growth());
context.reset(NewContext(NULL));
bool exclusive = true;
@@ -757,30 +873,27 @@ TEST_F(ObfuscatedFileUtilTest, TestCreateAndDeleteFile) {
FileSystemURL directory_url = CreateURLFromUTF8(
"series/of/directories");
url = FileSystemURLAppendUTF8(directory_url, "file name");
- EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory(
- context.get(), directory_url, exclusive, recursive));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->CreateDirectory(context.get(), directory_url, exclusive,
+ recursive));
// The oepration created 3 directories recursively.
EXPECT_EQ(3, change_observer()->get_and_reset_create_directory_count());
context.reset(NewContext(NULL));
- file_handle = base::kInvalidPlatformFileValue;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
- ofu()->CreateOrOpen(
- context.get(), url, file_flags, &file_handle, &created));
- ASSERT_TRUE(created);
+ file = ofu()->CreateOrOpen(context.get(), url, file_flags);
+ ASSERT_TRUE(file.IsValid());
+ ASSERT_TRUE(file.created());
EXPECT_EQ(1, change_observer()->get_and_reset_create_file_count());
- EXPECT_NE(base::kInvalidPlatformFileValue, file_handle);
- CheckFileAndCloseHandle(url, file_handle);
+ CheckFileAndCloseHandle(url, file.Pass());
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetLocalFilePath(
- context.get(), url, &local_path));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->GetLocalFilePath(context.get(), url, &local_path));
EXPECT_TRUE(base::PathExists(local_path));
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
- ofu()->DeleteFile(context.get(), url));
+ EXPECT_EQ(base::File::FILE_OK, ofu()->DeleteFile(context.get(), url));
EXPECT_EQ(1, change_observer()->get_and_reset_remove_file_count());
EXPECT_FALSE(base::PathExists(local_path));
@@ -793,30 +906,28 @@ TEST_F(ObfuscatedFileUtilTest, TestTruncate) {
FileSystemURL url = CreateURLFromUTF8("file");
scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
ofu()->Truncate(context.get(), url, 4));
context.reset(NewContext(NULL));
- ASSERT_EQ(base::PLATFORM_FILE_OK,
- ofu()->EnsureFileExists(context.get(), url, &created));
+ ASSERT_EQ(base::File::FILE_OK,
+ ofu()->EnsureFileExists(context.get(), url, &created));
ASSERT_TRUE(created);
EXPECT_EQ(1, change_observer()->get_and_reset_create_file_count());
context.reset(NewContext(NULL));
base::FilePath local_path;
- EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetLocalFilePath(
- context.get(), url, &local_path));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->GetLocalFilePath(context.get(), url, &local_path));
EXPECT_EQ(0, GetSize(local_path));
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->Truncate(
- context.get(), url, 10));
+ EXPECT_EQ(base::File::FILE_OK, ofu()->Truncate(context.get(), url, 10));
EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
EXPECT_EQ(10, GetSize(local_path));
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->Truncate(
- context.get(), url, 1));
+ EXPECT_EQ(base::File::FILE_OK, ofu()->Truncate(context.get(), url, 1));
EXPECT_EQ(1, GetSize(local_path));
EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
@@ -831,41 +942,32 @@ TEST_F(ObfuscatedFileUtilTest, TestQuotaOnTruncation) {
bool created = false;
FileSystemURL url = CreateURLFromUTF8("file");
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(
AllowUsageIncrease(PathCost(url))->context(),
url, &created));
ASSERT_TRUE(created);
ASSERT_EQ(0, ComputeTotalFileSize());
- ASSERT_EQ(base::PLATFORM_FILE_OK,
- ofu()->Truncate(
- AllowUsageIncrease(1020)->context(),
- url, 1020));
+ ASSERT_EQ(base::File::FILE_OK,
+ ofu()->Truncate(AllowUsageIncrease(1020)->context(), url, 1020));
ASSERT_EQ(1020, ComputeTotalFileSize());
- ASSERT_EQ(base::PLATFORM_FILE_OK,
- ofu()->Truncate(
- AllowUsageIncrease(-1020)->context(),
- url, 0));
+ ASSERT_EQ(base::File::FILE_OK,
+ ofu()->Truncate(AllowUsageIncrease(-1020)->context(), url, 0));
ASSERT_EQ(0, ComputeTotalFileSize());
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE,
- ofu()->Truncate(
- DisallowUsageIncrease(1021)->context(),
- url, 1021));
+ EXPECT_EQ(base::File::FILE_ERROR_NO_SPACE,
+ ofu()->Truncate(DisallowUsageIncrease(1021)->context(),
+ url, 1021));
ASSERT_EQ(0, ComputeTotalFileSize());
- EXPECT_EQ(base::PLATFORM_FILE_OK,
- ofu()->Truncate(
- AllowUsageIncrease(1020)->context(),
- url, 1020));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->Truncate(AllowUsageIncrease(1020)->context(), url, 1020));
ASSERT_EQ(1020, ComputeTotalFileSize());
- EXPECT_EQ(base::PLATFORM_FILE_OK,
- ofu()->Truncate(
- AllowUsageIncrease(0)->context(),
- url, 1020));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->Truncate(AllowUsageIncrease(0)->context(), url, 1020));
ASSERT_EQ(1020, ComputeTotalFileSize());
// quota exceeded
@@ -873,24 +975,21 @@ TEST_F(ObfuscatedFileUtilTest, TestQuotaOnTruncation) {
scoped_ptr<UsageVerifyHelper> helper = AllowUsageIncrease(-1);
helper->context()->set_allowed_bytes_growth(
helper->context()->allowed_bytes_growth() - 1);
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->Truncate(helper->context(), url, 1019));
ASSERT_EQ(1019, ComputeTotalFileSize());
}
// Delete backing file to make following truncation fail.
base::FilePath local_path;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
- ofu()->GetLocalFilePath(
- UnlimitedContext().get(),
- url, &local_path));
+ ASSERT_EQ(base::File::FILE_OK,
+ ofu()->GetLocalFilePath(UnlimitedContext().get(), url,
+ &local_path));
ASSERT_FALSE(local_path.empty());
ASSERT_TRUE(base::DeleteFile(local_path, false));
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
- ofu()->Truncate(
- LimitedContext(1234).get(),
- url, 1234));
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
+ ofu()->Truncate(LimitedContext(1234).get(), url, 1234));
ASSERT_EQ(0, ComputeTotalFileSize());
}
@@ -898,9 +997,8 @@ TEST_F(ObfuscatedFileUtilTest, TestEnsureFileExists) {
FileSystemURL url = CreateURLFromUTF8("fake/file");
bool created = false;
scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
- ofu()->EnsureFileExists(
- context.get(), url, &created));
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
+ ofu()->EnsureFileExists(context.get(), url, &created));
EXPECT_TRUE(change_observer()->HasNoChange());
// Verify that file creation requires sufficient quota for the path.
@@ -909,7 +1007,7 @@ TEST_F(ObfuscatedFileUtilTest, TestEnsureFileExists) {
created = false;
context->set_allowed_bytes_growth(
ObfuscatedFileUtil::ComputeFilePathCost(url.path()) - 1);
- ASSERT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE,
+ ASSERT_EQ(base::File::FILE_ERROR_NO_SPACE,
ofu()->EnsureFileExists(context.get(), url, &created));
ASSERT_FALSE(created);
EXPECT_TRUE(change_observer()->HasNoChange());
@@ -917,15 +1015,15 @@ TEST_F(ObfuscatedFileUtilTest, TestEnsureFileExists) {
context.reset(NewContext(NULL));
context->set_allowed_bytes_growth(
ObfuscatedFileUtil::ComputeFilePathCost(url.path()));
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(context.get(), url, &created));
ASSERT_TRUE(created);
EXPECT_EQ(1, change_observer()->get_and_reset_create_file_count());
- CheckFileAndCloseHandle(url, base::kInvalidPlatformFileValue);
+ CheckFileAndCloseHandle(url, base::File());
context.reset(NewContext(NULL));
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(context.get(), url, &created));
ASSERT_FALSE(created);
EXPECT_TRUE(change_observer()->HasNoChange());
@@ -935,15 +1033,14 @@ TEST_F(ObfuscatedFileUtilTest, TestEnsureFileExists) {
context.reset(NewContext(NULL));
bool exclusive = true;
bool recursive = true;
- EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory(
- context.get(),
- FileSystemURLDirName(url),
- exclusive, recursive));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->CreateDirectory(context.get(), FileSystemURLDirName(url),
+ exclusive, recursive));
// 2 directories: path/ and path/to.
EXPECT_EQ(2, change_observer()->get_and_reset_create_directory_count());
context.reset(NewContext(NULL));
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(context.get(), url, &created));
ASSERT_TRUE(created);
EXPECT_FALSE(DirectoryExists(url));
@@ -957,12 +1054,12 @@ TEST_F(ObfuscatedFileUtilTest, TestDirectoryOps) {
bool exclusive = false;
bool recursive = false;
FileSystemURL url = CreateURLFromUTF8("foo/bar");
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, ofu()->CreateDirectory(
- context.get(), url, exclusive, recursive));
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
+ ofu()->CreateDirectory(context.get(), url, exclusive, recursive));
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
- ofu()->DeleteDirectory(context.get(), url));
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
+ ofu()->DeleteDirectory(context.get(), url));
FileSystemURL root = CreateURLFromUTF8(std::string());
EXPECT_FALSE(DirectoryExists(url));
@@ -973,8 +1070,8 @@ TEST_F(ObfuscatedFileUtilTest, TestDirectoryOps) {
context.reset(NewContext(NULL));
exclusive = false;
recursive = true;
- EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory(
- context.get(), url, exclusive, recursive));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->CreateDirectory(context.get(), url, exclusive, recursive));
EXPECT_EQ(2, change_observer()->get_and_reset_create_directory_count());
EXPECT_TRUE(DirectoryExists(url));
@@ -990,37 +1087,37 @@ TEST_F(ObfuscatedFileUtilTest, TestDirectoryOps) {
// Can't remove a non-empty directory.
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_EMPTY,
- ofu()->DeleteDirectory(context.get(),
- FileSystemURLDirName(url)));
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_EMPTY,
+ ofu()->DeleteDirectory(context.get(),
+ FileSystemURLDirName(url)));
EXPECT_TRUE(change_observer()->HasNoChange());
- base::PlatformFileInfo file_info;
+ base::File::Info file_info;
base::FilePath local_path;
- EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetFileInfo(
- context.get(), url, &file_info, &local_path));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->GetFileInfo(context.get(), url, &file_info, &local_path));
EXPECT_TRUE(local_path.empty());
EXPECT_TRUE(file_info.is_directory);
EXPECT_FALSE(file_info.is_symbolic_link);
// Same create again should succeed, since exclusive is false.
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory(
- context.get(), url, exclusive, recursive));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->CreateDirectory(context.get(), url, exclusive, recursive));
EXPECT_TRUE(change_observer()->HasNoChange());
exclusive = true;
recursive = true;
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS, ofu()->CreateDirectory(
- context.get(), url, exclusive, recursive));
+ EXPECT_EQ(base::File::FILE_ERROR_EXISTS,
+ ofu()->CreateDirectory(context.get(), url, exclusive, recursive));
EXPECT_TRUE(change_observer()->HasNoChange());
// Verify that deleting a directory isn't stopped by zero quota, and that it
// frees up quota from its path.
context.reset(NewContext(NULL));
context->set_allowed_bytes_growth(0);
- EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->DeleteDirectory(context.get(), url));
+ EXPECT_EQ(base::File::FILE_OK, ofu()->DeleteDirectory(context.get(), url));
EXPECT_EQ(1, change_observer()->get_and_reset_remove_directory_count());
EXPECT_EQ(ObfuscatedFileUtil::ComputeFilePathCost(url.path()),
context->allowed_bytes_growth());
@@ -1032,8 +1129,8 @@ TEST_F(ObfuscatedFileUtilTest, TestDirectoryOps) {
context.reset(NewContext(NULL));
EXPECT_TRUE(ofu()->IsDirectoryEmpty(context.get(), url));
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, ofu()->GetFileInfo(
- context.get(), url, &file_info, &local_path));
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
+ ofu()->GetFileInfo(context.get(), url, &file_info, &local_path));
// Verify that file creation requires sufficient quota for the path.
exclusive = true;
@@ -1041,15 +1138,15 @@ TEST_F(ObfuscatedFileUtilTest, TestDirectoryOps) {
context.reset(NewContext(NULL));
context->set_allowed_bytes_growth(
ObfuscatedFileUtil::ComputeFilePathCost(url.path()) - 1);
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, ofu()->CreateDirectory(
- context.get(), url, exclusive, recursive));
+ EXPECT_EQ(base::File::FILE_ERROR_NO_SPACE,
+ ofu()->CreateDirectory(context.get(), url, exclusive, recursive));
EXPECT_TRUE(change_observer()->HasNoChange());
context.reset(NewContext(NULL));
context->set_allowed_bytes_growth(
ObfuscatedFileUtil::ComputeFilePathCost(url.path()));
- EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory(
- context.get(), url, exclusive, recursive));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->CreateDirectory(context.get(), url, exclusive, recursive));
EXPECT_EQ(1, change_observer()->get_and_reset_create_directory_count());
EXPECT_TRUE(DirectoryExists(url));
@@ -1058,16 +1155,16 @@ TEST_F(ObfuscatedFileUtilTest, TestDirectoryOps) {
exclusive = true;
recursive = false;
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS, ofu()->CreateDirectory(
- context.get(), url, exclusive, recursive));
+ EXPECT_EQ(base::File::FILE_ERROR_EXISTS,
+ ofu()->CreateDirectory(context.get(), url, exclusive, recursive));
EXPECT_TRUE(change_observer()->HasNoChange());
exclusive = true;
recursive = false;
url = CreateURLFromUTF8("foo");
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS, ofu()->CreateDirectory(
- context.get(), url, exclusive, recursive));
+ EXPECT_EQ(base::File::FILE_ERROR_EXISTS,
+ ofu()->CreateDirectory(context.get(), url, exclusive, recursive));
EXPECT_TRUE(change_observer()->HasNoChange());
url = CreateURLFromUTF8("blah");
@@ -1078,8 +1175,8 @@ TEST_F(ObfuscatedFileUtilTest, TestDirectoryOps) {
exclusive = true;
recursive = false;
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory(
- context.get(), url, exclusive, recursive));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->CreateDirectory(context.get(), url, exclusive, recursive));
EXPECT_EQ(1, change_observer()->get_and_reset_create_directory_count());
EXPECT_TRUE(DirectoryExists(url));
@@ -1088,8 +1185,8 @@ TEST_F(ObfuscatedFileUtilTest, TestDirectoryOps) {
exclusive = true;
recursive = false;
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS, ofu()->CreateDirectory(
- context.get(), url, exclusive, recursive));
+ EXPECT_EQ(base::File::FILE_ERROR_EXISTS,
+ ofu()->CreateDirectory(context.get(), url, exclusive, recursive));
EXPECT_TRUE(change_observer()->HasNoChange());
}
@@ -1098,8 +1195,8 @@ TEST_F(ObfuscatedFileUtilTest, TestReadDirectory) {
bool exclusive = true;
bool recursive = true;
FileSystemURL url = CreateURLFromUTF8("directory/to/use");
- EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory(
- context.get(), url, exclusive, recursive));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->CreateDirectory(context.get(), url, exclusive, recursive));
TestReadDirectoryHelper(url);
}
@@ -1116,14 +1213,14 @@ TEST_F(ObfuscatedFileUtilTest, TestReadDirectoryOnFile) {
scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
bool created = false;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
- ofu()->EnsureFileExists(context.get(), url, &created));
+ ASSERT_EQ(base::File::FILE_OK,
+ ofu()->EnsureFileExists(context.get(), url, &created));
ASSERT_TRUE(created);
- std::vector<DirectoryEntry> entries;
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY,
- AsyncFileTestHelper::ReadDirectory(
- file_system_context(), url, &entries));
+ std::vector<fileapi::DirectoryEntry> entries;
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_A_DIRECTORY,
+ AsyncFileTestHelper::ReadDirectory(file_system_context(), url,
+ &entries));
EXPECT_TRUE(ofu()->IsDirectoryEmpty(context.get(), url));
}
@@ -1136,14 +1233,14 @@ TEST_F(ObfuscatedFileUtilTest, TestTouch) {
base::Time last_modified_time = base::Time::Now();
// It's not there yet.
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
- ofu()->Touch(
- context.get(), url, last_access_time, last_modified_time));
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
+ ofu()->Touch(context.get(), url, last_access_time,
+ last_modified_time));
// OK, now create it.
context.reset(NewContext(NULL));
bool created = false;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(context.get(), url, &created));
ASSERT_TRUE(created);
TestTouchHelper(url, true);
@@ -1153,8 +1250,8 @@ TEST_F(ObfuscatedFileUtilTest, TestTouch) {
bool exclusive = true;
bool recursive = false;
url = CreateURLFromUTF8("dir");
- ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory(context.get(),
- url, exclusive, recursive));
+ ASSERT_EQ(base::File::FILE_OK,
+ ofu()->CreateDirectory(context.get(), url, exclusive, recursive));
TestTouchHelper(url, false);
}
@@ -1165,12 +1262,12 @@ TEST_F(ObfuscatedFileUtilTest, TestPathQuotas) {
url = CreateURLFromUTF8("file name");
context->set_allowed_bytes_growth(5);
bool created = false;
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE,
- ofu()->EnsureFileExists(context.get(), url, &created));
+ EXPECT_EQ(base::File::FILE_ERROR_NO_SPACE,
+ ofu()->EnsureFileExists(context.get(), url, &created));
EXPECT_FALSE(created);
context->set_allowed_bytes_growth(1024);
- EXPECT_EQ(base::PLATFORM_FILE_OK,
- ofu()->EnsureFileExists(context.get(), url, &created));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->EnsureFileExists(context.get(), url, &created));
EXPECT_TRUE(created);
int64 path_cost = ObfuscatedFileUtil::ComputeFilePathCost(url.path());
EXPECT_EQ(1024 - path_cost, context->allowed_bytes_growth());
@@ -1190,8 +1287,8 @@ TEST_F(ObfuscatedFileUtilTest, TestPathQuotas) {
}
context.reset(NewContext(NULL));
context->set_allowed_bytes_growth(1024);
- EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory(
- context.get(), url, exclusive, recursive));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->CreateDirectory(context.get(), url, exclusive, recursive));
EXPECT_EQ(1024 - path_cost, context->allowed_bytes_growth());
}
@@ -1201,39 +1298,39 @@ TEST_F(ObfuscatedFileUtilTest, TestCopyOrMoveFileNotFound) {
scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
bool is_copy_not_move = false;
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
- ofu()->CopyOrMoveFile(context.get(), source_url, dest_url,
- FileSystemOperation::OPTION_NONE,
- is_copy_not_move));
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
+ ofu()->CopyOrMoveFile(context.get(), source_url, dest_url,
+ FileSystemOperation::OPTION_NONE,
+ is_copy_not_move));
EXPECT_TRUE(change_observer()->HasNoChange());
context.reset(NewContext(NULL));
is_copy_not_move = true;
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
- ofu()->CopyOrMoveFile(context.get(), source_url, dest_url,
- FileSystemOperation::OPTION_NONE,
- is_copy_not_move));
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
+ ofu()->CopyOrMoveFile(context.get(), source_url, dest_url,
+ FileSystemOperation::OPTION_NONE,
+ is_copy_not_move));
EXPECT_TRUE(change_observer()->HasNoChange());
source_url = CreateURLFromUTF8("dir/dir/file");
bool exclusive = true;
bool recursive = true;
context.reset(NewContext(NULL));
- ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory(
- context.get(),
- FileSystemURLDirName(source_url),
- exclusive, recursive));
+ ASSERT_EQ(base::File::FILE_OK,
+ ofu()->CreateDirectory(context.get(),
+ FileSystemURLDirName(source_url),
+ exclusive, recursive));
EXPECT_EQ(2, change_observer()->get_and_reset_create_directory_count());
is_copy_not_move = false;
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
- ofu()->CopyOrMoveFile(context.get(), source_url, dest_url,
- FileSystemOperation::OPTION_NONE,
- is_copy_not_move));
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
+ ofu()->CopyOrMoveFile(context.get(), source_url, dest_url,
+ FileSystemOperation::OPTION_NONE,
+ is_copy_not_move));
EXPECT_TRUE(change_observer()->HasNoChange());
context.reset(NewContext(NULL));
is_copy_not_move = true;
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
- ofu()->CopyOrMoveFile(context.get(), source_url, dest_url,
- FileSystemOperation::OPTION_NONE,
- is_copy_not_move));
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
+ ofu()->CopyOrMoveFile(context.get(), source_url, dest_url,
+ FileSystemOperation::OPTION_NONE,
+ is_copy_not_move));
EXPECT_TRUE(change_observer()->HasNoChange());
}
@@ -1260,64 +1357,68 @@ TEST_F(ObfuscatedFileUtilTest, TestCopyOrMoveFileSuccess) {
FileSystemURL dest_url = CreateURLFromUTF8(test_case.dest_path);
context.reset(NewContext(NULL));
- ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory(
- context.get(),
- FileSystemURLDirName(source_url),
- exclusive, recursive));
+ ASSERT_EQ(base::File::FILE_OK,
+ ofu()->CreateDirectory(context.get(),
+ FileSystemURLDirName(source_url),
+ exclusive, recursive));
context.reset(NewContext(NULL));
- ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory(
- context.get(),
- FileSystemURLDirName(dest_url),
- exclusive, recursive));
+ ASSERT_EQ(base::File::FILE_OK,
+ ofu()->CreateDirectory(context.get(),
+ FileSystemURLDirName(dest_url),
+ exclusive, recursive));
bool created = false;
context.reset(NewContext(NULL));
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(context.get(), source_url, &created));
ASSERT_TRUE(created);
context.reset(NewContext(NULL));
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->Truncate(context.get(), source_url, kSourceLength));
if (test_case.cause_overwrite) {
context.reset(NewContext(NULL));
created = false;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(context.get(), dest_url, &created));
ASSERT_TRUE(created);
context.reset(NewContext(NULL));
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->Truncate(context.get(), dest_url, kDestLength));
}
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->CopyOrMoveFile(
- context.get(), source_url, dest_url, FileSystemOperation::OPTION_NONE,
- test_case.is_copy_not_move));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->CopyOrMoveFile(context.get(), source_url, dest_url,
+ FileSystemOperation::OPTION_NONE,
+ test_case.is_copy_not_move));
if (test_case.is_copy_not_move) {
- base::PlatformFileInfo file_info;
+ base::File::Info file_info;
base::FilePath local_path;
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetFileInfo(
- context.get(), source_url, &file_info, &local_path));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->GetFileInfo(context.get(), source_url, &file_info,
+ &local_path));
EXPECT_EQ(kSourceLength, file_info.size);
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->DeleteFile(context.get(), source_url));
} else {
- base::PlatformFileInfo file_info;
+ base::File::Info file_info;
base::FilePath local_path;
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, ofu()->GetFileInfo(
- context.get(), source_url, &file_info, &local_path));
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
+ ofu()->GetFileInfo(context.get(), source_url, &file_info,
+ &local_path));
}
- base::PlatformFileInfo file_info;
+ base::File::Info file_info;
base::FilePath local_path;
- EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->GetFileInfo(
- context.get(), dest_url, &file_info, &local_path));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->GetFileInfo(context.get(), dest_url, &file_info,
+ &local_path));
EXPECT_EQ(kSourceLength, file_info.size);
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->DeleteFile(context.get(), dest_url));
}
}
@@ -1327,32 +1428,29 @@ TEST_F(ObfuscatedFileUtilTest, TestCopyPathQuotas) {
FileSystemURL dest_url = CreateURLFromUTF8("destination path");
scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
bool created = false;
- ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->EnsureFileExists(
- context.get(), src_url, &created));
+ ASSERT_EQ(base::File::FILE_OK,
+ ofu()->EnsureFileExists(context.get(), src_url, &created));
bool is_copy = true;
// Copy, no overwrite.
context->set_allowed_bytes_growth(
ObfuscatedFileUtil::ComputeFilePathCost(dest_url.path()) - 1);
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE,
- ofu()->CopyOrMoveFile(
- context.get(),
- src_url, dest_url, FileSystemOperation::OPTION_NONE, is_copy));
+ EXPECT_EQ(base::File::FILE_ERROR_NO_SPACE,
+ ofu()->CopyOrMoveFile(context.get(), src_url, dest_url,
+ FileSystemOperation::OPTION_NONE, is_copy));
context.reset(NewContext(NULL));
context->set_allowed_bytes_growth(
ObfuscatedFileUtil::ComputeFilePathCost(dest_url.path()));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
- ofu()->CopyOrMoveFile(
- context.get(),
- src_url, dest_url, FileSystemOperation::OPTION_NONE, is_copy));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->CopyOrMoveFile(context.get(), src_url, dest_url,
+ FileSystemOperation::OPTION_NONE, is_copy));
// Copy, with overwrite.
context.reset(NewContext(NULL));
context->set_allowed_bytes_growth(0);
- EXPECT_EQ(base::PLATFORM_FILE_OK,
- ofu()->CopyOrMoveFile(
- context.get(),
- src_url, dest_url, FileSystemOperation::OPTION_NONE, is_copy));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->CopyOrMoveFile(context.get(), src_url, dest_url,
+ FileSystemOperation::OPTION_NONE, is_copy));
}
TEST_F(ObfuscatedFileUtilTest, TestMovePathQuotasWithRename) {
@@ -1360,8 +1458,8 @@ TEST_F(ObfuscatedFileUtilTest, TestMovePathQuotasWithRename) {
FileSystemURL dest_url = CreateURLFromUTF8("destination path");
scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
bool created = false;
- ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->EnsureFileExists(
- context.get(), src_url, &created));
+ ASSERT_EQ(base::File::FILE_OK,
+ ofu()->EnsureFileExists(context.get(), src_url, &created));
bool is_copy = false;
// Move, rename, no overwrite.
@@ -1369,45 +1467,43 @@ TEST_F(ObfuscatedFileUtilTest, TestMovePathQuotasWithRename) {
context->set_allowed_bytes_growth(
ObfuscatedFileUtil::ComputeFilePathCost(dest_url.path()) -
ObfuscatedFileUtil::ComputeFilePathCost(src_url.path()) - 1);
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE,
- ofu()->CopyOrMoveFile(
- context.get(),
- src_url, dest_url, FileSystemOperation::OPTION_NONE, is_copy));
+ EXPECT_EQ(base::File::FILE_ERROR_NO_SPACE,
+ ofu()->CopyOrMoveFile(context.get(), src_url, dest_url,
+ FileSystemOperation::OPTION_NONE, is_copy));
context.reset(NewContext(NULL));
context->set_allowed_bytes_growth(
ObfuscatedFileUtil::ComputeFilePathCost(dest_url.path()) -
ObfuscatedFileUtil::ComputeFilePathCost(src_url.path()));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
- ofu()->CopyOrMoveFile(
- context.get(),
- src_url, dest_url, FileSystemOperation::OPTION_NONE, is_copy));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->CopyOrMoveFile(context.get(), src_url, dest_url,
+ FileSystemOperation::OPTION_NONE, is_copy));
context.reset(NewContext(NULL));
- ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->EnsureFileExists(
- context.get(), src_url, &created));
+ ASSERT_EQ(base::File::FILE_OK,
+ ofu()->EnsureFileExists(context.get(), src_url, &created));
// Move, rename, with overwrite.
context.reset(NewContext(NULL));
context->set_allowed_bytes_growth(0);
- EXPECT_EQ(base::PLATFORM_FILE_OK,
- ofu()->CopyOrMoveFile(
- context.get(),
- src_url, dest_url, FileSystemOperation::OPTION_NONE, is_copy));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->CopyOrMoveFile(context.get(), src_url, dest_url,
+ FileSystemOperation::OPTION_NONE, is_copy));
}
TEST_F(ObfuscatedFileUtilTest, TestMovePathQuotasWithoutRename) {
FileSystemURL src_url = CreateURLFromUTF8("src path");
scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
bool created = false;
- ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->EnsureFileExists(
- context.get(), src_url, &created));
+ ASSERT_EQ(base::File::FILE_OK,
+ ofu()->EnsureFileExists(context.get(), src_url, &created));
bool exclusive = true;
bool recursive = false;
FileSystemURL dir_url = CreateURLFromUTF8("directory path");
context.reset(NewContext(NULL));
- ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory(
- context.get(), dir_url, exclusive, recursive));
+ ASSERT_EQ(base::File::FILE_OK,
+ ofu()->CreateDirectory(context.get(), dir_url, exclusive,
+ recursive));
FileSystemURL dest_url = FileSystemURLAppend(
dir_url, src_url.path().value());
@@ -1417,22 +1513,20 @@ TEST_F(ObfuscatedFileUtilTest, TestMovePathQuotasWithoutRename) {
// Move, no rename, no overwrite.
context.reset(NewContext(NULL));
context->set_allowed_bytes_growth(allowed_bytes_growth);
- EXPECT_EQ(base::PLATFORM_FILE_OK,
- ofu()->CopyOrMoveFile(
- context.get(),
- src_url, dest_url, FileSystemOperation::OPTION_NONE, is_copy));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->CopyOrMoveFile(context.get(), src_url, dest_url,
+ FileSystemOperation::OPTION_NONE, is_copy));
EXPECT_EQ(allowed_bytes_growth, context->allowed_bytes_growth());
// Move, no rename, with overwrite.
context.reset(NewContext(NULL));
- ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->EnsureFileExists(
- context.get(), src_url, &created));
+ ASSERT_EQ(base::File::FILE_OK,
+ ofu()->EnsureFileExists(context.get(), src_url, &created));
context.reset(NewContext(NULL));
context->set_allowed_bytes_growth(allowed_bytes_growth);
- EXPECT_EQ(base::PLATFORM_FILE_OK,
- ofu()->CopyOrMoveFile(
- context.get(),
- src_url, dest_url, FileSystemOperation::OPTION_NONE, is_copy));
+ EXPECT_EQ(base::File::FILE_OK,
+ ofu()->CopyOrMoveFile(context.get(), src_url, dest_url,
+ FileSystemOperation::OPTION_NONE, is_copy));
EXPECT_EQ(
allowed_bytes_growth +
ObfuscatedFileUtil::ComputeFilePathCost(src_url.path()),
@@ -1449,8 +1543,9 @@ TEST_F(ObfuscatedFileUtilTest, TestEnumerator) {
FileSystemURL src_url = CreateURLFromUTF8("source dir");
bool exclusive = true;
bool recursive = false;
- ASSERT_EQ(base::PLATFORM_FILE_OK, ofu()->CreateDirectory(
- context.get(), src_url, exclusive, recursive));
+ ASSERT_EQ(base::File::FILE_OK,
+ ofu()->CreateDirectory(context.get(), src_url, exclusive,
+ recursive));
std::set<base::FilePath::StringType> files;
std::set<base::FilePath::StringType> directories;
@@ -1459,7 +1554,7 @@ TEST_F(ObfuscatedFileUtilTest, TestEnumerator) {
FileSystemURL dest_url = CreateURLFromUTF8("destination dir");
EXPECT_FALSE(DirectoryExists(dest_url));
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::Copy(
file_system_context(), src_url, dest_url));
@@ -1467,7 +1562,7 @@ TEST_F(ObfuscatedFileUtilTest, TestEnumerator) {
EXPECT_TRUE(DirectoryExists(src_url));
EXPECT_TRUE(DirectoryExists(dest_url));
recursive = true;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::Remove(
file_system_context(), dest_url, recursive));
EXPECT_FALSE(DirectoryExists(dest_url));
@@ -1502,7 +1597,7 @@ TEST_F(ObfuscatedFileUtilTest, TestOriginEnumerator) {
scoped_ptr<FileSystemOperationContext> context(
NewContext(file_system.get()));
bool created = false;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(
context.get(),
file_system->CreateURLFromUTF8("file"),
@@ -1515,7 +1610,7 @@ TEST_F(ObfuscatedFileUtilTest, TestOriginEnumerator) {
scoped_ptr<FileSystemOperationContext> context(
NewContext(file_system.get()));
bool created = false;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(
context.get(),
file_system->CreateURLFromUTF8("file"),
@@ -1567,27 +1662,27 @@ TEST_F(ObfuscatedFileUtilTest, TestRevokeUsageCache) {
int64 expected_quota = 0;
- for (size_t i = 0; i < test::kRegularTestCaseSize; ++i) {
+ for (size_t i = 0; i < kRegularFileSystemTestCaseSize; ++i) {
SCOPED_TRACE(testing::Message() << "Creating kRegularTestCase " << i);
- const test::TestCaseRecord& test_case = test::kRegularTestCases[i];
+ const FileSystemTestCaseRecord& test_case =
+ kRegularFileSystemTestCases[i];
base::FilePath file_path(test_case.path);
expected_quota += ObfuscatedFileUtil::ComputeFilePathCost(file_path);
if (test_case.is_directory) {
bool exclusive = true;
bool recursive = false;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
- ofu()->CreateDirectory(context.get(), CreateURL(file_path),
- exclusive, recursive));
+ ASSERT_EQ(base::File::FILE_OK,
+ ofu()->CreateDirectory(context.get(), CreateURL(file_path),
+ exclusive, recursive));
} else {
bool created = false;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
- ofu()->EnsureFileExists(context.get(), CreateURL(file_path),
- &created));
+ ASSERT_EQ(base::File::FILE_OK,
+ ofu()->EnsureFileExists(context.get(), CreateURL(file_path),
+ &created));
ASSERT_TRUE(created);
- ASSERT_EQ(base::PLATFORM_FILE_OK,
- ofu()->Truncate(context.get(),
- CreateURL(file_path),
- test_case.data_file_size));
+ ASSERT_EQ(base::File::FILE_OK,
+ ofu()->Truncate(context.get(), CreateURL(file_path),
+ test_case.data_file_size));
expected_quota += test_case.data_file_size;
}
}
@@ -1613,21 +1708,20 @@ TEST_F(ObfuscatedFileUtilTest, TestInconsistency) {
const FileSystemURL kPath2 = CreateURLFromUTF8("fuga");
scoped_ptr<FileSystemOperationContext> context;
- base::PlatformFile file;
- base::PlatformFileInfo file_info;
+ base::File::Info file_info;
base::FilePath data_path;
bool created = false;
// Create a non-empty file.
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(context.get(), kPath1, &created));
EXPECT_TRUE(created);
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->Truncate(context.get(), kPath1, 10));
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->GetFileInfo(
context.get(), kPath1, &file_info, &data_path));
EXPECT_EQ(10, file_info.size);
@@ -1638,18 +1732,18 @@ TEST_F(ObfuscatedFileUtilTest, TestInconsistency) {
// Try to get file info of broken file.
EXPECT_FALSE(PathExists(kPath1));
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(context.get(), kPath1, &created));
EXPECT_TRUE(created);
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->GetFileInfo(
context.get(), kPath1, &file_info, &data_path));
EXPECT_EQ(0, file_info.size);
// Make another broken file to |kPath2|.
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(context.get(), kPath2, &created));
EXPECT_TRUE(created);
@@ -1658,31 +1752,30 @@ TEST_F(ObfuscatedFileUtilTest, TestInconsistency) {
// Repair broken |kPath1|.
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
ofu()->Touch(context.get(), kPath1, base::Time::Now(),
base::Time::Now()));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(context.get(), kPath1, &created));
EXPECT_TRUE(created);
// Copy from sound |kPath1| to broken |kPath2|.
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->CopyOrMoveFile(context.get(), kPath1, kPath2,
FileSystemOperation::OPTION_NONE,
true /* copy */));
ofu()->DestroyDirectoryDatabase(origin(), type_string());
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
- ofu()->CreateOrOpen(
- context.get(), kPath1,
- base::PLATFORM_FILE_READ | base::PLATFORM_FILE_CREATE,
- &file, &created));
- EXPECT_TRUE(created);
- EXPECT_TRUE(base::GetPlatformFileInfo(file, &file_info));
+ base::File file =
+ ofu()->CreateOrOpen(context.get(), kPath1,
+ base::File::FLAG_READ | base::File::FLAG_CREATE);
+ EXPECT_TRUE(file.IsValid());
+ EXPECT_TRUE(file.created());
+
+ EXPECT_TRUE(file.GetInfo(&file_info));
EXPECT_EQ(0, file_info.size);
- EXPECT_TRUE(base::ClosePlatformFile(file));
}
TEST_F(ObfuscatedFileUtilTest, TestIncompleteDirectoryReading) {
@@ -1697,24 +1790,24 @@ TEST_F(ObfuscatedFileUtilTest, TestIncompleteDirectoryReading) {
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kPath); ++i) {
bool created = false;
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(context.get(), kPath[i], &created));
EXPECT_TRUE(created);
}
- std::vector<DirectoryEntry> entries;
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ std::vector<fileapi::DirectoryEntry> entries;
+ EXPECT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::ReadDirectory(
file_system_context(), empty_path, &entries));
EXPECT_EQ(3u, entries.size());
base::FilePath local_path;
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->GetLocalFilePath(context.get(), kPath[0], &local_path));
EXPECT_TRUE(base::DeleteFile(local_path, false));
entries.clear();
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::ReadDirectory(
file_system_context(), empty_path, &entries));
EXPECT_EQ(ARRAYSIZE_UNSAFE(kPath) - 1, entries.size());
@@ -1725,16 +1818,15 @@ TEST_F(ObfuscatedFileUtilTest, TestDirectoryTimestampForCreation) {
const FileSystemURL dir_url = CreateURLFromUTF8("foo_dir");
// Create working directory.
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->CreateDirectory(context.get(), dir_url, false, false));
// EnsureFileExists, create case.
- FileSystemURL url(FileSystemURLAppendUTF8(
- dir_url, "EnsureFileExists_file"));
+ FileSystemURL url(FileSystemURLAppendUTF8(dir_url, "EnsureFileExists_file"));
bool created = false;
ClearTimestamp(dir_url);
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(context.get(), url, &created));
EXPECT_TRUE(created);
EXPECT_NE(base::Time(), GetModifiedTime(dir_url));
@@ -1743,7 +1835,7 @@ TEST_F(ObfuscatedFileUtilTest, TestDirectoryTimestampForCreation) {
created = true;
ClearTimestamp(dir_url);
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(context.get(), url, &created));
EXPECT_FALSE(created);
EXPECT_EQ(base::Time(), GetModifiedTime(dir_url));
@@ -1751,56 +1843,45 @@ TEST_F(ObfuscatedFileUtilTest, TestDirectoryTimestampForCreation) {
// fail case.
url = FileSystemURLAppendUTF8(dir_url, "EnsureFileExists_dir");
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->CreateDirectory(context.get(), url, false, false));
ClearTimestamp(dir_url);
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_A_FILE,
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_A_FILE,
ofu()->EnsureFileExists(context.get(), url, &created));
EXPECT_EQ(base::Time(), GetModifiedTime(dir_url));
// CreateOrOpen, create case.
url = FileSystemURLAppendUTF8(dir_url, "CreateOrOpen_file");
- base::PlatformFile file_handle = base::kInvalidPlatformFileValue;
- created = false;
ClearTimestamp(dir_url);
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
- ofu()->CreateOrOpen(
- context.get(), url,
- base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE,
- &file_handle, &created));
- EXPECT_NE(base::kInvalidPlatformFileValue, file_handle);
- EXPECT_TRUE(created);
- EXPECT_TRUE(base::ClosePlatformFile(file_handle));
+ base::File file =
+ ofu()->CreateOrOpen(context.get(), url,
+ base::File::FLAG_CREATE | base::File::FLAG_WRITE);
+
+ EXPECT_TRUE(file.IsValid());
+ EXPECT_TRUE(file.created());
+ file.Close();
EXPECT_NE(base::Time(), GetModifiedTime(dir_url));
// open case.
- file_handle = base::kInvalidPlatformFileValue;
- created = true;
ClearTimestamp(dir_url);
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
- ofu()->CreateOrOpen(
- context.get(), url,
- base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE,
- &file_handle, &created));
- EXPECT_NE(base::kInvalidPlatformFileValue, file_handle);
- EXPECT_FALSE(created);
- EXPECT_TRUE(base::ClosePlatformFile(file_handle));
+ file = ofu()->CreateOrOpen(context.get(), url,
+ base::File::FLAG_OPEN | base::File::FLAG_WRITE);
+ EXPECT_TRUE(file.IsValid());
+ EXPECT_FALSE(file.created());
+ file.Close();
EXPECT_EQ(base::Time(), GetModifiedTime(dir_url));
// fail case
- file_handle = base::kInvalidPlatformFileValue;
ClearTimestamp(dir_url);
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS,
- ofu()->CreateOrOpen(
- context.get(), url,
- base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE,
- &file_handle, &created));
- EXPECT_EQ(base::kInvalidPlatformFileValue, file_handle);
+ file = ofu()->CreateOrOpen(context.get(), url,
+ base::File::FLAG_CREATE | base::File::FLAG_WRITE);
+ EXPECT_FALSE(file.IsValid());
+ EXPECT_EQ(base::File::FILE_ERROR_EXISTS, file.error_details());
EXPECT_EQ(base::Time(), GetModifiedTime(dir_url));
// CreateDirectory, create case.
@@ -1809,7 +1890,7 @@ TEST_F(ObfuscatedFileUtilTest, TestDirectoryTimestampForCreation) {
FileSystemURL subdir_url(FileSystemURLAppendUTF8(url, "subdir"));
ClearTimestamp(dir_url);
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->CreateDirectory(context.get(), subdir_url,
true /* exclusive */, true /* recursive */));
EXPECT_NE(base::Time(), GetModifiedTime(dir_url));
@@ -1820,7 +1901,7 @@ TEST_F(ObfuscatedFileUtilTest, TestDirectoryTimestampForCreation) {
ClearTimestamp(dir_url);
ClearTimestamp(url);
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->CreateDirectory(context.get(), subdir_url,
true /* exclusive */, true /* recursive */));
EXPECT_EQ(base::Time(), GetModifiedTime(dir_url));
@@ -1830,7 +1911,7 @@ TEST_F(ObfuscatedFileUtilTest, TestDirectoryTimestampForCreation) {
url = FileSystemURLAppendUTF8(dir_url, "CreateDirectory_dir");
ClearTimestamp(dir_url);
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS,
+ EXPECT_EQ(base::File::FILE_ERROR_EXISTS,
ofu()->CreateDirectory(context.get(), url,
true /* exclusive */, true /* recursive */));
EXPECT_EQ(base::Time(), GetModifiedTime(dir_url));
@@ -1840,17 +1921,17 @@ TEST_F(ObfuscatedFileUtilTest, TestDirectoryTimestampForCreation) {
FileSystemURL src_path = FileSystemURLAppendUTF8(
dir_url, "CopyInForeignFile_src_file");
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(context.get(), src_path, &created));
EXPECT_TRUE(created);
base::FilePath src_local_path;
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->GetLocalFilePath(context.get(), src_path, &src_local_path));
ClearTimestamp(dir_url);
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->CopyInForeignFile(context.get(),
src_local_path,
url));
@@ -1862,7 +1943,7 @@ TEST_F(ObfuscatedFileUtilTest, TestDirectoryTimestampForDeletion) {
const FileSystemURL dir_url = CreateURLFromUTF8("foo_dir");
// Create working directory.
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->CreateDirectory(context.get(), dir_url, false, false));
// DeleteFile, delete case.
@@ -1870,20 +1951,20 @@ TEST_F(ObfuscatedFileUtilTest, TestDirectoryTimestampForDeletion) {
dir_url, "DeleteFile_file");
bool created = false;
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(context.get(), url, &created));
EXPECT_TRUE(created);
ClearTimestamp(dir_url);
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->DeleteFile(context.get(), url));
EXPECT_NE(base::Time(), GetModifiedTime(dir_url));
// fail case.
ClearTimestamp(dir_url);
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
ofu()->DeleteFile(context.get(), url));
EXPECT_EQ(base::Time(), GetModifiedTime(dir_url));
@@ -1891,28 +1972,28 @@ TEST_F(ObfuscatedFileUtilTest, TestDirectoryTimestampForDeletion) {
url = FileSystemURLAppendUTF8(dir_url, "DeleteDirectory_dir");
FileSystemURL file_path(FileSystemURLAppendUTF8(url, "pakeratta"));
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->CreateDirectory(context.get(), url, true, true));
created = false;
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(context.get(), file_path, &created));
EXPECT_TRUE(created);
ClearTimestamp(dir_url);
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_EMPTY,
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_EMPTY,
ofu()->DeleteDirectory(context.get(), url));
EXPECT_EQ(base::Time(), GetModifiedTime(dir_url));
// delete case.
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->DeleteFile(context.get(), file_path));
ClearTimestamp(dir_url);
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK, ofu()->DeleteDirectory(context.get(), url));
+ EXPECT_EQ(base::File::FILE_OK, ofu()->DeleteDirectory(context.get(), url));
EXPECT_NE(base::Time(), GetModifiedTime(dir_url));
}
@@ -1933,42 +2014,42 @@ TEST_F(ObfuscatedFileUtilTest, TestFileEnumeratorTimestamp) {
FileSystemURL url2 = FileSystemURLAppendUTF8(dir, "baz");
scoped_ptr<FileSystemOperationContext> context(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->CreateDirectory(context.get(), dir, false, false));
bool created = false;
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(context.get(), url1, &created));
EXPECT_TRUE(created);
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->CreateDirectory(context.get(), url2, false, false));
base::FilePath file_path;
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->GetLocalFilePath(context.get(), url1, &file_path));
EXPECT_FALSE(file_path.empty());
context.reset(NewContext(NULL));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->Touch(context.get(), url1,
base::Time::Now() + base::TimeDelta::FromHours(1),
base::Time()));
context.reset(NewContext(NULL));
- scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> file_enum(
+ scoped_ptr<fileapi::FileSystemFileUtil::AbstractFileEnumerator> file_enum(
ofu()->CreateFileEnumerator(context.get(), dir, false));
int count = 0;
base::FilePath file_path_each;
while (!(file_path_each = file_enum->Next()).empty()) {
context.reset(NewContext(NULL));
- base::PlatformFileInfo file_info;
+ base::File::Info file_info;
base::FilePath file_path;
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
ofu()->GetFileInfo(context.get(),
FileSystemURL::CreateForTest(
dir.origin(),
@@ -1997,14 +2078,14 @@ TEST_F(ObfuscatedFileUtilTest, MAYBE_TestQuotaOnCopyFile) {
bool created;
int64 expected_total_file_size = 0;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(
AllowUsageIncrease(PathCost(from_file))->context(),
from_file, &created));
ASSERT_TRUE(created);
ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize());
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(
AllowUsageIncrease(PathCost(obstacle_file))->context(),
obstacle_file, &created));
@@ -2013,7 +2094,7 @@ TEST_F(ObfuscatedFileUtilTest, MAYBE_TestQuotaOnCopyFile) {
int64 from_file_size = 1020;
expected_total_file_size += from_file_size;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->Truncate(
AllowUsageIncrease(from_file_size)->context(),
from_file, from_file_size));
@@ -2021,7 +2102,7 @@ TEST_F(ObfuscatedFileUtilTest, MAYBE_TestQuotaOnCopyFile) {
int64 obstacle_file_size = 1;
expected_total_file_size += obstacle_file_size;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->Truncate(
AllowUsageIncrease(obstacle_file_size)->context(),
obstacle_file, obstacle_file_size));
@@ -2029,7 +2110,7 @@ TEST_F(ObfuscatedFileUtilTest, MAYBE_TestQuotaOnCopyFile) {
int64 to_file1_size = from_file_size;
expected_total_file_size += to_file1_size;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->CopyOrMoveFile(
AllowUsageIncrease(
PathCost(to_file1) + to_file1_size)->context(),
@@ -2038,7 +2119,7 @@ TEST_F(ObfuscatedFileUtilTest, MAYBE_TestQuotaOnCopyFile) {
true /* copy */));
ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize());
- ASSERT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE,
+ ASSERT_EQ(base::File::FILE_ERROR_NO_SPACE,
ofu()->CopyOrMoveFile(
DisallowUsageIncrease(
PathCost(to_file2) + from_file_size)->context(),
@@ -2049,7 +2130,7 @@ TEST_F(ObfuscatedFileUtilTest, MAYBE_TestQuotaOnCopyFile) {
int64 old_obstacle_file_size = obstacle_file_size;
obstacle_file_size = from_file_size;
expected_total_file_size += obstacle_file_size - old_obstacle_file_size;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->CopyOrMoveFile(
AllowUsageIncrease(
obstacle_file_size - old_obstacle_file_size)->context(),
@@ -2061,7 +2142,7 @@ TEST_F(ObfuscatedFileUtilTest, MAYBE_TestQuotaOnCopyFile) {
int64 old_from_file_size = from_file_size;
from_file_size = old_from_file_size - 1;
expected_total_file_size += from_file_size - old_from_file_size;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->Truncate(
AllowUsageIncrease(
from_file_size - old_from_file_size)->context(),
@@ -2077,7 +2158,7 @@ TEST_F(ObfuscatedFileUtilTest, MAYBE_TestQuotaOnCopyFile) {
obstacle_file_size - old_obstacle_file_size);
helper->context()->set_allowed_bytes_growth(
helper->context()->allowed_bytes_growth() - 1);
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->CopyOrMoveFile(
helper->context(),
from_file, obstacle_file,
@@ -2094,7 +2175,7 @@ TEST_F(ObfuscatedFileUtilTest, TestQuotaOnMoveFile) {
bool created;
int64 expected_total_file_size = 0;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(
AllowUsageIncrease(PathCost(from_file))->context(),
from_file, &created));
@@ -2103,7 +2184,7 @@ TEST_F(ObfuscatedFileUtilTest, TestQuotaOnMoveFile) {
int64 from_file_size = 1020;
expected_total_file_size += from_file_size;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->Truncate(
AllowUsageIncrease(from_file_size)->context(),
from_file, from_file_size));
@@ -2111,7 +2192,7 @@ TEST_F(ObfuscatedFileUtilTest, TestQuotaOnMoveFile) {
int64 to_file_size ALLOW_UNUSED = from_file_size;
from_file_size = 0;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->CopyOrMoveFile(
AllowUsageIncrease(-PathCost(from_file) +
PathCost(to_file))->context(),
@@ -2120,14 +2201,14 @@ TEST_F(ObfuscatedFileUtilTest, TestQuotaOnMoveFile) {
false /* move */));
ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize());
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(
AllowUsageIncrease(PathCost(from_file))->context(),
from_file, &created));
ASSERT_TRUE(created);
ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize());
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(
AllowUsageIncrease(PathCost(obstacle_file))->context(),
obstacle_file, &created));
@@ -2136,7 +2217,7 @@ TEST_F(ObfuscatedFileUtilTest, TestQuotaOnMoveFile) {
from_file_size = 1020;
expected_total_file_size += from_file_size;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->Truncate(
AllowUsageIncrease(from_file_size)->context(),
from_file, from_file_size));
@@ -2144,7 +2225,7 @@ TEST_F(ObfuscatedFileUtilTest, TestQuotaOnMoveFile) {
int64 obstacle_file_size = 1;
expected_total_file_size += obstacle_file_size;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->Truncate(
AllowUsageIncrease(1)->context(),
obstacle_file, obstacle_file_size));
@@ -2154,7 +2235,7 @@ TEST_F(ObfuscatedFileUtilTest, TestQuotaOnMoveFile) {
obstacle_file_size = from_file_size;
from_file_size = 0;
expected_total_file_size -= old_obstacle_file_size;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->CopyOrMoveFile(
AllowUsageIncrease(
-old_obstacle_file_size - PathCost(from_file))->context(),
@@ -2163,7 +2244,7 @@ TEST_F(ObfuscatedFileUtilTest, TestQuotaOnMoveFile) {
false /* move */));
ASSERT_EQ(expected_total_file_size, ComputeTotalFileSize());
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(
AllowUsageIncrease(PathCost(from_file))->context(),
from_file, &created));
@@ -2172,7 +2253,7 @@ TEST_F(ObfuscatedFileUtilTest, TestQuotaOnMoveFile) {
from_file_size = 10;
expected_total_file_size += from_file_size;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->Truncate(
AllowUsageIncrease(from_file_size)->context(),
from_file, from_file_size));
@@ -2185,7 +2266,7 @@ TEST_F(ObfuscatedFileUtilTest, TestQuotaOnMoveFile) {
expected_total_file_size -= old_obstacle_file_size;
scoped_ptr<FileSystemOperationContext> context =
LimitedContext(-old_obstacle_file_size - PathCost(from_file) - 1);
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->CopyOrMoveFile(
context.get(), from_file, obstacle_file,
FileSystemOperation::OPTION_NONE,
@@ -2201,263 +2282,148 @@ TEST_F(ObfuscatedFileUtilTest, TestQuotaOnRemove) {
FileSystemURL dfile2(CreateURLFromUTF8("dir/dfile2"));
bool created;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(
AllowUsageIncrease(PathCost(file))->context(),
file, &created));
ASSERT_TRUE(created);
ASSERT_EQ(0, ComputeTotalFileSize());
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->CreateDirectory(
AllowUsageIncrease(PathCost(dir))->context(),
dir, false, false));
ASSERT_EQ(0, ComputeTotalFileSize());
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(
AllowUsageIncrease(PathCost(dfile1))->context(),
dfile1, &created));
ASSERT_TRUE(created);
ASSERT_EQ(0, ComputeTotalFileSize());
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(
AllowUsageIncrease(PathCost(dfile2))->context(),
dfile2, &created));
ASSERT_TRUE(created);
ASSERT_EQ(0, ComputeTotalFileSize());
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->Truncate(
AllowUsageIncrease(340)->context(),
file, 340));
ASSERT_EQ(340, ComputeTotalFileSize());
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->Truncate(
AllowUsageIncrease(1020)->context(),
dfile1, 1020));
ASSERT_EQ(1360, ComputeTotalFileSize());
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->Truncate(
AllowUsageIncrease(120)->context(),
dfile2, 120));
ASSERT_EQ(1480, ComputeTotalFileSize());
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->DeleteFile(
AllowUsageIncrease(-PathCost(file) - 340)->context(),
file));
ASSERT_EQ(1140, ComputeTotalFileSize());
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::Remove(
file_system_context(), dir, true /* recursive */));
ASSERT_EQ(0, ComputeTotalFileSize());
}
TEST_F(ObfuscatedFileUtilTest, TestQuotaOnOpen) {
- FileSystemURL file(CreateURLFromUTF8("file"));
- base::PlatformFile file_handle;
- bool created;
+ FileSystemURL url(CreateURLFromUTF8("file"));
+ bool created;
// Creating a file.
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(
- AllowUsageIncrease(PathCost(file))->context(),
- file, &created));
+ AllowUsageIncrease(PathCost(url))->context(),
+ url, &created));
ASSERT_TRUE(created);
ASSERT_EQ(0, ComputeTotalFileSize());
// Opening it, which shouldn't change the usage.
- ASSERT_EQ(base::PLATFORM_FILE_OK,
- ofu()->CreateOrOpen(
- AllowUsageIncrease(0)->context(), file,
- base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE,
- &file_handle, &created));
+ base::File file =
+ ofu()->CreateOrOpen(AllowUsageIncrease(0)->context(), url,
+ base::File::FLAG_OPEN | base::File::FLAG_WRITE);
+ ASSERT_TRUE(file.IsValid());
ASSERT_EQ(0, ComputeTotalFileSize());
- EXPECT_TRUE(base::ClosePlatformFile(file_handle));
+ file.Close();
const int length = 33;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->Truncate(
- AllowUsageIncrease(length)->context(), file, length));
+ AllowUsageIncrease(length)->context(), url, length));
ASSERT_EQ(length, ComputeTotalFileSize());
// Opening it with CREATE_ALWAYS flag, which should truncate the file size.
- ASSERT_EQ(base::PLATFORM_FILE_OK,
- ofu()->CreateOrOpen(
- AllowUsageIncrease(-length)->context(), file,
- base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE,
- &file_handle, &created));
+ file = ofu()->CreateOrOpen(
+ AllowUsageIncrease(-length)->context(), url,
+ base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+ ASSERT_TRUE(file.IsValid());
ASSERT_EQ(0, ComputeTotalFileSize());
- EXPECT_TRUE(base::ClosePlatformFile(file_handle));
+ file.Close();
// Extending the file again.
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->Truncate(
- AllowUsageIncrease(length)->context(), file, length));
+ AllowUsageIncrease(length)->context(), url, length));
ASSERT_EQ(length, ComputeTotalFileSize());
// Opening it with TRUNCATED flag, which should truncate the file size.
- ASSERT_EQ(base::PLATFORM_FILE_OK,
- ofu()->CreateOrOpen(
- AllowUsageIncrease(-length)->context(), file,
- base::PLATFORM_FILE_OPEN_TRUNCATED | base::PLATFORM_FILE_WRITE,
- &file_handle, &created));
+ file = ofu()->CreateOrOpen(
+ AllowUsageIncrease(-length)->context(), url,
+ base::File::FLAG_OPEN_TRUNCATED | base::File::FLAG_WRITE);
+ ASSERT_TRUE(file.IsValid());
ASSERT_EQ(0, ComputeTotalFileSize());
- EXPECT_TRUE(base::ClosePlatformFile(file_handle));
+ file.Close();
}
TEST_F(ObfuscatedFileUtilTest, MaybeDropDatabasesAliveCase) {
- scoped_ptr<ObfuscatedFileUtil> file_util(
- ObfuscatedFileUtil::CreateForTesting(
- NULL, data_dir_path(),
- base::MessageLoopProxy::current().get()));
- file_util->InitOriginDatabase(GURL(), true /*create*/);
- ASSERT_TRUE(file_util->origin_database_ != NULL);
-
- // Callback to Drop DB is called while ObfuscatedFileUtilTest is still alive.
- file_util->db_flush_delay_seconds_ = 0;
- file_util->MarkUsed();
- base::RunLoop().RunUntilIdle();
-
- ASSERT_TRUE(file_util->origin_database_ == NULL);
+ MaybeDropDatabasesAliveCaseTestBody();
}
TEST_F(ObfuscatedFileUtilTest, MaybeDropDatabasesAlreadyDeletedCase) {
- // Run message loop after OFU is already deleted to make sure callback doesn't
- // cause a crash for use after free.
- {
- scoped_ptr<ObfuscatedFileUtil> file_util(
- ObfuscatedFileUtil::CreateForTesting(
- NULL, data_dir_path(),
- base::MessageLoopProxy::current().get()));
- file_util->InitOriginDatabase(GURL(), true /*create*/);
- file_util->db_flush_delay_seconds_ = 0;
- file_util->MarkUsed();
- }
-
- // At this point the callback is still in the message queue but OFU is gone.
- base::RunLoop().RunUntilIdle();
+ MaybeDropDatabasesAlreadyDeletedCaseTestBody();
}
TEST_F(ObfuscatedFileUtilTest, DestroyDirectoryDatabase_Isolated) {
- storage_policy_->AddIsolated(origin_);
- scoped_ptr<ObfuscatedFileUtil> file_util(
- ObfuscatedFileUtil::CreateForTesting(
- storage_policy_.get(), data_dir_path(),
- base::MessageLoopProxy::current().get()));
- const FileSystemURL url = FileSystemURL::CreateForTest(
- origin_, kFileSystemTypePersistent, base::FilePath());
-
- // Create DirectoryDatabase for isolated origin.
- SandboxDirectoryDatabase* db =
- file_util->GetDirectoryDatabase(url, true /* create */);
- ASSERT_TRUE(db != NULL);
-
- // Destory it.
- ASSERT_TRUE(file_util->DestroyDirectoryDatabase(
- url.origin(), GetTypeString(url.type())));
- ASSERT_TRUE(file_util->directories_.empty());
+ DestroyDirectoryDatabase_IsolatedTestBody();
}
TEST_F(ObfuscatedFileUtilTest, GetDirectoryDatabase_Isolated) {
- storage_policy_->AddIsolated(origin_);
- scoped_ptr<ObfuscatedFileUtil> file_util(
- ObfuscatedFileUtil::CreateForTesting(
- storage_policy_.get(), data_dir_path(),
- base::MessageLoopProxy::current().get()));
- const FileSystemURL url = FileSystemURL::CreateForTest(
- origin_, kFileSystemTypePersistent, base::FilePath());
-
- // Create DirectoryDatabase for isolated origin.
- SandboxDirectoryDatabase* db =
- file_util->GetDirectoryDatabase(url, true /* create */);
- ASSERT_TRUE(db != NULL);
- ASSERT_EQ(1U, file_util->directories_.size());
-
- // Remove isolated.
- storage_policy_->RemoveIsolated(url.origin());
-
- // This should still get the same database.
- SandboxDirectoryDatabase* db2 =
- file_util->GetDirectoryDatabase(url, false /* create */);
- ASSERT_EQ(db, db2);
+ GetDirectoryDatabase_IsolatedTestBody();
}
TEST_F(ObfuscatedFileUtilTest, MigrationBackFromIsolated) {
- std::string kFakeDirectoryData("0123456789");
- base::FilePath old_directory_db_path;
-
- // Initialize the directory with one origin using
- // SandboxIsolatedOriginDatabase.
- {
- std::string origin_string =
- webkit_database::GetIdentifierFromOrigin(origin_);
- SandboxIsolatedOriginDatabase database_old(
- origin_string, data_dir_path(),
- base::FilePath(
- SandboxIsolatedOriginDatabase::kObsoleteOriginDirectory));
- base::FilePath path;
- EXPECT_TRUE(database_old.GetPathForOrigin(origin_string, &path));
- EXPECT_FALSE(path.empty());
-
- // Populate the origin directory with some fake data.
- old_directory_db_path = data_dir_path().Append(path);
- ASSERT_TRUE(base::CreateDirectory(old_directory_db_path));
- EXPECT_EQ(static_cast<int>(kFakeDirectoryData.size()),
- file_util::WriteFile(old_directory_db_path.AppendASCII("dummy"),
- kFakeDirectoryData.data(),
- kFakeDirectoryData.size()));
- }
-
- storage_policy_->AddIsolated(origin_);
- scoped_ptr<ObfuscatedFileUtil> file_util(
- ObfuscatedFileUtil::CreateForTesting(
- storage_policy_.get(), data_dir_path(),
- base::MessageLoopProxy::current().get()));
- base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
- base::FilePath origin_directory = file_util->GetDirectoryForOrigin(
- origin_, true /* create */, &error);
- EXPECT_EQ(base::PLATFORM_FILE_OK, error);
-
- // The database is migrated from the old one.
- EXPECT_TRUE(base::DirectoryExists(origin_directory));
- EXPECT_FALSE(base::DirectoryExists(old_directory_db_path));
-
- // Check we see the same contents in the new origin directory.
- std::string origin_db_data;
- EXPECT_TRUE(base::PathExists(origin_directory.AppendASCII("dummy")));
- EXPECT_TRUE(base::ReadFileToString(
- origin_directory.AppendASCII("dummy"), &origin_db_data));
- EXPECT_EQ(kFakeDirectoryData, origin_db_data);
+ MigrationBackFromIsolatedTestBody();
}
TEST_F(ObfuscatedFileUtilTest, OpenPathInNonDirectory) {
- FileSystemURL file(CreateURLFromUTF8("file"));
+ FileSystemURL url(CreateURLFromUTF8("file"));
FileSystemURL path_in_file(CreateURLFromUTF8("file/file"));
bool created;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
- ofu()->EnsureFileExists(UnlimitedContext().get(), file, &created));
+ ASSERT_EQ(base::File::FILE_OK,
+ ofu()->EnsureFileExists(UnlimitedContext().get(), url, &created));
ASSERT_TRUE(created);
- created = false;
- base::PlatformFile file_handle = base::kInvalidPlatformFileValue;
- int file_flags = base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE;
- ASSERT_EQ(base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY,
- ofu()->CreateOrOpen(UnlimitedContext().get(),
- path_in_file,
- file_flags,
- &file_handle,
- &created));
- ASSERT_FALSE(created);
- ASSERT_EQ(base::kInvalidPlatformFileValue, file_handle);
+ int file_flags = base::File::FLAG_CREATE | base::File::FLAG_WRITE;
+ base::File file =
+ ofu()->CreateOrOpen(UnlimitedContext().get(), path_in_file, file_flags);
+ ASSERT_FALSE(file.IsValid());
+ ASSERT_EQ(base::File::FILE_ERROR_NOT_A_DIRECTORY, file.error_details());
- ASSERT_EQ(base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY,
+ ASSERT_EQ(base::File::FILE_ERROR_NOT_A_DIRECTORY,
ofu()->CreateDirectory(UnlimitedContext().get(),
path_in_file,
false /* exclusive */,
@@ -2471,20 +2437,20 @@ TEST_F(ObfuscatedFileUtilTest, CreateDirectory_NotADirectoryInRecursive) {
CreateURLFromUTF8("file/child/grandchild"));
bool created;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
ofu()->EnsureFileExists(UnlimitedContext().get(), file, &created));
ASSERT_TRUE(created);
- ASSERT_EQ(base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY,
+ ASSERT_EQ(base::File::FILE_ERROR_NOT_A_DIRECTORY,
ofu()->CreateDirectory(UnlimitedContext().get(),
path_in_file,
false /* exclusive */,
true /* recursive */));
- ASSERT_EQ(base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY,
+ ASSERT_EQ(base::File::FILE_ERROR_NOT_A_DIRECTORY,
ofu()->CreateDirectory(UnlimitedContext().get(),
path_in_file_in_file,
false /* exclusive */,
true /* recursive */));
}
-} // namespace fileapi
+} // namespace content
diff --git a/chromium/content/browser/fileapi/plugin_private_file_system_backend_unittest.cc b/chromium/content/browser/fileapi/plugin_private_file_system_backend_unittest.cc
index 0cc2f48bb00..08fb9e0fbc7 100644
--- a/chromium/content/browser/fileapi/plugin_private_file_system_backend_unittest.cc
+++ b/chromium/content/browser/fileapi/plugin_private_file_system_backend_unittest.cc
@@ -9,28 +9,33 @@
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/run_loop.h"
+#include "content/public/test/async_file_test_helper.h"
#include "content/public/test/test_file_system_context.h"
#include "content/public/test/test_file_system_options.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "webkit/browser/fileapi/async_file_test_helper.h"
#include "webkit/browser/fileapi/file_system_context.h"
#include "webkit/browser/fileapi/isolated_context.h"
#include "webkit/browser/fileapi/obfuscated_file_util.h"
#include "webkit/browser/fileapi/plugin_private_file_system_backend.h"
#include "webkit/common/fileapi/file_system_util.h"
-namespace fileapi {
+using content::AsyncFileTestHelper;
+using fileapi::FileSystemContext;
+using fileapi::FileSystemURL;
+using fileapi::IsolatedContext;
+
+namespace content {
namespace {
const GURL kOrigin("http://www.example.com");
const std::string kPlugin1("plugin1");
const std::string kPlugin2("plugin2");
-const FileSystemType kType = kFileSystemTypePluginPrivate;
+const fileapi::FileSystemType kType = fileapi::kFileSystemTypePluginPrivate;
const std::string kRootName = "pluginprivate";
-void DidOpenFileSystem(base::PlatformFileError* error_out,
- base::PlatformFileError error) {
+void DidOpenFileSystem(base::File::Error* error_out,
+ base::File::Error error) {
*error_out = error;
}
@@ -58,7 +63,7 @@ class PluginPrivateFileSystemBackendTest : public testing::Test {
root.virtual_path().AppendASCII(relative));
}
- PluginPrivateFileSystemBackend* backend() const {
+ fileapi::PluginPrivateFileSystemBackend* backend() const {
return context_->plugin_private_backend();
}
@@ -72,31 +77,32 @@ class PluginPrivateFileSystemBackendTest : public testing::Test {
TEST_F(PluginPrivateFileSystemBackendTest, OpenFileSystemBasic) {
const std::string filesystem_id1 = RegisterFileSystem();
- base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
+ base::File::Error error = base::File::FILE_ERROR_FAILED;
backend()->OpenPrivateFileSystem(
kOrigin, kType, filesystem_id1, kPlugin1,
- OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+ fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
base::Bind(&DidOpenFileSystem, &error));
base::RunLoop().RunUntilIdle();
- ASSERT_EQ(base::PLATFORM_FILE_OK, error);
+ ASSERT_EQ(base::File::FILE_OK, error);
// Run this again with FAIL_IF_NONEXISTENT to see if it succeeds.
const std::string filesystem_id2 = RegisterFileSystem();
- error = base::PLATFORM_FILE_ERROR_FAILED;
+ error = base::File::FILE_ERROR_FAILED;
backend()->OpenPrivateFileSystem(
kOrigin, kType, filesystem_id2, kPlugin1,
- OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT,
+ fileapi::OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT,
base::Bind(&DidOpenFileSystem, &error));
base::RunLoop().RunUntilIdle();
- ASSERT_EQ(base::PLATFORM_FILE_OK, error);
+ ASSERT_EQ(base::File::FILE_OK, error);
const GURL root_url(
- GetIsolatedFileSystemRootURIString(kOrigin, filesystem_id1, kRootName));
+ fileapi::GetIsolatedFileSystemRootURIString(
+ kOrigin, filesystem_id1, kRootName));
FileSystemURL file = CreateURL(root_url, "foo");
base::FilePath platform_path;
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::CreateFile(context_.get(), file));
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::GetPlatformPath(context_.get(), file,
&platform_path));
EXPECT_TRUE(base_path().AppendASCII("000").AppendASCII(kPlugin1).IsParent(
@@ -106,36 +112,38 @@ TEST_F(PluginPrivateFileSystemBackendTest, OpenFileSystemBasic) {
TEST_F(PluginPrivateFileSystemBackendTest, PluginIsolation) {
// Open filesystem for kPlugin1 and kPlugin2.
const std::string filesystem_id1 = RegisterFileSystem();
- base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
+ base::File::Error error = base::File::FILE_ERROR_FAILED;
backend()->OpenPrivateFileSystem(
kOrigin, kType, filesystem_id1, kPlugin1,
- OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+ fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
base::Bind(&DidOpenFileSystem, &error));
base::RunLoop().RunUntilIdle();
- ASSERT_EQ(base::PLATFORM_FILE_OK, error);
+ ASSERT_EQ(base::File::FILE_OK, error);
const std::string filesystem_id2 = RegisterFileSystem();
- error = base::PLATFORM_FILE_ERROR_FAILED;
+ error = base::File::FILE_ERROR_FAILED;
backend()->OpenPrivateFileSystem(
kOrigin, kType, filesystem_id2, kPlugin2,
- OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+ fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
base::Bind(&DidOpenFileSystem, &error));
base::RunLoop().RunUntilIdle();
- ASSERT_EQ(base::PLATFORM_FILE_OK, error);
+ ASSERT_EQ(base::File::FILE_OK, error);
// Create 'foo' in kPlugin1.
const GURL root_url1(
- GetIsolatedFileSystemRootURIString(kOrigin, filesystem_id1, kRootName));
+ fileapi::GetIsolatedFileSystemRootURIString(
+ kOrigin, filesystem_id1, kRootName));
FileSystemURL file1 = CreateURL(root_url1, "foo");
base::FilePath platform_path;
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::CreateFile(context_.get(), file1));
EXPECT_TRUE(AsyncFileTestHelper::FileExists(
context_.get(), file1, AsyncFileTestHelper::kDontCheckSize));
// See the same path is not available in kPlugin2.
const GURL root_url2(
- GetIsolatedFileSystemRootURIString(kOrigin, filesystem_id2, kRootName));
+ fileapi::GetIsolatedFileSystemRootURIString(
+ kOrigin, filesystem_id2, kRootName));
FileSystemURL file2 = CreateURL(root_url2, "foo");
EXPECT_FALSE(AsyncFileTestHelper::FileExists(
context_.get(), file2, AsyncFileTestHelper::kDontCheckSize));
@@ -144,4 +152,4 @@ TEST_F(PluginPrivateFileSystemBackendTest, PluginIsolation) {
// TODO(kinuko,nhiroki): also test if DeleteOriginDataOnFileThread
// works fine when there's multiple plugin partitions.
-} // namespace fileapi
+} // namespace content
diff --git a/chromium/content/browser/fileapi/recursive_operation_delegate_unittest.cc b/chromium/content/browser/fileapi/recursive_operation_delegate_unittest.cc
index d3671fe89f8..a96a21213a2 100644
--- a/chromium/content/browser/fileapi/recursive_operation_delegate_unittest.cc
+++ b/chromium/content/browser/fileapi/recursive_operation_delegate_unittest.cc
@@ -19,10 +19,14 @@
#include "webkit/browser/fileapi/file_system_operation.h"
#include "webkit/browser/fileapi/file_system_operation_runner.h"
-namespace fileapi {
+using fileapi::FileSystemContext;
+using fileapi::FileSystemOperationContext;
+using fileapi::FileSystemURL;
+
+namespace content {
namespace {
-class LoggingRecursiveOperation : public RecursiveOperationDelegate {
+class LoggingRecursiveOperation : public fileapi::RecursiveOperationDelegate {
public:
struct LogEntry {
enum Type {
@@ -37,7 +41,7 @@ class LoggingRecursiveOperation : public RecursiveOperationDelegate {
LoggingRecursiveOperation(FileSystemContext* file_system_context,
const FileSystemURL& root,
const StatusCallback& callback)
- : RecursiveOperationDelegate(file_system_context),
+ : fileapi::RecursiveOperationDelegate(file_system_context),
root_(root),
callback_(callback),
weak_factory_(this) {
@@ -67,13 +71,13 @@ class LoggingRecursiveOperation : public RecursiveOperationDelegate {
virtual void ProcessDirectory(const FileSystemURL& url,
const StatusCallback& callback) OVERRIDE {
RecordLogEntry(LogEntry::PROCESS_DIRECTORY, url);
- callback.Run(base::PLATFORM_FILE_OK);
+ callback.Run(base::File::FILE_OK);
}
virtual void PostProcessDirectory(const FileSystemURL& url,
const StatusCallback& callback) OVERRIDE {
RecordLogEntry(LogEntry::POST_PROCESS_DIRECTORY, url);
- callback.Run(base::PLATFORM_FILE_OK);
+ callback.Run(base::File::FILE_OK);
}
private:
@@ -85,16 +89,16 @@ class LoggingRecursiveOperation : public RecursiveOperationDelegate {
}
void DidGetMetadata(const StatusCallback& callback,
- base::PlatformFileError result,
- const base::PlatformFileInfo& file_info) {
- if (result != base::PLATFORM_FILE_OK) {
+ base::File::Error result,
+ const base::File::Info& file_info) {
+ if (result != base::File::FILE_OK) {
callback.Run(result);
return;
}
callback.Run(file_info.is_directory ?
- base::PLATFORM_FILE_ERROR_NOT_A_FILE :
- base::PLATFORM_FILE_OK);
+ base::File::FILE_ERROR_NOT_A_FILE :
+ base::File::FILE_OK);
}
FileSystemURL root_;
@@ -105,15 +109,16 @@ class LoggingRecursiveOperation : public RecursiveOperationDelegate {
DISALLOW_COPY_AND_ASSIGN(LoggingRecursiveOperation);
};
-void ReportStatus(base::PlatformFileError* out_error,
- base::PlatformFileError error) {
+void ReportStatus(base::File::Error* out_error,
+ base::File::Error error) {
DCHECK(out_error);
*out_error = error;
}
// To test the Cancel() during operation, calls Cancel() of |operation|
// after |counter| times message posting.
-void CallCancelLater(RecursiveOperationDelegate* operation, int counter) {
+void CallCancelLater(fileapi::RecursiveOperationDelegate* operation,
+ int counter) {
if (counter > 0) {
base::MessageLoopProxy::current()->PostTask(
FROM_HERE,
@@ -145,7 +150,7 @@ class RecursiveOperationDelegateTest : public testing::Test {
return make_scoped_ptr(context);
}
- FileSystemFileUtil* file_util() {
+ fileapi::FileSystemFileUtil* file_util() {
return sandbox_file_system_.file_util();
}
@@ -156,7 +161,7 @@ class RecursiveOperationDelegateTest : public testing::Test {
FileSystemURL CreateFile(const std::string& path) {
FileSystemURL url = URLForPath(path);
bool created = false;
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
file_util()->EnsureFileExists(NewContext().get(),
url, &created));
EXPECT_TRUE(created);
@@ -165,7 +170,7 @@ class RecursiveOperationDelegateTest : public testing::Test {
FileSystemURL CreateDirectory(const std::string& path) {
FileSystemURL url = URLForPath(path);
- EXPECT_EQ(base::PLATFORM_FILE_OK,
+ EXPECT_EQ(base::File::FILE_OK,
file_util()->CreateDirectory(NewContext().get(), url,
false /* exclusive */, true));
return url;
@@ -182,7 +187,7 @@ class RecursiveOperationDelegateTest : public testing::Test {
TEST_F(RecursiveOperationDelegateTest, RootIsFile) {
FileSystemURL src_file(CreateFile("src"));
- base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
+ base::File::Error error = base::File::FILE_ERROR_FAILED;
scoped_ptr<FileSystemOperationContext> context = NewContext();
scoped_ptr<LoggingRecursiveOperation> operation(
new LoggingRecursiveOperation(
@@ -190,7 +195,7 @@ TEST_F(RecursiveOperationDelegateTest, RootIsFile) {
base::Bind(&ReportStatus, &error)));
operation->RunRecursively();
base::RunLoop().RunUntilIdle();
- ASSERT_EQ(base::PLATFORM_FILE_OK, error);
+ ASSERT_EQ(base::File::FILE_OK, error);
const std::vector<LoggingRecursiveOperation::LogEntry>& log_entries =
operation->log_entries();
@@ -207,7 +212,7 @@ TEST_F(RecursiveOperationDelegateTest, RootIsDirectory) {
FileSystemURL src_file2(CreateFile("src/dir1/file2"));
FileSystemURL src_file3(CreateFile("src/dir1/file3"));
- base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
+ base::File::Error error = base::File::FILE_ERROR_FAILED;
scoped_ptr<FileSystemOperationContext> context = NewContext();
scoped_ptr<LoggingRecursiveOperation> operation(
new LoggingRecursiveOperation(
@@ -215,7 +220,7 @@ TEST_F(RecursiveOperationDelegateTest, RootIsDirectory) {
base::Bind(&ReportStatus, &error)));
operation->RunRecursively();
base::RunLoop().RunUntilIdle();
- ASSERT_EQ(base::PLATFORM_FILE_OK, error);
+ ASSERT_EQ(base::File::FILE_OK, error);
const std::vector<LoggingRecursiveOperation::LogEntry>& log_entries =
operation->log_entries();
@@ -263,7 +268,7 @@ TEST_F(RecursiveOperationDelegateTest, Cancel) {
FileSystemURL src_file1(CreateFile("src/file1"));
FileSystemURL src_file2(CreateFile("src/dir1/file2"));
- base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
+ base::File::Error error = base::File::FILE_ERROR_FAILED;
scoped_ptr<FileSystemOperationContext> context = NewContext();
scoped_ptr<LoggingRecursiveOperation> operation(
new LoggingRecursiveOperation(
@@ -274,7 +279,7 @@ TEST_F(RecursiveOperationDelegateTest, Cancel) {
// Invoke Cancel(), after 5 times message posting.
CallCancelLater(operation.get(), 5);
base::RunLoop().RunUntilIdle();
- ASSERT_EQ(base::PLATFORM_FILE_ERROR_ABORT, error);
+ ASSERT_EQ(base::File::FILE_ERROR_ABORT, error);
}
-} // namespace fileapi
+} // namespace content
diff --git a/chromium/content/browser/fileapi/sandbox_database_test_helper.cc b/chromium/content/browser/fileapi/sandbox_database_test_helper.cc
new file mode 100644
index 00000000000..3e295679590
--- /dev/null
+++ b/chromium/content/browser/fileapi/sandbox_database_test_helper.cc
@@ -0,0 +1,93 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/fileapi/sandbox_database_test_helper.h"
+
+#include <algorithm>
+#include <functional>
+#include <vector>
+
+#include "base/file_util.h"
+#include "base/files/file.h"
+#include "base/files/file_enumerator.h"
+#include "base/stl_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/common/fileapi/file_system_util.h"
+
+using fileapi::FilePathToString;
+
+namespace content {
+
+void CorruptDatabase(const base::FilePath& db_path,
+ leveldb::FileType type,
+ ptrdiff_t offset,
+ size_t size) {
+ base::FileEnumerator file_enum(db_path, false /* not recursive */,
+ base::FileEnumerator::DIRECTORIES | base::FileEnumerator::FILES);
+ base::FilePath file_path;
+ base::FilePath picked_file_path;
+ uint64 picked_file_number = kuint64max;
+
+ while (!(file_path = file_enum.Next()).empty()) {
+ uint64 number = kuint64max;
+ leveldb::FileType file_type;
+ EXPECT_TRUE(leveldb::ParseFileName(FilePathToString(file_path.BaseName()),
+ &number, &file_type));
+ if (file_type == type &&
+ (picked_file_number == kuint64max || picked_file_number < number)) {
+ picked_file_path = file_path;
+ picked_file_number = number;
+ }
+ }
+
+ EXPECT_FALSE(picked_file_path.empty());
+ EXPECT_NE(kuint64max, picked_file_number);
+
+ base::File file(picked_file_path,
+ base::File::FLAG_OPEN | base::File::FLAG_READ |
+ base::File::FLAG_WRITE);
+ ASSERT_TRUE(file.IsValid());
+ EXPECT_FALSE(file.created());
+
+ base::File::Info file_info;
+ EXPECT_TRUE(file.GetInfo(&file_info));
+ if (offset < 0)
+ offset += file_info.size;
+ EXPECT_GE(offset, 0);
+ EXPECT_LE(offset, file_info.size);
+
+ size = std::min(size, static_cast<size_t>(file_info.size - offset));
+
+ std::vector<char> buf(size);
+ int read_size = file.Read(offset, vector_as_array(&buf), buf.size());
+ EXPECT_LT(0, read_size);
+ EXPECT_GE(buf.size(), static_cast<size_t>(read_size));
+ buf.resize(read_size);
+
+ std::transform(buf.begin(), buf.end(), buf.begin(),
+ std::logical_not<char>());
+
+ int written_size = file.Write(offset, vector_as_array(&buf), buf.size());
+ EXPECT_GT(written_size, 0);
+ EXPECT_EQ(buf.size(), static_cast<size_t>(written_size));
+}
+
+void DeleteDatabaseFile(const base::FilePath& db_path,
+ leveldb::FileType type) {
+ base::FileEnumerator file_enum(db_path, false /* not recursive */,
+ base::FileEnumerator::DIRECTORIES | base::FileEnumerator::FILES);
+ base::FilePath file_path;
+ while (!(file_path = file_enum.Next()).empty()) {
+ uint64 number = kuint64max;
+ leveldb::FileType file_type;
+ EXPECT_TRUE(leveldb::ParseFileName(FilePathToString(file_path.BaseName()),
+ &number, &file_type));
+ if (file_type == type) {
+ base::DeleteFile(file_path, false);
+ // We may have multiple files for the same type, so don't break here.
+ }
+ }
+}
+
+} // namespace content
diff --git a/chromium/content/browser/fileapi/sandbox_database_test_helper.h b/chromium/content/browser/fileapi/sandbox_database_test_helper.h
new file mode 100644
index 00000000000..881bdff2ce3
--- /dev/null
+++ b/chromium/content/browser/fileapi/sandbox_database_test_helper.h
@@ -0,0 +1,28 @@
+// 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_SANDBOX_DATABASE_TEST_HELPER_H_
+#define CONTENT_BROWSER_FILEAPI_SANDBOX_DATABASE_TEST_HELPER_H_
+
+#include <cstddef>
+
+#include "third_party/leveldatabase/src/db/filename.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace content {
+
+void CorruptDatabase(const base::FilePath& db_path,
+ leveldb::FileType type,
+ ptrdiff_t offset,
+ size_t size);
+
+void DeleteDatabaseFile(const base::FilePath& db_path,
+ leveldb::FileType type);
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_FILEAPI_SANDBOX_DATABASE_TEST_HELPER_H_
diff --git a/chromium/content/browser/fileapi/sandbox_directory_database_unittest.cc b/chromium/content/browser/fileapi/sandbox_directory_database_unittest.cc
new file mode 100644
index 00000000000..6bc3c5669fa
--- /dev/null
+++ b/chromium/content/browser/fileapi/sandbox_directory_database_unittest.cc
@@ -0,0 +1,675 @@
+// 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 "webkit/browser/fileapi/sandbox_directory_database.h"
+
+#include <math.h>
+#include <limits>
+
+#include "base/file_util.h"
+#include "base/files/file.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "content/browser/fileapi/sandbox_database_test_helper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/leveldatabase/src/include/leveldb/db.h"
+#include "webkit/common/fileapi/file_system_util.h"
+
+#define FPL(x) FILE_PATH_LITERAL(x)
+
+using fileapi::FilePathToString;
+using fileapi::SandboxDirectoryDatabase;
+
+namespace content {
+
+namespace {
+const base::FilePath::CharType kDirectoryDatabaseName[] = FPL("Paths");
+}
+
+class SandboxDirectoryDatabaseTest : public testing::Test {
+ public:
+ typedef SandboxDirectoryDatabase::FileId FileId;
+ typedef SandboxDirectoryDatabase::FileInfo FileInfo;
+
+ SandboxDirectoryDatabaseTest() {
+ EXPECT_TRUE(base_.CreateUniqueTempDir());
+ InitDatabase();
+ }
+
+ SandboxDirectoryDatabase* db() {
+ return db_.get();
+ }
+
+ void InitDatabase() {
+ // Call CloseDatabase() to avoid having multiple database instances for
+ // single directory at once.
+ CloseDatabase();
+ db_.reset(new SandboxDirectoryDatabase(path(), NULL));
+ }
+
+ void CloseDatabase() {
+ db_.reset();
+ }
+
+ base::File::Error AddFileInfo(
+ FileId parent_id, const base::FilePath::StringType& name) {
+ FileId file_id;
+ FileInfo info;
+ info.parent_id = parent_id;
+ info.name = name;
+ return db_->AddFileInfo(info, &file_id);
+ }
+
+ void CreateDirectory(FileId parent_id,
+ const base::FilePath::StringType& name,
+ FileId* file_id_out) {
+ FileInfo info;
+ info.parent_id = parent_id;
+ info.name = name;
+ ASSERT_EQ(base::File::FILE_OK, db_->AddFileInfo(info, file_id_out));
+ }
+
+ void CreateFile(FileId parent_id,
+ const base::FilePath::StringType& name,
+ const base::FilePath::StringType& data_path,
+ FileId* file_id_out) {
+ FileId file_id;
+
+ FileInfo info;
+ info.parent_id = parent_id;
+ info.name = name;
+ info.data_path = base::FilePath(data_path).NormalizePathSeparators();
+ ASSERT_EQ(base::File::FILE_OK, db_->AddFileInfo(info, &file_id));
+
+ base::FilePath local_path = path().Append(data_path);
+ if (!base::DirectoryExists(local_path.DirName()))
+ ASSERT_TRUE(base::CreateDirectory(local_path.DirName()));
+
+ base::File file(local_path,
+ base::File::FLAG_CREATE | base::File::FLAG_WRITE);
+ ASSERT_TRUE(file.IsValid());
+ ASSERT_TRUE(file.created());
+
+ if (file_id_out)
+ *file_id_out = file_id;
+ }
+
+ void ClearDatabaseAndDirectory() {
+ db_.reset();
+ ASSERT_TRUE(base::DeleteFile(path(), true /* recursive */));
+ ASSERT_TRUE(base::CreateDirectory(path()));
+ db_.reset(new SandboxDirectoryDatabase(path(), NULL));
+ }
+
+ bool RepairDatabase() {
+ return db()->RepairDatabase(
+ FilePathToString(path().Append(kDirectoryDatabaseName)));
+ }
+
+ const base::FilePath& path() {
+ return base_.path();
+ }
+
+ // Makes link from |parent_id| to |child_id| with |name|.
+ void MakeHierarchyLink(FileId parent_id,
+ FileId child_id,
+ const base::FilePath::StringType& name) {
+ ASSERT_TRUE(db()->db_->Put(
+ leveldb::WriteOptions(),
+ "CHILD_OF:" + base::Int64ToString(parent_id) + ":" +
+ FilePathToString(base::FilePath(name)),
+ base::Int64ToString(child_id)).ok());
+ }
+
+ // Deletes link from parent of |file_id| to |file_id|.
+ void DeleteHierarchyLink(FileId file_id) {
+ FileInfo file_info;
+ ASSERT_TRUE(db()->GetFileInfo(file_id, &file_info));
+ ASSERT_TRUE(db()->db_->Delete(
+ leveldb::WriteOptions(),
+ "CHILD_OF:" + base::Int64ToString(file_info.parent_id) + ":" +
+ FilePathToString(base::FilePath(file_info.name))).ok());
+ }
+
+ protected:
+ // Common temp base for nondestructive uses.
+ base::ScopedTempDir base_;
+ scoped_ptr<SandboxDirectoryDatabase> db_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SandboxDirectoryDatabaseTest);
+};
+
+TEST_F(SandboxDirectoryDatabaseTest, TestMissingFileGetInfo) {
+ FileId file_id = 888;
+ FileInfo info;
+ EXPECT_FALSE(db()->GetFileInfo(file_id, &info));
+}
+
+TEST_F(SandboxDirectoryDatabaseTest, TestGetRootFileInfoBeforeCreate) {
+ FileId file_id = 0;
+ FileInfo info;
+ EXPECT_TRUE(db()->GetFileInfo(file_id, &info));
+ EXPECT_EQ(0, info.parent_id);
+ EXPECT_TRUE(info.name.empty());
+ EXPECT_TRUE(info.data_path.empty());
+}
+
+TEST_F(SandboxDirectoryDatabaseTest, TestMissingParentAddFileInfo) {
+ FileId parent_id = 7;
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_A_DIRECTORY,
+ AddFileInfo(parent_id, FILE_PATH_LITERAL("foo")));
+}
+
+TEST_F(SandboxDirectoryDatabaseTest, TestAddNameClash) {
+ FileInfo info;
+ FileId file_id;
+ info.parent_id = 0;
+ info.name = FILE_PATH_LITERAL("dir 0");
+ EXPECT_EQ(base::File::FILE_OK, db()->AddFileInfo(info, &file_id));
+
+ // Check for name clash in the root directory.
+ base::FilePath::StringType name = info.name;
+ EXPECT_EQ(base::File::FILE_ERROR_EXISTS, AddFileInfo(0, name));
+ name = FILE_PATH_LITERAL("dir 1");
+ EXPECT_EQ(base::File::FILE_OK, AddFileInfo(0, name));
+
+ name = FILE_PATH_LITERAL("subdir 0");
+ EXPECT_EQ(base::File::FILE_OK, AddFileInfo(file_id, name));
+
+ // Check for name clash in a subdirectory.
+ EXPECT_EQ(base::File::FILE_ERROR_EXISTS, AddFileInfo(file_id, name));
+ name = FILE_PATH_LITERAL("subdir 1");
+ EXPECT_EQ(base::File::FILE_OK, AddFileInfo(file_id, name));
+}
+
+TEST_F(SandboxDirectoryDatabaseTest, TestRenameNoMoveNameClash) {
+ FileInfo info;
+ FileId file_id0;
+ base::FilePath::StringType name0 = FILE_PATH_LITERAL("foo");
+ base::FilePath::StringType name1 = FILE_PATH_LITERAL("bar");
+ base::FilePath::StringType name2 = FILE_PATH_LITERAL("bas");
+ info.parent_id = 0;
+ info.name = name0;
+ EXPECT_EQ(base::File::FILE_OK, db()->AddFileInfo(info, &file_id0));
+ EXPECT_EQ(base::File::FILE_OK, AddFileInfo(0, name1));
+ info.name = name1;
+ EXPECT_FALSE(db()->UpdateFileInfo(file_id0, info));
+ info.name = name2;
+ EXPECT_TRUE(db()->UpdateFileInfo(file_id0, info));
+}
+
+TEST_F(SandboxDirectoryDatabaseTest, TestMoveSameNameNameClash) {
+ FileInfo info;
+ FileId file_id0;
+ FileId file_id1;
+ base::FilePath::StringType name0 = FILE_PATH_LITERAL("foo");
+ base::FilePath::StringType name1 = FILE_PATH_LITERAL("bar");
+ info.parent_id = 0;
+ info.name = name0;
+ EXPECT_EQ(base::File::FILE_OK, db()->AddFileInfo(info, &file_id0));
+ info.parent_id = file_id0;
+ EXPECT_EQ(base::File::FILE_OK, db()->AddFileInfo(info, &file_id1));
+ info.parent_id = 0;
+ EXPECT_FALSE(db()->UpdateFileInfo(file_id1, info));
+ info.name = name1;
+ EXPECT_TRUE(db()->UpdateFileInfo(file_id1, info));
+}
+
+TEST_F(SandboxDirectoryDatabaseTest, TestMoveRenameNameClash) {
+ FileInfo info;
+ FileId file_id0;
+ FileId file_id1;
+ base::FilePath::StringType name0 = FILE_PATH_LITERAL("foo");
+ base::FilePath::StringType name1 = FILE_PATH_LITERAL("bar");
+ base::FilePath::StringType name2 = FILE_PATH_LITERAL("bas");
+ info.parent_id = 0;
+ info.name = name0;
+ EXPECT_EQ(base::File::FILE_OK, db()->AddFileInfo(info, &file_id0));
+ info.parent_id = file_id0;
+ info.name = name1;
+ EXPECT_EQ(base::File::FILE_OK, db()->AddFileInfo(info, &file_id1));
+ info.parent_id = 0;
+ info.name = name0;
+ EXPECT_FALSE(db()->UpdateFileInfo(file_id1, info));
+ info.name = name1;
+ EXPECT_TRUE(db()->UpdateFileInfo(file_id1, info));
+ // Also test a successful move+rename.
+ info.parent_id = file_id0;
+ info.name = name2;
+ EXPECT_TRUE(db()->UpdateFileInfo(file_id1, info));
+}
+
+TEST_F(SandboxDirectoryDatabaseTest, TestRemoveWithChildren) {
+ FileInfo info;
+ FileId file_id0;
+ FileId file_id1;
+ info.parent_id = 0;
+ info.name = FILE_PATH_LITERAL("foo");
+ EXPECT_EQ(base::File::FILE_OK, db()->AddFileInfo(info, &file_id0));
+ info.parent_id = file_id0;
+ EXPECT_EQ(base::File::FILE_OK, db()->AddFileInfo(info, &file_id1));
+ EXPECT_FALSE(db()->RemoveFileInfo(file_id0));
+ EXPECT_TRUE(db()->RemoveFileInfo(file_id1));
+ EXPECT_TRUE(db()->RemoveFileInfo(file_id0));
+}
+
+TEST_F(SandboxDirectoryDatabaseTest, TestGetChildWithName) {
+ FileInfo info;
+ FileId file_id0;
+ FileId file_id1;
+ base::FilePath::StringType name0 = FILE_PATH_LITERAL("foo");
+ base::FilePath::StringType name1 = FILE_PATH_LITERAL("bar");
+ info.parent_id = 0;
+ info.name = name0;
+ EXPECT_EQ(base::File::FILE_OK, db()->AddFileInfo(info, &file_id0));
+ info.parent_id = file_id0;
+ info.name = name1;
+ EXPECT_EQ(base::File::FILE_OK, db()->AddFileInfo(info, &file_id1));
+ EXPECT_NE(file_id0, file_id1);
+
+ FileId check_file_id;
+ EXPECT_FALSE(db()->GetChildWithName(0, name1, &check_file_id));
+ EXPECT_TRUE(db()->GetChildWithName(0, name0, &check_file_id));
+ EXPECT_EQ(file_id0, check_file_id);
+ EXPECT_FALSE(db()->GetChildWithName(file_id0, name0, &check_file_id));
+ EXPECT_TRUE(db()->GetChildWithName(file_id0, name1, &check_file_id));
+ EXPECT_EQ(file_id1, check_file_id);
+}
+
+TEST_F(SandboxDirectoryDatabaseTest, TestGetFileWithPath) {
+ FileInfo info;
+ FileId file_id0;
+ FileId file_id1;
+ FileId file_id2;
+ base::FilePath::StringType name0 = FILE_PATH_LITERAL("foo");
+ base::FilePath::StringType name1 = FILE_PATH_LITERAL("bar");
+ base::FilePath::StringType name2 = FILE_PATH_LITERAL("dog");
+
+ info.parent_id = 0;
+ info.name = name0;
+ EXPECT_EQ(base::File::FILE_OK, db()->AddFileInfo(info, &file_id0));
+ info.parent_id = file_id0;
+ info.name = name1;
+ EXPECT_EQ(base::File::FILE_OK, db()->AddFileInfo(info, &file_id1));
+ EXPECT_NE(file_id0, file_id1);
+ info.parent_id = file_id1;
+ info.name = name2;
+ EXPECT_EQ(base::File::FILE_OK, db()->AddFileInfo(info, &file_id2));
+ EXPECT_NE(file_id0, file_id2);
+ EXPECT_NE(file_id1, file_id2);
+
+ FileId check_file_id;
+ base::FilePath path = base::FilePath(name0);
+ EXPECT_TRUE(db()->GetFileWithPath(path, &check_file_id));
+ EXPECT_EQ(file_id0, check_file_id);
+
+ path = path.Append(name1);
+ EXPECT_TRUE(db()->GetFileWithPath(path, &check_file_id));
+ EXPECT_EQ(file_id1, check_file_id);
+
+ path = path.Append(name2);
+ EXPECT_TRUE(db()->GetFileWithPath(path, &check_file_id));
+ EXPECT_EQ(file_id2, check_file_id);
+}
+
+TEST_F(SandboxDirectoryDatabaseTest, TestListChildren) {
+ // No children in the root.
+ std::vector<FileId> children;
+ EXPECT_TRUE(db()->ListChildren(0, &children));
+ EXPECT_TRUE(children.empty());
+
+ // One child in the root.
+ FileId file_id0;
+ FileInfo info;
+ info.parent_id = 0;
+ info.name = FILE_PATH_LITERAL("foo");
+ EXPECT_EQ(base::File::FILE_OK, db()->AddFileInfo(info, &file_id0));
+ EXPECT_TRUE(db()->ListChildren(0, &children));
+ EXPECT_EQ(children.size(), 1UL);
+ EXPECT_EQ(children[0], file_id0);
+
+ // Two children in the root.
+ FileId file_id1;
+ info.name = FILE_PATH_LITERAL("bar");
+ EXPECT_EQ(base::File::FILE_OK, db()->AddFileInfo(info, &file_id1));
+ EXPECT_TRUE(db()->ListChildren(0, &children));
+ EXPECT_EQ(2UL, children.size());
+ if (children[0] == file_id0) {
+ EXPECT_EQ(children[1], file_id1);
+ } else {
+ EXPECT_EQ(children[1], file_id0);
+ EXPECT_EQ(children[0], file_id1);
+ }
+
+ // No children in a subdirectory.
+ EXPECT_TRUE(db()->ListChildren(file_id0, &children));
+ EXPECT_TRUE(children.empty());
+
+ // One child in a subdirectory.
+ info.parent_id = file_id0;
+ info.name = FILE_PATH_LITERAL("foo");
+ FileId file_id2;
+ FileId file_id3;
+ EXPECT_EQ(base::File::FILE_OK, db()->AddFileInfo(info, &file_id2));
+ EXPECT_TRUE(db()->ListChildren(file_id0, &children));
+ EXPECT_EQ(1UL, children.size());
+ EXPECT_EQ(children[0], file_id2);
+
+ // Two children in a subdirectory.
+ info.name = FILE_PATH_LITERAL("bar");
+ EXPECT_EQ(base::File::FILE_OK, db()->AddFileInfo(info, &file_id3));
+ EXPECT_TRUE(db()->ListChildren(file_id0, &children));
+ EXPECT_EQ(2UL, children.size());
+ if (children[0] == file_id2) {
+ EXPECT_EQ(children[1], file_id3);
+ } else {
+ EXPECT_EQ(children[1], file_id2);
+ EXPECT_EQ(children[0], file_id3);
+ }
+}
+
+TEST_F(SandboxDirectoryDatabaseTest, TestUpdateModificationTime) {
+ FileInfo info0;
+ FileId file_id;
+ info0.parent_id = 0;
+ info0.name = FILE_PATH_LITERAL("name");
+ info0.data_path = base::FilePath(FILE_PATH_LITERAL("fake path"));
+ info0.modification_time = base::Time::Now();
+ EXPECT_EQ(base::File::FILE_OK, db()->AddFileInfo(info0, &file_id));
+ FileInfo info1;
+ EXPECT_TRUE(db()->GetFileInfo(file_id, &info1));
+ EXPECT_EQ(info0.name, info1.name);
+ EXPECT_EQ(info0.parent_id, info1.parent_id);
+ EXPECT_EQ(info0.data_path, info1.data_path);
+ EXPECT_EQ(
+ floor(info0.modification_time.ToDoubleT()),
+ info1.modification_time.ToDoubleT());
+
+ EXPECT_TRUE(db()->UpdateModificationTime(file_id, base::Time::UnixEpoch()));
+ EXPECT_TRUE(db()->GetFileInfo(file_id, &info1));
+ EXPECT_EQ(info0.name, info1.name);
+ EXPECT_EQ(info0.parent_id, info1.parent_id);
+ EXPECT_EQ(info0.data_path, info1.data_path);
+ EXPECT_NE(info0.modification_time, info1.modification_time);
+ EXPECT_EQ(
+ info1.modification_time.ToDoubleT(),
+ floor(base::Time::UnixEpoch().ToDoubleT()));
+
+ EXPECT_FALSE(db()->UpdateModificationTime(999, base::Time::UnixEpoch()));
+}
+
+TEST_F(SandboxDirectoryDatabaseTest, TestSimpleFileOperations) {
+ FileId file_id = 888;
+ FileInfo info0;
+ EXPECT_FALSE(db()->GetFileInfo(file_id, &info0));
+ info0.parent_id = 0;
+ info0.data_path = base::FilePath(FILE_PATH_LITERAL("foo"));
+ info0.name = FILE_PATH_LITERAL("file name");
+ info0.modification_time = base::Time::Now();
+ EXPECT_EQ(base::File::FILE_OK, db()->AddFileInfo(info0, &file_id));
+ FileInfo info1;
+ EXPECT_TRUE(db()->GetFileInfo(file_id, &info1));
+ EXPECT_EQ(info0.parent_id, info1.parent_id);
+ EXPECT_EQ(info0.data_path, info1.data_path);
+ EXPECT_EQ(info0.name, info1.name);
+ EXPECT_EQ(
+ floor(info0.modification_time.ToDoubleT()),
+ info1.modification_time.ToDoubleT());
+}
+
+TEST_F(SandboxDirectoryDatabaseTest, TestOverwritingMoveFileSrcDirectory) {
+ FileId directory_id;
+ FileInfo info0;
+ info0.parent_id = 0;
+ info0.name = FILE_PATH_LITERAL("directory");
+ info0.modification_time = base::Time::Now();
+ EXPECT_EQ(base::File::FILE_OK, db()->AddFileInfo(info0, &directory_id));
+
+ FileId file_id;
+ FileInfo info1;
+ info1.parent_id = 0;
+ info1.data_path = base::FilePath(FILE_PATH_LITERAL("bar"));
+ info1.name = FILE_PATH_LITERAL("file");
+ info1.modification_time = base::Time::UnixEpoch();
+ EXPECT_EQ(base::File::FILE_OK, db()->AddFileInfo(info1, &file_id));
+
+ EXPECT_FALSE(db()->OverwritingMoveFile(directory_id, file_id));
+}
+
+TEST_F(SandboxDirectoryDatabaseTest, TestOverwritingMoveFileDestDirectory) {
+ FileId file_id;
+ FileInfo info0;
+ info0.parent_id = 0;
+ info0.name = FILE_PATH_LITERAL("file");
+ info0.data_path = base::FilePath(FILE_PATH_LITERAL("bar"));
+ info0.modification_time = base::Time::Now();
+ EXPECT_EQ(base::File::FILE_OK, db()->AddFileInfo(info0, &file_id));
+
+ FileId directory_id;
+ FileInfo info1;
+ info1.parent_id = 0;
+ info1.name = FILE_PATH_LITERAL("directory");
+ info1.modification_time = base::Time::UnixEpoch();
+ EXPECT_EQ(base::File::FILE_OK, db()->AddFileInfo(info1, &directory_id));
+
+ EXPECT_FALSE(db()->OverwritingMoveFile(file_id, directory_id));
+}
+
+TEST_F(SandboxDirectoryDatabaseTest, TestOverwritingMoveFileSuccess) {
+ FileId file_id0;
+ FileInfo info0;
+ info0.parent_id = 0;
+ info0.data_path = base::FilePath(FILE_PATH_LITERAL("foo"));
+ info0.name = FILE_PATH_LITERAL("file name 0");
+ info0.modification_time = base::Time::Now();
+ EXPECT_EQ(base::File::FILE_OK, db()->AddFileInfo(info0, &file_id0));
+
+ FileInfo dir_info;
+ FileId dir_id;
+ dir_info.parent_id = 0;
+ dir_info.name = FILE_PATH_LITERAL("directory name");
+ EXPECT_EQ(base::File::FILE_OK, db()->AddFileInfo(dir_info, &dir_id));
+
+ FileId file_id1;
+ FileInfo info1;
+ info1.parent_id = dir_id;
+ info1.data_path = base::FilePath(FILE_PATH_LITERAL("bar"));
+ info1.name = FILE_PATH_LITERAL("file name 1");
+ info1.modification_time = base::Time::UnixEpoch();
+ EXPECT_EQ(base::File::FILE_OK, db()->AddFileInfo(info1, &file_id1));
+
+ EXPECT_TRUE(db()->OverwritingMoveFile(file_id0, file_id1));
+
+ FileInfo check_info;
+ FileId check_id;
+
+ EXPECT_FALSE(db()->GetFileWithPath(base::FilePath(info0.name), &check_id));
+ EXPECT_TRUE(db()->GetFileWithPath(
+ base::FilePath(dir_info.name).Append(info1.name), &check_id));
+ EXPECT_TRUE(db()->GetFileInfo(check_id, &check_info));
+
+ EXPECT_EQ(info0.data_path, check_info.data_path);
+}
+
+TEST_F(SandboxDirectoryDatabaseTest, TestGetNextInteger) {
+ int64 next = -1;
+ EXPECT_TRUE(db()->GetNextInteger(&next));
+ EXPECT_EQ(0, next);
+ EXPECT_TRUE(db()->GetNextInteger(&next));
+ EXPECT_EQ(1, next);
+ InitDatabase();
+ EXPECT_TRUE(db()->GetNextInteger(&next));
+ EXPECT_EQ(2, next);
+ EXPECT_TRUE(db()->GetNextInteger(&next));
+ EXPECT_EQ(3, next);
+ InitDatabase();
+ EXPECT_TRUE(db()->GetNextInteger(&next));
+ EXPECT_EQ(4, next);
+}
+
+TEST_F(SandboxDirectoryDatabaseTest, TestConsistencyCheck_Empty) {
+ EXPECT_TRUE(db()->IsFileSystemConsistent());
+
+ int64 next = -1;
+ EXPECT_TRUE(db()->GetNextInteger(&next));
+ EXPECT_EQ(0, next);
+ EXPECT_TRUE(db()->IsFileSystemConsistent());
+}
+
+TEST_F(SandboxDirectoryDatabaseTest, TestConsistencyCheck_Consistent) {
+ FileId dir_id;
+ CreateFile(0, FPL("foo"), FPL("hoge"), NULL);
+ CreateDirectory(0, FPL("bar"), &dir_id);
+ CreateFile(dir_id, FPL("baz"), FPL("fuga"), NULL);
+ CreateFile(dir_id, FPL("fizz"), FPL("buzz"), NULL);
+
+ EXPECT_TRUE(db()->IsFileSystemConsistent());
+}
+
+TEST_F(SandboxDirectoryDatabaseTest,
+ TestConsistencyCheck_BackingMultiEntry) {
+ const base::FilePath::CharType kBackingFileName[] = FPL("the celeb");
+ CreateFile(0, FPL("foo"), kBackingFileName, NULL);
+
+ EXPECT_TRUE(db()->IsFileSystemConsistent());
+ ASSERT_TRUE(base::DeleteFile(path().Append(kBackingFileName), false));
+ CreateFile(0, FPL("bar"), kBackingFileName, NULL);
+ EXPECT_FALSE(db()->IsFileSystemConsistent());
+}
+
+TEST_F(SandboxDirectoryDatabaseTest, TestConsistencyCheck_FileLost) {
+ const base::FilePath::CharType kBackingFileName[] = FPL("hoge");
+ CreateFile(0, FPL("foo"), kBackingFileName, NULL);
+
+ EXPECT_TRUE(db()->IsFileSystemConsistent());
+ ASSERT_TRUE(base::DeleteFile(path().Append(kBackingFileName), false));
+ EXPECT_TRUE(db()->IsFileSystemConsistent());
+}
+
+TEST_F(SandboxDirectoryDatabaseTest, TestConsistencyCheck_OrphanFile) {
+ CreateFile(0, FPL("foo"), FPL("hoge"), NULL);
+
+ EXPECT_TRUE(db()->IsFileSystemConsistent());
+
+ base::File file(path().Append(FPL("Orphan File")),
+ base::File::FLAG_CREATE | base::File::FLAG_WRITE);
+ ASSERT_TRUE(file.IsValid());
+ ASSERT_TRUE(file.created());
+ file.Close();
+
+ EXPECT_TRUE(db()->IsFileSystemConsistent());
+}
+
+TEST_F(SandboxDirectoryDatabaseTest, TestConsistencyCheck_RootLoop) {
+ EXPECT_TRUE(db()->IsFileSystemConsistent());
+ MakeHierarchyLink(0, 0, base::FilePath::StringType());
+ EXPECT_FALSE(db()->IsFileSystemConsistent());
+}
+
+TEST_F(SandboxDirectoryDatabaseTest, TestConsistencyCheck_DirectoryLoop) {
+ FileId dir1_id;
+ FileId dir2_id;
+ base::FilePath::StringType dir1_name = FPL("foo");
+ CreateDirectory(0, dir1_name, &dir1_id);
+ CreateDirectory(dir1_id, FPL("bar"), &dir2_id);
+
+ EXPECT_TRUE(db()->IsFileSystemConsistent());
+ MakeHierarchyLink(dir2_id, dir1_id, dir1_name);
+ EXPECT_FALSE(db()->IsFileSystemConsistent());
+}
+
+TEST_F(SandboxDirectoryDatabaseTest, TestConsistencyCheck_NameMismatch) {
+ FileId dir_id;
+ FileId file_id;
+ CreateDirectory(0, FPL("foo"), &dir_id);
+ CreateFile(dir_id, FPL("bar"), FPL("hoge/fuga/piyo"), &file_id);
+
+ EXPECT_TRUE(db()->IsFileSystemConsistent());
+ DeleteHierarchyLink(file_id);
+ MakeHierarchyLink(dir_id, file_id, FPL("baz"));
+ EXPECT_FALSE(db()->IsFileSystemConsistent());
+}
+
+TEST_F(SandboxDirectoryDatabaseTest, TestConsistencyCheck_WreckedEntries) {
+ FileId dir1_id;
+ FileId dir2_id;
+ CreateDirectory(0, FPL("foo"), &dir1_id);
+ CreateDirectory(dir1_id, FPL("bar"), &dir2_id);
+ CreateFile(dir2_id, FPL("baz"), FPL("fizz/buzz"), NULL);
+
+ EXPECT_TRUE(db()->IsFileSystemConsistent());
+ DeleteHierarchyLink(dir2_id); // Delete link from |dir1_id| to |dir2_id|.
+ EXPECT_FALSE(db()->IsFileSystemConsistent());
+}
+
+TEST_F(SandboxDirectoryDatabaseTest, TestRepairDatabase_Success) {
+ base::FilePath::StringType kFileName = FPL("bar");
+
+ FileId file_id_prev;
+ CreateFile(0, FPL("foo"), FPL("hoge"), NULL);
+ CreateFile(0, kFileName, FPL("fuga"), &file_id_prev);
+
+ const base::FilePath kDatabaseDirectory =
+ path().Append(kDirectoryDatabaseName);
+ CloseDatabase();
+ CorruptDatabase(kDatabaseDirectory, leveldb::kDescriptorFile,
+ 0, std::numeric_limits<size_t>::max());
+ InitDatabase();
+ EXPECT_FALSE(db()->IsFileSystemConsistent());
+
+ FileId file_id;
+ EXPECT_TRUE(db()->GetChildWithName(0, kFileName, &file_id));
+ EXPECT_EQ(file_id_prev, file_id);
+
+ EXPECT_TRUE(db()->IsFileSystemConsistent());
+}
+
+TEST_F(SandboxDirectoryDatabaseTest, TestRepairDatabase_Failure) {
+ base::FilePath::StringType kFileName = FPL("bar");
+
+ CreateFile(0, FPL("foo"), FPL("hoge"), NULL);
+ CreateFile(0, kFileName, FPL("fuga"), NULL);
+
+ const base::FilePath kDatabaseDirectory =
+ path().Append(kDirectoryDatabaseName);
+ CloseDatabase();
+ CorruptDatabase(kDatabaseDirectory, leveldb::kDescriptorFile,
+ 0, std::numeric_limits<size_t>::max());
+ CorruptDatabase(kDatabaseDirectory, leveldb::kLogFile,
+ -1, 1);
+ InitDatabase();
+ EXPECT_FALSE(db()->IsFileSystemConsistent());
+
+ FileId file_id;
+ EXPECT_FALSE(db()->GetChildWithName(0, kFileName, &file_id));
+ EXPECT_TRUE(db()->IsFileSystemConsistent());
+}
+
+TEST_F(SandboxDirectoryDatabaseTest, TestRepairDatabase_MissingManifest) {
+ base::FilePath::StringType kFileName = FPL("bar");
+
+ FileId file_id_prev;
+ CreateFile(0, FPL("foo"), FPL("hoge"), NULL);
+ CreateFile(0, kFileName, FPL("fuga"), &file_id_prev);
+
+ const base::FilePath kDatabaseDirectory =
+ path().Append(kDirectoryDatabaseName);
+ CloseDatabase();
+
+ DeleteDatabaseFile(kDatabaseDirectory, leveldb::kDescriptorFile);
+
+ InitDatabase();
+ EXPECT_FALSE(db()->IsFileSystemConsistent());
+
+ FileId file_id;
+ EXPECT_TRUE(db()->GetChildWithName(0, kFileName, &file_id));
+ EXPECT_EQ(file_id_prev, file_id);
+
+ EXPECT_TRUE(db()->IsFileSystemConsistent());
+}
+
+} // namespace content
diff --git a/chromium/content/browser/fileapi/sandbox_file_system_backend_delegate_unittest.cc b/chromium/content/browser/fileapi/sandbox_file_system_backend_delegate_unittest.cc
index c87043b74ca..935f6ce2fe4 100644
--- a/chromium/content/browser/fileapi/sandbox_file_system_backend_delegate_unittest.cc
+++ b/chromium/content/browser/fileapi/sandbox_file_system_backend_delegate_unittest.cc
@@ -15,14 +15,17 @@
#include "url/gurl.h"
#include "webkit/browser/fileapi/file_system_url.h"
-namespace fileapi {
+using fileapi::FileSystemURL;
+
+namespace content {
namespace {
FileSystemURL CreateFileSystemURL(const char* path) {
const GURL kOrigin("http://foo/");
- return FileSystemURL::CreateForTest(
- kOrigin, kFileSystemTypeTemporary, base::FilePath::FromUTF8Unsafe(path));
+ return fileapi::FileSystemURL::CreateForTest(
+ kOrigin, fileapi::kFileSystemTypeTemporary,
+ base::FilePath::FromUTF8Unsafe(path));
}
} // namespace
@@ -31,7 +34,7 @@ class SandboxFileSystemBackendDelegateTest : public testing::Test {
protected:
virtual void SetUp() {
ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
- delegate_.reset(new SandboxFileSystemBackendDelegate(
+ delegate_.reset(new fileapi::SandboxFileSystemBackendDelegate(
NULL /* quota_manager_proxy */,
base::MessageLoopProxy::current().get(),
data_dir_.path(),
@@ -39,44 +42,48 @@ class SandboxFileSystemBackendDelegateTest : public testing::Test {
CreateAllowFileAccessOptions()));
}
+ bool IsAccessValid(const FileSystemURL& url) const {
+ return delegate_->IsAccessValid(url);
+ }
+
base::ScopedTempDir data_dir_;
base::MessageLoop message_loop_;
- scoped_ptr<SandboxFileSystemBackendDelegate> delegate_;
+ scoped_ptr<fileapi::SandboxFileSystemBackendDelegate> delegate_;
};
TEST_F(SandboxFileSystemBackendDelegateTest, IsAccessValid) {
// Normal case.
- EXPECT_TRUE(delegate_->IsAccessValid(CreateFileSystemURL("a")));
+ EXPECT_TRUE(IsAccessValid(CreateFileSystemURL("a")));
// Access to a path with parent references ('..') should be disallowed.
- EXPECT_FALSE(delegate_->IsAccessValid(CreateFileSystemURL("a/../b")));
+ EXPECT_FALSE(IsAccessValid(CreateFileSystemURL("a/../b")));
// Access from non-allowed scheme should be disallowed.
- EXPECT_FALSE(delegate_->IsAccessValid(
+ EXPECT_FALSE(IsAccessValid(
FileSystemURL::CreateForTest(
- GURL("unknown://bar"), kFileSystemTypeTemporary,
+ GURL("unknown://bar"), fileapi::kFileSystemTypeTemporary,
base::FilePath::FromUTF8Unsafe("foo"))));
// Access with restricted name should be disallowed.
- EXPECT_FALSE(delegate_->IsAccessValid(CreateFileSystemURL(".")));
- EXPECT_FALSE(delegate_->IsAccessValid(CreateFileSystemURL("..")));
+ EXPECT_FALSE(IsAccessValid(CreateFileSystemURL(".")));
+ EXPECT_FALSE(IsAccessValid(CreateFileSystemURL("..")));
// This is also disallowed due to Windows XP parent path handling.
- EXPECT_FALSE(delegate_->IsAccessValid(CreateFileSystemURL("...")));
+ EXPECT_FALSE(IsAccessValid(CreateFileSystemURL("...")));
// These are identified as unsafe cases due to weird path handling
// on Windows.
- EXPECT_FALSE(delegate_->IsAccessValid(CreateFileSystemURL(" ..")));
- EXPECT_FALSE(delegate_->IsAccessValid(CreateFileSystemURL(".. ")));
+ EXPECT_FALSE(IsAccessValid(CreateFileSystemURL(" ..")));
+ EXPECT_FALSE(IsAccessValid(CreateFileSystemURL(".. ")));
// Similar but safe cases.
- EXPECT_TRUE(delegate_->IsAccessValid(CreateFileSystemURL(" .")));
- EXPECT_TRUE(delegate_->IsAccessValid(CreateFileSystemURL(". ")));
- EXPECT_TRUE(delegate_->IsAccessValid(CreateFileSystemURL("b.")));
- EXPECT_TRUE(delegate_->IsAccessValid(CreateFileSystemURL(".b")));
+ EXPECT_TRUE(IsAccessValid(CreateFileSystemURL(" .")));
+ EXPECT_TRUE(IsAccessValid(CreateFileSystemURL(". ")));
+ EXPECT_TRUE(IsAccessValid(CreateFileSystemURL("b.")));
+ EXPECT_TRUE(IsAccessValid(CreateFileSystemURL(".b")));
// A path that looks like a drive letter.
- EXPECT_TRUE(delegate_->IsAccessValid(CreateFileSystemURL("c:")));
+ EXPECT_TRUE(IsAccessValid(CreateFileSystemURL("c:")));
}
-} // namespace fileapi
+} // namespace content
diff --git a/chromium/content/browser/fileapi/sandbox_file_system_backend_unittest.cc b/chromium/content/browser/fileapi/sandbox_file_system_backend_unittest.cc
index 4bb12261a97..eda34612be8 100644
--- a/chromium/content/browser/fileapi/sandbox_file_system_backend_unittest.cc
+++ b/chromium/content/browser/fileapi/sandbox_file_system_backend_unittest.cc
@@ -20,6 +20,10 @@
#include "webkit/browser/fileapi/sandbox_file_system_backend_delegate.h"
#include "webkit/common/fileapi/file_system_util.h"
+using fileapi::FileSystemURL;
+using fileapi::SandboxFileSystemBackend;
+using fileapi::SandboxFileSystemBackendDelegate;
+
// PS stands for path separator.
#if defined(FILE_PATH_USES_WIN_SEPARATORS)
#define PS "\\"
@@ -27,7 +31,7 @@
#define PS "/"
#endif
-namespace fileapi {
+namespace content {
namespace {
@@ -66,10 +70,10 @@ const struct RootPathFileURITest {
"000" PS "p", NULL },
};
-void DidOpenFileSystem(base::PlatformFileError* error_out,
+void DidOpenFileSystem(base::File::Error* error_out,
const GURL& origin_url,
const std::string& name,
- base::PlatformFileError error) {
+ base::File::Error error) {
*error_out = error;
}
@@ -82,7 +86,7 @@ class SandboxFileSystemBackendTest : public testing::Test {
SetUpNewDelegate(CreateAllowFileAccessOptions());
}
- void SetUpNewDelegate(const FileSystemOptions& options) {
+ void SetUpNewDelegate(const fileapi::FileSystemOptions& options) {
delegate_.reset(new SandboxFileSystemBackendDelegate(
NULL /* quota_manager_proxy */,
base::MessageLoopProxy::current().get(),
@@ -91,12 +95,12 @@ class SandboxFileSystemBackendTest : public testing::Test {
options));
}
- void SetUpNewBackend(const FileSystemOptions& options) {
+ void SetUpNewBackend(const fileapi::FileSystemOptions& options) {
SetUpNewDelegate(options);
backend_.reset(new SandboxFileSystemBackend(delegate_.get()));
}
- SandboxFileSystemBackendDelegate::OriginEnumerator*
+ fileapi::SandboxFileSystemBackendDelegate::OriginEnumerator*
CreateOriginEnumerator() const {
return backend_->CreateOriginEnumerator();
}
@@ -111,14 +115,15 @@ class SandboxFileSystemBackendTest : public testing::Test {
bool GetRootPath(const GURL& origin_url,
fileapi::FileSystemType type,
- OpenFileSystemMode mode,
+ fileapi::OpenFileSystemMode mode,
base::FilePath* root_path) {
- base::PlatformFileError error = base::PLATFORM_FILE_OK;
- backend_->OpenFileSystem(
- origin_url, type, mode,
+ base::File::Error error = base::File::FILE_OK;
+ backend_->ResolveURL(
+ FileSystemURL::CreateForTest(origin_url, type, base::FilePath()),
+ mode,
base::Bind(&DidOpenFileSystem, &error));
base::RunLoop().RunUntilIdle();
- if (error != base::PLATFORM_FILE_OK)
+ if (error != base::File::FILE_OK)
return false;
base::FilePath returned_root_path =
delegate_->GetBaseDirectoryForOriginAndType(
@@ -135,8 +140,8 @@ class SandboxFileSystemBackendTest : public testing::Test {
base::ScopedTempDir data_dir_;
base::MessageLoop message_loop_;
- scoped_ptr<SandboxFileSystemBackendDelegate> delegate_;
- scoped_ptr<SandboxFileSystemBackend> backend_;
+ scoped_ptr<fileapi::SandboxFileSystemBackendDelegate> delegate_;
+ scoped_ptr<fileapi::SandboxFileSystemBackend> backend_;
};
TEST_F(SandboxFileSystemBackendTest, Empty) {
@@ -170,7 +175,7 @@ TEST_F(SandboxFileSystemBackendTest, EnumerateOrigins) {
}
for (size_t i = 0; i < persistent_size; ++i) {
CreateOriginTypeDirectory(GURL(persistent_origins[i]),
- kFileSystemTypePersistent);
+ fileapi::kFileSystemTypePersistent);
persistent_set.insert(GURL(persistent_origins[i]));
}
@@ -181,11 +186,11 @@ TEST_F(SandboxFileSystemBackendTest, EnumerateOrigins) {
GURL current;
while (!(current = enumerator->Next()).is_empty()) {
SCOPED_TRACE(testing::Message() << "EnumerateOrigin " << current.spec());
- if (enumerator->HasFileSystemType(kFileSystemTypeTemporary)) {
+ if (enumerator->HasFileSystemType(fileapi::kFileSystemTypeTemporary)) {
ASSERT_TRUE(temporary_set.find(current) != temporary_set.end());
++temporary_actual_size;
}
- if (enumerator->HasFileSystemType(kFileSystemTypePersistent)) {
+ if (enumerator->HasFileSystemType(fileapi::kFileSystemTypePersistent)) {
ASSERT_TRUE(persistent_set.find(current) != persistent_set.end());
++persistent_actual_size;
}
@@ -208,7 +213,7 @@ TEST_F(SandboxFileSystemBackendTest, GetRootPathCreateAndExamine) {
base::FilePath root_path;
EXPECT_TRUE(GetRootPath(GURL(kRootPathTestCases[i].origin_url),
kRootPathTestCases[i].type,
- OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+ fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
&root_path));
base::FilePath expected = file_system_path().AppendASCII(
@@ -228,7 +233,7 @@ TEST_F(SandboxFileSystemBackendTest, GetRootPathCreateAndExamine) {
base::FilePath root_path;
EXPECT_TRUE(GetRootPath(GURL(kRootPathTestCases[i].origin_url),
kRootPathTestCases[i].type,
- OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT,
+ fileapi::OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT,
&root_path));
ASSERT_TRUE(returned_root_path.size() > i);
EXPECT_EQ(returned_root_path[i].value(), root_path.value());
@@ -244,14 +249,14 @@ TEST_F(SandboxFileSystemBackendTest,
GURL origin_url("http://foo.com:1/");
base::FilePath root_path1;
- EXPECT_TRUE(GetRootPath(origin_url, kFileSystemTypeTemporary,
- OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+ EXPECT_TRUE(GetRootPath(origin_url, fileapi::kFileSystemTypeTemporary,
+ fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
&root_path1));
SetUpNewBackend(CreateDisallowFileAccessOptions());
base::FilePath root_path2;
- EXPECT_TRUE(GetRootPath(origin_url, kFileSystemTypeTemporary,
- OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT,
+ EXPECT_TRUE(GetRootPath(origin_url, fileapi::kFileSystemTypeTemporary,
+ fileapi::OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT,
&root_path2));
EXPECT_EQ(root_path1.value(), root_path2.value());
@@ -266,7 +271,7 @@ TEST_F(SandboxFileSystemBackendTest, GetRootPathGetWithoutCreate) {
<< kRootPathTestCases[i].expected_path);
EXPECT_FALSE(GetRootPath(GURL(kRootPathTestCases[i].origin_url),
kRootPathTestCases[i].type,
- OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT,
+ fileapi::OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT,
NULL));
}
}
@@ -281,7 +286,7 @@ TEST_F(SandboxFileSystemBackendTest, GetRootPathInIncognito) {
EXPECT_FALSE(
GetRootPath(GURL(kRootPathTestCases[i].origin_url),
kRootPathTestCases[i].type,
- OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+ fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
NULL));
}
}
@@ -294,7 +299,7 @@ TEST_F(SandboxFileSystemBackendTest, GetRootPathFileURI) {
EXPECT_FALSE(
GetRootPath(GURL(kRootPathFileURITestCases[i].origin_url),
kRootPathFileURITestCases[i].type,
- OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+ fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
NULL));
}
}
@@ -307,7 +312,7 @@ TEST_F(SandboxFileSystemBackendTest, GetRootPathFileURIWithAllowFlag) {
base::FilePath root_path;
EXPECT_TRUE(GetRootPath(GURL(kRootPathFileURITestCases[i].origin_url),
kRootPathFileURITestCases[i].type,
- OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+ fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
&root_path));
base::FilePath expected = file_system_path().AppendASCII(
kRootPathFileURITestCases[i].expected_path);
@@ -316,4 +321,4 @@ TEST_F(SandboxFileSystemBackendTest, GetRootPathFileURIWithAllowFlag) {
}
}
-} // namespace fileapi
+} // namespace content
diff --git a/chromium/content/browser/fileapi/sandbox_isolated_origin_database_unittest.cc b/chromium/content/browser/fileapi/sandbox_isolated_origin_database_unittest.cc
new file mode 100644
index 00000000000..fbf6a5eb25d
--- /dev/null
+++ b/chromium/content/browser/fileapi/sandbox_isolated_origin_database_unittest.cc
@@ -0,0 +1,43 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/fileapi/sandbox_isolated_origin_database.h"
+#include "webkit/browser/fileapi/sandbox_origin_database.h"
+
+using fileapi::SandboxIsolatedOriginDatabase;
+
+namespace content {
+
+namespace {
+const base::FilePath::CharType kOriginDirectory[] = FILE_PATH_LITERAL("iso");
+} // namespace
+
+TEST(SandboxIsolatedOriginDatabaseTest, BasicTest) {
+ base::ScopedTempDir dir;
+ ASSERT_TRUE(dir.CreateUniqueTempDir());
+
+ std::string kOrigin("origin");
+ SandboxIsolatedOriginDatabase database(kOrigin, dir.path(),
+ base::FilePath(kOriginDirectory));
+
+ EXPECT_TRUE(database.HasOriginPath(kOrigin));
+
+ base::FilePath path1, path2;
+
+ EXPECT_FALSE(database.GetPathForOrigin(std::string(), &path1));
+ EXPECT_FALSE(database.GetPathForOrigin("foo", &path1));
+
+ EXPECT_TRUE(database.HasOriginPath(kOrigin));
+ EXPECT_TRUE(database.GetPathForOrigin(kOrigin, &path1));
+ EXPECT_TRUE(database.GetPathForOrigin(kOrigin, &path2));
+ EXPECT_FALSE(path1.empty());
+ EXPECT_FALSE(path2.empty());
+ EXPECT_EQ(path1, path2);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/fileapi/sandbox_origin_database_unittest.cc b/chromium/content/browser/fileapi/sandbox_origin_database_unittest.cc
new file mode 100644
index 00000000000..cda5b77d9a8
--- /dev/null
+++ b/chromium/content/browser/fileapi/sandbox_origin_database_unittest.cc
@@ -0,0 +1,305 @@
+// 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 <algorithm>
+#include <functional>
+#include <limits>
+#include <string>
+#include <vector>
+
+#include "base/file_util.h"
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/stl_util.h"
+#include "content/browser/fileapi/sandbox_database_test_helper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/leveldatabase/src/db/filename.h"
+#include "third_party/leveldatabase/src/include/leveldb/db.h"
+#include "webkit/browser/fileapi/sandbox_origin_database.h"
+#include "webkit/common/fileapi/file_system_util.h"
+
+using fileapi::SandboxOriginDatabase;
+
+namespace content {
+
+namespace {
+const base::FilePath::CharType kFileSystemDirName[] =
+ FILE_PATH_LITERAL("File System");
+const base::FilePath::CharType kOriginDatabaseName[] =
+ FILE_PATH_LITERAL("Origins");
+} // namespace
+
+TEST(SandboxOriginDatabaseTest, BasicTest) {
+ base::ScopedTempDir dir;
+ ASSERT_TRUE(dir.CreateUniqueTempDir());
+ const base::FilePath kFSDir = dir.path().Append(kFileSystemDirName);
+ EXPECT_FALSE(base::PathExists(kFSDir));
+ EXPECT_TRUE(base::CreateDirectory(kFSDir));
+
+ SandboxOriginDatabase database(kFSDir, NULL);
+ std::string origin("origin");
+
+ EXPECT_FALSE(database.HasOriginPath(origin));
+ // Double-check to make sure that had no side effects.
+ EXPECT_FALSE(database.HasOriginPath(origin));
+
+ base::FilePath path0;
+ base::FilePath path1;
+
+ // Empty strings aren't valid origins.
+ EXPECT_FALSE(database.GetPathForOrigin(std::string(), &path0));
+
+ EXPECT_TRUE(database.GetPathForOrigin(origin, &path0));
+ EXPECT_TRUE(database.HasOriginPath(origin));
+ EXPECT_TRUE(database.GetPathForOrigin(origin, &path1));
+ EXPECT_FALSE(path0.empty());
+ EXPECT_FALSE(path1.empty());
+ EXPECT_EQ(path0, path1);
+
+ EXPECT_TRUE(base::PathExists(kFSDir.Append(kOriginDatabaseName)));
+}
+
+TEST(SandboxOriginDatabaseTest, TwoPathTest) {
+ base::ScopedTempDir dir;
+ ASSERT_TRUE(dir.CreateUniqueTempDir());
+ const base::FilePath kFSDir = dir.path().Append(kFileSystemDirName);
+ EXPECT_FALSE(base::PathExists(kFSDir));
+ EXPECT_TRUE(base::CreateDirectory(kFSDir));
+
+ SandboxOriginDatabase database(kFSDir, NULL);
+ std::string origin0("origin0");
+ std::string origin1("origin1");
+
+ EXPECT_FALSE(database.HasOriginPath(origin0));
+ EXPECT_FALSE(database.HasOriginPath(origin1));
+
+ base::FilePath path0;
+ base::FilePath path1;
+ EXPECT_TRUE(database.GetPathForOrigin(origin0, &path0));
+ EXPECT_TRUE(database.HasOriginPath(origin0));
+ EXPECT_FALSE(database.HasOriginPath(origin1));
+ EXPECT_TRUE(database.GetPathForOrigin(origin1, &path1));
+ EXPECT_TRUE(database.HasOriginPath(origin1));
+ EXPECT_FALSE(path0.empty());
+ EXPECT_FALSE(path1.empty());
+ EXPECT_NE(path0, path1);
+
+ EXPECT_TRUE(base::PathExists(kFSDir.Append(kOriginDatabaseName)));
+}
+
+TEST(SandboxOriginDatabaseTest, DropDatabaseTest) {
+ base::ScopedTempDir dir;
+ ASSERT_TRUE(dir.CreateUniqueTempDir());
+ const base::FilePath kFSDir = dir.path().Append(kFileSystemDirName);
+ EXPECT_FALSE(base::PathExists(kFSDir));
+ EXPECT_TRUE(base::CreateDirectory(kFSDir));
+
+ SandboxOriginDatabase database(kFSDir, NULL);
+ std::string origin("origin");
+
+ EXPECT_FALSE(database.HasOriginPath(origin));
+
+ base::FilePath path0;
+ EXPECT_TRUE(database.GetPathForOrigin(origin, &path0));
+ EXPECT_TRUE(database.HasOriginPath(origin));
+ EXPECT_FALSE(path0.empty());
+
+ EXPECT_TRUE(base::PathExists(kFSDir.Append(kOriginDatabaseName)));
+
+ database.DropDatabase();
+
+ base::FilePath path1;
+ EXPECT_TRUE(database.HasOriginPath(origin));
+ EXPECT_TRUE(database.GetPathForOrigin(origin, &path1));
+ EXPECT_FALSE(path1.empty());
+ EXPECT_EQ(path0, path1);
+}
+
+TEST(SandboxOriginDatabaseTest, DeleteOriginTest) {
+ base::ScopedTempDir dir;
+ ASSERT_TRUE(dir.CreateUniqueTempDir());
+ const base::FilePath kFSDir = dir.path().Append(kFileSystemDirName);
+ EXPECT_FALSE(base::PathExists(kFSDir));
+ EXPECT_TRUE(base::CreateDirectory(kFSDir));
+
+ SandboxOriginDatabase database(kFSDir, NULL);
+ std::string origin("origin");
+
+ EXPECT_FALSE(database.HasOriginPath(origin));
+ EXPECT_TRUE(database.RemovePathForOrigin(origin));
+
+ base::FilePath path0;
+ EXPECT_TRUE(database.GetPathForOrigin(origin, &path0));
+ EXPECT_TRUE(database.HasOriginPath(origin));
+ EXPECT_FALSE(path0.empty());
+
+ EXPECT_TRUE(database.RemovePathForOrigin(origin));
+ EXPECT_FALSE(database.HasOriginPath(origin));
+
+ base::FilePath path1;
+ EXPECT_TRUE(database.GetPathForOrigin(origin, &path1));
+ EXPECT_FALSE(path1.empty());
+ EXPECT_NE(path0, path1);
+}
+
+TEST(SandboxOriginDatabaseTest, ListOriginsTest) {
+ base::ScopedTempDir dir;
+ ASSERT_TRUE(dir.CreateUniqueTempDir());
+ const base::FilePath kFSDir = dir.path().Append(kFileSystemDirName);
+ EXPECT_FALSE(base::PathExists(kFSDir));
+ EXPECT_TRUE(base::CreateDirectory(kFSDir));
+
+ std::vector<SandboxOriginDatabase::OriginRecord> origins;
+
+ SandboxOriginDatabase database(kFSDir, NULL);
+ EXPECT_TRUE(database.ListAllOrigins(&origins));
+ EXPECT_TRUE(origins.empty());
+ origins.clear();
+
+ std::string origin0("origin0");
+ std::string origin1("origin1");
+
+ EXPECT_FALSE(database.HasOriginPath(origin0));
+ EXPECT_FALSE(database.HasOriginPath(origin1));
+
+ base::FilePath path0;
+ base::FilePath path1;
+ EXPECT_TRUE(database.GetPathForOrigin(origin0, &path0));
+ EXPECT_TRUE(database.ListAllOrigins(&origins));
+ EXPECT_EQ(origins.size(), 1UL);
+ EXPECT_EQ(origins[0].origin, origin0);
+ EXPECT_EQ(origins[0].path, path0);
+ origins.clear();
+ EXPECT_TRUE(database.GetPathForOrigin(origin1, &path1));
+ EXPECT_TRUE(database.ListAllOrigins(&origins));
+ EXPECT_EQ(origins.size(), 2UL);
+ if (origins[0].origin == origin0) {
+ EXPECT_EQ(origins[0].path, path0);
+ EXPECT_EQ(origins[1].origin, origin1);
+ EXPECT_EQ(origins[1].path, path1);
+ } else {
+ EXPECT_EQ(origins[0].origin, origin1);
+ EXPECT_EQ(origins[0].path, path1);
+ EXPECT_EQ(origins[1].origin, origin0);
+ EXPECT_EQ(origins[1].path, path0);
+ }
+}
+
+TEST(SandboxOriginDatabaseTest, DatabaseRecoveryTest) {
+ // Checks if SandboxOriginDatabase properly handles database corruption.
+ // In this test, we'll register some origins to the origin database, then
+ // corrupt database and its log file.
+ // After repairing, the origin database should be consistent even when some
+ // entries lost.
+
+ base::ScopedTempDir dir;
+ ASSERT_TRUE(dir.CreateUniqueTempDir());
+ const base::FilePath kFSDir = dir.path().Append(kFileSystemDirName);
+ const base::FilePath kDBDir = kFSDir.Append(kOriginDatabaseName);
+ EXPECT_FALSE(base::PathExists(kFSDir));
+ EXPECT_TRUE(base::CreateDirectory(kFSDir));
+
+ const std::string kOrigins[] = {
+ "foo.example.com",
+ "bar.example.com",
+ "baz.example.com",
+ "hoge.example.com",
+ "fuga.example.com",
+ };
+
+ scoped_ptr<SandboxOriginDatabase> database(
+ new SandboxOriginDatabase(kFSDir, NULL));
+ for (size_t i = 0; i < arraysize(kOrigins); ++i) {
+ base::FilePath path;
+ EXPECT_FALSE(database->HasOriginPath(kOrigins[i]));
+ EXPECT_TRUE(database->GetPathForOrigin(kOrigins[i], &path));
+ EXPECT_FALSE(path.empty());
+ EXPECT_TRUE(database->GetPathForOrigin(kOrigins[i], &path));
+
+ if (i != 1)
+ EXPECT_TRUE(base::CreateDirectory(kFSDir.Append(path)));
+ }
+ database.reset();
+
+ const base::FilePath kGarbageDir = kFSDir.AppendASCII("foo");
+ const base::FilePath kGarbageFile = kGarbageDir.AppendASCII("bar");
+ EXPECT_TRUE(base::CreateDirectory(kGarbageDir));
+ base::File file(kGarbageFile,
+ base::File::FLAG_CREATE | base::File::FLAG_WRITE);
+ EXPECT_TRUE(file.IsValid());
+ file.Close();
+
+ // Corrupt database itself and last log entry to drop last 1 database
+ // operation. The database should detect the corruption and should recover
+ // its consistency after recovery.
+ CorruptDatabase(kDBDir, leveldb::kDescriptorFile,
+ 0, std::numeric_limits<size_t>::max());
+ CorruptDatabase(kDBDir, leveldb::kLogFile, -1, 1);
+
+ base::FilePath path;
+ database.reset(new SandboxOriginDatabase(kFSDir, NULL));
+ std::vector<SandboxOriginDatabase::OriginRecord> origins_in_db;
+ EXPECT_TRUE(database->ListAllOrigins(&origins_in_db));
+
+ // Expect all but last added origin will be repaired back, and kOrigins[1]
+ // should be dropped due to absence of backing directory.
+ EXPECT_EQ(arraysize(kOrigins) - 2, origins_in_db.size());
+
+ const std::string kOrigin("piyo.example.org");
+ EXPECT_FALSE(database->HasOriginPath(kOrigin));
+ EXPECT_TRUE(database->GetPathForOrigin(kOrigin, &path));
+ EXPECT_FALSE(path.empty());
+ EXPECT_TRUE(database->HasOriginPath(kOrigin));
+
+ EXPECT_FALSE(base::PathExists(kGarbageFile));
+ EXPECT_FALSE(base::PathExists(kGarbageDir));
+}
+
+TEST(SandboxOriginDatabaseTest, DatabaseRecoveryForMissingDBFileTest) {
+ const leveldb::FileType kLevelDBFileTypes[] = {
+ leveldb::kLogFile,
+ leveldb::kDBLockFile,
+ leveldb::kTableFile,
+ leveldb::kDescriptorFile,
+ leveldb::kCurrentFile,
+ leveldb::kTempFile,
+ leveldb::kInfoLogFile,
+ };
+
+ for (size_t i = 0; i < arraysize(kLevelDBFileTypes); ++i) {
+ base::ScopedTempDir dir;
+ ASSERT_TRUE(dir.CreateUniqueTempDir());
+ const base::FilePath kFSDir = dir.path().Append(kFileSystemDirName);
+ const base::FilePath kDBDir = kFSDir.Append(kOriginDatabaseName);
+ EXPECT_FALSE(base::PathExists(kFSDir));
+ EXPECT_TRUE(base::CreateDirectory(kFSDir));
+
+ const std::string kOrigin = "foo.example.com";
+ base::FilePath path;
+
+ scoped_ptr<SandboxOriginDatabase> database(
+ new SandboxOriginDatabase(kFSDir, NULL));
+ EXPECT_FALSE(database->HasOriginPath(kOrigin));
+ EXPECT_TRUE(database->GetPathForOrigin(kOrigin, &path));
+ EXPECT_FALSE(path.empty());
+ EXPECT_TRUE(database->GetPathForOrigin(kOrigin, &path));
+ EXPECT_TRUE(base::CreateDirectory(kFSDir.Append(path)));
+ database.reset();
+
+ DeleteDatabaseFile(kDBDir, kLevelDBFileTypes[i]);
+
+ database.reset(new SandboxOriginDatabase(kFSDir, NULL));
+ std::vector<SandboxOriginDatabase::OriginRecord> origins_in_db;
+ EXPECT_TRUE(database->ListAllOrigins(&origins_in_db));
+
+ const std::string kOrigin2("piyo.example.org");
+ EXPECT_FALSE(database->HasOriginPath(kOrigin2));
+ EXPECT_TRUE(database->GetPathForOrigin(kOrigin2, &path));
+ EXPECT_FALSE(path.empty());
+ EXPECT_TRUE(database->HasOriginPath(kOrigin2));
+ }
+}
+
+} // namespace content
diff --git a/chromium/content/browser/fileapi/sandbox_prioritized_origin_database_unittest.cc b/chromium/content/browser/fileapi/sandbox_prioritized_origin_database_unittest.cc
new file mode 100644
index 00000000000..e81dc7d18a0
--- /dev/null
+++ b/chromium/content/browser/fileapi/sandbox_prioritized_origin_database_unittest.cc
@@ -0,0 +1,217 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/fileapi/sandbox_origin_database.h"
+#include "webkit/browser/fileapi/sandbox_prioritized_origin_database.h"
+
+using fileapi::SandboxOriginDatabase;
+using fileapi::SandboxOriginDatabaseInterface;
+using fileapi::SandboxPrioritizedOriginDatabase;
+
+namespace content {
+
+TEST(SandboxPrioritizedOriginDatabaseTest, BasicTest) {
+ base::ScopedTempDir dir;
+ base::FilePath path;
+ ASSERT_TRUE(dir.CreateUniqueTempDir());
+
+ const std::string kOrigin1("origin1");
+ const std::string kOrigin2("origin2");
+
+ SandboxPrioritizedOriginDatabase database(dir.path(), NULL);
+
+ // Set the kOrigin1 as a parimary origin.
+ EXPECT_TRUE(database.InitializePrimaryOrigin(kOrigin1));
+
+ // Add two origins.
+ EXPECT_TRUE(database.GetPathForOrigin(kOrigin1, &path));
+ EXPECT_TRUE(database.GetPathForOrigin(kOrigin2, &path));
+
+ // Verify them.
+ EXPECT_TRUE(database.HasOriginPath(kOrigin1));
+ EXPECT_TRUE(database.GetPathForOrigin(kOrigin1, &path));
+ EXPECT_FALSE(path.empty());
+ EXPECT_TRUE(database.HasOriginPath(kOrigin2));
+ EXPECT_TRUE(database.GetPathForOrigin(kOrigin2, &path));
+ EXPECT_FALSE(path.empty());
+
+ std::vector<SandboxOriginDatabaseInterface::OriginRecord> origins;
+ database.ListAllOrigins(&origins);
+ ASSERT_EQ(2U, origins.size());
+ EXPECT_TRUE(origins[0].origin == kOrigin1 ||
+ origins[1].origin == kOrigin1);
+ EXPECT_TRUE(origins[0].origin == kOrigin2 ||
+ origins[1].origin == kOrigin2);
+ EXPECT_NE(origins[0].path, origins[1].path);
+
+ // Try remove path for kOrigin1.
+ database.RemovePathForOrigin(kOrigin1);
+
+ // Verify the removal.
+ EXPECT_FALSE(database.HasOriginPath(kOrigin1));
+ EXPECT_TRUE(database.HasOriginPath(kOrigin2));
+ database.ListAllOrigins(&origins);
+ ASSERT_EQ(1U, origins.size());
+ EXPECT_EQ(kOrigin2, origins[0].origin);
+
+ // Try remove path for kOrigin2.
+ database.RemovePathForOrigin(kOrigin2);
+
+ // Verify the removal.
+ EXPECT_FALSE(database.HasOriginPath(kOrigin1));
+ EXPECT_FALSE(database.HasOriginPath(kOrigin2));
+ database.ListAllOrigins(&origins);
+ EXPECT_TRUE(origins.empty());
+}
+
+TEST(SandboxPrioritizedOriginDatabaseTest, SetPrimaryLaterTest) {
+ base::ScopedTempDir dir;
+ base::FilePath path;
+ ASSERT_TRUE(dir.CreateUniqueTempDir());
+
+ const std::string kOrigin1("origin1");
+ const std::string kOrigin2("origin2");
+
+ SandboxPrioritizedOriginDatabase database(dir.path(), NULL);
+
+ EXPECT_TRUE(database.GetPrimaryOrigin().empty());
+
+ EXPECT_TRUE(database.GetPathForOrigin(kOrigin1, &path));
+ EXPECT_TRUE(database.GetPathForOrigin(kOrigin2, &path));
+
+ // Set the kOrigin1 as a parimary origin.
+ EXPECT_TRUE(database.InitializePrimaryOrigin(kOrigin1));
+ EXPECT_EQ(kOrigin1, database.GetPrimaryOrigin());
+
+ // Regardless of whether it is initialized as primary or not
+ // they should just work.
+ EXPECT_TRUE(database.HasOriginPath(kOrigin1));
+ EXPECT_TRUE(database.GetPathForOrigin(kOrigin1, &path));
+ EXPECT_FALSE(path.empty());
+ EXPECT_TRUE(database.HasOriginPath(kOrigin2));
+ EXPECT_TRUE(database.GetPathForOrigin(kOrigin2, &path));
+ EXPECT_FALSE(path.empty());
+}
+
+TEST(SandboxPrioritizedOriginDatabaseTest, LostPrimaryOriginFileTest) {
+ base::ScopedTempDir dir;
+ base::FilePath path;
+ ASSERT_TRUE(dir.CreateUniqueTempDir());
+
+ const std::string kOrigin1("origin1");
+ const std::string kData("foo");
+
+ SandboxPrioritizedOriginDatabase database(dir.path(), NULL);
+
+ EXPECT_TRUE(database.GetPrimaryOrigin().empty());
+
+ // Set the kOrigin1 as a parimary origin.
+ EXPECT_TRUE(database.InitializePrimaryOrigin(kOrigin1));
+ EXPECT_EQ(kOrigin1, database.GetPrimaryOrigin());
+
+ // Make sure it works.
+ EXPECT_TRUE(database.HasOriginPath(kOrigin1));
+ EXPECT_TRUE(database.GetPathForOrigin(kOrigin1, &path));
+
+ // Reset the database.
+ database.DropDatabase();
+
+ // kOrigin1 should still be marked as primary.
+ EXPECT_TRUE(database.HasOriginPath(kOrigin1));
+ EXPECT_TRUE(database.GetPathForOrigin(kOrigin1, &path));
+
+ // Corrupt the primary origin file.
+ base::WriteFile(database.primary_origin_file(), kData.data(), kData.size());
+
+ // Reset the database.
+ database.DropDatabase();
+
+ // kOrigin1 is no longer marked as primary, and unfortunately we fail
+ // to find the data for the origin.
+ EXPECT_FALSE(database.HasOriginPath(kOrigin1));
+}
+
+TEST(SandboxPrioritizedOriginDatabaseTest, MigrationTest) {
+ base::ScopedTempDir dir;
+ ASSERT_TRUE(dir.CreateUniqueTempDir());
+
+ const std::string kOrigin1("origin1");
+ const std::string kOrigin2("origin2");
+ const std::string kFakeDirectoryData1("0123456789");
+ const std::string kFakeDirectoryData2("abcde");
+ base::FilePath old_dir_db_path1, old_dir_db_path2;
+ base::FilePath path1, path2;
+
+ // Initialize the directory with two origins using the regular
+ // SandboxOriginDatabase.
+ {
+ SandboxOriginDatabase database_old(dir.path(), NULL);
+ base::FilePath old_db_path = database_old.GetDatabasePath();
+ EXPECT_FALSE(base::PathExists(old_db_path));
+
+ // Initialize paths for kOrigin1 and kOrigin2.
+ EXPECT_TRUE(database_old.GetPathForOrigin(kOrigin1, &path1));
+ EXPECT_FALSE(path1.empty());
+ EXPECT_TRUE(database_old.GetPathForOrigin(kOrigin2, &path2));
+ EXPECT_FALSE(path2.empty());
+
+ EXPECT_TRUE(base::DirectoryExists(old_db_path));
+
+ // Populate the origin directory with some fake data.
+ old_dir_db_path1 = dir.path().Append(path1);
+ ASSERT_TRUE(base::CreateDirectory(old_dir_db_path1));
+ EXPECT_EQ(static_cast<int>(kFakeDirectoryData1.size()),
+ base::WriteFile(old_dir_db_path1.AppendASCII("dummy"),
+ kFakeDirectoryData1.data(),
+ kFakeDirectoryData1.size()));
+ old_dir_db_path2 = dir.path().Append(path2);
+ ASSERT_TRUE(base::CreateDirectory(old_dir_db_path2));
+ EXPECT_EQ(static_cast<int>(kFakeDirectoryData2.size()),
+ base::WriteFile(old_dir_db_path2.AppendASCII("dummy"),
+ kFakeDirectoryData2.data(),
+ kFakeDirectoryData2.size()));
+ }
+
+ // Re-open the directory using sandboxPrioritizedOriginDatabase.
+ SandboxPrioritizedOriginDatabase database(dir.path(), NULL);
+
+ // Set the kOrigin1 as a parimary origin.
+ // (Trying to initialize another origin should fail).
+ EXPECT_TRUE(database.InitializePrimaryOrigin(kOrigin1));
+ EXPECT_FALSE(database.InitializePrimaryOrigin(kOrigin2));
+
+ EXPECT_EQ(kOrigin1, database.GetPrimaryOrigin());
+
+ // Regardless of whether the origin is registered as primary or not
+ // it should just work.
+ EXPECT_TRUE(database.HasOriginPath(kOrigin1));
+ EXPECT_TRUE(database.GetPathForOrigin(kOrigin1, &path1));
+ EXPECT_TRUE(database.HasOriginPath(kOrigin2));
+ EXPECT_TRUE(database.GetPathForOrigin(kOrigin2, &path2));
+
+ // The directory content must be kept (or migrated if necessary) as well.
+ std::string origin_db_data;
+ base::FilePath dir_db_path = dir.path().Append(path1);
+ EXPECT_TRUE(base::PathExists(dir_db_path.AppendASCII("dummy")));
+ EXPECT_TRUE(base::ReadFileToString(
+ dir_db_path.AppendASCII("dummy"), &origin_db_data));
+ EXPECT_EQ(kFakeDirectoryData1, origin_db_data);
+
+ origin_db_data.clear();
+ dir_db_path = dir.path().Append(path2);
+ EXPECT_TRUE(base::PathExists(dir_db_path.AppendASCII("dummy")));
+ EXPECT_TRUE(base::ReadFileToString(
+ dir_db_path.AppendASCII("dummy"), &origin_db_data));
+ EXPECT_EQ(kFakeDirectoryData2, origin_db_data);
+
+ // After the migration the kOrigin1 directory database path must be gone.
+ EXPECT_FALSE(base::PathExists(old_dir_db_path1));
+ EXPECT_TRUE(base::PathExists(old_dir_db_path2));
+}
+
+} // namespace content
diff --git a/chromium/content/browser/fileapi/timed_task_helper_unittest.cc b/chromium/content/browser/fileapi/timed_task_helper_unittest.cc
new file mode 100644
index 00000000000..59e465c98c5
--- /dev/null
+++ b/chromium/content/browser/fileapi/timed_task_helper_unittest.cc
@@ -0,0 +1,85 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/run_loop.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/fileapi/timed_task_helper.h"
+
+using fileapi::TimedTaskHelper;
+
+namespace content {
+
+namespace {
+
+class Embedder {
+ public:
+ Embedder()
+ : timer_(base::MessageLoopProxy::current().get()), timer_fired_(false) {}
+
+ void OnTimerFired() {
+ timer_fired_ = true;
+ }
+
+ TimedTaskHelper* timer() { return &timer_; }
+ bool timer_fired() const { return timer_fired_; }
+
+ private:
+ TimedTaskHelper timer_;
+ bool timer_fired_;
+};
+
+} // namespace
+
+TEST(TimedTaskHelper, FireTimerWhenAlive) {
+ base::MessageLoop message_loop;
+ Embedder embedder;
+
+ ASSERT_FALSE(embedder.timer_fired());
+ ASSERT_FALSE(embedder.timer()->IsRunning());
+
+ embedder.timer()->Start(
+ FROM_HERE,
+ base::TimeDelta::FromSeconds(0),
+ base::Bind(&Embedder::OnTimerFired, base::Unretained(&embedder)));
+
+ ASSERT_TRUE(embedder.timer()->IsRunning());
+ embedder.timer()->Reset();
+ ASSERT_TRUE(embedder.timer()->IsRunning());
+
+ base::RunLoop().RunUntilIdle();
+
+ ASSERT_TRUE(embedder.timer_fired());
+}
+
+TEST(TimedTaskHelper, FireTimerWhenAlreadyDeleted) {
+ base::MessageLoop message_loop;
+
+ // Run message loop after embedder is already deleted to make sure callback
+ // doesn't cause a crash for use after free.
+ {
+ Embedder embedder;
+
+ ASSERT_FALSE(embedder.timer_fired());
+ ASSERT_FALSE(embedder.timer()->IsRunning());
+
+ embedder.timer()->Start(
+ FROM_HERE,
+ base::TimeDelta::FromSeconds(0),
+ base::Bind(&Embedder::OnTimerFired, base::Unretained(&embedder)));
+
+ ASSERT_TRUE(embedder.timer()->IsRunning());
+ }
+
+ // At this point the callback is still in the message queue but
+ // embedder is gone.
+ base::RunLoop().RunUntilIdle();
+}
+
+} // namespace content
diff --git a/chromium/content/browser/fileapi/transient_file_util_unittest.cc b/chromium/content/browser/fileapi/transient_file_util_unittest.cc
index 2ddb315c55f..96df84f4fcf 100644
--- a/chromium/content/browser/fileapi/transient_file_util_unittest.cc
+++ b/chromium/content/browser/fileapi/transient_file_util_unittest.cc
@@ -7,7 +7,6 @@
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/memory/scoped_ptr.h"
-#include "base/platform_file.h"
#include "base/run_loop.h"
#include "content/public/test/test_file_system_context.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -17,7 +16,9 @@
#include "webkit/browser/fileapi/transient_file_util.h"
#include "webkit/common/blob/scoped_file.h"
-namespace fileapi {
+using fileapi::FileSystemURL;
+
+namespace content {
class TransientFileUtilTest : public testing::Test {
public:
@@ -27,7 +28,7 @@ class TransientFileUtilTest : public testing::Test {
virtual void SetUp() OVERRIDE {
file_system_context_ = CreateFileSystemContextForTesting(
NULL, base::FilePath(FILE_PATH_LITERAL("dummy")));
- transient_file_util_.reset(new TransientFileUtil);
+ transient_file_util_.reset(new fileapi::TransientFileUtil);
ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
}
@@ -41,10 +42,12 @@ class TransientFileUtilTest : public testing::Test {
FileSystemURL* file_url,
base::FilePath* file_path) {
EXPECT_TRUE(base::CreateTemporaryFileInDir(data_dir_.path(), file_path));
- IsolatedContext* isolated_context = IsolatedContext::GetInstance();
+ fileapi::IsolatedContext* isolated_context =
+ fileapi::IsolatedContext::GetInstance();
std::string name = "tmp";
std::string fsid = isolated_context->RegisterFileSystemForPath(
- kFileSystemTypeForTransientFile,
+ fileapi::kFileSystemTypeForTransientFile,
+ std::string(),
*file_path,
&name);
ASSERT_TRUE(!fsid.empty());
@@ -52,22 +55,24 @@ class TransientFileUtilTest : public testing::Test {
fsid).AppendASCII(name);
*file_url = file_system_context_->CreateCrackedFileSystemURL(
GURL("http://foo"),
- kFileSystemTypeIsolated,
+ fileapi::kFileSystemTypeIsolated,
virtual_path);
}
- scoped_ptr<FileSystemOperationContext> NewOperationContext() {
+ scoped_ptr<fileapi::FileSystemOperationContext> NewOperationContext() {
return make_scoped_ptr(
- new FileSystemOperationContext(file_system_context_.get()));
+ new fileapi::FileSystemOperationContext(file_system_context_.get()));
}
- FileSystemFileUtil* file_util() { return transient_file_util_.get(); }
+ fileapi::FileSystemFileUtil* file_util() {
+ return transient_file_util_.get();
+ }
private:
base::MessageLoop message_loop_;
base::ScopedTempDir data_dir_;
- scoped_refptr<FileSystemContext> file_system_context_;
- scoped_ptr<TransientFileUtil> transient_file_util_;
+ scoped_refptr<fileapi::FileSystemContext> file_system_context_;
+ scoped_ptr<fileapi::TransientFileUtil> transient_file_util_;
DISALLOW_COPY_AND_ASSIGN(TransientFileUtilTest);
};
@@ -78,8 +83,8 @@ TEST_F(TransientFileUtilTest, TransientFile) {
CreateAndRegisterTemporaryFile(&temp_url, &temp_path);
- base::PlatformFileError error;
- base::PlatformFileInfo file_info;
+ base::File::Error error;
+ base::File::Info file_info;
base::FilePath path;
// Make sure the file is there.
@@ -95,13 +100,13 @@ TEST_F(TransientFileUtilTest, TransientFile) {
&error,
&file_info,
&path);
- ASSERT_EQ(base::PLATFORM_FILE_OK, error);
+ ASSERT_EQ(base::File::FILE_OK, error);
ASSERT_EQ(temp_path, path);
ASSERT_FALSE(file_info.is_directory);
// The file should be still there.
ASSERT_TRUE(base::PathExists(temp_path));
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
file_util()->GetFileInfo(NewOperationContext().get(),
temp_url, &file_info, &path));
ASSERT_EQ(temp_path, path);
@@ -113,9 +118,9 @@ TEST_F(TransientFileUtilTest, TransientFile) {
// Now the temporary file and the transient filesystem must be gone too.
ASSERT_FALSE(base::PathExists(temp_path));
- ASSERT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND,
+ ASSERT_EQ(base::File::FILE_ERROR_NOT_FOUND,
file_util()->GetFileInfo(NewOperationContext().get(),
temp_url, &file_info, &path));
}
-} // namespace fileapi
+} // namespace content
diff --git a/chromium/content/browser/fileapi/upload_file_system_file_element_reader.cc b/chromium/content/browser/fileapi/upload_file_system_file_element_reader.cc
new file mode 100644
index 00000000000..9e6a288758e
--- /dev/null
+++ b/chromium/content/browser/fileapi/upload_file_system_file_element_reader.cc
@@ -0,0 +1,117 @@
+// 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/upload_file_system_file_element_reader.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "net/base/net_errors.h"
+#include "webkit/browser/blob/file_stream_reader.h"
+#include "webkit/browser/fileapi/file_system_context.h"
+#include "webkit/browser/fileapi/file_system_url.h"
+
+namespace content {
+
+UploadFileSystemFileElementReader::UploadFileSystemFileElementReader(
+ fileapi::FileSystemContext* file_system_context,
+ const GURL& url,
+ uint64 range_offset,
+ uint64 range_length,
+ const base::Time& expected_modification_time)
+ : file_system_context_(file_system_context),
+ url_(url),
+ range_offset_(range_offset),
+ range_length_(range_length),
+ expected_modification_time_(expected_modification_time),
+ stream_length_(0),
+ position_(0),
+ weak_ptr_factory_(this) {
+}
+
+UploadFileSystemFileElementReader::~UploadFileSystemFileElementReader() {
+}
+
+int UploadFileSystemFileElementReader::Init(
+ const net::CompletionCallback& callback) {
+ // Reset states.
+ weak_ptr_factory_.InvalidateWeakPtrs();
+ stream_length_ = 0;
+ position_ = 0;
+
+ // Initialize the stream reader and the length.
+ stream_reader_ =
+ file_system_context_->CreateFileStreamReader(
+ file_system_context_->CrackURL(url_),
+ range_offset_,
+ expected_modification_time_);
+ DCHECK(stream_reader_);
+
+ const int64 result = stream_reader_->GetLength(
+ base::Bind(&UploadFileSystemFileElementReader::OnGetLength,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback));
+ if (result >= 0) {
+ stream_length_ = result;
+ return net::OK;
+ }
+
+ // The error code can be casted to int.
+ return static_cast<int>(result);
+}
+
+uint64 UploadFileSystemFileElementReader::GetContentLength() const {
+ return std::min(stream_length_, range_length_);
+}
+
+uint64 UploadFileSystemFileElementReader::BytesRemaining() const {
+ return GetContentLength() - position_;
+}
+
+int UploadFileSystemFileElementReader::Read(
+ net::IOBuffer* buf,
+ int buf_length,
+ const net::CompletionCallback& callback) {
+ DCHECK_LT(0, buf_length);
+ DCHECK(stream_reader_);
+
+ const uint64 num_bytes_to_read =
+ std::min(BytesRemaining(), static_cast<uint64>(buf_length));
+
+ if (num_bytes_to_read == 0)
+ return 0;
+
+ const int result = stream_reader_->Read(
+ buf, num_bytes_to_read,
+ base::Bind(&UploadFileSystemFileElementReader::OnRead,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback));
+ if (result >= 0)
+ OnRead(net::CompletionCallback(), result);
+ return result;
+}
+
+void UploadFileSystemFileElementReader::OnGetLength(
+ const net::CompletionCallback& callback,
+ int64 result) {
+ if (result >= 0) {
+ stream_length_ = result;
+ callback.Run(net::OK);
+ return;
+ }
+ callback.Run(result);
+}
+
+void UploadFileSystemFileElementReader::OnRead(
+ const net::CompletionCallback& callback,
+ int result) {
+ if (result > 0) {
+ position_ += result;
+ DCHECK_LE(position_, GetContentLength());
+ }
+ if (!callback.is_null())
+ callback.Run(result);
+}
+
+} // namespace content
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
new file mode 100644
index 00000000000..1b33489d1b2
--- /dev/null
+++ b/chromium/content/browser/fileapi/upload_file_system_file_element_reader.h
@@ -0,0 +1,66 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_FILEAPI_UPLOAD_FILE_SYSTEM_FILE_ELEMENT_READER_H_
+#define CONTENT_BROWSER_FILEAPI_UPLOAD_FILE_SYSTEM_FILE_ELEMENT_READER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+#include "content/common/content_export.h"
+#include "net/base/upload_element_reader.h"
+#include "url/gurl.h"
+
+namespace webkit_blob {
+class FileStreamReader;
+}
+
+namespace fileapi {
+class FileSystemContext;
+}
+
+namespace content {
+
+// An UploadElementReader implementation for filesystem file.
+class CONTENT_EXPORT UploadFileSystemFileElementReader :
+ NON_EXPORTED_BASE(public net::UploadElementReader) {
+ public:
+ UploadFileSystemFileElementReader(
+ fileapi::FileSystemContext* file_system_context,
+ const GURL& url,
+ uint64 range_offset,
+ uint64 range_length,
+ const base::Time& expected_modification_time);
+ virtual ~UploadFileSystemFileElementReader();
+
+ // UploadElementReader overrides:
+ virtual int Init(const net::CompletionCallback& callback) OVERRIDE;
+ virtual uint64 GetContentLength() const OVERRIDE;
+ virtual uint64 BytesRemaining() const OVERRIDE;
+ virtual int Read(net::IOBuffer* buf,
+ int buf_length,
+ const net::CompletionCallback& callback) OVERRIDE;
+
+ private:
+ void OnGetLength(const net::CompletionCallback& callback, int64 result);
+ void OnRead(const net::CompletionCallback& callback, int result);
+
+ scoped_refptr<fileapi::FileSystemContext> file_system_context_;
+ const GURL url_;
+ const uint64 range_offset_;
+ const uint64 range_length_;
+ const base::Time expected_modification_time_;
+
+ scoped_ptr<webkit_blob::FileStreamReader> stream_reader_;
+
+ uint64 stream_length_;
+ uint64 position_;
+
+ base::WeakPtrFactory<UploadFileSystemFileElementReader> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(UploadFileSystemFileElementReader);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_FILEAPI_UPLOAD_FILE_SYSTEM_FILE_ELEMENT_READER_H_
diff --git a/chromium/content/browser/fileapi/upload_file_system_file_element_reader_unittest.cc b/chromium/content/browser/fileapi/upload_file_system_file_element_reader_unittest.cc
index 4ae03b9b344..641b96852ca 100644
--- a/chromium/content/browser/fileapi/upload_file_system_file_element_reader_unittest.cc
+++ b/chromium/content/browser/fileapi/upload_file_system_file_element_reader_unittest.cc
@@ -2,22 +2,27 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "webkit/browser/fileapi/upload_file_system_file_element_reader.h"
+#include "content/browser/fileapi/upload_file_system_file_element_reader.h"
#include "base/files/scoped_temp_dir.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
+#include "content/public/test/async_file_test_helper.h"
#include "content/public/test/test_file_system_context.h"
#include "net/base/io_buffer.h"
#include "net/base/test_completion_callback.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "webkit/browser/fileapi/async_file_test_helper.h"
#include "webkit/browser/fileapi/file_system_backend.h"
#include "webkit/browser/fileapi/file_system_context.h"
#include "webkit/browser/fileapi/file_system_operation_context.h"
#include "webkit/browser/fileapi/file_system_url.h"
-namespace fileapi {
+using content::AsyncFileTestHelper;
+using fileapi::FileSystemContext;
+using fileapi::FileSystemType;
+using fileapi::FileSystemURL;
+
+namespace content {
namespace {
@@ -34,13 +39,13 @@ class UploadFileSystemFileElementReaderTest : public testing::Test {
virtual void SetUp() OVERRIDE {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
- file_system_context_ = fileapi::CreateFileSystemContextForTesting(
+ file_system_context_ = CreateFileSystemContextForTesting(
NULL, temp_dir_.path());
file_system_context_->OpenFileSystem(
GURL(kFileSystemURLOrigin),
kFileSystemType,
- OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+ fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
base::Bind(&UploadFileSystemFileElementReaderTest::OnOpenFileSystem,
base::Unretained(this)));
base::RunLoop().RunUntilIdle();
@@ -89,12 +94,12 @@ class UploadFileSystemFileElementReaderTest : public testing::Test {
kFileSystemType,
base::FilePath().AppendASCII(filename));
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ ASSERT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::CreateFileWithData(
file_system_context_, url, buf, buf_size));
- base::PlatformFileInfo file_info;
- ASSERT_EQ(base::PLATFORM_FILE_OK,
+ base::File::Info file_info;
+ ASSERT_EQ(base::File::FILE_OK,
AsyncFileTestHelper::GetMetadata(
file_system_context_, url, &file_info));
*modification_time = file_info.last_modified;
@@ -102,8 +107,8 @@ class UploadFileSystemFileElementReaderTest : public testing::Test {
void OnOpenFileSystem(const GURL& root,
const std::string& name,
- base::PlatformFileError result) {
- ASSERT_EQ(base::PLATFORM_FILE_OK, result);
+ base::File::Error result) {
+ ASSERT_EQ(base::File::FILE_OK, result);
ASSERT_TRUE(root.is_valid());
file_system_root_url_ = root;
}
@@ -275,4 +280,4 @@ TEST_F(UploadFileSystemFileElementReaderTest, WrongURL) {
EXPECT_EQ(net::ERR_FILE_NOT_FOUND, init_callback.WaitForResult());
}
-} // namespace fileapi
+} // namespace content
diff --git a/chromium/content/browser/frame_host/OWNERS b/chromium/content/browser/frame_host/OWNERS
index 37b83e5c25e..df8aeac9541 100644
--- a/chromium/content/browser/frame_host/OWNERS
+++ b/chromium/content/browser/frame_host/OWNERS
@@ -1 +1 @@
-nasko@chromium.org
+nasko@chromium.org
diff --git a/chromium/content/browser/frame_host/cross_process_frame_connector.cc b/chromium/content/browser/frame_host/cross_process_frame_connector.cc
new file mode 100644
index 00000000000..7701f63d7ea
--- /dev/null
+++ b/chromium/content/browser/frame_host/cross_process_frame_connector.cc
@@ -0,0 +1,184 @@
+// 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/frame_host/cross_process_frame_connector.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/renderer_host/render_view_host_impl.h"
+#include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "content/common/frame_messages.h"
+#include "content/common/gpu/gpu_messages.h"
+#include "third_party/WebKit/public/web/WebInputEvent.h"
+
+namespace content {
+
+CrossProcessFrameConnector::CrossProcessFrameConnector(
+ RenderFrameProxyHost* frame_proxy_in_parent_renderer)
+ : frame_proxy_in_parent_renderer_(frame_proxy_in_parent_renderer),
+ view_(NULL),
+ device_scale_factor_(1) {
+}
+
+CrossProcessFrameConnector::~CrossProcessFrameConnector() {
+ if (view_)
+ view_->set_cross_process_frame_connector(NULL);
+}
+
+bool CrossProcessFrameConnector::OnMessageReceived(const IPC::Message& msg) {
+ bool handled = true;
+
+ IPC_BEGIN_MESSAGE_MAP(CrossProcessFrameConnector, msg)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_BuffersSwappedACK, OnBuffersSwappedACK)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_CompositorFrameSwappedACK,
+ OnCompositorFrameSwappedACK)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_ReclaimCompositorResources,
+ OnReclaimCompositorResources)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_ForwardInputEvent, OnForwardInputEvent)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_InitializeChildFrame,
+ OnInitializeChildFrame)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+
+ return handled;
+}
+
+void CrossProcessFrameConnector::set_view(
+ RenderWidgetHostViewChildFrame* view) {
+ // Detach ourselves from the previous |view_|.
+ if (view_)
+ view_->set_cross_process_frame_connector(NULL);
+
+ view_ = view;
+
+ // Attach ourselves to the new view and size it appropriately.
+ if (view_) {
+ view_->set_cross_process_frame_connector(this);
+ SetDeviceScaleFactor(device_scale_factor_);
+ SetSize(child_frame_rect_);
+ }
+}
+
+void CrossProcessFrameConnector::RenderProcessGone() {
+ frame_proxy_in_parent_renderer_->Send(new FrameMsg_ChildFrameProcessGone(
+ frame_proxy_in_parent_renderer_->GetRoutingID()));
+}
+
+void CrossProcessFrameConnector::ChildFrameBuffersSwapped(
+ const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& gpu_params,
+ int gpu_host_id) {
+
+ FrameMsg_BuffersSwapped_Params params;
+ params.size = gpu_params.size;
+ params.mailbox = gpu_params.mailbox;
+ params.gpu_route_id = gpu_params.route_id;
+ params.gpu_host_id = gpu_host_id;
+
+ frame_proxy_in_parent_renderer_->Send(new FrameMsg_BuffersSwapped(
+ frame_proxy_in_parent_renderer_->GetRoutingID(), params));
+}
+
+void CrossProcessFrameConnector::ChildFrameCompositorFrameSwapped(
+ uint32 output_surface_id,
+ int host_id,
+ int route_id,
+ scoped_ptr<cc::CompositorFrame> frame) {
+ FrameMsg_CompositorFrameSwapped_Params params;
+ frame->AssignTo(&params.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::OnBuffersSwappedACK(
+ const FrameHostMsg_BuffersSwappedACK_Params& params) {
+ AcceleratedSurfaceMsg_BufferPresented_Params ack_params;
+ ack_params.mailbox = params.mailbox;
+ ack_params.sync_point = params.sync_point;
+ RenderWidgetHostImpl::AcknowledgeBufferPresent(params.gpu_route_id,
+ params.gpu_host_id,
+ ack_params);
+
+ // TODO(kenrb): Special case stuff for Win + Mac.
+}
+
+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);
+}
+
+gfx::Rect CrossProcessFrameConnector::ChildFrameRect() {
+ return child_frame_rect_;
+}
+
+void CrossProcessFrameConnector::OnForwardInputEvent(
+ const blink::WebInputEvent* event) {
+ if (!view_)
+ return;
+
+ RenderWidgetHostImpl* child_widget =
+ RenderWidgetHostImpl::From(view_->GetRenderWidgetHost());
+ RenderWidgetHostImpl* parent_widget =
+ frame_proxy_in_parent_renderer_->GetRenderViewHost();
+
+ if (blink::WebInputEvent::isKeyboardEventType(event->type)) {
+ if (!parent_widget->GetLastKeyboardEvent())
+ return;
+ NativeWebKeyboardEvent keyboard_event(
+ *parent_widget->GetLastKeyboardEvent());
+ child_widget->ForwardKeyboardEvent(keyboard_event);
+ return;
+ }
+
+ if (blink::WebInputEvent::isMouseEventType(event->type)) {
+ child_widget->ForwardMouseEvent(
+ *static_cast<const blink::WebMouseEvent*>(event));
+ return;
+ }
+
+ if (event->type == blink::WebInputEvent::MouseWheel) {
+ child_widget->ForwardWheelEvent(
+ *static_cast<const blink::WebMouseWheelEvent*>(event));
+ return;
+ }
+}
+
+void CrossProcessFrameConnector::SetDeviceScaleFactor(float scale_factor) {
+ device_scale_factor_ = scale_factor;
+ if (view_) {
+ RenderWidgetHostImpl* child_widget =
+ RenderWidgetHostImpl::From(view_->GetRenderWidgetHost());
+ child_widget->NotifyScreenInfoChanged();
+ }
+}
+
+void CrossProcessFrameConnector::SetSize(gfx::Rect frame_rect) {
+ child_frame_rect_ = frame_rect;
+ if (view_)
+ view_->SetSize(frame_rect.size());
+}
+
+} // 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
new file mode 100644
index 00000000000..82452243f5b
--- /dev/null
+++ b/chromium/content/browser/frame_host/cross_process_frame_connector.h
@@ -0,0 +1,123 @@
+// 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_FRAME_HOST_CROSS_PROCESS_FRAME_CONNECTOR_H_
+#define CONTENT_BROWSER_FRAME_HOST_CROSS_PROCESS_FRAME_CONNECTOR_H_
+
+#include "cc/output/compositor_frame.h"
+#include "ui/gfx/rect.h"
+
+namespace blink {
+class WebInputEvent;
+}
+
+namespace IPC {
+class Message;
+}
+
+struct FrameHostMsg_BuffersSwappedACK_Params;
+struct FrameHostMsg_CompositorFrameSwappedACK_Params;
+struct FrameHostMsg_ReclaimCompositorResources_Params;
+struct GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params;
+
+namespace content {
+class RenderFrameProxyHost;
+class RenderWidgetHostImpl;
+class RenderWidgetHostViewChildFrame;
+
+// CrossProcessFrameConnector provides the platform view abstraction for
+// RenderWidgetHostViewChildFrame allowing RWHVChildFrame to remain ignorant
+// of RenderFrameHost.
+//
+// The RenderWidgetHostView of an out-of-process child frame needs to
+// communicate with the RenderFrameProxyHost representing this frame in the
+// process of the parent frame. For example, assume you have this page:
+//
+// -----------------
+// | frame 1 |
+// | ----------- |
+// | | frame 2 | |
+// | ----------- |
+// -----------------
+//
+// If frames 1 and 2 are in process A and B, there are 4 RenderFrameHosts:
+// A1 - RFH for frame 1 in process A
+// B1 - RFPH for frame 1 in process B
+// A2 - RFPH for frame 2 in process A
+// B2 - RFH for frame 2 in process B
+//
+// B2, having a parent frame in a different process, will have a
+// RenderWidgetHostViewChildFrame. This RenderWidgetHostViewChildFrame needs
+// to communicate with A2 because the embedding process is an abstract
+// for the child frame -- it needs information necessary for compositing child
+// frame textures, and also can pass platform messages such as view resizing.
+// CrossProcessFrameConnector bridges between B2's
+// RenderWidgetHostViewChildFrame and A2 to allow for this communication.
+// (Note: B1 is only mentioned for completeness. It is not needed in this
+// example.)
+//
+// CrossProcessFrameConnector objects are owned by the RenderFrameProxyHost
+// in the child frame's RenderFrameHostManager corresponding to the parent's
+// SiteInstance, A2 in the picture above. When a child frame navigates in a new
+// process, set_view() is called to update to the new view.
+//
+class CrossProcessFrameConnector {
+ public:
+ // |frame_proxy_in_parent_renderer| corresponds to A2 in the example above.
+ explicit CrossProcessFrameConnector(
+ RenderFrameProxyHost* frame_proxy_in_parent_renderer);
+ virtual ~CrossProcessFrameConnector();
+
+ bool OnMessageReceived(const IPC::Message &msg);
+
+ // |view| corresponds to B2's RenderWidgetHostViewChildFrame in the example
+ // above.
+ void set_view(RenderWidgetHostViewChildFrame* view);
+ RenderWidgetHostViewChildFrame* get_view_for_testing() { return view_; }
+
+ void RenderProcessGone();
+
+ // 'Platform' functionality exposed to RenderWidgetHostViewChildFrame.
+ // These methods can forward messages to the child frame proxy in the parent
+ // frame's renderer or attempt to handle them within the browser process.
+ void ChildFrameBuffersSwapped(
+ const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params,
+ int gpu_host_id);
+
+ void ChildFrameCompositorFrameSwapped(uint32 output_surface_id,
+ int host_id,
+ int route_id,
+ scoped_ptr<cc::CompositorFrame> frame);
+
+ gfx::Rect ChildFrameRect();
+
+ private:
+ // Handlers for messages received from the parent frame.
+ void OnBuffersSwappedACK(
+ const FrameHostMsg_BuffersSwappedACK_Params& params);
+ void OnCompositorFrameSwappedACK(
+ const FrameHostMsg_CompositorFrameSwappedACK_Params& params);
+ void OnReclaimCompositorResources(
+ const FrameHostMsg_ReclaimCompositorResources_Params& params);
+ void OnForwardInputEvent(const blink::WebInputEvent* event);
+ void OnInitializeChildFrame(gfx::Rect frame_rect, float scale_factor);
+
+ void SetDeviceScaleFactor(float scale_factor);
+ void SetSize(gfx::Rect frame_rect);
+
+ // The RenderFrameProxyHost that routes messages to the parent frame's
+ // renderer process.
+ RenderFrameProxyHost* frame_proxy_in_parent_renderer_;
+
+ // The RenderWidgetHostView for the frame. Initially NULL.
+ RenderWidgetHostViewChildFrame* view_;
+
+ gfx::Rect child_frame_rect_;
+ float device_scale_factor_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_FRAME_HOST_CROSS_PROCESS_FRAME_CONNECTOR_H_
+
diff --git a/chromium/content/browser/frame_host/cross_site_transferring_request.cc b/chromium/content/browser/frame_host/cross_site_transferring_request.cc
new file mode 100644
index 00000000000..6d3ac2c0d08
--- /dev/null
+++ b/chromium/content/browser/frame_host/cross_site_transferring_request.cc
@@ -0,0 +1,46 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/frame_host/cross_site_transferring_request.h"
+
+#include "base/logging.h"
+#include "content/browser/loader/cross_site_resource_handler.h"
+#include "content/browser/loader/resource_dispatcher_host_impl.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace content {
+
+namespace {
+
+void CancelRequestOnIOThread(GlobalRequestID global_request_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ ResourceDispatcherHostImpl::Get()->CancelTransferringNavigation(
+ global_request_id);
+}
+
+} // namespace
+
+CrossSiteTransferringRequest::CrossSiteTransferringRequest(
+ GlobalRequestID global_request_id)
+ : global_request_id_(global_request_id) {
+ DCHECK(global_request_id_ != GlobalRequestID());
+}
+
+CrossSiteTransferringRequest::~CrossSiteTransferringRequest() {
+ if (global_request_id_ == GlobalRequestID())
+ return;
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&CancelRequestOnIOThread, global_request_id_));
+}
+
+void CrossSiteTransferringRequest::ReleaseRequest() {
+ DCHECK_NE(-1, global_request_id_.child_id);
+ DCHECK_NE(-1, global_request_id_.request_id);
+ global_request_id_ = GlobalRequestID();
+}
+
+} // namespace content
diff --git a/chromium/content/browser/frame_host/cross_site_transferring_request.h b/chromium/content/browser/frame_host/cross_site_transferring_request.h
new file mode 100644
index 00000000000..a78684be77b
--- /dev/null
+++ b/chromium/content/browser/frame_host/cross_site_transferring_request.h
@@ -0,0 +1,40 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_FRAME_HOST_CROSS_SITE_TRANSFERRING_REQUEST_H_
+#define CONTENT_BROWSER_FRAME_HOST_CROSS_SITE_TRANSFERRING_REQUEST_H_
+
+#include "base/basictypes.h"
+#include "content/common/content_export.h"
+#include "content/public/browser/global_request_id.h"
+
+namespace content {
+
+class CrossSiteResourceHandler;
+
+// A UI thread object that owns a request being transferred. Deleting the
+// object without releasing the request will delete the underlying URLRequest.
+// This is needed to clean up the URLRequest when a cross site navigation is
+// cancelled.
+class CONTENT_EXPORT CrossSiteTransferringRequest {
+ public:
+ explicit CrossSiteTransferringRequest(GlobalRequestID global_request_id);
+ ~CrossSiteTransferringRequest();
+
+ // Relinquishes ownership of the request, so another process can take
+ // control of it.
+ void ReleaseRequest();
+
+ private:
+ // No need for a weak pointer here - nothing should have ownership of the
+ // cross site request until after |this| is deleted, or ReleaseRequest is
+ // called.
+ GlobalRequestID global_request_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(CrossSiteTransferringRequest);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_FRAME_HOST_CROSS_SITE_TRANSFERRING_REQUEST_H_
diff --git a/chromium/content/browser/frame_host/debug_urls.cc b/chromium/content/browser/frame_host/debug_urls.cc
index 8e36933b276..ae454ae64a7 100644
--- a/chromium/content/browser/frame_host/debug_urls.cc
+++ b/chromium/content/browser/frame_host/debug_urls.cc
@@ -6,6 +6,8 @@
#include <vector>
+#include "base/debug/asan_invalid_access.h"
+#include "base/debug/profiler.h"
#include "base/strings/utf_string_conversions.h"
#include "content/browser/gpu/gpu_process_host_ui_shim.h"
#include "content/browser/ppapi_plugin_process_host.h"
@@ -19,12 +21,23 @@ namespace content {
namespace {
+// Define the Asan debug URLs.
+const char kAsanCrashDomain[] = "crash";
+const char kAsanHeapOverflow[] = "/browser-heap-overflow";
+const char kAsanHeapUnderflow[] = "/browser-heap-underflow";
+const char kAsanUseAfterFree[] = "/browser-use-after-free";
+#if defined(SYZYASAN)
+const char kAsanCorruptHeapBlock[] = "/browser-corrupt-heap-block";
+const char kAsanCorruptHeap[] = "/browser-corrupt-heap";
+#endif
+
void HandlePpapiFlashDebugURL(const GURL& url) {
#if defined(ENABLE_PLUGINS)
bool crash = url == GURL(kChromeUIPpapiFlashCrashURL);
std::vector<PpapiPluginProcessHost*> hosts;
- PpapiPluginProcessHost::FindByName(UTF8ToUTF16(kFlashPluginName), &hosts);
+ PpapiPluginProcessHost::FindByName(
+ base::UTF8ToUTF16(kFlashPluginName), &hosts);
for (std::vector<PpapiPluginProcessHost*>::iterator iter = hosts.begin();
iter != hosts.end(); ++iter) {
if (crash)
@@ -35,6 +48,61 @@ void HandlePpapiFlashDebugURL(const GURL& url) {
#endif
}
+bool IsAsanDebugURL(const GURL& url) {
+#if defined(SYZYASAN)
+ if (!base::debug::IsBinaryInstrumented())
+ return false;
+#endif
+
+ if (!(url.is_valid() && url.SchemeIs(kChromeUIScheme) &&
+ url.DomainIs(kAsanCrashDomain, sizeof(kAsanCrashDomain) - 1) &&
+ url.has_path())) {
+ return false;
+ }
+
+ if (url.path() == kAsanHeapOverflow || url.path() == kAsanHeapUnderflow ||
+ url.path() == kAsanUseAfterFree) {
+ return true;
+ }
+
+#if defined(SYZYASAN)
+ if (url.path() == kAsanCorruptHeapBlock || url.path() == kAsanCorruptHeap)
+ return true;
+#endif
+
+ return false;
+}
+
+bool HandleAsanDebugURL(const GURL& url) {
+#if defined(SYZYASAN)
+ if (!base::debug::IsBinaryInstrumented())
+ return false;
+
+ if (url.path() == kAsanCorruptHeapBlock) {
+ base::debug::AsanCorruptHeapBlock();
+ return true;
+ } else if (url.path() == kAsanCorruptHeap) {
+ base::debug::AsanCorruptHeap();
+ return true;
+ }
+#endif
+
+#if defined(ADDRESS_SANITIZER) || defined(SYZYASAN)
+ if (url.path() == kAsanHeapOverflow) {
+ base::debug::AsanHeapOverflow();
+ } else if (url.path() == kAsanHeapUnderflow) {
+ base::debug::AsanHeapUnderflow();
+ } else if (url.path() == kAsanUseAfterFree) {
+ base::debug::AsanHeapUseAfterFree();
+ } else {
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+
} // namespace
bool HandleDebugURL(const GURL& url, PageTransition transition) {
@@ -42,6 +110,12 @@ bool HandleDebugURL(const GURL& url, PageTransition transition) {
if (!(transition & PAGE_TRANSITION_FROM_ADDRESS_BAR))
return false;
+ // NOTE: when you add handling of any URLs to this function, also
+ // update IsDebugURL, below.
+
+ if (IsAsanDebugURL(url))
+ return HandleAsanDebugURL(url);
+
if (url.host() == kChromeUIBrowserCrashHost) {
// Induce an intentional crash in the browser process.
CHECK(false);
@@ -79,14 +153,28 @@ bool HandleDebugURL(const GURL& url, PageTransition transition) {
return false;
}
+bool IsDebugURL(const GURL& url) {
+ // NOTE: when you add any URLs to this list, also update
+ // HandleDebugURL, above.
+ return IsRendererDebugURL(url) || IsAsanDebugURL(url) ||
+ (url.is_valid() &&
+ (url.host() == kChromeUIBrowserCrashHost ||
+ url == GURL(kChromeUIGpuCleanURL) ||
+ url == GURL(kChromeUIGpuCrashURL) ||
+ url == GURL(kChromeUIGpuHangURL) ||
+ url == GURL(kChromeUIPpapiFlashCrashURL) ||
+ url == GURL(kChromeUIPpapiFlashHangURL)));
+}
+
bool IsRendererDebugURL(const GURL& url) {
if (!url.is_valid())
return false;
- if (url.SchemeIs(kJavaScriptScheme))
+ if (url.SchemeIs(url::kJavaScriptScheme))
return true;
return url == GURL(kChromeUICrashURL) ||
+ url == GURL(kChromeUIDumpURL) ||
url == GURL(kChromeUIKillURL) ||
url == GURL(kChromeUIHangURL) ||
url == GURL(kChromeUIShorthangURL);
diff --git a/chromium/content/browser/frame_host/debug_urls.h b/chromium/content/browser/frame_host/debug_urls.h
index 01530b46807..2c871948ef2 100644
--- a/chromium/content/browser/frame_host/debug_urls.h
+++ b/chromium/content/browser/frame_host/debug_urls.h
@@ -15,6 +15,11 @@ namespace content {
// handles it and returns true.
bool HandleDebugURL(const GURL& url, PageTransition transition);
+// Returns whether this given url is a debugging url. It is a superset
+// of IsRendererDebugURL, below, and debugging urls that are handled
+// in the browser process.
+bool IsDebugURL(const GURL& url);
+
// Returns whether the given url is either a debugging url handled in the
// renderer process, such as one that crashes or hangs the renderer, or a
// javascript: URL that operates on the current page in the renderer. Such URLs
diff --git a/chromium/content/browser/frame_host/frame_tree.cc b/chromium/content/browser/frame_host/frame_tree.cc
index fef82eebab8..1accb0e9d1b 100644
--- a/chromium/content/browser/frame_host/frame_tree.cc
+++ b/chromium/content/browser/frame_host/frame_tree.cc
@@ -12,6 +12,8 @@
#include "content/browser/frame_host/navigator.h"
#include "content/browser/frame_host/render_frame_host_factory.h"
#include "content/browser/frame_host/render_frame_host_impl.h"
+#include "content/browser/renderer_host/render_view_host_factory.h"
+#include "content/browser/renderer_host/render_view_host_impl.h"
namespace content {
@@ -29,11 +31,13 @@ bool FrameTreeNodeForId(int64 frame_tree_node_id,
return true;
}
-// TODO(creis): Remove this version along with FrameTreeNode::frame_id().
-bool FrameTreeNodeForFrameId(int64 frame_id,
- FrameTreeNode** out_node,
- FrameTreeNode* node) {
- if (node->frame_id() == frame_id) {
+bool FrameTreeNodeForRoutingId(int routing_id,
+ int process_id,
+ FrameTreeNode** out_node,
+ FrameTreeNode* node) {
+ // TODO(creis): Look through the swapped out RFHs as well.
+ if (node->current_frame_host()->GetProcess()->GetID() == process_id &&
+ node->current_frame_host()->GetRoutingID() == routing_id) {
*out_node = node;
// Terminate iteration once the node has been found.
return false;
@@ -41,6 +45,15 @@ bool FrameTreeNodeForFrameId(int64 frame_id,
return true;
}
+// Iterate over the FrameTree to reset any node affected by the loss of the
+// given RenderViewHost's process.
+bool ResetNodesForNewProcess(RenderViewHost* render_view_host,
+ FrameTreeNode* node) {
+ if (render_view_host == node->current_frame_host()->render_view_host())
+ node->ResetForNewProcess();
+ return true;
+}
+
} // namespace
FrameTree::FrameTree(Navigator* navigator,
@@ -52,13 +65,14 @@ FrameTree::FrameTree(Navigator* navigator,
render_view_delegate_(render_view_delegate),
render_widget_delegate_(render_widget_delegate),
manager_delegate_(manager_delegate),
- root_(new FrameTreeNode(navigator,
+ root_(new FrameTreeNode(this,
+ navigator,
render_frame_delegate,
render_view_delegate,
render_widget_delegate,
manager_delegate,
- FrameTreeNode::kInvalidFrameId,
- std::string())) {
+ std::string())),
+ focused_frame_tree_node_id_(-1) {
}
FrameTree::~FrameTree() {
@@ -70,6 +84,13 @@ FrameTreeNode* FrameTree::FindByID(int64 frame_tree_node_id) {
return node;
}
+FrameTreeNode* FrameTree::FindByRoutingID(int routing_id, int process_id) {
+ FrameTreeNode* node = NULL;
+ ForEach(
+ base::Bind(&FrameTreeNodeForRoutingId, routing_id, process_id, &node));
+ return node;
+}
+
void FrameTree::ForEach(
const base::Callback<bool(FrameTreeNode*)>& on_node) const {
std::queue<FrameTreeNode*> queue;
@@ -86,106 +107,165 @@ void FrameTree::ForEach(
}
}
-bool FrameTree::IsFirstNavigationAfterSwap() const {
- return root_->frame_id() == FrameTreeNode::kInvalidFrameId;
-}
-
-void FrameTree::OnFirstNavigationAfterSwap(int main_frame_id) {
- root_->set_frame_id(main_frame_id);
+RenderFrameHostImpl* FrameTree::AddFrame(FrameTreeNode* parent,
+ int new_routing_id,
+ const std::string& frame_name) {
+ scoped_ptr<FrameTreeNode> node(new FrameTreeNode(
+ this, parent->navigator(), render_frame_delegate_, render_view_delegate_,
+ render_widget_delegate_, manager_delegate_, frame_name));
+ FrameTreeNode* node_ptr = node.get();
+ // AddChild is what creates the RenderFrameHost.
+ parent->AddChild(node.Pass(), new_routing_id);
+ return node_ptr->current_frame_host();
}
-RenderFrameHostImpl* FrameTree::AddFrame(int render_frame_host_id,
- int64 parent_frame_id,
- int64 frame_id,
- const std::string& frame_name) {
- FrameTreeNode* parent = FindByFrameID(parent_frame_id);
- // TODO(ajwong): Should the renderer be killed here? Would there be a race on
- // shutdown that might make this case possible?
- if (!parent)
- return NULL;
+void FrameTree::RemoveFrame(FrameTreeNode* child) {
+ FrameTreeNode* parent = child->parent();
+ if (!parent) {
+ NOTREACHED() << "Unexpected RemoveFrame call for main frame.";
+ return;
+ }
- scoped_ptr<FrameTreeNode> node(CreateNode(
- frame_id, frame_name, render_frame_host_id, parent));
- RenderFrameHostImpl* render_frame = node->render_frame_host();
- parent->AddChild(node.Pass());
- return render_frame;
-}
-
-void FrameTree::RemoveFrame(RenderFrameHostImpl* render_frame_host,
- int64 parent_frame_id,
- int64 frame_id) {
- // If switches::kSitePerProcess is not specified, then the FrameTree only
- // contains a node for the root element. However, even in this case
- // frame detachments need to be broadcast outwards.
- //
- // TODO(ajwong): Move this below the |parent| check after the FrameTree is
- // guaranteed to be correctly populated even without the
- // switches::kSitePerProcess flag.
- FrameTreeNode* parent = FindByFrameID(parent_frame_id);
- FrameTreeNode* child = FindByFrameID(frame_id);
+ // Notify observers of the frame removal.
+ RenderFrameHostImpl* render_frame_host = child->current_frame_host();
if (!on_frame_removed_.is_null()) {
on_frame_removed_.Run(
- render_frame_host->render_view_host(), frame_id);
+ render_frame_host->render_view_host(),
+ render_frame_host->GetRoutingID());
}
- // TODO(ajwong): Should the renderer be killed here? Would there be a race on
- // shutdown that might make this case possible?
- if (!parent || !child)
- return;
-
parent->RemoveChild(child);
}
-void FrameTree::SetFrameUrl(int64 frame_id, const GURL& url) {
- FrameTreeNode* node = FindByFrameID(frame_id);
- // TODO(ajwong): Should the renderer be killed here? Would there be a race on
- // shutdown that might make this case possible?
- if (!node)
- return;
-
- if (node)
- node->set_current_url(url);
+void FrameTree::ResetForMainFrameSwap() {
+ root_->ResetForNewProcess();
+ focused_frame_tree_node_id_ = -1;
}
-void FrameTree::SwapMainFrame(RenderFrameHostImpl* render_frame_host) {
- return root_->ResetForMainFrame(render_frame_host);
+void FrameTree::RenderProcessGone(RenderViewHost* render_view_host) {
+ // Walk the full tree looking for nodes that may be affected. Once a frame
+ // crashes, all of its child FrameTreeNodes go away.
+ // Note that the helper function may call ResetForNewProcess on a node, which
+ // clears its children before we iterate over them. That's ok, because
+ // ForEach does not add a node's children to the queue until after visiting
+ // the node itself.
+ ForEach(base::Bind(&ResetNodesForNewProcess, render_view_host));
}
RenderFrameHostImpl* FrameTree::GetMainFrame() const {
- return root_->render_frame_host();
+ return root_->current_frame_host();
+}
+
+FrameTreeNode* FrameTree::GetFocusedFrame() {
+ return FindByID(focused_frame_tree_node_id_);
+}
+
+void FrameTree::SetFocusedFrame(FrameTreeNode* node) {
+ focused_frame_tree_node_id_ = node->frame_tree_node_id();
}
void FrameTree::SetFrameRemoveListener(
- const base::Callback<void(RenderViewHostImpl*, int64)>& on_frame_removed) {
+ const base::Callback<void(RenderViewHostImpl*, int)>& on_frame_removed) {
on_frame_removed_ = on_frame_removed;
}
-FrameTreeNode* FrameTree::FindByFrameID(int64 frame_id) {
- FrameTreeNode* node = NULL;
- ForEach(base::Bind(&FrameTreeNodeForFrameId, frame_id, &node));
- return node;
+RenderViewHostImpl* FrameTree::CreateRenderViewHostForMainFrame(
+ SiteInstance* site_instance,
+ int routing_id,
+ int main_frame_routing_id,
+ bool swapped_out,
+ bool hidden) {
+ DCHECK(main_frame_routing_id != MSG_ROUTING_NONE);
+ RenderViewHostMap::iterator iter =
+ render_view_host_map_.find(site_instance->GetId());
+ if (iter != render_view_host_map_.end()) {
+ // If a RenderViewHost is pending shutdown for this |site_instance|, put it
+ // in the map of RenderViewHosts pending shutdown. Otherwise there should
+ // not be a RenderViewHost for the SiteInstance.
+ CHECK_EQ(RenderViewHostImpl::STATE_PENDING_SHUTDOWN,
+ iter->second->rvh_state());
+ render_view_host_pending_shutdown_map_.insert(
+ std::pair<int, RenderViewHostImpl*>(site_instance->GetId(),
+ iter->second));
+ render_view_host_map_.erase(iter);
+ }
+ RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
+ RenderViewHostFactory::Create(site_instance,
+ render_view_delegate_,
+ render_widget_delegate_,
+ routing_id,
+ main_frame_routing_id,
+ swapped_out,
+ hidden));
+
+ render_view_host_map_[site_instance->GetId()] = rvh;
+ return rvh;
}
-scoped_ptr<FrameTreeNode> FrameTree::CreateNode(
- int64 frame_id,
- const std::string& frame_name,
- int render_frame_host_id,
- FrameTreeNode* parent_node) {
- scoped_ptr<FrameTreeNode> frame_tree_node(new FrameTreeNode(
- parent_node->navigator(), render_frame_delegate_, render_view_delegate_,
- render_widget_delegate_, manager_delegate_, frame_id, frame_name));
-
- scoped_ptr<RenderFrameHostImpl> render_frame_host(
- RenderFrameHostFactory::Create(
- parent_node->render_frame_host()->render_view_host(),
- parent_node->render_frame_host()->delegate(),
- this,
- frame_tree_node.get(),
- render_frame_host_id,
- false));
-
- frame_tree_node->set_render_frame_host(render_frame_host.release(), true);
- return frame_tree_node.Pass();
+RenderViewHostImpl* FrameTree::GetRenderViewHostForSubFrame(
+ SiteInstance* site_instance) {
+ RenderViewHostMap::iterator iter =
+ render_view_host_map_.find(site_instance->GetId());
+ // TODO(creis): Mirror the frame tree so this check can't fail.
+ if (iter == render_view_host_map_.end())
+ return NULL;
+ return iter->second;
+}
+
+void FrameTree::RegisterRenderFrameHost(
+ RenderFrameHostImpl* render_frame_host) {
+ SiteInstance* site_instance =
+ render_frame_host->render_view_host()->GetSiteInstance();
+ RenderViewHostMap::iterator iter =
+ render_view_host_map_.find(site_instance->GetId());
+ CHECK(iter != render_view_host_map_.end());
+
+ iter->second->increment_ref_count();
+}
+
+void FrameTree::UnregisterRenderFrameHost(
+ RenderFrameHostImpl* render_frame_host) {
+ SiteInstance* site_instance =
+ render_frame_host->render_view_host()->GetSiteInstance();
+ int32 site_instance_id = site_instance->GetId();
+ RenderViewHostMap::iterator iter =
+ render_view_host_map_.find(site_instance_id);
+ if (iter != render_view_host_map_.end() &&
+ iter->second == render_frame_host->render_view_host()) {
+ // Decrement the refcount and shutdown the RenderViewHost if no one else is
+ // using it.
+ CHECK_GT(iter->second->ref_count(), 0);
+ iter->second->decrement_ref_count();
+ if (iter->second->ref_count() == 0) {
+ iter->second->Shutdown();
+ render_view_host_map_.erase(iter);
+ }
+ } else {
+ // The RenderViewHost should be in the list of RenderViewHosts pending
+ // shutdown.
+ bool render_view_host_found = false;
+ std::pair<RenderViewHostMultiMap::iterator,
+ RenderViewHostMultiMap::iterator> result =
+ render_view_host_pending_shutdown_map_.equal_range(site_instance_id);
+ for (RenderViewHostMultiMap::iterator multi_iter = result.first;
+ multi_iter != result.second;
+ ++multi_iter) {
+ if (multi_iter->second != render_frame_host->render_view_host())
+ continue;
+ render_view_host_found = true;
+ RenderViewHostImpl* rvh = multi_iter->second;
+ // Decrement the refcount and shutdown the RenderViewHost if no one else
+ // is using it.
+ CHECK_GT(rvh->ref_count(), 0);
+ rvh->decrement_ref_count();
+ if (rvh->ref_count() == 0) {
+ rvh->Shutdown();
+ render_view_host_pending_shutdown_map_.erase(multi_iter);
+ }
+ break;
+ }
+ CHECK(render_view_host_found);
+ }
}
} // namespace content
diff --git a/chromium/content/browser/frame_host/frame_tree.h b/chromium/content/browser/frame_host/frame_tree.h
index 3ef2d4c979e..c7fdc4eaf7a 100644
--- a/chromium/content/browser/frame_host/frame_tree.h
+++ b/chromium/content/browser/frame_host/frame_tree.h
@@ -34,9 +34,6 @@ class RenderWidgetHostDelegate;
// TODO(ajwong): Move NavigationController ownership to the main frame
// FrameTreeNode. Possibly expose access to it from here.
//
-// TODO(ajwong): Currently this class only contains FrameTreeNodes for
-// subframes if the --site-per-process flag is enabled.
-//
// This object is only used on the UI thread.
class CONTENT_EXPORT FrameTree {
public:
@@ -53,71 +50,80 @@ class CONTENT_EXPORT FrameTree {
RenderFrameHostManager::Delegate* manager_delegate);
~FrameTree();
+ FrameTreeNode* root() const { return root_.get(); }
+
// Returns the FrameTreeNode with the given |frame_tree_node_id|.
FrameTreeNode* FindByID(int64 frame_tree_node_id);
+ // Returns the FrameTreeNode with the given renderer-specific |routing_id|.
+ FrameTreeNode* FindByRoutingID(int routing_id, int process_id);
+
// 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.
+ // 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<bool(FrameTreeNode*)>& on_node) const;
- // After the FrameTree is created, or after SwapMainFrame() has been called,
- // the root node does not yet have a frame id. This is allocated by the
- // renderer and is published to the browser process on the first navigation
- // after a swap. These two functions are used to set the root node's frame
- // id.
- //
- // TODO(ajwong): Remove these once RenderFrameHost's routing id replaces
- // frame_id.
- bool IsFirstNavigationAfterSwap() const;
- void OnFirstNavigationAfterSwap(int main_frame_id);
-
// Frame tree manipulation routines.
- // TODO(creis): These should take in RenderFrameHost routing IDs.
- RenderFrameHostImpl* AddFrame(int render_frame_host_id,
- int64 parent_frame_tree_node_id,
- int64 frame_id,
+ RenderFrameHostImpl* AddFrame(FrameTreeNode* parent,
+ int new_routing_id,
const std::string& frame_name);
- void RemoveFrame(RenderFrameHostImpl* render_frame_host,
- int64 parent_frame_id,
- int64 frame_id);
- void SetFrameUrl(int64 frame_id, const GURL& url);
+ void RemoveFrame(FrameTreeNode* child);
- // Resets the FrameTree and changes RenderFrameHost for the main frame.
+ // Clears process specific-state after a main frame process swap.
// This destroys most of the frame tree but retains the root node so that
// navigation state may be kept on it between process swaps. Used to
// support bookkeeping for top-level navigations.
- //
- // If |main_frame| is NULL, reset tree to initially constructed state.
- //
- // TODO(ajwong): This function should not be given a |main_frame|. This is
- // required currently because the RenderViewHost owns its main frame. When
- // that relation is fixed, the FrameTree should be responsible for
- // created/destroying the main frame on the swap.
- void SwapMainFrame(RenderFrameHostImpl* main_frame);
+ // TODO(creis): Look into how we can remove the need for this method.
+ void ResetForMainFrameSwap();
+
+ // Update the frame tree after a process exits. Any nodes currently using the
+ // given |render_view_host| will lose all their children.
+ // TODO(creis): This should take a RenderProcessHost once RenderFrameHost
+ // knows its process. Until then, we would just be asking the RenderViewHost
+ // for its process, so we'll skip that step.
+ void RenderProcessGone(RenderViewHost* render_view_host);
// Convenience accessor for the main frame's RenderFrameHostImpl.
RenderFrameHostImpl* GetMainFrame() const;
+ // Returns the focused frame.
+ FrameTreeNode* GetFocusedFrame();
+
+ // Sets the focused frame.
+ void SetFocusedFrame(FrameTreeNode* node);
+
// Allows a client to listen for frame removal. The listener should expect
// to receive the RenderViewHostImpl containing the frame and the renderer-
- // specific frame ID of the removed frame.
+ // specific frame routing ID of the removed frame.
// TODO(creis): These parameters will later change to be the RenderFrameHost.
void SetFrameRemoveListener(
- const base::Callback<void(RenderViewHostImpl*, int64)>& on_frame_removed);
-
- FrameTreeNode* root() const { return root_.get(); }
+ const base::Callback<void(RenderViewHostImpl*, int)>& on_frame_removed);
+
+ // Creates a RenderViewHost for a new main frame RenderFrameHost in the given
+ // |site_instance|. The RenderViewHost will have its Shutdown method called
+ // when all of the RenderFrameHosts using it are deleted.
+ RenderViewHostImpl* CreateRenderViewHostForMainFrame(
+ SiteInstance* site_instance,
+ int routing_id,
+ int main_frame_routing_id,
+ bool swapped_out,
+ bool hidden);
+
+ // Returns the existing RenderViewHost for a new subframe RenderFrameHost.
+ // There should always be such a RenderViewHost, because the main frame
+ // RenderFrameHost for each SiteInstance should be created before subframes.
+ RenderViewHostImpl* GetRenderViewHostForSubFrame(SiteInstance* site_instance);
+
+ // Keeps track of which RenderFrameHosts are using each RenderViewHost. When
+ // the number drops to zero, we call Shutdown on the RenderViewHost.
+ void RegisterRenderFrameHost(RenderFrameHostImpl* render_frame_host);
+ void UnregisterRenderFrameHost(RenderFrameHostImpl* render_frame_host);
private:
- // Returns the FrameTreeNode with the given renderer-specific |frame_id|.
- // For internal use only.
- // TODO(creis): Replace this with a version that takes in a routing ID.
- FrameTreeNode* FindByFrameID(int64 frame_id);
-
- scoped_ptr<FrameTreeNode> CreateNode(int64 frame_id,
- const std::string& frame_name,
- int render_frame_host_id,
- FrameTreeNode* parent_node);
+ typedef base::hash_map<int, RenderViewHostImpl*> RenderViewHostMap;
+ typedef std::multimap<int, RenderViewHostImpl*> RenderViewHostMultiMap;
// These delegates are installed into all the RenderViewHosts and
// RenderFrameHosts that we create.
@@ -126,9 +132,28 @@ class CONTENT_EXPORT FrameTree {
RenderWidgetHostDelegate* render_widget_delegate_;
RenderFrameHostManager::Delegate* manager_delegate_;
+ // Map of SiteInstance ID to a RenderViewHost. This allows us to look up the
+ // RenderViewHost for a given SiteInstance when creating RenderFrameHosts.
+ // Combined with the refcount on RenderViewHost, this allows us to call
+ // Shutdown on the RenderViewHost and remove it from the map when no more
+ // RenderFrameHosts are using it.
+ //
+ // Must be declared before |root_| so that it is deleted afterward. Otherwise
+ // the map will be cleared before we delete the RenderFrameHosts in the tree.
+ RenderViewHostMap render_view_host_map_;
+
+ // Map of SiteInstance ID to RenderViewHosts that are pending shutdown. The
+ // renderers of these RVH are currently executing the unload event in
+ // background. When the SwapOutACK is received, they will be deleted. In the
+ // meantime, they are kept in this map, as they should not be reused (part of
+ // their state is already gone away).
+ RenderViewHostMultiMap render_view_host_pending_shutdown_map_;
+
scoped_ptr<FrameTreeNode> root_;
- base::Callback<void(RenderViewHostImpl*, int64)> on_frame_removed_;
+ int64 focused_frame_tree_node_id_;
+
+ base::Callback<void(RenderViewHostImpl*, int)> on_frame_removed_;
DISALLOW_COPY_AND_ASSIGN(FrameTree);
};
diff --git a/chromium/content/browser/frame_host/frame_tree_browsertest.cc b/chromium/content/browser/frame_host/frame_tree_browsertest.cc
new file mode 100644
index 00000000000..bc5f1cb04ff
--- /dev/null
+++ b/chromium/content/browser/frame_host/frame_tree_browsertest.cc
@@ -0,0 +1,158 @@
+// 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/frame_host/frame_tree.h"
+#include "content/browser/frame_host/frame_tree_node.h"
+#include "content/browser/renderer_host/render_view_host_impl.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_types.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_navigation_observer.h"
+#include "content/public/test/test_utils.h"
+#include "content/shell/browser/shell.h"
+#include "net/dns/mock_host_resolver.h"
+
+namespace content {
+
+class FrameTreeBrowserTest : public ContentBrowserTest {
+ public:
+ FrameTreeBrowserTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FrameTreeBrowserTest);
+};
+
+// Ensures FrameTree correctly reflects page structure during navigations.
+IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, FrameTreeShape) {
+ host_resolver()->AddRule("*", "127.0.0.1");
+ ASSERT_TRUE(test_server()->Start());
+
+ GURL base_url = test_server()->GetURL("files/site_isolation/");
+ GURL::Replacements replace_host;
+ std::string host_str("A.com"); // Must stay in scope with replace_host.
+ replace_host.SetHostStr(host_str);
+ base_url = base_url.ReplaceComponents(replace_host);
+
+ // Load doc without iframes. Verify FrameTree just has root.
+ // Frame tree:
+ // Site-A Root
+ NavigateToURL(shell(), base_url.Resolve("blank.html"));
+ FrameTreeNode* root =
+ static_cast<WebContentsImpl*>(shell()->web_contents())->
+ GetFrameTree()->root();
+ EXPECT_EQ(0U, root->child_count());
+
+ // Add 2 same-site frames. Verify 3 nodes in tree with proper names.
+ // Frame tree:
+ // Site-A Root -- Site-A frame1
+ // \-- Site-A frame2
+ WindowedNotificationObserver observer1(
+ content::NOTIFICATION_LOAD_STOP,
+ content::Source<NavigationController>(
+ &shell()->web_contents()->GetController()));
+ NavigateToURL(shell(), base_url.Resolve("frames-X-X.html"));
+ observer1.Wait();
+ ASSERT_EQ(2U, root->child_count());
+ EXPECT_EQ(0U, root->child_at(0)->child_count());
+ EXPECT_EQ(0U, root->child_at(1)->child_count());
+}
+
+// TODO(ajwong): Talk with nasko and merge this functionality with
+// FrameTreeShape.
+IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, FrameTreeShape2) {
+ ASSERT_TRUE(test_server()->Start());
+ NavigateToURL(shell(),
+ test_server()->GetURL("files/frame_tree/top.html"));
+
+ WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
+ FrameTreeNode* root = wc->GetFrameTree()->root();
+
+ // Check that the root node is properly created.
+ ASSERT_EQ(3UL, root->child_count());
+ EXPECT_EQ(std::string(), root->frame_name());
+
+ ASSERT_EQ(2UL, root->child_at(0)->child_count());
+ EXPECT_STREQ("1-1-name", root->child_at(0)->frame_name().c_str());
+
+ // Verify the deepest node exists and has the right name.
+ ASSERT_EQ(2UL, root->child_at(2)->child_count());
+ EXPECT_EQ(1UL, root->child_at(2)->child_at(1)->child_count());
+ EXPECT_EQ(0UL, root->child_at(2)->child_at(1)->child_at(0)->child_count());
+ EXPECT_STREQ("3-1-name",
+ root->child_at(2)->child_at(1)->child_at(0)->frame_name().c_str());
+
+ // Navigate to about:blank, which should leave only the root node of the frame
+ // tree in the browser process.
+ NavigateToURL(shell(), test_server()->GetURL("files/title1.html"));
+
+ root = wc->GetFrameTree()->root();
+ EXPECT_EQ(0UL, root->child_count());
+ EXPECT_EQ(std::string(), root->frame_name());
+}
+
+// Test that we can navigate away if the previous renderer doesn't clean up its
+// child frames.
+IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, FrameTreeAfterCrash) {
+ ASSERT_TRUE(test_server()->Start());
+ NavigateToURL(shell(),
+ test_server()->GetURL("files/frame_tree/top.html"));
+
+ // Crash the renderer so that it doesn't send any FrameDetached messages.
+ RenderProcessHostWatcher crash_observer(
+ shell()->web_contents(),
+ RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
+ NavigateToURL(shell(), GURL(kChromeUICrashURL));
+ crash_observer.Wait();
+
+ // The frame tree should be cleared.
+ WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
+ FrameTreeNode* root = wc->GetFrameTree()->root();
+ EXPECT_EQ(0UL, root->child_count());
+
+ // Navigate to a new URL.
+ GURL url(test_server()->GetURL("files/title1.html"));
+ NavigateToURL(shell(), url);
+ EXPECT_EQ(0UL, root->child_count());
+ EXPECT_EQ(url, root->current_url());
+}
+
+// Test that we can navigate away if the previous renderer doesn't clean up its
+// child frames.
+IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, NavigateWithLeftoverFrames) {
+ host_resolver()->AddRule("*", "127.0.0.1");
+ ASSERT_TRUE(test_server()->Start());
+
+ GURL base_url = test_server()->GetURL("files/site_isolation/");
+ GURL::Replacements replace_host;
+ std::string host_str("A.com"); // Must stay in scope with replace_host.
+ replace_host.SetHostStr(host_str);
+ base_url = base_url.ReplaceComponents(replace_host);
+
+ NavigateToURL(shell(),
+ test_server()->GetURL("files/frame_tree/top.html"));
+
+ // Hang the renderer so that it doesn't send any FrameDetached messages.
+ // (This navigation will never complete, so don't wait for it.)
+ shell()->LoadURL(GURL(kChromeUIHangURL));
+
+ // Check that the frame tree still has children.
+ WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
+ FrameTreeNode* root = wc->GetFrameTree()->root();
+ ASSERT_EQ(3UL, root->child_count());
+
+ // Navigate to a new URL. We use LoadURL because NavigateToURL will try to
+ // wait for the previous navigation to stop.
+ TestNavigationObserver tab_observer(wc, 1);
+ shell()->LoadURL(base_url.Resolve("blank.html"));
+ tab_observer.Wait();
+
+ // The frame tree should now be cleared.
+ EXPECT_EQ(0UL, root->child_count());
+}
+
+} // namespace content
diff --git a/chromium/content/browser/frame_host/frame_tree_node.cc b/chromium/content/browser/frame_host/frame_tree_node.cc
index 37b61b4fadf..ebc542e56e3 100644
--- a/chromium/content/browser/frame_host/frame_tree_node.cc
+++ b/chromium/content/browser/frame_host/frame_tree_node.cc
@@ -7,39 +7,51 @@
#include <queue>
#include "base/stl_util.h"
+#include "content/browser/frame_host/frame_tree.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"
namespace content {
-const int64 FrameTreeNode::kInvalidFrameId = -1;
int64 FrameTreeNode::next_frame_tree_node_id_ = 1;
-FrameTreeNode::FrameTreeNode(Navigator* navigator,
+FrameTreeNode::FrameTreeNode(FrameTree* frame_tree,
+ Navigator* navigator,
RenderFrameHostDelegate* render_frame_delegate,
RenderViewHostDelegate* render_view_delegate,
RenderWidgetHostDelegate* render_widget_delegate,
RenderFrameHostManager::Delegate* manager_delegate,
- int64 frame_id,
const std::string& name)
- : navigator_(navigator),
- render_manager_(render_frame_delegate,
- render_view_delegate,
- render_widget_delegate,
- manager_delegate),
- frame_tree_node_id_(next_frame_tree_node_id_++),
- frame_id_(frame_id),
- frame_name_(name),
- owns_render_frame_host_(true),
- render_frame_host_(NULL) {
-}
+ : frame_tree_(frame_tree),
+ navigator_(navigator),
+ render_manager_(this,
+ render_frame_delegate,
+ render_view_delegate,
+ render_widget_delegate,
+ manager_delegate),
+ frame_tree_node_id_(next_frame_tree_node_id_++),
+ frame_name_(name),
+ parent_(NULL) {}
FrameTreeNode::~FrameTreeNode() {
- if (owns_render_frame_host_)
- delete render_frame_host_;
}
-void FrameTreeNode::AddChild(scoped_ptr<FrameTreeNode> child) {
+bool FrameTreeNode::IsMainFrame() const {
+ return frame_tree_->root() == this;
+}
+
+void FrameTreeNode::AddChild(scoped_ptr<FrameTreeNode> child,
+ int frame_routing_id) {
+ // Initialize the RenderFrameHost for the new node. We always create child
+ // frames in the same SiteInstance as the current frame, and they can swap to
+ // a different one if they navigate away.
+ child->render_manager()->Init(
+ render_manager_.current_host()->GetSiteInstance()->GetBrowserContext(),
+ render_manager_.current_host()->GetSiteInstance(),
+ render_manager_.current_host()->GetRoutingID(),
+ frame_routing_id);
+ child->set_parent(this);
children_.push_back(child.release());
}
@@ -51,22 +63,24 @@ void FrameTreeNode::RemoveChild(FrameTreeNode* child) {
break;
}
- if (iter != children_.end())
- children_.erase(iter);
+ if (iter != children_.end()) {
+ // Subtle: we need to make sure the node is gone from the tree before
+ // observers are notified of its deletion.
+ scoped_ptr<FrameTreeNode> node_to_delete(*iter);
+ children_.weak_erase(iter);
+ node_to_delete->set_parent(NULL);
+ node_to_delete.reset();
+ }
}
-void FrameTreeNode::ResetForMainFrame(
- RenderFrameHostImpl* new_render_frame_host) {
- owns_render_frame_host_ = false;
- frame_id_ = kInvalidFrameId;
+void FrameTreeNode::ResetForNewProcess() {
current_url_ = GURL();
// The children may not have been cleared if a cross-process navigation
// commits before the old process cleans everything up. Make sure the child
- // nodes get deleted.
- children_.clear();
-
- render_frame_host_ = new_render_frame_host;
+ // nodes get deleted before swapping to a new process.
+ ScopedVector<FrameTreeNode> old_children = children_.Pass();
+ old_children.clear(); // May notify observers.
}
} // 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 9c22b125ba3..3b5e6acb6b3 100644
--- a/chromium/content/browser/frame_host/frame_tree_node.h
+++ b/chromium/content/browser/frame_host/frame_tree_node.h
@@ -18,6 +18,7 @@
namespace content {
+class FrameTree;
class Navigator;
class RenderFrameHostImpl;
@@ -27,39 +28,28 @@ class RenderFrameHostImpl;
// are frame-specific (as opposed to page-specific).
class CONTENT_EXPORT FrameTreeNode {
public:
- static const int64 kInvalidFrameId;
- FrameTreeNode(Navigator* navigator,
+ FrameTreeNode(FrameTree* frame_tree,
+ Navigator* navigator,
RenderFrameHostDelegate* render_frame_delegate,
RenderViewHostDelegate* render_view_delegate,
RenderWidgetHostDelegate* render_widget_delegate,
RenderFrameHostManager::Delegate* manager_delegate,
- int64 frame_id,
const std::string& name);
~FrameTreeNode();
- void AddChild(scoped_ptr<FrameTreeNode> child);
+ bool IsMainFrame() const;
+
+ void AddChild(scoped_ptr<FrameTreeNode> child, int frame_routing_id);
void RemoveChild(FrameTreeNode* child);
- // TODO(nasko): This method should be removed once RenderFrameHosts are
- // created by RenderFrameHostManager.
- void set_render_frame_host(
- RenderFrameHostImpl* render_frame_host,
- bool owns_render_frame_host) {
- render_frame_host_ = render_frame_host;
- owns_render_frame_host_ = owns_render_frame_host;
- }
+ // Clears process specific-state in this node to prepare for a new process.
+ void ResetForNewProcess();
- // Transitional API allowing the RenderFrameHost of a FrameTreeNode
- // representing the main frame to be provided by someone else. After
- // this is called, the FrameTreeNode no longer owns its RenderFrameHost.
- //
- // This should only be used for the main frame (aka root) in a frame tree.
- //
- // TODO(ajwong): Remove this method once the main frame RenderFrameHostImpl is
- // no longer owned by the RenderViewHostImpl.
- void ResetForMainFrame(RenderFrameHostImpl* new_render_frame_host);
+ FrameTree* frame_tree() const {
+ return frame_tree_;
+ }
Navigator* navigator() {
return navigator_.get();
@@ -73,16 +63,6 @@ class CONTENT_EXPORT FrameTreeNode {
return frame_tree_node_id_;
}
- // DO NOT USE. Only used by FrameTree until we replace renderer-specific
- // frame IDs with RenderFrameHost routing IDs.
- void set_frame_id(int64 frame_id) {
- DCHECK_EQ(frame_id_, kInvalidFrameId);
- frame_id_ = frame_id;
- }
- int64 frame_id() const {
- return frame_id_;
- }
-
const std::string& frame_name() const {
return frame_name_;
}
@@ -91,6 +71,8 @@ class CONTENT_EXPORT FrameTreeNode {
return children_.size();
}
+ FrameTreeNode* parent() const { return parent_; }
+
FrameTreeNode* child_at(size_t index) const {
return children_[index];
}
@@ -103,58 +85,44 @@ class CONTENT_EXPORT FrameTreeNode {
current_url_ = url;
}
- RenderFrameHostImpl* render_frame_host() const {
- return render_frame_host_;
+ RenderFrameHostImpl* current_frame_host() const {
+ return render_manager_.current_frame_host();
}
private:
+ void set_parent(FrameTreeNode* parent) { parent_ = parent; }
+
// The next available browser-global FrameTreeNode ID.
static int64 next_frame_tree_node_id_;
+ // The FrameTree that owns us.
+ FrameTree* frame_tree_; // not owned.
+
// The Navigator object responsible for managing navigations at this node
// of the frame tree.
scoped_refptr<Navigator> navigator_;
- // Manages creation and swapping of RenderViewHosts for this frame. This must
- // be declared before |children_| so that it gets deleted after them. That's
- // currently necessary so that RenderFrameHostImpl's destructor can call
- // GetProcess.
- // TODO(creis): This will eliminate the need for |render_frame_host_| below.
+ // Manages creation and swapping of RenderFrameHosts for this frame. This
+ // must be declared before |children_| so that it gets deleted after them.
+ // That's currently necessary so that RenderFrameHostImpl's destructor can
+ // call GetProcess.
RenderFrameHostManager render_manager_;
// A browser-global identifier for the frame in the page, which stays stable
// even if the frame does a cross-process navigation.
const int64 frame_tree_node_id_;
- // The renderer-specific identifier for the frame in the page.
- // TODO(creis): Remove this in favor of the RenderFrameHost's routing ID once
- // we create FrameTreeNodes for all frames (even without a flag), since this
- // value can change after cross-process navigations.
- int64 frame_id_;
-
// The assigned name of the frame. This name can be empty, unlike the unique
// name generated internally in the DOM tree.
std::string frame_name_;
+ // The parent node of this frame. NULL if this node is the root or if it has
+ // not yet been attached to the frame tree.
+ FrameTreeNode* parent_;
+
// The immediate children of this specific frame.
ScopedVector<FrameTreeNode> children_;
- // When ResetForMainFrame() is called, this is set to false and the
- // |render_frame_host_| below is not deleted on destruction.
- //
- // For the mainframe, the FrameTree does not own the |render_frame_host_|.
- // This is a transitional wart because RenderFrameHostManager does not yet
- // have the bookkeeping logic to handle creating a pending RenderFrameHost
- // along with a pending RenderViewHost. Thus, for the main frame, the
- // RenderViewHost currently retains ownership and the FrameTreeNode should
- // not delete it on destruction.
- bool owns_render_frame_host_;
-
- // The active RenderFrameHost for this frame. The FrameTreeNode does not
- // always own this pointer. See comments above |owns_render_frame_host_|.
- // TODO(ajwong): Replace with RenderFrameHostManager.
- RenderFrameHostImpl* render_frame_host_;
-
// Track the current frame's last committed URL, so we can estimate the
// process impact of out-of-process iframes.
// TODO(creis): Remove this when we can store subframe URLs in the
diff --git a/chromium/content/browser/frame_host/frame_tree_unittest.cc b/chromium/content/browser/frame_host/frame_tree_unittest.cc
index 6f1bb330380..a894061f32c 100644
--- a/chromium/content/browser/frame_host/frame_tree_unittest.cc
+++ b/chromium/content/browser/frame_host/frame_tree_unittest.cc
@@ -10,170 +10,211 @@
#include "content/browser/frame_host/render_frame_host_factory.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/common/view_messages.h"
+#include "content/public/browser/web_contents_observer.h"
#include "content/public/test/mock_render_process_host.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_browser_thread_bundle.h"
-#include "content/public/test/test_renderer_host.h"
+#include "content/test/test_render_view_host.h"
+#include "content/test/test_web_contents.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
namespace {
-class FrameTreeTest : public RenderViewHostTestHarness {
- protected:
- // Prints a FrameTree, for easy assertions of the tree hierarchy.
- std::string GetTreeState(FrameTree* frame_tree) {
- std::string result;
- AppendTreeNodeState(frame_tree->root(), &result);
+// Appends a description of the structure of the frame tree to |result|.
+void AppendTreeNodeState(FrameTreeNode* node, std::string* result) {
+ result->append(
+ base::Int64ToString(node->current_frame_host()->GetRoutingID()));
+ if (!node->frame_name().empty()) {
+ result->append(" '");
+ result->append(node->frame_name());
+ result->append("'");
+ }
+ result->append(": [");
+ const char* separator = "";
+ for (size_t i = 0; i < node->child_count(); i++) {
+ result->append(separator);
+ AppendTreeNodeState(node->child_at(i), result);
+ separator = ", ";
+ }
+ result->append("]");
+}
+
+// Logs calls to WebContentsObserver along with the state of the frame tree,
+// for later use in EXPECT_EQ().
+class TreeWalkingWebContentsLogger : public WebContentsObserver {
+ public:
+ explicit TreeWalkingWebContentsLogger(WebContents* web_contents)
+ : WebContentsObserver(web_contents) {}
+
+ virtual ~TreeWalkingWebContentsLogger() {
+ EXPECT_EQ("", log_) << "Activity logged that was not expected";
+ }
+
+ // Gets and resets the log, which is a string of what happened.
+ std::string GetLog() {
+ std::string result = log_;
+ log_.clear();
return result;
}
+ // content::WebContentsObserver implementation.
+ virtual void RenderFrameCreated(RenderFrameHost* render_frame_host) OVERRIDE {
+ LogWhatHappened("RenderFrameCreated", render_frame_host);
+ }
+
+ virtual void RenderFrameDeleted(RenderFrameHost* render_frame_host) OVERRIDE {
+ LogWhatHappened("RenderFrameDeleted", render_frame_host);
+ }
+
+ virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE {
+ LogWhatHappened("RenderProcessGone");
+ }
+
private:
- void AppendTreeNodeState(FrameTreeNode* node, std::string* result) {
- result->append(base::Int64ToString(node->frame_id()));
- if (!node->frame_name().empty()) {
- result->append(" '");
- result->append(node->frame_name());
- result->append("'");
- }
- result->append(": [");
- const char* separator = "";
- for (size_t i = 0; i < node->child_count(); i++) {
- result->append(separator);
- AppendTreeNodeState(node->child_at(i), result);
- separator = ", ";
+ void LogWhatHappened(const std::string& event_name) {
+ if (!log_.empty()) {
+ log_.append("\n");
}
- result->append("]");
+ log_.append(event_name + " -> ");
+ AppendTreeNodeState(
+ static_cast<WebContentsImpl*>(web_contents())->GetFrameTree()->root(),
+ &log_);
}
-};
-// The root node never changes during navigation even though its
-// RenderFrameHost does.
-// - Swapping main frame doesn't change root node.
-// - Swapping back to NULL doesn't crash (easier tear-down for interstitials).
-// - Main frame does not own RenderFrameHost.
-TEST_F(FrameTreeTest, RootNode) {
- FrameTree frame_tree(new NavigatorImpl(NULL, NULL), NULL, NULL, NULL, NULL);
-
- // Initial state has empty node.
- FrameTreeNode* root = frame_tree.root();
- ASSERT_TRUE(root);
- EXPECT_FALSE(frame_tree.GetMainFrame());
-
- // Swap in main frame.
- RenderFrameHostImpl* dummy = reinterpret_cast<RenderFrameHostImpl*>(0x1);
- frame_tree.SwapMainFrame(dummy);
- EXPECT_EQ(root, frame_tree.root());
- EXPECT_EQ(dummy, frame_tree.GetMainFrame());
-
- // Move back to NULL.
- frame_tree.SwapMainFrame(NULL);
- EXPECT_EQ(root, frame_tree.root());
- EXPECT_FALSE(frame_tree.GetMainFrame());
-
- // Move back to an invalid pointer, let the FrameTree go out of scope. Test
- // should not crash because the main frame isn't owned.
- frame_tree.SwapMainFrame(dummy);
-}
+ void LogWhatHappened(const std::string& event_name, RenderFrameHost* rfh) {
+ LogWhatHappened(
+ base::StringPrintf("%s(%d)", event_name.c_str(), rfh->GetRoutingID()));
+ }
-// Test that swapping the main frame resets the renderer-assigned frame id.
-// - On creation, frame id is unassigned.
-// - After a swap, frame id is unassigned.
-TEST_F(FrameTreeTest, FirstNavigationAfterSwap) {
- FrameTree frame_tree(new NavigatorImpl(NULL, NULL), NULL, NULL, NULL, NULL);
-
- EXPECT_TRUE(frame_tree.IsFirstNavigationAfterSwap());
- EXPECT_EQ(FrameTreeNode::kInvalidFrameId,
- frame_tree.root()->frame_id());
- frame_tree.OnFirstNavigationAfterSwap(1);
- EXPECT_FALSE(frame_tree.IsFirstNavigationAfterSwap());
- EXPECT_EQ(1, frame_tree.root()->frame_id());
-
- frame_tree.SwapMainFrame(NULL);
- EXPECT_TRUE(frame_tree.IsFirstNavigationAfterSwap());
- EXPECT_EQ(FrameTreeNode::kInvalidFrameId,
- frame_tree.root()->frame_id());
-}
+ std::string log_;
+
+ DISALLOW_COPY_AND_ASSIGN(TreeWalkingWebContentsLogger);
+};
+
+class FrameTreeTest : public RenderViewHostImplTestHarness {
+ protected:
+ // Prints a FrameTree, for easy assertions of the tree hierarchy.
+ std::string GetTreeState(FrameTree* frame_tree) {
+ std::string result;
+ AppendTreeNodeState(frame_tree->root(), &result);
+ return result;
+ }
+};
// Exercise tree manipulation routines.
// - Add a series of nodes and verify tree structure.
// - Remove a series of nodes and verify tree structure.
TEST_F(FrameTreeTest, Shape) {
- FrameTree frame_tree(new NavigatorImpl(NULL, NULL), NULL, NULL, NULL, NULL);
+ // Use the FrameTree of the WebContents so that it has all the delegates it
+ // needs. We may want to consider a test version of this.
+ FrameTree* frame_tree = contents()->GetFrameTree();
+ FrameTreeNode* root = frame_tree->root();
std::string no_children_node("no children node");
std::string deep_subtree("node with deep subtree");
- // Ensure the top-level node of the FrameTree is initialized by simulating a
- // main frame swap here.
- scoped_ptr<RenderFrameHostImpl> render_frame_host =
- RenderFrameHostFactory::Create(static_cast<RenderViewHostImpl*>(rvh()),
- NULL,
- &frame_tree,
- frame_tree.root(),
- process()->GetNextRoutingID(),
- false);
- frame_tree.SwapMainFrame(render_frame_host.get());
- frame_tree.OnFirstNavigationAfterSwap(5);
-
- ASSERT_EQ("5: []", GetTreeState(&frame_tree));
+ ASSERT_EQ("1: []", GetTreeState(frame_tree));
// Simulate attaching a series of frames to build the frame tree.
- frame_tree.AddFrame(process()->GetNextRoutingID(), 5, 14, std::string());
- frame_tree.AddFrame(process()->GetNextRoutingID(), 5, 15, std::string());
- frame_tree.AddFrame(process()->GetNextRoutingID(), 5, 16, std::string());
+ frame_tree->AddFrame(root, 14, std::string());
+ frame_tree->AddFrame(root, 15, std::string());
+ frame_tree->AddFrame(root, 16, std::string());
- frame_tree.AddFrame(process()->GetNextRoutingID(), 14, 244, std::string());
- frame_tree.AddFrame(process()->GetNextRoutingID(), 15, 255, no_children_node);
- frame_tree.AddFrame(process()->GetNextRoutingID(), 14, 245, std::string());
+ frame_tree->AddFrame(root->child_at(0), 244, std::string());
+ frame_tree->AddFrame(root->child_at(1), 255, no_children_node);
+ frame_tree->AddFrame(root->child_at(0), 245, std::string());
- ASSERT_EQ("5: [14: [244: [], 245: []], "
+ ASSERT_EQ("1: [14: [244: [], 245: []], "
"15: [255 'no children node': []], "
"16: []]",
- GetTreeState(&frame_tree));
-
- frame_tree.AddFrame(process()->GetNextRoutingID(), 16, 264, std::string());
- frame_tree.AddFrame(process()->GetNextRoutingID(), 16, 265, std::string());
- frame_tree.AddFrame(process()->GetNextRoutingID(), 16, 266, std::string());
- frame_tree.AddFrame(process()->GetNextRoutingID(), 16, 267, deep_subtree);
- frame_tree.AddFrame(process()->GetNextRoutingID(), 16, 268, std::string());
-
- frame_tree.AddFrame(process()->GetNextRoutingID(), 267, 365, std::string());
- frame_tree.AddFrame(process()->GetNextRoutingID(), 365, 455, std::string());
- frame_tree.AddFrame(process()->GetNextRoutingID(), 455, 555, std::string());
- frame_tree.AddFrame(process()->GetNextRoutingID(), 555, 655, std::string());
+ GetTreeState(frame_tree));
+
+ FrameTreeNode* child_16 = root->child_at(2);
+ frame_tree->AddFrame(child_16, 264, std::string());
+ frame_tree->AddFrame(child_16, 265, std::string());
+ frame_tree->AddFrame(child_16, 266, std::string());
+ frame_tree->AddFrame(child_16, 267, deep_subtree);
+ frame_tree->AddFrame(child_16, 268, std::string());
+
+ FrameTreeNode* child_267 = child_16->child_at(3);
+ frame_tree->AddFrame(child_267, 365, std::string());
+ frame_tree->AddFrame(child_267->child_at(0), 455, std::string());
+ frame_tree->AddFrame(child_267->child_at(0)->child_at(0), 555, std::string());
+ frame_tree->AddFrame(child_267->child_at(0)->child_at(0)->child_at(0), 655,
+ std::string());
// Now that's it's fully built, verify the tree structure is as expected.
- ASSERT_EQ("5: [14: [244: [], 245: []], "
+ ASSERT_EQ("1: [14: [244: [], 245: []], "
"15: [255 'no children node': []], "
"16: [264: [], 265: [], 266: [], "
"267 'node with deep subtree': "
"[365: [455: [555: [655: []]]]], 268: []]]",
- GetTreeState(&frame_tree));
+ GetTreeState(frame_tree));
- // Test removing of nodes.
- frame_tree.RemoveFrame(NULL, 555, 655);
- ASSERT_EQ("5: [14: [244: [], 245: []], "
+ FrameTreeNode* child_555 = child_267->child_at(0)->child_at(0)->child_at(0);
+ frame_tree->RemoveFrame(child_555);
+ ASSERT_EQ("1: [14: [244: [], 245: []], "
"15: [255 'no children node': []], "
"16: [264: [], 265: [], 266: [], "
"267 'node with deep subtree': "
- "[365: [455: [555: []]]], 268: []]]",
- GetTreeState(&frame_tree));
+ "[365: [455: []]], 268: []]]",
+ GetTreeState(frame_tree));
- frame_tree.RemoveFrame(NULL, 16, 265);
- ASSERT_EQ("5: [14: [244: [], 245: []], "
+ frame_tree->RemoveFrame(child_16->child_at(1));
+ ASSERT_EQ("1: [14: [244: [], 245: []], "
"15: [255 'no children node': []], "
"16: [264: [], 266: [], "
"267 'node with deep subtree': "
- "[365: [455: [555: []]]], 268: []]]",
- GetTreeState(&frame_tree));
+ "[365: [455: []]], 268: []]]",
+ GetTreeState(frame_tree));
- frame_tree.RemoveFrame(NULL, 5, 15);
- ASSERT_EQ("5: [14: [244: [], 245: []], "
+ frame_tree->RemoveFrame(root->child_at(1));
+ ASSERT_EQ("1: [14: [244: [], 245: []], "
"16: [264: [], 266: [], "
"267 'node with deep subtree': "
- "[365: [455: [555: []]]], 268: []]]",
- GetTreeState(&frame_tree));
+ "[365: [455: []]], 268: []]]",
+ GetTreeState(frame_tree));
+}
+
+// Do some simple manipulations of the frame tree, making sure that
+// WebContentsObservers see a consistent view of the tree as we go.
+TEST_F(FrameTreeTest, ObserverWalksTreeDuringFrameCreation) {
+ TreeWalkingWebContentsLogger activity(contents());
+ FrameTree* frame_tree = contents()->GetFrameTree();
+ FrameTreeNode* root = frame_tree->root();
+
+ // Simulate attaching a series of frames to build the frame tree.
+ main_test_rfh()->OnCreateChildFrame(14, std::string());
+ EXPECT_EQ("RenderFrameCreated(14) -> 1: [14: []]", activity.GetLog());
+ main_test_rfh()->OnCreateChildFrame(18, std::string());
+ EXPECT_EQ("RenderFrameCreated(18) -> 1: [14: [], 18: []]", activity.GetLog());
+ frame_tree->RemoveFrame(root->child_at(0));
+ EXPECT_EQ("RenderFrameDeleted(14) -> 1: [18: []]", activity.GetLog());
+ frame_tree->RemoveFrame(root->child_at(0));
+ EXPECT_EQ("RenderFrameDeleted(18) -> 1: []", activity.GetLog());
+}
+
+// Make sure that WebContentsObservers see a consistent view of the tree after
+// recovery from a render process crash.
+TEST_F(FrameTreeTest, ObserverWalksTreeAfterCrash) {
+ TreeWalkingWebContentsLogger activity(contents());
+
+ main_test_rfh()->OnCreateChildFrame(22, std::string());
+ EXPECT_EQ("RenderFrameCreated(22) -> 1: [22: []]", activity.GetLog());
+ main_test_rfh()->OnCreateChildFrame(23, std::string());
+ EXPECT_EQ("RenderFrameCreated(23) -> 1: [22: [], 23: []]", activity.GetLog());
+
+ // Crash the renderer
+ test_rvh()->OnMessageReceived(ViewHostMsg_RenderProcessGone(
+ 0, base::TERMINATION_STATUS_PROCESS_CRASHED, -1));
+ EXPECT_EQ(
+ "RenderFrameDeleted(22) -> 1: []\n"
+ "RenderFrameDeleted(23) -> 1: []\n"
+ "RenderProcessGone -> 1: []",
+ activity.GetLog());
}
} // namespace
diff --git a/chromium/content/browser/frame_host/interstitial_page_impl.cc b/chromium/content/browser/frame_host/interstitial_page_impl.cc
index 7af8d029f7e..a4ea2997bf7 100644
--- a/chromium/content/browser/frame_host/interstitial_page_impl.cc
+++ b/chromium/content/browser/frame_host/interstitial_page_impl.cc
@@ -19,14 +19,15 @@
#include "content/browser/frame_host/navigation_entry_impl.h"
#include "content/browser/loader/resource_dispatcher_host_impl.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
+#include "content/browser/renderer_host/render_view_host_delegate_view.h"
#include "content/browser/renderer_host/render_view_host_factory.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
+#include "content/browser/renderer_host/render_widget_host_view_base.h"
#include "content/browser/site_instance_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/browser/web_contents/web_contents_view.h"
+#include "content/common/frame_messages.h"
#include "content/common/view_messages.h"
-#include "content/port/browser/render_view_host_delegate_view.h"
-#include "content/port/browser/render_widget_host_view_port.h"
-#include "content/port/browser/web_contents_view_port.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
@@ -36,6 +37,7 @@
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/storage_partition.h"
+#include "content/public/browser/user_metrics.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/common/bindings_policy.h"
#include "content/public/common/page_transition_types.h"
@@ -75,6 +77,7 @@ class InterstitialPageImpl::InterstitialPageRVHDelegateView
explicit InterstitialPageRVHDelegateView(InterstitialPageImpl* page);
// RenderViewHostDelegateView implementation:
+#if defined(OS_MACOSX) || defined(OS_ANDROID)
virtual void ShowPopupMenu(const gfx::Rect& bounds,
int item_height,
double item_font_size,
@@ -82,6 +85,8 @@ class InterstitialPageImpl::InterstitialPageRVHDelegateView
const std::vector<MenuItem>& items,
bool right_aligned,
bool allow_multiple_selection) OVERRIDE;
+ virtual void HidePopupMenu() OVERRIDE;
+#endif
virtual void StartDragging(const DropData& drop_data,
WebDragOperationsMask operations_allowed,
const gfx::ImageSkia& image,
@@ -162,7 +167,8 @@ InterstitialPageImpl::InterstitialPageImpl(
// TODO(creis): We will also need to pass delegates for the RVHM as we
// start to use it.
frame_tree_(new InterstitialPageNavigatorImpl(this, controller_),
- NULL, NULL, NULL, NULL),
+ this, this, this,
+ static_cast<WebContentsImpl*>(web_contents)),
original_child_id_(web_contents->GetRenderProcessHost()->GetID()),
original_rvh_id_(web_contents->GetRenderViewHost()->GetRoutingID()),
should_revert_web_contents_title_(false),
@@ -247,9 +253,6 @@ void InterstitialPageImpl::Show() {
notification_registrar_.Add(this, NOTIFICATION_NAV_ENTRY_PENDING,
Source<NavigationController>(controller_));
- notification_registrar_.Add(
- this, NOTIFICATION_DOM_OPERATION_RESPONSE,
- Source<RenderViewHost>(render_view_host_));
}
void InterstitialPageImpl::Hide() {
@@ -280,19 +283,18 @@ void InterstitialPageImpl::Hide() {
if (render_view_host_->GetView() &&
render_view_host_->GetView()->HasFocus() &&
controller_->delegate()->GetRenderViewHost()->GetView()) {
- RenderWidgetHostViewPort::FromRWHV(
- controller_->delegate()->GetRenderViewHost()->GetView())->Focus();
+ controller_->delegate()->GetRenderViewHost()->GetView()->Focus();
}
- // Shutdown the RVH asynchronously, as we may have been called from a RVH
- // delegate method, and we can't delete the RVH out from under itself.
+ // Delete this and call Shutdown on the RVH asynchronously, as we may have
+ // been called from a RVH delegate method, and we can't delete the RVH out
+ // from under itself.
base::MessageLoop::current()->PostNonNestableTask(
FROM_HERE,
base::Bind(&InterstitialPageImpl::Shutdown,
- weak_ptr_factory_.GetWeakPtr(),
- render_view_host_));
+ weak_ptr_factory_.GetWeakPtr()));
render_view_host_ = NULL;
- frame_tree_.SwapMainFrame(NULL);
+ frame_tree_.ResetForMainFrameSwap();
controller_->delegate()->DetachInterstitialPage();
// Let's revert to the original title if necessary.
NavigationEntry* entry = controller_->GetVisibleEntry();
@@ -344,13 +346,6 @@ void InterstitialPageImpl::Observe(
TakeActionOnResourceDispatcher(CANCEL);
}
break;
- case NOTIFICATION_DOM_OPERATION_RESPONSE:
- if (enabled()) {
- Details<DomOperationNotificationDetails> dom_op_details(
- details);
- delegate_->CommandReceived(dom_op_details->json);
- }
- break;
default:
NOTREACHED();
}
@@ -361,15 +356,87 @@ void InterstitialPageImpl::NavigationEntryCommitted(
OnNavigatingAwayOrTabClosing();
}
-void InterstitialPageImpl::WebContentsDestroyed(WebContents* web_contents) {
+void InterstitialPageImpl::WebContentsDestroyed() {
OnNavigatingAwayOrTabClosing();
}
+bool InterstitialPageImpl::OnMessageReceived(
+ const IPC::Message& message,
+ RenderFrameHost* render_frame_host) {
+ return OnMessageReceived(message);
+}
+
+bool InterstitialPageImpl::OnMessageReceived(RenderFrameHost* render_frame_host,
+ const IPC::Message& message) {
+ return OnMessageReceived(message);
+}
+
+bool InterstitialPageImpl::OnMessageReceived(RenderViewHost* render_view_host,
+ const IPC::Message& message) {
+ return OnMessageReceived(message);
+}
+
+bool InterstitialPageImpl::OnMessageReceived(const IPC::Message& message) {
+
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(InterstitialPageImpl, message)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_DomOperationResponse,
+ OnDomOperationResponse)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+
+ return handled;
+}
+
+void InterstitialPageImpl::RenderFrameCreated(
+ RenderFrameHost* render_frame_host) {
+ // Note this is only for subframes in the interstitial, the notification for
+ // the main frame happens in RenderViewCreated.
+ controller_->delegate()->RenderFrameForInterstitialPageCreated(
+ render_frame_host);
+}
+
+void InterstitialPageImpl::UpdateTitle(
+ RenderFrameHost* render_frame_host,
+ int32 page_id,
+ const base::string16& title,
+ base::i18n::TextDirection title_direction) {
+ if (!enabled())
+ return;
+
+ RenderViewHost* render_view_host = render_frame_host->GetRenderViewHost();
+ DCHECK(render_view_host == render_view_host_);
+ NavigationEntry* entry = controller_->GetVisibleEntry();
+ if (!entry) {
+ // Crash reports from the field indicate this can be NULL.
+ // This is unexpected as InterstitialPages constructed with the
+ // new_navigation flag set to true create a transient navigation entry
+ // (that is returned as the active entry). And the only case so far of
+ // interstitial created with that flag set to false is with the
+ // SafeBrowsingBlockingPage, when the resource triggering the interstitial
+ // is a sub-resource, meaning the main page has already been loaded and a
+ // navigation entry should have been created.
+ NOTREACHED();
+ return;
+ }
+
+ // If this interstitial is shown on an existing navigation entry, we'll need
+ // to remember its title so we can revert to it when hidden.
+ if (!new_navigation_ && !should_revert_web_contents_title_) {
+ original_web_contents_title_ = entry->GetTitle();
+ should_revert_web_contents_title_ = true;
+ }
+ // TODO(evan): make use of title_direction.
+ // http://code.google.com/p/chromium/issues/detail?id=27094
+ entry->SetTitle(title);
+ controller_->delegate()->NotifyNavigationStateChanged(INVALIDATE_TYPE_TITLE);
+}
+
RenderViewHostDelegateView* InterstitialPageImpl::GetDelegateView() {
return rvh_delegate_view_.get();
}
-const GURL& InterstitialPageImpl::GetURL() const {
+const GURL& InterstitialPageImpl::GetMainFrameLastCommittedURL() const {
return url_;
}
@@ -387,7 +454,7 @@ void InterstitialPageImpl::RenderViewTerminated(
void InterstitialPageImpl::DidNavigate(
RenderViewHost* render_view_host,
- const ViewHostMsg_FrameNavigate_Params& params) {
+ const FrameHostMsg_DidCommitProvisionalLoad_Params& params) {
// A fast user could have navigated away from the page that triggered the
// interstitial while the interstitial was loading, that would have disabled
// us. In that case we can dismiss ourselves.
@@ -426,42 +493,7 @@ void InterstitialPageImpl::DidNavigate(
// an interstitial would hang.
web_contents_was_loading_ = controller_->delegate()->IsLoading();
controller_->delegate()->SetIsLoading(
- controller_->delegate()->GetRenderViewHost(), false, NULL);
-}
-
-void InterstitialPageImpl::UpdateTitle(
- RenderViewHost* render_view_host,
- int32 page_id,
- const base::string16& title,
- base::i18n::TextDirection title_direction) {
- if (!enabled())
- return;
-
- DCHECK(render_view_host == render_view_host_);
- NavigationEntry* entry = controller_->GetVisibleEntry();
- if (!entry) {
- // Crash reports from the field indicate this can be NULL.
- // This is unexpected as InterstitialPages constructed with the
- // new_navigation flag set to true create a transient navigation entry
- // (that is returned as the active entry). And the only case so far of
- // interstitial created with that flag set to false is with the
- // SafeBrowsingBlockingPage, when the resource triggering the interstitial
- // is a sub-resource, meaning the main page has already been loaded and a
- // navigation entry should have been created.
- NOTREACHED();
- return;
- }
-
- // If this interstitial is shown on an existing navigation entry, we'll need
- // to remember its title so we can revert to it when hidden.
- if (!new_navigation_ && !should_revert_web_contents_title_) {
- original_web_contents_title_ = entry->GetTitle();
- should_revert_web_contents_title_ = true;
- }
- // TODO(evan): make use of title_direction.
- // http://code.google.com/p/chromium/issues/detail?id=27094
- entry->SetTitle(title);
- controller_->delegate()->NotifyNavigationStateChanged(INVALIDATE_TYPE_TITLE);
+ controller_->delegate()->GetRenderViewHost(), false, true, NULL);
}
RendererPreferences InterstitialPageImpl::GetRendererPrefs(
@@ -479,7 +511,8 @@ WebPreferences InterstitialPageImpl::GetWebkitPrefs() {
void InterstitialPageImpl::RenderWidgetDeleted(
RenderWidgetHostImpl* render_widget_host) {
- delete this;
+ // TODO(creis): Remove this method once we verify the shutdown path is sane.
+ CHECK(!web_contents_);
}
bool InterstitialPageImpl::PreHandleKeyboardEvent(
@@ -497,7 +530,7 @@ void InterstitialPageImpl::HandleKeyboardEvent(
render_widget_host_delegate_->HandleKeyboardEvent(event);
}
-#if defined(OS_WIN) && defined(USE_AURA)
+#if defined(OS_WIN)
gfx::NativeViewAccessible
InterstitialPageImpl::GetParentNativeViewAccessible() {
return render_widget_host_delegate_->GetParentNativeViewAccessible();
@@ -524,24 +557,19 @@ RenderViewHost* InterstitialPageImpl::CreateRenderViewHost() {
session_storage_namespace_ =
new SessionStorageNamespaceImpl(dom_storage_context);
- return RenderViewHostFactory::Create(site_instance.get(),
- this,
- this,
- this,
- MSG_ROUTING_NONE,
- MSG_ROUTING_NONE,
- false,
- false);
+ // Use the RenderViewHost from our FrameTree.
+ frame_tree_.root()->render_manager()->Init(
+ browser_context, site_instance.get(), MSG_ROUTING_NONE, MSG_ROUTING_NONE);
+ return frame_tree_.root()->current_frame_host()->render_view_host();
}
WebContentsView* InterstitialPageImpl::CreateWebContentsView() {
if (!enabled() || !create_view_)
return NULL;
- WebContentsView* web_contents_view = web_contents()->GetView();
- WebContentsViewPort* web_contents_view_port =
- static_cast<WebContentsViewPort*>(web_contents_view);
- RenderWidgetHostView* view =
- web_contents_view_port->CreateViewForWidget(render_view_host_);
+ WebContentsView* wcv =
+ static_cast<WebContentsImpl*>(web_contents())->GetView();
+ RenderWidgetHostViewBase* view =
+ wcv->CreateViewForWidget(render_view_host_);
render_view_host_->SetView(view);
render_view_host_->AllowBindings(BINDINGS_POLICY_DOM_AUTOMATION);
@@ -549,13 +577,15 @@ WebContentsView* InterstitialPageImpl::CreateWebContentsView() {
GetMaxPageIDForSiteInstance(render_view_host_->GetSiteInstance());
render_view_host_->CreateRenderView(base::string16(),
MSG_ROUTING_NONE,
- max_page_id);
- controller_->delegate()->RenderViewForInterstitialPageCreated(
- render_view_host_);
- view->SetSize(web_contents_view->GetContainerSize());
+ MSG_ROUTING_NONE,
+ max_page_id,
+ false);
+ controller_->delegate()->RenderFrameForInterstitialPageCreated(
+ frame_tree_.root()->current_frame_host());
+ view->SetSize(web_contents()->GetContainerBounds().size());
// Don't show the interstitial until we have navigated to it.
view->Hide();
- return web_contents_view;
+ return wcv;
}
void InterstitialPageImpl::Proceed() {
@@ -574,7 +604,7 @@ void InterstitialPageImpl::Proceed() {
// Resumes the throbber, if applicable.
if (web_contents_was_loading_)
controller_->delegate()->SetIsLoading(
- controller_->delegate()->GetRenderViewHost(), true, NULL);
+ controller_->delegate()->GetRenderViewHost(), true, true, NULL);
// If this is a new navigation, the old page is going away, so we cancel any
// blocked requests for it. If it is not a new navigation, then it means the
@@ -663,7 +693,7 @@ void InterstitialPageImpl::Focus() {
// Focus the native window.
if (!enabled())
return;
- RenderWidgetHostViewPort::FromRWHV(render_view_host_->GetView())->Focus();
+ render_view_host_->GetView()->Focus();
}
void InterstitialPageImpl::FocusThroughTabTraversal(bool reverse) {
@@ -749,9 +779,8 @@ void InterstitialPageImpl::Disable() {
enabled_ = false;
}
-void InterstitialPageImpl::Shutdown(RenderViewHostImpl* render_view_host) {
- render_view_host->Shutdown();
- // We are deleted now.
+void InterstitialPageImpl::Shutdown() {
+ delete this;
}
void InterstitialPageImpl::OnNavigatingAwayOrTabClosing() {
@@ -800,11 +829,28 @@ void InterstitialPageImpl::TakeActionOnResourceDispatcher(
action));
}
+void InterstitialPageImpl::OnDomOperationResponse(
+ const std::string& json_string,
+ int automation_id) {
+ // Needed by test code.
+ DomOperationNotificationDetails details(json_string, automation_id);
+ NotificationService::current()->Notify(
+ NOTIFICATION_DOM_OPERATION_RESPONSE,
+ Source<WebContents>(web_contents()),
+ Details<DomOperationNotificationDetails>(&details));
+
+ if (!enabled())
+ return;
+ delegate_->CommandReceived(details.json);
+}
+
+
InterstitialPageImpl::InterstitialPageRVHDelegateView::
InterstitialPageRVHDelegateView(InterstitialPageImpl* page)
: interstitial_page_(page) {
}
+#if defined(OS_MACOSX) || defined(OS_ANDROID)
void InterstitialPageImpl::InterstitialPageRVHDelegateView::ShowPopupMenu(
const gfx::Rect& bounds,
int item_height,
@@ -816,13 +862,19 @@ void InterstitialPageImpl::InterstitialPageRVHDelegateView::ShowPopupMenu(
NOTREACHED() << "InterstitialPage does not support showing popup menus.";
}
+void InterstitialPageImpl::InterstitialPageRVHDelegateView::HidePopupMenu() {
+ NOTREACHED() << "InterstitialPage does not support showing popup menus.";
+}
+#endif
+
void InterstitialPageImpl::InterstitialPageRVHDelegateView::StartDragging(
const DropData& drop_data,
WebDragOperationsMask allowed_operations,
const gfx::ImageSkia& image,
const gfx::Vector2d& image_offset,
const DragEventSourceInfo& event_info) {
- NOTREACHED() << "InterstitialPage does not support dragging yet.";
+ interstitial_page_->render_view_host_->DragSourceSystemDragEnded();
+ DVLOG(1) << "InterstitialPage does not support dragging yet.";
}
void InterstitialPageImpl::InterstitialPageRVHDelegateView::UpdateDragCursor(
diff --git a/chromium/content/browser/frame_host/interstitial_page_impl.h b/chromium/content/browser/frame_host/interstitial_page_impl.h
index fddeacd10f4..8068fd34e01 100644
--- a/chromium/content/browser/frame_host/interstitial_page_impl.h
+++ b/chromium/content/browser/frame_host/interstitial_page_impl.h
@@ -13,6 +13,7 @@
#include "content/browser/frame_host/render_frame_host_delegate.h"
#include "content/browser/renderer_host/render_view_host_delegate.h"
#include "content/browser/renderer_host/render_widget_host_delegate.h"
+#include "content/public/browser/dom_operation_notification_details.h"
#include "content/public/browser/interstitial_page.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
@@ -26,7 +27,6 @@ class NavigationControllerImpl;
class RenderViewHostImpl;
class RenderWidgetHostView;
class WebContentsView;
-class WebContentsImpl;
enum ResourceRequestAction {
BLOCK,
@@ -91,6 +91,12 @@ class CONTENT_EXPORT InterstitialPageImpl
RenderViewHost* GetRenderViewHost() const;
#endif
+ // TODO(nasko): This should move to InterstitialPageNavigatorImpl, but in
+ // the meantime make it public, so it can be called directly.
+ void DidNavigate(
+ RenderViewHost* render_view_host,
+ const FrameHostMsg_DidCommitProvisionalLoad_Params& params);
+
protected:
// NotificationObserver method:
virtual void Observe(int type,
@@ -98,25 +104,30 @@ class CONTENT_EXPORT InterstitialPageImpl
const NotificationDetails& details) OVERRIDE;
// WebContentsObserver implementation:
- virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message,
+ RenderFrameHost* render_frame_host) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+ virtual void WebContentsDestroyed() OVERRIDE;
virtual void NavigationEntryCommitted(
const LoadCommittedDetails& load_details) OVERRIDE;
// RenderFrameHostDelegate implementation:
+ virtual bool OnMessageReceived(RenderFrameHost* render_frame_host,
+ const IPC::Message& message) OVERRIDE;
+ virtual void RenderFrameCreated(RenderFrameHost* render_frame_host) OVERRIDE;
+ virtual void UpdateTitle(RenderFrameHost* render_frame_host,
+ int32 page_id,
+ const base::string16& title,
+ base::i18n::TextDirection title_direction) OVERRIDE;
// RenderViewHostDelegate implementation:
virtual RenderViewHostDelegateView* GetDelegateView() OVERRIDE;
- virtual const GURL& GetURL() const OVERRIDE;
+ virtual bool OnMessageReceived(RenderViewHost* render_view_host,
+ const IPC::Message& message) OVERRIDE;
+ virtual const GURL& GetMainFrameLastCommittedURL() const OVERRIDE;
virtual void RenderViewTerminated(RenderViewHost* render_view_host,
base::TerminationStatus status,
int error_code) OVERRIDE;
- virtual void DidNavigate(
- RenderViewHost* render_view_host,
- const ViewHostMsg_FrameNavigate_Params& params) OVERRIDE;
- virtual void UpdateTitle(RenderViewHost* render_view_host,
- int32 page_id,
- const base::string16& title,
- base::i18n::TextDirection title_direction) OVERRIDE;
virtual RendererPreferences GetRendererPrefs(
BrowserContext* browser_context) const OVERRIDE;
virtual WebPreferences GetWebkitPrefs() OVERRIDE;
@@ -153,7 +164,7 @@ class CONTENT_EXPORT InterstitialPageImpl
bool* is_keyboard_shortcut) OVERRIDE;
virtual void HandleKeyboardEvent(
const NativeWebKeyboardEvent& event) OVERRIDE;
-#if defined(OS_WIN) && defined(USE_AURA)
+#if defined(OS_WIN)
virtual gfx::NativeViewAccessible GetParentNativeViewAccessible() OVERRIDE;
#endif
@@ -180,8 +191,8 @@ class CONTENT_EXPORT InterstitialPageImpl
// - any command sent by the RenderViewHost will be ignored.
void Disable();
- // Shutdown the RVH. We will be deleted by the time this method returns.
- void Shutdown(RenderViewHostImpl* render_view_host);
+ // Delete ourselves, causing Shutdown on the RVH to be called.
+ void Shutdown();
void OnNavigatingAwayOrTabClosing();
@@ -190,6 +201,10 @@ class CONTENT_EXPORT InterstitialPageImpl
// interstitial.
void TakeActionOnResourceDispatcher(ResourceRequestAction action);
+ // IPC message handlers.
+ void OnDomOperationResponse(const std::string& json_string,
+ int automation_id);
+
// The contents in which we are displayed. This is valid until Hide is
// called, at which point it will be set to NULL because the WebContents
// itself may be deleted.
@@ -229,6 +244,8 @@ class CONTENT_EXPORT InterstitialPageImpl
// The RenderViewHost displaying the interstitial contents. This is valid
// until Hide is called, at which point it will be set to NULL, signifying
// that shutdown has started.
+ // TODO(creis): This is now owned by the FrameTree. We should route things
+ // through the tree's root RenderFrameHost instead.
RenderViewHostImpl* render_view_host_;
// The frame tree structure of the current page.
diff --git a/chromium/content/browser/frame_host/interstitial_page_navigator_impl.cc b/chromium/content/browser/frame_host/interstitial_page_navigator_impl.cc
index 830e1bfde1d..fc45950742d 100644
--- a/chromium/content/browser/frame_host/interstitial_page_navigator_impl.cc
+++ b/chromium/content/browser/frame_host/interstitial_page_navigator_impl.cc
@@ -5,13 +5,29 @@
#include "content/browser/frame_host/interstitial_page_navigator_impl.h"
#include "content/browser/frame_host/interstitial_page_impl.h"
+#include "content/browser/frame_host/navigation_controller_impl.h"
#include "content/browser/frame_host/navigator_delegate.h"
+#include "content/browser/renderer_host/render_view_host_impl.h"
namespace content {
InterstitialPageNavigatorImpl::InterstitialPageNavigatorImpl(
InterstitialPageImpl* interstitial,
- NavigationControllerImpl* navigation_controller) {
+ NavigationControllerImpl* navigation_controller)
+ : interstitial_(interstitial),
+ controller_(navigation_controller) {}
+
+NavigationController* InterstitialPageNavigatorImpl::GetController() {
+ return controller_;
+}
+
+void InterstitialPageNavigatorImpl::DidNavigate(
+ RenderFrameHostImpl* render_frame_host,
+ const FrameHostMsg_DidCommitProvisionalLoad_Params& input_params) {
+ // TODO(nasko): Move implementation here, but for the time being call out
+ // to the interstitial page code.
+ interstitial_->DidNavigate(
+ render_frame_host->render_view_host(), input_params);
}
} // namespace content
diff --git a/chromium/content/browser/frame_host/interstitial_page_navigator_impl.h b/chromium/content/browser/frame_host/interstitial_page_navigator_impl.h
index 1b55fd21a3c..707754661e0 100644
--- a/chromium/content/browser/frame_host/interstitial_page_navigator_impl.h
+++ b/chromium/content/browser/frame_host/interstitial_page_navigator_impl.h
@@ -22,9 +22,22 @@ class CONTENT_EXPORT InterstitialPageNavigatorImpl : public Navigator {
InterstitialPageImpl* interstitial,
NavigationControllerImpl* navigation_controller);
+ virtual NavigationController* GetController() OVERRIDE;
+ virtual void DidNavigate(
+ RenderFrameHostImpl* render_frame_host,
+ const FrameHostMsg_DidCommitProvisionalLoad_Params&
+ input_params) OVERRIDE;
+
private:
virtual ~InterstitialPageNavigatorImpl() {}
+ // The InterstitialPage with which this navigator object is associated.
+ // Non owned pointer.
+ InterstitialPageImpl* interstitial_;
+
+ // The NavigationController associated with this navigator.
+ NavigationControllerImpl* controller_;
+
DISALLOW_COPY_AND_ASSIGN(InterstitialPageNavigatorImpl);
};
diff --git a/chromium/content/browser/frame_host/navigation_controller_delegate.h b/chromium/content/browser/frame_host/navigation_controller_delegate.h
index cb3204cde63..49dfc60bef0 100644
--- a/chromium/content/browser/frame_host/navigation_controller_delegate.h
+++ b/chromium/content/browser/frame_host/navigation_controller_delegate.h
@@ -16,6 +16,7 @@ struct LoadNotificationDetails;
struct NativeWebKeyboardEvent;
class InterstitialPage;
class InterstitialPageImpl;
+class RenderFrameHost;
class RenderViewHost;
class SiteInstance;
class WebContents;
@@ -35,11 +36,12 @@ class NavigationControllerDelegate {
virtual const std::string& GetContentsMimeType() const = 0;
virtual void NotifyNavigationStateChanged(unsigned changed_flags) = 0;
virtual void Stop() = 0;
- virtual SiteInstance* GetSiteInstance() const = 0;
virtual SiteInstance* GetPendingSiteInstance() const = 0;
virtual int32 GetMaxPageID() = 0;
virtual int32 GetMaxPageIDForSiteInstance(SiteInstance* site_instance) = 0;
virtual bool IsLoading() const = 0;
+ virtual bool IsBeingDestroyed() const = 0;
+ virtual bool CanOverscrollContent() const = 0;
// Methods from WebContentsImpl that NavigationControllerImpl needs to
// call.
@@ -57,6 +59,7 @@ class NavigationControllerDelegate {
virtual void UpdateMaxPageIDForSiteInstance(SiteInstance* site_instance,
int32 page_id) = 0;
virtual void ActivateAndShowRepostFormWarningDialog() = 0;
+ virtual bool HasAccessedInitialDocument() = 0;
// This method is needed, since we are no longer guaranteed that the
// embedder for NavigationController will be a WebContents object.
@@ -64,13 +67,14 @@ class NavigationControllerDelegate {
// Methods needed by InterstitialPageImpl.
virtual bool IsHidden() = 0;
- virtual void RenderViewForInterstitialPageCreated(
- RenderViewHost* render_view_host) = 0;
+ virtual void RenderFrameForInterstitialPageCreated(
+ RenderFrameHost* render_frame_host) = 0;
virtual void AttachInterstitialPage(
InterstitialPageImpl* interstitial_page) = 0;
virtual void DetachInterstitialPage() = 0;
virtual void SetIsLoading(RenderViewHost* render_view_host,
bool is_loading,
+ bool to_different_document,
LoadNotificationDetails* details) = 0;
};
diff --git a/chromium/content/browser/frame_host/navigation_controller_impl.cc b/chromium/content/browser/frame_host/navigation_controller_impl.cc
index 40f133679a5..6d1c597d760 100644
--- a/chromium/content/browser/frame_host/navigation_controller_impl.cc
+++ b/chromium/content/browser/frame_host/navigation_controller_impl.cc
@@ -5,12 +5,15 @@
#include "content/browser/frame_host/navigation_controller_impl.h"
#include "base/bind.h"
+#include "base/command_line.h"
#include "base/debug/trace_event.h"
#include "base/logging.h"
+#include "base/metrics/histogram.h"
#include "base/strings/string_number_conversions.h" // Temporary
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
+#include "cc/base/switches.h"
#include "content/browser/browser_url_handler_impl.h"
#include "content/browser/dom_storage/dom_storage_context_wrapper.h"
#include "content/browser/dom_storage/session_storage_namespace_impl.h"
@@ -20,6 +23,7 @@
#include "content/browser/frame_host/navigation_entry_screenshot_manager.h"
#include "content/browser/renderer_host/render_view_host_impl.h" // Temporary
#include "content/browser/site_instance_impl.h"
+#include "content/common/frame_messages.h"
#include "content/common/view_messages.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/content_browser_client.h"
@@ -33,11 +37,11 @@
#include "content/public/browser/user_metrics.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_constants.h"
-#include "content/public/common/url_constants.h"
#include "net/base/escape.h"
#include "net/base/mime_util.h"
#include "net/base/net_util.h"
#include "skia/ext/platform_canvas.h"
+#include "url/url_constants.h"
namespace content {
namespace {
@@ -105,7 +109,7 @@ bool AreURLsInPageNavigation(const GURL& existing_url,
const GURL& new_url,
bool renderer_says_in_page,
NavigationType navigation_type) {
- if (existing_url == new_url)
+ if (existing_url.GetOrigin() == new_url.GetOrigin())
return renderer_says_in_page;
if (!new_url.has_ref()) {
@@ -114,7 +118,7 @@ bool AreURLsInPageNavigation(const GURL& existing_url,
return navigation_type == NAVIGATION_TYPE_IN_PAGE;
}
- url_canon::Replacements<char> replacements;
+ url::Replacements<char> replacements;
replacements.ClearRef();
return existing_url.ReplaceComponents(replacements) ==
new_url.ReplaceComponents(replacements);
@@ -207,6 +211,7 @@ NavigationControllerImpl::NavigationControllerImpl(
ssl_manager_(this),
needs_reload_(false),
is_initial_navigation_(true),
+ in_navigate_to_pending_entry_(false),
pending_reload_(NO_RELOAD),
get_timestamp_callback_(base::Bind(&base::Time::Now)),
screenshot_manager_(new NavigationEntryScreenshotManager(this)) {
@@ -308,6 +313,7 @@ void NavigationControllerImpl::ReloadInternal(bool check_for_repost,
// POST wasn't involved; the latter case avoids issues with sending data to
// the wrong page.
entry->SetURL(entry->GetOriginalRequestURL());
+ entry->SetReferrer(Referrer());
}
if (g_check_for_repost && check_for_repost &&
@@ -332,9 +338,9 @@ void NavigationControllerImpl::ReloadInternal(bool check_for_repost,
// instance, and should not be treated as a cross-site reload.
SiteInstanceImpl* site_instance = entry->site_instance();
// Permit reloading guests without further checks.
- bool is_guest = site_instance && site_instance->HasProcess() &&
- site_instance->GetProcess()->IsGuest();
- if (!is_guest && site_instance &&
+ bool is_isolated_guest = site_instance && site_instance->HasProcess() &&
+ site_instance->GetProcess()->IsIsolatedGuest();
+ if (!is_isolated_guest && site_instance &&
site_instance->HasWrongProcessForURL(entry->GetURL())) {
// Create a navigation entry that resembles the current one, but do not
// copy page id, site instance, content state, or timestamp.
@@ -426,17 +432,12 @@ NavigationEntry* NavigationControllerImpl::GetVisibleEntry() const {
// long as no other page has tried to access the initial empty document in
// the new tab. If another page modifies this blank page, a URL spoof is
// possible, so we must stop showing the pending entry.
- RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
- delegate_->GetRenderViewHost());
bool safe_to_show_pending =
pending_entry_ &&
// Require a new navigation.
pending_entry_->GetPageID() == -1 &&
// Require either browser-initiated or an unmodified new tab.
- (!pending_entry_->is_renderer_initiated() ||
- (IsInitialNavigation() &&
- !GetLastCommittedEntry() &&
- !rvh->has_accessed_initial_document()));
+ (!pending_entry_->is_renderer_initiated() || IsUnmodifiedBlankTab());
// Also allow showing the pending entry for history navigations in a new tab,
// such as Ctrl+Back. In this case, no existing page is visible and no one
@@ -656,14 +657,14 @@ void NavigationControllerImpl::LoadURLWithParams(const LoadURLParams& params) {
case LOAD_TYPE_DEFAULT:
break;
case LOAD_TYPE_BROWSER_INITIATED_HTTP_POST:
- if (!params.url.SchemeIs(kHttpScheme) &&
- !params.url.SchemeIs(kHttpsScheme)) {
+ if (!params.url.SchemeIs(url::kHttpScheme) &&
+ !params.url.SchemeIs(url::kHttpsScheme)) {
NOTREACHED() << "Http post load must use http(s) scheme.";
return;
}
break;
case LOAD_TYPE_DATA:
- if (!params.url.SchemeIs(chrome::kDataScheme)) {
+ if (!params.url.SchemeIs(url::kDataScheme)) {
NOTREACHED() << "Data load must use data scheme.";
return;
}
@@ -703,7 +704,7 @@ void NavigationControllerImpl::LoadURLWithParams(const LoadURLParams& params) {
if (params.frame_tree_node_id != -1)
entry->set_frame_tree_node_id(params.frame_tree_node_id);
if (params.redirect_chain.size() > 0)
- entry->set_redirect_chain(params.redirect_chain);
+ entry->SetRedirectChain(params.redirect_chain);
if (params.should_replace_current_entry)
entry->set_should_replace_entry(true);
entry->set_should_clear_history_list(params.should_clear_history_list);
@@ -734,7 +735,8 @@ void NavigationControllerImpl::LoadURLWithParams(const LoadURLParams& params) {
}
bool NavigationControllerImpl::RendererDidNavigate(
- const ViewHostMsg_FrameNavigate_Params& params,
+ RenderFrameHost* rfh,
+ const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
LoadCommittedDetails* details) {
is_initial_navigation_ = false;
@@ -761,7 +763,7 @@ bool NavigationControllerImpl::RendererDidNavigate(
pending_entry_ && pending_entry_->should_replace_entry();
// Do navigation-type specific actions. These will make and commit an entry.
- details->type = ClassifyNavigation(params);
+ details->type = ClassifyNavigation(rfh, params);
// is_in_page must be computed before the entry gets committed.
details->is_in_page = IsURLInPageNavigation(
@@ -769,22 +771,22 @@ bool NavigationControllerImpl::RendererDidNavigate(
switch (details->type) {
case NAVIGATION_TYPE_NEW_PAGE:
- RendererDidNavigateToNewPage(params, details->did_replace_entry);
+ RendererDidNavigateToNewPage(rfh, params, details->did_replace_entry);
break;
case NAVIGATION_TYPE_EXISTING_PAGE:
- RendererDidNavigateToExistingPage(params);
+ RendererDidNavigateToExistingPage(rfh, params);
break;
case NAVIGATION_TYPE_SAME_PAGE:
- RendererDidNavigateToSamePage(params);
+ RendererDidNavigateToSamePage(rfh, params);
break;
case NAVIGATION_TYPE_IN_PAGE:
- RendererDidNavigateInPage(params, &details->did_replace_entry);
+ RendererDidNavigateInPage(rfh, params, &details->did_replace_entry);
break;
case NAVIGATION_TYPE_NEW_SUBFRAME:
- RendererDidNavigateNewSubframe(params);
+ RendererDidNavigateNewSubframe(rfh, params);
break;
case NAVIGATION_TYPE_AUTO_SUBFRAME:
- if (!RendererDidNavigateAutoSubframe(params))
+ if (!RendererDidNavigateAutoSubframe(rfh, params))
return false;
break;
case NAVIGATION_TYPE_NAV_IGNORE:
@@ -823,18 +825,30 @@ bool NavigationControllerImpl::RendererDidNavigate(
active_entry->SetTimestamp(timestamp);
active_entry->SetHttpStatusCode(params.http_status_code);
active_entry->SetPageState(params.page_state);
+ active_entry->SetRedirectChain(params.redirects);
+
+ // Use histogram to track memory impact of redirect chain because it's now
+ // not cleared for committed entries.
+ size_t redirect_chain_size = 0;
+ for (size_t i = 0; i < params.redirects.size(); ++i) {
+ redirect_chain_size += params.redirects[i].spec().length();
+ }
+ UMA_HISTOGRAM_COUNTS("Navigation.RedirectChainSize", redirect_chain_size);
// Once it is committed, we no longer need to track several pieces of state on
// the entry.
active_entry->ResetForCommit();
// The active entry's SiteInstance should match our SiteInstance.
- CHECK(active_entry->site_instance() == delegate_->GetSiteInstance());
+ // TODO(creis): This check won't pass for subframes until we create entries
+ // for subframe navigations.
+ if (PageTransitionIsMainFrame(params.transition))
+ CHECK(active_entry->site_instance() == rfh->GetSiteInstance());
// Remember the bindings the renderer process has at this point, so that
// we do not grant this entry additional bindings if we come back to it.
active_entry->SetBindings(
- delegate_->GetRenderViewHost()->GetEnabledBindings());
+ static_cast<RenderFrameHostImpl*>(rfh)->GetEnabledBindings());
// Now prep the rest of the details for the notification and broadcast.
details->entry = active_entry;
@@ -848,7 +862,8 @@ bool NavigationControllerImpl::RendererDidNavigate(
}
NavigationType NavigationControllerImpl::ClassifyNavigation(
- const ViewHostMsg_FrameNavigate_Params& params) const {
+ RenderFrameHost* rfh,
+ const FrameHostMsg_DidCommitProvisionalLoad_Params& params) const {
if (params.page_id == -1) {
// The renderer generates the page IDs, and so if it gives us the invalid
// page ID (-1) we know it didn't actually navigate. This happens in a few
@@ -871,7 +886,8 @@ NavigationType NavigationControllerImpl::ClassifyNavigation(
return NAVIGATION_TYPE_NAV_IGNORE;
}
- if (params.page_id > delegate_->GetMaxPageID()) {
+ if (params.page_id > delegate_->GetMaxPageIDForSiteInstance(
+ rfh->GetSiteInstance())) {
// Greater page IDs than we've ever seen before are new pages. We may or may
// not have a pending entry for the page, and this may or may not be the
// main frame.
@@ -895,7 +911,7 @@ NavigationType NavigationControllerImpl::ClassifyNavigation(
// Now we know that the notification is for an existing page. Find that entry.
int existing_entry_index = GetEntryIndexWithPageID(
- delegate_->GetSiteInstance(),
+ rfh->GetSiteInstance(),
params.page_id);
if (existing_entry_index == -1) {
// The page was not found. It could have been pruned because of the limit on
@@ -906,7 +922,7 @@ NavigationType NavigationControllerImpl::ClassifyNavigation(
// Because the unknown entry has committed, we risk showing the wrong URL in
// release builds. Instead, we'll kill the renderer process to be safe.
LOG(ERROR) << "terminating renderer for bad navigation: " << params.url;
- RecordAction(UserMetricsAction("BadMessageTerminate_NC"));
+ RecordAction(base::UserMetricsAction("BadMessageTerminate_NC"));
// Temporary code so we can get more information. Format:
// http://url/foo.html#page1#max3#frame1#ids:2_Nx,1_1x,3_2
@@ -916,7 +932,7 @@ NavigationType NavigationControllerImpl::ClassifyNavigation(
temp.append("#max");
temp.append(base::IntToString(delegate_->GetMaxPageID()));
temp.append("#frame");
- temp.append(base::IntToString(params.frame_id));
+ temp.append(base::IntToString(rfh->GetRoutingID()));
temp.append("#ids");
for (int i = 0; i < static_cast<int>(entries_.size()); ++i) {
// Append entry metadata (e.g., 3_7x):
@@ -929,14 +945,13 @@ NavigationType NavigationControllerImpl::ClassifyNavigation(
temp.append(base::IntToString(entries_[i]->site_instance()->GetId()));
else
temp.append("N");
- if (entries_[i]->site_instance() != delegate_->GetSiteInstance())
+ if (entries_[i]->site_instance() != rfh->GetSiteInstance())
temp.append("x");
temp.append(",");
}
GURL url(temp);
- static_cast<RenderViewHostImpl*>(
- delegate_->GetRenderViewHost())->Send(
- new ViewMsg_TempCrashWithData(url));
+ static_cast<RenderFrameHostImpl*>(rfh)->render_view_host()->Send(
+ new ViewMsg_TempCrashWithData(url));
return NAVIGATION_TYPE_NAV_IGNORE;
}
NavigationEntryImpl* existing_entry = entries_[existing_entry_index].get();
@@ -982,7 +997,9 @@ NavigationType NavigationControllerImpl::ClassifyNavigation(
}
void NavigationControllerImpl::RendererDidNavigateToNewPage(
- const ViewHostMsg_FrameNavigate_Params& params, bool replace_entry) {
+ RenderFrameHost* rfh,
+ const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
+ bool replace_entry) {
NavigationEntryImpl* new_entry;
bool update_virtual_url;
// Only make a copy of the pending entry if it is appropriate for the new page
@@ -990,7 +1007,7 @@ void NavigationControllerImpl::RendererDidNavigateToNewPage(
// the SiteInstance hasn't been assigned to something else.
if (pending_entry_ &&
(!pending_entry_->site_instance() ||
- pending_entry_->site_instance() == delegate_->GetSiteInstance())) {
+ pending_entry_->site_instance() == rfh->GetSiteInstance())) {
new_entry = new NavigationEntryImpl(*pending_entry_);
// Don't use the page type from the pending entry. Some interstitial page
@@ -1006,8 +1023,18 @@ void NavigationControllerImpl::RendererDidNavigateToNewPage(
// update the virtual URL when replaceState is called after a pushState.
GURL url = params.url;
bool needs_update = false;
- BrowserURLHandlerImpl::GetInstance()->RewriteURLIfNecessary(
- &url, browser_context_, &needs_update);
+ // We call RewriteURLIfNecessary twice: once when page navigation
+ // begins in CreateNavigationEntry, and once here when it commits.
+ // With the kEnableGpuBenchmarking flag, the rewriting includes
+ // handling debug URLs which cause an action to occur, and thus we
+ // should not rewrite them a second time.
+ bool skip_rewrite =
+ IsDebugURL(url) && base::CommandLine::ForCurrentProcess()->HasSwitch(
+ cc::switches::kEnableGpuBenchmarking);
+ if (!skip_rewrite) {
+ BrowserURLHandlerImpl::GetInstance()->RewriteURLIfNecessary(
+ &url, browser_context_, &needs_update);
+ }
new_entry->set_update_virtual_url_with_url(needs_update);
// When navigating to a new page, give the browser URL handler a chance to
@@ -1024,12 +1051,18 @@ void NavigationControllerImpl::RendererDidNavigateToNewPage(
new_entry->SetPageID(params.page_id);
new_entry->SetTransitionType(params.transition);
new_entry->set_site_instance(
- static_cast<SiteInstanceImpl*>(delegate_->GetSiteInstance()));
+ static_cast<SiteInstanceImpl*>(rfh->GetSiteInstance()));
new_entry->SetHasPostData(params.is_post);
new_entry->SetPostID(params.post_id);
new_entry->SetOriginalRequestURL(params.original_request_url);
new_entry->SetIsOverridingUserAgent(params.is_overriding_user_agent);
+ // history.pushState() is classified as a navigation to a new page, but
+ // sets was_within_same_page to true. In this case, we already have the
+ // title available, so set it immediately.
+ if (params.was_within_same_page && GetLastCommittedEntry())
+ new_entry->SetTitle(GetLastCommittedEntry()->GetTitle());
+
DCHECK(!params.history_list_was_cleared || !replace_entry);
// The browser requested to clear the session history when it initiated the
// navigation. Now we know that the renderer has updated its state accordingly
@@ -1044,14 +1077,15 @@ void NavigationControllerImpl::RendererDidNavigateToNewPage(
}
void NavigationControllerImpl::RendererDidNavigateToExistingPage(
- const ViewHostMsg_FrameNavigate_Params& params) {
+ RenderFrameHost* rfh,
+ const FrameHostMsg_DidCommitProvisionalLoad_Params& params) {
// We should only get here for main frame navigations.
DCHECK(PageTransitionIsMainFrame(params.transition));
// This is a back/forward navigation. The existing page for the ID is
// guaranteed to exist by ClassifyNavigation, and we just need to update it
// with new information from the renderer.
- int entry_index = GetEntryIndexWithPageID(delegate_->GetSiteInstance(),
+ int entry_index = GetEntryIndexWithPageID(rfh->GetSiteInstance(),
params.page_id);
DCHECK(entry_index >= 0 &&
entry_index < static_cast<int>(entries_.size()));
@@ -1059,6 +1093,7 @@ void NavigationControllerImpl::RendererDidNavigateToExistingPage(
// The URL may have changed due to redirects.
entry->SetURL(params.url);
+ entry->SetReferrer(params.referrer);
if (entry->update_virtual_url_with_url())
UpdateVirtualURLToURL(entry, params.url);
@@ -1070,9 +1105,9 @@ void NavigationControllerImpl::RendererDidNavigateToExistingPage(
// The site instance will normally be the same except during session restore,
// when no site instance will be assigned.
DCHECK(entry->site_instance() == NULL ||
- entry->site_instance() == delegate_->GetSiteInstance());
+ entry->site_instance() == rfh->GetSiteInstance());
entry->set_site_instance(
- static_cast<SiteInstanceImpl*>(delegate_->GetSiteInstance()));
+ static_cast<SiteInstanceImpl*>(rfh->GetSiteInstance()));
entry->SetHasPostData(params.is_post);
entry->SetPostID(params.post_id);
@@ -1091,16 +1126,17 @@ void NavigationControllerImpl::RendererDidNavigateToExistingPage(
// If a transient entry was removed, the indices might have changed, so we
// have to query the entry index again.
last_committed_entry_index_ =
- GetEntryIndexWithPageID(delegate_->GetSiteInstance(), params.page_id);
+ GetEntryIndexWithPageID(rfh->GetSiteInstance(), params.page_id);
}
void NavigationControllerImpl::RendererDidNavigateToSamePage(
- const ViewHostMsg_FrameNavigate_Params& params) {
+ RenderFrameHost* rfh,
+ const FrameHostMsg_DidCommitProvisionalLoad_Params& params) {
// This mode implies we have a pending entry that's the same as an existing
// entry for this page ID. This entry is guaranteed to exist by
// ClassifyNavigation. All we need to do is update the existing entry.
NavigationEntryImpl* existing_entry = GetEntryWithPageID(
- delegate_->GetSiteInstance(), params.page_id);
+ rfh->GetSiteInstance(), params.page_id);
// We assign the entry's unique ID to be that of the new one. Since this is
// always the result of a user action, we want to dismiss infobars, etc. like
@@ -1111,6 +1147,7 @@ void NavigationControllerImpl::RendererDidNavigateToSamePage(
if (existing_entry->update_virtual_url_with_url())
UpdateVirtualURLToURL(existing_entry, params.url);
existing_entry->SetURL(params.url);
+ existing_entry->SetReferrer(params.referrer);
// The page may have been requested with a different HTTP method.
existing_entry->SetHasPostData(params.is_post);
@@ -1120,12 +1157,14 @@ void NavigationControllerImpl::RendererDidNavigateToSamePage(
}
void NavigationControllerImpl::RendererDidNavigateInPage(
- const ViewHostMsg_FrameNavigate_Params& params, bool* did_replace_entry) {
+ RenderFrameHost* rfh,
+ const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
+ bool* did_replace_entry) {
DCHECK(PageTransitionIsMainFrame(params.transition)) <<
"WebKit should only tell us about in-page navs for the main frame.";
// We're guaranteed to have an entry for this one.
NavigationEntryImpl* existing_entry = GetEntryWithPageID(
- delegate_->GetSiteInstance(), params.page_id);
+ rfh->GetSiteInstance(), params.page_id);
// Reference fragment navigation. We're guaranteed to have the last_committed
// entry and it will be the same page as the new navigation (minus the
@@ -1135,6 +1174,9 @@ void NavigationControllerImpl::RendererDidNavigateInPage(
if (existing_entry->update_virtual_url_with_url())
UpdateVirtualURLToURL(existing_entry, params.url);
+ existing_entry->SetHasPostData(params.is_post);
+ existing_entry->SetPostID(params.post_id);
+
// This replaces the existing entry since the page ID didn't change.
*did_replace_entry = true;
@@ -1143,11 +1185,12 @@ void NavigationControllerImpl::RendererDidNavigateInPage(
// If a transient entry was removed, the indices might have changed, so we
// have to query the entry index again.
last_committed_entry_index_ =
- GetEntryIndexWithPageID(delegate_->GetSiteInstance(), params.page_id);
+ GetEntryIndexWithPageID(rfh->GetSiteInstance(), params.page_id);
}
void NavigationControllerImpl::RendererDidNavigateNewSubframe(
- const ViewHostMsg_FrameNavigate_Params& params) {
+ RenderFrameHost* rfh,
+ const FrameHostMsg_DidCommitProvisionalLoad_Params& params) {
if (PageTransitionCoreTypeIs(params.transition,
PAGE_TRANSITION_AUTO_SUBFRAME)) {
// This is not user-initiated. Ignore.
@@ -1168,7 +1211,8 @@ void NavigationControllerImpl::RendererDidNavigateNewSubframe(
}
bool NavigationControllerImpl::RendererDidNavigateAutoSubframe(
- const ViewHostMsg_FrameNavigate_Params& params) {
+ RenderFrameHost* rfh,
+ const FrameHostMsg_DidCommitProvisionalLoad_Params& params) {
// We're guaranteed to have a previously committed entry, and we now need to
// handle navigation inside of a subframe in it without creating a new entry.
DCHECK(GetLastCommittedEntry());
@@ -1177,7 +1221,7 @@ bool NavigationControllerImpl::RendererDidNavigateAutoSubframe(
// navigation entry. This is case "2." in NAV_AUTO_SUBFRAME comment in the
// header file. In case "1." this will be a NOP.
int entry_index = GetEntryIndexWithPageID(
- delegate_->GetSiteInstance(),
+ rfh->GetSiteInstance(),
params.page_id);
if (entry_index < 0 ||
entry_index >= static_cast<int>(entries_.size())) {
@@ -1302,6 +1346,7 @@ void NavigationControllerImpl::CopyStateFromAndPrune(
// that new and existing navigations in the tab's current SiteInstances
// are identified properly.
delegate_->CopyMaxPageIDsFrom(source->delegate()->GetWebContents());
+ max_restored_page_id_ = source->max_restored_page_id_;
// If there is a last committed entry, be sure to include it in the new
// max page ID map.
@@ -1390,6 +1435,12 @@ int32 NavigationControllerImpl::GetMaxRestoredPageID() const {
return max_restored_page_id_;
}
+bool NavigationControllerImpl::IsUnmodifiedBlankTab() const {
+ return IsInitialNavigation() &&
+ !GetLastCommittedEntry() &&
+ !delegate_->HasAccessedInitialDocument();
+}
+
SessionStorageNamespace*
NavigationControllerImpl::GetSessionStorageNamespace(SiteInstance* instance) {
std::string partition_id;
@@ -1564,13 +1615,17 @@ void NavigationControllerImpl::NavigateToPendingEntry(ReloadType reload_type) {
pending_entry_ = entries_[pending_entry_index_].get();
}
- if (!delegate_->NavigateToPendingEntry(reload_type))
+ // This call does not support re-entrancy. See http://crbug.com/347742.
+ CHECK(!in_navigate_to_pending_entry_);
+ in_navigate_to_pending_entry_ = true;
+ bool success = delegate_->NavigateToPendingEntry(reload_type);
+ in_navigate_to_pending_entry_ = false;
+
+ if (!success)
DiscardNonCommittedEntries();
// If the entry is being restored and doesn't have a SiteInstance yet, fill
// it in now that we know. This allows us to find the entry when it commits.
- // This works for browser-initiated navigations. We handle renderer-initiated
- // navigations to restored entries in WebContentsImpl::OnGoToEntryAtOffset.
if (pending_entry_ && !pending_entry_->site_instance() &&
pending_entry_->restore_type() != NavigationEntryImpl::RESTORE_NONE) {
pending_entry_->set_site_instance(static_cast<SiteInstanceImpl*>(
@@ -1650,6 +1705,12 @@ void NavigationControllerImpl::DiscardNonCommittedEntriesInternal() {
}
void NavigationControllerImpl::DiscardPendingEntry() {
+ // It is not safe to call DiscardPendingEntry while NavigateToEntry is in
+ // progress, since this will cause a use-after-free. (We only allow this
+ // when the tab is being destroyed for shutdown, since it won't return to
+ // NavigateToEntry in that case.) http://crbug.com/347742.
+ CHECK(!in_navigate_to_pending_entry_ || delegate_->IsBeingDestroyed());
+
if (pending_entry_index_ == -1)
delete pending_entry_;
pending_entry_ = NULL;
diff --git a/chromium/content/browser/frame_host/navigation_controller_impl.h b/chromium/content/browser/frame_host/navigation_controller_impl.h
index 99fc36ae002..7a06ba663b6 100644
--- a/chromium/content/browser/frame_host/navigation_controller_impl.h
+++ b/chromium/content/browser/frame_host/navigation_controller_impl.h
@@ -16,7 +16,7 @@
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_type.h"
-struct ViewHostMsg_FrameNavigate_Params;
+struct FrameHostMsg_DidCommitProvisionalLoad_Params;
namespace content {
class NavigationEntryImpl;
@@ -94,6 +94,10 @@ class CONTENT_EXPORT NavigationControllerImpl
virtual void PruneAllButLastCommitted() OVERRIDE;
virtual void ClearAllScreenshots() OVERRIDE;
+ // Whether this is the initial navigation in an unmodified new tab. In this
+ // case, we know there is no content displayed in the page.
+ bool IsUnmodifiedBlankTab() const;
+
// The session storage namespace that all child RenderViews belonging to
// |instance| should use.
SessionStorageNamespace* GetSessionStorageNamespace(
@@ -134,8 +138,10 @@ class CONTENT_EXPORT NavigationControllerImpl
//
// In the case that nothing has changed, the details structure is undefined
// and it will return false.
- bool RendererDidNavigate(const ViewHostMsg_FrameNavigate_Params& params,
- LoadCommittedDetails* details);
+ bool RendererDidNavigate(
+ RenderFrameHost* rfh,
+ const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
+ LoadCommittedDetails* details);
// Notifies us that we just became active. This is used by the WebContentsImpl
// so that we know to load URLs that were pending as "lazy" loads.
@@ -153,15 +159,11 @@ class CONTENT_EXPORT NavigationControllerImpl
// whether a navigation happened without loading anything, the same URL could
// be a reload, while only a different ref would be in-page (pages can't clear
// refs without reload, only change to "#" which we don't count as empty).
- bool IsURLInPageNavigation(const GURL& url) const {
- return IsURLInPageNavigation(url, false, NAVIGATION_TYPE_UNKNOWN);
- }
-
+ //
// The situation is made murkier by history.replaceState(), which could
// provide the same URL as part of an in-page navigation, not a reload. So
- // we need this form which lets the (untrustworthy) renderer resolve the
- // ambiguity, but only when the URLs are equal. This should be safe since the
- // origin isn't changing.
+ // we need to let the (untrustworthy) renderer resolve the ambiguity, but
+ // only when the URLs are on the same origin.
bool IsURLInPageNavigation(
const GURL& url,
bool renderer_says_in_page,
@@ -231,7 +233,8 @@ class CONTENT_EXPORT NavigationControllerImpl
// Classifies the given renderer navigation (see the NavigationType enum).
NavigationType ClassifyNavigation(
- const ViewHostMsg_FrameNavigate_Params& params) const;
+ RenderFrameHost* rfh,
+ const FrameHostMsg_DidCommitProvisionalLoad_Params& params) const;
// Causes the controller to load the specified entry. The function assumes
// ownership of the pointer since it is put in the navigation list.
@@ -251,17 +254,25 @@ class CONTENT_EXPORT NavigationControllerImpl
// whether the last entry has been replaced or not.
// See LoadCommittedDetails.did_replace_entry.
void RendererDidNavigateToNewPage(
- const ViewHostMsg_FrameNavigate_Params& params, bool replace_entry);
+ RenderFrameHost* rfh,
+ const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
+ bool replace_entry);
void RendererDidNavigateToExistingPage(
- const ViewHostMsg_FrameNavigate_Params& params);
+ RenderFrameHost* rfh,
+ const FrameHostMsg_DidCommitProvisionalLoad_Params& params);
void RendererDidNavigateToSamePage(
- const ViewHostMsg_FrameNavigate_Params& params);
+ RenderFrameHost* rfh,
+ const FrameHostMsg_DidCommitProvisionalLoad_Params& params);
void RendererDidNavigateInPage(
- const ViewHostMsg_FrameNavigate_Params& params, bool* did_replace_entry);
+ RenderFrameHost* rfh,
+ const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
+ bool* did_replace_entry);
void RendererDidNavigateNewSubframe(
- const ViewHostMsg_FrameNavigate_Params& params);
+ RenderFrameHost* rfh,
+ const FrameHostMsg_DidCommitProvisionalLoad_Params& params);
bool RendererDidNavigateAutoSubframe(
- const ViewHostMsg_FrameNavigate_Params& params);
+ RenderFrameHost* rfh,
+ const FrameHostMsg_DidCommitProvisionalLoad_Params& params);
// Helper function for code shared between Reload() and ReloadIgnoringCache().
void ReloadInternal(bool check_for_repost, ReloadType reload_type);
@@ -372,6 +383,9 @@ class CONTENT_EXPORT NavigationControllerImpl
// Becomes false when initial navigation commits.
bool is_initial_navigation_;
+ // Prevent unsafe re-entrant calls to NavigateToPendingEntry.
+ bool in_navigate_to_pending_entry_;
+
// Used to find the appropriate SessionStorageNamespace for the storage
// partition of a NavigationEntry.
//
diff --git a/chromium/content/browser/frame_host/navigation_controller_impl_browsertest.cc b/chromium/content/browser/frame_host/navigation_controller_impl_browsertest.cc
new file mode 100644
index 00000000000..20c00ee3edf
--- /dev/null
+++ b/chromium/content/browser/frame_host/navigation_controller_impl_browsertest.cc
@@ -0,0 +1,34 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "content/browser/frame_host/navigation_controller_impl.h"
+#include "content/browser/frame_host/navigation_entry_impl.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/content_browser_test.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/shell/browser/shell.h"
+
+namespace content {
+
+class NavigationControllerBrowserTest : public ContentBrowserTest {
+};
+
+IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest, LoadDataWithBaseURL) {
+ const GURL base_url("http://baseurl");
+ const GURL history_url("http://historyurl");
+ const std::string data = "<html><body>foo</body></html>";
+
+ const NavigationController& controller =
+ shell()->web_contents()->GetController();
+ // load data. Blocks until it is done.
+ content::LoadDataWithBaseURL(shell(), history_url, data, base_url);
+
+ // We should use history_url instead of the base_url as the original url of
+ // this navigation entry, because base_url is only used for resolving relative
+ // paths in the data, or enforcing same origin policy.
+ EXPECT_EQ(controller.GetVisibleEntry()->GetOriginalRequestURL(), history_url);
+}
+} // namespace content
+
diff --git a/chromium/content/browser/frame_host/navigation_controller_impl_unittest.cc b/chromium/content/browser/frame_host/navigation_controller_impl_unittest.cc
index 68f753a5e91..956288c33d9 100644
--- a/chromium/content/browser/frame_host/navigation_controller_impl_unittest.cc
+++ b/chromium/content/browser/frame_host/navigation_controller_impl_unittest.cc
@@ -11,12 +11,14 @@
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
+#include "content/browser/frame_host/cross_site_transferring_request.h"
#include "content/browser/frame_host/navigation_controller_impl.h"
#include "content/browser/frame_host/navigation_entry_impl.h"
#include "content/browser/frame_host/navigation_entry_screenshot_manager.h"
#include "content/browser/frame_host/navigator.h"
#include "content/browser/site_instance_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/common/frame_messages.h"
#include "content/common/view_messages.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/notification_registrar.h"
@@ -79,7 +81,7 @@ class MockScreenshotManager : public content::NavigationEntryScreenshotManager {
SkBitmap bitmap;
bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1, 1);
bitmap.allocPixels();
- bitmap.eraseRGB(0, 0, 0);
+ bitmap.eraseARGB(0, 0, 0, 0);
encoding_screenshot_in_progress_ = true;
OnScreenshotTaken(entry->GetUniqueID(), true, bitmap);
WaitUntilScreenshotIsReady();
@@ -233,21 +235,33 @@ SiteInstance* GetSiteInstanceFromEntry(NavigationEntry* entry) {
class TestWebContentsDelegate : public WebContentsDelegate {
public:
explicit TestWebContentsDelegate() :
- navigation_state_change_count_(0) {}
+ navigation_state_change_count_(0),
+ repost_form_warning_count_(0) {}
int navigation_state_change_count() {
return navigation_state_change_count_;
}
+ int repost_form_warning_count() {
+ return repost_form_warning_count_;
+ }
+
// Keep track of whether the tab has notified us of a navigation state change.
virtual void NavigationStateChanged(const WebContents* source,
unsigned changed_flags) OVERRIDE {
navigation_state_change_count_++;
}
+ virtual void ShowRepostFormWarningDialog(WebContents* source) OVERRIDE {
+ repost_form_warning_count_++;
+ }
+
private:
// The number of times NavigationStateChanged has been called.
int navigation_state_change_count_;
+
+ // The number of times ShowRepostFormWarningDialog() was called.
+ int repost_form_warning_count_;
};
// -----------------------------------------------------------------------------
@@ -276,7 +290,7 @@ TEST_F(NavigationControllerTest, GoToOffset) {
urls[i] = GURL(base::StringPrintf("http://www.a.com/%d", i));
}
- test_rvh()->SendNavigate(0, urls[0]);
+ main_test_rfh()->SendNavigate(0, urls[0]);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
EXPECT_EQ(urls[0], controller.GetVisibleEntry()->GetVirtualURL());
@@ -285,7 +299,7 @@ TEST_F(NavigationControllerTest, GoToOffset) {
EXPECT_FALSE(controller.CanGoToOffset(1));
for (int i = 1; i <= 4; ++i) {
- test_rvh()->SendNavigate(i, urls[i]);
+ main_test_rfh()->SendNavigate(i, urls[i]);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
EXPECT_EQ(urls[i], controller.GetVisibleEntry()->GetVirtualURL());
@@ -320,7 +334,7 @@ TEST_F(NavigationControllerTest, GoToOffset) {
url_index += offset;
// Check that the GoToOffset will land on the expected page.
EXPECT_EQ(urls[url_index], controller.GetPendingEntry()->GetVirtualURL());
- test_rvh()->SendNavigate(url_index, urls[url_index]);
+ main_test_rfh()->SendNavigate(url_index, urls[url_index]);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
// Check that we can go to any valid offset into the history.
@@ -363,7 +377,7 @@ TEST_F(NavigationControllerTest, LoadURL) {
// We should have gotten no notifications from the preceeding checks.
EXPECT_EQ(0U, notifications.size());
- test_rvh()->SendNavigate(0, url1);
+ main_test_rfh()->SendNavigate(0, url1);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
@@ -402,7 +416,7 @@ TEST_F(NavigationControllerTest, LoadURL) {
// Simulate the beforeunload ack for the cross-site transition, and then the
// commit.
- test_rvh()->SendShouldCloseACK(true);
+ test_rvh()->SendBeforeUnloadACK(true);
static_cast<TestRenderViewHost*>(
contents()->GetPendingRenderViewHost())->SendNavigate(1, url2);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
@@ -444,7 +458,7 @@ TEST_F(NavigationControllerTest, LoadURLSameTime) {
controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
- test_rvh()->SendNavigate(0, url1);
+ main_test_rfh()->SendNavigate(0, url1);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
@@ -453,8 +467,8 @@ TEST_F(NavigationControllerTest, LoadURLSameTime) {
// Simulate the beforeunload ack for the cross-site transition, and then the
// commit.
- test_rvh()->SendShouldCloseACK(true);
- test_rvh()->SendNavigate(1, url2);
+ test_rvh()->SendBeforeUnloadACK(true);
+ main_test_rfh()->SendNavigate(1, url2);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
@@ -527,7 +541,7 @@ TEST_F(NavigationControllerTest, LoadURLWithExtraParams_Data) {
GURL("data:text/html,dataurl"));
load_params.load_type = NavigationController::LOAD_TYPE_DATA;
load_params.base_url_for_data_url = GURL("http://foo");
- load_params.virtual_url_for_data_url = GURL(kAboutBlankURL);
+ load_params.virtual_url_for_data_url = GURL(url::kAboutBlankURL);
load_params.override_user_agent = NavigationController::UA_OVERRIDE_FALSE;
controller.LoadURLWithParams(load_params);
@@ -577,7 +591,7 @@ TEST_F(NavigationControllerTest, LoadURL_SamePage) {
controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
EXPECT_EQ(0U, notifications.size());
- test_rvh()->SendNavigate(0, url1);
+ main_test_rfh()->SendNavigate(0, url1);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
@@ -587,7 +601,7 @@ TEST_F(NavigationControllerTest, LoadURL_SamePage) {
controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
EXPECT_EQ(0U, notifications.size());
- test_rvh()->SendNavigate(0, url1);
+ main_test_rfh()->SendNavigate(0, url1);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
@@ -618,14 +632,14 @@ TEST_F(NavigationControllerTest, LoadURL_SamePage_DifferentMethod) {
const GURL url1("http://foo1");
controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
- ViewHostMsg_FrameNavigate_Params params;
+ FrameHostMsg_DidCommitProvisionalLoad_Params params;
params.page_id = 0;
params.url = url1;
params.transition = PAGE_TRANSITION_TYPED;
params.is_post = true;
params.post_id = 123;
params.page_state = PageState::CreateForTesting(url1, false, 0, 0);
- test_rvh()->SendNavigateWithParams(&params);
+ main_test_rfh()->SendNavigateWithParams(&params);
// The post data should be visible.
NavigationEntry* entry = controller.GetVisibleEntry();
@@ -634,7 +648,7 @@ TEST_F(NavigationControllerTest, LoadURL_SamePage_DifferentMethod) {
EXPECT_EQ(entry->GetPostID(), 123);
controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
- test_rvh()->SendNavigate(0, url1);
+ main_test_rfh()->SendNavigate(0, url1);
// We should not have produced a new session history entry.
ASSERT_EQ(controller.GetVisibleEntry(), entry);
@@ -655,7 +669,7 @@ TEST_F(NavigationControllerTest, LoadURL_Discarded) {
controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
EXPECT_EQ(0U, notifications.size());
- test_rvh()->SendNavigate(0, url1);
+ main_test_rfh()->SendNavigate(0, url1);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
@@ -692,13 +706,13 @@ TEST_F(NavigationControllerTest, LoadURL_NoPending) {
const GURL kExistingURL1("http://eh");
controller.LoadURL(
kExistingURL1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
- test_rvh()->SendNavigate(0, kExistingURL1);
+ main_test_rfh()->SendNavigate(0, kExistingURL1);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
// Do a new navigation without making a pending one.
const GURL kNewURL("http://see");
- test_rvh()->SendNavigate(99, kNewURL);
+ main_test_rfh()->SendNavigate(99, kNewURL);
// There should no longer be any pending entry, and the third navigation we
// just made should be committed.
@@ -722,7 +736,7 @@ TEST_F(NavigationControllerTest, LoadURL_NewPending) {
const GURL kExistingURL1("http://eh");
controller.LoadURL(
kExistingURL1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
- test_rvh()->SendNavigate(0, kExistingURL1);
+ main_test_rfh()->SendNavigate(0, kExistingURL1);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
@@ -733,7 +747,7 @@ TEST_F(NavigationControllerTest, LoadURL_NewPending) {
EXPECT_EQ(0U, notifications.size());
// After the beforeunload but before it commits, do a new navigation.
- test_rvh()->SendShouldCloseACK(true);
+ test_rvh()->SendBeforeUnloadACK(true);
const GURL kNewURL("http://see");
static_cast<TestRenderViewHost*>(
contents()->GetPendingRenderViewHost())->SendNavigate(3, kNewURL);
@@ -759,14 +773,14 @@ TEST_F(NavigationControllerTest, LoadURL_ExistingPending) {
const GURL kExistingURL1("http://foo/eh");
controller.LoadURL(
kExistingURL1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
- test_rvh()->SendNavigate(0, kExistingURL1);
+ main_test_rfh()->SendNavigate(0, kExistingURL1);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
const GURL kExistingURL2("http://foo/bee");
controller.LoadURL(
kExistingURL2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
- test_rvh()->SendNavigate(1, kExistingURL2);
+ main_test_rfh()->SendNavigate(1, kExistingURL2);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
@@ -780,7 +794,7 @@ TEST_F(NavigationControllerTest, LoadURL_ExistingPending) {
// Before that commits, do a new navigation.
const GURL kNewURL("http://foo/see");
LoadCommittedDetails details;
- test_rvh()->SendNavigate(3, kNewURL);
+ main_test_rfh()->SendNavigate(3, kNewURL);
// There should no longer be any pending entry, and the third navigation we
// just made should be committed.
@@ -805,7 +819,7 @@ TEST_F(NavigationControllerTest, LoadURL_PrivilegedPending) {
kExistingURL1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
// Pretend it has bindings so we can tell if we incorrectly copy it.
test_rvh()->AllowBindings(2);
- test_rvh()->SendNavigate(0, kExistingURL1);
+ main_test_rfh()->SendNavigate(0, kExistingURL1);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
@@ -813,7 +827,7 @@ TEST_F(NavigationControllerTest, LoadURL_PrivilegedPending) {
const GURL kExistingURL2("http://foo/eh");
controller.LoadURL(
kExistingURL2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
- test_rvh()->SendShouldCloseACK(true);
+ test_rvh()->SendBeforeUnloadACK(true);
TestRenderViewHost* foo_rvh = static_cast<TestRenderViewHost*>(
contents()->GetPendingRenderViewHost());
foo_rvh->SendNavigate(1, kExistingURL2);
@@ -823,7 +837,7 @@ TEST_F(NavigationControllerTest, LoadURL_PrivilegedPending) {
// Now make a pending back/forward navigation to a privileged entry.
// The zeroth entry should be pending.
controller.GoBack();
- foo_rvh->SendShouldCloseACK(true);
+ foo_rvh->SendBeforeUnloadACK(true);
EXPECT_EQ(0U, notifications.size());
EXPECT_EQ(0, controller.GetPendingEntryIndex());
EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
@@ -858,14 +872,14 @@ TEST_F(NavigationControllerTest, LoadURL_BackPreemptsPending) {
const GURL kExistingURL1("http://foo/eh");
controller.LoadURL(
kExistingURL1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
- test_rvh()->SendNavigate(0, kExistingURL1);
+ main_test_rfh()->SendNavigate(0, kExistingURL1);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
const GURL kExistingURL2("http://foo/bee");
controller.LoadURL(
kExistingURL2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
- test_rvh()->SendNavigate(1, kExistingURL2);
+ main_test_rfh()->SendNavigate(1, kExistingURL2);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
@@ -878,7 +892,7 @@ TEST_F(NavigationControllerTest, LoadURL_BackPreemptsPending) {
EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
// Before that commits, a back navigation from the renderer commits.
- test_rvh()->SendNavigate(0, kExistingURL1);
+ main_test_rfh()->SendNavigate(0, kExistingURL1);
// There should no longer be any pending entry, and the back navigation we
// just made should be committed.
@@ -903,7 +917,7 @@ TEST_F(NavigationControllerTest, LoadURL_IgnorePreemptsPending) {
contents()->SetDelegate(delegate.get());
// Without any navigations, the renderer starts at about:blank.
- const GURL kExistingURL(kAboutBlankURL);
+ const GURL kExistingURL(url::kAboutBlankURL);
// Now make a pending new navigation.
const GURL kNewURL("http://eh");
@@ -917,7 +931,7 @@ TEST_F(NavigationControllerTest, LoadURL_IgnorePreemptsPending) {
// Before that commits, a document.write and location.reload can cause the
// renderer to send a FrameNavigate with page_id -1.
- test_rvh()->SendNavigate(-1, kExistingURL);
+ main_test_rfh()->SendNavigate(-1, kExistingURL);
// This should clear the pending entry and notify of a navigation state
// change, so that we do not keep displaying kNewURL.
@@ -954,16 +968,14 @@ TEST_F(NavigationControllerTest, LoadURL_AbortDoesntCancelPending) {
// It may abort before committing, if it's a download or due to a stop or
// a new navigation from the user.
- ViewHostMsg_DidFailProvisionalLoadWithError_Params params;
- params.frame_id = 1;
- params.is_main_frame = true;
+ FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
params.error_code = net::ERR_ABORTED;
params.error_description = base::string16();
params.url = kNewURL;
params.showing_repost_interstitial = false;
- test_rvh()->OnMessageReceived(
- ViewHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
- params));
+ main_test_rfh()->OnMessageReceived(
+ FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
+ params));
// This should not clear the pending entry or notify of a navigation state
// change, so that we keep displaying kNewURL (until the user clears it).
@@ -994,7 +1006,7 @@ TEST_F(NavigationControllerTest, LoadURL_RedirectAbortDoesntShowPendingURL) {
const GURL kExistingURL("http://foo/eh");
controller.LoadURL(kExistingURL, content::Referrer(),
content::PAGE_TRANSITION_TYPED, std::string());
- test_rvh()->SendNavigate(0, kExistingURL);
+ main_test_rfh()->SendNavigate(0, kExistingURL);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
@@ -1020,11 +1032,11 @@ TEST_F(NavigationControllerTest, LoadURL_RedirectAbortDoesntShowPendingURL) {
// Now the navigation redirects.
const GURL kRedirectURL("http://foo/see");
- test_rvh()->OnMessageReceived(
- ViewHostMsg_DidRedirectProvisionalLoad(0, // routing_id
- -1, // pending page_id
- kNewURL, // old url
- kRedirectURL)); // new url
+ main_test_rfh()->OnMessageReceived(
+ FrameHostMsg_DidRedirectProvisionalLoad(0, // routing_id
+ -1, // pending page_id
+ kNewURL, // old url
+ kRedirectURL)); // new url
// We don't want to change the NavigationEntry's url, in case it cancels.
// Prevents regression of http://crbug.com/77786.
@@ -1032,23 +1044,21 @@ TEST_F(NavigationControllerTest, LoadURL_RedirectAbortDoesntShowPendingURL) {
// It may abort before committing, if it's a download or due to a stop or
// a new navigation from the user.
- ViewHostMsg_DidFailProvisionalLoadWithError_Params params;
- params.frame_id = 1;
- params.is_main_frame = true;
+ FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
params.error_code = net::ERR_ABORTED;
params.error_description = base::string16();
params.url = kRedirectURL;
params.showing_repost_interstitial = false;
- test_rvh()->OnMessageReceived(
- ViewHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
- params));
+ main_test_rfh()->OnMessageReceived(
+ FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
+ params));
// Because the pending entry is renderer initiated and not visible, we
// clear it when it fails.
EXPECT_EQ(-1, controller.GetPendingEntryIndex());
EXPECT_FALSE(controller.GetPendingEntry());
EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
- EXPECT_EQ(0, delegate->navigation_state_change_count());
+ EXPECT_EQ(1, delegate->navigation_state_change_count());
// The visible entry should be the last committed URL, not the pending one,
// so that no spoof is possible.
@@ -1063,6 +1073,7 @@ TEST_F(NavigationControllerTest, LoadURL_WithBindings) {
NavigationControllerImpl& controller = controller_impl();
TestNotificationTracker notifications;
RegisterForAllNavNotifications(&notifications, &controller);
+ std::vector<GURL> url_chain;
const GURL url1("http://foo1");
const GURL url2("http://foo2");
@@ -1089,13 +1100,18 @@ TEST_F(NavigationControllerTest, LoadURL_WithBindings) {
increment_active_view_count();
// Navigate to a second URL, simulate the beforeunload ack for the cross-site
- // transition, and set bindings on the pending RenderViewHost to simulate a
- // privileged url.
+ // transition, run the unload handler, and set bindings on the pending
+ // RenderViewHost to simulate a privileged url.
controller.LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
- orig_rvh->SendShouldCloseACK(true);
- contents()->GetPendingRenderViewHost()->AllowBindings(1);
- static_cast<TestRenderViewHost*>(
- contents()->GetPendingRenderViewHost())->SendNavigate(1, url2);
+ orig_rvh->SendBeforeUnloadACK(true);
+ contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
+ contents()->GetRenderManagerForTesting()->pending_frame_host(),
+ GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
+ url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
+ TestRenderViewHost* new_rvh =
+ static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
+ new_rvh->AllowBindings(1);
+ new_rvh->SendNavigate(1, url2);
// The second load should be committed, and bindings should be remembered.
EXPECT_EQ(controller.GetEntryCount(), 2);
@@ -1106,6 +1122,11 @@ TEST_F(NavigationControllerTest, LoadURL_WithBindings) {
// Going back, the first entry should still appear unprivileged.
controller.GoBack();
+ new_rvh->SendBeforeUnloadACK(true);
+ contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
+ contents()->GetRenderManagerForTesting()->pending_frame_host(),
+ GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
+ url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
orig_rvh->SendNavigate(0, url1);
EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
EXPECT_EQ(0, NavigationEntryImpl::FromNavigationEntry(
@@ -1121,11 +1142,11 @@ TEST_F(NavigationControllerTest, Reload) {
controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
EXPECT_EQ(0U, notifications.size());
- test_rvh()->SendNavigate(0, url1);
+ main_test_rfh()->SendNavigate(0, url1);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
ASSERT_TRUE(controller.GetVisibleEntry());
- controller.GetVisibleEntry()->SetTitle(ASCIIToUTF16("Title"));
+ controller.GetVisibleEntry()->SetTitle(base::ASCIIToUTF16("Title"));
controller.Reload(true);
EXPECT_EQ(0U, notifications.size());
@@ -1145,7 +1166,7 @@ TEST_F(NavigationControllerTest, Reload) {
// See http://crbug.com/96041.
EXPECT_TRUE(controller.GetVisibleEntry()->GetTitle().empty());
- test_rvh()->SendNavigate(0, url1);
+ main_test_rfh()->SendNavigate(0, url1);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
@@ -1173,14 +1194,14 @@ TEST_F(NavigationControllerTest, Reload_GeneratesNewPage) {
const GURL url2("http://foo2");
controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
- test_rvh()->SendNavigate(0, url1);
+ main_test_rfh()->SendNavigate(0, url1);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
controller.Reload(true);
EXPECT_EQ(0U, notifications.size());
- test_rvh()->SendNavigate(1, url2);
+ main_test_rfh()->SendNavigate(1, url2);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
@@ -1202,14 +1223,14 @@ TEST_F(NavigationControllerTest, ReloadWithGuest) {
const GURL url1("http://foo1");
controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
- test_rvh()->SendNavigate(0, url1);
+ main_test_rfh()->SendNavigate(0, url1);
ASSERT_TRUE(controller.GetVisibleEntry());
// Make the entry believe its RenderProcessHost is a guest.
NavigationEntryImpl* entry1 =
NavigationEntryImpl::FromNavigationEntry(controller.GetVisibleEntry());
reinterpret_cast<MockRenderProcessHost*>(
- entry1->site_instance()->GetProcess())->SetIsGuest(true);
+ entry1->site_instance()->GetProcess())->set_is_isolated_guest(true);
// And reload.
controller.Reload(true);
@@ -1238,7 +1259,8 @@ TEST_F(NavigationControllerTest, ReloadOriginalRequestURL) {
controller.LoadURL(
original_url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
EXPECT_EQ(0U, notifications.size());
- test_rvh()->SendNavigateWithOriginalRequestURL(0, final_url, original_url);
+ main_test_rfh()->SendNavigateWithOriginalRequestURL(
+ 0, final_url, original_url);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
@@ -1249,7 +1271,7 @@ TEST_F(NavigationControllerTest, ReloadOriginalRequestURL) {
EXPECT_EQ(final_url, controller.GetVisibleEntry()->GetURL());
// Reload using the original URL.
- controller.GetVisibleEntry()->SetTitle(ASCIIToUTF16("Title"));
+ controller.GetVisibleEntry()->SetTitle(base::ASCIIToUTF16("Title"));
controller.ReloadOriginalRequestURL(false);
EXPECT_EQ(0U, notifications.size());
@@ -1269,7 +1291,7 @@ TEST_F(NavigationControllerTest, ReloadOriginalRequestURL) {
EXPECT_TRUE(controller.GetVisibleEntry()->GetTitle().empty());
// Send that the navigation has proceeded; say it got redirected again.
- test_rvh()->SendNavigate(0, final_url);
+ main_test_rfh()->SendNavigate(0, final_url);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
@@ -1310,16 +1332,14 @@ TEST_F(NavigationControllerTest, ResetEntryValuesAfterCommit) {
pending_entry->set_is_renderer_initiated(true);
pending_entry->set_transferred_global_request_id(transfer_id);
pending_entry->set_should_replace_entry(true);
- pending_entry->set_redirect_chain(redirects);
pending_entry->set_should_clear_history_list(true);
EXPECT_EQ(post_data.get(), pending_entry->GetBrowserInitiatedPostData());
EXPECT_TRUE(pending_entry->is_renderer_initiated());
EXPECT_EQ(transfer_id, pending_entry->transferred_global_request_id());
EXPECT_TRUE(pending_entry->should_replace_entry());
- EXPECT_EQ(1U, pending_entry->redirect_chain().size());
EXPECT_TRUE(pending_entry->should_clear_history_list());
- test_rvh()->SendNavigate(0, url1);
+ main_test_rfh()->SendNavigate(0, url1);
// Certain values that are only used for pending entries get reset after
// commit.
@@ -1331,10 +1351,35 @@ TEST_F(NavigationControllerTest, ResetEntryValuesAfterCommit) {
EXPECT_EQ(GlobalRequestID(-1, -1),
committed_entry->transferred_global_request_id());
EXPECT_FALSE(committed_entry->should_replace_entry());
- EXPECT_EQ(0U, committed_entry->redirect_chain().size());
EXPECT_FALSE(committed_entry->should_clear_history_list());
}
+// Test that Redirects are preserved after a commit.
+TEST_F(NavigationControllerTest, RedirectsAreNotResetByCommit) {
+ NavigationControllerImpl& controller = controller_impl();
+ const GURL url1("http://foo1");
+ controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
+
+ // Set up some redirect values.
+ std::vector<GURL> redirects;
+ redirects.push_back(GURL("http://foo2"));
+
+ // Set redirects on the pending entry.
+ NavigationEntryImpl* pending_entry =
+ NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry());
+ pending_entry->SetRedirectChain(redirects);
+ EXPECT_EQ(1U, pending_entry->GetRedirectChain().size());
+ EXPECT_EQ(GURL("http://foo2"), pending_entry->GetRedirectChain()[0]);
+
+ // Normal navigation will preserve redirects in the committed entry.
+ main_test_rfh()->SendNavigateWithRedirects(0, url1, redirects);
+ NavigationEntryImpl* committed_entry =
+ NavigationEntryImpl::FromNavigationEntry(
+ controller.GetLastCommittedEntry());
+ ASSERT_EQ(1U, committed_entry->GetRedirectChain().size());
+ EXPECT_EQ(GURL("http://foo2"), committed_entry->GetRedirectChain()[0]);
+}
+
// Tests what happens when we navigate back successfully
TEST_F(NavigationControllerTest, Back) {
NavigationControllerImpl& controller = controller_impl();
@@ -1342,12 +1387,12 @@ TEST_F(NavigationControllerTest, Back) {
RegisterForAllNavNotifications(&notifications, &controller);
const GURL url1("http://foo1");
- test_rvh()->SendNavigate(0, url1);
+ main_test_rfh()->SendNavigate(0, url1);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
const GURL url2("http://foo2");
- test_rvh()->SendNavigate(1, url2);
+ main_test_rfh()->SendNavigate(1, url2);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
@@ -1371,7 +1416,7 @@ TEST_F(NavigationControllerTest, Back) {
EXPECT_GE(controller.GetEntryAtIndex(1)->GetTimestamp(),
controller.GetEntryAtIndex(0)->GetTimestamp());
- test_rvh()->SendNavigate(0, url2);
+ main_test_rfh()->SendNavigate(0, url2);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
@@ -1405,12 +1450,12 @@ TEST_F(NavigationControllerTest, Back_GeneratesNewPage) {
controller.LoadURL(
url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
- test_rvh()->SendNavigate(0, url1);
+ main_test_rfh()->SendNavigate(0, url1);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
controller.LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
- test_rvh()->SendNavigate(1, url2);
+ main_test_rfh()->SendNavigate(1, url2);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
@@ -1426,7 +1471,7 @@ TEST_F(NavigationControllerTest, Back_GeneratesNewPage) {
EXPECT_FALSE(controller.CanGoBack());
EXPECT_TRUE(controller.CanGoForward());
- test_rvh()->SendNavigate(2, url3);
+ main_test_rfh()->SendNavigate(2, url3);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
@@ -1452,12 +1497,12 @@ TEST_F(NavigationControllerTest, Back_NewPending) {
const GURL kUrl3("http://foo3");
// First navigate two places so we have some back history.
- test_rvh()->SendNavigate(0, kUrl1);
+ main_test_rfh()->SendNavigate(0, kUrl1);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
// controller.LoadURL(kUrl2, PAGE_TRANSITION_TYPED);
- test_rvh()->SendNavigate(1, kUrl2);
+ main_test_rfh()->SendNavigate(1, kUrl2);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
@@ -1482,12 +1527,12 @@ TEST_F(NavigationControllerTest, Back_OtherBackPending) {
const GURL kUrl3("http://foo/3");
// First navigate three places so we have some back history.
- test_rvh()->SendNavigate(0, kUrl1);
- test_rvh()->SendNavigate(1, kUrl2);
- test_rvh()->SendNavigate(2, kUrl3);
+ main_test_rfh()->SendNavigate(0, kUrl1);
+ main_test_rfh()->SendNavigate(1, kUrl2);
+ main_test_rfh()->SendNavigate(2, kUrl3);
// With nothing pending, say we get a navigation to the second entry.
- test_rvh()->SendNavigate(1, kUrl2);
+ main_test_rfh()->SendNavigate(1, kUrl2);
// We know all the entries have the same site instance, so we can just grab
// a random one for looking up other entries.
@@ -1503,7 +1548,7 @@ TEST_F(NavigationControllerTest, Back_OtherBackPending) {
// Now go forward to the last item again and say it was committed.
controller.GoForward();
- test_rvh()->SendNavigate(2, kUrl3);
+ main_test_rfh()->SendNavigate(2, kUrl3);
// Now start going back one to the second page. It will be pending.
controller.GoBack();
@@ -1512,7 +1557,7 @@ TEST_F(NavigationControllerTest, Back_OtherBackPending) {
// Not synthesize a totally new back event to the first page. This will not
// match the pending one.
- test_rvh()->SendNavigate(0, kUrl1);
+ main_test_rfh()->SendNavigate(0, kUrl1);
// The committed navigation should clear the pending entry.
EXPECT_EQ(-1, controller.GetPendingEntryIndex());
@@ -1531,16 +1576,16 @@ TEST_F(NavigationControllerTest, Forward) {
const GURL url1("http://foo1");
const GURL url2("http://foo2");
- test_rvh()->SendNavigate(0, url1);
+ main_test_rfh()->SendNavigate(0, url1);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
- test_rvh()->SendNavigate(1, url2);
+ main_test_rfh()->SendNavigate(1, url2);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
controller.GoBack();
- test_rvh()->SendNavigate(0, url1);
+ main_test_rfh()->SendNavigate(0, url1);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
@@ -1564,7 +1609,7 @@ TEST_F(NavigationControllerTest, Forward) {
EXPECT_GE(controller.GetEntryAtIndex(0)->GetTimestamp(),
controller.GetEntryAtIndex(1)->GetTimestamp());
- test_rvh()->SendNavigate(1, url2);
+ main_test_rfh()->SendNavigate(1, url2);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
@@ -1596,15 +1641,15 @@ TEST_F(NavigationControllerTest, Forward_GeneratesNewPage) {
const GURL url2("http://foo2");
const GURL url3("http://foo3");
- test_rvh()->SendNavigate(0, url1);
+ main_test_rfh()->SendNavigate(0, url1);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
- test_rvh()->SendNavigate(1, url2);
+ main_test_rfh()->SendNavigate(1, url2);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
controller.GoBack();
- test_rvh()->SendNavigate(0, url1);
+ main_test_rfh()->SendNavigate(0, url1);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
@@ -1620,7 +1665,7 @@ TEST_F(NavigationControllerTest, Forward_GeneratesNewPage) {
EXPECT_TRUE(controller.CanGoBack());
EXPECT_FALSE(controller.CanGoForward());
- test_rvh()->SendNavigate(2, url3);
+ main_test_rfh()->SendNavigate(2, url3);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
EXPECT_TRUE(notifications.Check1AndReset(NOTIFICATION_NAV_LIST_PRUNED));
@@ -1648,7 +1693,7 @@ TEST_F(NavigationControllerTest, Redirect) {
controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
EXPECT_EQ(0U, notifications.size());
- test_rvh()->SendNavigate(0, url2);
+ main_test_rfh()->SendNavigate(0, url2);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
@@ -1659,7 +1704,7 @@ TEST_F(NavigationControllerTest, Redirect) {
EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
- ViewHostMsg_FrameNavigate_Params params;
+ FrameHostMsg_DidCommitProvisionalLoad_Params params;
params.page_id = 0;
params.url = url2;
params.transition = PAGE_TRANSITION_SERVER_REDIRECT;
@@ -1673,7 +1718,8 @@ TEST_F(NavigationControllerTest, Redirect) {
LoadCommittedDetails details;
EXPECT_EQ(0U, notifications.size());
- EXPECT_TRUE(controller.RendererDidNavigate(params, &details));
+ EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
+ &details));
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
@@ -1705,7 +1751,7 @@ TEST_F(NavigationControllerTest, PostThenRedirect) {
controller.GetVisibleEntry()->SetHasPostData(true);
EXPECT_EQ(0U, notifications.size());
- test_rvh()->SendNavigate(0, url2);
+ main_test_rfh()->SendNavigate(0, url2);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
@@ -1716,7 +1762,7 @@ TEST_F(NavigationControllerTest, PostThenRedirect) {
EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
- ViewHostMsg_FrameNavigate_Params params;
+ FrameHostMsg_DidCommitProvisionalLoad_Params params;
params.page_id = 0;
params.url = url2;
params.transition = PAGE_TRANSITION_SERVER_REDIRECT;
@@ -1730,7 +1776,8 @@ TEST_F(NavigationControllerTest, PostThenRedirect) {
LoadCommittedDetails details;
EXPECT_EQ(0U, notifications.size());
- EXPECT_TRUE(controller.RendererDidNavigate(params, &details));
+ EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
+ &details));
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
@@ -1763,7 +1810,7 @@ TEST_F(NavigationControllerTest, ImmediateRedirect) {
EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
- ViewHostMsg_FrameNavigate_Params params;
+ FrameHostMsg_DidCommitProvisionalLoad_Params params;
params.page_id = 0;
params.url = url2;
params.transition = PAGE_TRANSITION_SERVER_REDIRECT;
@@ -1777,7 +1824,8 @@ TEST_F(NavigationControllerTest, ImmediateRedirect) {
LoadCommittedDetails details;
EXPECT_EQ(0U, notifications.size());
- EXPECT_TRUE(controller.RendererDidNavigate(params, &details));
+ EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
+ &details));
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
@@ -1801,12 +1849,12 @@ TEST_F(NavigationControllerTest, NewSubframe) {
RegisterForAllNavNotifications(&notifications, &controller);
const GURL url1("http://foo1");
- test_rvh()->SendNavigate(0, url1);
+ main_test_rfh()->SendNavigate(0, url1);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
const GURL url2("http://foo2");
- ViewHostMsg_FrameNavigate_Params params;
+ FrameHostMsg_DidCommitProvisionalLoad_Params params;
params.page_id = 1;
params.url = url2;
params.transition = PAGE_TRANSITION_MANUAL_SUBFRAME;
@@ -1816,7 +1864,8 @@ TEST_F(NavigationControllerTest, NewSubframe) {
params.page_state = PageState::CreateFromURL(url2);
LoadCommittedDetails details;
- EXPECT_TRUE(controller.RendererDidNavigate(params, &details));
+ EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
+ &details));
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
EXPECT_EQ(url1, details.previous_url);
@@ -1842,7 +1891,7 @@ TEST_F(NavigationControllerTest, SubframeOnEmptyPage) {
// Navigation controller currently has no entries.
const GURL url("http://foo2");
- ViewHostMsg_FrameNavigate_Params params;
+ FrameHostMsg_DidCommitProvisionalLoad_Params params;
params.page_id = 1;
params.url = url;
params.transition = PAGE_TRANSITION_AUTO_SUBFRAME;
@@ -1852,7 +1901,8 @@ TEST_F(NavigationControllerTest, SubframeOnEmptyPage) {
params.page_state = PageState::CreateFromURL(url);
LoadCommittedDetails details;
- EXPECT_FALSE(controller.RendererDidNavigate(params, &details));
+ EXPECT_FALSE(controller.RendererDidNavigate(main_test_rfh(), params,
+ &details));
EXPECT_EQ(0U, notifications.size());
}
@@ -1864,12 +1914,12 @@ TEST_F(NavigationControllerTest, AutoSubframe) {
RegisterForAllNavNotifications(&notifications, &controller);
const GURL url1("http://foo1");
- test_rvh()->SendNavigate(0, url1);
+ main_test_rfh()->SendNavigate(0, url1);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
const GURL url2("http://foo2");
- ViewHostMsg_FrameNavigate_Params params;
+ FrameHostMsg_DidCommitProvisionalLoad_Params params;
params.page_id = 0;
params.url = url2;
params.transition = PAGE_TRANSITION_AUTO_SUBFRAME;
@@ -1880,7 +1930,8 @@ TEST_F(NavigationControllerTest, AutoSubframe) {
// Navigating should do nothing.
LoadCommittedDetails details;
- EXPECT_FALSE(controller.RendererDidNavigate(params, &details));
+ EXPECT_FALSE(controller.RendererDidNavigate(main_test_rfh(), params,
+ &details));
EXPECT_EQ(0U, notifications.size());
// There should still be only one entry.
@@ -1895,13 +1946,13 @@ TEST_F(NavigationControllerTest, BackSubframe) {
// Main page.
const GURL url1("http://foo1");
- test_rvh()->SendNavigate(0, url1);
+ main_test_rfh()->SendNavigate(0, url1);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
// First manual subframe navigation.
const GURL url2("http://foo2");
- ViewHostMsg_FrameNavigate_Params params;
+ FrameHostMsg_DidCommitProvisionalLoad_Params params;
params.page_id = 1;
params.url = url2;
params.transition = PAGE_TRANSITION_MANUAL_SUBFRAME;
@@ -1912,7 +1963,8 @@ TEST_F(NavigationControllerTest, BackSubframe) {
// This should generate a new entry.
LoadCommittedDetails details;
- EXPECT_TRUE(controller.RendererDidNavigate(params, &details));
+ EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
+ &details));
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
EXPECT_EQ(2, controller.GetEntryCount());
@@ -1921,7 +1973,8 @@ TEST_F(NavigationControllerTest, BackSubframe) {
const GURL url3("http://foo3");
params.page_id = 2;
params.url = url3;
- EXPECT_TRUE(controller.RendererDidNavigate(params, &details));
+ EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
+ &details));
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
EXPECT_EQ(3, controller.GetEntryCount());
@@ -1931,7 +1984,8 @@ TEST_F(NavigationControllerTest, BackSubframe) {
controller.GoBack();
params.url = url2;
params.page_id = 1;
- EXPECT_TRUE(controller.RendererDidNavigate(params, &details));
+ EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
+ &details));
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
EXPECT_EQ(3, controller.GetEntryCount());
@@ -1943,7 +1997,8 @@ TEST_F(NavigationControllerTest, BackSubframe) {
controller.GoBack();
params.url = url1;
params.page_id = 0;
- EXPECT_TRUE(controller.RendererDidNavigate(params, &details));
+ EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
+ &details));
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
EXPECT_EQ(3, controller.GetEntryCount());
@@ -1960,11 +2015,11 @@ TEST_F(NavigationControllerTest, LinkClick) {
const GURL url1("http://foo1");
const GURL url2("http://foo2");
- test_rvh()->SendNavigate(0, url1);
+ main_test_rfh()->SendNavigate(0, url1);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
- test_rvh()->SendNavigate(1, url2);
+ main_test_rfh()->SendNavigate(1, url2);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
@@ -1985,13 +2040,13 @@ TEST_F(NavigationControllerTest, InPage) {
// Main page.
const GURL url1("http://foo");
- test_rvh()->SendNavigate(0, url1);
+ main_test_rfh()->SendNavigate(0, url1);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
// Ensure main page navigation to same url respects the was_within_same_page
// hint provided in the params.
- ViewHostMsg_FrameNavigate_Params self_params;
+ FrameHostMsg_DidCommitProvisionalLoad_Params self_params;
self_params.page_id = 0;
self_params.url = url1;
self_params.transition = PAGE_TRANSITION_LINK;
@@ -2002,7 +2057,8 @@ TEST_F(NavigationControllerTest, InPage) {
self_params.was_within_same_page = true;
LoadCommittedDetails details;
- EXPECT_TRUE(controller.RendererDidNavigate(self_params, &details));
+ EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), self_params,
+ &details));
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
EXPECT_TRUE(details.is_in_page);
@@ -2011,7 +2067,7 @@ TEST_F(NavigationControllerTest, InPage) {
// Fragment navigation to a new page_id.
const GURL url2("http://foo#a");
- ViewHostMsg_FrameNavigate_Params params;
+ FrameHostMsg_DidCommitProvisionalLoad_Params params;
params.page_id = 1;
params.url = url2;
params.transition = PAGE_TRANSITION_LINK;
@@ -2022,7 +2078,8 @@ TEST_F(NavigationControllerTest, InPage) {
params.was_within_same_page = true;
// This should generate a new entry.
- EXPECT_TRUE(controller.RendererDidNavigate(params, &details));
+ EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
+ &details));
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
EXPECT_TRUE(details.is_in_page);
@@ -2030,11 +2087,12 @@ TEST_F(NavigationControllerTest, InPage) {
EXPECT_EQ(2, controller.GetEntryCount());
// Go back one.
- ViewHostMsg_FrameNavigate_Params back_params(params);
+ FrameHostMsg_DidCommitProvisionalLoad_Params back_params(params);
controller.GoBack();
back_params.url = url1;
back_params.page_id = 0;
- EXPECT_TRUE(controller.RendererDidNavigate(back_params, &details));
+ EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), back_params,
+ &details));
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
EXPECT_TRUE(details.is_in_page);
@@ -2043,11 +2101,12 @@ TEST_F(NavigationControllerTest, InPage) {
EXPECT_EQ(back_params.url, controller.GetVisibleEntry()->GetURL());
// Go forward
- ViewHostMsg_FrameNavigate_Params forward_params(params);
+ FrameHostMsg_DidCommitProvisionalLoad_Params forward_params(params);
controller.GoForward();
forward_params.url = url2;
forward_params.page_id = 1;
- EXPECT_TRUE(controller.RendererDidNavigate(forward_params, &details));
+ EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), forward_params,
+ &details));
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
EXPECT_TRUE(details.is_in_page);
@@ -2061,9 +2120,11 @@ TEST_F(NavigationControllerTest, InPage) {
// one identified by an existing page ID. This would result in the second URL
// losing the reference fragment when you navigate away from it and then back.
controller.GoBack();
- EXPECT_TRUE(controller.RendererDidNavigate(back_params, &details));
+ EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), back_params,
+ &details));
controller.GoForward();
- EXPECT_TRUE(controller.RendererDidNavigate(forward_params, &details));
+ EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), forward_params,
+ &details));
EXPECT_EQ(forward_params.url,
controller.GetVisibleEntry()->GetURL());
@@ -2072,7 +2133,8 @@ TEST_F(NavigationControllerTest, InPage) {
params.page_id = 2;
params.url = url3;
navigation_entry_committed_counter_ = 0;
- EXPECT_TRUE(controller.RendererDidNavigate(params, &details));
+ EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
+ &details));
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
EXPECT_FALSE(details.is_in_page);
@@ -2087,13 +2149,13 @@ TEST_F(NavigationControllerTest, InPage_Replace) {
// Main page.
const GURL url1("http://foo");
- test_rvh()->SendNavigate(0, url1);
+ main_test_rfh()->SendNavigate(0, url1);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
// First navigation.
const GURL url2("http://foo#a");
- ViewHostMsg_FrameNavigate_Params params;
+ FrameHostMsg_DidCommitProvisionalLoad_Params params;
params.page_id = 0; // Same page_id
params.url = url2;
params.transition = PAGE_TRANSITION_LINK;
@@ -2101,10 +2163,12 @@ TEST_F(NavigationControllerTest, InPage_Replace) {
params.gesture = NavigationGestureUser;
params.is_post = false;
params.page_state = PageState::CreateFromURL(url2);
+ params.was_within_same_page = true;
// This should NOT generate a new entry, nor prune the list.
LoadCommittedDetails details;
- EXPECT_TRUE(controller.RendererDidNavigate(params, &details));
+ EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
+ &details));
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
EXPECT_TRUE(details.is_in_page);
@@ -2126,7 +2190,7 @@ TEST_F(NavigationControllerTest, ClientRedirectAfterInPageNavigation) {
// Load an initial page.
{
const GURL url("http://foo/");
- test_rvh()->SendNavigate(0, url);
+ main_test_rfh()->SendNavigate(0, url);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
}
@@ -2134,7 +2198,7 @@ TEST_F(NavigationControllerTest, ClientRedirectAfterInPageNavigation) {
// Navigate to a new page.
{
const GURL url("http://foo2/");
- test_rvh()->SendNavigate(1, url);
+ main_test_rfh()->SendNavigate(1, url);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
}
@@ -2142,7 +2206,7 @@ TEST_F(NavigationControllerTest, ClientRedirectAfterInPageNavigation) {
// Navigate within the page.
{
const GURL url("http://foo2/#a");
- ViewHostMsg_FrameNavigate_Params params;
+ FrameHostMsg_DidCommitProvisionalLoad_Params params;
params.page_id = 1; // Same page_id
params.url = url;
params.transition = PAGE_TRANSITION_LINK;
@@ -2151,10 +2215,12 @@ TEST_F(NavigationControllerTest, ClientRedirectAfterInPageNavigation) {
params.gesture = NavigationGestureUnknown;
params.is_post = false;
params.page_state = PageState::CreateFromURL(url);
+ params.was_within_same_page = true;
// This should NOT generate a new entry, nor prune the list.
LoadCommittedDetails details;
- EXPECT_TRUE(controller.RendererDidNavigate(params, &details));
+ EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
+ &details));
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
EXPECT_TRUE(details.is_in_page);
@@ -2165,7 +2231,7 @@ TEST_F(NavigationControllerTest, ClientRedirectAfterInPageNavigation) {
// Perform a client redirect to a new page.
{
const GURL url("http://foo3/");
- ViewHostMsg_FrameNavigate_Params params;
+ FrameHostMsg_DidCommitProvisionalLoad_Params params;
params.page_id = 2; // New page_id
params.url = url;
params.transition = PAGE_TRANSITION_CLIENT_REDIRECT;
@@ -2178,7 +2244,8 @@ TEST_F(NavigationControllerTest, ClientRedirectAfterInPageNavigation) {
// This SHOULD generate a new entry.
LoadCommittedDetails details;
- EXPECT_TRUE(controller.RendererDidNavigate(params, &details));
+ EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
+ &details));
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
EXPECT_FALSE(details.is_in_page);
@@ -2189,13 +2256,26 @@ TEST_F(NavigationControllerTest, ClientRedirectAfterInPageNavigation) {
{
const GURL url("http://foo2/");
controller.GoBack();
- test_rvh()->SendNavigate(1, url);
+ main_test_rfh()->SendNavigate(1, url);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
}
}
+TEST_F(NavigationControllerTest, PushStateWithoutPreviousEntry)
+{
+ ASSERT_FALSE(controller_impl().GetLastCommittedEntry());
+ FrameHostMsg_DidCommitProvisionalLoad_Params params;
+ GURL url("http://foo");
+ params.page_id = 1;
+ params.url = url;
+ params.page_state = PageState::CreateFromURL(url);
+ params.was_within_same_page = true;
+ test_rvh()->SendNavigateWithParams(&params);
+ // We pass if we don't crash.
+}
+
// NotificationObserver implementation used in verifying we've received the
// NOTIFICATION_NAV_LIST_PRUNED method.
class PrunedListener : public NotificationObserver {
@@ -2241,7 +2321,7 @@ TEST_F(NavigationControllerTest, EnforceMaxNavigationCount) {
GURL url(base::StringPrintf("http://www.a.com/%d", url_index));
controller.LoadURL(
url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
- test_rvh()->SendNavigate(url_index, url);
+ main_test_rfh()->SendNavigate(url_index, url);
}
EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount);
@@ -2253,7 +2333,7 @@ TEST_F(NavigationControllerTest, EnforceMaxNavigationCount) {
GURL url(base::StringPrintf("http://www.a.com/%d", url_index));
controller.LoadURL(
url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
- test_rvh()->SendNavigate(url_index, url);
+ main_test_rfh()->SendNavigate(url_index, url);
url_index++;
// We should have got a pruned navigation.
@@ -2271,7 +2351,7 @@ TEST_F(NavigationControllerTest, EnforceMaxNavigationCount) {
url = GURL(base::StringPrintf("http:////www.a.com/%d", url_index));
controller.LoadURL(
url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
- test_rvh()->SendNavigate(url_index, url);
+ main_test_rfh()->SendNavigate(url_index, url);
url_index++;
}
EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount);
@@ -2292,7 +2372,7 @@ TEST_F(NavigationControllerTest, RestoreNavigate) {
url, Referrer(), PAGE_TRANSITION_RELOAD, false, std::string(),
browser_context());
entry->SetPageID(0);
- entry->SetTitle(ASCIIToUTF16("Title"));
+ entry->SetTitle(base::ASCIIToUTF16("Title"));
entry->SetPageState(PageState::CreateFromEncodedData("state"));
const base::Time timestamp = base::Time::Now();
entry->SetTimestamp(timestamp);
@@ -2332,7 +2412,7 @@ TEST_F(NavigationControllerTest, RestoreNavigate) {
EXPECT_EQ(timestamp, our_controller.GetEntryAtIndex(0)->GetTimestamp());
// Say we navigated to that entry.
- ViewHostMsg_FrameNavigate_Params params;
+ FrameHostMsg_DidCommitProvisionalLoad_Params params;
params.page_id = 0;
params.url = url;
params.transition = PAGE_TRANSITION_LINK;
@@ -2341,7 +2421,8 @@ TEST_F(NavigationControllerTest, RestoreNavigate) {
params.is_post = false;
params.page_state = PageState::CreateFromURL(url);
LoadCommittedDetails details;
- our_controller.RendererDidNavigate(params, &details);
+ our_controller.RendererDidNavigate(our_contents->GetMainFrame(), params,
+ &details);
// There should be no longer any pending entry and one committed one. This
// means that we were able to locate the entry, assign its site instance, and
@@ -2371,7 +2452,7 @@ TEST_F(NavigationControllerTest, RestoreNavigateAfterFailure) {
url, Referrer(), PAGE_TRANSITION_RELOAD, false, std::string(),
browser_context());
entry->SetPageID(0);
- entry->SetTitle(ASCIIToUTF16("Title"));
+ entry->SetTitle(base::ASCIIToUTF16("Title"));
entry->SetPageState(PageState::CreateFromEncodedData("state"));
entries.push_back(entry);
scoped_ptr<WebContentsImpl> our_contents(static_cast<WebContentsImpl*>(
@@ -2404,21 +2485,17 @@ TEST_F(NavigationControllerTest, RestoreNavigateAfterFailure) {
// This pending navigation may have caused a different navigation to fail,
// which causes the pending entry to be cleared.
- TestRenderViewHost* rvh =
- static_cast<TestRenderViewHost*>(our_contents->GetRenderViewHost());
- ViewHostMsg_DidFailProvisionalLoadWithError_Params fail_load_params;
- fail_load_params.frame_id = 1;
- fail_load_params.is_main_frame = true;
+ FrameHostMsg_DidFailProvisionalLoadWithError_Params fail_load_params;
fail_load_params.error_code = net::ERR_ABORTED;
fail_load_params.error_description = base::string16();
fail_load_params.url = url;
fail_load_params.showing_repost_interstitial = false;
- rvh->OnMessageReceived(
- ViewHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
+ main_test_rfh()->OnMessageReceived(
+ FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
fail_load_params));
// Now the pending restored entry commits.
- ViewHostMsg_FrameNavigate_Params params;
+ FrameHostMsg_DidCommitProvisionalLoad_Params params;
params.page_id = 0;
params.url = url;
params.transition = PAGE_TRANSITION_LINK;
@@ -2427,7 +2504,8 @@ TEST_F(NavigationControllerTest, RestoreNavigateAfterFailure) {
params.is_post = false;
params.page_state = PageState::CreateFromURL(url);
LoadCommittedDetails details;
- our_controller.RendererDidNavigate(params, &details);
+ our_controller.RendererDidNavigate(our_contents->GetMainFrame(), params,
+ &details);
// There should be no pending entry and one committed one.
EXPECT_EQ(1, our_controller.GetEntryCount());
@@ -2449,7 +2527,7 @@ TEST_F(NavigationControllerTest, Interstitial) {
const GURL url1("http://foo");
controller.LoadURL(
url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
- test_rvh()->SendNavigate(0, url1);
+ main_test_rfh()->SendNavigate(0, url1);
// Now navigate somewhere with an interstitial.
const GURL url2("http://bar");
@@ -2460,7 +2538,7 @@ TEST_F(NavigationControllerTest, Interstitial) {
// At this point the interstitial will be displayed and the load will still
// be pending. If the user continues, the load will commit.
- test_rvh()->SendNavigate(1, url2);
+ main_test_rfh()->SendNavigate(1, url2);
// The page should be a normal page again.
EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetURL());
@@ -2480,19 +2558,19 @@ TEST_F(NavigationControllerTest, RemoveEntry) {
controller.LoadURL(
url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
- test_rvh()->SendNavigate(0, url1);
+ main_test_rfh()->SendNavigate(0, url1);
controller.LoadURL(
url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
- test_rvh()->SendNavigate(1, url2);
+ main_test_rfh()->SendNavigate(1, url2);
controller.LoadURL(
url3, Referrer(), PAGE_TRANSITION_TYPED, std::string());
- test_rvh()->SendNavigate(2, url3);
+ main_test_rfh()->SendNavigate(2, url3);
controller.LoadURL(
url4, Referrer(), PAGE_TRANSITION_TYPED, std::string());
- test_rvh()->SendNavigate(3, url4);
+ main_test_rfh()->SendNavigate(3, url4);
controller.LoadURL(
url5, Referrer(), PAGE_TRANSITION_TYPED, std::string());
- test_rvh()->SendNavigate(4, url5);
+ main_test_rfh()->SendNavigate(4, url5);
// Try to remove the last entry. Will fail because it is the current entry.
EXPECT_FALSE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 1));
@@ -2506,7 +2584,7 @@ TEST_F(NavigationControllerTest, RemoveEntry) {
EXPECT_FALSE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 2));
// Now commit and delete the last entry.
- test_rvh()->SendNavigate(3, url4);
+ main_test_rfh()->SendNavigate(3, url4);
EXPECT_TRUE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 1));
EXPECT_EQ(4, controller.GetEntryCount());
EXPECT_EQ(3, controller.GetLastCommittedEntryIndex());
@@ -2543,10 +2621,10 @@ TEST_F(NavigationControllerTest, TransientEntry) {
controller.LoadURL(
url0, Referrer(), PAGE_TRANSITION_TYPED, std::string());
- test_rvh()->SendNavigate(0, url0);
+ main_test_rfh()->SendNavigate(0, url0);
controller.LoadURL(
url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
- test_rvh()->SendNavigate(1, url1);
+ main_test_rfh()->SendNavigate(1, url1);
notifications.Reset();
@@ -2572,7 +2650,7 @@ TEST_F(NavigationControllerTest, TransientEntry) {
// Navigate.
controller.LoadURL(
url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
- test_rvh()->SendNavigate(2, url2);
+ main_test_rfh()->SendNavigate(2, url2);
// We should have navigated, transient entry should be gone.
EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
@@ -2583,7 +2661,7 @@ TEST_F(NavigationControllerTest, TransientEntry) {
transient_entry->SetURL(transient_url);
controller.SetTransientEntry(transient_entry);
EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
- test_rvh()->SendNavigate(3, url3);
+ main_test_rfh()->SendNavigate(3, url3);
// Transient entry should be gone.
EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL());
EXPECT_EQ(controller.GetEntryCount(), 4);
@@ -2595,7 +2673,7 @@ TEST_F(NavigationControllerTest, TransientEntry) {
transient_entry->SetURL(transient_url);
controller.SetTransientEntry(transient_entry);
EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
- test_rvh()->SendNavigate(4, url4);
+ main_test_rfh()->SendNavigate(4, url4);
EXPECT_EQ(url4, controller.GetVisibleEntry()->GetURL());
EXPECT_EQ(controller.GetEntryCount(), 5);
@@ -2610,7 +2688,7 @@ TEST_F(NavigationControllerTest, TransientEntry) {
// Transient entry should be gone.
EXPECT_EQ(url4, controller.GetVisibleEntry()->GetURL());
EXPECT_EQ(controller.GetEntryCount(), 5);
- test_rvh()->SendNavigate(3, url3);
+ main_test_rfh()->SendNavigate(3, url3);
// Add a transient and go to an entry before the current one.
transient_entry = new NavigationEntryImpl;
@@ -2623,7 +2701,7 @@ TEST_F(NavigationControllerTest, TransientEntry) {
EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
// Visible entry does not update for history navigations until commit.
EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL());
- test_rvh()->SendNavigate(1, url1);
+ main_test_rfh()->SendNavigate(1, url1);
EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
// Add a transient and go to an entry after the current one.
@@ -2637,7 +2715,7 @@ TEST_F(NavigationControllerTest, TransientEntry) {
// land on url2 (which is visible after the commit).
EXPECT_EQ(url2, controller.GetPendingEntry()->GetURL());
EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
- test_rvh()->SendNavigate(2, url2);
+ main_test_rfh()->SendNavigate(2, url2);
EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
// Add a transient and go forward.
@@ -2651,7 +2729,7 @@ TEST_F(NavigationControllerTest, TransientEntry) {
EXPECT_FALSE(controller.GetTransientEntry());
EXPECT_EQ(url3, controller.GetPendingEntry()->GetURL());
EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
- test_rvh()->SendNavigate(3, url3);
+ main_test_rfh()->SendNavigate(3, url3);
EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL());
// Add a transient and do an in-page navigation, replacing the current entry.
@@ -2659,7 +2737,7 @@ TEST_F(NavigationControllerTest, TransientEntry) {
transient_entry->SetURL(transient_url);
controller.SetTransientEntry(transient_entry);
EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
- test_rvh()->SendNavigate(3, url3_ref);
+ main_test_rfh()->SendNavigate(3, url3_ref);
// Transient entry should be gone.
EXPECT_FALSE(controller.GetTransientEntry());
EXPECT_EQ(url3_ref, controller.GetVisibleEntry()->GetURL());
@@ -2683,7 +2761,7 @@ TEST_F(NavigationControllerTest, ReloadTransient) {
// Load |url0|, and start a pending navigation to |url1|.
controller.LoadURL(
url0, Referrer(), PAGE_TRANSITION_TYPED, std::string());
- test_rvh()->SendNavigate(0, url0);
+ main_test_rfh()->SendNavigate(0, url0);
controller.LoadURL(
url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
@@ -2705,7 +2783,7 @@ TEST_F(NavigationControllerTest, ReloadTransient) {
EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url0);
// Load of |transient_url| completes.
- test_rvh()->SendNavigate(1, transient_url);
+ main_test_rfh()->SendNavigate(1, transient_url);
ASSERT_EQ(controller.GetEntryCount(), 2);
EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url0);
EXPECT_EQ(controller.GetEntryAtIndex(1)->GetURL(), transient_url);
@@ -2725,7 +2803,7 @@ TEST_F(NavigationControllerTest, RendererInitiatedPendingEntries) {
// We create pending entries for renderer-initiated navigations so that we
// can show them in new tabs when it is safe.
- navigator->DidStartProvisionalLoad(main_test_rfh(), 1, -1, true, url1);
+ navigator->DidStartProvisionalLoad(main_test_rfh(), -1, url1);
// Simulate what happens if a BrowserURLHandler rewrites the URL, causing
// the virtual URL to differ from the URL.
@@ -2739,35 +2817,35 @@ TEST_F(NavigationControllerTest, RendererInitiatedPendingEntries) {
is_renderer_initiated());
// If the user clicks another link, we should replace the pending entry.
- navigator->DidStartProvisionalLoad(main_test_rfh(), 1, -1, true, url2);
+ navigator->DidStartProvisionalLoad(main_test_rfh(), -1, url2);
EXPECT_EQ(url2, controller.GetPendingEntry()->GetURL());
EXPECT_EQ(url2, controller.GetPendingEntry()->GetVirtualURL());
// Once it commits, the URL and virtual URL should reflect the actual page.
- test_rvh()->SendNavigate(0, url2);
+ main_test_rfh()->SendNavigate(0, url2);
EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetURL());
EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetVirtualURL());
// We should not replace the pending entry for an error URL.
- navigator->DidStartProvisionalLoad(main_test_rfh(), 1, -1, true, url1);
+ navigator->DidStartProvisionalLoad(main_test_rfh(), -1, url1);
EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
navigator->DidStartProvisionalLoad(
- main_test_rfh(), 1, -1, true, GURL(kUnreachableWebDataURL));
+ main_test_rfh(), -1, GURL(kUnreachableWebDataURL));
EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
// We should remember if the pending entry will replace the current one.
// http://crbug.com/308444.
- navigator->DidStartProvisionalLoad(main_test_rfh(), 1, -1, true, url1);
+ navigator->DidStartProvisionalLoad(main_test_rfh(), -1, url1);
NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
set_should_replace_entry(true);
- navigator->DidStartProvisionalLoad(main_test_rfh(), 1, -1, true, url2);
+ navigator->DidStartProvisionalLoad(main_test_rfh(), -1, url2);
EXPECT_TRUE(
NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
should_replace_entry());
// TODO(nasko): Until OnNavigate is moved to RenderFrameHost, we need
// to go through the RenderViewHost. The TestRenderViewHost routes navigations
// to the main frame.
- test_rvh()->SendNavigate(0, url2);
+ main_test_rfh()->SendNavigate(0, url2);
EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetURL());
}
@@ -2787,7 +2865,7 @@ TEST_F(NavigationControllerTest, DontShowRendererURLUntilCommit) {
controller.LoadURL(url0, Referrer(), PAGE_TRANSITION_TYPED, std::string());
EXPECT_EQ(url0, controller.GetPendingEntry()->GetURL());
EXPECT_EQ(url0, controller.GetVisibleEntry()->GetURL());
- test_rvh()->SendNavigate(0, url0);
+ main_test_rfh()->SendNavigate(0, url0);
// For link clicks (renderer-initiated navigations), the pending entry should
// update before commit but the visible should not.
@@ -2802,7 +2880,7 @@ TEST_F(NavigationControllerTest, DontShowRendererURLUntilCommit) {
// After commit, both visible should be updated, there should be no pending
// entry, and we should no longer treat the entry as renderer-initiated.
- test_rvh()->SendNavigate(1, url1);
+ main_test_rfh()->SendNavigate(1, url1);
EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
EXPECT_FALSE(controller.GetPendingEntry());
EXPECT_FALSE(
@@ -2836,16 +2914,117 @@ TEST_F(NavigationControllerTest, ShowRendererURLInNewTabUntilModified) {
NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
is_renderer_initiated());
EXPECT_TRUE(controller.IsInitialNavigation());
- EXPECT_FALSE(test_rvh()->has_accessed_initial_document());
+ EXPECT_FALSE(contents()->HasAccessedInitialDocument());
// There should be no title yet.
EXPECT_TRUE(contents()->GetTitle().empty());
// If something else modifies the contents of the about:blank page, then
// we must revert to showing about:blank to avoid a URL spoof.
- test_rvh()->OnMessageReceived(
- ViewHostMsg_DidAccessInitialDocument(0));
- EXPECT_TRUE(test_rvh()->has_accessed_initial_document());
+ main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
+ EXPECT_TRUE(contents()->HasAccessedInitialDocument());
+ EXPECT_FALSE(controller.GetVisibleEntry());
+ EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
+
+ notifications.Reset();
+}
+
+// Tests that the URLs for browser-initiated navigations in new tabs are
+// displayed to the user even after they fail, as long as the initial
+// about:blank page has not been modified. If so, we must revert to showing
+// about:blank. See http://crbug.com/355537.
+TEST_F(NavigationControllerTest, ShowBrowserURLAfterFailUntilModified) {
+ NavigationControllerImpl& controller = controller_impl();
+ TestNotificationTracker notifications;
+ RegisterForAllNavNotifications(&notifications, &controller);
+
+ const GURL url("http://foo");
+
+ // For browser-initiated navigations in new tabs (with no committed entries),
+ // we show the pending entry's URL as long as the about:blank page is not
+ // modified. This is possible in cases that the user types a URL into a popup
+ // tab created with a slow URL.
+ NavigationController::LoadURLParams load_url_params(url);
+ load_url_params.transition_type = PAGE_TRANSITION_TYPED;
+ load_url_params.is_renderer_initiated = false;
+ controller.LoadURLWithParams(load_url_params);
+ EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
+ EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
+ EXPECT_FALSE(
+ NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
+ is_renderer_initiated());
+ EXPECT_TRUE(controller.IsInitialNavigation());
+ EXPECT_FALSE(contents()->HasAccessedInitialDocument());
+
+ // There should be no title yet.
+ EXPECT_TRUE(contents()->GetTitle().empty());
+
+ // Suppose it aborts before committing, if it's a 204 or download or due to a
+ // stop or a new navigation from the user. The URL should remain visible.
+ FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
+ params.error_code = net::ERR_ABORTED;
+ params.error_description = base::string16();
+ params.url = url;
+ params.showing_repost_interstitial = false;
+ main_test_rfh()->OnMessageReceived(
+ FrameHostMsg_DidFailProvisionalLoadWithError(0, params));
+ contents()->SetIsLoading(test_rvh(), false, true, NULL);
+ EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
+
+ // If something else later modifies the contents of the about:blank page, then
+ // we must revert to showing about:blank to avoid a URL spoof.
+ main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
+ EXPECT_TRUE(contents()->HasAccessedInitialDocument());
+ EXPECT_FALSE(controller.GetVisibleEntry());
+ EXPECT_FALSE(controller.GetPendingEntry());
+
+ notifications.Reset();
+}
+
+// Tests that the URLs for renderer-initiated navigations in new tabs are
+// displayed to the user even after they fail, as long as the initial
+// about:blank page has not been modified. If so, we must revert to showing
+// about:blank. See http://crbug.com/355537.
+TEST_F(NavigationControllerTest, ShowRendererURLAfterFailUntilModified) {
+ NavigationControllerImpl& controller = controller_impl();
+ TestNotificationTracker notifications;
+ RegisterForAllNavNotifications(&notifications, &controller);
+
+ const GURL url("http://foo");
+
+ // For renderer-initiated navigations in new tabs (with no committed entries),
+ // we show the pending entry's URL as long as the about:blank page is not
+ // modified.
+ NavigationController::LoadURLParams load_url_params(url);
+ load_url_params.transition_type = PAGE_TRANSITION_LINK;
+ load_url_params.is_renderer_initiated = true;
+ controller.LoadURLWithParams(load_url_params);
+ EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
+ EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
+ EXPECT_TRUE(
+ NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
+ is_renderer_initiated());
+ EXPECT_TRUE(controller.IsInitialNavigation());
+ EXPECT_FALSE(contents()->HasAccessedInitialDocument());
+
+ // There should be no title yet.
+ EXPECT_TRUE(contents()->GetTitle().empty());
+
+ // Suppose it aborts before committing, if it's a 204 or download or due to a
+ // stop or a new navigation from the user. The URL should remain visible.
+ FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
+ params.error_code = net::ERR_ABORTED;
+ params.error_description = base::string16();
+ params.url = url;
+ params.showing_repost_interstitial = false;
+ main_test_rfh()->OnMessageReceived(
+ FrameHostMsg_DidFailProvisionalLoadWithError(0, params));
+ EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
+
+ // If something else later modifies the contents of the about:blank page, then
+ // we must revert to showing about:blank to avoid a URL spoof.
+ main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
+ EXPECT_TRUE(contents()->HasAccessedInitialDocument());
EXPECT_FALSE(controller.GetVisibleEntry());
EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
@@ -2872,10 +3051,10 @@ TEST_F(NavigationControllerTest, DontShowRendererURLInNewTabAfterCommit) {
NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
is_renderer_initiated());
EXPECT_TRUE(controller.IsInitialNavigation());
- EXPECT_FALSE(test_rvh()->has_accessed_initial_document());
+ EXPECT_FALSE(contents()->HasAccessedInitialDocument());
// Simulate a commit and then starting a new pending navigation.
- test_rvh()->SendNavigate(0, url1);
+ main_test_rfh()->SendNavigate(0, url1);
NavigationController::LoadURLParams load_url2_params(url2);
load_url2_params.transition_type = PAGE_TRANSITION_LINK;
load_url2_params.is_renderer_initiated = true;
@@ -2883,7 +3062,7 @@ TEST_F(NavigationControllerTest, DontShowRendererURLInNewTabAfterCommit) {
// We should not consider this an initial navigation, and thus should
// not show the pending URL.
- EXPECT_FALSE(test_rvh()->has_accessed_initial_document());
+ EXPECT_FALSE(contents()->HasAccessedInitialDocument());
EXPECT_FALSE(controller.IsInitialNavigation());
EXPECT_TRUE(controller.GetVisibleEntry());
EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
@@ -2897,24 +3076,31 @@ TEST_F(NavigationControllerTest, IsInPageNavigation) {
NavigationControllerImpl& controller = controller_impl();
// Navigate to URL with no refs.
const GURL url("http://www.google.com/home.html");
- test_rvh()->SendNavigate(0, url);
+ main_test_rfh()->SendNavigate(0, url);
// Reloading the page is not an in-page navigation.
- EXPECT_FALSE(controller.IsURLInPageNavigation(url));
+ EXPECT_FALSE(controller.IsURLInPageNavigation(url, false,
+ NAVIGATION_TYPE_UNKNOWN));
const GURL other_url("http://www.google.com/add.html");
- EXPECT_FALSE(controller.IsURLInPageNavigation(other_url));
+ EXPECT_FALSE(controller.IsURLInPageNavigation(other_url, false,
+ NAVIGATION_TYPE_UNKNOWN));
const GURL url_with_ref("http://www.google.com/home.html#my_ref");
- EXPECT_TRUE(controller.IsURLInPageNavigation(url_with_ref));
+ EXPECT_TRUE(controller.IsURLInPageNavigation(url_with_ref, true,
+ NAVIGATION_TYPE_UNKNOWN));
// Navigate to URL with refs.
- test_rvh()->SendNavigate(1, url_with_ref);
+ main_test_rfh()->SendNavigate(1, url_with_ref);
// Reloading the page is not an in-page navigation.
- EXPECT_FALSE(controller.IsURLInPageNavigation(url_with_ref));
- EXPECT_FALSE(controller.IsURLInPageNavigation(url));
- EXPECT_FALSE(controller.IsURLInPageNavigation(other_url));
+ EXPECT_FALSE(controller.IsURLInPageNavigation(url_with_ref, false,
+ NAVIGATION_TYPE_UNKNOWN));
+ EXPECT_FALSE(controller.IsURLInPageNavigation(url, false,
+ NAVIGATION_TYPE_UNKNOWN));
+ EXPECT_FALSE(controller.IsURLInPageNavigation(other_url, false,
+ NAVIGATION_TYPE_UNKNOWN));
const GURL other_url_with_ref("http://www.google.com/home.html#my_other_ref");
- EXPECT_TRUE(controller.IsURLInPageNavigation(other_url_with_ref));
+ EXPECT_TRUE(controller.IsURLInPageNavigation(other_url_with_ref, true,
+ NAVIGATION_TYPE_UNKNOWN));
// Going to the same url again will be considered in-page
// if the renderer says it is even if the navigation type isn't IN_PAGE.
@@ -2925,6 +3111,17 @@ TEST_F(NavigationControllerTest, IsInPageNavigation) {
// type is IN_PAGE.
EXPECT_TRUE(controller.IsURLInPageNavigation(url, true,
NAVIGATION_TYPE_IN_PAGE));
+
+ // If the renderer says this is a same-origin in-page navigation, believe it.
+ // This is the pushState/replaceState case.
+ EXPECT_TRUE(controller.IsURLInPageNavigation(other_url, true,
+ NAVIGATION_TYPE_UNKNOWN));
+
+ // Don't believe the renderer if it claims a cross-origin navigation is
+ // in-page.
+ const GURL different_origin_url("http://www.example.com");
+ EXPECT_FALSE(controller.IsURLInPageNavigation(different_origin_url, true,
+ NAVIGATION_TYPE_UNKNOWN));
}
// Some pages can have subframes with the same base URL (minus the reference) as
@@ -2935,7 +3132,7 @@ TEST_F(NavigationControllerTest, SameSubframe) {
NavigationControllerImpl& controller = controller_impl();
// Navigate the main frame.
const GURL url("http://www.google.com/");
- test_rvh()->SendNavigate(0, url);
+ main_test_rfh()->SendNavigate(0, url);
// We should be at the first navigation entry.
EXPECT_EQ(controller.GetEntryCount(), 1);
@@ -2943,7 +3140,7 @@ TEST_F(NavigationControllerTest, SameSubframe) {
// Navigate a subframe that would normally count as in-page.
const GURL subframe("http://www.google.com/#");
- ViewHostMsg_FrameNavigate_Params params;
+ FrameHostMsg_DidCommitProvisionalLoad_Params params;
params.page_id = 0;
params.url = subframe;
params.transition = PAGE_TRANSITION_AUTO_SUBFRAME;
@@ -2952,7 +3149,8 @@ TEST_F(NavigationControllerTest, SameSubframe) {
params.is_post = false;
params.page_state = PageState::CreateFromURL(subframe);
LoadCommittedDetails details;
- EXPECT_FALSE(controller.RendererDidNavigate(params, &details));
+ EXPECT_FALSE(controller.RendererDidNavigate(main_test_rfh(), params,
+ &details));
// Nothing should have changed.
EXPECT_EQ(controller.GetEntryCount(), 1);
@@ -2965,7 +3163,7 @@ TEST_F(NavigationControllerTest, CloneAndGoBack) {
NavigationControllerImpl& controller = controller_impl();
const GURL url1("http://foo1");
const GURL url2("http://foo2");
- const base::string16 title(ASCIIToUTF16("Title"));
+ const base::string16 title(base::ASCIIToUTF16("Title"));
NavigateAndCommit(url1);
controller.GetVisibleEntry()->SetTitle(title);
@@ -2990,7 +3188,7 @@ TEST_F(NavigationControllerTest, CloneAndReload) {
NavigationControllerImpl& controller = controller_impl();
const GURL url1("http://foo1");
const GURL url2("http://foo2");
- const base::string16 title(ASCIIToUTF16("Title"));
+ const base::string16 title(base::ASCIIToUTF16("Title"));
NavigateAndCommit(url1);
controller.GetVisibleEntry()->SetTitle(title);
@@ -3058,7 +3256,7 @@ TEST_F(NavigationControllerTest, SubframeWhilePending) {
// Send a subframe update from the first page, as if one had just
// automatically loaded. Auto subframes don't increment the page ID.
const GURL url1_sub("http://foo/subframe");
- ViewHostMsg_FrameNavigate_Params params;
+ FrameHostMsg_DidCommitProvisionalLoad_Params params;
params.page_id = controller.GetLastCommittedEntry()->GetPageID();
params.url = url1_sub;
params.transition = PAGE_TRANSITION_AUTO_SUBFRAME;
@@ -3069,7 +3267,8 @@ TEST_F(NavigationControllerTest, SubframeWhilePending) {
LoadCommittedDetails details;
// This should return false meaning that nothing was actually updated.
- EXPECT_FALSE(controller.RendererDidNavigate(params, &details));
+ EXPECT_FALSE(controller.RendererDidNavigate(main_test_rfh(), params,
+ &details));
// The notification should have updated the last committed one, and not
// the pending load.
@@ -3591,8 +3790,63 @@ TEST_F(NavigationControllerTest, CopyStateFromAndPruneMaxEntriesReplaceEntry) {
NavigationControllerImpl::set_max_entry_count_for_testing(original_count);
}
+// Tests that we can navigate to the restored entries
+// imported by CopyStateFromAndPrune.
+TEST_F(NavigationControllerTest, CopyRestoredStateAndNavigate) {
+ const GURL kRestoredUrls[] = {
+ GURL("http://site1.com"),
+ GURL("http://site2.com"),
+ };
+ const GURL kInitialUrl("http://site3.com");
+
+ std::vector<NavigationEntry*> entries;
+ for (size_t i = 0; i < arraysize(kRestoredUrls); ++i) {
+ NavigationEntry* entry = NavigationControllerImpl::CreateNavigationEntry(
+ kRestoredUrls[i], Referrer(), PAGE_TRANSITION_RELOAD, false,
+ std::string(), browser_context());
+ entry->SetPageID(static_cast<int>(i));
+ entries.push_back(entry);
+ }
+
+ // Create a WebContents with restored entries.
+ scoped_ptr<TestWebContents> source_contents(
+ static_cast<TestWebContents*>(CreateTestWebContents()));
+ NavigationControllerImpl& source_controller =
+ source_contents->GetController();
+ source_controller.Restore(
+ entries.size() - 1,
+ NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY,
+ &entries);
+ ASSERT_EQ(0u, entries.size());
+ source_controller.LoadIfNecessary();
+ source_contents->CommitPendingNavigation();
+
+ // Load a page, then copy state from |source_contents|.
+ NavigateAndCommit(kInitialUrl);
+ contents()->ExpectSetHistoryLengthAndPrune(
+ GetSiteInstanceFromEntry(controller_impl().GetEntryAtIndex(0)), 2,
+ controller_impl().GetEntryAtIndex(0)->GetPageID());
+ controller_impl().CopyStateFromAndPrune(&source_controller, false);
+ ASSERT_EQ(3, controller_impl().GetEntryCount());
+
+ // Go back to the first entry one at a time and
+ // verify that it works as expected.
+ EXPECT_EQ(2, controller_impl().GetCurrentEntryIndex());
+ EXPECT_EQ(kInitialUrl, controller_impl().GetActiveEntry()->GetURL());
+
+ controller_impl().GoBack();
+ contents()->CommitPendingNavigation();
+ EXPECT_EQ(1, controller_impl().GetCurrentEntryIndex());
+ EXPECT_EQ(kRestoredUrls[1], controller_impl().GetActiveEntry()->GetURL());
+
+ controller_impl().GoBack();
+ contents()->CommitPendingNavigation();
+ EXPECT_EQ(0, controller_impl().GetCurrentEntryIndex());
+ EXPECT_EQ(kRestoredUrls[0], controller_impl().GetActiveEntry()->GetURL());
+}
+
// Tests that navigations initiated from the page (with the history object)
-// work as expected without navigation entries.
+// work as expected, creating pending entries.
TEST_F(NavigationControllerTest, HistoryNavigate) {
NavigationControllerImpl& controller = controller_impl();
const GURL url1("http://foo/1");
@@ -3605,38 +3859,39 @@ TEST_F(NavigationControllerTest, HistoryNavigate) {
controller.GoBack();
contents()->CommitPendingNavigation();
- // Simulate the page calling history.back(), it should not create a pending
- // entry.
+ // Simulate the page calling history.back(). It should create a pending entry.
contents()->OnGoToEntryAtOffset(-1);
- EXPECT_EQ(-1, controller.GetPendingEntryIndex());
+ EXPECT_EQ(0, controller.GetPendingEntryIndex());
// The actual cross-navigation is suspended until the current RVH tells us
// it unloaded, simulate that.
contents()->ProceedWithCrossSiteNavigation();
// Also make sure we told the page to navigate.
const IPC::Message* message =
- process()->sink().GetFirstMessageMatching(ViewMsg_Navigate::ID);
+ process()->sink().GetFirstMessageMatching(FrameMsg_Navigate::ID);
ASSERT_TRUE(message != NULL);
- Tuple1<ViewMsg_Navigate_Params> nav_params;
- ViewMsg_Navigate::Read(message, &nav_params);
+ Tuple1<FrameMsg_Navigate_Params> nav_params;
+ FrameMsg_Navigate::Read(message, &nav_params);
EXPECT_EQ(url1, nav_params.a.url);
process()->sink().ClearMessages();
// Now test history.forward()
- contents()->OnGoToEntryAtOffset(1);
- EXPECT_EQ(-1, controller.GetPendingEntryIndex());
+ contents()->OnGoToEntryAtOffset(2);
+ EXPECT_EQ(2, controller.GetPendingEntryIndex());
// The actual cross-navigation is suspended until the current RVH tells us
// it unloaded, simulate that.
contents()->ProceedWithCrossSiteNavigation();
- message = process()->sink().GetFirstMessageMatching(ViewMsg_Navigate::ID);
+ message = process()->sink().GetFirstMessageMatching(FrameMsg_Navigate::ID);
ASSERT_TRUE(message != NULL);
- ViewMsg_Navigate::Read(message, &nav_params);
+ FrameMsg_Navigate::Read(message, &nav_params);
EXPECT_EQ(url3, nav_params.a.url);
process()->sink().ClearMessages();
+ controller.DiscardNonCommittedEntries();
+
// Make sure an extravagant history.go() doesn't break.
contents()->OnGoToEntryAtOffset(120); // Out of bounds.
EXPECT_EQ(-1, controller.GetPendingEntryIndex());
- message = process()->sink().GetFirstMessageMatching(ViewMsg_Navigate::ID);
+ message = process()->sink().GetFirstMessageMatching(FrameMsg_Navigate::ID);
EXPECT_TRUE(message == NULL);
}
@@ -3733,7 +3988,7 @@ TEST_F(NavigationControllerTest, PruneAllButLastCommittedForPendingNotInList) {
EXPECT_EQ(1, controller.GetEntryCount());
// Try to commit the pending entry.
- test_rvh()->SendNavigate(2, url3);
+ main_test_rfh()->SendNavigate(2, url3);
EXPECT_EQ(-1, controller.GetPendingEntryIndex());
EXPECT_FALSE(controller.GetPendingEntry());
EXPECT_EQ(2, controller.GetEntryCount());
@@ -3775,7 +4030,7 @@ TEST_F(NavigationControllerTest, IsInitialNavigation) {
// After commit, it stays false.
const GURL url1("http://foo1");
- test_rvh()->SendNavigate(0, url1);
+ main_test_rfh()->SendNavigate(0, url1);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
EXPECT_FALSE(controller.IsInitialNavigation());
@@ -3798,7 +4053,7 @@ TEST_F(NavigationControllerTest, ClearFaviconOnRedirect) {
TestNotificationTracker notifications;
RegisterForAllNavNotifications(&notifications, &controller);
- test_rvh()->SendNavigate(0, kPageWithFavicon);
+ main_test_rfh()->SendNavigate(0, kPageWithFavicon);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
@@ -3813,7 +4068,7 @@ TEST_F(NavigationControllerTest, ClearFaviconOnRedirect) {
favicon_status.valid = true;
EXPECT_FALSE(DoImagesMatch(kDefaultFavicon, entry->GetFavicon().image));
- test_rvh()->SendNavigateWithTransition(
+ main_test_rfh()->SendNavigateWithTransition(
0, // same page ID.
kPageWithoutFavicon,
PAGE_TRANSITION_CLIENT_REDIRECT);
@@ -3838,7 +4093,7 @@ TEST_F(NavigationControllerTest, BackNavigationDoesNotClearFavicon) {
TestNotificationTracker notifications;
RegisterForAllNavNotifications(&notifications, &controller);
- test_rvh()->SendNavigate(0, kUrl1);
+ main_test_rfh()->SendNavigate(0, kUrl1);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
@@ -3852,10 +4107,10 @@ TEST_F(NavigationControllerTest, BackNavigationDoesNotClearFavicon) {
favicon_status.valid = true;
// Navigate to another page and go back to the original page.
- test_rvh()->SendNavigate(1, kUrl2);
+ main_test_rfh()->SendNavigate(1, kUrl2);
EXPECT_EQ(1U, navigation_entry_committed_counter_);
navigation_entry_committed_counter_ = 0;
- test_rvh()->SendNavigateWithTransition(
+ main_test_rfh()->SendNavigateWithTransition(
0,
kUrl1,
PAGE_TRANSITION_FORWARD_BACK);
@@ -3965,6 +4220,30 @@ TEST_F(NavigationControllerTest, MAYBE_PurgeScreenshot) {
}
}
+TEST_F(NavigationControllerTest, PushStateUpdatesTitle) {
+
+ // Navigate
+ test_rvh()->SendNavigate(1, GURL("http://foo"));
+
+ // Set title
+ base::string16 title(base::ASCIIToUTF16("Title"));
+ controller().GetLastCommittedEntry()->SetTitle(title);
+
+ // history.pushState() is called.
+ FrameHostMsg_DidCommitProvisionalLoad_Params params;
+ GURL url("http://foo#foo");
+ params.page_id = 2;
+ params.url = url;
+ params.page_state = PageState::CreateFromURL(url);
+ params.was_within_same_page = true;
+ test_rvh()->SendNavigateWithParams(&params);
+
+ // The title should immediately be visible on the new NavigationEntry.
+ base::string16 new_title =
+ controller().GetLastCommittedEntry()->GetTitleForDisplay(std::string());
+ EXPECT_EQ(title, new_title);
+}
+
// Test that the navigation controller clears its session history when a
// navigation commits with the clear history list flag set.
TEST_F(NavigationControllerTest, ClearHistoryList) {
@@ -4014,4 +4293,40 @@ TEST_F(NavigationControllerTest, ClearHistoryList) {
EXPECT_EQ(url4, controller.GetVisibleEntry()->GetURL());
}
+TEST_F(NavigationControllerTest, PostThenReplaceStateThenReload) {
+ scoped_ptr<TestWebContentsDelegate> delegate(new TestWebContentsDelegate());
+ EXPECT_FALSE(contents()->GetDelegate());
+ contents()->SetDelegate(delegate.get());
+
+ // Submit a form.
+ GURL url("http://foo");
+ FrameHostMsg_DidCommitProvisionalLoad_Params params;
+ params.page_id = 1;
+ params.url = url;
+ params.transition = PAGE_TRANSITION_FORM_SUBMIT;
+ params.gesture = NavigationGestureUser;
+ params.page_state = PageState::CreateFromURL(url);
+ params.was_within_same_page = false;
+ params.is_post = true;
+ params.post_id = 2;
+ test_rvh()->SendNavigateWithParams(&params);
+
+ // history.replaceState() is called.
+ GURL replace_url("http://foo#foo");
+ params.page_id = 1;
+ params.url = replace_url;
+ params.transition = PAGE_TRANSITION_LINK;
+ params.gesture = NavigationGestureUser;
+ params.page_state = PageState::CreateFromURL(replace_url);
+ params.was_within_same_page = true;
+ params.is_post = false;
+ params.post_id = -1;
+ test_rvh()->SendNavigateWithParams(&params);
+
+ // Now reload. replaceState overrides the POST, so we should not show a
+ // repost warning dialog.
+ controller_impl().Reload(true);
+ EXPECT_EQ(0, delegate->repost_form_warning_count());
+}
+
} // namespace content
diff --git a/chromium/content/browser/frame_host/navigation_entry_impl.cc b/chromium/content/browser/frame_host/navigation_entry_impl.cc
index d5d6fc2c982..b021d5eda18 100644
--- a/chromium/content/browser/frame_host/navigation_entry_impl.cc
+++ b/chromium/content/browser/frame_host/navigation_entry_impl.cc
@@ -289,6 +289,19 @@ int NavigationEntryImpl::GetHttpStatusCode() const {
return http_status_code_;
}
+void NavigationEntryImpl::SetRedirectChain(
+ const std::vector<GURL>& redirect_chain) {
+ redirect_chain_ = redirect_chain;
+}
+
+const std::vector<GURL>& NavigationEntryImpl::GetRedirectChain() const {
+ return redirect_chain_;
+}
+
+bool NavigationEntryImpl::IsRestored() const {
+ return restore_type_ != RESTORE_NONE;
+}
+
void NavigationEntryImpl::SetCanLoadLocalResources(bool allow) {
can_load_local_resources_ = allow;
}
@@ -331,7 +344,7 @@ void NavigationEntryImpl::ResetForCommit() {
set_is_renderer_initiated(false);
set_transferred_global_request_id(GlobalRequestID());
set_should_replace_entry(false);
- redirect_chain_.clear();
+
set_should_clear_history_list(false);
set_frame_tree_node_id(-1);
}
diff --git a/chromium/content/browser/frame_host/navigation_entry_impl.h b/chromium/content/browser/frame_host/navigation_entry_impl.h
index 8a518888ce5..613f45edb42 100644
--- a/chromium/content/browser/frame_host/navigation_entry_impl.h
+++ b/chromium/content/browser/frame_host/navigation_entry_impl.h
@@ -86,6 +86,9 @@ class CONTENT_EXPORT NavigationEntryImpl
virtual void ClearExtraData(const std::string& key) OVERRIDE;
virtual void SetHttpStatusCode(int http_status_code) OVERRIDE;
virtual int GetHttpStatusCode() const OVERRIDE;
+ virtual void SetRedirectChain(const std::vector<GURL>& redirects) OVERRIDE;
+ virtual const std::vector<GURL>& GetRedirectChain() const OVERRIDE;
+ virtual bool IsRestored() const OVERRIDE;
// Once a navigation entry is committed, we should no longer track several
// pieces of non-persisted state, as documented on the members below.
@@ -192,16 +195,6 @@ class CONTENT_EXPORT NavigationEntryImpl
should_replace_entry_ = should_replace_entry;
}
- // Any redirects present in a pending entry when it is transferred from one
- // process to another. Not valid after commit.
- const std::vector<GURL>& redirect_chain() const {
- return redirect_chain_;
- }
-
- void set_redirect_chain(const std::vector<GURL>& redirect_chain) {
- redirect_chain_ = redirect_chain;
- }
-
void SetScreenshotPNGData(scoped_refptr<base::RefCountedBytes> png_data);
const scoped_refptr<base::RefCountedBytes> screenshot() const {
return screenshot_;
@@ -314,7 +307,8 @@ class CONTENT_EXPORT NavigationEntryImpl
bool should_replace_entry_;
// This is used when transferring a pending entry from one process to another.
- // It is cleared in |ResetForCommit| and should not be persisted.
+ // We also send this data through session sync for offline analysis.
+ // It is preserved after commit but should not be persisted.
std::vector<GURL> redirect_chain_;
// This is set to true when this entry's navigation should clear the session
diff --git a/chromium/content/browser/frame_host/navigation_entry_impl_unittest.cc b/chromium/content/browser/frame_host/navigation_entry_impl_unittest.cc
index 488106fefed..79768019179 100644
--- a/chromium/content/browser/frame_host/navigation_entry_impl_unittest.cc
+++ b/chromium/content/browser/frame_host/navigation_entry_impl_unittest.cc
@@ -11,6 +11,8 @@
#include "content/public/common/ssl_status.h"
#include "testing/gtest/include/gtest/gtest.h"
+using base::ASCIIToUTF16;
+
namespace content {
class NavigationEntryTest : public testing::Test {
@@ -173,11 +175,14 @@ TEST_F(NavigationEntryTest, NavigationEntryAccessors) {
// Restored
EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE, entry1_->restore_type());
+ EXPECT_FALSE(entry1_->IsRestored());
EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE, entry2_->restore_type());
+ EXPECT_FALSE(entry2_->IsRestored());
entry2_->set_restore_type(
NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY);
EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY,
entry2_->restore_type());
+ EXPECT_TRUE(entry2_->IsRestored());
// Original URL
EXPECT_EQ(GURL(), entry1_->GetOriginalRequestURL());
diff --git a/chromium/content/browser/frame_host/navigation_entry_screenshot_manager.cc b/chromium/content/browser/frame_host/navigation_entry_screenshot_manager.cc
index 0b955fc6d7c..1e476827703 100644
--- a/chromium/content/browser/frame_host/navigation_entry_screenshot_manager.cc
+++ b/chromium/content/browser/frame_host/navigation_entry_screenshot_manager.cc
@@ -9,9 +9,13 @@
#include "content/browser/frame_host/navigation_controller_impl.h"
#include "content/browser/frame_host/navigation_entry_impl.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
+#include "content/public/browser/overscroll_configuration.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/common/content_switches.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/codec/png_codec.h"
namespace {
@@ -23,7 +27,7 @@ const int kMinScreenshotIntervalMS = 1000;
namespace content {
-// Encodes an SkBitmap to PNG data in a worker thread.
+// Converts SkBitmap to grayscale and encodes to PNG data in a worker thread.
class ScreenshotData : public base::RefCountedThreadSafe<ScreenshotData> {
public:
ScreenshotData() {
@@ -49,7 +53,21 @@ class ScreenshotData : public base::RefCountedThreadSafe<ScreenshotData> {
void EncodeOnWorker(const SkBitmap& bitmap) {
std::vector<unsigned char> data;
- if (gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, true, &data))
+ // Paint |bitmap| to a kA8_Config SkBitmap
+ SkBitmap a8Bitmap;
+ a8Bitmap.setConfig(SkBitmap::kA8_Config,
+ bitmap.width(),
+ bitmap.height(),
+ 0);
+ a8Bitmap.allocPixels();
+ SkCanvas canvas(a8Bitmap);
+ SkPaint paint;
+ SkColorFilter* filter = SkLumaColorFilter::Create();
+ paint.setColorFilter(filter);
+ filter->unref();
+ canvas.drawBitmap(bitmap, SkIntToScalar(0), SkIntToScalar(0), &paint);
+ // Encode the a8Bitmap to grayscale PNG treating alpha as color intensity
+ if (gfx::PNGCodec::EncodeA8SkBitmap(a8Bitmap, &data))
data_ = new base::RefCountedBytes(data);
}
@@ -79,12 +97,11 @@ void NavigationEntryScreenshotManager::TakeScreenshot() {
if (!entry)
return;
+ if (!owner_->delegate()->CanOverscrollContent())
+ return;
+
RenderViewHost* render_view_host =
owner_->delegate()->GetRenderViewHost();
- if (!static_cast<RenderViewHostImpl*>
- (render_view_host)->overscroll_controller()) {
- return;
- }
content::RenderWidgetHostView* view = render_view_host->GetView();
if (!view)
return;
@@ -117,11 +134,14 @@ void NavigationEntryScreenshotManager::TakeScreenshotImpl(
NavigationEntryImpl* entry) {
DCHECK(host && host->GetView());
DCHECK(entry);
- host->CopyFromBackingStore(gfx::Rect(),
+ SkBitmap::Config preferred_format = host->PreferredReadbackFormat();
+ host->CopyFromBackingStore(
+ gfx::Rect(),
host->GetView()->GetViewBounds().size(),
base::Bind(&NavigationEntryScreenshotManager::OnScreenshotTaken,
screenshot_factory_.GetWeakPtr(),
- entry->GetUniqueID()));
+ entry->GetUniqueID()),
+ preferred_format);
}
void NavigationEntryScreenshotManager::SetMinScreenshotIntervalMS(
diff --git a/chromium/content/browser/frame_host/navigator.cc b/chromium/content/browser/frame_host/navigator.cc
new file mode 100644
index 00000000000..3d5eb8f906b
--- /dev/null
+++ b/chromium/content/browser/frame_host/navigator.cc
@@ -0,0 +1,25 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/frame_host/navigator.h"
+
+#include "base/time/time.h"
+
+namespace content {
+
+NavigationController* Navigator::GetController() {
+ return NULL;
+}
+
+bool Navigator::NavigateToPendingEntry(
+ RenderFrameHostImpl* render_frame_host,
+ NavigationController::ReloadType reload_type) {
+ return false;
+}
+
+base::TimeTicks Navigator::GetCurrentLoadStart() {
+ return base::TimeTicks::Now();
+}
+
+} // namespace content
diff --git a/chromium/content/browser/frame_host/navigator.h b/chromium/content/browser/frame_host/navigator.h
index d6658e4cd90..a64edeb717b 100644
--- a/chromium/content/browser/frame_host/navigator.h
+++ b/chromium/content/browser/frame_host/navigator.h
@@ -7,12 +7,21 @@
#include "base/memory/ref_counted.h"
#include "content/common/content_export.h"
+#include "content/public/browser/navigation_controller.h"
+#include "ui/base/window_open_disposition.h"
class GURL;
+struct FrameHostMsg_DidCommitProvisionalLoad_Params;
+struct FrameHostMsg_DidFailProvisionalLoadWithError_Params;
+
+namespace base {
+class TimeTicks;
+}
namespace content {
class NavigationControllerImpl;
+class NavigationEntryImpl;
class NavigatorDelegate;
class RenderFrameHostImpl;
@@ -25,13 +34,91 @@ class RenderFrameHostImpl;
// from WebContentsImpl to this interface.
class CONTENT_EXPORT Navigator : public base::RefCounted<Navigator> {
public:
+ // Returns the NavigationController associated with this Navigator.
+ virtual NavigationController* GetController();
+
+
+ // Notifications coming from the RenderFrameHosts ----------------------------
+
// The RenderFrameHostImpl started a provisional load.
virtual void DidStartProvisionalLoad(RenderFrameHostImpl* render_frame_host,
- int64 frame_id,
- int64 parent_frame_id,
- bool main_frame,
+ int parent_routing_id,
const GURL& url) {};
+ // The RenderFrameHostImpl has failed a provisional load.
+ virtual void DidFailProvisionalLoadWithError(
+ RenderFrameHostImpl* render_frame_host,
+ const FrameHostMsg_DidFailProvisionalLoadWithError_Params& params) {};
+
+ // The RenderFrameHostImpl has failed to load the document.
+ virtual void DidFailLoadWithError(
+ RenderFrameHostImpl* render_frame_host,
+ const GURL& url,
+ int error_code,
+ const base::string16& error_description) {}
+
+ // The RenderFrameHostImpl processed a redirect during a provisional load.
+ //
+ // TODO(creis): Remove this method and have the pre-rendering code listen to
+ // WebContentsObserver::DidGetRedirectForResourceRequest instead.
+ // See http://crbug.com/78512.
+ virtual void DidRedirectProvisionalLoad(
+ RenderFrameHostImpl* render_frame_host,
+ int32 page_id,
+ const GURL& source_url,
+ const GURL& target_url) {}
+
+ // The RenderFrameHostImpl has committed a navigation.
+ virtual void DidNavigate(
+ RenderFrameHostImpl* render_frame_host,
+ const FrameHostMsg_DidCommitProvisionalLoad_Params& params) {}
+
+ // Called by the NavigationController to cause the Navigator to navigate
+ // to the current pending entry. The NavigationController should be called
+ // back with RendererDidNavigate on success or DiscardPendingEntry on failure.
+ // The callbacks can be inside of this function, or at some future time.
+ //
+ // The entry has a PageID of -1 if newly created (corresponding to navigation
+ // to a new URL).
+ //
+ // If this method returns false, then the navigation is discarded (equivalent
+ // to calling DiscardPendingEntry on the NavigationController).
+ //
+ // TODO(nasko): Remove this method from the interface, since Navigator and
+ // NavigationController know about each other. This will be possible once
+ // initialization of Navigator and NavigationController is properly done.
+ virtual bool NavigateToPendingEntry(
+ RenderFrameHostImpl* render_frame_host,
+ NavigationController::ReloadType reload_type);
+
+
+ // Navigation requests -------------------------------------------------------
+
+ virtual base::TimeTicks GetCurrentLoadStart();
+
+ // The RenderFrameHostImpl has received a request to open a URL with the
+ // specified |disposition|.
+ virtual void RequestOpenURL(RenderFrameHostImpl* render_frame_host,
+ const GURL& url,
+ const Referrer& referrer,
+ WindowOpenDisposition disposition,
+ bool should_replace_current_entry,
+ bool user_gesture) {}
+
+ // The RenderFrameHostImpl wants to transfer the request to a new renderer.
+ // |redirect_chain| contains any redirect URLs (excluding |url|) that happened
+ // before the transfer.
+ virtual void RequestTransferURL(
+ RenderFrameHostImpl* render_frame_host,
+ const GURL& url,
+ const std::vector<GURL>& redirect_chain,
+ const Referrer& referrer,
+ PageTransition page_transition,
+ WindowOpenDisposition disposition,
+ const GlobalRequestID& transferred_global_request_id,
+ bool should_replace_current_entry,
+ bool user_gesture) {}
+
protected:
friend class base::RefCounted<Navigator>;
virtual ~Navigator() {}
diff --git a/chromium/content/browser/frame_host/navigator_delegate.cc b/chromium/content/browser/frame_host/navigator_delegate.cc
new file mode 100644
index 00000000000..4389c81e2fb
--- /dev/null
+++ b/chromium/content/browser/frame_host/navigator_delegate.cc
@@ -0,0 +1,17 @@
+// 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/frame_host/navigator_delegate.h"
+
+namespace content {
+
+bool NavigatorDelegate::CanOverscrollContent() const {
+ return false;
+}
+
+bool NavigatorDelegate::ShouldPreserveAbortedURLs() {
+ return false;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/frame_host/navigator_delegate.h b/chromium/content/browser/frame_host/navigator_delegate.h
index 62b5b48b0bb..0835fe04a49 100644
--- a/chromium/content/browser/frame_host/navigator_delegate.h
+++ b/chromium/content/browser/frame_host/navigator_delegate.h
@@ -5,31 +5,104 @@
#ifndef CONTENT_BROWSER_FRAME_HOST_NAVIGATOR_DELEGATE_H_
#define CONTENT_BROWSER_FRAME_HOST_NAVIGATOR_DELEGATE_H_
+#include "base/strings/string16.h"
#include "content/public/browser/invalidate_type.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/common/page_transition_types.h"
+#include "ui/base/window_open_disposition.h"
+
+class GURL;
+struct FrameHostMsg_DidCommitProvisionalLoad_Params;
+struct FrameHostMsg_DidFailProvisionalLoadWithError_Params;
namespace content {
-class RenderFrameHost;
+class RenderFrameHostImpl;
+struct LoadCommittedDetails;
+struct OpenURLParams;
// A delegate API used by Navigator to notify its embedder of navigation
// related events.
-class NavigatorDelegate {
+class CONTENT_EXPORT NavigatorDelegate {
public:
// The RenderFrameHost started a provisional load for the frame
// represented by |render_frame_host|.
virtual void DidStartProvisionalLoad(
RenderFrameHostImpl* render_frame_host,
- int64 frame_id,
- int64 parent_frame_id,
- bool is_main_frame,
+ int parent_routing_id,
const GURL& validated_url,
bool is_error_page,
bool is_iframe_srcdoc) {}
+ // A provisional load in |render_frame_host| failed.
+ virtual void DidFailProvisionalLoadWithError(
+ RenderFrameHostImpl* render_frame_host,
+ const FrameHostMsg_DidFailProvisionalLoadWithError_Params& params) {}
+
+ // Document load in |render_frame_host| failed.
+ virtual void DidFailLoadWithError(
+ RenderFrameHostImpl* render_frame_host,
+ const GURL& url,
+ int error_code,
+ const base::string16& error_description) {}
+
+ // A redirect was processed in |render_frame_host| during a provisional load.
+ virtual void DidRedirectProvisionalLoad(
+ RenderFrameHostImpl* render_frame_host,
+ const GURL& validated_target_url) {}
+
+ // A navigation was committed in |render_frame_host|.
+ virtual void DidCommitProvisionalLoad(
+ RenderFrameHostImpl* render_frame_host,
+ const base::string16& frame_unique_name,
+ bool is_main_frame,
+ const GURL& url,
+ PageTransition transition_type) {}
+
+ // Handles post-navigation tasks in navigation BEFORE the entry has been
+ // committed to the NavigationController.
+ virtual void DidNavigateMainFramePreCommit(
+ const FrameHostMsg_DidCommitProvisionalLoad_Params& params) {}
+
+ // Handles post-navigation tasks in navigation AFTER the entry has been
+ // committed to the NavigationController. Note that the NavigationEntry is
+ // not provided since it may be invalid/changed after being committed. The
+ // NavigationController's last committed entry is for this navigation.
+ virtual void DidNavigateMainFramePostCommit(
+ const LoadCommittedDetails& details,
+ const FrameHostMsg_DidCommitProvisionalLoad_Params& params) {}
+ virtual void DidNavigateAnyFramePostCommit(
+ RenderFrameHostImpl* render_frame_host,
+ const LoadCommittedDetails& details,
+ const FrameHostMsg_DidCommitProvisionalLoad_Params& params) {}
+
+ virtual void SetMainFrameMimeType(const std::string& mime_type) {}
+ virtual bool CanOverscrollContent() const;
+
// Notification to the Navigator embedder that navigation state has
// changed. This method corresponds to
// WebContents::NotifyNavigationStateChanged.
virtual void NotifyChangedNavigationState(InvalidateTypes changed_flags) {}
+
+ // Notifies the Navigator embedder that it is beginning to navigate a frame.
+ virtual void AboutToNavigateRenderFrame(
+ RenderFrameHostImpl* render_frame_host) {}
+
+ // Notifies the Navigator embedder that a navigation to pending
+ // NavigationEntry has started in the browser process.
+ virtual void DidStartNavigationToPendingEntry(
+ RenderFrameHostImpl* render_frame_host,
+ const GURL& url,
+ NavigationController::ReloadType reload_type) {}
+
+ // Opens a URL with the given parameters. See PageNavigator::OpenURL, which
+ // this forwards to.
+ virtual void RequestOpenURL(RenderFrameHostImpl* render_frame_host,
+ const OpenURLParams& params) {}
+
+ // Returns whether URLs for aborted browser-initiated navigations should be
+ // preserved in the omnibox. Defaults to false.
+ virtual bool ShouldPreserveAbortedURLs();
};
} // namspace content
diff --git a/chromium/content/browser/frame_host/navigator_impl.cc b/chromium/content/browser/frame_host/navigator_impl.cc
index 6cc9dad1488..e12df2ac420 100644
--- a/chromium/content/browser/frame_host/navigator_impl.cc
+++ b/chromium/content/browser/frame_host/navigator_impl.cc
@@ -4,6 +4,8 @@
#include "content/browser/frame_host/navigator_impl.h"
+#include "base/command_line.h"
+#include "content/browser/frame_host/frame_tree.h"
#include "content/browser/frame_host/frame_tree_node.h"
#include "content/browser/frame_host/navigation_controller_impl.h"
#include "content/browser/frame_host/navigation_entry_impl.h"
@@ -11,14 +13,123 @@
#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/site_instance_impl.h"
+#include "content/browser/webui/web_ui_controller_factory_registry.h"
+#include "content/browser/webui/web_ui_impl.h"
+#include "content/common/frame_messages.h"
+#include "content/common/view_messages.h"
#include "content/public/browser/browser_context.h"
+#include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/global_request_id.h"
#include "content/public/browser/invalidate_type.h"
#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/navigation_details.h"
+#include "content/public/browser/page_navigator.h"
#include "content/public/browser/render_view_host.h"
+#include "content/public/common/bindings_policy.h"
+#include "content/public/common/content_client.h"
+#include "content/public/common/content_switches.h"
#include "content/public/common/url_constants.h"
+#include "content/public/common/url_utils.h"
namespace content {
+namespace {
+
+FrameMsg_Navigate_Type::Value GetNavigationType(
+ BrowserContext* browser_context, const NavigationEntryImpl& entry,
+ NavigationController::ReloadType reload_type) {
+ switch (reload_type) {
+ case NavigationControllerImpl::RELOAD:
+ return FrameMsg_Navigate_Type::RELOAD;
+ case NavigationControllerImpl::RELOAD_IGNORING_CACHE:
+ return FrameMsg_Navigate_Type::RELOAD_IGNORING_CACHE;
+ case NavigationControllerImpl::RELOAD_ORIGINAL_REQUEST_URL:
+ return FrameMsg_Navigate_Type::RELOAD_ORIGINAL_REQUEST_URL;
+ case NavigationControllerImpl::NO_RELOAD:
+ break; // Fall through to rest of function.
+ }
+
+ // |RenderViewImpl::PopulateStateFromPendingNavigationParams| differentiates
+ // between |RESTORE_WITH_POST| and |RESTORE|.
+ if (entry.restore_type() ==
+ NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY) {
+ if (entry.GetHasPostData())
+ return FrameMsg_Navigate_Type::RESTORE_WITH_POST;
+ return FrameMsg_Navigate_Type::RESTORE;
+ }
+
+ return FrameMsg_Navigate_Type::NORMAL;
+}
+
+void MakeNavigateParams(const NavigationEntryImpl& entry,
+ const NavigationControllerImpl& controller,
+ NavigationController::ReloadType reload_type,
+ FrameMsg_Navigate_Params* params) {
+ params->page_id = entry.GetPageID();
+ params->should_clear_history_list = entry.should_clear_history_list();
+ params->should_replace_current_entry = entry.should_replace_entry();
+ if (entry.should_clear_history_list()) {
+ // Set the history list related parameters to the same values a
+ // NavigationController would return before its first navigation. This will
+ // fully clear the RenderView's view of the session history.
+ params->pending_history_list_offset = -1;
+ params->current_history_list_offset = -1;
+ params->current_history_list_length = 0;
+ } else {
+ params->pending_history_list_offset = controller.GetIndexOfEntry(&entry);
+ params->current_history_list_offset =
+ controller.GetLastCommittedEntryIndex();
+ params->current_history_list_length = controller.GetEntryCount();
+ }
+ params->url = entry.GetURL();
+ if (!entry.GetBaseURLForDataURL().is_empty()) {
+ params->base_url_for_data_url = entry.GetBaseURLForDataURL();
+ params->history_url_for_data_url = entry.GetVirtualURL();
+ }
+ params->referrer = entry.GetReferrer();
+ params->transition = entry.GetTransitionType();
+ params->page_state = entry.GetPageState();
+ params->navigation_type =
+ GetNavigationType(controller.GetBrowserContext(), entry, reload_type);
+ params->request_time = base::Time::Now();
+ params->extra_headers = entry.extra_headers();
+ params->transferred_request_child_id =
+ entry.transferred_global_request_id().child_id;
+ params->transferred_request_request_id =
+ entry.transferred_global_request_id().request_id;
+ params->is_overriding_user_agent = entry.GetIsOverridingUserAgent();
+ // Avoid downloading when in view-source mode.
+ params->allow_download = !entry.IsViewSourceMode();
+ params->is_post = entry.GetHasPostData();
+ if (entry.GetBrowserInitiatedPostData()) {
+ params->browser_initiated_post_data.assign(
+ entry.GetBrowserInitiatedPostData()->front(),
+ entry.GetBrowserInitiatedPostData()->front() +
+ entry.GetBrowserInitiatedPostData()->size());
+ }
+
+ // Set the redirect chain to the navigation's redirects, unless we are
+ // returning to a completed navigation (whose previous redirects don't apply).
+ if (PageTransitionIsNewNavigation(params->transition)) {
+ params->redirects = entry.GetRedirectChain();
+ } else {
+ params->redirects.clear();
+ }
+
+ params->can_load_local_resources = entry.GetCanLoadLocalResources();
+ params->frame_to_navigate = entry.GetFrameToNavigate();
+}
+
+RenderFrameHostManager* GetRenderManager(RenderFrameHostImpl* rfh) {
+ if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess))
+ return rfh->frame_tree_node()->render_manager();
+
+ return rfh->frame_tree_node()->frame_tree()->root()->render_manager();
+}
+
+} // namespace
+
+
NavigatorImpl::NavigatorImpl(
NavigationControllerImpl* navigation_controller,
NavigatorDelegate* delegate)
@@ -26,27 +137,29 @@ NavigatorImpl::NavigatorImpl(
delegate_(delegate) {
}
+NavigationController* NavigatorImpl::GetController() {
+ return controller_;
+}
+
void NavigatorImpl::DidStartProvisionalLoad(
RenderFrameHostImpl* render_frame_host,
- int64 frame_id,
- int64 parent_frame_id,
- bool is_main_frame,
+ int parent_routing_id,
const GURL& url) {
bool is_error_page = (url.spec() == kUnreachableWebDataURL);
bool is_iframe_srcdoc = (url.spec() == kAboutSrcDocURL);
GURL validated_url(url);
RenderProcessHost* render_process_host = render_frame_host->GetProcess();
- RenderViewHost::FilterURL(render_process_host, false, &validated_url);
+ render_process_host->FilterURL(false, &validated_url);
+ bool is_main_frame = render_frame_host->frame_tree_node()->IsMainFrame();
+ NavigationEntryImpl* pending_entry =
+ NavigationEntryImpl::FromNavigationEntry(controller_->GetPendingEntry());
if (is_main_frame) {
// If there is no browser-initiated pending entry for this navigation and it
// is not for the error URL, create a pending entry using the current
// SiteInstance, and ensure the address bar updates accordingly. We don't
// know the referrer or extra headers at this point, but the referrer will
// be set properly upon commit.
- NavigationEntryImpl* pending_entry =
- NavigationEntryImpl::FromNavigationEntry(
- controller_->GetPendingEntry());
bool has_browser_initiated_pending_entry = pending_entry &&
!pending_entry->is_renderer_initiated();
if (!has_browser_initiated_pending_entry && !is_error_page) {
@@ -66,7 +179,7 @@ void NavigatorImpl::DidStartProvisionalLoad(
entry->set_transferred_global_request_id(
pending_entry->transferred_global_request_id());
entry->set_should_replace_entry(pending_entry->should_replace_entry());
- entry->set_redirect_chain(pending_entry->redirect_chain());
+ entry->SetRedirectChain(pending_entry->GetRedirectChain());
}
controller_->SetPendingEntry(entry);
if (delegate_)
@@ -77,9 +190,440 @@ void NavigatorImpl::DidStartProvisionalLoad(
if (delegate_) {
// Notify the observer about the start of the provisional load.
delegate_->DidStartProvisionalLoad(
- render_frame_host, frame_id, parent_frame_id, is_main_frame,
+ render_frame_host, parent_routing_id,
validated_url, is_error_page, is_iframe_srcdoc);
}
}
+
+void NavigatorImpl::DidFailProvisionalLoadWithError(
+ RenderFrameHostImpl* render_frame_host,
+ const FrameHostMsg_DidFailProvisionalLoadWithError_Params& params) {
+ VLOG(1) << "Failed Provisional Load: " << params.url.possibly_invalid_spec()
+ << ", error_code: " << params.error_code
+ << ", error_description: " << params.error_description
+ << ", showing_repost_interstitial: " <<
+ params.showing_repost_interstitial
+ << ", frame_id: " << render_frame_host->GetRoutingID();
+ GURL validated_url(params.url);
+ RenderProcessHost* render_process_host = render_frame_host->GetProcess();
+ render_process_host->FilterURL(false, &validated_url);
+
+ if (net::ERR_ABORTED == params.error_code) {
+ // EVIL HACK ALERT! Ignore failed loads when we're showing interstitials.
+ // This means that the interstitial won't be torn down properly, which is
+ // bad. But if we have an interstitial, go back to another tab type, and
+ // then load the same interstitial again, we could end up getting the first
+ // interstitial's "failed" message (as a result of the cancel) when we're on
+ // the second one. We can't tell this apart, so we think we're tearing down
+ // the current page which will cause a crash later on.
+ //
+ // http://code.google.com/p/chromium/issues/detail?id=2855
+ // Because this will not tear down the interstitial properly, if "back" is
+ // back to another tab type, the interstitial will still be somewhat alive
+ // in the previous tab type. If you navigate somewhere that activates the
+ // tab with the interstitial again, you'll see a flash before the new load
+ // commits of the interstitial page.
+ FrameTreeNode* root =
+ render_frame_host->frame_tree_node()->frame_tree()->root();
+ if (root->render_manager()->interstitial_page() != NULL) {
+ LOG(WARNING) << "Discarding message during interstitial.";
+ return;
+ }
+
+ // We used to cancel the pending renderer here for cross-site downloads.
+ // However, it's not safe to do that because the download logic repeatedly
+ // looks for this WebContents based on a render ID. Instead, we just
+ // leave the pending renderer around until the next navigation event
+ // (Navigate, DidNavigate, etc), which will clean it up properly.
+ //
+ // TODO(creis): Find a way to cancel any pending RFH here.
+ }
+
+ // We usually clear the pending entry when it fails, so that an arbitrary URL
+ // isn't left visible above a committed page. This must be enforced when
+ // the pending entry isn't visible (e.g., renderer-initiated navigations) to
+ // prevent URL spoofs for in-page navigations that don't go through
+ // DidStartProvisionalLoadForFrame.
+ //
+ // However, we do preserve the pending entry in some cases, such as on the
+ // initial navigation of an unmodified blank tab. We also allow the delegate
+ // to say when it's safe to leave aborted URLs in the omnibox, to let the user
+ // edit the URL and try again. This may be useful in cases that the committed
+ // page cannot be attacker-controlled. In these cases, we still allow the
+ // view to clear the pending entry and typed URL if the user requests
+ // (e.g., hitting Escape with focus in the address bar).
+ //
+ // Note: don't touch the transient entry, since an interstitial may exist.
+ bool should_preserve_entry = controller_->IsUnmodifiedBlankTab() ||
+ delegate_->ShouldPreserveAbortedURLs();
+ if (controller_->GetPendingEntry() != controller_->GetVisibleEntry() ||
+ !should_preserve_entry) {
+ controller_->DiscardPendingEntry();
+
+ // Also force the UI to refresh.
+ controller_->delegate()->NotifyNavigationStateChanged(INVALIDATE_TYPE_URL);
+ }
+
+ if (delegate_)
+ delegate_->DidFailProvisionalLoadWithError(render_frame_host, params);
+}
+
+void NavigatorImpl::DidFailLoadWithError(
+ RenderFrameHostImpl* render_frame_host,
+ const GURL& url,
+ int error_code,
+ const base::string16& error_description) {
+ if (delegate_) {
+ delegate_->DidFailLoadWithError(
+ render_frame_host, url, error_code,
+ error_description);
+ }
+}
+
+void NavigatorImpl::DidRedirectProvisionalLoad(
+ RenderFrameHostImpl* render_frame_host,
+ int32 page_id,
+ const GURL& source_url,
+ const GURL& target_url) {
+ // TODO(creis): Remove this method and have the pre-rendering code listen to
+ // WebContentsObserver::DidGetRedirectForResourceRequest instead.
+ // See http://crbug.com/78512.
+ GURL validated_source_url(source_url);
+ GURL validated_target_url(target_url);
+ RenderProcessHost* render_process_host = render_frame_host->GetProcess();
+ render_process_host->FilterURL(false, &validated_source_url);
+ render_process_host->FilterURL(false, &validated_target_url);
+ NavigationEntry* entry;
+ if (page_id == -1) {
+ entry = controller_->GetPendingEntry();
+ } else {
+ entry = controller_->GetEntryWithPageID(
+ render_frame_host->GetSiteInstance(), page_id);
+ }
+ if (!entry || entry->GetURL() != validated_source_url)
+ return;
+
+ if (delegate_) {
+ delegate_->DidRedirectProvisionalLoad(
+ render_frame_host, validated_target_url);
+ }
+}
+
+bool NavigatorImpl::NavigateToEntry(
+ RenderFrameHostImpl* render_frame_host,
+ const NavigationEntryImpl& entry,
+ NavigationController::ReloadType reload_type) {
+ TRACE_EVENT0("browser", "NavigatorImpl::NavigateToEntry");
+
+ // The renderer will reject IPC messages with URLs longer than
+ // this limit, so don't attempt to navigate with a longer URL.
+ if (entry.GetURL().spec().size() > GetMaxURLChars()) {
+ LOG(WARNING) << "Refusing to load URL as it exceeds " << GetMaxURLChars()
+ << " characters.";
+ return false;
+ }
+
+ RenderFrameHostManager* manager =
+ render_frame_host->frame_tree_node()->render_manager();
+ RenderFrameHostImpl* dest_render_frame_host = manager->Navigate(entry);
+ if (!dest_render_frame_host)
+ return false; // Unable to create the desired RenderFrameHost.
+
+ // Make sure no code called via RFHM::Navigate clears the pending entry.
+ CHECK_EQ(controller_->GetPendingEntry(), &entry);
+
+ // For security, we should never send non-Web-UI URLs to a Web UI renderer.
+ // Double check that here.
+ int enabled_bindings =
+ dest_render_frame_host->render_view_host()->GetEnabledBindings();
+ bool is_allowed_in_web_ui_renderer =
+ WebUIControllerFactoryRegistry::GetInstance()->IsURLAcceptableForWebUI(
+ controller_->GetBrowserContext(), entry.GetURL());
+ if ((enabled_bindings & BINDINGS_POLICY_WEB_UI) &&
+ !is_allowed_in_web_ui_renderer) {
+ // Log the URL to help us diagnose any future failures of this CHECK.
+ GetContentClient()->SetActiveURL(entry.GetURL());
+ CHECK(0);
+ }
+
+ // Notify observers that we will navigate in this RenderFrame.
+ if (delegate_)
+ delegate_->AboutToNavigateRenderFrame(dest_render_frame_host);
+
+ // Used for page load time metrics.
+ current_load_start_ = base::TimeTicks::Now();
+
+ // Navigate in the desired RenderFrameHost.
+ FrameMsg_Navigate_Params navigate_params;
+ MakeNavigateParams(entry, *controller_, reload_type, &navigate_params);
+ dest_render_frame_host->Navigate(navigate_params);
+
+ // Make sure no code called via RFH::Navigate clears the pending entry.
+ CHECK_EQ(controller_->GetPendingEntry(), &entry);
+
+ if (entry.GetPageID() == -1) {
+ // HACK!! This code suppresses javascript: URLs from being added to
+ // session history, which is what we want to do for javascript: URLs that
+ // do not generate content. What we really need is a message from the
+ // renderer telling us that a new page was not created. The same message
+ // could be used for mailto: URLs and the like.
+ if (entry.GetURL().SchemeIs(url::kJavaScriptScheme))
+ return false;
+ }
+
+ // Notify observers about navigation.
+ if (delegate_) {
+ delegate_->DidStartNavigationToPendingEntry(render_frame_host,
+ entry.GetURL(),
+ reload_type);
+ }
+
+ return true;
+}
+
+bool NavigatorImpl::NavigateToPendingEntry(
+ RenderFrameHostImpl* render_frame_host,
+ NavigationController::ReloadType reload_type) {
+ return NavigateToEntry(
+ render_frame_host,
+ *NavigationEntryImpl::FromNavigationEntry(controller_->GetPendingEntry()),
+ reload_type);
+}
+
+base::TimeTicks NavigatorImpl::GetCurrentLoadStart() {
+ return current_load_start_;
+}
+
+void NavigatorImpl::DidNavigate(
+ RenderFrameHostImpl* render_frame_host,
+ const FrameHostMsg_DidCommitProvisionalLoad_Params& input_params) {
+ FrameHostMsg_DidCommitProvisionalLoad_Params params(input_params);
+ FrameTree* frame_tree = render_frame_host->frame_tree_node()->frame_tree();
+ bool use_site_per_process =
+ CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess);
+
+ if (use_site_per_process) {
+ // TODO(creis): Until we mirror the frame tree in the subframe's process,
+ // cross-process subframe navigations happen in a renderer's main frame.
+ // Correct the transition type here if we know it is for a subframe.
+ NavigationEntryImpl* pending_entry =
+ NavigationEntryImpl::FromNavigationEntry(
+ controller_->GetPendingEntry());
+ if (!render_frame_host->frame_tree_node()->IsMainFrame() &&
+ pending_entry &&
+ pending_entry->frame_tree_node_id() ==
+ render_frame_host->frame_tree_node()->frame_tree_node_id()) {
+ params.transition = PAGE_TRANSITION_AUTO_SUBFRAME;
+ }
+ }
+
+ if (PageTransitionIsMainFrame(params.transition)) {
+ if (delegate_) {
+ // When overscroll navigation gesture is enabled, a screenshot of the page
+ // in its current state is taken so that it can be used during the
+ // nav-gesture. It is necessary to take the screenshot here, before
+ // calling RenderFrameHostManager::DidNavigateMainFrame, because that can
+ // change WebContents::GetRenderViewHost to return the new host, instead
+ // of the one that may have just been swapped out.
+ if (delegate_->CanOverscrollContent()) {
+ // Don't take screenshots if we are staying on the same page. We want
+ // in-page navigations to be super fast, and taking a screenshot
+ // currently blocks GPU for a longer time than we are willing to
+ // tolerate in this use case.
+ if (!params.was_within_same_page)
+ controller_->TakeScreenshot();
+ }
+
+ // Run tasks that must execute just before the commit.
+ delegate_->DidNavigateMainFramePreCommit(params);
+ }
+
+ if (!use_site_per_process)
+ frame_tree->root()->render_manager()->DidNavigateFrame(render_frame_host);
+ }
+
+ // When using --site-per-process, we notify the RFHM for all navigations,
+ // not just main frame navigations.
+ if (use_site_per_process) {
+ FrameTreeNode* frame = render_frame_host->frame_tree_node();
+ frame->render_manager()->DidNavigateFrame(render_frame_host);
+ }
+
+ // Update the site of the SiteInstance if it doesn't have one yet, unless
+ // assigning a site is not necessary for this URL. In that case, the
+ // SiteInstance can still be considered unused until a navigation to a real
+ // page.
+ SiteInstanceImpl* site_instance =
+ static_cast<SiteInstanceImpl*>(render_frame_host->GetSiteInstance());
+ if (!site_instance->HasSite() &&
+ ShouldAssignSiteForURL(params.url)) {
+ site_instance->SetSite(params.url);
+ }
+
+ // Need to update MIME type here because it's referred to in
+ // UpdateNavigationCommands() called by RendererDidNavigate() to
+ // determine whether or not to enable the encoding menu.
+ // It's updated only for the main frame. For a subframe,
+ // RenderView::UpdateURL does not set params.contents_mime_type.
+ // (see http://code.google.com/p/chromium/issues/detail?id=2929 )
+ // TODO(jungshik): Add a test for the encoding menu to avoid
+ // regressing it again.
+ // TODO(nasko): Verify the correctness of the above comment, since some of the
+ // code doesn't exist anymore. Also, move this code in the
+ // PageTransitionIsMainFrame code block above.
+ if (PageTransitionIsMainFrame(params.transition) && delegate_)
+ delegate_->SetMainFrameMimeType(params.contents_mime_type);
+
+ LoadCommittedDetails details;
+ bool did_navigate = controller_->RendererDidNavigate(render_frame_host,
+ params, &details);
+
+ // For now, keep track of each frame's URL in its FrameTreeNode. This lets
+ // us estimate our process count for implementing OOP iframes.
+ // TODO(creis): Remove this when we track which pages commit in each frame.
+ render_frame_host->frame_tree_node()->set_current_url(params.url);
+
+ // Send notification about committed provisional loads. This notification is
+ // different from the NAV_ENTRY_COMMITTED notification which doesn't include
+ // the actual URL navigated to and isn't sent for AUTO_SUBFRAME navigations.
+ if (details.type != NAVIGATION_TYPE_NAV_IGNORE && delegate_) {
+ // For AUTO_SUBFRAME navigations, an event for the main frame is generated
+ // that is not recorded in the navigation history. For the purpose of
+ // tracking navigation events, we treat this event as a sub frame navigation
+ // event.
+ bool is_main_frame = did_navigate ? details.is_main_frame : false;
+ PageTransition transition_type = params.transition;
+ // Whether or not a page transition was triggered by going backward or
+ // forward in the history is only stored in the navigation controller's
+ // entry list.
+ if (did_navigate &&
+ (controller_->GetLastCommittedEntry()->GetTransitionType() &
+ PAGE_TRANSITION_FORWARD_BACK)) {
+ transition_type = PageTransitionFromInt(
+ params.transition | PAGE_TRANSITION_FORWARD_BACK);
+ }
+
+ delegate_->DidCommitProvisionalLoad(render_frame_host,
+ params.frame_unique_name,
+ is_main_frame,
+ params.url,
+ transition_type);
+ }
+
+ if (!did_navigate)
+ return; // No navigation happened.
+
+ // DO NOT ADD MORE STUFF TO THIS FUNCTION! Your component should either listen
+ // for the appropriate notification (best) or you can add it to
+ // DidNavigateMainFramePostCommit / DidNavigateAnyFramePostCommit (only if
+ // necessary, please).
+
+ // Run post-commit tasks.
+ if (delegate_) {
+ if (details.is_main_frame)
+ delegate_->DidNavigateMainFramePostCommit(details, params);
+
+ delegate_->DidNavigateAnyFramePostCommit(
+ render_frame_host, details, params);
+ }
+}
+
+bool NavigatorImpl::ShouldAssignSiteForURL(const GURL& url) {
+ // about:blank should not "use up" a new SiteInstance. The SiteInstance can
+ // still be used for a normal web site.
+ if (url == GURL(url::kAboutBlankURL))
+ return false;
+
+ // The embedder will then have the opportunity to determine if the URL
+ // should "use up" the SiteInstance.
+ return GetContentClient()->browser()->ShouldAssignSiteForURL(url);
+}
+
+void NavigatorImpl::RequestOpenURL(
+ RenderFrameHostImpl* render_frame_host,
+ const GURL& url,
+ const Referrer& referrer,
+ WindowOpenDisposition disposition,
+ bool should_replace_current_entry,
+ bool user_gesture) {
+ SiteInstance* current_site_instance =
+ GetRenderManager(render_frame_host)->current_frame_host()->
+ GetSiteInstance();
+ // If this came from a swapped out RenderViewHost, we only allow the request
+ // if we are still in the same BrowsingInstance.
+ if (render_frame_host->render_view_host()->IsSwappedOut() &&
+ !render_frame_host->GetSiteInstance()->IsRelatedSiteInstance(
+ current_site_instance)) {
+ return;
+ }
+
+ // Delegate to RequestTransferURL because this is just the generic
+ // case where |old_request_id| is empty.
+ // TODO(creis): Pass the redirect_chain into this method to support client
+ // redirects. http://crbug.com/311721.
+ std::vector<GURL> redirect_chain;
+ RequestTransferURL(
+ render_frame_host, url, redirect_chain, referrer, PAGE_TRANSITION_LINK,
+ disposition, GlobalRequestID(),
+ should_replace_current_entry, user_gesture);
+}
+
+void NavigatorImpl::RequestTransferURL(
+ RenderFrameHostImpl* render_frame_host,
+ const GURL& url,
+ const std::vector<GURL>& redirect_chain,
+ const Referrer& referrer,
+ PageTransition page_transition,
+ WindowOpenDisposition disposition,
+ const GlobalRequestID& transferred_global_request_id,
+ bool should_replace_current_entry,
+ bool user_gesture) {
+ GURL dest_url(url);
+ SiteInstance* current_site_instance =
+ GetRenderManager(render_frame_host)->current_frame_host()->
+ GetSiteInstance();
+ if (!GetContentClient()->browser()->ShouldAllowOpenURL(
+ current_site_instance, url)) {
+ dest_url = GURL(url::kAboutBlankURL);
+ }
+
+ int64 frame_tree_node_id = -1;
+ if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess)) {
+ frame_tree_node_id =
+ render_frame_host->frame_tree_node()->frame_tree_node_id();
+ }
+ OpenURLParams params(
+ dest_url, referrer, frame_tree_node_id, disposition, page_transition,
+ true /* is_renderer_initiated */);
+ if (redirect_chain.size() > 0)
+ params.redirect_chain = redirect_chain;
+ params.transferred_global_request_id = transferred_global_request_id;
+ params.should_replace_current_entry = should_replace_current_entry;
+ params.user_gesture = user_gesture;
+
+ if (GetRenderManager(render_frame_host)->web_ui()) {
+ // Web UI pages sometimes want to override the page transition type for
+ // link clicks (e.g., so the new tab page can specify AUTO_BOOKMARK for
+ // automatically generated suggestions). We don't override other types
+ // like TYPED because they have different implications (e.g., autocomplete).
+ if (PageTransitionCoreTypeIs(params.transition, PAGE_TRANSITION_LINK))
+ params.transition =
+ GetRenderManager(render_frame_host)->web_ui()->
+ GetLinkTransitionType();
+
+ // Note also that we hide the referrer for Web UI pages. We don't really
+ // want web sites to see a referrer of "chrome://blah" (and some
+ // chrome: URLs might have search terms or other stuff we don't want to
+ // send to the site), so we send no referrer.
+ params.referrer = Referrer();
+
+ // Navigations in Web UI pages count as browser-initiated navigations.
+ params.is_renderer_initiated = false;
+ }
+
+ if (delegate_)
+ delegate_->RequestOpenURL(render_frame_host, params);
+}
+
} // namespace content
diff --git a/chromium/content/browser/frame_host/navigator_impl.h b/chromium/content/browser/frame_host/navigator_impl.h
index b66b87de036..1aa9a609c09 100644
--- a/chromium/content/browser/frame_host/navigator_impl.h
+++ b/chromium/content/browser/frame_host/navigator_impl.h
@@ -6,6 +6,7 @@
#define CONTENT_BROWSER_FRAME_HOST_NAVIGATOR_IMPL_H_
#include "base/memory/ref_counted.h"
+#include "content/browser/frame_host/navigation_controller_impl.h"
#include "content/browser/frame_host/navigator.h"
#include "content/common/content_export.h"
@@ -13,6 +14,7 @@ namespace content {
class NavigationControllerImpl;
class NavigatorDelegate;
+struct LoadCommittedDetails;
// This class is an implementation of Navigator, responsible for managing
// navigations in regular browser tabs.
@@ -22,15 +24,61 @@ class CONTENT_EXPORT NavigatorImpl : public Navigator {
NavigatorDelegate* delegate);
// Navigator implementation.
+ virtual NavigationController* GetController() OVERRIDE;
virtual void DidStartProvisionalLoad(RenderFrameHostImpl* render_frame_host,
- int64 frame_id,
- int64 parent_frame_id,
- bool main_frame,
+ int parent_routing_id,
const GURL& url) OVERRIDE;
+ virtual void DidFailProvisionalLoadWithError(
+ RenderFrameHostImpl* render_frame_host,
+ const FrameHostMsg_DidFailProvisionalLoadWithError_Params& params)
+ OVERRIDE;
+ virtual void DidFailLoadWithError(
+ RenderFrameHostImpl* render_frame_host,
+ const GURL& url,
+ int error_code,
+ const base::string16& error_description) OVERRIDE;
+ virtual void DidRedirectProvisionalLoad(
+ RenderFrameHostImpl* render_frame_host,
+ int32 page_id,
+ const GURL& source_url,
+ const GURL& target_url) OVERRIDE;
+ virtual void DidNavigate(
+ RenderFrameHostImpl* render_frame_host,
+ const FrameHostMsg_DidCommitProvisionalLoad_Params&
+ input_params) OVERRIDE;
+ virtual bool NavigateToPendingEntry(
+ RenderFrameHostImpl* render_frame_host,
+ NavigationController::ReloadType reload_type) OVERRIDE;
+ virtual base::TimeTicks GetCurrentLoadStart() OVERRIDE;
+ virtual void RequestOpenURL(RenderFrameHostImpl* render_frame_host,
+ const GURL& url,
+ const Referrer& referrer,
+ WindowOpenDisposition disposition,
+ bool should_replace_current_entry,
+ bool user_gesture) OVERRIDE;
+ virtual void RequestTransferURL(
+ RenderFrameHostImpl* render_frame_host,
+ const GURL& url,
+ const std::vector<GURL>& redirect_chain,
+ const Referrer& referrer,
+ PageTransition page_transition,
+ WindowOpenDisposition disposition,
+ const GlobalRequestID& transferred_global_request_id,
+ bool should_replace_current_entry,
+ bool user_gesture) OVERRIDE;
private:
virtual ~NavigatorImpl() {}
+ // Navigates to the given entry, which must be the pending entry. Private
+ // because all callers should use NavigateToPendingEntry.
+ bool NavigateToEntry(
+ RenderFrameHostImpl* render_frame_host,
+ const NavigationEntryImpl& entry,
+ NavigationController::ReloadType reload_type);
+
+ bool ShouldAssignSiteForURL(const GURL& url);
+
// The NavigationController that will keep track of session history for all
// RenderFrameHost objects using this NavigatorImpl.
// TODO(nasko): Move ownership of the NavigationController from
@@ -41,6 +89,9 @@ class CONTENT_EXPORT NavigatorImpl : public Navigator {
// events. Can be NULL in tests.
NavigatorDelegate* delegate_;
+ // System time at which the current load was started.
+ base::TimeTicks current_load_start_;
+
DISALLOW_COPY_AND_ASSIGN(NavigatorImpl);
};
diff --git a/chromium/content/browser/frame_host/render_frame_host_delegate.cc b/chromium/content/browser/frame_host/render_frame_host_delegate.cc
index 14ed0ad4cb2..8e424ead131 100644
--- a/chromium/content/browser/frame_host/render_frame_host_delegate.cc
+++ b/chromium/content/browser/frame_host/render_frame_host_delegate.cc
@@ -2,7 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <stddef.h>
+
+#include "base/strings/string16.h"
#include "content/browser/frame_host/render_frame_host_delegate.h"
+#include "url/gurl.h"
namespace content {
@@ -12,4 +16,18 @@ bool RenderFrameHostDelegate::OnMessageReceived(
return false;
}
+const GURL& RenderFrameHostDelegate::GetMainFrameLastCommittedURL() const {
+ return GURL::EmptyGURL();
+}
+
+bool RenderFrameHostDelegate::AddMessageToConsole(
+ int32 level, const base::string16& message, int32 line_no,
+ const base::string16& source_id) {
+ return false;
+}
+
+WebContents* RenderFrameHostDelegate::GetAsWebContents() {
+ return NULL;
+}
+
} // namespace content
diff --git a/chromium/content/browser/frame_host/render_frame_host_delegate.h b/chromium/content/browser/frame_host/render_frame_host_delegate.h
index 1de47dff379..b4db5d4c9c9 100644
--- a/chromium/content/browser/frame_host/render_frame_host_delegate.h
+++ b/chromium/content/browser/frame_host/render_frame_host_delegate.h
@@ -5,7 +5,12 @@
#ifndef CONTENT_BROWSER_FRAME_HOST_RENDER_FRAME_HOST_DELEGATE_H_
#define CONTENT_BROWSER_FRAME_HOST_RENDER_FRAME_HOST_DELEGATE_H_
+#include "base/basictypes.h"
+#include "base/i18n/rtl.h"
#include "content/common/content_export.h"
+#include "content/public/common/javascript_message_type.h"
+
+class GURL;
namespace IPC {
class Message;
@@ -13,6 +18,8 @@ class Message;
namespace content {
class RenderFrameHost;
+class WebContents;
+struct ContextMenuParams;
// An interface implemented by an object interested in knowing about the state
// of the RenderFrameHost.
@@ -22,12 +29,81 @@ class CONTENT_EXPORT RenderFrameHostDelegate {
virtual bool OnMessageReceived(RenderFrameHost* render_frame_host,
const IPC::Message& message);
+ // Gets the last committed URL. See WebContents::GetLastCommittedURL for a
+ // description of the semantics.
+ virtual const GURL& GetMainFrameLastCommittedURL() const;
+
+ // A message was added to to the console.
+ virtual bool AddMessageToConsole(int32 level,
+ const base::string16& message,
+ int32 line_no,
+ const base::string16& source_id);
+
// Informs the delegate whenever a RenderFrameHost is created.
virtual void RenderFrameCreated(RenderFrameHost* render_frame_host) {}
// Informs the delegate whenever a RenderFrameHost is deleted.
virtual void RenderFrameDeleted(RenderFrameHost* render_frame_host) {}
+ // The top-level RenderFrame began loading a new page. This corresponds to
+ // Blink's notion of the throbber starting.
+ // |to_different_document| will be true unless the load is a fragment
+ // navigation, or triggered by history.pushState/replaceState.
+ virtual void DidStartLoading(RenderFrameHost* render_frame_host,
+ bool to_different_document) {}
+
+ // The RenderFrameHost has been swapped out.
+ virtual void SwappedOut(RenderFrameHost* render_frame_host) {}
+
+ // Notification that a worker process has crashed.
+ virtual void WorkerCrashed(RenderFrameHost* render_frame_host) {}
+
+ // A context menu should be shown, to be built using the context information
+ // provided in the supplied params.
+ virtual void ShowContextMenu(RenderFrameHost* render_frame_host,
+ const ContextMenuParams& params) {}
+
+ // A JavaScript message, confirmation or prompt should be shown.
+ virtual void RunJavaScriptMessage(RenderFrameHost* render_frame_host,
+ const base::string16& message,
+ const base::string16& default_prompt,
+ const GURL& frame_url,
+ JavaScriptMessageType type,
+ IPC::Message* reply_msg) {}
+
+ virtual void RunBeforeUnloadConfirm(RenderFrameHost* render_frame_host,
+ const base::string16& message,
+ bool is_reload,
+ IPC::Message* reply_msg) {}
+
+ // Another page accessed the top-level initial empty document, which means it
+ // is no longer safe to display a pending URL without risking a URL spoof.
+ virtual void DidAccessInitialDocument() {}
+
+ // The frame set its opener to null, disowning it for the lifetime of the
+ // window. Only called for the top-level frame.
+ virtual void DidDisownOpener(RenderFrameHost* render_frame_host) {}
+
+ // The onload handler in the frame has completed. Only called for the top-
+ // level frame.
+ virtual void DocumentOnLoadCompleted(RenderFrameHost* render_frame_host) {}
+
+ // The page's title was changed and should be updated. Only called for the
+ // top-level frame.
+ virtual void UpdateTitle(RenderFrameHost* render_frame_host,
+ int32 page_id,
+ const base::string16& title,
+ base::i18n::TextDirection title_direction) {}
+
+ // The page's encoding was changed and should be updated. Only called for the
+ // top-level frame.
+ virtual void UpdateEncoding(RenderFrameHost* render_frame_host,
+ const std::string& encoding) {}
+
+ // Return this object cast to a WebContents, if it is one. If the object is
+ // not a WebContents, returns NULL.
+ virtual WebContents* GetAsWebContents();
+
protected:
virtual ~RenderFrameHostDelegate() {}
};
diff --git a/chromium/content/browser/frame_host/render_frame_host_impl.cc b/chromium/content/browser/frame_host/render_frame_host_impl.cc
index 658e834230d..397fd915ea4 100644
--- a/chromium/content/browser/frame_host/render_frame_host_impl.cc
+++ b/chromium/content/browser/frame_host/render_frame_host_impl.cc
@@ -4,31 +4,136 @@
#include "content/browser/frame_host/render_frame_host_impl.h"
+#include "base/bind.h"
#include "base/containers/hash_tables.h"
#include "base/lazy_instance.h"
+#include "base/metrics/user_metrics_action.h"
+#include "content/browser/child_process_security_policy_impl.h"
+#include "content/browser/frame_host/cross_process_frame_connector.h"
+#include "content/browser/frame_host/cross_site_transferring_request.h"
#include "content/browser/frame_host/frame_tree.h"
#include "content/browser/frame_host/frame_tree_node.h"
#include "content/browser/frame_host/navigator.h"
#include "content/browser/frame_host/render_frame_host_delegate.h"
+#include "content/browser/frame_host/render_frame_proxy_host.h"
+#include "content/browser/renderer_host/input/input_router.h"
+#include "content/browser/renderer_host/input/timeout_monitor.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
+#include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "content/common/desktop_notification_messages.h"
#include "content/common/frame_messages.h"
+#include "content/common/input_messages.h"
+#include "content/common/inter_process_time_ticks_converter.h"
+#include "content/common/swapped_out_messages.h"
#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/desktop_notification_delegate.h"
#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/user_metrics.h"
+#include "content/public/common/content_constants.h"
+#include "content/public/common/url_constants.h"
+#include "content/public/common/url_utils.h"
#include "url/gurl.h"
+using base::TimeDelta;
+
namespace content {
+namespace {
+
// The (process id, routing id) pair that identifies one RenderFrame.
typedef std::pair<int32, int32> RenderFrameHostID;
typedef base::hash_map<RenderFrameHostID, RenderFrameHostImpl*>
RoutingIDFrameMap;
-static base::LazyInstance<RoutingIDFrameMap> g_routing_id_frame_map =
+base::LazyInstance<RoutingIDFrameMap> g_routing_id_frame_map =
LAZY_INSTANCE_INITIALIZER;
+class DesktopNotificationDelegateImpl : public DesktopNotificationDelegate {
+ public:
+ DesktopNotificationDelegateImpl(RenderFrameHost* render_frame_host,
+ int notification_id)
+ : render_process_id_(render_frame_host->GetProcess()->GetID()),
+ render_frame_id_(render_frame_host->GetRoutingID()),
+ notification_id_(notification_id) {}
+
+ virtual ~DesktopNotificationDelegateImpl() {}
+
+ virtual void NotificationDisplayed() OVERRIDE {
+ RenderFrameHost* rfh =
+ RenderFrameHost::FromID(render_process_id_, render_frame_id_);
+ if (!rfh)
+ return;
+
+ rfh->Send(new DesktopNotificationMsg_PostDisplay(
+ rfh->GetRoutingID(), notification_id_));
+ }
+
+ virtual void NotificationError() OVERRIDE {
+ RenderFrameHost* rfh =
+ RenderFrameHost::FromID(render_process_id_, render_frame_id_);
+ if (!rfh)
+ return;
+
+ rfh->Send(new DesktopNotificationMsg_PostError(
+ rfh->GetRoutingID(), notification_id_));
+ delete this;
+ }
+
+ virtual void NotificationClosed(bool by_user) OVERRIDE {
+ RenderFrameHost* rfh =
+ RenderFrameHost::FromID(render_process_id_, render_frame_id_);
+ if (!rfh)
+ return;
+
+ rfh->Send(new DesktopNotificationMsg_PostClose(
+ rfh->GetRoutingID(), notification_id_, by_user));
+ static_cast<RenderFrameHostImpl*>(rfh)->NotificationClosed(
+ notification_id_);
+ delete this;
+ }
+
+ virtual void NotificationClick() OVERRIDE {
+ RenderFrameHost* rfh =
+ RenderFrameHost::FromID(render_process_id_, render_frame_id_);
+ if (!rfh)
+ return;
+
+ rfh->Send(new DesktopNotificationMsg_PostClick(
+ rfh->GetRoutingID(), notification_id_));
+ }
+
+ private:
+ int render_process_id_;
+ int render_frame_id_;
+ int notification_id_;
+};
+
+// Translate a WebKit text direction into a base::i18n one.
+base::i18n::TextDirection WebTextDirectionToChromeTextDirection(
+ blink::WebTextDirection dir) {
+ switch (dir) {
+ case blink::WebTextDirectionLeftToRight:
+ return base::i18n::LEFT_TO_RIGHT;
+ case blink::WebTextDirectionRightToLeft:
+ return base::i18n::RIGHT_TO_LEFT;
+ default:
+ NOTREACHED();
+ return base::i18n::UNKNOWN_DIRECTION;
+ }
+}
+
+} // namespace
+
+RenderFrameHost* RenderFrameHost::FromID(int render_process_id,
+ int render_frame_id) {
+ return RenderFrameHostImpl::FromID(render_process_id, render_frame_id);
+}
+
// static
RenderFrameHostImpl* RenderFrameHostImpl::FromID(
int process_id, int routing_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
RoutingIDFrameMap* frames = g_routing_id_frame_map.Pointer();
RoutingIDFrameMap::iterator it = frames->find(
RenderFrameHostID(process_id, routing_id));
@@ -44,10 +149,14 @@ RenderFrameHostImpl::RenderFrameHostImpl(
bool is_swapped_out)
: render_view_host_(render_view_host),
delegate_(delegate),
+ cross_process_frame_connector_(NULL),
+ render_frame_proxy_host_(NULL),
frame_tree_(frame_tree),
frame_tree_node_(frame_tree_node),
routing_id_(routing_id),
- is_swapped_out_(is_swapped_out) {
+ is_swapped_out_(is_swapped_out),
+ weak_ptr_factory_(this) {
+ frame_tree_->RegisterRenderFrameHost(this);
GetProcess()->AddRoute(routing_id_, this);
g_routing_id_frame_map.Get().insert(std::make_pair(
RenderFrameHostID(GetProcess()->GetID(), routing_id_),
@@ -60,69 +169,743 @@ RenderFrameHostImpl::~RenderFrameHostImpl() {
RenderFrameHostID(GetProcess()->GetID(), routing_id_));
if (delegate_)
delegate_->RenderFrameDeleted(this);
+
+ // Notify the FrameTree that this RFH is going away, allowing it to shut down
+ // the corresponding RenderViewHost if it is no longer needed.
+ frame_tree_->UnregisterRenderFrameHost(this);
}
int RenderFrameHostImpl::GetRoutingID() {
return routing_id_;
}
+SiteInstance* RenderFrameHostImpl::GetSiteInstance() {
+ return render_view_host_->GetSiteInstance();
+}
+
+RenderProcessHost* RenderFrameHostImpl::GetProcess() {
+ // TODO(nasko): This should return its own process, once we have working
+ // cross-process navigation for subframes.
+ return render_view_host_->GetProcess();
+}
+
+RenderFrameHost* RenderFrameHostImpl::GetParent() {
+ FrameTreeNode* parent_node = frame_tree_node_->parent();
+ if (!parent_node)
+ return NULL;
+ return parent_node->current_frame_host();
+}
+
+const std::string& RenderFrameHostImpl::GetFrameName() {
+ return frame_tree_node_->frame_name();
+}
+
+bool RenderFrameHostImpl::IsCrossProcessSubframe() {
+ FrameTreeNode* parent_node = frame_tree_node_->parent();
+ if (!parent_node)
+ return false;
+ return GetSiteInstance() !=
+ parent_node->current_frame_host()->GetSiteInstance();
+}
+
+GURL RenderFrameHostImpl::GetLastCommittedURL() {
+ return frame_tree_node_->current_url();
+}
+
+gfx::NativeView RenderFrameHostImpl::GetNativeView() {
+ RenderWidgetHostView* view = render_view_host_->GetView();
+ if (!view)
+ return NULL;
+ return view->GetNativeView();
+}
+
+void RenderFrameHostImpl::ExecuteJavaScript(
+ const base::string16& javascript) {
+ Send(new FrameMsg_JavaScriptExecuteRequest(routing_id_,
+ javascript,
+ 0, false));
+}
+
+void RenderFrameHostImpl::ExecuteJavaScript(
+ const base::string16& javascript,
+ const JavaScriptResultCallback& callback) {
+ static int next_id = 1;
+ int key = next_id++;
+ Send(new FrameMsg_JavaScriptExecuteRequest(routing_id_,
+ javascript,
+ key, true));
+ javascript_callbacks_.insert(std::make_pair(key, callback));
+}
+
+RenderViewHost* RenderFrameHostImpl::GetRenderViewHost() {
+ return render_view_host_;
+}
+
bool RenderFrameHostImpl::Send(IPC::Message* message) {
+ if (IPC_MESSAGE_ID_CLASS(message->type()) == InputMsgStart) {
+ return render_view_host_->input_router()->SendInput(
+ make_scoped_ptr(message));
+ }
+
+ if (render_view_host_->IsSwappedOut()) {
+ DCHECK(render_frame_proxy_host_);
+ return render_frame_proxy_host_->Send(message);
+ }
+
return GetProcess()->Send(message);
}
bool RenderFrameHostImpl::OnMessageReceived(const IPC::Message &msg) {
+ // Filter out most IPC messages if this renderer is swapped out.
+ // We still want to handle certain ACKs to keep our state consistent.
+ // TODO(nasko): Only check RenderViewHost state, as this object's own state
+ // isn't yet properly updated. Transition this check once the swapped out
+ // state is correct in RenderFrameHost itself.
+ if (render_view_host_->IsSwappedOut()) {
+ if (!SwappedOutMessages::CanHandleWhileSwappedOut(msg)) {
+ // If this is a synchronous message and we decided not to handle it,
+ // we must send an error reply, or else the renderer will be stuck
+ // and won't respond to future requests.
+ if (msg.is_sync()) {
+ IPC::Message* reply = IPC::SyncMessage::GenerateReply(&msg);
+ reply->set_reply_error();
+ Send(reply);
+ }
+ // Don't continue looking for someone to handle it.
+ return true;
+ }
+ }
+
if (delegate_->OnMessageReceived(this, msg))
return true;
+ RenderFrameProxyHost* proxy =
+ frame_tree_node_->render_manager()->GetProxyToParent();
+ if (proxy && proxy->cross_process_frame_connector() &&
+ proxy->cross_process_frame_connector()->OnMessageReceived(msg))
+ return true;
+
bool handled = true;
- bool msg_is_ok = true;
- IPC_BEGIN_MESSAGE_MAP_EX(RenderFrameHostImpl, msg, msg_is_ok)
+ IPC_BEGIN_MESSAGE_MAP(RenderFrameHostImpl, msg)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_AddMessageToConsole, OnAddMessageToConsole)
IPC_MESSAGE_HANDLER(FrameHostMsg_Detach, OnDetach)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_FrameFocused, OnFrameFocused)
IPC_MESSAGE_HANDLER(FrameHostMsg_DidStartProvisionalLoadForFrame,
OnDidStartProvisionalLoadForFrame)
- IPC_END_MESSAGE_MAP_EX()
-
- if (!msg_is_ok) {
- // The message had a handler, but its de-serialization failed.
- // Kill the renderer.
- RecordAction(UserMetricsAction("BadMessageTerminate_RFH"));
- GetProcess()->ReceivedBadMessage();
- }
+ IPC_MESSAGE_HANDLER(FrameHostMsg_DidFailProvisionalLoadWithError,
+ OnDidFailProvisionalLoadWithError)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_DidRedirectProvisionalLoad,
+ OnDidRedirectProvisionalLoad)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_DidFailLoadWithError,
+ OnDidFailLoadWithError)
+ IPC_MESSAGE_HANDLER_GENERIC(FrameHostMsg_DidCommitProvisionalLoad,
+ OnNavigate(msg))
+ IPC_MESSAGE_HANDLER(FrameHostMsg_OpenURL, OnOpenURL)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_DocumentOnLoadCompleted,
+ OnDocumentOnLoadCompleted)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_BeforeUnload_ACK, OnBeforeUnloadACK)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_SwapOut_ACK, OnSwapOutACK)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_ContextMenu, OnContextMenu)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_JavaScriptExecuteResponse,
+ OnJavaScriptExecuteResponse)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(FrameHostMsg_RunJavaScriptMessage,
+ OnRunJavaScriptMessage)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(FrameHostMsg_RunBeforeUnloadConfirm,
+ OnRunBeforeUnloadConfirm)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_DidAccessInitialDocument,
+ OnDidAccessInitialDocument)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_DidDisownOpener, OnDidDisownOpener)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_UpdateTitle, OnUpdateTitle)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_UpdateEncoding, OnUpdateEncoding)
+ IPC_MESSAGE_HANDLER(DesktopNotificationHostMsg_RequestPermission,
+ OnRequestDesktopNotificationPermission)
+ IPC_MESSAGE_HANDLER(DesktopNotificationHostMsg_Show,
+ OnShowDesktopNotification)
+ IPC_MESSAGE_HANDLER(DesktopNotificationHostMsg_Cancel,
+ OnCancelDesktopNotification)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_TextSurroundingSelectionResponse,
+ OnTextSurroundingSelectionResponse)
+ IPC_END_MESSAGE_MAP()
return handled;
}
void RenderFrameHostImpl::Init() {
- GetProcess()->ResumeRequestsForView(routing_id());
+ GetProcess()->ResumeRequestsForView(routing_id_);
}
-RenderProcessHost* RenderFrameHostImpl::GetProcess() const {
- // TODO(nasko): This should return its own process, once we have working
- // cross-process navigation for subframes.
- return render_view_host_->GetProcess();
+void RenderFrameHostImpl::OnAddMessageToConsole(
+ int32 level,
+ const base::string16& message,
+ int32 line_no,
+ const base::string16& source_id) {
+ if (delegate_->AddMessageToConsole(level, message, line_no, source_id))
+ return;
+
+ // Pass through log level only on WebUI pages to limit console spew.
+ int32 resolved_level =
+ HasWebUIScheme(delegate_->GetMainFrameLastCommittedURL()) ? level : 0;
+
+ if (resolved_level >= ::logging::GetMinLogLevel()) {
+ logging::LogMessage("CONSOLE", line_no, resolved_level).stream() << "\"" <<
+ message << "\", source: " << source_id << " (" << line_no << ")";
+ }
}
-void RenderFrameHostImpl::OnCreateChildFrame(int new_frame_routing_id,
- int64 parent_frame_id,
- int64 frame_id,
+void RenderFrameHostImpl::OnCreateChildFrame(int new_routing_id,
const std::string& frame_name) {
RenderFrameHostImpl* new_frame = frame_tree_->AddFrame(
- new_frame_routing_id, parent_frame_id, frame_id, frame_name);
+ frame_tree_node_, new_routing_id, frame_name);
if (delegate_)
delegate_->RenderFrameCreated(new_frame);
}
-void RenderFrameHostImpl::OnDetach(int64 parent_frame_id, int64 frame_id) {
- frame_tree_->RemoveFrame(this, parent_frame_id, frame_id);
+void RenderFrameHostImpl::OnDetach() {
+ frame_tree_->RemoveFrame(frame_tree_node_);
+}
+
+void RenderFrameHostImpl::OnFrameFocused() {
+ frame_tree_->SetFocusedFrame(frame_tree_node_);
+}
+
+void RenderFrameHostImpl::OnOpenURL(
+ const FrameHostMsg_OpenURL_Params& params) {
+ GURL validated_url(params.url);
+ GetProcess()->FilterURL(false, &validated_url);
+
+ frame_tree_node_->navigator()->RequestOpenURL(
+ this, validated_url, params.referrer, params.disposition,
+ params.should_replace_current_entry, params.user_gesture);
+}
+
+void RenderFrameHostImpl::OnDocumentOnLoadCompleted() {
+ // This message is only sent for top-level frames. TODO(avi): when frame tree
+ // mirroring works correctly, add a check here to enforce it.
+ delegate_->DocumentOnLoadCompleted(this);
}
void RenderFrameHostImpl::OnDidStartProvisionalLoadForFrame(
- int64 frame_id,
- int64 parent_frame_id,
- bool is_main_frame,
+ int parent_routing_id,
const GURL& url) {
frame_tree_node_->navigator()->DidStartProvisionalLoad(
- this, frame_id, parent_frame_id, is_main_frame, url);
+ this, parent_routing_id, url);
+}
+
+void RenderFrameHostImpl::OnDidFailProvisionalLoadWithError(
+ const FrameHostMsg_DidFailProvisionalLoadWithError_Params& params) {
+ frame_tree_node_->navigator()->DidFailProvisionalLoadWithError(this, params);
+}
+
+void RenderFrameHostImpl::OnDidFailLoadWithError(
+ const GURL& url,
+ int error_code,
+ const base::string16& error_description) {
+ GURL validated_url(url);
+ GetProcess()->FilterURL(false, &validated_url);
+
+ frame_tree_node_->navigator()->DidFailLoadWithError(
+ this, validated_url, error_code, error_description);
+}
+
+void RenderFrameHostImpl::OnDidRedirectProvisionalLoad(
+ int32 page_id,
+ const GURL& source_url,
+ const GURL& target_url) {
+ frame_tree_node_->navigator()->DidRedirectProvisionalLoad(
+ this, page_id, source_url, target_url);
+}
+
+// Called when the renderer navigates. For every frame loaded, we'll get this
+// notification containing parameters identifying the navigation.
+//
+// Subframes are identified by the page transition type. For subframes loaded
+// as part of a wider page load, the page_id will be the same as for the top
+// level frame. If the user explicitly requests a subframe navigation, we will
+// get a new page_id because we need to create a new navigation entry for that
+// action.
+void RenderFrameHostImpl::OnNavigate(const IPC::Message& msg) {
+ // Read the parameters out of the IPC message directly to avoid making another
+ // copy when we filter the URLs.
+ PickleIterator iter(msg);
+ FrameHostMsg_DidCommitProvisionalLoad_Params validated_params;
+ if (!IPC::ParamTraits<FrameHostMsg_DidCommitProvisionalLoad_Params>::
+ Read(&msg, &iter, &validated_params))
+ return;
+
+ // If we're waiting for a cross-site beforeunload ack from this renderer and
+ // we receive a Navigate message from the main frame, then the renderer was
+ // navigating already and sent it before hearing the ViewMsg_Stop message.
+ // We do not want to cancel the pending navigation in this case, since the
+ // old page will soon be stopped. Instead, treat this as a beforeunload ack
+ // to allow the pending navigation to continue.
+ if (render_view_host_->is_waiting_for_beforeunload_ack_ &&
+ render_view_host_->unload_ack_is_for_cross_site_transition_ &&
+ PageTransitionIsMainFrame(validated_params.transition)) {
+ OnBeforeUnloadACK(true, send_before_unload_start_time_,
+ base::TimeTicks::Now());
+ return;
+ }
+
+ // If we're waiting for an unload ack from this renderer and we receive a
+ // Navigate message, then the renderer was navigating before it received the
+ // unload request. It will either respond to the unload request soon or our
+ // timer will expire. Either way, we should ignore this message, because we
+ // have already committed to closing this renderer.
+ if (render_view_host_->IsWaitingForUnloadACK())
+ return;
+
+ RenderProcessHost* process = GetProcess();
+
+ // Attempts to commit certain off-limits URL should be caught more strictly
+ // than our FilterURL checks below. If a renderer violates this policy, it
+ // should be killed.
+ if (!CanCommitURL(validated_params.url)) {
+ VLOG(1) << "Blocked URL " << validated_params.url.spec();
+ validated_params.url = GURL(url::kAboutBlankURL);
+ RecordAction(base::UserMetricsAction("CanCommitURL_BlockedAndKilled"));
+ // Kills the process.
+ process->ReceivedBadMessage();
+ }
+
+ // Without this check, an evil renderer can trick the browser into creating
+ // a navigation entry for a banned URL. If the user clicks the back button
+ // followed by the forward button (or clicks reload, or round-trips through
+ // session restore, etc), we'll think that the browser commanded the
+ // renderer to load the URL and grant the renderer the privileges to request
+ // the URL. To prevent this attack, we block the renderer from inserting
+ // banned URLs into the navigation controller in the first place.
+ process->FilterURL(false, &validated_params.url);
+ process->FilterURL(true, &validated_params.referrer.url);
+ for (std::vector<GURL>::iterator it(validated_params.redirects.begin());
+ it != validated_params.redirects.end(); ++it) {
+ process->FilterURL(false, &(*it));
+ }
+ process->FilterURL(true, &validated_params.searchable_form_url);
+
+ // Without this check, the renderer can trick the browser into using
+ // filenames it can't access in a future session restore.
+ if (!render_view_host_->CanAccessFilesOfPageState(
+ validated_params.page_state)) {
+ GetProcess()->ReceivedBadMessage();
+ return;
+ }
+
+ frame_tree_node()->navigator()->DidNavigate(this, validated_params);
+}
+
+RenderWidgetHostImpl* RenderFrameHostImpl::GetRenderWidgetHost() {
+ return static_cast<RenderWidgetHostImpl*>(render_view_host_);
+}
+
+int RenderFrameHostImpl::GetEnabledBindings() {
+ return render_view_host_->GetEnabledBindings();
+}
+
+void RenderFrameHostImpl::OnCrossSiteResponse(
+ const GlobalRequestID& global_request_id,
+ scoped_ptr<CrossSiteTransferringRequest> cross_site_transferring_request,
+ const std::vector<GURL>& transfer_url_chain,
+ const Referrer& referrer,
+ PageTransition page_transition,
+ bool should_replace_current_entry) {
+ frame_tree_node_->render_manager()->OnCrossSiteResponse(
+ this, global_request_id, cross_site_transferring_request.Pass(),
+ transfer_url_chain, referrer, page_transition,
+ should_replace_current_entry);
+}
+
+void RenderFrameHostImpl::SwapOut(RenderFrameProxyHost* proxy) {
+ // TODO(creis): Move swapped out state to RFH. Until then, only update it
+ // when swapping out the main frame.
+ if (!GetParent()) {
+ // If this RenderViewHost is not in the default state, it must have already
+ // gone through this, therefore just return.
+ if (render_view_host_->rvh_state_ != RenderViewHostImpl::STATE_DEFAULT)
+ return;
+
+ render_view_host_->SetState(
+ RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK);
+ render_view_host_->unload_event_monitor_timeout_->Start(
+ base::TimeDelta::FromMilliseconds(
+ RenderViewHostImpl::kUnloadTimeoutMS));
+ }
+
+ set_render_frame_proxy_host(proxy);
+
+ if (render_view_host_->IsRenderViewLive())
+ Send(new FrameMsg_SwapOut(routing_id_, proxy->GetRoutingID()));
+
+ if (!GetParent())
+ delegate_->SwappedOut(this);
+
+ // Allow the navigation to proceed.
+ frame_tree_node_->render_manager()->SwappedOut(this);
+}
+
+void RenderFrameHostImpl::OnBeforeUnloadACK(
+ bool proceed,
+ const base::TimeTicks& renderer_before_unload_start_time,
+ const base::TimeTicks& renderer_before_unload_end_time) {
+ // TODO(creis): Support properly beforeunload on subframes. For now just
+ // pretend that the handler ran and allowed the navigation to proceed.
+ if (GetParent()) {
+ render_view_host_->is_waiting_for_beforeunload_ack_ = false;
+ frame_tree_node_->render_manager()->OnBeforeUnloadACK(
+ render_view_host_->unload_ack_is_for_cross_site_transition_, proceed,
+ renderer_before_unload_end_time);
+ return;
+ }
+
+ render_view_host_->decrement_in_flight_event_count();
+ render_view_host_->StopHangMonitorTimeout();
+ // If this renderer navigated while the beforeunload request was in flight, we
+ // may have cleared this state in OnNavigate, in which case we can ignore
+ // this message.
+ // However renderer might also be swapped out but we still want to proceed
+ // with navigation, otherwise it would block future navigations. This can
+ // happen when pending cross-site navigation is canceled by a second one just
+ // before OnNavigate while current RVH is waiting for commit but second
+ // navigation is started from the beginning.
+ if (!render_view_host_->is_waiting_for_beforeunload_ack_) {
+ return;
+ }
+
+ render_view_host_->is_waiting_for_beforeunload_ack_ = false;
+
+ base::TimeTicks before_unload_end_time;
+ if (!send_before_unload_start_time_.is_null() &&
+ !renderer_before_unload_start_time.is_null() &&
+ !renderer_before_unload_end_time.is_null()) {
+ // When passing TimeTicks across process boundaries, we need to compensate
+ // for any skew between the processes. Here we are converting the
+ // renderer's notion of before_unload_end_time to TimeTicks in the browser
+ // process. See comments in inter_process_time_ticks_converter.h for more.
+ InterProcessTimeTicksConverter converter(
+ LocalTimeTicks::FromTimeTicks(send_before_unload_start_time_),
+ LocalTimeTicks::FromTimeTicks(base::TimeTicks::Now()),
+ RemoteTimeTicks::FromTimeTicks(renderer_before_unload_start_time),
+ RemoteTimeTicks::FromTimeTicks(renderer_before_unload_end_time));
+ LocalTimeTicks browser_before_unload_end_time =
+ converter.ToLocalTimeTicks(
+ RemoteTimeTicks::FromTimeTicks(renderer_before_unload_end_time));
+ before_unload_end_time = browser_before_unload_end_time.ToTimeTicks();
+ }
+ frame_tree_node_->render_manager()->OnBeforeUnloadACK(
+ render_view_host_->unload_ack_is_for_cross_site_transition_, proceed,
+ before_unload_end_time);
+
+ // If canceled, notify the delegate to cancel its pending navigation entry.
+ if (!proceed)
+ render_view_host_->GetDelegate()->DidCancelLoading();
+}
+
+void RenderFrameHostImpl::OnSwapOutACK() {
+ OnSwappedOut(false);
+}
+
+void RenderFrameHostImpl::OnSwappedOut(bool timed_out) {
+ // For now, we only need to update the RVH state machine for top-level swaps.
+ // Subframe swaps (in --site-per-process) can just continue via RFHM.
+ if (!GetParent())
+ render_view_host_->OnSwappedOut(timed_out);
+ else
+ frame_tree_node_->render_manager()->SwappedOut(this);
+}
+
+void RenderFrameHostImpl::OnContextMenu(const ContextMenuParams& params) {
+ // Validate the URLs in |params|. If the renderer can't request the URLs
+ // directly, don't show them in the context menu.
+ ContextMenuParams validated_params(params);
+ RenderProcessHost* process = GetProcess();
+
+ // We don't validate |unfiltered_link_url| so that this field can be used
+ // when users want to copy the original link URL.
+ process->FilterURL(true, &validated_params.link_url);
+ process->FilterURL(true, &validated_params.src_url);
+ process->FilterURL(false, &validated_params.page_url);
+ process->FilterURL(true, &validated_params.frame_url);
+
+ delegate_->ShowContextMenu(this, validated_params);
+}
+
+void RenderFrameHostImpl::OnJavaScriptExecuteResponse(
+ int id, const base::ListValue& result) {
+ const base::Value* result_value;
+ if (!result.Get(0, &result_value)) {
+ // Programming error or rogue renderer.
+ NOTREACHED() << "Got bad arguments for OnJavaScriptExecuteResponse";
+ return;
+ }
+
+ std::map<int, JavaScriptResultCallback>::iterator it =
+ javascript_callbacks_.find(id);
+ if (it != javascript_callbacks_.end()) {
+ it->second.Run(result_value);
+ javascript_callbacks_.erase(it);
+ } else {
+ NOTREACHED() << "Received script response for unknown request";
+ }
+}
+
+void RenderFrameHostImpl::OnRunJavaScriptMessage(
+ const base::string16& message,
+ const base::string16& default_prompt,
+ const GURL& frame_url,
+ JavaScriptMessageType type,
+ IPC::Message* reply_msg) {
+ // While a JS message dialog is showing, tabs in the same process shouldn't
+ // process input events.
+ GetProcess()->SetIgnoreInputEvents(true);
+ render_view_host_->StopHangMonitorTimeout();
+ delegate_->RunJavaScriptMessage(this, message, default_prompt,
+ frame_url, type, reply_msg);
+}
+
+void RenderFrameHostImpl::OnRunBeforeUnloadConfirm(
+ const GURL& frame_url,
+ const base::string16& message,
+ bool is_reload,
+ IPC::Message* reply_msg) {
+ // While a JS before unload dialog is showing, tabs in the same process
+ // shouldn't process input events.
+ GetProcess()->SetIgnoreInputEvents(true);
+ render_view_host_->StopHangMonitorTimeout();
+ delegate_->RunBeforeUnloadConfirm(this, message, is_reload, reply_msg);
+}
+
+void RenderFrameHostImpl::OnRequestDesktopNotificationPermission(
+ const GURL& source_origin, int callback_context) {
+ base::Closure done_callback = base::Bind(
+ &RenderFrameHostImpl::DesktopNotificationPermissionRequestDone,
+ weak_ptr_factory_.GetWeakPtr(), callback_context);
+ GetContentClient()->browser()->RequestDesktopNotificationPermission(
+ source_origin, this, done_callback);
+}
+
+void RenderFrameHostImpl::OnShowDesktopNotification(
+ int notification_id,
+ const ShowDesktopNotificationHostMsgParams& params) {
+ base::Closure cancel_callback;
+ GetContentClient()->browser()->ShowDesktopNotification(
+ params, this,
+ new DesktopNotificationDelegateImpl(this, notification_id),
+ &cancel_callback);
+ cancel_notification_callbacks_[notification_id] = cancel_callback;
+}
+
+void RenderFrameHostImpl::OnCancelDesktopNotification(int notification_id) {
+ if (!cancel_notification_callbacks_.count(notification_id)) {
+ NOTREACHED();
+ return;
+ }
+ cancel_notification_callbacks_[notification_id].Run();
+ cancel_notification_callbacks_.erase(notification_id);
+}
+
+void RenderFrameHostImpl::OnTextSurroundingSelectionResponse(
+ const base::string16& content,
+ size_t start_offset,
+ size_t end_offset) {
+ render_view_host_->OnTextSurroundingSelectionResponse(
+ content, start_offset, end_offset);
+}
+
+void RenderFrameHostImpl::OnDidAccessInitialDocument() {
+ delegate_->DidAccessInitialDocument();
+}
+
+void RenderFrameHostImpl::OnDidDisownOpener() {
+ // This message is only sent for top-level frames. TODO(avi): when frame tree
+ // mirroring works correctly, add a check here to enforce it.
+ delegate_->DidDisownOpener(this);
+}
+
+void RenderFrameHostImpl::OnUpdateTitle(
+ int32 page_id,
+ const base::string16& title,
+ blink::WebTextDirection title_direction) {
+ // This message is only sent for top-level frames. TODO(avi): when frame tree
+ // mirroring works correctly, add a check here to enforce it.
+ if (title.length() > kMaxTitleChars) {
+ NOTREACHED() << "Renderer sent too many characters in title.";
+ return;
+ }
+
+ delegate_->UpdateTitle(this, page_id, title,
+ WebTextDirectionToChromeTextDirection(
+ title_direction));
+}
+
+void RenderFrameHostImpl::OnUpdateEncoding(const std::string& encoding_name) {
+ // This message is only sent for top-level frames. TODO(avi): when frame tree
+ // mirroring works correctly, add a check here to enforce it.
+ delegate_->UpdateEncoding(this, encoding_name);
+}
+
+void RenderFrameHostImpl::SetPendingShutdown(const base::Closure& on_swap_out) {
+ render_view_host_->SetPendingShutdown(on_swap_out);
+}
+
+bool RenderFrameHostImpl::CanCommitURL(const GURL& url) {
+ // TODO(creis): We should also check for WebUI pages here. Also, when the
+ // out-of-process iframes implementation is ready, we should check for
+ // cross-site URLs that are not allowed to commit in this process.
+
+ // Give the client a chance to disallow URLs from committing.
+ return GetContentClient()->browser()->CanCommitURL(GetProcess(), url);
+}
+
+void RenderFrameHostImpl::Navigate(const FrameMsg_Navigate_Params& params) {
+ TRACE_EVENT0("frame_host", "RenderFrameHostImpl::Navigate");
+ // Browser plugin guests are not allowed to navigate outside web-safe schemes,
+ // so do not grant them the ability to request additional URLs.
+ if (!GetProcess()->IsIsolatedGuest()) {
+ ChildProcessSecurityPolicyImpl::GetInstance()->GrantRequestURL(
+ GetProcess()->GetID(), params.url);
+ if (params.url.SchemeIs(url::kDataScheme) &&
+ params.base_url_for_data_url.SchemeIs(url::kFileScheme)) {
+ // If 'data:' is used, and we have a 'file:' base url, grant access to
+ // local files.
+ ChildProcessSecurityPolicyImpl::GetInstance()->GrantRequestURL(
+ GetProcess()->GetID(), params.base_url_for_data_url);
+ }
+ }
+
+ // Only send the message if we aren't suspended at the start of a cross-site
+ // request.
+ if (render_view_host_->navigations_suspended_) {
+ // Shouldn't be possible to have a second navigation while suspended, since
+ // navigations will only be suspended during a cross-site request. If a
+ // second navigation occurs, RenderFrameHostManager will cancel this pending
+ // RFH and create a new pending RFH.
+ DCHECK(!render_view_host_->suspended_nav_params_.get());
+ render_view_host_->suspended_nav_params_.reset(
+ new FrameMsg_Navigate_Params(params));
+ } else {
+ // Get back to a clean state, in case we start a new navigation without
+ // completing a RVH swap or unload handler.
+ render_view_host_->SetState(RenderViewHostImpl::STATE_DEFAULT);
+
+ Send(new FrameMsg_Navigate(routing_id_, params));
+ }
+
+ // Force the throbber to start. We do this because Blink's "started
+ // loading" message will be received asynchronously from the UI of the
+ // browser. But we want to keep the throbber in sync with what's happening
+ // in the UI. For example, we want to start throbbing immediately when the
+ // user naivgates even if the renderer is delayed. There is also an issue
+ // with the throbber starting because the WebUI (which controls whether the
+ // favicon is displayed) happens synchronously. If the start loading
+ // messages was asynchronous, then the default favicon would flash in.
+ //
+ // Blink doesn't send throb notifications for JavaScript URLs, so we
+ // don't want to either.
+ if (!params.url.SchemeIs(url::kJavaScriptScheme))
+ delegate_->DidStartLoading(this, true);
+}
+
+void RenderFrameHostImpl::NavigateToURL(const GURL& url) {
+ FrameMsg_Navigate_Params params;
+ params.page_id = -1;
+ params.pending_history_list_offset = -1;
+ params.current_history_list_offset = -1;
+ params.current_history_list_length = 0;
+ params.url = url;
+ params.transition = PAGE_TRANSITION_LINK;
+ params.navigation_type = FrameMsg_Navigate_Type::NORMAL;
+ Navigate(params);
+}
+
+void RenderFrameHostImpl::DispatchBeforeUnload(bool for_cross_site_transition) {
+ // TODO(creis): Support subframes.
+ if (!render_view_host_->IsRenderViewLive() || GetParent()) {
+ // We don't have a live renderer, so just skip running beforeunload.
+ render_view_host_->is_waiting_for_beforeunload_ack_ = true;
+ render_view_host_->unload_ack_is_for_cross_site_transition_ =
+ for_cross_site_transition;
+ base::TimeTicks now = base::TimeTicks::Now();
+ OnBeforeUnloadACK(true, now, now);
+ return;
+ }
+
+ // This may be called more than once (if the user clicks the tab close button
+ // several times, or if she clicks the tab close button then the browser close
+ // button), and we only send the message once.
+ if (render_view_host_->is_waiting_for_beforeunload_ack_) {
+ // Some of our close messages could be for the tab, others for cross-site
+ // transitions. We always want to think it's for closing the tab if any
+ // of the messages were, since otherwise it might be impossible to close
+ // (if there was a cross-site "close" request pending when the user clicked
+ // the close button). We want to keep the "for cross site" flag only if
+ // both the old and the new ones are also for cross site.
+ render_view_host_->unload_ack_is_for_cross_site_transition_ =
+ render_view_host_->unload_ack_is_for_cross_site_transition_ &&
+ for_cross_site_transition;
+ } else {
+ // Start the hang monitor in case the renderer hangs in the beforeunload
+ // handler.
+ render_view_host_->is_waiting_for_beforeunload_ack_ = true;
+ render_view_host_->unload_ack_is_for_cross_site_transition_ =
+ for_cross_site_transition;
+ // Increment the in-flight event count, to ensure that input events won't
+ // cancel the timeout timer.
+ render_view_host_->increment_in_flight_event_count();
+ render_view_host_->StartHangMonitorTimeout(
+ TimeDelta::FromMilliseconds(RenderViewHostImpl::kUnloadTimeoutMS));
+ send_before_unload_start_time_ = base::TimeTicks::Now();
+ Send(new FrameMsg_BeforeUnload(routing_id_));
+ }
+}
+
+void RenderFrameHostImpl::ExtendSelectionAndDelete(size_t before,
+ size_t after) {
+ Send(new FrameMsg_ExtendSelectionAndDelete(routing_id_, before, after));
+}
+
+void RenderFrameHostImpl::JavaScriptDialogClosed(
+ IPC::Message* reply_msg,
+ bool success,
+ const base::string16& user_input,
+ bool dialog_was_suppressed) {
+ GetProcess()->SetIgnoreInputEvents(false);
+ bool is_waiting = render_view_host_->is_waiting_for_beforeunload_ack() ||
+ render_view_host_->IsWaitingForUnloadACK();
+
+ // If we are executing as part of (before)unload event handling, we don't
+ // want to use the regular hung_renderer_delay_ms_ if the user has agreed to
+ // leave the current page. In this case, use the regular timeout value used
+ // during the (before)unload handling.
+ if (is_waiting) {
+ render_view_host_->StartHangMonitorTimeout(TimeDelta::FromMilliseconds(
+ success ? RenderViewHostImpl::kUnloadTimeoutMS
+ : render_view_host_->hung_renderer_delay_ms_));
+ }
+
+ FrameHostMsg_RunJavaScriptMessage::WriteReplyParams(reply_msg,
+ success, user_input);
+ Send(reply_msg);
+
+ // If we are waiting for an unload or beforeunload ack and the user has
+ // suppressed messages, kill the tab immediately; a page that's spamming
+ // alerts in onbeforeunload is presumably malicious, so there's no point in
+ // continuing to run its script and dragging out the process.
+ // This must be done after sending the reply since RenderView can't close
+ // correctly while waiting for a response.
+ if (is_waiting && dialog_was_suppressed)
+ render_view_host_->delegate_->RendererUnresponsive(
+ render_view_host_,
+ render_view_host_->is_waiting_for_beforeunload_ack(),
+ render_view_host_->IsWaitingForUnloadACK());
+}
+
+void RenderFrameHostImpl::NotificationClosed(int notification_id) {
+ cancel_notification_callbacks_.erase(notification_id);
+}
+
+void RenderFrameHostImpl::DesktopNotificationPermissionRequestDone(
+ int callback_context) {
+ Send(new DesktopNotificationMsg_PermissionRequestDone(
+ routing_id_, callback_context));
}
} // namespace content
diff --git a/chromium/content/browser/frame_host/render_frame_host_impl.h b/chromium/content/browser/frame_host/render_frame_host_impl.h
index 2822724adfa..854a14fb39e 100644
--- a/chromium/content/browser/frame_host/render_frame_host_impl.h
+++ b/chromium/content/browser/frame_host/render_frame_host_impl.h
@@ -5,25 +5,45 @@
#ifndef CONTENT_BROWSER_FRAME_HOST_RENDER_FRAME_HOST_IMPL_H_
#define CONTENT_BROWSER_FRAME_HOST_RENDER_FRAME_HOST_IMPL_H_
-#include <string>
+#include <map>
+#include <vector>
+#include "base/callback.h"
#include "base/compiler_specific.h"
+#include "base/memory/weak_ptr.h"
+#include "base/strings/string16.h"
+#include "base/time/time.h"
#include "content/common/content_export.h"
#include "content/public/browser/render_frame_host.h"
+#include "content/public/common/javascript_message_type.h"
+#include "content/public/common/page_transition_types.h"
+#include "third_party/WebKit/public/web/WebTextDirection.h"
class GURL;
+struct FrameHostMsg_DidFailProvisionalLoadWithError_Params;
+struct FrameHostMsg_OpenURL_Params;
+struct FrameMsg_Navigate_Params;
namespace base {
class FilePath;
+class ListValue;
}
namespace content {
+class CrossProcessFrameConnector;
+class CrossSiteTransferringRequest;
class FrameTree;
class FrameTreeNode;
class RenderFrameHostDelegate;
+class RenderFrameProxyHost;
class RenderProcessHost;
class RenderViewHostImpl;
+class RenderWidgetHostImpl;
+struct ContextMenuParams;
+struct GlobalRequestID;
+struct Referrer;
+struct ShowDesktopNotificationHostMsgParams;
class CONTENT_EXPORT RenderFrameHostImpl : public RenderFrameHost {
public:
@@ -33,6 +53,19 @@ class CONTENT_EXPORT RenderFrameHostImpl : public RenderFrameHost {
// RenderFrameHost
virtual int GetRoutingID() OVERRIDE;
+ virtual SiteInstance* GetSiteInstance() OVERRIDE;
+ virtual RenderProcessHost* GetProcess() OVERRIDE;
+ virtual RenderFrameHost* GetParent() OVERRIDE;
+ virtual const std::string& GetFrameName() OVERRIDE;
+ virtual bool IsCrossProcessSubframe() OVERRIDE;
+ virtual GURL GetLastCommittedURL() OVERRIDE;
+ virtual gfx::NativeView GetNativeView() OVERRIDE;
+ virtual void ExecuteJavaScript(
+ const base::string16& javascript) OVERRIDE;
+ virtual void ExecuteJavaScript(
+ const base::string16& javascript,
+ const JavaScriptResultCallback& callback) OVERRIDE;
+ virtual RenderViewHost* GetRenderViewHost() OVERRIDE;
// IPC::Sender
virtual bool Send(IPC::Message* msg) OVERRIDE;
@@ -41,15 +74,98 @@ class CONTENT_EXPORT RenderFrameHostImpl : public RenderFrameHost {
virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE;
void Init();
- RenderProcessHost* GetProcess() const;
int routing_id() const { return routing_id_; }
- void OnCreateChildFrame(int new_frame_routing_id,
- int64 parent_frame_id,
- int64 frame_id,
+ void OnCreateChildFrame(int new_routing_id,
const std::string& frame_name);
RenderViewHostImpl* render_view_host() { return render_view_host_; }
RenderFrameHostDelegate* delegate() { return delegate_; }
+ FrameTreeNode* frame_tree_node() { return frame_tree_node_; }
+ // TODO(nasko): The RenderWidgetHost will be owned by RenderFrameHost in
+ // the future, so update this accessor to return the right pointer.
+ RenderWidgetHostImpl* GetRenderWidgetHost();
+
+ // This function is called when this is a swapped out RenderFrameHost that
+ // lives in the same process as the parent frame. The
+ // |cross_process_frame_connector| allows the non-swapped-out
+ // RenderFrameHost for a frame to communicate with the parent process
+ // so that it may composite drawing data.
+ //
+ // Ownership is not transfered.
+ void set_cross_process_frame_connector(
+ CrossProcessFrameConnector* cross_process_frame_connector) {
+ cross_process_frame_connector_ = cross_process_frame_connector;
+ }
+
+ void set_render_frame_proxy_host(RenderFrameProxyHost* proxy) {
+ render_frame_proxy_host_ = proxy;
+ }
+
+ // Returns a bitwise OR of bindings types that have been enabled for this
+ // RenderFrameHostImpl's RenderView. See BindingsPolicy for details.
+ // TODO(creis): Make bindings frame-specific, to support cases like <webview>.
+ int GetEnabledBindings();
+
+ // Called on the pending RenderFrameHost when the network response is ready to
+ // commit. We should ensure that the old RenderFrameHost runs its unload
+ // handler and determine whether a transfer to a different RenderFrameHost is
+ // needed.
+ void OnCrossSiteResponse(
+ const GlobalRequestID& global_request_id,
+ scoped_ptr<CrossSiteTransferringRequest> cross_site_transferring_request,
+ const std::vector<GURL>& transfer_url_chain,
+ const Referrer& referrer,
+ PageTransition page_transition,
+ bool should_replace_current_entry);
+
+ // Tells the renderer that this RenderFrame is being swapped out for one in a
+ // different renderer process. It should run its unload handler, move to
+ // a blank document and create a RenderFrameProxy to replace the RenderFrame.
+ // The renderer should preserve the Proxy object until it exits, in case we
+ // come back. The renderer can exit if it has no other active RenderFrames,
+ // but not until WasSwappedOut is called (when it is no longer visible).
+ void SwapOut(RenderFrameProxyHost* proxy);
+
+ void OnSwappedOut(bool timed_out);
+ bool is_swapped_out() { return is_swapped_out_; }
+ void set_swapped_out(bool is_swapped_out) {
+ is_swapped_out_ = is_swapped_out;
+ }
+
+ // Sets the RVH for |this| as pending shutdown. |on_swap_out| will be called
+ // when the SwapOutACK is received.
+ void SetPendingShutdown(const base::Closure& on_swap_out);
+
+ // Sends the given navigation message. Use this rather than sending it
+ // yourself since this does the internal bookkeeping described below. This
+ // function takes ownership of the provided message pointer.
+ //
+ // If a cross-site request is in progress, we may be suspended while waiting
+ // for the onbeforeunload handler, so this function might buffer the message
+ // rather than sending it.
+ void Navigate(const FrameMsg_Navigate_Params& params);
+
+ // Load the specified URL; this is a shortcut for Navigate().
+ void NavigateToURL(const GURL& url);
+
+ // Runs the beforeunload handler for this frame. |for_cross_site_transition|
+ // indicates whether this call is for the current frame during a cross-process
+ // navigation. False means we're closing the entire tab.
+ void DispatchBeforeUnload(bool for_cross_site_transition);
+
+ // Deletes the current selection plus the specified number of characters
+ // before and after the selection or caret.
+ void ExtendSelectionAndDelete(size_t before, size_t after);
+
+ // Notifies the RenderFrame that the JavaScript message that was shown was
+ // closed by the user.
+ void JavaScriptDialogClosed(IPC::Message* reply_msg,
+ bool success,
+ const base::string16& user_input,
+ bool dialog_was_suppressed);
+
+ // Called when an HTML5 notification is closed.
+ void NotificationClosed(int notification_id);
protected:
friend class RenderFrameHostFactory;
@@ -65,23 +181,98 @@ class CONTENT_EXPORT RenderFrameHostImpl : public RenderFrameHost {
bool is_swapped_out);
private:
+ friend class TestRenderFrameHost;
friend class TestRenderViewHost;
// IPC Message handlers.
- void OnDetach(int64 parent_frame_id, int64 frame_id);
- void OnDidStartProvisionalLoadForFrame(int64 frame_id,
- int64 parent_frame_id,
- bool main_frame,
+ void OnAddMessageToConsole(int32 level,
+ const base::string16& message,
+ int32 line_no,
+ const base::string16& source_id);
+ void OnDetach();
+ void OnFrameFocused();
+ void OnOpenURL(const FrameHostMsg_OpenURL_Params& params);
+ void OnDocumentOnLoadCompleted();
+ void OnDidStartProvisionalLoadForFrame(int parent_routing_id,
const GURL& url);
+ void OnDidFailProvisionalLoadWithError(
+ const FrameHostMsg_DidFailProvisionalLoadWithError_Params& params);
+ void OnDidFailLoadWithError(
+ const GURL& url,
+ int error_code,
+ const base::string16& error_description);
+ void OnDidRedirectProvisionalLoad(int32 page_id,
+ const GURL& source_url,
+ const GURL& target_url);
+ void OnNavigate(const IPC::Message& msg);
+ void OnBeforeUnloadACK(
+ bool proceed,
+ const base::TimeTicks& renderer_before_unload_start_time,
+ const base::TimeTicks& renderer_before_unload_end_time);
+ void OnSwapOutACK();
+ void OnContextMenu(const ContextMenuParams& params);
+ void OnJavaScriptExecuteResponse(int id, const base::ListValue& result);
+ void OnRunJavaScriptMessage(const base::string16& message,
+ const base::string16& default_prompt,
+ const GURL& frame_url,
+ JavaScriptMessageType type,
+ IPC::Message* reply_msg);
+ void OnRunBeforeUnloadConfirm(const GURL& frame_url,
+ const base::string16& message,
+ bool is_reload,
+ IPC::Message* reply_msg);
+ void OnRequestDesktopNotificationPermission(const GURL& origin,
+ int callback_id);
+ void OnShowDesktopNotification(
+ int notification_id,
+ const ShowDesktopNotificationHostMsgParams& params);
+ void OnCancelDesktopNotification(int notification_id);
+ void OnTextSurroundingSelectionResponse(const base::string16& content,
+ size_t start_offset,
+ size_t end_offset);
+ void OnDidAccessInitialDocument();
+ void OnDidDisownOpener();
+ void OnUpdateTitle(int32 page_id,
+ const base::string16& title,
+ blink::WebTextDirection title_direction);
+ void OnUpdateEncoding(const std::string& encoding);
- bool is_swapped_out() { return is_swapped_out_; }
+ // Returns whether the given URL is allowed to commit in the current process.
+ // This is a more conservative check than RenderProcessHost::FilterURL, since
+ // it will be used to kill processes that commit unauthorized URLs.
+ bool CanCommitURL(const GURL& url);
+
+ void DesktopNotificationPermissionRequestDone(int callback_context);
- // TODO(nasko): This should be removed and replaced by RenderProcessHost.
- RenderViewHostImpl* render_view_host_; // Not owned.
+ // For now, RenderFrameHosts indirectly keep RenderViewHosts alive via a
+ // refcount that calls Shutdown when it reaches zero. This allows each
+ // RenderFrameHostManager to just care about RenderFrameHosts, while ensuring
+ // we have a RenderViewHost for each RenderFrameHost.
+ // TODO(creis): RenderViewHost will eventually go away and be replaced with
+ // some form of page context.
+ RenderViewHostImpl* render_view_host_;
RenderFrameHostDelegate* delegate_;
- // Reference to the whole frame tree that this RenderFrameHost belongs too.
+ // |cross_process_frame_connector_| passes messages from an out-of-process
+ // child frame to the parent process for compositing.
+ //
+ // This is only non-NULL when this is the swapped out RenderFrameHost in
+ // the same site instance as this frame's parent.
+ //
+ // See the class comment above CrossProcessFrameConnector for more
+ // information.
+ //
+ // This will move to RenderFrameProxyHost when that class is created.
+ CrossProcessFrameConnector* cross_process_frame_connector_;
+
+ // The proxy created for this RenderFrameHost. It is used to send and receive
+ // IPC messages while in swapped out state.
+ // TODO(nasko): This can be removed once we don't have a swapped out state on
+ // RenderFrameHosts. See https://crbug.com/357747.
+ RenderFrameProxyHost* render_frame_proxy_host_;
+
+ // Reference to the whole frame tree that this RenderFrameHost belongs to.
// Allows this RenderFrameHost to add and remove nodes in response to
// messages from the renderer requesting DOM manipulation.
FrameTree* frame_tree_;
@@ -89,9 +280,21 @@ class CONTENT_EXPORT RenderFrameHostImpl : public RenderFrameHost {
// The FrameTreeNode which this RenderFrameHostImpl is hosted in.
FrameTreeNode* frame_tree_node_;
+ // The mapping of pending JavaScript calls created by
+ // ExecuteJavaScript and their corresponding callbacks.
+ std::map<int, JavaScriptResultCallback> javascript_callbacks_;
+
+ // Map from notification_id to a callback to cancel them.
+ std::map<int, base::Closure> cancel_notification_callbacks_;
+
int routing_id_;
bool is_swapped_out_;
+ // When the last BeforeUnload message was sent.
+ base::TimeTicks send_before_unload_start_time_;
+
+ base::WeakPtrFactory<RenderFrameHostImpl> weak_ptr_factory_;
+
DISALLOW_COPY_AND_ASSIGN(RenderFrameHostImpl);
};
diff --git a/chromium/content/browser/frame_host/render_frame_host_manager.cc b/chromium/content/browser/frame_host/render_frame_host_manager.cc
index aeb87e753d5..fc36df203e8 100644
--- a/chromium/content/browser/frame_host/render_frame_host_manager.cc
+++ b/chromium/content/browser/frame_host/render_frame_host_manager.cc
@@ -9,12 +9,18 @@
#include "base/command_line.h"
#include "base/debug/trace_event.h"
#include "base/logging.h"
+#include "base/stl_util.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/devtools/render_view_devtools_agent_host.h"
+#include "content/browser/frame_host/cross_site_transferring_request.h"
#include "content/browser/frame_host/debug_urls.h"
#include "content/browser/frame_host/interstitial_page_impl.h"
#include "content/browser/frame_host/navigation_controller_impl.h"
#include "content/browser/frame_host/navigation_entry_impl.h"
+#include "content/browser/frame_host/navigator.h"
+#include "content/browser/frame_host/render_frame_host_factory.h"
+#include "content/browser/frame_host/render_frame_host_impl.h"
+#include "content/browser/frame_host/render_frame_proxy_host.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/renderer_host/render_view_host_factory.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
@@ -22,11 +28,11 @@
#include "content/browser/webui/web_ui_controller_factory_registry.h"
#include "content/browser/webui/web_ui_impl.h"
#include "content/common/view_messages.h"
-#include "content/port/browser/render_widget_host_view_port.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_widget_host_iterator.h"
+#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/user_metrics.h"
#include "content/public/browser/web_ui_controller.h"
#include "content/public/common/content_switches.h"
@@ -34,77 +40,73 @@
namespace content {
-RenderFrameHostManager::PendingNavigationParams::PendingNavigationParams()
- : is_transfer(false), frame_id(-1), should_replace_current_entry(false) {
-}
-
RenderFrameHostManager::PendingNavigationParams::PendingNavigationParams(
const GlobalRequestID& global_request_id,
- bool is_transfer,
+ scoped_ptr<CrossSiteTransferringRequest> cross_site_transferring_request,
const std::vector<GURL>& transfer_url_chain,
Referrer referrer,
PageTransition page_transition,
- int64 frame_id,
+ int render_frame_id,
bool should_replace_current_entry)
: global_request_id(global_request_id),
- is_transfer(is_transfer),
+ cross_site_transferring_request(cross_site_transferring_request.Pass()),
transfer_url_chain(transfer_url_chain),
referrer(referrer),
page_transition(page_transition),
- frame_id(frame_id),
+ render_frame_id(render_frame_id),
should_replace_current_entry(should_replace_current_entry) {
}
RenderFrameHostManager::PendingNavigationParams::~PendingNavigationParams() {}
+bool RenderFrameHostManager::ClearRFHsPendingShutdown(FrameTreeNode* node) {
+ node->render_manager()->pending_delete_hosts_.clear();
+ return true;
+}
+
RenderFrameHostManager::RenderFrameHostManager(
+ FrameTreeNode* frame_tree_node,
RenderFrameHostDelegate* render_frame_delegate,
RenderViewHostDelegate* render_view_delegate,
RenderWidgetHostDelegate* render_widget_delegate,
Delegate* delegate)
- : delegate_(delegate),
+ : frame_tree_node_(frame_tree_node),
+ delegate_(delegate),
cross_navigation_pending_(false),
render_frame_delegate_(render_frame_delegate),
render_view_delegate_(render_view_delegate),
render_widget_delegate_(render_widget_delegate),
- render_view_host_(NULL),
- pending_render_view_host_(NULL),
- interstitial_page_(NULL) {
+ interstitial_page_(NULL),
+ weak_factory_(this) {
+ DCHECK(frame_tree_node_);
}
RenderFrameHostManager::~RenderFrameHostManager() {
- if (pending_render_view_host_)
+ if (pending_render_frame_host_)
CancelPending();
- // We should always have a main RenderViewHost except in some tests.
- RenderViewHostImpl* render_view_host = render_view_host_;
- render_view_host_ = NULL;
- if (render_view_host)
- render_view_host->Shutdown();
+ // We should always have a current RenderFrameHost except in some tests.
+ SetRenderFrameHost(scoped_ptr<RenderFrameHostImpl>());
- // Shut down any swapped out RenderViewHosts.
- for (RenderViewHostMap::iterator iter = swapped_out_hosts_.begin();
- iter != swapped_out_hosts_.end();
- ++iter) {
- iter->second->Shutdown();
- }
+ // Delete any swapped out RenderFrameHosts.
+ STLDeleteValues(&proxy_hosts_);
}
void RenderFrameHostManager::Init(BrowserContext* browser_context,
SiteInstance* site_instance,
- int routing_id,
- int main_frame_routing_id) {
- // Create a RenderViewHost, once we have an instance. It is important to
- // immediately give this SiteInstance to a RenderViewHost so that it is
- // ref counted.
+ int view_routing_id,
+ int frame_routing_id) {
+ // Create a RenderViewHost and RenderFrameHost, once we have an instance. It
+ // is important to immediately give this SiteInstance to a RenderViewHost so
+ // that the SiteInstance is ref counted.
if (!site_instance)
site_instance = SiteInstance::Create(browser_context);
- render_view_host_ = static_cast<RenderViewHostImpl*>(
- RenderViewHostFactory::Create(
- site_instance, render_view_delegate_, render_frame_delegate_,
- render_widget_delegate_, routing_id, main_frame_routing_id, false,
- delegate_->IsHidden()));
- render_view_host_->AttachToFrameTree();
+
+ SetRenderFrameHost(CreateRenderFrameHost(site_instance,
+ view_routing_id,
+ frame_routing_id,
+ false,
+ delegate_->IsHidden()));
// Keep track of renderer processes as they start to shut down or are
// crashed/killed.
@@ -115,19 +117,39 @@ void RenderFrameHostManager::Init(BrowserContext* browser_context,
}
RenderViewHostImpl* RenderFrameHostManager::current_host() const {
- return render_view_host_;
+ if (!render_frame_host_)
+ return NULL;
+ return render_frame_host_->render_view_host();
}
RenderViewHostImpl* RenderFrameHostManager::pending_render_view_host() const {
- return pending_render_view_host_;
+ if (!pending_render_frame_host_)
+ return NULL;
+ return pending_render_frame_host_->render_view_host();
}
RenderWidgetHostView* RenderFrameHostManager::GetRenderWidgetHostView() const {
if (interstitial_page_)
return interstitial_page_->GetView();
- if (!render_view_host_)
+ if (!render_frame_host_)
+ return NULL;
+ return render_frame_host_->render_view_host()->GetView();
+}
+
+RenderFrameProxyHost* RenderFrameHostManager::GetProxyToParent() {
+ if (frame_tree_node_->IsMainFrame())
+ return NULL;
+
+ RenderFrameProxyHostMap::iterator iter =
+ proxy_hosts_.find(frame_tree_node_->parent()
+ ->render_manager()
+ ->current_frame_host()
+ ->GetSiteInstance()
+ ->GetId());
+ if (iter == proxy_hosts_.end())
return NULL;
- return render_view_host_->GetView();
+
+ return iter->second;
}
void RenderFrameHostManager::SetPendingWebUI(const NavigationEntryImpl& entry) {
@@ -142,111 +164,206 @@ void RenderFrameHostManager::SetPendingWebUI(const NavigationEntryImpl& entry) {
if (pending_web_ui_.get() &&
entry.bindings() != NavigationEntryImpl::kInvalidBindings &&
pending_web_ui_->GetBindings() != entry.bindings()) {
- RecordAction(UserMetricsAction("ProcessSwapBindingsMismatch_RVHM"));
+ RecordAction(
+ base::UserMetricsAction("ProcessSwapBindingsMismatch_RVHM"));
pending_web_ui_.reset();
}
}
-RenderViewHostImpl* RenderFrameHostManager::Navigate(
+RenderFrameHostImpl* RenderFrameHostManager::Navigate(
const NavigationEntryImpl& entry) {
TRACE_EVENT0("browser", "RenderFrameHostManager:Navigate");
- // Create a pending RenderViewHost. It will give us the one we should use
- RenderViewHostImpl* dest_render_view_host =
- static_cast<RenderViewHostImpl*>(UpdateRendererStateForNavigate(entry));
- if (!dest_render_view_host)
- return NULL; // We weren't able to create a pending render view host.
-
- // If the current render_view_host_ isn't live, we should create it so
- // that we don't show a sad tab while the dest_render_view_host fetches
+ // Create a pending RenderFrameHost to use for the navigation.
+ RenderFrameHostImpl* dest_render_frame_host = UpdateStateForNavigate(entry);
+ if (!dest_render_frame_host)
+ return NULL; // We weren't able to create a pending render frame host.
+
+ // If the current render_frame_host_ isn't live, we should create it so
+ // that we don't show a sad tab while the dest_render_frame_host fetches
// its first page. (Bug 1145340)
- if (dest_render_view_host != render_view_host_ &&
- !render_view_host_->IsRenderViewLive()) {
+ if (dest_render_frame_host != render_frame_host_ &&
+ !render_frame_host_->render_view_host()->IsRenderViewLive()) {
// Note: we don't call InitRenderView here because we are navigating away
// soon anyway, and we don't have the NavigationEntry for this host.
- delegate_->CreateRenderViewForRenderManager(render_view_host_,
- MSG_ROUTING_NONE);
+ delegate_->CreateRenderViewForRenderManager(
+ render_frame_host_->render_view_host(), MSG_ROUTING_NONE,
+ MSG_ROUTING_NONE, frame_tree_node_->IsMainFrame());
}
// If the renderer crashed, then try to create a new one to satisfy this
// navigation request.
- if (!dest_render_view_host->IsRenderViewLive()) {
+ if (!dest_render_frame_host->render_view_host()->IsRenderViewLive()) {
// Recreate the opener chain.
int opener_route_id = delegate_->CreateOpenerRenderViewsForRenderManager(
- dest_render_view_host->GetSiteInstance());
- if (!InitRenderView(dest_render_view_host, opener_route_id))
+ dest_render_frame_host->GetSiteInstance());
+ if (!InitRenderView(dest_render_frame_host->render_view_host(),
+ opener_route_id,
+ MSG_ROUTING_NONE,
+ frame_tree_node_->IsMainFrame()))
return NULL;
// Now that we've created a new renderer, be sure to hide it if it isn't
// our primary one. Otherwise, we might crash if we try to call Show()
// on it later.
- if (dest_render_view_host != render_view_host_ &&
- dest_render_view_host->GetView()) {
- dest_render_view_host->GetView()->Hide();
- } else {
+ if (dest_render_frame_host != render_frame_host_ &&
+ dest_render_frame_host->render_view_host()->GetView()) {
+ dest_render_frame_host->render_view_host()->GetView()->Hide();
+ } else if (frame_tree_node_->IsMainFrame()) {
// This is our primary renderer, notify here as we won't be calling
- // CommitPending (which does the notify).
- delegate_->NotifySwappedFromRenderManager(NULL, render_view_host_);
+ // CommitPending (which does the notify). We only do this for top-level
+ // frames.
+ delegate_->NotifySwappedFromRenderManager(
+ NULL, render_frame_host_->render_view_host());
}
}
- return dest_render_view_host;
+ // If entry includes the request ID of a request that is being transferred,
+ // the destination render frame will take ownership, so release ownership of
+ // the request.
+ if (pending_nav_params_ &&
+ pending_nav_params_->global_request_id ==
+ entry.transferred_global_request_id()) {
+ pending_nav_params_->cross_site_transferring_request->ReleaseRequest();
+ }
+
+ return dest_render_frame_host;
}
void RenderFrameHostManager::Stop() {
- render_view_host_->Stop();
+ render_frame_host_->render_view_host()->Stop();
// If we are cross-navigating, we should stop the pending renderers. This
// will lead to a DidFailProvisionalLoad, which will properly destroy them.
if (cross_navigation_pending_) {
- pending_render_view_host_->Send(
- new ViewMsg_Stop(pending_render_view_host_->GetRoutingID()));
+ pending_render_frame_host_->render_view_host()->Send(new ViewMsg_Stop(
+ pending_render_frame_host_->render_view_host()->GetRoutingID()));
}
}
void RenderFrameHostManager::SetIsLoading(bool is_loading) {
- render_view_host_->SetIsLoading(is_loading);
- if (pending_render_view_host_)
- pending_render_view_host_->SetIsLoading(is_loading);
+ render_frame_host_->render_view_host()->SetIsLoading(is_loading);
+ if (pending_render_frame_host_)
+ pending_render_frame_host_->render_view_host()->SetIsLoading(is_loading);
}
bool RenderFrameHostManager::ShouldCloseTabOnUnresponsiveRenderer() {
if (!cross_navigation_pending_)
return true;
- // We should always have a pending RVH when there's a cross-process navigation
+ // We should always have a pending RFH when there's a cross-process navigation
// in progress. Sanity check this for http://crbug.com/276333.
- CHECK(pending_render_view_host_);
+ CHECK(pending_render_frame_host_);
// If the tab becomes unresponsive during {before}unload while doing a
// cross-site navigation, proceed with the navigation. (This assumes that
- // the pending RenderViewHost is still responsive.)
- if (render_view_host_->is_waiting_for_unload_ack()) {
+ // the pending RenderFrameHost is still responsive.)
+ if (render_frame_host_->render_view_host()->IsWaitingForUnloadACK()) {
// The request has been started and paused while we're waiting for the
// unload handler to finish. We'll pretend that it did. The pending
// renderer will then be swapped in as part of the usual DidNavigate logic.
// (If the unload handler later finishes, this call will be ignored because
// the pending_nav_params_ state will already be cleaned up.)
current_host()->OnSwappedOut(true);
- } else if (render_view_host_->is_waiting_for_beforeunload_ack()) {
+ } else if (render_frame_host_->render_view_host()->
+ is_waiting_for_beforeunload_ack()) {
// Haven't gotten around to starting the request, because we're still
// waiting for the beforeunload handler to finish. We'll pretend that it
// did finish, to let the navigation proceed. Note that there's a danger
// that the beforeunload handler will later finish and possibly return
// false (meaning the navigation should not proceed), but we'll ignore it
// in this case because it took too long.
- if (pending_render_view_host_->are_navigations_suspended())
- pending_render_view_host_->SetNavigationsSuspended(
+ if (pending_render_frame_host_->render_view_host()->
+ are_navigations_suspended()) {
+ pending_render_frame_host_->render_view_host()->SetNavigationsSuspended(
false, base::TimeTicks::Now());
+ }
}
return false;
}
-void RenderFrameHostManager::SwappedOut(RenderViewHost* render_view_host) {
- // Make sure this is from our current RVH, and that we have a pending
+void RenderFrameHostManager::OnBeforeUnloadACK(
+ bool for_cross_site_transition,
+ bool proceed,
+ const base::TimeTicks& proceed_time) {
+ if (for_cross_site_transition) {
+ // Ignore if we're not in a cross-site navigation.
+ if (!cross_navigation_pending_)
+ return;
+
+ if (proceed) {
+ // Ok to unload the current page, so proceed with the cross-site
+ // navigation. Note that if navigations are not currently suspended, it
+ // might be because the renderer was deemed unresponsive and this call was
+ // already made by ShouldCloseTabOnUnresponsiveRenderer. In that case, it
+ // is ok to do nothing here.
+ if (pending_render_frame_host_ &&
+ pending_render_frame_host_->render_view_host()->
+ are_navigations_suspended()) {
+ pending_render_frame_host_->render_view_host()->
+ SetNavigationsSuspended(false, proceed_time);
+ }
+ } else {
+ // Current page says to cancel.
+ CancelPending();
+ cross_navigation_pending_ = false;
+ }
+ } else {
+ // Non-cross site transition means closing the entire tab.
+ bool proceed_to_fire_unload;
+ delegate_->BeforeUnloadFiredFromRenderManager(proceed, proceed_time,
+ &proceed_to_fire_unload);
+
+ if (proceed_to_fire_unload) {
+ // If we're about to close the tab and there's a pending RFH, cancel it.
+ // Otherwise, if the navigation in the pending RFH completes before the
+ // close in the current RFH, we'll lose the tab close.
+ if (pending_render_frame_host_) {
+ CancelPending();
+ cross_navigation_pending_ = false;
+ }
+
+ // This is not a cross-site navigation, the tab is being closed.
+ render_frame_host_->render_view_host()->ClosePage();
+ }
+ }
+}
+
+void RenderFrameHostManager::OnCrossSiteResponse(
+ RenderFrameHostImpl* pending_render_frame_host,
+ const GlobalRequestID& global_request_id,
+ scoped_ptr<CrossSiteTransferringRequest> cross_site_transferring_request,
+ const std::vector<GURL>& transfer_url_chain,
+ const Referrer& referrer,
+ PageTransition page_transition,
+ bool should_replace_current_entry) {
+ // This should be called either when the pending RFH is ready to commit or
+ // when we realize that the current RFH's request requires a transfer.
+ DCHECK(pending_render_frame_host == pending_render_frame_host_ ||
+ pending_render_frame_host == render_frame_host_);
+
+ // TODO(creis): Eventually we will want to check all navigation responses
+ // here, but currently we pass information for a transfer if
+ // ShouldSwapProcessesForRedirect returned true in the network stack.
+ // In that case, we should set up a transfer after the unload handler runs.
+ // If |cross_site_transferring_request| is NULL, we will just run the unload
+ // handler and resume.
+ pending_nav_params_.reset(new PendingNavigationParams(
+ global_request_id, cross_site_transferring_request.Pass(),
+ transfer_url_chain, referrer, page_transition,
+ pending_render_frame_host->GetRoutingID(),
+ should_replace_current_entry));
+
+ // Run the unload handler of the current page.
+ SwapOutOldPage();
+}
+
+void RenderFrameHostManager::SwappedOut(
+ RenderFrameHostImpl* render_frame_host) {
+ // Make sure this is from our current RFH, and that we have a pending
// navigation from OnCrossSiteResponse. (There may be no pending navigation
// for data URLs that don't make network requests, for example.) If not,
// just return early and ignore.
- if (render_view_host != render_view_host_ || !pending_nav_params_.get()) {
+ if (render_frame_host != render_frame_host_ || !pending_nav_params_.get()) {
pending_nav_params_.reset();
return;
}
@@ -256,7 +373,21 @@ void RenderFrameHostManager::SwappedOut(RenderViewHost* render_view_host) {
// TODO(creis): The blank swapped out page is visible during this time, but
// we can shorten this by delivering the response directly, rather than
// forcing an identical request to be made.
- if (pending_nav_params_->is_transfer) {
+ if (pending_nav_params_->cross_site_transferring_request) {
+ // Sanity check that the params are for the correct frame and process.
+ // These should match the RenderFrameHost that made the request.
+ // If it started as a cross-process navigation via OpenURL, this is the
+ // pending one. If it wasn't cross-process until the transfer, this is the
+ // current one.
+ int render_frame_id = pending_render_frame_host_ ?
+ pending_render_frame_host_->GetRoutingID() :
+ render_frame_host_->GetRoutingID();
+ DCHECK_EQ(render_frame_id, pending_nav_params_->render_frame_id);
+ int process_id = pending_render_frame_host_ ?
+ pending_render_frame_host_->GetProcess()->GetID() :
+ render_frame_host_->GetProcess()->GetID();
+ DCHECK_EQ(process_id, pending_nav_params_->global_request_id.child_id);
+
// Treat the last URL in the chain as the destination and the remainder as
// the redirect chain.
CHECK(pending_nav_params_->transfer_url_chain.size());
@@ -266,33 +397,33 @@ void RenderFrameHostManager::SwappedOut(RenderViewHost* render_view_host) {
// We don't know whether the original request had |user_action| set to true.
// However, since we force the navigation to be in the current tab, it
// doesn't matter.
- render_view_host->GetDelegate()->RequestTransferURL(
+ render_frame_host->frame_tree_node()->navigator()->RequestTransferURL(
+ render_frame_host,
transfer_url,
pending_nav_params_->transfer_url_chain,
pending_nav_params_->referrer,
pending_nav_params_->page_transition,
CURRENT_TAB,
- pending_nav_params_->frame_id,
pending_nav_params_->global_request_id,
pending_nav_params_->should_replace_current_entry,
true);
- } else if (pending_render_view_host_) {
+ } else if (pending_render_frame_host_) {
RenderProcessHostImpl* pending_process =
static_cast<RenderProcessHostImpl*>(
- pending_render_view_host_->GetProcess());
+ pending_render_frame_host_->GetProcess());
pending_process->ResumeDeferredNavigation(
pending_nav_params_->global_request_id);
}
pending_nav_params_.reset();
}
-void RenderFrameHostManager::DidNavigateMainFrame(
- RenderViewHost* render_view_host) {
+void RenderFrameHostManager::DidNavigateFrame(
+ RenderFrameHostImpl* render_frame_host) {
if (!cross_navigation_pending_) {
- DCHECK(!pending_render_view_host_);
+ DCHECK(!pending_render_frame_host_);
// We should only hear this from our current renderer.
- DCHECK(render_view_host == render_view_host_);
+ DCHECK_EQ(render_frame_host_, render_frame_host);
// Even when there is no pending RVH, there may be a pending Web UI.
if (pending_web_ui())
@@ -300,17 +431,22 @@ void RenderFrameHostManager::DidNavigateMainFrame(
return;
}
- if (render_view_host == pending_render_view_host_) {
+ if (render_frame_host == pending_render_frame_host_) {
// The pending cross-site navigation completed, so show the renderer.
// If it committed without sending network requests (e.g., data URLs),
- // then we still need to swap out the old RVH first and run its unload
- // handler. OK for that to happen in the background.
- if (pending_render_view_host_->HasPendingCrossSiteRequest())
+ // then we still need to swap out the old RFH first and run its unload
+ // handler, only if it hasn't happened yet. OK for that to happen in the
+ // background.
+ if (pending_render_frame_host_->render_view_host()->
+ HasPendingCrossSiteRequest() &&
+ pending_render_frame_host_->render_view_host()->rvh_state() ==
+ RenderViewHostImpl::STATE_DEFAULT) {
SwapOutOldPage();
+ }
CommitPending();
cross_navigation_pending_ = false;
- } else if (render_view_host == render_view_host_) {
+ } else if (render_frame_host == render_frame_host_) {
// A navigation in the original page has taken place. Cancel the pending
// one.
CancelPending();
@@ -321,39 +457,26 @@ void RenderFrameHostManager::DidNavigateMainFrame(
}
}
+// TODO(creis): Take in RenderFrameHost instead, since frames can have openers.
void RenderFrameHostManager::DidDisownOpener(RenderViewHost* render_view_host) {
// Notify all swapped out hosts, including the pending RVH.
- for (RenderViewHostMap::iterator iter = swapped_out_hosts_.begin();
- iter != swapped_out_hosts_.end();
+ for (RenderFrameProxyHostMap::iterator iter = proxy_hosts_.begin();
+ iter != proxy_hosts_.end();
++iter) {
DCHECK_NE(iter->second->GetSiteInstance(),
- current_host()->GetSiteInstance());
- iter->second->DisownOpener();
+ current_frame_host()->GetSiteInstance());
+ iter->second->GetRenderViewHost()->DisownOpener();
}
}
-void RenderFrameHostManager::RendererAbortedProvisionalLoad(
- RenderViewHost* render_view_host) {
- // We used to cancel the pending renderer here for cross-site downloads.
- // However, it's not safe to do that because the download logic repeatedly
- // looks for this WebContents based on a render view ID. Instead, we just
- // leave the pending renderer around until the next navigation event
- // (Navigate, DidNavigate, etc), which will clean it up properly.
- // TODO(creis): All of this will go away when we move the cross-site logic
- // to ResourceDispatcherHost, so that we intercept responses rather than
- // navigation events. (That's necessary to support onunload anyway.) Once
- // we've made that change, we won't create a pending renderer until we know
- // the response is not a download.
-}
-
void RenderFrameHostManager::RendererProcessClosing(
RenderProcessHost* render_process_host) {
// Remove any swapped out RVHs from this process, so that we don't try to
// swap them back in while the process is exiting. Start by finding them,
// since there could be more than one.
std::list<int> ids_to_remove;
- for (RenderViewHostMap::iterator iter = swapped_out_hosts_.begin();
- iter != swapped_out_hosts_.end();
+ for (RenderFrameProxyHostMap::iterator iter = proxy_hosts_.begin();
+ iter != proxy_hosts_.end();
++iter) {
if (iter->second->GetProcess() == render_process_host)
ids_to_remove.push_back(iter->first);
@@ -361,85 +484,12 @@ void RenderFrameHostManager::RendererProcessClosing(
// Now delete them.
while (!ids_to_remove.empty()) {
- swapped_out_hosts_[ids_to_remove.back()]->Shutdown();
- swapped_out_hosts_.erase(ids_to_remove.back());
+ delete proxy_hosts_[ids_to_remove.back()];
+ proxy_hosts_.erase(ids_to_remove.back());
ids_to_remove.pop_back();
}
}
-void RenderFrameHostManager::ShouldClosePage(
- bool for_cross_site_transition,
- bool proceed,
- const base::TimeTicks& proceed_time) {
- if (for_cross_site_transition) {
- // Ignore if we're not in a cross-site navigation.
- if (!cross_navigation_pending_)
- return;
-
- if (proceed) {
- // Ok to unload the current page, so proceed with the cross-site
- // navigation. Note that if navigations are not currently suspended, it
- // might be because the renderer was deemed unresponsive and this call was
- // already made by ShouldCloseTabOnUnresponsiveRenderer. In that case, it
- // is ok to do nothing here.
- if (pending_render_view_host_ &&
- pending_render_view_host_->are_navigations_suspended()) {
- pending_render_view_host_->SetNavigationsSuspended(false, proceed_time);
- }
- } else {
- // Current page says to cancel.
- CancelPending();
- cross_navigation_pending_ = false;
- }
- } else {
- // Non-cross site transition means closing the entire tab.
- bool proceed_to_fire_unload;
- delegate_->BeforeUnloadFiredFromRenderManager(proceed, proceed_time,
- &proceed_to_fire_unload);
-
- if (proceed_to_fire_unload) {
- // If we're about to close the tab and there's a pending RVH, cancel it.
- // Otherwise, if the navigation in the pending RVH completes before the
- // close in the current RVH, we'll lose the tab close.
- if (pending_render_view_host_) {
- CancelPending();
- cross_navigation_pending_ = false;
- }
-
- // This is not a cross-site navigation, the tab is being closed.
- render_view_host_->ClosePage();
- }
- }
-}
-
-void RenderFrameHostManager::OnCrossSiteResponse(
- RenderViewHost* pending_render_view_host,
- const GlobalRequestID& global_request_id,
- bool is_transfer,
- const std::vector<GURL>& transfer_url_chain,
- const Referrer& referrer,
- PageTransition page_transition,
- int64 frame_id,
- bool should_replace_current_entry) {
- // This should be called either when the pending RVH is ready to commit or
- // when we realize that the current RVH's request requires a transfer.
- DCHECK(
- pending_render_view_host == pending_render_view_host_ ||
- pending_render_view_host == render_view_host_);
-
- // TODO(creis): Eventually we will want to check all navigation responses
- // here, but currently we pass information for a transfer if
- // ShouldSwapProcessesForRedirect returned true in the network stack.
- // In that case, we should set up a transfer after the unload handler runs.
- // If is_transfer is false, we will just run the unload handler and resume.
- pending_nav_params_.reset(new PendingNavigationParams(
- global_request_id, is_transfer, transfer_url_chain, referrer,
- page_transition, frame_id, should_replace_current_entry));
-
- // Run the unload handler of the current page.
- SwapOutOldPage();
-}
-
void RenderFrameHostManager::SwapOutOldPage() {
// Should only see this while we have a pending renderer or transfer.
CHECK(cross_navigation_pending_ || pending_nav_params_.get());
@@ -447,26 +497,55 @@ void RenderFrameHostManager::SwapOutOldPage() {
// Tell the renderer to suppress any further modal dialogs so that we can swap
// it out. This must be done before canceling any current dialog, in case
// there is a loop creating additional dialogs.
- render_view_host_->SuppressDialogsUntilSwapOut();
+ // TODO(creis): Handle modal dialogs in subframe processes.
+ render_frame_host_->render_view_host()->SuppressDialogsUntilSwapOut();
// Now close any modal dialogs that would prevent us from swapping out. This
// must be done separately from SwapOut, so that the PageGroupLoadDeferrer is
// no longer on the stack when we send the SwapOut message.
delegate_->CancelModalDialogsForRenderManager();
- // Tell the old renderer it is being swapped out. This will fire the unload
- // handler (without firing the beforeunload handler a second time). When the
- // unload handler finishes and the navigation completes, we will send a
- // message to the ResourceDispatcherHost, allowing the pending RVH's response
- // to resume.
- render_view_host_->SwapOut();
+ // Create the RenderFrameProxyHost that will replace the
+ // RenderFrameHost which is swapping out. If one exists, ensure it is deleted
+ // from the map and not leaked.
+ RenderFrameProxyHostMap::iterator iter = proxy_hosts_.find(
+ render_frame_host_->GetSiteInstance()->GetId());
+ if (iter != proxy_hosts_.end()) {
+ delete iter->second;
+ proxy_hosts_.erase(iter);
+ }
+
+ RenderFrameProxyHost* proxy = new RenderFrameProxyHost(
+ render_frame_host_->GetSiteInstance(), frame_tree_node_);
+ proxy_hosts_[render_frame_host_->GetSiteInstance()->GetId()] = proxy;
+
+ // Tell the old frame it is being swapped out. This will fire the unload
+ // handler in the background (without firing the beforeunload handler a second
+ // time). When the navigation completes, we will send a message to the
+ // ResourceDispatcherHost, allowing the pending RVH's response to resume.
+ render_frame_host_->SwapOut(proxy);
// ResourceDispatcherHost has told us to run the onunload handler, which
// means it is not a download or unsafe page, and we are going to perform the
- // navigation. Thus, we no longer need to remember that the RenderViewHost
+ // navigation. Thus, we no longer need to remember that the RenderFrameHost
// is part of a pending cross-site request.
- if (pending_render_view_host_)
- pending_render_view_host_->SetHasPendingCrossSiteRequest(false);
+ if (pending_render_frame_host_) {
+ pending_render_frame_host_->render_view_host()->
+ SetHasPendingCrossSiteRequest(false);
+ }
+}
+
+void RenderFrameHostManager::ClearPendingShutdownRFHForSiteInstance(
+ int32 site_instance_id,
+ RenderFrameHostImpl* rfh) {
+ RFHPendingDeleteMap::iterator iter =
+ pending_delete_hosts_.find(site_instance_id);
+ if (iter != pending_delete_hosts_.end() && iter->second.get() == rfh)
+ pending_delete_hosts_.erase(site_instance_id);
+}
+
+void RenderFrameHostManager::ResetProxyHosts() {
+ STLDeleteValues(&proxy_hosts_);
}
void RenderFrameHostManager::Observe(
@@ -485,6 +564,41 @@ void RenderFrameHostManager::Observe(
}
}
+bool RenderFrameHostManager::ClearProxiesInSiteInstance(
+ int32 site_instance_id,
+ FrameTreeNode* node) {
+ RenderFrameProxyHostMap::iterator iter =
+ node->render_manager()->proxy_hosts_.find(site_instance_id);
+ if (iter != node->render_manager()->proxy_hosts_.end()) {
+ RenderFrameProxyHost* proxy = iter->second;
+ // If the RVH is pending swap out, it needs to switch state to
+ // pending shutdown. Otherwise it is deleted.
+ if (proxy->GetRenderViewHost()->rvh_state() ==
+ RenderViewHostImpl::STATE_PENDING_SWAP_OUT) {
+ scoped_ptr<RenderFrameHostImpl> swapped_out_rfh =
+ proxy->PassFrameHostOwnership();
+
+ swapped_out_rfh->SetPendingShutdown(base::Bind(
+ &RenderFrameHostManager::ClearPendingShutdownRFHForSiteInstance,
+ node->render_manager()->weak_factory_.GetWeakPtr(),
+ site_instance_id,
+ swapped_out_rfh.get()));
+ RFHPendingDeleteMap::iterator pending_delete_iter =
+ node->render_manager()->pending_delete_hosts_.find(site_instance_id);
+ if (pending_delete_iter ==
+ node->render_manager()->pending_delete_hosts_.end() ||
+ pending_delete_iter->second.get() != swapped_out_rfh) {
+ node->render_manager()->pending_delete_hosts_[site_instance_id] =
+ linked_ptr<RenderFrameHostImpl>(swapped_out_rfh.release());
+ }
+ }
+ delete proxy;
+ node->render_manager()->proxy_hosts_.erase(site_instance_id);
+ }
+
+ return true;
+}
+
bool RenderFrameHostManager::ShouldTransitionCrossSite() {
// False in the single-process mode, as it makes RVHs to accumulate
// in swapped_out_hosts_.
@@ -500,9 +614,12 @@ bool RenderFrameHostManager::ShouldSwapBrowsingInstancesForNavigation(
const NavigationEntryImpl* new_entry) const {
DCHECK(new_entry);
- // If new_entry already has a SiteInstance, assume it is correct and use it.
- if (new_entry->site_instance())
- return false;
+ // If new_entry already has a SiteInstance, assume it is correct. We only
+ // need to force a swap if it is in a different BrowsingInstance.
+ if (new_entry->site_instance()) {
+ return !new_entry->site_instance()->IsRelatedSiteInstance(
+ render_frame_host_->GetSiteInstance());
+ }
// Check for reasons to swap processes even if we are in a process model that
// doesn't usually swap (e.g., process-per-tab). Any time we return true,
@@ -517,7 +634,7 @@ bool RenderFrameHostManager::ShouldSwapBrowsingInstancesForNavigation(
const GURL& current_url = (current_entry) ?
SiteInstanceImpl::GetEffectiveURL(browser_context,
current_entry->GetURL()) :
- render_view_host_->GetSiteInstance()->GetSiteURL();
+ render_frame_host_->GetSiteInstance()->GetSiteURL();
const GURL& new_url = SiteInstanceImpl::GetEffectiveURL(browser_context,
new_entry->GetURL());
@@ -533,7 +650,7 @@ bool RenderFrameHostManager::ShouldSwapBrowsingInstancesForNavigation(
// If so, force a swap if destination is not an acceptable URL for Web UI.
// Here, data URLs are never allowed.
if (!WebUIControllerFactoryRegistry::GetInstance()->IsURLAcceptableForWebUI(
- browser_context, new_url, false)) {
+ browser_context, new_url)) {
return true;
}
} else {
@@ -547,7 +664,8 @@ bool RenderFrameHostManager::ShouldSwapBrowsingInstancesForNavigation(
// Check with the content client as well. Important to pass current_url here,
// which uses the SiteInstance's site if there is no current_entry.
if (GetContentClient()->browser()->ShouldSwapBrowsingInstancesForNavigation(
- render_view_host_->GetSiteInstance(), current_url, new_url)) {
+ render_frame_host_->GetSiteInstance(),
+ current_url, new_url)) {
return true;
}
@@ -584,17 +702,20 @@ SiteInstance* RenderFrameHostManager::GetSiteInstanceForEntry(
delegate_->GetControllerForRenderManager();
BrowserContext* browser_context = controller.GetBrowserContext();
+ // If the entry has an instance already we should use it.
+ if (entry.site_instance()) {
+ // If we are forcing a swap, this should be in a different BrowsingInstance.
+ if (force_browsing_instance_swap) {
+ CHECK(!entry.site_instance()->IsRelatedSiteInstance(
+ render_frame_host_->GetSiteInstance()));
+ }
+ return entry.site_instance();
+ }
+
// If a swap is required, we need to force the SiteInstance AND
// BrowsingInstance to be different ones, using CreateForURL.
- if (force_browsing_instance_swap) {
- // We shouldn't be forcing a swap if an entry already has a SiteInstance.
- CHECK(!entry.site_instance());
+ if (force_browsing_instance_swap)
return SiteInstance::CreateForURL(browser_context, dest_url);
- }
-
- // If the entry has an instance already we should use it.
- if (entry.site_instance())
- return entry.site_instance();
// (UGLY) HEURISTIC, process-per-site only:
//
@@ -730,7 +851,47 @@ SiteInstance* RenderFrameHostManager::GetSiteInstanceForEntry(
return current_instance->GetRelatedSiteInstance(dest_url);
}
-int RenderFrameHostManager::CreateRenderView(
+scoped_ptr<RenderFrameHostImpl> RenderFrameHostManager::CreateRenderFrameHost(
+ SiteInstance* site_instance,
+ int view_routing_id,
+ int frame_routing_id,
+ bool swapped_out,
+ bool hidden) {
+ if (frame_routing_id == MSG_ROUTING_NONE)
+ frame_routing_id = site_instance->GetProcess()->GetNextRoutingID();
+
+ // Create a RVH for main frames, or find the existing one for subframes.
+ FrameTree* frame_tree = frame_tree_node_->frame_tree();
+ RenderViewHostImpl* render_view_host = NULL;
+ if (frame_tree_node_->IsMainFrame()) {
+ render_view_host = frame_tree->CreateRenderViewHostForMainFrame(
+ site_instance, view_routing_id, frame_routing_id, swapped_out, hidden);
+ } else {
+ render_view_host = frame_tree->GetRenderViewHostForSubFrame(site_instance);
+
+ // If we haven't found a RVH for a subframe RFH, it's because we currently
+ // do not create top-level RFHs for pending subframe navigations. Create
+ // the RVH here for now.
+ // TODO(creis): Mirror the frame tree so this check isn't necessary.
+ if (!render_view_host) {
+ render_view_host = frame_tree->CreateRenderViewHostForMainFrame(
+ site_instance, view_routing_id, frame_routing_id, swapped_out,
+ hidden);
+ }
+ }
+
+ // TODO(creis): Pass hidden to RFH.
+ scoped_ptr<RenderFrameHostImpl> render_frame_host =
+ make_scoped_ptr(RenderFrameHostFactory::Create(render_view_host,
+ render_frame_delegate_,
+ frame_tree,
+ frame_tree_node_,
+ frame_routing_id,
+ swapped_out).release());
+ return render_frame_host.Pass();
+}
+
+int RenderFrameHostManager::CreateRenderFrame(
SiteInstance* instance,
int opener_route_id,
bool swapped_out,
@@ -738,74 +899,114 @@ int RenderFrameHostManager::CreateRenderView(
CHECK(instance);
DCHECK(!swapped_out || hidden); // Swapped out views should always be hidden.
- // We are creating a pending or swapped out RVH here. We should never create
- // it in the same SiteInstance as our current RVH.
- CHECK_NE(render_view_host_->GetSiteInstance(), instance);
+ scoped_ptr<RenderFrameHostImpl> new_render_frame_host;
+ RenderFrameHostImpl* frame_to_announce = NULL;
+ int routing_id = MSG_ROUTING_NONE;
+
+ // We are creating a pending or swapped out RFH here. We should never create
+ // it in the same SiteInstance as our current RFH.
+ CHECK_NE(render_frame_host_->GetSiteInstance(), instance);
- // Check if we've already created an RVH for this SiteInstance. If so, try
+ // Check if we've already created an RFH for this SiteInstance. If so, try
// to re-use the existing one, which has already been initialized. We'll
// remove it from the list of swapped out hosts if it commits.
- RenderViewHostImpl* new_render_view_host = static_cast<RenderViewHostImpl*>(
- GetSwappedOutRenderViewHost(instance));
- if (new_render_view_host) {
+ RenderFrameProxyHost* proxy = GetRenderFrameProxyHost(instance);
+
+ if (proxy) {
+ routing_id = proxy->GetRenderViewHost()->GetRoutingID();
+ // Delete the existing RenderFrameProxyHost, but reuse the RenderFrameHost.
// Prevent the process from exiting while we're trying to use it.
- if (!swapped_out)
- new_render_view_host->GetProcess()->AddPendingView();
+ if (!swapped_out) {
+ new_render_frame_host = proxy->PassFrameHostOwnership();
+ new_render_frame_host->GetProcess()->AddPendingView();
+
+ proxy_hosts_.erase(instance->GetId());
+ delete proxy;
+
+ // When a new render view is created by the renderer, the new WebContents
+ // gets a RenderViewHost in the SiteInstance of its opener WebContents.
+ // If not used in the first navigation, this RVH is swapped out and is not
+ // granted bindings, so we may need to grant them when swapping it in.
+ if (pending_web_ui() &&
+ !new_render_frame_host->GetProcess()->IsIsolatedGuest()) {
+ int required_bindings = pending_web_ui()->GetBindings();
+ RenderViewHost* rvh = new_render_frame_host->render_view_host();
+ if ((rvh->GetEnabledBindings() & required_bindings) !=
+ required_bindings) {
+ rvh->AllowBindings(required_bindings);
+ }
+ }
+ }
} else {
- // Create a new RenderViewHost if we don't find an existing one.
- new_render_view_host = static_cast<RenderViewHostImpl*>(
- RenderViewHostFactory::Create(instance,
- render_view_delegate_,
- render_frame_delegate_,
- render_widget_delegate_,
- MSG_ROUTING_NONE,
- MSG_ROUTING_NONE,
- swapped_out,
- hidden));
-
- // If the new RVH is swapped out already, store it. Otherwise prevent the
- // process from exiting while we're trying to navigate in it.
- if (swapped_out) {
- swapped_out_hosts_[instance->GetId()] = new_render_view_host;
+ // Create a new RenderFrameHost if we don't find an existing one.
+ new_render_frame_host = CreateRenderFrameHost(
+ instance, MSG_ROUTING_NONE, MSG_ROUTING_NONE, swapped_out, hidden);
+ RenderViewHostImpl* render_view_host =
+ new_render_frame_host->render_view_host();
+ int proxy_routing_id = MSG_ROUTING_NONE;
+
+ // Prevent the process from exiting while we're trying to navigate in it.
+ // Otherwise, if the new RFH is swapped out already, store it.
+ if (!swapped_out) {
+ new_render_frame_host->GetProcess()->AddPendingView();
} else {
- new_render_view_host->GetProcess()->AddPendingView();
+ proxy = new RenderFrameProxyHost(
+ new_render_frame_host->GetSiteInstance(), frame_tree_node_);
+ proxy_hosts_[instance->GetId()] = proxy;
+ proxy->TakeFrameHostOwnership(new_render_frame_host.Pass());
+ proxy_routing_id = proxy->GetRoutingID();
}
- bool success = InitRenderView(new_render_view_host, opener_route_id);
- if (success) {
- // Don't show the view until we get a DidNavigate from it.
- new_render_view_host->GetView()->Hide();
- } else if (!swapped_out) {
+ bool success = InitRenderView(
+ render_view_host, opener_route_id, proxy_routing_id,
+ frame_tree_node_->IsMainFrame());
+ if (success && frame_tree_node_->IsMainFrame()) {
+ // Don't show the main frame's view until we get a DidNavigate from it.
+ render_view_host->GetView()->Hide();
+ } else if (!swapped_out && pending_render_frame_host_) {
CancelPending();
}
+ routing_id = render_view_host->GetRoutingID();
+ frame_to_announce = new_render_frame_host.get();
}
- // Use this as our new pending RVH if it isn't swapped out.
+ // Use this as our new pending RFH if it isn't swapped out.
if (!swapped_out)
- pending_render_view_host_ = new_render_view_host;
+ pending_render_frame_host_ = new_render_frame_host.Pass();
- return new_render_view_host->GetRoutingID();
+ // If a brand new RFH was created, announce it to observers.
+ if (frame_to_announce)
+ render_frame_delegate_->RenderFrameCreated(frame_to_announce);
+
+ return routing_id;
}
bool RenderFrameHostManager::InitRenderView(RenderViewHost* render_view_host,
- int opener_route_id) {
+ int opener_route_id,
+ int proxy_routing_id,
+ bool for_main_frame) {
+ // We may have initialized this RenderViewHost for another RenderFrameHost.
+ if (render_view_host->IsRenderViewLive())
+ return true;
+
// If the pending navigation is to a WebUI and the RenderView is not in a
- // guest process, tell the RenderView about any bindings it will need enabled.
- if (pending_web_ui() && !render_view_host->GetProcess()->IsGuest()) {
+ // guest process, tell the RenderViewHost about any bindings it will need
+ // enabled.
+ if (pending_web_ui() && !render_view_host->GetProcess()->IsIsolatedGuest()) {
render_view_host->AllowBindings(pending_web_ui()->GetBindings());
} else {
// Ensure that we don't create an unprivileged RenderView in a WebUI-enabled
// process unless it's swapped out.
RenderViewHostImpl* rvh_impl =
static_cast<RenderViewHostImpl*>(render_view_host);
- if (!rvh_impl->is_swapped_out()) {
+ if (!rvh_impl->IsSwappedOut()) {
CHECK(!ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
render_view_host->GetProcess()->GetID()));
}
}
- return delegate_->CreateRenderViewForRenderManager(render_view_host,
- opener_route_id);
+ return delegate_->CreateRenderViewForRenderManager(
+ render_view_host, opener_route_id, proxy_routing_id, for_main_frame);
}
void RenderFrameHostManager::CommitPending() {
@@ -835,10 +1036,10 @@ void RenderFrameHostManager::CommitPending() {
pending_and_current_web_ui_.reset();
}
- // It's possible for the pending_render_view_host_ to be NULL when we aren't
+ // It's possible for the pending_render_frame_host_ to be NULL when we aren't
// crossing process boundaries. If so, we just needed to handle the Web UI
// committing above and we're done.
- if (!pending_render_view_host_) {
+ if (!pending_render_frame_host_) {
if (will_focus_location_bar)
delegate_->SetFocusToLocationBar(false);
return;
@@ -847,89 +1048,148 @@ void RenderFrameHostManager::CommitPending() {
// Remember if the page was focused so we can focus the new renderer in
// that case.
bool focus_render_view = !will_focus_location_bar &&
- render_view_host_->GetView() && render_view_host_->GetView()->HasFocus();
+ render_frame_host_->render_view_host()->GetView() &&
+ render_frame_host_->render_view_host()->GetView()->HasFocus();
+
+ // TODO(creis): As long as show/hide are on RVH, we don't want to do them for
+ // subframe navigations or they'll interfere with the top-level page.
+ bool is_main_frame = frame_tree_node_->IsMainFrame();
- // Swap in the pending view and make it active. Also ensure the FrameTree
+ // Swap in the pending frame and make it active. Also ensure the FrameTree
// stays in sync.
- RenderViewHostImpl* old_render_view_host = render_view_host_;
- render_view_host_ = pending_render_view_host_;
- pending_render_view_host_ = NULL;
- render_view_host_->AttachToFrameTree();
+ scoped_ptr<RenderFrameHostImpl> old_render_frame_host =
+ SetRenderFrameHost(pending_render_frame_host_.Pass());
+ if (is_main_frame)
+ render_frame_host_->render_view_host()->AttachToFrameTree();
// The process will no longer try to exit, so we can decrement the count.
- render_view_host_->GetProcess()->RemovePendingView();
+ render_frame_host_->GetProcess()->RemovePendingView();
// If the view is gone, then this RenderViewHost died while it was hidden.
// We ignored the RenderProcessGone call at the time, so we should send it now
// to make sure the sad tab shows up, etc.
- if (!render_view_host_->GetView())
- delegate_->RenderProcessGoneFromRenderManager(render_view_host_);
- else if (!delegate_->IsHidden())
- render_view_host_->GetView()->Show();
-
- // Hide the old view now that the new one is visible.
- if (old_render_view_host->GetView()) {
- old_render_view_host->GetView()->Hide();
- old_render_view_host->WasSwappedOut();
+ if (!render_frame_host_->render_view_host()->GetView()) {
+ delegate_->RenderProcessGoneFromRenderManager(
+ render_frame_host_->render_view_host());
+ } else if (!delegate_->IsHidden()) {
+ render_frame_host_->render_view_host()->GetView()->Show();
+ }
+
+ // If the old view is live and top-level, hide it now that the new one is
+ // visible.
+ int32 old_site_instance_id =
+ old_render_frame_host->GetSiteInstance()->GetId();
+ if (old_render_frame_host->render_view_host()->GetView()) {
+ if (is_main_frame) {
+ old_render_frame_host->render_view_host()->GetView()->Hide();
+ old_render_frame_host->render_view_host()->WasSwappedOut(base::Bind(
+ &RenderFrameHostManager::ClearPendingShutdownRFHForSiteInstance,
+ weak_factory_.GetWeakPtr(),
+ old_site_instance_id,
+ old_render_frame_host.get()));
+ } else {
+ // TODO(creis): We'll need to set this back to false if we navigate back.
+ old_render_frame_host->set_swapped_out(true);
+ }
}
// Make sure the size is up to date. (Fix for bug 1079768.)
delegate_->UpdateRenderViewSizeForRenderManager();
- if (will_focus_location_bar)
+ if (will_focus_location_bar) {
delegate_->SetFocusToLocationBar(false);
- else if (focus_render_view && render_view_host_->GetView())
- RenderWidgetHostViewPort::FromRWHV(render_view_host_->GetView())->Focus();
-
- // Notify that we've swapped RenderViewHosts. We do this
- // before shutting down the RVH so that we can clean up
- // RendererResources related to the RVH first.
- delegate_->NotifySwappedFromRenderManager(old_render_view_host,
- render_view_host_);
-
- // If the pending view was on the swapped out list, we can remove it.
- swapped_out_hosts_.erase(render_view_host_->GetSiteInstance()->GetId());
-
- // If there are no active RVHs in this SiteInstance, it means that
- // this RVH was the last active one in the SiteInstance. Now that we
- // know that all RVHs are swapped out, we can delete all the RVHs in
- // this SiteInstance.
- if (!static_cast<SiteInstanceImpl*>(old_render_view_host->GetSiteInstance())->
- active_view_count()) {
- ShutdownRenderViewHostsInSiteInstance(
- old_render_view_host->GetSiteInstance()->GetId());
- // This is deleted while cleaning up the SitaInstance's views.
- old_render_view_host = NULL;
- } else if (old_render_view_host->IsRenderViewLive()) {
- // If the old RVH is live, we are swapping it out and should keep track of
- // it in case we navigate back to it.
- DCHECK(old_render_view_host->is_swapped_out());
- // Temp fix for http://crbug.com/90867 until we do a better cleanup to make
- // sure we don't get different rvh instances for the same site instance
- // in the same rvhmgr.
- // TODO(creis): Clean this up.
- int32 old_site_instance_id =
- old_render_view_host->GetSiteInstance()->GetId();
- RenderViewHostMap::iterator iter =
- swapped_out_hosts_.find(old_site_instance_id);
- if (iter != swapped_out_hosts_.end() &&
- iter->second != old_render_view_host) {
- // Shutdown the RVH that will be replaced in the map to avoid a leak.
- iter->second->Shutdown();
+ } else if (focus_render_view &&
+ render_frame_host_->render_view_host()->GetView()) {
+ render_frame_host_->render_view_host()->GetView()->Focus();
+ }
+
+ // Notify that we've swapped RenderFrameHosts. We do this before shutting down
+ // the RFH so that we can clean up RendererResources related to the RFH first.
+ // TODO(creis): Only do this on top-level RFHs for now, and later update it to
+ // pass the RFHs.
+ if (is_main_frame) {
+ delegate_->NotifySwappedFromRenderManager(
+ old_render_frame_host->render_view_host(),
+ render_frame_host_->render_view_host());
+ }
+
+ // If the old RFH is not live, just return as there is no work to do.
+ if (!old_render_frame_host->render_view_host()->IsRenderViewLive()) {
+ return;
+ }
+
+ // If the old RFH is live, we are swapping it out and should keep track of
+ // it in case we navigate back to it, or it is waiting for the unload event
+ // to execute in the background.
+ // TODO(creis): Swap out the subframe in --site-per-process.
+ if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess))
+ DCHECK(old_render_frame_host->is_swapped_out() ||
+ !RenderViewHostImpl::IsRVHStateActive(
+ old_render_frame_host->render_view_host()->rvh_state()));
+
+ // If the RenderViewHost backing the RenderFrameHost is pending shutdown,
+ // the RenderFrameHost should be put in the map of RenderFrameHosts pending
+ // shutdown. Otherwise, it is stored in the map of proxy hosts.
+ if (old_render_frame_host->render_view_host()->rvh_state() ==
+ RenderViewHostImpl::STATE_PENDING_SHUTDOWN) {
+ // The proxy for this RenderFrameHost is created when sending the
+ // SwapOut message, so check if it already exists and delete it.
+ RenderFrameProxyHostMap::iterator iter =
+ proxy_hosts_.find(old_site_instance_id);
+ if (iter != proxy_hosts_.end()) {
+ delete iter->second;
+ proxy_hosts_.erase(iter);
+ }
+ RFHPendingDeleteMap::iterator pending_delete_iter =
+ pending_delete_hosts_.find(old_site_instance_id);
+ if (pending_delete_iter == pending_delete_hosts_.end() ||
+ pending_delete_iter->second.get() != old_render_frame_host) {
+ pending_delete_hosts_[old_site_instance_id] =
+ linked_ptr<RenderFrameHostImpl>(old_render_frame_host.release());
}
- swapped_out_hosts_[old_site_instance_id] = old_render_view_host;
} else {
- old_render_view_host->Shutdown();
- old_render_view_host = NULL; // Shutdown() deletes it.
+ // Capture the active view count on the old RFH SiteInstance, since the
+ // ownership will be passed into the proxy and the pointer will be invalid.
+ int active_view_count =
+ static_cast<SiteInstanceImpl*>(old_render_frame_host->GetSiteInstance())
+ ->active_view_count();
+
+ RenderFrameProxyHostMap::iterator iter =
+ proxy_hosts_.find(old_site_instance_id);
+ CHECK(iter != proxy_hosts_.end());
+ iter->second->TakeFrameHostOwnership(old_render_frame_host.Pass());
+
+ // If there are no active views in this SiteInstance, it means that
+ // this RFH was the last active one in the SiteInstance. Now that we
+ // know that all RFHs are swapped out, we can delete all the RFHs and RVHs
+ // in this SiteInstance.
+ if (!active_view_count) {
+ ShutdownRenderFrameHostsInSiteInstance(old_site_instance_id);
+ } else {
+ // If this is a subframe, it should have a CrossProcessFrameConnector
+ // created already and we just need to link it to the proper view in the
+ // new process.
+ if (!is_main_frame) {
+ RenderFrameProxyHost* proxy = GetProxyToParent();
+ if (proxy) {
+ proxy->SetChildRWHView(
+ render_frame_host_->render_view_host()->GetView());
+ }
+ }
+ }
}
}
-void RenderFrameHostManager::ShutdownRenderViewHostsInSiteInstance(
+void RenderFrameHostManager::ShutdownRenderFrameHostsInSiteInstance(
int32 site_instance_id) {
- // First remove any swapped out RVH for this SiteInstance from our
- // list.
- swapped_out_hosts_.erase(site_instance_id);
-
+ // First remove any swapped out RFH for this SiteInstance from our own list.
+ ClearProxiesInSiteInstance(site_instance_id, frame_tree_node_);
+
+ // Use the safe RenderWidgetHost iterator for now to find all RenderViewHosts
+ // in the SiteInstance, then tell their respective FrameTrees to remove all
+ // RenderFrameProxyHosts corresponding to them.
+ // TODO(creis): Replace this with a RenderFrameHostIterator that protects
+ // against use-after-frees if a later element is deleted before getting to it.
scoped_ptr<RenderWidgetHostIterator> widgets(
RenderWidgetHostImpl::GetAllRenderWidgetHosts());
while (RenderWidgetHost* widget = widgets->GetNextHost()) {
@@ -937,25 +1197,31 @@ void RenderFrameHostManager::ShutdownRenderViewHostsInSiteInstance(
continue;
RenderViewHostImpl* rvh =
static_cast<RenderViewHostImpl*>(RenderViewHost::From(widget));
- if (site_instance_id == rvh->GetSiteInstance()->GetId())
- rvh->Shutdown();
+ if (site_instance_id == rvh->GetSiteInstance()->GetId()) {
+ // This deletes all RenderFrameHosts using the |rvh|, which then causes
+ // |rvh| to Shutdown.
+ FrameTree* tree = rvh->GetDelegate()->GetFrameTree();
+ tree->ForEach(base::Bind(
+ &RenderFrameHostManager::ClearProxiesInSiteInstance,
+ site_instance_id));
+ }
}
}
-RenderViewHostImpl* RenderFrameHostManager::UpdateRendererStateForNavigate(
+RenderFrameHostImpl* RenderFrameHostManager::UpdateStateForNavigate(
const NavigationEntryImpl& entry) {
// If we are currently navigating cross-process, we want to get back to normal
// and then navigate as usual.
if (cross_navigation_pending_) {
- if (pending_render_view_host_)
+ if (pending_render_frame_host_)
CancelPending();
cross_navigation_pending_ = false;
}
- // render_view_host_'s SiteInstance and new_instance will not be deleted
+ // render_frame_host_'s SiteInstance and new_instance will not be deleted
// before the end of this method, so we don't have to worry about their ref
// counts dropping to zero.
- SiteInstance* current_instance = render_view_host_->GetSiteInstance();
+ SiteInstance* current_instance = render_frame_host_->GetSiteInstance();
SiteInstance* new_instance = current_instance;
// We do not currently swap processes for navigations in webview tag guests.
@@ -963,7 +1229,7 @@ RenderViewHostImpl* RenderFrameHostManager::UpdateRendererStateForNavigate(
// Determine if we need a new BrowsingInstance for this entry. If true, this
// implies that it will get a new SiteInstance (and likely process), and that
- // other tabs in the current BrosingInstance will be unalbe to script it.
+ // other tabs in the current BrowsingInstance will be unable to script it.
// This is used for cases that require a process swap even in the
// process-per-tab model, such as WebUI pages.
const NavigationEntry* current_entry =
@@ -974,13 +1240,13 @@ RenderViewHostImpl* RenderFrameHostManager::UpdateRendererStateForNavigate(
new_instance = GetSiteInstanceForEntry(entry, current_instance, force_swap);
// If force_swap is true, we must use a different SiteInstance. If we didn't,
- // we would have two RenderViewHosts in the same SiteInstance and the same
- // tab, resulting in page_id conflicts for their NavigationEntries.
+ // we would have two RenderFrameHosts in the same SiteInstance and the same
+ // frame, resulting in page_id conflicts for their NavigationEntries.
if (force_swap)
CHECK_NE(new_instance, current_instance);
if (new_instance != current_instance) {
- // New SiteInstance: create a pending RVH to navigate.
+ // New SiteInstance: create a pending RFH to navigate.
DCHECK(!cross_navigation_pending_);
// This will possibly create (set to NULL) a Web UI object for the pending
@@ -991,8 +1257,8 @@ RenderViewHostImpl* RenderFrameHostManager::UpdateRendererStateForNavigate(
// not have its bindings set appropriately.
SetPendingWebUI(entry);
- // Ensure that we have created RVHs for the new RVH's opener chain if
- // we are staying in the same BrowsingInstance. This allows the pending RVH
+ // Ensure that we have created RFHs for the new RFH's opener chain if
+ // we are staying in the same BrowsingInstance. This allows the pending RFH
// to send cross-process script calls to its opener(s).
int opener_route_id = MSG_ROUTING_NONE;
if (new_instance->IsRelatedSiteInstance(current_instance)) {
@@ -1000,26 +1266,26 @@ RenderViewHostImpl* RenderFrameHostManager::UpdateRendererStateForNavigate(
delegate_->CreateOpenerRenderViewsForRenderManager(new_instance);
}
- // Create a non-swapped-out pending RVH with the given opener and navigate
+ // Create a non-swapped-out pending RFH with the given opener and navigate
// it.
- int route_id = CreateRenderView(new_instance, opener_route_id, false,
- delegate_->IsHidden());
+ int route_id = CreateRenderFrame(new_instance, opener_route_id, false,
+ delegate_->IsHidden());
if (route_id == MSG_ROUTING_NONE)
return NULL;
- // Check if our current RVH is live before we set up a transition.
- if (!render_view_host_->IsRenderViewLive()) {
+ // Check if our current RFH is live before we set up a transition.
+ if (!render_frame_host_->render_view_host()->IsRenderViewLive()) {
if (!cross_navigation_pending_) {
- // The current RVH is not live. There's no reason to sit around with a
- // sad tab or a newly created RVH while we wait for the pending RVH to
- // navigate. Just switch to the pending RVH now and go back to non
+ // The current RFH is not live. There's no reason to sit around with a
+ // sad tab or a newly created RFH while we wait for the pending RFH to
+ // navigate. Just switch to the pending RFH now and go back to non
// cross-navigating (Note that we don't care about on{before}unload
- // handlers if the current RVH isn't live.)
+ // handlers if the current RFH isn't live.)
CommitPending();
- return render_view_host_;
+ return render_frame_host_.get();
} else {
NOTREACHED();
- return render_view_host_;
+ return render_frame_host_.get();
}
}
// Otherwise, it's safe to treat this as a pending cross-site transition.
@@ -1030,7 +1296,8 @@ RenderViewHostImpl* RenderFrameHostManager::UpdateRendererStateForNavigate(
// Navigate message) until we hear back from the old renderer's
// beforeunload handler. If the handler returns false, we'll have to
// cancel the request.
- DCHECK(!pending_render_view_host_->are_navigations_suspended());
+ DCHECK(!pending_render_frame_host_->render_view_host()->
+ are_navigations_suspended());
bool is_transfer =
entry.transferred_global_request_id() != GlobalRequestID();
if (is_transfer) {
@@ -1042,33 +1309,35 @@ RenderViewHostImpl* RenderFrameHostManager::UpdateRendererStateForNavigate(
// Also make sure the old render view stops, in case a load is in
// progress. (We don't want to do this for transfers, since it will
// interrupt the transfer with an unexpected DidStopLoading.)
- render_view_host_->Send(
- new ViewMsg_Stop(render_view_host_->GetRoutingID()));
+ render_frame_host_->render_view_host()->Send(new ViewMsg_Stop(
+ render_frame_host_->render_view_host()->GetRoutingID()));
- pending_render_view_host_->SetNavigationsSuspended(true,
- base::TimeTicks());
+ pending_render_frame_host_->render_view_host()->SetNavigationsSuspended(
+ true, base::TimeTicks());
// Tell the CrossSiteRequestManager that this RVH has a pending cross-site
// request, so that ResourceDispatcherHost will know to tell us to run the
// old page's unload handler before it sends the response.
- pending_render_view_host_->SetHasPendingCrossSiteRequest(true);
+ // TODO(creis): This needs to be on the RFH.
+ pending_render_frame_host_->render_view_host()->
+ SetHasPendingCrossSiteRequest(true);
}
- // We now have a pending RVH.
+ // We now have a pending RFH.
DCHECK(!cross_navigation_pending_);
cross_navigation_pending_ = true;
// Unless we are transferring an existing request, we should now
// tell the old render view to run its beforeunload handler, since it
// doesn't otherwise know that the cross-site request is happening. This
- // will trigger a call to ShouldClosePage with the reply.
+ // will trigger a call to OnBeforeUnloadACK with the reply.
if (!is_transfer)
- render_view_host_->FirePageBeforeUnload(true);
+ render_frame_host_->DispatchBeforeUnload(true);
- return pending_render_view_host_;
+ return pending_render_frame_host_.get();
}
- // Otherwise the same SiteInstance can be used. Navigate render_view_host_.
+ // Otherwise the same SiteInstance can be used. Navigate render_frame_host_.
DCHECK(!cross_navigation_pending_);
if (ShouldReuseWebUI(current_entry, &entry)) {
pending_web_ui_.reset();
@@ -1077,96 +1346,123 @@ RenderViewHostImpl* RenderFrameHostManager::UpdateRendererStateForNavigate(
SetPendingWebUI(entry);
// Make sure the new RenderViewHost has the right bindings.
- if (pending_web_ui() && !render_view_host_->GetProcess()->IsGuest())
- render_view_host_->AllowBindings(pending_web_ui()->GetBindings());
+ if (pending_web_ui() &&
+ !render_frame_host_->GetProcess()->IsIsolatedGuest()) {
+ render_frame_host_->render_view_host()->AllowBindings(
+ pending_web_ui()->GetBindings());
+ }
}
- if (pending_web_ui() && render_view_host_->IsRenderViewLive())
- pending_web_ui()->GetController()->RenderViewReused(render_view_host_);
+ if (pending_web_ui() &&
+ render_frame_host_->render_view_host()->IsRenderViewLive()) {
+ pending_web_ui()->GetController()->RenderViewReused(
+ render_frame_host_->render_view_host());
+ }
// The renderer can exit view source mode when any error or cancellation
// happen. We must overwrite to recover the mode.
if (entry.IsViewSourceMode()) {
- render_view_host_->Send(
- new ViewMsg_EnableViewSourceMode(render_view_host_->GetRoutingID()));
+ render_frame_host_->render_view_host()->Send(
+ new ViewMsg_EnableViewSourceMode(
+ render_frame_host_->render_view_host()->GetRoutingID()));
}
- return render_view_host_;
+ return render_frame_host_.get();
}
void RenderFrameHostManager::CancelPending() {
- RenderViewHostImpl* pending_render_view_host = pending_render_view_host_;
- pending_render_view_host_ = NULL;
+ scoped_ptr<RenderFrameHostImpl> pending_render_frame_host =
+ pending_render_frame_host_.Pass();
RenderViewDevToolsAgentHost::OnCancelPendingNavigation(
- pending_render_view_host,
- render_view_host_);
+ pending_render_frame_host->render_view_host(),
+ render_frame_host_->render_view_host());
// We no longer need to prevent the process from exiting.
- pending_render_view_host->GetProcess()->RemovePendingView();
+ pending_render_frame_host->GetProcess()->RemovePendingView();
- // The pending RVH may already be on the swapped out list if we started to
- // swap it back in and then canceled. If so, make sure it gets swapped out
- // again. If it's not on the swapped out list (e.g., aborting a pending
- // load), then it's safe to shut down.
- if (IsOnSwappedOutList(pending_render_view_host)) {
+ // If the SiteInstance for the pending RFH is being used by others, don't
+ // delete the RFH, just swap it out and it can be reused at a later point.
+ SiteInstanceImpl* site_instance = static_cast<SiteInstanceImpl*>(
+ pending_render_frame_host->GetSiteInstance());
+ if (site_instance->active_view_count() > 1) {
// Any currently suspended navigations are no longer needed.
- pending_render_view_host->CancelSuspendedNavigations();
+ pending_render_frame_host->render_view_host()->CancelSuspendedNavigations();
- pending_render_view_host->SwapOut();
+ RenderFrameProxyHost* proxy =
+ new RenderFrameProxyHost(site_instance, frame_tree_node_);
+ proxy_hosts_[site_instance->GetId()] = proxy;
+ pending_render_frame_host->SwapOut(proxy);
+ proxy->TakeFrameHostOwnership(pending_render_frame_host.Pass());
} else {
- // We won't be coming back, so shut this one down.
- pending_render_view_host->Shutdown();
+ // We won't be coming back, so delete this one.
+ pending_render_frame_host.reset();
}
pending_web_ui_.reset();
pending_and_current_web_ui_.reset();
}
-void RenderFrameHostManager::RenderViewDeleted(RenderViewHost* rvh) {
- // We are doing this in order to work around and to track a crasher
- // (http://crbug.com/23411) where it seems that pending_render_view_host_ is
- // deleted (not sure from where) but not NULLed.
- if (rvh == pending_render_view_host_) {
- // If you hit this NOTREACHED, please report it in the following bug
- // http://crbug.com/23411 Make sure to include what you were doing when it
- // happened (navigating to a new page, closing a tab...) and if you can
- // reproduce.
- NOTREACHED();
- pending_render_view_host_ = NULL;
- }
-
- // Make sure deleted RVHs are not kept in the swapped out list while we are
- // still alive. (If render_view_host_ is null, we're already being deleted.)
- if (!render_view_host_)
- return;
- // We can't look it up by SiteInstance ID, which may no longer be valid.
- for (RenderViewHostMap::iterator iter = swapped_out_hosts_.begin();
- iter != swapped_out_hosts_.end();
- ++iter) {
- if (iter->second == rvh) {
- swapped_out_hosts_.erase(iter);
- break;
+scoped_ptr<RenderFrameHostImpl> RenderFrameHostManager::SetRenderFrameHost(
+ scoped_ptr<RenderFrameHostImpl> render_frame_host) {
+ // Swap the two.
+ scoped_ptr<RenderFrameHostImpl> old_render_frame_host =
+ render_frame_host_.Pass();
+ render_frame_host_ = render_frame_host.Pass();
+
+ if (frame_tree_node_->IsMainFrame()) {
+ // Update the count of top-level frames using this SiteInstance. All
+ // subframes are in the same BrowsingInstance as the main frame, so we only
+ // count top-level ones. This makes the value easier for consumers to
+ // interpret.
+ if (render_frame_host_) {
+ static_cast<SiteInstanceImpl*>(render_frame_host_->GetSiteInstance())->
+ IncrementRelatedActiveContentsCount();
+ }
+ if (old_render_frame_host) {
+ static_cast<SiteInstanceImpl*>(old_render_frame_host->GetSiteInstance())->
+ DecrementRelatedActiveContentsCount();
}
}
+
+ return old_render_frame_host.Pass();
}
-bool RenderFrameHostManager::IsOnSwappedOutList(RenderViewHost* rvh) const {
- if (!rvh->GetSiteInstance())
+bool RenderFrameHostManager::IsRVHOnSwappedOutList(
+ RenderViewHostImpl* rvh) const {
+ RenderFrameProxyHost* proxy = GetRenderFrameProxyHost(
+ rvh->GetSiteInstance());
+ if (!proxy)
+ return false;
+ return IsOnSwappedOutList(proxy->render_frame_host());
+}
+
+bool RenderFrameHostManager::IsOnSwappedOutList(
+ RenderFrameHostImpl* rfh) const {
+ if (!rfh->GetSiteInstance())
return false;
- RenderViewHostMap::const_iterator iter = swapped_out_hosts_.find(
- rvh->GetSiteInstance()->GetId());
- if (iter == swapped_out_hosts_.end())
+ RenderFrameProxyHostMap::const_iterator iter = proxy_hosts_.find(
+ rfh->GetSiteInstance()->GetId());
+ if (iter == proxy_hosts_.end())
return false;
- return iter->second == rvh;
+ return iter->second->render_frame_host() == rfh;
}
RenderViewHostImpl* RenderFrameHostManager::GetSwappedOutRenderViewHost(
- SiteInstance* instance) {
- RenderViewHostMap::iterator iter = swapped_out_hosts_.find(instance->GetId());
- if (iter != swapped_out_hosts_.end())
+ SiteInstance* instance) const {
+ RenderFrameProxyHost* proxy = GetRenderFrameProxyHost(instance);
+ if (proxy)
+ return proxy->GetRenderViewHost();
+ return NULL;
+}
+
+RenderFrameProxyHost* RenderFrameHostManager::GetRenderFrameProxyHost(
+ SiteInstance* instance) const {
+ RenderFrameProxyHostMap::const_iterator iter =
+ proxy_hosts_.find(instance->GetId());
+ if (iter != proxy_hosts_.end())
return iter->second;
return NULL;
diff --git a/chromium/content/browser/frame_host/render_frame_host_manager.h b/chromium/content/browser/frame_host/render_frame_host_manager.h
index 94a4998b62f..35abab1668d 100644
--- a/chromium/content/browser/frame_host/render_frame_host_manager.h
+++ b/chromium/content/browser/frame_host/render_frame_host_manager.h
@@ -12,6 +12,7 @@
#include "content/browser/renderer_host/render_view_host_delegate.h"
#include "content/browser/site_instance_impl.h"
#include "content/common/content_export.h"
+#include "content/public/browser/global_request_id.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/common/referrer.h"
@@ -19,12 +20,17 @@
namespace content {
class BrowserContext;
+class CrossProcessFrameConnector;
+class CrossSiteTransferringRequest;
class InterstitialPageImpl;
+class FrameTreeNode;
class NavigationControllerImpl;
class NavigationEntry;
class NavigationEntryImpl;
class RenderFrameHostDelegate;
+class RenderFrameHostImpl;
class RenderFrameHostManagerTest;
+class RenderFrameProxyHost;
class RenderViewHost;
class RenderViewHostImpl;
class RenderWidgetHostDelegate;
@@ -34,9 +40,7 @@ class WebUIImpl;
// Manages RenderFrameHosts for a FrameTreeNode. This class acts as a state
// machine to make cross-process navigations in a frame possible.
-class CONTENT_EXPORT RenderFrameHostManager
- : public RenderViewHostDelegate::RendererManagement,
- public NotificationObserver {
+class CONTENT_EXPORT RenderFrameHostManager : public NotificationObserver {
public:
// Functions implemented by our owner that we need.
//
@@ -53,12 +57,14 @@ class CONTENT_EXPORT RenderFrameHostManager
// corresponding to this view host. If this method is not called and the
// process is not shared, then the WebContentsImpl will act as though the
// renderer is not running (i.e., it will render "sad tab"). This method is
- // automatically called from LoadURL.
- //
- // If you are attaching to an already-existing RenderView, you should call
- // InitWithExistingID.
+ // automatically called from LoadURL. |for_main_frame| indicates whether
+ // this RenderViewHost is used to render a top-level frame, so the
+ // appropriate RenderWidgetHostView type is used.
virtual bool CreateRenderViewForRenderManager(
- RenderViewHost* render_view_host, int opener_route_id) = 0;
+ RenderViewHost* render_view_host,
+ int opener_route_id,
+ int proxy_routing_id,
+ bool for_main_frame) = 0;
virtual void BeforeUnloadFiredFromRenderManager(
bool proceed, const base::TimeTicks& proceed_time,
bool* proceed_to_fire_unload) = 0;
@@ -107,6 +113,11 @@ class CONTENT_EXPORT RenderFrameHostManager
virtual ~Delegate() {}
};
+ // Used with FrameTree::ForEach to delete RenderFrameHosts pending shutdown
+ // from a FrameTreeNode's RenderFrameHostManager. Used during destruction of
+ // WebContentsImpl.
+ static bool ClearRFHsPendingShutdown(FrameTreeNode* node);
+
// All three delegate pointers must be non-NULL and are not owned by this
// class. They must outlive this class. The RenderViewHostDelegate and
// RenderWidgetHostDelegate are what will be installed into all
@@ -114,6 +125,7 @@ class CONTENT_EXPORT RenderFrameHostManager
//
// You must call Init() before using this class.
RenderFrameHostManager(
+ FrameTreeNode* frame_tree_node,
RenderFrameHostDelegate* render_frame_delegate,
RenderViewHostDelegate* render_view_delegate,
RenderWidgetHostDelegate* render_widget_delegate,
@@ -123,21 +135,33 @@ class CONTENT_EXPORT RenderFrameHostManager
// For arguments, see WebContentsImpl constructor.
void Init(BrowserContext* browser_context,
SiteInstance* site_instance,
- int routing_id,
- int main_frame_routing_id);
+ int view_routing_id,
+ int frame_routing_id);
- // Returns the currently active RenderViewHost.
+ // Returns the currently active RenderFrameHost.
//
// This will be non-NULL between Init() and Shutdown(). You may want to NULL
// check it in many cases, however. Windows can send us messages during the
// destruction process after it has been shut down.
+ RenderFrameHostImpl* current_frame_host() const {
+ return render_frame_host_.get();
+ }
+
+ // TODO(creis): Remove this when we no longer use RVH for navigation.
RenderViewHostImpl* current_host() const;
// Returns the view associated with the current RenderViewHost, or NULL if
// there is no current one.
RenderWidgetHostView* GetRenderWidgetHostView() const;
- // Returns the pending render view host, or NULL if there is no pending one.
+ RenderFrameProxyHost* GetProxyToParent();
+
+ // Returns the pending RenderFrameHost, or NULL if there is no pending one.
+ RenderFrameHostImpl* pending_frame_host() const {
+ return pending_render_frame_host_.get();
+ }
+
+ // TODO(creis): Remove this when we no longer use RVH for navigation.
RenderViewHostImpl* pending_render_view_host() const;
// Returns the current committed Web UI or NULL if none applies.
@@ -154,10 +178,10 @@ class CONTENT_EXPORT RenderFrameHostManager
void SetPendingWebUI(const NavigationEntryImpl& entry);
// Called when we want to instruct the renderer to navigate to the given
- // navigation entry. It may create a new RenderViewHost or re-use an existing
- // one. The RenderViewHost to navigate will be returned. Returns NULL if one
+ // navigation entry. It may create a new RenderFrameHost or re-use an existing
+ // one. The RenderFrameHost to navigate will be returned. Returns NULL if one
// could not be created.
- RenderViewHostImpl* Navigate(const NavigationEntryImpl& entry);
+ RenderFrameHostImpl* Navigate(const NavigationEntryImpl& entry);
// Instructs the various live views to stop. Called when the user directed the
// page to stop loading.
@@ -173,26 +197,47 @@ class CONTENT_EXPORT RenderFrameHostManager
// with the navigation instead of closing the tab.
bool ShouldCloseTabOnUnresponsiveRenderer();
- // The RenderViewHost has been swapped out, so we should resume the pending
- // network response and allow the pending RenderViewHost to commit.
- void SwappedOut(RenderViewHost* render_view_host);
+ // Confirms whether we should close the page or navigate away. This is called
+ // before a cross-site request or before a tab/window is closed (as indicated
+ // by the first parameter) to allow the appropriate renderer to approve or
+ // deny the request. |proceed| indicates whether the user chose to proceed.
+ // |proceed_time| is the time when the request was allowed to proceed.
+ void OnBeforeUnloadACK(bool for_cross_site_transition,
+ bool proceed,
+ const base::TimeTicks& proceed_time);
+
+ // The |pending_render_frame_host| is ready to commit a page. We should
+ // ensure that the old RenderFrameHost runs its unload handler first and
+ // determine whether a RenderFrameHost transfer is needed.
+ // |cross_site_transferring_request| is NULL if a request is not being
+ // transferred between renderers.
+ void OnCrossSiteResponse(
+ RenderFrameHostImpl* pending_render_frame_host,
+ const GlobalRequestID& global_request_id,
+ scoped_ptr<CrossSiteTransferringRequest> cross_site_transferring_request,
+ const std::vector<GURL>& transfer_url_chain,
+ const Referrer& referrer,
+ PageTransition page_transition,
+ bool should_replace_current_entry);
+
+ // The RenderFrameHost has been swapped out, so we should resume the pending
+ // network response and allow the pending RenderFrameHost to commit.
+ void SwappedOut(RenderFrameHostImpl* render_frame_host);
- // Called when a renderer's main frame navigates.
- void DidNavigateMainFrame(RenderViewHost* render_view_host);
+ // Called when a renderer's frame navigates.
+ void DidNavigateFrame(RenderFrameHostImpl* render_frame_host);
// Called when a renderer sets its opener to null.
void DidDisownOpener(RenderViewHost* render_view_host);
- // Helper method to create a RenderViewHost. If |swapped_out| is true, it
- // will be initially placed on the swapped out hosts list. Otherwise, it
- // will be used for a pending cross-site navigation.
- int CreateRenderView(SiteInstance* instance,
- int opener_route_id,
- bool swapped_out,
- bool hidden);
-
- // Called when a provisional load on the given renderer is aborted.
- void RendererAbortedProvisionalLoad(RenderViewHost* render_view_host);
+ // Helper method to create and initialize a RenderFrameHost. If |swapped_out|
+ // is true, it will be initially placed on the swapped out hosts list.
+ // Otherwise, it will be used for a pending cross-site navigation.
+ // Returns the routing id of the *view* associated with the frame.
+ int CreateRenderFrame(SiteInstance* instance,
+ int opener_route_id,
+ bool swapped_out,
+ bool hidden);
// Sets the passed passed interstitial as the currently showing interstitial.
// |interstitial_page| should be non NULL (use the remove_interstitial_page
@@ -213,88 +258,90 @@ class CONTENT_EXPORT RenderFrameHostManager
// showing.
InterstitialPageImpl* interstitial_page() const { return interstitial_page_; }
- // RenderViewHostDelegate::RendererManagement implementation.
- virtual void ShouldClosePage(
- bool for_cross_site_transition,
- bool proceed,
- const base::TimeTicks& proceed_time) OVERRIDE;
- virtual void OnCrossSiteResponse(
- RenderViewHost* pending_render_view_host,
- const GlobalRequestID& global_request_id,
- bool is_transfer,
- const std::vector<GURL>& transfer_url_chain,
- const Referrer& referrer,
- PageTransition page_transition,
- int64 frame_id,
- bool should_replace_current_entry) OVERRIDE;
-
// NotificationObserver implementation.
virtual void Observe(int type,
const NotificationSource& source,
const NotificationDetails& details) OVERRIDE;
- // Called when a RenderViewHost is about to be deleted.
- void RenderViewDeleted(RenderViewHost* rvh);
-
- // Returns whether the given RenderViewHost is on the list of swapped out
- // RenderViewHosts.
- bool IsOnSwappedOutList(RenderViewHost* rvh) const;
+ // Returns whether the given RenderFrameHost (or its associated
+ // RenderViewHost) is on the list of swapped out RenderFrameHosts.
+ bool IsRVHOnSwappedOutList(RenderViewHostImpl* rvh) const;
+ bool IsOnSwappedOutList(RenderFrameHostImpl* rfh) const;
- // Returns the swapped out RenderViewHost for the given SiteInstance, if any.
- RenderViewHostImpl* GetSwappedOutRenderViewHost(SiteInstance* instance);
+ // Returns the swapped out RenderViewHost or RenderFrameHost for the given
+ // SiteInstance, if any. This method is *deprecated* and
+ // GetRenderFrameProxyHost should be used.
+ RenderViewHostImpl* GetSwappedOutRenderViewHost(SiteInstance* instance) const;
+ RenderFrameProxyHost* GetRenderFrameProxyHost(
+ SiteInstance* instance) const;
// Runs the unload handler in the current page, when we know that a pending
// cross-process navigation is going to commit. We may initiate a transfer
// to a new process after this completes or times out.
void SwapOutOldPage();
+ // Deletes a RenderFrameHost that was pending shutdown.
+ void ClearPendingShutdownRFHForSiteInstance(int32 site_instance_id,
+ RenderFrameHostImpl* rfh);
+
+ // Deletes any proxy hosts associated with this node. Used during destruction
+ // of WebContentsImpl.
+ void ResetProxyHosts();
+
private:
friend class RenderFrameHostManagerTest;
friend class TestWebContents;
// Tracks information about a navigation while a cross-process transition is
- // in progress, in case we need to transfer it to a new RenderViewHost.
+ // in progress, in case we need to transfer it to a new RenderFrameHost.
+ // When a request is being transferred, deleting the PendingNavigationParams,
+ // and thus |cross_site_transferring_request|, will cancel the request being
+ // transferred, unless its ReleaseRequest method has been called.
struct PendingNavigationParams {
- PendingNavigationParams();
- PendingNavigationParams(const GlobalRequestID& global_request_id,
- bool is_transfer,
- const std::vector<GURL>& transfer_url,
- Referrer referrer,
- PageTransition page_transition,
- int64 frame_id,
- bool should_replace_current_entry);
+ PendingNavigationParams(
+ const GlobalRequestID& global_request_id,
+ scoped_ptr<CrossSiteTransferringRequest>
+ cross_site_transferring_request,
+ const std::vector<GURL>& transfer_url,
+ Referrer referrer,
+ PageTransition page_transition,
+ int render_frame_id,
+ bool should_replace_current_entry);
~PendingNavigationParams();
// The child ID and request ID for the pending navigation. Present whether
- // |is_transfer| is true or false.
+ // |request_transfer| is NULL or not.
GlobalRequestID global_request_id;
- // Whether this pending navigation needs to be transferred to another
- // process than the one it was going to commit in. If so, the
- // |transfer_url|, |referrer|, and |frame_id| parameters will be set.
- bool is_transfer;
+ // If a pending request needs to be transferred to another process, this
+ // owns the request until it's transferred to the new process, so it will be
+ // cleaned up if the navigation is cancelled. Otherwise, this is NULL.
+ scoped_ptr<CrossSiteTransferringRequest> cross_site_transferring_request;
+
+ // If |request_transfer| is non-NULL, the values below are all set.
- // If |is_transfer|, this is the URL chain of the request. The first entry
- // is the original request URL, and the last entry is the destination URL to
- // request in the new process.
+ // The first entry is the original request URL, and the last entry is the
+ // destination URL to request in the new process.
std::vector<GURL> transfer_url_chain;
- // If |is_transfer|, this is the referrer to use for the request in the new
- // process.
+ // This is the referrer to use for the request in the new process.
Referrer referrer;
- // If |is_transfer|, this is the transition type for the original
- // navigation.
+ // This is the transition type for the original navigation.
PageTransition page_transition;
- // If |is_transfer|, this is the frame ID to use in RequestTransferURL.
- int64 frame_id;
+ // This is the frame routing ID to use in RequestTransferURL.
+ int render_frame_id;
- // If |is_transfer|, this is whether the navigation should replace the
- // current history entry.
+ // This is whether the navigation should replace the current history entry.
bool should_replace_current_entry;
};
+ // Used with FrameTree::ForEach to erase RenderFrameProxyHosts from a
+ // FrameTreeNode's RenderFrameHostManager.
+ static bool ClearProxiesInSiteInstance(int32 site_instance_id,
+ FrameTreeNode* node);
+
// Returns whether this tab should transition to a new renderer for
// cross-site URLs. Enabled unless we see the --process-per-tab command line
// switch. Can be overridden in unit tests.
@@ -325,60 +372,86 @@ class CONTENT_EXPORT RenderFrameHostManager
SiteInstance* current_instance,
bool force_browsing_instance_swap);
- // Sets up the necessary state for a new RenderViewHost with the given opener.
- bool InitRenderView(RenderViewHost* render_view_host, int opener_route_id);
-
- // Sets the pending RenderViewHost/WebUI to be the active one. Note that this
- // doesn't require the pending render_view_host_ pointer to be non-NULL, since
- // there could be Web UI switching as well. Call this for every commit.
+ // Creates a RenderFrameHost and corresponding RenderViewHost if necessary.
+ scoped_ptr<RenderFrameHostImpl> CreateRenderFrameHost(SiteInstance* instance,
+ int view_routing_id,
+ int frame_routing_id,
+ bool swapped_out,
+ bool hidden);
+
+ // Sets up the necessary state for a new RenderViewHost with the given opener,
+ // if necessary. It creates a RenderFrameProxy in the target renderer process
+ // with the given |proxy_routing_id|, which is used to route IPC messages when
+ // in swapped out state. Returns early if the RenderViewHost has already been
+ // initialized for another RenderFrameHost.
+ // TODO(creis): opener_route_id is currently for the RenderViewHost but should
+ // be for the RenderFrame, since frames can have openers.
+ bool InitRenderView(RenderViewHost* render_view_host,
+ int opener_route_id,
+ int proxy_routing_id,
+ bool for_main_frame);
+
+ // Sets the pending RenderFrameHost/WebUI to be the active one. Note that this
+ // doesn't require the pending render_frame_host_ pointer to be non-NULL,
+ // since there could be Web UI switching as well. Call this for every commit.
void CommitPending();
- // Shutdown all RenderViewHosts in a SiteInstance. This is called
- // to shutdown views when all the views in a SiteInstance are
- // confirmed to be swapped out.
- void ShutdownRenderViewHostsInSiteInstance(int32 site_instance_id);
+ // Shutdown all RenderFrameHosts in a SiteInstance. This is called to shutdown
+ // frames when all the frames in a SiteInstance are confirmed to be swapped
+ // out.
+ void ShutdownRenderFrameHostsInSiteInstance(int32 site_instance_id);
// Helper method to terminate the pending RenderViewHost.
void CancelPending();
- RenderViewHostImpl* UpdateRendererStateForNavigate(
+ // Helper method to set the active RenderFrameHost. Returns the old
+ // RenderFrameHost and updates counts.
+ scoped_ptr<RenderFrameHostImpl> SetRenderFrameHost(
+ scoped_ptr<RenderFrameHostImpl> render_frame_host);
+
+ RenderFrameHostImpl* UpdateStateForNavigate(
const NavigationEntryImpl& entry);
// Called when a renderer process is starting to close. We should not
- // schedule new navigations in its swapped out RenderViewHosts after this.
+ // schedule new navigations in its swapped out RenderFrameHosts after this.
void RendererProcessClosing(RenderProcessHost* render_process_host);
+ // For use in creating RenderFrameHosts.
+ FrameTreeNode* frame_tree_node_;
+
// Our delegate, not owned by us. Guaranteed non-NULL.
Delegate* delegate_;
- // Whether a navigation requiring different RenderView's is pending. This is
- // either cross-site request is (in the new process model), or when required
- // for the view type (like view source versus not).
+ // Whether a navigation requiring different RenderFrameHosts is pending. This
+ // is either for cross-site requests or when required for the process type
+ // (like WebUI).
bool cross_navigation_pending_;
- // Implemented by the owner of this class, these delegates are installed into
- // all the RenderViewHosts that we create.
+ // Implemented by the owner of this class. These delegates are installed into
+ // all the RenderFrameHosts that we create.
RenderFrameHostDelegate* render_frame_delegate_;
RenderViewHostDelegate* render_view_delegate_;
RenderWidgetHostDelegate* render_widget_delegate_;
- // Our RenderView host and its associated Web UI (if any, will be NULL for
- // non-DOM-UI pages). This object is responsible for all communication with
- // a child RenderView instance.
- RenderViewHostImpl* render_view_host_;
+ // Our RenderFrameHost and its associated Web UI (if any, will be NULL for
+ // non-WebUI pages). This object is responsible for all communication with
+ // a child RenderFrame instance.
+ // For now, RenderFrameHost keeps a RenderViewHost in its SiteInstance alive.
+ // Eventually, RenderViewHost will be replaced with a page context.
+ scoped_ptr<RenderFrameHostImpl> render_frame_host_;
scoped_ptr<WebUIImpl> web_ui_;
- // A RenderViewHost used to load a cross-site page. This remains hidden
+ // A RenderFrameHost used to load a cross-site page. This remains hidden
// while a cross-site request is pending until it calls DidNavigate. It may
// have an associated Web UI, in which case the Web UI pointer will be non-
// NULL.
//
// The |pending_web_ui_| may be non-NULL even when the
- // |pending_render_view_host_| is NULL. This will happen when we're
- // transitioning between two Web UI pages: the RVH won't be swapped, so the
+ // |pending_render_frame_host_| is NULL. This will happen when we're
+ // transitioning between two Web UI pages: the RFH won't be swapped, so the
// pending pointer will be unused, but there will be a pending Web UI
// associated with the navigation.
- RenderViewHostImpl* pending_render_view_host_;
+ scoped_ptr<RenderFrameHostImpl> pending_render_frame_host_;
// Tracks information about any current pending cross-process navigation.
scoped_ptr<PendingNavigationParams> pending_nav_params_;
@@ -390,10 +463,14 @@ class CONTENT_EXPORT RenderFrameHostManager
scoped_ptr<WebUIImpl> pending_web_ui_;
base::WeakPtr<WebUIImpl> pending_and_current_web_ui_;
- // A map of site instance ID to swapped out RenderViewHosts. This may include
- // pending_render_view_host_ for navigations to existing entries.
- typedef base::hash_map<int32, RenderViewHostImpl*> RenderViewHostMap;
- RenderViewHostMap swapped_out_hosts_;
+ // A map of site instance ID to RenderFrameProxyHosts.
+ typedef base::hash_map<int32, RenderFrameProxyHost*> RenderFrameProxyHostMap;
+ RenderFrameProxyHostMap proxy_hosts_;
+
+ // A map of RenderFrameHosts pending shutdown.
+ typedef base::hash_map<int32, linked_ptr<RenderFrameHostImpl> >
+ RFHPendingDeleteMap;
+ RFHPendingDeleteMap pending_delete_hosts_;
// The intersitial page currently shown if any, not own by this class
// (the InterstitialPage is self-owned, it deletes itself when hidden).
@@ -401,6 +478,8 @@ class CONTENT_EXPORT RenderFrameHostManager
NotificationRegistrar registrar_;
+ base::WeakPtrFactory<RenderFrameHostManager> weak_factory_;
+
DISALLOW_COPY_AND_ASSIGN(RenderFrameHostManager);
};
diff --git a/chromium/content/browser/frame_host/render_frame_host_manager_browsertest.cc b/chromium/content/browser/frame_host/render_frame_host_manager_browsertest.cc
index 5692fac8745..ad2689ea138 100644
--- a/chromium/content/browser/frame_host/render_frame_host_manager_browsertest.cc
+++ b/chromium/content/browser/frame_host/render_frame_host_manager_browsertest.cc
@@ -4,6 +4,7 @@
#include <set>
+#include "base/command_line.h"
#include "base/json/json_reader.h"
#include "base/memory/ref_counted.h"
#include "base/path_service.h"
@@ -17,28 +18,51 @@
#include "content/common/content_constants_internal.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
-#include "content/public/browser/notification_details.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
-#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
+#include "content/public/common/bindings_policy.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_navigation_observer.h"
#include "content/public/test/test_utils.h"
#include "content/shell/browser/shell.h"
-#include "content/test/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
#include "net/base/net_util.h"
+#include "net/dns/mock_host_resolver.h"
#include "net/test/spawned_test_server/spawned_test_server.h"
+using base::ASCIIToUTF16;
+
namespace content {
+namespace {
+
+const char kOpenUrlViaClickTargetFunc[] =
+ "(function(url) {\n"
+ " var lnk = document.createElement(\"a\");\n"
+ " lnk.href = url;\n"
+ " lnk.target = \"_blank\";\n"
+ " document.body.appendChild(lnk);\n"
+ " lnk.click();\n"
+ "})";
+
+// Adds a link with given url and target=_blank, and clicks on it.
+void OpenUrlViaClickTarget(const internal::ToRenderFrameHost& adapter,
+ const GURL& url) {
+ EXPECT_TRUE(ExecuteScript(adapter,
+ std::string(kOpenUrlViaClickTargetFunc) + "(\"" + url.spec() + "\");"));
+}
+
+} // anonymous namespace
+
class RenderFrameHostManagerTest : public ContentBrowserTest {
public:
- RenderFrameHostManagerTest() {}
+ RenderFrameHostManagerTest() : foo_com_("foo.com") {
+ replace_host_.SetHostStr(foo_com_);
+ }
static bool GetFilePathWithHostAndPortReplacement(
const std::string& original_file_path,
@@ -50,24 +74,37 @@ class RenderFrameHostManagerTest : public ContentBrowserTest {
return net::SpawnedTestServer::GetFilePathWithReplacements(
original_file_path, replacement_text, replacement_path);
}
+
+ void StartServer() {
+ // Support multiple sites on the test server.
+ host_resolver()->AddRule("*", "127.0.0.1");
+ ASSERT_TRUE(test_server()->Start());
+
+ foo_host_port_ = test_server()->host_port_pair();
+ foo_host_port_.set_host(foo_com_);
+ }
+
+ // Returns a URL on foo.com with the given path.
+ GURL GetCrossSiteURL(const std::string& path) {
+ GURL cross_site_url(test_server()->GetURL(path));
+ return cross_site_url.ReplaceComponents(replace_host_);
+ }
+
+ protected:
+ std::string foo_com_;
+ GURL::Replacements replace_host_;
+ net::HostPortPair foo_host_port_;
};
// Web pages should not have script access to the swapped out page.
-IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
- DISABLED_NoScriptAccessAfterSwapOut) {
- // Start two servers with different sites.
- ASSERT_TRUE(test_server()->Start());
- net::SpawnedTestServer https_server(
- net::SpawnedTestServer::TYPE_HTTPS,
- net::SpawnedTestServer::kLocalhost,
- base::FilePath(FILE_PATH_LITERAL("content/test/data")));
- ASSERT_TRUE(https_server.Start());
+IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, NoScriptAccessAfterSwapOut) {
+ StartServer();
// Load a page with links that open in a new window.
std::string replacement_path;
ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
"files/click-noreferrer-links.html",
- https_server.host_port_pair(),
+ foo_host_port_,
&replacement_path));
NavigateToURL(shell(), test_server()->GetURL(replacement_path));
@@ -105,7 +142,7 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
EXPECT_TRUE(success);
// Now navigate the new window to a different site.
- NavigateToURL(new_shell, https_server.GetURL("files/title1.html"));
+ NavigateToURL(new_shell, GetCrossSiteURL("files/title1.html"));
scoped_refptr<SiteInstance> new_site_instance(
new_shell->web_contents()->GetSiteInstance());
EXPECT_NE(orig_site_instance, new_site_instance);
@@ -123,19 +160,13 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
// and target=_blank should create a new SiteInstance.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
SwapProcessWithRelNoreferrerAndTargetBlank) {
- // Start two servers with different sites.
- ASSERT_TRUE(test_server()->Start());
- net::SpawnedTestServer https_server(
- net::SpawnedTestServer::TYPE_HTTPS,
- net::SpawnedTestServer::kLocalhost,
- base::FilePath(FILE_PATH_LITERAL("content/test/data")));
- ASSERT_TRUE(https_server.Start());
+ StartServer();
// Load a page with links that open in a new window.
std::string replacement_path;
ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
"files/click-noreferrer-links.html",
- https_server.host_port_pair(),
+ foo_host_port_,
&replacement_path));
NavigateToURL(shell(), test_server()->GetURL(replacement_path));
@@ -177,19 +208,13 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
// targets.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
SwapProcessWithSameSiteRelNoreferrer) {
- // Start two servers with different sites.
- ASSERT_TRUE(test_server()->Start());
- net::SpawnedTestServer https_server(
- net::SpawnedTestServer::TYPE_HTTPS,
- net::SpawnedTestServer::kLocalhost,
- base::FilePath(FILE_PATH_LITERAL("content/test/data")));
- ASSERT_TRUE(https_server.Start());
+ StartServer();
// Load a page with links that open in a new window.
std::string replacement_path;
ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
"files/click-noreferrer-links.html",
- https_server.host_port_pair(),
+ foo_host_port_,
&replacement_path));
NavigateToURL(shell(), test_server()->GetURL(replacement_path));
@@ -231,19 +256,13 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
// target=_blank should not create a new SiteInstance.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
DontSwapProcessWithOnlyTargetBlank) {
- // Start two servers with different sites.
- ASSERT_TRUE(test_server()->Start());
- net::SpawnedTestServer https_server(
- net::SpawnedTestServer::TYPE_HTTPS,
- net::SpawnedTestServer::kLocalhost,
- base::FilePath(FILE_PATH_LITERAL("content/test/data")));
- ASSERT_TRUE(https_server.Start());
+ StartServer();
// Load a page with links that open in a new window.
std::string replacement_path;
ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
"files/click-noreferrer-links.html",
- https_server.host_port_pair(),
+ foo_host_port_,
&replacement_path));
NavigateToURL(shell(), test_server()->GetURL(replacement_path));
@@ -279,19 +298,13 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
// and no target=_blank should not create a new SiteInstance.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
DontSwapProcessWithOnlyRelNoreferrer) {
- // Start two servers with different sites.
- ASSERT_TRUE(test_server()->Start());
- net::SpawnedTestServer https_server(
- net::SpawnedTestServer::TYPE_HTTPS,
- net::SpawnedTestServer::kLocalhost,
- base::FilePath(FILE_PATH_LITERAL("content/test/data")));
- ASSERT_TRUE(https_server.Start());
+ StartServer();
// Load a page with links that open in a new window.
std::string replacement_path;
ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
"files/click-noreferrer-links.html",
- https_server.host_port_pair(),
+ foo_host_port_,
&replacement_path));
NavigateToURL(shell(), test_server()->GetURL(replacement_path));
@@ -326,19 +339,13 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
// named target window has swapped processes.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
AllowTargetedNavigationsAfterSwap) {
- // Start two servers with different sites.
- ASSERT_TRUE(test_server()->Start());
- net::SpawnedTestServer https_server(
- net::SpawnedTestServer::TYPE_HTTPS,
- net::SpawnedTestServer::kLocalhost,
- base::FilePath(FILE_PATH_LITERAL("content/test/data")));
- ASSERT_TRUE(https_server.Start());
+ StartServer();
// Load a page with links that open in a new window.
std::string replacement_path;
ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
"files/click-noreferrer-links.html",
- https_server.host_port_pair(),
+ foo_host_port_,
&replacement_path));
NavigateToURL(shell(), test_server()->GetURL(replacement_path));
@@ -368,7 +375,8 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
EXPECT_EQ(orig_site_instance, blank_site_instance);
// Now navigate the new tab to a different site.
- NavigateToURL(new_shell, https_server.GetURL("files/title1.html"));
+ GURL cross_site_url(GetCrossSiteURL("files/title1.html"));
+ NavigateToURL(new_shell, cross_site_url);
scoped_refptr<SiteInstance> new_site_instance(
new_shell->web_contents()->GetSiteInstance());
EXPECT_NE(orig_site_instance, new_site_instance);
@@ -389,7 +397,7 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
// If it navigates away to another process, the original window should
// still be able to close it (using a cross-process close message).
- NavigateToURL(new_shell, https_server.GetURL("files/title1.html"));
+ NavigateToURL(new_shell, cross_site_url);
EXPECT_EQ(new_site_instance,
new_shell->web_contents()->GetSiteInstance());
WebContentsDestroyedWatcher close_watcher(new_shell->web_contents());
@@ -404,25 +412,20 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
// Test that setting the opener to null in a window affects cross-process
// navigations, including those to existing entries. http://crbug.com/156669.
// Flaky on windows: http://crbug.com/291249
-#if defined(OS_WIN)
+// This test also crashes under ThreadSanitizer, http://crbug.com/356758.
+#if defined(OS_WIN) || defined(THREAD_SANITIZER)
#define MAYBE_DisownOpener DISABLED_DisownOpener
#else
#define MAYBE_DisownOpener DisownOpener
#endif
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, MAYBE_DisownOpener) {
- // Start two servers with different sites.
- ASSERT_TRUE(test_server()->Start());
- net::SpawnedTestServer https_server(
- net::SpawnedTestServer::TYPE_HTTPS,
- net::SpawnedTestServer::kLocalhost,
- base::FilePath(FILE_PATH_LITERAL("content/test/data")));
- ASSERT_TRUE(https_server.Start());
+ StartServer();
// Load a page with links that open in a new window.
std::string replacement_path;
ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
"files/click-noreferrer-links.html",
- https_server.host_port_pair(),
+ foo_host_port_,
&replacement_path));
NavigateToURL(shell(), test_server()->GetURL(replacement_path));
@@ -452,7 +455,7 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, MAYBE_DisownOpener) {
EXPECT_EQ(orig_site_instance, blank_site_instance);
// Now navigate the new tab to a different site.
- NavigateToURL(new_shell, https_server.GetURL("files/title1.html"));
+ NavigateToURL(new_shell, GetCrossSiteURL("files/title1.html"));
scoped_refptr<SiteInstance> new_site_instance(
new_shell->web_contents()->GetSiteInstance());
EXPECT_NE(orig_site_instance, new_site_instance);
@@ -475,7 +478,7 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, MAYBE_DisownOpener) {
EXPECT_TRUE(success);
// Now navigate forward again (creating a new process) and check opener.
- NavigateToURL(new_shell, https_server.GetURL("files/title1.html"));
+ NavigateToURL(new_shell, GetCrossSiteURL("files/title1.html"));
success = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
new_shell->web_contents(),
@@ -509,19 +512,13 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, DisownSubframeOpener) {
// 6) Post a message from _blank to a subframe of "foo".
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
SupportCrossProcessPostMessage) {
- // Start two servers with different sites.
- ASSERT_TRUE(test_server()->Start());
- net::SpawnedTestServer https_server(
- net::SpawnedTestServer::TYPE_HTTPS,
- net::SpawnedTestServer::kLocalhost,
- base::FilePath(FILE_PATH_LITERAL("content/test/data")));
- ASSERT_TRUE(https_server.Start());
+ StartServer();
// Load a page with links that open in a new window.
std::string replacement_path;
ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
"files/click-noreferrer-links.html",
- https_server.host_port_pair(),
+ foo_host_port_,
&replacement_path));
NavigateToURL(shell(), test_server()->GetURL(replacement_path));
@@ -552,7 +549,7 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
WaitForLoadStop(foo_contents);
EXPECT_EQ("/files/navigate_opener.html",
foo_contents->GetLastCommittedURL().path());
- NavigateToURL(new_shell, https_server.GetURL("files/post_message.html"));
+ NavigateToURL(new_shell, GetCrossSiteURL("files/post_message.html"));
scoped_refptr<SiteInstance> foo_site_instance(
foo_contents->GetSiteInstance());
EXPECT_NE(orig_site_instance, foo_site_instance);
@@ -656,19 +653,13 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
// The test will be enabled when the feature implementation lands.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
SupportCrossProcessPostMessageWithMessagePort) {
- // Start two servers with different sites.
- ASSERT_TRUE(test_server()->Start());
- net::SpawnedTestServer https_server(
- net::SpawnedTestServer::TYPE_HTTPS,
- net::SpawnedTestServer::kLocalhost,
- base::FilePath(FILE_PATH_LITERAL("content/test/data")));
- ASSERT_TRUE(https_server.Start());
+ StartServer();
// Load a page with links that open in a new window.
std::string replacement_path;
ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
"files/click-noreferrer-links.html",
- https_server.host_port_pair(),
+ foo_host_port_,
&replacement_path));
NavigateToURL(shell(), test_server()->GetURL(replacement_path));
@@ -697,9 +688,7 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
WaitForLoadStop(foo_contents);
EXPECT_EQ("/files/navigate_opener.html",
foo_contents->GetLastCommittedURL().path());
- NavigateToURL(
- new_shell,
- https_server.GetURL("files/post_message.html"));
+ NavigateToURL(new_shell, GetCrossSiteURL("files/post_message.html"));
scoped_refptr<SiteInstance> foo_site_instance(
foo_contents->GetSiteInstance());
EXPECT_NE(orig_site_instance, foo_site_instance);
@@ -713,9 +702,8 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
// 2) Post a message containing a MessagePort from opener to the the foo
// window. The foo window will reply via the passed port, causing the opener
// to update its own title.
- WindowedNotificationObserver title_observer(
- NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED,
- Source<WebContents>(opener_contents));
+ base::string16 expected_title = ASCIIToUTF16("msg-back-via-port");
+ TitleWatcher title_observer(opener_contents, expected_title);
EXPECT_TRUE(ExecuteScriptAndExtractBool(
opener_contents,
"window.domAutomationController.send(postWithPortToFoo());",
@@ -723,7 +711,7 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
EXPECT_TRUE(success);
ASSERT_FALSE(
opener_manager->GetSwappedOutRenderViewHost(orig_site_instance.get()));
- title_observer.Wait();
+ ASSERT_EQ(expected_title, title_observer.WaitAndGetTitle());
// Check message counts.
int opener_received_messages_via_port = 0;
@@ -752,19 +740,13 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
// still work after a process swap.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
AllowTargetedNavigationsInOpenerAfterSwap) {
- // Start two servers with different sites.
- ASSERT_TRUE(test_server()->Start());
- net::SpawnedTestServer https_server(
- net::SpawnedTestServer::TYPE_HTTPS,
- net::SpawnedTestServer::kLocalhost,
- base::FilePath(FILE_PATH_LITERAL("content/test/data")));
- ASSERT_TRUE(https_server.Start());
+ StartServer();
// Load a page with links that open in a new window.
std::string replacement_path;
ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
"files/click-noreferrer-links.html",
- https_server.host_port_pair(),
+ foo_host_port_,
&replacement_path));
NavigateToURL(shell(), test_server()->GetURL(replacement_path));
@@ -795,7 +777,7 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
EXPECT_EQ(orig_site_instance, blank_site_instance);
// Now navigate the original (opener) tab to a different site.
- NavigateToURL(shell(), https_server.GetURL("files/title1.html"));
+ NavigateToURL(shell(), GetCrossSiteURL("files/title1.html"));
scoped_refptr<SiteInstance> new_site_instance(
shell()->web_contents()->GetSiteInstance());
EXPECT_NE(orig_site_instance, new_site_instance);
@@ -820,19 +802,13 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
// See http://crbug.com/126333.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
ProcessExitWithSwappedOutViews) {
- // Start two servers with different sites.
- ASSERT_TRUE(test_server()->Start());
- net::SpawnedTestServer https_server(
- net::SpawnedTestServer::TYPE_HTTPS,
- net::SpawnedTestServer::kLocalhost,
- base::FilePath(FILE_PATH_LITERAL("content/test/data")));
- ASSERT_TRUE(https_server.Start());
+ StartServer();
// Load a page with links that open in a new window.
std::string replacement_path;
ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
"files/click-noreferrer-links.html",
- https_server.host_port_pair(),
+ foo_host_port_,
&replacement_path));
NavigateToURL(shell(), test_server()->GetURL(replacement_path));
@@ -862,7 +838,7 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
EXPECT_EQ(orig_site_instance, opened_site_instance);
// Now navigate the opened window to a different site.
- NavigateToURL(new_shell, https_server.GetURL("files/title1.html"));
+ NavigateToURL(new_shell, GetCrossSiteURL("files/title1.html"));
scoped_refptr<SiteInstance> new_site_instance(
new_shell->web_contents()->GetSiteInstance());
EXPECT_NE(orig_site_instance, new_site_instance);
@@ -874,10 +850,10 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
// Navigate the first window to a different site as well. The original
// process should exit, since all of its views are now swapped out.
- WindowedNotificationObserver exit_observer(
- NOTIFICATION_RENDERER_PROCESS_TERMINATED,
- Source<RenderProcessHost>(orig_process));
- NavigateToURL(shell(), https_server.GetURL("files/title1.html"));
+ RenderProcessHostWatcher exit_observer(
+ orig_process,
+ RenderProcessHostWatcher::WATCH_FOR_HOST_DESTRUCTION);
+ NavigateToURL(shell(), GetCrossSiteURL("files/title1.html"));
exit_observer.Wait();
scoped_refptr<SiteInstance> new_site_instance2(
shell()->web_contents()->GetSiteInstance());
@@ -887,22 +863,7 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
// Test for crbug.com/76666. A cross-site navigation that fails with a 204
// error should not make us ignore future renderer-initiated navigations.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, ClickLinkAfter204Error) {
- // Start two servers with different sites.
- ASSERT_TRUE(test_server()->Start());
- net::SpawnedTestServer https_server(
- net::SpawnedTestServer::TYPE_HTTPS,
- net::SpawnedTestServer::kLocalhost,
- base::FilePath(FILE_PATH_LITERAL("content/test/data")));
- ASSERT_TRUE(https_server.Start());
-
- // Load a page with links that open in a new window.
- // The links will point to the HTTPS server.
- std::string replacement_path;
- ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
- "files/click-noreferrer-links.html",
- https_server.host_port_pair(),
- &replacement_path));
- NavigateToURL(shell(), test_server()->GetURL(replacement_path));
+ StartServer();
// Get the original SiteInstance for later comparison.
scoped_refptr<SiteInstance> orig_site_instance(
@@ -910,30 +871,27 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, ClickLinkAfter204Error) {
EXPECT_TRUE(orig_site_instance.get() != NULL);
// Load a cross-site page that fails with a 204 error.
- NavigateToURL(shell(), https_server.GetURL("nocontent"));
+ NavigateToURL(shell(), GetCrossSiteURL("nocontent"));
- // We should still be looking at the normal page. The typed URL will
- // still be visible until the user clears it manually, but the last
- // committed URL will be the previous page.
+ // We should still be looking at the normal page. Because we started from a
+ // blank new tab, the typed URL will still be visible until the user clears it
+ // manually. The last committed URL will be the previous page.
scoped_refptr<SiteInstance> post_nav_site_instance(
shell()->web_contents()->GetSiteInstance());
EXPECT_EQ(orig_site_instance, post_nav_site_instance);
EXPECT_EQ("/nocontent",
shell()->web_contents()->GetVisibleURL().path());
- EXPECT_EQ("/files/click-noreferrer-links.html",
- shell()->web_contents()->GetController().
- GetLastCommittedEntry()->GetVirtualURL().path());
+ EXPECT_FALSE(
+ shell()->web_contents()->GetController().GetLastCommittedEntry());
// Renderer-initiated navigations should work.
- bool success = false;
- EXPECT_TRUE(ExecuteScriptAndExtractBool(
+ base::string16 expected_title = ASCIIToUTF16("Title Of Awesomeness");
+ TitleWatcher title_watcher(shell()->web_contents(), expected_title);
+ GURL url = test_server()->GetURL("files/title2.html");
+ EXPECT_TRUE(ExecuteScript(
shell()->web_contents(),
- "window.domAutomationController.send(clickNoRefLink());",
- &success));
- EXPECT_TRUE(success);
-
- // Wait for the cross-site transition in the current tab to finish.
- WaitForLoadStop(shell()->web_contents());
+ base::StringPrintf("location.href = '%s'", url.spec().c_str())));
+ ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
// Opens in same tab.
EXPECT_EQ(1u, Shell::windows().size());
@@ -941,9 +899,9 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, ClickLinkAfter204Error) {
shell()->web_contents()->GetLastCommittedURL().path());
// Should have the same SiteInstance.
- scoped_refptr<SiteInstance> noref_site_instance(
+ scoped_refptr<SiteInstance> new_site_instance(
shell()->web_contents()->GetSiteInstance());
- EXPECT_EQ(orig_site_instance, noref_site_instance);
+ EXPECT_EQ(orig_site_instance, new_site_instance);
}
// Test for crbug.com/9682. We should show the URL for a pending renderer-
@@ -1030,47 +988,26 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
EXPECT_FALSE(contents->GetController().GetVisibleEntry());
}
+// Crashes under ThreadSanitizer, http://crbug.com/356758.
+#if defined(THREAD_SANITIZER)
+#define MAYBE_BackForwardNotStale DISABLED_BackForwardNotStale
+#else
+#define MAYBE_BackForwardNotStale BackForwardNotStale
+#endif
// Test for http://crbug.com/93427. Ensure that cross-site navigations
// do not cause back/forward navigations to be considered stale by the
// renderer.
-IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, BackForwardNotStale) {
- NavigateToURL(shell(), GURL(kAboutBlankURL));
-
- // Start two servers with different sites.
- ASSERT_TRUE(test_server()->Start());
- net::SpawnedTestServer https_server(
- net::SpawnedTestServer::TYPE_HTTPS,
- net::SpawnedTestServer::kLocalhost,
- base::FilePath(FILE_PATH_LITERAL("content/test/data")));
- ASSERT_TRUE(https_server.Start());
+IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, MAYBE_BackForwardNotStale) {
+ StartServer();
+ NavigateToURL(shell(), GURL(url::kAboutBlankURL));
// Visit a page on first site.
- std::string replacement_path_a1;
- ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
- "files/title1.html",
- test_server()->host_port_pair(),
- &replacement_path_a1));
- NavigateToURL(shell(), test_server()->GetURL(replacement_path_a1));
+ NavigateToURL(shell(), test_server()->GetURL("files/title1.html"));
// Visit three pages on second site.
- std::string replacement_path_b1;
- ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
- "files/title1.html",
- https_server.host_port_pair(),
- &replacement_path_b1));
- NavigateToURL(shell(), https_server.GetURL(replacement_path_b1));
- std::string replacement_path_b2;
- ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
- "files/title2.html",
- https_server.host_port_pair(),
- &replacement_path_b2));
- NavigateToURL(shell(), https_server.GetURL(replacement_path_b2));
- std::string replacement_path_b3;
- ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
- "files/title3.html",
- https_server.host_port_pair(),
- &replacement_path_b3));
- NavigateToURL(shell(), https_server.GetURL(replacement_path_b3));
+ NavigateToURL(shell(), GetCrossSiteURL("files/title1.html"));
+ NavigateToURL(shell(), GetCrossSiteURL("files/title2.html"));
+ NavigateToURL(shell(), GetCrossSiteURL("files/title3.html"));
// History is now [blank, A1, B1, B2, *B3].
WebContents* contents = shell()->web_contents();
@@ -1078,7 +1015,7 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, BackForwardNotStale) {
// Open another window in same process to keep this process alive.
Shell* new_shell = CreateBrowser();
- NavigateToURL(new_shell, https_server.GetURL(replacement_path_b1));
+ NavigateToURL(new_shell, GetCrossSiteURL("files/title1.html"));
// Go back three times to first site.
{
@@ -1133,19 +1070,13 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, BackForwardNotStale) {
// Swapping out a render view should update its visiblity state.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
SwappedOutViewHasCorrectVisibilityState) {
- // Start two servers with different sites.
- ASSERT_TRUE(test_server()->Start());
- net::SpawnedTestServer https_server(
- net::SpawnedTestServer::TYPE_HTTPS,
- net::SpawnedTestServer::kLocalhost,
- base::FilePath(FILE_PATH_LITERAL("content/test/data")));
- ASSERT_TRUE(https_server.Start());
+ StartServer();
// Load a page with links that open in a new window.
std::string replacement_path;
ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
"files/click-noreferrer-links.html",
- https_server.host_port_pair(),
+ foo_host_port_,
&replacement_path));
NavigateToURL(shell(), test_server()->GetURL(replacement_path));
@@ -1175,7 +1106,7 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
// Now navigate the new window to a different site. This should swap out the
// tab's existing RenderView, causing it become hidden.
- NavigateToURL(new_shell, https_server.GetURL("files/title1.html"));
+ NavigateToURL(new_shell, GetCrossSiteURL("files/title1.html"));
EXPECT_TRUE(ExecuteScriptAndExtractBool(
rvh,
@@ -1228,19 +1159,20 @@ class RenderViewHostDestructionObserver : public WebContentsObserver {
std::set<RenderViewHost*> watched_render_view_hosts_;
};
+// Crashes under ThreadSanitizer, http://crbug.com/356758.
+#if defined(THREAD_SANITIZER)
+#define MAYBE_LeakingRenderViewHosts DISABLED_LeakingRenderViewHosts
+#else
+#define MAYBE_LeakingRenderViewHosts LeakingRenderViewHosts
+#endif
// Test for crbug.com/90867. Make sure we don't leak render view hosts since
// they may cause crashes or memory corruptions when trying to call dead
// delegate_. This test also verifies crbug.com/117420 and crbug.com/143255 to
// ensure that a separate SiteInstance is created when navigating to view-source
// URLs, regardless of current URL.
-IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, LeakingRenderViewHosts) {
- // Start two servers with different sites.
- ASSERT_TRUE(test_server()->Start());
- net::SpawnedTestServer https_server(
- net::SpawnedTestServer::TYPE_HTTPS,
- net::SpawnedTestServer::kLocalhost,
- base::FilePath(FILE_PATH_LITERAL("content/test/data")));
- ASSERT_TRUE(https_server.Start());
+IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
+ MAYBE_LeakingRenderViewHosts) {
+ StartServer();
// Observe the created render_view_host's to make sure they will not leak.
RenderViewHostDestructionObserver rvh_observers(shell()->web_contents());
@@ -1285,7 +1217,7 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, LeakingRenderViewHosts) {
EXPECT_NE(site_instance1, site_instance2);
// Now navigate to a different instance so that we swap out again.
- NavigateToURL(shell(), https_server.GetURL("files/title2.html"));
+ NavigateToURL(shell(), GetCrossSiteURL("files/title2.html"));
rvh_observers.EnsureRVHGetsDestructed(
shell()->web_contents()->GetRenderViewHost());
@@ -1301,22 +1233,16 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, LeakingRenderViewHosts) {
// interrupt the intended navigation and show swappedout:// instead.
// Specifically:
// 1) Open 2 tabs in an HTTP SiteInstance, with a subframe in the opener.
-// 2) Send the second tab to a different HTTPS SiteInstance.
-// This creates a swapped out opener for the first tab in the HTTPS process.
-// 3) Navigate the first tab to the HTTPS SiteInstance, and have the first
+// 2) Send the second tab to a different foo.com SiteInstance.
+// This creates a swapped out opener for the first tab in the foo process.
+// 3) Navigate the first tab to the foo.com SiteInstance, and have the first
// tab's unload handler remove its frame.
// This used to cause an update to the frame tree of the swapped out RV,
// just as it was navigating to a real page. That pre-empted the real
// navigation and visibly sent the tab to swappedout://.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
DontPreemptNavigationWithFrameTreeUpdate) {
- // Start two servers with different sites.
- ASSERT_TRUE(test_server()->Start());
- net::SpawnedTestServer https_server(
- net::SpawnedTestServer::TYPE_HTTPS,
- net::SpawnedTestServer::kLocalhost,
- base::FilePath(FILE_PATH_LITERAL("content/test/data")));
- ASSERT_TRUE(https_server.Start());
+ StartServer();
// 1. Load a page that deletes its iframe during unload.
NavigateToURL(shell(),
@@ -1345,17 +1271,17 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
EXPECT_EQ(orig_site_instance, new_shell->web_contents()->GetSiteInstance());
// 2. Send the second tab to a different process.
- NavigateToURL(new_shell, https_server.GetURL("files/title1.html"));
+ NavigateToURL(new_shell, GetCrossSiteURL("files/title1.html"));
scoped_refptr<SiteInstance> new_site_instance(
new_shell->web_contents()->GetSiteInstance());
EXPECT_NE(orig_site_instance, new_site_instance);
// 3. Send the first tab to the second tab's process.
- NavigateToURL(shell(), https_server.GetURL("files/title1.html"));
+ NavigateToURL(shell(), GetCrossSiteURL("files/title1.html"));
// Make sure it ends up at the right page.
WaitForLoadStop(shell()->web_contents());
- EXPECT_EQ(https_server.GetURL("files/title1.html"),
+ EXPECT_EQ(GetCrossSiteURL("files/title1.html"),
shell()->web_contents()->GetLastCommittedURL());
EXPECT_EQ(new_site_instance, shell()->web_contents()->GetSiteInstance());
}
@@ -1381,10 +1307,9 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, RendererDebugURLsDontSwap) {
ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
// Crash the renderer of the view-source page.
- RenderProcessHost* process = shell()->web_contents()->GetRenderProcessHost();
- WindowedNotificationObserver crash_observer(
- NOTIFICATION_RENDERER_PROCESS_CLOSED,
- Source<RenderProcessHost>(process));
+ RenderProcessHostWatcher crash_observer(
+ shell()->web_contents(),
+ RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
NavigateToURL(shell(), GURL(kChromeUICrashURL));
crash_observer.Wait();
}
@@ -1396,17 +1321,16 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, RendererDebugURLsDontSwap) {
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
IgnoreRendererDebugURLsWhenCrashed) {
// Visit a WebUI page with bindings.
- GURL webui_url = GURL(std::string(chrome::kChromeUIScheme) + "://" +
+ GURL webui_url = GURL(std::string(kChromeUIScheme) + "://" +
std::string(kChromeUIGpuHost));
NavigateToURL(shell(), webui_url);
- RenderProcessHost* process = shell()->web_contents()->GetRenderProcessHost();
EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
- process->GetID()));
+ shell()->web_contents()->GetRenderProcessHost()->GetID()));
// Crash the renderer of the WebUI page.
- WindowedNotificationObserver crash_observer(
- NOTIFICATION_RENDERER_PROCESS_CLOSED,
- Source<RenderProcessHost>(process));
+ RenderProcessHostWatcher crash_observer(
+ shell()->web_contents(),
+ RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
NavigateToURL(shell(), GURL(kChromeUICrashURL));
crash_observer.Wait();
@@ -1420,10 +1344,9 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
Shell* shell2 = Shell::CreateNewWindow(
shell()->web_contents()->GetBrowserContext(), GURL(), NULL,
MSG_ROUTING_NONE, gfx::Size());
- RenderProcessHost* process2 = shell2->web_contents()->GetRenderProcessHost();
- WindowedNotificationObserver crash_observer2(
- NOTIFICATION_RENDERER_PROCESS_CLOSED,
- Source<RenderProcessHost>(process2));
+ RenderProcessHostWatcher crash_observer2(
+ shell2->web_contents(),
+ RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
NavigateToURL(shell2, GURL(kChromeUIKillURL));
crash_observer2.Wait();
}
@@ -1433,7 +1356,7 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
// to other RenderViewHosts. See http://crbug.com/330811.
IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, ClearPendingWebUIOnCommit) {
// Visit a WebUI page with bindings.
- GURL webui_url(GURL(std::string(chrome::kChromeUIScheme) + "://" +
+ GURL webui_url(GURL(std::string(kChromeUIScheme) + "://" +
std::string(kChromeUIGpuHost)));
NavigateToURL(shell(), webui_url);
EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
@@ -1452,4 +1375,91 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, ClearPendingWebUIOnCommit) {
EXPECT_FALSE(web_contents->GetRenderManagerForTesting()->pending_web_ui());
}
+class RFHMProcessPerTabTest : public RenderFrameHostManagerTest {
+ public:
+ RFHMProcessPerTabTest() {}
+
+ virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
+ command_line->AppendSwitch(switches::kProcessPerTab);
+ }
+};
+
+// Test that we still swap processes for BrowsingInstance changes even in
+// --process-per-tab mode. See http://crbug.com/343017.
+// Disabled on Android: http://crbug.com/345873.
+// Crashes under ThreadSanitizer, http://crbug.com/356758.
+#if defined(OS_ANDROID) || defined(THREAD_SANITIZER)
+#define MAYBE_BackFromWebUI DISABLED_BackFromWebUI
+#else
+#define MAYBE_BackFromWebUI BackFromWebUI
+#endif
+IN_PROC_BROWSER_TEST_F(RFHMProcessPerTabTest, MAYBE_BackFromWebUI) {
+ ASSERT_TRUE(test_server()->Start());
+ GURL original_url(test_server()->GetURL("files/title2.html"));
+ NavigateToURL(shell(), original_url);
+
+ // Visit a WebUI page with bindings.
+ GURL webui_url(GURL(std::string(kChromeUIScheme) + "://" +
+ std::string(kChromeUIGpuHost)));
+ NavigateToURL(shell(), webui_url);
+ EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
+ shell()->web_contents()->GetRenderProcessHost()->GetID()));
+
+ // Go back and ensure we have no WebUI bindings.
+ TestNavigationObserver back_nav_load_observer(shell()->web_contents());
+ shell()->web_contents()->GetController().GoBack();
+ back_nav_load_observer.Wait();
+ EXPECT_EQ(original_url, shell()->web_contents()->GetLastCommittedURL());
+ EXPECT_FALSE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
+ shell()->web_contents()->GetRenderProcessHost()->GetID()));
+}
+
+// crbug.com/372360
+// The test loads url1, opens a link pointing to url2 in a new tab, and
+// navigates the new tab to url1.
+// The following is needed for the bug to happen:
+// - url1 must require webui bindings;
+// - navigating to url2 in the site instance of url1 should not swap
+// browsing instances, but should require a new site instance.
+IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, WebUIGetsBindings) {
+ GURL url1(std::string(kChromeUIScheme) + "://" +
+ std::string(kChromeUIGpuHost));
+ GURL url2(std::string(kChromeUIScheme) + "://" +
+ std::string(kChromeUIAccessibilityHost));
+
+ // Visit a WebUI page with bindings.
+ NavigateToURL(shell(), url1);
+ EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
+ shell()->web_contents()->GetRenderProcessHost()->GetID()));
+ SiteInstance* site_instance1 = shell()->web_contents()->GetSiteInstance();
+
+ // Open a new tab. Initially it gets a render view in the original tab's
+ // current site instance.
+ TestNavigationObserver nav_observer(NULL);
+ nav_observer.StartWatchingNewWebContents();
+ ShellAddedObserver shao;
+ OpenUrlViaClickTarget(shell()->web_contents(), url2);
+ nav_observer.Wait();
+ Shell* new_shell = shao.GetShell();
+ WebContentsImpl* new_web_contents = static_cast<WebContentsImpl*>(
+ new_shell->web_contents());
+ SiteInstance* site_instance2 = new_web_contents->GetSiteInstance();
+
+ EXPECT_NE(site_instance2, site_instance1);
+ EXPECT_TRUE(site_instance2->IsRelatedSiteInstance(site_instance1));
+ RenderViewHost* initial_rvh = new_web_contents->
+ GetRenderManagerForTesting()->GetSwappedOutRenderViewHost(site_instance1);
+ ASSERT_TRUE(initial_rvh);
+ // The following condition is what was causing the bug.
+ EXPECT_EQ(0, initial_rvh->GetEnabledBindings());
+
+ // Navigate to url1 and check bindings.
+ NavigateToURL(new_shell, url1);
+ // The navigation should have used the first SiteInstance, otherwise
+ // |initial_rvh| did not have a chance to be used.
+ EXPECT_EQ(new_web_contents->GetSiteInstance(), site_instance1);
+ EXPECT_EQ(BINDINGS_POLICY_WEB_UI,
+ new_web_contents->GetRenderViewHost()->GetEnabledBindings());
+}
+
} // namespace content
diff --git a/chromium/content/browser/frame_host/render_frame_host_manager_unittest.cc b/chromium/content/browser/frame_host/render_frame_host_manager_unittest.cc
index 2ba5fb8493a..a5100dfbe4b 100644
--- a/chromium/content/browser/frame_host/render_frame_host_manager_unittest.cc
+++ b/chromium/content/browser/frame_host/render_frame_host_manager_unittest.cc
@@ -2,12 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/files/file_path.h"
#include "base/strings/utf_string_conversions.h"
+#include "content/browser/frame_host/cross_site_transferring_request.h"
#include "content/browser/frame_host/navigation_controller_impl.h"
#include "content/browser/frame_host/navigation_entry_impl.h"
+#include "content/browser/frame_host/navigator.h"
#include "content/browser/frame_host/render_frame_host_manager.h"
#include "content/browser/site_instance_impl.h"
#include "content/browser/webui/web_ui_controller_factory_registry.h"
+#include "content/common/frame_messages.h"
#include "content/common/view_messages.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_service.h"
@@ -90,6 +94,159 @@ class BeforeUnloadFiredWebContentsDelegate : public WebContentsDelegate {
DISALLOW_COPY_AND_ASSIGN(BeforeUnloadFiredWebContentsDelegate);
};
+// This observer keeps track of the last deleted RenderViewHost to avoid
+// accessing it and causing use-after-free condition.
+class RenderViewHostDeletedObserver : public WebContentsObserver {
+ public:
+ RenderViewHostDeletedObserver(RenderViewHost* rvh)
+ : WebContentsObserver(WebContents::FromRenderViewHost(rvh)),
+ process_id_(rvh->GetProcess()->GetID()),
+ routing_id_(rvh->GetRoutingID()),
+ deleted_(false) {
+ }
+
+ virtual void RenderViewDeleted(RenderViewHost* render_view_host) OVERRIDE {
+ if (render_view_host->GetProcess()->GetID() == process_id_ &&
+ render_view_host->GetRoutingID() == routing_id_) {
+ deleted_ = true;
+ }
+ }
+
+ bool deleted() {
+ return deleted_;
+ }
+
+ private:
+ int process_id_;
+ int routing_id_;
+ bool deleted_;
+
+ DISALLOW_COPY_AND_ASSIGN(RenderViewHostDeletedObserver);
+};
+
+// This observer keeps track of the last deleted RenderFrameHost to avoid
+// accessing it and causing use-after-free condition.
+class RenderFrameHostDeletedObserver : public WebContentsObserver {
+ public:
+ RenderFrameHostDeletedObserver(RenderFrameHost* rfh)
+ : WebContentsObserver(WebContents::FromRenderFrameHost(rfh)),
+ process_id_(rfh->GetProcess()->GetID()),
+ routing_id_(rfh->GetRoutingID()),
+ deleted_(false) {
+ }
+
+ virtual void RenderFrameDeleted(RenderFrameHost* render_frame_host) OVERRIDE {
+ if (render_frame_host->GetProcess()->GetID() == process_id_ &&
+ render_frame_host->GetRoutingID() == routing_id_) {
+ deleted_ = true;
+ }
+ }
+
+ bool deleted() {
+ return deleted_;
+ }
+
+ private:
+ int process_id_;
+ int routing_id_;
+ bool deleted_;
+
+ DISALLOW_COPY_AND_ASSIGN(RenderFrameHostDeletedObserver);
+};
+
+
+// This observer is used to check whether IPC messages are being filtered for
+// swapped out RenderFrameHost objects. It observes the plugin crash and favicon
+// update events, which the FilterMessagesWhileSwappedOut test simulates being
+// sent. The test is successful if the event is not observed.
+// See http://crbug.com/351815
+class PluginFaviconMessageObserver : public WebContentsObserver {
+ public:
+ PluginFaviconMessageObserver(WebContents* web_contents)
+ : WebContentsObserver(web_contents),
+ plugin_crashed_(false),
+ favicon_received_(false) { }
+
+ virtual void PluginCrashed(const base::FilePath& plugin_path,
+ base::ProcessId plugin_pid) OVERRIDE {
+ plugin_crashed_ = true;
+ }
+
+ virtual void DidUpdateFaviconURL(
+ const std::vector<FaviconURL>& candidates) OVERRIDE {
+ favicon_received_ = true;
+ }
+
+ bool plugin_crashed() {
+ return plugin_crashed_;
+ }
+
+ bool favicon_received() {
+ return favicon_received_;
+ }
+
+ private:
+ bool plugin_crashed_;
+ bool favicon_received_;
+
+ DISALLOW_COPY_AND_ASSIGN(PluginFaviconMessageObserver);
+};
+
+// Ensures that RenderFrameDeleted and RenderFrameCreated are called in a
+// consistent manner.
+class FrameLifetimeConsistencyChecker : public WebContentsObserver {
+ public:
+ explicit FrameLifetimeConsistencyChecker(WebContentsImpl* web_contents)
+ : WebContentsObserver(web_contents) {
+ RenderViewCreated(web_contents->GetRenderViewHost());
+ RenderFrameCreated(web_contents->GetMainFrame());
+ }
+
+ virtual void RenderFrameCreated(RenderFrameHost* render_frame_host) OVERRIDE {
+ std::pair<int, int> routing_pair =
+ std::make_pair(render_frame_host->GetProcess()->GetID(),
+ render_frame_host->GetRoutingID());
+ bool was_live_already = !live_routes_.insert(routing_pair).second;
+ bool was_used_before = deleted_routes_.count(routing_pair) != 0;
+
+ if (was_live_already) {
+ FAIL() << "RenderFrameCreated called more than once for routing pair: "
+ << Format(render_frame_host);
+ } else if (was_used_before) {
+ FAIL() << "RenderFrameCreated called for routing pair "
+ << Format(render_frame_host) << " that was previously deleted.";
+ }
+ }
+
+ virtual void RenderFrameDeleted(RenderFrameHost* render_frame_host) OVERRIDE {
+ std::pair<int, int> routing_pair =
+ std::make_pair(render_frame_host->GetProcess()->GetID(),
+ render_frame_host->GetRoutingID());
+ bool was_live = live_routes_.erase(routing_pair);
+ bool was_dead_already = !deleted_routes_.insert(routing_pair).second;
+
+ if (was_dead_already) {
+ FAIL() << "RenderFrameDeleted called more than once for routing pair "
+ << Format(render_frame_host);
+ } else if (!was_live) {
+ FAIL() << "RenderFrameDeleted called for routing pair "
+ << Format(render_frame_host)
+ << " for which RenderFrameCreated was never called";
+ }
+ }
+
+ private:
+ std::string Format(RenderFrameHost* render_frame_host) {
+ return base::StringPrintf(
+ "(%d, %d -> %s )",
+ render_frame_host->GetProcess()->GetID(),
+ render_frame_host->GetRoutingID(),
+ render_frame_host->GetSiteInstance()->GetSiteURL().spec().c_str());
+ }
+ std::set<std::pair<int, int> > live_routes_;
+ std::set<std::pair<int, int> > deleted_routes_;
+};
+
} // namespace
class RenderFrameHostManagerTest
@@ -98,9 +255,11 @@ class RenderFrameHostManagerTest
virtual void SetUp() OVERRIDE {
RenderViewHostImplTestHarness::SetUp();
WebUIControllerFactory::RegisterFactory(&factory_);
+ lifetime_checker_.reset(new FrameLifetimeConsistencyChecker(contents()));
}
virtual void TearDown() OVERRIDE {
+ lifetime_checker_.reset();
RenderViewHostImplTestHarness::TearDown();
WebUIControllerFactory::UnregisterFactoryForTesting(&factory_);
}
@@ -109,6 +268,19 @@ class RenderFrameHostManagerTest
factory_.set_should_create_webui(should_create_webui);
}
+ void StartCrossSiteTransition(TestWebContents* contents) {
+ std::vector<GURL> url_chain;
+ contents->GetRenderManagerForTesting()->OnCrossSiteResponse(
+ contents->GetRenderManagerForTesting()->pending_frame_host(),
+ GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
+ url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
+ EXPECT_TRUE(contents->cross_navigation_pending());
+ RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
+ contents->GetRenderViewHost());
+ EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK,
+ rvh->rvh_state());
+ }
+
void NavigateActiveAndCommit(const GURL& url) {
// Note: we navigate the active RenderViewHost because previous navigations
// won't have committed yet, so NavigateAndCommit does the wrong thing
@@ -116,21 +288,36 @@ class RenderFrameHostManagerTest
controller().LoadURL(url, Referrer(), PAGE_TRANSITION_LINK, std::string());
TestRenderViewHost* old_rvh = test_rvh();
- // Simulate the ShouldClose_ACK that is received from the current renderer
+ // Simulate the BeforeUnload_ACK that is received from the current renderer
// for a cross-site navigation.
- if (old_rvh != active_rvh())
- old_rvh->SendShouldCloseACK(true);
+ if (old_rvh != active_rvh()) {
+ old_rvh->SendBeforeUnloadACK(true);
+ EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, old_rvh->rvh_state());
+ }
// Commit the navigation with a new page ID.
int32 max_page_id = contents()->GetMaxPageIDForSiteInstance(
active_rvh()->GetSiteInstance());
+ // Simulate the response coming from the pending renderer.
+ if (old_rvh != active_rvh())
+ StartCrossSiteTransition(contents());
+
// Simulate the SwapOut_ACK that fires if you commit a cross-site
// navigation.
- if (old_rvh != active_rvh())
+ if (old_rvh != active_rvh()) {
old_rvh->OnSwappedOut(false);
+ EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT,
+ old_rvh->rvh_state());
+ }
+ // Use an observer to avoid accessing a deleted renderer later on when the
+ // state is being checked.
+ RenderViewHostDeletedObserver rvh_observer(old_rvh);
active_test_rvh()->SendNavigate(max_page_id + 1, url);
+
+ if (old_rvh != active_rvh() && !rvh_observer.deleted())
+ EXPECT_TRUE(old_rvh->IsSwappedOut());
}
bool ShouldSwapProcesses(RenderFrameHostManager* manager,
@@ -167,17 +354,18 @@ class RenderFrameHostManagerTest
EXPECT_NE(ntp_rvh, dest_rvh);
// BeforeUnload finishes.
- ntp_rvh->SendShouldCloseACK(true);
+ ntp_rvh->SendBeforeUnloadACK(true);
- // Assume SwapOutACK times out, so the dest_rvh proceeds and commits.
dest_rvh->SendNavigate(101, kDestUrl);
+ ntp_rvh->OnSwappedOut(false);
- EXPECT_TRUE(ntp_rvh->is_swapped_out());
+ EXPECT_TRUE(ntp_rvh->IsSwappedOut());
return ntp_rvh;
}
private:
RenderFrameHostManagerTestWebUIControllerFactory factory_;
+ scoped_ptr<FrameLifetimeConsistencyChecker> lifetime_checker_;
};
// Tests that when you navigate from a chrome:// url to another page, and
@@ -218,8 +406,8 @@ TEST_F(RenderFrameHostManagerTest, NewTabPageProcesses) {
contents2->GetRenderManagerForTesting()->pending_render_view_host());
ASSERT_TRUE(dest_rvh2);
- ntp_rvh2->SendShouldCloseACK(true);
- ntp_rvh2->OnSwappedOut(false);
+ ntp_rvh2->SendBeforeUnloadACK(true);
+ StartCrossSiteTransition(contents2.get());
dest_rvh2->SendNavigate(101, kDestUrl);
// The two RVH's should be different in every way.
@@ -234,10 +422,10 @@ TEST_F(RenderFrameHostManagerTest, NewTabPageProcesses) {
contents2->GetController().LoadURL(
kChromeUrl, Referrer(), PAGE_TRANSITION_LINK, std::string());
- dest_rvh2->SendShouldCloseACK(true);
- dest_rvh2->OnSwappedOut(false);
+ dest_rvh2->SendBeforeUnloadACK(true);
+ StartCrossSiteTransition(contents2.get());
static_cast<TestRenderViewHost*>(contents2->GetRenderManagerForTesting()->
- pending_render_view_host())->SendNavigate(102, kChromeUrl);
+ pending_render_view_host())->SendNavigate(102, kChromeUrl);
EXPECT_NE(active_rvh()->GetSiteInstance(),
contents2->GetRenderViewHost()->GetSiteInstance());
@@ -253,53 +441,69 @@ TEST_F(RenderFrameHostManagerTest, NewTabPageProcesses) {
TEST_F(RenderFrameHostManagerTest, FilterMessagesWhileSwappedOut) {
const GURL kChromeURL("chrome://foo");
const GURL kDestUrl("http://www.google.com/");
+ std::vector<FaviconURL> icons;
// Navigate our first tab to a chrome url and then to the destination.
NavigateActiveAndCommit(kChromeURL);
TestRenderViewHost* ntp_rvh = static_cast<TestRenderViewHost*>(
contents()->GetRenderManagerForTesting()->current_host());
- // Send an update title message and make sure it works.
- const base::string16 ntp_title = ASCIIToUTF16("NTP Title");
- blink::WebTextDirection direction = blink::WebTextDirectionLeftToRight;
- EXPECT_TRUE(ntp_rvh->OnMessageReceived(
- ViewHostMsg_UpdateTitle(rvh()->GetRoutingID(), 0, ntp_title, direction)));
- EXPECT_EQ(ntp_title, contents()->GetTitle());
-
- // Navigate to a cross-site URL.
- contents()->GetController().LoadURL(
- kDestUrl, Referrer(), PAGE_TRANSITION_LINK, std::string());
- EXPECT_TRUE(contents()->cross_navigation_pending());
- TestRenderViewHost* dest_rvh = static_cast<TestRenderViewHost*>(
- contents()->GetRenderManagerForTesting()->pending_render_view_host());
- ASSERT_TRUE(dest_rvh);
- EXPECT_NE(ntp_rvh, dest_rvh);
-
- // Create one more view in the same SiteInstance where dest_rvh2
+ // Send an update favicon message and make sure it works.
+ const base::string16 ntp_title = base::ASCIIToUTF16("NTP Title");
+ {
+ PluginFaviconMessageObserver observer(contents());
+ EXPECT_TRUE(ntp_rvh->OnMessageReceived(
+ ViewHostMsg_UpdateFaviconURL(
+ rvh()->GetRoutingID(), icons)));
+ EXPECT_TRUE(observer.favicon_received());
+ }
+ // Create one more view in the same SiteInstance where ntp_rvh
// exists so that it doesn't get deleted on navigation to another
// site.
static_cast<SiteInstanceImpl*>(ntp_rvh->GetSiteInstance())->
increment_active_view_count();
- // BeforeUnload finishes.
- ntp_rvh->SendShouldCloseACK(true);
- // Assume SwapOutACK times out, so the dest_rvh proceeds and commits.
- dest_rvh->SendNavigate(101, kDestUrl);
+ // Navigate to a cross-site URL.
+ NavigateActiveAndCommit(kDestUrl);
+ TestRenderViewHost* dest_rvh = static_cast<TestRenderViewHost*>(
+ contents()->GetRenderViewHost());
+ ASSERT_TRUE(dest_rvh);
+ EXPECT_NE(ntp_rvh, dest_rvh);
+
+ // The new RVH should be able to update its favicon.
+ const base::string16 dest_title = base::ASCIIToUTF16("Google");
+ {
+ PluginFaviconMessageObserver observer(contents());
+ EXPECT_TRUE(
+ dest_rvh->OnMessageReceived(
+ ViewHostMsg_UpdateFaviconURL(rvh()->GetRoutingID(), icons)));
+ EXPECT_TRUE(observer.favicon_received());
+ }
- // The new RVH should be able to update its title.
- const base::string16 dest_title = ASCIIToUTF16("Google");
- EXPECT_TRUE(dest_rvh->OnMessageReceived(
- ViewHostMsg_UpdateTitle(rvh()->GetRoutingID(), 101, dest_title,
- direction)));
- EXPECT_EQ(dest_title, contents()->GetTitle());
+ // The old renderer, being slow, now updates the favicon. It should be
+ // filtered out and not take effect.
+ EXPECT_TRUE(ntp_rvh->IsSwappedOut());
+ {
+ PluginFaviconMessageObserver observer(contents());
+ EXPECT_TRUE(
+ ntp_rvh->OnMessageReceived(
+ ViewHostMsg_UpdateFaviconURL(rvh()->GetRoutingID(), icons)));
+ EXPECT_FALSE(observer.favicon_received());
+ }
- // The old renderer, being slow, now updates the title. It should be filtered
- // out and not take effect.
- EXPECT_TRUE(ntp_rvh->is_swapped_out());
- EXPECT_TRUE(ntp_rvh->OnMessageReceived(
- ViewHostMsg_UpdateTitle(rvh()->GetRoutingID(), 0, ntp_title, direction)));
- EXPECT_EQ(dest_title, contents()->GetTitle());
+ // The same logic should apply to RenderFrameHosts as well and routing through
+ // swapped out RFH shouldn't be allowed. Use a PluginCrashObserver to check
+ // if the IPC message is allowed through or not.
+ {
+ PluginFaviconMessageObserver observer(contents());
+ // TODO(nasko): Check that the RFH is in swapped out when the state moves
+ // from RVH to RFH.
+ EXPECT_TRUE(ntp_rvh->main_render_frame_host()->OnMessageReceived(
+ FrameHostMsg_PluginCrashed(
+ main_rfh()->GetRoutingID(), base::FilePath(), 0)));
+ EXPECT_FALSE(observer.plugin_crashed());
+ }
// We cannot filter out synchronous IPC messages, because the renderer would
// be left waiting for a reply. We pick RunBeforeUnloadConfirm as an example
@@ -308,23 +512,24 @@ TEST_F(RenderFrameHostManagerTest, FilterMessagesWhileSwappedOut) {
MockRenderProcessHost* ntp_process_host =
static_cast<MockRenderProcessHost*>(ntp_rvh->GetProcess());
ntp_process_host->sink().ClearMessages();
- const base::string16 msg = ASCIIToUTF16("Message");
+ RenderFrameHost* ntp_rfh = ntp_rvh->GetMainFrame();
+ const base::string16 msg = base::ASCIIToUTF16("Message");
bool result = false;
base::string16 unused;
- ViewHostMsg_RunBeforeUnloadConfirm before_unload_msg(
- rvh()->GetRoutingID(), kChromeURL, msg, false, &result, &unused);
+ FrameHostMsg_RunBeforeUnloadConfirm before_unload_msg(
+ ntp_rfh->GetRoutingID(), kChromeURL, msg, false, &result, &unused);
// Enable pumping for check in BrowserMessageFilter::CheckCanDispatchOnUI.
before_unload_msg.EnableMessagePumping();
- EXPECT_TRUE(ntp_rvh->OnMessageReceived(before_unload_msg));
+ EXPECT_TRUE(ntp_rfh->OnMessageReceived(before_unload_msg));
EXPECT_TRUE(ntp_process_host->sink().GetUniqueMessageMatching(IPC_REPLY_ID));
// Also test RunJavaScriptMessage.
ntp_process_host->sink().ClearMessages();
- ViewHostMsg_RunJavaScriptMessage js_msg(
- rvh()->GetRoutingID(), msg, msg, kChromeURL,
+ FrameHostMsg_RunJavaScriptMessage js_msg(
+ ntp_rfh->GetRoutingID(), msg, msg, kChromeURL,
JAVASCRIPT_MESSAGE_TYPE_CONFIRM, &result, &unused);
js_msg.EnableMessagePumping();
- EXPECT_TRUE(ntp_rvh->OnMessageReceived(js_msg));
+ EXPECT_TRUE(ntp_rfh->OnMessageReceived(js_msg));
EXPECT_TRUE(ntp_process_host->sink().GetUniqueMessageMatching(IPC_REPLY_ID));
}
@@ -345,23 +550,11 @@ TEST_F(RenderFrameHostManagerTest, WhiteListSwapCompositorFrame) {
EXPECT_TRUE(swapped_out_rwhv->did_swap_compositor_frame());
}
-TEST_F(RenderFrameHostManagerTest, WhiteListDidActivateAcceleratedCompositing) {
- TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost();
-
- MockRenderProcessHost* process_host =
- static_cast<MockRenderProcessHost*>(swapped_out_rvh->GetProcess());
- process_host->sink().ClearMessages();
- ViewHostMsg_DidActivateAcceleratedCompositing msg(
- rvh()->GetRoutingID(), true);
- EXPECT_TRUE(swapped_out_rvh->OnMessageReceived(msg));
- EXPECT_TRUE(swapped_out_rvh->is_accelerated_compositing_active());
-}
-
// Test if RenderViewHost::GetRenderWidgetHosts() only returns active
// widgets.
TEST_F(RenderFrameHostManagerTest, GetRenderWidgetHostsReturnsActiveViews) {
TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost();
- EXPECT_TRUE(swapped_out_rvh->is_swapped_out());
+ EXPECT_TRUE(swapped_out_rvh->IsSwappedOut());
scoped_ptr<RenderWidgetHostIterator> widgets(
RenderWidgetHost::GetRenderWidgetHosts());
@@ -371,7 +564,8 @@ TEST_F(RenderFrameHostManagerTest, GetRenderWidgetHostsReturnsActiveViews) {
RenderWidgetHost* widget = widgets->GetNextHost();
EXPECT_FALSE(widgets->GetNextHost());
RenderViewHost* rvh = RenderViewHost::From(widget);
- EXPECT_FALSE(static_cast<RenderViewHostImpl*>(rvh)->is_swapped_out());
+ EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT,
+ static_cast<RenderViewHostImpl*>(rvh)->rvh_state());
}
// Test if RenderViewHost::GetRenderWidgetHosts() returns a subset of
@@ -382,7 +576,7 @@ TEST_F(RenderFrameHostManagerTest, GetRenderWidgetHostsReturnsActiveViews) {
TEST_F(RenderFrameHostManagerTest,
GetRenderWidgetHostsWithinGetAllRenderWidgetHosts) {
TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost();
- EXPECT_TRUE(swapped_out_rvh->is_swapped_out());
+ EXPECT_TRUE(swapped_out_rvh->IsSwappedOut());
scoped_ptr<RenderWidgetHostIterator> widgets(
RenderWidgetHost::GetRenderWidgetHosts());
@@ -525,10 +719,10 @@ TEST_F(RenderFrameHostManagerTest, AlwaysSendEnableViewSourceMode) {
// Navigate.
controller().LoadURL(
kUrl, Referrer(), PAGE_TRANSITION_TYPED, std::string());
- // Simulate response from RenderView for FirePageBeforeUnload.
+ // Simulate response from RenderFrame for DispatchBeforeUnload.
base::TimeTicks now = base::TimeTicks::Now();
- test_rvh()->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(
- rvh()->GetRoutingID(), true, now, now));
+ main_test_rfh()->OnMessageReceived(FrameHostMsg_BeforeUnload_ACK(
+ main_test_rfh()->GetRoutingID(), true, now, now));
ASSERT_TRUE(pending_rvh()); // New pending RenderViewHost will be created.
RenderViewHost* last_rvh = pending_rvh();
int32 new_id = contents()->GetMaxPageIDForSiteInstance(
@@ -567,17 +761,18 @@ TEST_F(RenderFrameHostManagerTest, Init) {
scoped_ptr<TestWebContents> web_contents(
TestWebContents::Create(browser_context(), instance));
- RenderFrameHostManager manager(web_contents.get(), web_contents.get(),
- web_contents.get(), web_contents.get());
- manager.Init(browser_context(), instance, MSG_ROUTING_NONE, MSG_ROUTING_NONE);
-
- RenderViewHost* host = manager.current_host();
- ASSERT_TRUE(host);
- EXPECT_EQ(instance, host->GetSiteInstance());
- EXPECT_EQ(web_contents.get(), host->GetDelegate());
- EXPECT_TRUE(manager.GetRenderWidgetHostView());
- EXPECT_FALSE(manager.pending_render_view_host());
+ RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
+ RenderViewHostImpl* rvh = manager->current_host();
+ RenderFrameHostImpl* rfh = manager->current_frame_host();
+ ASSERT_TRUE(rvh);
+ ASSERT_TRUE(rfh);
+ EXPECT_EQ(rvh, rfh->render_view_host());
+ EXPECT_EQ(instance, rvh->GetSiteInstance());
+ EXPECT_EQ(web_contents.get(), rvh->GetDelegate());
+ EXPECT_EQ(web_contents.get(), rfh->delegate());
+ EXPECT_TRUE(manager->GetRenderWidgetHostView());
+ EXPECT_FALSE(manager->pending_render_view_host());
}
// Tests the Navigate function. We navigate three sites consecutively and check
@@ -592,13 +787,8 @@ TEST_F(RenderFrameHostManagerTest, Navigate) {
notifications.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
Source<WebContents>(web_contents.get()));
- // Create.
- RenderFrameHostManager manager(web_contents.get(), web_contents.get(),
- web_contents.get(), web_contents.get());
-
- manager.Init(browser_context(), instance, MSG_ROUTING_NONE, MSG_ROUTING_NONE);
-
- RenderViewHost* host;
+ RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
+ RenderFrameHostImpl* host;
// 1) The first navigation. --------------------------
const GURL kUrl1("http://www.google.com/");
@@ -606,16 +796,16 @@ TEST_F(RenderFrameHostManagerTest, Navigate) {
NULL /* instance */, -1 /* page_id */, kUrl1, Referrer(),
base::string16() /* title */, PAGE_TRANSITION_TYPED,
false /* is_renderer_init */);
- host = manager.Navigate(entry1);
+ host = manager->Navigate(entry1);
- // The RenderViewHost created in Init will be reused.
- EXPECT_TRUE(host == manager.current_host());
- EXPECT_FALSE(manager.pending_render_view_host());
+ // The RenderFrameHost created in Init will be reused.
+ EXPECT_TRUE(host == manager->current_frame_host());
+ EXPECT_FALSE(manager->pending_frame_host());
// Commit.
- manager.DidNavigateMainFrame(host);
+ manager->DidNavigateFrame(host);
// Commit to SiteInstance should be delayed until RenderView commit.
- EXPECT_TRUE(host == manager.current_host());
+ EXPECT_TRUE(host == manager->current_frame_host());
ASSERT_TRUE(host);
EXPECT_FALSE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
HasSite());
@@ -628,15 +818,15 @@ TEST_F(RenderFrameHostManagerTest, Navigate) {
Referrer(kUrl1, blink::WebReferrerPolicyDefault),
base::string16() /* title */, PAGE_TRANSITION_LINK,
true /* is_renderer_init */);
- host = manager.Navigate(entry2);
+ host = manager->Navigate(entry2);
- // The RenderViewHost created in Init will be reused.
- EXPECT_TRUE(host == manager.current_host());
- EXPECT_FALSE(manager.pending_render_view_host());
+ // The RenderFrameHost created in Init will be reused.
+ EXPECT_TRUE(host == manager->current_frame_host());
+ EXPECT_FALSE(manager->pending_frame_host());
// Commit.
- manager.DidNavigateMainFrame(host);
- EXPECT_TRUE(host == manager.current_host());
+ manager->DidNavigateFrame(host);
+ EXPECT_TRUE(host == manager->current_frame_host());
ASSERT_TRUE(host);
EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
HasSite());
@@ -648,22 +838,22 @@ TEST_F(RenderFrameHostManagerTest, Navigate) {
Referrer(kUrl2, blink::WebReferrerPolicyDefault),
base::string16() /* title */, PAGE_TRANSITION_LINK,
false /* is_renderer_init */);
- host = manager.Navigate(entry3);
+ host = manager->Navigate(entry3);
- // A new RenderViewHost should be created.
- EXPECT_TRUE(manager.pending_render_view_host());
- ASSERT_EQ(host, manager.pending_render_view_host());
+ // A new RenderFrameHost should be created.
+ EXPECT_TRUE(manager->pending_frame_host());
+ ASSERT_EQ(host, manager->pending_frame_host());
notifications.Reset();
// Commit.
- manager.DidNavigateMainFrame(manager.pending_render_view_host());
- EXPECT_TRUE(host == manager.current_host());
+ manager->DidNavigateFrame(manager->pending_frame_host());
+ EXPECT_TRUE(host == manager->current_frame_host());
ASSERT_TRUE(host);
EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
HasSite());
- // Check the pending RenderViewHost has been committed.
- EXPECT_FALSE(manager.pending_render_view_host());
+ // Check the pending RenderFrameHost has been committed.
+ EXPECT_FALSE(manager->pending_frame_host());
// We should observe a notification.
EXPECT_TRUE(
@@ -684,11 +874,7 @@ TEST_F(RenderFrameHostManagerTest, NavigateWithEarlyReNavigation) {
notifications.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
Source<WebContents>(web_contents.get()));
- // Create.
- RenderFrameHostManager manager(web_contents.get(), web_contents.get(),
- web_contents.get(), web_contents.get());
-
- manager.Init(browser_context(), instance, MSG_ROUTING_NONE, MSG_ROUTING_NONE);
+ RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
// 1) The first navigation. --------------------------
const GURL kUrl1("http://www.google.com/");
@@ -696,11 +882,11 @@ TEST_F(RenderFrameHostManagerTest, NavigateWithEarlyReNavigation) {
Referrer(), base::string16() /* title */,
PAGE_TRANSITION_TYPED,
false /* is_renderer_init */);
- RenderViewHost* host = manager.Navigate(entry1);
+ RenderFrameHostImpl* host = manager->Navigate(entry1);
- // The RenderViewHost created in Init will be reused.
- EXPECT_TRUE(host == manager.current_host());
- EXPECT_FALSE(manager.pending_render_view_host());
+ // The RenderFrameHost created in Init will be reused.
+ EXPECT_TRUE(host == manager->current_frame_host());
+ EXPECT_FALSE(manager->pending_frame_host());
// We should observe a notification.
EXPECT_TRUE(
@@ -708,10 +894,10 @@ TEST_F(RenderFrameHostManagerTest, NavigateWithEarlyReNavigation) {
notifications.Reset();
// Commit.
- manager.DidNavigateMainFrame(host);
+ manager->DidNavigateFrame(host);
// Commit to SiteInstance should be delayed until RenderView commit.
- EXPECT_TRUE(host == manager.current_host());
+ EXPECT_TRUE(host == manager->current_frame_host());
ASSERT_TRUE(host);
EXPECT_FALSE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
HasSite());
@@ -723,51 +909,50 @@ TEST_F(RenderFrameHostManagerTest, NavigateWithEarlyReNavigation) {
NULL /* instance */, -1 /* page_id */, kUrl2, Referrer(),
base::string16() /* title */, PAGE_TRANSITION_TYPED,
false /* is_renderer_init */);
- RenderViewHostImpl* host2 = static_cast<RenderViewHostImpl*>(
- manager.Navigate(entry2));
+ RenderFrameHostImpl* host2 = manager->Navigate(entry2);
int host2_process_id = host2->GetProcess()->GetID();
- // A new RenderViewHost should be created.
- EXPECT_TRUE(manager.pending_render_view_host());
- ASSERT_EQ(host2, manager.pending_render_view_host());
+ // A new RenderFrameHost should be created.
+ EXPECT_TRUE(manager->pending_frame_host());
+ ASSERT_EQ(host2, manager->pending_frame_host());
EXPECT_NE(host2, host);
// Check that the navigation is still suspended because the old RVH
// is not swapped out, yet.
- EXPECT_TRUE(host2->are_navigations_suspended());
+ EXPECT_TRUE(host2->render_view_host()->are_navigations_suspended());
MockRenderProcessHost* test_process_host2 =
static_cast<MockRenderProcessHost*>(host2->GetProcess());
test_process_host2->sink().ClearMessages();
- host2->NavigateToURL(kUrl2);
+ host2->render_view_host()->NavigateToURL(kUrl2);
EXPECT_FALSE(test_process_host2->sink().GetUniqueMessageMatching(
- ViewMsg_Navigate::ID));
+ FrameMsg_Navigate::ID));
// Allow closing the current Render View (precondition for swapping out
- // the RVH): Simulate response from RenderView for ViewMsg_ShouldClose sent by
- // FirePageBeforeUnload.
- TestRenderViewHost* test_host = static_cast<TestRenderViewHost*>(host);
+ // the RVH): Simulate response from RenderFrame for FrameMsg_BeforeUnload sent
+ // by DispatchBeforeUnload.
+ TestRenderViewHost* test_host =
+ static_cast<TestRenderViewHost*>(host->render_view_host());
MockRenderProcessHost* test_process_host =
static_cast<MockRenderProcessHost*>(test_host->GetProcess());
EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching(
- ViewMsg_ShouldClose::ID));
- test_host->SendShouldCloseACK(true);
+ FrameMsg_BeforeUnload::ID));
+ test_host->SendBeforeUnloadACK(true);
// CrossSiteResourceHandler::StartCrossSiteTransition triggers a
// call of RenderFrameHostManager::SwapOutOldPage before
- // RenderFrameHostManager::DidNavigateMainFrame is called.
- // The RVH is not swapped out until the commit.
- manager.SwapOutOldPage();
+ // RenderFrameHostManager::DidNavigateFrame is called.
+ // The RVH is swapped out after receiving the unload ack.
+ manager->SwapOutOldPage();
EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching(
- ViewMsg_SwapOut::ID));
+ FrameMsg_SwapOut::ID));
test_host->OnSwappedOut(false);
- EXPECT_EQ(host, manager.current_host());
- EXPECT_FALSE(static_cast<RenderViewHostImpl*>(
- manager.current_host())->is_swapped_out());
- EXPECT_EQ(host2, manager.pending_render_view_host());
+ EXPECT_EQ(host, manager->current_frame_host());
+ EXPECT_FALSE(manager->current_frame_host()->is_swapped_out());
+ EXPECT_EQ(host2, manager->pending_frame_host());
// There should be still no navigation messages being sent.
EXPECT_FALSE(test_process_host2->sink().GetUniqueMessageMatching(
- ViewMsg_Navigate::ID));
+ FrameMsg_Navigate::ID));
// 3) Cross-site navigate to next site before 2) has committed. --------------
const GURL kUrl3("http://webkit.org/");
@@ -776,51 +961,93 @@ TEST_F(RenderFrameHostManagerTest, NavigateWithEarlyReNavigation) {
PAGE_TRANSITION_TYPED,
false /* is_renderer_init */);
test_process_host->sink().ClearMessages();
- RenderViewHost* host3 = manager.Navigate(entry3);
+ RenderFrameHostImpl* host3 = manager->Navigate(entry3);
- // A new RenderViewHost should be created. host2 is now deleted.
- EXPECT_TRUE(manager.pending_render_view_host());
- ASSERT_EQ(host3, manager.pending_render_view_host());
+ // A new RenderFrameHost should be created. host2 is now deleted.
+ EXPECT_TRUE(manager->pending_frame_host());
+ ASSERT_EQ(host3, manager->pending_frame_host());
EXPECT_NE(host3, host);
EXPECT_NE(host3->GetProcess()->GetID(), host2_process_id);
- // Navigations in the new RVH should be suspended, which is ok because the
- // old RVH is not yet swapped out and can respond to a second beforeunload
- // request.
+ // Navigations in the new RVH should be suspended.
EXPECT_TRUE(static_cast<RenderViewHostImpl*>(
- host3)->are_navigations_suspended());
- EXPECT_EQ(host, manager.current_host());
- EXPECT_FALSE(static_cast<RenderViewHostImpl*>(
- manager.current_host())->is_swapped_out());
+ host3->render_view_host())->are_navigations_suspended());
+ EXPECT_EQ(host, manager->current_frame_host());
+ EXPECT_FALSE(manager->current_frame_host()->is_swapped_out());
// Simulate a response to the second beforeunload request.
EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching(
- ViewMsg_ShouldClose::ID));
- test_host->SendShouldCloseACK(true);
+ FrameMsg_BeforeUnload::ID));
+ test_host->SendBeforeUnloadACK(true);
// CrossSiteResourceHandler::StartCrossSiteTransition triggers a
// call of RenderFrameHostManager::SwapOutOldPage before
- // RenderFrameHostManager::DidNavigateMainFrame is called.
- // The RVH is not swapped out until the commit.
- manager.SwapOutOldPage();
- EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching(
- ViewMsg_SwapOut::ID));
+ // RenderFrameHostManager::DidNavigateFrame is called. Since the previous
+ // navigation has already caused the renderer to start swapping out, there
+ // will be no more SwapOut messages being sent.
+ manager->SwapOutOldPage();
+ EXPECT_FALSE(test_process_host->sink().GetUniqueMessageMatching(
+ FrameMsg_SwapOut::ID));
test_host->OnSwappedOut(false);
// Commit.
- manager.DidNavigateMainFrame(host3);
- EXPECT_TRUE(host3 == manager.current_host());
+ manager->DidNavigateFrame(host3);
+ EXPECT_TRUE(host3 == manager->current_frame_host());
ASSERT_TRUE(host3);
EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host3->GetSiteInstance())->
HasSite());
- // Check the pending RenderViewHost has been committed.
- EXPECT_FALSE(manager.pending_render_view_host());
+ // Check the pending RenderFrameHost has been committed.
+ EXPECT_FALSE(manager->pending_frame_host());
// We should observe a notification.
EXPECT_TRUE(
notifications.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED));
}
+// Test that navigation is not blocked when we make new navigation before
+// previous one has been committed. This is also a regression test for
+// http://crbug.com/104600.
+TEST_F(RenderFrameHostManagerTest, NewCrossNavigationBetweenSwapOutAndCommit) {
+ const GURL kUrl1("http://www.google.com/");
+ const GURL kUrl2("http://www.chromium.org/");
+ const GURL kUrl3("http://www.youtube.com/");
+
+ contents()->NavigateAndCommit(kUrl1);
+ TestRenderViewHost* rvh1 = test_rvh();
+
+ // Keep active_view_count nonzero so that no swapped out views in
+ // this SiteInstance get forcefully deleted.
+ static_cast<SiteInstanceImpl*>(rvh1->GetSiteInstance())->
+ increment_active_view_count();
+
+ // Navigate but don't commit.
+ contents()->GetController().LoadURL(
+ kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
+ EXPECT_TRUE(rvh1->is_waiting_for_beforeunload_ack());
+ contents()->ProceedWithCrossSiteNavigation();
+ EXPECT_FALSE(rvh1->is_waiting_for_beforeunload_ack());
+ StartCrossSiteTransition(contents());
+ EXPECT_TRUE(rvh1->IsWaitingForUnloadACK());
+
+ rvh1->OnSwappedOut(false);
+ EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT, rvh1->rvh_state());
+
+ TestRenderViewHost* rvh2 = pending_test_rvh();
+ EXPECT_TRUE(rvh2);
+ static_cast<SiteInstanceImpl*>(rvh2->GetSiteInstance())->
+ increment_active_view_count();
+
+ contents()->GetController().LoadURL(
+ kUrl3, Referrer(), PAGE_TRANSITION_LINK, std::string());
+ // Pending rvh2 is already deleted.
+ contents()->ProceedWithCrossSiteNavigation();
+
+ TestRenderViewHost* rvh3 = pending_test_rvh();
+ EXPECT_TRUE(rvh3);
+ // Navigation should be already unblocked by rvh1.
+ EXPECT_FALSE(rvh3->are_navigations_suspended());
+}
+
// Tests WebUI creation.
TEST_F(RenderFrameHostManagerTest, WebUI) {
set_should_create_webui(true);
@@ -828,25 +1055,23 @@ TEST_F(RenderFrameHostManagerTest, WebUI) {
scoped_ptr<TestWebContents> web_contents(
TestWebContents::Create(browser_context(), instance));
- RenderFrameHostManager manager(web_contents.get(), web_contents.get(),
- web_contents.get(), web_contents.get());
+ RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
- manager.Init(browser_context(), instance, MSG_ROUTING_NONE, MSG_ROUTING_NONE);
- EXPECT_FALSE(manager.current_host()->IsRenderViewLive());
+ EXPECT_FALSE(manager->current_host()->IsRenderViewLive());
const GURL kUrl("chrome://foo");
NavigationEntryImpl entry(NULL /* instance */, -1 /* page_id */, kUrl,
Referrer(), base::string16() /* title */,
PAGE_TRANSITION_TYPED,
false /* is_renderer_init */);
- RenderViewHost* host = manager.Navigate(entry);
+ RenderFrameHostImpl* host = manager->Navigate(entry);
- // We commit the pending RenderViewHost immediately because the previous
- // RenderViewHost was not live. We test a case where it is live in
+ // We commit the pending RenderFrameHost immediately because the previous
+ // RenderFrameHost was not live. We test a case where it is live in
// WebUIInNewTab.
EXPECT_TRUE(host);
- EXPECT_EQ(host, manager.current_host());
- EXPECT_FALSE(manager.pending_render_view_host());
+ EXPECT_EQ(host, manager->current_frame_host());
+ EXPECT_FALSE(manager->pending_frame_host());
// It's important that the site instance get set on the Web UI page as soon
// as the navigation starts, rather than lazily after it commits, so we don't
@@ -857,13 +1082,14 @@ TEST_F(RenderFrameHostManagerTest, WebUI) {
EXPECT_EQ(kUrl, host->GetSiteInstance()->GetSiteURL());
// The Web UI is committed immediately because the RenderViewHost has not been
- // used yet. UpdateRendererStateForNavigate() took the short cut path.
- EXPECT_FALSE(manager.pending_web_ui());
- EXPECT_TRUE(manager.web_ui());
+ // used yet. UpdateStateForNavigate() took the short cut path.
+ EXPECT_FALSE(manager->pending_web_ui());
+ EXPECT_TRUE(manager->web_ui());
// Commit.
- manager.DidNavigateMainFrame(host);
- EXPECT_TRUE(host->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
+ manager->DidNavigateFrame(host);
+ EXPECT_TRUE(
+ host->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
}
// Tests that we can open a WebUI link in a new tab from a WebUI page and still
@@ -875,12 +1101,11 @@ TEST_F(RenderFrameHostManagerTest, WebUIInNewTab) {
// Create a blank tab.
scoped_ptr<TestWebContents> web_contents1(
TestWebContents::Create(browser_context(), blank_instance));
- RenderFrameHostManager manager1(web_contents1.get(), web_contents1.get(),
- web_contents1.get(), web_contents1.get());
- manager1.Init(
- browser_context(), blank_instance, MSG_ROUTING_NONE, MSG_ROUTING_NONE);
+ RenderFrameHostManager* manager1 =
+ web_contents1->GetRenderManagerForTesting();
// Test the case that new RVH is considered live.
- manager1.current_host()->CreateRenderView(base::string16(), -1, -1);
+ manager1->current_host()->CreateRenderView(
+ base::string16(), -1, MSG_ROUTING_NONE, -1, false);
// Navigate to a WebUI page.
const GURL kUrl1("chrome://foo");
@@ -888,44 +1113,46 @@ TEST_F(RenderFrameHostManagerTest, WebUIInNewTab) {
Referrer(), base::string16() /* title */,
PAGE_TRANSITION_TYPED,
false /* is_renderer_init */);
- RenderViewHost* host1 = manager1.Navigate(entry1);
+ RenderFrameHostImpl* host1 = manager1->Navigate(entry1);
// We should have a pending navigation to the WebUI RenderViewHost.
// It should already have bindings.
- EXPECT_EQ(host1, manager1.pending_render_view_host());
- EXPECT_NE(host1, manager1.current_host());
- EXPECT_TRUE(host1->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
+ EXPECT_EQ(host1, manager1->pending_frame_host());
+ EXPECT_NE(host1, manager1->current_frame_host());
+ EXPECT_TRUE(
+ host1->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
// Commit and ensure we still have bindings.
- manager1.DidNavigateMainFrame(host1);
+ manager1->DidNavigateFrame(host1);
SiteInstance* webui_instance = host1->GetSiteInstance();
- EXPECT_EQ(host1, manager1.current_host());
- EXPECT_TRUE(host1->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
+ EXPECT_EQ(host1, manager1->current_frame_host());
+ EXPECT_TRUE(
+ host1->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
// Now simulate clicking a link that opens in a new tab.
scoped_ptr<TestWebContents> web_contents2(
TestWebContents::Create(browser_context(), webui_instance));
- RenderFrameHostManager manager2(web_contents2.get(), web_contents2.get(),
- web_contents2.get(), web_contents2.get());
- manager2.Init(
- browser_context(), webui_instance, MSG_ROUTING_NONE, MSG_ROUTING_NONE);
+ RenderFrameHostManager* manager2 =
+ web_contents2->GetRenderManagerForTesting();
// Make sure the new RVH is considered live. This is usually done in
// RenderWidgetHost::Init when opening a new tab from a link.
- manager2.current_host()->CreateRenderView(base::string16(), -1, -1);
+ manager2->current_host()->CreateRenderView(
+ base::string16(), -1, MSG_ROUTING_NONE, -1, false);
const GURL kUrl2("chrome://foo/bar");
NavigationEntryImpl entry2(NULL /* instance */, -1 /* page_id */, kUrl2,
Referrer(), base::string16() /* title */,
PAGE_TRANSITION_LINK,
true /* is_renderer_init */);
- RenderViewHost* host2 = manager2.Navigate(entry2);
+ RenderFrameHostImpl* host2 = manager2->Navigate(entry2);
// No cross-process transition happens because we are already in the right
// SiteInstance. We should grant bindings immediately.
- EXPECT_EQ(host2, manager2.current_host());
- EXPECT_TRUE(host2->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
+ EXPECT_EQ(host2, manager2->current_frame_host());
+ EXPECT_TRUE(
+ host2->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
- manager2.DidNavigateMainFrame(host2);
+ manager2->DidNavigateFrame(host2);
}
// Tests that we don't end up in an inconsistent state if a page does a back and
@@ -951,7 +1178,7 @@ TEST_F(RenderFrameHostManagerTest, PageDoesBackAndReload) {
pending_render_view_host());
// Before that RVH has committed, the evil page reloads itself.
- ViewHostMsg_FrameNavigate_Params params;
+ FrameHostMsg_DidCommitProvisionalLoad_Params params;
params.page_id = 1;
params.url = kUrl2;
params.transition = PAGE_TRANSITION_CLIENT_REDIRECT;
@@ -960,7 +1187,11 @@ TEST_F(RenderFrameHostManagerTest, PageDoesBackAndReload) {
params.was_within_same_page = false;
params.is_post = false;
params.page_state = PageState::CreateFromURL(kUrl2);
- contents()->DidNavigate(evil_rvh, params);
+
+ RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(evil_rvh);
+ RenderFrameHostImpl* rfh = RenderFrameHostImpl::FromID(
+ rvh->GetProcess()->GetID(), rvh->main_frame_routing_id());
+ contents()->GetFrameTree()->root()->navigator()->DidNavigate(rfh, params);
// That should have cancelled the pending RVH, and the evil RVH should be the
// current one.
@@ -1002,24 +1233,25 @@ TEST_F(RenderFrameHostManagerTest, NavigateAfterMissingSwapOutACK) {
EXPECT_TRUE(rvh2->is_waiting_for_beforeunload_ack());
contents()->ProceedWithCrossSiteNavigation();
EXPECT_FALSE(rvh2->is_waiting_for_beforeunload_ack());
- rvh2->SwapOut();
- EXPECT_TRUE(rvh2->is_waiting_for_unload_ack());
+ StartCrossSiteTransition(contents());
+ EXPECT_TRUE(rvh2->IsWaitingForUnloadACK());
- // The back navigation commits. We should proactively clear the
- // is_waiting_for_unload_ack state to be safe.
+ // The back navigation commits.
const NavigationEntry* entry1 = contents()->GetController().GetPendingEntry();
rvh1->SendNavigate(entry1->GetPageID(), entry1->GetURL());
- EXPECT_TRUE(rvh2->is_swapped_out());
- EXPECT_FALSE(rvh2->is_waiting_for_unload_ack());
+ EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT, rvh2->rvh_state());
// We should be able to navigate forward.
contents()->GetController().GoForward();
contents()->ProceedWithCrossSiteNavigation();
+ StartCrossSiteTransition(contents());
const NavigationEntry* entry2 = contents()->GetController().GetPendingEntry();
rvh2->SendNavigate(entry2->GetPageID(), entry2->GetURL());
EXPECT_EQ(rvh2, rvh());
- EXPECT_FALSE(rvh2->is_swapped_out());
- EXPECT_TRUE(rvh1->is_swapped_out());
+ EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state());
+ EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT, rvh1->rvh_state());
+ rvh1->OnSwappedOut(false);
+ EXPECT_TRUE(rvh1->IsSwappedOut());
}
// Test that we create swapped out RVHs for the opener chain when navigating an
@@ -1058,21 +1290,21 @@ TEST_F(RenderFrameHostManagerTest, CreateSwappedOutOpenerRVHs) {
rvh2->GetSiteInstance()));
// Ensure rvh1 is placed on swapped out list of the current tab.
- EXPECT_TRUE(manager->IsOnSwappedOutList(rvh1));
+ EXPECT_TRUE(manager->IsRVHOnSwappedOutList(rvh1));
EXPECT_EQ(rvh1,
manager->GetSwappedOutRenderViewHost(rvh1->GetSiteInstance()));
// Ensure a swapped out RVH is created in the first opener tab.
TestRenderViewHost* opener1_rvh = static_cast<TestRenderViewHost*>(
opener1_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance()));
- EXPECT_TRUE(opener1_manager->IsOnSwappedOutList(opener1_rvh));
- EXPECT_TRUE(opener1_rvh->is_swapped_out());
+ EXPECT_TRUE(opener1_manager->IsRVHOnSwappedOutList(opener1_rvh));
+ EXPECT_TRUE(opener1_rvh->IsSwappedOut());
// Ensure a swapped out RVH is created in the second opener tab.
TestRenderViewHost* opener2_rvh = static_cast<TestRenderViewHost*>(
opener2_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance()));
- EXPECT_TRUE(opener2_manager->IsOnSwappedOutList(opener2_rvh));
- EXPECT_TRUE(opener2_rvh->is_swapped_out());
+ EXPECT_TRUE(opener2_manager->IsRVHOnSwappedOutList(opener2_rvh));
+ EXPECT_TRUE(opener2_rvh->IsSwappedOut());
// Navigate to a cross-BrowsingInstance URL.
contents()->NavigateAndCommit(kChromeUrl);
@@ -1107,7 +1339,8 @@ TEST_F(RenderFrameHostManagerTest, CleanUpSwappedOutRVHOnProcessCrash) {
contents()->SetOpener(opener1.get());
// Make sure the new opener RVH is considered live.
- opener1_manager->current_host()->CreateRenderView(base::string16(), -1, -1);
+ opener1_manager->current_host()->CreateRenderView(
+ base::string16(), -1, MSG_ROUTING_NONE, -1, false);
// Use a cross-process navigation in the opener to swap out the old RVH.
EXPECT_FALSE(opener1_manager->GetSwappedOutRenderViewHost(
@@ -1173,8 +1406,8 @@ TEST_F(RenderFrameHostManagerTest, EnableWebUIWithSwappedOutOpener) {
// Ensure a swapped out RVH is created in the first opener tab.
TestRenderViewHost* opener1_rvh = static_cast<TestRenderViewHost*>(
opener1_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance()));
- EXPECT_TRUE(opener1_manager->IsOnSwappedOutList(opener1_rvh));
- EXPECT_TRUE(opener1_rvh->is_swapped_out());
+ EXPECT_TRUE(opener1_manager->IsRVHOnSwappedOutList(opener1_rvh));
+ EXPECT_TRUE(opener1_rvh->IsSwappedOut());
// Ensure the new RVH has WebUI bindings.
EXPECT_TRUE(rvh2->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
@@ -1190,13 +1423,9 @@ TEST_F(RenderFrameHostManagerTest, NoSwapOnGuestNavigations) {
scoped_ptr<TestWebContents> web_contents(
TestWebContents::Create(browser_context(), instance));
- // Create.
- RenderFrameHostManager manager(web_contents.get(), web_contents.get(),
- web_contents.get(), web_contents.get());
-
- manager.Init(browser_context(), instance, MSG_ROUTING_NONE, MSG_ROUTING_NONE);
+ RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
- RenderViewHost* host;
+ RenderFrameHostImpl* host;
// 1) The first navigation. --------------------------
const GURL kUrl1("http://www.google.com/");
@@ -1204,17 +1433,17 @@ TEST_F(RenderFrameHostManagerTest, NoSwapOnGuestNavigations) {
NULL /* instance */, -1 /* page_id */, kUrl1, Referrer(),
base::string16() /* title */, PAGE_TRANSITION_TYPED,
false /* is_renderer_init */);
- host = manager.Navigate(entry1);
+ host = manager->Navigate(entry1);
- // The RenderViewHost created in Init will be reused.
- EXPECT_TRUE(host == manager.current_host());
- EXPECT_FALSE(manager.pending_render_view_host());
- EXPECT_EQ(manager.current_host()->GetSiteInstance(), instance);
+ // The RenderFrameHost created in Init will be reused.
+ EXPECT_TRUE(host == manager->current_frame_host());
+ EXPECT_FALSE(manager->pending_frame_host());
+ EXPECT_EQ(manager->current_frame_host()->GetSiteInstance(), instance);
// Commit.
- manager.DidNavigateMainFrame(host);
+ manager->DidNavigateFrame(host);
// Commit to SiteInstance should be delayed until RenderView commit.
- EXPECT_EQ(host, manager.current_host());
+ EXPECT_EQ(host, manager->current_frame_host());
ASSERT_TRUE(host);
EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
HasSite());
@@ -1227,15 +1456,15 @@ TEST_F(RenderFrameHostManagerTest, NoSwapOnGuestNavigations) {
Referrer(kUrl1, blink::WebReferrerPolicyDefault),
base::string16() /* title */, PAGE_TRANSITION_LINK,
true /* is_renderer_init */);
- host = manager.Navigate(entry2);
+ host = manager->Navigate(entry2);
- // The RenderViewHost created in Init will be reused.
- EXPECT_EQ(host, manager.current_host());
- EXPECT_FALSE(manager.pending_render_view_host());
+ // The RenderFrameHost created in Init will be reused.
+ EXPECT_EQ(host, manager->current_frame_host());
+ EXPECT_FALSE(manager->pending_frame_host());
// Commit.
- manager.DidNavigateMainFrame(host);
- EXPECT_EQ(host, manager.current_host());
+ manager->DidNavigateFrame(host);
+ EXPECT_EQ(host, manager->current_frame_host());
ASSERT_TRUE(host);
EXPECT_EQ(static_cast<SiteInstanceImpl*>(host->GetSiteInstance()),
instance);
@@ -1255,11 +1484,7 @@ TEST_F(RenderFrameHostManagerTest, NavigateWithEarlyClose) {
notifications.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
Source<WebContents>(web_contents.get()));
- // Create.
- RenderFrameHostManager manager(web_contents.get(), web_contents.get(),
- web_contents.get(), web_contents.get());
-
- manager.Init(browser_context(), instance, MSG_ROUTING_NONE, MSG_ROUTING_NONE);
+ RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
// 1) The first navigation. --------------------------
const GURL kUrl1("http://www.google.com/");
@@ -1267,11 +1492,11 @@ TEST_F(RenderFrameHostManagerTest, NavigateWithEarlyClose) {
Referrer(), base::string16() /* title */,
PAGE_TRANSITION_TYPED,
false /* is_renderer_init */);
- RenderViewHost* host = manager.Navigate(entry1);
+ RenderFrameHostImpl* host = manager->Navigate(entry1);
- // The RenderViewHost created in Init will be reused.
- EXPECT_EQ(host, manager.current_host());
- EXPECT_FALSE(manager.pending_render_view_host());
+ // The RenderFrameHost created in Init will be reused.
+ EXPECT_EQ(host, manager->current_frame_host());
+ EXPECT_FALSE(manager->pending_frame_host());
// We should observe a notification.
EXPECT_TRUE(
@@ -1279,10 +1504,10 @@ TEST_F(RenderFrameHostManagerTest, NavigateWithEarlyClose) {
notifications.Reset();
// Commit.
- manager.DidNavigateMainFrame(host);
+ manager->DidNavigateFrame(host);
- // Commit to SiteInstance should be delayed until RenderView commit.
- EXPECT_EQ(host, manager.current_host());
+ // Commit to SiteInstance should be delayed until RenderFrame commits.
+ EXPECT_EQ(host, manager->current_frame_host());
EXPECT_FALSE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
HasSite());
static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->SetSite(kUrl1);
@@ -1293,27 +1518,330 @@ TEST_F(RenderFrameHostManagerTest, NavigateWithEarlyClose) {
NULL /* instance */, -1 /* page_id */, kUrl2, Referrer(),
base::string16() /* title */, PAGE_TRANSITION_TYPED,
false /* is_renderer_init */);
- RenderViewHostImpl* host2 = static_cast<RenderViewHostImpl*>(
- manager.Navigate(entry2));
+ RenderFrameHostImpl* host2 = manager->Navigate(entry2);
- // A new RenderViewHost should be created.
- ASSERT_EQ(host2, manager.pending_render_view_host());
+ // A new RenderFrameHost should be created.
+ ASSERT_EQ(host2, manager->pending_frame_host());
EXPECT_NE(host2, host);
- EXPECT_EQ(host, manager.current_host());
- EXPECT_FALSE(static_cast<RenderViewHostImpl*>(
- manager.current_host())->is_swapped_out());
- EXPECT_EQ(host2, manager.pending_render_view_host());
+ EXPECT_EQ(host, manager->current_frame_host());
+ EXPECT_FALSE(manager->current_frame_host()->is_swapped_out());
+ EXPECT_EQ(host2, manager->pending_frame_host());
// 3) Close the tab. -------------------------
notifications.ListenFor(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
- Source<RenderWidgetHost>(host2));
- manager.ShouldClosePage(false, true, base::TimeTicks());
+ Source<RenderWidgetHost>(host2->render_view_host()));
+ manager->OnBeforeUnloadACK(false, true, base::TimeTicks());
EXPECT_TRUE(
notifications.Check1AndReset(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED));
- EXPECT_FALSE(manager.pending_render_view_host());
- EXPECT_EQ(host, manager.current_host());
+ EXPECT_FALSE(manager->pending_frame_host());
+ EXPECT_EQ(host, manager->current_frame_host());
+}
+
+// Tests that the RenderViewHost is properly deleted when the SwapOutACK is
+// received before the new page commits.
+TEST_F(RenderFrameHostManagerTest,
+ SwapOutACKBeforeNewPageCommitsLeadsToDeletion) {
+ const GURL kUrl1("http://www.google.com/");
+ const GURL kUrl2("http://www.chromium.org/");
+
+ // Navigate to the first page.
+ contents()->NavigateAndCommit(kUrl1);
+ TestRenderViewHost* rvh1 = test_rvh();
+ RenderViewHostDeletedObserver rvh_deleted_observer(rvh1);
+ EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
+
+ // Navigate to new site, simulating onbeforeunload approval.
+ controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
+ base::TimeTicks now = base::TimeTicks::Now();
+ main_test_rfh()->OnMessageReceived(
+ FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
+ EXPECT_TRUE(contents()->cross_navigation_pending());
+ TestRenderViewHost* rvh2 =
+ static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
+
+ // Simulate rvh2's response, which leads to an unload request being sent to
+ // rvh1.
+ std::vector<GURL> url_chain;
+ url_chain.push_back(GURL());
+ contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
+ contents()->GetRenderManagerForTesting()->pending_frame_host(),
+ GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
+ url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
+ EXPECT_TRUE(contents()->cross_navigation_pending());
+ EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK,
+ rvh1->rvh_state());
+
+ // Simulate the swap out ack.
+ rvh1->OnSwappedOut(false);
+ EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT, rvh1->rvh_state());
+
+ // The new page commits.
+ contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED);
+ EXPECT_FALSE(contents()->cross_navigation_pending());
+ EXPECT_EQ(rvh2, rvh());
+ EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
+ EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state());
+
+ // rvh1 should have been deleted.
+ EXPECT_TRUE(rvh_deleted_observer.deleted());
+ rvh1 = NULL;
+}
+
+// Tests that the RenderViewHost is properly swapped out when the SwapOutACK is
+// received before the new page commits.
+TEST_F(RenderFrameHostManagerTest,
+ SwapOutACKBeforeNewPageCommitsLeadsToSwapOut) {
+ const GURL kUrl1("http://www.google.com/");
+ const GURL kUrl2("http://www.chromium.org/");
+
+ // Navigate to the first page.
+ contents()->NavigateAndCommit(kUrl1);
+ TestRenderViewHost* rvh1 = test_rvh();
+ RenderViewHostDeletedObserver rvh_deleted_observer(rvh1);
+ EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
+
+ // Increment the number of active views in SiteInstanceImpl so that rvh2 is
+ // not deleted on swap out.
+ static_cast<SiteInstanceImpl*>(
+ rvh1->GetSiteInstance())->increment_active_view_count();
+
+ // Navigate to new site, simulating onbeforeunload approval.
+ controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
+ base::TimeTicks now = base::TimeTicks::Now();
+ main_test_rfh()->OnMessageReceived(
+ FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
+ EXPECT_TRUE(contents()->cross_navigation_pending());
+ TestRenderViewHost* rvh2 =
+ static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
+
+ // Simulate rvh2's response, which leads to an unload request being sent to
+ // rvh1.
+ std::vector<GURL> url_chain;
+ url_chain.push_back(GURL());
+ contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
+ contents()->GetRenderManagerForTesting()->pending_frame_host(),
+ GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
+ url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
+ EXPECT_TRUE(contents()->cross_navigation_pending());
+ EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK,
+ rvh1->rvh_state());
+
+ // Simulate the swap out ack.
+ rvh1->OnSwappedOut(false);
+ EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT, rvh1->rvh_state());
+
+ // The new page commits.
+ contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED);
+ EXPECT_FALSE(contents()->cross_navigation_pending());
+ EXPECT_EQ(rvh2, rvh());
+ EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
+ EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state());
+
+ // rvh1 should be swapped out.
+ EXPECT_FALSE(rvh_deleted_observer.deleted());
+ EXPECT_TRUE(rvh1->IsSwappedOut());
+}
+
+// Tests that the RenderViewHost is properly deleted when the new
+// page commits before the swap out ack is received.
+TEST_F(RenderFrameHostManagerTest,
+ NewPageCommitsBeforeSwapOutACKLeadsToDeletion) {
+ const GURL kUrl1("http://www.google.com/");
+ const GURL kUrl2("http://www.chromium.org/");
+
+ // Navigate to the first page.
+ contents()->NavigateAndCommit(kUrl1);
+ TestRenderViewHost* rvh1 = test_rvh();
+ RenderViewHostDeletedObserver rvh_deleted_observer(rvh1);
+ EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
+
+ // Navigate to new site, simulating onbeforeunload approval.
+ controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
+ base::TimeTicks now = base::TimeTicks::Now();
+ main_test_rfh()->OnMessageReceived(
+ FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
+ EXPECT_TRUE(contents()->cross_navigation_pending());
+ TestRenderViewHost* rvh2 =
+ static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
+
+ // Simulate rvh2's response, which leads to an unload request being sent to
+ // rvh1.
+ std::vector<GURL> url_chain;
+ url_chain.push_back(GURL());
+ contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
+ contents()->GetRenderManagerForTesting()->pending_frame_host(),
+ GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
+ url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
+ EXPECT_TRUE(contents()->cross_navigation_pending());
+ EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK,
+ rvh1->rvh_state());
+
+ // The new page commits.
+ contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED);
+ EXPECT_FALSE(contents()->cross_navigation_pending());
+ EXPECT_EQ(rvh2, rvh());
+ EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
+ EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state());
+ EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SHUTDOWN, rvh1->rvh_state());
+
+ // Simulate the swap out ack.
+ rvh1->OnSwappedOut(false);
+
+ // rvh1 should have been deleted.
+ EXPECT_TRUE(rvh_deleted_observer.deleted());
+ rvh1 = NULL;
+}
+
+// Tests that the RenderViewHost is properly swapped out when the new page
+// commits before the swap out ack is received.
+TEST_F(RenderFrameHostManagerTest,
+ NewPageCommitsBeforeSwapOutACKLeadsToSwapOut) {
+ const GURL kUrl1("http://www.google.com/");
+ const GURL kUrl2("http://www.chromium.org/");
+
+ // Navigate to the first page.
+ contents()->NavigateAndCommit(kUrl1);
+ TestRenderViewHost* rvh1 = test_rvh();
+ RenderViewHostDeletedObserver rvh_deleted_observer(rvh1);
+ EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
+
+ // Increment the number of active views in SiteInstanceImpl so that rvh1 is
+ // not deleted on swap out.
+ static_cast<SiteInstanceImpl*>(
+ rvh1->GetSiteInstance())->increment_active_view_count();
+
+ // Navigate to new site, simulating onbeforeunload approval.
+ controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
+ base::TimeTicks now = base::TimeTicks::Now();
+ main_test_rfh()->OnMessageReceived(
+ FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
+ EXPECT_TRUE(contents()->cross_navigation_pending());
+ TestRenderViewHost* rvh2 =
+ static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
+
+ // Simulate rvh2's response, which leads to an unload request being sent to
+ // rvh1.
+ std::vector<GURL> url_chain;
+ url_chain.push_back(GURL());
+ contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
+ contents()->GetRenderManagerForTesting()->pending_frame_host(),
+ GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
+ url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
+ EXPECT_TRUE(contents()->cross_navigation_pending());
+ EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK,
+ rvh1->rvh_state());
+
+ // The new page commits.
+ contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED);
+ EXPECT_FALSE(contents()->cross_navigation_pending());
+ EXPECT_EQ(rvh2, rvh());
+ EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
+ EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state());
+ EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT, rvh1->rvh_state());
+
+ // Simulate the swap out ack.
+ rvh1->OnSwappedOut(false);
+
+ // rvh1 should be swapped out.
+ EXPECT_FALSE(rvh_deleted_observer.deleted());
+ EXPECT_TRUE(rvh1->IsSwappedOut());
+}
+
+// Test that the RenderViewHost is properly swapped out if a navigation in the
+// new renderer commits before sending the SwapOut message to the old renderer.
+// This simulates a cross-site navigation to a synchronously committing URL
+// (e.g., a data URL) and ensures it works properly.
+TEST_F(RenderFrameHostManagerTest,
+ CommitNewNavigationBeforeSendingSwapOut) {
+ const GURL kUrl1("http://www.google.com/");
+ const GURL kUrl2("http://www.chromium.org/");
+
+ // Navigate to the first page.
+ contents()->NavigateAndCommit(kUrl1);
+ TestRenderViewHost* rvh1 = test_rvh();
+ RenderViewHostDeletedObserver rvh_deleted_observer(rvh1);
+ EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
+
+ // Increment the number of active views in SiteInstanceImpl so that rvh1 is
+ // not deleted on swap out.
+ static_cast<SiteInstanceImpl*>(
+ rvh1->GetSiteInstance())->increment_active_view_count();
+
+ // Navigate to new site, simulating onbeforeunload approval.
+ controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
+ base::TimeTicks now = base::TimeTicks::Now();
+ main_test_rfh()->OnMessageReceived(
+ FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
+ EXPECT_TRUE(contents()->cross_navigation_pending());
+ TestRenderViewHost* rvh2 =
+ static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
+
+ // The new page commits.
+ contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED);
+ EXPECT_FALSE(contents()->cross_navigation_pending());
+ EXPECT_EQ(rvh2, rvh());
+ EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
+ EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state());
+ EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT, rvh1->rvh_state());
+
+ // Simulate the swap out ack.
+ rvh1->OnSwappedOut(false);
+
+ // rvh1 should be swapped out.
+ EXPECT_FALSE(rvh_deleted_observer.deleted());
+ EXPECT_TRUE(rvh1->IsSwappedOut());
+}
+
+// Test that a RenderFrameHost is properly deleted or swapped out when a
+// cross-site navigation is cancelled.
+TEST_F(RenderFrameHostManagerTest,
+ CancelPendingProperlyDeletesOrSwaps) {
+ const GURL kUrl1("http://www.google.com/");
+ const GURL kUrl2("http://www.chromium.org/");
+ RenderFrameHostImpl* pending_rfh = NULL;
+ base::TimeTicks now = base::TimeTicks::Now();
+
+ // Navigate to the first page.
+ contents()->NavigateAndCommit(kUrl1);
+ TestRenderViewHost* rvh1 = test_rvh();
+ EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
+
+ // Navigate to a new site, starting a cross-site navigation.
+ controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
+ {
+ pending_rfh = contents()->GetFrameTree()->root()->render_manager()
+ ->pending_frame_host();
+ RenderFrameHostDeletedObserver rvh_deleted_observer(pending_rfh);
+
+ // Cancel the navigation by simulating a declined beforeunload dialog.
+ main_test_rfh()->OnMessageReceived(
+ FrameHostMsg_BeforeUnload_ACK(0, false, now, now));
+ EXPECT_FALSE(contents()->cross_navigation_pending());
+
+ // Since the pending RFH is the only one for the new SiteInstance, it should
+ // be deleted.
+ EXPECT_TRUE(rvh_deleted_observer.deleted());
+ }
+
+ // Start another cross-site navigation.
+ controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
+ {
+ pending_rfh = contents()->GetFrameTree()->root()->render_manager()
+ ->pending_frame_host();
+ RenderFrameHostDeletedObserver rvh_deleted_observer(pending_rfh);
+
+ // Increment the number of active views in the new SiteInstance, which will
+ // cause the pending RFH to be swapped out instead of deleted.
+ static_cast<SiteInstanceImpl*>(
+ pending_rfh->GetSiteInstance())->increment_active_view_count();
+
+ main_test_rfh()->OnMessageReceived(
+ FrameHostMsg_BeforeUnload_ACK(0, false, now, now));
+ EXPECT_FALSE(contents()->cross_navigation_pending());
+ EXPECT_FALSE(rvh_deleted_observer.deleted());
+ }
}
} // namespace content
diff --git a/chromium/content/browser/frame_host/render_frame_message_filter.cc b/chromium/content/browser/frame_host/render_frame_message_filter.cc
index c1876806fae..8e414307b40 100644
--- a/chromium/content/browser/frame_host/render_frame_message_filter.cc
+++ b/chromium/content/browser/frame_host/render_frame_message_filter.cc
@@ -14,21 +14,16 @@ namespace content {
namespace {
void CreateChildFrameOnUI(int process_id,
- int parent_render_frame_id,
- int64 parent_frame_id,
- int64 frame_id,
+ int parent_routing_id,
const std::string& frame_name,
- int new_render_frame_id) {
+ int new_routing_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
RenderFrameHostImpl* render_frame_host =
- RenderFrameHostImpl::FromID(process_id, parent_render_frame_id);
+ RenderFrameHostImpl::FromID(process_id, parent_routing_id);
// Handles the RenderFrameHost being deleted on the UI thread while
// processing a subframe creation message.
- if (render_frame_host) {
- render_frame_host->OnCreateChildFrame(new_render_frame_id,
- parent_frame_id, frame_id,
- frame_name);
- }
+ if (render_frame_host)
+ render_frame_host->OnCreateChildFrame(new_routing_id, frame_name);
}
} // namespace
@@ -36,36 +31,33 @@ void CreateChildFrameOnUI(int process_id,
RenderFrameMessageFilter::RenderFrameMessageFilter(
int render_process_id,
RenderWidgetHelper* render_widget_helper)
- : render_process_id_(render_process_id),
+ : BrowserMessageFilter(FrameMsgStart),
+ render_process_id_(render_process_id),
render_widget_helper_(render_widget_helper) {
}
RenderFrameMessageFilter::~RenderFrameMessageFilter() {
}
-bool RenderFrameMessageFilter::OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) {
+bool RenderFrameMessageFilter::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(RenderFrameMessageFilter, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(RenderFrameMessageFilter, message)
IPC_MESSAGE_HANDLER(FrameHostMsg_CreateChildFrame, OnCreateChildFrame)
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return handled;
}
void RenderFrameMessageFilter::OnCreateChildFrame(
- int parent_render_frame_id,
- int64 parent_frame_id,
- int64 frame_id,
+ int parent_routing_id,
const std::string& frame_name,
- int* new_render_frame_id) {
- *new_render_frame_id = render_widget_helper_->GetNextRoutingID();
+ int* new_routing_id) {
+ *new_routing_id = render_widget_helper_->GetNextRoutingID();
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&CreateChildFrameOnUI, render_process_id_,
- parent_render_frame_id, parent_frame_id, frame_id, frame_name,
- *new_render_frame_id));
+ parent_routing_id, frame_name, *new_routing_id));
}
} // namespace content
diff --git a/chromium/content/browser/frame_host/render_frame_message_filter.h b/chromium/content/browser/frame_host/render_frame_message_filter.h
index 82a1b9420c8..25f7fbd57ad 100644
--- a/chromium/content/browser/frame_host/render_frame_message_filter.h
+++ b/chromium/content/browser/frame_host/render_frame_message_filter.h
@@ -21,15 +21,12 @@ class RenderFrameMessageFilter : public BrowserMessageFilter {
RenderFrameMessageFilter(int render_process_id,
RenderWidgetHelper* render_widget_helper);
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
private:
virtual ~RenderFrameMessageFilter();
- void OnCreateChildFrame(int parent_render_frame_id,
- int64 parent_frame_id,
- int64 frame_id,
+ void OnCreateChildFrame(int parent_routing_id,
const std::string& frame_name,
int* new_render_frame_id);
diff --git a/chromium/content/browser/frame_host/render_frame_proxy_host.cc b/chromium/content/browser/frame_host/render_frame_proxy_host.cc
new file mode 100644
index 00000000000..2c84798fa2c
--- /dev/null
+++ b/chromium/content/browser/frame_host/render_frame_proxy_host.cc
@@ -0,0 +1,85 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/frame_host/render_frame_proxy_host.h"
+
+#include "content/browser/frame_host/cross_process_frame_connector.h"
+#include "content/browser/frame_host/frame_tree_node.h"
+#include "content/browser/frame_host/render_frame_host_impl.h"
+#include "content/browser/frame_host/render_widget_host_view_child_frame.h"
+#include "content/browser/renderer_host/render_widget_host_view_base.h"
+#include "content/browser/site_instance_impl.h"
+#include "content/common/frame_messages.h"
+#include "ipc/ipc_message.h"
+
+namespace content {
+
+RenderFrameProxyHost::RenderFrameProxyHost(SiteInstance* site_instance,
+ FrameTreeNode* frame_tree_node)
+ : routing_id_(site_instance->GetProcess()->GetNextRoutingID()),
+ site_instance_(site_instance),
+ frame_tree_node_(frame_tree_node) {
+ GetProcess()->AddRoute(routing_id_, this);
+
+ if (!frame_tree_node_->IsMainFrame() &&
+ frame_tree_node_->parent()
+ ->render_manager()
+ ->current_frame_host()
+ ->GetSiteInstance() == site_instance) {
+ // The RenderFrameHost navigating cross-process is destroyed and a proxy for
+ // it is created in the parent's process. CrossProcessFrameConnector
+ // initialization only needs to happen on an initial cross-process
+ // navigation, when the RenderFrameHost leaves the same process as its
+ // parent. The same CrossProcessFrameConnector is used for subsequent cross-
+ // process navigations, but it will be destroyed if the frame is
+ // navigated back to the same SiteInstance as its parent.
+ cross_process_frame_connector_.reset(new CrossProcessFrameConnector(this));
+ }
+}
+
+RenderFrameProxyHost::~RenderFrameProxyHost() {
+ if (GetProcess()->HasConnection())
+ Send(new FrameMsg_DeleteProxy(routing_id_));
+
+ GetProcess()->RemoveRoute(routing_id_);
+}
+
+void RenderFrameProxyHost::SetChildRWHView(RenderWidgetHostView* view) {
+ cross_process_frame_connector_->set_view(
+ static_cast<RenderWidgetHostViewChildFrame*>(view));
+}
+
+RenderViewHostImpl* RenderFrameProxyHost::GetRenderViewHost() {
+ if (render_frame_host_.get())
+ return render_frame_host_->render_view_host();
+ return NULL;
+}
+
+scoped_ptr<RenderFrameHostImpl> RenderFrameProxyHost::PassFrameHostOwnership() {
+ render_frame_host_->set_render_frame_proxy_host(NULL);
+ return render_frame_host_.Pass();
+}
+
+bool RenderFrameProxyHost::Send(IPC::Message *msg) {
+ // TODO(nasko): For now, RenderFrameHost uses this object to send IPC messages
+ // while swapped out. This can be removed once we don't have a swapped out
+ // state on RenderFrameHosts. See https://crbug.com/357747.
+ msg->set_routing_id(routing_id_);
+ return GetProcess()->Send(msg);
+}
+
+bool RenderFrameProxyHost::OnMessageReceived(const IPC::Message& msg) {
+ if (cross_process_frame_connector_.get() &&
+ cross_process_frame_connector_->OnMessageReceived(msg))
+ return true;
+
+ // TODO(nasko): This can be removed once we don't have a swapped out state on
+ // RenderFrameHosts. See https://crbug.com/357747.
+ if (render_frame_host_.get())
+ return render_frame_host_->OnMessageReceived(msg);
+
+ return false;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/frame_host/render_frame_proxy_host.h b/chromium/content/browser/frame_host/render_frame_proxy_host.h
new file mode 100644
index 00000000000..218bcc411df
--- /dev/null
+++ b/chromium/content/browser/frame_host/render_frame_proxy_host.h
@@ -0,0 +1,124 @@
+// 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_FRAME_HOST_RENDER_FRAME_PROXY_HOST_H_
+#define CONTENT_BROWSER_FRAME_HOST_RENDER_FRAME_PROXY_HOST_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "content/browser/frame_host/render_frame_host_impl.h"
+#include "content/browser/site_instance_impl.h"
+#include "ipc/ipc_listener.h"
+#include "ipc/ipc_sender.h"
+
+namespace content {
+
+class CrossProcessFrameConnector;
+class FrameTreeNode;
+class RenderProcessHost;
+class RenderFrameHostImpl;
+class RenderViewHostImpl;
+class RenderWidgetHostView;
+
+// When a page's frames are rendered by multiple processes, each renderer has a
+// full copy of the frame tree. It has full RenderFrames for the frames it is
+// responsible for rendering and placeholder objects (i.e., RenderFrameProxies)
+// for frames rendered by other processes.
+//
+// This class is the browser-side host object for the placeholder. Each node in
+// the frame tree has a RenderFrameHost for the active SiteInstance and a set
+// of RenderFrameProxyHost objects - one for all other SiteInstances with
+// references to this frame. The proxies allow us to keep existing window
+// references valid over cross-process navigations and route cross-site
+// asynchronous JavaScript calls, such as postMessage.
+//
+// For now, RenderFrameProxyHost is created when a RenderFrameHost is swapped
+// out and acts just as a wrapper. It is destroyed when the RenderFrameHost is
+// swapped back in or is no longer referenced and is therefore deleted.
+//
+// Long term, RenderFrameProxyHost will be created whenever a cross-site
+// navigation occurs and a reference to the frame navigating needs to be kept
+// alive. A RenderFrameProxyHost and a RenderFrameHost for the same SiteInstance
+// can exist at the same time, but only one will be "active" at a time.
+// There are two cases where the two objects will coexist:
+// * When navigating cross-process and there is already a RenderFrameProxyHost
+// for the new SiteInstance. A pending RenderFrameHost is created, but it is
+// not used until it commits. At that point, RenderFrameHostManager transitions
+// the pending RenderFrameHost to the active one and deletes the proxy.
+// * When navigating cross-process and the existing document has an unload
+// event handler. When the new navigation commits, RenderFrameHostManager
+// creates a RenderFrameProxyHost for the old SiteInstance and uses it going
+// forward. It also instructs the RenderFrameHost to run the unload event
+// handler and is kept alive for the duration. Once the event handling is
+// complete, the RenderFrameHost is deleted.
+class RenderFrameProxyHost
+ : public IPC::Listener,
+ public IPC::Sender {
+ public:
+ RenderFrameProxyHost(SiteInstance* site_instance,
+ FrameTreeNode* frame_tree_node);
+ virtual ~RenderFrameProxyHost();
+
+ RenderProcessHost* GetProcess() {
+ return site_instance_->GetProcess();
+ }
+
+ int GetRoutingID() {
+ return routing_id_;
+ }
+
+ SiteInstance* GetSiteInstance() {
+ return site_instance_.get();
+ }
+
+ void SetChildRWHView(RenderWidgetHostView* view);
+
+ // TODO(nasko): The following methods should be removed once we don't have a
+ // swapped out state on RenderFrameHosts. See https://crbug.com/357747.
+ RenderFrameHostImpl* render_frame_host() {
+ return render_frame_host_.get();
+ }
+ RenderViewHostImpl* GetRenderViewHost();
+
+ void TakeFrameHostOwnership(
+ scoped_ptr<RenderFrameHostImpl> render_frame_host) {
+ render_frame_host_ = render_frame_host.Pass();
+ }
+ scoped_ptr<RenderFrameHostImpl> PassFrameHostOwnership();
+
+ // IPC::Sender
+ virtual bool Send(IPC::Message* msg) OVERRIDE;
+
+ // IPC::Listener
+ virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE;
+
+ CrossProcessFrameConnector* cross_process_frame_connector() {
+ return cross_process_frame_connector_.get();
+ }
+
+ private:
+ // This RenderFrameProxyHost's routing id.
+ int routing_id_;
+
+ // The SiteInstance this proxy is associated with.
+ scoped_refptr<SiteInstance> site_instance_;
+
+ // The node in the frame tree where this proxy is located.
+ FrameTreeNode* frame_tree_node_;
+
+ // When a RenderFrameHost is in a different process from its parent in the
+ // frame tree, this class connects its associated RenderWidgetHostView
+ // to this RenderFrameProxyHost, which corresponds to the same frame in the
+ // parent's renderer process.
+ scoped_ptr<CrossProcessFrameConnector> cross_process_frame_connector_;
+
+ // TODO(nasko): This can be removed once we don't have a swapped out state on
+ // RenderFrameHosts. See https://crbug.com/357747.
+ scoped_ptr<RenderFrameHostImpl> render_frame_host_;
+
+ DISALLOW_COPY_AND_ASSIGN(RenderFrameProxyHost);
+};
+
+} // namespace
+
+#endif // CONTENT_BROWSER_FRAME_HOST_RENDER_FRAME_PROXY_HOST_H_
diff --git a/chromium/content/browser/frame_host/render_widget_host_view_child_frame.cc b/chromium/content/browser/frame_host/render_widget_host_view_child_frame.cc
new file mode 100644
index 00000000000..d9e32c92c8b
--- /dev/null
+++ b/chromium/content/browser/frame_host/render_widget_host_view_child_frame.cc
@@ -0,0 +1,339 @@
+// 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/frame_host/render_widget_host_view_child_frame.h"
+
+#include "content/browser/frame_host/cross_process_frame_connector.h"
+#include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "content/common/gpu/gpu_messages.h"
+#include "content/common/view_messages.h"
+#include "content/public/browser/render_process_host.h"
+
+namespace content {
+
+RenderWidgetHostViewChildFrame::RenderWidgetHostViewChildFrame(
+ RenderWidgetHost* widget_host)
+ : host_(RenderWidgetHostImpl::From(widget_host)),
+ frame_connector_(NULL) {
+ host_->SetView(this);
+}
+
+RenderWidgetHostViewChildFrame::~RenderWidgetHostViewChildFrame() {
+}
+
+void RenderWidgetHostViewChildFrame::InitAsChild(
+ gfx::NativeView parent_view) {
+ NOTREACHED();
+}
+
+RenderWidgetHost* RenderWidgetHostViewChildFrame::GetRenderWidgetHost() const {
+ return host_;
+}
+
+void RenderWidgetHostViewChildFrame::SetSize(const gfx::Size& size) {
+ host_->WasResized();
+}
+
+void RenderWidgetHostViewChildFrame::SetBounds(const gfx::Rect& rect) {
+ SetSize(rect.size());
+}
+
+void RenderWidgetHostViewChildFrame::Focus() {
+}
+
+bool RenderWidgetHostViewChildFrame::HasFocus() const {
+ return false;
+}
+
+bool RenderWidgetHostViewChildFrame::IsSurfaceAvailableForCopy() const {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+void RenderWidgetHostViewChildFrame::Show() {
+ WasShown();
+}
+
+void RenderWidgetHostViewChildFrame::Hide() {
+ WasHidden();
+}
+
+bool RenderWidgetHostViewChildFrame::IsShowing() {
+ return !host_->is_hidden();
+}
+
+gfx::Rect RenderWidgetHostViewChildFrame::GetViewBounds() const {
+ gfx::Rect rect;
+ if (frame_connector_)
+ rect = frame_connector_->ChildFrameRect();
+ return rect;
+}
+
+gfx::NativeView RenderWidgetHostViewChildFrame::GetNativeView() const {
+ NOTREACHED();
+ return NULL;
+}
+
+gfx::NativeViewId RenderWidgetHostViewChildFrame::GetNativeViewId() const {
+ NOTREACHED();
+ return 0;
+}
+
+gfx::NativeViewAccessible
+RenderWidgetHostViewChildFrame::GetNativeViewAccessible() {
+ NOTREACHED();
+ return NULL;
+}
+
+void RenderWidgetHostViewChildFrame::SetBackgroundOpaque(bool opaque) {
+}
+
+gfx::Size RenderWidgetHostViewChildFrame::GetPhysicalBackingSize() const {
+ gfx::Size size;
+ if (frame_connector_)
+ size = frame_connector_->ChildFrameRect().size();
+ return size;
+}
+
+void RenderWidgetHostViewChildFrame::InitAsPopup(
+ RenderWidgetHostView* parent_host_view,
+ const gfx::Rect& pos) {
+ NOTREACHED();
+}
+
+void RenderWidgetHostViewChildFrame::InitAsFullscreen(
+ RenderWidgetHostView* reference_host_view) {
+ NOTREACHED();
+}
+
+void RenderWidgetHostViewChildFrame::ImeCancelComposition() {
+ NOTREACHED();
+}
+
+#if defined(OS_MACOSX) || defined(USE_AURA)
+void RenderWidgetHostViewChildFrame::ImeCompositionRangeChanged(
+ const gfx::Range& range,
+ const std::vector<gfx::Rect>& character_bounds) {
+ NOTREACHED();
+}
+#endif
+
+void RenderWidgetHostViewChildFrame::WasShown() {
+ if (!host_->is_hidden())
+ return;
+ host_->WasShown();
+}
+
+void RenderWidgetHostViewChildFrame::WasHidden() {
+ if (host_->is_hidden())
+ return;
+ host_->WasHidden();
+}
+
+void RenderWidgetHostViewChildFrame::MovePluginWindows(
+ const std::vector<WebPluginGeometry>& moves) {
+}
+
+void RenderWidgetHostViewChildFrame::Blur() {
+}
+
+void RenderWidgetHostViewChildFrame::UpdateCursor(const WebCursor& cursor) {
+}
+
+void RenderWidgetHostViewChildFrame::SetIsLoading(bool is_loading) {
+ NOTREACHED();
+}
+
+void RenderWidgetHostViewChildFrame::TextInputStateChanged(
+ const ViewHostMsg_TextInputState_Params& params) {
+}
+
+void RenderWidgetHostViewChildFrame::RenderProcessGone(
+ base::TerminationStatus status,
+ int error_code) {
+ if (frame_connector_)
+ frame_connector_->RenderProcessGone();
+ Destroy();
+}
+
+void RenderWidgetHostViewChildFrame::Destroy() {
+ if (frame_connector_) {
+ frame_connector_->set_view(NULL);
+ frame_connector_ = NULL;
+ }
+
+ host_->SetView(NULL);
+ host_ = NULL;
+ base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
+}
+
+void RenderWidgetHostViewChildFrame::SetTooltipText(
+ const base::string16& tooltip_text) {
+}
+
+void RenderWidgetHostViewChildFrame::SelectionChanged(
+ const base::string16& text,
+ size_t offset,
+ const gfx::Range& range) {
+}
+
+void RenderWidgetHostViewChildFrame::SelectionBoundsChanged(
+ const ViewHostMsg_SelectionBounds_Params& params) {
+}
+
+#if defined(OS_ANDROID)
+void RenderWidgetHostViewChildFrame::ShowDisambiguationPopup(
+ const gfx::Rect& target_rect,
+ const SkBitmap& zoomed_bitmap) {
+}
+
+void RenderWidgetHostViewChildFrame::LockCompositingSurface() {
+}
+
+void RenderWidgetHostViewChildFrame::UnlockCompositingSurface() {
+}
+#endif
+
+void RenderWidgetHostViewChildFrame::ScrollOffsetChanged() {
+}
+
+void RenderWidgetHostViewChildFrame::AcceleratedSurfaceInitialized(int host_id,
+ int route_id) {
+}
+
+void RenderWidgetHostViewChildFrame::AcceleratedSurfaceBuffersSwapped(
+ const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params,
+ int gpu_host_id) {
+ if (frame_connector_)
+ frame_connector_->ChildFrameBuffersSwapped(params, gpu_host_id);
+}
+
+void RenderWidgetHostViewChildFrame::AcceleratedSurfacePostSubBuffer(
+ const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params,
+ int gpu_host_id) {
+}
+
+void RenderWidgetHostViewChildFrame::OnSwapCompositorFrame(
+ uint32 output_surface_id,
+ scoped_ptr<cc::CompositorFrame> frame) {
+ if (frame_connector_) {
+ frame_connector_->ChildFrameCompositorFrameSwapped(
+ output_surface_id,
+ host_->GetProcess()->GetID(),
+ host_->GetRoutingID(),
+ frame.Pass());
+ }
+}
+
+void RenderWidgetHostViewChildFrame::GetScreenInfo(
+ blink::WebScreenInfo* results) {
+}
+
+gfx::Rect RenderWidgetHostViewChildFrame::GetBoundsInRootWindow() {
+ // We do not have any root window specific parts in this view.
+ return GetViewBounds();
+}
+
+#if defined(USE_AURA)
+void RenderWidgetHostViewChildFrame::ProcessAckedTouchEvent(
+ const TouchEventWithLatencyInfo& touch,
+ InputEventAckState ack_result) {
+}
+#endif // defined(USE_AURA)
+
+bool RenderWidgetHostViewChildFrame::LockMouse() {
+ return false;
+}
+
+void RenderWidgetHostViewChildFrame::UnlockMouse() {
+}
+
+#if defined(OS_MACOSX)
+void RenderWidgetHostViewChildFrame::SetActive(bool active) {
+}
+
+void RenderWidgetHostViewChildFrame::SetTakesFocusOnlyOnMouseDown(bool flag) {
+}
+
+void RenderWidgetHostViewChildFrame::SetWindowVisibility(bool visible) {
+}
+
+void RenderWidgetHostViewChildFrame::WindowFrameChanged() {
+}
+
+void RenderWidgetHostViewChildFrame::ShowDefinitionForSelection() {
+}
+
+bool RenderWidgetHostViewChildFrame::SupportsSpeech() const {
+ return false;
+}
+
+void RenderWidgetHostViewChildFrame::SpeakSelection() {
+}
+
+bool RenderWidgetHostViewChildFrame::IsSpeaking() const {
+ return false;
+}
+
+void RenderWidgetHostViewChildFrame::StopSpeaking() {
+}
+
+bool RenderWidgetHostViewChildFrame::PostProcessEventForPluginIme(
+ const NativeWebKeyboardEvent& event) {
+ return false;
+}
+#endif // defined(OS_MACOSX)
+
+void RenderWidgetHostViewChildFrame::CopyFromCompositingSurface(
+ const gfx::Rect& src_subrect,
+ const gfx::Size& /* dst_size */,
+ const base::Callback<void(bool, const SkBitmap&)>& callback,
+ const SkBitmap::Config config) {
+ callback.Run(false, SkBitmap());
+}
+
+void RenderWidgetHostViewChildFrame::CopyFromCompositingSurfaceToVideoFrame(
+ const gfx::Rect& src_subrect,
+ const scoped_refptr<media::VideoFrame>& target,
+ const base::Callback<void(bool)>& callback) {
+ NOTIMPLEMENTED();
+ callback.Run(false);
+}
+
+bool RenderWidgetHostViewChildFrame::CanCopyToVideoFrame() const {
+ return false;
+}
+
+void RenderWidgetHostViewChildFrame::AcceleratedSurfaceSuspend() {
+ NOTREACHED();
+}
+
+void RenderWidgetHostViewChildFrame::AcceleratedSurfaceRelease() {
+}
+
+bool RenderWidgetHostViewChildFrame::HasAcceleratedSurface(
+ const gfx::Size& desired_size) {
+ return false;
+}
+
+gfx::GLSurfaceHandle RenderWidgetHostViewChildFrame::GetCompositingSurface() {
+ return gfx::GLSurfaceHandle(gfx::kNullPluginWindow, gfx::TEXTURE_TRANSPORT);
+}
+
+#if defined(OS_WIN)
+void RenderWidgetHostViewChildFrame::SetParentNativeViewAccessible(
+ gfx::NativeViewAccessible accessible_parent) {
+}
+
+gfx::NativeViewId RenderWidgetHostViewChildFrame::GetParentForWindowlessPlugin()
+ const {
+ return NULL;
+}
+#endif // defined(OS_WIN)
+
+SkBitmap::Config RenderWidgetHostViewChildFrame::PreferredReadbackFormat() {
+ return SkBitmap::kARGB_8888_Config;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/frame_host/render_widget_host_view_child_frame.h b/chromium/content/browser/frame_host/render_widget_host_view_child_frame.h
new file mode 100644
index 00000000000..e288248aa45
--- /dev/null
+++ b/chromium/content/browser/frame_host/render_widget_host_view_child_frame.h
@@ -0,0 +1,174 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_FRAME_HOST_RENDER_WIDGET_HOST_VIEW_CHILD_FRAME_H_
+#define CONTENT_BROWSER_FRAME_HOST_RENDER_WIDGET_HOST_VIEW_CHILD_FRAME_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "content/browser/renderer_host/render_widget_host_view_base.h"
+#include "content/common/content_export.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/rect.h"
+
+struct ViewHostMsg_TextInputState_Params;
+
+namespace content {
+class CrossProcessFrameConnector;
+class RenderWidgetHost;
+class RenderWidgetHostImpl;
+
+// RenderWidgetHostViewChildFrame implements the view for a RenderWidgetHost
+// associated with content being rendered in a separate process from
+// content that is embedding it. This is not a platform-specific class; rather,
+// the embedding renderer process implements the platform containing the
+// widget, and the top-level frame's RenderWidgetHostView will ultimately
+// manage all native widget interaction.
+//
+// See comments in render_widget_host_view.h about this class and its members.
+class CONTENT_EXPORT RenderWidgetHostViewChildFrame
+ : public RenderWidgetHostViewBase {
+ public:
+ explicit RenderWidgetHostViewChildFrame(RenderWidgetHost* widget);
+ virtual ~RenderWidgetHostViewChildFrame();
+
+ void set_cross_process_frame_connector(
+ CrossProcessFrameConnector* frame_connector) {
+ frame_connector_ = frame_connector;
+ }
+
+ // RenderWidgetHostView implementation.
+ virtual void InitAsChild(gfx::NativeView parent_view) OVERRIDE;
+ virtual RenderWidgetHost* GetRenderWidgetHost() const OVERRIDE;
+ virtual void SetSize(const gfx::Size& size) OVERRIDE;
+ virtual void SetBounds(const gfx::Rect& rect) OVERRIDE;
+ virtual void Focus() OVERRIDE;
+ virtual bool HasFocus() const OVERRIDE;
+ virtual bool IsSurfaceAvailableForCopy() const OVERRIDE;
+ virtual void Show() OVERRIDE;
+ virtual void Hide() OVERRIDE;
+ virtual bool IsShowing() OVERRIDE;
+ virtual gfx::Rect GetViewBounds() const OVERRIDE;
+ virtual gfx::NativeView GetNativeView() const OVERRIDE;
+ virtual gfx::NativeViewId GetNativeViewId() const OVERRIDE;
+ virtual gfx::NativeViewAccessible GetNativeViewAccessible() OVERRIDE;
+ virtual void SetBackgroundOpaque(bool opaque) OVERRIDE;
+ virtual gfx::Size GetPhysicalBackingSize() const OVERRIDE;
+
+ // RenderWidgetHostViewBase implementation.
+ virtual void InitAsPopup(RenderWidgetHostView* parent_host_view,
+ const gfx::Rect& pos) OVERRIDE;
+ virtual void InitAsFullscreen(
+ RenderWidgetHostView* reference_host_view) OVERRIDE;
+ virtual void WasShown() OVERRIDE;
+ virtual void WasHidden() OVERRIDE;
+ virtual void MovePluginWindows(
+ const std::vector<WebPluginGeometry>& moves) OVERRIDE;
+ virtual void Blur() OVERRIDE;
+ virtual void UpdateCursor(const WebCursor& cursor) OVERRIDE;
+ virtual void SetIsLoading(bool is_loading) OVERRIDE;
+ virtual void TextInputStateChanged(
+ const ViewHostMsg_TextInputState_Params& params) OVERRIDE;
+ virtual void ImeCancelComposition() OVERRIDE;
+#if defined(OS_MACOSX) || defined(USE_AURA)
+ virtual void ImeCompositionRangeChanged(
+ const gfx::Range& range,
+ const std::vector<gfx::Rect>& character_bounds) OVERRIDE;
+#endif
+ virtual void RenderProcessGone(base::TerminationStatus status,
+ int error_code) OVERRIDE;
+ virtual void Destroy() OVERRIDE;
+ virtual void SetTooltipText(const base::string16& tooltip_text) OVERRIDE;
+ virtual void SelectionChanged(const base::string16& text,
+ size_t offset,
+ const gfx::Range& range) OVERRIDE;
+ virtual void SelectionBoundsChanged(
+ const ViewHostMsg_SelectionBounds_Params& params) OVERRIDE;
+ virtual void ScrollOffsetChanged() OVERRIDE;
+ virtual void CopyFromCompositingSurface(
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ const base::Callback<void(bool, const SkBitmap&)>& callback,
+ const SkBitmap::Config config) OVERRIDE;
+ virtual void CopyFromCompositingSurfaceToVideoFrame(
+ const gfx::Rect& src_subrect,
+ const scoped_refptr<media::VideoFrame>& target,
+ const base::Callback<void(bool)>& callback) OVERRIDE;
+ virtual bool CanCopyToVideoFrame() const OVERRIDE;
+ virtual void AcceleratedSurfaceInitialized(int host_id,
+ int route_id) OVERRIDE;
+ virtual void AcceleratedSurfaceBuffersSwapped(
+ const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params,
+ int gpu_host_id) OVERRIDE;
+ virtual void AcceleratedSurfacePostSubBuffer(
+ const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params,
+ int gpu_host_id) OVERRIDE;
+ virtual void AcceleratedSurfaceSuspend() OVERRIDE;
+ virtual void AcceleratedSurfaceRelease() OVERRIDE;
+ virtual bool HasAcceleratedSurface(const gfx::Size& desired_size) OVERRIDE;
+ virtual void OnSwapCompositorFrame(
+ uint32 output_surface_id,
+ scoped_ptr<cc::CompositorFrame> frame) OVERRIDE;
+ virtual void GetScreenInfo(blink::WebScreenInfo* results) OVERRIDE;
+ virtual gfx::Rect GetBoundsInRootWindow() OVERRIDE;
+ virtual gfx::GLSurfaceHandle GetCompositingSurface() OVERRIDE;
+#if defined(USE_AURA)
+ virtual void ProcessAckedTouchEvent(
+ const TouchEventWithLatencyInfo& touch,
+ InputEventAckState ack_result) OVERRIDE;
+#endif // defined(USE_AURA)
+ virtual bool LockMouse() OVERRIDE;
+ virtual void UnlockMouse() OVERRIDE;
+
+#if defined(OS_MACOSX)
+ // RenderWidgetHostView implementation.
+ virtual void SetActive(bool active) OVERRIDE;
+ virtual void SetTakesFocusOnlyOnMouseDown(bool flag) OVERRIDE;
+ virtual void SetWindowVisibility(bool visible) OVERRIDE;
+ virtual void WindowFrameChanged() OVERRIDE;
+ virtual void ShowDefinitionForSelection() OVERRIDE;
+ virtual bool SupportsSpeech() const OVERRIDE;
+ virtual void SpeakSelection() OVERRIDE;
+ virtual bool IsSpeaking() const OVERRIDE;
+ virtual void StopSpeaking() OVERRIDE;
+
+ // RenderWidgetHostViewBase implementation.
+ virtual bool PostProcessEventForPluginIme(
+ const NativeWebKeyboardEvent& event) OVERRIDE;
+#endif // defined(OS_MACOSX)
+
+#if defined(OS_ANDROID)
+ // RenderWidgetHostViewBase implementation.
+ virtual void ShowDisambiguationPopup(
+ const gfx::Rect& target_rect,
+ const SkBitmap& zoomed_bitmap) OVERRIDE;
+ virtual void LockCompositingSurface() OVERRIDE;
+ virtual void UnlockCompositingSurface() OVERRIDE;
+#endif // defined(OS_ANDROID)
+
+#if defined(OS_WIN)
+ virtual void SetParentNativeViewAccessible(
+ gfx::NativeViewAccessible accessible_parent) OVERRIDE;
+ virtual gfx::NativeViewId GetParentForWindowlessPlugin() const OVERRIDE;
+#endif
+
+ virtual SkBitmap::Config PreferredReadbackFormat() OVERRIDE;
+
+ protected:
+ friend class RenderWidgetHostView;
+
+ // Members will become private when RenderWidgetHostViewGuest is removed.
+ // The model object.
+ RenderWidgetHostImpl* host_;
+
+ // frame_connector_ provides a platform abstraction. Messages
+ // sent through it are routed to the embedding renderer process.
+ CrossProcessFrameConnector* frame_connector_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewChildFrame);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_FRAME_HOST_RENDER_WIDGET_HOST_VIEW_CHILD_FRAME_H_
diff --git a/chromium/content/browser/frame_host/render_widget_host_view_child_frame_unittest.cc b/chromium/content/browser/frame_host/render_widget_host_view_child_frame_unittest.cc
new file mode 100644
index 00000000000..95b4eb36391
--- /dev/null
+++ b/chromium/content/browser/frame_host/render_widget_host_view_child_frame_unittest.cc
@@ -0,0 +1,80 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/frame_host/render_widget_host_view_child_frame.h"
+
+#include "base/basictypes.h"
+#include "base/message_loop/message_loop.h"
+#include "content/browser/renderer_host/render_widget_host_delegate.h"
+#include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "content/common/view_messages.h"
+#include "content/public/browser/render_widget_host_view.h"
+#include "content/public/test/mock_render_process_host.h"
+#include "content/public/test/test_browser_context.h"
+#include "content/test/test_render_view_host.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+namespace {
+class MockRenderWidgetHostDelegate : public RenderWidgetHostDelegate {
+ public:
+ MockRenderWidgetHostDelegate() {}
+ virtual ~MockRenderWidgetHostDelegate() {}
+};
+
+class RenderWidgetHostViewChildFrameTest : public testing::Test {
+ public:
+ RenderWidgetHostViewChildFrameTest() {}
+
+ virtual void SetUp() {
+ browser_context_.reset(new TestBrowserContext);
+ MockRenderProcessHost* process_host =
+ new MockRenderProcessHost(browser_context_.get());
+ widget_host_ = new RenderWidgetHostImpl(
+ &delegate_, process_host, MSG_ROUTING_NONE, false);
+ view_ = new RenderWidgetHostViewChildFrame(widget_host_);
+ }
+
+ virtual void TearDown() {
+ if (view_)
+ view_->Destroy();
+ delete widget_host_;
+
+ browser_context_.reset();
+
+ message_loop_.DeleteSoon(FROM_HERE, browser_context_.release());
+ message_loop_.RunUntilIdle();
+ }
+
+ protected:
+ base::MessageLoopForUI message_loop_;
+ scoped_ptr<BrowserContext> browser_context_;
+ MockRenderWidgetHostDelegate delegate_;
+
+ // Tests should set these to NULL if they've already triggered their
+ // destruction.
+ RenderWidgetHostImpl* widget_host_;
+ RenderWidgetHostViewChildFrame* view_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewChildFrameTest);
+};
+
+} // namespace
+
+TEST_F(RenderWidgetHostViewChildFrameTest, VisibilityTest) {
+ view_->Show();
+ ASSERT_TRUE(view_->IsShowing());
+
+ view_->Hide();
+ ASSERT_FALSE(view_->IsShowing());
+
+ view_->WasShown();
+ ASSERT_TRUE(view_->IsShowing());
+
+ view_->WasHidden();
+ ASSERT_FALSE(view_->IsShowing());
+}
+
+} // namespace content
diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_guest.cc b/chromium/content/browser/frame_host/render_widget_host_view_guest.cc
index bbe5c8b4b86..8a02c69f5ea 100644
--- a/chromium/content/browser/renderer_host/render_widget_host_view_guest.cc
+++ b/chromium/content/browser/frame_host/render_widget_host_view_guest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// 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.
@@ -7,10 +7,13 @@
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "content/browser/browser_plugin/browser_plugin_guest.h"
+#include "content/browser/frame_host/render_widget_host_view_guest.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
-#include "content/browser/renderer_host/render_widget_host_view_guest.h"
#include "content/common/browser_plugin/browser_plugin_messages.h"
+#include "content/common/frame_messages.h"
#include "content/common/gpu/gpu_messages.h"
+#include "content/common/host_shared_bitmap_manager.h"
+#include "content/common/input/web_touch_event_traits.h"
#include "content/common/view_messages.h"
#include "content/common/webplugin_geometry.h"
#include "content/public/common/content_switches.h"
@@ -21,7 +24,7 @@
#import "content/browser/renderer_host/render_widget_host_view_mac_dictionary_helper.h"
#endif
-#if defined(OS_WIN) || defined(USE_AURA)
+#if defined(USE_AURA)
#include "content/browser/renderer_host/ui_events_helper.h"
#endif
@@ -29,46 +32,36 @@ namespace content {
namespace {
-#if defined(OS_WIN) || defined(USE_AURA)
-bool ShouldSendPinchGesture() {
- static bool pinch_allowed =
- CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnablePinch);
- return pinch_allowed;
-}
-
+#if defined(USE_AURA)
blink::WebGestureEvent CreateFlingCancelEvent(double time_stamp) {
blink::WebGestureEvent gesture_event;
gesture_event.timeStampSeconds = time_stamp;
gesture_event.type = blink::WebGestureEvent::GestureFlingCancel;
- gesture_event.sourceDevice = blink::WebGestureEvent::Touchscreen;
+ gesture_event.sourceDevice = blink::WebGestureDeviceTouchscreen;
return gesture_event;
}
-#endif // defined(OS_WIN) || defined(USE_AURA)
+#endif // defined(USE_AURA)
} // namespace
RenderWidgetHostViewGuest::RenderWidgetHostViewGuest(
RenderWidgetHost* widget_host,
BrowserPluginGuest* guest,
- RenderWidgetHostView* platform_view)
- : host_(RenderWidgetHostImpl::From(widget_host)),
- guest_(guest),
- platform_view_(static_cast<RenderWidgetHostViewPort*>(platform_view)) {
-#if defined(OS_WIN) || defined(USE_AURA)
+ RenderWidgetHostViewBase* platform_view)
+ : RenderWidgetHostViewChildFrame(widget_host),
+ // |guest| is NULL during test.
+ guest_(guest ? guest->AsWeakPtr() : base::WeakPtr<BrowserPluginGuest>()),
+ platform_view_(platform_view) {
+#if defined(USE_AURA)
gesture_recognizer_.reset(ui::GestureRecognizer::Create());
gesture_recognizer_->AddGestureEventHelper(this);
-#endif // defined(OS_WIN) || defined(USE_AURA)
- host_->SetView(this);
+#endif // defined(USE_AURA)
}
RenderWidgetHostViewGuest::~RenderWidgetHostViewGuest() {
-#if defined(OS_WIN) || defined(USE_AURA)
+#if defined(USE_AURA)
gesture_recognizer_->RemoveGestureEventHelper(this);
-#endif // defined(OS_WIN) || defined(USE_AURA)
-}
-
-RenderWidgetHost* RenderWidgetHostViewGuest::GetRenderWidgetHost() const {
- return host_;
+#endif // defined(USE_AURA)
}
void RenderWidgetHostViewGuest::WasShown() {
@@ -96,16 +89,11 @@ void RenderWidgetHostViewGuest::SetSize(const gfx::Size& size) {
host_->WasResized();
}
-gfx::Rect RenderWidgetHostViewGuest::GetBoundsInRootWindow() {
- // We do not have any root window specific parts in this view.
- return GetViewBounds();
-}
-
-gfx::GLSurfaceHandle RenderWidgetHostViewGuest::GetCompositingSurface() {
- return gfx::GLSurfaceHandle(gfx::kNullPluginWindow, gfx::TEXTURE_TRANSPORT);
+void RenderWidgetHostViewGuest::SetBounds(const gfx::Rect& rect) {
+ SetSize(rect.size());
}
-#if defined(OS_WIN) || defined(USE_AURA)
+#if defined(USE_AURA)
void RenderWidgetHostViewGuest::ProcessAckedTouchEvent(
const TouchEventWithLatencyInfo& touch, InputEventAckState ack_result) {
// TODO(fsamuel): Currently we will only take this codepath if the guest has
@@ -129,21 +117,11 @@ void RenderWidgetHostViewGuest::ProcessAckedTouchEvent(
}
#endif
-void RenderWidgetHostViewGuest::Show() {
- WasShown();
-}
-
-void RenderWidgetHostViewGuest::Hide() {
- WasHidden();
-}
-
-bool RenderWidgetHostViewGuest::IsShowing() {
- return !host_->is_hidden();
-}
-
gfx::Rect RenderWidgetHostViewGuest::GetViewBounds() const {
- RenderWidgetHostViewPort* rwhv = static_cast<RenderWidgetHostViewPort*>(
- guest_->GetEmbedderRenderWidgetHostView());
+ if (!guest_)
+ return gfx::Rect();
+
+ RenderWidgetHostViewBase* rwhv = GetGuestRenderWidgetHostView();
gfx::Rect embedder_bounds;
if (rwhv)
embedder_bounds = rwhv->GetViewBounds();
@@ -169,28 +147,33 @@ void RenderWidgetHostViewGuest::Destroy() {
platform_view_->Destroy();
}
+gfx::Size RenderWidgetHostViewGuest::GetPhysicalBackingSize() const {
+ return RenderWidgetHostViewBase::GetPhysicalBackingSize();
+}
+
+base::string16 RenderWidgetHostViewGuest::GetSelectedText() const {
+ return platform_view_->GetSelectedText();
+}
+
void RenderWidgetHostViewGuest::SetTooltipText(
const base::string16& tooltip_text) {
platform_view_->SetTooltipText(tooltip_text);
}
-void RenderWidgetHostViewGuest::AcceleratedSurfaceInitialized(int host_id,
- int route_id) {
-}
-
void RenderWidgetHostViewGuest::AcceleratedSurfaceBuffersSwapped(
const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params,
int gpu_host_id) {
- // If accelerated surface buffers are getting swapped then we're not using
- // the software path.
- guest_->clear_damage_buffer();
- BrowserPluginMsg_BuffersSwapped_Params guest_params;
+ if (!guest_)
+ return;
+
+ FrameMsg_BuffersSwapped_Params guest_params;
guest_params.size = params.size;
- guest_params.mailbox_name = params.mailbox_name;
- guest_params.route_id = params.route_id;
- guest_params.host_id = gpu_host_id;
+ guest_params.mailbox = params.mailbox;
+ guest_params.gpu_route_id = params.route_id;
+ guest_params.gpu_host_id = gpu_host_id;
guest_->SendMessageToEmbedder(
- new BrowserPluginMsg_BuffersSwapped(guest_->instance_id(), guest_params));
+ new BrowserPluginMsg_BuffersSwapped(guest_->instance_id(),
+ guest_params));
}
void RenderWidgetHostViewGuest::AcceleratedSurfacePostSubBuffer(
@@ -202,35 +185,42 @@ void RenderWidgetHostViewGuest::AcceleratedSurfacePostSubBuffer(
void RenderWidgetHostViewGuest::OnSwapCompositorFrame(
uint32 output_surface_id,
scoped_ptr<cc::CompositorFrame> frame) {
+ if (!guest_)
+ return;
+
+ if (!guest_->attached()) {
+ // If the guest doesn't have an embedder then there's nothing to give the
+ // the frame to.
+ return;
+ }
+ base::SharedMemoryHandle software_frame_handle =
+ base::SharedMemory::NULLHandle();
if (frame->software_frame_data) {
cc::SoftwareFrameData* frame_data = frame->software_frame_data.get();
-#ifdef OS_WIN
- base::SharedMemory shared_memory(frame_data->handle, true,
- host_->GetProcess()->GetHandle());
-#else
- base::SharedMemory shared_memory(frame_data->handle, true);
-#endif
+ scoped_ptr<cc::SharedBitmap> bitmap =
+ HostSharedBitmapManager::current()->GetSharedBitmapFromId(
+ frame_data->size, frame_data->bitmap_id);
+ if (!bitmap)
+ return;
- RenderWidgetHostView* embedder_view =
+ RenderWidgetHostView* embedder_rwhv =
guest_->GetEmbedderRenderWidgetHostView();
base::ProcessHandle embedder_pid =
- embedder_view->GetRenderWidgetHost()->GetProcess()->GetHandle();
+ embedder_rwhv->GetRenderWidgetHost()->GetProcess()->GetHandle();
- shared_memory.GiveToProcess(embedder_pid, &frame_data->handle);
+ bitmap->memory()->ShareToProcess(embedder_pid, &software_frame_handle);
}
- guest_->clear_damage_buffer();
- guest_->SendMessageToEmbedder(
- new BrowserPluginMsg_CompositorFrameSwapped(
- guest_->instance_id(),
- *frame,
- host_->GetRoutingID(),
- output_surface_id,
- host_->GetProcess()->GetID()));
-}
+ FrameMsg_CompositorFrameSwapped_Params guest_params;
+ frame->AssignTo(&guest_params.frame);
+ guest_params.output_surface_id = output_surface_id;
+ guest_params.producing_route_id = host_->GetRoutingID();
+ guest_params.producing_host_id = host_->GetProcess()->GetID();
+ guest_params.shared_memory_handle = software_frame_handle;
-void RenderWidgetHostViewGuest::SetBounds(const gfx::Rect& rect) {
- SetSize(rect.size());
+ guest_->SendMessageToEmbedder(
+ new BrowserPluginMsg_CompositorFrameSwapped(guest_->instance_id(),
+ guest_params));
}
bool RenderWidgetHostViewGuest::OnMessageReceived(const IPC::Message& msg) {
@@ -255,38 +245,38 @@ void RenderWidgetHostViewGuest::InitAsFullscreen(
}
gfx::NativeView RenderWidgetHostViewGuest::GetNativeView() const {
- return guest_->GetEmbedderRenderWidgetHostView()->GetNativeView();
+ if (!guest_)
+ return gfx::NativeView();
+
+ RenderWidgetHostView* rwhv = guest_->GetEmbedderRenderWidgetHostView();
+ if (!rwhv)
+ return gfx::NativeView();
+ return rwhv->GetNativeView();
}
gfx::NativeViewId RenderWidgetHostViewGuest::GetNativeViewId() const {
- if (guest_->GetEmbedderRenderWidgetHostView())
- return guest_->GetEmbedderRenderWidgetHostView()->GetNativeViewId();
- return static_cast<gfx::NativeViewId>(NULL);
+ if (!guest_)
+ return static_cast<gfx::NativeViewId>(NULL);
+
+ RenderWidgetHostView* rwhv = guest_->GetEmbedderRenderWidgetHostView();
+ if (!rwhv)
+ return static_cast<gfx::NativeViewId>(NULL);
+ return rwhv->GetNativeViewId();
}
gfx::NativeViewAccessible RenderWidgetHostViewGuest::GetNativeViewAccessible() {
- return guest_->GetEmbedderRenderWidgetHostView()->GetNativeViewAccessible();
+ if (!guest_)
+ return gfx::NativeViewAccessible();
+
+ RenderWidgetHostView* rwhv = guest_->GetEmbedderRenderWidgetHostView();
+ if (!rwhv)
+ return gfx::NativeViewAccessible();
+ return rwhv->GetNativeViewAccessible();
}
void RenderWidgetHostViewGuest::MovePluginWindows(
- const gfx::Vector2d& scroll_offset,
const std::vector<WebPluginGeometry>& moves) {
- platform_view_->MovePluginWindows(scroll_offset, moves);
-}
-
-void RenderWidgetHostViewGuest::Focus() {
-}
-
-void RenderWidgetHostViewGuest::Blur() {
-}
-
-bool RenderWidgetHostViewGuest::HasFocus() const {
- return false;
-}
-
-bool RenderWidgetHostViewGuest::IsSurfaceAvailableForCopy() const {
- NOTIMPLEMENTED();
- return false;
+ platform_view_->MovePluginWindows(moves);
}
void RenderWidgetHostViewGuest::UpdateCursor(const WebCursor& cursor) {
@@ -297,33 +287,37 @@ void RenderWidgetHostViewGuest::SetIsLoading(bool is_loading) {
platform_view_->SetIsLoading(is_loading);
}
-void RenderWidgetHostViewGuest::TextInputTypeChanged(
- ui::TextInputType type,
- ui::TextInputMode input_mode,
- bool can_compose_inline) {
- RenderWidgetHostViewPort* rwhv = RenderWidgetHostViewPort::FromRWHV(
- guest_->GetEmbedderRenderWidgetHostView());
+void RenderWidgetHostViewGuest::TextInputStateChanged(
+ const ViewHostMsg_TextInputState_Params& params) {
+ if (!guest_)
+ return;
+
+ RenderWidgetHostViewBase* rwhv = GetGuestRenderWidgetHostView();
if (!rwhv)
return;
// Forward the information to embedding RWHV.
- rwhv->TextInputTypeChanged(type, input_mode, can_compose_inline);
+ rwhv->TextInputStateChanged(params);
}
void RenderWidgetHostViewGuest::ImeCancelComposition() {
- RenderWidgetHostViewPort* rwhv = RenderWidgetHostViewPort::FromRWHV(
- guest_->GetEmbedderRenderWidgetHostView());
+ if (!guest_)
+ return;
+
+ RenderWidgetHostViewBase* rwhv = GetGuestRenderWidgetHostView();
if (!rwhv)
return;
// Forward the information to embedding RWHV.
rwhv->ImeCancelComposition();
}
-#if defined(OS_MACOSX) || defined(OS_WIN) || defined(USE_AURA)
+#if defined(OS_MACOSX) || defined(USE_AURA)
void RenderWidgetHostViewGuest::ImeCompositionRangeChanged(
const gfx::Range& range,
const std::vector<gfx::Rect>& character_bounds) {
- RenderWidgetHostViewPort* rwhv = RenderWidgetHostViewPort::FromRWHV(
- guest_->GetEmbedderRenderWidgetHostView());
+ if (!guest_)
+ return;
+
+ RenderWidgetHostViewBase* rwhv = GetGuestRenderWidgetHostView();
if (!rwhv)
return;
std::vector<gfx::Rect> guest_character_bounds;
@@ -336,29 +330,18 @@ void RenderWidgetHostViewGuest::ImeCompositionRangeChanged(
}
#endif
-void RenderWidgetHostViewGuest::DidUpdateBackingStore(
- const gfx::Rect& scroll_rect,
- const gfx::Vector2d& scroll_delta,
- const std::vector<gfx::Rect>& copy_rects,
- const ui::LatencyInfo& latency_info) {
- NOTREACHED();
-}
-
void RenderWidgetHostViewGuest::SelectionChanged(const base::string16& text,
size_t offset,
const gfx::Range& range) {
- RenderWidgetHostViewPort* rwhv = RenderWidgetHostViewPort::FromRWHV(
- guest_->GetEmbedderRenderWidgetHostView());
- if (!rwhv)
- return;
- // Forward the information to embedding RWHV.
- rwhv->SelectionChanged(text, offset, range);
+ platform_view_->SelectionChanged(text, offset, range);
}
void RenderWidgetHostViewGuest::SelectionBoundsChanged(
const ViewHostMsg_SelectionBounds_Params& params) {
- RenderWidgetHostViewPort* rwhv = RenderWidgetHostViewPort::FromRWHV(
- guest_->GetEmbedderRenderWidgetHostView());
+ if (!guest_)
+ return;
+
+ RenderWidgetHostViewBase* rwhv = GetGuestRenderWidgetHostView();
if (!rwhv)
return;
ViewHostMsg_SelectionBounds_Params guest_params(params);
@@ -367,68 +350,17 @@ void RenderWidgetHostViewGuest::SelectionBoundsChanged(
rwhv->SelectionBoundsChanged(guest_params);
}
-void RenderWidgetHostViewGuest::ScrollOffsetChanged() {
-}
-
-BackingStore* RenderWidgetHostViewGuest::AllocBackingStore(
- const gfx::Size& size) {
- NOTREACHED();
- return NULL;
-}
-
void RenderWidgetHostViewGuest::CopyFromCompositingSurface(
const gfx::Rect& src_subrect,
const gfx::Size& dst_size,
- const base::Callback<void(bool, const SkBitmap&)>& callback) {
+ const base::Callback<void(bool, const SkBitmap&)>& callback,
+ const SkBitmap::Config config) {
CHECK(guest_);
guest_->CopyFromCompositingSurface(src_subrect, dst_size, callback);
}
-void RenderWidgetHostViewGuest::CopyFromCompositingSurfaceToVideoFrame(
- const gfx::Rect& src_subrect,
- const scoped_refptr<media::VideoFrame>& target,
- const base::Callback<void(bool)>& callback) {
- NOTIMPLEMENTED();
- callback.Run(false);
-}
-
-bool RenderWidgetHostViewGuest::CanCopyToVideoFrame() const {
- return false;
-}
-
-void RenderWidgetHostViewGuest::AcceleratedSurfaceSuspend() {
- NOTREACHED();
-}
-
-void RenderWidgetHostViewGuest::AcceleratedSurfaceRelease() {
-}
-
-bool RenderWidgetHostViewGuest::HasAcceleratedSurface(
- const gfx::Size& desired_size) {
- return false;
-}
-
-void RenderWidgetHostViewGuest::SetBackground(const SkBitmap& background) {
- platform_view_->SetBackground(background);
-}
-
-#if defined(OS_WIN) && !defined(USE_AURA)
-void RenderWidgetHostViewGuest::SetClickthroughRegion(SkRegion* region) {
-}
-#endif
-
-void RenderWidgetHostViewGuest::SetHasHorizontalScrollbar(
- bool has_horizontal_scrollbar) {
- platform_view_->SetHasHorizontalScrollbar(has_horizontal_scrollbar);
-}
-
-void RenderWidgetHostViewGuest::SetScrollOffsetPinning(
- bool is_pinned_to_left, bool is_pinned_to_right) {
- platform_view_->SetScrollOffsetPinning(
- is_pinned_to_left, is_pinned_to_right);
-}
-
-void RenderWidgetHostViewGuest::OnAcceleratedCompositingStateChange() {
+void RenderWidgetHostViewGuest::SetBackgroundOpaque(bool opaque) {
+ platform_view_->SetBackgroundOpaque(opaque);
}
bool RenderWidgetHostViewGuest::LockMouse() {
@@ -440,17 +372,13 @@ void RenderWidgetHostViewGuest::UnlockMouse() {
}
void RenderWidgetHostViewGuest::GetScreenInfo(blink::WebScreenInfo* results) {
- RenderWidgetHostViewPort* embedder_view =
- RenderWidgetHostViewPort::FromRWHV(
- guest_->GetEmbedderRenderWidgetHostView());
+ if (!guest_)
+ return;
+ RenderWidgetHostViewBase* embedder_view = GetGuestRenderWidgetHostView();
if (embedder_view)
embedder_view->GetScreenInfo(results);
}
-void RenderWidgetHostViewGuest::OnAccessibilityEvents(
- const std::vector<AccessibilityHostMsg_EventParams>& params) {
-}
-
#if defined(OS_MACOSX)
void RenderWidgetHostViewGuest::SetActive(bool active) {
platform_view_->SetActive(active);
@@ -469,10 +397,15 @@ void RenderWidgetHostViewGuest::WindowFrameChanged() {
}
void RenderWidgetHostViewGuest::ShowDefinitionForSelection() {
+ if (!guest_)
+ return;
+
gfx::Point origin;
gfx::Rect guest_bounds = GetViewBounds();
- gfx::Rect embedder_bounds =
- guest_->GetEmbedderRenderWidgetHostView()->GetViewBounds();
+ RenderWidgetHostView* rwhv = guest_->GetEmbedderRenderWidgetHostView();
+ gfx::Rect embedder_bounds;
+ if (rwhv)
+ embedder_bounds = rwhv->GetViewBounds();
gfx::Vector2d guest_offset = gfx::Vector2d(
// Horizontal offset of guest from embedder.
@@ -481,7 +414,7 @@ void RenderWidgetHostViewGuest::ShowDefinitionForSelection() {
embedder_bounds.bottom() - guest_bounds.y());
RenderWidgetHostViewMacDictionaryHelper helper(platform_view_);
- helper.SetTargetView(guest_->GetEmbedderRenderWidgetHostView());
+ helper.SetTargetView(rwhv);
helper.set_offset(guest_offset);
helper.ShowDefinitionForSelection();
}
@@ -502,10 +435,6 @@ void RenderWidgetHostViewGuest::StopSpeaking() {
platform_view_->StopSpeaking();
}
-void RenderWidgetHostViewGuest::AboutToWaitForBackingStoreMsg() {
- NOTREACHED();
-}
-
bool RenderWidgetHostViewGuest::PostProcessEventForPluginIme(
const NativeWebKeyboardEvent& event) {
return false;
@@ -519,26 +448,14 @@ void RenderWidgetHostViewGuest::ShowDisambiguationPopup(
const SkBitmap& zoomed_bitmap) {
}
-void RenderWidgetHostViewGuest::HasTouchEventHandlers(bool need_touch_events) {
-}
-#endif // defined(OS_ANDROID)
-
-#if defined(TOOLKIT_GTK)
-GdkEventButton* RenderWidgetHostViewGuest::GetLastMouseDown() {
- return NULL;
-}
-
-gfx::NativeView RenderWidgetHostViewGuest::BuildInputMethodsGtkMenu() {
- return platform_view_->BuildInputMethodsGtkMenu();
+void RenderWidgetHostViewGuest::LockCompositingSurface() {
}
-#endif // defined(TOOLKIT_GTK)
-#if defined(OS_WIN) && !defined(USE_AURA)
-void RenderWidgetHostViewGuest::WillWmDestroy() {
+void RenderWidgetHostViewGuest::UnlockCompositingSurface() {
}
-#endif
+#endif // defined(OS_ANDROID)
-#if defined(OS_WIN) && defined(USE_AURA)
+#if defined(OS_WIN)
void RenderWidgetHostViewGuest::SetParentNativeViewAccessible(
gfx::NativeViewAccessible accessible_parent) {
}
@@ -561,7 +478,7 @@ bool RenderWidgetHostViewGuest::CanDispatchToConsumer(
return true;
}
-void RenderWidgetHostViewGuest::DispatchPostponedGestureEvent(
+void RenderWidgetHostViewGuest::DispatchGestureEvent(
ui::GestureEvent* event) {
ForwardGestureEventToRenderer(event);
}
@@ -572,23 +489,24 @@ void RenderWidgetHostViewGuest::DispatchCancelTouchEvent(
return;
blink::WebTouchEvent cancel_event;
- cancel_event.type = blink::WebInputEvent::TouchCancel;
- cancel_event.timeStampSeconds = event->time_stamp().InSecondsF();
+ // TODO(rbyers): This event has no touches in it. Don't we need to know what
+ // touches are currently active in order to cancel them all properly?
+ WebTouchEventTraits::ResetType(blink::WebInputEvent::TouchCancel,
+ event->time_stamp().InSecondsF(),
+ &cancel_event);
+
host_->ForwardTouchEventWithLatencyInfo(cancel_event, *event->latency());
}
bool RenderWidgetHostViewGuest::ForwardGestureEventToRenderer(
ui::GestureEvent* gesture) {
-#if defined(OS_WIN) || defined(USE_AURA)
+#if defined(USE_AURA)
if (!host_)
return false;
- // Pinch gestures are disabled by default on windows desktop. See
- // crbug.com/128477 and crbug.com/148816
if ((gesture->type() == ui::ET_GESTURE_PINCH_BEGIN ||
gesture->type() == ui::ET_GESTURE_PINCH_UPDATE ||
- gesture->type() == ui::ET_GESTURE_PINCH_END) &&
- !ShouldSendPinchGesture()) {
+ gesture->type() == ui::ET_GESTURE_PINCH_END) && !pinch_zoom_enabled_) {
return true;
}
@@ -626,5 +544,14 @@ void RenderWidgetHostViewGuest::ProcessGestures(
}
}
+SkBitmap::Config RenderWidgetHostViewGuest::PreferredReadbackFormat() {
+ return SkBitmap::kARGB_8888_Config;
+}
+
+RenderWidgetHostViewBase*
+RenderWidgetHostViewGuest::GetGuestRenderWidgetHostView() const {
+ return static_cast<RenderWidgetHostViewBase*>(
+ guest_->GetEmbedderRenderWidgetHostView());
+}
} // namespace content
diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_guest.h b/chromium/content/browser/frame_host/render_widget_host_view_guest.h
index ac9fa0ee2cb..6132b4e8655 100644
--- a/chromium/content/browser/renderer_host/render_widget_host_view_guest.h
+++ b/chromium/content/browser/frame_host/render_widget_host_view_guest.h
@@ -1,26 +1,24 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// 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_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_GUEST_H_
-#define CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_GUEST_H_
+#ifndef CONTENT_BROWSER_FRAME_HOST_RENDER_WIDGET_HOST_VIEW_GUEST_H_
+#define CONTENT_BROWSER_FRAME_HOST_RENDER_WIDGET_HOST_VIEW_GUEST_H_
#include <vector>
#include "base/memory/scoped_ptr.h"
-#include "content/browser/renderer_host/render_widget_host_view_base.h"
+#include "content/browser/frame_host/render_widget_host_view_child_frame.h"
#include "content/common/content_export.h"
+#include "content/common/cursors/webcursor.h"
#include "ui/events/event.h"
#include "ui/events/gestures/gesture_recognizer.h"
#include "ui/events/gestures/gesture_types.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/vector2d_f.h"
-#include "webkit/common/cursors/webcursor.h"
-#if defined(TOOLKIT_GTK)
-#include "content/browser/renderer_host/gtk_plugin_container_manager.h"
-#endif // defined(TOOLKIT_GTK)
+struct ViewHostMsg_TextInputState_Params;
namespace content {
class RenderWidgetHost;
@@ -28,45 +26,40 @@ class RenderWidgetHostImpl;
class BrowserPluginGuest;
struct NativeWebKeyboardEvent;
-// -----------------------------------------------------------------------------
// See comments in render_widget_host_view.h about this class and its members.
-// This version is for the webview plugin which handles a lot of the
+// This version is for the BrowserPlugin which handles a lot of the
// functionality in a diffent place and isn't platform specific.
+// The BrowserPlugin is currently a special case for out-of-process rendered
+// content and therefore inherits from RenderWidgetHostViewChildFrame.
+// Eventually all RenderWidgetHostViewGuest code will be subsumed by
+// RenderWidgetHostViewChildFrame and this class will be removed.
//
// Some elements that are platform specific will be deal with by delegating
// the relevant calls to the platform view.
-// -----------------------------------------------------------------------------
class CONTENT_EXPORT RenderWidgetHostViewGuest
- : public RenderWidgetHostViewBase,
+ : public RenderWidgetHostViewChildFrame,
public ui::GestureConsumer,
public ui::GestureEventHelper {
public:
RenderWidgetHostViewGuest(RenderWidgetHost* widget,
BrowserPluginGuest* guest,
- RenderWidgetHostView* platform_view);
+ RenderWidgetHostViewBase* platform_view);
virtual ~RenderWidgetHostViewGuest();
// RenderWidgetHostView implementation.
virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE;
virtual void InitAsChild(gfx::NativeView parent_view) OVERRIDE;
- virtual RenderWidgetHost* GetRenderWidgetHost() const OVERRIDE;
virtual void SetSize(const gfx::Size& size) OVERRIDE;
virtual void SetBounds(const gfx::Rect& rect) OVERRIDE;
virtual gfx::NativeView GetNativeView() const OVERRIDE;
virtual gfx::NativeViewId GetNativeViewId() const OVERRIDE;
virtual gfx::NativeViewAccessible GetNativeViewAccessible() OVERRIDE;
- virtual bool HasFocus() const OVERRIDE;
- virtual bool IsSurfaceAvailableForCopy() const OVERRIDE;
- virtual void Show() OVERRIDE;
- virtual void Hide() OVERRIDE;
- virtual bool IsShowing() OVERRIDE;
virtual gfx::Rect GetViewBounds() const OVERRIDE;
- virtual void SetBackground(const SkBitmap& background) OVERRIDE;
-#if defined(OS_WIN) && !defined(USE_AURA)
- virtual void SetClickthroughRegion(SkRegion* region) OVERRIDE;
-#endif
+ virtual void SetBackgroundOpaque(bool opaque) OVERRIDE;
+ virtual gfx::Size GetPhysicalBackingSize() const OVERRIDE;
+ virtual base::string16 GetSelectedText() const OVERRIDE;
- // RenderWidgetHostViewPort implementation.
+ // RenderWidgetHostViewBase implementation.
virtual void InitAsPopup(RenderWidgetHostView* parent_host_view,
const gfx::Rect& pos) OVERRIDE;
virtual void InitAsFullscreen(
@@ -74,50 +67,31 @@ class CONTENT_EXPORT RenderWidgetHostViewGuest
virtual void WasShown() OVERRIDE;
virtual void WasHidden() OVERRIDE;
virtual void MovePluginWindows(
- const gfx::Vector2d& scroll_offset,
const std::vector<WebPluginGeometry>& moves) OVERRIDE;
- virtual void Focus() OVERRIDE;
- virtual void Blur() OVERRIDE;
virtual void UpdateCursor(const WebCursor& cursor) OVERRIDE;
virtual void SetIsLoading(bool is_loading) OVERRIDE;
- virtual void TextInputTypeChanged(ui::TextInputType type,
- ui::TextInputMode input_mode,
- bool can_compose_inline) OVERRIDE;
+ virtual void TextInputStateChanged(
+ const ViewHostMsg_TextInputState_Params& params) OVERRIDE;
virtual void ImeCancelComposition() OVERRIDE;
-#if defined(OS_MACOSX) || defined(OS_WIN) || defined(USE_AURA)
+#if defined(OS_MACOSX) || defined(USE_AURA)
virtual void ImeCompositionRangeChanged(
const gfx::Range& range,
const std::vector<gfx::Rect>& character_bounds) OVERRIDE;
#endif
- virtual void DidUpdateBackingStore(
- const gfx::Rect& scroll_rect,
- const gfx::Vector2d& scroll_delta,
- const std::vector<gfx::Rect>& copy_rects,
- const ui::LatencyInfo& latency_info) OVERRIDE;
virtual void RenderProcessGone(base::TerminationStatus status,
int error_code) OVERRIDE;
virtual void Destroy() OVERRIDE;
- virtual void WillDestroyRenderWidget(RenderWidgetHost* rwh) {}
virtual void SetTooltipText(const base::string16& tooltip_text) OVERRIDE;
virtual void SelectionChanged(const base::string16& text,
size_t offset,
const gfx::Range& range) OVERRIDE;
virtual void SelectionBoundsChanged(
const ViewHostMsg_SelectionBounds_Params& params) OVERRIDE;
- virtual void ScrollOffsetChanged() OVERRIDE;
- virtual BackingStore* AllocBackingStore(const gfx::Size& size) OVERRIDE;
virtual void CopyFromCompositingSurface(
const gfx::Rect& src_subrect,
const gfx::Size& dst_size,
- const base::Callback<void(bool, const SkBitmap&)>& callback) OVERRIDE;
- virtual void CopyFromCompositingSurfaceToVideoFrame(
- const gfx::Rect& src_subrect,
- const scoped_refptr<media::VideoFrame>& target,
- const base::Callback<void(bool)>& callback) OVERRIDE;
- virtual bool CanCopyToVideoFrame() const OVERRIDE;
- virtual void OnAcceleratedCompositingStateChange() OVERRIDE;
- virtual void AcceleratedSurfaceInitialized(int host_id,
- int route_id) OVERRIDE;
+ const base::Callback<void(bool, const SkBitmap&)>& callback,
+ const SkBitmap::Config config) OVERRIDE;
virtual void AcceleratedSurfaceBuffersSwapped(
const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params,
int gpu_host_id) OVERRIDE;
@@ -127,26 +101,14 @@ class CONTENT_EXPORT RenderWidgetHostViewGuest
virtual void OnSwapCompositorFrame(
uint32 output_surface_id,
scoped_ptr<cc::CompositorFrame> frame) OVERRIDE;
- virtual void AcceleratedSurfaceSuspend() OVERRIDE;
- virtual void AcceleratedSurfaceRelease() OVERRIDE;
- virtual bool HasAcceleratedSurface(const gfx::Size& desired_size) OVERRIDE;
- virtual void SetHasHorizontalScrollbar(
- bool has_horizontal_scrollbar) OVERRIDE;
- virtual void SetScrollOffsetPinning(
- bool is_pinned_to_left, bool is_pinned_to_right) OVERRIDE;
- virtual gfx::Rect GetBoundsInRootWindow() OVERRIDE;
- virtual gfx::GLSurfaceHandle GetCompositingSurface() OVERRIDE;
-#if defined(OS_WIN) || defined(USE_AURA)
+#if defined(USE_AURA)
virtual void ProcessAckedTouchEvent(
const TouchEventWithLatencyInfo& touch,
InputEventAckState ack_result) OVERRIDE;
-#endif // defined(OS_WIN) || defined(USE_AURA)
+#endif
virtual bool LockMouse() OVERRIDE;
virtual void UnlockMouse() OVERRIDE;
virtual void GetScreenInfo(blink::WebScreenInfo* results) OVERRIDE;
- virtual void OnAccessibilityEvents(
- const std::vector<AccessibilityHostMsg_EventParams>&
- params) OVERRIDE;
#if defined(OS_MACOSX)
// RenderWidgetHostView implementation.
@@ -160,29 +122,20 @@ class CONTENT_EXPORT RenderWidgetHostViewGuest
virtual bool IsSpeaking() const OVERRIDE;
virtual void StopSpeaking() OVERRIDE;
- // RenderWidgetHostViewPort implementation.
- virtual void AboutToWaitForBackingStoreMsg() OVERRIDE;
+ // RenderWidgetHostViewBase implementation.
virtual bool PostProcessEventForPluginIme(
const NativeWebKeyboardEvent& event) OVERRIDE;
#endif // defined(OS_MACOSX)
#if defined(OS_ANDROID)
- // RenderWidgetHostViewPort implementation.
+ // RenderWidgetHostViewBase implementation.
virtual void ShowDisambiguationPopup(const gfx::Rect& target_rect,
const SkBitmap& zoomed_bitmap) OVERRIDE;
- virtual void HasTouchEventHandlers(bool need_touch_events) OVERRIDE;
+ virtual void LockCompositingSurface() OVERRIDE;
+ virtual void UnlockCompositingSurface() OVERRIDE;
#endif // defined(OS_ANDROID)
-#if defined(TOOLKIT_GTK)
- virtual GdkEventButton* GetLastMouseDown() OVERRIDE;
- virtual gfx::NativeView BuildInputMethodsGtkMenu() OVERRIDE;
-#endif // defined(TOOLKIT_GTK)
-
-#if defined(OS_WIN) && !defined(USE_AURA)
- virtual void WillWmDestroy() OVERRIDE;
-#endif // defined(OS_WIN) && !defined(USE_AURA)
-
-#if defined(OS_WIN) && defined(USE_AURA)
+#if defined(OS_WIN)
virtual void SetParentNativeViewAccessible(
gfx::NativeViewAccessible accessible_parent) OVERRIDE;
virtual gfx::NativeViewId GetParentForWindowlessPlugin() const OVERRIDE;
@@ -190,9 +143,11 @@ class CONTENT_EXPORT RenderWidgetHostViewGuest
// Overridden from ui::GestureEventHelper.
virtual bool CanDispatchToConsumer(ui::GestureConsumer* consumer) OVERRIDE;
- virtual void DispatchPostponedGestureEvent(ui::GestureEvent* event) OVERRIDE;
+ virtual void DispatchGestureEvent(ui::GestureEvent* event) OVERRIDE;
virtual void DispatchCancelTouchEvent(ui::TouchEvent* event) OVERRIDE;
+ virtual SkBitmap::Config PreferredReadbackFormat() OVERRIDE;
+
protected:
friend class RenderWidgetHostView;
@@ -206,21 +161,22 @@ class CONTENT_EXPORT RenderWidgetHostViewGuest
// Process all of the given gestures (passes them on to renderer)
void ProcessGestures(ui::GestureRecognizer::Gestures* gestures);
- // The model object.
- RenderWidgetHostImpl* host_;
+ RenderWidgetHostViewBase* GetGuestRenderWidgetHostView() const;
- BrowserPluginGuest *guest_;
+ // BrowserPluginGuest and RenderWidgetHostViewGuest's lifetimes are not tied
+ // to one another, therefore we access |guest_| through WeakPtr.
+ base::WeakPtr<BrowserPluginGuest> guest_;
gfx::Size size_;
// The platform view for this RenderWidgetHostView.
// RenderWidgetHostViewGuest mostly only cares about stuff related to
// compositing, the rest are directly forwared to this |platform_view_|.
- RenderWidgetHostViewPort* platform_view_;
-#if defined(OS_WIN) || defined(USE_AURA)
+ RenderWidgetHostViewBase* platform_view_;
+#if defined(USE_AURA)
scoped_ptr<ui::GestureRecognizer> gesture_recognizer_;
-#endif // defined(OS_WIN) || defined(USE_AURA)
+#endif
DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewGuest);
};
} // namespace content
-#endif // CHROME_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_GUEST_H_
+#endif // CONTENT_BROWSER_FRAME_HOST_RENDER_WIDGET_HOST_VIEW_GUEST_H_
diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_guest_unittest.cc b/chromium/content/browser/frame_host/render_widget_host_view_guest_unittest.cc
index b99743aa5ff..172be978f61 100644
--- a/chromium/content/browser/renderer_host/render_widget_host_view_guest_unittest.cc
+++ b/chromium/content/browser/frame_host/render_widget_host_view_guest_unittest.cc
@@ -1,8 +1,8 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// 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/renderer_host/render_widget_host_view_guest.h"
+#include "content/browser/frame_host/render_widget_host_view_guest.h"
#include "base/basictypes.h"
#include "base/message_loop/message_loop.h"
diff --git a/chromium/content/browser/gamepad/OWNERS b/chromium/content/browser/gamepad/OWNERS
index 3f809e82b19..16c21dcbeeb 100644
--- a/chromium/content/browser/gamepad/OWNERS
+++ b/chromium/content/browser/gamepad/OWNERS
@@ -1 +1,2 @@
scottmg@chromium.org
+bajones@chromium.org
diff --git a/chromium/content/browser/gamepad/canonical_axis_index_list.h b/chromium/content/browser/gamepad/canonical_axis_index_list.h
new file mode 100644
index 00000000000..58b85a6e189
--- /dev/null
+++ b/chromium/content/browser/gamepad/canonical_axis_index_list.h
@@ -0,0 +1,16 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file intentionally does not have header guards, it's included
+// inside a macro to generate enum values.
+
+// This file defines the canonical axes mapping order for gamepad-like devices.
+
+// TODO(SaurabhK): Consolidate with CanonicalAxisIndex enum in
+// gamepad_standard_mappings.h, crbug.com/351558.
+CANONICAL_AXIS_INDEX(AXIS_LEFT_STICK_X, 0)
+CANONICAL_AXIS_INDEX(AXIS_LEFT_STICK_Y, 1)
+CANONICAL_AXIS_INDEX(AXIS_RIGHT_STICK_X, 2)
+CANONICAL_AXIS_INDEX(AXIS_RIGHT_STICK_Y, 3)
+CANONICAL_AXIS_INDEX(NUM_CANONICAL_AXES, 4)
diff --git a/chromium/content/browser/gamepad/canonical_button_index_list.h b/chromium/content/browser/gamepad/canonical_button_index_list.h
new file mode 100644
index 00000000000..153ce0ea3dd
--- /dev/null
+++ b/chromium/content/browser/gamepad/canonical_button_index_list.h
@@ -0,0 +1,28 @@
+// 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.
+
+// This file intentionally does not have header guards, it's included
+// inside a macro to generate enum values.
+// This defines the canonical button mapping order for gamepad-like devices.
+
+// TODO(SaurabhK): Consolidate with CanonicalButtonIndex enum in
+// gamepad_standard_mappings.h, crbug.com/351558.
+CANONICAL_BUTTON_INDEX(BUTTON_PRIMARY, 0)
+CANONICAL_BUTTON_INDEX(BUTTON_SECONDARY, 1)
+CANONICAL_BUTTON_INDEX(BUTTON_TERTIARY, 2)
+CANONICAL_BUTTON_INDEX(BUTTON_QUATERNARY, 3)
+CANONICAL_BUTTON_INDEX(BUTTON_LEFT_SHOULDER, 4)
+CANONICAL_BUTTON_INDEX(BUTTON_RIGHT_SHOULDER, 5)
+CANONICAL_BUTTON_INDEX(BUTTON_LEFT_TRIGGER, 6)
+CANONICAL_BUTTON_INDEX(BUTTON_RIGHT_TRIGGER, 7)
+CANONICAL_BUTTON_INDEX(BUTTON_BACK_SELECT, 8)
+CANONICAL_BUTTON_INDEX(BUTTON_START, 9)
+CANONICAL_BUTTON_INDEX(BUTTON_LEFT_THUMBSTICK, 10)
+CANONICAL_BUTTON_INDEX(BUTTON_RIGHT_THUMBSTICK, 11)
+CANONICAL_BUTTON_INDEX(BUTTON_DPAD_UP, 12)
+CANONICAL_BUTTON_INDEX(BUTTON_DPAD_DOWN, 13)
+CANONICAL_BUTTON_INDEX(BUTTON_DPAD_LEFT, 14)
+CANONICAL_BUTTON_INDEX(BUTTON_DPAD_RIGHT, 15)
+CANONICAL_BUTTON_INDEX(BUTTON_META, 16)
+CANONICAL_BUTTON_INDEX(NUM_CANONICAL_BUTTONS, 17) \ No newline at end of file
diff --git a/chromium/content/browser/gamepad/gamepad_consumer.h b/chromium/content/browser/gamepad/gamepad_consumer.h
new file mode 100644
index 00000000000..3d07452fd1c
--- /dev/null
+++ b/chromium/content/browser/gamepad/gamepad_consumer.h
@@ -0,0 +1,29 @@
+// 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_GAMEPAD_GAMEPAD_CONSUMER_H
+#define CONTENT_BROWSER_GAMEPAD_GAMEPAD_CONSUMER_H
+
+#include "base/basictypes.h"
+#include "content/common/content_export.h"
+#include "third_party/WebKit/public/platform/WebGamepad.h"
+
+namespace content {
+
+class CONTENT_EXPORT GamepadConsumer {
+ public:
+ virtual void OnGamepadConnected(
+ unsigned index,
+ const blink::WebGamepad& gamepad) = 0;
+ virtual void OnGamepadDisconnected(
+ unsigned index,
+ const blink::WebGamepad& gamepad) = 0;
+
+ protected:
+ virtual ~GamepadConsumer() {}
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_GAMEPAD_GAMEPAD_CONSUMER_H
diff --git a/chromium/content/browser/gamepad/gamepad_platform_data_fetcher.h b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher.h
index 247306295e7..f3cf7cc7055 100644
--- a/chromium/content/browser/gamepad/gamepad_platform_data_fetcher.h
+++ b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher.h
@@ -12,7 +12,9 @@
#include "base/compiler_specific.h"
#include "content/browser/gamepad/gamepad_data_fetcher.h"
-#if defined(OS_WIN)
+#if defined(OS_ANDROID)
+#include "content/browser/gamepad/gamepad_platform_data_fetcher_android.h"
+#elif defined(OS_WIN)
#include "content/browser/gamepad/gamepad_platform_data_fetcher_win.h"
#elif defined(OS_MACOSX)
#include "content/browser/gamepad/gamepad_platform_data_fetcher_mac.h"
@@ -22,7 +24,11 @@
namespace content {
-#if defined(OS_WIN)
+#if defined(OS_ANDROID)
+
+typedef GamepadPlatformDataFetcherAndroid GamepadPlatformDataFetcher;
+
+#elif defined(OS_WIN)
typedef GamepadPlatformDataFetcherWin GamepadPlatformDataFetcher;
diff --git a/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_android.cc b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_android.cc
new file mode 100644
index 00000000000..a8673fa3252
--- /dev/null
+++ b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_android.cc
@@ -0,0 +1,149 @@
+// 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/gamepad/gamepad_platform_data_fetcher_android.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_array.h"
+#include "base/android/jni_string.h"
+#include "base/debug/trace_event.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+
+#include "jni/GamepadList_jni.h"
+
+#include "third_party/WebKit/public/platform/WebGamepads.h"
+
+using base::android::AttachCurrentThread;
+using base::android::CheckException;
+using base::android::ClearException;
+using base::android::ConvertJavaStringToUTF8;
+using base::android::ScopedJavaLocalRef;
+using blink::WebGamepad;
+using blink::WebGamepads;
+
+namespace content {
+
+bool
+GamepadPlatformDataFetcherAndroid::RegisterGamepadPlatformDataFetcherAndroid(
+ JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+GamepadPlatformDataFetcherAndroid::GamepadPlatformDataFetcherAndroid() {
+ PauseHint(false);
+}
+
+GamepadPlatformDataFetcherAndroid::~GamepadPlatformDataFetcherAndroid() {
+ PauseHint(true);
+}
+
+void GamepadPlatformDataFetcherAndroid::GetGamepadData(
+ blink::WebGamepads* pads,
+ bool devices_changed_hint) {
+ TRACE_EVENT0("GAMEPAD", "GetGamepadData");
+
+ pads->length = 0;
+
+ JNIEnv* env = AttachCurrentThread();
+ if (!env)
+ return;
+
+ Java_GamepadList_updateGamepadData(env, reinterpret_cast<intptr_t>(pads));
+}
+
+void GamepadPlatformDataFetcherAndroid::PauseHint(bool paused) {
+ JNIEnv* env = AttachCurrentThread();
+ if (!env)
+ return;
+
+ Java_GamepadList_notifyForGamepadsAccess(env, paused);
+}
+
+static void SetGamepadData(JNIEnv* env,
+ jobject obj,
+ jlong gamepads,
+ jint index,
+ jboolean mapping,
+ jboolean connected,
+ jstring devicename,
+ jlong timestamp,
+ jfloatArray jaxes,
+ jfloatArray jbuttons) {
+ DCHECK(gamepads);
+ blink::WebGamepads* pads = reinterpret_cast<WebGamepads*>(gamepads);
+ DCHECK_EQ(pads->length, unsigned(index));
+ DCHECK_LT(index, static_cast<int>(blink::WebGamepads::itemsLengthCap));
+
+ ++pads->length;
+
+ blink::WebGamepad& pad = pads->items[index];
+
+ pad.connected = connected;
+
+ pad.timestamp = timestamp;
+
+ // Do not set gamepad parameters for all the gamepad devices that are not
+ // attached.
+ if (!connected)
+ return;
+
+ // Map the Gamepad DeviceName String to the WebGamepad Id. Ideally it should
+ // be mapped to vendor and product information but it is only available at
+ // kernel level and it can not be queried using class
+ // android.hardware.input.InputManager.
+ // TODO(SaurabhK): Store a cached WebGamePad object in
+ // GamepadPlatformDataFetcherAndroid and only update constant WebGamepad
+ // values when a device has changed.
+ base::string16 device_name;
+ base::android::ConvertJavaStringToUTF16(env, devicename, &device_name);
+ const size_t name_to_copy =
+ std::min(device_name.size(), WebGamepad::idLengthCap - 1);
+ memcpy(pad.id,
+ device_name.data(),
+ name_to_copy * sizeof(base::string16::value_type));
+ pad.id[name_to_copy] = 0;
+
+ base::string16 mapping_name = base::UTF8ToUTF16(mapping ? "standard" : "");
+ const size_t mapping_to_copy =
+ std::min(mapping_name.size(), WebGamepad::mappingLengthCap - 1);
+ memcpy(pad.mapping,
+ mapping_name.data(),
+ mapping_to_copy * sizeof(base::string16::value_type));
+ pad.mapping[mapping_to_copy] = 0;
+
+ pad.timestamp = timestamp;
+
+ std::vector<float> axes;
+ base::android::JavaFloatArrayToFloatVector(env, jaxes, &axes);
+
+ // Set WebGamepad axeslength to total number of axes on the gamepad device.
+ // Only return the first axesLengthCap if axeslength captured by GamepadList
+ // is larger than axesLengthCap.
+ pad.axesLength = std::min(static_cast<int>(axes.size()),
+ static_cast<int>(WebGamepad::axesLengthCap));
+
+ // Copy axes state to the WebGamepad axes[].
+ for (unsigned int i = 0; i < pad.axesLength; i++) {
+ pad.axes[i] = static_cast<double>(axes[i]);
+ }
+
+ std::vector<float> buttons;
+ base::android::JavaFloatArrayToFloatVector(env, jbuttons, &buttons);
+
+ // Set WebGamepad buttonslength to total number of axes on the gamepad
+ // device. Only return the first buttonsLengthCap if axeslength captured by
+ // GamepadList is larger than buttonsLengthCap.
+ pad.buttonsLength = std::min(static_cast<int>(buttons.size()),
+ static_cast<int>(WebGamepad::buttonsLengthCap));
+
+ // Copy buttons state to the WebGamepad buttons[].
+ for (unsigned int j = 0; j < pad.buttonsLength; j++) {
+ pad.buttons[j].pressed = buttons[j];
+ pad.buttons[j].value = buttons[j];
+ }
+}
+
+} // namespace content
diff --git a/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_android.h b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_android.h
new file mode 100644
index 00000000000..bbbdc1f2c05
--- /dev/null
+++ b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_android.h
@@ -0,0 +1,40 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Define the data fetcher that GamepadProvider will use for android port.
+// (GamepadPlatformDataFetcher).
+
+#ifndef CONTENT_BROWSER_GAMEPAD_GAMEPAD_PLATFORM_DATA_FETCHER_ANDROID_H_
+#define CONTENT_BROWSER_GAMEPAD_GAMEPAD_PLATFORM_DATA_FETCHER_ANDROID_H_
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+#include "content/browser/gamepad/gamepad_data_fetcher.h"
+#include "content/browser/gamepad/gamepad_provider.h"
+#include "content/browser/gamepad/gamepad_standard_mappings.h"
+#include "third_party/WebKit/public/platform/WebGamepads.h"
+
+namespace content {
+
+class GamepadPlatformDataFetcherAndroid : public GamepadDataFetcher {
+ public:
+ GamepadPlatformDataFetcherAndroid();
+ virtual ~GamepadPlatformDataFetcherAndroid();
+
+ virtual void PauseHint(bool paused) OVERRIDE;
+
+ virtual void GetGamepadData(blink::WebGamepads* pads,
+ bool devices_changed_hint) OVERRIDE;
+
+ // Registers the JNI methods for GamepadsReader.
+ static bool RegisterGamepadPlatformDataFetcherAndroid(JNIEnv* env);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(GamepadPlatformDataFetcherAndroid);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_GAMEPAD_GAMEPAD_PLATFORM_DATA_FETCHER_ANDROID_H_
diff --git a/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_linux.cc b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_linux.cc
index d466dde4b06..a211bb67df1 100644
--- a/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_linux.cc
+++ b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_linux.cc
@@ -187,10 +187,21 @@ void GamepadPlatformDataFetcherLinux::RefreshDevice(udev_device* dev) {
vendor_id,
product_id);
base::TruncateUTF8ToByteSize(id, WebGamepad::idLengthCap - 1, &id);
- base::string16 tmp16 = UTF8ToUTF16(id);
+ base::string16 tmp16 = base::UTF8ToUTF16(id);
memset(pad.id, 0, sizeof(pad.id));
tmp16.copy(pad.id, arraysize(pad.id) - 1);
+ if (mapper) {
+ std::string mapping = "standard";
+ base::TruncateUTF8ToByteSize(mapping, WebGamepad::mappingLengthCap - 1,
+ &mapping);
+ tmp16 = base::UTF8ToUTF16(mapping);
+ memset(pad.mapping, 0, sizeof(pad.mapping));
+ tmp16.copy(pad.mapping, arraysize(pad.mapping) - 1);
+ } else {
+ pad.mapping[0] = 0;
+ }
+
pad.connected = true;
}
}
@@ -246,7 +257,8 @@ void GamepadPlatformDataFetcherLinux::ReadDeviceData(size_t index) {
} else if (event.type & JS_EVENT_BUTTON) {
if (item >= WebGamepad::buttonsLengthCap)
continue;
- pad.buttons[item] = event.value ? 1.0 : 0.0;
+ pad.buttons[item].pressed = event.value;
+ pad.buttons[item].value = event.value ? 1.0 : 0.0;
if (item >= pad.buttonsLength)
pad.buttonsLength = item + 1;
}
diff --git a/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_mac.h b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_mac.h
index 54e1944da6c..54e65392864 100644
--- a/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_mac.h
+++ b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_mac.h
@@ -83,7 +83,7 @@ class GamepadPlatformDataFetcherMac : public GamepadDataFetcher,
struct {
IOHIDDeviceRef device_ref;
IOHIDElementRef button_elements[blink::WebGamepad::buttonsLengthCap];
- IOHIDElementRef axis_elements[blink::WebGamepad::buttonsLengthCap];
+ IOHIDElementRef axis_elements[blink::WebGamepad::axesLengthCap];
CFIndex axis_minimums[blink::WebGamepad::axesLengthCap];
CFIndex axis_maximums[blink::WebGamepad::axesLengthCap];
// Function to map from device data to standard layout, if available.
diff --git a/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_mac.mm b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_mac.mm
index 69d0c63e31d..fc457cdfb55 100644
--- a/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_mac.mm
+++ b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_mac.mm
@@ -21,6 +21,13 @@ namespace content {
namespace {
+void CopyNSStringAsUTF16LittleEndian(
+ NSString* src, blink::WebUChar* dest, size_t dest_len) {
+ NSData* as16 = [src dataUsingEncoding:NSUTF16LittleEndianStringEncoding];
+ memset(dest, 0, dest_len);
+ [as16 getBytes:dest length:dest_len - sizeof(blink::WebUChar)];
+}
+
NSDictionary* DeviceMatching(uint32_t usage_page, uint32_t usage) {
return [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithUnsignedInt:usage_page],
@@ -36,6 +43,7 @@ float NormalizeAxis(CFIndex value, CFIndex min, CFIndex max) {
// http://www.usb.org/developers/hidpage
const uint32_t kGenericDesktopUsagePage = 0x01;
+const uint32_t kGameControlsUsagePage = 0x05;
const uint32_t kButtonUsagePage = 0x09;
const uint32_t kJoystickUsageNumber = 0x04;
const uint32_t kGameUsageNumber = 0x05;
@@ -159,12 +167,14 @@ void GamepadPlatformDataFetcherMac::AddButtonsAndAxes(NSArray* elements,
memset(pad.axes, 0, sizeof(pad.axes));
memset(pad.buttons, 0, sizeof(pad.buttons));
+ bool mapped_all_axes = true;
+
for (id elem in elements) {
IOHIDElementRef element = reinterpret_cast<IOHIDElementRef>(elem);
- uint32_t usagePage = IOHIDElementGetUsagePage(element);
+ uint32_t usage_page = IOHIDElementGetUsagePage(element);
uint32_t usage = IOHIDElementGetUsage(element);
if (IOHIDElementGetType(element) == kIOHIDElementTypeInput_Button &&
- usagePage == kButtonUsagePage) {
+ usage_page == kButtonUsagePage) {
uint32_t button_index = usage - 1;
if (button_index < WebGamepad::buttonsLengthCap) {
associated.hid.button_elements[button_index] = element;
@@ -180,9 +190,40 @@ void GamepadPlatformDataFetcherMac::AddButtonsAndAxes(NSArray* elements,
IOHIDElementGetLogicalMax(element);
associated.hid.axis_elements[axis_index] = element;
pad.axesLength = std::max(pad.axesLength, axis_index + 1);
+ } else {
+ mapped_all_axes = false;
}
}
}
+
+ if (!mapped_all_axes) {
+ // For axes who's usage puts them outside the standard axesLengthCap range.
+ uint32_t next_index = 0;
+ for (id elem in elements) {
+ IOHIDElementRef element = reinterpret_cast<IOHIDElementRef>(elem);
+ uint32_t usage_page = IOHIDElementGetUsagePage(element);
+ uint32_t usage = IOHIDElementGetUsage(element);
+ if (IOHIDElementGetType(element) == kIOHIDElementTypeInput_Misc &&
+ usage - kAxisMinimumUsageNumber >= WebGamepad::axesLengthCap &&
+ usage_page <= kGameControlsUsagePage) {
+ for (; next_index < WebGamepad::axesLengthCap; ++next_index) {
+ if (associated.hid.axis_elements[next_index] == NULL)
+ break;
+ }
+ if (next_index < WebGamepad::axesLengthCap) {
+ associated.hid.axis_minimums[next_index] =
+ IOHIDElementGetLogicalMin(element);
+ associated.hid.axis_maximums[next_index] =
+ IOHIDElementGetLogicalMax(element);
+ associated.hid.axis_elements[next_index] = element;
+ pad.axesLength = std::max(pad.axesLength, next_index + 1);
+ }
+ }
+
+ if (next_index >= WebGamepad::axesLengthCap)
+ break;
+ }
+ }
}
size_t GamepadPlatformDataFetcherMac::GetEmptySlot() {
@@ -261,12 +302,19 @@ void GamepadPlatformDataFetcherMac::DeviceAdd(IOHIDDeviceRef device) {
associated_[slot].hid.mapper ? "STANDARD GAMEPAD " : "",
vendor_int,
product_int];
- NSData* as16 = [ident dataUsingEncoding:NSUTF16LittleEndianStringEncoding];
-
- const size_t kOutputLengthBytes = sizeof(data_.items[slot].id);
- memset(&data_.items[slot].id, 0, kOutputLengthBytes);
- [as16 getBytes:data_.items[slot].id
- length:kOutputLengthBytes - sizeof(blink::WebUChar)];
+ CopyNSStringAsUTF16LittleEndian(
+ ident,
+ data_.items[slot].id,
+ sizeof(data_.items[slot].id));
+
+ if (associated_[slot].hid.mapper) {
+ CopyNSStringAsUTF16LittleEndian(
+ @"standard",
+ data_.items[slot].mapping,
+ sizeof(data_.items[slot].mapping));
+ } else {
+ data_.items[slot].mapping[0] = 0;
+ }
base::ScopedCFTypeRef<CFArrayRef> elements(
IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone));
@@ -317,10 +365,18 @@ void GamepadPlatformDataFetcherMac::ValueChanged(IOHIDValueRef value) {
WebGamepad& pad = data_.items[slot];
AssociatedData& associated = associated_[slot];
+ uint32_t value_length = IOHIDValueGetLength(value);
+ if (value_length > 4) {
+ // Workaround for bizarre issue with PS3 controllers that try to return
+ // massive (30+ byte) values and crash IOHIDValueGetIntegerValue
+ return;
+ }
+
// Find and fill in the associated button event, if any.
for (size_t i = 0; i < pad.buttonsLength; ++i) {
if (associated.hid.button_elements[i] == element) {
- pad.buttons[i] = IOHIDValueGetIntegerValue(value) ? 1.f : 0.f;
+ pad.buttons[i].pressed = IOHIDValueGetIntegerValue(value);
+ pad.buttons[i].value = pad.buttons[i].pressed ? 1.f : 0.f;
pad.timestamp = std::max(pad.timestamp, IOHIDValueGetTimeStamp(value));
return;
}
@@ -353,13 +409,20 @@ void GamepadPlatformDataFetcherMac::XboxDeviceAdd(XboxController* device) {
NSString* ident =
[NSString stringWithFormat:
- @"Xbox 360 Controller (STANDARD GAMEPAD Vendor: %04x Product: %04x)",
+ @"%@ (STANDARD GAMEPAD Vendor: %04x Product: %04x)",
+ device->GetControllerType() == XboxController::XBOX_360_CONTROLLER
+ ? @"Xbox 360 Controller"
+ : @"Xbox One Controller",
device->GetProductId(), device->GetVendorId()];
- NSData* as16 = [ident dataUsingEncoding:NSUTF16StringEncoding];
- const size_t kOutputLengthBytes = sizeof(data_.items[slot].id);
- memset(&data_.items[slot].id, 0, kOutputLengthBytes);
- [as16 getBytes:data_.items[slot].id
- length:kOutputLengthBytes - sizeof(blink::WebUChar)];
+ CopyNSStringAsUTF16LittleEndian(
+ ident,
+ data_.items[slot].id,
+ sizeof(data_.items[slot].id));
+
+ CopyNSStringAsUTF16LittleEndian(
+ @"standard",
+ data_.items[slot].mapping,
+ sizeof(data_.items[slot].mapping));
associated_[slot].is_xbox = true;
associated_[slot].xbox.device = device;
@@ -406,12 +469,16 @@ void GamepadPlatformDataFetcherMac::XboxValueChanged(
WebGamepad& pad = data_.items[slot];
for (size_t i = 0; i < 6; i++) {
- pad.buttons[i] = data.buttons[i] ? 1.0f : 0.0f;
+ pad.buttons[i].pressed = data.buttons[i];
+ pad.buttons[i].value = data.buttons[i] ? 1.0f : 0.0f;
}
- pad.buttons[6] = data.triggers[0];
- pad.buttons[7] = data.triggers[1];
+ pad.buttons[6].pressed = data.triggers[0] > kDefaultButtonPressedThreshold;
+ pad.buttons[6].value = data.triggers[0];
+ pad.buttons[7].pressed = data.triggers[1] > kDefaultButtonPressedThreshold;
+ pad.buttons[7].value = data.triggers[1];
for (size_t i = 8; i < 17; i++) {
- pad.buttons[i] = data.buttons[i - 2] ? 1.0f : 0.0f;
+ pad.buttons[i].pressed = data.buttons[i - 2];
+ pad.buttons[i].value = data.buttons[i - 2] ? 1.0f : 0.0f;
}
for (size_t i = 0; i < arraysize(data.axes); i++) {
pad.axes[i] = data.axes[i];
diff --git a/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_win.cc b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_win.cc
index f8f3dafb04a..c344e7e79d5 100644
--- a/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_win.cc
+++ b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_win.cc
@@ -4,22 +4,12 @@
#include "content/browser/gamepad/gamepad_platform_data_fetcher_win.h"
-#include <dinput.h>
-#include <dinputd.h>
-
#include "base/debug/trace_event.h"
#include "base/strings/stringprintf.h"
#include "base/win/windows_version.h"
#include "content/common/gamepad_hardware_buffer.h"
#include "content/common/gamepad_messages.h"
-// This was removed from the Windows 8 SDK for some reason.
-// We need it so we can get state for axes without worrying if they
-// exist.
-#ifndef DIDFT_OPTIONAL
-#define DIDFT_OPTIONAL 0x80000000
-#endif
-
namespace content {
using namespace blink;
@@ -60,170 +50,20 @@ const WebUChar* const GamepadSubTypeName(BYTE sub_type) {
}
}
-bool GetDirectInputVendorProduct(IDirectInputDevice8* gamepad,
- std::string* vendor,
- std::string* product) {
- DIPROPDWORD prop;
- prop.diph.dwSize = sizeof(DIPROPDWORD);
- prop.diph.dwHeaderSize = sizeof(DIPROPHEADER);
- prop.diph.dwObj = 0;
- prop.diph.dwHow = DIPH_DEVICE;
-
- if (FAILED(gamepad->GetProperty(DIPROP_VIDPID, &prop.diph)))
- return false;
- *vendor = base::StringPrintf("%04x", LOWORD(prop.dwData));
- *product = base::StringPrintf("%04x", HIWORD(prop.dwData));
- return true;
-}
-
-// Sets the deadzone value for all axes of a gamepad.
-// deadzone values range from 0 (no deadzone) to 10,000 (entire range
-// is dead).
-bool SetDirectInputDeadZone(IDirectInputDevice8* gamepad,
- int deadzone) {
- DIPROPDWORD prop;
- prop.diph.dwSize = sizeof(DIPROPDWORD);
- prop.diph.dwHeaderSize = sizeof(DIPROPHEADER);
- prop.diph.dwObj = 0;
- prop.diph.dwHow = DIPH_DEVICE;
- prop.dwData = deadzone;
- return SUCCEEDED(gamepad->SetProperty(DIPROP_DEADZONE, &prop.diph));
-}
-
-struct InternalDirectInputDevice {
- IDirectInputDevice8* gamepad;
- GamepadStandardMappingFunction mapper;
- wchar_t id[WebGamepad::idLengthCap];
- GUID guid;
-};
-
-struct EnumDevicesContext {
- IDirectInput8* directinput_interface;
- std::vector<InternalDirectInputDevice>* directinput_devices;
-};
-
-// We define our own data format structure to attempt to get as many
-// axes as possible.
-struct JoyData {
- long axes[10];
- char buttons[24];
- DWORD pov; // Often used for D-pads.
-};
-
-BOOL CALLBACK DirectInputEnumDevicesCallback(const DIDEVICEINSTANCE* instance,
- void* context) {
- EnumDevicesContext* ctxt = reinterpret_cast<EnumDevicesContext*>(context);
- IDirectInputDevice8* gamepad;
-
- if (FAILED(ctxt->directinput_interface->CreateDevice(instance->guidInstance,
- &gamepad,
- NULL)))
- return DIENUM_CONTINUE;
-
- gamepad->Acquire();
-
-#define MAKE_AXIS(i) \
- {0, FIELD_OFFSET(JoyData, axes) + 4 * i, \
- DIDFT_AXIS | DIDFT_MAKEINSTANCE(i) | DIDFT_OPTIONAL, 0}
-#define MAKE_BUTTON(i) \
- {&GUID_Button, FIELD_OFFSET(JoyData, buttons) + i, \
- DIDFT_BUTTON | DIDFT_MAKEINSTANCE(i) | DIDFT_OPTIONAL, 0}
-#define MAKE_POV() \
- {&GUID_POV, FIELD_OFFSET(JoyData, pov), DIDFT_POV | DIDFT_OPTIONAL, 0}
- DIOBJECTDATAFORMAT rgodf[] = {
- MAKE_AXIS(0),
- MAKE_AXIS(1),
- MAKE_AXIS(2),
- MAKE_AXIS(3),
- MAKE_AXIS(4),
- MAKE_AXIS(5),
- MAKE_AXIS(6),
- MAKE_AXIS(7),
- MAKE_AXIS(8),
- MAKE_AXIS(9),
- MAKE_BUTTON(0),
- MAKE_BUTTON(1),
- MAKE_BUTTON(2),
- MAKE_BUTTON(3),
- MAKE_BUTTON(4),
- MAKE_BUTTON(5),
- MAKE_BUTTON(6),
- MAKE_BUTTON(7),
- MAKE_BUTTON(8),
- MAKE_BUTTON(9),
- MAKE_BUTTON(10),
- MAKE_BUTTON(11),
- MAKE_BUTTON(12),
- MAKE_BUTTON(13),
- MAKE_BUTTON(14),
- MAKE_BUTTON(15),
- MAKE_BUTTON(16),
- MAKE_POV(),
- };
-#undef MAKE_AXIS
-#undef MAKE_BUTTON
-#undef MAKE_POV
-
- DIDATAFORMAT df = {
- sizeof (DIDATAFORMAT),
- sizeof (DIOBJECTDATAFORMAT),
- DIDF_ABSAXIS,
- sizeof (JoyData),
- sizeof (rgodf) / sizeof (rgodf[0]),
- rgodf
- };
-
- // If we can't set the data format on the device, don't add it to our
- // list, since we won't know how to read data from it.
- if (FAILED(gamepad->SetDataFormat(&df))) {
- gamepad->Release();
- return DIENUM_CONTINUE;
- }
-
- InternalDirectInputDevice device;
- device.guid = instance->guidInstance;
- device.gamepad = gamepad;
- std::string vendor;
- std::string product;
- if (!GetDirectInputVendorProduct(gamepad, &vendor, &product)) {
- gamepad->Release();
- return DIENUM_CONTINUE;
- }
-
- // Set the dead zone to 10% of the axis length for all axes. This
- // gives us a larger space for what's "neutral" so the controls don't
- // slowly drift.
- SetDirectInputDeadZone(gamepad, 1000);
- device.mapper = GetGamepadStandardMappingFunction(vendor, product);
- if (device.mapper) {
- swprintf(device.id,
- WebGamepad::idLengthCap,
- L"STANDARD GAMEPAD (%ls)",
- instance->tszProductName);
- ctxt->directinput_devices->push_back(device);
- } else {
- gamepad->Release();
- }
- return DIENUM_CONTINUE;
-}
-
} // namespace
GamepadPlatformDataFetcherWin::GamepadPlatformDataFetcherWin()
: xinput_dll_(base::FilePath(FILE_PATH_LITERAL("xinput1_3.dll"))),
xinput_available_(GetXInputDllFunctions()) {
- // TODO(teravest): http://crbug.com/260187 for Windows XP.
- // TODO(teravest): http://crbug.com/305267 for later versions of windows.
- directinput_available_ = false;
for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i)
pad_state_[i].status = DISCONNECTED;
+
+ raw_input_fetcher_.reset(new RawInputDataFetcher());
+ raw_input_fetcher_->StartMonitor();
}
GamepadPlatformDataFetcherWin::~GamepadPlatformDataFetcherWin() {
- for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) {
- if (pad_state_[i].status == DIRECTINPUT_CONNECTED)
- pad_state_[i].directinput_gamepad->Release();
- }
+ raw_input_fetcher_->StopMonitor();
}
int GamepadPlatformDataFetcherWin::FirstAvailableGamepadId() const {
@@ -243,11 +83,11 @@ bool GamepadPlatformDataFetcherWin::HasXInputGamepad(int index) const {
return false;
}
-bool GamepadPlatformDataFetcherWin::HasDirectInputGamepad(
- const GUID& guid) const {
+bool GamepadPlatformDataFetcherWin::HasRawInputGamepad(
+ const HANDLE handle) const {
for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) {
- if (pad_state_[i].status == DIRECTINPUT_CONNECTED &&
- pad_state_[i].guid == guid)
+ if (pad_state_[i].status == RAWINPUT_CONNECTED &&
+ pad_state_[i].raw_input_handle == handle)
return true;
}
return false;
@@ -273,36 +113,42 @@ void GamepadPlatformDataFetcherWin::EnumerateDevices(
if (xinput_available_ && GetXInputPadConnectivity(i, &pad)) {
pad_state_[pad_index].status = XINPUT_CONNECTED;
pad_state_[pad_index].xinput_index = i;
+ pad_state_[pad_index].mapper = NULL;
+ pads->length++;
}
}
- if (directinput_available_) {
- struct EnumDevicesContext context;
- std::vector<InternalDirectInputDevice> directinput_gamepads;
- context.directinput_interface = directinput_interface_;
- context.directinput_devices = &directinput_gamepads;
-
- directinput_interface_->EnumDevices(
- DI8DEVCLASS_GAMECTRL,
- &DirectInputEnumDevicesCallback,
- &context,
- DIEDFL_ATTACHEDONLY);
- for (size_t i = 0; i < directinput_gamepads.size(); ++i) {
- if (HasDirectInputGamepad(directinput_gamepads[i].guid)) {
- directinput_gamepads[i].gamepad->Release();
+ if (raw_input_fetcher_->Available()) {
+ std::vector<RawGamepadInfo*> raw_inputs =
+ raw_input_fetcher_->EnumerateDevices();
+ for (size_t i = 0; i < raw_inputs.size(); ++i) {
+ RawGamepadInfo* gamepad = raw_inputs[i];
+ if (HasRawInputGamepad(gamepad->handle))
continue;
- }
int pad_index = FirstAvailableGamepadId();
if (pad_index == -1)
return;
WebGamepad& pad = pads->items[pad_index];
pad.connected = true;
- wcscpy_s(pad.id, WebGamepad::idLengthCap, directinput_gamepads[i].id);
PadState& state = pad_state_[pad_index];
- state.status = DIRECTINPUT_CONNECTED;
- state.guid = directinput_gamepads[i].guid;
- state.directinput_gamepad = directinput_gamepads[i].gamepad;
- state.mapper = directinput_gamepads[i].mapper;
+ state.status = RAWINPUT_CONNECTED;
+ state.raw_input_handle = gamepad->handle;
+
+ std::string vendor = base::StringPrintf("%04x", gamepad->vendor_id);
+ std::string product = base::StringPrintf("%04x", gamepad->product_id);
+ state.mapper = GetGamepadStandardMappingFunction(vendor, product);
+
+ swprintf(pad.id, WebGamepad::idLengthCap,
+ L"%ls (%lsVendor: %04x Product: %04x)",
+ gamepad->id, state.mapper ? L"STANDARD GAMEPAD " : L"",
+ gamepad->vendor_id, gamepad->product_id);
+
+ if (state.mapper)
+ swprintf(pad.mapping, WebGamepad::mappingLengthCap, L"standard");
+ else
+ pad.mapping[0] = 0;
+
+ pads->length++;
}
}
}
@@ -312,7 +158,8 @@ void GamepadPlatformDataFetcherWin::GetGamepadData(WebGamepads* pads,
bool devices_changed_hint) {
TRACE_EVENT0("GAMEPAD", "GetGamepadData");
- if (!xinput_available_ && !directinput_available_) {
+ if (!xinput_available_ &&
+ !raw_input_fetcher_->Available()) {
pads->length = 0;
return;
}
@@ -328,13 +175,24 @@ void GamepadPlatformDataFetcherWin::GetGamepadData(WebGamepads* pads,
EnumerateDevices(pads);
for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) {
- WebGamepad& pad = pads->items[i];
+ // We rely on device_changed and GetCapabilities to tell us that
+ // something's been connected, but we will mark as disconnected if
+ // Get___PadState returns that we've lost the pad.
+ if (!pads->items[i].connected)
+ continue;
+
if (pad_state_[i].status == XINPUT_CONNECTED)
- GetXInputPadData(i, &pad);
- else if (pad_state_[i].status == DIRECTINPUT_CONNECTED)
- GetDirectInputPadData(i, &pad);
+ GetXInputPadData(i, &pads->items[i]);
+ else if (pad_state_[i].status == RAWINPUT_CONNECTED)
+ GetRawInputPadData(i, &pads->items[i]);
}
- pads->length = WebGamepads::itemsLengthCap;
+}
+
+void GamepadPlatformDataFetcherWin::PauseHint(bool pause) {
+ if (pause)
+ raw_input_fetcher_->StopMonitor();
+ else
+ raw_input_fetcher_->StartMonitor();
}
bool GamepadPlatformDataFetcherWin::GetXInputPadConnectivity(
@@ -353,6 +211,7 @@ bool GamepadPlatformDataFetcherWin::GetXInputPadConnectivity(
WebGamepad::idLengthCap,
L"Xbox 360 Controller (XInput STANDARD %ls)",
GamepadSubTypeName(caps.SubType));
+ swprintf(pad->mapping, WebGamepad::mappingLengthCap, L"standard");
return true;
}
}
@@ -360,12 +219,6 @@ bool GamepadPlatformDataFetcherWin::GetXInputPadConnectivity(
void GamepadPlatformDataFetcherWin::GetXInputPadData(
int i,
WebGamepad* pad) {
- // We rely on device_changed and GetCapabilities to tell us that
- // something's been connected, but we will mark as disconnected if
- // GetState returns that we've lost the pad.
- if (!pad->connected)
- return;
-
XINPUT_STATE state;
memset(&state, 0, sizeof(XINPUT_STATE));
TRACE_EVENT_BEGIN1("GAMEPAD", "XInputGetState", "id", i);
@@ -375,16 +228,24 @@ void GamepadPlatformDataFetcherWin::GetXInputPadData(
if (dwResult == ERROR_SUCCESS) {
pad->timestamp = state.dwPacketNumber;
pad->buttonsLength = 0;
-#define ADD(b) pad->buttons[pad->buttonsLength++] = \
- ((state.Gamepad.wButtons & (b)) ? 1.0 : 0.0);
+#define ADD(b) pad->buttons[pad->buttonsLength].pressed = \
+ (state.Gamepad.wButtons & (b)) != 0; \
+ pad->buttons[pad->buttonsLength++].value = \
+ ((state.Gamepad.wButtons & (b)) ? 1.f : 0.f);
ADD(XINPUT_GAMEPAD_A);
ADD(XINPUT_GAMEPAD_B);
ADD(XINPUT_GAMEPAD_X);
ADD(XINPUT_GAMEPAD_Y);
ADD(XINPUT_GAMEPAD_LEFT_SHOULDER);
ADD(XINPUT_GAMEPAD_RIGHT_SHOULDER);
- pad->buttons[pad->buttonsLength++] = state.Gamepad.bLeftTrigger / 255.0;
- pad->buttons[pad->buttonsLength++] = state.Gamepad.bRightTrigger / 255.0;
+ pad->buttons[pad->buttonsLength].pressed =
+ state.Gamepad.bLeftTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
+ pad->buttons[pad->buttonsLength++].value =
+ state.Gamepad.bLeftTrigger / 255.f;
+ pad->buttons[pad->buttonsLength].pressed =
+ state.Gamepad.bRightTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
+ pad->buttons[pad->buttonsLength++].value =
+ state.Gamepad.bRightTrigger / 255.f;
ADD(XINPUT_GAMEPAD_BACK);
ADD(XINPUT_GAMEPAD_START);
ADD(XINPUT_GAMEPAD_LEFT_THUMB);
@@ -405,67 +266,36 @@ void GamepadPlatformDataFetcherWin::GetXInputPadData(
}
}
-void GamepadPlatformDataFetcherWin::GetDirectInputPadData(
+void GamepadPlatformDataFetcherWin::GetRawInputPadData(
int index,
WebGamepad* pad) {
- if (!pad->connected)
- return;
-
- IDirectInputDevice8* gamepad = pad_state_[index].directinput_gamepad;
- if (FAILED(gamepad->Poll())) {
- // Polling didn't work, try acquiring the gamepad.
- if (FAILED(gamepad->Acquire())) {
- pad->buttonsLength = 0;
- pad->axesLength = 0;
- return;
- }
- // Try polling again.
- if (FAILED(gamepad->Poll())) {
- pad->buttonsLength = 0;
- pad->axesLength = 0;
- return;
- }
- }
- JoyData state;
- if (FAILED(gamepad->GetDeviceState(sizeof(JoyData), &state))) {
+ RawGamepadInfo* gamepad = raw_input_fetcher_->GetGamepadInfo(
+ pad_state_[index].raw_input_handle);
+ if (!gamepad) {
pad->connected = false;
return;
}
- WebGamepad raw;
- raw.connected = true;
- for (int i = 0; i < 16; i++)
- raw.buttons[i] = (state.buttons[i] & 0x80) ? 1.0 : 0.0;
-
- // We map the POV (often a D-pad) into the buttons 16-19.
- // DirectInput gives pov measurements in hundredths of degrees,
- // clockwise from "North".
- // We use 22.5 degree slices so we can handle diagonal D-raw presses.
- static const int arc_segment = 2250; // 22.5 degrees = 1/16 circle
- if (state.pov > arc_segment && state.pov < 7 * arc_segment)
- raw.buttons[19] = 1.0;
- else
- raw.buttons[19] = 0.0;
+ WebGamepad raw_pad = *pad;
- if (state.pov > 5 * arc_segment && state.pov < 11 * arc_segment)
- raw.buttons[17] = 1.0;
- else
- raw.buttons[17] = 0.0;
+ raw_pad.timestamp = gamepad->report_id;
+ raw_pad.buttonsLength = gamepad->buttons_length;
+ raw_pad.axesLength = gamepad->axes_length;
- if (state.pov > 9 * arc_segment && state.pov < 15 * arc_segment)
- raw.buttons[18] = 1.0;
- else
- raw.buttons[18] = 0.0;
+ for (unsigned int i = 0; i < raw_pad.buttonsLength; i++) {
+ raw_pad.buttons[i].pressed = gamepad->buttons[i];
+ raw_pad.buttons[i].value = gamepad->buttons[i] ? 1.0 : 0.0;
+ }
- if (state.pov < 3 * arc_segment ||
- (state.pov > 13 * arc_segment && state.pov < 36000))
- raw.buttons[16] = 1.0;
- else
- raw.buttons[16] = 0.0;
+ for (unsigned int i = 0; i < raw_pad.axesLength; i++)
+ raw_pad.axes[i] = gamepad->axes[i].value;
- for (int i = 0; i < 10; i++)
- raw.axes[i] = state.axes[i];
- pad_state_[index].mapper(raw, pad);
+ // Copy to the current state to the output buffer, using the mapping
+ // function, if there is one available.
+ if (pad_state_[index].mapper)
+ pad_state_[index].mapper(raw_pad, pad);
+ else
+ *pad = raw_pad;
}
bool GamepadPlatformDataFetcherWin::GetXInputDllFunctions() {
diff --git a/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_win.h b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_win.h
index c894143b076..28082a9d529 100644
--- a/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_win.h
+++ b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_win.h
@@ -10,8 +10,6 @@
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
-#define DIRECTINPUT_VERSION 0x0800
-#include <dinput.h>
#include <stdlib.h>
#include <Unknwn.h>
#include <WinDef.h>
@@ -20,9 +18,13 @@
#include "base/basictypes.h"
#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
#include "base/scoped_native_library.h"
#include "content/browser/gamepad/gamepad_data_fetcher.h"
#include "content/browser/gamepad/gamepad_standard_mappings.h"
+#include "content/browser/gamepad/raw_input_data_fetcher_win.h"
#include "third_party/WebKit/public/platform/WebGamepads.h"
namespace content {
@@ -33,12 +35,12 @@ class GamepadPlatformDataFetcherWin : public GamepadDataFetcher {
virtual ~GamepadPlatformDataFetcherWin();
virtual void GetGamepadData(blink::WebGamepads* pads,
bool devices_changed_hint) OVERRIDE;
+ virtual void PauseHint(bool paused) OVERRIDE;
+
private:
// XInput-specific implementation for GetGamepadData.
bool GetXInputGamepadData(blink::WebGamepads* pads,
bool devices_changed_hint);
- bool GetDirectInputGamepadData(blink::WebGamepads* pads,
- bool devices_changed_hint);
// The three function types we use from xinput1_3.dll.
typedef void (WINAPI *XInputEnableFunc)(BOOL enable);
@@ -58,15 +60,14 @@ class GamepadPlatformDataFetcherWin : public GamepadDataFetcher {
bool GetXInputPadConnectivity(int i, blink::WebGamepad* pad) const;
void GetXInputPadData(int i, blink::WebGamepad* pad);
- void GetDirectInputPadData(int i, blink::WebGamepad* pad);
+ void GetRawInputPadData(int i, blink::WebGamepad* pad);
int FirstAvailableGamepadId() const;
bool HasXInputGamepad(int index) const;
- bool HasDirectInputGamepad(const GUID &guid) const;
+ bool HasRawInputGamepad(const HANDLE handle) const;
base::ScopedNativeLibrary xinput_dll_;
bool xinput_available_;
- bool directinput_available_;
// Function pointers to XInput functionality, retrieved in
// |GetXinputDllFunctions|.
@@ -74,24 +75,23 @@ class GamepadPlatformDataFetcherWin : public GamepadDataFetcher {
XInputGetCapabilitiesFunc xinput_get_capabilities_;
XInputGetStateFunc xinput_get_state_;
- IDirectInput8* directinput_interface_;
-
enum PadConnectionStatus {
DISCONNECTED,
XINPUT_CONNECTED,
- DIRECTINPUT_CONNECTED
+ RAWINPUT_CONNECTED
};
struct PadState {
PadConnectionStatus status;
- int xinput_index; // XInput-only.
- // Fields below are for DirectInput devices only.
- GUID guid;
- IDirectInputDevice8* directinput_gamepad;
GamepadStandardMappingFunction mapper;
+
+ int xinput_index; // XInput-only
+ HANDLE raw_input_handle; // RawInput-only fields.
};
PadState pad_state_[blink::WebGamepads::itemsLengthCap];
+ scoped_ptr<RawInputDataFetcher> raw_input_fetcher_;
+
DISALLOW_COPY_AND_ASSIGN(GamepadPlatformDataFetcherWin);
};
diff --git a/chromium/content/browser/gamepad/gamepad_provider.cc b/chromium/content/browser/gamepad/gamepad_provider.cc
index 55a5f30ed78..16976d96538 100644
--- a/chromium/content/browser/gamepad/gamepad_provider.cc
+++ b/chromium/content/browser/gamepad/gamepad_provider.cc
@@ -15,9 +15,14 @@
#include "content/browser/gamepad/gamepad_data_fetcher.h"
#include "content/browser/gamepad/gamepad_platform_data_fetcher.h"
#include "content/browser/gamepad/gamepad_provider.h"
+#include "content/browser/gamepad/gamepad_service.h"
#include "content/common/gamepad_hardware_buffer.h"
#include "content/common/gamepad_messages.h"
#include "content/common/gamepad_user_gesture.h"
+#include "content/public/browser/browser_thread.h"
+
+using blink::WebGamepad;
+using blink::WebGamepads;
namespace content {
@@ -34,14 +39,16 @@ GamepadProvider::ClosureAndThread::~ClosureAndThread() {
GamepadProvider::GamepadProvider()
: is_paused_(true),
have_scheduled_do_poll_(false),
- devices_changed_(true) {
+ devices_changed_(true),
+ ever_had_user_gesture_(false) {
Initialize(scoped_ptr<GamepadDataFetcher>());
}
GamepadProvider::GamepadProvider(scoped_ptr<GamepadDataFetcher> fetcher)
: is_paused_(true),
have_scheduled_do_poll_(false),
- devices_changed_(true) {
+ devices_changed_(true),
+ ever_had_user_gesture_(false) {
Initialize(fetcher.Pass());
}
@@ -63,6 +70,12 @@ base::SharedMemoryHandle GamepadProvider::GetSharedMemoryHandleForProcess(
return renderer_handle;
}
+void GamepadProvider::GetCurrentGamepadData(WebGamepads* data) {
+ const WebGamepads& pads = SharedMemoryAsHardwareBuffer()->buffer;
+ base::AutoLock lock(shared_memory_lock_);
+ *data = pads;
+}
+
void GamepadProvider::Pause() {
{
base::AutoLock lock(is_paused_lock_);
@@ -111,17 +124,22 @@ void GamepadProvider::Initialize(scoped_ptr<GamepadDataFetcher> fetcher) {
CHECK(res);
GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer();
memset(hwbuf, 0, sizeof(GamepadHardwareBuffer));
+ pad_states_.reset(new PadState[WebGamepads::itemsLengthCap]);
polling_thread_.reset(new base::Thread("Gamepad polling thread"));
-#if defined(OS_MACOSX)
- // On Mac, the data fetcher uses IOKit which depends on CFRunLoop, so the
- // message loop needs to be a UI-type loop.
- const base::MessageLoop::Type kMessageLoopType = base::MessageLoop::TYPE_UI;
-#else
+#if defined(OS_LINUX)
// On Linux, the data fetcher needs to watch file descriptors, so the message
- // loop needs to be a libevent loop. On Windows it doesn't matter what the
- // loop is.
+ // loop needs to be a libevent loop.
const base::MessageLoop::Type kMessageLoopType = base::MessageLoop::TYPE_IO;
+#elif defined(OS_ANDROID)
+ // On Android, keeping a message loop of default type.
+ const base::MessageLoop::Type kMessageLoopType =
+ base::MessageLoop::TYPE_DEFAULT;
+#else
+ // On Mac, the data fetcher uses IOKit which depends on CFRunLoop, so the
+ // message loop needs to be a UI-type loop. On Windows it must be a UI loop
+ // to properly pump the MessageWindow that captures device state.
+ const base::MessageLoop::Type kMessageLoopType = base::MessageLoop::TYPE_UI;
#endif
polling_thread_->StartWithOptions(base::Thread::Options(kMessageLoopType, 0));
@@ -148,6 +166,41 @@ void GamepadProvider::SendPauseHint(bool paused) {
data_fetcher_->PauseHint(paused);
}
+bool GamepadProvider::PadState::Match(const WebGamepad& pad) const {
+ return connected_ == pad.connected &&
+ axes_length_ == pad.axesLength &&
+ buttons_length_ == pad.buttonsLength &&
+ memcmp(id_, pad.id, arraysize(id_)) == 0 &&
+ memcmp(mapping_, pad.mapping, arraysize(mapping_)) == 0;
+}
+
+void GamepadProvider::PadState::SetPad(const WebGamepad& pad) {
+ DCHECK(pad.connected);
+ connected_ = true;
+ axes_length_ = pad.axesLength;
+ buttons_length_ = pad.buttonsLength;
+ memcpy(id_, pad.id, arraysize(id_));
+ memcpy(mapping_, pad.mapping, arraysize(mapping_));
+}
+
+void GamepadProvider::PadState::SetDisconnected() {
+ connected_ = false;
+ axes_length_ = 0;
+ buttons_length_ = 0;
+ memset(id_, 0, arraysize(id_));
+ memset(mapping_, 0, arraysize(mapping_));
+}
+
+void GamepadProvider::PadState::AsWebGamepad(WebGamepad* pad) {
+ pad->connected = connected_;
+ pad->axesLength = axes_length_;
+ pad->buttonsLength = buttons_length_;
+ memcpy(pad->id, id_, arraysize(id_));
+ memcpy(pad->mapping, mapping_, arraysize(mapping_));
+ memset(pad->axes, 0, arraysize(pad->axes));
+ memset(pad->buttons, 0, arraysize(pad->buttons));
+}
+
void GamepadProvider::DoPoll() {
DCHECK(base::MessageLoop::current() == polling_thread_->message_loop());
DCHECK(have_scheduled_do_poll_);
@@ -158,7 +211,7 @@ void GamepadProvider::DoPoll() {
ANNOTATE_BENIGN_RACE_SIZED(
&hwbuf->buffer,
- sizeof(blink::WebGamepads),
+ sizeof(WebGamepads),
"Racey reads are discarded");
{
@@ -167,14 +220,35 @@ void GamepadProvider::DoPoll() {
devices_changed_ = false;
}
- // Acquire the SeqLock. There is only ever one writer to this data.
- // See gamepad_hardware_buffer.h.
- hwbuf->sequence.WriteBegin();
- data_fetcher_->GetGamepadData(&hwbuf->buffer, changed);
- hwbuf->sequence.WriteEnd();
+ {
+ base::AutoLock lock(shared_memory_lock_);
+
+ // Acquire the SeqLock. There is only ever one writer to this data.
+ // See gamepad_hardware_buffer.h.
+ hwbuf->sequence.WriteBegin();
+ data_fetcher_->GetGamepadData(&hwbuf->buffer, changed);
+ hwbuf->sequence.WriteEnd();
+ }
CheckForUserGesture();
+ if (ever_had_user_gesture_) {
+ for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) {
+ WebGamepad& pad = hwbuf->buffer.items[i];
+ PadState& state = pad_states_.get()[i];
+ if (pad.connected && !state.connected()) {
+ OnGamepadConnectionChange(true, i, pad);
+ } else if (!pad.connected && state.connected()) {
+ OnGamepadConnectionChange(false, i, pad);
+ } else if (pad.connected && state.connected() && !state.Match(pad)) {
+ WebGamepad old_pad;
+ state.AsWebGamepad(&old_pad);
+ OnGamepadConnectionChange(false, i, old_pad);
+ OnGamepadConnectionChange(true, i, pad);
+ }
+ }
+ }
+
// Schedule our next interval of polling.
ScheduleDoPoll();
}
@@ -197,6 +271,32 @@ void GamepadProvider::ScheduleDoPoll() {
have_scheduled_do_poll_ = true;
}
+void GamepadProvider::OnGamepadConnectionChange(
+ bool connected, int index, const WebGamepad& pad) {
+ PadState& state = pad_states_.get()[index];
+ if (connected)
+ state.SetPad(pad);
+ else
+ state.SetDisconnected();
+
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&GamepadProvider::DispatchGamepadConnectionChange,
+ base::Unretained(this),
+ connected,
+ index,
+ pad));
+}
+
+void GamepadProvider::DispatchGamepadConnectionChange(
+ bool connected, int index, const WebGamepad& pad) {
+ if (connected)
+ GamepadService::GetInstance()->OnGamepadConnected(index, pad);
+ else
+ GamepadService::GetInstance()->OnGamepadDisconnected(index, pad);
+}
+
GamepadHardwareBuffer* GamepadProvider::SharedMemoryAsHardwareBuffer() {
void* mem = gamepad_shared_memory_.memory();
CHECK(mem);
@@ -205,10 +305,11 @@ GamepadHardwareBuffer* GamepadProvider::SharedMemoryAsHardwareBuffer() {
void GamepadProvider::CheckForUserGesture() {
base::AutoLock lock(user_gesture_lock_);
- if (user_gesture_observers_.empty())
- return; // Don't need to check if nobody is listening.
+ if (user_gesture_observers_.empty() && ever_had_user_gesture_)
+ return;
if (GamepadsHaveUserGesture(SharedMemoryAsHardwareBuffer()->buffer)) {
+ ever_had_user_gesture_ = true;
for (size_t i = 0; i < user_gesture_observers_.size(); i++) {
user_gesture_observers_[i].message_loop->PostTask(FROM_HERE,
user_gesture_observers_[i].closure);
diff --git a/chromium/content/browser/gamepad/gamepad_provider.h b/chromium/content/browser/gamepad/gamepad_provider.h
index 5f10f137a14..86a4b17baf3 100644
--- a/chromium/content/browser/gamepad/gamepad_provider.h
+++ b/chromium/content/browser/gamepad/gamepad_provider.h
@@ -17,6 +17,7 @@
#include "base/synchronization/lock.h"
#include "base/system_monitor/system_monitor.h"
#include "content/common/content_export.h"
+#include "third_party/WebKit/public/platform/WebGamepads.h"
namespace base {
class MessageLoopProxy;
@@ -43,6 +44,8 @@ class CONTENT_EXPORT GamepadProvider :
base::SharedMemoryHandle GetSharedMemoryHandleForProcess(
base::ProcessHandle renderer_process);
+ void GetCurrentGamepadData(blink::WebGamepads* data);
+
// Pause and resume the background polling thread. Can be called from any
// thread.
void Pause();
@@ -70,6 +73,13 @@ class CONTENT_EXPORT GamepadProvider :
void DoPoll();
void ScheduleDoPoll();
+ void OnGamepadConnectionChange(bool connected,
+ int index,
+ const blink::WebGamepad& pad);
+ void DispatchGamepadConnectionChange(bool connected,
+ int index,
+ const blink::WebGamepad& pad);
+
GamepadHardwareBuffer* SharedMemoryAsHardwareBuffer();
// Checks the gamepad state to see if the user has interacted with it.
@@ -112,9 +122,36 @@ class CONTENT_EXPORT GamepadProvider :
base::Lock devices_changed_lock_;
bool devices_changed_;
- // When polling_thread_ is running, members below are only to be used
- // from that thread.
+ bool ever_had_user_gesture_;
+
+ class PadState {
+ public:
+ PadState() {
+ SetDisconnected();
+ }
+
+ bool Match(const blink::WebGamepad& pad) const;
+ void SetPad(const blink::WebGamepad& pad);
+ void SetDisconnected();
+ void AsWebGamepad(blink::WebGamepad* pad);
+
+ bool connected() const { return connected_; }
+
+ private:
+ bool connected_;
+ unsigned axes_length_;
+ unsigned buttons_length_;
+ blink::WebUChar id_[blink::WebGamepad::idLengthCap];
+ blink::WebUChar mapping_[blink::WebGamepad::mappingLengthCap];
+ };
+
+ // Used to detect connections and disconnections.
+ scoped_ptr<PadState[]> pad_states_;
+
+ // Only used on the polling thread.
scoped_ptr<GamepadDataFetcher> data_fetcher_;
+
+ base::Lock shared_memory_lock_;
base::SharedMemory gamepad_shared_memory_;
// Polling is done on this background thread.
diff --git a/chromium/content/browser/gamepad/gamepad_provider_unittest.cc b/chromium/content/browser/gamepad/gamepad_provider_unittest.cc
index 398edca98e4..adfef0c5bf3 100644
--- a/chromium/content/browser/gamepad/gamepad_provider_unittest.cc
+++ b/chromium/content/browser/gamepad/gamepad_provider_unittest.cc
@@ -71,7 +71,8 @@ TEST_F(GamepadProviderTest, PollingAccess) {
test_data.items[0].timestamp = 0;
test_data.items[0].buttonsLength = 1;
test_data.items[0].axesLength = 2;
- test_data.items[0].buttons[0] = 1.f;
+ test_data.items[0].buttons[0].value = 1.f;
+ test_data.items[0].buttons[0].pressed = true;
test_data.items[0].axes[0] = -1.f;
test_data.items[0].axes[1] = .5f;
@@ -102,7 +103,8 @@ TEST_F(GamepadProviderTest, PollingAccess) {
EXPECT_EQ(1u, output.length);
EXPECT_EQ(1u, output.items[0].buttonsLength);
- EXPECT_EQ(1.f, output.items[0].buttons[0]);
+ EXPECT_EQ(1.f, output.items[0].buttons[0].value);
+ EXPECT_EQ(true, output.items[0].buttons[0].pressed);
EXPECT_EQ(2u, output.items[0].axesLength);
EXPECT_EQ(-1.f, output.items[0].axes[0]);
EXPECT_EQ(0.5f, output.items[0].axes[1]);
@@ -116,12 +118,14 @@ TEST_F(GamepadProviderTest, UserGesture) {
no_button_data.items[0].timestamp = 0;
no_button_data.items[0].buttonsLength = 1;
no_button_data.items[0].axesLength = 2;
- no_button_data.items[0].buttons[0] = 0.f;
+ no_button_data.items[0].buttons[0].value = 0.f;
+ no_button_data.items[0].buttons[0].pressed = false;
no_button_data.items[0].axes[0] = -1.f;
no_button_data.items[0].axes[1] = .5f;
WebGamepads button_down_data = no_button_data;
- button_down_data.items[0].buttons[0] = 1.f;
+ button_down_data.items[0].buttons[0].value = 1.f;
+ button_down_data.items[0].buttons[0].pressed = true;
UserGestureListener listener;
GamepadProvider* provider = CreateProvider(no_button_data);
diff --git a/chromium/content/browser/gamepad/gamepad_service.cc b/chromium/content/browser/gamepad/gamepad_service.cc
index 11a6964b3ab..a2a83b316fa 100644
--- a/chromium/content/browser/gamepad/gamepad_service.cc
+++ b/chromium/content/browser/gamepad/gamepad_service.cc
@@ -7,18 +7,23 @@
#include "base/bind.h"
#include "base/logging.h"
#include "base/memory/singleton.h"
+#include "content/browser/gamepad/gamepad_consumer.h"
#include "content/browser/gamepad/gamepad_data_fetcher.h"
#include "content/browser/gamepad/gamepad_provider.h"
+#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
namespace content {
-GamepadService::GamepadService() : num_readers_(0) {
+GamepadService::GamepadService()
+ : num_active_consumers_(0),
+ gesture_callback_pending_(false) {
}
GamepadService::GamepadService(scoped_ptr<GamepadDataFetcher> fetcher)
- : num_readers_(0),
- provider_(new GamepadProvider(fetcher.Pass())) {
+ : provider_(new GamepadProvider(fetcher.Pass())),
+ num_active_consumers_(0),
+ gesture_callback_pending_(false) {
thread_checker_.DetachFromThread();
}
@@ -30,28 +35,48 @@ GamepadService* GamepadService::GetInstance() {
LeakySingletonTraits<GamepadService> >::get();
}
-void GamepadService::AddConsumer() {
+void GamepadService::ConsumerBecameActive(GamepadConsumer* consumer) {
DCHECK(thread_checker_.CalledOnValidThread());
- num_readers_++;
- DCHECK(num_readers_ > 0);
if (!provider_)
provider_.reset(new GamepadProvider);
- provider_->Resume();
+
+ std::pair<ConsumerSet::iterator, bool> insert_result =
+ consumers_.insert(consumer);
+ insert_result.first->is_active = true;
+ if (!insert_result.first->did_observe_user_gesture &&
+ !gesture_callback_pending_) {
+ provider_->RegisterForUserGesture(
+ base::Bind(&GamepadService::OnUserGesture,
+ base::Unretained(this)));
+ }
+
+ if (num_active_consumers_++ == 0)
+ provider_->Resume();
}
-void GamepadService::RemoveConsumer() {
- DCHECK(thread_checker_.CalledOnValidThread());
+void GamepadService::ConsumerBecameInactive(GamepadConsumer* consumer) {
+ DCHECK(provider_);
+ DCHECK(num_active_consumers_ > 0);
+ DCHECK(consumers_.count(consumer) > 0);
+ DCHECK(consumers_.find(consumer)->is_active);
- --num_readers_;
- DCHECK(num_readers_ >= 0);
+ consumers_.find(consumer)->is_active = false;
+ if (--num_active_consumers_ == 0)
+ provider_->Pause();
+}
- if (num_readers_ == 0)
+void GamepadService::RemoveConsumer(GamepadConsumer* consumer) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ ConsumerSet::iterator it = consumers_.find(consumer);
+ if (it->is_active && --num_active_consumers_ == 0)
provider_->Pause();
+ consumers_.erase(it);
}
void GamepadService::RegisterForUserGesture(const base::Closure& closure) {
- DCHECK(num_readers_ > 0);
+ DCHECK(consumers_.size() > 0);
DCHECK(thread_checker_.CalledOnValidThread());
provider_->RegisterForUserGesture(closure);
}
@@ -60,10 +85,59 @@ void GamepadService::Terminate() {
provider_.reset();
}
+void GamepadService::OnGamepadConnected(
+ int index,
+ const blink::WebGamepad& pad) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ for (ConsumerSet::iterator it = consumers_.begin();
+ it != consumers_.end(); ++it) {
+ if (it->did_observe_user_gesture && it->is_active)
+ it->consumer->OnGamepadConnected(index, pad);
+ }
+}
+
+void GamepadService::OnGamepadDisconnected(
+ int index,
+ const blink::WebGamepad& pad) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ for (ConsumerSet::iterator it = consumers_.begin();
+ it != consumers_.end(); ++it) {
+ if (it->did_observe_user_gesture && it->is_active)
+ it->consumer->OnGamepadDisconnected(index, pad);
+ }
+}
+
base::SharedMemoryHandle GamepadService::GetSharedMemoryHandleForProcess(
base::ProcessHandle handle) {
DCHECK(thread_checker_.CalledOnValidThread());
return provider_->GetSharedMemoryHandleForProcess(handle);
}
+void GamepadService::OnUserGesture() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ gesture_callback_pending_ = false;
+
+ if (!provider_ ||
+ num_active_consumers_ == 0)
+ return;
+
+ for (ConsumerSet::iterator it = consumers_.begin();
+ it != consumers_.end(); ++it) {
+ if (!it->did_observe_user_gesture && it->is_active) {
+ const ConsumerInfo& info = *it;
+ info.did_observe_user_gesture = true;
+ blink::WebGamepads gamepads;
+ provider_->GetCurrentGamepadData(&gamepads);
+ for (unsigned i = 0; i < blink::WebGamepads::itemsLengthCap; ++i) {
+ const blink::WebGamepad& pad = gamepads.items[i];
+ if (pad.connected)
+ info.consumer->OnGamepadConnected(i, pad);
+ }
+ }
+ }
+}
+
} // namespace content
diff --git a/chromium/content/browser/gamepad/gamepad_service.h b/chromium/content/browser/gamepad/gamepad_service.h
index 94620b34b60..60081884228 100644
--- a/chromium/content/browser/gamepad/gamepad_service.h
+++ b/chromium/content/browser/gamepad/gamepad_service.h
@@ -5,6 +5,8 @@
#ifndef CONTENT_BROWSER_GAMEPAD_GAMEPAD_SERVICE_H
#define CONTENT_BROWSER_GAMEPAD_GAMEPAD_SERVICE_H
+#include <set>
+
#include "base/basictypes.h"
#include "base/callback_forward.h"
#include "base/memory/scoped_ptr.h"
@@ -13,8 +15,13 @@
#include "base/threading/thread_checker.h"
#include "content/common/content_export.h"
+namespace blink {
+class WebGamepad;
+}
+
namespace content {
+class GamepadConsumer;
class GamepadDataFetcher;
class GamepadProvider;
class GamepadServiceTestConstructor;
@@ -30,14 +37,28 @@ class CONTENT_EXPORT GamepadService {
// Increments the number of users of the provider. The Provider is running
// when there's > 0 users, and is paused when the count drops to 0.
+ // consumer is registered to listen for gamepad connections. If this is the
+ // first time it is added to the set of consumers it will be treated
+ // specially: it will not be informed about connections before a new user
+ // gesture is observed at which point it will be notified for every connected
+ // gamepads.
//
// Must be called on the I/O thread.
- void AddConsumer();
+ void ConsumerBecameActive(GamepadConsumer* consumer);
- // Removes a consumer. Should be matched with an AddConsumer call.
+ // Decrements the number of users of the provider. consumer will not be
+ // informed about connections until it's added back via ConsumerBecameActive.
+ // Must be matched with a ConsumerBecameActive call.
//
// Must be called on the I/O thread.
- void RemoveConsumer();
+ void ConsumerBecameInactive(GamepadConsumer* consumer);
+
+ // Decrements the number of users of the provider and removes consumer from
+ // the set of consumers. Should be matched with a a ConsumerBecameActive
+ // call.
+ //
+ // Must be called on the I/O thread.
+ void RemoveConsumer(GamepadConsumer* consumer);
// Registers the given closure for calling when the user has interacted with
// the device. This callback will only be issued once. Should only be called
@@ -52,6 +73,12 @@ class CONTENT_EXPORT GamepadService {
// Stop/join with the background thread in GamepadProvider |provider_|.
void Terminate();
+ // Called on IO thread when a gamepad is connected.
+ void OnGamepadConnected(int index, const blink::WebGamepad& pad);
+
+ // Called on IO thread when a gamepad is disconnected.
+ void OnGamepadDisconnected(int index, const blink::WebGamepad& pad);
+
private:
friend struct DefaultSingletonTraits<GamepadService>;
friend class GamepadServiceTestConstructor;
@@ -64,11 +91,34 @@ class CONTENT_EXPORT GamepadService {
virtual ~GamepadService();
- int num_readers_;
+ void OnUserGesture();
+
+ struct ConsumerInfo {
+ ConsumerInfo(GamepadConsumer* consumer)
+ : consumer(consumer),
+ did_observe_user_gesture(false) {
+ }
+
+ bool operator<(const ConsumerInfo& other) const {
+ return consumer < other.consumer;
+ }
+
+ GamepadConsumer* consumer;
+ mutable bool is_active;
+ mutable bool did_observe_user_gesture;
+ };
+
scoped_ptr<GamepadProvider> provider_;
base::ThreadChecker thread_checker_;
+ typedef std::set<ConsumerInfo> ConsumerSet;
+ ConsumerSet consumers_;
+
+ int num_active_consumers_;
+
+ bool gesture_callback_pending_;
+
DISALLOW_COPY_AND_ASSIGN(GamepadService);
};
diff --git a/chromium/content/browser/gamepad/gamepad_standard_mappings.cc b/chromium/content/browser/gamepad/gamepad_standard_mappings.cc
new file mode 100644
index 00000000000..60ae0725fb8
--- /dev/null
+++ b/chromium/content/browser/gamepad/gamepad_standard_mappings.cc
@@ -0,0 +1,60 @@
+// 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/gamepad/gamepad_standard_mappings.h"
+
+namespace content {
+
+blink::WebGamepadButton AxisToButton(float input) {
+ float value = (input + 1.f) / 2.f;
+ return blink::WebGamepadButton(
+ value > kDefaultButtonPressedThreshold, value);
+}
+
+blink::WebGamepadButton AxisNegativeAsButton(float input) {
+ float value = (input < -0.5f) ? 1.f : 0.f;
+ return blink::WebGamepadButton(
+ value > kDefaultButtonPressedThreshold, value);
+}
+
+blink::WebGamepadButton AxisPositiveAsButton(float input) {
+ float value = (input > 0.5f) ? 1.f : 0.f;
+ return blink::WebGamepadButton(
+ value > kDefaultButtonPressedThreshold, value);
+}
+
+blink::WebGamepadButton ButtonFromButtonAndAxis(
+ blink::WebGamepadButton button, float axis) {
+ float value = (axis + 1.f) / 2.f;
+ return blink::WebGamepadButton(button.pressed, value);
+}
+
+void DpadFromAxis(blink::WebGamepad* mapped, float dir) {
+ bool up = false;
+ bool right = false;
+ bool down = false;
+ bool left = false;
+
+ // Dpad is mapped as a direction on one axis, where -1 is up and it
+ // increases clockwise to 1, which is up + left. It's set to a large (> 1.f)
+ // number when nothing is depressed, except on start up, sometimes it's 0.0
+ // for no data, rather than the large number.
+ if (dir != 0.0f) {
+ up = (dir >= -1.f && dir < -0.7f) || (dir >= .95f && dir <= 1.f);
+ right = dir >= -.75f && dir < -.1f;
+ down = dir >= -.2f && dir < .45f;
+ left = dir >= .4f && dir <= 1.f;
+ }
+
+ mapped->buttons[kButtonDpadUp].pressed = up;
+ mapped->buttons[kButtonDpadUp].value = up ? 1.f : 0.f;
+ mapped->buttons[kButtonDpadRight].pressed = right;
+ mapped->buttons[kButtonDpadRight].value = right ? 1.f : 0.f;
+ mapped->buttons[kButtonDpadDown].pressed = down;
+ mapped->buttons[kButtonDpadDown].value = down ? 1.f : 0.f;
+ mapped->buttons[kButtonDpadLeft].pressed = left;
+ mapped->buttons[kButtonDpadLeft].value = left ? 1.f : 0.f;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/gamepad/gamepad_standard_mappings.h b/chromium/content/browser/gamepad/gamepad_standard_mappings.h
index 6b533ca5829..e436f0c7497 100644
--- a/chromium/content/browser/gamepad/gamepad_standard_mappings.h
+++ b/chromium/content/browser/gamepad/gamepad_standard_mappings.h
@@ -6,10 +6,7 @@
#define CONTENT_BROWSER_GAMEPAD_GAMEPAD_STANDARD_MAPPINGS_H_
#include "base/strings/string_piece.h"
-
-namespace blink {
-class WebGamepad;
-}
+#include "content/common/gamepad_hardware_buffer.h"
namespace content {
@@ -56,6 +53,17 @@ enum CanonicalAxisIndex {
kNumAxes
};
+// Matches XInput's trigger deadzone
+const float kDefaultButtonPressedThreshold = 30.f/255.f;
+
+// Common mapping functions
+blink::WebGamepadButton AxisToButton(float input);
+blink::WebGamepadButton AxisNegativeAsButton(float input);
+blink::WebGamepadButton AxisPositiveAsButton(float input);
+blink::WebGamepadButton ButtonFromButtonAndAxis(
+ blink::WebGamepadButton button, float axis);
+void DpadFromAxis(blink::WebGamepad* mapped, float dir);
+
} // namespace content
#endif // CONTENT_BROWSER_GAMEPAD_GAMEPAD_STANDARD_MAPPINGS_H_
diff --git a/chromium/content/browser/gamepad/gamepad_standard_mappings_linux.cc b/chromium/content/browser/gamepad/gamepad_standard_mappings_linux.cc
index 712e753defc..86c3afde8a1 100644
--- a/chromium/content/browser/gamepad/gamepad_standard_mappings_linux.cc
+++ b/chromium/content/browser/gamepad/gamepad_standard_mappings_linux.cc
@@ -4,24 +4,10 @@
#include "content/browser/gamepad/gamepad_standard_mappings.h"
-#include "content/common/gamepad_hardware_buffer.h"
-
namespace content {
namespace {
-float AxisToButton(float input) {
- return (input + 1.f) / 2.f;
-}
-
-float AxisNegativeAsButton(float input) {
- return (input < -0.5f) ? 1.f : 0.f;
-}
-
-float AxisPositiveAsButton(float input) {
- return (input > 0.5f) ? 1.f : 0.f;
-}
-
void MapperXInputStyleGamepad(
const blink::WebGamepad& input,
blink::WebGamepad* mapped) {
@@ -90,6 +76,39 @@ void MapperPlaystationSixAxis(
mapped->axesLength = kNumAxes;
}
+void MapperDualshock4(
+ const blink::WebGamepad& input,
+ blink::WebGamepad* mapped) {
+ enum Dualshock4Buttons {
+ kTouchpadButton = kNumButtons,
+ kNumDualshock4Buttons
+ };
+
+ *mapped = input;
+ mapped->buttons[kButtonPrimary] = input.buttons[1];
+ mapped->buttons[kButtonSecondary] = input.buttons[2];
+ mapped->buttons[kButtonTertiary] = input.buttons[0];
+ mapped->buttons[kButtonQuaternary] = input.buttons[3];
+ mapped->buttons[kButtonLeftShoulder] = input.buttons[4];
+ mapped->buttons[kButtonRightShoulder] = input.buttons[5];
+ mapped->buttons[kButtonLeftTrigger] = AxisToButton(input.axes[3]);
+ mapped->buttons[kButtonRightTrigger] = AxisToButton(input.axes[4]);
+ mapped->buttons[kButtonBackSelect] = input.buttons[8];
+ mapped->buttons[kButtonStart] = input.buttons[9];
+ mapped->buttons[kButtonLeftThumbstick] = input.buttons[10];
+ mapped->buttons[kButtonRightThumbstick] = input.buttons[11];
+ mapped->buttons[kButtonDpadUp] = AxisNegativeAsButton(input.axes[7]);
+ mapped->buttons[kButtonDpadDown] = AxisPositiveAsButton(input.axes[7]);
+ mapped->buttons[kButtonDpadLeft] = AxisNegativeAsButton(input.axes[6]);
+ mapped->buttons[kButtonDpadRight] = AxisPositiveAsButton(input.axes[6]);
+ mapped->buttons[kButtonMeta] = input.buttons[12];
+ mapped->buttons[kTouchpadButton] = input.buttons[13];
+ mapped->axes[kAxisRightStickY] = input.axes[5];
+
+ mapped->buttonsLength = kNumDualshock4Buttons;
+ mapped->axesLength = kNumAxes;
+}
+
void MapperXGEAR(
const blink::WebGamepad& input,
blink::WebGamepad* mapped) {
@@ -129,6 +148,28 @@ void MapperDragonRiseGeneric(
mapped->axesLength = kNumAxes;
}
+void MapperOnLiveWireless(
+ const blink::WebGamepad& input,
+ blink::WebGamepad* mapped) {
+ *mapped = input;
+ mapped->buttons[kButtonLeftTrigger] = AxisToButton(input.axes[2]);
+ mapped->buttons[kButtonRightTrigger] = AxisToButton(input.axes[5]);
+ mapped->buttons[kButtonBackSelect] = input.buttons[6];
+ mapped->buttons[kButtonStart] = input.buttons[7];
+ mapped->buttons[kButtonLeftThumbstick] = input.buttons[9];
+ mapped->buttons[kButtonRightThumbstick] = input.buttons[10];
+ mapped->buttons[kButtonDpadUp] = AxisNegativeAsButton(input.axes[7]);
+ mapped->buttons[kButtonDpadDown] = AxisPositiveAsButton(input.axes[7]);
+ mapped->buttons[kButtonDpadLeft] = AxisNegativeAsButton(input.axes[6]);
+ mapped->buttons[kButtonDpadRight] = AxisPositiveAsButton(input.axes[6]);
+ mapped->buttons[kButtonMeta] = input.buttons[8];
+ mapped->axes[kAxisRightStickX] = input.axes[3];
+ mapped->axes[kAxisRightStickY] = input.axes[4];
+
+ mapped->buttonsLength = kNumButtons;
+ mapped->axesLength = kNumAxes;
+}
+
struct MappingData {
const char* const vendor_id;
@@ -143,9 +184,12 @@ struct MappingData {
{ "046d", "c21e", MapperXInputStyleGamepad }, // Logitech F510
{ "046d", "c21f", MapperXInputStyleGamepad }, // Logitech F710
{ "054c", "0268", MapperPlaystationSixAxis }, // Playstation SIXAXIS
+ { "054c", "05c4", MapperDualshock4 }, // Playstation Dualshock 4
{ "0925", "0005", MapperLakeviewResearch }, // SmartJoy PLUS Adapter
{ "0925", "8866", MapperLakeviewResearch }, // WiseGroup MP-8866
{ "0e8f", "0003", MapperXGEAR }, // XFXforce XGEAR PS2 Controller
+ { "2378", "1008", MapperOnLiveWireless }, // OnLive Controller (Bluetooth)
+ { "2378", "100a", MapperOnLiveWireless }, // OnLive Controller (Wired)
};
} // namespace
diff --git a/chromium/content/browser/gamepad/gamepad_standard_mappings_mac.mm b/chromium/content/browser/gamepad/gamepad_standard_mappings_mac.mm
index 2696c53c686..5481f0e9921 100644
--- a/chromium/content/browser/gamepad/gamepad_standard_mappings_mac.mm
+++ b/chromium/content/browser/gamepad/gamepad_standard_mappings_mac.mm
@@ -4,35 +4,10 @@
#include "content/browser/gamepad/gamepad_standard_mappings.h"
-#include "content/common/gamepad_hardware_buffer.h"
-
namespace content {
namespace {
-float AxisToButton(float input) {
- return (input + 1.f) / 2.f;
-}
-
-void DpadFromAxis(blink::WebGamepad* mapped, float dir) {
- // Dpad is mapped as a direction on one axis, where -1 is up and it
- // increases clockwise to 1, which is up + left. It's set to a large (> 1.f)
- // number when nothing is depressed, except on start up, sometimes it's 0.0
- // for no data, rather than the large number.
- if (dir == 0.0f) {
- mapped->buttons[kButtonDpadUp] = 0.f;
- mapped->buttons[kButtonDpadDown] = 0.f;
- mapped->buttons[kButtonDpadLeft] = 0.f;
- mapped->buttons[kButtonDpadRight] = 0.f;
- } else {
- mapped->buttons[kButtonDpadUp] = (dir >= -1.f && dir < -0.7f) ||
- (dir >= .95f && dir <= 1.f);
- mapped->buttons[kButtonDpadRight] = dir >= -.75f && dir < -.1f;
- mapped->buttons[kButtonDpadDown] = dir >= -.2f && dir < .45f;
- mapped->buttons[kButtonDpadLeft] = dir >= .4f && dir <= 1.f;
- }
-}
-
void MapperXbox360Gamepad(
const blink::WebGamepad& input,
blink::WebGamepad* mapped) {
@@ -64,16 +39,27 @@ void MapperPlaystationSixAxis(
mapped->buttons[kButtonQuaternary] = input.buttons[12];
mapped->buttons[kButtonLeftShoulder] = input.buttons[10];
mapped->buttons[kButtonRightShoulder] = input.buttons[11];
- mapped->buttons[kButtonLeftTrigger] = input.buttons[8];
- mapped->buttons[kButtonRightTrigger] = input.buttons[9];
+
+ mapped->buttons[kButtonLeftTrigger] = ButtonFromButtonAndAxis(
+ input.buttons[8], input.axes[14]);
+ mapped->buttons[kButtonRightTrigger] = ButtonFromButtonAndAxis(
+ input.buttons[9], input.axes[15]);
+
mapped->buttons[kButtonBackSelect] = input.buttons[0];
mapped->buttons[kButtonStart] = input.buttons[3];
mapped->buttons[kButtonLeftThumbstick] = input.buttons[1];
mapped->buttons[kButtonRightThumbstick] = input.buttons[2];
- mapped->buttons[kButtonDpadUp] = input.buttons[4];
- mapped->buttons[kButtonDpadDown] = input.buttons[6];
- mapped->buttons[kButtonDpadLeft] = input.buttons[7];
- mapped->buttons[kButtonDpadRight] = input.buttons[5];
+
+ // The SixAxis Dpad is pressure sensative
+ mapped->buttons[kButtonDpadUp] = ButtonFromButtonAndAxis(
+ input.buttons[4], input.axes[10]);
+ mapped->buttons[kButtonDpadDown] = ButtonFromButtonAndAxis(
+ input.buttons[6], input.axes[12]);
+ mapped->buttons[kButtonDpadLeft] = ButtonFromButtonAndAxis(
+ input.buttons[7], input.axes[13]);
+ mapped->buttons[kButtonDpadRight] = ButtonFromButtonAndAxis(
+ input.buttons[5], input.axes[11]);
+
mapped->buttons[kButtonMeta] = input.buttons[16];
mapped->axes[kAxisRightStickY] = input.axes[5];
@@ -81,6 +67,36 @@ void MapperPlaystationSixAxis(
mapped->axesLength = kNumAxes;
}
+void MapperDualshock4(
+ const blink::WebGamepad& input,
+ blink::WebGamepad* mapped) {
+ enum Dualshock4Buttons {
+ kTouchpadButton = kNumButtons,
+ kNumDualshock4Buttons
+ };
+
+ *mapped = input;
+ mapped->buttons[kButtonPrimary] = input.buttons[1];
+ mapped->buttons[kButtonSecondary] = input.buttons[2];
+ mapped->buttons[kButtonTertiary] = input.buttons[0];
+ mapped->buttons[kButtonQuaternary] = input.buttons[3];
+ mapped->buttons[kButtonLeftShoulder] = input.buttons[4];
+ mapped->buttons[kButtonRightShoulder] = input.buttons[5];
+ mapped->buttons[kButtonLeftTrigger] = AxisToButton(input.axes[3]);
+ mapped->buttons[kButtonRightTrigger] = AxisToButton(input.axes[4]);
+ mapped->buttons[kButtonBackSelect] = input.buttons[8];
+ mapped->buttons[kButtonStart] = input.buttons[9];
+ mapped->buttons[kButtonLeftThumbstick] = input.buttons[10];
+ mapped->buttons[kButtonRightThumbstick] = input.buttons[11];
+ mapped->buttons[kButtonMeta] = input.buttons[12];
+ mapped->buttons[kTouchpadButton] = input.buttons[13];
+ mapped->axes[kAxisRightStickY] = input.axes[5];
+ DpadFromAxis(mapped, input.axes[9]);
+
+ mapped->buttonsLength = kNumDualshock4Buttons;
+ mapped->axesLength = kNumAxes;
+}
+
void MapperDirectInputStyle(
const blink::WebGamepad& input,
blink::WebGamepad* mapped) {
@@ -184,6 +200,31 @@ void MapperDragonRiseGeneric(
mapped->axesLength = kNumAxes;
}
+void MapperOnLiveWireless(
+ const blink::WebGamepad& input,
+ blink::WebGamepad* mapped) {
+ *mapped = input;
+ mapped->buttons[kButtonPrimary] = input.buttons[0];
+ mapped->buttons[kButtonSecondary] = input.buttons[1];
+ mapped->buttons[kButtonTertiary] = input.buttons[3];
+ mapped->buttons[kButtonQuaternary] = input.buttons[4];
+ mapped->buttons[kButtonLeftShoulder] = input.buttons[6];
+ mapped->buttons[kButtonRightShoulder] = input.buttons[7];
+ mapped->buttons[kButtonLeftTrigger] = AxisToButton(input.axes[2]);
+ mapped->buttons[kButtonRightTrigger] = AxisToButton(input.axes[5]);
+ mapped->buttons[kButtonBackSelect] = input.buttons[10];
+ mapped->buttons[kButtonStart] = input.buttons[11];
+ mapped->buttons[kButtonLeftThumbstick] = input.buttons[13];
+ mapped->buttons[kButtonRightThumbstick] = input.buttons[14];
+ mapped->buttons[kButtonMeta] = input.buttons[12];
+ mapped->axes[kAxisRightStickX] = input.axes[3];
+ mapped->axes[kAxisRightStickY] = input.axes[4];
+ DpadFromAxis(mapped, input.axes[9]);
+
+ mapped->buttonsLength = kNumButtons;
+ mapped->axesLength = kNumAxes;
+}
+
struct MappingData {
const char* const vendor_id;
const char* const product_id;
@@ -197,10 +238,13 @@ struct MappingData {
{ "046d", "c218", MapperDirectInputStyle }, // Logitech F510, D mode
{ "046d", "c219", MapperDirectInputStyle }, // Logitech F710, D mode
{ "054c", "0268", MapperPlaystationSixAxis }, // Playstation SIXAXIS
+ { "054c", "05c4", MapperDualshock4 }, // Playstation Dualshock 4
{ "0925", "0005", MapperSmartJoyPLUS }, // SmartJoy PLUS Adapter
{ "0e8f", "0003", MapperXGEAR }, // XFXforce XGEAR PS2 Controller
{ "2222", "0060", MapperDirectInputStyle }, // Macally iShockX, analog mode
{ "2222", "4010", MapperMacallyIShock }, // Macally iShock
+ { "2378", "1008", MapperOnLiveWireless }, // OnLive Controller (Bluetooth)
+ { "2378", "100a", MapperOnLiveWireless }, // OnLive Controller (Wired)
};
} // namespace
diff --git a/chromium/content/browser/gamepad/gamepad_standard_mappings_win.cc b/chromium/content/browser/gamepad/gamepad_standard_mappings_win.cc
index 07adb9b82ad..39ce9fcefab 100644
--- a/chromium/content/browser/gamepad/gamepad_standard_mappings_win.cc
+++ b/chromium/content/browser/gamepad/gamepad_standard_mappings_win.cc
@@ -4,96 +4,101 @@
#include "content/browser/gamepad/gamepad_standard_mappings.h"
-#include "content/common/gamepad_hardware_buffer.h"
-
namespace content {
namespace {
-// Maps 0..65535 to -1..1.
-float NormalizeDirectInputAxis(long value) {
- return (value * 1.f / 32767.5f) - 1.f;
-}
-
-float AxisNegativeAsButton(long value) {
- return (value < 32767) ? 1.f : 0.f;
-}
-
-float AxisPositiveAsButton(long value) {
- return (value > 32767) ? 1.f : 0.f;
-}
-
-void MapperDragonRiseGeneric(
+void MapperLogitechDualAction(
const blink::WebGamepad& input,
blink::WebGamepad* mapped) {
*mapped = input;
- mapped->buttons[0] = input.buttons[1];
- mapped->buttons[1] = input.buttons[2];
- mapped->buttons[2] = input.buttons[0];
- mapped->buttons[12] = input.buttons[16];
- mapped->buttons[13] = input.buttons[17];
- mapped->buttons[14] = input.buttons[18];
- mapped->buttons[15] = input.buttons[19];
- mapped->buttonsLength = 16;
- mapped->axes[0] = NormalizeDirectInputAxis(input.axes[0]);
- mapped->axes[1] = NormalizeDirectInputAxis(input.axes[1]);
- mapped->axes[2] = NormalizeDirectInputAxis(input.axes[2]);
- mapped->axes[3] = NormalizeDirectInputAxis(input.axes[5]);
- mapped->axesLength = 4;
+ mapped->buttons[kButtonPrimary] = input.buttons[1];
+ mapped->buttons[kButtonSecondary] = input.buttons[2];
+ mapped->buttons[kButtonTertiary] = input.buttons[0];
+ mapped->axes[kAxisRightStickY] = input.axes[5];
+ DpadFromAxis(mapped, input.axes[9]);
+
+ mapped->buttonsLength = kNumButtons;
+ mapped->axesLength = kNumAxes;
}
-void MapperLogitechDualAction(
+void Mapper2Axes8Keys(
const blink::WebGamepad& input,
blink::WebGamepad* mapped) {
*mapped = input;
- mapped->buttons[0] = input.buttons[1];
- mapped->buttons[1] = input.buttons[2];
- mapped->buttons[2] = input.buttons[0];
- mapped->buttons[12] = input.buttons[16];
- mapped->buttons[13] = input.buttons[17];
- mapped->buttons[14] = input.buttons[18];
- mapped->buttons[15] = input.buttons[19];
- mapped->buttonsLength = 16;
- mapped->axes[0] = NormalizeDirectInputAxis(input.axes[0]);
- mapped->axes[1] = NormalizeDirectInputAxis(input.axes[1]);
- mapped->axes[2] = NormalizeDirectInputAxis(input.axes[2]);
- mapped->axes[3] = NormalizeDirectInputAxis(input.axes[5]);
- mapped->axesLength = 4;
+ mapped->buttons[kButtonPrimary] = input.buttons[2];
+ mapped->buttons[kButtonSecondary] = input.buttons[1];
+ mapped->buttons[kButtonTertiary] = input.buttons[3];
+ mapped->buttons[kButtonQuaternary] = input.buttons[0];
+ mapped->buttons[kButtonDpadUp] = AxisNegativeAsButton(input.axes[1]);
+ mapped->buttons[kButtonDpadDown] = AxisPositiveAsButton(input.axes[1]);
+ mapped->buttons[kButtonDpadLeft] = AxisNegativeAsButton(input.axes[0]);
+ mapped->buttons[kButtonDpadRight] = AxisPositiveAsButton(input.axes[0]);
+
+ // Missing buttons
+ mapped->buttons[kButtonLeftTrigger] = blink::WebGamepadButton();
+ mapped->buttons[kButtonRightTrigger] = blink::WebGamepadButton();
+ mapped->buttons[kButtonLeftThumbstick] = blink::WebGamepadButton();
+ mapped->buttons[kButtonRightThumbstick] = blink::WebGamepadButton();
+ mapped->buttons[kButtonMeta] = blink::WebGamepadButton();
+
+ mapped->buttonsLength = kNumButtons - 1;
+ mapped->axesLength = 0;
}
-void MapperLogitechPrecision(
+void MapperDualshock4(
const blink::WebGamepad& input,
blink::WebGamepad* mapped) {
+ enum Dualshock4Buttons {
+ kTouchpadButton = kNumButtons,
+ kNumDualshock4Buttons
+ };
+
*mapped = input;
- mapped->buttons[0] = input.buttons[1];
- mapped->buttons[1] = input.buttons[2];
- mapped->buttons[2] = input.buttons[0];
- mapped->buttons[kButtonLeftThumbstick] = 0; // Not present
- mapped->buttons[kButtonRightThumbstick] = 0; // Not present
- mapped->buttons[12] = AxisNegativeAsButton(input.axes[1]);
- mapped->buttons[13] = AxisPositiveAsButton(input.axes[1]);
- mapped->buttons[14] = AxisNegativeAsButton(input.axes[0]);
- mapped->buttons[15] = AxisPositiveAsButton(input.axes[0]);
- mapped->buttonsLength = 16;
- mapped->axesLength = 0;
+ mapped->buttons[kButtonPrimary] = input.buttons[1];
+ mapped->buttons[kButtonSecondary] = input.buttons[2];
+ mapped->buttons[kButtonTertiary] = input.buttons[0];
+ mapped->buttons[kButtonQuaternary] = input.buttons[3];
+ mapped->buttons[kButtonLeftShoulder] = input.buttons[4];
+ mapped->buttons[kButtonRightShoulder] = input.buttons[5];
+ mapped->buttons[kButtonLeftTrigger] = AxisToButton(input.axes[3]);
+ mapped->buttons[kButtonRightTrigger] = AxisToButton(input.axes[4]);
+ mapped->buttons[kButtonBackSelect] = input.buttons[8];
+ mapped->buttons[kButtonStart] = input.buttons[9];
+ mapped->buttons[kButtonLeftThumbstick] = input.buttons[10];
+ mapped->buttons[kButtonRightThumbstick] = input.buttons[11];
+ mapped->buttons[kButtonMeta] = input.buttons[12];
+ mapped->buttons[kTouchpadButton] = input.buttons[13];
+ mapped->axes[kAxisRightStickY] = input.axes[5];
+ DpadFromAxis(mapped, input.axes[9]);
+
+ mapped->buttonsLength = kNumDualshock4Buttons;
+ mapped->axesLength = kNumAxes;
}
-void Mapper2Axes8Keys(
+void MapperOnLiveWireless(
const blink::WebGamepad& input,
blink::WebGamepad* mapped) {
*mapped = input;
- mapped->buttons[kButtonLeftTrigger] = 0; // Not present
- mapped->buttons[kButtonRightTrigger] = 0; // Not present
- mapped->buttons[8] = input.buttons[6];
- mapped->buttons[9] = input.buttons[7];
- mapped->buttons[kButtonLeftThumbstick] = 0; // Not present
- mapped->buttons[kButtonRightThumbstick] = 0; // Not present
- mapped->buttons[12] = AxisNegativeAsButton(input.axes[1]);
- mapped->buttons[13] = AxisPositiveAsButton(input.axes[1]);
- mapped->buttons[14] = AxisNegativeAsButton(input.axes[0]);
- mapped->buttons[15] = AxisPositiveAsButton(input.axes[0]);
- mapped->buttonsLength = 16;
- mapped->axesLength = 0;
+ mapped->buttons[kButtonPrimary] = input.buttons[0];
+ mapped->buttons[kButtonSecondary] = input.buttons[1];
+ mapped->buttons[kButtonTertiary] = input.buttons[3];
+ mapped->buttons[kButtonQuaternary] = input.buttons[4];
+ mapped->buttons[kButtonLeftShoulder] = input.buttons[6];
+ mapped->buttons[kButtonRightShoulder] = input.buttons[7];
+ mapped->buttons[kButtonLeftTrigger] = AxisToButton(input.axes[2]);
+ mapped->buttons[kButtonRightTrigger] = AxisToButton(input.axes[5]);
+ mapped->buttons[kButtonBackSelect] = input.buttons[10];
+ mapped->buttons[kButtonStart] = input.buttons[11];
+ mapped->buttons[kButtonLeftThumbstick] = input.buttons[13];
+ mapped->buttons[kButtonRightThumbstick] = input.buttons[14];
+ mapped->buttons[kButtonMeta] = input.buttons[12];
+ mapped->axes[kAxisRightStickX] = input.axes[3];
+ mapped->axes[kAxisRightStickY] = input.axes[4];
+ DpadFromAxis(mapped, input.axes[9]);
+
+ mapped->buttonsLength = kNumButtons;
+ mapped->axesLength = kNumAxes;
}
struct MappingData {
@@ -102,10 +107,11 @@ struct MappingData {
GamepadStandardMappingFunction function;
} AvailableMappings[] = {
// http://www.linux-usb.org/usb.ids
- { "0079", "0006", MapperDragonRiseGeneric }, // DragonRise Generic USB
{ "046d", "c216", MapperLogitechDualAction }, // Logitech DualAction
- { "046d", "c21a", MapperLogitechPrecision }, // Logitech Precision
- { "12bd", "d012", Mapper2Axes8Keys }, // 2Axes 8Keys Game Pad
+ { "0079", "0011", Mapper2Axes8Keys }, // 2Axes 8Keys Game Pad
+ { "054c", "05c4", MapperDualshock4 }, // Playstation Dualshock 4
+ { "2378", "1008", MapperOnLiveWireless }, // OnLive Controller (Bluetooth)
+ { "2378", "100a", MapperOnLiveWireless }, // OnLive Controller (Wired)
};
} // namespace
diff --git a/chromium/content/browser/gamepad/raw_input_data_fetcher_win.cc b/chromium/content/browser/gamepad/raw_input_data_fetcher_win.cc
new file mode 100644
index 00000000000..05748216b27
--- /dev/null
+++ b/chromium/content/browser/gamepad/raw_input_data_fetcher_win.cc
@@ -0,0 +1,505 @@
+// 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/gamepad/raw_input_data_fetcher_win.h"
+
+#include "base/debug/trace_event.h"
+#include "content/common/gamepad_hardware_buffer.h"
+#include "content/common/gamepad_messages.h"
+
+namespace content {
+
+using namespace blink;
+
+namespace {
+
+float NormalizeAxis(long value, long min, long max) {
+ return (2.f * (value - min) / static_cast<float>(max - min)) - 1.f;
+}
+
+// From the HID Usage Tables specification.
+USHORT DeviceUsages[] = {
+ 0x04, // Joysticks
+ 0x05, // Gamepads
+ 0x08, // Multi Axis
+};
+
+const uint32_t kAxisMinimumUsageNumber = 0x30;
+const uint32_t kGameControlsUsagePage = 0x05;
+
+} // namespace
+
+RawInputDataFetcher::RawInputDataFetcher()
+ : hid_dll_(base::FilePath(FILE_PATH_LITERAL("hid.dll")))
+ , rawinput_available_(GetHidDllFunctions())
+ , filter_xinput_(true)
+ , events_monitored_(false) {}
+
+RawInputDataFetcher::~RawInputDataFetcher() {
+ DCHECK(!window_);
+ DCHECK(!events_monitored_);
+}
+
+void RawInputDataFetcher::WillDestroyCurrentMessageLoop() {
+ StopMonitor();
+}
+
+RAWINPUTDEVICE* RawInputDataFetcher::GetRawInputDevices(DWORD flags) {
+ int usage_count = arraysize(DeviceUsages);
+ scoped_ptr<RAWINPUTDEVICE[]> devices(new RAWINPUTDEVICE[usage_count]);
+ for (int i = 0; i < usage_count; ++i) {
+ devices[i].dwFlags = flags;
+ devices[i].usUsagePage = 1;
+ devices[i].usUsage = DeviceUsages[i];
+ devices[i].hwndTarget = window_->hwnd();
+ }
+ return devices.release();
+}
+
+void RawInputDataFetcher::StartMonitor() {
+ if (!rawinput_available_ || events_monitored_)
+ return;
+
+ if (!window_) {
+ window_.reset(new base::win::MessageWindow());
+ if (!window_->Create(base::Bind(&RawInputDataFetcher::HandleMessage,
+ base::Unretained(this)))) {
+ PLOG(ERROR) << "Failed to create the raw input window";
+ window_.reset();
+ return;
+ }
+ }
+
+ // Register to receive raw HID input.
+ scoped_ptr<RAWINPUTDEVICE[]> devices(GetRawInputDevices(RIDEV_INPUTSINK));
+ if (!RegisterRawInputDevices(devices.get(), arraysize(DeviceUsages),
+ sizeof(RAWINPUTDEVICE))) {
+ PLOG(ERROR) << "RegisterRawInputDevices() failed for RIDEV_INPUTSINK";
+ window_.reset();
+ return;
+ }
+
+ // Start observing message loop destruction if we start monitoring the first
+ // event.
+ if (!events_monitored_)
+ base::MessageLoop::current()->AddDestructionObserver(this);
+
+ events_monitored_ = true;
+}
+
+void RawInputDataFetcher::StopMonitor() {
+ if (!rawinput_available_ || !events_monitored_)
+ return;
+
+ // Stop receiving raw input.
+ DCHECK(window_);
+ scoped_ptr<RAWINPUTDEVICE[]> devices(GetRawInputDevices(RIDEV_REMOVE));
+
+ if (!RegisterRawInputDevices(devices.get(), arraysize(DeviceUsages),
+ sizeof(RAWINPUTDEVICE))) {
+ PLOG(INFO) << "RegisterRawInputDevices() failed for RIDEV_REMOVE";
+ }
+
+ events_monitored_ = false;
+ window_.reset();
+ ClearControllers();
+
+ // Stop observing message loop destruction if no event is being monitored.
+ base::MessageLoop::current()->RemoveDestructionObserver(this);
+}
+
+void RawInputDataFetcher::ClearControllers() {
+ while (!controllers_.empty()) {
+ RawGamepadInfo* gamepad_info = controllers_.begin()->second;
+ controllers_.erase(gamepad_info->handle);
+ delete gamepad_info;
+ }
+}
+
+std::vector<RawGamepadInfo*> RawInputDataFetcher::EnumerateDevices() {
+ std::vector<RawGamepadInfo*> valid_controllers;
+
+ ClearControllers();
+
+ UINT count = 0;
+ UINT result = GetRawInputDeviceList(NULL, &count, sizeof(RAWINPUTDEVICELIST));
+ if (result == static_cast<UINT>(-1)) {
+ PLOG(ERROR) << "GetRawInputDeviceList() failed";
+ return valid_controllers;
+ }
+ DCHECK_EQ(0u, result);
+
+ scoped_ptr<RAWINPUTDEVICELIST[]> device_list(new RAWINPUTDEVICELIST[count]);
+ result = GetRawInputDeviceList(device_list.get(), &count,
+ sizeof(RAWINPUTDEVICELIST));
+ if (result == static_cast<UINT>(-1)) {
+ PLOG(ERROR) << "GetRawInputDeviceList() failed";
+ return valid_controllers;
+ }
+ DCHECK_EQ(count, result);
+
+ for (UINT i = 0; i < count; ++i) {
+ if (device_list[i].dwType == RIM_TYPEHID) {
+ HANDLE device_handle = device_list[i].hDevice;
+ RawGamepadInfo* gamepad_info = ParseGamepadInfo(device_handle);
+ if (gamepad_info) {
+ controllers_[device_handle] = gamepad_info;
+ valid_controllers.push_back(gamepad_info);
+ }
+ }
+ }
+ return valid_controllers;
+}
+
+RawGamepadInfo* RawInputDataFetcher::GetGamepadInfo(HANDLE handle) {
+ std::map<HANDLE, RawGamepadInfo*>::iterator it = controllers_.find(handle);
+ if (it != controllers_.end())
+ return it->second;
+
+ return NULL;
+}
+
+RawGamepadInfo* RawInputDataFetcher::ParseGamepadInfo(HANDLE hDevice) {
+ UINT size = 0;
+
+ // Do we already have this device in the map?
+ if (GetGamepadInfo(hDevice))
+ return NULL;
+
+ // Query basic device info.
+ UINT result = GetRawInputDeviceInfo(hDevice, RIDI_DEVICEINFO,
+ NULL, &size);
+ if (result == static_cast<UINT>(-1)) {
+ PLOG(ERROR) << "GetRawInputDeviceInfo() failed";
+ return NULL;
+ }
+ DCHECK_EQ(0u, result);
+
+ scoped_ptr<uint8[]> di_buffer(new uint8[size]);
+ RID_DEVICE_INFO* device_info =
+ reinterpret_cast<RID_DEVICE_INFO*>(di_buffer.get());
+ result = GetRawInputDeviceInfo(hDevice, RIDI_DEVICEINFO,
+ di_buffer.get(), &size);
+ if (result == static_cast<UINT>(-1)) {
+ PLOG(ERROR) << "GetRawInputDeviceInfo() failed";
+ return NULL;
+ }
+ DCHECK_EQ(size, result);
+
+ // Make sure this device is of a type that we want to observe.
+ bool valid_type = false;
+ for (int i = 0; i < arraysize(DeviceUsages); ++i) {
+ if (device_info->hid.usUsage == DeviceUsages[i]) {
+ valid_type = true;
+ break;
+ }
+ }
+
+ if (!valid_type)
+ return NULL;
+
+ scoped_ptr<RawGamepadInfo> gamepad_info(new RawGamepadInfo);
+ gamepad_info->handle = hDevice;
+ gamepad_info->report_id = 0;
+ gamepad_info->vendor_id = device_info->hid.dwVendorId;
+ gamepad_info->product_id = device_info->hid.dwProductId;
+ gamepad_info->buttons_length = 0;
+ ZeroMemory(gamepad_info->buttons, sizeof(gamepad_info->buttons));
+ gamepad_info->axes_length = 0;
+ ZeroMemory(gamepad_info->axes, sizeof(gamepad_info->axes));
+
+ // Query device identifier
+ result = GetRawInputDeviceInfo(hDevice, RIDI_DEVICENAME,
+ NULL, &size);
+ if (result == static_cast<UINT>(-1)) {
+ PLOG(ERROR) << "GetRawInputDeviceInfo() failed";
+ return NULL;
+ }
+ DCHECK_EQ(0u, result);
+
+ scoped_ptr<wchar_t[]> name_buffer(new wchar_t[size]);
+ result = GetRawInputDeviceInfo(hDevice, RIDI_DEVICENAME,
+ name_buffer.get(), &size);
+ if (result == static_cast<UINT>(-1)) {
+ PLOG(ERROR) << "GetRawInputDeviceInfo() failed";
+ return NULL;
+ }
+ DCHECK_EQ(size, result);
+
+ // The presence of "IG_" in the device name indicates that this is an XInput
+ // Gamepad. Skip enumerating these devices and let the XInput path handle it.
+ // http://msdn.microsoft.com/en-us/library/windows/desktop/ee417014.aspx
+ if (filter_xinput_ && wcsstr( name_buffer.get(), L"IG_" ) )
+ return NULL;
+
+ // Get a friendly device name
+ BOOLEAN got_product_string = FALSE;
+ HANDLE hid_handle = CreateFile(name_buffer.get(), GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL);
+ if (hid_handle) {
+ got_product_string = hidd_get_product_string_(hid_handle, gamepad_info->id,
+ sizeof(gamepad_info->id));
+ CloseHandle(hid_handle);
+ }
+
+ if (!got_product_string)
+ swprintf(gamepad_info->id, WebGamepad::idLengthCap, L"Unknown Gamepad");
+
+ // Query device capabilities.
+ result = GetRawInputDeviceInfo(hDevice, RIDI_PREPARSEDDATA,
+ NULL, &size);
+ if (result == static_cast<UINT>(-1)) {
+ PLOG(ERROR) << "GetRawInputDeviceInfo() failed";
+ return NULL;
+ }
+ DCHECK_EQ(0u, result);
+
+ gamepad_info->ppd_buffer.reset(new uint8[size]);
+ gamepad_info->preparsed_data =
+ reinterpret_cast<PHIDP_PREPARSED_DATA>(gamepad_info->ppd_buffer.get());
+ result = GetRawInputDeviceInfo(hDevice, RIDI_PREPARSEDDATA,
+ gamepad_info->ppd_buffer.get(), &size);
+ if (result == static_cast<UINT>(-1)) {
+ PLOG(ERROR) << "GetRawInputDeviceInfo() failed";
+ return NULL;
+ }
+ DCHECK_EQ(size, result);
+
+ HIDP_CAPS caps;
+ NTSTATUS status = hidp_get_caps_(gamepad_info->preparsed_data, &caps);
+ DCHECK_EQ(HIDP_STATUS_SUCCESS, status);
+
+ // Query button information.
+ USHORT count = caps.NumberInputButtonCaps;
+ if (count > 0) {
+ scoped_ptr<HIDP_BUTTON_CAPS[]> button_caps(new HIDP_BUTTON_CAPS[count]);
+ status = hidp_get_button_caps_(
+ HidP_Input, button_caps.get(), &count, gamepad_info->preparsed_data);
+ DCHECK_EQ(HIDP_STATUS_SUCCESS, status);
+
+ for (uint32_t i = 0; i < count; ++i) {
+ if (button_caps[i].Range.UsageMin <= WebGamepad::buttonsLengthCap) {
+ uint32_t max_index =
+ std::min(WebGamepad::buttonsLengthCap,
+ static_cast<size_t>(button_caps[i].Range.UsageMax));
+ gamepad_info->buttons_length = std::max(
+ gamepad_info->buttons_length, max_index);
+ }
+ }
+ }
+
+ // Query axis information.
+ count = caps.NumberInputValueCaps;
+ scoped_ptr<HIDP_VALUE_CAPS[]> axes_caps(new HIDP_VALUE_CAPS[count]);
+ status = hidp_get_value_caps_(HidP_Input, axes_caps.get(), &count,
+ gamepad_info->preparsed_data);
+
+ bool mapped_all_axes = true;
+
+ for (UINT i = 0; i < count; i++) {
+ uint32_t axis_index = axes_caps[i].Range.UsageMin - kAxisMinimumUsageNumber;
+ if (axis_index < WebGamepad::axesLengthCap) {
+ gamepad_info->axes[axis_index].caps = axes_caps[i];
+ gamepad_info->axes[axis_index].value = 0;
+ gamepad_info->axes[axis_index].active = true;
+ gamepad_info->axes_length =
+ std::max(gamepad_info->axes_length, axis_index + 1);
+ } else {
+ mapped_all_axes = false;
+ }
+ }
+
+ if (!mapped_all_axes) {
+ // For axes who's usage puts them outside the standard axesLengthCap range.
+ uint32_t next_index = 0;
+ for (UINT i = 0; i < count; i++) {
+ uint32_t usage = axes_caps[i].Range.UsageMin - kAxisMinimumUsageNumber;
+ if (usage >= WebGamepad::axesLengthCap &&
+ axes_caps[i].UsagePage <= kGameControlsUsagePage) {
+
+ for (; next_index < WebGamepad::axesLengthCap; ++next_index) {
+ if (!gamepad_info->axes[next_index].active)
+ break;
+ }
+ if (next_index < WebGamepad::axesLengthCap) {
+ gamepad_info->axes[next_index].caps = axes_caps[i];
+ gamepad_info->axes[next_index].value = 0;
+ gamepad_info->axes[next_index].active = true;
+ gamepad_info->axes_length =
+ std::max(gamepad_info->axes_length, next_index + 1);
+ }
+ }
+
+ if (next_index >= WebGamepad::axesLengthCap)
+ break;
+ }
+ }
+
+ return gamepad_info.release();
+}
+
+void RawInputDataFetcher::UpdateGamepad(
+ RAWINPUT* input,
+ RawGamepadInfo* gamepad_info) {
+ NTSTATUS status;
+
+ gamepad_info->report_id++;
+
+ // Query button state.
+ if (gamepad_info->buttons_length) {
+ // Clear the button state
+ ZeroMemory(gamepad_info->buttons, sizeof(gamepad_info->buttons));
+ ULONG buttons_length = 0;
+
+ hidp_get_usages_ex_(HidP_Input,
+ 0,
+ NULL,
+ &buttons_length,
+ gamepad_info->preparsed_data,
+ reinterpret_cast<PCHAR>(input->data.hid.bRawData),
+ input->data.hid.dwSizeHid);
+
+ scoped_ptr<USAGE_AND_PAGE[]> usages(new USAGE_AND_PAGE[buttons_length]);
+ status =
+ hidp_get_usages_ex_(HidP_Input,
+ 0,
+ usages.get(),
+ &buttons_length,
+ gamepad_info->preparsed_data,
+ reinterpret_cast<PCHAR>(input->data.hid.bRawData),
+ input->data.hid.dwSizeHid);
+
+ if (status == HIDP_STATUS_SUCCESS) {
+ // Set each reported button to true.
+ for (uint32_t j = 0; j < buttons_length; j++) {
+ int32_t button_index = usages[j].Usage - 1;
+ if (button_index >= 0 &&
+ button_index < blink::WebGamepad::buttonsLengthCap)
+ gamepad_info->buttons[button_index] = true;
+ }
+ }
+ }
+
+ // Query axis state.
+ ULONG axis_value = 0;
+ LONG scaled_axis_value = 0;
+ for (uint32_t i = 0; i < gamepad_info->axes_length; i++) {
+ RawGamepadAxis* axis = &gamepad_info->axes[i];
+
+ // If the min is < 0 we have to query the scaled value, otherwise we need
+ // the normal unscaled value.
+ if (axis->caps.LogicalMin < 0) {
+ status = hidp_get_scaled_usage_value_(HidP_Input, axis->caps.UsagePage, 0,
+ axis->caps.Range.UsageMin, &scaled_axis_value,
+ gamepad_info->preparsed_data,
+ reinterpret_cast<PCHAR>(input->data.hid.bRawData),
+ input->data.hid.dwSizeHid);
+ if (status == HIDP_STATUS_SUCCESS) {
+ axis->value = NormalizeAxis(scaled_axis_value,
+ axis->caps.LogicalMin, axis->caps.LogicalMax);
+ }
+ } else {
+ status = hidp_get_usage_value_(HidP_Input, axis->caps.UsagePage, 0,
+ axis->caps.Range.UsageMin, &axis_value,
+ gamepad_info->preparsed_data,
+ reinterpret_cast<PCHAR>(input->data.hid.bRawData),
+ input->data.hid.dwSizeHid);
+ if (status == HIDP_STATUS_SUCCESS) {
+ axis->value = NormalizeAxis(axis_value,
+ axis->caps.LogicalMin, axis->caps.LogicalMax);
+ }
+ }
+ }
+}
+
+LRESULT RawInputDataFetcher::OnInput(HRAWINPUT input_handle) {
+ // Get the size of the input record.
+ UINT size = 0;
+ UINT result = GetRawInputData(
+ input_handle, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER));
+ if (result == static_cast<UINT>(-1)) {
+ PLOG(ERROR) << "GetRawInputData() failed";
+ return 0;
+ }
+ DCHECK_EQ(0u, result);
+
+ // Retrieve the input record.
+ scoped_ptr<uint8[]> buffer(new uint8[size]);
+ RAWINPUT* input = reinterpret_cast<RAWINPUT*>(buffer.get());
+ result = GetRawInputData(
+ input_handle, RID_INPUT, buffer.get(), &size, sizeof(RAWINPUTHEADER));
+ if (result == static_cast<UINT>(-1)) {
+ PLOG(ERROR) << "GetRawInputData() failed";
+ return 0;
+ }
+ DCHECK_EQ(size, result);
+
+ // Notify the observer about events generated locally.
+ if (input->header.dwType == RIM_TYPEHID && input->header.hDevice != NULL) {
+ RawGamepadInfo* gamepad = GetGamepadInfo(input->header.hDevice);
+ if (gamepad)
+ UpdateGamepad(input, gamepad);
+ }
+
+ return DefRawInputProc(&input, 1, sizeof(RAWINPUTHEADER));
+}
+
+bool RawInputDataFetcher::HandleMessage(UINT message,
+ WPARAM wparam,
+ LPARAM lparam,
+ LRESULT* result) {
+ switch (message) {
+ case WM_INPUT:
+ *result = OnInput(reinterpret_cast<HRAWINPUT>(lparam));
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool RawInputDataFetcher::GetHidDllFunctions() {
+ hidp_get_caps_ = NULL;
+ hidp_get_button_caps_ = NULL;
+ hidp_get_value_caps_ = NULL;
+ hidp_get_usages_ex_ = NULL;
+ hidp_get_usage_value_ = NULL;
+ hidp_get_scaled_usage_value_ = NULL;
+ hidd_get_product_string_ = NULL;
+
+ if (!hid_dll_.is_valid()) return false;
+
+ hidp_get_caps_ = reinterpret_cast<HidPGetCapsFunc>(
+ hid_dll_.GetFunctionPointer("HidP_GetCaps"));
+ if (!hidp_get_caps_)
+ return false;
+ hidp_get_button_caps_ = reinterpret_cast<HidPGetButtonCapsFunc>(
+ hid_dll_.GetFunctionPointer("HidP_GetButtonCaps"));
+ if (!hidp_get_button_caps_)
+ return false;
+ hidp_get_value_caps_ = reinterpret_cast<HidPGetValueCapsFunc>(
+ hid_dll_.GetFunctionPointer("HidP_GetValueCaps"));
+ if (!hidp_get_value_caps_)
+ return false;
+ hidp_get_usages_ex_ = reinterpret_cast<HidPGetUsagesExFunc>(
+ hid_dll_.GetFunctionPointer("HidP_GetUsagesEx"));
+ if (!hidp_get_usages_ex_)
+ return false;
+ hidp_get_usage_value_ = reinterpret_cast<HidPGetUsageValueFunc>(
+ hid_dll_.GetFunctionPointer("HidP_GetUsageValue"));
+ if (!hidp_get_usage_value_)
+ return false;
+ hidp_get_scaled_usage_value_ = reinterpret_cast<HidPGetScaledUsageValueFunc>(
+ hid_dll_.GetFunctionPointer("HidP_GetScaledUsageValue"));
+ if (!hidp_get_scaled_usage_value_)
+ return false;
+ hidd_get_product_string_ = reinterpret_cast<HidDGetStringFunc>(
+ hid_dll_.GetFunctionPointer("HidD_GetProductString"));
+ if (!hidd_get_product_string_)
+ return false;
+
+ return true;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/gamepad/raw_input_data_fetcher_win.h b/chromium/content/browser/gamepad/raw_input_data_fetcher_win.h
new file mode 100644
index 00000000000..e5a6edd507b
--- /dev/null
+++ b/chromium/content/browser/gamepad/raw_input_data_fetcher_win.h
@@ -0,0 +1,139 @@
+// 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_GAMEPAD_RAW_INPUT_DATA_FETCHER_WIN_H_
+#define CONTENT_BROWSER_GAMEPAD_RAW_INPUT_DATA_FETCHER_WIN_H_
+
+#include "build/build_config.h"
+
+#include <stdlib.h>
+#include <Unknwn.h>
+#include <WinDef.h>
+#include <windows.h>
+
+#include <hidsdi.h>
+#include <map>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/scoped_native_library.h"
+#include "base/win/message_window.h"
+#include "content/browser/gamepad/gamepad_data_fetcher.h"
+#include "content/browser/gamepad/gamepad_standard_mappings.h"
+#include "third_party/WebKit/public/platform/WebGamepads.h"
+
+namespace content {
+
+struct RawGamepadAxis {
+ HIDP_VALUE_CAPS caps;
+ float value;
+ bool active;
+};
+
+struct RawGamepadInfo {
+ HANDLE handle;
+ scoped_ptr<uint8[]> ppd_buffer;
+ PHIDP_PREPARSED_DATA preparsed_data;
+
+ uint32_t report_id;
+ uint32_t vendor_id;
+ uint32_t product_id;
+
+ wchar_t id[blink::WebGamepad::idLengthCap];
+
+ uint32_t buttons_length;
+ bool buttons[blink::WebGamepad::buttonsLengthCap];
+
+ uint32_t axes_length;
+ RawGamepadAxis axes[blink::WebGamepad::axesLengthCap];
+};
+
+class RawInputDataFetcher
+ : public base::SupportsWeakPtr<RawInputDataFetcher>,
+ public base::MessageLoop::DestructionObserver {
+ public:
+ explicit RawInputDataFetcher();
+ ~RawInputDataFetcher();
+
+ // DestructionObserver overrides.
+ virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
+
+ bool Available() { return rawinput_available_; }
+ void StartMonitor();
+ void StopMonitor();
+
+ std::vector<RawGamepadInfo*> EnumerateDevices();
+ RawGamepadInfo* GetGamepadInfo(HANDLE handle);
+
+ private:
+ RawGamepadInfo* ParseGamepadInfo(HANDLE hDevice);
+ void UpdateGamepad(RAWINPUT* input, RawGamepadInfo* gamepad_info);
+ // Handles WM_INPUT messages.
+ LRESULT OnInput(HRAWINPUT input_handle);
+ // Handles messages received by |window_|.
+ bool HandleMessage(UINT message,
+ WPARAM wparam,
+ LPARAM lparam,
+ LRESULT* result);
+ RAWINPUTDEVICE* GetRawInputDevices(DWORD flags);
+ void ClearControllers();
+
+ // Function types we use from hid.dll.
+ typedef NTSTATUS (__stdcall *HidPGetCapsFunc)(
+ PHIDP_PREPARSED_DATA PreparsedData, PHIDP_CAPS Capabilities);
+ typedef NTSTATUS (__stdcall *HidPGetButtonCapsFunc)(
+ HIDP_REPORT_TYPE ReportType, PHIDP_BUTTON_CAPS ButtonCaps,
+ PUSHORT ButtonCapsLength, PHIDP_PREPARSED_DATA PreparsedData);
+ typedef NTSTATUS (__stdcall *HidPGetValueCapsFunc)(
+ HIDP_REPORT_TYPE ReportType, PHIDP_VALUE_CAPS ValueCaps,
+ PUSHORT ValueCapsLength, PHIDP_PREPARSED_DATA PreparsedData);
+ typedef NTSTATUS(__stdcall* HidPGetUsagesExFunc)(
+ HIDP_REPORT_TYPE ReportType,
+ USHORT LinkCollection,
+ PUSAGE_AND_PAGE ButtonList,
+ ULONG* UsageLength,
+ PHIDP_PREPARSED_DATA PreparsedData,
+ PCHAR Report,
+ ULONG ReportLength);
+ typedef NTSTATUS (__stdcall *HidPGetUsageValueFunc)(
+ HIDP_REPORT_TYPE ReportType, USAGE UsagePage, USHORT LinkCollection,
+ USAGE Usage, PULONG UsageValue, PHIDP_PREPARSED_DATA PreparsedData,
+ PCHAR Report, ULONG ReportLength);
+ typedef NTSTATUS (__stdcall *HidPGetScaledUsageValueFunc)(
+ HIDP_REPORT_TYPE ReportType, USAGE UsagePage, USHORT LinkCollection,
+ USAGE Usage, PLONG UsageValue, PHIDP_PREPARSED_DATA PreparsedData,
+ PCHAR Report, ULONG ReportLength);
+ typedef BOOLEAN (__stdcall *HidDGetStringFunc)(
+ HANDLE HidDeviceObject, PVOID Buffer, ULONG BufferLength);
+
+ // Get functions from dynamically loaded hid.dll. Returns true if loading was
+ // successful.
+ bool GetHidDllFunctions();
+
+ base::ScopedNativeLibrary hid_dll_;
+ scoped_ptr<base::win::MessageWindow> window_;
+ bool rawinput_available_;
+ bool filter_xinput_;
+ bool events_monitored_;
+
+ std::map<HANDLE, RawGamepadInfo*> controllers_;
+
+ // Function pointers to HID functionality, retrieved in
+ // |GetHidDllFunctions|.
+ HidPGetCapsFunc hidp_get_caps_;
+ HidPGetButtonCapsFunc hidp_get_button_caps_;
+ HidPGetValueCapsFunc hidp_get_value_caps_;
+ HidPGetUsagesExFunc hidp_get_usages_ex_;
+ HidPGetUsageValueFunc hidp_get_usage_value_;
+ HidPGetScaledUsageValueFunc hidp_get_scaled_usage_value_;
+ HidDGetStringFunc hidd_get_product_string_;
+
+ DISALLOW_COPY_AND_ASSIGN(RawInputDataFetcher);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_GAMEPAD_RAW_INPUT_DATA_FETCHER_WIN_H_
diff --git a/chromium/content/browser/gamepad/xbox_data_fetcher_mac.cc b/chromium/content/browser/gamepad/xbox_data_fetcher_mac.cc
index 6619b9b48f7..0626f94212c 100644
--- a/chromium/content/browser/gamepad/xbox_data_fetcher_mac.cc
+++ b/chromium/content/browser/gamepad/xbox_data_fetcher_mac.cc
@@ -19,10 +19,14 @@
namespace {
const int kVendorMicrosoft = 0x045e;
-const int kProduct360Controller = 0x028e;
+const int kProductXbox360Controller = 0x028e;
+const int kProductXboxOneController = 0x02d1;
-const int kReadEndpoint = 1;
-const int kControlEndpoint = 2;
+const int kXbox360ReadEndpoint = 1;
+const int kXbox360ControlEndpoint = 2;
+
+const int kXboxOneReadEndpoint = 2;
+const int kXboxOneControlEndpoint = 1;
enum {
STATUS_MESSAGE_BUTTONS = 0,
@@ -35,12 +39,16 @@ enum {
};
enum {
+ XBOX_ONE_STATUS_MESSAGE_BUTTONS = 0x20,
+};
+
+enum {
CONTROL_MESSAGE_SET_RUMBLE = 0,
CONTROL_MESSAGE_SET_LED = 1,
};
#pragma pack(push, 1)
-struct ButtonData {
+struct Xbox360ButtonData {
bool dpad_up : 1;
bool dpad_down : 1;
bool dpad_left : 1;
@@ -73,15 +81,48 @@ struct ButtonData {
uint32 dummy2;
uint16 dummy3;
};
+
+struct XboxOneButtonData {
+ bool sync : 1;
+ bool dummy1 : 1; // Always 0.
+ bool start : 1;
+ bool back : 1;
+
+ bool a : 1;
+ bool b : 1;
+ bool x : 1;
+ bool y : 1;
+
+ bool dpad_up : 1;
+ bool dpad_down : 1;
+ bool dpad_left : 1;
+ bool dpad_right : 1;
+
+ bool bumper_left : 1;
+ bool bumper_right : 1;
+ bool stick_left_click : 1;
+ bool stick_right_click : 1;
+
+ uint16 trigger_left;
+ uint16 trigger_right;
+
+ int16 stick_left_x;
+ int16 stick_left_y;
+ int16 stick_right_x;
+ int16 stick_right_y;
+};
#pragma pack(pop)
-COMPILE_ASSERT(sizeof(ButtonData) == 0x12, xbox_button_data_wrong_size);
+COMPILE_ASSERT(sizeof(Xbox360ButtonData) == 18, xbox_button_data_wrong_size);
+COMPILE_ASSERT(sizeof(XboxOneButtonData) == 14, xbox_button_data_wrong_size);
// From MSDN:
// http://msdn.microsoft.com/en-us/library/windows/desktop/ee417001(v=vs.85).aspx#dead_zone
const int16 kLeftThumbDeadzone = 7849;
const int16 kRightThumbDeadzone = 8689;
-const uint8 kTriggerDeadzone = 30;
+const uint8 kXbox360TriggerDeadzone = 30;
+const uint16 kXboxOneTriggerMax = 1023;
+const uint16 kXboxOneTriggerDeadzone = 120;
void NormalizeAxis(int16 x,
int16 y,
@@ -117,13 +158,19 @@ void NormalizeAxis(int16 x,
}
float NormalizeTrigger(uint8 value) {
- return value < kTriggerDeadzone ? 0 :
- static_cast<float>(value - kTriggerDeadzone) /
- (std::numeric_limits<uint8>::max() - kTriggerDeadzone);
+ return value < kXbox360TriggerDeadzone ? 0 :
+ static_cast<float>(value - kXbox360TriggerDeadzone) /
+ (std::numeric_limits<uint8>::max() - kXbox360TriggerDeadzone);
}
-void NormalizeButtonData(const ButtonData& data,
- XboxController::Data* normalized_data) {
+float NormalizeXboxOneTrigger(uint16 value) {
+ return value < kXboxOneTriggerDeadzone ? 0 :
+ static_cast<float>(value - kXboxOneTriggerDeadzone) /
+ (kXboxOneTriggerMax - kXboxOneTriggerDeadzone);
+}
+
+void NormalizeXbox360ButtonData(const Xbox360ButtonData& data,
+ XboxController::Data* normalized_data) {
normalized_data->buttons[0] = data.a;
normalized_data->buttons[1] = data.b;
normalized_data->buttons[2] = data.x;
@@ -153,6 +200,37 @@ void NormalizeButtonData(const ButtonData& data,
&normalized_data->axes[3]);
}
+void NormalizeXboxOneButtonData(const XboxOneButtonData& data,
+ XboxController::Data* normalized_data) {
+ normalized_data->buttons[0] = data.a;
+ normalized_data->buttons[1] = data.b;
+ normalized_data->buttons[2] = data.x;
+ normalized_data->buttons[3] = data.y;
+ normalized_data->buttons[4] = data.bumper_left;
+ normalized_data->buttons[5] = data.bumper_right;
+ normalized_data->buttons[6] = data.back;
+ normalized_data->buttons[7] = data.start;
+ normalized_data->buttons[8] = data.stick_left_click;
+ normalized_data->buttons[9] = data.stick_right_click;
+ normalized_data->buttons[10] = data.dpad_up;
+ normalized_data->buttons[11] = data.dpad_down;
+ normalized_data->buttons[12] = data.dpad_left;
+ normalized_data->buttons[13] = data.dpad_right;
+ normalized_data->buttons[14] = data.sync;
+ normalized_data->triggers[0] = NormalizeXboxOneTrigger(data.trigger_left);
+ normalized_data->triggers[1] = NormalizeXboxOneTrigger(data.trigger_right);
+ NormalizeAxis(data.stick_left_x,
+ data.stick_left_y,
+ kLeftThumbDeadzone,
+ &normalized_data->axes[0],
+ &normalized_data->axes[1]);
+ NormalizeAxis(data.stick_right_x,
+ data.stick_right_y,
+ kRightThumbDeadzone,
+ &normalized_data->axes[2],
+ &normalized_data->axes[3]);
+}
+
} // namespace
XboxController::XboxController(Delegate* delegate)
@@ -163,7 +241,10 @@ XboxController::XboxController(Delegate* delegate)
read_buffer_size_(0),
led_pattern_(LED_NUM_PATTERNS),
location_id_(0),
- delegate_(delegate) {
+ delegate_(delegate),
+ controller_type_(UNKNOWN_CONTROLLER),
+ read_endpoint_(0),
+ control_endpoint_(0) {
}
XboxController::~XboxController() {
@@ -197,14 +278,37 @@ bool XboxController::OpenDevice(io_service_t service) {
UInt16 vendor_id;
kr = (*device_)->GetDeviceVendor(device_, &vendor_id);
- if (kr != KERN_SUCCESS)
+ if (kr != KERN_SUCCESS || vendor_id != kVendorMicrosoft)
return false;
+
UInt16 product_id;
kr = (*device_)->GetDeviceProduct(device_, &product_id);
if (kr != KERN_SUCCESS)
return false;
- if (vendor_id != kVendorMicrosoft || product_id != kProduct360Controller)
- return false;
+
+ IOUSBFindInterfaceRequest request;
+ switch (product_id) {
+ case kProductXbox360Controller:
+ controller_type_ = XBOX_360_CONTROLLER;
+ read_endpoint_ = kXbox360ReadEndpoint;
+ control_endpoint_ = kXbox360ControlEndpoint;
+ request.bInterfaceClass = 255;
+ request.bInterfaceSubClass = 93;
+ request.bInterfaceProtocol = 1;
+ request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
+ break;
+ case kProductXboxOneController:
+ controller_type_ = XBOX_ONE_CONTROLLER;
+ read_endpoint_ = kXboxOneReadEndpoint;
+ control_endpoint_ = kXboxOneControlEndpoint;
+ request.bInterfaceClass = 255;
+ request.bInterfaceSubClass = 71;
+ request.bInterfaceProtocol = 208;
+ request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
+ break;
+ default:
+ return false;
+ }
// Open the device and configure it.
kr = (*device_)->USBDeviceOpen(device_);
@@ -236,11 +340,6 @@ bool XboxController::OpenDevice(io_service_t service) {
//
// For more detail, see
// https://github.com/Grumbel/xboxdrv/blob/master/PROTOCOL
- IOUSBFindInterfaceRequest request;
- request.bInterfaceClass = 255;
- request.bInterfaceSubClass = 93;
- request.bInterfaceProtocol = 1;
- request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
io_iterator_t iter;
kr = (*device_)->CreateInterfaceIterator(device_, &request, &iter);
if (kr != KERN_SUCCESS)
@@ -318,19 +417,20 @@ bool XboxController::OpenDevice(io_service_t service) {
&transfer_type,
&max_packet_size,
&interval);
- if (kr != KERN_SUCCESS || transfer_type != kUSBInterrupt)
+ if (kr != KERN_SUCCESS || transfer_type != kUSBInterrupt) {
return false;
- if (i == kReadEndpoint) {
+ }
+ if (i == read_endpoint_) {
if (direction != kUSBIn)
return false;
- if (max_packet_size > 32)
- return false;
read_buffer_.reset(new uint8[max_packet_size]);
read_buffer_size_ = max_packet_size;
QueueRead();
- } else if (i == kControlEndpoint) {
+ } else if (i == control_endpoint_) {
if (direction != kUSBOut)
return false;
+ if (controller_type_ == XBOX_ONE_CONTROLLER)
+ WriteXboxOneInit();
}
}
@@ -355,7 +455,7 @@ void XboxController::SetLEDPattern(LEDPattern pattern) {
buffer[1] = length;
buffer[2] = static_cast<UInt8>(pattern);
kern_return_t kr = (*interface_)->WritePipeAsync(interface_,
- kControlEndpoint,
+ control_endpoint_,
buffer,
(UInt32)length,
WriteComplete,
@@ -372,7 +472,14 @@ int XboxController::GetVendorId() const {
}
int XboxController::GetProductId() const {
- return kProduct360Controller;
+ if (controller_type_ == XBOX_360_CONTROLLER)
+ return kProductXbox360Controller;
+ else
+ return kProductXboxOneController;
+}
+
+XboxController::ControllerType XboxController::GetControllerType() const {
+ return controller_type_;
}
void XboxController::WriteComplete(void* context, IOReturn result, void* arg0) {
@@ -397,14 +504,18 @@ void XboxController::GotData(void* context, IOReturn result, void* arg0) {
return;
}
- controller->ProcessPacket(bytes_read);
+ if (controller->GetControllerType() == XBOX_360_CONTROLLER)
+ controller->ProcessXbox360Packet(bytes_read);
+ else
+ controller->ProcessXboxOnePacket(bytes_read);
// Queue up another read.
controller->QueueRead();
}
-void XboxController::ProcessPacket(size_t length) {
- if (length < 2) return;
+void XboxController::ProcessXbox360Packet(size_t length) {
+ if (length < 2)
+ return;
DCHECK(length <= read_buffer_size_);
if (length > read_buffer_size_) {
IOError();
@@ -421,11 +532,11 @@ void XboxController::ProcessPacket(size_t length) {
length -= 2;
switch (type) {
case STATUS_MESSAGE_BUTTONS: {
- if (length != sizeof(ButtonData))
+ if (length != sizeof(Xbox360ButtonData))
return;
- ButtonData* data = reinterpret_cast<ButtonData*>(buffer);
+ Xbox360ButtonData* data = reinterpret_cast<Xbox360ButtonData*>(buffer);
Data normalized_data;
- NormalizeButtonData(*data, &normalized_data);
+ NormalizeXbox360ButtonData(*data, &normalized_data);
delegate_->XboxControllerGotData(this, normalized_data);
break;
}
@@ -443,9 +554,38 @@ void XboxController::ProcessPacket(size_t length) {
}
}
+void XboxController::ProcessXboxOnePacket(size_t length) {
+ if (length < 2)
+ return;
+ DCHECK(length <= read_buffer_size_);
+ if (length > read_buffer_size_) {
+ IOError();
+ return;
+ }
+ uint8* buffer = read_buffer_.get();
+
+ uint8 type = buffer[0];
+ buffer += 4;
+ length -= 4;
+ switch (type) {
+ case XBOX_ONE_STATUS_MESSAGE_BUTTONS: {
+ if (length != sizeof(XboxOneButtonData))
+ return;
+ XboxOneButtonData* data = reinterpret_cast<XboxOneButtonData*>(buffer);
+ Data normalized_data;
+ NormalizeXboxOneButtonData(*data, &normalized_data);
+ delegate_->XboxControllerGotData(this, normalized_data);
+ break;
+ }
+ default:
+ // Unknown packet: ignore!
+ break;
+ }
+}
+
void XboxController::QueueRead() {
kern_return_t kr = (*interface_)->ReadPipeAsync(interface_,
- kReadEndpoint,
+ read_endpoint_,
read_buffer_.get(),
read_buffer_size_,
GotData,
@@ -458,6 +598,27 @@ void XboxController::IOError() {
delegate_->XboxControllerError(this);
}
+void XboxController::WriteXboxOneInit() {
+ const UInt8 length = 2;
+
+ // This buffer will be released in WriteComplete when WritePipeAsync
+ // finishes.
+ UInt8* buffer = new UInt8[length];
+ buffer[0] = 0x05;
+ buffer[1] = 0x20;
+ kern_return_t kr = (*interface_)->WritePipeAsync(interface_,
+ control_endpoint_,
+ buffer,
+ (UInt32)length,
+ WriteComplete,
+ buffer);
+ if (kr != KERN_SUCCESS) {
+ delete[] buffer;
+ IOError();
+ return;
+ }
+}
+
//-----------------------------------------------------------------------------
XboxDataFetcher::XboxDataFetcher(Delegate* delegate)
@@ -510,16 +671,6 @@ void XboxDataFetcher::DeviceRemoved(void* context, io_iterator_t iterator) {
bool XboxDataFetcher::RegisterForNotifications() {
if (listening_)
return true;
- base::ScopedCFTypeRef<CFNumberRef> vendor_cf(CFNumberCreate(
- kCFAllocatorDefault, kCFNumberSInt32Type, &kVendorMicrosoft));
- base::ScopedCFTypeRef<CFNumberRef> product_cf(CFNumberCreate(
- kCFAllocatorDefault, kCFNumberSInt32Type, &kProduct360Controller));
- base::ScopedCFTypeRef<CFMutableDictionaryRef> matching_dict(
- IOServiceMatching(kIOUSBDeviceClassName));
- if (!matching_dict)
- return false;
- CFDictionarySetValue(matching_dict, CFSTR(kUSBVendorID), vendor_cf);
- CFDictionarySetValue(matching_dict, CFSTR(kUSBProductID), product_cf);
port_ = IONotificationPortCreate(kIOMasterPortDefault);
if (!port_)
return false;
@@ -530,6 +681,37 @@ bool XboxDataFetcher::RegisterForNotifications() {
listening_ = true;
+ if (!RegisterForDeviceNotifications(
+ kVendorMicrosoft, kProductXboxOneController,
+ &xbox_one_device_added_iter_,
+ &xbox_one_device_removed_iter_))
+ return false;
+
+ if (!RegisterForDeviceNotifications(
+ kVendorMicrosoft, kProductXbox360Controller,
+ &xbox_360_device_added_iter_,
+ &xbox_360_device_removed_iter_))
+ return false;
+
+ return true;
+}
+
+bool XboxDataFetcher::RegisterForDeviceNotifications(
+ int vendor_id,
+ int product_id,
+ base::mac::ScopedIOObject<io_iterator_t>* added_iter,
+ base::mac::ScopedIOObject<io_iterator_t>* removed_iter) {
+ base::ScopedCFTypeRef<CFNumberRef> vendor_cf(CFNumberCreate(
+ kCFAllocatorDefault, kCFNumberSInt32Type, &vendor_id));
+ base::ScopedCFTypeRef<CFNumberRef> product_cf(CFNumberCreate(
+ kCFAllocatorDefault, kCFNumberSInt32Type, &product_id));
+ base::ScopedCFTypeRef<CFMutableDictionaryRef> matching_dict(
+ IOServiceMatching(kIOUSBDeviceClassName));
+ if (!matching_dict)
+ return false;
+ CFDictionarySetValue(matching_dict, CFSTR(kUSBVendorID), vendor_cf);
+ CFDictionarySetValue(matching_dict, CFSTR(kUSBProductID), product_cf);
+
// IOServiceAddMatchingNotification() releases the dictionary when it's done.
// Retain it before each call to IOServiceAddMatchingNotification to keep
// things balanced.
@@ -542,12 +724,12 @@ bool XboxDataFetcher::RegisterForNotifications() {
DeviceAdded,
this,
&device_added_iter);
- device_added_iter_.reset(device_added_iter);
+ added_iter->reset(device_added_iter);
if (ret != kIOReturnSuccess) {
LOG(ERROR) << "Error listening for Xbox controller add events: " << ret;
return false;
}
- DeviceAdded(this, device_added_iter_.get());
+ DeviceAdded(this, added_iter->get());
CFRetain(matching_dict);
io_iterator_t device_removed_iter;
@@ -557,12 +739,12 @@ bool XboxDataFetcher::RegisterForNotifications() {
DeviceRemoved,
this,
&device_removed_iter);
- device_removed_iter_.reset(device_removed_iter);
+ removed_iter->reset(device_removed_iter);
if (ret != kIOReturnSuccess) {
LOG(ERROR) << "Error listening for Xbox controller remove events: " << ret;
return false;
}
- DeviceRemoved(this, device_removed_iter_.get());
+ DeviceRemoved(this, removed_iter->get());
return true;
}
diff --git a/chromium/content/browser/gamepad/xbox_data_fetcher_mac.h b/chromium/content/browser/gamepad/xbox_data_fetcher_mac.h
index ca8f7fcd88c..99a68f209bd 100644
--- a/chromium/content/browser/gamepad/xbox_data_fetcher_mac.h
+++ b/chromium/content/browser/gamepad/xbox_data_fetcher_mac.h
@@ -18,6 +18,12 @@
class XboxController {
public:
+ enum ControllerType {
+ UNKNOWN_CONTROLLER,
+ XBOX_360_CONTROLLER,
+ XBOX_ONE_CONTROLLER
+ };
+
enum LEDPattern {
LED_OFF = 0,
@@ -77,16 +83,20 @@ class XboxController {
UInt32 location_id() { return location_id_; }
int GetVendorId() const;
int GetProductId() const;
+ ControllerType GetControllerType() const;
private:
static void WriteComplete(void* context, IOReturn result, void* arg0);
static void GotData(void* context, IOReturn result, void* arg0);
- void ProcessPacket(size_t length);
+ void ProcessXbox360Packet(size_t length);
+ void ProcessXboxOnePacket(size_t length);
void QueueRead();
void IOError();
+ void WriteXboxOneInit();
+
// Handle for the USB device. IOUSBDeviceStruct320 is the latest version of
// the device API that is supported on Mac OS 10.6.
base::mac::ScopedIOPluginInterface<struct IOUSBDeviceStruct320> device_;
@@ -117,6 +127,10 @@ class XboxController {
Delegate* delegate_;
+ ControllerType controller_type_;
+ int read_endpoint_;
+ int control_endpoint_;
+
DISALLOW_COPY_AND_ASSIGN(XboxController);
};
@@ -134,6 +148,11 @@ class XboxDataFetcher : public XboxController::Delegate {
virtual ~XboxDataFetcher();
bool RegisterForNotifications();
+ bool RegisterForDeviceNotifications(
+ int vendor_id,
+ int product_id,
+ base::mac::ScopedIOObject<io_iterator_t>* added_iter,
+ base::mac::ScopedIOObject<io_iterator_t>* removed_iter);
void UnregisterFromNotifications();
XboxController* ControllerForLocation(UInt32 location_id);
@@ -158,8 +177,10 @@ class XboxDataFetcher : public XboxController::Delegate {
// do need to maintain a reference to it so we can invalidate it.
CFRunLoopSourceRef source_;
IONotificationPortRef port_;
- base::mac::ScopedIOObject<io_iterator_t> device_added_iter_;
- base::mac::ScopedIOObject<io_iterator_t> device_removed_iter_;
+ base::mac::ScopedIOObject<io_iterator_t> xbox_360_device_added_iter_;
+ base::mac::ScopedIOObject<io_iterator_t> xbox_360_device_removed_iter_;
+ base::mac::ScopedIOObject<io_iterator_t> xbox_one_device_added_iter_;
+ base::mac::ScopedIOObject<io_iterator_t> xbox_one_device_removed_iter_;
DISALLOW_COPY_AND_ASSIGN(XboxDataFetcher);
};
diff --git a/chromium/content/browser/geolocation/geolocation_dispatcher_host.cc b/chromium/content/browser/geolocation/geolocation_dispatcher_host.cc
index e461e63464d..ad1f2fcc444 100644
--- a/chromium/content/browser/geolocation/geolocation_dispatcher_host.cc
+++ b/chromium/content/browser/geolocation/geolocation_dispatcher_host.cc
@@ -4,140 +4,115 @@
#include "content/browser/geolocation/geolocation_dispatcher_host.h"
-#include <map>
-#include <set>
#include <utility>
#include "base/bind.h"
#include "base/metrics/histogram.h"
-#include "content/browser/geolocation/geolocation_provider_impl.h"
+#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/browser/renderer_host/render_message_filter.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
-#include "content/public/browser/geolocation_permission_context.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/content_browser_client.h"
#include "content/public/common/geoposition.h"
#include "content/common/geolocation_messages.h"
namespace content {
namespace {
-void NotifyGeolocationProviderPermissionGranted() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- GeolocationProviderImpl::GetInstance()->UserDidOptIntoLocationServices();
-}
-
-void SendGeolocationPermissionResponse(int render_process_id,
- int render_view_id,
- int bridge_id,
- bool allowed) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- RenderViewHostImpl* render_view_host =
- RenderViewHostImpl::FromID(render_process_id, render_view_id);
- if (!render_view_host)
- return;
- render_view_host->Send(
- new GeolocationMsg_PermissionSet(render_view_id, bridge_id, allowed));
-
- if (allowed) {
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::Bind(&NotifyGeolocationProviderPermissionGranted));
- }
-}
-
-class GeolocationDispatcherHostImpl : public GeolocationDispatcherHost {
- public:
- GeolocationDispatcherHostImpl(
- int render_process_id,
- GeolocationPermissionContext* geolocation_permission_context);
-
- // GeolocationDispatcherHost
- virtual bool OnMessageReceived(const IPC::Message& msg,
- bool* msg_was_ok) OVERRIDE;
+// Geoposition error codes for reporting in UMA.
+enum GeopositionErrorCode {
+ // NOTE: Do not renumber these as that would confuse interpretation of
+ // previously logged data. When making changes, also update the enum list
+ // in tools/metrics/histograms/histograms.xml to keep it in sync.
- private:
- virtual ~GeolocationDispatcherHostImpl();
+ // There was no error.
+ GEOPOSITION_ERROR_CODE_NONE = 0,
- void OnRequestPermission(int render_view_id,
- int bridge_id,
- const GURL& requesting_frame);
- void OnCancelPermissionRequest(int render_view_id,
- int bridge_id,
- const GURL& requesting_frame);
- void OnStartUpdating(int render_view_id,
- const GURL& requesting_frame,
- bool enable_high_accuracy);
- void OnStopUpdating(int render_view_id);
+ // User denied use of geolocation.
+ GEOPOSITION_ERROR_CODE_PERMISSION_DENIED = 1,
+ // Geoposition could not be determined.
+ GEOPOSITION_ERROR_CODE_POSITION_UNAVAILABLE = 2,
- virtual void PauseOrResume(int render_view_id, bool should_pause) OVERRIDE;
+ // Timeout.
+ GEOPOSITION_ERROR_CODE_TIMEOUT = 3,
- // Updates the |geolocation_provider_| with the currently required update
- // options.
- void RefreshGeolocationOptions();
-
- void OnLocationUpdate(const Geoposition& position);
-
- int render_process_id_;
- scoped_refptr<GeolocationPermissionContext> geolocation_permission_context_;
+ // NOTE: Add entries only immediately above this line.
+ GEOPOSITION_ERROR_CODE_COUNT = 4
+};
- struct RendererGeolocationOptions {
- bool high_accuracy;
- bool is_paused;
- };
+void RecordGeopositionErrorCode(Geoposition::ErrorCode error_code) {
+ GeopositionErrorCode code = GEOPOSITION_ERROR_CODE_NONE;
+ switch (error_code) {
+ case Geoposition::ERROR_CODE_NONE:
+ code = GEOPOSITION_ERROR_CODE_NONE;
+ break;
+ case Geoposition::ERROR_CODE_PERMISSION_DENIED:
+ code = GEOPOSITION_ERROR_CODE_PERMISSION_DENIED;
+ break;
+ case Geoposition::ERROR_CODE_POSITION_UNAVAILABLE:
+ code = GEOPOSITION_ERROR_CODE_POSITION_UNAVAILABLE;
+ break;
+ case Geoposition::ERROR_CODE_TIMEOUT:
+ code = GEOPOSITION_ERROR_CODE_TIMEOUT;
+ break;
+ }
+ UMA_HISTOGRAM_ENUMERATION("Geolocation.LocationUpdate.ErrorCode",
+ code,
+ GEOPOSITION_ERROR_CODE_COUNT);
+}
- // Used to keep track of the renderers in this process that are using
- // geolocation and the options associated with them. The map is iterated
- // when a location update is available and the fan out to individual bridge
- // IDs happens renderer side, in order to minimize context switches.
- // Only used on the IO thread.
- std::map<int, RendererGeolocationOptions> geolocation_renderers_;
+} // namespace
- // Used by Android WebView to support that case that a renderer is in the
- // 'paused' state but not yet using geolocation. If the renderer does start
- // using geolocation while paused, we move from this set into
- // |geolocation_renderers_|. If the renderer doesn't end up wanting to use
- // geolocation while 'paused' then we remove from this set. A renderer id
- // can exist only in this set or |geolocation_renderers_|, never both.
- std::set<int> pending_paused_geolocation_renderers_;
+GeolocationDispatcherHost::PendingPermission::PendingPermission(
+ int render_frame_id,
+ int render_process_id,
+ int bridge_id)
+ : render_frame_id(render_frame_id),
+ render_process_id(render_process_id),
+ bridge_id(bridge_id) {
+}
- // Only set whilst we are registered with the geolocation provider.
- GeolocationProviderImpl* geolocation_provider_;
+GeolocationDispatcherHost::PendingPermission::~PendingPermission() {
+}
- GeolocationProviderImpl::LocationUpdateCallback callback_;
+GeolocationDispatcherHost::GeolocationDispatcherHost(
+ WebContents* web_contents)
+ : WebContentsObserver(web_contents),
+ paused_(false),
+ weak_factory_(this) {
+ // This is initialized by WebContentsImpl. Do not add any non-trivial
+ // initialization here, defer to OnStartUpdating which is triggered whenever
+ // a javascript geolocation object is actually initialized.
+}
- DISALLOW_COPY_AND_ASSIGN(GeolocationDispatcherHostImpl);
-};
+GeolocationDispatcherHost::~GeolocationDispatcherHost() {
+}
-GeolocationDispatcherHostImpl::GeolocationDispatcherHostImpl(
- int render_process_id,
- GeolocationPermissionContext* geolocation_permission_context)
- : render_process_id_(render_process_id),
- geolocation_permission_context_(geolocation_permission_context),
- geolocation_provider_(NULL) {
- callback_ = base::Bind(
- &GeolocationDispatcherHostImpl::OnLocationUpdate, base::Unretained(this));
- // This is initialized by ResourceMessageFilter. Do not add any non-trivial
- // initialization here, defer to OnRegisterBridge which is triggered whenever
- // a javascript geolocation object is actually initialized.
+void GeolocationDispatcherHost::RenderFrameDeleted(
+ RenderFrameHost* render_frame_host) {
+ OnStopUpdating(render_frame_host);
}
-GeolocationDispatcherHostImpl::~GeolocationDispatcherHostImpl() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- if (geolocation_provider_)
- geolocation_provider_->RemoveLocationUpdateCallback(callback_);
+void GeolocationDispatcherHost::RenderViewHostChanged(
+ RenderViewHost* old_host,
+ RenderViewHost* new_host) {
+ updating_frames_.clear();
+ paused_ = false;
+ geolocation_subscription_.reset();
}
-bool GeolocationDispatcherHostImpl::OnMessageReceived(
- const IPC::Message& msg, bool* msg_was_ok) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- *msg_was_ok = true;
+bool GeolocationDispatcherHost::OnMessageReceived(
+ const IPC::Message& msg, RenderFrameHost* render_frame_host) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(GeolocationDispatcherHostImpl, msg, *msg_was_ok)
- IPC_MESSAGE_HANDLER(GeolocationHostMsg_CancelPermissionRequest,
- OnCancelPermissionRequest)
+ IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(GeolocationDispatcherHost, msg,
+ render_frame_host)
IPC_MESSAGE_HANDLER(GeolocationHostMsg_RequestPermission,
OnRequestPermission)
+ IPC_MESSAGE_HANDLER(GeolocationHostMsg_CancelPermissionRequest,
+ OnCancelPermissionRequest)
IPC_MESSAGE_HANDLER(GeolocationHostMsg_StartUpdating, OnStartUpdating)
IPC_MESSAGE_HANDLER(GeolocationHostMsg_StopUpdating, OnStopUpdating)
IPC_MESSAGE_UNHANDLED(handled = false)
@@ -145,160 +120,138 @@ bool GeolocationDispatcherHostImpl::OnMessageReceived(
return handled;
}
-void GeolocationDispatcherHostImpl::OnLocationUpdate(
+void GeolocationDispatcherHost::OnLocationUpdate(
const Geoposition& geoposition) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- for (std::map<int, RendererGeolocationOptions>::iterator it =
- geolocation_renderers_.begin();
- it != geolocation_renderers_.end(); ++it) {
- if (!(it->second.is_paused))
- Send(new GeolocationMsg_PositionUpdated(it->first, geoposition));
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ RecordGeopositionErrorCode(geoposition.error_code);
+ if (paused_)
+ return;
+
+ for (std::map<RenderFrameHost*, bool>::iterator i = updating_frames_.begin();
+ i != updating_frames_.end(); ++i) {
+ i->first->Send(new GeolocationMsg_PositionUpdated(
+ i->first->GetRoutingID(), geoposition));
}
}
-void GeolocationDispatcherHostImpl::OnRequestPermission(
- int render_view_id,
+void GeolocationDispatcherHost::OnRequestPermission(
+ RenderFrameHost* render_frame_host,
int bridge_id,
- const GURL& requesting_frame) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- DVLOG(1) << __FUNCTION__ << " " << render_process_id_ << ":"
- << render_view_id << ":" << bridge_id;
- if (geolocation_permission_context_.get()) {
- geolocation_permission_context_->RequestGeolocationPermission(
- render_process_id_,
- render_view_id,
- bridge_id,
- requesting_frame,
- base::Bind(&SendGeolocationPermissionResponse,
- render_process_id_,
- render_view_id,
- bridge_id));
- } else {
- BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- base::Bind(&SendGeolocationPermissionResponse, render_process_id_,
- render_view_id, bridge_id, true));
- }
+ const GURL& requesting_frame,
+ bool user_gesture) {
+ int render_process_id = render_frame_host->GetProcess()->GetID();
+ int render_frame_id = render_frame_host->GetRoutingID();
+
+ PendingPermission pending_permission(
+ render_frame_id, render_process_id, bridge_id);
+ pending_permissions_.push_back(pending_permission);
+
+ GetContentClient()->browser()->RequestGeolocationPermission(
+ web_contents(),
+ bridge_id,
+ requesting_frame,
+ user_gesture,
+ base::Bind(&GeolocationDispatcherHost::SendGeolocationPermissionResponse,
+ weak_factory_.GetWeakPtr(),
+ render_process_id, render_frame_id, bridge_id),
+ &pending_permissions_.back().cancel);
}
-void GeolocationDispatcherHostImpl::OnCancelPermissionRequest(
- int render_view_id,
+void GeolocationDispatcherHost::OnCancelPermissionRequest(
+ RenderFrameHost* render_frame_host,
int bridge_id,
const GURL& requesting_frame) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- DVLOG(1) << __FUNCTION__ << " " << render_process_id_ << ":"
- << render_view_id << ":" << bridge_id;
- if (geolocation_permission_context_.get()) {
- geolocation_permission_context_->CancelGeolocationPermissionRequest(
- render_process_id_, render_view_id, bridge_id, requesting_frame);
+ int render_process_id = render_frame_host->GetProcess()->GetID();
+ int render_frame_id = render_frame_host->GetRoutingID();
+ for (size_t i = 0; i < pending_permissions_.size(); ++i) {
+ if (pending_permissions_[i].render_process_id == render_process_id &&
+ pending_permissions_[i].render_frame_id == render_frame_id &&
+ pending_permissions_[i].bridge_id == bridge_id) {
+ if (!pending_permissions_[i].cancel.is_null())
+ pending_permissions_[i].cancel.Run();
+ pending_permissions_.erase(pending_permissions_.begin() + i);
+ return;
+ }
}
}
-void GeolocationDispatcherHostImpl::OnStartUpdating(
- int render_view_id,
+void GeolocationDispatcherHost::OnStartUpdating(
+ RenderFrameHost* render_frame_host,
const GURL& requesting_frame,
bool enable_high_accuracy) {
// StartUpdating() can be invoked as a result of high-accuracy mode
// being enabled / disabled. No need to record the dispatcher again.
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- DVLOG(1) << __FUNCTION__ << " " << render_process_id_ << ":"
- << render_view_id;
UMA_HISTOGRAM_BOOLEAN(
"Geolocation.GeolocationDispatcherHostImpl.EnableHighAccuracy",
enable_high_accuracy);
- std::map<int, RendererGeolocationOptions>::iterator it =
- geolocation_renderers_.find(render_view_id);
- if (it == geolocation_renderers_.end()) {
- bool should_start_paused = false;
- if (pending_paused_geolocation_renderers_.erase(render_view_id) == 1) {
- should_start_paused = true;
- }
- RendererGeolocationOptions opts = {
- enable_high_accuracy,
- should_start_paused
- };
- geolocation_renderers_[render_view_id] = opts;
- } else {
- it->second.high_accuracy = enable_high_accuracy;
- }
+ updating_frames_[render_frame_host] = enable_high_accuracy;
RefreshGeolocationOptions();
}
-void GeolocationDispatcherHostImpl::OnStopUpdating(int render_view_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- DVLOG(1) << __FUNCTION__ << " " << render_process_id_ << ":"
- << render_view_id;
- DCHECK_EQ(1U, geolocation_renderers_.count(render_view_id));
- geolocation_renderers_.erase(render_view_id);
+void GeolocationDispatcherHost::OnStopUpdating(
+ RenderFrameHost* render_frame_host) {
+ updating_frames_.erase(render_frame_host);
RefreshGeolocationOptions();
}
-void GeolocationDispatcherHostImpl::PauseOrResume(int render_view_id,
- bool should_pause) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- std::map<int, RendererGeolocationOptions>::iterator it =
- geolocation_renderers_.find(render_view_id);
- if (it == geolocation_renderers_.end()) {
- // This renderer is not using geolocation yet, but if it does before
- // we get a call to resume, we should start it up in the paused state.
- if (should_pause) {
- pending_paused_geolocation_renderers_.insert(render_view_id);
- } else {
- pending_paused_geolocation_renderers_.erase(render_view_id);
- }
- } else {
- RendererGeolocationOptions* opts = &(it->second);
- if (opts->is_paused != should_pause)
- opts->is_paused = should_pause;
- RefreshGeolocationOptions();
- }
+void GeolocationDispatcherHost::PauseOrResume(bool should_pause) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ paused_ = should_pause;
+ RefreshGeolocationOptions();
}
-void GeolocationDispatcherHostImpl::RefreshGeolocationOptions() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-
- bool needs_updates = false;
- bool use_high_accuracy = false;
- std::map<int, RendererGeolocationOptions>::const_iterator i =
- geolocation_renderers_.begin();
- for (; i != geolocation_renderers_.end(); ++i) {
- needs_updates |= !(i->second.is_paused);
- use_high_accuracy |= i->second.high_accuracy;
- if (needs_updates && use_high_accuracy)
- break;
+void GeolocationDispatcherHost::RefreshGeolocationOptions() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ if (updating_frames_.empty() || paused_) {
+ geolocation_subscription_.reset();
+ return;
}
- if (needs_updates) {
- if (!geolocation_provider_)
- geolocation_provider_ = GeolocationProviderImpl::GetInstance();
- // Re-add to re-establish our options, in case they changed.
- geolocation_provider_->AddLocationUpdateCallback(
- callback_, use_high_accuracy);
- } else {
- if (geolocation_provider_)
- geolocation_provider_->RemoveLocationUpdateCallback(callback_);
- geolocation_provider_ = NULL;
+
+ bool high_accuracy = false;
+ for (std::map<RenderFrameHost*, bool>::iterator i =
+ updating_frames_.begin(); i != updating_frames_.end(); ++i) {
+ if (i->second) {
+ high_accuracy = true;
+ break;
+ }
}
+ geolocation_subscription_ = GeolocationProvider::GetInstance()->
+ AddLocationUpdateCallback(
+ base::Bind(&GeolocationDispatcherHost::OnLocationUpdate,
+ base::Unretained(this)),
+ high_accuracy);
}
-} // namespace
-
-
-// GeolocationDispatcherHost --------------------------------------------------
-
-// static
-GeolocationDispatcherHost* GeolocationDispatcherHost::New(
+void GeolocationDispatcherHost::SendGeolocationPermissionResponse(
int render_process_id,
- GeolocationPermissionContext* geolocation_permission_context) {
- return new GeolocationDispatcherHostImpl(
- render_process_id,
- geolocation_permission_context);
-}
-
-GeolocationDispatcherHost::GeolocationDispatcherHost() {
-}
+ int render_frame_id,
+ int bridge_id,
+ bool allowed) {
+ for (size_t i = 0; i < pending_permissions_.size(); ++i) {
+ if (pending_permissions_[i].render_process_id == render_process_id &&
+ pending_permissions_[i].render_frame_id == render_frame_id &&
+ pending_permissions_[i].bridge_id == bridge_id) {
+ RenderFrameHost* render_frame_host =
+ RenderFrameHost::FromID(render_process_id, render_frame_id);
+ if (render_frame_host) {
+ render_frame_host->Send(new GeolocationMsg_PermissionSet(
+ render_frame_id, bridge_id, allowed));
+ }
+
+ if (allowed) {
+ GeolocationProviderImpl::GetInstance()->
+ UserDidOptIntoLocationServices();
+ }
+
+ pending_permissions_.erase(pending_permissions_.begin() + i);
+ return;
+ }
+ }
-GeolocationDispatcherHost::~GeolocationDispatcherHost() {
+ NOTREACHED();
}
} // namespace content
diff --git a/chromium/content/browser/geolocation/geolocation_dispatcher_host.h b/chromium/content/browser/geolocation/geolocation_dispatcher_host.h
index 5620e262f8b..e8bda4026d6 100644
--- a/chromium/content/browser/geolocation/geolocation_dispatcher_host.h
+++ b/chromium/content/browser/geolocation/geolocation_dispatcher_host.h
@@ -5,31 +5,84 @@
#ifndef CONTENT_BROWSER_GEOLOCATION_GEOLOCATION_DISPATCHER_HOST_H_
#define CONTENT_BROWSER_GEOLOCATION_GEOLOCATION_DISPATCHER_HOST_H_
-#include "content/public/browser/browser_message_filter.h"
+#include <map>
+#include <vector>
-namespace content {
+#include "base/callback_forward.h"
+#include "base/memory/weak_ptr.h"
+#include "content/browser/geolocation/geolocation_provider_impl.h"
+#include "content/public/browser/web_contents_observer.h"
+
+class GURL;
-class GeolocationPermissionContext;
+namespace content {
-// GeolocationDispatcherHost is a browser filter for Geolocation messages.
+// GeolocationDispatcherHost is an observer for Geolocation messages.
// It's the complement of GeolocationDispatcher (owned by RenderView).
-class GeolocationDispatcherHost : public BrowserMessageFilter {
+class GeolocationDispatcherHost : public WebContentsObserver {
public:
- static GeolocationDispatcherHost* New(
- int render_process_id,
- GeolocationPermissionContext* geolocation_permission_context);
-
- // Pause or resumes geolocation for the given |render_view_id|. Should
- // be called on the IO thread. Resuming when nothing is paused is a no-op.
- // If a renderer is paused while not currently using geolocation but
- // then goes on to do so before being resumed, then that renderer will
- // not get geolocation updates until it is resumed.
- virtual void PauseOrResume(int render_view_id, bool should_pause) = 0;
-
- protected:
- GeolocationDispatcherHost();
+ explicit GeolocationDispatcherHost(WebContents* web_contents);
virtual ~GeolocationDispatcherHost();
+ // Pause or resumes geolocation. Resuming when nothing is paused is a no-op.
+ // If the web contents is paused while not currently using geolocation but
+ // then goes on to do so before being resumed, then it will not get
+ // geolocation updates until it is resumed.
+ void PauseOrResume(bool should_pause);
+
+ private:
+ // WebContentsObserver
+ virtual void RenderFrameDeleted(RenderFrameHost* render_frame_host) OVERRIDE;
+ virtual void RenderViewHostChanged(RenderViewHost* old_host,
+ RenderViewHost* new_host) OVERRIDE;
+ virtual bool OnMessageReceived(
+ const IPC::Message& msg, RenderFrameHost* render_frame_host) OVERRIDE;
+
+ // Message handlers:
+ void OnRequestPermission(RenderFrameHost* render_frame_host,
+ int bridge_id,
+ const GURL& requesting_frame,
+ bool user_gesture);
+ void OnCancelPermissionRequest(RenderFrameHost* render_frame_host,
+ int bridge_id,
+ const GURL& requesting_frame);
+ void OnStartUpdating(RenderFrameHost* render_frame_host,
+ const GURL& requesting_frame,
+ bool enable_high_accuracy);
+ void OnStopUpdating(RenderFrameHost* render_frame_host);
+
+ // Updates the geolocation provider with the currently required update
+ // options.
+ void RefreshGeolocationOptions();
+
+ void OnLocationUpdate(const Geoposition& position);
+
+ void SendGeolocationPermissionResponse(int render_process_id,
+ int render_frame_id,
+ int bridge_id,
+ bool allowed);
+
+ // A map from the RenderFrameHosts that have requested geolocation updates to
+ // the type of accuracy they requested (true = high accuracy).
+ std::map<RenderFrameHost*, bool> updating_frames_;
+ bool paused_;
+
+ struct PendingPermission {
+ PendingPermission(int render_frame_id,
+ int render_process_id,
+ int bridge_id);
+ ~PendingPermission();
+ int render_frame_id;
+ int render_process_id;
+ int bridge_id;
+ base::Closure cancel;
+ };
+ std::vector<PendingPermission> pending_permissions_;
+
+ scoped_ptr<GeolocationProvider::Subscription> geolocation_subscription_;
+
+ base::WeakPtrFactory<GeolocationDispatcherHost> weak_factory_;
+
DISALLOW_COPY_AND_ASSIGN(GeolocationDispatcherHost);
};
diff --git a/chromium/content/browser/geolocation/geolocation_provider_impl.cc b/chromium/content/browser/geolocation/geolocation_provider_impl.cc
index f343c2920ff..626a52afa4f 100644
--- a/chromium/content/browser/geolocation/geolocation_provider_impl.cc
+++ b/chromium/content/browser/geolocation/geolocation_provider_impl.cc
@@ -16,84 +16,43 @@
namespace content {
-namespace {
-void OverrideLocationForTestingOnIOThread(
- const Geoposition& position,
- const base::Closure& completion_callback,
- scoped_refptr<base::MessageLoopProxy> callback_loop) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- GeolocationProviderImpl::GetInstance()->OverrideLocationForTesting(position);
- callback_loop->PostTask(FROM_HERE, completion_callback);
-}
-} // namespace
-
GeolocationProvider* GeolocationProvider::GetInstance() {
return GeolocationProviderImpl::GetInstance();
}
-void GeolocationProvider::OverrideLocationForTesting(
- const Geoposition& position,
- const base::Closure& completion_callback) {
- base::Closure closure = base::Bind(&OverrideLocationForTestingOnIOThread,
- position,
- completion_callback,
- base::MessageLoopProxy::current());
- if (!BrowserThread::CurrentlyOn(BrowserThread::IO))
- BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, closure);
- else
- closure.Run();
-}
-
-void GeolocationProviderImpl::AddLocationUpdateCallback(
+scoped_ptr<GeolocationProvider::Subscription>
+GeolocationProviderImpl::AddLocationUpdateCallback(
const LocationUpdateCallback& callback, bool use_high_accuracy) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- bool found = false;
- CallbackList::iterator i = callbacks_.begin();
- for (; i != callbacks_.end(); ++i) {
- if (i->first.Equals(callback)) {
- i->second = use_high_accuracy;
- found = true;
- break;
- }
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ scoped_ptr<GeolocationProvider::Subscription> subscription;
+ if (use_high_accuracy) {
+ subscription = high_accuracy_callbacks_.Add(callback);
+ } else {
+ subscription = low_accuracy_callbacks_.Add(callback);
}
- if (!found)
- callbacks_.push_back(std::make_pair(callback, use_high_accuracy));
OnClientsChanged();
if (position_.Validate() ||
position_.error_code != Geoposition::ERROR_CODE_NONE) {
callback.Run(position_);
}
-}
-bool GeolocationProviderImpl::RemoveLocationUpdateCallback(
- const LocationUpdateCallback& callback) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- bool removed = false;
- CallbackList::iterator i = callbacks_.begin();
- for (; i != callbacks_.end(); ++i) {
- if (i->first.Equals(callback)) {
- callbacks_.erase(i);
- removed = true;
- break;
- }
- }
- if (removed)
- OnClientsChanged();
- return removed;
+ return subscription.Pass();
}
void GeolocationProviderImpl::UserDidOptIntoLocationServices() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
bool was_permission_granted = user_did_opt_into_location_services_;
user_did_opt_into_location_services_ = true;
if (IsRunning() && !was_permission_granted)
InformProvidersPermissionGranted();
}
-bool GeolocationProviderImpl::LocationServicesOptedIn() const {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- return user_did_opt_into_location_services_;
+void GeolocationProviderImpl::OverrideLocationForTesting(
+ const Geoposition& position) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ ignore_location_updates_ = true;
+ NotifyClients(position);
}
void GeolocationProviderImpl::OnLocationUpdate(const Geoposition& position) {
@@ -101,22 +60,14 @@ void GeolocationProviderImpl::OnLocationUpdate(const Geoposition& position) {
// Will be true only in testing.
if (ignore_location_updates_)
return;
- BrowserThread::PostTask(BrowserThread::IO,
+ BrowserThread::PostTask(BrowserThread::UI,
FROM_HERE,
base::Bind(&GeolocationProviderImpl::NotifyClients,
base::Unretained(this), position));
}
-void GeolocationProviderImpl::OverrideLocationForTesting(
- const Geoposition& position) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- position_ = position;
- ignore_location_updates_ = true;
- NotifyClients(position);
-}
-
GeolocationProviderImpl* GeolocationProviderImpl::GetInstance() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
return Singleton<GeolocationProviderImpl>::get();
}
@@ -125,12 +76,16 @@ GeolocationProviderImpl::GeolocationProviderImpl()
user_did_opt_into_location_services_(false),
ignore_location_updates_(false),
arbitrator_(NULL) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ high_accuracy_callbacks_.set_removal_callback(
+ base::Bind(&GeolocationProviderImpl::OnClientsChanged,
+ base::Unretained(this)));
+ low_accuracy_callbacks_.set_removal_callback(
+ base::Bind(&GeolocationProviderImpl::OnClientsChanged,
+ base::Unretained(this)));
}
GeolocationProviderImpl::~GeolocationProviderImpl() {
- // All callbacks should have unregistered before this singleton is destructed.
- DCHECK(callbacks_.empty());
Stop();
DCHECK(!arbitrator_);
}
@@ -140,30 +95,25 @@ bool GeolocationProviderImpl::OnGeolocationThread() const {
}
void GeolocationProviderImpl::OnClientsChanged() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
base::Closure task;
- if (callbacks_.empty()) {
+ if (high_accuracy_callbacks_.empty() && low_accuracy_callbacks_.empty()) {
DCHECK(IsRunning());
- // We have no more observers, so we clear the cached geoposition so that
- // when the next observer is added we will not provide a stale position.
- position_ = Geoposition();
+ if (!ignore_location_updates_) {
+ // We have no more observers, so we clear the cached geoposition so that
+ // when the next observer is added we will not provide a stale position.
+ position_ = Geoposition();
+ }
task = base::Bind(&GeolocationProviderImpl::StopProviders,
base::Unretained(this));
} else {
if (!IsRunning()) {
Start();
- if (LocationServicesOptedIn())
+ if (user_did_opt_into_location_services_)
InformProvidersPermissionGranted();
}
// Determine a set of options that satisfies all clients.
- bool use_high_accuracy = false;
- CallbackList::iterator i = callbacks_.begin();
- for (; i != callbacks_.end(); ++i) {
- if (i->second) {
- use_high_accuracy = true;
- break;
- }
- }
+ bool use_high_accuracy = !high_accuracy_callbacks_.empty();
// Send the current options to the providers as they may have changed.
task = base::Bind(&GeolocationProviderImpl::StartProviders,
@@ -201,18 +151,12 @@ void GeolocationProviderImpl::InformProvidersPermissionGranted() {
}
void GeolocationProviderImpl::NotifyClients(const Geoposition& position) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(position.Validate() ||
position.error_code != Geoposition::ERROR_CODE_NONE);
position_ = position;
- CallbackList::const_iterator it = callbacks_.begin();
- while (it != callbacks_.end()) {
- // Advance iterator before calling the observer to guard against synchronous
- // unregister.
- LocationUpdateCallback callback = it->first;
- ++it;
- callback.Run(position_);
- }
+ high_accuracy_callbacks_.Notify(position_);
+ low_accuracy_callbacks_.Notify(position_);
}
void GeolocationProviderImpl::Init() {
diff --git a/chromium/content/browser/geolocation/geolocation_provider_impl.h b/chromium/content/browser/geolocation/geolocation_provider_impl.h
index 6613f44fc69..e3b8762ed44 100644
--- a/chromium/content/browser/geolocation/geolocation_provider_impl.h
+++ b/chromium/content/browser/geolocation/geolocation_provider_impl.h
@@ -21,33 +21,16 @@ template<typename Type> struct DefaultSingletonTraits;
namespace content {
class LocationArbitrator;
-// This is the main API to the geolocation subsystem. The application will hold
-// a single instance of this class and can register multiple clients to be
-// notified of location changes:
-// * Callbacks are registered by AddLocationUpdateCallback() and will keep
-// receiving updates until unregistered by RemoveLocationUpdateCallback().
-// The application must instantiate the GeolocationProvider on the IO thread and
-// must communicate with it on the same thread.
-// The underlying location arbitrator will only be enabled whilst there is at
-// least one registered observer or pending callback. The arbitrator and the
-// location providers it uses run on a separate Geolocation thread.
class CONTENT_EXPORT GeolocationProviderImpl
: public NON_EXPORTED_BASE(GeolocationProvider),
public base::Thread {
public:
// GeolocationProvider implementation:
- virtual void AddLocationUpdateCallback(const LocationUpdateCallback& callback,
- bool use_high_accuracy) OVERRIDE;
- virtual bool RemoveLocationUpdateCallback(
- const LocationUpdateCallback& callback) OVERRIDE;
+ virtual scoped_ptr<GeolocationProvider::Subscription>
+ AddLocationUpdateCallback(const LocationUpdateCallback& callback,
+ bool use_high_accuracy) OVERRIDE;
virtual void UserDidOptIntoLocationServices() OVERRIDE;
-
- bool LocationServicesOptedIn() const;
-
- // Overrides the location for automation/testing. Suppresses any further
- // updates from the actual providers and sends an update with the overridden
- // position to all registered clients.
- void OverrideLocationForTesting(const Geoposition& override_position);
+ virtual void OverrideLocationForTesting(const Geoposition& position) OVERRIDE;
// Callback from the LocationArbitrator. Public for testing.
void OnLocationUpdate(const Geoposition& position);
@@ -58,6 +41,10 @@ class CONTENT_EXPORT GeolocationProviderImpl
// instantiated on the same thread. Ownership is NOT returned.
static GeolocationProviderImpl* GetInstance();
+ bool user_did_opt_into_location_services_for_testing() {
+ return user_did_opt_into_location_services_;
+ }
+
protected:
friend struct DefaultSingletonTraits<GeolocationProviderImpl>;
GeolocationProviderImpl();
@@ -67,9 +54,6 @@ class CONTENT_EXPORT GeolocationProviderImpl
virtual LocationArbitrator* CreateArbitrator();
private:
- typedef std::pair<LocationUpdateCallback, bool> LocationUpdateInfo;
- typedef std::list<LocationUpdateInfo> CallbackList;
-
bool OnGeolocationThread() const;
// Start and stop providers as needed when clients are added or removed.
@@ -94,8 +78,9 @@ class CONTENT_EXPORT GeolocationProviderImpl
virtual void Init() OVERRIDE;
virtual void CleanUp() OVERRIDE;
- // Only used on the IO thread
- CallbackList callbacks_;
+ base::CallbackList<void(const Geoposition&)> high_accuracy_callbacks_;
+ base::CallbackList<void(const Geoposition&)> low_accuracy_callbacks_;
+
bool user_did_opt_into_location_services_;
Geoposition position_;
diff --git a/chromium/content/browser/geolocation/geolocation_provider_unittest.cc b/chromium/content/browser/geolocation/geolocation_provider_unittest.cc
index a2aa88d9e65..95fa1b69145 100644
--- a/chromium/content/browser/geolocation/geolocation_provider_unittest.cc
+++ b/chromium/content/browser/geolocation/geolocation_provider_unittest.cc
@@ -114,7 +114,7 @@ class GeolocationProviderTest : public testing::Test {
protected:
GeolocationProviderTest()
: message_loop_(),
- io_thread_(BrowserThread::IO, &message_loop_),
+ ui_thread_(BrowserThread::UI, &message_loop_),
provider_(new LocationProviderForTestArbitrator) {
}
@@ -131,7 +131,7 @@ class GeolocationProviderTest : public testing::Test {
void GetProvidersStarted(bool* started);
base::MessageLoop message_loop_;
- TestBrowserThread io_thread_;
+ TestBrowserThread ui_thread_;
scoped_ptr<LocationProviderForTestArbitrator> provider_;
};
@@ -167,19 +167,25 @@ void GeolocationProviderTest::SendMockLocation(const Geoposition& position) {
// Regression test for http://crbug.com/59377
TEST_F(GeolocationProviderTest, OnPermissionGrantedWithoutObservers) {
- EXPECT_FALSE(provider()->LocationServicesOptedIn());
+ EXPECT_FALSE(provider()->user_did_opt_into_location_services_for_testing());
provider()->UserDidOptIntoLocationServices();
- EXPECT_TRUE(provider()->LocationServicesOptedIn());
+ EXPECT_TRUE(provider()->user_did_opt_into_location_services_for_testing());
+}
+
+void DummyFunction(const Geoposition& position) {
}
TEST_F(GeolocationProviderTest, StartStop) {
EXPECT_FALSE(provider()->IsRunning());
- GeolocationProviderImpl::LocationUpdateCallback null_callback;
- provider()->AddLocationUpdateCallback(null_callback, false);
+ GeolocationProviderImpl::LocationUpdateCallback callback =
+ base::Bind(&DummyFunction);
+ scoped_ptr<content::GeolocationProvider::Subscription> subscription =
+ provider()->AddLocationUpdateCallback(callback, false);
EXPECT_TRUE(provider()->IsRunning());
EXPECT_TRUE(ProvidersStarted());
- provider()->RemoveLocationUpdateCallback(null_callback);
+ subscription.reset();
+
EXPECT_FALSE(ProvidersStarted());
EXPECT_TRUE(provider()->IsRunning());
}
@@ -196,11 +202,12 @@ TEST_F(GeolocationProviderTest, StalePositionNotSent) {
&MockGeolocationObserver::OnLocationUpdate,
base::Unretained(&first_observer));
EXPECT_CALL(first_observer, OnLocationUpdate(GeopositionEq(first_position)));
- provider()->AddLocationUpdateCallback(first_callback, false);
+ scoped_ptr<content::GeolocationProvider::Subscription> subscription =
+ provider()->AddLocationUpdateCallback(first_callback, false);
SendMockLocation(first_position);
base::MessageLoop::current()->Run();
- provider()->RemoveLocationUpdateCallback(first_callback);
+ subscription.reset();
Geoposition second_position;
second_position.latitude = 13;
@@ -216,7 +223,8 @@ TEST_F(GeolocationProviderTest, StalePositionNotSent) {
GeolocationProviderImpl::LocationUpdateCallback second_callback = base::Bind(
&MockGeolocationObserver::OnLocationUpdate,
base::Unretained(&second_observer));
- provider()->AddLocationUpdateCallback(second_callback, false);
+ scoped_ptr<content::GeolocationProvider::Subscription> subscription2 =
+ provider()->AddLocationUpdateCallback(second_callback, false);
base::MessageLoop::current()->RunUntilIdle();
// The second observer should receive the new position now.
@@ -225,7 +233,7 @@ TEST_F(GeolocationProviderTest, StalePositionNotSent) {
SendMockLocation(second_position);
base::MessageLoop::current()->Run();
- provider()->RemoveLocationUpdateCallback(second_callback);
+ subscription2.reset();
EXPECT_FALSE(ProvidersStarted());
}
@@ -240,8 +248,9 @@ TEST_F(GeolocationProviderTest, OverrideLocationForTesting) {
GeolocationProviderImpl::LocationUpdateCallback callback = base::Bind(
&MockGeolocationObserver::OnLocationUpdate,
base::Unretained(&mock_observer));
- provider()->AddLocationUpdateCallback(callback, false);
- provider()->RemoveLocationUpdateCallback(callback);
+ scoped_ptr<content::GeolocationProvider::Subscription> subscription =
+ provider()->AddLocationUpdateCallback(callback, false);
+ subscription.reset();
// Wait for the providers to be stopped now that all clients are gone.
EXPECT_FALSE(ProvidersStarted());
}
diff --git a/chromium/content/browser/geolocation/location_api_adapter_android.h b/chromium/content/browser/geolocation/location_api_adapter_android.h
index f81ac93826b..d1d2fa52ee7 100644
--- a/chromium/content/browser/geolocation/location_api_adapter_android.h
+++ b/chromium/content/browser/geolocation/location_api_adapter_android.h
@@ -5,7 +5,7 @@
#ifndef CONTENT_BROWSER_GEOLOCATION_LOCATION_API_ADAPTER_ANDROID_H_
#define CONTENT_BROWSER_GEOLOCATION_LOCATION_API_ADAPTER_ANDROID_H_
-#include "base/android/jni_helper.h"
+#include "base/android/jni_weak_ref.h"
#include "base/android/scoped_java_ref.h"
#include "base/memory/ref_counted.h"
#include "base/memory/singleton.h"
diff --git a/chromium/content/browser/geolocation/location_arbitrator_impl.h b/chromium/content/browser/geolocation/location_arbitrator_impl.h
index e756f5e15de..da20e7b1889 100644
--- a/chromium/content/browser/geolocation/location_arbitrator_impl.h
+++ b/chromium/content/browser/geolocation/location_arbitrator_impl.h
@@ -11,8 +11,8 @@
#include "base/time/time.h"
#include "content/browser/geolocation/location_arbitrator.h"
#include "content/common/content_export.h"
-#include "content/port/browser/location_provider.h"
#include "content/public/browser/access_token_store.h"
+#include "content/public/browser/location_provider.h"
#include "content/public/common/geoposition.h"
#include "net/url_request/url_request_context_getter.h"
diff --git a/chromium/content/browser/geolocation/location_provider_base.h b/chromium/content/browser/geolocation/location_provider_base.h
index dbc42402d5e..36d900bd747 100644
--- a/chromium/content/browser/geolocation/location_provider_base.h
+++ b/chromium/content/browser/geolocation/location_provider_base.h
@@ -6,7 +6,7 @@
#define CONTENT_BROWSER_GEOLOCATION_LOCATION_PROVIDER_H_
#include "content/common/content_export.h"
-#include "content/port/browser/location_provider.h"
+#include "content/public/browser/location_provider.h"
namespace content {
diff --git a/chromium/content/browser/geolocation/network_location_provider.cc b/chromium/content/browser/geolocation/network_location_provider.cc
index efc68a73231..2a9d7c8a3b8 100644
--- a/chromium/content/browser/geolocation/network_location_provider.cc
+++ b/chromium/content/browser/geolocation/network_location_provider.cc
@@ -77,7 +77,7 @@ bool NetworkLocationProvider::PositionCache::MakeKey(
key->clear();
const size_t kCharsPerMacAddress = 6 * 3 + 1; // e.g. "11:22:33:44:55:66|"
key->reserve(wifi_data.access_point_data.size() * kCharsPerMacAddress);
- const base::string16 separator(ASCIIToUTF16("|"));
+ const base::string16 separator(base::ASCIIToUTF16("|"));
for (WifiData::AccessPointDataSet::const_iterator iter =
wifi_data.access_point_data.begin();
iter != wifi_data.access_point_data.end();
diff --git a/chromium/content/browser/geolocation/network_location_provider_unittest.cc b/chromium/content/browser/geolocation/network_location_provider_unittest.cc
index f7c732049a8..c574b7a7b96 100644
--- a/chromium/content/browser/geolocation/network_location_provider_unittest.cc
+++ b/chromium/content/browser/geolocation/network_location_provider_unittest.cc
@@ -162,11 +162,11 @@ class GeolocationNetworkProviderTest : public testing::Test {
for (int i = 0; i < ap_count; ++i) {
AccessPointData ap;
ap.mac_address =
- ASCIIToUTF16(base::StringPrintf("%02d-34-56-78-54-32", i));
+ base::ASCIIToUTF16(base::StringPrintf("%02d-34-56-78-54-32", i));
ap.radio_signal_strength = ap_count - i;
ap.channel = IndexToChannel(i);
ap.signal_to_noise = i + 42;
- ap.ssid = ASCIIToUTF16("Some nice+network|name\\");
+ ap.ssid = base::ASCIIToUTF16("Some nice+network|name\\");
data.access_point_data.insert(ap);
}
return data;
@@ -234,7 +234,7 @@ class GeolocationNetworkProviderTest : public testing::Test {
}
static GURL UrlWithoutQuery(const GURL& url) {
- url_canon::Replacements<char> replacements;
+ url::Replacements<char> replacements;
replacements.ClearQuery();
return url.ReplaceComponents(replacements);
}
@@ -421,7 +421,7 @@ TEST_F(GeolocationNetworkProviderTest, MultipleWifiScansComplete) {
EXPECT_TRUE(position.Validate());
// Token should be in the store.
- EXPECT_EQ(UTF8ToUTF16(REFERENCE_ACCESS_TOKEN),
+ EXPECT_EQ(base::UTF8ToUTF16(REFERENCE_ACCESS_TOKEN),
access_token_store_->access_token_set_[test_server_url_]);
// Wifi updated again, with one less AP. This is 'close enough' to the
@@ -517,7 +517,7 @@ TEST_F(GeolocationNetworkProviderTest, NetworkRequestDeferredForPermission) {
TEST_F(GeolocationNetworkProviderTest,
NetworkRequestWithWifiDataDeferredForPermission) {
access_token_store_->access_token_set_[test_server_url_] =
- UTF8ToUTF16(REFERENCE_ACCESS_TOKEN);
+ base::UTF8ToUTF16(REFERENCE_ACCESS_TOKEN);
scoped_ptr<LocationProvider> provider(CreateProvider(false));
EXPECT_TRUE(provider->StartProvider(false));
net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
diff --git a/chromium/content/browser/geolocation/network_location_request.cc b/chromium/content/browser/geolocation/network_location_request.cc
index 3ed709218fe..8fb6e6a9076 100644
--- a/chromium/content/browser/geolocation/network_location_request.cc
+++ b/chromium/content/browser/geolocation/network_location_request.cc
@@ -262,7 +262,7 @@ void AddWifiData(const WifiData& wifi_data,
iter != access_points_by_signal_strength.end();
++iter) {
base::DictionaryValue* wifi_dict = new base::DictionaryValue();
- AddString("macAddress", UTF16ToUTF8((*iter)->mac_address), wifi_dict);
+ AddString("macAddress", base::UTF16ToUTF8((*iter)->mac_address), wifi_dict);
AddInteger("signalStrength", (*iter)->radio_signal_strength, wifi_dict);
AddInteger("age", age_milliseconds, wifi_dict);
AddInteger("channel", (*iter)->channel, wifi_dict);
diff --git a/chromium/content/browser/geolocation/wifi_data_provider_chromeos.cc b/chromium/content/browser/geolocation/wifi_data_provider_chromeos.cc
index a8b20414ceb..49edb23e85c 100644
--- a/chromium/content/browser/geolocation/wifi_data_provider_chromeos.cc
+++ b/chromium/content/browser/geolocation/wifi_data_provider_chromeos.cc
@@ -151,11 +151,11 @@ bool WifiDataProviderChromeOs::GetAccessPointData(
= access_points.begin();
i != access_points.end(); ++i) {
AccessPointData ap_data;
- ap_data.mac_address = ASCIIToUTF16(i->mac_address);
+ ap_data.mac_address = base::ASCIIToUTF16(i->mac_address);
ap_data.radio_signal_strength = i->signal_strength;
ap_data.channel = i->channel;
ap_data.signal_to_noise = i->signal_to_noise;
- ap_data.ssid = UTF8ToUTF16(i->ssid);
+ ap_data.ssid = base::UTF8ToUTF16(i->ssid);
result->insert(ap_data);
}
// If the age is significantly longer than our long polling time, assume the
diff --git a/chromium/content/browser/geolocation/wifi_data_provider_chromeos_unittest.cc b/chromium/content/browser/geolocation/wifi_data_provider_chromeos_unittest.cc
index f9deb663539..f282f31e510 100644
--- a/chromium/content/browser/geolocation/wifi_data_provider_chromeos_unittest.cc
+++ b/chromium/content/browser/geolocation/wifi_data_provider_chromeos_unittest.cc
@@ -84,7 +84,8 @@ TEST_F(GeolocationChromeOsWifiDataProviderTest, GetOneAccessPoint) {
AddAccessPoints(1, 1);
EXPECT_TRUE(GetAccessPointData());
ASSERT_EQ(1u, ap_data_.size());
- EXPECT_EQ("00:00:03:04:05:06", UTF16ToUTF8(ap_data_.begin()->mac_address));
+ EXPECT_EQ("00:00:03:04:05:06",
+ base::UTF16ToUTF8(ap_data_.begin()->mac_address));
}
TEST_F(GeolocationChromeOsWifiDataProviderTest, GetManyAccessPoints) {
diff --git a/chromium/content/browser/geolocation/wifi_data_provider_common.cc b/chromium/content/browser/geolocation/wifi_data_provider_common.cc
index 9c2cbae2345..388a17e5a9b 100644
--- a/chromium/content/browser/geolocation/wifi_data_provider_common.cc
+++ b/chromium/content/browser/geolocation/wifi_data_provider_common.cc
@@ -15,13 +15,13 @@ base::string16 MacAddressAsString16(const uint8 mac_as_int[6]) {
// Format is XX-XX-XX-XX-XX-XX.
static const char* const kMacFormatString =
"%02x-%02x-%02x-%02x-%02x-%02x";
- return ASCIIToUTF16(base::StringPrintf(kMacFormatString,
- mac_as_int[0],
- mac_as_int[1],
- mac_as_int[2],
- mac_as_int[3],
- mac_as_int[4],
- mac_as_int[5]));
+ return base::ASCIIToUTF16(base::StringPrintf(kMacFormatString,
+ mac_as_int[0],
+ mac_as_int[1],
+ mac_as_int[2],
+ mac_as_int[3],
+ mac_as_int[4],
+ mac_as_int[5]));
}
WifiDataProviderCommon::WifiDataProviderCommon()
diff --git a/chromium/content/browser/geolocation/wifi_data_provider_common_unittest.cc b/chromium/content/browser/geolocation/wifi_data_provider_common_unittest.cc
index 61e236f98a8..9c55dfd3335 100644
--- a/chromium/content/browser/geolocation/wifi_data_provider_common_unittest.cc
+++ b/chromium/content/browser/geolocation/wifi_data_provider_common_unittest.cc
@@ -173,7 +173,7 @@ TEST_F(GeolocationWifiDataProviderCommonTest, IntermittentWifi){
single_access_point.mac_address = 3;
single_access_point.radio_signal_strength = 4;
single_access_point.signal_to_noise = 5;
- single_access_point.ssid = ASCIIToUTF16("foossid");
+ single_access_point.ssid = base::ASCIIToUTF16("foossid");
wlan_api_->data_out_.insert(single_access_point);
provider_->StartDataProvider();
@@ -204,7 +204,7 @@ TEST_F(GeolocationWifiDataProviderCommonTest, DoScanWithResults) {
single_access_point.mac_address = 3;
single_access_point.radio_signal_strength = 4;
single_access_point.signal_to_noise = 5;
- single_access_point.ssid = ASCIIToUTF16("foossid");
+ single_access_point.ssid = base::ASCIIToUTF16("foossid");
wlan_api_->data_out_.insert(single_access_point);
provider_->StartDataProvider();
diff --git a/chromium/content/browser/geolocation/wifi_data_provider_common_win.cc b/chromium/content/browser/geolocation/wifi_data_provider_common_win.cc
index a0d334f0c44..aac728d4ec0 100644
--- a/chromium/content/browser/geolocation/wifi_data_provider_common_win.cc
+++ b/chromium/content/browser/geolocation/wifi_data_provider_common_win.cc
@@ -19,9 +19,9 @@ bool ConvertToAccessPointData(const NDIS_WLAN_BSSID& data,
access_point_data->mac_address = MacAddressAsString16(data.MacAddress);
access_point_data->radio_signal_strength = data.Rssi;
// Note that _NDIS_802_11_SSID::Ssid::Ssid is not null-terminated.
- UTF8ToUTF16(reinterpret_cast<const char*>(data.Ssid.Ssid),
- data.Ssid.SsidLength,
- &access_point_data->ssid);
+ base::UTF8ToUTF16(reinterpret_cast<const char*>(data.Ssid.Ssid),
+ data.Ssid.SsidLength,
+ &access_point_data->ssid);
return true;
}
diff --git a/chromium/content/browser/geolocation/wifi_data_provider_linux.cc b/chromium/content/browser/geolocation/wifi_data_provider_linux.cc
index a8d2135fc92..5838c048e35 100644
--- a/chromium/content/browser/geolocation/wifi_data_provider_linux.cc
+++ b/chromium/content/browser/geolocation/wifi_data_provider_linux.cc
@@ -246,7 +246,7 @@ bool NetworkManagerWlanApi::GetAccessPointsForAdapter(
<< ": " << response->ToString();
continue;
}
- uint8* ssid_bytes = NULL;
+ const uint8* ssid_bytes = NULL;
size_t ssid_length = 0;
if (!variant_reader.PopArrayOfBytes(&ssid_bytes, &ssid_length)) {
LOG(WARNING) << "Unexpected response for " << access_point_path.value()
@@ -254,7 +254,7 @@ bool NetworkManagerWlanApi::GetAccessPointsForAdapter(
continue;
}
std::string ssid(ssid_bytes, ssid_bytes + ssid_length);
- access_point_data.ssid = UTF8ToUTF16(ssid);
+ access_point_data.ssid = base::UTF8ToUTF16(ssid);
}
{ // Read the mac address
@@ -275,7 +275,7 @@ bool NetworkManagerWlanApi::GetAccessPointsForAdapter(
if (!base::HexStringToBytes(mac, &mac_bytes) || mac_bytes.size() != 6) {
LOG(WARNING) << "Can't parse mac address (found " << mac_bytes.size()
<< " bytes) so using raw string: " << mac;
- access_point_data.mac_address = UTF8ToUTF16(mac);
+ access_point_data.mac_address = base::UTF8ToUTF16(mac);
} else {
access_point_data.mac_address = MacAddressAsString16(&mac_bytes[0]);
}
diff --git a/chromium/content/browser/geolocation/wifi_data_provider_linux_unittest.cc b/chromium/content/browser/geolocation/wifi_data_provider_linux_unittest.cc
index 2e724706078..498b230643c 100644
--- a/chromium/content/browser/geolocation/wifi_data_provider_linux_unittest.cc
+++ b/chromium/content/browser/geolocation/wifi_data_provider_linux_unittest.cc
@@ -222,8 +222,9 @@ TEST_F(GeolocationWifiDataProviderLinuxTest, GetAccessPointData) {
// Check the contents of the access point data.
// The expected values come from CreateAccessPointProxyResponse() above.
- EXPECT_EQ("test", UTF16ToUTF8(access_point_data.ssid));
- EXPECT_EQ("00-11-22-33-44-55", UTF16ToUTF8(access_point_data.mac_address));
+ EXPECT_EQ("test", base::UTF16ToUTF8(access_point_data.ssid));
+ EXPECT_EQ("00-11-22-33-44-55",
+ base::UTF16ToUTF8(access_point_data.mac_address));
EXPECT_EQ(-50, access_point_data.radio_signal_strength);
EXPECT_EQ(4, access_point_data.channel);
}
diff --git a/chromium/content/browser/geolocation/wifi_data_provider_mac.cc b/chromium/content/browser/geolocation/wifi_data_provider_mac.cc
index e7d8e8247f9..6a87801c893 100644
--- a/chromium/content/browser/geolocation/wifi_data_provider_mac.cc
+++ b/chromium/content/browser/geolocation/wifi_data_provider_mac.cc
@@ -140,9 +140,10 @@ bool Apple80211Api::GetAccessPointData(WifiData::AccessPointDataSet* data) {
// WirelessNetworkInfo::noise appears to be noise floor in dBm.
access_point_data.signal_to_noise = access_point_info->signal -
access_point_info->noise;
- if (!UTF8ToUTF16(reinterpret_cast<const char*>(access_point_info->name),
- access_point_info->nameLen,
- &access_point_data.ssid)) {
+ if (!base::UTF8ToUTF16(reinterpret_cast<const char*>(
+ access_point_info->name),
+ access_point_info->nameLen,
+ &access_point_data.ssid)) {
access_point_data.ssid.clear();
}
data->insert(access_point_data);
diff --git a/chromium/content/browser/geolocation/wifi_data_provider_win.cc b/chromium/content/browser/geolocation/wifi_data_provider_win.cc
index ed69865fbd0..9345144438d 100644
--- a/chromium/content/browser/geolocation/wifi_data_provider_win.cc
+++ b/chromium/content/browser/geolocation/wifi_data_provider_win.cc
@@ -152,7 +152,8 @@ int PerformQuery(HANDLE adapter_handle,
BYTE* buffer,
DWORD buffer_size,
DWORD* bytes_out);
-bool ResizeBuffer(int requested_size, scoped_ptr_malloc<BYTE>* buffer);
+bool ResizeBuffer(int requested_size,
+ scoped_ptr<BYTE, base::FreeDeleter>* buffer);
// Gets the system directory and appends a trailing slash if not already
// present.
bool GetSystemDirectory(base::string16* path);
@@ -468,8 +469,8 @@ bool WindowsNdisApi::GetInterfaceDataNDIS(HANDLE adapter_handle,
WifiData::AccessPointDataSet* data) {
DCHECK(data);
- scoped_ptr_malloc<BYTE> buffer(
- reinterpret_cast<BYTE*>(malloc(oid_buffer_size_)));
+ scoped_ptr<BYTE, base::FreeDeleter> buffer(
+ static_cast<BYTE*>(malloc(oid_buffer_size_)));
if (buffer == NULL) {
return false;
}
@@ -520,9 +521,9 @@ bool GetNetworkData(const WLAN_BSS_ENTRY& bss_entry,
access_point_data->mac_address = MacAddressAsString16(bss_entry.dot11Bssid);
access_point_data->radio_signal_strength = bss_entry.lRssi;
// bss_entry.dot11Ssid.ucSSID is not null-terminated.
- UTF8ToUTF16(reinterpret_cast<const char*>(bss_entry.dot11Ssid.ucSSID),
- static_cast<ULONG>(bss_entry.dot11Ssid.uSSIDLength),
- &access_point_data->ssid);
+ base::UTF8ToUTF16(reinterpret_cast<const char*>(bss_entry.dot11Ssid.ucSSID),
+ static_cast<ULONG>(bss_entry.dot11Ssid.uSSIDLength),
+ &access_point_data->ssid);
// TODO(steveblock): Is it possible to get the following?
// access_point_data->signal_to_noise
// access_point_data->age
@@ -597,7 +598,8 @@ int PerformQuery(HANDLE adapter_handle,
return ERROR_SUCCESS;
}
-bool ResizeBuffer(int requested_size, scoped_ptr_malloc<BYTE>* buffer) {
+bool ResizeBuffer(int requested_size,
+ scoped_ptr<BYTE, base::FreeDeleter>* buffer) {
DCHECK_GT(requested_size, 0);
DCHECK(buffer);
if (requested_size > kMaximumBufferSize) {
@@ -617,7 +619,7 @@ bool GetSystemDirectory(base::string16* path) {
if (buffer_size == 0) {
return false;
}
- scoped_ptr<char16[]> buffer(new char16[buffer_size]);
+ scoped_ptr<base::char16[]> buffer(new base::char16[buffer_size]);
// Return value excludes terminating NULL.
int characters_written = ::GetSystemDirectory(buffer.get(), buffer_size);
diff --git a/chromium/content/browser/gpu/browser_gpu_channel_host_factory.cc b/chromium/content/browser/gpu/browser_gpu_channel_host_factory.cc
index d0ce8b48065..e2d1654e154 100644
--- a/chromium/content/browser/gpu/browser_gpu_channel_host_factory.cc
+++ b/chromium/content/browser/gpu/browser_gpu_channel_host_factory.cc
@@ -6,29 +6,86 @@
#include "base/bind.h"
#include "base/debug/trace_event.h"
+#include "base/synchronization/waitable_event.h"
#include "base/threading/thread_restrictions.h"
#include "content/browser/gpu/gpu_data_manager_impl.h"
#include "content/browser/gpu/gpu_process_host.h"
#include "content/browser/gpu/gpu_surface_tracker.h"
#include "content/common/child_process_host_impl.h"
-#include "content/common/gpu/client/gpu_memory_buffer_impl_shm.h"
+#include "content/common/gpu/client/gpu_memory_buffer_impl.h"
#include "content/common/gpu/gpu_messages.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/gpu_data_manager.h"
#include "content/public/common/content_client.h"
+#include "ipc/ipc_channel_handle.h"
#include "ipc/ipc_forwarding_message_filter.h"
+#include "ipc/message_filter.h"
namespace content {
BrowserGpuChannelHostFactory* BrowserGpuChannelHostFactory::instance_ = NULL;
-BrowserGpuChannelHostFactory::CreateRequest::CreateRequest()
- : event(true, false),
- gpu_host_id(0),
- route_id(MSG_ROUTING_NONE) {
-}
-
-BrowserGpuChannelHostFactory::CreateRequest::~CreateRequest() {
+struct BrowserGpuChannelHostFactory::CreateRequest {
+ CreateRequest()
+ : event(true, false), gpu_host_id(0), route_id(MSG_ROUTING_NONE),
+ result(CREATE_COMMAND_BUFFER_FAILED) {}
+ ~CreateRequest() {}
+ base::WaitableEvent event;
+ int gpu_host_id;
+ int32 route_id;
+ CreateCommandBufferResult result;
+};
+
+class BrowserGpuChannelHostFactory::EstablishRequest
+ : public base::RefCountedThreadSafe<EstablishRequest> {
+ public:
+ static scoped_refptr<EstablishRequest> Create(CauseForGpuLaunch cause,
+ int gpu_client_id,
+ int gpu_host_id);
+ void Wait();
+ void Cancel();
+
+ int gpu_host_id() { return gpu_host_id_; }
+ IPC::ChannelHandle& channel_handle() { return channel_handle_; }
+ gpu::GPUInfo gpu_info() { return gpu_info_; }
+
+ private:
+ friend class base::RefCountedThreadSafe<EstablishRequest>;
+ explicit EstablishRequest(CauseForGpuLaunch cause,
+ int gpu_client_id,
+ int gpu_host_id);
+ ~EstablishRequest() {}
+ void EstablishOnIO();
+ void OnEstablishedOnIO(const IPC::ChannelHandle& channel_handle,
+ const gpu::GPUInfo& gpu_info);
+ void FinishOnIO();
+ void FinishOnMain();
+
+ base::WaitableEvent event_;
+ CauseForGpuLaunch cause_for_gpu_launch_;
+ const int gpu_client_id_;
+ int gpu_host_id_;
+ bool reused_gpu_process_;
+ IPC::ChannelHandle channel_handle_;
+ gpu::GPUInfo gpu_info_;
+ bool finished_;
+ scoped_refptr<base::MessageLoopProxy> main_loop_;
+};
+
+scoped_refptr<BrowserGpuChannelHostFactory::EstablishRequest>
+BrowserGpuChannelHostFactory::EstablishRequest::Create(CauseForGpuLaunch cause,
+ int gpu_client_id,
+ int gpu_host_id) {
+ scoped_refptr<EstablishRequest> establish_request =
+ new EstablishRequest(cause, gpu_client_id, gpu_host_id);
+ scoped_refptr<base::MessageLoopProxy> loop =
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
+ // PostTask outside the constructor to ensure at least one reference exists.
+ loop->PostTask(
+ FROM_HERE,
+ base::Bind(&BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO,
+ establish_request));
+ return establish_request;
}
BrowserGpuChannelHostFactory::EstablishRequest::EstablishRequest(
@@ -42,15 +99,6 @@ BrowserGpuChannelHostFactory::EstablishRequest::EstablishRequest(
reused_gpu_process_(false),
finished_(false),
main_loop_(base::MessageLoopProxy::current()) {
- scoped_refptr<base::MessageLoopProxy> loop =
- BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
- loop->PostTask(
- FROM_HERE,
- base::Bind(&BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO,
- this));
-}
-
-BrowserGpuChannelHostFactory::EstablishRequest::~EstablishRequest() {
}
void BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO() {
@@ -59,6 +107,7 @@ void BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO() {
host = GpuProcessHost::Get(GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED,
cause_for_gpu_launch_);
if (!host) {
+ LOG(ERROR) << "Failed to launch GPU process.";
FinishOnIO();
return;
}
@@ -70,6 +119,7 @@ void BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO() {
// failure in ChannelEstablishedOnIO, but we ended up with the same
// process ID, meaning the failure was not because of a channel error,
// but another reason. So fail now.
+ LOG(ERROR) << "Failed to create channel.";
FinishOnIO();
return;
}
@@ -90,6 +140,8 @@ void BrowserGpuChannelHostFactory::EstablishRequest::OnEstablishedOnIO(
if (channel_handle.name.empty() && reused_gpu_process_) {
// We failed after re-using the GPU process, but it may have died in the
// mean time. Retry to have a chance to create a fresh GPU process.
+ DVLOG(1) << "Failed to create channel on existing GPU process. Trying to "
+ "restart GPU process.";
EstablishOnIO();
} else {
channel_handle_ = channel_handle;
@@ -141,7 +193,11 @@ bool BrowserGpuChannelHostFactory::CanUseForTesting() {
void BrowserGpuChannelHostFactory::Initialize(bool establish_gpu_channel) {
DCHECK(!instance_);
- instance_ = new BrowserGpuChannelHostFactory(establish_gpu_channel);
+ instance_ = new BrowserGpuChannelHostFactory();
+ if (establish_gpu_channel) {
+ instance_->EstablishGpuChannel(CAUSE_FOR_GPU_LAUNCH_BROWSER_STARTUP,
+ base::Closure());
+ }
}
void BrowserGpuChannelHostFactory::Terminate() {
@@ -150,15 +206,11 @@ void BrowserGpuChannelHostFactory::Terminate() {
instance_ = NULL;
}
-BrowserGpuChannelHostFactory::BrowserGpuChannelHostFactory(
- bool establish_gpu_channel)
+BrowserGpuChannelHostFactory::BrowserGpuChannelHostFactory()
: gpu_client_id_(ChildProcessHostImpl::GenerateChildProcessUniqueId()),
shutdown_event_(new base::WaitableEvent(true, false)),
- gpu_host_id_(0) {
- if (establish_gpu_channel) {
- pending_request_ = new EstablishRequest(
- CAUSE_FOR_GPU_LAUNCH_BROWSER_STARTUP, gpu_client_id_, gpu_host_id_);
- }
+ gpu_host_id_(0),
+ next_create_gpu_memory_buffer_request_id_(0) {
}
BrowserGpuChannelHostFactory::~BrowserGpuChannelHostFactory() {
@@ -183,10 +235,6 @@ BrowserGpuChannelHostFactory::GetIOLoopProxy() {
return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
}
-base::WaitableEvent* BrowserGpuChannelHostFactory::GetShutDownEvent() {
- return shutdown_event_.get();
-}
-
scoped_ptr<base::SharedMemory>
BrowserGpuChannelHostFactory::AllocateSharedMemory(size_t size) {
scoped_ptr<base::SharedMemory> shm(new base::SharedMemory());
@@ -213,21 +261,24 @@ void BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO(
surface_id,
gpu_client_id_,
init_params,
+ request->route_id,
base::Bind(&BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO,
request));
}
// static
void BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO(
- CreateRequest* request, int32 route_id) {
- request->route_id = route_id;
+ CreateRequest* request, CreateCommandBufferResult result) {
+ request->result = result;
request->event.Signal();
}
-int32 BrowserGpuChannelHostFactory::CreateViewCommandBuffer(
+CreateCommandBufferResult BrowserGpuChannelHostFactory::CreateViewCommandBuffer(
int32 surface_id,
- const GPUCreateCommandBufferConfig& init_params) {
+ const GPUCreateCommandBufferConfig& init_params,
+ int32 route_id) {
CreateRequest request;
+ request.route_id = route_id;
GetIOLoopProxy()->PostTask(FROM_HERE, base::Bind(
&BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO,
base::Unretained(this),
@@ -242,7 +293,7 @@ int32 BrowserGpuChannelHostFactory::CreateViewCommandBuffer(
"BrowserGpuChannelHostFactory::CreateViewCommandBuffer");
base::ThreadRestrictions::ScopedAllowWait allow_wait;
request.event.Wait();
- return request.route_id;
+ return request.result;
}
void BrowserGpuChannelHostFactory::CreateImageOnIO(
@@ -332,7 +383,7 @@ void BrowserGpuChannelHostFactory::EstablishGpuChannel(
if (!gpu_channel_ && !pending_request_) {
// We should only get here if the context was lost.
- pending_request_ = new EstablishRequest(
+ pending_request_ = EstablishRequest::Create(
cause_for_gpu_launch, gpu_client_id_, gpu_host_id_);
}
@@ -354,14 +405,15 @@ GpuChannelHost* BrowserGpuChannelHostFactory::GetGpuChannel() {
void BrowserGpuChannelHostFactory::GpuChannelEstablished() {
DCHECK(IsMainThread());
DCHECK(pending_request_);
- if (pending_request_->channel_handle().name.empty())
- return;
-
- GetContentClient()->SetGpuInfo(pending_request_->gpu_info());
- gpu_channel_ = GpuChannelHost::Create(this,
- pending_request_->gpu_host_id(),
- pending_request_->gpu_info(),
- pending_request_->channel_handle());
+ if (pending_request_->channel_handle().name.empty()) {
+ DCHECK(!gpu_channel_);
+ } else {
+ GetContentClient()->SetGpuInfo(pending_request_->gpu_info());
+ gpu_channel_ = GpuChannelHost::Create(this,
+ pending_request_->gpu_info(),
+ pending_request_->channel_handle(),
+ shutdown_event_.get());
+ }
gpu_host_id_ = pending_request_->gpu_host_id();
pending_request_ = NULL;
@@ -372,31 +424,23 @@ void BrowserGpuChannelHostFactory::GpuChannelEstablished() {
}
scoped_ptr<gfx::GpuMemoryBuffer>
- BrowserGpuChannelHostFactory::AllocateGpuMemoryBuffer(
- size_t width,
- size_t height,
- unsigned internalformat) {
- if (!GpuMemoryBufferImpl::IsFormatValid(internalformat))
- return scoped_ptr<gfx::GpuMemoryBuffer>();
-
- size_t size = width * height *
- GpuMemoryBufferImpl::BytesPerPixel(internalformat);
- scoped_ptr<base::SharedMemory> shm(new base::SharedMemory());
- if (!shm->CreateAnonymous(size))
- return scoped_ptr<gfx::GpuMemoryBuffer>();
-
- scoped_ptr<GpuMemoryBufferImplShm> buffer(
- new GpuMemoryBufferImplShm(gfx::Size(width, height), internalformat));
- if (!buffer->InitializeFromSharedMemory(shm.Pass()))
+BrowserGpuChannelHostFactory::AllocateGpuMemoryBuffer(size_t width,
+ size_t height,
+ unsigned internalformat,
+ unsigned usage) {
+ if (!GpuMemoryBufferImpl::IsFormatValid(internalformat) ||
+ !GpuMemoryBufferImpl::IsUsageValid(usage))
return scoped_ptr<gfx::GpuMemoryBuffer>();
- return buffer.PassAs<gfx::GpuMemoryBuffer>();
+ return GpuMemoryBufferImpl::Create(gfx::Size(width, height),
+ internalformat,
+ usage).PassAs<gfx::GpuMemoryBuffer>();
}
// static
void BrowserGpuChannelHostFactory::AddFilterOnIO(
int host_id,
- scoped_refptr<IPC::ChannelProxy::MessageFilter> filter) {
+ scoped_refptr<IPC::MessageFilter> filter) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
GpuProcessHost* host = GpuProcessHost::FromID(host_id);
@@ -427,4 +471,90 @@ void BrowserGpuChannelHostFactory::SetHandlerForControlMessages(
filter));
}
+void BrowserGpuChannelHostFactory::CreateGpuMemoryBuffer(
+ const gfx::GpuMemoryBufferHandle& handle,
+ const gfx::Size& size,
+ unsigned internalformat,
+ unsigned usage,
+ const CreateGpuMemoryBufferCallback& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ uint32 request_id = next_create_gpu_memory_buffer_request_id_++;
+ create_gpu_memory_buffer_requests_[request_id] = callback;
+ GetIOLoopProxy()->PostTask(
+ FROM_HERE,
+ base::Bind(&BrowserGpuChannelHostFactory::CreateGpuMemoryBufferOnIO,
+ base::Unretained(this),
+ handle,
+ size,
+ internalformat,
+ usage,
+ request_id));
+}
+
+void BrowserGpuChannelHostFactory::DestroyGpuMemoryBuffer(
+ const gfx::GpuMemoryBufferHandle& handle,
+ int32 sync_point) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ GetIOLoopProxy()->PostTask(
+ FROM_HERE,
+ base::Bind(&BrowserGpuChannelHostFactory::DestroyGpuMemoryBufferOnIO,
+ base::Unretained(this),
+ handle,
+ sync_point));
+}
+
+void BrowserGpuChannelHostFactory::CreateGpuMemoryBufferOnIO(
+ const gfx::GpuMemoryBufferHandle& handle,
+ const gfx::Size& size,
+ unsigned internalformat,
+ unsigned usage,
+ uint32 request_id) {
+ GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
+ if (!host) {
+ GpuMemoryBufferCreatedOnIO(request_id, gfx::GpuMemoryBufferHandle());
+ return;
+ }
+
+ host->CreateGpuMemoryBuffer(
+ handle,
+ size,
+ internalformat,
+ usage,
+ base::Bind(&BrowserGpuChannelHostFactory::GpuMemoryBufferCreatedOnIO,
+ base::Unretained(this),
+ request_id));
+}
+
+void BrowserGpuChannelHostFactory::GpuMemoryBufferCreatedOnIO(
+ uint32 request_id,
+ const gfx::GpuMemoryBufferHandle& handle) {
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&BrowserGpuChannelHostFactory::OnGpuMemoryBufferCreated,
+ base::Unretained(this),
+ request_id,
+ handle));
+}
+
+void BrowserGpuChannelHostFactory::OnGpuMemoryBufferCreated(
+ uint32 request_id,
+ const gfx::GpuMemoryBufferHandle& handle) {
+ CreateGpuMemoryBufferCallbackMap::iterator iter =
+ create_gpu_memory_buffer_requests_.find(request_id);
+ DCHECK(iter != create_gpu_memory_buffer_requests_.end());
+ iter->second.Run(handle);
+ create_gpu_memory_buffer_requests_.erase(iter);
+}
+
+void BrowserGpuChannelHostFactory::DestroyGpuMemoryBufferOnIO(
+ const gfx::GpuMemoryBufferHandle& handle,
+ int32 sync_point) {
+ GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
+ if (!host)
+ return;
+
+ host->DestroyGpuMemoryBuffer(handle, sync_point);
+}
+
} // namespace content
diff --git a/chromium/content/browser/gpu/browser_gpu_channel_host_factory.h b/chromium/content/browser/gpu/browser_gpu_channel_host_factory.h
index 8b461e4aec5..dc85f67ff45 100644
--- a/chromium/content/browser/gpu/browser_gpu_channel_host_factory.h
+++ b/chromium/content/browser/gpu/browser_gpu_channel_host_factory.h
@@ -9,15 +9,15 @@
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
-#include "base/process/process.h"
-#include "base/synchronization/waitable_event.h"
#include "content/common/gpu/client/gpu_channel_host.h"
-#include "ipc/ipc_channel_handle.h"
+#include "content/common/gpu/client/gpu_memory_buffer_factory_host.h"
+#include "ipc/message_filter.h"
namespace content {
class CONTENT_EXPORT BrowserGpuChannelHostFactory
- : public GpuChannelHostFactory {
+ : public GpuChannelHostFactory,
+ public GpuMemoryBufferFactoryHost {
public:
static void Initialize(bool establish_gpu_channel);
static void Terminate();
@@ -27,12 +27,12 @@ class CONTENT_EXPORT BrowserGpuChannelHostFactory
virtual bool IsMainThread() OVERRIDE;
virtual base::MessageLoop* GetMainLoop() OVERRIDE;
virtual scoped_refptr<base::MessageLoopProxy> GetIOLoopProxy() OVERRIDE;
- virtual base::WaitableEvent* GetShutDownEvent() OVERRIDE;
virtual scoped_ptr<base::SharedMemory> AllocateSharedMemory(
size_t size) OVERRIDE;
- virtual int32 CreateViewCommandBuffer(
+ virtual CreateCommandBufferResult CreateViewCommandBuffer(
int32 surface_id,
- const GPUCreateCommandBufferConfig& init_params) OVERRIDE;
+ const GPUCreateCommandBufferConfig& init_params,
+ int32 route_id) OVERRIDE;
virtual void CreateImage(
gfx::PluginWindowHandle window,
int32 image_id,
@@ -41,7 +41,18 @@ class CONTENT_EXPORT BrowserGpuChannelHostFactory
virtual scoped_ptr<gfx::GpuMemoryBuffer> AllocateGpuMemoryBuffer(
size_t width,
size_t height,
- unsigned internalformat) OVERRIDE;
+ unsigned internalformat,
+ unsigned usage) OVERRIDE;
+
+ // GpuMemoryBufferFactoryHost implementation.
+ virtual void CreateGpuMemoryBuffer(
+ const gfx::GpuMemoryBufferHandle& handle,
+ const gfx::Size& size,
+ unsigned internalformat,
+ unsigned usage,
+ const CreateGpuMemoryBufferCallback& callback) OVERRIDE;
+ virtual void DestroyGpuMemoryBuffer(const gfx::GpuMemoryBufferHandle& handle,
+ int32 sync_point) OVERRIDE;
// Specify a task runner and callback to be used for a set of messages. The
// callback will be set up on the current GpuProcessHost, identified by
@@ -63,47 +74,10 @@ class CONTENT_EXPORT BrowserGpuChannelHostFactory
static bool CanUseForTesting();
private:
- struct CreateRequest {
- CreateRequest();
- ~CreateRequest();
- base::WaitableEvent event;
- int gpu_host_id;
- int32 route_id;
- };
-
- class EstablishRequest : public base::RefCountedThreadSafe<EstablishRequest> {
- public:
- explicit EstablishRequest(CauseForGpuLaunch cause,
- int gpu_client_id,
- int gpu_host_id);
- void Wait();
- void Cancel();
-
- int gpu_host_id() { return gpu_host_id_; }
- IPC::ChannelHandle& channel_handle() { return channel_handle_; }
- gpu::GPUInfo gpu_info() { return gpu_info_; }
-
- private:
- friend class base::RefCountedThreadSafe<EstablishRequest>;
- ~EstablishRequest();
- void EstablishOnIO();
- void OnEstablishedOnIO(const IPC::ChannelHandle& channel_handle,
- const gpu::GPUInfo& gpu_info);
- void FinishOnIO();
- void FinishOnMain();
-
- base::WaitableEvent event_;
- CauseForGpuLaunch cause_for_gpu_launch_;
- const int gpu_client_id_;
- int gpu_host_id_;
- bool reused_gpu_process_;
- IPC::ChannelHandle channel_handle_;
- gpu::GPUInfo gpu_info_;
- bool finished_;
- scoped_refptr<base::MessageLoopProxy> main_loop_;
- };
-
- explicit BrowserGpuChannelHostFactory(bool establish_gpu_channel);
+ struct CreateRequest;
+ class EstablishRequest;
+
+ BrowserGpuChannelHostFactory();
virtual ~BrowserGpuChannelHostFactory();
void GpuChannelEstablished();
@@ -111,7 +85,8 @@ class CONTENT_EXPORT BrowserGpuChannelHostFactory
CreateRequest* request,
int32 surface_id,
const GPUCreateCommandBufferConfig& init_params);
- static void CommandBufferCreatedOnIO(CreateRequest* request, int32 route_id);
+ static void CommandBufferCreatedOnIO(CreateRequest* request,
+ CreateCommandBufferResult result);
void CreateImageOnIO(
gfx::PluginWindowHandle window,
int32 image_id,
@@ -121,9 +96,22 @@ class CONTENT_EXPORT BrowserGpuChannelHostFactory
static void OnImageCreated(
const CreateImageCallback& callback, const gfx::Size size);
void DeleteImageOnIO(int32 image_id, int32 sync_point);
- static void AddFilterOnIO(
- int gpu_host_id,
- scoped_refptr<IPC::ChannelProxy::MessageFilter> filter);
+ static void AddFilterOnIO(int gpu_host_id,
+ scoped_refptr<IPC::MessageFilter> filter);
+
+ void CreateGpuMemoryBufferOnIO(const gfx::GpuMemoryBufferHandle& handle,
+ const gfx::Size& size,
+ unsigned internalformat,
+ unsigned usage,
+ uint32 request_id);
+ void GpuMemoryBufferCreatedOnIO(
+ uint32 request_id,
+ const gfx::GpuMemoryBufferHandle& handle);
+ void OnGpuMemoryBufferCreated(
+ uint32 request_id,
+ const gfx::GpuMemoryBufferHandle& handle);
+ void DestroyGpuMemoryBufferOnIO(const gfx::GpuMemoryBufferHandle& handle,
+ int32 sync_point);
const int gpu_client_id_;
scoped_ptr<base::WaitableEvent> shutdown_event_;
@@ -132,6 +120,11 @@ class CONTENT_EXPORT BrowserGpuChannelHostFactory
scoped_refptr<EstablishRequest> pending_request_;
std::vector<base::Closure> established_callbacks_;
+ uint32 next_create_gpu_memory_buffer_request_id_;
+ typedef std::map<uint32, CreateGpuMemoryBufferCallback>
+ CreateGpuMemoryBufferCallbackMap;
+ CreateGpuMemoryBufferCallbackMap create_gpu_memory_buffer_requests_;
+
static BrowserGpuChannelHostFactory* instance_;
DISALLOW_COPY_AND_ASSIGN(BrowserGpuChannelHostFactory);
diff --git a/chromium/content/browser/gpu/compositor_util.cc b/chromium/content/browser/gpu/compositor_util.cc
index 73af1304a42..83ab50cbb29 100644
--- a/chromium/content/browser/gpu/compositor_util.cc
+++ b/chromium/content/browser/gpu/compositor_util.cc
@@ -6,6 +6,7 @@
#include "base/command_line.h"
#include "base/logging.h"
+#include "base/metrics/field_trial.h"
#include "build/build_config.h"
#include "cc/base/switches.h"
#include "content/browser/gpu/gpu_data_manager_impl.h"
@@ -16,20 +17,37 @@ namespace content {
namespace {
+static bool IsGpuRasterizationBlacklisted() {
+ GpuDataManagerImpl* manager = GpuDataManagerImpl::GetInstance();
+ bool field_trial_enabled =
+ (base::FieldTrialList::FindFullName(
+ "GpuRasterizationExpandedDeviceWhitelist") == "Enabled");
+
+ if (field_trial_enabled) {
+ return manager->IsFeatureBlacklisted(
+ gpu::GPU_FEATURE_TYPE_GPU_RASTERIZATION) &&
+ manager->IsFeatureBlacklisted(
+ gpu::GPU_FEATURE_TYPE_GPU_RASTERIZATION_FIELD_TRIAL);
+ }
+
+ return manager->IsFeatureBlacklisted(
+ gpu::GPU_FEATURE_TYPE_GPU_RASTERIZATION);
+}
+
+const char* kGpuCompositingFeatureName = "gpu_compositing";
+const char* kWebGLFeatureName = "webgl";
+const char* kRasterizationFeatureName = "rasterization";
+const char* kThreadedRasterizationFeatureName = "threaded_rasterization";
+
struct GpuFeatureInfo {
std::string name;
- uint32 blocked;
+ bool blocked;
bool disabled;
std::string disabled_description;
bool fallback_to_software;
};
-#if defined(OS_CHROMEOS)
-const size_t kNumFeatures = 14;
-#else
-const size_t kNumFeatures = 13;
-#endif
-const GpuFeatureInfo GetGpuFeatureInfo(size_t index) {
+const GpuFeatureInfo GetGpuFeatureInfo(size_t index, bool* eof) {
const CommandLine& command_line = *CommandLine::ForCurrentProcess();
GpuDataManagerImpl* manager = GpuDataManagerImpl::GetInstance();
@@ -46,57 +64,28 @@ const GpuFeatureInfo GetGpuFeatureInfo(size_t index) {
true
},
{
- "compositing",
- manager->IsFeatureBlacklisted(
- gpu::GPU_FEATURE_TYPE_ACCELERATED_COMPOSITING),
- command_line.HasSwitch(switches::kDisableAcceleratedCompositing),
- "Accelerated compositing has been disabled, either via about:flags or"
- " command line. This adversely affects performance of all hardware"
- " accelerated features.",
- true
- },
- {
- "3d_css",
- manager->IsFeatureBlacklisted(
- gpu::GPU_FEATURE_TYPE_ACCELERATED_COMPOSITING) ||
- manager->IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_3D_CSS),
- command_line.HasSwitch(switches::kDisableAcceleratedLayers),
- "Accelerated layers have been disabled at the command line.",
- false
- },
- {
- "css_animation",
- manager->IsFeatureBlacklisted(
- gpu::GPU_FEATURE_TYPE_ACCELERATED_COMPOSITING) ||
- manager->IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_3D_CSS),
- command_line.HasSwitch(cc::switches::kDisableThreadedAnimation) ||
- command_line.HasSwitch(switches::kDisableAcceleratedCompositing) ||
- command_line.HasSwitch(switches::kDisableAcceleratedLayers),
- "Accelerated CSS animation has been disabled at the command line.",
+ kGpuCompositingFeatureName,
+ manager->IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_GPU_COMPOSITING),
+ false,
+ "Gpu compositing has been disabled, either via about:flags or"
+ " command line. The browser will fall back to software compositing"
+ " and hardware acceleration will be unavailable.",
true
},
{
- "webgl",
+ kWebGLFeatureName,
manager->IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_WEBGL),
command_line.HasSwitch(switches::kDisableExperimentalWebGL),
"WebGL has been disabled, either via about:flags or command line.",
false
},
{
- "multisampling",
- manager->IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_MULTISAMPLING),
- command_line.HasSwitch(switches::kDisableGLMultisampling),
- "Multisampling has been disabled, either via about:flags or command"
- " line.",
- false
- },
- {
"flash_3d",
manager->IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_FLASH3D),
command_line.HasSwitch(switches::kDisableFlash3d),
"Using 3d in flash has been disabled, either via about:flags or"
" command line.",
- false
+ true
},
{
"flash_stage3d",
@@ -104,7 +93,7 @@ const GpuFeatureInfo GetGpuFeatureInfo(size_t index) {
command_line.HasSwitch(switches::kDisableFlashStage3d),
"Using Stage3d in Flash has been disabled, either via about:flags or"
" command line.",
- false
+ true
},
{
"flash_stage3d_baseline",
@@ -114,15 +103,7 @@ const GpuFeatureInfo GetGpuFeatureInfo(size_t index) {
command_line.HasSwitch(switches::kDisableFlashStage3d),
"Using Stage3d Baseline profile in Flash has been disabled, either"
" via about:flags or command line.",
- false
- },
- {
- "texture_sharing",
- manager->IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_TEXTURE_SHARING),
- command_line.HasSwitch(switches::kDisableImageTransportSurface),
- "Sharing textures between processes has been disabled, either via"
- " about:flags or command line.",
- false
+ true
},
{
"video_decode",
@@ -144,16 +125,6 @@ const GpuFeatureInfo GetGpuFeatureInfo(size_t index) {
true
},
#endif
- {
- "video",
- manager->IsFeatureBlacklisted(
- gpu::GPU_FEATURE_TYPE_ACCELERATED_VIDEO),
- command_line.HasSwitch(switches::kDisableAcceleratedVideo) ||
- command_line.HasSwitch(switches::kDisableAcceleratedCompositing),
- "Accelerated video presentation has been disabled, either via"
- " about:flags or command line.",
- true
- },
#if defined(OS_CHROMEOS)
{
"panel_fitting",
@@ -165,102 +136,64 @@ const GpuFeatureInfo GetGpuFeatureInfo(size_t index) {
},
#endif
{
- "force_compositing_mode",
- manager->IsFeatureBlacklisted(
- gpu::GPU_FEATURE_TYPE_FORCE_COMPOSITING_MODE) &&
- !IsForceCompositingModeEnabled(),
- !IsForceCompositingModeEnabled() &&
- !manager->IsFeatureBlacklisted(
- gpu::GPU_FEATURE_TYPE_FORCE_COMPOSITING_MODE),
- "Force compositing mode is off, either disabled at the command"
- " line or not supported by the current system.",
- false
+ kRasterizationFeatureName,
+ IsGpuRasterizationBlacklisted() &&
+ !IsGpuRasterizationEnabled() && !IsForceGpuRasterizationEnabled(),
+ !IsGpuRasterizationEnabled() && !IsForceGpuRasterizationEnabled() &&
+ !IsGpuRasterizationBlacklisted(),
+ "Accelerated rasterization has been disabled, either via about:flags"
+ " or command line.",
+ true
},
+ {
+ kThreadedRasterizationFeatureName,
+ false,
+ !IsImplSidePaintingEnabled(),
+ "Threaded rasterization has not been enabled or"
+ " is not supported by the current system.",
+ false
+ }
+
};
+ DCHECK(index < arraysize(kGpuFeatureInfo));
+ *eof = (index == arraysize(kGpuFeatureInfo) - 1);
return kGpuFeatureInfo[index];
}
-bool CanDoAcceleratedCompositing() {
- const GpuDataManagerImpl* manager = GpuDataManagerImpl::GetInstance();
-
- // Don't use force compositing mode if gpu access has been blocked or
- // accelerated compositing is blacklisted.
- if (!manager->GpuAccessAllowed(NULL) ||
- manager->IsFeatureBlacklisted(
- gpu::GPU_FEATURE_TYPE_ACCELERATED_COMPOSITING))
- return false;
-
- // Check for SwiftShader.
- if (manager->ShouldUseSwiftShader())
- return false;
-
- const CommandLine& command_line = *CommandLine::ForCurrentProcess();
- if (command_line.HasSwitch(switches::kDisableAcceleratedCompositing))
- return false;
-
- return true;
-}
-
-bool IsForceCompositingModeBlacklisted() {
- return GpuDataManagerImpl::GetInstance()->IsFeatureBlacklisted(
- gpu::GPU_FEATURE_TYPE_FORCE_COMPOSITING_MODE);
-}
-
} // namespace
-bool IsThreadedCompositingEnabled() {
+bool IsPinchVirtualViewportEnabled() {
const CommandLine& command_line = *CommandLine::ForCurrentProcess();
- // Command line switches take precedence over blacklist.
- if (command_line.HasSwitch(switches::kDisableForceCompositingMode) ||
- command_line.HasSwitch(switches::kDisableThreadedCompositing)) {
+ // Command line switches take precedence over platform default.
+ if (command_line.HasSwitch(cc::switches::kDisablePinchVirtualViewport))
return false;
- } else if (command_line.HasSwitch(switches::kEnableThreadedCompositing)) {
+ if (command_line.HasSwitch(cc::switches::kEnablePinchVirtualViewport))
return true;
- }
-#if defined(USE_AURA)
- // We always want threaded compositing on Aura.
- return true;
-#endif
-
- if (!CanDoAcceleratedCompositing() || IsForceCompositingModeBlacklisted())
- return false;
-
-#if defined(OS_MACOSX) || defined(OS_WIN)
- // Windows Vista+ has been shipping with TCM enabled at 100% since M24 and
- // Mac OSX 10.8+ since M28. The blacklist check above takes care of returning
- // false before this hits on unsupported Win/Mac versions.
+#if defined(OS_CHROMEOS)
return true;
-#endif
-
+#else
return false;
+#endif
}
-bool IsForceCompositingModeEnabled() {
- // Force compositing mode is a subset of threaded compositing mode.
- if (IsThreadedCompositingEnabled())
- return true;
-
+bool IsThreadedCompositingEnabled() {
const CommandLine& command_line = *CommandLine::ForCurrentProcess();
- // Command line switches take precedence over blacklisting.
- if (command_line.HasSwitch(switches::kDisableForceCompositingMode))
+ // Command line switches take precedence over blacklist.
+ if (command_line.HasSwitch(switches::kDisableThreadedCompositing))
return false;
- else if (command_line.HasSwitch(switches::kForceCompositingMode))
+ if (command_line.HasSwitch(switches::kEnableThreadedCompositing))
return true;
- if (!CanDoAcceleratedCompositing() || IsForceCompositingModeBlacklisted())
- return false;
-
-#if defined(OS_MACOSX) || defined(OS_WIN)
- // Windows Vista+ has been shipping with TCM enabled at 100% since M24 and
- // Mac OSX 10.8+ since M28. The blacklist check above takes care of returning
- // false before this hits on unsupported Win/Mac versions.
+#if defined(USE_AURA) || defined(OS_MACOSX)
+ // We always want threaded compositing on Aura and Mac (the fallback is a
+ // threaded software compositor).
return true;
-#endif
-
+#else
return false;
+#endif
}
bool IsDelegatedRendererEnabled() {
@@ -277,8 +210,7 @@ bool IsDelegatedRendererEnabled() {
enabled &= !command_line.HasSwitch(switches::kDisableDelegatedRenderer);
// Needs compositing, and thread.
- if (enabled &&
- (!IsForceCompositingModeEnabled() || !IsThreadedCompositingEnabled())) {
+ if (enabled && !IsThreadedCompositingEnabled()) {
enabled = false;
LOG(ERROR) << "Disabling delegated-rendering because it needs "
<< "force-compositing-mode and threaded-compositing.";
@@ -287,21 +219,52 @@ bool IsDelegatedRendererEnabled() {
return enabled;
}
-bool IsDeadlineSchedulingEnabled() {
+bool IsImplSidePaintingEnabled() {
const CommandLine& command_line = *CommandLine::ForCurrentProcess();
- // Default to enabled.
- bool enabled = true;
+ if (command_line.HasSwitch(switches::kDisableImplSidePainting))
+ return false;
+ else if (command_line.HasSwitch(switches::kEnableImplSidePainting))
+ return true;
+ else if (command_line.HasSwitch(
+ switches::kEnableBleedingEdgeRenderingFastPaths))
+ return true;
- // Flags override.
- enabled |= command_line.HasSwitch(switches::kEnableDeadlineScheduling);
- enabled &= !command_line.HasSwitch(switches::kDisableDeadlineScheduling);
+#if defined(OS_MACOSX) || defined(OS_WIN)
+ return false;
+#else
+ return IsThreadedCompositingEnabled();
+#endif
+}
- return enabled;
+bool IsGpuRasterizationEnabled() {
+ const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+
+ if (!IsImplSidePaintingEnabled())
+ return false;
+
+ if (command_line.HasSwitch(switches::kDisableGpuRasterization))
+ return false;
+ else if (command_line.HasSwitch(switches::kEnableGpuRasterization))
+ return true;
+
+ if (IsGpuRasterizationBlacklisted()) {
+ return false;
+ }
+
+ return true;
}
-base::Value* GetFeatureStatus() {
+bool IsForceGpuRasterizationEnabled() {
const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+
+ if (!IsImplSidePaintingEnabled())
+ return false;
+
+ return command_line.HasSwitch(switches::kForceGpuRasterization);
+}
+
+base::Value* GetFeatureStatus() {
GpuDataManagerImpl* manager = GpuDataManagerImpl::GetInstance();
std::string gpu_access_blocked_reason;
bool gpu_access_blocked =
@@ -309,64 +272,47 @@ base::Value* GetFeatureStatus() {
base::DictionaryValue* feature_status_dict = new base::DictionaryValue();
- for (size_t i = 0; i < kNumFeatures; ++i) {
- const GpuFeatureInfo gpu_feature_info = GetGpuFeatureInfo(i);
- // force_compositing_mode status is part of the compositing status.
- if (gpu_feature_info.name == "force_compositing_mode")
- continue;
-
+ bool eof = false;
+ for (size_t i = 0; !eof; ++i) {
+ const GpuFeatureInfo gpu_feature_info = GetGpuFeatureInfo(i, &eof);
std::string status;
if (gpu_feature_info.disabled) {
status = "disabled";
- if (gpu_feature_info.name == "css_animation") {
- status += "_software_animated";
- } else if (gpu_feature_info.name == "raster") {
- if (cc::switches::IsImplSidePaintingEnabled())
- status += "_software_multithreaded";
- else
- status += "_software";
- } else {
- if (gpu_feature_info.fallback_to_software)
- status += "_software";
- else
- status += "_off";
- }
- } else if (manager->ShouldUseSwiftShader()) {
- status = "unavailable_software";
+ if (gpu_feature_info.fallback_to_software)
+ status += "_software";
+ else
+ status += "_off";
+ if (gpu_feature_info.name == kThreadedRasterizationFeatureName)
+ status += "_ok";
} else if (gpu_feature_info.blocked ||
gpu_access_blocked) {
status = "unavailable";
- if (gpu_feature_info.fallback_to_software)
+ if (gpu_feature_info.fallback_to_software) {
status += "_software";
- else
+ } else
status += "_off";
} else {
status = "enabled";
- if (gpu_feature_info.name == "webgl" &&
- (command_line.HasSwitch(switches::kDisableAcceleratedCompositing) ||
- manager->IsFeatureBlacklisted(
- gpu::GPU_FEATURE_TYPE_ACCELERATED_COMPOSITING)))
+ if (gpu_feature_info.name == kWebGLFeatureName &&
+ manager->IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_GPU_COMPOSITING))
status += "_readback";
- bool has_thread = IsThreadedCompositingEnabled();
- if (gpu_feature_info.name == "compositing") {
- bool force_compositing = IsForceCompositingModeEnabled();
- if (force_compositing)
+ if (gpu_feature_info.name == kRasterizationFeatureName) {
+ if (IsForceGpuRasterizationEnabled())
status += "_force";
- if (has_thread)
- status += "_threaded";
- }
- if (gpu_feature_info.name == "css_animation") {
- if (has_thread)
- status = "accelerated_threaded";
- else
- status = "accelerated";
}
+ if (gpu_feature_info.name == kThreadedRasterizationFeatureName)
+ status += "_on";
}
- // TODO(reveman): Remove this when crbug.com/223286 has been fixed.
- if (gpu_feature_info.name == "raster" &&
- cc::switches::IsImplSidePaintingEnabled()) {
- status = "disabled_software_multithreaded";
+ if (gpu_feature_info.name == kGpuCompositingFeatureName) {
+ if (IsThreadedCompositingEnabled())
+ status += "_threaded";
}
+ if (gpu_feature_info.name == kWebGLFeatureName &&
+ (gpu_feature_info.blocked || gpu_access_blocked) &&
+ manager->ShouldUseSwiftShader()) {
+ status = "unavailable_software";
+ }
+
feature_status_dict->SetString(
gpu_feature_info.name.c_str(), status.c_str());
}
@@ -388,17 +334,26 @@ base::Value* GetProblems() {
"GPU process was unable to boot: " + gpu_access_blocked_reason);
problem->Set("crBugs", new base::ListValue());
problem->Set("webkitBugs", new base::ListValue());
+ base::ListValue* disabled_features = new base::ListValue();
+ disabled_features->AppendString("all");
+ problem->Set("affectedGpuSettings", disabled_features);
+ problem->SetString("tag", "disabledFeatures");
problem_list->Insert(0, problem);
}
- for (size_t i = 0; i < kNumFeatures; ++i) {
- const GpuFeatureInfo gpu_feature_info = GetGpuFeatureInfo(i);
+ bool eof = false;
+ for (size_t i = 0; !eof; ++i) {
+ const GpuFeatureInfo gpu_feature_info = GetGpuFeatureInfo(i, &eof);
if (gpu_feature_info.disabled) {
base::DictionaryValue* problem = new base::DictionaryValue();
problem->SetString(
"description", gpu_feature_info.disabled_description);
problem->Set("crBugs", new base::ListValue());
problem->Set("webkitBugs", new base::ListValue());
+ base::ListValue* disabled_features = new base::ListValue();
+ disabled_features->AppendString(gpu_feature_info.name);
+ problem->Set("affectedGpuSettings", disabled_features);
+ problem->SetString("tag", "disabledFeatures");
problem_list->Append(problem);
}
}
diff --git a/chromium/content/browser/gpu/compositor_util.h b/chromium/content/browser/gpu/compositor_util.h
index 89329f42f71..72a3ac0d42d 100644
--- a/chromium/content/browser/gpu/compositor_util.h
+++ b/chromium/content/browser/gpu/compositor_util.h
@@ -10,17 +10,28 @@
namespace content {
+// Note: When adding a function here, please make sure the logic is not
+// duplicated in the renderer.
+
+// Returns true if the virtual viewport model of pinch-to-zoom is on (via
+// flags, or platform default).
+CONTENT_EXPORT bool IsPinchVirtualViewportEnabled();
+
// Returns true if the threaded compositor is on (via flags or field trial).
CONTENT_EXPORT bool IsThreadedCompositingEnabled();
-// Returns true if force-compositing-mode is on (via flags or field trial).
-CONTENT_EXPORT bool IsForceCompositingModeEnabled();
-
// Returns true if delegated-renderer is on (via flags, or platform default).
CONTENT_EXPORT bool IsDelegatedRendererEnabled();
-// Returns true if deadline scheduling is on (via flags, or platform default).
-CONTENT_EXPORT bool IsDeadlineSchedulingEnabled();
+// Returns true if impl-side painting is on (via flags, or platform default)
+// for the renderer.
+CONTENT_EXPORT bool IsImplSidePaintingEnabled();
+
+// Returns true if gpu rasterization is on (via flags) for the renderer.
+CONTENT_EXPORT bool IsGpuRasterizationEnabled();
+
+// Returns true if force-gpu-rasterization is on (via flags) for the renderer.
+CONTENT_EXPORT bool IsForceGpuRasterizationEnabled();
CONTENT_EXPORT base::Value* GetFeatureStatus();
CONTENT_EXPORT base::Value* GetProblems();
diff --git a/chromium/content/browser/gpu/compositor_util_browsertest.cc b/chromium/content/browser/gpu/compositor_util_browsertest.cc
index ac39daceed3..1f3b9f3f576 100644
--- a/chromium/content/browser/gpu/compositor_util_browsertest.cc
+++ b/chromium/content/browser/gpu/compositor_util_browsertest.cc
@@ -3,7 +3,7 @@
// found in the LICENSE file.
#include "content/browser/gpu/compositor_util.h"
-#include "content/test/content_browser_test.h"
+#include "content/public/test/content_browser_test.h"
#if defined(OS_MACOSX)
#include "base/mac/mac_util.h"
@@ -27,7 +27,7 @@ IN_PROC_BROWSER_TEST_F(CompositorUtilTest, CompositingModeAsExpected) {
#if defined(USE_AURA)
expected_mode = DELEGATED;
#elif defined(OS_ANDROID)
- expected_mode = THREADED;
+ expected_mode = DELEGATED;
#elif defined(OS_MACOSX)
expected_mode = THREADED;
// Lion and SnowLeopard have compositing blacklisted when using the Apple
@@ -41,10 +41,6 @@ IN_PROC_BROWSER_TEST_F(CompositorUtilTest, CompositingModeAsExpected) {
expected_mode = THREADED;
#endif
- EXPECT_EQ(expected_mode == ENABLED ||
- expected_mode == THREADED ||
- expected_mode == DELEGATED,
- IsForceCompositingModeEnabled());
EXPECT_EQ(expected_mode == THREADED ||
expected_mode == DELEGATED,
IsThreadedCompositingEnabled());
diff --git a/chromium/content/browser/gpu/gpu_data_manager_impl.cc b/chromium/content/browser/gpu/gpu_data_manager_impl.cc
index 18b1a71c16e..93d15c71108 100644
--- a/chromium/content/browser/gpu/gpu_data_manager_impl.cc
+++ b/chromium/content/browser/gpu/gpu_data_manager_impl.cc
@@ -139,19 +139,19 @@ void GpuDataManagerImpl::UpdateVideoMemoryUsageStats(
}
void GpuDataManagerImpl::AppendRendererCommandLine(
- CommandLine* command_line) const {
+ base::CommandLine* command_line) const {
base::AutoLock auto_lock(lock_);
private_->AppendRendererCommandLine(command_line);
}
void GpuDataManagerImpl::AppendGpuCommandLine(
- CommandLine* command_line) const {
+ base::CommandLine* command_line) const {
base::AutoLock auto_lock(lock_);
private_->AppendGpuCommandLine(command_line);
}
void GpuDataManagerImpl::AppendPluginCommandLine(
- CommandLine* command_line) const {
+ base::CommandLine* command_line) const {
base::AutoLock auto_lock(lock_);
private_->AppendPluginCommandLine(command_line);
}
@@ -206,13 +206,6 @@ void GpuDataManagerImpl::HandleGpuSwitch() {
private_->HandleGpuSwitch();
}
-#if defined(OS_WIN)
-bool GpuDataManagerImpl::IsUsingAcceleratedSurface() const {
- base::AutoLock auto_lock(lock_);
- return private_->IsUsingAcceleratedSurface();
-}
-#endif
-
void GpuDataManagerImpl::BlockDomainFrom3DAPIs(
const GURL& url, DomainGuilt guilt) {
base::AutoLock auto_lock(lock_);
@@ -248,6 +241,11 @@ unsigned int GpuDataManagerImpl::GetDisplayCount() const {
return private_->GetDisplayCount();
}
+bool GpuDataManagerImpl::UpdateActiveGpu(uint32 vendor_id, uint32 device_id) {
+ base::AutoLock auto_lock(lock_);
+ return private_->UpdateActiveGpu(vendor_id, device_id);
+}
+
void GpuDataManagerImpl::Notify3DAPIBlocked(const GURL& url,
int render_process_id,
int render_view_id,
diff --git a/chromium/content/browser/gpu/gpu_data_manager_impl.h b/chromium/content/browser/gpu/gpu_data_manager_impl.h
index 7fb75a40894..5a783e46c75 100644
--- a/chromium/content/browser/gpu/gpu_data_manager_impl.h
+++ b/chromium/content/browser/gpu/gpu_data_manager_impl.h
@@ -22,10 +22,13 @@
#include "content/public/common/three_d_api_types.h"
#include "gpu/config/gpu_info.h"
-class CommandLine;
class GURL;
struct WebPreferences;
+namespace base {
+class CommandLine;
+}
+
namespace content {
class GpuDataManagerImplPrivate;
@@ -95,15 +98,14 @@ class CONTENT_EXPORT GpuDataManagerImpl
// Insert disable-feature switches corresponding to preliminary gpu feature
// flags into the renderer process command line.
- void AppendRendererCommandLine(CommandLine* command_line) const;
+ void AppendRendererCommandLine(base::CommandLine* command_line) const;
- // Insert switches into gpu process command line: kUseGL,
- // kDisableGLMultisampling.
- void AppendGpuCommandLine(CommandLine* command_line) const;
+ // Insert switches into gpu process command line: kUseGL, etc.
+ void AppendGpuCommandLine(base::CommandLine* command_line) const;
// Insert switches into plugin process command line:
// kDisableCoreAnimationPlugins.
- void AppendPluginCommandLine(CommandLine* command_line) const;
+ void AppendPluginCommandLine(base::CommandLine* command_line) const;
// Update WebPreferences for renderer based on blacklisting decisions.
void UpdateRendererWebPrefs(WebPreferences* prefs) const;
@@ -133,12 +135,6 @@ class CONTENT_EXPORT GpuDataManagerImpl
// Called when switching gpu.
void HandleGpuSwitch();
-#if defined(OS_WIN)
- // Is the GPU process using the accelerated surface to present, instead of
- // presenting by itself.
- bool IsUsingAcceleratedSurface() const;
-#endif
-
// Maintenance of domains requiring explicit user permission before
// using client-facing 3D APIs (WebGL, Pepper 3D), either because
// the domain has caused the GPU to reset, or because too many GPU
@@ -170,6 +166,10 @@ class CONTENT_EXPORT GpuDataManagerImpl
void SetDisplayCount(unsigned int display_count);
unsigned int GetDisplayCount() const;
+ // Set the active gpu.
+ // Return true if it's a different GPU from the previous active one.
+ bool UpdateActiveGpu(uint32 vendor_id, uint32 device_id);
+
// Called when GPU process initialization failed.
void OnGpuProcessInitFailure();
diff --git a/chromium/content/browser/gpu/gpu_data_manager_impl_private.cc b/chromium/content/browser/gpu/gpu_data_manager_impl_private.cc
index a9dfa8c8f50..2c5b91e0142 100644
--- a/chromium/content/browser/gpu/gpu_data_manager_impl_private.cc
+++ b/chromium/content/browser/gpu/gpu_data_manager_impl_private.cc
@@ -145,29 +145,20 @@ void UpdateStats(const gpu::GPUInfo& gpu_info,
const gpu::GpuFeatureType kGpuFeatures[] = {
gpu::GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS,
- gpu::GPU_FEATURE_TYPE_ACCELERATED_COMPOSITING,
- gpu::GPU_FEATURE_TYPE_WEBGL,
- gpu::GPU_FEATURE_TYPE_TEXTURE_SHARING
- };
+ gpu::GPU_FEATURE_TYPE_GPU_COMPOSITING, gpu::GPU_FEATURE_TYPE_WEBGL};
const std::string kGpuBlacklistFeatureHistogramNames[] = {
"GPU.BlacklistFeatureTestResults.Accelerated2dCanvas",
- "GPU.BlacklistFeatureTestResults.AcceleratedCompositing",
- "GPU.BlacklistFeatureTestResults.Webgl",
- "GPU.BlacklistFeatureTestResults.TextureSharing"
- };
+ "GPU.BlacklistFeatureTestResults.GpuCompositing",
+ "GPU.BlacklistFeatureTestResults.Webgl", };
const bool kGpuFeatureUserFlags[] = {
command_line.HasSwitch(switches::kDisableAccelerated2dCanvas),
- command_line.HasSwitch(switches::kDisableAcceleratedCompositing),
- command_line.HasSwitch(switches::kDisableExperimentalWebGL),
- command_line.HasSwitch(switches::kDisableImageTransportSurface)
- };
+ command_line.HasSwitch(switches::kDisableGpu),
+ command_line.HasSwitch(switches::kDisableExperimentalWebGL), };
#if defined(OS_WIN)
const std::string kGpuBlacklistFeatureHistogramNamesWin[] = {
"GPU.BlacklistFeatureTestResultsWindows.Accelerated2dCanvas",
- "GPU.BlacklistFeatureTestResultsWindows.AcceleratedCompositing",
- "GPU.BlacklistFeatureTestResultsWindows.Webgl",
- "GPU.BlacklistFeatureTestResultsWindows.TextureSharing"
- };
+ "GPU.BlacklistFeatureTestResultsWindows.GpuCompositing",
+ "GPU.BlacklistFeatureTestResultsWindows.Webgl", };
#endif
const size_t kNumFeatures =
sizeof(kGpuFeatures) / sizeof(gpu::GpuFeatureType);
@@ -221,42 +212,82 @@ void DisplayReconfigCallback(CGDirectDisplayID display,
reinterpret_cast<GpuDataManagerImpl*>(gpu_data_manager);
DCHECK(manager);
+ // Display change.
+ bool display_changed = false;
uint32_t displayCount;
CGGetActiveDisplayList(0, NULL, &displayCount);
-
- bool fireGpuSwitch = flags & kCGDisplayAddFlag;
-
if (displayCount != manager->GetDisplayCount()) {
manager->SetDisplayCount(displayCount);
- fireGpuSwitch = true;
+ display_changed = true;
+ }
+
+ // Gpu change.
+ bool gpu_changed = false;
+ if (flags & kCGDisplayAddFlag) {
+ uint32 vendor_id, device_id;
+ if (gpu::CollectGpuID(&vendor_id, &device_id) == gpu::kGpuIDSuccess) {
+ gpu_changed = manager->UpdateActiveGpu(vendor_id, device_id);
+ }
}
- if (fireGpuSwitch)
+ if (display_changed || gpu_changed)
manager->HandleGpuSwitch();
}
#endif // OS_MACOSX
#if defined(OS_ANDROID)
void ApplyAndroidWorkarounds(const gpu::GPUInfo& gpu_info,
- CommandLine* command_line) {
+ CommandLine* command_line,
+ std::set<int>* workarounds) {
std::string vendor(StringToLowerASCII(gpu_info.gl_vendor));
std::string renderer(StringToLowerASCII(gpu_info.gl_renderer));
+ std::string version(StringToLowerASCII(gpu_info.gl_version));
+
bool is_img =
gpu_info.gl_vendor.find("Imagination") != std::string::npos;
gfx::DeviceDisplayInfo info;
int default_tile_size = 256;
- // For very high resolution displays (eg. Nexus 10), set the default
- // tile size to be 512. This should be removed in favour of a generic
- // hueristic that works across all platforms and devices, once that
- // exists: http://crbug.com/159524. This switches to 512 for screens
- // containing 40 or more 256x256 tiles, such that 1080p devices do
- // not use 512x512 tiles (eg. 1920x1280 requires 37.5 tiles)
- int numTiles = (info.GetDisplayWidth() *
- info.GetDisplayHeight()) / (256 * 256);
- if (numTiles >= 40)
- default_tile_size = 512;
+ // TODO(epenner): Now that this is somewhat generic, maybe we can
+ // unify this for all platforms (http://crbug.com/159524)
+
+ bool real_size_supported = true;
+ int display_width = info.GetPhysicalDisplayWidth();
+ int display_height = info.GetPhysicalDisplayHeight();
+ if (display_width == 0 || display_height == 0) {
+ real_size_supported = false;
+ display_width = info.GetDisplayWidth();
+ display_height = info.GetDisplayHeight();
+ }
+
+ int portrait_width = std::min(display_width, display_height);
+ int landscape_width = std::max(display_width, display_height);
+
+ if (real_size_supported) {
+ // Maximum HD dimensions should be 768x1280
+ // Maximum FHD dimensions should be 1200x1920
+ if (portrait_width > 768 || landscape_width > 1280)
+ default_tile_size = 384;
+ if (portrait_width > 1200 || landscape_width > 1920)
+ default_tile_size = 512;
+
+ // Adjust for some resolutions that barely straddle an extra
+ // tile when in portrait mode. This helps worst case scroll/raster
+ // by not needing a full extra tile for each row.
+ if (default_tile_size == 256 && portrait_width == 768)
+ default_tile_size += 32;
+ if (default_tile_size == 384 && portrait_width == 1200)
+ default_tile_size += 32;
+ } else {
+ // We don't know the exact resolution due to screen controls etc.
+ // So this just estimates the values above using tile counts.
+ int numTiles = (display_width * display_height) / (256 * 256);
+ if (numTiles > 16)
+ default_tile_size = 384;
+ if (numTiles >= 40)
+ default_tile_size = 512;
+ }
// IMG: Fast async texture uploads only work with non-power-of-two,
// but still multiple-of-eight sizes.
@@ -279,21 +310,6 @@ void ApplyAndroidWorkarounds(const gpu::GPUInfo& gpu_info,
}
#endif // OS_ANDROID
-// Overwrite force gpu workaround if a commandline switch exists.
-void AdjustGpuSwitchingOption(std::set<int>* workarounds) {
- DCHECK(workarounds);
- const CommandLine& command_line = *CommandLine::ForCurrentProcess();
- std::string option = command_line.GetSwitchValueASCII(
- switches::kGpuSwitching);
- if (option == switches::kGpuSwitchingOptionNameForceDiscrete) {
- workarounds->erase(gpu::FORCE_INTEGRATED_GPU);
- workarounds->insert(gpu::FORCE_DISCRETE_GPU);
- } else if (option == switches::kGpuSwitchingOptionNameForceIntegrated) {
- workarounds->erase(gpu::FORCE_DISCRETE_GPU);
- workarounds->insert(gpu::FORCE_INTEGRATED_GPU);
- }
-}
-
// Block all domains' use of 3D APIs for this many milliseconds if
// approaching a threshold where system stability might be compromised.
const int64 kBlockAllDomainsMs = 10000;
@@ -322,9 +338,6 @@ void GpuDataManagerImplPrivate::InitializeForTesting(
}
bool GpuDataManagerImplPrivate::IsFeatureBlacklisted(int feature) const {
- if (CommandLine::ForCurrentProcess()->HasSwitch("chrome-frame") &&
- feature == gpu::GPU_FEATURE_TYPE_TEXTURE_SHARING)
- return false;
#if defined(OS_CHROMEOS)
if (feature == gpu::GPU_FEATURE_TYPE_PANEL_FITTING &&
CommandLine::ForCurrentProcess()->HasSwitch(
@@ -506,14 +519,14 @@ void GpuDataManagerImplPrivate::SetGLStrings(const std::string& gl_vendor,
// situation where GPU process collected GL strings before this call.
if (!gpu_info_.gl_vendor.empty() ||
!gpu_info_.gl_renderer.empty() ||
- !gpu_info_.gl_version_string.empty())
+ !gpu_info_.gl_version.empty())
return;
gpu::GPUInfo gpu_info = gpu_info_;
gpu_info.gl_vendor = gl_vendor;
gpu_info.gl_renderer = gl_renderer;
- gpu_info.gl_version_string = gl_version;
+ gpu_info.gl_version = gl_version;
gpu::CollectDriverInfoGL(&gpu_info);
@@ -529,7 +542,7 @@ void GpuDataManagerImplPrivate::GetGLStrings(std::string* gl_vendor,
*gl_vendor = gpu_info_.gl_vendor;
*gl_renderer = gpu_info_.gl_renderer;
- *gl_version = gpu_info_.gl_version_string;
+ *gl_version = gpu_info_.gl_version;
}
void GpuDataManagerImplPrivate::Initialize() {
@@ -581,16 +594,7 @@ void GpuDataManagerImplPrivate::Initialize() {
gpu_info);
}
-void GpuDataManagerImplPrivate::UpdateGpuInfo(const gpu::GPUInfo& gpu_info) {
- // No further update of gpu_info if falling back to SwiftShader.
- if (use_swiftshader_)
- return;
-
- gpu::MergeGPUInfo(&gpu_info_, gpu_info);
- gpu::DetermineActiveGPU(&gpu_info_);
- complete_gpu_info_already_requested_ =
- complete_gpu_info_already_requested_ || gpu_info_.finalized;
-
+void GpuDataManagerImplPrivate::UpdateGpuInfoHelper() {
GetContentClient()->SetGpuInfo(gpu_info_);
if (gpu_blacklist_) {
@@ -605,12 +609,25 @@ void GpuDataManagerImplPrivate::UpdateGpuInfo(const gpu::GPUInfo& gpu_info) {
gpu_driver_bugs_ = gpu_driver_bug_list_->MakeDecision(
gpu::GpuControlList::kOsAny, std::string(), gpu_info_);
}
- AdjustGpuSwitchingOption(&gpu_driver_bugs_);
+ gpu::GpuDriverBugList::AppendWorkaroundsFromCommandLine(
+ &gpu_driver_bugs_, *CommandLine::ForCurrentProcess());
// We have to update GpuFeatureType before notify all the observers.
NotifyGpuInfoUpdate();
}
+void GpuDataManagerImplPrivate::UpdateGpuInfo(const gpu::GPUInfo& gpu_info) {
+ // No further update of gpu_info if falling back to SwiftShader.
+ if (use_swiftshader_)
+ return;
+
+ gpu::MergeGPUInfo(&gpu_info_, gpu_info);
+ complete_gpu_info_already_requested_ =
+ complete_gpu_info_already_requested_ || gpu_info_.finalized;
+
+ UpdateGpuInfoHelper();
+}
+
void GpuDataManagerImplPrivate::UpdateVideoMemoryUsageStats(
const GPUVideoMemoryUsageStats& video_memory_usage_stats) {
GpuDataManagerImpl::UnlockedSession session(owner_);
@@ -622,13 +639,6 @@ void GpuDataManagerImplPrivate::AppendRendererCommandLine(
CommandLine* command_line) const {
DCHECK(command_line);
- if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_WEBGL)) {
- if (!command_line->HasSwitch(switches::kDisablePepper3d))
- command_line->AppendSwitch(switches::kDisablePepper3d);
- }
- if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_ACCELERATED_COMPOSITING) &&
- !command_line->HasSwitch(switches::kDisableAcceleratedCompositing))
- command_line->AppendSwitch(switches::kDisableAcceleratedCompositing);
if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE) &&
!command_line->HasSwitch(switches::kDisableAcceleratedVideoDecode))
command_line->AppendSwitch(switches::kDisableAcceleratedVideoDecode);
@@ -638,15 +648,9 @@ void GpuDataManagerImplPrivate::AppendRendererCommandLine(
command_line->AppendSwitch(switches::kDisableWebRtcHWEncoding);
#endif
- if (use_software_compositor_ &&
- !command_line->HasSwitch(switches::kEnableSoftwareCompositing))
- command_line->AppendSwitch(switches::kEnableSoftwareCompositing);
-
#if defined(USE_AURA)
- if (!CanUseGpuBrowserCompositor()) {
+ if (!CanUseGpuBrowserCompositor())
command_line->AppendSwitch(switches::kDisableGpuCompositing);
- command_line->AppendSwitch(switches::kDisablePepper3d);
- }
#endif
}
@@ -654,20 +658,11 @@ void GpuDataManagerImplPrivate::AppendGpuCommandLine(
CommandLine* command_line) const {
DCHECK(command_line);
- bool reduce_sandbox = false;
-
std::string use_gl =
CommandLine::ForCurrentProcess()->GetSwitchValueASCII(switches::kUseGL);
base::FilePath swiftshader_path =
CommandLine::ForCurrentProcess()->GetSwitchValuePath(
switches::kSwiftShaderPath);
- if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_MULTISAMPLING) &&
- !command_line->HasSwitch(switches::kDisableGLMultisampling)) {
- command_line->AppendSwitch(switches::kDisableGLMultisampling);
- }
- if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_TEXTURE_SHARING)) {
- command_line->AppendSwitch(switches::kDisableImageTransportSurface);
- }
if (gpu_driver_bugs_.find(gpu::DISABLE_D3D11) != gpu_driver_bugs_.end())
command_line->AppendSwitch(switches::kDisableD3D11);
if (use_swiftshader_) {
@@ -675,11 +670,10 @@ void GpuDataManagerImplPrivate::AppendGpuCommandLine(
if (swiftshader_path.empty())
swiftshader_path = swiftshader_path_;
} else if ((IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_WEBGL) ||
- IsFeatureBlacklisted(
- gpu::GPU_FEATURE_TYPE_ACCELERATED_COMPOSITING) ||
+ IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_GPU_COMPOSITING) ||
IsFeatureBlacklisted(
gpu::GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS)) &&
- (use_gl == "any")) {
+ (use_gl == "any")) {
command_line->AppendSwitchASCII(
switches::kUseGL, gfx::kGLImplementationOSMesaName);
} else if (!use_gl.empty()) {
@@ -711,24 +705,6 @@ void GpuDataManagerImplPrivate::AppendGpuCommandLine(
}
#endif
-#if defined(OS_WIN)
- // DisplayLink 7.1 and earlier can cause the GPU process to crash on startup.
- // http://crbug.com/177611
- // Thinkpad USB Port Replicator driver causes GPU process to crash when the
- // sandbox is enabled. http://crbug.com/181665.
- if ((gpu_info_.display_link_version.IsValid()
- && gpu_info_.display_link_version.IsOlderThan("7.2")) ||
- gpu_info_.lenovo_dcute) {
- reduce_sandbox = true;
- }
-#endif
-
- if (gpu_info_.optimus)
- reduce_sandbox = true;
-
- if (reduce_sandbox)
- command_line->AppendSwitch(switches::kReduceGpuSandbox);
-
// Pass GPU and driver information to GPU process. We try to avoid full GPU
// info collection at GPU process startup, but we need gpu vendor_id,
// device_id, driver_vendor, driver_version for deciding whether we need to
@@ -751,9 +727,7 @@ void GpuDataManagerImplPrivate::AppendPluginCommandLine(
// TODO(jbauman): Add proper blacklist support for core animation plugins so
// special-casing this video card won't be necessary. See
// http://crbug.com/134015
- if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_ACCELERATED_COMPOSITING) ||
- CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kDisableAcceleratedCompositing)) {
+ if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_GPU_COMPOSITING)) {
if (!command_line->HasSwitch(
switches::kDisableCoreAnimationPlugins))
command_line->AppendSwitch(
@@ -766,10 +740,10 @@ void GpuDataManagerImplPrivate::UpdateRendererWebPrefs(
WebPreferences* prefs) const {
DCHECK(prefs);
- if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_ACCELERATED_COMPOSITING))
- prefs->accelerated_compositing_enabled = false;
- if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_WEBGL))
+ if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_WEBGL)) {
prefs->experimental_webgl_enabled = false;
+ prefs->pepper_3d_enabled = false;
+ }
if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_FLASH3D))
prefs->flash_3d_enabled = false;
if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_FLASH_STAGE3D)) {
@@ -780,38 +754,23 @@ void GpuDataManagerImplPrivate::UpdateRendererWebPrefs(
prefs->flash_stage3d_baseline_enabled = false;
if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS))
prefs->accelerated_2d_canvas_enabled = false;
- if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_MULTISAMPLING) ||
+ if (IsDriverBugWorkaroundActive(gpu::DISABLE_MULTISAMPLING) ||
(IsDriverBugWorkaroundActive(gpu::DISABLE_MULTIMONITOR_MULTISAMPLING) &&
display_count_ > 1))
prefs->gl_multisampling_enabled = false;
- if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_3D_CSS)) {
- prefs->accelerated_compositing_for_3d_transforms_enabled = false;
- prefs->accelerated_compositing_for_animation_enabled = false;
- }
- if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_ACCELERATED_VIDEO))
- prefs->accelerated_compositing_for_video_enabled = false;
-
- // Accelerated video and animation are slower than regular when using
- // SwiftShader. 3D CSS may also be too slow to be worthwhile.
- if (ShouldUseSwiftShader()) {
- prefs->accelerated_compositing_for_video_enabled = false;
- prefs->accelerated_compositing_for_animation_enabled = false;
- prefs->accelerated_compositing_for_3d_transforms_enabled = false;
- prefs->accelerated_compositing_for_plugins_enabled = false;
- }
-
- if (use_software_compositor_) {
- prefs->force_compositing_mode = true;
- prefs->accelerated_compositing_enabled = true;
- prefs->accelerated_compositing_for_3d_transforms_enabled = true;
- prefs->accelerated_compositing_for_plugins_enabled = true;
- prefs->accelerated_compositing_for_video_enabled = true;
- }
#if defined(USE_AURA)
- if (!CanUseGpuBrowserCompositor())
+ if (!CanUseGpuBrowserCompositor()) {
prefs->accelerated_2d_canvas_enabled = false;
+ prefs->pepper_3d_enabled = false;
+ }
#endif
+
+ if (!IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE) &&
+ !CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableAcceleratedVideoDecode)) {
+ prefs->pepper_accelerated_video_decode_enabled = true;
+ }
}
void GpuDataManagerImplPrivate::DisableHardwareAcceleration() {
@@ -839,7 +798,9 @@ std::string GpuDataManagerImplPrivate::GetDriverBugListVersion() const {
void GpuDataManagerImplPrivate::GetBlacklistReasons(
base::ListValue* reasons) const {
if (gpu_blacklist_)
- gpu_blacklist_->GetReasons(reasons);
+ gpu_blacklist_->GetReasons(reasons, "disabledFeatures");
+ if (gpu_driver_bug_list_)
+ gpu_driver_bug_list_->GetReasons(reasons, "workarounds");
}
void GpuDataManagerImplPrivate::GetDriverBugWorkarounds(
@@ -894,24 +855,40 @@ void GpuDataManagerImplPrivate::HandleGpuSwitch() {
observer_list_->Notify(&GpuDataManagerObserver::OnGpuSwitching);
}
-#if defined(OS_WIN)
-bool GpuDataManagerImplPrivate::IsUsingAcceleratedSurface() const {
- if (base::win::GetVersion() < base::win::VERSION_VISTA)
- return false;
-
- if (use_swiftshader_)
- return false;
- CommandLine* command_line = CommandLine::ForCurrentProcess();
- if (command_line->HasSwitch(switches::kDisableImageTransportSurface))
- return false;
- return !IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_TEXTURE_SHARING);
+bool GpuDataManagerImplPrivate::UpdateActiveGpu(
+ uint32 vendor_id, uint32 device_id) {
+ if (gpu_info_.gpu.vendor_id == vendor_id &&
+ gpu_info_.gpu.device_id == device_id) {
+ // The primary GPU is active.
+ if (gpu_info_.gpu.active)
+ return false;
+ gpu_info_.gpu.active = true;
+ for (size_t ii = 0; ii < gpu_info_.secondary_gpus.size(); ++ii)
+ gpu_info_.secondary_gpus[ii].active = false;
+ } else {
+ // A secondary GPU is active.
+ for (size_t ii = 0; ii < gpu_info_.secondary_gpus.size(); ++ii) {
+ if (gpu_info_.secondary_gpus[ii].vendor_id == vendor_id &&
+ gpu_info_.secondary_gpus[ii].device_id == device_id) {
+ if (gpu_info_.secondary_gpus[ii].active)
+ return false;
+ gpu_info_.secondary_gpus[ii].active = true;
+ } else {
+ gpu_info_.secondary_gpus[ii].active = false;
+ }
+ }
+ gpu_info_.gpu.active = false;
+ }
+ UpdateGpuInfoHelper();
+ return true;
}
-#endif
bool GpuDataManagerImplPrivate::CanUseGpuBrowserCompositor() const {
- return !ShouldUseSwiftShader() &&
- !IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_ACCELERATED_COMPOSITING) &&
- !IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_FORCE_COMPOSITING_MODE);
+ if (ShouldUseSwiftShader())
+ return false;
+ if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_GPU_COMPOSITING))
+ return false;
+ return true;
}
void GpuDataManagerImplPrivate::BlockDomainFrom3DAPIs(
@@ -960,22 +937,11 @@ GpuDataManagerImplPrivate::GpuDataManagerImplPrivate(
owner_(owner),
display_count_(0),
gpu_process_accessible_(true),
- use_software_compositor_(false),
finalized_(false) {
DCHECK(owner_);
CommandLine* command_line = CommandLine::ForCurrentProcess();
- if (command_line->HasSwitch(switches::kDisableAcceleratedCompositing)) {
- command_line->AppendSwitch(switches::kDisableAccelerated2dCanvas);
- command_line->AppendSwitch(switches::kDisableAcceleratedLayers);
- }
if (command_line->HasSwitch(switches::kDisableGpu))
DisableHardwareAcceleration();
- if (command_line->HasSwitch(switches::kEnableSoftwareCompositing))
- use_software_compositor_ = true;
- //TODO(jbauman): enable for Chrome OS and Linux
-#if defined(USE_AURA) && !defined(OS_CHROMEOS)
- use_software_compositor_ = true;
-#endif
#if defined(OS_MACOSX)
CGGetActiveDisplayList (0, NULL, &display_count_);
@@ -1024,36 +990,20 @@ void GpuDataManagerImplPrivate::InitializeImpl(
UpdateGpuSwitchingManager(gpu_info);
UpdatePreliminaryBlacklistedFeatures();
- CommandLine* command_line = CommandLine::ForCurrentProcess();
- // We pass down the list to GPU command buffer through commandline
- // switches at GPU process launch. However, in situations where we don't
- // have a GPU process, we append the browser process commandline.
- if (command_line->HasSwitch(switches::kSingleProcess) ||
- command_line->HasSwitch(switches::kInProcessGPU)) {
- if (!gpu_driver_bugs_.empty()) {
- command_line->AppendSwitchASCII(switches::kGpuDriverBugWorkarounds,
- IntSetToString(gpu_driver_bugs_));
- }
- }
#if defined(OS_ANDROID)
- ApplyAndroidWorkarounds(gpu_info, command_line);
+ ApplyAndroidWorkarounds(
+ gpu_info, CommandLine::ForCurrentProcess(), &gpu_driver_bugs_);
#endif // OS_ANDROID
}
void GpuDataManagerImplPrivate::UpdateBlacklistedFeatures(
const std::set<int>& features) {
- CommandLine* command_line = CommandLine::ForCurrentProcess();
blacklisted_features_ = features;
// Force disable using the GPU for these features, even if they would
// otherwise be allowed.
- if (card_blacklisted_ ||
- command_line->HasSwitch(switches::kBlacklistAcceleratedCompositing)) {
- blacklisted_features_.insert(
- gpu::GPU_FEATURE_TYPE_ACCELERATED_COMPOSITING);
- }
- if (card_blacklisted_ ||
- command_line->HasSwitch(switches::kBlacklistWebGL)) {
+ if (card_blacklisted_) {
+ blacklisted_features_.insert(gpu::GPU_FEATURE_TYPE_GPU_COMPOSITING);
blacklisted_features_.insert(gpu::GPU_FEATURE_TYPE_WEBGL);
}
diff --git a/chromium/content/browser/gpu/gpu_data_manager_impl_private.h b/chromium/content/browser/gpu/gpu_data_manager_impl_private.h
index 8332c505913..8e5514fad31 100644
--- a/chromium/content/browser/gpu/gpu_data_manager_impl_private.h
+++ b/chromium/content/browser/gpu/gpu_data_manager_impl_private.h
@@ -18,6 +18,10 @@
#include "gpu/config/gpu_blacklist.h"
#include "gpu/config/gpu_driver_bug_list.h"
+namespace base {
+class CommandLine;
+}
+
namespace content {
class CONTENT_EXPORT GpuDataManagerImplPrivate {
@@ -57,11 +61,11 @@ class CONTENT_EXPORT GpuDataManagerImplPrivate {
void UpdateVideoMemoryUsageStats(
const GPUVideoMemoryUsageStats& video_memory_usage_stats);
- void AppendRendererCommandLine(CommandLine* command_line) const;
+ void AppendRendererCommandLine(base::CommandLine* command_line) const;
- void AppendGpuCommandLine(CommandLine* command_line) const;
+ void AppendGpuCommandLine(base::CommandLine* command_line) const;
- void AppendPluginCommandLine(CommandLine* command_line) const;
+ void AppendPluginCommandLine(base::CommandLine* command_line) const;
void UpdateRendererWebPrefs(WebPreferences* prefs) const;
@@ -82,12 +86,6 @@ class CONTENT_EXPORT GpuDataManagerImplPrivate {
void HandleGpuSwitch();
-#if defined(OS_WIN)
- // Is the GPU process using the accelerated surface to present, instead of
- // presenting by itself.
- bool IsUsingAcceleratedSurface() const;
-#endif
-
bool CanUseGpuBrowserCompositor() const;
void BlockDomainFrom3DAPIs(
@@ -109,6 +107,8 @@ class CONTENT_EXPORT GpuDataManagerImplPrivate {
void SetDisplayCount(unsigned int display_count);
unsigned int GetDisplayCount() const;
+ bool UpdateActiveGpu(uint32 vendor_id, uint32 device_id);
+
void OnGpuProcessInitFailure();
virtual ~GpuDataManagerImplPrivate();
@@ -183,6 +183,8 @@ class CONTENT_EXPORT GpuDataManagerImplPrivate {
const std::string& gpu_driver_bug_list_json,
const gpu::GPUInfo& gpu_info);
+ void UpdateGpuInfoHelper();
+
void UpdateBlacklistedFeatures(const std::set<int>& features);
// This should only be called once at initialization time, when preliminary
@@ -252,8 +254,6 @@ class CONTENT_EXPORT GpuDataManagerImplPrivate {
bool gpu_process_accessible_;
- bool use_software_compositor_;
-
// True if all future Initialize calls should be ignored.
bool finalized_;
diff --git a/chromium/content/browser/gpu/gpu_data_manager_impl_private_unittest.cc b/chromium/content/browser/gpu/gpu_data_manager_impl_private_unittest.cc
index 2d70b734c4e..8955ce1f88d 100644
--- a/chromium/content/browser/gpu/gpu_data_manager_impl_private_unittest.cc
+++ b/chromium/content/browser/gpu/gpu_data_manager_impl_private_unittest.cc
@@ -8,9 +8,9 @@
#include "base/time/time.h"
#include "content/browser/gpu/gpu_data_manager_impl_private.h"
#include "content/public/browser/gpu_data_manager_observer.h"
-#include "content/public/common/gpu_feature_type.h"
-#include "content/public/common/gpu_info.h"
#include "gpu/command_buffer/service/gpu_switches.h"
+#include "gpu/config/gpu_feature_type.h"
+#include "gpu/config/gpu_info.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
@@ -41,6 +41,11 @@ class TestObserver : public GpuDataManagerObserver {
video_memory_usage_stats_updated_ = true;
}
+ void Reset() {
+ gpu_info_updated_ = false;
+ video_memory_usage_stats_updated_ = false;
+ }
+
private:
bool gpu_info_updated_;
bool video_memory_usage_stats_updated_;
@@ -165,7 +170,7 @@ TEST_F(GpuDataManagerImplPrivateTest, GpuSideBlacklisting) {
}
);
- GPUInfo gpu_info;
+ gpu::GPUInfo gpu_info;
gpu_info.gpu.vendor_id = 0x10de;
gpu_info.gpu.device_id = 0x0640;
manager->InitializeForTesting(blacklist_json, gpu_info);
@@ -173,7 +178,7 @@ TEST_F(GpuDataManagerImplPrivateTest, GpuSideBlacklisting) {
EXPECT_TRUE(manager->GpuAccessAllowed(&reason));
EXPECT_TRUE(reason.empty());
EXPECT_EQ(1u, manager->GetBlacklistedFeatureCount());
- EXPECT_TRUE(manager->IsFeatureBlacklisted(GPU_FEATURE_TYPE_WEBGL));
+ EXPECT_TRUE(manager->IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_WEBGL));
gpu_info.gl_vendor = "NVIDIA";
gpu_info.gl_renderer = "NVIDIA GeForce GT 120";
@@ -181,9 +186,9 @@ TEST_F(GpuDataManagerImplPrivateTest, GpuSideBlacklisting) {
EXPECT_FALSE(manager->GpuAccessAllowed(&reason));
EXPECT_FALSE(reason.empty());
EXPECT_EQ(2u, manager->GetBlacklistedFeatureCount());
- EXPECT_TRUE(manager->IsFeatureBlacklisted(GPU_FEATURE_TYPE_WEBGL));
+ EXPECT_TRUE(manager->IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_WEBGL));
EXPECT_TRUE(manager->IsFeatureBlacklisted(
- GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS));
+ gpu::GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS));
}
TEST_F(GpuDataManagerImplPrivateTest, GpuSideExceptions) {
@@ -213,7 +218,7 @@ TEST_F(GpuDataManagerImplPrivateTest, GpuSideExceptions) {
]
}
);
- GPUInfo gpu_info;
+ gpu::GPUInfo gpu_info;
gpu_info.gpu.vendor_id = 0x10de;
gpu_info.gpu.device_id = 0x0640;
manager->InitializeForTesting(blacklist_json, gpu_info);
@@ -238,7 +243,7 @@ TEST_F(GpuDataManagerImplPrivateTest, DisableHardwareAcceleration) {
manager->DisableHardwareAcceleration();
EXPECT_FALSE(manager->GpuAccessAllowed(&reason));
EXPECT_FALSE(reason.empty());
- EXPECT_EQ(static_cast<size_t>(NUMBER_OF_GPU_FEATURE_TYPES),
+ EXPECT_EQ(static_cast<size_t>(gpu::NUMBER_OF_GPU_FEATURE_TYPES),
manager->GetBlacklistedFeatureCount());
}
@@ -260,8 +265,8 @@ TEST_F(GpuDataManagerImplPrivateTest, SwiftShaderRendering) {
EXPECT_TRUE(manager->ShouldUseSwiftShader());
EXPECT_TRUE(manager->GpuAccessAllowed(NULL));
EXPECT_EQ(1u, manager->GetBlacklistedFeatureCount());
- EXPECT_TRUE(
- manager->IsFeatureBlacklisted(GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS));
+ EXPECT_TRUE(manager->IsFeatureBlacklisted(
+ gpu::GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS));
}
TEST_F(GpuDataManagerImplPrivateTest, SwiftShaderRendering2) {
@@ -281,8 +286,8 @@ TEST_F(GpuDataManagerImplPrivateTest, SwiftShaderRendering2) {
EXPECT_TRUE(manager->GpuAccessAllowed(NULL));
EXPECT_TRUE(manager->ShouldUseSwiftShader());
EXPECT_EQ(1u, manager->GetBlacklistedFeatureCount());
- EXPECT_TRUE(
- manager->IsFeatureBlacklisted(GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS));
+ EXPECT_TRUE(manager->IsFeatureBlacklisted(
+ gpu::GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS));
}
TEST_F(GpuDataManagerImplPrivateTest, GpuInfoUpdate) {
@@ -297,7 +302,7 @@ TEST_F(GpuDataManagerImplPrivateTest, GpuInfoUpdate) {
}
EXPECT_FALSE(observer.gpu_info_updated());
- GPUInfo gpu_info;
+ gpu::GPUInfo gpu_info;
manager->UpdateGpuInfo(gpu_info);
{
base::RunLoop run_loop;
@@ -328,7 +333,7 @@ TEST_F(GpuDataManagerImplPrivateTest, NoGpuInfoUpdateWithSwiftShader) {
}
EXPECT_FALSE(observer.gpu_info_updated());
- GPUInfo gpu_info;
+ gpu::GPUInfo gpu_info;
manager->UpdateGpuInfo(gpu_info);
{
base::RunLoop run_loop;
@@ -503,7 +508,7 @@ TEST_F(GpuDataManagerImplPrivateTest, SetGLStrings) {
"device_id": ["0x0042"],
"driver_version": {
"op": ">=",
- "number": "8.0.2"
+ "value": "8.0.2"
}
}
],
@@ -514,7 +519,7 @@ TEST_F(GpuDataManagerImplPrivateTest, SetGLStrings) {
]
}
);
- GPUInfo gpu_info;
+ gpu::GPUInfo gpu_info;
gpu_info.gpu.vendor_id = 0x8086;
gpu_info.gpu.device_id = 0x0042;
manager->InitializeForTesting(blacklist_json, gpu_info);
@@ -530,7 +535,7 @@ TEST_F(GpuDataManagerImplPrivateTest, SetGLStrings) {
manager->SetGLStrings(kGLVendorMesa, kGLRendererMesa, kGLVersionMesa801);
EXPECT_TRUE(manager->GpuAccessAllowed(NULL));
EXPECT_EQ(1u, manager->GetBlacklistedFeatureCount());
- EXPECT_TRUE(manager->IsFeatureBlacklisted(GPU_FEATURE_TYPE_WEBGL));
+ EXPECT_TRUE(manager->IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_WEBGL));
}
TEST_F(GpuDataManagerImplPrivateTest, SetGLStringsNoEffects) {
@@ -556,7 +561,7 @@ TEST_F(GpuDataManagerImplPrivateTest, SetGLStringsNoEffects) {
"device_id": ["0x0042"],
"driver_version": {
"op": ">=",
- "number": "8.0.2"
+ "value": "8.0.2"
}
}
],
@@ -567,7 +572,7 @@ TEST_F(GpuDataManagerImplPrivateTest, SetGLStringsNoEffects) {
]
}
);
- GPUInfo gpu_info;
+ gpu::GPUInfo gpu_info;
gpu_info.gpu.vendor_id = 0x8086;
gpu_info.gpu.device_id = 0x0042;
gpu_info.gl_vendor = kGLVendorMesa;
@@ -580,7 +585,7 @@ TEST_F(GpuDataManagerImplPrivateTest, SetGLStringsNoEffects) {
// Full GPUInfo, the entry applies.
EXPECT_TRUE(manager->GpuAccessAllowed(NULL));
EXPECT_EQ(1u, manager->GetBlacklistedFeatureCount());
- EXPECT_TRUE(manager->IsFeatureBlacklisted(GPU_FEATURE_TYPE_WEBGL));
+ EXPECT_TRUE(manager->IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_WEBGL));
// Now assume browser gets GL strings from local state.
// SetGLStrings() has no effects because GPUInfo already got these strings.
@@ -588,7 +593,7 @@ TEST_F(GpuDataManagerImplPrivateTest, SetGLStringsNoEffects) {
manager->SetGLStrings(kGLVendorMesa, kGLRendererMesa, kGLVersionMesa802);
EXPECT_TRUE(manager->GpuAccessAllowed(NULL));
EXPECT_EQ(1u, manager->GetBlacklistedFeatureCount());
- EXPECT_TRUE(manager->IsFeatureBlacklisted(GPU_FEATURE_TYPE_WEBGL));
+ EXPECT_TRUE(manager->IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_WEBGL));
}
#endif // OS_LINUX
@@ -641,12 +646,12 @@ TEST_F(GpuDataManagerImplPrivateTest, BlacklistAllFeatures) {
}
);
- GPUInfo gpu_info;
+ gpu::GPUInfo gpu_info;
gpu_info.gpu.vendor_id = 0x10de;
gpu_info.gpu.device_id = 0x0640;
manager->InitializeForTesting(blacklist_json, gpu_info);
- EXPECT_EQ(static_cast<size_t>(NUMBER_OF_GPU_FEATURE_TYPES),
+ EXPECT_EQ(static_cast<size_t>(gpu::NUMBER_OF_GPU_FEATURE_TYPES),
manager->GetBlacklistedFeatureCount());
// TODO(zmo): remove the Linux specific behavior once we fix
// crbug.com/238466.
@@ -659,4 +664,81 @@ TEST_F(GpuDataManagerImplPrivateTest, BlacklistAllFeatures) {
#endif
}
+TEST_F(GpuDataManagerImplPrivateTest, UpdateActiveGpu) {
+ ScopedGpuDataManagerImpl manager;
+
+ const std::string blacklist_json = LONG_STRING_CONST(
+ {
+ "name": "gpu blacklist",
+ "version": "0.1",
+ "entries": [
+ {
+ "id": 1,
+ "vendor_id": "0x8086",
+ "multi_gpu_category": "active",
+ "features": [
+ "webgl"
+ ]
+ }
+ ]
+ }
+ );
+
+ // Two GPUs, the secondary Intel GPU is active.
+ gpu::GPUInfo gpu_info;
+ gpu_info.gpu.vendor_id = 0x10de;
+ gpu_info.gpu.device_id = 0x0640;
+ gpu_info.gpu.active = false;
+ gpu::GPUInfo::GPUDevice intel_gpu;
+ intel_gpu.vendor_id = 0x8086;
+ intel_gpu.device_id = 0x04a1;
+ intel_gpu.active = true;
+ gpu_info.secondary_gpus.push_back(intel_gpu);
+
+ manager->InitializeForTesting(blacklist_json, gpu_info);
+ TestObserver observer;
+ manager->AddObserver(&observer);
+
+ EXPECT_EQ(1u, manager->GetBlacklistedFeatureCount());
+
+ // Update with the same Intel GPU active.
+ EXPECT_FALSE(manager->UpdateActiveGpu(0x8086, 0x04a1));
+ {
+ base::RunLoop run_loop;
+ run_loop.RunUntilIdle();
+ }
+ EXPECT_FALSE(observer.gpu_info_updated());
+ EXPECT_EQ(1u, manager->GetBlacklistedFeatureCount());
+
+ // Set NVIDIA GPU to be active.
+ EXPECT_TRUE(manager->UpdateActiveGpu(0x10de, 0x0640));
+ {
+ base::RunLoop run_loop;
+ run_loop.RunUntilIdle();
+ }
+ EXPECT_TRUE(observer.gpu_info_updated());
+ EXPECT_EQ(0u, manager->GetBlacklistedFeatureCount());
+
+ observer.Reset();
+ EXPECT_FALSE(observer.gpu_info_updated());
+
+ // Update with the same NVIDIA GPU active.
+ EXPECT_FALSE(manager->UpdateActiveGpu(0x10de, 0x0640));
+ {
+ base::RunLoop run_loop;
+ run_loop.RunUntilIdle();
+ }
+ EXPECT_FALSE(observer.gpu_info_updated());
+ EXPECT_EQ(0u, manager->GetBlacklistedFeatureCount());
+
+ // Set Intel GPU to be active.
+ EXPECT_TRUE(manager->UpdateActiveGpu(0x8086, 0x04a1));
+ {
+ base::RunLoop run_loop;
+ run_loop.RunUntilIdle();
+ }
+ EXPECT_TRUE(observer.gpu_info_updated());
+ EXPECT_EQ(1u, manager->GetBlacklistedFeatureCount());
+}
+
} // namespace content
diff --git a/chromium/content/browser/gpu/gpu_internals_ui.cc b/chromium/content/browser/gpu/gpu_internals_ui.cc
index 5ff4373c497..997911656e7 100644
--- a/chromium/content/browser/gpu/gpu_internals_ui.cc
+++ b/chromium/content/browser/gpu/gpu_internals_ui.cc
@@ -30,6 +30,10 @@
#include "grit/content_resources.h"
#include "third_party/angle/src/common/version.h"
+#if defined(OS_WIN)
+#include "ui/base/win/shell.h"
+#endif
+
namespace content {
namespace {
@@ -87,8 +91,8 @@ std::string GPUDeviceToString(const gpu::GPUInfo::GPUDevice& gpu) {
std::string device = base::StringPrintf("0x%04x", gpu.device_id);
if (!gpu.device_string.empty())
device += " [" + gpu.device_string + "]";
- return base::StringPrintf(
- "VENDOR = %s, DEVICE= %s", vendor.c_str(), device.c_str());
+ return base::StringPrintf("VENDOR = %s, DEVICE= %s%s",
+ vendor.c_str(), device.c_str(), gpu.active ? " *ACTIVE*" : "");
}
base::DictionaryValue* GpuInfoAsDictionaryValue() {
@@ -118,8 +122,15 @@ base::DictionaryValue* GpuInfoAsDictionaryValue() {
basic_info->Append(NewDescriptionValuePair(
"DisplayLink Version", gpu_info.display_link_version.GetString()));
}
- basic_info->Append(NewDescriptionValuePair("Driver vendor",
- gpu_info.driver_vendor));
+#if defined(OS_WIN)
+ std::string compositor =
+ ui::win::IsAeroGlassEnabled() ? "Aero Glass" : "none";
+ basic_info->Append(
+ NewDescriptionValuePair("Desktop compositing", compositor));
+#endif
+
+ basic_info->Append(
+ NewDescriptionValuePair("Driver vendor", gpu_info.driver_vendor));
basic_info->Append(NewDescriptionValuePair("Driver version",
gpu_info.driver_version));
basic_info->Append(NewDescriptionValuePair("Driver date",
@@ -128,16 +139,16 @@ base::DictionaryValue* GpuInfoAsDictionaryValue() {
gpu_info.pixel_shader_version));
basic_info->Append(NewDescriptionValuePair("Vertex shader version",
gpu_info.vertex_shader_version));
- basic_info->Append(NewDescriptionValuePair("Machine model",
- gpu_info.machine_model));
- basic_info->Append(NewDescriptionValuePair("GL version",
- gpu_info.gl_version));
+ basic_info->Append(NewDescriptionValuePair("Machine model name",
+ gpu_info.machine_model_name));
+ basic_info->Append(NewDescriptionValuePair("Machine model version",
+ gpu_info.machine_model_version));
basic_info->Append(NewDescriptionValuePair("GL_VENDOR",
gpu_info.gl_vendor));
basic_info->Append(NewDescriptionValuePair("GL_RENDERER",
gpu_info.gl_renderer));
basic_info->Append(NewDescriptionValuePair("GL_VERSION",
- gpu_info.gl_version_string));
+ gpu_info.gl_version));
basic_info->Append(NewDescriptionValuePair("GL_EXTENSIONS",
gpu_info.gl_extensions));
basic_info->Append(NewDescriptionValuePair("Window system binding vendor",
@@ -146,6 +157,10 @@ base::DictionaryValue* GpuInfoAsDictionaryValue() {
gpu_info.gl_ws_version));
basic_info->Append(NewDescriptionValuePair("Window system binding extensions",
gpu_info.gl_ws_extensions));
+ std::string direct_rendering = gpu_info.direct_rendering ? "Yes" : "No";
+ basic_info->Append(
+ NewDescriptionValuePair("Direct rendering", direct_rendering));
+
std::string reset_strategy =
base::StringPrintf("0x%04x", gpu_info.gl_reset_notification_strategy);
basic_info->Append(NewDescriptionValuePair(
@@ -313,7 +328,7 @@ base::Value* GpuMessageHandler::OnRequestClientInfo(
dict->SetString("operating_system",
base::SysInfo::OperatingSystemName() + " " +
base::SysInfo::OperatingSystemVersion());
- dict->SetString("angle_revision", base::UintToString(BUILD_REVISION));
+ dict->SetString("angle_commit_id", ANGLE_COMMIT_HASH);
dict->SetString("graphics_backend", "Skia");
dict->SetString("blacklist_version",
GpuDataManagerImpl::GetInstance()->GetBlacklistVersion());
@@ -334,7 +349,7 @@ void GpuMessageHandler::OnGpuInfoUpdate() {
scoped_ptr<base::DictionaryValue> gpu_info_val(GpuInfoAsDictionaryValue());
// Add in blacklisting features
- base::DictionaryValue* feature_status = new DictionaryValue;
+ base::DictionaryValue* feature_status = new base::DictionaryValue;
feature_status->Set("featureStatus", GetFeatureStatus());
feature_status->Set("problems", GetProblems());
feature_status->Set("workarounds", GetDriverBugWorkarounds());
diff --git a/chromium/content/browser/gpu/gpu_ipc_browsertests.cc b/chromium/content/browser/gpu/gpu_ipc_browsertests.cc
index ca2ccf9ee05..cbe45393419 100644
--- a/chromium/content/browser/gpu/gpu_ipc_browsertests.cc
+++ b/chromium/content/browser/gpu/gpu_ipc_browsertests.cc
@@ -9,10 +9,9 @@
#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/public/browser/browser_thread.h"
#include "content/public/browser/gpu_data_manager.h"
#include "content/public/common/content_switches.h"
-#include "content/test/content_browser_test.h"
+#include "content/public/test/content_browser_test.h"
#include "ui/gl/gl_switches.h"
#include "webkit/common/gpu/webgraphicscontext3d_in_process_command_buffer_impl.h"
@@ -20,6 +19,10 @@ namespace {
using content::WebGraphicsContext3DCommandBufferImpl;
+const content::CauseForGpuLaunch kInitCause =
+ content::
+ CAUSE_FOR_GPU_LAUNCH_WEBGRAPHICSCONTEXT3DCOMMANDBUFFERIMPL_INITIALIZE;
+
class ContextTestBase : public content::ContentBrowserTest {
public:
virtual void SetUpOnMainThread() OVERRIDE {
@@ -32,16 +35,17 @@ class ContextTestBase : public content::ContentBrowserTest {
content::BrowserGpuChannelHostFactory* factory =
content::BrowserGpuChannelHostFactory::instance();
CHECK(factory);
+ bool lose_context_when_out_of_memory = false;
scoped_refptr<content::GpuChannelHost> gpu_channel_host(
- factory->EstablishGpuChannelSync(
- content::
- CAUSE_FOR_GPU_LAUNCH_WEBGRAPHICSCONTEXT3DCOMMANDBUFFERIMPL_INITIALIZE));
+ factory->EstablishGpuChannelSync(kInitCause));
context_.reset(
WebGraphicsContext3DCommandBufferImpl::CreateOffscreenContext(
gpu_channel_host.get(),
blink::WebGraphicsContext3D::Attributes(),
+ lose_context_when_out_of_memory,
GURL(),
- WebGraphicsContext3DCommandBufferImpl::SharedMemoryLimits()));
+ WebGraphicsContext3DCommandBufferImpl::SharedMemoryLimits(),
+ NULL));
CHECK(context_.get());
context_->makeContextCurrent();
context_support_ = context_->GetContextSupport();
@@ -55,7 +59,7 @@ class ContextTestBase : public content::ContentBrowserTest {
}
protected:
- scoped_ptr<WebGraphicsContext3DCommandBufferImpl> context_;
+ scoped_ptr<content::WebGraphicsContext3DCommandBufferImpl> context_;
gpu::ContextSupport* context_support_;
};
@@ -67,32 +71,22 @@ class ContextTestBase : public content::ContentBrowserTest {
namespace content {
-class BrowserGpuChannelHostFactoryTest : public ContextTestBase {
+class BrowserGpuChannelHostFactoryTest : public ContentBrowserTest {
public:
virtual void SetUpOnMainThread() OVERRIDE {
- if (!content::BrowserGpuChannelHostFactory::CanUseForTesting())
+ if (!BrowserGpuChannelHostFactory::CanUseForTesting())
return;
// Start all tests without a gpu channel so that the tests exercise a
// consistent codepath.
- if (!content::BrowserGpuChannelHostFactory::instance())
- content::BrowserGpuChannelHostFactory::Initialize(false);
+ if (!BrowserGpuChannelHostFactory::instance())
+ BrowserGpuChannelHostFactory::Initialize(false);
CHECK(GetFactory());
ContentBrowserTest::SetUpOnMainThread();
}
- virtual void TearDownOnMainThread() OVERRIDE {
- ContextTestBase::TearDownOnMainThread();
- }
-
- virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
- // Start all tests without a gpu channel so that the tests exercise a
- // consistent codepath.
- command_line->AppendSwitch(switches::kDisableGpuProcessPrelaunch);
- }
-
void OnContextLost(const base::Closure callback, int* counter) {
(*counter)++;
callback.Run();
@@ -109,9 +103,7 @@ class BrowserGpuChannelHostFactoryTest : public ContextTestBase {
void EstablishAndWait() {
base::RunLoop run_loop;
- GetFactory()->EstablishGpuChannel(
- CAUSE_FOR_GPU_LAUNCH_WEBGRAPHICSCONTEXT3DCOMMANDBUFFERIMPL_INITIALIZE,
- run_loop.QuitClosure());
+ GetFactory()->EstablishGpuChannel(kInitCause, run_loop.QuitClosure());
run_loop.Run();
}
@@ -125,62 +117,56 @@ class BrowserGpuChannelHostFactoryTest : public ContextTestBase {
}
scoped_ptr<WebGraphicsContext3DCommandBufferImpl> CreateContext() {
+ bool lose_context_when_out_of_memory = false;
return make_scoped_ptr(
WebGraphicsContext3DCommandBufferImpl::CreateOffscreenContext(
GetGpuChannel(),
blink::WebGraphicsContext3D::Attributes(),
+ lose_context_when_out_of_memory,
GURL(),
- WebGraphicsContext3DCommandBufferImpl::SharedMemoryLimits()));
+ WebGraphicsContext3DCommandBufferImpl::SharedMemoryLimits(),
+ NULL));
}
};
-IN_PROC_BROWSER_TEST_F(BrowserGpuChannelHostFactoryTest, Basic) {
- if (!context_)
- return;
-
+// Fails since UI Compositor establishes a GpuChannel.
+IN_PROC_BROWSER_TEST_F(BrowserGpuChannelHostFactoryTest, DISABLED_Basic) {
DCHECK(!IsChannelEstablished());
EstablishAndWait();
EXPECT_TRUE(GetGpuChannel() != NULL);
}
+// Fails since UI Compositor establishes a GpuChannel.
IN_PROC_BROWSER_TEST_F(BrowserGpuChannelHostFactoryTest,
- EstablishAndTerminate) {
- if (!context_)
- return;
-
+ DISABLED_EstablishAndTerminate) {
DCHECK(!IsChannelEstablished());
base::RunLoop run_loop;
- GetFactory()->EstablishGpuChannel(
- CAUSE_FOR_GPU_LAUNCH_WEBGRAPHICSCONTEXT3DCOMMANDBUFFERIMPL_INITIALIZE,
- run_loop.QuitClosure());
+ GetFactory()->EstablishGpuChannel(kInitCause, run_loop.QuitClosure());
GetFactory()->Terminate();
// The callback should still trigger.
run_loop.Run();
}
-IN_PROC_BROWSER_TEST_F(BrowserGpuChannelHostFactoryTest, AlreadyEstablished) {
- if (!context_)
- return;
-
+// Fails since UI Compositor establishes a GpuChannel.
+IN_PROC_BROWSER_TEST_F(BrowserGpuChannelHostFactoryTest,
+ DISABLED_AlreadyEstablished) {
DCHECK(!IsChannelEstablished());
scoped_refptr<GpuChannelHost> gpu_channel =
- GetFactory()->EstablishGpuChannelSync(
- CAUSE_FOR_GPU_LAUNCH_WEBGRAPHICSCONTEXT3DCOMMANDBUFFERIMPL_INITIALIZE);
+ GetFactory()->EstablishGpuChannelSync(kInitCause);
// Expect established callback immediately.
bool event = false;
GetFactory()->EstablishGpuChannel(
- CAUSE_FOR_GPU_LAUNCH_WEBGRAPHICSCONTEXT3DCOMMANDBUFFERIMPL_INITIALIZE,
+ kInitCause,
base::Bind(&BrowserGpuChannelHostFactoryTest::Signal, &event));
EXPECT_TRUE(event);
EXPECT_EQ(gpu_channel, GetGpuChannel());
}
-IN_PROC_BROWSER_TEST_F(BrowserGpuChannelHostFactoryTest, CrashAndRecover) {
- if (!context_)
- return;
-
+// Fails since UI Compositor establishes a GpuChannel.
+IN_PROC_BROWSER_TEST_F(BrowserGpuChannelHostFactoryTest,
+ DISABLED_CrashAndRecover) {
DCHECK(!IsChannelEstablished());
EstablishAndWait();
scoped_refptr<GpuChannelHost> host = GetGpuChannel();
diff --git a/chromium/content/browser/gpu/gpu_process_host.cc b/chromium/content/browser/gpu/gpu_process_host.cc
index 1b7da1c8d7c..9176b6d891c 100644
--- a/chromium/content/browser/gpu/gpu_process_host.cc
+++ b/chromium/content/browser/gpu/gpu_process_host.cc
@@ -25,17 +25,20 @@
#include "content/common/child_process_host_impl.h"
#include "content/common/gpu/gpu_messages.h"
#include "content/common/view_messages.h"
-#include "content/port/browser/render_widget_host_view_frame_subscriber.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/browser/render_widget_host_view.h"
+#include "content/public/browser/render_widget_host_view_frame_subscriber.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/result_codes.h"
+#include "content/public/common/sandboxed_process_launcher_delegate.h"
#include "gpu/command_buffer/service/gpu_switches.h"
#include "ipc/ipc_channel_handle.h"
#include "ipc/ipc_switches.h"
+#include "ipc/message_filter.h"
+#include "media/base/media_switches.h"
#include "ui/events/latency_info.h"
#include "ui/gl/gl_switches.h"
@@ -43,15 +46,18 @@
#if defined(OS_WIN)
#include "base/win/windows_version.h"
#include "content/common/sandbox_win.h"
-#include "content/public/common/sandboxed_process_launcher_delegate.h"
#include "sandbox/win/src/sandbox_policy.h"
-#include "ui/surface/accelerated_surface_win.h"
+#include "ui/gfx/switches.h"
#endif
#if defined(USE_OZONE)
#include "ui/ozone/ozone_switches.h"
#endif
+#if defined(USE_X11) && !defined(OS_CHROMEOS)
+#include "ui/gfx/x/x11_switches.h"
+#endif
+
namespace content {
bool GpuProcessHost::gpu_enabled_ = true;
@@ -84,104 +90,27 @@ void SendGpuProcessMessage(GpuProcessHost::GpuProcessKind kind,
}
}
-void AcceleratedSurfaceBuffersSwappedCompletedForGPU(
- int host_id,
- int route_id,
- bool alive,
- base::TimeTicks vsync_timebase,
- base::TimeDelta vsync_interval) {
- if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
- BrowserThread::PostTask(
- BrowserThread::IO,
- FROM_HERE,
- base::Bind(&AcceleratedSurfaceBuffersSwappedCompletedForGPU,
- host_id,
- route_id,
- alive,
- vsync_timebase,
- vsync_interval));
- return;
- }
-
- GpuProcessHost* host = GpuProcessHost::FromID(host_id);
- if (host) {
- if (alive) {
- AcceleratedSurfaceMsg_BufferPresented_Params ack_params;
- ack_params.sync_point = 0;
-#if defined(OS_WIN)
- ack_params.vsync_timebase = vsync_timebase;
- ack_params.vsync_interval = vsync_interval;
-#endif
- host->Send(
- new AcceleratedSurfaceMsg_BufferPresented(route_id, ack_params));
- } else {
- host->ForceShutdown();
- }
- }
-}
-
-#if defined(OS_WIN)
-// This sends a ViewMsg_SwapBuffers_ACK directly to the renderer process
-// (RenderWidget).
-void AcceleratedSurfaceBuffersSwappedCompletedForRenderer(
- int surface_id,
- base::TimeTicks timebase,
- base::TimeDelta interval,
- const ui::LatencyInfo& latency_info) {
- if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
- BrowserThread::PostTask(
- BrowserThread::UI,
- FROM_HERE,
- base::Bind(&AcceleratedSurfaceBuffersSwappedCompletedForRenderer,
- surface_id, timebase, interval, latency_info));
- return;
- }
-
- int render_process_id = 0;
- int render_widget_id = 0;
- if (!GpuSurfaceTracker::Get()->GetRenderWidgetIDForSurface(
- surface_id, &render_process_id, &render_widget_id)) {
- RenderWidgetHostImpl::CompositorFrameDrawn(latency_info);
- return;
- }
- RenderWidgetHost* rwh =
- RenderWidgetHost::FromID(render_process_id, render_widget_id);
- if (!rwh)
- return;
- RenderWidgetHostImpl::From(rwh)->AcknowledgeSwapBuffersToRenderer();
- if (interval != base::TimeDelta())
- RenderWidgetHostImpl::From(rwh)->UpdateVSyncParameters(timebase, interval);
- RenderWidgetHostImpl::From(rwh)->FrameSwapped(latency_info);
- RenderWidgetHostImpl::From(rwh)->DidReceiveRendererFrame();
-}
-
-void AcceleratedSurfaceBuffersSwappedCompleted(
- int host_id,
- int route_id,
- int surface_id,
- bool alive,
- base::TimeTicks timebase,
- base::TimeDelta interval,
- const ui::LatencyInfo& latency_info) {
- AcceleratedSurfaceBuffersSwappedCompletedForGPU(
- host_id, route_id, alive, timebase, interval);
- AcceleratedSurfaceBuffersSwappedCompletedForRenderer(
- surface_id, timebase, interval, latency_info);
-}
-
// NOTE: changes to this class need to be reviewed by the security team.
class GpuSandboxedProcessLauncherDelegate
: public SandboxedProcessLauncherDelegate {
public:
- explicit GpuSandboxedProcessLauncherDelegate(CommandLine* cmd_line)
+ GpuSandboxedProcessLauncherDelegate(CommandLine* cmd_line,
+ ChildProcessHost* host)
+#if defined(OS_WIN)
: cmd_line_(cmd_line) {}
+#elif defined(OS_POSIX)
+ : ipc_fd_(host->TakeClientFileDescriptor()) {}
+#endif
+
virtual ~GpuSandboxedProcessLauncherDelegate() {}
- virtual void ShouldSandbox(bool* in_sandbox) OVERRIDE {
- if (cmd_line_->HasSwitch(switches::kDisableGpuSandbox)) {
- *in_sandbox = false;
+#if defined(OS_WIN)
+ virtual bool ShouldSandbox() OVERRIDE {
+ bool sandbox = !cmd_line_->HasSwitch(switches::kDisableGpuSandbox);
+ if(! sandbox) {
DVLOG(1) << "GPU sandbox is disabled";
}
+ return sandbox;
}
virtual void PreSandbox(bool* disable_default_policy,
@@ -268,19 +197,25 @@ class GpuSandboxedProcessLauncherDelegate
}
}
}
+#elif defined(OS_POSIX)
+
+ virtual int GetIpcFd() OVERRIDE {
+ return ipc_fd_;
+ }
+#endif // OS_WIN
private:
+#if defined(OS_WIN)
CommandLine* cmd_line_;
+#elif defined(OS_POSIX)
+ int ipc_fd_;
+#endif // OS_WIN
};
-#endif // defined(OS_WIN)
} // anonymous namespace
// static
bool GpuProcessHost::ValidateHost(GpuProcessHost* host) {
- if (!host)
- return false;
-
// The Gpu process is invalid if it's not using SwiftShader, the card is
// blacklisted, and we can kill it and start over.
if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess) ||
@@ -573,8 +508,12 @@ bool GpuProcessHost::Init() {
if (in_process_) {
DCHECK(g_gpu_main_thread_factory);
- CommandLine::ForCurrentProcess()->AppendSwitch(
- switches::kDisableGpuWatchdog);
+ CommandLine* command_line = CommandLine::ForCurrentProcess();
+ command_line->AppendSwitch(switches::kDisableGpuWatchdog);
+
+ GpuDataManagerImpl* gpu_data_manager = GpuDataManagerImpl::GetInstance();
+ DCHECK(gpu_data_manager);
+ gpu_data_manager->AppendGpuCommandLine(command_line);
in_process_gpu_thread_.reset(g_gpu_main_thread_factory(channel_id));
in_process_gpu_thread_->Start();
@@ -614,7 +553,7 @@ bool GpuProcessHost::Send(IPC::Message* msg) {
return result;
}
-void GpuProcessHost::AddFilter(IPC::ChannelProxy::MessageFilter* filter) {
+void GpuProcessHost::AddFilter(IPC::MessageFilter* filter) {
DCHECK(CalledOnValidThread());
process_->GetHost()->AddFilter(filter);
}
@@ -627,6 +566,8 @@ bool GpuProcessHost::OnMessageReceived(const IPC::Message& message) {
IPC_MESSAGE_HANDLER(GpuHostMsg_CommandBufferCreated, OnCommandBufferCreated)
IPC_MESSAGE_HANDLER(GpuHostMsg_DestroyCommandBuffer, OnDestroyCommandBuffer)
IPC_MESSAGE_HANDLER(GpuHostMsg_ImageCreated, OnImageCreated)
+ IPC_MESSAGE_HANDLER(GpuHostMsg_GpuMemoryBufferCreated,
+ OnGpuMemoryBufferCreated)
IPC_MESSAGE_HANDLER(GpuHostMsg_DidCreateOffscreenContext,
OnDidCreateOffscreenContext)
IPC_MESSAGE_HANDLER(GpuHostMsg_DidLoseContext, OnDidLoseContext)
@@ -638,16 +579,6 @@ bool GpuProcessHost::OnMessageReceived(const IPC::Message& message) {
IPC_MESSAGE_HANDLER(GpuHostMsg_AcceleratedSurfaceBuffersSwapped,
OnAcceleratedSurfaceBuffersSwapped)
#endif
-#if defined(OS_WIN)
- IPC_MESSAGE_HANDLER(GpuHostMsg_AcceleratedSurfaceBuffersSwapped,
- OnAcceleratedSurfaceBuffersSwapped)
- IPC_MESSAGE_HANDLER(GpuHostMsg_AcceleratedSurfacePostSubBuffer,
- OnAcceleratedSurfacePostSubBuffer)
- IPC_MESSAGE_HANDLER(GpuHostMsg_AcceleratedSurfaceSuspend,
- OnAcceleratedSurfaceSuspend)
- IPC_MESSAGE_HANDLER(GpuHostMsg_AcceleratedSurfaceRelease,
- OnAcceleratedSurfaceRelease)
-#endif
IPC_MESSAGE_HANDLER(GpuHostMsg_DestroyChannel,
OnDestroyChannel)
IPC_MESSAGE_HANDLER(GpuHostMsg_CacheShader,
@@ -677,6 +608,7 @@ void GpuProcessHost::EstablishGpuChannel(
// If GPU features are already blacklisted, no need to establish the channel.
if (!GpuDataManagerImpl::GetInstance()->GpuAccessAllowed(NULL)) {
+ DVLOG(1) << "GPU blacklisted, refusing to open a GPU channel.";
callback.Run(IPC::ChannelHandle(), gpu::GPUInfo());
return;
}
@@ -684,6 +616,7 @@ void GpuProcessHost::EstablishGpuChannel(
if (Send(new GpuMsg_EstablishChannel(client_id, share_context))) {
channel_requests_.push(callback);
} else {
+ DVLOG(1) << "Failed to send GpuMsg_EstablishChannel.";
callback.Run(IPC::ChannelHandle(), gpu::GPUInfo());
}
@@ -698,6 +631,7 @@ void GpuProcessHost::CreateViewCommandBuffer(
int surface_id,
int client_id,
const GPUCreateCommandBufferConfig& init_params,
+ int route_id,
const CreateCommandBufferCallback& callback) {
TRACE_EVENT0("gpu", "GpuProcessHost::CreateViewCommandBuffer");
@@ -705,12 +639,14 @@ void GpuProcessHost::CreateViewCommandBuffer(
if (!compositing_surface.is_null() &&
Send(new GpuMsg_CreateViewCommandBuffer(
- compositing_surface, surface_id, client_id, init_params))) {
+ compositing_surface, surface_id, client_id, init_params, route_id))) {
create_command_buffer_requests_.push(callback);
surface_refs_.insert(std::make_pair(surface_id,
GpuSurfaceTracker::GetInstance()->GetSurfaceRefForSurface(surface_id)));
} else {
- callback.Run(MSG_ROUTING_NONE);
+ // Could distinguish here between compositing_surface being NULL
+ // and Send failing, if desired.
+ callback.Run(CREATE_COMMAND_BUFFER_FAILED_AND_CHANNEL_LOST);
}
}
@@ -739,17 +675,42 @@ void GpuProcessHost::DeleteImage(int client_id,
Send(new GpuMsg_DeleteImage(client_id, image_id, sync_point));
}
+void GpuProcessHost::CreateGpuMemoryBuffer(
+ const gfx::GpuMemoryBufferHandle& handle,
+ const gfx::Size& size,
+ unsigned internalformat,
+ unsigned usage,
+ const CreateGpuMemoryBufferCallback& callback) {
+ TRACE_EVENT0("gpu", "GpuProcessHost::CreateGpuMemoryBuffer");
+
+ DCHECK(CalledOnValidThread());
+
+ if (Send(new GpuMsg_CreateGpuMemoryBuffer(
+ handle, size, internalformat, usage))) {
+ create_gpu_memory_buffer_requests_.push(callback);
+ } else {
+ callback.Run(gfx::GpuMemoryBufferHandle());
+ }
+}
+
+void GpuProcessHost::DestroyGpuMemoryBuffer(
+ const gfx::GpuMemoryBufferHandle& handle,
+ int sync_point) {
+ TRACE_EVENT0("gpu", "GpuProcessHost::DestroyGpuMemoryBuffer");
+
+ DCHECK(CalledOnValidThread());
+
+ Send(new GpuMsg_DestroyGpuMemoryBuffer(handle, sync_point));
+}
+
void GpuProcessHost::OnInitialized(bool result, const gpu::GPUInfo& gpu_info) {
UMA_HISTOGRAM_BOOLEAN("GPU.GPUProcessInitialized", result);
initialized_ = result;
-#if defined(OS_WIN)
- if (kind_ == GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED)
- AcceleratedPresenter::SetAdapterLUID(gpu_info.adapter_luid);
-#endif
-
if (!initialized_)
GpuDataManagerImpl::GetInstance()->OnGpuProcessInitFailure();
+ else if (!in_process_)
+ GpuDataManagerImpl::GetInstance()->UpdateGpuInfo(gpu_info);
}
void GpuProcessHost::OnChannelEstablished(
@@ -784,7 +745,7 @@ void GpuProcessHost::OnChannelEstablished(
GpuDataManagerImpl::GetInstance()->GetGPUInfo());
}
-void GpuProcessHost::OnCommandBufferCreated(const int32 route_id) {
+void GpuProcessHost::OnCommandBufferCreated(CreateCommandBufferResult result) {
TRACE_EVENT0("gpu", "GpuProcessHost::OnCommandBufferCreated");
if (create_command_buffer_requests_.empty())
@@ -793,7 +754,7 @@ void GpuProcessHost::OnCommandBufferCreated(const int32 route_id) {
CreateCommandBufferCallback callback =
create_command_buffer_requests_.front();
create_command_buffer_requests_.pop();
- callback.Run(route_id);
+ callback.Run(result);
}
void GpuProcessHost::OnDestroyCommandBuffer(int32 surface_id) {
@@ -815,6 +776,19 @@ void GpuProcessHost::OnImageCreated(const gfx::Size size) {
callback.Run(size);
}
+void GpuProcessHost::OnGpuMemoryBufferCreated(
+ const gfx::GpuMemoryBufferHandle& handle) {
+ TRACE_EVENT0("gpu", "GpuProcessHost::OnGpuMemoryBufferCreated");
+
+ if (create_gpu_memory_buffer_requests_.empty())
+ return;
+
+ CreateGpuMemoryBufferCallback callback =
+ create_gpu_memory_buffer_requests_.front();
+ create_gpu_memory_buffer_requests_.pop();
+ callback.Run(handle);
+}
+
void GpuProcessHost::OnDidCreateOffscreenContext(const GURL& url) {
urls_with_live_offscreen_contexts_.insert(url);
}
@@ -872,6 +846,17 @@ void GpuProcessHost::OnAcceleratedSurfaceBuffersSwapped(
const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params) {
TRACE_EVENT0("gpu", "GpuProcessHost::OnAcceleratedSurfaceBuffersSwapped");
+ if (!ui::LatencyInfo::Verify(params.latency_info,
+ "GpuHostMsg_AcceleratedSurfaceBuffersSwapped"))
+ return;
+
+ gfx::AcceleratedWidget native_widget =
+ GpuSurfaceTracker::Get()->AcquireNativeWidget(params.surface_id);
+ if (native_widget) {
+ RenderWidgetHelper::OnNativeSurfaceBuffersSwappedOnIOThread(this, params);
+ return;
+ }
+
gfx::GLSurfaceHandle surface_handle =
GpuSurfaceTracker::Get()->GetSurfaceHandle(params.surface_id);
// Compositor window is always gfx::kNullPluginWindow.
@@ -883,28 +868,29 @@ void GpuProcessHost::OnAcceleratedSurfaceBuffersSwapped(
return;
}
- base::ScopedClosureRunner scoped_completion_runner(
- base::Bind(&AcceleratedSurfaceBuffersSwappedCompletedForGPU,
- host_id_, params.route_id,
- true /* alive */, base::TimeTicks(), base::TimeDelta()));
+ AcceleratedSurfaceMsg_BufferPresented_Params ack_params;
+ ack_params.sync_point = 0;
int render_process_id = 0;
int render_widget_id = 0;
if (!GpuSurfaceTracker::Get()->GetRenderWidgetIDForSurface(
params.surface_id, &render_process_id, &render_widget_id)) {
+ Send(new AcceleratedSurfaceMsg_BufferPresented(params.route_id,
+ ack_params));
return;
}
RenderWidgetHelper* helper =
RenderWidgetHelper::FromProcessHostID(render_process_id);
- if (!helper)
+ if (!helper) {
+ Send(new AcceleratedSurfaceMsg_BufferPresented(params.route_id,
+ ack_params));
return;
+ }
// Pass the SwapBuffers on to the RenderWidgetHelper to wake up the UI thread
// if the browser is waiting for a new frame. Otherwise the RenderWidgetHelper
// will forward to the RenderWidgetHostView via RenderProcessHostImpl and
// RenderWidgetHostImpl.
- ignore_result(scoped_completion_runner.Release());
-
ViewHostMsg_CompositorSurfaceBuffersSwapped_Params view_params;
view_params.surface_id = params.surface_id;
view_params.surface_handle = params.surface_handle;
@@ -919,128 +905,6 @@ void GpuProcessHost::OnAcceleratedSurfaceBuffersSwapped(
}
#endif // OS_MACOSX
-#if defined(OS_WIN)
-void GpuProcessHost::OnAcceleratedSurfaceBuffersSwapped(
- const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params) {
- TRACE_EVENT0("gpu", "GpuProcessHost::OnAcceleratedSurfaceBuffersSwapped");
-
- base::ScopedClosureRunner scoped_completion_runner(
- base::Bind(&AcceleratedSurfaceBuffersSwappedCompleted,
- host_id_, params.route_id, params.surface_id,
- true, base::TimeTicks(), base::TimeDelta(), ui::LatencyInfo()));
-
- gfx::GLSurfaceHandle handle =
- GpuSurfaceTracker::Get()->GetSurfaceHandle(params.surface_id);
-
- if (handle.is_null())
- return;
-
- if (handle.transport_type == gfx::TEXTURE_TRANSPORT) {
- TRACE_EVENT1("gpu", "SurfaceIDNotFound_RoutingToUI",
- "surface_id", params.surface_id);
- // This is a content area swap, send it on to the UI thread.
- ignore_result(scoped_completion_runner.Release());
- RouteOnUIThread(GpuHostMsg_AcceleratedSurfaceBuffersSwapped(params));
- return;
- }
-
- // Otherwise it's the UI swap.
-
- scoped_refptr<AcceleratedPresenter> presenter(
- AcceleratedPresenter::GetForWindow(handle.handle));
- if (!presenter) {
- TRACE_EVENT1("gpu",
- "EarlyOut_NativeWindowNotFound",
- "handle",
- handle.handle);
- ignore_result(scoped_completion_runner.Release());
- AcceleratedSurfaceBuffersSwappedCompleted(host_id_,
- params.route_id,
- params.surface_id,
- true,
- base::TimeTicks(),
- base::TimeDelta(),
- params.latency_info);
- return;
- }
-
- ignore_result(scoped_completion_runner.Release());
- presenter->AsyncPresentAndAcknowledge(
- params.size,
- params.surface_handle,
- params.latency_info,
- base::Bind(&AcceleratedSurfaceBuffersSwappedCompleted,
- host_id_,
- params.route_id,
- params.surface_id));
-
- FrameSubscriberMap::iterator it = frame_subscribers_.find(params.surface_id);
- if (it != frame_subscribers_.end() && it->second) {
- const base::Time present_time = base::Time::Now();
- scoped_refptr<media::VideoFrame> target_frame;
- RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback copy_callback;
- if (it->second->ShouldCaptureFrame(present_time,
- &target_frame, &copy_callback)) {
- // It is a potential improvement to do the copy in present, but we use a
- // simpler approach for now.
- presenter->AsyncCopyToVideoFrame(
- gfx::Rect(params.size), target_frame,
- base::Bind(copy_callback, present_time));
- }
- }
-}
-
-void GpuProcessHost::OnAcceleratedSurfacePostSubBuffer(
- const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params) {
- TRACE_EVENT0("gpu", "GpuProcessHost::OnAcceleratedSurfacePostSubBuffer");
-
- NOTIMPLEMENTED();
-}
-
-void GpuProcessHost::OnAcceleratedSurfaceSuspend(int32 surface_id) {
- TRACE_EVENT0("gpu", "GpuProcessHost::OnAcceleratedSurfaceSuspend");
-
- gfx::PluginWindowHandle handle =
- GpuSurfaceTracker::Get()->GetSurfaceHandle(surface_id).handle;
-
- if (!handle) {
-#if defined(USE_AURA)
- RouteOnUIThread(GpuHostMsg_AcceleratedSurfaceSuspend(surface_id));
-#endif
- return;
- }
-
- scoped_refptr<AcceleratedPresenter> presenter(
- AcceleratedPresenter::GetForWindow(handle));
- if (!presenter)
- return;
-
- presenter->Suspend();
-}
-
-void GpuProcessHost::OnAcceleratedSurfaceRelease(
- const GpuHostMsg_AcceleratedSurfaceRelease_Params& params) {
- TRACE_EVENT0("gpu", "GpuProcessHost::OnAcceleratedSurfaceRelease");
-
- gfx::PluginWindowHandle handle =
- GpuSurfaceTracker::Get()->GetSurfaceHandle(params.surface_id).handle;
- if (!handle) {
-#if defined(USE_AURA)
- RouteOnUIThread(GpuHostMsg_AcceleratedSurfaceRelease(params));
- return;
-#endif
- }
-
- scoped_refptr<AcceleratedPresenter> presenter(
- AcceleratedPresenter::GetForWindow(handle));
- if (!presenter)
- return;
-
- presenter->ReleaseSurface();
-}
-
-#endif // OS_WIN
-
void GpuProcessHost::OnProcessLaunched() {
UMA_HISTOGRAM_TIMES("GPU.GPUProcessLaunchTime",
base::TimeTicks::Now() - init_start_time_);
@@ -1110,10 +974,8 @@ bool GpuProcessHost::LaunchGpuProcess(const std::string& channel_id) {
static const char* const kSwitchNames[] = {
switches::kDisableAcceleratedVideoDecode,
switches::kDisableBreakpad,
- switches::kDisableGLMultisampling,
switches::kDisableGpuSandbox,
switches::kDisableGpuWatchdog,
- switches::kDisableImageTransportSurface,
switches::kDisableLogging,
switches::kDisableSeccompFilterSandbox,
#if defined(ENABLE_WEBRTC)
@@ -1123,9 +985,11 @@ bool GpuProcessHost::LaunchGpuProcess(const std::string& channel_id) {
switches::kEnableShareGroupAsyncTextureUpload,
switches::kGpuStartupDialog,
switches::kGpuSandboxAllowSysVShm,
+ switches::kGpuSandboxFailuresFatal,
+ switches::kGpuSandboxStartAfterInitialization,
+ switches::kIgnoreResolutionLimitsForAcceleratedVideoDecode,
switches::kLoggingLevel,
switches::kNoSandbox,
- switches::kReduceGpuSandbox,
switches::kTestGLLib,
switches::kTraceStartup,
switches::kV,
@@ -1139,6 +1003,9 @@ bool GpuProcessHost::LaunchGpuProcess(const std::string& channel_id) {
#if defined(USE_OZONE)
switches::kOzonePlatform,
#endif
+#if defined(USE_X11) && !defined(OS_CHROMEOS)
+ switches::kX11Display,
+#endif
};
cmd_line->CopySwitchesFrom(browser_command_line, kSwitchNames,
arraysize(kSwitchNames));
@@ -1166,12 +1033,8 @@ bool GpuProcessHost::LaunchGpuProcess(const std::string& channel_id) {
cmd_line->PrependWrapper(gpu_launcher);
process_->Launch(
-#if defined(OS_WIN)
- new GpuSandboxedProcessLauncherDelegate(cmd_line),
-#elif defined(OS_POSIX)
- false,
- base::EnvironmentMap(),
-#endif
+ new GpuSandboxedProcessLauncherDelegate(cmd_line,
+ process_->GetHost()),
cmd_line);
process_launched_ = true;
@@ -1193,7 +1056,7 @@ void GpuProcessHost::SendOutstandingReplies() {
CreateCommandBufferCallback callback =
create_command_buffer_requests_.front();
create_command_buffer_requests_.pop();
- callback.Run(MSG_ROUTING_NONE);
+ callback.Run(CREATE_COMMAND_BUFFER_FAILED_AND_CHANNEL_LOST);
}
}
diff --git a/chromium/content/browser/gpu/gpu_process_host.h b/chromium/content/browser/gpu/gpu_process_host.h
index 32345b72800..cf789465248 100644
--- a/chromium/content/browser/gpu/gpu_process_host.h
+++ b/chromium/content/browser/gpu/gpu_process_host.h
@@ -19,12 +19,13 @@
#include "content/common/content_export.h"
#include "content/common/gpu/gpu_memory_uma_stats.h"
#include "content/common/gpu/gpu_process_launch_causes.h"
+#include "content/common/gpu/gpu_result_codes.h"
#include "content/public/browser/browser_child_process_host_delegate.h"
#include "content/public/browser/gpu_data_manager.h"
#include "gpu/command_buffer/common/constants.h"
#include "gpu/config/gpu_info.h"
-#include "ipc/ipc_channel_proxy.h"
#include "ipc/ipc_sender.h"
+#include "ipc/message_filter.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/gfx/size.h"
#include "url/gurl.h"
@@ -34,6 +35,10 @@ struct GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params;
struct GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params;
struct GpuHostMsg_AcceleratedSurfaceRelease_Params;
+namespace gfx {
+struct GpuMemoryBufferHandle;
+}
+
namespace IPC {
struct ChannelHandle;
}
@@ -59,10 +64,14 @@ class GpuProcessHost : public BrowserChildProcessHostDelegate,
typedef base::Callback<void(const IPC::ChannelHandle&, const gpu::GPUInfo&)>
EstablishChannelCallback;
- typedef base::Callback<void(int32)> CreateCommandBufferCallback;
+ typedef base::Callback<void(CreateCommandBufferResult)>
+ CreateCommandBufferCallback;
typedef base::Callback<void(const gfx::Size)> CreateImageCallback;
+ typedef base::Callback<void(const gfx::GpuMemoryBufferHandle& handle)>
+ CreateGpuMemoryBufferCallback;
+
static bool gpu_enabled() { return gpu_enabled_; }
// Creates a new GpuProcessHost or gets an existing one, resulting in the
@@ -96,7 +105,7 @@ class GpuProcessHost : public BrowserChildProcessHostDelegate,
virtual bool Send(IPC::Message* msg) OVERRIDE;
// Adds a message filter to the GpuProcessHost's channel.
- void AddFilter(IPC::ChannelProxy::MessageFilter* filter);
+ void AddFilter(IPC::MessageFilter* filter);
// Tells the GPU process to create a new channel for communication with a
// client. Once the GPU process responds asynchronously with the IPC handle
@@ -112,6 +121,7 @@ class GpuProcessHost : public BrowserChildProcessHostDelegate,
int surface_id,
int client_id,
const GPUCreateCommandBufferConfig& init_params,
+ int route_id,
const CreateCommandBufferCallback& callback);
// Tells the GPU process to create a new image using the given window.
@@ -124,6 +134,14 @@ class GpuProcessHost : public BrowserChildProcessHostDelegate,
// Tells the GPU process to delete image.
void DeleteImage(int client_id, int image_id, int sync_point);
+ void CreateGpuMemoryBuffer(const gfx::GpuMemoryBufferHandle& handle,
+ const gfx::Size& size,
+ unsigned internalformat,
+ unsigned usage,
+ const CreateGpuMemoryBufferCallback& callback);
+ void DestroyGpuMemoryBuffer(const gfx::GpuMemoryBufferHandle& handle,
+ int sync_point);
+
// What kind of GPU process, e.g. sandboxed or unsandboxed.
GpuProcessKind kind();
@@ -155,9 +173,10 @@ class GpuProcessHost : public BrowserChildProcessHostDelegate,
// Message handlers.
void OnInitialized(bool result, const gpu::GPUInfo& gpu_info);
void OnChannelEstablished(const IPC::ChannelHandle& channel_handle);
- void OnCommandBufferCreated(const int32 route_id);
+ void OnCommandBufferCreated(CreateCommandBufferResult result);
void OnDestroyCommandBuffer(int32 surface_id);
void OnImageCreated(const gfx::Size size);
+ void OnGpuMemoryBufferCreated(const gfx::GpuMemoryBufferHandle& handle);
void OnDidCreateOffscreenContext(const GURL& url);
void OnDidLoseContext(bool offscreen,
gpu::error::ContextLostReason reason,
@@ -168,16 +187,6 @@ class GpuProcessHost : public BrowserChildProcessHostDelegate,
void OnAcceleratedSurfaceBuffersSwapped(
const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params);
#endif
- // Note: Different implementations depending on USE_AURA.
-#if defined(OS_WIN)
- void OnAcceleratedSurfaceBuffersSwapped(
- const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params);
- void OnAcceleratedSurfacePostSubBuffer(
- const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params);
- void OnAcceleratedSurfaceSuspend(int32 surface_id);
- void OnAcceleratedSurfaceRelease(
- const GpuHostMsg_AcceleratedSurfaceRelease_Params& params);
-#endif
void CreateChannelCache(int32 client_id);
void OnDestroyChannel(int32 client_id);
@@ -205,6 +214,8 @@ class GpuProcessHost : public BrowserChildProcessHostDelegate,
// The pending create image requests we need to reply to.
std::queue<CreateImageCallback> create_image_requests_;
+ // The pending create gpu memory buffer requests we need to reply to.
+ std::queue<CreateGpuMemoryBufferCallback> create_gpu_memory_buffer_requests_;
// Qeueud messages to send when the process launches.
std::queue<IPC::Message*> queued_messages_;
diff --git a/chromium/content/browser/gpu/gpu_process_host_ui_shim.cc b/chromium/content/browser/gpu/gpu_process_host_ui_shim.cc
index b17bc59faae..a6a6805d176 100644
--- a/chromium/content/browser/gpu/gpu_process_host_ui_shim.cc
+++ b/chromium/content/browser/gpu/gpu_process_host_ui_shim.cc
@@ -7,7 +7,6 @@
#include <algorithm>
#include "base/bind.h"
-#include "base/command_line.h"
#include "base/debug/trace_event.h"
#include "base/id_map.h"
#include "base/lazy_instance.h"
@@ -17,14 +16,13 @@
#include "content/browser/gpu/gpu_surface_tracker.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
+#include "content/browser/renderer_host/render_widget_host_view_base.h"
#include "content/common/gpu/gpu_messages.h"
-#include "content/port/browser/render_widget_host_view_port.h"
#include "content/public/browser/browser_thread.h"
-#include "ui/gl/gl_switches.h"
-// From gl2/gl2ext.h.
-#ifndef GL_MAILBOX_SIZE_CHROMIUM
-#define GL_MAILBOX_SIZE_CHROMIUM 64
+#if defined(USE_OZONE)
+#include "ui/ozone/ozone_platform.h"
+#include "ui/ozone/public/gpu_platform_support_host.h"
#endif
namespace content {
@@ -73,7 +71,7 @@ class ScopedSendOnIOThread {
bool cancelled_;
};
-RenderWidgetHostViewPort* GetRenderWidgetHostViewFromSurfaceID(
+RenderWidgetHostViewBase* GetRenderWidgetHostViewFromSurfaceID(
int surface_id) {
int render_process_id = 0;
int render_widget_id = 0;
@@ -83,7 +81,7 @@ RenderWidgetHostViewPort* GetRenderWidgetHostViewFromSurfaceID(
RenderWidgetHost* host =
RenderWidgetHost::FromID(render_process_id, render_widget_id);
- return host ? RenderWidgetHostViewPort::FromRWHV(host->GetView()) : NULL;
+ return host ? static_cast<RenderWidgetHostViewBase*>(host->GetView()) : NULL;
}
} // namespace
@@ -97,6 +95,11 @@ void RouteToGpuProcessHostUIShimTask(int host_id, const IPC::Message& msg) {
GpuProcessHostUIShim::GpuProcessHostUIShim(int host_id)
: host_id_(host_id) {
g_hosts_by_id.Pointer()->AddWithID(this, host_id_);
+#if defined(USE_OZONE)
+ ui::OzonePlatform::GetInstance()
+ ->GetGpuPlatformSupportHost()
+ ->OnChannelEstablished(host_id, this);
+#endif
}
// static
@@ -113,6 +116,12 @@ void GpuProcessHostUIShim::Destroy(int host_id, const std::string& message) {
logging::LOG_ERROR, "GpuProcessHostUIShim",
message);
+#if defined(USE_OZONE)
+ ui::OzonePlatform::GetInstance()
+ ->GetGpuPlatformSupportHost()
+ ->OnChannelDestroyed(host_id);
+#endif
+
delete FromID(host_id);
}
@@ -152,6 +161,13 @@ bool GpuProcessHostUIShim::Send(IPC::Message* msg) {
bool GpuProcessHostUIShim::OnMessageReceived(const IPC::Message& message) {
DCHECK(CalledOnValidThread());
+#if defined(USE_OZONE)
+ if (ui::OzonePlatform::GetInstance()
+ ->GetGpuPlatformSupportHost()
+ ->OnMessageReceived(message))
+ return true;
+#endif
+
if (message.routing_id() != MSG_ROUTING_CONTROL)
return false;
@@ -201,8 +217,6 @@ bool GpuProcessHostUIShim::OnControlMessageReceived(
OnUpdateVSyncParameters)
IPC_MESSAGE_HANDLER(GpuHostMsg_FrameDrawn, OnFrameDrawn)
- IPC_MESSAGE_HANDLER(GpuHostMsg_ResizeView, OnResizeView)
-
IPC_MESSAGE_UNHANDLED_ERROR()
IPC_END_MESSAGE_MAP()
@@ -243,37 +257,9 @@ void GpuProcessHostUIShim::OnGraphicsInfoCollected(
GpuDataManagerImpl::GetInstance()->UpdateGpuInfo(gpu_info);
}
-void GpuProcessHostUIShim::OnResizeView(int32 surface_id,
- int32 route_id,
- gfx::Size size) {
- // Always respond even if the window no longer exists. The GPU process cannot
- // make progress on the resizing command buffer until it receives the
- // response.
- ScopedSendOnIOThread delayed_send(
- host_id_,
- new AcceleratedSurfaceMsg_ResizeViewACK(route_id));
-
- RenderWidgetHostViewPort* view =
- GetRenderWidgetHostViewFromSurfaceID(surface_id);
- if (!view)
- return;
-
- view->ResizeCompositingSurface(size);
-}
-
-static base::TimeDelta GetSwapDelay() {
- CommandLine* cmd_line = CommandLine::ForCurrentProcess();
- int delay = 0;
- if (cmd_line->HasSwitch(switches::kGpuSwapDelay)) {
- base::StringToInt(cmd_line->GetSwitchValueNative(
- switches::kGpuSwapDelay).c_str(), &delay);
- }
- return base::TimeDelta::FromMilliseconds(delay);
-}
-
void GpuProcessHostUIShim::OnAcceleratedSurfaceInitialized(int32 surface_id,
int32 route_id) {
- RenderWidgetHostViewPort* view =
+ RenderWidgetHostViewBase* view =
GetRenderWidgetHostViewFromSurfaceID(surface_id);
if (!view)
return;
@@ -284,35 +270,41 @@ void GpuProcessHostUIShim::OnAcceleratedSurfaceBuffersSwapped(
const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params) {
TRACE_EVENT0("renderer",
"GpuProcessHostUIShim::OnAcceleratedSurfaceBuffersSwapped");
+ if (!ui::LatencyInfo::Verify(params.latency_info,
+ "GpuHostMsg_AcceleratedSurfaceBuffersSwapped"))
+ return;
AcceleratedSurfaceMsg_BufferPresented_Params ack_params;
- ack_params.mailbox_name = params.mailbox_name;
+ ack_params.mailbox = params.mailbox;
ack_params.sync_point = 0;
ScopedSendOnIOThread delayed_send(
host_id_,
new AcceleratedSurfaceMsg_BufferPresented(params.route_id,
ack_params));
- if (!params.mailbox_name.empty() &&
- params.mailbox_name.length() != GL_MAILBOX_SIZE_CHROMIUM)
- return;
-
- RenderWidgetHostViewPort* view = GetRenderWidgetHostViewFromSurfaceID(
+ RenderWidgetHostViewBase* view = GetRenderWidgetHostViewFromSurfaceID(
params.surface_id);
if (!view)
return;
delayed_send.Cancel();
- static const base::TimeDelta swap_delay = GetSwapDelay();
- if (swap_delay.ToInternalValue())
- base::PlatformThread::Sleep(swap_delay);
+ GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params view_params = params;
+
+ RenderWidgetHostImpl* impl =
+ RenderWidgetHostImpl::From(view->GetRenderWidgetHost());
+ for (size_t i = 0; i < view_params.latency_info.size(); i++)
+ impl->AddLatencyInfoComponentIds(&view_params.latency_info[i]);
// View must send ACK message after next composite.
- view->AcceleratedSurfaceBuffersSwapped(params, host_id_);
+ view->AcceleratedSurfaceBuffersSwapped(view_params, host_id_);
view->DidReceiveRendererFrame();
}
-void GpuProcessHostUIShim::OnFrameDrawn(const ui::LatencyInfo& latency_info) {
+void GpuProcessHostUIShim::OnFrameDrawn(
+ const std::vector<ui::LatencyInfo>& latency_info) {
+ if (!ui::LatencyInfo::Verify(latency_info,
+ "GpuProcessHostUIShim::OnFrameDrawn"))
+ return;
RenderWidgetHostImpl::CompositorFrameDrawn(latency_info);
}
@@ -320,28 +312,33 @@ void GpuProcessHostUIShim::OnAcceleratedSurfacePostSubBuffer(
const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params) {
TRACE_EVENT0("renderer",
"GpuProcessHostUIShim::OnAcceleratedSurfacePostSubBuffer");
-
+ if (!ui::LatencyInfo::Verify(params.latency_info,
+ "GpuHostMsg_AcceleratedSurfacePostSubBuffer"))
+ return;
AcceleratedSurfaceMsg_BufferPresented_Params ack_params;
- ack_params.mailbox_name = params.mailbox_name;
+ ack_params.mailbox = params.mailbox;
ack_params.sync_point = 0;
- ScopedSendOnIOThread delayed_send(
+ ScopedSendOnIOThread delayed_send(
host_id_,
new AcceleratedSurfaceMsg_BufferPresented(params.route_id,
ack_params));
- if (!params.mailbox_name.empty() &&
- params.mailbox_name.length() != GL_MAILBOX_SIZE_CHROMIUM)
- return;
-
- RenderWidgetHostViewPort* view =
+ RenderWidgetHostViewBase* view =
GetRenderWidgetHostViewFromSurfaceID(params.surface_id);
if (!view)
return;
delayed_send.Cancel();
+ GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params view_params = params;
+
+ RenderWidgetHostImpl* impl =
+ RenderWidgetHostImpl::From(view->GetRenderWidgetHost());
+ for (size_t i = 0; i < view_params.latency_info.size(); i++)
+ impl->AddLatencyInfoComponentIds(&view_params.latency_info[i]);
+
// View must send ACK message after next composite.
- view->AcceleratedSurfacePostSubBuffer(params, host_id_);
+ view->AcceleratedSurfacePostSubBuffer(view_params, host_id_);
view->DidReceiveRendererFrame();
}
@@ -349,7 +346,7 @@ void GpuProcessHostUIShim::OnAcceleratedSurfaceSuspend(int32 surface_id) {
TRACE_EVENT0("renderer",
"GpuProcessHostUIShim::OnAcceleratedSurfaceSuspend");
- RenderWidgetHostViewPort* view =
+ RenderWidgetHostViewBase* view =
GetRenderWidgetHostViewFromSurfaceID(surface_id);
if (!view)
return;
@@ -359,7 +356,7 @@ void GpuProcessHostUIShim::OnAcceleratedSurfaceSuspend(int32 surface_id) {
void GpuProcessHostUIShim::OnAcceleratedSurfaceRelease(
const GpuHostMsg_AcceleratedSurfaceRelease_Params& params) {
- RenderWidgetHostViewPort* view = GetRenderWidgetHostViewFromSurfaceID(
+ RenderWidgetHostViewBase* view = GetRenderWidgetHostViewFromSurfaceID(
params.surface_id);
if (!view)
return;
diff --git a/chromium/content/browser/gpu/gpu_process_host_ui_shim.h b/chromium/content/browser/gpu/gpu_process_host_ui_shim.h
index bd7da0a37c3..2fe19789bfe 100644
--- a/chromium/content/browser/gpu/gpu_process_host_ui_shim.h
+++ b/chromium/content/browser/gpu/gpu_process_host_ui_shim.h
@@ -87,9 +87,6 @@ class GpuProcessHostUIShim : public IPC::Listener,
void OnLogMessage(int level, const std::string& header,
const std::string& message);
- void OnResizeView(int32 surface_id,
- int32 route_id,
- gfx::Size size);
void OnGraphicsInfoCollected(const gpu::GPUInfo& gpu_info);
@@ -106,7 +103,7 @@ class GpuProcessHostUIShim : public IPC::Listener,
void OnUpdateVSyncParameters(int surface_id,
base::TimeTicks timebase,
base::TimeDelta interval);
- void OnFrameDrawn(const ui::LatencyInfo& latency_info);
+ void OnFrameDrawn(const std::vector<ui::LatencyInfo>& latency_info);
// The serial number of the GpuProcessHost / GpuProcessHostUIShim pair.
int host_id_;
diff --git a/chromium/content/browser/gpu/gpu_surface_tracker.cc b/chromium/content/browser/gpu/gpu_surface_tracker.cc
index 350e6f3831c..42ce146dd4d 100644
--- a/chromium/content/browser/gpu/gpu_surface_tracker.cc
+++ b/chromium/content/browser/gpu/gpu_surface_tracker.cc
@@ -10,57 +10,8 @@
#include "base/logging.h"
-#if defined(TOOLKIT_GTK)
-#include "base/bind.h"
-#include "content/public/browser/browser_thread.h"
-#include "ui/gfx/gtk_native_view_id_manager.h"
-#endif // defined(TOOLKIT_GTK)
-
namespace content {
-namespace {
-#if defined(TOOLKIT_GTK)
-
-void ReleasePermanentXIDDispatcher(
- const gfx::PluginWindowHandle& surface) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- GtkNativeViewManager* manager = GtkNativeViewManager::GetInstance();
- manager->ReleasePermanentXID(surface);
-}
-
-// Implementation of SurfaceRef that allows GTK to ref and unref the
-// surface with the GtkNativeViewManager.
-class SurfaceRefPluginWindow : public GpuSurfaceTracker::SurfaceRef {
- public:
- SurfaceRefPluginWindow(const gfx::PluginWindowHandle& surface_ref);
- private:
- virtual ~SurfaceRefPluginWindow();
- gfx::PluginWindowHandle surface_;
-};
-
-SurfaceRefPluginWindow::SurfaceRefPluginWindow(
- const gfx::PluginWindowHandle& surface)
- : surface_(surface) {
- if (surface_ != gfx::kNullPluginWindow) {
- GtkNativeViewManager* manager = GtkNativeViewManager::GetInstance();
- if (!manager->AddRefPermanentXID(surface_)) {
- LOG(ERROR) << "Surface " << surface << " cannot be referenced.";
- }
- }
-}
-
-SurfaceRefPluginWindow::~SurfaceRefPluginWindow() {
- if (surface_ != gfx::kNullPluginWindow) {
- BrowserThread::PostTask(BrowserThread::UI,
- FROM_HERE,
- base::Bind(&ReleasePermanentXIDDispatcher,
- surface_));
- }
-}
-#endif // defined(TOOLKIT_GTK)
-} // anonymous
-
GpuSurfaceTracker::GpuSurfaceTracker()
: next_surface_id_(1) {
GpuSurfaceLookup::InitInstance(this);
@@ -134,9 +85,6 @@ void GpuSurfaceTracker::SetSurfaceHandle(int surface_id,
DCHECK(surface_map_.find(surface_id) != surface_map_.end());
SurfaceInfo& info = surface_map_[surface_id];
info.handle = handle;
-#if defined(TOOLKIT_GTK)
- info.surface_ref = new SurfaceRefPluginWindow(handle.handle);
-#endif // defined(TOOLKIT_GTK)
}
gfx::GLSurfaceHandle GpuSurfaceTracker::GetSurfaceHandle(int surface_id) {
diff --git a/chromium/content/browser/gpu/shader_disk_cache.cc b/chromium/content/browser/gpu/shader_disk_cache.cc
index fc578bc4bf0..c987cefc9a0 100644
--- a/chromium/content/browser/gpu/shader_disk_cache.cc
+++ b/chromium/content/browser/gpu/shader_disk_cache.cc
@@ -524,7 +524,7 @@ void ShaderDiskCache::Init() {
int rv = disk_cache::CreateCacheBackend(
net::SHADER_CACHE,
- net::CACHE_BACKEND_BLOCKFILE,
+ net::CACHE_BACKEND_DEFAULT,
cache_path_.Append(kGpuCachePath),
gpu::kDefaultMaxProgramCacheMemoryBytes,
true,
@@ -541,7 +541,7 @@ void ShaderDiskCache::Cache(const std::string& key, const std::string& shader) {
if (!cache_available_)
return;
- ShaderDiskCacheEntry* shim =
+ scoped_refptr<ShaderDiskCacheEntry> shim =
new ShaderDiskCacheEntry(AsWeakPtr(), key, shader);
shim->Cache();
diff --git a/chromium/content/browser/gpu/test_support_gpu.gypi b/chromium/content/browser/gpu/test_support_gpu.gypi
index 4892e9a38e9..9dcedc0cda9 100644
--- a/chromium/content/browser/gpu/test_support_gpu.gypi
+++ b/chromium/content/browser/gpu/test_support_gpu.gypi
@@ -51,12 +51,7 @@
# See comments about "xcode_settings" elsewhere in this file.
'xcode_settings': {'OTHER_LDFLAGS': ['-Wl,-ObjC']},
}],
- ['toolkit_uses_gtk == 1', {
- 'dependencies': [
- '<(src_dir)/build/linux/system.gyp:gtk',
- ],
- }],
- ['toolkit_uses_gtk == 1 or chromeos==1 or (OS=="linux" and use_aura==1)', {
+ ['chromeos==1 or (OS=="linux" and use_aura==1)', {
'dependencies': [
'<(src_dir)/build/linux/system.gyp:ssl',
],
diff --git a/chromium/content/browser/histogram_controller.cc b/chromium/content/browser/histogram_controller.cc
index 240de2556b9..3b11c32bf11 100644
--- a/chromium/content/browser/histogram_controller.cc
+++ b/chromium/content/browser/histogram_controller.cc
@@ -6,6 +6,7 @@
#include "base/bind.h"
#include "base/metrics/histogram.h"
+#include "base/process/process_handle.h"
#include "content/browser/histogram_subscriber.h"
#include "content/common/child_process_messages.h"
#include "content/public/browser/browser_child_process_host_iterator.h"
@@ -72,7 +73,8 @@ void HistogramController::GetHistogramDataFromChildProcesses(
int pending_processes = 0;
for (BrowserChildProcessHostIterator iter; !iter.Done(); ++iter) {
- int type = iter.GetData().process_type;
+ const ChildProcessData& data = iter.GetData();
+ int type = data.process_type;
if (type != PROCESS_TYPE_PLUGIN &&
type != PROCESS_TYPE_GPU &&
type != PROCESS_TYPE_PPAPI_PLUGIN &&
@@ -80,6 +82,13 @@ void HistogramController::GetHistogramDataFromChildProcesses(
continue;
}
+ // In some cases, there may be no child process of the given type (for
+ // example, the GPU process may not exist and there may instead just be a
+ // GPU thread in the browser process). If that's the case, then the process
+ // handle will be base::kNullProcessHandle and we shouldn't ask it for data.
+ if (data.handle == base::kNullProcessHandle)
+ continue;
+
++pending_processes;
if (!iter.Send(new ChildProcessMsg_GetChildHistogramData(sequence_number)))
--pending_processes;
diff --git a/chromium/content/browser/histogram_internals_request_job.cc b/chromium/content/browser/histogram_internals_request_job.cc
index dc387bf3732..aaa7a43eb26 100644
--- a/chromium/content/browser/histogram_internals_request_job.cc
+++ b/chromium/content/browser/histogram_internals_request_job.cc
@@ -18,10 +18,9 @@ HistogramInternalsRequestJob::HistogramInternalsRequestJob(
net::URLRequest* request, net::NetworkDelegate* network_delegate)
: net::URLRequestSimpleJob(request, network_delegate) {
const std::string& spec = request->url().possibly_invalid_spec();
- const url_parse::Parsed& parsed =
- request->url().parsed_for_possibly_invalid_spec();
+ const url::Parsed& parsed = request->url().parsed_for_possibly_invalid_spec();
// + 1 to skip the slash at the beginning of the path.
- int offset = parsed.CountCharactersBefore(url_parse::Parsed::PATH, false) + 1;
+ int offset = parsed.CountCharactersBefore(url::Parsed::PATH, false) + 1;
if (offset < static_cast<int>(spec.size()))
path_.assign(spec.substr(offset));
diff --git a/chromium/content/browser/histogram_message_filter.cc b/chromium/content/browser/histogram_message_filter.cc
index f44accdc349..8a58fff55f6 100644
--- a/chromium/content/browser/histogram_message_filter.cc
+++ b/chromium/content/browser/histogram_message_filter.cc
@@ -14,18 +14,18 @@
namespace content {
-HistogramMessageFilter::HistogramMessageFilter() {}
+HistogramMessageFilter::HistogramMessageFilter()
+ : BrowserMessageFilter(ChildProcessMsgStart) {}
-bool HistogramMessageFilter::OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) {
+bool HistogramMessageFilter::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(HistogramMessageFilter, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(HistogramMessageFilter, message)
IPC_MESSAGE_HANDLER(ChildProcessHostMsg_ChildHistogramData,
OnChildHistogramData)
IPC_MESSAGE_HANDLER(ChildProcessHostMsg_GetBrowserHistogram,
OnGetBrowserHistogram)
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return handled;
}
diff --git a/chromium/content/browser/histogram_message_filter.h b/chromium/content/browser/histogram_message_filter.h
index 64c13cb6987..67867061687 100644
--- a/chromium/content/browser/histogram_message_filter.h
+++ b/chromium/content/browser/histogram_message_filter.h
@@ -19,8 +19,7 @@ class HistogramMessageFilter : public BrowserMessageFilter {
HistogramMessageFilter();
// BrowserMessageFilter implementation.
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
private:
virtual ~HistogramMessageFilter();
diff --git a/chromium/content/browser/host_zoom_map_impl.cc b/chromium/content/browser/host_zoom_map_impl.cc
index e4058c2e613..1b5f7a2f7d7 100644
--- a/chromium/content/browser/host_zoom_map_impl.cc
+++ b/chromium/content/browser/host_zoom_map_impl.cc
@@ -4,13 +4,16 @@
#include "content/browser/host_zoom_map_impl.h"
+#include <algorithm>
#include <cmath>
#include "base/strings/string_piece.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
+#include "content/browser/frame_host/navigation_entry_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/web_contents/web_contents_impl.h"
#include "content/common/view_messages.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
@@ -24,6 +27,27 @@ static const char* kHostZoomMapKeyName = "content_host_zoom_map";
namespace content {
+namespace {
+
+std::string GetHostFromProcessView(int render_process_id, int render_view_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ RenderViewHost* render_view_host =
+ RenderViewHost::FromID(render_process_id, render_view_id);
+ if (!render_view_host)
+ return std::string();
+
+ WebContents* web_contents = WebContents::FromRenderViewHost(render_view_host);
+
+ NavigationEntry* entry =
+ web_contents->GetController().GetLastCommittedEntry();
+ if (!entry)
+ return std::string();
+
+ return net::GetHostOrSpecFromURL(entry->GetURL());
+}
+
+} // namespace
+
HostZoomMap* HostZoomMap::GetForBrowserContext(BrowserContext* context) {
HostZoomMapImpl* rv = static_cast<HostZoomMapImpl*>(
context->GetUserData(kHostZoomMapKeyName));
@@ -34,6 +58,22 @@ HostZoomMap* HostZoomMap::GetForBrowserContext(BrowserContext* context) {
return rv;
}
+// Helper function for setting/getting zoom levels for WebContents without
+// having to import HostZoomMapImpl everywhere.
+double HostZoomMap::GetZoomLevel(const WebContents* web_contents) {
+ HostZoomMapImpl* host_zoom_map = static_cast<HostZoomMapImpl*>(
+ HostZoomMap::GetForBrowserContext(web_contents->GetBrowserContext()));
+ return host_zoom_map->GetZoomLevelForWebContents(
+ *static_cast<const WebContentsImpl*>(web_contents));
+}
+
+void HostZoomMap::SetZoomLevel(const WebContents* web_contents, double level) {
+ HostZoomMapImpl* host_zoom_map = static_cast<HostZoomMapImpl*>(
+ HostZoomMap::GetForBrowserContext(web_contents->GetBrowserContext()));
+ host_zoom_map->SetZoomLevelForWebContents(
+ *static_cast<const WebContentsImpl*>(web_contents), level);
+}
+
HostZoomMapImpl::HostZoomMapImpl()
: default_zoom_level_(0.0) {
registrar_.Add(
@@ -68,6 +108,22 @@ double HostZoomMapImpl::GetZoomLevelForHost(const std::string& host) const {
return (i == host_zoom_levels_.end()) ? default_zoom_level_ : i->second;
}
+bool HostZoomMapImpl::HasZoomLevel(const std::string& scheme,
+ const std::string& host) const {
+ base::AutoLock auto_lock(lock_);
+
+ SchemeHostZoomLevels::const_iterator scheme_iterator(
+ scheme_host_zoom_levels_.find(scheme));
+
+ const HostZoomLevels& zoom_levels =
+ (scheme_iterator != scheme_host_zoom_levels_.end())
+ ? scheme_iterator->second
+ : host_zoom_levels_;
+
+ HostZoomLevels::const_iterator i(zoom_levels.find(host));
+ return i != zoom_levels.end();
+}
+
double HostZoomMapImpl::GetZoomLevelForHostAndScheme(
const std::string& scheme,
const std::string& host) const {
@@ -84,6 +140,42 @@ double HostZoomMapImpl::GetZoomLevelForHostAndScheme(
return GetZoomLevelForHost(host);
}
+HostZoomMap::ZoomLevelVector HostZoomMapImpl::GetAllZoomLevels() const {
+ HostZoomMap::ZoomLevelVector result;
+ {
+ base::AutoLock auto_lock(lock_);
+ result.reserve(host_zoom_levels_.size() + scheme_host_zoom_levels_.size());
+ for (HostZoomLevels::const_iterator i = host_zoom_levels_.begin();
+ i != host_zoom_levels_.end();
+ ++i) {
+ ZoomLevelChange change = {HostZoomMap::ZOOM_CHANGED_FOR_HOST,
+ i->first, // host
+ std::string(), // scheme
+ i->second // zoom level
+ };
+ result.push_back(change);
+ }
+ for (SchemeHostZoomLevels::const_iterator i =
+ scheme_host_zoom_levels_.begin();
+ i != scheme_host_zoom_levels_.end();
+ ++i) {
+ const std::string& scheme = i->first;
+ const HostZoomLevels& host_zoom_levels = i->second;
+ for (HostZoomLevels::const_iterator j = host_zoom_levels.begin();
+ j != host_zoom_levels.end();
+ ++j) {
+ ZoomLevelChange change = {HostZoomMap::ZOOM_CHANGED_FOR_SCHEME_AND_HOST,
+ j->first, // host
+ scheme, // scheme
+ j->second // zoom level
+ };
+ result.push_back(change);
+ }
+ }
+ }
+ return result;
+}
+
void HostZoomMapImpl::SetZoomLevelForHost(const std::string& host,
double level) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
@@ -97,16 +189,9 @@ void HostZoomMapImpl::SetZoomLevelForHost(const std::string& host,
host_zoom_levels_[host] = level;
}
- // Notify renderers from this browser context.
- for (RenderProcessHost::iterator i(RenderProcessHost::AllHostsIterator());
- !i.IsAtEnd(); i.Advance()) {
- RenderProcessHost* render_process_host = i.GetCurrentValue();
- if (HostZoomMap::GetForBrowserContext(
- render_process_host->GetBrowserContext()) == this) {
- render_process_host->Send(
- new ViewMsg_SetZoomLevelForCurrentURL(std::string(), host, level));
- }
- }
+ // TODO(wjmaclean) Should we use a GURL here? crbug.com/384486
+ SendZoomLevelChange(std::string(), host, level);
+
HostZoomMap::ZoomLevelChange change;
change.mode = HostZoomMap::ZOOM_CHANGED_FOR_HOST;
change.host = host;
@@ -124,16 +209,7 @@ void HostZoomMapImpl::SetZoomLevelForHostAndScheme(const std::string& scheme,
scheme_host_zoom_levels_[scheme][host] = level;
}
- // Notify renderers from this browser context.
- for (RenderProcessHost::iterator i(RenderProcessHost::AllHostsIterator());
- !i.IsAtEnd(); i.Advance()) {
- RenderProcessHost* render_process_host = i.GetCurrentValue();
- if (HostZoomMap::GetForBrowserContext(
- render_process_host->GetBrowserContext()) == this) {
- render_process_host->Send(
- new ViewMsg_SetZoomLevelForCurrentURL(scheme, host, level));
- }
- }
+ SendZoomLevelChange(scheme, host, level);
HostZoomMap::ZoomLevelChange change;
change.mode = HostZoomMap::ZOOM_CHANGED_FOR_SCHEME_AND_HOST;
@@ -158,16 +234,78 @@ HostZoomMapImpl::AddZoomLevelChangedCallback(
return zoom_level_changed_callbacks_.Add(callback);
}
+double HostZoomMapImpl::GetZoomLevelForWebContents(
+ const WebContentsImpl& web_contents_impl) const {
+ int render_process_id = web_contents_impl.GetRenderProcessHost()->GetID();
+ int routing_id = web_contents_impl.GetRenderViewHost()->GetRoutingID();
+
+ if (UsesTemporaryZoomLevel(render_process_id, routing_id))
+ return GetTemporaryZoomLevel(render_process_id, routing_id);
+
+ // Get the url from the navigation controller directly, as calling
+ // WebContentsImpl::GetLastCommittedURL() may give us a virtual url that
+ // is different than is stored in the map.
+ GURL url;
+ NavigationEntry* entry =
+ web_contents_impl.GetController().GetLastCommittedEntry();
+ // It is possible for a WebContent's zoom level to be queried before
+ // a navigation has occurred.
+ if (entry)
+ url = entry->GetURL();
+ return GetZoomLevelForHostAndScheme(url.scheme(),
+ net::GetHostOrSpecFromURL(url));
+}
+
+void HostZoomMapImpl::SetZoomLevelForWebContents(
+ const WebContentsImpl& web_contents_impl,
+ double level) {
+ int render_process_id = web_contents_impl.GetRenderProcessHost()->GetID();
+ int render_view_id = web_contents_impl.GetRenderViewHost()->GetRoutingID();
+ if (UsesTemporaryZoomLevel(render_process_id, render_view_id)) {
+ SetTemporaryZoomLevel(render_process_id, render_view_id, level);
+ } else {
+ // Get the url from the navigation controller directly, as calling
+ // WebContentsImpl::GetLastCommittedURL() may give us a virtual url that
+ // is different than what the render view is using. If the two don't match,
+ // the attempt to set the zoom will fail.
+ NavigationEntry* entry =
+ web_contents_impl.GetController().GetLastCommittedEntry();
+ // Tests may invoke this function with a null entry, but we don't
+ // want to save zoom levels in this case.
+ if (!entry)
+ return;
+
+ GURL url = entry->GetURL();
+ SetZoomLevelForHost(net::GetHostOrSpecFromURL(url), level);
+ }
+}
+
+void HostZoomMapImpl::SetZoomLevelForView(int render_process_id,
+ int render_view_id,
+ double level,
+ const std::string& host) {
+ if (UsesTemporaryZoomLevel(render_process_id, render_view_id))
+ SetTemporaryZoomLevel(render_process_id, render_view_id, level);
+ else
+ SetZoomLevelForHost(host, level);
+}
+
+bool HostZoomMapImpl::UsesTemporaryZoomLevel(int render_process_id,
+ int render_view_id) const {
+ RenderViewKey key(render_process_id, render_view_id);
+
+ base::AutoLock auto_lock(lock_);
+ return ContainsKey(temporary_zoom_levels_, key);
+}
+
double HostZoomMapImpl::GetTemporaryZoomLevel(int render_process_id,
int render_view_id) const {
base::AutoLock auto_lock(lock_);
- for (size_t i = 0; i < temporary_zoom_levels_.size(); ++i) {
- if (temporary_zoom_levels_[i].render_process_id == render_process_id &&
- temporary_zoom_levels_[i].render_view_id == render_view_id) {
- return temporary_zoom_levels_[i].zoom_level;
- }
- }
- return 0;
+ RenderViewKey key(render_process_id, render_view_id);
+ if (!ContainsKey(temporary_zoom_levels_, key))
+ return 0;
+
+ return temporary_zoom_levels_.find(key)->second;
}
void HostZoomMapImpl::SetTemporaryZoomLevel(int render_process_id,
@@ -176,31 +314,18 @@ void HostZoomMapImpl::SetTemporaryZoomLevel(int render_process_id,
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
{
+ RenderViewKey key(render_process_id, render_view_id);
base::AutoLock auto_lock(lock_);
- size_t i;
- for (i = 0; i < temporary_zoom_levels_.size(); ++i) {
- if (temporary_zoom_levels_[i].render_process_id == render_process_id &&
- temporary_zoom_levels_[i].render_view_id == render_view_id) {
- if (level) {
- temporary_zoom_levels_[i].zoom_level = level;
- } else {
- temporary_zoom_levels_.erase(temporary_zoom_levels_.begin() + i);
- }
- break;
- }
- }
-
- if (level && i == temporary_zoom_levels_.size()) {
- TemporaryZoomLevel temp;
- temp.render_process_id = render_process_id;
- temp.render_view_id = render_view_id;
- temp.zoom_level = level;
- temporary_zoom_levels_.push_back(temp);
- }
+ temporary_zoom_levels_[key] = level;
}
+ RenderViewHost* host =
+ RenderViewHost::FromID(render_process_id, render_view_id);
+ host->Send(new ViewMsg_SetZoomLevelForView(render_view_id, true, level));
+
HostZoomMap::ZoomLevelChange change;
change.mode = HostZoomMap::ZOOM_CHANGED_TEMPORARY_ZOOM;
+ change.host = GetHostFromProcessView(render_process_id, render_view_id);
change.zoom_level = level;
zoom_level_changed_callbacks_.Notify(change);
@@ -211,18 +336,10 @@ void HostZoomMapImpl::Observe(int type,
const NotificationDetails& details) {
switch (type) {
case NOTIFICATION_RENDER_VIEW_HOST_WILL_CLOSE_RENDER_VIEW: {
- base::AutoLock auto_lock(lock_);
int render_view_id = Source<RenderViewHost>(source)->GetRoutingID();
int render_process_id =
Source<RenderViewHost>(source)->GetProcess()->GetID();
-
- for (size_t i = 0; i < temporary_zoom_levels_.size(); ++i) {
- if (temporary_zoom_levels_[i].render_process_id == render_process_id &&
- temporary_zoom_levels_[i].render_view_id == render_view_id) {
- temporary_zoom_levels_.erase(temporary_zoom_levels_.begin() + i);
- break;
- }
- }
+ ClearTemporaryZoomLevel(render_process_id, render_view_id);
break;
}
default:
@@ -230,6 +347,41 @@ void HostZoomMapImpl::Observe(int type,
}
}
+void HostZoomMapImpl::ClearTemporaryZoomLevel(int render_process_id,
+ int render_view_id) {
+ {
+ base::AutoLock auto_lock(lock_);
+ RenderViewKey key(render_process_id, render_view_id);
+ TemporaryZoomLevels::iterator it = temporary_zoom_levels_.find(key);
+ if (it == temporary_zoom_levels_.end())
+ return;
+ temporary_zoom_levels_.erase(it);
+ }
+ RenderViewHost* host =
+ RenderViewHost::FromID(render_process_id, render_view_id);
+ DCHECK(host);
+ // Send a new zoom level, host-specific if one exists.
+ host->Send(new ViewMsg_SetZoomLevelForView(
+ render_view_id,
+ false,
+ GetZoomLevelForHost(
+ GetHostFromProcessView(render_process_id, render_view_id))));
+}
+
+void HostZoomMapImpl::SendZoomLevelChange(const std::string& scheme,
+ const std::string& host,
+ double level) {
+ for (RenderProcessHost::iterator i(RenderProcessHost::AllHostsIterator());
+ !i.IsAtEnd(); i.Advance()) {
+ RenderProcessHost* render_process_host = i.GetCurrentValue();
+ if (HostZoomMap::GetForBrowserContext(
+ render_process_host->GetBrowserContext()) == this) {
+ render_process_host->Send(
+ new ViewMsg_SetZoomLevelForCurrentURL(scheme, host, level));
+ }
+ }
+}
+
HostZoomMapImpl::~HostZoomMapImpl() {
}
diff --git a/chromium/content/browser/host_zoom_map_impl.h b/chromium/content/browser/host_zoom_map_impl.h
index eab10402b09..821fa0e4923 100644
--- a/chromium/content/browser/host_zoom_map_impl.h
+++ b/chromium/content/browser/host_zoom_map_impl.h
@@ -19,6 +19,8 @@
namespace content {
+class WebContentsImpl;
+
// HostZoomMap needs to be deleted on the UI thread because it listens
// to notifications on there (and holds a NotificationRegistrar).
class CONTENT_EXPORT HostZoomMapImpl : public NON_EXPORTED_BASE(HostZoomMap),
@@ -33,6 +35,10 @@ class CONTENT_EXPORT HostZoomMapImpl : public NON_EXPORTED_BASE(HostZoomMap),
virtual double GetZoomLevelForHostAndScheme(
const std::string& scheme,
const std::string& host) const OVERRIDE;
+ // TODO(wjmaclean) Should we use a GURL here? crbug.com/384486
+ virtual bool HasZoomLevel(const std::string& scheme,
+ const std::string& host) const OVERRIDE;
+ virtual ZoomLevelVector GetAllZoomLevels() const OVERRIDE;
virtual void SetZoomLevelForHost(
const std::string& host,
double level) OVERRIDE;
@@ -40,11 +46,37 @@ class CONTENT_EXPORT HostZoomMapImpl : public NON_EXPORTED_BASE(HostZoomMap),
const std::string& scheme,
const std::string& host,
double level) OVERRIDE;
+ virtual bool UsesTemporaryZoomLevel(int render_process_id,
+ int render_view_id) const OVERRIDE;
+ virtual void SetTemporaryZoomLevel(int render_process_id,
+ int render_view_id,
+ double level) OVERRIDE;
+
+ virtual void ClearTemporaryZoomLevel(int render_process_id,
+ int render_view_id) OVERRIDE;
virtual double GetDefaultZoomLevel() const OVERRIDE;
virtual void SetDefaultZoomLevel(double level) OVERRIDE;
virtual scoped_ptr<Subscription> AddZoomLevelChangedCallback(
const ZoomLevelChangedCallback& callback) OVERRIDE;
+ // Returns the current zoom level for the specified WebContents. This may
+ // be a temporary zoom level, depending on UsesTemporaryZoomLevel().
+ double GetZoomLevelForWebContents(
+ const WebContentsImpl& web_contents_impl) const;
+
+ // Sets the zoom level for this WebContents. If this WebContents is using
+ // a temporary zoom level, then level is only applied to this WebContents.
+ // Otherwise, the level will be applied on a host level.
+ void SetZoomLevelForWebContents(const WebContentsImpl& web_contents_impl,
+ double level);
+
+ // Sets the zoom level for the specified view. The level may be set for only
+ // this view, or for the host, depending on UsesTemporaryZoomLevel().
+ void SetZoomLevelForView(int render_process_id,
+ int render_view_id,
+ double level,
+ const std::string& host);
+
// Returns the temporary zoom level that's only valid for the lifetime of
// the given WebContents (i.e. isn't saved and doesn't affect other
// WebContentses) if it exists, the default zoom level otherwise.
@@ -53,25 +85,39 @@ class CONTENT_EXPORT HostZoomMapImpl : public NON_EXPORTED_BASE(HostZoomMap),
double GetTemporaryZoomLevel(int render_process_id,
int render_view_id) const;
- // Sets the temporary zoom level that's only valid for the lifetime of this
- // WebContents.
- //
- // This should only be called on the UI thread.
- void SetTemporaryZoomLevel(int render_process_id,
- int render_view_id,
- double level);
-
// NotificationObserver implementation.
virtual void Observe(int type,
const NotificationSource& source,
const NotificationDetails& details) OVERRIDE;
private:
- double GetZoomLevelForHost(const std::string& host) const;
-
typedef std::map<std::string, double> HostZoomLevels;
typedef std::map<std::string, HostZoomLevels> SchemeHostZoomLevels;
+ struct RenderViewKey {
+ int render_process_id;
+ int render_view_id;
+ RenderViewKey(int render_process_id, int render_view_id)
+ : render_process_id(render_process_id),
+ render_view_id(render_view_id) {}
+ bool operator<(const RenderViewKey& other) const {
+ return render_process_id < other.render_process_id ||
+ ((render_process_id == other.render_process_id) &&
+ (render_view_id < other.render_view_id));
+ }
+ };
+
+ typedef std::map<RenderViewKey, double> TemporaryZoomLevels;
+
+ double GetZoomLevelForHost(const std::string& host) const;
+
+ // Notifies the renderers from this browser context to change the zoom level
+ // for the specified host and scheme.
+ // TODO(wjmaclean) Should we use a GURL here? crbug.com/384486
+ void SendZoomLevelChange(const std::string& scheme,
+ const std::string& host,
+ double level);
+
// Callbacks called when zoom level changes.
base::CallbackList<void(const ZoomLevelChange&)>
zoom_level_changed_callbacks_;
@@ -81,15 +127,9 @@ class CONTENT_EXPORT HostZoomMapImpl : public NON_EXPORTED_BASE(HostZoomMap),
SchemeHostZoomLevels scheme_host_zoom_levels_;
double default_zoom_level_;
- struct TemporaryZoomLevel {
- int render_process_id;
- int render_view_id;
- double zoom_level;
- };
-
// Don't expect more than a couple of tabs that are using a temporary zoom
// level, so vector is fine for now.
- std::vector<TemporaryZoomLevel> temporary_zoom_levels_;
+ TemporaryZoomLevels temporary_zoom_levels_;
// Used around accesses to |host_zoom_levels_|, |default_zoom_level_| and
// |temporary_zoom_levels_| to guarantee thread safety.
diff --git a/chromium/content/browser/host_zoom_map_impl_unittest.cc b/chromium/content/browser/host_zoom_map_impl_unittest.cc
index df1b762e144..a96f15e288a 100644
--- a/chromium/content/browser/host_zoom_map_impl_unittest.cc
+++ b/chromium/content/browser/host_zoom_map_impl_unittest.cc
@@ -57,4 +57,29 @@ TEST_F(HostZoomMapTest, GetSetZoomLevelWithScheme) {
host_zoom_map.GetZoomLevelForHostAndScheme("http", "login"));
}
+TEST_F(HostZoomMapTest, GetAllZoomLevels) {
+ HostZoomMapImpl host_zoom_map;
+
+ double zoomed = 2.5;
+ host_zoom_map.SetZoomLevelForHost("zoomed.com", zoomed);
+ host_zoom_map.SetZoomLevelForHostAndScheme("https", "zoomed.com", zoomed);
+ host_zoom_map.SetZoomLevelForHostAndScheme("chrome", "login", zoomed);
+
+ HostZoomMap::ZoomLevelVector levels = host_zoom_map.GetAllZoomLevels();
+ HostZoomMap::ZoomLevelChange expected[] = {
+ {HostZoomMap::ZOOM_CHANGED_FOR_HOST, "zoomed.com", std::string(), zoomed},
+ {HostZoomMap::ZOOM_CHANGED_FOR_SCHEME_AND_HOST, "login", "chrome",
+ zoomed},
+ {HostZoomMap::ZOOM_CHANGED_FOR_SCHEME_AND_HOST, "zoomed.com", "https",
+ zoomed}, };
+ ASSERT_EQ(arraysize(expected), levels.size());
+ for (size_t i = 0; i < arraysize(expected); ++i) {
+ SCOPED_TRACE(testing::Message() << "levels[" << i << "]");
+ EXPECT_EQ(expected[i].mode, levels[i].mode);
+ EXPECT_EQ(expected[i].scheme, levels[i].scheme);
+ EXPECT_EQ(expected[i].host, levels[i].host);
+ EXPECT_EQ(expected[i].zoom_level, levels[i].zoom_level);
+ }
+}
+
} // namespace content
diff --git a/chromium/content/browser/indexed_db/OWNERS b/chromium/content/browser/indexed_db/OWNERS
index b106dad853f..d68c26c9187 100644
--- a/chromium/content/browser/indexed_db/OWNERS
+++ b/chromium/content/browser/indexed_db/OWNERS
@@ -1,4 +1,5 @@
dgrogan@chromium.org
michaeln@chromium.org
jsbell@chromium.org
-alecflett@chromium.org
+ericu@chromium.org
+cmumford@chromium.org
diff --git a/chromium/content/browser/indexed_db/indexed_db_active_blob_registry.cc b/chromium/content/browser/indexed_db/indexed_db_active_blob_registry.cc
new file mode 100644
index 00000000000..b65b24725af
--- /dev/null
+++ b/chromium/content/browser/indexed_db/indexed_db_active_blob_registry.cc
@@ -0,0 +1,147 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/stl_util.h"
+#include "base/task_runner.h"
+#include "content/browser/indexed_db/indexed_db_active_blob_registry.h"
+#include "content/browser/indexed_db/indexed_db_backing_store.h"
+#include "content/browser/indexed_db/indexed_db_factory.h"
+#include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
+
+namespace content {
+
+IndexedDBActiveBlobRegistry::IndexedDBActiveBlobRegistry(
+ IndexedDBBackingStore* backing_store)
+ : backing_store_(backing_store), weak_factory_(this) {}
+
+IndexedDBActiveBlobRegistry::~IndexedDBActiveBlobRegistry() {
+}
+
+void IndexedDBActiveBlobRegistry::AddBlobRef(int64 database_id,
+ int64 blob_key) {
+ DCHECK(backing_store_);
+ DCHECK(backing_store_->task_runner()->RunsTasksOnCurrentThread());
+ DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
+ DCHECK(DatabaseMetaDataKey::IsValidBlobKey(blob_key));
+ DCHECK(!ContainsKey(deleted_dbs_, database_id));
+ bool need_ref = use_tracker_.empty();
+ SingleDBMap& single_db_map = use_tracker_[database_id];
+ SingleDBMap::iterator iter = single_db_map.find(blob_key);
+ if (iter == single_db_map.end()) {
+ single_db_map[blob_key] = false;
+ if (need_ref) {
+ backing_store_->factory()->ReportOutstandingBlobs(
+ backing_store_->origin_url(), true);
+ }
+ } else {
+ DCHECK(!need_ref);
+ DCHECK(!iter->second); // You can't add a reference once it's been deleted.
+ }
+}
+
+void IndexedDBActiveBlobRegistry::ReleaseBlobRef(int64 database_id,
+ int64 blob_key) {
+ DCHECK(backing_store_);
+ DCHECK(backing_store_->task_runner()->RunsTasksOnCurrentThread());
+ DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
+ DCHECK(DatabaseMetaDataKey::IsValidBlobKey(blob_key));
+ AllDBsMap::iterator db_pair = use_tracker_.find(database_id);
+ if (db_pair == use_tracker_.end()) {
+ NOTREACHED();
+ return;
+ }
+ SingleDBMap& single_db = db_pair->second;
+ SingleDBMap::iterator blob_pair = single_db.find(blob_key);
+ if (blob_pair == single_db.end()) {
+ NOTREACHED();
+ return;
+ }
+ bool delete_in_backend = false;
+ DeletedDBSet::iterator db_to_delete = deleted_dbs_.find(database_id);
+ bool db_marked_for_deletion = db_to_delete != deleted_dbs_.end();
+ // Don't bother deleting the file if we're going to delete its whole
+ // database directory soon.
+ delete_in_backend = blob_pair->second && !db_marked_for_deletion;
+ single_db.erase(blob_pair);
+ if (single_db.empty()) {
+ use_tracker_.erase(db_pair);
+ if (db_marked_for_deletion) {
+ delete_in_backend = true;
+ blob_key = DatabaseMetaDataKey::kAllBlobsKey;
+ deleted_dbs_.erase(db_to_delete);
+ }
+ }
+ if (delete_in_backend)
+ backing_store_->ReportBlobUnused(database_id, blob_key);
+ if (use_tracker_.empty()) {
+ backing_store_->factory()->ReportOutstandingBlobs(
+ backing_store_->origin_url(), false);
+ }
+}
+
+bool IndexedDBActiveBlobRegistry::MarkDeletedCheckIfUsed(int64 database_id,
+ int64 blob_key) {
+ DCHECK(backing_store_);
+ DCHECK(backing_store_->task_runner()->RunsTasksOnCurrentThread());
+ DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
+ AllDBsMap::iterator db_pair = use_tracker_.find(database_id);
+ if (db_pair == use_tracker_.end())
+ return false;
+
+ if (blob_key == DatabaseMetaDataKey::kAllBlobsKey) {
+ deleted_dbs_.insert(database_id);
+ return true;
+ }
+
+ SingleDBMap& single_db = db_pair->second;
+ SingleDBMap::iterator iter = single_db.find(blob_key);
+ if (iter == single_db.end())
+ return false;
+
+ iter->second = true;
+ return true;
+}
+
+void IndexedDBActiveBlobRegistry::ReleaseBlobRefThreadSafe(
+ scoped_refptr<base::TaskRunner> task_runner,
+ base::WeakPtr<IndexedDBActiveBlobRegistry> weak_ptr,
+ int64 database_id,
+ int64 blob_key,
+ const base::FilePath& unused) {
+ task_runner->PostTask(FROM_HERE,
+ base::Bind(&IndexedDBActiveBlobRegistry::ReleaseBlobRef,
+ weak_ptr,
+ database_id,
+ blob_key));
+}
+
+webkit_blob::ShareableFileReference::FinalReleaseCallback
+IndexedDBActiveBlobRegistry::GetFinalReleaseCallback(int64 database_id,
+ int64 blob_key) {
+ return base::Bind(
+ &IndexedDBActiveBlobRegistry::ReleaseBlobRefThreadSafe,
+ scoped_refptr<base::TaskRunner>(backing_store_->task_runner()),
+ weak_factory_.GetWeakPtr(),
+ database_id,
+ blob_key);
+}
+
+base::Closure IndexedDBActiveBlobRegistry::GetAddBlobRefCallback(
+ int64 database_id,
+ int64 blob_key) {
+ return base::Bind(&IndexedDBActiveBlobRegistry::AddBlobRef,
+ weak_factory_.GetWeakPtr(),
+ database_id,
+ blob_key);
+}
+
+void IndexedDBActiveBlobRegistry::ForceShutdown() {
+ weak_factory_.InvalidateWeakPtrs();
+ use_tracker_.clear();
+ backing_store_ = NULL;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/indexed_db/indexed_db_active_blob_registry.h b/chromium/content/browser/indexed_db/indexed_db_active_blob_registry.h
new file mode 100644
index 00000000000..92380e13260
--- /dev/null
+++ b/chromium/content/browser/indexed_db/indexed_db_active_blob_registry.h
@@ -0,0 +1,74 @@
+// 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_INDEXED_DB_INDEXED_DB_ACTIVE_BLOB_REGISTRY_H_
+#define CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_ACTIVE_BLOB_REGISTRY_H_
+
+#include <map>
+#include <set>
+#include <utility>
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "base/memory/weak_ptr.h"
+#include "content/common/content_export.h"
+#include "webkit/common/blob/shareable_file_reference.h"
+
+namespace content {
+
+class IndexedDBBackingStore;
+
+class CONTENT_EXPORT IndexedDBActiveBlobRegistry {
+ public:
+ explicit IndexedDBActiveBlobRegistry(IndexedDBBackingStore* backing_store);
+ ~IndexedDBActiveBlobRegistry();
+
+ // Most methods of this class, and the closure returned by
+ // GetAddBlobRefCallback, should only be called on the backing_store's task
+ // runner. The exception is the closure returned by GetFinalReleaseCallback,
+ // which just calls ReleaseBlobRefThreadSafe.
+
+ // Use DatabaseMetaDataKey::AllBlobsKey for "the whole database".
+ bool MarkDeletedCheckIfUsed(int64 database_id, int64 blob_key);
+
+ webkit_blob::ShareableFileReference::FinalReleaseCallback
+ GetFinalReleaseCallback(int64 database_id, int64 blob_key);
+ // This closure holds a raw pointer to the IndexedDBActiveBlobRegistry,
+ // and may not be called after it is deleted.
+ base::Closure GetAddBlobRefCallback(int64 database_id, int64 blob_key);
+ // Call this to force the registry to drop its use counts, permitting the
+ // factory to drop any blob-related refcount for the backing store.
+ // This will also turn any outstanding callbacks into no-ops.
+ void ForceShutdown();
+
+ private:
+ void AddBlobRef(int64 database_id, int64 blob_key);
+ void ReleaseBlobRef(int64 database_id, int64 blob_key);
+ static void ReleaseBlobRefThreadSafe(
+ scoped_refptr<base::TaskRunner> task_runner,
+ base::WeakPtr<IndexedDBActiveBlobRegistry> weak_ptr,
+ int64 database_id,
+ int64 blob_key,
+ const base::FilePath& unused);
+
+ // Maps blob_key -> IsDeleted; if the record's absent, it's not in active use
+ // and we don't care if it's deleted.
+ typedef std::map<int64, bool> SingleDBMap;
+ // Maps DB ID -> SingleDBMap
+ typedef std::map<int64, SingleDBMap> AllDBsMap;
+ typedef std::set<int64> DeletedDBSet;
+
+ AllDBsMap use_tracker_;
+ DeletedDBSet deleted_dbs_;
+ // As long as we've got blobs registered in use_tracker_,
+ // backing_store_->factory() will keep backing_store_ alive for us. And
+ // backing_store_ owns us, so we'll stay alive as long as we're needed.
+ IndexedDBBackingStore* backing_store_;
+ base::WeakPtrFactory<IndexedDBActiveBlobRegistry> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(IndexedDBActiveBlobRegistry);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_ACTIVE_BLOB_REGISTRY_H_
diff --git a/chromium/content/browser/indexed_db/indexed_db_active_blob_registry_unittest.cc b/chromium/content/browser/indexed_db/indexed_db_active_blob_registry_unittest.cc
new file mode 100644
index 00000000000..c8f84758f80
--- /dev/null
+++ b/chromium/content/browser/indexed_db/indexed_db_active_blob_registry_unittest.cc
@@ -0,0 +1,274 @@
+// 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 <set>
+
+#include "base/test/test_simple_task_runner.h"
+#include "content/browser/indexed_db/indexed_db_active_blob_registry.h"
+#include "content/browser/indexed_db/indexed_db_backing_store.h"
+#include "content/browser/indexed_db/indexed_db_factory.h"
+#include "content/browser/indexed_db/indexed_db_fake_backing_store.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+namespace {
+
+class MockIDBFactory : public IndexedDBFactory {
+ public:
+ MockIDBFactory() : IndexedDBFactory(NULL), duplicate_calls_(false) {}
+
+ virtual void ReportOutstandingBlobs(const GURL& origin_url,
+ bool blobs_outstanding) OVERRIDE {
+ if (blobs_outstanding) {
+ if (origins_.count(origin_url)) {
+ duplicate_calls_ = true;
+ } else {
+ origins_.insert(origin_url);
+ }
+ } else {
+ if (!origins_.count(origin_url)) {
+ duplicate_calls_ = true;
+ } else {
+ origins_.erase(origin_url);
+ }
+ }
+ }
+
+ bool CheckNoOriginsInUse() const {
+ return !duplicate_calls_ && !origins_.size();
+ }
+ bool CheckSingleOriginInUse(const GURL& origin) const {
+ return !duplicate_calls_ && origins_.size() == 1 && origins_.count(origin);
+ }
+
+ protected:
+ virtual ~MockIDBFactory() {}
+
+ private:
+ std::set<GURL> origins_;
+ bool duplicate_calls_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockIDBFactory);
+};
+
+class MockIDBBackingStore : public IndexedDBFakeBackingStore {
+ public:
+ MockIDBBackingStore(IndexedDBFactory* factory, base::TaskRunner* task_runner)
+ : IndexedDBFakeBackingStore(factory, task_runner),
+ duplicate_calls_(false) {}
+
+ virtual void ReportBlobUnused(int64 database_id, int64 blob_key) OVERRIDE {
+ unused_blobs_.insert(std::make_pair(database_id, blob_key));
+ }
+
+ typedef std::pair<int64, int64> KeyPair;
+ typedef std::set<KeyPair> KeyPairSet;
+ bool CheckUnusedBlobsEmpty() const {
+ return !duplicate_calls_ && !unused_blobs_.size();
+ }
+ bool CheckSingleUnusedBlob(int64 database_id, int64 blob_key) const {
+ return !duplicate_calls_ && unused_blobs_.size() == 1 &&
+ unused_blobs_.count(std::make_pair(database_id, blob_key));
+ }
+
+ const KeyPairSet& unused_blobs() const { return unused_blobs_; }
+
+ protected:
+ virtual ~MockIDBBackingStore() {}
+
+ private:
+ KeyPairSet unused_blobs_;
+ bool duplicate_calls_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockIDBBackingStore);
+};
+
+// Base class for our test fixtures.
+class IndexedDBActiveBlobRegistryTest : public testing::Test {
+ public:
+ IndexedDBActiveBlobRegistryTest()
+ : task_runner_(new base::TestSimpleTaskRunner),
+ factory_(new MockIDBFactory),
+ backing_store_(new MockIDBBackingStore(factory_, task_runner_)),
+ registry_(new IndexedDBActiveBlobRegistry(backing_store_.get())) {}
+
+ void RunUntilIdle() { task_runner_->RunUntilIdle(); }
+ MockIDBFactory* factory() const { return factory_.get(); }
+ MockIDBBackingStore* backing_store() const { return backing_store_.get(); }
+ IndexedDBActiveBlobRegistry* registry() const { return registry_.get(); }
+
+ static const int64 kDatabaseId0 = 7;
+ static const int64 kDatabaseId1 = 12;
+ static const int64 kBlobKey0 = 77;
+ static const int64 kBlobKey1 = 14;
+
+ typedef webkit_blob::ShareableFileReference::FinalReleaseCallback
+ ReleaseCallback;
+
+ private:
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+ scoped_refptr<MockIDBFactory> factory_;
+ scoped_refptr<MockIDBBackingStore> backing_store_;
+ scoped_ptr<IndexedDBActiveBlobRegistry> registry_;
+
+ DISALLOW_COPY_AND_ASSIGN(IndexedDBActiveBlobRegistryTest);
+};
+
+TEST_F(IndexedDBActiveBlobRegistryTest, DeleteUnused) {
+ EXPECT_TRUE(factory()->CheckNoOriginsInUse());
+ EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
+
+ EXPECT_FALSE(registry()->MarkDeletedCheckIfUsed(kDatabaseId0, kBlobKey0));
+ RunUntilIdle();
+
+ EXPECT_TRUE(factory()->CheckNoOriginsInUse());
+ EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
+}
+
+TEST_F(IndexedDBActiveBlobRegistryTest, SimpleUse) {
+ EXPECT_TRUE(factory()->CheckNoOriginsInUse());
+ EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
+
+ base::Closure add_ref =
+ registry()->GetAddBlobRefCallback(kDatabaseId0, kBlobKey0);
+ ReleaseCallback release =
+ registry()->GetFinalReleaseCallback(kDatabaseId0, kBlobKey0);
+ add_ref.Run();
+ RunUntilIdle();
+
+ EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin_url()));
+ EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
+
+ release.Run(base::FilePath());
+ RunUntilIdle();
+
+ EXPECT_TRUE(factory()->CheckNoOriginsInUse());
+ EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
+}
+
+TEST_F(IndexedDBActiveBlobRegistryTest, DeleteWhileInUse) {
+ EXPECT_TRUE(factory()->CheckNoOriginsInUse());
+ EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
+
+ base::Closure add_ref =
+ registry()->GetAddBlobRefCallback(kDatabaseId0, kBlobKey0);
+ ReleaseCallback release =
+ registry()->GetFinalReleaseCallback(kDatabaseId0, kBlobKey0);
+
+ add_ref.Run();
+ RunUntilIdle();
+
+ EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin_url()));
+ EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
+
+ EXPECT_TRUE(registry()->MarkDeletedCheckIfUsed(kDatabaseId0, kBlobKey0));
+ RunUntilIdle();
+
+ EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin_url()));
+ EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
+
+ release.Run(base::FilePath());
+ RunUntilIdle();
+
+ EXPECT_TRUE(factory()->CheckNoOriginsInUse());
+ EXPECT_TRUE(backing_store()->CheckSingleUnusedBlob(kDatabaseId0, kBlobKey0));
+}
+
+TEST_F(IndexedDBActiveBlobRegistryTest, MultipleBlobs) {
+ EXPECT_TRUE(factory()->CheckNoOriginsInUse());
+ EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
+
+ base::Closure add_ref_00 =
+ registry()->GetAddBlobRefCallback(kDatabaseId0, kBlobKey0);
+ ReleaseCallback release_00 =
+ registry()->GetFinalReleaseCallback(kDatabaseId0, kBlobKey0);
+ base::Closure add_ref_01 =
+ registry()->GetAddBlobRefCallback(kDatabaseId0, kBlobKey1);
+ ReleaseCallback release_01 =
+ registry()->GetFinalReleaseCallback(kDatabaseId0, kBlobKey1);
+ base::Closure add_ref_10 =
+ registry()->GetAddBlobRefCallback(kDatabaseId1, kBlobKey0);
+ ReleaseCallback release_10 =
+ registry()->GetFinalReleaseCallback(kDatabaseId1, kBlobKey0);
+ base::Closure add_ref_11 =
+ registry()->GetAddBlobRefCallback(kDatabaseId1, kBlobKey1);
+ ReleaseCallback release_11 =
+ registry()->GetFinalReleaseCallback(kDatabaseId1, kBlobKey1);
+
+ add_ref_00.Run();
+ add_ref_01.Run();
+ RunUntilIdle();
+
+ EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin_url()));
+ EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
+
+ release_00.Run(base::FilePath());
+ add_ref_10.Run();
+ add_ref_11.Run();
+ RunUntilIdle();
+
+ EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin_url()));
+ EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
+
+ EXPECT_TRUE(registry()->MarkDeletedCheckIfUsed(kDatabaseId0, kBlobKey1));
+ RunUntilIdle();
+
+ EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin_url()));
+ EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
+
+ release_01.Run(base::FilePath());
+ release_11.Run(base::FilePath());
+ RunUntilIdle();
+
+ EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin_url()));
+ EXPECT_TRUE(backing_store()->CheckSingleUnusedBlob(kDatabaseId0, kBlobKey1));
+
+ release_10.Run(base::FilePath());
+ RunUntilIdle();
+
+ EXPECT_TRUE(factory()->CheckNoOriginsInUse());
+ EXPECT_TRUE(backing_store()->CheckSingleUnusedBlob(kDatabaseId0, kBlobKey1));
+}
+
+TEST_F(IndexedDBActiveBlobRegistryTest, ForceShutdown) {
+ EXPECT_TRUE(factory()->CheckNoOriginsInUse());
+ EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
+
+ base::Closure add_ref_0 =
+ registry()->GetAddBlobRefCallback(kDatabaseId0, kBlobKey0);
+ ReleaseCallback release_0 =
+ registry()->GetFinalReleaseCallback(kDatabaseId0, kBlobKey0);
+ base::Closure add_ref_1 =
+ registry()->GetAddBlobRefCallback(kDatabaseId0, kBlobKey1);
+ ReleaseCallback release_1 =
+ registry()->GetFinalReleaseCallback(kDatabaseId0, kBlobKey1);
+
+ add_ref_0.Run();
+ RunUntilIdle();
+
+ EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin_url()));
+ EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
+
+ registry()->ForceShutdown();
+
+ add_ref_1.Run();
+ RunUntilIdle();
+
+ // Nothing changes.
+ EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin_url()));
+ EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
+
+ release_0.Run(base::FilePath());
+ release_1.Run(base::FilePath());
+ RunUntilIdle();
+
+ // Nothing changes.
+ EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin_url()));
+ EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
+}
+
+} // namespace
+
+} // namespace content
diff --git a/chromium/content/browser/indexed_db/indexed_db_backing_store.cc b/chromium/content/browser/indexed_db/indexed_db_backing_store.cc
index e08a7b3c564..0a83a914f84 100644
--- a/chromium/content/browser/indexed_db/indexed_db_backing_store.cc
+++ b/chromium/content/browser/indexed_db/indexed_db_backing_store.cc
@@ -5,14 +5,23 @@
#include "content/browser/indexed_db/indexed_db_backing_store.h"
#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/format_macros.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
-#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
+#include "content/browser/child_process_security_policy_impl.h"
+#include "content/browser/indexed_db/indexed_db_blob_info.h"
+#include "content/browser/indexed_db/indexed_db_class_factory.h"
+#include "content/browser/indexed_db/indexed_db_database_error.h"
#include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
#include "content/browser/indexed_db/indexed_db_metadata.h"
#include "content/browser/indexed_db/indexed_db_tracing.h"
+#include "content/browser/indexed_db/indexed_db_value.h"
#include "content/browser/indexed_db/leveldb/leveldb_comparator.h"
#include "content/browser/indexed_db/leveldb/leveldb_database.h"
#include "content/browser/indexed_db/leveldb/leveldb_iterator.h"
@@ -20,17 +29,53 @@
#include "content/common/indexed_db/indexed_db_key.h"
#include "content/common/indexed_db/indexed_db_key_path.h"
#include "content/common/indexed_db/indexed_db_key_range.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/url_request/url_request_context.h"
#include "third_party/WebKit/public/platform/WebIDBTypes.h"
#include "third_party/WebKit/public/web/WebSerializedScriptValueVersion.h"
#include "third_party/leveldatabase/env_chromium.h"
+#include "webkit/browser/blob/blob_data_handle.h"
+#include "webkit/browser/fileapi/file_stream_writer.h"
+#include "webkit/browser/fileapi/file_writer_delegate.h"
+#include "webkit/browser/fileapi/local_file_stream_writer.h"
#include "webkit/common/database/database_identifier.h"
+using base::FilePath;
using base::StringPiece;
+using fileapi::FileWriterDelegate;
namespace content {
namespace {
+FilePath GetBlobDirectoryName(const FilePath& pathBase, int64 database_id) {
+ return pathBase.AppendASCII(base::StringPrintf("%" PRIx64, database_id));
+}
+
+FilePath GetBlobDirectoryNameForKey(const FilePath& pathBase,
+ int64 database_id,
+ int64 key) {
+ FilePath path = GetBlobDirectoryName(pathBase, database_id);
+ path = path.AppendASCII(base::StringPrintf(
+ "%02x", static_cast<int>(key & 0x000000000000ff00) >> 8));
+ return path;
+}
+
+FilePath GetBlobFileNameForKey(const FilePath& pathBase,
+ int64 database_id,
+ int64 key) {
+ FilePath path = GetBlobDirectoryNameForKey(pathBase, database_id, key);
+ path = path.AppendASCII(base::StringPrintf("%" PRIx64, key));
+ return path;
+}
+
+bool MakeIDBBlobDirectory(const FilePath& pathBase,
+ int64 database_id,
+ int64 key) {
+ FilePath path = GetBlobDirectoryNameForKey(pathBase, database_id, key);
+ return base::CreateDirectory(path);
+}
+
static std::string ComputeOriginIdentifier(const GURL& origin_url) {
return webkit_database::GetIdentifierFromOrigin(origin_url) + "@1";
}
@@ -41,6 +86,17 @@ static base::FilePath ComputeFileName(const GURL& origin_url) {
.AddExtension(FILE_PATH_LITERAL(".indexeddb.leveldb"));
}
+static base::FilePath ComputeBlobPath(const GURL& origin_url) {
+ return base::FilePath()
+ .AppendASCII(webkit_database::GetIdentifierFromOrigin(origin_url))
+ .AddExtension(FILE_PATH_LITERAL(".indexeddb.blob"));
+}
+
+static base::FilePath ComputeCorruptionFileName(const GURL& origin_url) {
+ return ComputeFileName(origin_url)
+ .Append(FILE_PATH_LITERAL("corruption_info.json"));
+}
+
} // namespace
static const int64 kKeyGeneratorInitialNumber =
@@ -69,6 +125,12 @@ enum IndexedDBBackingStoreErrorSource {
DELETE_DATABASE,
TRANSACTION_COMMIT_METHOD, // TRANSACTION_COMMIT is a WinNT.h macro
GET_DATABASE_NAMES,
+ DELETE_INDEX,
+ CLEAR_OBJECT_STORE,
+ READ_BLOB_JOURNAL,
+ DECODE_BLOB_JOURNAL,
+ GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER,
+ GET_BLOB_INFO_FOR_RECORD,
INTERNAL_ERROR_MAX,
};
@@ -84,13 +146,12 @@ static void RecordInternalError(const char* type,
->Add(location);
}
-// Use to signal conditions that usually indicate developer error, but
-// could be caused by data corruption. A macro is used instead of an
-// inline function so that the assert and log report the line number.
+// Use to signal conditions caused by data corruption.
+// A macro is used instead of an inline function so that the assert and log
+// report the line number.
#define REPORT_ERROR(type, location) \
do { \
LOG(ERROR) << "IndexedDB " type " Error: " #location; \
- NOTREACHED(); \
RecordInternalError(type, location); \
} while (0)
@@ -99,6 +160,25 @@ static void RecordInternalError(const char* type,
REPORT_ERROR("Consistency", location)
#define INTERNAL_WRITE_ERROR(location) REPORT_ERROR("Write", location)
+// Use to signal conditions that usually indicate developer error, but
+// could be caused by data corruption. A macro is used instead of an
+// inline function so that the assert and log report the line number.
+// TODO(cmumford): Improve test coverage so that all error conditions are
+// "tested" and then delete this macro.
+#define REPORT_ERROR_UNTESTED(type, location) \
+ do { \
+ LOG(ERROR) << "IndexedDB " type " Error: " #location; \
+ NOTREACHED(); \
+ RecordInternalError(type, location); \
+ } while (0)
+
+#define INTERNAL_READ_ERROR_UNTESTED(location) \
+ REPORT_ERROR_UNTESTED("Read", location)
+#define INTERNAL_CONSISTENCY_ERROR_UNTESTED(location) \
+ REPORT_ERROR_UNTESTED("Consistency", location)
+#define INTERNAL_WRITE_ERROR_UNTESTED(location) \
+ REPORT_ERROR_UNTESTED("Write", location)
+
static void PutBool(LevelDBTransaction* transaction,
const StringPiece& key,
bool value) {
@@ -107,19 +187,35 @@ static void PutBool(LevelDBTransaction* transaction,
transaction->Put(key, &buffer);
}
+// Was able to use LevelDB to read the data w/o error, but the data read was not
+// in the expected format.
+static leveldb::Status InternalInconsistencyStatus() {
+ return leveldb::Status::Corruption("Internal inconsistency");
+}
+
+static leveldb::Status InvalidDBKeyStatus() {
+ return leveldb::Status::InvalidArgument("Invalid database key ID");
+}
+
+static leveldb::Status IOErrorStatus() {
+ return leveldb::Status::IOError("IO Error");
+}
+
template <typename DBOrTransaction>
-static bool GetInt(DBOrTransaction* db,
- const StringPiece& key,
- int64* found_int,
- bool* found) {
+static leveldb::Status GetInt(DBOrTransaction* db,
+ const StringPiece& key,
+ int64* found_int,
+ bool* found) {
std::string result;
- bool ok = db->Get(key, &result, found);
- if (!ok)
- return false;
+ leveldb::Status s = db->Get(key, &result, found);
+ if (!s.ok())
+ return s;
if (!*found)
- return true;
+ return leveldb::Status::OK();
StringPiece slice(result);
- return DecodeInt(&slice, found_int) && slice.empty();
+ if (DecodeInt(&slice, found_int) && slice.empty())
+ return s;
+ return InternalInconsistencyStatus();
}
static void PutInt(LevelDBTransaction* transaction,
@@ -132,18 +228,20 @@ static void PutInt(LevelDBTransaction* transaction,
}
template <typename DBOrTransaction>
-WARN_UNUSED_RESULT static bool GetVarInt(DBOrTransaction* db,
- const StringPiece& key,
- int64* found_int,
- bool* found) {
+WARN_UNUSED_RESULT static leveldb::Status GetVarInt(DBOrTransaction* db,
+ const StringPiece& key,
+ int64* found_int,
+ bool* found) {
std::string result;
- bool ok = db->Get(key, &result, found);
- if (!ok)
- return false;
+ leveldb::Status s = db->Get(key, &result, found);
+ if (!s.ok())
+ return s;
if (!*found)
- return true;
+ return leveldb::Status::OK();
StringPiece slice(result);
- return DecodeVarInt(&slice, found_int) && slice.empty();
+ if (DecodeVarInt(&slice, found_int) && slice.empty())
+ return s;
+ return InternalInconsistencyStatus();
}
static void PutVarInt(LevelDBTransaction* transaction,
@@ -155,19 +253,22 @@ static void PutVarInt(LevelDBTransaction* transaction,
}
template <typename DBOrTransaction>
-WARN_UNUSED_RESULT static bool GetString(DBOrTransaction* db,
- const StringPiece& key,
- base::string16* found_string,
- bool* found) {
+WARN_UNUSED_RESULT static leveldb::Status GetString(
+ DBOrTransaction* db,
+ const StringPiece& key,
+ base::string16* found_string,
+ bool* found) {
std::string result;
*found = false;
- bool ok = db->Get(key, &result, found);
- if (!ok)
- return false;
+ leveldb::Status s = db->Get(key, &result, found);
+ if (!s.ok())
+ return s;
if (!*found)
- return true;
+ return leveldb::Status::OK();
StringPiece slice(result);
- return DecodeString(&slice, found_string) && slice.empty();
+ if (DecodeString(&slice, found_string) && slice.empty())
+ return s;
+ return InternalInconsistencyStatus();
}
static void PutString(LevelDBTransaction* transaction,
@@ -194,24 +295,26 @@ static int CompareIndexKeys(const StringPiece& a, const StringPiece& b) {
return Compare(a, b, true /*index_keys*/);
}
-class Comparator : public LevelDBComparator {
- public:
- virtual int Compare(const StringPiece& a, const StringPiece& b) const
- OVERRIDE {
- return content::Compare(a, b, false /*index_keys*/);
- }
- virtual const char* Name() const OVERRIDE { return "idb_cmp1"; }
-};
+int IndexedDBBackingStore::Comparator::Compare(const StringPiece& a,
+ const StringPiece& b) const {
+ return content::Compare(a, b, false /*index_keys*/);
+}
+
+const char* IndexedDBBackingStore::Comparator::Name() const {
+ return "idb_cmp1";
+}
// 0 - Initial version.
// 1 - Adds UserIntVersion to DatabaseMetaData.
// 2 - Adds DataVersion to to global metadata.
-static const int64 kLatestKnownSchemaVersion = 2;
+// 3 - Adds metadata needed for blob support.
+static const int64 kLatestKnownSchemaVersion = 3;
WARN_UNUSED_RESULT static bool IsSchemaKnown(LevelDBDatabase* db, bool* known) {
int64 db_schema_version = 0;
bool found = false;
- bool ok = GetInt(db, SchemaVersionKey::Encode(), &db_schema_version, &found);
- if (!ok)
+ leveldb::Status s =
+ GetInt(db, SchemaVersionKey::Encode(), &db_schema_version, &found);
+ if (!s.ok())
return false;
if (!found) {
*known = true;
@@ -225,8 +328,8 @@ WARN_UNUSED_RESULT static bool IsSchemaKnown(LevelDBDatabase* db, bool* known) {
const uint32 latest_known_data_version =
blink::kSerializedScriptValueVersion;
int64 db_data_version = 0;
- ok = GetInt(db, DataVersionKey::Encode(), &db_data_version, &found);
- if (!ok)
+ s = GetInt(db, DataVersionKey::Encode(), &db_data_version, &found);
+ if (!s.ok())
return false;
if (!found) {
*known = true;
@@ -242,23 +345,24 @@ WARN_UNUSED_RESULT static bool IsSchemaKnown(LevelDBDatabase* db, bool* known) {
return true;
}
-WARN_UNUSED_RESULT static bool SetUpMetadata(
- LevelDBDatabase* db,
- const std::string& origin_identifier) {
+// TODO(ericu): Move this down into the member section of this file. I'm
+// leaving it here for this CL as it's easier to see the diffs in place.
+WARN_UNUSED_RESULT bool IndexedDBBackingStore::SetUpMetadata() {
const uint32 latest_known_data_version =
blink::kSerializedScriptValueVersion;
const std::string schema_version_key = SchemaVersionKey::Encode();
const std::string data_version_key = DataVersionKey::Encode();
- scoped_refptr<LevelDBTransaction> transaction = new LevelDBTransaction(db);
+ scoped_refptr<LevelDBTransaction> transaction =
+ IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get());
int64 db_schema_version = 0;
int64 db_data_version = 0;
bool found = false;
- bool ok =
+ leveldb::Status s =
GetInt(transaction.get(), schema_version_key, &db_schema_version, &found);
- if (!ok) {
- INTERNAL_READ_ERROR(SET_UP_METADATA);
+ if (!s.ok()) {
+ INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA);
return false;
}
if (!found) {
@@ -267,6 +371,12 @@ WARN_UNUSED_RESULT static bool SetUpMetadata(
PutInt(transaction.get(), schema_version_key, db_schema_version);
db_data_version = latest_known_data_version;
PutInt(transaction.get(), data_version_key, db_data_version);
+ // If a blob directory already exists for this database, blow it away. It's
+ // leftover from a partially-purged previous generation of data.
+ if (!base::DeleteFile(blob_path_, true)) {
+ INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA);
+ return false;
+ }
} else {
// Upgrade old backing store.
DCHECK_LE(db_schema_version, kLatestKnownSchemaVersion);
@@ -274,22 +384,22 @@ WARN_UNUSED_RESULT static bool SetUpMetadata(
db_schema_version = 1;
PutInt(transaction.get(), schema_version_key, db_schema_version);
const std::string start_key =
- DatabaseNameKey::EncodeMinKeyForOrigin(origin_identifier);
+ DatabaseNameKey::EncodeMinKeyForOrigin(origin_identifier_);
const std::string stop_key =
- DatabaseNameKey::EncodeStopKeyForOrigin(origin_identifier);
- scoped_ptr<LevelDBIterator> it = db->CreateIterator();
- for (it->Seek(start_key);
- it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
- it->Next()) {
+ DatabaseNameKey::EncodeStopKeyForOrigin(origin_identifier_);
+ scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
+ for (s = it->Seek(start_key);
+ s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
+ s = it->Next()) {
int64 database_id = 0;
found = false;
- bool ok = GetInt(transaction.get(), it->Key(), &database_id, &found);
- if (!ok) {
- INTERNAL_READ_ERROR(SET_UP_METADATA);
+ s = GetInt(transaction.get(), it->Key(), &database_id, &found);
+ if (!s.ok()) {
+ INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA);
return false;
}
if (!found) {
- INTERNAL_CONSISTENCY_ERROR(SET_UP_METADATA);
+ INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_UP_METADATA);
return false;
}
std::string int_version_key = DatabaseMetaDataKey::Encode(
@@ -299,23 +409,35 @@ WARN_UNUSED_RESULT static bool SetUpMetadata(
IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
}
}
- if (db_schema_version < 2) {
+ if (s.ok() && db_schema_version < 2) {
db_schema_version = 2;
PutInt(transaction.get(), schema_version_key, db_schema_version);
db_data_version = blink::kSerializedScriptValueVersion;
PutInt(transaction.get(), data_version_key, db_data_version);
}
+ if (db_schema_version < 3) {
+ db_schema_version = 3;
+ if (!base::DeleteFile(blob_path_, true)) {
+ INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA);
+ return false;
+ }
+ }
+ }
+
+ if (!s.ok()) {
+ INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA);
+ return false;
}
// All new values will be written using this serialization version.
found = false;
- ok = GetInt(transaction.get(), data_version_key, &db_data_version, &found);
- if (!ok) {
- INTERNAL_READ_ERROR(SET_UP_METADATA);
+ s = GetInt(transaction.get(), data_version_key, &db_data_version, &found);
+ if (!s.ok()) {
+ INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA);
return false;
}
if (!found) {
- INTERNAL_CONSISTENCY_ERROR(SET_UP_METADATA);
+ INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_UP_METADATA);
return false;
}
if (db_data_version < latest_known_data_version) {
@@ -326,64 +448,292 @@ WARN_UNUSED_RESULT static bool SetUpMetadata(
DCHECK_EQ(db_schema_version, kLatestKnownSchemaVersion);
DCHECK_EQ(db_data_version, latest_known_data_version);
- if (!transaction->Commit()) {
- INTERNAL_WRITE_ERROR(SET_UP_METADATA);
+ s = transaction->Commit();
+ if (!s.ok()) {
+ INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA);
return false;
}
return true;
}
template <typename DBOrTransaction>
-WARN_UNUSED_RESULT static bool GetMaxObjectStoreId(DBOrTransaction* db,
- int64 database_id,
- int64* max_object_store_id) {
+WARN_UNUSED_RESULT static leveldb::Status GetMaxObjectStoreId(
+ DBOrTransaction* db,
+ int64 database_id,
+ int64* max_object_store_id) {
const std::string max_object_store_id_key = DatabaseMetaDataKey::Encode(
database_id, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID);
- bool ok =
- GetMaxObjectStoreId(db, max_object_store_id_key, max_object_store_id);
- return ok;
+ return GetMaxObjectStoreId(db, max_object_store_id_key, max_object_store_id);
}
template <typename DBOrTransaction>
-WARN_UNUSED_RESULT static bool GetMaxObjectStoreId(
+WARN_UNUSED_RESULT static leveldb::Status GetMaxObjectStoreId(
DBOrTransaction* db,
const std::string& max_object_store_id_key,
int64* max_object_store_id) {
*max_object_store_id = -1;
bool found = false;
- bool ok = GetInt(db, max_object_store_id_key, max_object_store_id, &found);
- if (!ok)
- return false;
+ leveldb::Status s =
+ GetInt(db, max_object_store_id_key, max_object_store_id, &found);
+ if (!s.ok())
+ return s;
if (!found)
*max_object_store_id = 0;
DCHECK_GE(*max_object_store_id, 0);
- return true;
+ return s;
}
class DefaultLevelDBFactory : public LevelDBFactory {
public:
+ DefaultLevelDBFactory() {}
virtual leveldb::Status OpenLevelDB(const base::FilePath& file_name,
const LevelDBComparator* comparator,
scoped_ptr<LevelDBDatabase>* db,
bool* is_disk_full) OVERRIDE {
return LevelDBDatabase::Open(file_name, comparator, db, is_disk_full);
}
- virtual bool DestroyLevelDB(const base::FilePath& file_name) OVERRIDE {
+ virtual leveldb::Status DestroyLevelDB(const base::FilePath& file_name)
+ OVERRIDE {
return LevelDBDatabase::Destroy(file_name);
}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DefaultLevelDBFactory);
};
+static bool GetBlobKeyGeneratorCurrentNumber(
+ LevelDBTransaction* leveldb_transaction,
+ int64 database_id,
+ int64* blob_key_generator_current_number) {
+ const std::string key_gen_key = DatabaseMetaDataKey::Encode(
+ database_id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER);
+
+ // Default to initial number if not found.
+ int64 cur_number = DatabaseMetaDataKey::kBlobKeyGeneratorInitialNumber;
+ std::string data;
+
+ bool found = false;
+ bool ok = leveldb_transaction->Get(key_gen_key, &data, &found).ok();
+ if (!ok) {
+ INTERNAL_READ_ERROR_UNTESTED(GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER);
+ return false;
+ }
+ if (found) {
+ StringPiece slice(data);
+ if (!DecodeVarInt(&slice, &cur_number) || !slice.empty() ||
+ !DatabaseMetaDataKey::IsValidBlobKey(cur_number)) {
+ INTERNAL_READ_ERROR_UNTESTED(GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER);
+ return false;
+ }
+ }
+ *blob_key_generator_current_number = cur_number;
+ return true;
+}
+
+static bool UpdateBlobKeyGeneratorCurrentNumber(
+ LevelDBTransaction* leveldb_transaction,
+ int64 database_id,
+ int64 blob_key_generator_current_number) {
+#ifndef NDEBUG
+ int64 old_number;
+ if (!GetBlobKeyGeneratorCurrentNumber(
+ leveldb_transaction, database_id, &old_number))
+ return false;
+ DCHECK_LT(old_number, blob_key_generator_current_number);
+#endif
+ DCHECK(
+ DatabaseMetaDataKey::IsValidBlobKey(blob_key_generator_current_number));
+ const std::string key = DatabaseMetaDataKey::Encode(
+ database_id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER);
+
+ PutVarInt(leveldb_transaction, key, blob_key_generator_current_number);
+ return true;
+}
+
+// TODO(ericu): Error recovery. If we persistently can't read the
+// blob journal, the safe thing to do is to clear it and leak the blobs,
+// though that may be costly. Still, database/directory deletion should always
+// clean things up, and we can write an fsck that will do a full correction if
+// need be.
+template <typename T>
+static leveldb::Status GetBlobJournal(const StringPiece& leveldb_key,
+ T* leveldb_transaction,
+ BlobJournalType* journal) {
+ std::string data;
+ bool found = false;
+ leveldb::Status s = leveldb_transaction->Get(leveldb_key, &data, &found);
+ if (!s.ok()) {
+ INTERNAL_READ_ERROR_UNTESTED(READ_BLOB_JOURNAL);
+ return s;
+ }
+ journal->clear();
+ if (!found || !data.size())
+ return leveldb::Status::OK();
+ StringPiece slice(data);
+ if (!DecodeBlobJournal(&slice, journal)) {
+ INTERNAL_READ_ERROR_UNTESTED(DECODE_BLOB_JOURNAL);
+ s = InternalInconsistencyStatus();
+ }
+ return s;
+}
+
+static void ClearBlobJournal(LevelDBTransaction* leveldb_transaction,
+ const std::string& level_db_key) {
+ leveldb_transaction->Remove(level_db_key);
+}
+
+static void UpdatePrimaryJournalWithBlobList(
+ LevelDBTransaction* leveldb_transaction,
+ const BlobJournalType& journal) {
+ const std::string leveldb_key = BlobJournalKey::Encode();
+ std::string data;
+ EncodeBlobJournal(journal, &data);
+ leveldb_transaction->Put(leveldb_key, &data);
+}
+
+static void UpdateLiveBlobJournalWithBlobList(
+ LevelDBTransaction* leveldb_transaction,
+ const BlobJournalType& journal) {
+ const std::string leveldb_key = LiveBlobJournalKey::Encode();
+ std::string data;
+ EncodeBlobJournal(journal, &data);
+ leveldb_transaction->Put(leveldb_key, &data);
+}
+
+static leveldb::Status MergeBlobsIntoLiveBlobJournal(
+ LevelDBTransaction* leveldb_transaction,
+ const BlobJournalType& journal) {
+ BlobJournalType old_journal;
+ const std::string key = LiveBlobJournalKey::Encode();
+ leveldb::Status s = GetBlobJournal(key, leveldb_transaction, &old_journal);
+ if (!s.ok())
+ return s;
+
+ old_journal.insert(old_journal.end(), journal.begin(), journal.end());
+
+ UpdateLiveBlobJournalWithBlobList(leveldb_transaction, old_journal);
+ return leveldb::Status::OK();
+}
+
+static void UpdateBlobJournalWithDatabase(
+ LevelDBDirectTransaction* leveldb_transaction,
+ int64 database_id) {
+ BlobJournalType journal;
+ journal.push_back(
+ std::make_pair(database_id, DatabaseMetaDataKey::kAllBlobsKey));
+ const std::string key = BlobJournalKey::Encode();
+ std::string data;
+ EncodeBlobJournal(journal, &data);
+ leveldb_transaction->Put(key, &data);
+}
+
+static leveldb::Status MergeDatabaseIntoLiveBlobJournal(
+ LevelDBDirectTransaction* leveldb_transaction,
+ int64 database_id) {
+ BlobJournalType journal;
+ const std::string key = LiveBlobJournalKey::Encode();
+ leveldb::Status s = GetBlobJournal(key, leveldb_transaction, &journal);
+ if (!s.ok())
+ return s;
+ journal.push_back(
+ std::make_pair(database_id, DatabaseMetaDataKey::kAllBlobsKey));
+ std::string data;
+ EncodeBlobJournal(journal, &data);
+ leveldb_transaction->Put(key, &data);
+ return leveldb::Status::OK();
+}
+
+// Blob Data is encoded as a series of:
+// { is_file [bool], key [int64 as varInt],
+// type [string-with-length, may be empty],
+// (for Blobs only) size [int64 as varInt]
+// (for Files only) fileName [string-with-length]
+// }
+// There is no length field; just read until you run out of data.
+static std::string EncodeBlobData(
+ const std::vector<IndexedDBBlobInfo*>& blob_info) {
+ std::string ret;
+ std::vector<IndexedDBBlobInfo*>::const_iterator iter;
+ for (iter = blob_info.begin(); iter != blob_info.end(); ++iter) {
+ const IndexedDBBlobInfo& info = **iter;
+ EncodeBool(info.is_file(), &ret);
+ EncodeVarInt(info.key(), &ret);
+ EncodeStringWithLength(info.type(), &ret);
+ if (info.is_file())
+ EncodeStringWithLength(info.file_name(), &ret);
+ else
+ EncodeVarInt(info.size(), &ret);
+ }
+ return ret;
+}
+
+static bool DecodeBlobData(const std::string& data,
+ std::vector<IndexedDBBlobInfo>* output) {
+ std::vector<IndexedDBBlobInfo> ret;
+ output->clear();
+ StringPiece slice(data);
+ while (!slice.empty()) {
+ bool is_file;
+ int64 key;
+ base::string16 type;
+ int64 size;
+ base::string16 file_name;
+
+ if (!DecodeBool(&slice, &is_file))
+ return false;
+ if (!DecodeVarInt(&slice, &key) ||
+ !DatabaseMetaDataKey::IsValidBlobKey(key))
+ return false;
+ if (!DecodeStringWithLength(&slice, &type))
+ return false;
+ if (is_file) {
+ if (!DecodeStringWithLength(&slice, &file_name))
+ return false;
+ ret.push_back(IndexedDBBlobInfo(key, type, file_name));
+ } else {
+ if (!DecodeVarInt(&slice, &size) || size < 0)
+ return false;
+ ret.push_back(IndexedDBBlobInfo(type, static_cast<uint64>(size), key));
+ }
+ }
+ output->swap(ret);
+
+ return true;
+}
+
IndexedDBBackingStore::IndexedDBBackingStore(
+ IndexedDBFactory* indexed_db_factory,
const GURL& origin_url,
+ const base::FilePath& blob_path,
+ net::URLRequestContext* request_context,
scoped_ptr<LevelDBDatabase> db,
- scoped_ptr<LevelDBComparator> comparator)
- : origin_url_(origin_url),
+ scoped_ptr<LevelDBComparator> comparator,
+ base::TaskRunner* task_runner)
+ : indexed_db_factory_(indexed_db_factory),
+ origin_url_(origin_url),
+ blob_path_(blob_path),
origin_identifier_(ComputeOriginIdentifier(origin_url)),
+ request_context_(request_context),
+ task_runner_(task_runner),
db_(db.Pass()),
- comparator_(comparator.Pass()) {}
+ comparator_(comparator.Pass()),
+ active_blob_registry_(this) {
+}
IndexedDBBackingStore::~IndexedDBBackingStore() {
+ if (!blob_path_.empty() && !child_process_ids_granted_.empty()) {
+ ChildProcessSecurityPolicyImpl* policy =
+ ChildProcessSecurityPolicyImpl::GetInstance();
+ std::set<int>::const_iterator iter;
+ for (iter = child_process_ids_granted_.begin();
+ iter != child_process_ids_granted_.end();
+ ++iter) {
+ policy->RevokeAllPermissionsForFile(*iter, blob_path_);
+ }
+ }
+ STLDeleteContainerPairSecondPointers(incognito_blob_map_.begin(),
+ incognito_blob_map_.end());
// db_'s destructor uses comparator_. The order of destruction is important.
db_.reset();
comparator_.reset();
@@ -417,24 +767,34 @@ enum IndexedDBBackingStoreOpenResult {
INDEXED_DB_BACKING_STORE_OPEN_DISK_FULL_DEPRECATED,
INDEXED_DB_BACKING_STORE_OPEN_ORIGIN_TOO_LONG,
INDEXED_DB_BACKING_STORE_OPEN_NO_RECOVERY,
+ INDEXED_DB_BACKING_STORE_OPEN_FAILED_PRIOR_CORRUPTION,
+ INDEXED_DB_BACKING_STORE_OPEN_FAILED_CLEANUP_JOURNAL_ERROR,
INDEXED_DB_BACKING_STORE_OPEN_MAX,
};
// static
scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
+ IndexedDBFactory* indexed_db_factory,
const GURL& origin_url,
const base::FilePath& path_base,
+ net::URLRequestContext* request_context,
blink::WebIDBDataLoss* data_loss,
std::string* data_loss_message,
- bool* disk_full) {
+ bool* disk_full,
+ base::TaskRunner* task_runner,
+ bool clean_journal) {
*data_loss = blink::WebIDBDataLossNone;
DefaultLevelDBFactory leveldb_factory;
- return IndexedDBBackingStore::Open(origin_url,
+ return IndexedDBBackingStore::Open(indexed_db_factory,
+ origin_url,
path_base,
+ request_context,
data_loss,
data_loss_message,
disk_full,
- &leveldb_factory);
+ &leveldb_factory,
+ task_runner,
+ clean_journal);
}
static std::string OriginToCustomHistogramSuffix(const GURL& origin_url) {
@@ -464,7 +824,7 @@ static void HistogramOpenStatus(IndexedDBBackingStoreOpenResult result,
}
static bool IsPathTooLong(const base::FilePath& leveldb_dir) {
- int limit = file_util::GetMaximumPathComponentLength(leveldb_dir.DirName());
+ int limit = base::GetMaximumPathComponentLength(leveldb_dir.DirName());
if (limit == -1) {
DLOG(WARNING) << "GetMaximumPathComponentLength returned -1";
// In limited testing, ChromeOS returns 143, other OSes 255.
@@ -493,14 +853,89 @@ static bool IsPathTooLong(const base::FilePath& leveldb_dir) {
return false;
}
+leveldb::Status IndexedDBBackingStore::DestroyBackingStore(
+ const base::FilePath& path_base,
+ const GURL& origin_url) {
+ const base::FilePath file_path =
+ path_base.Append(ComputeFileName(origin_url));
+ DefaultLevelDBFactory leveldb_factory;
+ return leveldb_factory.DestroyLevelDB(file_path);
+}
+
+bool IndexedDBBackingStore::ReadCorruptionInfo(const base::FilePath& path_base,
+ const GURL& origin_url,
+ std::string& message) {
+ const base::FilePath info_path =
+ path_base.Append(ComputeCorruptionFileName(origin_url));
+
+ if (IsPathTooLong(info_path))
+ return false;
+
+ const int64 max_json_len = 4096;
+ int64 file_size(0);
+ if (!GetFileSize(info_path, &file_size) || file_size > max_json_len)
+ return false;
+ if (!file_size) {
+ NOTREACHED();
+ return false;
+ }
+
+ base::File file(info_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
+ bool success = false;
+ if (file.IsValid()) {
+ std::vector<char> bytes(file_size);
+ if (file_size == file.Read(0, &bytes[0], file_size)) {
+ std::string input_js(&bytes[0], file_size);
+ base::JSONReader reader;
+ scoped_ptr<base::Value> val(reader.ReadToValue(input_js));
+ if (val && val->GetType() == base::Value::TYPE_DICTIONARY) {
+ base::DictionaryValue* dict_val =
+ static_cast<base::DictionaryValue*>(val.get());
+ success = dict_val->GetString("message", &message);
+ }
+ }
+ file.Close();
+ }
+
+ base::DeleteFile(info_path, false);
+
+ return success;
+}
+
+bool IndexedDBBackingStore::RecordCorruptionInfo(
+ const base::FilePath& path_base,
+ const GURL& origin_url,
+ const std::string& message) {
+ const base::FilePath info_path =
+ path_base.Append(ComputeCorruptionFileName(origin_url));
+ if (IsPathTooLong(info_path))
+ return false;
+
+ base::DictionaryValue root_dict;
+ root_dict.SetString("message", message);
+ std::string output_js;
+ base::JSONWriter::Write(&root_dict, &output_js);
+
+ base::File file(info_path,
+ base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+ if (!file.IsValid())
+ return false;
+ int written = file.Write(0, output_js.c_str(), output_js.length());
+ return size_t(written) == output_js.length();
+}
+
// static
scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
+ IndexedDBFactory* indexed_db_factory,
const GURL& origin_url,
const base::FilePath& path_base,
+ net::URLRequestContext* request_context,
blink::WebIDBDataLoss* data_loss,
std::string* data_loss_message,
bool* is_disk_full,
- LevelDBFactory* leveldb_factory) {
+ LevelDBFactory* leveldb_factory,
+ base::TaskRunner* task_runner,
+ bool clean_journal) {
IDB_TRACE("IndexedDBBackingStore::Open");
DCHECK(!path_base.empty());
*data_loss = blink::WebIDBDataLossNone;
@@ -509,7 +944,7 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
scoped_ptr<LevelDBComparator> comparator(new Comparator());
- if (!IsStringASCII(path_base.AsUTF8Unsafe())) {
+ if (!base::IsStringASCII(path_base.AsUTF8Unsafe())) {
HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_ATTEMPT_NON_ASCII,
origin_url);
}
@@ -523,6 +958,8 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
const base::FilePath file_path =
path_base.Append(ComputeFileName(origin_url));
+ const base::FilePath blob_path =
+ path_base.Append(ComputeBlobPath(origin_url));
if (IsPathTooLong(file_path)) {
HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_ORIGIN_TOO_LONG,
@@ -546,8 +983,17 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
bool is_schema_known = false;
if (db) {
- bool ok = IsSchemaKnown(db.get(), &is_schema_known);
- if (!ok) {
+ std::string corruption_message;
+ if (ReadCorruptionInfo(path_base, origin_url, corruption_message)) {
+ LOG(ERROR) << "IndexedDB recovering from a corrupted (and deleted) "
+ "database.";
+ HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_PRIOR_CORRUPTION,
+ origin_url);
+ db.reset();
+ *data_loss = blink::WebIDBDataLossTotal;
+ *data_loss_message =
+ "IndexedDB (database was corrupt): " + corruption_message;
+ } else if (!IsSchemaKnown(db.get(), &is_schema_known)) {
LOG(ERROR) << "IndexedDB had IO error checking schema, treating it as "
"failure to open";
HistogramOpenStatus(
@@ -580,8 +1026,8 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
} else {
DCHECK(!is_schema_known || leveldb_env::IsCorruption(status));
LOG(ERROR) << "IndexedDB backing store open failed, attempting cleanup";
- bool success = leveldb_factory->DestroyLevelDB(file_path);
- if (!success) {
+ status = leveldb_factory->DestroyLevelDB(file_path);
+ if (!status.ok()) {
LOG(ERROR) << "IndexedDB backing store cleanup failed";
HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_DESTROY_FAILED,
origin_url);
@@ -607,20 +1053,38 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
return scoped_refptr<IndexedDBBackingStore>();
}
- return Create(origin_url, db.Pass(), comparator.Pass());
+ scoped_refptr<IndexedDBBackingStore> backing_store =
+ Create(indexed_db_factory,
+ origin_url,
+ blob_path,
+ request_context,
+ db.Pass(),
+ comparator.Pass(),
+ task_runner);
+
+ if (clean_journal && backing_store &&
+ !backing_store->CleanUpBlobJournal(LiveBlobJournalKey::Encode()).ok()) {
+ HistogramOpenStatus(
+ INDEXED_DB_BACKING_STORE_OPEN_FAILED_CLEANUP_JOURNAL_ERROR, origin_url);
+ return scoped_refptr<IndexedDBBackingStore>();
+ }
+ return backing_store;
}
// static
scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::OpenInMemory(
- const GURL& origin_url) {
+ const GURL& origin_url,
+ base::TaskRunner* task_runner) {
DefaultLevelDBFactory leveldb_factory;
- return IndexedDBBackingStore::OpenInMemory(origin_url, &leveldb_factory);
+ return IndexedDBBackingStore::OpenInMemory(
+ origin_url, &leveldb_factory, task_runner);
}
// static
scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::OpenInMemory(
const GURL& origin_url,
- LevelDBFactory* leveldb_factory) {
+ LevelDBFactory* leveldb_factory,
+ base::TaskRunner* task_runner) {
IDB_TRACE("IndexedDBBackingStore::OpenInMemory");
scoped_ptr<LevelDBComparator> comparator(new Comparator());
@@ -634,26 +1098,50 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::OpenInMemory(
}
HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_MEMORY_SUCCESS, origin_url);
- return Create(origin_url, db.Pass(), comparator.Pass());
+ return Create(NULL /* indexed_db_factory */,
+ origin_url,
+ base::FilePath(),
+ NULL /* request_context */,
+ db.Pass(),
+ comparator.Pass(),
+ task_runner);
}
// static
scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Create(
+ IndexedDBFactory* indexed_db_factory,
const GURL& origin_url,
+ const base::FilePath& blob_path,
+ net::URLRequestContext* request_context,
scoped_ptr<LevelDBDatabase> db,
- scoped_ptr<LevelDBComparator> comparator) {
+ scoped_ptr<LevelDBComparator> comparator,
+ base::TaskRunner* task_runner) {
// TODO(jsbell): Handle comparator name changes.
-
scoped_refptr<IndexedDBBackingStore> backing_store(
- new IndexedDBBackingStore(origin_url, db.Pass(), comparator.Pass()));
- if (!SetUpMetadata(backing_store->db_.get(),
- backing_store->origin_identifier_))
+ new IndexedDBBackingStore(indexed_db_factory,
+ origin_url,
+ blob_path,
+ request_context,
+ db.Pass(),
+ comparator.Pass(),
+ task_runner));
+ if (!backing_store->SetUpMetadata())
return scoped_refptr<IndexedDBBackingStore>();
return backing_store;
}
-std::vector<base::string16> IndexedDBBackingStore::GetDatabaseNames() {
+void IndexedDBBackingStore::GrantChildProcessPermissions(int child_process_id) {
+ if (!child_process_ids_granted_.count(child_process_id)) {
+ child_process_ids_granted_.insert(child_process_id);
+ ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadFile(
+ child_process_id, blob_path_);
+ }
+}
+
+std::vector<base::string16> IndexedDBBackingStore::GetDatabaseNames(
+ leveldb::Status* s) {
+ *s = leveldb::Status::OK();
std::vector<base::string16> found_names;
const std::string start_key =
DatabaseNameKey::EncodeMinKeyForOrigin(origin_identifier_);
@@ -663,86 +1151,114 @@ std::vector<base::string16> IndexedDBBackingStore::GetDatabaseNames() {
DCHECK(found_names.empty());
scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
- for (it->Seek(start_key);
- it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
- it->Next()) {
+ for (*s = it->Seek(start_key);
+ s->ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
+ *s = it->Next()) {
StringPiece slice(it->Key());
DatabaseNameKey database_name_key;
- if (!DatabaseNameKey::Decode(&slice, &database_name_key)) {
- INTERNAL_CONSISTENCY_ERROR(GET_DATABASE_NAMES);
+ if (!DatabaseNameKey::Decode(&slice, &database_name_key) ||
+ !slice.empty()) {
+ INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_DATABASE_NAMES);
continue;
}
found_names.push_back(database_name_key.database_name());
}
+
+ if (!s->ok())
+ INTERNAL_READ_ERROR_UNTESTED(GET_DATABASE_NAMES);
+
return found_names;
}
-bool IndexedDBBackingStore::GetIDBDatabaseMetaData(
+leveldb::Status IndexedDBBackingStore::GetIDBDatabaseMetaData(
const base::string16& name,
IndexedDBDatabaseMetadata* metadata,
bool* found) {
const std::string key = DatabaseNameKey::Encode(origin_identifier_, name);
*found = false;
- bool ok = GetInt(db_.get(), key, &metadata->id, found);
- if (!ok) {
+ leveldb::Status s = GetInt(db_.get(), key, &metadata->id, found);
+ if (!s.ok()) {
INTERNAL_READ_ERROR(GET_IDBDATABASE_METADATA);
- return false;
+ return s;
}
if (!*found)
- return true;
-
- ok = GetString(db_.get(),
- DatabaseMetaDataKey::Encode(metadata->id,
- DatabaseMetaDataKey::USER_VERSION),
- &metadata->version,
- found);
- if (!ok) {
- INTERNAL_READ_ERROR(GET_IDBDATABASE_METADATA);
- return false;
+ return leveldb::Status::OK();
+
+ s = GetString(db_.get(),
+ DatabaseMetaDataKey::Encode(metadata->id,
+ DatabaseMetaDataKey::USER_VERSION),
+ &metadata->version,
+ found);
+ if (!s.ok()) {
+ INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
+ return s;
}
if (!*found) {
- INTERNAL_CONSISTENCY_ERROR(GET_IDBDATABASE_METADATA);
- return false;
+ INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
+ return InternalInconsistencyStatus();
}
- ok = GetVarInt(db_.get(),
- DatabaseMetaDataKey::Encode(
- metadata->id, DatabaseMetaDataKey::USER_INT_VERSION),
- &metadata->int_version,
- found);
- if (!ok) {
- INTERNAL_READ_ERROR(GET_IDBDATABASE_METADATA);
- return false;
+ s = GetVarInt(db_.get(),
+ DatabaseMetaDataKey::Encode(
+ metadata->id, DatabaseMetaDataKey::USER_INT_VERSION),
+ &metadata->int_version,
+ found);
+ if (!s.ok()) {
+ INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
+ return s;
}
if (!*found) {
- INTERNAL_CONSISTENCY_ERROR(GET_IDBDATABASE_METADATA);
- return false;
+ INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
+ return InternalInconsistencyStatus();
}
if (metadata->int_version == IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION)
metadata->int_version = IndexedDBDatabaseMetadata::NO_INT_VERSION;
- ok = GetMaxObjectStoreId(
+ s = GetMaxObjectStoreId(
db_.get(), metadata->id, &metadata->max_object_store_id);
- if (!ok) {
- INTERNAL_READ_ERROR(GET_IDBDATABASE_METADATA);
- return false;
+ if (!s.ok()) {
+ INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
}
- return true;
+ // We don't cache this, we just check it if it's there.
+ int64 blob_key_generator_current_number =
+ DatabaseMetaDataKey::kInvalidBlobKey;
+
+ s = GetVarInt(
+ db_.get(),
+ DatabaseMetaDataKey::Encode(
+ metadata->id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER),
+ &blob_key_generator_current_number,
+ found);
+ if (!s.ok()) {
+ INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
+ return s;
+ }
+ if (!*found) {
+ // This database predates blob support.
+ *found = true;
+ } else if (!DatabaseMetaDataKey::IsValidBlobKey(
+ blob_key_generator_current_number)) {
+ INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
+ return InternalInconsistencyStatus();
+ }
+
+ return s;
}
-WARN_UNUSED_RESULT static bool GetNewDatabaseId(LevelDBTransaction* transaction,
- int64* new_id) {
+WARN_UNUSED_RESULT static leveldb::Status GetNewDatabaseId(
+ LevelDBTransaction* transaction,
+ int64* new_id) {
*new_id = -1;
int64 max_database_id = -1;
bool found = false;
- bool ok =
+ leveldb::Status s =
GetInt(transaction, MaxDatabaseIdKey::Encode(), &max_database_id, &found);
- if (!ok) {
- INTERNAL_READ_ERROR(GET_NEW_DATABASE_ID);
- return false;
+ if (!s.ok()) {
+ INTERNAL_READ_ERROR_UNTESTED(GET_NEW_DATABASE_ID);
+ return s;
}
if (!found)
max_database_id = 0;
@@ -752,20 +1268,20 @@ WARN_UNUSED_RESULT static bool GetNewDatabaseId(LevelDBTransaction* transaction,
int64 database_id = max_database_id + 1;
PutInt(transaction, MaxDatabaseIdKey::Encode(), database_id);
*new_id = database_id;
- return true;
+ return leveldb::Status::OK();
}
-bool IndexedDBBackingStore::CreateIDBDatabaseMetaData(
+leveldb::Status IndexedDBBackingStore::CreateIDBDatabaseMetaData(
const base::string16& name,
const base::string16& version,
int64 int_version,
int64* row_id) {
scoped_refptr<LevelDBTransaction> transaction =
- new LevelDBTransaction(db_.get());
+ IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get());
- bool ok = GetNewDatabaseId(transaction.get(), row_id);
- if (!ok)
- return false;
+ leveldb::Status s = GetNewDatabaseId(transaction.get(), row_id);
+ if (!s.ok())
+ return s;
DCHECK_GE(*row_id, 0);
if (int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION)
@@ -782,11 +1298,16 @@ bool IndexedDBBackingStore::CreateIDBDatabaseMetaData(
DatabaseMetaDataKey::Encode(*row_id,
DatabaseMetaDataKey::USER_INT_VERSION),
int_version);
- if (!transaction->Commit()) {
- INTERNAL_WRITE_ERROR(CREATE_IDBDATABASE_METADATA);
- return false;
- }
- return true;
+ PutVarInt(
+ transaction.get(),
+ DatabaseMetaDataKey::Encode(
+ *row_id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER),
+ DatabaseMetaDataKey::kBlobKeyGeneratorInitialNumber);
+
+ s = transaction->Commit();
+ if (!s.ok())
+ INTERNAL_WRITE_ERROR_UNTESTED(CREATE_IDBDATABASE_METADATA);
+ return s;
}
bool IndexedDBBackingStore::UpdateIDBDatabaseIntVersion(
@@ -803,46 +1324,119 @@ bool IndexedDBBackingStore::UpdateIDBDatabaseIntVersion(
return true;
}
-static void DeleteRange(LevelDBTransaction* transaction,
- const std::string& begin,
- const std::string& end) {
+// If you're deleting a range that contains user keys that have blob info, this
+// won't clean up the blobs.
+static leveldb::Status DeleteRangeBasic(LevelDBTransaction* transaction,
+ const std::string& begin,
+ const std::string& end,
+ bool upper_open) {
scoped_ptr<LevelDBIterator> it = transaction->CreateIterator();
- for (it->Seek(begin); it->IsValid() && CompareKeys(it->Key(), end) < 0;
- it->Next())
+ leveldb::Status s;
+ for (s = it->Seek(begin); s.ok() && it->IsValid() &&
+ (upper_open ? CompareKeys(it->Key(), end) < 0
+ : CompareKeys(it->Key(), end) <= 0);
+ s = it->Next())
transaction->Remove(it->Key());
+ return s;
+}
+
+static leveldb::Status DeleteBlobsInRange(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ const std::string& start_key,
+ const std::string& end_key,
+ bool upper_open) {
+ scoped_ptr<LevelDBIterator> it = transaction->transaction()->CreateIterator();
+ leveldb::Status s = it->Seek(start_key);
+ for (; s.ok() && it->IsValid() &&
+ (upper_open ? CompareKeys(it->Key(), end_key) < 0
+ : CompareKeys(it->Key(), end_key) <= 0);
+ s = it->Next()) {
+ StringPiece key_piece(it->Key());
+ std::string user_key =
+ BlobEntryKey::ReencodeToObjectStoreDataKey(&key_piece);
+ if (!user_key.size()) {
+ INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
+ return InternalInconsistencyStatus();
+ }
+ transaction->PutBlobInfo(
+ database_id, object_store_id, user_key, NULL, NULL);
+ }
+ return s;
}
-bool IndexedDBBackingStore::DeleteDatabase(const base::string16& name) {
+static leveldb::Status DeleteBlobsInObjectStore(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id) {
+ std::string start_key, stop_key;
+ start_key =
+ BlobEntryKey::EncodeMinKeyForObjectStore(database_id, object_store_id);
+ stop_key =
+ BlobEntryKey::EncodeStopKeyForObjectStore(database_id, object_store_id);
+ return DeleteBlobsInRange(
+ transaction, database_id, object_store_id, start_key, stop_key, true);
+}
+
+leveldb::Status IndexedDBBackingStore::DeleteDatabase(
+ const base::string16& name) {
IDB_TRACE("IndexedDBBackingStore::DeleteDatabase");
- scoped_ptr<LevelDBWriteOnlyTransaction> transaction =
- LevelDBWriteOnlyTransaction::Create(db_.get());
+ scoped_ptr<LevelDBDirectTransaction> transaction =
+ LevelDBDirectTransaction::Create(db_.get());
+
+ leveldb::Status s;
+ s = CleanUpBlobJournal(BlobJournalKey::Encode());
+ if (!s.ok())
+ return s;
IndexedDBDatabaseMetadata metadata;
bool success = false;
- bool ok = GetIDBDatabaseMetaData(name, &metadata, &success);
- if (!ok)
- return false;
+ s = GetIDBDatabaseMetaData(name, &metadata, &success);
+ if (!s.ok())
+ return s;
if (!success)
- return true;
+ return leveldb::Status::OK();
const std::string start_key = DatabaseMetaDataKey::Encode(
metadata.id, DatabaseMetaDataKey::ORIGIN_NAME);
const std::string stop_key = DatabaseMetaDataKey::Encode(
metadata.id + 1, DatabaseMetaDataKey::ORIGIN_NAME);
scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
- for (it->Seek(start_key);
- it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
- it->Next())
+ for (s = it->Seek(start_key);
+ s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
+ s = it->Next())
transaction->Remove(it->Key());
+ if (!s.ok()) {
+ INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE);
+ return s;
+ }
const std::string key = DatabaseNameKey::Encode(origin_identifier_, name);
transaction->Remove(key);
- if (!transaction->Commit()) {
- INTERNAL_WRITE_ERROR(DELETE_DATABASE);
- return false;
+ bool need_cleanup = false;
+ if (active_blob_registry()->MarkDeletedCheckIfUsed(
+ metadata.id, DatabaseMetaDataKey::kAllBlobsKey)) {
+ s = MergeDatabaseIntoLiveBlobJournal(transaction.get(), metadata.id);
+ if (!s.ok())
+ return s;
+ } else {
+ UpdateBlobJournalWithDatabase(transaction.get(), metadata.id);
+ need_cleanup = true;
}
- return true;
+
+ s = transaction->Commit();
+ if (!s.ok()) {
+ INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE);
+ return s;
+ }
+
+ if (need_cleanup)
+ CleanUpBlobJournal(BlobJournalKey::Encode());
+
+ db_->Compact(start_key, stop_key);
+ return s;
}
static bool CheckObjectStoreAndMetaDataType(const LevelDBIterator* it,
@@ -854,23 +1448,24 @@ static bool CheckObjectStoreAndMetaDataType(const LevelDBIterator* it,
StringPiece slice(it->Key());
ObjectStoreMetaDataKey meta_data_key;
- bool ok = ObjectStoreMetaDataKey::Decode(&slice, &meta_data_key);
+ bool ok =
+ ObjectStoreMetaDataKey::Decode(&slice, &meta_data_key) && slice.empty();
DCHECK(ok);
if (meta_data_key.ObjectStoreId() != object_store_id)
return false;
if (meta_data_key.MetaDataType() != meta_data_type)
return false;
- return true;
+ return ok;
}
// TODO(jsbell): This should do some error handling rather than
// plowing ahead when bad data is encountered.
-bool IndexedDBBackingStore::GetObjectStores(
+leveldb::Status IndexedDBBackingStore::GetObjectStores(
int64 database_id,
IndexedDBDatabaseMetadata::ObjectStoreMap* object_stores) {
IDB_TRACE("IndexedDBBackingStore::GetObjectStores");
if (!KeyPrefix::IsValidDatabaseId(database_id))
- return false;
+ return InvalidDBKeyStatus();
const std::string start_key =
ObjectStoreMetaDataKey::Encode(database_id, 1, 0);
const std::string stop_key =
@@ -879,16 +1474,19 @@ bool IndexedDBBackingStore::GetObjectStores(
DCHECK(object_stores->empty());
scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
- it->Seek(start_key);
- while (it->IsValid() && CompareKeys(it->Key(), stop_key) < 0) {
+ leveldb::Status s = it->Seek(start_key);
+ while (s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0) {
StringPiece slice(it->Key());
ObjectStoreMetaDataKey meta_data_key;
- bool ok = ObjectStoreMetaDataKey::Decode(&slice, &meta_data_key);
+ bool ok =
+ ObjectStoreMetaDataKey::Decode(&slice, &meta_data_key) && slice.empty();
DCHECK(ok);
- if (meta_data_key.MetaDataType() != ObjectStoreMetaDataKey::NAME) {
- INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES);
+ if (!ok || meta_data_key.MetaDataType() != ObjectStoreMetaDataKey::NAME) {
+ INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
// Possible stale metadata, but don't fail the load.
- it->Next();
+ s = it->Next();
+ if (!s.ok())
+ break;
continue;
}
@@ -900,76 +1498,88 @@ bool IndexedDBBackingStore::GetObjectStores(
{
StringPiece slice(it->Value());
if (!DecodeString(&slice, &object_store_name) || !slice.empty())
- INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES);
+ INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
}
- it->Next();
+ s = it->Next();
+ if (!s.ok())
+ break;
if (!CheckObjectStoreAndMetaDataType(it.get(),
stop_key,
object_store_id,
ObjectStoreMetaDataKey::KEY_PATH)) {
- INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES);
+ INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
break;
}
IndexedDBKeyPath key_path;
{
StringPiece slice(it->Value());
if (!DecodeIDBKeyPath(&slice, &key_path) || !slice.empty())
- INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES);
+ INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
}
- it->Next();
+ s = it->Next();
+ if (!s.ok())
+ break;
if (!CheckObjectStoreAndMetaDataType(
it.get(),
stop_key,
object_store_id,
ObjectStoreMetaDataKey::AUTO_INCREMENT)) {
- INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES);
+ INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
break;
}
bool auto_increment;
{
StringPiece slice(it->Value());
if (!DecodeBool(&slice, &auto_increment) || !slice.empty())
- INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES);
+ INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
}
- it->Next(); // Is evicatble.
+ s = it->Next(); // Is evictable.
+ if (!s.ok())
+ break;
if (!CheckObjectStoreAndMetaDataType(it.get(),
stop_key,
object_store_id,
ObjectStoreMetaDataKey::EVICTABLE)) {
- INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES);
+ INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
break;
}
- it->Next(); // Last version.
+ s = it->Next(); // Last version.
+ if (!s.ok())
+ break;
if (!CheckObjectStoreAndMetaDataType(
it.get(),
stop_key,
object_store_id,
ObjectStoreMetaDataKey::LAST_VERSION)) {
- INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES);
+ INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
break;
}
- it->Next(); // Maximum index id allocated.
+ s = it->Next(); // Maximum index id allocated.
+ if (!s.ok())
+ break;
if (!CheckObjectStoreAndMetaDataType(
it.get(),
stop_key,
object_store_id,
ObjectStoreMetaDataKey::MAX_INDEX_ID)) {
- INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES);
+ INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
break;
}
int64 max_index_id;
{
StringPiece slice(it->Value());
if (!DecodeInt(&slice, &max_index_id) || !slice.empty())
- INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES);
+ INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
}
- it->Next(); // [optional] has key path (is not null)
+ s = it->Next(); // [optional] has key path (is not null)
+ if (!s.ok())
+ break;
if (CheckObjectStoreAndMetaDataType(it.get(),
stop_key,
object_store_id,
@@ -978,7 +1588,7 @@ bool IndexedDBBackingStore::GetObjectStores(
{
StringPiece slice(it->Value());
if (!DecodeBool(&slice, &has_key_path))
- INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES);
+ INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
}
// This check accounts for two layers of legacy coding:
// (1) Initially, has_key_path was added to distinguish null vs. string.
@@ -987,12 +1597,14 @@ bool IndexedDBBackingStore::GetObjectStores(
if (!has_key_path &&
(key_path.type() == blink::WebIDBKeyPathTypeString &&
!key_path.string().empty())) {
- INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES);
+ INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
break;
}
if (!has_key_path)
key_path = IndexedDBKeyPath();
- it->Next();
+ s = it->Next();
+ if (!s.ok())
+ break;
}
int64 key_generator_current_number = -1;
@@ -1003,13 +1615,15 @@ bool IndexedDBBackingStore::GetObjectStores(
ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER)) {
StringPiece slice(it->Value());
if (!DecodeInt(&slice, &key_generator_current_number) || !slice.empty())
- INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES);
+ INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
// TODO(jsbell): Return key_generator_current_number, cache in
// object store, and write lazily to backing store. For now,
// just assert that if it was written it was valid.
DCHECK_GE(key_generator_current_number, kKeyGeneratorInitialNumber);
- it->Next();
+ s = it->Next();
+ if (!s.ok())
+ break;
}
IndexedDBObjectStoreMetadata metadata(object_store_name,
@@ -1017,36 +1631,43 @@ bool IndexedDBBackingStore::GetObjectStores(
key_path,
auto_increment,
max_index_id);
- if (!GetIndexes(database_id, object_store_id, &metadata.indexes))
- return false;
+ s = GetIndexes(database_id, object_store_id, &metadata.indexes);
+ if (!s.ok())
+ break;
(*object_stores)[object_store_id] = metadata;
}
- return true;
+
+ if (!s.ok())
+ INTERNAL_READ_ERROR_UNTESTED(GET_OBJECT_STORES);
+
+ return s;
}
-WARN_UNUSED_RESULT static bool SetMaxObjectStoreId(
+WARN_UNUSED_RESULT static leveldb::Status SetMaxObjectStoreId(
LevelDBTransaction* transaction,
int64 database_id,
int64 object_store_id) {
const std::string max_object_store_id_key = DatabaseMetaDataKey::Encode(
database_id, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID);
int64 max_object_store_id = -1;
- bool ok = GetMaxObjectStoreId(
+ leveldb::Status s = GetMaxObjectStoreId(
transaction, max_object_store_id_key, &max_object_store_id);
- if (!ok) {
- INTERNAL_READ_ERROR(SET_MAX_OBJECT_STORE_ID);
- return false;
+ if (!s.ok()) {
+ INTERNAL_READ_ERROR_UNTESTED(SET_MAX_OBJECT_STORE_ID);
+ return s;
}
if (object_store_id <= max_object_store_id) {
- INTERNAL_CONSISTENCY_ERROR(SET_MAX_OBJECT_STORE_ID);
- return false;
+ INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_MAX_OBJECT_STORE_ID);
+ return InternalInconsistencyStatus();
}
PutInt(transaction, max_object_store_id_key, object_store_id);
- return true;
+ return s;
}
-bool IndexedDBBackingStore::CreateObjectStore(
+void IndexedDBBackingStore::Compact() { db_->CompactAll(); }
+
+leveldb::Status IndexedDBBackingStore::CreateObjectStore(
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
int64 object_store_id,
@@ -1055,10 +1676,12 @@ bool IndexedDBBackingStore::CreateObjectStore(
bool auto_increment) {
IDB_TRACE("IndexedDBBackingStore::CreateObjectStore");
if (!KeyPrefix::ValidIds(database_id, object_store_id))
- return false;
+ return InvalidDBKeyStatus();
LevelDBTransaction* leveldb_transaction = transaction->transaction();
- if (!SetMaxObjectStoreId(leveldb_transaction, database_id, object_store_id))
- return false;
+ leveldb::Status s =
+ SetMaxObjectStoreId(leveldb_transaction, database_id, object_store_id);
+ if (!s.ok())
+ return s;
const std::string name_key = ObjectStoreMetaDataKey::Encode(
database_id, object_store_id, ObjectStoreMetaDataKey::NAME);
@@ -1092,62 +1715,83 @@ bool IndexedDBBackingStore::CreateObjectStore(
key_generator_current_number_key,
kKeyGeneratorInitialNumber);
PutInt(leveldb_transaction, names_key, object_store_id);
- return true;
+ return s;
}
-bool IndexedDBBackingStore::DeleteObjectStore(
+leveldb::Status IndexedDBBackingStore::DeleteObjectStore(
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
int64 object_store_id) {
IDB_TRACE("IndexedDBBackingStore::DeleteObjectStore");
if (!KeyPrefix::ValidIds(database_id, object_store_id))
- return false;
+ return InvalidDBKeyStatus();
LevelDBTransaction* leveldb_transaction = transaction->transaction();
base::string16 object_store_name;
bool found = false;
- bool ok = GetString(
- leveldb_transaction,
- ObjectStoreMetaDataKey::Encode(
- database_id, object_store_id, ObjectStoreMetaDataKey::NAME),
- &object_store_name,
- &found);
- if (!ok) {
- INTERNAL_READ_ERROR(DELETE_OBJECT_STORE);
- return false;
+ leveldb::Status s =
+ GetString(leveldb_transaction,
+ ObjectStoreMetaDataKey::Encode(
+ database_id, object_store_id, ObjectStoreMetaDataKey::NAME),
+ &object_store_name,
+ &found);
+ if (!s.ok()) {
+ INTERNAL_READ_ERROR_UNTESTED(DELETE_OBJECT_STORE);
+ return s;
}
if (!found) {
- INTERNAL_CONSISTENCY_ERROR(DELETE_OBJECT_STORE);
- return false;
+ INTERNAL_CONSISTENCY_ERROR_UNTESTED(DELETE_OBJECT_STORE);
+ return InternalInconsistencyStatus();
}
- DeleteRange(
+ s = DeleteBlobsInObjectStore(transaction, database_id, object_store_id);
+ if (!s.ok()) {
+ INTERNAL_CONSISTENCY_ERROR_UNTESTED(DELETE_OBJECT_STORE);
+ return s;
+ }
+
+ s = DeleteRangeBasic(
leveldb_transaction,
ObjectStoreMetaDataKey::Encode(database_id, object_store_id, 0),
- ObjectStoreMetaDataKey::EncodeMaxKey(database_id, object_store_id));
+ ObjectStoreMetaDataKey::EncodeMaxKey(database_id, object_store_id),
+ true);
+
+ if (s.ok()) {
+ leveldb_transaction->Remove(
+ ObjectStoreNamesKey::Encode(database_id, object_store_name));
+
+ s = DeleteRangeBasic(
+ leveldb_transaction,
+ IndexFreeListKey::Encode(database_id, object_store_id, 0),
+ IndexFreeListKey::EncodeMaxKey(database_id, object_store_id),
+ true);
+ }
- leveldb_transaction->Remove(
- ObjectStoreNamesKey::Encode(database_id, object_store_name));
+ if (s.ok()) {
+ s = DeleteRangeBasic(
+ leveldb_transaction,
+ IndexMetaDataKey::Encode(database_id, object_store_id, 0, 0),
+ IndexMetaDataKey::EncodeMaxKey(database_id, object_store_id),
+ true);
+ }
- DeleteRange(leveldb_transaction,
- IndexFreeListKey::Encode(database_id, object_store_id, 0),
- IndexFreeListKey::EncodeMaxKey(database_id, object_store_id));
- DeleteRange(leveldb_transaction,
- IndexMetaDataKey::Encode(database_id, object_store_id, 0, 0),
- IndexMetaDataKey::EncodeMaxKey(database_id, object_store_id));
+ if (!s.ok()) {
+ INTERNAL_WRITE_ERROR_UNTESTED(DELETE_OBJECT_STORE);
+ return s;
+ }
return ClearObjectStore(transaction, database_id, object_store_id);
}
-bool IndexedDBBackingStore::GetRecord(
+leveldb::Status IndexedDBBackingStore::GetRecord(
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
int64 object_store_id,
const IndexedDBKey& key,
- std::string* record) {
+ IndexedDBValue* record) {
IDB_TRACE("IndexedDBBackingStore::GetRecord");
if (!KeyPrefix::ValidIds(database_id, object_store_id))
- return false;
+ return InvalidDBKeyStatus();
LevelDBTransaction* leveldb_transaction = transaction->transaction();
const std::string leveldb_key =
@@ -1157,30 +1801,30 @@ bool IndexedDBBackingStore::GetRecord(
record->clear();
bool found = false;
- bool ok = leveldb_transaction->Get(leveldb_key, &data, &found);
- if (!ok) {
+ leveldb::Status s = leveldb_transaction->Get(leveldb_key, &data, &found);
+ if (!s.ok()) {
INTERNAL_READ_ERROR(GET_RECORD);
- return false;
+ return s;
}
if (!found)
- return true;
+ return s;
if (data.empty()) {
- INTERNAL_READ_ERROR(GET_RECORD);
- return false;
+ INTERNAL_READ_ERROR_UNTESTED(GET_RECORD);
+ return leveldb::Status::NotFound("Record contained no data");
}
int64 version;
StringPiece slice(data);
if (!DecodeVarInt(&slice, &version)) {
- INTERNAL_READ_ERROR(GET_RECORD);
- return false;
+ INTERNAL_READ_ERROR_UNTESTED(GET_RECORD);
+ return InternalInconsistencyStatus();
}
- *record = slice.as_string();
- return true;
+ record->bits = slice.as_string();
+ return transaction->GetBlobInfoForRecord(database_id, leveldb_key, record);
}
-WARN_UNUSED_RESULT static bool GetNewVersionNumber(
+WARN_UNUSED_RESULT static leveldb::Status GetNewVersionNumber(
LevelDBTransaction* transaction,
int64 database_id,
int64 object_store_id,
@@ -1191,10 +1835,11 @@ WARN_UNUSED_RESULT static bool GetNewVersionNumber(
*new_version_number = -1;
int64 last_version = -1;
bool found = false;
- bool ok = GetInt(transaction, last_version_key, &last_version, &found);
- if (!ok) {
- INTERNAL_READ_ERROR(GET_NEW_VERSION_NUMBER);
- return false;
+ leveldb::Status s =
+ GetInt(transaction, last_version_key, &last_version, &found);
+ if (!s.ok()) {
+ INTERNAL_READ_ERROR_UNTESTED(GET_NEW_VERSION_NUMBER);
+ return s;
}
if (!found)
last_version = 0;
@@ -1208,36 +1853,45 @@ WARN_UNUSED_RESULT static bool GetNewVersionNumber(
DCHECK(version > last_version);
*new_version_number = version;
- return true;
+ return s;
}
-bool IndexedDBBackingStore::PutRecord(
+leveldb::Status IndexedDBBackingStore::PutRecord(
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
int64 object_store_id,
const IndexedDBKey& key,
- const std::string& value,
+ IndexedDBValue& value,
+ ScopedVector<webkit_blob::BlobDataHandle>* handles,
RecordIdentifier* record_identifier) {
IDB_TRACE("IndexedDBBackingStore::PutRecord");
if (!KeyPrefix::ValidIds(database_id, object_store_id))
- return false;
+ return InvalidDBKeyStatus();
DCHECK(key.IsValid());
LevelDBTransaction* leveldb_transaction = transaction->transaction();
int64 version = -1;
- bool ok = GetNewVersionNumber(
+ leveldb::Status s = GetNewVersionNumber(
leveldb_transaction, database_id, object_store_id, &version);
- if (!ok)
- return false;
+ if (!s.ok())
+ return s;
DCHECK_GE(version, 0);
- const std::string object_storedata_key =
+ const std::string object_store_data_key =
ObjectStoreDataKey::Encode(database_id, object_store_id, key);
std::string v;
EncodeVarInt(version, &v);
- v.append(value);
-
- leveldb_transaction->Put(object_storedata_key, &v);
+ v.append(value.bits);
+
+ leveldb_transaction->Put(object_store_data_key, &v);
+ s = transaction->PutBlobInfoIfNeeded(database_id,
+ object_store_id,
+ object_store_data_key,
+ &value.blob_info,
+ handles);
+ if (!s.ok())
+ return s;
+ DCHECK(!handles->size());
const std::string exists_entry_key =
ExistsEntryKey::Encode(database_id, object_store_id, key);
@@ -1248,52 +1902,124 @@ bool IndexedDBBackingStore::PutRecord(
std::string key_encoded;
EncodeIDBKey(key, &key_encoded);
record_identifier->Reset(key_encoded, version);
- return true;
+ return s;
}
-bool IndexedDBBackingStore::ClearObjectStore(
+leveldb::Status IndexedDBBackingStore::ClearObjectStore(
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
int64 object_store_id) {
IDB_TRACE("IndexedDBBackingStore::ClearObjectStore");
if (!KeyPrefix::ValidIds(database_id, object_store_id))
- return false;
+ return InvalidDBKeyStatus();
const std::string start_key =
KeyPrefix(database_id, object_store_id).Encode();
const std::string stop_key =
KeyPrefix(database_id, object_store_id + 1).Encode();
- DeleteRange(transaction->transaction(), start_key, stop_key);
- return true;
+ leveldb::Status s =
+ DeleteRangeBasic(transaction->transaction(), start_key, stop_key, true);
+ if (!s.ok()) {
+ INTERNAL_WRITE_ERROR(CLEAR_OBJECT_STORE);
+ return s;
+ }
+ return DeleteBlobsInObjectStore(transaction, database_id, object_store_id);
}
-bool IndexedDBBackingStore::DeleteRecord(
+leveldb::Status IndexedDBBackingStore::DeleteRecord(
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
int64 object_store_id,
const RecordIdentifier& record_identifier) {
IDB_TRACE("IndexedDBBackingStore::DeleteRecord");
if (!KeyPrefix::ValidIds(database_id, object_store_id))
- return false;
+ return InvalidDBKeyStatus();
LevelDBTransaction* leveldb_transaction = transaction->transaction();
const std::string object_store_data_key = ObjectStoreDataKey::Encode(
database_id, object_store_id, record_identifier.primary_key());
leveldb_transaction->Remove(object_store_data_key);
+ leveldb::Status s = transaction->PutBlobInfoIfNeeded(
+ database_id, object_store_id, object_store_data_key, NULL, NULL);
+ if (!s.ok())
+ return s;
const std::string exists_entry_key = ExistsEntryKey::Encode(
database_id, object_store_id, record_identifier.primary_key());
leveldb_transaction->Remove(exists_entry_key);
- return true;
+ return leveldb::Status::OK();
+}
+
+leveldb::Status IndexedDBBackingStore::DeleteRange(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ const IndexedDBKeyRange& key_range) {
+ leveldb::Status s;
+ scoped_ptr<IndexedDBBackingStore::Cursor> start_cursor =
+ OpenObjectStoreCursor(transaction,
+ database_id,
+ object_store_id,
+ key_range,
+ indexed_db::CURSOR_NEXT,
+ &s);
+ if (!s.ok())
+ return s;
+ if (!start_cursor)
+ return leveldb::Status::OK(); // Empty range == delete success.
+
+ scoped_ptr<IndexedDBBackingStore::Cursor> end_cursor =
+ OpenObjectStoreCursor(transaction,
+ database_id,
+ object_store_id,
+ key_range,
+ indexed_db::CURSOR_PREV,
+ &s);
+
+ if (!s.ok())
+ return s;
+ if (!end_cursor)
+ return leveldb::Status::OK(); // Empty range == delete success.
+
+ BlobEntryKey start_blob_key, end_blob_key;
+
+ std::string start_key = ObjectStoreDataKey::Encode(
+ database_id, object_store_id, start_cursor->key());
+ base::StringPiece start_key_piece(start_key);
+ if (!BlobEntryKey::FromObjectStoreDataKey(&start_key_piece, &start_blob_key))
+ return InternalInconsistencyStatus();
+ std::string stop_key = ObjectStoreDataKey::Encode(
+ database_id, object_store_id, end_cursor->key());
+ base::StringPiece stop_key_piece(stop_key);
+ if (!BlobEntryKey::FromObjectStoreDataKey(&stop_key_piece, &end_blob_key))
+ return InternalInconsistencyStatus();
+
+ s = DeleteBlobsInRange(transaction,
+ database_id,
+ object_store_id,
+ start_blob_key.Encode(),
+ end_blob_key.Encode(),
+ false);
+ if (!s.ok())
+ return s;
+ s = DeleteRangeBasic(transaction->transaction(), start_key, stop_key, false);
+ if (!s.ok())
+ return s;
+ start_key =
+ ExistsEntryKey::Encode(database_id, object_store_id, start_cursor->key());
+ stop_key =
+ ExistsEntryKey::Encode(database_id, object_store_id, end_cursor->key());
+ return DeleteRangeBasic(
+ transaction->transaction(), start_key, stop_key, false);
}
-bool IndexedDBBackingStore::GetKeyGeneratorCurrentNumber(
+leveldb::Status IndexedDBBackingStore::GetKeyGeneratorCurrentNumber(
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
int64 object_store_id,
int64* key_generator_current_number) {
if (!KeyPrefix::ValidIds(database_id, object_store_id))
- return false;
+ return InvalidDBKeyStatus();
LevelDBTransaction* leveldb_transaction = transaction->transaction();
const std::string key_generator_current_number_key =
@@ -1306,19 +2032,19 @@ bool IndexedDBBackingStore::GetKeyGeneratorCurrentNumber(
std::string data;
bool found = false;
- bool ok =
+ leveldb::Status s =
leveldb_transaction->Get(key_generator_current_number_key, &data, &found);
- if (!ok) {
- INTERNAL_READ_ERROR(GET_KEY_GENERATOR_CURRENT_NUMBER);
- return false;
+ if (!s.ok()) {
+ INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER);
+ return s;
}
if (found && !data.empty()) {
StringPiece slice(data);
if (!DecodeInt(&slice, key_generator_current_number) || !slice.empty()) {
- INTERNAL_READ_ERROR(GET_KEY_GENERATOR_CURRENT_NUMBER);
- return false;
+ INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER);
+ return InternalInconsistencyStatus();
}
- return true;
+ return s;
}
// Previously, the key generator state was not stored explicitly
@@ -1334,14 +2060,14 @@ bool IndexedDBBackingStore::GetKeyGeneratorCurrentNumber(
scoped_ptr<LevelDBIterator> it = leveldb_transaction->CreateIterator();
int64 max_numeric_key = 0;
- for (it->Seek(start_key);
- it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
- it->Next()) {
+ for (s = it->Seek(start_key);
+ s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
+ s = it->Next()) {
StringPiece slice(it->Key());
ObjectStoreDataKey data_key;
- if (!ObjectStoreDataKey::Decode(&slice, &data_key)) {
- INTERNAL_READ_ERROR(GET_KEY_GENERATOR_CURRENT_NUMBER);
- return false;
+ if (!ObjectStoreDataKey::Decode(&slice, &data_key) || !slice.empty()) {
+ INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER);
+ return InternalInconsistencyStatus();
}
scoped_ptr<IndexedDBKey> user_key = data_key.user_key();
if (user_key->type() == blink::WebIDBKeyTypeNumber) {
@@ -1351,27 +2077,31 @@ bool IndexedDBBackingStore::GetKeyGeneratorCurrentNumber(
}
}
- *key_generator_current_number = max_numeric_key + 1;
- return true;
+ if (s.ok())
+ *key_generator_current_number = max_numeric_key + 1;
+ else
+ INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER);
+
+ return s;
}
-bool IndexedDBBackingStore::MaybeUpdateKeyGeneratorCurrentNumber(
+leveldb::Status IndexedDBBackingStore::MaybeUpdateKeyGeneratorCurrentNumber(
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
int64 object_store_id,
int64 new_number,
bool check_current) {
if (!KeyPrefix::ValidIds(database_id, object_store_id))
- return false;
+ return InvalidDBKeyStatus();
if (check_current) {
int64 current_number;
- bool ok = GetKeyGeneratorCurrentNumber(
+ leveldb::Status s = GetKeyGeneratorCurrentNumber(
transaction, database_id, object_store_id, &current_number);
- if (!ok)
- return false;
+ if (!s.ok())
+ return s;
if (new_number <= current_number)
- return true;
+ return s;
}
const std::string key_generator_current_number_key =
@@ -1381,10 +2111,10 @@ bool IndexedDBBackingStore::MaybeUpdateKeyGeneratorCurrentNumber(
ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER);
PutInt(
transaction->transaction(), key_generator_current_number_key, new_number);
- return true;
+ return leveldb::Status::OK();
}
-bool IndexedDBBackingStore::KeyExistsInObjectStore(
+leveldb::Status IndexedDBBackingStore::KeyExistsInObjectStore(
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
int64 object_store_id,
@@ -1393,35 +2123,328 @@ bool IndexedDBBackingStore::KeyExistsInObjectStore(
bool* found) {
IDB_TRACE("IndexedDBBackingStore::KeyExistsInObjectStore");
if (!KeyPrefix::ValidIds(database_id, object_store_id))
- return false;
+ return InvalidDBKeyStatus();
*found = false;
const std::string leveldb_key =
ObjectStoreDataKey::Encode(database_id, object_store_id, key);
std::string data;
- bool ok = transaction->transaction()->Get(leveldb_key, &data, found);
- if (!ok) {
- INTERNAL_READ_ERROR(KEY_EXISTS_IN_OBJECT_STORE);
- return false;
+ leveldb::Status s =
+ transaction->transaction()->Get(leveldb_key, &data, found);
+ if (!s.ok()) {
+ INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_OBJECT_STORE);
+ return s;
}
if (!*found)
- return true;
+ return leveldb::Status::OK();
if (!data.size()) {
- INTERNAL_READ_ERROR(KEY_EXISTS_IN_OBJECT_STORE);
- return false;
+ INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_OBJECT_STORE);
+ return InternalInconsistencyStatus();
}
int64 version;
StringPiece slice(data);
if (!DecodeVarInt(&slice, &version))
- return false;
+ return InternalInconsistencyStatus();
std::string encoded_key;
EncodeIDBKey(key, &encoded_key);
found_record_identifier->Reset(encoded_key, version);
+ return s;
+}
+
+class IndexedDBBackingStore::Transaction::ChainedBlobWriterImpl
+ : public IndexedDBBackingStore::Transaction::ChainedBlobWriter {
+ public:
+ typedef IndexedDBBackingStore::Transaction::WriteDescriptorVec
+ WriteDescriptorVec;
+ ChainedBlobWriterImpl(
+ int64 database_id,
+ IndexedDBBackingStore* backing_store,
+ WriteDescriptorVec& blobs,
+ scoped_refptr<IndexedDBBackingStore::BlobWriteCallback> callback)
+ : waiting_for_callback_(false),
+ database_id_(database_id),
+ backing_store_(backing_store),
+ callback_(callback),
+ aborted_(false) {
+ blobs_.swap(blobs);
+ iter_ = blobs_.begin();
+ backing_store->task_runner()->PostTask(
+ FROM_HERE, base::Bind(&ChainedBlobWriterImpl::WriteNextFile, this));
+ }
+
+ virtual void set_delegate(scoped_ptr<FileWriterDelegate> delegate) OVERRIDE {
+ delegate_.reset(delegate.release());
+ }
+
+ virtual void ReportWriteCompletion(bool succeeded,
+ int64 bytes_written) OVERRIDE {
+ DCHECK(waiting_for_callback_);
+ DCHECK(!succeeded || bytes_written >= 0);
+ waiting_for_callback_ = false;
+ if (delegate_.get()) // Only present for Blob, not File.
+ content::BrowserThread::DeleteSoon(
+ content::BrowserThread::IO, FROM_HERE, delegate_.release());
+ if (aborted_) {
+ self_ref_ = NULL;
+ return;
+ }
+ if (iter_->size() != -1 && iter_->size() != bytes_written)
+ succeeded = false;
+ if (succeeded) {
+ ++iter_;
+ WriteNextFile();
+ } else {
+ callback_->Run(false);
+ }
+ }
+
+ virtual void Abort() OVERRIDE {
+ if (!waiting_for_callback_)
+ return;
+ self_ref_ = this;
+ aborted_ = true;
+ }
+
+ private:
+ virtual ~ChainedBlobWriterImpl() {}
+
+ void WriteNextFile() {
+ DCHECK(!waiting_for_callback_);
+ DCHECK(!aborted_);
+ if (iter_ == blobs_.end()) {
+ DCHECK(!self_ref_);
+ callback_->Run(true);
+ return;
+ } else {
+ if (!backing_store_->WriteBlobFile(database_id_, *iter_, this)) {
+ callback_->Run(false);
+ return;
+ }
+ waiting_for_callback_ = true;
+ }
+ }
+
+ bool waiting_for_callback_;
+ scoped_refptr<ChainedBlobWriterImpl> self_ref_;
+ WriteDescriptorVec blobs_;
+ WriteDescriptorVec::const_iterator iter_;
+ int64 database_id_;
+ IndexedDBBackingStore* backing_store_;
+ scoped_refptr<IndexedDBBackingStore::BlobWriteCallback> callback_;
+ scoped_ptr<FileWriterDelegate> delegate_;
+ bool aborted_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChainedBlobWriterImpl);
+};
+
+class LocalWriteClosure : public FileWriterDelegate::DelegateWriteCallback,
+ public base::RefCounted<LocalWriteClosure> {
+ public:
+ LocalWriteClosure(IndexedDBBackingStore::Transaction::ChainedBlobWriter*
+ chained_blob_writer,
+ base::TaskRunner* task_runner)
+ : chained_blob_writer_(chained_blob_writer),
+ task_runner_(task_runner),
+ bytes_written_(0) {}
+
+ void Run(base::File::Error rv,
+ int64 bytes,
+ FileWriterDelegate::WriteProgressStatus write_status) {
+ DCHECK_GE(bytes, 0);
+ bytes_written_ += bytes;
+ if (write_status == FileWriterDelegate::SUCCESS_IO_PENDING)
+ return; // We don't care about progress events.
+ if (rv == base::File::FILE_OK) {
+ DCHECK_EQ(write_status, FileWriterDelegate::SUCCESS_COMPLETED);
+ } else {
+ DCHECK(write_status == FileWriterDelegate::ERROR_WRITE_STARTED ||
+ write_status == FileWriterDelegate::ERROR_WRITE_NOT_STARTED);
+ }
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&LocalWriteClosure::callBlobCallbackOnIDBTaskRunner,
+ this,
+ write_status == FileWriterDelegate::SUCCESS_COMPLETED));
+ }
+
+ void writeBlobToFileOnIOThread(const FilePath& file_path,
+ const GURL& blob_url,
+ net::URLRequestContext* request_context) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+ scoped_ptr<fileapi::FileStreamWriter> writer(
+ fileapi::FileStreamWriter::CreateForLocalFile(
+ task_runner_, file_path, 0,
+ fileapi::FileStreamWriter::CREATE_NEW_FILE));
+ scoped_ptr<FileWriterDelegate> delegate(
+ new FileWriterDelegate(writer.Pass(),
+ FileWriterDelegate::FLUSH_ON_COMPLETION));
+
+ DCHECK(blob_url.is_valid());
+ scoped_ptr<net::URLRequest> blob_request(request_context->CreateRequest(
+ blob_url, net::DEFAULT_PRIORITY, delegate.get(), NULL));
+
+ delegate->Start(blob_request.Pass(),
+ base::Bind(&LocalWriteClosure::Run, this));
+ chained_blob_writer_->set_delegate(delegate.Pass());
+ }
+
+ private:
+ virtual ~LocalWriteClosure() {}
+ friend class base::RefCounted<LocalWriteClosure>;
+
+ void callBlobCallbackOnIDBTaskRunner(bool succeeded) {
+ DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ chained_blob_writer_->ReportWriteCompletion(succeeded, bytes_written_);
+ }
+
+ IndexedDBBackingStore::Transaction::ChainedBlobWriter* chained_blob_writer_;
+ base::TaskRunner* task_runner_;
+ int64 bytes_written_;
+
+ DISALLOW_COPY_AND_ASSIGN(LocalWriteClosure);
+};
+
+bool IndexedDBBackingStore::WriteBlobFile(
+ int64 database_id,
+ const Transaction::WriteDescriptor& descriptor,
+ Transaction::ChainedBlobWriter* chained_blob_writer) {
+
+ if (!MakeIDBBlobDirectory(blob_path_, database_id, descriptor.key()))
+ return false;
+
+ FilePath path = GetBlobFileName(database_id, descriptor.key());
+
+ if (descriptor.is_file()) {
+ DCHECK(!descriptor.file_path().empty());
+ if (!base::CopyFile(descriptor.file_path(), path))
+ return false;
+
+ base::File::Info info;
+ if (base::GetFileInfo(descriptor.file_path(), &info)) {
+ if (descriptor.size() != -1) {
+ if (descriptor.size() != info.size)
+ return false;
+ // The round-trip can be lossy; round to nearest millisecond.
+ int64 delta = (descriptor.last_modified() -
+ info.last_modified).InMilliseconds();
+ if (std::abs(delta) > 1)
+ return false;
+ }
+ if (!base::TouchFile(path, info.last_accessed, info.last_modified)) {
+ // TODO(ericu): Complain quietly; timestamp's probably not vital.
+ }
+ } else {
+ // TODO(ericu): Complain quietly; timestamp's probably not vital.
+ }
+
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&Transaction::ChainedBlobWriter::ReportWriteCompletion,
+ chained_blob_writer,
+ true,
+ info.size));
+ } else {
+ DCHECK(descriptor.url().is_valid());
+ scoped_refptr<LocalWriteClosure> write_closure(
+ new LocalWriteClosure(chained_blob_writer, task_runner_));
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&LocalWriteClosure::writeBlobToFileOnIOThread,
+ write_closure.get(),
+ path,
+ descriptor.url(),
+ request_context_));
+ }
return true;
}
+void IndexedDBBackingStore::ReportBlobUnused(int64 database_id,
+ int64 blob_key) {
+ DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
+ bool all_blobs = blob_key == DatabaseMetaDataKey::kAllBlobsKey;
+ DCHECK(all_blobs || DatabaseMetaDataKey::IsValidBlobKey(blob_key));
+ scoped_refptr<LevelDBTransaction> transaction =
+ IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get());
+
+ std::string live_blob_key = LiveBlobJournalKey::Encode();
+ BlobJournalType live_blob_journal;
+ if (!GetBlobJournal(live_blob_key, transaction.get(), &live_blob_journal)
+ .ok())
+ return;
+ DCHECK(live_blob_journal.size());
+
+ std::string primary_key = BlobJournalKey::Encode();
+ BlobJournalType primary_journal;
+ if (!GetBlobJournal(primary_key, transaction.get(), &primary_journal).ok())
+ return;
+
+ // There are several cases to handle. If blob_key is kAllBlobsKey, we want to
+ // remove all entries with database_id from the live_blob journal and add only
+ // kAllBlobsKey to the primary journal. Otherwise if IsValidBlobKey(blob_key)
+ // and we hit kAllBlobsKey for the right database_id in the journal, we leave
+ // the kAllBlobsKey entry in the live_blob journal but add the specific blob
+ // to the primary. Otherwise if IsValidBlobKey(blob_key) and we find a
+ // matching (database_id, blob_key) tuple, we should move it to the primary
+ // journal.
+ BlobJournalType new_live_blob_journal;
+ for (BlobJournalType::iterator journal_iter = live_blob_journal.begin();
+ journal_iter != live_blob_journal.end();
+ ++journal_iter) {
+ int64 current_database_id = journal_iter->first;
+ int64 current_blob_key = journal_iter->second;
+ bool current_all_blobs =
+ current_blob_key == DatabaseMetaDataKey::kAllBlobsKey;
+ DCHECK(KeyPrefix::IsValidDatabaseId(current_database_id) ||
+ current_all_blobs);
+ if (current_database_id == database_id &&
+ (all_blobs || current_all_blobs || blob_key == current_blob_key)) {
+ if (!all_blobs) {
+ primary_journal.push_back(
+ std::make_pair(database_id, current_blob_key));
+ if (current_all_blobs)
+ new_live_blob_journal.push_back(*journal_iter);
+ new_live_blob_journal.insert(new_live_blob_journal.end(),
+ ++journal_iter,
+ live_blob_journal.end()); // All the rest.
+ break;
+ }
+ } else {
+ new_live_blob_journal.push_back(*journal_iter);
+ }
+ }
+ if (all_blobs) {
+ primary_journal.push_back(
+ std::make_pair(database_id, DatabaseMetaDataKey::kAllBlobsKey));
+ }
+ UpdatePrimaryJournalWithBlobList(transaction.get(), primary_journal);
+ UpdateLiveBlobJournalWithBlobList(transaction.get(), new_live_blob_journal);
+ transaction->Commit();
+ // We could just do the deletions/cleaning here, but if there are a lot of
+ // blobs about to be garbage collected, it'd be better to wait and do them all
+ // at once.
+ StartJournalCleaningTimer();
+}
+
+// The this reference is a raw pointer that's declared Unretained inside the
+// timer code, so this won't confuse IndexedDBFactory's check for
+// HasLastBackingStoreReference. It's safe because if the backing store is
+// deleted, the timer will automatically be canceled on destruction.
+void IndexedDBBackingStore::StartJournalCleaningTimer() {
+ journal_cleaning_timer_.Start(
+ FROM_HERE,
+ base::TimeDelta::FromSeconds(5),
+ this,
+ &IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn);
+}
+
+// This assumes a file path of dbId/second-to-LSB-of-counter/counter.
+FilePath IndexedDBBackingStore::GetBlobFileName(int64 database_id, int64 key) {
+ return GetBlobFileNameForKey(blob_path_, database_id, key);
+}
+
static bool CheckIndexAndMetaDataKey(const LevelDBIterator* it,
const std::string& stop_key,
int64 index_id,
@@ -1442,13 +2465,13 @@ static bool CheckIndexAndMetaDataKey(const LevelDBIterator* it,
// TODO(jsbell): This should do some error handling rather than plowing ahead
// when bad data is encountered.
-bool IndexedDBBackingStore::GetIndexes(
+leveldb::Status IndexedDBBackingStore::GetIndexes(
int64 database_id,
int64 object_store_id,
IndexedDBObjectStoreMetadata::IndexMap* indexes) {
IDB_TRACE("IndexedDBBackingStore::GetIndexes");
if (!KeyPrefix::ValidIds(database_id, object_store_id))
- return false;
+ return InvalidDBKeyStatus();
const std::string start_key =
IndexMetaDataKey::Encode(database_id, object_store_id, 0, 0);
const std::string stop_key =
@@ -1457,17 +2480,19 @@ bool IndexedDBBackingStore::GetIndexes(
DCHECK(indexes->empty());
scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
- it->Seek(start_key);
- while (it->IsValid() && CompareKeys(it->Key(), stop_key) < 0) {
+ leveldb::Status s = it->Seek(start_key);
+ while (s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0) {
StringPiece slice(it->Key());
IndexMetaDataKey meta_data_key;
bool ok = IndexMetaDataKey::Decode(&slice, &meta_data_key);
DCHECK(ok);
if (meta_data_key.meta_data_type() != IndexMetaDataKey::NAME) {
- INTERNAL_CONSISTENCY_ERROR(GET_INDEXES);
+ INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
// Possible stale metadata due to http://webkit.org/b/85557 but don't fail
// the load.
- it->Next();
+ s = it->Next();
+ if (!s.ok())
+ break;
continue;
}
@@ -1478,78 +2503,200 @@ bool IndexedDBBackingStore::GetIndexes(
{
StringPiece slice(it->Value());
if (!DecodeString(&slice, &index_name) || !slice.empty())
- INTERNAL_CONSISTENCY_ERROR(GET_INDEXES);
+ INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
}
- it->Next(); // unique flag
+ s = it->Next(); // unique flag
+ if (!s.ok())
+ break;
if (!CheckIndexAndMetaDataKey(
it.get(), stop_key, index_id, IndexMetaDataKey::UNIQUE)) {
- INTERNAL_CONSISTENCY_ERROR(GET_INDEXES);
+ INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
break;
}
bool index_unique;
{
StringPiece slice(it->Value());
if (!DecodeBool(&slice, &index_unique) || !slice.empty())
- INTERNAL_CONSISTENCY_ERROR(GET_INDEXES);
+ INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
}
- it->Next(); // key_path
+ s = it->Next(); // key_path
+ if (!s.ok())
+ break;
if (!CheckIndexAndMetaDataKey(
it.get(), stop_key, index_id, IndexMetaDataKey::KEY_PATH)) {
- INTERNAL_CONSISTENCY_ERROR(GET_INDEXES);
+ INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
break;
}
IndexedDBKeyPath key_path;
{
StringPiece slice(it->Value());
if (!DecodeIDBKeyPath(&slice, &key_path) || !slice.empty())
- INTERNAL_CONSISTENCY_ERROR(GET_INDEXES);
+ INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
}
- it->Next(); // [optional] multi_entry flag
+ s = it->Next(); // [optional] multi_entry flag
+ if (!s.ok())
+ break;
bool index_multi_entry = false;
if (CheckIndexAndMetaDataKey(
it.get(), stop_key, index_id, IndexMetaDataKey::MULTI_ENTRY)) {
StringPiece slice(it->Value());
if (!DecodeBool(&slice, &index_multi_entry) || !slice.empty())
- INTERNAL_CONSISTENCY_ERROR(GET_INDEXES);
+ INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
- it->Next();
+ s = it->Next();
+ if (!s.ok())
+ break;
}
(*indexes)[index_id] = IndexedDBIndexMetadata(
index_name, index_id, key_path, index_unique, index_multi_entry);
}
- return true;
+
+ if (!s.ok())
+ INTERNAL_READ_ERROR_UNTESTED(GET_INDEXES);
+
+ return s;
+}
+
+bool IndexedDBBackingStore::RemoveBlobFile(int64 database_id, int64 key) {
+ FilePath fileName = GetBlobFileName(database_id, key);
+ return base::DeleteFile(fileName, false);
+}
+
+bool IndexedDBBackingStore::RemoveBlobDirectory(int64 database_id) {
+ FilePath dirName = GetBlobDirectoryName(blob_path_, database_id);
+ return base::DeleteFile(dirName, true);
+}
+
+leveldb::Status IndexedDBBackingStore::CleanUpBlobJournal(
+ const std::string& level_db_key) {
+ scoped_refptr<LevelDBTransaction> journal_transaction =
+ IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get());
+ BlobJournalType journal;
+ leveldb::Status s =
+ GetBlobJournal(level_db_key, journal_transaction.get(), &journal);
+ if (!s.ok())
+ return s;
+ if (!journal.size())
+ return leveldb::Status::OK();
+ BlobJournalType::iterator journal_iter;
+ for (journal_iter = journal.begin(); journal_iter != journal.end();
+ ++journal_iter) {
+ int64 database_id = journal_iter->first;
+ int64 blob_key = journal_iter->second;
+ DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
+ if (blob_key == DatabaseMetaDataKey::kAllBlobsKey) {
+ if (!RemoveBlobDirectory(database_id))
+ return IOErrorStatus();
+ } else {
+ DCHECK(DatabaseMetaDataKey::IsValidBlobKey(blob_key));
+ if (!RemoveBlobFile(database_id, blob_key))
+ return IOErrorStatus();
+ }
+ }
+ ClearBlobJournal(journal_transaction.get(), level_db_key);
+ return journal_transaction->Commit();
}
-WARN_UNUSED_RESULT static bool SetMaxIndexId(LevelDBTransaction* transaction,
- int64 database_id,
- int64 object_store_id,
- int64 index_id) {
+leveldb::Status IndexedDBBackingStore::Transaction::GetBlobInfoForRecord(
+ int64 database_id,
+ const std::string& object_store_data_key,
+ IndexedDBValue* value) {
+ BlobChangeRecord* change_record = NULL;
+ BlobChangeMap::const_iterator blob_iter =
+ blob_change_map_.find(object_store_data_key);
+ if (blob_iter != blob_change_map_.end()) {
+ change_record = blob_iter->second;
+ } else {
+ blob_iter = incognito_blob_map_.find(object_store_data_key);
+ if (blob_iter != incognito_blob_map_.end())
+ change_record = blob_iter->second;
+ }
+ if (change_record) {
+ // Either we haven't written the blob to disk yet or we're in incognito
+ // mode, so we have to send back the one they sent us. This change record
+ // includes the original UUID.
+ value->blob_info = change_record->blob_info();
+ return leveldb::Status::OK();
+ }
+
+ BlobEntryKey blob_entry_key;
+ StringPiece leveldb_key_piece(object_store_data_key);
+ if (!BlobEntryKey::FromObjectStoreDataKey(&leveldb_key_piece,
+ &blob_entry_key)) {
+ NOTREACHED();
+ return InternalInconsistencyStatus();
+ }
+ scoped_ptr<LevelDBIterator> it = transaction()->CreateIterator();
+ std::string encoded_key = blob_entry_key.Encode();
+ leveldb::Status s = it->Seek(encoded_key);
+ if (!s.ok())
+ return s;
+ if (it->IsValid() && CompareKeys(it->Key(), encoded_key) == 0) {
+ if (!DecodeBlobData(it->Value().as_string(), &value->blob_info)) {
+ INTERNAL_READ_ERROR(GET_BLOB_INFO_FOR_RECORD);
+ return InternalInconsistencyStatus();
+ }
+ std::vector<IndexedDBBlobInfo>::iterator iter;
+ for (iter = value->blob_info.begin(); iter != value->blob_info.end();
+ ++iter) {
+ iter->set_file_path(
+ backing_store_->GetBlobFileName(database_id, iter->key()));
+ iter->set_mark_used_callback(
+ backing_store_->active_blob_registry()->GetAddBlobRefCallback(
+ database_id, iter->key()));
+ iter->set_release_callback(
+ backing_store_->active_blob_registry()->GetFinalReleaseCallback(
+ database_id, iter->key()));
+ if (iter->is_file()) {
+ base::File::Info info;
+ if (base::GetFileInfo(iter->file_path(), &info)) {
+ // This should always work, but it isn't fatal if it doesn't; it just
+ // means a potential slow synchronous call from the renderer later.
+ iter->set_last_modified(info.last_modified);
+ iter->set_size(info.size);
+ }
+ }
+ }
+ }
+ return leveldb::Status::OK();
+}
+
+void IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn() {
+ CleanUpBlobJournal(BlobJournalKey::Encode());
+}
+
+WARN_UNUSED_RESULT static leveldb::Status SetMaxIndexId(
+ LevelDBTransaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id) {
int64 max_index_id = -1;
const std::string max_index_id_key = ObjectStoreMetaDataKey::Encode(
database_id, object_store_id, ObjectStoreMetaDataKey::MAX_INDEX_ID);
bool found = false;
- bool ok = GetInt(transaction, max_index_id_key, &max_index_id, &found);
- if (!ok) {
- INTERNAL_READ_ERROR(SET_MAX_INDEX_ID);
- return false;
+ leveldb::Status s =
+ GetInt(transaction, max_index_id_key, &max_index_id, &found);
+ if (!s.ok()) {
+ INTERNAL_READ_ERROR_UNTESTED(SET_MAX_INDEX_ID);
+ return s;
}
if (!found)
max_index_id = kMinimumIndexId;
if (index_id <= max_index_id) {
- INTERNAL_CONSISTENCY_ERROR(SET_MAX_INDEX_ID);
- return false;
+ INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_MAX_INDEX_ID);
+ return InternalInconsistencyStatus();
}
PutInt(transaction, max_index_id_key, index_id);
- return true;
+ return s;
}
-bool IndexedDBBackingStore::CreateIndex(
+leveldb::Status IndexedDBBackingStore::CreateIndex(
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
int64 object_store_id,
@@ -1560,11 +2707,13 @@ bool IndexedDBBackingStore::CreateIndex(
bool is_multi_entry) {
IDB_TRACE("IndexedDBBackingStore::CreateIndex");
if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
- return false;
+ return InvalidDBKeyStatus();
LevelDBTransaction* leveldb_transaction = transaction->transaction();
- if (!SetMaxIndexId(
- leveldb_transaction, database_id, object_store_id, index_id))
- return false;
+ leveldb::Status s = SetMaxIndexId(
+ leveldb_transaction, database_id, object_store_id, index_id);
+
+ if (!s.ok())
+ return s;
const std::string name_key = IndexMetaDataKey::Encode(
database_id, object_store_id, index_id, IndexMetaDataKey::NAME);
@@ -1579,34 +2728,42 @@ bool IndexedDBBackingStore::CreateIndex(
PutBool(leveldb_transaction, unique_key, is_unique);
PutIDBKeyPath(leveldb_transaction, key_path_key, key_path);
PutBool(leveldb_transaction, multi_entry_key, is_multi_entry);
- return true;
+ return s;
}
-bool IndexedDBBackingStore::DeleteIndex(
+leveldb::Status IndexedDBBackingStore::DeleteIndex(
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
int64 object_store_id,
int64 index_id) {
IDB_TRACE("IndexedDBBackingStore::DeleteIndex");
if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
- return false;
+ return InvalidDBKeyStatus();
LevelDBTransaction* leveldb_transaction = transaction->transaction();
const std::string index_meta_data_start =
IndexMetaDataKey::Encode(database_id, object_store_id, index_id, 0);
const std::string index_meta_data_end =
IndexMetaDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
- DeleteRange(leveldb_transaction, index_meta_data_start, index_meta_data_end);
+ leveldb::Status s = DeleteRangeBasic(
+ leveldb_transaction, index_meta_data_start, index_meta_data_end, true);
- const std::string index_data_start =
- IndexDataKey::EncodeMinKey(database_id, object_store_id, index_id);
- const std::string index_data_end =
- IndexDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
- DeleteRange(leveldb_transaction, index_data_start, index_data_end);
- return true;
+ if (s.ok()) {
+ const std::string index_data_start =
+ IndexDataKey::EncodeMinKey(database_id, object_store_id, index_id);
+ const std::string index_data_end =
+ IndexDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
+ s = DeleteRangeBasic(
+ leveldb_transaction, index_data_start, index_data_end, true);
+ }
+
+ if (!s.ok())
+ INTERNAL_WRITE_ERROR_UNTESTED(DELETE_INDEX);
+
+ return s;
}
-bool IndexedDBBackingStore::PutIndexDataForRecord(
+leveldb::Status IndexedDBBackingStore::PutIndexDataForRecord(
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
int64 object_store_id,
@@ -1616,7 +2773,7 @@ bool IndexedDBBackingStore::PutIndexDataForRecord(
IDB_TRACE("IndexedDBBackingStore::PutIndexDataForRecord");
DCHECK(key.IsValid());
if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
- return false;
+ return InvalidDBKeyStatus();
std::string encoded_key;
EncodeIDBKey(key, &encoded_key);
@@ -1634,24 +2791,27 @@ bool IndexedDBBackingStore::PutIndexDataForRecord(
data.append(record_identifier.primary_key());
transaction->transaction()->Put(index_data_key, &data);
- return true;
+ return leveldb::Status::OK();
}
static bool FindGreatestKeyLessThanOrEqual(LevelDBTransaction* transaction,
const std::string& target,
- std::string* found_key) {
+ std::string* found_key,
+ leveldb::Status& s) {
scoped_ptr<LevelDBIterator> it = transaction->CreateIterator();
- it->Seek(target);
+ s = it->Seek(target);
+ if (!s.ok())
+ return false;
if (!it->IsValid()) {
- it->SeekToLast();
- if (!it->IsValid())
+ s = it->SeekToLast();
+ if (!s.ok() || !it->IsValid())
return false;
}
while (CompareIndexKeys(it->Key(), target) > 0) {
- it->Prev();
- if (!it->IsValid())
+ s = it->Prev();
+ if (!s.ok() || !it->IsValid())
return false;
}
@@ -1659,39 +2819,39 @@ static bool FindGreatestKeyLessThanOrEqual(LevelDBTransaction* transaction,
*found_key = it->Key().as_string();
// There can be several index keys that compare equal. We want the last one.
- it->Next();
- } while (it->IsValid() && !CompareIndexKeys(it->Key(), target));
+ s = it->Next();
+ } while (s.ok() && it->IsValid() && !CompareIndexKeys(it->Key(), target));
return true;
}
-static bool VersionExists(LevelDBTransaction* transaction,
- int64 database_id,
- int64 object_store_id,
- int64 version,
- const std::string& encoded_primary_key,
- bool* exists) {
+static leveldb::Status VersionExists(LevelDBTransaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64 version,
+ const std::string& encoded_primary_key,
+ bool* exists) {
const std::string key =
ExistsEntryKey::Encode(database_id, object_store_id, encoded_primary_key);
std::string data;
- bool ok = transaction->Get(key, &data, exists);
- if (!ok) {
- INTERNAL_READ_ERROR(VERSION_EXISTS);
- return false;
+ leveldb::Status s = transaction->Get(key, &data, exists);
+ if (!s.ok()) {
+ INTERNAL_READ_ERROR_UNTESTED(VERSION_EXISTS);
+ return s;
}
if (!*exists)
- return true;
+ return s;
StringPiece slice(data);
int64 decoded;
if (!DecodeInt(&slice, &decoded) || !slice.empty())
- return false;
+ return InternalInconsistencyStatus();
*exists = (decoded == version);
- return true;
+ return s;
}
-bool IndexedDBBackingStore::FindKeyInIndex(
+leveldb::Status IndexedDBBackingStore::FindKeyInIndex(
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
int64 object_store_id,
@@ -1709,44 +2869,48 @@ bool IndexedDBBackingStore::FindKeyInIndex(
const std::string leveldb_key =
IndexDataKey::Encode(database_id, object_store_id, index_id, key);
scoped_ptr<LevelDBIterator> it = leveldb_transaction->CreateIterator();
- it->Seek(leveldb_key);
+ leveldb::Status s = it->Seek(leveldb_key);
+ if (!s.ok()) {
+ INTERNAL_READ_ERROR_UNTESTED(FIND_KEY_IN_INDEX);
+ return s;
+ }
for (;;) {
if (!it->IsValid())
- return true;
+ return leveldb::Status::OK();
if (CompareIndexKeys(it->Key(), leveldb_key) > 0)
- return true;
+ return leveldb::Status::OK();
StringPiece slice(it->Value());
int64 version;
if (!DecodeVarInt(&slice, &version)) {
- INTERNAL_READ_ERROR(FIND_KEY_IN_INDEX);
- return false;
+ INTERNAL_READ_ERROR_UNTESTED(FIND_KEY_IN_INDEX);
+ return InternalInconsistencyStatus();
}
*found_encoded_primary_key = slice.as_string();
bool exists = false;
- bool ok = VersionExists(leveldb_transaction,
- database_id,
- object_store_id,
- version,
- *found_encoded_primary_key,
- &exists);
- if (!ok)
- return false;
+ s = VersionExists(leveldb_transaction,
+ database_id,
+ object_store_id,
+ version,
+ *found_encoded_primary_key,
+ &exists);
+ if (!s.ok())
+ return s;
if (!exists) {
// Delete stale index data entry and continue.
leveldb_transaction->Remove(it->Key());
- it->Next();
+ s = it->Next();
continue;
}
*found = true;
- return true;
+ return s;
}
}
-bool IndexedDBBackingStore::GetPrimaryKeyViaIndex(
+leveldb::Status IndexedDBBackingStore::GetPrimaryKeyViaIndex(
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
int64 object_store_id,
@@ -1755,33 +2919,36 @@ bool IndexedDBBackingStore::GetPrimaryKeyViaIndex(
scoped_ptr<IndexedDBKey>* primary_key) {
IDB_TRACE("IndexedDBBackingStore::GetPrimaryKeyViaIndex");
if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
- return false;
+ return InvalidDBKeyStatus();
bool found = false;
std::string found_encoded_primary_key;
- bool ok = FindKeyInIndex(transaction,
- database_id,
- object_store_id,
- index_id,
- key,
- &found_encoded_primary_key,
- &found);
- if (!ok) {
- INTERNAL_READ_ERROR(GET_PRIMARY_KEY_VIA_INDEX);
- return false;
+ leveldb::Status s = FindKeyInIndex(transaction,
+ database_id,
+ object_store_id,
+ index_id,
+ key,
+ &found_encoded_primary_key,
+ &found);
+ if (!s.ok()) {
+ INTERNAL_READ_ERROR_UNTESTED(GET_PRIMARY_KEY_VIA_INDEX);
+ return s;
}
if (!found)
- return true;
+ return s;
if (!found_encoded_primary_key.size()) {
- INTERNAL_READ_ERROR(GET_PRIMARY_KEY_VIA_INDEX);
- return false;
+ INTERNAL_READ_ERROR_UNTESTED(GET_PRIMARY_KEY_VIA_INDEX);
+ return InvalidDBKeyStatus();
}
StringPiece slice(found_encoded_primary_key);
- return DecodeIDBKey(&slice, primary_key) && slice.empty();
+ if (DecodeIDBKey(&slice, primary_key) && slice.empty())
+ return s;
+ else
+ return InvalidDBKeyStatus();
}
-bool IndexedDBBackingStore::KeyExistsInIndex(
+leveldb::Status IndexedDBBackingStore::KeyExistsInIndex(
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
int64 object_store_id,
@@ -1791,65 +2958,81 @@ bool IndexedDBBackingStore::KeyExistsInIndex(
bool* exists) {
IDB_TRACE("IndexedDBBackingStore::KeyExistsInIndex");
if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
- return false;
+ return InvalidDBKeyStatus();
*exists = false;
std::string found_encoded_primary_key;
- bool ok = FindKeyInIndex(transaction,
- database_id,
- object_store_id,
- index_id,
- index_key,
- &found_encoded_primary_key,
- exists);
- if (!ok) {
- INTERNAL_READ_ERROR(KEY_EXISTS_IN_INDEX);
- return false;
+ leveldb::Status s = FindKeyInIndex(transaction,
+ database_id,
+ object_store_id,
+ index_id,
+ index_key,
+ &found_encoded_primary_key,
+ exists);
+ if (!s.ok()) {
+ INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_INDEX);
+ return s;
}
if (!*exists)
- return true;
+ return leveldb::Status::OK();
if (found_encoded_primary_key.empty()) {
- INTERNAL_READ_ERROR(KEY_EXISTS_IN_INDEX);
- return false;
+ INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_INDEX);
+ return InvalidDBKeyStatus();
}
StringPiece slice(found_encoded_primary_key);
- return DecodeIDBKey(&slice, found_primary_key) && slice.empty();
+ if (DecodeIDBKey(&slice, found_primary_key) && slice.empty())
+ return s;
+ else
+ return InvalidDBKeyStatus();
}
IndexedDBBackingStore::Cursor::Cursor(
const IndexedDBBackingStore::Cursor* other)
- : transaction_(other->transaction_),
+ : backing_store_(other->backing_store_),
+ transaction_(other->transaction_),
+ database_id_(other->database_id_),
cursor_options_(other->cursor_options_),
current_key_(new IndexedDBKey(*other->current_key_)) {
if (other->iterator_) {
- iterator_ = transaction_->CreateIterator();
+ iterator_ = transaction_->transaction()->CreateIterator();
if (other->iterator_->IsValid()) {
- iterator_->Seek(other->iterator_->Key());
+ leveldb::Status s = iterator_->Seek(other->iterator_->Key());
+ // TODO(cmumford): Handle this error (crbug.com/363397)
DCHECK(iterator_->IsValid());
}
}
}
-IndexedDBBackingStore::Cursor::Cursor(LevelDBTransaction* transaction,
- const CursorOptions& cursor_options)
- : transaction_(transaction), cursor_options_(cursor_options) {}
+IndexedDBBackingStore::Cursor::Cursor(
+ scoped_refptr<IndexedDBBackingStore> backing_store,
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ const CursorOptions& cursor_options)
+ : backing_store_(backing_store),
+ transaction_(transaction),
+ database_id_(database_id),
+ cursor_options_(cursor_options) {
+}
IndexedDBBackingStore::Cursor::~Cursor() {}
-bool IndexedDBBackingStore::Cursor::FirstSeek() {
- iterator_ = transaction_->CreateIterator();
+bool IndexedDBBackingStore::Cursor::FirstSeek(leveldb::Status* s) {
+ iterator_ = transaction_->transaction()->CreateIterator();
if (cursor_options_.forward)
- iterator_->Seek(cursor_options_.low_key);
+ *s = iterator_->Seek(cursor_options_.low_key);
else
- iterator_->Seek(cursor_options_.high_key);
+ *s = iterator_->Seek(cursor_options_.high_key);
+ if (!s->ok())
+ return false;
- return Continue(0, READY);
+ return Continue(0, READY, s);
}
-bool IndexedDBBackingStore::Cursor::Advance(uint32 count) {
+bool IndexedDBBackingStore::Cursor::Advance(uint32 count, leveldb::Status* s) {
+ *s = leveldb::Status::OK();
while (count--) {
- if (!Continue())
+ if (!Continue(s))
return false;
}
return true;
@@ -1857,9 +3040,11 @@ bool IndexedDBBackingStore::Cursor::Advance(uint32 count) {
bool IndexedDBBackingStore::Cursor::Continue(const IndexedDBKey* key,
const IndexedDBKey* primary_key,
- IteratorState next_state) {
+ IteratorState next_state,
+ leveldb::Status* s) {
DCHECK(!key || key->IsValid());
DCHECK(!primary_key || primary_key->IsValid());
+ *s = leveldb::Status::OK();
// TODO(alecflett): avoid a copy here?
IndexedDBKey previous_key = current_key_ ? *current_key_ : IndexedDBKey();
@@ -1883,13 +3068,15 @@ bool IndexedDBBackingStore::Cursor::Continue(const IndexedDBKey* key,
} else {
leveldb_key = EncodeKey(*key);
}
- iterator_->Seek(leveldb_key);
+ *s = iterator_->Seek(leveldb_key);
first_iteration = false;
} else if (forward) {
- iterator_->Next();
+ *s = iterator_->Next();
} else {
- iterator_->Prev();
+ *s = iterator_->Prev();
}
+ if (!s->ok())
+ return false;
} else {
next_state = SEEK; // for subsequent iterations
}
@@ -1926,13 +3113,13 @@ bool IndexedDBBackingStore::Cursor::Continue(const IndexedDBKey* key,
if (key) {
if (forward) {
- if (primary_key && current_key_->IsEqual(*key) &&
+ if (primary_key && current_key_->Equals(*key) &&
this->primary_key().IsLessThan(*primary_key))
continue;
if (current_key_->IsLessThan(*key))
continue;
} else {
- if (primary_key && key->IsEqual(*current_key_) &&
+ if (primary_key && key->Equals(*current_key_) &&
primary_key->IsLessThan(this->primary_key()))
continue;
if (key->IsLessThan(*current_key_))
@@ -1941,7 +3128,7 @@ bool IndexedDBBackingStore::Cursor::Continue(const IndexedDBKey* key,
}
if (cursor_options_.unique) {
- if (previous_key.IsValid() && current_key_->IsEqual(previous_key)) {
+ if (previous_key.IsValid() && current_key_->Equals(previous_key)) {
// We should never be able to walk forward all the way
// to the previous key.
DCHECK(!last_duplicate_key.IsValid());
@@ -1956,7 +3143,7 @@ bool IndexedDBBackingStore::Cursor::Continue(const IndexedDBKey* key,
// We need to walk forward because we hit the boundary
// between key ranges.
- if (!last_duplicate_key.IsEqual(*current_key_)) {
+ if (!last_duplicate_key.Equals(*current_key_)) {
forward = true;
continue;
}
@@ -1968,7 +3155,7 @@ bool IndexedDBBackingStore::Cursor::Continue(const IndexedDBKey* key,
}
DCHECK(!last_duplicate_key.IsValid() ||
- (forward && last_duplicate_key.IsEqual(*current_key_)));
+ (forward && last_duplicate_key.Equals(*current_key_)));
return true;
}
@@ -2014,16 +3201,21 @@ IndexedDBBackingStore::Cursor::record_identifier() const {
class ObjectStoreKeyCursorImpl : public IndexedDBBackingStore::Cursor {
public:
ObjectStoreKeyCursorImpl(
- LevelDBTransaction* transaction,
+ scoped_refptr<IndexedDBBackingStore> backing_store,
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
- : IndexedDBBackingStore::Cursor(transaction, cursor_options) {}
+ : IndexedDBBackingStore::Cursor(backing_store,
+ transaction,
+ database_id,
+ cursor_options) {}
virtual Cursor* Clone() OVERRIDE {
return new ObjectStoreKeyCursorImpl(this);
}
// IndexedDBBackingStore::Cursor
- virtual std::string* value() OVERRIDE {
+ virtual IndexedDBValue* value() OVERRIDE {
NOTREACHED();
return NULL;
}
@@ -2043,13 +3235,15 @@ class ObjectStoreKeyCursorImpl : public IndexedDBBackingStore::Cursor {
private:
explicit ObjectStoreKeyCursorImpl(const ObjectStoreKeyCursorImpl* other)
: IndexedDBBackingStore::Cursor(other) {}
+
+ DISALLOW_COPY_AND_ASSIGN(ObjectStoreKeyCursorImpl);
};
bool ObjectStoreKeyCursorImpl::LoadCurrentRow() {
StringPiece slice(iterator_->Key());
ObjectStoreDataKey object_store_data_key;
if (!ObjectStoreDataKey::Decode(&slice, &object_store_data_key)) {
- INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
+ INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
return false;
}
@@ -2058,7 +3252,7 @@ bool ObjectStoreKeyCursorImpl::LoadCurrentRow() {
int64 version;
slice = StringPiece(iterator_->Value());
if (!DecodeVarInt(&slice, &version)) {
- INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
+ INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
return false;
}
@@ -2073,14 +3267,19 @@ bool ObjectStoreKeyCursorImpl::LoadCurrentRow() {
class ObjectStoreCursorImpl : public IndexedDBBackingStore::Cursor {
public:
ObjectStoreCursorImpl(
- LevelDBTransaction* transaction,
+ scoped_refptr<IndexedDBBackingStore> backing_store,
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
- : IndexedDBBackingStore::Cursor(transaction, cursor_options) {}
+ : IndexedDBBackingStore::Cursor(backing_store,
+ transaction,
+ database_id,
+ cursor_options) {}
virtual Cursor* Clone() OVERRIDE { return new ObjectStoreCursorImpl(this); }
// IndexedDBBackingStore::Cursor
- virtual std::string* value() OVERRIDE { return &current_value_; }
+ virtual IndexedDBValue* value() OVERRIDE { return &current_value_; }
virtual bool LoadCurrentRow() OVERRIDE;
protected:
@@ -2099,23 +3298,25 @@ class ObjectStoreCursorImpl : public IndexedDBBackingStore::Cursor {
: IndexedDBBackingStore::Cursor(other),
current_value_(other->current_value_) {}
- std::string current_value_;
+ IndexedDBValue current_value_;
+
+ DISALLOW_COPY_AND_ASSIGN(ObjectStoreCursorImpl);
};
bool ObjectStoreCursorImpl::LoadCurrentRow() {
- StringPiece slice(iterator_->Key());
+ StringPiece key_slice(iterator_->Key());
ObjectStoreDataKey object_store_data_key;
- if (!ObjectStoreDataKey::Decode(&slice, &object_store_data_key)) {
- INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
+ if (!ObjectStoreDataKey::Decode(&key_slice, &object_store_data_key)) {
+ INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
return false;
}
current_key_ = object_store_data_key.user_key();
int64 version;
- slice = StringPiece(iterator_->Value());
- if (!DecodeVarInt(&slice, &version)) {
- INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
+ StringPiece value_slice = StringPiece(iterator_->Value());
+ if (!DecodeVarInt(&value_slice, &version)) {
+ INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
return false;
}
@@ -2124,21 +3325,31 @@ bool ObjectStoreCursorImpl::LoadCurrentRow() {
EncodeIDBKey(*current_key_, &encoded_key);
record_identifier_.Reset(encoded_key, version);
- current_value_ = slice.as_string();
+ if (!transaction_->GetBlobInfoForRecord(database_id_,
+ iterator_->Key().as_string(),
+ &current_value_).ok()) {
+ return false;
+ }
+ current_value_.bits = value_slice.as_string();
return true;
}
class IndexKeyCursorImpl : public IndexedDBBackingStore::Cursor {
public:
IndexKeyCursorImpl(
- LevelDBTransaction* transaction,
+ scoped_refptr<IndexedDBBackingStore> backing_store,
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
- : IndexedDBBackingStore::Cursor(transaction, cursor_options) {}
+ : IndexedDBBackingStore::Cursor(backing_store,
+ transaction,
+ database_id,
+ cursor_options) {}
virtual Cursor* Clone() OVERRIDE { return new IndexKeyCursorImpl(this); }
// IndexedDBBackingStore::Cursor
- virtual std::string* value() OVERRIDE {
+ virtual IndexedDBValue* value() OVERRIDE {
NOTREACHED();
return NULL;
}
@@ -2174,13 +3385,15 @@ class IndexKeyCursorImpl : public IndexedDBBackingStore::Cursor {
primary_key_(new IndexedDBKey(*other->primary_key_)) {}
scoped_ptr<IndexedDBKey> primary_key_;
+
+ DISALLOW_COPY_AND_ASSIGN(IndexKeyCursorImpl);
};
bool IndexKeyCursorImpl::LoadCurrentRow() {
StringPiece slice(iterator_->Key());
IndexDataKey index_data_key;
if (!IndexDataKey::Decode(&slice, &index_data_key)) {
- INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
+ INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
return false;
}
@@ -2190,12 +3403,12 @@ bool IndexKeyCursorImpl::LoadCurrentRow() {
slice = StringPiece(iterator_->Value());
int64 index_data_version;
if (!DecodeVarInt(&slice, &index_data_version)) {
- INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
+ INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
return false;
}
if (!DecodeIDBKey(&slice, &primary_key_) || !slice.empty()) {
- INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
+ INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
return false;
}
@@ -2206,29 +3419,30 @@ bool IndexKeyCursorImpl::LoadCurrentRow() {
std::string result;
bool found = false;
- bool ok = transaction_->Get(primary_leveldb_key, &result, &found);
- if (!ok) {
- INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
+ leveldb::Status s =
+ transaction_->transaction()->Get(primary_leveldb_key, &result, &found);
+ if (!s.ok()) {
+ INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
return false;
}
if (!found) {
- transaction_->Remove(iterator_->Key());
+ transaction_->transaction()->Remove(iterator_->Key());
return false;
}
if (!result.size()) {
- INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
+ INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
return false;
}
int64 object_store_data_version;
slice = StringPiece(result);
if (!DecodeVarInt(&slice, &object_store_data_version)) {
- INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
+ INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
return false;
}
if (object_store_data_version != index_data_version) {
- transaction_->Remove(iterator_->Key());
+ transaction_->transaction()->Remove(iterator_->Key());
return false;
}
@@ -2238,14 +3452,19 @@ bool IndexKeyCursorImpl::LoadCurrentRow() {
class IndexCursorImpl : public IndexedDBBackingStore::Cursor {
public:
IndexCursorImpl(
- LevelDBTransaction* transaction,
+ scoped_refptr<IndexedDBBackingStore> backing_store,
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
- : IndexedDBBackingStore::Cursor(transaction, cursor_options) {}
+ : IndexedDBBackingStore::Cursor(backing_store,
+ transaction,
+ database_id,
+ cursor_options) {}
virtual Cursor* Clone() OVERRIDE { return new IndexCursorImpl(this); }
// IndexedDBBackingStore::Cursor
- virtual std::string* value() OVERRIDE { return &current_value_; }
+ virtual IndexedDBValue* value() OVERRIDE { return &current_value_; }
virtual const IndexedDBKey& primary_key() const OVERRIDE {
return *primary_key_;
}
@@ -2280,15 +3499,17 @@ class IndexCursorImpl : public IndexedDBBackingStore::Cursor {
primary_leveldb_key_(other->primary_leveldb_key_) {}
scoped_ptr<IndexedDBKey> primary_key_;
- std::string current_value_;
+ IndexedDBValue current_value_;
std::string primary_leveldb_key_;
+
+ DISALLOW_COPY_AND_ASSIGN(IndexCursorImpl);
};
bool IndexCursorImpl::LoadCurrentRow() {
StringPiece slice(iterator_->Key());
IndexDataKey index_data_key;
if (!IndexDataKey::Decode(&slice, &index_data_key)) {
- INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
+ INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
return false;
}
@@ -2298,14 +3519,15 @@ bool IndexCursorImpl::LoadCurrentRow() {
slice = StringPiece(iterator_->Value());
int64 index_data_version;
if (!DecodeVarInt(&slice, &index_data_version)) {
- INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
+ INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
return false;
}
if (!DecodeIDBKey(&slice, &primary_key_)) {
- INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
+ INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
return false;
}
+ DCHECK_EQ(index_data_key.DatabaseId(), database_id_);
primary_leveldb_key_ =
ObjectStoreDataKey::Encode(index_data_key.DatabaseId(),
index_data_key.ObjectStoreId(),
@@ -2313,34 +3535,37 @@ bool IndexCursorImpl::LoadCurrentRow() {
std::string result;
bool found = false;
- bool ok = transaction_->Get(primary_leveldb_key_, &result, &found);
- if (!ok) {
- INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
+ leveldb::Status s =
+ transaction_->transaction()->Get(primary_leveldb_key_, &result, &found);
+ if (!s.ok()) {
+ INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
return false;
}
if (!found) {
- transaction_->Remove(iterator_->Key());
+ transaction_->transaction()->Remove(iterator_->Key());
return false;
}
if (!result.size()) {
- INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
+ INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
return false;
}
int64 object_store_data_version;
slice = StringPiece(result);
if (!DecodeVarInt(&slice, &object_store_data_version)) {
- INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
+ INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
return false;
}
if (object_store_data_version != index_data_version) {
- transaction_->Remove(iterator_->Key());
+ transaction_->transaction()->Remove(iterator_->Key());
return false;
}
- current_value_ = slice.as_string();
- return true;
+ current_value_.bits = slice.as_string();
+ return transaction_->GetBlobInfoForRecord(database_id_,
+ primary_leveldb_key_,
+ &current_value_).ok();
}
bool ObjectStoreCursorOptions(
@@ -2371,6 +3596,8 @@ bool ObjectStoreCursorOptions(
cursor_options->low_open = range.lowerOpen();
}
+ leveldb::Status s;
+
if (!upper_bound) {
cursor_options->high_key =
ObjectStoreDataKey::Encode(database_id, object_store_id, MaxIDBKey());
@@ -2379,9 +3606,11 @@ bool ObjectStoreCursorOptions(
cursor_options->high_open = true; // Not included.
} else {
// We need a key that exists.
+ // TODO(cmumford): Handle this error (crbug.com/363397)
if (!FindGreatestKeyLessThanOrEqual(transaction,
cursor_options->high_key,
- &cursor_options->high_key))
+ &cursor_options->high_key,
+ s))
return false;
cursor_options->high_open = false;
}
@@ -2393,8 +3622,9 @@ bool ObjectStoreCursorOptions(
if (!cursor_options->forward) {
// For reverse cursors, we need a key that exists.
std::string found_high_key;
+ // TODO(cmumford): Handle this error (crbug.com/363397)
if (!FindGreatestKeyLessThanOrEqual(
- transaction, cursor_options->high_key, &found_high_key))
+ transaction, cursor_options->high_key, &found_high_key, s))
return false;
// If the target key should not be included, but we end up with a smaller
@@ -2444,6 +3674,8 @@ bool IndexCursorOptions(
cursor_options->low_open = range.lowerOpen();
}
+ leveldb::Status s;
+
if (!upper_bound) {
cursor_options->high_key =
IndexDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
@@ -2452,7 +3684,8 @@ bool IndexCursorOptions(
if (!cursor_options->forward) { // We need a key that exists.
if (!FindGreatestKeyLessThanOrEqual(transaction,
cursor_options->high_key,
- &cursor_options->high_key))
+ &cursor_options->high_key,
+ s))
return false;
cursor_options->high_open = false;
}
@@ -2463,8 +3696,9 @@ bool IndexCursorOptions(
std::string found_high_key;
// Seek to the *last* key in the set of non-unique keys
+ // TODO(cmumford): Handle this error (crbug.com/363397)
if (!FindGreatestKeyLessThanOrEqual(
- transaction, cursor_options->high_key, &found_high_key))
+ transaction, cursor_options->high_key, &found_high_key, s))
return false;
// If the target key should not be included, but we end up with a smaller
@@ -2485,8 +3719,10 @@ IndexedDBBackingStore::OpenObjectStoreCursor(
int64 database_id,
int64 object_store_id,
const IndexedDBKeyRange& range,
- indexed_db::CursorDirection direction) {
+ indexed_db::CursorDirection direction,
+ leveldb::Status* s) {
IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreCursor");
+ *s = leveldb::Status::OK();
LevelDBTransaction* leveldb_transaction = transaction->transaction();
IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
if (!ObjectStoreCursorOptions(leveldb_transaction,
@@ -2496,9 +3732,9 @@ IndexedDBBackingStore::OpenObjectStoreCursor(
direction,
&cursor_options))
return scoped_ptr<IndexedDBBackingStore::Cursor>();
- scoped_ptr<ObjectStoreCursorImpl> cursor(
- new ObjectStoreCursorImpl(leveldb_transaction, cursor_options));
- if (!cursor->FirstSeek())
+ scoped_ptr<ObjectStoreCursorImpl> cursor(new ObjectStoreCursorImpl(
+ this, transaction, database_id, cursor_options));
+ if (!cursor->FirstSeek(s))
return scoped_ptr<IndexedDBBackingStore::Cursor>();
return cursor.PassAs<IndexedDBBackingStore::Cursor>();
@@ -2510,8 +3746,10 @@ IndexedDBBackingStore::OpenObjectStoreKeyCursor(
int64 database_id,
int64 object_store_id,
const IndexedDBKeyRange& range,
- indexed_db::CursorDirection direction) {
+ indexed_db::CursorDirection direction,
+ leveldb::Status* s) {
IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreKeyCursor");
+ *s = leveldb::Status::OK();
LevelDBTransaction* leveldb_transaction = transaction->transaction();
IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
if (!ObjectStoreCursorOptions(leveldb_transaction,
@@ -2521,9 +3759,9 @@ IndexedDBBackingStore::OpenObjectStoreKeyCursor(
direction,
&cursor_options))
return scoped_ptr<IndexedDBBackingStore::Cursor>();
- scoped_ptr<ObjectStoreKeyCursorImpl> cursor(
- new ObjectStoreKeyCursorImpl(leveldb_transaction, cursor_options));
- if (!cursor->FirstSeek())
+ scoped_ptr<ObjectStoreKeyCursorImpl> cursor(new ObjectStoreKeyCursorImpl(
+ this, transaction, database_id, cursor_options));
+ if (!cursor->FirstSeek(s))
return scoped_ptr<IndexedDBBackingStore::Cursor>();
return cursor.PassAs<IndexedDBBackingStore::Cursor>();
@@ -2536,8 +3774,10 @@ IndexedDBBackingStore::OpenIndexKeyCursor(
int64 object_store_id,
int64 index_id,
const IndexedDBKeyRange& range,
- indexed_db::CursorDirection direction) {
+ indexed_db::CursorDirection direction,
+ leveldb::Status* s) {
IDB_TRACE("IndexedDBBackingStore::OpenIndexKeyCursor");
+ *s = leveldb::Status::OK();
LevelDBTransaction* leveldb_transaction = transaction->transaction();
IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
if (!IndexCursorOptions(leveldb_transaction,
@@ -2549,8 +3789,8 @@ IndexedDBBackingStore::OpenIndexKeyCursor(
&cursor_options))
return scoped_ptr<IndexedDBBackingStore::Cursor>();
scoped_ptr<IndexKeyCursorImpl> cursor(
- new IndexKeyCursorImpl(leveldb_transaction, cursor_options));
- if (!cursor->FirstSeek())
+ new IndexKeyCursorImpl(this, transaction, database_id, cursor_options));
+ if (!cursor->FirstSeek(s))
return scoped_ptr<IndexedDBBackingStore::Cursor>();
return cursor.PassAs<IndexedDBBackingStore::Cursor>();
@@ -2563,7 +3803,8 @@ IndexedDBBackingStore::OpenIndexCursor(
int64 object_store_id,
int64 index_id,
const IndexedDBKeyRange& range,
- indexed_db::CursorDirection direction) {
+ indexed_db::CursorDirection direction,
+ leveldb::Status* s) {
IDB_TRACE("IndexedDBBackingStore::OpenIndexCursor");
LevelDBTransaction* leveldb_transaction = transaction->transaction();
IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
@@ -2576,8 +3817,8 @@ IndexedDBBackingStore::OpenIndexCursor(
&cursor_options))
return scoped_ptr<IndexedDBBackingStore::Cursor>();
scoped_ptr<IndexCursorImpl> cursor(
- new IndexCursorImpl(leveldb_transaction, cursor_options));
- if (!cursor->FirstSeek())
+ new IndexCursorImpl(this, transaction, database_id, cursor_options));
+ if (!cursor->FirstSeek(s))
return scoped_ptr<IndexedDBBackingStore::Cursor>();
return cursor.PassAs<IndexedDBBackingStore::Cursor>();
@@ -2585,31 +3826,424 @@ IndexedDBBackingStore::OpenIndexCursor(
IndexedDBBackingStore::Transaction::Transaction(
IndexedDBBackingStore* backing_store)
- : backing_store_(backing_store) {}
+ : backing_store_(backing_store), database_id_(-1) {
+}
-IndexedDBBackingStore::Transaction::~Transaction() {}
+IndexedDBBackingStore::Transaction::~Transaction() {
+ STLDeleteContainerPairSecondPointers(
+ blob_change_map_.begin(), blob_change_map_.end());
+ STLDeleteContainerPairSecondPointers(incognito_blob_map_.begin(),
+ incognito_blob_map_.end());
+}
void IndexedDBBackingStore::Transaction::Begin() {
IDB_TRACE("IndexedDBBackingStore::Transaction::Begin");
DCHECK(!transaction_.get());
- transaction_ = new LevelDBTransaction(backing_store_->db_.get());
+ transaction_ = IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
+ backing_store_->db_.get());
+
+ // If incognito, this snapshots blobs just as the above transaction_
+ // constructor snapshots the leveldb.
+ BlobChangeMap::const_iterator iter;
+ for (iter = backing_store_->incognito_blob_map_.begin();
+ iter != backing_store_->incognito_blob_map_.end();
+ ++iter)
+ incognito_blob_map_[iter->first] = iter->second->Clone().release();
}
-bool IndexedDBBackingStore::Transaction::Commit() {
- IDB_TRACE("IndexedDBBackingStore::Transaction::Commit");
- DCHECK(transaction_.get());
- bool result = transaction_->Commit();
+static GURL getURLFromUUID(const string& uuid) {
+ return GURL("blob:uuid/" + uuid);
+}
+
+leveldb::Status IndexedDBBackingStore::Transaction::HandleBlobPreTransaction(
+ BlobEntryKeyValuePairVec* new_blob_entries,
+ WriteDescriptorVec* new_files_to_write) {
+ if (backing_store_->is_incognito())
+ return leveldb::Status::OK();
+
+ BlobChangeMap::iterator iter = blob_change_map_.begin();
+ new_blob_entries->clear();
+ new_files_to_write->clear();
+ if (iter != blob_change_map_.end()) {
+ // Create LevelDBTransaction for the name generator seed and add-journal.
+ scoped_refptr<LevelDBTransaction> pre_transaction =
+ IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
+ backing_store_->db_.get());
+ BlobJournalType journal;
+ for (; iter != blob_change_map_.end(); ++iter) {
+ std::vector<IndexedDBBlobInfo>::iterator info_iter;
+ std::vector<IndexedDBBlobInfo*> new_blob_keys;
+ for (info_iter = iter->second->mutable_blob_info().begin();
+ info_iter != iter->second->mutable_blob_info().end();
+ ++info_iter) {
+ int64 next_blob_key = -1;
+ bool result = GetBlobKeyGeneratorCurrentNumber(
+ pre_transaction.get(), database_id_, &next_blob_key);
+ if (!result || next_blob_key < 0)
+ return InternalInconsistencyStatus();
+ BlobJournalEntryType journal_entry =
+ std::make_pair(database_id_, next_blob_key);
+ journal.push_back(journal_entry);
+ if (info_iter->is_file()) {
+ new_files_to_write->push_back(
+ WriteDescriptor(info_iter->file_path(),
+ next_blob_key,
+ info_iter->size(),
+ info_iter->last_modified()));
+ } else {
+ new_files_to_write->push_back(
+ WriteDescriptor(getURLFromUUID(info_iter->uuid()),
+ next_blob_key,
+ info_iter->size()));
+ }
+ info_iter->set_key(next_blob_key);
+ new_blob_keys.push_back(&*info_iter);
+ result = UpdateBlobKeyGeneratorCurrentNumber(
+ pre_transaction.get(), database_id_, next_blob_key + 1);
+ if (!result)
+ return InternalInconsistencyStatus();
+ }
+ BlobEntryKey blob_entry_key;
+ StringPiece key_piece(iter->second->key());
+ if (!BlobEntryKey::FromObjectStoreDataKey(&key_piece, &blob_entry_key)) {
+ NOTREACHED();
+ return InternalInconsistencyStatus();
+ }
+ new_blob_entries->push_back(
+ std::make_pair(blob_entry_key, EncodeBlobData(new_blob_keys)));
+ }
+ UpdatePrimaryJournalWithBlobList(pre_transaction.get(), journal);
+ leveldb::Status s = pre_transaction->Commit();
+ if (!s.ok())
+ return InternalInconsistencyStatus();
+ }
+ return leveldb::Status::OK();
+}
+
+bool IndexedDBBackingStore::Transaction::CollectBlobFilesToRemove() {
+ if (backing_store_->is_incognito())
+ return true;
+
+ BlobChangeMap::const_iterator iter = blob_change_map_.begin();
+ // Look up all old files to remove as part of the transaction, store their
+ // names in blobs_to_remove_, and remove their old blob data entries.
+ if (iter != blob_change_map_.end()) {
+ scoped_ptr<LevelDBIterator> db_iter = transaction_->CreateIterator();
+ for (; iter != blob_change_map_.end(); ++iter) {
+ BlobEntryKey blob_entry_key;
+ StringPiece key_piece(iter->second->key());
+ if (!BlobEntryKey::FromObjectStoreDataKey(&key_piece, &blob_entry_key)) {
+ NOTREACHED();
+ INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
+ transaction_ = NULL;
+ return false;
+ }
+ if (database_id_ < 0)
+ database_id_ = blob_entry_key.database_id();
+ else
+ DCHECK_EQ(database_id_, blob_entry_key.database_id());
+ std::string blob_entry_key_bytes = blob_entry_key.Encode();
+ db_iter->Seek(blob_entry_key_bytes);
+ if (db_iter->IsValid() &&
+ !CompareKeys(db_iter->Key(), blob_entry_key_bytes)) {
+ std::vector<IndexedDBBlobInfo> blob_info;
+ if (!DecodeBlobData(db_iter->Value().as_string(), &blob_info)) {
+ INTERNAL_READ_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
+ transaction_ = NULL;
+ return false;
+ }
+ std::vector<IndexedDBBlobInfo>::iterator blob_info_iter;
+ for (blob_info_iter = blob_info.begin();
+ blob_info_iter != blob_info.end();
+ ++blob_info_iter)
+ blobs_to_remove_.push_back(
+ std::make_pair(database_id_, blob_info_iter->key()));
+ transaction_->Remove(blob_entry_key_bytes);
+ }
+ }
+ }
+ return true;
+}
+
+leveldb::Status IndexedDBBackingStore::Transaction::SortBlobsToRemove() {
+ IndexedDBActiveBlobRegistry* registry =
+ backing_store_->active_blob_registry();
+ BlobJournalType::iterator iter;
+ BlobJournalType primary_journal, live_blob_journal;
+ for (iter = blobs_to_remove_.begin(); iter != blobs_to_remove_.end();
+ ++iter) {
+ if (registry->MarkDeletedCheckIfUsed(iter->first, iter->second))
+ live_blob_journal.push_back(*iter);
+ else
+ primary_journal.push_back(*iter);
+ }
+ UpdatePrimaryJournalWithBlobList(transaction_.get(), primary_journal);
+ leveldb::Status s =
+ MergeBlobsIntoLiveBlobJournal(transaction_.get(), live_blob_journal);
+ if (!s.ok())
+ return s;
+ // To signal how many blobs need attention right now.
+ blobs_to_remove_.swap(primary_journal);
+ return leveldb::Status::OK();
+}
+
+leveldb::Status IndexedDBBackingStore::Transaction::CommitPhaseOne(
+ scoped_refptr<BlobWriteCallback> callback) {
+ IDB_TRACE("IndexedDBBackingStore::Transaction::CommitPhaseOne");
+ DCHECK(transaction_);
+ DCHECK(backing_store_->task_runner()->RunsTasksOnCurrentThread());
+
+ leveldb::Status s;
+
+ s = backing_store_->CleanUpBlobJournal(BlobJournalKey::Encode());
+ if (!s.ok()) {
+ INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
+ transaction_ = NULL;
+ return s;
+ }
+
+ BlobEntryKeyValuePairVec new_blob_entries;
+ WriteDescriptorVec new_files_to_write;
+ s = HandleBlobPreTransaction(&new_blob_entries, &new_files_to_write);
+ if (!s.ok()) {
+ INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
+ transaction_ = NULL;
+ return s;
+ }
+
+ DCHECK(!new_files_to_write.size() ||
+ KeyPrefix::IsValidDatabaseId(database_id_));
+ if (!CollectBlobFilesToRemove()) {
+ INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
+ transaction_ = NULL;
+ return InternalInconsistencyStatus();
+ }
+
+ if (new_files_to_write.size()) {
+ // This kicks off the writes of the new blobs, if any.
+ // This call will zero out new_blob_entries and new_files_to_write.
+ WriteNewBlobs(new_blob_entries, new_files_to_write, callback);
+ // Remove the add journal, if any; once the blobs are written, and we
+ // commit, this will do the cleanup.
+ ClearBlobJournal(transaction_.get(), BlobJournalKey::Encode());
+ } else {
+ callback->Run(true);
+ }
+
+ return leveldb::Status::OK();
+}
+
+leveldb::Status IndexedDBBackingStore::Transaction::CommitPhaseTwo() {
+ IDB_TRACE("IndexedDBBackingStore::Transaction::CommitPhaseTwo");
+ leveldb::Status s;
+ if (blobs_to_remove_.size()) {
+ s = SortBlobsToRemove();
+ if (!s.ok()) {
+ INTERNAL_READ_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
+ transaction_ = NULL;
+ return s;
+ }
+ }
+
+ s = transaction_->Commit();
transaction_ = NULL;
- if (!result)
- INTERNAL_WRITE_ERROR(TRANSACTION_COMMIT_METHOD);
- return result;
+
+ if (s.ok() && backing_store_->is_incognito() && !blob_change_map_.empty()) {
+ BlobChangeMap& target_map = backing_store_->incognito_blob_map_;
+ BlobChangeMap::iterator iter;
+ for (iter = blob_change_map_.begin(); iter != blob_change_map_.end();
+ ++iter) {
+ BlobChangeMap::iterator target_record = target_map.find(iter->first);
+ if (target_record != target_map.end()) {
+ delete target_record->second;
+ target_map.erase(target_record);
+ }
+ if (iter->second) {
+ target_map[iter->first] = iter->second;
+ iter->second = NULL;
+ }
+ }
+ }
+ if (!s.ok())
+ INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
+ else if (blobs_to_remove_.size())
+ s = backing_store_->CleanUpBlobJournal(BlobJournalKey::Encode());
+
+ return s;
+}
+
+
+class IndexedDBBackingStore::Transaction::BlobWriteCallbackWrapper
+ : public IndexedDBBackingStore::BlobWriteCallback {
+ public:
+ BlobWriteCallbackWrapper(IndexedDBBackingStore::Transaction* transaction,
+ scoped_refptr<BlobWriteCallback> callback)
+ : transaction_(transaction), callback_(callback) {}
+ virtual void Run(bool succeeded) OVERRIDE {
+ callback_->Run(succeeded);
+ if (succeeded) // Else it's already been deleted during rollback.
+ transaction_->chained_blob_writer_ = NULL;
+ }
+
+ private:
+ virtual ~BlobWriteCallbackWrapper() {}
+ friend class base::RefCounted<IndexedDBBackingStore::BlobWriteCallback>;
+
+ IndexedDBBackingStore::Transaction* transaction_;
+ scoped_refptr<BlobWriteCallback> callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(BlobWriteCallbackWrapper);
+};
+
+void IndexedDBBackingStore::Transaction::WriteNewBlobs(
+ BlobEntryKeyValuePairVec& new_blob_entries,
+ WriteDescriptorVec& new_files_to_write,
+ scoped_refptr<BlobWriteCallback> callback) {
+ DCHECK_GT(new_files_to_write.size(), 0UL);
+ DCHECK_GT(database_id_, 0);
+ BlobEntryKeyValuePairVec::iterator blob_entry_iter;
+ for (blob_entry_iter = new_blob_entries.begin();
+ blob_entry_iter != new_blob_entries.end();
+ ++blob_entry_iter) {
+ // Add the new blob-table entry for each blob to the main transaction, or
+ // remove any entry that may exist if there's no new one.
+ if (!blob_entry_iter->second.size())
+ transaction_->Remove(blob_entry_iter->first.Encode());
+ else
+ transaction_->Put(blob_entry_iter->first.Encode(),
+ &blob_entry_iter->second);
+ }
+ // Creating the writer will start it going asynchronously.
+ chained_blob_writer_ =
+ new ChainedBlobWriterImpl(database_id_,
+ backing_store_,
+ new_files_to_write,
+ new BlobWriteCallbackWrapper(this, callback));
}
void IndexedDBBackingStore::Transaction::Rollback() {
IDB_TRACE("IndexedDBBackingStore::Transaction::Rollback");
DCHECK(transaction_.get());
+ if (chained_blob_writer_) {
+ chained_blob_writer_->Abort();
+ chained_blob_writer_ = NULL;
+ }
transaction_->Rollback();
transaction_ = NULL;
}
+IndexedDBBackingStore::BlobChangeRecord::BlobChangeRecord(
+ const std::string& key,
+ int64 object_store_id)
+ : key_(key), object_store_id_(object_store_id) {
+}
+
+IndexedDBBackingStore::BlobChangeRecord::~BlobChangeRecord() {
+}
+
+void IndexedDBBackingStore::BlobChangeRecord::SetBlobInfo(
+ std::vector<IndexedDBBlobInfo>* blob_info) {
+ blob_info_.clear();
+ if (blob_info)
+ blob_info_.swap(*blob_info);
+}
+
+void IndexedDBBackingStore::BlobChangeRecord::SetHandles(
+ ScopedVector<webkit_blob::BlobDataHandle>* handles) {
+ handles_.clear();
+ if (handles)
+ handles_.swap(*handles);
+}
+
+scoped_ptr<IndexedDBBackingStore::BlobChangeRecord>
+IndexedDBBackingStore::BlobChangeRecord::Clone() const {
+ scoped_ptr<IndexedDBBackingStore::BlobChangeRecord> record(
+ new BlobChangeRecord(key_, object_store_id_));
+ record->blob_info_ = blob_info_;
+
+ ScopedVector<webkit_blob::BlobDataHandle>::const_iterator iter;
+ for (iter = handles_.begin(); iter != handles_.end(); ++iter)
+ record->handles_.push_back(new webkit_blob::BlobDataHandle(**iter));
+ return record.Pass();
+}
+
+leveldb::Status IndexedDBBackingStore::Transaction::PutBlobInfoIfNeeded(
+ int64 database_id,
+ int64 object_store_id,
+ const std::string& object_store_data_key,
+ std::vector<IndexedDBBlobInfo>* blob_info,
+ ScopedVector<webkit_blob::BlobDataHandle>* handles) {
+ if (!blob_info || blob_info->empty()) {
+ blob_change_map_.erase(object_store_data_key);
+ incognito_blob_map_.erase(object_store_data_key);
+
+ BlobEntryKey blob_entry_key;
+ StringPiece leveldb_key_piece(object_store_data_key);
+ if (!BlobEntryKey::FromObjectStoreDataKey(&leveldb_key_piece,
+ &blob_entry_key)) {
+ NOTREACHED();
+ return InternalInconsistencyStatus();
+ }
+ std::string value;
+ bool found = false;
+ leveldb::Status s =
+ transaction()->Get(blob_entry_key.Encode(), &value, &found);
+ if (!s.ok())
+ return s;
+ if (!found)
+ return leveldb::Status::OK();
+ }
+ PutBlobInfo(
+ database_id, object_store_id, object_store_data_key, blob_info, handles);
+ return leveldb::Status::OK();
+}
+
+// This is storing an info, even if empty, even if the previous key had no blob
+// info that we know of. It duplicates a bunch of information stored in the
+// leveldb transaction, but only w.r.t. the user keys altered--we don't keep the
+// changes to exists or index keys here.
+void IndexedDBBackingStore::Transaction::PutBlobInfo(
+ int64 database_id,
+ int64 object_store_id,
+ const std::string& object_store_data_key,
+ std::vector<IndexedDBBlobInfo>* blob_info,
+ ScopedVector<webkit_blob::BlobDataHandle>* handles) {
+ DCHECK_GT(object_store_data_key.size(), 0UL);
+ if (database_id_ < 0)
+ database_id_ = database_id;
+ DCHECK_EQ(database_id_, database_id);
+
+ BlobChangeMap::iterator it = blob_change_map_.find(object_store_data_key);
+ BlobChangeRecord* record = NULL;
+ if (it == blob_change_map_.end()) {
+ record = new BlobChangeRecord(object_store_data_key, object_store_id);
+ blob_change_map_[object_store_data_key] = record;
+ } else {
+ record = it->second;
+ }
+ DCHECK_EQ(record->object_store_id(), object_store_id);
+ record->SetBlobInfo(blob_info);
+ record->SetHandles(handles);
+ DCHECK(!handles || !handles->size());
+}
+
+IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
+ const GURL& url,
+ int64_t key,
+ int64_t size)
+ : is_file_(false), url_(url), key_(key), size_(size) {
+}
+
+IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
+ const FilePath& file_path,
+ int64_t key,
+ int64_t size,
+ base::Time last_modified)
+ : is_file_(true),
+ file_path_(file_path),
+ key_(key),
+ size_(size),
+ last_modified_(last_modified) {
+}
+
} // namespace content
diff --git a/chromium/content/browser/indexed_db/indexed_db_backing_store.h b/chromium/content/browser/indexed_db/indexed_db_backing_store.h
index 890f95ebdfc..59e381f2fef 100644
--- a/chromium/content/browser/indexed_db/indexed_db_backing_store.h
+++ b/chromium/content/browser/indexed_db/indexed_db_backing_store.h
@@ -5,15 +5,23 @@
#ifndef CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_BACKING_STORE_H_
#define CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_BACKING_STORE_H_
+#include <map>
+#include <set>
#include <string>
+#include <utility>
#include <vector>
#include "base/basictypes.h"
#include "base/files/file_path.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_piece.h"
+#include "base/time/time.h"
#include "base/timer/timer.h"
#include "content/browser/indexed_db/indexed_db.h"
+#include "content/browser/indexed_db/indexed_db_active_blob_registry.h"
+#include "content/browser/indexed_db/indexed_db_blob_info.h"
+#include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
#include "content/browser/indexed_db/indexed_db_metadata.h"
#include "content/browser/indexed_db/leveldb/leveldb_iterator.h"
#include "content/browser/indexed_db/leveldb/leveldb_transaction.h"
@@ -23,11 +31,26 @@
#include "content/common/indexed_db/indexed_db_key_range.h"
#include "third_party/leveldatabase/src/include/leveldb/status.h"
#include "url/gurl.h"
+#include "webkit/browser/blob/blob_data_handle.h"
+
+namespace base {
+class TaskRunner;
+}
+
+namespace fileapi {
+class FileWriterDelegate;
+}
+
+namespace net {
+class URLRequestContext;
+}
namespace content {
+class IndexedDBFactory;
class LevelDBComparator;
class LevelDBDatabase;
+struct IndexedDBValue;
class LevelDBFactory {
public:
@@ -36,7 +59,7 @@ class LevelDBFactory {
const LevelDBComparator* comparator,
scoped_ptr<LevelDBDatabase>* db,
bool* is_disk_full) = 0;
- virtual bool DestroyLevelDB(const base::FilePath& file_name) = 0;
+ virtual leveldb::Status DestroyLevelDB(const base::FilePath& file_name) = 0;
};
class CONTENT_EXPORT IndexedDBBackingStore
@@ -44,56 +67,89 @@ class CONTENT_EXPORT IndexedDBBackingStore
public:
class CONTENT_EXPORT Transaction;
+ class CONTENT_EXPORT Comparator : public LevelDBComparator {
+ public:
+ virtual int Compare(const base::StringPiece& a,
+ const base::StringPiece& b) const OVERRIDE;
+ virtual const char* Name() const OVERRIDE;
+ };
+
const GURL& origin_url() const { return origin_url_; }
+ IndexedDBFactory* factory() const { return indexed_db_factory_; }
+ base::TaskRunner* task_runner() const { return task_runner_; }
base::OneShotTimer<IndexedDBBackingStore>* close_timer() {
return &close_timer_;
}
+ IndexedDBActiveBlobRegistry* active_blob_registry() {
+ return &active_blob_registry_;
+ }
static scoped_refptr<IndexedDBBackingStore> Open(
+ IndexedDBFactory* indexed_db_factory,
const GURL& origin_url,
const base::FilePath& path_base,
+ net::URLRequestContext* request_context,
blink::WebIDBDataLoss* data_loss,
std::string* data_loss_message,
- bool* disk_full);
-
+ bool* disk_full,
+ base::TaskRunner* task_runner,
+ bool clean_journal);
static scoped_refptr<IndexedDBBackingStore> Open(
+ IndexedDBFactory* indexed_db_factory,
const GURL& origin_url,
const base::FilePath& path_base,
+ net::URLRequestContext* request_context,
blink::WebIDBDataLoss* data_loss,
std::string* data_loss_message,
bool* disk_full,
- LevelDBFactory* factory);
+ LevelDBFactory* leveldb_factory,
+ base::TaskRunner* task_runner,
+ bool clean_journal);
static scoped_refptr<IndexedDBBackingStore> OpenInMemory(
- const GURL& origin_url);
+ const GURL& origin_url,
+ base::TaskRunner* task_runner);
static scoped_refptr<IndexedDBBackingStore> OpenInMemory(
const GURL& origin_url,
- LevelDBFactory* factory);
-
- virtual std::vector<base::string16> GetDatabaseNames();
- virtual bool GetIDBDatabaseMetaData(const base::string16& name,
- IndexedDBDatabaseMetadata* metadata,
- bool* success) WARN_UNUSED_RESULT;
- virtual bool CreateIDBDatabaseMetaData(const base::string16& name,
- const base::string16& version,
- int64 int_version,
- int64* row_id);
+ LevelDBFactory* leveldb_factory,
+ base::TaskRunner* task_runner);
+
+ void GrantChildProcessPermissions(int child_process_id);
+
+ // Compact is public for testing.
+ virtual void Compact();
+ virtual std::vector<base::string16> GetDatabaseNames(leveldb::Status*);
+ virtual leveldb::Status GetIDBDatabaseMetaData(
+ const base::string16& name,
+ IndexedDBDatabaseMetadata* metadata,
+ bool* success) WARN_UNUSED_RESULT;
+ virtual leveldb::Status CreateIDBDatabaseMetaData(
+ const base::string16& name,
+ const base::string16& version,
+ int64 int_version,
+ int64* row_id);
virtual bool UpdateIDBDatabaseIntVersion(
IndexedDBBackingStore::Transaction* transaction,
int64 row_id,
int64 int_version);
- virtual bool DeleteDatabase(const base::string16& name);
-
- bool GetObjectStores(int64 database_id,
- IndexedDBDatabaseMetadata::ObjectStoreMap* map)
- WARN_UNUSED_RESULT;
- virtual bool CreateObjectStore(
+ virtual leveldb::Status DeleteDatabase(const base::string16& name);
+
+ // Assumes caller has already closed the backing store.
+ static leveldb::Status DestroyBackingStore(const base::FilePath& path_base,
+ const GURL& origin_url);
+ static bool RecordCorruptionInfo(const base::FilePath& path_base,
+ const GURL& origin_url,
+ const std::string& message);
+ leveldb::Status GetObjectStores(
+ int64 database_id,
+ IndexedDBDatabaseMetadata::ObjectStoreMap* map) WARN_UNUSED_RESULT;
+ virtual leveldb::Status CreateObjectStore(
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
int64 object_store_id,
const base::string16& name,
const IndexedDBKeyPath& key_path,
bool auto_increment);
- virtual bool DeleteObjectStore(
+ virtual leveldb::Status DeleteObjectStore(
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
int64 object_store_id) WARN_UNUSED_RESULT;
@@ -119,36 +175,55 @@ class CONTENT_EXPORT IndexedDBBackingStore
DISALLOW_COPY_AND_ASSIGN(RecordIdentifier);
};
- virtual bool GetRecord(IndexedDBBackingStore::Transaction* transaction,
- int64 database_id,
- int64 object_store_id,
- const IndexedDBKey& key,
- std::string* record) WARN_UNUSED_RESULT;
- virtual bool PutRecord(IndexedDBBackingStore::Transaction* transaction,
- int64 database_id,
- int64 object_store_id,
- const IndexedDBKey& key,
- const std::string& value,
- RecordIdentifier* record) WARN_UNUSED_RESULT;
- virtual bool ClearObjectStore(IndexedDBBackingStore::Transaction* transaction,
- int64 database_id,
- int64 object_store_id) WARN_UNUSED_RESULT;
- virtual bool DeleteRecord(IndexedDBBackingStore::Transaction* transaction,
- int64 database_id,
- int64 object_store_id,
- const RecordIdentifier& record) WARN_UNUSED_RESULT;
- virtual bool GetKeyGeneratorCurrentNumber(
+ class BlobWriteCallback : public base::RefCounted<BlobWriteCallback> {
+ public:
+ virtual void Run(bool succeeded) = 0;
+
+ protected:
+ virtual ~BlobWriteCallback() {}
+ friend class base::RefCounted<BlobWriteCallback>;
+ };
+
+ virtual leveldb::Status GetRecord(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ const IndexedDBKey& key,
+ IndexedDBValue* record) WARN_UNUSED_RESULT;
+ virtual leveldb::Status PutRecord(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ const IndexedDBKey& key,
+ IndexedDBValue& value,
+ ScopedVector<webkit_blob::BlobDataHandle>* handles,
+ RecordIdentifier* record) WARN_UNUSED_RESULT;
+ virtual leveldb::Status ClearObjectStore(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id) WARN_UNUSED_RESULT;
+ virtual leveldb::Status DeleteRecord(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ const RecordIdentifier& record) WARN_UNUSED_RESULT;
+ virtual leveldb::Status DeleteRange(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ const IndexedDBKeyRange&) WARN_UNUSED_RESULT;
+ virtual leveldb::Status GetKeyGeneratorCurrentNumber(
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
int64 object_store_id,
int64* current_number) WARN_UNUSED_RESULT;
- virtual bool MaybeUpdateKeyGeneratorCurrentNumber(
+ virtual leveldb::Status MaybeUpdateKeyGeneratorCurrentNumber(
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
int64 object_store_id,
int64 new_state,
bool check_current) WARN_UNUSED_RESULT;
- virtual bool KeyExistsInObjectStore(
+ virtual leveldb::Status KeyExistsInObjectStore(
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
int64 object_store_id,
@@ -156,39 +231,47 @@ class CONTENT_EXPORT IndexedDBBackingStore
RecordIdentifier* found_record_identifier,
bool* found) WARN_UNUSED_RESULT;
- virtual bool CreateIndex(IndexedDBBackingStore::Transaction* transaction,
- int64 database_id,
- int64 object_store_id,
- int64 index_id,
- const base::string16& name,
- const IndexedDBKeyPath& key_path,
- bool is_unique,
- bool is_multi_entry) WARN_UNUSED_RESULT;
- virtual bool DeleteIndex(IndexedDBBackingStore::Transaction* transaction,
- int64 database_id,
- int64 object_store_id,
- int64 index_id) WARN_UNUSED_RESULT;
- virtual bool PutIndexDataForRecord(
+ virtual leveldb::Status CreateIndex(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ const base::string16& name,
+ const IndexedDBKeyPath& key_path,
+ bool is_unique,
+ bool is_multi_entry) WARN_UNUSED_RESULT;
+ virtual leveldb::Status DeleteIndex(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id) WARN_UNUSED_RESULT;
+ virtual leveldb::Status PutIndexDataForRecord(
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
int64 object_store_id,
int64 index_id,
const IndexedDBKey& key,
const RecordIdentifier& record) WARN_UNUSED_RESULT;
- virtual bool GetPrimaryKeyViaIndex(
+ virtual leveldb::Status GetPrimaryKeyViaIndex(
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
int64 object_store_id,
int64 index_id,
const IndexedDBKey& key,
scoped_ptr<IndexedDBKey>* primary_key) WARN_UNUSED_RESULT;
- virtual bool KeyExistsInIndex(IndexedDBBackingStore::Transaction* transaction,
- int64 database_id,
- int64 object_store_id,
- int64 index_id,
- const IndexedDBKey& key,
- scoped_ptr<IndexedDBKey>* found_primary_key,
- bool* exists) WARN_UNUSED_RESULT;
+ virtual leveldb::Status KeyExistsInIndex(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ const IndexedDBKey& key,
+ scoped_ptr<IndexedDBKey>* found_primary_key,
+ bool* exists) WARN_UNUSED_RESULT;
+
+ // Public for IndexedDBActiveBlobRegistry::ReleaseBlobRef.
+ virtual void ReportBlobUnused(int64 database_id, int64 blob_key);
+
+ base::FilePath GetBlobFileName(int64 database_id, int64 key);
class Cursor {
public:
@@ -214,24 +297,29 @@ class CONTENT_EXPORT IndexedDBBackingStore
};
const IndexedDBKey& key() const { return *current_key_; }
- bool Continue() { return Continue(NULL, NULL, SEEK); }
- bool Continue(const IndexedDBKey* key, IteratorState state) {
- return Continue(key, NULL, state);
+ bool Continue(leveldb::Status* s) { return Continue(NULL, NULL, SEEK, s); }
+ bool Continue(const IndexedDBKey* key,
+ IteratorState state,
+ leveldb::Status* s) {
+ return Continue(key, NULL, state, s);
}
bool Continue(const IndexedDBKey* key,
const IndexedDBKey* primary_key,
- IteratorState state);
- bool Advance(uint32 count);
- bool FirstSeek();
+ IteratorState state,
+ leveldb::Status*);
+ bool Advance(uint32 count, leveldb::Status*);
+ bool FirstSeek(leveldb::Status*);
virtual Cursor* Clone() = 0;
virtual const IndexedDBKey& primary_key() const;
- virtual std::string* value() = 0;
+ virtual IndexedDBValue* value() = 0;
virtual const RecordIdentifier& record_identifier() const;
virtual bool LoadCurrentRow() = 0;
protected:
- Cursor(LevelDBTransaction* transaction,
+ Cursor(scoped_refptr<IndexedDBBackingStore> backing_store,
+ Transaction* transaction,
+ int64 database_id,
const CursorOptions& cursor_options);
explicit Cursor(const IndexedDBBackingStore::Cursor* other);
@@ -242,11 +330,16 @@ class CONTENT_EXPORT IndexedDBBackingStore
bool IsPastBounds() const;
bool HaveEnteredRange() const;
- LevelDBTransaction* transaction_;
+ IndexedDBBackingStore* backing_store_;
+ Transaction* transaction_;
+ int64 database_id_;
const CursorOptions cursor_options_;
scoped_ptr<LevelDBIterator> iterator_;
scoped_ptr<IndexedDBKey> current_key_;
IndexedDBBackingStore::RecordIdentifier record_identifier_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Cursor);
};
virtual scoped_ptr<Cursor> OpenObjectStoreKeyCursor(
@@ -254,73 +347,221 @@ class CONTENT_EXPORT IndexedDBBackingStore
int64 database_id,
int64 object_store_id,
const IndexedDBKeyRange& key_range,
- indexed_db::CursorDirection);
+ indexed_db::CursorDirection,
+ leveldb::Status*);
virtual scoped_ptr<Cursor> OpenObjectStoreCursor(
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
int64 object_store_id,
const IndexedDBKeyRange& key_range,
- indexed_db::CursorDirection);
+ indexed_db::CursorDirection,
+ leveldb::Status*);
virtual scoped_ptr<Cursor> OpenIndexKeyCursor(
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
int64 object_store_id,
int64 index_id,
const IndexedDBKeyRange& key_range,
- indexed_db::CursorDirection);
+ indexed_db::CursorDirection,
+ leveldb::Status*);
virtual scoped_ptr<Cursor> OpenIndexCursor(
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
int64 object_store_id,
int64 index_id,
const IndexedDBKeyRange& key_range,
- indexed_db::CursorDirection);
+ indexed_db::CursorDirection,
+ leveldb::Status*);
+
+ class BlobChangeRecord {
+ public:
+ BlobChangeRecord(const std::string& key, int64 object_store_id);
+ ~BlobChangeRecord();
+ const std::string& key() const { return key_; }
+ int64 object_store_id() const { return object_store_id_; }
+ void SetBlobInfo(std::vector<IndexedDBBlobInfo>* blob_info);
+ std::vector<IndexedDBBlobInfo>& mutable_blob_info() { return blob_info_; }
+ const std::vector<IndexedDBBlobInfo>& blob_info() const {
+ return blob_info_;
+ }
+ void SetHandles(ScopedVector<webkit_blob::BlobDataHandle>* handles);
+ scoped_ptr<BlobChangeRecord> Clone() const;
+
+ private:
+ std::string key_;
+ int64 object_store_id_;
+ std::vector<IndexedDBBlobInfo> blob_info_;
+ ScopedVector<webkit_blob::BlobDataHandle> handles_;
+ DISALLOW_COPY_AND_ASSIGN(BlobChangeRecord);
+ };
+ typedef std::map<std::string, BlobChangeRecord*> BlobChangeMap;
class Transaction {
public:
explicit Transaction(IndexedDBBackingStore* backing_store);
virtual ~Transaction();
virtual void Begin();
- virtual bool Commit();
+ // The callback will be called eventually on success or failure, or
+ // immediately if phase one is complete due to lack of any blobs to write.
+ virtual leveldb::Status CommitPhaseOne(scoped_refptr<BlobWriteCallback>);
+ virtual leveldb::Status CommitPhaseTwo();
virtual void Rollback();
void Reset() {
backing_store_ = NULL;
transaction_ = NULL;
}
+ leveldb::Status PutBlobInfoIfNeeded(
+ int64 database_id,
+ int64 object_store_id,
+ const std::string& object_store_data_key,
+ std::vector<IndexedDBBlobInfo>*,
+ ScopedVector<webkit_blob::BlobDataHandle>* handles);
+ void PutBlobInfo(int64 database_id,
+ int64 object_store_id,
+ const std::string& object_store_data_key,
+ std::vector<IndexedDBBlobInfo>*,
+ ScopedVector<webkit_blob::BlobDataHandle>* handles);
LevelDBTransaction* transaction() { return transaction_; }
+ leveldb::Status GetBlobInfoForRecord(
+ int64 database_id,
+ const std::string& object_store_data_key,
+ IndexedDBValue* value);
+
+ // This holds a BlobEntryKey and the encoded IndexedDBBlobInfo vector stored
+ // under that key.
+ typedef std::vector<std::pair<BlobEntryKey, std::string> >
+ BlobEntryKeyValuePairVec;
+
+ class WriteDescriptor {
+ public:
+ WriteDescriptor(const GURL& url, int64_t key, int64_t size);
+ WriteDescriptor(const base::FilePath& path,
+ int64_t key,
+ int64_t size,
+ base::Time last_modified);
+
+ bool is_file() const { return is_file_; }
+ const GURL& url() const {
+ DCHECK(!is_file_);
+ return url_;
+ }
+ const base::FilePath& file_path() const {
+ DCHECK(is_file_);
+ return file_path_;
+ }
+ int64_t key() const { return key_; }
+ int64_t size() const { return size_; }
+ base::Time last_modified() const { return last_modified_; }
+
+ private:
+ bool is_file_;
+ GURL url_;
+ base::FilePath file_path_;
+ int64_t key_;
+ int64_t size_;
+ base::Time last_modified_;
+ };
+
+ class ChainedBlobWriter : public base::RefCounted<ChainedBlobWriter> {
+ public:
+ virtual void set_delegate(
+ scoped_ptr<fileapi::FileWriterDelegate> delegate) = 0;
+
+ // TODO(ericu): Add a reason in the event of failure.
+ virtual void ReportWriteCompletion(bool succeeded,
+ int64 bytes_written) = 0;
+
+ virtual void Abort() = 0;
+
+ protected:
+ virtual ~ChainedBlobWriter() {}
+ friend class base::RefCounted<ChainedBlobWriter>;
+ };
+
+ class ChainedBlobWriterImpl;
+
+ typedef std::vector<WriteDescriptor> WriteDescriptorVec;
+
private:
+ class BlobWriteCallbackWrapper;
+
+ leveldb::Status HandleBlobPreTransaction(
+ BlobEntryKeyValuePairVec* new_blob_entries,
+ WriteDescriptorVec* new_files_to_write);
+ // Returns true on success, false on failure.
+ bool CollectBlobFilesToRemove();
+ // The callback will be called eventually on success or failure.
+ void WriteNewBlobs(BlobEntryKeyValuePairVec& new_blob_entries,
+ WriteDescriptorVec& new_files_to_write,
+ scoped_refptr<BlobWriteCallback> callback);
+ leveldb::Status SortBlobsToRemove();
+
IndexedDBBackingStore* backing_store_;
scoped_refptr<LevelDBTransaction> transaction_;
+ BlobChangeMap blob_change_map_;
+ BlobChangeMap incognito_blob_map_;
+ int64 database_id_;
+ BlobJournalType blobs_to_remove_;
+ scoped_refptr<ChainedBlobWriter> chained_blob_writer_;
};
protected:
- IndexedDBBackingStore(const GURL& origin_url,
+ IndexedDBBackingStore(IndexedDBFactory* indexed_db_factory,
+ const GURL& origin_url,
+ const base::FilePath& blob_path,
+ net::URLRequestContext* request_context,
scoped_ptr<LevelDBDatabase> db,
- scoped_ptr<LevelDBComparator> comparator);
+ scoped_ptr<LevelDBComparator> comparator,
+ base::TaskRunner* task_runner);
virtual ~IndexedDBBackingStore();
friend class base::RefCounted<IndexedDBBackingStore>;
+ bool is_incognito() const { return !indexed_db_factory_; }
+
+ bool SetUpMetadata();
+
+ virtual bool WriteBlobFile(
+ int64 database_id,
+ const Transaction::WriteDescriptor& descriptor,
+ Transaction::ChainedBlobWriter* chained_blob_writer);
+ virtual bool RemoveBlobFile(int64 database_id, int64 key);
+ virtual void StartJournalCleaningTimer();
+ void CleanPrimaryJournalIgnoreReturn();
+
private:
static scoped_refptr<IndexedDBBackingStore> Create(
+ IndexedDBFactory* indexed_db_factory,
const GURL& origin_url,
+ const base::FilePath& blob_path,
+ net::URLRequestContext* request_context,
scoped_ptr<LevelDBDatabase> db,
- scoped_ptr<LevelDBComparator> comparator);
-
- bool FindKeyInIndex(IndexedDBBackingStore::Transaction* transaction,
- int64 database_id,
- int64 object_store_id,
- int64 index_id,
- const IndexedDBKey& key,
- std::string* found_encoded_primary_key,
- bool* found);
- bool GetIndexes(int64 database_id,
- int64 object_store_id,
- IndexedDBObjectStoreMetadata::IndexMap* map)
+ scoped_ptr<LevelDBComparator> comparator,
+ base::TaskRunner* task_runner);
+
+ static bool ReadCorruptionInfo(const base::FilePath& path_base,
+ const GURL& origin_url,
+ std::string& message);
+
+ leveldb::Status FindKeyInIndex(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ const IndexedDBKey& key,
+ std::string* found_encoded_primary_key,
+ bool* found);
+ leveldb::Status GetIndexes(int64 database_id,
+ int64 object_store_id,
+ IndexedDBObjectStoreMetadata::IndexMap* map)
WARN_UNUSED_RESULT;
+ bool RemoveBlobDirectory(int64 database_id);
+ leveldb::Status CleanUpBlobJournal(const std::string& level_db_key);
+ IndexedDBFactory* indexed_db_factory_;
const GURL origin_url_;
+ base::FilePath blob_path_;
// The origin identifier is a key prefix unique to the origin used in the
// leveldb backing store to partition data by origin. It is a normalized
@@ -330,9 +571,20 @@ class CONTENT_EXPORT IndexedDBBackingStore
// provides for future flexibility.
const std::string origin_identifier_;
+ net::URLRequestContext* request_context_;
+ base::TaskRunner* task_runner_;
+ std::set<int> child_process_ids_granted_;
+ BlobChangeMap incognito_blob_map_;
+ base::OneShotTimer<IndexedDBBackingStore> journal_cleaning_timer_;
+
scoped_ptr<LevelDBDatabase> db_;
scoped_ptr<LevelDBComparator> comparator_;
+ // Whenever blobs are registered in active_blob_registry_, indexed_db_factory_
+ // will hold a reference to this backing store.
+ IndexedDBActiveBlobRegistry active_blob_registry_;
base::OneShotTimer<IndexedDBBackingStore> close_timer_;
+
+ DISALLOW_COPY_AND_ASSIGN(IndexedDBBackingStore);
};
} // namespace content
diff --git a/chromium/content/browser/indexed_db/indexed_db_backing_store_unittest.cc b/chromium/content/browser/indexed_db/indexed_db_backing_store_unittest.cc
index d3e82964af4..76d6d5fa526 100644
--- a/chromium/content/browser/indexed_db/indexed_db_backing_store_unittest.cc
+++ b/chromium/content/browser/indexed_db/indexed_db_backing_store_unittest.cc
@@ -4,68 +4,689 @@
#include "content/browser/indexed_db/indexed_db_backing_store.h"
+#include "base/callback.h"
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
#include "base/logging.h"
+#include "base/macros.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/task_runner.h"
+#include "base/test/test_simple_task_runner.h"
+#include "content/browser/indexed_db/indexed_db_context_impl.h"
#include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
+#include "content/browser/indexed_db/indexed_db_value.h"
+#include "content/public/test/mock_special_storage_policy.h"
+#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/public/platform/WebIDBTypes.h"
+#include "webkit/browser/blob/blob_data_handle.h"
+#include "webkit/browser/quota/special_storage_policy.h"
+
+using base::ASCIIToUTF16;
namespace content {
namespace {
+class Comparator : public LevelDBComparator {
+ public:
+ virtual int Compare(const base::StringPiece& a,
+ const base::StringPiece& b) const OVERRIDE {
+ return content::Compare(a, b, false /*index_keys*/);
+ }
+ virtual const char* Name() const OVERRIDE { return "idb_cmp1"; }
+};
+
+class DefaultLevelDBFactory : public LevelDBFactory {
+ public:
+ DefaultLevelDBFactory() {}
+ virtual leveldb::Status OpenLevelDB(const base::FilePath& file_name,
+ const LevelDBComparator* comparator,
+ scoped_ptr<LevelDBDatabase>* db,
+ bool* is_disk_full) OVERRIDE {
+ return LevelDBDatabase::Open(file_name, comparator, db, is_disk_full);
+ }
+ virtual leveldb::Status DestroyLevelDB(
+ const base::FilePath& file_name) OVERRIDE {
+ return LevelDBDatabase::Destroy(file_name);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DefaultLevelDBFactory);
+};
+
+class TestableIndexedDBBackingStore : public IndexedDBBackingStore {
+ public:
+ static scoped_refptr<TestableIndexedDBBackingStore> Open(
+ IndexedDBFactory* indexed_db_factory,
+ const GURL& origin_url,
+ const base::FilePath& path_base,
+ net::URLRequestContext* request_context,
+ LevelDBFactory* leveldb_factory,
+ base::TaskRunner* task_runner) {
+ DCHECK(!path_base.empty());
+
+ scoped_ptr<LevelDBComparator> comparator(new Comparator());
+
+ if (!base::CreateDirectory(path_base))
+ return scoped_refptr<TestableIndexedDBBackingStore>();
+
+ const base::FilePath file_path = path_base.AppendASCII("test_db_path");
+ const base::FilePath blob_path = path_base.AppendASCII("test_blob_path");
+
+ scoped_ptr<LevelDBDatabase> db;
+ bool is_disk_full = false;
+ leveldb::Status status = leveldb_factory->OpenLevelDB(
+ file_path, comparator.get(), &db, &is_disk_full);
+
+ if (!db || !status.ok())
+ return scoped_refptr<TestableIndexedDBBackingStore>();
+
+ scoped_refptr<TestableIndexedDBBackingStore> backing_store(
+ new TestableIndexedDBBackingStore(indexed_db_factory,
+ origin_url,
+ blob_path,
+ request_context,
+ db.Pass(),
+ comparator.Pass(),
+ task_runner));
+
+ if (!backing_store->SetUpMetadata())
+ return scoped_refptr<TestableIndexedDBBackingStore>();
+
+ return backing_store;
+ }
+
+ const std::vector<IndexedDBBackingStore::Transaction::WriteDescriptor>&
+ writes() const {
+ return writes_;
+ }
+ void ClearWrites() { writes_.clear(); }
+ const std::vector<int64>& removals() const { return removals_; }
+ void ClearRemovals() { removals_.clear(); }
+
+ protected:
+ virtual ~TestableIndexedDBBackingStore() {}
+
+ virtual bool WriteBlobFile(
+ int64 database_id,
+ const Transaction::WriteDescriptor& descriptor,
+ Transaction::ChainedBlobWriter* chained_blob_writer) OVERRIDE {
+ if (KeyPrefix::IsValidDatabaseId(database_id_)) {
+ if (database_id_ != database_id) {
+ return false;
+ }
+ } else {
+ database_id_ = database_id;
+ }
+ writes_.push_back(descriptor);
+ task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&Transaction::ChainedBlobWriter::ReportWriteCompletion,
+ chained_blob_writer,
+ true,
+ 1));
+ return true;
+ }
+
+ virtual bool RemoveBlobFile(int64 database_id, int64 key) OVERRIDE {
+ if (database_id_ != database_id ||
+ !KeyPrefix::IsValidDatabaseId(database_id)) {
+ return false;
+ }
+ removals_.push_back(key);
+ return true;
+ }
+
+ // Timers don't play nicely with unit tests.
+ virtual void StartJournalCleaningTimer() OVERRIDE {
+ CleanPrimaryJournalIgnoreReturn();
+ }
+
+ private:
+ TestableIndexedDBBackingStore(IndexedDBFactory* indexed_db_factory,
+ const GURL& origin_url,
+ const base::FilePath& blob_path,
+ net::URLRequestContext* request_context,
+ scoped_ptr<LevelDBDatabase> db,
+ scoped_ptr<LevelDBComparator> comparator,
+ base::TaskRunner* task_runner)
+ : IndexedDBBackingStore(indexed_db_factory,
+ origin_url,
+ blob_path,
+ request_context,
+ db.Pass(),
+ comparator.Pass(),
+ task_runner),
+ database_id_(0) {}
+
+ int64 database_id_;
+ std::vector<Transaction::WriteDescriptor> writes_;
+ std::vector<int64> removals_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestableIndexedDBBackingStore);
+};
+
+class TestIDBFactory : public IndexedDBFactory {
+ public:
+ explicit TestIDBFactory(IndexedDBContextImpl* idb_context)
+ : IndexedDBFactory(idb_context) {}
+
+ scoped_refptr<TestableIndexedDBBackingStore> OpenBackingStoreForTest(
+ const GURL& origin,
+ net::URLRequestContext* url_request_context) {
+ blink::WebIDBDataLoss data_loss;
+ std::string data_loss_reason;
+ bool disk_full;
+ scoped_refptr<IndexedDBBackingStore> backing_store =
+ OpenBackingStore(origin,
+ context()->data_path(),
+ url_request_context,
+ &data_loss,
+ &data_loss_reason,
+ &disk_full);
+ scoped_refptr<TestableIndexedDBBackingStore> testable_store =
+ static_cast<TestableIndexedDBBackingStore*>(backing_store.get());
+ return testable_store;
+ }
+
+ protected:
+ virtual ~TestIDBFactory() {}
+
+ virtual scoped_refptr<IndexedDBBackingStore> OpenBackingStoreHelper(
+ const GURL& origin_url,
+ const base::FilePath& data_directory,
+ net::URLRequestContext* request_context,
+ blink::WebIDBDataLoss* data_loss,
+ std::string* data_loss_message,
+ bool* disk_full,
+ bool first_time) OVERRIDE {
+ DefaultLevelDBFactory leveldb_factory;
+ return TestableIndexedDBBackingStore::Open(this,
+ origin_url,
+ data_directory,
+ request_context,
+ &leveldb_factory,
+ context()->TaskRunner());
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestIDBFactory);
+};
+
class IndexedDBBackingStoreTest : public testing::Test {
public:
IndexedDBBackingStoreTest() {}
virtual void SetUp() {
const GURL origin("http://localhost:81");
- backing_store_ = IndexedDBBackingStore::OpenInMemory(origin);
+ task_runner_ = new base::TestSimpleTaskRunner();
+ special_storage_policy_ = new MockSpecialStoragePolicy();
+ special_storage_policy_->SetAllUnlimited(true);
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ idb_context_ = new IndexedDBContextImpl(
+ temp_dir_.path(), special_storage_policy_, NULL, task_runner_);
+ idb_factory_ = new TestIDBFactory(idb_context_);
+ backing_store_ =
+ idb_factory_->OpenBackingStoreForTest(origin, &url_request_context_);
// useful keys and values during tests
- m_value1 = "value1";
- m_value2 = "value2";
- m_value3 = "value3";
+ m_value1 = IndexedDBValue("value1", std::vector<IndexedDBBlobInfo>());
+ m_value2 = IndexedDBValue("value2", std::vector<IndexedDBBlobInfo>());
+
+ m_blob_info.push_back(
+ IndexedDBBlobInfo("uuid 3", base::UTF8ToUTF16("blob type"), 1));
+ m_blob_info.push_back(
+ IndexedDBBlobInfo("uuid 4",
+ base::FilePath(FILE_PATH_LITERAL("path/to/file")),
+ base::UTF8ToUTF16("file name"),
+ base::UTF8ToUTF16("file type")));
+ m_value3 = IndexedDBValue("value3", m_blob_info);
+
m_key1 = IndexedDBKey(99, blink::WebIDBKeyTypeNumber);
m_key2 = IndexedDBKey(ASCIIToUTF16("key2"));
m_key3 = IndexedDBKey(ASCIIToUTF16("key3"));
}
+ // This just checks the data that survive getting stored and recalled, e.g.
+ // the file path and UUID will change and thus aren't verified.
+ bool CheckBlobInfoMatches(const std::vector<IndexedDBBlobInfo>& reads) const {
+ if (m_blob_info.size() != reads.size())
+ return false;
+ for (size_t i = 0; i < m_blob_info.size(); ++i) {
+ const IndexedDBBlobInfo& a = m_blob_info[i];
+ const IndexedDBBlobInfo& b = reads[i];
+ if (a.is_file() != b.is_file())
+ return false;
+ if (a.type() != b.type())
+ return false;
+ if (a.is_file()) {
+ if (a.file_name() != b.file_name())
+ return false;
+ } else {
+ if (a.size() != b.size())
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool CheckBlobReadsMatchWrites(
+ const std::vector<IndexedDBBlobInfo>& reads) const {
+ if (backing_store_->writes().size() != reads.size())
+ return false;
+ std::set<int64> ids;
+ for (size_t i = 0; i < backing_store_->writes().size(); ++i)
+ ids.insert(backing_store_->writes()[i].key());
+ if (ids.size() != backing_store_->writes().size())
+ return false;
+ for (size_t i = 0; i < reads.size(); ++i) {
+ if (ids.count(reads[i].key()) != 1)
+ return false;
+ }
+ return true;
+ }
+
+ bool CheckBlobWrites() const {
+ if (backing_store_->writes().size() != m_blob_info.size())
+ return false;
+ for (size_t i = 0; i < backing_store_->writes().size(); ++i) {
+ const IndexedDBBackingStore::Transaction::WriteDescriptor& desc =
+ backing_store_->writes()[i];
+ const IndexedDBBlobInfo& info = m_blob_info[i];
+ if (desc.is_file() != info.is_file())
+ return false;
+ if (desc.is_file()) {
+ if (desc.file_path() != info.file_path())
+ return false;
+ } else {
+ if (desc.url() != GURL("blob:uuid/" + info.uuid()))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool CheckBlobRemovals() const {
+ if (backing_store_->removals().size() != backing_store_->writes().size())
+ return false;
+ for (size_t i = 0; i < backing_store_->writes().size(); ++i) {
+ if (backing_store_->writes()[i].key() != backing_store_->removals()[i])
+ return false;
+ }
+ return true;
+ }
+
protected:
- scoped_refptr<IndexedDBBackingStore> backing_store_;
+ base::ScopedTempDir temp_dir_;
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+ scoped_refptr<MockSpecialStoragePolicy> special_storage_policy_;
+ scoped_refptr<IndexedDBContextImpl> idb_context_;
+ scoped_refptr<TestIDBFactory> idb_factory_;
+ net::TestURLRequestContext url_request_context_;
+
+ scoped_refptr<TestableIndexedDBBackingStore> backing_store_;
// Sample keys and values that are consistent.
IndexedDBKey m_key1;
IndexedDBKey m_key2;
IndexedDBKey m_key3;
- std::string m_value1;
- std::string m_value2;
- std::string m_value3;
+ IndexedDBValue m_value1;
+ IndexedDBValue m_value2;
+ IndexedDBValue m_value3;
+ std::vector<IndexedDBBlobInfo> m_blob_info;
private:
DISALLOW_COPY_AND_ASSIGN(IndexedDBBackingStoreTest);
};
+class TestCallback : public IndexedDBBackingStore::BlobWriteCallback {
+ public:
+ TestCallback() : called(false), succeeded(false) {}
+ virtual void Run(bool succeeded_in) OVERRIDE {
+ called = true;
+ succeeded = succeeded_in;
+ }
+ bool called;
+ bool succeeded;
+
+ protected:
+ virtual ~TestCallback() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestCallback);
+};
+
TEST_F(IndexedDBBackingStoreTest, PutGetConsistency) {
{
IndexedDBBackingStore::Transaction transaction1(backing_store_);
transaction1.Begin();
+ ScopedVector<webkit_blob::BlobDataHandle> handles;
IndexedDBBackingStore::RecordIdentifier record;
- bool ok = backing_store_->PutRecord(
- &transaction1, 1, 1, m_key1, m_value1, &record);
- EXPECT_TRUE(ok);
- transaction1.Commit();
+ leveldb::Status s = backing_store_->PutRecord(
+ &transaction1, 1, 1, m_key1, m_value1, &handles, &record);
+ EXPECT_TRUE(s.ok());
+ scoped_refptr<TestCallback> callback(new TestCallback());
+ EXPECT_TRUE(transaction1.CommitPhaseOne(callback).ok());
+ EXPECT_TRUE(callback->called);
+ EXPECT_TRUE(callback->succeeded);
+ EXPECT_TRUE(transaction1.CommitPhaseTwo().ok());
}
{
IndexedDBBackingStore::Transaction transaction2(backing_store_);
transaction2.Begin();
- std::string result_value;
- bool ok =
- backing_store_->GetRecord(&transaction2, 1, 1, m_key1, &result_value);
- transaction2.Commit();
- EXPECT_TRUE(ok);
- EXPECT_EQ(m_value1, result_value);
+ IndexedDBValue result_value;
+ EXPECT_TRUE(
+ backing_store_->GetRecord(&transaction2, 1, 1, m_key1, &result_value)
+ .ok());
+ scoped_refptr<TestCallback> callback(new TestCallback());
+ EXPECT_TRUE(transaction2.CommitPhaseOne(callback).ok());
+ EXPECT_TRUE(callback->called);
+ EXPECT_TRUE(callback->succeeded);
+ EXPECT_TRUE(transaction2.CommitPhaseTwo().ok());
+ EXPECT_EQ(m_value1.bits, result_value.bits);
+ }
+}
+
+TEST_F(IndexedDBBackingStoreTest, PutGetConsistencyWithBlobs) {
+ {
+ IndexedDBBackingStore::Transaction transaction1(backing_store_);
+ transaction1.Begin();
+ ScopedVector<webkit_blob::BlobDataHandle> handles;
+ IndexedDBBackingStore::RecordIdentifier record;
+ EXPECT_TRUE(backing_store_->PutRecord(&transaction1,
+ 1,
+ 1,
+ m_key3,
+ m_value3,
+ &handles,
+ &record).ok());
+ scoped_refptr<TestCallback> callback(new TestCallback());
+ EXPECT_TRUE(transaction1.CommitPhaseOne(callback).ok());
+ task_runner_->RunUntilIdle();
+ EXPECT_TRUE(CheckBlobWrites());
+ EXPECT_TRUE(callback->called);
+ EXPECT_TRUE(callback->succeeded);
+ EXPECT_TRUE(transaction1.CommitPhaseTwo().ok());
+ }
+
+ {
+ IndexedDBBackingStore::Transaction transaction2(backing_store_);
+ transaction2.Begin();
+ IndexedDBValue result_value;
+ EXPECT_TRUE(
+ backing_store_->GetRecord(&transaction2, 1, 1, m_key3, &result_value)
+ .ok());
+ scoped_refptr<TestCallback> callback(new TestCallback());
+ EXPECT_TRUE(transaction2.CommitPhaseOne(callback).ok());
+ EXPECT_TRUE(callback->called);
+ EXPECT_TRUE(callback->succeeded);
+ EXPECT_TRUE(transaction2.CommitPhaseTwo().ok());
+ EXPECT_EQ(m_value3.bits, result_value.bits);
+ EXPECT_TRUE(CheckBlobInfoMatches(result_value.blob_info));
+ EXPECT_TRUE(CheckBlobReadsMatchWrites(result_value.blob_info));
+ }
+
+ {
+ IndexedDBBackingStore::Transaction transaction3(backing_store_);
+ transaction3.Begin();
+ IndexedDBValue result_value;
+ EXPECT_TRUE(backing_store_->DeleteRange(&transaction3,
+ 1,
+ 1,
+ IndexedDBKeyRange(m_key3)).ok());
+ scoped_refptr<TestCallback> callback(new TestCallback());
+ EXPECT_TRUE(transaction3.CommitPhaseOne(callback).ok());
+ task_runner_->RunUntilIdle();
+ EXPECT_TRUE(callback->called);
+ EXPECT_TRUE(callback->succeeded);
+ EXPECT_TRUE(transaction3.CommitPhaseTwo().ok());
+ EXPECT_TRUE(CheckBlobRemovals());
+ }
+}
+
+TEST_F(IndexedDBBackingStoreTest, DeleteRange) {
+ IndexedDBKey key0 = IndexedDBKey(ASCIIToUTF16("key0"));
+ IndexedDBKey key1 = IndexedDBKey(ASCIIToUTF16("key1"));
+ IndexedDBKey key2 = IndexedDBKey(ASCIIToUTF16("key2"));
+ IndexedDBKey key3 = IndexedDBKey(ASCIIToUTF16("key3"));
+ IndexedDBBlobInfo blob0("uuid 0", base::UTF8ToUTF16("type 0"), 1);
+ IndexedDBBlobInfo blob1("uuid 1", base::UTF8ToUTF16("type 1"), 1);
+ IndexedDBBlobInfo blob2("uuid 2", base::UTF8ToUTF16("type 2"), 1);
+ IndexedDBBlobInfo blob3("uuid 3", base::UTF8ToUTF16("type 3"), 1);
+ IndexedDBKeyRange ranges[] = {IndexedDBKeyRange(key1, key2, false, false),
+ IndexedDBKeyRange(key1, key2, false, false),
+ IndexedDBKeyRange(key0, key2, true, false),
+ IndexedDBKeyRange(key1, key3, false, true),
+ IndexedDBKeyRange(key0, key3, true, true)};
+
+ for (unsigned i = 0; i < sizeof(ranges) / sizeof(IndexedDBKeyRange); ++i) {
+ backing_store_->ClearWrites();
+ backing_store_->ClearRemovals();
+
+ {
+ std::vector<IndexedDBBlobInfo> blob_info0, blob_info1, blob_info2,
+ blob_info3;
+ blob_info0.push_back(blob0);
+ blob_info1.push_back(blob1);
+ blob_info2.push_back(blob2);
+ blob_info3.push_back(blob3);
+ IndexedDBValue value0 = IndexedDBValue("value0", blob_info0);
+ IndexedDBValue value1 = IndexedDBValue("value1", blob_info1);
+ IndexedDBValue value2 = IndexedDBValue("value2", blob_info2);
+ IndexedDBValue value3 = IndexedDBValue("value3", blob_info3);
+ IndexedDBBackingStore::Transaction transaction1(backing_store_);
+ transaction1.Begin();
+ ScopedVector<webkit_blob::BlobDataHandle> handles;
+ IndexedDBBackingStore::RecordIdentifier record;
+ EXPECT_TRUE(backing_store_->PutRecord(&transaction1,
+ 1,
+ i + 1,
+ key0,
+ value0,
+ &handles,
+ &record).ok());
+ EXPECT_TRUE(backing_store_->PutRecord(&transaction1,
+ 1,
+ i + 1,
+ key1,
+ value1,
+ &handles,
+ &record).ok());
+ EXPECT_TRUE(backing_store_->PutRecord(&transaction1,
+ 1,
+ i + 1,
+ key2,
+ value2,
+ &handles,
+ &record).ok());
+ EXPECT_TRUE(backing_store_->PutRecord(&transaction1,
+ 1,
+ i + 1,
+ key3,
+ value3,
+ &handles,
+ &record).ok());
+ scoped_refptr<TestCallback> callback(new TestCallback());
+ EXPECT_TRUE(transaction1.CommitPhaseOne(callback).ok());
+ task_runner_->RunUntilIdle();
+ EXPECT_TRUE(callback->called);
+ EXPECT_TRUE(callback->succeeded);
+ EXPECT_TRUE(transaction1.CommitPhaseTwo().ok());
+ }
+
+ {
+ IndexedDBBackingStore::Transaction transaction2(backing_store_);
+ transaction2.Begin();
+ IndexedDBValue result_value;
+ EXPECT_TRUE(
+ backing_store_->DeleteRange(&transaction2, 1, i + 1, ranges[i]).ok());
+ scoped_refptr<TestCallback> callback(new TestCallback());
+ EXPECT_TRUE(transaction2.CommitPhaseOne(callback).ok());
+ task_runner_->RunUntilIdle();
+ EXPECT_TRUE(callback->called);
+ EXPECT_TRUE(callback->succeeded);
+ EXPECT_TRUE(transaction2.CommitPhaseTwo().ok());
+ EXPECT_EQ(2UL, backing_store_->removals().size());
+ EXPECT_EQ(backing_store_->writes()[1].key(),
+ backing_store_->removals()[0]);
+ EXPECT_EQ(backing_store_->writes()[2].key(),
+ backing_store_->removals()[1]);
+ }
+ }
+}
+
+TEST_F(IndexedDBBackingStoreTest, DeleteRangeEmptyRange) {
+ IndexedDBKey key0 = IndexedDBKey(ASCIIToUTF16("key0"));
+ IndexedDBKey key1 = IndexedDBKey(ASCIIToUTF16("key1"));
+ IndexedDBKey key2 = IndexedDBKey(ASCIIToUTF16("key2"));
+ IndexedDBKey key3 = IndexedDBKey(ASCIIToUTF16("key3"));
+ IndexedDBKey key4 = IndexedDBKey(ASCIIToUTF16("key4"));
+ IndexedDBBlobInfo blob0("uuid 0", base::UTF8ToUTF16("type 0"), 1);
+ IndexedDBBlobInfo blob1("uuid 1", base::UTF8ToUTF16("type 1"), 1);
+ IndexedDBBlobInfo blob2("uuid 2", base::UTF8ToUTF16("type 2"), 1);
+ IndexedDBBlobInfo blob3("uuid 3", base::UTF8ToUTF16("type 3"), 1);
+ IndexedDBKeyRange ranges[] = {IndexedDBKeyRange(key3, key4, true, false),
+ IndexedDBKeyRange(key2, key1, false, false),
+ IndexedDBKeyRange(key2, key1, true, true)};
+
+ for (unsigned i = 0; i < arraysize(ranges); ++i) {
+ backing_store_->ClearWrites();
+ backing_store_->ClearRemovals();
+
+ {
+ std::vector<IndexedDBBlobInfo> blob_info0, blob_info1, blob_info2,
+ blob_info3;
+ blob_info0.push_back(blob0);
+ blob_info1.push_back(blob1);
+ blob_info2.push_back(blob2);
+ blob_info3.push_back(blob3);
+ IndexedDBValue value0 = IndexedDBValue("value0", blob_info0);
+ IndexedDBValue value1 = IndexedDBValue("value1", blob_info1);
+ IndexedDBValue value2 = IndexedDBValue("value2", blob_info2);
+ IndexedDBValue value3 = IndexedDBValue("value3", blob_info3);
+ IndexedDBBackingStore::Transaction transaction1(backing_store_);
+ transaction1.Begin();
+ ScopedVector<webkit_blob::BlobDataHandle> handles;
+ IndexedDBBackingStore::RecordIdentifier record;
+ EXPECT_TRUE(backing_store_->PutRecord(&transaction1,
+ 1,
+ i + 1,
+ key0,
+ value0,
+ &handles,
+ &record).ok());
+ EXPECT_TRUE(backing_store_->PutRecord(&transaction1,
+ 1,
+ i + 1,
+ key1,
+ value1,
+ &handles,
+ &record).ok());
+ EXPECT_TRUE(backing_store_->PutRecord(&transaction1,
+ 1,
+ i + 1,
+ key2,
+ value2,
+ &handles,
+ &record).ok());
+ EXPECT_TRUE(backing_store_->PutRecord(&transaction1,
+ 1,
+ i + 1,
+ key3,
+ value3,
+ &handles,
+ &record).ok());
+ scoped_refptr<TestCallback> callback(new TestCallback());
+ EXPECT_TRUE(transaction1.CommitPhaseOne(callback).ok());
+ task_runner_->RunUntilIdle();
+ EXPECT_TRUE(callback->called);
+ EXPECT_TRUE(callback->succeeded);
+ EXPECT_TRUE(transaction1.CommitPhaseTwo().ok());
+ }
+
+ {
+ IndexedDBBackingStore::Transaction transaction2(backing_store_);
+ transaction2.Begin();
+ IndexedDBValue result_value;
+ EXPECT_TRUE(
+ backing_store_->DeleteRange(&transaction2, 1, i + 1, ranges[i]).ok());
+ scoped_refptr<TestCallback> callback(new TestCallback());
+ EXPECT_TRUE(transaction2.CommitPhaseOne(callback).ok());
+ task_runner_->RunUntilIdle();
+ EXPECT_TRUE(callback->called);
+ EXPECT_TRUE(callback->succeeded);
+ EXPECT_TRUE(transaction2.CommitPhaseTwo().ok());
+ EXPECT_EQ(0UL, backing_store_->removals().size());
+ }
+ }
+}
+
+TEST_F(IndexedDBBackingStoreTest, LiveBlobJournal) {
+ {
+ IndexedDBBackingStore::Transaction transaction1(backing_store_);
+ transaction1.Begin();
+ ScopedVector<webkit_blob::BlobDataHandle> handles;
+ IndexedDBBackingStore::RecordIdentifier record;
+ EXPECT_TRUE(backing_store_->PutRecord(&transaction1,
+ 1,
+ 1,
+ m_key3,
+ m_value3,
+ &handles,
+ &record).ok());
+ scoped_refptr<TestCallback> callback(new TestCallback());
+ EXPECT_TRUE(transaction1.CommitPhaseOne(callback).ok());
+ task_runner_->RunUntilIdle();
+ EXPECT_TRUE(CheckBlobWrites());
+ EXPECT_TRUE(callback->called);
+ EXPECT_TRUE(callback->succeeded);
+ EXPECT_TRUE(transaction1.CommitPhaseTwo().ok());
+ }
+
+ IndexedDBValue read_result_value;
+ {
+ IndexedDBBackingStore::Transaction transaction2(backing_store_);
+ transaction2.Begin();
+ EXPECT_TRUE(
+ backing_store_->GetRecord(
+ &transaction2, 1, 1, m_key3, &read_result_value)
+ .ok());
+ scoped_refptr<TestCallback> callback(new TestCallback());
+ EXPECT_TRUE(transaction2.CommitPhaseOne(callback).ok());
+ EXPECT_TRUE(callback->called);
+ EXPECT_TRUE(callback->succeeded);
+ EXPECT_TRUE(transaction2.CommitPhaseTwo().ok());
+ EXPECT_EQ(m_value3.bits, read_result_value.bits);
+ EXPECT_TRUE(CheckBlobInfoMatches(read_result_value.blob_info));
+ EXPECT_TRUE(CheckBlobReadsMatchWrites(read_result_value.blob_info));
+ for (size_t i = 0; i < read_result_value.blob_info.size(); ++i) {
+ read_result_value.blob_info[i].mark_used_callback().Run();
+ }
+ }
+
+ {
+ IndexedDBBackingStore::Transaction transaction3(backing_store_);
+ transaction3.Begin();
+ EXPECT_TRUE(backing_store_->DeleteRange(&transaction3,
+ 1,
+ 1,
+ IndexedDBKeyRange(m_key3)).ok());
+ scoped_refptr<TestCallback> callback(new TestCallback());
+ EXPECT_TRUE(transaction3.CommitPhaseOne(callback).ok());
+ task_runner_->RunUntilIdle();
+ EXPECT_TRUE(callback->called);
+ EXPECT_TRUE(callback->succeeded);
+ EXPECT_TRUE(transaction3.CommitPhaseTwo().ok());
+ EXPECT_EQ(0U, backing_store_->removals().size());
+ for (size_t i = 0; i < read_result_value.blob_info.size(); ++i) {
+ read_result_value.blob_info[i].release_callback().Run(
+ read_result_value.blob_info[i].file_path());
+ }
+ task_runner_->RunUntilIdle();
+ EXPECT_NE(0U, backing_store_->removals().size());
+ EXPECT_TRUE(CheckBlobRemovals());
}
}
@@ -85,67 +706,79 @@ TEST_F(IndexedDBBackingStoreTest, HighIds) {
{
IndexedDBBackingStore::Transaction transaction1(backing_store_);
transaction1.Begin();
+ ScopedVector<webkit_blob::BlobDataHandle> handles;
IndexedDBBackingStore::RecordIdentifier record;
- bool ok = backing_store_->PutRecord(&transaction1,
- high_database_id,
- high_object_store_id,
- m_key1,
- m_value1,
- &record);
- EXPECT_TRUE(ok);
-
- ok = backing_store_->PutIndexDataForRecord(&transaction1,
- high_database_id,
- high_object_store_id,
- invalid_high_index_id,
- index_key,
- record);
- EXPECT_FALSE(ok);
-
- ok = backing_store_->PutIndexDataForRecord(&transaction1,
- high_database_id,
- high_object_store_id,
- high_index_id,
- index_key,
- record);
- EXPECT_TRUE(ok);
-
- ok = transaction1.Commit();
- EXPECT_TRUE(ok);
+ leveldb::Status s = backing_store_->PutRecord(&transaction1,
+ high_database_id,
+ high_object_store_id,
+ m_key1,
+ m_value1,
+ &handles,
+ &record);
+ EXPECT_TRUE(s.ok());
+
+ s = backing_store_->PutIndexDataForRecord(&transaction1,
+ high_database_id,
+ high_object_store_id,
+ invalid_high_index_id,
+ index_key,
+ record);
+ EXPECT_FALSE(s.ok());
+
+ s = backing_store_->PutIndexDataForRecord(&transaction1,
+ high_database_id,
+ high_object_store_id,
+ high_index_id,
+ index_key,
+ record);
+ EXPECT_TRUE(s.ok());
+
+ scoped_refptr<TestCallback> callback(new TestCallback());
+ s = transaction1.CommitPhaseOne(callback);
+ EXPECT_TRUE(s.ok());
+ EXPECT_TRUE(callback->called);
+ EXPECT_TRUE(callback->succeeded);
+ s = transaction1.CommitPhaseTwo();
+ EXPECT_TRUE(s.ok());
}
{
IndexedDBBackingStore::Transaction transaction2(backing_store_);
transaction2.Begin();
- std::string result_value;
- bool ok = backing_store_->GetRecord(&transaction2,
- high_database_id,
- high_object_store_id,
- m_key1,
- &result_value);
- EXPECT_TRUE(ok);
- EXPECT_EQ(m_value1, result_value);
+ IndexedDBValue result_value;
+ leveldb::Status s = backing_store_->GetRecord(&transaction2,
+ high_database_id,
+ high_object_store_id,
+ m_key1,
+ &result_value);
+ EXPECT_TRUE(s.ok());
+ EXPECT_EQ(m_value1.bits, result_value.bits);
scoped_ptr<IndexedDBKey> new_primary_key;
- ok = backing_store_->GetPrimaryKeyViaIndex(&transaction2,
- high_database_id,
- high_object_store_id,
- invalid_high_index_id,
- index_key,
- &new_primary_key);
- EXPECT_FALSE(ok);
-
- ok = backing_store_->GetPrimaryKeyViaIndex(&transaction2,
- high_database_id,
- high_object_store_id,
- high_index_id,
- index_key,
- &new_primary_key);
- EXPECT_TRUE(ok);
- EXPECT_TRUE(new_primary_key->IsEqual(m_key1));
-
- ok = transaction2.Commit();
- EXPECT_TRUE(ok);
+ s = backing_store_->GetPrimaryKeyViaIndex(&transaction2,
+ high_database_id,
+ high_object_store_id,
+ invalid_high_index_id,
+ index_key,
+ &new_primary_key);
+ EXPECT_FALSE(s.ok());
+
+ s = backing_store_->GetPrimaryKeyViaIndex(&transaction2,
+ high_database_id,
+ high_object_store_id,
+ high_index_id,
+ index_key,
+ &new_primary_key);
+ EXPECT_TRUE(s.ok());
+ EXPECT_TRUE(new_primary_key->Equals(m_key1));
+
+ scoped_refptr<TestCallback> callback(new TestCallback());
+ s = transaction2.CommitPhaseOne(callback);
+ EXPECT_TRUE(s.ok());
+ EXPECT_TRUE(callback->called);
+ EXPECT_TRUE(callback->succeeded);
+ s = transaction2.CommitPhaseTwo();
+ EXPECT_TRUE(s.ok());
}
}
@@ -157,82 +790,85 @@ TEST_F(IndexedDBBackingStoreTest, InvalidIds) {
const int64 index_id = kMinimumIndexId;
const int64 invalid_low_index_id = 19; // index_ids must be > kMinimumIndexId
- std::string result_value;
+ IndexedDBValue result_value;
IndexedDBBackingStore::Transaction transaction1(backing_store_);
transaction1.Begin();
+ ScopedVector<webkit_blob::BlobDataHandle> handles;
IndexedDBBackingStore::RecordIdentifier record;
- bool ok = backing_store_->PutRecord(&transaction1,
- database_id,
- KeyPrefix::kInvalidId,
- m_key1,
- m_value1,
- &record);
- EXPECT_FALSE(ok);
- ok = backing_store_->PutRecord(
- &transaction1, database_id, 0, m_key1, m_value1, &record);
- EXPECT_FALSE(ok);
- ok = backing_store_->PutRecord(&transaction1,
- KeyPrefix::kInvalidId,
- object_store_id,
- m_key1,
- m_value1,
- &record);
- EXPECT_FALSE(ok);
- ok = backing_store_->PutRecord(
- &transaction1, 0, object_store_id, m_key1, m_value1, &record);
- EXPECT_FALSE(ok);
-
- ok = backing_store_->GetRecord(
+ leveldb::Status s = backing_store_->PutRecord(&transaction1,
+ database_id,
+ KeyPrefix::kInvalidId,
+ m_key1,
+ m_value1,
+ &handles,
+ &record);
+ EXPECT_FALSE(s.ok());
+ s = backing_store_->PutRecord(
+ &transaction1, database_id, 0, m_key1, m_value1, &handles, &record);
+ EXPECT_FALSE(s.ok());
+ s = backing_store_->PutRecord(&transaction1,
+ KeyPrefix::kInvalidId,
+ object_store_id,
+ m_key1,
+ m_value1,
+ &handles,
+ &record);
+ EXPECT_FALSE(s.ok());
+ s = backing_store_->PutRecord(
+ &transaction1, 0, object_store_id, m_key1, m_value1, &handles, &record);
+ EXPECT_FALSE(s.ok());
+
+ s = backing_store_->GetRecord(
&transaction1, database_id, KeyPrefix::kInvalidId, m_key1, &result_value);
- EXPECT_FALSE(ok);
- ok = backing_store_->GetRecord(
+ EXPECT_FALSE(s.ok());
+ s = backing_store_->GetRecord(
&transaction1, database_id, 0, m_key1, &result_value);
- EXPECT_FALSE(ok);
- ok = backing_store_->GetRecord(&transaction1,
- KeyPrefix::kInvalidId,
- object_store_id,
- m_key1,
- &result_value);
- EXPECT_FALSE(ok);
- ok = backing_store_->GetRecord(
+ EXPECT_FALSE(s.ok());
+ s = backing_store_->GetRecord(&transaction1,
+ KeyPrefix::kInvalidId,
+ object_store_id,
+ m_key1,
+ &result_value);
+ EXPECT_FALSE(s.ok());
+ s = backing_store_->GetRecord(
&transaction1, 0, object_store_id, m_key1, &result_value);
- EXPECT_FALSE(ok);
+ EXPECT_FALSE(s.ok());
scoped_ptr<IndexedDBKey> new_primary_key;
- ok = backing_store_->GetPrimaryKeyViaIndex(&transaction1,
- database_id,
- object_store_id,
- KeyPrefix::kInvalidId,
- m_key1,
- &new_primary_key);
- EXPECT_FALSE(ok);
- ok = backing_store_->GetPrimaryKeyViaIndex(&transaction1,
- database_id,
- object_store_id,
- invalid_low_index_id,
- m_key1,
- &new_primary_key);
- EXPECT_FALSE(ok);
- ok = backing_store_->GetPrimaryKeyViaIndex(
+ s = backing_store_->GetPrimaryKeyViaIndex(&transaction1,
+ database_id,
+ object_store_id,
+ KeyPrefix::kInvalidId,
+ m_key1,
+ &new_primary_key);
+ EXPECT_FALSE(s.ok());
+ s = backing_store_->GetPrimaryKeyViaIndex(&transaction1,
+ database_id,
+ object_store_id,
+ invalid_low_index_id,
+ m_key1,
+ &new_primary_key);
+ EXPECT_FALSE(s.ok());
+ s = backing_store_->GetPrimaryKeyViaIndex(
&transaction1, database_id, object_store_id, 0, m_key1, &new_primary_key);
- EXPECT_FALSE(ok);
-
- ok = backing_store_->GetPrimaryKeyViaIndex(&transaction1,
- KeyPrefix::kInvalidId,
- object_store_id,
- index_id,
- m_key1,
- &new_primary_key);
- EXPECT_FALSE(ok);
- ok = backing_store_->GetPrimaryKeyViaIndex(&transaction1,
- database_id,
- KeyPrefix::kInvalidId,
- index_id,
- m_key1,
- &new_primary_key);
- EXPECT_FALSE(ok);
+ EXPECT_FALSE(s.ok());
+
+ s = backing_store_->GetPrimaryKeyViaIndex(&transaction1,
+ KeyPrefix::kInvalidId,
+ object_store_id,
+ index_id,
+ m_key1,
+ &new_primary_key);
+ EXPECT_FALSE(s.ok());
+ s = backing_store_->GetPrimaryKeyViaIndex(&transaction1,
+ database_id,
+ KeyPrefix::kInvalidId,
+ index_id,
+ m_key1,
+ &new_primary_key);
+ EXPECT_FALSE(s.ok());
}
TEST_F(IndexedDBBackingStoreTest, CreateDatabase) {
@@ -254,42 +890,47 @@ TEST_F(IndexedDBBackingStoreTest, CreateDatabase) {
const IndexedDBKeyPath index_key_path(ASCIIToUTF16("index_key"));
{
- bool ok = backing_store_->CreateIDBDatabaseMetaData(
+ leveldb::Status s = backing_store_->CreateIDBDatabaseMetaData(
database_name, version, int_version, &database_id);
- EXPECT_TRUE(ok);
+ EXPECT_TRUE(s.ok());
EXPECT_GT(database_id, 0);
IndexedDBBackingStore::Transaction transaction(backing_store_);
transaction.Begin();
- ok = backing_store_->CreateObjectStore(&transaction,
- database_id,
- object_store_id,
- object_store_name,
- object_store_key_path,
- auto_increment);
- EXPECT_TRUE(ok);
-
- ok = backing_store_->CreateIndex(&transaction,
- database_id,
- object_store_id,
- index_id,
- index_name,
- index_key_path,
- unique,
- multi_entry);
- EXPECT_TRUE(ok);
-
- ok = transaction.Commit();
- EXPECT_TRUE(ok);
+ s = backing_store_->CreateObjectStore(&transaction,
+ database_id,
+ object_store_id,
+ object_store_name,
+ object_store_key_path,
+ auto_increment);
+ EXPECT_TRUE(s.ok());
+
+ s = backing_store_->CreateIndex(&transaction,
+ database_id,
+ object_store_id,
+ index_id,
+ index_name,
+ index_key_path,
+ unique,
+ multi_entry);
+ EXPECT_TRUE(s.ok());
+
+ scoped_refptr<TestCallback> callback(new TestCallback());
+ s = transaction.CommitPhaseOne(callback);
+ EXPECT_TRUE(s.ok());
+ EXPECT_TRUE(callback->called);
+ EXPECT_TRUE(callback->succeeded);
+ s = transaction.CommitPhaseTwo();
+ EXPECT_TRUE(s.ok());
}
{
IndexedDBDatabaseMetadata database;
bool found;
- bool ok = backing_store_->GetIDBDatabaseMetaData(
+ leveldb::Status s = backing_store_->GetIDBDatabaseMetaData(
database_name, &database, &found);
- EXPECT_TRUE(ok);
+ EXPECT_TRUE(s.ok());
EXPECT_TRUE(found);
// database.name is not filled in by the implementation.
@@ -297,8 +938,8 @@ TEST_F(IndexedDBBackingStoreTest, CreateDatabase) {
EXPECT_EQ(int_version, database.int_version);
EXPECT_EQ(database_id, database.id);
- ok = backing_store_->GetObjectStores(database.id, &database.object_stores);
- EXPECT_TRUE(ok);
+ s = backing_store_->GetObjectStores(database.id, &database.object_stores);
+ EXPECT_TRUE(s.ok());
EXPECT_EQ(1UL, database.object_stores.size());
IndexedDBObjectStoreMetadata object_store =
diff --git a/chromium/content/browser/indexed_db/indexed_db_blob_info.cc b/chromium/content/browser/indexed_db/indexed_db_blob_info.cc
new file mode 100644
index 00000000000..2c7d9ec019c
--- /dev/null
+++ b/chromium/content/browser/indexed_db/indexed_db_blob_info.cc
@@ -0,0 +1,93 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/indexed_db/indexed_db_blob_info.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
+
+namespace content {
+
+IndexedDBBlobInfo::IndexedDBBlobInfo()
+ : is_file_(false), size_(-1), key_(DatabaseMetaDataKey::kInvalidBlobKey) {
+}
+
+IndexedDBBlobInfo::~IndexedDBBlobInfo() {}
+
+IndexedDBBlobInfo::IndexedDBBlobInfo(const std::string& uuid,
+ const base::string16& type,
+ int64 size)
+ : is_file_(false),
+ uuid_(uuid),
+ type_(type),
+ size_(size),
+ key_(DatabaseMetaDataKey::kInvalidBlobKey) {
+}
+
+IndexedDBBlobInfo::IndexedDBBlobInfo(const base::string16& type,
+ int64 size,
+ int64 key)
+ : is_file_(false), type_(type), size_(size), key_(key) {
+}
+
+IndexedDBBlobInfo::IndexedDBBlobInfo(const std::string& uuid,
+ const base::FilePath& file_path,
+ const base::string16& file_name,
+ const base::string16& type)
+ : is_file_(true),
+ uuid_(uuid),
+ type_(type),
+ size_(-1),
+ file_name_(file_name),
+ file_path_(file_path),
+ key_(DatabaseMetaDataKey::kInvalidBlobKey) {
+}
+
+IndexedDBBlobInfo::IndexedDBBlobInfo(int64 key,
+ const base::string16& type,
+ const base::string16& file_name)
+ : is_file_(true), type_(type), size_(-1), file_name_(file_name), key_(key) {
+}
+
+void IndexedDBBlobInfo::set_size(int64 size) {
+ DCHECK_EQ(-1, size_);
+ size_ = size;
+}
+
+void IndexedDBBlobInfo::set_uuid(const std::string& uuid) {
+ DCHECK(uuid_.empty());
+ uuid_ = uuid;
+ DCHECK(!uuid_.empty());
+}
+
+void IndexedDBBlobInfo::set_file_path(const base::FilePath& file_path) {
+ DCHECK(file_path_.empty());
+ file_path_ = file_path;
+}
+
+void IndexedDBBlobInfo::set_last_modified(const base::Time& time) {
+ DCHECK(base::Time().is_null());
+ DCHECK(is_file_);
+ last_modified_ = time;
+}
+
+void IndexedDBBlobInfo::set_key(int64 key) {
+ DCHECK_EQ(DatabaseMetaDataKey::kInvalidBlobKey, key_);
+ key_ = key;
+}
+
+void IndexedDBBlobInfo::set_mark_used_callback(
+ const base::Closure& mark_used_callback) {
+ DCHECK(mark_used_callback_.is_null());
+ mark_used_callback_ = mark_used_callback;
+}
+
+void IndexedDBBlobInfo::set_release_callback(
+ const ReleaseCallback& release_callback) {
+ DCHECK(release_callback_.is_null());
+ release_callback_ = release_callback;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/indexed_db/indexed_db_blob_info.h b/chromium/content/browser/indexed_db/indexed_db_blob_info.h
new file mode 100644
index 00000000000..a2ac99149fc
--- /dev/null
+++ b/chromium/content/browser/indexed_db/indexed_db_blob_info.h
@@ -0,0 +1,76 @@
+// 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_INDEXED_DB_INDEXED_DB_BLOB_INFO_H_
+#define CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_BLOB_INFO_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/time/time.h"
+#include "content/common/content_export.h"
+#include "webkit/common/blob/shareable_file_reference.h"
+
+namespace content {
+
+class CONTENT_EXPORT IndexedDBBlobInfo {
+ public:
+ typedef webkit_blob::ShareableFileReference::FinalReleaseCallback
+ ReleaseCallback;
+ IndexedDBBlobInfo();
+ ~IndexedDBBlobInfo();
+ // These two are used for Blobs.
+ IndexedDBBlobInfo(const std::string& uuid,
+ const base::string16& type,
+ int64 size);
+ IndexedDBBlobInfo(const base::string16& type, int64 size, int64 key);
+ // These two are used for Files.
+ IndexedDBBlobInfo(const std::string& uuid,
+ const base::FilePath& file_path,
+ const base::string16& file_name,
+ const base::string16& type);
+ IndexedDBBlobInfo(int64 key,
+ const base::string16& type,
+ const base::string16& file_name);
+
+ bool is_file() const { return is_file_; }
+ const std::string& uuid() const { return uuid_; }
+ const base::string16& type() const { return type_; }
+ int64 size() const { return size_; }
+ const base::string16& file_name() const { return file_name_; }
+ int64 key() const { return key_; }
+ const base::FilePath& file_path() const { return file_path_; }
+ const base::Time& last_modified() const { return last_modified_; }
+ const base::Closure& mark_used_callback() const {
+ return mark_used_callback_;
+ }
+ const ReleaseCallback& release_callback() const { return release_callback_; }
+
+ void set_size(int64 size);
+ void set_uuid(const std::string& uuid);
+ void set_file_path(const base::FilePath& file_path);
+ void set_last_modified(const base::Time& time);
+ void set_key(int64 key);
+ void set_mark_used_callback(const base::Closure& mark_used_callback);
+ void set_release_callback(const ReleaseCallback& release_callback);
+
+ private:
+ bool is_file_;
+ std::string uuid_; // Always for Blob; sometimes for File.
+ base::string16 type_; // Mime type.
+ int64 size_; // -1 if unknown for File.
+ base::string16 file_name_; // Only for File.
+ base::FilePath file_path_; // Only for File.
+ base::Time last_modified_; // Only for File; valid only if size is.
+
+ // Valid only when this comes out of the database.
+ int64 key_;
+ base::Closure mark_used_callback_;
+ ReleaseCallback release_callback_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_BLOB_INFO_H_
diff --git a/chromium/content/browser/indexed_db/indexed_db_browsertest.cc b/chromium/content/browser/indexed_db/indexed_db_browsertest.cc
index e979168e76d..ddbfb44bbac 100644
--- a/chromium/content/browser/indexed_db/indexed_db_browsertest.cc
+++ b/chromium/content/browser/indexed_db/indexed_db_browsertest.cc
@@ -5,13 +5,18 @@
#include "base/bind.h"
#include "base/command_line.h"
#include "base/file_util.h"
+#include "base/files/file.h"
+#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
+#include "base/lazy_instance.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/thread_test_helper.h"
#include "content/browser/browser_main_loop.h"
+#include "content/browser/indexed_db/indexed_db_class_factory.h"
#include "content/browser/indexed_db/indexed_db_context_impl.h"
+#include "content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
@@ -21,12 +26,18 @@
#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/shell/browser/shell.h"
-#include "content/test/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
+#include "net/base/escape.h"
+#include "net/base/net_errors.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
#include "webkit/browser/database/database_util.h"
#include "webkit/browser/quota/quota_manager.h"
+using base::ASCIIToUTF16;
using quota::QuotaManager;
using webkit_database::DatabaseUtil;
@@ -38,6 +49,25 @@ class IndexedDBBrowserTest : public ContentBrowserTest {
public:
IndexedDBBrowserTest() : disk_usage_(-1) {}
+ virtual void SetUp() OVERRIDE {
+ GetTestClassFactory()->Reset();
+ IndexedDBClassFactory::SetIndexedDBClassFactoryGetter(GetIDBClassFactory);
+ ContentBrowserTest::SetUp();
+ }
+
+ virtual void TearDown() OVERRIDE {
+ IndexedDBClassFactory::SetIndexedDBClassFactoryGetter(NULL);
+ ContentBrowserTest::TearDown();
+ }
+
+ void FailOperation(FailClass failure_class,
+ FailMethod failure_method,
+ int fail_on_instance_num,
+ int fail_on_call_num) {
+ GetTestClassFactory()->FailOperation(
+ failure_class, failure_method, fail_on_instance_num, fail_on_call_num);
+ }
+
void SimpleTest(const GURL& test_url, bool incognito = false) {
// The test page will perform tests on IndexedDB, then navigate to either
// a #pass or #fail ref.
@@ -118,13 +148,26 @@ class IndexedDBBrowserTest : public ContentBrowserTest {
base::MessageLoop::current()->RunUntilIdle();
return disk_usage_;
}
+
private:
+ static MockBrowserTestIndexedDBClassFactory* GetTestClassFactory() {
+ static ::base::LazyInstance<MockBrowserTestIndexedDBClassFactory>::Leaky
+ s_factory = LAZY_INSTANCE_INITIALIZER;
+ return s_factory.Pointer();
+ }
+
+ static IndexedDBClassFactory* GetIDBClassFactory() {
+ return GetTestClassFactory();
+ }
+
virtual void DidGetDiskUsage(int64 bytes) {
EXPECT_GT(bytes, 0);
disk_usage_ = bytes;
}
int64 disk_usage_;
+
+ DISALLOW_COPY_AND_ASSIGN(IndexedDBBrowserTest);
};
IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CursorTest) {
@@ -201,10 +244,15 @@ IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, Bug109187Test) {
class IndexedDBBrowserTestWithLowQuota : public IndexedDBBrowserTest {
public:
+ IndexedDBBrowserTestWithLowQuota() {}
+
virtual void SetUpOnMainThread() OVERRIDE {
const int kInitialQuotaKilobytes = 5000;
SetQuota(kInitialQuotaKilobytes);
}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(IndexedDBBrowserTestWithLowQuota);
};
IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithLowQuota, QuotaTest) {
@@ -213,9 +261,14 @@ IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithLowQuota, QuotaTest) {
class IndexedDBBrowserTestWithGCExposed : public IndexedDBBrowserTest {
public:
+ IndexedDBBrowserTestWithGCExposed() {}
+
virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
command_line->AppendSwitchASCII(switches::kJavaScriptFlags, "--expose-gc");
}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(IndexedDBBrowserTestWithGCExposed);
};
IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithGCExposed,
@@ -243,6 +296,7 @@ static void CopyLevelDBToProfile(Shell* shell,
class IndexedDBBrowserTestWithPreexistingLevelDB : public IndexedDBBrowserTest {
public:
+ IndexedDBBrowserTestWithPreexistingLevelDB() {}
virtual void SetUpOnMainThread() OVERRIDE {
scoped_refptr<IndexedDBContextImpl> context = GetContext();
context->TaskRunner()->PostTask(
@@ -257,6 +311,8 @@ class IndexedDBBrowserTestWithPreexistingLevelDB : public IndexedDBBrowserTest {
virtual std::string EnclosingLevelDBDir() = 0;
+ private:
+ DISALLOW_COPY_AND_ASSIGN(IndexedDBBrowserTestWithPreexistingLevelDB);
};
class IndexedDBBrowserTestWithVersion0Schema : public
@@ -355,6 +411,231 @@ IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CanDeleteWhenOverQuotaTest) {
SimpleTest(GetTestUrl("indexeddb", "delete_over_quota.html"));
}
+namespace {
+
+static void CompactIndexedDBBackingStore(
+ scoped_refptr<IndexedDBContextImpl> context,
+ const GURL& origin_url) {
+ IndexedDBFactory* factory = context->GetIDBFactory();
+
+ std::pair<IndexedDBFactory::OriginDBMapIterator,
+ IndexedDBFactory::OriginDBMapIterator> range =
+ factory->GetOpenDatabasesForOrigin(origin_url);
+
+ if (range.first == range.second) // If no open db's for this origin
+ return;
+
+ // Compact the first db's backing store since all the db's are in the same
+ // backing store.
+ IndexedDBDatabase* db = range.first->second;
+ IndexedDBBackingStore* backing_store = db->backing_store();
+ backing_store->Compact();
+}
+
+static void CorruptIndexedDBDatabase(
+ IndexedDBContextImpl* context,
+ const GURL& origin_url,
+ base::WaitableEvent* signal_when_finished) {
+
+ CompactIndexedDBBackingStore(context, origin_url);
+
+ int numFiles = 0;
+ int numErrors = 0;
+ base::FilePath idb_data_path = context->GetFilePath(origin_url);
+ const bool recursive = false;
+ base::FileEnumerator enumerator(
+ idb_data_path, recursive, base::FileEnumerator::FILES);
+ for (base::FilePath idb_file = enumerator.Next(); !idb_file.empty();
+ idb_file = enumerator.Next()) {
+ int64 size(0);
+ GetFileSize(idb_file, &size);
+
+ if (idb_file.Extension() == FILE_PATH_LITERAL(".ldb")) {
+ numFiles++;
+ base::File file(idb_file,
+ base::File::FLAG_WRITE | base::File::FLAG_OPEN_TRUNCATED);
+ if (file.IsValid()) {
+ // Was opened truncated, expand back to the original
+ // file size and fill with zeros (corrupting the file).
+ file.SetLength(size);
+ } else {
+ numErrors++;
+ }
+ }
+ }
+
+ VLOG(0) << "There were " << numFiles << " in " << idb_data_path.value()
+ << " with " << numErrors << " errors";
+ signal_when_finished->Signal();
+}
+
+const std::string s_corrupt_db_test_prefix = "/corrupt/test/";
+
+static scoped_ptr<net::test_server::HttpResponse> CorruptDBRequestHandler(
+ IndexedDBContextImpl* context,
+ const GURL& origin_url,
+ const std::string& path,
+ IndexedDBBrowserTest* test,
+ const net::test_server::HttpRequest& request) {
+ std::string request_path;
+ if (path.find(s_corrupt_db_test_prefix) != std::string::npos)
+ request_path = request.relative_url.substr(s_corrupt_db_test_prefix.size());
+ else
+ return scoped_ptr<net::test_server::HttpResponse>();
+
+ // Remove the query string if present.
+ std::string request_query;
+ size_t query_pos = request_path.find('?');
+ if (query_pos != std::string::npos) {
+ request_query = request_path.substr(query_pos + 1);
+ request_path = request_path.substr(0, query_pos);
+ }
+
+ if (request_path == "corruptdb" && !request_query.empty()) {
+ VLOG(0) << "Requested to corrupt IndexedDB: " << request_query;
+ base::WaitableEvent signal_when_finished(false, false);
+ context->TaskRunner()->PostTask(FROM_HERE,
+ base::Bind(&CorruptIndexedDBDatabase,
+ base::ConstRef(context),
+ origin_url,
+ &signal_when_finished));
+ signal_when_finished.Wait();
+
+ scoped_ptr<net::test_server::BasicHttpResponse> http_response(
+ new net::test_server::BasicHttpResponse);
+ http_response->set_code(net::HTTP_OK);
+ return http_response.PassAs<net::test_server::HttpResponse>();
+ } else if (request_path == "fail" && !request_query.empty()) {
+ FailClass failure_class = FAIL_CLASS_NOTHING;
+ FailMethod failure_method = FAIL_METHOD_NOTHING;
+ int instance_num = 1;
+ int call_num = 1;
+ std::string fail_class;
+ std::string fail_method;
+
+ url::Component query(0, request_query.length()), key_pos, value_pos;
+ while (url::ExtractQueryKeyValue(
+ request_query.c_str(), &query, &key_pos, &value_pos)) {
+ std::string escaped_key(request_query.substr(key_pos.begin, key_pos.len));
+ std::string escaped_value(
+ request_query.substr(value_pos.begin, value_pos.len));
+
+ std::string key = net::UnescapeURLComponent(
+ escaped_key,
+ net::UnescapeRule::NORMAL | net::UnescapeRule::SPACES |
+ net::UnescapeRule::URL_SPECIAL_CHARS);
+
+ std::string value = net::UnescapeURLComponent(
+ escaped_value,
+ net::UnescapeRule::NORMAL | net::UnescapeRule::SPACES |
+ net::UnescapeRule::URL_SPECIAL_CHARS);
+
+ if (key == "method")
+ fail_method = value;
+ else if (key == "class")
+ fail_class = value;
+ else if (key == "instNum")
+ instance_num = atoi(value.c_str());
+ else if (key == "callNum")
+ call_num = atoi(value.c_str());
+ else
+ NOTREACHED() << "Unknown param: \"" << key << "\"";
+ }
+
+ if (fail_class == "LevelDBTransaction") {
+ failure_class = FAIL_CLASS_LEVELDB_TRANSACTION;
+ if (fail_method == "Get")
+ failure_method = FAIL_METHOD_GET;
+ else if (fail_method == "Commit")
+ failure_method = FAIL_METHOD_COMMIT;
+ else {
+ NOTREACHED() << "Unknown method: \"" << fail_method << "\"";
+ }
+ }
+
+ DCHECK_GE(instance_num, 1);
+ DCHECK_GE(call_num, 1);
+
+ test->FailOperation(failure_class, failure_method, instance_num, call_num);
+
+ scoped_ptr<net::test_server::BasicHttpResponse> http_response(
+ new net::test_server::BasicHttpResponse);
+ http_response->set_code(net::HTTP_OK);
+ return http_response.PassAs<net::test_server::HttpResponse>();
+ }
+
+ // A request for a test resource
+ base::FilePath resourcePath =
+ content::GetTestFilePath("indexeddb", request_path.c_str());
+ scoped_ptr<net::test_server::BasicHttpResponse> http_response(
+ new net::test_server::BasicHttpResponse);
+ http_response->set_code(net::HTTP_OK);
+ std::string file_contents;
+ if (!base::ReadFileToString(resourcePath, &file_contents))
+ return scoped_ptr<net::test_server::HttpResponse>();
+ http_response->set_content(file_contents);
+ return http_response.PassAs<net::test_server::HttpResponse>();
+}
+
+} // namespace
+
+class IndexedDBBrowserCorruptionTest
+ : public IndexedDBBrowserTest,
+ public ::testing::WithParamInterface<const char*> {};
+
+IN_PROC_BROWSER_TEST_P(IndexedDBBrowserCorruptionTest,
+ OperationOnCorruptedOpenDatabase) {
+ ASSERT_TRUE(embedded_test_server()->Started() ||
+ embedded_test_server()->InitializeAndWaitUntilReady());
+ const GURL& origin_url = embedded_test_server()->base_url();
+ embedded_test_server()->RegisterRequestHandler(
+ base::Bind(&CorruptDBRequestHandler,
+ base::ConstRef(GetContext()),
+ origin_url,
+ s_corrupt_db_test_prefix,
+ this));
+
+ std::string test_file = s_corrupt_db_test_prefix +
+ "corrupted_open_db_detection.html#" + GetParam();
+ SimpleTest(embedded_test_server()->GetURL(test_file));
+
+ test_file = s_corrupt_db_test_prefix + "corrupted_open_db_recovery.html";
+ SimpleTest(embedded_test_server()->GetURL(test_file));
+}
+
+INSTANTIATE_TEST_CASE_P(IndexedDBBrowserCorruptionTestInstantiation,
+ IndexedDBBrowserCorruptionTest,
+ ::testing::Values("get",
+ "iterate",
+ "clearObjectStore"));
+
+// Crashes flakily on various platforms. crbug.com/375856
+IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest,
+ DISABLED_DeleteCompactsBackingStore) {
+ const GURL test_url = GetTestUrl("indexeddb", "delete_compact.html");
+ SimpleTest(GURL(test_url.spec() + "#fill"));
+ int64 after_filling = RequestDiskUsage();
+ EXPECT_GT(after_filling, 0);
+
+ SimpleTest(GURL(test_url.spec() + "#purge"));
+ int64 after_deleting = RequestDiskUsage();
+ EXPECT_LT(after_deleting, after_filling);
+
+ // The above tests verify basic assertions - that filling writes data and
+ // deleting reduces the amount stored.
+
+ // The below tests make assumptions about implementation specifics, such as
+ // data compression, compaction efficiency, and the maximum amount of
+ // metadata and log data remains after a deletion. It is possible that
+ // changes to the implementation may require these constants to be tweaked.
+
+ const int kTestFillBytes = 1024 * 1024 * 5; // 5MB
+ EXPECT_GT(after_filling, kTestFillBytes);
+
+ const int kTestCompactBytes = 1024 * 1024 * 1; // 1MB
+ EXPECT_LT(after_deleting, kTestCompactBytes);
+}
+
// Complex multi-step (converted from pyauto) tests begin here.
// Verify null key path persists after restarting browser.
@@ -395,7 +676,7 @@ IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, ConnectionsClosedOnTabClose) {
// Start on a different URL to force a new renderer process.
Shell* new_shell = CreateBrowser();
- NavigateToURL(new_shell, GURL(kAboutBlankURL));
+ NavigateToURL(new_shell, GURL(url::kAboutBlankURL));
NavigateAndWaitForTitle(new_shell, "version_change_blocked.html", "#tab2",
"setVersion(3) blocked");
@@ -423,6 +704,7 @@ IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, ForceCloseEventTest) {
base::string16 expected_title16(ASCIIToUTF16("connection closed"));
TitleWatcher title_watcher(shell()->web_contents(), expected_title16);
+ title_watcher.AlsoWaitForTitle(ASCIIToUTF16("connection closed with error"));
EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle());
}
@@ -433,8 +715,14 @@ class IndexedDBBrowserTestSingleProcess : public IndexedDBBrowserTest {
}
};
+// Crashing on Android due to kSingleProcess flag: http://crbug.com/342525
+#if defined(OS_ANDROID)
+#define MAYBE_RenderThreadShutdownTest DISABLED_RenderThreadShutdownTest
+#else
+#define MAYBE_RenderThreadShutdownTest RenderThreadShutdownTest
+#endif
IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestSingleProcess,
- RenderThreadShutdownTest) {
+ MAYBE_RenderThreadShutdownTest) {
SimpleTest(GetTestUrl("indexeddb", "shutdown_with_requests.html"));
}
diff --git a/chromium/content/browser/indexed_db/indexed_db_callbacks.cc b/chromium/content/browser/indexed_db/indexed_db_callbacks.cc
index 755551cec89..74a76fdce53 100644
--- a/chromium/content/browser/indexed_db/indexed_db_callbacks.cc
+++ b/chromium/content/browser/indexed_db/indexed_db_callbacks.cc
@@ -6,14 +6,27 @@
#include <algorithm>
+#include "base/guid.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "content/browser/child_process_security_policy_impl.h"
+#include "content/browser/fileapi/fileapi_message_filter.h"
+#include "content/browser/indexed_db/indexed_db_blob_info.h"
#include "content/browser/indexed_db/indexed_db_connection.h"
+#include "content/browser/indexed_db/indexed_db_context_impl.h"
#include "content/browser/indexed_db/indexed_db_cursor.h"
#include "content/browser/indexed_db/indexed_db_database_callbacks.h"
#include "content/browser/indexed_db/indexed_db_database_error.h"
#include "content/browser/indexed_db/indexed_db_metadata.h"
+#include "content/browser/indexed_db/indexed_db_value.h"
#include "content/common/indexed_db/indexed_db_constants.h"
#include "content/common/indexed_db/indexed_db_messages.h"
+#include "webkit/browser/blob/blob_storage_context.h"
#include "webkit/browser/quota/quota_manager.h"
+#include "webkit/common/blob/blob_data.h"
+#include "webkit/common/blob/shareable_file_reference.h"
+
+using webkit_blob::ShareableFileReference;
namespace content {
@@ -32,7 +45,8 @@ IndexedDBCallbacks::IndexedDBCallbacks(IndexedDBDispatcherHost* dispatcher_host,
ipc_cursor_id_(kNoCursor),
host_transaction_id_(kNoTransaction),
ipc_database_id_(kNoDatabase),
- ipc_database_callbacks_id_(kNoDatabaseCallbacks) {}
+ ipc_database_callbacks_id_(kNoDatabaseCallbacks),
+ data_loss_(blink::WebIDBDataLossNone) {}
IndexedDBCallbacks::IndexedDBCallbacks(IndexedDBDispatcherHost* dispatcher_host,
int32 ipc_thread_id,
@@ -44,7 +58,8 @@ IndexedDBCallbacks::IndexedDBCallbacks(IndexedDBDispatcherHost* dispatcher_host,
ipc_cursor_id_(ipc_cursor_id),
host_transaction_id_(kNoTransaction),
ipc_database_id_(kNoDatabase),
- ipc_database_callbacks_id_(kNoDatabaseCallbacks) {}
+ ipc_database_callbacks_id_(kNoDatabaseCallbacks),
+ data_loss_(blink::WebIDBDataLossNone) {}
IndexedDBCallbacks::IndexedDBCallbacks(IndexedDBDispatcherHost* dispatcher_host,
int32 ipc_thread_id,
@@ -59,7 +74,8 @@ IndexedDBCallbacks::IndexedDBCallbacks(IndexedDBDispatcherHost* dispatcher_host,
host_transaction_id_(host_transaction_id),
origin_url_(origin_url),
ipc_database_id_(kNoDatabase),
- ipc_database_callbacks_id_(ipc_database_callbacks_id) {}
+ ipc_database_callbacks_id_(ipc_database_callbacks_id),
+ data_loss_(blink::WebIDBDataLossNone) {}
IndexedDBCallbacks::~IndexedDBCallbacks() {}
@@ -78,6 +94,7 @@ void IndexedDBCallbacks::OnSuccess(const std::vector<base::string16>& value) {
DCHECK_EQ(kNoTransaction, host_transaction_id_);
DCHECK_EQ(kNoDatabase, ipc_database_id_);
DCHECK_EQ(kNoDatabaseCallbacks, ipc_database_callbacks_id_);
+ DCHECK_EQ(blink::WebIDBDataLossNone, data_loss_);
std::vector<base::string16> list;
for (unsigned i = 0; i < value.size(); ++i)
@@ -101,12 +118,17 @@ void IndexedDBCallbacks::OnBlocked(int64 existing_version) {
ipc_thread_id_, ipc_callbacks_id_, existing_version));
}
+void IndexedDBCallbacks::OnDataLoss(blink::WebIDBDataLoss data_loss,
+ std::string data_loss_message) {
+ DCHECK_NE(blink::WebIDBDataLossNone, data_loss);
+ data_loss_ = data_loss;
+ data_loss_message_ = data_loss_message;
+}
+
void IndexedDBCallbacks::OnUpgradeNeeded(
int64 old_version,
scoped_ptr<IndexedDBConnection> connection,
- const IndexedDBDatabaseMetadata& metadata,
- blink::WebIDBDataLoss data_loss,
- std::string data_loss_message) {
+ const IndexedDBDatabaseMetadata& metadata) {
DCHECK(dispatcher_host_.get());
DCHECK_EQ(kNoCursor, ipc_cursor_id_);
@@ -127,8 +149,8 @@ void IndexedDBCallbacks::OnUpgradeNeeded(
params.ipc_database_callbacks_id = ipc_database_callbacks_id_;
params.old_version = old_version;
params.idb_metadata = IndexedDBDispatcherHost::ConvertMetadata(metadata);
- params.data_loss = data_loss;
- params.data_loss_message = data_loss_message;
+ params.data_loss = data_loss_;
+ params.data_loss_message = data_loss_message_;
dispatcher_host_->Send(new IndexedDBMsg_CallbacksUpgradeNeeded(params));
}
@@ -159,42 +181,178 @@ void IndexedDBCallbacks::OnSuccess(scoped_ptr<IndexedDBConnection> connection,
dispatcher_host_ = NULL;
}
+static std::string CreateBlobData(
+ const IndexedDBBlobInfo& blob_info,
+ scoped_refptr<IndexedDBDispatcherHost> dispatcher_host,
+ webkit_blob::BlobStorageContext* blob_storage_context,
+ base::TaskRunner* task_runner) {
+ std::string uuid = blob_info.uuid();
+ if (!uuid.empty()) {
+ // We're sending back a live blob, not a reference into our backing store.
+ scoped_ptr<webkit_blob::BlobDataHandle> blob_data_handle(
+ blob_storage_context->GetBlobDataFromUUID(uuid));
+ dispatcher_host->HoldBlobDataHandle(uuid, blob_data_handle);
+ return uuid;
+ }
+ scoped_refptr<ShareableFileReference> shareable_file =
+ ShareableFileReference::Get(blob_info.file_path());
+ if (!shareable_file.get()) {
+ shareable_file = ShareableFileReference::GetOrCreate(
+ blob_info.file_path(),
+ ShareableFileReference::DONT_DELETE_ON_FINAL_RELEASE,
+ task_runner);
+ if (!blob_info.release_callback().is_null())
+ shareable_file->AddFinalReleaseCallback(blob_info.release_callback());
+ }
+
+ uuid = base::GenerateGUID();
+ scoped_refptr<webkit_blob::BlobData> blob_data =
+ new webkit_blob::BlobData(uuid);
+ blob_data->AppendFile(
+ blob_info.file_path(), 0, blob_info.size(), blob_info.last_modified());
+ scoped_ptr<webkit_blob::BlobDataHandle> blob_data_handle(
+ blob_storage_context->AddFinishedBlob(blob_data.get()));
+ dispatcher_host->HoldBlobDataHandle(uuid, blob_data_handle);
+
+ return uuid;
+}
+
+static bool CreateAllBlobs(
+ const std::vector<IndexedDBBlobInfo>& blob_info,
+ std::vector<IndexedDBMsg_BlobOrFileInfo>* blob_or_file_info,
+ scoped_refptr<IndexedDBDispatcherHost> dispatcher_host) {
+ DCHECK_EQ(blob_info.size(), blob_or_file_info->size());
+ size_t i;
+ if (!dispatcher_host->blob_storage_context())
+ return false;
+ for (i = 0; i < blob_info.size(); ++i) {
+ (*blob_or_file_info)[i].uuid =
+ CreateBlobData(blob_info[i],
+ dispatcher_host,
+ dispatcher_host->blob_storage_context(),
+ dispatcher_host->Context()->TaskRunner());
+ }
+ return true;
+}
+
+template <class ParamType, class MsgType>
+static void CreateBlobsAndSend(
+ ParamType* params,
+ scoped_refptr<IndexedDBDispatcherHost> dispatcher_host,
+ const std::vector<IndexedDBBlobInfo>& blob_info,
+ std::vector<IndexedDBMsg_BlobOrFileInfo>* blob_or_file_info) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ if (CreateAllBlobs(blob_info, blob_or_file_info, dispatcher_host))
+ dispatcher_host->Send(new MsgType(*params));
+}
+
+static void BlobLookupForCursorPrefetch(
+ IndexedDBMsg_CallbacksSuccessCursorPrefetch_Params* params,
+ scoped_refptr<IndexedDBDispatcherHost> dispatcher_host,
+ const std::vector<IndexedDBValue>& values) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_EQ(values.size(), params->blob_or_file_infos.size());
+
+ std::vector<IndexedDBValue>::const_iterator value_iter;
+ std::vector<std::vector<IndexedDBMsg_BlobOrFileInfo> >::iterator blob_iter;
+ for (value_iter = values.begin(), blob_iter =
+ params->blob_or_file_infos.begin(); value_iter != values.end();
+ ++value_iter, ++blob_iter) {
+ if (!CreateAllBlobs(value_iter->blob_info, &*blob_iter, dispatcher_host))
+ return;
+ }
+ dispatcher_host->Send(
+ new IndexedDBMsg_CallbacksSuccessCursorPrefetch(*params));
+}
+
+static void FillInBlobData(
+ const std::vector<IndexedDBBlobInfo>& blob_info,
+ std::vector<IndexedDBMsg_BlobOrFileInfo>* blob_or_file_info) {
+ for (std::vector<IndexedDBBlobInfo>::const_iterator iter = blob_info.begin();
+ iter != blob_info.end();
+ ++iter) {
+ if (iter->is_file()) {
+ IndexedDBMsg_BlobOrFileInfo info;
+ info.is_file = true;
+ info.mime_type = iter->type();
+ info.file_name = iter->file_name();
+ info.file_path = iter->file_path().AsUTF16Unsafe();
+ info.size = iter->size();
+ info.last_modified = iter->last_modified().ToDoubleT();
+ blob_or_file_info->push_back(info);
+ } else {
+ IndexedDBMsg_BlobOrFileInfo info;
+ info.mime_type = iter->type();
+ info.size = iter->size();
+ blob_or_file_info->push_back(info);
+ }
+ }
+}
+
+void IndexedDBCallbacks::RegisterBlobsAndSend(
+ const std::vector<IndexedDBBlobInfo>& blob_info,
+ const base::Closure& callback) {
+ std::vector<IndexedDBBlobInfo>::const_iterator iter;
+ for (iter = blob_info.begin(); iter != blob_info.end(); ++iter) {
+ if (!iter->mark_used_callback().is_null())
+ iter->mark_used_callback().Run();
+ }
+ DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO));
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, callback);
+}
+
void IndexedDBCallbacks::OnSuccess(scoped_refptr<IndexedDBCursor> cursor,
const IndexedDBKey& key,
const IndexedDBKey& primary_key,
- std::string* value) {
+ IndexedDBValue* value) {
DCHECK(dispatcher_host_.get());
DCHECK_EQ(kNoCursor, ipc_cursor_id_);
DCHECK_EQ(kNoTransaction, host_transaction_id_);
DCHECK_EQ(kNoDatabase, ipc_database_id_);
DCHECK_EQ(kNoDatabaseCallbacks, ipc_database_callbacks_id_);
+ DCHECK_EQ(blink::WebIDBDataLossNone, data_loss_);
int32 ipc_object_id = dispatcher_host_->Add(cursor.get());
- IndexedDBMsg_CallbacksSuccessIDBCursor_Params params;
- params.ipc_thread_id = ipc_thread_id_;
- params.ipc_callbacks_id = ipc_callbacks_id_;
- params.ipc_cursor_id = ipc_object_id;
- params.key = key;
- params.primary_key = primary_key;
+ scoped_ptr<IndexedDBMsg_CallbacksSuccessIDBCursor_Params> params(
+ new IndexedDBMsg_CallbacksSuccessIDBCursor_Params());
+ params->ipc_thread_id = ipc_thread_id_;
+ params->ipc_callbacks_id = ipc_callbacks_id_;
+ params->ipc_cursor_id = ipc_object_id;
+ params->key = key;
+ params->primary_key = primary_key;
if (value && !value->empty())
- std::swap(params.value, *value);
+ std::swap(params->value, value->bits);
// TODO(alecflett): Avoid a copy here: the whole params object is
// being copied into the message.
- dispatcher_host_->Send(new IndexedDBMsg_CallbacksSuccessIDBCursor(params));
-
+ if (!value || value->blob_info.empty()) {
+ dispatcher_host_->Send(new IndexedDBMsg_CallbacksSuccessIDBCursor(*params));
+ } else {
+ IndexedDBMsg_CallbacksSuccessIDBCursor_Params* p = params.get();
+ FillInBlobData(value->blob_info, &p->blob_or_file_info);
+ RegisterBlobsAndSend(
+ value->blob_info,
+ base::Bind(
+ CreateBlobsAndSend<IndexedDBMsg_CallbacksSuccessIDBCursor_Params,
+ IndexedDBMsg_CallbacksSuccessIDBCursor>,
+ base::Owned(params.release()),
+ dispatcher_host_,
+ value->blob_info,
+ base::Unretained(&p->blob_or_file_info)));
+ }
dispatcher_host_ = NULL;
}
void IndexedDBCallbacks::OnSuccess(const IndexedDBKey& key,
const IndexedDBKey& primary_key,
- std::string* value) {
+ IndexedDBValue* value) {
DCHECK(dispatcher_host_.get());
DCHECK_NE(kNoCursor, ipc_cursor_id_);
DCHECK_EQ(kNoTransaction, host_transaction_id_);
DCHECK_EQ(kNoDatabase, ipc_database_id_);
DCHECK_EQ(kNoDatabaseCallbacks, ipc_database_callbacks_id_);
+ DCHECK_EQ(blink::WebIDBDataLossNone, data_loss_);
IndexedDBCursor* idb_cursor =
dispatcher_host_->GetCursorFromId(ipc_cursor_id_);
@@ -202,25 +360,41 @@ void IndexedDBCallbacks::OnSuccess(const IndexedDBKey& key,
DCHECK(idb_cursor);
if (!idb_cursor)
return;
- IndexedDBMsg_CallbacksSuccessCursorContinue_Params params;
- params.ipc_thread_id = ipc_thread_id_;
- params.ipc_callbacks_id = ipc_callbacks_id_;
- params.ipc_cursor_id = ipc_cursor_id_;
- params.key = key;
- params.primary_key = primary_key;
+
+ scoped_ptr<IndexedDBMsg_CallbacksSuccessCursorContinue_Params> params(
+ new IndexedDBMsg_CallbacksSuccessCursorContinue_Params());
+ params->ipc_thread_id = ipc_thread_id_;
+ params->ipc_callbacks_id = ipc_callbacks_id_;
+ params->ipc_cursor_id = ipc_cursor_id_;
+ params->key = key;
+ params->primary_key = primary_key;
if (value && !value->empty())
- std::swap(params.value, *value);
+ std::swap(params->value, value->bits);
// TODO(alecflett): Avoid a copy here: the whole params object is
// being copied into the message.
- dispatcher_host_->Send(
- new IndexedDBMsg_CallbacksSuccessCursorContinue(params));
+ if (!value || value->blob_info.empty()) {
+ dispatcher_host_->Send(
+ new IndexedDBMsg_CallbacksSuccessCursorContinue(*params));
+ } else {
+ IndexedDBMsg_CallbacksSuccessCursorContinue_Params* p = params.get();
+ FillInBlobData(value->blob_info, &p->blob_or_file_info);
+ RegisterBlobsAndSend(
+ value->blob_info,
+ base::Bind(CreateBlobsAndSend<
+ IndexedDBMsg_CallbacksSuccessCursorContinue_Params,
+ IndexedDBMsg_CallbacksSuccessCursorContinue>,
+ base::Owned(params.release()),
+ dispatcher_host_,
+ value->blob_info,
+ base::Unretained(&p->blob_or_file_info)));
+ }
dispatcher_host_ = NULL;
}
void IndexedDBCallbacks::OnSuccessWithPrefetch(
const std::vector<IndexedDBKey>& keys,
const std::vector<IndexedDBKey>& primary_keys,
- const std::vector<std::string>& values) {
+ std::vector<IndexedDBValue>& values) {
DCHECK_EQ(keys.size(), primary_keys.size());
DCHECK_EQ(keys.size(), values.size());
@@ -230,6 +404,7 @@ void IndexedDBCallbacks::OnSuccessWithPrefetch(
DCHECK_EQ(kNoTransaction, host_transaction_id_);
DCHECK_EQ(kNoDatabase, ipc_database_id_);
DCHECK_EQ(kNoDatabaseCallbacks, ipc_database_callbacks_id_);
+ DCHECK_EQ(blink::WebIDBDataLossNone, data_loss_);
std::vector<IndexedDBKey> msgKeys;
std::vector<IndexedDBKey> msgPrimaryKeys;
@@ -239,19 +414,52 @@ void IndexedDBCallbacks::OnSuccessWithPrefetch(
msgPrimaryKeys.push_back(primary_keys[i]);
}
- IndexedDBMsg_CallbacksSuccessCursorPrefetch_Params params;
- params.ipc_thread_id = ipc_thread_id_;
- params.ipc_callbacks_id = ipc_callbacks_id_;
- params.ipc_cursor_id = ipc_cursor_id_;
- params.keys = msgKeys;
- params.primary_keys = msgPrimaryKeys;
- params.values = values;
- dispatcher_host_->Send(
- new IndexedDBMsg_CallbacksSuccessCursorPrefetch(params));
+ scoped_ptr<IndexedDBMsg_CallbacksSuccessCursorPrefetch_Params> params(
+ new IndexedDBMsg_CallbacksSuccessCursorPrefetch_Params());
+ params->ipc_thread_id = ipc_thread_id_;
+ params->ipc_callbacks_id = ipc_callbacks_id_;
+ params->ipc_cursor_id = ipc_cursor_id_;
+ params->keys = msgKeys;
+ params->primary_keys = msgPrimaryKeys;
+ std::vector<std::string>& values_bits = params->values;
+ values_bits.resize(values.size());
+ std::vector<std::vector<IndexedDBMsg_BlobOrFileInfo> >& values_blob_infos =
+ params->blob_or_file_infos;
+ values_blob_infos.resize(values.size());
+
+ bool found_blob_info = false;
+ std::vector<IndexedDBValue>::iterator iter = values.begin();
+ for (size_t i = 0; iter != values.end(); ++iter, ++i) {
+ values_bits[i].swap(iter->bits);
+ if (iter->blob_info.size()) {
+ found_blob_info = true;
+ FillInBlobData(iter->blob_info, &values_blob_infos[i]);
+ std::vector<IndexedDBBlobInfo>::const_iterator blob_iter;
+ for (blob_iter = iter->blob_info.begin();
+ blob_iter != iter->blob_info.end();
+ ++blob_iter) {
+ if (!blob_iter->mark_used_callback().is_null())
+ blob_iter->mark_used_callback().Run();
+ }
+ }
+ }
+
+ if (found_blob_info) {
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(BlobLookupForCursorPrefetch,
+ base::Owned(params.release()),
+ dispatcher_host_,
+ values));
+ } else {
+ dispatcher_host_->Send(
+ new IndexedDBMsg_CallbacksSuccessCursorPrefetch(*params.get()));
+ }
dispatcher_host_ = NULL;
}
-void IndexedDBCallbacks::OnSuccess(std::string* value,
+void IndexedDBCallbacks::OnSuccess(IndexedDBValue* value,
const IndexedDBKey& key,
const IndexedDBKeyPath& key_path) {
DCHECK(dispatcher_host_.get());
@@ -260,38 +468,63 @@ void IndexedDBCallbacks::OnSuccess(std::string* value,
DCHECK_EQ(kNoTransaction, host_transaction_id_);
DCHECK_EQ(kNoDatabase, ipc_database_id_);
DCHECK_EQ(kNoDatabaseCallbacks, ipc_database_callbacks_id_);
-
- std::string value_copy;
+ DCHECK_EQ(blink::WebIDBDataLossNone, data_loss_);
+
+ scoped_ptr<IndexedDBMsg_CallbacksSuccessValueWithKey_Params> params(
+ new IndexedDBMsg_CallbacksSuccessValueWithKey_Params());
+ params->ipc_thread_id = ipc_thread_id_;
+ params->ipc_callbacks_id = ipc_callbacks_id_;
+ params->primary_key = key;
+ params->key_path = key_path;
if (value && !value->empty())
- std::swap(value_copy, *value);
-
- dispatcher_host_->Send(new IndexedDBMsg_CallbacksSuccessValueWithKey(
- ipc_thread_id_,
- ipc_callbacks_id_,
- // TODO(alecflett): Avoid a copy here.
- value_copy,
- key,
- key_path));
+ std::swap(params->value, value->bits);
+ if (!value || value->blob_info.empty()) {
+ dispatcher_host_->Send(
+ new IndexedDBMsg_CallbacksSuccessValueWithKey(*params));
+ } else {
+ IndexedDBMsg_CallbacksSuccessValueWithKey_Params* p = params.get();
+ FillInBlobData(value->blob_info, &p->blob_or_file_info);
+ RegisterBlobsAndSend(
+ value->blob_info,
+ base::Bind(
+ CreateBlobsAndSend<IndexedDBMsg_CallbacksSuccessValueWithKey_Params,
+ IndexedDBMsg_CallbacksSuccessValueWithKey>,
+ base::Owned(params.release()),
+ dispatcher_host_,
+ value->blob_info,
+ base::Unretained(&p->blob_or_file_info)));
+ }
dispatcher_host_ = NULL;
}
-void IndexedDBCallbacks::OnSuccess(std::string* value) {
+void IndexedDBCallbacks::OnSuccess(IndexedDBValue* value) {
DCHECK(dispatcher_host_.get());
-
DCHECK(kNoCursor == ipc_cursor_id_ || value == NULL);
DCHECK_EQ(kNoTransaction, host_transaction_id_);
DCHECK_EQ(kNoDatabase, ipc_database_id_);
DCHECK_EQ(kNoDatabaseCallbacks, ipc_database_callbacks_id_);
+ DCHECK_EQ(blink::WebIDBDataLossNone, data_loss_);
- std::string value_copy;
+ scoped_ptr<IndexedDBMsg_CallbacksSuccessValue_Params> params(
+ new IndexedDBMsg_CallbacksSuccessValue_Params());
+ params->ipc_thread_id = ipc_thread_id_;
+ params->ipc_callbacks_id = ipc_callbacks_id_;
if (value && !value->empty())
- std::swap(value_copy, *value);
-
- dispatcher_host_->Send(new IndexedDBMsg_CallbacksSuccessValue(
- ipc_thread_id_,
- ipc_callbacks_id_,
- // TODO(alecflett): avoid a copy here.
- value_copy));
+ std::swap(params->value, value->bits);
+ if (!value || value->blob_info.empty()) {
+ dispatcher_host_->Send(new IndexedDBMsg_CallbacksSuccessValue(*params));
+ } else {
+ IndexedDBMsg_CallbacksSuccessValue_Params* p = params.get();
+ FillInBlobData(value->blob_info, &p->blob_or_file_info);
+ RegisterBlobsAndSend(
+ value->blob_info,
+ base::Bind(CreateBlobsAndSend<IndexedDBMsg_CallbacksSuccessValue_Params,
+ IndexedDBMsg_CallbacksSuccessValue>,
+ base::Owned(params.release()),
+ dispatcher_host_,
+ value->blob_info,
+ base::Unretained(&p->blob_or_file_info)));
+ }
dispatcher_host_ = NULL;
}
@@ -302,6 +535,7 @@ void IndexedDBCallbacks::OnSuccess(const IndexedDBKey& value) {
DCHECK_EQ(kNoTransaction, host_transaction_id_);
DCHECK_EQ(kNoDatabase, ipc_database_id_);
DCHECK_EQ(kNoDatabaseCallbacks, ipc_database_callbacks_id_);
+ DCHECK_EQ(blink::WebIDBDataLossNone, data_loss_);
dispatcher_host_->Send(new IndexedDBMsg_CallbacksSuccessIndexedDBKey(
ipc_thread_id_, ipc_callbacks_id_, value));
@@ -315,6 +549,7 @@ void IndexedDBCallbacks::OnSuccess(int64 value) {
DCHECK_EQ(kNoTransaction, host_transaction_id_);
DCHECK_EQ(kNoDatabase, ipc_database_id_);
DCHECK_EQ(kNoDatabaseCallbacks, ipc_database_callbacks_id_);
+ DCHECK_EQ(blink::WebIDBDataLossNone, data_loss_);
dispatcher_host_->Send(new IndexedDBMsg_CallbacksSuccessInteger(
ipc_thread_id_, ipc_callbacks_id_, value));
@@ -328,6 +563,7 @@ void IndexedDBCallbacks::OnSuccess() {
DCHECK_EQ(kNoTransaction, host_transaction_id_);
DCHECK_EQ(kNoDatabase, ipc_database_id_);
DCHECK_EQ(kNoDatabaseCallbacks, ipc_database_callbacks_id_);
+ DCHECK_EQ(blink::WebIDBDataLossNone, data_loss_);
dispatcher_host_->Send(new IndexedDBMsg_CallbacksSuccessUndefined(
ipc_thread_id_, ipc_callbacks_id_));
diff --git a/chromium/content/browser/indexed_db/indexed_db_callbacks.h b/chromium/content/browser/indexed_db/indexed_db_callbacks.h
index 048b563bf2f..8041fb8beae 100644
--- a/chromium/content/browser/indexed_db/indexed_db_callbacks.h
+++ b/chromium/content/browser/indexed_db/indexed_db_callbacks.h
@@ -20,11 +20,13 @@
#include "url/gurl.h"
namespace content {
+class IndexedDBBlobInfo;
class IndexedDBConnection;
class IndexedDBCursor;
class IndexedDBDatabase;
class IndexedDBDatabaseCallbacks;
struct IndexedDBDatabaseMetadata;
+struct IndexedDBValue;
class CONTENT_EXPORT IndexedDBCallbacks
: public base::RefCounted<IndexedDBCallbacks> {
@@ -57,12 +59,12 @@ class CONTENT_EXPORT IndexedDBCallbacks
virtual void OnBlocked(int64 existing_version);
// IndexedDBFactory::Open
+ virtual void OnDataLoss(blink::WebIDBDataLoss data_loss,
+ std::string data_loss_message);
virtual void OnUpgradeNeeded(
int64 old_version,
scoped_ptr<IndexedDBConnection> connection,
- const content::IndexedDBDatabaseMetadata& metadata,
- blink::WebIDBDataLoss data_loss,
- std::string data_loss_message);
+ const content::IndexedDBDatabaseMetadata& metadata);
virtual void OnSuccess(scoped_ptr<IndexedDBConnection> connection,
const content::IndexedDBDatabaseMetadata& metadata);
@@ -70,41 +72,47 @@ class CONTENT_EXPORT IndexedDBCallbacks
virtual void OnSuccess(scoped_refptr<IndexedDBCursor> cursor,
const IndexedDBKey& key,
const IndexedDBKey& primary_key,
- std::string* value);
+ IndexedDBValue* value);
// IndexedDBCursor::Continue / Advance
virtual void OnSuccess(const IndexedDBKey& key,
const IndexedDBKey& primary_key,
- std::string* value);
+ IndexedDBValue* value);
// IndexedDBCursor::PrefetchContinue
virtual void OnSuccessWithPrefetch(
const std::vector<IndexedDBKey>& keys,
const std::vector<IndexedDBKey>& primary_keys,
- const std::vector<std::string>& values);
+ std::vector<IndexedDBValue>& values);
// IndexedDBDatabase::Get (with key injection)
- virtual void OnSuccess(std::string* data,
+ virtual void OnSuccess(IndexedDBValue* value,
const IndexedDBKey& key,
const IndexedDBKeyPath& key_path);
// IndexedDBDatabase::Get
- virtual void OnSuccess(std::string* value);
+ virtual void OnSuccess(IndexedDBValue* value);
// IndexedDBDatabase::Put / IndexedDBCursor::Update
- virtual void OnSuccess(const IndexedDBKey& value);
+ virtual void OnSuccess(const IndexedDBKey& key);
// IndexedDBDatabase::Count
+ // IndexedDBFactory::DeleteDatabase
virtual void OnSuccess(int64 value);
// IndexedDBDatabase::Delete
// IndexedDBCursor::Continue / Advance (when complete)
virtual void OnSuccess();
+ blink::WebIDBDataLoss data_loss() const { return data_loss_; }
+
protected:
virtual ~IndexedDBCallbacks();
private:
+ void RegisterBlobsAndSend(const std::vector<IndexedDBBlobInfo>& blob_info,
+ const base::Closure& callback);
+
friend class base::RefCounted<IndexedDBCallbacks>;
// Originally from IndexedDBCallbacks:
@@ -120,6 +128,12 @@ class CONTENT_EXPORT IndexedDBCallbacks
GURL origin_url_;
int32 ipc_database_id_;
int32 ipc_database_callbacks_id_;
+
+ // Stored in OnDataLoss, merged with OnUpgradeNeeded response.
+ blink::WebIDBDataLoss data_loss_;
+ std::string data_loss_message_;
+
+ DISALLOW_COPY_AND_ASSIGN(IndexedDBCallbacks);
};
} // namespace content
diff --git a/chromium/content/browser/indexed_db/indexed_db_class_factory.cc b/chromium/content/browser/indexed_db/indexed_db_class_factory.cc
new file mode 100644
index 00000000000..a2a133689dd
--- /dev/null
+++ b/chromium/content/browser/indexed_db/indexed_db_class_factory.cc
@@ -0,0 +1,30 @@
+// 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/indexed_db/indexed_db_class_factory.h"
+#include "content/browser/indexed_db/leveldb/leveldb_transaction.h"
+
+namespace content {
+
+static IndexedDBClassFactory::GetterCallback* s_factory_getter;
+static ::base::LazyInstance<IndexedDBClassFactory>::Leaky s_factory =
+ LAZY_INSTANCE_INITIALIZER;
+
+void IndexedDBClassFactory::SetIndexedDBClassFactoryGetter(GetterCallback* cb) {
+ s_factory_getter = cb;
+}
+
+IndexedDBClassFactory* IndexedDBClassFactory::Get() {
+ if (s_factory_getter)
+ return (*s_factory_getter)();
+ else
+ return s_factory.Pointer();
+}
+
+LevelDBTransaction* IndexedDBClassFactory::CreateLevelDBTransaction(
+ LevelDBDatabase* db) {
+ return new LevelDBTransaction(db);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/indexed_db/indexed_db_class_factory.h b/chromium/content/browser/indexed_db/indexed_db_class_factory.h
new file mode 100644
index 00000000000..eb3c6566463
--- /dev/null
+++ b/chromium/content/browser/indexed_db/indexed_db_class_factory.h
@@ -0,0 +1,36 @@
+// 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_INDEXED_DB_INDEXED_DB_CLASS_FACTORY_H_
+#define CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_CLASS_FACTORY_H_
+
+#include "base/lazy_instance.h"
+#include "content/common/content_export.h"
+
+namespace content {
+
+class LevelDBDatabase;
+class LevelDBTransaction;
+
+// Use this factory to create some IndexedDB objects. Exists solely to
+// facilitate tests which sometimes need to inject mock objects into the system.
+class CONTENT_EXPORT IndexedDBClassFactory {
+ public:
+ typedef IndexedDBClassFactory* GetterCallback();
+
+ static IndexedDBClassFactory* Get();
+
+ static void SetIndexedDBClassFactoryGetter(GetterCallback* cb);
+
+ virtual LevelDBTransaction* CreateLevelDBTransaction(LevelDBDatabase* db);
+
+ protected:
+ IndexedDBClassFactory() {}
+ virtual ~IndexedDBClassFactory() {}
+ friend struct base::DefaultLazyInstanceTraits<IndexedDBClassFactory>;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_CLASS_FACTORY_H_
diff --git a/chromium/content/browser/indexed_db/indexed_db_cleanup_on_io_error_unittest.cc b/chromium/content/browser/indexed_db/indexed_db_cleanup_on_io_error_unittest.cc
index d9445415959..49d04362d89 100644
--- a/chromium/content/browser/indexed_db/indexed_db_cleanup_on_io_error_unittest.cc
+++ b/chromium/content/browser/indexed_db/indexed_db_cleanup_on_io_error_unittest.cc
@@ -4,6 +4,7 @@
#include <cerrno>
+#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/strings/string16.h"
@@ -20,22 +21,37 @@ using content::LevelDBDatabase;
using content::LevelDBFactory;
using content::LevelDBSnapshot;
+namespace base {
+class TaskRunner;
+}
+
+namespace content {
+class IndexedDBFactory;
+}
+
+namespace net {
+class URLRequestContext;
+}
+
namespace {
class BustedLevelDBDatabase : public LevelDBDatabase {
public:
+ BustedLevelDBDatabase() {}
static scoped_ptr<LevelDBDatabase> Open(
const base::FilePath& file_name,
const LevelDBComparator* /*comparator*/) {
return scoped_ptr<LevelDBDatabase>(new BustedLevelDBDatabase);
}
- virtual bool Get(const base::StringPiece& key,
- std::string* value,
- bool* found,
- const LevelDBSnapshot* = 0) OVERRIDE {
- // false means IO error.
- return false;
+ virtual leveldb::Status Get(const base::StringPiece& key,
+ std::string* value,
+ bool* found,
+ const LevelDBSnapshot* = 0) OVERRIDE {
+ return leveldb::Status::IOError("It's busted!");
}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BustedLevelDBDatabase);
};
class MockLevelDBFactory : public LevelDBFactory {
@@ -49,34 +65,46 @@ class MockLevelDBFactory : public LevelDBFactory {
*db = BustedLevelDBDatabase::Open(file_name, comparator);
return leveldb::Status::OK();
}
- virtual bool DestroyLevelDB(const base::FilePath& file_name) OVERRIDE {
+ virtual leveldb::Status DestroyLevelDB(const base::FilePath& file_name)
+ OVERRIDE {
EXPECT_FALSE(destroy_called_);
destroy_called_ = true;
- return false;
+ return leveldb::Status::IOError("error");
}
virtual ~MockLevelDBFactory() { EXPECT_TRUE(destroy_called_); }
private:
bool destroy_called_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockLevelDBFactory);
};
TEST(IndexedDBIOErrorTest, CleanUpTest) {
+ content::IndexedDBFactory* factory = NULL;
const GURL origin("http://localhost:81");
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
const base::FilePath path = temp_directory.path();
+ net::URLRequestContext* request_context = NULL;
MockLevelDBFactory mock_leveldb_factory;
blink::WebIDBDataLoss data_loss =
blink::WebIDBDataLossNone;
std::string data_loss_message;
bool disk_full = false;
+ base::TaskRunner* task_runner = NULL;
+ bool clean_journal = false;
scoped_refptr<IndexedDBBackingStore> backing_store =
- IndexedDBBackingStore::Open(origin,
+ IndexedDBBackingStore::Open(factory,
+ origin,
path,
+ request_context,
&data_loss,
&data_loss_message,
&disk_full,
- &mock_leveldb_factory);
+ &mock_leveldb_factory,
+ task_runner,
+ clean_journal);
}
// TODO(dgrogan): Remove expect_destroy if we end up not using it again. It is
@@ -96,10 +124,11 @@ class MockErrorLevelDBFactory : public LevelDBFactory {
return MakeIOError(
"some filename", "some message", leveldb_env::kNewLogger, error_);
}
- virtual bool DestroyLevelDB(const base::FilePath& file_name) OVERRIDE {
+ virtual leveldb::Status DestroyLevelDB(const base::FilePath& file_name)
+ OVERRIDE {
EXPECT_FALSE(destroy_called_);
destroy_called_ = true;
- return false;
+ return leveldb::Status::IOError("error");
}
virtual ~MockErrorLevelDBFactory() {
EXPECT_EQ(expect_destroy_, destroy_called_);
@@ -109,10 +138,14 @@ class MockErrorLevelDBFactory : public LevelDBFactory {
T error_;
bool expect_destroy_;
bool destroy_called_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockErrorLevelDBFactory);
};
TEST(IndexedDBNonRecoverableIOErrorTest, NuancedCleanupTest) {
+ content::IndexedDBFactory* factory = NULL;
const GURL origin("http://localhost:81");
+ net::URLRequestContext* request_context = NULL;
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
const base::FilePath path = temp_directory.path();
@@ -120,44 +153,62 @@ TEST(IndexedDBNonRecoverableIOErrorTest, NuancedCleanupTest) {
blink::WebIDBDataLossNone;
std::string data_loss_reason;
bool disk_full = false;
+ base::TaskRunner* task_runner = NULL;
+ bool clean_journal = false;
MockErrorLevelDBFactory<int> mock_leveldb_factory(ENOSPC, false);
scoped_refptr<IndexedDBBackingStore> backing_store =
- IndexedDBBackingStore::Open(origin,
+ IndexedDBBackingStore::Open(factory,
+ origin,
path,
+ request_context,
&data_loss,
&data_loss_reason,
&disk_full,
- &mock_leveldb_factory);
+ &mock_leveldb_factory,
+ task_runner,
+ clean_journal);
- MockErrorLevelDBFactory<base::PlatformFileError> mock_leveldb_factory2(
- base::PLATFORM_FILE_ERROR_NO_MEMORY, false);
+ MockErrorLevelDBFactory<base::File::Error> mock_leveldb_factory2(
+ base::File::FILE_ERROR_NO_MEMORY, false);
scoped_refptr<IndexedDBBackingStore> backing_store2 =
- IndexedDBBackingStore::Open(origin,
+ IndexedDBBackingStore::Open(factory,
+ origin,
path,
+ request_context,
&data_loss,
&data_loss_reason,
&disk_full,
- &mock_leveldb_factory2);
+ &mock_leveldb_factory2,
+ task_runner,
+ clean_journal);
MockErrorLevelDBFactory<int> mock_leveldb_factory3(EIO, false);
scoped_refptr<IndexedDBBackingStore> backing_store3 =
- IndexedDBBackingStore::Open(origin,
+ IndexedDBBackingStore::Open(factory,
+ origin,
path,
+ request_context,
&data_loss,
&data_loss_reason,
&disk_full,
- &mock_leveldb_factory3);
+ &mock_leveldb_factory3,
+ task_runner,
+ clean_journal);
- MockErrorLevelDBFactory<base::PlatformFileError> mock_leveldb_factory4(
- base::PLATFORM_FILE_ERROR_FAILED, false);
+ MockErrorLevelDBFactory<base::File::Error> mock_leveldb_factory4(
+ base::File::FILE_ERROR_FAILED, false);
scoped_refptr<IndexedDBBackingStore> backing_store4 =
- IndexedDBBackingStore::Open(origin,
+ IndexedDBBackingStore::Open(factory,
+ origin,
path,
+ request_context,
&data_loss,
&data_loss_reason,
&disk_full,
- &mock_leveldb_factory4);
+ &mock_leveldb_factory4,
+ task_runner,
+ clean_journal);
}
} // namespace
diff --git a/chromium/content/browser/indexed_db/indexed_db_connection.cc b/chromium/content/browser/indexed_db/indexed_db_connection.cc
index 81139895366..0669e14c0d0 100644
--- a/chromium/content/browser/indexed_db/indexed_db_connection.cc
+++ b/chromium/content/browser/indexed_db/indexed_db_connection.cc
@@ -34,4 +34,4 @@ bool IndexedDBConnection::IsConnected() {
return database_.get() != NULL;
}
-} // namespace blink
+} // namespace content
diff --git a/chromium/content/browser/indexed_db/indexed_db_connection.h b/chromium/content/browser/indexed_db/indexed_db_connection.h
index e4ed928257d..95d1f2e86d3 100644
--- a/chromium/content/browser/indexed_db/indexed_db_connection.h
+++ b/chromium/content/browser/indexed_db/indexed_db_connection.h
@@ -34,6 +34,8 @@ class CONTENT_EXPORT IndexedDBConnection {
// The callbacks_ member is cleared when the connection is closed.
// May be NULL in unit tests.
scoped_refptr<IndexedDBDatabaseCallbacks> callbacks_;
+
+ DISALLOW_COPY_AND_ASSIGN(IndexedDBConnection);
};
} // namespace content
diff --git a/chromium/content/browser/indexed_db/indexed_db_context_impl.cc b/chromium/content/browser/indexed_db/indexed_db_context_impl.cc
index e8ab70bbbf4..c2bd0afa62c 100644
--- a/chromium/content/browser/indexed_db/indexed_db_context_impl.cc
+++ b/chromium/content/browser/indexed_db/indexed_db_context_impl.cc
@@ -5,12 +5,14 @@
#include "content/browser/indexed_db/indexed_db_context_impl.h"
#include <algorithm>
+#include <utility>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/files/file_enumerator.h"
#include "base/logging.h"
+#include "base/metrics/histogram.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
@@ -30,7 +32,7 @@
#include "content/public/common/content_switches.h"
#include "ui/base/text/bytes_formatting.h"
#include "webkit/browser/database/database_util.h"
-#include "webkit/browser/quota/quota_manager.h"
+#include "webkit/browser/quota/quota_manager_proxy.h"
#include "webkit/browser/quota/special_storage_policy.h"
#include "webkit/common/database/database_identifier.h"
@@ -162,19 +164,19 @@ static bool HostNameComparator(const GURL& i, const GURL& j) {
return i.host() < j.host();
}
-ListValue* IndexedDBContextImpl::GetAllOriginsDetails() {
+base::ListValue* IndexedDBContextImpl::GetAllOriginsDetails() {
DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
std::vector<GURL> origins = GetAllOrigins();
std::sort(origins.begin(), origins.end(), HostNameComparator);
- scoped_ptr<ListValue> list(new ListValue());
+ scoped_ptr<base::ListValue> list(new base::ListValue());
for (std::vector<GURL>::const_iterator iter = origins.begin();
iter != origins.end();
++iter) {
const GURL& origin_url = *iter;
- scoped_ptr<DictionaryValue> info(new DictionaryValue());
+ scoped_ptr<base::DictionaryValue> info(new base::DictionaryValue());
info->SetString("url", origin_url.spec());
info->SetString("size", ui::FormatBytes(GetOriginDiskUsage(origin_url)));
info->SetDouble("last_modified",
@@ -187,17 +189,17 @@ ListValue* IndexedDBContextImpl::GetAllOriginsDetails() {
// origins in the outer loop.
if (factory_) {
- std::vector<IndexedDBDatabase*> databases =
+ std::pair<IndexedDBFactory::OriginDBMapIterator,
+ IndexedDBFactory::OriginDBMapIterator> range =
factory_->GetOpenDatabasesForOrigin(origin_url);
// TODO(jsbell): Sort by name?
- scoped_ptr<ListValue> database_list(new ListValue());
+ scoped_ptr<base::ListValue> database_list(new base::ListValue());
- for (std::vector<IndexedDBDatabase*>::iterator it = databases.begin();
- it != databases.end();
+ for (IndexedDBFactory::OriginDBMapIterator it = range.first;
+ it != range.second;
++it) {
-
- const IndexedDBDatabase* db = *it;
- scoped_ptr<DictionaryValue> db_info(new DictionaryValue());
+ const IndexedDBDatabase* db = it->second;
+ scoped_ptr<base::DictionaryValue> db_info(new base::DictionaryValue());
db_info->SetString("name", db->name());
db_info->SetDouble("pending_opens", db->PendingOpenCount());
@@ -208,16 +210,16 @@ ListValue* IndexedDBContextImpl::GetAllOriginsDetails() {
db->ConnectionCount() - db->PendingUpgradeCount() -
db->RunningUpgradeCount());
- scoped_ptr<ListValue> transaction_list(new ListValue());
+ scoped_ptr<base::ListValue> transaction_list(new base::ListValue());
std::vector<const IndexedDBTransaction*> transactions =
db->transaction_coordinator().GetTransactions();
for (std::vector<const IndexedDBTransaction*>::iterator trans_it =
transactions.begin();
trans_it != transactions.end();
++trans_it) {
-
const IndexedDBTransaction* transaction = *trans_it;
- scoped_ptr<DictionaryValue> transaction_info(new DictionaryValue());
+ scoped_ptr<base::DictionaryValue> transaction_info(
+ new base::DictionaryValue());
const char* kModes[] = { "readonly", "readwrite", "versionchange" };
transaction_info->SetString("mode", kModes[transaction->mode()]);
@@ -231,6 +233,9 @@ ListValue* IndexedDBContextImpl::GetAllOriginsDetails() {
else
transaction_info->SetString("status", "started");
break;
+ case IndexedDBTransaction::COMMITTING:
+ transaction_info->SetString("status", "committing");
+ break;
case IndexedDBTransaction::FINISHED:
transaction_info->SetString("status", "finished");
break;
@@ -257,7 +262,7 @@ ListValue* IndexedDBContextImpl::GetAllOriginsDetails() {
transaction_info->SetDouble(
"tasks_completed", transaction->diagnostics().tasks_completed);
- scoped_ptr<ListValue> scope(new ListValue());
+ scoped_ptr<base::ListValue> scope(new base::ListValue());
for (std::set<int64>::const_iterator scope_it =
transaction->scope().begin();
scope_it != transaction->scope().end();
@@ -296,7 +301,7 @@ base::Time IndexedDBContextImpl::GetOriginLastModified(const GURL& origin_url) {
if (data_path_.empty() || !IsInOriginSet(origin_url))
return base::Time();
base::FilePath idb_directory = GetFilePath(origin_url);
- base::PlatformFileInfo file_info;
+ base::File::Info file_info;
if (!base::GetFileInfo(idb_directory, &file_info))
return base::Time();
return file_info.last_modified;
@@ -304,14 +309,14 @@ base::Time IndexedDBContextImpl::GetOriginLastModified(const GURL& origin_url) {
void IndexedDBContextImpl::DeleteForOrigin(const GURL& origin_url) {
DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
- ForceClose(origin_url);
+ ForceClose(origin_url, FORCE_CLOSE_DELETE_ORIGIN);
if (data_path_.empty() || !IsInOriginSet(origin_url))
return;
base::FilePath idb_directory = GetFilePath(origin_url);
EnsureDiskUsageCacheInitialized(origin_url);
- bool deleted = LevelDBDatabase::Destroy(idb_directory);
- if (!deleted) {
+ leveldb::Status s = LevelDBDatabase::Destroy(idb_directory);
+ if (!s.ok()) {
LOG(WARNING) << "Failed to delete LevelDB database: "
<< idb_directory.AsUTF8Unsafe();
} else {
@@ -323,33 +328,26 @@ void IndexedDBContextImpl::DeleteForOrigin(const GURL& origin_url) {
}
QueryDiskAndUpdateQuotaUsage(origin_url);
- if (deleted) {
+ if (s.ok()) {
RemoveFromOriginSet(origin_url);
origin_size_map_.erase(origin_url);
space_available_map_.erase(origin_url);
}
}
-void IndexedDBContextImpl::ForceClose(const GURL origin_url) {
+void IndexedDBContextImpl::ForceClose(const GURL origin_url,
+ ForceCloseReason reason) {
DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
+ UMA_HISTOGRAM_ENUMERATION("WebCore.IndexedDB.Context.ForceCloseReason",
+ reason,
+ FORCE_CLOSE_REASON_MAX);
+
if (data_path_.empty() || !IsInOriginSet(origin_url))
return;
- if (connections_.find(origin_url) != connections_.end()) {
- ConnectionSet& connections = connections_[origin_url];
- ConnectionSet::iterator it = connections.begin();
- while (it != connections.end()) {
- // Remove before closing so callbacks don't double-erase
- IndexedDBConnection* connection = *it;
- DCHECK(connection->IsConnected());
- connections.erase(it++);
- connection->ForceClose();
- }
- DCHECK_EQ(connections_[origin_url].size(), 0UL);
- connections_.erase(origin_url);
- }
if (factory_)
factory_->ForceClose(origin_url);
+ DCHECK_EQ(0UL, GetConnectionCount(origin_url));
}
size_t IndexedDBContextImpl::GetConnectionCount(const GURL& origin_url) {
@@ -357,10 +355,10 @@ size_t IndexedDBContextImpl::GetConnectionCount(const GURL& origin_url) {
if (data_path_.empty() || !IsInOriginSet(origin_url))
return 0;
- if (connections_.find(origin_url) == connections_.end())
+ if (!factory_)
return 0;
- return connections_[origin_url].size();
+ return factory_->GetConnectionCount(origin_url);
}
base::FilePath IndexedDBContextImpl::GetFilePath(const GURL& origin_url) const {
@@ -382,14 +380,12 @@ void IndexedDBContextImpl::SetTaskRunnerForTesting(
void IndexedDBContextImpl::ConnectionOpened(const GURL& origin_url,
IndexedDBConnection* connection) {
DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
- DCHECK_EQ(connections_[origin_url].count(connection), 0UL);
if (quota_manager_proxy()) {
quota_manager_proxy()->NotifyStorageAccessed(
quota::QuotaClient::kIndexedDatabase,
origin_url,
quota::kStorageTypeTemporary);
}
- connections_[origin_url].insert(connection);
if (AddToOriginSet(origin_url)) {
// A newly created db, notify the quota system.
QueryDiskAndUpdateQuotaUsage(origin_url);
@@ -402,26 +398,24 @@ void IndexedDBContextImpl::ConnectionOpened(const GURL& origin_url,
void IndexedDBContextImpl::ConnectionClosed(const GURL& origin_url,
IndexedDBConnection* connection) {
DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
- // May not be in the map if connection was forced to close
- if (connections_.find(origin_url) == connections_.end() ||
- connections_[origin_url].count(connection) != 1)
- return;
if (quota_manager_proxy()) {
quota_manager_proxy()->NotifyStorageAccessed(
quota::QuotaClient::kIndexedDatabase,
origin_url,
quota::kStorageTypeTemporary);
}
- connections_[origin_url].erase(connection);
- if (connections_[origin_url].size() == 0) {
+ if (factory_ && factory_->GetConnectionCount(origin_url) == 0)
QueryDiskAndUpdateQuotaUsage(origin_url);
- connections_.erase(origin_url);
- }
}
void IndexedDBContextImpl::TransactionComplete(const GURL& origin_url) {
- DCHECK(connections_.find(origin_url) != connections_.end() &&
- connections_[origin_url].size() > 0);
+ DCHECK(!factory_ || factory_->GetConnectionCount(origin_url) > 0);
+ QueryDiskAndUpdateQuotaUsage(origin_url);
+ QueryAvailableQuota(origin_url);
+}
+
+void IndexedDBContextImpl::DatabaseDeleted(const GURL& origin_url) {
+ AddToOriginSet(origin_url);
QueryDiskAndUpdateQuotaUsage(origin_url);
QueryAvailableQuota(origin_url);
}
@@ -462,7 +456,7 @@ IndexedDBContextImpl::~IndexedDBContextImpl() {
special_storage_policy_ &&
special_storage_policy_->HasSessionOnlyOrigins();
- // Clearning only session-only databases, and there are none.
+ // Clearing only session-only databases, and there are none.
if (!has_session_only_databases)
return;
diff --git a/chromium/content/browser/indexed_db/indexed_db_context_impl.h b/chromium/content/browser/indexed_db/indexed_db_context_impl.h
index d6b12429440..b3ec9850fdd 100644
--- a/chromium/content/browser/indexed_db/indexed_db_context_impl.h
+++ b/chromium/content/browser/indexed_db/indexed_db_context_impl.h
@@ -7,6 +7,7 @@
#include <map>
#include <set>
+#include <string>
#include <vector>
#include "base/compiler_specific.h"
@@ -67,6 +68,7 @@ class CONTENT_EXPORT IndexedDBContextImpl
void ConnectionOpened(const GURL& origin_url, IndexedDBConnection* db);
void ConnectionClosed(const GURL& origin_url, IndexedDBConnection* db);
void TransactionComplete(const GURL& origin_url);
+ void DatabaseDeleted(const GURL& origin_url);
bool WouldBeOverQuota(const GURL& origin_url, int64 additional_bytes);
bool IsOverQuota(const GURL& origin_url);
@@ -75,9 +77,19 @@ class CONTENT_EXPORT IndexedDBContextImpl
std::vector<GURL> GetAllOrigins();
base::Time GetOriginLastModified(const GURL& origin_url);
base::ListValue* GetAllOriginsDetails();
+
+ // Recorded in histograms, so append only.
+ enum ForceCloseReason {
+ FORCE_CLOSE_DELETE_ORIGIN = 0,
+ FORCE_CLOSE_BACKING_STORE_FAILURE,
+ FORCE_CLOSE_INTERNALS_PAGE,
+ FORCE_CLOSE_REASON_MAX
+ };
+
// ForceClose takes a value rather than a reference since it may release the
// owning object.
- void ForceClose(const GURL origin_url);
+ void ForceClose(const GURL origin_url, ForceCloseReason reason);
+
base::FilePath GetFilePath(const GURL& origin_url) const;
base::FilePath data_path() const { return data_path_; }
bool IsInOriginSet(const GURL& origin_url) {
@@ -136,8 +148,6 @@ class CONTENT_EXPORT IndexedDBContextImpl
scoped_ptr<std::set<GURL> > origin_set_;
OriginToSizeMap origin_size_map_;
OriginToSizeMap space_available_map_;
- typedef std::set<IndexedDBConnection*> ConnectionSet;
- std::map<GURL, ConnectionSet> connections_;
DISALLOW_COPY_AND_ASSIGN(IndexedDBContextImpl);
};
diff --git a/chromium/content/browser/indexed_db/indexed_db_cursor.cc b/chromium/content/browser/indexed_db/indexed_db_cursor.cc
index 6d7bc1c2d59..7715a998a75 100644
--- a/chromium/content/browser/indexed_db/indexed_db_cursor.cc
+++ b/chromium/content/browser/indexed_db/indexed_db_cursor.cc
@@ -4,12 +4,15 @@
#include "content/browser/indexed_db/indexed_db_cursor.h"
+#include <vector>
+
#include "base/bind.h"
#include "base/logging.h"
#include "content/browser/indexed_db/indexed_db_callbacks.h"
#include "content/browser/indexed_db/indexed_db_database_error.h"
#include "content/browser/indexed_db/indexed_db_tracing.h"
#include "content/browser/indexed_db/indexed_db_transaction.h"
+#include "content/browser/indexed_db/indexed_db_value.h"
namespace content {
@@ -59,9 +62,13 @@ void IndexedDBCursor::CursorAdvanceOperation(
scoped_refptr<IndexedDBCallbacks> callbacks,
IndexedDBTransaction* /*transaction*/) {
IDB_TRACE("IndexedDBCursor::CursorAdvanceOperation");
- if (!cursor_ || !cursor_->Advance(count)) {
+ leveldb::Status s;
+ // TODO(cmumford): Handle this error (crbug.com/363397). Although this will
+ // properly fail, caller will not know why, and any corruption
+ // will be ignored.
+ if (!cursor_ || !cursor_->Advance(count, &s)) {
cursor_.reset();
- callbacks->OnSuccess(static_cast<std::string*>(NULL));
+ callbacks->OnSuccess(static_cast<IndexedDBValue*>(NULL));
return;
}
@@ -74,11 +81,16 @@ void IndexedDBCursor::CursorIterationOperation(
scoped_refptr<IndexedDBCallbacks> callbacks,
IndexedDBTransaction* /*transaction*/) {
IDB_TRACE("IndexedDBCursor::CursorIterationOperation");
- if (!cursor_ ||
- !cursor_->Continue(
- key.get(), primary_key.get(), IndexedDBBackingStore::Cursor::SEEK)) {
+ leveldb::Status s;
+ // TODO(cmumford): Handle this error (crbug.com/363397). Although this will
+ // properly fail, caller will not know why, and any corruption
+ // will be ignored.
+ if (!cursor_ || !cursor_->Continue(key.get(),
+ primary_key.get(),
+ IndexedDBBackingStore::Cursor::SEEK,
+ &s) || !s.ok()) {
cursor_.reset();
- callbacks->OnSuccess(static_cast<std::string*>(NULL));
+ callbacks->OnSuccess(static_cast<IndexedDBValue*>(NULL));
return;
}
@@ -106,30 +118,39 @@ void IndexedDBCursor::CursorPrefetchIterationOperation(
std::vector<IndexedDBKey> found_keys;
std::vector<IndexedDBKey> found_primary_keys;
- std::vector<std::string> found_values;
+ std::vector<IndexedDBValue> found_values;
- if (cursor_)
- saved_cursor_.reset(cursor_->Clone());
+ saved_cursor_.reset();
const size_t max_size_estimate = 10 * 1024 * 1024;
size_t size_estimate = 0;
+ leveldb::Status s;
+ // TODO(cmumford): Handle this error (crbug.com/363397). Although this will
+ // properly fail, caller will not know why, and any corruption
+ // will be ignored.
for (int i = 0; i < number_to_fetch; ++i) {
- if (!cursor_ || !cursor_->Continue()) {
+ if (!cursor_ || !cursor_->Continue(&s)) {
cursor_.reset();
break;
}
+ if (i == 0) {
+ // First prefetched result is always used, so that's the position
+ // a cursor should be reset to if the prefetch is invalidated.
+ saved_cursor_.reset(cursor_->Clone());
+ }
+
found_keys.push_back(cursor_->key());
found_primary_keys.push_back(cursor_->primary_key());
switch (cursor_type_) {
case indexed_db::CURSOR_KEY_ONLY:
- found_values.push_back(std::string());
+ found_values.push_back(IndexedDBValue());
break;
case indexed_db::CURSOR_KEY_AND_VALUE: {
- std::string value;
+ IndexedDBValue value;
value.swap(*cursor_->value());
- size_estimate += value.size();
+ size_estimate += value.SizeEstimate();
found_values.push_back(value);
break;
}
@@ -144,7 +165,7 @@ void IndexedDBCursor::CursorPrefetchIterationOperation(
}
if (!found_keys.size()) {
- callbacks->OnSuccess(static_cast<std::string*>(NULL));
+ callbacks->OnSuccess(static_cast<IndexedDBValue*>(NULL));
return;
}
@@ -152,19 +173,25 @@ void IndexedDBCursor::CursorPrefetchIterationOperation(
found_keys, found_primary_keys, found_values);
}
-void IndexedDBCursor::PrefetchReset(int used_prefetches, int) {
+leveldb::Status IndexedDBCursor::PrefetchReset(int used_prefetches,
+ int /* unused_prefetches */) {
IDB_TRACE("IndexedDBCursor::PrefetchReset");
cursor_.swap(saved_cursor_);
saved_cursor_.reset();
+ leveldb::Status s;
if (closed_)
- return;
+ return s;
if (cursor_) {
- for (int i = 0; i < used_prefetches; ++i) {
- bool ok = cursor_->Continue();
+ // First prefetched result is always used.
+ DCHECK_GT(used_prefetches, 0);
+ for (int i = 0; i < used_prefetches - 1; ++i) {
+ bool ok = cursor_->Continue(&s);
DCHECK(ok);
}
}
+
+ return s;
}
void IndexedDBCursor::Close() {
diff --git a/chromium/content/browser/indexed_db/indexed_db_cursor.h b/chromium/content/browser/indexed_db/indexed_db_cursor.h
index f1afc186a78..7c1491aff7f 100644
--- a/chromium/content/browser/indexed_db/indexed_db_cursor.h
+++ b/chromium/content/browser/indexed_db/indexed_db_cursor.h
@@ -32,11 +32,11 @@ class CONTENT_EXPORT IndexedDBCursor
scoped_refptr<IndexedDBCallbacks> callbacks);
void PrefetchContinue(int number_to_fetch,
scoped_refptr<IndexedDBCallbacks> callbacks);
- void PrefetchReset(int used_prefetches, int unused_prefetches);
+ leveldb::Status PrefetchReset(int used_prefetches, int unused_prefetches);
const IndexedDBKey& key() const { return cursor_->key(); }
const IndexedDBKey& primary_key() const { return cursor_->primary_key(); }
- std::string* Value() const {
+ IndexedDBValue* Value() const {
return (cursor_type_ == indexed_db::CURSOR_KEY_ONLY) ? NULL
: cursor_->value();
}
@@ -69,6 +69,8 @@ class CONTENT_EXPORT IndexedDBCursor
scoped_ptr<IndexedDBBackingStore::Cursor> saved_cursor_;
bool closed_;
+
+ DISALLOW_COPY_AND_ASSIGN(IndexedDBCursor);
};
} // namespace content
diff --git a/chromium/content/browser/indexed_db/indexed_db_database.cc b/chromium/content/browser/indexed_db/indexed_db_database.cc
index 5b982fa6cf6..cc0738b6d8e 100644
--- a/chromium/content/browser/indexed_db/indexed_db_database.cc
+++ b/chromium/content/browser/indexed_db/indexed_db_database.cc
@@ -10,50 +10,32 @@
#include "base/auto_reset.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
+#include "content/browser/indexed_db/indexed_db_blob_info.h"
#include "content/browser/indexed_db/indexed_db_connection.h"
+#include "content/browser/indexed_db/indexed_db_context_impl.h"
#include "content/browser/indexed_db/indexed_db_cursor.h"
#include "content/browser/indexed_db/indexed_db_factory.h"
#include "content/browser/indexed_db/indexed_db_index_writer.h"
+#include "content/browser/indexed_db/indexed_db_pending_connection.h"
#include "content/browser/indexed_db/indexed_db_tracing.h"
#include "content/browser/indexed_db/indexed_db_transaction.h"
+#include "content/browser/indexed_db/indexed_db_value.h"
#include "content/common/indexed_db/indexed_db_key_path.h"
#include "content/common/indexed_db/indexed_db_key_range.h"
-#include "content/public/browser/browser_thread.h"
#include "third_party/WebKit/public/platform/WebIDBDatabaseException.h"
+#include "third_party/leveldatabase/env_chromium.h"
+#include "webkit/browser/blob/blob_data_handle.h"
+using base::ASCIIToUTF16;
using base::Int64ToString16;
using blink::WebIDBKeyTypeNumber;
namespace content {
-// PendingOpenCall has a scoped_refptr<IndexedDBDatabaseCallbacks> because it
-// isn't a connection yet.
-class IndexedDBDatabase::PendingOpenCall {
- public:
- PendingOpenCall(scoped_refptr<IndexedDBCallbacks> callbacks,
- scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
- int64 transaction_id,
- int64 version)
- : callbacks_(callbacks),
- database_callbacks_(database_callbacks),
- version_(version),
- transaction_id_(transaction_id) {}
- scoped_refptr<IndexedDBCallbacks> Callbacks() { return callbacks_; }
- scoped_refptr<IndexedDBDatabaseCallbacks> DatabaseCallbacks() {
- return database_callbacks_;
- }
- int64 Version() { return version_; }
- int64 TransactionId() const { return transaction_id_; }
-
- private:
- scoped_refptr<IndexedDBCallbacks> callbacks_;
- scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks_;
- int64 version_;
- const int64 transaction_id_;
-};
-
// PendingUpgradeCall has a scoped_ptr<IndexedDBConnection> because it owns the
// in-progress connection.
class IndexedDBDatabase::PendingUpgradeCall {
@@ -66,10 +48,13 @@ class IndexedDBDatabase::PendingUpgradeCall {
connection_(connection.Pass()),
version_(version),
transaction_id_(transaction_id) {}
- scoped_refptr<IndexedDBCallbacks> Callbacks() { return callbacks_; }
- scoped_ptr<IndexedDBConnection> Connection() { return connection_.Pass(); }
- int64 Version() { return version_; }
- int64 TransactionId() const { return transaction_id_; }
+ scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; }
+ // Takes ownership of the connection object.
+ scoped_ptr<IndexedDBConnection> ReleaseConnection() WARN_UNUSED_RESULT {
+ return connection_.Pass();
+ }
+ int64 version() const { return version_; }
+ int64 transaction_id() const { return transaction_id_; }
private:
scoped_refptr<IndexedDBCallbacks> callbacks_;
@@ -87,9 +72,9 @@ class IndexedDBDatabase::PendingSuccessCall {
IndexedDBConnection* connection,
int64 version)
: callbacks_(callbacks), connection_(connection), version_(version) {}
- scoped_refptr<IndexedDBCallbacks> Callbacks() { return callbacks_; }
- IndexedDBConnection* Connection() { return connection_; }
- int64 Version() { return version_; }
+ scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; }
+ IndexedDBConnection* connection() const { return connection_; }
+ int64 version() const { return version_; }
private:
scoped_refptr<IndexedDBCallbacks> callbacks_;
@@ -101,7 +86,7 @@ class IndexedDBDatabase::PendingDeleteCall {
public:
explicit PendingDeleteCall(scoped_refptr<IndexedDBCallbacks> callbacks)
: callbacks_(callbacks) {}
- scoped_refptr<IndexedDBCallbacks> Callbacks() { return callbacks_; }
+ scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; }
private:
scoped_refptr<IndexedDBCallbacks> callbacks_;
@@ -111,21 +96,19 @@ scoped_refptr<IndexedDBDatabase> IndexedDBDatabase::Create(
const base::string16& name,
IndexedDBBackingStore* backing_store,
IndexedDBFactory* factory,
- const Identifier& unique_identifier) {
+ const Identifier& unique_identifier,
+ leveldb::Status* s) {
scoped_refptr<IndexedDBDatabase> database =
new IndexedDBDatabase(name, backing_store, factory, unique_identifier);
- if (!database->OpenInternal())
- return 0;
- return database;
+ *s = database->OpenInternal();
+ if (s->ok())
+ return database;
+ else
+ return NULL;
}
namespace {
const base::string16::value_type kNoStringVersion[] = {0};
-
-template <typename T, typename U>
-bool Contains(const T& container, const U& item) {
- return container.find(item) != container.end();
-}
}
IndexedDBDatabase::IndexedDBDatabase(const base::string16& name,
@@ -139,9 +122,7 @@ IndexedDBDatabase::IndexedDBDatabase(const base::string16& name,
IndexedDBDatabaseMetadata::NO_INT_VERSION,
kInvalidId),
identifier_(unique_identifier),
- factory_(factory),
- running_version_change_transaction_(NULL) {
- DCHECK(!metadata_.name.empty());
+ factory_(factory) {
}
void IndexedDBDatabase::AddObjectStore(
@@ -190,14 +171,14 @@ void IndexedDBDatabase::RemoveIndex(int64 object_store_id, int64 index_id) {
metadata_.object_stores[object_store_id] = object_store;
}
-bool IndexedDBDatabase::OpenInternal() {
+leveldb::Status IndexedDBDatabase::OpenInternal() {
bool success = false;
- bool ok = backing_store_->GetIDBDatabaseMetaData(
+ leveldb::Status s = backing_store_->GetIDBDatabaseMetaData(
metadata_.name, &metadata_, &success);
DCHECK(success == (metadata_.id != kInvalidId)) << "success = " << success
<< " id = " << metadata_.id;
- if (!ok)
- return false;
+ if (!s.ok())
+ return s;
if (success)
return backing_store_->GetObjectStores(metadata_.id,
&metadata_.object_stores);
@@ -212,6 +193,16 @@ IndexedDBDatabase::~IndexedDBDatabase() {
DCHECK(pending_delete_calls_.empty());
}
+scoped_ptr<IndexedDBConnection> IndexedDBDatabase::CreateConnection(
+ scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
+ int child_process_id) {
+ scoped_ptr<IndexedDBConnection> connection(
+ new IndexedDBConnection(this, database_callbacks));
+ connections_.insert(connection.get());
+ backing_store_->GrantChildProcessPermissions(child_process_id);
+ return connection.Pass();
+}
+
IndexedDBTransaction* IndexedDBDatabase::GetTransaction(
int64 transaction_id) const {
TransactionMap::const_iterator trans_iterator =
@@ -222,7 +213,7 @@ IndexedDBTransaction* IndexedDBDatabase::GetTransaction(
}
bool IndexedDBDatabase::ValidateObjectStoreId(int64 object_store_id) const {
- if (!Contains(metadata_.object_stores, object_store_id)) {
+ if (!ContainsKey(metadata_.object_stores, object_store_id)) {
DLOG(ERROR) << "Invalid object_store_id";
return false;
}
@@ -235,7 +226,7 @@ bool IndexedDBDatabase::ValidateObjectStoreIdAndIndexId(int64 object_store_id,
return false;
const IndexedDBObjectStoreMetadata& object_store_metadata =
metadata_.object_stores.find(object_store_id)->second;
- if (!Contains(object_store_metadata.indexes, index_id)) {
+ if (!ContainsKey(object_store_metadata.indexes, index_id)) {
DLOG(ERROR) << "Invalid index_id";
return false;
}
@@ -250,7 +241,7 @@ bool IndexedDBDatabase::ValidateObjectStoreIdAndOptionalIndexId(
const IndexedDBObjectStoreMetadata& object_store_metadata =
metadata_.object_stores.find(object_store_id)->second;
if (index_id != IndexedDBIndexMetadata::kInvalidId &&
- !Contains(object_store_metadata.indexes, index_id)) {
+ !ContainsKey(object_store_metadata.indexes, index_id)) {
DLOG(ERROR) << "Invalid index_id";
return false;
}
@@ -264,7 +255,7 @@ bool IndexedDBDatabase::ValidateObjectStoreIdAndNewIndexId(
return false;
const IndexedDBObjectStoreMetadata& object_store_metadata =
metadata_.object_stores.find(object_store_id)->second;
- if (Contains(object_store_metadata.indexes, index_id)) {
+ if (ContainsKey(object_store_metadata.indexes, index_id)) {
DLOG(ERROR) << "Invalid index_id";
return false;
}
@@ -276,17 +267,20 @@ void IndexedDBDatabase::CreateObjectStore(int64 transaction_id,
const base::string16& name,
const IndexedDBKeyPath& key_path,
bool auto_increment) {
- IDB_TRACE("IndexedDBDatabase::CreateObjectStore");
+ IDB_TRACE1("IndexedDBDatabase::CreateObjectStore", "txn.id", transaction_id);
IndexedDBTransaction* transaction = GetTransaction(transaction_id);
if (!transaction)
return;
DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
- if (Contains(metadata_.object_stores, object_store_id)) {
+ if (ContainsKey(metadata_.object_stores, object_store_id)) {
DLOG(ERROR) << "Invalid object_store_id";
return;
}
+ // Store creation is done synchronously, as it may be followed by
+ // index creation (also sync) since preemptive OpenCursor/SetIndexKeys
+ // may follow.
IndexedDBObjectStoreMetadata object_store_metadata(
name,
object_store_id,
@@ -294,39 +288,35 @@ void IndexedDBDatabase::CreateObjectStore(int64 transaction_id,
auto_increment,
IndexedDBDatabase::kMinimumIndexId);
- transaction->ScheduleTask(
- base::Bind(&IndexedDBDatabase::CreateObjectStoreOperation,
- this,
- object_store_metadata),
- base::Bind(&IndexedDBDatabase::CreateObjectStoreAbortOperation,
- this,
- object_store_id));
-
- AddObjectStore(object_store_metadata, object_store_id);
-}
-
-void IndexedDBDatabase::CreateObjectStoreOperation(
- const IndexedDBObjectStoreMetadata& object_store_metadata,
- IndexedDBTransaction* transaction) {
- IDB_TRACE("IndexedDBDatabase::CreateObjectStoreOperation");
- if (!backing_store_->CreateObjectStore(
- transaction->BackingStoreTransaction(),
- transaction->database()->id(),
- object_store_metadata.id,
- object_store_metadata.name,
- object_store_metadata.key_path,
- object_store_metadata.auto_increment)) {
- transaction->Abort(IndexedDBDatabaseError(
+ leveldb::Status s =
+ backing_store_->CreateObjectStore(transaction->BackingStoreTransaction(),
+ transaction->database()->id(),
+ object_store_metadata.id,
+ object_store_metadata.name,
+ object_store_metadata.key_path,
+ object_store_metadata.auto_increment);
+ if (!s.ok()) {
+ IndexedDBDatabaseError error(
blink::WebIDBDatabaseExceptionUnknownError,
ASCIIToUTF16("Internal error creating object store '") +
- object_store_metadata.name + ASCIIToUTF16("'.")));
+ object_store_metadata.name + ASCIIToUTF16("'."));
+ transaction->Abort(error);
+ if (leveldb_env::IsCorruption(s))
+ factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
+ error);
return;
}
+
+ AddObjectStore(object_store_metadata, object_store_id);
+ transaction->ScheduleAbortTask(
+ base::Bind(&IndexedDBDatabase::CreateObjectStoreAbortOperation,
+ this,
+ object_store_id));
}
void IndexedDBDatabase::DeleteObjectStore(int64 transaction_id,
int64 object_store_id) {
- IDB_TRACE("IndexedDBDatabase::DeleteObjectStore");
+ IDB_TRACE1("IndexedDBDatabase::DeleteObjectStore", "txn.id", transaction_id);
IndexedDBTransaction* transaction = GetTransaction(transaction_id);
if (!transaction)
return;
@@ -335,17 +325,10 @@ void IndexedDBDatabase::DeleteObjectStore(int64 transaction_id,
if (!ValidateObjectStoreId(object_store_id))
return;
- const IndexedDBObjectStoreMetadata& object_store_metadata =
- metadata_.object_stores[object_store_id];
-
transaction->ScheduleTask(
base::Bind(&IndexedDBDatabase::DeleteObjectStoreOperation,
this,
- object_store_metadata),
- base::Bind(&IndexedDBDatabase::DeleteObjectStoreAbortOperation,
- this,
- object_store_metadata));
- RemoveObjectStore(object_store_id);
+ object_store_id));
}
void IndexedDBDatabase::CreateIndex(int64 transaction_id,
@@ -355,7 +338,7 @@ void IndexedDBDatabase::CreateIndex(int64 transaction_id,
const IndexedDBKeyPath& key_path,
bool unique,
bool multi_entry) {
- IDB_TRACE("IndexedDBDatabase::CreateIndex");
+ IDB_TRACE1("IndexedDBDatabase::CreateIndex", "txn.id", transaction_id);
IndexedDBTransaction* transaction = GetTransaction(transaction_id);
if (!transaction)
return;
@@ -363,27 +346,12 @@ void IndexedDBDatabase::CreateIndex(int64 transaction_id,
if (!ValidateObjectStoreIdAndNewIndexId(object_store_id, index_id))
return;
+
+ // Index creation is done synchronously since preemptive
+ // OpenCursor/SetIndexKeys may follow.
const IndexedDBIndexMetadata index_metadata(
name, index_id, key_path, unique, multi_entry);
- transaction->ScheduleTask(
- base::Bind(&IndexedDBDatabase::CreateIndexOperation,
- this,
- object_store_id,
- index_metadata),
- base::Bind(&IndexedDBDatabase::CreateIndexAbortOperation,
- this,
- object_store_id,
- index_id));
-
- AddIndex(object_store_id, index_metadata, index_id);
-}
-
-void IndexedDBDatabase::CreateIndexOperation(
- int64 object_store_id,
- const IndexedDBIndexMetadata& index_metadata,
- IndexedDBTransaction* transaction) {
- IDB_TRACE("IndexedDBDatabase::CreateIndexOperation");
if (!backing_store_->CreateIndex(transaction->BackingStoreTransaction(),
transaction->database()->id(),
object_store_id,
@@ -391,7 +359,7 @@ void IndexedDBDatabase::CreateIndexOperation(
index_metadata.name,
index_metadata.key_path,
index_metadata.unique,
- index_metadata.multi_entry)) {
+ index_metadata.multi_entry).ok()) {
base::string16 error_string =
ASCIIToUTF16("Internal error creating index '") +
index_metadata.name + ASCIIToUTF16("'.");
@@ -399,13 +367,22 @@ void IndexedDBDatabase::CreateIndexOperation(
blink::WebIDBDatabaseExceptionUnknownError, error_string));
return;
}
+
+ AddIndex(object_store_id, index_metadata, index_id);
+ transaction->ScheduleAbortTask(
+ base::Bind(&IndexedDBDatabase::CreateIndexAbortOperation,
+ this,
+ object_store_id,
+ index_id));
}
void IndexedDBDatabase::CreateIndexAbortOperation(
int64 object_store_id,
int64 index_id,
IndexedDBTransaction* transaction) {
- IDB_TRACE("IndexedDBDatabase::CreateIndexAbortOperation");
+ IDB_TRACE1("IndexedDBDatabase::CreateIndexAbortOperation",
+ "txn.id",
+ transaction->id());
DCHECK(!transaction);
RemoveIndex(object_store_id, index_id);
}
@@ -413,7 +390,7 @@ void IndexedDBDatabase::CreateIndexAbortOperation(
void IndexedDBDatabase::DeleteIndex(int64 transaction_id,
int64 object_store_id,
int64 index_id) {
- IDB_TRACE("IndexedDBDatabase::DeleteIndex");
+ IDB_TRACE1("IndexedDBDatabase::DeleteIndex", "txn.id", transaction_id);
IndexedDBTransaction* transaction = GetTransaction(transaction_id);
if (!transaction)
return;
@@ -421,46 +398,58 @@ void IndexedDBDatabase::DeleteIndex(int64 transaction_id,
if (!ValidateObjectStoreIdAndIndexId(object_store_id, index_id))
return;
- const IndexedDBIndexMetadata& index_metadata =
- metadata_.object_stores[object_store_id].indexes[index_id];
transaction->ScheduleTask(
base::Bind(&IndexedDBDatabase::DeleteIndexOperation,
this,
object_store_id,
- index_metadata),
- base::Bind(&IndexedDBDatabase::DeleteIndexAbortOperation,
- this,
- object_store_id,
- index_metadata));
-
- RemoveIndex(object_store_id, index_id);
+ index_id));
}
void IndexedDBDatabase::DeleteIndexOperation(
int64 object_store_id,
- const IndexedDBIndexMetadata& index_metadata,
+ int64 index_id,
IndexedDBTransaction* transaction) {
- IDB_TRACE("IndexedDBDatabase::DeleteIndexOperation");
- bool ok = backing_store_->DeleteIndex(transaction->BackingStoreTransaction(),
- transaction->database()->id(),
- object_store_id,
- index_metadata.id);
- if (!ok) {
+ IDB_TRACE1(
+ "IndexedDBDatabase::DeleteIndexOperation", "txn.id", transaction->id());
+
+ const IndexedDBIndexMetadata index_metadata =
+ metadata_.object_stores[object_store_id].indexes[index_id];
+
+ leveldb::Status s =
+ backing_store_->DeleteIndex(transaction->BackingStoreTransaction(),
+ transaction->database()->id(),
+ object_store_id,
+ index_id);
+ if (!s.ok()) {
base::string16 error_string =
ASCIIToUTF16("Internal error deleting index '") +
index_metadata.name + ASCIIToUTF16("'.");
- transaction->Abort(IndexedDBDatabaseError(
- blink::WebIDBDatabaseExceptionUnknownError, error_string));
+ IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
+ error_string);
+ transaction->Abort(error);
+ if (leveldb_env::IsCorruption(s))
+ factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
+ error);
+ return;
}
+
+ RemoveIndex(object_store_id, index_id);
+ transaction->ScheduleAbortTask(
+ base::Bind(&IndexedDBDatabase::DeleteIndexAbortOperation,
+ this,
+ object_store_id,
+ index_metadata));
}
void IndexedDBDatabase::DeleteIndexAbortOperation(
int64 object_store_id,
const IndexedDBIndexMetadata& index_metadata,
IndexedDBTransaction* transaction) {
- IDB_TRACE("IndexedDBDatabase::DeleteIndexAbortOperation");
DCHECK(!transaction);
+ IDB_TRACE1("IndexedDBDatabase::DeleteIndexAbortOperation",
+ "txn.id",
+ transaction->id());
AddIndex(object_store_id, index_metadata, IndexedDBIndexMetadata::kInvalidId);
}
@@ -477,6 +466,7 @@ void IndexedDBDatabase::Commit(int64 transaction_id) {
void IndexedDBDatabase::Abort(int64 transaction_id) {
// If the transaction is unknown, then it has already been aborted by the
// backend before this call so it is safe to ignore it.
+ IDB_TRACE1("IndexedDBDatabase::Abort", "txn.id", transaction_id);
IndexedDBTransaction* transaction = GetTransaction(transaction_id);
if (transaction)
transaction->Abort();
@@ -484,6 +474,7 @@ void IndexedDBDatabase::Abort(int64 transaction_id) {
void IndexedDBDatabase::Abort(int64 transaction_id,
const IndexedDBDatabaseError& error) {
+ IDB_TRACE1("IndexedDBDatabase::Abort(error)", "txn.id", transaction_id);
// If the transaction is unknown, then it has already been aborted by the
// backend before this call so it is safe to ignore it.
IndexedDBTransaction* transaction = GetTransaction(transaction_id);
@@ -497,7 +488,7 @@ void IndexedDBDatabase::Get(int64 transaction_id,
scoped_ptr<IndexedDBKeyRange> key_range,
bool key_only,
scoped_refptr<IndexedDBCallbacks> callbacks) {
- IDB_TRACE("IndexedDBDatabase::Get");
+ IDB_TRACE1("IndexedDBDatabase::Get", "txn.id", transaction_id);
IndexedDBTransaction* transaction = GetTransaction(transaction_id);
if (!transaction)
return;
@@ -522,7 +513,7 @@ void IndexedDBDatabase::GetOperation(
indexed_db::CursorType cursor_type,
scoped_refptr<IndexedDBCallbacks> callbacks,
IndexedDBTransaction* transaction) {
- IDB_TRACE("IndexedDBDatabase::GetOperation");
+ IDB_TRACE1("IndexedDBDatabase::GetOperation", "txn.id", transaction->id());
DCHECK(metadata_.object_stores.find(object_store_id) !=
metadata_.object_stores.end());
@@ -531,6 +522,7 @@ void IndexedDBDatabase::GetOperation(
const IndexedDBKey* key;
+ leveldb::Status s;
scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
if (key_range->IsOnlyKey()) {
key = &key_range->lower();
@@ -543,7 +535,8 @@ void IndexedDBDatabase::GetOperation(
id(),
object_store_id,
*key_range,
- indexed_db::CURSOR_NEXT);
+ indexed_db::CURSOR_NEXT,
+ &s);
} else if (cursor_type == indexed_db::CURSOR_KEY_ONLY) {
// Index Value Retrieval Operation
backing_store_cursor = backing_store_->OpenIndexKeyCursor(
@@ -552,7 +545,8 @@ void IndexedDBDatabase::GetOperation(
object_store_id,
index_id,
*key_range,
- indexed_db::CURSOR_NEXT);
+ indexed_db::CURSOR_NEXT,
+ &s);
} else {
// Index Referenced Value Retrieval Operation
backing_store_cursor = backing_store_->OpenIndexCursor(
@@ -561,7 +555,18 @@ void IndexedDBDatabase::GetOperation(
object_store_id,
index_id,
*key_range,
- indexed_db::CURSOR_NEXT);
+ indexed_db::CURSOR_NEXT,
+ &s);
+ }
+
+ if (!s.ok()) {
+ DLOG(ERROR) << "Unable to open cursor operation: " << s.ToString();
+ IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
+ "Internal error deleting data in range");
+ if (leveldb_env::IsCorruption(s)) {
+ factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
+ error);
+ }
}
if (!backing_store_cursor) {
@@ -573,19 +578,22 @@ void IndexedDBDatabase::GetOperation(
}
scoped_ptr<IndexedDBKey> primary_key;
- bool ok;
if (index_id == IndexedDBIndexMetadata::kInvalidId) {
// Object Store Retrieval Operation
- std::string value;
- ok = backing_store_->GetRecord(transaction->BackingStoreTransaction(),
- id(),
- object_store_id,
- *key,
- &value);
- if (!ok) {
- callbacks->OnError(
- IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
- "Internal error in GetRecord."));
+ IndexedDBValue value;
+ s = backing_store_->GetRecord(transaction->BackingStoreTransaction(),
+ id(),
+ object_store_id,
+ *key,
+ &value);
+ if (!s.ok()) {
+ IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
+ "Internal error in GetRecord.");
+ callbacks->OnError(error);
+
+ if (leveldb_env::IsCorruption(s))
+ factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
+ error);
return;
}
@@ -605,17 +613,20 @@ void IndexedDBDatabase::GetOperation(
}
// From here we are dealing only with indexes.
- ok = backing_store_->GetPrimaryKeyViaIndex(
+ s = backing_store_->GetPrimaryKeyViaIndex(
transaction->BackingStoreTransaction(),
id(),
object_store_id,
index_id,
*key,
&primary_key);
- if (!ok) {
- callbacks->OnError(
- IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
- "Internal error in GetPrimaryKeyViaIndex."));
+ if (!s.ok()) {
+ IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
+ "Internal error in GetPrimaryKeyViaIndex.");
+ callbacks->OnError(error);
+ if (leveldb_env::IsCorruption(s))
+ factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
+ error);
return;
}
if (!primary_key) {
@@ -629,16 +640,19 @@ void IndexedDBDatabase::GetOperation(
}
// Index Referenced Value Retrieval Operation
- std::string value;
- ok = backing_store_->GetRecord(transaction->BackingStoreTransaction(),
- id(),
- object_store_id,
- *primary_key,
- &value);
- if (!ok) {
- callbacks->OnError(
- IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
- "Internal error in GetRecord."));
+ IndexedDBValue value;
+ s = backing_store_->GetRecord(transaction->BackingStoreTransaction(),
+ id(),
+ object_store_id,
+ *primary_key,
+ &value);
+ if (!s.ok()) {
+ IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
+ "Internal error in GetRecord.");
+ callbacks->OnError(error);
+ if (leveldb_env::IsCorruption(s))
+ factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
+ error);
return;
}
@@ -655,19 +669,19 @@ void IndexedDBDatabase::GetOperation(
}
static scoped_ptr<IndexedDBKey> GenerateKey(
- scoped_refptr<IndexedDBBackingStore> backing_store,
- scoped_refptr<IndexedDBTransaction> transaction,
+ IndexedDBBackingStore* backing_store,
+ IndexedDBTransaction* transaction,
int64 database_id,
int64 object_store_id) {
const int64 max_generator_value =
9007199254740992LL; // Maximum integer storable as ECMAScript number.
int64 current_number;
- bool ok = backing_store->GetKeyGeneratorCurrentNumber(
+ leveldb::Status s = backing_store->GetKeyGeneratorCurrentNumber(
transaction->BackingStoreTransaction(),
database_id,
object_store_id,
&current_number);
- if (!ok) {
+ if (!s.ok()) {
LOG(ERROR) << "Failed to GetKeyGeneratorCurrentNumber";
return make_scoped_ptr(new IndexedDBKey());
}
@@ -677,13 +691,12 @@ static scoped_ptr<IndexedDBKey> GenerateKey(
return make_scoped_ptr(new IndexedDBKey(current_number, WebIDBKeyTypeNumber));
}
-static bool UpdateKeyGenerator(
- scoped_refptr<IndexedDBBackingStore> backing_store,
- scoped_refptr<IndexedDBTransaction> transaction,
- int64 database_id,
- int64 object_store_id,
- const IndexedDBKey& key,
- bool check_current) {
+static leveldb::Status UpdateKeyGenerator(IndexedDBBackingStore* backing_store,
+ IndexedDBTransaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ const IndexedDBKey& key,
+ bool check_current) {
DCHECK_EQ(WebIDBKeyTypeNumber, key.type());
return backing_store->MaybeUpdateKeyGeneratorCurrentNumber(
transaction->BackingStoreTransaction(),
@@ -696,25 +709,26 @@ static bool UpdateKeyGenerator(
struct IndexedDBDatabase::PutOperationParams {
PutOperationParams() {}
int64 object_store_id;
- std::string value;
+ IndexedDBValue value;
+ ScopedVector<webkit_blob::BlobDataHandle> handles;
scoped_ptr<IndexedDBKey> key;
IndexedDBDatabase::PutMode put_mode;
scoped_refptr<IndexedDBCallbacks> callbacks;
- std::vector<int64> index_ids;
std::vector<IndexKeys> index_keys;
+ private:
DISALLOW_COPY_AND_ASSIGN(PutOperationParams);
};
void IndexedDBDatabase::Put(int64 transaction_id,
int64 object_store_id,
- std::string* value,
+ IndexedDBValue* value,
+ ScopedVector<webkit_blob::BlobDataHandle>* handles,
scoped_ptr<IndexedDBKey> key,
PutMode put_mode,
scoped_refptr<IndexedDBCallbacks> callbacks,
- const std::vector<int64>& index_ids,
const std::vector<IndexKeys>& index_keys) {
- IDB_TRACE("IndexedDBDatabase::Put");
+ IDB_TRACE1("IndexedDBDatabase::Put", "txn.id", transaction_id);
IndexedDBTransaction* transaction = GetTransaction(transaction_id);
if (!transaction)
return;
@@ -724,13 +738,14 @@ void IndexedDBDatabase::Put(int64 transaction_id,
return;
DCHECK(key);
+ DCHECK(value);
scoped_ptr<PutOperationParams> params(new PutOperationParams());
params->object_store_id = object_store_id;
params->value.swap(*value);
+ params->handles.swap(*handles);
params->key = key.Pass();
params->put_mode = put_mode;
params->callbacks = callbacks;
- params->index_ids = index_ids;
params->index_keys = index_keys;
transaction->ScheduleTask(base::Bind(
&IndexedDBDatabase::PutOperation, this, base::Passed(&params)));
@@ -738,9 +753,8 @@ void IndexedDBDatabase::Put(int64 transaction_id,
void IndexedDBDatabase::PutOperation(scoped_ptr<PutOperationParams> params,
IndexedDBTransaction* transaction) {
- IDB_TRACE("IndexedDBDatabase::PutOperation");
+ IDB_TRACE1("IndexedDBDatabase::PutOperation", "txn.id", transaction->id());
DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY);
- DCHECK_EQ(params->index_ids.size(), params->index_keys.size());
bool key_was_generated = false;
DCHECK(metadata_.object_stores.find(params->object_store_id) !=
@@ -752,8 +766,8 @@ void IndexedDBDatabase::PutOperation(scoped_ptr<PutOperationParams> params,
scoped_ptr<IndexedDBKey> key;
if (params->put_mode != IndexedDBDatabase::CURSOR_UPDATE &&
object_store.auto_increment && !params->key->IsValid()) {
- scoped_ptr<IndexedDBKey> auto_inc_key =
- GenerateKey(backing_store_, transaction, id(), params->object_store_id);
+ scoped_ptr<IndexedDBKey> auto_inc_key = GenerateKey(
+ backing_store_.get(), transaction, id(), params->object_store_id);
key_was_generated = true;
if (!auto_inc_key->IsValid()) {
params->callbacks->OnError(
@@ -771,17 +785,20 @@ void IndexedDBDatabase::PutOperation(scoped_ptr<PutOperationParams> params,
IndexedDBBackingStore::RecordIdentifier record_identifier;
if (params->put_mode == IndexedDBDatabase::ADD_ONLY) {
bool found = false;
- bool ok = backing_store_->KeyExistsInObjectStore(
+ leveldb::Status s = backing_store_->KeyExistsInObjectStore(
transaction->BackingStoreTransaction(),
id(),
params->object_store_id,
*key,
&record_identifier,
&found);
- if (!ok) {
- params->callbacks->OnError(
- IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
- "Internal error checking key existence."));
+ if (!s.ok()) {
+ IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
+ "Internal error checking key existence.");
+ params->callbacks->OnError(error);
+ if (leveldb_env::IsCorruption(s))
+ factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
+ error);
return;
}
if (found) {
@@ -801,7 +818,6 @@ void IndexedDBDatabase::PutOperation(scoped_ptr<PutOperationParams> params,
object_store,
*key,
key_was_generated,
- params->index_ids,
params->index_keys,
&index_writers,
&error_message,
@@ -820,17 +836,22 @@ void IndexedDBDatabase::PutOperation(scoped_ptr<PutOperationParams> params,
// Before this point, don't do any mutation. After this point, rollback the
// transaction in case of error.
- backing_store_success =
+ leveldb::Status s =
backing_store_->PutRecord(transaction->BackingStoreTransaction(),
id(),
params->object_store_id,
*key,
params->value,
+ &params->handles,
&record_identifier);
- if (!backing_store_success) {
- params->callbacks->OnError(IndexedDBDatabaseError(
+ if (!s.ok()) {
+ IndexedDBDatabaseError error(
blink::WebIDBDatabaseExceptionUnknownError,
- "Internal error: backing store error performing put/add."));
+ "Internal error: backing store error performing put/add.");
+ params->callbacks->OnError(error);
+ if (leveldb_env::IsCorruption(s))
+ factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
+ error);
return;
}
@@ -846,16 +867,19 @@ void IndexedDBDatabase::PutOperation(scoped_ptr<PutOperationParams> params,
if (object_store.auto_increment &&
params->put_mode != IndexedDBDatabase::CURSOR_UPDATE &&
key->type() == WebIDBKeyTypeNumber) {
- bool ok = UpdateKeyGenerator(backing_store_,
- transaction,
- id(),
- params->object_store_id,
- *key,
- !key_was_generated);
- if (!ok) {
- params->callbacks->OnError(
- IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
- "Internal error updating key generator."));
+ leveldb::Status s = UpdateKeyGenerator(backing_store_.get(),
+ transaction,
+ id(),
+ params->object_store_id,
+ *key,
+ !key_was_generated);
+ if (!s.ok()) {
+ IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
+ "Internal error updating key generator.");
+ params->callbacks->OnError(error);
+ if (leveldb_env::IsCorruption(s))
+ factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
+ error);
return;
}
}
@@ -866,9 +890,8 @@ void IndexedDBDatabase::PutOperation(scoped_ptr<PutOperationParams> params,
void IndexedDBDatabase::SetIndexKeys(int64 transaction_id,
int64 object_store_id,
scoped_ptr<IndexedDBKey> primary_key,
- const std::vector<int64>& index_ids,
const std::vector<IndexKeys>& index_keys) {
- IDB_TRACE("IndexedDBDatabase::SetIndexKeys");
+ IDB_TRACE1("IndexedDBDatabase::SetIndexKeys", "txn.id", transaction_id);
IndexedDBTransaction* transaction = GetTransaction(transaction_id);
if (!transaction)
return;
@@ -878,17 +901,20 @@ void IndexedDBDatabase::SetIndexKeys(int64 transaction_id,
// evaluate if it's worth the extra complexity.
IndexedDBBackingStore::RecordIdentifier record_identifier;
bool found = false;
- bool ok = backing_store_->KeyExistsInObjectStore(
+ leveldb::Status s = backing_store_->KeyExistsInObjectStore(
transaction->BackingStoreTransaction(),
metadata_.id,
object_store_id,
*primary_key,
&record_identifier,
&found);
- if (!ok) {
- transaction->Abort(
- IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
- "Internal error setting index keys."));
+ if (!s.ok()) {
+ IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
+ "Internal error setting index keys.");
+ transaction->Abort(error);
+ if (leveldb_env::IsCorruption(s))
+ factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
+ error);
return;
}
if (!found) {
@@ -911,7 +937,6 @@ void IndexedDBDatabase::SetIndexKeys(int64 transaction_id,
object_store_metadata,
*primary_key,
false,
- index_ids,
index_keys,
&index_writers,
&error_message,
@@ -941,7 +966,7 @@ void IndexedDBDatabase::SetIndexKeys(int64 transaction_id,
void IndexedDBDatabase::SetIndexesReady(int64 transaction_id,
int64,
const std::vector<int64>& index_ids) {
- IDB_TRACE("IndexedDBDatabase::SetIndexesReady");
+ IDB_TRACE1("IndexedDBDatabase::SetIndexesReady", "txn.id", transaction_id);
IndexedDBTransaction* transaction = GetTransaction(transaction_id);
if (!transaction)
return;
@@ -957,7 +982,9 @@ void IndexedDBDatabase::SetIndexesReady(int64 transaction_id,
void IndexedDBDatabase::SetIndexesReadyOperation(
size_t index_count,
IndexedDBTransaction* transaction) {
- IDB_TRACE("IndexedDBDatabase::SetIndexesReadyOperation");
+ IDB_TRACE1("IndexedDBDatabase::SetIndexesReadyOperation",
+ "txn.id",
+ transaction->id());
for (size_t i = 0; i < index_count; ++i)
transaction->DidCompletePreemptiveEvent();
}
@@ -972,6 +999,7 @@ struct IndexedDBDatabase::OpenCursorOperationParams {
IndexedDBDatabase::TaskType task_type;
scoped_refptr<IndexedDBCallbacks> callbacks;
+ private:
DISALLOW_COPY_AND_ASSIGN(OpenCursorOperationParams);
};
@@ -984,7 +1012,7 @@ void IndexedDBDatabase::OpenCursor(
bool key_only,
TaskType task_type,
scoped_refptr<IndexedDBCallbacks> callbacks) {
- IDB_TRACE("IndexedDBDatabase::OpenCursor");
+ IDB_TRACE1("IndexedDBDatabase::OpenCursor", "txn.id", transaction_id);
IndexedDBTransaction* transaction = GetTransaction(transaction_id);
if (!transaction)
return;
@@ -1008,7 +1036,8 @@ void IndexedDBDatabase::OpenCursor(
void IndexedDBDatabase::OpenCursorOperation(
scoped_ptr<OpenCursorOperationParams> params,
IndexedDBTransaction* transaction) {
- IDB_TRACE("IndexedDBDatabase::OpenCursorOperation");
+ IDB_TRACE1(
+ "IndexedDBDatabase::OpenCursorOperation", "txn.id", transaction->id());
// The frontend has begun indexing, so this pauses the transaction
// until the indexing is complete. This can't happen any earlier
@@ -1017,6 +1046,7 @@ void IndexedDBDatabase::OpenCursorOperation(
if (params->task_type == IndexedDBDatabase::PREEMPTIVE_TASK)
transaction->AddPreemptiveEvent();
+ leveldb::Status s;
scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
if (params->index_id == IndexedDBIndexMetadata::kInvalidId) {
if (params->cursor_type == indexed_db::CURSOR_KEY_ONLY) {
@@ -1026,14 +1056,16 @@ void IndexedDBDatabase::OpenCursorOperation(
id(),
params->object_store_id,
*params->key_range,
- params->direction);
+ params->direction,
+ &s);
} else {
backing_store_cursor = backing_store_->OpenObjectStoreCursor(
transaction->BackingStoreTransaction(),
id(),
params->object_store_id,
*params->key_range,
- params->direction);
+ params->direction,
+ &s);
}
} else {
DCHECK_EQ(params->task_type, IndexedDBDatabase::NORMAL_TASK);
@@ -1044,7 +1076,8 @@ void IndexedDBDatabase::OpenCursorOperation(
params->object_store_id,
params->index_id,
*params->key_range,
- params->direction);
+ params->direction,
+ &s);
} else {
backing_store_cursor = backing_store_->OpenIndexCursor(
transaction->BackingStoreTransaction(),
@@ -1052,12 +1085,24 @@ void IndexedDBDatabase::OpenCursorOperation(
params->object_store_id,
params->index_id,
*params->key_range,
- params->direction);
+ params->direction,
+ &s);
+ }
+ }
+
+ if (!s.ok()) {
+ DLOG(ERROR) << "Unable to open cursor operation: " << s.ToString();
+ IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
+ "Internal error opening cursor operation");
+ if (leveldb_env::IsCorruption(s)) {
+ factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
+ error);
}
}
if (!backing_store_cursor) {
- params->callbacks->OnSuccess(static_cast<std::string*>(NULL));
+ // Why is Success being called?
+ params->callbacks->OnSuccess(static_cast<IndexedDBValue*>(NULL));
return;
}
@@ -1075,7 +1120,7 @@ void IndexedDBDatabase::Count(int64 transaction_id,
int64 index_id,
scoped_ptr<IndexedDBKeyRange> key_range,
scoped_refptr<IndexedDBCallbacks> callbacks) {
- IDB_TRACE("IndexedDBDatabase::Count");
+ IDB_TRACE1("IndexedDBDatabase::Count", "txn.id", transaction_id);
IndexedDBTransaction* transaction = GetTransaction(transaction_id);
if (!transaction)
return;
@@ -1097,17 +1142,19 @@ void IndexedDBDatabase::CountOperation(
scoped_ptr<IndexedDBKeyRange> key_range,
scoped_refptr<IndexedDBCallbacks> callbacks,
IndexedDBTransaction* transaction) {
- IDB_TRACE("IndexedDBDatabase::CountOperation");
+ IDB_TRACE1("IndexedDBDatabase::CountOperation", "txn.id", transaction->id());
uint32 count = 0;
scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
+ leveldb::Status s;
if (index_id == IndexedDBIndexMetadata::kInvalidId) {
backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor(
transaction->BackingStoreTransaction(),
id(),
object_store_id,
*key_range,
- indexed_db::CURSOR_NEXT);
+ indexed_db::CURSOR_NEXT,
+ &s);
} else {
backing_store_cursor = backing_store_->OpenIndexKeyCursor(
transaction->BackingStoreTransaction(),
@@ -1115,7 +1162,17 @@ void IndexedDBDatabase::CountOperation(
object_store_id,
index_id,
*key_range,
- indexed_db::CURSOR_NEXT);
+ indexed_db::CURSOR_NEXT,
+ &s);
+ }
+ if (!s.ok()) {
+ DLOG(ERROR) << "Unable perform count operation: " << s.ToString();
+ IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
+ "Internal error performing count operation");
+ if (leveldb_env::IsCorruption(s)) {
+ factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
+ error);
+ }
}
if (!backing_store_cursor) {
callbacks->OnSuccess(count);
@@ -1124,7 +1181,9 @@ void IndexedDBDatabase::CountOperation(
do {
++count;
- } while (backing_store_cursor->Continue());
+ } while (backing_store_cursor->Continue(&s));
+
+ // TODO(cmumford): Check for database corruption.
callbacks->OnSuccess(count);
}
@@ -1134,7 +1193,7 @@ void IndexedDBDatabase::DeleteRange(
int64 object_store_id,
scoped_ptr<IndexedDBKeyRange> key_range,
scoped_refptr<IndexedDBCallbacks> callbacks) {
- IDB_TRACE("IndexedDBDatabase::DeleteRange");
+ IDB_TRACE1("IndexedDBDatabase::DeleteRange", "txn.id", transaction_id);
IndexedDBTransaction* transaction = GetTransaction(transaction_id);
if (!transaction)
return;
@@ -1155,36 +1214,32 @@ void IndexedDBDatabase::DeleteRangeOperation(
scoped_ptr<IndexedDBKeyRange> key_range,
scoped_refptr<IndexedDBCallbacks> callbacks,
IndexedDBTransaction* transaction) {
- IDB_TRACE("IndexedDBDatabase::DeleteRangeOperation");
- scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor =
- backing_store_->OpenObjectStoreCursor(
- transaction->BackingStoreTransaction(),
- id(),
- object_store_id,
- *key_range,
- indexed_db::CURSOR_NEXT);
- if (backing_store_cursor) {
- do {
- if (!backing_store_->DeleteRecord(
- transaction->BackingStoreTransaction(),
- id(),
- object_store_id,
- backing_store_cursor->record_identifier())) {
- callbacks->OnError(
- IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
- "Internal error deleting data in range"));
- return;
- }
- } while (backing_store_cursor->Continue());
+ IDB_TRACE1(
+ "IndexedDBDatabase::DeleteRangeOperation", "txn.id", transaction->id());
+ leveldb::Status s =
+ backing_store_->DeleteRange(transaction->BackingStoreTransaction(),
+ id(),
+ object_store_id,
+ *key_range);
+ if (!s.ok()) {
+ base::string16 error_string =
+ ASCIIToUTF16("Internal error deleting data in range");
+ IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
+ error_string);
+ transaction->Abort(error);
+ if (leveldb_env::IsCorruption(s)) {
+ factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
+ error);
+ }
+ return;
}
-
callbacks->OnSuccess();
}
void IndexedDBDatabase::Clear(int64 transaction_id,
int64 object_store_id,
scoped_refptr<IndexedDBCallbacks> callbacks) {
- IDB_TRACE("IndexedDBDatabase::Clear");
+ IDB_TRACE1("IndexedDBDatabase::Clear", "txn.id", transaction_id);
IndexedDBTransaction* transaction = GetTransaction(transaction_id);
if (!transaction)
return;
@@ -1201,49 +1256,67 @@ void IndexedDBDatabase::ClearOperation(
int64 object_store_id,
scoped_refptr<IndexedDBCallbacks> callbacks,
IndexedDBTransaction* transaction) {
- IDB_TRACE("IndexedDBDatabase::ObjectStoreClearOperation");
- if (!backing_store_->ClearObjectStore(
- transaction->BackingStoreTransaction(), id(), object_store_id)) {
- callbacks->OnError(
- IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
- "Internal error clearing object store"));
+ IDB_TRACE1("IndexedDBDatabase::ClearOperation", "txn.id", transaction->id());
+ leveldb::Status s = backing_store_->ClearObjectStore(
+ transaction->BackingStoreTransaction(), id(), object_store_id);
+ if (!s.ok()) {
+ IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
+ "Internal error clearing object store");
+ callbacks->OnError(error);
+ if (leveldb_env::IsCorruption(s)) {
+ factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
+ error);
+ }
return;
}
callbacks->OnSuccess();
}
void IndexedDBDatabase::DeleteObjectStoreOperation(
- const IndexedDBObjectStoreMetadata& object_store_metadata,
+ int64 object_store_id,
IndexedDBTransaction* transaction) {
- IDB_TRACE("IndexedDBDatabase::DeleteObjectStoreOperation");
- bool ok =
+ IDB_TRACE1("IndexedDBDatabase::DeleteObjectStoreOperation",
+ "txn.id",
+ transaction->id());
+
+ const IndexedDBObjectStoreMetadata object_store_metadata =
+ metadata_.object_stores[object_store_id];
+ leveldb::Status s =
backing_store_->DeleteObjectStore(transaction->BackingStoreTransaction(),
transaction->database()->id(),
- object_store_metadata.id);
- if (!ok) {
+ object_store_id);
+ if (!s.ok()) {
base::string16 error_string =
ASCIIToUTF16("Internal error deleting object store '") +
object_store_metadata.name + ASCIIToUTF16("'.");
- transaction->Abort(IndexedDBDatabaseError(
- blink::WebIDBDatabaseExceptionUnknownError, error_string));
+ IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
+ error_string);
+ transaction->Abort(error);
+ if (leveldb_env::IsCorruption(s))
+ factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
+ error);
+ return;
}
+
+ RemoveObjectStore(object_store_id);
+ transaction->ScheduleAbortTask(
+ base::Bind(&IndexedDBDatabase::DeleteObjectStoreAbortOperation,
+ this,
+ object_store_metadata));
}
void IndexedDBDatabase::VersionChangeOperation(
int64 version,
scoped_refptr<IndexedDBCallbacks> callbacks,
scoped_ptr<IndexedDBConnection> connection,
- blink::WebIDBDataLoss data_loss,
- std::string data_loss_message,
IndexedDBTransaction* transaction) {
- IDB_TRACE("IndexedDBDatabase::VersionChangeOperation");
+ IDB_TRACE1(
+ "IndexedDBDatabase::VersionChangeOperation", "txn.id", transaction->id());
int64 old_version = metadata_.int_version;
DCHECK_GT(version, old_version);
- metadata_.int_version = version;
+
if (!backing_store_->UpdateIDBDatabaseIntVersion(
- transaction->BackingStoreTransaction(),
- id(),
- metadata_.int_version)) {
+ transaction->BackingStoreTransaction(), id(), version)) {
IndexedDBDatabaseError error(
blink::WebIDBDatabaseExceptionUnknownError,
ASCIIToUTF16(
@@ -1253,61 +1326,47 @@ void IndexedDBDatabase::VersionChangeOperation(
transaction->Abort(error);
return;
}
+
+ transaction->ScheduleAbortTask(
+ base::Bind(&IndexedDBDatabase::VersionChangeAbortOperation,
+ this,
+ metadata_.version,
+ metadata_.int_version));
+ metadata_.int_version = version;
+ metadata_.version = kNoStringVersion;
+
DCHECK(!pending_second_half_open_);
pending_second_half_open_.reset(
new PendingSuccessCall(callbacks, connection.get(), version));
- callbacks->OnUpgradeNeeded(
- old_version, connection.Pass(), metadata(), data_loss, data_loss_message);
-}
-
-void IndexedDBDatabase::TransactionStarted(IndexedDBTransaction* transaction) {
-
- if (transaction->mode() == indexed_db::TRANSACTION_VERSION_CHANGE) {
- DCHECK(!running_version_change_transaction_);
- running_version_change_transaction_ = transaction;
- }
+ callbacks->OnUpgradeNeeded(old_version, connection.Pass(), metadata());
}
-void IndexedDBDatabase::TransactionFinished(IndexedDBTransaction* transaction) {
-
+void IndexedDBDatabase::TransactionFinished(IndexedDBTransaction* transaction,
+ bool committed) {
DCHECK(transactions_.find(transaction->id()) != transactions_.end());
DCHECK_EQ(transactions_[transaction->id()], transaction);
transactions_.erase(transaction->id());
- if (transaction->mode() == indexed_db::TRANSACTION_VERSION_CHANGE) {
- DCHECK_EQ(transaction, running_version_change_transaction_);
- running_version_change_transaction_ = NULL;
- }
-}
-void IndexedDBDatabase::TransactionFinishedAndAbortFired(
- IndexedDBTransaction* transaction) {
if (transaction->mode() == indexed_db::TRANSACTION_VERSION_CHANGE) {
if (pending_second_half_open_) {
- pending_second_half_open_->Callbacks()->OnError(
- IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError,
- "Version change transaction was aborted in "
- "upgradeneeded event handler."));
+ if (committed) {
+ DCHECK_EQ(pending_second_half_open_->version(), metadata_.int_version);
+ DCHECK(metadata_.id != kInvalidId);
+
+ // Connection was already minted for OnUpgradeNeeded callback.
+ scoped_ptr<IndexedDBConnection> connection;
+ pending_second_half_open_->callbacks()->OnSuccess(connection.Pass(),
+ this->metadata());
+ } else {
+ pending_second_half_open_->callbacks()->OnError(
+ IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError,
+ "Version change transaction was aborted in "
+ "upgradeneeded event handler."));
+ }
pending_second_half_open_.reset();
}
- ProcessPendingCalls();
- }
-}
-
-void IndexedDBDatabase::TransactionFinishedAndCompleteFired(
- IndexedDBTransaction* transaction) {
- if (transaction->mode() == indexed_db::TRANSACTION_VERSION_CHANGE) {
- DCHECK(pending_second_half_open_);
- if (pending_second_half_open_) {
- DCHECK_EQ(pending_second_half_open_->Version(), metadata_.int_version);
- DCHECK(metadata_.id != kInvalidId);
- // Connection was already minted for OnUpgradeNeeded callback.
- scoped_ptr<IndexedDBConnection> connection;
-
- pending_second_half_open_->Callbacks()->OnSuccess(connection.Pass(),
- this->metadata());
- pending_second_half_open_.reset();
- }
+ // Connection queue is now unblocked.
ProcessPendingCalls();
}
}
@@ -1343,15 +1402,15 @@ size_t IndexedDBDatabase::PendingDeleteCount() const {
void IndexedDBDatabase::ProcessPendingCalls() {
if (pending_run_version_change_transaction_call_ && ConnectionCount() == 1) {
- DCHECK(pending_run_version_change_transaction_call_->Version() >
+ DCHECK(pending_run_version_change_transaction_call_->version() >
metadata_.int_version);
scoped_ptr<PendingUpgradeCall> pending_call =
pending_run_version_change_transaction_call_.Pass();
- RunVersionChangeTransactionFinal(pending_call->Callbacks(),
- pending_call->Connection(),
- pending_call->TransactionId(),
- pending_call->Version());
- DCHECK_EQ(static_cast<size_t>(1), ConnectionCount());
+ RunVersionChangeTransactionFinal(pending_call->callbacks(),
+ pending_call->ReleaseConnection(),
+ pending_call->transaction_id(),
+ pending_call->version());
+ DCHECK_EQ(1u, ConnectionCount());
// Fall through would be a no-op, since transaction must complete
// asynchronously.
DCHECK(IsDeleteDatabaseBlocked());
@@ -1368,7 +1427,7 @@ void IndexedDBDatabase::ProcessPendingCalls() {
scoped_ptr<PendingDeleteCall> pending_delete_call(
pending_delete_calls.front());
pending_delete_calls.pop_front();
- DeleteDatabaseFinal(pending_delete_call->Callbacks());
+ DeleteDatabaseFinal(pending_delete_call->callbacks());
}
// delete_database_final should never re-queue calls.
DCHECK(pending_delete_calls_.empty());
@@ -1379,12 +1438,8 @@ void IndexedDBDatabase::ProcessPendingCalls() {
PendingOpenCallList pending_open_calls;
pending_open_calls_.swap(pending_open_calls);
while (!pending_open_calls.empty()) {
- scoped_ptr<PendingOpenCall> pending_open_call(pending_open_calls.front());
+ OpenConnection(pending_open_calls.front());
pending_open_calls.pop_front();
- OpenConnection(pending_open_call->Callbacks(),
- pending_open_call->DatabaseCallbacks(),
- pending_open_call->TransactionId(),
- pending_open_call->Version());
}
}
}
@@ -1394,51 +1449,35 @@ void IndexedDBDatabase::CreateTransaction(
IndexedDBConnection* connection,
const std::vector<int64>& object_store_ids,
uint16 mode) {
-
+ IDB_TRACE1("IndexedDBDatabase::CreateTransaction", "txn.id", transaction_id);
DCHECK(connections_.count(connection));
DCHECK(transactions_.find(transaction_id) == transactions_.end());
if (transactions_.find(transaction_id) != transactions_.end())
return;
- scoped_refptr<IndexedDBTransaction> transaction = new IndexedDBTransaction(
+ // The transaction will add itself to this database's coordinator, which
+ // manages the lifetime of the object.
+ TransactionCreated(new IndexedDBTransaction(
transaction_id,
connection->callbacks(),
std::set<int64>(object_store_ids.begin(), object_store_ids.end()),
static_cast<indexed_db::TransactionMode>(mode),
this,
- new IndexedDBBackingStore::Transaction(backing_store_));
- TransactionCreated(transaction);
+ new IndexedDBBackingStore::Transaction(backing_store_)));
}
-void IndexedDBDatabase::TransactionCreated(
- scoped_refptr<IndexedDBTransaction> transaction) {
+void IndexedDBDatabase::TransactionCreated(IndexedDBTransaction* transaction) {
transactions_[transaction->id()] = transaction;
}
bool IndexedDBDatabase::IsOpenConnectionBlocked() const {
return !pending_delete_calls_.empty() ||
- running_version_change_transaction_ ||
+ transaction_coordinator_.IsRunningVersionChangeTransaction() ||
pending_run_version_change_transaction_call_;
}
void IndexedDBDatabase::OpenConnection(
- scoped_refptr<IndexedDBCallbacks> callbacks,
- scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
- int64 transaction_id,
- int64 version) {
- const blink::WebIDBDataLoss kDataLoss =
- blink::WebIDBDataLossNone;
- OpenConnection(
- callbacks, database_callbacks, transaction_id, version, kDataLoss, "");
-}
-
-void IndexedDBDatabase::OpenConnection(
- scoped_refptr<IndexedDBCallbacks> callbacks,
- scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
- int64 transaction_id,
- int64 version,
- blink::WebIDBDataLoss data_loss,
- std::string data_loss_message) {
+ const IndexedDBPendingConnection& connection) {
DCHECK(backing_store_);
// TODO(jsbell): Should have a priority queue so that higher version
@@ -1447,29 +1486,28 @@ void IndexedDBDatabase::OpenConnection(
// The backing store only detects data loss when it is first opened. The
// presence of existing connections means we didn't even check for data loss
// so there'd better not be any.
- DCHECK_NE(blink::WebIDBDataLossTotal, data_loss);
- pending_open_calls_.push_back(new PendingOpenCall(
- callbacks, database_callbacks, transaction_id, version));
+ DCHECK_NE(blink::WebIDBDataLossTotal, connection.callbacks->data_loss());
+ pending_open_calls_.push_back(connection);
return;
}
if (metadata_.id == kInvalidId) {
// The database was deleted then immediately re-opened; OpenInternal()
// recreates it in the backing store.
- if (OpenInternal()) {
+ if (OpenInternal().ok()) {
DCHECK_EQ(IndexedDBDatabaseMetadata::NO_INT_VERSION,
metadata_.int_version);
} else {
base::string16 message;
- if (version == IndexedDBDatabaseMetadata::NO_INT_VERSION) {
+ if (connection.version == IndexedDBDatabaseMetadata::NO_INT_VERSION) {
message = ASCIIToUTF16(
"Internal error opening database with no version specified.");
} else {
message =
ASCIIToUTF16("Internal error opening database with version ") +
- Int64ToString16(version);
+ Int64ToString16(connection.version);
}
- callbacks->OnError(IndexedDBDatabaseError(
+ connection.callbacks->OnError(IndexedDBDatabaseError(
blink::WebIDBDatabaseExceptionUnknownError, message));
return;
}
@@ -1481,65 +1519,67 @@ void IndexedDBDatabase::OpenConnection(
metadata_.version == kNoStringVersion &&
metadata_.int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION;
- scoped_ptr<IndexedDBConnection> connection(
- new IndexedDBConnection(this, database_callbacks));
-
- if (version == IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION) {
+ if (connection.version == IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION) {
// For unit tests only - skip upgrade steps. Calling from script with
// DEFAULT_INT_VERSION throws exception.
// TODO(jsbell): DCHECK that not in unit tests.
DCHECK(is_new_database);
- connections_.insert(connection.get());
- callbacks->OnSuccess(connection.Pass(), this->metadata());
+ connection.callbacks->OnSuccess(
+ CreateConnection(connection.database_callbacks,
+ connection.child_process_id),
+ this->metadata());
return;
}
- if (version == IndexedDBDatabaseMetadata::NO_INT_VERSION) {
+ // We may need to change the version.
+ int64 local_version = connection.version;
+ if (local_version == IndexedDBDatabaseMetadata::NO_INT_VERSION) {
if (!is_new_database) {
- connections_.insert(connection.get());
- callbacks->OnSuccess(connection.Pass(), this->metadata());
+ connection.callbacks->OnSuccess(
+ CreateConnection(connection.database_callbacks,
+ connection.child_process_id),
+ this->metadata());
return;
}
// Spec says: If no version is specified and no database exists, set
// database version to 1.
- version = 1;
+ local_version = 1;
}
- if (version > metadata_.int_version) {
- connections_.insert(connection.get());
- RunVersionChangeTransaction(callbacks,
- connection.Pass(),
- transaction_id,
- version,
- data_loss,
- data_loss_message);
+ if (local_version > metadata_.int_version) {
+ RunVersionChangeTransaction(connection.callbacks,
+ CreateConnection(connection.database_callbacks,
+ connection.child_process_id),
+ connection.transaction_id,
+ local_version);
return;
}
- if (version < metadata_.int_version) {
- callbacks->OnError(IndexedDBDatabaseError(
+ if (local_version < metadata_.int_version) {
+ connection.callbacks->OnError(IndexedDBDatabaseError(
blink::WebIDBDatabaseExceptionVersionError,
- ASCIIToUTF16("The requested version (") + Int64ToString16(version) +
+ ASCIIToUTF16("The requested version (") +
+ Int64ToString16(local_version) +
ASCIIToUTF16(") is less than the existing version (") +
Int64ToString16(metadata_.int_version) + ASCIIToUTF16(").")));
return;
}
- DCHECK_EQ(version, metadata_.int_version);
- connections_.insert(connection.get());
- callbacks->OnSuccess(connection.Pass(), this->metadata());
+ DCHECK_EQ(local_version, metadata_.int_version);
+ connection.callbacks->OnSuccess(
+ CreateConnection(connection.database_callbacks,
+ connection.child_process_id),
+ this->metadata());
}
void IndexedDBDatabase::RunVersionChangeTransaction(
scoped_refptr<IndexedDBCallbacks> callbacks,
scoped_ptr<IndexedDBConnection> connection,
int64 transaction_id,
- int64 requested_version,
- blink::WebIDBDataLoss data_loss,
- std::string data_loss_message) {
+ int64 requested_version) {
DCHECK(callbacks);
DCHECK(connections_.count(connection.get()));
if (ConnectionCount() > 1) {
- DCHECK_NE(blink::WebIDBDataLossTotal, data_loss);
+ DCHECK_NE(blink::WebIDBDataLossTotal, callbacks->data_loss());
// Front end ensures the event is not fired at connections that have
// close_pending set.
for (ConnectionSet::const_iterator it = connections_.begin();
@@ -1560,12 +1600,8 @@ void IndexedDBDatabase::RunVersionChangeTransaction(
callbacks, connection.Pass(), transaction_id, requested_version));
return;
}
- RunVersionChangeTransactionFinal(callbacks,
- connection.Pass(),
- transaction_id,
- requested_version,
- data_loss,
- data_loss_message);
+ RunVersionChangeTransactionFinal(
+ callbacks, connection.Pass(), transaction_id, requested_version);
}
void IndexedDBDatabase::RunVersionChangeTransactionFinal(
@@ -1573,45 +1609,19 @@ void IndexedDBDatabase::RunVersionChangeTransactionFinal(
scoped_ptr<IndexedDBConnection> connection,
int64 transaction_id,
int64 requested_version) {
- const blink::WebIDBDataLoss kDataLoss =
- blink::WebIDBDataLossNone;
- RunVersionChangeTransactionFinal(callbacks,
- connection.Pass(),
- transaction_id,
- requested_version,
- kDataLoss,
- "");
-}
-
-void IndexedDBDatabase::RunVersionChangeTransactionFinal(
- scoped_refptr<IndexedDBCallbacks> callbacks,
- scoped_ptr<IndexedDBConnection> connection,
- int64 transaction_id,
- int64 requested_version,
- blink::WebIDBDataLoss data_loss,
- std::string data_loss_message) {
std::vector<int64> object_store_ids;
CreateTransaction(transaction_id,
connection.get(),
object_store_ids,
indexed_db::TRANSACTION_VERSION_CHANGE);
- scoped_refptr<IndexedDBTransaction> transaction =
- transactions_[transaction_id];
- transaction->ScheduleTask(
+ transactions_[transaction_id]->ScheduleTask(
base::Bind(&IndexedDBDatabase::VersionChangeOperation,
this,
requested_version,
callbacks,
- base::Passed(&connection),
- data_loss,
- data_loss_message),
- base::Bind(&IndexedDBDatabase::VersionChangeAbortOperation,
- this,
- metadata_.version,
- metadata_.int_version));
-
+ base::Passed(&connection)));
DCHECK(!pending_second_half_open_);
}
@@ -1645,17 +1655,31 @@ void IndexedDBDatabase::DeleteDatabaseFinal(
scoped_refptr<IndexedDBCallbacks> callbacks) {
DCHECK(!IsDeleteDatabaseBlocked());
DCHECK(backing_store_);
- if (!backing_store_->DeleteDatabase(metadata_.name)) {
+ if (!backing_store_->DeleteDatabase(metadata_.name).ok()) {
callbacks->OnError(
IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
"Internal error deleting database."));
return;
}
+ int64 old_version = metadata_.int_version;
metadata_.version = kNoStringVersion;
metadata_.id = kInvalidId;
metadata_.int_version = IndexedDBDatabaseMetadata::NO_INT_VERSION;
metadata_.object_stores.clear();
- callbacks->OnSuccess();
+ callbacks->OnSuccess(old_version);
+ if (factory_)
+ factory_->DatabaseDeleted(identifier_);
+}
+
+void IndexedDBDatabase::ForceClose() {
+ // IndexedDBConnection::ForceClose() may delete this database, so hold ref.
+ scoped_refptr<IndexedDBDatabase> protect(this);
+ ConnectionSet::const_iterator it = connections_.begin();
+ while (it != connections_.end()) {
+ IndexedDBConnection* connection = *it++;
+ connection->ForceClose();
+ }
+ DCHECK(connections_.empty());
}
void IndexedDBDatabase::Close(IndexedDBConnection* connection, bool forced) {
@@ -1663,6 +1687,7 @@ void IndexedDBDatabase::Close(IndexedDBConnection* connection, bool forced) {
DCHECK(connection->IsConnected());
DCHECK(connection->database() == this);
+ IDB_TRACE("IndexedDBDatabase::Close");
// Abort outstanding transactions from the closing connection. This
// can not happen if the close is requested by the connection itself
// as the front-end defers the close until all transactions are
@@ -1682,8 +1707,8 @@ void IndexedDBDatabase::Close(IndexedDBConnection* connection, bool forced) {
connections_.erase(connection);
if (pending_second_half_open_ &&
- pending_second_half_open_->Connection() == connection) {
- pending_second_half_open_->Callbacks()->OnError(
+ pending_second_half_open_->connection() == connection) {
+ pending_second_half_open_->callbacks()->OnError(
IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError,
"The connection was closed."));
pending_second_half_open_.reset();
@@ -1702,7 +1727,7 @@ void IndexedDBDatabase::Close(IndexedDBConnection* connection, bool forced) {
// factory_ should only be null in unit tests.
// TODO(jsbell): DCHECK(factory_ || !in_unit_tests) - somehow.
if (factory_) {
- factory_->ReleaseDatabase(identifier_, origin_url, forced);
+ factory_->ReleaseDatabase(identifier_, forced);
factory_ = NULL;
}
}
@@ -1711,16 +1736,20 @@ void IndexedDBDatabase::Close(IndexedDBConnection* connection, bool forced) {
void IndexedDBDatabase::CreateObjectStoreAbortOperation(
int64 object_store_id,
IndexedDBTransaction* transaction) {
- IDB_TRACE("IndexedDBDatabase::CreateObjectStoreAbortOperation");
DCHECK(!transaction);
+ IDB_TRACE1("IndexedDBDatabase::CreateObjectStoreAbortOperation",
+ "txn.id",
+ transaction->id());
RemoveObjectStore(object_store_id);
}
void IndexedDBDatabase::DeleteObjectStoreAbortOperation(
const IndexedDBObjectStoreMetadata& object_store_metadata,
IndexedDBTransaction* transaction) {
- IDB_TRACE("IndexedDBDatabase::DeleteObjectStoreAbortOperation");
DCHECK(!transaction);
+ IDB_TRACE1("IndexedDBDatabase::DeleteObjectStoreAbortOperation",
+ "txn.id",
+ transaction->id());
AddObjectStore(object_store_metadata,
IndexedDBObjectStoreMetadata::kInvalidId);
}
@@ -1729,8 +1758,10 @@ void IndexedDBDatabase::VersionChangeAbortOperation(
const base::string16& previous_version,
int64 previous_int_version,
IndexedDBTransaction* transaction) {
- IDB_TRACE("IndexedDBDatabase::VersionChangeAbortOperation");
DCHECK(!transaction);
+ IDB_TRACE1("IndexedDBDatabase::VersionChangeAbortOperation",
+ "txn.id",
+ transaction->id());
metadata_.version = previous_version;
metadata_.int_version = previous_int_version;
}
diff --git a/chromium/content/browser/indexed_db/indexed_db_database.h b/chromium/content/browser/indexed_db/indexed_db_database.h
index 6297f3dcd68..63a438a4bb6 100644
--- a/chromium/content/browser/indexed_db/indexed_db_database.h
+++ b/chromium/content/browser/indexed_db/indexed_db_database.h
@@ -8,6 +8,7 @@
#include <list>
#include <map>
#include <string>
+#include <utility>
#include <vector>
#include "base/basictypes.h"
@@ -16,12 +17,14 @@
#include "content/browser/indexed_db/indexed_db_backing_store.h"
#include "content/browser/indexed_db/indexed_db_callbacks.h"
#include "content/browser/indexed_db/indexed_db_metadata.h"
+#include "content/browser/indexed_db/indexed_db_pending_connection.h"
#include "content/browser/indexed_db/indexed_db_transaction_coordinator.h"
#include "content/browser/indexed_db/list_set.h"
#include "url/gurl.h"
namespace content {
+class IndexedDBBlobInfo;
class IndexedDBConnection;
class IndexedDBDatabaseCallbacks;
class IndexedDBFactory;
@@ -29,6 +32,7 @@ class IndexedDBKey;
class IndexedDBKeyPath;
class IndexedDBKeyRange;
class IndexedDBTransaction;
+struct IndexedDBValue;
class CONTENT_EXPORT IndexedDBDatabase
: NON_EXPORTED_BASE(public base::RefCounted<IndexedDBDatabase>) {
@@ -44,7 +48,9 @@ class CONTENT_EXPORT IndexedDBDatabase
CURSOR_UPDATE
};
- typedef std::vector<IndexedDBKey> IndexKeys;
+ // An index and corresponding set of keys
+ typedef std::pair<int64, std::vector<IndexedDBKey> > IndexKeys;
+
// Identifier is pair of (origin url, database name).
typedef std::pair<GURL, base::string16> Identifier;
@@ -55,7 +61,8 @@ class CONTENT_EXPORT IndexedDBDatabase
const base::string16& name,
IndexedDBBackingStore* backing_store,
IndexedDBFactory* factory,
- const Identifier& unique_identifier);
+ const Identifier& unique_identifier,
+ leveldb::Status* s);
const Identifier& identifier() const { return identifier_; }
IndexedDBBackingStore* backing_store() { return backing_store_.get(); }
@@ -71,18 +78,7 @@ class CONTENT_EXPORT IndexedDBDatabase
int64 new_max_index_id);
void RemoveIndex(int64 object_store_id, int64 index_id);
- void OpenConnection(
- scoped_refptr<IndexedDBCallbacks> callbacks,
- scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
- int64 transaction_id,
- int64 version);
- void OpenConnection(
- scoped_refptr<IndexedDBCallbacks> callbacks,
- scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
- int64 transaction_id,
- int64 version,
- blink::WebIDBDataLoss data_loss,
- std::string data_loss_message);
+ void OpenConnection(const IndexedDBPendingConnection& connection);
void DeleteDatabase(scoped_refptr<IndexedDBCallbacks> callbacks);
const IndexedDBDatabaseMetadata& metadata() const { return metadata_; }
@@ -97,6 +93,7 @@ class CONTENT_EXPORT IndexedDBDatabase
const std::vector<int64>& object_store_ids,
uint16 mode);
void Close(IndexedDBConnection* connection, bool forced);
+ void ForceClose();
void Commit(int64 transaction_id);
void Abort(int64 transaction_id);
@@ -118,11 +115,8 @@ class CONTENT_EXPORT IndexedDBDatabase
return transaction_coordinator_;
}
- void TransactionCreated(scoped_refptr<IndexedDBTransaction> transaction);
- void TransactionStarted(IndexedDBTransaction* transaction);
- void TransactionFinished(IndexedDBTransaction* transaction);
- void TransactionFinishedAndCompleteFired(IndexedDBTransaction* transaction);
- void TransactionFinishedAndAbortFired(IndexedDBTransaction* transaction);
+ void TransactionCreated(IndexedDBTransaction* transaction);
+ void TransactionFinished(IndexedDBTransaction* transaction, bool committed);
// Called by transactions to report failure committing to the backing store.
void TransactionCommitFailed();
@@ -135,16 +129,15 @@ class CONTENT_EXPORT IndexedDBDatabase
scoped_refptr<IndexedDBCallbacks> callbacks);
void Put(int64 transaction_id,
int64 object_store_id,
- std::string* value,
+ IndexedDBValue* value,
+ ScopedVector<webkit_blob::BlobDataHandle>* handles,
scoped_ptr<IndexedDBKey> key,
PutMode mode,
scoped_refptr<IndexedDBCallbacks> callbacks,
- const std::vector<int64>& index_ids,
const std::vector<IndexKeys>& index_keys);
void SetIndexKeys(int64 transaction_id,
int64 object_store_id,
scoped_ptr<IndexedDBKey> primary_key,
- const std::vector<int64>& index_ids,
const std::vector<IndexKeys>& index_keys);
void SetIndexesReady(int64 transaction_id,
int64 object_store_id,
@@ -182,13 +175,10 @@ class CONTENT_EXPORT IndexedDBDatabase
size_t PendingDeleteCount() const;
// Asynchronous tasks scheduled within transactions:
- void CreateObjectStoreOperation(
- const IndexedDBObjectStoreMetadata& object_store_metadata,
- IndexedDBTransaction* transaction);
void CreateObjectStoreAbortOperation(int64 object_store_id,
IndexedDBTransaction* transaction);
void DeleteObjectStoreOperation(
- const IndexedDBObjectStoreMetadata& object_store_metadata,
+ int64 object_store_id,
IndexedDBTransaction* transaction);
void DeleteObjectStoreAbortOperation(
const IndexedDBObjectStoreMetadata& object_store_metadata,
@@ -196,17 +186,12 @@ class CONTENT_EXPORT IndexedDBDatabase
void VersionChangeOperation(int64 version,
scoped_refptr<IndexedDBCallbacks> callbacks,
scoped_ptr<IndexedDBConnection> connection,
- blink::WebIDBDataLoss data_loss,
- std::string data_loss_message,
IndexedDBTransaction* transaction);
void VersionChangeAbortOperation(const base::string16& previous_version,
int64 previous_int_version,
IndexedDBTransaction* transaction);
- void CreateIndexOperation(int64 object_store_id,
- const IndexedDBIndexMetadata& index_metadata,
- IndexedDBTransaction* transaction);
void DeleteIndexOperation(int64 object_store_id,
- const IndexedDBIndexMetadata& index_metadata,
+ int64 index_id,
IndexedDBTransaction* transaction);
void CreateIndexAbortOperation(int64 object_store_id,
int64 index_id,
@@ -251,30 +236,25 @@ class CONTENT_EXPORT IndexedDBDatabase
~IndexedDBDatabase();
bool IsOpenConnectionBlocked() const;
- bool OpenInternal();
+ leveldb::Status OpenInternal();
void RunVersionChangeTransaction(scoped_refptr<IndexedDBCallbacks> callbacks,
scoped_ptr<IndexedDBConnection> connection,
int64 transaction_id,
- int64 requested_version,
- blink::WebIDBDataLoss data_loss,
- std::string data_loss_message);
+ int64 requested_version);
void RunVersionChangeTransactionFinal(
scoped_refptr<IndexedDBCallbacks> callbacks,
scoped_ptr<IndexedDBConnection> connection,
int64 transaction_id,
int64 requested_version);
- void RunVersionChangeTransactionFinal(
- scoped_refptr<IndexedDBCallbacks> callbacks,
- scoped_ptr<IndexedDBConnection> connection,
- int64 transaction_id,
- int64 requested_version,
- blink::WebIDBDataLoss data_loss,
- std::string data_loss_message);
void ProcessPendingCalls();
bool IsDeleteDatabaseBlocked() const;
void DeleteDatabaseFinal(scoped_refptr<IndexedDBCallbacks> callbacks);
+ scoped_ptr<IndexedDBConnection> CreateConnection(
+ scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
+ int child_process_id);
+
IndexedDBTransaction* GetTransaction(int64 transaction_id) const;
bool ValidateObjectStoreId(int64 object_store_id) const;
@@ -289,18 +269,14 @@ class CONTENT_EXPORT IndexedDBDatabase
IndexedDBDatabaseMetadata metadata_;
const Identifier identifier_;
- // This might not need to be a scoped_refptr since the factory's lifetime is
- // that of the page group, but it's better to be conservitive than sorry.
scoped_refptr<IndexedDBFactory> factory_;
IndexedDBTransactionCoordinator transaction_coordinator_;
- IndexedDBTransaction* running_version_change_transaction_;
typedef std::map<int64, IndexedDBTransaction*> TransactionMap;
TransactionMap transactions_;
- class PendingOpenCall;
- typedef std::list<PendingOpenCall*> PendingOpenCallList;
+ typedef std::list<IndexedDBPendingConnection> PendingOpenCallList;
PendingOpenCallList pending_open_calls_;
class PendingUpgradeCall;
@@ -314,6 +290,8 @@ class CONTENT_EXPORT IndexedDBDatabase
typedef list_set<IndexedDBConnection*> ConnectionSet;
ConnectionSet connections_;
+
+ DISALLOW_COPY_AND_ASSIGN(IndexedDBDatabase);
};
} // namespace content
diff --git a/chromium/content/browser/indexed_db/indexed_db_database_callbacks.h b/chromium/content/browser/indexed_db/indexed_db_database_callbacks.h
index 0fae7807fcb..ad16a04b1d7 100644
--- a/chromium/content/browser/indexed_db/indexed_db_database_callbacks.h
+++ b/chromium/content/browser/indexed_db/indexed_db_database_callbacks.h
@@ -36,6 +36,8 @@ class CONTENT_EXPORT IndexedDBDatabaseCallbacks
scoped_refptr<IndexedDBDispatcherHost> dispatcher_host_;
int ipc_thread_id_;
int ipc_database_callbacks_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(IndexedDBDatabaseCallbacks);
};
} // namespace content
diff --git a/chromium/content/browser/indexed_db/indexed_db_database_error.h b/chromium/content/browser/indexed_db/indexed_db_database_error.h
index 1a4f449d818..25b5ea6ed1c 100644
--- a/chromium/content/browser/indexed_db/indexed_db_database_error.h
+++ b/chromium/content/browser/indexed_db/indexed_db_database_error.h
@@ -13,10 +13,9 @@ namespace content {
class IndexedDBDatabaseError {
public:
- IndexedDBDatabaseError(uint16 code)
- : code_(code) {}
+ explicit IndexedDBDatabaseError(uint16 code) : code_(code) {}
IndexedDBDatabaseError(uint16 code, const char* message)
- : code_(code), message_(ASCIIToUTF16(message)) {}
+ : code_(code), message_(base::ASCIIToUTF16(message)) {}
IndexedDBDatabaseError(uint16 code, const base::string16& message)
: code_(code), message_(message) {}
~IndexedDBDatabaseError() {}
@@ -27,6 +26,8 @@ class IndexedDBDatabaseError {
private:
const uint16 code_;
const base::string16 message_;
+
+ DISALLOW_COPY_AND_ASSIGN(IndexedDBDatabaseError);
};
} // namespace content
diff --git a/chromium/content/browser/indexed_db/indexed_db_database_unittest.cc b/chromium/content/browser/indexed_db/indexed_db_database_unittest.cc
index 73a7ec22e2b..dcaf8233657 100644
--- a/chromium/content/browser/indexed_db/indexed_db_database_unittest.cc
+++ b/chromium/content/browser/indexed_db/indexed_db_database_unittest.cc
@@ -4,8 +4,11 @@
#include "content/browser/indexed_db/indexed_db_database.h"
+#include <set>
+
#include "base/auto_reset.h"
#include "base/logging.h"
+#include "base/run_loop.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "content/browser/indexed_db/indexed_db.h"
@@ -13,14 +16,20 @@
#include "content/browser/indexed_db/indexed_db_callbacks.h"
#include "content/browser/indexed_db/indexed_db_connection.h"
#include "content/browser/indexed_db/indexed_db_cursor.h"
-#include "content/browser/indexed_db/indexed_db_database.h"
#include "content/browser/indexed_db/indexed_db_factory.h"
#include "content/browser/indexed_db/indexed_db_fake_backing_store.h"
#include "content/browser/indexed_db/indexed_db_transaction.h"
+#include "content/browser/indexed_db/indexed_db_value.h"
#include "content/browser/indexed_db/mock_indexed_db_callbacks.h"
#include "content/browser/indexed_db/mock_indexed_db_database_callbacks.h"
#include "testing/gtest/include/gtest/gtest.h"
+using base::ASCIIToUTF16;
+
+namespace {
+const int kFakeChildProcessId = 0;
+}
+
namespace content {
TEST(IndexedDBDatabaseTest, BackingStoreRetention) {
@@ -29,11 +38,14 @@ TEST(IndexedDBDatabaseTest, BackingStoreRetention) {
EXPECT_TRUE(backing_store->HasOneRef());
IndexedDBFactory* factory = 0;
- scoped_refptr<IndexedDBDatabase> db = IndexedDBDatabase::Create(
- ASCIIToUTF16("db"),
- backing_store,
- factory,
- IndexedDBDatabase::Identifier());
+ leveldb::Status s;
+ scoped_refptr<IndexedDBDatabase> db =
+ IndexedDBDatabase::Create(ASCIIToUTF16("db"),
+ backing_store,
+ factory,
+ IndexedDBDatabase::Identifier(),
+ &s);
+ ASSERT_TRUE(s.ok());
EXPECT_FALSE(backing_store->HasOneRef()); // local and db
db = NULL;
EXPECT_TRUE(backing_store->HasOneRef()); // local
@@ -45,22 +57,27 @@ TEST(IndexedDBDatabaseTest, ConnectionLifecycle) {
EXPECT_TRUE(backing_store->HasOneRef()); // local
IndexedDBFactory* factory = 0;
+ leveldb::Status s;
scoped_refptr<IndexedDBDatabase> db =
IndexedDBDatabase::Create(ASCIIToUTF16("db"),
backing_store,
factory,
- IndexedDBDatabase::Identifier());
-
+ IndexedDBDatabase::Identifier(),
+ &s);
+ ASSERT_TRUE(s.ok());
EXPECT_FALSE(backing_store->HasOneRef()); // local and db
scoped_refptr<MockIndexedDBCallbacks> request1(new MockIndexedDBCallbacks());
scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks1(
new MockIndexedDBDatabaseCallbacks());
const int64 transaction_id1 = 1;
- db->OpenConnection(request1,
- callbacks1,
- transaction_id1,
- IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
+ IndexedDBPendingConnection connection1(
+ request1,
+ callbacks1,
+ kFakeChildProcessId,
+ transaction_id1,
+ IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
+ db->OpenConnection(connection1);
EXPECT_FALSE(backing_store->HasOneRef()); // db, connection count > 0
@@ -68,10 +85,13 @@ TEST(IndexedDBDatabaseTest, ConnectionLifecycle) {
scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks2(
new MockIndexedDBDatabaseCallbacks());
const int64 transaction_id2 = 2;
- db->OpenConnection(request2,
- callbacks2,
- transaction_id2,
- IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
+ IndexedDBPendingConnection connection2(
+ request2,
+ callbacks2,
+ kFakeChildProcessId,
+ transaction_id2,
+ IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
+ db->OpenConnection(connection2);
EXPECT_FALSE(backing_store->HasOneRef()); // local and connection
@@ -95,22 +115,27 @@ TEST(IndexedDBDatabaseTest, ForcedClose) {
EXPECT_TRUE(backing_store->HasOneRef());
IndexedDBFactory* factory = 0;
+ leveldb::Status s;
scoped_refptr<IndexedDBDatabase> database =
IndexedDBDatabase::Create(ASCIIToUTF16("db"),
backing_store,
factory,
- IndexedDBDatabase::Identifier());
-
+ IndexedDBDatabase::Identifier(),
+ &s);
+ ASSERT_TRUE(s.ok());
EXPECT_FALSE(backing_store->HasOneRef()); // local and db
scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks(
new MockIndexedDBDatabaseCallbacks());
scoped_refptr<MockIndexedDBCallbacks> request(new MockIndexedDBCallbacks());
const int64 upgrade_transaction_id = 3;
- database->OpenConnection(request,
- callbacks,
- upgrade_transaction_id,
- IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
+ IndexedDBPendingConnection connection(
+ request,
+ callbacks,
+ kFakeChildProcessId,
+ upgrade_transaction_id,
+ IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
+ database->OpenConnection(connection);
EXPECT_EQ(database, request->connection()->database());
const int64 transaction_id = 123;
@@ -131,20 +156,23 @@ class MockDeleteCallbacks : public IndexedDBCallbacks {
MockDeleteCallbacks()
: IndexedDBCallbacks(NULL, 0, 0),
blocked_called_(false),
- success_void_called_(false) {}
+ success_called_(false) {}
virtual void OnBlocked(int64 existing_version) OVERRIDE {
blocked_called_ = true;
}
- virtual void OnSuccess() OVERRIDE { success_void_called_ = true; }
+ virtual void OnSuccess(int64 result) OVERRIDE { success_called_ = true; }
bool blocked_called() const { return blocked_called_; }
+ bool success_called() const { return success_called_; }
private:
- virtual ~MockDeleteCallbacks() { EXPECT_TRUE(success_void_called_); }
+ virtual ~MockDeleteCallbacks() {}
bool blocked_called_;
- bool success_void_called_;
+ bool success_called_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockDeleteCallbacks);
};
TEST(IndexedDBDatabaseTest, PendingDelete) {
@@ -153,22 +181,27 @@ TEST(IndexedDBDatabaseTest, PendingDelete) {
EXPECT_TRUE(backing_store->HasOneRef()); // local
IndexedDBFactory* factory = 0;
+ leveldb::Status s;
scoped_refptr<IndexedDBDatabase> db =
IndexedDBDatabase::Create(ASCIIToUTF16("db"),
backing_store,
factory,
- IndexedDBDatabase::Identifier());
-
+ IndexedDBDatabase::Identifier(),
+ &s);
+ ASSERT_TRUE(s.ok());
EXPECT_FALSE(backing_store->HasOneRef()); // local and db
scoped_refptr<MockIndexedDBCallbacks> request1(new MockIndexedDBCallbacks());
scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks1(
new MockIndexedDBDatabaseCallbacks());
const int64 transaction_id1 = 1;
- db->OpenConnection(request1,
- callbacks1,
- transaction_id1,
- IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
+ IndexedDBPendingConnection connection(
+ request1,
+ callbacks1,
+ kFakeChildProcessId,
+ transaction_id1,
+ IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
+ db->OpenConnection(connection);
EXPECT_FALSE(backing_store->HasOneRef()); // local and db
@@ -182,6 +215,201 @@ TEST(IndexedDBDatabaseTest, PendingDelete) {
EXPECT_FALSE(db->backing_store());
EXPECT_TRUE(backing_store->HasOneRef()); // local
+ EXPECT_TRUE(request2->success_called());
+}
+
+void DummyOperation(IndexedDBTransaction* transaction) {
+}
+
+class IndexedDBDatabaseOperationTest : public testing::Test {
+ public:
+ IndexedDBDatabaseOperationTest() : commit_success_(leveldb::Status::OK()) {}
+
+ virtual void SetUp() {
+ backing_store_ = new IndexedDBFakeBackingStore();
+ leveldb::Status s;
+ db_ = IndexedDBDatabase::Create(ASCIIToUTF16("db"),
+ backing_store_,
+ NULL /*factory*/,
+ IndexedDBDatabase::Identifier(),
+ &s);
+ ASSERT_TRUE(s.ok());
+
+ request_ = new MockIndexedDBCallbacks();
+ callbacks_ = new MockIndexedDBDatabaseCallbacks();
+ const int64 transaction_id = 1;
+ db_->OpenConnection(IndexedDBPendingConnection(
+ request_,
+ callbacks_,
+ kFakeChildProcessId,
+ transaction_id,
+ IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION));
+ EXPECT_EQ(IndexedDBDatabaseMetadata::NO_INT_VERSION,
+ db_->metadata().int_version);
+
+ transaction_ = new IndexedDBTransaction(
+ transaction_id,
+ callbacks_,
+ std::set<int64>() /*scope*/,
+ indexed_db::TRANSACTION_VERSION_CHANGE,
+ db_,
+ new IndexedDBFakeBackingStore::FakeTransaction(commit_success_));
+ db_->TransactionCreated(transaction_);
+
+ // Add a dummy task which takes the place of the VersionChangeOperation
+ // which kicks off the upgrade. This ensures that the transaction has
+ // processed at least one task before the CreateObjectStore call.
+ transaction_->ScheduleTask(base::Bind(&DummyOperation));
+ }
+
+ void RunPostedTasks() { base::RunLoop().RunUntilIdle(); }
+
+ protected:
+ scoped_refptr<IndexedDBFakeBackingStore> backing_store_;
+ scoped_refptr<IndexedDBDatabase> db_;
+ scoped_refptr<MockIndexedDBCallbacks> request_;
+ scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks_;
+ scoped_refptr<IndexedDBTransaction> transaction_;
+
+ leveldb::Status commit_success_;
+
+ private:
+ base::MessageLoop message_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(IndexedDBDatabaseOperationTest);
+};
+
+TEST_F(IndexedDBDatabaseOperationTest, CreateObjectStore) {
+ EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
+ const int64 store_id = 1001;
+ db_->CreateObjectStore(transaction_->id(),
+ store_id,
+ ASCIIToUTF16("store"),
+ IndexedDBKeyPath(),
+ false /*auto_increment*/);
+ EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
+ RunPostedTasks();
+ transaction_->Commit();
+ EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
+}
+
+TEST_F(IndexedDBDatabaseOperationTest, CreateIndex) {
+ EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
+ const int64 store_id = 1001;
+ db_->CreateObjectStore(transaction_->id(),
+ store_id,
+ ASCIIToUTF16("store"),
+ IndexedDBKeyPath(),
+ false /*auto_increment*/);
+ EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
+ const int64 index_id = 2002;
+ db_->CreateIndex(transaction_->id(),
+ store_id,
+ index_id,
+ ASCIIToUTF16("index"),
+ IndexedDBKeyPath(),
+ false /*unique*/,
+ false /*multi_entry*/);
+ EXPECT_EQ(
+ 1ULL,
+ db_->metadata().object_stores.find(store_id)->second.indexes.size());
+ RunPostedTasks();
+ transaction_->Commit();
+ EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
+ EXPECT_EQ(
+ 1ULL,
+ db_->metadata().object_stores.find(store_id)->second.indexes.size());
+}
+
+class IndexedDBDatabaseOperationAbortTest
+ : public IndexedDBDatabaseOperationTest {
+ public:
+ IndexedDBDatabaseOperationAbortTest() {
+ commit_success_ = leveldb::Status::NotFound("Bummer.");
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(IndexedDBDatabaseOperationAbortTest);
+};
+
+TEST_F(IndexedDBDatabaseOperationAbortTest, CreateObjectStore) {
+ EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
+ const int64 store_id = 1001;
+ db_->CreateObjectStore(transaction_->id(),
+ store_id,
+ ASCIIToUTF16("store"),
+ IndexedDBKeyPath(),
+ false /*auto_increment*/);
+ EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
+ RunPostedTasks();
+ transaction_->Commit();
+ EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
+}
+
+TEST_F(IndexedDBDatabaseOperationAbortTest, CreateIndex) {
+ EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
+ const int64 store_id = 1001;
+ db_->CreateObjectStore(transaction_->id(),
+ store_id,
+ ASCIIToUTF16("store"),
+ IndexedDBKeyPath(),
+ false /*auto_increment*/);
+ EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
+ const int64 index_id = 2002;
+ db_->CreateIndex(transaction_->id(),
+ store_id,
+ index_id,
+ ASCIIToUTF16("index"),
+ IndexedDBKeyPath(),
+ false /*unique*/,
+ false /*multi_entry*/);
+ EXPECT_EQ(
+ 1ULL,
+ db_->metadata().object_stores.find(store_id)->second.indexes.size());
+ RunPostedTasks();
+ transaction_->Commit();
+ EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
+}
+
+TEST_F(IndexedDBDatabaseOperationTest, CreatePutDelete) {
+ EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
+ const int64 store_id = 1001;
+
+ // Creation is synchronous.
+ db_->CreateObjectStore(transaction_->id(),
+ store_id,
+ ASCIIToUTF16("store"),
+ IndexedDBKeyPath(),
+ false /*auto_increment*/);
+ EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
+
+
+ // Put is asynchronous
+ IndexedDBValue value("value1", std::vector<IndexedDBBlobInfo>());
+ ScopedVector<webkit_blob::BlobDataHandle> handles;
+ scoped_ptr<IndexedDBKey> key(new IndexedDBKey("key"));
+ std::vector<IndexedDBDatabase::IndexKeys> index_keys;
+ scoped_refptr<MockIndexedDBCallbacks> request(
+ new MockIndexedDBCallbacks(false));
+ db_->Put(transaction_->id(),
+ store_id,
+ &value,
+ &handles,
+ key.Pass(),
+ IndexedDBDatabase::ADD_ONLY,
+ request,
+ index_keys);
+
+ // Deletion is asynchronous.
+ db_->DeleteObjectStore(transaction_->id(),
+ store_id);
+ EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
+
+ // This will execute the Put then Delete.
+ RunPostedTasks();
+ EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
+
+ transaction_->Commit(); // Cleans up the object hierarchy.
}
} // namespace content
diff --git a/chromium/content/browser/indexed_db/indexed_db_dispatcher_host.cc b/chromium/content/browser/indexed_db/indexed_db_dispatcher_host.cc
index 6f1bf07dca9..47265d5c986 100644
--- a/chromium/content/browser/indexed_db/indexed_db_dispatcher_host.cc
+++ b/chromium/content/browser/indexed_db/indexed_db_dispatcher_host.cc
@@ -7,14 +7,19 @@
#include "base/bind.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
+#include "base/memory/scoped_vector.h"
#include "base/process/process.h"
+#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
+#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/indexed_db/indexed_db_callbacks.h"
#include "content/browser/indexed_db/indexed_db_connection.h"
#include "content/browser/indexed_db/indexed_db_context_impl.h"
#include "content/browser/indexed_db/indexed_db_cursor.h"
#include "content/browser/indexed_db/indexed_db_database_callbacks.h"
#include "content/browser/indexed_db/indexed_db_metadata.h"
+#include "content/browser/indexed_db/indexed_db_pending_connection.h"
+#include "content/browser/indexed_db/indexed_db_value.h"
#include "content/browser/renderer_host/render_message_filter.h"
#include "content/common/indexed_db/indexed_db_messages.h"
#include "content/public/browser/browser_thread.h"
@@ -23,6 +28,7 @@
#include "content/public/common/result_codes.h"
#include "third_party/WebKit/public/platform/WebIDBDatabaseException.h"
#include "url/gurl.h"
+#include "webkit/browser/blob/blob_storage_context.h"
#include "webkit/browser/database/database_util.h"
#include "webkit/common/database/database_identifier.h"
@@ -32,14 +38,50 @@ using blink::WebIDBKey;
namespace content {
IndexedDBDispatcherHost::IndexedDBDispatcherHost(
- IndexedDBContextImpl* indexed_db_context)
- : indexed_db_context_(indexed_db_context),
+ int ipc_process_id,
+ net::URLRequestContextGetter* request_context_getter,
+ IndexedDBContextImpl* indexed_db_context,
+ ChromeBlobStorageContext* blob_storage_context)
+ : BrowserMessageFilter(IndexedDBMsgStart),
+ request_context_getter_(request_context_getter),
+ request_context_(NULL),
+ indexed_db_context_(indexed_db_context),
+ blob_storage_context_(blob_storage_context),
database_dispatcher_host_(new DatabaseDispatcherHost(this)),
- cursor_dispatcher_host_(new CursorDispatcherHost(this)) {
+ cursor_dispatcher_host_(new CursorDispatcherHost(this)),
+ ipc_process_id_(ipc_process_id) {
DCHECK(indexed_db_context_);
}
-IndexedDBDispatcherHost::~IndexedDBDispatcherHost() {}
+IndexedDBDispatcherHost::IndexedDBDispatcherHost(
+ int ipc_process_id,
+ net::URLRequestContext* request_context,
+ IndexedDBContextImpl* indexed_db_context,
+ ChromeBlobStorageContext* blob_storage_context)
+ : BrowserMessageFilter(IndexedDBMsgStart),
+ request_context_(request_context),
+ indexed_db_context_(indexed_db_context),
+ blob_storage_context_(blob_storage_context),
+ database_dispatcher_host_(new DatabaseDispatcherHost(this)),
+ cursor_dispatcher_host_(new CursorDispatcherHost(this)),
+ ipc_process_id_(ipc_process_id) {
+ DCHECK(indexed_db_context_);
+}
+
+IndexedDBDispatcherHost::~IndexedDBDispatcherHost() {
+ STLDeleteValues(&blob_data_handle_map_);
+}
+
+void IndexedDBDispatcherHost::OnChannelConnected(int32 peer_pid) {
+ BrowserMessageFilter::OnChannelConnected(peer_pid);
+
+ if (request_context_getter_.get()) {
+ DCHECK(!request_context_);
+ request_context_ = request_context_getter_->GetURLRequestContext();
+ request_context_getter_ = NULL;
+ DCHECK(request_context_);
+ }
+}
void IndexedDBDispatcherHost::OnChannelClosing() {
bool success = indexed_db_context_->TaskRunner()->PostTask(
@@ -76,30 +118,31 @@ void IndexedDBDispatcherHost::ResetDispatcherHosts() {
base::TaskRunner* IndexedDBDispatcherHost::OverrideTaskRunnerForMessage(
const IPC::Message& message) {
- if (IPC_MESSAGE_CLASS(message) == IndexedDBMsgStart)
+ if (IPC_MESSAGE_CLASS(message) == IndexedDBMsgStart &&
+ message.type() != IndexedDBHostMsg_DatabasePut::ID)
return indexed_db_context_->TaskRunner();
return NULL;
}
-bool IndexedDBDispatcherHost::OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) {
+bool IndexedDBDispatcherHost::OnMessageReceived(const IPC::Message& message) {
if (IPC_MESSAGE_CLASS(message) != IndexedDBMsgStart)
return false;
- DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
+ DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread() ||
+ message.type() == IndexedDBHostMsg_DatabasePut::ID);
- bool handled =
- database_dispatcher_host_->OnMessageReceived(message, message_was_ok) ||
- cursor_dispatcher_host_->OnMessageReceived(message, message_was_ok);
+ bool handled = database_dispatcher_host_->OnMessageReceived(message) ||
+ cursor_dispatcher_host_->OnMessageReceived(message);
if (!handled) {
handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(IndexedDBDispatcherHost, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(IndexedDBDispatcherHost, message)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_FactoryGetDatabaseNames,
OnIDBFactoryGetDatabaseNames)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_FactoryOpen, OnIDBFactoryOpen)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_FactoryDeleteDatabase,
OnIDBFactoryDeleteDatabase)
+ IPC_MESSAGE_HANDLER(IndexedDBHostMsg_AckReceivedBlobs, OnAckReceivedBlobs)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
}
@@ -166,6 +209,22 @@ uint32 IndexedDBDispatcherHost::TransactionIdToProcessId(
return (host_transaction_id >> 32) & 0xffffffff;
}
+void IndexedDBDispatcherHost::HoldBlobDataHandle(
+ const std::string& uuid,
+ scoped_ptr<webkit_blob::BlobDataHandle>& blob_data_handle) {
+ DCHECK(!ContainsKey(blob_data_handle_map_, uuid));
+ blob_data_handle_map_[uuid] = blob_data_handle.release();
+}
+
+void IndexedDBDispatcherHost::DropBlobDataHandle(const std::string& uuid) {
+ BlobDataHandleMap::iterator iter = blob_data_handle_map_.find(uuid);
+ if (iter != blob_data_handle_map_.end()) {
+ delete iter->second;
+ blob_data_handle_map_.erase(iter);
+ } else {
+ DLOG(FATAL) << "Failed to find blob UUID in map:" << uuid;
+ }
+}
IndexedDBCursor* IndexedDBDispatcherHost::GetCursorFromId(int32 ipc_cursor_id) {
DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
@@ -185,7 +244,6 @@ IndexedDBCursor* IndexedDBDispatcherHost::GetCursorFromId(int32 ipc_cursor_id) {
web_metadata.object_stores.begin();
iter != web_metadata.object_stores.end();
++iter) {
-
const content::IndexedDBObjectStoreMetadata& web_store_metadata =
iter->second;
::IndexedDBObjectStoreMetadata idb_store_metadata;
@@ -226,7 +284,8 @@ void IndexedDBDispatcherHost::OnIDBFactoryGetDatabaseNames(
new IndexedDBCallbacks(
this, params.ipc_thread_id, params.ipc_callbacks_id),
origin_url,
- indexed_db_path);
+ indexed_db_path,
+ request_context_);
}
void IndexedDBDispatcherHost::OnIDBFactoryOpen(
@@ -251,13 +310,14 @@ void IndexedDBDispatcherHost::OnIDBFactoryOpen(
scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks =
new IndexedDBDatabaseCallbacks(
this, params.ipc_thread_id, params.ipc_database_callbacks_id);
- Context()->GetIDBFactory()->Open(params.name,
- params.version,
- host_transaction_id,
- callbacks,
- database_callbacks,
- origin_url,
- indexed_db_path);
+ IndexedDBPendingConnection connection(callbacks,
+ database_callbacks,
+ ipc_process_id_,
+ host_transaction_id,
+ params.version);
+ DCHECK(request_context_);
+ Context()->GetIDBFactory()->Open(
+ params.name, connection, request_context_, origin_url, indexed_db_path);
}
void IndexedDBDispatcherHost::OnIDBFactoryDeleteDatabase(
@@ -266,14 +326,32 @@ void IndexedDBDispatcherHost::OnIDBFactoryDeleteDatabase(
GURL origin_url =
webkit_database::GetOriginFromIdentifier(params.database_identifier);
base::FilePath indexed_db_path = indexed_db_context_->data_path();
+ DCHECK(request_context_);
Context()->GetIDBFactory()->DeleteDatabase(
params.name,
+ request_context_,
new IndexedDBCallbacks(
this, params.ipc_thread_id, params.ipc_callbacks_id),
origin_url,
indexed_db_path);
}
+// OnPutHelper exists only to allow us to hop threads while holding a reference
+// to the IndexedDBDispatcherHost.
+void IndexedDBDispatcherHost::OnPutHelper(
+ const IndexedDBHostMsg_DatabasePut_Params& params,
+ std::vector<webkit_blob::BlobDataHandle*> handles) {
+ database_dispatcher_host_->OnPut(params, handles);
+}
+
+void IndexedDBDispatcherHost::OnAckReceivedBlobs(
+ const std::vector<std::string>& uuids) {
+ DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
+ std::vector<std::string>::const_iterator iter;
+ for (iter = uuids.begin(); iter != uuids.end(); ++iter)
+ DropBlobDataHandle(*iter);
+}
+
void IndexedDBDispatcherHost::FinishTransaction(int64 host_transaction_id,
bool committed) {
DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
@@ -305,7 +383,7 @@ ObjectType* IndexedDBDispatcherHost::GetOrTerminateProcess(
if (!return_object) {
NOTREACHED() << "Uh oh, couldn't find object with id "
<< ipc_return_object_id;
- RecordAction(UserMetricsAction("BadMessageTerminate_IDBMF"));
+ RecordAction(base::UserMetricsAction("BadMessageTerminate_IDBMF"));
BadMessageReceived();
}
return return_object;
@@ -320,7 +398,7 @@ ObjectType* IndexedDBDispatcherHost::GetOrTerminateProcess(
if (!return_object) {
NOTREACHED() << "Uh oh, couldn't find object with id "
<< ipc_return_object_id;
- RecordAction(UserMetricsAction("BadMessageTerminate_IDBMF"));
+ RecordAction(base::UserMetricsAction("BadMessageTerminate_IDBMF"));
BadMessageReceived();
}
return return_object;
@@ -381,13 +459,15 @@ void IndexedDBDispatcherHost::DatabaseDispatcherHost::CloseAll() {
}
bool IndexedDBDispatcherHost::DatabaseDispatcherHost::OnMessageReceived(
- const IPC::Message& message,
- bool* msg_is_ok) {
+ const IPC::Message& message) {
+
DCHECK(
+ (message.type() == IndexedDBHostMsg_DatabasePut::ID) ||
parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
+
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(
- IndexedDBDispatcherHost::DatabaseDispatcherHost, message, *msg_is_ok)
+ IPC_BEGIN_MESSAGE_MAP(
+ IndexedDBDispatcherHost::DatabaseDispatcherHost, message)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCreateObjectStore,
OnCreateObjectStore)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseDeleteObjectStore,
@@ -397,7 +477,7 @@ bool IndexedDBDispatcherHost::DatabaseDispatcherHost::OnMessageReceived(
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseClose, OnClose)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseDestroyed, OnDestroyed)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseGet, OnGet)
- IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabasePut, OnPut)
+ IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabasePut, OnPutWrapper)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseSetIndexKeys, OnSetIndexKeys)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseSetIndexesReady,
OnSetIndexesReady)
@@ -411,6 +491,7 @@ bool IndexedDBDispatcherHost::DatabaseDispatcherHost::OnMessageReceived(
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCommit, OnCommit)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
+
return handled;
}
@@ -492,6 +573,8 @@ void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDestroyed(
DCHECK(
parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
IndexedDBConnection* connection = map_.Lookup(ipc_object_id);
+ if (connection->IsConnected())
+ connection->Close();
parent_->Context()
->ConnectionClosed(database_url_map_[ipc_object_id], connection);
database_url_map_.erase(ipc_object_id);
@@ -518,11 +601,31 @@ void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnGet(
callbacks);
}
-void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnPut(
+void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnPutWrapper(
const IndexedDBHostMsg_DatabasePut_Params& params) {
+ std::vector<webkit_blob::BlobDataHandle*> handles;
+ for (size_t i = 0; i < params.blob_or_file_info.size(); ++i) {
+ const IndexedDBMsg_BlobOrFileInfo& info = params.blob_or_file_info[i];
+ handles.push_back(parent_->blob_storage_context_->context()
+ ->GetBlobDataFromUUID(info.uuid)
+ .release());
+ }
+ parent_->indexed_db_context_->TaskRunner()->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &IndexedDBDispatcherHost::OnPutHelper, parent_, params, handles));
+}
+
+void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnPut(
+ const IndexedDBHostMsg_DatabasePut_Params& params,
+ std::vector<webkit_blob::BlobDataHandle*> handles) {
+
DCHECK(
parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
+ ScopedVector<webkit_blob::BlobDataHandle> scoped_handles;
+ scoped_handles.swap(handles);
+
IndexedDBConnection* connection =
parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
if (!connection || !connection->IsConnected())
@@ -531,26 +634,44 @@ void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnPut(
parent_, params.ipc_thread_id, params.ipc_callbacks_id));
int64 host_transaction_id = parent_->HostTransactionId(params.transaction_id);
- if (params.index_ids.size() != params.index_keys.size()) {
- connection->database()->Abort(
- host_transaction_id,
- IndexedDBDatabaseError(
- blink::WebIDBDatabaseExceptionUnknownError,
- "Malformed IPC message: index_ids.size() != index_keys.size()"));
- parent_->BadMessageReceived();
- return;
+
+ std::vector<IndexedDBBlobInfo> blob_info(params.blob_or_file_info.size());
+
+ ChildProcessSecurityPolicyImpl* policy =
+ ChildProcessSecurityPolicyImpl::GetInstance();
+
+ for (size_t i = 0; i < params.blob_or_file_info.size(); ++i) {
+ const IndexedDBMsg_BlobOrFileInfo& info = params.blob_or_file_info[i];
+ if (info.is_file) {
+ base::FilePath path = base::FilePath::FromUTF16Unsafe(info.file_path);
+ if (!policy->CanReadFile(parent_->ipc_process_id_, path)) {
+ parent_->BadMessageReceived();
+ return;
+ }
+ blob_info[i] =
+ IndexedDBBlobInfo(info.uuid, path, info.file_name, info.mime_type);
+ if (info.size != static_cast<uint64_t>(-1)) {
+ blob_info[i].set_last_modified(
+ base::Time::FromDoubleT(info.last_modified));
+ blob_info[i].set_size(info.size);
+ }
+ } else {
+ blob_info[i] = IndexedDBBlobInfo(info.uuid, info.mime_type, info.size);
+ }
}
// TODO(alecflett): Avoid a copy here.
- std::string value_copy(params.value);
+ IndexedDBValue value;
+ value.bits = params.value;
+ value.blob_info.swap(blob_info);
connection->database()->Put(
host_transaction_id,
params.object_store_id,
- &value_copy,
+ &value,
+ &scoped_handles,
make_scoped_ptr(new IndexedDBKey(params.key)),
static_cast<IndexedDBDatabase::PutMode>(params.put_mode),
callbacks,
- params.index_ids,
params.index_keys);
TransactionIDToSizeMap* map =
&parent_->database_dispatcher_host_->transaction_size_map_;
@@ -569,21 +690,10 @@ void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnSetIndexKeys(
return;
int64 host_transaction_id = parent_->HostTransactionId(params.transaction_id);
- if (params.index_ids.size() != params.index_keys.size()) {
- connection->database()->Abort(
- host_transaction_id,
- IndexedDBDatabaseError(
- blink::WebIDBDatabaseExceptionUnknownError,
- "Malformed IPC message: index_ids.size() != index_keys.size()"));
- parent_->BadMessageReceived();
- return;
- }
-
connection->database()->SetIndexKeys(
host_transaction_id,
params.object_store_id,
make_scoped_ptr(new IndexedDBKey(params.primary_key)),
- params.index_ids,
params.index_keys);
}
@@ -773,14 +883,10 @@ IndexedDBDispatcherHost::CursorDispatcherHost::CursorDispatcherHost(
IndexedDBDispatcherHost::CursorDispatcherHost::~CursorDispatcherHost() {}
bool IndexedDBDispatcherHost::CursorDispatcherHost::OnMessageReceived(
- const IPC::Message& message,
- bool* msg_is_ok) {
- DCHECK(
- parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
-
+ const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(
- IndexedDBDispatcherHost::CursorDispatcherHost, message, *msg_is_ok)
+ IPC_BEGIN_MESSAGE_MAP(
+ IndexedDBDispatcherHost::CursorDispatcherHost, message)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorAdvance, OnAdvance)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorContinue, OnContinue)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorPrefetch, OnPrefetch)
@@ -788,6 +894,11 @@ bool IndexedDBDispatcherHost::CursorDispatcherHost::OnMessageReceived(
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorDestroyed, OnDestroyed)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
+
+ DCHECK(
+ !handled ||
+ parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
+
return handled;
}
@@ -795,7 +906,7 @@ void IndexedDBDispatcherHost::CursorDispatcherHost::OnAdvance(
int32 ipc_cursor_id,
int32 ipc_thread_id,
int32 ipc_callbacks_id,
- unsigned long count) {
+ uint32 count) {
DCHECK(
parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
IndexedDBCursor* idb_cursor =
@@ -860,7 +971,11 @@ void IndexedDBDispatcherHost::CursorDispatcherHost::OnPrefetchReset(
if (!idb_cursor)
return;
- idb_cursor->PrefetchReset(used_prefetches, unused_prefetches);
+ leveldb::Status s =
+ idb_cursor->PrefetchReset(used_prefetches, unused_prefetches);
+ // TODO(cmumford): Handle this error (crbug.com/363397)
+ if (!s.ok())
+ DLOG(ERROR) << "Unable to reset prefetch";
}
void IndexedDBDispatcherHost::CursorDispatcherHost::OnDestroyed(
diff --git a/chromium/content/browser/indexed_db/indexed_db_dispatcher_host.h b/chromium/content/browser/indexed_db/indexed_db_dispatcher_host.h
index bcbbfba029b..abbdab99234 100644
--- a/chromium/content/browser/indexed_db/indexed_db_dispatcher_host.h
+++ b/chromium/content/browser/indexed_db/indexed_db_dispatcher_host.h
@@ -6,13 +6,17 @@
#define CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_DISPATCHER_HOST_H_
#include <map>
+#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/id_map.h"
#include "base/memory/ref_counted.h"
+#include "content/browser/fileapi/chrome_blob_storage_context.h"
#include "content/public/browser/browser_message_filter.h"
+#include "net/url_request/url_request_context_getter.h"
#include "url/gurl.h"
+#include "webkit/browser/blob/blob_data_handle.h"
struct IndexedDBDatabaseMetadata;
struct IndexedDBHostMsg_DatabaseCount_Params;
@@ -41,23 +45,33 @@ struct IndexedDBDatabaseMetadata;
class IndexedDBDispatcherHost : public BrowserMessageFilter {
public:
// Only call the constructor from the UI thread.
- explicit IndexedDBDispatcherHost(IndexedDBContextImpl* indexed_db_context);
+ IndexedDBDispatcherHost(int ipc_process_id,
+ net::URLRequestContextGetter* request_context_getter,
+ IndexedDBContextImpl* indexed_db_context,
+ ChromeBlobStorageContext* blob_storage_context);
+ IndexedDBDispatcherHost(int ipc_process_id,
+ net::URLRequestContext* request_context,
+ IndexedDBContextImpl* indexed_db_context,
+ ChromeBlobStorageContext* blob_storage_context);
static ::IndexedDBDatabaseMetadata ConvertMetadata(
const content::IndexedDBDatabaseMetadata& metadata);
// BrowserMessageFilter implementation.
+ virtual void OnChannelConnected(int32 peer_pid) OVERRIDE;
virtual void OnChannelClosing() OVERRIDE;
virtual void OnDestruct() const OVERRIDE;
virtual base::TaskRunner* OverrideTaskRunnerForMessage(
const IPC::Message& message) OVERRIDE;
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
void FinishTransaction(int64 host_transaction_id, bool committed);
// A shortcut for accessing our context.
IndexedDBContextImpl* Context() { return indexed_db_context_; }
+ webkit_blob::BlobStorageContext* blob_storage_context() const {
+ return blob_storage_context_->context();
+ }
// IndexedDBCallbacks call these methods to add the results into the
// applicable map. See below for more details.
@@ -80,6 +94,11 @@ class IndexedDBDispatcherHost : public BrowserMessageFilter {
static uint32 TransactionIdToRendererTransactionId(int64 host_transaction_id);
static uint32 TransactionIdToProcessId(int64 host_transaction_id);
+ void HoldBlobDataHandle(
+ const std::string& uuid,
+ scoped_ptr<webkit_blob::BlobDataHandle>& blob_data_handle);
+ void DropBlobDataHandle(const std::string& uuid);
+
private:
// Friends to enable OnDestruct() delegation.
friend class BrowserThread;
@@ -96,6 +115,10 @@ class IndexedDBDispatcherHost : public BrowserMessageFilter {
void OnIDBFactoryDeleteDatabase(
const IndexedDBHostMsg_FactoryDeleteDatabase_Params& p);
+ void OnAckReceivedBlobs(const std::vector<std::string>& uuids);
+ void OnPutHelper(const IndexedDBHostMsg_DatabasePut_Params& params,
+ std::vector<webkit_blob::BlobDataHandle*> handles);
+
void ResetDispatcherHosts();
// IDMap for RefCounted types
@@ -127,6 +150,8 @@ class IndexedDBDispatcherHost : public BrowserMessageFilter {
private:
IDMap<scoped_refptr<RefCountedType>, IDMapOwnPointer> map_;
+
+ DISALLOW_COPY_AND_ASSIGN(RefIDMap);
};
// Helper templates.
@@ -153,7 +178,7 @@ class IndexedDBDispatcherHost : public BrowserMessageFilter {
~DatabaseDispatcherHost();
void CloseAll();
- bool OnMessageReceived(const IPC::Message& message, bool* msg_is_ok);
+ bool OnMessageReceived(const IPC::Message& message);
void OnCreateObjectStore(
const IndexedDBHostMsg_DatabaseCreateObjectStore_Params& params);
@@ -166,7 +191,11 @@ class IndexedDBDispatcherHost : public BrowserMessageFilter {
void OnDestroyed(int32 ipc_database_id);
void OnGet(const IndexedDBHostMsg_DatabaseGet_Params& params);
- void OnPut(const IndexedDBHostMsg_DatabasePut_Params& params);
+ // OnPutWrapper starts on the IO thread so that it can grab BlobDataHandles
+ // before posting to the IDB TaskRunner for the rest of the job.
+ void OnPutWrapper(const IndexedDBHostMsg_DatabasePut_Params& params);
+ void OnPut(const IndexedDBHostMsg_DatabasePut_Params& params,
+ std::vector<webkit_blob::BlobDataHandle*> handles);
void OnSetIndexKeys(
const IndexedDBHostMsg_DatabaseSetIndexKeys_Params& params);
void OnSetIndexesReady(int32 ipc_database_id,
@@ -197,6 +226,9 @@ class IndexedDBDispatcherHost : public BrowserMessageFilter {
TransactionIDToSizeMap transaction_size_map_;
TransactionIDToURLMap transaction_url_map_;
TransactionIDToDatabaseIDMap transaction_database_map_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DatabaseDispatcherHost);
};
class CursorDispatcherHost {
@@ -204,12 +236,12 @@ class IndexedDBDispatcherHost : public BrowserMessageFilter {
explicit CursorDispatcherHost(IndexedDBDispatcherHost* parent);
~CursorDispatcherHost();
- bool OnMessageReceived(const IPC::Message& message, bool* msg_is_ok);
+ bool OnMessageReceived(const IPC::Message& message);
void OnAdvance(int32 ipc_object_store_id,
int32 ipc_thread_id,
int32 ipc_callbacks_id,
- unsigned long count);
+ uint32 count);
void OnContinue(int32 ipc_object_store_id,
int32 ipc_thread_id,
int32 ipc_callbacks_id,
@@ -226,14 +258,28 @@ class IndexedDBDispatcherHost : public BrowserMessageFilter {
IndexedDBDispatcherHost* parent_;
RefIDMap<IndexedDBCursor> map_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CursorDispatcherHost);
};
+ // The getter holds the context until OnChannelConnected() can be called from
+ // the IO thread, which will extract the net::URLRequestContext from it.
+ scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
+ net::URLRequestContext* request_context_;
scoped_refptr<IndexedDBContextImpl> indexed_db_context_;
+ scoped_refptr<ChromeBlobStorageContext> blob_storage_context_;
+
+ typedef std::map<std::string, webkit_blob::BlobDataHandle*> BlobDataHandleMap;
+ BlobDataHandleMap blob_data_handle_map_;
// Only access on IndexedDB thread.
scoped_ptr<DatabaseDispatcherHost> database_dispatcher_host_;
scoped_ptr<CursorDispatcherHost> cursor_dispatcher_host_;
+ // Used to set file permissions for blob storage.
+ int ipc_process_id_;
+
DISALLOW_IMPLICIT_CONSTRUCTORS(IndexedDBDispatcherHost);
};
diff --git a/chromium/content/browser/indexed_db/indexed_db_factory.cc b/chromium/content/browser/indexed_db/indexed_db_factory.cc
index d4f0df9f3ce..3f08d06c9bd 100644
--- a/chromium/content/browser/indexed_db/indexed_db_factory.cc
+++ b/chromium/content/browser/indexed_db/indexed_db_factory.cc
@@ -4,16 +4,22 @@
#include "content/browser/indexed_db/indexed_db_factory.h"
+#include <vector>
+
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "content/browser/indexed_db/indexed_db_backing_store.h"
#include "content/browser/indexed_db/indexed_db_context_impl.h"
+#include "content/browser/indexed_db/indexed_db_database_error.h"
#include "content/browser/indexed_db/indexed_db_tracing.h"
#include "content/browser/indexed_db/indexed_db_transaction_coordinator.h"
#include "third_party/WebKit/public/platform/WebIDBDatabaseException.h"
+#include "third_party/leveldatabase/env_chromium.h"
#include "webkit/common/database/database_identifier.h"
+using base::ASCIIToUTF16;
+
namespace content {
const int64 kBackingStoreGracePeriodMs = 2000;
@@ -23,23 +29,49 @@ IndexedDBFactory::IndexedDBFactory(IndexedDBContextImpl* context)
IndexedDBFactory::~IndexedDBFactory() {}
-void IndexedDBFactory::ReleaseDatabase(
- const IndexedDBDatabase::Identifier& identifier,
- const GURL& origin_url,
- bool forcedClose) {
+void IndexedDBFactory::RemoveDatabaseFromMaps(
+ const IndexedDBDatabase::Identifier& identifier) {
IndexedDBDatabaseMap::iterator it = database_map_.find(identifier);
DCHECK(it != database_map_.end());
- DCHECK(!it->second->backing_store());
+ IndexedDBDatabase* database = it->second;
database_map_.erase(it);
+ std::pair<OriginDBMap::iterator, OriginDBMap::iterator> range =
+ origin_dbs_.equal_range(database->identifier().first);
+ DCHECK(range.first != range.second);
+ for (OriginDBMap::iterator it2 = range.first; it2 != range.second; ++it2) {
+ if (it2->second == database) {
+ origin_dbs_.erase(it2);
+ break;
+ }
+ }
+}
+
+void IndexedDBFactory::ReleaseDatabase(
+ const IndexedDBDatabase::Identifier& identifier,
+ bool forcedClose) {
+
+ DCHECK(!database_map_.find(identifier)->second->backing_store());
+
+ RemoveDatabaseFromMaps(identifier);
+
// No grace period on a forced-close, as the initiator is
// assuming the backing store will be released once all
// connections are closed.
- ReleaseBackingStore(origin_url, forcedClose);
+ ReleaseBackingStore(identifier.first, forcedClose);
}
void IndexedDBFactory::ReleaseBackingStore(const GURL& origin_url,
bool immediate) {
+ if (immediate) {
+ IndexedDBBackingStoreMap::iterator it =
+ backing_stores_with_active_blobs_.find(origin_url);
+ if (it != backing_stores_with_active_blobs_.end()) {
+ it->second->active_blob_registry()->ForceShutdown();
+ backing_stores_with_active_blobs_.erase(it);
+ }
+ }
+
// Only close if this is the last reference.
if (!HasLastBackingStoreReference(origin_url))
return;
@@ -91,6 +123,15 @@ bool IndexedDBFactory::HasLastBackingStoreReference(const GURL& origin_url)
}
void IndexedDBFactory::ForceClose(const GURL& origin_url) {
+ std::pair<OriginDBMapIterator, OriginDBMapIterator> range =
+ GetOpenDatabasesForOrigin(origin_url);
+
+ while (range.first != range.second) {
+ IndexedDBDatabase* db = range.first->second;
+ ++range.first;
+ db->ForceClose();
+ }
+
if (backing_store_map_.find(origin_url) != backing_store_map_.end())
ReleaseBackingStore(origin_url, true /* immediate */);
}
@@ -105,13 +146,36 @@ void IndexedDBFactory::ContextDestroyed() {
++it)
it->second->close_timer()->Stop();
backing_store_map_.clear();
+ backing_stores_with_active_blobs_.clear();
context_ = NULL;
}
+void IndexedDBFactory::ReportOutstandingBlobs(const GURL& origin_url,
+ bool blobs_outstanding) {
+ if (!context_)
+ return;
+ if (blobs_outstanding) {
+ DCHECK(!backing_stores_with_active_blobs_.count(origin_url));
+ IndexedDBBackingStoreMap::iterator it = backing_store_map_.find(origin_url);
+ if (it != backing_store_map_.end())
+ backing_stores_with_active_blobs_.insert(*it);
+ else
+ DCHECK(false);
+ } else {
+ IndexedDBBackingStoreMap::iterator it =
+ backing_stores_with_active_blobs_.find(origin_url);
+ if (it != backing_stores_with_active_blobs_.end()) {
+ backing_stores_with_active_blobs_.erase(it);
+ ReleaseBackingStore(origin_url, false /* immediate */);
+ }
+ }
+}
+
void IndexedDBFactory::GetDatabaseNames(
scoped_refptr<IndexedDBCallbacks> callbacks,
const GURL& origin_url,
- const base::FilePath& data_directory) {
+ const base::FilePath& data_directory,
+ net::URLRequestContext* request_context) {
IDB_TRACE("IndexedDBFactory::GetDatabaseNames");
// TODO(dgrogan): Plumb data_loss back to script eventually?
blink::WebIDBDataLoss data_loss;
@@ -120,6 +184,7 @@ void IndexedDBFactory::GetDatabaseNames(
scoped_refptr<IndexedDBBackingStore> backing_store =
OpenBackingStore(origin_url,
data_directory,
+ request_context,
&data_loss,
&data_loss_message,
&disk_full);
@@ -131,12 +196,20 @@ void IndexedDBFactory::GetDatabaseNames(
return;
}
- callbacks->OnSuccess(backing_store->GetDatabaseNames());
+ leveldb::Status s;
+ std::vector<base::string16> names = backing_store->GetDatabaseNames(&s);
+ if (!s.ok()) {
+ // TODO(cmumford): Handle this error
+ DLOG(ERROR) << "Internal error getting database names";
+ }
+ callbacks->OnSuccess(names);
+ backing_store = NULL;
ReleaseBackingStore(origin_url, false /* immediate */);
}
void IndexedDBFactory::DeleteDatabase(
const base::string16& name,
+ net::URLRequestContext* request_context,
scoped_refptr<IndexedDBCallbacks> callbacks,
const GURL& origin_url,
const base::FilePath& data_directory) {
@@ -157,6 +230,7 @@ void IndexedDBFactory::DeleteDatabase(
scoped_refptr<IndexedDBBackingStore> backing_store =
OpenBackingStore(origin_url,
data_directory,
+ request_context,
&data_loss,
&data_loss_message,
&disk_full);
@@ -169,33 +243,67 @@ void IndexedDBFactory::DeleteDatabase(
return;
}
- scoped_refptr<IndexedDBDatabase> database =
- IndexedDBDatabase::Create(name, backing_store, this, unique_identifier);
+ leveldb::Status s;
+ scoped_refptr<IndexedDBDatabase> database = IndexedDBDatabase::Create(
+ name, backing_store, this, unique_identifier, &s);
if (!database) {
- callbacks->OnError(IndexedDBDatabaseError(
+ IndexedDBDatabaseError error(
blink::WebIDBDatabaseExceptionUnknownError,
ASCIIToUTF16(
"Internal error creating database backend for "
- "indexedDB.deleteDatabase.")));
+ "indexedDB.deleteDatabase."));
+ callbacks->OnError(error);
+ if (leveldb_env::IsCorruption(s))
+ HandleBackingStoreCorruption(origin_url, error);
return;
}
database_map_[unique_identifier] = database;
+ origin_dbs_.insert(std::make_pair(origin_url, database));
database->DeleteDatabase(callbacks);
- database_map_.erase(unique_identifier);
+ RemoveDatabaseFromMaps(unique_identifier);
+ database = NULL;
+ backing_store = NULL;
ReleaseBackingStore(origin_url, false /* immediate */);
}
+void IndexedDBFactory::DatabaseDeleted(
+ const IndexedDBDatabase::Identifier& identifier) {
+ // NULL after ContextDestroyed() called, and in some unit tests.
+ if (!context_)
+ return;
+ context_->DatabaseDeleted(identifier.first);
+}
+
void IndexedDBFactory::HandleBackingStoreFailure(const GURL& origin_url) {
// NULL after ContextDestroyed() called, and in some unit tests.
if (!context_)
return;
- context_->ForceClose(origin_url);
+ context_->ForceClose(origin_url,
+ IndexedDBContextImpl::FORCE_CLOSE_BACKING_STORE_FAILURE);
+}
+
+void IndexedDBFactory::HandleBackingStoreCorruption(
+ const GURL& origin_url,
+ const IndexedDBDatabaseError& error) {
+ // Make a copy of origin_url as this is likely a reference to a member of a
+ // backing store which this function will be deleting.
+ GURL saved_origin_url(origin_url);
+ DCHECK(context_);
+ base::FilePath path_base = context_->data_path();
+ IndexedDBBackingStore::RecordCorruptionInfo(
+ path_base, saved_origin_url, base::UTF16ToUTF8(error.message()));
+ HandleBackingStoreFailure(saved_origin_url);
+ // Note: DestroyBackingStore only deletes LevelDB files, leaving all others,
+ // so our corruption info file will remain.
+ leveldb::Status s =
+ IndexedDBBackingStore::DestroyBackingStore(path_base, saved_origin_url);
+ if (!s.ok())
+ DLOG(ERROR) << "Unable to delete backing store: " << s.ToString();
}
bool IndexedDBFactory::IsDatabaseOpen(const GURL& origin_url,
const base::string16& name) const {
-
return !!database_map_.count(IndexedDBDatabase::Identifier(origin_url, name));
}
@@ -212,9 +320,29 @@ bool IndexedDBFactory::IsBackingStorePendingClose(const GURL& origin_url)
return it->second->close_timer()->IsRunning();
}
+scoped_refptr<IndexedDBBackingStore> IndexedDBFactory::OpenBackingStoreHelper(
+ const GURL& origin_url,
+ const base::FilePath& data_directory,
+ net::URLRequestContext* request_context,
+ blink::WebIDBDataLoss* data_loss,
+ std::string* data_loss_message,
+ bool* disk_full,
+ bool first_time) {
+ return IndexedDBBackingStore::Open(this,
+ origin_url,
+ data_directory,
+ request_context,
+ data_loss,
+ data_loss_message,
+ disk_full,
+ context_->TaskRunner(),
+ first_time);
+}
+
scoped_refptr<IndexedDBBackingStore> IndexedDBFactory::OpenBackingStore(
const GURL& origin_url,
const base::FilePath& data_directory,
+ net::URLRequestContext* request_context,
blink::WebIDBDataLoss* data_loss,
std::string* data_loss_message,
bool* disk_full) {
@@ -227,17 +355,25 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBFactory::OpenBackingStore(
}
scoped_refptr<IndexedDBBackingStore> backing_store;
+ bool first_time = false;
if (open_in_memory) {
- backing_store = IndexedDBBackingStore::OpenInMemory(origin_url);
+ backing_store =
+ IndexedDBBackingStore::OpenInMemory(origin_url, context_->TaskRunner());
} else {
- backing_store = IndexedDBBackingStore::Open(origin_url,
- data_directory,
- data_loss,
- data_loss_message,
- disk_full);
+ first_time = !backends_opened_since_boot_.count(origin_url);
+
+ backing_store = OpenBackingStoreHelper(origin_url,
+ data_directory,
+ request_context,
+ data_loss,
+ data_loss_message,
+ disk_full,
+ first_time);
}
if (backing_store.get()) {
+ if (first_time)
+ backends_opened_since_boot_.insert(origin_url);
backing_store_map_[origin_url] = backing_store;
// If an in-memory database, bind lifetime to this factory instance.
if (open_in_memory)
@@ -245,7 +381,7 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBFactory::OpenBackingStore(
// All backing stores associated with this factory should be of the same
// type.
- DCHECK(session_only_backing_stores_.empty() || open_in_memory);
+ DCHECK_NE(session_only_backing_stores_.empty(), open_in_memory);
return backing_store;
}
@@ -253,14 +389,11 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBFactory::OpenBackingStore(
return 0;
}
-void IndexedDBFactory::Open(
- const base::string16& name,
- int64 version,
- int64 transaction_id,
- scoped_refptr<IndexedDBCallbacks> callbacks,
- scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
- const GURL& origin_url,
- const base::FilePath& data_directory) {
+void IndexedDBFactory::Open(const base::string16& name,
+ const IndexedDBPendingConnection& connection,
+ net::URLRequestContext* request_context,
+ const GURL& origin_url,
+ const base::FilePath& data_directory) {
IDB_TRACE("IndexedDBFactory::Open");
scoped_refptr<IndexedDBDatabase> database;
IndexedDBDatabase::Identifier unique_identifier(origin_url, name);
@@ -274,59 +407,73 @@ void IndexedDBFactory::Open(
scoped_refptr<IndexedDBBackingStore> backing_store =
OpenBackingStore(origin_url,
data_directory,
+ request_context,
&data_loss,
&data_loss_message,
&disk_full);
if (!backing_store) {
if (disk_full) {
- callbacks->OnError(
+ connection.callbacks->OnError(
IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionQuotaError,
ASCIIToUTF16(
"Encountered full disk while opening "
"backing store for indexedDB.open.")));
return;
}
- callbacks->OnError(IndexedDBDatabaseError(
+ connection.callbacks->OnError(IndexedDBDatabaseError(
blink::WebIDBDatabaseExceptionUnknownError,
ASCIIToUTF16(
"Internal error opening backing store for indexedDB.open.")));
return;
}
- database =
- IndexedDBDatabase::Create(name, backing_store, this, unique_identifier);
+ leveldb::Status s;
+ database = IndexedDBDatabase::Create(
+ name, backing_store, this, unique_identifier, &s);
if (!database) {
- callbacks->OnError(IndexedDBDatabaseError(
- blink::WebIDBDatabaseExceptionUnknownError,
- ASCIIToUTF16(
- "Internal error creating database backend for indexedDB.open.")));
+ DLOG(ERROR) << "Unable to create the database";
+ IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16(
+ "Internal error creating "
+ "database backend for "
+ "indexedDB.open."));
+ connection.callbacks->OnError(error);
+ if (leveldb_env::IsCorruption(s)) {
+ backing_store = NULL; // Closes the LevelDB so that it can be deleted
+ HandleBackingStoreCorruption(origin_url, error);
+ }
return;
}
} else {
database = it->second;
}
- database->OpenConnection(callbacks,
- database_callbacks,
- transaction_id,
- version,
- data_loss,
- data_loss_message);
+ if (data_loss != blink::WebIDBDataLossNone)
+ connection.callbacks->OnDataLoss(data_loss, data_loss_message);
- if (!was_open && database->ConnectionCount() > 0)
+ database->OpenConnection(connection);
+
+ if (!was_open && database->ConnectionCount() > 0) {
database_map_[unique_identifier] = database;
+ origin_dbs_.insert(std::make_pair(origin_url, database));
+ }
}
-std::vector<IndexedDBDatabase*> IndexedDBFactory::GetOpenDatabasesForOrigin(
- const GURL& origin_url) const {
- std::vector<IndexedDBDatabase*> result;
- for (IndexedDBDatabaseMap::const_iterator it = database_map_.begin();
- it != database_map_.end();
- ++it) {
- if (it->first.first == origin_url)
- result.push_back(it->second.get());
- }
- return result;
+std::pair<IndexedDBFactory::OriginDBMapIterator,
+ IndexedDBFactory::OriginDBMapIterator>
+IndexedDBFactory::GetOpenDatabasesForOrigin(const GURL& origin_url) const {
+ return origin_dbs_.equal_range(origin_url);
+}
+
+size_t IndexedDBFactory::GetConnectionCount(const GURL& origin_url) const {
+ size_t count(0);
+
+ std::pair<OriginDBMapIterator, OriginDBMapIterator> range =
+ GetOpenDatabasesForOrigin(origin_url);
+ for (OriginDBMapIterator it = range.first; it != range.second; ++it)
+ count += it->second->ConnectionCount();
+
+ return count;
}
} // namespace content
diff --git a/chromium/content/browser/indexed_db/indexed_db_factory.h b/chromium/content/browser/indexed_db/indexed_db_factory.h
index 24d48d9e9e6..c2177de7e56 100644
--- a/chromium/content/browser/indexed_db/indexed_db_factory.h
+++ b/chromium/content/browser/indexed_db/indexed_db_factory.h
@@ -7,6 +7,8 @@
#include <map>
#include <set>
+#include <string>
+#include <utility>
#include "base/basictypes.h"
#include "base/files/file_path.h"
@@ -18,50 +20,64 @@
#include "content/common/content_export.h"
#include "url/gurl.h"
+namespace net {
+class URLRequestContext;
+}
+
namespace content {
class IndexedDBBackingStore;
class IndexedDBContextImpl;
+struct IndexedDBPendingConnection;
class CONTENT_EXPORT IndexedDBFactory
: NON_EXPORTED_BASE(public base::RefCountedThreadSafe<IndexedDBFactory>) {
public:
+ typedef std::multimap<GURL, IndexedDBDatabase*> OriginDBMap;
+ typedef OriginDBMap::const_iterator OriginDBMapIterator;
+
explicit IndexedDBFactory(IndexedDBContextImpl* context);
- // Notifications from weak pointers.
void ReleaseDatabase(const IndexedDBDatabase::Identifier& identifier,
- const GURL& origin_url,
bool forcedClose);
void GetDatabaseNames(scoped_refptr<IndexedDBCallbacks> callbacks,
const GURL& origin_url,
- const base::FilePath& data_directory);
+ const base::FilePath& data_directory,
+ net::URLRequestContext* request_context);
void Open(const base::string16& name,
- int64 version,
- int64 transaction_id,
- scoped_refptr<IndexedDBCallbacks> callbacks,
- scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
+ const IndexedDBPendingConnection& connection,
+ net::URLRequestContext* request_context,
const GURL& origin_url,
const base::FilePath& data_directory);
void DeleteDatabase(const base::string16& name,
+ net::URLRequestContext* request_context,
scoped_refptr<IndexedDBCallbacks> callbacks,
const GURL& origin_url,
const base::FilePath& data_directory);
void HandleBackingStoreFailure(const GURL& origin_url);
+ void HandleBackingStoreCorruption(const GURL& origin_url,
+ const IndexedDBDatabaseError& error);
- // Iterates over all databases; for diagnostics only.
- std::vector<IndexedDBDatabase*> GetOpenDatabasesForOrigin(
+ std::pair<OriginDBMapIterator, OriginDBMapIterator> GetOpenDatabasesForOrigin(
const GURL& origin_url) const;
- // Called by IndexedDBContext after all connections are closed, to
- // ensure the backing store closed immediately.
void ForceClose(const GURL& origin_url);
// Called by the IndexedDBContext destructor so the factory can do cleanup.
void ContextDestroyed();
+ // Called by the IndexedDBActiveBlobRegistry.
+ virtual void ReportOutstandingBlobs(const GURL& origin_url,
+ bool blobs_outstanding);
+
+ // Called by an IndexedDBDatabase when it is actually deleted.
+ void DatabaseDeleted(const IndexedDBDatabase::Identifier& identifier);
+
+ size_t GetConnectionCount(const GURL& origin_url) const;
+
protected:
friend class base::RefCountedThreadSafe<IndexedDBFactory>;
@@ -70,12 +86,23 @@ class CONTENT_EXPORT IndexedDBFactory
virtual scoped_refptr<IndexedDBBackingStore> OpenBackingStore(
const GURL& origin_url,
const base::FilePath& data_directory,
+ net::URLRequestContext* request_context,
blink::WebIDBDataLoss* data_loss,
std::string* data_loss_reason,
bool* disk_full);
+ virtual scoped_refptr<IndexedDBBackingStore> OpenBackingStoreHelper(
+ const GURL& origin_url,
+ const base::FilePath& data_directory,
+ net::URLRequestContext* request_context,
+ blink::WebIDBDataLoss* data_loss,
+ std::string* data_loss_message,
+ bool* disk_full,
+ bool first_time);
+
void ReleaseBackingStore(const GURL& origin_url, bool immediate);
void CloseBackingStore(const GURL& origin_url);
+ IndexedDBContextImpl* context() const { return context_; }
private:
FRIEND_TEST_ALL_PREFIXES(IndexedDBFactoryTest,
@@ -102,18 +129,24 @@ class CONTENT_EXPORT IndexedDBFactory
const base::string16& name) const;
bool IsBackingStoreOpen(const GURL& origin_url) const;
bool IsBackingStorePendingClose(const GURL& origin_url) const;
+ void RemoveDatabaseFromMaps(const IndexedDBDatabase::Identifier& identifier);
IndexedDBContextImpl* context_;
typedef std::map<IndexedDBDatabase::Identifier,
- scoped_refptr<IndexedDBDatabase> > IndexedDBDatabaseMap;
+ IndexedDBDatabase*> IndexedDBDatabaseMap;
IndexedDBDatabaseMap database_map_;
+ OriginDBMap origin_dbs_;
typedef std::map<GURL, scoped_refptr<IndexedDBBackingStore> >
IndexedDBBackingStoreMap;
IndexedDBBackingStoreMap backing_store_map_;
std::set<scoped_refptr<IndexedDBBackingStore> > session_only_backing_stores_;
+ IndexedDBBackingStoreMap backing_stores_with_active_blobs_;
+ std::set<GURL> backends_opened_since_boot_;
+
+ DISALLOW_COPY_AND_ASSIGN(IndexedDBFactory);
};
} // namespace content
diff --git a/chromium/content/browser/indexed_db/indexed_db_factory_unittest.cc b/chromium/content/browser/indexed_db/indexed_db_factory_unittest.cc
index 7d958e71b61..975f9bcbcd3 100644
--- a/chromium/content/browser/indexed_db/indexed_db_factory_unittest.cc
+++ b/chromium/content/browser/indexed_db/indexed_db_factory_unittest.cc
@@ -9,7 +9,9 @@
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/test/test_simple_task_runner.h"
#include "content/browser/indexed_db/indexed_db_connection.h"
+#include "content/browser/indexed_db/indexed_db_context_impl.h"
#include "content/browser/indexed_db/mock_indexed_db_callbacks.h"
#include "content/browser/indexed_db/mock_indexed_db_database_callbacks.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -18,23 +20,16 @@
#include "url/gurl.h"
#include "webkit/common/database/database_identifier.h"
-namespace content {
-
-class IndexedDBFactoryTest : public testing::Test {
- public:
- IndexedDBFactoryTest() {}
+using base::ASCIIToUTF16;
- protected:
- // For timers to post events.
- base::MessageLoop loop_;
+namespace content {
- private:
- DISALLOW_COPY_AND_ASSIGN(IndexedDBFactoryTest);
-};
+namespace {
class MockIDBFactory : public IndexedDBFactory {
public:
- MockIDBFactory() : IndexedDBFactory(NULL) {}
+ explicit MockIDBFactory(IndexedDBContextImpl* context)
+ : IndexedDBFactory(context) {}
scoped_refptr<IndexedDBBackingStore> TestOpenBackingStore(
const GURL& origin,
const base::FilePath& data_directory) {
@@ -45,6 +40,7 @@ class MockIDBFactory : public IndexedDBFactory {
scoped_refptr<IndexedDBBackingStore> backing_store =
OpenBackingStore(origin,
data_directory,
+ NULL /* request_context */,
&data_loss,
&data_loss_message,
&disk_full);
@@ -63,28 +59,57 @@ class MockIDBFactory : public IndexedDBFactory {
private:
virtual ~MockIDBFactory() {}
+
+ DISALLOW_COPY_AND_ASSIGN(MockIDBFactory);
+};
+
+} // namespace
+
+class IndexedDBFactoryTest : public testing::Test {
+ public:
+ IndexedDBFactoryTest() {
+ task_runner_ = new base::TestSimpleTaskRunner();
+ context_ = new IndexedDBContextImpl(base::FilePath(),
+ NULL /* special_storage_policy */,
+ NULL /* quota_manager_proxy */,
+ task_runner_.get());
+ idb_factory_ = new MockIDBFactory(context_.get());
+ }
+
+ protected:
+ // For timers to post events.
+ base::MessageLoop loop_;
+
+ MockIDBFactory* factory() const { return idb_factory_.get(); }
+ void clear_factory() { idb_factory_ = NULL; }
+ IndexedDBContextImpl* context() const { return context_.get(); }
+
+ private:
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+ scoped_refptr<IndexedDBContextImpl> context_;
+ scoped_refptr<MockIDBFactory> idb_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(IndexedDBFactoryTest);
};
TEST_F(IndexedDBFactoryTest, BackingStoreLifetime) {
GURL origin1("http://localhost:81");
GURL origin2("http://localhost:82");
- scoped_refptr<MockIDBFactory> factory = new MockIDBFactory();
-
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
scoped_refptr<IndexedDBBackingStore> disk_store1 =
- factory->TestOpenBackingStore(origin1, temp_directory.path());
+ factory()->TestOpenBackingStore(origin1, temp_directory.path());
scoped_refptr<IndexedDBBackingStore> disk_store2 =
- factory->TestOpenBackingStore(origin1, temp_directory.path());
+ factory()->TestOpenBackingStore(origin1, temp_directory.path());
EXPECT_EQ(disk_store1.get(), disk_store2.get());
scoped_refptr<IndexedDBBackingStore> disk_store3 =
- factory->TestOpenBackingStore(origin2, temp_directory.path());
+ factory()->TestOpenBackingStore(origin2, temp_directory.path());
- factory->TestCloseBackingStore(disk_store1);
- factory->TestCloseBackingStore(disk_store3);
+ factory()->TestCloseBackingStore(disk_store1);
+ factory()->TestCloseBackingStore(disk_store3);
EXPECT_FALSE(disk_store1->HasOneRef());
EXPECT_FALSE(disk_store2->HasOneRef());
@@ -97,30 +122,28 @@ TEST_F(IndexedDBFactoryTest, BackingStoreLifetime) {
TEST_F(IndexedDBFactoryTest, BackingStoreLazyClose) {
GURL origin("http://localhost:81");
- scoped_refptr<MockIDBFactory> factory = new MockIDBFactory();
-
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
scoped_refptr<IndexedDBBackingStore> store =
- factory->TestOpenBackingStore(origin, temp_directory.path());
+ factory()->TestOpenBackingStore(origin, temp_directory.path());
// Give up the local refptr so that the factory has the only
// outstanding reference.
IndexedDBBackingStore* store_ptr = store.get();
store = NULL;
EXPECT_FALSE(store_ptr->close_timer()->IsRunning());
- factory->TestReleaseBackingStore(store_ptr, false);
+ factory()->TestReleaseBackingStore(store_ptr, false);
EXPECT_TRUE(store_ptr->close_timer()->IsRunning());
- factory->TestOpenBackingStore(origin, temp_directory.path());
+ factory()->TestOpenBackingStore(origin, temp_directory.path());
EXPECT_FALSE(store_ptr->close_timer()->IsRunning());
- factory->TestReleaseBackingStore(store_ptr, false);
+ factory()->TestReleaseBackingStore(store_ptr, false);
EXPECT_TRUE(store_ptr->close_timer()->IsRunning());
// Take back a ref ptr and ensure that the actual close
// stops a running timer.
store = store_ptr;
- factory->TestCloseBackingStore(store_ptr);
+ factory()->TestCloseBackingStore(store_ptr);
EXPECT_FALSE(store_ptr->close_timer()->IsRunning());
}
@@ -128,25 +151,24 @@ TEST_F(IndexedDBFactoryTest, MemoryBackingStoreLifetime) {
GURL origin1("http://localhost:81");
GURL origin2("http://localhost:82");
- scoped_refptr<MockIDBFactory> factory = new MockIDBFactory();
scoped_refptr<IndexedDBBackingStore> mem_store1 =
- factory->TestOpenBackingStore(origin1, base::FilePath());
+ factory()->TestOpenBackingStore(origin1, base::FilePath());
scoped_refptr<IndexedDBBackingStore> mem_store2 =
- factory->TestOpenBackingStore(origin1, base::FilePath());
+ factory()->TestOpenBackingStore(origin1, base::FilePath());
EXPECT_EQ(mem_store1.get(), mem_store2.get());
scoped_refptr<IndexedDBBackingStore> mem_store3 =
- factory->TestOpenBackingStore(origin2, base::FilePath());
+ factory()->TestOpenBackingStore(origin2, base::FilePath());
- factory->TestCloseBackingStore(mem_store1);
- factory->TestCloseBackingStore(mem_store3);
+ factory()->TestCloseBackingStore(mem_store1);
+ factory()->TestCloseBackingStore(mem_store3);
EXPECT_FALSE(mem_store1->HasOneRef());
EXPECT_FALSE(mem_store2->HasOneRef());
EXPECT_FALSE(mem_store3->HasOneRef());
- factory = NULL;
+ clear_factory();
EXPECT_FALSE(mem_store1->HasOneRef()); // mem_store1 and 2
EXPECT_FALSE(mem_store2->HasOneRef()); // mem_store1 and 2
EXPECT_TRUE(mem_store3->HasOneRef());
@@ -159,38 +181,41 @@ TEST_F(IndexedDBFactoryTest, RejectLongOrigins) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
const base::FilePath base_path = temp_directory.path();
- scoped_refptr<MockIDBFactory> factory = new MockIDBFactory();
- int limit = file_util::GetMaximumPathComponentLength(base_path);
+ int limit = base::GetMaximumPathComponentLength(base_path);
EXPECT_GT(limit, 0);
std::string origin(limit + 1, 'x');
GURL too_long_origin("http://" + origin + ":81/");
scoped_refptr<IndexedDBBackingStore> diskStore1 =
- factory->TestOpenBackingStore(too_long_origin, base_path);
+ factory()->TestOpenBackingStore(too_long_origin, base_path);
EXPECT_FALSE(diskStore1);
GURL ok_origin("http://someorigin.com:82/");
scoped_refptr<IndexedDBBackingStore> diskStore2 =
- factory->TestOpenBackingStore(ok_origin, base_path);
+ factory()->TestOpenBackingStore(ok_origin, base_path);
EXPECT_TRUE(diskStore2);
}
class DiskFullFactory : public IndexedDBFactory {
public:
- DiskFullFactory() : IndexedDBFactory(NULL) {}
+ explicit DiskFullFactory(IndexedDBContextImpl* context)
+ : IndexedDBFactory(context) {}
private:
virtual ~DiskFullFactory() {}
virtual scoped_refptr<IndexedDBBackingStore> OpenBackingStore(
const GURL& origin_url,
const base::FilePath& data_directory,
+ net::URLRequestContext* request_context,
blink::WebIDBDataLoss* data_loss,
std::string* data_loss_message,
bool* disk_full) OVERRIDE {
*disk_full = true;
return scoped_refptr<IndexedDBBackingStore>();
}
+
+ DISALLOW_COPY_AND_ASSIGN(DiskFullFactory);
};
class LookingForQuotaErrorMockCallbacks : public IndexedDBCallbacks {
@@ -201,28 +226,37 @@ class LookingForQuotaErrorMockCallbacks : public IndexedDBCallbacks {
error_called_ = true;
EXPECT_EQ(blink::WebIDBDatabaseExceptionQuotaError, error.code());
}
+ bool error_called() const { return error_called_; }
private:
- virtual ~LookingForQuotaErrorMockCallbacks() { EXPECT_TRUE(error_called_); }
+ virtual ~LookingForQuotaErrorMockCallbacks() {}
bool error_called_;
+
+ DISALLOW_COPY_AND_ASSIGN(LookingForQuotaErrorMockCallbacks);
};
TEST_F(IndexedDBFactoryTest, QuotaErrorOnDiskFull) {
const GURL origin("http://localhost:81");
+ base::ScopedTempDir temp_directory;
+ ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
- scoped_refptr<DiskFullFactory> factory = new DiskFullFactory;
+ scoped_refptr<DiskFullFactory> factory = new DiskFullFactory(context());
scoped_refptr<LookingForQuotaErrorMockCallbacks> callbacks =
new LookingForQuotaErrorMockCallbacks;
scoped_refptr<IndexedDBDatabaseCallbacks> dummy_database_callbacks =
new IndexedDBDatabaseCallbacks(NULL, 0, 0);
const base::string16 name(ASCIIToUTF16("name"));
+ IndexedDBPendingConnection connection(callbacks,
+ dummy_database_callbacks,
+ 0, /* child_process_id */
+ 2, /* transaction_id */
+ 1 /* version */);
factory->Open(name,
- 1, /* version */
- 2, /* transaction_id */
- callbacks,
- dummy_database_callbacks,
+ connection,
+ NULL /* request_context */,
origin,
- base::FilePath(FILE_PATH_LITERAL("/dummy")));
+ temp_directory.path());
+ EXPECT_TRUE(callbacks->error_called());
}
TEST_F(IndexedDBFactoryTest, BackingStoreReleasedOnForcedClose) {
@@ -231,29 +265,31 @@ TEST_F(IndexedDBFactoryTest, BackingStoreReleasedOnForcedClose) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
- scoped_refptr<IndexedDBFactory> factory = new IndexedDBFactory(NULL);
-
scoped_refptr<MockIndexedDBCallbacks> callbacks(new MockIndexedDBCallbacks());
scoped_refptr<MockIndexedDBDatabaseCallbacks> db_callbacks(
new MockIndexedDBDatabaseCallbacks());
const int64 transaction_id = 1;
- factory->Open(ASCIIToUTF16("db"),
- IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION,
- transaction_id,
- callbacks,
- db_callbacks,
- origin,
- temp_directory.path());
+ IndexedDBPendingConnection connection(
+ callbacks,
+ db_callbacks,
+ 0, /* child_process_id */
+ transaction_id,
+ IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
+ factory()->Open(ASCIIToUTF16("db"),
+ connection,
+ NULL /* request_context */,
+ origin,
+ temp_directory.path());
EXPECT_TRUE(callbacks->connection());
- EXPECT_TRUE(factory->IsBackingStoreOpen(origin));
- EXPECT_FALSE(factory->IsBackingStorePendingClose(origin));
+ EXPECT_TRUE(factory()->IsBackingStoreOpen(origin));
+ EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin));
callbacks->connection()->ForceClose();
- EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
- EXPECT_FALSE(factory->IsBackingStorePendingClose(origin));
+ EXPECT_FALSE(factory()->IsBackingStoreOpen(origin));
+ EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin));
}
TEST_F(IndexedDBFactoryTest, BackingStoreReleaseDelayedOnClose) {
@@ -262,40 +298,42 @@ TEST_F(IndexedDBFactoryTest, BackingStoreReleaseDelayedOnClose) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
- scoped_refptr<IndexedDBFactory> factory = new IndexedDBFactory(NULL);
-
scoped_refptr<MockIndexedDBCallbacks> callbacks(new MockIndexedDBCallbacks());
scoped_refptr<MockIndexedDBDatabaseCallbacks> db_callbacks(
new MockIndexedDBDatabaseCallbacks());
const int64 transaction_id = 1;
- factory->Open(ASCIIToUTF16("db"),
- IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION,
- transaction_id,
- callbacks,
- db_callbacks,
- origin,
- temp_directory.path());
+ IndexedDBPendingConnection connection(
+ callbacks,
+ db_callbacks,
+ 0, /* child_process_id */
+ transaction_id,
+ IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
+ factory()->Open(ASCIIToUTF16("db"),
+ connection,
+ NULL /* request_context */,
+ origin,
+ temp_directory.path());
EXPECT_TRUE(callbacks->connection());
IndexedDBBackingStore* store =
callbacks->connection()->database()->backing_store();
EXPECT_FALSE(store->HasOneRef()); // Factory and database.
- EXPECT_TRUE(factory->IsBackingStoreOpen(origin));
+ EXPECT_TRUE(factory()->IsBackingStoreOpen(origin));
callbacks->connection()->Close();
EXPECT_TRUE(store->HasOneRef()); // Factory.
- EXPECT_TRUE(factory->IsBackingStoreOpen(origin));
- EXPECT_TRUE(factory->IsBackingStorePendingClose(origin));
+ EXPECT_TRUE(factory()->IsBackingStoreOpen(origin));
+ EXPECT_TRUE(factory()->IsBackingStorePendingClose(origin));
EXPECT_TRUE(store->close_timer()->IsRunning());
// Take a ref so it won't be destroyed out from under the test.
scoped_refptr<IndexedDBBackingStore> store_ref = store;
// Now simulate shutdown, which should stop the timer.
- factory->ContextDestroyed();
+ factory()->ContextDestroyed();
EXPECT_TRUE(store->HasOneRef()); // Local.
EXPECT_FALSE(store->close_timer()->IsRunning());
- EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
- EXPECT_FALSE(factory->IsBackingStorePendingClose(origin));
+ EXPECT_FALSE(factory()->IsBackingStoreOpen(origin));
+ EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin));
}
TEST_F(IndexedDBFactoryTest, DeleteDatabaseClosesBackingStore) {
@@ -304,25 +342,25 @@ TEST_F(IndexedDBFactoryTest, DeleteDatabaseClosesBackingStore) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
- scoped_refptr<IndexedDBFactory> factory = new IndexedDBFactory(NULL);
- EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
+ EXPECT_FALSE(factory()->IsBackingStoreOpen(origin));
const bool expect_connection = false;
scoped_refptr<MockIndexedDBCallbacks> callbacks(
new MockIndexedDBCallbacks(expect_connection));
- factory->DeleteDatabase(ASCIIToUTF16("db"),
- callbacks,
- origin,
- temp_directory.path());
+ factory()->DeleteDatabase(ASCIIToUTF16("db"),
+ NULL /* request_context */,
+ callbacks,
+ origin,
+ temp_directory.path());
- EXPECT_TRUE(factory->IsBackingStoreOpen(origin));
- EXPECT_FALSE(factory->IsBackingStorePendingClose(origin));
+ EXPECT_TRUE(factory()->IsBackingStoreOpen(origin));
+ EXPECT_TRUE(factory()->IsBackingStorePendingClose(origin));
// Now simulate shutdown, which should stop the timer.
- factory->ContextDestroyed();
+ factory()->ContextDestroyed();
- EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
- EXPECT_FALSE(factory->IsBackingStorePendingClose(origin));
+ EXPECT_FALSE(factory()->IsBackingStoreOpen(origin));
+ EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin));
}
TEST_F(IndexedDBFactoryTest, GetDatabaseNamesClosesBackingStore) {
@@ -331,24 +369,22 @@ TEST_F(IndexedDBFactoryTest, GetDatabaseNamesClosesBackingStore) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
- scoped_refptr<IndexedDBFactory> factory = new IndexedDBFactory(NULL);
- EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
+ EXPECT_FALSE(factory()->IsBackingStoreOpen(origin));
const bool expect_connection = false;
scoped_refptr<MockIndexedDBCallbacks> callbacks(
new MockIndexedDBCallbacks(expect_connection));
- factory->GetDatabaseNames(callbacks,
- origin,
- temp_directory.path());
+ factory()->GetDatabaseNames(
+ callbacks, origin, temp_directory.path(), NULL /* request_context */);
- EXPECT_TRUE(factory->IsBackingStoreOpen(origin));
- EXPECT_FALSE(factory->IsBackingStorePendingClose(origin));
+ EXPECT_TRUE(factory()->IsBackingStoreOpen(origin));
+ EXPECT_TRUE(factory()->IsBackingStorePendingClose(origin));
// Now simulate shutdown, which should stop the timer.
- factory->ContextDestroyed();
+ factory()->ContextDestroyed();
- EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
- EXPECT_FALSE(factory->IsBackingStorePendingClose(origin));
+ EXPECT_FALSE(factory()->IsBackingStoreOpen(origin));
+ EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin));
}
TEST_F(IndexedDBFactoryTest, ForceCloseReleasesBackingStore) {
@@ -357,40 +393,44 @@ TEST_F(IndexedDBFactoryTest, ForceCloseReleasesBackingStore) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
- scoped_refptr<IndexedDBFactory> factory = new IndexedDBFactory(NULL);
-
scoped_refptr<MockIndexedDBCallbacks> callbacks(new MockIndexedDBCallbacks());
scoped_refptr<MockIndexedDBDatabaseCallbacks> db_callbacks(
new MockIndexedDBDatabaseCallbacks());
const int64 transaction_id = 1;
- factory->Open(ASCIIToUTF16("db"),
- IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION,
- transaction_id,
- callbacks,
- db_callbacks,
- origin,
- temp_directory.path());
+ IndexedDBPendingConnection connection(
+ callbacks,
+ db_callbacks,
+ 0, /* child_process_id */
+ transaction_id,
+ IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
+ factory()->Open(ASCIIToUTF16("db"),
+ connection,
+ NULL /* request_context */,
+ origin,
+ temp_directory.path());
EXPECT_TRUE(callbacks->connection());
- EXPECT_TRUE(factory->IsBackingStoreOpen(origin));
- EXPECT_FALSE(factory->IsBackingStorePendingClose(origin));
+ EXPECT_TRUE(factory()->IsBackingStoreOpen(origin));
+ EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin));
callbacks->connection()->Close();
- EXPECT_TRUE(factory->IsBackingStoreOpen(origin));
- EXPECT_TRUE(factory->IsBackingStorePendingClose(origin));
+ EXPECT_TRUE(factory()->IsBackingStoreOpen(origin));
+ EXPECT_TRUE(factory()->IsBackingStorePendingClose(origin));
- factory->ForceClose(origin);
+ factory()->ForceClose(origin);
- EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
- EXPECT_FALSE(factory->IsBackingStorePendingClose(origin));
+ EXPECT_FALSE(factory()->IsBackingStoreOpen(origin));
+ EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin));
// Ensure it is safe if the store is not open.
- factory->ForceClose(origin);
+ factory()->ForceClose(origin);
}
class UpgradeNeededCallbacks : public MockIndexedDBCallbacks {
public:
+ UpgradeNeededCallbacks() {}
+
virtual void OnSuccess(scoped_ptr<IndexedDBConnection> connection,
const IndexedDBDatabaseMetadata& metadata) OVERRIDE {
EXPECT_TRUE(connection_.get());
@@ -400,14 +440,15 @@ class UpgradeNeededCallbacks : public MockIndexedDBCallbacks {
virtual void OnUpgradeNeeded(
int64 old_version,
scoped_ptr<IndexedDBConnection> connection,
- const content::IndexedDBDatabaseMetadata& metadata,
- blink::WebIDBDataLoss data_loss,
- std::string data_loss_message) OVERRIDE {
+ const content::IndexedDBDatabaseMetadata& metadata) OVERRIDE {
connection_ = connection.Pass();
}
protected:
virtual ~UpgradeNeededCallbacks() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(UpgradeNeededCallbacks);
};
class ErrorCallbacks : public MockIndexedDBCallbacks {
@@ -417,12 +458,13 @@ class ErrorCallbacks : public MockIndexedDBCallbacks {
virtual void OnError(const IndexedDBDatabaseError& error) OVERRIDE {
saw_error_= true;
}
-
- protected:
- virtual ~ErrorCallbacks() { EXPECT_TRUE(saw_error_); }
+ bool saw_error() const { return saw_error_; }
private:
+ virtual ~ErrorCallbacks() {}
bool saw_error_;
+
+ DISALLOW_COPY_AND_ASSIGN(ErrorCallbacks);
};
TEST_F(IndexedDBFactoryTest, DatabaseFailedOpen) {
@@ -431,7 +473,6 @@ TEST_F(IndexedDBFactoryTest, DatabaseFailedOpen) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
- scoped_refptr<IndexedDBFactory> factory = new IndexedDBFactory(NULL);
const base::string16 db_name(ASCIIToUTF16("db"));
const int64 db_version = 2;
const int64 transaction_id = 1;
@@ -442,14 +483,17 @@ TEST_F(IndexedDBFactoryTest, DatabaseFailedOpen) {
{
scoped_refptr<MockIndexedDBCallbacks> callbacks(
new UpgradeNeededCallbacks());
- factory->Open(db_name,
- db_version,
- transaction_id,
- callbacks,
- db_callbacks,
- origin,
- temp_directory.path());
- EXPECT_TRUE(factory->IsDatabaseOpen(origin, db_name));
+ IndexedDBPendingConnection connection(callbacks,
+ db_callbacks,
+ 0, /* child_process_id */
+ transaction_id,
+ db_version);
+ factory()->Open(db_name,
+ connection,
+ NULL /* request_context */,
+ origin,
+ temp_directory.path());
+ EXPECT_TRUE(factory()->IsDatabaseOpen(origin, db_name));
// Pump the message loop so the upgrade transaction can run.
base::MessageLoop::current()->RunUntilIdle();
@@ -457,25 +501,29 @@ TEST_F(IndexedDBFactoryTest, DatabaseFailedOpen) {
callbacks->connection()->database()->Commit(transaction_id);
callbacks->connection()->Close();
- EXPECT_FALSE(factory->IsDatabaseOpen(origin, db_name));
+ EXPECT_FALSE(factory()->IsDatabaseOpen(origin, db_name));
}
// Open at version < 2, which will fail; ensure factory doesn't retain
// the database object.
{
- scoped_refptr<IndexedDBCallbacks> callbacks(new ErrorCallbacks());
- factory->Open(db_name,
- db_version - 1,
- transaction_id,
- callbacks,
- db_callbacks,
- origin,
- temp_directory.path());
- EXPECT_FALSE(factory->IsDatabaseOpen(origin, db_name));
+ scoped_refptr<ErrorCallbacks> callbacks(new ErrorCallbacks());
+ IndexedDBPendingConnection connection(callbacks,
+ db_callbacks,
+ 0, /* child_process_id */
+ transaction_id,
+ db_version - 1);
+ factory()->Open(db_name,
+ connection,
+ NULL /* request_context */,
+ origin,
+ temp_directory.path());
+ EXPECT_TRUE(callbacks->saw_error());
+ EXPECT_FALSE(factory()->IsDatabaseOpen(origin, db_name));
}
// Terminate all pending-close timers.
- factory->ForceClose(origin);
+ factory()->ForceClose(origin);
}
} // namespace content
diff --git a/chromium/content/browser/indexed_db/indexed_db_fake_backing_store.cc b/chromium/content/browser/indexed_db/indexed_db_fake_backing_store.cc
index 3efd410727c..461c0808c60 100644
--- a/chromium/content/browser/indexed_db/indexed_db_fake_backing_store.cc
+++ b/chromium/content/browser/indexed_db/indexed_db_fake_backing_store.cc
@@ -4,116 +4,167 @@
#include "content/browser/indexed_db/indexed_db_fake_backing_store.h"
+#include "base/files/file_path.h"
#include "base/memory/scoped_ptr.h"
namespace content {
+IndexedDBFakeBackingStore::IndexedDBFakeBackingStore()
+ : IndexedDBBackingStore(NULL /* indexed_db_factory */,
+ GURL("http://localhost:81"),
+ base::FilePath(),
+ NULL /* request_context */,
+ scoped_ptr<LevelDBDatabase>(),
+ scoped_ptr<LevelDBComparator>(),
+ NULL /* task_runner */) {
+}
+IndexedDBFakeBackingStore::IndexedDBFakeBackingStore(
+ IndexedDBFactory* factory,
+ base::TaskRunner* task_runner)
+ : IndexedDBBackingStore(factory,
+ GURL("http://localhost:81"),
+ base::FilePath(),
+ NULL /* request_context */,
+ scoped_ptr<LevelDBDatabase>(),
+ scoped_ptr<LevelDBComparator>(),
+ task_runner) {
+}
IndexedDBFakeBackingStore::~IndexedDBFakeBackingStore() {}
-std::vector<base::string16> IndexedDBFakeBackingStore::GetDatabaseNames() {
+std::vector<base::string16> IndexedDBFakeBackingStore::GetDatabaseNames(
+ leveldb::Status* s) {
+ *s = leveldb::Status::OK();
return std::vector<base::string16>();
}
-bool IndexedDBFakeBackingStore::GetIDBDatabaseMetaData(
+leveldb::Status IndexedDBFakeBackingStore::GetIDBDatabaseMetaData(
const base::string16& name,
IndexedDBDatabaseMetadata*,
bool* found) {
- return true;
+ return leveldb::Status::OK();
}
-bool IndexedDBFakeBackingStore::CreateIDBDatabaseMetaData(
+leveldb::Status IndexedDBFakeBackingStore::CreateIDBDatabaseMetaData(
const base::string16& name,
const base::string16& version,
int64 int_version,
int64* row_id) {
- return true;
+ return leveldb::Status::OK();
}
bool IndexedDBFakeBackingStore::UpdateIDBDatabaseIntVersion(Transaction*,
int64 row_id,
int64 version) {
return false;
}
-bool IndexedDBFakeBackingStore::DeleteDatabase(const base::string16& name) {
- return true;
+leveldb::Status IndexedDBFakeBackingStore::DeleteDatabase(
+ const base::string16& name) {
+ return leveldb::Status::OK();
}
-bool IndexedDBFakeBackingStore::CreateObjectStore(Transaction*,
- int64 database_id,
- int64 object_store_id,
- const base::string16& name,
- const IndexedDBKeyPath&,
- bool auto_increment) {
- return false;
+leveldb::Status IndexedDBFakeBackingStore::CreateObjectStore(
+ Transaction*,
+ int64 database_id,
+ int64 object_store_id,
+ const base::string16& name,
+ const IndexedDBKeyPath&,
+ bool auto_increment) {
+ return leveldb::Status::OK();
}
-bool IndexedDBFakeBackingStore::ClearObjectStore(Transaction*,
- int64 database_id,
- int64 object_store_id) {
- return false;
+leveldb::Status IndexedDBFakeBackingStore::DeleteObjectStore(
+ Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id) {
+ return leveldb::Status::OK();
}
-bool IndexedDBFakeBackingStore::DeleteRecord(Transaction*,
- int64 database_id,
- int64 object_store_id,
- const RecordIdentifier&) {
- return false;
+
+leveldb::Status IndexedDBFakeBackingStore::PutRecord(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ const IndexedDBKey& key,
+ IndexedDBValue& value,
+ ScopedVector<webkit_blob::BlobDataHandle>* handles,
+ RecordIdentifier* record) {
+ return leveldb::Status::OK();
+}
+
+leveldb::Status IndexedDBFakeBackingStore::ClearObjectStore(
+ Transaction*,
+ int64 database_id,
+ int64 object_store_id) {
+ return leveldb::Status::OK();
+}
+leveldb::Status IndexedDBFakeBackingStore::DeleteRecord(
+ Transaction*,
+ int64 database_id,
+ int64 object_store_id,
+ const RecordIdentifier&) {
+ return leveldb::Status::OK();
}
-bool IndexedDBFakeBackingStore::GetKeyGeneratorCurrentNumber(
+leveldb::Status IndexedDBFakeBackingStore::GetKeyGeneratorCurrentNumber(
Transaction*,
int64 database_id,
int64 object_store_id,
int64* current_number) {
- return true;
+ return leveldb::Status::OK();
}
-bool IndexedDBFakeBackingStore::MaybeUpdateKeyGeneratorCurrentNumber(
+leveldb::Status IndexedDBFakeBackingStore::MaybeUpdateKeyGeneratorCurrentNumber(
Transaction*,
int64 database_id,
int64 object_store_id,
int64 new_number,
bool check_current) {
- return true;
+ return leveldb::Status::OK();
}
-bool IndexedDBFakeBackingStore::KeyExistsInObjectStore(
+leveldb::Status IndexedDBFakeBackingStore::KeyExistsInObjectStore(
Transaction*,
int64 database_id,
int64 object_store_id,
const IndexedDBKey&,
RecordIdentifier* found_record_identifier,
bool* found) {
- return true;
+ return leveldb::Status::OK();
}
-bool IndexedDBFakeBackingStore::CreateIndex(Transaction*,
- int64 database_id,
- int64 object_store_id,
- int64 index_id,
- const base::string16& name,
- const IndexedDBKeyPath&,
- bool is_unique,
- bool is_multi_entry) {
- return false;
+leveldb::Status IndexedDBFakeBackingStore::CreateIndex(
+ Transaction*,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ const base::string16& name,
+ const IndexedDBKeyPath&,
+ bool is_unique,
+ bool is_multi_entry) {
+ return leveldb::Status::OK();
}
-bool IndexedDBFakeBackingStore::DeleteIndex(Transaction*,
- int64 database_id,
- int64 object_store_id,
- int64 index_id) {
- return false;
+leveldb::Status IndexedDBFakeBackingStore::DeleteIndex(Transaction*,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id) {
+ return leveldb::Status::OK();
}
-bool IndexedDBFakeBackingStore::PutIndexDataForRecord(Transaction*,
- int64 database_id,
- int64 object_store_id,
- int64 index_id,
- const IndexedDBKey&,
- const RecordIdentifier&) {
- return false;
+leveldb::Status IndexedDBFakeBackingStore::PutIndexDataForRecord(
+ Transaction*,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ const IndexedDBKey&,
+ const RecordIdentifier&) {
+ return leveldb::Status::OK();
}
+void IndexedDBFakeBackingStore::ReportBlobUnused(int64 database_id,
+ int64 blob_key) {}
+
scoped_ptr<IndexedDBBackingStore::Cursor>
IndexedDBFakeBackingStore::OpenObjectStoreKeyCursor(
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
int64 object_store_id,
const IndexedDBKeyRange& key_range,
- indexed_db::CursorDirection) {
+ indexed_db::CursorDirection,
+ leveldb::Status* s) {
return scoped_ptr<IndexedDBBackingStore::Cursor>();
}
scoped_ptr<IndexedDBBackingStore::Cursor>
@@ -122,7 +173,8 @@ IndexedDBFakeBackingStore::OpenObjectStoreCursor(
int64 database_id,
int64 object_store_id,
const IndexedDBKeyRange& key_range,
- indexed_db::CursorDirection) {
+ indexed_db::CursorDirection,
+ leveldb::Status* s) {
return scoped_ptr<IndexedDBBackingStore::Cursor>();
}
scoped_ptr<IndexedDBBackingStore::Cursor>
@@ -132,7 +184,8 @@ IndexedDBFakeBackingStore::OpenIndexKeyCursor(
int64 object_store_id,
int64 index_id,
const IndexedDBKeyRange& key_range,
- indexed_db::CursorDirection) {
+ indexed_db::CursorDirection,
+ leveldb::Status* s) {
return scoped_ptr<IndexedDBBackingStore::Cursor>();
}
scoped_ptr<IndexedDBBackingStore::Cursor>
@@ -142,14 +195,24 @@ IndexedDBFakeBackingStore::OpenIndexCursor(
int64 object_store_id,
int64 index_id,
const IndexedDBKeyRange& key_range,
- indexed_db::CursorDirection) {
+ indexed_db::CursorDirection,
+ leveldb::Status* s) {
return scoped_ptr<IndexedDBBackingStore::Cursor>();
}
-IndexedDBFakeBackingStore::FakeTransaction::FakeTransaction(bool result)
- : IndexedDBBackingStore::Transaction(NULL), result_(result) {}
+IndexedDBFakeBackingStore::FakeTransaction::FakeTransaction(
+ leveldb::Status result)
+ : IndexedDBBackingStore::Transaction(NULL), result_(result) {
+}
void IndexedDBFakeBackingStore::FakeTransaction::Begin() {}
-bool IndexedDBFakeBackingStore::FakeTransaction::Commit() { return result_; }
+leveldb::Status IndexedDBFakeBackingStore::FakeTransaction::CommitPhaseOne(
+ scoped_refptr<BlobWriteCallback> callback) {
+ callback->Run(true);
+ return leveldb::Status::OK();
+}
+leveldb::Status IndexedDBFakeBackingStore::FakeTransaction::CommitPhaseTwo() {
+ return result_;
+}
void IndexedDBFakeBackingStore::FakeTransaction::Rollback() {}
} // namespace content
diff --git a/chromium/content/browser/indexed_db/indexed_db_fake_backing_store.h b/chromium/content/browser/indexed_db/indexed_db_fake_backing_store.h
index 14b956244d0..fef28af3bd9 100644
--- a/chromium/content/browser/indexed_db/indexed_db_fake_backing_store.h
+++ b/chromium/content/browser/indexed_db/indexed_db_fake_backing_store.h
@@ -9,118 +9,150 @@
#include "content/browser/indexed_db/indexed_db_backing_store.h"
+namespace base {
+class TaskRunner;
+}
+
namespace content {
+class IndexedDBFactory;
+
class IndexedDBFakeBackingStore : public IndexedDBBackingStore {
public:
- IndexedDBFakeBackingStore()
- : IndexedDBBackingStore(GURL("http://localhost:81"),
- scoped_ptr<LevelDBDatabase>(),
- scoped_ptr<LevelDBComparator>()) {}
- virtual std::vector<base::string16> GetDatabaseNames() OVERRIDE;
- virtual bool GetIDBDatabaseMetaData(const base::string16& name,
- IndexedDBDatabaseMetadata*,
- bool* found) OVERRIDE;
- virtual bool CreateIDBDatabaseMetaData(const base::string16& name,
- const base::string16& version,
- int64 int_version,
- int64* row_id) OVERRIDE;
+ IndexedDBFakeBackingStore();
+ IndexedDBFakeBackingStore(IndexedDBFactory* factory,
+ base::TaskRunner* task_runner);
+ virtual std::vector<base::string16> GetDatabaseNames(leveldb::Status* s)
+ OVERRIDE;
+ virtual leveldb::Status GetIDBDatabaseMetaData(const base::string16& name,
+ IndexedDBDatabaseMetadata*,
+ bool* found) OVERRIDE;
+ virtual leveldb::Status CreateIDBDatabaseMetaData(
+ const base::string16& name,
+ const base::string16& version,
+ int64 int_version,
+ int64* row_id) OVERRIDE;
virtual bool UpdateIDBDatabaseIntVersion(Transaction*,
int64 row_id,
int64 version) OVERRIDE;
- virtual bool DeleteDatabase(const base::string16& name) OVERRIDE;
-
- virtual bool CreateObjectStore(Transaction*,
- int64 database_id,
- int64 object_store_id,
- const base::string16& name,
- const IndexedDBKeyPath&,
- bool auto_increment) OVERRIDE;
-
- virtual bool ClearObjectStore(Transaction*,
- int64 database_id,
- int64 object_store_id) OVERRIDE;
- virtual bool DeleteRecord(Transaction*,
- int64 database_id,
- int64 object_store_id,
- const RecordIdentifier&) OVERRIDE;
- virtual bool GetKeyGeneratorCurrentNumber(Transaction*,
+ virtual leveldb::Status DeleteDatabase(const base::string16& name) OVERRIDE;
+
+ virtual leveldb::Status CreateObjectStore(Transaction*,
int64 database_id,
int64 object_store_id,
- int64* current_number) OVERRIDE;
- virtual bool MaybeUpdateKeyGeneratorCurrentNumber(Transaction*,
- int64 database_id,
- int64 object_store_id,
- int64 new_number,
- bool check_current)
+ const base::string16& name,
+ const IndexedDBKeyPath&,
+ bool auto_increment) OVERRIDE;
+
+ virtual leveldb::Status DeleteObjectStore(Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id) OVERRIDE;
+
+ virtual leveldb::Status PutRecord(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ const IndexedDBKey& key,
+ IndexedDBValue& value,
+ ScopedVector<webkit_blob::BlobDataHandle>* handles,
+ RecordIdentifier* record) OVERRIDE;
+
+ virtual leveldb::Status ClearObjectStore(Transaction*,
+ int64 database_id,
+ int64 object_store_id) OVERRIDE;
+ virtual leveldb::Status DeleteRecord(Transaction*,
+ int64 database_id,
+ int64 object_store_id,
+ const RecordIdentifier&) OVERRIDE;
+ virtual leveldb::Status GetKeyGeneratorCurrentNumber(Transaction*,
+ int64 database_id,
+ int64 object_store_id,
+ int64* current_number)
OVERRIDE;
- virtual bool KeyExistsInObjectStore(Transaction*,
+ virtual leveldb::Status MaybeUpdateKeyGeneratorCurrentNumber(
+ Transaction*,
+ int64 database_id,
+ int64 object_store_id,
+ int64 new_number,
+ bool check_current) OVERRIDE;
+ virtual leveldb::Status KeyExistsInObjectStore(
+ Transaction*,
+ int64 database_id,
+ int64 object_store_id,
+ const IndexedDBKey&,
+ RecordIdentifier* found_record_identifier,
+ bool* found) OVERRIDE;
+
+ virtual leveldb::Status CreateIndex(Transaction*,
int64 database_id,
int64 object_store_id,
- const IndexedDBKey&,
- RecordIdentifier* found_record_identifier,
- bool* found) OVERRIDE;
-
- virtual bool CreateIndex(Transaction*,
- int64 database_id,
- int64 object_store_id,
- int64 index_id,
- const base::string16& name,
- const IndexedDBKeyPath&,
- bool is_unique,
- bool is_multi_entry) OVERRIDE;
- virtual bool DeleteIndex(Transaction*,
- int64 database_id,
- int64 object_store_id,
- int64 index_id) OVERRIDE;
- virtual bool PutIndexDataForRecord(Transaction*,
- int64 database_id,
- int64 object_store_id,
- int64 index_id,
- const IndexedDBKey&,
- const RecordIdentifier&) OVERRIDE;
-
+ int64 index_id,
+ const base::string16& name,
+ const IndexedDBKeyPath&,
+ bool is_unique,
+ bool is_multi_entry) OVERRIDE;
+ virtual leveldb::Status DeleteIndex(Transaction*,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id) OVERRIDE;
+ virtual leveldb::Status PutIndexDataForRecord(Transaction*,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ const IndexedDBKey&,
+ const RecordIdentifier&)
+ OVERRIDE;
+ virtual void ReportBlobUnused(int64 database_id, int64 blob_key) OVERRIDE;
virtual scoped_ptr<Cursor> OpenObjectStoreKeyCursor(
Transaction* transaction,
int64 database_id,
int64 object_store_id,
const IndexedDBKeyRange& key_range,
- indexed_db::CursorDirection) OVERRIDE;
+ indexed_db::CursorDirection,
+ leveldb::Status*) OVERRIDE;
virtual scoped_ptr<Cursor> OpenObjectStoreCursor(
Transaction* transaction,
int64 database_id,
int64 object_store_id,
const IndexedDBKeyRange& key_range,
- indexed_db::CursorDirection) OVERRIDE;
+ indexed_db::CursorDirection,
+ leveldb::Status*) OVERRIDE;
virtual scoped_ptr<Cursor> OpenIndexKeyCursor(
Transaction* transaction,
int64 database_id,
int64 object_store_id,
int64 index_id,
const IndexedDBKeyRange& key_range,
- indexed_db::CursorDirection) OVERRIDE;
+ indexed_db::CursorDirection,
+ leveldb::Status*) OVERRIDE;
virtual scoped_ptr<Cursor> OpenIndexCursor(Transaction* transaction,
int64 database_id,
int64 object_store_id,
int64 index_id,
const IndexedDBKeyRange& key_range,
- indexed_db::CursorDirection)
- OVERRIDE;
+ indexed_db::CursorDirection,
+ leveldb::Status*) OVERRIDE;
class FakeTransaction : public IndexedDBBackingStore::Transaction {
public:
- FakeTransaction(bool result);
+ explicit FakeTransaction(leveldb::Status phase_two_result);
virtual void Begin() OVERRIDE;
- virtual bool Commit() OVERRIDE;
+ virtual leveldb::Status CommitPhaseOne(
+ scoped_refptr<BlobWriteCallback>) OVERRIDE;
+ virtual leveldb::Status CommitPhaseTwo() OVERRIDE;
virtual void Rollback() OVERRIDE;
private:
- bool result_;
+ leveldb::Status result_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeTransaction);
};
protected:
friend class base::RefCounted<IndexedDBFakeBackingStore>;
virtual ~IndexedDBFakeBackingStore();
+
+ DISALLOW_COPY_AND_ASSIGN(IndexedDBFakeBackingStore);
};
} // namespace content
diff --git a/chromium/content/browser/indexed_db/indexed_db_index_writer.cc b/chromium/content/browser/indexed_db/indexed_db_index_writer.cc
index 3e8dc3f86de..0f5a25419ab 100644
--- a/chromium/content/browser/indexed_db/indexed_db_index_writer.cc
+++ b/chromium/content/browser/indexed_db/indexed_db_index_writer.cc
@@ -13,6 +13,8 @@
#include "content/common/indexed_db/indexed_db_key_path.h"
#include "content/common/indexed_db/indexed_db_key_range.h"
+using base::ASCIIToUTF16;
+
namespace content {
IndexWriter::IndexWriter(
@@ -36,13 +38,14 @@ bool IndexWriter::VerifyIndexKeys(
const IndexedDBKey& primary_key,
base::string16* error_message) const {
*can_add_keys = false;
- for (size_t i = 0; i < index_keys_.size(); ++i) {
+ DCHECK_EQ(index_id, index_keys_.first);
+ for (size_t i = 0; i < index_keys_.second.size(); ++i) {
bool ok = AddingKeyAllowed(backing_store,
transaction,
database_id,
object_store_id,
index_id,
- (index_keys_)[i],
+ (index_keys_.second)[i],
primary_key,
can_add_keys);
if (!ok)
@@ -68,16 +71,18 @@ void IndexWriter::WriteIndexKeys(
int64 database_id,
int64 object_store_id) const {
int64 index_id = index_metadata_.id;
- for (size_t i = 0; i < index_keys_.size(); ++i) {
- bool ok = backing_store->PutIndexDataForRecord(transaction,
- database_id,
- object_store_id,
- index_id,
- index_keys_[i],
- record_identifier);
+ DCHECK_EQ(index_id, index_keys_.first);
+ for (size_t i = 0; i < index_keys_.second.size(); ++i) {
+ leveldb::Status s =
+ backing_store->PutIndexDataForRecord(transaction,
+ database_id,
+ object_store_id,
+ index_id,
+ index_keys_.second[i],
+ record_identifier);
// This should have already been verified as a valid write during
// verify_index_keys.
- DCHECK(ok);
+ DCHECK(s.ok());
}
}
@@ -98,51 +103,49 @@ bool IndexWriter::AddingKeyAllowed(
scoped_ptr<IndexedDBKey> found_primary_key;
bool found = false;
- bool ok = backing_store->KeyExistsInIndex(transaction,
- database_id,
- object_store_id,
- index_id,
- index_key,
- &found_primary_key,
- &found);
- if (!ok)
+ leveldb::Status s = backing_store->KeyExistsInIndex(transaction,
+ database_id,
+ object_store_id,
+ index_id,
+ index_key,
+ &found_primary_key,
+ &found);
+ if (!s.ok())
return false;
if (!found ||
- (primary_key.IsValid() && found_primary_key->IsEqual(primary_key)))
+ (primary_key.IsValid() && found_primary_key->Equals(primary_key)))
*allowed = true;
return true;
}
bool MakeIndexWriters(
- scoped_refptr<IndexedDBTransaction> transaction,
+ IndexedDBTransaction* transaction,
IndexedDBBackingStore* backing_store,
int64 database_id,
const IndexedDBObjectStoreMetadata& object_store,
const IndexedDBKey& primary_key, // makes a copy
bool key_was_generated,
- const std::vector<int64>& index_ids,
const std::vector<IndexedDBDatabase::IndexKeys>& index_keys,
ScopedVector<IndexWriter>* index_writers,
base::string16* error_message,
bool* completed) {
- DCHECK_EQ(index_ids.size(), index_keys.size());
*completed = false;
- std::map<int64, IndexedDBDatabase::IndexKeys> index_key_map;
- for (size_t i = 0; i < index_ids.size(); ++i)
- index_key_map[index_ids[i]] = index_keys[i];
-
- for (IndexedDBObjectStoreMetadata::IndexMap::const_iterator it =
- object_store.indexes.begin();
- it != object_store.indexes.end();
+ for (std::vector<IndexedDBDatabase::IndexKeys>::const_iterator it =
+ index_keys.begin();
+ it != index_keys.end();
++it) {
- const IndexedDBIndexMetadata& index = it->second;
+ IndexedDBObjectStoreMetadata::IndexMap::const_iterator found =
+ object_store.indexes.find(it->first);
+ if (found == object_store.indexes.end())
+ continue;
+ const IndexedDBIndexMetadata& index = found->second;
+ IndexedDBDatabase::IndexKeys keys = *it;
- IndexedDBDatabase::IndexKeys keys = index_key_map[it->first];
// If the object_store is using auto_increment, then any indexes with an
// identical key_path need to also use the primary (generated) key as a key.
if (key_was_generated && (index.key_path == object_store.key_path))
- keys.push_back(primary_key);
+ keys.second.push_back(primary_key);
scoped_ptr<IndexWriter> index_writer(new IndexWriter(index, keys));
bool can_add_keys = false;
diff --git a/chromium/content/browser/indexed_db/indexed_db_index_writer.h b/chromium/content/browser/indexed_db/indexed_db_index_writer.h
index 8a2cdadee3a..ef4bb16a525 100644
--- a/chromium/content/browser/indexed_db/indexed_db_index_writer.h
+++ b/chromium/content/browser/indexed_db/indexed_db_index_writer.h
@@ -56,16 +56,17 @@ class IndexWriter {
const IndexedDBIndexMetadata index_metadata_;
IndexedDBDatabase::IndexKeys index_keys_;
+
+ DISALLOW_COPY_AND_ASSIGN(IndexWriter);
};
bool MakeIndexWriters(
- scoped_refptr<IndexedDBTransaction> transaction,
+ IndexedDBTransaction* transaction,
IndexedDBBackingStore* store,
int64 database_id,
const IndexedDBObjectStoreMetadata& metadata,
const IndexedDBKey& primary_key,
bool key_was_generated,
- const std::vector<int64>& index_ids,
const std::vector<IndexedDBDatabase::IndexKeys>& index_keys,
ScopedVector<IndexWriter>* index_writers,
base::string16* error_message,
diff --git a/chromium/content/browser/indexed_db/indexed_db_internals_ui.cc b/chromium/content/browser/indexed_db/indexed_db_internals_ui.cc
index b5e888f5356..5e181f43c89 100644
--- a/chromium/content/browser/indexed_db/indexed_db_internals_ui.cc
+++ b/chromium/content/browser/indexed_db/indexed_db_internals_ui.cc
@@ -87,7 +87,7 @@ void IndexedDBInternalsUI::GetAllOriginsOnIndexedDBThread(
const base::FilePath& context_path) {
DCHECK(context->TaskRunner()->RunsTasksOnCurrentThread());
- scoped_ptr<ListValue> info_list(static_cast<IndexedDBContextImpl*>(
+ scoped_ptr<base::ListValue> info_list(static_cast<IndexedDBContextImpl*>(
context.get())->GetAllOriginsDetails());
BrowserThread::PostTask(BrowserThread::UI,
@@ -98,7 +98,7 @@ void IndexedDBInternalsUI::GetAllOriginsOnIndexedDBThread(
context_path));
}
-void IndexedDBInternalsUI::OnOriginsReady(scoped_ptr<ListValue> origins,
+void IndexedDBInternalsUI::OnOriginsReady(scoped_ptr<base::ListValue> origins,
const base::FilePath& path) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
web_ui()->CallJavascriptFunction(
@@ -201,7 +201,8 @@ void IndexedDBInternalsUI::DownloadOriginDataOnIndexedDBThread(
if (!context->IsInOriginSet(origin_url))
return;
- context->ForceClose(origin_url);
+ context->ForceClose(origin_url,
+ IndexedDBContextImpl::FORCE_CLOSE_INTERNALS_PAGE);
size_t connection_count = context->GetConnectionCount(origin_url);
base::ScopedTempDir temp_dir;
@@ -242,7 +243,8 @@ void IndexedDBInternalsUI::ForceCloseOriginOnIndexedDBThread(
if (!context->IsInOriginSet(origin_url))
return;
- context->ForceClose(origin_url);
+ context->ForceClose(origin_url,
+ IndexedDBContextImpl::FORCE_CLOSE_INTERNALS_PAGE);
size_t connection_count = context->GetConnectionCount(origin_url);
BrowserThread::PostTask(BrowserThread::UI,
@@ -261,7 +263,7 @@ void IndexedDBInternalsUI::OnForcedClose(const base::FilePath& partition_path,
"indexeddb.onForcedClose",
base::StringValue(partition_path.value()),
base::StringValue(origin_url.spec()),
- base::FundamentalValue(double(connection_count)));
+ base::FundamentalValue(static_cast<double>(connection_count)));
}
void IndexedDBInternalsUI::OnDownloadDataReady(
@@ -308,6 +310,8 @@ class FileDeleter : public DownloadItem::Observer {
private:
const base::FilePath temp_dir_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileDeleter);
};
void FileDeleter::OnDownloadUpdated(DownloadItem* item) {
@@ -338,11 +342,11 @@ void IndexedDBInternalsUI::OnDownloadStarted(
const base::FilePath& temp_path,
size_t connection_count,
DownloadItem* item,
- net::Error error) {
+ DownloadInterruptReason interrupt_reason) {
- if (error != net::OK) {
- LOG(ERROR)
- << "Error downloading database dump: " << net::ErrorToString(error);
+ if (interrupt_reason != DOWNLOAD_INTERRUPT_REASON_NONE) {
+ LOG(ERROR) << "Error downloading database dump: "
+ << DownloadInterruptReasonToString(interrupt_reason);
return;
}
@@ -351,7 +355,7 @@ void IndexedDBInternalsUI::OnDownloadStarted(
"indexeddb.onOriginDownloadReady",
base::StringValue(partition_path.value()),
base::StringValue(origin_url.spec()),
- base::FundamentalValue(double(connection_count)));
+ base::FundamentalValue(static_cast<double>(connection_count)));
}
} // namespace content
diff --git a/chromium/content/browser/indexed_db/indexed_db_internals_ui.h b/chromium/content/browser/indexed_db/indexed_db_internals_ui.h
index a196e036de1..f51df01db09 100644
--- a/chromium/content/browser/indexed_db/indexed_db_internals_ui.h
+++ b/chromium/content/browser/indexed_db/indexed_db_internals_ui.h
@@ -9,9 +9,9 @@
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
+#include "content/public/browser/download_interrupt_reasons.h"
#include "content/public/browser/indexed_db_context.h"
#include "content/public/browser/web_ui_controller.h"
-#include "net/base/net_errors.h"
namespace base {
class ListValue;
@@ -52,8 +52,8 @@ class IndexedDBInternalsUI : public WebUIController {
const GURL& origin_url,
const base::FilePath& temp_path,
size_t connection_count,
- content::DownloadItem* item,
- net::Error error);
+ DownloadItem* item,
+ DownloadInterruptReason interrupt_reason);
void ForceCloseOrigin(const base::ListValue* args);
void ForceCloseOriginOnIndexedDBThread(
diff --git a/chromium/content/browser/indexed_db/indexed_db_leveldb_coding.cc b/chromium/content/browser/indexed_db/indexed_db_leveldb_coding.cc
index 01c27d10d0f..d0064267c93 100644
--- a/chromium/content/browser/indexed_db/indexed_db_leveldb_coding.cc
+++ b/chromium/content/browser/indexed_db/indexed_db_leveldb_coding.cc
@@ -44,6 +44,13 @@
// <0, 0, 0, 0> => backing store schema version [SchemaVersionKey]
// <0, 0, 0, 1> => maximum allocated database [MaxDatabaseIdKey]
// <0, 0, 0, 2> => SerializedScriptValue version [DataVersionKey]
+// <0, 0, 0, 3>
+// => Blob journal
+// The format of the journal is:
+// {database_id (var int), blobKey (var int)}*.
+// If the blobKey is kAllBlobsKey, the whole database should be deleted.
+// [BlobJournalKey]
+// <0, 0, 0, 4> => Live blob journal; same format. [LiveBlobJournalKey]
// <0, 0, 0, 100, database id>
// => Existence implies the database id is in the free list
// [DatabaseFreeListKey]
@@ -59,6 +66,7 @@
// <database id, 0, 0, 2> => IDB string version data (obsolete)
// <database id, 0, 0, 3> => maximum allocated object store id
// <database id, 0, 0, 4> => IDB integer version (var int)
+// <database id, 0, 0, 5> => blob key generator current number
//
//
// Object store metadata: [ObjectStoreMetaDataKey]
@@ -133,6 +141,14 @@
// <database id, object store id, 2, user key> => "version"
//
//
+// Blob entry table: [BlobEntryKey]
+// --------------------------------
+//
+// The prefix is followed by a type byte and the encoded IDB primary key.
+//
+// <database id, object store id, 3, user key> => array of IndexedDBBlobInfo
+//
+//
// Index data
// ----------
// The prefix is followed by a type byte, the encoded IDB index key, a
@@ -145,7 +161,6 @@
// same user (index) key in non-unique indexes prior to the inclusion of the
// primary key in the data.
-
using base::StringPiece;
using blink::WebIDBKeyType;
using blink::WebIDBKeyTypeArray;
@@ -164,8 +179,8 @@ using blink::WebIDBKeyPathTypeString;
namespace content {
// As most of the IndexedDBKeys and encoded values are short, we
-// initialize some Vectors with a default inline buffer size to reduce
-// the memory re-allocations when the Vectors are appended.
+// initialize some std::vectors with a default inline buffer size to reduce
+// the memory re-allocations when the std::vectors are appended.
static const size_t kDefaultInlineBufferSize = 32;
static const unsigned char kIndexedDBKeyNullTypeByte = 0;
@@ -181,12 +196,15 @@ static const unsigned char kIndexedDBKeyPathTypeCodedByte2 = 0;
static const unsigned char kObjectStoreDataIndexId = 1;
static const unsigned char kExistsEntryIndexId = 2;
+static const unsigned char kBlobEntryIndexId = 3;
static const unsigned char kSchemaVersionTypeByte = 0;
static const unsigned char kMaxDatabaseIdTypeByte = 1;
static const unsigned char kDataVersionTypeByte = 2;
+static const unsigned char kBlobJournalTypeByte = 3;
+static const unsigned char kLiveBlobJournalTypeByte = 4;
static const unsigned char kMaxSimpleGlobalMetaDataTypeByte =
- 3; // Insert before this and increment.
+ 5; // Insert before this and increment.
static const unsigned char kDatabaseFreeListTypeByte = 100;
static const unsigned char kDatabaseNameTypeByte = 201;
@@ -263,10 +281,11 @@ void EncodeString(const base::string16& value, std::string* into) {
// Backing store is UTF-16BE, convert from host endianness.
size_t length = value.length();
size_t current = into->size();
- into->resize(into->size() + length * sizeof(char16));
+ into->resize(into->size() + length * sizeof(base::char16));
- const char16* src = value.c_str();
- char16* dst = reinterpret_cast<char16*>(&*into->begin() + current);
+ const base::char16* src = value.c_str();
+ base::char16* dst =
+ reinterpret_cast<base::char16*>(&*into->begin() + current);
for (unsigned i = 0; i < length; ++i)
*dst++ = htons(*src++);
}
@@ -292,14 +311,6 @@ void EncodeIDBKey(const IndexedDBKey& value, std::string* into) {
size_t previous_size = into->size();
DCHECK(value.IsValid());
switch (value.type()) {
- case WebIDBKeyTypeNull:
- case WebIDBKeyTypeInvalid:
- case WebIDBKeyTypeMin:
- default: {
- NOTREACHED();
- EncodeByte(kIndexedDBKeyNullTypeByte, into);
- return;
- }
case WebIDBKeyTypeArray: {
EncodeByte(kIndexedDBKeyArrayTypeByte, into);
size_t length = value.array().size();
@@ -309,35 +320,34 @@ void EncodeIDBKey(const IndexedDBKey& value, std::string* into) {
DCHECK_GT(into->size(), previous_size);
return;
}
- case WebIDBKeyTypeBinary: {
+ case WebIDBKeyTypeBinary:
EncodeByte(kIndexedDBKeyBinaryTypeByte, into);
EncodeBinary(value.binary(), into);
DCHECK_GT(into->size(), previous_size);
return;
- }
- case WebIDBKeyTypeString: {
+ case WebIDBKeyTypeString:
EncodeByte(kIndexedDBKeyStringTypeByte, into);
EncodeStringWithLength(value.string(), into);
DCHECK_GT(into->size(), previous_size);
return;
- }
- case WebIDBKeyTypeDate: {
+ case WebIDBKeyTypeDate:
EncodeByte(kIndexedDBKeyDateTypeByte, into);
EncodeDouble(value.date(), into);
- DCHECK_EQ(static_cast<size_t>(9),
- static_cast<size_t>(into->size() - previous_size));
+ DCHECK_EQ(9u, static_cast<size_t>(into->size() - previous_size));
return;
- }
- case WebIDBKeyTypeNumber: {
+ case WebIDBKeyTypeNumber:
EncodeByte(kIndexedDBKeyNumberTypeByte, into);
EncodeDouble(value.number(), into);
- DCHECK_EQ(static_cast<size_t>(9),
- static_cast<size_t>(into->size() - previous_size));
+ DCHECK_EQ(9u, static_cast<size_t>(into->size() - previous_size));
+ return;
+ case WebIDBKeyTypeNull:
+ case WebIDBKeyTypeInvalid:
+ case WebIDBKeyTypeMin:
+ default:
+ NOTREACHED();
+ EncodeByte(kIndexedDBKeyNullTypeByte, into);
return;
- }
}
-
- NOTREACHED();
}
void EncodeIDBKeyPath(const IndexedDBKeyPath& value, std::string* into) {
@@ -366,6 +376,14 @@ void EncodeIDBKeyPath(const IndexedDBKeyPath& value, std::string* into) {
}
}
+void EncodeBlobJournal(const BlobJournalType& journal, std::string* into) {
+ BlobJournalType::const_iterator iter;
+ for (iter = journal.begin(); iter != journal.end(); ++iter) {
+ EncodeVarInt(iter->first, into);
+ EncodeVarInt(iter->second, into);
+ }
+}
+
bool DecodeByte(StringPiece* slice, unsigned char* value) {
if (slice->empty())
return false;
@@ -428,16 +446,17 @@ bool DecodeString(StringPiece* slice, base::string16* value) {
}
// Backing store is UTF-16BE, convert to host endianness.
- DCHECK(!(slice->size() % sizeof(char16)));
- size_t length = slice->size() / sizeof(char16);
+ DCHECK(!(slice->size() % sizeof(base::char16)));
+ size_t length = slice->size() / sizeof(base::char16);
base::string16 decoded;
decoded.reserve(length);
- const char16* encoded = reinterpret_cast<const char16*>(slice->begin());
+ const base::char16* encoded =
+ reinterpret_cast<const base::char16*>(slice->begin());
for (unsigned i = 0; i < length; ++i)
decoded.push_back(ntohs(*encoded++));
*value = decoded;
- slice->remove_prefix(length * sizeof(char16));
+ slice->remove_prefix(length * sizeof(base::char16));
return true;
}
@@ -448,7 +467,7 @@ bool DecodeStringWithLength(StringPiece* slice, base::string16* value) {
int64 length = 0;
if (!DecodeVarInt(slice, &length) || length < 0)
return false;
- size_t bytes = length * sizeof(char16);
+ size_t bytes = length * sizeof(base::char16);
if (slice->size() < bytes)
return false;
@@ -597,6 +616,27 @@ bool DecodeIDBKeyPath(StringPiece* slice, IndexedDBKeyPath* value) {
return false;
}
+bool DecodeBlobJournal(StringPiece* slice, BlobJournalType* journal) {
+ BlobJournalType output;
+ while (!slice->empty()) {
+ int64 database_id = -1;
+ int64 blob_key = -1;
+ if (!DecodeVarInt(slice, &database_id))
+ return false;
+ if (!KeyPrefix::IsValidDatabaseId(database_id))
+ return false;
+ if (!DecodeVarInt(slice, &blob_key))
+ return false;
+ if (!DatabaseMetaDataKey::IsValidBlobKey(blob_key) &&
+ (blob_key != DatabaseMetaDataKey::kAllBlobsKey)) {
+ return false;
+ }
+ output.push_back(std::make_pair(database_id, blob_key));
+ }
+ journal->swap(output);
+ return true;
+}
+
bool ConsumeEncodedIDBKey(StringPiece* slice) {
unsigned char type = (*slice)[0];
slice->remove_prefix(1);
@@ -628,9 +668,9 @@ bool ConsumeEncodedIDBKey(StringPiece* slice) {
int64 length = 0;
if (!DecodeVarInt(slice, &length) || length < 0)
return false;
- if (slice->size() < static_cast<size_t>(length) * sizeof(char16))
+ if (slice->size() < static_cast<size_t>(length) * sizeof(base::char16))
return false;
- slice->remove_prefix(length * sizeof(char16));
+ slice->remove_prefix(length * sizeof(base::char16));
return true;
}
case kIndexedDBKeyDateTypeByte:
@@ -690,19 +730,19 @@ int CompareEncodedStringsWithLength(StringPiece* slice1,
*ok = false;
return 0;
}
- DCHECK_GE(slice1->size(), len1 * sizeof(char16));
- DCHECK_GE(slice2->size(), len2 * sizeof(char16));
- if (slice1->size() < len1 * sizeof(char16) ||
- slice2->size() < len2 * sizeof(char16)) {
+ DCHECK_GE(slice1->size(), len1 * sizeof(base::char16));
+ DCHECK_GE(slice2->size(), len2 * sizeof(base::char16));
+ if (slice1->size() < len1 * sizeof(base::char16) ||
+ slice2->size() < len2 * sizeof(base::char16)) {
*ok = false;
return 0;
}
// Extract the string data, and advance the passed slices.
- StringPiece string1(slice1->begin(), len1 * sizeof(char16));
- StringPiece string2(slice2->begin(), len2 * sizeof(char16));
- slice1->remove_prefix(len1 * sizeof(char16));
- slice2->remove_prefix(len2 * sizeof(char16));
+ StringPiece string1(slice1->begin(), len1 * sizeof(base::char16));
+ StringPiece string2(slice2->begin(), len2 * sizeof(base::char16));
+ slice1->remove_prefix(len1 * sizeof(base::char16));
+ slice2->remove_prefix(len2 * sizeof(base::char16));
*ok = true;
// Strings are UTF-16BE encoded, so a simple memcmp is sufficient.
@@ -878,6 +918,14 @@ int CompareSuffix<ObjectStoreDataKey>(StringPiece* slice_a,
}
template <>
+int CompareSuffix<BlobEntryKey>(StringPiece* slice_a,
+ StringPiece* slice_b,
+ bool only_compare_index_keys,
+ bool* ok) {
+ return CompareEncodedIDBKeys(slice_a, slice_b, ok);
+}
+
+template <>
int CompareSuffix<IndexDataKey>(StringPiece* slice_a,
StringPiece* slice_b,
bool only_compare_index_keys,
@@ -1038,6 +1086,15 @@ int Compare(const StringPiece& a,
&slice_a, &slice_b, /*only_compare_index_keys*/ false, ok);
}
+ case KeyPrefix::BLOB_ENTRY: {
+ // Provide a stable ordering for invalid data.
+ if (slice_a.empty() || slice_b.empty())
+ return CompareSizes(slice_a.size(), slice_b.size());
+
+ return CompareSuffix<BlobEntryKey>(
+ &slice_a, &slice_b, /*only_compare_index_keys*/ false, ok);
+ }
+
case KeyPrefix::INDEX_DATA: {
// Provide a stable ordering for invalid data.
if (slice_a.empty() || slice_b.empty())
@@ -1239,6 +1296,8 @@ KeyPrefix::Type KeyPrefix::type() const {
return OBJECT_STORE_DATA;
if (index_id_ == kExistsEntryIndexId)
return EXISTS_ENTRY;
+ if (index_id_ == kBlobEntryIndexId)
+ return BLOB_ENTRY;
if (index_id_ >= kMinimumIndexId)
return INDEX_DATA;
@@ -1264,6 +1323,18 @@ std::string DataVersionKey::Encode() {
return ret;
}
+std::string BlobJournalKey::Encode() {
+ std::string ret = KeyPrefix::EncodeEmpty();
+ ret.push_back(kBlobJournalTypeByte);
+ return ret;
+}
+
+std::string LiveBlobJournalKey::Encode() {
+ std::string ret = KeyPrefix::EncodeEmpty();
+ ret.push_back(kLiveBlobJournalTypeByte);
+ return ret;
+}
+
DatabaseFreeListKey::DatabaseFreeListKey() : database_id_(-1) {}
bool DatabaseFreeListKey::Decode(StringPiece* slice,
@@ -1348,6 +1419,14 @@ int DatabaseNameKey::Compare(const DatabaseNameKey& other) {
return database_name_.compare(other.database_name_);
}
+bool DatabaseMetaDataKey::IsValidBlobKey(int64 blob_key) {
+ return blob_key >= kBlobKeyGeneratorInitialNumber;
+}
+
+const int64 DatabaseMetaDataKey::kAllBlobsKey = 1;
+const int64 DatabaseMetaDataKey::kBlobKeyGeneratorInitialNumber = 2;
+const int64 DatabaseMetaDataKey::kInvalidBlobKey = -1;
+
std::string DatabaseMetaDataKey::Encode(int64 database_id,
MetaDataType meta_data_type) {
KeyPrefix prefix(database_id);
@@ -1747,6 +1826,86 @@ scoped_ptr<IndexedDBKey> ExistsEntryKey::user_key() const {
const int64 ExistsEntryKey::kSpecialIndexNumber = kExistsEntryIndexId;
+bool BlobEntryKey::Decode(StringPiece* slice, BlobEntryKey* result) {
+ KeyPrefix prefix;
+ if (!KeyPrefix::Decode(slice, &prefix))
+ return false;
+ DCHECK(prefix.database_id_);
+ DCHECK(prefix.object_store_id_);
+ DCHECK_EQ(prefix.index_id_, kSpecialIndexNumber);
+
+ if (!ExtractEncodedIDBKey(slice, &result->encoded_user_key_))
+ return false;
+ result->database_id_ = prefix.database_id_;
+ result->object_store_id_ = prefix.object_store_id_;
+
+ return true;
+}
+
+bool BlobEntryKey::FromObjectStoreDataKey(StringPiece* slice,
+ BlobEntryKey* result) {
+ KeyPrefix prefix;
+ if (!KeyPrefix::Decode(slice, &prefix))
+ return false;
+ DCHECK(prefix.database_id_);
+ DCHECK(prefix.object_store_id_);
+ DCHECK_EQ(prefix.index_id_, ObjectStoreDataKey::kSpecialIndexNumber);
+
+ if (!ExtractEncodedIDBKey(slice, &result->encoded_user_key_))
+ return false;
+ result->database_id_ = prefix.database_id_;
+ result->object_store_id_ = prefix.object_store_id_;
+ return true;
+}
+
+std::string BlobEntryKey::ReencodeToObjectStoreDataKey(StringPiece* slice) {
+ // TODO(ericu): We could be more efficient here, since the suffix is the same.
+ BlobEntryKey key;
+ if (!Decode(slice, &key))
+ return std::string();
+
+ return ObjectStoreDataKey::Encode(
+ key.database_id_, key.object_store_id_, key.encoded_user_key_);
+}
+
+std::string BlobEntryKey::EncodeMinKeyForObjectStore(int64 database_id,
+ int64 object_store_id) {
+ // Our implied encoded_user_key_ here is empty, the lowest possible key.
+ return Encode(database_id, object_store_id, std::string());
+}
+
+std::string BlobEntryKey::EncodeStopKeyForObjectStore(int64 database_id,
+ int64 object_store_id) {
+ DCHECK(KeyPrefix::ValidIds(database_id, object_store_id));
+ KeyPrefix prefix(KeyPrefix::CreateWithSpecialIndex(
+ database_id, object_store_id, kSpecialIndexNumber + 1));
+ return prefix.Encode();
+}
+
+std::string BlobEntryKey::Encode() const {
+ DCHECK(!encoded_user_key_.empty());
+ return Encode(database_id_, object_store_id_, encoded_user_key_);
+}
+
+std::string BlobEntryKey::Encode(int64 database_id,
+ int64 object_store_id,
+ const IndexedDBKey& user_key) {
+ std::string encoded_key;
+ EncodeIDBKey(user_key, &encoded_key);
+ return Encode(database_id, object_store_id, encoded_key);
+}
+
+std::string BlobEntryKey::Encode(int64 database_id,
+ int64 object_store_id,
+ const std::string& encoded_user_key) {
+ DCHECK(KeyPrefix::ValidIds(database_id, object_store_id));
+ KeyPrefix prefix(KeyPrefix::CreateWithSpecialIndex(
+ database_id, object_store_id, kSpecialIndexNumber));
+ return prefix.Encode() + encoded_user_key;
+}
+
+const int64 BlobEntryKey::kSpecialIndexNumber = kBlobEntryIndexId;
+
IndexDataKey::IndexDataKey()
: database_id_(-1),
object_store_id_(-1),
diff --git a/chromium/content/browser/indexed_db/indexed_db_leveldb_coding.h b/chromium/content/browser/indexed_db/indexed_db_leveldb_coding.h
index 556b4605af2..85bc091b56b 100644
--- a/chromium/content/browser/indexed_db/indexed_db_leveldb_coding.h
+++ b/chromium/content/browser/indexed_db/indexed_db_leveldb_coding.h
@@ -6,6 +6,8 @@
#define CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_LEVELDB_CODING_H_
#include <string>
+#include <utility>
+#include <vector>
#include "base/basictypes.h"
#include "base/logging.h"
@@ -23,6 +25,10 @@ CONTENT_EXPORT extern const unsigned char kMinimumIndexId;
CONTENT_EXPORT std::string MaxIDBKey();
CONTENT_EXPORT std::string MinIDBKey();
+// DatabaseId, BlobKey
+typedef std::pair<int64_t, int64_t> BlobJournalEntryType;
+typedef std::vector<BlobJournalEntryType> BlobJournalType;
+
CONTENT_EXPORT void EncodeByte(unsigned char value, std::string* into);
CONTENT_EXPORT void EncodeBool(bool value, std::string* into);
CONTENT_EXPORT void EncodeInt(int64 value, std::string* into);
@@ -36,6 +42,8 @@ CONTENT_EXPORT void EncodeDouble(double value, std::string* into);
CONTENT_EXPORT void EncodeIDBKey(const IndexedDBKey& value, std::string* into);
CONTENT_EXPORT void EncodeIDBKeyPath(const IndexedDBKeyPath& value,
std::string* into);
+CONTENT_EXPORT void EncodeBlobJournal(const BlobJournalType& journal,
+ std::string* into);
CONTENT_EXPORT WARN_UNUSED_RESULT bool DecodeByte(base::StringPiece* slice,
unsigned char* value);
@@ -60,6 +68,9 @@ CONTENT_EXPORT WARN_UNUSED_RESULT bool DecodeIDBKey(
CONTENT_EXPORT WARN_UNUSED_RESULT bool DecodeIDBKeyPath(
base::StringPiece* slice,
IndexedDBKeyPath* value);
+CONTENT_EXPORT WARN_UNUSED_RESULT bool DecodeBlobJournal(
+ base::StringPiece* slice,
+ BlobJournalType* journal);
CONTENT_EXPORT int CompareEncodedStringsWithLength(base::StringPiece* slice1,
base::StringPiece* slice2,
@@ -92,13 +103,16 @@ class KeyPrefix {
static std::string EncodeEmpty();
int Compare(const KeyPrefix& other) const;
+ // These are serialized to disk; any new items must be appended, and none can
+ // be deleted.
enum Type {
GLOBAL_METADATA,
DATABASE_METADATA,
OBJECT_STORE_DATA,
EXISTS_ENTRY,
INDEX_DATA,
- INVALID_TYPE
+ INVALID_TYPE,
+ BLOB_ENTRY
};
static const size_t kMaxDatabaseIdSizeBits = 3;
@@ -124,7 +138,7 @@ class KeyPrefix {
static const int64 kMaxIndexId =
(1ULL << kMaxIndexIdBits) - 1; // max signed int32
- static bool IsValidDatabaseId(int64 database_id);
+ CONTENT_EXPORT static bool IsValidDatabaseId(int64 database_id);
static bool IsValidObjectStoreId(int64 index_id);
static bool IsValidIndexId(int64 index_id);
static bool ValidIds(int64 database_id,
@@ -172,6 +186,16 @@ class DataVersionKey {
static std::string Encode();
};
+class BlobJournalKey {
+ public:
+ static std::string Encode();
+};
+
+class LiveBlobJournalKey {
+ public:
+ static std::string Encode();
+};
+
class DatabaseFreeListKey {
public:
DatabaseFreeListKey();
@@ -212,9 +236,16 @@ class DatabaseMetaDataKey {
USER_VERSION = 2,
MAX_OBJECT_STORE_ID = 3,
USER_INT_VERSION = 4,
- MAX_SIMPLE_METADATA_TYPE = 5
+ BLOB_KEY_GENERATOR_CURRENT_NUMBER = 5,
+ MAX_SIMPLE_METADATA_TYPE = 6
};
+ CONTENT_EXPORT static const int64 kAllBlobsKey;
+ static const int64 kBlobKeyGeneratorInitialNumber;
+ // All keys <= 0 are invalid. This one's just a convenient example.
+ static const int64 kInvalidBlobKey;
+
+ static bool IsValidBlobKey(int64 blobKey);
CONTENT_EXPORT static std::string Encode(int64 database_id,
MetaDataType type);
};
@@ -384,6 +415,36 @@ class ExistsEntryKey {
DISALLOW_COPY_AND_ASSIGN(ExistsEntryKey);
};
+class BlobEntryKey {
+ public:
+ BlobEntryKey() : database_id_(0), object_store_id_(0) {}
+ static bool Decode(base::StringPiece* slice, BlobEntryKey* result);
+ static bool FromObjectStoreDataKey(base::StringPiece* slice,
+ BlobEntryKey* result);
+ static std::string ReencodeToObjectStoreDataKey(base::StringPiece* slice);
+ static std::string EncodeMinKeyForObjectStore(int64 database_id,
+ int64 object_store_id);
+ static std::string EncodeStopKeyForObjectStore(int64 database_id,
+ int64 object_store_id);
+ static std::string Encode(int64 database_id,
+ int64 object_store_id,
+ const IndexedDBKey& user_key);
+ std::string Encode() const;
+ int64 database_id() const { return database_id_; }
+ int64 object_store_id() const { return object_store_id_; }
+
+ static const int64 kSpecialIndexNumber;
+
+ private:
+ static std::string Encode(int64 database_id,
+ int64 object_store_id,
+ const std::string& encoded_user_key);
+ int64 database_id_;
+ int64 object_store_id_;
+ // This is the user's ObjectStoreDataKey, not the BlobEntryKey itself.
+ std::string encoded_user_key_;
+};
+
class IndexDataKey {
public:
IndexDataKey();
diff --git a/chromium/content/browser/indexed_db/indexed_db_leveldb_coding_unittest.cc b/chromium/content/browser/indexed_db/indexed_db_leveldb_coding_unittest.cc
index 44c4ab88cdb..0bd647cff3a 100644
--- a/chromium/content/browser/indexed_db/indexed_db_leveldb_coding_unittest.cc
+++ b/chromium/content/browser/indexed_db/indexed_db_leveldb_coding_unittest.cc
@@ -15,6 +15,7 @@
#include "content/common/indexed_db/indexed_db_key_path.h"
#include "testing/gtest/include/gtest/gtest.h"
+using base::ASCIIToUTF16;
using base::StringPiece;
using blink::WebIDBKeyTypeDate;
using blink::WebIDBKeyTypeNumber;
@@ -77,7 +78,7 @@ TEST(IndexedDBLevelDBCodingTest, DecodeByte) {
EncodeByte(n, &v);
unsigned char res;
- ASSERT_GT(v.size(), static_cast<size_t>(0));
+ ASSERT_GT(v.size(), 0u);
StringPiece slice(v);
EXPECT_TRUE(DecodeByte(&slice, &res));
EXPECT_EQ(n, res);
@@ -175,13 +176,13 @@ static std::string WrappedEncodeInt(int64 value) {
}
TEST(IndexedDBLevelDBCodingTest, EncodeInt) {
- EXPECT_EQ(static_cast<size_t>(1), WrappedEncodeInt(0).size());
- EXPECT_EQ(static_cast<size_t>(1), WrappedEncodeInt(1).size());
- EXPECT_EQ(static_cast<size_t>(1), WrappedEncodeInt(255).size());
- EXPECT_EQ(static_cast<size_t>(2), WrappedEncodeInt(256).size());
- EXPECT_EQ(static_cast<size_t>(4), WrappedEncodeInt(0xffffffff).size());
+ EXPECT_EQ(1u, WrappedEncodeInt(0).size());
+ EXPECT_EQ(1u, WrappedEncodeInt(1).size());
+ EXPECT_EQ(1u, WrappedEncodeInt(255).size());
+ EXPECT_EQ(2u, WrappedEncodeInt(256).size());
+ EXPECT_EQ(4u, WrappedEncodeInt(0xffffffff).size());
#ifdef NDEBUG
- EXPECT_EQ(static_cast<size_t>(8), WrappedEncodeInt(-1).size());
+ EXPECT_EQ(8u, WrappedEncodeInt(-1).size());
#endif
}
@@ -228,7 +229,7 @@ TEST(IndexedDBLevelDBCodingTest, DecodeInt) {
for (size_t i = 0; i < test_cases.size(); ++i) {
int64 n = test_cases[i];
std::string v = WrappedEncodeInt(n);
- ASSERT_GT(v.size(), static_cast<size_t>(0));
+ ASSERT_GT(v.size(), 0u);
StringPiece slice(v);
int64 value;
EXPECT_TRUE(DecodeInt(&slice, &value));
@@ -236,7 +237,7 @@ TEST(IndexedDBLevelDBCodingTest, DecodeInt) {
EXPECT_TRUE(slice.empty());
// Verify decoding at an offset, to detect unaligned memory access.
- v.insert(v.begin(), static_cast<size_t>(1), static_cast<char>(0));
+ v.insert(v.begin(), 1u, static_cast<char>(0));
slice = StringPiece(&*v.begin() + 1, v.size() - 1);
EXPECT_TRUE(DecodeInt(&slice, &value));
EXPECT_EQ(n, value);
@@ -256,17 +257,15 @@ static std::string WrappedEncodeVarInt(int64 value) {
}
TEST(IndexedDBLevelDBCodingTest, EncodeVarInt) {
- EXPECT_EQ(static_cast<size_t>(1), WrappedEncodeVarInt(0).size());
- EXPECT_EQ(static_cast<size_t>(1), WrappedEncodeVarInt(1).size());
- EXPECT_EQ(static_cast<size_t>(2), WrappedEncodeVarInt(255).size());
- EXPECT_EQ(static_cast<size_t>(2), WrappedEncodeVarInt(256).size());
- EXPECT_EQ(static_cast<size_t>(5), WrappedEncodeVarInt(0xffffffff).size());
- EXPECT_EQ(static_cast<size_t>(8),
- WrappedEncodeVarInt(0xfffffffffffffLL).size());
- EXPECT_EQ(static_cast<size_t>(9),
- WrappedEncodeVarInt(0x7fffffffffffffffLL).size());
+ EXPECT_EQ(1u, WrappedEncodeVarInt(0).size());
+ EXPECT_EQ(1u, WrappedEncodeVarInt(1).size());
+ EXPECT_EQ(2u, WrappedEncodeVarInt(255).size());
+ EXPECT_EQ(2u, WrappedEncodeVarInt(256).size());
+ EXPECT_EQ(5u, WrappedEncodeVarInt(0xffffffff).size());
+ EXPECT_EQ(8u, WrappedEncodeVarInt(0xfffffffffffffLL).size());
+ EXPECT_EQ(9u, WrappedEncodeVarInt(0x7fffffffffffffffLL).size());
#ifdef NDEBUG
- EXPECT_EQ(static_cast<size_t>(10), WrappedEncodeVarInt(-100).size());
+ EXPECT_EQ(10u, WrappedEncodeVarInt(-100).size());
#endif
}
@@ -287,7 +286,7 @@ TEST(IndexedDBLevelDBCodingTest, DecodeVarInt) {
for (size_t i = 0; i < test_cases.size(); ++i) {
int64 n = test_cases[i];
std::string v = WrappedEncodeVarInt(n);
- ASSERT_GT(v.size(), static_cast<size_t>(0));
+ ASSERT_GT(v.size(), 0u);
StringPiece slice(v);
int64 res;
EXPECT_TRUE(DecodeVarInt(&slice, &res));
@@ -301,7 +300,7 @@ TEST(IndexedDBLevelDBCodingTest, DecodeVarInt) {
EXPECT_FALSE(DecodeVarInt(&slice, &res));
// Verify decoding at an offset, to detect unaligned memory access.
- v.insert(v.begin(), static_cast<size_t>(1), static_cast<char>(0));
+ v.insert(v.begin(), 1u, static_cast<char>(0));
slice = StringPiece(&*v.begin() + 1, v.size() - 1);
EXPECT_TRUE(DecodeVarInt(&slice, &res));
EXPECT_EQ(n, res);
@@ -316,24 +315,19 @@ static std::string WrappedEncodeString(base::string16 value) {
}
TEST(IndexedDBLevelDBCodingTest, EncodeString) {
- const char16 test_string_a[] = {'f', 'o', 'o', '\0'};
- const char16 test_string_b[] = {0xdead, 0xbeef, '\0'};
+ const base::char16 test_string_a[] = {'f', 'o', 'o', '\0'};
+ const base::char16 test_string_b[] = {0xdead, 0xbeef, '\0'};
- EXPECT_EQ(static_cast<size_t>(0),
- WrappedEncodeString(ASCIIToUTF16("")).size());
- EXPECT_EQ(static_cast<size_t>(2),
- WrappedEncodeString(ASCIIToUTF16("a")).size());
- EXPECT_EQ(static_cast<size_t>(6),
- WrappedEncodeString(ASCIIToUTF16("foo")).size());
- EXPECT_EQ(static_cast<size_t>(6),
- WrappedEncodeString(base::string16(test_string_a)).size());
- EXPECT_EQ(static_cast<size_t>(4),
- WrappedEncodeString(base::string16(test_string_b)).size());
+ EXPECT_EQ(0u, WrappedEncodeString(ASCIIToUTF16("")).size());
+ EXPECT_EQ(2u, WrappedEncodeString(ASCIIToUTF16("a")).size());
+ EXPECT_EQ(6u, WrappedEncodeString(ASCIIToUTF16("foo")).size());
+ EXPECT_EQ(6u, WrappedEncodeString(base::string16(test_string_a)).size());
+ EXPECT_EQ(4u, WrappedEncodeString(base::string16(test_string_b)).size());
}
TEST(IndexedDBLevelDBCodingTest, DecodeString) {
- const char16 test_string_a[] = {'f', 'o', 'o', '\0'};
- const char16 test_string_b[] = {0xdead, 0xbeef, '\0'};
+ const base::char16 test_string_a[] = {'f', 'o', 'o', '\0'};
+ const base::char16 test_string_b[] = {0xdead, 0xbeef, '\0'};
std::vector<base::string16> test_cases;
test_cases.push_back(base::string16());
@@ -357,7 +351,7 @@ TEST(IndexedDBLevelDBCodingTest, DecodeString) {
EXPECT_TRUE(slice.empty());
// Verify decoding at an offset, to detect unaligned memory access.
- v.insert(v.begin(), static_cast<size_t>(1), static_cast<char>(0));
+ v.insert(v.begin(), 1u, static_cast<char>(0));
slice = StringPiece(&*v.begin() + 1, v.size() - 1);
EXPECT_TRUE(DecodeString(&slice, &result));
EXPECT_EQ(test_case, result);
@@ -372,27 +366,23 @@ static std::string WrappedEncodeStringWithLength(base::string16 value) {
}
TEST(IndexedDBLevelDBCodingTest, EncodeStringWithLength) {
- const char16 test_string_a[] = {'f', 'o', 'o', '\0'};
- const char16 test_string_b[] = {0xdead, 0xbeef, '\0'};
+ const base::char16 test_string_a[] = {'f', 'o', 'o', '\0'};
+ const base::char16 test_string_b[] = {0xdead, 0xbeef, '\0'};
- EXPECT_EQ(static_cast<size_t>(1),
- WrappedEncodeStringWithLength(base::string16()).size());
- EXPECT_EQ(static_cast<size_t>(3),
- WrappedEncodeStringWithLength(ASCIIToUTF16("a")).size());
- EXPECT_EQ(static_cast<size_t>(7),
- WrappedEncodeStringWithLength(
- base::string16(test_string_a)).size());
- EXPECT_EQ(static_cast<size_t>(5),
- WrappedEncodeStringWithLength(
- base::string16(test_string_b)).size());
+ EXPECT_EQ(1u, WrappedEncodeStringWithLength(base::string16()).size());
+ EXPECT_EQ(3u, WrappedEncodeStringWithLength(ASCIIToUTF16("a")).size());
+ EXPECT_EQ(
+ 7u, WrappedEncodeStringWithLength(base::string16(test_string_a)).size());
+ EXPECT_EQ(
+ 5u, WrappedEncodeStringWithLength(base::string16(test_string_b)).size());
}
TEST(IndexedDBLevelDBCodingTest, DecodeStringWithLength) {
- const char16 test_string_a[] = {'f', 'o', 'o', '\0'};
- const char16 test_string_b[] = {0xdead, 0xbeef, '\0'};
+ const base::char16 test_string_a[] = {'f', 'o', 'o', '\0'};
+ const base::char16 test_string_b[] = {0xdead, 0xbeef, '\0'};
const int kLongStringLen = 1234;
- char16 long_string[kLongStringLen + 1];
+ base::char16 long_string[kLongStringLen + 1];
for (int i = 0; i < kLongStringLen; ++i)
long_string[i] = i;
long_string[kLongStringLen] = 0;
@@ -408,7 +398,7 @@ TEST(IndexedDBLevelDBCodingTest, DecodeStringWithLength) {
for (size_t i = 0; i < test_cases.size(); ++i) {
base::string16 s = test_cases[i];
std::string v = WrappedEncodeStringWithLength(s);
- ASSERT_GT(v.size(), static_cast<size_t>(0));
+ ASSERT_GT(v.size(), 0u);
StringPiece slice(v);
base::string16 res;
EXPECT_TRUE(DecodeStringWithLength(&slice, &res));
@@ -422,7 +412,7 @@ TEST(IndexedDBLevelDBCodingTest, DecodeStringWithLength) {
EXPECT_FALSE(DecodeStringWithLength(&slice, &res));
// Verify decoding at an offset, to detect unaligned memory access.
- v.insert(v.begin(), static_cast<size_t>(1), static_cast<char>(0));
+ v.insert(v.begin(), 1u, static_cast<char>(0));
slice = StringPiece(&*v.begin() + 1, v.size() - 1);
EXPECT_TRUE(DecodeStringWithLength(&slice, &res));
EXPECT_EQ(s, res);
@@ -444,12 +434,12 @@ static int CompareStrings(const std::string& p, const std::string& q) {
}
TEST(IndexedDBLevelDBCodingTest, CompareEncodedStringsWithLength) {
- const char16 test_string_a[] = {0x1000, 0x1000, '\0'};
- const char16 test_string_b[] = {0x1000, 0x1000, 0x1000, '\0'};
- const char16 test_string_c[] = {0x1000, 0x1000, 0x1001, '\0'};
- const char16 test_string_d[] = {0x1001, 0x1000, 0x1000, '\0'};
- const char16 test_string_e[] = {0xd834, 0xdd1e, '\0'};
- const char16 test_string_f[] = {0xfffd, '\0'};
+ const base::char16 test_string_a[] = {0x1000, 0x1000, '\0'};
+ const base::char16 test_string_b[] = {0x1000, 0x1000, 0x1000, '\0'};
+ const base::char16 test_string_c[] = {0x1000, 0x1000, 0x1001, '\0'};
+ const base::char16 test_string_d[] = {0x1001, 0x1000, 0x1000, '\0'};
+ const base::char16 test_string_e[] = {0xd834, 0xdd1e, '\0'};
+ const base::char16 test_string_f[] = {0xfffd, '\0'};
std::vector<base::string16> test_cases;
test_cases.push_back(ASCIIToUTF16(""));
@@ -495,13 +485,13 @@ static std::string WrappedEncodeBinary(std::string value) {
TEST(IndexedDBLevelDBCodingTest, EncodeBinary) {
const unsigned char binary_data[] = {0x00, 0x01, 0xfe, 0xff};
EXPECT_EQ(
- static_cast<size_t>(1),
+ 1u,
WrappedEncodeBinary(std::string(binary_data, binary_data + 0)).size());
EXPECT_EQ(
- static_cast<size_t>(2),
+ 2u,
WrappedEncodeBinary(std::string(binary_data, binary_data + 1)).size());
EXPECT_EQ(
- static_cast<size_t>(5),
+ 5u,
WrappedEncodeBinary(std::string(binary_data, binary_data + 4)).size());
}
@@ -516,7 +506,7 @@ TEST(IndexedDBLevelDBCodingTest, DecodeBinary) {
for (size_t i = 0; i < test_cases.size(); ++i) {
std::string value = test_cases[i];
std::string v = WrappedEncodeBinary(value);
- ASSERT_GT(v.size(), static_cast<size_t>(0));
+ ASSERT_GT(v.size(), 0u);
StringPiece slice(v);
std::string result;
EXPECT_TRUE(DecodeBinary(&slice, &result));
@@ -530,7 +520,7 @@ TEST(IndexedDBLevelDBCodingTest, DecodeBinary) {
EXPECT_FALSE(DecodeBinary(&slice, &result));
// Verify decoding at an offset, to detect unaligned memory access.
- v.insert(v.begin(), static_cast<size_t>(1), static_cast<char>(0));
+ v.insert(v.begin(), 1u, static_cast<char>(0));
slice = StringPiece(&*v.begin() + 1, v.size() - 1);
EXPECT_TRUE(DecodeBinary(&slice, &result));
EXPECT_EQ(value, result);
@@ -545,8 +535,8 @@ static std::string WrappedEncodeDouble(double value) {
}
TEST(IndexedDBLevelDBCodingTest, EncodeDouble) {
- EXPECT_EQ(static_cast<size_t>(8), WrappedEncodeDouble(0).size());
- EXPECT_EQ(static_cast<size_t>(8), WrappedEncodeDouble(3.14).size());
+ EXPECT_EQ(8u, WrappedEncodeDouble(0).size());
+ EXPECT_EQ(8u, WrappedEncodeDouble(3.14).size());
}
TEST(IndexedDBLevelDBCodingTest, DecodeDouble) {
@@ -557,7 +547,7 @@ TEST(IndexedDBLevelDBCodingTest, DecodeDouble) {
for (size_t i = 0; i < test_cases.size(); ++i) {
double value = test_cases[i];
std::string v = WrappedEncodeDouble(value);
- ASSERT_GT(v.size(), static_cast<size_t>(0));
+ ASSERT_GT(v.size(), 0u);
StringPiece slice(v);
double result;
EXPECT_TRUE(DecodeDouble(&slice, &result));
@@ -571,7 +561,7 @@ TEST(IndexedDBLevelDBCodingTest, DecodeDouble) {
EXPECT_FALSE(DecodeDouble(&slice, &result));
// Verify decoding at an offset, to detect unaligned memory access.
- v.insert(v.begin(), static_cast<size_t>(1), static_cast<char>(0));
+ v.insert(v.begin(), 1u, static_cast<char>(0));
slice = StringPiece(&*v.begin() + 1, v.size() - 1);
EXPECT_TRUE(DecodeDouble(&slice, &result));
EXPECT_EQ(value, result);
@@ -606,7 +596,7 @@ TEST(IndexedDBLevelDBCodingTest, EncodeDecodeIDBKey) {
EncodeIDBKey(expected_key, &v);
slice = StringPiece(&*v.begin(), v.size());
EXPECT_TRUE(DecodeIDBKey(&slice, &decoded_key));
- EXPECT_TRUE(decoded_key->IsEqual(expected_key));
+ EXPECT_TRUE(decoded_key->Equals(expected_key));
EXPECT_TRUE(slice.empty());
slice = StringPiece(&*v.begin(), v.size() - 1);
@@ -701,6 +691,76 @@ TEST(IndexedDBLevelDBCodingTest, EncodeDecodeIDBKeyPath) {
}
}
+TEST(IndexedDBLevelDBCodingTest, EncodeDecodeBlobJournal) {
+ std::vector<IndexedDBKeyPath> key_paths;
+ std::vector<std::string> encoded_paths;
+
+ std::vector<BlobJournalType> journals;
+
+ { // Empty journal
+ BlobJournalType journal;
+ journals.push_back(journal);
+ }
+
+ { // One item
+ BlobJournalType journal;
+ journal.push_back(std::make_pair(4, 7));
+ journals.push_back(journal);
+ }
+
+ { // kAllBlobsKey
+ BlobJournalType journal;
+ journal.push_back(std::make_pair(5, DatabaseMetaDataKey::kAllBlobsKey));
+ journals.push_back(journal);
+ }
+
+ { // A bunch of items
+ BlobJournalType journal;
+ journal.push_back(std::make_pair(4, 7));
+ journal.push_back(std::make_pair(5, 6));
+ journal.push_back(std::make_pair(4, 5));
+ journal.push_back(std::make_pair(4, 4));
+ journal.push_back(std::make_pair(1, 12));
+ journal.push_back(std::make_pair(4, 3));
+ journal.push_back(std::make_pair(15, 14));
+ journals.push_back(journal);
+ }
+
+ std::vector<BlobJournalType>::const_iterator journal_iter;
+ for (journal_iter = journals.begin(); journal_iter != journals.end();
+ ++journal_iter) {
+ std::string encoding;
+ EncodeBlobJournal(*journal_iter, &encoding);
+ StringPiece slice(encoding);
+ BlobJournalType journal_out;
+ EXPECT_TRUE(DecodeBlobJournal(&slice, &journal_out));
+ EXPECT_EQ(*journal_iter, journal_out);
+ }
+
+ journals.clear();
+
+ { // Illegal database id
+ BlobJournalType journal;
+ journal.push_back(std::make_pair(0, 3));
+ journals.push_back(journal);
+ }
+
+ { // Illegal blob id
+ BlobJournalType journal;
+ journal.push_back(std::make_pair(4, 0));
+ journals.push_back(journal);
+ }
+
+ for (journal_iter = journals.begin(); journal_iter != journals.end();
+ ++journal_iter) {
+ std::string encoding;
+ EncodeBlobJournal(*journal_iter, &encoding);
+ StringPiece slice(encoding);
+ BlobJournalType journal_out;
+ EXPECT_FALSE(DecodeBlobJournal(&slice, &journal_out));
+ }
+}
+
TEST(IndexedDBLevelDBCodingTest, DecodeLegacyIDBKeyPath) {
// Legacy encoding of string key paths.
std::vector<IndexedDBKeyPath> key_paths;
diff --git a/chromium/content/browser/indexed_db/indexed_db_metadata.h b/chromium/content/browser/indexed_db/indexed_db_metadata.h
index 00970a61ee5..68df82ed842 100644
--- a/chromium/content/browser/indexed_db/indexed_db_metadata.h
+++ b/chromium/content/browser/indexed_db/indexed_db_metadata.h
@@ -79,6 +79,6 @@ struct CONTENT_EXPORT IndexedDBDatabaseMetadata {
ObjectStoreMap object_stores;
};
-}
+} // namespace content
#endif // CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_METADATA_H_
diff --git a/chromium/content/browser/indexed_db/indexed_db_pending_connection.cc b/chromium/content/browser/indexed_db/indexed_db_pending_connection.cc
new file mode 100644
index 00000000000..b1d63180852
--- /dev/null
+++ b/chromium/content/browser/indexed_db/indexed_db_pending_connection.cc
@@ -0,0 +1,23 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/indexed_db/indexed_db_pending_connection.h"
+
+namespace content {
+
+IndexedDBPendingConnection::IndexedDBPendingConnection(
+ scoped_refptr<IndexedDBCallbacks> callbacks_in,
+ scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks_in,
+ int child_process_id_in,
+ int64 transaction_id_in,
+ int64 version_in)
+ : callbacks(callbacks_in),
+ database_callbacks(database_callbacks_in),
+ child_process_id(child_process_id_in),
+ transaction_id(transaction_id_in),
+ version(version_in) {}
+
+IndexedDBPendingConnection::~IndexedDBPendingConnection() {}
+
+} // namespace content
diff --git a/chromium/content/browser/indexed_db/indexed_db_pending_connection.h b/chromium/content/browser/indexed_db/indexed_db_pending_connection.h
new file mode 100644
index 00000000000..598bef2641b
--- /dev/null
+++ b/chromium/content/browser/indexed_db/indexed_db_pending_connection.h
@@ -0,0 +1,36 @@
+// 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_INDEXED_DB_INDEXED_DB_PENDING_CONNECTION_H_
+#define CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_PENDING_CONNECTION_H_
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "content/browser/indexed_db/indexed_db_callbacks.h"
+#include "content/browser/indexed_db/indexed_db_database_callbacks.h"
+#include "content/common/content_export.h"
+
+namespace content {
+
+class IndexedDBCallbacks;
+class IndexedDBDatabaseCallbacks;
+
+struct CONTENT_EXPORT IndexedDBPendingConnection {
+ IndexedDBPendingConnection(
+ scoped_refptr<IndexedDBCallbacks> callbacks_in,
+ scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks_in,
+ int child_process_id_in,
+ int64 transaction_id_in,
+ int64 version_in);
+ ~IndexedDBPendingConnection();
+ scoped_refptr<IndexedDBCallbacks> callbacks;
+ scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks;
+ int child_process_id;
+ int64 transaction_id;
+ int64 version;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_PENDING_CONNECTION_H_
diff --git a/chromium/content/browser/indexed_db/indexed_db_quota_client.h b/chromium/content/browser/indexed_db/indexed_db_quota_client.h
index 3c48c279ccf..6f4c50ef690 100644
--- a/chromium/content/browser/indexed_db/indexed_db_quota_client.h
+++ b/chromium/content/browser/indexed_db/indexed_db_quota_client.h
@@ -32,17 +32,21 @@ class IndexedDBQuotaClient : public quota::QuotaClient,
// QuotaClient method overrides
virtual ID id() const OVERRIDE;
virtual void OnQuotaManagerDestroyed() OVERRIDE;
- virtual void GetOriginUsage(const GURL& origin_url,
- quota::StorageType type,
- const GetUsageCallback& callback) OVERRIDE;
- virtual void GetOriginsForType(quota::StorageType type,
- const GetOriginsCallback& callback) OVERRIDE;
- virtual void GetOriginsForHost(quota::StorageType type,
- const std::string& host,
- const GetOriginsCallback& callback) OVERRIDE;
- virtual void DeleteOriginData(const GURL& origin,
- quota::StorageType type,
- const DeletionCallback& callback) OVERRIDE;
+ CONTENT_EXPORT virtual void GetOriginUsage(const GURL& origin_url,
+ quota::StorageType type,
+ const GetUsageCallback& callback)
+ OVERRIDE;
+ CONTENT_EXPORT virtual void GetOriginsForType(
+ quota::StorageType type,
+ const GetOriginsCallback& callback) OVERRIDE;
+ CONTENT_EXPORT virtual void GetOriginsForHost(
+ quota::StorageType type,
+ const std::string& host,
+ const GetOriginsCallback& callback) OVERRIDE;
+ CONTENT_EXPORT virtual void DeleteOriginData(const GURL& origin,
+ quota::StorageType type,
+ const DeletionCallback& callback)
+ OVERRIDE;
virtual bool DoesSupport(quota::StorageType type) const OVERRIDE;
private:
diff --git a/chromium/content/browser/indexed_db/indexed_db_quota_client_unittest.cc b/chromium/content/browser/indexed_db/indexed_db_quota_client_unittest.cc
index cd2f146deb7..54ce775d6e2 100644
--- a/chromium/content/browser/indexed_db/indexed_db_quota_client_unittest.cc
+++ b/chromium/content/browser/indexed_db/indexed_db_quota_client_unittest.cc
@@ -14,11 +14,11 @@
#include "content/browser/browser_thread_impl.h"
#include "content/browser/indexed_db/indexed_db_context_impl.h"
#include "content/browser/indexed_db/indexed_db_quota_client.h"
+#include "content/browser/quota/mock_quota_manager.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "webkit/browser/quota/mock_quota_manager.h"
#include "webkit/common/database/database_identifier.h"
// Declared to shorten the line lengths.
@@ -44,7 +44,7 @@ class IndexedDBQuotaClientTest : public testing::Test {
browser_context_.reset(new TestBrowserContext());
scoped_refptr<quota::QuotaManager> quota_manager =
- new quota::MockQuotaManager(
+ new MockQuotaManager(
false /*in_memory*/,
browser_context_->GetPath(),
base::MessageLoop::current()->message_loop_proxy(),
@@ -135,7 +135,7 @@ class IndexedDBQuotaClientTest : public testing::Test {
void SetFileSizeTo(const base::FilePath& path, int size) {
std::string junk(size, 'a');
- ASSERT_EQ(size, file_util::WriteFile(path, junk.c_str(), size));
+ ASSERT_EQ(size, base::WriteFile(path, junk.c_str(), size));
}
void AddFakeIndexedDB(const GURL& origin, int size) {
diff --git a/chromium/content/browser/indexed_db/indexed_db_tracing.h b/chromium/content/browser/indexed_db/indexed_db_tracing.h
index 86ddaa34bfa..b7a5a4157d3 100644
--- a/chromium/content/browser/indexed_db/indexed_db_tracing.h
+++ b/chromium/content/browser/indexed_db/indexed_db_tracing.h
@@ -7,5 +7,7 @@
#include "base/debug/trace_event.h"
#define IDB_TRACE(a) TRACE_EVENT0("IndexedDB", (a));
+#define IDB_TRACE1(a, arg1_name, arg1_val) \
+ TRACE_EVENT1("IndexedDB", (a), (arg1_name), (arg1_val));
#endif // CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_TRACING_H_
diff --git a/chromium/content/browser/indexed_db/indexed_db_transaction.cc b/chromium/content/browser/indexed_db/indexed_db_transaction.cc
index 387b1500efa..4de6784ae8c 100644
--- a/chromium/content/browser/indexed_db/indexed_db_transaction.cc
+++ b/chromium/content/browser/indexed_db/indexed_db_transaction.cc
@@ -81,24 +81,14 @@ IndexedDBTransaction::~IndexedDBTransaction() {
// complete or aborted.
DCHECK_EQ(state_, FINISHED);
DCHECK(preemptive_task_queue_.empty());
+ DCHECK_EQ(pending_preemptive_events_, 0);
DCHECK(task_queue_.empty());
DCHECK(abort_task_stack_.empty());
}
-void IndexedDBTransaction::ScheduleTask(Operation task, Operation abort_task) {
- if (state_ == FINISHED)
- return;
-
- timeout_timer_.Stop();
- used_ = true;
- task_queue_.push(task);
- ++diagnostics_.tasks_scheduled;
- abort_task_stack_.push(abort_task);
- RunTasksIfStarted();
-}
-
void IndexedDBTransaction::ScheduleTask(IndexedDBDatabase::TaskType type,
Operation task) {
+ DCHECK_NE(state_, COMMITTING);
if (state_ == FINISHED)
return;
@@ -113,6 +103,12 @@ void IndexedDBTransaction::ScheduleTask(IndexedDBDatabase::TaskType type,
RunTasksIfStarted();
}
+void IndexedDBTransaction::ScheduleAbortTask(Operation abort_task) {
+ DCHECK_NE(FINISHED, state_);
+ DCHECK(used_);
+ abort_task_stack_.push(abort_task);
+}
+
void IndexedDBTransaction::RunTasksIfStarted() {
DCHECK(used_);
@@ -135,7 +131,7 @@ void IndexedDBTransaction::Abort() {
}
void IndexedDBTransaction::Abort(const IndexedDBDatabaseError& error) {
- IDB_TRACE("IndexedDBTransaction::Abort");
+ IDB_TRACE1("IndexedDBTransaction::Abort", "txn.id", id());
if (state_ == FINISHED)
return;
@@ -154,9 +150,10 @@ void IndexedDBTransaction::Abort(const IndexedDBDatabaseError& error) {
// Run the abort tasks, if any.
while (!abort_task_stack_.empty())
- abort_task_stack_.pop().Run(0);
+ abort_task_stack_.pop().Run(NULL);
preemptive_task_queue_.clear();
+ pending_preemptive_events_ = 0;
task_queue_.clear();
// Backing store resources (held via cursors) must be released
@@ -173,12 +170,11 @@ void IndexedDBTransaction::Abort(const IndexedDBDatabaseError& error) {
#ifndef NDEBUG
DCHECK(!database_->transaction_coordinator().IsActive(this));
#endif
- database_->TransactionFinished(this);
if (callbacks_.get())
callbacks_->OnAbort(id_, error);
- database_->TransactionFinishedAndAbortFired(this);
+ database_->TransactionFinished(this, false);
database_ = NULL;
}
@@ -203,7 +199,6 @@ void IndexedDBTransaction::Start() {
// TransactionCoordinator has started this transaction.
DCHECK_EQ(CREATED, state_);
state_ = STARTED;
- database_->TransactionStarted(this);
diagnostics_.start_time = base::Time::Now();
if (!used_)
@@ -212,14 +207,43 @@ void IndexedDBTransaction::Start() {
RunTasksIfStarted();
}
+class BlobWriteCallbackImpl : public IndexedDBBackingStore::BlobWriteCallback {
+ public:
+ explicit BlobWriteCallbackImpl(
+ scoped_refptr<IndexedDBTransaction> transaction)
+ : transaction_(transaction) {}
+ virtual void Run(bool succeeded) OVERRIDE {
+ transaction_->BlobWriteComplete(succeeded);
+ }
+
+ protected:
+ virtual ~BlobWriteCallbackImpl() {}
+
+ private:
+ scoped_refptr<IndexedDBTransaction> transaction_;
+};
+
+void IndexedDBTransaction::BlobWriteComplete(bool success) {
+ IDB_TRACE("IndexedDBTransaction::BlobWriteComplete");
+ if (state_ == FINISHED) // aborted
+ return;
+ DCHECK_EQ(state_, COMMITTING);
+ if (success)
+ CommitPhaseTwo();
+ else
+ Abort(IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionDataError,
+ "Failed to write blobs."));
+}
+
void IndexedDBTransaction::Commit() {
- IDB_TRACE("IndexedDBTransaction::Commit");
+ IDB_TRACE1("IndexedDBTransaction::Commit", "txn.id", id());
// In multiprocess ports, front-end may have requested a commit but
// an abort has already been initiated asynchronously by the
// back-end.
if (state_ == FINISHED)
return;
+ DCHECK_NE(state_, COMMITTING);
DCHECK(!used_ || state_ == STARTED);
commit_pending_ = true;
@@ -230,6 +254,28 @@ void IndexedDBTransaction::Commit() {
if (HasPendingTasks())
return;
+ state_ = COMMITTING;
+
+ if (!used_) {
+ CommitPhaseTwo();
+ } else {
+ scoped_refptr<IndexedDBBackingStore::BlobWriteCallback> callback(
+ new BlobWriteCallbackImpl(this));
+ // CommitPhaseOne will call the callback synchronously if there are no blobs
+ // to write.
+ if (!transaction_->CommitPhaseOne(callback).ok())
+ Abort(IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionDataError,
+ "Error processing blob journal."));
+ }
+}
+
+void IndexedDBTransaction::CommitPhaseTwo() {
+ // Abort may have been called just as the blob write completed.
+ if (state_ == FINISHED)
+ return;
+
+ DCHECK_EQ(state_, COMMITTING);
+
// The last reference to this object may be released while performing the
// commit steps below. We therefore take a self reference to keep ourselves
// alive while executing this method.
@@ -239,7 +285,7 @@ void IndexedDBTransaction::Commit() {
state_ = FINISHED;
- bool committed = !used_ || transaction_->Commit();
+ bool committed = !used_ || transaction_->CommitPhaseTwo().ok();
// Backing store resources (held via cursors) must be released
// before script callbacks are fired, as the script callbacks may
@@ -252,21 +298,20 @@ void IndexedDBTransaction::Commit() {
// front-end is notified, as the transaction completion unblocks
// operations like closing connections.
database_->transaction_coordinator().DidFinishTransaction(this);
- database_->TransactionFinished(this);
if (committed) {
abort_task_stack_.clear();
callbacks_->OnComplete(id_);
- database_->TransactionFinishedAndCompleteFired(this);
+ database_->TransactionFinished(this, true);
} else {
while (!abort_task_stack_.empty())
- abort_task_stack_.pop().Run(0);
+ abort_task_stack_.pop().Run(NULL);
callbacks_->OnAbort(
id_,
IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
"Internal error committing transaction."));
- database_->TransactionFinishedAndAbortFired(this);
+ database_->TransactionFinished(this, false);
database_->TransactionCommitFailed();
}
@@ -274,7 +319,7 @@ void IndexedDBTransaction::Commit() {
}
void IndexedDBTransaction::ProcessTaskQueue() {
- IDB_TRACE("IndexedDBTransaction::ProcessTaskQueue");
+ IDB_TRACE1("IndexedDBTransaction::ProcessTaskQueue", "txn.id", id());
// May have been aborted.
if (!should_process_queue_)
@@ -296,7 +341,7 @@ void IndexedDBTransaction::ProcessTaskQueue() {
TaskQueue* task_queue =
pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_;
while (!task_queue->empty() && state_ != FINISHED) {
- DCHECK_EQ(STARTED, state_);
+ DCHECK_EQ(state_, STARTED);
Operation task(task_queue->pop());
task.Run(this);
if (!pending_preemptive_events_) {
@@ -320,18 +365,23 @@ void IndexedDBTransaction::ProcessTaskQueue() {
if (state_ == FINISHED)
return;
+ DCHECK(state_ == STARTED);
+
// Otherwise, start a timer in case the front-end gets wedged and
- // never requests further activity.
- timeout_timer_.Start(
- FROM_HERE,
- base::TimeDelta::FromSeconds(kInactivityTimeoutPeriodSeconds),
- base::Bind(&IndexedDBTransaction::Timeout, this));
+ // never requests further activity. Read-only transactions don't
+ // block other transactions, so don't time those out.
+ if (mode_ != indexed_db::TRANSACTION_READ_ONLY) {
+ timeout_timer_.Start(
+ FROM_HERE,
+ base::TimeDelta::FromSeconds(kInactivityTimeoutPeriodSeconds),
+ base::Bind(&IndexedDBTransaction::Timeout, this));
+ }
}
void IndexedDBTransaction::Timeout() {
Abort(IndexedDBDatabaseError(
blink::WebIDBDatabaseExceptionTimeoutError,
- ASCIIToUTF16("Transaction timed out due to inactivity.")));
+ base::ASCIIToUTF16("Transaction timed out due to inactivity.")));
}
void IndexedDBTransaction::CloseOpenCursors() {
diff --git a/chromium/content/browser/indexed_db/indexed_db_transaction.h b/chromium/content/browser/indexed_db/indexed_db_transaction.h
index 6684856e573..e30422f15ab 100644
--- a/chromium/content/browser/indexed_db/indexed_db_transaction.h
+++ b/chromium/content/browser/indexed_db/indexed_db_transaction.h
@@ -20,6 +20,7 @@
namespace content {
+class BlobWriteCallbackImpl;
class IndexedDBCursor;
class IndexedDBDatabaseCallbacks;
@@ -49,8 +50,8 @@ class CONTENT_EXPORT IndexedDBTransaction
void ScheduleTask(Operation task) {
ScheduleTask(IndexedDBDatabase::NORMAL_TASK, task);
}
- void ScheduleTask(Operation task, Operation abort_task);
void ScheduleTask(IndexedDBDatabase::TaskType, Operation task);
+ void ScheduleAbortTask(Operation abort_task);
void RegisterOpenCursor(IndexedDBCursor* cursor);
void UnregisterOpenCursor(IndexedDBCursor* cursor);
void AddPreemptiveEvent() { pending_preemptive_events_++; }
@@ -67,9 +68,11 @@ class CONTENT_EXPORT IndexedDBTransaction
IndexedDBDatabaseCallbacks* connection() const { return callbacks_; }
enum State {
- CREATED, // Created, but not yet started by coordinator.
- STARTED, // Started by the coordinator.
- FINISHED, // Either aborted or committed.
+ CREATED, // Created, but not yet started by coordinator.
+ STARTED, // Started by the coordinator.
+ COMMITTING, // In the process of committing, possibly waiting for blobs
+ // to be written.
+ FINISHED, // Either aborted or committed.
};
State state() const { return state_; }
@@ -85,6 +88,15 @@ class CONTENT_EXPORT IndexedDBTransaction
const Diagnostics& diagnostics() const { return diagnostics_; }
private:
+ friend class BlobWriteCallbackImpl;
+
+ FRIEND_TEST_ALL_PREFIXES(IndexedDBTransactionTestMode, AbortPreemptive);
+ FRIEND_TEST_ALL_PREFIXES(IndexedDBTransactionTest, Timeout);
+ FRIEND_TEST_ALL_PREFIXES(IndexedDBTransactionTest,
+ SchedulePreemptiveTask);
+ FRIEND_TEST_ALL_PREFIXES(IndexedDBTransactionTestMode,
+ ScheduleNormalTask);
+
friend class base::RefCounted<IndexedDBTransaction>;
virtual ~IndexedDBTransaction();
@@ -93,8 +105,10 @@ class CONTENT_EXPORT IndexedDBTransaction
bool IsTaskQueueEmpty() const;
bool HasPendingTasks() const;
+ void BlobWriteComplete(bool success);
void ProcessTaskQueue();
void CloseOpenCursors();
+ void CommitPhaseTwo();
void Timeout();
const int64 id_;
@@ -118,6 +132,8 @@ class CONTENT_EXPORT IndexedDBTransaction
private:
std::queue<Operation> queue_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaskQueue);
};
class TaskStack {
@@ -131,6 +147,8 @@ class CONTENT_EXPORT IndexedDBTransaction
private:
std::stack<Operation> stack_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaskStack);
};
TaskQueue task_queue_;
diff --git a/chromium/content/browser/indexed_db/indexed_db_transaction_coordinator.cc b/chromium/content/browser/indexed_db/indexed_db_transaction_coordinator.cc
index be47624cf4a..a1eaad41ecd 100644
--- a/chromium/content/browser/indexed_db/indexed_db_transaction_coordinator.cc
+++ b/chromium/content/browser/indexed_db/indexed_db_transaction_coordinator.cc
@@ -28,7 +28,7 @@ void IndexedDBTransactionCoordinator::DidCreateTransaction(
}
void IndexedDBTransactionCoordinator::DidFinishTransaction(
- scoped_refptr<IndexedDBTransaction> transaction) {
+ IndexedDBTransaction* transaction) {
if (queued_transactions_.count(transaction)) {
DCHECK(!started_transactions_.count(transaction));
queued_transactions_.erase(transaction);
@@ -40,6 +40,13 @@ void IndexedDBTransactionCoordinator::DidFinishTransaction(
ProcessQueuedTransactions();
}
+bool IndexedDBTransactionCoordinator::IsRunningVersionChangeTransaction()
+ const {
+ return !started_transactions_.empty() &&
+ (*started_transactions_.begin())->mode() ==
+ indexed_db::TRANSACTION_VERSION_CHANGE;
+}
+
#ifndef NDEBUG
// Verifies internal consistency while returning whether anything is found.
bool IndexedDBTransactionCoordinator::IsActive(
@@ -77,9 +84,7 @@ void IndexedDBTransactionCoordinator::ProcessQueuedTransactions() {
if (queued_transactions_.empty())
return;
- DCHECK(started_transactions_.empty() ||
- (*started_transactions_.begin())->mode() !=
- indexed_db::TRANSACTION_VERSION_CHANGE);
+ DCHECK(!IsRunningVersionChangeTransaction());
// The locked_scope set accumulates the ids of object stores in the scope of
// running read/write transactions. Other read-write transactions with
@@ -143,7 +148,7 @@ bool IndexedDBTransactionCoordinator::CanStartTransaction(
DCHECK(queued_transactions_.count(transaction));
switch (transaction->mode()) {
case indexed_db::TRANSACTION_VERSION_CHANGE:
- DCHECK_EQ(static_cast<size_t>(1), queued_transactions_.size());
+ DCHECK_EQ(1u, queued_transactions_.size());
DCHECK(started_transactions_.empty());
DCHECK(locked_scope.empty());
return true;
diff --git a/chromium/content/browser/indexed_db/indexed_db_transaction_coordinator.h b/chromium/content/browser/indexed_db/indexed_db_transaction_coordinator.h
index a5d543c1d8c..98db0b39de0 100644
--- a/chromium/content/browser/indexed_db/indexed_db_transaction_coordinator.h
+++ b/chromium/content/browser/indexed_db/indexed_db_transaction_coordinator.h
@@ -25,7 +25,9 @@ class IndexedDBTransactionCoordinator {
// Called by transactions as they start and finish.
void DidCreateTransaction(scoped_refptr<IndexedDBTransaction> transaction);
- void DidFinishTransaction(scoped_refptr<IndexedDBTransaction> transaction);
+ void DidFinishTransaction(IndexedDBTransaction* transaction);
+
+ bool IsRunningVersionChangeTransaction() const;
#ifndef NDEBUG
bool IsActive(IndexedDBTransaction* transaction);
@@ -45,6 +47,8 @@ class IndexedDBTransactionCoordinator {
typedef list_set<scoped_refptr<IndexedDBTransaction> > TransactionSet;
TransactionSet queued_transactions_;
TransactionSet started_transactions_;
+
+ DISALLOW_COPY_AND_ASSIGN(IndexedDBTransactionCoordinator);
};
} // namespace content
diff --git a/chromium/content/browser/indexed_db/indexed_db_transaction_unittest.cc b/chromium/content/browser/indexed_db/indexed_db_transaction_unittest.cc
index 9aa68fa55dc..c6a1244d4dc 100644
--- a/chromium/content/browser/indexed_db/indexed_db_transaction_unittest.cc
+++ b/chromium/content/browser/indexed_db/indexed_db_transaction_unittest.cc
@@ -14,21 +14,49 @@
namespace content {
-namespace {
+class AbortObserver {
+ public:
+ AbortObserver() : abort_task_called_(false) {}
+
+ void AbortTask(IndexedDBTransaction* transaction) {
+ abort_task_called_ = true;
+ }
+
+ bool abort_task_called() const { return abort_task_called_; }
+
+ private:
+ bool abort_task_called_;
+ DISALLOW_COPY_AND_ASSIGN(AbortObserver);
+};
class IndexedDBTransactionTest : public testing::Test {
public:
IndexedDBTransactionTest() {
- IndexedDBFactory* factory = NULL;
backing_store_ = new IndexedDBFakeBackingStore();
- db_ = IndexedDBDatabase::Create(ASCIIToUTF16("db"),
+ CreateDB();
+ }
+
+ void CreateDB() {
+ // DB is created here instead of the constructor to workaround a
+ // "peculiarity of C++". More info at
+ // https://code.google.com/p/googletest/wiki/FAQ#My_compiler_complains_that_a_constructor_(or_destructor)_cannot
+ IndexedDBFactory* factory = NULL;
+ leveldb::Status s;
+ db_ = IndexedDBDatabase::Create(base::ASCIIToUTF16("db"),
backing_store_,
factory,
- IndexedDBDatabase::Identifier());
+ IndexedDBDatabase::Identifier(),
+ &s);
+ ASSERT_TRUE(s.ok());
}
void RunPostedTasks() { message_loop_.RunUntilIdle(); }
void DummyOperation(IndexedDBTransaction* transaction) {}
+ void AbortableOperation(AbortObserver* observer,
+ IndexedDBTransaction* transaction) {
+ transaction->ScheduleAbortTask(
+ base::Bind(&AbortObserver::AbortTask, base::Unretained(observer)));
+ }
protected:
scoped_refptr<IndexedDBFakeBackingStore> backing_store_;
@@ -40,15 +68,23 @@ class IndexedDBTransactionTest : public testing::Test {
DISALLOW_COPY_AND_ASSIGN(IndexedDBTransactionTest);
};
+class IndexedDBTransactionTestMode : public IndexedDBTransactionTest,
+ public testing::WithParamInterface<indexed_db::TransactionMode> {
+ public:
+ IndexedDBTransactionTestMode() {}
+ private:
+ DISALLOW_COPY_AND_ASSIGN(IndexedDBTransactionTestMode);
+};
+
TEST_F(IndexedDBTransactionTest, Timeout) {
const int64 id = 0;
const std::set<int64> scope;
- const bool commit_success = true;
+ const leveldb::Status commit_success = leveldb::Status::OK();
scoped_refptr<IndexedDBTransaction> transaction = new IndexedDBTransaction(
id,
new MockIndexedDBDatabaseCallbacks(),
scope,
- indexed_db::TRANSACTION_READ_ONLY,
+ indexed_db::TRANSACTION_READ_WRITE,
db_,
new IndexedDBFakeBackingStore::FakeTransaction(commit_success));
db_->TransactionCreated(transaction);
@@ -56,60 +92,207 @@ TEST_F(IndexedDBTransactionTest, Timeout) {
// No conflicting transactions, so coordinator will start it immediately:
EXPECT_EQ(IndexedDBTransaction::STARTED, transaction->state());
EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
+ EXPECT_EQ(0, transaction->diagnostics().tasks_scheduled);
+ EXPECT_EQ(0, transaction->diagnostics().tasks_completed);
// Schedule a task - timer won't be started until it's processed.
transaction->ScheduleTask(base::Bind(
&IndexedDBTransactionTest::DummyOperation, base::Unretained(this)));
EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
+ EXPECT_EQ(1, transaction->diagnostics().tasks_scheduled);
+ EXPECT_EQ(0, transaction->diagnostics().tasks_completed);
RunPostedTasks();
EXPECT_TRUE(transaction->IsTimeoutTimerRunning());
- // Abort should cancel the timer.
- transaction->Abort();
+ transaction->Timeout();
EXPECT_EQ(IndexedDBTransaction::FINISHED, transaction->state());
EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
+ EXPECT_EQ(1, transaction->diagnostics().tasks_scheduled);
+ EXPECT_EQ(1, transaction->diagnostics().tasks_completed);
// This task will be ignored.
transaction->ScheduleTask(base::Bind(
&IndexedDBTransactionTest::DummyOperation, base::Unretained(this)));
EXPECT_EQ(IndexedDBTransaction::FINISHED, transaction->state());
EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
+ EXPECT_EQ(1, transaction->diagnostics().tasks_scheduled);
+ EXPECT_EQ(1, transaction->diagnostics().tasks_completed);
}
-class AbortObserver {
- public:
- AbortObserver() : abort_task_called_(false) {}
+TEST_F(IndexedDBTransactionTest, NoTimeoutReadOnly) {
+ const int64 id = 0;
+ const std::set<int64> scope;
+ const leveldb::Status commit_success = leveldb::Status::OK();
+ scoped_refptr<IndexedDBTransaction> transaction = new IndexedDBTransaction(
+ id,
+ new MockIndexedDBDatabaseCallbacks(),
+ scope,
+ indexed_db::TRANSACTION_READ_ONLY,
+ db_,
+ new IndexedDBFakeBackingStore::FakeTransaction(commit_success));
+ db_->TransactionCreated(transaction);
- void AbortTask(IndexedDBTransaction* transaction) {
- abort_task_called_ = true;
- }
+ // No conflicting transactions, so coordinator will start it immediately:
+ EXPECT_EQ(IndexedDBTransaction::STARTED, transaction->state());
+ EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
- bool abort_task_called() const { return abort_task_called_; }
+ // Schedule a task - timer won't be started until it's processed.
+ transaction->ScheduleTask(base::Bind(
+ &IndexedDBTransactionTest::DummyOperation, base::Unretained(this)));
+ EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
- private:
- bool abort_task_called_;
- DISALLOW_COPY_AND_ASSIGN(AbortObserver);
-};
+ // Transaction is read-only, so no need to time it out.
+ RunPostedTasks();
+ EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
+
+ // Clean up to avoid leaks.
+ transaction->Abort();
+ EXPECT_EQ(IndexedDBTransaction::FINISHED, transaction->state());
+ EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
+}
-TEST_F(IndexedDBTransactionTest, AbortTasks) {
+TEST_P(IndexedDBTransactionTestMode, ScheduleNormalTask) {
const int64 id = 0;
const std::set<int64> scope;
- const bool commit_failure = false;
+ const leveldb::Status commit_success = leveldb::Status::OK();
scoped_refptr<IndexedDBTransaction> transaction = new IndexedDBTransaction(
id,
new MockIndexedDBDatabaseCallbacks(),
scope,
- indexed_db::TRANSACTION_READ_ONLY,
+ GetParam(),
+ db_,
+ new IndexedDBFakeBackingStore::FakeTransaction(commit_success));
+
+ EXPECT_FALSE(transaction->HasPendingTasks());
+ EXPECT_TRUE(transaction->IsTaskQueueEmpty());
+ EXPECT_TRUE(transaction->task_queue_.empty());
+ EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
+ EXPECT_EQ(0, transaction->diagnostics().tasks_scheduled);
+ EXPECT_EQ(0, transaction->diagnostics().tasks_completed);
+
+ db_->TransactionCreated(transaction);
+
+ EXPECT_FALSE(transaction->HasPendingTasks());
+ EXPECT_TRUE(transaction->IsTaskQueueEmpty());
+ EXPECT_TRUE(transaction->task_queue_.empty());
+ EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
+
+ transaction->ScheduleTask(
+ IndexedDBDatabase::NORMAL_TASK,
+ base::Bind(&IndexedDBTransactionTest::DummyOperation,
+ base::Unretained(this)));
+
+ EXPECT_EQ(1, transaction->diagnostics().tasks_scheduled);
+ EXPECT_EQ(0, transaction->diagnostics().tasks_completed);
+
+ EXPECT_TRUE(transaction->HasPendingTasks());
+ EXPECT_FALSE(transaction->IsTaskQueueEmpty());
+ EXPECT_FALSE(transaction->task_queue_.empty());
+ EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
+
+ // Pump the message loop so that the transaction completes all pending tasks,
+ // otherwise it will defer the commit.
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_FALSE(transaction->HasPendingTasks());
+ EXPECT_TRUE(transaction->IsTaskQueueEmpty());
+ EXPECT_TRUE(transaction->task_queue_.empty());
+ EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
+ EXPECT_EQ(IndexedDBTransaction::STARTED, transaction->state());
+ EXPECT_EQ(1, transaction->diagnostics().tasks_scheduled);
+ EXPECT_EQ(1, transaction->diagnostics().tasks_completed);
+
+ transaction->Commit();
+
+ EXPECT_EQ(IndexedDBTransaction::FINISHED, transaction->state());
+ EXPECT_FALSE(transaction->HasPendingTasks());
+ EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
+ EXPECT_TRUE(transaction->IsTaskQueueEmpty());
+ EXPECT_TRUE(transaction->task_queue_.empty());
+ EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
+ EXPECT_EQ(1, transaction->diagnostics().tasks_scheduled);
+ EXPECT_EQ(1, transaction->diagnostics().tasks_completed);
+}
+
+TEST_F(IndexedDBTransactionTest, SchedulePreemptiveTask) {
+ const int64 id = 0;
+ const std::set<int64> scope;
+ const leveldb::Status commit_failure = leveldb::Status::Corruption("Ouch.");
+ scoped_refptr<IndexedDBTransaction> transaction = new IndexedDBTransaction(
+ id,
+ new MockIndexedDBDatabaseCallbacks(),
+ scope,
+ indexed_db::TRANSACTION_VERSION_CHANGE,
db_,
new IndexedDBFakeBackingStore::FakeTransaction(commit_failure));
+
+ EXPECT_FALSE(transaction->HasPendingTasks());
+ EXPECT_TRUE(transaction->IsTaskQueueEmpty());
+ EXPECT_TRUE(transaction->task_queue_.empty());
+ EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
+ EXPECT_EQ(0, transaction->diagnostics().tasks_scheduled);
+ EXPECT_EQ(0, transaction->diagnostics().tasks_completed);
+
db_->TransactionCreated(transaction);
- AbortObserver observer;
+ EXPECT_FALSE(transaction->HasPendingTasks());
+ EXPECT_TRUE(transaction->IsTaskQueueEmpty());
+ EXPECT_TRUE(transaction->task_queue_.empty());
+ EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
+
transaction->ScheduleTask(
+ IndexedDBDatabase::PREEMPTIVE_TASK,
base::Bind(&IndexedDBTransactionTest::DummyOperation,
- base::Unretained(this)),
- base::Bind(&AbortObserver::AbortTask, base::Unretained(&observer)));
+ base::Unretained(this)));
+ transaction->AddPreemptiveEvent();
+
+ EXPECT_TRUE(transaction->HasPendingTasks());
+ EXPECT_FALSE(transaction->IsTaskQueueEmpty());
+ EXPECT_TRUE(transaction->task_queue_.empty());
+ EXPECT_FALSE(transaction->preemptive_task_queue_.empty());
+
+ // Pump the message loop so that the transaction completes all pending tasks,
+ // otherwise it will defer the commit.
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_TRUE(transaction->HasPendingTasks());
+ EXPECT_TRUE(transaction->IsTaskQueueEmpty());
+ EXPECT_TRUE(transaction->task_queue_.empty());
+ EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
+ EXPECT_EQ(IndexedDBTransaction::STARTED, transaction->state());
+ EXPECT_EQ(0, transaction->diagnostics().tasks_scheduled);
+ EXPECT_EQ(0, transaction->diagnostics().tasks_completed);
+
+ transaction->DidCompletePreemptiveEvent();
+ transaction->Commit();
+
+ EXPECT_EQ(IndexedDBTransaction::FINISHED, transaction->state());
+ EXPECT_FALSE(transaction->HasPendingTasks());
+ EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
+ EXPECT_TRUE(transaction->IsTaskQueueEmpty());
+ EXPECT_TRUE(transaction->task_queue_.empty());
+ EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
+ EXPECT_EQ(0, transaction->diagnostics().tasks_scheduled);
+ EXPECT_EQ(0, transaction->diagnostics().tasks_completed);
+}
+
+TEST_P(IndexedDBTransactionTestMode, AbortTasks) {
+ const int64 id = 0;
+ const std::set<int64> scope;
+ const leveldb::Status commit_failure = leveldb::Status::Corruption("Ouch.");
+ scoped_refptr<IndexedDBTransaction> transaction = new IndexedDBTransaction(
+ id,
+ new MockIndexedDBDatabaseCallbacks(),
+ scope,
+ GetParam(),
+ db_,
+ new IndexedDBFakeBackingStore::FakeTransaction(commit_failure));
+ db_->TransactionCreated(transaction);
+
+ AbortObserver observer;
+ transaction->ScheduleTask(
+ base::Bind(&IndexedDBTransactionTest::AbortableOperation,
+ base::Unretained(this),
+ base::Unretained(&observer)));
// Pump the message loop so that the transaction completes all pending tasks,
// otherwise it will defer the commit.
@@ -118,8 +301,69 @@ TEST_F(IndexedDBTransactionTest, AbortTasks) {
EXPECT_FALSE(observer.abort_task_called());
transaction->Commit();
EXPECT_TRUE(observer.abort_task_called());
+ EXPECT_EQ(IndexedDBTransaction::FINISHED, transaction->state());
+ EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
+}
+
+TEST_P(IndexedDBTransactionTestMode, AbortPreemptive) {
+ const int64 id = 0;
+ const std::set<int64> scope;
+ const leveldb::Status commit_success = leveldb::Status::OK();
+ scoped_refptr<IndexedDBTransaction> transaction = new IndexedDBTransaction(
+ id,
+ new MockIndexedDBDatabaseCallbacks(),
+ scope,
+ GetParam(),
+ db_,
+ new IndexedDBFakeBackingStore::FakeTransaction(commit_success));
+ db_->TransactionCreated(transaction);
+
+ // No conflicting transactions, so coordinator will start it immediately:
+ EXPECT_EQ(IndexedDBTransaction::STARTED, transaction->state());
+ EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
+
+ transaction->ScheduleTask(
+ IndexedDBDatabase::PREEMPTIVE_TASK,
+ base::Bind(&IndexedDBTransactionTest::DummyOperation,
+ base::Unretained(this)));
+ EXPECT_EQ(0, transaction->pending_preemptive_events_);
+ transaction->AddPreemptiveEvent();
+ EXPECT_EQ(1, transaction->pending_preemptive_events_);
+
+ RunPostedTasks();
+
+ transaction->Abort();
+ EXPECT_EQ(IndexedDBTransaction::FINISHED, transaction->state());
+ EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
+ EXPECT_EQ(0, transaction->pending_preemptive_events_);
+ EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
+ EXPECT_TRUE(transaction->task_queue_.empty());
+ EXPECT_FALSE(transaction->HasPendingTasks());
+ EXPECT_EQ(transaction->diagnostics().tasks_completed,
+ transaction->diagnostics().tasks_scheduled);
+ EXPECT_FALSE(transaction->should_process_queue_);
+ EXPECT_TRUE(transaction->backing_store_transaction_begun_);
+ EXPECT_TRUE(transaction->used_);
+ EXPECT_FALSE(transaction->commit_pending_);
+
+ // This task will be ignored.
+ transaction->ScheduleTask(base::Bind(
+ &IndexedDBTransactionTest::DummyOperation, base::Unretained(this)));
+ EXPECT_EQ(IndexedDBTransaction::FINISHED, transaction->state());
+ EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
+ EXPECT_FALSE(transaction->HasPendingTasks());
+ EXPECT_EQ(transaction->diagnostics().tasks_completed,
+ transaction->diagnostics().tasks_scheduled);
}
-} // namespace
+static const indexed_db::TransactionMode kTestModes[] = {
+ indexed_db::TRANSACTION_READ_ONLY,
+ indexed_db::TRANSACTION_READ_WRITE,
+ indexed_db::TRANSACTION_VERSION_CHANGE
+};
+
+INSTANTIATE_TEST_CASE_P(IndexedDBTransactions,
+ IndexedDBTransactionTestMode,
+ ::testing::ValuesIn(kTestModes));
} // namespace content
diff --git a/chromium/content/browser/indexed_db/indexed_db_unittest.cc b/chromium/content/browser/indexed_db/indexed_db_unittest.cc
index 7c8f249f2cf..6fa627b1632 100644
--- a/chromium/content/browser/indexed_db/indexed_db_unittest.cc
+++ b/chromium/content/browser/indexed_db/indexed_db_unittest.cc
@@ -13,9 +13,9 @@
#include "content/browser/indexed_db/mock_indexed_db_database_callbacks.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/url_constants.h"
+#include "content/public/test/mock_special_storage_policy.h"
#include "content/public/test/test_browser_context.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "webkit/browser/quota/mock_special_storage_policy.h"
#include "webkit/browser/quota/quota_manager.h"
#include "webkit/browser/quota/special_storage_policy.h"
#include "webkit/common/database/database_identifier.h"
@@ -30,9 +30,8 @@ class IndexedDBTest : public testing::Test {
IndexedDBTest()
: kNormalOrigin("http://normal/"),
kSessionOnlyOrigin("http://session-only/"),
- message_loop_(base::MessageLoop::TYPE_IO),
task_runner_(new base::TestSimpleTaskRunner),
- special_storage_policy_(new quota::MockSpecialStoragePolicy),
+ special_storage_policy_(new MockSpecialStoragePolicy),
file_thread_(BrowserThread::FILE_USER_BLOCKING, &message_loop_),
io_thread_(BrowserThread::IO, &message_loop_) {
special_storage_policy_->AddSessionOnly(kSessionOnlyOrigin);
@@ -41,9 +40,9 @@ class IndexedDBTest : public testing::Test {
protected:
void FlushIndexedDBTaskRunner() { task_runner_->RunUntilIdle(); }
- base::MessageLoop message_loop_;
+ base::MessageLoopForIO message_loop_;
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
- scoped_refptr<quota::MockSpecialStoragePolicy> special_storage_policy_;
+ scoped_refptr<MockSpecialStoragePolicy> special_storage_policy_;
private:
BrowserThreadImpl file_thread_;
@@ -116,35 +115,43 @@ TEST_F(IndexedDBTest, SetForceKeepSessionState) {
EXPECT_TRUE(base::DirectoryExists(session_only_path));
}
-class MockConnection : public IndexedDBConnection {
+class ForceCloseDBCallbacks : public IndexedDBCallbacks {
public:
- explicit MockConnection(bool expect_force_close)
- : IndexedDBConnection(NULL, NULL),
- expect_force_close_(expect_force_close),
- force_close_called_(false) {}
-
- virtual ~MockConnection() {
- EXPECT_TRUE(force_close_called_ == expect_force_close_);
+ ForceCloseDBCallbacks(scoped_refptr<IndexedDBContextImpl> idb_context,
+ const GURL& origin_url)
+ : IndexedDBCallbacks(NULL, 0, 0),
+ idb_context_(idb_context),
+ origin_url_(origin_url) {}
+
+ virtual void OnSuccess() OVERRIDE {}
+ virtual void OnSuccess(const std::vector<base::string16>&) OVERRIDE {}
+ virtual void OnSuccess(scoped_ptr<IndexedDBConnection> connection,
+ const IndexedDBDatabaseMetadata& metadata) OVERRIDE {
+ connection_ = connection.Pass();
+ idb_context_->ConnectionOpened(origin_url_, connection_.get());
}
- virtual void ForceClose() OVERRIDE {
- ASSERT_TRUE(expect_force_close_);
- force_close_called_ = true;
- }
+ IndexedDBConnection* connection() { return connection_.get(); }
- virtual bool IsConnected() OVERRIDE {
- return !force_close_called_;
- }
+ protected:
+ virtual ~ForceCloseDBCallbacks() {}
private:
- bool expect_force_close_;
- bool force_close_called_;
+ scoped_refptr<IndexedDBContextImpl> idb_context_;
+ GURL origin_url_;
+ scoped_ptr<IndexedDBConnection> connection_;
+ DISALLOW_COPY_AND_ASSIGN(ForceCloseDBCallbacks);
};
TEST_F(IndexedDBTest, ForceCloseOpenDatabasesOnDelete) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ scoped_refptr<MockIndexedDBDatabaseCallbacks> open_db_callbacks(
+ new MockIndexedDBDatabaseCallbacks());
+ scoped_refptr<MockIndexedDBDatabaseCallbacks> closed_db_callbacks(
+ new MockIndexedDBDatabaseCallbacks());
+
base::FilePath test_path;
// Create the scope which will ensure we run the destructor of the context.
@@ -156,33 +163,39 @@ TEST_F(IndexedDBTest, ForceCloseOpenDatabasesOnDelete) {
scoped_refptr<IndexedDBContextImpl> idb_context = new IndexedDBContextImpl(
temp_dir.path(), special_storage_policy_, NULL, task_runner_);
- test_path = idb_context->GetFilePathForTesting(
- webkit_database::GetIdentifierFromOrigin(kTestOrigin));
- ASSERT_TRUE(base::CreateDirectory(test_path));
+ scoped_refptr<ForceCloseDBCallbacks> open_callbacks =
+ new ForceCloseDBCallbacks(idb_context, kTestOrigin);
- const bool kExpectForceClose = true;
+ scoped_refptr<ForceCloseDBCallbacks> closed_callbacks =
+ new ForceCloseDBCallbacks(idb_context, kTestOrigin);
- MockConnection connection1(kExpectForceClose);
- idb_context->TaskRunner()->PostTask(
- FROM_HERE,
- base::Bind(&IndexedDBContextImpl::ConnectionOpened,
- idb_context,
- kTestOrigin,
- &connection1));
+ IndexedDBFactory* factory = idb_context->GetIDBFactory();
- MockConnection connection2(!kExpectForceClose);
- idb_context->TaskRunner()->PostTask(
- FROM_HERE,
- base::Bind(&IndexedDBContextImpl::ConnectionOpened,
- idb_context,
- kTestOrigin,
- &connection2));
- idb_context->TaskRunner()->PostTask(
- FROM_HERE,
- base::Bind(&IndexedDBContextImpl::ConnectionClosed,
- idb_context,
- kTestOrigin,
- &connection2));
+ test_path = idb_context->GetFilePathForTesting(
+ webkit_database::GetIdentifierFromOrigin(kTestOrigin));
+
+ IndexedDBPendingConnection open_connection(open_callbacks,
+ open_db_callbacks,
+ 0 /* child_process_id */,
+ 0 /* host_transaction_id */,
+ 0 /* version */);
+ factory->Open(base::ASCIIToUTF16("opendb"),
+ open_connection,
+ NULL /* request_context */,
+ kTestOrigin,
+ idb_context->data_path());
+ IndexedDBPendingConnection closed_connection(closed_callbacks,
+ closed_db_callbacks,
+ 0 /* child_process_id */,
+ 0 /* host_transaction_id */,
+ 0 /* version */);
+ factory->Open(base::ASCIIToUTF16("closeddb"),
+ closed_connection,
+ NULL /* request_context */,
+ kTestOrigin,
+ idb_context->data_path());
+
+ closed_callbacks->connection()->Close();
idb_context->TaskRunner()->PostTask(
FROM_HERE,
@@ -195,6 +208,8 @@ TEST_F(IndexedDBTest, ForceCloseOpenDatabasesOnDelete) {
// Make sure we wait until the destructor has run.
message_loop_.RunUntilIdle();
+ EXPECT_TRUE(open_db_callbacks->forced_close_called());
+ EXPECT_FALSE(closed_db_callbacks->forced_close_called());
EXPECT_FALSE(base::DirectoryExists(test_path));
}
@@ -238,11 +253,15 @@ TEST_F(IndexedDBTest, ForceCloseOpenDatabasesOnCommitFailure) {
scoped_refptr<MockIndexedDBDatabaseCallbacks> db_callbacks(
new MockIndexedDBDatabaseCallbacks());
const int64 transaction_id = 1;
- factory->Open(ASCIIToUTF16("db"),
- IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION,
- transaction_id,
- callbacks,
- db_callbacks,
+ IndexedDBPendingConnection connection(
+ callbacks,
+ db_callbacks,
+ 0 /* child_process_id */,
+ transaction_id,
+ IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
+ factory->Open(base::ASCIIToUTF16("db"),
+ connection,
+ NULL /* request_context */,
kTestOrigin,
temp_dir.path());
diff --git a/chromium/content/browser/indexed_db/indexed_db_value.cc b/chromium/content/browser/indexed_db/indexed_db_value.cc
new file mode 100644
index 00000000000..28435211df5
--- /dev/null
+++ b/chromium/content/browser/indexed_db/indexed_db_value.cc
@@ -0,0 +1,20 @@
+// 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/indexed_db/indexed_db_value.h"
+
+#include "base/logging.h"
+
+namespace content {
+
+IndexedDBValue::IndexedDBValue() {}
+IndexedDBValue::IndexedDBValue(
+ const std::string& input_bits,
+ const std::vector<IndexedDBBlobInfo>& input_blob_info)
+ : bits(input_bits), blob_info(input_blob_info) {
+ DCHECK(!input_blob_info.size() || input_bits.size());
+}
+IndexedDBValue::~IndexedDBValue() {}
+
+} // namespace content
diff --git a/chromium/content/browser/indexed_db/indexed_db_value.h b/chromium/content/browser/indexed_db/indexed_db_value.h
new file mode 100644
index 00000000000..d313038dd69
--- /dev/null
+++ b/chromium/content/browser/indexed_db/indexed_db_value.h
@@ -0,0 +1,44 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_VALUE_H_
+#define CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_VALUE_H_
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "content/browser/indexed_db/indexed_db_blob_info.h"
+#include "content/common/content_export.h"
+
+namespace content {
+
+struct CONTENT_EXPORT IndexedDBValue {
+ IndexedDBValue();
+ IndexedDBValue(const std::string& input_bits,
+ const std::vector<IndexedDBBlobInfo>& input_blob_info);
+ ~IndexedDBValue();
+
+ void swap(IndexedDBValue& value) {
+ bits.swap(value.bits);
+ blob_info.swap(value.blob_info);
+ }
+
+ bool empty() const { return bits.empty(); }
+ void clear() {
+ bits.clear();
+ blob_info.clear();
+ }
+
+ size_t SizeEstimate() const {
+ return bits.size() + blob_info.size() * sizeof(IndexedDBBlobInfo);
+ }
+
+ std::string bits;
+ std::vector<IndexedDBBlobInfo> blob_info;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_VALUE_H_
diff --git a/chromium/content/browser/indexed_db/leveldb/leveldb_comparator.h b/chromium/content/browser/indexed_db/leveldb/leveldb_comparator.h
index 2e8e3299916..aac88089e97 100644
--- a/chromium/content/browser/indexed_db/leveldb/leveldb_comparator.h
+++ b/chromium/content/browser/indexed_db/leveldb/leveldb_comparator.h
@@ -6,10 +6,11 @@
#define CONTENT_BROWSER_INDEXED_DB_LEVELDB_LEVELDB_COMPARATOR_H_
#include "base/strings/string_piece.h"
+#include "content/common/content_export.h"
namespace content {
-class LevelDBComparator {
+class CONTENT_EXPORT LevelDBComparator {
public:
virtual ~LevelDBComparator() {}
diff --git a/chromium/content/browser/indexed_db/leveldb/leveldb_database.cc b/chromium/content/browser/indexed_db/leveldb/leveldb_database.cc
index fd4d30cae66..16f4fc79c61 100644
--- a/chromium/content/browser/indexed_db/leveldb/leveldb_database.cc
+++ b/chromium/content/browser/indexed_db/leveldb/leveldb_database.cc
@@ -7,6 +7,7 @@
#include <cerrno>
#include "base/basictypes.h"
+#include "base/files/file.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/metrics/histogram.h"
@@ -21,7 +22,6 @@
#include "third_party/leveldatabase/env_chromium.h"
#include "third_party/leveldatabase/env_idb.h"
#include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
-#include "third_party/leveldatabase/src/include/leveldb/comparator.h"
#include "third_party/leveldatabase/src/include/leveldb/db.h"
#include "third_party/leveldatabase/src/include/leveldb/env.h"
#include "third_party/leveldatabase/src/include/leveldb/slice.h"
@@ -54,27 +54,26 @@ static StringPiece MakeStringPiece(const leveldb::Slice& s) {
return StringPiece(s.data(), s.size());
}
-class ComparatorAdapter : public leveldb::Comparator {
- public:
- explicit ComparatorAdapter(const LevelDBComparator* comparator)
- : comparator_(comparator) {}
+LevelDBDatabase::ComparatorAdapter::ComparatorAdapter(
+ const LevelDBComparator* comparator)
+ : comparator_(comparator) {}
- virtual int Compare(const leveldb::Slice& a, const leveldb::Slice& b) const
- OVERRIDE {
- return comparator_->Compare(MakeStringPiece(a), MakeStringPiece(b));
- }
+int LevelDBDatabase::ComparatorAdapter::Compare(const leveldb::Slice& a,
+ const leveldb::Slice& b) const {
+ return comparator_->Compare(MakeStringPiece(a), MakeStringPiece(b));
+}
- virtual const char* Name() const OVERRIDE { return comparator_->Name(); }
+const char* LevelDBDatabase::ComparatorAdapter::Name() const {
+ return comparator_->Name();
+}
- // TODO(jsbell): Support the methods below in the future.
- virtual void FindShortestSeparator(std::string* start,
- const leveldb::Slice& limit) const
- OVERRIDE {}
- virtual void FindShortSuccessor(std::string* key) const OVERRIDE {}
+// TODO(jsbell): Support the methods below in the future.
+void LevelDBDatabase::ComparatorAdapter::FindShortestSeparator(
+ std::string* start,
+ const leveldb::Slice& limit) const {}
- private:
- const LevelDBComparator* comparator_;
-};
+void LevelDBDatabase::ComparatorAdapter::FindShortSuccessor(
+ std::string* key) const {}
LevelDBSnapshot::LevelDBSnapshot(LevelDBDatabase* db)
: db_(db->db_.get()), snapshot_(db_->GetSnapshot()) {}
@@ -109,13 +108,11 @@ static leveldb::Status OpenDB(leveldb::Comparator* comparator,
return leveldb::DB::Open(options, path.AsUTF8Unsafe(), db);
}
-bool LevelDBDatabase::Destroy(const base::FilePath& file_name) {
+leveldb::Status LevelDBDatabase::Destroy(const base::FilePath& file_name) {
leveldb::Options options;
options.env = leveldb::IDBEnv();
// ChromiumEnv assumes UTF8, converts back to FilePath before using.
- const leveldb::Status s =
- leveldb::DestroyDB(file_name.AsUTF8Unsafe(), options);
- return s.ok();
+ return leveldb::DestroyDB(file_name.AsUTF8Unsafe(), options);
}
namespace {
@@ -127,8 +124,10 @@ class LockImpl : public LevelDBLock {
private:
leveldb::Env* env_;
leveldb::FileLock* lock_;
+
+ DISALLOW_COPY_AND_ASSIGN(LockImpl);
};
-}
+} // namespace
scoped_ptr<LevelDBLock> LevelDBDatabase::LockForTesting(
const base::FilePath& file_name) {
@@ -191,14 +190,14 @@ static void ParseAndHistogramIOErrorDetails(const std::string& histogram_name,
std::string error_histogram_name(histogram_name);
if (result == leveldb_env::METHOD_AND_PFE) {
- DCHECK(error < 0);
+ DCHECK_LT(error, 0);
error_histogram_name.append(std::string(".PFE.") +
leveldb_env::MethodIDToString(method));
base::LinearHistogram::FactoryGet(
error_histogram_name,
1,
- -base::PLATFORM_FILE_ERROR_MAX,
- -base::PLATFORM_FILE_ERROR_MAX + 1,
+ -base::File::FILE_ERROR_MAX,
+ -base::File::FILE_ERROR_MAX + 1,
base::HistogramBase::kUmaTargetedHistogramFlag)->Add(-error);
} else if (result == leveldb_env::METHOD_AND_ERRNO) {
error_histogram_name.append(std::string(".Errno.") +
@@ -216,7 +215,7 @@ static void ParseAndHistogramCorruptionDetails(
const std::string& histogram_name,
const leveldb::Status& status) {
int error = leveldb_env::GetCorruptionCode(status);
- DCHECK(error >= 0);
+ DCHECK_GE(error, 0);
std::string corruption_histogram_name(histogram_name);
corruption_histogram_name.append(".Corruption");
const int kNumPatterns = leveldb_env::GetNumCorruptionCodes();
@@ -318,35 +317,32 @@ scoped_ptr<LevelDBDatabase> LevelDBDatabase::OpenInMemory(
return result.Pass();
}
-bool LevelDBDatabase::Put(const StringPiece& key, std::string* value) {
+leveldb::Status LevelDBDatabase::Put(const StringPiece& key,
+ std::string* value) {
leveldb::WriteOptions write_options;
write_options.sync = kSyncWrites;
const leveldb::Status s =
db_->Put(write_options, MakeSlice(key), MakeSlice(*value));
- if (s.ok())
- return true;
- LOG(ERROR) << "LevelDB put failed: " << s.ToString();
- return false;
+ if (!s.ok())
+ LOG(ERROR) << "LevelDB put failed: " << s.ToString();
+ return s;
}
-bool LevelDBDatabase::Remove(const StringPiece& key) {
+leveldb::Status LevelDBDatabase::Remove(const StringPiece& key) {
leveldb::WriteOptions write_options;
write_options.sync = kSyncWrites;
const leveldb::Status s = db_->Delete(write_options, MakeSlice(key));
- if (s.ok())
- return true;
- if (s.IsNotFound())
- return false;
- LOG(ERROR) << "LevelDB remove failed: " << s.ToString();
- return false;
+ if (!s.IsNotFound())
+ LOG(ERROR) << "LevelDB remove failed: " << s.ToString();
+ return s;
}
-bool LevelDBDatabase::Get(const StringPiece& key,
- std::string* value,
- bool* found,
- const LevelDBSnapshot* snapshot) {
+leveldb::Status LevelDBDatabase::Get(const StringPiece& key,
+ std::string* value,
+ bool* found,
+ const LevelDBSnapshot* snapshot) {
*found = false;
leveldb::ReadOptions read_options;
read_options.verify_checksums = true; // TODO(jsbell): Disable this if the
@@ -356,26 +352,26 @@ bool LevelDBDatabase::Get(const StringPiece& key,
const leveldb::Status s = db_->Get(read_options, MakeSlice(key), value);
if (s.ok()) {
*found = true;
- return true;
+ return s;
}
if (s.IsNotFound())
- return true;
+ return leveldb::Status::OK();
HistogramLevelDBError("WebCore.IndexedDB.LevelDBReadErrors", s);
LOG(ERROR) << "LevelDB get failed: " << s.ToString();
- return false;
+ return s;
}
-bool LevelDBDatabase::Write(const LevelDBWriteBatch& write_batch) {
+leveldb::Status LevelDBDatabase::Write(const LevelDBWriteBatch& write_batch) {
leveldb::WriteOptions write_options;
write_options.sync = kSyncWrites;
const leveldb::Status s =
db_->Write(write_options, write_batch.write_batch_.get());
- if (s.ok())
- return true;
- HistogramLevelDBError("WebCore.IndexedDB.LevelDBWriteErrors", s);
- LOG(ERROR) << "LevelDB write failed: " << s.ToString();
- return false;
+ if (!s.ok()) {
+ HistogramLevelDBError("WebCore.IndexedDB.LevelDBWriteErrors", s);
+ LOG(ERROR) << "LevelDB write failed: " << s.ToString();
+ }
+ return s;
}
namespace {
@@ -384,10 +380,10 @@ class IteratorImpl : public LevelDBIterator {
virtual ~IteratorImpl() {}
virtual bool IsValid() const OVERRIDE;
- virtual void SeekToLast() OVERRIDE;
- virtual void Seek(const StringPiece& target) OVERRIDE;
- virtual void Next() OVERRIDE;
- virtual void Prev() OVERRIDE;
+ virtual leveldb::Status SeekToLast() OVERRIDE;
+ virtual leveldb::Status Seek(const StringPiece& target) OVERRIDE;
+ virtual leveldb::Status Next() OVERRIDE;
+ virtual leveldb::Status Prev() OVERRIDE;
virtual StringPiece Key() const OVERRIDE;
virtual StringPiece Value() const OVERRIDE;
@@ -397,40 +393,46 @@ class IteratorImpl : public LevelDBIterator {
void CheckStatus();
scoped_ptr<leveldb::Iterator> iterator_;
+
+ DISALLOW_COPY_AND_ASSIGN(IteratorImpl);
};
-}
+} // namespace
IteratorImpl::IteratorImpl(scoped_ptr<leveldb::Iterator> it)
: iterator_(it.Pass()) {}
void IteratorImpl::CheckStatus() {
- const leveldb::Status s = iterator_->status();
+ const leveldb::Status& s = iterator_->status();
if (!s.ok())
LOG(ERROR) << "LevelDB iterator error: " << s.ToString();
}
bool IteratorImpl::IsValid() const { return iterator_->Valid(); }
-void IteratorImpl::SeekToLast() {
+leveldb::Status IteratorImpl::SeekToLast() {
iterator_->SeekToLast();
CheckStatus();
+ return iterator_->status();
}
-void IteratorImpl::Seek(const StringPiece& target) {
+leveldb::Status IteratorImpl::Seek(const StringPiece& target) {
iterator_->Seek(MakeSlice(target));
CheckStatus();
+ return iterator_->status();
}
-void IteratorImpl::Next() {
+leveldb::Status IteratorImpl::Next() {
DCHECK(IsValid());
iterator_->Next();
CheckStatus();
+ return iterator_->status();
}
-void IteratorImpl::Prev() {
+leveldb::Status IteratorImpl::Prev() {
DCHECK(IsValid());
iterator_->Prev();
CheckStatus();
+ return iterator_->status();
}
StringPiece IteratorImpl::Key() const {
@@ -458,4 +460,13 @@ const LevelDBComparator* LevelDBDatabase::Comparator() const {
return comparator_;
}
+void LevelDBDatabase::Compact(const base::StringPiece& start,
+ const base::StringPiece& stop) {
+ const leveldb::Slice start_slice = MakeSlice(start);
+ const leveldb::Slice stop_slice = MakeSlice(stop);
+ db_->CompactRange(&start_slice, &stop_slice);
+}
+
+void LevelDBDatabase::CompactAll() { db_->CompactRange(NULL, NULL); }
+
} // namespace content
diff --git a/chromium/content/browser/indexed_db/leveldb/leveldb_database.h b/chromium/content/browser/indexed_db/leveldb/leveldb_database.h
index 6f9c919014a..f6bf7bed5bf 100644
--- a/chromium/content/browser/indexed_db/leveldb/leveldb_database.h
+++ b/chromium/content/browser/indexed_db/leveldb/leveldb_database.h
@@ -12,6 +12,7 @@
#include "base/strings/string16.h"
#include "base/strings/string_piece.h"
#include "content/common/content_export.h"
+#include "third_party/leveldatabase/src/include/leveldb/comparator.h"
#include "third_party/leveldatabase/src/include/leveldb/status.h"
namespace leveldb {
@@ -38,35 +39,61 @@ class LevelDBSnapshot {
leveldb::DB* db_;
const leveldb::Snapshot* snapshot_;
+
+ DISALLOW_COPY_AND_ASSIGN(LevelDBSnapshot);
};
class CONTENT_EXPORT LevelDBLock {
-public:
+ public:
+ LevelDBLock() {}
virtual ~LevelDBLock() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LevelDBLock);
};
class CONTENT_EXPORT LevelDBDatabase {
public:
+ class ComparatorAdapter : public leveldb::Comparator {
+ public:
+ explicit ComparatorAdapter(const LevelDBComparator* comparator);
+
+ virtual int Compare(const leveldb::Slice& a,
+ const leveldb::Slice& b) const OVERRIDE;
+
+ virtual const char* Name() const OVERRIDE;
+
+ virtual void FindShortestSeparator(std::string* start,
+ const leveldb::Slice& limit) const
+ OVERRIDE;
+ virtual void FindShortSuccessor(std::string* key) const OVERRIDE;
+
+ private:
+ const LevelDBComparator* comparator_;
+ };
+
static leveldb::Status Open(const base::FilePath& file_name,
const LevelDBComparator* comparator,
scoped_ptr<LevelDBDatabase>* db,
bool* is_disk_full = 0);
static scoped_ptr<LevelDBDatabase> OpenInMemory(
const LevelDBComparator* comparator);
- static bool Destroy(const base::FilePath& file_name);
+ static leveldb::Status Destroy(const base::FilePath& file_name);
static scoped_ptr<LevelDBLock> LockForTesting(
const base::FilePath& file_name);
virtual ~LevelDBDatabase();
- bool Put(const base::StringPiece& key, std::string* value);
- bool Remove(const base::StringPiece& key);
- virtual bool Get(const base::StringPiece& key,
- std::string* value,
- bool* found,
- const LevelDBSnapshot* = 0);
- bool Write(const LevelDBWriteBatch& write_batch);
+ leveldb::Status Put(const base::StringPiece& key, std::string* value);
+ leveldb::Status Remove(const base::StringPiece& key);
+ virtual leveldb::Status Get(const base::StringPiece& key,
+ std::string* value,
+ bool* found,
+ const LevelDBSnapshot* = 0);
+ leveldb::Status Write(const LevelDBWriteBatch& write_batch);
scoped_ptr<LevelDBIterator> CreateIterator(const LevelDBSnapshot* = 0);
const LevelDBComparator* Comparator() const;
+ void Compact(const base::StringPiece& start, const base::StringPiece& stop);
+ void CompactAll();
protected:
LevelDBDatabase();
diff --git a/chromium/content/browser/indexed_db/leveldb/leveldb_iterator.h b/chromium/content/browser/indexed_db/leveldb/leveldb_iterator.h
index 96cd6b91281..1069a8b0a3c 100644
--- a/chromium/content/browser/indexed_db/leveldb/leveldb_iterator.h
+++ b/chromium/content/browser/indexed_db/leveldb/leveldb_iterator.h
@@ -6,6 +6,7 @@
#define CONTENT_BROWSER_INDEXED_DB_LEVELDB_LEVELDB_ITERATOR_H_
#include "base/strings/string_piece.h"
+#include "third_party/leveldatabase/src/include/leveldb/status.h"
namespace content {
@@ -13,10 +14,10 @@ class LevelDBIterator {
public:
virtual ~LevelDBIterator() {}
virtual bool IsValid() const = 0;
- virtual void SeekToLast() = 0;
- virtual void Seek(const base::StringPiece& target) = 0;
- virtual void Next() = 0;
- virtual void Prev() = 0;
+ virtual leveldb::Status SeekToLast() = 0;
+ virtual leveldb::Status Seek(const base::StringPiece& target) = 0;
+ virtual leveldb::Status Next() = 0;
+ virtual leveldb::Status Prev() = 0;
virtual base::StringPiece Key() const = 0;
virtual base::StringPiece Value() const = 0;
};
diff --git a/chromium/content/browser/indexed_db/leveldb/leveldb_transaction.cc b/chromium/content/browser/indexed_db/leveldb/leveldb_transaction.cc
index 0c3619465bb..e20d404c3c5 100644
--- a/chromium/content/browser/indexed_db/leveldb/leveldb_transaction.cc
+++ b/chromium/content/browser/indexed_db/leveldb/leveldb_transaction.cc
@@ -62,9 +62,9 @@ void LevelDBTransaction::Remove(const StringPiece& key) {
Set(key, &empty, true);
}
-bool LevelDBTransaction::Get(const StringPiece& key,
- std::string* value,
- bool* found) {
+leveldb::Status LevelDBTransaction::Get(const StringPiece& key,
+ std::string* value,
+ bool* found) {
*found = false;
DCHECK(!finished_);
std::string string_key(key.begin(), key.end() - key.begin());
@@ -72,27 +72,25 @@ bool LevelDBTransaction::Get(const StringPiece& key,
if (it != data_.end()) {
if (it->second->deleted)
- return true;
+ return leveldb::Status::OK();
*value = it->second->value;
*found = true;
- return true;
+ return leveldb::Status::OK();
}
- bool ok = db_->Get(key, value, found, &snapshot_);
- if (!ok) {
+ leveldb::Status s = db_->Get(key, value, found, &snapshot_);
+ if (!s.ok())
DCHECK(!*found);
- return false;
- }
- return true;
+ return s;
}
-bool LevelDBTransaction::Commit() {
+leveldb::Status LevelDBTransaction::Commit() {
DCHECK(!finished_);
if (data_.empty()) {
finished_ = true;
- return true;
+ return leveldb::Status::OK();
}
scoped_ptr<LevelDBWriteBatch> write_batch = LevelDBWriteBatch::Create();
@@ -105,12 +103,12 @@ bool LevelDBTransaction::Commit() {
write_batch->Remove(iterator->first);
}
- if (!db_->Write(*write_batch))
- return false;
-
- Clear();
- finished_ = true;
- return true;
+ leveldb::Status s = db_->Write(*write_batch);
+ if (s.ok()) {
+ Clear();
+ finished_ = true;
+ }
+ return s;
}
void LevelDBTransaction::Rollback() {
@@ -132,27 +130,32 @@ bool LevelDBTransaction::DataIterator::IsValid() const {
return iterator_ != data_->end();
}
-void LevelDBTransaction::DataIterator::SeekToLast() {
+leveldb::Status LevelDBTransaction::DataIterator::SeekToLast() {
iterator_ = data_->end();
if (iterator_ != data_->begin())
--iterator_;
+ return leveldb::Status::OK();
}
-void LevelDBTransaction::DataIterator::Seek(const StringPiece& target) {
+leveldb::Status LevelDBTransaction::DataIterator::Seek(
+ const StringPiece& target) {
iterator_ = data_->lower_bound(target);
+ return leveldb::Status::OK();
}
-void LevelDBTransaction::DataIterator::Next() {
+leveldb::Status LevelDBTransaction::DataIterator::Next() {
DCHECK(IsValid());
++iterator_;
+ return leveldb::Status::OK();
}
-void LevelDBTransaction::DataIterator::Prev() {
+leveldb::Status LevelDBTransaction::DataIterator::Prev() {
DCHECK(IsValid());
if (iterator_ != data_->begin())
--iterator_;
else
iterator_ = data_->end();
+ return leveldb::Status::OK();
}
StringPiece LevelDBTransaction::DataIterator::Key() const {
@@ -203,29 +206,39 @@ bool LevelDBTransaction::TransactionIterator::IsValid() const {
return !!current_;
}
-void LevelDBTransaction::TransactionIterator::SeekToLast() {
- data_iterator_->SeekToLast();
- db_iterator_->SeekToLast();
+leveldb::Status LevelDBTransaction::TransactionIterator::SeekToLast() {
+ leveldb::Status s = data_iterator_->SeekToLast();
+ DCHECK(s.ok());
+ s = db_iterator_->SeekToLast();
+ if (!s.ok())
+ return s;
direction_ = REVERSE;
HandleConflictsAndDeletes();
SetCurrentIteratorToLargestKey();
+ return s;
}
-void LevelDBTransaction::TransactionIterator::Seek(const StringPiece& target) {
- data_iterator_->Seek(target);
- db_iterator_->Seek(target);
+leveldb::Status LevelDBTransaction::TransactionIterator::Seek(
+ const StringPiece& target) {
+ leveldb::Status s = data_iterator_->Seek(target);
+ DCHECK(s.ok());
+ s = db_iterator_->Seek(target);
+ if (!s.ok())
+ return s;
direction_ = FORWARD;
HandleConflictsAndDeletes();
SetCurrentIteratorToSmallestKey();
+ return s;
}
-void LevelDBTransaction::TransactionIterator::Next() {
+leveldb::Status LevelDBTransaction::TransactionIterator::Next() {
DCHECK(IsValid());
if (data_changed_)
RefreshDataIterator();
+ leveldb::Status s;
if (direction_ != FORWARD) {
// Ensure the non-current iterator is positioned after Key().
@@ -238,7 +251,9 @@ void LevelDBTransaction::TransactionIterator::Next() {
!comparator_->Compare(non_current->Key(), Key())) {
// Take an extra step so the non-current key is
// strictly greater than Key().
- non_current->Next();
+ s = non_current->Next();
+ if (!s.ok())
+ return s;
}
DCHECK(!non_current->IsValid() ||
comparator_->Compare(non_current->Key(), Key()) > 0);
@@ -246,13 +261,17 @@ void LevelDBTransaction::TransactionIterator::Next() {
direction_ = FORWARD;
}
- current_->Next();
+ s = current_->Next();
+ if (!s.ok())
+ return s;
HandleConflictsAndDeletes();
SetCurrentIteratorToSmallestKey();
+ return leveldb::Status::OK();
}
-void LevelDBTransaction::TransactionIterator::Prev() {
+leveldb::Status LevelDBTransaction::TransactionIterator::Prev() {
DCHECK(IsValid());
+ leveldb::Status s;
if (data_changed_)
RefreshDataIterator();
@@ -263,7 +282,9 @@ void LevelDBTransaction::TransactionIterator::Prev() {
? data_iterator_.get()
: db_iterator_.get();
- non_current->Seek(Key());
+ s = non_current->Seek(Key());
+ if (!s.ok())
+ return s;
if (non_current->IsValid()) {
// Iterator is at first entry >= Key().
// Step back once to entry < key.
@@ -280,9 +301,12 @@ void LevelDBTransaction::TransactionIterator::Prev() {
direction_ = REVERSE;
}
- current_->Prev();
+ s = current_->Prev();
+ if (!s.ok())
+ return s;
HandleConflictsAndDeletes();
SetCurrentIteratorToLargestKey();
+ return leveldb::Status::OK();
}
StringPiece LevelDBTransaction::TransactionIterator::Key() const {
@@ -423,32 +447,49 @@ void LevelDBTransaction::NotifyIterators() {
}
}
-scoped_ptr<LevelDBWriteOnlyTransaction> LevelDBWriteOnlyTransaction::Create(
+scoped_ptr<LevelDBDirectTransaction> LevelDBDirectTransaction::Create(
LevelDBDatabase* db) {
- return make_scoped_ptr(new LevelDBWriteOnlyTransaction(db));
+ return make_scoped_ptr(new LevelDBDirectTransaction(db));
}
-LevelDBWriteOnlyTransaction::LevelDBWriteOnlyTransaction(LevelDBDatabase* db)
+LevelDBDirectTransaction::LevelDBDirectTransaction(LevelDBDatabase* db)
: db_(db), write_batch_(LevelDBWriteBatch::Create()), finished_(false) {}
-LevelDBWriteOnlyTransaction::~LevelDBWriteOnlyTransaction() {
+LevelDBDirectTransaction::~LevelDBDirectTransaction() {
write_batch_->Clear();
}
-void LevelDBWriteOnlyTransaction::Remove(const StringPiece& key) {
+void LevelDBDirectTransaction::Put(const StringPiece& key,
+ const std::string* value) {
DCHECK(!finished_);
- write_batch_->Remove(key);
+ write_batch_->Put(key, *value);
}
-bool LevelDBWriteOnlyTransaction::Commit() {
+leveldb::Status LevelDBDirectTransaction::Get(const StringPiece& key,
+ std::string* value,
+ bool* found) {
+ *found = false;
DCHECK(!finished_);
- if (!db_->Write(*write_batch_))
- return false;
+ leveldb::Status s = db_->Get(key, value, found);
+ DCHECK(s.ok() || !*found);
+ return s;
+}
- finished_ = true;
- write_batch_->Clear();
- return true;
+void LevelDBDirectTransaction::Remove(const StringPiece& key) {
+ DCHECK(!finished_);
+ write_batch_->Remove(key);
+}
+
+leveldb::Status LevelDBDirectTransaction::Commit() {
+ DCHECK(!finished_);
+
+ leveldb::Status s = db_->Write(*write_batch_);
+ if (s.ok()) {
+ finished_ = true;
+ write_batch_->Clear();
+ }
+ return s;
}
} // namespace content
diff --git a/chromium/content/browser/indexed_db/leveldb/leveldb_transaction.h b/chromium/content/browser/indexed_db/leveldb/leveldb_transaction.h
index b7b5d64df1e..f6b5a95e121 100644
--- a/chromium/content/browser/indexed_db/leveldb/leveldb_transaction.h
+++ b/chromium/content/browser/indexed_db/leveldb/leveldb_transaction.h
@@ -9,6 +9,7 @@
#include <set>
#include <string>
+#include "base/gtest_prod_util.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/string_piece.h"
@@ -23,19 +24,27 @@ class LevelDBWriteBatch;
class CONTENT_EXPORT LevelDBTransaction
: public base::RefCounted<LevelDBTransaction> {
public:
- explicit LevelDBTransaction(LevelDBDatabase* db);
void Put(const base::StringPiece& key, std::string* value);
void Remove(const base::StringPiece& key);
- bool Get(const base::StringPiece& key, std::string* value, bool* found);
- bool Commit();
+ virtual leveldb::Status Get(const base::StringPiece& key,
+ std::string* value,
+ bool* found);
+ virtual leveldb::Status Commit();
void Rollback();
scoped_ptr<LevelDBIterator> CreateIterator();
- private:
+ protected:
virtual ~LevelDBTransaction();
+ explicit LevelDBTransaction(LevelDBDatabase* db);
+ friend class IndexedDBClassFactory;
+
+ private:
friend class base::RefCounted<LevelDBTransaction>;
+ FRIEND_TEST_ALL_PREFIXES(LevelDBDatabaseTest, Transaction);
+ FRIEND_TEST_ALL_PREFIXES(LevelDBDatabaseTest, TransactionCommitTest);
+ FRIEND_TEST_ALL_PREFIXES(LevelDBDatabaseTest, TransactionIterator);
struct Record {
Record();
@@ -66,10 +75,10 @@ class CONTENT_EXPORT LevelDBTransaction
virtual ~DataIterator();
virtual bool IsValid() const OVERRIDE;
- virtual void SeekToLast() OVERRIDE;
- virtual void Seek(const base::StringPiece& slice) OVERRIDE;
- virtual void Next() OVERRIDE;
- virtual void Prev() OVERRIDE;
+ virtual leveldb::Status SeekToLast() OVERRIDE;
+ virtual leveldb::Status Seek(const base::StringPiece& slice) OVERRIDE;
+ virtual leveldb::Status Next() OVERRIDE;
+ virtual leveldb::Status Prev() OVERRIDE;
virtual base::StringPiece Key() const OVERRIDE;
virtual base::StringPiece Value() const OVERRIDE;
bool IsDeleted() const;
@@ -78,6 +87,8 @@ class CONTENT_EXPORT LevelDBTransaction
explicit DataIterator(LevelDBTransaction* transaction);
DataType* data_;
DataType::iterator iterator_;
+
+ DISALLOW_COPY_AND_ASSIGN(DataIterator);
};
class TransactionIterator : public LevelDBIterator {
@@ -87,10 +98,10 @@ class CONTENT_EXPORT LevelDBTransaction
scoped_refptr<LevelDBTransaction> transaction);
virtual bool IsValid() const OVERRIDE;
- virtual void SeekToLast() OVERRIDE;
- virtual void Seek(const base::StringPiece& target) OVERRIDE;
- virtual void Next() OVERRIDE;
- virtual void Prev() OVERRIDE;
+ virtual leveldb::Status SeekToLast() OVERRIDE;
+ virtual leveldb::Status Seek(const base::StringPiece& target) OVERRIDE;
+ virtual leveldb::Status Next() OVERRIDE;
+ virtual leveldb::Status Prev() OVERRIDE;
virtual base::StringPiece Key() const OVERRIDE;
virtual base::StringPiece Value() const OVERRIDE;
void DataChanged();
@@ -116,6 +127,8 @@ class CONTENT_EXPORT LevelDBTransaction
};
Direction direction_;
mutable bool data_changed_;
+
+ DISALLOW_COPY_AND_ASSIGN(TransactionIterator);
};
void Set(const base::StringPiece& key, std::string* value, bool deleted);
@@ -131,22 +144,32 @@ class CONTENT_EXPORT LevelDBTransaction
DataType data_;
bool finished_;
std::set<TransactionIterator*> iterators_;
+
+ DISALLOW_COPY_AND_ASSIGN(LevelDBTransaction);
};
-class LevelDBWriteOnlyTransaction {
+// Reads go straight to the database, ignoring any writes cached in
+// write_batch_, and writes are write-through, without consolidation.
+class LevelDBDirectTransaction {
public:
- static scoped_ptr<LevelDBWriteOnlyTransaction> Create(LevelDBDatabase* db);
+ static scoped_ptr<LevelDBDirectTransaction> Create(LevelDBDatabase* db);
- ~LevelDBWriteOnlyTransaction();
+ ~LevelDBDirectTransaction();
+ void Put(const base::StringPiece& key, const std::string* value);
+ leveldb::Status Get(const base::StringPiece& key,
+ std::string* value,
+ bool* found);
void Remove(const base::StringPiece& key);
- bool Commit();
+ leveldb::Status Commit();
private:
- explicit LevelDBWriteOnlyTransaction(LevelDBDatabase* db);
+ explicit LevelDBDirectTransaction(LevelDBDatabase* db);
LevelDBDatabase* db_;
scoped_ptr<LevelDBWriteBatch> write_batch_;
bool finished_;
+
+ DISALLOW_COPY_AND_ASSIGN(LevelDBDirectTransaction);
};
} // namespace content
diff --git a/chromium/content/browser/indexed_db/leveldb/leveldb_unittest.cc b/chromium/content/browser/indexed_db/leveldb/leveldb_unittest.cc
index 3edcae7fad6..83fdd59b878 100644
--- a/chromium/content/browser/indexed_db/leveldb/leveldb_unittest.cc
+++ b/chromium/content/browser/indexed_db/leveldb/leveldb_unittest.cc
@@ -6,9 +6,9 @@
#include <cstring>
#include <string>
+#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
-#include "base/platform_file.h"
#include "base/strings/string16.h"
#include "base/strings/string_piece.h"
#include "content/browser/indexed_db/leveldb/leveldb_comparator.h"
@@ -33,6 +33,8 @@ class SimpleComparator : public LevelDBComparator {
virtual const char* Name() const OVERRIDE { return "temp_comparator"; }
};
+} // namespace
+
TEST(LevelDBDatabaseTest, CorruptionTest) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
@@ -47,43 +49,38 @@ TEST(LevelDBDatabaseTest, CorruptionTest) {
LevelDBDatabase::Open(temp_directory.path(), &comparator, &leveldb);
EXPECT_TRUE(leveldb);
put_value = value;
- bool success = leveldb->Put(key, &put_value);
- EXPECT_TRUE(success);
+ leveldb::Status status = leveldb->Put(key, &put_value);
+ EXPECT_TRUE(status.ok());
leveldb.Pass();
EXPECT_FALSE(leveldb);
LevelDBDatabase::Open(temp_directory.path(), &comparator, &leveldb);
EXPECT_TRUE(leveldb);
bool found = false;
- success = leveldb->Get(key, &got_value, &found);
- EXPECT_TRUE(success);
+ status = leveldb->Get(key, &got_value, &found);
+ EXPECT_TRUE(status.ok());
EXPECT_TRUE(found);
EXPECT_EQ(value, got_value);
leveldb.Pass();
EXPECT_FALSE(leveldb);
base::FilePath file_path = temp_directory.path().AppendASCII("CURRENT");
- base::PlatformFile handle = base::CreatePlatformFile(
- file_path,
- base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE,
- NULL,
- NULL);
- base::TruncatePlatformFile(handle, 0);
- base::ClosePlatformFile(handle);
-
- leveldb::Status status =
- LevelDBDatabase::Open(temp_directory.path(), &comparator, &leveldb);
+ base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_WRITE);
+ file.SetLength(0);
+ file.Close();
+
+ status = LevelDBDatabase::Open(temp_directory.path(), &comparator, &leveldb);
EXPECT_FALSE(leveldb);
EXPECT_FALSE(status.ok());
- bool destroyed = LevelDBDatabase::Destroy(temp_directory.path());
- EXPECT_TRUE(destroyed);
+ status = LevelDBDatabase::Destroy(temp_directory.path());
+ EXPECT_TRUE(status.ok());
status = LevelDBDatabase::Open(temp_directory.path(), &comparator, &leveldb);
EXPECT_TRUE(status.ok());
EXPECT_TRUE(leveldb);
- success = leveldb->Get(key, &got_value, &found);
- EXPECT_TRUE(success);
+ status = leveldb->Get(key, &got_value, &found);
+ EXPECT_TRUE(status.ok());
EXPECT_FALSE(found);
}
@@ -102,42 +99,42 @@ TEST(LevelDBDatabaseTest, Transaction) {
const std::string old_value("value");
put_value = old_value;
- bool success = leveldb->Put(key, &put_value);
- EXPECT_TRUE(success);
+ leveldb::Status status = leveldb->Put(key, &put_value);
+ EXPECT_TRUE(status.ok());
scoped_refptr<LevelDBTransaction> transaction =
new LevelDBTransaction(leveldb.get());
const std::string new_value("new value");
put_value = new_value;
- success = leveldb->Put(key, &put_value);
- EXPECT_TRUE(success);
+ status = leveldb->Put(key, &put_value);
+ EXPECT_TRUE(status.ok());
bool found = false;
- success = transaction->Get(key, &got_value, &found);
- EXPECT_TRUE(success);
+ status = transaction->Get(key, &got_value, &found);
+ EXPECT_TRUE(status.ok());
EXPECT_TRUE(found);
EXPECT_EQ(comparator.Compare(got_value, old_value), 0);
found = false;
- success = leveldb->Get(key, &got_value, &found);
- EXPECT_TRUE(success);
+ status = leveldb->Get(key, &got_value, &found);
+ EXPECT_TRUE(status.ok());
EXPECT_TRUE(found);
EXPECT_EQ(comparator.Compare(got_value, new_value), 0);
const std::string added_key("added key");
const std::string added_value("added value");
put_value = added_value;
- success = leveldb->Put(added_key, &put_value);
- EXPECT_TRUE(success);
+ status = leveldb->Put(added_key, &put_value);
+ EXPECT_TRUE(status.ok());
- success = leveldb->Get(added_key, &got_value, &found);
- EXPECT_TRUE(success);
+ status = leveldb->Get(added_key, &got_value, &found);
+ EXPECT_TRUE(status.ok());
EXPECT_TRUE(found);
EXPECT_EQ(comparator.Compare(got_value, added_value), 0);
- success = transaction->Get(added_key, &got_value, &found);
- EXPECT_TRUE(success);
+ status = transaction->Get(added_key, &got_value, &found);
+ EXPECT_TRUE(status.ok());
EXPECT_FALSE(found);
const std::string another_key("another key");
@@ -145,8 +142,8 @@ TEST(LevelDBDatabaseTest, Transaction) {
put_value = another_value;
transaction->Put(another_key, &put_value);
- success = transaction->Get(another_key, &got_value, &found);
- EXPECT_TRUE(success);
+ status = transaction->Get(another_key, &got_value, &found);
+ EXPECT_TRUE(status.ok());
EXPECT_TRUE(found);
EXPECT_EQ(comparator.Compare(got_value, another_value), 0);
}
@@ -161,24 +158,23 @@ TEST(LevelDBDatabaseTest, TransactionIterator) {
const std::string value2("value2");
std::string put_value;
SimpleComparator comparator;
- bool success;
scoped_ptr<LevelDBDatabase> leveldb;
LevelDBDatabase::Open(temp_directory.path(), &comparator, &leveldb);
EXPECT_TRUE(leveldb);
put_value = value1;
- success = leveldb->Put(key1, &put_value);
- EXPECT_TRUE(success);
+ leveldb::Status s = leveldb->Put(key1, &put_value);
+ EXPECT_TRUE(s.ok());
put_value = value2;
- success = leveldb->Put(key2, &put_value);
- EXPECT_TRUE(success);
+ s = leveldb->Put(key2, &put_value);
+ EXPECT_TRUE(s.ok());
scoped_refptr<LevelDBTransaction> transaction =
new LevelDBTransaction(leveldb.get());
- success = leveldb->Remove(key2);
- EXPECT_TRUE(success);
+ s = leveldb->Remove(key2);
+ EXPECT_TRUE(s.ok());
scoped_ptr<LevelDBIterator> it = transaction->CreateIterator();
@@ -212,7 +208,6 @@ TEST(LevelDBDatabaseTest, TransactionCommitTest) {
std::string put_value;
std::string got_value;
SimpleComparator comparator;
- bool success;
bool found;
scoped_ptr<LevelDBDatabase> leveldb;
@@ -231,16 +226,16 @@ TEST(LevelDBDatabaseTest, TransactionCommitTest) {
put_value = value3;
transaction->Put(key2, &put_value);
- success = transaction->Commit();
- EXPECT_TRUE(success);
+ leveldb::Status status = transaction->Commit();
+ EXPECT_TRUE(status.ok());
- success = leveldb->Get(key1, &got_value, &found);
- EXPECT_TRUE(success);
+ status = leveldb->Get(key1, &got_value, &found);
+ EXPECT_TRUE(status.ok());
EXPECT_TRUE(found);
EXPECT_EQ(value1, got_value);
- success = leveldb->Get(key2, &got_value, &found);
- EXPECT_TRUE(success);
+ status = leveldb->Get(key2, &got_value, &found);
+ EXPECT_TRUE(status.ok());
EXPECT_TRUE(found);
EXPECT_EQ(value3, got_value);
}
@@ -269,6 +264,4 @@ TEST(LevelDB, Locking) {
EXPECT_TRUE(status.ok());
}
-} // namespace
-
} // namespace content
diff --git a/chromium/content/browser/indexed_db/list_set_unittest.cc b/chromium/content/browser/indexed_db/list_set_unittest.cc
index 405fb49a7d0..658414c950d 100644
--- a/chromium/content/browser/indexed_db/list_set_unittest.cc
+++ b/chromium/content/browser/indexed_db/list_set_unittest.cc
@@ -50,7 +50,7 @@ TEST(ListSetTest, ListSetConstIterator) {
TEST(ListSetTest, ListSetPrimitive) {
list_set<int> set;
EXPECT_TRUE(set.empty());
- EXPECT_EQ(static_cast<size_t>(0), set.size());
+ EXPECT_EQ(0u, set.size());
{
list_set<int>::iterator it = set.begin();
EXPECT_EQ(set.end(), it);
@@ -58,16 +58,16 @@ TEST(ListSetTest, ListSetPrimitive) {
for (int i = 5; i > 0; --i)
set.insert(i);
- EXPECT_EQ(static_cast<size_t>(5), set.size());
+ EXPECT_EQ(5u, set.size());
EXPECT_FALSE(set.empty());
set.erase(3);
- EXPECT_EQ(static_cast<size_t>(4), set.size());
+ EXPECT_EQ(4u, set.size());
- EXPECT_EQ(static_cast<size_t>(1), set.count(2));
+ EXPECT_EQ(1u, set.count(2));
set.erase(2);
- EXPECT_EQ(static_cast<size_t>(0), set.count(2));
- EXPECT_EQ(static_cast<size_t>(3), set.size());
+ EXPECT_EQ(0u, set.count(2));
+ EXPECT_EQ(3u, set.size());
{
list_set<int>::iterator it = set.begin();
@@ -84,7 +84,7 @@ TEST(ListSetTest, ListSetPrimitive) {
set.erase(4);
set.erase(5);
- EXPECT_EQ(static_cast<size_t>(0), set.size());
+ EXPECT_EQ(0u, set.size());
EXPECT_TRUE(set.empty());
{
list_set<int>::iterator it = set.begin();
@@ -111,7 +111,7 @@ class Wrapped {
TEST(ListSetTest, ListSetObject) {
list_set<Wrapped<int> > set;
- EXPECT_EQ(static_cast<size_t>(0), set.size());
+ EXPECT_EQ(0u, set.size());
{
list_set<Wrapped<int> >::iterator it = set.begin();
EXPECT_EQ(set.end(), it);
@@ -121,7 +121,7 @@ TEST(ListSetTest, ListSetObject) {
set.insert(Wrapped<int>(1));
set.insert(Wrapped<int>(2));
- EXPECT_EQ(static_cast<size_t>(3), set.size());
+ EXPECT_EQ(3u, set.size());
{
list_set<Wrapped<int> >::iterator it = set.begin();
@@ -138,7 +138,7 @@ TEST(ListSetTest, ListSetObject) {
set.erase(Wrapped<int>(1));
set.erase(Wrapped<int>(2));
- EXPECT_EQ(static_cast<size_t>(0), set.size());
+ EXPECT_EQ(0u, set.size());
{
list_set<Wrapped<int> >::iterator it = set.begin();
EXPECT_EQ(set.end(), it);
@@ -151,7 +151,7 @@ TEST(ListSetTest, ListSetPointer) {
scoped_ptr<Wrapped<int> > w2(new Wrapped<int>(2));
list_set<Wrapped<int>*> set;
- EXPECT_EQ(static_cast<size_t>(0), set.size());
+ EXPECT_EQ(0u, set.size());
{
list_set<Wrapped<int>*>::iterator it = set.begin();
EXPECT_EQ(set.end(), it);
@@ -161,7 +161,7 @@ TEST(ListSetTest, ListSetPointer) {
set.insert(w1.get());
set.insert(w2.get());
- EXPECT_EQ(static_cast<size_t>(3), set.size());
+ EXPECT_EQ(3u, set.size());
{
list_set<Wrapped<int>*>::iterator it = set.begin();
@@ -178,7 +178,7 @@ TEST(ListSetTest, ListSetPointer) {
set.erase(w1.get());
set.erase(w2.get());
- EXPECT_EQ(static_cast<size_t>(0), set.size());
+ EXPECT_EQ(0u, set.size());
{
list_set<Wrapped<int>*>::iterator it = set.begin();
EXPECT_EQ(set.end(), it);
@@ -199,7 +199,7 @@ class RefCounted : public base::RefCounted<RefCounted<T> > {
TEST(ListSetTest, ListSetRefCounted) {
list_set<scoped_refptr<RefCounted<int> > > set;
- EXPECT_EQ(static_cast<size_t>(0), set.size());
+ EXPECT_EQ(0u, set.size());
{
list_set<scoped_refptr<RefCounted<int> > >::iterator it = set.begin();
EXPECT_EQ(set.end(), it);
@@ -213,7 +213,7 @@ TEST(ListSetTest, ListSetRefCounted) {
set.insert(r1);
set.insert(r2);
- EXPECT_EQ(static_cast<size_t>(3), set.size());
+ EXPECT_EQ(3u, set.size());
{
list_set<scoped_refptr<RefCounted<int> > >::iterator it = set.begin();
@@ -230,7 +230,7 @@ TEST(ListSetTest, ListSetRefCounted) {
set.erase(r1);
set.erase(r2);
- EXPECT_EQ(static_cast<size_t>(0), set.size());
+ EXPECT_EQ(0u, set.size());
{
list_set<scoped_refptr<RefCounted<int> > >::iterator it = set.begin();
EXPECT_EQ(set.end(), it);
diff --git a/chromium/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.cc b/chromium/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.cc
new file mode 100644
index 00000000000..0014070b3a2
--- /dev/null
+++ b/chromium/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.cc
@@ -0,0 +1,168 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "base/logging.h"
+#include "content/browser/indexed_db/leveldb/leveldb_transaction.h"
+#include "content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.h"
+#include "third_party/leveldatabase/src/include/leveldb/status.h"
+
+namespace {
+
+class FunctionTracer {
+ public:
+ FunctionTracer(const std::string& class_name,
+ const std::string& method_name,
+ int instance_num)
+ : class_name_(class_name),
+ method_name_(method_name),
+ instance_count_(instance_num),
+ current_call_num_(0) {}
+
+ void log_call() {
+ current_call_num_++;
+ VLOG(0) << class_name_ << '[' << instance_count_ << "]::" << method_name_
+ << "()[" << current_call_num_ << ']';
+ }
+
+ private:
+ std::string class_name_;
+ std::string method_name_;
+ int instance_count_;
+ int current_call_num_;
+};
+
+} // namespace
+
+namespace content {
+
+class LevelDBTestTansaction : public LevelDBTransaction {
+ public:
+ LevelDBTestTansaction(LevelDBDatabase* db,
+ FailMethod fail_method,
+ int fail_on_call_num)
+ : LevelDBTransaction(db),
+ fail_method_(fail_method),
+ fail_on_call_num_(fail_on_call_num),
+ current_call_num_(0) {
+ DCHECK(fail_method != FAIL_METHOD_NOTHING);
+ DCHECK_GT(fail_on_call_num, 0);
+ }
+
+ virtual leveldb::Status Get(const base::StringPiece& key,
+ std::string* value,
+ bool* found) OVERRIDE {
+ if (fail_method_ != FAIL_METHOD_GET ||
+ ++current_call_num_ != fail_on_call_num_)
+ return LevelDBTransaction::Get(key, value, found);
+
+ *found = false;
+ return leveldb::Status::Corruption("Corrupted for the test");
+ }
+
+ virtual leveldb::Status Commit() OVERRIDE {
+ if (fail_method_ != FAIL_METHOD_COMMIT ||
+ ++current_call_num_ != fail_on_call_num_)
+ return LevelDBTransaction::Commit();
+
+ return leveldb::Status::Corruption("Corrupted for the test");
+ }
+
+ private:
+ virtual ~LevelDBTestTansaction() {}
+
+ FailMethod fail_method_;
+ int fail_on_call_num_;
+ int current_call_num_;
+};
+
+class LevelDBTraceTansaction : public LevelDBTransaction {
+ public:
+ LevelDBTraceTansaction(LevelDBDatabase* db, int tx_num)
+ : LevelDBTransaction(db),
+ commit_tracer_(s_class_name, "Commit", tx_num),
+ get_tracer_(s_class_name, "Get", tx_num) {}
+
+ virtual leveldb::Status Get(const base::StringPiece& key,
+ std::string* value,
+ bool* found) OVERRIDE {
+ get_tracer_.log_call();
+ return LevelDBTransaction::Get(key, value, found);
+ }
+
+ virtual leveldb::Status Commit() OVERRIDE {
+ commit_tracer_.log_call();
+ return LevelDBTransaction::Commit();
+ }
+
+ private:
+ virtual ~LevelDBTraceTansaction() {}
+
+ const static std::string s_class_name;
+
+ FunctionTracer commit_tracer_;
+ FunctionTracer get_tracer_;
+};
+
+const std::string LevelDBTraceTansaction::s_class_name = "LevelDBTransaction";
+
+MockBrowserTestIndexedDBClassFactory::MockBrowserTestIndexedDBClassFactory()
+ : failure_class_(FAIL_CLASS_NOTHING),
+ failure_method_(FAIL_METHOD_NOTHING),
+ only_trace_calls_(false) {
+}
+
+MockBrowserTestIndexedDBClassFactory::~MockBrowserTestIndexedDBClassFactory() {
+}
+
+LevelDBTransaction*
+MockBrowserTestIndexedDBClassFactory::CreateLevelDBTransaction(
+ LevelDBDatabase* db) {
+ instance_count_[FAIL_CLASS_LEVELDB_TRANSACTION] =
+ instance_count_[FAIL_CLASS_LEVELDB_TRANSACTION] + 1;
+ if (only_trace_calls_) {
+ return new LevelDBTraceTansaction(
+ db, instance_count_[FAIL_CLASS_LEVELDB_TRANSACTION]);
+ } else {
+ if (failure_class_ == FAIL_CLASS_LEVELDB_TRANSACTION &&
+ instance_count_[FAIL_CLASS_LEVELDB_TRANSACTION] ==
+ fail_on_instance_num_[FAIL_CLASS_LEVELDB_TRANSACTION]) {
+ return new LevelDBTestTansaction(
+ db,
+ failure_method_,
+ fail_on_call_num_[FAIL_CLASS_LEVELDB_TRANSACTION]);
+ } else {
+ return IndexedDBClassFactory::CreateLevelDBTransaction(db);
+ }
+ }
+}
+
+void MockBrowserTestIndexedDBClassFactory::FailOperation(
+ FailClass failure_class,
+ FailMethod failure_method,
+ int fail_on_instance_num,
+ int fail_on_call_num) {
+ VLOG(0) << "FailOperation: class=" << failure_class
+ << ", method=" << failure_method
+ << ", instanceNum=" << fail_on_instance_num
+ << ", callNum=" << fail_on_call_num;
+ DCHECK(failure_class != FAIL_CLASS_NOTHING);
+ DCHECK(failure_method != FAIL_METHOD_NOTHING);
+ failure_class_ = failure_class;
+ failure_method_ = failure_method;
+ fail_on_instance_num_[failure_class_] = fail_on_instance_num;
+ fail_on_call_num_[failure_class_] = fail_on_call_num;
+ instance_count_.clear();
+}
+
+void MockBrowserTestIndexedDBClassFactory::Reset() {
+ failure_class_ = FAIL_CLASS_NOTHING;
+ failure_method_ = FAIL_METHOD_NOTHING;
+ instance_count_.clear();
+ fail_on_instance_num_.clear();
+ fail_on_call_num_.clear();
+}
+
+} // namespace content
diff --git a/chromium/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.h b/chromium/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.h
new file mode 100644
index 00000000000..b30f150cb4a
--- /dev/null
+++ b/chromium/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.h
@@ -0,0 +1,52 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_MOCK_BROWSERTEST_INDEXED_DB_CLASS_FACTORY_H_
+#define CONTENT_BROWSER_INDEXED_DB_MOCK_BROWSERTEST_INDEXED_DB_CLASS_FACTORY_H_
+
+#include <map>
+
+#include "content/browser/indexed_db/indexed_db_class_factory.h"
+
+namespace content {
+
+class LevelDBTransaction;
+class LevelDBDatabase;
+
+enum FailClass {
+ FAIL_CLASS_NOTHING,
+ FAIL_CLASS_LEVELDB_TRANSACTION,
+};
+
+enum FailMethod {
+ FAIL_METHOD_NOTHING,
+ FAIL_METHOD_COMMIT,
+ FAIL_METHOD_GET,
+};
+
+class MockBrowserTestIndexedDBClassFactory : public IndexedDBClassFactory {
+ public:
+ MockBrowserTestIndexedDBClassFactory();
+ virtual ~MockBrowserTestIndexedDBClassFactory();
+ virtual LevelDBTransaction* CreateLevelDBTransaction(
+ LevelDBDatabase* db) OVERRIDE;
+
+ void FailOperation(FailClass failure_class,
+ FailMethod failure_method,
+ int fail_on_instance_num,
+ int fail_on_call_num);
+ void Reset();
+
+ private:
+ FailClass failure_class_;
+ FailMethod failure_method_;
+ std::map<FailClass, int> instance_count_;
+ std::map<FailClass, int> fail_on_instance_num_;
+ std::map<FailClass, int> fail_on_call_num_;
+ bool only_trace_calls_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_MOCK_BROWSERTEST_INDEXED_DB_CLASS_FACTORY_H_
diff --git a/chromium/content/browser/indexed_db/mock_indexed_db_callbacks.cc b/chromium/content/browser/indexed_db/mock_indexed_db_callbacks.cc
index a585246ab34..62afee8d438 100644
--- a/chromium/content/browser/indexed_db/mock_indexed_db_callbacks.cc
+++ b/chromium/content/browser/indexed_db/mock_indexed_db_callbacks.cc
@@ -19,8 +19,13 @@ MockIndexedDBCallbacks::~MockIndexedDBCallbacks() {
void MockIndexedDBCallbacks::OnSuccess() {}
+void MockIndexedDBCallbacks::OnSuccess(int64 result) {
+}
+
void MockIndexedDBCallbacks::OnSuccess(const std::vector<base::string16>&) {}
+void MockIndexedDBCallbacks::OnSuccess(const IndexedDBKey& key) {}
+
void MockIndexedDBCallbacks::OnSuccess(
scoped_ptr<IndexedDBConnection> connection,
const IndexedDBDatabaseMetadata& metadata) {
diff --git a/chromium/content/browser/indexed_db/mock_indexed_db_callbacks.h b/chromium/content/browser/indexed_db/mock_indexed_db_callbacks.h
index 1af3ca1781e..fecfd1abb29 100644
--- a/chromium/content/browser/indexed_db/mock_indexed_db_callbacks.h
+++ b/chromium/content/browser/indexed_db/mock_indexed_db_callbacks.h
@@ -5,6 +5,8 @@
#ifndef CONTENT_BROWSER_INDEXED_DB_MOCK_INDEXED_DB_CALLBACKS_H_
#define CONTENT_BROWSER_INDEXED_DB_MOCK_INDEXED_DB_CALLBACKS_H_
+#include <vector>
+
#include "content/browser/indexed_db/indexed_db_callbacks.h"
#include "content/browser/indexed_db/indexed_db_connection.h"
@@ -13,10 +15,12 @@ namespace content {
class MockIndexedDBCallbacks : public IndexedDBCallbacks {
public:
MockIndexedDBCallbacks();
- MockIndexedDBCallbacks(bool expect_connection);
+ explicit MockIndexedDBCallbacks(bool expect_connection);
virtual void OnSuccess() OVERRIDE;
- virtual void OnSuccess(const std::vector<base::string16>&) OVERRIDE;
+ virtual void OnSuccess(int64 result) OVERRIDE;
+ virtual void OnSuccess(const std::vector<base::string16>& result) OVERRIDE;
+ virtual void OnSuccess(const IndexedDBKey& key) OVERRIDE;
virtual void OnSuccess(scoped_ptr<IndexedDBConnection> connection,
const IndexedDBDatabaseMetadata& metadata) OVERRIDE;
IndexedDBConnection* connection() { return connection_.get(); }
diff --git a/chromium/content/browser/loader/OWNERS b/chromium/content/browser/loader/OWNERS
index 5b22ddc2ba5..1e9e8232811 100644
--- a/chromium/content/browser/loader/OWNERS
+++ b/chromium/content/browser/loader/OWNERS
@@ -1 +1,2 @@
-simonjam@chromium.org
+davidben@chromium.org
+mmenke@chromium.org \ No newline at end of file
diff --git a/chromium/content/browser/loader/async_resource_handler.cc b/chromium/content/browser/loader/async_resource_handler.cc
index 93d7e839aa2..69e831bd298 100644
--- a/chromium/content/browser/loader/async_resource_handler.cc
+++ b/chromium/content/browser/loader/async_resource_handler.cc
@@ -23,7 +23,6 @@
#include "content/browser/resource_context_impl.h"
#include "content/common/resource_messages.h"
#include "content/common/view_messages.h"
-#include "content/public/browser/global_request_id.h"
#include "content/public/browser/resource_dispatcher_host_delegate.h"
#include "content/public/common/resource_response.h"
#include "net/base/io_buffer.h"
@@ -87,7 +86,8 @@ AsyncResourceHandler::AsyncResourceHandler(
did_defer_(false),
has_checked_for_sufficient_resources_(false),
sent_received_response_msg_(false),
- sent_first_data_msg_(false) {
+ sent_first_data_msg_(false),
+ reported_transfer_size_(0) {
InitializeResourceBufferConstants();
}
@@ -96,29 +96,22 @@ AsyncResourceHandler::~AsyncResourceHandler() {
rdh_->FinishedWithResourcesForRequest(request());
}
-bool AsyncResourceHandler::OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) {
+bool AsyncResourceHandler::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(AsyncResourceHandler, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(AsyncResourceHandler, message)
IPC_MESSAGE_HANDLER(ResourceHostMsg_FollowRedirect, OnFollowRedirect)
IPC_MESSAGE_HANDLER(ResourceHostMsg_DataReceived_ACK, OnDataReceivedACK)
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return handled;
}
-void AsyncResourceHandler::OnFollowRedirect(
- int request_id,
- bool has_new_first_party_for_cookies,
- const GURL& new_first_party_for_cookies) {
+void AsyncResourceHandler::OnFollowRedirect(int request_id) {
if (!request()->status().is_success()) {
DVLOG(1) << "OnFollowRedirect for invalid request";
return;
}
- if (has_new_first_party_for_cookies)
- request()->set_first_party_for_cookies(new_first_party_for_cookies);
-
ResumeIfDeferred();
}
@@ -132,18 +125,16 @@ void AsyncResourceHandler::OnDataReceivedACK(int request_id) {
}
}
-bool AsyncResourceHandler::OnUploadProgress(int request_id,
- uint64 position,
+bool AsyncResourceHandler::OnUploadProgress(uint64 position,
uint64 size) {
ResourceMessageFilter* filter = GetFilter();
if (!filter)
return false;
return filter->Send(
- new ResourceMsg_UploadProgress(request_id, position, size));
+ new ResourceMsg_UploadProgress(GetRequestID(), position, size));
}
-bool AsyncResourceHandler::OnRequestRedirected(int request_id,
- const GURL& new_url,
+bool AsyncResourceHandler::OnRequestRedirected(const GURL& new_url,
ResourceResponse* response,
bool* defer) {
const ResourceRequestInfoImpl* info = GetRequestInfo();
@@ -159,14 +150,20 @@ bool AsyncResourceHandler::OnRequestRedirected(int request_id,
}
DevToolsNetLogObserver::PopulateResponseInfo(request(), response);
+ response->head.encoded_data_length = request()->GetTotalReceivedBytes();
+ reported_transfer_size_ = 0;
response->head.request_start = request()->creation_time();
response->head.response_start = TimeTicks::Now();
+ // TODO(davidben): Is it necessary to pass the new first party URL for
+ // cookies? The only case where it can change is top-level navigation requests
+ // and hopefully those will eventually all be owned by the browser. It's
+ // possible this is still needed while renderer-owned ones exist.
return info->filter()->Send(new ResourceMsg_ReceivedRedirect(
- request_id, new_url, response->head));
+ GetRequestID(), new_url, request()->first_party_for_cookies(),
+ response->head));
}
-bool AsyncResourceHandler::OnResponseStarted(int request_id,
- ResourceResponse* response,
+bool AsyncResourceHandler::OnResponseStarted(ResourceResponse* response,
bool* defer) {
// For changes to the main frame, inform the renderer of the new URL's
// per-host settings before the request actually commits. This way the
@@ -197,9 +194,17 @@ bool AsyncResourceHandler::OnResponseStarted(int request_id,
net::GetHostOrSpecFromURL(request_url))));
}
+ // If the parent handler downloaded the resource to a file, grant the child
+ // read permissions on it.
+ if (!response->head.download_file_path.empty()) {
+ rdh_->RegisterDownloadedTempFile(
+ info->GetChildID(), info->GetRequestID(),
+ response->head.download_file_path);
+ }
+
response->head.request_start = request()->creation_time();
response->head.response_start = TimeTicks::Now();
- info->filter()->Send(new ResourceMsg_ReceivedResponse(request_id,
+ info->filter()->Send(new ResourceMsg_ReceivedResponse(GetRequestID(),
response->head));
sent_received_response_msg_ = true;
@@ -207,21 +212,22 @@ bool AsyncResourceHandler::OnResponseStarted(int request_id,
std::vector<char> copy(request()->response_info().metadata->data(),
request()->response_info().metadata->data() +
request()->response_info().metadata->size());
- info->filter()->Send(new ResourceMsg_ReceivedCachedMetadata(request_id,
+ info->filter()->Send(new ResourceMsg_ReceivedCachedMetadata(GetRequestID(),
copy));
}
return true;
}
-bool AsyncResourceHandler::OnWillStart(int request_id,
- const GURL& url,
- bool* defer) {
+bool AsyncResourceHandler::OnWillStart(const GURL& url, bool* defer) {
+ return true;
+}
+
+bool AsyncResourceHandler::OnBeforeNetworkStart(const GURL& url, bool* defer) {
return true;
}
-bool AsyncResourceHandler::OnWillRead(int request_id,
- scoped_refptr<net::IOBuffer>* buf,
+bool AsyncResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf,
int* buf_size,
int min_size) {
DCHECK_EQ(-1, min_size);
@@ -242,8 +248,7 @@ bool AsyncResourceHandler::OnWillRead(int request_id,
return true;
}
-bool AsyncResourceHandler::OnReadCompleted(int request_id, int bytes_read,
- bool* defer) {
+bool AsyncResourceHandler::OnReadCompleted(int bytes_read, bool* defer) {
DCHECK_GE(bytes_read, 0);
if (!bytes_read)
@@ -268,16 +273,18 @@ bool AsyncResourceHandler::OnReadCompleted(int request_id, int bytes_read,
if (!buffer_->ShareToProcess(filter->PeerHandle(), &handle, &size))
return false;
filter->Send(new ResourceMsg_SetDataBuffer(
- request_id, handle, size, filter->peer_pid()));
+ GetRequestID(), handle, size, filter->peer_pid()));
sent_first_data_msg_ = true;
}
int data_offset = buffer_->GetLastAllocationOffset();
- int encoded_data_length =
- DevToolsNetLogObserver::GetAndResetEncodedDataLength(request());
+
+ int64_t current_transfer_size = request()->GetTotalReceivedBytes();
+ int encoded_data_length = current_transfer_size - reported_transfer_size_;
+ reported_transfer_size_ = current_transfer_size;
filter->Send(new ResourceMsg_DataReceived(
- request_id, data_offset, bytes_read, encoded_data_length));
+ GetRequestID(), data_offset, bytes_read, encoded_data_length));
++pending_data_count_;
UMA_HISTOGRAM_CUSTOM_COUNTS(
"Net.AsyncResourceHandler_PendingDataCount",
@@ -294,20 +301,19 @@ bool AsyncResourceHandler::OnReadCompleted(int request_id, int bytes_read,
return true;
}
-void AsyncResourceHandler::OnDataDownloaded(
- int request_id, int bytes_downloaded) {
- int encoded_data_length =
- DevToolsNetLogObserver::GetAndResetEncodedDataLength(request());
+void AsyncResourceHandler::OnDataDownloaded(int bytes_downloaded) {
+ int64_t current_transfer_size = request()->GetTotalReceivedBytes();
+ int encoded_data_length = current_transfer_size - reported_transfer_size_;
+ reported_transfer_size_ = current_transfer_size;
ResourceMessageFilter* filter = GetFilter();
if (filter) {
filter->Send(new ResourceMsg_DataDownloaded(
- request_id, bytes_downloaded, encoded_data_length));
+ GetRequestID(), bytes_downloaded, encoded_data_length));
}
}
void AsyncResourceHandler::OnResponseCompleted(
- int request_id,
const net::URLRequestStatus& status,
const std::string& security_info,
bool* defer) {
@@ -329,8 +335,6 @@ void AsyncResourceHandler::OnResponseCompleted(
CHECK(status.status() != net::URLRequestStatus::SUCCESS ||
sent_received_response_msg_);
- TimeTicks completion_time = TimeTicks::Now();
-
int error_code = status.error();
bool was_ignored_by_handler = info->WasIgnoredByHandler();
@@ -350,12 +354,16 @@ void AsyncResourceHandler::OnResponseCompleted(
error_code = net::ERR_FAILED;
}
+ ResourceMsg_RequestCompleteData request_complete_data;
+ request_complete_data.error_code = error_code;
+ request_complete_data.was_ignored_by_handler = was_ignored_by_handler;
+ request_complete_data.exists_in_cache = request()->response_info().was_cached;
+ request_complete_data.security_info = security_info;
+ request_complete_data.completion_time = TimeTicks::Now();
+ request_complete_data.encoded_data_length =
+ request()->GetTotalReceivedBytes();
info->filter()->Send(
- new ResourceMsg_RequestComplete(request_id,
- error_code,
- was_ignored_by_handler,
- security_info,
- completion_time));
+ new ResourceMsg_RequestComplete(GetRequestID(), request_complete_data));
}
bool AsyncResourceHandler::EnsureResourceBufferIsInitialized() {
diff --git a/chromium/content/browser/loader/async_resource_handler.h b/chromium/content/browser/loader/async_resource_handler.h
index f603f4f858f..b0821191e0d 100644
--- a/chromium/content/browser/loader/async_resource_handler.h
+++ b/chromium/content/browser/loader/async_resource_handler.h
@@ -32,42 +32,29 @@ class AsyncResourceHandler : public ResourceHandler,
ResourceDispatcherHostImpl* rdh);
virtual ~AsyncResourceHandler();
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
// ResourceHandler implementation:
- virtual bool OnUploadProgress(int request_id,
- uint64 position,
- uint64 size) OVERRIDE;
- virtual bool OnRequestRedirected(int request_id,
- const GURL& new_url,
+ virtual bool OnUploadProgress(uint64 position, uint64 size) OVERRIDE;
+ virtual bool OnRequestRedirected(const GURL& new_url,
ResourceResponse* response,
bool* defer) OVERRIDE;
- virtual bool OnResponseStarted(int request_id,
- ResourceResponse* response,
+ virtual bool OnResponseStarted(ResourceResponse* response,
bool* defer) OVERRIDE;
- virtual bool OnWillStart(int request_id,
- const GURL& url,
- bool* defer) OVERRIDE;
- virtual bool OnWillRead(int request_id,
- scoped_refptr<net::IOBuffer>* buf,
+ virtual bool OnWillStart(const GURL& url, bool* defer) OVERRIDE;
+ virtual bool OnBeforeNetworkStart(const GURL& url, bool* defer) OVERRIDE;
+ virtual bool OnWillRead(scoped_refptr<net::IOBuffer>* buf,
int* buf_size,
int min_size) OVERRIDE;
- virtual bool OnReadCompleted(int request_id,
- int bytes_read,
- bool* defer) OVERRIDE;
- virtual void OnResponseCompleted(int request_id,
- const net::URLRequestStatus& status,
+ virtual bool OnReadCompleted(int bytes_read, bool* defer) OVERRIDE;
+ virtual void OnResponseCompleted(const net::URLRequestStatus& status,
const std::string& security_info,
bool* defer) OVERRIDE;
- virtual void OnDataDownloaded(int request_id,
- int bytes_downloaded) OVERRIDE;
+ virtual void OnDataDownloaded(int bytes_downloaded) OVERRIDE;
private:
// IPC message handlers:
- void OnFollowRedirect(int request_id,
- bool has_new_first_party_for_cookies,
- const GURL& new_first_party_for_cookies);
+ void OnFollowRedirect(int request_id);
void OnDataReceivedACK(int request_id);
bool EnsureResourceBufferIsInitialized();
@@ -89,6 +76,8 @@ class AsyncResourceHandler : public ResourceHandler,
bool sent_received_response_msg_;
bool sent_first_data_msg_;
+ int64_t reported_transfer_size_;
+
DISALLOW_COPY_AND_ASSIGN(AsyncResourceHandler);
};
diff --git a/chromium/content/browser/loader/buffered_resource_handler.cc b/chromium/content/browser/loader/buffered_resource_handler.cc
index 2a5ef9cd120..8fb8c5d4484 100644
--- a/chromium/content/browser/loader/buffered_resource_handler.cc
+++ b/chromium/content/browser/loader/buffered_resource_handler.cc
@@ -15,6 +15,7 @@
#include "content/browser/loader/certificate_resource_handler.h"
#include "content/browser/loader/resource_dispatcher_host_impl.h"
#include "content/browser/loader/resource_request_info_impl.h"
+#include "content/browser/loader/stream_resource_handler.h"
#include "content/browser/plugin_service_impl.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/download_item.h"
@@ -103,10 +104,8 @@ void BufferedResourceHandler::SetController(ResourceController* controller) {
next_handler_->SetController(this);
}
-bool BufferedResourceHandler::OnResponseStarted(
- int request_id,
- ResourceResponse* response,
- bool* defer) {
+bool BufferedResourceHandler::OnResponseStarted(ResourceResponse* response,
+ bool* defer) {
response_ = response;
// TODO(darin): It is very odd to special-case 304 responses at this level.
@@ -146,12 +145,11 @@ bool BufferedResourceHandler::OnResponseStarted(
// We'll let the original event handler provide a buffer, and reuse it for
// subsequent reads until we're done buffering.
-bool BufferedResourceHandler::OnWillRead(int request_id,
- scoped_refptr<net::IOBuffer>* buf,
+bool BufferedResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf,
int* buf_size,
int min_size) {
if (state_ == STATE_STREAMING)
- return next_handler_->OnWillRead(request_id, buf, buf_size, min_size);
+ return next_handler_->OnWillRead(buf, buf_size, min_size);
DCHECK_EQ(-1, min_size);
@@ -160,7 +158,7 @@ bool BufferedResourceHandler::OnWillRead(int request_id,
*buf = new DependentIOBuffer(read_buffer_.get(), bytes_read_);
*buf_size = read_buffer_size_ - bytes_read_;
} else {
- if (!next_handler_->OnWillRead(request_id, buf, buf_size, min_size))
+ if (!next_handler_->OnWillRead(buf, buf_size, min_size))
return false;
read_buffer_ = *buf;
@@ -170,10 +168,9 @@ bool BufferedResourceHandler::OnWillRead(int request_id,
return true;
}
-bool BufferedResourceHandler::OnReadCompleted(int request_id, int bytes_read,
- bool* defer) {
+bool BufferedResourceHandler::OnReadCompleted(int bytes_read, bool* defer) {
if (state_ == STATE_STREAMING)
- return next_handler_->OnReadCompleted(request_id, bytes_read, defer);
+ return next_handler_->OnReadCompleted(bytes_read, defer);
DCHECK_EQ(state_, STATE_BUFFERING);
bytes_read_ += bytes_read;
@@ -186,7 +183,6 @@ bool BufferedResourceHandler::OnReadCompleted(int request_id, int bytes_read,
}
void BufferedResourceHandler::OnResponseCompleted(
- int request_id,
const net::URLRequestStatus& status,
const std::string& security_info,
bool* defer) {
@@ -194,7 +190,7 @@ void BufferedResourceHandler::OnResponseCompleted(
// handler defers OnResponseCompleted.
state_ = STATE_STREAMING;
- next_handler_->OnResponseCompleted(request_id, status, security_info, defer);
+ next_handler_->OnResponseCompleted(status, security_info, defer);
}
void BufferedResourceHandler::Resume() {
@@ -242,7 +238,7 @@ bool BufferedResourceHandler::ProcessResponse(bool* defer) {
state_ = STATE_REPLAYING;
- if (!next_handler_->OnResponseStarted(GetRequestID(), response_.get(), defer))
+ if (!next_handler_->OnResponseStarted(response_.get(), defer))
return false;
if (!read_buffer_.get()) {
@@ -307,9 +303,10 @@ bool BufferedResourceHandler::SelectNextHandler(bool* defer) {
if (net::IsSupportedCertificateMimeType(mime_type)) {
// Install certificate file.
+ info->set_is_download(true);
scoped_ptr<ResourceHandler> handler(
new CertificateResourceHandler(request()));
- return UseAlternateNextHandler(handler.Pass());
+ return UseAlternateNextHandler(handler.Pass(), std::string());
}
if (!info->allow_download())
@@ -320,10 +317,12 @@ bool BufferedResourceHandler::SelectNextHandler(bool* defer) {
if (net::IsSupportedMimeType(mime_type))
return true;
+ std::string payload;
scoped_ptr<ResourceHandler> handler(
- host_->MaybeInterceptAsStream(request(), response_.get()));
- if (handler)
- return UseAlternateNextHandler(handler.Pass());
+ host_->MaybeInterceptAsStream(request(), response_.get(), &payload));
+ if (handler) {
+ return UseAlternateNextHandler(handler.Pass(), payload);
+ }
#if defined(ENABLE_PLUGINS)
bool stale;
@@ -333,6 +332,7 @@ bool BufferedResourceHandler::SelectNextHandler(bool* defer) {
PluginServiceImpl::GetInstance()->GetPlugins(
base::Bind(&BufferedResourceHandler::OnPluginsLoaded,
weak_ptr_factory_.GetWeakPtr()));
+ request()->LogBlockedBy("BufferedResourceHandler");
*defer = true;
return true;
}
@@ -351,11 +351,12 @@ bool BufferedResourceHandler::SelectNextHandler(bool* defer) {
content::DownloadItem::kInvalidId,
scoped_ptr<DownloadSaveInfo>(new DownloadSaveInfo()),
DownloadUrlParameters::OnStartedCallback()));
- return UseAlternateNextHandler(handler.Pass());
+ return UseAlternateNextHandler(handler.Pass(), std::string());
}
bool BufferedResourceHandler::UseAlternateNextHandler(
- scoped_ptr<ResourceHandler> new_handler) {
+ scoped_ptr<ResourceHandler> new_handler,
+ const std::string& payload_for_old_handler) {
if (response_->head.headers.get() && // Can be NULL if FTP.
response_->head.headers->response_code() / 100 != 2) {
// The response code indicates that this is an error page, but we don't
@@ -363,38 +364,55 @@ bool BufferedResourceHandler::UseAlternateNextHandler(
// own error page instead of triggering a download.
// TODO(abarth): We should abstract the response_code test, but this kind
// of check is scattered throughout our codebase.
- request()->CancelWithError(net::ERR_FILE_NOT_FOUND);
+ request()->CancelWithError(net::ERR_INVALID_RESPONSE);
return false;
}
- int request_id = GetRequestID();
-
// Inform the original ResourceHandler that this will be handled entirely by
// the new ResourceHandler.
// TODO(darin): We should probably check the return values of these.
- // TODO(davidben): These DCHECKs do actually trigger.
bool defer_ignored = false;
- next_handler_->OnResponseStarted(request_id, response_.get(), &defer_ignored);
- DCHECK(!defer_ignored);
- net::URLRequestStatus status(net::URLRequestStatus::CANCELED,
- net::ERR_ABORTED);
- next_handler_->OnResponseCompleted(request_id, status, std::string(),
- &defer_ignored);
+ next_handler_->OnResponseStarted(response_.get(), &defer_ignored);
+ // Although deferring OnResponseStarted is legal, the only downstream handler
+ // which does so is CrossSiteResourceHandler. Cross-site transitions should
+ // not trigger when switching handlers.
DCHECK(!defer_ignored);
+ if (payload_for_old_handler.empty()) {
+ net::URLRequestStatus status(net::URLRequestStatus::CANCELED,
+ net::ERR_ABORTED);
+ next_handler_->OnResponseCompleted(status, std::string(), &defer_ignored);
+ DCHECK(!defer_ignored);
+ } else {
+ scoped_refptr<net::IOBuffer> buf;
+ int size = 0;
+
+ next_handler_->OnWillRead(&buf, &size, -1);
+ CHECK_GE(size, static_cast<int>(payload_for_old_handler.length()));
+
+ memcpy(buf->data(), payload_for_old_handler.c_str(),
+ payload_for_old_handler.length());
+
+ next_handler_->OnReadCompleted(payload_for_old_handler.length(),
+ &defer_ignored);
+ DCHECK(!defer_ignored);
+
+ net::URLRequestStatus status(net::URLRequestStatus::SUCCESS, 0);
+ next_handler_->OnResponseCompleted(status, std::string(), &defer_ignored);
+ DCHECK(!defer_ignored);
+ }
// This is handled entirely within the new ResourceHandler, so just reset the
// original ResourceHandler.
next_handler_ = new_handler.Pass();
next_handler_->SetController(this);
- return CopyReadBufferToNextHandler(request_id);
+ return CopyReadBufferToNextHandler();
}
bool BufferedResourceHandler::ReplayReadCompleted(bool* defer) {
DCHECK(read_buffer_.get());
- bool result = next_handler_->OnReadCompleted(GetRequestID(), bytes_read_,
- defer);
+ bool result = next_handler_->OnReadCompleted(bytes_read_, defer);
read_buffer_ = NULL;
read_buffer_size_ = 0;
@@ -454,13 +472,13 @@ bool BufferedResourceHandler::HasSupportingPlugin(bool* stale) {
#endif
}
-bool BufferedResourceHandler::CopyReadBufferToNextHandler(int request_id) {
- if (!bytes_read_)
+bool BufferedResourceHandler::CopyReadBufferToNextHandler() {
+ if (!read_buffer_.get())
return true;
scoped_refptr<net::IOBuffer> buf;
int buf_len = 0;
- if (!next_handler_->OnWillRead(request_id, &buf, &buf_len, bytes_read_))
+ if (!next_handler_->OnWillRead(&buf, &buf_len, bytes_read_))
return false;
CHECK((buf_len >= bytes_read_) && (bytes_read_ >= 0));
@@ -470,6 +488,7 @@ bool BufferedResourceHandler::CopyReadBufferToNextHandler(int request_id) {
void BufferedResourceHandler::OnPluginsLoaded(
const std::vector<WebPluginInfo>& plugins) {
+ request()->LogUnblocked();
bool defer = false;
if (!ProcessResponse(&defer)) {
controller()->Cancel();
diff --git a/chromium/content/browser/loader/buffered_resource_handler.h b/chromium/content/browser/loader/buffered_resource_handler.h
index 23d15cccf6f..0b6d6989097 100644
--- a/chromium/content/browser/loader/buffered_resource_handler.h
+++ b/chromium/content/browser/loader/buffered_resource_handler.h
@@ -33,17 +33,13 @@ class BufferedResourceHandler
private:
// ResourceHandler implementation:
virtual void SetController(ResourceController* controller) OVERRIDE;
- virtual bool OnResponseStarted(int request_id,
- ResourceResponse* response,
+ virtual bool OnResponseStarted(ResourceResponse* response,
bool* defer) OVERRIDE;
- virtual bool OnWillRead(int request_id,
- scoped_refptr<net::IOBuffer>* buf,
+ virtual bool OnWillRead(scoped_refptr<net::IOBuffer>* buf,
int* buf_size,
int min_size) OVERRIDE;
- virtual bool OnReadCompleted(int request_id, int bytes_read,
- bool* defer) OVERRIDE;
- virtual void OnResponseCompleted(int request_id,
- const net::URLRequestStatus& status,
+ virtual bool OnReadCompleted(int bytes_read, bool* defer) OVERRIDE;
+ virtual void OnResponseCompleted(const net::URLRequestStatus& status,
const std::string& security_info,
bool* defer) OVERRIDE;
@@ -58,7 +54,8 @@ class BufferedResourceHandler
bool ShouldSniffContent();
bool DetermineMimeType();
bool SelectNextHandler(bool* defer);
- bool UseAlternateNextHandler(scoped_ptr<ResourceHandler> handler);
+ bool UseAlternateNextHandler(scoped_ptr<ResourceHandler> handler,
+ const std::string& payload_for_old_handler);
bool ReplayReadCompleted(bool* defer);
void CallReplayReadCompleted();
@@ -67,7 +64,7 @@ class BufferedResourceHandler
bool HasSupportingPlugin(bool* is_stale);
// Copies data from |read_buffer_| to |next_handler_|.
- bool CopyReadBufferToNextHandler(int request_id);
+ bool CopyReadBufferToNextHandler();
// Called on the IO thread once the list of plugins has been loaded.
void OnPluginsLoaded(const std::vector<WebPluginInfo>& plugins);
diff --git a/chromium/content/browser/loader/certificate_resource_handler.cc b/chromium/content/browser/loader/certificate_resource_handler.cc
index b9ae4ee4e56..9b3a607eb46 100644
--- a/chromium/content/browser/loader/certificate_resource_handler.cc
+++ b/chromium/content/browser/loader/certificate_resource_handler.cc
@@ -29,37 +29,36 @@ CertificateResourceHandler::CertificateResourceHandler(
CertificateResourceHandler::~CertificateResourceHandler() {
}
-bool CertificateResourceHandler::OnUploadProgress(int request_id,
- uint64 position,
- uint64 size) {
+bool CertificateResourceHandler::OnUploadProgress(uint64 position,
+ uint64 size) {
return true;
}
-bool CertificateResourceHandler::OnRequestRedirected(int request_id,
- const GURL& url,
- ResourceResponse* resp,
- bool* defer) {
+bool CertificateResourceHandler::OnRequestRedirected(const GURL& url,
+ ResourceResponse* resp,
+ bool* defer) {
url_ = url;
return true;
}
-bool CertificateResourceHandler::OnResponseStarted(int request_id,
- ResourceResponse* resp,
- bool* defer) {
+bool CertificateResourceHandler::OnResponseStarted(ResourceResponse* resp,
+ bool* defer) {
cert_type_ = net::GetCertificateMimeTypeForMimeType(resp->head.mime_type);
return cert_type_ != net::CERTIFICATE_MIME_TYPE_UNKNOWN;
}
-bool CertificateResourceHandler::OnWillStart(int request_id,
- const GURL& url,
- bool* defer) {
+bool CertificateResourceHandler::OnWillStart(const GURL& url, bool* defer) {
return true;
}
-bool CertificateResourceHandler::OnWillRead(int request_id,
- scoped_refptr<net::IOBuffer>* buf,
- int* buf_size,
- int min_size) {
+bool CertificateResourceHandler::OnBeforeNetworkStart(const GURL& url,
+ bool* defer) {
+ return true;
+}
+
+bool CertificateResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf,
+ int* buf_size,
+ int min_size) {
static const int kReadBufSize = 32768;
// TODO(gauravsh): Should we use 'min_size' here?
@@ -73,9 +72,7 @@ bool CertificateResourceHandler::OnWillRead(int request_id,
return true;
}
-bool CertificateResourceHandler::OnReadCompleted(int request_id,
- int bytes_read,
- bool* defer) {
+bool CertificateResourceHandler::OnReadCompleted(int bytes_read, bool* defer) {
if (!bytes_read)
return true;
@@ -85,8 +82,8 @@ bool CertificateResourceHandler::OnReadCompleted(int request_id,
// Release the ownership of the buffer, and store a reference
// to it. A new one will be allocated in OnWillRead().
- net::IOBuffer* buffer = NULL;
- read_buffer_.swap(&buffer);
+ scoped_refptr<net::IOBuffer> buffer;
+ read_buffer_.swap(buffer);
// TODO(gauravsh): Should this be handled by a separate thread?
buffer_.push_back(std::make_pair(buffer, bytes_read));
@@ -94,7 +91,6 @@ bool CertificateResourceHandler::OnReadCompleted(int request_id,
}
void CertificateResourceHandler::OnResponseCompleted(
- int request_id,
const net::URLRequestStatus& urs,
const std::string& sec_info,
bool* defer) {
@@ -111,8 +107,8 @@ void CertificateResourceHandler::OnResponseCompleted(
// data is well-formed.
const ResourceRequestInfo* info = GetRequestInfo();
GetContentClient()->browser()->AddCertificate(
- request(), cert_type_, content_bytes, content_length_,
- info->GetChildID(), info->GetRouteID());
+ cert_type_, content_bytes, content_length_,
+ info->GetChildID(), info->GetRenderFrameID());
}
void CertificateResourceHandler::AssembleResource() {
@@ -138,9 +134,7 @@ void CertificateResourceHandler::AssembleResource() {
DCHECK_EQ(content_length_, bytes_copied);
}
-void CertificateResourceHandler::OnDataDownloaded(
- int request_id,
- int bytes_downloaded) {
+void CertificateResourceHandler::OnDataDownloaded(int bytes_downloaded) {
NOTREACHED();
}
diff --git a/chromium/content/browser/loader/certificate_resource_handler.h b/chromium/content/browser/loader/certificate_resource_handler.h
index 845bea01fd9..9c67860eb7e 100644
--- a/chromium/content/browser/loader/certificate_resource_handler.h
+++ b/chromium/content/browser/loader/certificate_resource_handler.h
@@ -34,45 +34,38 @@ class CertificateResourceHandler : public ResourceHandler {
explicit CertificateResourceHandler(net::URLRequest* request);
virtual ~CertificateResourceHandler();
- virtual bool OnUploadProgress(int request_id,
- uint64 position,
- uint64 size) OVERRIDE;
+ virtual bool OnUploadProgress(uint64 position, uint64 size) OVERRIDE;
// Not needed, as this event handler ought to be the final resource.
- virtual bool OnRequestRedirected(int request_id,
- const GURL& url,
+ virtual bool OnRequestRedirected(const GURL& url,
ResourceResponse* resp,
bool* defer) OVERRIDE;
// Check if this indeed an X509 cert.
- virtual bool OnResponseStarted(int request_id,
- ResourceResponse* resp,
+ virtual bool OnResponseStarted(ResourceResponse* resp,
bool* defer) OVERRIDE;
// Pass-through implementation.
- virtual bool OnWillStart(int request_id,
- const GURL& url,
- bool* defer) OVERRIDE;
+ virtual bool OnWillStart(const GURL& url, bool* defer) OVERRIDE;
+
+ // Pass-through implementation.
+ virtual bool OnBeforeNetworkStart(const GURL& url, bool* defer) OVERRIDE;
// Create a new buffer to store received data.
- virtual bool OnWillRead(int request_id,
- scoped_refptr<net::IOBuffer>* buf,
+ virtual bool OnWillRead(scoped_refptr<net::IOBuffer>* buf,
int* buf_size,
int min_size) OVERRIDE;
// A read was completed, maybe allocate a new buffer for further data.
- virtual bool OnReadCompleted(int request_id,
- int bytes_read,
- bool* defer) OVERRIDE;
+ virtual bool OnReadCompleted(int bytes_read, bool* defer) OVERRIDE;
// Done downloading the certificate.
- virtual void OnResponseCompleted(int request_id,
- const net::URLRequestStatus& urs,
+ virtual void OnResponseCompleted(const net::URLRequestStatus& urs,
const std::string& sec_info,
bool* defer) OVERRIDE;
// N/A to cert downloading.
- virtual void OnDataDownloaded(int request_id, int bytes_downloaded) OVERRIDE;
+ virtual void OnDataDownloaded(int bytes_downloaded) OVERRIDE;
private:
typedef std::vector<std::pair<scoped_refptr<net::IOBuffer>,
diff --git a/chromium/content/browser/loader/cross_site_resource_handler.cc b/chromium/content/browser/loader/cross_site_resource_handler.cc
index 4215a9fd965..8fa7238c357 100644
--- a/chromium/content/browser/loader/cross_site_resource_handler.cc
+++ b/chromium/content/browser/loader/cross_site_resource_handler.cc
@@ -7,69 +7,97 @@
#include <string>
#include "base/bind.h"
+#include "base/command_line.h"
#include "base/logging.h"
+#include "content/browser/appcache/appcache_interceptor.h"
+#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/cross_site_request_manager.h"
+#include "content/browser/frame_host/cross_site_transferring_request.h"
+#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/browser/loader/resource_dispatcher_host_impl.h"
#include "content/browser/loader/resource_request_info_impl.h"
-#include "content/browser/renderer_host/render_view_host_delegate.h"
-#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/global_request_id.h"
#include "content/public/browser/resource_controller.h"
+#include "content/public/browser/site_instance.h"
+#include "content/public/common/content_switches.h"
#include "content/public/common/resource_response.h"
+#include "content/public/common/url_constants.h"
#include "net/http/http_response_headers.h"
#include "net/url_request/url_request.h"
-#include "webkit/browser/appcache/appcache_interceptor.h"
namespace content {
namespace {
+bool leak_requests_for_testing_ = false;
+
// The parameters to OnCrossSiteResponseHelper exceed the number of arguments
// base::Bind supports.
struct CrossSiteResponseParams {
- CrossSiteResponseParams(int render_view_id,
- const GlobalRequestID& global_request_id,
- bool is_transfer,
- const std::vector<GURL>& transfer_url_chain,
- const Referrer& referrer,
- PageTransition page_transition,
- int64 frame_id,
- bool should_replace_current_entry)
- : render_view_id(render_view_id),
+ CrossSiteResponseParams(
+ int render_frame_id,
+ const GlobalRequestID& global_request_id,
+ bool is_transfer,
+ const std::vector<GURL>& transfer_url_chain,
+ const Referrer& referrer,
+ PageTransition page_transition,
+ bool should_replace_current_entry)
+ : render_frame_id(render_frame_id),
global_request_id(global_request_id),
is_transfer(is_transfer),
transfer_url_chain(transfer_url_chain),
referrer(referrer),
page_transition(page_transition),
- frame_id(frame_id),
should_replace_current_entry(should_replace_current_entry) {
}
- int render_view_id;
+ int render_frame_id;
GlobalRequestID global_request_id;
bool is_transfer;
std::vector<GURL> transfer_url_chain;
Referrer referrer;
PageTransition page_transition;
- int64 frame_id;
bool should_replace_current_entry;
};
void OnCrossSiteResponseHelper(const CrossSiteResponseParams& params) {
- RenderViewHostImpl* rvh =
- RenderViewHostImpl::FromID(params.global_request_id.child_id,
- params.render_view_id);
- if (rvh) {
- rvh->OnCrossSiteResponse(
- params.global_request_id, params.is_transfer,
+ scoped_ptr<CrossSiteTransferringRequest> cross_site_transferring_request;
+ if (params.is_transfer) {
+ cross_site_transferring_request.reset(new CrossSiteTransferringRequest(
+ params.global_request_id));
+ }
+
+ RenderFrameHostImpl* rfh =
+ RenderFrameHostImpl::FromID(params.global_request_id.child_id,
+ params.render_frame_id);
+ if (rfh) {
+ rfh->OnCrossSiteResponse(
+ params.global_request_id, cross_site_transferring_request.Pass(),
params.transfer_url_chain, params.referrer,
- params.page_transition, params.frame_id,
- params.should_replace_current_entry);
+ params.page_transition, params.should_replace_current_entry);
+ } else if (leak_requests_for_testing_ && cross_site_transferring_request) {
+ // Some unit tests expect requests to be leaked in this case, so they can
+ // pass them along manually.
+ cross_site_transferring_request->ReleaseRequest();
}
}
+bool CheckNavigationPolicyOnUI(GURL url, int process_id, int render_frame_id) {
+ RenderFrameHostImpl* rfh =
+ RenderFrameHostImpl::FromID(process_id, render_frame_id);
+ if (!rfh)
+ return false;
+
+ // TODO(nasko): This check is very simplistic and is used temporarily only
+ // for --site-per-process. It should be updated to match the check performed
+ // by RenderFrameHostManager::UpdateStateForNavigate.
+ return !SiteInstance::IsSameWebSite(
+ rfh->GetSiteInstance()->GetBrowserContext(),
+ rfh->GetSiteInstance()->GetSiteURL(), url);
+}
+
} // namespace
CrossSiteResourceHandler::CrossSiteResourceHandler(
@@ -80,7 +108,7 @@ CrossSiteResourceHandler::CrossSiteResourceHandler(
in_cross_site_transition_(false),
completed_during_transition_(false),
did_defer_(false),
- completed_status_() {
+ weak_ptr_factory_(this) {
}
CrossSiteResourceHandler::~CrossSiteResourceHandler() {
@@ -89,18 +117,20 @@ CrossSiteResourceHandler::~CrossSiteResourceHandler() {
}
bool CrossSiteResourceHandler::OnRequestRedirected(
- int request_id,
const GURL& new_url,
ResourceResponse* response,
bool* defer) {
+ // Top-level requests change their cookie first-party URL on redirects, while
+ // subframes retain the parent's value.
+ if (GetRequestInfo()->GetResourceType() == ResourceType::MAIN_FRAME)
+ request()->set_first_party_for_cookies(new_url);
+
// We should not have started the transition before being redirected.
DCHECK(!in_cross_site_transition_);
- return next_handler_->OnRequestRedirected(
- request_id, new_url, response, defer);
+ return next_handler_->OnRequestRedirected(new_url, response, defer);
}
bool CrossSiteResourceHandler::OnResponseStarted(
- int request_id,
ResourceResponse* response,
bool* defer) {
// At this point, we know that the response is safe to send back to the
@@ -119,6 +149,18 @@ bool CrossSiteResourceHandler::OnResponseStarted(
bool should_transfer =
GetContentClient()->browser()->ShouldSwapProcessesForRedirect(
info->GetContext(), request()->original_url(), request()->url());
+
+ // When the --site-per-process flag is passed, we transfer processes for
+ // cross-site navigations. This is skipped if a transfer is already required
+ // or for WebUI processes for now, since pages like the NTP host multiple
+ // cross-site WebUI iframes.
+ if (!should_transfer &&
+ CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess) &&
+ !ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
+ info->GetChildID())) {
+ return DeferForNavigationPolicyCheck(info, response, defer);
+ }
+
bool swap_needed = should_transfer ||
CrossSiteRequestManager::GetInstance()->
HasPendingCrossSiteRequest(info->GetChildID(), info->GetRouteID());
@@ -129,37 +171,46 @@ bool CrossSiteResourceHandler::OnResponseStarted(
//
// Similarly, HTTP 204 (No Content) responses leave us showing the previous
// page. We should allow the navigation to finish without running the unload
- // handler or swapping in the pending RenderViewHost.
+ // handler or swapping in the pending RenderFrameHost.
//
- // In both cases, any pending RenderViewHost (if one was created for this
+ // In both cases, any pending RenderFrameHost (if one was created for this
// navigation) will stick around until the next cross-site navigation, since
// we are unable to tell when to destroy it.
// See RenderFrameHostManager::RendererAbortedProvisionalLoad.
- if (!swap_needed || info->IsDownload() ||
+ //
+ // TODO(davidben): Unify IsDownload() and is_stream(). Several places need to
+ // check for both and remembering about streams is error-prone.
+ if (!swap_needed || info->IsDownload() || info->is_stream() ||
(response->head.headers.get() &&
response->head.headers->response_code() == 204)) {
- return next_handler_->OnResponseStarted(request_id, response, defer);
+ return next_handler_->OnResponseStarted(response, defer);
}
// Now that we know a swap is needed and we have something to commit, we
// pause to let the UI thread run the unload handler of the previous page
// and set up a transfer if needed.
- StartCrossSiteTransition(request_id, response, should_transfer);
+ StartCrossSiteTransition(response, should_transfer);
// Defer loading until after the onunload event handler has run.
- did_defer_ = *defer = true;
+ *defer = true;
+ OnDidDefer();
return true;
}
-bool CrossSiteResourceHandler::OnReadCompleted(int request_id,
- int bytes_read,
- bool* defer) {
+void CrossSiteResourceHandler::ResumeOrTransfer(bool is_transfer) {
+ if (is_transfer) {
+ StartCrossSiteTransition(response_, is_transfer);
+ } else {
+ ResumeResponse();
+ }
+}
+
+bool CrossSiteResourceHandler::OnReadCompleted(int bytes_read, bool* defer) {
CHECK(!in_cross_site_transition_);
- return next_handler_->OnReadCompleted(request_id, bytes_read, defer);
+ return next_handler_->OnReadCompleted(bytes_read, defer);
}
void CrossSiteResourceHandler::OnResponseCompleted(
- int request_id,
const net::URLRequestStatus& status,
const std::string& security_info,
bool* defer) {
@@ -172,8 +223,7 @@ void CrossSiteResourceHandler::OnResponseCompleted(
status.status() != net::URLRequestStatus::FAILED ||
!CrossSiteRequestManager::GetInstance()->HasPendingCrossSiteRequest(
info->GetChildID(), info->GetRouteID())) {
- next_handler_->OnResponseCompleted(request_id, status,
- security_info, defer);
+ next_handler_->OnResponseCompleted(status, security_info, defer);
return;
}
@@ -181,7 +231,7 @@ void CrossSiteResourceHandler::OnResponseCompleted(
// so that the error message (e.g., 404) can be displayed to the user.
// Also continue with the logic below to remember that we completed
// during the cross-site transition.
- StartCrossSiteTransition(request_id, NULL, false);
+ StartCrossSiteTransition(NULL, false);
}
// We have to buffer the call until after the transition completes.
@@ -191,15 +241,14 @@ void CrossSiteResourceHandler::OnResponseCompleted(
// Defer to tell RDH not to notify the world or clean up the pending request.
// We will do so in ResumeResponse.
- did_defer_ = true;
*defer = true;
+ OnDidDefer();
}
// We can now send the response to the new renderer, which will cause
// WebContentsImpl to swap in the new renderer and destroy the old one.
void CrossSiteResourceHandler::ResumeResponse() {
DCHECK(request());
- DCHECK(in_cross_site_transition_);
in_cross_site_transition_ = false;
ResourceRequestInfoImpl* info = GetRequestInfo();
@@ -207,8 +256,7 @@ void CrossSiteResourceHandler::ResumeResponse() {
// Send OnResponseStarted to the new renderer.
DCHECK(response_);
bool defer = false;
- if (!next_handler_->OnResponseStarted(info->GetRequestID(), response_.get(),
- &defer)) {
+ if (!next_handler_->OnResponseStarted(response_.get(), &defer)) {
controller()->Cancel();
} else if (!defer) {
// Unpause the request to resume reading. Any further reads will be
@@ -224,8 +272,7 @@ void CrossSiteResourceHandler::ResumeResponse() {
// event handler.
if (completed_during_transition_) {
bool defer = false;
- next_handler_->OnResponseCompleted(info->GetRequestID(),
- completed_status_,
+ next_handler_->OnResponseCompleted(completed_status_,
completed_security_info_,
&defer);
if (!defer)
@@ -233,10 +280,15 @@ void CrossSiteResourceHandler::ResumeResponse() {
}
}
-// Prepare to render the cross-site response in a new RenderViewHost, by
-// telling the old RenderViewHost to run its onunload handler.
+// static
+void CrossSiteResourceHandler::SetLeakRequestsForTesting(
+ bool leak_requests_for_testing) {
+ leak_requests_for_testing_ = leak_requests_for_testing;
+}
+
+// Prepare to render the cross-site response in a new RenderFrameHost, by
+// telling the old RenderFrameHost to run its onunload handler.
void CrossSiteResourceHandler::StartCrossSiteTransition(
- int request_id,
ResourceResponse* response,
bool should_transfer) {
in_cross_site_transition_ = true;
@@ -247,7 +299,6 @@ void CrossSiteResourceHandler::StartCrossSiteTransition(
ResourceRequestInfoImpl* info = GetRequestInfo();
info->set_cross_site_handler(this);
- DCHECK_EQ(request_id, info->GetRequestID());
GlobalRequestID global_id(info->GetChildID(), info->GetRequestID());
// Tell the contents responsible for this request that a cross-site response
@@ -258,37 +309,72 @@ void CrossSiteResourceHandler::StartCrossSiteTransition(
// occurred, plus the destination URL at the end.
std::vector<GURL> transfer_url_chain;
Referrer referrer;
- int frame_id = -1;
+ int render_frame_id = info->GetRenderFrameID();
if (should_transfer) {
transfer_url_chain = request()->url_chain();
referrer = Referrer(GURL(request()->referrer()), info->GetReferrerPolicy());
- frame_id = info->GetFrameID();
- appcache::AppCacheInterceptor::PrepareForCrossSiteTransfer(
+ AppCacheInterceptor::PrepareForCrossSiteTransfer(
request(), global_id.child_id);
- ResourceDispatcherHostImpl::Get()->MarkAsTransferredNavigation(
- global_id, transfer_url_chain.front());
+ ResourceDispatcherHostImpl::Get()->MarkAsTransferredNavigation(global_id);
}
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(
&OnCrossSiteResponseHelper,
- CrossSiteResponseParams(info->GetRouteID(),
+ CrossSiteResponseParams(render_frame_id,
global_id,
should_transfer,
transfer_url_chain,
referrer,
info->GetPageTransition(),
- frame_id,
info->should_replace_current_entry())));
}
+bool CrossSiteResourceHandler::DeferForNavigationPolicyCheck(
+ ResourceRequestInfoImpl* info,
+ ResourceResponse* response,
+ bool* defer) {
+ // Store the response_ object internally, since the navigation is deferred
+ // regardless of whether it will be a transfer or not.
+ response_ = response;
+
+ // Always defer the navigation to the UI thread to make a policy decision.
+ // It will send the result back to the IO thread to either resume or
+ // transfer it to a new renderer.
+ // TODO(nasko): If the UI thread result is that transfer is required, the
+ // IO thread will defer to the UI thread again through
+ // StartCrossSiteTransition. This is unnecessary and the policy check on the
+ // UI thread should be refactored to avoid the extra hop.
+ BrowserThread::PostTaskAndReplyWithResult(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&CheckNavigationPolicyOnUI,
+ request()->url(),
+ info->GetChildID(),
+ info->GetRenderFrameID()),
+ base::Bind(&CrossSiteResourceHandler::ResumeOrTransfer,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ // Defer loading until it is known whether the navigation will transfer
+ // to a new process or continue in the existing one.
+ *defer = true;
+ OnDidDefer();
+ return true;
+}
+
void CrossSiteResourceHandler::ResumeIfDeferred() {
if (did_defer_) {
+ request()->LogUnblocked();
did_defer_ = false;
controller()->Resume();
}
}
+void CrossSiteResourceHandler::OnDidDefer() {
+ did_defer_ = true;
+ request()->LogBlockedBy("CrossSiteResourceHandler");
+}
+
} // namespace content
diff --git a/chromium/content/browser/loader/cross_site_resource_handler.h b/chromium/content/browser/loader/cross_site_resource_handler.h
index 29e3f5acdbc..257af02c01b 100644
--- a/chromium/content/browser/loader/cross_site_resource_handler.h
+++ b/chromium/content/browser/loader/cross_site_resource_handler.h
@@ -6,7 +6,9 @@
#define CONTENT_BROWSER_LOADER_CROSS_SITE_RESOURCE_HANDLER_H_
#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
#include "content/browser/loader/layered_resource_handler.h"
+#include "content/common/content_export.h"
#include "net/url_request/url_request_status.h"
namespace net {
@@ -27,18 +29,14 @@ class CrossSiteResourceHandler : public LayeredResourceHandler {
virtual ~CrossSiteResourceHandler();
// ResourceHandler implementation:
- virtual bool OnRequestRedirected(int request_id,
- const GURL& new_url,
+ virtual bool OnRequestRedirected(const GURL& new_url,
ResourceResponse* response,
bool* defer) OVERRIDE;
- virtual bool OnResponseStarted(int request_id,
- ResourceResponse* response,
+ virtual bool OnResponseStarted(ResourceResponse* response,
bool* defer) OVERRIDE;
- virtual bool OnReadCompleted(int request_id,
- int bytes_read,
+ virtual bool OnReadCompleted(int bytes_read,
bool* defer) OVERRIDE;
- virtual void OnResponseCompleted(int request_id,
- const net::URLRequestStatus& status,
+ virtual void OnResponseCompleted(const net::URLRequestStatus& status,
const std::string& security_info,
bool* defer) OVERRIDE;
@@ -46,15 +44,30 @@ class CrossSiteResourceHandler : public LayeredResourceHandler {
// WebContentsImpl to swap in the new renderer and destroy the old one.
void ResumeResponse();
+ // When set to true, requests are leaked when they can't be passed to a
+ // RenderViewHost, for unit tests.
+ CONTENT_EXPORT static void SetLeakRequestsForTesting(
+ bool leak_requests_for_testing);
+
private:
// Prepare to render the cross-site response in a new RenderViewHost, by
// telling the old RenderViewHost to run its onunload handler.
- void StartCrossSiteTransition(int request_id,
- ResourceResponse* response,
+ void StartCrossSiteTransition(ResourceResponse* response,
bool should_transfer);
+ // Defer the navigation to the UI thread to check whether transfer is required
+ // or not. Currently only used in --site-per-process.
+ bool DeferForNavigationPolicyCheck(ResourceRequestInfoImpl* info,
+ ResourceResponse* response,
+ bool* defer);
+
+ void ResumeOrTransfer(bool is_transfer);
void ResumeIfDeferred();
+ // Called when about to defer a request. Sets |did_defer_| and logs the
+ // defferral
+ void OnDidDefer();
+
bool has_started_response_;
bool in_cross_site_transition_;
bool completed_during_transition_;
@@ -63,6 +76,12 @@ class CrossSiteResourceHandler : public LayeredResourceHandler {
std::string completed_security_info_;
scoped_refptr<ResourceResponse> response_;
+ // TODO(nasko): WeakPtr is needed in --site-per-process, since all navigations
+ // are deferred to the UI thread and come back to IO thread via
+ // PostTaskAndReplyWithResult. If a transfer is needed, it goes back to the UI
+ // thread. This can be removed once the code is changed to only do one hop.
+ base::WeakPtrFactory<CrossSiteResourceHandler> weak_ptr_factory_;
+
DISALLOW_COPY_AND_ASSIGN(CrossSiteResourceHandler);
};
diff --git a/chromium/content/browser/loader/detachable_resource_handler.cc b/chromium/content/browser/loader/detachable_resource_handler.cc
index 7cc7b09d0ce..b8ef55d4fff 100644
--- a/chromium/content/browser/loader/detachable_resource_handler.cc
+++ b/chromium/content/browser/loader/detachable_resource_handler.cc
@@ -44,8 +44,7 @@ void DetachableResourceHandler::Detach() {
net::URLRequestStatus status(net::URLRequestStatus::CANCELED,
net::ERR_ABORTED);
bool defer_ignored = false;
- next_handler_->OnResponseCompleted(GetRequestID(), status, std::string(),
- &defer_ignored);
+ next_handler_->OnResponseCompleted(status, std::string(), &defer_ignored);
DCHECK(!defer_ignored);
// If |next_handler_| were to defer its shutdown in OnResponseCompleted,
// this would destroy it anyway. Fortunately, AsyncResourceHandler never
@@ -83,17 +82,14 @@ void DetachableResourceHandler::SetController(ResourceController* controller) {
next_handler_->SetController(this);
}
-bool DetachableResourceHandler::OnUploadProgress(int request_id,
- uint64 position,
- uint64 size) {
+bool DetachableResourceHandler::OnUploadProgress(uint64 position, uint64 size) {
if (!next_handler_)
return true;
- return next_handler_->OnUploadProgress(request_id, position, size);
+ return next_handler_->OnUploadProgress(position, size);
}
-bool DetachableResourceHandler::OnRequestRedirected(int request_id,
- const GURL& url,
+bool DetachableResourceHandler::OnRequestRedirected(const GURL& url,
ResourceResponse* response,
bool* defer) {
DCHECK(!is_deferred_);
@@ -101,14 +97,12 @@ bool DetachableResourceHandler::OnRequestRedirected(int request_id,
if (!next_handler_)
return true;
- bool ret = next_handler_->OnRequestRedirected(request_id, url, response,
- &is_deferred_);
+ bool ret = next_handler_->OnRequestRedirected(url, response, &is_deferred_);
*defer = is_deferred_;
return ret;
}
-bool DetachableResourceHandler::OnResponseStarted(int request_id,
- ResourceResponse* response,
+bool DetachableResourceHandler::OnResponseStarted(ResourceResponse* response,
bool* defer) {
DCHECK(!is_deferred_);
@@ -116,25 +110,36 @@ bool DetachableResourceHandler::OnResponseStarted(int request_id,
return true;
bool ret =
- next_handler_->OnResponseStarted(request_id, response, &is_deferred_);
+ next_handler_->OnResponseStarted(response, &is_deferred_);
*defer = is_deferred_;
return ret;
}
-bool DetachableResourceHandler::OnWillStart(int request_id, const GURL& url,
- bool* defer) {
+bool DetachableResourceHandler::OnWillStart(const GURL& url, bool* defer) {
DCHECK(!is_deferred_);
if (!next_handler_)
return true;
- bool ret = next_handler_->OnWillStart(request_id, url, &is_deferred_);
+ bool ret = next_handler_->OnWillStart(url, &is_deferred_);
*defer = is_deferred_;
return ret;
}
-bool DetachableResourceHandler::OnWillRead(int request_id,
- scoped_refptr<net::IOBuffer>* buf,
+bool DetachableResourceHandler::OnBeforeNetworkStart(const GURL& url,
+ bool* defer) {
+ DCHECK(!is_deferred_);
+
+ if (!next_handler_)
+ return true;
+
+ bool ret =
+ next_handler_->OnBeforeNetworkStart(url, &is_deferred_);
+ *defer = is_deferred_;
+ return ret;
+}
+
+bool DetachableResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf,
int* buf_size,
int min_size) {
if (!next_handler_) {
@@ -146,24 +151,22 @@ bool DetachableResourceHandler::OnWillRead(int request_id,
return true;
}
- return next_handler_->OnWillRead(request_id, buf, buf_size, min_size);
+ return next_handler_->OnWillRead(buf, buf_size, min_size);
}
-bool DetachableResourceHandler::OnReadCompleted(int request_id, int bytes_read,
- bool* defer) {
+bool DetachableResourceHandler::OnReadCompleted(int bytes_read, bool* defer) {
DCHECK(!is_deferred_);
if (!next_handler_)
return true;
bool ret =
- next_handler_->OnReadCompleted(request_id, bytes_read, &is_deferred_);
+ next_handler_->OnReadCompleted(bytes_read, &is_deferred_);
*defer = is_deferred_;
return ret;
}
void DetachableResourceHandler::OnResponseCompleted(
- int request_id,
const net::URLRequestStatus& status,
const std::string& security_info,
bool* defer) {
@@ -175,17 +178,15 @@ void DetachableResourceHandler::OnResponseCompleted(
is_finished_ = true;
- next_handler_->OnResponseCompleted(request_id, status, security_info,
- &is_deferred_);
+ next_handler_->OnResponseCompleted(status, security_info, &is_deferred_);
*defer = is_deferred_;
}
-void DetachableResourceHandler::OnDataDownloaded(int request_id,
- int bytes_downloaded) {
+void DetachableResourceHandler::OnDataDownloaded(int bytes_downloaded) {
if (!next_handler_)
return;
- next_handler_->OnDataDownloaded(request_id, bytes_downloaded);
+ next_handler_->OnDataDownloaded(bytes_downloaded);
}
void DetachableResourceHandler::Resume() {
diff --git a/chromium/content/browser/loader/detachable_resource_handler.h b/chromium/content/browser/loader/detachable_resource_handler.h
index 990084d93b8..9a685701a9d 100644
--- a/chromium/content/browser/loader/detachable_resource_handler.h
+++ b/chromium/content/browser/loader/detachable_resource_handler.h
@@ -48,27 +48,22 @@ class DetachableResourceHandler : public ResourceHandler,
// ResourceHandler implementation:
virtual void SetController(ResourceController* controller) OVERRIDE;
- virtual bool OnUploadProgress(int request_id, uint64 position,
- uint64 size) OVERRIDE;
- virtual bool OnRequestRedirected(int request_id, const GURL& url,
+ virtual bool OnUploadProgress(uint64 position, uint64 size) OVERRIDE;
+ virtual bool OnRequestRedirected(const GURL& url,
ResourceResponse* response,
bool* defer) OVERRIDE;
- virtual bool OnResponseStarted(int request_id,
- ResourceResponse* response,
+ virtual bool OnResponseStarted(ResourceResponse* response,
bool* defer) OVERRIDE;
- virtual bool OnWillStart(int request_id, const GURL& url,
- bool* defer) OVERRIDE;
- virtual bool OnWillRead(int request_id,
- scoped_refptr<net::IOBuffer>* buf,
+ virtual bool OnWillStart(const GURL& url, bool* defer) OVERRIDE;
+ virtual bool OnBeforeNetworkStart(const GURL& url, bool* defer) OVERRIDE;
+ virtual bool OnWillRead(scoped_refptr<net::IOBuffer>* buf,
int* buf_size,
int min_size) OVERRIDE;
- virtual bool OnReadCompleted(int request_id, int bytes_read,
- bool* defer) OVERRIDE;
- virtual void OnResponseCompleted(int request_id,
- const net::URLRequestStatus& status,
+ virtual bool OnReadCompleted(int bytes_read, bool* defer) OVERRIDE;
+ virtual void OnResponseCompleted(const net::URLRequestStatus& status,
const std::string& security_info,
bool* defer) OVERRIDE;
- virtual void OnDataDownloaded(int request_id, int bytes_downloaded) OVERRIDE;
+ virtual void OnDataDownloaded(int bytes_downloaded) OVERRIDE;
// ResourceController implementation:
virtual void Resume() OVERRIDE;
diff --git a/chromium/content/browser/loader/layered_resource_handler.cc b/chromium/content/browser/loader/layered_resource_handler.cc
index 30d3180ef3c..1c65820f610 100644
--- a/chromium/content/browser/loader/layered_resource_handler.cc
+++ b/chromium/content/browser/loader/layered_resource_handler.cc
@@ -29,60 +29,60 @@ void LayeredResourceHandler::SetController(ResourceController* controller) {
next_handler_->SetController(controller);
}
-bool LayeredResourceHandler::OnUploadProgress(int request_id, uint64 position,
+bool LayeredResourceHandler::OnUploadProgress(uint64 position,
uint64 size) {
DCHECK(next_handler_.get());
- return next_handler_->OnUploadProgress(request_id, position, size);
+ return next_handler_->OnUploadProgress(position, size);
}
-bool LayeredResourceHandler::OnRequestRedirected(int request_id,
- const GURL& url,
+bool LayeredResourceHandler::OnRequestRedirected(const GURL& url,
ResourceResponse* response,
bool* defer) {
DCHECK(next_handler_.get());
- return next_handler_->OnRequestRedirected(request_id, url, response, defer);
+ return next_handler_->OnRequestRedirected(url, response, defer);
}
-bool LayeredResourceHandler::OnResponseStarted(int request_id,
- ResourceResponse* response,
+bool LayeredResourceHandler::OnResponseStarted(ResourceResponse* response,
bool* defer) {
DCHECK(next_handler_.get());
- return next_handler_->OnResponseStarted(request_id, response, defer);
+ return next_handler_->OnResponseStarted(response, defer);
}
-bool LayeredResourceHandler::OnWillStart(int request_id, const GURL& url,
+bool LayeredResourceHandler::OnWillStart(const GURL& url,
bool* defer) {
DCHECK(next_handler_.get());
- return next_handler_->OnWillStart(request_id, url, defer);
+ return next_handler_->OnWillStart(url, defer);
}
-bool LayeredResourceHandler::OnWillRead(int request_id,
- scoped_refptr<net::IOBuffer>* buf,
+bool LayeredResourceHandler::OnBeforeNetworkStart(const GURL& url,
+ bool* defer) {
+ DCHECK(next_handler_.get());
+ return next_handler_->OnBeforeNetworkStart(url, defer);
+}
+
+bool LayeredResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf,
int* buf_size,
int min_size) {
DCHECK(next_handler_.get());
- return next_handler_->OnWillRead(request_id, buf, buf_size, min_size);
+ return next_handler_->OnWillRead(buf, buf_size, min_size);
}
-bool LayeredResourceHandler::OnReadCompleted(int request_id, int bytes_read,
- bool* defer) {
+bool LayeredResourceHandler::OnReadCompleted(int bytes_read, bool* defer) {
DCHECK(next_handler_.get());
- return next_handler_->OnReadCompleted(request_id, bytes_read, defer);
+ return next_handler_->OnReadCompleted(bytes_read, defer);
}
void LayeredResourceHandler::OnResponseCompleted(
- int request_id,
const net::URLRequestStatus& status,
const std::string& security_info,
bool* defer) {
DCHECK(next_handler_.get());
- next_handler_->OnResponseCompleted(request_id, status, security_info, defer);
+ next_handler_->OnResponseCompleted(status, security_info, defer);
}
-void LayeredResourceHandler::OnDataDownloaded(int request_id,
- int bytes_downloaded) {
+void LayeredResourceHandler::OnDataDownloaded(int bytes_downloaded) {
DCHECK(next_handler_.get());
- next_handler_->OnDataDownloaded(request_id, bytes_downloaded);
+ next_handler_->OnDataDownloaded(bytes_downloaded);
}
} // namespace content
diff --git a/chromium/content/browser/loader/layered_resource_handler.h b/chromium/content/browser/loader/layered_resource_handler.h
index e0778958d07..dc6de2314dc 100644
--- a/chromium/content/browser/loader/layered_resource_handler.h
+++ b/chromium/content/browser/loader/layered_resource_handler.h
@@ -25,27 +25,23 @@ class CONTENT_EXPORT LayeredResourceHandler : public ResourceHandler {
// ResourceHandler implementation:
virtual void SetController(ResourceController* controller) OVERRIDE;
- virtual bool OnUploadProgress(int request_id, uint64 position,
- uint64 size) OVERRIDE;
- virtual bool OnRequestRedirected(int request_id, const GURL& url,
+ virtual bool OnUploadProgress(uint64 position, uint64 size) OVERRIDE;
+ virtual bool OnRequestRedirected(const GURL& url,
ResourceResponse* response,
bool* defer) OVERRIDE;
- virtual bool OnResponseStarted(int request_id,
- ResourceResponse* response,
+ virtual bool OnResponseStarted(ResourceResponse* response,
bool* defer) OVERRIDE;
- virtual bool OnWillStart(int request_id, const GURL& url,
- bool* defer) OVERRIDE;
- virtual bool OnWillRead(int request_id,
- scoped_refptr<net::IOBuffer>* buf,
+ virtual bool OnWillStart(const GURL& url, bool* defer) OVERRIDE;
+ virtual bool OnBeforeNetworkStart(const GURL& url, bool* defer) OVERRIDE;
+ virtual bool OnWillRead(scoped_refptr<net::IOBuffer>* buf,
int* buf_size,
int min_size) OVERRIDE;
- virtual bool OnReadCompleted(int request_id, int bytes_read,
+ virtual bool OnReadCompleted(int bytes_read,
bool* defer) OVERRIDE;
- virtual void OnResponseCompleted(int request_id,
- const net::URLRequestStatus& status,
+ virtual void OnResponseCompleted(const net::URLRequestStatus& status,
const std::string& security_info,
bool* defer) OVERRIDE;
- virtual void OnDataDownloaded(int request_id, int bytes_downloaded) OVERRIDE;
+ virtual void OnDataDownloaded(int bytes_downloaded) OVERRIDE;
scoped_ptr<ResourceHandler> next_handler_;
};
diff --git a/chromium/content/browser/loader/offline_policy.cc b/chromium/content/browser/loader/offline_policy.cc
deleted file mode 100644
index 9c9361f5258..00000000000
--- a/chromium/content/browser/loader/offline_policy.cc
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/loader/offline_policy.h"
-
-#include "base/command_line.h"
-#include "base/metrics/histogram.h"
-#include "content/public/common/content_switches.h"
-#include "net/base/load_flags.h"
-#include "net/http/http_response_info.h"
-#include "net/url_request/url_request.h"
-
-namespace content {
-
-OfflinePolicy::OfflinePolicy()
- : enabled_(CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kEnableOfflineCacheAccess)),
- state_(INIT),
- resource_loads_initiated_(0),
- resource_loads_successfully_started_(0) {}
-
-OfflinePolicy::~OfflinePolicy() {
- RecordAndResetStats();
-}
-
-void OfflinePolicy::RecordAndResetStats() {
- if (enabled_ && OFFLINE == state_ && 0 != resource_loads_initiated_) {
- UMA_HISTOGRAM_PERCENTAGE(
- "OfflinePolicy.SuccessfulResourceLoadPercentage",
- (resource_loads_successfully_started_ * 100 /
- resource_loads_initiated_));
- }
- resource_loads_initiated_ = 0;
- resource_loads_successfully_started_ = 0;
-}
-
-int OfflinePolicy::GetAdditionalLoadFlags(int current_flags,
- bool reset_state) {
- // Don't do anything if offline mode is disabled.
- if (!enabled_)
- return 0;
-
- if (reset_state) {
- RecordAndResetStats();
- state_ = INIT;
- }
-
- ++resource_loads_initiated_;
-
- // If a consumer has requested something contradictory, it wins; we
- // don't modify the load flags.
- if (current_flags &
- (net::LOAD_BYPASS_CACHE | net::LOAD_PREFERRING_CACHE |
- net::LOAD_ONLY_FROM_CACHE | net::LOAD_FROM_CACHE_IF_OFFLINE |
- net::LOAD_DISABLE_CACHE)) {
- return 0;
- }
-
- switch(state_) {
- case INIT:
- return net::LOAD_FROM_CACHE_IF_OFFLINE;
- case ONLINE:
- return 0;
- case OFFLINE:
- return net::LOAD_ONLY_FROM_CACHE;
- }
- NOTREACHED();
- return 0;
-}
-
-void OfflinePolicy::UpdateStateForSuccessfullyStartedRequest(
- const net::HttpResponseInfo& response_info) {
- // Don't do anything if offline mode is disabled.
- if (!enabled_)
- return;
-
- // If we get here, we're going to be providing some amount of information
- // to the renderer.
- ++resource_loads_successfully_started_;
-
- if (state_ != INIT)
- // We've already made the decision for the rest of this set
- // of navigations.
- return;
-
- if (response_info.server_data_unavailable) {
- state_ = OFFLINE;
- } else if (response_info.network_accessed) {
- // If we got the response from the network or validated it as part
- // of this request, that means our connection to the host is working.
- state_ = ONLINE;
- }
-}
-
-} // namespace content
diff --git a/chromium/content/browser/loader/offline_policy.h b/chromium/content/browser/loader/offline_policy.h
deleted file mode 100644
index 975d4adb170..00000000000
--- a/chromium/content/browser/loader/offline_policy.h
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_LOADER_OFFLINE_POLICY
-#define CONTENT_BROWSER_LOADER_OFFLINE_POLICY
-
-#include <map>
-
-#include "base/basictypes.h"
-#include "content/common/content_export.h"
-
-struct ResourceHostMsg_Request;
-
-namespace net {
-class HttpResponseInfo;
-class URLRequest;
-}
-
-namespace content {
-
-// This class controls under what conditions resources will be fetched
-// from cache even if stale rather than from the network. For example,
-// one policy would be that if requests for a particular route (e.g. "tab")
-// is unable to reach the server, other requests made with the same route
-// can be loaded from cache without first requiring a network timeout.
-//
-// There is a single OfflinePolicy object per user navigation unit
-// (generally a tab).
-class CONTENT_EXPORT OfflinePolicy {
- public:
- OfflinePolicy();
- ~OfflinePolicy();
-
- // Return any additional load flags to be ORed for a request from
- // this route with the given |resource_type|. |reset_state| indicates
- // that this request should reinitialized the internal state for this
- // policy object (e.g. in the case of a main frame load).
- int GetAdditionalLoadFlags(int current_flags, bool reset_state);
-
- // Incorporate online/offline information from a successfully started request.
- void UpdateStateForSuccessfullyStartedRequest(
- const net::HttpResponseInfo& response_info);
-
-private:
- enum State { INIT, ONLINE, OFFLINE };
-
- void RecordAndResetStats();
-
- bool enabled_;
- State state_;
- int resource_loads_initiated_;
- int resource_loads_successfully_started_;
-
- DISALLOW_COPY_AND_ASSIGN(OfflinePolicy);
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_LOADER_OFFLINE_POLICY
diff --git a/chromium/content/browser/loader/offline_policy_unittest.cc b/chromium/content/browser/loader/offline_policy_unittest.cc
deleted file mode 100644
index 40243c8f04b..00000000000
--- a/chromium/content/browser/loader/offline_policy_unittest.cc
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/loader/offline_policy.h"
-
-#include "base/command_line.h"
-#include "content/public/common/content_switches.h"
-#include "net/base/load_flags.h"
-#include "net/http/http_response_info.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "webkit/common/resource_type.h"
-
-namespace content {
-
-class OfflinePolicyTest : public testing::Test {
- protected:
- virtual void SetUp() {
- CommandLine::ForCurrentProcess()->AppendSwitch(
- switches::kEnableOfflineCacheAccess);
- policy_ = new OfflinePolicy;
- }
-
- virtual void TearDown() {
- delete policy_;
- policy_ = NULL;
- }
-
- OfflinePolicy* policy_;
-};
-
-// Confirm that the initial state of an offline object is to return
-// LOAD_FROM_CACHE_IF_OFFLINE until it gets changed.
-TEST_F(OfflinePolicyTest, InitialState) {
- // Two loads without any reset, no UpdateStateForSuccessfullyStartedRequest.
- EXPECT_EQ(net::LOAD_FROM_CACHE_IF_OFFLINE,
- policy_->GetAdditionalLoadFlags(0, true));
- EXPECT_EQ(net::LOAD_FROM_CACHE_IF_OFFLINE,
- policy_->GetAdditionalLoadFlags(0, false));
-}
-
-// Completion without any network probing doesn't change result value.
-TEST_F(OfflinePolicyTest, CompletedUncertain) {
- EXPECT_EQ(net::LOAD_FROM_CACHE_IF_OFFLINE,
- policy_->GetAdditionalLoadFlags(0, true));
- net::HttpResponseInfo response_info;
- policy_->UpdateStateForSuccessfullyStartedRequest(response_info);
- EXPECT_EQ(net::LOAD_FROM_CACHE_IF_OFFLINE,
- policy_->GetAdditionalLoadFlags(0, false));
-}
-
-// Completion with a failed network probe changes result value.
-TEST_F(OfflinePolicyTest, CompletedNoNetwork) {
- EXPECT_EQ(net::LOAD_FROM_CACHE_IF_OFFLINE,
- policy_->GetAdditionalLoadFlags(0, true));
- net::HttpResponseInfo response_info;
- response_info.server_data_unavailable = true;
- policy_->UpdateStateForSuccessfullyStartedRequest(response_info);
- EXPECT_EQ(net::LOAD_ONLY_FROM_CACHE,
- policy_->GetAdditionalLoadFlags(0, false));
-}
-
-// Completion with a successful network probe changes result value.
-TEST_F(OfflinePolicyTest, CompletedNetwork) {
- EXPECT_EQ(net::LOAD_FROM_CACHE_IF_OFFLINE,
- policy_->GetAdditionalLoadFlags(0, true));
- net::HttpResponseInfo response_info;
- response_info.network_accessed = true;
- policy_->UpdateStateForSuccessfullyStartedRequest(response_info);
- EXPECT_EQ(0, policy_->GetAdditionalLoadFlags(0, false));
-}
-
-// A new navigation resets a state change.
-TEST_F(OfflinePolicyTest, NewNavigationReset) {
- EXPECT_EQ(net::LOAD_FROM_CACHE_IF_OFFLINE,
- policy_->GetAdditionalLoadFlags(0, true));
- net::HttpResponseInfo response_info;
- response_info.network_accessed = true;
- policy_->UpdateStateForSuccessfullyStartedRequest(response_info);
- EXPECT_EQ(0, policy_->GetAdditionalLoadFlags(0, false));
- EXPECT_EQ(net::LOAD_FROM_CACHE_IF_OFFLINE,
- policy_->GetAdditionalLoadFlags(0, true));
- EXPECT_EQ(net::LOAD_FROM_CACHE_IF_OFFLINE,
- policy_->GetAdditionalLoadFlags(0, false));
-}
-
-// Cache related flags inhibit the returning of any special flags.
-TEST_F(OfflinePolicyTest, ConsumerFlagOverride) {
- EXPECT_EQ(0, policy_->GetAdditionalLoadFlags(net::LOAD_BYPASS_CACHE, true));
- net::HttpResponseInfo response_info;
- response_info.server_data_unavailable = true;
- policy_->UpdateStateForSuccessfullyStartedRequest(response_info);
- EXPECT_EQ(0, policy_->GetAdditionalLoadFlags(net::LOAD_BYPASS_CACHE, false));
-}
-
-}
diff --git a/chromium/content/browser/loader/redirect_to_file_resource_handler.cc b/chromium/content/browser/loader/redirect_to_file_resource_handler.cc
index 37372afbbd9..caf36cddf39 100644
--- a/chromium/content/browser/loader/redirect_to_file_resource_handler.cc
+++ b/chromium/content/browser/loader/redirect_to_file_resource_handler.cc
@@ -5,14 +5,11 @@
#include "content/browser/loader/redirect_to_file_resource_handler.h"
#include "base/bind.h"
-#include "base/files/file_util_proxy.h"
#include "base/logging.h"
-#include "base/message_loop/message_loop_proxy.h"
-#include "base/platform_file.h"
#include "base/threading/thread_restrictions.h"
-#include "content/browser/loader/resource_dispatcher_host_impl.h"
#include "content/browser/loader/resource_request_info_impl.h"
-#include "content/public/browser/browser_thread.h"
+#include "content/browser/loader/temporary_file_stream.h"
+#include "content/public/browser/resource_controller.h"
#include "content/public/common/resource_response.h"
#include "net/base/file_stream.h"
#include "net/base/io_buffer.h"
@@ -53,57 +50,142 @@ namespace content {
static const int kInitialReadBufSize = 32768;
static const int kMaxReadBufSize = 524288;
+// A separate IO thread object to manage the lifetime of the net::FileStream and
+// the ShareableFileReference. When the handler is destroyed, it asynchronously
+// closes net::FileStream after all pending writes complete. Only after the
+// stream is closed is the ShareableFileReference released, to ensure the
+// temporary is not deleted before it is closed.
+class RedirectToFileResourceHandler::Writer {
+ public:
+ Writer(RedirectToFileResourceHandler* handler,
+ scoped_ptr<net::FileStream> file_stream,
+ ShareableFileReference* deletable_file)
+ : handler_(handler),
+ file_stream_(file_stream.Pass()),
+ is_writing_(false),
+ deletable_file_(deletable_file) {
+ DCHECK(!deletable_file_->path().empty());
+ }
+
+ bool is_writing() const { return is_writing_; }
+ const base::FilePath& path() const { return deletable_file_->path(); }
+
+ int Write(net::IOBuffer* buf, int buf_len) {
+ DCHECK(!is_writing_);
+ DCHECK(handler_);
+ int result = file_stream_->Write(
+ buf, buf_len,
+ base::Bind(&Writer::DidWriteToFile, base::Unretained(this)));
+ if (result == net::ERR_IO_PENDING)
+ is_writing_ = true;
+ return result;
+ }
+
+ void Close() {
+ handler_ = NULL;
+ if (!is_writing_)
+ CloseAndDelete();
+ }
+
+ private:
+ // Only DidClose can delete this.
+ ~Writer() {
+ }
+
+ void DidWriteToFile(int result) {
+ DCHECK(is_writing_);
+ is_writing_ = false;
+ if (handler_) {
+ handler_->DidWriteToFile(result);
+ } else {
+ CloseAndDelete();
+ }
+ }
+
+ void CloseAndDelete() {
+ DCHECK(!is_writing_);
+ int result = file_stream_->Close(base::Bind(&Writer::DidClose,
+ base::Unretained(this)));
+ if (result != net::ERR_IO_PENDING)
+ DidClose(result);
+ }
+
+ void DidClose(int result) {
+ delete this;
+ }
+
+ RedirectToFileResourceHandler* handler_;
+
+ scoped_ptr<net::FileStream> file_stream_;
+ bool is_writing_;
+
+ // We create a ShareableFileReference that's deletable for the temp file
+ // created as a result of the download.
+ scoped_refptr<webkit_blob::ShareableFileReference> deletable_file_;
+
+ DISALLOW_COPY_AND_ASSIGN(Writer);
+};
+
RedirectToFileResourceHandler::RedirectToFileResourceHandler(
scoped_ptr<ResourceHandler> next_handler,
- net::URLRequest* request,
- ResourceDispatcherHostImpl* host)
+ net::URLRequest* request)
: LayeredResourceHandler(request, next_handler.Pass()),
- weak_factory_(this),
- host_(host),
buf_(new net::GrowableIOBuffer()),
buf_write_pending_(false),
write_cursor_(0),
- write_callback_pending_(false),
+ writer_(NULL),
next_buffer_size_(kInitialReadBufSize),
did_defer_(false),
- completed_during_write_(false) {
+ completed_during_write_(false),
+ weak_factory_(this) {
}
RedirectToFileResourceHandler::~RedirectToFileResourceHandler() {
+ // Orphan the writer to asynchronously close and release the temporary file.
+ if (writer_) {
+ writer_->Close();
+ writer_ = NULL;
+ }
+}
+
+void RedirectToFileResourceHandler::
+ SetCreateTemporaryFileStreamFunctionForTesting(
+ const CreateTemporaryFileStreamFunction& create_temporary_file_stream) {
+ create_temporary_file_stream_ = create_temporary_file_stream;
}
bool RedirectToFileResourceHandler::OnResponseStarted(
- int request_id,
ResourceResponse* response,
bool* defer) {
if (response->head.error_code == net::OK ||
response->head.error_code == net::ERR_IO_PENDING) {
- DCHECK(deletable_file_.get() && !deletable_file_->path().empty());
- response->head.download_file_path = deletable_file_->path();
+ DCHECK(writer_);
+ response->head.download_file_path = writer_->path();
}
- return next_handler_->OnResponseStarted(request_id, response, defer);
+ return next_handler_->OnResponseStarted(response, defer);
}
-bool RedirectToFileResourceHandler::OnWillStart(int request_id,
- const GURL& url,
- bool* defer) {
- if (!deletable_file_.get()) {
- // Defer starting the request until we have created the temporary file.
- // TODO(darin): This is sub-optimal. We should not delay starting the
- // network request like this.
- did_defer_ = *defer = true;
- base::FileUtilProxy::CreateTemporary(
- BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE).get(),
- base::PLATFORM_FILE_ASYNC,
+bool RedirectToFileResourceHandler::OnWillStart(const GURL& url, bool* defer) {
+ DCHECK(!writer_);
+
+ // Defer starting the request until we have created the temporary file.
+ // TODO(darin): This is sub-optimal. We should not delay starting the
+ // network request like this.
+ will_start_url_ = url;
+ did_defer_ = *defer = true;
+ if (create_temporary_file_stream_.is_null()) {
+ CreateTemporaryFileStream(
+ base::Bind(&RedirectToFileResourceHandler::DidCreateTemporaryFile,
+ weak_factory_.GetWeakPtr()));
+ } else {
+ create_temporary_file_stream_.Run(
base::Bind(&RedirectToFileResourceHandler::DidCreateTemporaryFile,
weak_factory_.GetWeakPtr()));
- return true;
}
- return next_handler_->OnWillStart(request_id, url, defer);
+ return true;
}
bool RedirectToFileResourceHandler::OnWillRead(
- int request_id,
scoped_refptr<net::IOBuffer>* buf,
int* buf_size,
int min_size) {
@@ -122,8 +204,7 @@ bool RedirectToFileResourceHandler::OnWillRead(
return true;
}
-bool RedirectToFileResourceHandler::OnReadCompleted(int request_id,
- int bytes_read,
+bool RedirectToFileResourceHandler::OnReadCompleted(int bytes_read,
bool* defer) {
DCHECK(buf_write_pending_);
buf_write_pending_ = false;
@@ -147,11 +228,10 @@ bool RedirectToFileResourceHandler::OnReadCompleted(int request_id,
}
void RedirectToFileResourceHandler::OnResponseCompleted(
- int request_id,
const net::URLRequestStatus& status,
const std::string& security_info,
bool* defer) {
- if (write_callback_pending_) {
+ if (writer_ && writer_->is_writing()) {
completed_during_write_ = true;
completed_status_ = status;
completed_security_info_ = security_info;
@@ -159,34 +239,38 @@ void RedirectToFileResourceHandler::OnResponseCompleted(
*defer = true;
return;
}
- next_handler_->OnResponseCompleted(request_id, status, security_info, defer);
+ next_handler_->OnResponseCompleted(status, security_info, defer);
}
void RedirectToFileResourceHandler::DidCreateTemporaryFile(
- base::PlatformFileError /*error_code*/,
- base::PassPlatformFile file_handle,
- const base::FilePath& file_path) {
- deletable_file_ = ShareableFileReference::GetOrCreate(
- file_path,
- ShareableFileReference::DELETE_ON_FINAL_RELEASE,
- BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE).get());
- file_stream_.reset(
- new net::FileStream(file_handle.ReleaseValue(),
- base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_ASYNC,
- NULL));
- const ResourceRequestInfo* info = GetRequestInfo();
- host_->RegisterDownloadedTempFile(
- info->GetChildID(), info->GetRequestID(), deletable_file_.get());
- ResumeIfDeferred();
+ base::File::Error error_code,
+ scoped_ptr<net::FileStream> file_stream,
+ ShareableFileReference* deletable_file) {
+ DCHECK(!writer_);
+ if (error_code != base::File::FILE_OK) {
+ controller()->CancelWithError(net::FileErrorToNetError(error_code));
+ return;
+ }
+
+ writer_ = new Writer(this, file_stream.Pass(), deletable_file);
+
+ // Resume the request.
+ DCHECK(did_defer_);
+ bool defer = false;
+ if (!next_handler_->OnWillStart(will_start_url_, &defer)) {
+ controller()->Cancel();
+ } else if (!defer) {
+ ResumeIfDeferred();
+ } else {
+ did_defer_ = false;
+ }
+ will_start_url_ = GURL();
}
void RedirectToFileResourceHandler::DidWriteToFile(int result) {
- write_callback_pending_ = false;
- int request_id = GetRequestID();
-
bool failed = false;
if (result > 0) {
- next_handler_->OnDataDownloaded(request_id, result);
+ next_handler_->OnDataDownloaded(result);
write_cursor_ += result;
failed = !WriteMore();
} else {
@@ -194,20 +278,37 @@ void RedirectToFileResourceHandler::DidWriteToFile(int result) {
}
if (failed) {
- ResumeIfDeferred();
- } else if (completed_during_write_) {
+ DCHECK(!writer_->is_writing());
+ // TODO(davidben): Recover the error code from WriteMore or |result|, as
+ // appropriate.
+ if (completed_during_write_ && completed_status_.is_success()) {
+ // If the request successfully completed mid-write, but the write failed,
+ // convert the status to a failure for downstream.
+ completed_status_.set_status(net::URLRequestStatus::CANCELED);
+ completed_status_.set_error(net::ERR_FAILED);
+ }
+ if (!completed_during_write_)
+ controller()->CancelWithError(net::ERR_FAILED);
+ }
+
+ if (completed_during_write_ && !writer_->is_writing()) {
+ // Resume shutdown now that all data has been written to disk. Note that
+ // this should run even in the |failed| case above, otherwise a failed write
+ // leaves the handler stuck.
bool defer = false;
- next_handler_->OnResponseCompleted(request_id,
- completed_status_,
+ next_handler_->OnResponseCompleted(completed_status_,
completed_security_info_,
&defer);
- if (!defer)
+ if (!defer) {
ResumeIfDeferred();
+ } else {
+ did_defer_ = false;
+ }
}
}
bool RedirectToFileResourceHandler::WriteMore() {
- DCHECK(file_stream_.get());
+ DCHECK(writer_);
for (;;) {
if (write_cursor_ == buf_->offset()) {
// We've caught up to the network load, but it may be in the process of
@@ -220,7 +321,7 @@ bool RedirectToFileResourceHandler::WriteMore() {
}
return true;
}
- if (write_callback_pending_)
+ if (writer_->is_writing())
return true;
DCHECK(write_cursor_ < buf_->offset());
@@ -242,18 +343,12 @@ bool RedirectToFileResourceHandler::WriteMore() {
buf_.get(), buf_->StartOfBuffer() + write_cursor_);
int write_len = buf_->offset() - write_cursor_;
- int rv = file_stream_->Write(
- wrapped.get(),
- write_len,
- base::Bind(&RedirectToFileResourceHandler::DidWriteToFile,
- base::Unretained(this)));
- if (rv == net::ERR_IO_PENDING) {
- write_callback_pending_ = true;
+ int rv = writer_->Write(wrapped.get(), write_len);
+ if (rv == net::ERR_IO_PENDING)
return true;
- }
if (rv <= 0)
return false;
- next_handler_->OnDataDownloaded(GetRequestID(), rv);
+ next_handler_->OnDataDownloaded(rv);
write_cursor_ += rv;
}
}
diff --git a/chromium/content/browser/loader/redirect_to_file_resource_handler.h b/chromium/content/browser/loader/redirect_to_file_resource_handler.h
index 3d62835e888..07763f5c16a 100644
--- a/chromium/content/browser/loader/redirect_to_file_resource_handler.h
+++ b/chromium/content/browser/loader/redirect_to_file_resource_handler.h
@@ -5,14 +5,20 @@
#ifndef CONTENT_BROWSER_LOADER_REDIRECT_TO_FILE_RESOURCE_HANDLER_H_
#define CONTENT_BROWSER_LOADER_REDIRECT_TO_FILE_RESOURCE_HANDLER_H_
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#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/memory/weak_ptr.h"
-#include "base/platform_file.h"
#include "content/browser/loader/layered_resource_handler.h"
+#include "content/browser/loader/temporary_file_stream.h"
+#include "content/common/content_export.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_status.h"
+#include "url/gurl.h"
namespace net {
class FileStream;
@@ -24,49 +30,57 @@ class ShareableFileReference;
}
namespace content {
-class ResourceDispatcherHostImpl;
-// Redirects network data to a file. This is intended to be layered in front
-// of either the AsyncResourceHandler or the SyncResourceHandler.
-class RedirectToFileResourceHandler : public LayeredResourceHandler {
+// Redirects network data to a file. This is intended to be layered in front of
+// either the AsyncResourceHandler or the SyncResourceHandler. The downstream
+// resource handler does not see OnWillRead or OnReadCompleted calls. Instead,
+// the ResourceResponse contains the path to a temporary file and
+// OnDataDownloaded is called as the file downloads.
+class CONTENT_EXPORT RedirectToFileResourceHandler
+ : public LayeredResourceHandler {
public:
- RedirectToFileResourceHandler(
- scoped_ptr<ResourceHandler> next_handler,
- net::URLRequest* request,
- ResourceDispatcherHostImpl* resource_dispatcher_host);
+ typedef base::Callback<void(const CreateTemporaryFileStreamCallback&)>
+ CreateTemporaryFileStreamFunction;
+
+ // Create a RedirectToFileResourceHandler for |request| which wraps
+ // |next_handler|.
+ RedirectToFileResourceHandler(scoped_ptr<ResourceHandler> next_handler,
+ net::URLRequest* request);
virtual ~RedirectToFileResourceHandler();
- // ResourceHandler implementation:
- virtual bool OnResponseStarted(int request_id,
- ResourceResponse* response,
+ // Replace the CreateTemporaryFileStream implementation with a mocked one for
+ // testing purposes. The function should create a net::FileStream and a
+ // ShareableFileReference and then asynchronously pass them to the
+ // CreateTemporaryFileStreamCallback.
+ void SetCreateTemporaryFileStreamFunctionForTesting(
+ const CreateTemporaryFileStreamFunction& create_temporary_file_stream);
+
+ // LayeredResourceHandler implementation:
+ virtual bool OnResponseStarted(ResourceResponse* response,
bool* defer) OVERRIDE;
- virtual bool OnWillStart(int request_id,
- const GURL& url,
- bool* defer) OVERRIDE;
- virtual bool OnWillRead(int request_id,
- scoped_refptr<net::IOBuffer>* buf,
+ virtual bool OnWillStart(const GURL& url, bool* defer) OVERRIDE;
+ virtual bool OnWillRead(scoped_refptr<net::IOBuffer>* buf,
int* buf_size,
int min_size) OVERRIDE;
- virtual bool OnReadCompleted(int request_id,
- int bytes_read,
- bool* defer) OVERRIDE;
- virtual void OnResponseCompleted(int request_id,
- const net::URLRequestStatus& status,
+ virtual bool OnReadCompleted(int bytes_read, bool* defer) OVERRIDE;
+ virtual void OnResponseCompleted(const net::URLRequestStatus& status,
const std::string& security_info,
bool* defer) OVERRIDE;
private:
- void DidCreateTemporaryFile(base::PlatformFileError error_code,
- base::PassPlatformFile file_handle,
- const base::FilePath& file_path);
+ void DidCreateTemporaryFile(
+ base::File::Error error_code,
+ scoped_ptr<net::FileStream> file_stream,
+ webkit_blob::ShareableFileReference* deletable_file);
+
+ // Called by RedirectToFileResourceHandler::Writer.
void DidWriteToFile(int result);
+
bool WriteMore();
bool BufIsFull() const;
void ResumeIfDeferred();
- base::WeakPtrFactory<RedirectToFileResourceHandler> weak_factory_;
-
- ResourceDispatcherHostImpl* host_;
+ CreateTemporaryFileStreamFunction create_temporary_file_stream_;
// We allocate a single, fixed-size IO buffer (buf_) used to read from the
// network (buf_write_pending_ is true while the system is copying data into
@@ -79,8 +93,11 @@ class RedirectToFileResourceHandler : public LayeredResourceHandler {
bool buf_write_pending_;
int write_cursor_;
- scoped_ptr<net::FileStream> file_stream_;
- bool write_callback_pending_;
+ // Helper writer object which maintains references to the net::FileStream and
+ // webkit_blob::ShareableFileReference. This is maintained separately so that,
+ // on Windows, the temporary file isn't deleted until after it is closed.
+ class Writer;
+ Writer* writer_;
// |next_buffer_size_| is the size of the buffer to be allocated on the next
// OnWillRead() call. We exponentially grow the size of the buffer allocated
@@ -89,16 +106,15 @@ class RedirectToFileResourceHandler : public LayeredResourceHandler {
// was filled, up to a maximum size of 512k.
int next_buffer_size_;
- // We create a ShareableFileReference that's deletable for the temp
- // file created as a result of the download.
- scoped_refptr<webkit_blob::ShareableFileReference> deletable_file_;
-
- bool did_defer_ ;
+ bool did_defer_;
bool completed_during_write_;
+ GURL will_start_url_;
net::URLRequestStatus completed_status_;
std::string completed_security_info_;
+ base::WeakPtrFactory<RedirectToFileResourceHandler> weak_factory_;
+
DISALLOW_COPY_AND_ASSIGN(RedirectToFileResourceHandler);
};
diff --git a/chromium/content/browser/loader/resource_dispatcher_host_browsertest.cc b/chromium/content/browser/loader/resource_dispatcher_host_browsertest.cc
index 4d5d5ab598b..0858bd67e03 100644
--- a/chromium/content/browser/loader/resource_dispatcher_host_browsertest.cc
+++ b/chromium/content/browser/loader/resource_dispatcher_host_browsertest.cc
@@ -9,17 +9,15 @@
#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/browser/notification_service.h"
-#include "content/public/browser/notification_types.h"
#include "content/public/browser/web_contents.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/shell/browser/shell_content_browser_client.h"
#include "content/shell/browser/shell_network_delegate.h"
-#include "content/test/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
#include "content/test/net/url_request_failed_job.h"
#include "content/test/net/url_request_mock_http_job.h"
#include "net/base/net_errors.h"
@@ -27,6 +25,8 @@
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
+using base::ASCIIToUTF16;
+
namespace content {
class ResourceDispatcherHostBrowserTest : public ContentBrowserTest,
@@ -248,7 +248,7 @@ IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest,
// Navigate to a cross-site page that loads immediately without making a
// network request. The unload event should still be run.
- NavigateToURL(shell(), GURL(kAboutBlankURL));
+ NavigateToURL(shell(), GURL(url::kAboutBlankURL));
// Check that the cookie was set.
EXPECT_EQ("onunloadCookie=foo", GetCookies(url));
@@ -305,9 +305,9 @@ IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, CrossSiteAfterCrash) {
NavigateToURL(shell(), GURL("about:blank"));
// Cause the renderer to crash.
- WindowedNotificationObserver crash_observer(
- NOTIFICATION_RENDERER_PROCESS_CLOSED,
- NotificationService::AllSources());
+ RenderProcessHostWatcher crash_observer(
+ shell()->web_contents(),
+ RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
NavigateToURL(shell(), GURL(kChromeUICrashURL));
// Wait for browser to notice the renderer crash.
crash_observer.Wait();
diff --git a/chromium/content/browser/loader/resource_dispatcher_host_impl.cc b/chromium/content/browser/loader/resource_dispatcher_host_impl.cc
index 712c8db992a..0e5abc6653c 100644
--- a/chromium/content/browser/loader/resource_dispatcher_host_impl.cc
+++ b/chromium/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -6,6 +6,7 @@
#include "content/browser/loader/resource_dispatcher_host_impl.h"
+#include <algorithm>
#include <set>
#include <vector>
@@ -22,6 +23,7 @@
#include "base/metrics/sparse_histogram.h"
#include "base/stl_util.h"
#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
+#include "content/browser/appcache/appcache_interceptor.h"
#include "content/browser/appcache/chrome_appcache_service.h"
#include "content/browser/cert_store_impl.h"
#include "content/browser/child_process_security_policy_impl.h"
@@ -46,11 +48,14 @@
#include "content/browser/renderer_host/render_view_host_delegate.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/resource_context_impl.h"
+#include "content/browser/service_worker/service_worker_request_handler.h"
#include "content/browser/streams/stream.h"
#include "content/browser/streams/stream_context.h"
#include "content/browser/streams/stream_registry.h"
#include "content/browser/worker_host/worker_service_impl.h"
+#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/resource_messages.h"
+#include "content/common/resource_request_body.h"
#include "content/common/ssl_status_serialization.h"
#include "content/common/view_messages.h"
#include "content/public/browser/browser_thread.h"
@@ -65,7 +70,6 @@
#include "content/public/browser/user_metrics.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/process_type.h"
-#include "content/public/common/url_constants.h"
#include "ipc/ipc_message_macros.h"
#include "ipc/ipc_message_start.h"
#include "net/base/auth.h"
@@ -83,7 +87,7 @@
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_job_factory.h"
-#include "webkit/browser/appcache/appcache_interceptor.h"
+#include "url/url_constants.h"
#include "webkit/common/blob/blob_data.h"
#include "webkit/browser/blob/blob_data_handle.h"
#include "webkit/browser/blob/blob_storage_context.h"
@@ -92,13 +96,11 @@
#include "webkit/browser/fileapi/file_system_context.h"
#include "webkit/common/appcache/appcache_interfaces.h"
#include "webkit/common/blob/shareable_file_reference.h"
-#include "webkit/common/resource_request_body.h"
using base::Time;
using base::TimeDelta;
using base::TimeTicks;
using webkit_blob::ShareableFileReference;
-using webkit_glue::ResourceRequestBody;
// ----------------------------------------------------------------------------
@@ -156,12 +158,15 @@ void AbortRequestBeforeItStarts(ResourceMessageFilter* filter,
filter->Send(sync_result);
} else {
// Tell the renderer that this request was disallowed.
+ ResourceMsg_RequestCompleteData request_complete_data;
+ request_complete_data.error_code = net::ERR_ABORTED;
+ request_complete_data.was_ignored_by_handler = false;
+ request_complete_data.exists_in_cache = false;
+ // No security info needed, connection not established.
+ request_complete_data.completion_time = base::TimeTicks();
+ request_complete_data.encoded_data_length = 0;
filter->Send(new ResourceMsg_RequestComplete(
- request_id,
- net::ERR_ABORTED,
- false,
- std::string(), // No security info needed, connection not established.
- base::TimeTicks()));
+ request_id, request_complete_data));
}
}
@@ -253,16 +258,18 @@ void RemoveDownloadFileFromChildSecurityPolicy(int child_id,
#pragma warning(default: 4748)
#endif
-net::Error CallbackAndReturn(
+DownloadInterruptReason CallbackAndReturn(
const DownloadUrlParameters::OnStartedCallback& started_cb,
- net::Error net_error) {
+ DownloadInterruptReason interrupt_reason) {
if (started_cb.is_null())
- return net_error;
+ return interrupt_reason;
BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- base::Bind(started_cb, static_cast<DownloadItem*>(NULL), net_error));
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(
+ started_cb, static_cast<DownloadItem*>(NULL), interrupt_reason));
- return net_error;
+ return interrupt_reason;
}
int GetCertID(net::URLRequest* request, int child_id) {
@@ -274,27 +281,40 @@ int GetCertID(net::URLRequest* request, int child_id) {
}
void NotifyRedirectOnUI(int render_process_id,
- int render_view_id,
+ int render_frame_host,
scoped_ptr<ResourceRedirectDetails> details) {
- RenderViewHostImpl* host =
- RenderViewHostImpl::FromID(render_process_id, render_view_id);
- if (!host)
+ RenderFrameHostImpl* host =
+ RenderFrameHostImpl::FromID(render_process_id, render_frame_host);
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(WebContents::FromRenderFrameHost(host));
+ if (!web_contents)
return;
-
- RenderViewHostDelegate* delegate = host->GetDelegate();
- delegate->DidGetRedirectForResourceRequest(*details.get());
+ web_contents->DidGetRedirectForResourceRequest(
+ host->render_view_host(), *details.get());
}
void NotifyResponseOnUI(int render_process_id,
- int render_view_id,
+ int render_frame_host,
scoped_ptr<ResourceRequestDetails> details) {
- RenderViewHostImpl* host =
- RenderViewHostImpl::FromID(render_process_id, render_view_id);
- if (!host)
+ RenderFrameHostImpl* host =
+ RenderFrameHostImpl::FromID(render_process_id, render_frame_host);
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(WebContents::FromRenderFrameHost(host));
+ if (!web_contents)
return;
+ web_contents->DidGetResourceResponseStart(*details.get());
+}
+
+bool IsValidatedSCT(
+ const net::SignedCertificateTimestampAndStatus& sct_status) {
+ return sct_status.status == net::ct::SCT_STATUS_OK;
+}
- RenderViewHostDelegate* delegate = host->GetDelegate();
- delegate->DidGetResourceResponseStart(*details.get());
+webkit_blob::BlobStorageContext* GetBlobStorageContext(
+ ResourceMessageFilter* filter) {
+ if (!filter->blob_storage_context())
+ return NULL;
+ return filter->blob_storage_context()->context();
}
} // namespace
@@ -457,7 +477,7 @@ void ResourceDispatcherHostImpl::CancelRequestsForContext(
}
}
-net::Error ResourceDispatcherHostImpl::BeginDownload(
+DownloadInterruptReason ResourceDispatcherHostImpl::BeginDownload(
scoped_ptr<net::URLRequest> request,
const Referrer& referrer,
bool is_content_initiated,
@@ -469,7 +489,8 @@ net::Error ResourceDispatcherHostImpl::BeginDownload(
uint32 download_id,
const DownloadStartedCallback& started_callback) {
if (is_shutdown_)
- return CallbackAndReturn(started_callback, net::ERR_INSUFFICIENT_RESOURCES);
+ return CallbackAndReturn(started_callback,
+ DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN);
const GURL& url = request->original_url();
@@ -496,18 +517,13 @@ net::Error ResourceDispatcherHostImpl::BeginDownload(
}
request->SetLoadFlags(request->load_flags() | extra_load_flags);
- // No need to get offline load flags for downloads, but make sure
- // we have an OfflinePolicy to receive request completions.
- GlobalRoutingID id(child_id, route_id);
- if (!offline_policy_map_[id])
- offline_policy_map_[id] = new OfflinePolicy();
-
// Check if the renderer is permitted to request the requested URL.
if (!ChildProcessSecurityPolicyImpl::GetInstance()->
CanRequestURL(child_id, url)) {
VLOG(1) << "Denied unauthorized download request for "
<< url.possibly_invalid_spec();
- return CallbackAndReturn(started_callback, net::ERR_ACCESS_DENIED);
+ return CallbackAndReturn(started_callback,
+ DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST);
}
request_id_--;
@@ -516,14 +532,15 @@ net::Error ResourceDispatcherHostImpl::BeginDownload(
if (!request_context->job_factory()->IsHandledURL(url)) {
VLOG(1) << "Download request for unsupported protocol: "
<< url.possibly_invalid_spec();
- return CallbackAndReturn(started_callback, net::ERR_ACCESS_DENIED);
+ return CallbackAndReturn(started_callback,
+ DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST);
}
ResourceRequestInfoImpl* extra_info =
CreateRequestInfo(child_id, route_id, true, context);
extra_info->AssociateWithRequest(request.get()); // Request takes ownership.
- if (request->url().SchemeIs(chrome::kBlobScheme)) {
+ if (request->url().SchemeIs(url::kBlobScheme)) {
ChromeBlobStorageContext* blob_context =
GetChromeBlobStorageContextForResourceContext(context);
webkit_blob::BlobProtocolHandler::SetRequestedBlobDataHandle(
@@ -540,7 +557,7 @@ net::Error ResourceDispatcherHostImpl::BeginDownload(
BeginRequestInternal(request.Pass(), handler.Pass());
- return net::OK;
+ return DOWNLOAD_INTERRUPT_REASON_NONE;
}
void ResourceDispatcherHostImpl::ClearLoginDelegateForRequest(
@@ -591,18 +608,17 @@ ResourceDispatcherHostImpl::CreateResourceHandlerForDownload(
scoped_ptr<ResourceHandler>
ResourceDispatcherHostImpl::MaybeInterceptAsStream(net::URLRequest* request,
- ResourceResponse* response) {
+ ResourceResponse* response,
+ std::string* payload) {
ResourceRequestInfoImpl* info = ResourceRequestInfoImpl::ForRequest(request);
const std::string& mime_type = response->head.mime_type;
GURL origin;
- std::string target_id;
if (!delegate_ ||
- !delegate_->ShouldInterceptResourceAsStream(info->GetContext(),
- request->url(),
+ !delegate_->ShouldInterceptResourceAsStream(request,
mime_type,
&origin,
- &target_id)) {
+ payload)) {
return scoped_ptr<ResourceHandler>();
}
@@ -616,12 +632,11 @@ ResourceDispatcherHostImpl::MaybeInterceptAsStream(net::URLRequest* request,
info->set_is_stream(true);
delegate_->OnStreamCreated(
- info->GetContext(),
- info->GetChildID(),
- info->GetRouteID(),
- target_id,
- handler->stream()->CreateHandle(request->url(), mime_type),
- request->GetExpectedContentSize());
+ request,
+ handler->stream()->CreateHandle(
+ request->url(),
+ mime_type,
+ response->head.headers));
return handler.PassAs<ResourceHandler>();
}
@@ -645,26 +660,6 @@ ResourceDispatcherHostImpl::CreateLoginDelegate(
return delegate_->CreateLoginDelegate(auth_info, loader->request());
}
-bool ResourceDispatcherHostImpl::AcceptAuthRequest(
- ResourceLoader* loader,
- net::AuthChallengeInfo* auth_info) {
- if (delegate_ && !delegate_->AcceptAuthRequest(loader->request(), auth_info))
- return false;
-
- return true;
-}
-
-bool ResourceDispatcherHostImpl::AcceptSSLClientCertificateRequest(
- ResourceLoader* loader,
- net::SSLCertRequestInfo* cert_info) {
- if (delegate_ && !delegate_->AcceptSSLClientCertificateRequest(
- loader->request(), cert_info)) {
- return false;
- }
-
- return true;
-}
-
bool ResourceDispatcherHostImpl::HandleExternalProtocol(ResourceLoader* loader,
const GURL& url) {
if (!delegate_)
@@ -680,8 +675,8 @@ bool ResourceDispatcherHostImpl::HandleExternalProtocol(ResourceLoader* loader,
if (job_factory->IsHandledURL(url))
return false;
- return delegate_->HandleExternalProtocol(url, info->GetChildID(),
- info->GetRouteID());
+ return delegate_->HandleExternalProtocol(
+ url, info->GetChildID(), info->GetRouteID());
}
void ResourceDispatcherHostImpl::DidStartRequest(ResourceLoader* loader) {
@@ -697,8 +692,8 @@ void ResourceDispatcherHostImpl::DidReceiveRedirect(ResourceLoader* loader,
const GURL& new_url) {
ResourceRequestInfoImpl* info = loader->GetRequestInfo();
- int render_process_id, render_view_id;
- if (!info->GetAssociatedRenderView(&render_process_id, &render_view_id))
+ int render_process_id, render_frame_host;
+ if (!info->GetAssociatedRenderFrame(&render_process_id, &render_frame_host))
return;
// Notify the observers on the UI thread.
@@ -710,29 +705,21 @@ void ResourceDispatcherHostImpl::DidReceiveRedirect(ResourceLoader* loader,
BrowserThread::UI, FROM_HERE,
base::Bind(
&NotifyRedirectOnUI,
- render_process_id, render_view_id, base::Passed(&detail)));
+ render_process_id, render_frame_host, base::Passed(&detail)));
}
void ResourceDispatcherHostImpl::DidReceiveResponse(ResourceLoader* loader) {
ResourceRequestInfoImpl* info = loader->GetRequestInfo();
- // There should be an entry in the map created when we dispatched the
- // request unless it's been detached and the renderer has died.
- OfflineMap::iterator policy_it(
- offline_policy_map_.find(info->GetGlobalRoutingID()));
- if (offline_policy_map_.end() != policy_it) {
- policy_it->second->UpdateStateForSuccessfullyStartedRequest(
- loader->request()->response_info());
- } else {
- // Unless detached, we should have an entry in offline_policy_map_ from
- // when this request traversed Begin{Download,SaveFile,Request}.
- // TODO(rdsmith): This isn't currently true; see http://crbug.com/241176.
- DCHECK(info->detachable_handler() &&
- info->detachable_handler()->is_detached());
+ if (loader->request()->was_fetched_via_proxy() &&
+ loader->request()->was_fetched_via_spdy() &&
+ loader->request()->url().SchemeIs("http")) {
+ scheduler_->OnReceivedSpdyProxiedHttpResponse(
+ info->GetChildID(), info->GetRouteID());
}
- int render_process_id, render_view_id;
- if (!info->GetAssociatedRenderView(&render_process_id, &render_view_id))
+ int render_process_id, render_frame_host;
+ if (!info->GetAssociatedRenderFrame(&render_process_id, &render_frame_host))
return;
// Notify the observers on the UI thread.
@@ -743,7 +730,7 @@ void ResourceDispatcherHostImpl::DidReceiveResponse(ResourceLoader* loader) {
BrowserThread::UI, FROM_HERE,
base::Bind(
&NotifyResponseOnUI,
- render_process_id, render_view_id, base::Passed(&detail)));
+ render_process_id, render_frame_host, base::Passed(&detail)));
}
void ResourceDispatcherHostImpl::DidFinishLoading(ResourceLoader* loader) {
@@ -757,11 +744,18 @@ void ResourceDispatcherHostImpl::DidFinishLoading(ResourceLoader* loader) {
"Net.ErrorCodesForMainFrame3",
-loader->request()->status().error());
- if (loader->request()->url().SchemeIsSecure() &&
- loader->request()->url().host() == "www.google.com") {
- UMA_HISTOGRAM_SPARSE_SLOWLY(
- "Net.ErrorCodesForHTTPSGoogleMainFrame2",
- -loader->request()->status().error());
+ if (loader->request()->url().SchemeIsSecure()) {
+ if (loader->request()->url().host() == "www.google.com") {
+ UMA_HISTOGRAM_SPARSE_SLOWLY("Net.ErrorCodesForHTTPSGoogleMainFrame2",
+ -loader->request()->status().error());
+ }
+
+ int num_valid_scts = std::count_if(
+ loader->request()->ssl_info().signed_certificate_timestamps.begin(),
+ loader->request()->ssl_info().signed_certificate_timestamps.end(),
+ IsValidatedSCT);
+ UMA_HISTOGRAM_COUNTS_100(
+ "Net.CertificateTransparency.MainFrameValidSCTCount", num_valid_scts);
}
} else {
if (info->GetResourceType() == ResourceType::IMAGE) {
@@ -775,29 +769,16 @@ void ResourceDispatcherHostImpl::DidFinishLoading(ResourceLoader* loader) {
-loader->request()->status().error());
}
+ if (delegate_)
+ delegate_->RequestComplete(loader->request());
+
// Destroy the ResourceLoader.
RemovePendingRequest(info->GetChildID(), info->GetRequestID());
}
-// static
-bool ResourceDispatcherHostImpl::RenderViewForRequest(
- const net::URLRequest* request,
- int* render_process_id,
- int* render_view_id) {
- const ResourceRequestInfoImpl* info =
- ResourceRequestInfoImpl::ForRequest(request);
- if (!info) {
- *render_process_id = -1;
- *render_view_id = -1;
- return false;
- }
-
- return info->GetAssociatedRenderView(render_process_id, render_view_id);
-}
-
void ResourceDispatcherHostImpl::OnInit() {
scheduler_.reset(new ResourceScheduler);
- appcache::AppCacheInterceptor::EnsureRegistered();
+ AppCacheInterceptor::EnsureRegistered();
}
void ResourceDispatcherHostImpl::OnShutdown() {
@@ -833,11 +814,10 @@ void ResourceDispatcherHostImpl::OnShutdown() {
bool ResourceDispatcherHostImpl::OnMessageReceived(
const IPC::Message& message,
- ResourceMessageFilter* filter,
- bool* message_was_ok) {
+ ResourceMessageFilter* filter) {
filter_ = filter;
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(ResourceDispatcherHostImpl, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(ResourceDispatcherHostImpl, message)
IPC_MESSAGE_HANDLER(ResourceHostMsg_RequestResource, OnRequestResource)
IPC_MESSAGE_HANDLER_DELAY_REPLY(ResourceHostMsg_SyncLoad, OnSyncLoad)
IPC_MESSAGE_HANDLER(ResourceHostMsg_ReleaseDownloadedFile,
@@ -846,7 +826,7 @@ bool ResourceDispatcherHostImpl::OnMessageReceived(
IPC_MESSAGE_HANDLER(ResourceHostMsg_UploadProgress_ACK, OnUploadProgressACK)
IPC_MESSAGE_HANDLER(ResourceHostMsg_CancelRequest, OnCancelRequest)
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
if (!handled && IPC_MESSAGE_ID_CLASS(message.type()) == ResourceMsgStart) {
PickleIterator iter(message);
@@ -859,9 +839,13 @@ bool ResourceDispatcherHostImpl::OnMessageReceived(
ObserverList<ResourceMessageDelegate>::Iterator del_it(*it->second);
ResourceMessageDelegate* delegate;
while (!handled && (delegate = del_it.GetNext()) != NULL) {
- handled = delegate->OnMessageReceived(message, message_was_ok);
+ handled = delegate->OnMessageReceived(message);
}
}
+
+ // As the unhandled resource message effectively has no consumer, mark it as
+ // handled to prevent needless propagation through the filter pipeline.
+ handled = true;
}
filter_ = NULL;
@@ -869,10 +853,10 @@ bool ResourceDispatcherHostImpl::OnMessageReceived(
}
void ResourceDispatcherHostImpl::OnRequestResource(
- const IPC::Message& message,
+ int routing_id,
int request_id,
const ResourceHostMsg_Request& request_data) {
- BeginRequest(request_id, request_data, NULL, message.routing_id());
+ BeginRequest(request_id, request_data, NULL, routing_id);
}
// Begins a resource request with the given params on behalf of the specified
@@ -916,8 +900,8 @@ void ResourceDispatcherHostImpl::UpdateRequestForTransfer(
// ResourceRequestInfo rather than caching it locally. This lets us update
// the info object when a transfer occurs.
info->UpdateForTransfer(child_id, route_id, request_data.origin_pid,
- request_id, request_data.frame_id,
- request_data.parent_frame_id, filter_->GetWeakPtr());
+ request_id, request_data.parent_render_frame_id,
+ filter_->GetWeakPtr());
// Update maps that used the old IDs, if necessary. Some transfers in tests
// do not actually use a different ID, so not all maps need to be updated.
@@ -925,13 +909,6 @@ void ResourceDispatcherHostImpl::UpdateRequestForTransfer(
UpdateOutstandingRequestsStats(*info, old_stats);
IncrementOutstandingRequestsMemory(1, *info);
if (old_routing_id != new_routing_id) {
- if (offline_policy_map_.find(old_routing_id) != offline_policy_map_.end()) {
- if (offline_policy_map_.find(new_routing_id) !=
- offline_policy_map_.end())
- delete offline_policy_map_[new_routing_id];
- offline_policy_map_[new_routing_id] = offline_policy_map_[old_routing_id];
- offline_policy_map_.erase(old_routing_id);
- }
if (blocked_loaders_map_.find(old_routing_id) !=
blocked_loaders_map_.end()) {
blocked_loaders_map_[new_routing_id] =
@@ -954,17 +931,7 @@ void ResourceDispatcherHostImpl::UpdateRequestForTransfer(
}
}
- // Notify the delegate to allow it to update state as well.
- if (delegate_) {
- delegate_->WillTransferRequestToNewProcess(old_routing_id.child_id,
- old_routing_id.route_id,
- old_request_id.request_id,
- child_id,
- route_id,
- request_id);
- }
-
- appcache::AppCacheInterceptor::CompleteCrossSiteTransfer(
+ AppCacheInterceptor::CompleteCrossSiteTransfer(
loader->request(),
child_id,
request_data.appcache_host_id);
@@ -984,7 +951,7 @@ void ResourceDispatcherHostImpl::BeginRequest(
// Reject invalid priority.
if (request_data.priority < net::MINIMUM_PRIORITY ||
request_data.priority > net::MAXIMUM_PRIORITY) {
- RecordAction(UserMetricsAction("BadMessageTerminate_RDH"));
+ RecordAction(base::UserMetricsAction("BadMessageTerminate_RDH"));
filter_->BadMessageReceived();
return;
}
@@ -997,7 +964,6 @@ void ResourceDispatcherHostImpl::BeginRequest(
// If the request that's coming in is being transferred from another process,
// we want to reuse and resume the old loader rather than start a new one.
- linked_ptr<ResourceLoader> deferred_loader;
{
LoaderMap::iterator it = pending_loaders_.find(
GlobalRequestID(request_data.transferred_request_child_id,
@@ -1006,13 +972,13 @@ void ResourceDispatcherHostImpl::BeginRequest(
// If the request is transferring to a new process, we can update our
// state and let it resume with its existing ResourceHandlers.
if (it->second->is_transferring()) {
- deferred_loader = it->second;
+ linked_ptr<ResourceLoader> deferred_loader = it->second;
UpdateRequestForTransfer(child_id, route_id, request_id,
request_data, deferred_loader);
deferred_loader->CompleteTransfer();
} else {
- RecordAction(UserMetricsAction("BadMessageTerminate_RDH"));
+ RecordAction(base::UserMetricsAction("BadMessageTerminate_RDH"));
filter_->BadMessageReceived();
}
return;
@@ -1032,8 +998,6 @@ void ResourceDispatcherHostImpl::BeginRequest(
return;
}
- const Referrer referrer(request_data.referrer, request_data.referrer_policy);
-
// Allow the observer to block/handle the request.
if (delegate_ && !delegate_->ShouldBeginRequest(child_id,
route_id,
@@ -1049,12 +1013,6 @@ void ResourceDispatcherHostImpl::BeginRequest(
int load_flags =
BuildLoadFlagsForRequest(request_data, child_id, is_sync_load);
- GlobalRoutingID id(child_id, route_id);
- if (!offline_policy_map_[id])
- offline_policy_map_[id] = new OfflinePolicy();
- load_flags |= offline_policy_map_[id]->GetAdditionalLoadFlags(
- load_flags, request_data.resource_type == ResourceType::MAIN_FRAME);
-
// Sync loads should have maximum priority and should be the only
// requets that have the ignore limits flag set.
if (is_sync_load) {
@@ -1065,30 +1023,31 @@ void ResourceDispatcherHostImpl::BeginRequest(
}
// Construct the request.
+ net::CookieStore* cookie_store =
+ GetContentClient()->browser()->OverrideCookieStoreForRenderProcess(
+ child_id);
scoped_ptr<net::URLRequest> new_request;
- net::URLRequest* request;
new_request = request_context->CreateRequest(
- request_data.url, request_data.priority, NULL);
- request = new_request.get();
+ request_data.url, request_data.priority, NULL, cookie_store);
- request->set_method(request_data.method);
- request->set_first_party_for_cookies(request_data.first_party_for_cookies);
- SetReferrerForRequest(request, referrer);
+ new_request->set_method(request_data.method);
+ new_request->set_first_party_for_cookies(
+ request_data.first_party_for_cookies);
+
+ const Referrer referrer(request_data.referrer, request_data.referrer_policy);
+ SetReferrerForRequest(new_request.get(), referrer);
net::HttpRequestHeaders headers;
headers.AddHeadersFromString(request_data.headers);
- request->SetExtraRequestHeaders(headers);
+ new_request->SetExtraRequestHeaders(headers);
- request->SetLoadFlags(load_flags);
+ new_request->SetLoadFlags(load_flags);
// Resolve elements from request_body and prepare upload data.
if (request_data.request_body.get()) {
- webkit_blob::BlobStorageContext* blob_context = NULL;
- if (filter_->blob_storage_context())
- blob_context = filter_->blob_storage_context()->context();
- request->set_upload(UploadDataStreamBuilder::Build(
+ new_request->set_upload(UploadDataStreamBuilder::Build(
request_data.request_body.get(),
- blob_context,
+ GetBlobStorageContext(filter_),
filter_->file_system_context(),
BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)
.get()));
@@ -1107,9 +1066,8 @@ void ResourceDispatcherHostImpl::BeginRequest(
request_id,
request_data.render_frame_id,
request_data.is_main_frame,
- request_data.frame_id,
request_data.parent_is_main_frame,
- request_data.parent_frame_id,
+ request_data.parent_render_frame_id,
request_data.resource_type,
request_data.transition_type,
request_data.should_replace_current_entry,
@@ -1118,32 +1076,44 @@ void ResourceDispatcherHostImpl::BeginRequest(
allow_download,
request_data.has_user_gesture,
request_data.referrer_policy,
+ request_data.visiblity_state,
resource_context,
filter_->GetWeakPtr(),
!is_sync_load);
- extra_info->AssociateWithRequest(request); // Request takes ownership.
+ // Request takes ownership.
+ extra_info->AssociateWithRequest(new_request.get());
- if (request->url().SchemeIs(chrome::kBlobScheme)) {
+ if (new_request->url().SchemeIs(url::kBlobScheme)) {
// Hang on to a reference to ensure the blob is not released prior
// to the job being started.
webkit_blob::BlobProtocolHandler::SetRequestedBlobDataHandle(
- request,
+ new_request.get(),
filter_->blob_storage_context()->context()->
- GetBlobDataFromPublicURL(request->url()));
+ GetBlobDataFromPublicURL(new_request->url()));
}
+ // Initialize the service worker handler for the request.
+ ServiceWorkerRequestHandler::InitializeHandler(
+ new_request.get(),
+ filter_->service_worker_context(),
+ GetBlobStorageContext(filter_),
+ child_id,
+ request_data.service_worker_provider_id,
+ request_data.resource_type);
+
// Have the appcache associate its extra info with the request.
- appcache::AppCacheInterceptor::SetExtraRequestInfo(
- request, filter_->appcache_service(), child_id,
+ AppCacheInterceptor::SetExtraRequestInfo(
+ new_request.get(), filter_->appcache_service(), child_id,
request_data.appcache_host_id, request_data.resource_type);
scoped_ptr<ResourceHandler> handler(
CreateResourceHandler(
- request,
+ new_request.get(),
request_data, sync_result, route_id, process_type, child_id,
resource_context));
- BeginRequestInternal(new_request.Pass(), handler.Pass());
+ if (handler)
+ BeginRequestInternal(new_request.Pass(), handler.Pass());
}
scoped_ptr<ResourceHandler> ResourceDispatcherHostImpl::CreateResourceHandler(
@@ -1157,30 +1127,45 @@ scoped_ptr<ResourceHandler> ResourceDispatcherHostImpl::CreateResourceHandler(
// Construct the IPC resource handler.
scoped_ptr<ResourceHandler> handler;
if (sync_result) {
+ // download_to_file is not supported for synchronous requests.
+ if (request_data.download_to_file) {
+ RecordAction(base::UserMetricsAction("BadMessageTerminate_RDH"));
+ filter_->BadMessageReceived();
+ return scoped_ptr<ResourceHandler>();
+ }
+
handler.reset(new SyncResourceHandler(request, sync_result, this));
} else {
handler.reset(new AsyncResourceHandler(request, this));
- if (IsDetachableResourceType(request_data.resource_type)) {
- handler.reset(new DetachableResourceHandler(
- request,
- base::TimeDelta::FromMilliseconds(kDefaultDetachableCancelDelayMs),
- handler.Pass()));
+
+ // The RedirectToFileResourceHandler depends on being next in the chain.
+ if (request_data.download_to_file) {
+ handler.reset(
+ new RedirectToFileResourceHandler(handler.Pass(), request));
}
}
- // The RedirectToFileResourceHandler depends on being next in the chain.
- if (request_data.download_to_file) {
- handler.reset(
- new RedirectToFileResourceHandler(handler.Pass(), request, this));
+ // Prefetches and <a ping> requests outlive their child process.
+ if (!sync_result && IsDetachableResourceType(request_data.resource_type)) {
+ handler.reset(new DetachableResourceHandler(
+ request,
+ base::TimeDelta::FromMilliseconds(kDefaultDetachableCancelDelayMs),
+ handler.Pass()));
}
// Install a CrossSiteResourceHandler for all main frame requests. This will
// let us check whether a transfer is required and pause for the unload
// handler either if so or if a cross-process navigation is already under way.
- if (request_data.resource_type == ResourceType::MAIN_FRAME &&
- process_type == PROCESS_TYPE_RENDERER) {
+ bool is_swappable_navigation =
+ request_data.resource_type == ResourceType::MAIN_FRAME;
+ // If we are using --site-per-process, install it for subframes as well.
+ if (!is_swappable_navigation &&
+ CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess)) {
+ is_swappable_navigation =
+ request_data.resource_type == ResourceType::SUB_FRAME;
+ }
+ if (is_swappable_navigation && process_type == PROCESS_TYPE_RENDERER)
handler.reset(new CrossSiteResourceHandler(handler.Pass(), request));
- }
// Insert a buffered event handler before the actual one.
handler.reset(
@@ -1220,7 +1205,11 @@ void ResourceDispatcherHostImpl::OnDataDownloadedACK(int request_id) {
}
void ResourceDispatcherHostImpl::RegisterDownloadedTempFile(
- int child_id, int request_id, ShareableFileReference* reference) {
+ int child_id, int request_id, const base::FilePath& file_path) {
+ scoped_refptr<ShareableFileReference> reference =
+ ShareableFileReference::Get(file_path);
+ DCHECK(reference);
+
registered_temp_files_[child_id][request_id] = reference;
ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadFile(
child_id, reference->path());
@@ -1262,8 +1251,26 @@ void ResourceDispatcherHostImpl::OnUploadProgressACK(int request_id) {
loader->OnUploadProgressACK();
}
+// Note that this cancel is subtly different from the other
+// CancelRequest methods in this file, which also tear down the loader.
void ResourceDispatcherHostImpl::OnCancelRequest(int request_id) {
- CancelRequest(filter_->child_id(), request_id, true);
+ int child_id = filter_->child_id();
+
+ // When the old renderer dies, it sends a message to us to cancel its
+ // requests.
+ if (IsTransferredNavigation(GlobalRequestID(child_id, request_id)))
+ return;
+
+ ResourceLoader* loader = GetLoader(child_id, request_id);
+ if (!loader) {
+ // We probably want to remove this warning eventually, but I wanted to be
+ // able to notice when this happens during initial development since it
+ // should be rare and may indicate a bug.
+ DVLOG(1) << "Canceling a request that wasn't found";
+ return;
+ }
+
+ loader->CancelRequest(true);
}
ResourceRequestInfoImpl* ResourceDispatcherHostImpl::CreateRequestInfo(
@@ -1279,9 +1286,8 @@ ResourceRequestInfoImpl* ResourceDispatcherHostImpl::CreateRequestInfo(
request_id_,
MSG_ROUTING_NONE, // render_frame_id
false, // is_main_frame
- -1, // frame_id
false, // parent_is_main_frame
- -1, // parent_frame_id
+ -1, // parent_render_frame_id
ResourceType::SUB_RESOURCE,
PAGE_TRANSITION_LINK,
false, // should_replace_current_entry
@@ -1290,6 +1296,7 @@ ResourceRequestInfoImpl* ResourceDispatcherHostImpl::CreateRequestInfo(
download, // allow_download
false, // has_user_gesture
blink::WebReferrerPolicyDefault,
+ blink::WebPageVisibilityStateVisible,
context,
base::WeakPtr<ResourceMessageFilter>(), // filter
true); // is_async
@@ -1324,11 +1331,6 @@ void ResourceDispatcherHostImpl::BeginSaveFile(
base::debug::Alias(url_buf);
CHECK(ContainsKey(active_resource_contexts_, context));
- scoped_ptr<ResourceHandler> handler(
- new SaveFileResourceHandler(child_id,
- route_id,
- url,
- save_file_manager_.get()));
request_id_--;
const net::URLRequestContext* request_context = context->GetRequestContext();
@@ -1342,8 +1344,13 @@ void ResourceDispatcherHostImpl::BeginSaveFile(
return;
}
+ net::CookieStore* cookie_store =
+ GetContentClient()->browser()->OverrideCookieStoreForRenderProcess(
+ child_id);
scoped_ptr<net::URLRequest> request(
- request_context->CreateRequest(url, net::DEFAULT_PRIORITY, NULL));
+ request_context->CreateRequest(url, net::DEFAULT_PRIORITY, NULL,
+ cookie_store));
+
request->set_method("GET");
SetReferrerForRequest(request.get(), referrer);
@@ -1351,23 +1358,31 @@ void ResourceDispatcherHostImpl::BeginSaveFile(
// future, maybe we can use a configuration to configure this behavior.
request->SetLoadFlags(net::LOAD_PREFERRING_CACHE);
- // No need to get offline load flags for save files, but make sure
- // we have an OfflinePolicy to receive request completions.
- GlobalRoutingID id(child_id, route_id);
- if (!offline_policy_map_[id])
- offline_policy_map_[id] = new OfflinePolicy();
-
// Since we're just saving some resources we need, disallow downloading.
ResourceRequestInfoImpl* extra_info =
CreateRequestInfo(child_id, route_id, false, context);
extra_info->AssociateWithRequest(request.get()); // Request takes ownership.
+ scoped_ptr<ResourceHandler> handler(
+ new SaveFileResourceHandler(request.get(),
+ child_id,
+ route_id,
+ url,
+ save_file_manager_.get()));
+
BeginRequestInternal(request.Pass(), handler.Pass());
}
void ResourceDispatcherHostImpl::MarkAsTransferredNavigation(
- const GlobalRequestID& id, const GURL& target_url) {
- GetLoader(id)->MarkAsTransferring(target_url);
+ const GlobalRequestID& id) {
+ GetLoader(id)->MarkAsTransferring();
+}
+
+void ResourceDispatcherHostImpl::CancelTransferringNavigation(
+ const GlobalRequestID& id) {
+ // Request should still exist and be in the middle of a transfer.
+ DCHECK(IsTransferredNavigation(id));
+ RemovePendingRequest(id.child_id, id.request_id);
}
void ResourceDispatcherHostImpl::ResumeDeferredNavigation(
@@ -1466,27 +1481,6 @@ void ResourceDispatcherHostImpl::CancelRequestsForRoute(int child_id,
CancelBlockedRequestsForRoute(child_id, *iter);
}
}
-
- // Cleanup the offline state for the route.
- if (-1 != route_id) {
- OfflineMap::iterator it = offline_policy_map_.find(
- GlobalRoutingID(child_id, route_id));
- if (offline_policy_map_.end() != it) {
- delete it->second;
- offline_policy_map_.erase(it);
- }
- } else {
- for (OfflineMap::iterator it = offline_policy_map_.begin();
- offline_policy_map_.end() != it;) {
- // Increment iterator so deletion doesn't invalidate it.
- OfflineMap::iterator current_it = it++;
-
- if (child_id == current_it->first.child_id) {
- delete current_it->second;
- offline_policy_map_.erase(current_it);
- }
- }
- }
}
// Cancels the request and removes it from the list.
@@ -1517,15 +1511,7 @@ void ResourceDispatcherHostImpl::RemovePendingLoader(
}
void ResourceDispatcherHostImpl::CancelRequest(int child_id,
- int request_id,
- bool from_renderer) {
- if (from_renderer) {
- // When the old renderer dies, it sends a message to us to cancel its
- // requests.
- if (IsTransferredNavigation(GlobalRequestID(child_id, request_id)))
- return;
- }
-
+ int request_id) {
ResourceLoader* loader = GetLoader(child_id, request_id);
if (!loader) {
// We probably want to remove this warning eventually, but I wanted to be
@@ -1535,7 +1521,7 @@ void ResourceDispatcherHostImpl::CancelRequest(int child_id,
return;
}
- loader->CancelRequest(from_renderer);
+ RemovePendingRequest(child_id, request_id);
}
ResourceDispatcherHostImpl::OustandingRequestsStats
@@ -1652,8 +1638,7 @@ void ResourceDispatcherHostImpl::BeginRequestInternal(
request->CancelWithError(net::ERR_INSUFFICIENT_RESOURCES);
bool defer = false;
- handler->OnResponseCompleted(info->GetRequestID(), request->status(),
- std::string(), &defer);
+ handler->OnResponseCompleted(request->status(), std::string(), &defer);
if (defer) {
// TODO(darin): The handler is not ready for us to kill the request. Oops!
NOTREACHED();
@@ -1872,7 +1857,7 @@ ResourceDispatcherHostImpl::HttpAuthRelationTypeOf(
if (net::registry_controlled_domains::SameDomainOrHost(
first_party, request_url,
- net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES))
+ net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES))
return HTTP_AUTH_RELATION_SAME_DOMAIN;
if (allow_cross_origin_auth_prompt())
diff --git a/chromium/content/browser/loader/resource_dispatcher_host_impl.h b/chromium/content/browser/loader/resource_dispatcher_host_impl.h
index 2e0253ae519..3cc7b4fd1ec 100644
--- a/chromium/content/browser/loader/resource_dispatcher_host_impl.h
+++ b/chromium/content/browser/loader/resource_dispatcher_host_impl.h
@@ -26,7 +26,6 @@
#include "base/timer/timer.h"
#include "content/browser/download/download_resource_handler.h"
#include "content/browser/loader/global_routing_id.h"
-#include "content/browser/loader/offline_policy.h"
#include "content/browser/loader/resource_loader.h"
#include "content/browser/loader/resource_loader_delegate.h"
#include "content/browser/loader/resource_scheduler.h"
@@ -78,7 +77,7 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl
// ResourceDispatcherHost implementation:
virtual void SetDelegate(ResourceDispatcherHostDelegate* delegate) OVERRIDE;
virtual void SetAllowCrossOriginAuthPrompt(bool value) OVERRIDE;
- virtual net::Error BeginDownload(
+ virtual DownloadInterruptReason BeginDownload(
scoped_ptr<net::URLRequest> request,
const Referrer& referrer,
bool is_content_initiated,
@@ -110,10 +109,8 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl
void CancelRequestsForContext(ResourceContext* context);
// Returns true if the message was a resource message that was processed.
- // If it was, message_was_ok will be false iff the message was corrupt.
bool OnMessageReceived(const IPC::Message& message,
- ResourceMessageFilter* filter,
- bool* message_was_ok);
+ ResourceMessageFilter* filter);
// Initiates a save file from the browser process (as opposed to a resource
// request from the renderer or another child process).
@@ -123,16 +120,16 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl
int route_id,
ResourceContext* context);
- // Cancels the given request if it still exists. We ignore cancels from the
- // renderer in the event of a download.
- void CancelRequest(int child_id,
- int request_id,
- bool from_renderer);
+ // Cancels the given request if it still exists.
+ void CancelRequest(int child_id, int request_id);
// Marks the request as "parked". This happens if a request is
// redirected cross-site and needs to be resumed by a new render view.
- void MarkAsTransferredNavigation(const GlobalRequestID& id,
- const GURL& target_url);
+ void MarkAsTransferredNavigation(const GlobalRequestID& id);
+
+ // Cancels a request previously marked as being transferred, for use when a
+ // navigation was cancelled.
+ void CancelTransferringNavigation(const GlobalRequestID& id);
// Resumes the request without transferring it to a new render view.
void ResumeDeferredNavigation(const GlobalRequestID& id);
@@ -186,7 +183,7 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl
// no longer needed.
void RegisterDownloadedTempFile(
int child_id, int request_id,
- webkit_blob::ShareableFileReference* reference);
+ const base::FilePath& file_path);
void UnregisterDownloadedTempFile(int child_id, int request_id);
// Needed for the sync IPC message dispatcher macros.
@@ -213,10 +210,13 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl
const DownloadUrlParameters::OnStartedCallback& started_cb);
// Must be called after the ResourceRequestInfo has been created
- // and associated with the request.
+ // and associated with the request. If |payload| is set to a non-empty value,
+ // the value will be sent to the old resource handler instead of cancelling
+ // it, except on HTTP errors.
scoped_ptr<ResourceHandler> MaybeInterceptAsStream(
net::URLRequest* request,
- ResourceResponse* response);
+ ResourceResponse* response,
+ std::string* payload);
void ClearSSLClientAuthHandlerForRequest(net::URLRequest* request);
@@ -234,6 +234,8 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl
void FinishedWithResourcesForRequest(const net::URLRequest* request_);
private:
+ friend class ResourceDispatcherHostTest;
+
FRIEND_TEST_ALL_PREFIXES(ResourceDispatcherHostTest,
TestBlockedRequestsProcessDies);
FRIEND_TEST_ALL_PREFIXES(ResourceDispatcherHostTest,
@@ -257,12 +259,6 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl
virtual ResourceDispatcherHostLoginDelegate* CreateLoginDelegate(
ResourceLoader* loader,
net::AuthChallengeInfo* auth_info) OVERRIDE;
- virtual bool AcceptAuthRequest(
- ResourceLoader* loader,
- net::AuthChallengeInfo* auth_info) OVERRIDE;
- virtual bool AcceptSSLClientCertificateRequest(
- ResourceLoader* loader,
- net::SSLCertRequestInfo* cert_info) OVERRIDE;
virtual bool HandleExternalProtocol(ResourceLoader* loader,
const GURL& url) OVERRIDE;
virtual void DidStartRequest(ResourceLoader* loader) OVERRIDE;
@@ -271,14 +267,6 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl
virtual void DidReceiveResponse(ResourceLoader* loader) OVERRIDE;
virtual void DidFinishLoading(ResourceLoader* loader) OVERRIDE;
- // Extracts the render view/process host's identifiers from the given request
- // and places them in the given out params (both required). If there are no
- // such IDs associated with the request (such as non-page-related requests),
- // this function will return false and both out params will be -1.
- static bool RenderViewForRequest(const net::URLRequest* request,
- int* render_process_host_id,
- int* render_view_host_id);
-
// An init helper that runs on the IO thread.
void OnInit();
@@ -353,7 +341,7 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl
int route_id,
bool cancel_requests);
- void OnRequestResource(const IPC::Message& msg,
+ void OnRequestResource(int routing_id,
int request_id,
const ResourceHostMsg_Request& request_data);
void OnSyncLoad(int request_id,
@@ -518,10 +506,6 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl
scoped_ptr<ResourceScheduler> scheduler_;
- typedef std::map<GlobalRoutingID, OfflinePolicy*> OfflineMap;
-
- OfflineMap offline_policy_map_;
-
DISALLOW_COPY_AND_ASSIGN(ResourceDispatcherHostImpl);
};
diff --git a/chromium/content/browser/loader/resource_dispatcher_host_unittest.cc b/chromium/content/browser/loader/resource_dispatcher_host_unittest.cc
index 52d34328847..a815032ac6f 100644
--- a/chromium/content/browser/loader/resource_dispatcher_host_unittest.cc
+++ b/chromium/content/browser/loader/resource_dispatcher_host_unittest.cc
@@ -4,9 +4,12 @@
#include <vector>
+#include "base/basictypes.h"
#include "base/bind.h"
+#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/memory/scoped_vector.h"
+#include "base/memory/shared_memory.h"
#include "base/message_loop/message_loop.h"
#include "base/pickle.h"
#include "base/run_loop.h"
@@ -14,6 +17,7 @@
#include "base/strings/string_split.h"
#include "content/browser/browser_thread_impl.h"
#include "content/browser/child_process_security_policy_impl.h"
+#include "content/browser/loader/cross_site_resource_handler.h"
#include "content/browser/loader/detachable_resource_handler.h"
#include "content/browser/loader/resource_dispatcher_host_impl.h"
#include "content/browser/loader/resource_loader.h"
@@ -31,6 +35,7 @@
#include "content/public/common/process_type.h"
#include "content/public/common/resource_response.h"
#include "content/public/test/test_browser_context.h"
+#include "content/public/test/test_browser_thread_bundle.h"
#include "content/test/test_content_browser_client.h"
#include "net/base/net_errors.h"
#include "net/base/request_priority.h"
@@ -40,15 +45,19 @@
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_job.h"
+#include "net/url_request/url_request_job_factory.h"
#include "net/url_request/url_request_simple_job.h"
#include "net/url_request/url_request_test_job.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "webkit/common/appcache/appcache_interfaces.h"
+#include "webkit/common/blob/shareable_file_reference.h"
// TODO(eroman): Write unit tests for SafeBrowsing that exercise
// SafeBrowsingResourceHandler.
+using webkit_blob::ShareableFileReference;
+
namespace content {
namespace {
@@ -70,9 +79,31 @@ void GetResponseHead(const std::vector<IPC::Message>& messages,
void GenerateIPCMessage(
scoped_refptr<ResourceMessageFilter> filter,
scoped_ptr<IPC::Message> message) {
- bool msg_is_ok;
ResourceDispatcherHostImpl::Get()->OnMessageReceived(
- *message, filter.get(), &msg_is_ok);
+ *message, filter.get());
+}
+
+// On Windows, ResourceMsg_SetDataBuffer supplies a HANDLE which is not
+// automatically released.
+//
+// See ResourceDispatcher::ReleaseResourcesInDataMessage.
+//
+// TODO(davidben): It would be nice if the behavior for base::SharedMemoryHandle
+// were more like it is in POSIX where the received fds are tracked in a
+// ref-counted core that closes them if not extracted.
+void ReleaseHandlesInMessage(const IPC::Message& message) {
+ if (message.type() == ResourceMsg_SetDataBuffer::ID) {
+ PickleIterator iter(message);
+ int request_id;
+ CHECK(message.ReadInt(&iter, &request_id));
+ base::SharedMemoryHandle shm_handle;
+ if (IPC::ParamTraits<base::SharedMemoryHandle>::Read(&message,
+ &iter,
+ &shm_handle)) {
+ if (base::SharedMemory::IsHandleValid(shm_handle))
+ base::SharedMemory::CloseHandle(shm_handle);
+ }
+ }
}
} // namespace
@@ -85,6 +116,7 @@ static int RequestIDForMessage(const IPC::Message& msg) {
case ResourceMsg_ReceivedRedirect::ID:
case ResourceMsg_SetDataBuffer::ID:
case ResourceMsg_DataReceived::ID:
+ case ResourceMsg_DataDownloaded::ID:
case ResourceMsg_RequestComplete::ID: {
bool result = PickleIterator(msg).ReadInt(&request_id);
DCHECK(result);
@@ -107,12 +139,11 @@ static ResourceHostMsg_Request CreateResourceRequest(
request.origin_pid = 0;
request.resource_type = type;
request.request_context = 0;
- request.appcache_host_id = appcache::kNoHostId;
+ request.appcache_host_id = appcache::kAppCacheNoHostId;
request.download_to_file = false;
request.is_main_frame = true;
- request.frame_id = 0;
request.parent_is_main_frame = false;
- request.parent_frame_id = -1;
+ request.parent_render_frame_id = -1;
request.transition_type = PAGE_TRANSITION_LINK;
request.allow_download = true;
return request;
@@ -126,6 +157,13 @@ static void KickOffRequest() {
// We may want to move this to a shared space if it is useful for something else
class ResourceIPCAccumulator {
public:
+ ~ResourceIPCAccumulator() {
+ for (size_t i = 0; i < messages_.size(); i++) {
+ ReleaseHandlesInMessage(messages_[i]);
+ }
+ }
+
+ // On Windows, takes ownership of SharedMemoryHandles in |msg|.
void AddMessage(const IPC::Message& msg) {
messages_.push_back(msg);
}
@@ -133,7 +171,8 @@ class ResourceIPCAccumulator {
// This groups the messages by their request ID. The groups will be in order
// that the first message for each request ID was received, and the messages
// within the groups will be in the order that they appeared.
- // Note that this clears messages_.
+ // Note that this clears messages_. The caller takes ownership of any
+ // SharedMemoryHandles in messages placed into |msgs|.
typedef std::vector< std::vector<IPC::Message> > ClassifiedMessages;
void GetClassifiedMessages(ClassifiedMessages* msgs);
@@ -166,35 +205,39 @@ void ResourceIPCAccumulator::GetClassifiedMessages(ClassifiedMessages* msgs) {
}
}
-// This class forwards the incoming messages to the ResourceDispatcherHostTest.
// This is used to emulate different sub-processes, since this filter will
-// have a different ID than the original. For the test, we want all the incoming
-// messages to go to the same place, which is why this forwards.
-class ForwardingFilter : public ResourceMessageFilter {
+// have a different ID than the original.
+class TestFilter : public ResourceMessageFilter {
public:
- explicit ForwardingFilter(IPC::Sender* dest,
- ResourceContext* resource_context)
- : ResourceMessageFilter(
- ChildProcessHostImpl::GenerateChildProcessUniqueId(),
- PROCESS_TYPE_RENDERER, NULL, NULL, NULL,
- base::Bind(&ForwardingFilter::GetContexts,
- base::Unretained(this))),
- dest_(dest),
- resource_context_(resource_context) {
+ explicit TestFilter(ResourceContext* resource_context)
+ : ResourceMessageFilter(
+ ChildProcessHostImpl::GenerateChildProcessUniqueId(),
+ PROCESS_TYPE_RENDERER, NULL, NULL, NULL, NULL,
+ base::Bind(&TestFilter::GetContexts, base::Unretained(this))),
+ resource_context_(resource_context),
+ canceled_(false),
+ received_after_canceled_(0) {
+ ChildProcessSecurityPolicyImpl::GetInstance()->Add(child_id());
set_peer_pid_for_testing(base::GetCurrentProcId());
}
+ void set_canceled(bool canceled) { canceled_ = canceled; }
+ int received_after_canceled() const { return received_after_canceled_; }
+
// ResourceMessageFilter override
virtual bool Send(IPC::Message* msg) OVERRIDE {
- if (!dest_)
- return false;
- return dest_->Send(msg);
+ // No messages should be received when the process has been canceled.
+ if (canceled_)
+ received_after_canceled_++;
+ ReleaseHandlesInMessage(*msg);
+ delete msg;
+ return true;
}
ResourceContext* resource_context() { return resource_context_; }
protected:
- virtual ~ForwardingFilter() {}
+ virtual ~TestFilter() {}
private:
void GetContexts(const ResourceHostMsg_Request& request,
@@ -204,12 +247,65 @@ class ForwardingFilter : public ResourceMessageFilter {
*request_context = resource_context_->GetRequestContext();
}
- IPC::Sender* dest_;
ResourceContext* resource_context_;
+ bool canceled_;
+ int received_after_canceled_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestFilter);
+};
+
+
+// This class forwards the incoming messages to the ResourceDispatcherHostTest.
+// For the test, we want all the incoming messages to go to the same place,
+// which is why this forwards.
+class ForwardingFilter : public TestFilter {
+ public:
+ explicit ForwardingFilter(IPC::Sender* dest,
+ ResourceContext* resource_context)
+ : TestFilter(resource_context),
+ dest_(dest) {
+ }
+
+ // TestFilter override
+ virtual bool Send(IPC::Message* msg) OVERRIDE {
+ return dest_->Send(msg);
+ }
+
+ private:
+ virtual ~ForwardingFilter() {}
+
+ IPC::Sender* dest_;
DISALLOW_COPY_AND_ASSIGN(ForwardingFilter);
};
+// This class is a variation on URLRequestTestJob that will call
+// URLRequest::OnBeforeNetworkStart before starting.
+class URLRequestTestDelayedNetworkJob : public net::URLRequestTestJob {
+ public:
+ URLRequestTestDelayedNetworkJob(net::URLRequest* request,
+ net::NetworkDelegate* network_delegate)
+ : net::URLRequestTestJob(request, network_delegate) {}
+
+ // Only start if not deferred for network start.
+ virtual void Start() OVERRIDE {
+ bool defer = false;
+ NotifyBeforeNetworkStart(&defer);
+ if (defer)
+ return;
+ net::URLRequestTestJob::Start();
+ }
+
+ virtual void ResumeNetworkStart() OVERRIDE {
+ net::URLRequestTestJob::StartAsync();
+ }
+
+ private:
+ virtual ~URLRequestTestDelayedNetworkJob() {}
+
+ DISALLOW_COPY_AND_ASSIGN(URLRequestTestDelayedNetworkJob);
+};
+
// This class is a variation on URLRequestTestJob in that it does
// not complete start upon entry, only when specifically told to.
class URLRequestTestDelayedStartJob : public net::URLRequestTestJob {
@@ -365,6 +461,66 @@ class URLRequestBigJob : public net::URLRequestSimpleJob {
}
};
+class ResourceDispatcherHostTest;
+
+class TestURLRequestJobFactory : public net::URLRequestJobFactory {
+ public:
+ explicit TestURLRequestJobFactory(ResourceDispatcherHostTest* test_fixture)
+ : test_fixture_(test_fixture),
+ delay_start_(false),
+ delay_complete_(false),
+ network_start_notification_(false),
+ url_request_jobs_created_count_(0) {
+ }
+
+ void HandleScheme(const std::string& scheme) {
+ supported_schemes_.insert(scheme);
+ }
+
+ int url_request_jobs_created_count() const {
+ return url_request_jobs_created_count_;
+ }
+
+ void SetDelayedStartJobGeneration(bool delay_job_start) {
+ delay_start_ = delay_job_start;
+ }
+
+ void SetDelayedCompleteJobGeneration(bool delay_job_complete) {
+ delay_complete_ = delay_job_complete;
+ }
+
+ void SetNetworkStartNotificationJobGeneration(bool notification) {
+ network_start_notification_ = notification;
+ }
+
+ virtual net::URLRequestJob* MaybeCreateJobWithProtocolHandler(
+ const std::string& scheme,
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) const OVERRIDE;
+
+ virtual bool IsHandledProtocol(const std::string& scheme) const OVERRIDE {
+ return supported_schemes_.count(scheme) > 0;
+ }
+
+ virtual bool IsHandledURL(const GURL& url) const OVERRIDE {
+ return supported_schemes_.count(url.scheme()) > 0;
+ }
+
+ virtual bool IsSafeRedirectTarget(const GURL& location) const OVERRIDE {
+ return false;
+ }
+
+ private:
+ ResourceDispatcherHostTest* test_fixture_;
+ bool delay_start_;
+ bool delay_complete_;
+ bool network_start_notification_;
+ mutable int url_request_jobs_created_count_;
+ std::set<std::string> supported_schemes_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestURLRequestJobFactory);
+};
+
// Associated with an URLRequest to determine if the URLRequest gets deleted.
class TestUserData : public base::SupportsUserData::Data {
public:
@@ -394,7 +550,8 @@ enum GenericResourceThrottleFlags {
NONE = 0,
DEFER_STARTING_REQUEST = 1 << 0,
DEFER_PROCESSING_RESPONSE = 1 << 1,
- CANCEL_BEFORE_START = 1 << 2
+ CANCEL_BEFORE_START = 1 << 2,
+ DEFER_NETWORK_START = 1 << 3
};
// Throttle that tracks the current throttle blocking a request. Only one
@@ -441,6 +598,15 @@ class GenericResourceThrottle : public ResourceThrottle {
}
}
+ virtual void OnBeforeNetworkStart(bool* defer) OVERRIDE {
+ ASSERT_EQ(NULL, active_throttle_);
+
+ if (flags_ & DEFER_NETWORK_START) {
+ active_throttle_ = this;
+ *defer = true;
+ }
+ }
+
virtual const char* GetNameForLogging() const OVERRIDE {
return "GenericResourceThrottle";
}
@@ -522,30 +688,48 @@ class TestResourceDispatcherHostDelegate
scoped_ptr<base::SupportsUserData::Data> user_data_;
};
+// Waits for a ShareableFileReference to be released.
+class ShareableFileReleaseWaiter {
+ public:
+ ShareableFileReleaseWaiter(const base::FilePath& path) {
+ scoped_refptr<ShareableFileReference> file =
+ ShareableFileReference::Get(path);
+ file->AddFinalReleaseCallback(
+ base::Bind(&ShareableFileReleaseWaiter::Released,
+ base::Unretained(this)));
+ }
+
+ void Wait() {
+ loop_.Run();
+ }
+
+ private:
+ void Released(const base::FilePath& path) {
+ loop_.Quit();
+ }
+
+ base::RunLoop loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(ShareableFileReleaseWaiter);
+};
+
class ResourceDispatcherHostTest : public testing::Test,
public IPC::Sender {
public:
ResourceDispatcherHostTest()
- : ui_thread_(BrowserThread::UI, &message_loop_),
- file_thread_(BrowserThread::FILE_USER_BLOCKING, &message_loop_),
- cache_thread_(BrowserThread::CACHE, &message_loop_),
- io_thread_(BrowserThread::IO, &message_loop_),
+ : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
old_factory_(NULL),
send_data_received_acks_(false) {
browser_context_.reset(new TestBrowserContext());
BrowserContext::EnsureResourceContextInitialized(browser_context_.get());
- message_loop_.RunUntilIdle();
- ResourceContext* resource_context = browser_context_->GetResourceContext();
- filter_ = new ForwardingFilter(this, resource_context);
- resource_context->GetRequestContext()->set_network_delegate(
- &network_delegate_);
- }
-
- virtual ~ResourceDispatcherHostTest() {
- for (std::set<int>::iterator it = child_ids_.begin();
- it != child_ids_.end(); ++it) {
- host_.CancelRequestsForProcess(*it);
- }
+ base::RunLoop().RunUntilIdle();
+ filter_ = MakeForwardingFilter();
+ // TODO(cbentzel): Better way to get URLRequestContext?
+ net::URLRequestContext* request_context =
+ browser_context_->GetResourceContext()->GetRequestContext();
+ job_factory_.reset(new TestURLRequestJobFactory(this));
+ request_context->set_job_factory(job_factory_.get());
+ request_context->set_network_delegate(&network_delegate_);
}
// IPC::Sender implementation
@@ -557,36 +741,33 @@ class ResourceDispatcherHostTest : public testing::Test,
GenerateDataReceivedACK(*msg);
}
+ if (wait_for_request_complete_loop_ &&
+ msg->type() == ResourceMsg_RequestComplete::ID) {
+ wait_for_request_complete_loop_->Quit();
+ }
+
+ // Do not release handles in it yet; the accumulator owns them now.
delete msg;
return true;
}
protected:
+ friend class TestURLRequestJobFactory;
+
// testing::Test
- virtual void SetUp() {
- DCHECK(!test_fixture_);
- test_fixture_ = this;
+ virtual void SetUp() OVERRIDE {
ChildProcessSecurityPolicyImpl::GetInstance()->Add(0);
- net::URLRequest::Deprecated::RegisterProtocolFactory(
- "test",
- &ResourceDispatcherHostTest::Factory);
- EnsureTestSchemeIsAllowed();
- delay_start_ = false;
- delay_complete_ = false;
- url_request_jobs_created_count_ = 0;
+ HandleScheme("test");
}
virtual void TearDown() {
- net::URLRequest::Deprecated::RegisterProtocolFactory("test", NULL);
- if (!scheme_.empty())
- net::URLRequest::Deprecated::RegisterProtocolFactory(
- scheme_, old_factory_);
-
EXPECT_TRUE(URLRequestTestDelayedStartJob::DelayedStartQueueEmpty());
URLRequestTestDelayedStartJob::ClearQueue();
- DCHECK(test_fixture_ == this);
- test_fixture_ = NULL;
+ for (std::set<int>::iterator it = child_ids_.begin();
+ it != child_ids_.end(); ++it) {
+ host_.CancelRequestsForProcess(*it);
+ }
host_.Shutdown();
@@ -600,7 +781,16 @@ class ResourceDispatcherHostTest : public testing::Test,
WorkerServiceImpl::GetInstance()->PerformTeardownForTesting();
browser_context_.reset();
- message_loop_.RunUntilIdle();
+ base::RunLoop().RunUntilIdle();
+ }
+
+ // Creates a new ForwardingFilter and registers it with |child_ids_| so as not
+ // to leak per-child state on test shutdown.
+ ForwardingFilter* MakeForwardingFilter() {
+ ForwardingFilter* filter =
+ new ForwardingFilter(this, browser_context_->GetResourceContext());
+ child_ids_.insert(filter->child_id());
+ return filter;
}
// Creates a request using the current test object as the filter and
@@ -616,6 +806,11 @@ class ResourceDispatcherHostTest : public testing::Test,
ResourceType::Type type);
void CancelRequest(int request_id);
+ void RendererCancelRequest(int request_id) {
+ ResourceMessageFilter* old_filter = SetFilter(filter_.get());
+ host_.OnCancelRequest(request_id);
+ SetFilter(old_filter);
+ }
void CompleteStartRequest(int request_id);
void CompleteStartRequest(ResourceMessageFilter* filter, int request_id);
@@ -629,10 +824,6 @@ class ResourceDispatcherHostTest : public testing::Test,
policy->RegisterWebSafeScheme(scheme);
}
- void EnsureTestSchemeIsAllowed() {
- EnsureSchemeIsAllowed("test");
- }
-
// Sets a particular response for any request from now on. To switch back to
// the default bahavior, pass an empty |headers|. |headers| should be raw-
// formatted (NULLs instead of EOLs).
@@ -651,58 +842,10 @@ class ResourceDispatcherHostTest : public testing::Test,
// Intercepts requests for the given protocol.
void HandleScheme(const std::string& scheme) {
- DCHECK(scheme_.empty());
- DCHECK(!old_factory_);
- scheme_ = scheme;
- old_factory_ = net::URLRequest::Deprecated::RegisterProtocolFactory(
- scheme_, &ResourceDispatcherHostTest::Factory);
+ job_factory_->HandleScheme(scheme);
EnsureSchemeIsAllowed(scheme);
}
- // Our own net::URLRequestJob factory.
- static net::URLRequestJob* Factory(net::URLRequest* request,
- net::NetworkDelegate* network_delegate,
- const std::string& scheme) {
- url_request_jobs_created_count_++;
- if (test_fixture_->response_headers_.empty()) {
- if (delay_start_) {
- return new URLRequestTestDelayedStartJob(request, network_delegate);
- } else if (delay_complete_) {
- return new URLRequestTestDelayedCompletionJob(request,
- network_delegate);
- } else if (scheme == "big-job") {
- return new URLRequestBigJob(request, network_delegate);
- } else {
- return new net::URLRequestTestJob(request, network_delegate);
- }
- } else {
- if (delay_start_) {
- return new URLRequestTestDelayedStartJob(
- request, network_delegate,
- test_fixture_->response_headers_, test_fixture_->response_data_,
- false);
- } else if (delay_complete_) {
- return new URLRequestTestDelayedCompletionJob(
- request, network_delegate,
- test_fixture_->response_headers_, test_fixture_->response_data_,
- false);
- } else {
- return new net::URLRequestTestJob(
- request, network_delegate,
- test_fixture_->response_headers_, test_fixture_->response_data_,
- false);
- }
- }
- }
-
- void SetDelayedStartJobGeneration(bool delay_job_start) {
- delay_start_ = delay_job_start;
- }
-
- void SetDelayedCompleteJobGeneration(bool delay_job_complete) {
- delay_complete_ = delay_job_complete;
- }
-
void GenerateDataReceivedACK(const IPC::Message& msg) {
EXPECT_EQ(ResourceMsg_DataReceived::ID, msg.type());
@@ -717,12 +860,24 @@ class ResourceDispatcherHostTest : public testing::Test,
base::Bind(&GenerateIPCMessage, filter_, base::Passed(&ack)));
}
- base::MessageLoopForIO message_loop_;
- BrowserThreadImpl ui_thread_;
- BrowserThreadImpl file_thread_;
- BrowserThreadImpl cache_thread_;
- BrowserThreadImpl io_thread_;
+ // Setting filters for testing renderer messages.
+ // Returns the previous filter.
+ ResourceMessageFilter* SetFilter(ResourceMessageFilter* new_filter) {
+ ResourceMessageFilter* old_filter = host_.filter_;
+ host_.filter_ = new_filter;
+ return old_filter;
+ }
+
+ void WaitForRequestComplete() {
+ DCHECK(!wait_for_request_complete_loop_);
+ wait_for_request_complete_loop_.reset(new base::RunLoop);
+ wait_for_request_complete_loop_->Run();
+ wait_for_request_complete_loop_.reset();
+ }
+
+ content::TestBrowserThreadBundle thread_bundle_;
scoped_ptr<TestBrowserContext> browser_context_;
+ scoped_ptr<TestURLRequestJobFactory> job_factory_;
scoped_refptr<ForwardingFilter> filter_;
net::TestNetworkDelegate network_delegate_;
ResourceDispatcherHostImpl host_;
@@ -733,16 +888,8 @@ class ResourceDispatcherHostTest : public testing::Test,
net::URLRequest::ProtocolFactory* old_factory_;
bool send_data_received_acks_;
std::set<int> child_ids_;
- static ResourceDispatcherHostTest* test_fixture_;
- static bool delay_start_;
- static bool delay_complete_;
- static int url_request_jobs_created_count_;
+ scoped_ptr<base::RunLoop> wait_for_request_complete_loop_;
};
-// Static.
-ResourceDispatcherHostTest* ResourceDispatcherHostTest::test_fixture_ = NULL;
-bool ResourceDispatcherHostTest::delay_start_ = false;
-bool ResourceDispatcherHostTest::delay_complete_ = false;
-int ResourceDispatcherHostTest::url_request_jobs_created_count_ = 0;
void ResourceDispatcherHostTest::MakeTestRequest(int render_view_id,
int request_id,
@@ -757,19 +904,15 @@ void ResourceDispatcherHostTest::MakeTestRequestWithResourceType(
int request_id,
const GURL& url,
ResourceType::Type type) {
- // If it's already there, this'll be dropped on the floor, which is fine.
- child_ids_.insert(filter->child_id());
-
ResourceHostMsg_Request request =
CreateResourceRequest("GET", type, url);
ResourceHostMsg_RequestResource msg(render_view_id, request_id, request);
- bool msg_was_ok;
- host_.OnMessageReceived(msg, filter, &msg_was_ok);
+ host_.OnMessageReceived(msg, filter);
KickOffRequest();
}
void ResourceDispatcherHostTest::CancelRequest(int request_id) {
- host_.CancelRequest(filter_->child_id(), request_id, false);
+ host_.CancelRequest(filter_->child_id(), request_id);
}
void ResourceDispatcherHostTest::CompleteStartRequest(int request_id) {
@@ -903,9 +1046,8 @@ TEST_F(ResourceDispatcherHostTest, TestMany) {
MakeTestRequest(0, 5, net::URLRequestTestJob::test_url_redirect_to_url_2());
// Finish the redirection
- ResourceHostMsg_FollowRedirect redirect_msg(5, false, GURL());
- bool msg_was_ok;
- host_.OnMessageReceived(redirect_msg, filter_.get(), &msg_was_ok);
+ ResourceHostMsg_FollowRedirect redirect_msg(5);
+ host_.OnMessageReceived(redirect_msg, filter_.get());
base::MessageLoop::current()->RunUntilIdle();
// flush all the pending requests
@@ -940,7 +1082,7 @@ TEST_F(ResourceDispatcherHostTest, Cancel) {
// Cancel request must come from the renderer for a detachable resource to
// delay.
- host_.CancelRequest(filter_->child_id(), 4, true);
+ RendererCancelRequest(4);
// The handler should have been detached now.
GlobalRequestID global_request_id(filter_->child_id(), 4);
@@ -965,14 +1107,13 @@ TEST_F(ResourceDispatcherHostTest, Cancel) {
CheckSuccessfulRequest(msgs[2], net::URLRequestTestJob::test_data_3());
// Check that request 2 and 4 got canceled, as far as the renderer is
- // concerned.
- ASSERT_EQ(2U, msgs[1].size());
+ // concerned. Request 2 will have been deleted.
+ ASSERT_EQ(1U, msgs[1].size());
ASSERT_EQ(ResourceMsg_ReceivedResponse::ID, msgs[1][0].type());
- CheckRequestCompleteErrorCode(msgs[1][1], net::ERR_ABORTED);
ASSERT_EQ(2U, msgs[3].size());
ASSERT_EQ(ResourceMsg_ReceivedResponse::ID, msgs[3][0].type());
- CheckRequestCompleteErrorCode(msgs[1][1], net::ERR_ABORTED);
+ CheckRequestCompleteErrorCode(msgs[3][1], net::ERR_ABORTED);
// However, request 4 should have actually gone to completion. (Only request 2
// was canceled.)
@@ -994,7 +1135,8 @@ TEST_F(ResourceDispatcherHostTest, DetachedResourceTimesOut) {
info->detachable_handler()->set_cancel_delay(
base::TimeDelta::FromMilliseconds(200));
base::MessageLoop::current()->RunUntilIdle();
- host_.CancelRequest(filter_->child_id(), 1, true);
+
+ RendererCancelRequest(1);
// From the renderer's perspective, the request was cancelled.
ResourceIPCAccumulator::ClassifiedMessages msgs;
@@ -1031,11 +1173,10 @@ TEST_F(ResourceDispatcherHostTest, DeletedFilterDetached) {
ResourceHostMsg_Request request_ping = CreateResourceRequest(
"GET", ResourceType::PING, net::URLRequestTestJob::test_url_3());
- bool msg_was_ok;
ResourceHostMsg_RequestResource msg_prefetch(0, 1, request_prefetch);
- host_.OnMessageReceived(msg_prefetch, filter_, &msg_was_ok);
+ host_.OnMessageReceived(msg_prefetch, filter_);
ResourceHostMsg_RequestResource msg_ping(0, 2, request_ping);
- host_.OnMessageReceived(msg_ping, filter_, &msg_was_ok);
+ host_.OnMessageReceived(msg_ping, filter_);
// Remove the filter before processing the requests by simulating channel
// closure.
@@ -1083,8 +1224,7 @@ TEST_F(ResourceDispatcherHostTest, DeletedFilterDetachedRedirect) {
net::URLRequestTestJob::test_url_redirect_to_url_2());
ResourceHostMsg_RequestResource msg(0, 1, request);
- bool msg_was_ok;
- host_.OnMessageReceived(msg, filter_, &msg_was_ok);
+ host_.OnMessageReceived(msg, filter_);
// Remove the filter before processing the request by simulating channel
// closure.
@@ -1135,7 +1275,9 @@ TEST_F(ResourceDispatcherHostTest, CancelWhileStartIsDeferred) {
host_.SetDelegate(&delegate);
MakeTestRequest(0, 1, net::URLRequestTestJob::test_url_1());
- CancelRequest(1);
+ // We cancel from the renderer because all non-renderer cancels delete
+ // the request synchronously.
+ RendererCancelRequest(1);
// Our TestResourceThrottle should not have been deleted yet. This is to
// ensure that destruction of the URLRequest happens asynchronously to
@@ -1161,7 +1303,7 @@ TEST_F(ResourceDispatcherHostTest, DetachWhileStartIsDeferred) {
ResourceType::PREFETCH); // detachable type
// Cancel request must come from the renderer for a detachable resource to
// detach.
- host_.CancelRequest(filter_->child_id(), 1, true);
+ RendererCancelRequest(1);
// Even after driving the event loop, the request has not been deleted.
EXPECT_FALSE(was_deleted);
@@ -1208,7 +1350,7 @@ TEST_F(ResourceDispatcherHostTest, CancelInResourceThrottleWillStartRequest) {
CheckRequestCompleteErrorCode(msgs[0][0], net::ERR_ABORTED);
// Make sure URLRequest is never started.
- EXPECT_EQ(0, url_request_jobs_created_count_);
+ EXPECT_EQ(0, job_factory_->url_request_jobs_created_count());
}
TEST_F(ResourceDispatcherHostTest, PausedStartError) {
@@ -1217,7 +1359,7 @@ TEST_F(ResourceDispatcherHostTest, PausedStartError) {
delegate.set_flags(DEFER_PROCESSING_RESPONSE);
host_.SetDelegate(&delegate);
- SetDelayedStartJobGeneration(true);
+ job_factory_->SetDelayedStartJobGeneration(true);
MakeTestRequest(0, 1, net::URLRequestTestJob::test_url_error());
CompleteStartRequest(1);
@@ -1228,6 +1370,33 @@ TEST_F(ResourceDispatcherHostTest, PausedStartError) {
EXPECT_EQ(0, host_.pending_requests());
}
+// Test the OnBeforeNetworkStart throttle.
+TEST_F(ResourceDispatcherHostTest, ThrottleNetworkStart) {
+ // Arrange to have requests deferred before processing response headers.
+ TestResourceDispatcherHostDelegate delegate;
+ delegate.set_flags(DEFER_NETWORK_START);
+ host_.SetDelegate(&delegate);
+
+ job_factory_->SetNetworkStartNotificationJobGeneration(true);
+ MakeTestRequest(0, 1, net::URLRequestTestJob::test_url_2());
+
+ // Should have deferred for network start.
+ GenericResourceThrottle* first_throttle =
+ GenericResourceThrottle::active_throttle();
+ ASSERT_TRUE(first_throttle);
+ EXPECT_EQ(0, network_delegate()->completed_requests());
+ EXPECT_EQ(1, host_.pending_requests());
+
+ first_throttle->Resume();
+
+ // Flush all the pending requests.
+ while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+ base::MessageLoop::current()->RunUntilIdle();
+
+ EXPECT_EQ(1, network_delegate()->completed_requests());
+ EXPECT_EQ(0, host_.pending_requests());
+}
+
TEST_F(ResourceDispatcherHostTest, ThrottleAndResumeTwice) {
// Arrange to have requests deferred before starting.
TestResourceDispatcherHostDelegate delegate;
@@ -1289,36 +1458,11 @@ TEST_F(ResourceDispatcherHostTest, CancelInDelegate) {
CheckRequestCompleteErrorCode(msgs[0][0], net::ERR_ACCESS_DENIED);
}
-// The host delegate acts as a second one so we can have some requests
-// pending and some canceled.
-class TestFilter : public ForwardingFilter {
- public:
- explicit TestFilter(ResourceContext* resource_context)
- : ForwardingFilter(NULL, resource_context),
- has_canceled_(false),
- received_after_canceled_(0) {
- }
-
- // ForwardingFilter override
- virtual bool Send(IPC::Message* msg) OVERRIDE {
- // no messages should be received when the process has been canceled
- if (has_canceled_)
- received_after_canceled_++;
- delete msg;
- return true;
- }
-
- bool has_canceled_;
- int received_after_canceled_;
-
- private:
- virtual ~TestFilter() {}
-};
-
// Tests CancelRequestsForProcess
TEST_F(ResourceDispatcherHostTest, TestProcessCancel) {
scoped_refptr<TestFilter> test_filter = new TestFilter(
browser_context_->GetResourceContext());
+ child_ids_.insert(test_filter->child_id());
// request 1 goes to the test delegate
ResourceHostMsg_Request request = CreateResourceRequest(
@@ -1360,7 +1504,7 @@ TEST_F(ResourceDispatcherHostTest, TestProcessCancel) {
// Cancel the requests to the test process.
host_.CancelRequestsForProcess(filter_->child_id());
- test_filter->has_canceled_ = true;
+ test_filter->set_canceled(true);
// The requests should all be cancelled, except request 4, which is detached.
EXPECT_EQ(1, host_.pending_requests());
@@ -1375,7 +1519,7 @@ TEST_F(ResourceDispatcherHostTest, TestProcessCancel) {
EXPECT_EQ(0, host_.pending_requests());
// The test delegate should not have gotten any messages after being canceled.
- ASSERT_EQ(0, test_filter->received_after_canceled_);
+ ASSERT_EQ(0, test_filter->received_after_canceled());
// There should be two results.
ResourceIPCAccumulator::ClassifiedMessages msgs;
@@ -1535,8 +1679,7 @@ TEST_F(ResourceDispatcherHostTest, TestBlockingCancelingRequests) {
// Tests that blocked requests are canceled if their associated process dies.
TEST_F(ResourceDispatcherHostTest, TestBlockedRequestsProcessDies) {
// This second filter is used to emulate a second process.
- scoped_refptr<ForwardingFilter> second_filter = new ForwardingFilter(
- this, browser_context_->GetResourceContext());
+ scoped_refptr<ForwardingFilter> second_filter = MakeForwardingFilter();
host_.BlockRequestsForRoute(second_filter->child_id(), 0);
@@ -1582,8 +1725,7 @@ TEST_F(ResourceDispatcherHostTest, TestBlockedRequestsProcessDies) {
// destructor to make sure the blocked requests are deleted.
TEST_F(ResourceDispatcherHostTest, TestBlockedRequestsDontLeak) {
// This second filter is used to emulate a second process.
- scoped_refptr<ForwardingFilter> second_filter = new ForwardingFilter(
- this, browser_context_->GetResourceContext());
+ scoped_refptr<ForwardingFilter> second_filter = MakeForwardingFilter();
host_.BlockRequestsForRoute(filter_->child_id(), 1);
host_.BlockRequestsForRoute(filter_->child_id(), 2);
@@ -1667,8 +1809,7 @@ TEST_F(ResourceDispatcherHostTest, TooMuchOutstandingRequestsMemory) {
size_t kMaxRequests = kMaxCostPerProcess / kMemoryCostOfTest2Req;
// This second filter is used to emulate a second process.
- scoped_refptr<ForwardingFilter> second_filter = new ForwardingFilter(
- this, browser_context_->GetResourceContext());
+ scoped_refptr<ForwardingFilter> second_filter = MakeForwardingFilter();
// Saturate the number of outstanding requests for our process.
for (size_t i = 0; i < kMaxRequests; ++i) {
@@ -1736,10 +1877,8 @@ TEST_F(ResourceDispatcherHostTest, TooManyOutstandingRequests) {
host_.set_max_num_in_flight_requests(kMaxRequests);
// Needed to emulate additional processes.
- scoped_refptr<ForwardingFilter> second_filter = new ForwardingFilter(
- this, browser_context_->GetResourceContext());
- scoped_refptr<ForwardingFilter> third_filter = new ForwardingFilter(
- this, browser_context_->GetResourceContext());
+ scoped_refptr<ForwardingFilter> second_filter = MakeForwardingFilter();
+ scoped_refptr<ForwardingFilter> third_filter = MakeForwardingFilter();
// Saturate the number of outstanding requests for our process.
for (size_t i = 0; i < kMaxRequestsPerProcess; ++i) {
@@ -1911,18 +2050,20 @@ TEST_F(ResourceDispatcherHostTest, ForbiddenDownload) {
// Flush all pending requests.
while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+ base::MessageLoop::current()->RunUntilIdle();
// Sorts out all the messages we saw by request.
ResourceIPCAccumulator::ClassifiedMessages msgs;
accum_.GetClassifiedMessages(&msgs);
// We should have gotten one RequestComplete message.
+ ASSERT_EQ(1U, msgs.size());
ASSERT_EQ(1U, msgs[0].size());
EXPECT_EQ(ResourceMsg_RequestComplete::ID, msgs[0][0].type());
// The RequestComplete message should have had the error code of
- // ERR_FILE_NOT_FOUND.
- CheckRequestCompleteErrorCode(msgs[0][0], net::ERR_FILE_NOT_FOUND);
+ // ERR_INVALID_RESPONSE.
+ CheckRequestCompleteErrorCode(msgs[0][0], net::ERR_INVALID_RESPONSE);
}
// Test for http://crbug.com/76202 . We don't want to destroy a
@@ -1946,7 +2087,7 @@ TEST_F(ResourceDispatcherHostTest, IgnoreCancelForDownloads) {
response_data.resize(1025, ' ');
SetResponse(raw_headers, response_data);
- SetDelayedCompleteJobGeneration(true);
+ job_factory_->SetDelayedCompleteJobGeneration(true);
HandleScheme("http");
MakeTestRequestWithResourceType(filter_.get(), render_view_id, request_id,
@@ -1958,8 +2099,7 @@ TEST_F(ResourceDispatcherHostTest, IgnoreCancelForDownloads) {
// And now simulate a cancellation coming from the renderer.
ResourceHostMsg_CancelRequest msg(request_id);
- bool msg_was_ok;
- host_.OnMessageReceived(msg, filter_.get(), &msg_was_ok);
+ host_.OnMessageReceived(msg, filter_.get());
// Since the request had already started processing as a download,
// the cancellation above should have been ignored and the request
@@ -1982,7 +2122,7 @@ TEST_F(ResourceDispatcherHostTest, CancelRequestsForContext) {
response_data.resize(1025, ' ');
SetResponse(raw_headers, response_data);
- SetDelayedCompleteJobGeneration(true);
+ job_factory_->SetDelayedCompleteJobGeneration(true);
HandleScheme("http");
MakeTestRequestWithResourceType(filter_.get(), render_view_id, request_id,
@@ -1994,8 +2134,7 @@ TEST_F(ResourceDispatcherHostTest, CancelRequestsForContext) {
// And now simulate a cancellation coming from the renderer.
ResourceHostMsg_CancelRequest msg(request_id);
- bool msg_was_ok;
- host_.OnMessageReceived(msg, filter_.get(), &msg_was_ok);
+ host_.OnMessageReceived(msg, filter_.get());
// Since the request had already started processing as a download,
// the cancellation above should have been ignored and the request
@@ -2022,7 +2161,7 @@ TEST_F(ResourceDispatcherHostTest, CancelRequestsForContextDetached) {
ResourceType::PREFETCH); // detachable type
// Simulate a cancel coming from the renderer.
- host_.CancelRequest(filter_->child_id(), request_id, true);
+ RendererCancelRequest(request_id);
// Since the request had already started processing as detachable,
// the cancellation above should have been ignored and the request
@@ -2059,13 +2198,11 @@ TEST_F(ResourceDispatcherHostTest, CancelRequestsForContextTransferred) {
GlobalRequestID global_request_id(filter_->child_id(), request_id);
- host_.MarkAsTransferredNavigation(global_request_id,
- GURL("http://example.com/blah"));
+ host_.MarkAsTransferredNavigation(global_request_id);
// And now simulate a cancellation coming from the renderer.
ResourceHostMsg_CancelRequest msg(request_id);
- bool msg_was_ok;
- host_.OnMessageReceived(msg, filter_.get(), &msg_was_ok);
+ host_.OnMessageReceived(msg, filter_.get());
// Since the request is marked as being transferred,
// the cancellation above should have been ignored and the request
@@ -2084,6 +2221,10 @@ TEST_F(ResourceDispatcherHostTest, CancelRequestsForContextTransferred) {
// Test transferred navigations with text/html, which doesn't trigger any
// content sniffing.
TEST_F(ResourceDispatcherHostTest, TransferNavigationHtml) {
+ // This test expects the cross site request to be leaked, so it can transfer
+ // the request directly.
+ CrossSiteResourceHandler::SetLeakRequestsForTesting(true);
+
EXPECT_EQ(0, host_.pending_requests());
int render_view_id = 0;
@@ -2110,9 +2251,8 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationHtml) {
SetResponse("HTTP/1.1 200 OK\n"
"Content-Type: text/html\n\n",
kResponseBody);
- ResourceHostMsg_FollowRedirect redirect_msg(request_id, false, GURL());
- bool msg_was_ok;
- host_.OnMessageReceived(redirect_msg, filter_.get(), &msg_was_ok);
+ ResourceHostMsg_FollowRedirect redirect_msg(request_id);
+ host_.OnMessageReceived(redirect_msg, filter_.get());
base::MessageLoop::current()->RunUntilIdle();
// Flush all the pending requests to get the response through the
@@ -2123,8 +2263,7 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationHtml) {
SetBrowserClientForTesting(old_client);
// This second filter is used to emulate a second process.
- scoped_refptr<ForwardingFilter> second_filter = new ForwardingFilter(
- this, browser_context_->GetResourceContext());
+ scoped_refptr<ForwardingFilter> second_filter = MakeForwardingFilter();
int new_render_view_id = 1;
int new_request_id = 2;
@@ -2135,12 +2274,9 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationHtml) {
request.transferred_request_child_id = filter_->child_id();
request.transferred_request_request_id = request_id;
- // For cleanup.
- child_ids_.insert(second_filter->child_id());
ResourceHostMsg_RequestResource transfer_request_msg(
new_render_view_id, new_request_id, request);
- host_.OnMessageReceived(
- transfer_request_msg, second_filter.get(), &msg_was_ok);
+ host_.OnMessageReceived(transfer_request_msg, second_filter.get());
base::MessageLoop::current()->RunUntilIdle();
// Check generated messages.
@@ -2156,6 +2292,10 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationHtml) {
// BufferedResourceHandler to buffer the response to sniff the content
// before the transfer occurs.
TEST_F(ResourceDispatcherHostTest, TransferNavigationText) {
+ // This test expects the cross site request to be leaked, so it can transfer
+ // the request directly.
+ CrossSiteResourceHandler::SetLeakRequestsForTesting(true);
+
EXPECT_EQ(0, host_.pending_requests());
int render_view_id = 0;
@@ -2184,9 +2324,8 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationText) {
SetResponse("HTTP/1.1 200 OK\n"
"Content-Type: text/plain\n\n",
kResponseBody);
- ResourceHostMsg_FollowRedirect redirect_msg(request_id, false, GURL());
- bool msg_was_ok;
- host_.OnMessageReceived(redirect_msg, filter_.get(), &msg_was_ok);
+ ResourceHostMsg_FollowRedirect redirect_msg(request_id);
+ host_.OnMessageReceived(redirect_msg, filter_.get());
base::MessageLoop::current()->RunUntilIdle();
// Flush all the pending requests to get the response through the
@@ -2197,8 +2336,7 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationText) {
SetBrowserClientForTesting(old_client);
// This second filter is used to emulate a second process.
- scoped_refptr<ForwardingFilter> second_filter = new ForwardingFilter(
- this, browser_context_->GetResourceContext());
+ scoped_refptr<ForwardingFilter> second_filter = MakeForwardingFilter();
int new_render_view_id = 1;
int new_request_id = 2;
@@ -2209,12 +2347,9 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationText) {
request.transferred_request_child_id = filter_->child_id();
request.transferred_request_request_id = request_id;
- // For cleanup.
- child_ids_.insert(second_filter->child_id());
ResourceHostMsg_RequestResource transfer_request_msg(
new_render_view_id, new_request_id, request);
- host_.OnMessageReceived(
- transfer_request_msg, second_filter.get(), &msg_was_ok);
+ host_.OnMessageReceived(transfer_request_msg, second_filter.get());
base::MessageLoop::current()->RunUntilIdle();
// Check generated messages.
@@ -2227,6 +2362,10 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationText) {
}
TEST_F(ResourceDispatcherHostTest, TransferNavigationWithProcessCrash) {
+ // This test expects the cross site request to be leaked, so it can transfer
+ // the request directly.
+ CrossSiteResourceHandler::SetLeakRequestsForTesting(true);
+
EXPECT_EQ(0, host_.pending_requests());
int render_view_id = 0;
@@ -2247,21 +2386,16 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationWithProcessCrash) {
// Create a first filter that can be deleted before the second one starts.
{
- scoped_refptr<ForwardingFilter> first_filter = new ForwardingFilter(
- this, browser_context_->GetResourceContext());
+ scoped_refptr<ForwardingFilter> first_filter = MakeForwardingFilter();
first_child_id = first_filter->child_id();
ResourceHostMsg_Request first_request =
CreateResourceRequest("GET", ResourceType::MAIN_FRAME,
GURL("http://example.com/blah"));
- // For cleanup.
- child_ids_.insert(first_child_id);
ResourceHostMsg_RequestResource first_request_msg(
render_view_id, request_id, first_request);
- bool msg_was_ok;
- host_.OnMessageReceived(
- first_request_msg, first_filter.get(), &msg_was_ok);
+ host_.OnMessageReceived(first_request_msg, first_filter.get());
base::MessageLoop::current()->RunUntilIdle();
// Now that we're blocked on the redirect, update the response and unblock
@@ -2269,8 +2403,8 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationWithProcessCrash) {
SetResponse("HTTP/1.1 200 OK\n"
"Content-Type: text/html\n\n",
kResponseBody);
- ResourceHostMsg_FollowRedirect redirect_msg(request_id, false, GURL());
- host_.OnMessageReceived(redirect_msg, first_filter.get(), &msg_was_ok);
+ ResourceHostMsg_FollowRedirect redirect_msg(request_id);
+ host_.OnMessageReceived(redirect_msg, first_filter.get());
base::MessageLoop::current()->RunUntilIdle();
// Flush all the pending requests to get the response through the
@@ -2286,8 +2420,7 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationWithProcessCrash) {
GlobalRequestID first_global_request_id(first_child_id, request_id);
// This second filter is used to emulate a second process.
- scoped_refptr<ForwardingFilter> second_filter = new ForwardingFilter(
- this, browser_context_->GetResourceContext());
+ scoped_refptr<ForwardingFilter> second_filter = MakeForwardingFilter();
int new_render_view_id = 1;
int new_request_id = 2;
@@ -2302,9 +2435,7 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationWithProcessCrash) {
child_ids_.insert(second_filter->child_id());
ResourceHostMsg_RequestResource transfer_request_msg(
new_render_view_id, new_request_id, request);
- bool msg_was_ok;
- host_.OnMessageReceived(
- transfer_request_msg, second_filter.get(), &msg_was_ok);
+ host_.OnMessageReceived(transfer_request_msg, second_filter.get());
base::MessageLoop::current()->RunUntilIdle();
// Check generated messages.
@@ -2317,6 +2448,10 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationWithProcessCrash) {
}
TEST_F(ResourceDispatcherHostTest, TransferNavigationWithTwoRedirects) {
+ // This test expects the cross site request to be leaked, so it can transfer
+ // the request directly.
+ CrossSiteResourceHandler::SetLeakRequestsForTesting(true);
+
EXPECT_EQ(0, host_.pending_requests());
int render_view_id = 0;
@@ -2340,9 +2475,8 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationWithTwoRedirects) {
// Now that we're blocked on the redirect, simulate hitting another redirect.
SetResponse("HTTP/1.1 302 Found\n"
"Location: http://other.com/blerg\n\n");
- ResourceHostMsg_FollowRedirect redirect_msg(request_id, false, GURL());
- bool msg_was_ok;
- host_.OnMessageReceived(redirect_msg, filter_.get(), &msg_was_ok);
+ ResourceHostMsg_FollowRedirect redirect_msg(request_id);
+ host_.OnMessageReceived(redirect_msg, filter_.get());
base::MessageLoop::current()->RunUntilIdle();
// Now that we're blocked on the second redirect, update the response and
@@ -2353,8 +2487,8 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationWithTwoRedirects) {
SetResponse("HTTP/1.1 200 OK\n"
"Content-Type: text/plain\n\n",
kResponseBody);
- ResourceHostMsg_FollowRedirect redirect_msg2(request_id, false, GURL());
- host_.OnMessageReceived(redirect_msg2, filter_.get(), &msg_was_ok);
+ ResourceHostMsg_FollowRedirect redirect_msg2(request_id);
+ host_.OnMessageReceived(redirect_msg2, filter_.get());
base::MessageLoop::current()->RunUntilIdle();
// Flush all the pending requests to get the response through the
@@ -2365,8 +2499,7 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationWithTwoRedirects) {
SetBrowserClientForTesting(old_client);
// This second filter is used to emulate a second process.
- scoped_refptr<ForwardingFilter> second_filter = new ForwardingFilter(
- this, browser_context_->GetResourceContext());
+ scoped_refptr<ForwardingFilter> second_filter = MakeForwardingFilter();
int new_render_view_id = 1;
int new_request_id = 2;
@@ -2381,8 +2514,7 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationWithTwoRedirects) {
child_ids_.insert(second_filter->child_id());
ResourceHostMsg_RequestResource transfer_request_msg(
new_render_view_id, new_request_id, request);
- host_.OnMessageReceived(
- transfer_request_msg, second_filter.get(), &msg_was_ok);
+ host_.OnMessageReceived(transfer_request_msg, second_filter.get());
// Verify that we update the ResourceRequestInfo.
GlobalRequestID global_request_id(second_filter->child_id(), new_request_id);
@@ -2468,7 +2600,7 @@ TEST_F(ResourceDispatcherHostTest, DataSentBeforeDetach) {
response_data.resize(kAllocSize, ' ');
SetResponse(raw_headers, response_data);
- SetDelayedCompleteJobGeneration(true);
+ job_factory_->SetDelayedCompleteJobGeneration(true);
HandleScheme("http");
MakeTestRequestWithResourceType(filter_.get(), render_view_id, request_id,
@@ -2480,8 +2612,7 @@ TEST_F(ResourceDispatcherHostTest, DataSentBeforeDetach) {
// Simulate a cancellation coming from the renderer.
ResourceHostMsg_CancelRequest msg(request_id);
- bool msg_was_ok;
- host_.OnMessageReceived(msg, filter_.get(), &msg_was_ok);
+ host_.OnMessageReceived(msg, filter_.get());
EXPECT_EQ(1, host_.pending_requests());
@@ -2544,8 +2675,7 @@ TEST_F(ResourceDispatcherHostTest, DelayedDataReceivedACKs) {
EXPECT_EQ(ResourceMsg_DataReceived::ID, msgs[0][i].type());
ResourceHostMsg_DataReceived_ACK msg(1);
- bool msg_was_ok;
- host_.OnMessageReceived(msg, filter_.get(), &msg_was_ok);
+ host_.OnMessageReceived(msg, filter_.get());
}
base::MessageLoop::current()->RunUntilIdle();
@@ -2579,8 +2709,7 @@ TEST_F(ResourceDispatcherHostTest, DataReceivedUnexpectedACKs) {
// Send some unexpected ACKs.
for (size_t i = 0; i < 128; ++i) {
ResourceHostMsg_DataReceived_ACK msg(1);
- bool msg_was_ok;
- host_.OnMessageReceived(msg, filter_.get(), &msg_was_ok);
+ host_.OnMessageReceived(msg, filter_.get());
}
msgs[0].erase(msgs[0].begin());
@@ -2598,8 +2727,7 @@ TEST_F(ResourceDispatcherHostTest, DataReceivedUnexpectedACKs) {
EXPECT_EQ(ResourceMsg_DataReceived::ID, msgs[0][i].type());
ResourceHostMsg_DataReceived_ACK msg(1);
- bool msg_was_ok;
- host_.OnMessageReceived(msg, filter_.get(), &msg_was_ok);
+ host_.OnMessageReceived(msg, filter_.get());
}
base::MessageLoop::current()->RunUntilIdle();
@@ -2609,4 +2737,194 @@ TEST_F(ResourceDispatcherHostTest, DataReceivedUnexpectedACKs) {
}
}
+// Tests the dispatcher host's temporary file management.
+TEST_F(ResourceDispatcherHostTest, RegisterDownloadedTempFile) {
+ const int kRequestID = 1;
+
+ // Create a temporary file.
+ base::FilePath file_path;
+ ASSERT_TRUE(base::CreateTemporaryFile(&file_path));
+ scoped_refptr<ShareableFileReference> deletable_file =
+ ShareableFileReference::GetOrCreate(
+ file_path,
+ ShareableFileReference::DELETE_ON_FINAL_RELEASE,
+ BrowserThread::GetMessageLoopProxyForThread(
+ BrowserThread::FILE).get());
+
+ // Not readable.
+ EXPECT_FALSE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(
+ filter_->child_id(), file_path));
+
+ // Register it for a resource request.
+ host_.RegisterDownloadedTempFile(filter_->child_id(), kRequestID, file_path);
+
+ // Should be readable now.
+ EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(
+ filter_->child_id(), file_path));
+
+ // The child releases from the request.
+ ResourceHostMsg_ReleaseDownloadedFile release_msg(kRequestID);
+ host_.OnMessageReceived(release_msg, filter_);
+
+ // Still readable because there is another reference to the file. (The child
+ // may take additional blob references.)
+ EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(
+ filter_->child_id(), file_path));
+
+ // Release extra references and wait for the file to be deleted. (This relies
+ // on the delete happening on the FILE thread which is mapped to main thread
+ // in this test.)
+ deletable_file = NULL;
+ base::RunLoop().RunUntilIdle();
+
+ // The file is no longer readable to the child and has been deleted.
+ EXPECT_FALSE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(
+ filter_->child_id(), file_path));
+ EXPECT_FALSE(base::PathExists(file_path));
+}
+
+// Tests that temporary files held on behalf of child processes are released
+// when the child process dies.
+TEST_F(ResourceDispatcherHostTest, ReleaseTemporiesOnProcessExit) {
+ const int kRequestID = 1;
+
+ // Create a temporary file.
+ base::FilePath file_path;
+ ASSERT_TRUE(base::CreateTemporaryFile(&file_path));
+ scoped_refptr<ShareableFileReference> deletable_file =
+ ShareableFileReference::GetOrCreate(
+ file_path,
+ ShareableFileReference::DELETE_ON_FINAL_RELEASE,
+ BrowserThread::GetMessageLoopProxyForThread(
+ BrowserThread::FILE).get());
+
+ // Register it for a resource request.
+ host_.RegisterDownloadedTempFile(filter_->child_id(), kRequestID, file_path);
+ deletable_file = NULL;
+
+ // Should be readable now.
+ EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(
+ filter_->child_id(), file_path));
+
+ // Let the process die.
+ filter_->OnChannelClosing();
+ base::RunLoop().RunUntilIdle();
+
+ // The file is no longer readable to the child and has been deleted.
+ EXPECT_FALSE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(
+ filter_->child_id(), file_path));
+ EXPECT_FALSE(base::PathExists(file_path));
+}
+
+TEST_F(ResourceDispatcherHostTest, DownloadToFile) {
+ // Make a request which downloads to file.
+ ResourceHostMsg_Request request = CreateResourceRequest(
+ "GET", ResourceType::SUB_RESOURCE, net::URLRequestTestJob::test_url_1());
+ request.download_to_file = true;
+ ResourceHostMsg_RequestResource request_msg(0, 1, request);
+ host_.OnMessageReceived(request_msg, filter_);
+
+ // Running the message loop until idle does not work because
+ // RedirectToFileResourceHandler posts things to base::WorkerPool. Instead,
+ // wait for the ResourceMsg_RequestComplete to go out. Then run the event loop
+ // until idle so the loader is gone.
+ WaitForRequestComplete();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, host_.pending_requests());
+
+ ResourceIPCAccumulator::ClassifiedMessages msgs;
+ accum_.GetClassifiedMessages(&msgs);
+
+ ASSERT_EQ(1U, msgs.size());
+ const std::vector<IPC::Message>& messages = msgs[0];
+
+ // The request should contain the following messages:
+ // ReceivedResponse (indicates headers received and filename)
+ // DataDownloaded* (bytes downloaded and total length)
+ // RequestComplete (request is done)
+
+ // ReceivedResponse
+ ResourceResponseHead response_head;
+ GetResponseHead(messages, &response_head);
+ ASSERT_FALSE(response_head.download_file_path.empty());
+
+ // DataDownloaded
+ size_t total_len = 0;
+ for (size_t i = 1; i < messages.size() - 1; i++) {
+ ASSERT_EQ(ResourceMsg_DataDownloaded::ID, messages[i].type());
+ PickleIterator iter(messages[i]);
+ int request_id, data_len;
+ ASSERT_TRUE(IPC::ReadParam(&messages[i], &iter, &request_id));
+ ASSERT_TRUE(IPC::ReadParam(&messages[i], &iter, &data_len));
+ total_len += data_len;
+ }
+ EXPECT_EQ(net::URLRequestTestJob::test_data_1().size(), total_len);
+
+ // RequestComplete
+ CheckRequestCompleteErrorCode(messages.back(), net::OK);
+
+ // Verify that the data ended up in the temporary file.
+ std::string contents;
+ ASSERT_TRUE(base::ReadFileToString(response_head.download_file_path,
+ &contents));
+ EXPECT_EQ(net::URLRequestTestJob::test_data_1(), contents);
+
+ // The file should be readable by the child.
+ EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(
+ filter_->child_id(), response_head.download_file_path));
+
+ // When the renderer releases the file, it should be deleted. Again,
+ // RunUntilIdle doesn't work because base::WorkerPool is involved.
+ ShareableFileReleaseWaiter waiter(response_head.download_file_path);
+ ResourceHostMsg_ReleaseDownloadedFile release_msg(1);
+ host_.OnMessageReceived(release_msg, filter_);
+ waiter.Wait();
+ // The release callback runs before the delete is scheduled, so pump the
+ // message loop for the delete itself. (This relies on the delete happening on
+ // the FILE thread which is mapped to main thread in this test.)
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_FALSE(base::PathExists(response_head.download_file_path));
+ EXPECT_FALSE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(
+ filter_->child_id(), response_head.download_file_path));
+}
+
+net::URLRequestJob* TestURLRequestJobFactory::MaybeCreateJobWithProtocolHandler(
+ const std::string& scheme,
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) const {
+ url_request_jobs_created_count_++;
+ if (test_fixture_->response_headers_.empty()) {
+ if (delay_start_) {
+ return new URLRequestTestDelayedStartJob(request, network_delegate);
+ } else if (delay_complete_) {
+ return new URLRequestTestDelayedCompletionJob(request,
+ network_delegate);
+ } else if (network_start_notification_) {
+ return new URLRequestTestDelayedNetworkJob(request, network_delegate);
+ } else if (scheme == "big-job") {
+ return new URLRequestBigJob(request, network_delegate);
+ } else {
+ return new net::URLRequestTestJob(request, network_delegate);
+ }
+ } else {
+ if (delay_start_) {
+ return new URLRequestTestDelayedStartJob(
+ request, network_delegate,
+ test_fixture_->response_headers_, test_fixture_->response_data_,
+ false);
+ } else if (delay_complete_) {
+ return new URLRequestTestDelayedCompletionJob(
+ request, network_delegate,
+ test_fixture_->response_headers_, test_fixture_->response_data_,
+ false);
+ } else {
+ return new net::URLRequestTestJob(
+ request, network_delegate,
+ test_fixture_->response_headers_, test_fixture_->response_data_,
+ false);
+ }
+ }
+}
+
} // namespace content
diff --git a/chromium/content/browser/loader/resource_handler.h b/chromium/content/browser/loader/resource_handler.h
index af8aebdb855..a9d8a38aaa9 100644
--- a/chromium/content/browser/loader/resource_handler.h
+++ b/chromium/content/browser/loader/resource_handler.h
@@ -45,15 +45,13 @@ class CONTENT_EXPORT ResourceHandler
virtual void SetController(ResourceController* controller);
// Called as upload progress is made. The return value is ignored.
- virtual bool OnUploadProgress(int request_id,
- uint64 position,
- uint64 size) = 0;
+ virtual bool OnUploadProgress(uint64 position, uint64 size) = 0;
// The request was redirected to a new URL. |*defer| has an initial value of
// false. Set |*defer| to true to defer the redirect. The redirect may be
// followed later on via ResourceDispatcherHost::FollowDeferredRedirect. If
// the handler returns false, then the request is cancelled.
- virtual bool OnRequestRedirected(int request_id, const GURL& url,
+ virtual bool OnRequestRedirected(const GURL& url,
ResourceResponse* response,
bool* defer) = 0;
@@ -61,42 +59,48 @@ class CONTENT_EXPORT ResourceHandler
// false, then the request is cancelled. Set |*defer| to true to defer
// processing of the response. Call ResourceDispatcherHostImpl::
// ResumeDeferredRequest to continue processing the response.
- virtual bool OnResponseStarted(int request_id,
- ResourceResponse* response,
- bool* defer) = 0;
-
- // Called before the net::URLRequest for |request_id| (whose url is |url|) is
- // to be started. If the handler returns false, then the request is
- // cancelled. Otherwise if the return value is true, the ResourceHandler can
- // delay the request from starting by setting |*defer = true|. A deferred
- // request will not have called net::URLRequest::Start(), and will not resume
- // until someone calls ResourceDispatcherHost::StartDeferredRequest().
- virtual bool OnWillStart(int request_id, const GURL& url, bool* defer) = 0;
+ virtual bool OnResponseStarted(ResourceResponse* response, bool* defer) = 0;
+
+ // Called before the net::URLRequest (whose url is |url|) is to be started.
+ // If the handler returns false, then the request is cancelled. Otherwise if
+ // the return value is true, the ResourceHandler can delay the request from
+ // starting by setting |*defer = true|. A deferred request will not have
+ // called net::URLRequest::Start(), and will not resume until someone calls
+ // ResourceDispatcherHost::StartDeferredRequest().
+ virtual bool OnWillStart(const GURL& url, bool* defer) = 0;
+
+ // Called before the net::URLRequest (whose url is |url|} uses the network for
+ // the first time to load the resource. If the handler returns false, then the
+ // request is cancelled. Otherwise if the return value is true, the
+ // ResourceHandler can delay the request from starting by setting |*defer =
+ // true|. Call controller()->Resume() to continue if deferred.
+ virtual bool OnBeforeNetworkStart(const GURL& url, bool* defer) = 0;
// Data will be read for the response. Upon success, this method places the
// size and address of the buffer where the data is to be written in its
- // out-params. This call will be followed by either OnReadCompleted or
- // OnResponseCompleted, at which point the buffer may be recycled.
+ // out-params. This call will be followed by either OnReadCompleted (on
+ // successful read or EOF) or OnResponseCompleted (on error). If
+ // OnReadCompleted is called, the buffer may be recycled. Otherwise, it may
+ // not be recycled and may potentially outlive the handler. If |min_size| is
+ // not -1, it is the minimum size of the returned buffer.
//
// If the handler returns false, then the request is cancelled. Otherwise,
// once data is available, OnReadCompleted will be called.
- virtual bool OnWillRead(int request_id,
- scoped_refptr<net::IOBuffer>* buf,
+ virtual bool OnWillRead(scoped_refptr<net::IOBuffer>* buf,
int* buf_size,
int min_size) = 0;
// Data (*bytes_read bytes) was written into the buffer provided by
// OnWillRead. A return value of false cancels the request, true continues
// reading data. Set |*defer| to true to defer reading more response data.
- // Call controller()->Resume() to continue reading response data.
- virtual bool OnReadCompleted(int request_id, int bytes_read,
- bool* defer) = 0;
+ // Call controller()->Resume() to continue reading response data. A zero
+ // |bytes_read| signals that no further data is available.
+ virtual bool OnReadCompleted(int bytes_read, bool* defer) = 0;
// The response is complete. The final response status is given. Set
// |*defer| to true to defer destruction to a later time. Otherwise, the
// request will be destroyed upon return.
- virtual void OnResponseCompleted(int request_id,
- const net::URLRequestStatus& status,
+ virtual void OnResponseCompleted(const net::URLRequestStatus& status,
const std::string& security_info,
bool* defer) = 0;
@@ -104,7 +108,7 @@ class CONTENT_EXPORT ResourceHandler
// to indicate progress of 'download_to_file' requests. OnReadCompleted
// calls are consumed by the RedirectToFileResourceHandler and replaced
// with OnDataDownloaded calls.
- virtual void OnDataDownloaded(int request_id, int bytes_downloaded) = 0;
+ virtual void OnDataDownloaded(int bytes_downloaded) = 0;
protected:
ResourceHandler(net::URLRequest* request);
diff --git a/chromium/content/browser/loader/resource_loader.cc b/chromium/content/browser/loader/resource_loader.cc
index bdc9644b1dd..0becbf42a3d 100644
--- a/chromium/content/browser/loader/resource_loader.cc
+++ b/chromium/content/browser/loader/resource_loader.cc
@@ -8,6 +8,7 @@
#include "base/message_loop/message_loop.h"
#include "base/metrics/histogram.h"
#include "base/time/time.h"
+#include "content/browser/appcache/appcache_interceptor.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/loader/cross_site_resource_handler.h"
#include "content/browser/loader/detachable_resource_handler.h"
@@ -29,7 +30,6 @@
#include "net/http/http_response_headers.h"
#include "net/ssl/client_cert_store.h"
#include "net/url_request/url_request_status.h"
-#include "webkit/browser/appcache/appcache_interceptor.h"
using base::TimeDelta;
using base::TimeTicks;
@@ -54,7 +54,7 @@ void PopulateResourceResponse(net::URLRequest* request,
response->head.connection_info = response_info.connection_info;
response->head.was_fetched_via_proxy = request->was_fetched_via_proxy();
response->head.socket_address = request->GetSocketAddress();
- appcache::AppCacheInterceptor::GetExtraResponseInfo(
+ AppCacheInterceptor::GetExtraResponseInfo(
request,
&response->head.appcache_id,
&response->head.appcache_manifest_url);
@@ -99,8 +99,7 @@ void ResourceLoader::StartRequest() {
// Give the handler a chance to delay the URLRequest from being started.
bool defer_start = false;
- if (!handler_->OnWillStart(GetRequestInfo()->GetRequestID(), request_->url(),
- &defer_start)) {
+ if (!handler_->OnWillStart(request_->url(), &defer_start)) {
Cancel();
return;
}
@@ -127,8 +126,6 @@ void ResourceLoader::CancelWithError(int error_code) {
}
void ResourceLoader::ReportUploadProgress() {
- ResourceRequestInfoImpl* info = GetRequestInfo();
-
if (waiting_for_upload_progress_ack_)
return; // Send one progress event at a time.
@@ -152,8 +149,7 @@ void ResourceLoader::ReportUploadProgress() {
if (is_finished || enough_new_progress || too_much_time_passed) {
if (request_->load_flags() & net::LOAD_ENABLE_UPLOAD_PROGRESS) {
- handler_->OnUploadProgress(
- info->GetRequestID(), progress.position(), progress.size());
+ handler_->OnUploadProgress(progress.position(), progress.size());
waiting_for_upload_progress_ack_ = true;
}
last_upload_ticks_ = TimeTicks::Now();
@@ -161,18 +157,19 @@ void ResourceLoader::ReportUploadProgress() {
}
}
-void ResourceLoader::MarkAsTransferring(const GURL& target_url) {
- CHECK_EQ(GetRequestInfo()->GetResourceType(), ResourceType::MAIN_FRAME)
- << "Cannot transfer non-main frame navigations";
+void ResourceLoader::MarkAsTransferring() {
+ CHECK(ResourceType::IsFrame(GetRequestInfo()->GetResourceType()))
+ << "Can only transfer for navigations";
is_transferring_ = true;
-
- // When transferring a request to another process, the renderer doesn't get
- // a chance to update the cookie policy URL. Do it here instead.
- request()->set_first_party_for_cookies(target_url);
}
void ResourceLoader::CompleteTransfer() {
- DCHECK_EQ(DEFERRED_READ, deferred_stage_);
+ // Although CrossSiteResourceHandler defers at OnResponseStarted
+ // (DEFERRED_READ), it may be seeing a replay of events via
+ // BufferedResourceHandler, and so the request itself is actually deferred at
+ // a later read stage.
+ DCHECK(DEFERRED_READ == deferred_stage_ ||
+ DEFERRED_RESPONSE_COMPLETE == deferred_stage_);
is_transferring_ = false;
GetRequestInfo()->cross_site_handler()->ResumeResponse();
@@ -204,7 +201,7 @@ void ResourceLoader::OnReceivedRedirect(net::URLRequest* unused,
ResourceRequestInfoImpl* info = GetRequestInfo();
- if (info->process_type() != PROCESS_TYPE_PLUGIN &&
+ if (info->GetProcessType() != PROCESS_TYPE_PLUGIN &&
!ChildProcessSecurityPolicyImpl::GetInstance()->
CanRequestURL(info->GetChildID(), new_url)) {
VLOG(1) << "Denied unauthorized request for "
@@ -226,8 +223,7 @@ void ResourceLoader::OnReceivedRedirect(net::URLRequest* unused,
scoped_refptr<ResourceResponse> response(new ResourceResponse());
PopulateResourceResponse(request_.get(), response.get());
- if (!handler_->OnRequestRedirected(
- info->GetRequestID(), new_url, response.get(), defer)) {
+ if (!handler_->OnRequestRedirected(new_url, response.get(), defer)) {
Cancel();
} else if (*defer) {
deferred_stage_ = DEFERRED_REDIRECT; // Follow redirect when resumed.
@@ -243,11 +239,6 @@ void ResourceLoader::OnAuthRequired(net::URLRequest* unused,
return;
}
- if (!delegate_->AcceptAuthRequest(this, auth_info)) {
- request_->CancelAuth();
- return;
- }
-
// Create a login dialog on the UI thread to get authentication data, or pull
// from cache and continue on the IO thread.
@@ -263,7 +254,7 @@ void ResourceLoader::OnCertificateRequested(
net::SSLCertRequestInfo* cert_info) {
DCHECK_EQ(request_.get(), unused);
- if (!delegate_->AcceptSSLClientCertificateRequest(this, cert_info)) {
+ if (request_->load_flags() & net::LOAD_PREFETCH) {
request_->Cancel();
return;
}
@@ -283,8 +274,8 @@ void ResourceLoader::OnSSLCertificateError(net::URLRequest* request,
ResourceRequestInfoImpl* info = GetRequestInfo();
int render_process_id;
- int render_view_id;
- if (!info->GetAssociatedRenderView(&render_process_id, &render_view_id))
+ int render_frame_id;
+ if (!info->GetAssociatedRenderFrame(&render_process_id, &render_frame_id))
NOTREACHED();
SSLManager::OnSSLCertificateError(
@@ -293,11 +284,24 @@ void ResourceLoader::OnSSLCertificateError(net::URLRequest* request,
info->GetResourceType(),
request_->url(),
render_process_id,
- render_view_id,
+ render_frame_id,
ssl_info,
fatal);
}
+void ResourceLoader::OnBeforeNetworkStart(net::URLRequest* unused,
+ bool* defer) {
+ DCHECK_EQ(request_.get(), unused);
+
+ // Give the handler a chance to delay the URLRequest from using the network.
+ if (!handler_->OnBeforeNetworkStart(request_->url(), defer)) {
+ Cancel();
+ return;
+ } else if (*defer) {
+ deferred_stage_ = DEFERRED_NETWORK_START;
+ }
+}
+
void ResourceLoader::OnResponseStarted(net::URLRequest* unused) {
DCHECK_EQ(request_.get(), unused);
@@ -352,12 +356,21 @@ void ResourceLoader::OnReadCompleted(net::URLRequest* unused, int bytes_read) {
CompleteRead(bytes_read);
- if (is_deferred())
+ // If the handler cancelled or deferred the request, do not continue
+ // processing the read. If cancelled, the URLRequest has already been
+ // cancelled and will schedule an erroring OnReadCompleted later. If deferred,
+ // do nothing until resumed.
+ //
+ // Note: if bytes_read is 0 (EOF) and the handler defers, resumption will call
+ // ResponseCompleted().
+ if (is_deferred() || !request_->status().is_success())
return;
- if (request_->status().is_success() && bytes_read > 0) {
+ if (bytes_read > 0) {
StartReading(true); // Read the next chunk.
} else {
+ // URLRequest reported an EOF. Call ResponseCompleted.
+ DCHECK_EQ(0, bytes_read);
ResponseCompleted();
}
}
@@ -400,6 +413,9 @@ void ResourceLoader::Resume() {
case DEFERRED_START:
StartRequestInternal();
break;
+ case DEFERRED_NETWORK_START:
+ request_->ResumeNetworkStart();
+ break;
case DEFERRED_REDIRECT:
request_->FollowDeferredRedirect();
break;
@@ -409,6 +425,12 @@ void ResourceLoader::Resume() {
base::Bind(&ResourceLoader::ResumeReading,
weak_ptr_factory_.GetWeakPtr()));
break;
+ case DEFERRED_RESPONSE_COMPLETE:
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&ResourceLoader::ResponseCompleted,
+ weak_ptr_factory_.GetWeakPtr()));
+ break;
case DEFERRED_FINISH:
// Delay self-destruction since we don't know how we were reached.
base::MessageLoop::current()->PostTask(
@@ -425,6 +447,11 @@ void ResourceLoader::Cancel() {
void ResourceLoader::StartRequestInternal() {
DCHECK(!request_->is_pending());
+
+ if (!request_->status().is_success()) {
+ return;
+ }
+
request_->Start();
delegate_->DidStartRequest(this);
@@ -483,9 +510,9 @@ void ResourceLoader::StoreSignedCertificateTimestamps(
for (net::SignedCertificateTimestampAndStatusList::const_iterator iter =
sct_list.begin(); iter != sct_list.end(); ++iter) {
- const int sct_id(sct_store->Store(iter->sct_, process_id));
+ const int sct_id(sct_store->Store(iter->sct, process_id));
sct_ids->push_back(
- SignedCertificateTimestampIDAndStatus(sct_id, iter->status_));
+ SignedCertificateTimestampIDAndStatus(sct_id, iter->status));
}
}
@@ -521,8 +548,7 @@ void ResourceLoader::CompleteResponseStarted() {
delegate_->DidReceiveResponse(this);
bool defer = false;
- if (!handler_->OnResponseStarted(
- info->GetRequestID(), response.get(), &defer)) {
+ if (!handler_->OnResponseStarted(response.get(), &defer)) {
Cancel();
} else if (defer) {
read_deferral_start_time_ = base::TimeTicks::Now();
@@ -568,7 +594,6 @@ void ResourceLoader::ResumeReading() {
}
void ResourceLoader::ReadMore(int* bytes_read) {
- ResourceRequestInfoImpl* info = GetRequestInfo();
DCHECK(!is_deferred());
// Make sure we track the buffer in at least one place. This ensures it gets
@@ -576,7 +601,7 @@ void ResourceLoader::ReadMore(int* bytes_read) {
// doesn't use the buffer.
scoped_refptr<net::IOBuffer> buf;
int buf_size;
- if (!handler_->OnWillRead(info->GetRequestID(), &buf, &buf_size, -1)) {
+ if (!handler_->OnWillRead(&buf, &buf_size, -1)) {
Cancel();
return;
}
@@ -594,14 +619,18 @@ void ResourceLoader::CompleteRead(int bytes_read) {
DCHECK(bytes_read >= 0);
DCHECK(request_->status().is_success());
- ResourceRequestInfoImpl* info = GetRequestInfo();
-
bool defer = false;
- if (!handler_->OnReadCompleted(info->GetRequestID(), bytes_read, &defer)) {
+ if (!handler_->OnReadCompleted(bytes_read, &defer)) {
Cancel();
} else if (defer) {
- deferred_stage_ = DEFERRED_READ; // Read next chunk when resumed.
+ deferred_stage_ =
+ bytes_read > 0 ? DEFERRED_READ : DEFERRED_RESPONSE_COMPLETE;
}
+
+ // Note: the request may still have been cancelled while OnReadCompleted
+ // returns true if OnReadCompleted caused request to get cancelled
+ // out-of-band. (In AwResourceDispatcherHostDelegate::DownloadStarting, for
+ // instance.)
}
void ResourceLoader::ResponseCompleted() {
@@ -625,8 +654,7 @@ void ResourceLoader::ResponseCompleted() {
}
bool defer = false;
- handler_->OnResponseCompleted(info->GetRequestID(), request_->status(),
- security_info, &defer);
+ handler_->OnResponseCompleted(request_->status(), security_info, &defer);
if (defer) {
// The handler is not ready to die yet. We will call DidFinishLoading when
// we resume.
diff --git a/chromium/content/browser/loader/resource_loader.h b/chromium/content/browser/loader/resource_loader.h
index d0b3ae806ce..d06f58ca617 100644
--- a/chromium/content/browser/loader/resource_loader.h
+++ b/chromium/content/browser/loader/resource_loader.h
@@ -39,7 +39,7 @@ class CONTENT_EXPORT ResourceLoader : public net::URLRequest::Delegate,
void ReportUploadProgress();
bool is_transferring() const { return is_transferring_; }
- void MarkAsTransferring(const GURL& target_url);
+ void MarkAsTransferring();
void CompleteTransfer();
net::URLRequest* request() { return request_.get(); }
@@ -66,6 +66,8 @@ class CONTENT_EXPORT ResourceLoader : public net::URLRequest::Delegate,
virtual void OnSSLCertificateError(net::URLRequest* request,
const net::SSLInfo& info,
bool fatal) OVERRIDE;
+ virtual void OnBeforeNetworkStart(net::URLRequest* request,
+ bool* defer) OVERRIDE;
virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE;
virtual void OnReadCompleted(net::URLRequest* request,
int bytes_read) OVERRIDE;
@@ -96,6 +98,7 @@ class CONTENT_EXPORT ResourceLoader : public net::URLRequest::Delegate,
void StartReading(bool is_continuation);
void ResumeReading();
void ReadMore(int* bytes_read);
+ // Passes a read result to the handler.
void CompleteRead(int bytes_read);
void ResponseCompleted();
void CallDidFinishLoading();
@@ -117,8 +120,10 @@ class CONTENT_EXPORT ResourceLoader : public net::URLRequest::Delegate,
enum DeferredStage {
DEFERRED_NONE,
DEFERRED_START,
+ DEFERRED_NETWORK_START,
DEFERRED_REDIRECT,
DEFERRED_READ,
+ DEFERRED_RESPONSE_COMPLETE,
DEFERRED_FINISH
};
DeferredStage deferred_stage_;
diff --git a/chromium/content/browser/loader/resource_loader_delegate.h b/chromium/content/browser/loader/resource_loader_delegate.h
index 871debc3e45..38c8a38bf7a 100644
--- a/chromium/content/browser/loader/resource_loader_delegate.h
+++ b/chromium/content/browser/loader/resource_loader_delegate.h
@@ -9,7 +9,6 @@
namespace net {
class AuthChallengeInfo;
-class SSLCertRequestInfo;
}
namespace content {
@@ -22,12 +21,6 @@ class CONTENT_EXPORT ResourceLoaderDelegate {
ResourceLoader* loader,
net::AuthChallengeInfo* auth_info) = 0;
- virtual bool AcceptAuthRequest(ResourceLoader* loader,
- net::AuthChallengeInfo* auth_info) = 0;
- virtual bool AcceptSSLClientCertificateRequest(
- ResourceLoader* loader,
- net::SSLCertRequestInfo* cert_info) = 0;
-
virtual bool HandleExternalProtocol(ResourceLoader* loader,
const GURL& url) = 0;
diff --git a/chromium/content/browser/loader/resource_loader_unittest.cc b/chromium/content/browser/loader/resource_loader_unittest.cc
index 8f962118a79..d2fdd99b3ba 100644
--- a/chromium/content/browser/loader/resource_loader_unittest.cc
+++ b/chromium/content/browser/loader/resource_loader_unittest.cc
@@ -4,20 +4,33 @@
#include "content/browser/loader/resource_loader.h"
+#include "base/file_util.h"
+#include "base/files/file.h"
+#include "base/message_loop/message_loop_proxy.h"
#include "base/run_loop.h"
#include "content/browser/browser_thread_impl.h"
+#include "content/browser/loader/redirect_to_file_resource_handler.h"
#include "content/browser/loader/resource_loader_delegate.h"
#include "content/public/browser/resource_request_info.h"
+#include "content/public/common/resource_response.h"
#include "content/public/test/mock_resource_context.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "content/test/test_content_browser_client.h"
+#include "ipc/ipc_message.h"
+#include "net/base/io_buffer.h"
+#include "net/base/mock_file_stream.h"
#include "net/base/request_priority.h"
#include "net/cert/x509_certificate.h"
#include "net/ssl/client_cert_store.h"
#include "net/ssl/ssl_cert_request_info.h"
#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_job_factory_impl.h"
+#include "net/url_request/url_request_test_job.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/common/blob/shareable_file_reference.h"
+
+using webkit_blob::ShareableFileReference;
namespace content {
namespace {
@@ -64,56 +77,150 @@ class ClientCertStoreStub : public net::ClientCertStore {
std::vector<std::string> requested_authorities_;
};
+// Arbitrary read buffer size.
+const int kReadBufSize = 1024;
+
// Dummy implementation of ResourceHandler, instance of which is needed to
// initialize ResourceLoader.
class ResourceHandlerStub : public ResourceHandler {
public:
- ResourceHandlerStub() : ResourceHandler(NULL) {}
+ explicit ResourceHandlerStub(net::URLRequest* request)
+ : ResourceHandler(request),
+ read_buffer_(new net::IOBuffer(kReadBufSize)),
+ defer_request_on_will_start_(false),
+ expect_reads_(true),
+ cancel_on_read_completed_(false),
+ defer_eof_(false),
+ received_on_will_read_(false),
+ received_eof_(false),
+ received_response_completed_(false),
+ total_bytes_downloaded_(0) {
+ }
+
+ // If true, defers the resource load in OnWillStart.
+ void set_defer_request_on_will_start(bool defer_request_on_will_start) {
+ defer_request_on_will_start_ = defer_request_on_will_start;
+ }
+
+ // If true, expect OnWillRead / OnReadCompleted pairs for handling
+ // data. Otherwise, expect OnDataDownloaded.
+ void set_expect_reads(bool expect_reads) { expect_reads_ = expect_reads; }
+
+ // If true, cancel the request in OnReadCompleted by returning false.
+ void set_cancel_on_read_completed(bool cancel_on_read_completed) {
+ cancel_on_read_completed_ = cancel_on_read_completed;
+ }
- virtual bool OnUploadProgress(int request_id,
- uint64 position,
- uint64 size) OVERRIDE {
+ // If true, cancel the request in OnReadCompleted by returning false.
+ void set_defer_eof(bool defer_eof) { defer_eof_ = defer_eof; }
+
+ const GURL& start_url() const { return start_url_; }
+ ResourceResponse* response() const { return response_.get(); }
+ bool received_response_completed() const {
+ return received_response_completed_;
+ }
+ const net::URLRequestStatus& status() const { return status_; }
+ int total_bytes_downloaded() const { return total_bytes_downloaded_; }
+
+ void Resume() {
+ controller()->Resume();
+ }
+
+ // ResourceHandler implementation:
+ virtual bool OnUploadProgress(uint64 position, uint64 size) OVERRIDE {
+ NOTREACHED();
return true;
}
- virtual bool OnRequestRedirected(int request_id,
- const GURL& url,
+ virtual bool OnRequestRedirected(const GURL& url,
ResourceResponse* response,
bool* defer) OVERRIDE {
+ NOTREACHED();
+ return true;
+ }
+
+ virtual bool OnResponseStarted(ResourceResponse* response,
+ bool* defer) OVERRIDE {
+ EXPECT_FALSE(response_);
+ response_ = response;
return true;
}
- virtual bool OnResponseStarted(int request_id,
- ResourceResponse* response,
- bool* defer) OVERRIDE { return true; }
+ virtual bool OnWillStart(const GURL& url, bool* defer) OVERRIDE {
+ EXPECT_TRUE(start_url_.is_empty());
+ start_url_ = url;
+ *defer = defer_request_on_will_start_;
+ return true;
+ }
- virtual bool OnWillStart(int request_id,
- const GURL& url,
- bool* defer) OVERRIDE {
+ virtual bool OnBeforeNetworkStart(const GURL& url, bool* defer) OVERRIDE {
return true;
}
- virtual bool OnWillRead(int request_id,
- scoped_refptr<net::IOBuffer>* buf,
+ virtual bool OnWillRead(scoped_refptr<net::IOBuffer>* buf,
int* buf_size,
int min_size) OVERRIDE {
+ EXPECT_TRUE(expect_reads_);
+ EXPECT_FALSE(received_on_will_read_);
+ EXPECT_FALSE(received_eof_);
+ EXPECT_FALSE(received_response_completed_);
+
+ *buf = read_buffer_;
+ *buf_size = kReadBufSize;
+ received_on_will_read_ = true;
return true;
}
- virtual bool OnReadCompleted(int request_id,
- int bytes_read,
- bool* defer) OVERRIDE {
- return true;
+ virtual bool OnReadCompleted(int bytes_read, bool* defer) OVERRIDE {
+ EXPECT_TRUE(received_on_will_read_);
+ EXPECT_TRUE(expect_reads_);
+ EXPECT_FALSE(received_response_completed_);
+
+ if (bytes_read == 0) {
+ received_eof_ = true;
+ if (defer_eof_) {
+ defer_eof_ = false;
+ *defer = true;
+ }
+ }
+
+ // Need another OnWillRead() call before seeing an OnReadCompleted().
+ received_on_will_read_ = false;
+
+ return !cancel_on_read_completed_;
}
- virtual void OnResponseCompleted(int request_id,
- const net::URLRequestStatus& status,
+ virtual void OnResponseCompleted(const net::URLRequestStatus& status,
const std::string& security_info,
bool* defer) OVERRIDE {
+ EXPECT_FALSE(received_response_completed_);
+ if (status.is_success() && expect_reads_)
+ EXPECT_TRUE(received_eof_);
+
+ received_response_completed_ = true;
+ status_ = status;
}
- virtual void OnDataDownloaded(int request_id,
- int bytes_downloaded) OVERRIDE {}
+ virtual void OnDataDownloaded(int bytes_downloaded) OVERRIDE {
+ EXPECT_FALSE(expect_reads_);
+ total_bytes_downloaded_ += bytes_downloaded;
+ }
+
+ private:
+ scoped_refptr<net::IOBuffer> read_buffer_;
+
+ bool defer_request_on_will_start_;
+ bool expect_reads_;
+ bool cancel_on_read_completed_;
+ bool defer_eof_;
+
+ GURL start_url_;
+ scoped_refptr<ResourceResponse> response_;
+ bool received_on_will_read_;
+ bool received_eof_;
+ bool received_response_completed_;
+ net::URLRequestStatus status_;
+ int total_bytes_downloaded_;
};
// Test browser client that captures calls to SelectClientCertificates and
@@ -162,6 +269,16 @@ class ResourceContextStub : public MockResourceContext {
scoped_ptr<net::ClientCertStore> dummy_cert_store_;
};
+// Fails to create a temporary file with the given error.
+void CreateTemporaryError(
+ base::File::Error error,
+ const CreateTemporaryFileStreamCallback& callback) {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, error, base::Passed(scoped_ptr<net::FileStream>()),
+ scoped_refptr<ShareableFileReference>()));
+}
+
} // namespace
class ResourceLoaderTest : public testing::Test,
@@ -169,7 +286,52 @@ class ResourceLoaderTest : public testing::Test,
protected:
ResourceLoaderTest()
: thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
- resource_context_(&test_url_request_context_) {
+ resource_context_(&test_url_request_context_),
+ raw_ptr_resource_handler_(NULL),
+ raw_ptr_to_request_(NULL) {
+ job_factory_.SetProtocolHandler(
+ "test", net::URLRequestTestJob::CreateProtocolHandler());
+ test_url_request_context_.set_job_factory(&job_factory_);
+ }
+
+ GURL test_url() const {
+ return net::URLRequestTestJob::test_url_1();
+ }
+
+ std::string test_data() const {
+ return net::URLRequestTestJob::test_data_1();
+ }
+
+ virtual scoped_ptr<ResourceHandler> WrapResourceHandler(
+ scoped_ptr<ResourceHandlerStub> leaf_handler,
+ net::URLRequest* request) {
+ return leaf_handler.PassAs<ResourceHandler>();
+ }
+
+ virtual void SetUp() OVERRIDE {
+ const int kRenderProcessId = 1;
+ const int kRenderViewId = 2;
+
+ scoped_ptr<net::URLRequest> request(
+ new net::URLRequest(test_url(),
+ net::DEFAULT_PRIORITY,
+ NULL,
+ resource_context_.GetRequestContext()));
+ raw_ptr_to_request_ = request.get();
+ ResourceRequestInfo::AllocateForTesting(request.get(),
+ ResourceType::MAIN_FRAME,
+ &resource_context_,
+ kRenderProcessId,
+ kRenderViewId,
+ MSG_ROUTING_NONE,
+ false);
+ scoped_ptr<ResourceHandlerStub> resource_handler(
+ new ResourceHandlerStub(request.get()));
+ raw_ptr_resource_handler_ = resource_handler.get();
+ loader_.reset(new ResourceLoader(
+ request.Pass(),
+ WrapResourceHandler(resource_handler.Pass(), raw_ptr_to_request_),
+ this));
}
// ResourceLoaderDelegate:
@@ -178,16 +340,6 @@ class ResourceLoaderTest : public testing::Test,
net::AuthChallengeInfo* auth_info) OVERRIDE {
return NULL;
}
- virtual bool AcceptAuthRequest(
- ResourceLoader* loader,
- net::AuthChallengeInfo* auth_info) OVERRIDE {
- return false;
- };
- virtual bool AcceptSSLClientCertificateRequest(
- ResourceLoader* loader,
- net::SSLCertRequestInfo* cert_info) OVERRIDE {
- return true;
- }
virtual bool HandleExternalProtocol(ResourceLoader* loader,
const GURL& url) OVERRIDE {
return false;
@@ -200,8 +352,14 @@ class ResourceLoaderTest : public testing::Test,
content::TestBrowserThreadBundle thread_bundle_;
+ net::URLRequestJobFactoryImpl job_factory_;
net::TestURLRequestContext test_url_request_context_;
ResourceContextStub resource_context_;
+
+ // The ResourceLoader owns the URLRequest and the ResourceHandler.
+ ResourceHandlerStub* raw_ptr_resource_handler_;
+ net::URLRequest* raw_ptr_to_request_;
+ scoped_ptr<ResourceLoader> loader_;
};
// Verifies if a call to net::UrlRequest::Delegate::OnCertificateRequested()
@@ -209,21 +367,6 @@ class ResourceLoaderTest : public testing::Test,
// certificates are correctly passed to the content browser client for
// selection.
TEST_F(ResourceLoaderTest, ClientCertStoreLookup) {
- const int kRenderProcessId = 1;
- const int kRenderViewId = 2;
-
- scoped_ptr<net::URLRequest> request(
- new net::URLRequest(GURL("dummy"),
- net::DEFAULT_PRIORITY,
- NULL,
- resource_context_.GetRequestContext()));
- ResourceRequestInfo::AllocateForTesting(request.get(),
- ResourceType::MAIN_FRAME,
- &resource_context_,
- kRenderProcessId,
- kRenderViewId,
- false);
-
// Set up the test client cert store.
net::CertificateList dummy_certs(1, scoped_refptr<net::X509Certificate>(
new net::X509Certificate("test", "test", base::Time(), base::Time())));
@@ -231,17 +374,12 @@ TEST_F(ResourceLoaderTest, ClientCertStoreLookup) {
new ClientCertStoreStub(dummy_certs));
EXPECT_EQ(0, test_store->request_count());
- // Ownership of the |request| and |test_store| is about to be turned over to
- // ResourceLoader. We need to keep raw pointer copies to access these objects
- // later.
- net::URLRequest* raw_ptr_to_request = request.get();
+ // Ownership of the |test_store| is about to be turned over to ResourceLoader.
+ // We need to keep raw pointer copies to access these objects later.
ClientCertStoreStub* raw_ptr_to_store = test_store.get();
resource_context_.SetClientCertStore(
test_store.PassAs<net::ClientCertStore>());
- scoped_ptr<ResourceHandler> resource_handler(new ResourceHandlerStub());
- ResourceLoader loader(request.Pass(), resource_handler.Pass(), this);
-
// Prepare a dummy certificate request.
scoped_refptr<net::SSLCertRequestInfo> cert_request_info(
new net::SSLCertRequestInfo());
@@ -254,7 +392,7 @@ TEST_F(ResourceLoaderTest, ClientCertStoreLookup) {
// Everything is set up. Trigger the resource loader certificate request event
// and run the message loop.
- loader.OnCertificateRequested(raw_ptr_to_request, cert_request_info.get());
+ loader_->OnCertificateRequested(raw_ptr_to_request_, cert_request_info.get());
base::RunLoop().RunUntilIdle();
// Restore the original content browser client.
@@ -274,28 +412,6 @@ TEST_F(ResourceLoaderTest, ClientCertStoreLookup) {
// on a platform with a NULL client cert store still calls the content browser
// client for selection.
TEST_F(ResourceLoaderTest, ClientCertStoreNull) {
- const int kRenderProcessId = 1;
- const int kRenderViewId = 2;
-
- scoped_ptr<net::URLRequest> request(
- new net::URLRequest(GURL("dummy"),
- net::DEFAULT_PRIORITY,
- NULL,
- resource_context_.GetRequestContext()));
- ResourceRequestInfo::AllocateForTesting(request.get(),
- ResourceType::MAIN_FRAME,
- &resource_context_,
- kRenderProcessId,
- kRenderViewId,
- false);
-
- // Ownership of the |request| is about to be turned over to ResourceLoader. We
- // need to keep a raw pointer copy to access this object later.
- net::URLRequest* raw_ptr_to_request = request.get();
-
- scoped_ptr<ResourceHandler> resource_handler(new ResourceHandlerStub());
- ResourceLoader loader(request.Pass(), resource_handler.Pass(), this);
-
// Prepare a dummy certificate request.
scoped_refptr<net::SSLCertRequestInfo> cert_request_info(
new net::SSLCertRequestInfo());
@@ -308,7 +424,7 @@ TEST_F(ResourceLoaderTest, ClientCertStoreNull) {
// Everything is set up. Trigger the resource loader certificate request event
// and run the message loop.
- loader.OnCertificateRequested(raw_ptr_to_request, cert_request_info.get());
+ loader_->OnCertificateRequested(raw_ptr_to_request_, cert_request_info.get());
base::RunLoop().RunUntilIdle();
// Restore the original content browser client.
@@ -320,4 +436,295 @@ TEST_F(ResourceLoaderTest, ClientCertStoreNull) {
EXPECT_EQ(net::CertificateList(), test_client.passed_certs());
}
+TEST_F(ResourceLoaderTest, ResumeCancelledRequest) {
+ raw_ptr_resource_handler_->set_defer_request_on_will_start(true);
+
+ loader_->StartRequest();
+ loader_->CancelRequest(true);
+ static_cast<ResourceController*>(loader_.get())->Resume();
+}
+
+// Tests that no invariants are broken if a ResourceHandler cancels during
+// OnReadCompleted.
+TEST_F(ResourceLoaderTest, CancelOnReadCompleted) {
+ raw_ptr_resource_handler_->set_cancel_on_read_completed(true);
+
+ loader_->StartRequest();
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(test_url(), raw_ptr_resource_handler_->start_url());
+ EXPECT_TRUE(raw_ptr_resource_handler_->received_response_completed());
+ EXPECT_EQ(net::URLRequestStatus::CANCELED,
+ raw_ptr_resource_handler_->status().status());
+}
+
+// Tests that no invariants are broken if a ResourceHandler defers EOF.
+TEST_F(ResourceLoaderTest, DeferEOF) {
+ raw_ptr_resource_handler_->set_defer_eof(true);
+
+ loader_->StartRequest();
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(test_url(), raw_ptr_resource_handler_->start_url());
+ EXPECT_FALSE(raw_ptr_resource_handler_->received_response_completed());
+
+ raw_ptr_resource_handler_->Resume();
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(raw_ptr_resource_handler_->received_response_completed());
+ EXPECT_EQ(net::URLRequestStatus::SUCCESS,
+ raw_ptr_resource_handler_->status().status());
+}
+
+class ResourceLoaderRedirectToFileTest : public ResourceLoaderTest {
+ public:
+ ResourceLoaderRedirectToFileTest()
+ : file_stream_(NULL),
+ redirect_to_file_resource_handler_(NULL) {
+ }
+
+ base::FilePath temp_path() const { return temp_path_; }
+ ShareableFileReference* deletable_file() const {
+ return deletable_file_.get();
+ }
+ net::testing::MockFileStream* file_stream() const { return file_stream_; }
+ RedirectToFileResourceHandler* redirect_to_file_resource_handler() const {
+ return redirect_to_file_resource_handler_;
+ }
+
+ void ReleaseLoader() {
+ file_stream_ = NULL;
+ deletable_file_ = NULL;
+ loader_.reset();
+ }
+
+ virtual scoped_ptr<ResourceHandler> WrapResourceHandler(
+ scoped_ptr<ResourceHandlerStub> leaf_handler,
+ net::URLRequest* request) OVERRIDE {
+ leaf_handler->set_expect_reads(false);
+
+ // Make a temporary file.
+ CHECK(base::CreateTemporaryFile(&temp_path_));
+ int flags = base::File::FLAG_WRITE | base::File::FLAG_TEMPORARY |
+ base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_ASYNC;
+ base::File file(temp_path_, flags);
+ CHECK(file.IsValid());
+
+ // Create mock file streams and a ShareableFileReference.
+ scoped_ptr<net::testing::MockFileStream> file_stream(
+ new net::testing::MockFileStream(file.Pass(),
+ base::MessageLoopProxy::current()));
+ file_stream_ = file_stream.get();
+ deletable_file_ = ShareableFileReference::GetOrCreate(
+ temp_path_,
+ ShareableFileReference::DELETE_ON_FINAL_RELEASE,
+ BrowserThread::GetMessageLoopProxyForThread(
+ BrowserThread::FILE).get());
+
+ // Inject them into the handler.
+ scoped_ptr<RedirectToFileResourceHandler> handler(
+ new RedirectToFileResourceHandler(
+ leaf_handler.PassAs<ResourceHandler>(), request));
+ redirect_to_file_resource_handler_ = handler.get();
+ handler->SetCreateTemporaryFileStreamFunctionForTesting(
+ base::Bind(&ResourceLoaderRedirectToFileTest::PostCallback,
+ base::Unretained(this),
+ base::Passed(file_stream.PassAs<net::FileStream>())));
+ return handler.PassAs<ResourceHandler>();
+ }
+
+ private:
+ void PostCallback(
+ scoped_ptr<net::FileStream> file_stream,
+ const CreateTemporaryFileStreamCallback& callback) {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, base::File::FILE_OK,
+ base::Passed(&file_stream), deletable_file_));
+ }
+
+ base::FilePath temp_path_;
+ scoped_refptr<ShareableFileReference> deletable_file_;
+ // These are owned by the ResourceLoader.
+ net::testing::MockFileStream* file_stream_;
+ RedirectToFileResourceHandler* redirect_to_file_resource_handler_;
+};
+
+// Tests that a RedirectToFileResourceHandler works and forwards everything
+// downstream.
+TEST_F(ResourceLoaderRedirectToFileTest, Basic) {
+ // Run it to completion.
+ loader_->StartRequest();
+ base::RunLoop().RunUntilIdle();
+
+ // Check that the handler forwarded all information to the downstream handler.
+ EXPECT_EQ(temp_path(),
+ raw_ptr_resource_handler_->response()->head.download_file_path);
+ EXPECT_EQ(test_url(), raw_ptr_resource_handler_->start_url());
+ EXPECT_TRUE(raw_ptr_resource_handler_->received_response_completed());
+ EXPECT_EQ(net::URLRequestStatus::SUCCESS,
+ raw_ptr_resource_handler_->status().status());
+ EXPECT_EQ(test_data().size(), static_cast<size_t>(
+ raw_ptr_resource_handler_->total_bytes_downloaded()));
+
+ // Check that the data was written to the file.
+ std::string contents;
+ ASSERT_TRUE(base::ReadFileToString(temp_path(), &contents));
+ EXPECT_EQ(test_data(), contents);
+
+ // Release the loader and the saved reference to file. The file should be gone
+ // now.
+ ReleaseLoader();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(base::PathExists(temp_path()));
+}
+
+// Tests that RedirectToFileResourceHandler handles errors in creating the
+// temporary file.
+TEST_F(ResourceLoaderRedirectToFileTest, CreateTemporaryError) {
+ // Swap out the create temporary function.
+ redirect_to_file_resource_handler()->
+ SetCreateTemporaryFileStreamFunctionForTesting(
+ base::Bind(&CreateTemporaryError, base::File::FILE_ERROR_FAILED));
+
+ // Run it to completion.
+ loader_->StartRequest();
+ base::RunLoop().RunUntilIdle();
+
+ // To downstream, the request was canceled.
+ EXPECT_TRUE(raw_ptr_resource_handler_->received_response_completed());
+ EXPECT_EQ(net::URLRequestStatus::CANCELED,
+ raw_ptr_resource_handler_->status().status());
+ EXPECT_EQ(0, raw_ptr_resource_handler_->total_bytes_downloaded());
+}
+
+// Tests that RedirectToFileResourceHandler handles synchronous write errors.
+TEST_F(ResourceLoaderRedirectToFileTest, WriteError) {
+ file_stream()->set_forced_error(net::ERR_FAILED);
+
+ // Run it to completion.
+ loader_->StartRequest();
+ base::RunLoop().RunUntilIdle();
+
+ // To downstream, the request was canceled sometime after it started, but
+ // before any data was written.
+ EXPECT_EQ(temp_path(),
+ raw_ptr_resource_handler_->response()->head.download_file_path);
+ EXPECT_EQ(test_url(), raw_ptr_resource_handler_->start_url());
+ EXPECT_TRUE(raw_ptr_resource_handler_->received_response_completed());
+ EXPECT_EQ(net::URLRequestStatus::CANCELED,
+ raw_ptr_resource_handler_->status().status());
+ EXPECT_EQ(0, raw_ptr_resource_handler_->total_bytes_downloaded());
+
+ // Release the loader. The file should be gone now.
+ ReleaseLoader();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(base::PathExists(temp_path()));
+}
+
+// Tests that RedirectToFileResourceHandler handles asynchronous write errors.
+TEST_F(ResourceLoaderRedirectToFileTest, WriteErrorAsync) {
+ file_stream()->set_forced_error_async(net::ERR_FAILED);
+
+ // Run it to completion.
+ loader_->StartRequest();
+ base::RunLoop().RunUntilIdle();
+
+ // To downstream, the request was canceled sometime after it started, but
+ // before any data was written.
+ EXPECT_EQ(temp_path(),
+ raw_ptr_resource_handler_->response()->head.download_file_path);
+ EXPECT_EQ(test_url(), raw_ptr_resource_handler_->start_url());
+ EXPECT_TRUE(raw_ptr_resource_handler_->received_response_completed());
+ EXPECT_EQ(net::URLRequestStatus::CANCELED,
+ raw_ptr_resource_handler_->status().status());
+ EXPECT_EQ(0, raw_ptr_resource_handler_->total_bytes_downloaded());
+
+ // Release the loader. The file should be gone now.
+ ReleaseLoader();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(base::PathExists(temp_path()));
+}
+
+// Tests that RedirectToFileHandler defers completion if there are outstanding
+// writes and accounts for errors which occur in that time.
+TEST_F(ResourceLoaderRedirectToFileTest, DeferCompletion) {
+ // Program the MockFileStream to error asynchronously, but throttle the
+ // callback.
+ file_stream()->set_forced_error_async(net::ERR_FAILED);
+ file_stream()->ThrottleCallbacks();
+
+ // Run it as far as it will go.
+ loader_->StartRequest();
+ base::RunLoop().RunUntilIdle();
+
+ // At this point, the request should have completed.
+ EXPECT_EQ(net::URLRequestStatus::SUCCESS,
+ raw_ptr_to_request_->status().status());
+
+ // However, the resource loader stack is stuck somewhere after receiving the
+ // response.
+ EXPECT_EQ(temp_path(),
+ raw_ptr_resource_handler_->response()->head.download_file_path);
+ EXPECT_EQ(test_url(), raw_ptr_resource_handler_->start_url());
+ EXPECT_FALSE(raw_ptr_resource_handler_->received_response_completed());
+ EXPECT_EQ(0, raw_ptr_resource_handler_->total_bytes_downloaded());
+
+ // Now, release the floodgates.
+ file_stream()->ReleaseCallbacks();
+ base::RunLoop().RunUntilIdle();
+
+ // Although the URLRequest was successful, the leaf handler sees a failure
+ // because the write never completed.
+ EXPECT_TRUE(raw_ptr_resource_handler_->received_response_completed());
+ EXPECT_EQ(net::URLRequestStatus::CANCELED,
+ raw_ptr_resource_handler_->status().status());
+
+ // Release the loader. The file should be gone now.
+ ReleaseLoader();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(base::PathExists(temp_path()));
+}
+
+// Tests that a RedirectToFileResourceHandler behaves properly when the
+// downstream handler defers OnWillStart.
+TEST_F(ResourceLoaderRedirectToFileTest, DownstreamDeferStart) {
+ // Defer OnWillStart.
+ raw_ptr_resource_handler_->set_defer_request_on_will_start(true);
+
+ // Run as far as we'll go.
+ loader_->StartRequest();
+ base::RunLoop().RunUntilIdle();
+
+ // The request should have stopped at OnWillStart.
+ EXPECT_EQ(test_url(), raw_ptr_resource_handler_->start_url());
+ EXPECT_FALSE(raw_ptr_resource_handler_->response());
+ EXPECT_FALSE(raw_ptr_resource_handler_->received_response_completed());
+ EXPECT_EQ(0, raw_ptr_resource_handler_->total_bytes_downloaded());
+
+ // Now resume the request. Now we complete.
+ raw_ptr_resource_handler_->Resume();
+ base::RunLoop().RunUntilIdle();
+
+ // Check that the handler forwarded all information to the downstream handler.
+ EXPECT_EQ(temp_path(),
+ raw_ptr_resource_handler_->response()->head.download_file_path);
+ EXPECT_EQ(test_url(), raw_ptr_resource_handler_->start_url());
+ EXPECT_TRUE(raw_ptr_resource_handler_->received_response_completed());
+ EXPECT_EQ(net::URLRequestStatus::SUCCESS,
+ raw_ptr_resource_handler_->status().status());
+ EXPECT_EQ(test_data().size(), static_cast<size_t>(
+ raw_ptr_resource_handler_->total_bytes_downloaded()));
+
+ // Check that the data was written to the file.
+ std::string contents;
+ ASSERT_TRUE(base::ReadFileToString(temp_path(), &contents));
+ EXPECT_EQ(test_data(), contents);
+
+ // Release the loader. The file should be gone now.
+ ReleaseLoader();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(base::PathExists(temp_path()));
+}
+
} // namespace content
diff --git a/chromium/content/browser/loader/resource_message_delegate.h b/chromium/content/browser/loader/resource_message_delegate.h
index f4478084634..934d60c0231 100644
--- a/chromium/content/browser/loader/resource_message_delegate.h
+++ b/chromium/content/browser/loader/resource_message_delegate.h
@@ -29,8 +29,7 @@ class CONTENT_EXPORT ResourceMessageDelegate {
// Called when the ResourceDispatcherHostImpl receives a message specifically
// for this delegate.
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) = 0;
+ virtual bool OnMessageReceived(const IPC::Message& message) = 0;
void set_request_id(const GlobalRequestID& new_request_id) {
id_ = new_request_id;
diff --git a/chromium/content/browser/loader/resource_message_filter.cc b/chromium/content/browser/loader/resource_message_filter.cc
index c3f30ff796e..137029cf644 100644
--- a/chromium/content/browser/loader/resource_message_filter.cc
+++ b/chromium/content/browser/loader/resource_message_filter.cc
@@ -7,6 +7,8 @@
#include "content/browser/appcache/chrome_appcache_service.h"
#include "content/browser/fileapi/chrome_blob_storage_context.h"
#include "content/browser/loader/resource_dispatcher_host_impl.h"
+#include "content/browser/service_worker/service_worker_context_wrapper.h"
+#include "content/common/resource_messages.h"
#include "content/public/browser/resource_context.h"
#include "webkit/browser/fileapi/file_system_context.h"
@@ -18,12 +20,15 @@ ResourceMessageFilter::ResourceMessageFilter(
ChromeAppCacheService* appcache_service,
ChromeBlobStorageContext* blob_storage_context,
fileapi::FileSystemContext* file_system_context,
+ ServiceWorkerContextWrapper* service_worker_context,
const GetContextsCallback& get_contexts_callback)
- : child_id_(child_id),
+ : BrowserMessageFilter(ResourceMsgStart),
+ child_id_(child_id),
process_type_(process_type),
appcache_service_(appcache_service),
blob_storage_context_(blob_storage_context),
file_system_context_(file_system_context),
+ service_worker_context_(service_worker_context),
get_contexts_callback_(get_contexts_callback),
weak_ptr_factory_(this) {
}
@@ -37,10 +42,8 @@ void ResourceMessageFilter::OnChannelClosing() {
ResourceDispatcherHostImpl::Get()->CancelRequestsForProcess(child_id_);
}
-bool ResourceMessageFilter::OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) {
- return ResourceDispatcherHostImpl::Get()->OnMessageReceived(
- message, this, message_was_ok);
+bool ResourceMessageFilter::OnMessageReceived(const IPC::Message& message) {
+ return ResourceDispatcherHostImpl::Get()->OnMessageReceived(message, this);
}
void ResourceMessageFilter::GetContexts(
diff --git a/chromium/content/browser/loader/resource_message_filter.h b/chromium/content/browser/loader/resource_message_filter.h
index a2fb2e38fc6..90af00e7e73 100644
--- a/chromium/content/browser/loader/resource_message_filter.h
+++ b/chromium/content/browser/loader/resource_message_filter.h
@@ -27,6 +27,7 @@ namespace content {
class ChromeAppCacheService;
class ChromeBlobStorageContext;
class ResourceContext;
+class ServiceWorkerContextWrapper;
// This class filters out incoming IPC messages for network requests and
// processes them on the IPC thread. As a result, network requests are not
@@ -47,12 +48,12 @@ class CONTENT_EXPORT ResourceMessageFilter : public BrowserMessageFilter {
ChromeAppCacheService* appcache_service,
ChromeBlobStorageContext* blob_storage_context,
fileapi::FileSystemContext* file_system_context,
+ ServiceWorkerContextWrapper* service_worker_context,
const GetContextsCallback& get_contexts_callback);
// BrowserMessageFilter implementation.
virtual void OnChannelClosing() OVERRIDE;
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
void GetContexts(const ResourceHostMsg_Request& request,
ResourceContext** resource_context,
@@ -74,6 +75,10 @@ class CONTENT_EXPORT ResourceMessageFilter : public BrowserMessageFilter {
return file_system_context_.get();
}
+ ServiceWorkerContextWrapper* service_worker_context() const {
+ return service_worker_context_.get();
+ }
+
int child_id() const { return child_id_; }
int process_type() const { return process_type_; }
@@ -92,6 +97,7 @@ class CONTENT_EXPORT ResourceMessageFilter : public BrowserMessageFilter {
scoped_refptr<ChromeAppCacheService> appcache_service_;
scoped_refptr<ChromeBlobStorageContext> blob_storage_context_;
scoped_refptr<fileapi::FileSystemContext> file_system_context_;
+ scoped_refptr<ServiceWorkerContextWrapper> service_worker_context_;
GetContextsCallback get_contexts_callback_;
diff --git a/chromium/content/browser/loader/resource_request_info_impl.cc b/chromium/content/browser/loader/resource_request_info_impl.cc
index 32ba7ac6dea..03d925ee786 100644
--- a/chromium/content/browser/loader/resource_request_info_impl.cc
+++ b/chromium/content/browser/loader/resource_request_info_impl.cc
@@ -29,6 +29,7 @@ void ResourceRequestInfo::AllocateForTesting(
ResourceContext* context,
int render_process_id,
int render_view_id,
+ int render_frame_id,
bool is_async) {
ResourceRequestInfoImpl* info =
new ResourceRequestInfoImpl(
@@ -37,11 +38,10 @@ void ResourceRequestInfo::AllocateForTesting(
render_view_id, // route_id
0, // origin_pid
0, // request_id
- MSG_ROUTING_NONE, // render_frame_id
+ render_frame_id, // render_frame_id
resource_type == ResourceType::MAIN_FRAME, // is_main_frame
- 0, // frame_id
false, // parent_is_main_frame
- 0, // parent_frame_id
+ 0, // parent_render_frame_id
resource_type, // resource_type
PAGE_TRANSITION_LINK, // transition_type
false, // should_replace_current_entry
@@ -49,7 +49,8 @@ void ResourceRequestInfo::AllocateForTesting(
false, // is_stream
true, // allow_download
false, // has_user_gesture
- blink::WebReferrerPolicyDefault, // referrer_policy
+ blink::WebReferrerPolicyDefault, // referrer_policy
+ blink::WebPageVisibilityStateVisible, // visibility_state
context, // context
base::WeakPtr<ResourceMessageFilter>(), // filter
is_async); // is_async
@@ -57,16 +58,16 @@ void ResourceRequestInfo::AllocateForTesting(
}
// static
-bool ResourceRequestInfo::GetRenderViewForRequest(
+bool ResourceRequestInfo::GetRenderFrameForRequest(
const net::URLRequest* request,
int* render_process_id,
- int* render_view_id) {
+ int* render_frame_id) {
URLRequestUserData* user_data = static_cast<URLRequestUserData*>(
request->GetUserData(URLRequestUserData::kUserDataKey));
if (!user_data)
return false;
*render_process_id = user_data->render_process_id();
- *render_view_id = user_data->render_view_id();
+ *render_frame_id = user_data->render_frame_id();
return true;
}
@@ -93,9 +94,8 @@ ResourceRequestInfoImpl::ResourceRequestInfoImpl(
int request_id,
int render_frame_id,
bool is_main_frame,
- int64 frame_id,
bool parent_is_main_frame,
- int64 parent_frame_id,
+ int parent_render_frame_id,
ResourceType::Type resource_type,
PageTransition transition_type,
bool should_replace_current_entry,
@@ -104,6 +104,7 @@ ResourceRequestInfoImpl::ResourceRequestInfoImpl(
bool allow_download,
bool has_user_gesture,
blink::WebReferrerPolicy referrer_policy,
+ blink::WebPageVisibilityState visibility_state,
ResourceContext* context,
base::WeakPtr<ResourceMessageFilter> filter,
bool is_async)
@@ -116,9 +117,8 @@ ResourceRequestInfoImpl::ResourceRequestInfoImpl(
request_id_(request_id),
render_frame_id_(render_frame_id),
is_main_frame_(is_main_frame),
- frame_id_(frame_id),
parent_is_main_frame_(parent_is_main_frame),
- parent_frame_id_(parent_frame_id),
+ parent_render_frame_id_(parent_render_frame_id),
should_replace_current_entry_(should_replace_current_entry),
is_download_(is_download),
is_stream_(is_stream),
@@ -129,6 +129,7 @@ ResourceRequestInfoImpl::ResourceRequestInfoImpl(
transition_type_(transition_type),
memory_cost_(0),
referrer_policy_(referrer_policy),
+ visibility_state_(visibility_state),
context_(context),
filter_(filter),
is_async_(is_async) {
@@ -165,26 +166,31 @@ bool ResourceRequestInfoImpl::IsMainFrame() const {
return is_main_frame_;
}
-int64 ResourceRequestInfoImpl::GetFrameID() const {
- return frame_id_;
-}
-
bool ResourceRequestInfoImpl::ParentIsMainFrame() const {
return parent_is_main_frame_;
}
-int64 ResourceRequestInfoImpl::GetParentFrameID() const {
- return parent_frame_id_;
+int ResourceRequestInfoImpl::GetParentRenderFrameID() const {
+ return parent_render_frame_id_;
}
ResourceType::Type ResourceRequestInfoImpl::GetResourceType() const {
return resource_type_;
}
+int ResourceRequestInfoImpl::GetProcessType() const {
+ return process_type_;
+}
+
blink::WebReferrerPolicy ResourceRequestInfoImpl::GetReferrerPolicy() const {
return referrer_policy_;
}
+blink::WebPageVisibilityState
+ResourceRequestInfoImpl::GetVisibilityState() const {
+ return visibility_state_;
+}
+
PageTransition ResourceRequestInfoImpl::GetPageTransition() const {
return transition_type_;
}
@@ -197,26 +203,26 @@ bool ResourceRequestInfoImpl::WasIgnoredByHandler() const {
return was_ignored_by_handler_;
}
-bool ResourceRequestInfoImpl::GetAssociatedRenderView(
+bool ResourceRequestInfoImpl::GetAssociatedRenderFrame(
int* render_process_id,
- int* render_view_id) const {
+ int* render_frame_id) const {
// If the request is from the worker process, find a content that owns the
// worker.
if (process_type_ == PROCESS_TYPE_WORKER) {
// Need to display some related UI for this network request - pick an
// arbitrary parent to do so.
if (!WorkerServiceImpl::GetInstance()->GetRendererForWorker(
- child_id_, render_process_id, render_view_id)) {
+ child_id_, render_process_id, render_frame_id)) {
*render_process_id = -1;
- *render_view_id = -1;
+ *render_frame_id = -1;
return false;
}
} else if (process_type_ == PROCESS_TYPE_PLUGIN) {
*render_process_id = origin_pid_;
- *render_view_id = route_id_;
+ *render_frame_id = render_frame_id_;
} else {
*render_process_id = child_id_;
- *render_view_id = route_id_;
+ *render_frame_id = render_frame_id_;
}
return true;
}
@@ -232,11 +238,11 @@ bool ResourceRequestInfoImpl::IsDownload() const {
void ResourceRequestInfoImpl::AssociateWithRequest(net::URLRequest* request) {
request->SetUserData(NULL, this);
int render_process_id;
- int render_view_id;
- if (GetAssociatedRenderView(&render_process_id, &render_view_id)) {
+ int render_frame_id;
+ if (GetAssociatedRenderFrame(&render_process_id, &render_frame_id)) {
request->SetUserData(
URLRequestUserData::kUserDataKey,
- new URLRequestUserData(render_process_id, render_view_id));
+ new URLRequestUserData(render_process_id, render_frame_id));
}
}
@@ -253,14 +259,13 @@ void ResourceRequestInfoImpl::UpdateForTransfer(
int route_id,
int origin_pid,
int request_id,
- int64 frame_id,
- int64 parent_frame_id,
+ int parent_render_frame_id,
base::WeakPtr<ResourceMessageFilter> filter) {
child_id_ = child_id;
route_id_ = route_id;
origin_pid_ = origin_pid;
request_id_ = request_id;
- frame_id_ = frame_id;
+ parent_render_frame_id_ = parent_render_frame_id;
filter_ = filter;
}
diff --git a/chromium/content/browser/loader/resource_request_info_impl.h b/chromium/content/browser/loader/resource_request_info_impl.h
index 2635106c5ca..82c81c0985e 100644
--- a/chromium/content/browser/loader/resource_request_info_impl.h
+++ b/chromium/content/browser/loader/resource_request_info_impl.h
@@ -47,9 +47,8 @@ class ResourceRequestInfoImpl : public ResourceRequestInfo,
int request_id,
int render_frame_id,
bool is_main_frame,
- int64 frame_id,
bool parent_is_main_frame,
- int64 parent_frame_id,
+ int parent_render_frame_id,
ResourceType::Type resource_type,
PageTransition transition_type,
bool should_replace_current_entry,
@@ -58,6 +57,7 @@ class ResourceRequestInfoImpl : public ResourceRequestInfo,
bool allow_download,
bool has_user_gesture,
blink::WebReferrerPolicy referrer_policy,
+ blink::WebPageVisibilityState visibility_state,
ResourceContext* context,
base::WeakPtr<ResourceMessageFilter> filter,
bool is_async);
@@ -71,16 +71,17 @@ class ResourceRequestInfoImpl : public ResourceRequestInfo,
virtual int GetRequestID() const OVERRIDE;
virtual int GetRenderFrameID() const OVERRIDE;
virtual bool IsMainFrame() const OVERRIDE;
- virtual int64 GetFrameID() const OVERRIDE;
virtual bool ParentIsMainFrame() const OVERRIDE;
- virtual int64 GetParentFrameID() const OVERRIDE;
+ virtual int GetParentRenderFrameID() const OVERRIDE;
virtual ResourceType::Type GetResourceType() const OVERRIDE;
+ virtual int GetProcessType() const OVERRIDE;
virtual blink::WebReferrerPolicy GetReferrerPolicy() const OVERRIDE;
+ virtual blink::WebPageVisibilityState GetVisibilityState() const OVERRIDE;
virtual PageTransition GetPageTransition() const OVERRIDE;
virtual bool HasUserGesture() const OVERRIDE;
virtual bool WasIgnoredByHandler() const OVERRIDE;
- virtual bool GetAssociatedRenderView(int* render_process_id,
- int* render_view_id) const OVERRIDE;
+ virtual bool GetAssociatedRenderFrame(int* render_process_id,
+ int* render_frame_id) const OVERRIDE;
virtual bool IsAsync() const OVERRIDE;
virtual bool IsDownload() const OVERRIDE;
@@ -103,8 +104,7 @@ class ResourceRequestInfoImpl : public ResourceRequestInfo,
int route_id,
int origin_pid,
int request_id,
- int64 frame_id,
- int64 parent_frame_id,
+ int parent_render_frame_id,
base::WeakPtr<ResourceMessageFilter> filter);
// CrossSiteResourceHandler for this request. May be null.
@@ -168,9 +168,8 @@ class ResourceRequestInfoImpl : public ResourceRequestInfo,
int request_id_;
int render_frame_id_;
bool is_main_frame_;
- int64 frame_id_;
bool parent_is_main_frame_;
- int64 parent_frame_id_;
+ int parent_render_frame_id_;
bool should_replace_current_entry_;
bool is_download_;
bool is_stream_;
@@ -181,6 +180,7 @@ class ResourceRequestInfoImpl : public ResourceRequestInfo,
PageTransition transition_type_;
int memory_cost_;
blink::WebReferrerPolicy referrer_policy_;
+ blink::WebPageVisibilityState visibility_state_;
ResourceContext* context_;
// The filter might be deleted without deleting this object if the process
// exits during a transfer.
diff --git a/chromium/content/browser/loader/resource_scheduler.cc b/chromium/content/browser/loader/resource_scheduler.cc
index 4ae240e6fe3..a826580cdcd 100644
--- a/chromium/content/browser/loader/resource_scheduler.cc
+++ b/chromium/content/browser/loader/resource_scheduler.cc
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <set>
+
#include "content/browser/loader/resource_scheduler.h"
#include "base/stl_util.h"
@@ -23,54 +25,47 @@ namespace content {
static const size_t kMaxNumDelayableRequestsPerClient = 10;
static const size_t kMaxNumDelayableRequestsPerHost = 6;
-// A thin wrapper around net::PriorityQueue that deals with
-// ScheduledResourceRequests instead of PriorityQueue::Pointers.
-class ResourceScheduler::RequestQueue {
- private:
- typedef net::PriorityQueue<ScheduledResourceRequest*> NetQueue;
- public:
- class Iterator {
- public:
- Iterator(NetQueue* queue) : queue_(queue) {
- DCHECK(queue != NULL);
- current_pointer_ = queue_->FirstMax();
- }
+struct ResourceScheduler::RequestPriorityParams {
+ RequestPriorityParams()
+ : priority(net::DEFAULT_PRIORITY),
+ intra_priority(0) {
+ }
- Iterator& operator++() {
- current_pointer_ = queue_->GetNextTowardsLastMin(current_pointer_);
- return *this;
- }
+ RequestPriorityParams(net::RequestPriority priority, int intra_priority)
+ : priority(priority),
+ intra_priority(intra_priority) {
+ }
- Iterator operator++(int) {
- Iterator result(*this);
- ++(*this);
- return result;
- }
+ bool operator==(const RequestPriorityParams& other) const {
+ return (priority == other.priority) &&
+ (intra_priority == other.intra_priority);
+ }
- ScheduledResourceRequest* value() {
- return current_pointer_.value();
- }
+ bool operator!=(const RequestPriorityParams& other) const {
+ return !(*this == other);
+ }
- bool is_null() {
- return current_pointer_.is_null();
- }
+ bool GreaterThan(const RequestPriorityParams& other) const {
+ if (priority != other.priority)
+ return priority > other.priority;
+ return intra_priority > other.intra_priority;
+ }
- private:
- NetQueue* queue_;
- NetQueue::Pointer current_pointer_;
- };
+ net::RequestPriority priority;
+ int intra_priority;
+};
- RequestQueue() : queue_(net::NUM_PRIORITIES) {}
+class ResourceScheduler::RequestQueue {
+ public:
+ typedef std::multiset<ScheduledResourceRequest*, ScheduledResourceSorter>
+ NetQueue;
+
+ RequestQueue() : fifo_ordering_ids_(0) {}
~RequestQueue() {}
// Adds |request| to the queue with given |priority|.
- void Insert(ScheduledResourceRequest* request,
- net::RequestPriority priority) {
- DCHECK(!ContainsKey(pointers_, request));
- NetQueue::Pointer pointer = queue_.Insert(request, priority);
- pointers_[request] = pointer;
- }
+ void Insert(ScheduledResourceRequest* request);
// Removes |request| from the queue.
void Erase(ScheduledResourceRequest* request) {
@@ -78,17 +73,16 @@ class ResourceScheduler::RequestQueue {
DCHECK(it != pointers_.end());
if (it == pointers_.end())
return;
- queue_.Erase(it->second);
+ queue_.erase(it->second);
pointers_.erase(it);
}
- // Returns the highest priority request that's queued, or NULL if none are.
- ScheduledResourceRequest* FirstMax() {
- return queue_.FirstMax().value();
+ NetQueue::iterator GetNextHighestIterator() {
+ return queue_.begin();
}
- Iterator GetNextHighestIterator() {
- return Iterator(&queue_);
+ NetQueue::iterator End() {
+ return queue_.end();
}
// Returns true if |request| is queued.
@@ -100,7 +94,16 @@ class ResourceScheduler::RequestQueue {
bool IsEmpty() const { return queue_.size() == 0; }
private:
- typedef std::map<ScheduledResourceRequest*, NetQueue::Pointer> PointerMap;
+ typedef std::map<ScheduledResourceRequest*, NetQueue::iterator> PointerMap;
+
+ uint32 MakeFifoOrderingId() {
+ fifo_ordering_ids_ += 1;
+ return fifo_ordering_ids_;
+ }
+
+ // Used to create an ordering ID for scheduled resources so that resources
+ // with same priority/intra_priority stay in fifo order.
+ uint32 fifo_ordering_ids_;
NetQueue queue_;
PointerMap pointers_;
@@ -114,13 +117,17 @@ class ResourceScheduler::ScheduledResourceRequest
public:
ScheduledResourceRequest(const ClientId& client_id,
net::URLRequest* request,
- ResourceScheduler* scheduler)
+ ResourceScheduler* scheduler,
+ const RequestPriorityParams& priority)
: ResourceMessageDelegate(request),
client_id_(client_id),
request_(request),
ready_(false),
deferred_(false),
- scheduler_(scheduler) {
+ scheduler_(scheduler),
+ priority_(priority),
+ fifo_ordering_(0),
+ accounted_as_delayable_request_(false) {
TRACE_EVENT_ASYNC_BEGIN1("net", "URLRequest", request_,
"url", request->url().spec());
}
@@ -138,19 +145,34 @@ class ResourceScheduler::ScheduledResourceRequest
}
}
+ void set_request_priority_params(const RequestPriorityParams& priority) {
+ priority_ = priority;
+ }
+ const RequestPriorityParams& get_request_priority_params() const {
+ return priority_;
+ }
const ClientId& client_id() const { return client_id_; }
net::URLRequest* url_request() { return request_; }
const net::URLRequest* url_request() const { return request_; }
+ uint32 fifo_ordering() const { return fifo_ordering_; }
+ void set_fifo_ordering(uint32 fifo_ordering) {
+ fifo_ordering_ = fifo_ordering;
+ }
+ bool accounted_as_delayable_request() const {
+ return accounted_as_delayable_request_;
+ }
+ void set_accounted_as_delayable_request(bool accounted) {
+ accounted_as_delayable_request_ = accounted;
+ }
private:
// ResourceMessageDelegate interface:
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE {
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(ScheduledResourceRequest, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(ScheduledResourceRequest, message)
IPC_MESSAGE_HANDLER(ResourceHostMsg_DidChangePriority, DidChangePriority)
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return handled;
}
@@ -163,8 +185,9 @@ class ResourceScheduler::ScheduledResourceRequest
return "ResourceScheduler";
}
- void DidChangePriority(int request_id, net::RequestPriority new_priority) {
- scheduler_->ReprioritizeRequest(this, new_priority);
+ void DidChangePriority(int request_id, net::RequestPriority new_priority,
+ int intra_priority_value) {
+ scheduler_->ReprioritizeRequest(this, new_priority, intra_priority_value);
}
ClientId client_id_;
@@ -172,18 +195,302 @@ class ResourceScheduler::ScheduledResourceRequest
bool ready_;
bool deferred_;
ResourceScheduler* scheduler_;
+ RequestPriorityParams priority_;
+ uint32 fifo_ordering_;
+ // True if the request is delayable in |in_flight_requests_|.
+ bool accounted_as_delayable_request_;
DISALLOW_COPY_AND_ASSIGN(ScheduledResourceRequest);
};
+bool ResourceScheduler::ScheduledResourceSorter::operator()(
+ const ScheduledResourceRequest* a,
+ const ScheduledResourceRequest* b) const {
+ // Want the set to be ordered first by decreasing priority, then by
+ // decreasing intra_priority.
+ // ie. with (priority, intra_priority)
+ // [(1, 0), (1, 0), (0, 100), (0, 0)]
+ if (a->get_request_priority_params() != b->get_request_priority_params())
+ return a->get_request_priority_params().GreaterThan(
+ b->get_request_priority_params());
+
+ // If priority/intra_priority is the same, fall back to fifo ordering.
+ // std::multiset doesn't guarantee this until c++11.
+ return a->fifo_ordering() < b->fifo_ordering();
+}
+
+void ResourceScheduler::RequestQueue::Insert(
+ ScheduledResourceRequest* request) {
+ DCHECK(!ContainsKey(pointers_, request));
+ request->set_fifo_ordering(MakeFifoOrderingId());
+ pointers_[request] = queue_.insert(request);
+}
+
// Each client represents a tab.
-struct ResourceScheduler::Client {
- Client() : has_body(false) {}
+class ResourceScheduler::Client {
+ public:
+ Client()
+ : has_body_(false),
+ using_spdy_proxy_(false),
+ total_delayable_count_(0) {}
~Client() {}
- bool has_body;
- RequestQueue pending_requests;
- RequestSet in_flight_requests;
+ void ScheduleRequest(
+ net::URLRequest* url_request,
+ ScheduledResourceRequest* request) {
+ if (ShouldStartRequest(request) == START_REQUEST) {
+ StartRequest(request);
+ } else {
+ pending_requests_.Insert(request);
+ }
+ }
+
+ void RemoveRequest(ScheduledResourceRequest* request) {
+ if (pending_requests_.IsQueued(request)) {
+ pending_requests_.Erase(request);
+ DCHECK(!ContainsKey(in_flight_requests_, request));
+ } else {
+ EraseInFlightRequest(request);
+
+ // Removing this request may have freed up another to load.
+ LoadAnyStartablePendingRequests();
+ }
+ }
+
+ RequestSet RemoveAllRequests() {
+ RequestSet unowned_requests;
+ for (RequestSet::iterator it = in_flight_requests_.begin();
+ it != in_flight_requests_.end(); ++it) {
+ unowned_requests.insert(*it);
+ (*it)->set_accounted_as_delayable_request(false);
+ }
+ ClearInFlightRequests();
+ return unowned_requests;
+ }
+
+ void OnNavigate() {
+ has_body_ = false;
+ }
+
+ void OnWillInsertBody() {
+ has_body_ = true;
+ LoadAnyStartablePendingRequests();
+ }
+
+ void OnReceivedSpdyProxiedHttpResponse() {
+ if (!using_spdy_proxy_) {
+ using_spdy_proxy_ = true;
+ LoadAnyStartablePendingRequests();
+ }
+ }
+
+ void ReprioritizeRequest(ScheduledResourceRequest* request,
+ RequestPriorityParams old_priority_params,
+ RequestPriorityParams new_priority_params) {
+ request->url_request()->SetPriority(new_priority_params.priority);
+ request->set_request_priority_params(new_priority_params);
+ if (!pending_requests_.IsQueued(request)) {
+ DCHECK(ContainsKey(in_flight_requests_, request));
+ // The priority and SPDY support may have changed, so update the
+ // delayable count.
+ SetRequestDelayable(request, IsDelayableRequest(request));
+ // Request has already started.
+ return;
+ }
+
+ pending_requests_.Erase(request);
+ pending_requests_.Insert(request);
+
+ if (new_priority_params.priority > old_priority_params.priority) {
+ // Check if this request is now able to load at its new priority.
+ LoadAnyStartablePendingRequests();
+ }
+ }
+
+ private:
+ enum ShouldStartReqResult {
+ DO_NOT_START_REQUEST_AND_STOP_SEARCHING = -2,
+ DO_NOT_START_REQUEST_AND_KEEP_SEARCHING = -1,
+ START_REQUEST = 1,
+ };
+
+ void InsertInFlightRequest(ScheduledResourceRequest* request) {
+ in_flight_requests_.insert(request);
+ if (IsDelayableRequest(request))
+ SetRequestDelayable(request, true);
+ }
+
+ void EraseInFlightRequest(ScheduledResourceRequest* request) {
+ size_t erased = in_flight_requests_.erase(request);
+ DCHECK_EQ(1u, erased);
+ SetRequestDelayable(request, false);
+ DCHECK_LE(total_delayable_count_, in_flight_requests_.size());
+ }
+
+ void ClearInFlightRequests() {
+ in_flight_requests_.clear();
+ total_delayable_count_ = 0;
+ }
+
+ bool IsDelayableRequest(ScheduledResourceRequest* request) {
+ if (request->url_request()->priority() < net::LOW) {
+ net::HostPortPair host_port_pair =
+ net::HostPortPair::FromURL(request->url_request()->url());
+ net::HttpServerProperties& http_server_properties =
+ *request->url_request()->context()->http_server_properties();
+ if (!http_server_properties.SupportsSpdy(host_port_pair)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void SetRequestDelayable(ScheduledResourceRequest* request,
+ bool delayable) {
+ if (request->accounted_as_delayable_request() == delayable)
+ return;
+ if (delayable)
+ total_delayable_count_++;
+ else
+ total_delayable_count_--;
+ request->set_accounted_as_delayable_request(delayable);
+ }
+
+ bool ShouldKeepSearching(
+ const net::HostPortPair& active_request_host) const {
+ size_t same_host_count = 0;
+ for (RequestSet::const_iterator it = in_flight_requests_.begin();
+ it != in_flight_requests_.end(); ++it) {
+ net::HostPortPair host_port_pair =
+ net::HostPortPair::FromURL((*it)->url_request()->url());
+ if (active_request_host.Equals(host_port_pair)) {
+ same_host_count++;
+ if (same_host_count >= kMaxNumDelayableRequestsPerHost)
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void StartRequest(ScheduledResourceRequest* request) {
+ InsertInFlightRequest(request);
+ request->Start();
+ }
+
+ // ShouldStartRequest is the main scheduling algorithm.
+ //
+ // Requests are categorized into two categories:
+ //
+ // 1. Immediately issued requests, which are:
+ //
+ // * Higher priority requests (>= net::LOW).
+ // * Synchronous requests.
+ // * Requests to SPDY-capable origin servers.
+ // * Non-HTTP[S] requests.
+ //
+ // 2. The remainder are delayable requests, which follow these rules:
+ //
+ // * If no high priority requests are in flight, start loading low priority
+ // requests.
+ // * Once the renderer has a <body>, start loading delayable requests.
+ // * Never exceed 10 delayable requests in flight per client.
+ // * Never exceed 6 delayable requests for a given host.
+ // * Prior to <body>, allow one delayable request to load at a time.
+ ShouldStartReqResult ShouldStartRequest(
+ ScheduledResourceRequest* request) const {
+ const net::URLRequest& url_request = *request->url_request();
+ // TODO(simonjam): This may end up causing disk contention. We should
+ // experiment with throttling if that happens.
+ if (!url_request.url().SchemeIsHTTPOrHTTPS()) {
+ return START_REQUEST;
+ }
+
+ if (using_spdy_proxy_ && url_request.url().SchemeIs("http")) {
+ return START_REQUEST;
+ }
+
+ net::HttpServerProperties& http_server_properties =
+ *url_request.context()->http_server_properties();
+
+ if (url_request.priority() >= net::LOW ||
+ !ResourceRequestInfo::ForRequest(&url_request)->IsAsync()) {
+ return START_REQUEST;
+ }
+
+ net::HostPortPair host_port_pair =
+ net::HostPortPair::FromURL(url_request.url());
+
+ // TODO(willchan): We should really improve this algorithm as described in
+ // crbug.com/164101. Also, theoretically we should not count a SPDY request
+ // against the delayable requests limit.
+ if (http_server_properties.SupportsSpdy(host_port_pair)) {
+ return START_REQUEST;
+ }
+
+ size_t num_delayable_requests_in_flight = total_delayable_count_;
+ if (num_delayable_requests_in_flight >= kMaxNumDelayableRequestsPerClient) {
+ return DO_NOT_START_REQUEST_AND_STOP_SEARCHING;
+ }
+
+ if (ShouldKeepSearching(host_port_pair)) {
+ // There may be other requests for other hosts we'd allow,
+ // so keep checking.
+ return DO_NOT_START_REQUEST_AND_KEEP_SEARCHING;
+ }
+
+ bool have_immediate_requests_in_flight =
+ in_flight_requests_.size() > num_delayable_requests_in_flight;
+ if (have_immediate_requests_in_flight && !has_body_ &&
+ num_delayable_requests_in_flight != 0) {
+ return DO_NOT_START_REQUEST_AND_STOP_SEARCHING;
+ }
+
+ return START_REQUEST;
+ }
+
+ void LoadAnyStartablePendingRequests() {
+ // We iterate through all the pending requests, starting with the highest
+ // priority one. For each entry, one of three things can happen:
+ // 1) We start the request, remove it from the list, and keep checking.
+ // 2) We do NOT start the request, but ShouldStartRequest() signals us that
+ // there may be room for other requests, so we keep checking and leave
+ // the previous request still in the list.
+ // 3) We do not start the request, same as above, but StartRequest() tells
+ // us there's no point in checking any further requests.
+ RequestQueue::NetQueue::iterator request_iter =
+ pending_requests_.GetNextHighestIterator();
+
+ while (request_iter != pending_requests_.End()) {
+ ScheduledResourceRequest* request = *request_iter;
+ ShouldStartReqResult query_result = ShouldStartRequest(request);
+
+ if (query_result == START_REQUEST) {
+ pending_requests_.Erase(request);
+ StartRequest(request);
+
+ // StartRequest can modify the pending list, so we (re)start evaluation
+ // from the currently highest priority request. Avoid copying a singular
+ // iterator, which would trigger undefined behavior.
+ if (pending_requests_.GetNextHighestIterator() ==
+ pending_requests_.End())
+ break;
+ request_iter = pending_requests_.GetNextHighestIterator();
+ } else if (query_result == DO_NOT_START_REQUEST_AND_KEEP_SEARCHING) {
+ ++request_iter;
+ continue;
+ } else {
+ DCHECK(query_result == DO_NOT_START_REQUEST_AND_STOP_SEARCHING);
+ break;
+ }
+ }
+ }
+
+ bool has_body_;
+ bool using_spdy_proxy_;
+ RequestQueue pending_requests_;
+ RequestSet in_flight_requests_;
+ // The number of delayable in-flight requests.
+ size_t total_delayable_count_;
};
ResourceScheduler::ResourceScheduler() {
@@ -201,7 +508,8 @@ scoped_ptr<ResourceThrottle> ResourceScheduler::ScheduleRequest(
DCHECK(CalledOnValidThread());
ClientId client_id = MakeClientId(child_id, route_id);
scoped_ptr<ScheduledResourceRequest> request(
- new ScheduledResourceRequest(client_id, url_request, this));
+ new ScheduledResourceRequest(client_id, url_request, this,
+ RequestPriorityParams(url_request->priority(), 0)));
ClientMap::iterator it = client_map_.find(client_id);
if (it == client_map_.end()) {
@@ -215,11 +523,7 @@ scoped_ptr<ResourceThrottle> ResourceScheduler::ScheduleRequest(
}
Client* client = it->second;
- if (ShouldStartRequest(request.get(), client) == START_REQUEST) {
- StartRequest(request.get(), client);
- } else {
- client->pending_requests.Insert(request.get(), url_request->priority());
- }
+ client->ScheduleRequest(url_request, request.get());
return request.PassAs<ResourceThrottle>();
}
@@ -236,17 +540,7 @@ void ResourceScheduler::RemoveRequest(ScheduledResourceRequest* request) {
}
Client* client = client_it->second;
-
- if (client->pending_requests.IsQueued(request)) {
- client->pending_requests.Erase(request);
- DCHECK(!ContainsKey(client->in_flight_requests, request));
- } else {
- size_t erased = client->in_flight_requests.erase(request);
- DCHECK(erased);
-
- // Removing this request may have freed up another to load.
- LoadAnyStartablePendingRequests(client);
- }
+ client->RemoveRequest(request);
}
void ResourceScheduler::OnClientCreated(int child_id, int route_id) {
@@ -270,11 +564,11 @@ void ResourceScheduler::OnClientDeleted(int child_id, int route_id) {
// FYI, ResourceDispatcherHost cancels all of the requests after this function
// is called. It should end up canceling all of the requests except for a
// cross-renderer navigation.
- for (RequestSet::iterator it = client->in_flight_requests.begin();
- it != client->in_flight_requests.end(); ++it) {
+ RequestSet client_unowned_requests = client->RemoveAllRequests();
+ for (RequestSet::iterator it = client_unowned_requests.begin();
+ it != client_unowned_requests.end(); ++it) {
unowned_requests_.insert(*it);
}
- client->in_flight_requests.clear();
delete client;
client_map_.erase(it);
@@ -291,7 +585,7 @@ void ResourceScheduler::OnNavigate(int child_id, int route_id) {
}
Client* client = it->second;
- client->has_body = false;
+ client->OnNavigate();
}
void ResourceScheduler::OnWillInsertBody(int child_id, int route_id) {
@@ -305,193 +599,54 @@ void ResourceScheduler::OnWillInsertBody(int child_id, int route_id) {
}
Client* client = it->second;
- client->has_body = false;
- if (!client->has_body) {
- client->has_body = true;
- LoadAnyStartablePendingRequests(client);
- }
+ client->OnWillInsertBody();
}
-void ResourceScheduler::StartRequest(ScheduledResourceRequest* request,
- Client* client) {
- client->in_flight_requests.insert(request);
- request->Start();
+void ResourceScheduler::OnReceivedSpdyProxiedHttpResponse(
+ int child_id,
+ int route_id) {
+ DCHECK(CalledOnValidThread());
+ ClientId client_id = MakeClientId(child_id, route_id);
+
+ ClientMap::iterator client_it = client_map_.find(client_id);
+ if (client_it == client_map_.end()) {
+ return;
+ }
+
+ Client* client = client_it->second;
+ client->OnReceivedSpdyProxiedHttpResponse();
}
void ResourceScheduler::ReprioritizeRequest(ScheduledResourceRequest* request,
- net::RequestPriority new_priority) {
+ net::RequestPriority new_priority,
+ int new_intra_priority_value) {
if (request->url_request()->load_flags() & net::LOAD_IGNORE_LIMITS) {
// We should not be re-prioritizing requests with the
// IGNORE_LIMITS flag.
NOTREACHED();
return;
}
- net::RequestPriority old_priority = request->url_request()->priority();
- DCHECK_NE(new_priority, old_priority);
- request->url_request()->SetPriority(new_priority);
+ RequestPriorityParams new_priority_params(new_priority,
+ new_intra_priority_value);
+ RequestPriorityParams old_priority_params =
+ request->get_request_priority_params();
+
+ DCHECK(old_priority_params != new_priority_params);
+
ClientMap::iterator client_it = client_map_.find(request->client_id());
if (client_it == client_map_.end()) {
// The client was likely deleted shortly before we received this IPC.
+ request->url_request()->SetPriority(new_priority_params.priority);
+ request->set_request_priority_params(new_priority_params);
return;
}
- Client *client = client_it->second;
- if (!client->pending_requests.IsQueued(request)) {
- DCHECK(ContainsKey(client->in_flight_requests, request));
- // Request has already started.
+ if (old_priority_params == new_priority_params)
return;
- }
-
- client->pending_requests.Erase(request);
- client->pending_requests.Insert(request,
- request->url_request()->priority());
-
- if (new_priority > old_priority) {
- // Check if this request is now able to load at its new priority.
- LoadAnyStartablePendingRequests(client);
- }
-}
-
-void ResourceScheduler::LoadAnyStartablePendingRequests(Client* client) {
- // We iterate through all the pending requests, starting with the highest
- // priority one. For each entry, one of three things can happen:
- // 1) We start the request, remove it from the list, and keep checking.
- // 2) We do NOT start the request, but ShouldStartRequest() signals us that
- // there may be room for other requests, so we keep checking and leave
- // the previous request still in the list.
- // 3) We do not start the request, same as above, but StartRequest() tells
- // us there's no point in checking any further requests.
-
- RequestQueue::Iterator request_iter =
- client->pending_requests.GetNextHighestIterator();
-
- while (!request_iter.is_null()) {
- ScheduledResourceRequest* request = request_iter.value();
- ShouldStartReqResult query_result = ShouldStartRequest(request, client);
-
- if (query_result == START_REQUEST) {
- client->pending_requests.Erase(request);
- StartRequest(request, client);
-
- // StartRequest can modify the pending list, so we (re)start evaluation
- // from the currently highest priority request. Avoid copying a singular
- // iterator, which would trigger undefined behavior.
- if (client->pending_requests.GetNextHighestIterator().is_null())
- break;
- request_iter = client->pending_requests.GetNextHighestIterator();
- } else if (query_result == DO_NOT_START_REQUEST_AND_KEEP_SEARCHING) {
- ++request_iter;
- continue;
- } else {
- DCHECK(query_result == DO_NOT_START_REQUEST_AND_STOP_SEARCHING);
- break;
- }
- }
-}
-void ResourceScheduler::GetNumDelayableRequestsInFlight(
- Client* client,
- const net::HostPortPair& active_request_host,
- size_t* total_delayable,
- size_t* total_for_active_host) const {
- DCHECK(client != NULL && total_delayable != NULL &&
- total_for_active_host != NULL);
-
- size_t total_delayable_count = 0;
- size_t same_host_count = 0;
- for (RequestSet::iterator it = client->in_flight_requests.begin();
- it != client->in_flight_requests.end(); ++it) {
- net::HostPortPair host_port_pair =
- net::HostPortPair::FromURL((*it)->url_request()->url());
-
- if (active_request_host.Equals(host_port_pair)) {
- same_host_count++;
- }
-
- if ((*it)->url_request()->priority() < net::LOW) {
- const net::HttpServerProperties& http_server_properties =
- *(*it)->url_request()->context()->http_server_properties();
-
- if (!http_server_properties.SupportsSpdy(host_port_pair)) {
- ++total_delayable_count;
- }
- }
- }
- *total_delayable = total_delayable_count;
- *total_for_active_host = same_host_count;
-}
-
-// ShouldStartRequest is the main scheduling algorithm.
-//
-// Requests are categorized into two categories:
-//
-// 1. Immediately issued requests, which are:
-//
-// * Higher priority requests (>= net::LOW).
-// * Synchronous requests.
-// * Requests to SPDY-capable origin servers.
-// * Non-HTTP[S] requests.
-//
-// 2. The remainder are delayable requests, which follow these rules:
-//
-// * If no high priority requests are in flight, start loading low priority
-// requests.
-// * Once the renderer has a <body>, start loading delayable requests.
-// * Never exceed 10 delayable requests in flight per client.
-// * Never exceed 6 delayable requests for a given host.
-// * Prior to <body>, allow one delayable request to load at a time.
-ResourceScheduler::ShouldStartReqResult ResourceScheduler::ShouldStartRequest(
- ScheduledResourceRequest* request,
- Client* client) const {
- const net::URLRequest& url_request = *request->url_request();
-
- // TODO(simonjam): This may end up causing disk contention. We should
- // experiment with throttling if that happens.
- if (!url_request.url().SchemeIsHTTPOrHTTPS()) {
- return START_REQUEST;
- }
-
- const net::HttpServerProperties& http_server_properties =
- *url_request.context()->http_server_properties();
-
- if (url_request.priority() >= net::LOW ||
- !ResourceRequestInfo::ForRequest(&url_request)->IsAsync()) {
- return START_REQUEST;
- }
-
- net::HostPortPair host_port_pair =
- net::HostPortPair::FromURL(url_request.url());
-
- // TODO(willchan): We should really improve this algorithm as described in
- // crbug.com/164101. Also, theoretically we should not count a SPDY request
- // against the delayable requests limit.
- if (http_server_properties.SupportsSpdy(host_port_pair)) {
- return START_REQUEST;
- }
-
- size_t num_delayable_requests_in_flight = 0;
- size_t num_requests_in_flight_for_host = 0;
- GetNumDelayableRequestsInFlight(client, host_port_pair,
- &num_delayable_requests_in_flight,
- &num_requests_in_flight_for_host);
-
- if (num_delayable_requests_in_flight >= kMaxNumDelayableRequestsPerClient) {
- return DO_NOT_START_REQUEST_AND_STOP_SEARCHING;
- }
-
- if (num_requests_in_flight_for_host >= kMaxNumDelayableRequestsPerHost) {
- // There may be other requests for other hosts we'd allow, so keep checking.
- return DO_NOT_START_REQUEST_AND_KEEP_SEARCHING;
- }
-
- bool have_immediate_requests_in_flight =
- client->in_flight_requests.size() > num_delayable_requests_in_flight;
- if (have_immediate_requests_in_flight && !client->has_body &&
- num_delayable_requests_in_flight != 0) {
- return DO_NOT_START_REQUEST_AND_STOP_SEARCHING;
- }
-
- return START_REQUEST;
+ Client *client = client_it->second;
+ client->ReprioritizeRequest(
+ request, old_priority_params, new_priority_params);
}
ResourceScheduler::ClientId ResourceScheduler::MakeClientId(
diff --git a/chromium/content/browser/loader/resource_scheduler.h b/chromium/content/browser/loader/resource_scheduler.h
index dddc831a2e2..0e918f10c6d 100644
--- a/chromium/content/browser/loader/resource_scheduler.h
+++ b/chromium/content/browser/loader/resource_scheduler.h
@@ -78,10 +78,21 @@ class CONTENT_EXPORT ResourceScheduler : public base::NonThreadSafe {
// resource loads won't interfere with first paint.
void OnWillInsertBody(int child_id, int route_id);
+ // Signals from the IO thread
+
+ // Called when we received a response to a http request that was served
+ // from a proxy using SPDY.
+ void OnReceivedSpdyProxiedHttpResponse(int child_id, int route_id);
+
private:
class RequestQueue;
class ScheduledResourceRequest;
- struct Client;
+ struct RequestPriorityParams;
+ struct ScheduledResourceSorter {
+ bool operator()(const ScheduledResourceRequest* a,
+ const ScheduledResourceRequest* b) const;
+ };
+ class Client;
typedef int64 ClientId;
typedef std::map<ClientId, Client*> ClientMap;
@@ -90,9 +101,6 @@ class CONTENT_EXPORT ResourceScheduler : public base::NonThreadSafe {
// Called when a ScheduledResourceRequest is destroyed.
void RemoveRequest(ScheduledResourceRequest* request);
- // Unthrottles the |request| and adds it to |client|.
- void StartRequest(ScheduledResourceRequest* request, Client* client);
-
// Update the queue position for |request|, possibly causing it to start
// loading.
//
@@ -100,30 +108,8 @@ class CONTENT_EXPORT ResourceScheduler : public base::NonThreadSafe {
// reprioritized, it will move to the end of the queue for that priority
// level.
void ReprioritizeRequest(ScheduledResourceRequest* request,
- net::RequestPriority new_priority);
-
- // Attempts to load any pending requests in |client|, based on the
- // results of ShouldStartRequest().
- void LoadAnyStartablePendingRequests(Client* client);
-
- // Returns the number of requests with priority < LOW that are currently in
- // flight.
- void GetNumDelayableRequestsInFlight(
- Client* client,
- const net::HostPortPair& active_request_host,
- size_t* total_delayable,
- size_t* total_for_active_host) const;
-
- enum ShouldStartReqResult {
- DO_NOT_START_REQUEST_AND_STOP_SEARCHING = -2,
- DO_NOT_START_REQUEST_AND_KEEP_SEARCHING = -1,
- START_REQUEST = 1,
- };
-
- // Returns true if the request should start. This is the core scheduling
- // algorithm.
- ShouldStartReqResult ShouldStartRequest(ScheduledResourceRequest* request,
- Client* client) const;
+ net::RequestPriority new_priority,
+ int intra_priority_value);
// Returns the client ID for the given |child_id| and |route_id| combo.
ClientId MakeClientId(int child_id, int route_id);
diff --git a/chromium/content/browser/loader/resource_scheduler_filter.cc b/chromium/content/browser/loader/resource_scheduler_filter.cc
index 66e9ea4fbd0..4c825dab987 100644
--- a/chromium/content/browser/loader/resource_scheduler_filter.cc
+++ b/chromium/content/browser/loader/resource_scheduler_filter.cc
@@ -6,20 +6,28 @@
#include "content/browser/loader/resource_dispatcher_host_impl.h"
#include "content/browser/loader/resource_scheduler.h"
+#include "content/common/frame_messages.h"
#include "content/common/view_messages.h"
#include "content/public/common/page_transition_types.h"
namespace content {
+namespace {
+const uint32 kFilteredMessageClasses[] = {
+ FrameMsgStart,
+ ViewMsgStart,
+};
+} // namespace
ResourceSchedulerFilter::ResourceSchedulerFilter(int child_id)
- : child_id_(child_id) {
+ : BrowserMessageFilter(
+ kFilteredMessageClasses, arraysize(kFilteredMessageClasses)),
+ child_id_(child_id) {
}
ResourceSchedulerFilter::~ResourceSchedulerFilter() {
}
-bool ResourceSchedulerFilter::OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) {
+bool ResourceSchedulerFilter::OnMessageReceived(const IPC::Message& message) {
ResourceScheduler* scheduler =
ResourceDispatcherHostImpl::Get()->scheduler();
// scheduler can be NULL during shutdown, in which case it's ok to ignore the
@@ -28,10 +36,10 @@ bool ResourceSchedulerFilter::OnMessageReceived(const IPC::Message& message,
return false;
switch (message.type()) {
- case ViewHostMsg_FrameNavigate::ID: {
+ case FrameHostMsg_DidCommitProvisionalLoad::ID: {
PickleIterator iter(message);
- ViewHostMsg_FrameNavigate_Params params;
- if (!IPC::ParamTraits<ViewHostMsg_FrameNavigate_Params>::Read(
+ FrameHostMsg_DidCommitProvisionalLoad_Params params;
+ if (!IPC::ParamTraits<FrameHostMsg_DidCommitProvisionalLoad_Params>::Read(
&message, &iter, &params)) {
break;
}
diff --git a/chromium/content/browser/loader/resource_scheduler_filter.h b/chromium/content/browser/loader/resource_scheduler_filter.h
index 05a8631a2a0..9f4c5f3a7ed 100644
--- a/chromium/content/browser/loader/resource_scheduler_filter.h
+++ b/chromium/content/browser/loader/resource_scheduler_filter.h
@@ -19,8 +19,7 @@ class ResourceSchedulerFilter : public BrowserMessageFilter {
explicit ResourceSchedulerFilter(int child_id);
// BrowserMessageFilter methods:
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
private:
virtual ~ResourceSchedulerFilter();
diff --git a/chromium/content/browser/loader/resource_scheduler_unittest.cc b/chromium/content/browser/loader/resource_scheduler_unittest.cc
index fbdc72b4660..f4cb0e0f091 100644
--- a/chromium/content/browser/loader/resource_scheduler_unittest.cc
+++ b/chromium/content/browser/loader/resource_scheduler_unittest.cc
@@ -103,6 +103,7 @@ class FakeResourceMessageFilter : public ResourceMessageFilter {
NULL /* appcache_service */,
NULL /* blob_storage_context */,
NULL /* file_system_context */,
+ NULL /* service_worker_context */,
base::Bind(&FakeResourceMessageFilter::GetContexts,
base::Unretained(this))) {
}
@@ -124,7 +125,6 @@ class ResourceSchedulerTest : public testing::Test {
protected:
ResourceSchedulerTest()
: next_request_id_(0),
- message_loop_(base::MessageLoop::TYPE_IO),
ui_thread_(BrowserThread::UI, &message_loop_),
io_thread_(BrowserThread::IO, &message_loop_) {
scheduler_.OnClientCreated(kChildId, kRouteId);
@@ -140,7 +140,7 @@ class ResourceSchedulerTest : public testing::Test {
net::RequestPriority priority,
int route_id) {
scoped_ptr<net::URLRequest> url_request(
- context_.CreateRequest(GURL(url), priority, NULL));
+ context_.CreateRequest(GURL(url), priority, NULL, NULL));
ResourceRequestInfoImpl* info = new ResourceRequestInfoImpl(
PROCESS_TYPE_RENDERER, // process_type
kChildId, // child_id
@@ -149,9 +149,8 @@ class ResourceSchedulerTest : public testing::Test {
++next_request_id_, // request_id
MSG_ROUTING_NONE, // render_frame_id
false, // is_main_frame
- 0, // frame_id
false, // parent_is_main_frame
- 0, // parent_frame_id
+ 0, // parent_render_frame_id
ResourceType::SUB_RESOURCE, // resource_type
PAGE_TRANSITION_LINK, // transition_type
false, // should_replace_current_entry
@@ -159,7 +158,8 @@ class ResourceSchedulerTest : public testing::Test {
false, // is_stream
true, // allow_download
false, // has_user_gesture
- blink::WebReferrerPolicyDefault, // referrer_policy
+ blink::WebReferrerPolicyDefault, // referrer_policy
+ blink::WebPageVisibilityStateVisible, // visibility_state
NULL, // context
base::WeakPtr<ResourceMessageFilter>(), // filter
true); // is_async
@@ -189,20 +189,20 @@ class ResourceSchedulerTest : public testing::Test {
}
void ChangeRequestPriority(TestRequest* request,
- net::RequestPriority new_priority) {
+ net::RequestPriority new_priority,
+ int intra_priority = 0) {
scoped_refptr<FakeResourceMessageFilter> filter(
new FakeResourceMessageFilter(kChildId));
const ResourceRequestInfoImpl* info = ResourceRequestInfoImpl::ForRequest(
request->url_request());
const GlobalRequestID& id = info->GetGlobalRequestID();
- ResourceHostMsg_DidChangePriority msg(id.request_id, new_priority);
- bool ok = false;
- rdh_.OnMessageReceived(msg, filter.get(), &ok);
- EXPECT_TRUE(ok);
+ ResourceHostMsg_DidChangePriority msg(id.request_id, new_priority,
+ intra_priority);
+ rdh_.OnMessageReceived(msg, filter.get());
}
int next_request_id_;
- base::MessageLoop message_loop_;
+ base::MessageLoopForIO message_loop_;
BrowserThreadImpl ui_thread_;
BrowserThreadImpl io_thread_;
ResourceDispatcherHostImpl rdh_;
@@ -454,6 +454,28 @@ TEST_F(ResourceSchedulerTest, ReprioritizedRequestGoesToBackOfQueue) {
EXPECT_FALSE(idle->started());
}
+TEST_F(ResourceSchedulerTest, HigherIntraPriorityGoesToFrontOfQueue) {
+ // Dummies to enforce scheduling.
+ scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
+ scoped_ptr<TestRequest> low(NewRequest("http://host/high", net::LOWEST));
+
+ const int kMaxNumDelayableRequestsPerClient = 10; // Should match the .cc.
+ ScopedVector<TestRequest> lows;
+ for (int i = 0; i < kMaxNumDelayableRequestsPerClient; ++i) {
+ string url = "http://host/low" + base::IntToString(i);
+ lows.push_back(NewRequest(url.c_str(), net::IDLE));
+ }
+
+ scoped_ptr<TestRequest> request(NewRequest("http://host/req", net::IDLE));
+ EXPECT_FALSE(request->started());
+
+ ChangeRequestPriority(request.get(), net::IDLE, 1);
+ EXPECT_FALSE(request->started());
+
+ scheduler_.OnWillInsertBody(kChildId, kRouteId);
+ EXPECT_TRUE(request->started());
+}
+
TEST_F(ResourceSchedulerTest, NonHTTPSchedulesImmediately) {
// Dummies to enforce scheduling.
scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
@@ -464,6 +486,51 @@ TEST_F(ResourceSchedulerTest, NonHTTPSchedulesImmediately) {
EXPECT_TRUE(request->started());
}
+TEST_F(ResourceSchedulerTest, SpdyProxySchedulesImmediately) {
+ scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
+ scoped_ptr<TestRequest> low(NewRequest("http://host/low", net::LOWEST));
+
+ scoped_ptr<TestRequest> request(NewRequest("http://host/req", net::IDLE));
+ EXPECT_FALSE(request->started());
+
+ scheduler_.OnReceivedSpdyProxiedHttpResponse(kChildId, kRouteId);
+ EXPECT_TRUE(request->started());
+
+ scoped_ptr<TestRequest> after(NewRequest("http://host/after", net::IDLE));
+ EXPECT_TRUE(after->started());
+}
+
+TEST_F(ResourceSchedulerTest, NewSpdyHostInDelayableRequests) {
+ scheduler_.OnWillInsertBody(kChildId, kRouteId);
+ const int kMaxNumDelayableRequestsPerClient = 10; // Should match the .cc.
+
+ scoped_ptr<TestRequest> low1_spdy(
+ NewRequest("http://spdyhost1:8080/low", net::LOWEST));
+ // Cancel a request after we learn the server supports SPDY.
+ ScopedVector<TestRequest> lows;
+ for (int i = 0; i < kMaxNumDelayableRequestsPerClient - 1; ++i) {
+ string url = "http://host" + base::IntToString(i) + "/low";
+ lows.push_back(NewRequest(url.c_str(), net::LOWEST));
+ }
+ scoped_ptr<TestRequest> low1(NewRequest("http://host/low", net::LOWEST));
+ EXPECT_FALSE(low1->started());
+ http_server_properties_.SetSupportsSpdy(
+ net::HostPortPair("spdyhost1", 8080), true);
+ low1_spdy.reset();
+ EXPECT_TRUE(low1->started());
+
+ low1.reset();
+ scoped_ptr<TestRequest> low2_spdy(
+ NewRequest("http://spdyhost2:8080/low", net::IDLE));
+ // Reprioritize a request after we learn the server supports SPDY.
+ EXPECT_TRUE(low2_spdy->started());
+ http_server_properties_.SetSupportsSpdy(
+ net::HostPortPair("spdyhost2", 8080), true);
+ ChangeRequestPriority(low2_spdy.get(), net::LOWEST);
+ scoped_ptr<TestRequest> low2(NewRequest("http://host/low", net::LOWEST));
+ EXPECT_TRUE(low2->started());
+}
+
} // unnamed namespace
} // namespace content
diff --git a/chromium/content/browser/loader/stream_resource_handler.cc b/chromium/content/browser/loader/stream_resource_handler.cc
index 67309e8189d..46713fc67fa 100644
--- a/chromium/content/browser/loader/stream_resource_handler.cc
+++ b/chromium/content/browser/loader/stream_resource_handler.cc
@@ -9,22 +9,21 @@
#include "content/browser/streams/stream.h"
#include "content/browser/streams/stream_registry.h"
#include "content/public/browser/resource_controller.h"
-#include "content/public/common/url_constants.h"
#include "net/base/io_buffer.h"
#include "net/url_request/url_request_status.h"
+#include "url/url_constants.h"
namespace content {
-StreamResourceHandler::StreamResourceHandler(
- net::URLRequest* request,
- StreamRegistry* registry,
- const GURL& origin)
+StreamResourceHandler::StreamResourceHandler(net::URLRequest* request,
+ StreamRegistry* registry,
+ const GURL& origin)
: ResourceHandler(request),
read_buffer_(NULL) {
// TODO(tyoshino): Find a way to share this with the blob URL creation in
// WebKit.
- GURL url(std::string(chrome::kBlobScheme) + ":" +
- origin.spec() + base::GenerateGUID());
+ GURL url(std::string(url::kBlobScheme) + ":" + origin.spec() +
+ base::GenerateGUID());
stream_ = new Stream(registry, this, url);
}
@@ -32,33 +31,31 @@ StreamResourceHandler::~StreamResourceHandler() {
stream_->RemoveWriteObserver(this);
}
-bool StreamResourceHandler::OnUploadProgress(int request_id,
- uint64 position,
+bool StreamResourceHandler::OnUploadProgress(uint64 position,
uint64 size) {
return true;
}
-bool StreamResourceHandler::OnRequestRedirected(int request_id,
- const GURL& url,
+bool StreamResourceHandler::OnRequestRedirected(const GURL& url,
ResourceResponse* resp,
bool* defer) {
return true;
}
-bool StreamResourceHandler::OnResponseStarted(int request_id,
- ResourceResponse* resp,
+bool StreamResourceHandler::OnResponseStarted(ResourceResponse* resp,
bool* defer) {
return true;
}
-bool StreamResourceHandler::OnWillStart(int request_id,
- const GURL& url,
- bool* defer) {
+bool StreamResourceHandler::OnWillStart(const GURL& url, bool* defer) {
return true;
}
-bool StreamResourceHandler::OnWillRead(int request_id,
- scoped_refptr<net::IOBuffer>* buf,
+bool StreamResourceHandler::OnBeforeNetworkStart(const GURL& url, bool* defer) {
+ return true;
+}
+
+bool StreamResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf,
int* buf_size,
int min_size) {
static const int kReadBufSize = 32768;
@@ -72,9 +69,7 @@ bool StreamResourceHandler::OnWillRead(int request_id,
return true;
}
-bool StreamResourceHandler::OnReadCompleted(int request_id,
- int bytes_read,
- bool* defer) {
+bool StreamResourceHandler::OnReadCompleted(int bytes_read, bool* defer) {
if (!bytes_read)
return true;
@@ -83,8 +78,8 @@ bool StreamResourceHandler::OnReadCompleted(int request_id,
// Release the ownership of the buffer, and store a reference
// to it. A new one will be allocated in OnWillRead().
- net::IOBuffer* buffer = NULL;
- read_buffer_.swap(&buffer);
+ scoped_refptr<net::IOBuffer> buffer;
+ read_buffer_.swap(buffer);
stream_->AddData(buffer, bytes_read);
if (!stream_->can_add_data())
@@ -94,16 +89,13 @@ bool StreamResourceHandler::OnReadCompleted(int request_id,
}
void StreamResourceHandler::OnResponseCompleted(
- int request_id,
const net::URLRequestStatus& status,
const std::string& sec_info,
bool* defer) {
stream_->Finalize();
}
-void StreamResourceHandler::OnDataDownloaded(
- int request_id,
- int bytes_downloaded) {
+void StreamResourceHandler::OnDataDownloaded(int bytes_downloaded) {
NOTREACHED();
}
diff --git a/chromium/content/browser/loader/stream_resource_handler.h b/chromium/content/browser/loader/stream_resource_handler.h
index 14c576705d4..3b8bc217890 100644
--- a/chromium/content/browser/loader/stream_resource_handler.h
+++ b/chromium/content/browser/loader/stream_resource_handler.h
@@ -31,41 +31,33 @@ class StreamResourceHandler : public StreamWriteObserver,
const GURL& origin);
virtual ~StreamResourceHandler();
- virtual bool OnUploadProgress(int request_id,
- uint64 position,
- uint64 size) OVERRIDE;
+ virtual bool OnUploadProgress(uint64 position, uint64 size) OVERRIDE;
// Not needed, as this event handler ought to be the final resource.
- virtual bool OnRequestRedirected(int request_id,
- const GURL& url,
+ virtual bool OnRequestRedirected(const GURL& url,
ResourceResponse* resp,
bool* defer) OVERRIDE;
- virtual bool OnResponseStarted(int request_id,
- ResourceResponse* resp,
+ virtual bool OnResponseStarted(ResourceResponse* resp,
bool* defer) OVERRIDE;
- virtual bool OnWillStart(int request_id,
- const GURL& url,
- bool* defer) OVERRIDE;
+ virtual bool OnWillStart(const GURL& url, bool* defer) OVERRIDE;
+
+ virtual bool OnBeforeNetworkStart(const GURL& url, bool* defer) OVERRIDE;
// Create a new buffer to store received data.
- virtual bool OnWillRead(int request_id,
- scoped_refptr<net::IOBuffer>* buf,
+ virtual bool OnWillRead(scoped_refptr<net::IOBuffer>* buf,
int* buf_size,
int min_size) OVERRIDE;
// A read was completed, forward the data to the Stream.
- virtual bool OnReadCompleted(int request_id,
- int bytes_read,
- bool* defer) OVERRIDE;
+ virtual bool OnReadCompleted(int bytes_read, bool* defer) OVERRIDE;
- virtual void OnResponseCompleted(int request_id,
- const net::URLRequestStatus& status,
+ virtual void OnResponseCompleted(const net::URLRequestStatus& status,
const std::string& sec_info,
bool* defer) OVERRIDE;
- virtual void OnDataDownloaded(int request_id, int bytes_downloaded) OVERRIDE;
+ virtual void OnDataDownloaded(int bytes_downloaded) OVERRIDE;
Stream* stream() { return stream_.get(); }
diff --git a/chromium/content/browser/loader/sync_resource_handler.cc b/chromium/content/browser/loader/sync_resource_handler.cc
index 1310d0f3669..090b9391056 100644
--- a/chromium/content/browser/loader/sync_resource_handler.cc
+++ b/chromium/content/browser/loader/sync_resource_handler.cc
@@ -10,7 +10,6 @@
#include "content/browser/loader/resource_message_filter.h"
#include "content/browser/loader/resource_request_info_impl.h"
#include "content/common/resource_messages.h"
-#include "content/public/browser/global_request_id.h"
#include "content/public/browser/resource_dispatcher_host_delegate.h"
#include "content/public/browser/resource_request_info.h"
#include "net/base/io_buffer.h"
@@ -25,7 +24,8 @@ SyncResourceHandler::SyncResourceHandler(
: ResourceHandler(request),
read_buffer_(new net::IOBuffer(kReadBufSize)),
result_message_(result_message),
- rdh_(resource_dispatcher_host) {
+ rdh_(resource_dispatcher_host),
+ total_transfer_size_(0) {
result_.final_url = request->url();
}
@@ -40,14 +40,11 @@ SyncResourceHandler::~SyncResourceHandler() {
}
}
-bool SyncResourceHandler::OnUploadProgress(int request_id,
- uint64 position,
- uint64 size) {
+bool SyncResourceHandler::OnUploadProgress(uint64 position, uint64 size) {
return true;
}
bool SyncResourceHandler::OnRequestRedirected(
- int request_id,
const GURL& new_url,
ResourceResponse* response,
bool* defer) {
@@ -65,11 +62,12 @@ bool SyncResourceHandler::OnRequestRedirected(
return false;
}
result_.final_url = new_url;
+
+ total_transfer_size_ += request()->GetTotalReceivedBytes();
return true;
}
bool SyncResourceHandler::OnResponseStarted(
- int request_id,
ResourceResponse* response,
bool* defer) {
const ResourceRequestInfoImpl* info = GetRequestInfo();
@@ -95,14 +93,15 @@ bool SyncResourceHandler::OnResponseStarted(
return true;
}
-bool SyncResourceHandler::OnWillStart(int request_id,
- const GURL& url,
- bool* defer) {
+bool SyncResourceHandler::OnWillStart(const GURL& url, bool* defer) {
+ return true;
+}
+
+bool SyncResourceHandler::OnBeforeNetworkStart(const GURL& url, bool* defer) {
return true;
}
-bool SyncResourceHandler::OnWillRead(int request_id,
- scoped_refptr<net::IOBuffer>* buf,
+bool SyncResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf,
int* buf_size,
int min_size) {
DCHECK(min_size == -1);
@@ -111,8 +110,7 @@ bool SyncResourceHandler::OnWillRead(int request_id,
return true;
}
-bool SyncResourceHandler::OnReadCompleted(int request_id, int bytes_read,
- bool* defer) {
+bool SyncResourceHandler::OnReadCompleted(int bytes_read, bool* defer) {
if (!bytes_read)
return true;
result_.data.append(read_buffer_->data(), bytes_read);
@@ -120,7 +118,6 @@ bool SyncResourceHandler::OnReadCompleted(int request_id, int bytes_read,
}
void SyncResourceHandler::OnResponseCompleted(
- int request_id,
const net::URLRequestStatus& status,
const std::string& security_info,
bool* defer) {
@@ -130,8 +127,8 @@ void SyncResourceHandler::OnResponseCompleted(
result_.error_code = status.error();
- result_.encoded_data_length =
- DevToolsNetLogObserver::GetAndResetEncodedDataLength(request());
+ int total_transfer_size = request()->GetTotalReceivedBytes();
+ result_.encoded_data_length = total_transfer_size_ + total_transfer_size;
ResourceHostMsg_SyncLoad::WriteReplyParams(result_message_, result_);
filter->Send(result_message_);
@@ -139,9 +136,7 @@ void SyncResourceHandler::OnResponseCompleted(
return;
}
-void SyncResourceHandler::OnDataDownloaded(
- int request_id,
- int bytes_downloaded) {
+void SyncResourceHandler::OnDataDownloaded(int bytes_downloaded) {
// Sync requests don't involve ResourceMsg_DataDownloaded messages
// being sent back to renderers as progress is made.
}
diff --git a/chromium/content/browser/loader/sync_resource_handler.h b/chromium/content/browser/loader/sync_resource_handler.h
index 996393da0a3..693b48f3b37 100644
--- a/chromium/content/browser/loader/sync_resource_handler.h
+++ b/chromium/content/browser/loader/sync_resource_handler.h
@@ -33,31 +33,22 @@ class SyncResourceHandler : public ResourceHandler {
ResourceDispatcherHostImpl* resource_dispatcher_host);
virtual ~SyncResourceHandler();
- virtual bool OnUploadProgress(int request_id,
- uint64 position,
- uint64 size) OVERRIDE;
- virtual bool OnRequestRedirected(int request_id,
- const GURL& new_url,
+ virtual bool OnUploadProgress(uint64 position, uint64 size) OVERRIDE;
+ virtual bool OnRequestRedirected(const GURL& new_url,
ResourceResponse* response,
bool* defer) OVERRIDE;
- virtual bool OnResponseStarted(int request_id,
- ResourceResponse* response,
+ virtual bool OnResponseStarted(ResourceResponse* response,
bool* defer) OVERRIDE;
- virtual bool OnWillStart(int request_id,
- const GURL& url,
- bool* defer) OVERRIDE;
- virtual bool OnWillRead(int request_id,
- scoped_refptr<net::IOBuffer>* buf,
+ virtual bool OnWillStart(const GURL& url, bool* defer) OVERRIDE;
+ virtual bool OnBeforeNetworkStart(const GURL& url, bool* defer) OVERRIDE;
+ virtual bool OnWillRead(scoped_refptr<net::IOBuffer>* buf,
int* buf_size,
int min_size) OVERRIDE;
- virtual bool OnReadCompleted(int request_id,
- int bytes_read,
- bool* defer) OVERRIDE;
- virtual void OnResponseCompleted(int request_id,
- const net::URLRequestStatus& status,
+ virtual bool OnReadCompleted(int bytes_read, bool* defer) OVERRIDE;
+ virtual void OnResponseCompleted(const net::URLRequestStatus& status,
const std::string& security_info,
bool* defer) OVERRIDE;
- virtual void OnDataDownloaded(int request_id, int bytes_downloaded) OVERRIDE;
+ virtual void OnDataDownloaded(int bytes_downloaded) OVERRIDE;
private:
enum { kReadBufSize = 3840 };
@@ -67,6 +58,7 @@ class SyncResourceHandler : public ResourceHandler {
SyncLoadResult result_;
IPC::Message* result_message_;
ResourceDispatcherHostImpl* rdh_;
+ int64 total_transfer_size_;
};
} // namespace content
diff --git a/chromium/content/browser/loader/temporary_file_stream.cc b/chromium/content/browser/loader/temporary_file_stream.cc
new file mode 100644
index 00000000000..0569d490100
--- /dev/null
+++ b/chromium/content/browser/loader/temporary_file_stream.cc
@@ -0,0 +1,63 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/loader/temporary_file_stream.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file_proxy.h"
+#include "base/memory/ref_counted.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/base/file_stream.h"
+#include "webkit/common/blob/shareable_file_reference.h"
+
+using webkit_blob::ShareableFileReference;
+
+namespace content {
+
+namespace {
+
+void DidCreateTemporaryFile(
+ const CreateTemporaryFileStreamCallback& callback,
+ scoped_ptr<base::FileProxy> file_proxy,
+ base::File::Error error_code,
+ const base::FilePath& file_path) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ if (!file_proxy->IsValid()) {
+ callback.Run(error_code, scoped_ptr<net::FileStream>(), NULL);
+ return;
+ }
+
+ scoped_refptr<base::TaskRunner> task_runner =
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE);
+
+ // Cancelled or not, create the deletable_file so the temporary is cleaned up.
+ scoped_refptr<ShareableFileReference> deletable_file =
+ ShareableFileReference::GetOrCreate(
+ file_path,
+ ShareableFileReference::DELETE_ON_FINAL_RELEASE,
+ task_runner.get());
+
+ scoped_ptr<net::FileStream> file_stream(
+ new net::FileStream(file_proxy->TakeFile(), task_runner));
+
+ callback.Run(error_code, file_stream.Pass(), deletable_file);
+}
+
+} // namespace
+
+void CreateTemporaryFileStream(
+ const CreateTemporaryFileStreamCallback& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ scoped_ptr<base::FileProxy> file_proxy(new base::FileProxy(
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE).get()));
+ base::FileProxy* proxy = file_proxy.get();
+ proxy->CreateTemporary(
+ base::File::FLAG_ASYNC,
+ base::Bind(&DidCreateTemporaryFile, callback, Passed(&file_proxy)));
+}
+
+} // namespace content
diff --git a/chromium/content/browser/loader/temporary_file_stream.h b/chromium/content/browser/loader/temporary_file_stream.h
new file mode 100644
index 00000000000..1cb4bc25be8
--- /dev/null
+++ b/chromium/content/browser/loader/temporary_file_stream.h
@@ -0,0 +1,45 @@
+// 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_LOADER_TEMPORARY_FILE_STREAM_H_
+#define CONTENT_BROWSER_LOADER_TEMPORARY_FILE_STREAM_H_
+
+#include "base/callback_forward.h"
+#include "base/files/file.h"
+#include "base/memory/scoped_ptr.h"
+#include "content/common/content_export.h"
+
+namespace net {
+class FileStream;
+}
+
+namespace webkit_blob {
+class ShareableFileReference;
+}
+
+namespace content {
+
+typedef base::Callback<void(base::File::Error,
+ scoped_ptr<net::FileStream>,
+ webkit_blob::ShareableFileReference*)>
+ CreateTemporaryFileStreamCallback;
+
+// Creates a temporary file and asynchronously calls |callback| with a
+// net::FileStream and webkit_blob::ShareableFileReference. The file is deleted
+// when the webkit_blob::ShareableFileReference is deleted. Note it is the
+// consumer's responsibility to ensure the webkit_blob::ShareableFileReference
+// stays in scope until net::FileStream has finished closing the file. On error,
+// |callback| is called with an error in the first parameter.
+//
+// This function may only be called on the IO thread.
+//
+// TODO(davidben): Juggling the net::FileStream and
+// webkit_blob::ShareableFileReference lifetimes is a nuisance. The two should
+// be tied together so the consumer need not deal with it.
+CONTENT_EXPORT void CreateTemporaryFileStream(
+ const CreateTemporaryFileStreamCallback& callback);
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_LOADER_TEMPORARY_FILE_STREAM_H_
diff --git a/chromium/content/browser/loader/temporary_file_stream_unittest.cc b/chromium/content/browser/loader/temporary_file_stream_unittest.cc
new file mode 100644
index 00000000000..1903d446b76
--- /dev/null
+++ b/chromium/content/browser/loader/temporary_file_stream_unittest.cc
@@ -0,0 +1,120 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/loader/temporary_file_stream.h"
+
+#include <string.h>
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/run_loop.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "net/base/file_stream.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/common/blob/shareable_file_reference.h"
+
+using webkit_blob::ShareableFileReference;
+
+namespace content {
+
+namespace {
+
+const char kTestData[] = "0123456789";
+const int kTestDataSize = arraysize(kTestData) - 1;
+
+class WaitForFileStream {
+ public:
+ base::File::Error error() const { return error_; }
+ net::FileStream* file_stream() const { return file_stream_.get(); }
+ ShareableFileReference* deletable_file() const {
+ return deletable_file_.get();
+ }
+
+ void OnFileStreamCreated(base::File::Error error,
+ scoped_ptr<net::FileStream> file_stream,
+ ShareableFileReference* deletable_file) {
+ error_ = error;
+ file_stream_ = file_stream.Pass();
+ deletable_file_ = deletable_file;
+ loop_.Quit();
+ }
+
+ void Wait() {
+ loop_.Run();
+ }
+
+ void Release() {
+ file_stream_.reset(NULL);
+ deletable_file_ = NULL;
+ }
+ private:
+ base::RunLoop loop_;
+ base::File::Error error_;
+ scoped_ptr<net::FileStream> file_stream_;
+ scoped_refptr<ShareableFileReference> deletable_file_;
+};
+
+} // namespace
+
+TEST(TemporaryFileStreamTest, Basic) {
+ TestBrowserThreadBundle thread_bundle(TestBrowserThreadBundle::IO_MAINLOOP);
+
+ // Create a temporary.
+ WaitForFileStream file_stream_waiter;
+ CreateTemporaryFileStream(base::Bind(&WaitForFileStream::OnFileStreamCreated,
+ base::Unretained(&file_stream_waiter)));
+ file_stream_waiter.Wait();
+
+ // The temporary should exist.
+ EXPECT_EQ(base::File::FILE_OK, file_stream_waiter.error());
+ base::FilePath file_path = file_stream_waiter.deletable_file()->path();
+ EXPECT_TRUE(base::PathExists(file_path));
+
+ // Write some data to the temporary.
+ int bytes_written = 0;
+ scoped_refptr<net::IOBufferWithSize> buf =
+ new net::IOBufferWithSize(kTestDataSize);
+ memcpy(buf->data(), kTestData, kTestDataSize);
+ scoped_refptr<net::DrainableIOBuffer> drainable =
+ new net::DrainableIOBuffer(buf.get(), buf->size());
+ while (bytes_written != kTestDataSize) {
+ net::TestCompletionCallback write_callback;
+ int rv = file_stream_waiter.file_stream()->Write(
+ drainable.get(), drainable->BytesRemaining(),
+ write_callback.callback());
+ if (rv == net::ERR_IO_PENDING)
+ rv = write_callback.WaitForResult();
+ ASSERT_LT(0, rv);
+ drainable->DidConsume(rv);
+ bytes_written += rv;
+ }
+
+ // Verify the data matches.
+ std::string contents;
+ ASSERT_TRUE(base::ReadFileToString(file_path, &contents));
+ EXPECT_EQ(kTestData, contents);
+
+ // Close the file.
+ net::TestCompletionCallback close_callback;
+ int rv = file_stream_waiter.file_stream()->Close(close_callback.callback());
+ if (rv == net::ERR_IO_PENDING)
+ rv = close_callback.WaitForResult();
+ EXPECT_EQ(net::OK, rv);
+
+ // Release everything. The file should be gone now.
+ file_stream_waiter.Release();
+ base::MessageLoop::current()->RunUntilIdle();
+
+ // The temporary should be gone now.
+ EXPECT_FALSE(base::PathExists(file_path));
+}
+
+} // content
diff --git a/chromium/content/browser/loader/throttling_resource_handler.cc b/chromium/content/browser/loader/throttling_resource_handler.cc
index 36c595fbf1b..a302f1da797 100644
--- a/chromium/content/browser/loader/throttling_resource_handler.cc
+++ b/chromium/content/browser/loader/throttling_resource_handler.cc
@@ -31,8 +31,7 @@ ThrottlingResourceHandler::ThrottlingResourceHandler(
ThrottlingResourceHandler::~ThrottlingResourceHandler() {
}
-bool ThrottlingResourceHandler::OnRequestRedirected(int request_id,
- const GURL& new_url,
+bool ThrottlingResourceHandler::OnRequestRedirected(const GURL& new_url,
ResourceResponse* response,
bool* defer) {
DCHECK(!cancelled_by_resource_throttle_);
@@ -55,13 +54,10 @@ bool ThrottlingResourceHandler::OnRequestRedirected(int request_id,
next_index_ = 0; // Reset for next time.
- return next_handler_->OnRequestRedirected(request_id, new_url, response,
- defer);
+ return next_handler_->OnRequestRedirected(new_url, response, defer);
}
-bool ThrottlingResourceHandler::OnWillStart(int request_id,
- const GURL& url,
- bool* defer) {
+bool ThrottlingResourceHandler::OnWillStart(const GURL& url, bool* defer) {
DCHECK(!cancelled_by_resource_throttle_);
*defer = false;
@@ -81,11 +77,34 @@ bool ThrottlingResourceHandler::OnWillStart(int request_id,
next_index_ = 0; // Reset for next time.
- return next_handler_->OnWillStart(request_id, url, defer);
+ return next_handler_->OnWillStart(url, defer);
}
-bool ThrottlingResourceHandler::OnResponseStarted(int request_id,
- ResourceResponse* response,
+bool ThrottlingResourceHandler::OnBeforeNetworkStart(const GURL& url,
+ bool* defer) {
+ DCHECK(!cancelled_by_resource_throttle_);
+
+ *defer = false;
+ while (next_index_ < throttles_.size()) {
+ int index = next_index_;
+ throttles_[index]->OnBeforeNetworkStart(defer);
+ next_index_++;
+ if (cancelled_by_resource_throttle_)
+ return false;
+ if (*defer) {
+ OnRequestDefered(index);
+ deferred_stage_ = DEFERRED_NETWORK_START;
+ deferred_url_ = url;
+ return true; // Do not cancel.
+ }
+ }
+
+ next_index_ = 0; // Reset for next time.
+
+ return next_handler_->OnBeforeNetworkStart(url, defer);
+}
+
+bool ThrottlingResourceHandler::OnResponseStarted(ResourceResponse* response,
bool* defer) {
DCHECK(!cancelled_by_resource_throttle_);
@@ -105,7 +124,7 @@ bool ThrottlingResourceHandler::OnResponseStarted(int request_id,
next_index_ = 0; // Reset for next time.
- return next_handler_->OnResponseStarted(request_id, response, defer);
+ return next_handler_->OnResponseStarted(response, defer);
}
void ThrottlingResourceHandler::Cancel() {
@@ -137,6 +156,9 @@ void ThrottlingResourceHandler::Resume() {
case DEFERRED_START:
ResumeStart();
break;
+ case DEFERRED_NETWORK_START:
+ ResumeNetworkStart();
+ break;
case DEFERRED_REDIRECT:
ResumeRedirect();
break;
@@ -153,7 +175,21 @@ void ThrottlingResourceHandler::ResumeStart() {
deferred_url_ = GURL();
bool defer = false;
- if (!OnWillStart(GetRequestID(), url, &defer)) {
+ if (!OnWillStart(url, &defer)) {
+ controller()->Cancel();
+ } else if (!defer) {
+ controller()->Resume();
+ }
+}
+
+void ThrottlingResourceHandler::ResumeNetworkStart() {
+ DCHECK(!cancelled_by_resource_throttle_);
+
+ GURL url = deferred_url_;
+ deferred_url_ = GURL();
+
+ bool defer = false;
+ if (!OnBeforeNetworkStart(url, &defer)) {
controller()->Cancel();
} else if (!defer) {
controller()->Resume();
@@ -169,7 +205,7 @@ void ThrottlingResourceHandler::ResumeRedirect() {
deferred_response_.swap(response);
bool defer = false;
- if (!OnRequestRedirected(GetRequestID(), new_url, response.get(), &defer)) {
+ if (!OnRequestRedirected(new_url, response.get(), &defer)) {
controller()->Cancel();
} else if (!defer) {
controller()->Resume();
@@ -183,7 +219,7 @@ void ThrottlingResourceHandler::ResumeResponse() {
deferred_response_.swap(response);
bool defer = false;
- if (!OnResponseStarted(GetRequestID(), response.get(), &defer)) {
+ if (!OnResponseStarted(response.get(), &defer)) {
controller()->Cancel();
} else if (!defer) {
controller()->Resume();
diff --git a/chromium/content/browser/loader/throttling_resource_handler.h b/chromium/content/browser/loader/throttling_resource_handler.h
index 9f98921e26f..fd284dbba2c 100644
--- a/chromium/content/browser/loader/throttling_resource_handler.h
+++ b/chromium/content/browser/loader/throttling_resource_handler.h
@@ -31,14 +31,13 @@ class ThrottlingResourceHandler : public LayeredResourceHandler,
virtual ~ThrottlingResourceHandler();
// LayeredResourceHandler overrides:
- virtual bool OnRequestRedirected(int request_id, const GURL& url,
+ virtual bool OnRequestRedirected(const GURL& url,
ResourceResponse* response,
bool* defer) OVERRIDE;
- virtual bool OnResponseStarted(int request_id,
- ResourceResponse* response,
+ virtual bool OnResponseStarted(ResourceResponse* response,
bool* defer) OVERRIDE;
- virtual bool OnWillStart(int request_id, const GURL& url,
- bool* defer) OVERRIDE;
+ virtual bool OnWillStart(const GURL& url, bool* defer) OVERRIDE;
+ virtual bool OnBeforeNetworkStart(const GURL& url, bool* defer) OVERRIDE;
// ResourceController implementation:
virtual void Cancel() OVERRIDE;
@@ -48,6 +47,7 @@ class ThrottlingResourceHandler : public LayeredResourceHandler,
private:
void ResumeStart();
+ void ResumeNetworkStart();
void ResumeRedirect();
void ResumeResponse();
@@ -58,6 +58,7 @@ class ThrottlingResourceHandler : public LayeredResourceHandler,
enum DeferredStage {
DEFERRED_NONE,
DEFERRED_START,
+ DEFERRED_NETWORK_START,
DEFERRED_REDIRECT,
DEFERRED_RESPONSE
};
diff --git a/chromium/content/browser/loader/upload_data_stream_builder.cc b/chromium/content/browser/loader/upload_data_stream_builder.cc
index ba7cca77d25..2db9fdf0fef 100644
--- a/chromium/content/browser/loader/upload_data_stream_builder.cc
+++ b/chromium/content/browser/loader/upload_data_stream_builder.cc
@@ -5,18 +5,17 @@
#include "content/browser/loader/upload_data_stream_builder.h"
#include "base/logging.h"
+#include "content/browser/fileapi/upload_file_system_file_element_reader.h"
+#include "content/common/resource_request_body.h"
#include "net/base/upload_bytes_element_reader.h"
#include "net/base/upload_data_stream.h"
#include "net/base/upload_file_element_reader.h"
#include "webkit/browser/blob/blob_data_handle.h"
#include "webkit/browser/blob/blob_storage_context.h"
-#include "webkit/browser/fileapi/upload_file_system_file_element_reader.h"
-#include "webkit/common/resource_request_body.h"
using webkit_blob::BlobData;
using webkit_blob::BlobDataHandle;
using webkit_blob::BlobStorageContext;
-using webkit_glue::ResourceRequestBody;
namespace content {
namespace {
@@ -123,7 +122,7 @@ scoped_ptr<net::UploadDataStream> UploadDataStreamBuilder::Build(
break;
case ResourceRequestBody::Element::TYPE_FILE_FILESYSTEM:
element_readers.push_back(
- new fileapi::UploadFileSystemFileElementReader(
+ new content::UploadFileSystemFileElementReader(
file_system_context,
element.filesystem_url(),
element.offset(),
diff --git a/chromium/content/browser/loader/upload_data_stream_builder.h b/chromium/content/browser/loader/upload_data_stream_builder.h
index e2b6ce3fc29..60b6f337c22 100644
--- a/chromium/content/browser/loader/upload_data_stream_builder.h
+++ b/chromium/content/browser/loader/upload_data_stream_builder.h
@@ -24,12 +24,10 @@ namespace webkit_blob {
class BlobStorageContext;
}
-namespace webkit_glue {
-class ResourceRequestBody;
-}
-
namespace content {
+class ResourceRequestBody;
+
class CONTENT_EXPORT UploadDataStreamBuilder {
public:
// Creates a new UploadDataStream from this request body.
@@ -43,7 +41,7 @@ class CONTENT_EXPORT UploadDataStreamBuilder {
// filesystem URLs. |file_task_runner| is used to perform file operations
// when the data gets uploaded.
static scoped_ptr<net::UploadDataStream> Build(
- webkit_glue::ResourceRequestBody* body,
+ ResourceRequestBody* body,
webkit_blob::BlobStorageContext* blob_context,
fileapi::FileSystemContext* file_system_context,
base::TaskRunner* file_task_runner);
diff --git a/chromium/content/browser/loader/upload_data_stream_builder_unittest.cc b/chromium/content/browser/loader/upload_data_stream_builder_unittest.cc
index 4c9661cbee6..67a57e3d9ab 100644
--- a/chromium/content/browser/loader/upload_data_stream_builder_unittest.cc
+++ b/chromium/content/browser/loader/upload_data_stream_builder_unittest.cc
@@ -10,19 +10,19 @@
#include "base/files/file_path.h"
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_proxy.h"
+#include "base/run_loop.h"
#include "base/time/time.h"
+#include "content/common/resource_request_body.h"
#include "net/base/upload_bytes_element_reader.h"
#include "net/base/upload_data_stream.h"
#include "net/base/upload_file_element_reader.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#include "webkit/browser/blob/blob_storage_context.h"
-#include "webkit/common/resource_request_body.h"
using webkit_blob::BlobData;
using webkit_blob::BlobDataHandle;
using webkit_blob::BlobStorageContext;
-using webkit_glue::ResourceRequestBody;
namespace content {
namespace {
@@ -94,170 +94,214 @@ TEST(UploadDataStreamBuilderTest, CreateUploadDataStreamWithoutBlob) {
TEST(UploadDataStreamBuilderTest, ResolveBlobAndCreateUploadDataStream) {
base::MessageLoop message_loop;
- // Setup blob data for testing.
- base::Time time1, time2;
- base::Time::FromString("Tue, 15 Nov 1994, 12:45:26 GMT", &time1);
- base::Time::FromString("Mon, 14 Nov 1994, 11:30:49 GMT", &time2);
-
- BlobStorageContext blob_storage_context;
-
- const std::string blob_id0("id-0");
- scoped_refptr<BlobData> blob_data(new BlobData(blob_id0));
- scoped_ptr<BlobDataHandle> handle1 =
- blob_storage_context.AddFinishedBlob(blob_data);
-
- const std::string blob_id1("id-1");
- blob_data = new BlobData(blob_id1);
- blob_data->AppendData("BlobData");
- blob_data->AppendFile(
- base::FilePath(FILE_PATH_LITERAL("BlobFile.txt")), 0, 20, time1);
- scoped_ptr<BlobDataHandle> handle2 =
- blob_storage_context.AddFinishedBlob(blob_data);
-
- // Setup upload data elements for comparison.
- ResourceRequestBody::Element blob_element1, blob_element2;
- blob_element1.SetToBytes(
- blob_data->items().at(0).bytes() +
- static_cast<int>(blob_data->items().at(0).offset()),
- static_cast<int>(blob_data->items().at(0).length()));
- blob_element2.SetToFilePathRange(
- blob_data->items().at(1).path(),
- blob_data->items().at(1).offset(),
- blob_data->items().at(1).length(),
- blob_data->items().at(1).expected_modification_time());
-
- ResourceRequestBody::Element upload_element1, upload_element2;
- upload_element1.SetToBytes("Hello", 5);
- upload_element2.SetToFilePathRange(
- base::FilePath(FILE_PATH_LITERAL("foo1.txt")), 0, 20, time2);
-
- // Test no blob reference.
- scoped_refptr<ResourceRequestBody> request_body(new ResourceRequestBody());
- request_body->AppendBytes(upload_element1.bytes(), upload_element1.length());
- request_body->AppendFileRange(upload_element2.path(),
- upload_element2.offset(),
- upload_element2.length(),
- upload_element2.expected_modification_time());
-
- scoped_ptr<net::UploadDataStream> upload(
- UploadDataStreamBuilder::Build(request_body.get(),
- &blob_storage_context,
- NULL,
- base::MessageLoopProxy::current().get()));
-
- ASSERT_EQ(2U, upload->element_readers().size());
- EXPECT_TRUE(AreElementsEqual(*upload->element_readers()[0], upload_element1));
- EXPECT_TRUE(AreElementsEqual(*upload->element_readers()[1], upload_element2));
-
- // Test having only one blob reference that refers to empty blob data.
- request_body = new ResourceRequestBody();
- request_body->AppendBlob(blob_id0);
-
- upload =
- UploadDataStreamBuilder::Build(request_body.get(),
- &blob_storage_context,
- NULL,
- base::MessageLoopProxy::current().get());
- ASSERT_EQ(0U, upload->element_readers().size());
-
- // Test having only one blob reference.
- request_body = new ResourceRequestBody();
- request_body->AppendBlob(blob_id1);
-
- upload =
- UploadDataStreamBuilder::Build(request_body.get(),
- &blob_storage_context,
- NULL,
- base::MessageLoopProxy::current().get());
- ASSERT_EQ(2U, upload->element_readers().size());
- EXPECT_TRUE(AreElementsEqual(*upload->element_readers()[0], blob_element1));
- EXPECT_TRUE(AreElementsEqual(*upload->element_readers()[1], blob_element2));
-
- // Test having one blob reference at the beginning.
- request_body = new ResourceRequestBody();
- request_body->AppendBlob(blob_id1);
- request_body->AppendBytes(upload_element1.bytes(), upload_element1.length());
- request_body->AppendFileRange(upload_element2.path(),
- upload_element2.offset(),
- upload_element2.length(),
- upload_element2.expected_modification_time());
-
- upload =
- UploadDataStreamBuilder::Build(request_body.get(),
- &blob_storage_context,
- NULL,
- base::MessageLoopProxy::current().get());
- ASSERT_EQ(4U, upload->element_readers().size());
- EXPECT_TRUE(AreElementsEqual(*upload->element_readers()[0], blob_element1));
- EXPECT_TRUE(AreElementsEqual(*upload->element_readers()[1], blob_element2));
- EXPECT_TRUE(AreElementsEqual(*upload->element_readers()[2], upload_element1));
- EXPECT_TRUE(AreElementsEqual(*upload->element_readers()[3], upload_element2));
-
- // Test having one blob reference at the end.
- request_body = new ResourceRequestBody();
- request_body->AppendBytes(upload_element1.bytes(), upload_element1.length());
- request_body->AppendFileRange(upload_element2.path(),
- upload_element2.offset(),
- upload_element2.length(),
- upload_element2.expected_modification_time());
- request_body->AppendBlob(blob_id1);
-
- upload =
- UploadDataStreamBuilder::Build(request_body.get(),
- &blob_storage_context,
- NULL,
- base::MessageLoopProxy::current().get());
- ASSERT_EQ(4U, upload->element_readers().size());
- EXPECT_TRUE(AreElementsEqual(*upload->element_readers()[0], upload_element1));
- EXPECT_TRUE(AreElementsEqual(*upload->element_readers()[1], upload_element2));
- EXPECT_TRUE(AreElementsEqual(*upload->element_readers()[2], blob_element1));
- EXPECT_TRUE(AreElementsEqual(*upload->element_readers()[3], blob_element2));
-
- // Test having one blob reference in the middle.
- request_body = new ResourceRequestBody();
- request_body->AppendBytes(upload_element1.bytes(), upload_element1.length());
- request_body->AppendBlob(blob_id1);
- request_body->AppendFileRange(upload_element2.path(),
- upload_element2.offset(),
- upload_element2.length(),
- upload_element2.expected_modification_time());
-
- upload =
- UploadDataStreamBuilder::Build(request_body.get(),
- &blob_storage_context,
- NULL,
- base::MessageLoopProxy::current().get());
- ASSERT_EQ(4U, upload->element_readers().size());
- EXPECT_TRUE(AreElementsEqual(*upload->element_readers()[0], upload_element1));
- EXPECT_TRUE(AreElementsEqual(*upload->element_readers()[1], blob_element1));
- EXPECT_TRUE(AreElementsEqual(*upload->element_readers()[2], blob_element2));
- EXPECT_TRUE(AreElementsEqual(*upload->element_readers()[3], upload_element2));
-
- // Test having multiple blob references.
- request_body = new ResourceRequestBody();
- request_body->AppendBlob(blob_id1);
- request_body->AppendBytes(upload_element1.bytes(), upload_element1.length());
- request_body->AppendBlob(blob_id1);
- request_body->AppendBlob(blob_id1);
- request_body->AppendFileRange(upload_element2.path(),
- upload_element2.offset(),
- upload_element2.length(),
- upload_element2.expected_modification_time());
-
- upload =
- UploadDataStreamBuilder::Build(request_body.get(),
- &blob_storage_context,
- NULL,
- base::MessageLoopProxy::current().get());
- ASSERT_EQ(8U, upload->element_readers().size());
- EXPECT_TRUE(AreElementsEqual(*upload->element_readers()[0], blob_element1));
- EXPECT_TRUE(AreElementsEqual(*upload->element_readers()[1], blob_element2));
- EXPECT_TRUE(AreElementsEqual(*upload->element_readers()[2], upload_element1));
- EXPECT_TRUE(AreElementsEqual(*upload->element_readers()[3], blob_element1));
- EXPECT_TRUE(AreElementsEqual(*upload->element_readers()[4], blob_element2));
- EXPECT_TRUE(AreElementsEqual(*upload->element_readers()[5], blob_element1));
- EXPECT_TRUE(AreElementsEqual(*upload->element_readers()[6], blob_element2));
- EXPECT_TRUE(AreElementsEqual(*upload->element_readers()[7], upload_element2));
+ {
+ // Setup blob data for testing.
+ base::Time time1, time2;
+ base::Time::FromString("Tue, 15 Nov 1994, 12:45:26 GMT", &time1);
+ base::Time::FromString("Mon, 14 Nov 1994, 11:30:49 GMT", &time2);
+
+ BlobStorageContext blob_storage_context;
+
+ const std::string blob_id0("id-0");
+ scoped_refptr<BlobData> blob_data(new BlobData(blob_id0));
+ scoped_ptr<BlobDataHandle> handle1 =
+ blob_storage_context.AddFinishedBlob(blob_data);
+
+ const std::string blob_id1("id-1");
+ blob_data = new BlobData(blob_id1);
+ blob_data->AppendData("BlobData");
+ blob_data->AppendFile(
+ base::FilePath(FILE_PATH_LITERAL("BlobFile.txt")), 0, 20, time1);
+ scoped_ptr<BlobDataHandle> handle2 =
+ blob_storage_context.AddFinishedBlob(blob_data);
+
+ // Setup upload data elements for comparison.
+ ResourceRequestBody::Element blob_element1, blob_element2;
+ blob_element1.SetToBytes(
+ blob_data->items().at(0).bytes() +
+ static_cast<int>(blob_data->items().at(0).offset()),
+ static_cast<int>(blob_data->items().at(0).length()));
+ blob_element2.SetToFilePathRange(
+ blob_data->items().at(1).path(),
+ blob_data->items().at(1).offset(),
+ blob_data->items().at(1).length(),
+ blob_data->items().at(1).expected_modification_time());
+
+ ResourceRequestBody::Element upload_element1, upload_element2;
+ upload_element1.SetToBytes("Hello", 5);
+ upload_element2.SetToFilePathRange(
+ base::FilePath(FILE_PATH_LITERAL("foo1.txt")), 0, 20, time2);
+
+ // Test no blob reference.
+ scoped_refptr<ResourceRequestBody> request_body(new ResourceRequestBody());
+ request_body->AppendBytes(
+ upload_element1.bytes(),
+ upload_element1.length());
+ request_body->AppendFileRange(
+ upload_element2.path(),
+ upload_element2.offset(),
+ upload_element2.length(),
+ upload_element2.expected_modification_time());
+
+ scoped_ptr<net::UploadDataStream> upload(
+ UploadDataStreamBuilder::Build(
+ request_body.get(),
+ &blob_storage_context,
+ NULL,
+ base::MessageLoopProxy::current().get()));
+
+ ASSERT_EQ(2U, upload->element_readers().size());
+ EXPECT_TRUE(AreElementsEqual(
+ *upload->element_readers()[0], upload_element1));
+ EXPECT_TRUE(AreElementsEqual(
+ *upload->element_readers()[1], upload_element2));
+
+ // Test having only one blob reference that refers to empty blob data.
+ request_body = new ResourceRequestBody();
+ request_body->AppendBlob(blob_id0);
+
+ upload = UploadDataStreamBuilder::Build(
+ request_body.get(),
+ &blob_storage_context,
+ NULL,
+ base::MessageLoopProxy::current().get());
+ ASSERT_EQ(0U, upload->element_readers().size());
+
+ // Test having only one blob reference.
+ request_body = new ResourceRequestBody();
+ request_body->AppendBlob(blob_id1);
+
+ upload = UploadDataStreamBuilder::Build(
+ request_body.get(),
+ &blob_storage_context,
+ NULL,
+ base::MessageLoopProxy::current().get());
+ ASSERT_EQ(2U, upload->element_readers().size());
+ EXPECT_TRUE(AreElementsEqual(
+ *upload->element_readers()[0], blob_element1));
+ EXPECT_TRUE(AreElementsEqual(
+ *upload->element_readers()[1], blob_element2));
+
+ // Test having one blob reference at the beginning.
+ request_body = new ResourceRequestBody();
+ request_body->AppendBlob(blob_id1);
+ request_body->AppendBytes(
+ upload_element1.bytes(),
+ upload_element1.length());
+ request_body->AppendFileRange(
+ upload_element2.path(),
+ upload_element2.offset(),
+ upload_element2.length(),
+ upload_element2.expected_modification_time());
+
+ upload = UploadDataStreamBuilder::Build(
+ request_body.get(),
+ &blob_storage_context,
+ NULL,
+ base::MessageLoopProxy::current().get());
+ ASSERT_EQ(4U, upload->element_readers().size());
+ EXPECT_TRUE(AreElementsEqual(
+ *upload->element_readers()[0], blob_element1));
+ EXPECT_TRUE(AreElementsEqual(
+ *upload->element_readers()[1], blob_element2));
+ EXPECT_TRUE(AreElementsEqual(
+ *upload->element_readers()[2], upload_element1));
+ EXPECT_TRUE(AreElementsEqual(
+ *upload->element_readers()[3], upload_element2));
+
+ // Test having one blob reference at the end.
+ request_body = new ResourceRequestBody();
+ request_body->AppendBytes(
+ upload_element1.bytes(),
+ upload_element1.length());
+ request_body->AppendFileRange(
+ upload_element2.path(),
+ upload_element2.offset(),
+ upload_element2.length(),
+ upload_element2.expected_modification_time());
+ request_body->AppendBlob(blob_id1);
+
+ upload =
+ UploadDataStreamBuilder::Build(request_body.get(),
+ &blob_storage_context,
+ NULL,
+ base::MessageLoopProxy::current().get());
+ ASSERT_EQ(4U, upload->element_readers().size());
+ EXPECT_TRUE(AreElementsEqual(
+ *upload->element_readers()[0], upload_element1));
+ EXPECT_TRUE(AreElementsEqual(
+ *upload->element_readers()[1], upload_element2));
+ EXPECT_TRUE(AreElementsEqual(
+ *upload->element_readers()[2], blob_element1));
+ EXPECT_TRUE(AreElementsEqual(
+ *upload->element_readers()[3], blob_element2));
+
+ // Test having one blob reference in the middle.
+ request_body = new ResourceRequestBody();
+ request_body->AppendBytes(
+ upload_element1.bytes(),
+ upload_element1.length());
+ request_body->AppendBlob(blob_id1);
+ request_body->AppendFileRange(
+ upload_element2.path(),
+ upload_element2.offset(),
+ upload_element2.length(),
+ upload_element2.expected_modification_time());
+
+ upload = UploadDataStreamBuilder::Build(
+ request_body.get(),
+ &blob_storage_context,
+ NULL,
+ base::MessageLoopProxy::current().get());
+ ASSERT_EQ(4U, upload->element_readers().size());
+ EXPECT_TRUE(AreElementsEqual(
+ *upload->element_readers()[0], upload_element1));
+ EXPECT_TRUE(AreElementsEqual(
+ *upload->element_readers()[1], blob_element1));
+ EXPECT_TRUE(AreElementsEqual(
+ *upload->element_readers()[2], blob_element2));
+ EXPECT_TRUE(AreElementsEqual(
+ *upload->element_readers()[3], upload_element2));
+
+ // Test having multiple blob references.
+ request_body = new ResourceRequestBody();
+ request_body->AppendBlob(blob_id1);
+ request_body->AppendBytes(
+ upload_element1.bytes(),
+ upload_element1.length());
+ request_body->AppendBlob(blob_id1);
+ request_body->AppendBlob(blob_id1);
+ request_body->AppendFileRange(
+ upload_element2.path(),
+ upload_element2.offset(),
+ upload_element2.length(),
+ upload_element2.expected_modification_time());
+
+ upload = UploadDataStreamBuilder::Build(
+ request_body.get(),
+ &blob_storage_context,
+ NULL,
+ base::MessageLoopProxy::current().get());
+ ASSERT_EQ(8U, upload->element_readers().size());
+ EXPECT_TRUE(AreElementsEqual(
+ *upload->element_readers()[0], blob_element1));
+ EXPECT_TRUE(AreElementsEqual(
+ *upload->element_readers()[1], blob_element2));
+ EXPECT_TRUE(AreElementsEqual(
+ *upload->element_readers()[2], upload_element1));
+ EXPECT_TRUE(AreElementsEqual(
+ *upload->element_readers()[3], blob_element1));
+ EXPECT_TRUE(AreElementsEqual(
+ *upload->element_readers()[4], blob_element2));
+ EXPECT_TRUE(AreElementsEqual(
+ *upload->element_readers()[5], blob_element1));
+ EXPECT_TRUE(AreElementsEqual(
+ *upload->element_readers()[6], blob_element2));
+ EXPECT_TRUE(AreElementsEqual(
+ *upload->element_readers()[7], upload_element2));
+ }
+ // Clean up for ASAN.
+ base::RunLoop().RunUntilIdle();
}
} // namespace content
diff --git a/chromium/content/browser/mach_broker_mac.h b/chromium/content/browser/mach_broker_mac.h
index 51038bfa010..dede40068eb 100644
--- a/chromium/content/browser/mach_broker_mac.h
+++ b/chromium/content/browser/mach_broker_mac.h
@@ -44,6 +44,10 @@ class CONTENT_EXPORT MachBroker : public base::ProcessMetrics::PortProvider,
// and false if otherwise.
static bool ChildSendTaskPortToParent();
+ // Returns the Mach port name to use when sending or receiving messages.
+ // Does the Right Thing in the browser and in child processes.
+ static std::string GetMachPortName();
+
// Returns the global MachBroker.
static MachBroker* GetInstance();
@@ -93,9 +97,6 @@ class CONTENT_EXPORT MachBroker : public base::ProcessMetrics::PortProvider,
// Removes all mappings belonging to |pid| from the broker.
void InvalidatePid(base::ProcessHandle pid);
- // Returns the Mach port name to use when sending or receiving messages.
- // Does the Right Thing in the browser and in child processes.
- static std::string GetMachPortName();
// Callback used to register notifications on the UI thread.
void RegisterNotifications();
diff --git a/chromium/content/browser/mach_broker_mac.mm b/chromium/content/browser/mach_broker_mac.mm
index 24a0e249813..1c9e6e55bd8 100644
--- a/chromium/content/browser/mach_broker_mac.mm
+++ b/chromium/content/browser/mach_broker_mac.mm
@@ -12,6 +12,7 @@
#include "base/command_line.h"
#include "base/logging.h"
#include "base/mac/foundation_util.h"
+#include "base/mac/mach_logging.h"
#include "base/mac/scoped_mach_port.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
@@ -28,11 +29,6 @@ namespace content {
namespace {
-// Prints a string representation of a Mach error code.
-std::string MachErrorCode(kern_return_t err) {
- return base::StringPrintf("0x%x %s", err, mach_error_string(err));
-}
-
// Mach message structure used in the child as a sending message.
struct MachBroker_ChildSendMsg {
mach_msg_header_t header;
@@ -57,28 +53,27 @@ class MachListenerThreadDelegate : public base::PlatformThread::Delegate {
}
bool Init() {
- DCHECK(server_port_ == MACH_PORT_NULL);
+ DCHECK(server_port_.get() == MACH_PORT_NULL);
mach_port_t port;
kern_return_t kr = mach_port_allocate(mach_task_self(),
MACH_PORT_RIGHT_RECEIVE,
&port);
if (kr != KERN_SUCCESS) {
- LOG(ERROR) << "Failed to allocate MachBroker server port: "
- << MachErrorCode(kr);
+ MACH_LOG(ERROR, kr) << "mach_port_allocate";
return false;
}
+ server_port_.reset(port);
// Allocate a send right for the server port.
kr = mach_port_insert_right(
mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
if (kr != KERN_SUCCESS) {
- LOG(ERROR) << "Failed to insert send right for MachBroker server port: "
- << MachErrorCode(kr);
+ MACH_LOG(ERROR, kr) << "mach_port_insert_right";
return false;
}
-
- server_port_.reset(port);
+ // Deallocate the right after registering with the bootstrap server.
+ base::mac::ScopedMachSendRight send_right(port);
// Register the port with the bootstrap server. Because bootstrap_register
// is deprecated, this has to be wraped in an ObjC interface.
@@ -96,33 +91,35 @@ class MachListenerThreadDelegate : public base::PlatformThread::Delegate {
msg.header.msgh_size = sizeof(msg);
msg.header.msgh_local_port = server_port_.get();
+ const mach_msg_option_t options = MACH_RCV_MSG |
+ MACH_RCV_TRAILER_TYPE(MACH_RCV_TRAILER_AUDIT) |
+ MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT);
+
kern_return_t kr;
- do {
+ while ((kr = mach_msg(&msg.header,
+ options,
+ 0,
+ sizeof(msg),
+ server_port_,
+ MACH_MSG_TIMEOUT_NONE,
+ MACH_PORT_NULL)) == KERN_SUCCESS) {
// Use the kernel audit information to make sure this message is from
// a task that this process spawned. The kernel audit token contains the
// unspoofable pid of the task that sent the message.
- mach_msg_option_t options = MACH_RCV_MSG |
- MACH_RCV_TRAILER_TYPE(MACH_RCV_TRAILER_AUDIT) |
- MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT);
-
- kr = mach_msg(&msg.header, options, 0, sizeof(msg), server_port_,
- MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
- if (kr == KERN_SUCCESS) {
- // TODO(rsesek): In the 10.7 SDK, there's audit_token_to_pid().
- pid_t child_pid;
- audit_token_to_au32(msg.trailer.msgh_audit,
- NULL, NULL, NULL, NULL, NULL, &child_pid, NULL, NULL);
-
- mach_port_t child_task_port = msg.child_task_port.name;
-
- // Take the lock and update the broker information.
- base::AutoLock lock(broker_->GetLock());
- broker_->FinalizePid(child_pid, child_task_port);
- }
- } while (kr == KERN_SUCCESS);
-
- LOG(ERROR) << "MachBroker thread exiting; mach_msg() likely failed: "
- << MachErrorCode(kr);
+ //
+ // TODO(rsesek): In the 10.7 SDK, there's audit_token_to_pid().
+ pid_t child_pid;
+ audit_token_to_au32(msg.trailer.msgh_audit,
+ NULL, NULL, NULL, NULL, NULL, &child_pid, NULL, NULL);
+
+ mach_port_t child_task_port = msg.child_task_port.name;
+
+ // Take the lock and update the broker information.
+ base::AutoLock lock(broker_->GetLock());
+ broker_->FinalizePid(child_pid, child_task_port);
+ }
+
+ MACH_LOG(ERROR, kr) << "mach_msg";
}
private:
@@ -130,28 +127,23 @@ class MachListenerThreadDelegate : public base::PlatformThread::Delegate {
// NULL.
MachBroker* broker_; // weak
- base::mac::ScopedMachPort server_port_;
+ base::mac::ScopedMachReceiveRight server_port_;
DISALLOW_COPY_AND_ASSIGN(MachListenerThreadDelegate);
};
+// static
bool MachBroker::ChildSendTaskPortToParent() {
// Look up the named MachBroker port that's been registered with the
// bootstrap server.
- mach_port_t bootstrap_port;
- kern_return_t kr = task_get_bootstrap_port(mach_task_self(), &bootstrap_port);
- if (kr != KERN_SUCCESS) {
- LOG(ERROR) << "Failed to look up bootstrap port: " << MachErrorCode(kr);
- return false;
- }
-
mach_port_t parent_port;
- kr = bootstrap_look_up(bootstrap_port,
+ kern_return_t kr = bootstrap_look_up(bootstrap_port,
const_cast<char*>(GetMachPortName().c_str()), &parent_port);
if (kr != KERN_SUCCESS) {
- LOG(ERROR) << "Failed to look up named parent port: " << MachErrorCode(kr);
+ BOOTSTRAP_LOG(ERROR, kr) << "bootstrap_look_up";
return false;
}
+ base::mac::ScopedMachSendRight scoped_right(parent_port);
// Create the check in message. This will copy a send right on this process'
// (the child's) task port and send it to the parent.
@@ -169,13 +161,24 @@ bool MachBroker::ChildSendTaskPortToParent() {
kr = mach_msg(&msg.header, MACH_SEND_MSG | MACH_SEND_TIMEOUT, sizeof(msg),
0, MACH_PORT_NULL, 100 /*milliseconds*/, MACH_PORT_NULL);
if (kr != KERN_SUCCESS) {
- LOG(ERROR) << "Failed to send task port to parent: " << MachErrorCode(kr);
+ MACH_LOG(ERROR, kr) << "mach_msg";
return false;
}
return true;
}
+// static
+std::string MachBroker::GetMachPortName() {
+ const CommandLine* command_line = CommandLine::ForCurrentProcess();
+ const bool is_child = command_line->HasSwitch(switches::kProcessType);
+
+ // In non-browser (child) processes, use the parent's pid.
+ const pid_t pid = is_child ? getppid() : getpid();
+ return base::StringPrintf("%s.rohitfork.%d", base::mac::BaseBundleID(), pid);
+}
+
+// static
MachBroker* MachBroker::GetInstance() {
return Singleton<MachBroker, LeakySingletonTraits<MachBroker> >::get();
}
@@ -279,22 +282,10 @@ void MachBroker::InvalidatePid(base::ProcessHandle pid) {
kern_return_t kr = mach_port_deallocate(mach_task_self(),
it->second);
- LOG_IF(WARNING, kr != KERN_SUCCESS)
- << "Failed to mach_port_deallocate mach task " << it->second
- << ", error " << MachErrorCode(kr);
+ MACH_LOG_IF(WARNING, kr != KERN_SUCCESS, kr) << "mach_port_deallocate";
mach_map_.erase(it);
}
-// static
-std::string MachBroker::GetMachPortName() {
- const CommandLine* command_line = CommandLine::ForCurrentProcess();
- const bool is_child = command_line->HasSwitch(switches::kProcessType);
-
- // In non-browser (child) processes, use the parent's pid.
- const pid_t pid = is_child ? getppid() : getpid();
- return base::StringPrintf("%s.rohitfork.%d", base::mac::BaseBundleID(), pid);
-}
-
void MachBroker::RegisterNotifications() {
registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_CLOSED,
NotificationService::AllBrowserContextsAndSources());
diff --git a/chromium/content/browser/media/OWNERS b/chromium/content/browser/media/OWNERS
index d132d0e6061..b1afdbbadb8 100644
--- a/chromium/content/browser/media/OWNERS
+++ b/chromium/content/browser/media/OWNERS
@@ -1,7 +1,7 @@
acolwell@chromium.org
dalecurtis@chromium.org
ddorwin@chromium.org
-fischman@chromium.org
+perkj@chromium.org
scherkus@chromium.org
shadi@chromium.org
tommi@chromium.org
@@ -9,3 +9,8 @@ vrk@chromium.org
wjia@chromium.org
xhwang@chromium.org
xians@chromium.org
+
+per-file cast_*=hclam@chromium.org
+per-file cast_*=hubbe@chromium.org
+per-file cast_*=mikhal@chromium.org
+per-file cast_*=pwestin@google.com
diff --git a/chromium/content/browser/media/android/browser_demuxer_android.cc b/chromium/content/browser/media/android/browser_demuxer_android.cc
index 8480b14f98c..197895261d7 100644
--- a/chromium/content/browser/media/android/browser_demuxer_android.cc
+++ b/chromium/content/browser/media/android/browser_demuxer_android.cc
@@ -26,11 +26,6 @@ class BrowserDemuxerAndroid::Internal : public media::DemuxerAndroid {
demuxer_->AddDemuxerClient(demuxer_client_id_, client);
}
- virtual void RequestDemuxerConfigs() OVERRIDE {
- DCHECK(ClientIDExists()) << demuxer_client_id_;
- demuxer_->Send(new MediaPlayerMsg_MediaConfigRequest(demuxer_client_id_));
- }
-
virtual void RequestDemuxerData(media::DemuxerStream::Type type) OVERRIDE {
DCHECK(ClientIDExists()) << demuxer_client_id_;
demuxer_->Send(new MediaPlayerMsg_ReadFromDemuxer(
@@ -57,7 +52,8 @@ class BrowserDemuxerAndroid::Internal : public media::DemuxerAndroid {
DISALLOW_COPY_AND_ASSIGN(Internal);
};
-BrowserDemuxerAndroid::BrowserDemuxerAndroid() {}
+BrowserDemuxerAndroid::BrowserDemuxerAndroid()
+ : BrowserMessageFilter(MediaPlayerMsgStart) {}
BrowserDemuxerAndroid::~BrowserDemuxerAndroid() {}
@@ -74,10 +70,9 @@ void BrowserDemuxerAndroid::OverrideThreadForMessage(
}
}
-bool BrowserDemuxerAndroid::OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) {
+bool BrowserDemuxerAndroid::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(BrowserDemuxerAndroid, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(BrowserDemuxerAndroid, message)
IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_DemuxerReady, OnDemuxerReady)
IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_ReadFromDemuxerAck,
OnReadFromDemuxerAck)
diff --git a/chromium/content/browser/media/android/browser_demuxer_android.h b/chromium/content/browser/media/android/browser_demuxer_android.h
index 76018313cb7..4ab7c8d84c2 100644
--- a/chromium/content/browser/media/android/browser_demuxer_android.h
+++ b/chromium/content/browser/media/android/browser_demuxer_android.h
@@ -23,8 +23,7 @@ class CONTENT_EXPORT BrowserDemuxerAndroid : public BrowserMessageFilter {
// BrowserMessageFilter overrides.
virtual void OverrideThreadForMessage(const IPC::Message& message,
BrowserThread::ID* thread) OVERRIDE;
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
// Returns an uninitialized demuxer implementation associated with
// |demuxer_client_id|, which can be used to communicate with the real demuxer
diff --git a/chromium/content/browser/media/android/browser_media_player_manager.cc b/chromium/content/browser/media/android/browser_media_player_manager.cc
index 43b93da7fc3..5df9016f7b7 100644
--- a/chromium/content/browser/media/android/browser_media_player_manager.cc
+++ b/chromium/content/browser/media/android/browser_media_player_manager.cc
@@ -4,6 +4,7 @@
#include "content/browser/media/android/browser_media_player_manager.h"
+#include "base/android/scoped_java_ref.h"
#include "base/command_line.h"
#include "content/browser/android/content_view_core_impl.h"
#include "content/browser/media/android/browser_demuxer_android.h"
@@ -12,37 +13,31 @@
#include "content/browser/web_contents/web_contents_view_android.h"
#include "content/common/media/media_player_messages_android.h"
#include "content/public/browser/android/content_view_core.h"
+#include "content/public/browser/android/external_video_surface_container.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/render_view_host.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"
#include "content/public/common/content_switches.h"
-#include "media/base/android/media_drm_bridge.h"
#include "media/base/android/media_player_bridge.h"
#include "media/base/android/media_source_player.h"
#include "media/base/media_switches.h"
-using media::MediaDrmBridge;
using media::MediaPlayerAndroid;
using media::MediaPlayerBridge;
using media::MediaPlayerManager;
using media::MediaSourcePlayer;
+namespace content {
+
// Threshold on the number of media players per renderer before we start
// attempting to release inactive media players.
-static const int kMediaPlayerThreshold = 1;
-
-// Maximum sizes for various EME message parameters. These are checks to
-// prevent unnecessarily large messages from being passed around, and the sizes
-// are somewhat arbitrary as the EME specification doesn't specify any limits.
-static const size_t kEmeUuidSize = 16;
-static const size_t kEmeTypeMaximum = 50; // Type is a MIME type.
-static const size_t kEmeInitDataMaximum = 10240; // 10 KB
-static const size_t kEmeResponseMaximum = 10240; // 10 KB
-
-namespace content {
+const int kMediaPlayerThreshold = 1;
static BrowserMediaPlayerManager::Factory g_factory = NULL;
@@ -53,27 +48,36 @@ void BrowserMediaPlayerManager::RegisterFactory(Factory factory) {
// static
BrowserMediaPlayerManager* BrowserMediaPlayerManager::Create(
- RenderViewHost* rvh) {
+ RenderFrameHost* rfh) {
if (g_factory)
- return g_factory(rvh);
- return new BrowserMediaPlayerManager(rvh);
+ return g_factory(rfh);
+ return new BrowserMediaPlayerManager(rfh);
+}
+
+ContentViewCoreImpl* BrowserMediaPlayerManager::GetContentViewCore() const {
+ return ContentViewCoreImpl::FromWebContents(web_contents());
}
-#if !defined(GOOGLE_TV)
-// static
MediaPlayerAndroid* BrowserMediaPlayerManager::CreateMediaPlayer(
- MediaPlayerHostMsg_Initialize_Type type,
- int player_id,
- const GURL& url,
- const GURL& first_party_for_cookies,
- int demuxer_client_id,
+ const MediaPlayerHostMsg_Initialize_Params& media_player_params,
bool hide_url_log,
MediaPlayerManager* manager,
BrowserDemuxerAndroid* demuxer) {
- switch (type) {
+ switch (media_player_params.type) {
case MEDIA_PLAYER_TYPE_URL: {
+ const std::string user_agent = GetContentClient()->GetUserAgent();
MediaPlayerBridge* media_player_bridge = new MediaPlayerBridge(
- player_id, url, first_party_for_cookies, hide_url_log, manager);
+ media_player_params.player_id,
+ media_player_params.url,
+ media_player_params.first_party_for_cookies,
+ user_agent,
+ hide_url_log,
+ manager,
+ base::Bind(&BrowserMediaPlayerManager::OnMediaResourcesRequested,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::Bind(&BrowserMediaPlayerManager::OnMediaResourcesReleased,
+ weak_ptr_factory_.GetWeakPtr()),
+ media_player_params.frame_url);
BrowserMediaPlayerManager* browser_media_player_manager =
static_cast<BrowserMediaPlayerManager*>(manager);
ContentViewCoreImpl* content_view_core_impl =
@@ -85,8 +89,9 @@ MediaPlayerAndroid* BrowserMediaPlayerManager::CreateMediaPlayer(
// TODO(qinmin): extract the metadata once the user decided to load
// the page.
browser_media_player_manager->OnMediaMetadataChanged(
- player_id, base::TimeDelta(), 0, 0, false);
- } else if (!content_view_core_impl->ShouldBlockMediaRequest(url)) {
+ media_player_params.player_id, base::TimeDelta(), 0, 0, false);
+ } else if (!content_view_core_impl->ShouldBlockMediaRequest(
+ media_player_params.url)) {
media_player_bridge->Initialize();
}
return media_player_bridge;
@@ -94,53 +99,35 @@ MediaPlayerAndroid* BrowserMediaPlayerManager::CreateMediaPlayer(
case MEDIA_PLAYER_TYPE_MEDIA_SOURCE: {
return new MediaSourcePlayer(
- player_id, manager, demuxer->CreateDemuxer(demuxer_client_id));
+ media_player_params.player_id,
+ manager,
+ base::Bind(&BrowserMediaPlayerManager::OnMediaResourcesRequested,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::Bind(&BrowserMediaPlayerManager::OnMediaResourcesReleased,
+ weak_ptr_factory_.GetWeakPtr()),
+ demuxer->CreateDemuxer(media_player_params.demuxer_client_id),
+ media_player_params.frame_url);
}
}
NOTREACHED();
return NULL;
}
-#endif
BrowserMediaPlayerManager::BrowserMediaPlayerManager(
- RenderViewHost* render_view_host)
- : WebContentsObserver(WebContents::FromRenderViewHost(render_view_host)),
+ RenderFrameHost* render_frame_host)
+ : render_frame_host_(render_frame_host),
fullscreen_player_id_(-1),
- pending_fullscreen_player_id_(-1),
fullscreen_player_is_released_(false),
- web_contents_(WebContents::FromRenderViewHost(render_view_host)),
+ web_contents_(WebContents::FromRenderFrameHost(render_frame_host)),
weak_ptr_factory_(this) {
}
-BrowserMediaPlayerManager::~BrowserMediaPlayerManager() {}
-
-bool BrowserMediaPlayerManager::OnMessageReceived(const IPC::Message& msg) {
- bool handled = true;
- IPC_BEGIN_MESSAGE_MAP(BrowserMediaPlayerManager, msg)
- IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_EnterFullscreen, OnEnterFullscreen)
- IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_ExitFullscreen, OnExitFullscreen)
- IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_Initialize, OnInitialize)
- IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_Start, OnStart)
- IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_Seek, OnSeek)
- IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_Pause, OnPause)
- IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_SetVolume, OnSetVolume)
- IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_Release, OnReleaseResources)
- IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_DestroyMediaPlayer, OnDestroyPlayer)
- IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_DestroyAllMediaPlayers,
- DestroyAllMediaPlayers)
- IPC_MESSAGE_HANDLER(MediaKeysHostMsg_InitializeCDM,
- OnInitializeCDM)
- IPC_MESSAGE_HANDLER(MediaKeysHostMsg_CreateSession, OnCreateSession)
- IPC_MESSAGE_HANDLER(MediaKeysHostMsg_UpdateSession, OnUpdateSession)
- IPC_MESSAGE_HANDLER(MediaKeysHostMsg_ReleaseSession, OnReleaseSession)
-#if defined(VIDEO_HOLE)
- IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_NotifyExternalSurface,
- OnNotifyExternalSurface)
-#endif // defined(VIDEO_HOLE)
- IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP()
- return handled;
+BrowserMediaPlayerManager::~BrowserMediaPlayerManager() {
+ // During the tear down process, OnDestroyPlayer() may or may not be called
+ // (e.g. the WebContents may be destroyed before the render process). So
+ // we cannot DCHECK(players_.empty()) here. Instead, all media players in
+ // |players_| will be destroyed here because |player_| is a ScopedVector.
}
void BrowserMediaPlayerManager::FullscreenPlayerPlay() {
@@ -151,8 +138,8 @@ void BrowserMediaPlayerManager::FullscreenPlayerPlay() {
fullscreen_player_is_released_ = false;
}
player->Start();
- Send(new MediaPlayerMsg_DidMediaPlayerPlay(
- routing_id(), fullscreen_player_id_));
+ Send(new MediaPlayerMsg_DidMediaPlayerPlay(RoutingID(),
+ fullscreen_player_id_));
}
}
@@ -160,8 +147,8 @@ void BrowserMediaPlayerManager::FullscreenPlayerPause() {
MediaPlayerAndroid* player = GetFullscreenPlayer();
if (player) {
player->Pause(true);
- Send(new MediaPlayerMsg_DidMediaPlayerPause(
- routing_id(), fullscreen_player_id_));
+ Send(new MediaPlayerMsg_DidMediaPlayerPause(RoutingID(),
+ fullscreen_player_id_));
}
}
@@ -177,23 +164,34 @@ void BrowserMediaPlayerManager::FullscreenPlayerSeek(int msec) {
}
void BrowserMediaPlayerManager::ExitFullscreen(bool release_media_player) {
- Send(new MediaPlayerMsg_DidExitFullscreen(
- routing_id(), fullscreen_player_id_));
+ if (!CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableOverlayFullscreenVideoSubtitle)) {
+ if (WebContentsDelegate* delegate = web_contents_->GetDelegate())
+ delegate->ToggleFullscreenModeForTab(web_contents_, false);
+ if (RenderWidgetHostViewAndroid* view_android =
+ static_cast<RenderWidgetHostViewAndroid*>(
+ web_contents_->GetRenderWidgetHostView())) {
+ view_android->SetOverlayVideoMode(false);
+ }
+ }
+
+ Send(
+ new MediaPlayerMsg_DidExitFullscreen(RoutingID(), fullscreen_player_id_));
video_view_.reset();
MediaPlayerAndroid* player = GetFullscreenPlayer();
fullscreen_player_id_ = -1;
if (!player)
return;
if (release_media_player)
- player->Release();
+ ReleaseFullscreenPlayer(player);
else
player->SetVideoSurface(gfx::ScopedJavaSurface());
}
void BrowserMediaPlayerManager::OnTimeUpdate(int player_id,
base::TimeDelta current_time) {
- Send(new MediaPlayerMsg_MediaTimeUpdate(
- routing_id(), player_id, current_time));
+ Send(
+ new MediaPlayerMsg_MediaTimeUpdate(RoutingID(), player_id, current_time));
}
void BrowserMediaPlayerManager::SetVideoSurface(
@@ -201,38 +199,51 @@ void BrowserMediaPlayerManager::SetVideoSurface(
MediaPlayerAndroid* player = GetFullscreenPlayer();
if (!player)
return;
- if (!surface.IsEmpty()) {
- Send(new MediaPlayerMsg_DidEnterFullscreen(routing_id(),
- player->player_id()));
- }
+
+ bool empty_surface = surface.IsEmpty();
player->SetVideoSurface(surface.Pass());
+ if (empty_surface)
+ return;
+
+ Send(new MediaPlayerMsg_DidEnterFullscreen(RoutingID(), player->player_id()));
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableOverlayFullscreenVideoSubtitle)) {
+ return;
+ }
+ if (RenderWidgetHostViewAndroid* view_android =
+ static_cast<RenderWidgetHostViewAndroid*>(
+ web_contents_->GetRenderWidgetHostView())) {
+ view_android->SetOverlayVideoMode(true);
+ }
+ if (WebContentsDelegate* delegate = web_contents_->GetDelegate())
+ delegate->ToggleFullscreenModeForTab(web_contents_, true);
}
void BrowserMediaPlayerManager::OnMediaMetadataChanged(
int player_id, base::TimeDelta duration, int width, int height,
bool success) {
Send(new MediaPlayerMsg_MediaMetadataChanged(
- routing_id(), player_id, duration, width, height, success));
+ RoutingID(), player_id, duration, width, height, success));
if (fullscreen_player_id_ == player_id)
video_view_->UpdateMediaMetadata();
}
void BrowserMediaPlayerManager::OnPlaybackComplete(int player_id) {
- Send(new MediaPlayerMsg_MediaPlaybackCompleted(routing_id(), player_id));
+ Send(new MediaPlayerMsg_MediaPlaybackCompleted(RoutingID(), player_id));
if (fullscreen_player_id_ == player_id)
video_view_->OnPlaybackComplete();
}
void BrowserMediaPlayerManager::OnMediaInterrupted(int player_id) {
// Tell WebKit that the audio should be paused, then release all resources
- Send(new MediaPlayerMsg_DidMediaPlayerPause(routing_id(), player_id));
+ Send(new MediaPlayerMsg_MediaPlayerReleased(RoutingID(), player_id));
OnReleaseResources(player_id);
}
void BrowserMediaPlayerManager::OnBufferingUpdate(
int player_id, int percentage) {
Send(new MediaPlayerMsg_MediaBufferingUpdate(
- routing_id(), player_id, percentage));
+ RoutingID(), player_id, percentage));
if (fullscreen_player_id_ == player_id)
video_view_->OnBufferingUpdate(percentage);
}
@@ -240,61 +251,33 @@ void BrowserMediaPlayerManager::OnBufferingUpdate(
void BrowserMediaPlayerManager::OnSeekRequest(
int player_id,
const base::TimeDelta& time_to_seek) {
- Send(new MediaPlayerMsg_SeekRequest(routing_id(), player_id, time_to_seek));
+ Send(new MediaPlayerMsg_SeekRequest(RoutingID(), player_id, time_to_seek));
+}
+
+void BrowserMediaPlayerManager::PauseVideo() {
+ Send(new MediaPlayerMsg_PauseVideo(RoutingID()));
}
void BrowserMediaPlayerManager::OnSeekComplete(
int player_id,
const base::TimeDelta& current_time) {
- Send(new MediaPlayerMsg_SeekCompleted(routing_id(), player_id, current_time));
+ Send(new MediaPlayerMsg_SeekCompleted(RoutingID(), player_id, current_time));
}
void BrowserMediaPlayerManager::OnError(int player_id, int error) {
- Send(new MediaPlayerMsg_MediaError(routing_id(), player_id, error));
+ Send(new MediaPlayerMsg_MediaError(RoutingID(), player_id, error));
if (fullscreen_player_id_ == player_id)
video_view_->OnMediaPlayerError(error);
}
void BrowserMediaPlayerManager::OnVideoSizeChanged(
int player_id, int width, int height) {
- Send(new MediaPlayerMsg_MediaVideoSizeChanged(routing_id(), player_id,
+ Send(new MediaPlayerMsg_MediaVideoSizeChanged(RoutingID(), player_id,
width, height));
if (fullscreen_player_id_ == player_id)
video_view_->OnVideoSizeChanged(width, height);
}
-void BrowserMediaPlayerManager::RequestMediaResources(int player_id) {
- int num_active_player = 0;
- ScopedVector<MediaPlayerAndroid>::iterator it;
- for (it = players_.begin(); it != players_.end(); ++it) {
- if (!(*it)->IsPlayerReady())
- continue;
-
- // The player is already active, ignore it.
- if ((*it)->player_id() == player_id)
- return;
- else
- num_active_player++;
- }
-
- // Number of active players are less than the threshold, do nothing.
- if (num_active_player < kMediaPlayerThreshold)
- return;
-
- for (it = players_.begin(); it != players_.end(); ++it) {
- if ((*it)->IsPlayerReady() && !(*it)->IsPlaying() &&
- fullscreen_player_id_ != (*it)->player_id()) {
- (*it)->Release();
- Send(new MediaPlayerMsg_MediaPlayerReleased(
- routing_id(), (*it)->player_id()));
- }
- }
-}
-
-void BrowserMediaPlayerManager::ReleaseMediaResources(int player_id) {
- // Nothing needs to be done.
-}
-
media::MediaResourceGetter*
BrowserMediaPlayerManager::GetMediaResourceGetter() {
if (!media_resource_getter_.get()) {
@@ -303,8 +286,13 @@ BrowserMediaPlayerManager::GetMediaResourceGetter() {
StoragePartition* partition = host->GetStoragePartition();
fileapi::FileSystemContext* file_system_context =
partition ? partition->GetFileSystemContext() : NULL;
+ // Eventually this needs to be fixed to pass the correct frame rather
+ // than just using the main frame.
media_resource_getter_.reset(new MediaResourceGetterImpl(
- context, file_system_context, host->GetID(), routing_id()));
+ context,
+ file_system_context,
+ host->GetID(),
+ web_contents()->GetMainFrame()->GetRoutingID()));
}
return media_resource_getter_.get();
}
@@ -322,25 +310,7 @@ MediaPlayerAndroid* BrowserMediaPlayerManager::GetPlayer(int player_id) {
return NULL;
}
-MediaDrmBridge* BrowserMediaPlayerManager::GetDrmBridge(int media_keys_id) {
- for (ScopedVector<MediaDrmBridge>::iterator it = drm_bridges_.begin();
- it != drm_bridges_.end(); ++it) {
- if ((*it)->media_keys_id() == media_keys_id)
- return *it;
- }
- return NULL;
-}
-
-void BrowserMediaPlayerManager::DestroyAllMediaPlayers() {
- players_.clear();
- drm_bridges_.clear();
- if (fullscreen_player_id_ != -1) {
- video_view_.reset();
- fullscreen_player_id_ = -1;
- }
-}
-
-void BrowserMediaPlayerManager::OnProtectedSurfaceRequested(int player_id) {
+void BrowserMediaPlayerManager::RequestFullScreen(int player_id) {
if (fullscreen_player_id_ == player_id)
return;
@@ -350,66 +320,16 @@ void BrowserMediaPlayerManager::OnProtectedSurfaceRequested(int player_id) {
return;
}
- // If the player is pending approval, wait for the approval to happen.
- if (media_keys_ids_pending_approval_.end() !=
- media_keys_ids_pending_approval_.find(player_id)) {
- pending_fullscreen_player_id_ = player_id;
- return;
- }
-
- // Send an IPC to the render process to request the video element to enter
- // fullscreen. OnEnterFullscreen() will be called later on success.
- // This guarantees the fullscreen video will be rendered correctly.
- // During the process, DisableFullscreenEncryptedMediaPlayback() may get
- // called before or after OnEnterFullscreen(). If it is called before
- // OnEnterFullscreen(), the player will not enter fullscreen. And it will
- // retry the process once CreateSession() is allowed to proceed.
- // TODO(qinmin): make this flag default on android.
- if (CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kDisableGestureRequirementForMediaFullscreen)) {
- Send(new MediaPlayerMsg_RequestFullscreen(routing_id(), player_id));
- }
-}
-
-// The following 5 functions are EME MediaKeySession events.
-
-void BrowserMediaPlayerManager::OnSessionCreated(
- int media_keys_id,
- uint32 session_id,
- const std::string& web_session_id) {
- Send(new MediaKeysMsg_SessionCreated(
- routing_id(), media_keys_id, session_id, web_session_id));
-}
-
-void BrowserMediaPlayerManager::OnSessionMessage(
- int media_keys_id,
- uint32 session_id,
- const std::vector<uint8>& message,
- const std::string& destination_url) {
- Send(new MediaKeysMsg_SessionMessage(
- routing_id(), media_keys_id, session_id, message, destination_url));
+ Send(new MediaPlayerMsg_RequestFullscreen(RoutingID(), player_id));
}
-void BrowserMediaPlayerManager::OnSessionReady(int media_keys_id,
- uint32 session_id) {
- Send(new MediaKeysMsg_SessionReady(routing_id(), media_keys_id, session_id));
-}
-
-void BrowserMediaPlayerManager::OnSessionClosed(int media_keys_id,
- uint32 session_id) {
- Send(new MediaKeysMsg_SessionClosed(routing_id(), media_keys_id, session_id));
-}
-
-void BrowserMediaPlayerManager::OnSessionError(
- int media_keys_id,
- uint32 session_id,
- media::MediaKeys::KeyError error_code,
- int system_code) {
- Send(new MediaKeysMsg_SessionError(
- routing_id(), media_keys_id, session_id, error_code, system_code));
+#if defined(VIDEO_HOLE)
+bool
+BrowserMediaPlayerManager::ShouldUseVideoOverlayForEmbeddedEncryptedVideo() {
+ RendererPreferences* prefs = web_contents_->GetMutableRendererPrefs();
+ return prefs->use_video_overlay_for_embedded_encrypted_video;
}
-#if defined(VIDEO_HOLE)
void BrowserMediaPlayerManager::AttachExternalVideoSurface(int player_id,
jobject surface) {
MediaPlayerAndroid* player = GetPlayer(player_id);
@@ -425,51 +345,74 @@ void BrowserMediaPlayerManager::DetachExternalVideoSurface(int player_id) {
player->SetVideoSurface(gfx::ScopedJavaSurface());
}
+void BrowserMediaPlayerManager::OnFrameInfoUpdated() {
+ if (external_video_surface_container_)
+ external_video_surface_container_->OnFrameInfoUpdated();
+}
+
void BrowserMediaPlayerManager::OnNotifyExternalSurface(
int player_id, bool is_request, const gfx::RectF& rect) {
if (!web_contents_)
return;
- WebContentsViewAndroid* view =
- static_cast<WebContentsViewAndroid*>(web_contents_->GetView());
- if (view)
- view->NotifyExternalSurface(player_id, is_request, rect);
+ if (is_request) {
+ OnRequestExternalSurface(player_id, rect);
+ }
+ if (external_video_surface_container_) {
+ external_video_surface_container_->OnExternalVideoSurfacePositionChanged(
+ player_id, rect);
+ }
}
-#endif // defined(VIDEO_HOLE)
-void BrowserMediaPlayerManager::DisableFullscreenEncryptedMediaPlayback() {
- if (fullscreen_player_id_ == -1)
- return;
-
- // If the fullscreen player is not playing back encrypted video, do nothing.
- MediaDrmBridge* drm_bridge = GetDrmBridge(fullscreen_player_id_);
- if (!drm_bridge)
- return;
-
- // Exit fullscreen.
- pending_fullscreen_player_id_ = fullscreen_player_id_;
- OnExitFullscreen(fullscreen_player_id_);
+void BrowserMediaPlayerManager::OnRequestExternalSurface(
+ int player_id, const gfx::RectF& rect) {
+ if (!external_video_surface_container_) {
+ ContentBrowserClient* client = GetContentClient()->browser();
+ external_video_surface_container_.reset(
+ client->OverrideCreateExternalVideoSurfaceContainer(web_contents_));
+ }
+ // It's safe to use base::Unretained(this), because the callbacks will not
+ // be called after running ReleaseExternalVideoSurface().
+ if (external_video_surface_container_) {
+ external_video_surface_container_->RequestExternalVideoSurface(
+ player_id,
+ base::Bind(&BrowserMediaPlayerManager::AttachExternalVideoSurface,
+ base::Unretained(this)),
+ base::Bind(&BrowserMediaPlayerManager::DetachExternalVideoSurface,
+ base::Unretained(this)));
+ }
}
+#endif // defined(VIDEO_HOLE)
void BrowserMediaPlayerManager::OnEnterFullscreen(int player_id) {
DCHECK_EQ(fullscreen_player_id_, -1);
- if (media_keys_ids_pending_approval_.find(player_id) !=
- media_keys_ids_pending_approval_.end()) {
- return;
- }
-
+#if defined(VIDEO_HOLE)
+ if (external_video_surface_container_)
+ external_video_surface_container_->ReleaseExternalVideoSurface(player_id);
+#endif // defined(VIDEO_HOLE)
if (video_view_.get()) {
fullscreen_player_id_ = player_id;
video_view_->OpenVideo();
- } else if (!ContentVideoView::HasContentVideoView()) {
+ return;
+ } else if (!ContentVideoView::GetInstance()) {
// In Android WebView, two ContentViewCores could both try to enter
// fullscreen video, we just ignore the second one.
- fullscreen_player_id_ = player_id;
- ContentViewCoreImpl* content_view_core_impl =
- ContentViewCoreImpl::FromWebContents(web_contents());
- video_view_.reset(new ContentVideoView(content_view_core_impl->GetContext(),
- content_view_core_impl->GetContentVideoViewClient(), this));
+ video_view_.reset(new ContentVideoView(this));
+ base::android::ScopedJavaLocalRef<jobject> j_content_video_view =
+ video_view_->GetJavaObject(base::android::AttachCurrentThread());
+ if (!j_content_video_view.is_null()) {
+ fullscreen_player_id_ = player_id;
+ return;
+ }
}
+
+ // Force the second video to exit fullscreen.
+ // TODO(qinmin): There is no need to send DidEnterFullscreen message.
+ // However, if we don't send the message, page layers will not be
+ // correctly restored. http:crbug.com/367346.
+ Send(new MediaPlayerMsg_DidEnterFullscreen(RoutingID(), player_id));
+ Send(new MediaPlayerMsg_DidExitFullscreen(RoutingID(), player_id));
+ video_view_.reset();
}
void BrowserMediaPlayerManager::OnExitFullscreen(int player_id) {
@@ -482,29 +425,37 @@ void BrowserMediaPlayerManager::OnExitFullscreen(int player_id) {
}
void BrowserMediaPlayerManager::OnInitialize(
- MediaPlayerHostMsg_Initialize_Type type,
- int player_id,
- const GURL& url,
- const GURL& first_party_for_cookies,
- int demuxer_client_id) {
- DCHECK(type != MEDIA_PLAYER_TYPE_MEDIA_SOURCE || demuxer_client_id > 0)
+ const MediaPlayerHostMsg_Initialize_Params& media_player_params) {
+ DCHECK(media_player_params.type != MEDIA_PLAYER_TYPE_MEDIA_SOURCE ||
+ media_player_params.demuxer_client_id > 0)
<< "Media source players must have positive demuxer client IDs: "
- << demuxer_client_id;
+ << media_player_params.demuxer_client_id;
- RemovePlayer(player_id);
+ RemovePlayer(media_player_params.player_id);
RenderProcessHostImpl* host = static_cast<RenderProcessHostImpl*>(
web_contents()->GetRenderProcessHost());
- AddPlayer(CreateMediaPlayer(
- type, player_id, url, first_party_for_cookies, demuxer_client_id,
+ MediaPlayerAndroid* player = CreateMediaPlayer(
+ media_player_params,
+
host->GetBrowserContext()->IsOffTheRecord(), this,
- host->browser_demuxer_android()));
+ host->browser_demuxer_android());
+
+ if (!player)
+ return;
+
+ AddPlayer(player);
}
void BrowserMediaPlayerManager::OnStart(int player_id) {
MediaPlayerAndroid* player = GetPlayer(player_id);
- if (player)
- player->Start();
+ if (!player)
+ return;
+ player->Start();
+ if (fullscreen_player_id_ == player_id && fullscreen_player_is_released_) {
+ video_view_->OpenVideo();
+ fullscreen_player_is_released_ = false;
+ }
}
void BrowserMediaPlayerManager::OnSeek(
@@ -529,19 +480,16 @@ void BrowserMediaPlayerManager::OnSetVolume(int player_id, double volume) {
player->SetVolume(volume);
}
+void BrowserMediaPlayerManager::OnSetPoster(int player_id, const GURL& url) {
+ // To be overridden by subclasses.
+}
+
void BrowserMediaPlayerManager::OnReleaseResources(int player_id) {
MediaPlayerAndroid* player = GetPlayer(player_id);
if (player)
player->Release();
if (player_id == fullscreen_player_id_)
fullscreen_player_is_released_ = true;
-
-#if defined(VIDEO_HOLE)
- WebContentsViewAndroid* view =
- static_cast<WebContentsViewAndroid*>(web_contents_->GetView());
- if (view)
- view->NotifyExternalSurface(player_id, false, gfx::RectF());
-#endif // defined(VIDEO_HOLE)
}
void BrowserMediaPlayerManager::OnDestroyPlayer(int player_id) {
@@ -550,117 +498,9 @@ void BrowserMediaPlayerManager::OnDestroyPlayer(int player_id) {
fullscreen_player_id_ = -1;
}
-void BrowserMediaPlayerManager::OnInitializeCDM(
- int media_keys_id,
- const std::vector<uint8>& uuid,
- const GURL& frame_url) {
- if (uuid.size() != kEmeUuidSize) {
- // This failure will be discovered and reported by OnCreateSession()
- // as GetDrmBridge() will return null.
- NOTREACHED() << "Invalid UUID for ID: " << media_keys_id;
- return;
- }
-
- AddDrmBridge(media_keys_id, uuid, frame_url);
- // In EME v0.1b MediaKeys lives in the media element. So the |media_keys_id|
- // is the same as the |player_id|.
- OnSetMediaKeys(media_keys_id, media_keys_id);
-}
-
-void BrowserMediaPlayerManager::OnCreateSession(
- int media_keys_id,
- uint32 session_id,
- const std::string& type,
- const std::vector<uint8>& init_data) {
- if (type.length() > kEmeTypeMaximum) {
- OnSessionError(
- media_keys_id, session_id, media::MediaKeys::kUnknownError, 0);
- return;
- }
- if (init_data.size() > kEmeInitDataMaximum) {
- OnSessionError(
- media_keys_id, session_id, media::MediaKeys::kUnknownError, 0);
- return;
- }
-
- if (CommandLine::ForCurrentProcess()
- ->HasSwitch(switches::kDisableInfobarForProtectedMediaIdentifier)) {
- GenerateKeyIfAllowed(media_keys_id, session_id, type, init_data, true);
- return;
- }
-
- MediaDrmBridge* drm_bridge = GetDrmBridge(media_keys_id);
- if (!drm_bridge) {
- DLOG(WARNING) << "No MediaDrmBridge for ID: " << media_keys_id << " found";
- OnSessionError(
- media_keys_id, session_id, media::MediaKeys::kUnknownError, 0);
- return;
- }
-
- if (media_keys_ids_approved_.find(media_keys_id) ==
- media_keys_ids_approved_.end()) {
- media_keys_ids_pending_approval_.insert(media_keys_id);
- }
- web_contents()->GetDelegate()->RequestProtectedMediaIdentifierPermission(
- web_contents(),
- drm_bridge->frame_url(),
- base::Bind(&BrowserMediaPlayerManager::GenerateKeyIfAllowed,
- weak_ptr_factory_.GetWeakPtr(),
- media_keys_id,
- session_id,
- type,
- init_data));
-}
-
-void BrowserMediaPlayerManager::OnUpdateSession(
- int media_keys_id,
- uint32 session_id,
- const std::vector<uint8>& response) {
- MediaDrmBridge* drm_bridge = GetDrmBridge(media_keys_id);
- if (!drm_bridge) {
- DLOG(WARNING) << "No MediaDrmBridge for ID: " << media_keys_id << " found";
- OnSessionError(
- media_keys_id, session_id, media::MediaKeys::kUnknownError, 0);
- return;
- }
-
- if (response.size() > kEmeResponseMaximum) {
- DLOG(WARNING) << "Response for ID: " << media_keys_id
- << " too long: " << response.size();
- OnSessionError(
- media_keys_id, session_id, media::MediaKeys::kUnknownError, 0);
- return;
- }
-
- drm_bridge->UpdateSession(session_id, &response[0], response.size());
- // In EME v0.1b MediaKeys lives in the media element. So the |media_keys_id|
- // is the same as the |player_id|.
- // TODO(xhwang): Separate |media_keys_id| and |player_id|.
- MediaPlayerAndroid* player = GetPlayer(media_keys_id);
- if (player)
- player->OnKeyAdded();
-}
-
-void BrowserMediaPlayerManager::OnReleaseSession(int media_keys_id,
- uint32 session_id) {
- MediaDrmBridge* drm_bridge = GetDrmBridge(media_keys_id);
- if (!drm_bridge) {
- DLOG(WARNING) << "No MediaDrmBridge for ID: " << media_keys_id << " found";
- OnSessionError(
- media_keys_id, session_id, media::MediaKeys::kUnknownError, 0);
- return;
- }
-
- drm_bridge->ReleaseSession(session_id);
-}
-
void BrowserMediaPlayerManager::AddPlayer(MediaPlayerAndroid* player) {
DCHECK(!GetPlayer(player->player_id()));
players_.push_back(player);
- if (player->IsRemote()) {
- Send(new MediaPlayerMsg_ConnectedToRemoteDevice(routing_id(),
- player->player_id()));
- }
}
void BrowserMediaPlayerManager::RemovePlayer(int player_id) {
@@ -668,10 +508,6 @@ void BrowserMediaPlayerManager::RemovePlayer(int player_id) {
it != players_.end(); ++it) {
MediaPlayerAndroid* player = *it;
if (player->player_id() == player_id) {
- if (player->IsRemote()) {
- Send(new MediaPlayerMsg_DisconnectedFromRemoteDevice(
- routing_id(), player->player_id()));
- }
players_.erase(it);
break;
}
@@ -687,94 +523,61 @@ scoped_ptr<media::MediaPlayerAndroid> BrowserMediaPlayerManager::SwapPlayer(
previous_player = *it;
players_.weak_erase(it);
players_.push_back(player);
- if (!previous_player->IsRemote() && player->IsRemote()) {
- Send(new MediaPlayerMsg_ConnectedToRemoteDevice(
- routing_id(), player->player_id()));
- } else if (previous_player->IsRemote() && !player->IsRemote()) {
- Send(new MediaPlayerMsg_DisconnectedFromRemoteDevice(
- routing_id(), player->player_id()));
- }
break;
}
}
return scoped_ptr<media::MediaPlayerAndroid>(previous_player);
}
-void BrowserMediaPlayerManager::AddDrmBridge(int media_keys_id,
- const std::vector<uint8>& uuid,
- const GURL& frame_url) {
- DCHECK(!GetDrmBridge(media_keys_id));
- // TODO(xhwang/ddorwin): Pass the security level from key system.
- std::string security_level = "L3";
- if (CommandLine::ForCurrentProcess()
- ->HasSwitch(switches::kMediaDrmEnableNonCompositing)) {
- security_level = "L1";
- }
-
- scoped_ptr<MediaDrmBridge> drm_bridge(MediaDrmBridge::Create(
- media_keys_id, uuid, frame_url, security_level, this));
- if (!drm_bridge) {
- // This failure will be discovered and reported by OnCreateSession()
- // as GetDrmBridge() will return null.
- DVLOG(1) << "failed to create drm bridge.";
- return;
- }
+int BrowserMediaPlayerManager::RoutingID() {
+ return render_frame_host_->GetRoutingID();
+}
- drm_bridges_.push_back(drm_bridge.release());
+bool BrowserMediaPlayerManager::Send(IPC::Message* msg) {
+ return render_frame_host_->Send(msg);
}
-void BrowserMediaPlayerManager::RemoveDrmBridge(int media_keys_id) {
- for (ScopedVector<MediaDrmBridge>::iterator it = drm_bridges_.begin();
- it != drm_bridges_.end(); ++it) {
- if ((*it)->media_keys_id() == media_keys_id) {
- drm_bridges_.erase(it);
- break;
- }
- }
+void BrowserMediaPlayerManager::ReleaseFullscreenPlayer(
+ MediaPlayerAndroid* player) {
+ player->Release();
}
-void BrowserMediaPlayerManager::OnSetMediaKeys(int player_id,
- int media_keys_id) {
- MediaPlayerAndroid* player = GetPlayer(player_id);
- MediaDrmBridge* drm_bridge = GetDrmBridge(media_keys_id);
- if (!player || !drm_bridge) {
- DVLOG(1) << "OnSetMediaKeys(): Player and MediaKeys must be present.";
- return;
+void BrowserMediaPlayerManager::OnMediaResourcesRequested(int player_id) {
+ int num_active_player = 0;
+ ScopedVector<MediaPlayerAndroid>::iterator it;
+ for (it = players_.begin(); it != players_.end(); ++it) {
+ if (!(*it)->IsPlayerReady())
+ continue;
+
+ // The player is already active, ignore it.
+ if ((*it)->player_id() == player_id)
+ return;
+ else
+ num_active_player++;
}
- // TODO(qinmin): add the logic to decide whether we should create the
- // fullscreen surface for EME lv1.
- player->SetDrmBridge(drm_bridge);
-}
-
-void BrowserMediaPlayerManager::GenerateKeyIfAllowed(
- int media_keys_id,
- uint32 session_id,
- const std::string& type,
- const std::vector<uint8>& init_data,
- bool allowed) {
- if (!allowed)
- return;
- MediaDrmBridge* drm_bridge = GetDrmBridge(media_keys_id);
- if (!drm_bridge) {
- DLOG(WARNING) << "No MediaDrmBridge for ID: " << media_keys_id << " found";
- OnSessionError(
- media_keys_id, session_id, media::MediaKeys::kUnknownError, 0);
+ // Number of active players are less than the threshold, do nothing.
+ if (num_active_player < kMediaPlayerThreshold)
return;
+
+ for (it = players_.begin(); it != players_.end(); ++it) {
+ if ((*it)->IsPlayerReady() && !(*it)->IsPlaying() &&
+ fullscreen_player_id_ != (*it)->player_id()) {
+ (*it)->Release();
+ Send(new MediaPlayerMsg_MediaPlayerReleased(RoutingID(),
+ (*it)->player_id()));
+ }
}
- media_keys_ids_pending_approval_.erase(media_keys_id);
- media_keys_ids_approved_.insert(media_keys_id);
- drm_bridge->CreateSession(session_id, type, &init_data[0], init_data.size());
+}
- // TODO(qinmin): currently |media_keys_id| and player ID are identical.
- // This might not be true in the future.
- if (pending_fullscreen_player_id_ != media_keys_id)
+void BrowserMediaPlayerManager::OnMediaResourcesReleased(int player_id) {
+#if defined(VIDEO_HOLE)
+ MediaPlayerAndroid* player = GetPlayer(player_id);
+ if (player && player->IsSurfaceInUse())
return;
-
- pending_fullscreen_player_id_ = -1;
- MediaPlayerAndroid* player = GetPlayer(media_keys_id);
- if (player->IsPlaying())
- OnProtectedSurfaceRequested(media_keys_id);
+ if (external_video_surface_container_)
+ external_video_surface_container_->ReleaseExternalVideoSurface(player_id);
+#endif // defined(VIDEO_HOLE)
}
} // namespace content
diff --git a/chromium/content/browser/media/android/browser_media_player_manager.h b/chromium/content/browser/media/android/browser_media_player_manager.h
index d407e7e56a8..4c533885bf6 100644
--- a/chromium/content/browser/media/android/browser_media_player_manager.h
+++ b/chromium/content/browser/media/android/browser_media_player_manager.h
@@ -5,19 +5,15 @@
#ifndef CONTENT_BROWSER_MEDIA_ANDROID_BROWSER_MEDIA_PLAYER_MANAGER_H_
#define CONTENT_BROWSER_MEDIA_ANDROID_BROWSER_MEDIA_PLAYER_MANAGER_H_
-#include <map>
-#include <set>
-#include <string>
-#include <vector>
-
#include "base/basictypes.h"
#include "base/callback.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "base/time/time.h"
#include "content/browser/android/content_video_view.h"
+#include "content/common/content_export.h"
#include "content/common/media/media_player_messages_enums_android.h"
-#include "content/public/browser/web_contents_observer.h"
+#include "ipc/ipc_message.h"
#include "media/base/android/media_player_android.h"
#include "media/base/android/media_player_manager.h"
#include "ui/gfx/rect_f.h"
@@ -25,33 +21,35 @@
namespace media {
class DemuxerAndroid;
-class MediaDrmBridge;
}
+struct MediaPlayerHostMsg_Initialize_Params;
+
namespace content {
class BrowserDemuxerAndroid;
+class ContentViewCoreImpl;
+class ExternalVideoSurfaceContainer;
+class RenderFrameHost;
class WebContents;
-// This class manages all the MediaPlayerAndroid objects. It receives
-// control operations from the the render process, and forwards
+// This class manages all the MediaPlayerAndroid objects.
+// It receives control operations from the the render process, and forwards
// them to corresponding MediaPlayerAndroid object. Callbacks from
-// MediaPlayerAndroid objects are converted to IPCs and then sent to the
-// render process.
+// MediaPlayerAndroid objects are converted to IPCs and then sent to the render
+// process.
class CONTENT_EXPORT BrowserMediaPlayerManager
- : public WebContentsObserver,
- public media::MediaPlayerManager {
+ : public media::MediaPlayerManager {
public:
// Permits embedders to provide an extended version of the class.
- typedef BrowserMediaPlayerManager* (*Factory)(RenderViewHost*);
+ typedef BrowserMediaPlayerManager* (*Factory)(RenderFrameHost*);
static void RegisterFactory(Factory factory);
// Returns a new instance using the registered factory if available.
- static BrowserMediaPlayerManager* Create(RenderViewHost* rvh);
+ static BrowserMediaPlayerManager* Create(RenderFrameHost* rfh);
- virtual ~BrowserMediaPlayerManager();
+ ContentViewCoreImpl* GetContentViewCore() const;
- // WebContentsObserver overrides.
- virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+ virtual ~BrowserMediaPlayerManager();
// Fullscreen video playback controls.
virtual void FullscreenPlayerPlay();
@@ -64,6 +62,9 @@ class CONTENT_EXPORT BrowserMediaPlayerManager
// Any actual seek started by renderer will be handled by browser in OnSeek().
void OnSeekRequest(int player_id, const base::TimeDelta& time_to_seek);
+ // Pauses all video players manages by this class.
+ void PauseVideo();
+
// media::MediaPlayerManager overrides.
virtual void OnTimeUpdate(
int player_id, base::TimeDelta current_time) OVERRIDE;
@@ -82,74 +83,42 @@ class CONTENT_EXPORT BrowserMediaPlayerManager
virtual void OnError(int player_id, int error) OVERRIDE;
virtual void OnVideoSizeChanged(
int player_id, int width, int height) OVERRIDE;
- virtual void RequestMediaResources(int player_id) OVERRIDE;
- virtual void ReleaseMediaResources(int player_id) OVERRIDE;
virtual media::MediaResourceGetter* GetMediaResourceGetter() OVERRIDE;
virtual media::MediaPlayerAndroid* GetFullscreenPlayer() OVERRIDE;
virtual media::MediaPlayerAndroid* GetPlayer(int player_id) OVERRIDE;
- virtual media::MediaDrmBridge* GetDrmBridge(int media_keys_id) OVERRIDE;
- virtual void DestroyAllMediaPlayers() OVERRIDE;
- virtual void OnProtectedSurfaceRequested(int player_id) OVERRIDE;
- virtual void OnSessionCreated(int media_keys_id,
- uint32 session_id,
- const std::string& web_session_id) OVERRIDE;
- virtual void OnSessionMessage(int media_keys_id,
- uint32 session_id,
- const std::vector<uint8>& message,
- const std::string& destination_url) OVERRIDE;
- virtual void OnSessionReady(int media_keys_id, uint32 session_id) OVERRIDE;
- virtual void OnSessionClosed(int media_keys_id, uint32 session_id) OVERRIDE;
- virtual void OnSessionError(int media_keys_id,
- uint32 session_id,
- media::MediaKeys::KeyError error_code,
- int system_code) OVERRIDE;
-
+ virtual void RequestFullScreen(int player_id) OVERRIDE;
#if defined(VIDEO_HOLE)
+ virtual bool ShouldUseVideoOverlayForEmbeddedEncryptedVideo() OVERRIDE;
+
void AttachExternalVideoSurface(int player_id, jobject surface);
void DetachExternalVideoSurface(int player_id);
+ void OnFrameInfoUpdated();
#endif // defined(VIDEO_HOLE)
- // Called to disble the current fullscreen playback if the video is encrypted.
- // TODO(qinmin): remove this once we have the new fullscreen mode.
- void DisableFullscreenEncryptedMediaPlayback();
-
- protected:
- // Clients must use Create() or subclass constructor.
- explicit BrowserMediaPlayerManager(RenderViewHost* render_view_host);
-
// Message handlers.
virtual void OnEnterFullscreen(int player_id);
virtual void OnExitFullscreen(int player_id);
virtual void OnInitialize(
- MediaPlayerHostMsg_Initialize_Type type,
- int player_id,
- const GURL& url,
- const GURL& first_party_for_cookies,
- int demuxer_client_id);
+ const MediaPlayerHostMsg_Initialize_Params& media_player_params);
virtual void OnStart(int player_id);
virtual void OnSeek(int player_id, const base::TimeDelta& time);
virtual void OnPause(int player_id, bool is_media_related_action);
virtual void OnSetVolume(int player_id, double volume);
+ virtual void OnSetPoster(int player_id, const GURL& poster);
virtual void OnReleaseResources(int player_id);
virtual void OnDestroyPlayer(int player_id);
- void OnInitializeCDM(int media_keys_id,
- const std::vector<uint8>& uuid,
- const GURL& frame_url);
- void OnCreateSession(int media_keys_id,
- uint32 session_id,
- const std::string& type,
- const std::vector<uint8>& init_data);
- void OnUpdateSession(int media_keys_id,
- uint32 session_id,
- const std::vector<uint8>& response);
- void OnReleaseSession(int media_keys_id, uint32 session_id);
- void OnSetMediaKeys(int player_id, int media_keys_id);
-
+ virtual void ReleaseFullscreenPlayer(media::MediaPlayerAndroid* player);
#if defined(VIDEO_HOLE)
- virtual void OnNotifyExternalSurface(
+ void OnNotifyExternalSurface(
int player_id, bool is_request, const gfx::RectF& rect);
#endif // defined(VIDEO_HOLE)
+ protected:
+ // Clients must use Create() or subclass constructor.
+ explicit BrowserMediaPlayerManager(RenderFrameHost* render_frame_host);
+
+ WebContents* web_contents() const { return web_contents_; }
+
// Adds a given player to the list.
void AddPlayer(media::MediaPlayerAndroid* player);
@@ -163,70 +132,59 @@ class CONTENT_EXPORT BrowserMediaPlayerManager
int player_id,
media::MediaPlayerAndroid* player);
- // Adds a new MediaDrmBridge for the given |uuid|, |media_keys_id|, and
- // |frame_url|.
- void AddDrmBridge(int media_keys_id,
- const std::vector<uint8>& uuid,
- const GURL& frame_url);
+ int RoutingID();
- // Removes the DRM bridge with the specified id.
- void RemoveDrmBridge(int media_keys_id);
+ // Helper function to send messages to RenderFrameObserver.
+ bool Send(IPC::Message* msg);
private:
- void GenerateKeyIfAllowed(int media_keys_id,
- uint32 session_id,
- const std::string& type,
- const std::vector<uint8>& init_data,
- bool allowed);
-
- // Constructs a MediaPlayerAndroid object. Declared static to permit embedders
- // to override functionality.
- //
- // Objects must call |manager->RequestMediaResources()| before decoding
- // and |manager->ReleaseMediaSources()| after finishing. This allows the
- // manager to track decoding resources across the process and free them as
- // needed.
- static media::MediaPlayerAndroid* CreateMediaPlayer(
- MediaPlayerHostMsg_Initialize_Type type,
- int player_id,
- const GURL& url,
- const GURL& first_party_for_cookies,
- int demuxer_client_id,
+ // Constructs a MediaPlayerAndroid object.
+ media::MediaPlayerAndroid* CreateMediaPlayer(
+ const MediaPlayerHostMsg_Initialize_Params& media_player_params,
bool hide_url_log,
media::MediaPlayerManager* manager,
BrowserDemuxerAndroid* demuxer);
- // An array of managed players.
- ScopedVector<media::MediaPlayerAndroid> players_;
+ // MediaPlayerAndroid must call this before it is going to decode
+ // media streams. This helps the manager object maintain an array
+ // of active MediaPlayerAndroid objects and release the resources
+ // when needed. Currently we only count video resources as they are
+ // constrained by hardware and memory limits.
+ virtual void OnMediaResourcesRequested(int player_id);
+
+ // Similar to the above call, MediaPlayerAndroid must call this method when
+ // releasing all the decoding resources.
+ virtual void OnMediaResourcesReleased(int player_id);
+
+#if defined(VIDEO_HOLE)
+ void OnRequestExternalSurface(int player_id, const gfx::RectF& rect);
+#endif // defined(VIDEO_HOLE)
- // An array of managed media DRM bridges.
- ScopedVector<media::MediaDrmBridge> drm_bridges_;
+ RenderFrameHost* const render_frame_host_;
- // a set of media keys IDs that are pending approval or approved to access
- // device DRM credentials.
- // These 2 sets does not cover all the EME videos. If a video only streams
- // clear data, it will not be included in either set.
- std::set<int> media_keys_ids_pending_approval_;
- std::set<int> media_keys_ids_approved_;
+ // An array of managed players.
+ ScopedVector<media::MediaPlayerAndroid> players_;
// The fullscreen video view object or NULL if video is not played in
// fullscreen.
scoped_ptr<ContentVideoView> video_view_;
+#if defined(VIDEO_HOLE)
+ scoped_ptr<ExternalVideoSurfaceContainer> external_video_surface_container_;
+#endif
+
// Player ID of the fullscreen media player.
int fullscreen_player_id_;
- // The player ID pending to enter fullscreen.
- int pending_fullscreen_player_id_;
-
// Whether the fullscreen player has been Release()-d.
bool fullscreen_player_is_released_;
- WebContents* web_contents_;
+ WebContents* const web_contents_;
// Object for retrieving resources media players.
scoped_ptr<media::MediaResourceGetter> media_resource_getter_;
+ // NOTE: Weak pointers must be invalidated before all other member variables.
base::WeakPtrFactory<BrowserMediaPlayerManager> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(BrowserMediaPlayerManager);
diff --git a/chromium/content/browser/media/android/media_drm_credential_manager.cc b/chromium/content/browser/media/android/media_drm_credential_manager.cc
index 8151256dad6..23c6cf2cecd 100644
--- a/chromium/content/browser/media/android/media_drm_credential_manager.cc
+++ b/chromium/content/browser/media/android/media_drm_credential_manager.cc
@@ -8,10 +8,14 @@
#include "base/android/scoped_java_ref.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
+#include "base/location.h"
+#include "base/message_loop/message_loop_proxy.h"
#include "jni/MediaDrmCredentialManager_jni.h"
#include "media/base/android/media_drm_bridge.h"
#include "url/gurl.h"
+#include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR.
+
using base::android::ScopedJavaGlobalRef;
namespace {
@@ -28,11 +32,6 @@ void MediaDrmCredentialManagerCallback(
namespace content {
-// TODO(qinmin): Move the UUID definition to some common places.
-static const uint8 kWidevineUuid[16] = {
- 0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE,
- 0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED };
-
MediaDrmCredentialManager::MediaDrmCredentialManager() {};
MediaDrmCredentialManager::~MediaDrmCredentialManager() {};
@@ -43,15 +42,15 @@ MediaDrmCredentialManager* MediaDrmCredentialManager::GetInstance() {
}
void MediaDrmCredentialManager::ResetCredentials(
- const ResetCredentialsCB& callback) {
+ const ResetCredentialsCB& reset_credentials_cb) {
// Ignore reset request if one is already in progress.
if (!reset_credentials_cb_.is_null())
return;
- reset_credentials_cb_ = callback;
+ reset_credentials_cb_ = reset_credentials_cb;
// First reset the L3 credential.
- if (!ResetCredentialsInternal("L3")) {
+ if (!ResetCredentialsInternal(media::MediaDrmBridge::SECURITY_LEVEL_3)) {
// TODO(qinmin): We should post a task instead.
base::ResetAndReturn(&reset_credentials_cb_).Run(false);
}
@@ -77,9 +76,9 @@ void ResetCredentials(
}
void MediaDrmCredentialManager::OnResetCredentialsCompleted(
- const std::string& security_level, bool success) {
- if (security_level == "L3" && success) {
- if (ResetCredentialsInternal("L1"))
+ SecurityLevel security_level, bool success) {
+ if (security_level == media::MediaDrmBridge::SECURITY_LEVEL_3 && success) {
+ if (ResetCredentialsInternal(media::MediaDrmBridge::SECURITY_LEVEL_1))
return;
success = false;
}
@@ -89,15 +88,24 @@ void MediaDrmCredentialManager::OnResetCredentialsCompleted(
}
bool MediaDrmCredentialManager::ResetCredentialsInternal(
- const std::string& security_level) {
- std::vector<uint8> uuid(kWidevineUuid, kWidevineUuid + 16);
- media_drm_bridge_ = media::MediaDrmBridge::Create(
- 0, uuid, GURL(), security_level, NULL);
+ SecurityLevel security_level) {
+ media_drm_bridge_ =
+ media::MediaDrmBridge::CreateSessionless(kWidevineKeySystem);
if (!media_drm_bridge_)
return false;
- media_drm_bridge_->ResetDeviceCredentials(
+
+ ResetCredentialsCB reset_credentials_cb =
base::Bind(&MediaDrmCredentialManager::OnResetCredentialsCompleted,
- base::Unretained(this), security_level));
+ base::Unretained(this), security_level);
+
+ if (!media_drm_bridge_->SetSecurityLevel(security_level)) {
+ // No need to reset credentials for unsupported |security_level|.
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE, base::Bind(reset_credentials_cb, true));
+ return true;
+ }
+
+ media_drm_bridge_->ResetDeviceCredentials(reset_credentials_cb);
return true;
}
diff --git a/chromium/content/browser/media/android/media_drm_credential_manager.h b/chromium/content/browser/media/android/media_drm_credential_manager.h
index 6b6845e2b80..f2cb0fa1fe7 100644
--- a/chromium/content/browser/media/android/media_drm_credential_manager.h
+++ b/chromium/content/browser/media/android/media_drm_credential_manager.h
@@ -24,27 +24,29 @@ class MediaDrmCredentialManager {
// Called to reset the DRM credentials. (for Java)
static void ResetCredentials(JNIEnv* env, jclass clazz, jobject callback);
- // Called to reset the DRM credentials.
- void ResetCredentials(const ResetCredentialsCB& callback);
+ // Called to reset the DRM credentials. The result is returned in the
+ // |reset_credentials_cb|.
+ void ResetCredentials(const ResetCredentialsCB& reset_credentials_cb);
static bool RegisterMediaDrmCredentialManager(JNIEnv* env);
private:
friend struct DefaultSingletonTraits<MediaDrmCredentialManager>;
friend class Singleton<MediaDrmCredentialManager>;
+ typedef media::MediaDrmBridge::SecurityLevel SecurityLevel;
MediaDrmCredentialManager();
~MediaDrmCredentialManager();
+ // Callback function passed to MediaDrmBridge. It is called when credentials
+ // reset is completed.
+ void OnResetCredentialsCompleted(SecurityLevel security_level, bool success);
- // Callback function passed to MediaDrmBridge. It is called when reset
- // completed.
- void OnResetCredentialsCompleted(const std::string& security_level,
- bool success);
-
- // Reset DRM credentials for a particular security level. Returns false if
- // we fail to create the MediaDrmBridge, or true otherwise.
- bool ResetCredentialsInternal(const std::string& security_level);
+ // Resets DRM credentials for a particular |security_level|. Returns false if
+ // we fail to create the MediaDrmBridge at all, in which case we cannot reset
+ // the credentials. Otherwise, the result is returned asynchronously in
+ // OnResetCredentialsCompleted() function.
+ bool ResetCredentialsInternal(SecurityLevel security_level);
// The MediaDrmBridge object used to perform the credential reset.
scoped_ptr<media::MediaDrmBridge> media_drm_bridge_;
diff --git a/chromium/content/browser/media/android/media_resource_getter_impl.cc b/chromium/content/browser/media/android/media_resource_getter_impl.cc
index 53a5094dce4..bcf293d9252 100644
--- a/chromium/content/browser/media/android/media_resource_getter_impl.cc
+++ b/chromium/content/browser/media/android/media_resource_getter_impl.cc
@@ -11,16 +11,23 @@
#include "base/threading/sequenced_worker_pool.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/fileapi/browser_file_system_helper.h"
+#include "content/browser/fileapi/chrome_blob_storage_context.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/common/content_client.h"
+#include "content/public/common/url_constants.h"
#include "jni/MediaResourceGetter_jni.h"
#include "net/cookies/cookie_monster.h"
#include "net/cookies/cookie_store.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
#include "url/gurl.h"
+#include "webkit/browser/blob/blob_data_handle.h"
+#include "webkit/browser/blob/blob_storage_context.h"
+
+using base::android::ConvertUTF8ToJavaString;
+using base::android::ScopedJavaLocalRef;
namespace content {
@@ -31,21 +38,62 @@ static void ReturnResultOnUIThread(
BrowserThread::UI, FROM_HERE, base::Bind(callback, result));
}
+static void RequestPlatformPathFromBlobURL(
+ const GURL& url,
+ BrowserContext* browser_context,
+ const base::Callback<void(const std::string&)>& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ ChromeBlobStorageContext* context =
+ ChromeBlobStorageContext::GetFor(browser_context);
+ scoped_ptr<webkit_blob::BlobDataHandle> handle =
+ context->context()->GetBlobDataFromPublicURL(url);
+ const std::vector<webkit_blob::BlobData::Item> items =
+ handle->data()->items();
+
+ // TODO(qinmin): handle the case when the blob data is not a single file.
+ DLOG_IF(WARNING, items.size() != 1u)
+ << "More than one blob data are present: " << items.size();
+ ReturnResultOnUIThread(callback, items[0].path().value());
+}
+
+static void RequestPlaformPathFromFileSystemURL(
+ const GURL& url,
+ int render_process_id,
+ scoped_refptr<fileapi::FileSystemContext> file_system_context,
+ const base::Callback<void(const std::string&)>& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ base::FilePath platform_path;
+ SyncGetPlatformPath(file_system_context.get(),
+ render_process_id,
+ url,
+ &platform_path);
+ base::FilePath data_storage_path;
+ PathService::Get(base::DIR_ANDROID_APP_DATA, &data_storage_path);
+ if (data_storage_path.IsParent(platform_path))
+ ReturnResultOnUIThread(callback, platform_path.value());
+ else
+ ReturnResultOnUIThread(callback, std::string());
+}
+
// Get the metadata from a media URL. When finished, a task is posted to the UI
// thread to run the callback function.
static void GetMediaMetadata(
const std::string& url, const std::string& cookies,
+ const std::string& user_agent,
const media::MediaResourceGetter::ExtractMediaMetadataCB& callback) {
JNIEnv* env = base::android::AttachCurrentThread();
- base::android::ScopedJavaLocalRef<jstring> j_url_string =
- base::android::ConvertUTF8ToJavaString(env, url);
- base::android::ScopedJavaLocalRef<jstring> j_cookies =
- base::android::ConvertUTF8ToJavaString(env, cookies);
+ ScopedJavaLocalRef<jstring> j_url_string = ConvertUTF8ToJavaString(env, url);
+ ScopedJavaLocalRef<jstring> j_cookies = ConvertUTF8ToJavaString(env, cookies);
jobject j_context = base::android::GetApplicationContext();
- base::android::ScopedJavaLocalRef<jobject> j_metadata =
- Java_MediaResourceGetter_extractMediaMetadata(
- env, j_context, j_url_string.obj(), j_cookies.obj());
+ ScopedJavaLocalRef<jstring> j_user_agent = ConvertUTF8ToJavaString(
+ env, user_agent);
+ ScopedJavaLocalRef<jobject> j_metadata =
+ Java_MediaResourceGetter_extractMediaMetadata(env,
+ j_context,
+ j_url_string.obj(),
+ j_cookies.obj(),
+ j_user_agent.obj());
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(callback, base::TimeDelta::FromMilliseconds(
@@ -63,7 +111,7 @@ class CookieGetterTask
: public base::RefCountedThreadSafe<CookieGetterTask> {
public:
CookieGetterTask(BrowserContext* browser_context,
- int renderer_id, int routing_id);
+ int render_process_id, int render_frame_id);
// Called by CookieGetterImpl to start getting cookies for a URL.
void RequestCookies(
@@ -86,20 +134,20 @@ class CookieGetterTask
ResourceContext* resource_context_;
// Render process id, used to check whether the process can access cookies.
- int renderer_id_;
+ int render_process_id_;
- // Routing id for the render view, used to check tab specific cookie policy.
- int routing_id_;
+ // Render frame id, used to check tab specific cookie policy.
+ int render_frame_id_;
DISALLOW_COPY_AND_ASSIGN(CookieGetterTask);
};
CookieGetterTask::CookieGetterTask(
- BrowserContext* browser_context, int renderer_id, int routing_id)
+ BrowserContext* browser_context, int render_process_id, int render_frame_id)
: context_getter_(browser_context->GetRequestContext()),
resource_context_(browser_context->GetResourceContext()),
- renderer_id_(renderer_id),
- routing_id_(routing_id) {
+ render_process_id_(render_process_id),
+ render_frame_id_(render_frame_id) {
}
CookieGetterTask::~CookieGetterTask() {}
@@ -110,7 +158,7 @@ void CookieGetterTask::RequestCookies(
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
ChildProcessSecurityPolicyImpl* policy =
ChildProcessSecurityPolicyImpl::GetInstance();
- if (!policy->CanAccessCookiesForOrigin(renderer_id_, url)) {
+ if (!policy->CanAccessCookiesForOrigin(render_process_id_, url)) {
callback.Run(std::string());
return;
}
@@ -139,7 +187,7 @@ void CookieGetterTask::CheckPolicyForCookies(
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (GetContentClient()->browser()->AllowGetCookie(
url, first_party_for_cookies, cookie_list,
- resource_context_, renderer_id_, routing_id_)) {
+ resource_context_, render_process_id_, render_frame_id_)) {
net::CookieStore* cookie_store =
context_getter_->GetURLRequestContext()->cookie_store();
cookie_store->GetCookiesWithOptionsAsync(
@@ -149,67 +197,16 @@ void CookieGetterTask::CheckPolicyForCookies(
}
}
-// The task object that retrieves platform path on the FILE thread.
-class PlatformPathGetterTask
- : public base::RefCountedThreadSafe<PlatformPathGetterTask> {
- public:
- PlatformPathGetterTask(fileapi::FileSystemContext* file_system_context,
- int renderer_id);
-
- // Called by MediaResourceGetterImpl to get the platform path from a file
- // system URL.
- void RequestPlaformPath(
- const GURL& url,
- const media::MediaResourceGetter::GetPlatformPathCB& callback);
-
- private:
- friend class base::RefCountedThreadSafe<PlatformPathGetterTask>;
- virtual ~PlatformPathGetterTask();
-
- // File system context for getting the platform path.
- fileapi::FileSystemContext* file_system_context_;
-
- // Render process id, used to check whether the process can access the URL.
- int renderer_id_;
-
- DISALLOW_COPY_AND_ASSIGN(PlatformPathGetterTask);
-};
-
-PlatformPathGetterTask::PlatformPathGetterTask(
- fileapi::FileSystemContext* file_system_context, int renderer_id)
- : file_system_context_(file_system_context),
- renderer_id_(renderer_id) {
-}
-
-PlatformPathGetterTask::~PlatformPathGetterTask() {}
-
-void PlatformPathGetterTask::RequestPlaformPath(
- const GURL& url,
- const media::MediaResourceGetter::GetPlatformPathCB& callback) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
- base::FilePath platform_path;
- SyncGetPlatformPath(file_system_context_,
- renderer_id_,
- url,
- &platform_path);
- base::FilePath data_storage_path;
- PathService::Get(base::DIR_ANDROID_APP_DATA, &data_storage_path);
- if (data_storage_path.IsParent(platform_path))
- callback.Run(platform_path.value());
- else
- callback.Run(std::string());
-}
-
MediaResourceGetterImpl::MediaResourceGetterImpl(
BrowserContext* browser_context,
fileapi::FileSystemContext* file_system_context,
- int renderer_id, int routing_id)
+ int render_process_id,
+ int render_frame_id)
: browser_context_(browser_context),
file_system_context_(file_system_context),
- weak_this_(this),
- renderer_id_(renderer_id),
- routing_id_(routing_id) {
-}
+ render_process_id_(render_process_id),
+ render_frame_id_(render_frame_id),
+ weak_factory_(this) {}
MediaResourceGetterImpl::~MediaResourceGetterImpl() {}
@@ -218,10 +215,11 @@ void MediaResourceGetterImpl::GetCookies(
const GetCookieCB& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
scoped_refptr<CookieGetterTask> task = new CookieGetterTask(
- browser_context_, renderer_id_, routing_id_);
+ browser_context_, render_process_id_, render_frame_id_);
GetCookieCB cb = base::Bind(&MediaResourceGetterImpl::GetCookiesCallback,
- weak_this_.GetWeakPtr(), callback);
+ weak_factory_.GetWeakPtr(),
+ callback);
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
@@ -236,21 +234,30 @@ void MediaResourceGetterImpl::GetCookiesCallback(
callback.Run(cookies);
}
-void MediaResourceGetterImpl::GetPlatformPathFromFileSystemURL(
- const GURL& url, const GetPlatformPathCB& callback) {
+void MediaResourceGetterImpl::GetPlatformPathFromURL(
+ const GURL& url, const GetPlatformPathCB& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- scoped_refptr<PlatformPathGetterTask> task = new PlatformPathGetterTask(
- file_system_context_, renderer_id_);
+ DCHECK(url.SchemeIsFileSystem() || url.SchemeIs(url::kBlobScheme));
+
+ GetPlatformPathCB cb =
+ base::Bind(&MediaResourceGetterImpl::GetPlatformPathCallback,
+ weak_factory_.GetWeakPtr(),
+ callback);
+
+ if (url.SchemeIs(url::kBlobScheme)) {
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&RequestPlatformPathFromBlobURL, url, browser_context_, cb));
+ return;
+ }
- GetPlatformPathCB cb = base::Bind(
- &MediaResourceGetterImpl::GetPlatformPathCallback,
- weak_this_.GetWeakPtr(), callback);
+ scoped_refptr<fileapi::FileSystemContext> context(file_system_context_);
BrowserThread::PostTask(
BrowserThread::FILE,
FROM_HERE,
- base::Bind(&PlatformPathGetterTask::RequestPlaformPath,
- task, url,
- base::Bind(&ReturnResultOnUIThread, cb)));
+ base::Bind(&RequestPlaformPathFromFileSystemURL, url, render_process_id_,
+ context, cb));
}
void MediaResourceGetterImpl::GetPlatformPathCallback(
@@ -261,11 +268,12 @@ void MediaResourceGetterImpl::GetPlatformPathCallback(
void MediaResourceGetterImpl::ExtractMediaMetadata(
const std::string& url, const std::string& cookies,
- const ExtractMediaMetadataCB& callback) {
+ const std::string& user_agent, const ExtractMediaMetadataCB& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool();
pool->PostWorkerTask(
- FROM_HERE, base::Bind(&GetMediaMetadata, url, cookies, callback));
+ FROM_HERE,
+ base::Bind(&GetMediaMetadata, url, cookies, user_agent, callback));
}
// static
diff --git a/chromium/content/browser/media/android/media_resource_getter_impl.h b/chromium/content/browser/media/android/media_resource_getter_impl.h
index 2b6e42ea313..c1ed4b97b36 100644
--- a/chromium/content/browser/media/android/media_resource_getter_impl.h
+++ b/chromium/content/browser/media/android/media_resource_getter_impl.h
@@ -32,11 +32,11 @@ class ResourceContext;
class MediaResourceGetterImpl : public media::MediaResourceGetter {
public:
// Construct a MediaResourceGetterImpl object. |browser_context| and
- // |renderer_id| are passed to retrieve the CookieStore.
+ // |render_process_id| are passed to retrieve the CookieStore.
// |file_system_context| are used to get the platform path.
MediaResourceGetterImpl(BrowserContext* browser_context,
fileapi::FileSystemContext* file_system_context,
- int renderer_id, int routing_id);
+ int render_process_id, int render_frame_id);
virtual ~MediaResourceGetterImpl();
// media::MediaResourceGetter implementation.
@@ -44,11 +44,12 @@ class MediaResourceGetterImpl : public media::MediaResourceGetter {
virtual void GetCookies(const GURL& url,
const GURL& first_party_for_cookies,
const GetCookieCB& callback) OVERRIDE;
- virtual void GetPlatformPathFromFileSystemURL(
+ virtual void GetPlatformPathFromURL(
const GURL& url,
const GetPlatformPathCB& callback) OVERRIDE;
virtual void ExtractMediaMetadata(
const std::string& url, const std::string& cookies,
+ const std::string& user_agent,
const ExtractMediaMetadataCB& callback) OVERRIDE;
static bool RegisterMediaResourceGetter(JNIEnv* env);
@@ -68,14 +69,14 @@ class MediaResourceGetterImpl : public media::MediaResourceGetter {
// FileSystemContext to be used on FILE thread.
fileapi::FileSystemContext* file_system_context_;
- // Used to post tasks.
- base::WeakPtrFactory<MediaResourceGetterImpl> weak_this_;
-
// Render process id, used to check whether the process can access cookies.
- int renderer_id_;
+ int render_process_id_;
+
+ // Render frame id, used to check tab specific cookie policy.
+ int render_frame_id_;
- // Routing id for the render view, used to check tab specific cookie policy.
- int routing_id_;
+ // NOTE: Weak pointers must be invalidated before all other member variables.
+ base::WeakPtrFactory<MediaResourceGetterImpl> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(MediaResourceGetterImpl);
};
diff --git a/chromium/content/browser/media/capture/DEPS b/chromium/content/browser/media/capture/DEPS
new file mode 100644
index 00000000000..c88063c1564
--- /dev/null
+++ b/chromium/content/browser/media/capture/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+third_party/libyuv", # For scaling in desktop_capture_device.cc.
+]
diff --git a/chromium/content/browser/media/capture/OWNERS b/chromium/content/browser/media/capture/OWNERS
new file mode 100644
index 00000000000..063a202ff2d
--- /dev/null
+++ b/chromium/content/browser/media/capture/OWNERS
@@ -0,0 +1,4 @@
+hclam@chromium.org
+miu@chromium.org
+sergeyu@chromium.org
+wez@chromium.org
diff --git a/chromium/content/browser/renderer_host/media/audio_mirroring_manager.cc b/chromium/content/browser/media/capture/audio_mirroring_manager.cc
index 8a2bc4b0b5c..b8051fa73d0 100644
--- a/chromium/content/browser/renderer_host/media/audio_mirroring_manager.cc
+++ b/chromium/content/browser/media/capture/audio_mirroring_manager.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "content/browser/renderer_host/media/audio_mirroring_manager.h"
+#include "content/browser/media/capture/audio_mirroring_manager.h"
#include "content/public/browser/browser_thread.h"
diff --git a/chromium/content/browser/renderer_host/media/audio_mirroring_manager.h b/chromium/content/browser/media/capture/audio_mirroring_manager.h
index 0db4f17539f..fe7db7ad52e 100644
--- a/chromium/content/browser/renderer_host/media/audio_mirroring_manager.h
+++ b/chromium/content/browser/media/capture/audio_mirroring_manager.h
@@ -24,8 +24,8 @@
// #2 and #3 above may occur in any order, as it is the job of
// AudioMirroringManager to realize when the players can be "matched up."
-#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_AUDIO_MIRRORING_MANAGER_H_
-#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_AUDIO_MIRRORING_MANAGER_H_
+#ifndef CONTENT_BROWSER_MEDIA_CAPTURE_AUDIO_MIRRORING_MANAGER_H_
+#define CONTENT_BROWSER_MEDIA_CAPTURE_AUDIO_MIRRORING_MANAGER_H_
#include <map>
#include <utility>
@@ -105,4 +105,4 @@ class CONTENT_EXPORT AudioMirroringManager {
} // namespace content
-#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_AUDIO_MIRRORING_MANAGER_H_
+#endif // CONTENT_BROWSER_MEDIA_CAPTURE_AUDIO_MIRRORING_MANAGER_H_
diff --git a/chromium/content/browser/renderer_host/media/audio_mirroring_manager_unittest.cc b/chromium/content/browser/media/capture/audio_mirroring_manager_unittest.cc
index 2468b2c48c9..cd197769aa5 100644
--- a/chromium/content/browser/renderer_host/media/audio_mirroring_manager_unittest.cc
+++ b/chromium/content/browser/media/capture/audio_mirroring_manager_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "content/browser/renderer_host/media/audio_mirroring_manager.h"
+#include "content/browser/media/capture/audio_mirroring_manager.h"
#include <map>
@@ -46,8 +46,7 @@ class MockMirroringDestination
class AudioMirroringManagerTest : public testing::Test {
public:
AudioMirroringManagerTest()
- : message_loop_(base::MessageLoop::TYPE_IO),
- io_thread_(BrowserThread::IO, &message_loop_),
+ : io_thread_(BrowserThread::IO, &message_loop_),
params_(AudioParameters::AUDIO_FAKE, media::CHANNEL_LAYOUT_STEREO,
AudioParameters::kAudioCDSampleRate, 16,
AudioParameters::kAudioCDSampleRate / 10) {}
@@ -102,7 +101,7 @@ class AudioMirroringManagerTest : public testing::Test {
}
private:
- base::MessageLoop message_loop_;
+ base::MessageLoopForIO message_loop_;
BrowserThreadImpl io_thread_;
AudioParameters params_;
AudioMirroringManager mirroring_manager_;
diff --git a/chromium/content/browser/renderer_host/media/video_capture_device_impl.cc b/chromium/content/browser/media/capture/content_video_capture_device_core.cc
index 9b37b296c4f..6878652de07 100644
--- a/chromium/content/browser/renderer_host/media/video_capture_device_impl.cc
+++ b/chromium/content/browser/media/capture/content_video_capture_device_core.cc
@@ -1,8 +1,8 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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/renderer_host/media/video_capture_device_impl.h"
+#include "content/browser/media/capture/content_video_capture_device_core.h"
#include "base/basictypes.h"
#include "base/bind.h"
@@ -15,13 +15,14 @@
#include "base/message_loop/message_loop_proxy.h"
#include "base/metrics/histogram.h"
#include "base/sequenced_task_runner.h"
+#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
#include "content/public/browser/browser_thread.h"
-#include "media/base/bind_to_loop.h"
+#include "media/base/bind_to_current_loop.h"
#include "media/base/video_frame.h"
#include "media/base/video_util.h"
#include "media/video/capture/video_capture_types.h"
@@ -34,10 +35,8 @@ namespace {
void DeleteCaptureMachineOnUIThread(
scoped_ptr<VideoCaptureMachine> capture_machine) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- if (capture_machine) {
- capture_machine->Stop();
- capture_machine.reset();
- }
+
+ capture_machine.reset();
}
} // namespace
@@ -50,19 +49,24 @@ ThreadSafeCaptureOracle::ThreadSafeCaptureOracle(
oracle_(oracle.Pass()),
params_(params),
capture_size_updated_(false) {
- // Frame dimensions must each be an even integer since the client wants (or
- // will convert to) YUV420.
- capture_size_ = gfx::Size(
- MakeEven(params.requested_format.frame_size.width()),
- MakeEven(params.requested_format.frame_size.height()));
- frame_rate_ = params.requested_format.frame_rate;
+ switch (params_.requested_format.pixel_format) {
+ case media::PIXEL_FORMAT_I420:
+ video_frame_format_ = media::VideoFrame::I420;
+ break;
+ case media::PIXEL_FORMAT_TEXTURE:
+ video_frame_format_ = media::VideoFrame::NATIVE_TEXTURE;
+ break;
+ default:
+ LOG(FATAL) << "Unexpected pixel_format "
+ << params_.requested_format.pixel_format;
+ }
}
ThreadSafeCaptureOracle::~ThreadSafeCaptureOracle() {}
bool ThreadSafeCaptureOracle::ObserveEventAndDecideCapture(
VideoCaptureOracle::Event event,
- base::Time event_time,
+ base::TimeTicks event_time,
scoped_refptr<media::VideoFrame>* storage,
CaptureFrameCallback* callback) {
base::AutoLock guard(lock_);
@@ -71,7 +75,8 @@ bool ThreadSafeCaptureOracle::ObserveEventAndDecideCapture(
return false; // Capture is stopped.
scoped_refptr<media::VideoCaptureDevice::Client::Buffer> output_buffer =
- client_->ReserveOutputBuffer(media::VideoFrame::I420, capture_size_);
+ client_->ReserveOutputBuffer(video_frame_format_,
+ params_.requested_format.frame_size);
const bool should_capture =
oracle_->ObserveEventAndDecideCapture(event, event_time);
const bool content_is_dirty =
@@ -112,23 +117,32 @@ bool ThreadSafeCaptureOracle::ObserveEventAndDecideCapture(
TRACE_EVENT_ASYNC_BEGIN2("mirroring", "Capture", output_buffer.get(),
"frame_number", frame_number,
"trigger", event_name);
+ // NATIVE_TEXTURE frames wrap a texture mailbox, which we don't have at the
+ // moment. We do not construct those frames.
+ if (video_frame_format_ != media::VideoFrame::NATIVE_TEXTURE) {
+ *storage = media::VideoFrame::WrapExternalPackedMemory(
+ video_frame_format_,
+ params_.requested_format.frame_size,
+ gfx::Rect(params_.requested_format.frame_size),
+ params_.requested_format.frame_size,
+ static_cast<uint8*>(output_buffer->data()),
+ output_buffer->size(),
+ base::SharedMemory::NULLHandle(),
+ base::TimeDelta(),
+ base::Closure());
+ }
*callback = base::Bind(&ThreadSafeCaptureOracle::DidCaptureFrame,
this,
- output_buffer,
- frame_number);
- *storage = media::VideoFrame::WrapExternalPackedMemory(
- media::VideoFrame::I420,
- capture_size_,
- gfx::Rect(capture_size_),
- capture_size_,
- static_cast<uint8*>(output_buffer->data()),
- output_buffer->size(),
- base::SharedMemory::NULLHandle(),
- base::TimeDelta(),
- base::Closure());
+ frame_number,
+ output_buffer);
return true;
}
+gfx::Size ThreadSafeCaptureOracle::GetCaptureSize() const {
+ base::AutoLock guard(lock_);
+ return params_.requested_format.frame_size;
+}
+
void ThreadSafeCaptureOracle::UpdateCaptureSize(const gfx::Size& source_size) {
base::AutoLock guard(lock_);
@@ -142,11 +156,11 @@ void ThreadSafeCaptureOracle::UpdateCaptureSize(const gfx::Size& source_size) {
source_size.height() > params_.requested_format.frame_size.height()) {
gfx::Rect capture_rect = media::ComputeLetterboxRegion(
gfx::Rect(params_.requested_format.frame_size), source_size);
- capture_size_ = gfx::Size(MakeEven(capture_rect.width()),
- MakeEven(capture_rect.height()));
+ params_.requested_format.frame_size.SetSize(
+ MakeEven(capture_rect.width()), MakeEven(capture_rect.height()));
} else {
- capture_size_ = gfx::Size(MakeEven(source_size.width()),
- MakeEven(source_size.height()));
+ params_.requested_format.frame_size.SetSize(
+ MakeEven(source_size.width()), MakeEven(source_size.height()));
}
capture_size_updated_ = true;
}
@@ -157,16 +171,17 @@ void ThreadSafeCaptureOracle::Stop() {
client_.reset();
}
-void ThreadSafeCaptureOracle::ReportError() {
+void ThreadSafeCaptureOracle::ReportError(const std::string& reason) {
base::AutoLock guard(lock_);
if (client_)
- client_->OnError();
+ client_->OnError(reason);
}
void ThreadSafeCaptureOracle::DidCaptureFrame(
- scoped_refptr<media::VideoCaptureDevice::Client::Buffer> buffer,
int frame_number,
- base::Time timestamp,
+ const scoped_refptr<media::VideoCaptureDevice::Client::Buffer>& buffer,
+ const scoped_refptr<media::VideoFrame>& frame,
+ base::TimeTicks timestamp,
bool success) {
base::AutoLock guard(lock_);
TRACE_EVENT_ASYNC_END2("mirroring", "Capture", buffer.get(),
@@ -178,16 +193,14 @@ void ThreadSafeCaptureOracle::DidCaptureFrame(
if (success) {
if (oracle_->CompleteCapture(frame_number, timestamp)) {
- client_->OnIncomingCapturedBuffer(buffer,
- media::VideoFrame::I420,
- capture_size_,
- timestamp,
- frame_rate_);
+ media::VideoCaptureFormat format = params_.requested_format;
+ format.frame_size = frame->coded_size();
+ client_->OnIncomingCapturedVideoFrame(buffer, format, frame, timestamp);
}
}
}
-void VideoCaptureDeviceImpl::AllocateAndStart(
+void ContentVideoCaptureDeviceCore::AllocateAndStart(
const media::VideoCaptureParams& params,
scoped_ptr<media::VideoCaptureDevice::Client> client) {
DCHECK(thread_checker_.CalledOnValidThread());
@@ -198,19 +211,38 @@ void VideoCaptureDeviceImpl::AllocateAndStart(
}
if (params.requested_format.frame_rate <= 0) {
- DVLOG(1) << "invalid frame_rate: " << params.requested_format.frame_rate;
- client->OnError();
+ std::string error_msg("Invalid frame_rate: ");
+ error_msg += base::DoubleToString(params.requested_format.frame_rate);
+ DVLOG(1) << error_msg;
+ client->OnError(error_msg);
return;
}
- if (params.requested_format.frame_size.width() < kMinFrameWidth ||
- params.requested_format.frame_size.height() < kMinFrameHeight) {
- DVLOG(1) << "invalid frame size: "
- << params.requested_format.frame_size.ToString();
- client->OnError();
+ if (params.requested_format.pixel_format != media::PIXEL_FORMAT_I420 &&
+ params.requested_format.pixel_format != media::PIXEL_FORMAT_TEXTURE) {
+ std::string error_msg = base::StringPrintf(
+ "unsupported format: %d", params.requested_format.pixel_format);
+ DVLOG(1) << error_msg;
+ client->OnError(error_msg);
return;
}
+ if (params.requested_format.frame_size.width() < kMinFrameWidth ||
+ params.requested_format.frame_size.height() < kMinFrameHeight) {
+ std::string error_msg =
+ "invalid frame size: " + params.requested_format.frame_size.ToString();
+ DVLOG(1) << error_msg;
+ client->OnError(error_msg);
+ return;
+ }
+
+ media::VideoCaptureParams new_params = params;
+ // Frame dimensions must each be an even integer since the client wants (or
+ // will convert to) YUV420.
+ new_params.requested_format.frame_size.SetSize(
+ MakeEven(params.requested_format.frame_size.width()),
+ MakeEven(params.requested_format.frame_size.height()));
+
base::TimeDelta capture_period = base::TimeDelta::FromMicroseconds(
1000000.0 / params.requested_format.frame_rate + 0.5);
@@ -218,21 +250,22 @@ void VideoCaptureDeviceImpl::AllocateAndStart(
new VideoCaptureOracle(capture_period,
kAcceleratedSubscriberIsSupported));
oracle_proxy_ =
- new ThreadSafeCaptureOracle(client.Pass(), oracle.Pass(), params);
+ new ThreadSafeCaptureOracle(client.Pass(), oracle.Pass(), new_params);
// Starts the capture machine asynchronously.
BrowserThread::PostTaskAndReplyWithResult(
- BrowserThread::UI, FROM_HERE,
+ BrowserThread::UI,
+ FROM_HERE,
base::Bind(&VideoCaptureMachine::Start,
base::Unretained(capture_machine_.get()),
- oracle_proxy_),
- base::Bind(&VideoCaptureDeviceImpl::CaptureStarted,
- AsWeakPtr()));
+ oracle_proxy_,
+ new_params),
+ base::Bind(&ContentVideoCaptureDeviceCore::CaptureStarted, AsWeakPtr()));
TransitionStateTo(kCapturing);
}
-void VideoCaptureDeviceImpl::StopAndDeAllocate() {
+void ContentVideoCaptureDeviceCore::StopAndDeAllocate() {
DCHECK(thread_checker_.CalledOnValidThread());
if (state_ != kCapturing)
@@ -247,34 +280,40 @@ void VideoCaptureDeviceImpl::StopAndDeAllocate() {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE, base::Bind(
&VideoCaptureMachine::Stop,
- base::Unretained(capture_machine_.get())));
+ base::Unretained(capture_machine_.get()),
+ base::Bind(&base::DoNothing)));
}
-void VideoCaptureDeviceImpl::CaptureStarted(bool success) {
+void ContentVideoCaptureDeviceCore::CaptureStarted(bool success) {
DCHECK(thread_checker_.CalledOnValidThread());
if (!success) {
- DVLOG(1) << "Failed to start capture machine.";
- Error();
+ std::string reason("Failed to start capture machine.");
+ DVLOG(1) << reason;
+ Error(reason);
}
}
-VideoCaptureDeviceImpl::VideoCaptureDeviceImpl(
+ContentVideoCaptureDeviceCore::ContentVideoCaptureDeviceCore(
scoped_ptr<VideoCaptureMachine> capture_machine)
: state_(kIdle),
capture_machine_(capture_machine.Pass()) {}
-VideoCaptureDeviceImpl::~VideoCaptureDeviceImpl() {
+ContentVideoCaptureDeviceCore::~ContentVideoCaptureDeviceCore() {
// If capture_machine is not NULL, then we need to return to the UI thread to
// safely stop the capture machine.
if (capture_machine_) {
+ VideoCaptureMachine* capture_machine_ptr = capture_machine_.get();
BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE, base::Bind(
- &DeleteCaptureMachineOnUIThread, base::Passed(&capture_machine_)));
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&VideoCaptureMachine::Stop,
+ base::Unretained(capture_machine_ptr),
+ base::Bind(&DeleteCaptureMachineOnUIThread,
+ base::Passed(&capture_machine_))));
}
- DVLOG(1) << "VideoCaptureDeviceImpl@" << this << " destroying.";
+ DVLOG(1) << "ContentVideoCaptureDeviceCore@" << this << " destroying.";
}
-void VideoCaptureDeviceImpl::TransitionStateTo(State next_state) {
+void ContentVideoCaptureDeviceCore::TransitionStateTo(State next_state) {
DCHECK(thread_checker_.CalledOnValidThread());
#ifndef NDEBUG
@@ -288,14 +327,14 @@ void VideoCaptureDeviceImpl::TransitionStateTo(State next_state) {
state_ = next_state;
}
-void VideoCaptureDeviceImpl::Error() {
+void ContentVideoCaptureDeviceCore::Error(const std::string& reason) {
DCHECK(thread_checker_.CalledOnValidThread());
if (state_ == kIdle)
return;
if (oracle_proxy_)
- oracle_proxy_->ReportError();
+ oracle_proxy_->ReportError(reason);
StopAndDeAllocate();
TransitionStateTo(kError);
diff --git a/chromium/content/browser/renderer_host/media/video_capture_device_impl.h b/chromium/content/browser/media/capture/content_video_capture_device_core.h
index d9329b6aa43..3a7e6974ae7 100644
--- a/chromium/content/browser/renderer_host/media/video_capture_device_impl.h
+++ b/chromium/content/browser/media/capture/content_video_capture_device_core.h
@@ -1,9 +1,9 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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_RENDERER_HOST_VIDEO_CAPTURE_DEVICE_IMPL_H_
-#define CONTENT_BROWSER_RENDERER_HOST_VIDEO_CAPTURE_DEVICE_IMPL_H_
+#ifndef CONTENT_BROWSER_MEDIA_CAPTURE_CONTENT_VIDEO_CAPTURE_DEVICE_CORE_H_
+#define CONTENT_BROWSER_MEDIA_CAPTURE_CONTENT_VIDEO_CAPTURE_DEVICE_CORE_H_
#include <string>
@@ -11,10 +11,16 @@
#include "base/memory/weak_ptr.h"
#include "base/threading/thread.h"
#include "base/threading/thread_checker.h"
-#include "content/browser/renderer_host/media/video_capture_oracle.h"
+#include "content/browser/media/capture/video_capture_oracle.h"
#include "content/common/content_export.h"
+#include "media/base/video_frame.h"
#include "media/video/capture/video_capture_device.h"
+namespace media {
+class VideoCaptureParams;
+class VideoFrame;
+} // namespace media
+
namespace content {
const int kMinFrameWidth = 2;
@@ -48,14 +54,15 @@ class ThreadSafeCaptureOracle
const media::VideoCaptureParams& params);
// Called when a captured frame is available or an error has occurred.
- // If |success| is true then the frame provided is valid and |timestamp|
- // indicates when the frame was painted.
- // If |success| is false, both the frame provided and |timestamp| are invalid.
- typedef base::Callback<void(base::Time timestamp, bool success)>
- CaptureFrameCallback;
+ // If |success| is true then |frame| is valid and |timestamp| indicates when
+ // the frame was painted.
+ // If |success| is false, all other parameters are invalid.
+ typedef base::Callback<void(const scoped_refptr<media::VideoFrame>& frame,
+ base::TimeTicks timestamp,
+ bool success)> CaptureFrameCallback;
bool ObserveEventAndDecideCapture(VideoCaptureOracle::Event event,
- base::Time event_time,
+ base::TimeTicks event_time,
scoped_refptr<media::VideoFrame>* storage,
CaptureFrameCallback* callback);
@@ -63,6 +70,9 @@ class ThreadSafeCaptureOracle
return oracle_->capture_period();
}
+ // Returns the current capture resolution.
+ gfx::Size GetCaptureSize() const;
+
// Updates capture resolution based on the supplied source size and the
// maximum frame size.
void UpdateCaptureSize(const gfx::Size& source_size);
@@ -71,7 +81,7 @@ class ThreadSafeCaptureOracle
void Stop();
// Signal an error to the client.
- void ReportError();
+ void ReportError(const std::string& reason);
private:
friend class base::RefCountedThreadSafe<ThreadSafeCaptureOracle>;
@@ -79,12 +89,14 @@ class ThreadSafeCaptureOracle
// Callback invoked on completion of all captures.
void DidCaptureFrame(
- scoped_refptr<media::VideoCaptureDevice::Client::Buffer> buffer,
int frame_number,
- base::Time timestamp,
+ const scoped_refptr<media::VideoCaptureDevice::Client::Buffer>& buffer,
+ const scoped_refptr<media::VideoFrame>& frame,
+ base::TimeTicks timestamp,
bool success);
+
// Protects everything below it.
- base::Lock lock_;
+ mutable base::Lock lock_;
// Recipient of our capture activity.
scoped_ptr<media::VideoCaptureDevice::Client> client_;
@@ -93,14 +105,13 @@ class ThreadSafeCaptureOracle
const scoped_ptr<VideoCaptureOracle> oracle_;
// The video capture parameters used to construct the oracle proxy.
- const media::VideoCaptureParams params_;
+ media::VideoCaptureParams params_;
// Indicates if capture size has been updated after construction.
bool capture_size_updated_;
- // The current capturing resolution and frame rate.
- gfx::Size capture_size_;
- int frame_rate_;
+ // The current capturing format, as a media::VideoFrame::Format.
+ media::VideoFrame::Format video_frame_format_;
};
// Keeps track of the video capture source frames and executes copying on the
@@ -115,11 +126,12 @@ class VideoCaptureMachine {
// Starts capturing. Returns true if succeeded.
// Must be run on the UI BrowserThread.
- virtual bool Start(
- const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy) = 0;
+ virtual bool Start(const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy,
+ const media::VideoCaptureParams& params) = 0;
// Stops capturing. Must be run on the UI BrowserThread.
- virtual void Stop() = 0;
+ // |callback| is invoked after the capturing has stopped.
+ virtual void Stop(const base::Closure& callback) = 0;
protected:
bool started_;
@@ -128,23 +140,24 @@ class VideoCaptureMachine {
DISALLOW_COPY_AND_ASSIGN(VideoCaptureMachine);
};
-// The "meat" of the video capture implementation.
+// The "meat" of a content video capturer.
//
// Separating this from the "shell classes" WebContentsVideoCaptureDevice and
-// BrowserCompositorCaptureDevice allows safe destruction without needing to
-// block any threads (e.g., the IO BrowserThread), as well as code sharing.
+// DesktopCaptureDeviceAura allows safe destruction without needing to block any
+// threads (e.g., the IO BrowserThread), as well as code sharing.
//
-// VideoCaptureDeviceImpl manages a simple state machine and the pipeline (see
-// notes at top of this file). It times the start of successive
-// captures and facilitates the processing of each through the stages of the
+// ContentVideoCaptureDeviceCore manages a simple state machine and the pipeline
+// (see notes at top of this file). It times the start of successive captures
+// and facilitates the processing of each through the stages of the
// pipeline.
-class CONTENT_EXPORT VideoCaptureDeviceImpl
- : public base::SupportsWeakPtr<VideoCaptureDeviceImpl> {
+class CONTENT_EXPORT ContentVideoCaptureDeviceCore
+ : public base::SupportsWeakPtr<ContentVideoCaptureDeviceCore> {
public:
- VideoCaptureDeviceImpl(scoped_ptr<VideoCaptureMachine> capture_machine);
- virtual ~VideoCaptureDeviceImpl();
+ ContentVideoCaptureDeviceCore(
+ scoped_ptr<VideoCaptureMachine> capture_machine);
+ virtual ~ContentVideoCaptureDeviceCore();
- // Asynchronous requests to change VideoCaptureDeviceImpl state.
+ // Asynchronous requests to change ContentVideoCaptureDeviceCore state.
void AllocateAndStart(const media::VideoCaptureParams& params,
scoped_ptr<media::VideoCaptureDevice::Client> client);
void StopAndDeAllocate();
@@ -164,7 +177,7 @@ class CONTENT_EXPORT VideoCaptureDeviceImpl
void CaptureStarted(bool success);
// Stops capturing and notifies client_ of an error state.
- void Error();
+ void Error(const std::string& reason);
// Tracks that all activity occurs on the media stream manager's thread.
base::ThreadChecker thread_checker_;
@@ -178,14 +191,14 @@ class CONTENT_EXPORT VideoCaptureDeviceImpl
scoped_ptr<VideoCaptureMachine> capture_machine_;
// Our thread-safe capture oracle which serves as the gateway to the video
- // capture pipeline. Besides the WCVCD itself, it is the only component of the
- // system with direct access to |client_|.
+ // capture pipeline. Besides the VideoCaptureDevice itself, it is the only
+ // component of the/ system with direct access to |client_|.
scoped_refptr<ThreadSafeCaptureOracle> oracle_proxy_;
- DISALLOW_COPY_AND_ASSIGN(VideoCaptureDeviceImpl);
+ DISALLOW_COPY_AND_ASSIGN(ContentVideoCaptureDeviceCore);
};
} // namespace content
-#endif // CONTENT_BROWSER_RENDERER_HOST_VIDEO_CAPTURE_DEVICE_IMPL_H_
+#endif // CONTENT_BROWSER_MEDIA_CAPTURE_CONTENT_VIDEO_CAPTURE_DEVICE_CORE_H_
diff --git a/chromium/content/browser/renderer_host/media/desktop_capture_device.cc b/chromium/content/browser/media/capture/desktop_capture_device.cc
index e82b96fdb0b..cf180c9db26 100644
--- a/chromium/content/browser/renderer_host/media/desktop_capture_device.cc
+++ b/chromium/content/browser/media/capture/desktop_capture_device.cc
@@ -2,15 +2,19 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "content/browser/renderer_host/media/desktop_capture_device.h"
+#include "content/browser/media/capture/desktop_capture_device.h"
#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
+#include "base/metrics/field_trial.h"
+#include "base/metrics/histogram.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/string_number_conversions.h"
#include "base/synchronization/lock.h"
#include "base/threading/sequenced_worker_pool.h"
+#include "base/threading/thread.h"
+#include "content/browser/media/capture/desktop_capture_device_uma_types.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/desktop_media_id.h"
#include "media/base/video_util.h"
@@ -50,13 +54,17 @@ class DesktopCaptureDevice::Core
public webrtc::DesktopCapturer::Callback {
public:
Core(scoped_refptr<base::SequencedTaskRunner> task_runner,
- scoped_ptr<webrtc::DesktopCapturer> capturer);
+ scoped_ptr<base::Thread> thread,
+ scoped_ptr<webrtc::DesktopCapturer> capturer,
+ DesktopMediaID::Type type);
// Implementation of VideoCaptureDevice methods.
void AllocateAndStart(const media::VideoCaptureParams& params,
scoped_ptr<Client> client);
void StopAndDeAllocate();
+ void SetNotificationWindowId(gfx::NativeViewId window_id);
+
private:
friend class base::RefCountedThreadSafe<Core>;
virtual ~Core();
@@ -86,9 +94,14 @@ class DesktopCaptureDevice::Core
// Captures a single frame.
void DoCapture();
+ void DoSetNotificationWindowId(gfx::NativeViewId window_id);
+
// Task runner used for capturing operations.
scoped_refptr<base::SequencedTaskRunner> task_runner_;
+ // The thread on which the capturer is running.
+ scoped_ptr<base::Thread> thread_;
+
// The underlying DesktopCapturer instance used to capture frames.
scoped_ptr<webrtc::DesktopCapturer> desktop_capturer_;
@@ -121,16 +134,34 @@ class DesktopCaptureDevice::Core
// True when waiting for |desktop_capturer_| to capture current frame.
bool capture_in_progress_;
+ // True if the first capture call has returned. Used to log the first capture
+ // result.
+ bool first_capture_returned_;
+
+ // The type of the capturer.
+ DesktopMediaID::Type capturer_type_;
+
+ scoped_ptr<webrtc::BasicDesktopFrame> black_frame_;
+
DISALLOW_COPY_AND_ASSIGN(Core);
};
DesktopCaptureDevice::Core::Core(
scoped_refptr<base::SequencedTaskRunner> task_runner,
- scoped_ptr<webrtc::DesktopCapturer> capturer)
+ scoped_ptr<base::Thread> thread,
+ scoped_ptr<webrtc::DesktopCapturer> capturer,
+ DesktopMediaID::Type type)
: task_runner_(task_runner),
+ thread_(thread.Pass()),
desktop_capturer_(capturer.Pass()),
capture_task_posted_(false),
- capture_in_progress_(false) {}
+ capture_in_progress_(false),
+ first_capture_returned_(false),
+ capturer_type_(type) {
+ DCHECK(!task_runner_.get() || !thread_.get());
+ if (thread_.get())
+ task_runner_ = thread_->message_loop_proxy();
+}
DesktopCaptureDevice::Core::~Core() {
}
@@ -152,6 +183,12 @@ void DesktopCaptureDevice::Core::StopAndDeAllocate() {
base::Bind(&Core::DoStopAndDeAllocate, this));
}
+void DesktopCaptureDevice::Core::SetNotificationWindowId(
+ gfx::NativeViewId window_id) {
+ task_runner_->PostTask(
+ FROM_HERE, base::Bind(&Core::DoSetNotificationWindowId, this, window_id));
+}
+
webrtc::SharedMemory*
DesktopCaptureDevice::Core::CreateSharedMemory(size_t size) {
return NULL;
@@ -162,19 +199,56 @@ void DesktopCaptureDevice::Core::OnCaptureCompleted(
DCHECK(task_runner_->RunsTasksOnCurrentThread());
DCHECK(capture_in_progress_);
+ if (!first_capture_returned_) {
+ first_capture_returned_ = true;
+ if (capturer_type_ == DesktopMediaID::TYPE_SCREEN) {
+ IncrementDesktopCaptureCounter(frame ? FIRST_SCREEN_CAPTURE_SUCCEEDED
+ : FIRST_SCREEN_CAPTURE_FAILED);
+ } else {
+ IncrementDesktopCaptureCounter(frame ? FIRST_WINDOW_CAPTURE_SUCCEEDED
+ : FIRST_WINDOW_CAPTURE_FAILED);
+ }
+ }
+
capture_in_progress_ = false;
if (!frame) {
- LOG(ERROR) << "Failed to capture a frame.";
- client_->OnError();
+ std::string log("Failed to capture a frame.");
+ LOG(ERROR) << log;
+ client_->OnError(log);
return;
}
if (!client_)
return;
+ base::TimeDelta capture_time(
+ base::TimeDelta::FromMilliseconds(frame->capture_time_ms()));
+ UMA_HISTOGRAM_TIMES(
+ capturer_type_ == DesktopMediaID::TYPE_SCREEN ? kUmaScreenCaptureTime
+ : kUmaWindowCaptureTime,
+ capture_time);
+
scoped_ptr<webrtc::DesktopFrame> owned_frame(frame);
+ // On OSX We receive a 1x1 frame when the shared window is minimized. It
+ // cannot be subsampled to I420 and will be dropped downstream. So we replace
+ // it with a black frame to avoid the video appearing frozen at the last
+ // frame.
+ if (frame->size().width() == 1 || frame->size().height() == 1) {
+ if (!black_frame_.get()) {
+ black_frame_.reset(
+ new webrtc::BasicDesktopFrame(
+ webrtc::DesktopSize(capture_format_.frame_size.width(),
+ capture_format_.frame_size.height())));
+ memset(black_frame_->data(),
+ 0,
+ black_frame_->stride() * black_frame_->size().height());
+ }
+ owned_frame.reset();
+ frame = black_frame_.get();
+ }
+
// Handle initial frame size and size changes.
RefreshCaptureFormat(frame->size());
@@ -231,8 +305,8 @@ void DesktopCaptureDevice::Core::OnCaptureCompleted(
output_data = output_frame_->data();
}
- client_->OnIncomingCapturedFrame(output_data, output_bytes, base::Time::Now(),
- 0, capture_format_);
+ client_->OnIncomingCapturedData(
+ output_data, output_bytes, capture_format_, 0, base::TimeTicks::Now());
}
void DesktopCaptureDevice::Core::DoAllocateAndStart(
@@ -347,14 +421,17 @@ void DesktopCaptureDevice::Core::DoCapture() {
DCHECK(!capture_in_progress_);
}
+void DesktopCaptureDevice::Core::DoSetNotificationWindowId(
+ gfx::NativeViewId window_id) {
+ DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(window_id);
+ desktop_capturer_->SetExcludedWindow(window_id);
+}
+
// static
scoped_ptr<media::VideoCaptureDevice> DesktopCaptureDevice::Create(
const DesktopMediaID& source) {
- scoped_refptr<base::SequencedWorkerPool> blocking_pool =
- BrowserThread::GetBlockingPool();
- scoped_refptr<base::SequencedTaskRunner> task_runner =
- blocking_pool->GetSequencedTaskRunner(
- blocking_pool->GetSequenceToken());
+ scoped_ptr<base::Thread> ui_thread;
webrtc::DesktopCaptureOptions options =
webrtc::DesktopCaptureOptions::CreateDefault();
@@ -365,12 +442,29 @@ scoped_ptr<media::VideoCaptureDevice> DesktopCaptureDevice::Create(
switch (source.type) {
case DesktopMediaID::TYPE_SCREEN: {
- scoped_ptr<webrtc::DesktopCapturer> screen_capturer;
+ scoped_ptr<webrtc::ScreenCapturer> screen_capturer;
+
+#if defined(OS_WIN)
+ bool magnification_allowed =
+ base::FieldTrialList::FindFullName("ScreenCaptureUseMagnification") ==
+ "Enabled";
+
+ if (magnification_allowed) {
+ // The magnification capturer requires running on a dedicated UI thread.
+ ui_thread.reset(new base::Thread("screenCaptureUIThread"));
+ base::Thread::Options thread_options(base::MessageLoop::TYPE_UI, 0);
+ ui_thread->StartWithOptions(thread_options);
+
+ options.set_allow_use_magnification_api(true);
+ }
+#endif
+
screen_capturer.reset(webrtc::ScreenCapturer::Create(options));
- if (screen_capturer) {
+ if (screen_capturer && screen_capturer->SelectScreen(source.id)) {
capturer.reset(new webrtc::DesktopAndCursorComposer(
screen_capturer.release(),
- webrtc::MouseCursorMonitor::CreateForScreen(options)));
+ webrtc::MouseCursorMonitor::CreateForScreen(options, source.id)));
+ IncrementDesktopCaptureCounter(SCREEN_CAPTURER_CREATED);
}
break;
}
@@ -379,9 +473,11 @@ scoped_ptr<media::VideoCaptureDevice> DesktopCaptureDevice::Create(
scoped_ptr<webrtc::WindowCapturer> window_capturer(
webrtc::WindowCapturer::Create(options));
if (window_capturer && window_capturer->SelectWindow(source.id)) {
+ window_capturer->BringSelectedWindowToFront();
capturer.reset(new webrtc::DesktopAndCursorComposer(
window_capturer.release(),
webrtc::MouseCursorMonitor::CreateForWindow(options, source.id)));
+ IncrementDesktopCaptureCounter(WINDOW_CATPTURER_CREATED);
}
break;
}
@@ -392,17 +488,21 @@ scoped_ptr<media::VideoCaptureDevice> DesktopCaptureDevice::Create(
}
scoped_ptr<media::VideoCaptureDevice> result;
- if (capturer)
- result.reset(new DesktopCaptureDevice(task_runner, capturer.Pass()));
+ if (capturer) {
+ scoped_refptr<base::SequencedTaskRunner> task_runner;
+ if (!ui_thread.get()) {
+ scoped_refptr<base::SequencedWorkerPool> blocking_pool =
+ BrowserThread::GetBlockingPool();
+ task_runner = blocking_pool->GetSequencedTaskRunner(
+ blocking_pool->GetSequenceToken());
+ }
+ result.reset(new DesktopCaptureDevice(
+ task_runner, ui_thread.Pass(), capturer.Pass(), source.type));
+ }
return result.Pass();
}
-DesktopCaptureDevice::DesktopCaptureDevice(
- scoped_refptr<base::SequencedTaskRunner> task_runner,
- scoped_ptr<webrtc::DesktopCapturer> capturer)
- : core_(new Core(task_runner, capturer.Pass())) {}
-
DesktopCaptureDevice::~DesktopCaptureDevice() {
StopAndDeAllocate();
}
@@ -417,4 +517,17 @@ void DesktopCaptureDevice::StopAndDeAllocate() {
core_->StopAndDeAllocate();
}
+void DesktopCaptureDevice::SetNotificationWindowId(
+ gfx::NativeViewId window_id) {
+ core_->SetNotificationWindowId(window_id);
+}
+
+DesktopCaptureDevice::DesktopCaptureDevice(
+ scoped_refptr<base::SequencedTaskRunner> task_runner,
+ scoped_ptr<base::Thread> thread,
+ scoped_ptr<webrtc::DesktopCapturer> capturer,
+ DesktopMediaID::Type type)
+ : core_(new Core(task_runner, thread.Pass(), capturer.Pass(), type)) {
+}
+
} // namespace content
diff --git a/chromium/content/browser/renderer_host/media/desktop_capture_device.h b/chromium/content/browser/media/capture/desktop_capture_device.h
index b418a298d63..9fb8ca419fa 100644
--- a/chromium/content/browser/renderer_host/media/desktop_capture_device.h
+++ b/chromium/content/browser/media/capture/desktop_capture_device.h
@@ -2,16 +2,19 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_DESKTOP_CAPTURE_DEVICE_H_
-#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_DESKTOP_CAPTURE_DEVICE_H_
+#ifndef CONTENT_BROWSER_MEDIA_CAPTURE_DESKTOP_CAPTURE_DEVICE_H_
+#define CONTENT_BROWSER_MEDIA_CAPTURE_DESKTOP_CAPTURE_DEVICE_H_
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "content/common/content_export.h"
+#include "content/public/browser/desktop_media_id.h"
#include "media/video/capture/video_capture_device.h"
+#include "ui/gfx/native_widget_types.h"
namespace base {
class SequencedTaskRunner;
+class Thread;
} // namespace base
namespace webrtc {
@@ -20,8 +23,6 @@ class DesktopCapturer;
namespace content {
-struct DesktopMediaID;
-
// DesktopCaptureDevice implements VideoCaptureDevice for screens and windows.
// It's essentially an adapter between webrtc::DesktopCapturer and
// VideoCaptureDevice.
@@ -33,8 +34,6 @@ class CONTENT_EXPORT DesktopCaptureDevice : public media::VideoCaptureDevice {
static scoped_ptr<media::VideoCaptureDevice> Create(
const DesktopMediaID& source);
- DesktopCaptureDevice(scoped_refptr<base::SequencedTaskRunner> task_runner,
- scoped_ptr<webrtc::DesktopCapturer> desktop_capturer);
virtual ~DesktopCaptureDevice();
// VideoCaptureDevice interface.
@@ -42,8 +41,19 @@ class CONTENT_EXPORT DesktopCaptureDevice : public media::VideoCaptureDevice {
scoped_ptr<Client> client) OVERRIDE;
virtual void StopAndDeAllocate() OVERRIDE;
+ // Set the platform-dependent window id for the notification window.
+ void SetNotificationWindowId(gfx::NativeViewId window_id);
+
private:
+ friend class DesktopCaptureDeviceTest;
class Core;
+
+ // Either |task_runner| or |thread| should be non-NULL, but not both.
+ DesktopCaptureDevice(scoped_refptr<base::SequencedTaskRunner> task_runner,
+ scoped_ptr<base::Thread> thread,
+ scoped_ptr<webrtc::DesktopCapturer> desktop_capturer,
+ DesktopMediaID::Type type);
+
scoped_refptr<Core> core_;
DISALLOW_COPY_AND_ASSIGN(DesktopCaptureDevice);
@@ -51,4 +61,4 @@ class CONTENT_EXPORT DesktopCaptureDevice : public media::VideoCaptureDevice {
} // namespace content
-#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_DESKTOP_CAPTURE_DEVICE_H_
+#endif // CONTENT_BROWSER_MEDIA_CAPTURE_DESKTOP_CAPTURE_DEVICE_H_
diff --git a/chromium/content/browser/renderer_host/media/desktop_capture_device_aura.cc b/chromium/content/browser/media/capture/desktop_capture_device_aura.cc
index a36d26b8b5f..08aec1eba2e 100644
--- a/chromium/content/browser/renderer_host/media/desktop_capture_device_aura.cc
+++ b/chromium/content/browser/media/capture/desktop_capture_device_aura.cc
@@ -2,24 +2,28 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "content/browser/renderer_host/media/desktop_capture_device_aura.h"
+#include "content/browser/media/capture/desktop_capture_device_aura.h"
#include "base/logging.h"
+#include "base/metrics/histogram.h"
#include "base/timer/timer.h"
#include "cc/output/copy_output_request.h"
#include "cc/output/copy_output_result.h"
-#include "content/browser/aura/image_transport_factory.h"
-#include "content/browser/renderer_host/media/video_capture_device_impl.h"
+#include "content/browser/compositor/image_transport_factory.h"
+#include "content/browser/media/capture/content_video_capture_device_core.h"
+#include "content/browser/media/capture/desktop_capture_device_uma_types.h"
#include "content/common/gpu/client/gl_helper.h"
#include "content/public/browser/browser_thread.h"
+#include "media/base/bind_to_current_loop.h"
#include "media/base/video_util.h"
#include "media/video/capture/video_capture_types.h"
#include "skia/ext/image_operations.h"
#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/aura/client/screen_position_client.h"
#include "ui/aura/env.h"
-#include "ui/aura/root_window.h"
#include "ui/aura/window.h"
#include "ui/aura/window_observer.h"
+#include "ui/aura/window_tree_host.h"
#include "ui/base/cursor/cursors_aura.h"
#include "ui/compositor/compositor.h"
#include "ui/compositor/dip_util.h"
@@ -95,15 +99,18 @@ class DesktopVideoCaptureMachine
virtual ~DesktopVideoCaptureMachine();
// VideoCaptureFrameSource overrides.
- virtual bool Start(
- const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy) OVERRIDE;
- virtual void Stop() OVERRIDE;
+ virtual bool Start(const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy,
+ const media::VideoCaptureParams& params) OVERRIDE;
+ virtual void Stop(const base::Closure& callback) OVERRIDE;
// Implements aura::WindowObserver.
virtual void OnWindowBoundsChanged(aura::Window* window,
const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds) OVERRIDE;
virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE;
+ virtual void OnWindowAddedToRootWindow(aura::Window* window) OVERRIDE;
+ virtual void OnWindowRemovingFromRootWindow(aura::Window* window,
+ aura::Window* new_root) OVERRIDE;
// Implements ui::CompositorObserver.
virtual void OnCompositingDidCommit(ui::Compositor* compositor) OVERRIDE {}
@@ -113,9 +120,6 @@ class DesktopVideoCaptureMachine
virtual void OnCompositingAborted(ui::Compositor* compositor) OVERRIDE {}
virtual void OnCompositingLockStateChanged(
ui::Compositor* compositor) OVERRIDE {}
- virtual void OnUpdateVSyncParameters(ui::Compositor* compositor,
- base::TimeTicks timebase,
- base::TimeDelta interval) OVERRIDE {}
private:
// Captures a frame.
@@ -128,7 +132,15 @@ class DesktopVideoCaptureMachine
// Response callback for cc::Layer::RequestCopyOfOutput().
void DidCopyOutput(
scoped_refptr<media::VideoFrame> video_frame,
- base::Time start_time,
+ base::TimeTicks start_time,
+ const ThreadSafeCaptureOracle::CaptureFrameCallback& capture_frame_cb,
+ scoped_ptr<cc::CopyOutputResult> result);
+
+ // A helper which does the real work for DidCopyOutput. Returns true if
+ // succeeded.
+ bool ProcessCopyOutputResponse(
+ scoped_refptr<media::VideoFrame> video_frame,
+ base::TimeTicks start_time,
const ThreadSafeCaptureOracle::CaptureFrameCallback& capture_frame_cb,
scoped_ptr<cc::CopyOutputResult> result);
@@ -143,9 +155,6 @@ class DesktopVideoCaptureMachine
// The window associated with the desktop.
aura::Window* desktop_window_;
- // The layer associated with the desktop.
- ui::Layer* desktop_layer_;
-
// The timer that kicks off period captures.
base::Timer timer_;
@@ -155,6 +164,9 @@ class DesktopVideoCaptureMachine
// Makes all the decisions about which frames to copy, and how.
scoped_refptr<ThreadSafeCaptureOracle> oracle_proxy_;
+ // The capture parameters for this capture.
+ media::VideoCaptureParams capture_params_;
+
// YUV readback pipeline.
scoped_ptr<content::ReadbackYUVInterface> yuv_readback_pipeline_;
@@ -169,27 +181,28 @@ class DesktopVideoCaptureMachine
DesktopVideoCaptureMachine::DesktopVideoCaptureMachine(
const DesktopMediaID& source)
: desktop_window_(NULL),
- desktop_layer_(NULL),
timer_(true, true),
window_id_(source) {}
DesktopVideoCaptureMachine::~DesktopVideoCaptureMachine() {}
bool DesktopVideoCaptureMachine::Start(
- const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy) {
+ const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy,
+ const media::VideoCaptureParams& params) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
desktop_window_ = content::DesktopMediaID::GetAuraWindowById(window_id_);
if (!desktop_window_)
return false;
- // If the desktop layer is already destroyed then return failure.
- desktop_layer_ = desktop_window_->layer();
- if (!desktop_layer_)
+ // If the associated layer is already destroyed then return failure.
+ ui::Layer* layer = desktop_window_->layer();
+ if (!layer)
return false;
DCHECK(oracle_proxy.get());
oracle_proxy_ = oracle_proxy;
+ capture_params_ = params;
// Update capture size.
UpdateCaptureSize();
@@ -198,11 +211,8 @@ bool DesktopVideoCaptureMachine::Start(
desktop_window_->AddObserver(this);
// Start observing compositor updates.
- ui::Compositor* compositor = desktop_layer_->GetCompositor();
- if (!compositor)
- return false;
-
- compositor->AddObserver(this);
+ if (desktop_window_->GetHost())
+ desktop_window_->GetHost()->compositor()->AddObserver(this);
// Starts timer.
timer_.Start(FROM_HERE, oracle_proxy_->capture_period(),
@@ -213,31 +223,31 @@ bool DesktopVideoCaptureMachine::Start(
return true;
}
-void DesktopVideoCaptureMachine::Stop() {
+void DesktopVideoCaptureMachine::Stop(const base::Closure& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- // Stop observing window events.
- if (desktop_window_)
+ // Stop observing compositor and window events.
+ if (desktop_window_) {
+ if (desktop_window_->GetHost())
+ desktop_window_->GetHost()->compositor()->RemoveObserver(this);
desktop_window_->RemoveObserver(this);
-
- // Stop observing compositor updates.
- if (desktop_layer_) {
- ui::Compositor* compositor = desktop_layer_->GetCompositor();
- if (compositor)
- compositor->RemoveObserver(this);
+ desktop_window_ = NULL;
}
// Stop timer.
timer_.Stop();
started_ = false;
+
+ callback.Run();
}
void DesktopVideoCaptureMachine::UpdateCaptureSize() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- if (oracle_proxy_ && desktop_layer_) {
+ if (oracle_proxy_ && desktop_window_) {
+ ui::Layer* layer = desktop_window_->layer();
oracle_proxy_->UpdateCaptureSize(ui::ConvertSizeToPixel(
- desktop_layer_, desktop_layer_->bounds().size()));
+ layer, layer->bounds().size()));
}
ClearCursorState();
}
@@ -245,14 +255,14 @@ void DesktopVideoCaptureMachine::UpdateCaptureSize() {
void DesktopVideoCaptureMachine::Capture(bool dirty) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- // Do not capture if the desktop layer is already destroyed.
- if (!desktop_layer_)
+ // Do not capture if the desktop window is already destroyed.
+ if (!desktop_window_)
return;
scoped_refptr<media::VideoFrame> frame;
ThreadSafeCaptureOracle::CaptureFrameCallback capture_frame_cb;
- const base::Time start_time = base::Time::Now();
+ const base::TimeTicks start_time = base::TimeTicks::Now();
const VideoCaptureOracle::Event event =
dirty ? VideoCaptureOracle::kCompositorUpdate
: VideoCaptureOracle::kTimerPoll;
@@ -267,12 +277,12 @@ void DesktopVideoCaptureMachine::Capture(bool dirty) {
gfx::Rect(desktop_window_->bounds().width(),
desktop_window_->bounds().height()));
request->set_area(window_rect);
- desktop_layer_->RequestCopyOfOutput(request.Pass());
+ desktop_window_->layer()->RequestCopyOfOutput(request.Pass());
}
}
void CopyOutputFinishedForVideo(
- base::Time start_time,
+ base::TimeTicks start_time,
const ThreadSafeCaptureOracle::CaptureFrameCallback& capture_frame_cb,
const scoped_refptr<media::VideoFrame>& target,
const SkBitmap& cursor_bitmap,
@@ -282,16 +292,81 @@ void CopyOutputFinishedForVideo(
if (!cursor_bitmap.isNull())
RenderCursorOnVideoFrame(target, cursor_bitmap, cursor_position);
release_callback->Run(0, false);
- capture_frame_cb.Run(start_time, result);
+ capture_frame_cb.Run(target, start_time, result);
+}
+
+void RunSingleReleaseCallback(scoped_ptr<cc::SingleReleaseCallback> cb,
+ const std::vector<uint32>& sync_points) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ GLHelper* gl_helper = ImageTransportFactory::GetInstance()->GetGLHelper();
+ DCHECK(gl_helper);
+ for (unsigned i = 0; i < sync_points.size(); i++)
+ gl_helper->WaitSyncPoint(sync_points[i]);
+ uint32 new_sync_point = gl_helper->InsertSyncPoint();
+ cb->Run(new_sync_point, false);
}
void DesktopVideoCaptureMachine::DidCopyOutput(
scoped_refptr<media::VideoFrame> video_frame,
- base::Time start_time,
+ base::TimeTicks start_time,
const ThreadSafeCaptureOracle::CaptureFrameCallback& capture_frame_cb,
scoped_ptr<cc::CopyOutputResult> result) {
- if (result->IsEmpty() || result->size().IsEmpty())
- return;
+ static bool first_call = true;
+
+ bool succeeded = ProcessCopyOutputResponse(
+ video_frame, start_time, capture_frame_cb, result.Pass());
+
+ base::TimeDelta capture_time = base::TimeTicks::Now() - start_time;
+ UMA_HISTOGRAM_TIMES(
+ window_id_.type == DesktopMediaID::TYPE_SCREEN ? kUmaScreenCaptureTime
+ : kUmaWindowCaptureTime,
+ capture_time);
+
+ if (first_call) {
+ first_call = false;
+ if (window_id_.type == DesktopMediaID::TYPE_SCREEN) {
+ IncrementDesktopCaptureCounter(succeeded ? FIRST_SCREEN_CAPTURE_SUCCEEDED
+ : FIRST_SCREEN_CAPTURE_FAILED);
+ } else {
+ IncrementDesktopCaptureCounter(succeeded
+ ? FIRST_WINDOW_CAPTURE_SUCCEEDED
+ : FIRST_WINDOW_CAPTURE_SUCCEEDED);
+ }
+ }
+}
+
+bool DesktopVideoCaptureMachine::ProcessCopyOutputResponse(
+ scoped_refptr<media::VideoFrame> video_frame,
+ base::TimeTicks start_time,
+ const ThreadSafeCaptureOracle::CaptureFrameCallback& capture_frame_cb,
+ scoped_ptr<cc::CopyOutputResult> result) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (result->IsEmpty() || result->size().IsEmpty() || !desktop_window_)
+ return false;
+
+ if (capture_params_.requested_format.pixel_format ==
+ media::PIXEL_FORMAT_TEXTURE) {
+ DCHECK(!video_frame);
+ cc::TextureMailbox texture_mailbox;
+ scoped_ptr<cc::SingleReleaseCallback> release_callback;
+ result->TakeTexture(&texture_mailbox, &release_callback);
+ DCHECK(texture_mailbox.IsTexture());
+ if (!texture_mailbox.IsTexture())
+ return false;
+ video_frame = media::VideoFrame::WrapNativeTexture(
+ make_scoped_ptr(new gpu::MailboxHolder(texture_mailbox.mailbox(),
+ texture_mailbox.target(),
+ texture_mailbox.sync_point())),
+ media::BindToCurrentLoop(base::Bind(&RunSingleReleaseCallback,
+ base::Passed(&release_callback))),
+ result->size(),
+ gfx::Rect(result->size()),
+ result->size(),
+ base::TimeDelta(),
+ media::VideoFrame::ReadPixelsCB());
+ capture_frame_cb.Run(video_frame, start_time, true);
+ return true;
+ }
// Compute the dest size we want after the letterboxing resize. Make the
// coordinates and sizes even because we letterbox in YUV space
@@ -307,19 +382,19 @@ void DesktopVideoCaptureMachine::DidCopyOutput(
region_in_frame.width() & ~1,
region_in_frame.height() & ~1);
if (region_in_frame.IsEmpty())
- return;
+ return false;
ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
GLHelper* gl_helper = factory->GetGLHelper();
if (!gl_helper)
- return;
+ return false;
cc::TextureMailbox texture_mailbox;
scoped_ptr<cc::SingleReleaseCallback> release_callback;
result->TakeTexture(&texture_mailbox, &release_callback);
DCHECK(texture_mailbox.IsTexture());
if (!texture_mailbox.IsTexture())
- return;
+ return false;
gfx::Rect result_rect(result->size());
if (!yuv_readback_pipeline_ ||
@@ -338,16 +413,24 @@ void DesktopVideoCaptureMachine::DidCopyOutput(
gfx::Point cursor_position_in_frame = UpdateCursorState(region_in_frame);
yuv_readback_pipeline_->ReadbackYUV(
- texture_mailbox.name(), texture_mailbox.sync_point(), video_frame.get(),
- base::Bind(&CopyOutputFinishedForVideo, start_time, capture_frame_cb,
- video_frame, scaled_cursor_bitmap_, cursor_position_in_frame,
+ texture_mailbox.mailbox(),
+ texture_mailbox.sync_point(),
+ video_frame.get(),
+ base::Bind(&CopyOutputFinishedForVideo,
+ start_time,
+ capture_frame_cb,
+ video_frame,
+ scaled_cursor_bitmap_,
+ cursor_position_in_frame,
base::Passed(&release_callback)));
+ return true;
}
gfx::Point DesktopVideoCaptureMachine::UpdateCursorState(
const gfx::Rect& region_in_frame) {
- const gfx::Rect desktop_bounds = desktop_layer_->bounds();
- gfx::NativeCursor cursor = desktop_window_->GetDispatcher()->last_cursor();
+ const gfx::Rect desktop_bounds = desktop_window_->layer()->bounds();
+ gfx::NativeCursor cursor =
+ desktop_window_->GetHost()->last_cursor();
if (last_cursor_ != cursor) {
SkBitmap cursor_bitmap;
if (ui::GetCursorBitmap(cursor, &cursor_bitmap, &cursor_hot_point_)) {
@@ -367,8 +450,10 @@ gfx::Point DesktopVideoCaptureMachine::UpdateCursorState(
}
gfx::Point cursor_position = aura::Env::GetInstance()->last_mouse_location();
+ aura::client::GetScreenPositionClient(desktop_window_->GetRootWindow())->
+ ConvertPointFromScreen(desktop_window_, &cursor_position);
const gfx::Point hot_point_in_dip = ui::ConvertPointToDIP(
- desktop_layer_, cursor_hot_point_);
+ desktop_window_->layer(), cursor_hot_point_);
cursor_position.Offset(-desktop_bounds.x() - hot_point_in_dip.x(),
-desktop_bounds.y() - hot_point_in_dip.y());
return gfx::Point(
@@ -396,13 +481,24 @@ void DesktopVideoCaptureMachine::OnWindowBoundsChanged(
}
void DesktopVideoCaptureMachine::OnWindowDestroyed(aura::Window* window) {
- DCHECK(desktop_window_ && window == desktop_window_);
- desktop_window_ = NULL;
- desktop_layer_ = NULL;
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- // Post task to stop capture on UI thread.
- BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
- &DesktopVideoCaptureMachine::Stop, AsWeakPtr()));
+ Stop(base::Bind(&base::DoNothing));
+
+ oracle_proxy_->ReportError("OnWindowDestroyed()");
+}
+
+void DesktopVideoCaptureMachine::OnWindowAddedToRootWindow(
+ aura::Window* window) {
+ DCHECK(window == desktop_window_);
+ window->GetHost()->compositor()->AddObserver(this);
+}
+
+void DesktopVideoCaptureMachine::OnWindowRemovingFromRootWindow(
+ aura::Window* window,
+ aura::Window* new_root) {
+ DCHECK(window == desktop_window_);
+ window->GetHost()->compositor()->RemoveObserver(this);
}
void DesktopVideoCaptureMachine::OnCompositingEnded(
@@ -415,7 +511,7 @@ void DesktopVideoCaptureMachine::OnCompositingEnded(
DesktopCaptureDeviceAura::DesktopCaptureDeviceAura(
const DesktopMediaID& source)
- : impl_(new VideoCaptureDeviceImpl(scoped_ptr<VideoCaptureMachine>(
+ : core_(new ContentVideoCaptureDeviceCore(scoped_ptr<VideoCaptureMachine>(
new DesktopVideoCaptureMachine(source)))) {}
DesktopCaptureDeviceAura::~DesktopCaptureDeviceAura() {
@@ -425,6 +521,9 @@ DesktopCaptureDeviceAura::~DesktopCaptureDeviceAura() {
// static
media::VideoCaptureDevice* DesktopCaptureDeviceAura::Create(
const DesktopMediaID& source) {
+ IncrementDesktopCaptureCounter(source.type == DesktopMediaID::TYPE_SCREEN
+ ? SCREEN_CAPTURER_CREATED
+ : WINDOW_CATPTURER_CREATED);
return new DesktopCaptureDeviceAura(source);
}
@@ -432,11 +531,11 @@ void DesktopCaptureDeviceAura::AllocateAndStart(
const media::VideoCaptureParams& params,
scoped_ptr<Client> client) {
DVLOG(1) << "Allocating " << params.requested_format.frame_size.ToString();
- impl_->AllocateAndStart(params, client.Pass());
+ core_->AllocateAndStart(params, client.Pass());
}
void DesktopCaptureDeviceAura::StopAndDeAllocate() {
- impl_->StopAndDeAllocate();
+ core_->StopAndDeAllocate();
}
} // namespace content
diff --git a/chromium/content/browser/renderer_host/media/desktop_capture_device_aura.h b/chromium/content/browser/media/capture/desktop_capture_device_aura.h
index faca53b9b6e..997b3ce3a00 100644
--- a/chromium/content/browser/renderer_host/media/desktop_capture_device_aura.h
+++ b/chromium/content/browser/media/capture/desktop_capture_device_aura.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_DESKTOP_CAPTURE_DEVICE_AURA_H_
-#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_DESKTOP_CAPTURE_DEVICE_AURA_H_
+#ifndef CONTENT_BROWSER_MEDIA_CAPTURE_DESKTOP_CAPTURE_DEVICE_AURA_H_
+#define CONTENT_BROWSER_MEDIA_CAPTURE_DESKTOP_CAPTURE_DEVICE_AURA_H_
#include <string>
@@ -18,7 +18,7 @@ class Window;
namespace content {
-class VideoCaptureDeviceImpl;
+class ContentVideoCaptureDeviceCore;
// An implementation of VideoCaptureDevice that mirrors an Aura window.
class CONTENT_EXPORT DesktopCaptureDeviceAura
@@ -37,7 +37,7 @@ class CONTENT_EXPORT DesktopCaptureDeviceAura
private:
DesktopCaptureDeviceAura(const DesktopMediaID& source);
- const scoped_ptr<class VideoCaptureDeviceImpl> impl_;
+ const scoped_ptr<class ContentVideoCaptureDeviceCore> core_;
DISALLOW_COPY_AND_ASSIGN(DesktopCaptureDeviceAura);
};
@@ -45,4 +45,4 @@ class CONTENT_EXPORT DesktopCaptureDeviceAura
} // namespace content
-#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_DESKTOP_CAPTURE_DEVICE_AURA_H_
+#endif // CONTENT_BROWSER_MEDIA_CAPTURE_DESKTOP_CAPTURE_DEVICE_AURA_H_
diff --git a/chromium/content/browser/renderer_host/media/desktop_capture_device_aura_unittest.cc b/chromium/content/browser/media/capture/desktop_capture_device_aura_unittest.cc
index 8ad1495a70c..3cfccf0de54 100644
--- a/chromium/content/browser/renderer_host/media/desktop_capture_device_aura_unittest.cc
+++ b/chromium/content/browser/media/capture/desktop_capture_device_aura_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "content/browser/renderer_host/media/desktop_capture_device_aura.h"
+#include "content/browser/media/capture/desktop_capture_device_aura.h"
#include "base/synchronization/waitable_event.h"
#include "content/browser/browser_thread_impl.h"
@@ -14,6 +14,8 @@
#include "ui/aura/test/aura_test_helper.h"
#include "ui/aura/test/test_window_delegate.h"
#include "ui/aura/window.h"
+#include "ui/compositor/test/context_factories_for_test.h"
+#include "ui/wm/core/default_activation_client.h"
using ::testing::_;
using ::testing::AnyNumber;
@@ -22,6 +24,10 @@ using ::testing::Expectation;
using ::testing::InvokeWithoutArgs;
using ::testing::SaveArg;
+namespace media {
+class VideoFrame;
+} // namespace media
+
namespace content {
namespace {
@@ -32,19 +38,18 @@ class MockDeviceClient : public media::VideoCaptureDevice::Client {
MOCK_METHOD2(ReserveOutputBuffer,
scoped_refptr<Buffer>(media::VideoFrame::Format format,
const gfx::Size& dimensions));
- MOCK_METHOD0(OnError, void());
- MOCK_METHOD5(OnIncomingCapturedFrame,
+ MOCK_METHOD1(OnError, void(const std::string& reason));
+ MOCK_METHOD5(OnIncomingCapturedData,
void(const uint8* data,
int length,
- base::Time timestamp,
+ const media::VideoCaptureFormat& frame_format,
int rotation,
- const media::VideoCaptureFormat& frame_format));
- MOCK_METHOD5(OnIncomingCapturedBuffer,
+ base::TimeTicks timestamp));
+ MOCK_METHOD4(OnIncomingCapturedVideoFrame,
void(const scoped_refptr<Buffer>& buffer,
- media::VideoFrame::Format format,
- const gfx::Size& dimensions,
- base::Time timestamp,
- int frame_rate));
+ const media::VideoCaptureFormat& buffer_format,
+ const scoped_refptr<media::VideoFrame>& frame,
+ base::TimeTicks timestamp));
};
// Test harness that sets up a minimal environment with necessary stubs.
@@ -56,8 +61,13 @@ class DesktopCaptureDeviceAuraTest : public testing::Test {
protected:
virtual void SetUp() OVERRIDE {
+ // The ContextFactory must exist before any Compositors are created.
+ bool enable_pixel_output = false;
+ ui::ContextFactory* context_factory =
+ ui::InitializeContextFactoryForTests(enable_pixel_output);
helper_.reset(new aura::test::AuraTestHelper(&message_loop_));
- helper_->SetUp();
+ helper_->SetUp(context_factory);
+ new wm::DefaultActivationClient(helper_->root_window());
// We need a window to cover desktop area so that DesktopCaptureDeviceAura
// can use gfx::NativeWindow::GetWindowAtScreenPoint() to locate the
@@ -65,7 +75,7 @@ class DesktopCaptureDeviceAuraTest : public testing::Test {
gfx::Rect desktop_bounds = root_window()->bounds();
window_delegate_.reset(new aura::test::TestWindowDelegate());
desktop_window_.reset(new aura::Window(window_delegate_.get()));
- desktop_window_->Init(ui::LAYER_TEXTURED);
+ desktop_window_->Init(aura::WINDOW_LAYER_TEXTURED);
desktop_window_->SetBounds(desktop_bounds);
aura::client::ParentWindowWithContext(
desktop_window_.get(), root_window(), desktop_bounds);
@@ -78,6 +88,7 @@ class DesktopCaptureDeviceAuraTest : public testing::Test {
desktop_window_.reset();
window_delegate_.reset();
helper_->TearDown();
+ ui::TerminateContextFactoryForTests();
}
aura::Window* root_window() { return helper_->root_window(); }
@@ -98,7 +109,7 @@ TEST_F(DesktopCaptureDeviceAuraTest, StartAndStop) {
content::DesktopMediaID::RegisterAuraWindow(root_window())));
scoped_ptr<MockDeviceClient> client(new MockDeviceClient());
- EXPECT_CALL(*client, OnError()).Times(0);
+ EXPECT_CALL(*client, OnError(_)).Times(0);
media::VideoCaptureParams capture_params;
capture_params.requested_format.frame_size.SetSize(640, 480);
diff --git a/chromium/content/browser/media/capture/desktop_capture_device_uma_types.cc b/chromium/content/browser/media/capture/desktop_capture_device_uma_types.cc
new file mode 100644
index 00000000000..c91641c5336
--- /dev/null
+++ b/chromium/content/browser/media/capture/desktop_capture_device_uma_types.cc
@@ -0,0 +1,20 @@
+// 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/media/capture/desktop_capture_device_uma_types.h"
+
+#include "base/metrics/histogram.h"
+
+namespace content {
+
+const char kUmaScreenCaptureTime[] = "WebRTC.ScreenCaptureTime";
+const char kUmaWindowCaptureTime[] = "WebRTC.WindowCaptureTime";
+
+void IncrementDesktopCaptureCounter(DesktopCaptureCounters counter) {
+ UMA_HISTOGRAM_ENUMERATION("WebRTC.DesktopCaptureCounters",
+ counter,
+ DESKTOP_CAPTURE_COUNTER_BOUNDARY);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/media/capture/desktop_capture_device_uma_types.h b/chromium/content/browser/media/capture/desktop_capture_device_uma_types.h
new file mode 100644
index 00000000000..0994fcd3150
--- /dev/null
+++ b/chromium/content/browser/media/capture/desktop_capture_device_uma_types.h
@@ -0,0 +1,30 @@
+// 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_MEDIA_CAPTURE_DESKTOP_CAPTURE_DEVICE_UMA_TYPES_H_
+#define CONTENT_BROWSER_MEDIA_CAPTURE_DESKTOP_CAPTURE_DEVICE_UMA_TYPES_H_
+
+namespace content {
+
+// This enum must be kept in-sync with DesktopCaptureCounters defined in
+// histograms.xml. New fields should be added right before
+// DESKTOP_CAPTURE_COUNTER_BOUNDARY.
+enum DesktopCaptureCounters {
+ SCREEN_CAPTURER_CREATED,
+ WINDOW_CATPTURER_CREATED,
+ FIRST_SCREEN_CAPTURE_SUCCEEDED,
+ FIRST_SCREEN_CAPTURE_FAILED,
+ FIRST_WINDOW_CAPTURE_SUCCEEDED,
+ FIRST_WINDOW_CAPTURE_FAILED,
+ DESKTOP_CAPTURE_COUNTER_BOUNDARY
+};
+
+extern const char kUmaScreenCaptureTime[];
+extern const char kUmaWindowCaptureTime[];
+
+void IncrementDesktopCaptureCounter(DesktopCaptureCounters counter);
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_MEDIA_CAPTURE_DESKTOP_CAPTURE_DEVICE_UMA_TYPES_H_
diff --git a/chromium/content/browser/renderer_host/media/desktop_capture_device_unittest.cc b/chromium/content/browser/media/capture/desktop_capture_device_unittest.cc
index 03dbd53059f..0c1a360e292 100644
--- a/chromium/content/browser/renderer_host/media/desktop_capture_device_unittest.cc
+++ b/chromium/content/browser/media/capture/desktop_capture_device_unittest.cc
@@ -2,13 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "content/browser/renderer_host/media/desktop_capture_device.h"
+#include "content/browser/media/capture/desktop_capture_device.h"
#include "base/basictypes.h"
#include "base/sequenced_task_runner.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/test_timeouts.h"
#include "base/threading/sequenced_worker_pool.h"
+#include "base/threading/thread.h"
#include "base/time/time.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -43,19 +44,18 @@ class MockDeviceClient : public media::VideoCaptureDevice::Client {
MOCK_METHOD2(ReserveOutputBuffer,
scoped_refptr<Buffer>(media::VideoFrame::Format format,
const gfx::Size& dimensions));
- MOCK_METHOD0(OnError, void());
- MOCK_METHOD5(OnIncomingCapturedFrame,
+ MOCK_METHOD1(OnError, void(const std::string& reason));
+ MOCK_METHOD5(OnIncomingCapturedData,
void(const uint8* data,
int length,
- base::Time timestamp,
+ const media::VideoCaptureFormat& frame_format,
int rotation,
- const media::VideoCaptureFormat& frame_format));
- MOCK_METHOD5(OnIncomingCapturedBuffer,
+ base::TimeTicks timestamp));
+ MOCK_METHOD4(OnIncomingCapturedVideoFrame,
void(const scoped_refptr<Buffer>& buffer,
- media::VideoFrame::Format format,
- const gfx::Size& dimensions,
- base::Time timestamp,
- int frame_rate));
+ const media::VideoCaptureFormat& buffer_format,
+ const scoped_refptr<media::VideoFrame>& frame,
+ base::TimeTicks timestamp));
};
// DesktopFrame wrapper that flips wrapped frame upside down by inverting
@@ -119,24 +119,42 @@ class FakeScreenCapturer : public webrtc::ScreenCapturer {
MouseShapeObserver* mouse_shape_observer) OVERRIDE {
}
+ virtual bool GetScreenList(ScreenList* screens) OVERRIDE {
+ return false;
+ }
+
+ virtual bool SelectScreen(webrtc::ScreenId id) OVERRIDE {
+ return false;
+ }
+
private:
Callback* callback_;
int frame_index_;
bool generate_inverted_frames_;
};
+} // namespace
+
class DesktopCaptureDeviceTest : public testing::Test {
public:
virtual void SetUp() OVERRIDE {
worker_pool_ = new base::SequencedWorkerPool(3, "TestCaptureThread");
}
+ void CreateScreenCaptureDevice(scoped_ptr<webrtc::DesktopCapturer> capturer) {
+ capture_device_.reset(new DesktopCaptureDevice(
+ worker_pool_->GetSequencedTaskRunner(worker_pool_->GetSequenceToken()),
+ thread_.Pass(),
+ capturer.Pass(),
+ DesktopMediaID::TYPE_SCREEN));
+ }
+
protected:
scoped_refptr<base::SequencedWorkerPool> worker_pool_;
+ scoped_ptr<base::Thread> thread_;
+ scoped_ptr<DesktopCaptureDevice> capture_device_;
};
-} // namespace
-
// There is currently no screen capturer implementation for ozone. So disable
// the test that uses a real screen-capturer instead of FakeScreenCapturer.
// http://crbug.com/260318
@@ -148,30 +166,28 @@ class DesktopCaptureDeviceTest : public testing::Test {
TEST_F(DesktopCaptureDeviceTest, MAYBE_Capture) {
scoped_ptr<webrtc::DesktopCapturer> capturer(
webrtc::ScreenCapturer::Create());
- DesktopCaptureDevice capture_device(
- worker_pool_->GetSequencedTaskRunner(worker_pool_->GetSequenceToken()),
- capturer.Pass());
+ CreateScreenCaptureDevice(capturer.Pass());
+
media::VideoCaptureFormat format;
base::WaitableEvent done_event(false, false);
int frame_size;
scoped_ptr<MockDeviceClient> client(new MockDeviceClient());
- EXPECT_CALL(*client, OnError()).Times(0);
- EXPECT_CALL(*client, OnIncomingCapturedFrame(_, _, _, _, _))
- .WillRepeatedly(
- DoAll(SaveArg<1>(&frame_size),
- SaveArg<4>(&format),
- InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal)));
+ EXPECT_CALL(*client, OnError(_)).Times(0);
+ EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _)).WillRepeatedly(
+ DoAll(SaveArg<1>(&frame_size),
+ SaveArg<2>(&format),
+ InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal)));
media::VideoCaptureParams capture_params;
capture_params.requested_format.frame_size.SetSize(640, 480);
capture_params.requested_format.frame_rate = kFrameRate;
capture_params.requested_format.pixel_format = media::PIXEL_FORMAT_I420;
capture_params.allow_resolution_change = false;
- capture_device.AllocateAndStart(
+ capture_device_->AllocateAndStart(
capture_params, client.PassAs<media::VideoCaptureDevice::Client>());
EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout()));
- capture_device.StopAndDeAllocate();
+ capture_device_->StopAndDeAllocate();
EXPECT_GT(format.frame_size.width(), 0);
EXPECT_GT(format.frame_size.height(), 0);
@@ -187,21 +203,18 @@ TEST_F(DesktopCaptureDeviceTest, MAYBE_Capture) {
TEST_F(DesktopCaptureDeviceTest, ScreenResolutionChangeConstantResolution) {
FakeScreenCapturer* mock_capturer = new FakeScreenCapturer();
- DesktopCaptureDevice capture_device(
- worker_pool_->GetSequencedTaskRunner(worker_pool_->GetSequenceToken()),
- scoped_ptr<webrtc::DesktopCapturer>(mock_capturer));
+ CreateScreenCaptureDevice(scoped_ptr<webrtc::DesktopCapturer>(mock_capturer));
media::VideoCaptureFormat format;
base::WaitableEvent done_event(false, false);
int frame_size;
scoped_ptr<MockDeviceClient> client(new MockDeviceClient());
- EXPECT_CALL(*client, OnError()).Times(0);
- EXPECT_CALL(*client, OnIncomingCapturedFrame(_, _, _, _, _))
- .WillRepeatedly(
- DoAll(SaveArg<1>(&frame_size),
- SaveArg<4>(&format),
- InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal)));
+ EXPECT_CALL(*client, OnError(_)).Times(0);
+ EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _)).WillRepeatedly(
+ DoAll(SaveArg<1>(&frame_size),
+ SaveArg<2>(&format),
+ InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal)));
media::VideoCaptureParams capture_params;
capture_params.requested_format.frame_size.SetSize(kTestFrameWidth1,
@@ -210,7 +223,7 @@ TEST_F(DesktopCaptureDeviceTest, ScreenResolutionChangeConstantResolution) {
capture_params.requested_format.pixel_format = media::PIXEL_FORMAT_I420;
capture_params.allow_resolution_change = false;
- capture_device.AllocateAndStart(
+ capture_device_->AllocateAndStart(
capture_params, client.PassAs<media::VideoCaptureDevice::Client>());
// Capture at least two frames, to ensure that the source frame size has
@@ -219,7 +232,7 @@ TEST_F(DesktopCaptureDeviceTest, ScreenResolutionChangeConstantResolution) {
done_event.Reset();
EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout()));
- capture_device.StopAndDeAllocate();
+ capture_device_->StopAndDeAllocate();
EXPECT_EQ(kTestFrameWidth1, format.frame_size.width());
EXPECT_EQ(kTestFrameHeight1, format.frame_size.height());
@@ -235,19 +248,16 @@ TEST_F(DesktopCaptureDeviceTest, ScreenResolutionChangeConstantResolution) {
TEST_F(DesktopCaptureDeviceTest, ScreenResolutionChangeVariableResolution) {
FakeScreenCapturer* mock_capturer = new FakeScreenCapturer();
- DesktopCaptureDevice capture_device(
- worker_pool_->GetSequencedTaskRunner(worker_pool_->GetSequenceToken()),
- scoped_ptr<webrtc::DesktopCapturer>(mock_capturer));
+ CreateScreenCaptureDevice(scoped_ptr<webrtc::DesktopCapturer>(mock_capturer));
media::VideoCaptureFormat format;
base::WaitableEvent done_event(false, false);
scoped_ptr<MockDeviceClient> client(new MockDeviceClient());
- EXPECT_CALL(*client, OnError()).Times(0);
- EXPECT_CALL(*client, OnIncomingCapturedFrame(_, _, _, _, _))
- .WillRepeatedly(
- DoAll(SaveArg<4>(&format),
- InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal)));
+ EXPECT_CALL(*client, OnError(_)).Times(0);
+ EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _)).WillRepeatedly(
+ DoAll(SaveArg<2>(&format),
+ InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal)));
media::VideoCaptureParams capture_params;
capture_params.requested_format.frame_size.SetSize(kTestFrameWidth2,
@@ -256,7 +266,7 @@ TEST_F(DesktopCaptureDeviceTest, ScreenResolutionChangeVariableResolution) {
capture_params.requested_format.pixel_format = media::PIXEL_FORMAT_I420;
capture_params.allow_resolution_change = false;
- capture_device.AllocateAndStart(
+ capture_device_->AllocateAndStart(
capture_params, client.PassAs<media::VideoCaptureDevice::Client>());
// Capture at least three frames, to ensure that the source frame size has
@@ -267,7 +277,7 @@ TEST_F(DesktopCaptureDeviceTest, ScreenResolutionChangeVariableResolution) {
done_event.Reset();
EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout()));
- capture_device.StopAndDeAllocate();
+ capture_device_->StopAndDeAllocate();
EXPECT_EQ(kTestFrameWidth1, format.frame_size.width());
EXPECT_EQ(kTestFrameHeight1, format.frame_size.height());
diff --git a/chromium/content/browser/renderer_host/media/video_capture_oracle.cc b/chromium/content/browser/media/capture/video_capture_oracle.cc
index fb41ec2fe4e..c41e5e7e0b1 100644
--- a/chromium/content/browser/renderer_host/media/video_capture_oracle.cc
+++ b/chromium/content/browser/media/capture/video_capture_oracle.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "content/browser/renderer_host/media/video_capture_oracle.h"
+#include "content/browser/media/capture/video_capture_oracle.h"
#include "base/debug/trace_event.h"
@@ -33,8 +33,8 @@ VideoCaptureOracle::VideoCaptureOracle(base::TimeDelta capture_period,
kNumRedundantCapturesOfStaticContent) {}
bool VideoCaptureOracle::ObserveEventAndDecideCapture(
- Event event,
- base::Time event_time) {
+ Event event,
+ base::TimeTicks event_time) {
// Record |event| and decide whether it's a good time to capture.
const bool content_is_dirty = (event == kCompositorUpdate ||
event == kSoftwarePaint);
@@ -54,7 +54,7 @@ int VideoCaptureOracle::RecordCapture() {
}
bool VideoCaptureOracle::CompleteCapture(int frame_number,
- base::Time timestamp) {
+ base::TimeTicks timestamp) {
// Drop frame if previous frame number is higher or we're trying to deliver
// a frame with the same timestamp.
if (last_delivered_frame_number_ > frame_number ||
@@ -88,7 +88,8 @@ SmoothEventSampler::SmoothEventSampler(base::TimeDelta capture_period,
DCHECK_GT(capture_period_.InMicroseconds(), 0);
}
-bool SmoothEventSampler::AddEventAndConsiderSampling(base::Time event_time) {
+bool SmoothEventSampler::AddEventAndConsiderSampling(
+ base::TimeTicks event_time) {
DCHECK(!event_time.is_null());
// Add tokens to the bucket based on advancement in time. Then, re-bound the
@@ -108,7 +109,8 @@ bool SmoothEventSampler::AddEventAndConsiderSampling(base::Time event_time) {
if (token_bucket_ < base::TimeDelta())
token_bucket_ = base::TimeDelta();
TRACE_COUNTER1("mirroring",
- "MirroringTokenBucketUsec", token_bucket_.InMicroseconds());
+ "MirroringTokenBucketUsec",
+ std::max<int64>(0, token_bucket_.InMicroseconds()));
}
current_event_ = event_time;
@@ -119,7 +121,8 @@ bool SmoothEventSampler::AddEventAndConsiderSampling(base::Time event_time) {
void SmoothEventSampler::RecordSample() {
token_bucket_ -= capture_period_;
TRACE_COUNTER1("mirroring",
- "MirroringTokenBucketUsec", token_bucket_.InMicroseconds());
+ "MirroringTokenBucketUsec",
+ std::max<int64>(0, token_bucket_.InMicroseconds()));
bool was_paused = overdue_sample_count_ == redundant_capture_goal_;
if (HasUnrecordedEvent()) {
@@ -137,7 +140,8 @@ void SmoothEventSampler::RecordSample() {
<< "Content changed; capture will resume.";
}
-bool SmoothEventSampler::IsOverdueForSamplingAt(base::Time event_time) const {
+bool SmoothEventSampler::IsOverdueForSamplingAt(base::TimeTicks event_time)
+ const {
DCHECK(!event_time.is_null());
// If we don't get events on compositor updates on this platform, then we
@@ -149,6 +153,9 @@ bool SmoothEventSampler::IsOverdueForSamplingAt(base::Time event_time) const {
}
}
+ if (last_sample_.is_null())
+ return true;
+
// If we're dirty but not yet old, then we've recently gotten updates, so we
// won't request a sample just yet.
base::TimeDelta dirty_interval = event_time - last_sample_;
diff --git a/chromium/content/browser/renderer_host/media/video_capture_oracle.h b/chromium/content/browser/media/capture/video_capture_oracle.h
index 739b56971a5..6242b6a29a8 100644
--- a/chromium/content/browser/renderer_host/media/video_capture_oracle.h
+++ b/chromium/content/browser/media/capture/video_capture_oracle.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_ORACLE_H_
-#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_ORACLE_H_
+#ifndef CONTENT_BROWSER_MEDIA_CAPTURE_VIDEO_CAPTURE_ORACLE_H_
+#define CONTENT_BROWSER_MEDIA_CAPTURE_VIDEO_CAPTURE_ORACLE_H_
#include "base/callback_forward.h"
#include "base/memory/scoped_ptr.h"
@@ -22,7 +22,7 @@ class CONTENT_EXPORT SmoothEventSampler {
// Add a new event to the event history, and return whether it ought to be
// sampled based on the desired |capture_period|. The event is not recorded as
// a sample until RecordSample() is called.
- bool AddEventAndConsiderSampling(base::Time event_time);
+ bool AddEventAndConsiderSampling(base::TimeTicks event_time);
// Operates on the last event added by AddEventAndConsiderSampling(), marking
// it as sampled. After this point we are current in the stream of events, as
@@ -31,7 +31,7 @@ class CONTENT_EXPORT SmoothEventSampler {
// Returns true if, at time |event_time|, sampling should occur because too
// much time will have passed relative to the last event and/or sample.
- bool IsOverdueForSamplingAt(base::Time event_time) const;
+ bool IsOverdueForSamplingAt(base::TimeTicks event_time) const;
// Returns true if AddEventAndConsiderSampling() has been called since the
// last call to RecordSample().
@@ -43,8 +43,8 @@ class CONTENT_EXPORT SmoothEventSampler {
const int redundant_capture_goal_;
const base::TimeDelta token_bucket_capacity_;
- base::Time current_event_;
- base::Time last_sample_;
+ base::TimeTicks current_event_;
+ base::TimeTicks last_sample_;
int overdue_sample_count_;
base::TimeDelta token_bucket_;
@@ -70,9 +70,7 @@ class CONTENT_EXPORT VideoCaptureOracle {
// Record an event of type |event|, and decide whether the caller should do a
// frame capture immediately. Decisions of the oracle are final: the caller
// must do what it is told.
- bool ObserveEventAndDecideCapture(
- Event event,
- base::Time event_time);
+ bool ObserveEventAndDecideCapture(Event event, base::TimeTicks event_time);
// Record the start of a capture. Returns a frame_number to be used with
// CompleteCapture().
@@ -80,7 +78,7 @@ class CONTENT_EXPORT VideoCaptureOracle {
// Record the completion of a capture. Returns true iff the captured frame
// should be delivered.
- bool CompleteCapture(int frame_number, base::Time timestamp);
+ bool CompleteCapture(int frame_number, base::TimeTicks timestamp);
base::TimeDelta capture_period() const { return capture_period_; }
@@ -96,7 +94,7 @@ class CONTENT_EXPORT VideoCaptureOracle {
int last_delivered_frame_number_;
// Stores the timestamp of the last delivered frame.
- base::Time last_delivered_frame_timestamp_;
+ base::TimeTicks last_delivered_frame_timestamp_;
// Tracks present/paint history.
SmoothEventSampler sampler_;
@@ -104,4 +102,4 @@ class CONTENT_EXPORT VideoCaptureOracle {
} // namespace content
-#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_ORACLE_H_
+#endif // CONTENT_BROWSER_MEDIA_CAPTURE_VIDEO_CAPTURE_ORACLE_H_
diff --git a/chromium/content/browser/renderer_host/media/video_capture_oracle_unittest.cc b/chromium/content/browser/media/capture/video_capture_oracle_unittest.cc
index 40c1826d957..dff8e97de7f 100644
--- a/chromium/content/browser/renderer_host/media/video_capture_oracle_unittest.cc
+++ b/chromium/content/browser/media/capture/video_capture_oracle_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "content/browser/renderer_host/media/video_capture_oracle.h"
+#include "content/browser/media/capture/video_capture_oracle.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
@@ -12,7 +12,8 @@ namespace content {
namespace {
void SteadyStateSampleAndAdvance(base::TimeDelta vsync,
- SmoothEventSampler* sampler, base::Time* t) {
+ SmoothEventSampler* sampler,
+ base::TimeTicks* t) {
ASSERT_TRUE(sampler->AddEventAndConsiderSampling(*t));
ASSERT_TRUE(sampler->HasUnrecordedEvent());
sampler->RecordSample();
@@ -23,7 +24,8 @@ void SteadyStateSampleAndAdvance(base::TimeDelta vsync,
}
void SteadyStateNoSampleAndAdvance(base::TimeDelta vsync,
- SmoothEventSampler* sampler, base::Time* t) {
+ SmoothEventSampler* sampler,
+ base::TimeTicks* t) {
ASSERT_FALSE(sampler->AddEventAndConsiderSampling(*t));
ASSERT_TRUE(sampler->HasUnrecordedEvent());
ASSERT_FALSE(sampler->IsOverdueForSamplingAt(*t));
@@ -31,9 +33,16 @@ void SteadyStateNoSampleAndAdvance(base::TimeDelta vsync,
ASSERT_FALSE(sampler->IsOverdueForSamplingAt(*t));
}
+void TimeTicksFromString(const char* string, base::TimeTicks* t) {
+ base::Time time;
+ ASSERT_TRUE(base::Time::FromString(string, &time));
+ *t = base::TimeTicks::UnixEpoch() + (time - base::Time::UnixEpoch());
+}
+
void TestRedundantCaptureStrategy(base::TimeDelta capture_period,
int redundant_capture_goal,
- SmoothEventSampler* sampler, base::Time* t) {
+ SmoothEventSampler* sampler,
+ base::TimeTicks* t) {
// Before any events have been considered, we're overdue for sampling.
ASSERT_TRUE(sampler->IsOverdueForSamplingAt(*t));
@@ -68,8 +77,8 @@ TEST(SmoothEventSamplerTest, Sample60HertzAt30Hertz) {
const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 60;
SmoothEventSampler sampler(capture_period, true, redundant_capture_goal);
- base::Time t;
- ASSERT_TRUE(base::Time::FromString("Sat, 23 Mar 2013 1:21:08 GMT", &t));
+ base::TimeTicks t;
+ TimeTicksFromString("Sat, 23 Mar 2013 1:21:08 GMT", &t);
TestRedundantCaptureStrategy(capture_period, redundant_capture_goal,
&sampler, &t);
@@ -108,8 +117,8 @@ TEST(SmoothEventSamplerTest, Sample50HertzAt30Hertz) {
const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 50;
SmoothEventSampler sampler(capture_period, true, redundant_capture_goal);
- base::Time t;
- ASSERT_TRUE(base::Time::FromString("Sat, 23 Mar 2013 1:21:08 GMT", &t));
+ base::TimeTicks t;
+ TimeTicksFromString("Sat, 23 Mar 2013 1:21:08 GMT", &t);
TestRedundantCaptureStrategy(capture_period, redundant_capture_goal,
&sampler, &t);
@@ -154,8 +163,8 @@ TEST(SmoothEventSamplerTest, Sample75HertzAt30Hertz) {
const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 75;
SmoothEventSampler sampler(capture_period, true, redundant_capture_goal);
- base::Time t;
- ASSERT_TRUE(base::Time::FromString("Sat, 23 Mar 2013 1:21:08 GMT", &t));
+ base::TimeTicks t;
+ TimeTicksFromString("Sat, 23 Mar 2013 1:21:08 GMT", &t);
TestRedundantCaptureStrategy(capture_period, redundant_capture_goal,
&sampler, &t);
@@ -204,8 +213,8 @@ TEST(SmoothEventSamplerTest, Sample30HertzAt30Hertz) {
const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 30;
SmoothEventSampler sampler(capture_period, true, redundant_capture_goal);
- base::Time t;
- ASSERT_TRUE(base::Time::FromString("Sat, 23 Mar 2013 1:21:08 GMT", &t));
+ base::TimeTicks t;
+ TimeTicksFromString("Sat, 23 Mar 2013 1:21:08 GMT", &t);
TestRedundantCaptureStrategy(capture_period, redundant_capture_goal,
&sampler, &t);
@@ -240,8 +249,8 @@ TEST(SmoothEventSamplerTest, Sample24HertzAt30Hertz) {
const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 24;
SmoothEventSampler sampler(capture_period, true, redundant_capture_goal);
- base::Time t;
- ASSERT_TRUE(base::Time::FromString("Sat, 23 Mar 2013 1:21:08 GMT", &t));
+ base::TimeTicks t;
+ TimeTicksFromString("Sat, 23 Mar 2013 1:21:08 GMT", &t);
TestRedundantCaptureStrategy(capture_period, redundant_capture_goal,
&sampler, &t);
@@ -274,8 +283,8 @@ TEST(SmoothEventSamplerTest, DoubleDrawAtOneTimeStillDirties) {
const base::TimeDelta overdue_period = base::TimeDelta::FromSeconds(1);
SmoothEventSampler sampler(capture_period, true, 1);
- base::Time t;
- ASSERT_TRUE(base::Time::FromString("Sat, 23 Mar 2013 1:21:08 GMT", &t));
+ base::TimeTicks t;
+ TimeTicksFromString("Sat, 23 Mar 2013 1:21:08 GMT", &t);
ASSERT_TRUE(sampler.AddEventAndConsiderSampling(t));
sampler.RecordSample();
@@ -299,8 +308,8 @@ TEST(SmoothEventSamplerTest, FallbackToPollingIfUpdatesUnreliable) {
SmoothEventSampler should_not_poll(timer_interval, true, 1);
SmoothEventSampler should_poll(timer_interval, false, 1);
- base::Time t;
- ASSERT_TRUE(base::Time::FromString("Sat, 23 Mar 2013 1:21:08 GMT", &t));
+ base::TimeTicks t;
+ TimeTicksFromString("Sat, 23 Mar 2013 1:21:08 GMT", &t);
// Do one round of the "happy case" where an event was received and
// RecordSample() was called by the client.
@@ -355,8 +364,8 @@ struct DataPoint {
void ReplayCheckingSamplerDecisions(const DataPoint* data_points,
size_t num_data_points,
SmoothEventSampler* sampler) {
- base::Time t;
- ASSERT_TRUE(base::Time::FromString("Sat, 23 Mar 2013 1:21:08 GMT", &t));
+ base::TimeTicks t;
+ TimeTicksFromString("Sat, 23 Mar 2013 1:21:08 GMT", &t);
for (size_t i = 0; i < num_data_points; ++i) {
t += base::TimeDelta::FromMicroseconds(
static_cast<int64>(data_points[i].increment_ms * 1000));
diff --git a/chromium/content/browser/renderer_host/media/web_contents_audio_input_stream.cc b/chromium/content/browser/media/capture/web_contents_audio_input_stream.cc
index 8336eb2c195..ad5a33f6923 100644
--- a/chromium/content/browser/renderer_host/media/web_contents_audio_input_stream.cc
+++ b/chromium/content/browser/media/capture/web_contents_audio_input_stream.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "content/browser/renderer_host/media/web_contents_audio_input_stream.h"
+#include "content/browser/media/capture/web_contents_audio_input_stream.h"
#include <string>
@@ -11,9 +11,9 @@
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/threading/thread_checker.h"
-#include "content/browser/renderer_host/media/audio_mirroring_manager.h"
-#include "content/browser/renderer_host/media/web_contents_capture_util.h"
-#include "content/browser/renderer_host/media/web_contents_tracker.h"
+#include "content/browser/media/capture/audio_mirroring_manager.h"
+#include "content/browser/media/capture/web_contents_capture_util.h"
+#include "content/browser/media/capture/web_contents_tracker.h"
#include "content/public/browser/browser_thread.h"
#include "media/audio/virtual_audio_input_stream.h"
#include "media/audio/virtual_audio_output_stream.h"
@@ -281,7 +281,7 @@ void WebContentsAudioInputStream::Impl::OnTargetChanged(int render_process_id,
WebContentsAudioInputStream* WebContentsAudioInputStream::Create(
const std::string& device_id,
const media::AudioParameters& params,
- const scoped_refptr<base::MessageLoopProxy>& worker_loop,
+ const scoped_refptr<base::SingleThreadTaskRunner>& worker_task_runner,
AudioMirroringManager* audio_mirroring_manager) {
int render_process_id;
int render_view_id;
@@ -295,7 +295,7 @@ WebContentsAudioInputStream* WebContentsAudioInputStream::Create(
audio_mirroring_manager,
new WebContentsTracker(),
new media::VirtualAudioInputStream(
- params, worker_loop,
+ params, worker_task_runner,
media::VirtualAudioInputStream::AfterCloseCallback()));
}
diff --git a/chromium/content/browser/renderer_host/media/web_contents_audio_input_stream.h b/chromium/content/browser/media/capture/web_contents_audio_input_stream.h
index 486547ecaa2..c0a2c6afd7f 100644
--- a/chromium/content/browser/renderer_host/media/web_contents_audio_input_stream.h
+++ b/chromium/content/browser/media/capture/web_contents_audio_input_stream.h
@@ -13,8 +13,8 @@
// navigation, crash/reload, etc. events; and take appropriate actions to
// provide a seamless, uninterrupted mirroring experience.
-#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_WEB_CONTENTS_AUDIO_INPUT_STREAM_H_
-#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_WEB_CONTENTS_AUDIO_INPUT_STREAM_H_
+#ifndef CONTENT_BROWSER_MEDIA_CAPTURE_WEB_CONTENTS_AUDIO_INPUT_STREAM_H_
+#define CONTENT_BROWSER_MEDIA_CAPTURE_WEB_CONTENTS_AUDIO_INPUT_STREAM_H_
#include <string>
@@ -23,7 +23,7 @@
#include "media/audio/audio_io.h"
namespace base {
-class MessageLoopProxy;
+class SingleThreadTaskRunner;
}
namespace media {
@@ -55,13 +55,13 @@ class CONTENT_EXPORT WebContentsAudioInputStream
// WebContentsCaptureUtil::ExtractTabCaptureTarget(). The caller must
// guarantee Close() is called on the returned object so that it may
// self-destruct.
- // |worker_loop| is the loop on which AudioInputCallback methods are called
- // and may or may not be the single thread that invokes the AudioInputStream
- // methods.
+ // |worker_task_runner| is the task runner on which AudioInputCallback methods
+ // are called and may or may not be the single thread that invokes the
+ // AudioInputStream methods.
static WebContentsAudioInputStream* Create(
const std::string& device_id,
const media::AudioParameters& params,
- const scoped_refptr<base::MessageLoopProxy>& worker_loop,
+ const scoped_refptr<base::SingleThreadTaskRunner>& worker_task_runner,
AudioMirroringManager* audio_mirroring_manager);
private:
@@ -89,4 +89,4 @@ class CONTENT_EXPORT WebContentsAudioInputStream
} // namespace content
-#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_WEB_CONTENTS_AUDIO_INPUT_STREAM_H_
+#endif // CONTENT_BROWSER_MEDIA_CAPTURE_WEB_CONTENTS_AUDIO_INPUT_STREAM_H_
diff --git a/chromium/content/browser/renderer_host/media/web_contents_audio_input_stream_unittest.cc b/chromium/content/browser/media/capture/web_contents_audio_input_stream_unittest.cc
index 3ce336d39c9..72f6109029d 100644
--- a/chromium/content/browser/renderer_host/media/web_contents_audio_input_stream_unittest.cc
+++ b/chromium/content/browser/media/capture/web_contents_audio_input_stream_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "content/browser/renderer_host/media/web_contents_audio_input_stream.h"
+#include "content/browser/media/capture/web_contents_audio_input_stream.h"
#include <list>
@@ -12,8 +12,8 @@
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
#include "content/browser/browser_thread_impl.h"
-#include "content/browser/renderer_host/media/audio_mirroring_manager.h"
-#include "content/browser/renderer_host/media/web_contents_tracker.h"
+#include "content/browser/media/capture/audio_mirroring_manager.h"
+#include "content/browser/media/capture/web_contents_tracker.h"
#include "media/audio/simple_sources.h"
#include "media/audio/virtual_audio_input_stream.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -161,10 +161,11 @@ class MockAudioInputCallback : public AudioInputStream::AudioInputCallback {
public:
MockAudioInputCallback() {}
- MOCK_METHOD5(OnData, void(AudioInputStream* stream, const uint8* src,
- uint32 size, uint32 hardware_delay_bytes,
- double volume));
- MOCK_METHOD1(OnClose, void(AudioInputStream* stream));
+ MOCK_METHOD4(OnData,
+ void(AudioInputStream* stream,
+ const media::AudioBus* src,
+ uint32 hardware_delay_bytes,
+ double volume));
MOCK_METHOD1(OnError, void(AudioInputStream* stream));
private:
@@ -239,10 +240,9 @@ class WebContentsAudioInputStreamTest : public testing::Test {
static_cast<AudioMirroringManager::MirroringDestination*>(NULL)))
.RetiresOnSaturation();
- EXPECT_CALL(mock_input_callback_, OnData(NotNull(), NotNull(), _, _, _))
+ EXPECT_CALL(mock_input_callback_, OnData(NotNull(), NotNull(), _, _))
.WillRepeatedly(
InvokeWithoutArgs(&on_data_event_, &base::WaitableEvent::Signal));
- EXPECT_CALL(mock_input_callback_, OnClose(_)); // At Stop() time.
wcais_->Start(&mock_input_callback_);
diff --git a/chromium/content/browser/renderer_host/media/web_contents_capture_util.cc b/chromium/content/browser/media/capture/web_contents_capture_util.cc
index 28b0fa6c8dc..57b636a8960 100644
--- a/chromium/content/browser/renderer_host/media/web_contents_capture_util.cc
+++ b/chromium/content/browser/media/capture/web_contents_capture_util.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "content/browser/renderer_host/media/web_contents_capture_util.h"
+#include "content/browser/media/capture/web_contents_capture_util.h"
#include "base/basictypes.h"
#include "base/strings/string_number_conversions.h"
diff --git a/chromium/content/browser/renderer_host/media/web_contents_capture_util.h b/chromium/content/browser/media/capture/web_contents_capture_util.h
index 8f376afdeec..5f43f423b8b 100644
--- a/chromium/content/browser/renderer_host/media/web_contents_capture_util.h
+++ b/chromium/content/browser/media/capture/web_contents_capture_util.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_WEB_CONTENTS_CAPTURE_UTIL_H_
-#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_WEB_CONTENTS_CAPTURE_UTIL_H_
+#ifndef CONTENT_BROWSER_MEDIA_CAPTURE_WEB_CONTENTS_CAPTURE_UTIL_H_
+#define CONTENT_BROWSER_MEDIA_CAPTURE_WEB_CONTENTS_CAPTURE_UTIL_H_
#include <string>
@@ -32,4 +32,4 @@ class CONTENT_EXPORT WebContentsCaptureUtil {
} // namespace content
-#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_WEB_CONTENTS_CAPTURE_UTIL_H_
+#endif // CONTENT_BROWSER_MEDIA_CAPTURE_WEB_CONTENTS_CAPTURE_UTIL_H_
diff --git a/chromium/content/browser/renderer_host/media/web_contents_tracker.cc b/chromium/content/browser/media/capture/web_contents_tracker.cc
index 3a75080cb46..07653310128 100644
--- a/chromium/content/browser/renderer_host/media/web_contents_tracker.cc
+++ b/chromium/content/browser/media/capture/web_contents_tracker.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "content/browser/renderer_host/media/web_contents_tracker.h"
+#include "content/browser/media/capture/web_contents_tracker.h"
#include "base/message_loop/message_loop_proxy.h"
#include "content/public/browser/browser_thread.h"
@@ -95,7 +95,7 @@ void WebContentsTracker::DidNavigateMainFrame(
OnWebContentsChangeEvent();
}
-void WebContentsTracker::WebContentsDestroyed(WebContents* web_contents) {
+void WebContentsTracker::WebContentsDestroyed() {
OnWebContentsChangeEvent();
}
diff --git a/chromium/content/browser/renderer_host/media/web_contents_tracker.h b/chromium/content/browser/media/capture/web_contents_tracker.h
index 632ced28be7..f8957f3f839 100644
--- a/chromium/content/browser/renderer_host/media/web_contents_tracker.h
+++ b/chromium/content/browser/media/capture/web_contents_tracker.h
@@ -12,8 +12,8 @@
// same thread. This can be any thread, and the decision is locked-in by
// WebContentsTracker when Start() is called.
-#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_WEB_CONTENTS_TRACKER_H_
-#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_WEB_CONTENTS_TRACKER_H_
+#ifndef CONTENT_BROWSER_MEDIA_CAPTURE_WEB_CONTENTS_TRACKER_H_
+#define CONTENT_BROWSER_MEDIA_CAPTURE_WEB_CONTENTS_TRACKER_H_
#include "base/callback.h"
#include "base/memory/ref_counted.h"
@@ -73,7 +73,7 @@ class CONTENT_EXPORT WebContentsTracker
OVERRIDE;
virtual void DidNavigateMainFrame(const LoadCommittedDetails& details,
const FrameNavigateParams& params) OVERRIDE;
- virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE;
+ virtual void WebContentsDestroyed() OVERRIDE;
scoped_refptr<base::MessageLoopProxy> message_loop_;
ChangeCallback callback_;
@@ -83,4 +83,4 @@ class CONTENT_EXPORT WebContentsTracker
} // namespace content
-#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_WEB_CONTENTS_TRACKER_H_
+#endif // CONTENT_BROWSER_MEDIA_CAPTURE_WEB_CONTENTS_TRACKER_H_
diff --git a/chromium/content/browser/renderer_host/media/web_contents_video_capture_device.cc b/chromium/content/browser/media/capture/web_contents_video_capture_device.cc
index 15258a04523..8a8474c2782 100644
--- a/chromium/content/browser/renderer_host/media/web_contents_video_capture_device.cc
+++ b/chromium/content/browser/media/capture/web_contents_video_capture_device.cc
@@ -48,31 +48,32 @@
// Turning on verbose logging will cause the effective frame rate to be logged
// at 5-second intervals.
-#include "content/browser/renderer_host/media/web_contents_video_capture_device.h"
+#include "content/browser/media/capture/web_contents_video_capture_device.h"
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/metrics/histogram.h"
#include "base/sequenced_task_runner.h"
#include "base/threading/thread.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
-#include "content/browser/renderer_host/media/video_capture_device_impl.h"
-#include "content/browser/renderer_host/media/video_capture_oracle.h"
-#include "content/browser/renderer_host/media/web_contents_capture_util.h"
+#include "content/browser/media/capture/content_video_capture_device_core.h"
+#include "content/browser/media/capture/video_capture_oracle.h"
+#include "content/browser/media/capture/web_contents_capture_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/browser/web_contents/web_contents_impl.h"
-#include "content/port/browser/render_widget_host_view_frame_subscriber.h"
-#include "content/port/browser/render_widget_host_view_port.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host_view.h"
+#include "content/public/browser/render_widget_host_view_frame_subscriber.h"
#include "content/public/browser/web_contents_observer.h"
#include "media/base/video_util.h"
#include "media/video/capture/video_capture_types.h"
@@ -99,26 +100,44 @@ gfx::Rect ComputeYV12LetterboxRegion(const gfx::Size& frame_size,
return result;
}
-// Wrapper function to invoke ThreadSafeCaptureOracle::CaptureFrameCallback, is
-// compatible with RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback.
-void InvokeCaptureFrameCallback(
- const ThreadSafeCaptureOracle::CaptureFrameCallback& capture_frame_cb,
- base::Time timestamp,
- bool frame_captured) {
- capture_frame_cb.Run(timestamp, frame_captured);
+void DeleteOnWorkerThread(scoped_ptr<base::Thread> render_thread,
+ const base::Closure& callback) {
+ render_thread.reset();
+
+ // After thread join call the callback on UI thread.
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback);
}
+// Responsible for logging the effective frame rate.
+class VideoFrameDeliveryLog {
+ public:
+ VideoFrameDeliveryLog();
+
+ // Report that the frame posted with |frame_time| has been delivered.
+ void ChronicleFrameDelivery(base::TimeTicks frame_time);
+
+ private:
+ // The following keep track of and log the effective frame rate whenever
+ // verbose logging is turned on.
+ base::TimeTicks last_frame_rate_log_time_;
+ int count_frames_rendered_;
+
+ DISALLOW_COPY_AND_ASSIGN(VideoFrameDeliveryLog);
+};
+
// FrameSubscriber is a proxy to the ThreadSafeCaptureOracle that's compatible
// with RenderWidgetHostViewFrameSubscriber. We create one per event type.
class FrameSubscriber : public RenderWidgetHostViewFrameSubscriber {
public:
FrameSubscriber(VideoCaptureOracle::Event event_type,
- const scoped_refptr<ThreadSafeCaptureOracle>& oracle)
+ const scoped_refptr<ThreadSafeCaptureOracle>& oracle,
+ VideoFrameDeliveryLog* delivery_log)
: event_type_(event_type),
- oracle_proxy_(oracle) {}
+ oracle_proxy_(oracle),
+ delivery_log_(delivery_log) {}
virtual bool ShouldCaptureFrame(
- base::Time present_time,
+ base::TimeTicks present_time,
scoped_refptr<media::VideoFrame>* storage,
RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback*
deliver_frame_cb) OVERRIDE;
@@ -126,6 +145,7 @@ class FrameSubscriber : public RenderWidgetHostViewFrameSubscriber {
private:
const VideoCaptureOracle::Event event_type_;
scoped_refptr<ThreadSafeCaptureOracle> oracle_proxy_;
+ VideoFrameDeliveryLog* const delivery_log_;
};
// ContentCaptureSubscription is the relationship between a RenderWidgetHost
@@ -145,11 +165,11 @@ class FrameSubscriber : public RenderWidgetHostViewFrameSubscriber {
// autonomously on some other thread.
class ContentCaptureSubscription : public content::NotificationObserver {
public:
- typedef base::Callback<void(
- const base::Time&,
- const scoped_refptr<media::VideoFrame>&,
- const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&)>
- CaptureCallback;
+ typedef base::Callback<
+ void(const base::TimeTicks&,
+ const scoped_refptr<media::VideoFrame>&,
+ const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&)>
+ CaptureCallback;
// Create a subscription. Whenever a manual capture is required, the
// subscription will invoke |capture_callback| on the UI thread to do the
@@ -171,6 +191,7 @@ class ContentCaptureSubscription : public content::NotificationObserver {
const int render_process_id_;
const int render_view_id_;
+ VideoFrameDeliveryLog delivery_log_;
FrameSubscriber paint_subscriber_;
FrameSubscriber timer_subscriber_;
content::NotificationRegistrar registrar_;
@@ -202,27 +223,25 @@ void RenderVideoFrame(const SkBitmap& input,
// this seems disadvantageous.
class WebContentsCaptureMachine
: public VideoCaptureMachine,
- public WebContentsObserver,
- public base::SupportsWeakPtr<WebContentsCaptureMachine> {
+ public WebContentsObserver {
public:
WebContentsCaptureMachine(int render_process_id, int render_view_id);
virtual ~WebContentsCaptureMachine();
// VideoCaptureMachine overrides.
- virtual bool Start(
- const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy) OVERRIDE;
- virtual void Stop() OVERRIDE;
+ virtual bool Start(const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy,
+ const media::VideoCaptureParams& params) OVERRIDE;
+ virtual void Stop(const base::Closure& callback) OVERRIDE;
// Starts a copy from the backing store or the composited surface. Must be run
// on the UI BrowserThread. |deliver_frame_cb| will be run when the operation
// completes. The copy will occur to |target|.
//
// This may be used as a ContentCaptureSubscription::CaptureCallback.
- void Capture(
- const base::Time& start_time,
- const scoped_refptr<media::VideoFrame>& target,
- const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
- deliver_frame_cb);
+ void Capture(const base::TimeTicks& start_time,
+ const scoped_refptr<media::VideoFrame>& target,
+ const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
+ deliver_frame_cb);
// content::WebContentsObserver implementation.
virtual void DidShowFullscreenWidget(int routing_id) OVERRIDE {
@@ -250,7 +269,7 @@ class WebContentsCaptureMachine
RenewFrameSubscription();
}
- virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE;
+ virtual void WebContentsDestroyed() OVERRIDE;
private:
// Starts observing the web contents, returning false if lookup fails.
@@ -261,7 +280,7 @@ class WebContentsCaptureMachine
// Response callback for RenderWidgetHost::CopyFromBackingStore().
void DidCopyFromBackingStore(
- const base::Time& start_time,
+ const base::TimeTicks& start_time,
const scoped_refptr<media::VideoFrame>& target,
const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
deliver_frame_cb,
@@ -270,7 +289,7 @@ class WebContentsCaptureMachine
// Response callback for RWHVP::CopyFromCompositingSurfaceToVideoFrame().
void DidCopyFromCompositingSurfaceToVideoFrame(
- const base::Time& start_time,
+ const base::TimeTicks& start_time,
const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
deliver_frame_cb,
bool success);
@@ -286,11 +305,14 @@ class WebContentsCaptureMachine
// A dedicated worker thread on which SkBitmap->VideoFrame conversion will
// occur. Only used when this activity cannot be done on the GPU.
- base::Thread render_thread_;
+ scoped_ptr<base::Thread> render_thread_;
// Makes all the decisions about which frames to copy, and how.
scoped_refptr<ThreadSafeCaptureOracle> oracle_proxy_;
+ // Video capture parameters that this machine is started with.
+ media::VideoCaptureParams capture_params_;
+
// Routing ID of any active fullscreen render widget or MSG_ROUTING_NONE
// otherwise.
int fullscreen_widget_id_;
@@ -302,31 +324,15 @@ class WebContentsCaptureMachine
// oracle, and initiating captures accordingly.
scoped_ptr<ContentCaptureSubscription> subscription_;
- DISALLOW_COPY_AND_ASSIGN(WebContentsCaptureMachine);
-};
-
-// Responsible for logging the effective frame rate.
-// TODO(nick): Make this compatible with the push model and hook it back up.
-class VideoFrameDeliveryLog {
- public:
- VideoFrameDeliveryLog();
-
- // Treat |frame_number| as having been delivered, and update the
- // frame rate statistics accordingly.
- void ChronicleFrameDelivery(int frame_number);
+ // Weak pointer factory used to invalidate callbacks.
+ // NOTE: Weak pointers must be invalidated before all other member variables.
+ base::WeakPtrFactory<WebContentsCaptureMachine> weak_ptr_factory_;
- private:
- // The following keep track of and log the effective frame rate whenever
- // verbose logging is turned on.
- base::Time last_frame_rate_log_time_;
- int count_frames_rendered_;
- int last_frame_number_;
-
- DISALLOW_COPY_AND_ASSIGN(VideoFrameDeliveryLog);
+ DISALLOW_COPY_AND_ASSIGN(WebContentsCaptureMachine);
};
bool FrameSubscriber::ShouldCaptureFrame(
- base::Time present_time,
+ base::TimeTicks present_time,
scoped_refptr<media::VideoFrame>* storage,
DeliverFrameCallback* deliver_frame_cb) {
TRACE_EVENT1("mirroring", "FrameSubscriber::ShouldCaptureFrame",
@@ -336,7 +342,10 @@ bool FrameSubscriber::ShouldCaptureFrame(
bool oracle_decision = oracle_proxy_->ObserveEventAndDecideCapture(
event_type_, present_time, storage, &capture_frame_cb);
- *deliver_frame_cb = base::Bind(&InvokeCaptureFrameCallback, capture_frame_cb);
+ if (!capture_frame_cb.is_null())
+ *deliver_frame_cb = base::Bind(capture_frame_cb, *storage);
+ if (oracle_decision)
+ delivery_log_->ChronicleFrameDelivery(present_time);
return oracle_decision;
}
@@ -346,21 +355,24 @@ ContentCaptureSubscription::ContentCaptureSubscription(
const CaptureCallback& capture_callback)
: render_process_id_(source.GetProcess()->GetID()),
render_view_id_(source.GetRoutingID()),
- paint_subscriber_(VideoCaptureOracle::kSoftwarePaint, oracle_proxy),
- timer_subscriber_(VideoCaptureOracle::kTimerPoll, oracle_proxy),
+ delivery_log_(),
+ paint_subscriber_(VideoCaptureOracle::kSoftwarePaint, oracle_proxy,
+ &delivery_log_),
+ timer_subscriber_(VideoCaptureOracle::kTimerPoll, oracle_proxy,
+ &delivery_log_),
capture_callback_(capture_callback),
timer_(true, true) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- RenderWidgetHostViewPort* view =
- RenderWidgetHostViewPort::FromRWHV(source.GetView());
+ RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
+ source.GetView());
// Subscribe to accelerated presents. These will be serviced directly by the
// oracle.
if (view && kAcceleratedSubscriberIsSupported) {
scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber(
new FrameSubscriber(VideoCaptureOracle::kCompositorUpdate,
- oracle_proxy));
+ oracle_proxy, &delivery_log_));
view->BeginFrameSubscription(subscriber.Pass());
}
@@ -383,8 +395,8 @@ ContentCaptureSubscription::~ContentCaptureSubscription() {
RenderViewHost* source = RenderViewHost::FromID(render_process_id_,
render_view_id_);
if (source) {
- RenderWidgetHostViewPort* view =
- RenderWidgetHostViewPort::FromRWHV(source->GetView());
+ RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
+ source->GetView());
if (view)
view->EndFrameSubscription();
}
@@ -403,9 +415,16 @@ void ContentCaptureSubscription::Observe(
// This message occurs on window resizes and visibility changes even when
// accelerated compositing is active, so we need to filter out these cases.
- if (!rwh || !rwh->GetView() || (rwh->is_accelerated_compositing_active() &&
- rwh->GetView()->IsSurfaceAvailableForCopy()))
+ if (!rwh || !rwh->GetView())
+ return;
+ // Mac sends DID_UPDATE_BACKING_STORE messages to inform the capture system
+ // of new software compositor frames, so always treat these messages as
+ // signals of a new frame on Mac.
+ // http://crbug.com/333986
+#if !defined(OS_MACOSX)
+ if (rwh->GetView()->IsSurfaceAvailableForCopy())
return;
+#endif
TRACE_EVENT1("mirroring", "ContentCaptureSubscription::Observe",
"instance", this);
@@ -413,7 +432,7 @@ void ContentCaptureSubscription::Observe(
base::Closure copy_done_callback;
scoped_refptr<media::VideoFrame> frame;
RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback deliver_frame_cb;
- const base::Time start_time = base::Time::Now();
+ const base::TimeTicks start_time = base::TimeTicks::Now();
if (paint_subscriber_.ShouldCaptureFrame(start_time,
&frame,
&deliver_frame_cb)) {
@@ -432,7 +451,7 @@ void ContentCaptureSubscription::OnTimer() {
scoped_refptr<media::VideoFrame> frame;
RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback deliver_frame_cb;
- const base::Time start_time = base::Time::Now();
+ const base::TimeTicks start_time = base::TimeTicks::Now();
if (timer_subscriber_.ShouldCaptureFrame(start_time,
&frame,
&deliver_frame_cb)) {
@@ -511,39 +530,29 @@ void RenderVideoFrame(const SkBitmap& input,
VideoFrameDeliveryLog::VideoFrameDeliveryLog()
: last_frame_rate_log_time_(),
- count_frames_rendered_(0),
- last_frame_number_(0) {
+ count_frames_rendered_(0) {
}
-void VideoFrameDeliveryLog::ChronicleFrameDelivery(int frame_number) {
+void VideoFrameDeliveryLog::ChronicleFrameDelivery(base::TimeTicks frame_time) {
// Log frame rate, if verbose logging is turned on.
static const base::TimeDelta kFrameRateLogInterval =
base::TimeDelta::FromSeconds(10);
- const base::Time now = base::Time::Now();
if (last_frame_rate_log_time_.is_null()) {
- last_frame_rate_log_time_ = now;
+ last_frame_rate_log_time_ = frame_time;
count_frames_rendered_ = 0;
- last_frame_number_ = frame_number;
} else {
++count_frames_rendered_;
- const base::TimeDelta elapsed = now - last_frame_rate_log_time_;
+ const base::TimeDelta elapsed = frame_time - last_frame_rate_log_time_;
if (elapsed >= kFrameRateLogInterval) {
const double measured_fps =
count_frames_rendered_ / elapsed.InSecondsF();
- const int frames_elapsed = frame_number - last_frame_number_;
- const int count_frames_dropped = frames_elapsed - count_frames_rendered_;
- DCHECK_LE(0, count_frames_dropped);
- UMA_HISTOGRAM_PERCENTAGE(
- "TabCapture.FrameDropPercentage",
- (count_frames_dropped * 100 + frames_elapsed / 2) / frames_elapsed);
UMA_HISTOGRAM_COUNTS(
"TabCapture.FrameRate",
static_cast<int>(measured_fps));
VLOG(1) << "Current measured frame rate for "
<< "WebContentsVideoCaptureDevice is " << measured_fps << " FPS.";
- last_frame_rate_log_time_ = now;
+ last_frame_rate_log_time_ = frame_time;
count_frames_rendered_ = 0;
- last_frame_number_ = frame_number;
}
}
}
@@ -552,54 +561,77 @@ WebContentsCaptureMachine::WebContentsCaptureMachine(int render_process_id,
int render_view_id)
: initial_render_process_id_(render_process_id),
initial_render_view_id_(render_view_id),
- render_thread_("WebContentsVideo_RenderThread"),
- fullscreen_widget_id_(MSG_ROUTING_NONE) {}
-
-WebContentsCaptureMachine::~WebContentsCaptureMachine() {}
+ fullscreen_widget_id_(MSG_ROUTING_NONE),
+ weak_ptr_factory_(this) {}
+
+WebContentsCaptureMachine::~WebContentsCaptureMachine() {
+ BrowserThread::PostBlockingPoolTask(
+ FROM_HERE,
+ base::Bind(&DeleteOnWorkerThread, base::Passed(&render_thread_),
+ base::Bind(&base::DoNothing)));
+}
bool WebContentsCaptureMachine::Start(
- const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy) {
+ const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy,
+ const media::VideoCaptureParams& params) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!started_);
DCHECK(oracle_proxy.get());
oracle_proxy_ = oracle_proxy;
+ capture_params_ = params;
- if (!render_thread_.Start()) {
+ render_thread_.reset(new base::Thread("WebContentsVideo_RenderThread"));
+ if (!render_thread_->Start()) {
DVLOG(1) << "Failed to spawn render thread.";
+ render_thread_.reset();
return false;
}
- if (!StartObservingWebContents())
+ if (!StartObservingWebContents()) {
+ DVLOG(1) << "Failed to observe web contents.";
+ render_thread_.reset();
return false;
+ }
started_ = true;
return true;
}
-void WebContentsCaptureMachine::Stop() {
+void WebContentsCaptureMachine::Stop(const base::Closure& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
subscription_.reset();
if (web_contents()) {
web_contents()->DecrementCapturerCount();
Observe(NULL);
}
- render_thread_.Stop();
+
+ // Any callback that intend to use render_thread_ will not work after it is
+ // passed.
+ weak_ptr_factory_.InvalidateWeakPtrs();
+
+ // The render thread cannot be stopped on the UI thread, so post a message
+ // to the thread pool used for blocking operations.
+ BrowserThread::PostBlockingPoolTask(
+ FROM_HERE,
+ base::Bind(&DeleteOnWorkerThread, base::Passed(&render_thread_),
+ callback));
+
started_ = false;
}
void WebContentsCaptureMachine::Capture(
- const base::Time& start_time,
+ const base::TimeTicks& start_time,
const scoped_refptr<media::VideoFrame>& target,
const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
deliver_frame_cb) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
RenderWidgetHost* rwh = GetTarget();
- RenderWidgetHostViewPort* view =
- rwh ? RenderWidgetHostViewPort::FromRWHV(rwh->GetView()) : NULL;
+ RenderWidgetHostViewBase* view =
+ rwh ? static_cast<RenderWidgetHostViewBase*>(rwh->GetView()) : NULL;
if (!view || !rwh) {
- deliver_frame_cb.Run(base::Time(), false);
+ deliver_frame_cb.Run(base::TimeTicks(), false);
return;
}
@@ -618,26 +650,24 @@ void WebContentsCaptureMachine::Capture(
view_size.width() * view_size.height() / 1024);
}
- if (!view->IsSurfaceAvailableForCopy()) {
- // Fallback to the more expensive renderer-side copy if the surface and
- // backing store are not accessible.
- rwh->GetSnapshotFromRenderer(
- gfx::Rect(),
- base::Bind(&WebContentsCaptureMachine::DidCopyFromBackingStore,
- this->AsWeakPtr(), start_time, target, deliver_frame_cb));
- } else if (view->CanCopyToVideoFrame()) {
+ if (view->CanCopyToVideoFrame()) {
view->CopyFromCompositingSurfaceToVideoFrame(
gfx::Rect(view_size),
target,
base::Bind(&WebContentsCaptureMachine::
DidCopyFromCompositingSurfaceToVideoFrame,
- this->AsWeakPtr(), start_time, deliver_frame_cb));
+ weak_ptr_factory_.GetWeakPtr(),
+ start_time, deliver_frame_cb));
} else {
rwh->CopyFromBackingStore(
gfx::Rect(),
fitted_size, // Size here is a request not always honored.
base::Bind(&WebContentsCaptureMachine::DidCopyFromBackingStore,
- this->AsWeakPtr(), start_time, target, deliver_frame_cb));
+ weak_ptr_factory_.GetWeakPtr(),
+ start_time,
+ target,
+ deliver_frame_cb),
+ SkBitmap::kARGB_8888_Config);
}
}
@@ -661,7 +691,7 @@ bool WebContentsCaptureMachine::StartObservingWebContents() {
WebContentsImpl* contents = static_cast<WebContentsImpl*>(web_contents());
if (contents) {
- contents->IncrementCapturerCount();
+ contents->IncrementCapturerCount(oracle_proxy_->GetCaptureSize());
fullscreen_widget_id_ = contents->GetFullscreenWidgetRoutingID();
RenewFrameSubscription();
return true;
@@ -671,13 +701,12 @@ bool WebContentsCaptureMachine::StartObservingWebContents() {
return false;
}
-void WebContentsCaptureMachine::WebContentsDestroyed(
- WebContents* web_contents) {
+void WebContentsCaptureMachine::WebContentsDestroyed() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
subscription_.reset();
- web_contents->DecrementCapturerCount();
- oracle_proxy_->ReportError();
+ web_contents()->DecrementCapturerCount();
+ oracle_proxy_->ReportError("WebContentsDestroyed()");
}
RenderWidgetHost* WebContentsCaptureMachine::GetTarget() {
@@ -698,7 +727,7 @@ RenderWidgetHost* WebContentsCaptureMachine::GetTarget() {
}
void WebContentsCaptureMachine::DidCopyFromBackingStore(
- const base::Time& start_time,
+ const base::TimeTicks& start_time,
const scoped_refptr<media::VideoFrame>& target,
const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
deliver_frame_cb,
@@ -706,12 +735,13 @@ void WebContentsCaptureMachine::DidCopyFromBackingStore(
const SkBitmap& bitmap) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- base::Time now = base::Time::Now();
+ base::TimeTicks now = base::TimeTicks::Now();
+ DCHECK(render_thread_.get());
if (success) {
UMA_HISTOGRAM_TIMES("TabCapture.CopyTimeBitmap", now - start_time);
TRACE_EVENT_ASYNC_STEP_INTO0("mirroring", "Capture", target.get(),
"Render");
- render_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind(
+ render_thread_->message_loop_proxy()->PostTask(FROM_HERE, base::Bind(
&RenderVideoFrame, bitmap, target,
base::Bind(deliver_frame_cb, start_time)));
} else {
@@ -722,12 +752,12 @@ void WebContentsCaptureMachine::DidCopyFromBackingStore(
}
void WebContentsCaptureMachine::DidCopyFromCompositingSurfaceToVideoFrame(
- const base::Time& start_time,
+ const base::TimeTicks& start_time,
const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
deliver_frame_cb,
bool success) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- base::Time now = base::Time::Now();
+ base::TimeTicks now = base::TimeTicks::Now();
if (success) {
UMA_HISTOGRAM_TIMES("TabCapture.CopyTimeVideoFrame", now - start_time);
@@ -749,14 +779,15 @@ void WebContentsCaptureMachine::RenewFrameSubscription() {
return;
subscription_.reset(new ContentCaptureSubscription(*rwh, oracle_proxy_,
- base::Bind(&WebContentsCaptureMachine::Capture, this->AsWeakPtr())));
+ base::Bind(&WebContentsCaptureMachine::Capture,
+ weak_ptr_factory_.GetWeakPtr())));
}
} // namespace
WebContentsVideoCaptureDevice::WebContentsVideoCaptureDevice(
int render_process_id, int render_view_id)
- : impl_(new VideoCaptureDeviceImpl(scoped_ptr<VideoCaptureMachine>(
+ : core_(new ContentVideoCaptureDeviceCore(scoped_ptr<VideoCaptureMachine>(
new WebContentsCaptureMachine(render_process_id, render_view_id)))) {}
WebContentsVideoCaptureDevice::~WebContentsVideoCaptureDevice() {
@@ -781,11 +812,11 @@ void WebContentsVideoCaptureDevice::AllocateAndStart(
const media::VideoCaptureParams& params,
scoped_ptr<Client> client) {
DVLOG(1) << "Allocating " << params.requested_format.frame_size.ToString();
- impl_->AllocateAndStart(params, client.Pass());
+ core_->AllocateAndStart(params, client.Pass());
}
void WebContentsVideoCaptureDevice::StopAndDeAllocate() {
- impl_->StopAndDeAllocate();
+ core_->StopAndDeAllocate();
}
} // namespace content
diff --git a/chromium/content/browser/renderer_host/media/web_contents_video_capture_device.h b/chromium/content/browser/media/capture/web_contents_video_capture_device.h
index e9140d3dd8f..16c7b09f8e1 100644
--- a/chromium/content/browser/renderer_host/media/web_contents_video_capture_device.h
+++ b/chromium/content/browser/media/capture/web_contents_video_capture_device.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_WEB_CONTENTS_VIDEO_CAPTURE_DEVICE_H_
-#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_WEB_CONTENTS_VIDEO_CAPTURE_DEVICE_H_
+#ifndef CONTENT_BROWSER_MEDIA_CAPTURE_WEB_CONTENTS_VIDEO_CAPTURE_DEVICE_H_
+#define CONTENT_BROWSER_MEDIA_CAPTURE_WEB_CONTENTS_VIDEO_CAPTURE_DEVICE_H_
#include <string>
@@ -13,7 +13,7 @@
namespace content {
-class VideoCaptureDeviceImpl;
+class ContentVideoCaptureDeviceCore;
// A virtualized VideoCaptureDevice that mirrors the displayed contents of a
// tab (accessed via its associated WebContents instance), producing a stream of
@@ -35,8 +35,6 @@ class CONTENT_EXPORT WebContentsVideoCaptureDevice
// |destroy_cb| is invoked on an outside thread once all outstanding objects
// are completely destroyed -- this will be some time after the
// WebContentsVideoCaptureDevice is itself deleted.
- // TODO(miu): Passing a destroy callback suggests needing to revisit the
- // design philosophy of an asynchronous DeAllocate(). http://crbug.com/158641
static media::VideoCaptureDevice* Create(const std::string& device_id);
virtual ~WebContentsVideoCaptureDevice();
@@ -49,7 +47,7 @@ class CONTENT_EXPORT WebContentsVideoCaptureDevice
private:
WebContentsVideoCaptureDevice(int render_process_id, int render_view_id);
- const scoped_ptr<VideoCaptureDeviceImpl> impl_;
+ const scoped_ptr<ContentVideoCaptureDeviceCore> core_;
DISALLOW_COPY_AND_ASSIGN(WebContentsVideoCaptureDevice);
};
@@ -57,4 +55,4 @@ class CONTENT_EXPORT WebContentsVideoCaptureDevice
} // namespace content
-#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_WEB_CONTENTS_VIDEO_CAPTURE_DEVICE_H_
+#endif // CONTENT_BROWSER_MEDIA_CAPTURE_WEB_CONTENTS_VIDEO_CAPTURE_DEVICE_H_
diff --git a/chromium/content/browser/renderer_host/media/web_contents_video_capture_device_unittest.cc b/chromium/content/browser/media/capture/web_contents_video_capture_device_unittest.cc
index db766cd7378..dc1bd5a54d9 100644
--- a/chromium/content/browser/renderer_host/media/web_contents_video_capture_device_unittest.cc
+++ b/chromium/content/browser/media/capture/web_contents_video_capture_device_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "content/browser/renderer_host/media/web_contents_video_capture_device.h"
+#include "content/browser/media/capture/web_contents_video_capture_device.h"
#include "base/bind_helpers.h"
#include "base/debug/debugger.h"
@@ -11,20 +11,21 @@
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "content/browser/browser_thread_impl.h"
+#include "content/browser/media/capture/video_capture_oracle.h"
+#include "content/browser/media/capture/web_contents_capture_util.h"
#include "content/browser/renderer_host/media/video_capture_buffer_pool.h"
-#include "content/browser/renderer_host/media/video_capture_oracle.h"
-#include "content/browser/renderer_host/media/web_contents_capture_util.h"
#include "content/browser/renderer_host/render_view_host_factory.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
-#include "content/port/browser/render_widget_host_view_frame_subscriber.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
+#include "content/public/browser/render_widget_host_view_frame_subscriber.h"
#include "content/public/test/mock_render_process_host.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "content/public/test/test_utils.h"
#include "content/test/test_render_view_host.h"
#include "content/test/test_web_contents.h"
+#include "media/base/video_frame.h"
#include "media/base/video_util.h"
#include "media/base/yuv_convert.h"
#include "media/video/capture/video_capture_types.h"
@@ -192,7 +193,7 @@ class CaptureTestView : public TestRenderWidgetHostView {
// Simulate a compositor paint event for our subscriber.
void SimulateUpdate() {
- const base::Time present_time = base::Time::Now();
+ const base::TimeTicks present_time = base::TimeTicks::Now();
RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback callback;
scoped_refptr<media::VideoFrame> target;
if (subscriber_ && subscriber_->ShouldCaptureFrame(present_time,
@@ -228,14 +229,13 @@ class CaptureTestRenderViewHost : public TestRenderViewHost {
public:
CaptureTestRenderViewHost(SiteInstance* instance,
RenderViewHostDelegate* delegate,
- RenderFrameHostDelegate* frame_delegate,
RenderWidgetHostDelegate* widget_delegate,
int routing_id,
int main_frame_routing_id,
bool swapped_out,
CaptureTestSourceController* controller)
- : TestRenderViewHost(instance, delegate, frame_delegate, widget_delegate,
- routing_id, main_frame_routing_id, swapped_out),
+ : TestRenderViewHost(instance, delegate, widget_delegate, routing_id,
+ main_frame_routing_id, swapped_out),
controller_(controller) {
// Override the default view installed by TestRenderViewHost; we need
// our special subclass which has mocked-out tab capture support.
@@ -248,7 +248,8 @@ class CaptureTestRenderViewHost : public TestRenderViewHost {
virtual void CopyFromBackingStore(
const gfx::Rect& src_rect,
const gfx::Size& accelerated_dst_size,
- const base::Callback<void(bool, const SkBitmap&)>& callback) OVERRIDE {
+ const base::Callback<void(bool, const SkBitmap&)>& callback,
+ const SkBitmap::Config& bitmap_config) OVERRIDE {
gfx::Size size = controller_->GetCopyResultSize();
SkColor color = controller_->GetSolidColor();
@@ -290,15 +291,13 @@ class CaptureTestRenderViewHostFactory : public RenderViewHostFactory {
virtual RenderViewHost* CreateRenderViewHost(
SiteInstance* instance,
RenderViewHostDelegate* delegate,
- RenderFrameHostDelegate* frame_delegate,
RenderWidgetHostDelegate* widget_delegate,
int routing_id,
int main_frame_routing_id,
bool swapped_out) OVERRIDE {
- return new CaptureTestRenderViewHost(instance, delegate, frame_delegate,
- widget_delegate, routing_id,
- main_frame_routing_id, swapped_out,
- controller_);
+ return new CaptureTestRenderViewHost(instance, delegate, widget_delegate,
+ routing_id, main_frame_routing_id,
+ swapped_out, controller_);
}
private:
CaptureTestSourceController* controller_;
@@ -321,8 +320,9 @@ class StubClient : public media::VideoCaptureDevice::Client {
virtual scoped_refptr<media::VideoCaptureDevice::Client::Buffer>
ReserveOutputBuffer(media::VideoFrame::Format format,
const gfx::Size& dimensions) OVERRIDE {
+ CHECK_EQ(format, media::VideoFrame::I420);
const size_t frame_bytes =
- media::VideoFrame::AllocationSize(format, dimensions);
+ media::VideoFrame::AllocationSize(media::VideoFrame::I420, dimensions);
int buffer_id_to_drop = VideoCaptureBufferPool::kInvalidId; // Ignored.
int buffer_id =
buffer_pool_->ReserveForProducer(frame_bytes, &buffer_id_to_drop);
@@ -335,36 +335,33 @@ class StubClient : public media::VideoCaptureDevice::Client {
new PoolBuffer(buffer_pool_, buffer_id, data, size));
}
- virtual void OnIncomingCapturedFrame(
+ virtual void OnIncomingCapturedData(
const uint8* data,
int length,
- base::Time timestamp,
+ const media::VideoCaptureFormat& frame_format,
int rotation,
- const media::VideoCaptureFormat& frame_format) OVERRIDE {
+ base::TimeTicks timestamp) OVERRIDE {
FAIL();
}
- virtual void OnIncomingCapturedBuffer(const scoped_refptr<Buffer>& buffer,
- media::VideoFrame::Format format,
- const gfx::Size& dimensions,
- base::Time timestamp,
- int frame_rate) OVERRIDE {
- EXPECT_EQ(gfx::Size(kTestWidth, kTestHeight), dimensions);
- EXPECT_EQ(media::VideoFrame::I420, format);
+ virtual void OnIncomingCapturedVideoFrame(
+ const scoped_refptr<Buffer>& buffer,
+ const media::VideoCaptureFormat& buffer_format,
+ const scoped_refptr<media::VideoFrame>& frame,
+ base::TimeTicks timestamp) OVERRIDE {
+ EXPECT_EQ(gfx::Size(kTestWidth, kTestHeight), buffer_format.frame_size);
+ EXPECT_EQ(media::PIXEL_FORMAT_I420, buffer_format.pixel_format);
+ EXPECT_EQ(media::VideoFrame::I420, frame->format());
uint8 yuv[3];
- size_t offset = 0;
- for (int plane = 0; plane < 3; ++plane) {
- yuv[plane] = reinterpret_cast<uint8*>(buffer->data())[offset];
- offset += media::VideoFrame::PlaneAllocationSize(
- media::VideoFrame::I420, plane, dimensions);
- }
+ for (int plane = 0; plane < 3; ++plane)
+ yuv[plane] = frame->data(plane)[0];
// TODO(nick): We just look at the first pixel presently, because if
// the analysis is too slow, the backlog of frames will grow without bound
// and trouble erupts. http://crbug.com/174519
color_callback_.Run((SkColorSetRGB(yuv[0], yuv[1], yuv[2])));
}
- virtual void OnError() OVERRIDE {
+ virtual void OnError(const std::string& reason) OVERRIDE {
error_callback_.Run();
}
diff --git a/chromium/content/browser/media/cdm/browser_cdm_manager.cc b/chromium/content/browser/media/cdm/browser_cdm_manager.cc
new file mode 100644
index 00000000000..a71bfafbd5f
--- /dev/null
+++ b/chromium/content/browser/media/cdm/browser_cdm_manager.cc
@@ -0,0 +1,286 @@
+// 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/media/cdm/browser_cdm_manager.h"
+
+#include "base/command_line.h"
+#include "content/common/media/cdm_messages.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/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/content_switches.h"
+#include "media/base/browser_cdm.h"
+#include "media/base/browser_cdm_factory.h"
+#include "media/base/media_switches.h"
+
+namespace content {
+
+using media::BrowserCdm;
+using media::MediaKeys;
+
+// Maximum lengths for various EME API parameters. These are checks to
+// prevent unnecessarily large parameters from being passed around, and the
+// lengths are somewhat arbitrary as the EME spec doesn't specify any limits.
+const size_t kMaxInitDataLength = 64 * 1024; // 64 KB
+const size_t kMaxSessionResponseLength = 64 * 1024; // 64 KB
+const size_t kMaxKeySystemLength = 256;
+
+// static
+BrowserCdmManager* BrowserCdmManager::Create(RenderFrameHost* rfh) {
+ return new BrowserCdmManager(rfh);
+}
+
+BrowserCdmManager::BrowserCdmManager(RenderFrameHost* render_frame_host)
+ : render_frame_host_(render_frame_host),
+ web_contents_(WebContents::FromRenderFrameHost(render_frame_host)),
+ weak_ptr_factory_(this) {
+}
+
+BrowserCdmManager::~BrowserCdmManager() {
+ // During the tear down process, OnDestroyCdm() may or may not be called
+ // (e.g. WebContents may be destroyed before the render process is killed). So
+ // we cannot DCHECK(cdm_map_.empty()) here. Instead, all CDMs in |cdm_map_|
+ // will be destroyed here because they are owned by BrowserCdmManager.
+}
+
+BrowserCdm* BrowserCdmManager::GetCdm(int cdm_id) {
+ return cdm_map_.get(cdm_id);
+}
+
+void BrowserCdmManager::OnSessionCreated(
+ int cdm_id,
+ uint32 session_id,
+ const std::string& web_session_id) {
+ Send(new CdmMsg_SessionCreated(
+ RoutingID(), cdm_id, session_id, web_session_id));
+}
+
+void BrowserCdmManager::OnSessionMessage(
+ int cdm_id,
+ uint32 session_id,
+ const std::vector<uint8>& message,
+ const GURL& destination_url) {
+ GURL verified_gurl = destination_url;
+ if (!verified_gurl.is_valid() && !verified_gurl.is_empty()) {
+ DLOG(WARNING) << "SessionMessage destination_url is invalid : "
+ << destination_url.possibly_invalid_spec();
+ verified_gurl = GURL::EmptyGURL(); // Replace invalid destination_url.
+ }
+
+ Send(new CdmMsg_SessionMessage(
+ RoutingID(), cdm_id, session_id, message, verified_gurl));
+}
+
+void BrowserCdmManager::OnSessionReady(int cdm_id, uint32 session_id) {
+ Send(new CdmMsg_SessionReady(RoutingID(), cdm_id, session_id));
+}
+
+void BrowserCdmManager::OnSessionClosed(int cdm_id, uint32 session_id) {
+ Send(new CdmMsg_SessionClosed(RoutingID(), cdm_id, session_id));
+}
+
+void BrowserCdmManager::OnSessionError(int cdm_id,
+ uint32 session_id,
+ MediaKeys::KeyError error_code,
+ uint32 system_code) {
+ Send(new CdmMsg_SessionError(
+ RoutingID(), cdm_id, session_id, error_code, system_code));
+}
+
+void BrowserCdmManager::OnInitializeCdm(int cdm_id,
+ const std::string& key_system,
+ const GURL& security_origin) {
+ if (key_system.size() > kMaxKeySystemLength) {
+ // This failure will be discovered and reported by OnCreateSession()
+ // as GetCdm() will return null.
+ NOTREACHED() << "Invalid key system: " << key_system;
+ return;
+ }
+
+ AddCdm(cdm_id, key_system, security_origin);
+}
+
+void BrowserCdmManager::OnCreateSession(
+ int cdm_id,
+ uint32 session_id,
+ CdmHostMsg_CreateSession_ContentType content_type,
+ const std::vector<uint8>& init_data) {
+ if (init_data.size() > kMaxInitDataLength) {
+ LOG(WARNING) << "InitData for ID: " << cdm_id
+ << " too long: " << init_data.size();
+ OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0);
+ return;
+ }
+
+ // Convert the session content type into a MIME type. "audio" and "video"
+ // don't matter, so using "video" for the MIME type.
+ // Ref:
+ // https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html#dom-createsession
+ std::string mime_type;
+ switch (content_type) {
+ case CREATE_SESSION_TYPE_WEBM:
+ mime_type = "video/webm";
+ break;
+ case CREATE_SESSION_TYPE_MP4:
+ mime_type = "video/mp4";
+ break;
+ default:
+ NOTREACHED();
+ return;
+ }
+
+#if defined(OS_ANDROID)
+ if (CommandLine::ForCurrentProcess()
+ ->HasSwitch(switches::kDisableInfobarForProtectedMediaIdentifier)) {
+ CreateSessionIfPermitted(cdm_id, session_id, mime_type, init_data, true);
+ return;
+ }
+#endif
+
+ BrowserCdm* cdm = GetCdm(cdm_id);
+ if (!cdm) {
+ DLOG(WARNING) << "No CDM for ID " << cdm_id << " found";
+ OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0);
+ return;
+ }
+
+ std::map<int, GURL>::const_iterator iter =
+ cdm_security_origin_map_.find(cdm_id);
+ if (iter == cdm_security_origin_map_.end()) {
+ NOTREACHED();
+ OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0);
+ return;
+ }
+
+ base::Closure cancel_callback;
+ GetContentClient()->browser()->RequestProtectedMediaIdentifierPermission(
+ web_contents_,
+ iter->second,
+ base::Bind(&BrowserCdmManager::CreateSessionIfPermitted,
+ weak_ptr_factory_.GetWeakPtr(),
+ cdm_id,
+ session_id,
+ mime_type,
+ init_data),
+ &cancel_callback);
+ if (!cancel_callback.is_null())
+ cdm_cancel_permision_map_[cdm_id] = cancel_callback;
+}
+
+void BrowserCdmManager::OnUpdateSession(
+ int cdm_id,
+ uint32 session_id,
+ const std::vector<uint8>& response) {
+ BrowserCdm* cdm = GetCdm(cdm_id);
+ if (!cdm) {
+ DLOG(WARNING) << "No CDM for ID " << cdm_id << " found";
+ OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0);
+ return;
+ }
+
+ if (response.size() > kMaxSessionResponseLength) {
+ LOG(WARNING) << "Response for ID " << cdm_id
+ << " is too long: " << response.size();
+ OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0);
+ return;
+ }
+
+ cdm->UpdateSession(session_id, &response[0], response.size());
+}
+
+void BrowserCdmManager::OnReleaseSession(int cdm_id, uint32 session_id) {
+ BrowserCdm* cdm = GetCdm(cdm_id);
+ if (!cdm) {
+ DLOG(WARNING) << "No CDM for ID " << cdm_id << " found";
+ OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0);
+ return;
+ }
+
+ cdm->ReleaseSession(session_id);
+}
+
+void BrowserCdmManager::OnDestroyCdm(int cdm_id) {
+ BrowserCdm* cdm = GetCdm(cdm_id);
+ if (!cdm)
+ return;
+
+ CancelAllPendingSessionCreations(cdm_id);
+ RemoveCdm(cdm_id);
+}
+
+void BrowserCdmManager::CancelAllPendingSessionCreations(int cdm_id) {
+ if (cdm_cancel_permision_map_.count(cdm_id)) {
+ cdm_cancel_permision_map_[cdm_id].Run();
+ cdm_cancel_permision_map_.erase(cdm_id);
+ }
+}
+
+void BrowserCdmManager::AddCdm(int cdm_id,
+ const std::string& key_system,
+ const GURL& security_origin) {
+ DCHECK(!GetCdm(cdm_id));
+ base::WeakPtr<BrowserCdmManager> weak_this = weak_ptr_factory_.GetWeakPtr();
+ scoped_ptr<BrowserCdm> cdm(media::CreateBrowserCdm(
+ key_system,
+ base::Bind(&BrowserCdmManager::OnSessionCreated, weak_this, cdm_id),
+ base::Bind(&BrowserCdmManager::OnSessionMessage, weak_this, cdm_id),
+ base::Bind(&BrowserCdmManager::OnSessionReady, weak_this, cdm_id),
+ base::Bind(&BrowserCdmManager::OnSessionClosed, weak_this, cdm_id),
+ base::Bind(&BrowserCdmManager::OnSessionError, weak_this, cdm_id)));
+
+ if (!cdm) {
+ // This failure will be discovered and reported by OnCreateSession()
+ // as GetCdm() will return null.
+ DVLOG(1) << "failed to create CDM.";
+ return;
+ }
+
+ cdm_map_.add(cdm_id, cdm.Pass());
+ cdm_security_origin_map_[cdm_id] = security_origin;
+}
+
+void BrowserCdmManager::RemoveCdm(int cdm_id) {
+ // TODO(xhwang): Detach CDM from the player it's set to. In prefixed
+ // EME implementation the current code is fine because we always destroy the
+ // player before we destroy the DrmBridge. This will not always be the case
+ // in unprefixed EME implementation.
+ cdm_map_.erase(cdm_id);
+ cdm_security_origin_map_.erase(cdm_id);
+ cdm_cancel_permision_map_.erase(cdm_id);
+}
+
+int BrowserCdmManager::RoutingID() {
+ return render_frame_host_->GetRoutingID();
+}
+
+bool BrowserCdmManager::Send(IPC::Message* msg) {
+ return render_frame_host_->Send(msg);
+}
+
+void BrowserCdmManager::CreateSessionIfPermitted(
+ int cdm_id,
+ uint32 session_id,
+ const std::string& content_type,
+ const std::vector<uint8>& init_data,
+ bool permitted) {
+ cdm_cancel_permision_map_.erase(cdm_id);
+ if (!permitted) {
+ OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0);
+ return;
+ }
+
+ BrowserCdm* cdm = GetCdm(cdm_id);
+ if (!cdm) {
+ DLOG(WARNING) << "No CDM for ID: " << cdm_id << " found";
+ OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0);
+ return;
+ }
+
+ // This could fail, in which case a SessionError will be fired.
+ cdm->CreateSession(session_id, content_type, &init_data[0], init_data.size());
+}
+
+} // namespace content
diff --git a/chromium/content/browser/media/cdm/browser_cdm_manager.h b/chromium/content/browser/media/cdm/browser_cdm_manager.h
new file mode 100644
index 00000000000..85be4c182a5
--- /dev/null
+++ b/chromium/content/browser/media/cdm/browser_cdm_manager.h
@@ -0,0 +1,128 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_MEDIA_CDM_BROWSER_CDM_MANAGER_H_
+#define CONTENT_BROWSER_MEDIA_CDM_BROWSER_CDM_MANAGER_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/containers/scoped_ptr_hash_map.h"
+#include "base/memory/scoped_ptr.h"
+#include "content/common/content_export.h"
+#include "content/common/media/cdm_messages_enums.h"
+#include "ipc/ipc_message.h"
+// TODO(xhwang): Drop this when KeyError is moved to a common header.
+#include "media/base/media_keys.h"
+#include "url/gurl.h"
+
+namespace media {
+class BrowserCdm;
+}
+
+namespace content {
+
+class RenderFrameHost;
+class WebContents;
+
+// This class manages all CDM objects. It receives control operations from the
+// the render process, and forwards them to corresponding CDM object. Callbacks
+// from CDM objects are converted to IPCs and then sent to the render process.
+class CONTENT_EXPORT BrowserCdmManager {
+ public:
+ // Creates a new BrowserCdmManager for |rfh|.
+ static BrowserCdmManager* Create(RenderFrameHost* rfh);
+
+ ~BrowserCdmManager();
+
+ media::BrowserCdm* GetCdm(int cdm_id);
+
+ // CDM callbacks.
+ void OnSessionCreated(int cdm_id,
+ uint32 session_id,
+ const std::string& web_session_id);
+ void OnSessionMessage(int cdm_id,
+ uint32 session_id,
+ const std::vector<uint8>& message,
+ const GURL& destination_url);
+ void OnSessionReady(int cdm_id, uint32 session_id);
+ void OnSessionClosed(int cdm_id, uint32 session_id);
+ void OnSessionError(int cdm_id,
+ uint32 session_id,
+ media::MediaKeys::KeyError error_code,
+ uint32 system_code);
+
+ // Message handlers.
+ void OnInitializeCdm(int cdm_id,
+ const std::string& key_system,
+ const GURL& frame_url);
+ void OnCreateSession(int cdm_id,
+ uint32 session_id,
+ CdmHostMsg_CreateSession_ContentType content_type,
+ const std::vector<uint8>& init_data);
+ void OnUpdateSession(int cdm_id,
+ uint32 session_id,
+ const std::vector<uint8>& response);
+ void OnReleaseSession(int cdm_id, uint32 session_id);
+ void OnSetCdm(int player_id, int cdm_id);
+ void OnDestroyCdm(int cdm_id);
+
+ private:
+ // Clients must use Create() or subclass constructor.
+ explicit BrowserCdmManager(RenderFrameHost* render_frame_host);
+
+ // Cancels all pending session creations associated with |cdm_id|.
+ void CancelAllPendingSessionCreations(int cdm_id);
+
+ // Adds a new CDM identified by |cdm_id| for the given |key_system| and
+ // |security_origin|.
+ void AddCdm(int cdm_id,
+ const std::string& key_system,
+ const GURL& security_origin);
+
+ // Removes the CDM with the specified id.
+ void RemoveCdm(int cdm_id);
+
+ int RoutingID();
+
+ // Helper function to send messages to RenderFrameObserver.
+ bool Send(IPC::Message* msg);
+
+ // If |permitted| is false, it does nothing but send
+ // |CdmMsg_SessionError| IPC message.
+ // The primary use case is infobar permission callback, i.e., when infobar
+ // can decide user's intention either from interacting with the actual info
+ // bar or from the saved preference.
+ void CreateSessionIfPermitted(int cdm_id,
+ uint32 session_id,
+ const std::string& content_type,
+ const std::vector<uint8>& init_data,
+ bool permitted);
+
+ RenderFrameHost* const render_frame_host_;
+
+ WebContents* const web_contents_;
+
+ // A map from CDM IDs to managed CDMs.
+ typedef base::ScopedPtrHashMap<int, media::BrowserCdm> CdmMap;
+ CdmMap cdm_map_;
+
+ // Map from CDM ID to CDM's security origin.
+ std::map<int, GURL> cdm_security_origin_map_;
+
+ // Map from CDM ID to a callback to cancel the permission request.
+ std::map<int, base::Closure> cdm_cancel_permision_map_;
+
+ // NOTE: Weak pointers must be invalidated before all other member variables.
+ base::WeakPtrFactory<BrowserCdmManager> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserCdmManager);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_MEDIA_CDM_BROWSER_CDM_MANAGER_H_
diff --git a/chromium/content/browser/media/encrypted_media_browsertest.cc b/chromium/content/browser/media/encrypted_media_browsertest.cc
index f4e5eafdc4c..9c0dbac6351 100644
--- a/chromium/content/browser/media/encrypted_media_browsertest.cc
+++ b/chromium/content/browser/media/encrypted_media_browsertest.cc
@@ -116,8 +116,8 @@ class EncryptedMediaTest : public content::MediaBrowserTest,
// We want to fail quickly when a test fails because an error is encountered.
virtual void AddWaitForTitles(content::TitleWatcher* title_watcher) OVERRIDE {
MediaBrowserTest::AddWaitForTitles(title_watcher);
- title_watcher->AlsoWaitForTitle(ASCIIToUTF16(kEmeNotSupportedError));
- title_watcher->AlsoWaitForTitle(ASCIIToUTF16(kEmeKeyError));
+ title_watcher->AlsoWaitForTitle(base::ASCIIToUTF16(kEmeNotSupportedError));
+ title_watcher->AlsoWaitForTitle(base::ASCIIToUTF16(kEmeKeyError));
}
#if defined(OS_ANDROID)
diff --git a/chromium/content/browser/media/media_browsertest.cc b/chromium/content/browser/media/media_browsertest.cc
index 06558233632..bdd77096bc3 100644
--- a/chromium/content/browser/media/media_browsertest.cc
+++ b/chromium/content/browser/media/media_browsertest.cc
@@ -9,8 +9,8 @@
#include "content/public/browser/web_contents.h"
#include "content/public/common/url_constants.h"
#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/content_browser_test_utils.h"
#include "content/shell/browser/shell.h"
-#include "content/test/content_browser_test_utils.h"
// TODO(wolenetz): Fix Media.YUV* tests on MSVS 2012 x64. crbug.com/180074
#if defined(OS_WIN) && defined(ARCH_CPU_X86_64) && _MSC_VER == 1700
@@ -26,14 +26,6 @@ const char MediaBrowserTest::kEnded[] = "ENDED";
const char MediaBrowserTest::kError[] = "ERROR";
const char MediaBrowserTest::kFailed[] = "FAILED";
-void MediaBrowserTest::SetUp() {
- // TODO(danakj): The GPU Video Decoder needs real GL bindings.
- // crbug.com/269087
- UseRealGLBindings();
-
- ContentBrowserTest::SetUp();
-}
-
void MediaBrowserTest::RunMediaTestPage(
const char* html_page, std::vector<StringPair>* query_params,
const char* expected, bool http) {
@@ -59,7 +51,7 @@ void MediaBrowserTest::RunMediaTestPage(
}
void MediaBrowserTest::RunTest(const GURL& gurl, const char* expected) {
- const base::string16 expected_title = ASCIIToUTF16(expected);
+ const base::string16 expected_title = base::ASCIIToUTF16(expected);
DVLOG(1) << "Running test URL: " << gurl;
TitleWatcher title_watcher(shell()->web_contents(), expected_title);
AddWaitForTitles(&title_watcher);
@@ -70,9 +62,9 @@ void MediaBrowserTest::RunTest(const GURL& gurl, const char* expected) {
}
void MediaBrowserTest::AddWaitForTitles(content::TitleWatcher* title_watcher) {
- title_watcher->AlsoWaitForTitle(ASCIIToUTF16(kEnded));
- title_watcher->AlsoWaitForTitle(ASCIIToUTF16(kError));
- title_watcher->AlsoWaitForTitle(ASCIIToUTF16(kFailed));
+ title_watcher->AlsoWaitForTitle(base::ASCIIToUTF16(kEnded));
+ title_watcher->AlsoWaitForTitle(base::ASCIIToUTF16(kError));
+ title_watcher->AlsoWaitForTitle(base::ASCIIToUTF16(kFailed));
}
// Tests playback and seeking of an audio or video file over file or http based
@@ -117,6 +109,14 @@ IN_PROC_BROWSER_TEST_P(MediaTest, VideoBearWebm) {
PlayVideo("bear.webm", GetParam());
}
+IN_PROC_BROWSER_TEST_P(MediaTest, VideoBearOpusWebm) {
+ PlayVideo("bear-opus.webm", GetParam());
+}
+
+IN_PROC_BROWSER_TEST_P(MediaTest, VideoBearOpusOgg) {
+ PlayVideo("bear-opus.ogg", GetParam());
+}
+
IN_PROC_BROWSER_TEST_P(MediaTest, VideoBearSilentWebm) {
PlayVideo("bear_silent.webm", GetParam());
}
@@ -201,7 +201,7 @@ IN_PROC_BROWSER_TEST_P(MediaTest, VideoTulipWebm) {
// Covers tear-down when navigating away as opposed to browser exiting.
IN_PROC_BROWSER_TEST_F(MediaTest, Navigate) {
PlayVideo("bear.ogv", false);
- NavigateToURL(shell(), GURL(kAboutBlankURL));
+ NavigateToURL(shell(), GURL(url::kAboutBlankURL));
EXPECT_FALSE(shell()->web_contents()->IsCrashed());
}
@@ -217,22 +217,24 @@ IN_PROC_BROWSER_TEST_F(MediaTest, MAYBE(Yuv422pTheora)) {
}
IN_PROC_BROWSER_TEST_F(MediaTest, MAYBE(Yuv444pTheora)) {
- // TODO(scherkus): Support YUV444 http://crbug.com/104711
- RunColorFormatTest("yuv424p.ogv", "ERROR");
+ RunColorFormatTest("yuv444p.ogv", "ENDED");
}
IN_PROC_BROWSER_TEST_F(MediaTest, MAYBE(Yuv420pVp8)) {
RunColorFormatTest("yuv420p.webm", "ENDED");
}
+IN_PROC_BROWSER_TEST_F(MediaTest, MAYBE(Yuv444pVp9)) {
+ RunColorFormatTest("yuv444p.webm", "ENDED");
+}
+
#if defined(USE_PROPRIETARY_CODECS)
IN_PROC_BROWSER_TEST_F(MediaTest, MAYBE(Yuv420pH264)) {
RunColorFormatTest("yuv420p.mp4", "ENDED");
}
IN_PROC_BROWSER_TEST_F(MediaTest, MAYBE(Yuvj420pH264)) {
- // TODO(rileya): Support YUVJ420P properly http://crbug.com/310273
- RunColorFormatTest("yuvj420p.mp4", "FAILED");
+ RunColorFormatTest("yuvj420p.mp4", "ENDED");
}
IN_PROC_BROWSER_TEST_F(MediaTest, MAYBE(Yuv422pH264)) {
@@ -240,8 +242,7 @@ IN_PROC_BROWSER_TEST_F(MediaTest, MAYBE(Yuv422pH264)) {
}
IN_PROC_BROWSER_TEST_F(MediaTest, MAYBE(Yuv444pH264)) {
- // TODO(scherkus): Support YUV444 http://crbug.com/104711
- RunColorFormatTest("yuv444p.mp4", "ERROR");
+ RunColorFormatTest("yuv444p.mp4", "ENDED");
}
#if defined(OS_CHROMEOS)
diff --git a/chromium/content/browser/media/media_browsertest.h b/chromium/content/browser/media/media_browsertest.h
index 6637ead2706..b242db30259 100644
--- a/chromium/content/browser/media/media_browsertest.h
+++ b/chromium/content/browser/media/media_browsertest.h
@@ -8,7 +8,7 @@
#include <utility>
#include <vector>
-#include "content/test/content_browser_test.h"
+#include "content/public/test/content_browser_test.h"
namespace content {
@@ -26,8 +26,6 @@ class MediaBrowserTest : public ContentBrowserTest {
static const char kError[];
static const char kFailed[];
- virtual void SetUp() OVERRIDE;
-
// Runs a html page with a list of URL query parameters.
// If http is true, the test starts a local http test server to load the test
// page, otherwise a local file URL is loaded inside the content shell.
diff --git a/chromium/content/browser/media/media_canplaytype_browsertest.cc b/chromium/content/browser/media/media_canplaytype_browsertest.cc
new file mode 100644
index 00000000000..cfcfd5ae2c5
--- /dev/null
+++ b/chromium/content/browser/media/media_canplaytype_browsertest.cc
@@ -0,0 +1,1065 @@
+// 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 <string>
+
+#include "content/browser/media/media_browsertest.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/shell/browser/shell.h"
+
+#if defined(OS_ANDROID)
+#include "base/android/build_info.h"
+#endif
+
+const char kProbably[] = "probably";
+const char kMaybe[] = "maybe";
+const char kNot[] = "";
+
+#if defined(USE_PROPRIETARY_CODECS)
+const char kPropProbably[] = "probably";
+const char kPropMaybe[] = "maybe";
+#else
+const char kPropProbably[] = "";
+const char kPropMaybe[] = "";
+#endif // USE_PROPRIETARY_CODECS
+
+// TODO(amogh.bihani): Change the opus tests when opus is on
+// Android. (http://crbug.com/318436).
+#if !defined(OS_ANDROID)
+const char kOggVideoProbably[] = "probably";
+const char kOggVideoMaybe[] = "maybe";
+const char kTheoraProbably[] = "probably";
+const char kOpusProbably[] = "probably";
+#if defined(USE_PROPRIETARY_CODECS)
+const char kTheoraAndPropProbably[] = "probably";
+const char kOpusAndPropProbably[] = "probably";
+#else
+const char kTheoraAndPropProbably[] = "";
+const char kOpusAndPropProbably[] = "";
+#endif // USE_PROPRIETARY_CODECS
+#else
+const char kOggVideoProbably[] = "";
+const char kOggVideoMaybe[] = "";
+const char kTheoraProbably[] = "";
+const char kOpusProbably[] = "";
+const char kTheoraAndPropProbably[] = "maybe";
+const char kOpusAndPropProbably[] = "maybe";
+#endif // !OS_ANDROID
+
+namespace content {
+
+class MediaCanPlayTypeTest : public MediaBrowserTest {
+public:
+ MediaCanPlayTypeTest() : url_("about:blank") { }
+
+ virtual void SetUpOnMainThread() OVERRIDE {
+ NavigateToURL(shell(), url_);
+ }
+
+ std::string CanPlay(const std::string& type) {
+ std::string command("document.createElement('video').canPlayType(");
+ command.append(type);
+ command.append(")");
+
+ std::string result;
+ EXPECT_TRUE(ExecuteScriptAndExtractString(
+ shell()->web_contents(),
+ "window.domAutomationController.send(" + command + ");",
+ &result));
+ return result;
+ }
+
+private:
+ GURL url_;
+ DISALLOW_COPY_AND_ASSIGN(MediaCanPlayTypeTest);
+};
+
+IN_PROC_BROWSER_TEST_F(MediaCanPlayTypeTest, CodecSupportTest_wav) {
+ EXPECT_EQ(kMaybe, CanPlay("'audio/wav'"));
+ EXPECT_EQ(kProbably, CanPlay("'audio/wav; codecs=\"1\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/wav; codecs=\"vorbis\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/wav; codecs=\"theora\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/wav; codecs=\"vp8\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/wav; codecs=\"vp8.0\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/wav; codecs=\"vp9\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/wav; codecs=\"vp9.0\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/wav; codecs=\"opus\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/wav; codecs=\"avc1\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/wav; codecs=\"avc3\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/wav; codecs=\"mp4a\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/wav; codecs=\"avc1.4D401E\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/wav; codecs=\"avc3.64001F\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/wav; codecs=\"mp4a.40.5\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/wav; codecs=\"1, mp4a\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/wav; codecs=\"1, opus\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/wav; codecs=\"1, theora\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/wav; codecs=\"1, vorbis\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/wav; codecs=\"opus, vorbis\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/wav; codecs=\"opus, theora\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/wav; codecs=\"vorbis, mp4a\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/wav; codecs=\"unknown\"'"));
+
+ EXPECT_EQ(kMaybe, CanPlay("'audio/x-wav'"));
+ EXPECT_EQ(kProbably, CanPlay("'audio/x-wav; codecs=\"1\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/x-wav; codecs=\"vorbis\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-wav; codecs=\"theora\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/x-wav; codecs=\"vp8\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-wav; codecs=\"vp8.0\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-wav; codecs=\"vp9\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-wav; codecs=\"vp9.0\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-wav; codecs=\"opus\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/x-wav; codecs=\"avc1\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-wav; codecs=\"avc3\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-wav; codecs=\"mp4a\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-wav; codecs=\"avc1.4D401E\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-wav; codecs=\"avc3.64001F\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-wav; codecs=\"mp4a.40.5\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/x-wav; codecs=\"1, mp4a\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-wav; codecs=\"1, opus\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-wav; codecs=\"1, theora\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-wav; codecs=\"1, vorbis\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-wav; codecs=\"opus, vorbis\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-wav; codecs=\"opus, theora\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-wav; codecs=\"vorbis, mp4a\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/x-wav; codecs=\"unknown\"'"));
+}
+
+IN_PROC_BROWSER_TEST_F(MediaCanPlayTypeTest, CodecSupportTest_webm) {
+ // On Android, VP9 is supported only on KitKat and above (API level 19).
+ std::string VP9Probably = "probably";
+ std::string VP9AndOpusProbably = "probably";
+#if defined(OS_ANDROID)
+ VP9AndOpusProbably = "";
+ if (base::android::BuildInfo::GetInstance()->sdk_int() < 19)
+ VP9Probably = "";
+#endif
+ EXPECT_EQ(kMaybe, CanPlay("'video/webm'"));
+
+ EXPECT_EQ(kProbably, CanPlay("'video/webm; codecs=\"vp8\"'"));
+ EXPECT_EQ(kProbably, CanPlay("'video/webm; codecs=\"vp8.0\"'"));
+ EXPECT_EQ(kProbably, CanPlay("'video/webm; codecs=\"vp8, vorbis\"'"));
+ EXPECT_EQ(kProbably, CanPlay("'video/webm; codecs=\"vp8.0, vorbis\"'"));
+ EXPECT_EQ(kOpusProbably, CanPlay("'video/webm; codecs=\"vp8, opus\"'"));
+ EXPECT_EQ(kOpusProbably, CanPlay("'video/webm; codecs=\"vp8.0, opus\"'"));
+
+ EXPECT_EQ(VP9Probably, CanPlay("'video/webm; codecs=\"vp9\"'"));
+ EXPECT_EQ(VP9Probably, CanPlay("'video/webm; codecs=\"vp9.0\"'"));
+ EXPECT_EQ(VP9Probably, CanPlay("'video/webm; codecs=\"vp9, vorbis\"'"));
+ EXPECT_EQ(VP9Probably, CanPlay("'video/webm; codecs=\"vp9.0, vorbis\"'"));
+ EXPECT_EQ(VP9AndOpusProbably, CanPlay("'video/webm; codecs=\"vp9, opus\"'"));
+ EXPECT_EQ(VP9AndOpusProbably,
+ CanPlay("'video/webm; codecs=\"vp9.0, opus\"'"));
+
+ EXPECT_EQ(VP9Probably, CanPlay("'video/webm; codecs=\"vp8, vp9\"'"));
+ EXPECT_EQ(VP9Probably, CanPlay("'video/webm; codecs=\"vp8.0, vp9.0\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/webm; codecs=\"vp8, theora\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/webm; codecs=\"vp8, avc1\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/webm; codecs=\"vp9, avc3\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'video/webm; codecs=\"vp8, 1\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/webm; codecs=\"vp8.0, 1\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/webm; codecs=\"vp8, mp4a.40.2\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/webm; codecs=\"vp8.0, mp4a.40.2\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'video/webm; codecs=\"vp9, 1\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/webm; codecs=\"vp9.0, 1\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/webm; codecs=\"vp9, mp4a.40.2\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/webm; codecs=\"vp9.0, mp4a.40.2\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'video/webm; codecs=\"theora\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/webm; codecs=\"1\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'video/webm; codecs=\"avc1\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/webm; codecs=\"avc3\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/webm; codecs=\"mp4a\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/webm; codecs=\"avc1.4D401E\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/webm; codecs=\"avc3.64001F\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/webm; codecs=\"mp4a.40.2\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'video/webm; codecs=\"VP8\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/webm; codecs=\"VP8.0\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/webm; codecs=\"VP9\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/webm; codecs=\"Vp9.0\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'video/webm; codecs=\"VP8, Vorbis\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/webm; codecs=\"vp8, Vorbis\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/webm; codecs=\"VP9, Opus\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/webm; codecs=\"vp9, Opus\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'video/webm; codecs=\"unknown\"'"));
+
+ EXPECT_EQ(kMaybe, CanPlay("'audio/webm'"));
+ EXPECT_EQ(kProbably, CanPlay("'audio/webm; codecs=\"vorbis\"'"));
+ EXPECT_EQ(kOpusProbably, CanPlay("'audio/webm; codecs=\"opus\"'"));
+ EXPECT_EQ(kOpusProbably, CanPlay("'audio/webm; codecs=\"opus, vorbis\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/webm; codecs=\"vp8\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/webm; codecs=\"vp8.0\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/webm; codecs=\"vp8, vorbis\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/webm; codecs=\"vp8.0, vorbis\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/webm; codecs=\"vp8, opus\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/webm; codecs=\"vp8.0, opus\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/webm; codecs=\"vp9\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/webm; codecs=\"vp9.0\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/webm; codecs=\"vp9, vorbis\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/webm; codecs=\"vp9.0, vorbis\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/webm; codecs=\"vp9, opus\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/webm; codecs=\"vp9.0, opus\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/webm; codecs=\"1\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/webm; codecs=\"1, opus\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/webm; codecs=\"1, vorbis\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/webm; codecs=\"vorbis, mp4a\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/webm; codecs=\"avc1\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/webm; codecs=\"avc3\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/webm; codecs=\"mp4a\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/webm; codecs=\"avc1.4D401E\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/webm; codecs=\"avc3.64001F\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/webm; codecs=\"mp4a.40.2\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/webm; codecs=\"unknown\"'"));
+}
+
+IN_PROC_BROWSER_TEST_F(MediaCanPlayTypeTest, CodecSupportTest_ogg) {
+ EXPECT_EQ(kOggVideoMaybe, CanPlay("'video/ogg'"));
+ EXPECT_EQ(kOggVideoProbably, CanPlay("'video/ogg; codecs=\"theora\"'"));
+ EXPECT_EQ(kOggVideoProbably,
+ CanPlay("'video/ogg; codecs=\"theora, vorbis\"'"));
+ EXPECT_EQ(kOggVideoProbably,
+ CanPlay("'video/ogg; codecs=\"theora, opus\"'"));
+ EXPECT_EQ(kOggVideoProbably,
+ CanPlay("'video/ogg; codecs=\"opus, vorbis\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'video/ogg; codecs=\"vp8\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/ogg; codecs=\"vp8.0\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/ogg; codecs=\"vp9\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/ogg; codecs=\"vp9.0\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'video/ogg; codecs=\"avc1\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/ogg; codecs=\"avc3\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/ogg; codecs=\"mp4a\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/ogg; codecs=\"avc1, mp4a\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/ogg; codecs=\"avc1, vorbis\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/ogg; codecs=\"avc3, mp4a\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/ogg; codecs=\"avc3, vorbis\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/ogg; codecs=\"avc1, vp8\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/ogg; codecs=\"avc3, vp9\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/ogg; codecs=\"avc1, avc3\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'video/ogg; codecs=\"avc1.4D401E\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/ogg; codecs=\"avc3.64001F\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/ogg; codecs=\"mp4a.4.02\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/ogg; codecs=\"avc1.4D401E, mp4a.40.2\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/ogg; codecs=\"avc3.64001F, mp4a.40.2\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/ogg; codecs=\"avc1.4D401E, vorbis\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/ogg; codecs=\"avc3.64001F, vorbis\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'video/ogg; codecs=\"Theora\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/ogg; codecs=\"Opus\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/ogg; codecs=\"Vorbis\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/ogg; codecs=\"Theora, Opus\"'"));
+ EXPECT_EQ(kNot, CanPlay("'video/ogg; codecs=\"Theora, Vorbis\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'video/ogg; codecs=\"unknown\"'"));
+
+ EXPECT_EQ(kMaybe, CanPlay("'audio/ogg'"));
+ EXPECT_EQ(kProbably, CanPlay("'audio/ogg; codecs=\"vorbis\"'"));
+ EXPECT_EQ(kOpusProbably, CanPlay("'audio/ogg; codecs=\"opus\"'"));
+ EXPECT_EQ(kOpusProbably, CanPlay("'audio/ogg; codecs=\"vorbis, opus\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/ogg; codecs=\"theora\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/ogg; codecs=\"theora, vorbis\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/ogg; codecs=\"theora, opus\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/ogg; codecs=\"opus, 1\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/ogg; codecs=\"vorbis, 1\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/ogg; codecs=\"vp8\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/ogg; codecs=\"vp8.0\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/ogg; codecs=\"vp9\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/ogg; codecs=\"vp9.0\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/ogg; codecs=\"avc1\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/ogg; codecs=\"avc3\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/ogg; codecs=\"mp4a\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/ogg; codecs=\"avc1.4D401E\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/ogg; codecs=\"avc3.64001F\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/ogg; codecs=\"mp4a.40.2\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/ogg; codecs=\"Theora\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/ogg; codecs=\"Opus\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/ogg; codecs=\"Vorbis\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/ogg; codecs=\"Theora, Vorbis\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/ogg; codecs=\"Theora, Opus\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/ogg; codecs=\"unknown\"'"));
+
+ EXPECT_EQ(kMaybe, CanPlay("'application/ogg'"));
+ EXPECT_EQ(kProbably, CanPlay("'application/ogg; codecs=\"vorbis\"'"));
+ EXPECT_EQ(kTheoraProbably, CanPlay("'application/ogg; codecs=\"theora\"'"));
+ EXPECT_EQ(kOpusProbably, CanPlay("'application/ogg; codecs=\"opus\"'"));
+ EXPECT_EQ(kTheoraProbably,
+ CanPlay("'application/ogg; codecs=\"theora, vorbis\"'"));
+ EXPECT_EQ(kTheoraProbably,
+ CanPlay("'application/ogg; codecs=\"theora, opus\"'"));
+ EXPECT_EQ(kOpusProbably,
+ CanPlay("'application/ogg; codecs=\"opus, vorbis\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'application/ogg; codecs=\"vp8\"'"));
+ EXPECT_EQ(kNot, CanPlay("'application/ogg; codecs=\"vp8.0\"'"));
+ EXPECT_EQ(kNot, CanPlay("'application/ogg; codecs=\"vp9\"'"));
+ EXPECT_EQ(kNot, CanPlay("'application/ogg; codecs=\"vp9.0\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'application/ogg; codecs=\"avc1\"'"));
+ EXPECT_EQ(kNot, CanPlay("'application/ogg; codecs=\"avc3\"'"));
+ EXPECT_EQ(kNot, CanPlay("'application/ogg; codecs=\"mp4a\"'"));
+ EXPECT_EQ(kNot, CanPlay("'application/ogg; codecs=\"avc1, mp4a\"'"));
+ EXPECT_EQ(kNot, CanPlay("'application/ogg; codecs=\"avc1, vorbis\"'"));
+ EXPECT_EQ(kNot, CanPlay("'application/ogg; codecs=\"avc3, mp4a\"'"));
+ EXPECT_EQ(kNot, CanPlay("'application/ogg; codecs=\"avc3, vorbis\"'"));
+ EXPECT_EQ(kNot, CanPlay("'application/ogg; codecs=\"avc1, vp8\"'"));
+ EXPECT_EQ(kNot, CanPlay("'application/ogg; codecs=\"avc3, vp9\"'"));
+ EXPECT_EQ(kNot, CanPlay("'application/ogg; codecs=\"avc1, avc3\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'application/ogg; codecs=\"avc1.4D401E\"'"));
+ EXPECT_EQ(kNot, CanPlay("'application/ogg; codecs=\"avc3.64001F\"'"));
+ EXPECT_EQ(kNot, CanPlay("'application/ogg; codecs=\"mp4a.40.2\"'"));
+ EXPECT_EQ(kNot,
+ CanPlay("'application/ogg; codecs=\"avc1.4D401E, mp4a.40.2\"'"));
+ EXPECT_EQ(kNot,
+ CanPlay("'application/ogg; codecs=\"avc3.64001F, mp4a.40.2\"'"));
+ EXPECT_EQ(kNot, CanPlay("'application/ogg; codecs=\"avc1.4D401E, vorbis\"'"));
+ EXPECT_EQ(kNot, CanPlay("'application/ogg; codecs=\"avc3.64001F, vorbis\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'application/ogg; codecs=\"Theora\"'"));
+ EXPECT_EQ(kNot, CanPlay("'application/ogg; codecs=\"Vorbis\"'"));
+ EXPECT_EQ(kNot, CanPlay("'application/ogg; codecs=\"Opus\"'"));
+ EXPECT_EQ(kNot, CanPlay("'application/ogg; codecs=\"Theora, Vorbis\"'"));
+ EXPECT_EQ(kNot, CanPlay("'application/ogg; codecs=\"Theora, Opus\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'application/ogg; codecs=\"unknown\"'"));
+}
+
+IN_PROC_BROWSER_TEST_F(MediaCanPlayTypeTest, CodecSupportTest_mp3) {
+ EXPECT_EQ(kNot, CanPlay("'video/mp3'"));
+ EXPECT_EQ(kNot, CanPlay("'video/mpeg'"));
+ EXPECT_EQ(kNot, CanPlay("'video/x-mp3'"));
+
+ // audio/mpeg without a codecs parameter (RFC 3003 compliant)
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/mpeg'"));
+
+ // audio/mpeg with mp3 in codecs parameter. (Not RFC compliant, but
+ // very common in the wild so it is a defacto standard).
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/mpeg; codecs=\"mp3\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"avc1\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"avc3\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"avc1.4D401E\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"avc3.64001F\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"mp4a\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"mp4a.40.2\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"avc1.unknown\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"avc3.unknown\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"mp4a.unknown\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"avc1.\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"avc3.\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"mp4a.\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"vorbis\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"opus\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"theora\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"vp8\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"vp8.0\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"vp9\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"vp9.0\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"AVC1\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"AVC1.4d401e\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"AVC3\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"AVC3.64001f\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"MP4A\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"MP4A.40.2\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"AVC1, MP4\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"AVC3, MP4\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\", AVC1.4D401E, MP4.40.2\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\", AVC3.64001F, MP4.40.2\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"avc2\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"avc4\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"avc1x\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"avc3x\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"mp4ax\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"unknown\"'"));
+
+ // audio/mp3 does not allow any codecs parameter
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/mp3'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"avc1\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"avc3\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"avc1.4D401E\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"avc3.64001F\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"mp4a\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"mp4a.40.2\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"avc1.unknown\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"avc3.unknown\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"mp4a.unknown\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"avc1.\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"avc3.\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"mp4a.\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"vorbis\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"opus\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"theora\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"vp8\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"vp8.0\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"vp9\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"vp9.0\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"AVC1\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"AVC1.4d401e\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"AVC3\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"AVC3.64001f\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"MP4A\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"MP4A.40.2\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"AVC1, MP4\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"AVC3, MP4\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\", AVC1.4D401E, MP4.40.2\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\", AVC3.64001F, MP4.40.2\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"avc2\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"avc4\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"avc1x\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"avc3x\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"mp4ax\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"unknown\"'"));
+
+ // audio/x-mp3 does not allow any codecs parameter
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/x-mp3'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"avc1\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"avc3\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"avc1.4D401E\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"avc3.64001F\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"mp4a\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"mp4a.40.2\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"avc1.unknown\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"avc3.unknown\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"mp4a.unknown\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"vorbis\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"opus\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"theora\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"vp8\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"vp8.0\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"vp9\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"vp9.0\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"AVC1\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"AVC1.4d401e\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"AVC3\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"AVC3.64001f\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"MP4A\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"MP4A.40.2\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"AVC1, MP4\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"AVC3, MP4\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\", AVC1.4D401E, MP4.40.2\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\", AVC3.64001F, MP4.40.2\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"avc2\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"avc4\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"avc1x\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"avc3x\"'"));
+ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"mp4ax\"'"));
+
+ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"unknown\"'"));
+}
+
+IN_PROC_BROWSER_TEST_F(MediaCanPlayTypeTest, CodecSupportTest_mp4) {
+ // TODO(amogh.bihani): Change this expectation when bug 53193 is fixed.
+ std::string PropAndVP9Probably = "";
+#if defined (OS_ANDROID)
+ if (base::android::BuildInfo::GetInstance()->sdk_int() < 19)
+ PropAndVP9Probably = "maybe";
+ else
+ PropAndVP9Probably = "probably";
+#else
+#if defined(USE_PROPRIETARY_CODECS)
+ PropAndVP9Probably = "probably";
+#endif // USE_PROPRIETARY_CODECS
+#endif // OS_ANDROID
+ EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4'"));
+
+ EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"avc1\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"avc3\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"mp4a\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"avc1, mp4a\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"avc3, mp4a\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"avc1, avc3\"'"));
+
+ EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"avc1.4D401E\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"avc3.64001F\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"mp4a.40.2\"'"));
+ EXPECT_EQ(kPropProbably,
+ CanPlay("'video/mp4; codecs=\"avc1.4D401E, mp4a.40.2\"'"));
+ EXPECT_EQ(kPropProbably,
+ CanPlay("'video/mp4; codecs=\"avc3.64001F, mp4a.40.5\"'"));
+
+ // TODO(amogh.bihani): Change these tests when bug 53193 is fixed.
+ // http://crbug.com/53193 ----------------------------------------------------
+ EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"avc1.unknown\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"avc3.unknown\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"mp4a.unknown\"'"));
+
+ EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"avc1.\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"avc3.\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"mp4a.\"'"));
+
+ EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"vp8\"'"));
+ EXPECT_EQ(PropAndVP9Probably, CanPlay("'video/mp4; codecs=\"vp9\"'"));
+
+ EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"vorbis\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"avc1, vorbis\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"avc3, vorbis\"'"));
+ EXPECT_EQ(kPropProbably,
+ CanPlay("'video/mp4; codecs=\"avc1.4D401E, vorbis\"'"));
+ EXPECT_EQ(kPropProbably,
+ CanPlay("'video/mp4; codecs=\"avc3.64001F, vorbis\"'"));
+
+ EXPECT_EQ(kOpusAndPropProbably, CanPlay("'video/mp4; codecs=\"opus\"'"));
+ EXPECT_EQ(kOpusAndPropProbably, CanPlay("'video/mp4; codecs=\"vp8, opus\"'"));
+ EXPECT_EQ(kOpusAndPropProbably, CanPlay("'video/mp4; codecs=\"vp9, opus\"'"));
+
+ EXPECT_EQ(kTheoraAndPropProbably, CanPlay("'video/mp4; codecs=\"theora\"'"));
+ EXPECT_EQ(kTheoraAndPropProbably,
+ CanPlay("'video/mp4; codecs=\"theora, vorbis\"'"));
+ EXPECT_EQ(kTheoraAndPropProbably,
+ CanPlay("'video/mp4; codecs=\"theora, mp4a\"'"));
+ EXPECT_EQ(kTheoraAndPropProbably,
+ CanPlay("'video/mp4; codecs=\"theora, mp4a.40.2\"'"));
+ EXPECT_EQ(kTheoraAndPropProbably,
+ CanPlay("'video/mp4; codecs=\"theora, avc1\"'"));
+ EXPECT_EQ(kTheoraAndPropProbably,
+ CanPlay("'video/mp4; codecs=\"theora, avc3\"'"));
+ EXPECT_EQ(kTheoraAndPropProbably,
+ CanPlay("'video/mp4; codecs=\"theora, avc1.4D401E\"'"));
+ EXPECT_EQ(kTheoraAndPropProbably,
+ CanPlay("'video/mp4; codecs=\"theora, avc3.64001F\"'"));
+
+ EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4; codecs=\"AVC1\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4; codecs=\"AVC1.4d401e\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4; codecs=\"AVC3\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4; codecs=\"AVC3.64001f\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4; codecs=\"MP4A\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4; codecs=\"MP4A.40.2\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4; codecs=\"AVC1, MP4\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4; codecs=\"AVC3, MP4\"'"));
+ EXPECT_EQ(kPropMaybe,
+ CanPlay("'video/mp4; codecs=\", AVC1.4D401E, MP4.40.2\"'"));
+ EXPECT_EQ(kPropMaybe,
+ CanPlay("'video/mp4; codecs=\", AVC3.64001F, MP4.40.2\"'"));
+
+ EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4; codecs=\"avc2\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4; codecs=\"avc4\"'"));
+
+ EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4; codecs=\"avc1x\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4; codecs=\"avc3x\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4; codecs=\"mp4ax\"'"));
+
+ EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4; codecs=\"unknown\"'"));
+ // ---------------------------------------------------------------------------
+
+ EXPECT_EQ(kPropMaybe, CanPlay("'video/x-m4v'"));
+
+ EXPECT_EQ(kPropProbably, CanPlay("'video/x-m4v; codecs=\"avc1\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'video/x-m4v; codecs=\"avc3\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'video/x-m4v; codecs=\"mp4a\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'video/x-m4v; codecs=\"avc1, mp4a\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'video/x-m4v; codecs=\"avc3, mp4a\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'video/x-m4v; codecs=\"avc1, avc3\"'"));
+
+ EXPECT_EQ(kPropProbably, CanPlay("'video/x-m4v; codecs=\"avc1.4D401E\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'video/x-m4v; codecs=\"avc3.64001F\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'video/x-m4v; codecs=\"mp4a.40.2\"'"));
+ EXPECT_EQ(kPropProbably,
+ CanPlay("'video/x-m4v; codecs=\"avc1.4D401E, mp4a.40.2\"'"));
+ EXPECT_EQ(kPropProbably,
+ CanPlay("'video/x-m4v; codecs=\"avc3.64001F, mp4a.40.5\"'"));
+
+ // TODO(amogh.bihani): Change these tests when bug 53193 is fixed.
+ // http://crbug.com/53193 ----------------------------------------------------
+ EXPECT_EQ(kPropProbably, CanPlay("'video/x-m4v; codecs=\"avc1.unknown\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'video/x-m4v; codecs=\"avc3.unknown\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'video/x-m4v; codecs=\"mp4a.unknown\"'"));
+
+ EXPECT_EQ(kPropProbably, CanPlay("'video/x-m4v; codecs=\"avc1.\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'video/x-m4v; codecs=\"avc3.\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'video/x-m4v; codecs=\"mp4a.\"'"));
+
+ EXPECT_EQ(kPropProbably, CanPlay("'video/x-m4v; codecs=\"vp8\"'"));
+ EXPECT_EQ(PropAndVP9Probably, CanPlay("'video/x-m4v; codecs=\"vp9\"'"));
+
+ EXPECT_EQ(kPropProbably, CanPlay("'video/x-m4v; codecs=\"vorbis\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'video/x-m4v; codecs=\"avc1, vorbis\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'video/x-m4v; codecs=\"avc3, vorbis\"'"));
+ EXPECT_EQ(kPropProbably,
+ CanPlay("'video/x-m4v; codecs=\"avc1.4D401E, vorbis\"'"));
+ EXPECT_EQ(kPropProbably,
+ CanPlay("'video/x-m4v; codecs=\"avc3.64001F, vorbis\"'"));
+
+ EXPECT_EQ(kOpusAndPropProbably, CanPlay("'video/x-m4v; codecs=\"opus\"'"));
+ EXPECT_EQ(kOpusAndPropProbably,
+ CanPlay("'video/x-m4v; codecs=\"vp8, opus\"'"));
+ EXPECT_EQ(kOpusAndPropProbably,
+ CanPlay("'video/x-m4v; codecs=\"vp9, opus\"'"));
+
+ EXPECT_EQ(kTheoraAndPropProbably,
+ CanPlay("'video/x-m4v; codecs=\"theora\"'"));
+ EXPECT_EQ(kTheoraAndPropProbably,
+ CanPlay("'video/x-m4v; codecs=\"theora, vorbis\"'"));
+ EXPECT_EQ(kTheoraAndPropProbably,
+ CanPlay("'video/x-m4v; codecs=\"theora, mp4a\"'"));
+ EXPECT_EQ(kTheoraAndPropProbably,
+ CanPlay("'video/x-m4v; codecs=\"theora, mp4a.40.2\"'"));
+ EXPECT_EQ(kTheoraAndPropProbably,
+ CanPlay("'video/x-m4v; codecs=\"theora, avc1\"'"));
+ EXPECT_EQ(kTheoraAndPropProbably,
+ CanPlay("'video/x-m4v; codecs=\"theora, avc3\"'"));
+ EXPECT_EQ(kTheoraAndPropProbably,
+ CanPlay("'video/x-m4v; codecs=\"theora, avc1.4D401E\"'"));
+ EXPECT_EQ(kTheoraAndPropProbably,
+ CanPlay("'video/x-m4v; codecs=\"theora, avc3.64001F\"'"));
+
+ EXPECT_EQ(kPropMaybe, CanPlay("'video/x-m4v; codecs=\"AVC1\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'video/x-m4v; codecs=\"AVC1.4d401e\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'video/x-m4v; codecs=\"AVC3\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'video/x-m4v; codecs=\"AVC3.64001f\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'video/x-m4v; codecs=\"MP4A\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'video/x-m4v; codecs=\"MP4A.40.2\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'video/x-m4v; codecs=\"AVC1, MP4\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'video/x-m4v; codecs=\"AVC3, MP4\"'"));
+ EXPECT_EQ(kPropMaybe,
+ CanPlay("'video/x-m4v; codecs=\", AVC1.4D401E, MP4.40.2\"'"));
+ EXPECT_EQ(kPropMaybe,
+ CanPlay("'video/x-m4v; codecs=\", AVC3.64001F, MP4.40.2\"'"));
+
+ EXPECT_EQ(kPropMaybe, CanPlay("'video/x-m4v; codecs=\"avc2\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'video/x-m4v; codecs=\"avc4\"'"));
+
+ EXPECT_EQ(kPropMaybe, CanPlay("'video/x-m4v; codecs=\"avc1x\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'video/x-m4v; codecs=\"avc3x\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'video/x-m4v; codecs=\"mp4ax\"'"));
+
+ EXPECT_EQ(kPropMaybe, CanPlay("'video/x-m4v; codecs=\"unknown\"'"));
+ // ---------------------------------------------------------------------------
+
+ EXPECT_EQ(kPropMaybe, CanPlay("'audio/mp4'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"mp4a\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"mp4a.40.2\"'"));
+
+ // TODO(amogh.bihani): Change these tests when bug 53193 is fixed.
+ // http://crbug.com/53193 ----------------------------------------------------
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"avc1\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"avc3\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"avc1, mp4a\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"avc3, mp4a\"'"));
+
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"avc1.4D401E\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"avc3.64001F\"'"));
+
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"avc1.unknown\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"avc3.unknown\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"mp4a.unknown\"'"));
+
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"avc1.\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"avc3.\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"mp4a.\"'"));
+
+ EXPECT_EQ(kPropProbably,
+ CanPlay("'audio/mp4; codecs=\"avc1.4D401E, mp4a.40.2\"'"));
+ EXPECT_EQ(kPropProbably,
+ CanPlay("'audio/mp4; codecs=\"avc3.64001F mp4a.40.2\"'"));
+ EXPECT_EQ(kPropProbably,
+ CanPlay("'audio/mp4; codecs=\"mp4a, vorbis\"'"));
+ EXPECT_EQ(kPropProbably,
+ CanPlay("'audio/mp4; codecs=\"mp4a.40.2, vorbis\"'"));
+
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"vorbis\"'"));
+
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"vp8\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"vp8.0\"'"));
+ EXPECT_EQ(PropAndVP9Probably, CanPlay("'audio/mp4; codecs=\"vp9\"'"));
+ EXPECT_EQ(PropAndVP9Probably, CanPlay("'audio/mp4; codecs=\"vp9.0\"'"));
+
+ EXPECT_EQ(kOpusAndPropProbably, CanPlay("'audio/mp4; codecs=\"opus\"'"));
+ EXPECT_EQ(kOpusAndPropProbably,
+ CanPlay("'audio/mp4; codecs=\"mp4a, opus\"'"));
+ EXPECT_EQ(kOpusAndPropProbably,
+ CanPlay("'audio/mp4; codecs=\"vorbis, opus\"'"));
+ EXPECT_EQ(kOpusAndPropProbably, CanPlay("'audio/mp4; codecs=\"vp8, opus\"'"));
+ EXPECT_EQ(kOpusAndPropProbably, CanPlay("'audio/mp4; codecs=\"vp9, opus\"'"));
+
+ EXPECT_EQ(kTheoraAndPropProbably, CanPlay("'audio/mp4; codecs=\"theora\"'"));
+ EXPECT_EQ(kTheoraAndPropProbably,
+ CanPlay("'audio/mp4; codecs=\"theora, vorbis\"'"));
+ EXPECT_EQ(kTheoraAndPropProbably,
+ CanPlay("'audio/mp4; codecs=\"theora, mp4a\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"avc1, vorbis\"'"));
+
+ EXPECT_EQ(kPropMaybe, CanPlay("'audio/mp4; codecs=\"AVC1\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'audio/mp4; codecs=\"AVC1.4d401e\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'audio/mp4; codecs=\"AVC3\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'audio/mp4; codecs=\"AVC3.64001f\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'audio/mp4; codecs=\"MP4A\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'audio/mp4; codecs=\"MP4A.40.2\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'audio/mp4; codecs=\"AVC1, MP4\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'audio/mp4; codecs=\"AVC3, MP4\"'"));
+ EXPECT_EQ(kPropMaybe,
+ CanPlay("'audio/mp4; codecs=\", AVC1.4D401E, MP4.40.2\"'"));
+ EXPECT_EQ(kPropMaybe,
+ CanPlay("'audio/mp4; codecs=\", AVC3.64001F, MP4.40.2\"'"));
+
+ EXPECT_EQ(kPropMaybe, CanPlay("'audio/mp4; codecs=\"avc2\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'audio/mp4; codecs=\"avc4\"'"));
+
+ EXPECT_EQ(kPropMaybe, CanPlay("'audio/mp4; codecs=\"avc1x\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'audio/mp4; codecs=\"avc3x\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'audio/mp4; codecs=\"mp4ax\"'"));
+
+ EXPECT_EQ(kPropMaybe, CanPlay("'audio/mp4; codecs=\"unknown\"'"));
+ // ---------------------------------------------------------------------------
+
+ EXPECT_EQ(kPropMaybe, CanPlay("'audio/x-m4a'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/x-m4a; codecs=\"mp4a\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/x-m4a; codecs=\"mp4a.40.2\"'"));
+
+ // TODO(amogh.bihani): Change these tests when bug 53193 is fixed.
+ // http://crbug.com/53193 ----------------------------------------------------
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/x-m4a; codecs=\"avc1\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/x-m4a; codecs=\"avc3\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/x-m4a; codecs=\"avc1, mp4a\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/x-m4a; codecs=\"avc3, mp4a\"'"));
+
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/x-m4a; codecs=\"avc1.4D401E\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/x-m4a; codecs=\"avc3.64001F\"'"));
+
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/x-m4a; codecs=\"avc1.unknown\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/x-m4a; codecs=\"avc3.unknown\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/x-m4a; codecs=\"mp4a.unknown\"'"));
+
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/x-m4a; codecs=\"avc1.\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/x-m4a; codecs=\"avc3.\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/x-m4a; codecs=\"mp4a.\"'"));
+
+ EXPECT_EQ(kPropProbably,
+ CanPlay("'audio/x-m4a; codecs=\"avc1.4D401E, mp4a.40.2\"'"));
+ EXPECT_EQ(kPropProbably,
+ CanPlay("'audio/x-m4a; codecs=\"avc3.64001F mp4a.40.2\"'"));
+ EXPECT_EQ(kPropProbably,
+ CanPlay("'audio/x-m4a; codecs=\"mp4a, vorbis\"'"));
+ EXPECT_EQ(kPropProbably,
+ CanPlay("'audio/x-m4a; codecs=\"mp4a.40.2, vorbis\"'"));
+
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/x-m4a; codecs=\"vorbis\"'"));
+
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/x-m4a; codecs=\"vp8\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/x-m4a; codecs=\"vp8.0\"'"));
+ EXPECT_EQ(PropAndVP9Probably, CanPlay("'audio/x-m4a; codecs=\"vp9\"'"));
+ EXPECT_EQ(PropAndVP9Probably, CanPlay("'audio/x-m4a; codecs=\"vp9.0\"'"));
+
+ EXPECT_EQ(kOpusAndPropProbably, CanPlay("'audio/x-m4a; codecs=\"opus\"'"));
+ EXPECT_EQ(kOpusAndPropProbably,
+ CanPlay("'audio/x-m4a; codecs=\"mp4a, opus\"'"));
+ EXPECT_EQ(kOpusAndPropProbably,
+ CanPlay("'audio/x-m4a; codecs=\"vorbis, opus\"'"));
+ EXPECT_EQ(kOpusAndPropProbably,
+ CanPlay("'audio/x-m4a; codecs=\"vp8, opus\"'"));
+ EXPECT_EQ(kOpusAndPropProbably,
+ CanPlay("'audio/x-m4a; codecs=\"vp9, opus\"'"));
+
+ EXPECT_EQ(kTheoraAndPropProbably,
+ CanPlay("'audio/x-m4a; codecs=\"theora\"'"));
+ EXPECT_EQ(kTheoraAndPropProbably,
+ CanPlay("'audio/x-m4a; codecs=\"theora, vorbis\"'"));
+ EXPECT_EQ(kTheoraAndPropProbably,
+ CanPlay("'audio/x-m4a; codecs=\"theora, mp4a\"'"));
+ EXPECT_EQ(kPropProbably, CanPlay("'audio/x-m4a; codecs=\"avc1, vorbis\"'"));
+
+ EXPECT_EQ(kPropMaybe, CanPlay("'audio/x-m4a; codecs=\"AVC1\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'audio/x-m4a; codecs=\"AVC1.4d401e\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'audio/x-m4a; codecs=\"AVC3\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'audio/x-m4a; codecs=\"AVC3.64001f\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'audio/x-m4a; codecs=\"MP4A\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'audio/x-m4a; codecs=\"MP4A.40.2\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'audio/x-m4a; codecs=\"AVC1, MP4\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'audio/x-m4a; codecs=\"AVC3, MP4\"'"));
+ EXPECT_EQ(kPropMaybe,
+ CanPlay("'audio/x-m4a; codecs=\", AVC1.4D401E, MP4.40.2\"'"));
+ EXPECT_EQ(kPropMaybe,
+ CanPlay("'audio/x-m4a; codecs=\", AVC3.64001F, MP4.40.2\"'"));
+
+ EXPECT_EQ(kPropMaybe, CanPlay("'audio/x-m4a; codecs=\"avc2\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'audio/x-m4a; codecs=\"avc4\"'"));
+
+ EXPECT_EQ(kPropMaybe, CanPlay("'audio/x-m4a; codecs=\"avc1x\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'audio/x-m4a; codecs=\"avc3x\"'"));
+ EXPECT_EQ(kPropMaybe, CanPlay("'audio/x-m4a; codecs=\"mp4ax\"'"));
+
+ EXPECT_EQ(kPropMaybe, CanPlay("'audio/x-m4a; codecs=\"unknown\"'"));
+ // ---------------------------------------------------------------------------
+}
+
+IN_PROC_BROWSER_TEST_F(MediaCanPlayTypeTest, CodecSupportTest_HLS) {
+ // HLS are supported only on Android IceCreamSandwich and above (API level 14)
+ std::string HLSProbably = "";
+ std::string HLSMaybe = "";
+ std::string HLSAndVP9Probably = "";
+#if defined(OS_ANDROID)
+ int sdk = base::android::BuildInfo::GetInstance()->sdk_int();
+ if (sdk > 13) {
+ HLSProbably = "probably";
+ HLSMaybe = "maybe";
+ if (sdk < 19)
+ HLSAndVP9Probably = "maybe";
+ else
+ HLSAndVP9Probably = "probably";
+ }
+#endif
+ EXPECT_EQ(HLSMaybe, CanPlay("'application/x-mpegurl'"));
+
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/x-mpegurl; codecs=\"avc1\"'"));
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/x-mpegurl; codecs=\"avc3\"'"));
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/x-mpegurl; codecs=\"mp4a\"'"));
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/x-mpegurl; codecs=\"avc1, mp4a\"'"));
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/x-mpegurl; codecs=\"avc3, mp4a\"'"));
+
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/x-mpegurl; codecs=\"avc1.4D401E\"'"));
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/x-mpegurl; codecs=\"avc3.64001F\"'"));
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/x-mpegurl; codecs=\"mp4a.40.2\"'"));
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/x-mpegurl; codecs=\"avc1.4D401E, mp4a.40.2\"'"));
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/x-mpegurl; codecs=\"avc3.64001F, mp4a.40.5\"'"));
+
+ // TODO(amogh.bihani): Change these tests when bug 53193 is fixed.
+ // http://crbug.com/53193 ----------------------------------------------------
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/x-mpegurl; codecs=\"avc1.unknown\"'"));
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/x-mpegurl; codecs=\"avc3.unknown\"'"));
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/x-mpegurl; codecs=\"mp4a.unknown\"'"));
+
+ EXPECT_EQ(HLSProbably, CanPlay("'application/x-mpegurl; codecs=\"avc1.\"'"));
+ EXPECT_EQ(HLSProbably, CanPlay("'application/x-mpegurl; codecs=\"avc3.\"'"));
+ EXPECT_EQ(HLSProbably, CanPlay("'application/x-mpegurl; codecs=\"mp4a.\"'"));
+
+ EXPECT_EQ(HLSProbably, CanPlay("'application/x-mpegurl; codecs=\"vp8\"'"));
+ EXPECT_EQ(HLSAndVP9Probably,
+ CanPlay("'application/x-mpegurl; codecs=\"vp9\"'"));
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/x-mpegurl; codecs=\"vorbis\"'"));
+
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/x-mpegurl; codecs=\"avc1, vorbis\"'"));
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/x-mpegurl; codecs=\"avc3, vorbis\"'"));
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/x-mpegurl; codecs=\"avc1.4D401E, vorbis\"'"));
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/x-mpegurl; codecs=\"avc3.64001F, vorbis\"'"));
+
+ EXPECT_EQ(HLSMaybe, CanPlay("'application/x-mpegurl; codecs=\"opus\"'"));
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/x-mpegurl; codecs=\"vp8, opus\"'"));
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/x-mpegurl; codecs=\"vp9, opus\"'"));
+
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/x-mpegurl; codecs=\"theora\"'"));
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/x-mpegurl; codecs=\"theora, vorbis\"'"));
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/x-mpegurl; codecs=\"theora, mp4a\"'"));
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/x-mpegurl; codecs=\"theora, mp4a.40.2\"'"));
+
+ EXPECT_EQ(HLSMaybe, CanPlay("'application/x-mpegurl; codecs=\"AVC1\"'"));
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/x-mpegurl; codecs=\"AVC1.4d401e\"'"));
+ EXPECT_EQ(HLSMaybe, CanPlay("'application/x-mpegurl; codecs=\"AVC3\"'"));
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/x-mpegurl; codecs=\"AVC3.64001f\"'"));
+ EXPECT_EQ(HLSMaybe, CanPlay("'application/x-mpegurl; codecs=\"MP4A\"'"));
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/x-mpegurl; codecs=\"MP4A.40.2\"'"));
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/x-mpegurl; codecs=\"AVC1, MP4\"'"));
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/x-mpegurl; codecs=\"AVC3, MP4\"'"));
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/x-mpegurl; codecs=\", AVC1.4D401E, MP4.40.2\"'"));
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/x-mpegurl; codecs=\", AVC3.64001F, MP4.40.2\"'"));
+
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/x-mpegurl; codecs=\"avc2\"'"));
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/x-mpegurl; codecs=\"avc4\"'"));
+
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/x-mpegurl; codecs=\"avc1x\"'"));
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/x-mpegurl; codecs=\"avc3x\"'"));
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/x-mpegurl; codecs=\"mp4ax\"'"));
+
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/x-mpegurl; codecs=\"unknown\"'"));
+ // ---------------------------------------------------------------------------
+
+ EXPECT_EQ(HLSMaybe, CanPlay("'application/vnd.apple.mpegurl'"));
+
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc1\"'"));
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc3\"'"));
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"mp4a\"'"));
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc1, mp4a\"'"));
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc3, mp4a\"'"));
+
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc1.4D401E\"'"));
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc3.64001F\"'"));
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"mp4a.40.2\"'"));
+
+ // TODO(amogh.bihani): Change these tests when bug 53193 is fixed.
+ // http://crbug.com/53193 ----------------------------------------------------
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc1.unknown\"'"));
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc3.unknown\"'"));
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"mp4a.unknown\"'"));
+
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc1.\"'"));
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc3.\"'"));
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"mp4a.\"'"));
+
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"vp8\"'"));
+ EXPECT_EQ(HLSAndVP9Probably,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"vp9\"'"));
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"vorbis\"'"));
+
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc1, vorbis\"'"));
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc3, vorbis\"'"));
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc1.4D401E, vorbis\"'"));
+ EXPECT_EQ(HLSProbably,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc3.64001F, vorbis\"'"));
+
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"opus\"'"));
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"vp8, opus\"'"));
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"vp9, opus\"'"));
+
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"theora\"'"));
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"theora, vorbis\"'"));
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"theora, mp4a\"'"));
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"theora, mp4a.40.2\"'"));
+
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"AVC1\"'"));
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"AVC1.4d401e\"'"));
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"AVC3\"'"));
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"AVC3.64001f\"'"));
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"MP4A\"'"));
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"MP4A.40.2\"'"));
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"AVC1, MP4\"'"));
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"AVC3, MP4\"'"));
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/vnd.apple.mpegurl; "
+ "codecs=\", AVC1.4D401E, MP4.40.2\"'"));
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/vnd.apple.mpegurl; "
+ "codecs=\", AVC3.64001F, MP4.40.2\"'"));
+
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc2\"'"));
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc4\"'"));
+
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc1x\"'"));
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc3x\"'"));
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"mp4ax\"'"));
+
+ EXPECT_EQ(HLSMaybe,
+ CanPlay("'application/vnd.apple.mpegurl; codecs=\"unknown\"'"));
+ // ---------------------------------------------------------------------------
+}
+
+} // namespace content
diff --git a/chromium/content/browser/media/media_devices_monitor.cc b/chromium/content/browser/media/media_devices_monitor.cc
deleted file mode 100644
index 9f2c18353f1..00000000000
--- a/chromium/content/browser/media/media_devices_monitor.cc
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/public/browser/media_devices_monitor.h"
-
-#include "content/browser/browser_main_loop.h"
-#include "content/browser/renderer_host/media/media_stream_manager.h"
-#include "content/public/browser/browser_thread.h"
-
-namespace content {
-namespace {
-void EnsureMonitorCaptureDevicesInternal(
- MediaStreamManager* media_stream_manager) {
- media_stream_manager->EnsureDeviceMonitorStarted();
-}
-}
-
-void EnsureMonitorCaptureDevices() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::Bind(&EnsureMonitorCaptureDevicesInternal,
- BrowserMainLoop::GetInstance()->media_stream_manager()));
-}
-
-} // namespace content
diff --git a/chromium/content/browser/media/media_internals.cc b/chromium/content/browser/media/media_internals.cc
index 1ee42d17572..1d3f4b4c09d 100644
--- a/chromium/content/browser/media/media_internals.cc
+++ b/chromium/content/browser/media/media_internals.cc
@@ -5,6 +5,7 @@
#include "content/browser/media/media_internals.h"
#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_ui.h"
@@ -23,6 +24,38 @@ base::string16 SerializeUpdate(const std::string& function,
function, std::vector<const base::Value*>(1, value));
}
+std::string EffectsToString(int effects) {
+ if (effects == media::AudioParameters::NO_EFFECTS)
+ return "NO_EFFECTS";
+
+ struct {
+ int flag;
+ const char* name;
+ } flags[] = {
+ { media::AudioParameters::ECHO_CANCELLER, "ECHO_CANCELLER" },
+ { media::AudioParameters::DUCKING, "DUCKING" },
+ { media::AudioParameters::KEYBOARD_MIC, "KEYBOARD_MIC" },
+ };
+
+ std::string ret;
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(flags); ++i) {
+ if (effects & flags[i].flag) {
+ if (!ret.empty())
+ ret += " | ";
+ ret += flags[i].name;
+ effects &= ~flags[i].flag;
+ }
+ }
+
+ if (effects) {
+ if (!ret.empty())
+ ret += " | ";
+ ret += base::IntToString(effects);
+ }
+
+ return ret;
+}
+
const char kAudioLogStatusKey[] = "status";
const char kAudioLogUpdateFunction[] = "media.updateAudioComponent";
@@ -39,8 +72,7 @@ class AudioLogImpl : public media::AudioLog {
virtual void OnCreated(int component_id,
const media::AudioParameters& params,
- const std::string& input_device_id,
- const std::string& output_device_id) OVERRIDE;
+ const std::string& device_id) OVERRIDE;
virtual void OnStarted(int component_id) OVERRIDE;
virtual void OnStopped(int component_id) OVERRIDE;
virtual void OnClosed(int component_id) OVERRIDE;
@@ -72,20 +104,19 @@ AudioLogImpl::~AudioLogImpl() {}
void AudioLogImpl::OnCreated(int component_id,
const media::AudioParameters& params,
- const std::string& input_device_id,
- const std::string& output_device_id) {
+ const std::string& device_id) {
base::DictionaryValue dict;
StoreComponentMetadata(component_id, &dict);
dict.SetString(kAudioLogStatusKey, "created");
- dict.SetString("input_device_id", input_device_id);
+ dict.SetString("device_id", device_id);
dict.SetInteger("input_channels", params.input_channels());
dict.SetInteger("frames_per_buffer", params.frames_per_buffer());
dict.SetInteger("sample_rate", params.sample_rate());
- dict.SetString("output_device_id", output_device_id);
dict.SetInteger("channels", params.channels());
dict.SetString("channel_layout",
ChannelLayoutToString(params.channel_layout()));
+ dict.SetString("effects", EffectsToString(params.effects()));
media_internals_->SendUpdateAndCache(
FormatCacheKey(component_id), kAudioLogUpdateFunction, &dict);
diff --git a/chromium/content/browser/media/media_internals_handler.cc b/chromium/content/browser/media/media_internals_handler.cc
index 9a3e7be2142..74eb8354a16 100644
--- a/chromium/content/browser/media/media_internals_handler.cc
+++ b/chromium/content/browser/media/media_internals_handler.cc
@@ -9,7 +9,7 @@
#include "base/values.h"
#include "content/browser/media/media_internals_proxy.h"
#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
@@ -41,9 +41,9 @@ void MediaInternalsMessageHandler::OnGetEverything(
void MediaInternalsMessageHandler::OnUpdate(const base::string16& update) {
// Don't try to execute JavaScript in a RenderView that no longer exists nor
// if the chrome://media-internals page hasn't finished loading.
- RenderViewHost* host = web_ui()->GetWebContents()->GetRenderViewHost();
+ RenderFrameHost* host = web_ui()->GetWebContents()->GetMainFrame();
if (host && page_load_complete_)
- host->ExecuteJavascriptInWebFrame(base::string16(), update);
+ host->ExecuteJavaScript(update);
}
} // namespace content
diff --git a/chromium/content/browser/media/media_internals_unittest.cc b/chromium/content/browser/media/media_internals_unittest.cc
index 2947104cad3..b9208c8dfd1 100644
--- a/chromium/content/browser/media/media_internals_unittest.cc
+++ b/chromium/content/browser/media/media_internals_unittest.cc
@@ -16,8 +16,7 @@
namespace {
const int kTestComponentID = 0;
-const char kTestInputDeviceID[] = "test-input-id";
-const char kTestOutputDeviceID[] = "test-output-id";
+const char kTestDeviceID[] = "test-device-id";
} // namespace
namespace content {
@@ -31,9 +30,12 @@ class MediaInternalsTest
base::Unretained(this))),
test_params_(media::AudioParameters::AUDIO_PCM_LINEAR,
media::CHANNEL_LAYOUT_MONO,
+ 0,
48000,
16,
- 128),
+ 128,
+ media::AudioParameters::ECHO_CANCELLER |
+ media::AudioParameters::DUCKING),
test_component_(GetParam()),
audio_log_(media_internals_->CreateAudioLog(test_component_)) {
media_internals_->AddUpdateCallback(update_cb_);
@@ -87,7 +89,7 @@ class MediaInternalsTest
TEST_P(MediaInternalsTest, AudioLogCreateStartStopErrorClose) {
audio_log_->OnCreated(
- kTestComponentID, test_params_, kTestInputDeviceID, kTestOutputDeviceID);
+ kTestComponentID, test_params_, kTestDeviceID);
base::RunLoop().RunUntilIdle();
ExpectString("channel_layout",
@@ -96,8 +98,8 @@ TEST_P(MediaInternalsTest, AudioLogCreateStartStopErrorClose) {
ExpectInt("frames_per_buffer", test_params_.frames_per_buffer());
ExpectInt("channels", test_params_.channels());
ExpectInt("input_channels", test_params_.input_channels());
- ExpectString("output_device_id", kTestOutputDeviceID);
- ExpectString("input_device_id", kTestInputDeviceID);
+ ExpectString("effects", "ECHO_CANCELLER | DUCKING");
+ ExpectString("device_id", kTestDeviceID);
ExpectInt("component_id", kTestComponentID);
ExpectInt("component_type", test_component_);
ExpectStatus("created");
@@ -128,7 +130,7 @@ TEST_P(MediaInternalsTest, AudioLogCreateStartStopErrorClose) {
TEST_P(MediaInternalsTest, AudioLogCreateClose) {
audio_log_->OnCreated(
- kTestComponentID, test_params_, kTestInputDeviceID, kTestOutputDeviceID);
+ kTestComponentID, test_params_, kTestDeviceID);
base::RunLoop().RunUntilIdle();
ExpectStatus("created");
diff --git a/chromium/content/browser/media/media_source_browsertest.cc b/chromium/content/browser/media/media_source_browsertest.cc
index 27242e5f800..d5d86154637 100644
--- a/chromium/content/browser/media/media_source_browsertest.cc
+++ b/chromium/content/browser/media/media_source_browsertest.cc
@@ -11,6 +11,9 @@
// Common media types.
const char kWebMAudioOnly[] = "audio/webm; codecs=\"vorbis\"";
+#if !defined(OS_ANDROID)
+const char kWebMOpusAudioOnly[] = "audio/webm; codecs=\"opus\"";
+#endif
const char kWebMVideoOnly[] = "video/webm; codecs=\"vp8\"";
const char kWebMAudioVideo[] = "video/webm; codecs=\"vorbis, vp8\"";
@@ -59,6 +62,13 @@ IN_PROC_BROWSER_TEST_F(MediaSourceTest, Playback_VideoOnly_WebM) {
TestSimplePlayback("bear-320x240-video-only.webm", kWebMVideoOnly, kEnded);
}
+// Opus is not supported in Android as of now.
+#if !defined(OS_ANDROID)
+IN_PROC_BROWSER_TEST_F(MediaSourceTest, Playback_AudioOnly_Opus_WebM) {
+ TestSimplePlayback("bear-opus.webm", kWebMOpusAudioOnly, kEnded);
+}
+#endif
+
IN_PROC_BROWSER_TEST_F(MediaSourceTest, Playback_AudioOnly_WebM) {
TestSimplePlayback("bear-320x240-audio-only.webm", kWebMAudioOnly, kEnded);
}
diff --git a/chromium/content/browser/media/media_web_contents_observer.cc b/chromium/content/browser/media/media_web_contents_observer.cc
new file mode 100644
index 00000000000..0214eb52f68
--- /dev/null
+++ b/chromium/content/browser/media/media_web_contents_observer.cc
@@ -0,0 +1,206 @@
+// 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/media/media_web_contents_observer.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "base/stl_util.h"
+#include "content/browser/media/cdm/browser_cdm_manager.h"
+#include "content/common/media/cdm_messages.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "ipc/ipc_message_macros.h"
+
+#if defined(OS_ANDROID)
+#include "content/browser/media/android/browser_media_player_manager.h"
+#include "content/common/media/media_player_messages_android.h"
+#include "media/base/android/media_player_android.h"
+#endif // defined(OS_ANDROID)
+
+namespace content {
+
+MediaWebContentsObserver::MediaWebContentsObserver(
+ RenderViewHost* render_view_host)
+ : WebContentsObserver(WebContents::FromRenderViewHost(render_view_host)) {
+}
+
+MediaWebContentsObserver::~MediaWebContentsObserver() {
+}
+
+void MediaWebContentsObserver::RenderFrameDeleted(
+ RenderFrameHost* render_frame_host) {
+ uintptr_t key = reinterpret_cast<uintptr_t>(render_frame_host);
+ // Always destroy the media players before CDMs because we do not support
+ // detaching CDMs from media players yet. See http://crbug.com/330324
+#if defined(OS_ANDROID)
+ media_player_managers_.erase(key);
+#endif
+ cdm_managers_.erase(key);
+}
+
+bool MediaWebContentsObserver::OnMessageReceived(
+ const IPC::Message& msg,
+ RenderFrameHost* render_frame_host) {
+#if defined(OS_ANDROID)
+ // Handles MediaPlayer messages first because MediaPlayers messages are much
+ // more frequent than CDM messages.
+ if (OnMediaPlayerMessageReceived(msg, render_frame_host))
+ return true;
+
+ if (OnMediaPlayerSetCdmMessageReceived(msg, render_frame_host))
+ return true;
+#endif // defined(OS_ANDROID)
+
+ if (OnCdmMessageReceived(msg, render_frame_host))
+ return true;
+
+ return false;
+}
+
+bool MediaWebContentsObserver::OnCdmMessageReceived(
+ const IPC::Message& msg,
+ RenderFrameHost* render_frame_host) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(MediaWebContentsObserver, msg)
+ IPC_MESSAGE_FORWARD(CdmHostMsg_InitializeCdm,
+ GetCdmManager(render_frame_host),
+ BrowserCdmManager::OnInitializeCdm)
+ IPC_MESSAGE_FORWARD(CdmHostMsg_CreateSession,
+ GetCdmManager(render_frame_host),
+ BrowserCdmManager::OnCreateSession)
+ IPC_MESSAGE_FORWARD(CdmHostMsg_UpdateSession,
+ GetCdmManager(render_frame_host),
+ BrowserCdmManager::OnUpdateSession)
+ IPC_MESSAGE_FORWARD(CdmHostMsg_ReleaseSession,
+ GetCdmManager(render_frame_host),
+ BrowserCdmManager::OnReleaseSession)
+ IPC_MESSAGE_FORWARD(CdmHostMsg_DestroyCdm,
+ GetCdmManager(render_frame_host),
+ BrowserCdmManager::OnDestroyCdm)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+BrowserCdmManager* MediaWebContentsObserver::GetCdmManager(
+ RenderFrameHost* render_frame_host) {
+ uintptr_t key = reinterpret_cast<uintptr_t>(render_frame_host);
+ if (!cdm_managers_.contains(key)) {
+ cdm_managers_.set(
+ key, make_scoped_ptr(BrowserCdmManager::Create(render_frame_host)));
+ }
+ return cdm_managers_.get(key);
+}
+
+#if defined(OS_ANDROID)
+bool MediaWebContentsObserver::OnMediaPlayerMessageReceived(
+ const IPC::Message& msg,
+ RenderFrameHost* render_frame_host) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(MediaWebContentsObserver, msg)
+ IPC_MESSAGE_FORWARD(MediaPlayerHostMsg_EnterFullscreen,
+ GetMediaPlayerManager(render_frame_host),
+ BrowserMediaPlayerManager::OnEnterFullscreen)
+ IPC_MESSAGE_FORWARD(MediaPlayerHostMsg_ExitFullscreen,
+ GetMediaPlayerManager(render_frame_host),
+ BrowserMediaPlayerManager::OnExitFullscreen)
+ IPC_MESSAGE_FORWARD(MediaPlayerHostMsg_Initialize,
+ GetMediaPlayerManager(render_frame_host),
+ BrowserMediaPlayerManager::OnInitialize)
+ IPC_MESSAGE_FORWARD(MediaPlayerHostMsg_Start,
+ GetMediaPlayerManager(render_frame_host),
+ BrowserMediaPlayerManager::OnStart)
+ IPC_MESSAGE_FORWARD(MediaPlayerHostMsg_Seek,
+ GetMediaPlayerManager(render_frame_host),
+ BrowserMediaPlayerManager::OnSeek)
+ IPC_MESSAGE_FORWARD(MediaPlayerHostMsg_Pause,
+ GetMediaPlayerManager(render_frame_host),
+ BrowserMediaPlayerManager::OnPause)
+ IPC_MESSAGE_FORWARD(MediaPlayerHostMsg_SetVolume,
+ GetMediaPlayerManager(render_frame_host),
+ BrowserMediaPlayerManager::OnSetVolume)
+ IPC_MESSAGE_FORWARD(MediaPlayerHostMsg_SetPoster,
+ GetMediaPlayerManager(render_frame_host),
+ BrowserMediaPlayerManager::OnSetPoster)
+ IPC_MESSAGE_FORWARD(MediaPlayerHostMsg_Release,
+ GetMediaPlayerManager(render_frame_host),
+ BrowserMediaPlayerManager::OnReleaseResources)
+ IPC_MESSAGE_FORWARD(MediaPlayerHostMsg_DestroyMediaPlayer,
+ GetMediaPlayerManager(render_frame_host),
+ BrowserMediaPlayerManager::OnDestroyPlayer)
+#if defined(VIDEO_HOLE)
+ IPC_MESSAGE_FORWARD(MediaPlayerHostMsg_NotifyExternalSurface,
+ GetMediaPlayerManager(render_frame_host),
+ BrowserMediaPlayerManager::OnNotifyExternalSurface)
+#endif // defined(VIDEO_HOLE)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+bool MediaWebContentsObserver::OnMediaPlayerSetCdmMessageReceived(
+ const IPC::Message& msg,
+ RenderFrameHost* render_frame_host) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(
+ MediaWebContentsObserver, msg, render_frame_host)
+ IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_SetCdm, OnSetCdm)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void MediaWebContentsObserver::OnSetCdm(RenderFrameHost* render_frame_host,
+ int player_id,
+ int cdm_id) {
+ media::MediaPlayerAndroid* media_player =
+ GetMediaPlayerManager(render_frame_host)->GetPlayer(player_id);
+ if (!media_player) {
+ NOTREACHED() << "OnSetCdm: MediaPlayer not found for " << player_id;
+ return;
+ }
+
+ media::BrowserCdm* cdm = GetCdmManager(render_frame_host)->GetCdm(cdm_id);
+ if (!cdm) {
+ NOTREACHED() << "OnSetCdm: CDM not found for " << cdm_id;
+ return;
+ }
+
+ // TODO(xhwang): This could possibly fail. In that case we should reject the
+ // promise.
+ media_player->SetCdm(cdm);
+}
+
+BrowserMediaPlayerManager* MediaWebContentsObserver::GetMediaPlayerManager(
+ RenderFrameHost* render_frame_host) {
+ uintptr_t key = reinterpret_cast<uintptr_t>(render_frame_host);
+ if (!media_player_managers_.contains(key)) {
+ media_player_managers_.set(
+ key,
+ make_scoped_ptr(BrowserMediaPlayerManager::Create(render_frame_host)));
+ }
+ return media_player_managers_.get(key);
+}
+
+void MediaWebContentsObserver::PauseVideo() {
+ for (MediaPlayerManagerMap::iterator iter = media_player_managers_.begin();
+ iter != media_player_managers_.end(); ++iter) {
+ BrowserMediaPlayerManager* manager = iter->second;
+ manager->PauseVideo();
+ }
+}
+
+#if defined(VIDEO_HOLE)
+void MediaWebContentsObserver::OnFrameInfoUpdated() {
+ for (MediaPlayerManagerMap::iterator iter = media_player_managers_.begin();
+ iter != media_player_managers_.end(); ++iter) {
+ BrowserMediaPlayerManager* manager = iter->second;
+ manager->OnFrameInfoUpdated();
+ }
+}
+#endif // defined(VIDEO_HOLE)
+
+#endif // defined(OS_ANDROID)
+
+} // namespace content
diff --git a/chromium/content/browser/media/media_web_contents_observer.h b/chromium/content/browser/media/media_web_contents_observer.h
new file mode 100644
index 00000000000..8ed89932057
--- /dev/null
+++ b/chromium/content/browser/media/media_web_contents_observer.h
@@ -0,0 +1,85 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_MEDIA_MEDIA_WEB_CONTENTS_OBSERVER_H_
+#define CONTENT_BROWSER_MEDIA_MEDIA_WEB_CONTENTS_OBSERVER_H_
+
+#include "base/compiler_specific.h"
+#include "base/containers/scoped_ptr_hash_map.h"
+#include "content/common/content_export.h"
+#include "content/public/browser/web_contents_observer.h"
+
+namespace content {
+
+class BrowserCdmManager;
+#if defined(OS_ANDROID)
+class BrowserMediaPlayerManager;
+#endif // defined(OS_ANDROID)
+class RenderViewHost;
+
+// This class manages all RenderFrame based media related managers at the
+// browser side. It receives IPC messages from media RenderFrameObservers and
+// forwards them to the corresponding managers. The managers are responsible
+// for sending IPCs back to the RenderFrameObservers at the render side.
+class CONTENT_EXPORT MediaWebContentsObserver : public WebContentsObserver {
+ public:
+ explicit MediaWebContentsObserver(RenderViewHost* render_view_host);
+ virtual ~MediaWebContentsObserver();
+
+ // WebContentsObserver implementations.
+ virtual void RenderFrameDeleted(RenderFrameHost* render_frame_host) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message,
+ RenderFrameHost* render_frame_host) OVERRIDE;
+
+ // Helper function to handle CDM IPC messages. Returns whether the |message|
+ // is handled in the function.
+ bool OnCdmMessageReceived(const IPC::Message& message,
+ RenderFrameHost* render_frame_host);
+
+ // Gets the CDM manager associated with |render_frame_host|. Creates
+ // a new one if it doesn't exist. The caller doesn't own the returned pointer.
+ BrowserCdmManager* GetCdmManager(RenderFrameHost* render_frame_host);
+
+#if defined(OS_ANDROID)
+ // Helper functions to handle media player IPC messages. Returns whether the
+ // |message| is handled in the function.
+ bool OnMediaPlayerMessageReceived(const IPC::Message& message,
+ RenderFrameHost* render_frame_host);
+ bool OnMediaPlayerSetCdmMessageReceived(const IPC::Message& message,
+ RenderFrameHost* render_frame_host);
+
+ // Gets the media player manager associated with |render_frame_host|. Creates
+ // a new one if it doesn't exist. The caller doesn't own the returned pointer.
+ BrowserMediaPlayerManager* GetMediaPlayerManager(
+ RenderFrameHost* render_frame_host);
+
+ void OnSetCdm(RenderFrameHost* render_frame_host, int player_id, int cdm_id);
+
+ // Pauses all media player.
+ void PauseVideo();
+
+#if defined(VIDEO_HOLE)
+ void OnFrameInfoUpdated();
+#endif // defined(VIDEO_HOLE)
+
+#endif // defined(OS_ANDROID)
+
+ private:
+ // Map from RenderFrameHost* to BrowserCdmManager.
+ typedef base::ScopedPtrHashMap<uintptr_t, BrowserCdmManager> CdmManagerMap;
+ CdmManagerMap cdm_managers_;
+
+#if defined(OS_ANDROID)
+ // Map from RenderFrameHost* to BrowserMediaPlayerManager.
+ typedef base::ScopedPtrHashMap<uintptr_t, BrowserMediaPlayerManager>
+ MediaPlayerManagerMap;
+ MediaPlayerManagerMap media_player_managers_;
+#endif // defined(OS_ANDROID)
+
+ DISALLOW_COPY_AND_ASSIGN(MediaWebContentsObserver);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_MEDIA_MEDIA_WEB_CONTENTS_OBSERVER_H_
diff --git a/chromium/content/browser/media/midi_dispatcher_host.cc b/chromium/content/browser/media/midi_dispatcher_host.cc
new file mode 100644
index 00000000000..9b6faa1c585
--- /dev/null
+++ b/chromium/content/browser/media/midi_dispatcher_host.cc
@@ -0,0 +1,123 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/media/midi_dispatcher_host.h"
+
+#include "base/bind.h"
+#include "content/browser/child_process_security_policy_impl.h"
+#include "content/browser/frame_host/render_frame_host_impl.h"
+#include "content/common/media/midi_messages.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.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/web_contents.h"
+#include "url/gurl.h"
+
+namespace content {
+
+MidiDispatcherHost::PendingPermission::PendingPermission(
+ int render_process_id,
+ int render_frame_id,
+ int bridge_id)
+ : render_process_id(render_process_id),
+ render_frame_id(render_frame_id),
+ bridge_id(bridge_id) {
+}
+
+MidiDispatcherHost::PendingPermission::~PendingPermission() {
+}
+
+MidiDispatcherHost::MidiDispatcherHost(WebContents* web_contents)
+ : WebContentsObserver(web_contents),
+ weak_factory_(this) {
+}
+
+MidiDispatcherHost::~MidiDispatcherHost() {
+}
+
+bool MidiDispatcherHost::OnMessageReceived(const IPC::Message& message,
+ RenderFrameHost* render_frame_host) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(MidiDispatcherHost, message,
+ render_frame_host)
+ IPC_MESSAGE_HANDLER(MidiHostMsg_RequestSysExPermission,
+ OnRequestSysExPermission)
+ IPC_MESSAGE_HANDLER(MidiHostMsg_CancelSysExPermissionRequest,
+ OnCancelSysExPermissionRequest)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void MidiDispatcherHost::OnRequestSysExPermission(
+ RenderFrameHost* render_frame_host,
+ int bridge_id,
+ const GURL& origin,
+ bool user_gesture) {
+ int render_process_id = render_frame_host->GetProcess()->GetID();
+ int render_frame_id = render_frame_host->GetRoutingID();
+
+ PendingPermission pending_permission(
+ render_process_id, render_frame_id, bridge_id);
+ pending_permissions_.push_back(pending_permission);
+
+ GetContentClient()->browser()->RequestMidiSysExPermission(
+ web_contents(),
+ bridge_id,
+ origin,
+ user_gesture,
+ base::Bind(&MidiDispatcherHost::WasSysExPermissionGranted,
+ weak_factory_.GetWeakPtr(),
+ render_process_id, render_frame_id, bridge_id),
+ &pending_permissions_.back().cancel);
+}
+
+void MidiDispatcherHost::OnCancelSysExPermissionRequest(
+ RenderFrameHost* render_frame_host,
+ int bridge_id,
+ const GURL& requesting_frame) {
+ int render_process_id = render_frame_host->GetProcess()->GetID();
+ int render_frame_id = render_frame_host->GetRoutingID();
+
+ for (size_t i = 0; i < pending_permissions_.size(); ++i) {
+ if (pending_permissions_[i].render_process_id == render_process_id &&
+ pending_permissions_[i].render_frame_id == render_frame_id &&
+ pending_permissions_[i].bridge_id == bridge_id) {
+ if (!pending_permissions_[i].cancel.is_null())
+ pending_permissions_[i].cancel.Run();
+ pending_permissions_.erase(pending_permissions_.begin() + i);
+ return;
+ }
+ }
+}
+
+void MidiDispatcherHost::WasSysExPermissionGranted(int render_process_id,
+ int render_frame_id,
+ int bridge_id,
+ bool is_allowed) {
+ for (size_t i = 0; i < pending_permissions_.size(); ++i) {
+ if (pending_permissions_[i].render_process_id == render_process_id &&
+ pending_permissions_[i].render_frame_id == render_frame_id &&
+ pending_permissions_[i].bridge_id == bridge_id) {
+ RenderFrameHost* render_frame_host =
+ RenderFrameHost::FromID(render_process_id, render_frame_id);
+ if (render_frame_host) {
+ render_frame_host->Send(new MidiMsg_SysExPermissionApproved(
+ render_frame_id, bridge_id, is_allowed));
+ }
+
+ if (is_allowed) {
+ ChildProcessSecurityPolicyImpl::GetInstance()->
+ GrantSendMidiSysExMessage(render_process_id);
+ }
+
+ pending_permissions_.erase(pending_permissions_.begin() + i);
+ return;
+ }
+ }
+}
+
+} // namespace content
diff --git a/chromium/content/browser/media/midi_dispatcher_host.h b/chromium/content/browser/media/midi_dispatcher_host.h
new file mode 100644
index 00000000000..87b7b1df776
--- /dev/null
+++ b/chromium/content/browser/media/midi_dispatcher_host.h
@@ -0,0 +1,60 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_MEDIA_MIDI_DISPATCHER_HOST_H_
+#define CONTENT_BROWSER_MEDIA_MIDI_DISPATCHER_HOST_H_
+
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/memory/weak_ptr.h"
+#include "content/public/browser/web_contents_observer.h"
+
+namespace content {
+
+class BrowserContext;
+
+// MidiDispatcherHost handles permissions for using system exclusive messages.
+class MidiDispatcherHost : public WebContentsObserver {
+ public:
+ explicit MidiDispatcherHost(WebContents* web_contents);
+ virtual ~MidiDispatcherHost();
+
+ // WebContentsObserver implementation.
+ virtual bool OnMessageReceived(const IPC::Message& message,
+ RenderFrameHost* render_frame_host) OVERRIDE;
+
+ private:
+ void OnRequestSysExPermission(RenderFrameHost* render_frame_host,
+ int bridge_id,
+ const GURL& origin,
+ bool user_gesture);
+ void OnCancelSysExPermissionRequest(RenderFrameHost* render_frame_host,
+ int bridge_id,
+ const GURL& requesting_frame);
+ void WasSysExPermissionGranted(int render_process_id,
+ int render_frame_id,
+ int bridge_id,
+ bool is_allowed);
+
+ struct PendingPermission {
+ PendingPermission(int render_process_id,
+ int render_frame_id,
+ int bridge_id);
+ ~PendingPermission();
+ int render_process_id;
+ int render_frame_id;
+ int bridge_id;
+ base::Closure cancel;
+ };
+ std::vector<PendingPermission> pending_permissions_;
+
+ base::WeakPtrFactory<MidiDispatcherHost> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(MidiDispatcherHost);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_MEDIA_MIDI_DISPATCHER_HOST_H_
diff --git a/chromium/content/browser/renderer_host/media/midi_host.cc b/chromium/content/browser/media/midi_host.cc
index d6781f61df5..950cf48e0a9 100644
--- a/chromium/content/browser/renderer_host/media/midi_host.cc
+++ b/chromium/content/browser/media/midi_host.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "content/browser/renderer_host/media/midi_host.h"
+#include "content/browser/media/midi_host.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
@@ -19,8 +19,8 @@
#include "media/midi/midi_message_queue.h"
#include "media/midi/midi_message_util.h"
-using media::MIDIManager;
-using media::MIDIPortInfoList;
+using media::MidiManager;
+using media::MidiPortInfoList;
namespace content {
namespace {
@@ -35,9 +35,6 @@ const size_t kMaxInFlightBytes = 10 * 1024 * 1024; // 10 MB.
// how many bytes will be sent before reporting back to the renderer.
const size_t kAcknowledgementThresholdBytes = 1024 * 1024; // 1 MB.
-const uint8 kSysExMessage = 0xf0;
-const uint8 kEndOfSysExMessage = 0xf7;
-
bool IsDataByte(uint8 data) {
return (data & 0x80) == 0;
}
@@ -48,65 +45,45 @@ bool IsSystemRealTimeMessage(uint8 data) {
} // namespace
-MIDIHost::MIDIHost(int renderer_process_id, media::MIDIManager* midi_manager)
- : renderer_process_id_(renderer_process_id),
+using media::kSysExByte;
+using media::kEndOfSysExByte;
+
+MidiHost::MidiHost(int renderer_process_id, media::MidiManager* midi_manager)
+ : BrowserMessageFilter(MidiMsgStart),
+ renderer_process_id_(renderer_process_id),
has_sys_ex_permission_(false),
midi_manager_(midi_manager),
sent_bytes_in_flight_(0),
bytes_sent_since_last_acknowledgement_(0) {
}
-MIDIHost::~MIDIHost() {
+MidiHost::~MidiHost() {
if (midi_manager_)
midi_manager_->EndSession(this);
}
-void MIDIHost::OnDestruct() const {
+void MidiHost::OnDestruct() const {
BrowserThread::DeleteOnIOThread::Destruct(this);
}
-///////////////////////////////////////////////////////////////////////////////
// IPC Messages handler
-bool MIDIHost::OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) {
+bool MidiHost::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(MIDIHost, message, *message_was_ok)
- IPC_MESSAGE_HANDLER(MIDIHostMsg_StartSession, OnStartSession)
- IPC_MESSAGE_HANDLER(MIDIHostMsg_SendData, OnSendData)
+ IPC_BEGIN_MESSAGE_MAP(MidiHost, message)
+ IPC_MESSAGE_HANDLER(MidiHostMsg_StartSession, OnStartSession)
+ IPC_MESSAGE_HANDLER(MidiHostMsg_SendData, OnSendData)
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return handled;
}
-void MIDIHost::OnStartSession(int client_id) {
- MIDIPortInfoList input_ports;
- MIDIPortInfoList output_ports;
-
- // Initialize devices and register to receive MIDI data.
- bool success = false;
- if (midi_manager_) {
- success = midi_manager_->StartSession(this);
- if (success) {
- input_ports = midi_manager_->input_ports();
- output_ports = midi_manager_->output_ports();
- received_messages_queues_.clear();
- received_messages_queues_.resize(input_ports.size());
- // ChildSecurityPolicy is set just before OnStartSession by
- // MIDIDispatcherHost. So we can safely cache the policy.
- has_sys_ex_permission_ = ChildProcessSecurityPolicyImpl::GetInstance()->
- CanSendMIDISysExMessage(renderer_process_id_);
- }
- }
-
- Send(new MIDIMsg_SessionStarted(
- client_id,
- success,
- input_ports,
- output_ports));
+void MidiHost::OnStartSession(int client_id) {
+ if (midi_manager_)
+ midi_manager_->StartSession(this, client_id);
}
-void MIDIHost::OnSendData(uint32 port,
+void MidiHost::OnSendData(uint32 port,
const std::vector<uint8>& data,
double timestamp) {
if (!midi_manager_)
@@ -119,8 +96,8 @@ void MIDIHost::OnSendData(uint32 port,
// in JavaScript. The actual permission check for security purposes
// happens here in the browser process.
if (!has_sys_ex_permission_ &&
- (std::find(data.begin(), data.end(), kSysExMessage) != data.end())) {
- RecordAction(UserMetricsAction("BadMessageTerminate_MIDI"));
+ std::find(data.begin(), data.end(), kSysExByte) != data.end()) {
+ RecordAction(base::UserMetricsAction("BadMessageTerminate_MIDI"));
BadMessageReceived();
return;
}
@@ -128,29 +105,52 @@ void MIDIHost::OnSendData(uint32 port,
if (!IsValidWebMIDIData(data))
return;
- base::AutoLock auto_lock(in_flight_lock_);
- // Sanity check that we won't send too much data.
- // TODO(yukawa): Consider to send an error event back to the renderer
- // after some future discussion in W3C.
- if (data.size() + sent_bytes_in_flight_ > kMaxInFlightBytes)
- return;
- midi_manager_->DispatchSendMIDIData(this, port, data, timestamp);
- sent_bytes_in_flight_ += data.size();
+ {
+ base::AutoLock auto_lock(in_flight_lock_);
+ // Sanity check that we won't send too much data.
+ // TODO(yukawa): Consider to send an error event back to the renderer
+ // after some future discussion in W3C.
+ if (data.size() + sent_bytes_in_flight_ > kMaxInFlightBytes)
+ return;
+ sent_bytes_in_flight_ += data.size();
+ }
+ midi_manager_->DispatchSendMidiData(this, port, data, timestamp);
+}
+
+void MidiHost::CompleteStartSession(int client_id, media::MidiResult result) {
+ MidiPortInfoList input_ports;
+ MidiPortInfoList output_ports;
+
+ if (result == media::MIDI_OK) {
+ input_ports = midi_manager_->input_ports();
+ output_ports = midi_manager_->output_ports();
+ received_messages_queues_.clear();
+ received_messages_queues_.resize(input_ports.size());
+ // ChildSecurityPolicy is set just before OnStartSession by
+ // MidiDispatcherHost. So we can safely cache the policy.
+ has_sys_ex_permission_ = ChildProcessSecurityPolicyImpl::GetInstance()->
+ CanSendMidiSysExMessage(renderer_process_id_);
+ }
+
+ Send(new MidiMsg_SessionStarted(client_id,
+ result,
+ input_ports,
+ output_ports));
}
-void MIDIHost::ReceiveMIDIData(
+void MidiHost::ReceiveMidiData(
uint32 port,
const uint8* data,
size_t length,
double timestamp) {
- TRACE_EVENT0("midi", "MIDIHost::ReceiveMIDIData");
+ TRACE_EVENT0("midi", "MidiHost::ReceiveMidiData");
if (received_messages_queues_.size() <= port)
return;
// Lazy initialization
if (received_messages_queues_[port] == NULL)
- received_messages_queues_[port] = new media::MIDIMessageQueue(true);
+ received_messages_queues_[port] = new media::MidiMessageQueue(true);
received_messages_queues_[port]->Add(data, length);
std::vector<uint8> message;
@@ -162,15 +162,15 @@ void MIDIHost::ReceiveMIDIData(
// MIDI devices may send a system exclusive messages even if the renderer
// doesn't have a permission to receive it. Don't kill the renderer as
// OnSendData() does.
- if (message[0] == kSysExMessage && !has_sys_ex_permission_)
+ if (message[0] == kSysExByte && !has_sys_ex_permission_)
continue;
// Send to the renderer.
- Send(new MIDIMsg_DataReceived(port, message, timestamp));
+ Send(new MidiMsg_DataReceived(port, message, timestamp));
}
}
-void MIDIHost::AccumulateMIDIBytesSent(size_t n) {
+void MidiHost::AccumulateMidiBytesSent(size_t n) {
{
base::AutoLock auto_lock(in_flight_lock_);
if (n <= sent_bytes_in_flight_)
@@ -183,14 +183,14 @@ void MIDIHost::AccumulateMIDIBytesSent(size_t n) {
if (bytes_sent_since_last_acknowledgement_ >=
kAcknowledgementThresholdBytes) {
- Send(new MIDIMsg_AcknowledgeSentData(
+ Send(new MidiMsg_AcknowledgeSentData(
bytes_sent_since_last_acknowledgement_));
bytes_sent_since_last_acknowledgement_ = 0;
}
}
// static
-bool MIDIHost::IsValidWebMIDIData(const std::vector<uint8>& data) {
+bool MidiHost::IsValidWebMIDIData(const std::vector<uint8>& data) {
bool in_sysex = false;
size_t waiting_data_length = 0;
for (size_t i = 0; i < data.size(); ++i) {
@@ -204,17 +204,17 @@ bool MIDIHost::IsValidWebMIDIData(const std::vector<uint8>& data) {
continue; // Found data byte as expected.
}
if (in_sysex) {
- if (data[i] == kEndOfSysExMessage)
+ if (data[i] == kEndOfSysExByte)
in_sysex = false;
else if (!IsDataByte(current))
return false; // Error: |current| should have been data byte.
continue; // Found data byte as expected.
}
- if (current == kSysExMessage) {
+ if (current == kSysExByte) {
in_sysex = true;
continue; // Found SysEX
}
- waiting_data_length = media::GetMIDIMessageLength(current);
+ waiting_data_length = media::GetMidiMessageLength(current);
if (waiting_data_length == 0)
return false; // Error: |current| should have been a valid status byte.
--waiting_data_length; // Found status byte
diff --git a/chromium/content/browser/renderer_host/media/midi_host.h b/chromium/content/browser/media/midi_host.h
index 153863fe603..8be45d8218f 100644
--- a/chromium/content/browser/renderer_host/media/midi_host.h
+++ b/chromium/content/browser/media/midi_host.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_MIDI_HOST_H_
-#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_MIDI_HOST_H_
+#ifndef CONTENT_BROWSER_MEDIA_MIDI_HOST_H_
+#define CONTENT_BROWSER_MEDIA_MIDI_HOST_H_
#include <vector>
@@ -17,30 +17,31 @@
#include "media/midi/midi_manager.h"
namespace media {
-class MIDIManager;
-class MIDIMessageQueue;
+class MidiManager;
+class MidiMessageQueue;
}
namespace content {
-class CONTENT_EXPORT MIDIHost
+class CONTENT_EXPORT MidiHost
: public BrowserMessageFilter,
- public media::MIDIManagerClient {
+ public media::MidiManagerClient {
public:
// Called from UI thread from the owner of this object.
- MIDIHost(int renderer_process_id, media::MIDIManager* midi_manager);
+ MidiHost(int renderer_process_id, media::MidiManager* midi_manager);
// BrowserMessageFilter implementation.
virtual void OnDestruct() const OVERRIDE;
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
- // MIDIManagerClient implementation.
- virtual void ReceiveMIDIData(uint32 port,
+ // MidiManagerClient implementation.
+ virtual void CompleteStartSession(int client_id,
+ media::MidiResult result) OVERRIDE;
+ virtual void ReceiveMidiData(uint32 port,
const uint8* data,
size_t length,
double timestamp) OVERRIDE;
- virtual void AccumulateMIDIBytesSent(size_t n) OVERRIDE;
+ virtual void AccumulateMidiBytesSent(size_t n) OVERRIDE;
// Start session to access MIDI hardware.
void OnStartSession(int client_id);
@@ -51,13 +52,13 @@ class CONTENT_EXPORT MIDIHost
double timestamp);
private:
- FRIEND_TEST_ALL_PREFIXES(MIDIHostTest, IsValidWebMIDIData);
- friend class base::DeleteHelper<MIDIHost>;
+ FRIEND_TEST_ALL_PREFIXES(MidiHostTest, IsValidWebMIDIData);
+ friend class base::DeleteHelper<MidiHost>;
friend class BrowserThread;
- virtual ~MIDIHost();
+ virtual ~MidiHost();
- // Returns true if |data| fulfills the requirements of MIDIOutput.send API
+ // Returns true if |data| fulfills the requirements of MidiOutput.send API
// defined in the WebMIDI spec.
// - |data| must be any number of complete MIDI messages (data abbreviation
// called "running status" is disallowed).
@@ -75,10 +76,10 @@ class CONTENT_EXPORT MIDIHost
// does not support MIDI. If not supported then a call to
// OnRequestAccess() will always refuse access and a call to
// OnSendData() will do nothing.
- media::MIDIManager* const midi_manager_;
+ media::MidiManager* const midi_manager_;
// Buffers where data sent from each MIDI input port is stored.
- ScopedVector<media::MIDIMessageQueue> received_messages_queues_;
+ ScopedVector<media::MidiMessageQueue> received_messages_queues_;
// The number of bytes sent to the platform-specific MIDI sending
// system, but not yet completed.
@@ -91,9 +92,9 @@ class CONTENT_EXPORT MIDIHost
// Protects access to |sent_bytes_in_flight_|.
base::Lock in_flight_lock_;
- DISALLOW_COPY_AND_ASSIGN(MIDIHost);
+ DISALLOW_COPY_AND_ASSIGN(MidiHost);
};
} // namespace content
-#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_MIDI_HOST_H_
+#endif // CONTENT_BROWSER_MEDIA_MIDI_HOST_H_
diff --git a/chromium/content/browser/renderer_host/media/midi_host_unittest.cc b/chromium/content/browser/media/midi_host_unittest.cc
index fedb4265b9b..e06e4e4de51 100644
--- a/chromium/content/browser/renderer_host/media/midi_host_unittest.cc
+++ b/chromium/content/browser/media/midi_host_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "content/browser/renderer_host/media/midi_host.h"
+#include "content/browser/media/midi_host.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -41,22 +41,22 @@ void PushToVector(const T(&data)[N], std::vector<T>* buffer) {
} // namespace
-TEST(MIDIHostTest, IsValidWebMIDIData) {
+TEST(MidiHostTest, IsValidWebMIDIData) {
// Test single event scenario
- EXPECT_TRUE(MIDIHost::IsValidWebMIDIData(AsVector(kGMOn)));
- EXPECT_TRUE(MIDIHost::IsValidWebMIDIData(AsVector(kGSOn)));
- EXPECT_TRUE(MIDIHost::IsValidWebMIDIData(AsVector(kNoteOn)));
- EXPECT_TRUE(MIDIHost::IsValidWebMIDIData(AsVector(kChannelPressure)));
- EXPECT_TRUE(MIDIHost::IsValidWebMIDIData(AsVector(kTimingClock)));
- EXPECT_FALSE(MIDIHost::IsValidWebMIDIData(AsVector(kBrokenData1)));
- EXPECT_FALSE(MIDIHost::IsValidWebMIDIData(AsVector(kBrokenData2)));
- EXPECT_FALSE(MIDIHost::IsValidWebMIDIData(AsVector(kBrokenData3)));
- EXPECT_FALSE(MIDIHost::IsValidWebMIDIData(AsVector(kDataByte0)));
+ EXPECT_TRUE(MidiHost::IsValidWebMIDIData(AsVector(kGMOn)));
+ EXPECT_TRUE(MidiHost::IsValidWebMIDIData(AsVector(kGSOn)));
+ EXPECT_TRUE(MidiHost::IsValidWebMIDIData(AsVector(kNoteOn)));
+ EXPECT_TRUE(MidiHost::IsValidWebMIDIData(AsVector(kChannelPressure)));
+ EXPECT_TRUE(MidiHost::IsValidWebMIDIData(AsVector(kTimingClock)));
+ EXPECT_FALSE(MidiHost::IsValidWebMIDIData(AsVector(kBrokenData1)));
+ EXPECT_FALSE(MidiHost::IsValidWebMIDIData(AsVector(kBrokenData2)));
+ EXPECT_FALSE(MidiHost::IsValidWebMIDIData(AsVector(kBrokenData3)));
+ EXPECT_FALSE(MidiHost::IsValidWebMIDIData(AsVector(kDataByte0)));
// MIDI running status should be disallowed
- EXPECT_FALSE(MIDIHost::IsValidWebMIDIData(
+ EXPECT_FALSE(MidiHost::IsValidWebMIDIData(
AsVector(kNoteOnWithRunningStatus)));
- EXPECT_FALSE(MIDIHost::IsValidWebMIDIData(
+ EXPECT_FALSE(MidiHost::IsValidWebMIDIData(
AsVector(kChannelPressureWithRunningStatus)));
// Multiple messages are allowed as long as each of them is complete.
@@ -67,9 +67,9 @@ TEST(MIDIHostTest, IsValidWebMIDIData) {
PushToVector(kGSOn, &buffer);
PushToVector(kTimingClock, &buffer);
PushToVector(kNoteOn, &buffer);
- EXPECT_TRUE(MIDIHost::IsValidWebMIDIData(buffer));
+ EXPECT_TRUE(MidiHost::IsValidWebMIDIData(buffer));
PushToVector(kBrokenData1, &buffer);
- EXPECT_FALSE(MIDIHost::IsValidWebMIDIData(buffer));
+ EXPECT_FALSE(MidiHost::IsValidWebMIDIData(buffer));
}
// MIDI realtime message can be placed at any position.
@@ -77,13 +77,14 @@ TEST(MIDIHostTest, IsValidWebMIDIData) {
const uint8 kNoteOnWithRealTimeClock[] = {
0x90, 0xf8, 0x3c, 0x7f, 0x90, 0xf8, 0x3c, 0xf8, 0x7f, 0xf8,
};
- EXPECT_TRUE(MIDIHost::IsValidWebMIDIData(
+ EXPECT_TRUE(MidiHost::IsValidWebMIDIData(
AsVector(kNoteOnWithRealTimeClock)));
const uint8 kGMOnWithRealTimeClock[] = {
0xf0, 0xf8, 0x7e, 0x7f, 0x09, 0x01, 0xf8, 0xf7,
};
- EXPECT_TRUE(MIDIHost::IsValidWebMIDIData(AsVector(kGMOnWithRealTimeClock)));
+ EXPECT_TRUE(MidiHost::IsValidWebMIDIData(
+ AsVector(kGMOnWithRealTimeClock)));
}
}
diff --git a/chromium/content/browser/media/webrtc_browsertest.cc b/chromium/content/browser/media/webrtc_browsertest.cc
index 47847a7d53b..f5bb216ead0 100644
--- a/chromium/content/browser/media/webrtc_browsertest.cc
+++ b/chromium/content/browser/media/webrtc_browsertest.cc
@@ -3,234 +3,133 @@
// found in the LICENSE file.
#include "base/command_line.h"
-#include "base/debug/trace_event_impl.h"
-#include "base/json/json_reader.h"
+#include "base/file_util.h"
+#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
+#include "base/threading/platform_thread.h"
#include "base/values.h"
+#include "content/browser/media/webrtc_internals.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test_utils.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/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
+#include "content/test/webrtc_content_browsertest_base.h"
#include "media/audio/audio_manager.h"
+#include "media/base/media_switches.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
-#include "testing/perf/perf_test.h"
#if defined(OS_WIN)
#include "base/win/windows_version.h"
#endif
-const char kForceIsac16K[] =
-#ifdef OS_ANDROID
- // The default audio codec, Opus, doesn't work on Android.
- "true";
+#if defined (OS_ANDROID) || defined(THREAD_SANITIZER)
+// Just do the bare minimum of audio checking on Android and under TSAN since
+// it's a bit sensitive to device performance.
+static const char kUseLenientAudioChecking[] = "true";
#else
- "false";
+static const char kUseLenientAudioChecking[] = "false";
#endif
-namespace {
-
-static const char kGetUserMediaAndStop[] = "getUserMediaAndStop";
-static const char kGetUserMediaAndWaitAndStop[] = "getUserMediaAndWaitAndStop";
-static const char kGetUserMediaAndAnalyseAndStop[] =
- "getUserMediaAndAnalyseAndStop";
-
-std::string GenerateGetUserMediaCall(const char* function_name,
- int min_width,
- int max_width,
- int min_height,
- int max_height,
- int min_frame_rate,
- int max_frame_rate) {
- return base::StringPrintf(
- "%s({video: {mandatory: {minWidth: %d, maxWidth: %d, "
- "minHeight: %d, maxHeight: %d, minFrameRate: %d, maxFrameRate: %d}, "
- "optional: []}});",
- function_name,
- min_width,
- max_width,
- min_height,
- max_height,
- min_frame_rate,
- max_frame_rate);
-}
-}
-
namespace content {
-class WebrtcBrowserTest: public ContentBrowserTest {
+class WebRtcBrowserTest : public WebRtcContentBrowserTest,
+ public testing::WithParamInterface<bool> {
public:
- WebrtcBrowserTest() {}
- virtual ~WebrtcBrowserTest() {}
+ WebRtcBrowserTest() {}
+ virtual ~WebRtcBrowserTest() {}
virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
- // We need fake devices in this test since we want to run on naked VMs. We
- // assume these switches are set by default in content_browsertests.
- ASSERT_TRUE(CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kUseFakeDeviceForMediaStream));
- ASSERT_TRUE(CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kUseFakeUIForMediaStream));
+ WebRtcContentBrowserTest::SetUpCommandLine(command_line);
- // The video playback will not work without a GPU, so force its use here.
- // This may not be available on all VMs though.
- command_line->AppendSwitch(switches::kUseGpuInTests);
+ bool enable_audio_track_processing = GetParam();
+ if (!enable_audio_track_processing)
+ command_line->AppendSwitch(switches::kDisableAudioTrackProcessing);
}
- void DumpChromeTraceCallback(
- const scoped_refptr<base::RefCountedString>& events,
- bool has_more_events) {
- // Convert the dump output into a correct JSON List.
- std::string contents = "[" + events->data() + "]";
-
- int error_code;
- std::string error_message;
- scoped_ptr<base::Value> value(
- base::JSONReader::ReadAndReturnError(contents,
- base::JSON_ALLOW_TRAILING_COMMAS,
- &error_code,
- &error_message));
-
- ASSERT_TRUE(value.get() != NULL) << error_message;
- EXPECT_EQ(value->GetType(), base::Value::TYPE_LIST);
-
- base::ListValue* values;
- ASSERT_TRUE(value->GetAsList(&values));
-
- int duration_ns = 0;
- std::string samples_duration;
- double timestamp_ns = 0.0;
- double previous_timestamp_ns = 0.0;
- std::string samples_interarrival_ns;
- for (ListValue::iterator it = values->begin(); it != values->end(); ++it) {
- const DictionaryValue* dict;
- EXPECT_TRUE((*it)->GetAsDictionary(&dict));
-
- if (dict->GetInteger("dur", &duration_ns))
- samples_duration.append(base::StringPrintf("%d,", duration_ns));
- if (dict->GetDouble("ts", &timestamp_ns)) {
- if (previous_timestamp_ns) {
- samples_interarrival_ns.append(
- base::StringPrintf("%f,", timestamp_ns - previous_timestamp_ns));
- }
- previous_timestamp_ns = timestamp_ns;
- }
- }
- ASSERT_GT(samples_duration.size(), 0u)
- << "Could not collect any samples during test, this is bad";
- perf_test::PrintResultList("video_capture",
- "",
- "sample_duration",
- samples_duration,
- "ns",
- true);
- perf_test::PrintResultList("video_capture",
- "",
- "interarrival_time",
- samples_interarrival_ns,
- "ns",
- true);
- }
+ // Convenience function since most peerconnection-call.html tests just load
+ // the page, kick off some javascript and wait for the title to change to OK.
+ void MakeTypicalPeerConnectionCall(const std::string& javascript) {
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
- protected:
- bool ExecuteJavascript(const std::string& javascript) {
- return ExecuteScript(shell()->web_contents(), javascript);
- }
+ GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
+ NavigateToURL(shell(), url);
- void ExpectTitle(const std::string& expected_title) const {
- base::string16 expected_title16(ASCIIToUTF16(expected_title));
- TitleWatcher title_watcher(shell()->web_contents(), expected_title16);
- EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle());
+ DisableOpusIfOnAndroid();
+ ExecuteJavascriptAndWaitForOk(javascript);
}
-};
-
-// These tests will all make a getUserMedia call with different constraints and
-// see that the success callback is called. If the error callback is called or
-// none of the callbacks are called the tests will simply time out and fail.
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, GetVideoStreamAndStop) {
- ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
-
- GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
- NavigateToURL(shell(), url);
-
- EXPECT_TRUE(ExecuteJavascript(
- base::StringPrintf("%s({video: true});", kGetUserMediaAndStop)));
-
- ExpectTitle("OK");
-}
-
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, GetAudioAndVideoStreamAndStop) {
- ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
-
- GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
- NavigateToURL(shell(), url);
-
- EXPECT_TRUE(ExecuteJavascript(base::StringPrintf(
- "%s({video: true, audio: true});", kGetUserMediaAndStop)));
- ExpectTitle("OK");
-}
-
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, GetAudioAndVideoStreamAndClone) {
- ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+ // Convenience method for making calls that detect if audio os playing (which
+ // has some special prerequisites, such that there needs to be an audio output
+ // device on the executing machine).
+ void MakeAudioDetectingPeerConnectionCall(const std::string& javascript) {
+ if (!media::AudioManager::Get()->HasAudioOutputDevices()) {
+ // Bots with no output devices will force the audio code into a state
+ // where it doesn't manage to set either the low or high latency path.
+ // This test will compute useless values in that case, so skip running on
+ // such bots (see crbug.com/326338).
+ LOG(INFO) << "Missing output devices: skipping test...";
+ return;
+ }
- GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
- NavigateToURL(shell(), url);
+ ASSERT_TRUE(CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kUseFakeDeviceForMediaStream))
+ << "Must run with fake devices since the test will explicitly look "
+ << "for the fake device signal.";
- EXPECT_TRUE(ExecuteJavascript("getUserMediaAndClone();"));
+ MakeTypicalPeerConnectionCall(javascript);
+ }
- ExpectTitle("OK");
-}
+ void DisableOpusIfOnAndroid() {
+#if defined(OS_ANDROID)
+ // Always force iSAC 16K on Android for now (Opus is broken).
+ EXPECT_EQ("isac-forced",
+ ExecuteJavascriptAndReturnResult("forceIsac16KInSdp();"));
+#endif
+ }
+};
+static const bool kRunTestsWithFlag[] = { false, true };
+INSTANTIATE_TEST_CASE_P(WebRtcBrowserTests,
+ WebRtcBrowserTest,
+ testing::ValuesIn(kRunTestsWithFlag));
#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(ARCH_CPU_ARM_FAMILY)
// Timing out on ARM linux bot: http://crbug.com/238490
-#define MAYBE_CanSetupVideoCall DISABLED_CanSetupVideoCall
+#define MAYBE_CanSetupDefaultVideoCall DISABLED_CanSetupDefaultVideoCall
#else
-#define MAYBE_CanSetupVideoCall CanSetupVideoCall
+#define MAYBE_CanSetupDefaultVideoCall CanSetupDefaultVideoCall
#endif
// These tests will make a complete PeerConnection-based call and verify that
// video is playing for the call.
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MAYBE_CanSetupVideoCall) {
- ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
-
- GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
- NavigateToURL(shell(), url);
+IN_PROC_BROWSER_TEST_P(WebRtcBrowserTest, MAYBE_CanSetupDefaultVideoCall) {
+ MakeTypicalPeerConnectionCall(
+ "callAndExpectResolution({video: true}, 640, 480);");
+}
- EXPECT_TRUE(ExecuteJavascript("call({video: true});"));
- ExpectTitle("OK");
+IN_PROC_BROWSER_TEST_P(WebRtcBrowserTest, CanSetupVideoCallWith1To1AspecRatio) {
+ const std::string javascript =
+ "callAndExpectResolution({video: {mandatory: {minWidth: 320,"
+ " maxWidth: 320, minHeight: 320, maxHeight: 320}}}, 320, 320);";
+ MakeTypicalPeerConnectionCall(javascript);
}
-// This test will make a simple getUserMedia page, verify that video is playing
-// in a simple local <video>, and for a couple of seconds, collect some
-// performance traces.
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, TracePerformanceDuringGetUserMedia) {
- ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+IN_PROC_BROWSER_TEST_P(WebRtcBrowserTest,
+ CanSetupVideoCallWith16To9AspecRatio) {
+ const std::string javascript =
+ "callAndExpectResolution({video: {mandatory: {minWidth: 640,"
+ " maxWidth: 640, minAspectRatio: 1.777}}}, 640, 360);";
+ MakeTypicalPeerConnectionCall(javascript);
+}
- GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
- NavigateToURL(shell(), url);
- // Put getUserMedia to work and let it run for a couple of seconds.
- EXPECT_TRUE(ExecuteJavascript(base::StringPrintf(
- "%s({video: true}, 10);", kGetUserMediaAndWaitAndStop)));
-
- // Make sure the stream is up and running, then start collecting traces.
- ExpectTitle("Running...");
- base::debug::TraceLog* trace_log = base::debug::TraceLog::GetInstance();
- trace_log->SetEnabled(base::debug::CategoryFilter("video"),
- base::debug::TraceLog::ENABLE_SAMPLING);
- // Check that we are indeed recording.
- EXPECT_EQ(trace_log->GetNumTracesRecorded(), 1);
-
- // Wait until the page title changes to "OK". Do not sleep() here since that
- // would stop both this code and the browser underneath.
- ExpectTitle("OK");
-
- // Note that we need to stop the trace recording before flushing the data.
- trace_log->SetDisabled();
- trace_log->Flush(base::Bind(&WebrtcBrowserTest::DumpChromeTraceCallback,
- base::Unretained(this)));
+IN_PROC_BROWSER_TEST_P(WebRtcBrowserTest,
+ CanSetupVideoCallWith4To3AspecRatio) {
+ const std::string javascript =
+ "callAndExpectResolution({video: {mandatory: {minWidth: 960,"
+ "maxAspectRatio: 1.333}}}, 960, 720);";
+ MakeTypicalPeerConnectionCall(javascript);
}
#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(ARCH_CPU_ARM_FAMILY)
@@ -240,83 +139,73 @@ IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, TracePerformanceDuringGetUserMedia) {
#define MAYBE_CanSetupAudioAndVideoCall CanSetupAudioAndVideoCall
#endif
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MAYBE_CanSetupAudioAndVideoCall) {
- ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
-
- GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
- NavigateToURL(shell(), url);
-
- EXPECT_TRUE(ExecuteJavascript("call({video: true, audio: true});"));
- ExpectTitle("OK");
+IN_PROC_BROWSER_TEST_P(WebRtcBrowserTest, MAYBE_CanSetupAudioAndVideoCall) {
+ MakeTypicalPeerConnectionCall("call({video: true, audio: true});");
}
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MANUAL_CanSetupCallAndSendDtmf) {
- ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
-
- GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
- NavigateToURL(shell(), url);
-
- EXPECT_TRUE(
- ExecuteJavascript("callAndSendDtmf('123,abc');"));
+IN_PROC_BROWSER_TEST_P(WebRtcBrowserTest, MANUAL_CanSetupCallAndSendDtmf) {
+ MakeTypicalPeerConnectionCall("callAndSendDtmf(\'123,abc\');");
}
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest,
+// TODO(phoglund): this test fails because the peer connection state will be
+// stable in the second negotiation round rather than have-local-offer.
+// http://crbug.com/293125.
+IN_PROC_BROWSER_TEST_P(WebRtcBrowserTest,
DISABLED_CanMakeEmptyCallThenAddStreamsAndRenegotiate) {
- ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
-
- GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
- NavigateToURL(shell(), url);
-
const char* kJavascript =
"callEmptyThenAddOneStreamAndRenegotiate({video: true, audio: true});";
- EXPECT_TRUE(ExecuteJavascript(kJavascript));
- ExpectTitle("OK");
+ MakeTypicalPeerConnectionCall(kJavascript);
}
// Below 2 test will make a complete PeerConnection-based call between pc1 and
// pc2, and then use the remote stream to setup a call between pc3 and pc4, and
// then verify that video is received on pc3 and pc4.
+// The stream sent from pc3 to pc4 is the stream received on pc1.
+// The stream sent from pc4 to pc3 is cloned from stream the stream received
+// on pc2.
// Flaky on win xp. http://crbug.com/304775
#if defined(OS_WIN)
#define MAYBE_CanForwardRemoteStream DISABLED_CanForwardRemoteStream
#define MAYBE_CanForwardRemoteStream720p DISABLED_CanForwardRemoteStream720p
#else
#define MAYBE_CanForwardRemoteStream CanForwardRemoteStream
+// Flaky on TSAN v2. http://crbug.com/373637
+#if defined(THREAD_SANITIZER)
+#define MAYBE_CanForwardRemoteStream720p DISABLED_CanForwardRemoteStream720p
+#else
#define MAYBE_CanForwardRemoteStream720p CanForwardRemoteStream720p
#endif
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MAYBE_CanForwardRemoteStream) {
- ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
-
- GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
- NavigateToURL(shell(), url);
-
- EXPECT_TRUE(ExecuteJavascript(
- "callAndForwardRemoteStream({video: true, audio: true});"));
- ExpectTitle("OK");
+#endif
+IN_PROC_BROWSER_TEST_P(WebRtcBrowserTest, MAYBE_CanForwardRemoteStream) {
+#if defined (OS_ANDROID)
+ // This test fails on Nexus 5 devices.
+ // TODO(henrika): see http://crbug.com/362437 and http://crbug.com/359389
+ // for details.
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kDisableWebRtcHWDecoding);
+#endif
+ MakeTypicalPeerConnectionCall(
+ "callAndForwardRemoteStream({video: true, audio: false});");
}
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MAYBE_CanForwardRemoteStream720p) {
- ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
-
- GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
- NavigateToURL(shell(), url);
-
- const std::string cmd = GenerateGetUserMediaCall("callAndForwardRemoteStream",
- 1280, 1280,
- 720, 720, 30, 30);
- EXPECT_TRUE(ExecuteJavascript(cmd));
- ExpectTitle("OK");
+IN_PROC_BROWSER_TEST_P(WebRtcBrowserTest, MAYBE_CanForwardRemoteStream720p) {
+#if defined (OS_ANDROID)
+ // This test fails on Nexus 5 devices.
+ // TODO(henrika): see http://crbug.com/362437 and http://crbug.com/359389
+ // for details.
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kDisableWebRtcHWDecoding);
+#endif
+ const std::string javascript = GenerateGetUserMediaCall(
+ "callAndForwardRemoteStream", 1280, 1280, 720, 720, 10, 30);
+ MakeTypicalPeerConnectionCall(javascript);
}
// This test will make a complete PeerConnection-based call but remove the
// MSID and bundle attribute from the initial offer to verify that
// video is playing for the call even if the initiating client don't support
// MSID. http://tools.ietf.org/html/draft-alvestrand-rtcweb-msid-02
-#if defined(OS_WIN) && defined(USE_AURA)
-// Disabled for win7_aura, see http://crbug.com/235089.
-#define MAYBE_CanSetupAudioAndVideoCallWithoutMsidAndBundle\
- DISABLED_CanSetupAudioAndVideoCallWithoutMsidAndBundle
-#elif defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(ARCH_CPU_ARM_FAMILY)
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(ARCH_CPU_ARM_FAMILY)
// Timing out on ARM linux, see http://crbug.com/240373
#define MAYBE_CanSetupAudioAndVideoCallWithoutMsidAndBundle\
DISABLED_CanSetupAudioAndVideoCallWithoutMsidAndBundle
@@ -324,85 +213,51 @@ IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MAYBE_CanForwardRemoteStream720p) {
#define MAYBE_CanSetupAudioAndVideoCallWithoutMsidAndBundle\
CanSetupAudioAndVideoCallWithoutMsidAndBundle
#endif
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest,
+IN_PROC_BROWSER_TEST_P(WebRtcBrowserTest,
MAYBE_CanSetupAudioAndVideoCallWithoutMsidAndBundle) {
- ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
-
- GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
- NavigateToURL(shell(), url);
-
- EXPECT_TRUE(ExecuteJavascript("callWithoutMsidAndBundle();"));
- ExpectTitle("OK");
+ MakeTypicalPeerConnectionCall("callWithoutMsidAndBundle();");
}
// This test will modify the SDP offer to an unsupported codec, which should
// cause SetLocalDescription to fail.
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest,
- NegotiateUnsupportedVideoCodec) {
- ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
-
- GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
- NavigateToURL(shell(), url);
-
- EXPECT_TRUE(ExecuteJavascript("negotiateUnsupportedVideoCodec();"));
- ExpectTitle("OK");
+IN_PROC_BROWSER_TEST_P(WebRtcBrowserTest, NegotiateUnsupportedVideoCodec) {
+ MakeTypicalPeerConnectionCall("negotiateUnsupportedVideoCodec();");
}
// This test will modify the SDP offer to use no encryption, which should
// cause SetLocalDescription to fail.
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, NegotiateNonCryptoCall) {
- ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
-
- GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
- NavigateToURL(shell(), url);
+IN_PROC_BROWSER_TEST_P(WebRtcBrowserTest, NegotiateNonCryptoCall) {
+ MakeTypicalPeerConnectionCall("negotiateNonCryptoCall();");
+}
- EXPECT_TRUE(ExecuteJavascript("negotiateNonCryptoCall();"));
- ExpectTitle("OK");
+// This test can negotiate an SDP offer that includes a b=AS:xx to control
+// the bandwidth for audio and video
+IN_PROC_BROWSER_TEST_P(WebRtcBrowserTest, NegotiateOfferWithBLine) {
+ MakeTypicalPeerConnectionCall("negotiateOfferWithBLine();");
}
// This test will make a complete PeerConnection-based call using legacy SDP
// settings: GIce, external SDES, and no BUNDLE.
-#if defined(OS_WIN) && defined(USE_AURA)
-// Disabled for win7_aura, see http://crbug.com/235089.
-#define MAYBE_CanSetupLegacyCall DISABLED_CanSetupLegacyCall
-#elif defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(ARCH_CPU_ARM_FAMILY)
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(ARCH_CPU_ARM_FAMILY)
// Timing out on ARM linux, see http://crbug.com/240373
#define MAYBE_CanSetupLegacyCall DISABLED_CanSetupLegacyCall
#else
#define MAYBE_CanSetupLegacyCall CanSetupLegacyCall
#endif
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MAYBE_CanSetupLegacyCall) {
- ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
-
- GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
- NavigateToURL(shell(), url);
-
- EXPECT_TRUE(ExecuteJavascript("callWithLegacySdp();"));
- ExpectTitle("OK");
+IN_PROC_BROWSER_TEST_P(WebRtcBrowserTest, MAYBE_CanSetupLegacyCall) {
+ MakeTypicalPeerConnectionCall("callWithLegacySdp();");
}
// This test will make a PeerConnection-based call and test an unreliable text
// dataChannel.
// TODO(mallinath) - Remove this test after rtp based data channel is disabled.
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, CallWithDataOnly) {
- ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
-
- GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
- NavigateToURL(shell(), url);
-
- EXPECT_TRUE(ExecuteJavascript("callWithDataOnly();"));
- ExpectTitle("OK");
+IN_PROC_BROWSER_TEST_P(WebRtcBrowserTest, CallWithDataOnly) {
+ MakeTypicalPeerConnectionCall("callWithDataOnly();");
}
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, CallWithSctpDataOnly) {
- ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
-
- GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
- NavigateToURL(shell(), url);
-
- EXPECT_TRUE(ExecuteJavascript("callWithSctpDataOnly();"));
- ExpectTitle("OK");
+IN_PROC_BROWSER_TEST_P(WebRtcBrowserTest, CallWithSctpDataOnly) {
+ MakeTypicalPeerConnectionCall("callWithSctpDataOnly();");
}
#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(ARCH_CPU_ARM_FAMILY)
@@ -415,14 +270,8 @@ IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, CallWithSctpDataOnly) {
// This test will make a PeerConnection-based call and test an unreliable text
// dataChannel and audio and video tracks.
// TODO(mallinath) - Remove this test after rtp based data channel is disabled.
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MAYBE_CallWithDataAndMedia) {
- ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
-
- GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
- NavigateToURL(shell(), url);
-
- EXPECT_TRUE(ExecuteJavascript("callWithDataAndMedia();"));
- ExpectTitle("OK");
+IN_PROC_BROWSER_TEST_P(WebRtcBrowserTest, DISABLED_CallWithDataAndMedia) {
+ MakeTypicalPeerConnectionCall("callWithDataAndMedia();");
}
@@ -433,15 +282,9 @@ IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MAYBE_CallWithDataAndMedia) {
#define MAYBE_CallWithSctpDataAndMedia CallWithSctpDataAndMedia
#endif
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest,
+IN_PROC_BROWSER_TEST_P(WebRtcBrowserTest,
MAYBE_CallWithSctpDataAndMedia) {
- ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
-
- GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
- NavigateToURL(shell(), url);
-
- EXPECT_TRUE(ExecuteJavascript("callWithSctpDataAndMedia();"));
- ExpectTitle("OK");
+ MakeTypicalPeerConnectionCall("callWithSctpDataAndMedia();");
}
#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(ARCH_CPU_ARM_FAMILY)
@@ -454,14 +297,8 @@ IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest,
// This test will make a PeerConnection-based call and test an unreliable text
// dataChannel and later add an audio and video track.
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MAYBE_CallWithDataAndLaterAddMedia) {
- ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
-
- GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
- NavigateToURL(shell(), url);
-
- EXPECT_TRUE(ExecuteJavascript("callWithDataAndLaterAddMedia();"));
- ExpectTitle("OK");
+IN_PROC_BROWSER_TEST_P(WebRtcBrowserTest, MAYBE_CallWithDataAndLaterAddMedia) {
+ MakeTypicalPeerConnectionCall("callWithDataAndLaterAddMedia();");
}
#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(ARCH_CPU_ARM_FAMILY)
@@ -474,14 +311,8 @@ IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MAYBE_CallWithDataAndLaterAddMedia) {
// This test will make a PeerConnection-based call and send a new Video
// MediaStream that has been created based on a MediaStream created with
// getUserMedia.
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MAYBE_CallWithNewVideoMediaStream) {
- ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
-
- GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
- NavigateToURL(shell(), url);
-
- EXPECT_TRUE(ExecuteJavascript("callWithNewVideoMediaStream();"));
- ExpectTitle("OK");
+IN_PROC_BROWSER_TEST_P(WebRtcBrowserTest, MAYBE_CallWithNewVideoMediaStream) {
+ MakeTypicalPeerConnectionCall("callWithNewVideoMediaStream();");
}
// This test will make a PeerConnection-based call and send a new Video
@@ -490,132 +321,147 @@ IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MAYBE_CallWithNewVideoMediaStream) {
// AudioTrack is added instead.
// TODO(phoglund): This test is manual since not all buildbots has an audio
// input.
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MANUAL_CallAndModifyStream) {
- ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+IN_PROC_BROWSER_TEST_P(WebRtcBrowserTest, MANUAL_CallAndModifyStream) {
+ MakeTypicalPeerConnectionCall(
+ "callWithNewVideoMediaStreamLaterSwitchToAudio();");
+}
- GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
- NavigateToURL(shell(), url);
+IN_PROC_BROWSER_TEST_P(WebRtcBrowserTest, AddTwoMediaStreamsToOnePC) {
+ MakeTypicalPeerConnectionCall("addTwoMediaStreamsToOneConnection();");
+}
- EXPECT_TRUE(
- ExecuteJavascript("callWithNewVideoMediaStreamLaterSwitchToAudio();"));
- ExpectTitle("OK");
+IN_PROC_BROWSER_TEST_P(WebRtcBrowserTest,
+ EstablishAudioVideoCallAndEnsureAudioIsPlaying) {
+ MakeAudioDetectingPeerConnectionCall(base::StringPrintf(
+ "callAndEnsureAudioIsPlaying(%s, {audio:true, video:true});",
+ kUseLenientAudioChecking));
}
-// This test calls getUserMedia in sequence with different constraints.
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, TestGetUserMediaConstraints) {
- ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+IN_PROC_BROWSER_TEST_P(WebRtcBrowserTest,
+ EstablishAudioOnlyCallAndEnsureAudioIsPlaying) {
+ MakeAudioDetectingPeerConnectionCall(base::StringPrintf(
+ "callAndEnsureAudioIsPlaying(%s, {audio:true});",
+ kUseLenientAudioChecking));
+}
- GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-
- std::vector<std::string> list_of_get_user_media_calls;
- list_of_get_user_media_calls.push_back(GenerateGetUserMediaCall(
- kGetUserMediaAndStop, 320, 320, 180, 180, 30, 30));
- list_of_get_user_media_calls.push_back(GenerateGetUserMediaCall(
- kGetUserMediaAndStop, 320, 320, 240, 240, 30, 30));
- list_of_get_user_media_calls.push_back(GenerateGetUserMediaCall(
- kGetUserMediaAndStop, 640, 640, 360, 360, 30, 30));
- list_of_get_user_media_calls.push_back(GenerateGetUserMediaCall(
- kGetUserMediaAndStop, 640, 640, 480, 480, 30, 30));
- list_of_get_user_media_calls.push_back(GenerateGetUserMediaCall(
- kGetUserMediaAndStop, 960, 960, 720, 720, 30, 30));
- list_of_get_user_media_calls.push_back(GenerateGetUserMediaCall(
- kGetUserMediaAndStop, 1280, 1280, 720, 720, 30, 30));
- list_of_get_user_media_calls.push_back(GenerateGetUserMediaCall(
- kGetUserMediaAndStop, 1920, 1920, 1080, 1080, 30, 30));
-
- for (std::vector<std::string>::iterator const_iterator =
- list_of_get_user_media_calls.begin();
- const_iterator != list_of_get_user_media_calls.end();
- ++const_iterator) {
- DVLOG(1) << "Calling getUserMedia: " << *const_iterator;
- NavigateToURL(shell(), url);
- EXPECT_TRUE(ExecuteJavascript(*const_iterator));
- ExpectTitle("OK");
- }
+IN_PROC_BROWSER_TEST_P(WebRtcBrowserTest,
+ EstablishAudioVideoCallAndVerifyMutingWorks) {
+ MakeAudioDetectingPeerConnectionCall(base::StringPrintf(
+ "callAndEnsureAudioTrackMutingWorks(%s);", kUseLenientAudioChecking));
}
-// This test calls getUserMedia and checks for aspect ratio behavior.
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, TestGetUserMediaAspectRatio) {
- ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+// Flaky on TSAN v2: http://crbug.com/373637
+#if defined(THREAD_SANITIZER)
+#define MAYBE_EstablishAudioVideoCallAndVerifyUnmutingWorks\
+ DISABLED_EstablishAudioVideoCallAndVerifyUnmutingWorks
+#else
+#define MAYBE_EstablishAudioVideoCallAndVerifyUnmutingWorks\
+ EstablishAudioVideoCallAndVerifyUnmutingWorks
+#endif
+IN_PROC_BROWSER_TEST_P(WebRtcBrowserTest,
+ MAYBE_EstablishAudioVideoCallAndVerifyUnmutingWorks) {
+ MakeAudioDetectingPeerConnectionCall(base::StringPrintf(
+ "callAndEnsureAudioTrackUnmutingWorks(%s);", kUseLenientAudioChecking));
+}
- GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
+IN_PROC_BROWSER_TEST_P(WebRtcBrowserTest, CallAndVerifyVideoMutingWorks) {
+ MakeTypicalPeerConnectionCall("callAndEnsureVideoTrackMutingWorks();");
+}
+
+#if defined(OS_WIN)
+#define IntToStringType base::IntToString16
+#else
+#define IntToStringType base::IntToString
+#endif
- std::string constraints_4_3 = GenerateGetUserMediaCall(
- kGetUserMediaAndAnalyseAndStop, 640, 640, 480, 480, 30, 30);
- std::string constraints_16_9 = GenerateGetUserMediaCall(
- kGetUserMediaAndAnalyseAndStop, 640, 640, 360, 360, 30, 30);
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(ARCH_CPU_ARM_FAMILY)
+// Timing out on ARM linux bot: http://crbug.com/238490
+#define MAYBE_CallWithAecDump DISABLED_CallWithAecDump
+#else
+#define MAYBE_CallWithAecDump CallWithAecDump
+#endif
- // TODO(mcasas): add more aspect ratios, in particular 16:10 crbug.com/275594.
+// This tests will make a complete PeerConnection-based call, verify that
+// video is playing for the call, and verify that a non-empty AEC dump file
+// exists. The AEC dump is enabled through webrtc-internals. The HTML and
+// Javascript is bypassed since it would trigger a file picker dialog. Instead,
+// the dialog callback FileSelected() is invoked directly. In fact, there's
+// never a webrtc-internals page opened at all since that's not needed.
+IN_PROC_BROWSER_TEST_P(WebRtcBrowserTest, MAYBE_CallWithAecDump) {
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
- NavigateToURL(shell(), url);
- EXPECT_TRUE(ExecuteJavascript(constraints_4_3));
- ExpectTitle("4:3 letterbox");
+ // We must navigate somewhere first so that the render process is created.
+ NavigateToURL(shell(), GURL(""));
- NavigateToURL(shell(), url);
- EXPECT_TRUE(ExecuteJavascript(constraints_16_9));
- ExpectTitle("16:9 letterbox");
-}
+ base::FilePath dump_file;
+ ASSERT_TRUE(CreateTemporaryFile(&dump_file));
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, AddTwoMediaStreamsToOnePC) {
- ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+ // This fakes the behavior of another open tab with webrtc-internals, and
+ // enabling AEC dump in that tab.
+ WebRTCInternals::GetInstance()->FileSelected(dump_file, -1, NULL);
GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
NavigateToURL(shell(), url);
-
- EXPECT_TRUE(
- ExecuteJavascript("addTwoMediaStreamsToOneConnection();"));
- ExpectTitle("OK");
+ DisableOpusIfOnAndroid();
+ ExecuteJavascriptAndWaitForOk("call({video: true, audio: true});");
+
+ // Get the ID for the render process host. There should only be one.
+ RenderProcessHost::iterator it(
+ content::RenderProcessHost::AllHostsIterator());
+ int render_process_host_id = it.GetCurrentValue()->GetID();
+ EXPECT_GE(render_process_host_id, 0);
+
+ // Add file extensions that we expect to be added.
+ static const int kExpectedConsumerId = 0;
+ dump_file = dump_file.AddExtension(IntToStringType(render_process_host_id))
+ .AddExtension(IntToStringType(kExpectedConsumerId));
+
+ EXPECT_TRUE(base::PathExists(dump_file));
+ int64 file_size = 0;
+ EXPECT_TRUE(base::GetFileSize(dump_file, &file_size));
+ EXPECT_GT(file_size, 0);
+
+ base::DeleteFile(dump_file, false);
}
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest,
- EstablishAudioVideoCallAndMeasureOutputLevel) {
- if (!media::AudioManager::Get()->HasAudioOutputDevices()) {
- // Bots with no output devices will force the audio code into a different
- // path where it doesn't manage to set either the low or high latency path.
- // This test will compute useless values in that case, so skip running on
- // such bots (see crbug.com/326338).
- LOG(INFO) << "Missing output devices: skipping test...";
- return;
- }
+// TODO(grunell): Add test for multiple dumps when re-use of
+// MediaStreamAudioProcessor in AudioCapturer has been removed.
- ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
- ASSERT_TRUE(CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kUseFakeDeviceForMediaStream))
- << "Must run with fake devices since the test will explicitly look "
- << "for the fake device signal.";
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(ARCH_CPU_ARM_FAMILY)
+// Timing out on ARM linux bot: http://crbug.com/238490
+#define MAYBE_CallWithAecDumpEnabledThenDisabled DISABLED_CallWithAecDumpEnabledThenDisabled
+#else
+#define MAYBE_CallWithAecDumpEnabledThenDisabled CallWithAecDumpEnabledThenDisabled
+#endif
- GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
- NavigateToURL(shell(), url);
+// As above, but enable and disable dump before starting a call. The file should
+// be created, but should be empty.
+IN_PROC_BROWSER_TEST_P(WebRtcBrowserTest,
+ MAYBE_CallWithAecDumpEnabledThenDisabled) {
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
- EXPECT_TRUE(ExecuteJavascript(
- base::StringPrintf("callAndEnsureAudioIsPlaying(%s);", kForceIsac16K)));
- ExpectTitle("OK");
-}
+ // We must navigate somewhere first so that the render process is created.
+ NavigateToURL(shell(), GURL(""));
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest,
- EstablishAudioVideoCallAndVerifyMutingWorks) {
- if (!media::AudioManager::Get()->HasAudioOutputDevices()) {
- // Bots with no output devices will force the audio code into a different
- // path where it doesn't manage to set either the low or high latency path.
- // This test will compute useless values in that case, so skip running on
- // such bots (see crbug.com/326338).
- LOG(INFO) << "Missing output devices: skipping test...";
- return;
- }
+ base::FilePath dump_file;
+ ASSERT_TRUE(CreateTemporaryFile(&dump_file));
- ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
- ASSERT_TRUE(CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kUseFakeDeviceForMediaStream))
- << "Must run with fake devices since the test will explicitly look "
- << "for the fake device signal.";
+ // This fakes the behavior of another open tab with webrtc-internals, and
+ // enabling AEC dump in that tab, then disabling it.
+ WebRTCInternals::GetInstance()->FileSelected(dump_file, -1, NULL);
+ WebRTCInternals::GetInstance()->DisableAecDump();
GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
NavigateToURL(shell(), url);
+ DisableOpusIfOnAndroid();
+ ExecuteJavascriptAndWaitForOk("call({video: true, audio: true});");
+
+ EXPECT_TRUE(base::PathExists(dump_file));
+ int64 file_size = 0;
+ EXPECT_TRUE(base::GetFileSize(dump_file, &file_size));
+ EXPECT_EQ(0, file_size);
- EXPECT_TRUE(ExecuteJavascript(
- base::StringPrintf("callAndEnsureAudioMutingWorks(%s);",
- kForceIsac16K)));
- ExpectTitle("OK");
+ base::DeleteFile(dump_file, false);
}
} // namespace content
diff --git a/chromium/content/browser/media/webrtc_getusermedia_browsertest.cc b/chromium/content/browser/media/webrtc_getusermedia_browsertest.cc
new file mode 100644
index 00000000000..c7e1b4214e5
--- /dev/null
+++ b/chromium/content/browser/media/webrtc_getusermedia_browsertest.cc
@@ -0,0 +1,607 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/command_line.h"
+#include "base/debug/trace_event_impl.h"
+#include "base/json/json_reader.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/trace_event_analyzer.h"
+#include "base/values.h"
+#include "content/browser/media/webrtc_internals.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/browser_test_utils.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/webrtc_content_browsertest_base.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "testing/perf/perf_test.h"
+
+#if defined(OS_WIN)
+#include "base/win/windows_version.h"
+#endif
+
+using trace_analyzer::TraceAnalyzer;
+using trace_analyzer::Query;
+using trace_analyzer::TraceEventVector;
+
+namespace {
+
+static const char kGetUserMediaAndStop[] = "getUserMediaAndStop";
+static const char kGetUserMediaAndGetStreamUp[] = "getUserMediaAndGetStreamUp";
+static const char kGetUserMediaAndAnalyseAndStop[] =
+ "getUserMediaAndAnalyseAndStop";
+static const char kGetUserMediaAndExpectFailure[] =
+ "getUserMediaAndExpectFailure";
+static const char kRenderSameTrackMediastreamAndStop[] =
+ "renderSameTrackMediastreamAndStop";
+static const char kRenderClonedMediastreamAndStop[] =
+ "renderClonedMediastreamAndStop";
+static const char kRenderClonedTrackMediastreamAndStop[] =
+ "renderClonedTrackMediastreamAndStop";
+static const char kRenderDuplicatedMediastreamAndStop[] =
+ "renderDuplicatedMediastreamAndStop";
+
+// Results returned by JS.
+static const char kOK[] = "OK";
+
+std::string GenerateGetUserMediaWithMandatorySourceID(
+ const std::string& function_name,
+ const std::string& audio_source_id,
+ const std::string& video_source_id) {
+ const std::string audio_constraint =
+ "audio: {mandatory: { sourceId:\"" + audio_source_id + "\"}}, ";
+
+ const std::string video_constraint =
+ "video: {mandatory: { sourceId:\"" + video_source_id + "\"}}";
+ return function_name + "({" + audio_constraint + video_constraint + "});";
+}
+
+std::string GenerateGetUserMediaWithOptionalSourceID(
+ const std::string& function_name,
+ const std::string& audio_source_id,
+ const std::string& video_source_id) {
+ const std::string audio_constraint =
+ "audio: {optional: [{sourceId:\"" + audio_source_id + "\"}]}, ";
+
+ const std::string video_constraint =
+ "video: {optional: [{ sourceId:\"" + video_source_id + "\"}]}";
+ return function_name + "({" + audio_constraint + video_constraint + "});";
+}
+
+} // namespace
+
+namespace content {
+
+class WebRtcGetUserMediaBrowserTest: public WebRtcContentBrowserTest,
+ public testing::WithParamInterface<bool> {
+ public:
+ WebRtcGetUserMediaBrowserTest() : trace_log_(NULL) {}
+ virtual ~WebRtcGetUserMediaBrowserTest() {}
+
+ virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
+ WebRtcContentBrowserTest::SetUpCommandLine(command_line);
+
+ bool enable_audio_track_processing = GetParam();
+ if (!enable_audio_track_processing)
+ command_line->AppendSwitch(switches::kDisableAudioTrackProcessing);
+ }
+
+ void StartTracing() {
+ CHECK(trace_log_ == NULL) << "Can only can start tracing once";
+ trace_log_ = base::debug::TraceLog::GetInstance();
+ trace_log_->SetEnabled(base::debug::CategoryFilter("video"),
+ base::debug::TraceLog::RECORDING_MODE,
+ base::debug::TraceLog::ENABLE_SAMPLING);
+ // Check that we are indeed recording.
+ EXPECT_EQ(trace_log_->GetNumTracesRecorded(), 1);
+ }
+
+ void StopTracing() {
+ CHECK(message_loop_runner_ == NULL) << "Calling StopTracing more than once";
+ trace_log_->SetDisabled();
+ message_loop_runner_ = new MessageLoopRunner;
+ trace_log_->Flush(base::Bind(
+ &WebRtcGetUserMediaBrowserTest::OnTraceDataCollected,
+ base::Unretained(this)));
+ message_loop_runner_->Run();
+ }
+
+ void OnTraceDataCollected(
+ const scoped_refptr<base::RefCountedString>& events_str_ptr,
+ bool has_more_events) {
+ CHECK(!has_more_events);
+ recorded_trace_data_ = events_str_ptr;
+ message_loop_runner_->Quit();
+ }
+
+ TraceAnalyzer* CreateTraceAnalyzer() {
+ return TraceAnalyzer::Create("[" + recorded_trace_data_->data() + "]");
+ }
+
+ void RunGetUserMediaAndCollectMeasures(const int time_to_sample_secs,
+ const std::string& measure_filter,
+ const std::string& graph_name) {
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+
+ GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
+ NavigateToURL(shell(), url);
+
+ // Put getUserMedia to work and let it run for a couple of seconds.
+ DCHECK(time_to_sample_secs);
+ ExecuteJavascriptAndWaitForOk(
+ base::StringPrintf("%s({video: true});",
+ kGetUserMediaAndGetStreamUp));
+
+ // Now the stream is up and running, start collecting traces.
+ StartTracing();
+
+ // Let the stream run for a while in javascript.
+ ExecuteJavascriptAndWaitForOk(
+ base::StringPrintf("waitAndStopVideoTrack(%d);", time_to_sample_secs));
+
+ // Wait until the page title changes to "OK". Do not sleep() here since that
+ // would stop both this code and the browser underneath.
+ StopTracing();
+
+ scoped_ptr<TraceAnalyzer> analyzer(CreateTraceAnalyzer());
+ analyzer->AssociateBeginEndEvents();
+ trace_analyzer::TraceEventVector events;
+ DCHECK(measure_filter.size());
+ analyzer->FindEvents(
+ Query::EventNameIs(measure_filter),
+ &events);
+ ASSERT_GT(events.size(), 0u)
+ << "Could not collect any samples during test, this is bad";
+
+ std::string duration_us;
+ std::string interarrival_us;
+ for (size_t i = 0; i != events.size(); ++i) {
+ duration_us.append(
+ base::StringPrintf("%d,", static_cast<int>(events[i]->duration)));
+ }
+
+ for (size_t i = 1; i < events.size(); ++i) {
+ // The event |timestamp| comes in ns, divide to get us like |duration|.
+ interarrival_us.append(base::StringPrintf("%d,",
+ static_cast<int>((events[i]->timestamp - events[i - 1]->timestamp) /
+ base::Time::kNanosecondsPerMicrosecond)));
+ }
+
+ perf_test::PrintResultList(
+ graph_name, "", "sample_duration", duration_us, "us", true);
+
+ perf_test::PrintResultList(
+ graph_name, "", "interarrival_time", interarrival_us, "us", true);
+ }
+
+ void RunTwoGetTwoGetUserMediaWithDifferentContraints(
+ const std::string& constraints1,
+ const std::string& constraints2,
+ const std::string& expected_result) {
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+
+ GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
+ NavigateToURL(shell(), url);
+
+ std::string command = "twoGetUserMedia(" + constraints1 + ',' +
+ constraints2 + ')';
+
+ EXPECT_EQ(expected_result, ExecuteJavascriptAndReturnResult(command));
+ }
+
+ void GetInputDevices(std::vector<std::string>* audio_ids,
+ std::vector<std::string>* video_ids) {
+ GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
+ NavigateToURL(shell(), url);
+
+ std::string devices_as_json = ExecuteJavascriptAndReturnResult(
+ "getSources()");
+ EXPECT_FALSE(devices_as_json.empty());
+
+ int error_code;
+ std::string error_message;
+ scoped_ptr<base::Value> value(
+ base::JSONReader::ReadAndReturnError(devices_as_json,
+ base::JSON_ALLOW_TRAILING_COMMAS,
+ &error_code,
+ &error_message));
+
+ ASSERT_TRUE(value.get() != NULL) << error_message;
+ EXPECT_EQ(value->GetType(), base::Value::TYPE_LIST);
+
+ base::ListValue* values;
+ ASSERT_TRUE(value->GetAsList(&values));
+
+ for (base::ListValue::iterator it = values->begin();
+ it != values->end(); ++it) {
+ const base::DictionaryValue* dict;
+ std::string kind;
+ std::string device_id;
+ ASSERT_TRUE((*it)->GetAsDictionary(&dict));
+ ASSERT_TRUE(dict->GetString("kind", &kind));
+ ASSERT_TRUE(dict->GetString("id", &device_id));
+ ASSERT_FALSE(device_id.empty());
+ EXPECT_TRUE(kind == "audio" || kind == "video");
+ if (kind == "audio") {
+ audio_ids->push_back(device_id);
+ } else if (kind == "video") {
+ video_ids->push_back(device_id);
+ }
+ }
+ ASSERT_FALSE(audio_ids->empty());
+ ASSERT_FALSE(video_ids->empty());
+ }
+
+ private:
+ base::debug::TraceLog* trace_log_;
+ scoped_refptr<base::RefCountedString> recorded_trace_data_;
+ scoped_refptr<MessageLoopRunner> message_loop_runner_;
+};
+
+static const bool kRunTestsWithFlag[] = { false, true };
+INSTANTIATE_TEST_CASE_P(WebRtcGetUserMediaBrowserTests,
+ WebRtcGetUserMediaBrowserTest,
+ testing::ValuesIn(kRunTestsWithFlag));
+
+// These tests will all make a getUserMedia call with different constraints and
+// see that the success callback is called. If the error callback is called or
+// none of the callbacks are called the tests will simply time out and fail.
+IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, GetVideoStreamAndStop) {
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+
+ GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
+ NavigateToURL(shell(), url);
+
+ ExecuteJavascriptAndWaitForOk(
+ base::StringPrintf("%s({video: true});", kGetUserMediaAndStop));
+}
+
+IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
+ RenderSameTrackMediastreamAndStop) {
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+
+ GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
+ NavigateToURL(shell(), url);
+
+ ExecuteJavascriptAndWaitForOk(
+ base::StringPrintf("%s({video: true});",
+ kRenderSameTrackMediastreamAndStop));
+}
+
+IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
+ RenderClonedMediastreamAndStop) {
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+
+ GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
+ NavigateToURL(shell(), url);
+
+
+ ExecuteJavascriptAndWaitForOk(
+ base::StringPrintf("%s({video: true});",
+ kRenderClonedMediastreamAndStop));
+}
+
+IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
+ kRenderClonedTrackMediastreamAndStop) {
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+
+ GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
+ NavigateToURL(shell(), url);
+
+ ExecuteJavascriptAndWaitForOk(
+ base::StringPrintf("%s({video: true});",
+ kRenderClonedTrackMediastreamAndStop));
+}
+
+IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
+ kRenderDuplicatedMediastreamAndStop) {
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+
+ GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
+ NavigateToURL(shell(), url);
+
+ ExecuteJavascriptAndWaitForOk(
+ base::StringPrintf("%s({video: true});",
+ kRenderDuplicatedMediastreamAndStop));
+}
+
+IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
+ GetAudioAndVideoStreamAndStop) {
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+
+ GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
+ NavigateToURL(shell(), url);
+
+ ExecuteJavascriptAndWaitForOk(base::StringPrintf(
+ "%s({video: true, audio: true});", kGetUserMediaAndStop));
+}
+
+IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
+ GetAudioAndVideoStreamAndClone) {
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+
+ GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
+ NavigateToURL(shell(), url);
+
+ ExecuteJavascriptAndWaitForOk("getUserMediaAndClone();");
+}
+
+IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
+ RenderVideoTrackInMultipleTagsAndPause) {
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+
+ GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
+ NavigateToURL(shell(), url);
+
+ ExecuteJavascriptAndWaitForOk("getUserMediaAndRenderInSeveralVideoTags();");
+}
+
+
+
+IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
+ GetUserMediaWithMandatorySourceID) {
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+
+ std::vector<std::string> audio_ids;
+ std::vector<std::string> video_ids;
+ GetInputDevices(&audio_ids, &video_ids);
+
+ GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
+
+ // Test all combinations of mandatory sourceID;
+ for (std::vector<std::string>::const_iterator video_it = video_ids.begin();
+ video_it != video_ids.end(); ++video_it) {
+ for (std::vector<std::string>::const_iterator audio_it = audio_ids.begin();
+ audio_it != audio_ids.end(); ++audio_it) {
+ NavigateToURL(shell(), url);
+ EXPECT_EQ(kOK, ExecuteJavascriptAndReturnResult(
+ GenerateGetUserMediaWithMandatorySourceID(
+ kGetUserMediaAndStop,
+ *audio_it,
+ *video_it)));
+ }
+ }
+}
+
+IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
+ GetUserMediaWithInvalidMandatorySourceID) {
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+
+ std::vector<std::string> audio_ids;
+ std::vector<std::string> video_ids;
+ GetInputDevices(&audio_ids, &video_ids);
+
+ GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
+
+ // Test with invalid mandatory audio sourceID.
+ NavigateToURL(shell(), url);
+ EXPECT_EQ("DevicesNotFoundError", ExecuteJavascriptAndReturnResult(
+ GenerateGetUserMediaWithMandatorySourceID(
+ kGetUserMediaAndExpectFailure,
+ "something invalid",
+ video_ids[0])));
+
+ // Test with invalid mandatory video sourceID.
+ EXPECT_EQ("DevicesNotFoundError", ExecuteJavascriptAndReturnResult(
+ GenerateGetUserMediaWithMandatorySourceID(
+ kGetUserMediaAndExpectFailure,
+ audio_ids[0],
+ "something invalid")));
+
+ // Test with empty mandatory audio sourceID.
+ EXPECT_EQ("DevicesNotFoundError", ExecuteJavascriptAndReturnResult(
+ GenerateGetUserMediaWithMandatorySourceID(
+ kGetUserMediaAndExpectFailure,
+ "",
+ video_ids[0])));
+}
+
+IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
+ GetUserMediaWithInvalidOptionalSourceID) {
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+
+ std::vector<std::string> audio_ids;
+ std::vector<std::string> video_ids;
+ GetInputDevices(&audio_ids, &video_ids);
+
+ GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
+
+ // Test with invalid optional audio sourceID.
+ NavigateToURL(shell(), url);
+ EXPECT_EQ(kOK, ExecuteJavascriptAndReturnResult(
+ GenerateGetUserMediaWithOptionalSourceID(
+ kGetUserMediaAndStop,
+ "something invalid",
+ video_ids[0])));
+
+ // Test with invalid optional video sourceID.
+ EXPECT_EQ(kOK, ExecuteJavascriptAndReturnResult(
+ GenerateGetUserMediaWithOptionalSourceID(
+ kGetUserMediaAndStop,
+ audio_ids[0],
+ "something invalid")));
+
+ // Test with empty optional audio sourceID.
+ EXPECT_EQ(kOK, ExecuteJavascriptAndReturnResult(
+ GenerateGetUserMediaWithOptionalSourceID(
+ kGetUserMediaAndStop,
+ "",
+ video_ids[0])));
+}
+
+IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, TwoGetUserMediaAndStop) {
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+
+ GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
+ NavigateToURL(shell(), url);
+
+ ExecuteJavascriptAndWaitForOk(
+ "twoGetUserMediaAndStop({video: true, audio: true});");
+}
+
+IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
+ TwoGetUserMediaWithEqualConstraints) {
+ std::string constraints1 = "{video: true, audio: true}";
+ const std::string& constraints2 = constraints1;
+ std::string expected_result = "w=640:h=480-w=640:h=480";
+
+ RunTwoGetTwoGetUserMediaWithDifferentContraints(constraints1, constraints2,
+ expected_result);
+}
+
+IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
+ TwoGetUserMediaWithSecondVideoCropped) {
+ std::string constraints1 = "{video: true}";
+ std::string constraints2 = "{video: {mandatory: {maxHeight: 360}}}";
+ std::string expected_result = "w=640:h=480-w=640:h=360";
+ RunTwoGetTwoGetUserMediaWithDifferentContraints(constraints1, constraints2,
+ expected_result);
+}
+
+IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
+ TwoGetUserMediaWithFirstHdSecondVga) {
+ std::string constraints1 =
+ "{video: {mandatory: {minWidth:1280 , minHeight: 720}}}";
+ std::string constraints2 =
+ "{video: {mandatory: {maxWidth:640 , maxHeight: 480}}}";
+ std::string expected_result = "w=1280:h=720-w=640:h=480";
+ RunTwoGetTwoGetUserMediaWithDifferentContraints(constraints1, constraints2,
+ expected_result);
+}
+
+IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
+ GetUserMediaWithTooHighVideoConstraintsValues) {
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+
+ GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
+
+ int large_value = 99999;
+ std::string call = GenerateGetUserMediaCall(kGetUserMediaAndExpectFailure,
+ large_value,
+ large_value,
+ large_value,
+ large_value,
+ large_value,
+ large_value);
+ NavigateToURL(shell(), url);
+
+ // TODO(perkj): A proper error code should be returned by gUM.
+ EXPECT_EQ("TrackStartError", ExecuteJavascriptAndReturnResult(call));
+}
+
+// This test will make a simple getUserMedia page, verify that video is playing
+// in a simple local <video>, and for a couple of seconds, collect some
+// performance traces from VideoCaptureController colorspace conversion and
+// potential resizing.
+IN_PROC_BROWSER_TEST_P(
+ WebRtcGetUserMediaBrowserTest,
+ TraceVideoCaptureControllerPerformanceDuringGetUserMedia) {
+ RunGetUserMediaAndCollectMeasures(
+ 10,
+ "VideoCaptureController::OnIncomingCapturedData",
+ "VideoCaptureController");
+}
+
+// This test calls getUserMedia and checks for aspect ratio behavior.
+IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
+ TestGetUserMediaAspectRatio4To3) {
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+
+ GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
+
+ std::string constraints_4_3 = GenerateGetUserMediaCall(
+ kGetUserMediaAndAnalyseAndStop, 640, 640, 480, 480, 10, 30);
+
+ NavigateToURL(shell(), url);
+ ASSERT_EQ("w=640:h=480",
+ ExecuteJavascriptAndReturnResult(constraints_4_3));
+}
+
+// This test calls getUserMedia and checks for aspect ratio behavior.
+IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
+ TestGetUserMediaAspectRatio16To9) {
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+
+ GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
+
+ std::string constraints_16_9 = GenerateGetUserMediaCall(
+ kGetUserMediaAndAnalyseAndStop, 640, 640, 360, 360, 10, 30);
+
+ NavigateToURL(shell(), url);
+ ASSERT_EQ("w=640:h=360",
+ ExecuteJavascriptAndReturnResult(constraints_16_9));
+}
+
+// This test calls getUserMedia and checks for aspect ratio behavior.
+IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
+ TestGetUserMediaAspectRatio1To1) {
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+
+ GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
+
+ std::string constraints_1_1 = GenerateGetUserMediaCall(
+ kGetUserMediaAndAnalyseAndStop, 320, 320, 320, 320, 10, 30);
+
+ NavigateToURL(shell(), url);
+ ASSERT_EQ("w=320:h=320",
+ ExecuteJavascriptAndReturnResult(constraints_1_1));
+}
+
+namespace {
+
+struct UserMediaSizes {
+ int min_width;
+ int max_width;
+ int min_height;
+ int max_height;
+ int min_frame_rate;
+ int max_frame_rate;
+};
+
+} // namespace
+
+class WebRtcConstraintsBrowserTest
+ : public WebRtcContentBrowserTest,
+ public testing::WithParamInterface<UserMediaSizes> {
+ public:
+ WebRtcConstraintsBrowserTest() : user_media_(GetParam()) {}
+ const UserMediaSizes& user_media() const { return user_media_; }
+
+ private:
+ UserMediaSizes user_media_;
+};
+
+// This test calls getUserMedia in sequence with different constraints.
+IN_PROC_BROWSER_TEST_P(WebRtcConstraintsBrowserTest, GetUserMediaConstraints) {
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+
+ GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
+
+ std::string call = GenerateGetUserMediaCall(kGetUserMediaAndStop,
+ user_media().min_width,
+ user_media().max_width,
+ user_media().min_height,
+ user_media().max_height,
+ user_media().min_frame_rate,
+ user_media().max_frame_rate);
+ DVLOG(1) << "Calling getUserMedia: " << call;
+ NavigateToURL(shell(), url);
+ ExecuteJavascriptAndWaitForOk(call);
+}
+
+static const UserMediaSizes kAllUserMediaSizes[] = {
+ {320, 320, 180, 180, 10, 30},
+ {320, 320, 240, 240, 10, 30},
+ {640, 640, 360, 360, 10, 30},
+ {640, 640, 480, 480, 10, 30},
+ {960, 960, 720, 720, 10, 30},
+ {1280, 1280, 720, 720, 10, 30}};
+
+INSTANTIATE_TEST_CASE_P(UserMedia,
+ WebRtcConstraintsBrowserTest,
+ testing::ValuesIn(kAllUserMediaSizes));
+
+} // namespace content
diff --git a/chromium/content/browser/media/webrtc_identity_store_backend.cc b/chromium/content/browser/media/webrtc_identity_store_backend.cc
index d599dcda742..57a0b04f058 100644
--- a/chromium/content/browser/media/webrtc_identity_store_backend.cc
+++ b/chromium/content/browser/media/webrtc_identity_store_backend.cc
@@ -6,6 +6,8 @@
#include "base/file_util.h"
#include "base/files/file_path.h"
+#include "base/memory/scoped_vector.h"
+#include "base/strings/string_util.h"
#include "content/public/browser/browser_thread.h"
#include "net/base/net_errors.h"
#include "sql/error_delegate_util.h"
@@ -31,6 +33,7 @@ static bool InitDB(sql::Connection* db) {
db->DoesColumnExist(kWebRTCIdentityStoreDBName, "private_key") &&
db->DoesColumnExist(kWebRTCIdentityStoreDBName, "creation_time"))
return true;
+
if (!db->Execute("DROP TABLE webrtc_identity_store"))
return false;
}
@@ -153,7 +156,7 @@ class WebRTCIdentityStoreBackend::SqlLiteStorage
std::string identity_name;
Identity identity;
};
- typedef std::vector<PendingOperation*> PendingOperationList;
+ typedef ScopedVector<PendingOperation> PendingOperationList;
virtual ~SqlLiteStorage() {}
void OnDatabaseError(int error, sql::Statement* stmt);
@@ -343,7 +346,7 @@ void WebRTCIdentityStoreBackend::OnLoaded(scoped_ptr<IdentityMap> out_map) {
if (state_ != LOADING)
return;
- DVLOG(2) << "WebRTC identity store has loaded.";
+ DVLOG(3) << "WebRTC identity store has loaded.";
state_ = LOADED;
identities_.swap(*out_map);
@@ -370,7 +373,7 @@ void WebRTCIdentityStoreBackend::SqlLiteStorage::Load(IdentityMap* out_map) {
// from it.
const base::FilePath dir = path_.DirName();
if (!base::PathExists(dir) && !base::CreateDirectory(dir)) {
- DLOG(ERROR) << "Unable to open DB file path.";
+ DVLOG(2) << "Unable to open DB file path.";
return;
}
@@ -379,13 +382,13 @@ void WebRTCIdentityStoreBackend::SqlLiteStorage::Load(IdentityMap* out_map) {
db_->set_error_callback(base::Bind(&SqlLiteStorage::OnDatabaseError, this));
if (!db_->Open(path_)) {
- DLOG(ERROR) << "Unable to open DB.";
+ DVLOG(2) << "Unable to open DB.";
db_.reset();
return;
}
if (!InitDB(db_.get())) {
- DLOG(ERROR) << "Unable to init DB.";
+ DVLOG(2) << "Unable to init DB.";
db_.reset();
return;
}
@@ -470,21 +473,26 @@ void WebRTCIdentityStoreBackend::SqlLiteStorage::DeleteBetween(
sql::Transaction transaction(db_.get());
if (!transaction.Begin()) {
- DLOG(ERROR) << "Failed to begin the transaction.";
+ DVLOG(2) << "Failed to begin the transaction.";
+ return;
+ }
+
+ if (!del_stmt.Run()) {
+ DVLOG(2) << "Failed to run the delete statement.";
return;
}
- CHECK(del_stmt.Run());
- transaction.Commit();
+ if (!transaction.Commit())
+ DVLOG(2) << "Failed to commit the transaction.";
}
void WebRTCIdentityStoreBackend::SqlLiteStorage::OnDatabaseError(
int error,
sql::Statement* stmt) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
- if (!sql::IsErrorCatastrophic(error))
- return;
+
db_->RazeAndClose();
+ // It's not safe to reset |db_| here.
}
void WebRTCIdentityStoreBackend::SqlLiteStorage::BatchOperation(
@@ -542,33 +550,44 @@ void WebRTCIdentityStoreBackend::SqlLiteStorage::Commit() {
sql::Transaction transaction(db_.get());
if (!transaction.Begin()) {
- DLOG(ERROR) << "Failed to begin the transaction.";
+ DVLOG(2) << "Failed to begin the transaction.";
return;
}
- for (PendingOperationList::iterator it = pending_operations_.begin();
- it != pending_operations_.end();
+ // Swaps |pending_operations_| into a temporary list to make sure
+ // |pending_operations_| is always cleared in case of DB errors.
+ PendingOperationList pending_operations_copy;
+ pending_operations_.swap(pending_operations_copy);
+
+ for (PendingOperationList::const_iterator it =
+ pending_operations_copy.begin();
+ it != pending_operations_copy.end();
++it) {
- scoped_ptr<PendingOperation> po(*it);
- switch (po->type) {
+ switch ((*it)->type) {
case ADD_IDENTITY: {
add_stmt.Reset(true);
- add_stmt.BindString(0, po->origin.spec());
- add_stmt.BindString(1, po->identity_name);
- add_stmt.BindString(2, po->identity.common_name);
- const std::string& cert = po->identity.certificate;
+ add_stmt.BindString(0, (*it)->origin.spec());
+ add_stmt.BindString(1, (*it)->identity_name);
+ add_stmt.BindString(2, (*it)->identity.common_name);
+ const std::string& cert = (*it)->identity.certificate;
add_stmt.BindBlob(3, cert.data(), cert.size());
- const std::string& private_key = po->identity.private_key;
+ const std::string& private_key = (*it)->identity.private_key;
add_stmt.BindBlob(4, private_key.data(), private_key.size());
- add_stmt.BindInt64(5, po->identity.creation_time);
- CHECK(add_stmt.Run());
+ add_stmt.BindInt64(5, (*it)->identity.creation_time);
+ if (!add_stmt.Run()) {
+ DVLOG(2) << "Failed to add the identity to DB.";
+ return;
+ }
break;
}
case DELETE_IDENTITY:
del_stmt.Reset(true);
- del_stmt.BindString(0, po->origin.spec());
- del_stmt.BindString(1, po->identity_name);
- CHECK(del_stmt.Run());
+ del_stmt.BindString(0, (*it)->origin.spec());
+ del_stmt.BindString(1, (*it)->identity_name);
+ if (!del_stmt.Run()) {
+ DVLOG(2) << "Failed to delete the identity from DB.";
+ return;
+ }
break;
default:
@@ -576,8 +595,9 @@ void WebRTCIdentityStoreBackend::SqlLiteStorage::Commit() {
break;
}
}
- transaction.Commit();
- pending_operations_.clear();
+
+ if (!transaction.Commit())
+ DVLOG(2) << "Failed to commit the transaction.";
}
} // namespace content
diff --git a/chromium/content/browser/media/webrtc_identity_store_unittest.cc b/chromium/content/browser/media/webrtc_identity_store_unittest.cc
index 85bd18f3768..586559f2c05 100644
--- a/chromium/content/browser/media/webrtc_identity_store_unittest.cc
+++ b/chromium/content/browser/media/webrtc_identity_store_unittest.cc
@@ -10,6 +10,8 @@
#include "content/public/test/test_browser_thread_bundle.h"
#include "content/public/test/test_utils.h"
#include "net/base/net_errors.h"
+#include "sql/connection.h"
+#include "sql/test/test_helpers.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
@@ -343,4 +345,35 @@ TEST_F(WebRTCIdentityStoreTest, IdentityPersistentAcrossRestart) {
EXPECT_EQ(key_1, key_2);
}
+TEST_F(WebRTCIdentityStoreTest, HandleDBErrors) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ Restart(temp_dir.path());
+
+ bool completed_1 = false;
+ std::string cert_1, key_1;
+
+ // Creates an identity.
+ RequestIdentityAndRunUtilIdle(kFakeOrigin,
+ kFakeIdentityName1,
+ kFakeCommonName1,
+ &completed_1,
+ &cert_1,
+ &key_1);
+
+ // Make the table corrupted.
+ base::FilePath db_path =
+ temp_dir.path().Append(FILE_PATH_LITERAL("WebRTCIdentityStore"));
+ EXPECT_TRUE(sql::test::CorruptSizeInHeader(db_path));
+
+ // Reset to commit the DB changes, which should fail and not crash.
+ webrtc_identity_store_ = NULL;
+ RunUntilIdle();
+
+ // Verifies the corrupted table was razed.
+ scoped_ptr<sql::Connection> db(new sql::Connection());
+ EXPECT_TRUE(db->Open(db_path));
+ EXPECT_EQ(0U, sql::test::CountSQLTables(db.get()));
+}
+
} // namespace content
diff --git a/chromium/content/browser/media/webrtc_internals.cc b/chromium/content/browser/media/webrtc_internals.cc
index e94447a3e0a..892f0378d2e 100644
--- a/chromium/content/browser/media/webrtc_internals.cc
+++ b/chromium/content/browser/media/webrtc_internals.cc
@@ -4,12 +4,15 @@
#include "content/browser/media/webrtc_internals.h"
+#include "base/path_service.h"
#include "content/browser/media/webrtc_internals_ui_observer.h"
+#include "content/browser/web_contents/web_contents_view.h"
#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/child_process_data.h"
+#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/web_contents.h"
using base::ProcessId;
using std::string;
@@ -30,15 +33,29 @@ static base::ListValue* EnsureLogList(base::DictionaryValue* dict) {
} // namespace
-WebRTCInternals::WebRTCInternals() : is_recording_rtp_(false) {
+WebRTCInternals::WebRTCInternals()
+ : aec_dump_enabled_(false) {
registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_TERMINATED,
NotificationService::AllBrowserContextsAndSources());
-
- BrowserChildProcessObserver::Add(this);
+// TODO(grunell): Shouldn't all the webrtc_internals* files be excluded from the
+// build if WebRTC is disabled?
+#if defined(ENABLE_WEBRTC)
+ aec_dump_file_path_ =
+ GetContentClient()->browser()->GetDefaultDownloadDirectory();
+ if (aec_dump_file_path_.empty()) {
+ // In this case the default path (|aec_dump_file_path_|) will be empty and
+ // the platform default path will be used in the file dialog (with no
+ // default file name). See SelectFileDialog::SelectFile. On Android where
+ // there's no dialog we'll fail to open the file.
+ VLOG(1) << "Could not get the download directory.";
+ } else {
+ aec_dump_file_path_ =
+ aec_dump_file_path_.Append(FILE_PATH_LITERAL("audio.aecdump"));
+ }
+#endif // defined(ENABLE_WEBRTC)
}
WebRTCInternals::~WebRTCInternals() {
- BrowserChildProcessObserver::Remove(this);
}
WebRTCInternals* WebRTCInternals::GetInstance() {
@@ -154,6 +171,30 @@ void WebRTCInternals::OnAddStats(base::ProcessId pid, int lid,
SendUpdate("addStats", &dict);
}
+void WebRTCInternals::OnGetUserMedia(int rid,
+ base::ProcessId pid,
+ const std::string& origin,
+ bool audio,
+ bool video,
+ const std::string& audio_constraints,
+ const std::string& video_constraints) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ dict->SetInteger("rid", rid);
+ dict->SetInteger("pid", static_cast<int>(pid));
+ dict->SetString("origin", origin);
+ if (audio)
+ dict->SetString("audio", audio_constraints);
+ if (video)
+ dict->SetString("video", video_constraints);
+
+ get_user_media_requests_.Append(dict);
+
+ if (observers_.might_have_observers())
+ SendUpdate("addGetUserMedia", dict);
+}
+
void WebRTCInternals::AddObserver(WebRTCInternalsUIObserver *observer) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
observers_.AddObserver(observer);
@@ -162,25 +203,65 @@ void WebRTCInternals::AddObserver(WebRTCInternalsUIObserver *observer) {
void WebRTCInternals::RemoveObserver(WebRTCInternalsUIObserver *observer) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
observers_.RemoveObserver(observer);
-}
-void WebRTCInternals::SendAllUpdates() {
- if (observers_.might_have_observers())
- SendUpdate("updateAllPeerConnections", &peer_connection_data_);
+ // Disables the AEC recording if it is enabled and the last webrtc-internals
+ // page is going away.
+ if (aec_dump_enabled_ && !observers_.might_have_observers())
+ DisableAecDump();
}
-void WebRTCInternals::StartRtpRecording() {
- if (!is_recording_rtp_) {
- is_recording_rtp_ = true;
- // TODO(justinlin): start RTP recording.
+void WebRTCInternals::UpdateObserver(WebRTCInternalsUIObserver* observer) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (peer_connection_data_.GetSize() > 0)
+ observer->OnUpdate("updateAllPeerConnections", &peer_connection_data_);
+
+ for (base::ListValue::iterator it = get_user_media_requests_.begin();
+ it != get_user_media_requests_.end();
+ ++it) {
+ observer->OnUpdate("addGetUserMedia", *it);
}
}
-void WebRTCInternals::StopRtpRecording() {
- if (is_recording_rtp_) {
- is_recording_rtp_ = false;
- // TODO(justinlin): stop RTP recording.
+void WebRTCInternals::EnableAecDump(content::WebContents* web_contents) {
+#if defined(ENABLE_WEBRTC)
+#if defined(OS_ANDROID)
+ EnableAecDumpOnAllRenderProcessHosts();
+#else
+ select_file_dialog_ = ui::SelectFileDialog::Create(this, NULL);
+ select_file_dialog_->SelectFile(
+ ui::SelectFileDialog::SELECT_SAVEAS_FILE,
+ base::string16(),
+ aec_dump_file_path_,
+ NULL,
+ 0,
+ FILE_PATH_LITERAL(""),
+ web_contents->GetTopLevelNativeWindow(),
+ NULL);
+#endif
+#endif
+}
+
+void WebRTCInternals::DisableAecDump() {
+#if defined(ENABLE_WEBRTC)
+ aec_dump_enabled_ = false;
+
+ // Tear down the dialog since the user has unchecked the AEC dump box.
+ select_file_dialog_ = NULL;
+
+ for (RenderProcessHost::iterator i(
+ content::RenderProcessHost::AllHostsIterator());
+ !i.IsAtEnd(); i.Advance()) {
+ i.GetCurrentValue()->DisableAecDump();
}
+#endif
+}
+
+void WebRTCInternals::ResetForTesting() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ observers_.Clear();
+ peer_connection_data_.Clear();
+ get_user_media_requests_.Clear();
+ aec_dump_enabled_ = false;
}
void WebRTCInternals::SendUpdate(const string& command, base::Value* value) {
@@ -191,12 +272,6 @@ void WebRTCInternals::SendUpdate(const string& command, base::Value* value) {
OnUpdate(command, value));
}
-void WebRTCInternals::BrowserChildProcessCrashed(
- const ChildProcessData& data) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- OnRendererExit(data.id);
-}
-
void WebRTCInternals::Observe(int type,
const NotificationSource& source,
const NotificationDetails& details) {
@@ -205,6 +280,21 @@ void WebRTCInternals::Observe(int type,
OnRendererExit(Source<RenderProcessHost>(source)->GetID());
}
+void WebRTCInternals::FileSelected(const base::FilePath& path,
+ int /* unused_index */,
+ void* /*unused_params */) {
+#if defined(ENABLE_WEBRTC)
+ aec_dump_file_path_ = path;
+ EnableAecDumpOnAllRenderProcessHosts();
+#endif
+}
+
+void WebRTCInternals::FileSelectionCanceled(void* params) {
+#if defined(ENABLE_WEBRTC)
+ SendUpdate("aecRecordingFileSelectionCancelled", NULL);
+#endif
+}
+
void WebRTCInternals::OnRendererExit(int render_process_id) {
// Iterates from the end of the list to remove the PeerConnections created
// by the exitting renderer.
@@ -229,15 +319,39 @@ void WebRTCInternals::OnRendererExit(int render_process_id) {
peer_connection_data_.Remove(i, NULL);
}
}
+
+ bool found_any = false;
+ // Iterates from the end of the list to remove the getUserMedia requests
+ // created by the exiting renderer.
+ for (int i = get_user_media_requests_.GetSize() - 1; i >= 0; --i) {
+ base::DictionaryValue* record = NULL;
+ get_user_media_requests_.GetDictionary(i, &record);
+
+ int this_rid = 0;
+ record->GetInteger("rid", &this_rid);
+
+ if (this_rid == render_process_id) {
+ get_user_media_requests_.Remove(i, NULL);
+ found_any = true;
+ }
+ }
+
+ if (found_any && observers_.might_have_observers()) {
+ base::DictionaryValue update;
+ update.SetInteger("rid", render_process_id);
+ SendUpdate("removeGetUserMediaForRenderer", &update);
+ }
}
-// TODO(justlin): Calls this method as necessary to update the recording status
-// UI.
-void WebRTCInternals::SendRtpRecordingUpdate() {
- DCHECK(is_recording_rtp_);
- base::DictionaryValue update;
- // TODO(justinlin): Fill in |update| with values as appropriate.
- SendUpdate("updateDumpStatus", &update);
+#if defined(ENABLE_WEBRTC)
+void WebRTCInternals::EnableAecDumpOnAllRenderProcessHosts() {
+ aec_dump_enabled_ = true;
+ for (RenderProcessHost::iterator i(
+ content::RenderProcessHost::AllHostsIterator());
+ !i.IsAtEnd(); i.Advance()) {
+ i.GetCurrentValue()->EnableAecDump(aec_dump_file_path_);
+ }
}
+#endif
} // namespace content
diff --git a/chromium/content/browser/media/webrtc_internals.h b/chromium/content/browser/media/webrtc_internals.h
index 58e9a9fbb9b..7ff95b9bca9 100644
--- a/chromium/content/browser/media/webrtc_internals.h
+++ b/chromium/content/browser/media/webrtc_internals.h
@@ -5,24 +5,26 @@
#ifndef CONTENT_BROWSER_MEDIA_WEBRTC_INTERNALS_H_
#define CONTENT_BROWSER_MEDIA_WEBRTC_INTERNALS_H_
+#include "base/gtest_prod_util.h"
#include "base/memory/singleton.h"
#include "base/observer_list.h"
#include "base/process/process.h"
#include "base/values.h"
#include "content/common/content_export.h"
-#include "content/public/browser/browser_child_process_observer.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
+#include "ui/shell_dialogs/select_file_dialog.h"
namespace content {
+class WebContents;
class WebRTCInternalsUIObserver;
// This is a singleton class running in the browser UI thread.
// It collects peer connection infomation from the renderers,
// forwards the data to WebRTCInternalsUIObserver and
// sends data collecting commands to the renderers.
-class CONTENT_EXPORT WebRTCInternals : public BrowserChildProcessObserver,
- public NotificationObserver {
+class CONTENT_EXPORT WebRTCInternals : public NotificationObserver,
+ public ui::SelectFileDialog::Listener {
public:
static WebRTCInternals* GetInstance();
@@ -58,38 +60,74 @@ class CONTENT_EXPORT WebRTCInternals : public BrowserChildProcessObserver,
// local id, |value| is the list of stats reports.
void OnAddStats(base::ProcessId pid, int lid, const base::ListValue& value);
+ // This method is called when getUserMedia is called. |render_process_id| is
+ // the id of the render process (not OS pid), which is needed because we might
+ // not be able to get the OS process id when the render process terminates and
+ // we want to clean up. |pid| is the renderer OS process id, |origin| is the
+ // security origin of the getUserMedia call, |audio| is true if audio stream
+ // is requested, |video| is true if the video stream is requested,
+ // |audio_constraints| is the constraints for the audio, |video_constraints|
+ // is the constraints for the video.
+ void OnGetUserMedia(int render_process_id,
+ base::ProcessId pid,
+ const std::string& origin,
+ bool audio,
+ bool video,
+ const std::string& audio_constraints,
+ const std::string& video_constraints);
+
// Methods for adding or removing WebRTCInternalsUIObserver.
void AddObserver(WebRTCInternalsUIObserver *observer);
void RemoveObserver(WebRTCInternalsUIObserver *observer);
- // Sends all update data to the observers.
- void SendAllUpdates();
+ // Sends all update data to |observer|.
+ void UpdateObserver(WebRTCInternalsUIObserver* observer);
+
+ // Enables or disables AEC dump (diagnostic echo canceller recording).
+ void EnableAecDump(content::WebContents* web_contents);
+ void DisableAecDump();
+
+ bool aec_dump_enabled() {
+ return aec_dump_enabled_;
+ }
- // Tells the renderer processes to start or stop recording RTP packets.
- void StartRtpRecording();
- void StopRtpRecording();
+ base::FilePath aec_dump_file_path() {
+ return aec_dump_file_path_;
+ }
+
+ void ResetForTesting();
private:
friend struct DefaultSingletonTraits<WebRTCInternals>;
+ FRIEND_TEST_ALL_PREFIXES(WebRtcBrowserTest, CallWithAecDump);
+ FRIEND_TEST_ALL_PREFIXES(WebRtcBrowserTest,
+ CallWithAecDumpEnabledThenDisabled);
+ FRIEND_TEST_ALL_PREFIXES(WebRTCInternalsTest,
+ AecRecordingFileSelectionCanceled);
WebRTCInternals();
virtual ~WebRTCInternals();
void SendUpdate(const std::string& command, base::Value* value);
- // BrowserChildProcessObserver implementation.
- virtual void BrowserChildProcessCrashed(
- const ChildProcessData& data) OVERRIDE;
-
// NotificationObserver implementation.
virtual void Observe(int type,
const NotificationSource& source,
const NotificationDetails& details) OVERRIDE;
+ // ui::SelectFileDialog::Listener implementation.
+ virtual void FileSelected(const base::FilePath& path,
+ int index,
+ void* unused_params) OVERRIDE;
+ virtual void FileSelectionCanceled(void* params) OVERRIDE;
+
// Called when a renderer exits (including crashes).
void OnRendererExit(int render_process_id);
- void SendRtpRecordingUpdate();
+#if defined(ENABLE_WEBRTC)
+ // Enables AEC dump on all render process hosts using |aec_dump_file_path_|.
+ void EnableAecDumpOnAllRenderProcessHosts();
+#endif
ObserverList<WebRTCInternalsUIObserver> observers_;
@@ -97,7 +135,8 @@ class CONTENT_EXPORT WebRTCInternals : public BrowserChildProcessObserver,
// updates.
// Each item of the list represents the data for one PeerConnection, which
// contains these fields:
- // "pid" -- processId of the renderer that creates the PeerConnection.
+ // "rid" -- the renderer id.
+ // "pid" -- OS process id of the renderer that creates the PeerConnection.
// "lid" -- local Id assigned to the PeerConnection.
// "url" -- url of the web page that created the PeerConnection.
// "servers" and "constraints" -- server configuration and media constraints
@@ -107,9 +146,23 @@ class CONTENT_EXPORT WebRTCInternals : public BrowserChildProcessObserver,
// are strings.
base::ListValue peer_connection_data_;
+ // A list of getUserMedia requests. Each item is a DictionaryValue that
+ // contains these fields:
+ // "rid" -- the renderer id.
+ // "pid" -- proceddId of the renderer.
+ // "origin" -- the security origin of the request.
+ // "audio" -- the serialized audio constraints if audio is requested.
+ // "video" -- the serialized video constraints if video is requested.
+ base::ListValue get_user_media_requests_;
+
NotificationRegistrar registrar_;
- bool is_recording_rtp_;
+ // For managing select file dialog.
+ scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
+
+ // AEC dump (diagnostic echo canceller recording) state.
+ bool aec_dump_enabled_;
+ base::FilePath aec_dump_file_path_;
};
} // namespace content
diff --git a/chromium/content/browser/media/webrtc_internals_browsertest.cc b/chromium/content/browser/media/webrtc_internals_browsertest.cc
index 0d279b3075b..7b352d66b24 100644
--- a/chromium/content/browser/media/webrtc_internals_browsertest.cc
+++ b/chromium/content/browser/media/webrtc_internals_browsertest.cc
@@ -9,9 +9,10 @@
#include "base/values.h"
#include "content/public/common/content_switches.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/shell/browser/shell.h"
-#include "content/test/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
+#include "media/base/media_switches.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
using std::string;
@@ -116,19 +117,39 @@ class PeerConnectionEntry {
std::map<string, StatsMap> stats_;
};
+class UserMediaRequestEntry {
+ public:
+ UserMediaRequestEntry(int pid,
+ int rid,
+ const std::string& origin,
+ const std::string& audio_constraints,
+ const std::string& video_constraints)
+ : pid(pid),
+ rid(rid),
+ origin(origin),
+ audio_constraints(audio_constraints),
+ video_constraints(video_constraints) {}
+
+ int pid;
+ int rid;
+ std::string origin;
+ std::string audio_constraints;
+ std::string video_constraints;
+};
+
static const int64 FAKE_TIME_STAMP = 3600000;
#if defined(OS_WIN)
// All tests are flaky on Windows: crbug.com/277322.
-#define MAYBE_WebRTCInternalsBrowserTest DISABLED_WebRTCInternalsBrowserTest
+#define MAYBE_WebRtcInternalsBrowserTest DISABLED_WebRtcInternalsBrowserTest
#else
-#define MAYBE_WebRTCInternalsBrowserTest WebRTCInternalsBrowserTest
+#define MAYBE_WebRtcInternalsBrowserTest WebRtcInternalsBrowserTest
#endif
-class MAYBE_WebRTCInternalsBrowserTest: public ContentBrowserTest {
+class MAYBE_WebRtcInternalsBrowserTest: public ContentBrowserTest {
public:
- MAYBE_WebRTCInternalsBrowserTest() {}
- virtual ~MAYBE_WebRTCInternalsBrowserTest() {}
+ MAYBE_WebRtcInternalsBrowserTest() {}
+ virtual ~MAYBE_WebRtcInternalsBrowserTest() {}
virtual void SetUpOnMainThread() OVERRIDE {
// We need fake devices in this test since we want to run on naked VMs. We
@@ -145,7 +166,7 @@ class MAYBE_WebRTCInternalsBrowserTest: public ContentBrowserTest {
}
void ExpectTitle(const std::string& expected_title) const {
- base::string16 expected_title16(ASCIIToUTF16(expected_title));
+ base::string16 expected_title16(base::ASCIIToUTF16(expected_title));
TitleWatcher title_watcher(shell()->web_contents(), expected_title16);
EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle());
}
@@ -166,6 +187,24 @@ class MAYBE_WebRTCInternalsBrowserTest: public ContentBrowserTest {
ASSERT_TRUE(ExecuteJavascript("removePeerConnection(" + ss.str() + ");"));
}
+ // Execute the javascript of addGetUserMedia.
+ void ExecuteAddGetUserMediaJs(const UserMediaRequestEntry& request) {
+ std::stringstream ss;
+ ss << "{pid:" << request.pid << ", rid:" << request.rid << ", origin:'"
+ << request.origin << "', audio:'" << request.audio_constraints
+ << "', video:'" << request.video_constraints << "'}";
+
+ ASSERT_TRUE(ExecuteJavascript("addGetUserMedia(" + ss.str() + ");"));
+ }
+
+ // Execute the javascript of removeGetUserMediaForRenderer.
+ void ExecuteRemoveGetUserMediaForRendererJs(int rid) {
+ std::stringstream ss;
+ ss << "{rid:" << rid << "}";
+ ASSERT_TRUE(
+ ExecuteJavascript("removeGetUserMediaForRenderer(" + ss.str() + ");"));
+ }
+
// Verifies that the DOM element with id |id| exists.
void VerifyElementWithId(const string& id) {
bool result = false;
@@ -186,6 +225,42 @@ class MAYBE_WebRTCInternalsBrowserTest: public ContentBrowserTest {
EXPECT_TRUE(result);
}
+ // Verifies the JS Array of userMediaRequests matches |requests|.
+ void VerifyUserMediaRequest(
+ const std::vector<UserMediaRequestEntry>& requests) {
+ string json_requests;
+ ASSERT_TRUE(ExecuteScriptAndExtractString(
+ shell()->web_contents(),
+ "window.domAutomationController.send("
+ "JSON.stringify(userMediaRequests));",
+ &json_requests));
+ scoped_ptr<base::Value> value_requests;
+ value_requests.reset(base::JSONReader::Read(json_requests));
+
+ EXPECT_EQ(base::Value::TYPE_LIST, value_requests->GetType());
+
+ base::ListValue* list_request =
+ static_cast<base::ListValue*>(value_requests.get());
+ EXPECT_EQ(requests.size(), list_request->GetSize());
+
+ for (size_t i = 0; i < requests.size(); ++i) {
+ base::DictionaryValue* dict = NULL;
+ ASSERT_TRUE(list_request->GetDictionary(i, &dict));
+ int pid, rid;
+ std::string origin, audio, video;
+ ASSERT_TRUE(dict->GetInteger("pid", &pid));
+ ASSERT_TRUE(dict->GetInteger("rid", &rid));
+ ASSERT_TRUE(dict->GetString("origin", &origin));
+ ASSERT_TRUE(dict->GetString("audio", &audio));
+ ASSERT_TRUE(dict->GetString("video", &video));
+ EXPECT_EQ(requests[i].pid, pid);
+ EXPECT_EQ(requests[i].rid, rid);
+ EXPECT_EQ(requests[i].origin, origin);
+ EXPECT_EQ(requests[i].audio_constraints, audio);
+ EXPECT_EQ(requests[i].video_constraints, video);
+ }
+ }
+
// Verifies that DOM for |pc| is correctly created with the right content.
void VerifyPeerConnectionEntry(const PeerConnectionEntry& pc) {
VerifyElementWithId(pc.getIdString());
@@ -402,7 +477,7 @@ class MAYBE_WebRTCInternalsBrowserTest: public ContentBrowserTest {
}
};
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRTCInternalsBrowserTest,
+IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest,
AddAndRemovePeerConnection) {
GURL url("chrome://webrtc-internals");
NavigateToURL(shell(), url);
@@ -424,7 +499,7 @@ IN_PROC_BROWSER_TEST_F(MAYBE_WebRTCInternalsBrowserTest,
VerifyNoElementWithId(pc_2.getIdString());
}
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRTCInternalsBrowserTest,
+IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest,
UpdateAllPeerConnections) {
GURL url("chrome://webrtc-internals");
NavigateToURL(shell(), url);
@@ -442,7 +517,7 @@ IN_PROC_BROWSER_TEST_F(MAYBE_WebRTCInternalsBrowserTest,
VerifyPeerConnectionEntry(pc_1);
}
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRTCInternalsBrowserTest, UpdatePeerConnection) {
+IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, UpdatePeerConnection) {
GURL url("chrome://webrtc-internals");
NavigateToURL(shell(), url);
@@ -479,7 +554,7 @@ IN_PROC_BROWSER_TEST_F(MAYBE_WebRTCInternalsBrowserTest, UpdatePeerConnection) {
}
// Tests that adding random named stats updates the dataSeries and graphs.
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRTCInternalsBrowserTest, AddStats) {
+IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, AddStats) {
GURL url("chrome://webrtc-internals");
NavigateToURL(shell(), url);
@@ -505,7 +580,7 @@ IN_PROC_BROWSER_TEST_F(MAYBE_WebRTCInternalsBrowserTest, AddStats) {
}
// Tests that the bandwidth estimation values are drawn on a single graph.
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRTCInternalsBrowserTest, BweCompoundGraph) {
+IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, BweCompoundGraph) {
GURL url("chrome://webrtc-internals");
NavigateToURL(shell(), url);
@@ -545,7 +620,7 @@ IN_PROC_BROWSER_TEST_F(MAYBE_WebRTCInternalsBrowserTest, BweCompoundGraph) {
// Tests that the total packet/byte count is converted to count per second,
// and the converted data is drawn.
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRTCInternalsBrowserTest, ConvertedGraphs) {
+IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, ConvertedGraphs) {
GURL url("chrome://webrtc-internals");
NavigateToURL(shell(), url);
@@ -587,7 +662,7 @@ IN_PROC_BROWSER_TEST_F(MAYBE_WebRTCInternalsBrowserTest, ConvertedGraphs) {
// Timing out on ARM linux bot: http://crbug.com/238490
// Disabling due to failure on Linux, Mac, Win: http://crbug.com/272413
// Sanity check of the page content under a real PeerConnection call.
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRTCInternalsBrowserTest,
+IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest,
DISABLED_WithRealPeerConnectionCall) {
// Start a peerconnection call in the first window.
ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
@@ -670,7 +745,7 @@ IN_PROC_BROWSER_TEST_F(MAYBE_WebRTCInternalsBrowserTest,
EXPECT_GT(count, 0);
}
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRTCInternalsBrowserTest, CreatePageDump) {
+IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, CreatePageDump) {
GURL url("chrome://webrtc-internals");
NavigateToURL(shell(), url);
@@ -715,4 +790,76 @@ IN_PROC_BROWSER_TEST_F(MAYBE_WebRTCInternalsBrowserTest, CreatePageDump) {
VerifyStatsDump(dump.get(), pc_0, type, id, stats);
}
+IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, UpdateGetUserMedia) {
+ GURL url("chrome://webrtc-internals");
+ NavigateToURL(shell(), url);
+
+ UserMediaRequestEntry request1(1, 1, "origin", "ac", "vc");
+ UserMediaRequestEntry request2(2, 2, "origin2", "ac2", "vc2");
+ ExecuteAddGetUserMediaJs(request1);
+ ExecuteAddGetUserMediaJs(request2);
+
+ std::vector<UserMediaRequestEntry> list;
+ list.push_back(request1);
+ list.push_back(request2);
+ VerifyUserMediaRequest(list);
+
+ ExecuteRemoveGetUserMediaForRendererJs(1);
+ list.erase(list.begin());
+ VerifyUserMediaRequest(list);
+
+ ExecuteRemoveGetUserMediaForRendererJs(2);
+ list.erase(list.begin());
+ VerifyUserMediaRequest(list);
+}
+
+// Tests that the received propagation delta values are converted and drawn
+// correctly.
+IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest,
+ ReceivedPropagationDelta) {
+ GURL url("chrome://webrtc-internals");
+ NavigateToURL(shell(), url);
+
+ PeerConnectionEntry pc(1, 0);
+ ExecuteAddPeerConnectionJs(pc);
+
+ StatsUnit stats = {FAKE_TIME_STAMP};
+ stats.values["googReceivedPacketGroupArrivalTimeDebug"] =
+ "[1000, 1100, 1200]";
+ stats.values["googReceivedPacketGroupPropagationDeltaDebug"] =
+ "[10, 20, 30]";
+ const string stats_type = "bwe";
+ const string stats_id = "videobwe";
+ ExecuteAndVerifyAddStats(pc, stats_type, stats_id, stats);
+
+ string graph_id = pc.getIdString() + "-" + stats_id +
+ "-googReceivedPacketGroupPropagationDeltaDebug";
+ string data_series_id =
+ stats_id + "-googReceivedPacketGroupPropagationDeltaDebug";
+ bool result = false;
+ // Verify that the graph exists.
+ ASSERT_TRUE(ExecuteScriptAndExtractBool(
+ shell()->web_contents(),
+ "window.domAutomationController.send("
+ " graphViews['" + graph_id + "'] != null)",
+ &result));
+ EXPECT_TRUE(result);
+
+ // Verify that the graph contains multiple data points.
+ int count = 0;
+ ASSERT_TRUE(ExecuteScriptAndExtractInt(
+ shell()->web_contents(),
+ "window.domAutomationController.send("
+ " graphViews['" + graph_id + "'].getDataSeriesCount())",
+ &count));
+ EXPECT_EQ(1, count);
+ ASSERT_TRUE(ExecuteScriptAndExtractInt(
+ shell()->web_contents(),
+ "window.domAutomationController.send("
+ " peerConnectionDataStore['" + pc.getIdString() + "']" +
+ " .getDataSeries('" + data_series_id + "').getCount())",
+ &count));
+ EXPECT_EQ(3, count);
+}
+
} // namespace content
diff --git a/chromium/content/browser/media/webrtc_internals_message_handler.cc b/chromium/content/browser/media/webrtc_internals_message_handler.cc
index 8cf85790286..bffd5098bfa 100644
--- a/chromium/content/browser/media/webrtc_internals_message_handler.cc
+++ b/chromium/content/browser/media/webrtc_internals_message_handler.cc
@@ -7,8 +7,8 @@
#include "content/browser/media/webrtc_internals.h"
#include "content/common/media/peer_connection_tracker_messages.h"
#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
-#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
@@ -23,29 +23,25 @@ WebRTCInternalsMessageHandler::~WebRTCInternalsMessageHandler() {
}
void WebRTCInternalsMessageHandler::RegisterMessages() {
- web_ui()->RegisterMessageCallback("getAllUpdates",
- base::Bind(&WebRTCInternalsMessageHandler::OnGetAllUpdates,
- base::Unretained(this)));
-
web_ui()->RegisterMessageCallback("getAllStats",
base::Bind(&WebRTCInternalsMessageHandler::OnGetAllStats,
base::Unretained(this)));
- web_ui()->RegisterMessageCallback("startRtpRecording",
- base::Bind(&WebRTCInternalsMessageHandler::OnStartRtpRecording,
- base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("enableAecRecording",
+ base::Bind(&WebRTCInternalsMessageHandler::OnSetAecRecordingEnabled,
+ base::Unretained(this), true));
- web_ui()->RegisterMessageCallback("stopRtpRecording",
- base::Bind(&WebRTCInternalsMessageHandler::OnStopRtpRecording,
- base::Unretained(this)));
-}
+ web_ui()->RegisterMessageCallback("disableAecRecording",
+ base::Bind(&WebRTCInternalsMessageHandler::OnSetAecRecordingEnabled,
+ base::Unretained(this), false));
-void WebRTCInternalsMessageHandler::OnGetAllUpdates(
- const base::ListValue* list) {
- WebRTCInternals::GetInstance()->SendAllUpdates();
+ web_ui()->RegisterMessageCallback("finishedDOMLoad",
+ base::Bind(&WebRTCInternalsMessageHandler::OnDOMLoadDone,
+ base::Unretained(this)));
}
-void WebRTCInternalsMessageHandler::OnGetAllStats(const base::ListValue* list) {
+void WebRTCInternalsMessageHandler::OnGetAllStats(
+ const base::ListValue* /* unused_list */) {
for (RenderProcessHost::iterator i(
content::RenderProcessHost::AllHostsIterator());
!i.IsAtEnd(); i.Advance()) {
@@ -53,26 +49,39 @@ void WebRTCInternalsMessageHandler::OnGetAllStats(const base::ListValue* list) {
}
}
-void WebRTCInternalsMessageHandler::OnStartRtpRecording(
- const base::ListValue* list) {
- WebRTCInternals::GetInstance()->StartRtpRecording();
+void WebRTCInternalsMessageHandler::OnSetAecRecordingEnabled(
+ bool enable, const base::ListValue* /* unused_list */) {
+ if (enable)
+ WebRTCInternals::GetInstance()->EnableAecDump(web_ui()->GetWebContents());
+ else
+ WebRTCInternals::GetInstance()->DisableAecDump();
}
-void WebRTCInternalsMessageHandler::OnStopRtpRecording(
- const base::ListValue* list) {
- WebRTCInternals::GetInstance()->StopRtpRecording();
+void WebRTCInternalsMessageHandler::OnDOMLoadDone(
+ const base::ListValue* /* unused_list */) {
+ WebRTCInternals::GetInstance()->UpdateObserver(this);
+
+ if (WebRTCInternals::GetInstance()->aec_dump_enabled()) {
+ std::vector<const base::Value*> args_vector;
+ base::string16 script = WebUI::GetJavascriptCall("setAecRecordingEnabled",
+ args_vector);
+ RenderFrameHost* host = web_ui()->GetWebContents()->GetMainFrame();
+ if (host)
+ host->ExecuteJavaScript(script);
+ }
}
void WebRTCInternalsMessageHandler::OnUpdate(const std::string& command,
- const base::Value* args) {
+ const base::Value* args) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
std::vector<const base::Value*> args_vector;
- args_vector.push_back(args);
+ if (args)
+ args_vector.push_back(args);
base::string16 update = WebUI::GetJavascriptCall(command, args_vector);
- RenderViewHost* host = web_ui()->GetWebContents()->GetRenderViewHost();
+ RenderFrameHost* host = web_ui()->GetWebContents()->GetMainFrame();
if (host)
- host->ExecuteJavascriptInWebFrame(base::string16(), update);
+ host->ExecuteJavaScript(update);
}
} // namespace content
diff --git a/chromium/content/browser/media/webrtc_internals_message_handler.h b/chromium/content/browser/media/webrtc_internals_message_handler.h
index ee2059a646d..8c5f10ca401 100644
--- a/chromium/content/browser/media/webrtc_internals_message_handler.h
+++ b/chromium/content/browser/media/webrtc_internals_message_handler.h
@@ -30,13 +30,12 @@ class WebRTCInternalsMessageHandler : public WebUIMessageHandler,
virtual void OnUpdate(const std::string& command,
const base::Value* args) OVERRIDE;
+ private:
// Javascript message handler.
- void OnGetAllUpdates(const base::ListValue* list);
void OnGetAllStats(const base::ListValue* list);
- void OnStartRtpRecording(const base::ListValue* list);
- void OnStopRtpRecording(const base::ListValue* list);
+ void OnSetAecRecordingEnabled(bool enable, const base::ListValue* list);
+ void OnDOMLoadDone(const base::ListValue* list);
- private:
DISALLOW_COPY_AND_ASSIGN(WebRTCInternalsMessageHandler);
};
diff --git a/chromium/content/browser/media/webrtc_internals_ui_observer.h b/chromium/content/browser/media/webrtc_internals_ui_observer.h
index 8d2c60e63b8..31082b6181f 100644
--- a/chromium/content/browser/media/webrtc_internals_ui_observer.h
+++ b/chromium/content/browser/media/webrtc_internals_ui_observer.h
@@ -18,7 +18,8 @@ class WebRTCInternalsUIObserver {
public:
virtual ~WebRTCInternalsUIObserver() {}
- // This is called on the browser IO thread.
+ // This is called on the browser IO thread. |args| can be NULL if there are no
+ // arguments.
virtual void OnUpdate(const std::string& command,
const base::Value* args) = 0;
};
diff --git a/chromium/content/browser/media/webrtc_internals_unittest.cc b/chromium/content/browser/media/webrtc_internals_unittest.cc
index a00ccb9877c..e4b0531fa54 100644
--- a/chromium/content/browser/media/webrtc_internals_unittest.cc
+++ b/chromium/content/browser/media/webrtc_internals_unittest.cc
@@ -23,7 +23,8 @@ class MockWebRTCInternalsProxy : public WebRTCInternalsUIObserver {
virtual void OnUpdate(const std::string& command,
const base::Value* value) OVERRIDE {
command_ = command;
- value_.reset(value->DeepCopy());
+ if (value)
+ value_.reset(value->DeepCopy());
}
std::string command() {
@@ -41,17 +42,49 @@ class MockWebRTCInternalsProxy : public WebRTCInternalsUIObserver {
class WebRTCInternalsTest : public testing::Test {
public:
- WebRTCInternalsTest() : io_thread_(BrowserThread::UI, &io_loop_) {}
+ WebRTCInternalsTest() : io_thread_(BrowserThread::UI, &io_loop_) {
+ WebRTCInternals::GetInstance()->ResetForTesting();
+ }
protected:
- std::string ExpectedInfo(std::string prefix,
- std::string id,
- std::string suffix) {
- static const std::string kstatic_part1 = std::string(
- "{\"constraints\":\"c\",");
- static const std::string kstatic_part2 = std::string(
- ",\"servers\":\"s\",\"url\":\"u\"}");
- return prefix + kstatic_part1 + id + kstatic_part2 + suffix;
+ void VerifyString(const base::DictionaryValue* dict,
+ const std::string& key,
+ const std::string& expected) {
+ std::string actual;
+ EXPECT_TRUE(dict->GetString(key, &actual));
+ EXPECT_EQ(expected, actual);
+ }
+
+ void VerifyInt(const base::DictionaryValue* dict,
+ const std::string& key,
+ int expected) {
+ int actual;
+ EXPECT_TRUE(dict->GetInteger(key, &actual));
+ EXPECT_EQ(expected, actual);
+ }
+
+ void VerifyList(const base::DictionaryValue* dict,
+ const std::string& key,
+ const base::ListValue& expected) {
+ const base::ListValue* actual = NULL;
+ EXPECT_TRUE(dict->GetList(key, &actual));
+ EXPECT_TRUE(expected.Equals(actual));
+ }
+
+ void VerifyGetUserMediaData(base::Value* actual_data,
+ int rid,
+ int pid,
+ const std::string& origin,
+ const std::string& audio,
+ const std::string& video) {
+ base::DictionaryValue* dict = NULL;
+ EXPECT_TRUE(actual_data->GetAsDictionary(&dict));
+
+ VerifyInt(dict, "rid", rid);
+ VerifyInt(dict, "pid", pid);
+ VerifyString(dict, "origin", origin);
+ VerifyString(dict, "audio", audio);
+ VerifyString(dict, "video", video);
}
base::MessageLoop io_loop_;
@@ -83,19 +116,11 @@ TEST_F(WebRTCInternalsTest, SendAddPeerConnectionUpdate) {
base::DictionaryValue* dict = NULL;
EXPECT_TRUE(observer->value()->GetAsDictionary(&dict));
- int int_value;
- EXPECT_TRUE(dict->GetInteger("pid", &int_value));
- EXPECT_EQ(1, int_value);
- EXPECT_TRUE(dict->GetInteger("lid", &int_value));
- EXPECT_EQ(2, int_value);
-
- std::string value;
- EXPECT_TRUE(dict->GetString("url", &value));
- EXPECT_EQ(kUrl, value);
- EXPECT_TRUE(dict->GetString("servers", &value));
- EXPECT_EQ(kServers, value);
- EXPECT_TRUE(dict->GetString("constraints", &value));
- EXPECT_EQ(kContraints, value);
+ VerifyInt(dict, "pid", 1);
+ VerifyInt(dict, "lid", 2);
+ VerifyString(dict, "url", kUrl);
+ VerifyString(dict, "servers", kServers);
+ VerifyString(dict, "constraints", kContraints);
WebRTCInternals::GetInstance()->RemoveObserver(observer.get());
WebRTCInternals::GetInstance()->OnRemovePeerConnection(1, 2);
@@ -113,11 +138,8 @@ TEST_F(WebRTCInternalsTest, SendRemovePeerConnectionUpdate) {
base::DictionaryValue* dict = NULL;
EXPECT_TRUE(observer->value()->GetAsDictionary(&dict));
- int int_value;
- EXPECT_TRUE(dict->GetInteger("pid", &int_value));
- EXPECT_EQ(1, int_value);
- EXPECT_TRUE(dict->GetInteger("lid", &int_value));
- EXPECT_EQ(2, int_value);
+ VerifyInt(dict, "pid", 1);
+ VerifyInt(dict, "lid", 2);
WebRTCInternals::GetInstance()->RemoveObserver(observer.get());
}
@@ -139,20 +161,122 @@ TEST_F(WebRTCInternalsTest, SendUpdatePeerConnectionUpdate) {
base::DictionaryValue* dict = NULL;
EXPECT_TRUE(observer->value()->GetAsDictionary(&dict));
- int int_value;
- EXPECT_TRUE(dict->GetInteger("pid", &int_value));
- EXPECT_EQ(1, int_value);
- EXPECT_TRUE(dict->GetInteger("lid", &int_value));
- EXPECT_EQ(2, int_value);
-
- std::string value;
- EXPECT_TRUE(dict->GetString("type", &value));
- EXPECT_EQ(update_type, value);
- EXPECT_TRUE(dict->GetString("value", &value));
- EXPECT_EQ(update_value, value);
+ VerifyInt(dict, "pid", 1);
+ VerifyInt(dict, "lid", 2);
+ VerifyString(dict, "type", update_type);
+ VerifyString(dict, "value", update_value);
WebRTCInternals::GetInstance()->OnRemovePeerConnection(1, 2);
WebRTCInternals::GetInstance()->RemoveObserver(observer.get());
}
+TEST_F(WebRTCInternalsTest, AddGetUserMedia) {
+ scoped_ptr<MockWebRTCInternalsProxy> observer(new MockWebRTCInternalsProxy());
+
+ // Add one observer before "getUserMedia".
+ WebRTCInternals::GetInstance()->AddObserver(observer.get());
+
+ const int rid = 1;
+ const int pid = 2;
+ const std::string audio_constraint = "aaa";
+ const std::string video_constraint = "vvv";
+ WebRTCInternals::GetInstance()->OnGetUserMedia(
+ rid, pid, kUrl, true, true, audio_constraint, video_constraint);
+
+ EXPECT_EQ("addGetUserMedia", observer->command());
+ VerifyGetUserMediaData(
+ observer->value(), rid, pid, kUrl, audio_constraint, video_constraint);
+
+ WebRTCInternals::GetInstance()->RemoveObserver(observer.get());
+}
+
+TEST_F(WebRTCInternalsTest, SendAllUpdateWithGetUserMedia) {
+ const int rid = 1;
+ const int pid = 2;
+ const std::string audio_constraint = "aaa";
+ const std::string video_constraint = "vvv";
+ WebRTCInternals::GetInstance()->OnGetUserMedia(
+ rid, pid, kUrl, true, true, audio_constraint, video_constraint);
+
+ scoped_ptr<MockWebRTCInternalsProxy> observer(new MockWebRTCInternalsProxy());
+ // Add one observer after "getUserMedia".
+ WebRTCInternals::GetInstance()->AddObserver(observer.get());
+ WebRTCInternals::GetInstance()->UpdateObserver(observer.get());
+
+ EXPECT_EQ("addGetUserMedia", observer->command());
+ VerifyGetUserMediaData(
+ observer->value(), rid, pid, kUrl, audio_constraint, video_constraint);
+
+ WebRTCInternals::GetInstance()->RemoveObserver(observer.get());
+}
+
+TEST_F(WebRTCInternalsTest, SendAllUpdatesWithPeerConnectionUpdate) {
+ const int rid = 0, pid = 1, lid = 2;
+ const std::string update_type = "fakeType";
+ const std::string update_value = "fakeValue";
+
+ WebRTCInternals::GetInstance()->OnAddPeerConnection(
+ rid, pid, lid, kUrl, kServers, kContraints);
+ WebRTCInternals::GetInstance()->OnUpdatePeerConnection(
+ pid, lid, update_type, update_value);
+
+ scoped_ptr<MockWebRTCInternalsProxy> observer(new MockWebRTCInternalsProxy());
+ WebRTCInternals::GetInstance()->AddObserver(observer.get());
+
+ WebRTCInternals::GetInstance()->UpdateObserver(observer.get());
+
+ EXPECT_EQ("updateAllPeerConnections", observer->command());
+
+ base::ListValue* list = NULL;
+ EXPECT_TRUE(observer->value()->GetAsList(&list));
+ EXPECT_EQ(1U, list->GetSize());
+
+ base::DictionaryValue* dict = NULL;
+ EXPECT_TRUE((*list->begin())->GetAsDictionary(&dict));
+
+ VerifyInt(dict, "rid", rid);
+ VerifyInt(dict, "pid", pid);
+ VerifyInt(dict, "lid", lid);
+ VerifyString(dict, "url", kUrl);
+ VerifyString(dict, "servers", kServers);
+ VerifyString(dict, "constraints", kContraints);
+
+ base::ListValue* log = NULL;
+ EXPECT_TRUE(dict->GetList("log", &log));
+ EXPECT_EQ(1U, log->GetSize());
+
+ EXPECT_TRUE((*log->begin())->GetAsDictionary(&dict));
+ VerifyString(dict, "type", update_type);
+ VerifyString(dict, "value", update_value);
+}
+
+TEST_F(WebRTCInternalsTest, OnAddStats) {
+ const int rid = 0, pid = 1, lid = 2;
+ scoped_ptr<MockWebRTCInternalsProxy> observer(new MockWebRTCInternalsProxy());
+ WebRTCInternals::GetInstance()->AddObserver(observer.get());
+ WebRTCInternals::GetInstance()->OnAddPeerConnection(
+ rid, pid, lid, kUrl, kServers, kContraints);
+
+ base::ListValue list;
+ list.AppendString("xxx");
+ list.AppendString("yyy");
+ WebRTCInternals::GetInstance()->OnAddStats(pid, lid, list);
+
+ EXPECT_EQ("addStats", observer->command());
+ base::DictionaryValue* dict = NULL;
+ EXPECT_TRUE(observer->value()->GetAsDictionary(&dict));
+
+ VerifyInt(dict, "pid", pid);
+ VerifyInt(dict, "lid", lid);
+ VerifyList(dict, "reports", list);
+}
+
+TEST_F(WebRTCInternalsTest, AecRecordingFileSelectionCanceled) {
+ scoped_ptr<MockWebRTCInternalsProxy> observer(new MockWebRTCInternalsProxy());
+ WebRTCInternals::GetInstance()->AddObserver(observer.get());
+ WebRTCInternals::GetInstance()->FileSelectionCanceled(NULL);
+ EXPECT_EQ("aecRecordingFileSelectionCancelled", observer->command());
+ EXPECT_EQ(NULL, observer->value());
+}
+
} // namespace content
diff --git a/chromium/content/browser/message_port_message_filter.cc b/chromium/content/browser/message_port_message_filter.cc
index 70d8175b503..ab3174bc8cc 100644
--- a/chromium/content/browser/message_port_message_filter.cc
+++ b/chromium/content/browser/message_port_message_filter.cc
@@ -11,7 +11,8 @@ namespace content {
MessagePortMessageFilter::MessagePortMessageFilter(
const NextRoutingIDCallback& callback)
- : next_routing_id_(callback) {
+ : BrowserMessageFilter(MessagePortMsgStart),
+ next_routing_id_(callback) {
}
MessagePortMessageFilter::~MessagePortMessageFilter() {
@@ -21,10 +22,9 @@ void MessagePortMessageFilter::OnChannelClosing() {
MessagePortService::GetInstance()->OnMessagePortMessageFilterClosing(this);
}
-bool MessagePortMessageFilter::OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) {
+bool MessagePortMessageFilter::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(MessagePortMessageFilter, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(MessagePortMessageFilter, message)
IPC_MESSAGE_HANDLER(MessagePortHostMsg_CreateMessagePort,
OnCreateMessagePort)
IPC_MESSAGE_FORWARD(MessagePortHostMsg_DestroyMessagePort,
@@ -43,7 +43,7 @@ bool MessagePortMessageFilter::OnMessageReceived(const IPC::Message& message,
MessagePortService::GetInstance(),
MessagePortService::SendQueuedMessages)
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return handled;
}
@@ -56,6 +56,22 @@ int MessagePortMessageFilter::GetNextRoutingID() {
return next_routing_id_.Run();
}
+void MessagePortMessageFilter::UpdateMessagePortsWithNewRoutes(
+ const std::vector<int>& message_port_ids,
+ std::vector<int>* new_routing_ids) {
+ DCHECK(new_routing_ids);
+ new_routing_ids->clear();
+ new_routing_ids->resize(message_port_ids.size());
+
+ for (size_t i = 0; i < message_port_ids.size(); ++i) {
+ (*new_routing_ids)[i] = GetNextRoutingID();
+ MessagePortService::GetInstance()->UpdateMessagePort(
+ message_port_ids[i],
+ this,
+ (*new_routing_ids)[i]);
+ }
+}
+
void MessagePortMessageFilter::OnCreateMessagePort(int *route_id,
int* message_port_id) {
*route_id = next_routing_id_.Run();
diff --git a/chromium/content/browser/message_port_message_filter.h b/chromium/content/browser/message_port_message_filter.h
index 0dd38c94e6c..64d07d6c328 100644
--- a/chromium/content/browser/message_port_message_filter.h
+++ b/chromium/content/browser/message_port_message_filter.h
@@ -6,13 +6,14 @@
#define CONTENT_BROWSER_MESSAGE_PORT_MESSAGE_FILTER_H_
#include "base/callback.h"
+#include "content/common/content_export.h"
#include "content/public/browser/browser_message_filter.h"
namespace content {
// Filter for MessagePort related IPC messages (creating and destroying a
// MessagePort, sending a message via a MessagePort etc).
-class MessagePortMessageFilter : public BrowserMessageFilter {
+class CONTENT_EXPORT MessagePortMessageFilter : public BrowserMessageFilter {
public:
typedef base::Callback<int(void)> NextRoutingIDCallback;
@@ -22,18 +23,25 @@ class MessagePortMessageFilter : public BrowserMessageFilter {
// BrowserMessageFilter implementation.
virtual void OnChannelClosing() OVERRIDE;
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
virtual void OnDestruct() const OVERRIDE;
int GetNextRoutingID();
+ // Updates message ports registered for |message_port_ids| and returns
+ // new routing IDs for the updated ports via |new_routing_ids|.
+ void UpdateMessagePortsWithNewRoutes(
+ const std::vector<int>& message_port_ids,
+ std::vector<int>* new_routing_ids);
+
+ protected:
+ // This is protected, so we can define sub classes for testing.
+ virtual ~MessagePortMessageFilter();
+
private:
friend class BrowserThread;
friend class base::DeleteHelper<MessagePortMessageFilter>;
- virtual ~MessagePortMessageFilter();
-
// Message handlers.
void OnCreateMessagePort(int* route_id, int* message_port_id);
diff --git a/chromium/content/browser/mime_registry_message_filter.cc b/chromium/content/browser/mime_registry_message_filter.cc
index f23cc004e4b..0a823af2a9f 100644
--- a/chromium/content/browser/mime_registry_message_filter.cc
+++ b/chromium/content/browser/mime_registry_message_filter.cc
@@ -9,7 +9,8 @@
namespace content {
-MimeRegistryMessageFilter::MimeRegistryMessageFilter() {
+MimeRegistryMessageFilter::MimeRegistryMessageFilter()
+ : BrowserMessageFilter(MimeRegistryMsgStart) {
}
MimeRegistryMessageFilter::~MimeRegistryMessageFilter() {
@@ -22,10 +23,9 @@ void MimeRegistryMessageFilter::OverrideThreadForMessage(
*thread = BrowserThread::FILE;
}
-bool MimeRegistryMessageFilter::OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) {
+bool MimeRegistryMessageFilter::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(MimeRegistryMessageFilter, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(MimeRegistryMessageFilter, message)
IPC_MESSAGE_HANDLER(MimeRegistryMsg_GetMimeTypeFromExtension,
OnGetMimeTypeFromExtension)
IPC_MESSAGE_HANDLER(MimeRegistryMsg_GetMimeTypeFromFile,
diff --git a/chromium/content/browser/mime_registry_message_filter.h b/chromium/content/browser/mime_registry_message_filter.h
index e86fe190b1d..4237f508e74 100644
--- a/chromium/content/browser/mime_registry_message_filter.h
+++ b/chromium/content/browser/mime_registry_message_filter.h
@@ -17,8 +17,7 @@ class MimeRegistryMessageFilter : public BrowserMessageFilter {
virtual void OverrideThreadForMessage(
const IPC::Message& message,
BrowserThread::ID* thread) OVERRIDE;
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
private:
virtual ~MimeRegistryMessageFilter();
diff --git a/chromium/content/browser/mojo/mojo_application_host.cc b/chromium/content/browser/mojo/mojo_application_host.cc
new file mode 100644
index 00000000000..90187b9b92f
--- /dev/null
+++ b/chromium/content/browser/mojo/mojo_application_host.cc
@@ -0,0 +1,71 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/mojo/mojo_application_host.h"
+
+#include "content/common/mojo/mojo_messages.h"
+#include "content/public/browser/browser_thread.h"
+#include "ipc/ipc_sender.h"
+#include "mojo/embedder/platform_channel_pair.h"
+
+namespace content {
+namespace {
+
+base::PlatformFile PlatformFileFromScopedPlatformHandle(
+ mojo::embedder::ScopedPlatformHandle handle) {
+#if defined(OS_POSIX)
+ return handle.release().fd;
+#elif defined(OS_WIN)
+ return handle.release().handle;
+#endif
+}
+
+} // namespace
+
+MojoApplicationHost::MojoApplicationHost() : did_activate_(false) {
+}
+
+MojoApplicationHost::~MojoApplicationHost() {
+}
+
+bool MojoApplicationHost::Init() {
+ DCHECK(!child_service_provider_.get()) << "Already initialized!";
+
+ mojo::embedder::PlatformChannelPair channel_pair;
+
+ mojo::ScopedMessagePipeHandle message_pipe = channel_init_.Init(
+ PlatformFileFromScopedPlatformHandle(channel_pair.PassServerHandle()),
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO));
+ if (!message_pipe.is_valid())
+ return false;
+
+ // Forward this to the client once we know its process handle.
+ client_handle_ = channel_pair.PassClientHandle();
+
+ child_service_provider_.reset(
+ BindToPipe(new ServiceProviderImpl(), message_pipe.Pass()));
+ return true;
+}
+
+bool MojoApplicationHost::Activate(IPC::Sender* sender,
+ base::ProcessHandle process_handle) {
+ DCHECK(!did_activate_);
+ DCHECK(client_handle_.is_valid());
+
+ base::PlatformFile client_file =
+ PlatformFileFromScopedPlatformHandle(client_handle_.Pass());
+ did_activate_ = sender->Send(new MojoMsg_Activate(
+ IPC::GetFileHandleForProcess(client_file, process_handle, true)));
+ return did_activate_;
+}
+
+void MojoApplicationHost::ServiceProviderImpl::ConnectToService(
+ const mojo::String& service_url,
+ const mojo::String& service_name,
+ mojo::ScopedMessagePipeHandle handle,
+ const mojo::String& requestor_url) {
+ // TODO(darin): Provide something meaningful here.
+}
+
+} // namespace content
diff --git a/chromium/content/browser/mojo/mojo_application_host.h b/chromium/content/browser/mojo/mojo_application_host.h
new file mode 100644
index 00000000000..f5913269d72
--- /dev/null
+++ b/chromium/content/browser/mojo/mojo_application_host.h
@@ -0,0 +1,70 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_MOJO_MOJO_APPLICATION_HOST_H_
+#define CONTENT_BROWSER_MOJO_MOJO_APPLICATION_HOST_H_
+
+#include "base/process/process_handle.h"
+#include "mojo/embedder/channel_init.h"
+#include "mojo/embedder/scoped_platform_handle.h"
+#include "mojo/public/interfaces/service_provider/service_provider.mojom.h"
+
+namespace IPC {
+class Sender;
+}
+
+namespace content {
+
+// MojoApplicationHost represents the code needed on the browser side to setup
+// a child process as a Mojo application via Chrome IPC. The child process
+// should use MojoApplication to handle messages generated by an instance of
+// MojoApplicationHost. MojoApplicationHost makes the mojo::ShellClient
+// interface available so that child-provided services can be invoked.
+class MojoApplicationHost {
+ public:
+ MojoApplicationHost();
+ virtual ~MojoApplicationHost();
+
+ // Two-phase initialization:
+ // 1- Init makes the shell_client() available synchronously.
+ // 2- Activate establishes the actual connection to the peer process.
+ bool Init();
+ bool Activate(IPC::Sender* sender, base::ProcessHandle process_handle);
+
+ bool did_activate() const { return did_activate_; }
+
+ mojo::ServiceProvider* service_provider() {
+ DCHECK(child_service_provider_.get());
+ return child_service_provider_->client();
+ }
+
+ private:
+ class ServiceProviderImpl
+ : public mojo::InterfaceImpl<mojo::ServiceProvider> {
+ public:
+ virtual void OnConnectionError() OVERRIDE {
+ // TODO(darin): How should we handle this error?
+ }
+
+ // mojo::ServiceProvider methods:
+ virtual void ConnectToService(
+ const mojo::String& service_url,
+ const mojo::String& service_name,
+ mojo::ScopedMessagePipeHandle handle,
+ const mojo::String& requestor_url) OVERRIDE;
+ };
+
+ mojo::embedder::ChannelInit channel_init_;
+ mojo::embedder::ScopedPlatformHandle client_handle_;
+
+ scoped_ptr<ServiceProviderImpl> child_service_provider_;
+
+ bool did_activate_;
+
+ DISALLOW_COPY_AND_ASSIGN(MojoApplicationHost);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_MOJO_MOJO_APPLICATION_HOST_H_
diff --git a/chromium/content/browser/net/browser_online_state_observer.cc b/chromium/content/browser/net/browser_online_state_observer.cc
index 2276fd70a0a..76f2bb350c8 100644
--- a/chromium/content/browser/net/browser_online_state_observer.cc
+++ b/chromium/content/browser/net/browser_online_state_observer.cc
@@ -21,8 +21,7 @@ void BrowserOnlineStateObserver::OnConnectionTypeChanged(
net::NetworkChangeNotifier::ConnectionType type) {
for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator());
!it.IsAtEnd(); it.Advance()) {
- it.GetCurrentValue()->Send(new ViewMsg_NetworkStateChanged(
- type != net::NetworkChangeNotifier::CONNECTION_NONE));
+ it.GetCurrentValue()->Send(new ViewMsg_NetworkTypeChanged(type));
}
}
diff --git a/chromium/content/browser/net/sqlite_persistent_cookie_store.cc b/chromium/content/browser/net/sqlite_persistent_cookie_store.cc
index e2b78803314..967101f4dbb 100644
--- a/chromium/content/browser/net/sqlite_persistent_cookie_store.cc
+++ b/chromium/content/browser/net/sqlite_persistent_cookie_store.cc
@@ -12,6 +12,7 @@
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/callback.h"
+#include "base/command_line.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/location.h"
@@ -28,6 +29,7 @@
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/cookie_crypto_delegate.h"
#include "content/public/browser/cookie_store_factory.h"
+#include "content/public/common/content_switches.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "net/cookies/canonical_cookie.h"
#include "net/cookies/cookie_constants.h"
@@ -75,7 +77,7 @@ class SQLitePersistentCookieStore::Backend
const scoped_refptr<base::SequencedTaskRunner>& background_task_runner,
bool restore_old_session_cookies,
quota::SpecialStoragePolicy* special_storage_policy,
- scoped_ptr<CookieCryptoDelegate> crypto_delegate)
+ CookieCryptoDelegate* crypto_delegate)
: path_(path),
num_pending_(0),
force_keep_session_state_(false),
@@ -88,7 +90,7 @@ class SQLitePersistentCookieStore::Backend
background_task_runner_(background_task_runner),
num_priority_waiting_(0),
total_priority_requests_(0),
- crypto_(crypto_delegate.Pass()) {}
+ crypto_(crypto_delegate) {}
// Creates or loads the SQLite database.
void Load(const LoadedCallback& loaded_callback);
@@ -273,7 +275,9 @@ class SQLitePersistentCookieStore::Backend
base::TimeDelta priority_wait_duration_;
// Class with functions that do cryptographic operations (for protecting
// cookies stored persistently).
- scoped_ptr<CookieCryptoDelegate> crypto_;
+ //
+ // Not owned.
+ CookieCryptoDelegate* crypto_;
DISALLOW_COPY_AND_ASSIGN(Backend);
};
@@ -630,7 +634,7 @@ bool SQLitePersistentCookieStore::Backend::InitializeDatabase() {
std::string key =
net::registry_controlled_domains::GetDomainAndRegistry(
domain,
- net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
+ net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
keys_to_load_[key].insert(domain);
}
@@ -717,7 +721,7 @@ bool SQLitePersistentCookieStore::Backend::LoadCookiesForDomains(
while (smt.Step()) {
std::string value;
std::string encrypted_value = smt.ColumnString(4);
- if (!encrypted_value.empty() && crypto_.get()) {
+ if (!encrypted_value.empty() && crypto_) {
crypto_->DecryptString(encrypted_value, &value);
} else {
DCHECK(encrypted_value.empty());
@@ -996,7 +1000,7 @@ void SQLitePersistentCookieStore::Backend::Commit() {
add_smt.BindInt64(0, po->cc().CreationDate().ToInternalValue());
add_smt.BindString(1, po->cc().Domain());
add_smt.BindString(2, po->cc().Name());
- if (crypto_.get()) {
+ if (crypto_) {
std::string encrypted_value;
add_smt.BindCString(3, ""); // value
crypto_->EncryptString(po->cc().Value(), &encrypted_value);
@@ -1202,13 +1206,13 @@ SQLitePersistentCookieStore::SQLitePersistentCookieStore(
const scoped_refptr<base::SequencedTaskRunner>& background_task_runner,
bool restore_old_session_cookies,
quota::SpecialStoragePolicy* special_storage_policy,
- scoped_ptr<CookieCryptoDelegate> crypto_delegate)
+ CookieCryptoDelegate* crypto_delegate)
: backend_(new Backend(path,
client_task_runner,
background_task_runner,
restore_old_session_cookies,
special_storage_policy,
- crypto_delegate.Pass())) {
+ crypto_delegate)) {
}
void SQLitePersistentCookieStore::Load(const LoadedCallback& loaded_callback) {
@@ -1248,40 +1252,86 @@ SQLitePersistentCookieStore::~SQLitePersistentCookieStore() {
// a reference if the background runner has not run Close() yet.
}
-net::CookieStore* CreatePersistentCookieStore(
- const base::FilePath& path,
- bool restore_old_session_cookies,
- quota::SpecialStoragePolicy* storage_policy,
- net::CookieMonster::Delegate* cookie_monster_delegate,
- const scoped_refptr<base::SequencedTaskRunner>& client_task_runner,
- const scoped_refptr<base::SequencedTaskRunner>& background_task_runner,
- scoped_ptr<CookieCryptoDelegate> crypto_delegate) {
- SQLitePersistentCookieStore* persistent_store =
- new SQLitePersistentCookieStore(
- path,
- client_task_runner,
- background_task_runner,
- restore_old_session_cookies,
- storage_policy,
- crypto_delegate.Pass());
- return new net::CookieMonster(persistent_store, cookie_monster_delegate);
+CookieStoreConfig::CookieStoreConfig()
+ : session_cookie_mode(EPHEMERAL_SESSION_COOKIES),
+ crypto_delegate(NULL) {
+ // Default to an in-memory cookie store.
}
-net::CookieStore* CreatePersistentCookieStore(
- const base::FilePath& path,
- bool restore_old_session_cookies,
- quota::SpecialStoragePolicy* storage_policy,
- net::CookieMonster::Delegate* cookie_monster_delegate,
- scoped_ptr<CookieCryptoDelegate> crypto_delegate) {
- return CreatePersistentCookieStore(
- path,
- restore_old_session_cookies,
- storage_policy,
- cookie_monster_delegate,
- BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO),
- BrowserThread::GetBlockingPool()->GetSequencedTaskRunner(
- BrowserThread::GetBlockingPool()->GetSequenceToken()),
- crypto_delegate.Pass());
+CookieStoreConfig::CookieStoreConfig(
+ const base::FilePath& path,
+ SessionCookieMode session_cookie_mode,
+ quota::SpecialStoragePolicy* storage_policy,
+ net::CookieMonsterDelegate* cookie_delegate)
+ : path(path),
+ session_cookie_mode(session_cookie_mode),
+ storage_policy(storage_policy),
+ cookie_delegate(cookie_delegate),
+ crypto_delegate(NULL) {
+ CHECK(!path.empty() || session_cookie_mode == EPHEMERAL_SESSION_COOKIES);
+}
+
+CookieStoreConfig::~CookieStoreConfig() {
+}
+
+net::CookieStore* CreateCookieStore(const CookieStoreConfig& config) {
+ net::CookieMonster* cookie_monster = NULL;
+
+ if (config.path.empty()) {
+ // Empty path means in-memory store.
+ cookie_monster = new net::CookieMonster(NULL, config.cookie_delegate);
+ } else {
+ scoped_refptr<base::SequencedTaskRunner> client_task_runner =
+ config.client_task_runner;
+ scoped_refptr<base::SequencedTaskRunner> background_task_runner =
+ config.background_task_runner;
+
+ if (!client_task_runner) {
+ client_task_runner =
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
+ }
+
+ if (!background_task_runner) {
+ background_task_runner =
+ BrowserThread::GetBlockingPool()->GetSequencedTaskRunner(
+ BrowserThread::GetBlockingPool()->GetSequenceToken());
+ }
+
+ SQLitePersistentCookieStore* persistent_store =
+ new SQLitePersistentCookieStore(
+ config.path,
+ client_task_runner,
+ background_task_runner,
+ (config.session_cookie_mode ==
+ CookieStoreConfig::RESTORED_SESSION_COOKIES),
+ config.storage_policy,
+ config.crypto_delegate);
+
+ cookie_monster =
+ new net::CookieMonster(persistent_store, config.cookie_delegate);
+ if ((config.session_cookie_mode ==
+ CookieStoreConfig::PERSISTANT_SESSION_COOKIES) ||
+ (config.session_cookie_mode ==
+ CookieStoreConfig::RESTORED_SESSION_COOKIES)) {
+ cookie_monster->SetPersistSessionCookies(true);
+ }
+ }
+
+ // In the case of Android WebView, the cookie store may be created
+ // before the browser process fully initializes -- certainly before
+ // the main loop ever runs. In this situation, the CommandLine singleton
+ // will not have been set up. Android tests do not need file cookies
+ // so always ignore them here.
+ //
+ // TODO(ajwong): Remove the InitializedForCurrentProcess() check
+ // once http://crbug.com/331424 is resolved.
+ if (CommandLine::InitializedForCurrentProcess() &&
+ CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableFileCookies)) {
+ cookie_monster->SetEnableFileScheme(true);
+ }
+
+ return cookie_monster;
}
} // namespace content
diff --git a/chromium/content/browser/net/sqlite_persistent_cookie_store.h b/chromium/content/browser/net/sqlite_persistent_cookie_store.h
index e94327e2322..5773de186a3 100644
--- a/chromium/content/browser/net/sqlite_persistent_cookie_store.h
+++ b/chromium/content/browser/net/sqlite_persistent_cookie_store.h
@@ -51,7 +51,7 @@ class CONTENT_EXPORT SQLitePersistentCookieStore
const scoped_refptr<base::SequencedTaskRunner>& background_task_runner,
bool restore_old_session_cookies,
quota::SpecialStoragePolicy* special_storage_policy,
- scoped_ptr<CookieCryptoDelegate> crypto_delegate);
+ CookieCryptoDelegate* crypto_delegate);
// net::CookieMonster::PersistentCookieStore:
virtual void Load(const LoadedCallback& loaded_callback) OVERRIDE;
diff --git a/chromium/content/browser/net/sqlite_persistent_cookie_store_perftest.cc b/chromium/content/browser/net/sqlite_persistent_cookie_store_perftest.cc
index 980a0c168b5..7092aa28efa 100644
--- a/chromium/content/browser/net/sqlite_persistent_cookie_store_perftest.cc
+++ b/chromium/content/browser/net/sqlite_persistent_cookie_store_perftest.cc
@@ -67,8 +67,7 @@ class SQLitePersistentCookieStorePerfTest : public testing::Test {
temp_dir_.path().Append(cookie_filename),
client_task_runner(),
background_task_runner(),
- false, NULL,
- scoped_ptr<content::CookieCryptoDelegate>());
+ false, NULL, NULL);
std::vector<net::CanonicalCookie*> cookies;
Load();
ASSERT_EQ(0u, cookies_.size());
@@ -99,8 +98,7 @@ class SQLitePersistentCookieStorePerfTest : public testing::Test {
temp_dir_.path().Append(cookie_filename),
client_task_runner(),
background_task_runner(),
- false, NULL,
- scoped_ptr<content::CookieCryptoDelegate>());
+ false, NULL, NULL);
}
virtual void TearDown() OVERRIDE {
diff --git a/chromium/content/browser/net/sqlite_persistent_cookie_store_unittest.cc b/chromium/content/browser/net/sqlite_persistent_cookie_store_unittest.cc
index 11c8419673a..d6e30a6587e 100644
--- a/chromium/content/browser/net/sqlite_persistent_cookie_store_unittest.cc
+++ b/chromium/content/browser/net/sqlite_persistent_cookie_store_unittest.cc
@@ -128,15 +128,16 @@ class SQLitePersistentCookieStoreTest : public testing::Test {
void CreateAndLoad(bool crypt_cookies,
bool restore_old_session_cookies,
CanonicalCookieVector* cookies) {
+ if (crypt_cookies)
+ cookie_crypto_delegate_.reset(new CookieCryptor());
+
store_ = new SQLitePersistentCookieStore(
temp_dir_.path().Append(kCookieFilename),
client_task_runner(),
background_task_runner(),
restore_old_session_cookies,
NULL,
- crypt_cookies ?
- scoped_ptr<content::CookieCryptoDelegate>(new CookieCryptor) :
- scoped_ptr<content::CookieCryptoDelegate>());
+ cookie_crypto_delegate_.get());
Load(cookies);
}
@@ -190,6 +191,7 @@ class SQLitePersistentCookieStoreTest : public testing::Test {
CanonicalCookieVector cookies_;
base::ScopedTempDir temp_dir_;
scoped_refptr<SQLitePersistentCookieStore> store_;
+ scoped_ptr<content::CookieCryptoDelegate> cookie_crypto_delegate_;
};
TEST_F(SQLitePersistentCookieStoreTest, TestInvalidMetaTableRecovery) {
@@ -276,8 +278,7 @@ TEST_F(SQLitePersistentCookieStoreTest, TestLoadCookiesForKey) {
temp_dir_.path().Append(kCookieFilename),
client_task_runner(),
background_task_runner(),
- false, NULL,
- scoped_ptr<content::CookieCryptoDelegate>());
+ false, NULL, NULL);
// Posting a blocking task to db_thread_ makes sure that the DB thread waits
// until both Load and LoadCookiesForKey have been posted to its task queue.
@@ -337,7 +338,7 @@ TEST_F(SQLitePersistentCookieStoreTest, TestFlush) {
// File timestamps don't work well on all platforms, so we'll determine
// whether the DB file has been modified by checking its size.
base::FilePath path = temp_dir_.path().Append(kCookieFilename);
- base::PlatformFileInfo info;
+ base::File::Info info;
ASSERT_TRUE(base::GetFileInfo(path, &info));
int64 base_size = info.size;
diff --git a/chromium/content/browser/net/view_blob_internals_job_factory.cc b/chromium/content/browser/net/view_blob_internals_job_factory.cc
index 85356e5aae5..984763d45c9 100644
--- a/chromium/content/browser/net/view_blob_internals_job_factory.cc
+++ b/chromium/content/browser/net/view_blob_internals_job_factory.cc
@@ -13,7 +13,7 @@ namespace content {
// static.
bool ViewBlobInternalsJobFactory::IsSupportedURL(const GURL& url) {
- return url.SchemeIs(chrome::kChromeUIScheme) &&
+ return url.SchemeIs(kChromeUIScheme) &&
url.host() == kChromeUIBlobInternalsHost;
}
diff --git a/chromium/content/browser/net/view_http_cache_job_factory.cc b/chromium/content/browser/net/view_http_cache_job_factory.cc
index 4264389d10c..a0cce463d74 100644
--- a/chromium/content/browser/net/view_http_cache_job_factory.cc
+++ b/chromium/content/browser/net/view_http_cache_job_factory.cc
@@ -190,7 +190,7 @@ void ViewHttpCacheJob::Core::OnIOComplete(int result) {
// Static.
bool ViewHttpCacheJobFactory::IsSupportedURL(const GURL& url) {
- return url.SchemeIs(chrome::kChromeUIScheme) &&
+ return url.SchemeIs(kChromeUIScheme) &&
url.host() == kChromeUINetworkViewCacheHost;
}
diff --git a/chromium/content/browser/plugin_browsertest.cc b/chromium/content/browser/plugin_browsertest.cc
index 204a1432038..2e4053385ca 100644
--- a/chromium/content/browser/plugin_browsertest.cc
+++ b/chromium/content/browser/plugin_browsertest.cc
@@ -12,11 +12,11 @@
#include "content/public/browser/resource_dispatcher_host_delegate.h"
#include "content/public/common/content_switches.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/shell/common/shell_switches.h"
-#include "content/test/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
#include "content/test/net/url_request_mock_http_job.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/url_request/url_request.h"
@@ -33,6 +33,8 @@
#define MAYBE(x) x
#endif
+using base::ASCIIToUTF16;
+
namespace content {
namespace {
@@ -62,9 +64,6 @@ class PluginTest : public ContentBrowserTest {
KEY_WRITE) == ERROR_SUCCESS) {
regkey.CreateKey(L"BROWSER_TESTS.EXE", KEY_READ);
}
- } else if (strcmp(test_info->name(), "FlashSecurity") == 0) {
- command_line->AppendSwitchASCII(switches::kTestSandbox,
- "security_tests.dll");
}
#elif defined(OS_MACOSX)
base::FilePath plugin_dir;
@@ -329,9 +328,10 @@ IN_PROC_BROWSER_TEST_F(PluginTest,
// If this flakes, reopen http://crbug.com/17645
// As of 6 July 2011, this test is flaky on Windows (perhaps due to timing out).
-#if !defined(OS_MACOSX)
+#if !defined(OS_MACOSX) && !defined(OS_LINUX)
// Disabled on Mac because the plugin side isn't implemented yet, see
// "TODO(port)" in plugin_javascript_open_popup.cc.
+// Disabled on Linux because we don't support NPAPI any more.
IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(OpenPopupWindowWithPlugin)) {
LoadAndWait(GetURL("get_javascript_open_popup_with_plugin.html"));
}
diff --git a/chromium/content/browser/plugin_data_remover_impl.cc b/chromium/content/browser/plugin_data_remover_impl.cc
index 670d80dd4b6..a318e724d4b 100644
--- a/chromium/content/browser/plugin_data_remover_impl.cc
+++ b/chromium/content/browser/plugin_data_remover_impl.cc
@@ -208,7 +208,7 @@ class PluginDataRemoverImpl::Context
// the browser).
#if defined(OS_WIN)
base::FilePath plugin_data_path =
- profile_path.Append(base::FilePath(UTF8ToUTF16(plugin_name_)));
+ profile_path.Append(base::FilePath(base::UTF8ToUTF16(plugin_name_)));
#else
base::FilePath plugin_data_path =
profile_path.Append(base::FilePath(plugin_name_));
@@ -226,7 +226,7 @@ class PluginDataRemoverImpl::Context
return;
DCHECK(!channel_.get());
- channel_.reset(new IPC::Channel(handle, IPC::Channel::MODE_CLIENT, this));
+ channel_ = IPC::Channel::CreateClient(handle, this);
if (!channel_->Connect()) {
NOTREACHED() << "Couldn't connect to plugin";
SignalDone();
diff --git a/chromium/content/browser/plugin_data_remover_impl_browsertest.cc b/chromium/content/browser/plugin_data_remover_impl_browsertest.cc
index 8308a51ba51..3dfe49285ba 100644
--- a/chromium/content/browser/plugin_data_remover_impl_browsertest.cc
+++ b/chromium/content/browser/plugin_data_remover_impl_browsertest.cc
@@ -10,9 +10,9 @@
#include "content/browser/plugin_data_remover_impl.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_switches.h"
+#include "content/public/test/content_browser_test.h"
#include "content/public/test/test_utils.h"
#include "content/shell/browser/shell.h"
-#include "content/test/content_browser_test.h"
namespace content {
diff --git a/chromium/content/browser/plugin_loader_posix.cc b/chromium/content/browser/plugin_loader_posix.cc
index e6bc3ea27f5..7b278811343 100644
--- a/chromium/content/browser/plugin_loader_posix.cc
+++ b/chromium/content/browser/plugin_loader_posix.cc
@@ -14,6 +14,7 @@
#include "content/common/utility_messages.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/plugin_service.h"
+#include "content/public/browser/user_metrics.h"
namespace content {
@@ -21,16 +22,35 @@ PluginLoaderPosix::PluginLoaderPosix()
: next_load_index_(0) {
}
-void PluginLoaderPosix::LoadPlugins(
- scoped_refptr<base::MessageLoopProxy> target_loop,
+void PluginLoaderPosix::GetPlugins(
const PluginService::GetPluginsCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- callbacks_.push_back(PendingCallback(target_loop, callback));
+ std::vector<WebPluginInfo> cached_plugins;
+ if (PluginList::Singleton()->GetPluginsNoRefresh(&cached_plugins)) {
+ // Can't assume the caller is reentrant.
+ base::MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(callback, cached_plugins));
+ return;
+ }
+
+ if (callbacks_.empty()) {
+ callbacks_.push_back(callback);
+
+ PluginList::Singleton()->PrepareForPluginLoading();
- if (callbacks_.size() == 1) {
- BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
- base::Bind(&PluginLoaderPosix::GetPluginsToLoad, this));
+ BrowserThread::PostTask(BrowserThread::FILE,
+ FROM_HERE,
+ base::Bind(&PluginLoaderPosix::GetPluginsToLoad,
+ make_scoped_refptr(this)));
+ } else {
+ // If we are currently loading plugins, the plugin list might have been
+ // invalidated in the mean time, or might get invalidated before we finish.
+ // We'll wait until we have finished the current run, then try to get them
+ // again from the plugin list. If it has indeed been invalidated, it will
+ // restart plugin loading, otherwise it will immediately run the callback.
+ callbacks_.push_back(base::Bind(&PluginLoaderPosix::GetPluginsWrapper,
+ make_scoped_refptr(this), callback));
}
}
@@ -45,6 +65,9 @@ bool PluginLoaderPosix::OnMessageReceived(const IPC::Message& message) {
}
void PluginLoaderPosix::OnProcessCrashed(int exit_code) {
+ RecordAction(
+ base::UserMetricsAction("PluginLoaderPosix.UtilityProcessCrashed"));
+
if (next_load_index_ == canonical_list_.size()) {
// How this case occurs is unknown. See crbug.com/111935.
canonical_list_.clear();
@@ -100,6 +123,9 @@ void PluginLoaderPosix::LoadPluginsInternal() {
if (MaybeRunPendingCallbacks())
return;
+ RecordAction(
+ base::UserMetricsAction("PluginLoaderPosix.LaunchUtilityProcess"));
+
if (load_start_time_.is_null())
load_start_time_ = base::TimeTicks::Now();
@@ -115,6 +141,16 @@ void PluginLoaderPosix::LoadPluginsInternal() {
process_host_->Send(new UtilityMsg_LoadPlugins(canonical_list_));
}
+void PluginLoaderPosix::GetPluginsWrapper(
+ const PluginService::GetPluginsCallback& callback,
+ const std::vector<WebPluginInfo>& plugins_unused) {
+ // We are being called after plugin loading has finished, but we don't know
+ // whether the plugin list has been invalidated in the mean time
+ // (and therefore |plugins| might already be stale). So we simply ignore it
+ // and call regular GetPlugins() instead.
+ GetPlugins(callback);
+}
+
void PluginLoaderPosix::OnPluginLoaded(uint32 index,
const WebPluginInfo& plugin) {
if (index != next_load_index_) {
@@ -165,37 +201,20 @@ bool PluginLoaderPosix::MaybeRunPendingCallbacks() {
PluginList::Singleton()->SetPlugins(loaded_plugins_);
- // Only call the first callback with loaded plugins because there may be
- // some extra plugin paths added since the first callback is added.
- if (!callbacks_.empty()) {
- PendingCallback callback = callbacks_.front();
- callbacks_.pop_front();
- callback.target_loop->PostTask(
- FROM_HERE,
- base::Bind(callback.callback, loaded_plugins_));
+ for (std::vector<PluginService::GetPluginsCallback>::iterator it =
+ callbacks_.begin();
+ it != callbacks_.end(); ++it) {
+ base::MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(*it, loaded_plugins_));
}
+ callbacks_.clear();
HISTOGRAM_TIMES("PluginLoaderPosix.LoadDone",
(base::TimeTicks::Now() - load_start_time_)
* base::Time::kMicrosecondsPerMillisecond);
load_start_time_ = base::TimeTicks();
- if (!callbacks_.empty()) {
- BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
- base::Bind(&PluginLoaderPosix::GetPluginsToLoad, this));
- return false;
- }
return true;
}
-PluginLoaderPosix::PendingCallback::PendingCallback(
- scoped_refptr<base::MessageLoopProxy> loop,
- const PluginService::GetPluginsCallback& cb)
- : target_loop(loop),
- callback(cb) {
-}
-
-PluginLoaderPosix::PendingCallback::~PendingCallback() {
-}
-
} // namespace content
diff --git a/chromium/content/browser/plugin_loader_posix.h b/chromium/content/browser/plugin_loader_posix.h
index 60cd5c61221..42c6437bde2 100644
--- a/chromium/content/browser/plugin_loader_posix.h
+++ b/chromium/content/browser/plugin_loader_posix.h
@@ -5,7 +5,6 @@
#ifndef CONTENT_BROWSER_PLUGIN_LOADER_POSIX_H_
#define CONTENT_BROWSER_PLUGIN_LOADER_POSIX_H_
-#include <deque>
#include <vector>
#include "base/basictypes.h"
@@ -51,10 +50,9 @@ class CONTENT_EXPORT PluginLoaderPosix
public:
PluginLoaderPosix();
- // Must be called from the IO thread.
- void LoadPlugins(
- scoped_refptr<base::MessageLoopProxy> target_loop,
- const PluginService::GetPluginsCallback& callback);
+ // Must be called from the IO thread. The |callback| will be called on the IO
+ // thread too.
+ void GetPlugins(const PluginService::GetPluginsCallback& callback);
// UtilityProcessHostClient:
virtual void OnProcessCrashed(int exit_code) OVERRIDE;
@@ -64,15 +62,6 @@ class CONTENT_EXPORT PluginLoaderPosix
virtual bool Send(IPC::Message* msg) OVERRIDE;
private:
- struct PendingCallback {
- PendingCallback(scoped_refptr<base::MessageLoopProxy> target_loop,
- const PluginService::GetPluginsCallback& callback);
- ~PendingCallback();
-
- scoped_refptr<base::MessageLoopProxy> target_loop;
- PluginService::GetPluginsCallback callback;
- };
-
virtual ~PluginLoaderPosix();
// Called on the FILE thread to get the list of plugin paths to probe.
@@ -81,6 +70,12 @@ class CONTENT_EXPORT PluginLoaderPosix
// Must be called on the IO thread.
virtual void LoadPluginsInternal();
+ // Called after plugin loading has finished, if we don't know whether the
+ // plugin list has been invalidated in the mean time.
+ void GetPluginsWrapper(
+ const PluginService::GetPluginsCallback& callback,
+ const std::vector<WebPluginInfo>& plugins_unused);
+
// Message handlers.
void OnPluginLoaded(uint32 index, const WebPluginInfo& plugin);
void OnPluginLoadFailed(uint32 index, const base::FilePath& plugin_path);
@@ -113,7 +108,7 @@ class CONTENT_EXPORT PluginLoaderPosix
// The callback and message loop on which the callback will be run when the
// plugin loading process has been completed.
- std::deque<PendingCallback> callbacks_;
+ std::vector<PluginService::GetPluginsCallback> callbacks_;
// The time at which plugin loading started.
base::TimeTicks load_start_time_;
diff --git a/chromium/content/browser/plugin_loader_posix_unittest.cc b/chromium/content/browser/plugin_loader_posix_unittest.cc
index 13620f74dd3..542b2c6cbb4 100644
--- a/chromium/content/browser/plugin_loader_posix_unittest.cc
+++ b/chromium/content/browser/plugin_loader_posix_unittest.cc
@@ -11,9 +11,12 @@
#include "base/message_loop/message_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "content/browser/browser_thread_impl.h"
+#include "content/common/plugin_list.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+using base::ASCIIToUTF16;
+
namespace content {
class MockPluginLoaderPosix : public PluginLoaderPosix {
@@ -94,7 +97,8 @@ class PluginLoaderPosixTest : public testing::Test {
WebPluginInfo plugin3_;
private:
- base::ShadowingAtExitManager at_exit_manager_; // Destroys PluginService.
+ // Destroys PluginService and PluginList.
+ base::ShadowingAtExitManager at_exit_manager_;
base::MessageLoopForIO message_loop_;
BrowserThreadImpl file_thread_;
@@ -108,25 +112,48 @@ TEST_F(PluginLoaderPosixTest, QueueRequests) {
PluginService::GetPluginsCallback callback =
base::Bind(&VerifyCallback, base::Unretained(&did_callback));
- EXPECT_EQ(0u, plugin_loader()->number_of_pending_callbacks());
- plugin_loader()->LoadPlugins(message_loop()->message_loop_proxy(), callback);
- EXPECT_EQ(1u, plugin_loader()->number_of_pending_callbacks());
+ plugin_loader()->GetPlugins(callback);
+ plugin_loader()->GetPlugins(callback);
+
+ EXPECT_CALL(*plugin_loader(), LoadPluginsInternal()).Times(1);
+ message_loop()->RunUntilIdle();
- plugin_loader()->LoadPlugins(message_loop()->message_loop_proxy(), callback);
- EXPECT_EQ(2u, plugin_loader()->number_of_pending_callbacks());
+ EXPECT_EQ(0, did_callback);
- EXPECT_CALL(*plugin_loader(), LoadPluginsInternal()).Times(2);
+ plugin_loader()->canonical_list()->clear();
+ plugin_loader()->canonical_list()->push_back(plugin1_.path);
+ plugin_loader()->TestOnPluginLoaded(0, plugin1_);
+ message_loop()->RunUntilIdle();
+
+ EXPECT_EQ(2, did_callback);
+}
+
+TEST_F(PluginLoaderPosixTest, QueueRequestsAndInvalidate) {
+ int did_callback = 0;
+ PluginService::GetPluginsCallback callback =
+ base::Bind(&VerifyCallback, base::Unretained(&did_callback));
+
+ plugin_loader()->GetPlugins(callback);
+
+ EXPECT_CALL(*plugin_loader(), LoadPluginsInternal()).Times(1);
message_loop()->RunUntilIdle();
EXPECT_EQ(0, did_callback);
+ ::testing::Mock::VerifyAndClearExpectations(plugin_loader());
+
+ // Invalidate the plugin list, then queue up another request.
+ PluginList::Singleton()->RefreshPlugins();
+ plugin_loader()->GetPlugins(callback);
+
+ EXPECT_CALL(*plugin_loader(), LoadPluginsInternal()).Times(1);
plugin_loader()->canonical_list()->clear();
plugin_loader()->canonical_list()->push_back(plugin1_.path);
plugin_loader()->TestOnPluginLoaded(0, plugin1_);
message_loop()->RunUntilIdle();
+ // Only the first request should have been fulfilled.
EXPECT_EQ(1, did_callback);
- EXPECT_EQ(1u, plugin_loader()->number_of_pending_callbacks());
plugin_loader()->canonical_list()->clear();
plugin_loader()->canonical_list()->push_back(plugin1_.path);
@@ -134,7 +161,6 @@ TEST_F(PluginLoaderPosixTest, QueueRequests) {
message_loop()->RunUntilIdle();
EXPECT_EQ(2, did_callback);
- EXPECT_EQ(0u, plugin_loader()->number_of_pending_callbacks());
}
TEST_F(PluginLoaderPosixTest, ThreeSuccessfulLoads) {
@@ -142,7 +168,7 @@ TEST_F(PluginLoaderPosixTest, ThreeSuccessfulLoads) {
PluginService::GetPluginsCallback callback =
base::Bind(&VerifyCallback, base::Unretained(&did_callback));
- plugin_loader()->LoadPlugins(message_loop()->message_loop_proxy(), callback);
+ plugin_loader()->GetPlugins(callback);
EXPECT_CALL(*plugin_loader(), LoadPluginsInternal()).Times(1);
message_loop()->RunUntilIdle();
@@ -182,7 +208,7 @@ TEST_F(PluginLoaderPosixTest, ThreeSuccessfulLoadsThenCrash) {
PluginService::GetPluginsCallback callback =
base::Bind(&VerifyCallback, base::Unretained(&did_callback));
- plugin_loader()->LoadPlugins(message_loop()->message_loop_proxy(), callback);
+ plugin_loader()->GetPlugins(callback);
EXPECT_CALL(*plugin_loader(), LoadPluginsInternal()).Times(2);
message_loop()->RunUntilIdle();
@@ -224,7 +250,7 @@ TEST_F(PluginLoaderPosixTest, TwoFailures) {
PluginService::GetPluginsCallback callback =
base::Bind(&VerifyCallback, base::Unretained(&did_callback));
- plugin_loader()->LoadPlugins(message_loop()->message_loop_proxy(), callback);
+ plugin_loader()->GetPlugins(callback);
EXPECT_CALL(*plugin_loader(), LoadPluginsInternal()).Times(1);
message_loop()->RunUntilIdle();
@@ -262,7 +288,7 @@ TEST_F(PluginLoaderPosixTest, CrashedProcess) {
PluginService::GetPluginsCallback callback =
base::Bind(&VerifyCallback, base::Unretained(&did_callback));
- plugin_loader()->LoadPlugins(message_loop()->message_loop_proxy(), callback);
+ plugin_loader()->GetPlugins(callback);
EXPECT_CALL(*plugin_loader(), LoadPluginsInternal()).Times(1);
message_loop()->RunUntilIdle();
@@ -294,7 +320,7 @@ TEST_F(PluginLoaderPosixTest, InternalPlugin) {
PluginService::GetPluginsCallback callback =
base::Bind(&VerifyCallback, base::Unretained(&did_callback));
- plugin_loader()->LoadPlugins(message_loop()->message_loop_proxy(), callback);
+ plugin_loader()->GetPlugins(callback);
EXPECT_CALL(*plugin_loader(), LoadPluginsInternal()).Times(1);
message_loop()->RunUntilIdle();
@@ -342,7 +368,7 @@ TEST_F(PluginLoaderPosixTest, AllCrashed) {
PluginService::GetPluginsCallback callback =
base::Bind(&VerifyCallback, base::Unretained(&did_callback));
- plugin_loader()->LoadPlugins(message_loop()->message_loop_proxy(), callback);
+ plugin_loader()->GetPlugins(callback);
// Spin the loop so that the canonical list of plugins can be set.
EXPECT_CALL(*plugin_loader(), LoadPluginsInternal()).Times(1);
diff --git a/chromium/content/browser/plugin_process_host.cc b/chromium/content/browser/plugin_process_host.cc
index 9ec7510dfcf..6c65dbf3c92 100644
--- a/chromium/content/browser/plugin_process_host.cc
+++ b/chromium/content/browser/plugin_process_host.cc
@@ -36,16 +36,14 @@
#include "content/public/browser/resource_context.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/process_type.h"
+#include "content/public/common/sandboxed_process_launcher_delegate.h"
#include "ipc/ipc_switches.h"
#include "net/url_request/url_request_context_getter.h"
#include "ui/base/ui_base_switches.h"
#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/switches.h"
#include "ui/gl/gl_switches.h"
-#if defined(USE_X11)
-#include "ui/gfx/gtk_native_view_id_manager.h"
-#endif
-
#if defined(OS_MACOSX)
#include "base/mac/mac_util.h"
#include "content/common/plugin_carbon_interpose_constants_mac.h"
@@ -55,7 +53,6 @@
#if defined(OS_WIN)
#include "base/win/windows_version.h"
#include "content/common/plugin_constants_win.h"
-#include "content/public/common/sandboxed_process_launcher_delegate.h"
#endif
namespace content {
@@ -76,34 +73,39 @@ void PluginProcessHost::OnPluginWindowDestroyed(HWND window, HWND parent) {
void PluginProcessHost::AddWindow(HWND window) {
plugin_parent_windows_set_.insert(window);
}
+#endif // defined(OS_WIN)
// NOTE: changes to this class need to be reviewed by the security team.
class PluginSandboxedProcessLauncherDelegate
: public SandboxedProcessLauncherDelegate {
public:
- PluginSandboxedProcessLauncherDelegate() {}
+ explicit PluginSandboxedProcessLauncherDelegate(ChildProcessHost* host)
+#if defined(OS_POSIX)
+ : ipc_fd_(host->TakeClientFileDescriptor())
+#endif // OS_POSIX
+ {}
+
virtual ~PluginSandboxedProcessLauncherDelegate() {}
- virtual void ShouldSandbox(bool* in_sandbox) OVERRIDE {
- *in_sandbox = false;
+#if defined(OS_WIN)
+ virtual bool ShouldSandbox() OVERRIDE {
+ return false;
+ }
+
+#elif defined(OS_POSIX)
+ virtual int GetIpcFd() OVERRIDE {
+ return ipc_fd_;
}
+#endif // OS_WIN
private:
+#if defined(OS_POSIX)
+ int ipc_fd_;
+#endif // OS_POSIX
+
DISALLOW_COPY_AND_ASSIGN(PluginSandboxedProcessLauncherDelegate);
};
-#endif // defined(OS_WIN)
-
-#if defined(TOOLKIT_GTK)
-void PluginProcessHost::OnMapNativeViewId(gfx::NativeViewId id,
- gfx::PluginWindowHandle* output) {
- *output = 0;
-#if !defined(USE_AURA)
- GtkNativeViewManager::GetInstance()->GetXIDForId(output, id);
-#endif
-}
-#endif // defined(TOOLKIT_GTK)
-
PluginProcessHost::PluginProcessHost()
#if defined(OS_MACOSX)
: plugin_cursor_visible_(true)
@@ -189,20 +191,20 @@ bool PluginProcessHost::Init(const WebPluginInfo& info) {
// any associated values) if present in the browser command line
static const char* const kSwitchNames[] = {
switches::kDisableBreakpad,
-#if defined(OS_MACOSX)
- switches::kDisableCoreAnimationPlugins,
- switches::kEnableSandboxLogging,
-#endif
+ switches::kDisableDirectNPAPIRequests,
switches::kEnableStatsTable,
switches::kFullMemoryCrashReport,
switches::kLoggingLevel,
switches::kLogPluginMessages,
switches::kNoSandbox,
switches::kPluginStartupDialog,
- switches::kTestSandbox,
switches::kTraceStartup,
switches::kUseGL,
- switches::kUserAgent,
+ switches::kForceDeviceScaleFactor,
+#if defined(OS_MACOSX)
+ switches::kDisableCoreAnimationPlugins,
+ switches::kEnableSandboxLogging,
+#endif
};
cmd_line->CopySwitchesFrom(browser_command_line, kSwitchNames,
@@ -226,7 +228,7 @@ bool PluginProcessHost::Init(const WebPluginInfo& info) {
#if defined(OS_POSIX)
base::EnvironmentMap env;
#if defined(OS_MACOSX) && !defined(__LP64__)
- if (!browser_command_line.HasSwitch(switches::kDisableCarbonInterposing)) {
+ if (browser_command_line.HasSwitch(switches::kEnableCarbonInterposing)) {
std::string interpose_list = GetContentClient()->GetCarbonInterposePath();
if (!interpose_list.empty()) {
// Add our interposing library for Carbon. This is stripped back out in
@@ -243,12 +245,7 @@ bool PluginProcessHost::Init(const WebPluginInfo& info) {
#endif
process_->Launch(
-#if defined(OS_WIN)
- new PluginSandboxedProcessLauncherDelegate,
-#elif defined(OS_POSIX)
- false,
- env,
-#endif
+ new PluginSandboxedProcessLauncherDelegate(process_->GetHost()),
cmd_line);
// The plugin needs to be shutdown gracefully, i.e. NP_Shutdown needs to be
@@ -264,7 +261,7 @@ bool PluginProcessHost::Init(const WebPluginInfo& info) {
// TODO(jam): right now we're passing NULL for appcache, blob storage, and
// file system. If NPAPI plugins actually use this, we'll have to plumb them.
ResourceMessageFilter* resource_message_filter = new ResourceMessageFilter(
- process_->GetData().id, PROCESS_TYPE_PLUGIN, NULL, NULL, NULL,
+ process_->GetData().id, PROCESS_TYPE_PLUGIN, NULL, NULL, NULL, NULL,
get_contexts_callback);
process_->AddFilter(resource_message_filter);
return true;
@@ -286,10 +283,6 @@ bool PluginProcessHost::OnMessageReceived(const IPC::Message& msg) {
IPC_MESSAGE_HANDLER(PluginProcessHostMsg_PluginWindowDestroyed,
OnPluginWindowDestroyed)
#endif
-#if defined(TOOLKIT_GTK)
- IPC_MESSAGE_HANDLER(PluginProcessHostMsg_MapNativeViewId,
- OnMapNativeViewId)
-#endif
#if defined(OS_MACOSX)
IPC_MESSAGE_HANDLER(PluginProcessHostMsg_PluginSelectWindow,
OnPluginSelectWindow)
diff --git a/chromium/content/browser/plugin_process_host.h b/chromium/content/browser/plugin_process_host.h
index aaaf6ddf566..71c758039ba 100644
--- a/chromium/content/browser/plugin_process_host.h
+++ b/chromium/content/browser/plugin_process_host.h
@@ -133,10 +133,6 @@ class CONTENT_EXPORT PluginProcessHost : public BrowserChildProcessHostDelegate,
void OnPluginWindowDestroyed(HWND window, HWND parent);
#endif
-#if defined(USE_X11)
- void OnMapNativeViewId(gfx::NativeViewId id, gfx::PluginWindowHandle* output);
-#endif
-
#if defined(OS_MACOSX)
void OnPluginSelectWindow(uint32 window_id, gfx::Rect window_rect,
bool modal);
diff --git a/chromium/content/browser/plugin_service_impl.cc b/chromium/content/browser/plugin_service_impl.cc
index cb0a7b4e599..a2c3170baf5 100644
--- a/chromium/content/browser/plugin_service_impl.cc
+++ b/chromium/content/browser/plugin_service_impl.cc
@@ -115,6 +115,12 @@ void NotifyPluginDirChanged(const base::FilePath& path, bool error) {
}
#endif
+void ForwardCallback(base::MessageLoopProxy* target_loop,
+ const PluginService::GetPluginsCallback& callback,
+ const std::vector<WebPluginInfo>& plugins) {
+ target_loop->PostTask(FROM_HERE, base::Bind(callback, plugins));
+}
+
} // namespace
// static
@@ -292,7 +298,7 @@ PluginProcessHost* PluginServiceImpl::FindOrStartNpapiPluginProcess(
// Record when NPAPI Flash process is started for the first time.
static bool counted = false;
- if (!counted && UTF16ToUTF8(info.name) == kFlashPluginName) {
+ if (!counted && base::UTF16ToUTF8(info.name) == kFlashPluginName) {
counted = true;
UMA_HISTOGRAM_ENUMERATION("Plugin.FlashUsage",
START_NPAPI_FLASH_AT_LEAST_ONCE,
@@ -574,7 +580,7 @@ base::string16 PluginServiceImpl::GetPluginDisplayNameByPath(
// Many plugins on the Mac have .plugin in the actual name, which looks
// terrible, so look for that and strip it off if present.
const std::string kPluginExtension = ".plugin";
- if (EndsWith(plugin_name, ASCIIToUTF16(kPluginExtension), true))
+ if (EndsWith(plugin_name, base::ASCIIToUTF16(kPluginExtension), true))
plugin_name.erase(plugin_name.length() - kPluginExtension.length());
#endif // OS_MACOSX
}
@@ -597,20 +603,9 @@ void PluginServiceImpl::GetPlugins(const GetPluginsCallback& callback) {
return;
}
#if defined(OS_POSIX)
- std::vector<WebPluginInfo> cached_plugins;
- if (PluginList::Singleton()->GetPluginsNoRefresh(&cached_plugins)) {
- // Can't assume the caller is reentrant.
- target_loop->PostTask(FROM_HERE,
- base::Bind(callback, cached_plugins));
- } else {
- // If we switch back to loading plugins in process, then we need to make
- // sure g_thread_init() gets called since plugins may call glib at load.
- if (!plugin_loader_.get())
- plugin_loader_ = new PluginLoaderPosix;
- BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
- base::Bind(&PluginLoaderPosix::LoadPlugins, plugin_loader_,
- target_loop, callback));
- }
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+ base::Bind(&PluginServiceImpl::GetPluginsOnIOThread,
+ base::Unretained(this), target_loop, callback));
#else
NOTREACHED();
#endif
@@ -629,6 +624,23 @@ void PluginServiceImpl::GetPluginsInternal(
base::Bind(callback, plugins));
}
+#if defined(OS_POSIX)
+void PluginServiceImpl::GetPluginsOnIOThread(
+ base::MessageLoopProxy* target_loop,
+ const GetPluginsCallback& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ // If we switch back to loading plugins in process, then we need to make
+ // sure g_thread_init() gets called since plugins may call glib at load.
+
+ if (!plugin_loader_)
+ plugin_loader_ = new PluginLoaderPosix;
+
+ plugin_loader_->GetPlugins(
+ base::Bind(&ForwardCallback, make_scoped_refptr(target_loop), callback));
+}
+#endif
+
void PluginServiceImpl::OnWaitableEventSignaled(
base::WaitableEvent* waitable_event) {
#if defined(OS_WIN)
@@ -749,7 +761,7 @@ void PluginServiceImpl::RefreshPlugins() {
}
void PluginServiceImpl::AddExtraPluginPath(const base::FilePath& path) {
- if (!NPAPIPluginsSupported()) {
+ if (!NPAPIPluginsSupported()) {
// TODO(jam): remove and just have CHECK once we're sure this doesn't get
// triggered.
DVLOG(0) << "NPAPI plugins not supported";
@@ -787,8 +799,7 @@ void PluginServiceImpl::GetInternalPlugins(
}
bool PluginServiceImpl::NPAPIPluginsSupported() {
-#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_BSD) || \
- (defined(OS_LINUX) && !defined(USE_AURA))
+#if defined(OS_WIN) || defined(OS_MACOSX)
return true;
#else
return false;
@@ -841,9 +852,11 @@ bool PluginServiceImpl::IsPluginWindow(HWND window) {
}
#endif
-bool PluginServiceImpl::PpapiDevChannelSupported() {
+bool PluginServiceImpl::PpapiDevChannelSupported(
+ BrowserContext* browser_context,
+ const GURL& document_url) {
return content::GetContentClient()->browser()->
- IsPluginAllowedToUseDevChannelAPIs();
+ IsPluginAllowedToUseDevChannelAPIs(browser_context, document_url);
}
} // namespace content
diff --git a/chromium/content/browser/plugin_service_impl.h b/chromium/content/browser/plugin_service_impl.h
index e95fa7c9786..861889c7a36 100644
--- a/chromium/content/browser/plugin_service_impl.h
+++ b/chromium/content/browser/plugin_service_impl.h
@@ -41,12 +41,6 @@ namespace base {
class MessageLoopProxy;
}
-namespace webkit {
-namespace npapi {
-class PluginList;
-}
-}
-
namespace content {
class BrowserContext;
class PluginDirWatcherDelegate;
@@ -121,7 +115,8 @@ class CONTENT_EXPORT PluginServiceImpl
// Returns true iff the given HWND is a plugin.
bool IsPluginWindow(HWND window);
#endif
- virtual bool PpapiDevChannelSupported() OVERRIDE;
+ virtual bool PpapiDevChannelSupported(BrowserContext* browser_context,
+ const GURL& document_url) OVERRIDE;
// Returns the plugin process host corresponding to the plugin process that
// has been started by this service. This will start a process to host the
@@ -185,6 +180,12 @@ class CONTENT_EXPORT PluginServiceImpl
void GetPluginsInternal(base::MessageLoopProxy* target_loop,
const GetPluginsCallback& callback);
+#if defined(OS_POSIX)
+ void GetPluginsOnIOThread(
+ base::MessageLoopProxy* target_loop,
+ const GetPluginsCallback& callback);
+#endif
+
// Binding directly to GetAllowedPluginForOpenChannelToPlugin() isn't possible
// because more arity is needed <http://crbug.com/98542>. This just forwards.
void ForwardGetAllowedPluginForOpenChannelToPlugin(
diff --git a/chromium/content/browser/plugin_service_impl_browsertest.cc b/chromium/content/browser/plugin_service_impl_browsertest.cc
index e99f3e537c6..7a3271e6c78 100644
--- a/chromium/content/browser/plugin_service_impl_browsertest.cc
+++ b/chromium/content/browser/plugin_service_impl_browsertest.cc
@@ -13,10 +13,10 @@
#include "content/public/browser/resource_context.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_switches.h"
+#include "content/public/test/content_browser_test.h"
#include "content/public/test/test_browser_thread.h"
#include "content/public/test/test_utils.h"
#include "content/shell/browser/shell.h"
-#include "content/test/content_browser_test.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace content {
@@ -60,7 +60,7 @@ class MockPluginProcessHostClient : public PluginProcessHost::Client,
ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
ASSERT_TRUE(set_plugin_info_called_);
ASSERT_TRUE(!channel_);
- channel_ = new IPC::Channel(handle, IPC::Channel::MODE_CLIENT, this);
+ channel_ = IPC::Channel::CreateClient(handle, this).release();
ASSERT_TRUE(channel_->Connect());
}
diff --git a/chromium/content/browser/power_monitor_message_broadcaster.h b/chromium/content/browser/power_monitor_message_broadcaster.h
index 04af601d719..082ba670bbd 100644
--- a/chromium/content/browser/power_monitor_message_broadcaster.h
+++ b/chromium/content/browser/power_monitor_message_broadcaster.h
@@ -6,13 +6,11 @@
#define CONTENT_BROWSER_POWER_MONITOR_MESSAGE_BROADCASTER_H_
#include "base/basictypes.h"
-#include "base/memory/ref_counted.h"
#include "base/power_monitor/power_observer.h"
#include "content/common/content_export.h"
-#include "content/public/browser/browser_thread.h"
namespace IPC {
- class Sender;
+class Sender;
}
namespace content {
@@ -36,6 +34,6 @@ class CONTENT_EXPORT PowerMonitorMessageBroadcaster
DISALLOW_COPY_AND_ASSIGN(PowerMonitorMessageBroadcaster);
};
-} // namespace base
+} // namespace content
#endif // CONTENT_BROWSER_POWER_MONITOR_MESSAGE_BROADCASTER_H_
diff --git a/chromium/content/browser/power_profiler/power_data_provider.h b/chromium/content/browser/power_profiler/power_data_provider.h
new file mode 100644
index 00000000000..85f67e7f5fb
--- /dev/null
+++ b/chromium/content/browser/power_profiler/power_data_provider.h
@@ -0,0 +1,38 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_POWER_PROFILER_POWER_DATA_PROVIDER_H_
+#define CONTENT_BROWSER_POWER_PROFILER_POWER_DATA_PROVIDER_H_
+
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "content/browser/power_profiler/power_event.h"
+
+namespace base {
+class TimeDelta;
+} // namespace base
+
+namespace content {
+
+typedef std::vector<PowerEvent> PowerEventVector;
+
+// A class used to get power usage.
+class PowerDataProvider {
+ public:
+ static scoped_ptr<PowerDataProvider> Create();
+
+ PowerDataProvider() {}
+ virtual ~PowerDataProvider() {}
+
+ // Returns a vector of power events, one per type, for the types it supports.
+ virtual PowerEventVector GetData() = 0;
+
+ // Returns sampling rate at which the provider can operate.
+ virtual base::TimeDelta GetSamplingRate() = 0;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_POWER_PROFILER_POWER_DATA_PROVIDER_H_
diff --git a/chromium/content/browser/power_profiler/power_data_provider_dummy.cc b/chromium/content/browser/power_profiler/power_data_provider_dummy.cc
new file mode 100644
index 00000000000..749f7c5066d
--- /dev/null
+++ b/chromium/content/browser/power_profiler/power_data_provider_dummy.cc
@@ -0,0 +1,13 @@
+// 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/power_profiler/power_data_provider.h"
+
+namespace content {
+
+scoped_ptr<PowerDataProvider> PowerDataProvider::Create() {
+ return make_scoped_ptr<PowerDataProvider>(NULL);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/power_profiler/power_data_provider_ia_win.cc b/chromium/content/browser/power_profiler/power_data_provider_ia_win.cc
new file mode 100644
index 00000000000..2127ae29973
--- /dev/null
+++ b/chromium/content/browser/power_profiler/power_data_provider_ia_win.cc
@@ -0,0 +1,100 @@
+// 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/power_profiler/power_data_provider_ia_win.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+namespace content {
+
+// Default sampling period, as recommended by Intel Power Gadget.
+// Section 3.1 of
+// http://software.intel.com/en-us/blogs/2013/10/03/using-the-intel-power-gadget-api-on-windows
+const int kDefaultSamplePeriodMs = 50;
+
+scoped_ptr<PowerDataProvider> PowerDataProvider::Create() {
+ scoped_ptr<PowerDataProviderIA> provider(new PowerDataProviderIA());
+ if (provider->Initialize())
+ return make_scoped_ptr<PowerDataProvider>(provider.release());
+
+ return make_scoped_ptr<PowerDataProvider>(NULL);
+}
+
+PowerDataProviderIA::PowerDataProviderIA()
+ : sockets_number_(0),
+ is_open_(false) {
+ for (int i = 0; i < PowerEvent::ID_COUNT; i++ )
+ power_msr_ids_[i] = -1;
+}
+
+PowerDataProviderIA::~PowerDataProviderIA() {
+}
+
+PowerEventVector PowerDataProviderIA::GetData() {
+ PowerEventVector events;
+
+ if (!energy_lib_.ReadSample())
+ return events;
+
+ PowerEvent event;
+ double package_power = 0.0;
+ double power[3];
+ int data_count;
+
+ for (int i = 0; i < sockets_number_; i++) {
+ if (power_msr_ids_[PowerEvent::SOC_PACKAGE] == -1)
+ break;
+
+ energy_lib_.GetPowerData(i,
+ power_msr_ids_[PowerEvent::SOC_PACKAGE], power, &data_count);
+ package_power += power[0];
+ }
+
+ event.type = PowerEvent::SOC_PACKAGE;
+ event.value = package_power;
+ event.time = base::TimeTicks::Now();
+ events.push_back(event);
+
+ return events;
+}
+
+base::TimeDelta PowerDataProviderIA::GetSamplingRate() {
+ return base::TimeDelta::FromMilliseconds(kDefaultSamplePeriodMs);
+}
+
+bool PowerDataProviderIA::Initialize() {
+ if (is_open_)
+ return true;
+
+ if (!energy_lib_.IntelEnergyLibInitialize()) {
+ LOG(ERROR) << "Power Data Provider initialize failed!";
+ return false;
+ }
+
+ energy_lib_.GetNumNodes(&sockets_number_);
+
+ const std::wstring package_msr_name = L"Processor";
+
+ int msr_number;
+ energy_lib_.GetNumMsrs(&msr_number);
+
+ int func_id;
+ wchar_t name[32];
+ for(int i = 0; i < msr_number; i++) {
+ energy_lib_.GetMsrFunc(i, &func_id);
+ energy_lib_.GetMsrName(i, name);
+
+ if (func_id != 1)
+ continue;
+
+ if (package_msr_name.compare(name) == 0)
+ power_msr_ids_[PowerEvent::SOC_PACKAGE] = i;
+ }
+
+ is_open_ = true;
+ return true;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/power_profiler/power_data_provider_ia_win.h b/chromium/content/browser/power_profiler/power_data_provider_ia_win.h
new file mode 100644
index 00000000000..2c7e489328d
--- /dev/null
+++ b/chromium/content/browser/power_profiler/power_data_provider_ia_win.h
@@ -0,0 +1,35 @@
+// 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_POWER_PROFILER_POWER_DATA_PROVIDER_IA_WIN_H_
+#define CONTENT_BROWSER_POWER_PROFILER_POWER_DATA_PROVIDER_IA_WIN_H_
+
+#include "content/browser/power_profiler/power_data_provider.h"
+#include "third_party/power_gadget/PowerGadgetLib.h"
+
+namespace content {
+
+// A class used to get power usage via Power Gadget API.
+class PowerDataProviderIA : public PowerDataProvider {
+ public:
+ PowerDataProviderIA();
+
+ virtual ~PowerDataProviderIA();
+
+ bool Initialize();
+ virtual PowerEventVector GetData() OVERRIDE;
+ virtual base::TimeDelta GetSamplingRate() OVERRIDE;
+
+ private:
+ CIntelPowerGadgetLib energy_lib_;
+
+ int sockets_number_;
+ int power_msr_ids_[PowerEvent::ID_COUNT];
+ bool is_open_;
+ DISALLOW_COPY_AND_ASSIGN(PowerDataProviderIA);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_POWER_PROFILER_POWER_DATA_PROVIDER_IA_WIN_H_
diff --git a/chromium/content/browser/power_profiler/power_event.cc b/chromium/content/browser/power_profiler/power_event.cc
new file mode 100644
index 00000000000..52d3fd70677
--- /dev/null
+++ b/chromium/content/browser/power_profiler/power_event.cc
@@ -0,0 +1,17 @@
+// 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/power_profiler/power_event.h"
+
+namespace content {
+
+const char* kPowerTypeNames[] = {
+ "SoC_Package",
+ "Device"
+};
+
+COMPILE_ASSERT(arraysize(kPowerTypeNames) == PowerEvent::ID_COUNT,
+ kPowerTypeNames_incorrect_size);
+
+} // namespace content
diff --git a/chromium/content/browser/power_profiler/power_event.h b/chromium/content/browser/power_profiler/power_event.h
new file mode 100644
index 00000000000..b4031327a4f
--- /dev/null
+++ b/chromium/content/browser/power_profiler/power_event.h
@@ -0,0 +1,42 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_POWER_PROFILER_POWER_EVENT_H_
+#define CONTENT_BROWSER_POWER_PROFILER_POWER_EVENT_H_
+
+#include "base/time/time.h"
+
+namespace content {
+
+struct PowerEvent {
+ enum Type {
+ // Total power of SoC. including CPU, GT and others on the chip,
+ // modules which aren't part of the SoC such as the screen are not included.
+ SOC_PACKAGE,
+
+ // Whole device power.
+ DEVICE,
+
+ // Keep this at the end.
+ ID_COUNT
+ };
+
+ Type type;
+
+ base::TimeTicks time; // Time that power data was read.
+
+ // Power value between last event and this one, in watts.
+ // E.g, event1 {t1, v1}; event2 {t2, v2}; event3 {t3, v3}.
+ // Suppose event1 is the first event the observer received, then event2,
+ // event3. Then v2 is the average power from t1 to t2, v3 is the average
+ // power from t2 to t3. v1 should be ignored since event1 only means the
+ // start point of power profiling.
+ double value;
+};
+
+extern const char* kPowerTypeNames[];
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_POWER_PROFILER_POWER_EVENT_H_
diff --git a/chromium/content/browser/power_profiler/power_profiler_observer.h b/chromium/content/browser/power_profiler/power_profiler_observer.h
new file mode 100644
index 00000000000..027aa3a5606
--- /dev/null
+++ b/chromium/content/browser/power_profiler/power_profiler_observer.h
@@ -0,0 +1,33 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_POWER_PROFILER_POWER_PROFILER_OBSERVER_H_
+#define CONTENT_BROWSER_POWER_PROFILER_POWER_PROFILER_OBSERVER_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "content/common/content_export.h"
+
+namespace content {
+
+struct PowerEvent;
+typedef std::vector<PowerEvent> PowerEventVector;
+
+// A class used to monitor power usage data.
+class CONTENT_EXPORT PowerProfilerObserver {
+ public:
+ PowerProfilerObserver() {}
+ virtual ~PowerProfilerObserver() {}
+
+ // This method will be called on the UI thread.
+ virtual void OnPowerEvent(const PowerEventVector&) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PowerProfilerObserver);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_POWER_PROFILER_POWER_PROFILER_OBSERVER_H_
diff --git a/chromium/content/browser/power_profiler/power_profiler_service.cc b/chromium/content/browser/power_profiler/power_profiler_service.cc
new file mode 100644
index 00000000000..026b42ab893
--- /dev/null
+++ b/chromium/content/browser/power_profiler/power_profiler_service.cc
@@ -0,0 +1,109 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/power_profiler/power_profiler_service.h"
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace content {
+
+PowerProfilerService::PowerProfilerService()
+ : status_(UNINITIALIZED),
+ data_provider_(PowerDataProvider::Create()) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ // No provider supported for current platform.
+ if (!data_provider_.get())
+ return;
+ sample_period_ = data_provider_->GetSamplingRate();
+ status_ = INITIALIZED;
+ task_runner_ = BrowserThread::GetBlockingPool()->GetSequencedTaskRunner(
+ BrowserThread::GetBlockingPool()->GetSequenceToken());
+}
+
+PowerProfilerService::PowerProfilerService(
+ scoped_ptr<PowerDataProvider> provider,
+ scoped_refptr<base::TaskRunner> task_runner,
+ const base::TimeDelta& sample_period)
+ : task_runner_(task_runner),
+ status_(UNINITIALIZED),
+ sample_period_(sample_period),
+ data_provider_(provider.Pass()) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ if (data_provider_.get())
+ status_ = INITIALIZED;
+}
+
+PowerProfilerService::~PowerProfilerService() {
+}
+
+bool PowerProfilerService::IsAvailable() {
+ return status_ != UNINITIALIZED;
+}
+
+PowerProfilerService* PowerProfilerService::GetInstance() {
+ return Singleton<PowerProfilerService>::get();
+}
+
+void PowerProfilerService::AddObserver(PowerProfilerObserver* observer) {
+ if (status_ == UNINITIALIZED)
+ return;
+
+ observers_.AddObserver(observer);
+ if (status_ != PROFILING)
+ Start();
+}
+
+void PowerProfilerService::RemoveObserver(PowerProfilerObserver* observer) {
+ observers_.RemoveObserver(observer);
+
+ if (status_ == PROFILING && !observers_.might_have_observers())
+ Stop();
+}
+
+void PowerProfilerService::Start() {
+ DCHECK(status_ == INITIALIZED);
+ status_ = PROFILING;
+
+ // Send out power events immediately.
+ QueryData();
+
+ query_power_timer_.Start(FROM_HERE,
+ sample_period_, this, &PowerProfilerService::QueryData);
+}
+
+void PowerProfilerService::Stop() {
+ DCHECK(status_ == PROFILING);
+
+ query_power_timer_.Stop();
+ status_ = INITIALIZED;
+}
+
+void PowerProfilerService::QueryData() {
+ task_runner_->PostTask(
+ FROM_HERE, base::Bind(&PowerProfilerService::QueryDataOnTaskRunner,
+ base::Unretained(this)));
+}
+
+void PowerProfilerService::Notify(const PowerEventVector& events) {
+ FOR_EACH_OBSERVER(PowerProfilerObserver, observers_, OnPowerEvent(events));
+}
+
+void PowerProfilerService::QueryDataOnTaskRunner() {
+ DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(status_ == PROFILING);
+
+ // Get data and notify.
+ PowerEventVector events = data_provider_->GetData();
+ if (events.size() != 0) {
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
+ &PowerProfilerService::Notify, base::Unretained(this), events));
+ }
+}
+
+} // namespace content
diff --git a/chromium/content/browser/power_profiler/power_profiler_service.h b/chromium/content/browser/power_profiler/power_profiler_service.h
new file mode 100644
index 00000000000..35f2f65cce3
--- /dev/null
+++ b/chromium/content/browser/power_profiler/power_profiler_service.h
@@ -0,0 +1,77 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_POWER_PROFILER_POWER_PROFILER_SERVICE_H_
+#define CONTENT_BROWSER_POWER_PROFILER_POWER_PROFILER_SERVICE_H_
+
+#include "base/basictypes.h"
+#include "base/memory/singleton.h"
+#include "base/observer_list.h"
+#include "base/task_runner.h"
+#include "base/timer/timer.h"
+#include "content/browser/power_profiler/power_data_provider.h"
+#include "content/browser/power_profiler/power_profiler_observer.h"
+#include "content/common/content_export.h"
+
+namespace content {
+
+// A class used to query power information and notify the observers.
+class CONTENT_EXPORT PowerProfilerService {
+ public:
+ static PowerProfilerService* GetInstance();
+
+ // Add and remove an observer.
+ void AddObserver(PowerProfilerObserver* observer);
+ void RemoveObserver(PowerProfilerObserver* observer);
+
+ bool IsAvailable();
+
+ virtual ~PowerProfilerService();
+
+ private:
+ enum Status {
+ UNINITIALIZED,
+ INITIALIZED, // Initialized, profiling has not started.
+ PROFILING
+ };
+
+ friend struct DefaultSingletonTraits<PowerProfilerService>;
+ friend class PowerProfilerServiceTest;
+
+ PowerProfilerService();
+
+ PowerProfilerService(scoped_ptr<PowerDataProvider> provider,
+ scoped_refptr<base::TaskRunner> task_runner,
+ const base::TimeDelta& sample_period);
+
+ void Start();
+ void Stop();
+
+ // Query power data from PowerDataProvider, executes on the WorkerPool thread.
+ void QueryDataOnTaskRunner();
+
+ // Initiate the query on the UI thread, the task is delegated to
+ // QueryDataOnTaskRunner.
+ void QueryData();
+
+ // Executes on the UI thread.
+ void Notify(const PowerEventVector&);
+
+ base::RepeatingTimer<PowerProfilerService> query_power_timer_;
+ scoped_refptr<base::TaskRunner> task_runner_;
+
+ Status status_;
+
+ // Sampling period of power data measurement.
+ base::TimeDelta sample_period_;
+ ObserverList<PowerProfilerObserver> observers_;
+
+ scoped_ptr<PowerDataProvider> data_provider_;
+
+ DISALLOW_COPY_AND_ASSIGN(PowerProfilerService);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_POWER_PROFILER_POWER_PROFILER_SERVICE_H_
diff --git a/chromium/content/browser/power_profiler/power_profiler_service_unittest.cc b/chromium/content/browser/power_profiler/power_profiler_service_unittest.cc
new file mode 100644
index 00000000000..f7f77dc82dd
--- /dev/null
+++ b/chromium/content/browser/power_profiler/power_profiler_service_unittest.cc
@@ -0,0 +1,143 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "content/browser/browser_thread_impl.h"
+#include "content/browser/power_profiler/power_profiler_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+namespace {
+
+const int kNumEvents = 3;
+const int kDefaultSamplePeriodMs = 50;
+
+// Provide a set number of power events.
+class TestPowerDataProvider : public PowerDataProvider {
+ public:
+ TestPowerDataProvider(int count) : num_events_to_send_(count) {}
+ virtual ~TestPowerDataProvider() {}
+
+ virtual PowerEventVector GetData() OVERRIDE {
+ PowerEventVector events;
+ if (num_events_to_send_ == 0)
+ return events;
+
+ PowerEvent event;
+ event.type = PowerEvent::SOC_PACKAGE;
+ event.time = base::TimeTicks::Now();
+ event.value = 1.0;
+ events.push_back(event);
+
+ num_events_to_send_--;
+ return events;
+ }
+
+ virtual base::TimeDelta GetSamplingRate() OVERRIDE {
+ return base::TimeDelta::FromMilliseconds(kDefaultSamplePeriodMs);
+ }
+
+ private:
+ int num_events_to_send_;
+ DISALLOW_COPY_AND_ASSIGN(TestPowerDataProvider);
+};
+
+class TestPowerProfilerObserver : public PowerProfilerObserver {
+ public:
+ TestPowerProfilerObserver()
+ : valid_event_count_(0),
+ total_num_events_received_(0) {}
+ virtual ~TestPowerProfilerObserver() {}
+
+ virtual void OnPowerEvent(const PowerEventVector& events) OVERRIDE {
+ if (IsValidEvent(events[0]))
+ ++valid_event_count_;
+
+ total_num_events_received_++;
+ if (total_num_events_received_ >= kNumEvents) {
+ // All expected events received, exiting.
+ quit_closure_.Run();
+ }
+ }
+
+ int valid_event_count() const { return valid_event_count_; }
+ void set_quit_closure(base::Closure closure) { quit_closure_ = closure; }
+
+ private:
+ bool IsValidEvent(const PowerEvent& event) {
+ return event.type == PowerEvent::SOC_PACKAGE &&
+ !event.time.is_null() &&
+ event.value > 0;
+ }
+
+ int valid_event_count_;
+ int total_num_events_received_;
+ base::Closure quit_closure_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestPowerProfilerObserver);
+};
+
+} // namespace
+
+class PowerProfilerServiceTest : public testing::Test {
+ public:
+ void ServiceStartTest() {
+ service_.reset(new PowerProfilerService(
+ make_scoped_ptr<PowerDataProvider>(
+ new TestPowerDataProvider(kNumEvents)),
+ message_loop_.message_loop_proxy(),
+ base::TimeDelta::FromMilliseconds(1)));
+ EXPECT_TRUE(service_->IsAvailable());
+ }
+
+ void AddObserverTest() {
+ service_->AddObserver(&observer_);
+
+ // No PowerEvents received.
+ EXPECT_EQ(observer_.valid_event_count(), 0);
+ }
+
+ void RemoveObserverTest() {
+ service_->RemoveObserver(&observer_);
+
+ // Received |kNumEvents| events.
+ EXPECT_EQ(observer_.valid_event_count(), kNumEvents);
+ }
+
+ protected:
+ PowerProfilerServiceTest() : ui_thread_(BrowserThread::UI, &message_loop_) {}
+ virtual ~PowerProfilerServiceTest() {}
+
+ void RegisterQuitClosure(base::Closure closure) {
+ observer_.set_quit_closure(closure);
+ }
+
+ private:
+ scoped_ptr<PowerProfilerService> service_;
+ TestPowerProfilerObserver observer_;
+
+ // UI thread.
+ base::MessageLoopForUI message_loop_;
+ BrowserThreadImpl ui_thread_;
+
+ DISALLOW_COPY_AND_ASSIGN(PowerProfilerServiceTest);
+};
+
+// Test whether PowerProfilerService dispatches power events to observer
+// properly.
+TEST_F(PowerProfilerServiceTest, AvailableService) {
+ base::RunLoop run_loop;
+ RegisterQuitClosure(run_loop.QuitClosure());
+
+ ServiceStartTest();
+ AddObserverTest();
+
+ run_loop.Run();
+
+ RemoveObserverTest();
+}
+
+} // namespace content
diff --git a/chromium/content/browser/power_save_blocker_android.cc b/chromium/content/browser/power_save_blocker_android.cc
index ad9b24cb276..79da1a907ed 100644
--- a/chromium/content/browser/power_save_blocker_android.cc
+++ b/chromium/content/browser/power_save_blocker_android.cc
@@ -5,7 +5,7 @@
#include "content/browser/power_save_blocker_android.h"
#include "base/android/jni_android.h"
-#include "base/android/jni_helper.h"
+#include "base/android/jni_weak_ref.h"
#include "base/logging.h"
#include "content/browser/power_save_blocker_impl.h"
#include "content/public/browser/android/content_view_core.h"
diff --git a/chromium/content/browser/power_save_blocker_win.cc b/chromium/content/browser/power_save_blocker_win.cc
index cf9bb02301a..727600dff14 100644
--- a/chromium/content/browser/power_save_blocker_win.cc
+++ b/chromium/content/browser/power_save_blocker_win.cc
@@ -39,7 +39,7 @@ HANDLE CreatePowerRequest(POWER_REQUEST_TYPE type, const std::string& reason) {
if (!PowerCreateRequestFn || !PowerSetRequestFn)
return INVALID_HANDLE_VALUE;
}
- base::string16 wide_reason = ASCIIToUTF16(reason);
+ base::string16 wide_reason = base::ASCIIToUTF16(reason);
REASON_CONTEXT context = {0};
context.Version = POWER_REQUEST_CONTEXT_VERSION;
context.Flags = POWER_REQUEST_CONTEXT_SIMPLE_STRING;
diff --git a/chromium/content/browser/power_save_blocker_x11.cc b/chromium/content/browser/power_save_blocker_x11.cc
index f55fac9580a..e5fb4e5f3df 100644
--- a/chromium/content/browser/power_save_blocker_x11.cc
+++ b/chromium/content/browser/power_save_blocker_x11.cc
@@ -31,12 +31,6 @@
#include "dbus/object_proxy.h"
#include "ui/gfx/x/x11_types.h"
-#if defined(TOOLKIT_GTK)
-#include "base/message_loop/message_pump_gtk.h"
-#else
-#include "base/message_loop/message_pump_x11.h"
-#endif
-
namespace {
enum DBusAPI {
@@ -299,7 +293,7 @@ void PowerSaveBlockerImpl::Delegate::RemoveBlock(DBusAPI api) {
// static
bool PowerSaveBlockerImpl::Delegate::DPMSEnabled() {
- XDisplay* display = base::MessagePumpForUI::GetDefaultXDisplay();
+ XDisplay* display = gfx::GetXDisplay();
BOOL enabled = false;
int dummy;
if (DPMSQueryExtension(display, &dummy, &dummy) && DPMSCapable(display)) {
diff --git a/chromium/content/browser/ppapi_plugin_process_host.cc b/chromium/content/browser/ppapi_plugin_process_host.cc
index 2cecc772d4b..fc6a75ebed7 100644
--- a/chromium/content/browser/ppapi_plugin_process_host.cc
+++ b/chromium/content/browser/ppapi_plugin_process_host.cc
@@ -21,6 +21,7 @@
#include "content/public/common/content_switches.h"
#include "content/public/common/pepper_plugin_info.h"
#include "content/public/common/process_type.h"
+#include "content/public/common/sandboxed_process_launcher_delegate.h"
#include "ipc/ipc_switches.h"
#include "net/base/network_change_notifier.h"
#include "ppapi/proxy/ppapi_messages.h"
@@ -28,24 +29,30 @@
#if defined(OS_WIN)
#include "content/common/sandbox_win.h"
-#include "content/public/common/sandboxed_process_launcher_delegate.h"
#include "sandbox/win/src/sandbox_policy.h"
#endif
namespace content {
-#if defined(OS_WIN)
// NOTE: changes to this class need to be reviewed by the security team.
class PpapiPluginSandboxedProcessLauncherDelegate
: public content::SandboxedProcessLauncherDelegate {
public:
- explicit PpapiPluginSandboxedProcessLauncherDelegate(bool is_broker)
- : is_broker_(is_broker) {}
+ PpapiPluginSandboxedProcessLauncherDelegate(bool is_broker,
+ const PepperPluginInfo& info,
+ ChildProcessHost* host)
+ :
+#if defined(OS_POSIX)
+ info_(info),
+ ipc_fd_(host->TakeClientFileDescriptor()),
+#endif // OS_POSIX
+ is_broker_(is_broker) {}
+
virtual ~PpapiPluginSandboxedProcessLauncherDelegate() {}
- virtual void ShouldSandbox(bool* in_sandbox) OVERRIDE {
- if (is_broker_)
- *in_sandbox = false;
+#if defined(OS_WIN)
+ virtual bool ShouldSandbox() OVERRIDE {
+ return !is_broker_;
}
virtual void PreSpawnTarget(sandbox::TargetPolicy* policy,
@@ -61,12 +68,27 @@ class PpapiPluginSandboxedProcessLauncherDelegate
*success = (result == sandbox::SBOX_ALL_OK);
}
+#elif defined(OS_POSIX)
+ virtual bool ShouldUseZygote() OVERRIDE {
+ const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
+ CommandLine::StringType plugin_launcher = browser_command_line
+ .GetSwitchValueNative(switches::kPpapiPluginLauncher);
+ return !is_broker_ && plugin_launcher.empty() && info_.is_sandboxed;
+ }
+ virtual int GetIpcFd() OVERRIDE {
+ return ipc_fd_;
+ }
+#endif // OS_WIN
+
private:
+#if defined(OS_POSIX)
+ const PepperPluginInfo& info_;
+ int ipc_fd_;
+#endif // OS_POSIX
bool is_broker_;
DISALLOW_COPY_AND_ASSIGN(PpapiPluginSandboxedProcessLauncherDelegate);
};
-#endif // OS_WIN
class PpapiPluginProcessHost::PluginNetworkObserver
: public net::NetworkChangeNotifier::IPAddressObserver,
@@ -207,10 +229,17 @@ void PpapiPluginProcessHost::OpenChannelToPlugin(Client* client) {
PpapiPluginProcessHost::PpapiPluginProcessHost(
const PepperPluginInfo& info,
const base::FilePath& profile_data_directory)
- : permissions_(
- ppapi::PpapiPermissions::GetForCommandLine(info.permissions)),
- profile_data_directory_(profile_data_directory),
+ : profile_data_directory_(profile_data_directory),
is_broker_(false) {
+ uint32 base_permissions = info.permissions;
+
+ // We don't have to do any whitelisting for APIs in this process host, so
+ // don't bother passing a browser context or document url here.
+ if (GetContentClient()->browser()->IsPluginAllowedToUseDevChannelAPIs(
+ NULL, GURL()))
+ base_permissions |= ppapi::PERMISSION_DEV_CHANNEL;
+ permissions_ = ppapi::PpapiPermissions::GetForCommandLine(base_permissions);
+
process_.reset(new BrowserChildProcessHostImpl(
PROCESS_TYPE_PPAPI_PLUGIN, this));
@@ -250,7 +279,7 @@ bool PpapiPluginProcessHost::Init(const PepperPluginInfo& info) {
if (info.name.empty()) {
process_->SetName(plugin_path_.BaseName().LossyDisplayName());
} else {
- process_->SetName(UTF8ToUTF16(info.name));
+ process_->SetName(base::UTF8ToUTF16(info.name));
}
std::string channel_id = process_->GetHost()->CreateChannel();
@@ -330,17 +359,13 @@ bool PpapiPluginProcessHost::Init(const PepperPluginInfo& info) {
// plugin launcher means we need to use another process instead of just
// forking the zygote.
#if defined(OS_POSIX)
- bool use_zygote = !is_broker_ && plugin_launcher.empty() && info.is_sandboxed;
if (!info.is_sandboxed)
cmd_line->AppendSwitchASCII(switches::kNoSandbox, std::string());
#endif // OS_POSIX
process_->Launch(
-#if defined(OS_WIN)
- new PpapiPluginSandboxedProcessLauncherDelegate(is_broker_),
-#elif defined(OS_POSIX)
- use_zygote,
- base::EnvironmentMap(),
-#endif
+ new PpapiPluginSandboxedProcessLauncherDelegate(is_broker_,
+ info,
+ process_->GetHost()),
cmd_line);
return true;
}
@@ -388,13 +413,10 @@ bool PpapiPluginProcessHost::OnMessageReceived(const IPC::Message& msg) {
// Called when the browser <--> plugin channel has been established.
void PpapiPluginProcessHost::OnChannelConnected(int32 peer_pid) {
- bool supports_dev_channel =
- GetContentClient()->browser()->IsPluginAllowedToUseDevChannelAPIs();
// This will actually load the plugin. Errors will actually not be reported
// back at this point. Instead, the plugin will fail to establish the
// connections when we request them on behalf of the renderer(s).
- Send(new PpapiMsg_LoadPlugin(plugin_path_, permissions_,
- supports_dev_channel));
+ Send(new PpapiMsg_LoadPlugin(plugin_path_, permissions_));
// Process all pending channel requests from the renderers.
for (size_t i = 0; i < pending_requests_.size(); i++)
diff --git a/chromium/content/browser/profiler_controller_impl.cc b/chromium/content/browser/profiler_controller_impl.cc
index 14c2f38b7a5..27c95ed9138 100644
--- a/chromium/content/browser/profiler_controller_impl.cc
+++ b/chromium/content/browser/profiler_controller_impl.cc
@@ -5,7 +5,7 @@
#include "content/browser/profiler_controller_impl.h"
#include "base/bind.h"
-#include "base/command_line.h"
+#include "base/process/process_handle.h"
#include "base/tracked_objects.h"
#include "content/common/child_process_messages.h"
#include "content/public/browser/browser_child_process_host_iterator.h"
@@ -13,7 +13,6 @@
#include "content/public/browser/child_process_data.h"
#include "content/public/browser/profiler_subscriber.h"
#include "content/public/browser/render_process_host.h"
-#include "content/public/common/content_switches.h"
namespace content {
@@ -78,12 +77,12 @@ void ProfilerControllerImpl::GetProfilerDataFromChildProcesses(
int pending_processes = 0;
for (BrowserChildProcessHostIterator iter; !iter.Done(); ++iter) {
- // Skips requesting profiler data from the "GPU Process" if we are using in
- // process GPU. Those stats should be in the Browser-process's GPU thread.
- if (iter.GetData().process_type == PROCESS_TYPE_GPU &&
- CommandLine::ForCurrentProcess()->HasSwitch(switches::kInProcessGPU)) {
+ // In some cases, there may be no child process of the given type (for
+ // example, the GPU process may not exist and there may instead just be a
+ // GPU thread in the browser process). If that's the case, then the process
+ // handle will be base::kNullProcessHandle and we shouldn't ask it for data.
+ if (iter.GetData().handle == base::kNullProcessHandle)
continue;
- }
++pending_processes;
if (!iter.Send(new ChildProcessMsg_GetChildProfilerData(sequence_number)))
diff --git a/chromium/content/browser/profiler_message_filter.cc b/chromium/content/browser/profiler_message_filter.cc
index adea8e71fa0..ef8b2b72ffc 100644
--- a/chromium/content/browser/profiler_message_filter.cc
+++ b/chromium/content/browser/profiler_message_filter.cc
@@ -12,7 +12,8 @@
namespace content {
ProfilerMessageFilter::ProfilerMessageFilter(int process_type)
- : process_type_(process_type) {
+ : BrowserMessageFilter(ChildProcessMsgStart),
+ process_type_(process_type) {
}
void ProfilerMessageFilter::OnChannelConnected(int32 peer_pid) {
@@ -21,17 +22,16 @@ void ProfilerMessageFilter::OnChannelConnected(int32 peer_pid) {
Send(new ChildProcessMsg_SetProfilerStatus(status));
}
-bool ProfilerMessageFilter::OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) {
+bool ProfilerMessageFilter::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(ProfilerMessageFilter, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(ProfilerMessageFilter, message)
IPC_MESSAGE_HANDLER(ChildProcessHostMsg_ChildProfilerData,
OnChildProfilerData)
#if defined(USE_TCMALLOC)
IPC_MESSAGE_HANDLER(ChildProcessHostMsg_TcmallocStats, OnTcmallocStats)
#endif
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return handled;
}
diff --git a/chromium/content/browser/profiler_message_filter.h b/chromium/content/browser/profiler_message_filter.h
index bbac0babbbb..1575abcc6f2 100644
--- a/chromium/content/browser/profiler_message_filter.h
+++ b/chromium/content/browser/profiler_message_filter.h
@@ -24,8 +24,7 @@ class ProfilerMessageFilter : public BrowserMessageFilter {
virtual void OnChannelConnected(int32 peer_pid) OVERRIDE;
// BrowserMessageFilter implementation.
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
protected:
virtual ~ProfilerMessageFilter();
diff --git a/chromium/content/browser/push_messaging_message_filter.cc b/chromium/content/browser/push_messaging_message_filter.cc
new file mode 100644
index 00000000000..451c11253f6
--- /dev/null
+++ b/chromium/content/browser/push_messaging_message_filter.cc
@@ -0,0 +1,97 @@
+// 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/push_messaging_message_filter.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "content/browser/renderer_host/render_process_host_impl.h"
+#include "content/common/push_messaging_messages.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/push_messaging_service.h"
+
+namespace content {
+
+PushMessagingMessageFilter::PushMessagingMessageFilter(int render_process_id)
+ : BrowserMessageFilter(PushMessagingMsgStart),
+ render_process_id_(render_process_id),
+ service_(NULL),
+ weak_factory_(this) {}
+
+PushMessagingMessageFilter::~PushMessagingMessageFilter() {}
+
+bool PushMessagingMessageFilter::OnMessageReceived(
+ const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(PushMessagingMessageFilter, message)
+ IPC_MESSAGE_HANDLER(PushMessagingHostMsg_Register, OnRegister)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void PushMessagingMessageFilter::OnRegister(int routing_id,
+ int callbacks_id,
+ const std::string& sender_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ // TODO(mvanouwerkerk): Validate arguments?
+ // TODO(mvanouwerkerk): A WebContentsObserver could avoid this PostTask
+ // by receiving the IPC on the UI thread.
+ BrowserThread::PostTask(BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&PushMessagingMessageFilter::DoRegister,
+ weak_factory_.GetWeakPtr(),
+ routing_id,
+ callbacks_id,
+ sender_id));
+}
+
+void PushMessagingMessageFilter::DoRegister(int routing_id,
+ int callbacks_id,
+ const std::string& sender_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (!service()) {
+ DidRegister(routing_id, callbacks_id, GURL(), "", false);
+ return;
+ }
+ // TODO(mvanouwerkerk): Pass in a real app ID based on Service Worker ID.
+ std::string app_id = "https://example.com 0";
+ service_->Register(app_id,
+ sender_id,
+ base::Bind(&PushMessagingMessageFilter::DidRegister,
+ weak_factory_.GetWeakPtr(),
+ routing_id,
+ callbacks_id));
+}
+
+void PushMessagingMessageFilter::DidRegister(int routing_id,
+ int callbacks_id,
+ const GURL& endpoint,
+ const std::string& registration_id,
+ bool success) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (success) {
+ Send(new PushMessagingMsg_RegisterSuccess(routing_id,
+ callbacks_id,
+ endpoint,
+ registration_id));
+ } else {
+ Send(new PushMessagingMsg_RegisterError(routing_id, callbacks_id));
+ }
+}
+
+PushMessagingService* PushMessagingMessageFilter::service() {
+ if (!service_) {
+ RenderProcessHostImpl* host = static_cast<RenderProcessHostImpl*>(
+ RenderProcessHost::FromID(render_process_id_));
+ if (!host)
+ return NULL;
+ service_ = host->GetBrowserContext()->GetPushMessagingService();
+ }
+ return service_;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/push_messaging_message_filter.h b/chromium/content/browser/push_messaging_message_filter.h
new file mode 100644
index 00000000000..abc02188873
--- /dev/null
+++ b/chromium/content/browser/push_messaging_message_filter.h
@@ -0,0 +1,54 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_PUSH_MESSAGING_MESSAGE_FILTER_H_
+#define CONTENT_BROWSER_PUSH_MESSAGING_MESSAGE_FILTER_H_
+
+#include <string>
+
+#include "base/memory/weak_ptr.h"
+#include "content/public/browser/browser_message_filter.h"
+#include "url/gurl.h"
+
+namespace content {
+
+class PushMessagingService;
+
+class PushMessagingMessageFilter : public BrowserMessageFilter {
+ public:
+ explicit PushMessagingMessageFilter(int render_process_id);
+
+ private:
+ virtual ~PushMessagingMessageFilter();
+
+ // BrowserMessageFilter implementation.
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+
+ void OnRegister(int routing_id,
+ int callbacks_id,
+ const std::string& sender_id);
+
+ void DoRegister(int routing_id,
+ int callbacks_id,
+ const std::string& sender_id);
+
+ void DidRegister(int routing_id,
+ int callbacks_id,
+ const GURL& endpoint,
+ const std::string& registration_id,
+ bool success);
+
+ PushMessagingService* service();
+
+ int render_process_id_;
+ PushMessagingService* service_; // Not owned.
+
+ base::WeakPtrFactory<PushMessagingMessageFilter> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(PushMessagingMessageFilter);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_PUSH_MESSAGING_MESSAGE_FILTER_H_
diff --git a/chromium/content/browser/quota/DEPS b/chromium/content/browser/quota/DEPS
new file mode 100644
index 00000000000..32545422a6d
--- /dev/null
+++ b/chromium/content/browser/quota/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+third_party/leveldatabase/src/include/leveldb",
+ "+third_party/leveldatabase/src/helpers/memenv",
+]
diff --git a/chromium/content/browser/quota/mock_quota_manager.cc b/chromium/content/browser/quota/mock_quota_manager.cc
new file mode 100644
index 00000000000..0343f39d01f
--- /dev/null
+++ b/chromium/content/browser/quota/mock_quota_manager.cc
@@ -0,0 +1,147 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/quota/mock_quota_manager.h"
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "url/gurl.h"
+
+using quota::kQuotaStatusOk;
+
+namespace content {
+
+MockQuotaManager::OriginInfo::OriginInfo(
+ const GURL& origin,
+ StorageType type,
+ int quota_client_mask,
+ base::Time modified)
+ : origin(origin),
+ type(type),
+ quota_client_mask(quota_client_mask),
+ modified(modified) {
+}
+
+MockQuotaManager::OriginInfo::~OriginInfo() {}
+
+MockQuotaManager::StorageInfo::StorageInfo() : usage(0), quota(kint64max) {}
+MockQuotaManager::StorageInfo::~StorageInfo() {}
+
+MockQuotaManager::MockQuotaManager(
+ bool is_incognito,
+ const base::FilePath& profile_path,
+ base::SingleThreadTaskRunner* io_thread,
+ base::SequencedTaskRunner* db_thread,
+ SpecialStoragePolicy* special_storage_policy)
+ : QuotaManager(is_incognito, profile_path, io_thread, db_thread,
+ special_storage_policy),
+ weak_factory_(this) {
+}
+
+void MockQuotaManager::GetUsageAndQuota(
+ const GURL& origin,
+ quota::StorageType type,
+ const GetUsageAndQuotaCallback& callback) {
+ StorageInfo& info = usage_and_quota_map_[std::make_pair(origin, type)];
+ callback.Run(quota::kQuotaStatusOk, info.usage, info.quota);
+}
+
+void MockQuotaManager::SetQuota(const GURL& origin, StorageType type,
+ int64 quota) {
+ usage_and_quota_map_[std::make_pair(origin, type)].quota = quota;
+}
+
+bool MockQuotaManager::AddOrigin(
+ const GURL& origin,
+ StorageType type,
+ int quota_client_mask,
+ base::Time modified) {
+ origins_.push_back(OriginInfo(origin, type, quota_client_mask, modified));
+ return true;
+}
+
+bool MockQuotaManager::OriginHasData(
+ const GURL& origin,
+ StorageType type,
+ QuotaClient::ID quota_client) const {
+ for (std::vector<OriginInfo>::const_iterator current = origins_.begin();
+ current != origins_.end();
+ ++current) {
+ if (current->origin == origin &&
+ current->type == type &&
+ current->quota_client_mask & quota_client)
+ return true;
+ }
+ return false;
+}
+
+void MockQuotaManager::GetOriginsModifiedSince(
+ StorageType type,
+ base::Time modified_since,
+ const GetOriginsCallback& callback) {
+ std::set<GURL>* origins_to_return = new std::set<GURL>();
+ for (std::vector<OriginInfo>::const_iterator current = origins_.begin();
+ current != origins_.end();
+ ++current) {
+ if (current->type == type && current->modified >= modified_since)
+ origins_to_return->insert(current->origin);
+ }
+
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&MockQuotaManager::DidGetModifiedSince,
+ weak_factory_.GetWeakPtr(),
+ callback,
+ base::Owned(origins_to_return),
+ type));
+}
+
+void MockQuotaManager::DeleteOriginData(
+ const GURL& origin,
+ StorageType type,
+ int quota_client_mask,
+ const StatusCallback& callback) {
+ for (std::vector<OriginInfo>::iterator current = origins_.begin();
+ current != origins_.end();
+ ++current) {
+ if (current->origin == origin && current->type == type) {
+ // Modify the mask: if it's 0 after "deletion", remove the origin.
+ current->quota_client_mask &= ~quota_client_mask;
+ if (current->quota_client_mask == 0)
+ origins_.erase(current);
+ break;
+ }
+ }
+
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&MockQuotaManager::DidDeleteOriginData,
+ weak_factory_.GetWeakPtr(),
+ callback,
+ kQuotaStatusOk));
+}
+
+MockQuotaManager::~MockQuotaManager() {}
+
+void MockQuotaManager::UpdateUsage(
+ const GURL& origin, StorageType type, int64 delta) {
+ usage_and_quota_map_[std::make_pair(origin, type)].usage += delta;
+}
+
+void MockQuotaManager::DidGetModifiedSince(
+ const GetOriginsCallback& callback,
+ std::set<GURL>* origins,
+ StorageType storage_type) {
+ callback.Run(*origins, storage_type);
+}
+
+void MockQuotaManager::DidDeleteOriginData(
+ const StatusCallback& callback,
+ QuotaStatusCode status) {
+ callback.Run(status);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/quota/mock_quota_manager.h b/chromium/content/browser/quota/mock_quota_manager.h
new file mode 100644
index 00000000000..ded0b907904
--- /dev/null
+++ b/chromium/content/browser/quota/mock_quota_manager.h
@@ -0,0 +1,152 @@
+// 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_QUOTA_MOCK_QUOTA_MANAGER_H_
+#define CONTENT_BROWSER_QUOTA_MOCK_QUOTA_MANAGER_H_
+
+#include <map>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "url/gurl.h"
+#include "webkit/browser/quota/quota_client.h"
+#include "webkit/browser/quota/quota_manager.h"
+#include "webkit/browser/quota/quota_task.h"
+#include "webkit/common/quota/quota_types.h"
+
+using quota::GetOriginsCallback;
+using quota::QuotaClient;
+using quota::QuotaManager;
+using quota::QuotaStatusCode;
+using quota::SpecialStoragePolicy;
+using quota::StatusCallback;
+using quota::StorageType;
+
+namespace content {
+
+// Mocks the pieces of QuotaManager's interface.
+//
+// For usage/quota tracking test:
+// Usage and quota information can be updated by following private helper
+// methods: SetQuota() and UpdateUsage().
+//
+// For time-based deletion test:
+// Origins can be added to the mock by calling AddOrigin, and that list of
+// origins is then searched through in GetOriginsModifiedSince.
+// Neither GetOriginsModifiedSince nor DeleteOriginData touches the actual
+// origin data stored in the profile.
+class MockQuotaManager : public QuotaManager {
+ public:
+ MockQuotaManager(bool is_incognito,
+ const base::FilePath& profile_path,
+ base::SingleThreadTaskRunner* io_thread,
+ base::SequencedTaskRunner* db_thread,
+ SpecialStoragePolicy* special_storage_policy);
+
+ // Overrides QuotaManager's implementation. The internal usage data is
+ // updated when MockQuotaManagerProxy::NotifyStorageModified() is
+ // called. The internal quota value can be updated by calling
+ // a helper method MockQuotaManagerProxy::SetQuota().
+ virtual void GetUsageAndQuota(
+ const GURL& origin,
+ quota::StorageType type,
+ const GetUsageAndQuotaCallback& callback) OVERRIDE;
+
+ // Overrides QuotaManager's implementation with a canned implementation that
+ // allows clients to set up the origin database that should be queried. This
+ // method will only search through the origins added explicitly via AddOrigin.
+ virtual void GetOriginsModifiedSince(
+ StorageType type,
+ base::Time modified_since,
+ const GetOriginsCallback& callback) OVERRIDE;
+
+ // Removes an origin from the canned list of origins, but doesn't touch
+ // anything on disk. The caller must provide |quota_client_mask| which
+ // specifies the types of QuotaClients which should be removed from this
+ // origin as a bitmask built from QuotaClient::IDs. Setting the mask to
+ // QuotaClient::kAllClientsMask will remove all clients from the origin,
+ // regardless of type.
+ virtual void DeleteOriginData(const GURL& origin,
+ StorageType type,
+ int quota_client_mask,
+ const StatusCallback& callback) OVERRIDE;
+
+ // Helper method for updating internal quota info.
+ void SetQuota(const GURL& origin, StorageType type, int64 quota);
+
+ // Helper methods for timed-deletion testing:
+ // Adds an origin to the canned list that will be searched through via
+ // GetOriginsModifiedSince. The caller must provide |quota_client_mask|
+ // which specifies the types of QuotaClients this canned origin contains
+ // as a bitmask built from QuotaClient::IDs.
+ bool AddOrigin(const GURL& origin,
+ StorageType type,
+ int quota_client_mask,
+ base::Time modified);
+
+ // Helper methods for timed-deletion testing:
+ // Checks an origin and type against the origins that have been added via
+ // AddOrigin and removed via DeleteOriginData. If the origin exists in the
+ // canned list with the proper StorageType and client, returns true.
+ bool OriginHasData(const GURL& origin,
+ StorageType type,
+ QuotaClient::ID quota_client) const;
+
+ protected:
+ virtual ~MockQuotaManager();
+
+ private:
+ friend class MockQuotaManagerProxy;
+
+ // Contains the essential bits of information about an origin that the
+ // MockQuotaManager needs to understand for time-based deletion:
+ // the origin itself, the StorageType and its modification time.
+ struct OriginInfo {
+ OriginInfo(const GURL& origin,
+ StorageType type,
+ int quota_client_mask,
+ base::Time modified);
+ ~OriginInfo();
+
+ GURL origin;
+ StorageType type;
+ int quota_client_mask;
+ base::Time modified;
+ };
+
+ // Contains the essential information for each origin for usage/quota testing.
+ // (Ideally this should probably merged into the above struct, but for
+ // regular usage/quota testing we hardly need modified time but only
+ // want to keep usage and quota information, so this struct exists.
+ struct StorageInfo {
+ StorageInfo();
+ ~StorageInfo();
+ int64 usage;
+ int64 quota;
+ };
+
+ typedef std::pair<GURL, StorageType> OriginAndType;
+ typedef std::map<OriginAndType, StorageInfo> UsageAndQuotaMap;
+
+ // This must be called via MockQuotaManagerProxy.
+ void UpdateUsage(const GURL& origin, StorageType type, int64 delta);
+ void DidGetModifiedSince(const GetOriginsCallback& callback,
+ std::set<GURL>* origins,
+ StorageType storage_type);
+ void DidDeleteOriginData(const StatusCallback& callback,
+ QuotaStatusCode status);
+
+ // The list of stored origins that have been added via AddOrigin.
+ std::vector<OriginInfo> origins_;
+ UsageAndQuotaMap usage_and_quota_map_;
+ base::WeakPtrFactory<MockQuotaManager> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockQuotaManager);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_QUOTA_MOCK_QUOTA_MANAGER_H_
diff --git a/chromium/content/browser/quota/mock_quota_manager_proxy.cc b/chromium/content/browser/quota/mock_quota_manager_proxy.cc
new file mode 100644
index 00000000000..ddeef6a7b39
--- /dev/null
+++ b/chromium/content/browser/quota/mock_quota_manager_proxy.cc
@@ -0,0 +1,61 @@
+// 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/quota/mock_quota_manager_proxy.h"
+
+#include "base/message_loop/message_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "url/gurl.h"
+
+using quota::kStorageTypeUnknown;
+
+namespace content {
+
+MockQuotaManagerProxy::MockQuotaManagerProxy(
+ MockQuotaManager* quota_manager,
+ base::SingleThreadTaskRunner* task_runner)
+ : QuotaManagerProxy(quota_manager, task_runner),
+ storage_accessed_count_(0),
+ storage_modified_count_(0),
+ last_notified_type_(kStorageTypeUnknown),
+ last_notified_delta_(0),
+ registered_client_(NULL) {}
+
+void MockQuotaManagerProxy::RegisterClient(QuotaClient* client) {
+ DCHECK(!registered_client_);
+ registered_client_ = client;
+}
+
+void MockQuotaManagerProxy::SimulateQuotaManagerDestroyed() {
+ if (registered_client_) {
+ // We cannot call this in the destructor as the client (indirectly)
+ // holds a refptr of the proxy.
+ registered_client_->OnQuotaManagerDestroyed();
+ registered_client_ = NULL;
+ }
+}
+
+void MockQuotaManagerProxy::NotifyStorageAccessed(
+ QuotaClient::ID client_id, const GURL& origin, StorageType type) {
+ ++storage_accessed_count_;
+ last_notified_origin_ = origin;
+ last_notified_type_ = type;
+}
+
+void MockQuotaManagerProxy::NotifyStorageModified(
+ QuotaClient::ID client_id, const GURL& origin,
+ StorageType type, int64 delta) {
+ ++storage_modified_count_;
+ last_notified_origin_ = origin;
+ last_notified_type_ = type;
+ last_notified_delta_ = delta;
+ if (mock_manager())
+ mock_manager()->UpdateUsage(origin, type, delta);
+}
+
+MockQuotaManagerProxy::~MockQuotaManagerProxy() {
+ DCHECK(!registered_client_);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/quota/mock_quota_manager_proxy.h b/chromium/content/browser/quota/mock_quota_manager_proxy.h
new file mode 100644
index 00000000000..9e8835fb08a
--- /dev/null
+++ b/chromium/content/browser/quota/mock_quota_manager_proxy.h
@@ -0,0 +1,87 @@
+// 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_QUOTA_MOCK_QUOTA_MANAGER_PROXY_H_
+#define CONTENT_BROWSER_QUOTA_MOCK_QUOTA_MANAGER_PROXY_H_
+
+#include "content/browser/quota/mock_quota_manager.h"
+#include "url/gurl.h"
+#include "webkit/browser/quota/quota_client.h"
+#include "webkit/browser/quota/quota_manager_proxy.h"
+#include "webkit/common/quota/quota_types.h"
+
+using quota::QuotaManagerProxy;
+
+namespace content {
+
+class MockQuotaManager;
+
+class MockQuotaManagerProxy : public QuotaManagerProxy {
+ public:
+ // It is ok to give NULL to |quota_manager|.
+ MockQuotaManagerProxy(MockQuotaManager* quota_manager,
+ base::SingleThreadTaskRunner* task_runner);
+
+ virtual void RegisterClient(QuotaClient* client) OVERRIDE;
+
+ void SimulateQuotaManagerDestroyed();
+
+ // We don't mock them.
+ virtual void NotifyOriginInUse(const GURL& origin) OVERRIDE {}
+ virtual void NotifyOriginNoLongerInUse(const GURL& origin) OVERRIDE {}
+ virtual void SetUsageCacheEnabled(QuotaClient::ID client_id,
+ const GURL& origin,
+ StorageType type,
+ bool enabled) OVERRIDE {}
+ virtual void GetUsageAndQuota(
+ base::SequencedTaskRunner* original_task_runner,
+ const GURL& origin,
+ StorageType type,
+ const QuotaManager::GetUsageAndQuotaCallback& callback) OVERRIDE {}
+
+ // Validates the |client_id| and updates the internal access count
+ // which can be accessed via notify_storage_accessed_count().
+ // The also records the |origin| and |type| in last_notified_origin_ and
+ // last_notified_type_.
+ virtual void NotifyStorageAccessed(QuotaClient::ID client_id,
+ const GURL& origin,
+ StorageType type) OVERRIDE;
+
+ // Records the |origin|, |type| and |delta| as last_notified_origin_,
+ // last_notified_type_ and last_notified_delta_ respecitvely.
+ // If non-null MockQuotaManager is given to the constructor this also
+ // updates the manager's internal usage information.
+ virtual void NotifyStorageModified(QuotaClient::ID client_id,
+ const GURL& origin,
+ StorageType type,
+ int64 delta) OVERRIDE;
+
+ int notify_storage_accessed_count() const { return storage_accessed_count_; }
+ int notify_storage_modified_count() const { return storage_modified_count_; }
+ GURL last_notified_origin() const { return last_notified_origin_; }
+ StorageType last_notified_type() const { return last_notified_type_; }
+ int64 last_notified_delta() const { return last_notified_delta_; }
+
+ protected:
+ virtual ~MockQuotaManagerProxy();
+
+ private:
+ MockQuotaManager* mock_manager() const {
+ return static_cast<MockQuotaManager*>(quota_manager());
+ }
+
+ int storage_accessed_count_;
+ int storage_modified_count_;
+ GURL last_notified_origin_;
+ StorageType last_notified_type_;
+ int64 last_notified_delta_;
+
+ QuotaClient* registered_client_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockQuotaManagerProxy);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_QUOTA_MOCK_QUOTA_MANAGER_PROXY_H_
diff --git a/chromium/content/browser/quota/mock_quota_manager_unittest.cc b/chromium/content/browser/quota/mock_quota_manager_unittest.cc
new file mode 100644
index 00000000000..e1885c4ff28
--- /dev/null
+++ b/chromium/content/browser/quota/mock_quota_manager_unittest.cc
@@ -0,0 +1,226 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <set>
+
+#include "base/bind.h"
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/run_loop.h"
+#include "content/browser/quota/mock_quota_manager.h"
+#include "content/public/test/mock_special_storage_policy.h"
+#include "content/public/test/mock_storage_client.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using quota::kQuotaStatusOk;
+using quota::kStorageTypePersistent;
+using quota::kStorageTypeTemporary;
+
+namespace content {
+
+const char kTestOrigin1[] = "http://host1:1/";
+const char kTestOrigin2[] = "http://host2:1/";
+const char kTestOrigin3[] = "http://host3:1/";
+
+const GURL kOrigin1(kTestOrigin1);
+const GURL kOrigin2(kTestOrigin2);
+const GURL kOrigin3(kTestOrigin3);
+
+const StorageType kTemporary = kStorageTypeTemporary;
+const StorageType kPersistent = kStorageTypePersistent;
+
+const QuotaClient::ID kClientFile = QuotaClient::kFileSystem;
+const QuotaClient::ID kClientDB = QuotaClient::kIndexedDatabase;
+
+class MockQuotaManagerTest : public testing::Test {
+ public:
+ MockQuotaManagerTest()
+ : deletion_callback_count_(0),
+ weak_factory_(this) {
+ }
+
+ virtual void SetUp() {
+ ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
+ policy_ = new MockSpecialStoragePolicy;
+ manager_ = new MockQuotaManager(false /* is_incognito */,
+ data_dir_.path(),
+ base::MessageLoopProxy::current().get(),
+ base::MessageLoopProxy::current().get(),
+ policy_.get());
+ }
+
+ virtual void TearDown() {
+ // Make sure the quota manager cleans up correctly.
+ manager_ = NULL;
+ base::RunLoop().RunUntilIdle();
+ }
+
+ void GetModifiedOrigins(StorageType type, base::Time since) {
+ manager_->GetOriginsModifiedSince(
+ type, since,
+ base::Bind(&MockQuotaManagerTest::GotModifiedOrigins,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void GotModifiedOrigins(const std::set<GURL>& origins, StorageType type) {
+ origins_ = origins;
+ type_ = type;
+ }
+
+ void DeleteOriginData(const GURL& origin, StorageType type,
+ int quota_client_mask) {
+ manager_->DeleteOriginData(
+ origin, type, quota_client_mask,
+ base::Bind(&MockQuotaManagerTest::DeletedOriginData,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void DeletedOriginData(QuotaStatusCode status) {
+ ++deletion_callback_count_;
+ EXPECT_EQ(kQuotaStatusOk, status);
+ }
+
+ int deletion_callback_count() const {
+ return deletion_callback_count_;
+ }
+
+ MockQuotaManager* manager() const {
+ return manager_.get();
+ }
+
+ const std::set<GURL>& origins() const {
+ return origins_;
+ }
+
+ const StorageType& type() const {
+ return type_;
+ }
+
+ private:
+ base::MessageLoop message_loop_;
+ base::ScopedTempDir data_dir_;
+ scoped_refptr<MockQuotaManager> manager_;
+ scoped_refptr<MockSpecialStoragePolicy> policy_;
+
+ int deletion_callback_count_;
+
+ std::set<GURL> origins_;
+ StorageType type_;
+
+ base::WeakPtrFactory<MockQuotaManagerTest> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockQuotaManagerTest);
+};
+
+TEST_F(MockQuotaManagerTest, BasicOriginManipulation) {
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kTemporary, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kTemporary, kClientDB));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kPersistent, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kPersistent, kClientDB));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kTemporary, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kTemporary, kClientDB));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kPersistent, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kPersistent, kClientDB));
+
+ manager()->AddOrigin(kOrigin1, kTemporary, kClientFile, base::Time::Now());
+ EXPECT_TRUE(manager()->OriginHasData(kOrigin1, kTemporary, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kTemporary, kClientDB));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kPersistent, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kPersistent, kClientDB));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kTemporary, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kTemporary, kClientDB));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kPersistent, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kPersistent, kClientDB));
+
+ manager()->AddOrigin(kOrigin1, kPersistent, kClientFile, base::Time::Now());
+ EXPECT_TRUE(manager()->OriginHasData(kOrigin1, kTemporary, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kTemporary, kClientDB));
+ EXPECT_TRUE(manager()->OriginHasData(kOrigin1, kPersistent, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kPersistent, kClientDB));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kTemporary, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kTemporary, kClientDB));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kPersistent, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kPersistent, kClientDB));
+
+ manager()->AddOrigin(kOrigin2, kTemporary, kClientFile | kClientDB,
+ base::Time::Now());
+ EXPECT_TRUE(manager()->OriginHasData(kOrigin1, kTemporary, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kTemporary, kClientDB));
+ EXPECT_TRUE(manager()->OriginHasData(kOrigin1, kPersistent, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kPersistent, kClientDB));
+ EXPECT_TRUE(manager()->OriginHasData(kOrigin2, kTemporary, kClientFile));
+ EXPECT_TRUE(manager()->OriginHasData(kOrigin2, kTemporary, kClientDB));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kPersistent, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kPersistent, kClientDB));
+}
+
+TEST_F(MockQuotaManagerTest, OriginDeletion) {
+ manager()->AddOrigin(kOrigin1, kTemporary, kClientFile, base::Time::Now());
+ manager()->AddOrigin(kOrigin2, kTemporary, kClientFile | kClientDB,
+ base::Time::Now());
+ manager()->AddOrigin(kOrigin3, kTemporary, kClientFile | kClientDB,
+ base::Time::Now());
+
+ DeleteOriginData(kOrigin2, kTemporary, kClientFile);
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(1, deletion_callback_count());
+ EXPECT_TRUE(manager()->OriginHasData(kOrigin1, kTemporary, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kTemporary, kClientFile));
+ EXPECT_TRUE(manager()->OriginHasData(kOrigin2, kTemporary, kClientDB));
+ EXPECT_TRUE(manager()->OriginHasData(kOrigin3, kTemporary, kClientFile));
+ EXPECT_TRUE(manager()->OriginHasData(kOrigin3, kTemporary, kClientDB));
+
+ DeleteOriginData(kOrigin3, kTemporary, kClientFile | kClientDB);
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(2, deletion_callback_count());
+ EXPECT_TRUE(manager()->OriginHasData(kOrigin1, kTemporary, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kTemporary, kClientFile));
+ EXPECT_TRUE(manager()->OriginHasData(kOrigin2, kTemporary, kClientDB));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin3, kTemporary, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin3, kTemporary, kClientDB));
+}
+
+TEST_F(MockQuotaManagerTest, ModifiedOrigins) {
+ base::Time now = base::Time::Now();
+ base::Time then = base::Time();
+ base::TimeDelta an_hour = base::TimeDelta::FromMilliseconds(3600000);
+ base::TimeDelta a_minute = base::TimeDelta::FromMilliseconds(60000);
+
+ GetModifiedOrigins(kTemporary, then);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(origins().empty());
+
+ manager()->AddOrigin(kOrigin1, kTemporary, kClientFile, now - an_hour);
+
+ GetModifiedOrigins(kTemporary, then);
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(kTemporary, type());
+ EXPECT_EQ(1UL, origins().size());
+ EXPECT_EQ(1UL, origins().count(kOrigin1));
+ EXPECT_EQ(0UL, origins().count(kOrigin2));
+
+ manager()->AddOrigin(kOrigin2, kTemporary, kClientFile, now);
+
+ GetModifiedOrigins(kTemporary, then);
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(kTemporary, type());
+ EXPECT_EQ(2UL, origins().size());
+ EXPECT_EQ(1UL, origins().count(kOrigin1));
+ EXPECT_EQ(1UL, origins().count(kOrigin2));
+
+ GetModifiedOrigins(kTemporary, now - a_minute);
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(kTemporary, type());
+ EXPECT_EQ(1UL, origins().size());
+ EXPECT_EQ(0UL, origins().count(kOrigin1));
+ EXPECT_EQ(1UL, origins().count(kOrigin2));
+}
+} // Namespace content
diff --git a/chromium/content/browser/quota/quota_backend_impl_unittest.cc b/chromium/content/browser/quota/quota_backend_impl_unittest.cc
new file mode 100644
index 00000000000..201fab09375
--- /dev/null
+++ b/chromium/content/browser/quota/quota_backend_impl_unittest.cc
@@ -0,0 +1,273 @@
+// 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 "webkit/browser/fileapi/quota/quota_backend_impl.h"
+
+#include <string>
+
+#include "base/files/scoped_temp_dir.h"
+#include "base/message_loop/message_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
+#include "third_party/leveldatabase/src/include/leveldb/env.h"
+#include "webkit/browser/fileapi/file_system_usage_cache.h"
+#include "webkit/browser/fileapi/obfuscated_file_util.h"
+#include "webkit/browser/quota/quota_manager_proxy.h"
+
+using fileapi::FileSystemUsageCache;
+using fileapi::ObfuscatedFileUtil;
+using fileapi::QuotaBackendImpl;
+using fileapi::SandboxFileSystemBackendDelegate;
+
+namespace content {
+
+namespace {
+
+const char kOrigin[] = "http://example.com";
+
+bool DidReserveQuota(bool accepted,
+ base::File::Error* error_out,
+ int64* delta_out,
+ base::File::Error error,
+ int64 delta) {
+ DCHECK(error_out);
+ DCHECK(delta_out);
+ *error_out = error;
+ *delta_out = delta;
+ return accepted;
+}
+
+class MockQuotaManagerProxy : public quota::QuotaManagerProxy {
+ public:
+ MockQuotaManagerProxy()
+ : QuotaManagerProxy(NULL, NULL),
+ storage_modified_count_(0),
+ usage_(0), quota_(0) {}
+
+ // We don't mock them.
+ virtual void NotifyOriginInUse(const GURL& origin) OVERRIDE {}
+ virtual void NotifyOriginNoLongerInUse(const GURL& origin) OVERRIDE {}
+ virtual void SetUsageCacheEnabled(quota::QuotaClient::ID client_id,
+ const GURL& origin,
+ quota::StorageType type,
+ bool enabled) OVERRIDE {}
+
+ virtual void NotifyStorageModified(
+ quota::QuotaClient::ID client_id,
+ const GURL& origin,
+ quota::StorageType type,
+ int64 delta) OVERRIDE {
+ ++storage_modified_count_;
+ usage_ += delta;
+ ASSERT_LE(usage_, quota_);
+ }
+
+ virtual void GetUsageAndQuota(
+ base::SequencedTaskRunner* original_task_runner,
+ const GURL& origin,
+ quota::StorageType type,
+ const GetUsageAndQuotaCallback& callback) OVERRIDE {
+ callback.Run(quota::kQuotaStatusOk, usage_, quota_);
+ }
+
+ int storage_modified_count() { return storage_modified_count_; }
+ int64 usage() { return usage_; }
+ void set_usage(int64 usage) { usage_ = usage; }
+ void set_quota(int64 quota) { quota_ = quota; }
+
+ protected:
+ virtual ~MockQuotaManagerProxy() {}
+
+ private:
+ int storage_modified_count_;
+ int64 usage_;
+ int64 quota_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockQuotaManagerProxy);
+};
+
+} // namespace
+
+class QuotaBackendImplTest : public testing::Test {
+ public:
+ QuotaBackendImplTest()
+ : file_system_usage_cache_(file_task_runner()),
+ quota_manager_proxy_(new MockQuotaManagerProxy) {}
+
+ virtual void SetUp() OVERRIDE {
+ ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
+ in_memory_env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
+ file_util_.reset(ObfuscatedFileUtil::CreateForTesting(
+ NULL, data_dir_.path(), in_memory_env_.get(), file_task_runner()));
+ backend_.reset(new QuotaBackendImpl(file_task_runner(),
+ file_util_.get(),
+ &file_system_usage_cache_,
+ quota_manager_proxy_.get()));
+ }
+
+ virtual void TearDown() OVERRIDE {
+ backend_.reset();
+ quota_manager_proxy_ = NULL;
+ file_util_.reset();
+ message_loop_.RunUntilIdle();
+ }
+
+ protected:
+ void InitializeForOriginAndType(const GURL& origin,
+ fileapi::FileSystemType type) {
+ ASSERT_TRUE(file_util_->InitOriginDatabase(origin, true /* create */));
+ ASSERT_TRUE(file_util_->origin_database_ != NULL);
+
+ std::string type_string =
+ SandboxFileSystemBackendDelegate::GetTypeString(type);
+ base::File::Error error = base::File::FILE_ERROR_FAILED;
+ base::FilePath path = file_util_->GetDirectoryForOriginAndType(
+ origin, type_string, true /* create */, &error);
+ ASSERT_EQ(base::File::FILE_OK, error);
+
+ ASSERT_TRUE(file_system_usage_cache_.UpdateUsage(
+ GetUsageCachePath(origin, type), 0));
+ }
+
+ base::SequencedTaskRunner* file_task_runner() {
+ return base::MessageLoopProxy::current().get();
+ }
+
+ base::FilePath GetUsageCachePath(const GURL& origin,
+ fileapi::FileSystemType type) {
+ base::FilePath path;
+ base::File::Error error =
+ backend_->GetUsageCachePath(origin, type, &path);
+ EXPECT_EQ(base::File::FILE_OK, error);
+ EXPECT_FALSE(path.empty());
+ return path;
+ }
+
+ base::MessageLoop message_loop_;
+ base::ScopedTempDir data_dir_;
+ scoped_ptr<leveldb::Env> in_memory_env_;
+ scoped_ptr<ObfuscatedFileUtil> file_util_;
+ FileSystemUsageCache file_system_usage_cache_;
+ scoped_refptr<MockQuotaManagerProxy> quota_manager_proxy_;
+ scoped_ptr<QuotaBackendImpl> backend_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuotaBackendImplTest);
+};
+
+TEST_F(QuotaBackendImplTest, ReserveQuota_Basic) {
+ fileapi::FileSystemType type = fileapi::kFileSystemTypeTemporary;
+ InitializeForOriginAndType(GURL(kOrigin), type);
+ quota_manager_proxy_->set_quota(10000);
+
+ int64 delta = 0;
+
+ const int64 kDelta1 = 1000;
+ base::File::Error error = base::File::FILE_ERROR_FAILED;
+ backend_->ReserveQuota(GURL(kOrigin), type, kDelta1,
+ base::Bind(&DidReserveQuota, true, &error, &delta));
+ EXPECT_EQ(base::File::FILE_OK, error);
+ EXPECT_EQ(kDelta1, delta);
+ EXPECT_EQ(kDelta1, quota_manager_proxy_->usage());
+
+ const int64 kDelta2 = -300;
+ error = base::File::FILE_ERROR_FAILED;
+ backend_->ReserveQuota(GURL(kOrigin), type, kDelta2,
+ base::Bind(&DidReserveQuota, true, &error, &delta));
+ EXPECT_EQ(base::File::FILE_OK, error);
+ EXPECT_EQ(kDelta2, delta);
+ EXPECT_EQ(kDelta1 + kDelta2, quota_manager_proxy_->usage());
+
+ EXPECT_EQ(2, quota_manager_proxy_->storage_modified_count());
+}
+
+TEST_F(QuotaBackendImplTest, ReserveQuota_NoSpace) {
+ fileapi::FileSystemType type = fileapi::kFileSystemTypeTemporary;
+ InitializeForOriginAndType(GURL(kOrigin), type);
+ quota_manager_proxy_->set_quota(100);
+
+ int64 delta = 0;
+
+ const int64 kDelta = 1000;
+ base::File::Error error = base::File::FILE_ERROR_FAILED;
+ backend_->ReserveQuota(GURL(kOrigin), type, kDelta,
+ base::Bind(&DidReserveQuota, true, &error, &delta));
+ EXPECT_EQ(base::File::FILE_OK, error);
+ EXPECT_EQ(100, delta);
+ EXPECT_EQ(100, quota_manager_proxy_->usage());
+
+ EXPECT_EQ(1, quota_manager_proxy_->storage_modified_count());
+}
+
+TEST_F(QuotaBackendImplTest, ReserveQuota_Revert) {
+ fileapi::FileSystemType type = fileapi::kFileSystemTypeTemporary;
+ InitializeForOriginAndType(GURL(kOrigin), type);
+ quota_manager_proxy_->set_quota(10000);
+
+ int64 delta = 0;
+
+ const int64 kDelta = 1000;
+ base::File::Error error = base::File::FILE_ERROR_FAILED;
+ backend_->ReserveQuota(GURL(kOrigin), type, kDelta,
+ base::Bind(&DidReserveQuota, false, &error, &delta));
+ EXPECT_EQ(base::File::FILE_OK, error);
+ EXPECT_EQ(kDelta, delta);
+ EXPECT_EQ(0, quota_manager_proxy_->usage());
+
+ EXPECT_EQ(2, quota_manager_proxy_->storage_modified_count());
+}
+
+TEST_F(QuotaBackendImplTest, ReleaseReservedQuota) {
+ fileapi::FileSystemType type = fileapi::kFileSystemTypeTemporary;
+ InitializeForOriginAndType(GURL(kOrigin), type);
+ const int64 kInitialUsage = 2000;
+ quota_manager_proxy_->set_usage(kInitialUsage);
+ quota_manager_proxy_->set_quota(10000);
+
+ const int64 kSize = 1000;
+ backend_->ReleaseReservedQuota(GURL(kOrigin), type, kSize);
+ EXPECT_EQ(kInitialUsage - kSize, quota_manager_proxy_->usage());
+
+ EXPECT_EQ(1, quota_manager_proxy_->storage_modified_count());
+}
+
+TEST_F(QuotaBackendImplTest, CommitQuotaUsage) {
+ fileapi::FileSystemType type = fileapi::kFileSystemTypeTemporary;
+ InitializeForOriginAndType(GURL(kOrigin), type);
+ quota_manager_proxy_->set_quota(10000);
+ base::FilePath path = GetUsageCachePath(GURL(kOrigin), type);
+
+ const int64 kDelta1 = 1000;
+ backend_->CommitQuotaUsage(GURL(kOrigin), type, kDelta1);
+ EXPECT_EQ(kDelta1, quota_manager_proxy_->usage());
+ int64 usage = 0;
+ EXPECT_TRUE(file_system_usage_cache_.GetUsage(path, &usage));
+ EXPECT_EQ(kDelta1, usage);
+
+ const int64 kDelta2 = -300;
+ backend_->CommitQuotaUsage(GURL(kOrigin), type, kDelta2);
+ EXPECT_EQ(kDelta1 + kDelta2, quota_manager_proxy_->usage());
+ usage = 0;
+ EXPECT_TRUE(file_system_usage_cache_.GetUsage(path, &usage));
+ EXPECT_EQ(kDelta1 + kDelta2, usage);
+
+ EXPECT_EQ(2, quota_manager_proxy_->storage_modified_count());
+}
+
+TEST_F(QuotaBackendImplTest, DirtyCount) {
+ fileapi::FileSystemType type = fileapi::kFileSystemTypeTemporary;
+ InitializeForOriginAndType(GURL(kOrigin), type);
+ base::FilePath path = GetUsageCachePath(GURL(kOrigin), type);
+
+ backend_->IncrementDirtyCount(GURL(kOrigin), type);
+ uint32 dirty = 0;
+ ASSERT_TRUE(file_system_usage_cache_.GetDirty(path, &dirty));
+ EXPECT_EQ(1u, dirty);
+
+ backend_->DecrementDirtyCount(GURL(kOrigin), type);
+ ASSERT_TRUE(file_system_usage_cache_.GetDirty(path, &dirty));
+ EXPECT_EQ(0u, dirty);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/quota/quota_database_unittest.cc b/chromium/content/browser/quota/quota_database_unittest.cc
new file mode 100644
index 00000000000..1415ed53f28
--- /dev/null
+++ b/chromium/content/browser/quota/quota_database_unittest.cc
@@ -0,0 +1,567 @@
+// 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 <algorithm>
+#include <iterator>
+#include <set>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/message_loop/message_loop.h"
+#include "content/public/test/mock_special_storage_policy.h"
+#include "sql/connection.h"
+#include "sql/meta_table.h"
+#include "sql/statement.h"
+#include "sql/transaction.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+#include "webkit/browser/quota/quota_database.h"
+
+using quota::kStorageTypePersistent;
+using quota::kStorageTypeTemporary;
+using quota::QuotaDatabase;
+
+namespace content {
+namespace {
+
+const base::Time kZeroTime;
+
+const char kDBFileName[] = "quota_manager.db";
+
+} // namespace
+
+class QuotaDatabaseTest : public testing::Test {
+ protected:
+ typedef QuotaDatabase::QuotaTableEntry QuotaTableEntry;
+ typedef QuotaDatabase::QuotaTableCallback QuotaTableCallback;
+ typedef QuotaDatabase::OriginInfoTableCallback
+ OriginInfoTableCallback;
+
+ void LazyOpen(const base::FilePath& kDbFile) {
+ QuotaDatabase db(kDbFile);
+ EXPECT_FALSE(db.LazyOpen(false));
+ ASSERT_TRUE(db.LazyOpen(true));
+ EXPECT_TRUE(db.db_.get());
+ EXPECT_TRUE(kDbFile.empty() || base::PathExists(kDbFile));
+ }
+
+ void UpgradeSchemaV2toV3(const base::FilePath& kDbFile) {
+ const QuotaTableEntry entries[] = {
+ QuotaTableEntry("a", kStorageTypeTemporary, 1),
+ QuotaTableEntry("b", kStorageTypeTemporary, 2),
+ QuotaTableEntry("c", kStorageTypePersistent, 3),
+ };
+
+ CreateV2Database(kDbFile, entries, ARRAYSIZE_UNSAFE(entries));
+
+ QuotaDatabase db(kDbFile);
+ EXPECT_TRUE(db.LazyOpen(true));
+ EXPECT_TRUE(db.db_.get());
+
+ typedef EntryVerifier<QuotaTableEntry> Verifier;
+ Verifier verifier(entries, entries + ARRAYSIZE_UNSAFE(entries));
+ EXPECT_TRUE(db.DumpQuotaTable(
+ base::Bind(&Verifier::Run, base::Unretained(&verifier))));
+ EXPECT_TRUE(verifier.table.empty());
+ }
+
+ void HostQuota(const base::FilePath& kDbFile) {
+ QuotaDatabase db(kDbFile);
+ ASSERT_TRUE(db.LazyOpen(true));
+
+ const char* kHost = "foo.com";
+ const int kQuota1 = 13579;
+ const int kQuota2 = kQuota1 + 1024;
+
+ int64 quota = -1;
+ EXPECT_FALSE(db.GetHostQuota(kHost, kStorageTypeTemporary, &quota));
+ EXPECT_FALSE(db.GetHostQuota(kHost, kStorageTypePersistent, &quota));
+
+ // Insert quota for temporary.
+ EXPECT_TRUE(db.SetHostQuota(kHost, kStorageTypeTemporary, kQuota1));
+ EXPECT_TRUE(db.GetHostQuota(kHost, kStorageTypeTemporary, &quota));
+ EXPECT_EQ(kQuota1, quota);
+
+ // Update quota for temporary.
+ EXPECT_TRUE(db.SetHostQuota(kHost, kStorageTypeTemporary, kQuota2));
+ EXPECT_TRUE(db.GetHostQuota(kHost, kStorageTypeTemporary, &quota));
+ EXPECT_EQ(kQuota2, quota);
+
+ // Quota for persistent must not be updated.
+ EXPECT_FALSE(db.GetHostQuota(kHost, kStorageTypePersistent, &quota));
+
+ // Delete temporary storage quota.
+ EXPECT_TRUE(db.DeleteHostQuota(kHost, kStorageTypeTemporary));
+ EXPECT_FALSE(db.GetHostQuota(kHost, kStorageTypeTemporary, &quota));
+ }
+
+ void GlobalQuota(const base::FilePath& kDbFile) {
+ QuotaDatabase db(kDbFile);
+ ASSERT_TRUE(db.LazyOpen(true));
+
+ const char* kTempQuotaKey = QuotaDatabase::kTemporaryQuotaOverrideKey;
+ const char* kAvailSpaceKey = QuotaDatabase::kDesiredAvailableSpaceKey;
+
+ int64 value = 0;
+ const int64 kValue1 = 456;
+ const int64 kValue2 = 123000;
+ EXPECT_FALSE(db.GetQuotaConfigValue(kTempQuotaKey, &value));
+ EXPECT_FALSE(db.GetQuotaConfigValue(kAvailSpaceKey, &value));
+
+ EXPECT_TRUE(db.SetQuotaConfigValue(kTempQuotaKey, kValue1));
+ EXPECT_TRUE(db.GetQuotaConfigValue(kTempQuotaKey, &value));
+ EXPECT_EQ(kValue1, value);
+
+ EXPECT_TRUE(db.SetQuotaConfigValue(kTempQuotaKey, kValue2));
+ EXPECT_TRUE(db.GetQuotaConfigValue(kTempQuotaKey, &value));
+ EXPECT_EQ(kValue2, value);
+
+ EXPECT_TRUE(db.SetQuotaConfigValue(kAvailSpaceKey, kValue1));
+ EXPECT_TRUE(db.GetQuotaConfigValue(kAvailSpaceKey, &value));
+ EXPECT_EQ(kValue1, value);
+
+ EXPECT_TRUE(db.SetQuotaConfigValue(kAvailSpaceKey, kValue2));
+ EXPECT_TRUE(db.GetQuotaConfigValue(kAvailSpaceKey, &value));
+ EXPECT_EQ(kValue2, value);
+ }
+
+ void OriginLastAccessTimeLRU(const base::FilePath& kDbFile) {
+ QuotaDatabase db(kDbFile);
+ ASSERT_TRUE(db.LazyOpen(true));
+
+ std::set<GURL> exceptions;
+ GURL origin;
+ EXPECT_TRUE(db.GetLRUOrigin(kStorageTypeTemporary, exceptions,
+ NULL, &origin));
+ EXPECT_TRUE(origin.is_empty());
+
+ const GURL kOrigin1("http://a/");
+ const GURL kOrigin2("http://b/");
+ const GURL kOrigin3("http://c/");
+ const GURL kOrigin4("http://p/");
+
+ // Adding three temporary storages, and
+ EXPECT_TRUE(db.SetOriginLastAccessTime(
+ kOrigin1, kStorageTypeTemporary, base::Time::FromInternalValue(10)));
+ EXPECT_TRUE(db.SetOriginLastAccessTime(
+ kOrigin2, kStorageTypeTemporary, base::Time::FromInternalValue(20)));
+ EXPECT_TRUE(db.SetOriginLastAccessTime(
+ kOrigin3, kStorageTypeTemporary, base::Time::FromInternalValue(30)));
+
+ // one persistent.
+ EXPECT_TRUE(db.SetOriginLastAccessTime(
+ kOrigin4, kStorageTypePersistent, base::Time::FromInternalValue(40)));
+
+ EXPECT_TRUE(db.GetLRUOrigin(kStorageTypeTemporary, exceptions,
+ NULL, &origin));
+ EXPECT_EQ(kOrigin1.spec(), origin.spec());
+
+ // Test that unlimited origins are exluded from eviction, but
+ // protected origins are not excluded.
+ scoped_refptr<MockSpecialStoragePolicy> policy(
+ new MockSpecialStoragePolicy);
+ policy->AddUnlimited(kOrigin1);
+ policy->AddProtected(kOrigin2);
+ EXPECT_TRUE(db.GetLRUOrigin(
+ kStorageTypeTemporary, exceptions, policy.get(), &origin));
+ EXPECT_EQ(kOrigin2.spec(), origin.spec());
+
+ exceptions.insert(kOrigin1);
+ EXPECT_TRUE(db.GetLRUOrigin(kStorageTypeTemporary, exceptions,
+ NULL, &origin));
+ EXPECT_EQ(kOrigin2.spec(), origin.spec());
+
+ exceptions.insert(kOrigin2);
+ EXPECT_TRUE(db.GetLRUOrigin(kStorageTypeTemporary, exceptions,
+ NULL, &origin));
+ EXPECT_EQ(kOrigin3.spec(), origin.spec());
+
+ exceptions.insert(kOrigin3);
+ EXPECT_TRUE(db.GetLRUOrigin(kStorageTypeTemporary, exceptions,
+ NULL, &origin));
+ EXPECT_TRUE(origin.is_empty());
+
+ EXPECT_TRUE(db.SetOriginLastAccessTime(
+ kOrigin1, kStorageTypeTemporary, base::Time::Now()));
+
+ // Delete origin/type last access time information.
+ EXPECT_TRUE(db.DeleteOriginInfo(kOrigin3, kStorageTypeTemporary));
+
+ // Querying again to see if the deletion has worked.
+ exceptions.clear();
+ EXPECT_TRUE(db.GetLRUOrigin(kStorageTypeTemporary, exceptions,
+ NULL, &origin));
+ EXPECT_EQ(kOrigin2.spec(), origin.spec());
+
+ exceptions.insert(kOrigin1);
+ exceptions.insert(kOrigin2);
+ EXPECT_TRUE(db.GetLRUOrigin(kStorageTypeTemporary, exceptions,
+ NULL, &origin));
+ EXPECT_TRUE(origin.is_empty());
+ }
+
+ void OriginLastModifiedSince(const base::FilePath& kDbFile) {
+ QuotaDatabase db(kDbFile);
+ ASSERT_TRUE(db.LazyOpen(true));
+
+ std::set<GURL> origins;
+ EXPECT_TRUE(db.GetOriginsModifiedSince(
+ kStorageTypeTemporary, &origins, base::Time()));
+ EXPECT_TRUE(origins.empty());
+
+ const GURL kOrigin1("http://a/");
+ const GURL kOrigin2("http://b/");
+ const GURL kOrigin3("http://c/");
+
+ // Report last mod time for the test origins.
+ EXPECT_TRUE(db.SetOriginLastModifiedTime(
+ kOrigin1, kStorageTypeTemporary, base::Time::FromInternalValue(0)));
+ EXPECT_TRUE(db.SetOriginLastModifiedTime(
+ kOrigin2, kStorageTypeTemporary, base::Time::FromInternalValue(10)));
+ EXPECT_TRUE(db.SetOriginLastModifiedTime(
+ kOrigin3, kStorageTypeTemporary, base::Time::FromInternalValue(20)));
+
+ EXPECT_TRUE(db.GetOriginsModifiedSince(
+ kStorageTypeTemporary, &origins, base::Time()));
+ EXPECT_EQ(3U, origins.size());
+ EXPECT_EQ(1U, origins.count(kOrigin1));
+ EXPECT_EQ(1U, origins.count(kOrigin2));
+ EXPECT_EQ(1U, origins.count(kOrigin3));
+
+ EXPECT_TRUE(db.GetOriginsModifiedSince(
+ kStorageTypeTemporary, &origins, base::Time::FromInternalValue(5)));
+ EXPECT_EQ(2U, origins.size());
+ EXPECT_EQ(0U, origins.count(kOrigin1));
+ EXPECT_EQ(1U, origins.count(kOrigin2));
+ EXPECT_EQ(1U, origins.count(kOrigin3));
+
+ EXPECT_TRUE(db.GetOriginsModifiedSince(
+ kStorageTypeTemporary, &origins, base::Time::FromInternalValue(15)));
+ EXPECT_EQ(1U, origins.size());
+ EXPECT_EQ(0U, origins.count(kOrigin1));
+ EXPECT_EQ(0U, origins.count(kOrigin2));
+ EXPECT_EQ(1U, origins.count(kOrigin3));
+
+ EXPECT_TRUE(db.GetOriginsModifiedSince(
+ kStorageTypeTemporary, &origins, base::Time::FromInternalValue(25)));
+ EXPECT_TRUE(origins.empty());
+
+ // Update origin1's mod time but for persistent storage.
+ EXPECT_TRUE(db.SetOriginLastModifiedTime(
+ kOrigin1, kStorageTypePersistent, base::Time::FromInternalValue(30)));
+
+ // Must have no effects on temporary origins info.
+ EXPECT_TRUE(db.GetOriginsModifiedSince(
+ kStorageTypeTemporary, &origins, base::Time::FromInternalValue(5)));
+ EXPECT_EQ(2U, origins.size());
+ EXPECT_EQ(0U, origins.count(kOrigin1));
+ EXPECT_EQ(1U, origins.count(kOrigin2));
+ EXPECT_EQ(1U, origins.count(kOrigin3));
+
+ // One more update for persistent origin2.
+ EXPECT_TRUE(db.SetOriginLastModifiedTime(
+ kOrigin2, kStorageTypePersistent, base::Time::FromInternalValue(40)));
+
+ EXPECT_TRUE(db.GetOriginsModifiedSince(
+ kStorageTypePersistent, &origins, base::Time::FromInternalValue(25)));
+ EXPECT_EQ(2U, origins.size());
+ EXPECT_EQ(1U, origins.count(kOrigin1));
+ EXPECT_EQ(1U, origins.count(kOrigin2));
+ EXPECT_EQ(0U, origins.count(kOrigin3));
+
+ EXPECT_TRUE(db.GetOriginsModifiedSince(
+ kStorageTypePersistent, &origins, base::Time::FromInternalValue(35)));
+ EXPECT_EQ(1U, origins.size());
+ EXPECT_EQ(0U, origins.count(kOrigin1));
+ EXPECT_EQ(1U, origins.count(kOrigin2));
+ EXPECT_EQ(0U, origins.count(kOrigin3));
+ }
+
+ void RegisterInitialOriginInfo(const base::FilePath& kDbFile) {
+ QuotaDatabase db(kDbFile);
+
+ const GURL kOrigins[] = {
+ GURL("http://a/"),
+ GURL("http://b/"),
+ GURL("http://c/") };
+ std::set<GURL> origins(kOrigins, kOrigins + ARRAYSIZE_UNSAFE(kOrigins));
+
+ EXPECT_TRUE(db.RegisterInitialOriginInfo(origins, kStorageTypeTemporary));
+
+ int used_count = -1;
+ EXPECT_TRUE(db.FindOriginUsedCount(GURL("http://a/"),
+ kStorageTypeTemporary,
+ &used_count));
+ EXPECT_EQ(0, used_count);
+
+ EXPECT_TRUE(db.SetOriginLastAccessTime(
+ GURL("http://a/"), kStorageTypeTemporary,
+ base::Time::FromDoubleT(1.0)));
+ used_count = -1;
+ EXPECT_TRUE(db.FindOriginUsedCount(GURL("http://a/"),
+ kStorageTypeTemporary,
+ &used_count));
+ EXPECT_EQ(1, used_count);
+
+ EXPECT_TRUE(db.RegisterInitialOriginInfo(origins, kStorageTypeTemporary));
+
+ used_count = -1;
+ EXPECT_TRUE(db.FindOriginUsedCount(GURL("http://a/"),
+ kStorageTypeTemporary,
+ &used_count));
+ EXPECT_EQ(1, used_count);
+ }
+
+ template <typename EntryType>
+ struct EntryVerifier {
+ std::set<EntryType> table;
+
+ template <typename Iterator>
+ EntryVerifier(Iterator itr, Iterator end)
+ : table(itr, end) {}
+
+ bool Run(const EntryType& entry) {
+ EXPECT_EQ(1u, table.erase(entry));
+ return true;
+ }
+ };
+
+ void DumpQuotaTable(const base::FilePath& kDbFile) {
+ QuotaTableEntry kTableEntries[] = {
+ QuotaTableEntry("http://go/", kStorageTypeTemporary, 1),
+ QuotaTableEntry("http://oo/", kStorageTypeTemporary, 2),
+ QuotaTableEntry("http://gle/", kStorageTypePersistent, 3)
+ };
+ QuotaTableEntry* begin = kTableEntries;
+ QuotaTableEntry* end = kTableEntries + ARRAYSIZE_UNSAFE(kTableEntries);
+
+ QuotaDatabase db(kDbFile);
+ EXPECT_TRUE(db.LazyOpen(true));
+ AssignQuotaTable(db.db_.get(), begin, end);
+ db.Commit();
+
+ typedef EntryVerifier<QuotaTableEntry> Verifier;
+ Verifier verifier(begin, end);
+ EXPECT_TRUE(db.DumpQuotaTable(
+ base::Bind(&Verifier::Run, base::Unretained(&verifier))));
+ EXPECT_TRUE(verifier.table.empty());
+ }
+
+ void DumpOriginInfoTable(const base::FilePath& kDbFile) {
+ base::Time now(base::Time::Now());
+ typedef QuotaDatabase::OriginInfoTableEntry Entry;
+ Entry kTableEntries[] = {
+ Entry(GURL("http://go/"), kStorageTypeTemporary, 2147483647, now, now),
+ Entry(GURL("http://oo/"), kStorageTypeTemporary, 0, now, now),
+ Entry(GURL("http://gle/"), kStorageTypeTemporary, 1, now, now),
+ };
+ Entry* begin = kTableEntries;
+ Entry* end = kTableEntries + ARRAYSIZE_UNSAFE(kTableEntries);
+
+ QuotaDatabase db(kDbFile);
+ EXPECT_TRUE(db.LazyOpen(true));
+ AssignOriginInfoTable(db.db_.get(), begin, end);
+ db.Commit();
+
+ typedef EntryVerifier<Entry> Verifier;
+ Verifier verifier(begin, end);
+ EXPECT_TRUE(db.DumpOriginInfoTable(
+ base::Bind(&Verifier::Run, base::Unretained(&verifier))));
+ EXPECT_TRUE(verifier.table.empty());
+ }
+
+ private:
+ template <typename Iterator>
+ void AssignQuotaTable(sql::Connection* db, Iterator itr, Iterator end) {
+ ASSERT_NE(db, (sql::Connection*)NULL);
+ for (; itr != end; ++itr) {
+ const char* kSql =
+ "INSERT INTO HostQuotaTable"
+ " (host, type, quota)"
+ " VALUES (?, ?, ?)";
+ sql::Statement statement;
+ statement.Assign(db->GetCachedStatement(SQL_FROM_HERE, kSql));
+ ASSERT_TRUE(statement.is_valid());
+
+ statement.BindString(0, itr->host);
+ statement.BindInt(1, static_cast<int>(itr->type));
+ statement.BindInt64(2, itr->quota);
+ EXPECT_TRUE(statement.Run());
+ }
+ }
+
+ template <typename Iterator>
+ void AssignOriginInfoTable(sql::Connection* db, Iterator itr, Iterator end) {
+ ASSERT_NE(db, (sql::Connection*)NULL);
+ for (; itr != end; ++itr) {
+ const char* kSql =
+ "INSERT INTO OriginInfoTable"
+ " (origin, type, used_count, last_access_time, last_modified_time)"
+ " VALUES (?, ?, ?, ?, ?)";
+ sql::Statement statement;
+ statement.Assign(db->GetCachedStatement(SQL_FROM_HERE, kSql));
+ ASSERT_TRUE(statement.is_valid());
+
+ statement.BindString(0, itr->origin.spec());
+ statement.BindInt(1, static_cast<int>(itr->type));
+ statement.BindInt(2, itr->used_count);
+ statement.BindInt64(3, itr->last_access_time.ToInternalValue());
+ statement.BindInt64(4, itr->last_modified_time.ToInternalValue());
+ EXPECT_TRUE(statement.Run());
+ }
+ }
+
+ bool OpenDatabase(sql::Connection* db, const base::FilePath& kDbFile) {
+ if (kDbFile.empty()) {
+ return db->OpenInMemory();
+ }
+ if (!base::CreateDirectory(kDbFile.DirName()))
+ return false;
+ if (!db->Open(kDbFile))
+ return false;
+ db->Preload();
+ return true;
+ }
+
+ // Create V2 database and populate some data.
+ void CreateV2Database(
+ const base::FilePath& kDbFile,
+ const QuotaTableEntry* entries,
+ size_t entries_size) {
+ scoped_ptr<sql::Connection> db(new sql::Connection);
+ scoped_ptr<sql::MetaTable> meta_table(new sql::MetaTable);
+
+ // V2 schema definitions.
+ static const int kCurrentVersion = 2;
+ static const int kCompatibleVersion = 2;
+ static const char kHostQuotaTable[] = "HostQuotaTable";
+ static const char kOriginLastAccessTable[] = "OriginLastAccessTable";
+ static const QuotaDatabase::TableSchema kTables[] = {
+ { kHostQuotaTable,
+ "(host TEXT NOT NULL,"
+ " type INTEGER NOT NULL,"
+ " quota INTEGER,"
+ " UNIQUE(host, type))" },
+ { kOriginLastAccessTable,
+ "(origin TEXT NOT NULL,"
+ " type INTEGER NOT NULL,"
+ " used_count INTEGER,"
+ " last_access_time INTEGER,"
+ " UNIQUE(origin, type))" },
+ };
+ static const QuotaDatabase::IndexSchema kIndexes[] = {
+ { "HostIndex",
+ kHostQuotaTable,
+ "(host)",
+ false },
+ { "OriginLastAccessIndex",
+ kOriginLastAccessTable,
+ "(origin, last_access_time)",
+ false },
+ };
+
+ ASSERT_TRUE(OpenDatabase(db.get(), kDbFile));
+ EXPECT_TRUE(QuotaDatabase::CreateSchema(
+ db.get(), meta_table.get(),
+ kCurrentVersion, kCompatibleVersion,
+ kTables, ARRAYSIZE_UNSAFE(kTables),
+ kIndexes, ARRAYSIZE_UNSAFE(kIndexes)));
+
+ // V2 and V3 QuotaTable are compatible, so we can simply use
+ // AssignQuotaTable to poplulate v2 database here.
+ db->BeginTransaction();
+ AssignQuotaTable(db.get(), entries, entries + entries_size);
+ db->CommitTransaction();
+ }
+
+ base::MessageLoop message_loop_;
+};
+
+TEST_F(QuotaDatabaseTest, LazyOpen) {
+ base::ScopedTempDir data_dir;
+ ASSERT_TRUE(data_dir.CreateUniqueTempDir());
+ const base::FilePath kDbFile = data_dir.path().AppendASCII(kDBFileName);
+ LazyOpen(kDbFile);
+ LazyOpen(base::FilePath());
+}
+
+TEST_F(QuotaDatabaseTest, UpgradeSchema) {
+ base::ScopedTempDir data_dir;
+ ASSERT_TRUE(data_dir.CreateUniqueTempDir());
+ const base::FilePath kDbFile = data_dir.path().AppendASCII(kDBFileName);
+ UpgradeSchemaV2toV3(kDbFile);
+}
+
+TEST_F(QuotaDatabaseTest, HostQuota) {
+ base::ScopedTempDir data_dir;
+ ASSERT_TRUE(data_dir.CreateUniqueTempDir());
+ const base::FilePath kDbFile = data_dir.path().AppendASCII(kDBFileName);
+ HostQuota(kDbFile);
+ HostQuota(base::FilePath());
+}
+
+TEST_F(QuotaDatabaseTest, GlobalQuota) {
+ base::ScopedTempDir data_dir;
+ ASSERT_TRUE(data_dir.CreateUniqueTempDir());
+ const base::FilePath kDbFile = data_dir.path().AppendASCII(kDBFileName);
+ GlobalQuota(kDbFile);
+ GlobalQuota(base::FilePath());
+}
+
+TEST_F(QuotaDatabaseTest, OriginLastAccessTimeLRU) {
+ base::ScopedTempDir data_dir;
+ ASSERT_TRUE(data_dir.CreateUniqueTempDir());
+ const base::FilePath kDbFile = data_dir.path().AppendASCII(kDBFileName);
+ OriginLastAccessTimeLRU(kDbFile);
+ OriginLastAccessTimeLRU(base::FilePath());
+}
+
+TEST_F(QuotaDatabaseTest, OriginLastModifiedSince) {
+ base::ScopedTempDir data_dir;
+ ASSERT_TRUE(data_dir.CreateUniqueTempDir());
+ const base::FilePath kDbFile = data_dir.path().AppendASCII(kDBFileName);
+ OriginLastModifiedSince(kDbFile);
+ OriginLastModifiedSince(base::FilePath());
+}
+
+TEST_F(QuotaDatabaseTest, BootstrapFlag) {
+ base::ScopedTempDir data_dir;
+ ASSERT_TRUE(data_dir.CreateUniqueTempDir());
+
+ const base::FilePath kDbFile = data_dir.path().AppendASCII(kDBFileName);
+ QuotaDatabase db(kDbFile);
+
+ EXPECT_FALSE(db.IsOriginDatabaseBootstrapped());
+ EXPECT_TRUE(db.SetOriginDatabaseBootstrapped(true));
+ EXPECT_TRUE(db.IsOriginDatabaseBootstrapped());
+ EXPECT_TRUE(db.SetOriginDatabaseBootstrapped(false));
+ EXPECT_FALSE(db.IsOriginDatabaseBootstrapped());
+}
+
+TEST_F(QuotaDatabaseTest, RegisterInitialOriginInfo) {
+ base::ScopedTempDir data_dir;
+ ASSERT_TRUE(data_dir.CreateUniqueTempDir());
+ const base::FilePath kDbFile = data_dir.path().AppendASCII(kDBFileName);
+ RegisterInitialOriginInfo(kDbFile);
+ RegisterInitialOriginInfo(base::FilePath());
+}
+
+TEST_F(QuotaDatabaseTest, DumpQuotaTable) {
+ base::ScopedTempDir data_dir;
+ ASSERT_TRUE(data_dir.CreateUniqueTempDir());
+ const base::FilePath kDbFile = data_dir.path().AppendASCII(kDBFileName);
+ DumpQuotaTable(kDbFile);
+ DumpQuotaTable(base::FilePath());
+}
+
+TEST_F(QuotaDatabaseTest, DumpOriginInfoTable) {
+ base::ScopedTempDir data_dir;
+ ASSERT_TRUE(data_dir.CreateUniqueTempDir());
+ const base::FilePath kDbFile = data_dir.path().AppendASCII(kDBFileName);
+ DumpOriginInfoTable(kDbFile);
+ DumpOriginInfoTable(base::FilePath());
+}
+} // namespace content
diff --git a/chromium/content/browser/quota/quota_manager_unittest.cc b/chromium/content/browser/quota/quota_manager_unittest.cc
new file mode 100644
index 00000000000..5baa025e859
--- /dev/null
+++ b/chromium/content/browser/quota/quota_manager_unittest.cc
@@ -0,0 +1,2190 @@
+// 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 <algorithm>
+#include <set>
+#include <sstream>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/run_loop.h"
+#include "base/stl_util.h"
+#include "base/sys_info.h"
+#include "base/time/time.h"
+#include "content/public/test/mock_special_storage_policy.h"
+#include "content/public/test/mock_storage_client.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+#include "webkit/browser/quota/quota_database.h"
+#include "webkit/browser/quota/quota_manager.h"
+#include "webkit/browser/quota/quota_manager_proxy.h"
+
+using base::MessageLoopProxy;
+using quota::kQuotaErrorAbort;
+using quota::kQuotaErrorInvalidModification;
+using quota::kQuotaErrorNotSupported;
+using quota::kQuotaStatusOk;
+using quota::kQuotaStatusUnknown;
+using quota::kStorageTypePersistent;
+using quota::kStorageTypeSyncable;
+using quota::kStorageTypeTemporary;
+using quota::kStorageTypeUnknown;
+using quota::QuotaClient;
+using quota::QuotaManager;
+using quota::QuotaStatusCode;
+using quota::StorageType;
+using quota::UsageAndQuota;
+using quota::UsageInfo;
+using quota::UsageInfoEntries;
+
+namespace content {
+
+namespace {
+
+// For shorter names.
+const StorageType kTemp = kStorageTypeTemporary;
+const StorageType kPerm = kStorageTypePersistent;
+const StorageType kSync = kStorageTypeSyncable;
+
+const int kAllClients = QuotaClient::kAllClientsMask;
+
+const int64 kAvailableSpaceForApp = 13377331U;
+
+const int64 kMinimumPreserveForSystem = QuotaManager::kMinimumPreserveForSystem;
+const int kPerHostTemporaryPortion = QuotaManager::kPerHostTemporaryPortion;
+
+// Returns a deterministic value for the amount of available disk space.
+int64 GetAvailableDiskSpaceForTest(const base::FilePath&) {
+ return kAvailableSpaceForApp + kMinimumPreserveForSystem;
+}
+
+} // namespace
+
+class QuotaManagerTest : public testing::Test {
+ protected:
+ typedef QuotaManager::QuotaTableEntry QuotaTableEntry;
+ typedef QuotaManager::QuotaTableEntries QuotaTableEntries;
+ typedef QuotaManager::OriginInfoTableEntries OriginInfoTableEntries;
+
+ public:
+ QuotaManagerTest()
+ : mock_time_counter_(0),
+ weak_factory_(this) {
+ }
+
+ virtual void SetUp() {
+ ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
+ mock_special_storage_policy_ = new MockSpecialStoragePolicy;
+ ResetQuotaManager(false /* is_incognito */);
+ }
+
+ virtual void TearDown() {
+ // Make sure the quota manager cleans up correctly.
+ quota_manager_ = NULL;
+ base::RunLoop().RunUntilIdle();
+ }
+
+ protected:
+ void ResetQuotaManager(bool is_incognito) {
+ quota_manager_ = new QuotaManager(is_incognito,
+ data_dir_.path(),
+ MessageLoopProxy::current().get(),
+ MessageLoopProxy::current().get(),
+ mock_special_storage_policy_.get());
+ // Don't (automatically) start the eviction for testing.
+ quota_manager_->eviction_disabled_ = true;
+ // Don't query the hard disk for remaining capacity.
+ quota_manager_->get_disk_space_fn_ = &GetAvailableDiskSpaceForTest;
+ additional_callback_count_ = 0;
+ }
+
+ MockStorageClient* CreateClient(
+ const MockOriginData* mock_data,
+ size_t mock_data_size,
+ QuotaClient::ID id) {
+ return new MockStorageClient(quota_manager_->proxy(),
+ mock_data, id, mock_data_size);
+ }
+
+ void RegisterClient(MockStorageClient* client) {
+ quota_manager_->proxy()->RegisterClient(client);
+ }
+
+ void GetUsageInfo() {
+ usage_info_.clear();
+ quota_manager_->GetUsageInfo(
+ base::Bind(&QuotaManagerTest::DidGetUsageInfo,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void GetUsageAndQuotaForWebApps(const GURL& origin,
+ StorageType type) {
+ quota_status_ = kQuotaStatusUnknown;
+ usage_ = -1;
+ quota_ = -1;
+ quota_manager_->GetUsageAndQuotaForWebApps(
+ origin, type, base::Bind(&QuotaManagerTest::DidGetUsageAndQuota,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void GetUsageAndQuotaForStorageClient(const GURL& origin,
+ StorageType type) {
+ quota_status_ = kQuotaStatusUnknown;
+ usage_ = -1;
+ quota_ = -1;
+ quota_manager_->GetUsageAndQuota(
+ origin, type, base::Bind(&QuotaManagerTest::DidGetUsageAndQuota,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void GetTemporaryGlobalQuota() {
+ quota_status_ = kQuotaStatusUnknown;
+ quota_ = -1;
+ quota_manager_->GetTemporaryGlobalQuota(
+ base::Bind(&QuotaManagerTest::DidGetQuota,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void SetTemporaryGlobalQuota(int64 new_quota) {
+ quota_status_ = kQuotaStatusUnknown;
+ quota_ = -1;
+ quota_manager_->SetTemporaryGlobalOverrideQuota(
+ new_quota,
+ base::Bind(&QuotaManagerTest::DidGetQuota,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void GetPersistentHostQuota(const std::string& host) {
+ quota_status_ = kQuotaStatusUnknown;
+ quota_ = -1;
+ quota_manager_->GetPersistentHostQuota(
+ host,
+ base::Bind(&QuotaManagerTest::DidGetHostQuota,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void SetPersistentHostQuota(const std::string& host, int64 new_quota) {
+ quota_status_ = kQuotaStatusUnknown;
+ quota_ = -1;
+ quota_manager_->SetPersistentHostQuota(
+ host, new_quota,
+ base::Bind(&QuotaManagerTest::DidGetHostQuota,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void GetGlobalUsage(StorageType type) {
+ usage_ = -1;
+ unlimited_usage_ = -1;
+ quota_manager_->GetGlobalUsage(
+ type,
+ base::Bind(&QuotaManagerTest::DidGetGlobalUsage,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void GetHostUsage(const std::string& host, StorageType type) {
+ usage_ = -1;
+ quota_manager_->GetHostUsage(
+ host, type,
+ base::Bind(&QuotaManagerTest::DidGetHostUsage,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void RunAdditionalUsageAndQuotaTask(const GURL& origin, StorageType type) {
+ quota_manager_->GetUsageAndQuota(
+ origin, type,
+ base::Bind(&QuotaManagerTest::DidGetUsageAndQuotaAdditional,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void DeleteClientOriginData(QuotaClient* client,
+ const GURL& origin,
+ StorageType type) {
+ DCHECK(client);
+ quota_status_ = kQuotaStatusUnknown;
+ client->DeleteOriginData(
+ origin, type,
+ base::Bind(&QuotaManagerTest::StatusCallback,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void EvictOriginData(const GURL& origin,
+ StorageType type) {
+ quota_status_ = kQuotaStatusUnknown;
+ quota_manager_->EvictOriginData(
+ origin, type,
+ base::Bind(&QuotaManagerTest::StatusCallback,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void DeleteOriginData(const GURL& origin,
+ StorageType type,
+ int quota_client_mask) {
+ quota_status_ = kQuotaStatusUnknown;
+ quota_manager_->DeleteOriginData(
+ origin, type, quota_client_mask,
+ base::Bind(&QuotaManagerTest::StatusCallback,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void DeleteHostData(const std::string& host,
+ StorageType type,
+ int quota_client_mask) {
+ quota_status_ = kQuotaStatusUnknown;
+ quota_manager_->DeleteHostData(
+ host, type, quota_client_mask,
+ base::Bind(&QuotaManagerTest::StatusCallback,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void GetAvailableSpace() {
+ quota_status_ = kQuotaStatusUnknown;
+ available_space_ = -1;
+ quota_manager_->GetAvailableSpace(
+ base::Bind(&QuotaManagerTest::DidGetAvailableSpace,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void GetUsageAndQuotaForEviction() {
+ quota_status_ = kQuotaStatusUnknown;
+ usage_ = -1;
+ unlimited_usage_ = -1;
+ quota_ = -1;
+ available_space_ = -1;
+ quota_manager_->GetUsageAndQuotaForEviction(
+ base::Bind(&QuotaManagerTest::DidGetUsageAndQuotaForEviction,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void GetCachedOrigins(StorageType type, std::set<GURL>* origins) {
+ ASSERT_TRUE(origins != NULL);
+ origins->clear();
+ quota_manager_->GetCachedOrigins(type, origins);
+ }
+
+ void NotifyStorageAccessed(QuotaClient* client,
+ const GURL& origin,
+ StorageType type) {
+ DCHECK(client);
+ quota_manager_->NotifyStorageAccessedInternal(
+ client->id(), origin, type, IncrementMockTime());
+ }
+
+ void DeleteOriginFromDatabase(const GURL& origin, StorageType type) {
+ quota_manager_->DeleteOriginFromDatabase(origin, type);
+ }
+
+ void GetLRUOrigin(StorageType type) {
+ lru_origin_ = GURL();
+ quota_manager_->GetLRUOrigin(
+ type,
+ base::Bind(&QuotaManagerTest::DidGetLRUOrigin,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void NotifyOriginInUse(const GURL& origin) {
+ quota_manager_->NotifyOriginInUse(origin);
+ }
+
+ void NotifyOriginNoLongerInUse(const GURL& origin) {
+ quota_manager_->NotifyOriginNoLongerInUse(origin);
+ }
+
+ void GetOriginsModifiedSince(StorageType type, base::Time modified_since) {
+ modified_origins_.clear();
+ modified_origins_type_ = kStorageTypeUnknown;
+ quota_manager_->GetOriginsModifiedSince(
+ type, modified_since,
+ base::Bind(&QuotaManagerTest::DidGetModifiedOrigins,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void DumpQuotaTable() {
+ quota_entries_.clear();
+ quota_manager_->DumpQuotaTable(
+ base::Bind(&QuotaManagerTest::DidDumpQuotaTable,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void DumpOriginInfoTable() {
+ origin_info_entries_.clear();
+ quota_manager_->DumpOriginInfoTable(
+ base::Bind(&QuotaManagerTest::DidDumpOriginInfoTable,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void DidGetUsageInfo(const UsageInfoEntries& entries) {
+ usage_info_.insert(usage_info_.begin(), entries.begin(), entries.end());
+ }
+
+ void DidGetUsageAndQuota(QuotaStatusCode status, int64 usage, int64 quota) {
+ quota_status_ = status;
+ usage_ = usage;
+ quota_ = quota;
+ }
+
+ void DidGetQuota(QuotaStatusCode status,
+ int64 quota) {
+ quota_status_ = status;
+ quota_ = quota;
+ }
+
+ void DidGetAvailableSpace(QuotaStatusCode status, int64 available_space) {
+ quota_status_ = status;
+ available_space_ = available_space;
+ }
+
+ void DidGetHostQuota(QuotaStatusCode status,
+ int64 quota) {
+ quota_status_ = status;
+ quota_ = quota;
+ }
+
+ void DidGetGlobalUsage(int64 usage,
+ int64 unlimited_usage) {
+ usage_ = usage;
+ unlimited_usage_ = unlimited_usage;
+ }
+
+ void DidGetHostUsage(int64 usage) {
+ usage_ = usage;
+ }
+
+ void StatusCallback(QuotaStatusCode status) {
+ ++status_callback_count_;
+ quota_status_ = status;
+ }
+
+ void DidGetUsageAndQuotaForEviction(QuotaStatusCode status,
+ const UsageAndQuota& usage_and_quota) {
+ quota_status_ = status;
+ limited_usage_ = usage_and_quota.global_limited_usage;
+ quota_ = usage_and_quota.quota;
+ available_space_ = usage_and_quota.available_disk_space;
+ }
+
+ void DidGetLRUOrigin(const GURL& origin) {
+ lru_origin_ = origin;
+ }
+
+ void DidGetModifiedOrigins(const std::set<GURL>& origins, StorageType type) {
+ modified_origins_ = origins;
+ modified_origins_type_ = type;
+ }
+
+ void DidDumpQuotaTable(const QuotaTableEntries& entries) {
+ quota_entries_ = entries;
+ }
+
+ void DidDumpOriginInfoTable(const OriginInfoTableEntries& entries) {
+ origin_info_entries_ = entries;
+ }
+
+ void GetUsage_WithModifyTestBody(const StorageType type);
+
+ void set_additional_callback_count(int c) { additional_callback_count_ = c; }
+ int additional_callback_count() const { return additional_callback_count_; }
+ void DidGetUsageAndQuotaAdditional(
+ QuotaStatusCode status, int64 usage, int64 quota) {
+ ++additional_callback_count_;
+ }
+
+ QuotaManager* quota_manager() const { return quota_manager_.get(); }
+ void set_quota_manager(QuotaManager* quota_manager) {
+ quota_manager_ = quota_manager;
+ }
+
+ MockSpecialStoragePolicy* mock_special_storage_policy() const {
+ return mock_special_storage_policy_.get();
+ }
+
+ QuotaStatusCode status() const { return quota_status_; }
+ const UsageInfoEntries& usage_info() const { return usage_info_; }
+ int64 usage() const { return usage_; }
+ int64 limited_usage() const { return limited_usage_; }
+ int64 unlimited_usage() const { return unlimited_usage_; }
+ int64 quota() const { return quota_; }
+ int64 available_space() const { return available_space_; }
+ const GURL& lru_origin() const { return lru_origin_; }
+ const std::set<GURL>& modified_origins() const { return modified_origins_; }
+ StorageType modified_origins_type() const { return modified_origins_type_; }
+ const QuotaTableEntries& quota_entries() const { return quota_entries_; }
+ const OriginInfoTableEntries& origin_info_entries() const {
+ return origin_info_entries_;
+ }
+ base::FilePath profile_path() const { return data_dir_.path(); }
+ int status_callback_count() const { return status_callback_count_; }
+ void reset_status_callback_count() { status_callback_count_ = 0; }
+
+ private:
+ base::Time IncrementMockTime() {
+ ++mock_time_counter_;
+ return base::Time::FromDoubleT(mock_time_counter_ * 10.0);
+ }
+
+ base::MessageLoop message_loop_;
+ base::ScopedTempDir data_dir_;
+
+ scoped_refptr<QuotaManager> quota_manager_;
+ scoped_refptr<MockSpecialStoragePolicy> mock_special_storage_policy_;
+
+ QuotaStatusCode quota_status_;
+ UsageInfoEntries usage_info_;
+ int64 usage_;
+ int64 limited_usage_;
+ int64 unlimited_usage_;
+ int64 quota_;
+ int64 available_space_;
+ GURL lru_origin_;
+ std::set<GURL> modified_origins_;
+ StorageType modified_origins_type_;
+ QuotaTableEntries quota_entries_;
+ OriginInfoTableEntries origin_info_entries_;
+ int status_callback_count_;
+
+ int additional_callback_count_;
+
+ int mock_time_counter_;
+
+ base::WeakPtrFactory<QuotaManagerTest> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuotaManagerTest);
+};
+
+TEST_F(QuotaManagerTest, GetUsageInfo) {
+ static const MockOriginData kData1[] = {
+ { "http://foo.com/", kTemp, 10 },
+ { "http://foo.com:8080/", kTemp, 15 },
+ { "http://bar.com/", kTemp, 20 },
+ { "http://bar.com/", kPerm, 50 },
+ };
+ static const MockOriginData kData2[] = {
+ { "https://foo.com/", kTemp, 30 },
+ { "https://foo.com:8081/", kTemp, 35 },
+ { "http://bar.com/", kPerm, 40 },
+ { "http://example.com/", kPerm, 40 },
+ };
+ RegisterClient(CreateClient(kData1, ARRAYSIZE_UNSAFE(kData1),
+ QuotaClient::kFileSystem));
+ RegisterClient(CreateClient(kData2, ARRAYSIZE_UNSAFE(kData2),
+ QuotaClient::kDatabase));
+
+ GetUsageInfo();
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(4U, usage_info().size());
+ for (size_t i = 0; i < usage_info().size(); ++i) {
+ const UsageInfo& info = usage_info()[i];
+ if (info.host == "foo.com" && info.type == kTemp) {
+ EXPECT_EQ(10 + 15 + 30 + 35, info.usage);
+ } else if (info.host == "bar.com" && info.type == kTemp) {
+ EXPECT_EQ(20, info.usage);
+ } else if (info.host == "bar.com" && info.type == kPerm) {
+ EXPECT_EQ(50 + 40, info.usage);
+ } else if (info.host == "example.com" && info.type == kPerm) {
+ EXPECT_EQ(40, info.usage);
+ } else {
+ ADD_FAILURE()
+ << "Unexpected host, type: " << info.host << ", " << info.type;
+ }
+ }
+}
+
+TEST_F(QuotaManagerTest, GetUsageAndQuota_Simple) {
+ static const MockOriginData kData[] = {
+ { "http://foo.com/", kTemp, 10 },
+ { "http://foo.com/", kPerm, 80 },
+ };
+ RegisterClient(CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem));
+
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(80, usage());
+ EXPECT_EQ(0, quota());
+
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(10, usage());
+ EXPECT_LE(0, quota());
+ int64 quota_returned_for_foo = quota();
+
+ GetUsageAndQuotaForWebApps(GURL("http://bar.com/"), kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(0, usage());
+ EXPECT_EQ(quota_returned_for_foo, quota());
+}
+
+TEST_F(QuotaManagerTest, GetUsage_NoClient) {
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(0, usage());
+
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(0, usage());
+
+ GetHostUsage("foo.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, usage());
+
+ GetHostUsage("foo.com", kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, usage());
+
+ GetGlobalUsage(kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, usage());
+ EXPECT_EQ(0, unlimited_usage());
+
+ GetGlobalUsage(kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, usage());
+ EXPECT_EQ(0, unlimited_usage());
+}
+
+TEST_F(QuotaManagerTest, GetUsage_EmptyClient) {
+ RegisterClient(CreateClient(NULL, 0, QuotaClient::kFileSystem));
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(0, usage());
+
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(0, usage());
+
+ GetHostUsage("foo.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, usage());
+
+ GetHostUsage("foo.com", kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, usage());
+
+ GetGlobalUsage(kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, usage());
+ EXPECT_EQ(0, unlimited_usage());
+
+ GetGlobalUsage(kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, usage());
+ EXPECT_EQ(0, unlimited_usage());
+}
+
+TEST_F(QuotaManagerTest, GetTemporaryUsageAndQuota_MultiOrigins) {
+ static const MockOriginData kData[] = {
+ { "http://foo.com/", kTemp, 10 },
+ { "http://foo.com:8080/", kTemp, 20 },
+ { "http://bar.com/", kTemp, 5 },
+ { "https://bar.com/", kTemp, 7 },
+ { "http://baz.com/", kTemp, 30 },
+ { "http://foo.com/", kPerm, 40 },
+ };
+ RegisterClient(CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem));
+
+ // This time explicitly sets a temporary global quota.
+ SetTemporaryGlobalQuota(100);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(100, quota());
+
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(10 + 20, usage());
+
+ const int kPerHostQuota = 100 / kPerHostTemporaryPortion;
+
+ // The host's quota should be its full portion of the global quota
+ // since global usage is under the global quota.
+ EXPECT_EQ(kPerHostQuota, quota());
+
+ GetUsageAndQuotaForWebApps(GURL("http://bar.com/"), kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(5 + 7, usage());
+ EXPECT_EQ(kPerHostQuota, quota());
+}
+
+TEST_F(QuotaManagerTest, GetUsage_MultipleClients) {
+ static const MockOriginData kData1[] = {
+ { "http://foo.com/", kTemp, 1 },
+ { "http://bar.com/", kTemp, 2 },
+ { "http://bar.com/", kPerm, 4 },
+ { "http://unlimited/", kPerm, 8 },
+ { "http://installed/", kPerm, 16 },
+ };
+ static const MockOriginData kData2[] = {
+ { "https://foo.com/", kTemp, 128 },
+ { "http://example.com/", kPerm, 256 },
+ { "http://unlimited/", kTemp, 512 },
+ { "http://installed/", kTemp, 1024 },
+ };
+ mock_special_storage_policy()->AddUnlimited(GURL("http://unlimited/"));
+ mock_special_storage_policy()->GrantQueryDiskSize(GURL("http://installed/"));
+ RegisterClient(CreateClient(kData1, ARRAYSIZE_UNSAFE(kData1),
+ QuotaClient::kFileSystem));
+ RegisterClient(CreateClient(kData2, ARRAYSIZE_UNSAFE(kData2),
+ QuotaClient::kDatabase));
+
+ const int64 kTempQuotaBase =
+ GetAvailableDiskSpaceForTest(base::FilePath()) / kPerHostTemporaryPortion;
+
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(1 + 128, usage());
+
+ GetUsageAndQuotaForWebApps(GURL("http://bar.com/"), kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(4, usage());
+
+ GetUsageAndQuotaForWebApps(GURL("http://unlimited/"), kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(512, usage());
+ EXPECT_EQ(std::min(kAvailableSpaceForApp, kTempQuotaBase) + usage(), quota());
+
+ GetUsageAndQuotaForWebApps(GURL("http://unlimited/"), kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(8, usage());
+ EXPECT_EQ(kAvailableSpaceForApp + usage(), quota());
+
+ GetAvailableSpace();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_LE(0, available_space());
+
+ GetUsageAndQuotaForWebApps(GURL("http://installed/"), kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(1024, usage());
+ EXPECT_EQ(std::min(kAvailableSpaceForApp, kTempQuotaBase) + usage(), quota());
+
+ GetUsageAndQuotaForWebApps(GURL("http://installed/"), kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(16, usage());
+ EXPECT_EQ(usage(), quota()); // Over-budget case.
+
+ GetGlobalUsage(kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(1 + 2 + 128 + 512 + 1024, usage());
+ EXPECT_EQ(512, unlimited_usage());
+
+ GetGlobalUsage(kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(4 + 8 + 16 + 256, usage());
+ EXPECT_EQ(8, unlimited_usage());
+}
+
+void QuotaManagerTest::GetUsage_WithModifyTestBody(const StorageType type) {
+ const MockOriginData data[] = {
+ { "http://foo.com/", type, 10 },
+ { "http://foo.com:1/", type, 20 },
+ };
+ MockStorageClient* client = CreateClient(data, ARRAYSIZE_UNSAFE(data),
+ QuotaClient::kFileSystem);
+ RegisterClient(client);
+
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), type);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(10 + 20, usage());
+
+ client->ModifyOriginAndNotify(GURL("http://foo.com/"), type, 30);
+ client->ModifyOriginAndNotify(GURL("http://foo.com:1/"), type, -5);
+ client->AddOriginAndNotify(GURL("https://foo.com/"), type, 1);
+
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), type);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(10 + 20 + 30 - 5 + 1, usage());
+ int foo_usage = usage();
+
+ client->AddOriginAndNotify(GURL("http://bar.com/"), type, 40);
+ GetUsageAndQuotaForWebApps(GURL("http://bar.com/"), type);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(40, usage());
+
+ GetGlobalUsage(type);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(foo_usage + 40, usage());
+ EXPECT_EQ(0, unlimited_usage());
+}
+
+TEST_F(QuotaManagerTest, GetTemporaryUsage_WithModify) {
+ GetUsage_WithModifyTestBody(kTemp);
+}
+
+TEST_F(QuotaManagerTest, GetTemporaryUsageAndQuota_WithAdditionalTasks) {
+ static const MockOriginData kData[] = {
+ { "http://foo.com/", kTemp, 10 },
+ { "http://foo.com:8080/", kTemp, 20 },
+ { "http://bar.com/", kTemp, 13 },
+ { "http://foo.com/", kPerm, 40 },
+ };
+ RegisterClient(CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem));
+ SetTemporaryGlobalQuota(100);
+ base::RunLoop().RunUntilIdle();
+
+ const int kPerHostQuota = 100 / QuotaManager::kPerHostTemporaryPortion;
+
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kTemp);
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kTemp);
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(10 + 20, usage());
+ EXPECT_EQ(kPerHostQuota, quota());
+
+ set_additional_callback_count(0);
+ RunAdditionalUsageAndQuotaTask(GURL("http://foo.com/"),
+ kTemp);
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kTemp);
+ RunAdditionalUsageAndQuotaTask(GURL("http://bar.com/"), kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(10 + 20, usage());
+ EXPECT_EQ(kPerHostQuota, quota());
+ EXPECT_EQ(2, additional_callback_count());
+}
+
+TEST_F(QuotaManagerTest, GetTemporaryUsageAndQuota_NukeManager) {
+ static const MockOriginData kData[] = {
+ { "http://foo.com/", kTemp, 10 },
+ { "http://foo.com:8080/", kTemp, 20 },
+ { "http://bar.com/", kTemp, 13 },
+ { "http://foo.com/", kPerm, 40 },
+ };
+ RegisterClient(CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem));
+ SetTemporaryGlobalQuota(100);
+ base::RunLoop().RunUntilIdle();
+
+ set_additional_callback_count(0);
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kTemp);
+ RunAdditionalUsageAndQuotaTask(GURL("http://foo.com/"),
+ kTemp);
+ RunAdditionalUsageAndQuotaTask(GURL("http://bar.com/"),
+ kTemp);
+
+ DeleteOriginData(GURL("http://foo.com/"), kTemp, kAllClients);
+ DeleteOriginData(GURL("http://bar.com/"), kTemp, kAllClients);
+
+ // Nuke before waiting for callbacks.
+ set_quota_manager(NULL);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaErrorAbort, status());
+}
+
+TEST_F(QuotaManagerTest, GetTemporaryUsageAndQuota_Overbudget) {
+ static const MockOriginData kData[] = {
+ { "http://usage1/", kTemp, 1 },
+ { "http://usage10/", kTemp, 10 },
+ { "http://usage200/", kTemp, 200 },
+ };
+ RegisterClient(CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem));
+ SetTemporaryGlobalQuota(100);
+ base::RunLoop().RunUntilIdle();
+
+ const int kPerHostQuota = 100 / QuotaManager::kPerHostTemporaryPortion;
+
+ GetUsageAndQuotaForWebApps(GURL("http://usage1/"), kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(1, usage());
+ EXPECT_EQ(1, quota()); // should be clamped to our current usage
+
+ GetUsageAndQuotaForWebApps(GURL("http://usage10/"), kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(10, usage());
+ EXPECT_EQ(10, quota());
+
+ GetUsageAndQuotaForWebApps(GURL("http://usage200/"), kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(200, usage());
+ EXPECT_EQ(kPerHostQuota, quota()); // should be clamped to the nominal quota
+}
+
+TEST_F(QuotaManagerTest, GetTemporaryUsageAndQuota_Unlimited) {
+ static const MockOriginData kData[] = {
+ { "http://usage10/", kTemp, 10 },
+ { "http://usage50/", kTemp, 50 },
+ { "http://unlimited/", kTemp, 4000 },
+ };
+ mock_special_storage_policy()->AddUnlimited(GURL("http://unlimited/"));
+ MockStorageClient* client = CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem);
+ RegisterClient(client);
+
+ // Test when not overbugdet.
+ SetTemporaryGlobalQuota(1000);
+ base::RunLoop().RunUntilIdle();
+
+ GetGlobalUsage(kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(10 + 50 + 4000, usage());
+ EXPECT_EQ(4000, unlimited_usage());
+
+ const int kPerHostQuotaFor1000 =
+ 1000 / QuotaManager::kPerHostTemporaryPortion;
+
+ GetUsageAndQuotaForWebApps(GURL("http://usage10/"), kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(10, usage());
+ EXPECT_EQ(kPerHostQuotaFor1000, quota());
+
+ GetUsageAndQuotaForWebApps(GURL("http://usage50/"), kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(50, usage());
+ EXPECT_EQ(kPerHostQuotaFor1000, quota());
+
+ GetUsageAndQuotaForWebApps(GURL("http://unlimited/"), kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(4000, usage());
+ EXPECT_EQ(kAvailableSpaceForApp + usage(), quota());
+
+ GetUsageAndQuotaForStorageClient(GURL("http://unlimited/"), kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(0, usage());
+ EXPECT_EQ(QuotaManager::kNoLimit, quota());
+
+ // Test when overbugdet.
+ SetTemporaryGlobalQuota(100);
+ base::RunLoop().RunUntilIdle();
+
+ const int kPerHostQuotaFor100 =
+ 100 / QuotaManager::kPerHostTemporaryPortion;
+
+ GetUsageAndQuotaForWebApps(GURL("http://usage10/"), kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(10, usage());
+ EXPECT_EQ(kPerHostQuotaFor100, quota());
+
+ GetUsageAndQuotaForWebApps(GURL("http://usage50/"), kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(50, usage());
+ EXPECT_EQ(kPerHostQuotaFor100, quota());
+
+ GetUsageAndQuotaForWebApps(GURL("http://unlimited/"), kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(4000, usage());
+ EXPECT_EQ(kAvailableSpaceForApp + usage(), quota());
+
+ GetUsageAndQuotaForStorageClient(GURL("http://unlimited/"), kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(0, usage());
+ EXPECT_EQ(QuotaManager::kNoLimit, quota());
+
+ // Revoke the unlimited rights and make sure the change is noticed.
+ mock_special_storage_policy()->Reset();
+ mock_special_storage_policy()->NotifyCleared();
+
+ GetGlobalUsage(kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(10 + 50 + 4000, usage());
+ EXPECT_EQ(0, unlimited_usage());
+
+ GetUsageAndQuotaForWebApps(GURL("http://usage10/"), kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(10, usage());
+ EXPECT_EQ(10, quota()); // should be clamped to our current usage
+
+ GetUsageAndQuotaForWebApps(GURL("http://usage50/"), kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(50, usage());
+ EXPECT_EQ(kPerHostQuotaFor100, quota());
+
+ GetUsageAndQuotaForWebApps(GURL("http://unlimited/"), kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(4000, usage());
+ EXPECT_EQ(kPerHostQuotaFor100, quota());
+
+ GetUsageAndQuotaForStorageClient(GURL("http://unlimited/"), kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(4000, usage());
+ EXPECT_EQ(kPerHostQuotaFor100, quota());
+}
+
+TEST_F(QuotaManagerTest, OriginInUse) {
+ const GURL kFooOrigin("http://foo.com/");
+ const GURL kBarOrigin("http://bar.com/");
+
+ EXPECT_FALSE(quota_manager()->IsOriginInUse(kFooOrigin));
+ quota_manager()->NotifyOriginInUse(kFooOrigin); // count of 1
+ EXPECT_TRUE(quota_manager()->IsOriginInUse(kFooOrigin));
+ quota_manager()->NotifyOriginInUse(kFooOrigin); // count of 2
+ EXPECT_TRUE(quota_manager()->IsOriginInUse(kFooOrigin));
+ quota_manager()->NotifyOriginNoLongerInUse(kFooOrigin); // count of 1
+ EXPECT_TRUE(quota_manager()->IsOriginInUse(kFooOrigin));
+
+ EXPECT_FALSE(quota_manager()->IsOriginInUse(kBarOrigin));
+ quota_manager()->NotifyOriginInUse(kBarOrigin);
+ EXPECT_TRUE(quota_manager()->IsOriginInUse(kBarOrigin));
+ quota_manager()->NotifyOriginNoLongerInUse(kBarOrigin);
+ EXPECT_FALSE(quota_manager()->IsOriginInUse(kBarOrigin));
+
+ quota_manager()->NotifyOriginNoLongerInUse(kFooOrigin);
+ EXPECT_FALSE(quota_manager()->IsOriginInUse(kFooOrigin));
+}
+
+TEST_F(QuotaManagerTest, GetAndSetPerststentHostQuota) {
+ RegisterClient(CreateClient(NULL, 0, QuotaClient::kFileSystem));
+
+ GetPersistentHostQuota("foo.com");
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, quota());
+
+ SetPersistentHostQuota("foo.com", 100);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(100, quota());
+
+ GetPersistentHostQuota("foo.com");
+ SetPersistentHostQuota("foo.com", 200);
+ GetPersistentHostQuota("foo.com");
+ SetPersistentHostQuota("foo.com", QuotaManager::kPerHostPersistentQuotaLimit);
+ GetPersistentHostQuota("foo.com");
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(QuotaManager::kPerHostPersistentQuotaLimit, quota());
+
+ // Persistent quota should be capped at the per-host quota limit.
+ SetPersistentHostQuota("foo.com",
+ QuotaManager::kPerHostPersistentQuotaLimit + 100);
+ GetPersistentHostQuota("foo.com");
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(QuotaManager::kPerHostPersistentQuotaLimit, quota());
+}
+
+TEST_F(QuotaManagerTest, GetAndSetPersistentUsageAndQuota) {
+ RegisterClient(CreateClient(NULL, 0, QuotaClient::kFileSystem));
+
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(0, usage());
+ EXPECT_EQ(0, quota());
+
+ SetPersistentHostQuota("foo.com", 100);
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(0, usage());
+ EXPECT_EQ(100, quota());
+
+ // For installed app GetUsageAndQuotaForWebApps returns the capped quota.
+ mock_special_storage_policy()->GrantQueryDiskSize(GURL("http://installed/"));
+ SetPersistentHostQuota("installed", kAvailableSpaceForApp + 100);
+ GetUsageAndQuotaForWebApps(GURL("http://installed/"), kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kAvailableSpaceForApp, quota());
+
+ // Ditto for unlimited apps.
+ mock_special_storage_policy()->AddUnlimited(GURL("http://unlimited/"));
+ GetUsageAndQuotaForWebApps(GURL("http://unlimited/"), kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kAvailableSpaceForApp, quota());
+
+ // GetUsageAndQuotaForStorageClient should just return 0 usage and
+ // kNoLimit quota.
+ GetUsageAndQuotaForStorageClient(GURL("http://unlimited/"), kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, usage());
+ EXPECT_EQ(QuotaManager::kNoLimit, quota());
+}
+
+TEST_F(QuotaManagerTest, GetSyncableQuota) {
+ RegisterClient(CreateClient(NULL, 0, QuotaClient::kFileSystem));
+
+ // Pre-condition check: available disk space (for testing) is less than
+ // the default quota for syncable storage.
+ EXPECT_LE(kAvailableSpaceForApp,
+ QuotaManager::kSyncableStorageDefaultHostQuota);
+
+ // For installed apps the quota manager should return
+ // kAvailableSpaceForApp as syncable quota (because of the pre-condition).
+ mock_special_storage_policy()->GrantQueryDiskSize(GURL("http://installed/"));
+ GetUsageAndQuotaForWebApps(GURL("http://installed/"), kSync);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(0, usage());
+ EXPECT_EQ(kAvailableSpaceForApp, quota());
+
+ // If it's not installed (which shouldn't happen in real case) it
+ // should just return the default host quota for syncable.
+ GetUsageAndQuotaForWebApps(GURL("http://foo/"), kSync);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(0, usage());
+ EXPECT_EQ(QuotaManager::kSyncableStorageDefaultHostQuota, quota());
+}
+
+TEST_F(QuotaManagerTest, GetPersistentUsageAndQuota_MultiOrigins) {
+ static const MockOriginData kData[] = {
+ { "http://foo.com/", kPerm, 10 },
+ { "http://foo.com:8080/", kPerm, 20 },
+ { "https://foo.com/", kPerm, 13 },
+ { "https://foo.com:8081/", kPerm, 19 },
+ { "http://bar.com/", kPerm, 5 },
+ { "https://bar.com/", kPerm, 7 },
+ { "http://baz.com/", kPerm, 30 },
+ { "http://foo.com/", kTemp, 40 },
+ };
+ RegisterClient(CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem));
+
+ SetPersistentHostQuota("foo.com", 100);
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(10 + 20 + 13 + 19, usage());
+ EXPECT_EQ(100, quota());
+}
+
+TEST_F(QuotaManagerTest, GetPersistentUsage_WithModify) {
+ GetUsage_WithModifyTestBody(kPerm);
+}
+
+TEST_F(QuotaManagerTest, GetPersistentUsageAndQuota_WithAdditionalTasks) {
+ static const MockOriginData kData[] = {
+ { "http://foo.com/", kPerm, 10 },
+ { "http://foo.com:8080/", kPerm, 20 },
+ { "http://bar.com/", kPerm, 13 },
+ { "http://foo.com/", kTemp, 40 },
+ };
+ RegisterClient(CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem));
+ SetPersistentHostQuota("foo.com", 100);
+
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kPerm);
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kPerm);
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(10 + 20, usage());
+ EXPECT_EQ(100, quota());
+
+ set_additional_callback_count(0);
+ RunAdditionalUsageAndQuotaTask(GURL("http://foo.com/"),
+ kPerm);
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kPerm);
+ RunAdditionalUsageAndQuotaTask(GURL("http://bar.com/"), kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(10 + 20, usage());
+ EXPECT_EQ(2, additional_callback_count());
+}
+
+TEST_F(QuotaManagerTest, GetPersistentUsageAndQuota_NukeManager) {
+ static const MockOriginData kData[] = {
+ { "http://foo.com/", kPerm, 10 },
+ { "http://foo.com:8080/", kPerm, 20 },
+ { "http://bar.com/", kPerm, 13 },
+ { "http://foo.com/", kTemp, 40 },
+ };
+ RegisterClient(CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem));
+ SetPersistentHostQuota("foo.com", 100);
+
+ set_additional_callback_count(0);
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kPerm);
+ RunAdditionalUsageAndQuotaTask(GURL("http://foo.com/"), kPerm);
+ RunAdditionalUsageAndQuotaTask(GURL("http://bar.com/"), kPerm);
+
+ // Nuke before waiting for callbacks.
+ set_quota_manager(NULL);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaErrorAbort, status());
+}
+
+TEST_F(QuotaManagerTest, GetUsage_Simple) {
+ static const MockOriginData kData[] = {
+ { "http://foo.com/", kPerm, 1 },
+ { "http://foo.com:1/", kPerm, 20 },
+ { "http://bar.com/", kTemp, 300 },
+ { "https://buz.com/", kTemp, 4000 },
+ { "http://buz.com/", kTemp, 50000 },
+ { "http://bar.com:1/", kPerm, 600000 },
+ { "http://foo.com/", kTemp, 7000000 },
+ };
+ RegisterClient(CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem));
+
+ GetGlobalUsage(kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(usage(), 1 + 20 + 600000);
+ EXPECT_EQ(0, unlimited_usage());
+
+ GetGlobalUsage(kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(usage(), 300 + 4000 + 50000 + 7000000);
+ EXPECT_EQ(0, unlimited_usage());
+
+ GetHostUsage("foo.com", kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(usage(), 1 + 20);
+
+ GetHostUsage("buz.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(usage(), 4000 + 50000);
+}
+
+TEST_F(QuotaManagerTest, GetUsage_WithModification) {
+ static const MockOriginData kData[] = {
+ { "http://foo.com/", kPerm, 1 },
+ { "http://foo.com:1/", kPerm, 20 },
+ { "http://bar.com/", kTemp, 300 },
+ { "https://buz.com/", kTemp, 4000 },
+ { "http://buz.com/", kTemp, 50000 },
+ { "http://bar.com:1/", kPerm, 600000 },
+ { "http://foo.com/", kTemp, 7000000 },
+ };
+
+ MockStorageClient* client = CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem);
+ RegisterClient(client);
+
+ GetGlobalUsage(kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(usage(), 1 + 20 + 600000);
+ EXPECT_EQ(0, unlimited_usage());
+
+ client->ModifyOriginAndNotify(
+ GURL("http://foo.com/"), kPerm, 80000000);
+
+ GetGlobalUsage(kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(usage(), 1 + 20 + 600000 + 80000000);
+ EXPECT_EQ(0, unlimited_usage());
+
+ GetGlobalUsage(kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(usage(), 300 + 4000 + 50000 + 7000000);
+ EXPECT_EQ(0, unlimited_usage());
+
+ client->ModifyOriginAndNotify(
+ GURL("http://foo.com/"), kTemp, 1);
+
+ GetGlobalUsage(kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(usage(), 300 + 4000 + 50000 + 7000000 + 1);
+ EXPECT_EQ(0, unlimited_usage());
+
+ GetHostUsage("buz.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(usage(), 4000 + 50000);
+
+ client->ModifyOriginAndNotify(
+ GURL("http://buz.com/"), kTemp, 900000000);
+
+ GetHostUsage("buz.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(usage(), 4000 + 50000 + 900000000);
+}
+
+TEST_F(QuotaManagerTest, GetUsage_WithDeleteOrigin) {
+ static const MockOriginData kData[] = {
+ { "http://foo.com/", kTemp, 1 },
+ { "http://foo.com:1/", kTemp, 20 },
+ { "http://foo.com/", kPerm, 300 },
+ { "http://bar.com/", kTemp, 4000 },
+ };
+ MockStorageClient* client = CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem);
+ RegisterClient(client);
+
+ GetGlobalUsage(kTemp);
+ base::RunLoop().RunUntilIdle();
+ int64 predelete_global_tmp = usage();
+
+ GetHostUsage("foo.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ int64 predelete_host_tmp = usage();
+
+ GetHostUsage("foo.com", kPerm);
+ base::RunLoop().RunUntilIdle();
+ int64 predelete_host_pers = usage();
+
+ DeleteClientOriginData(client, GURL("http://foo.com/"),
+ kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+
+ GetGlobalUsage(kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_global_tmp - 1, usage());
+
+ GetHostUsage("foo.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_host_tmp - 1, usage());
+
+ GetHostUsage("foo.com", kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_host_pers, usage());
+}
+
+TEST_F(QuotaManagerTest, GetAvailableSpaceTest) {
+ GetAvailableSpace();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_LE(0, available_space());
+}
+
+TEST_F(QuotaManagerTest, EvictOriginData) {
+ static const MockOriginData kData1[] = {
+ { "http://foo.com/", kTemp, 1 },
+ { "http://foo.com:1/", kTemp, 20 },
+ { "http://foo.com/", kPerm, 300 },
+ { "http://bar.com/", kTemp, 4000 },
+ };
+ static const MockOriginData kData2[] = {
+ { "http://foo.com/", kTemp, 50000 },
+ { "http://foo.com:1/", kTemp, 6000 },
+ { "http://foo.com/", kPerm, 700 },
+ { "https://foo.com/", kTemp, 80 },
+ { "http://bar.com/", kTemp, 9 },
+ };
+ MockStorageClient* client1 = CreateClient(kData1, ARRAYSIZE_UNSAFE(kData1),
+ QuotaClient::kFileSystem);
+ MockStorageClient* client2 = CreateClient(kData2, ARRAYSIZE_UNSAFE(kData2),
+ QuotaClient::kDatabase);
+ RegisterClient(client1);
+ RegisterClient(client2);
+
+ GetGlobalUsage(kTemp);
+ base::RunLoop().RunUntilIdle();
+ int64 predelete_global_tmp = usage();
+
+ GetHostUsage("foo.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ int64 predelete_host_tmp = usage();
+
+ GetHostUsage("foo.com", kPerm);
+ base::RunLoop().RunUntilIdle();
+ int64 predelete_host_pers = usage();
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kData1); ++i)
+ quota_manager()->NotifyStorageAccessed(QuotaClient::kUnknown,
+ GURL(kData1[i].origin), kData1[i].type);
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kData2); ++i)
+ quota_manager()->NotifyStorageAccessed(QuotaClient::kUnknown,
+ GURL(kData2[i].origin), kData2[i].type);
+ base::RunLoop().RunUntilIdle();
+
+ EvictOriginData(GURL("http://foo.com/"), kTemp);
+ base::RunLoop().RunUntilIdle();
+
+ DumpOriginInfoTable();
+ base::RunLoop().RunUntilIdle();
+
+ typedef OriginInfoTableEntries::const_iterator iterator;
+ for (iterator itr(origin_info_entries().begin()),
+ end(origin_info_entries().end());
+ itr != end; ++itr) {
+ if (itr->type == kTemp)
+ EXPECT_NE(std::string("http://foo.com/"), itr->origin.spec());
+ }
+
+ GetGlobalUsage(kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_global_tmp - (1 + 50000), usage());
+
+ GetHostUsage("foo.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_host_tmp - (1 + 50000), usage());
+
+ GetHostUsage("foo.com", kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_host_pers, usage());
+}
+
+TEST_F(QuotaManagerTest, EvictOriginDataWithDeletionError) {
+ static const MockOriginData kData[] = {
+ { "http://foo.com/", kTemp, 1 },
+ { "http://foo.com:1/", kTemp, 20 },
+ { "http://foo.com/", kPerm, 300 },
+ { "http://bar.com/", kTemp, 4000 },
+ };
+ static const int kNumberOfTemporaryOrigins = 3;
+ MockStorageClient* client = CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem);
+ RegisterClient(client);
+
+ GetGlobalUsage(kTemp);
+ base::RunLoop().RunUntilIdle();
+ int64 predelete_global_tmp = usage();
+
+ GetHostUsage("foo.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ int64 predelete_host_tmp = usage();
+
+ GetHostUsage("foo.com", kPerm);
+ base::RunLoop().RunUntilIdle();
+ int64 predelete_host_pers = usage();
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kData); ++i)
+ NotifyStorageAccessed(client, GURL(kData[i].origin), kData[i].type);
+ base::RunLoop().RunUntilIdle();
+
+ client->AddOriginToErrorSet(GURL("http://foo.com/"), kTemp);
+
+ for (int i = 0;
+ i < QuotaManager::kThresholdOfErrorsToBeBlacklisted + 1;
+ ++i) {
+ EvictOriginData(GURL("http://foo.com/"), kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaErrorInvalidModification, status());
+ }
+
+ DumpOriginInfoTable();
+ base::RunLoop().RunUntilIdle();
+
+ bool found_origin_in_database = false;
+ typedef OriginInfoTableEntries::const_iterator iterator;
+ for (iterator itr(origin_info_entries().begin()),
+ end(origin_info_entries().end());
+ itr != end; ++itr) {
+ if (itr->type == kTemp &&
+ GURL("http://foo.com/") == itr->origin) {
+ found_origin_in_database = true;
+ break;
+ }
+ }
+ // The origin "http://foo.com/" should be in the database.
+ EXPECT_TRUE(found_origin_in_database);
+
+ for (size_t i = 0; i < kNumberOfTemporaryOrigins - 1; ++i) {
+ GetLRUOrigin(kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(lru_origin().is_empty());
+ // The origin "http://foo.com/" should not be in the LRU list.
+ EXPECT_NE(std::string("http://foo.com/"), lru_origin().spec());
+ DeleteOriginFromDatabase(lru_origin(), kTemp);
+ base::RunLoop().RunUntilIdle();
+ }
+
+ // Now the LRU list must be empty.
+ GetLRUOrigin(kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(lru_origin().is_empty());
+
+ // Deleting origins from the database should not affect the results of the
+ // following checks.
+ GetGlobalUsage(kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_global_tmp, usage());
+
+ GetHostUsage("foo.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_host_tmp, usage());
+
+ GetHostUsage("foo.com", kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_host_pers, usage());
+}
+
+TEST_F(QuotaManagerTest, GetUsageAndQuotaForEviction) {
+ static const MockOriginData kData[] = {
+ { "http://foo.com/", kTemp, 1 },
+ { "http://foo.com:1/", kTemp, 20 },
+ { "http://foo.com/", kPerm, 300 },
+ { "http://unlimited/", kTemp, 4000 },
+ };
+
+ mock_special_storage_policy()->AddUnlimited(GURL("http://unlimited/"));
+ MockStorageClient* client = CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem);
+ RegisterClient(client);
+
+ SetTemporaryGlobalQuota(10000000);
+ base::RunLoop().RunUntilIdle();
+
+ GetUsageAndQuotaForEviction();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(21, limited_usage());
+ EXPECT_EQ(10000000, quota());
+ EXPECT_LE(0, available_space());
+}
+
+TEST_F(QuotaManagerTest, DeleteHostDataSimple) {
+ static const MockOriginData kData[] = {
+ { "http://foo.com/", kTemp, 1 },
+ };
+ MockStorageClient* client = CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem);
+ RegisterClient(client);
+
+ GetGlobalUsage(kTemp);
+ base::RunLoop().RunUntilIdle();
+ const int64 predelete_global_tmp = usage();
+
+ GetHostUsage("foo.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ int64 predelete_host_tmp = usage();
+
+ GetHostUsage("foo.com", kPerm);
+ base::RunLoop().RunUntilIdle();
+ int64 predelete_host_pers = usage();
+
+ DeleteHostData(std::string(), kTemp, kAllClients);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+
+ GetGlobalUsage(kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_global_tmp, usage());
+
+ GetHostUsage("foo.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_host_tmp, usage());
+
+ GetHostUsage("foo.com", kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_host_pers, usage());
+
+ DeleteHostData("foo.com", kTemp, kAllClients);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+
+ GetGlobalUsage(kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_global_tmp - 1, usage());
+
+ GetHostUsage("foo.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_host_tmp - 1, usage());
+
+ GetHostUsage("foo.com", kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_host_pers, usage());
+}
+
+TEST_F(QuotaManagerTest, DeleteHostDataMultiple) {
+ static const MockOriginData kData1[] = {
+ { "http://foo.com/", kTemp, 1 },
+ { "http://foo.com:1/", kTemp, 20 },
+ { "http://foo.com/", kPerm, 300 },
+ { "http://bar.com/", kTemp, 4000 },
+ };
+ static const MockOriginData kData2[] = {
+ { "http://foo.com/", kTemp, 50000 },
+ { "http://foo.com:1/", kTemp, 6000 },
+ { "http://foo.com/", kPerm, 700 },
+ { "https://foo.com/", kTemp, 80 },
+ { "http://bar.com/", kTemp, 9 },
+ };
+ MockStorageClient* client1 = CreateClient(kData1, ARRAYSIZE_UNSAFE(kData1),
+ QuotaClient::kFileSystem);
+ MockStorageClient* client2 = CreateClient(kData2, ARRAYSIZE_UNSAFE(kData2),
+ QuotaClient::kDatabase);
+ RegisterClient(client1);
+ RegisterClient(client2);
+
+ GetGlobalUsage(kTemp);
+ base::RunLoop().RunUntilIdle();
+ const int64 predelete_global_tmp = usage();
+
+ GetHostUsage("foo.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ const int64 predelete_foo_tmp = usage();
+
+ GetHostUsage("bar.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ const int64 predelete_bar_tmp = usage();
+
+ GetHostUsage("foo.com", kPerm);
+ base::RunLoop().RunUntilIdle();
+ const int64 predelete_foo_pers = usage();
+
+ GetHostUsage("bar.com", kPerm);
+ base::RunLoop().RunUntilIdle();
+ const int64 predelete_bar_pers = usage();
+
+ reset_status_callback_count();
+ DeleteHostData("foo.com", kTemp, kAllClients);
+ DeleteHostData("bar.com", kTemp, kAllClients);
+ DeleteHostData("foo.com", kTemp, kAllClients);
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(3, status_callback_count());
+
+ DumpOriginInfoTable();
+ base::RunLoop().RunUntilIdle();
+
+ typedef OriginInfoTableEntries::const_iterator iterator;
+ for (iterator itr(origin_info_entries().begin()),
+ end(origin_info_entries().end());
+ itr != end; ++itr) {
+ if (itr->type == kTemp) {
+ EXPECT_NE(std::string("http://foo.com/"), itr->origin.spec());
+ EXPECT_NE(std::string("http://foo.com:1/"), itr->origin.spec());
+ EXPECT_NE(std::string("https://foo.com/"), itr->origin.spec());
+ EXPECT_NE(std::string("http://bar.com/"), itr->origin.spec());
+ }
+ }
+
+ GetGlobalUsage(kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_global_tmp - (1 + 20 + 4000 + 50000 + 6000 + 80 + 9),
+ usage());
+
+ GetHostUsage("foo.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_foo_tmp - (1 + 20 + 50000 + 6000 + 80), usage());
+
+ GetHostUsage("bar.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_bar_tmp - (4000 + 9), usage());
+
+ GetHostUsage("foo.com", kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_foo_pers, usage());
+
+ GetHostUsage("bar.com", kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_bar_pers, usage());
+}
+
+// Single-run DeleteOriginData cases must be well covered by
+// EvictOriginData tests.
+TEST_F(QuotaManagerTest, DeleteOriginDataMultiple) {
+ static const MockOriginData kData1[] = {
+ { "http://foo.com/", kTemp, 1 },
+ { "http://foo.com:1/", kTemp, 20 },
+ { "http://foo.com/", kPerm, 300 },
+ { "http://bar.com/", kTemp, 4000 },
+ };
+ static const MockOriginData kData2[] = {
+ { "http://foo.com/", kTemp, 50000 },
+ { "http://foo.com:1/", kTemp, 6000 },
+ { "http://foo.com/", kPerm, 700 },
+ { "https://foo.com/", kTemp, 80 },
+ { "http://bar.com/", kTemp, 9 },
+ };
+ MockStorageClient* client1 = CreateClient(kData1, ARRAYSIZE_UNSAFE(kData1),
+ QuotaClient::kFileSystem);
+ MockStorageClient* client2 = CreateClient(kData2, ARRAYSIZE_UNSAFE(kData2),
+ QuotaClient::kDatabase);
+ RegisterClient(client1);
+ RegisterClient(client2);
+
+ GetGlobalUsage(kTemp);
+ base::RunLoop().RunUntilIdle();
+ const int64 predelete_global_tmp = usage();
+
+ GetHostUsage("foo.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ const int64 predelete_foo_tmp = usage();
+
+ GetHostUsage("bar.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ const int64 predelete_bar_tmp = usage();
+
+ GetHostUsage("foo.com", kPerm);
+ base::RunLoop().RunUntilIdle();
+ const int64 predelete_foo_pers = usage();
+
+ GetHostUsage("bar.com", kPerm);
+ base::RunLoop().RunUntilIdle();
+ const int64 predelete_bar_pers = usage();
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kData1); ++i)
+ quota_manager()->NotifyStorageAccessed(QuotaClient::kUnknown,
+ GURL(kData1[i].origin), kData1[i].type);
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kData2); ++i)
+ quota_manager()->NotifyStorageAccessed(QuotaClient::kUnknown,
+ GURL(kData2[i].origin), kData2[i].type);
+ base::RunLoop().RunUntilIdle();
+
+ reset_status_callback_count();
+ DeleteOriginData(GURL("http://foo.com/"), kTemp, kAllClients);
+ DeleteOriginData(GURL("http://bar.com/"), kTemp, kAllClients);
+ DeleteOriginData(GURL("http://foo.com/"), kTemp, kAllClients);
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(3, status_callback_count());
+
+ DumpOriginInfoTable();
+ base::RunLoop().RunUntilIdle();
+
+ typedef OriginInfoTableEntries::const_iterator iterator;
+ for (iterator itr(origin_info_entries().begin()),
+ end(origin_info_entries().end());
+ itr != end; ++itr) {
+ if (itr->type == kTemp) {
+ EXPECT_NE(std::string("http://foo.com/"), itr->origin.spec());
+ EXPECT_NE(std::string("http://bar.com/"), itr->origin.spec());
+ }
+ }
+
+ GetGlobalUsage(kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_global_tmp - (1 + 4000 + 50000 + 9), usage());
+
+ GetHostUsage("foo.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_foo_tmp - (1 + 50000), usage());
+
+ GetHostUsage("bar.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_bar_tmp - (4000 + 9), usage());
+
+ GetHostUsage("foo.com", kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_foo_pers, usage());
+
+ GetHostUsage("bar.com", kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_bar_pers, usage());
+}
+
+TEST_F(QuotaManagerTest, GetCachedOrigins) {
+ static const MockOriginData kData[] = {
+ { "http://a.com/", kTemp, 1 },
+ { "http://a.com:1/", kTemp, 20 },
+ { "http://b.com/", kPerm, 300 },
+ { "http://c.com/", kTemp, 4000 },
+ };
+ MockStorageClient* client = CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem);
+ RegisterClient(client);
+
+ // TODO(kinuko): Be careful when we add cache pruner.
+
+ std::set<GURL> origins;
+ GetCachedOrigins(kTemp, &origins);
+ EXPECT_TRUE(origins.empty());
+
+ // No matter how we make queries the quota manager tries to cache all
+ // the origins at startup.
+ GetHostUsage("a.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ GetCachedOrigins(kTemp, &origins);
+ EXPECT_EQ(3U, origins.size());
+
+ GetHostUsage("b.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ GetCachedOrigins(kTemp, &origins);
+ EXPECT_EQ(3U, origins.size());
+
+ GetCachedOrigins(kPerm, &origins);
+ EXPECT_TRUE(origins.empty());
+
+ GetGlobalUsage(kTemp);
+ base::RunLoop().RunUntilIdle();
+ GetCachedOrigins(kTemp, &origins);
+ EXPECT_EQ(3U, origins.size());
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kData); ++i) {
+ if (kData[i].type == kTemp)
+ EXPECT_TRUE(origins.find(GURL(kData[i].origin)) != origins.end());
+ }
+}
+
+TEST_F(QuotaManagerTest, NotifyAndLRUOrigin) {
+ static const MockOriginData kData[] = {
+ { "http://a.com/", kTemp, 0 },
+ { "http://a.com:1/", kTemp, 0 },
+ { "https://a.com/", kTemp, 0 },
+ { "http://b.com/", kPerm, 0 }, // persistent
+ { "http://c.com/", kTemp, 0 },
+ };
+ MockStorageClient* client = CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem);
+ RegisterClient(client);
+
+ GURL origin;
+ GetLRUOrigin(kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(lru_origin().is_empty());
+
+ NotifyStorageAccessed(client, GURL("http://a.com/"), kTemp);
+ GetLRUOrigin(kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ("http://a.com/", lru_origin().spec());
+
+ NotifyStorageAccessed(client, GURL("http://b.com/"), kPerm);
+ NotifyStorageAccessed(client, GURL("https://a.com/"), kTemp);
+ NotifyStorageAccessed(client, GURL("http://c.com/"), kTemp);
+ GetLRUOrigin(kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ("http://a.com/", lru_origin().spec());
+
+ DeleteOriginFromDatabase(lru_origin(), kTemp);
+ GetLRUOrigin(kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ("https://a.com/", lru_origin().spec());
+
+ DeleteOriginFromDatabase(lru_origin(), kTemp);
+ GetLRUOrigin(kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ("http://c.com/", lru_origin().spec());
+}
+
+TEST_F(QuotaManagerTest, GetLRUOriginWithOriginInUse) {
+ static const MockOriginData kData[] = {
+ { "http://a.com/", kTemp, 0 },
+ { "http://a.com:1/", kTemp, 0 },
+ { "https://a.com/", kTemp, 0 },
+ { "http://b.com/", kPerm, 0 }, // persistent
+ { "http://c.com/", kTemp, 0 },
+ };
+ MockStorageClient* client = CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem);
+ RegisterClient(client);
+
+ GURL origin;
+ GetLRUOrigin(kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(lru_origin().is_empty());
+
+ NotifyStorageAccessed(client, GURL("http://a.com/"), kTemp);
+ NotifyStorageAccessed(client, GURL("http://b.com/"), kPerm);
+ NotifyStorageAccessed(client, GURL("https://a.com/"), kTemp);
+ NotifyStorageAccessed(client, GURL("http://c.com/"), kTemp);
+
+ GetLRUOrigin(kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ("http://a.com/", lru_origin().spec());
+
+ // Notify origin http://a.com is in use.
+ NotifyOriginInUse(GURL("http://a.com/"));
+ GetLRUOrigin(kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ("https://a.com/", lru_origin().spec());
+
+ // Notify origin https://a.com is in use while GetLRUOrigin is running.
+ GetLRUOrigin(kTemp);
+ NotifyOriginInUse(GURL("https://a.com/"));
+ base::RunLoop().RunUntilIdle();
+ // Post-filtering must have excluded the returned origin, so we will
+ // see empty result here.
+ EXPECT_TRUE(lru_origin().is_empty());
+
+ // Notify access for http://c.com while GetLRUOrigin is running.
+ GetLRUOrigin(kTemp);
+ NotifyStorageAccessed(client, GURL("http://c.com/"), kTemp);
+ base::RunLoop().RunUntilIdle();
+ // Post-filtering must have excluded the returned origin, so we will
+ // see empty result here.
+ EXPECT_TRUE(lru_origin().is_empty());
+
+ NotifyOriginNoLongerInUse(GURL("http://a.com/"));
+ NotifyOriginNoLongerInUse(GURL("https://a.com/"));
+ GetLRUOrigin(kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ("http://a.com/", lru_origin().spec());
+}
+
+TEST_F(QuotaManagerTest, GetOriginsModifiedSince) {
+ static const MockOriginData kData[] = {
+ { "http://a.com/", kTemp, 0 },
+ { "http://a.com:1/", kTemp, 0 },
+ { "https://a.com/", kTemp, 0 },
+ { "http://b.com/", kPerm, 0 }, // persistent
+ { "http://c.com/", kTemp, 0 },
+ };
+ MockStorageClient* client = CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem);
+ RegisterClient(client);
+
+ GetOriginsModifiedSince(kTemp, base::Time());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(modified_origins().empty());
+ EXPECT_EQ(modified_origins_type(), kTemp);
+
+ base::Time time1 = client->IncrementMockTime();
+ client->ModifyOriginAndNotify(GURL("http://a.com/"), kTemp, 10);
+ client->ModifyOriginAndNotify(GURL("http://a.com:1/"), kTemp, 10);
+ client->ModifyOriginAndNotify(GURL("http://b.com/"), kPerm, 10);
+ base::Time time2 = client->IncrementMockTime();
+ client->ModifyOriginAndNotify(GURL("https://a.com/"), kTemp, 10);
+ client->ModifyOriginAndNotify(GURL("http://c.com/"), kTemp, 10);
+ base::Time time3 = client->IncrementMockTime();
+
+ GetOriginsModifiedSince(kTemp, time1);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(4U, modified_origins().size());
+ EXPECT_EQ(modified_origins_type(), kTemp);
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kData); ++i) {
+ if (kData[i].type == kTemp)
+ EXPECT_EQ(1U, modified_origins().count(GURL(kData[i].origin)));
+ }
+
+ GetOriginsModifiedSince(kTemp, time2);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(2U, modified_origins().size());
+
+ GetOriginsModifiedSince(kTemp, time3);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(modified_origins().empty());
+ EXPECT_EQ(modified_origins_type(), kTemp);
+
+ client->ModifyOriginAndNotify(GURL("http://a.com/"), kTemp, 10);
+
+ GetOriginsModifiedSince(kTemp, time3);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(1U, modified_origins().size());
+ EXPECT_EQ(1U, modified_origins().count(GURL("http://a.com/")));
+ EXPECT_EQ(modified_origins_type(), kTemp);
+}
+
+TEST_F(QuotaManagerTest, DumpQuotaTable) {
+ SetPersistentHostQuota("example1.com", 1);
+ SetPersistentHostQuota("example2.com", 20);
+ SetPersistentHostQuota("example3.com", 300);
+ base::RunLoop().RunUntilIdle();
+
+ DumpQuotaTable();
+ base::RunLoop().RunUntilIdle();
+
+ const QuotaTableEntry kEntries[] = {
+ QuotaTableEntry("example1.com", kPerm, 1),
+ QuotaTableEntry("example2.com", kPerm, 20),
+ QuotaTableEntry("example3.com", kPerm, 300),
+ };
+ std::set<QuotaTableEntry> entries
+ (kEntries, kEntries + ARRAYSIZE_UNSAFE(kEntries));
+
+ typedef QuotaTableEntries::const_iterator iterator;
+ for (iterator itr(quota_entries().begin()), end(quota_entries().end());
+ itr != end; ++itr) {
+ SCOPED_TRACE(testing::Message()
+ << "host = " << itr->host << ", "
+ << "quota = " << itr->quota);
+ EXPECT_EQ(1u, entries.erase(*itr));
+ }
+ EXPECT_TRUE(entries.empty());
+}
+
+TEST_F(QuotaManagerTest, DumpOriginInfoTable) {
+ using std::make_pair;
+
+ quota_manager()->NotifyStorageAccessed(
+ QuotaClient::kUnknown,
+ GURL("http://example.com/"),
+ kTemp);
+ quota_manager()->NotifyStorageAccessed(
+ QuotaClient::kUnknown,
+ GURL("http://example.com/"),
+ kPerm);
+ quota_manager()->NotifyStorageAccessed(
+ QuotaClient::kUnknown,
+ GURL("http://example.com/"),
+ kPerm);
+ base::RunLoop().RunUntilIdle();
+
+ DumpOriginInfoTable();
+ base::RunLoop().RunUntilIdle();
+
+ typedef std::pair<GURL, StorageType> TypedOrigin;
+ typedef std::pair<TypedOrigin, int> Entry;
+ const Entry kEntries[] = {
+ make_pair(make_pair(GURL("http://example.com/"), kTemp), 1),
+ make_pair(make_pair(GURL("http://example.com/"), kPerm), 2),
+ };
+ std::set<Entry> entries
+ (kEntries, kEntries + ARRAYSIZE_UNSAFE(kEntries));
+
+ typedef OriginInfoTableEntries::const_iterator iterator;
+ for (iterator itr(origin_info_entries().begin()),
+ end(origin_info_entries().end());
+ itr != end; ++itr) {
+ SCOPED_TRACE(testing::Message()
+ << "host = " << itr->origin << ", "
+ << "type = " << itr->type << ", "
+ << "used_count = " << itr->used_count);
+ EXPECT_EQ(1u, entries.erase(
+ make_pair(make_pair(itr->origin, itr->type),
+ itr->used_count)));
+ }
+ EXPECT_TRUE(entries.empty());
+}
+
+TEST_F(QuotaManagerTest, QuotaForEmptyHost) {
+ GetPersistentHostQuota(std::string());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(0, quota());
+
+ SetPersistentHostQuota(std::string(), 10);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaErrorNotSupported, status());
+}
+
+TEST_F(QuotaManagerTest, DeleteSpecificClientTypeSingleOrigin) {
+ static const MockOriginData kData1[] = {
+ { "http://foo.com/", kTemp, 1 },
+ };
+ static const MockOriginData kData2[] = {
+ { "http://foo.com/", kTemp, 2 },
+ };
+ static const MockOriginData kData3[] = {
+ { "http://foo.com/", kTemp, 4 },
+ };
+ static const MockOriginData kData4[] = {
+ { "http://foo.com/", kTemp, 8 },
+ };
+ MockStorageClient* client1 = CreateClient(kData1, ARRAYSIZE_UNSAFE(kData1),
+ QuotaClient::kFileSystem);
+ MockStorageClient* client2 = CreateClient(kData2, ARRAYSIZE_UNSAFE(kData2),
+ QuotaClient::kAppcache);
+ MockStorageClient* client3 = CreateClient(kData3, ARRAYSIZE_UNSAFE(kData3),
+ QuotaClient::kDatabase);
+ MockStorageClient* client4 = CreateClient(kData4, ARRAYSIZE_UNSAFE(kData4),
+ QuotaClient::kIndexedDatabase);
+ RegisterClient(client1);
+ RegisterClient(client2);
+ RegisterClient(client3);
+ RegisterClient(client4);
+
+ GetHostUsage("foo.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ const int64 predelete_foo_tmp = usage();
+
+ DeleteOriginData(GURL("http://foo.com/"), kTemp, QuotaClient::kFileSystem);
+ base::RunLoop().RunUntilIdle();
+ GetHostUsage("foo.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_foo_tmp - 1, usage());
+
+ DeleteOriginData(GURL("http://foo.com/"), kTemp, QuotaClient::kAppcache);
+ base::RunLoop().RunUntilIdle();
+ GetHostUsage("foo.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_foo_tmp - 2 - 1, usage());
+
+ DeleteOriginData(GURL("http://foo.com/"), kTemp, QuotaClient::kDatabase);
+ base::RunLoop().RunUntilIdle();
+ GetHostUsage("foo.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_foo_tmp - 4 - 2 - 1, usage());
+
+ DeleteOriginData(GURL("http://foo.com/"), kTemp,
+ QuotaClient::kIndexedDatabase);
+ base::RunLoop().RunUntilIdle();
+ GetHostUsage("foo.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_foo_tmp - 8 - 4 - 2 - 1, usage());
+}
+
+TEST_F(QuotaManagerTest, DeleteSpecificClientTypeSingleHost) {
+ static const MockOriginData kData1[] = {
+ { "http://foo.com:1111/", kTemp, 1 },
+ };
+ static const MockOriginData kData2[] = {
+ { "http://foo.com:2222/", kTemp, 2 },
+ };
+ static const MockOriginData kData3[] = {
+ { "http://foo.com:3333/", kTemp, 4 },
+ };
+ static const MockOriginData kData4[] = {
+ { "http://foo.com:4444/", kTemp, 8 },
+ };
+ MockStorageClient* client1 = CreateClient(kData1, ARRAYSIZE_UNSAFE(kData1),
+ QuotaClient::kFileSystem);
+ MockStorageClient* client2 = CreateClient(kData2, ARRAYSIZE_UNSAFE(kData2),
+ QuotaClient::kAppcache);
+ MockStorageClient* client3 = CreateClient(kData3, ARRAYSIZE_UNSAFE(kData3),
+ QuotaClient::kDatabase);
+ MockStorageClient* client4 = CreateClient(kData4, ARRAYSIZE_UNSAFE(kData4),
+ QuotaClient::kIndexedDatabase);
+ RegisterClient(client1);
+ RegisterClient(client2);
+ RegisterClient(client3);
+ RegisterClient(client4);
+
+ GetHostUsage("foo.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ const int64 predelete_foo_tmp = usage();
+
+ DeleteHostData("foo.com", kTemp, QuotaClient::kFileSystem);
+ base::RunLoop().RunUntilIdle();
+ GetHostUsage("foo.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_foo_tmp - 1, usage());
+
+ DeleteHostData("foo.com", kTemp, QuotaClient::kAppcache);
+ base::RunLoop().RunUntilIdle();
+ GetHostUsage("foo.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_foo_tmp - 2 - 1, usage());
+
+ DeleteHostData("foo.com", kTemp, QuotaClient::kDatabase);
+ base::RunLoop().RunUntilIdle();
+ GetHostUsage("foo.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_foo_tmp - 4 - 2 - 1, usage());
+
+ DeleteHostData("foo.com", kTemp, QuotaClient::kIndexedDatabase);
+ base::RunLoop().RunUntilIdle();
+ GetHostUsage("foo.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_foo_tmp - 8 - 4 - 2 - 1, usage());
+}
+
+TEST_F(QuotaManagerTest, DeleteMultipleClientTypesSingleOrigin) {
+ static const MockOriginData kData1[] = {
+ { "http://foo.com/", kTemp, 1 },
+ };
+ static const MockOriginData kData2[] = {
+ { "http://foo.com/", kTemp, 2 },
+ };
+ static const MockOriginData kData3[] = {
+ { "http://foo.com/", kTemp, 4 },
+ };
+ static const MockOriginData kData4[] = {
+ { "http://foo.com/", kTemp, 8 },
+ };
+ MockStorageClient* client1 = CreateClient(kData1, ARRAYSIZE_UNSAFE(kData1),
+ QuotaClient::kFileSystem);
+ MockStorageClient* client2 = CreateClient(kData2, ARRAYSIZE_UNSAFE(kData2),
+ QuotaClient::kAppcache);
+ MockStorageClient* client3 = CreateClient(kData3, ARRAYSIZE_UNSAFE(kData3),
+ QuotaClient::kDatabase);
+ MockStorageClient* client4 = CreateClient(kData4, ARRAYSIZE_UNSAFE(kData4),
+ QuotaClient::kIndexedDatabase);
+ RegisterClient(client1);
+ RegisterClient(client2);
+ RegisterClient(client3);
+ RegisterClient(client4);
+
+ GetHostUsage("foo.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ const int64 predelete_foo_tmp = usage();
+
+ DeleteOriginData(GURL("http://foo.com/"), kTemp,
+ QuotaClient::kFileSystem | QuotaClient::kDatabase);
+ base::RunLoop().RunUntilIdle();
+ GetHostUsage("foo.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_foo_tmp - 4 - 1, usage());
+
+ DeleteOriginData(GURL("http://foo.com/"), kTemp,
+ QuotaClient::kAppcache | QuotaClient::kIndexedDatabase);
+ base::RunLoop().RunUntilIdle();
+ GetHostUsage("foo.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_foo_tmp - 8 - 4 - 2 - 1, usage());
+}
+
+TEST_F(QuotaManagerTest, DeleteMultipleClientTypesSingleHost) {
+ static const MockOriginData kData1[] = {
+ { "http://foo.com:1111/", kTemp, 1 },
+ };
+ static const MockOriginData kData2[] = {
+ { "http://foo.com:2222/", kTemp, 2 },
+ };
+ static const MockOriginData kData3[] = {
+ { "http://foo.com:3333/", kTemp, 4 },
+ };
+ static const MockOriginData kData4[] = {
+ { "http://foo.com:4444/", kTemp, 8 },
+ };
+ MockStorageClient* client1 = CreateClient(kData1, ARRAYSIZE_UNSAFE(kData1),
+ QuotaClient::kFileSystem);
+ MockStorageClient* client2 = CreateClient(kData2, ARRAYSIZE_UNSAFE(kData2),
+ QuotaClient::kAppcache);
+ MockStorageClient* client3 = CreateClient(kData3, ARRAYSIZE_UNSAFE(kData3),
+ QuotaClient::kDatabase);
+ MockStorageClient* client4 = CreateClient(kData4, ARRAYSIZE_UNSAFE(kData4),
+ QuotaClient::kIndexedDatabase);
+ RegisterClient(client1);
+ RegisterClient(client2);
+ RegisterClient(client3);
+ RegisterClient(client4);
+
+ GetHostUsage("foo.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ const int64 predelete_foo_tmp = usage();
+
+ DeleteHostData("foo.com", kTemp,
+ QuotaClient::kFileSystem | QuotaClient::kAppcache);
+ base::RunLoop().RunUntilIdle();
+ GetHostUsage("foo.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_foo_tmp - 2 - 1, usage());
+
+ DeleteHostData("foo.com", kTemp,
+ QuotaClient::kDatabase | QuotaClient::kIndexedDatabase);
+ base::RunLoop().RunUntilIdle();
+ GetHostUsage("foo.com", kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(predelete_foo_tmp - 8 - 4 - 2 - 1, usage());
+}
+
+TEST_F(QuotaManagerTest, GetUsageAndQuota_Incognito) {
+ ResetQuotaManager(true);
+
+ static const MockOriginData kData[] = {
+ { "http://foo.com/", kTemp, 10 },
+ { "http://foo.com/", kPerm, 80 },
+ };
+ RegisterClient(CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem));
+
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(80, usage());
+ EXPECT_EQ(0, quota());
+
+ SetTemporaryGlobalQuota(100);
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(10, usage());
+ EXPECT_LE(std::min(static_cast<int64>(100 / kPerHostTemporaryPortion),
+ QuotaManager::kIncognitoDefaultQuotaLimit), quota());
+
+ mock_special_storage_policy()->AddUnlimited(GURL("http://foo.com/"));
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kPerm);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(80, usage());
+ EXPECT_EQ(QuotaManager::kIncognitoDefaultQuotaLimit, quota());
+
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kTemp);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(10, usage());
+ EXPECT_EQ(QuotaManager::kIncognitoDefaultQuotaLimit, quota());
+}
+
+} // namespace content
diff --git a/chromium/content/browser/quota/quota_reservation_manager_unittest.cc b/chromium/content/browser/quota/quota_reservation_manager_unittest.cc
new file mode 100644
index 00000000000..84caddcd0ed
--- /dev/null
+++ b/chromium/content/browser/quota/quota_reservation_manager_unittest.cc
@@ -0,0 +1,367 @@
+// 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 "webkit/browser/fileapi/quota/quota_reservation_manager.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/file_util.h"
+#include "base/files/file.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/fileapi/quota/open_file_handle.h"
+#include "webkit/browser/fileapi/quota/quota_reservation.h"
+
+using fileapi::kFileSystemTypeTemporary;
+using fileapi::OpenFileHandle;
+using fileapi::QuotaReservation;
+using fileapi::QuotaReservationManager;
+
+namespace content {
+
+namespace {
+
+const char kOrigin[] = "http://example.com";
+const fileapi::FileSystemType kType = kFileSystemTypeTemporary;
+const int64 kInitialFileSize = 1;
+
+typedef QuotaReservationManager::ReserveQuotaCallback ReserveQuotaCallback;
+
+int64 GetFileSize(const base::FilePath& path) {
+ int64 size = 0;
+ base::GetFileSize(path, &size);
+ return size;
+}
+
+void SetFileSize(const base::FilePath& path, int64 size) {
+ base::File file(path, base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_WRITE);
+ ASSERT_TRUE(file.IsValid());
+ ASSERT_TRUE(file.SetLength(size));
+}
+
+class FakeBackend : public QuotaReservationManager::QuotaBackend {
+ public:
+ FakeBackend()
+ : on_memory_usage_(kInitialFileSize),
+ on_disk_usage_(kInitialFileSize) {}
+ virtual ~FakeBackend() {}
+
+ virtual void ReserveQuota(const GURL& origin,
+ fileapi::FileSystemType type,
+ int64 delta,
+ const ReserveQuotaCallback& callback) OVERRIDE {
+ EXPECT_EQ(GURL(kOrigin), origin);
+ EXPECT_EQ(kType, type);
+ on_memory_usage_ += delta;
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(base::IgnoreResult(callback), base::File::FILE_OK, delta));
+ }
+
+ virtual void ReleaseReservedQuota(const GURL& origin,
+ fileapi::FileSystemType type,
+ int64 size) OVERRIDE {
+ EXPECT_LE(0, size);
+ EXPECT_EQ(GURL(kOrigin), origin);
+ EXPECT_EQ(kType, type);
+ on_memory_usage_ -= size;
+ }
+
+ virtual void CommitQuotaUsage(const GURL& origin,
+ fileapi::FileSystemType type,
+ int64 delta) OVERRIDE {
+ EXPECT_EQ(GURL(kOrigin), origin);
+ EXPECT_EQ(kType, type);
+ on_disk_usage_ += delta;
+ on_memory_usage_ += delta;
+ }
+
+ virtual void IncrementDirtyCount(const GURL& origin,
+ fileapi::FileSystemType type) OVERRIDE {}
+ virtual void DecrementDirtyCount(const GURL& origin,
+ fileapi::FileSystemType type) OVERRIDE {}
+
+ int64 on_memory_usage() { return on_memory_usage_; }
+ int64 on_disk_usage() { return on_disk_usage_; }
+
+ private:
+ int64 on_memory_usage_;
+ int64 on_disk_usage_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeBackend);
+};
+
+class FakeWriter {
+ public:
+ explicit FakeWriter(scoped_ptr<OpenFileHandle> handle)
+ : handle_(handle.Pass()),
+ path_(handle_->platform_path()),
+ max_written_offset_(handle_->GetEstimatedFileSize()),
+ append_mode_write_amount_(0),
+ dirty_(false) {
+ }
+
+ ~FakeWriter() {
+ if (handle_)
+ EXPECT_FALSE(dirty_);
+ }
+
+ int64 Truncate(int64 length) {
+ int64 consumed = 0;
+
+ if (max_written_offset_ < length) {
+ consumed = length - max_written_offset_;
+ max_written_offset_ = length;
+ }
+ SetFileSize(path_, length);
+ return consumed;
+ }
+
+ int64 Write(int64 max_offset) {
+ dirty_ = true;
+
+ int64 consumed = 0;
+ if (max_written_offset_ < max_offset) {
+ consumed = max_offset - max_written_offset_;
+ max_written_offset_ = max_offset;
+ }
+ if (GetFileSize(path_) < max_offset)
+ SetFileSize(path_, max_offset);
+ return consumed;
+ }
+
+ int64 Append(int64 amount) {
+ dirty_ = true;
+ append_mode_write_amount_ += amount;
+ SetFileSize(path_, GetFileSize(path_) + amount);
+ return amount;
+ }
+
+ void ReportUsage() {
+ handle_->UpdateMaxWrittenOffset(max_written_offset_);
+ handle_->AddAppendModeWriteAmount(append_mode_write_amount_);
+ max_written_offset_ = handle_->GetEstimatedFileSize();
+ append_mode_write_amount_ = 0;
+ dirty_ = false;
+ }
+
+ void ClearWithoutUsageReport() {
+ handle_.reset();
+ }
+
+ private:
+ scoped_ptr<OpenFileHandle> handle_;
+ base::FilePath path_;
+ int64 max_written_offset_;
+ int64 append_mode_write_amount_;
+ bool dirty_;
+};
+
+void ExpectSuccess(bool* done, base::File::Error error) {
+ EXPECT_FALSE(*done);
+ *done = true;
+ EXPECT_EQ(base::File::FILE_OK, error);
+}
+
+void RefreshReservation(QuotaReservation* reservation, int64 size) {
+ DCHECK(reservation);
+
+ bool done = false;
+ reservation->RefreshReservation(size, base::Bind(&ExpectSuccess, &done));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(done);
+}
+
+} // namespace
+
+class QuotaReservationManagerTest : public testing::Test {
+ public:
+ QuotaReservationManagerTest() {}
+ virtual ~QuotaReservationManagerTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ ASSERT_TRUE(work_dir_.CreateUniqueTempDir());
+ file_path_ = work_dir_.path().Append(FILE_PATH_LITERAL("hoge"));
+ SetFileSize(file_path_, kInitialFileSize);
+
+ scoped_ptr<QuotaReservationManager::QuotaBackend> backend(new FakeBackend);
+ reservation_manager_.reset(new QuotaReservationManager(backend.Pass()));
+ }
+
+ virtual void TearDown() OVERRIDE {
+ reservation_manager_.reset();
+ }
+
+ FakeBackend* fake_backend() {
+ return static_cast<FakeBackend*>(reservation_manager_->backend_.get());
+ }
+
+ QuotaReservationManager* reservation_manager() {
+ return reservation_manager_.get();
+ }
+
+ const base::FilePath& file_path() const {
+ return file_path_;
+ }
+
+ private:
+ base::MessageLoop message_loop_;
+ base::ScopedTempDir work_dir_;
+ base::FilePath file_path_;
+ scoped_ptr<QuotaReservationManager> reservation_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuotaReservationManagerTest);
+};
+
+TEST_F(QuotaReservationManagerTest, BasicTest) {
+ scoped_refptr<QuotaReservation> reservation =
+ reservation_manager()->CreateReservation(GURL(kOrigin), kType);
+
+ {
+ RefreshReservation(reservation.get(), 10 + 20 + 3);
+ int64 cached_reserved_quota = reservation->remaining_quota();
+ FakeWriter writer(reservation->GetOpenFileHandle(file_path()));
+
+ cached_reserved_quota -= writer.Write(kInitialFileSize + 10);
+ EXPECT_LE(0, cached_reserved_quota);
+ cached_reserved_quota -= writer.Append(20);
+ EXPECT_LE(0, cached_reserved_quota);
+
+ writer.ReportUsage();
+ }
+
+ EXPECT_EQ(3, reservation->remaining_quota());
+ EXPECT_EQ(kInitialFileSize + 10 + 20, GetFileSize(file_path()));
+ EXPECT_EQ(kInitialFileSize + 10 + 20, fake_backend()->on_disk_usage());
+ EXPECT_EQ(kInitialFileSize + 10 + 20 + 3, fake_backend()->on_memory_usage());
+
+ {
+ RefreshReservation(reservation.get(), 5);
+ FakeWriter writer(reservation->GetOpenFileHandle(file_path()));
+
+ EXPECT_EQ(0, writer.Truncate(3));
+
+ writer.ReportUsage();
+ }
+
+ EXPECT_EQ(5, reservation->remaining_quota());
+ EXPECT_EQ(3, GetFileSize(file_path()));
+ EXPECT_EQ(3, fake_backend()->on_disk_usage());
+ EXPECT_EQ(3 + 5, fake_backend()->on_memory_usage());
+
+ reservation = NULL;
+
+ EXPECT_EQ(3, fake_backend()->on_memory_usage());
+}
+
+TEST_F(QuotaReservationManagerTest, MultipleWriter) {
+ scoped_refptr<QuotaReservation> reservation =
+ reservation_manager()->CreateReservation(GURL(kOrigin), kType);
+
+ {
+ RefreshReservation(reservation.get(), 10 + 20 + 30 + 40 + 5);
+ int64 cached_reserved_quota = reservation->remaining_quota();
+ FakeWriter writer1(reservation->GetOpenFileHandle(file_path()));
+ FakeWriter writer2(reservation->GetOpenFileHandle(file_path()));
+ FakeWriter writer3(reservation->GetOpenFileHandle(file_path()));
+
+ cached_reserved_quota -= writer1.Write(kInitialFileSize + 10);
+ EXPECT_LE(0, cached_reserved_quota);
+ cached_reserved_quota -= writer2.Write(kInitialFileSize + 20);
+ cached_reserved_quota -= writer3.Append(30);
+ EXPECT_LE(0, cached_reserved_quota);
+ cached_reserved_quota -= writer3.Append(40);
+ EXPECT_LE(0, cached_reserved_quota);
+
+ writer1.ReportUsage();
+ writer2.ReportUsage();
+ writer3.ReportUsage();
+ }
+
+ EXPECT_EQ(kInitialFileSize + 20 + 30 + 40, GetFileSize(file_path()));
+ EXPECT_EQ(kInitialFileSize + 10 + 20 + 30 + 40 + 5,
+ fake_backend()->on_memory_usage());
+ EXPECT_EQ(kInitialFileSize + 20 + 30 + 40, fake_backend()->on_disk_usage());
+
+ reservation = NULL;
+
+ EXPECT_EQ(kInitialFileSize + 20 + 30 + 40, fake_backend()->on_disk_usage());
+}
+
+TEST_F(QuotaReservationManagerTest, MultipleClient) {
+ scoped_refptr<QuotaReservation> reservation1 =
+ reservation_manager()->CreateReservation(GURL(kOrigin), kType);
+ RefreshReservation(reservation1, 10);
+ int64 cached_reserved_quota1 = reservation1->remaining_quota();
+
+ scoped_refptr<QuotaReservation> reservation2 =
+ reservation_manager()->CreateReservation(GURL(kOrigin), kType);
+ RefreshReservation(reservation2, 20);
+ int64 cached_reserved_quota2 = reservation2->remaining_quota();
+
+ scoped_ptr<FakeWriter> writer1(
+ new FakeWriter(reservation1->GetOpenFileHandle(file_path())));
+
+ scoped_ptr<FakeWriter> writer2(
+ new FakeWriter(reservation2->GetOpenFileHandle(file_path())));
+
+ cached_reserved_quota1 -= writer1->Write(kInitialFileSize + 10);
+ EXPECT_LE(0, cached_reserved_quota1);
+
+ cached_reserved_quota2 -= writer2->Append(20);
+ EXPECT_LE(0, cached_reserved_quota2);
+
+ writer1->ReportUsage();
+ RefreshReservation(reservation1.get(), 2);
+ cached_reserved_quota1 = reservation1->remaining_quota();
+
+ writer2->ReportUsage();
+ RefreshReservation(reservation2.get(), 3);
+ cached_reserved_quota2 = reservation2->remaining_quota();
+
+ writer1.reset();
+ writer2.reset();
+
+ EXPECT_EQ(kInitialFileSize + 10 + 20, GetFileSize(file_path()));
+ EXPECT_EQ(kInitialFileSize + 10 + 20 + 2 + 3,
+ fake_backend()->on_memory_usage());
+ EXPECT_EQ(kInitialFileSize + 10 + 20, fake_backend()->on_disk_usage());
+
+ reservation1 = NULL;
+ EXPECT_EQ(kInitialFileSize + 10 + 20 + 3, fake_backend()->on_memory_usage());
+
+ reservation2 = NULL;
+ EXPECT_EQ(kInitialFileSize + 10 + 20, fake_backend()->on_memory_usage());
+}
+
+TEST_F(QuotaReservationManagerTest, ClientCrash) {
+ scoped_refptr<QuotaReservation> reservation1 =
+ reservation_manager()->CreateReservation(GURL(kOrigin), kType);
+ RefreshReservation(reservation1.get(), 15);
+
+ scoped_refptr<QuotaReservation> reservation2 =
+ reservation_manager()->CreateReservation(GURL(kOrigin), kType);
+ RefreshReservation(reservation2.get(), 20);
+
+ {
+ FakeWriter writer(reservation1->GetOpenFileHandle(file_path()));
+
+ writer.Write(kInitialFileSize + 10);
+
+ reservation1->OnClientCrash();
+ writer.ClearWithoutUsageReport();
+ }
+ reservation1 = NULL;
+
+ EXPECT_EQ(kInitialFileSize + 10, GetFileSize(file_path()));
+ EXPECT_EQ(kInitialFileSize + 15 + 20, fake_backend()->on_memory_usage());
+ EXPECT_EQ(kInitialFileSize + 10, fake_backend()->on_disk_usage());
+
+ reservation2 = NULL;
+ EXPECT_EQ(kInitialFileSize + 10, fake_backend()->on_memory_usage());
+}
+
+} // namespace content
diff --git a/chromium/content/browser/quota/quota_temporary_storage_evictor_unittest.cc b/chromium/content/browser/quota/quota_temporary_storage_evictor_unittest.cc
new file mode 100644
index 00000000000..a9d93f6bb9d
--- /dev/null
+++ b/chromium/content/browser/quota/quota_temporary_storage_evictor_unittest.cc
@@ -0,0 +1,414 @@
+// 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 <list>
+#include <map>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/run_loop.h"
+#include "content/public/test/mock_storage_client.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/quota/quota_manager.h"
+#include "webkit/browser/quota/quota_temporary_storage_evictor.h"
+
+using quota::QuotaTemporaryStorageEvictor;
+using quota::StorageType;
+using quota::UsageAndQuota;
+
+namespace content {
+
+class QuotaTemporaryStorageEvictorTest;
+
+namespace {
+
+class MockQuotaEvictionHandler : public quota::QuotaEvictionHandler {
+ public:
+ explicit MockQuotaEvictionHandler(QuotaTemporaryStorageEvictorTest *test)
+ : quota_(0),
+ available_space_(0),
+ error_on_evict_origin_data_(false),
+ error_on_get_usage_and_quota_(false) {}
+
+ virtual void EvictOriginData(
+ const GURL& origin,
+ StorageType type,
+ const EvictOriginDataCallback& callback) OVERRIDE {
+ if (error_on_evict_origin_data_) {
+ callback.Run(quota::kQuotaErrorInvalidModification);
+ return;
+ }
+ int64 origin_usage = EnsureOriginRemoved(origin);
+ if (origin_usage >= 0)
+ available_space_ += origin_usage;
+ callback.Run(quota::kQuotaStatusOk);
+ }
+
+ virtual void GetUsageAndQuotaForEviction(
+ const UsageAndQuotaCallback& callback) OVERRIDE {
+ if (error_on_get_usage_and_quota_) {
+ callback.Run(quota::kQuotaErrorInvalidAccess, UsageAndQuota());
+ return;
+ }
+ if (!task_for_get_usage_and_quota_.is_null())
+ task_for_get_usage_and_quota_.Run();
+ UsageAndQuota quota_and_usage(-1, GetUsage(), quota_, available_space_);
+ callback.Run(quota::kQuotaStatusOk, quota_and_usage);
+ }
+
+ virtual void GetLRUOrigin(
+ StorageType type,
+ const GetLRUOriginCallback& callback) OVERRIDE {
+ if (origin_order_.empty())
+ callback.Run(GURL());
+ else
+ callback.Run(GURL(origin_order_.front()));
+ }
+
+ int64 GetUsage() const {
+ int64 total_usage = 0;
+ for (std::map<GURL, int64>::const_iterator p = origins_.begin();
+ p != origins_.end();
+ ++p)
+ total_usage += p->second;
+ return total_usage;
+ }
+
+ void set_quota(int64 quota) {
+ quota_ = quota;
+ }
+ void set_available_space(int64 available_space) {
+ available_space_ = available_space;
+ }
+ void set_task_for_get_usage_and_quota(const base::Closure& task) {
+ task_for_get_usage_and_quota_= task;
+ }
+ void set_error_on_evict_origin_data(bool error_on_evict_origin_data) {
+ error_on_evict_origin_data_ = error_on_evict_origin_data;
+ }
+ void set_error_on_get_usage_and_quota(bool error_on_get_usage_and_quota) {
+ error_on_get_usage_and_quota_ = error_on_get_usage_and_quota;
+ }
+
+ // Simulates an access to |origin|. It reorders the internal LRU list.
+ // It internally uses AddOrigin().
+ void AccessOrigin(const GURL& origin) {
+ std::map<GURL, int64>::iterator found = origins_.find(origin);
+ EXPECT_TRUE(origins_.end() != found);
+ AddOrigin(origin, found->second);
+ }
+
+ // Simulates adding or overwriting the |origin| to the internal origin set
+ // with the |usage|. It also adds or moves the |origin| to the end of the
+ // LRU list.
+ void AddOrigin(const GURL& origin, int64 usage) {
+ EnsureOriginRemoved(origin);
+ origin_order_.push_back(origin);
+ origins_[origin] = usage;
+ }
+
+ private:
+ int64 EnsureOriginRemoved(const GURL& origin) {
+ int64 origin_usage;
+ if (origins_.find(origin) == origins_.end())
+ return -1;
+ else
+ origin_usage = origins_[origin];
+
+ origins_.erase(origin);
+ origin_order_.remove(origin);
+ return origin_usage;
+ }
+
+ int64 quota_;
+ int64 available_space_;
+ std::list<GURL> origin_order_;
+ std::map<GURL, int64> origins_;
+ bool error_on_evict_origin_data_;
+ bool error_on_get_usage_and_quota_;
+
+ base::Closure task_for_get_usage_and_quota_;
+};
+
+} // namespace
+
+class QuotaTemporaryStorageEvictorTest : public testing::Test {
+ public:
+ QuotaTemporaryStorageEvictorTest()
+ : num_get_usage_and_quota_for_eviction_(0),
+ weak_factory_(this) {}
+
+ virtual void SetUp() {
+ quota_eviction_handler_.reset(new MockQuotaEvictionHandler(this));
+
+ // Run multiple evictions in a single RunUntilIdle() when interval_ms == 0
+ temporary_storage_evictor_.reset(new QuotaTemporaryStorageEvictor(
+ quota_eviction_handler_.get(), 0));
+ }
+
+ virtual void TearDown() {
+ temporary_storage_evictor_.reset();
+ quota_eviction_handler_.reset();
+ base::RunLoop().RunUntilIdle();
+ }
+
+ void TaskForRepeatedEvictionTest(
+ const std::pair<GURL, int64>& origin_to_be_added,
+ const GURL& origin_to_be_accessed,
+ int expected_usage_after_first,
+ int expected_usage_after_second) {
+ EXPECT_GE(4, num_get_usage_and_quota_for_eviction_);
+ switch (num_get_usage_and_quota_for_eviction_) {
+ case 2:
+ EXPECT_EQ(expected_usage_after_first,
+ quota_eviction_handler()->GetUsage());
+ if (!origin_to_be_added.first.is_empty())
+ quota_eviction_handler()->AddOrigin(origin_to_be_added.first,
+ origin_to_be_added.second);
+ if (!origin_to_be_accessed.is_empty())
+ quota_eviction_handler()->AccessOrigin(origin_to_be_accessed);
+ break;
+ case 3:
+ EXPECT_EQ(expected_usage_after_second,
+ quota_eviction_handler()->GetUsage());
+ temporary_storage_evictor()->set_repeated_eviction(false);
+ break;
+ }
+ ++num_get_usage_and_quota_for_eviction_;
+ }
+
+ protected:
+ MockQuotaEvictionHandler* quota_eviction_handler() const {
+ return static_cast<MockQuotaEvictionHandler*>(
+ quota_eviction_handler_.get());
+ }
+
+ QuotaTemporaryStorageEvictor* temporary_storage_evictor() const {
+ return temporary_storage_evictor_.get();
+ }
+
+ const QuotaTemporaryStorageEvictor::Statistics& statistics() const {
+ return temporary_storage_evictor()->statistics_;
+ }
+
+ void set_repeated_eviction(bool repeated_eviction) const {
+ return temporary_storage_evictor_->set_repeated_eviction(repeated_eviction);
+ }
+
+ int num_get_usage_and_quota_for_eviction() const {
+ return num_get_usage_and_quota_for_eviction_;
+ }
+
+ int64 default_min_available_disk_space_to_start_eviction() const {
+ return 1000 * 1000 * 500;
+ }
+
+ void set_min_available_disk_space_to_start_eviction(int64 value) const {
+ temporary_storage_evictor_->set_min_available_disk_space_to_start_eviction(
+ value);
+ }
+
+ void reset_min_available_disk_space_to_start_eviction() const {
+ temporary_storage_evictor_->
+ reset_min_available_disk_space_to_start_eviction();
+ }
+
+ base::MessageLoop message_loop_;
+ scoped_ptr<MockQuotaEvictionHandler> quota_eviction_handler_;
+ scoped_ptr<QuotaTemporaryStorageEvictor> temporary_storage_evictor_;
+
+ int num_get_usage_and_quota_for_eviction_;
+
+ base::WeakPtrFactory<QuotaTemporaryStorageEvictorTest> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuotaTemporaryStorageEvictorTest);
+};
+
+TEST_F(QuotaTemporaryStorageEvictorTest, SimpleEvictionTest) {
+ quota_eviction_handler()->AddOrigin(GURL("http://www.z.com"), 3000);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.y.com"), 200);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.x.com"), 500);
+ quota_eviction_handler()->set_quota(4000);
+ quota_eviction_handler()->set_available_space(1000000000);
+ EXPECT_EQ(3000 + 200 + 500, quota_eviction_handler()->GetUsage());
+ set_repeated_eviction(false);
+ temporary_storage_evictor()->Start();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(200 + 500, quota_eviction_handler()->GetUsage());
+
+ EXPECT_EQ(0, statistics().num_errors_on_evicting_origin);
+ EXPECT_EQ(0, statistics().num_errors_on_getting_usage_and_quota);
+ EXPECT_EQ(1, statistics().num_evicted_origins);
+ EXPECT_EQ(1, statistics().num_eviction_rounds);
+ EXPECT_EQ(0, statistics().num_skipped_eviction_rounds);
+}
+
+TEST_F(QuotaTemporaryStorageEvictorTest, MultipleEvictionTest) {
+ quota_eviction_handler()->AddOrigin(GURL("http://www.z.com"), 20);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.y.com"), 2900);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.x.com"), 450);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.w.com"), 400);
+ quota_eviction_handler()->set_quota(4000);
+ quota_eviction_handler()->set_available_space(1000000000);
+ EXPECT_EQ(20 + 2900 + 450 + 400, quota_eviction_handler()->GetUsage());
+ set_repeated_eviction(false);
+ temporary_storage_evictor()->Start();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(450 + 400, quota_eviction_handler()->GetUsage());
+
+ EXPECT_EQ(0, statistics().num_errors_on_evicting_origin);
+ EXPECT_EQ(0, statistics().num_errors_on_getting_usage_and_quota);
+ EXPECT_EQ(2, statistics().num_evicted_origins);
+ EXPECT_EQ(1, statistics().num_eviction_rounds);
+ EXPECT_EQ(0, statistics().num_skipped_eviction_rounds);
+}
+
+TEST_F(QuotaTemporaryStorageEvictorTest, RepeatedEvictionTest) {
+ const int64 a_size = 400;
+ const int64 b_size = 150;
+ const int64 c_size = 120;
+ const int64 d_size = 292;
+ const int64 initial_total_size = a_size + b_size + c_size + d_size;
+ const int64 e_size = 275;
+
+ quota_eviction_handler()->AddOrigin(GURL("http://www.d.com"), d_size);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.c.com"), c_size);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.b.com"), b_size);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.a.com"), a_size);
+ quota_eviction_handler()->set_quota(1000);
+ quota_eviction_handler()->set_available_space(1000000000);
+ quota_eviction_handler()->set_task_for_get_usage_and_quota(
+ base::Bind(&QuotaTemporaryStorageEvictorTest::TaskForRepeatedEvictionTest,
+ weak_factory_.GetWeakPtr(),
+ std::make_pair(GURL("http://www.e.com"), e_size), GURL(),
+ initial_total_size - d_size,
+ initial_total_size - d_size + e_size - c_size));
+ EXPECT_EQ(initial_total_size, quota_eviction_handler()->GetUsage());
+ temporary_storage_evictor()->Start();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(initial_total_size - d_size + e_size - c_size - b_size,
+ quota_eviction_handler()->GetUsage());
+ EXPECT_EQ(5, num_get_usage_and_quota_for_eviction());
+
+ EXPECT_EQ(0, statistics().num_errors_on_evicting_origin);
+ EXPECT_EQ(0, statistics().num_errors_on_getting_usage_and_quota);
+ EXPECT_EQ(3, statistics().num_evicted_origins);
+ EXPECT_EQ(2, statistics().num_eviction_rounds);
+ EXPECT_EQ(0, statistics().num_skipped_eviction_rounds);
+}
+
+TEST_F(QuotaTemporaryStorageEvictorTest, RepeatedEvictionSkippedTest) {
+ const int64 a_size = 400;
+ const int64 b_size = 150;
+ const int64 c_size = 120;
+ const int64 d_size = 292;
+ const int64 initial_total_size = a_size + b_size + c_size + d_size;
+
+ quota_eviction_handler()->AddOrigin(GURL("http://www.d.com"), d_size);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.c.com"), c_size);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.b.com"), b_size);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.a.com"), a_size);
+ quota_eviction_handler()->set_quota(1000);
+ quota_eviction_handler()->set_available_space(1000000000);
+ quota_eviction_handler()->set_task_for_get_usage_and_quota(
+ base::Bind(&QuotaTemporaryStorageEvictorTest::TaskForRepeatedEvictionTest,
+ weak_factory_.GetWeakPtr(), std::make_pair(GURL(), 0), GURL(),
+ initial_total_size - d_size, initial_total_size - d_size));
+ EXPECT_EQ(initial_total_size, quota_eviction_handler()->GetUsage());
+ set_repeated_eviction(true);
+ temporary_storage_evictor()->Start();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(initial_total_size - d_size, quota_eviction_handler()->GetUsage());
+ EXPECT_EQ(4, num_get_usage_and_quota_for_eviction());
+
+ EXPECT_EQ(0, statistics().num_errors_on_evicting_origin);
+ EXPECT_EQ(0, statistics().num_errors_on_getting_usage_and_quota);
+ EXPECT_EQ(1, statistics().num_evicted_origins);
+ EXPECT_EQ(3, statistics().num_eviction_rounds);
+ EXPECT_EQ(2, statistics().num_skipped_eviction_rounds);
+}
+
+TEST_F(QuotaTemporaryStorageEvictorTest, RepeatedEvictionWithAccessOriginTest) {
+ const int64 a_size = 400;
+ const int64 b_size = 150;
+ const int64 c_size = 120;
+ const int64 d_size = 292;
+ const int64 initial_total_size = a_size + b_size + c_size + d_size;
+ const int64 e_size = 275;
+
+ quota_eviction_handler()->AddOrigin(GURL("http://www.d.com"), d_size);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.c.com"), c_size);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.b.com"), b_size);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.a.com"), a_size);
+ quota_eviction_handler()->set_quota(1000);
+ quota_eviction_handler()->set_available_space(1000000000);
+ quota_eviction_handler()->set_task_for_get_usage_and_quota(
+ base::Bind(&QuotaTemporaryStorageEvictorTest::TaskForRepeatedEvictionTest,
+ weak_factory_.GetWeakPtr(),
+ std::make_pair(GURL("http://www.e.com"), e_size),
+ GURL("http://www.c.com"),
+ initial_total_size - d_size,
+ initial_total_size - d_size + e_size - b_size));
+ EXPECT_EQ(initial_total_size, quota_eviction_handler()->GetUsage());
+ temporary_storage_evictor()->Start();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(initial_total_size - d_size + e_size - b_size - a_size,
+ quota_eviction_handler()->GetUsage());
+ EXPECT_EQ(5, num_get_usage_and_quota_for_eviction());
+
+ EXPECT_EQ(0, statistics().num_errors_on_evicting_origin);
+ EXPECT_EQ(0, statistics().num_errors_on_getting_usage_and_quota);
+ EXPECT_EQ(3, statistics().num_evicted_origins);
+ EXPECT_EQ(2, statistics().num_eviction_rounds);
+ EXPECT_EQ(0, statistics().num_skipped_eviction_rounds);
+}
+
+TEST_F(QuotaTemporaryStorageEvictorTest, DiskSpaceNonEvictionTest) {
+ quota_eviction_handler()->AddOrigin(GURL("http://www.z.com"), 414);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.x.com"), 450);
+ quota_eviction_handler()->set_quota(10000);
+ quota_eviction_handler()->set_available_space(
+ default_min_available_disk_space_to_start_eviction() - 350);
+ EXPECT_EQ(414 + 450, quota_eviction_handler()->GetUsage());
+ reset_min_available_disk_space_to_start_eviction();
+ set_repeated_eviction(false);
+ temporary_storage_evictor()->Start();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(414 + 450, quota_eviction_handler()->GetUsage());
+
+ EXPECT_EQ(0, statistics().num_errors_on_evicting_origin);
+ EXPECT_EQ(0, statistics().num_errors_on_getting_usage_and_quota);
+ EXPECT_EQ(0, statistics().num_evicted_origins);
+ EXPECT_EQ(1, statistics().num_eviction_rounds);
+ EXPECT_EQ(1, statistics().num_skipped_eviction_rounds);
+}
+
+TEST_F(QuotaTemporaryStorageEvictorTest, DiskSpaceEvictionTest) {
+ quota_eviction_handler()->AddOrigin(GURL("http://www.z.com"), 294);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.y.com"), 120);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.x.com"), 150);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.w.com"), 300);
+ quota_eviction_handler()->set_quota(10000);
+ quota_eviction_handler()->set_available_space(
+ default_min_available_disk_space_to_start_eviction() - 350);
+ EXPECT_EQ(294 + 120 + 150 + 300, quota_eviction_handler()->GetUsage());
+ set_min_available_disk_space_to_start_eviction(
+ default_min_available_disk_space_to_start_eviction());
+ set_repeated_eviction(false);
+ temporary_storage_evictor()->Start();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(150 + 300, quota_eviction_handler()->GetUsage());
+
+ EXPECT_EQ(0, statistics().num_errors_on_evicting_origin);
+ EXPECT_EQ(0, statistics().num_errors_on_getting_usage_and_quota);
+ EXPECT_EQ(2, statistics().num_evicted_origins);
+ EXPECT_EQ(1, statistics().num_eviction_rounds);
+ EXPECT_EQ(0, statistics().num_skipped_eviction_rounds);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/quota/storage_monitor_unittest.cc b/chromium/content/browser/quota/storage_monitor_unittest.cc
new file mode 100644
index 00000000000..5926e9ac1fd
--- /dev/null
+++ b/chromium/content/browser/quota/storage_monitor_unittest.cc
@@ -0,0 +1,707 @@
+// 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 <vector>
+
+#include "base/files/scoped_temp_dir.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/run_loop.h"
+#include "content/public/test/mock_special_storage_policy.h"
+#include "content/public/test/mock_storage_client.h"
+#include "net/base/net_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/quota/quota_manager.h"
+#include "webkit/browser/quota/quota_manager_proxy.h"
+#include "webkit/browser/quota/storage_monitor.h"
+#include "webkit/browser/quota/storage_observer.h"
+
+using quota::HostStorageObservers;
+using quota::kQuotaErrorNotSupported;
+using quota::kQuotaStatusOk;
+using quota::kStorageTypePersistent;
+using quota::kStorageTypeTemporary;
+using quota::QuotaClient;
+using quota::QuotaManager;
+using quota::QuotaStatusCode;
+using quota::SpecialStoragePolicy;
+using quota::StorageMonitor;
+using quota::StorageObserver;
+using quota::StorageObserverList;
+using quota::StorageType;
+using quota::StorageTypeObservers;
+
+namespace content {
+
+namespace {
+
+const char kDefaultOrigin[] = "http://www.foo.com/";
+const char kAlternativeOrigin[] = "http://www.bar.com/";
+
+class MockObserver : public StorageObserver {
+ public:
+ const StorageObserver::Event& LastEvent() const {
+ CHECK(!events_.empty());
+ return events_.back();
+ }
+
+ int EventCount() const {
+ return events_.size();
+ }
+
+ // StorageObserver implementation:
+ virtual void OnStorageEvent(const StorageObserver::Event& event) OVERRIDE {
+ events_.push_back(event);
+ }
+
+ private:
+ std::vector<StorageObserver::Event> events_;
+};
+
+// A mock quota manager for overriding GetUsageAndQuotaForWebApps().
+class UsageMockQuotaManager : public QuotaManager {
+ public:
+ UsageMockQuotaManager(SpecialStoragePolicy* special_storage_policy)
+ : QuotaManager(
+ false,
+ base::FilePath(),
+ base::MessageLoopProxy::current().get(),
+ base::MessageLoopProxy::current().get(),
+ special_storage_policy),
+ callback_usage_(0),
+ callback_quota_(0),
+ callback_status_(kQuotaStatusOk),
+ initialized_(false) {
+ }
+
+ void SetCallbackParams(int64 usage, int64 quota, QuotaStatusCode status) {
+ initialized_ = true;
+ callback_quota_ = quota;
+ callback_usage_ = usage;
+ callback_status_ = status;
+ }
+
+ void InvokeCallback() {
+ delayed_callback_.Run(callback_status_, callback_usage_, callback_quota_);
+ }
+
+ virtual void GetUsageAndQuotaForWebApps(
+ const GURL& origin,
+ StorageType type,
+ const GetUsageAndQuotaCallback& callback) OVERRIDE {
+ if (initialized_)
+ callback.Run(callback_status_, callback_usage_, callback_quota_);
+ else
+ delayed_callback_ = callback;
+ }
+
+ protected:
+ virtual ~UsageMockQuotaManager() {}
+
+ private:
+ int64 callback_usage_;
+ int64 callback_quota_;
+ QuotaStatusCode callback_status_;
+ bool initialized_;
+ GetUsageAndQuotaCallback delayed_callback_;
+};
+
+} // namespace
+
+class StorageMonitorTestBase : public testing::Test {
+ protected:
+ void DispatchPendingEvents(StorageObserverList& observer_list) {
+ observer_list.DispatchPendingEvent();
+ }
+
+ const StorageObserver::Event* GetPendingEvent(
+ const StorageObserverList& observer_list) {
+ return observer_list.notification_timer_.IsRunning()
+ ? &observer_list.pending_event_ : NULL;
+ }
+
+ const StorageObserver::Event* GetPendingEvent(
+ const HostStorageObservers& host_observers) {
+ return GetPendingEvent(host_observers.observers_);
+ }
+
+ int GetRequiredUpdatesCount(const StorageObserverList& observer_list) {
+ int count = 0;
+ for (StorageObserverList::StorageObserverStateMap::const_iterator it =
+ observer_list.observers_.begin();
+ it != observer_list.observers_.end(); ++it) {
+ if (it->second.requires_update)
+ ++count;
+ }
+
+ return count;
+ }
+
+ int GetRequiredUpdatesCount(const HostStorageObservers& host_observers) {
+ return GetRequiredUpdatesCount(host_observers.observers_);
+ }
+
+ void SetLastNotificationTime(StorageObserverList& observer_list,
+ StorageObserver* observer) {
+ ASSERT_TRUE(observer_list.observers_.find(observer) !=
+ observer_list.observers_.end());
+
+ StorageObserverList::ObserverState& state =
+ observer_list.observers_[observer];
+ state.last_notification_time = base::TimeTicks::Now() - state.rate;
+ }
+
+ void SetLastNotificationTime(HostStorageObservers& host_observers,
+ StorageObserver* observer) {
+ SetLastNotificationTime(host_observers.observers_, observer);
+ }
+
+ int GetObserverCount(const HostStorageObservers& host_observers) {
+ return host_observers.observers_.ObserverCount();
+ }
+};
+
+class StorageTestWithManagerBase : public StorageMonitorTestBase {
+ public:
+ virtual void SetUp() OVERRIDE {
+ storage_policy_ = new MockSpecialStoragePolicy();
+ quota_manager_ = new UsageMockQuotaManager(storage_policy_.get());
+ }
+
+ virtual void TearDown() OVERRIDE {
+ // This ensures the quota manager is destroyed correctly.
+ quota_manager_ = NULL;
+ base::RunLoop().RunUntilIdle();
+ }
+
+ protected:
+ base::MessageLoop message_loop_;
+ scoped_refptr<MockSpecialStoragePolicy> storage_policy_;
+ scoped_refptr<UsageMockQuotaManager> quota_manager_;
+};
+
+// Tests for StorageObserverList:
+
+typedef StorageMonitorTestBase StorageObserverListTest;
+
+// Test dispatching events to one observer.
+TEST_F(StorageObserverListTest, DispatchEventToSingleObserver) {
+ // A message loop is required as StorageObserverList may schedule jobs.
+ base::MessageLoop loop(base::MessageLoop::TYPE_DEFAULT);
+
+ StorageObserver::MonitorParams params(kStorageTypePersistent,
+ GURL(kDefaultOrigin),
+ base::TimeDelta::FromHours(1),
+ false);
+ MockObserver mock_observer;
+ StorageObserverList observer_list;
+ observer_list.AddObserver(&mock_observer, params);
+
+ StorageObserver::Event event;
+ event.filter = params.filter;
+
+ // Verify that the first event is dispatched immediately.
+ event.quota = 1;
+ event.usage = 1;
+ observer_list.OnStorageChange(event);
+ EXPECT_EQ(1, mock_observer.EventCount());
+ EXPECT_EQ(event, mock_observer.LastEvent());
+ EXPECT_EQ(NULL, GetPendingEvent(observer_list));
+ EXPECT_EQ(0, GetRequiredUpdatesCount(observer_list));
+
+ // Verify that the next event is pending.
+ event.quota = 2;
+ event.usage = 2;
+ observer_list.OnStorageChange(event);
+ EXPECT_EQ(1, mock_observer.EventCount());
+ ASSERT_TRUE(GetPendingEvent(observer_list));
+ EXPECT_EQ(event, *GetPendingEvent(observer_list));
+ EXPECT_EQ(1, GetRequiredUpdatesCount(observer_list));
+
+ // Fake the last notification time so that an event will be dispatched.
+ SetLastNotificationTime(observer_list, &mock_observer);
+ event.quota = 3;
+ event.usage = 3;
+ observer_list.OnStorageChange(event);
+ EXPECT_EQ(2, mock_observer.EventCount());
+ EXPECT_EQ(event, mock_observer.LastEvent());
+ EXPECT_EQ(NULL, GetPendingEvent(observer_list));
+ EXPECT_EQ(0, GetRequiredUpdatesCount(observer_list));
+
+ // Remove the observer.
+ event.quota = 4;
+ event.usage = 4;
+ observer_list.RemoveObserver(&mock_observer);
+ observer_list.OnStorageChange(event);
+ EXPECT_EQ(2, mock_observer.EventCount());
+ EXPECT_EQ(NULL, GetPendingEvent(observer_list));
+}
+
+// Test dispatching events to multiple observers.
+TEST_F(StorageObserverListTest, DispatchEventToMultipleObservers) {
+ // A message loop is required as StorageObserverList may schedule jobs.
+ base::MessageLoop loop(base::MessageLoop::TYPE_DEFAULT);
+
+ MockObserver mock_observer1;
+ MockObserver mock_observer2;
+ StorageObserverList observer_list;
+ StorageObserver::Filter filter(kStorageTypePersistent,
+ GURL(kDefaultOrigin));
+ observer_list.AddObserver(
+ &mock_observer1,
+ StorageObserver::MonitorParams(
+ filter, base::TimeDelta::FromHours(1), false));
+ observer_list.AddObserver(
+ &mock_observer2,
+ StorageObserver::MonitorParams(
+ filter, base::TimeDelta::FromHours(2), false));
+
+ StorageObserver::Event event;
+ event.filter = filter;
+
+ // Verify that the first event is dispatched immediately.
+ event.quota = 1;
+ event.usage = 1;
+ observer_list.OnStorageChange(event);
+ EXPECT_EQ(1, mock_observer1.EventCount());
+ EXPECT_EQ(1, mock_observer2.EventCount());
+ EXPECT_EQ(event, mock_observer1.LastEvent());
+ EXPECT_EQ(event, mock_observer2.LastEvent());
+ EXPECT_EQ(NULL, GetPendingEvent(observer_list));
+ EXPECT_EQ(0, GetRequiredUpdatesCount(observer_list));
+
+ // Fake the last notification time so that observer1 will receive the next
+ // event, but it will be pending for observer2.
+ SetLastNotificationTime(observer_list, &mock_observer1);
+ event.quota = 2;
+ event.usage = 2;
+ observer_list.OnStorageChange(event);
+ EXPECT_EQ(2, mock_observer1.EventCount());
+ EXPECT_EQ(1, mock_observer2.EventCount());
+ EXPECT_EQ(event, mock_observer1.LastEvent());
+ ASSERT_TRUE(GetPendingEvent(observer_list));
+ EXPECT_EQ(event, *GetPendingEvent(observer_list));
+ EXPECT_EQ(1, GetRequiredUpdatesCount(observer_list));
+
+ // Now dispatch the pending event to observer2.
+ SetLastNotificationTime(observer_list, &mock_observer2);
+ DispatchPendingEvents(observer_list);
+ EXPECT_EQ(2, mock_observer1.EventCount());
+ EXPECT_EQ(2, mock_observer2.EventCount());
+ EXPECT_EQ(event, mock_observer1.LastEvent());
+ EXPECT_EQ(event, mock_observer2.LastEvent());
+ EXPECT_EQ(NULL, GetPendingEvent(observer_list));
+ EXPECT_EQ(0, GetRequiredUpdatesCount(observer_list));
+}
+
+// Ensure that the |origin| field in events match the origin specified by the
+// observer on registration.
+TEST_F(StorageObserverListTest, ReplaceEventOrigin) {
+ StorageObserver::MonitorParams params(kStorageTypePersistent,
+ GURL(kDefaultOrigin),
+ base::TimeDelta::FromHours(1),
+ false);
+ MockObserver mock_observer;
+ StorageObserverList observer_list;
+ observer_list.AddObserver(&mock_observer, params);
+
+ StorageObserver::Event dispatched_event;
+ dispatched_event.filter = params.filter;
+ dispatched_event.filter.origin = GURL("https://www.foo.com/bar");
+ observer_list.OnStorageChange(dispatched_event);
+
+ EXPECT_EQ(params.filter.origin, mock_observer.LastEvent().filter.origin);
+}
+
+// Tests for HostStorageObservers:
+
+typedef StorageTestWithManagerBase HostStorageObserversTest;
+
+// Verify that HostStorageObservers is initialized after the first usage change.
+TEST_F(HostStorageObserversTest, InitializeOnUsageChange) {
+ StorageObserver::MonitorParams params(kStorageTypePersistent,
+ GURL(kDefaultOrigin),
+ base::TimeDelta::FromHours(1),
+ false);
+ const int64 kUsage = 324554;
+ const int64 kQuota = 234354354;
+ quota_manager_->SetCallbackParams(kUsage, kQuota, kQuotaStatusOk);
+
+ MockObserver mock_observer;
+ HostStorageObservers host_observers(quota_manager_.get());
+ host_observers.AddObserver(&mock_observer, params);
+
+ // Verify that HostStorageObservers dispatches the first event correctly.
+ StorageObserver::Event expected_event(params.filter, kUsage, kQuota);
+ host_observers.NotifyUsageChange(params.filter, 87324);
+ EXPECT_EQ(1, mock_observer.EventCount());
+ EXPECT_EQ(expected_event, mock_observer.LastEvent());
+ EXPECT_TRUE(host_observers.is_initialized());
+
+ // Verify that HostStorageObservers handles subsequent usage changes
+ // correctly.
+ const int64 kDelta = 2345;
+ expected_event.usage += kDelta;
+ SetLastNotificationTime(host_observers, &mock_observer);
+ host_observers.NotifyUsageChange(params.filter, kDelta);
+ EXPECT_EQ(2, mock_observer.EventCount());
+ EXPECT_EQ(expected_event, mock_observer.LastEvent());
+}
+
+// Verify that HostStorageObservers is initialized after the adding the first
+// observer that elected to receive the initial state.
+TEST_F(HostStorageObserversTest, InitializeOnObserver) {
+ const int64 kUsage = 74387;
+ const int64 kQuota = 92834743;
+ quota_manager_->SetCallbackParams(kUsage, kQuota, kQuotaStatusOk);
+ HostStorageObservers host_observers(quota_manager_.get());
+
+ // |host_observers| should not be initialized after the first observer is
+ // added because it did not elect to receive the initial state.
+ StorageObserver::MonitorParams params(kStorageTypePersistent,
+ GURL(kDefaultOrigin),
+ base::TimeDelta::FromHours(1),
+ false);
+ MockObserver mock_observer1;
+ host_observers.AddObserver(&mock_observer1, params);
+ EXPECT_FALSE(host_observers.is_initialized());
+ EXPECT_EQ(0, mock_observer1.EventCount());
+
+ // |host_observers| should be initialized after the second observer is
+ // added.
+ MockObserver mock_observer2;
+ params.dispatch_initial_state = true;
+ host_observers.AddObserver(&mock_observer2, params);
+ StorageObserver::Event expected_event(params.filter, kUsage, kQuota);
+ EXPECT_EQ(0, mock_observer1.EventCount());
+ EXPECT_EQ(1, mock_observer2.EventCount());
+ EXPECT_EQ(expected_event, mock_observer2.LastEvent());
+ EXPECT_TRUE(host_observers.is_initialized());
+ EXPECT_EQ(NULL, GetPendingEvent(host_observers));
+ EXPECT_EQ(0, GetRequiredUpdatesCount(host_observers));
+
+ // Verify that both observers will receive events after a usage change.
+ const int64 kDelta = 2345;
+ expected_event.usage += kDelta;
+ SetLastNotificationTime(host_observers, &mock_observer2);
+ host_observers.NotifyUsageChange(params.filter, kDelta);
+ EXPECT_EQ(1, mock_observer1.EventCount());
+ EXPECT_EQ(2, mock_observer2.EventCount());
+ EXPECT_EQ(expected_event, mock_observer1.LastEvent());
+ EXPECT_EQ(expected_event, mock_observer2.LastEvent());
+ EXPECT_EQ(NULL, GetPendingEvent(host_observers));
+ EXPECT_EQ(0, GetRequiredUpdatesCount(host_observers));
+
+ // Verify that the addition of a third observer only causes an event to be
+ // dispatched to the new observer.
+ MockObserver mock_observer3;
+ params.dispatch_initial_state = true;
+ host_observers.AddObserver(&mock_observer3, params);
+ EXPECT_EQ(1, mock_observer1.EventCount());
+ EXPECT_EQ(2, mock_observer2.EventCount());
+ EXPECT_EQ(1, mock_observer3.EventCount());
+ EXPECT_EQ(expected_event, mock_observer3.LastEvent());
+}
+
+// Verify that negative usage and quota is changed to zero.
+TEST_F(HostStorageObserversTest, NegativeUsageAndQuota) {
+ StorageObserver::MonitorParams params(kStorageTypePersistent,
+ GURL(kDefaultOrigin),
+ base::TimeDelta::FromHours(1),
+ false);
+ const int64 kUsage = -324554;
+ const int64 kQuota = -234354354;
+ quota_manager_->SetCallbackParams(kUsage, kQuota, kQuotaStatusOk);
+
+ MockObserver mock_observer;
+ HostStorageObservers host_observers(quota_manager_.get());
+ host_observers.AddObserver(&mock_observer, params);
+
+ StorageObserver::Event expected_event(params.filter, 0, 0);
+ host_observers.NotifyUsageChange(params.filter, -87324);
+ EXPECT_EQ(expected_event, mock_observer.LastEvent());
+}
+
+// Verify that HostStorageObservers can recover from a bad initialization.
+TEST_F(HostStorageObserversTest, RecoverFromBadUsageInit) {
+ StorageObserver::MonitorParams params(kStorageTypePersistent,
+ GURL(kDefaultOrigin),
+ base::TimeDelta::FromHours(1),
+ false);
+ MockObserver mock_observer;
+ HostStorageObservers host_observers(quota_manager_.get());
+ host_observers.AddObserver(&mock_observer, params);
+
+ // Set up the quota manager to return an error status.
+ const int64 kUsage = 6656;
+ const int64 kQuota = 99585556;
+ quota_manager_->SetCallbackParams(kUsage, kQuota, kQuotaErrorNotSupported);
+
+ // Verify that |host_observers| is not initialized and an event has not been
+ // dispatched.
+ host_observers.NotifyUsageChange(params.filter, 9438);
+ EXPECT_EQ(0, mock_observer.EventCount());
+ EXPECT_FALSE(host_observers.is_initialized());
+ EXPECT_EQ(NULL, GetPendingEvent(host_observers));
+ EXPECT_EQ(0, GetRequiredUpdatesCount(host_observers));
+
+ // Now ensure that quota manager returns a good status.
+ quota_manager_->SetCallbackParams(kUsage, kQuota, kQuotaStatusOk);
+ host_observers.NotifyUsageChange(params.filter, 9048543);
+ StorageObserver::Event expected_event(params.filter, kUsage, kQuota);
+ EXPECT_EQ(1, mock_observer.EventCount());
+ EXPECT_EQ(expected_event, mock_observer.LastEvent());
+ EXPECT_TRUE(host_observers.is_initialized());
+}
+
+// Verify that HostStorageObservers handle initialization of the cached usage
+// and quota correctly.
+TEST_F(HostStorageObserversTest, AsyncInitialization) {
+ StorageObserver::MonitorParams params(kStorageTypePersistent,
+ GURL(kDefaultOrigin),
+ base::TimeDelta::FromHours(1),
+ false);
+ MockObserver mock_observer;
+ HostStorageObservers host_observers(quota_manager_.get());
+ host_observers.AddObserver(&mock_observer, params);
+
+ // Trigger initialization. Leave the mock quota manager uninitialized so that
+ // the callback is not invoked.
+ host_observers.NotifyUsageChange(params.filter, 7645);
+ EXPECT_EQ(0, mock_observer.EventCount());
+ EXPECT_FALSE(host_observers.is_initialized());
+ EXPECT_EQ(NULL, GetPendingEvent(host_observers));
+ EXPECT_EQ(0, GetRequiredUpdatesCount(host_observers));
+
+ // Simulate notifying |host_observers| of a usage change before initialization
+ // is complete.
+ const int64 kUsage = 6656;
+ const int64 kQuota = 99585556;
+ const int64 kDelta = 327643;
+ host_observers.NotifyUsageChange(params.filter, kDelta);
+ EXPECT_EQ(0, mock_observer.EventCount());
+ EXPECT_FALSE(host_observers.is_initialized());
+ EXPECT_EQ(NULL, GetPendingEvent(host_observers));
+ EXPECT_EQ(0, GetRequiredUpdatesCount(host_observers));
+
+ // Simulate an asynchronous callback from QuotaManager.
+ quota_manager_->SetCallbackParams(kUsage, kQuota, kQuotaStatusOk);
+ quota_manager_->InvokeCallback();
+ StorageObserver::Event expected_event(params.filter, kUsage + kDelta, kQuota);
+ EXPECT_EQ(1, mock_observer.EventCount());
+ EXPECT_EQ(expected_event, mock_observer.LastEvent());
+ EXPECT_TRUE(host_observers.is_initialized());
+ EXPECT_EQ(NULL, GetPendingEvent(host_observers));
+ EXPECT_EQ(0, GetRequiredUpdatesCount(host_observers));
+}
+
+// Tests for StorageTypeObservers:
+
+typedef StorageTestWithManagerBase StorageTypeObserversTest;
+
+// Test adding and removing observers.
+TEST_F(StorageTypeObserversTest, AddRemoveObservers) {
+ StorageTypeObservers type_observers(quota_manager_.get());
+
+ StorageObserver::MonitorParams params1(kStorageTypePersistent,
+ GURL(kDefaultOrigin),
+ base::TimeDelta::FromHours(1),
+ false);
+ StorageObserver::MonitorParams params2(kStorageTypePersistent,
+ GURL(kAlternativeOrigin),
+ base::TimeDelta::FromHours(1),
+ false);
+ std::string host1 = net::GetHostOrSpecFromURL(params1.filter.origin);
+ std::string host2 = net::GetHostOrSpecFromURL(params2.filter.origin);
+
+ MockObserver mock_observer1;
+ MockObserver mock_observer2;
+ MockObserver mock_observer3;
+ type_observers.AddObserver(&mock_observer1, params1);
+ type_observers.AddObserver(&mock_observer2, params1);
+
+ type_observers.AddObserver(&mock_observer1, params2);
+ type_observers.AddObserver(&mock_observer2, params2);
+ type_observers.AddObserver(&mock_observer3, params2);
+
+ // Verify that the observers have been removed correctly.
+ ASSERT_TRUE(type_observers.GetHostObservers(host1));
+ ASSERT_TRUE(type_observers.GetHostObservers(host2));
+ EXPECT_EQ(2, GetObserverCount(*type_observers.GetHostObservers(host1)));
+ EXPECT_EQ(3, GetObserverCount(*type_observers.GetHostObservers(host2)));
+
+ // Remove an observer for a specific filter.
+ type_observers.RemoveObserverForFilter(&mock_observer1, params1.filter);
+ ASSERT_TRUE(type_observers.GetHostObservers(host1));
+ ASSERT_TRUE(type_observers.GetHostObservers(host2));
+ EXPECT_EQ(1, GetObserverCount(*type_observers.GetHostObservers(host1)));
+ EXPECT_EQ(3, GetObserverCount(*type_observers.GetHostObservers(host2)));
+
+ // Remove all instances of an observer.
+ type_observers.RemoveObserver(&mock_observer2);
+ ASSERT_TRUE(type_observers.GetHostObservers(host2));
+ EXPECT_EQ(2, GetObserverCount(*type_observers.GetHostObservers(host2)));
+ // Observers of host1 has been deleted as it is empty.
+ EXPECT_FALSE(type_observers.GetHostObservers(host1));
+}
+
+// Tests for StorageMonitor:
+
+class StorageMonitorTest : public StorageTestWithManagerBase {
+ public:
+ StorageMonitorTest()
+ : storage_monitor_(NULL),
+ params1_(kStorageTypeTemporary,
+ GURL(kDefaultOrigin),
+ base::TimeDelta::FromHours(1),
+ false),
+ params2_(kStorageTypePersistent,
+ GURL(kDefaultOrigin),
+ base::TimeDelta::FromHours(1),
+ false) {
+ }
+
+ protected:
+ virtual void SetUp() OVERRIDE {
+ StorageTestWithManagerBase::SetUp();
+
+ storage_monitor_ = quota_manager_->storage_monitor_.get();
+ host_ = net::GetHostOrSpecFromURL(params1_.filter.origin);
+
+ storage_monitor_->AddObserver(&mock_observer1_, params1_);
+ storage_monitor_->AddObserver(&mock_observer2_, params1_);
+
+ storage_monitor_->AddObserver(&mock_observer1_, params2_);
+ storage_monitor_->AddObserver(&mock_observer2_, params2_);
+ storage_monitor_->AddObserver(&mock_observer3_, params2_);
+ }
+
+ int GetObserverCount(StorageType storage_type) {
+ const StorageTypeObservers* type_observers =
+ storage_monitor_->GetStorageTypeObservers(storage_type);
+ return StorageMonitorTestBase::GetObserverCount(
+ *type_observers->GetHostObservers(host_));
+ }
+
+ void CheckObserverCount(int expected_temporary, int expected_persistent) {
+ ASSERT_TRUE(storage_monitor_->GetStorageTypeObservers(
+ kStorageTypeTemporary));
+ ASSERT_TRUE(storage_monitor_->GetStorageTypeObservers(
+ kStorageTypeTemporary)->GetHostObservers(host_));
+ EXPECT_EQ(expected_temporary, GetObserverCount(kStorageTypeTemporary));
+
+ ASSERT_TRUE(storage_monitor_->GetStorageTypeObservers(
+ kStorageTypePersistent));
+ ASSERT_TRUE(storage_monitor_->GetStorageTypeObservers(
+ kStorageTypePersistent)->GetHostObservers(host_));
+ EXPECT_EQ(expected_persistent, GetObserverCount(kStorageTypePersistent));
+ }
+
+ StorageMonitor* storage_monitor_;
+ StorageObserver::MonitorParams params1_;
+ StorageObserver::MonitorParams params2_;
+ MockObserver mock_observer1_;
+ MockObserver mock_observer2_;
+ MockObserver mock_observer3_;
+ std::string host_;
+};
+
+// Test adding storage observers.
+TEST_F(StorageMonitorTest, AddObservers) {
+ // Verify that the observers are added correctly.
+ CheckObserverCount(2, 3);
+}
+
+// Test dispatching events to storage observers.
+TEST_F(StorageMonitorTest, EventDispatch) {
+ // Verify dispatch of events.
+ const int64 kUsage = 5325;
+ const int64 kQuota = 903845;
+ quota_manager_->SetCallbackParams(kUsage, kQuota, kQuotaStatusOk);
+ storage_monitor_->NotifyUsageChange(params1_.filter, 9048543);
+
+ StorageObserver::Event expected_event(params1_.filter, kUsage, kQuota);
+ EXPECT_EQ(1, mock_observer1_.EventCount());
+ EXPECT_EQ(1, mock_observer2_.EventCount());
+ EXPECT_EQ(0, mock_observer3_.EventCount());
+ EXPECT_EQ(expected_event, mock_observer1_.LastEvent());
+ EXPECT_EQ(expected_event, mock_observer2_.LastEvent());
+}
+
+// Test removing all instances of an observer.
+TEST_F(StorageMonitorTest, RemoveObserver) {
+ storage_monitor_->RemoveObserver(&mock_observer1_);
+ CheckObserverCount(1, 2);
+}
+
+// Test removing an observer for a specific filter.
+TEST_F(StorageMonitorTest, RemoveObserverForFilter) {
+ storage_monitor_->RemoveObserverForFilter(&mock_observer1_, params2_.filter);
+ CheckObserverCount(2, 2);
+}
+
+// Integration test for QuotaManager and StorageMonitor:
+
+class StorageMonitorIntegrationTest : public testing::Test {
+ public:
+ virtual void SetUp() OVERRIDE {
+ ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
+ storage_policy_ = new MockSpecialStoragePolicy();
+ quota_manager_ = new QuotaManager(
+ false,
+ data_dir_.path(),
+ base::MessageLoopProxy::current().get(),
+ base::MessageLoopProxy::current().get(),
+ storage_policy_.get());
+
+ client_ = new MockStorageClient(quota_manager_->proxy(),
+ NULL,
+ QuotaClient::kFileSystem,
+ 0);
+
+ quota_manager_->proxy()->RegisterClient(client_);
+ }
+
+ virtual void TearDown() OVERRIDE {
+ // This ensures the quota manager is destroyed correctly.
+ quota_manager_ = NULL;
+ base::RunLoop().RunUntilIdle();
+ }
+
+ protected:
+ base::MessageLoop message_loop_;
+ base::ScopedTempDir data_dir_;
+ scoped_refptr<MockSpecialStoragePolicy> storage_policy_;
+ scoped_refptr<QuotaManager> quota_manager_;
+ MockStorageClient* client_;
+};
+
+// This test simulates a usage change in a quota client and verifies that a
+// storage observer will receive a storage event.
+TEST_F(StorageMonitorIntegrationTest, NotifyUsageEvent) {
+ const StorageType kTestStorageType = kStorageTypePersistent;
+ const int64 kTestUsage = 234743;
+
+ // Register the observer.
+ StorageObserver::MonitorParams params(kTestStorageType,
+ GURL(kDefaultOrigin),
+ base::TimeDelta::FromHours(1),
+ false);
+ MockObserver mock_observer;
+ quota_manager_->AddStorageObserver(&mock_observer, params);
+
+ // Fire a usage change.
+ client_->AddOriginAndNotify(GURL(kDefaultOrigin),
+ kTestStorageType,
+ kTestUsage);
+ base::RunLoop().RunUntilIdle();
+
+ // Verify that the observer receives it.
+ ASSERT_EQ(1, mock_observer.EventCount());
+ const StorageObserver::Event& event = mock_observer.LastEvent();
+ EXPECT_EQ(params.filter, event.filter);
+ EXPECT_EQ(kTestUsage, event.usage);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/quota/usage_tracker_unittest.cc b/chromium/content/browser/quota/usage_tracker_unittest.cc
new file mode 100644
index 00000000000..3cba8bd4791
--- /dev/null
+++ b/chromium/content/browser/quota/usage_tracker_unittest.cc
@@ -0,0 +1,336 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "content/public/test/mock_special_storage_policy.h"
+#include "net/base/net_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/quota/usage_tracker.h"
+
+using quota::kQuotaStatusOk;
+using quota::kStorageTypeTemporary;
+using quota::QuotaClient;
+using quota::QuotaClientList;
+using quota::SpecialStoragePolicy;
+using quota::StorageType;
+using quota::UsageTracker;
+
+namespace content {
+
+namespace {
+
+void DidGetGlobalUsage(bool* done,
+ int64* usage_out,
+ int64* unlimited_usage_out,
+ int64 usage,
+ int64 unlimited_usage) {
+ EXPECT_FALSE(*done);
+ *done = true;
+ *usage_out = usage;
+ *unlimited_usage_out = unlimited_usage;
+}
+
+void DidGetUsage(bool* done,
+ int64* usage_out,
+ int64 usage) {
+ EXPECT_FALSE(*done);
+ *done = true;
+ *usage_out = usage;
+}
+
+} // namespace
+
+class MockQuotaClient : public QuotaClient {
+ public:
+ MockQuotaClient() {}
+ virtual ~MockQuotaClient() {}
+
+ virtual ID id() const OVERRIDE {
+ return kFileSystem;
+ }
+
+ virtual void OnQuotaManagerDestroyed() OVERRIDE {}
+
+ virtual void GetOriginUsage(const GURL& origin,
+ StorageType type,
+ const GetUsageCallback& callback) OVERRIDE {
+ EXPECT_EQ(kStorageTypeTemporary, type);
+ int64 usage = GetUsage(origin);
+ base::MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(callback, usage));
+ }
+
+ virtual void GetOriginsForType(StorageType type,
+ const GetOriginsCallback& callback) OVERRIDE {
+ EXPECT_EQ(kStorageTypeTemporary, type);
+ std::set<GURL> origins;
+ for (UsageMap::const_iterator itr = usage_map_.begin();
+ itr != usage_map_.end(); ++itr) {
+ origins.insert(itr->first);
+ }
+ base::MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(callback, origins));
+ }
+
+ virtual void GetOriginsForHost(StorageType type,
+ const std::string& host,
+ const GetOriginsCallback& callback) OVERRIDE {
+ EXPECT_EQ(kStorageTypeTemporary, type);
+ std::set<GURL> origins;
+ for (UsageMap::const_iterator itr = usage_map_.begin();
+ itr != usage_map_.end(); ++itr) {
+ if (net::GetHostOrSpecFromURL(itr->first) == host)
+ origins.insert(itr->first);
+ }
+ base::MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(callback, origins));
+ }
+
+ virtual void DeleteOriginData(const GURL& origin,
+ StorageType type,
+ const DeletionCallback& callback) OVERRIDE {
+ EXPECT_EQ(kStorageTypeTemporary, type);
+ usage_map_.erase(origin);
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(callback, kQuotaStatusOk));
+ }
+
+ virtual bool DoesSupport(quota::StorageType type) const OVERRIDE {
+ return type == quota::kStorageTypeTemporary;
+ }
+
+ int64 GetUsage(const GURL& origin) {
+ UsageMap::const_iterator found = usage_map_.find(origin);
+ if (found == usage_map_.end())
+ return 0;
+ return found->second;
+ }
+
+ void SetUsage(const GURL& origin, int64 usage) {
+ usage_map_[origin] = usage;
+ }
+
+ int64 UpdateUsage(const GURL& origin, int64 delta) {
+ return usage_map_[origin] += delta;
+ }
+
+ private:
+ typedef std::map<GURL, int64> UsageMap;
+
+ UsageMap usage_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockQuotaClient);
+};
+
+class UsageTrackerTest : public testing::Test {
+ public:
+ UsageTrackerTest()
+ : storage_policy_(new MockSpecialStoragePolicy()),
+ usage_tracker_(GetUsageTrackerList(), kStorageTypeTemporary,
+ storage_policy_.get(), NULL) {
+ }
+
+ virtual ~UsageTrackerTest() {}
+
+ UsageTracker* usage_tracker() {
+ return &usage_tracker_;
+ }
+
+ void UpdateUsage(const GURL& origin, int64 delta) {
+ quota_client_.UpdateUsage(origin, delta);
+ usage_tracker_.UpdateUsageCache(quota_client_.id(), origin, delta);
+ base::RunLoop().RunUntilIdle();
+ }
+
+ void UpdateUsageWithoutNotification(const GURL& origin, int64 delta) {
+ quota_client_.UpdateUsage(origin, delta);
+ }
+
+ void GetGlobalLimitedUsage(int64* limited_usage) {
+ bool done = false;
+ usage_tracker_.GetGlobalLimitedUsage(base::Bind(
+ &DidGetUsage, &done, limited_usage));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(done);
+ }
+
+ void GetGlobalUsage(int64* usage, int64* unlimited_usage) {
+ bool done = false;
+ usage_tracker_.GetGlobalUsage(base::Bind(
+ &DidGetGlobalUsage,
+ &done, usage, unlimited_usage));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(done);
+ }
+
+ void GetHostUsage(const std::string& host, int64* usage) {
+ bool done = false;
+ usage_tracker_.GetHostUsage(host, base::Bind(&DidGetUsage, &done, usage));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(done);
+ }
+
+ void GrantUnlimitedStoragePolicy(const GURL& origin) {
+ if (!storage_policy_->IsStorageUnlimited(origin)) {
+ storage_policy_->AddUnlimited(origin);
+ storage_policy_->NotifyGranted(
+ origin, SpecialStoragePolicy::STORAGE_UNLIMITED);
+ }
+ }
+
+ void RevokeUnlimitedStoragePolicy(const GURL& origin) {
+ if (storage_policy_->IsStorageUnlimited(origin)) {
+ storage_policy_->RemoveUnlimited(origin);
+ storage_policy_->NotifyRevoked(
+ origin, SpecialStoragePolicy::STORAGE_UNLIMITED);
+ }
+ }
+
+ void SetUsageCacheEnabled(const GURL& origin, bool enabled) {
+ usage_tracker_.SetUsageCacheEnabled(
+ quota_client_.id(), origin, enabled);
+ }
+
+ private:
+ QuotaClientList GetUsageTrackerList() {
+ QuotaClientList client_list;
+ client_list.push_back(&quota_client_);
+ return client_list;
+ }
+
+ base::MessageLoop message_loop_;
+
+ scoped_refptr<MockSpecialStoragePolicy> storage_policy_;
+ MockQuotaClient quota_client_;
+ UsageTracker usage_tracker_;
+
+ DISALLOW_COPY_AND_ASSIGN(UsageTrackerTest);
+};
+
+TEST_F(UsageTrackerTest, GrantAndRevokeUnlimitedStorage) {
+ int64 usage = 0;
+ int64 unlimited_usage = 0;
+ int64 host_usage = 0;
+ GetGlobalUsage(&usage, &unlimited_usage);
+ EXPECT_EQ(0, usage);
+ EXPECT_EQ(0, unlimited_usage);
+
+ const GURL origin("http://example.com");
+ const std::string host(net::GetHostOrSpecFromURL(origin));
+
+ UpdateUsage(origin, 100);
+ GetGlobalUsage(&usage, &unlimited_usage);
+ GetHostUsage(host, &host_usage);
+ EXPECT_EQ(100, usage);
+ EXPECT_EQ(0, unlimited_usage);
+ EXPECT_EQ(100, host_usage);
+
+ GrantUnlimitedStoragePolicy(origin);
+ GetGlobalUsage(&usage, &unlimited_usage);
+ GetHostUsage(host, &host_usage);
+ EXPECT_EQ(100, usage);
+ EXPECT_EQ(100, unlimited_usage);
+ EXPECT_EQ(100, host_usage);
+
+ RevokeUnlimitedStoragePolicy(origin);
+ GetGlobalUsage(&usage, &unlimited_usage);
+ GetHostUsage(host, &host_usage);
+ EXPECT_EQ(100, usage);
+ EXPECT_EQ(0, unlimited_usage);
+ EXPECT_EQ(100, host_usage);
+}
+
+TEST_F(UsageTrackerTest, CacheDisabledClientTest) {
+ int64 usage = 0;
+ int64 unlimited_usage = 0;
+ int64 host_usage = 0;
+
+ const GURL origin("http://example.com");
+ const std::string host(net::GetHostOrSpecFromURL(origin));
+
+ UpdateUsage(origin, 100);
+ GetGlobalUsage(&usage, &unlimited_usage);
+ GetHostUsage(host, &host_usage);
+ EXPECT_EQ(100, usage);
+ EXPECT_EQ(0, unlimited_usage);
+ EXPECT_EQ(100, host_usage);
+
+ UpdateUsageWithoutNotification(origin, 100);
+ GetGlobalUsage(&usage, &unlimited_usage);
+ GetHostUsage(host, &host_usage);
+ EXPECT_EQ(100, usage);
+ EXPECT_EQ(0, unlimited_usage);
+ EXPECT_EQ(100, host_usage);
+
+ GrantUnlimitedStoragePolicy(origin);
+ UpdateUsageWithoutNotification(origin, 100);
+ SetUsageCacheEnabled(origin, false);
+ UpdateUsageWithoutNotification(origin, 100);
+
+ GetGlobalUsage(&usage, &unlimited_usage);
+ GetHostUsage(host, &host_usage);
+ EXPECT_EQ(400, usage);
+ EXPECT_EQ(400, unlimited_usage);
+ EXPECT_EQ(400, host_usage);
+
+ RevokeUnlimitedStoragePolicy(origin);
+ GetGlobalUsage(&usage, &unlimited_usage);
+ GetHostUsage(host, &host_usage);
+ EXPECT_EQ(400, usage);
+ EXPECT_EQ(0, unlimited_usage);
+ EXPECT_EQ(400, host_usage);
+
+ SetUsageCacheEnabled(origin, true);
+ UpdateUsage(origin, 100);
+
+ GetGlobalUsage(&usage, &unlimited_usage);
+ GetHostUsage(host, &host_usage);
+ EXPECT_EQ(500, usage);
+ EXPECT_EQ(0, unlimited_usage);
+ EXPECT_EQ(500, host_usage);
+}
+
+TEST_F(UsageTrackerTest, LimitedGlobalUsageTest) {
+ const GURL kNormal("http://normal");
+ const GURL kUnlimited("http://unlimited");
+ const GURL kNonCached("http://non_cached");
+ const GURL kNonCachedUnlimited("http://non_cached-unlimited");
+
+ GrantUnlimitedStoragePolicy(kUnlimited);
+ GrantUnlimitedStoragePolicy(kNonCachedUnlimited);
+
+ SetUsageCacheEnabled(kNonCached, false);
+ SetUsageCacheEnabled(kNonCachedUnlimited, false);
+
+ UpdateUsageWithoutNotification(kNormal, 1);
+ UpdateUsageWithoutNotification(kUnlimited, 2);
+ UpdateUsageWithoutNotification(kNonCached, 4);
+ UpdateUsageWithoutNotification(kNonCachedUnlimited, 8);
+
+ int64 limited_usage = 0;
+ int64 total_usage = 0;
+ int64 unlimited_usage = 0;
+
+ GetGlobalLimitedUsage(&limited_usage);
+ GetGlobalUsage(&total_usage, &unlimited_usage);
+ EXPECT_EQ(1 + 4, limited_usage);
+ EXPECT_EQ(1 + 2 + 4 + 8, total_usage);
+ EXPECT_EQ(2 + 8, unlimited_usage);
+
+ UpdateUsageWithoutNotification(kNonCached, 16 - 4);
+ UpdateUsageWithoutNotification(kNonCachedUnlimited, 32 - 8);
+
+ GetGlobalLimitedUsage(&limited_usage);
+ GetGlobalUsage(&total_usage, &unlimited_usage);
+ EXPECT_EQ(1 + 16, limited_usage);
+ EXPECT_EQ(1 + 2 + 16 + 32, total_usage);
+ EXPECT_EQ(2 + 32, unlimited_usage);
+}
+
+
+} // namespace content
diff --git a/chromium/content/browser/quota_dispatcher_host.cc b/chromium/content/browser/quota_dispatcher_host.cc
index f83ee3f7be9..fcee63396e5 100644
--- a/chromium/content/browser/quota_dispatcher_host.cc
+++ b/chromium/content/browser/quota_dispatcher_host.cc
@@ -6,6 +6,7 @@
#include "base/bind.h"
#include "base/memory/weak_ptr.h"
+#include "base/numerics/safe_conversions.h"
#include "content/common/quota_messages.h"
#include "content/public/browser/quota_permission_context.h"
#include "net/base/net_util.h"
@@ -99,75 +100,72 @@ class QuotaDispatcherHost::RequestQuotaDispatcher
typedef RequestQuotaDispatcher self_type;
RequestQuotaDispatcher(base::WeakPtr<QuotaDispatcherHost> dispatcher_host,
- int request_id,
- const GURL& origin,
- StorageType type,
- int64 requested_quota,
- int render_view_id)
- : RequestDispatcher(dispatcher_host, request_id),
- origin_(origin),
- host_(net::GetHostOrSpecFromURL(origin)),
- type_(type),
+ const StorageQuotaParams& params)
+ : RequestDispatcher(dispatcher_host, params.request_id),
+ params_(params),
+ current_usage_(0),
current_quota_(0),
- requested_quota_(requested_quota),
- render_view_id_(render_view_id),
- weak_factory_(this) {}
+ requested_quota_(0),
+ weak_factory_(this) {
+ // Convert the requested size from uint64 to int64 since the quota backend
+ // requires int64 values.
+ // TODO(nhiroki): The backend should accept uint64 values.
+ requested_quota_ = base::saturated_cast<int64>(params_.requested_size);
+ }
virtual ~RequestQuotaDispatcher() {}
void Start() {
DCHECK(dispatcher_host());
- DCHECK(type_ == quota::kStorageTypeTemporary ||
- type_ == quota::kStorageTypePersistent ||
- type_ == quota::kStorageTypeSyncable);
- if (type_ == quota::kStorageTypePersistent) {
- quota_manager()->GetPersistentHostQuota(
- host_,
- base::Bind(&self_type::DidGetHostQuota,
- weak_factory_.GetWeakPtr(), host_, type_));
+
+ DCHECK(params_.storage_type == quota::kStorageTypeTemporary ||
+ params_.storage_type == quota::kStorageTypePersistent ||
+ params_.storage_type == quota::kStorageTypeSyncable);
+ if (params_.storage_type == quota::kStorageTypePersistent) {
+ quota_manager()->GetUsageAndQuotaForWebApps(
+ params_.origin_url, params_.storage_type,
+ base::Bind(&self_type::DidGetPersistentUsageAndQuota,
+ weak_factory_.GetWeakPtr()));
} else {
quota_manager()->GetUsageAndQuotaForWebApps(
- origin_, type_,
+ params_.origin_url, params_.storage_type,
base::Bind(&self_type::DidGetTemporaryUsageAndQuota,
weak_factory_.GetWeakPtr()));
}
}
private:
- void DidGetHostQuota(const std::string& host,
- StorageType type,
- QuotaStatusCode status,
- int64 quota) {
+ void DidGetPersistentUsageAndQuota(QuotaStatusCode status,
+ int64 usage,
+ int64 quota) {
if (!dispatcher_host())
return;
- DCHECK_EQ(type_, type);
- DCHECK_EQ(host_, host);
if (status != quota::kQuotaStatusOk) {
- DidFinish(status, 0);
- return;
- }
- if (requested_quota_ < 0) {
- DidFinish(quota::kQuotaErrorInvalidModification, 0);
+ DidFinish(status, 0, 0);
return;
}
- if (requested_quota_ <= quota) {
+
+ if (quota_manager()->IsStorageUnlimited(params_.origin_url,
+ params_.storage_type) ||
+ requested_quota_ <= quota) {
// Seems like we can just let it go.
- DidFinish(quota::kQuotaStatusOk, requested_quota_);
+ DidFinish(quota::kQuotaStatusOk, usage, params_.requested_size);
return;
}
+ current_usage_ = usage;
current_quota_ = quota;
+
// Otherwise we need to consult with the permission context and
- // possibly show an infobar.
+ // possibly show a prompt.
DCHECK(permission_context());
- permission_context()->RequestQuotaPermission(
- origin_, type_, requested_quota_, render_process_id(), render_view_id_,
+ permission_context()->RequestQuotaPermission(params_, render_process_id(),
base::Bind(&self_type::DidGetPermissionResponse,
weak_factory_.GetWeakPtr()));
}
void DidGetTemporaryUsageAndQuota(QuotaStatusCode status,
- int64 usage_unused,
+ int64 usage,
int64 quota) {
- DidFinish(status, std::min(requested_quota_, quota));
+ DidFinish(status, usage, std::min(requested_quota_, quota));
}
void DidGetPermissionResponse(
@@ -176,20 +174,22 @@ class QuotaDispatcherHost::RequestQuotaDispatcher
return;
if (response != QuotaPermissionContext::QUOTA_PERMISSION_RESPONSE_ALLOW) {
// User didn't allow the new quota. Just returning the current quota.
- DidFinish(quota::kQuotaStatusOk, current_quota_);
+ DidFinish(quota::kQuotaStatusOk, current_usage_, current_quota_);
return;
}
// Now we're allowed to set the new quota.
quota_manager()->SetPersistentHostQuota(
- host_, requested_quota_,
+ net::GetHostOrSpecFromURL(params_.origin_url), params_.requested_size,
base::Bind(&self_type::DidSetHostQuota, weak_factory_.GetWeakPtr()));
}
void DidSetHostQuota(QuotaStatusCode status, int64 new_quota) {
- DidFinish(status, new_quota);
+ DidFinish(status, current_usage_, new_quota);
}
- void DidFinish(QuotaStatusCode status, int64 granted_quota) {
+ void DidFinish(QuotaStatusCode status,
+ int64 usage,
+ int64 granted_quota) {
if (!dispatcher_host())
return;
DCHECK(dispatcher_host());
@@ -197,17 +197,15 @@ class QuotaDispatcherHost::RequestQuotaDispatcher
dispatcher_host()->Send(new QuotaMsg_DidFail(request_id(), status));
} else {
dispatcher_host()->Send(new QuotaMsg_DidGrantStorageQuota(
- request_id(), granted_quota));
+ request_id(), usage, granted_quota));
}
Completed();
}
- const GURL origin_;
- const std::string host_;
- const StorageType type_;
+ StorageQuotaParams params_;
+ int64 current_usage_;
int64 current_quota_;
- const int64 requested_quota_;
- const int render_view_id_;
+ int64 requested_quota_;
base::WeakPtrFactory<self_type> weak_factory_;
};
@@ -215,23 +213,22 @@ QuotaDispatcherHost::QuotaDispatcherHost(
int process_id,
QuotaManager* quota_manager,
QuotaPermissionContext* permission_context)
- : process_id_(process_id),
+ : BrowserMessageFilter(QuotaMsgStart),
+ process_id_(process_id),
quota_manager_(quota_manager),
permission_context_(permission_context),
weak_factory_(this) {
}
-bool QuotaDispatcherHost::OnMessageReceived(
- const IPC::Message& message, bool* message_was_ok) {
- *message_was_ok = true;
+bool QuotaDispatcherHost::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(QuotaDispatcherHost, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(QuotaDispatcherHost, message)
IPC_MESSAGE_HANDLER(QuotaHostMsg_QueryStorageUsageAndQuota,
OnQueryStorageUsageAndQuota)
IPC_MESSAGE_HANDLER(QuotaHostMsg_RequestStorageQuota,
OnRequestStorageQuota)
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return handled;
}
@@ -247,27 +244,18 @@ void QuotaDispatcherHost::OnQueryStorageUsageAndQuota(
}
void QuotaDispatcherHost::OnRequestStorageQuota(
- int render_view_id,
- int request_id,
- const GURL& origin,
- StorageType type,
- int64 requested_size) {
- if (quota_manager_->IsStorageUnlimited(origin, type)) {
- // If the origin is marked 'unlimited' we always just return ok.
- Send(new QuotaMsg_DidGrantStorageQuota(request_id, requested_size));
- return;
- }
-
- if (type != quota::kStorageTypeTemporary &&
- type != quota::kStorageTypePersistent) {
+ const StorageQuotaParams& params) {
+ if (params.storage_type != quota::kStorageTypeTemporary &&
+ params.storage_type != quota::kStorageTypePersistent) {
// Unsupported storage types.
- Send(new QuotaMsg_DidFail(request_id, quota::kQuotaErrorNotSupported));
+ Send(new QuotaMsg_DidFail(params.request_id,
+ quota::kQuotaErrorNotSupported));
return;
}
- RequestQuotaDispatcher* dispatcher = new RequestQuotaDispatcher(
- weak_factory_.GetWeakPtr(), request_id, origin, type,
- requested_size, render_view_id);
+ RequestQuotaDispatcher* dispatcher =
+ new RequestQuotaDispatcher(weak_factory_.GetWeakPtr(),
+ params);
dispatcher->Start();
}
diff --git a/chromium/content/browser/quota_dispatcher_host.h b/chromium/content/browser/quota_dispatcher_host.h
index 64469c1a49a..d241ab59c37 100644
--- a/chromium/content/browser/quota_dispatcher_host.h
+++ b/chromium/content/browser/quota_dispatcher_host.h
@@ -22,6 +22,7 @@ class QuotaManager;
namespace content {
class QuotaPermissionContext;
+struct StorageQuotaParams;
class QuotaDispatcherHost : public BrowserMessageFilter {
public:
@@ -30,8 +31,7 @@ class QuotaDispatcherHost : public BrowserMessageFilter {
QuotaPermissionContext* permission_context);
// BrowserMessageFilter:
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
protected:
virtual ~QuotaDispatcherHost();
@@ -45,12 +45,7 @@ class QuotaDispatcherHost : public BrowserMessageFilter {
int request_id,
const GURL& origin_url,
quota::StorageType type);
- void OnRequestStorageQuota(
- int render_view_id,
- int request_id,
- const GURL& origin_url,
- quota::StorageType type,
- int64 requested_size);
+ void OnRequestStorageQuota(const StorageQuotaParams& params);
// The ID of this process.
int process_id_;
diff --git a/chromium/content/browser/renderer_data_memoizing_store.h b/chromium/content/browser/renderer_data_memoizing_store.h
index ff55982b1a8..ce8e2937380 100644
--- a/chromium/content/browser/renderer_data_memoizing_store.h
+++ b/chromium/content/browser/renderer_data_memoizing_store.h
@@ -9,8 +9,8 @@
#include "base/bind.h"
#include "base/synchronization/lock.h"
-#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_process_host_observer.h"
namespace content {
@@ -90,6 +90,7 @@ class RendererDataMemoizingStore : public RenderProcessHostObserver {
}
}
+ DCHECK(item_id);
return item_id;
}
diff --git a/chromium/content/browser/renderer_host/DEPS b/chromium/content/browser/renderer_host/DEPS
index 1d612c46229..55a8ec5f01d 100644
--- a/chromium/content/browser/renderer_host/DEPS
+++ b/chromium/content/browser/renderer_host/DEPS
@@ -22,18 +22,30 @@ specific_include_rules = {
"+content/public/browser/web_contents_view.h",
"+media/filters",
],
- "render_sandbox_host_linux\.cc": [
+ "sandbox_ipc_linux\.cc": [
"+third_party/WebKit/public/platform/linux/WebFontInfo.h",
"+third_party/WebKit/public/web/WebKit.h",
],
+ "sandbox_ipc_linux\.h": [
+ "+content/child/blink_platform_impl.h",
+ ],
"render_process_host_impl\.cc": [
"+content/browser/frame_host/render_frame_message_filter.h",
],
+ "render_widget_host_view_mac\.mm": [
+ "+content/browser/frame_host",
+ "+content/public/browser/web_contents.h",
+ ],
+ "ime_adapter_android\.cc": [
+ "+content/browser/frame_host",
+ "+content/public/browser/web_contents.h",
+ ],
# TODO(nasko): Remove these exceptions once we've untangled the dependency
# of RenderViewHost on the FrameTree.
"render_view_host_impl\.(cc|h)": [
"+content/browser/frame_host/frame_tree.h",
- "+content/browser/frame_host/render_frame_host_factory.h",
- "+content/browser/frame_host/render_frame_host_impl.h",
+ ],
+ "render_widget_host_view_aura\.cc": [
+ "+content/browser/frame_host",
],
}
diff --git a/chromium/content/browser/renderer_host/OWNERS b/chromium/content/browser/renderer_host/OWNERS
index 93266a3f46b..9d25787925e 100644
--- a/chromium/content/browser/renderer_host/OWNERS
+++ b/chromium/content/browser/renderer_host/OWNERS
@@ -22,3 +22,9 @@ sadrul@chromium.org
per-file *websocket*=ricea@chromium.org
per-file *websocket*=tyoshino@chromium.org
per-file *websocket*=yhirano@chromium.org
+
+# Linux sandboxing
+per-file render_sandbox_host_linux.*=jln@chromium.org
+per-file render_sandbox_host_linux.*=jorgelo@chromium.org
+per-file sandbox_ipc_linux.*=jln@chromium.org
+per-file sandbox_ipc_linux.*=jorgelo@chromium.org
diff --git a/chromium/content/browser/renderer_host/backing_store.cc b/chromium/content/browser/renderer_host/backing_store.cc
deleted file mode 100644
index 8557437fb25..00000000000
--- a/chromium/content/browser/renderer_host/backing_store.cc
+++ /dev/null
@@ -1,21 +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/renderer_host/backing_store.h"
-
-namespace content {
-
-BackingStore::BackingStore(RenderWidgetHost* widget, const gfx::Size& size)
- : render_widget_host_(widget),
- size_(size) {
-}
-
-BackingStore::~BackingStore() {
-}
-
-size_t BackingStore::MemorySize() {
- return size_.GetArea() * 4;
-}
-
-} // namespace content
diff --git a/chromium/content/browser/renderer_host/backing_store.h b/chromium/content/browser/renderer_host/backing_store.h
deleted file mode 100644
index ba36920401c..00000000000
--- a/chromium/content/browser/renderer_host/backing_store.h
+++ /dev/null
@@ -1,93 +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_RENDERER_HOST_BACKING_STORE_H_
-#define CONTENT_BROWSER_RENDERER_HOST_BACKING_STORE_H_
-
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/callback_forward.h"
-#include "content/common/content_export.h"
-#include "ui/gfx/size.h"
-#include "ui/gfx/vector2d.h"
-#include "ui/surface/transport_dib.h"
-
-class RenderProcessHost;
-
-namespace gfx {
-class Rect;
-}
-
-namespace skia {
-class PlatformBitmap;
-}
-
-namespace content {
-class RenderProcessHost;
-class RenderWidgetHost;
-
-// Represents a backing store for the pixels in a RenderWidgetHost.
-class CONTENT_EXPORT BackingStore {
- public:
- virtual ~BackingStore();
-
- RenderWidgetHost* render_widget_host() const {
- return render_widget_host_;
- }
- const gfx::Size& size() { return size_; }
-
- // The number of bytes that this backing store consumes. The default
- // implementation just assumes there's 32 bits per pixel over the current
- // size of the screen. Implementations may override this if they have more
- // information about the color depth.
- virtual size_t MemorySize();
-
- // Paints the bitmap from the renderer onto the backing store. bitmap_rect
- // gives the location of bitmap, and copy_rects specifies the subregion(s) of
- // the backingstore to be painted from the bitmap. All coordinates are in
- // DIPs. |scale_factor| contains the expected device scale factor of the
- // backing store.
- //
- // PaintToBackingStore does not need to guarantee that this has happened by
- // the time it returns, in which case it will set |scheduled_callback| to
- // true and will call |callback| when completed.
- virtual void PaintToBackingStore(
- RenderProcessHost* process,
- TransportDIB::Id bitmap,
- const gfx::Rect& bitmap_rect,
- const std::vector<gfx::Rect>& copy_rects,
- float scale_factor,
- const base::Closure& completion_callback,
- bool* scheduled_completion_callback) = 0;
-
- // Extracts the gives subset of the backing store and copies it to the given
- // PlatformCanvas. The PlatformCanvas should not be initialized. This function
- // will call initialize() with the correct size. The return value indicates
- // success.
- virtual bool CopyFromBackingStore(const gfx::Rect& rect,
- skia::PlatformBitmap* output) = 0;
-
- // Scrolls the contents of clip_rect in the backing store by |delta| (but
- // |delta|.x() and |delta|.y() cannot both be non-zero).
- virtual void ScrollBackingStore(const gfx::Vector2d& delta,
- const gfx::Rect& clip_rect,
- const gfx::Size& view_size) = 0;
- protected:
- // Can only be constructed via subclasses.
- BackingStore(RenderWidgetHost* widget, const gfx::Size& size);
-
- private:
- // The owner of this backing store.
- RenderWidgetHost* render_widget_host_;
-
- // The size of the backing store.
- gfx::Size size_;
-
- DISALLOW_COPY_AND_ASSIGN(BackingStore);
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_RENDERER_HOST_BACKING_STORE_H_
diff --git a/chromium/content/browser/renderer_host/backing_store_aura.cc b/chromium/content/browser/renderer_host/backing_store_aura.cc
deleted file mode 100644
index 105bf446acd..00000000000
--- a/chromium/content/browser/renderer_host/backing_store_aura.cc
+++ /dev/null
@@ -1,176 +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/renderer_host/backing_store_aura.h"
-
-#include "content/browser/renderer_host/dip_util.h"
-#include "content/browser/renderer_host/render_process_host_impl.h"
-#include "content/public/browser/render_widget_host.h"
-#include "skia/ext/platform_canvas.h"
-#include "third_party/skia/include/core/SkCanvas.h"
-#include "ui/gfx/canvas.h"
-#include "ui/gfx/image/image_skia.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/rect_conversions.h"
-#include "ui/gfx/size_conversions.h"
-#include "ui/gfx/vector2d_conversions.h"
-
-namespace {
-
-gfx::Size ToPixelSize(gfx::Size dipSize, float scale) {
- return gfx::ToCeiledSize(gfx::ScaleSize(dipSize, scale));
-}
-
-} // namespace
-
-
-namespace content {
-
-// Assume that somewhere along the line, someone will do width * height * 4
-// with signed numbers. If the maximum value is 2**31, then 2**31 / 4 =
-// 2**29 and floor(sqrt(2**29)) = 23170.
-
-// Max height and width for layers
-static const int kMaxVideoLayerSize = 23170;
-
-BackingStoreAura::BackingStoreAura(RenderWidgetHost* widget,
- const gfx::Size& size)
- : BackingStore(widget, size) {
- device_scale_factor_ =
- ui::GetImageScale(GetScaleFactorForView(widget->GetView()));
- gfx::Size pixel_size = ToPixelSize(size, device_scale_factor_);
- bitmap_.setConfig(SkBitmap::kARGB_8888_Config,
- pixel_size.width(), pixel_size.height());
- bitmap_.allocPixels();
- canvas_.reset(new SkCanvas(bitmap_));
-}
-
-BackingStoreAura::~BackingStoreAura() {
-}
-
-void BackingStoreAura::SkiaShowRect(const gfx::Point& point,
- gfx::Canvas* canvas) {
- gfx::ImageSkia image = gfx::ImageSkia(gfx::ImageSkiaRep(bitmap_,
- device_scale_factor_));
- canvas->DrawImageInt(image, point.x(), point.y());
-}
-
-void BackingStoreAura::ScaleFactorChanged(float device_scale_factor) {
- if (device_scale_factor == device_scale_factor_)
- return;
-
- gfx::Size old_pixel_size = ToPixelSize(size(), device_scale_factor_);
- device_scale_factor_ = device_scale_factor;
-
- gfx::Size pixel_size = ToPixelSize(size(), device_scale_factor_);
- SkBitmap new_bitmap;
- new_bitmap.setConfig(SkBitmap::kARGB_8888_Config,
- pixel_size.width(), pixel_size.height());
- new_bitmap.allocPixels();
- scoped_ptr<SkCanvas> new_canvas(new SkCanvas(new_bitmap));
-
- // Copy old contents; a low-res flash is better than a black flash.
- SkPaint copy_paint;
- copy_paint.setXfermodeMode(SkXfermode::kSrc_Mode);
- SkIRect src_rect = SkIRect::MakeWH(old_pixel_size.width(),
- old_pixel_size.height());
- SkRect dst_rect = SkRect::MakeWH(pixel_size.width(), pixel_size.height());
- new_canvas.get()->drawBitmapRect(bitmap_, &src_rect, dst_rect, &copy_paint);
-
- canvas_.swap(new_canvas);
- bitmap_ = new_bitmap;
-}
-
-size_t BackingStoreAura::MemorySize() {
- // NOTE: The computation may be different when the canvas is a subrectangle of
- // a larger bitmap.
- return ToPixelSize(size(), device_scale_factor_).GetArea() * 4;
-}
-
-void BackingStoreAura::PaintToBackingStore(
- RenderProcessHost* process,
- TransportDIB::Id bitmap,
- const gfx::Rect& bitmap_rect,
- const std::vector<gfx::Rect>& copy_rects,
- float scale_factor,
- const base::Closure& completion_callback,
- bool* scheduled_completion_callback) {
- *scheduled_completion_callback = false;
- if (bitmap_rect.IsEmpty())
- return;
-
- gfx::Rect pixel_bitmap_rect = gfx::ToEnclosingRect(
- gfx::ScaleRect(bitmap_rect, scale_factor));
-
- const int width = pixel_bitmap_rect.width();
- const int height = pixel_bitmap_rect.height();
-
- if (width <= 0 || width > kMaxVideoLayerSize ||
- height <= 0 || height > kMaxVideoLayerSize)
- return;
-
- TransportDIB* dib = process->GetTransportDIB(bitmap);
- if (!dib)
- return;
-
- SkPaint copy_paint;
- copy_paint.setXfermodeMode(SkXfermode::kSrc_Mode);
-
- SkBitmap sk_bitmap;
- sk_bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
- sk_bitmap.setPixels(dib->memory());
- for (size_t i = 0; i < copy_rects.size(); i++) {
- const gfx::Rect pixel_copy_rect = gfx::ToEnclosingRect(
- gfx::ScaleRect(copy_rects[i], scale_factor));
- int x = pixel_copy_rect.x() - pixel_bitmap_rect.x();
- int y = pixel_copy_rect.y() - pixel_bitmap_rect.y();
- SkIRect srcrect = SkIRect::MakeXYWH(x, y,
- pixel_copy_rect.width(),
- pixel_copy_rect.height());
-
- const gfx::Rect pixel_copy_dst_rect = gfx::ToEnclosingRect(
- gfx::ScaleRect(copy_rects[i], device_scale_factor_));
- SkRect dstrect = SkRect::MakeXYWH(
- SkIntToScalar(pixel_copy_dst_rect.x()),
- SkIntToScalar(pixel_copy_dst_rect.y()),
- SkIntToScalar(pixel_copy_dst_rect.width()),
- SkIntToScalar(pixel_copy_dst_rect.height()));
- canvas_.get()->drawBitmapRect(sk_bitmap, &srcrect, dstrect, &copy_paint);
- }
-}
-
-void BackingStoreAura::ScrollBackingStore(const gfx::Vector2d& delta,
- const gfx::Rect& clip_rect,
- const gfx::Size& view_size) {
- gfx::Rect pixel_rect = gfx::ToEnclosingRect(
- gfx::ScaleRect(clip_rect, device_scale_factor_));
- gfx::Vector2d pixel_delta = gfx::ToFlooredVector2d(
- gfx::ScaleVector2d(delta, device_scale_factor_));
-
- int x = std::min(pixel_rect.x(), pixel_rect.x() - pixel_delta.x());
- int y = std::min(pixel_rect.y(), pixel_rect.y() - pixel_delta.y());
- int w = pixel_rect.width() + abs(pixel_delta.x());
- int h = pixel_rect.height() + abs(pixel_delta.y());
- SkIRect rect = SkIRect::MakeXYWH(x, y, w, h);
- bitmap_.scrollRect(&rect, pixel_delta.x(), pixel_delta.y());
-}
-
-bool BackingStoreAura::CopyFromBackingStore(const gfx::Rect& rect,
- skia::PlatformBitmap* output) {
- const int width =
- std::min(size().width(), rect.width()) * device_scale_factor_;
- const int height =
- std::min(size().height(), rect.height()) * device_scale_factor_;
- if (!output->Allocate(width, height, true))
- return false;
-
- SkIRect skrect = SkIRect::MakeXYWH(rect.x(), rect.y(), width, height);
- SkBitmap b;
- if (!canvas_->readPixels(skrect, &b))
- return false;
- SkCanvas(output->GetBitmap()).writePixels(b, rect.x(), rect.y());
- return true;
-}
-
-} // namespace content
diff --git a/chromium/content/browser/renderer_host/backing_store_aura.h b/chromium/content/browser/renderer_host/backing_store_aura.h
deleted file mode 100644
index 0b8e0729d0e..00000000000
--- a/chromium/content/browser/renderer_host/backing_store_aura.h
+++ /dev/null
@@ -1,66 +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_RENDERER_HOST_BACKING_STORE_AURA_H_
-#define CONTENT_BROWSER_RENDERER_HOST_BACKING_STORE_AURA_H_
-
-#include "base/memory/scoped_ptr.h"
-#include "content/browser/renderer_host/backing_store.h"
-#include "content/common/content_export.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-
-class SkCanvas;
-
-namespace gfx {
-class Point;
-class Canvas;
-}
-
-namespace content {
-class RenderProcessHost;
-
-// A backing store that uses skia. This is the backing store used by
-// RenderWidgetHostViewAura.
-class BackingStoreAura : public BackingStore {
- public:
- CONTENT_EXPORT BackingStoreAura(
- RenderWidgetHost* widget,
- const gfx::Size& size);
-
- virtual ~BackingStoreAura();
-
- CONTENT_EXPORT void SkiaShowRect(const gfx::Point& point,
- gfx::Canvas* canvas);
-
- // Called when the view's backing scale factor changes.
- void ScaleFactorChanged(float device_scale_factor);
-
- // BackingStore implementation.
- virtual size_t MemorySize() OVERRIDE;
- virtual void PaintToBackingStore(
- RenderProcessHost* process,
- TransportDIB::Id bitmap,
- const gfx::Rect& bitmap_rect,
- const std::vector<gfx::Rect>& copy_rects,
- float scale_factor,
- const base::Closure& completion_callback,
- bool* scheduled_completion_callback) OVERRIDE;
- virtual bool CopyFromBackingStore(const gfx::Rect& rect,
- skia::PlatformBitmap* output) OVERRIDE;
- virtual void ScrollBackingStore(const gfx::Vector2d& delta,
- const gfx::Rect& clip_rect,
- const gfx::Size& view_size) OVERRIDE;
-
- private:
- SkBitmap bitmap_;
-
- scoped_ptr<SkCanvas> canvas_;
- float device_scale_factor_;
-
- DISALLOW_COPY_AND_ASSIGN(BackingStoreAura);
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_RENDERER_HOST_BACKING_STORE_AURA_H_
diff --git a/chromium/content/browser/renderer_host/backing_store_gtk.cc b/chromium/content/browser/renderer_host/backing_store_gtk.cc
deleted file mode 100644
index 147ee4d224e..00000000000
--- a/chromium/content/browser/renderer_host/backing_store_gtk.cc
+++ /dev/null
@@ -1,692 +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/renderer_host/backing_store_gtk.h"
-
-#include <cairo-xlib.h>
-#include <gtk/gtk.h>
-#include <stdlib.h>
-#include <sys/ipc.h>
-#include <sys/shm.h>
-#include <X11/extensions/sync.h>
-
-#if defined(OS_OPENBSD) || defined(OS_FREEBSD)
-#include <sys/endian.h>
-#endif
-
-#include <algorithm>
-#include <limits>
-#include <queue>
-#include <utility>
-
-#include "base/compiler_specific.h"
-#include "base/logging.h"
-#include "base/memory/singleton.h"
-#include "base/metrics/histogram.h"
-#include "base/time/time.h"
-#include "content/browser/renderer_host/render_process_host_impl.h"
-#include "skia/ext/platform_canvas.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "ui/base/gtk/gtk_signal.h"
-#include "ui/base/x/x11_util_internal.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/rect_conversions.h"
-#include "ui/gfx/x/x11_types.h"
-#include "ui/surface/transport_dib.h"
-
-namespace content {
-namespace {
-
-// Assume that somewhere along the line, someone will do width * height * 4
-// with signed numbers. If the maximum value is 2**31, then 2**31 / 4 =
-// 2**29 and floor(sqrt(2**29)) = 23170.
-
-// Max height and width for layers
-static const int kMaxVideoLayerSize = 23170;
-
-
-// X Backing Stores:
-//
-// Unlike Windows, where the backing store is kept in heap memory, we keep our
-// backing store in the X server, as a pixmap. Thus expose events just require
-// instructing the X server to copy from the backing store to the window.
-//
-// The backing store is in the same format as the visual which our main window
-// is using. Bitmaps from the renderer are uploaded to the X server, either via
-// shared memory or over the wire, and XRENDER is used to convert them to the
-// correct format for the backing store.
-
-// Destroys the image and the associated shared memory structures. This is a
-// helper function for code using shared memory.
-void DestroySharedImage(XDisplay* display,
- XImage* image,
- XShmSegmentInfo* shminfo) {
- XShmDetach(display, shminfo);
- XDestroyImage(image);
- shmdt(shminfo->shmaddr);
-}
-
-// So we don't don't want to call XSync(), which can block the UI loop for
-// ~100ms on first paint and is generally slow. We optionally use the
-// XSyncExtension to push a callback into the X11 event queue and get a
-// callback instead of blocking until the event queue is cleared.
-//
-// TODO(erg): If gfx::GetXDisplay() ever gets fixed to handle multiple Displays,
-// this must be modified to be per Display instead of a Singleton.
-class XSyncHandler {
- public:
- static XSyncHandler* GetInstance() {
- return Singleton<XSyncHandler>::get();
- }
-
- bool Enabled() {
- return loaded_extension_;
- }
-
- void PushPaintCounter(TransportDIB* dib,
- XDisplay* display,
- Picture picture,
- Pixmap pixmap,
- const base::Closure& completion_callback);
-
- private:
- friend struct DefaultSingletonTraits<XSyncHandler>;
-
- // A struct that has cleanup and callback tasks that were queued into the
- // future and are run on |g_backing_store_sync_alarm| firing.
- struct BackingStoreEvents {
- BackingStoreEvents(TransportDIB* dib, XDisplay* d, Picture pic, Pixmap pix,
- const base::Closure& c)
- : dib(dib),
- display(d),
- picture(pic),
- pixmap(pix),
- closure(c) {
- dib->IncreaseInFlightCounter();
- }
-
- TransportDIB* dib;
-
- // The display we're running on.
- XDisplay* display;
-
- // Data to delete.
- Picture picture;
- Pixmap pixmap;
-
- // Callback once everything else is done.
- base::Closure closure;
- };
-
- XSyncHandler();
- ~XSyncHandler();
-
- // An event filter notified about all XEvents. We then filter out XSync
- // events that are on counters that we made.
- CHROMEG_CALLBACK_1(XSyncHandler, GdkFilterReturn, OnEvent, GdkXEvent*,
- GdkEvent*);
-
- // Whether we successfully loaded XSyncExtension.
- bool loaded_extension_;
-
- // The event ids returned to us by XSyncQueryExtension().
- int xsync_event_base_;
- int xsync_error_base_;
-
- XSyncCounter backing_store_sync_counter_;
- XSyncAlarm backing_store_sync_alarm_;
-
- // A queue of pending paints that we clean up after as alarms fire.
- std::queue<BackingStoreEvents*> backing_store_events_;
-};
-
-void XSyncHandler::PushPaintCounter(TransportDIB* dib,
- XDisplay* display,
- Picture picture,
- Pixmap pixmap,
- const base::Closure& completion_callback) {
- backing_store_events_.push(new BackingStoreEvents(
- dib, display, picture, pixmap, completion_callback));
-
- // Push a change counter event into the X11 event queue that will trigger our
- // alarm when it is processed.
- XSyncValue value;
- XSyncIntToValue(&value, 1);
- XSyncChangeCounter(gfx::GetXDisplay(),
- backing_store_sync_counter_,
- value);
-}
-
-XSyncHandler::XSyncHandler()
- : loaded_extension_(false),
- xsync_event_base_(0),
- xsync_error_base_(0),
- backing_store_sync_counter_(0),
- backing_store_sync_alarm_(0) {
- XDisplay* display = gfx::GetXDisplay();
- if (XSyncQueryExtension(display,
- &xsync_event_base_,
- &xsync_error_base_)) {
- // Create our monotonically increasing counter.
- XSyncValue value;
- XSyncIntToValue(&value, 0);
- backing_store_sync_counter_ = XSyncCreateCounter(display, value);
-
- // Cerate our alarm that watches for changes to our counter.
- XSyncAlarmAttributes attributes;
- attributes.trigger.counter = backing_store_sync_counter_;
- backing_store_sync_alarm_ = XSyncCreateAlarm(display,
- XSyncCACounter,
- &attributes);
-
- // Add our filter to the message loop to handle alarm triggers.
- gdk_window_add_filter(NULL, &OnEventThunk, this);
-
- loaded_extension_ = true;
- }
-}
-
-XSyncHandler::~XSyncHandler() {
- if (loaded_extension_)
- gdk_window_remove_filter(NULL, &OnEventThunk, this);
-
- XSync(gfx::GetXDisplay(), False);
- while (!backing_store_events_.empty()) {
- // We delete the X11 resources we're holding onto. We don't run the
- // callbacks because we are shutting down.
- BackingStoreEvents* data = backing_store_events_.front();
- backing_store_events_.pop();
- XRenderFreePicture(data->display, data->picture);
- XFreePixmap(data->display, data->pixmap);
- data->dib->DecreaseInFlightCounter();
- delete data;
- }
-}
-
-GdkFilterReturn XSyncHandler::OnEvent(GdkXEvent* gdkxevent,
- GdkEvent* event) {
- XEvent* xevent = reinterpret_cast<XEvent*>(gdkxevent);
- if (xevent->type == xsync_event_base_ + XSyncAlarmNotify) {
- XSyncAlarmNotifyEvent* alarm_event =
- reinterpret_cast<XSyncAlarmNotifyEvent*>(xevent);
- if (alarm_event->alarm == backing_store_sync_alarm_) {
- if (alarm_event->counter_value.hi == 0 &&
- alarm_event->counter_value.lo == 0) {
- // We receive an event about the initial state of the counter during
- // alarm creation. We must ignore this event instead of responding to
- // it.
- return GDK_FILTER_REMOVE;
- }
-
- DCHECK(!backing_store_events_.empty());
- BackingStoreEvents* data = backing_store_events_.front();
- backing_store_events_.pop();
-
- // We are responsible for deleting all the data in the struct now that
- // we are finished with it.
- XRenderFreePicture(data->display, data->picture);
- XFreePixmap(data->display, data->pixmap);
-
- // Dispatch the closure we were given.
- data->closure.Run();
-
- data->dib->DecreaseInFlightCounter();
- delete data;
-
- return GDK_FILTER_REMOVE;
- }
- }
-
- return GDK_FILTER_CONTINUE;
-}
-
-} // namespace
-
-BackingStoreGtk::BackingStoreGtk(RenderWidgetHost* widget,
- const gfx::Size& size,
- void* visual,
- int depth)
- : BackingStore(widget, size),
- display_(gfx::GetXDisplay()),
- shared_memory_support_(ui::QuerySharedMemorySupport(display_)),
- use_render_(ui::QueryRenderSupport(display_)),
- visual_(visual),
- visual_depth_(depth),
- root_window_(ui::GetX11RootWindow()) {
-#if defined(OS_OPENBSD) || defined(OS_FREEBSD)
- COMPILE_ASSERT(_BYTE_ORDER == _LITTLE_ENDIAN, assumes_little_endian);
-#else
- COMPILE_ASSERT(__BYTE_ORDER == __LITTLE_ENDIAN, assumes_little_endian);
-#endif
-
- pixmap_ = XCreatePixmap(display_, root_window_,
- size.width(), size.height(), depth);
-
- if (use_render_) {
- picture_ = XRenderCreatePicture(
- display_, pixmap_,
- ui::GetRenderVisualFormat(display_,
- static_cast<Visual*>(visual)),
- 0, NULL);
- pixmap_bpp_ = 0;
- } else {
- picture_ = 0;
- pixmap_bpp_ = gfx::BitsPerPixelForPixmapDepth(display_, depth);
- }
-
- pixmap_gc_ = XCreateGC(display_, pixmap_, 0, NULL);
-}
-
-BackingStoreGtk::BackingStoreGtk(RenderWidgetHost* widget,
- const gfx::Size& size)
- : BackingStore(widget, size),
- display_(NULL),
- shared_memory_support_(ui::SHARED_MEMORY_NONE),
- use_render_(false),
- pixmap_bpp_(0),
- visual_(NULL),
- visual_depth_(-1),
- root_window_(0),
- pixmap_(0),
- picture_(0),
- pixmap_gc_(NULL) {
-}
-
-BackingStoreGtk::~BackingStoreGtk() {
- // In unit tests, display_ may be NULL.
- if (!display_)
- return;
-
- XRenderFreePicture(display_, picture_);
- XFreePixmap(display_, pixmap_);
- XFreeGC(display_, static_cast<GC>(pixmap_gc_));
-}
-
-size_t BackingStoreGtk::MemorySize() {
- if (!use_render_)
- return size().GetArea() * (pixmap_bpp_ / 8);
- else
- return size().GetArea() * 4;
-}
-
-void BackingStoreGtk::PaintRectWithoutXrender(
- TransportDIB* bitmap,
- const gfx::Rect& bitmap_rect,
- const std::vector<gfx::Rect>& copy_rects) {
- const int width = bitmap_rect.width();
- const int height = bitmap_rect.height();
- Pixmap pixmap = XCreatePixmap(display_, root_window_, width, height,
- visual_depth_);
-
- // Draw ARGB transport DIB onto our pixmap.
- gfx::PutARGBImage(display_, visual_, visual_depth_, pixmap,
- pixmap_gc_, static_cast<uint8*>(bitmap->memory()),
- width, height);
-
- for (size_t i = 0; i < copy_rects.size(); i++) {
- const gfx::Rect& copy_rect = copy_rects[i];
- XCopyArea(display_,
- pixmap, // src
- pixmap_, // dest
- static_cast<GC>(pixmap_gc_), // gc
- copy_rect.x() - bitmap_rect.x(), // src_x
- copy_rect.y() - bitmap_rect.y(), // src_y
- copy_rect.width(), // width
- copy_rect.height(), // height
- copy_rect.x(), // dest_x
- copy_rect.y()); // dest_y
- }
-
- XFreePixmap(display_, pixmap);
-}
-
-void BackingStoreGtk::PaintToBackingStore(
- RenderProcessHost* process,
- TransportDIB::Id bitmap,
- const gfx::Rect& bitmap_rect,
- const std::vector<gfx::Rect>& copy_rects,
- float scale_factor,
- const base::Closure& completion_callback,
- bool* scheduled_completion_callback) {
- *scheduled_completion_callback = false;
-
- if (!display_)
- return;
-
- if (bitmap_rect.IsEmpty())
- return;
-
- gfx::Rect pixel_bitmap_rect = gfx::ToEnclosedRect(
- gfx::ScaleRect(bitmap_rect, scale_factor));
- const int width = pixel_bitmap_rect.width();
- const int height = pixel_bitmap_rect.height();
-
- if (width <= 0 || width > kMaxVideoLayerSize ||
- height <= 0 || height > kMaxVideoLayerSize)
- return;
-
- TransportDIB* dib = process->GetTransportDIB(bitmap);
- if (!dib)
- return;
-
- if (!use_render_)
- return PaintRectWithoutXrender(dib, bitmap_rect, copy_rects);
-
- Picture picture;
- Pixmap pixmap;
-
- if (shared_memory_support_ == ui::SHARED_MEMORY_PIXMAP) {
- XShmSegmentInfo shminfo = {0};
- shminfo.shmseg = dib->MapToX(display_);
-
- // The NULL in the following is the |data| pointer: this is an artifact of
- // Xlib trying to be helpful, rather than just exposing the X protocol. It
- // assumes that we have the shared memory segment mapped into our memory,
- // which we don't, and it's trying to calculate an offset by taking the
- // difference between the |data| pointer and the address of the mapping in
- // |shminfo|. Since both are NULL, the offset will be calculated to be 0,
- // which is correct for us.
- pixmap = XShmCreatePixmap(display_, root_window_, NULL, &shminfo,
- width, height, 32);
- } else {
- // We don't have shared memory pixmaps. Fall back to creating a pixmap
- // ourselves and putting an image on it.
- pixmap = XCreatePixmap(display_, root_window_, width, height, 32);
- GC gc = XCreateGC(display_, pixmap, 0, NULL);
-
- if (shared_memory_support_ == ui::SHARED_MEMORY_PUTIMAGE) {
- const XID shmseg = dib->MapToX(display_);
-
- XShmSegmentInfo shminfo;
- memset(&shminfo, 0, sizeof(shminfo));
- shminfo.shmseg = shmseg;
- shminfo.shmaddr = static_cast<char*>(dib->memory());
-
- XImage* image = XShmCreateImage(display_, static_cast<Visual*>(visual_),
- 32, ZPixmap,
- shminfo.shmaddr, &shminfo,
- width, height);
-
- // This code path is important for performance and we have found that
- // different techniques work better on different platforms. See
- // http://code.google.com/p/chromium/issues/detail?id=44124.
- //
- // Checking for ARM is an approximation, but it seems to be a good one so
- // far.
-#if defined(ARCH_CPU_ARM_FAMILY)
- for (size_t i = 0; i < copy_rects.size(); i++) {
- const gfx::Rect& copy_rect = copy_rects[i];
- gfx::Rect pixel_copy_rect = gfx::ToEnclosedRect(
- gfx::ScaleRect(copy_rect, scale_factor));
- XShmPutImage(display_, pixmap, gc, image,
- pixel_copy_rect.x() - pixel_bitmap_rect.x(), /* source x */
- pixel_copy_rect.y() - pixel_bitmap_rect.y(), /* source y */
- pixel_copy_rect.x() - pixel_bitmap_rect.x(), /* dest x */
- pixel_copy_rect.y() - pixel_bitmap_rect.y(), /* dest y */
- pixel_copy_rect.width(), pixel_copy_rect.height(),
- False /* send_event */);
- }
-#else
- XShmPutImage(display_, pixmap, gc, image,
- 0, 0 /* source x, y */, 0, 0 /* dest x, y */,
- width, height, False /* send_event */);
-#endif
- XDestroyImage(image);
- } else { // case SHARED_MEMORY_NONE
- // No shared memory support, we have to copy the bitmap contents
- // to the X server. Xlib wraps the underlying PutImage call
- // behind several layers of functions which try to convert the
- // image into the format which the X server expects. The
- // following values hopefully disable all conversions.
- XImage image;
- memset(&image, 0, sizeof(image));
-
- image.width = width;
- image.height = height;
- image.depth = 32;
- image.bits_per_pixel = 32;
- image.format = ZPixmap;
- image.byte_order = LSBFirst;
- image.bitmap_unit = 8;
- image.bitmap_bit_order = LSBFirst;
- image.bytes_per_line = width * 4;
- image.red_mask = 0xff;
- image.green_mask = 0xff00;
- image.blue_mask = 0xff0000;
- image.data = static_cast<char*>(dib->memory());
-
- XPutImage(display_, pixmap, gc, &image,
- 0, 0 /* source x, y */, 0, 0 /* dest x, y */,
- width, height);
- }
- XFreeGC(display_, gc);
- }
-
- picture = ui::CreatePictureFromSkiaPixmap(display_, pixmap);
-
- if (scale_factor != 1.0) {
- float up_scale = 1.0 / scale_factor;
- XTransform scaling = { {
- { XDoubleToFixed(1), XDoubleToFixed(0), XDoubleToFixed(0) },
- { XDoubleToFixed(0), XDoubleToFixed(1), XDoubleToFixed(0) },
- { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(up_scale) }
- } };
- XRenderSetPictureTransform(display_, picture, &scaling);
- XRenderSetPictureFilter(display_, picture, FilterGood, NULL, 0);
- }
- for (size_t i = 0; i < copy_rects.size(); i++) {
- const gfx::Rect& copy_rect = copy_rects[i];
- XRenderComposite(display_,
- PictOpSrc, // op
- picture, // src
- 0, // mask
- picture_, // dest
- copy_rect.x() - bitmap_rect.x(), // src_x
- copy_rect.y() - bitmap_rect.y(), // src_y
- 0, // mask_x
- 0, // mask_y
- copy_rect.x(), // dest_x
- copy_rect.y(), // dest_y
- copy_rect.width(), // width
- copy_rect.height()); // height
- }
-
- // In the case of shared memory, we wait for the composite to complete so that
- // we are sure that the X server has finished reading from the shared memory
- // segment.
- if (shared_memory_support_ != ui::SHARED_MEMORY_NONE) {
- XSyncHandler* handler = XSyncHandler::GetInstance();
- if (handler->Enabled()) {
- *scheduled_completion_callback = true;
- handler->PushPaintCounter(
- dib, display_, picture, pixmap, completion_callback);
- } else {
- XSync(display_, False);
- }
- }
-
- if (*scheduled_completion_callback == false) {
- // If we didn't schedule a callback, we need to delete our resources now.
- XRenderFreePicture(display_, picture);
- XFreePixmap(display_, pixmap);
- }
-}
-
-bool BackingStoreGtk::CopyFromBackingStore(const gfx::Rect& rect,
- skia::PlatformBitmap* output) {
- base::TimeTicks begin_time = base::TimeTicks::Now();
-
- if (visual_depth_ < 24) {
- // CopyFromBackingStore() copies pixels out of the XImage
- // in a way that assumes that each component (red, green,
- // blue) is a byte. This doesn't work on visuals which
- // encode a pixel color with less than a byte per color.
- return false;
- }
-
- const int width = std::min(size().width(), rect.width());
- const int height = std::min(size().height(), rect.height());
-
- XImage* image;
- XShmSegmentInfo shminfo; // Used only when shared memory is enabled.
- if (shared_memory_support_ != ui::SHARED_MEMORY_NONE) {
- // Use shared memory for faster copies when it's available.
- Visual* visual = static_cast<Visual*>(visual_);
- memset(&shminfo, 0, sizeof(shminfo));
- image = XShmCreateImage(display_, visual, 32,
- ZPixmap, NULL, &shminfo, width, height);
- if (!image) {
- return false;
- }
- // Create the shared memory segment for the image and map it.
- if (image->bytes_per_line == 0 || image->height == 0 ||
- static_cast<size_t>(image->height) >
- (std::numeric_limits<size_t>::max() / image->bytes_per_line)) {
- XDestroyImage(image);
- return false;
- }
- shminfo.shmid = shmget(IPC_PRIVATE, image->bytes_per_line * image->height,
- IPC_CREAT|0600);
- if (shminfo.shmid == -1) {
- XDestroyImage(image);
- LOG(WARNING) << "Failed to get shared memory segment. "
- "Performance may be degraded.";
- return false;
- } else {
- VLOG(1) << "Got shared memory segment " << shminfo.shmid;
- }
-
- void* mapped_memory = shmat(shminfo.shmid, NULL, SHM_RDONLY);
- shmctl(shminfo.shmid, IPC_RMID, 0);
- if (mapped_memory == (void*)-1) {
- XDestroyImage(image);
- return false;
- }
- shminfo.shmaddr = image->data = static_cast<char*>(mapped_memory);
-
- if (!XShmAttach(display_, &shminfo) ||
- !XShmGetImage(display_, pixmap_, image, rect.x(), rect.y(),
- AllPlanes)) {
- DestroySharedImage(display_, image, &shminfo);
- LOG(WARNING) << "X failed to get shared memory segment. "
- "Performance may be degraded.";
- return false;
- }
-
- VLOG(1) << "Using X shared memory segment " << shminfo.shmid;
- } else {
- LOG(WARNING) << "Not using X shared memory.";
- // Non-shared memory case just copy the image from the server.
- image = XGetImage(display_, pixmap_,
- rect.x(), rect.y(), width, height,
- AllPlanes, ZPixmap);
- }
-
- // TODO(jhawkins): Need to convert the image data if the image bits per pixel
- // is not 32.
- // Note that this also initializes the output bitmap as opaque.
- if (!output->Allocate(width, height, true) ||
- image->bits_per_pixel != 32) {
- if (shared_memory_support_ != ui::SHARED_MEMORY_NONE)
- DestroySharedImage(display_, image, &shminfo);
- else
- XDestroyImage(image);
- return false;
- }
-
- // The X image might have a different row stride, so iterate through
- // it and copy each row out, only up to the pixels we're actually
- // using. This code assumes a visual mode where a pixel is
- // represented using a 32-bit unsigned int, with a byte per component.
- const SkBitmap& bitmap = output->GetBitmap();
- SkAutoLockPixels alp(bitmap);
-
- for (int y = 0; y < height; y++) {
- const uint32* src_row = reinterpret_cast<uint32*>(
- &image->data[image->bytes_per_line * y]);
- uint32* dest_row = bitmap.getAddr32(0, y);
- for (int x = 0; x < width; ++x, ++dest_row) {
- // Force alpha to be 0xff, because otherwise it causes rendering problems.
- *dest_row = src_row[x] | 0xff000000;
- }
- }
-
- if (shared_memory_support_ != ui::SHARED_MEMORY_NONE)
- DestroySharedImage(display_, image, &shminfo);
- else
- XDestroyImage(image);
-
- HISTOGRAM_TIMES("BackingStore.RetrievalFromX",
- base::TimeTicks::Now() - begin_time);
- return true;
-}
-
-void BackingStoreGtk::ScrollBackingStore(const gfx::Vector2d& delta,
- const gfx::Rect& clip_rect,
- const gfx::Size& view_size) {
- if (!display_)
- return;
-
- // We only support scrolling in one direction at a time.
- DCHECK(delta.x() == 0 || delta.y() == 0);
-
- if (delta.y()) {
- // Positive values of |delta|.y() scroll up
- if (abs(delta.y()) < clip_rect.height()) {
- XCopyArea(display_, pixmap_, pixmap_, static_cast<GC>(pixmap_gc_),
- clip_rect.x() /* source x */,
- std::max(clip_rect.y(), clip_rect.y() - delta.y()),
- clip_rect.width(),
- clip_rect.height() - abs(delta.y()),
- clip_rect.x() /* dest x */,
- std::max(clip_rect.y(), clip_rect.y() + delta.y()) /* dest y */
- );
- }
- } else if (delta.x()) {
- // Positive values of |delta|.x() scroll right
- if (abs(delta.x()) < clip_rect.width()) {
- XCopyArea(display_, pixmap_, pixmap_, static_cast<GC>(pixmap_gc_),
- std::max(clip_rect.x(), clip_rect.x() - delta.x()),
- clip_rect.y() /* source y */,
- clip_rect.width() - abs(delta.x()),
- clip_rect.height(),
- std::max(clip_rect.x(), clip_rect.x() + delta.x()) /* dest x */,
- clip_rect.y() /* dest x */);
- }
- }
-}
-
-void BackingStoreGtk::XShowRect(const gfx::Point &origin,
- const gfx::Rect& rect, XID target) {
- XCopyArea(display_, pixmap_, target, static_cast<GC>(pixmap_gc_),
- rect.x(), rect.y(), rect.width(), rect.height(),
- rect.x() + origin.x(), rect.y() + origin.y());
-}
-
-#if defined(TOOLKIT_GTK)
-void BackingStoreGtk::PaintToRect(const gfx::Rect& rect, GdkDrawable* target) {
- cairo_surface_t* surface = cairo_xlib_surface_create(
- display_, pixmap_, static_cast<Visual*>(visual_),
- size().width(), size().height());
- cairo_t* cr = gdk_cairo_create(target);
-
- cairo_translate(cr, rect.x(), rect.y());
- double x_scale = static_cast<double>(rect.width()) / size().width();
- double y_scale = static_cast<double>(rect.height()) / size().height();
- cairo_scale(cr, x_scale, y_scale);
-
- cairo_pattern_t* pattern = cairo_pattern_create_for_surface(surface);
- cairo_pattern_set_filter(pattern, CAIRO_FILTER_BEST);
- cairo_set_source(cr, pattern);
- cairo_pattern_destroy(pattern);
-
- cairo_identity_matrix(cr);
-
- cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
- cairo_fill(cr);
- cairo_destroy(cr);
-}
-#endif
-
-} // namespace content
diff --git a/chromium/content/browser/renderer_host/backing_store_gtk.h b/chromium/content/browser/renderer_host/backing_store_gtk.h
deleted file mode 100644
index 29776fcdf67..00000000000
--- a/chromium/content/browser/renderer_host/backing_store_gtk.h
+++ /dev/null
@@ -1,107 +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_RENDERER_HOST_BACKING_STORE_GTK_H_
-#define CONTENT_BROWSER_RENDERER_HOST_BACKING_STORE_GTK_H_
-
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/compiler_specific.h"
-#include "build/build_config.h"
-#include "content/browser/renderer_host/backing_store.h"
-#include "content/common/content_export.h"
-#include "ui/gfx/x/x11_types.h"
-
-namespace gfx {
-class Point;
-class Rect;
-}
-
-typedef struct _GdkDrawable GdkDrawable;
-
-namespace content {
-
-class CONTENT_EXPORT BackingStoreGtk : public BackingStore {
- public:
- // Create a backing store on the X server. The visual is an Xlib Visual
- // describing the format of the target window and the depth is the color
- // depth of the X window which will be drawn into.
- BackingStoreGtk(RenderWidgetHost* widget,
- const gfx::Size& size,
- void* visual,
- int depth);
-
- // This is for unittesting only. An object constructed using this constructor
- // will silently ignore all paints
- BackingStoreGtk(RenderWidgetHost* widget, const gfx::Size& size);
-
- virtual ~BackingStoreGtk();
-
- XDisplay* display() const { return display_; }
- XID root_window() const { return root_window_; }
-
- // Copy from the server-side backing store to the target window
- // origin: the destination rectangle origin
- // damage: the area to copy
- // target: the X id of the target window
- void XShowRect(const gfx::Point &origin, const gfx::Rect& damage,
- XID target);
-
-#if defined(TOOLKIT_GTK)
- // Paint the backing store into the target's |dest_rect|.
- void PaintToRect(const gfx::Rect& dest_rect, GdkDrawable* target);
-#endif
-
- // BackingStore implementation.
- virtual size_t MemorySize() OVERRIDE;
- virtual void PaintToBackingStore(
- RenderProcessHost* process,
- TransportDIB::Id bitmap,
- const gfx::Rect& bitmap_rect,
- const std::vector<gfx::Rect>& copy_rects,
- float scale_factor,
- const base::Closure& completion_callback,
- bool* scheduled_completion_callback) OVERRIDE;
- virtual bool CopyFromBackingStore(const gfx::Rect& rect,
- skia::PlatformBitmap* output) OVERRIDE;
- virtual void ScrollBackingStore(const gfx::Vector2d& delta,
- const gfx::Rect& clip_rect,
- const gfx::Size& view_size) OVERRIDE;
-
- private:
- // Paints the bitmap from the renderer onto the backing store without
- // using Xrender to composite the pixmaps.
- void PaintRectWithoutXrender(TransportDIB* bitmap,
- const gfx::Rect& bitmap_rect,
- const std::vector<gfx::Rect>& copy_rects);
-
- // This is the connection to the X server where this backing store will be
- // displayed.
- XDisplay* const display_;
- // What flavor, if any, MIT-SHM (X shared memory) support we have.
- const ui::SharedMemorySupport shared_memory_support_;
- // If this is true, then we can use Xrender to composite our pixmaps.
- const bool use_render_;
- // If |use_render_| is false, this is the number of bits-per-pixel for |depth|
- int pixmap_bpp_;
- // if |use_render_| is false, we need the Visual to get the RGB masks.
- void* const visual_;
- // This is the depth of the target window.
- const int visual_depth_;
- // The parent window (probably a GtkDrawingArea) for this backing store.
- const XID root_window_;
- // This is a handle to the server side pixmap which is our backing store.
- XID pixmap_;
- // This is the RENDER picture pointing at |pixmap_|.
- XID picture_;
- // This is a default graphic context, used in XCopyArea
- void* pixmap_gc_;
-
- DISALLOW_COPY_AND_ASSIGN(BackingStoreGtk);
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_RENDERER_HOST_BACKING_STORE_GTK_H_
diff --git a/chromium/content/browser/renderer_host/backing_store_mac.h b/chromium/content/browser/renderer_host/backing_store_mac.h
deleted file mode 100644
index 698ca3d028c..00000000000
--- a/chromium/content/browser/renderer_host/backing_store_mac.h
+++ /dev/null
@@ -1,76 +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_RENDERER_HOST_BACKING_STORE_MAC_H_
-#define CONTENT_BROWSER_RENDERER_HOST_BACKING_STORE_MAC_H_
-
-#include "base/basictypes.h"
-#include "base/mac/scoped_cftyperef.h"
-#include "content/browser/renderer_host/backing_store.h"
-
-namespace content {
-
-class BackingStoreMac : public BackingStore {
- public:
- // |size| is in view units, |device_scale_factor| is the backingScaleFactor.
- // The pixel size of the backing store is size.Scale(device_scale_factor).
- BackingStoreMac(RenderWidgetHost* widget,
- const gfx::Size& size,
- float device_scale_factor);
- virtual ~BackingStoreMac();
-
- // A CGLayer that stores the contents of the backing store, cached in GPU
- // memory if possible.
- CGLayerRef cg_layer() { return cg_layer_; }
-
- // A CGBitmapContext that stores the contents of the backing store if the
- // corresponding Cocoa view has not been inserted into an NSWindow yet.
- CGContextRef cg_bitmap() { return cg_bitmap_; }
-
- // Called when the view's backing scale factor changes.
- void ScaleFactorChanged(float device_scale_factor);
-
- // BackingStore implementation.
- virtual size_t MemorySize() OVERRIDE;
- virtual void PaintToBackingStore(
- RenderProcessHost* process,
- TransportDIB::Id bitmap,
- const gfx::Rect& bitmap_rect,
- const std::vector<gfx::Rect>& copy_rects,
- float scale_factor,
- const base::Closure& completion_callback,
- bool* scheduled_completion_callback) OVERRIDE;
- virtual bool CopyFromBackingStore(const gfx::Rect& rect,
- skia::PlatformBitmap* output) OVERRIDE;
- virtual void ScrollBackingStore(const gfx::Vector2d& delta,
- const gfx::Rect& clip_rect,
- const gfx::Size& view_size) OVERRIDE;
-
- void CopyFromBackingStoreToCGContext(const CGRect& dest_rect,
- CGContextRef context);
-
- private:
- // Creates a CGLayer associated with its owner view's window's graphics
- // context, sized properly for the backing store. Returns NULL if the owner
- // is not in a window with a CGContext. cg_layer_ is assigned this method's
- // result.
- CGLayerRef CreateCGLayer();
-
- // Creates a CGBitmapContext sized properly for the backing store. The
- // owner view need not be in a window. cg_bitmap_ is assigned this method's
- // result.
- CGContextRef CreateCGBitmapContext();
-
- base::ScopedCFTypeRef<CGContextRef> cg_bitmap_;
- base::ScopedCFTypeRef<CGLayerRef> cg_layer_;
-
- // Number of physical pixels per view unit. This is 1 or 2 in practice.
- float device_scale_factor_;
-
- DISALLOW_COPY_AND_ASSIGN(BackingStoreMac);
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_RENDERER_HOST_BACKING_STORE_MAC_H_
diff --git a/chromium/content/browser/renderer_host/backing_store_mac.mm b/chromium/content/browser/renderer_host/backing_store_mac.mm
deleted file mode 100644
index 8b3fda9840d..00000000000
--- a/chromium/content/browser/renderer_host/backing_store_mac.mm
+++ /dev/null
@@ -1,297 +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.
-
-#import <Cocoa/Cocoa.h>
-
-#include "content/browser/renderer_host/backing_store_mac.h"
-
-#include <cmath>
-
-#include "base/logging.h"
-#include "base/mac/mac_util.h"
-#include "base/mac/scoped_cftyperef.h"
-#include "content/browser/renderer_host/render_process_host_impl.h"
-#include "content/browser/renderer_host/render_widget_host_impl.h"
-#include "content/public/browser/render_widget_host_view.h"
-#include "skia/ext/platform_canvas.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "third_party/skia/include/core/SkCanvas.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/rect_conversions.h"
-#include "ui/gfx/size_conversions.h"
-#include "ui/gfx/scoped_cg_context_save_gstate_mac.h"
-#include "ui/surface/transport_dib.h"
-
-namespace content {
-
-// Mac Backing Stores:
-//
-// Since backing stores are only ever written to or drawn into windows, we keep
-// our backing store in a CGLayer that can get cached in GPU memory. This
-// allows acclerated drawing into the layer and lets scrolling and such happen
-// all or mostly on the GPU, which is good for performance.
-
-BackingStoreMac::BackingStoreMac(RenderWidgetHost* widget,
- const gfx::Size& size,
- float device_scale_factor)
- : BackingStore(widget, size), device_scale_factor_(device_scale_factor) {
- cg_layer_.reset(CreateCGLayer());
- if (!cg_layer_) {
- // The view isn't in a window yet. Use a CGBitmapContext for now.
- cg_bitmap_.reset(CreateCGBitmapContext());
- CGContextScaleCTM(cg_bitmap_, device_scale_factor_, device_scale_factor_);
- }
-}
-
-BackingStoreMac::~BackingStoreMac() {
-}
-
-void BackingStoreMac::ScaleFactorChanged(float device_scale_factor) {
- if (device_scale_factor == device_scale_factor_)
- return;
-
- device_scale_factor_ = device_scale_factor;
-
- base::ScopedCFTypeRef<CGLayerRef> new_layer(CreateCGLayer());
- // If we have a layer, copy the old contents. A pixelated flash is better
- // than a white flash.
- if (new_layer && cg_layer_) {
- CGContextRef layer = CGLayerGetContext(new_layer);
- CGContextDrawLayerAtPoint(layer, CGPointMake(0, 0), cg_layer_);
- }
-
- cg_layer_.swap(new_layer);
- if (!cg_layer_) {
- // The view isn't in a window yet. Use a CGBitmapContext for now.
- cg_bitmap_.reset(CreateCGBitmapContext());
- CGContextScaleCTM(cg_bitmap_, device_scale_factor_, device_scale_factor_);
- }
-}
-
-size_t BackingStoreMac::MemorySize() {
- return gfx::ToFlooredSize(
- gfx::ScaleSize(size(), device_scale_factor_)).GetArea() * 4;
-}
-
-void BackingStoreMac::PaintToBackingStore(
- RenderProcessHost* process,
- TransportDIB::Id bitmap,
- const gfx::Rect& bitmap_rect,
- const std::vector<gfx::Rect>& copy_rects,
- float scale_factor,
- const base::Closure& completion_callback,
- bool* scheduled_completion_callback) {
- *scheduled_completion_callback = false;
- DCHECK_NE(static_cast<bool>(cg_layer()), static_cast<bool>(cg_bitmap()));
-
- TransportDIB* dib = process->GetTransportDIB(bitmap);
- if (!dib)
- return;
-
- gfx::Size pixel_size = gfx::ToFlooredSize(
- gfx::ScaleSize(size(), device_scale_factor_));
- gfx::Rect pixel_bitmap_rect = ToFlooredRectDeprecated(
- gfx::ScaleRect(bitmap_rect, scale_factor));
-
- size_t bitmap_byte_count =
- pixel_bitmap_rect.width() * pixel_bitmap_rect.height() * 4;
- DCHECK_GE(dib->size(), bitmap_byte_count);
-
- base::ScopedCFTypeRef<CGDataProviderRef> data_provider(
- CGDataProviderCreateWithData(
- NULL, dib->memory(), bitmap_byte_count, NULL));
-
- base::ScopedCFTypeRef<CGImageRef> bitmap_image(
- CGImageCreate(pixel_bitmap_rect.width(),
- pixel_bitmap_rect.height(),
- 8,
- 32,
- 4 * pixel_bitmap_rect.width(),
- base::mac::GetSystemColorSpace(),
- kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host,
- data_provider,
- NULL,
- false,
- kCGRenderingIntentDefault));
-
- for (size_t i = 0; i < copy_rects.size(); i++) {
- const gfx::Rect& copy_rect = copy_rects[i];
- gfx::Rect pixel_copy_rect = ToFlooredRectDeprecated(
- gfx::ScaleRect(copy_rect, scale_factor));
-
- // Only the subpixels given by copy_rect have pixels to copy.
- base::ScopedCFTypeRef<CGImageRef> image(CGImageCreateWithImageInRect(
- bitmap_image,
- CGRectMake(pixel_copy_rect.x() - pixel_bitmap_rect.x(),
- pixel_copy_rect.y() - pixel_bitmap_rect.y(),
- pixel_copy_rect.width(),
- pixel_copy_rect.height())));
-
- if (!cg_layer()) {
- // The view may have moved to a window. Try to get a CGLayer.
- cg_layer_.reset(CreateCGLayer());
- if (cg_layer()) {
- // Now that we have a layer, copy the cached image into it.
- base::ScopedCFTypeRef<CGImageRef> bitmap_image(
- CGBitmapContextCreateImage(cg_bitmap_));
- CGContextDrawImage(CGLayerGetContext(cg_layer()),
- CGRectMake(0, 0, size().width(), size().height()),
- bitmap_image);
- // Discard the cache bitmap, since we no longer need it.
- cg_bitmap_.reset(NULL);
- }
- }
-
- DCHECK_NE(static_cast<bool>(cg_layer()), static_cast<bool>(cg_bitmap()));
-
- if (cg_layer()) {
- // The CGLayer's origin is in the lower left, but flipping the CTM would
- // cause the image to get drawn upside down. So we move the rectangle
- // to the right position before drawing the image.
- CGContextRef layer = CGLayerGetContext(cg_layer());
- gfx::Rect paint_rect = copy_rect;
- paint_rect.set_y(size().height() - copy_rect.bottom());
- CGContextDrawImage(layer, paint_rect.ToCGRect(), image);
- } else {
- // The layer hasn't been created yet, so draw into the cache bitmap.
- gfx::Rect paint_rect = copy_rect;
- paint_rect.set_y(size().height() - copy_rect.bottom());
- CGContextDrawImage(cg_bitmap_, paint_rect.ToCGRect(), image);
- }
- }
-}
-
-bool BackingStoreMac::CopyFromBackingStore(const gfx::Rect& rect,
- skia::PlatformBitmap* output) {
- // TODO(thakis): Make sure this works with HiDPI backing stores.
- if (!output->Allocate(rect.width(), rect.height(), true))
- return false;
-
- CGContextRef temp_context = output->GetSurface();
- gfx::ScopedCGContextSaveGState save_gstate(temp_context);
- CGContextTranslateCTM(temp_context, 0.0, size().height());
- CGContextScaleCTM(temp_context, 1.0, -1.0);
- if (cg_layer()) {
- CGContextDrawLayerAtPoint(temp_context, CGPointMake(-rect.x(), -rect.y()),
- cg_layer());
- } else {
- base::ScopedCFTypeRef<CGImageRef> bitmap_image(
- CGBitmapContextCreateImage(cg_bitmap_));
- CGContextDrawImage(
- temp_context,
- CGRectMake(-rect.x(), -rect.y(), rect.width(), rect.height()),
- bitmap_image);
- }
-
- return true;
-}
-
-// Scroll the contents of our CGLayer
-void BackingStoreMac::ScrollBackingStore(const gfx::Vector2d& delta,
- const gfx::Rect& clip_rect,
- const gfx::Size& view_size) {
- DCHECK_NE(static_cast<bool>(cg_layer()), static_cast<bool>(cg_bitmap()));
-
- // "Scroll" the contents of the layer by creating a new CGLayer,
- // copying the contents of the old one into the new one offset by the scroll
- // amount, swapping in the new CGLayer, and then painting in the new data.
- //
- // The Windows code always sets the whole backing store as the source of the
- // scroll. Thus, we only have to worry about pixels which will end up inside
- // the clipping rectangle. (Note that the clipping rectangle is not
- // translated by the scroll.)
-
- // We assume |clip_rect| is contained within the backing store.
- DCHECK(clip_rect.bottom() <= size().height());
- DCHECK(clip_rect.right() <= size().width());
-
- if ((delta.x() || delta.y()) &&
- abs(delta.x()) < size().width() && abs(delta.y()) < size().height()) {
- if (cg_layer()) {
- CGContextRef layer = CGLayerGetContext(cg_layer());
- gfx::ScopedCGContextSaveGState save_gstate(layer);
- CGContextClipToRect(layer,
- CGRectMake(clip_rect.x(),
- size().height() - clip_rect.bottom(),
- clip_rect.width(),
- clip_rect.height()));
- CGContextDrawLayerAtPoint(layer,
- CGPointMake(delta.x(), -delta.y()), cg_layer());
- } else {
- // We don't have a layer, so scroll the contents of the CGBitmapContext.
- base::ScopedCFTypeRef<CGImageRef> bitmap_image(
- CGBitmapContextCreateImage(cg_bitmap_));
- gfx::ScopedCGContextSaveGState save_gstate(cg_bitmap_);
- CGContextClipToRect(cg_bitmap_,
- CGRectMake(clip_rect.x(),
- size().height() - clip_rect.bottom(),
- clip_rect.width(),
- clip_rect.height()));
- CGContextDrawImage(cg_bitmap_,
- CGRectMake(delta.x(), -delta.y(),
- size().width(), size().height()),
- bitmap_image);
- }
- }
-}
-
-void BackingStoreMac::CopyFromBackingStoreToCGContext(const CGRect& dest_rect,
- CGContextRef context) {
- gfx::ScopedCGContextSaveGState save_gstate(context);
- CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
- if (cg_layer_) {
- CGContextDrawLayerInRect(context, dest_rect, cg_layer_);
- } else {
- base::ScopedCFTypeRef<CGImageRef> image(
- CGBitmapContextCreateImage(cg_bitmap_));
- CGContextDrawImage(context, dest_rect, image);
- }
-}
-
-CGLayerRef BackingStoreMac::CreateCGLayer() {
- // The CGLayer should be optimized for drawing into the containing window,
- // so extract a CGContext corresponding to the window to be passed to
- // CGLayerCreateWithContext.
- NSWindow* window = [render_widget_host()->GetView()->GetNativeView() window];
- if ([window windowNumber] <= 0) {
- // This catches a nil |window|, as well as windows that exist but that
- // aren't yet connected to WindowServer.
- return NULL;
- }
-
- NSGraphicsContext* ns_context = [window graphicsContext];
- DCHECK(ns_context);
-
- CGContextRef cg_context = static_cast<CGContextRef>(
- [ns_context graphicsPort]);
- DCHECK(cg_context);
-
- // Note: This takes the backingScaleFactor of cg_context into account. The
- // bitmap backing |layer| will be size() * 2 in HiDPI mode automatically.
- CGLayerRef layer = CGLayerCreateWithContext(cg_context,
- size().ToCGSize(),
- NULL);
- DCHECK(layer);
-
- return layer;
-}
-
-CGContextRef BackingStoreMac::CreateCGBitmapContext() {
- gfx::Size pixel_size = gfx::ToFlooredSize(
- gfx::ScaleSize(size(), device_scale_factor_));
- // A CGBitmapContext serves as a stand-in for the layer before the view is
- // in a containing window.
- CGContextRef context = CGBitmapContextCreate(NULL,
- pixel_size.width(),
- pixel_size.height(),
- 8, pixel_size.width() * 4,
- base::mac::GetSystemColorSpace(),
- kCGImageAlphaPremultipliedFirst |
- kCGBitmapByteOrder32Host);
- DCHECK(context);
-
- return context;
-}
-
-} // namespace content
diff --git a/chromium/content/browser/renderer_host/backing_store_manager.cc b/chromium/content/browser/renderer_host/backing_store_manager.cc
deleted file mode 100644
index 4d79b0ff4f2..00000000000
--- a/chromium/content/browser/renderer_host/backing_store_manager.cc
+++ /dev/null
@@ -1,282 +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/renderer_host/backing_store_manager.h"
-
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/containers/mru_cache.h"
-#include "base/sys_info.h"
-#include "content/browser/renderer_host/backing_store.h"
-#include "content/browser/renderer_host/render_widget_host_impl.h"
-#include "content/public/common/content_switches.h"
-
-namespace content {
-namespace {
-
-// There are two separate caches, |large_cache| and |small_cache|. large_cache
-// is meant for large items (tabs, popup windows), while small_cache is meant
-// for small items (extension popups, HTML5 notifications). The idea is that
-// we'll almost always try to evict from large_cache first since small_cache
-// items will tend to be visible more of the time.
-typedef base::OwningMRUCache<RenderWidgetHost*, BackingStore*>
- BackingStoreCache;
-BackingStoreCache* large_cache = NULL;
-BackingStoreCache* small_cache = NULL;
-
-// Threshold is based on a single large-monitor-width toolstrip.
-// (32bpp, 32 pixels high, 1920 pixels wide)
-// TODO(aa): The extension system no longer supports toolstrips, but we think
-// this might be helping for other examples of small HTML views in Chrome.
-// Maybe this cache should be redesigned to simply prefer smaller objects to
-// larger ones, rather than having a fixed threshold.
-// For more background, see: crbug.com/100506.
-const size_t kSmallThreshold = 4 * 32 * 1920;
-
-// Pick a large monitor size to use as a multiplier. This is multiplied by the
-// max number of large backing stores (usually tabs) to pick a ceiling on the
-// max memory to use.
-// TODO(erikkay) Perhaps we should actually use monitor size? That way we
-// could make an assertion like "worse case, there are two tabs in the cache".
-// However, the small_cache might mess up these calculations a bit.
-// TODO(erikkay) 32bpp assumption isn't great.
-const size_t kMemoryMultiplier = 4 * 1920 * 1200; // ~9MB
-
-// The maximum number of large BackingStoreCache objects (tabs) to use.
-// Use a minimum of 2, and add one for each 256MB of physical memory you have.
-// Cap at 5, the thinking being that even if you have a gigantic amount of
-// RAM, there's a limit to how much caching helps beyond a certain number
-// of tabs. If users *really* want unlimited stores, allow it via the
-// --disable-backing-store-limit flag.
-static size_t MaxNumberOfBackingStores() {
- static bool unlimited = false;
- const CommandLine& command = *CommandLine::ForCurrentProcess();
- unlimited = command.HasSwitch(switches::kDisableBackingStoreLimit);
-
-
- if (unlimited) {
- // 100 isn't truly unlimited, but given that backing stores count against
- // GDI memory, it's well past any reasonable number. Many systems will
- // begin to fail in strange ways well before they hit 100 stores.
- return 100;
- } else {
- return std::min(5, 2 + (base::SysInfo::AmountOfPhysicalMemoryMB() / 256));
- }
-}
-
-// The maximum about of memory to use for all BackingStoreCache object combined.
-static size_t MaxBackingStoreMemory() {
- // Compute in terms of the number of large monitor's worth of backing-store.
- return MaxNumberOfBackingStores() * kMemoryMultiplier;
-}
-
-// Expires the given |backing_store| from |cache|.
-void ExpireBackingStoreAt(BackingStoreCache* cache,
- BackingStoreCache::iterator backing_store) {
- cache->Erase(backing_store);
-}
-
-size_t ExpireLastBackingStore(BackingStoreCache* cache) {
- if (cache->size() < 1)
- return 0;
-
- // Crazy C++ alert: rbegin.base() is a forward iterator pointing to end(),
- // so we need to do -- to move one back to the actual last item.
- BackingStoreCache::iterator entry = --cache->rbegin().base();
- size_t entry_size = entry->second->MemorySize();
- ExpireBackingStoreAt(cache, entry);
- return entry_size;
-}
-
-void CreateCacheSpace(size_t size) {
- // Given a request for |size|, first free from the large cache (until there's
- // only one item left) and then do the same from the small cache if we still
- // don't have enough.
- while (size > 0 && (large_cache->size() > 1 || small_cache->size() > 1)) {
- BackingStoreCache* cache =
- (large_cache->size() > 1) ? large_cache : small_cache;
- while (size > 0 && cache->size() > 1) {
- size_t entry_size = ExpireLastBackingStore(cache);
- if (size > entry_size)
- size -= entry_size;
- else
- size = 0;
- }
- }
- DCHECK(size == 0);
-}
-
-// Creates the backing store for the host based on the dimensions passed in.
-// Removes the existing backing store if there is one.
-BackingStore* CreateBackingStore(RenderWidgetHost* host,
- const gfx::Size& backing_store_size) {
- // Remove any existing backing store in case we're replacing it.
- BackingStoreManager::RemoveBackingStore(host);
-
- if (!large_cache) {
- large_cache = new BackingStoreCache(BackingStoreCache::NO_AUTO_EVICT);
- small_cache = new BackingStoreCache(BackingStoreCache::NO_AUTO_EVICT);
- }
-
- // TODO(erikkay) 32bpp is not always accurate
- size_t new_mem = backing_store_size.GetArea() * 4;
- size_t current_mem = BackingStoreManager::MemorySize();
- size_t max_mem = MaxBackingStoreMemory();
- DCHECK(new_mem < max_mem);
- if (current_mem + new_mem > max_mem) {
- // Need to remove old backing stores to make room for the new one. We
- // don't want to do this when the backing store is being replace by a new
- // one for the same WebContents, but this case won't get called then: we'll
- // have removed the old one in the RemoveBackingStore above, and the cache
- // won't be over-sized.
- CreateCacheSpace((current_mem + new_mem) - max_mem);
- }
- DCHECK((BackingStoreManager::MemorySize() + new_mem) <= max_mem);
-
- BackingStoreCache* cache;
- if (new_mem > kSmallThreshold) {
- // Limit the number of large backing stores (tabs) to the memory tier number
- // (between 2-5). While we allow a larger amount of memory for people who
- // have large windows, this means that those who use small browser windows
- // won't ever cache more than 5 tabs, so they pay a smaller memory cost.
- if (large_cache->size() >= MaxNumberOfBackingStores())
- ExpireLastBackingStore(large_cache);
- cache = large_cache;
- } else {
- cache = small_cache;
- }
- BackingStore* backing_store = RenderWidgetHostImpl::From(
- host)->AllocBackingStore(backing_store_size);
- if (backing_store)
- cache->Put(host, backing_store);
- return backing_store;
-}
-
-int ComputeTotalArea(const std::vector<gfx::Rect>& rects) {
- // We assume that the given rects are non-overlapping, which is a property of
- // the paint rects generated by the PaintAggregator.
-#ifndef NDEBUG
- for (size_t i = 0; i < rects.size(); ++i) {
- for (size_t j = 0; j < rects.size(); ++j) {
- if (i != j)
- DCHECK(!rects[i].Intersects(rects[j]));
- }
- }
-#endif
- int area = 0;
- for (size_t i = 0; i < rects.size(); ++i)
- area += rects[i].size().GetArea();
- return area;
-}
-
-} // namespace
-
-// BackingStoreManager ---------------------------------------------------------
-
-// static
-BackingStore* BackingStoreManager::GetBackingStore(
- RenderWidgetHost* host,
- const gfx::Size& desired_size) {
- BackingStore* backing_store = Lookup(host);
- if (backing_store) {
- // If we already have a backing store, then make sure it is the correct
- // size.
- if (backing_store->size() == desired_size)
- return backing_store;
- backing_store = NULL;
- }
-
- return backing_store;
-}
-
-// static
-void BackingStoreManager::PrepareBackingStore(
- RenderWidgetHost* host,
- const gfx::Size& backing_store_size,
- TransportDIB::Id bitmap,
- const gfx::Rect& bitmap_rect,
- const std::vector<gfx::Rect>& copy_rects,
- float scale_factor,
- const base::Closure& completion_callback,
- bool* needs_full_paint,
- bool* scheduled_completion_callback) {
- BackingStore* backing_store = GetBackingStore(host, backing_store_size);
- if (!backing_store) {
- // We need to get Webkit to generate a new paint here, as we
- // don't have a previous snapshot.
- if (bitmap_rect.size() != backing_store_size ||
- bitmap_rect.x() != 0 || bitmap_rect.y() != 0 ||
- ComputeTotalArea(copy_rects) != backing_store_size.GetArea() ||
- !(backing_store = CreateBackingStore(host, backing_store_size))) {
- DCHECK(needs_full_paint != NULL);
- *needs_full_paint = true;
- *scheduled_completion_callback = false;
- // Makes no sense to paint the transport dib if we are going
- // to request a full paint.
- return;
- }
- }
-
- backing_store->PaintToBackingStore(host->GetProcess(), bitmap,
- bitmap_rect, copy_rects, scale_factor,
- completion_callback,
- scheduled_completion_callback);
-}
-
-// static
-BackingStore* BackingStoreManager::Lookup(RenderWidgetHost* host) {
- if (large_cache) {
- BackingStoreCache::iterator it = large_cache->Get(host);
- if (it != large_cache->end())
- return it->second;
-
- // This moves host to the front of the MRU.
- it = small_cache->Get(host);
- if (it != small_cache->end())
- return it->second;
- }
- return NULL;
-}
-
-// static
-void BackingStoreManager::RemoveBackingStore(RenderWidgetHost* host) {
- if (!large_cache)
- return;
-
- BackingStoreCache* cache = large_cache;
- BackingStoreCache::iterator it = cache->Peek(host);
- if (it == cache->end()) {
- cache = small_cache;
- it = cache->Peek(host);
- if (it == cache->end())
- return;
- }
- cache->Erase(it);
-}
-
-// static
-void BackingStoreManager::RemoveAllBackingStores() {
- if (large_cache) {
- large_cache->Clear();
- small_cache->Clear();
- }
-}
-
-// static
-size_t BackingStoreManager::MemorySize() {
- if (!large_cache)
- return 0;
-
- size_t mem = 0;
- BackingStoreCache::iterator it;
- for (it = large_cache->begin(); it != large_cache->end(); ++it)
- mem += it->second->MemorySize();
-
- for (it = small_cache->begin(); it != small_cache->end(); ++it)
- mem += it->second->MemorySize();
-
- return mem;
-}
-
-} // namespace content
diff --git a/chromium/content/browser/renderer_host/backing_store_manager.h b/chromium/content/browser/renderer_host/backing_store_manager.h
deleted file mode 100644
index 86111889d11..00000000000
--- a/chromium/content/browser/renderer_host/backing_store_manager.h
+++ /dev/null
@@ -1,86 +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_RENDERER_HOST_BACKING_STORE_MANAGER_H_
-#define CONTENT_BROWSER_RENDERER_HOST_BACKING_STORE_MANAGER_H_
-
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/callback_forward.h"
-#include "base/process/process.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/size.h"
-#include "ui/surface/transport_dib.h"
-
-namespace content {
-class BackingStore;
-class RenderWidgetHost;
-
-// This class manages backing stores in the browsr. Every RenderWidgetHost is
-// associated with a backing store which it requests from this class. The
-// hosts don't maintain any references to the backing stores. These backing
-// stores are maintained in a cache which can be trimmed as needed.
-class BackingStoreManager {
- public:
- // Returns a backing store which matches the desired dimensions.
- //
- // backing_store_rect
- // The desired backing store dimensions.
- // Returns a pointer to the backing store on success, NULL on failure.
- static BackingStore* GetBackingStore(RenderWidgetHost* host,
- const gfx::Size& desired_size);
-
- // Makes a backing store which is fully ready for consumption, i.e. the
- // bitmap from the renderer has been copied into the backing store.
- //
- // backing_store_size
- // The desired backing store dimensions, in DIPs.
- // bitmap_section
- // The bitmap section from the renderer.
- // bitmap_rect
- // The rect to be painted into the backing store, in DIPs.
- // scale_factor
- // The device scale factor the backing store is expected to be at.
- // If the backing store's device scale factor doesn't match, it will need
- // to scale |bitmap| at paint time. This will only be out of sync with the
- // backing store scale factor for a few frames, right after device scale
- // changes.
- // needs_full_paint
- // Set if we need to send out a request to paint the view
- // to the renderer.
- static void PrepareBackingStore(
- RenderWidgetHost* host,
- const gfx::Size& backing_store_size,
- TransportDIB::Id bitmap,
- const gfx::Rect& bitmap_rect,
- const std::vector<gfx::Rect>& copy_rects,
- float scale_factor,
- const base::Closure& completion_callback,
- bool* needs_full_paint,
- bool* scheduled_completion_callback);
-
- // Returns a matching backing store for the host.
- // Returns NULL if we fail to find one.
- static BackingStore* Lookup(RenderWidgetHost* host);
-
- // Removes the backing store for the host.
- static void RemoveBackingStore(RenderWidgetHost* host);
-
- // Removes all backing stores.
- static void RemoveAllBackingStores();
-
- // Current size in bytes of the backing store cache.
- static size_t MemorySize();
-
- private:
- // Not intended for instantiation.
- BackingStoreManager() {}
-
- DISALLOW_COPY_AND_ASSIGN(BackingStoreManager);
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_RENDERER_HOST_BACKING_STORE_MANAGER_H_
diff --git a/chromium/content/browser/renderer_host/backing_store_win.cc b/chromium/content/browser/renderer_host/backing_store_win.cc
deleted file mode 100644
index 8e761dd0c32..00000000000
--- a/chromium/content/browser/renderer_host/backing_store_win.cc
+++ /dev/null
@@ -1,186 +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/renderer_host/backing_store_win.h"
-
-#include "base/command_line.h"
-#include "content/browser/renderer_host/render_process_host_impl.h"
-#include "content/browser/renderer_host/render_widget_host_impl.h"
-#include "content/public/common/content_switches.h"
-#include "skia/ext/platform_canvas.h"
-#include "ui/gfx/gdi_util.h"
-#include "ui/gfx/rect_conversions.h"
-#include "ui/gfx/size_conversions.h"
-#include "ui/gfx/win/dpi.h"
-#include "ui/surface/transport_dib.h"
-
-namespace content {
-namespace {
-
-// Creates a dib conforming to the height/width/section parameters passed in.
-HANDLE CreateDIB(HDC dc, int width, int height, int color_depth) {
- BITMAPV5HEADER hdr = {0};
- ZeroMemory(&hdr, sizeof(BITMAPV5HEADER));
-
- // These values are shared with gfx::PlatformDevice
- hdr.bV5Size = sizeof(BITMAPINFOHEADER);
- hdr.bV5Width = width;
- hdr.bV5Height = -height; // minus means top-down bitmap
- hdr.bV5Planes = 1;
- hdr.bV5BitCount = color_depth;
- hdr.bV5Compression = BI_RGB; // no compression
- hdr.bV5SizeImage = 0;
- hdr.bV5XPelsPerMeter = 1;
- hdr.bV5YPelsPerMeter = 1;
- hdr.bV5ClrUsed = 0;
- hdr.bV5ClrImportant = 0;
-
- if (BackingStoreWin::ColorManagementEnabled()) {
- hdr.bV5CSType = LCS_sRGB;
- hdr.bV5Intent = LCS_GM_IMAGES;
- }
-
- void* data = NULL;
- HANDLE dib = CreateDIBSection(dc, reinterpret_cast<BITMAPINFO*>(&hdr),
- 0, &data, NULL, 0);
- DCHECK(data);
- return dib;
-}
-
-} // namespace
-
-BackingStoreWin::BackingStoreWin(RenderWidgetHost* widget,
- const gfx::Size& size)
- : BackingStore(widget, size),
- backing_store_dib_(NULL),
- original_bitmap_(NULL) {
- HDC screen_dc = ::GetDC(NULL);
- color_depth_ = ::GetDeviceCaps(screen_dc, BITSPIXEL);
- // Color depths less than 16 bpp require a palette to be specified. Instead,
- // we specify the desired color depth as 16 which lets the OS to come up
- // with an approximation.
- if (color_depth_ < 16)
- color_depth_ = 16;
- hdc_ = CreateCompatibleDC(screen_dc);
- ReleaseDC(NULL, screen_dc);
-}
-
-BackingStoreWin::~BackingStoreWin() {
- DCHECK(hdc_);
- if (original_bitmap_) {
- SelectObject(hdc_, original_bitmap_);
- }
- if (backing_store_dib_) {
- DeleteObject(backing_store_dib_);
- backing_store_dib_ = NULL;
- }
- DeleteDC(hdc_);
-}
-
-// static
-bool BackingStoreWin::ColorManagementEnabled() {
- static bool enabled = false;
- static bool checked = false;
- if (!checked) {
- checked = true;
- const CommandLine& command = *CommandLine::ForCurrentProcess();
- enabled = command.HasSwitch(switches::kEnableMonitorProfile);
- }
- return enabled;
-}
-
-size_t BackingStoreWin::MemorySize() {
- gfx::Size size_in_pixels = gfx::ToCeiledSize(gfx::ScaleSize(size(),
- gfx::win::GetDeviceScaleFactor()));
- return size_in_pixels.GetArea() * (color_depth_ / 8);
-}
-
-void BackingStoreWin::PaintToBackingStore(
- RenderProcessHost* process,
- TransportDIB::Id bitmap,
- const gfx::Rect& bitmap_rect,
- const std::vector<gfx::Rect>& copy_rects,
- float scale_factor,
- const base::Closure& completion_callback,
- bool* scheduled_completion_callback) {
- TRACE_EVENT0("content", "BackingStoreWin::PaintToBackingStore");
- *scheduled_completion_callback = false;
- gfx::Size size_in_pixels = gfx::ToCeiledSize(gfx::ScaleSize(
- size(), scale_factor));
- if (!backing_store_dib_) {
- backing_store_dib_ = CreateDIB(hdc_,
- size_in_pixels.width(),
- size_in_pixels.height(),
- color_depth_);
- if (!backing_store_dib_) {
- NOTREACHED();
- return;
- }
- original_bitmap_ = SelectObject(hdc_, backing_store_dib_);
- }
-
- TransportDIB* dib = process->GetTransportDIB(bitmap);
- if (!dib)
- return;
-
- gfx::Rect pixel_bitmap_rect = gfx::ToEnclosingRect(
- gfx::ScaleRect(bitmap_rect, scale_factor));
-
- BITMAPINFO bitmap_info;
- memset(&bitmap_info, 0, sizeof(bitmap_info));
- gfx::CreateBitmapHeader(pixel_bitmap_rect.width(),
- pixel_bitmap_rect.height(),
- &bitmap_info.bmiHeader);
- // Account for a bitmap_rect that exceeds the bounds of our view.
- gfx::Rect view_rect(size());
-
- for (size_t i = 0; i < copy_rects.size(); i++) {
- gfx::Rect paint_rect = gfx::IntersectRects(view_rect, copy_rects[i]);
- gfx::Rect pixel_copy_rect = gfx::ToEnclosingRect(
- gfx::ScaleRect(paint_rect, scale_factor));
- gfx::Rect target_rect = pixel_copy_rect;
- gfx::StretchDIBits(hdc_,
- target_rect.x(),
- target_rect.y(),
- target_rect.width(),
- target_rect.height(),
- pixel_copy_rect.x() - pixel_bitmap_rect.x(),
- pixel_copy_rect.y() - pixel_bitmap_rect.y(),
- pixel_copy_rect.width(),
- pixel_copy_rect.height(),
- dib->memory(),
- &bitmap_info);
- }
-}
-
-bool BackingStoreWin::CopyFromBackingStore(const gfx::Rect& rect,
- skia::PlatformBitmap* output) {
- TRACE_EVENT0("content", "BackingStoreWin::CopyFromBackingStore");
- // TODO(kevers): Make sure this works with HiDPI backing stores.
- if (!output->Allocate(rect.width(), rect.height(), true))
- return false;
-
- HDC temp_dc = output->GetSurface();
- BitBlt(temp_dc, 0, 0, rect.width(), rect.height(),
- hdc(), rect.x(), rect.y(), SRCCOPY);
- return true;
-}
-
-void BackingStoreWin::ScrollBackingStore(const gfx::Vector2d& delta,
- const gfx::Rect& clip_rect,
- const gfx::Size& view_size) {
- TRACE_EVENT0("content", "BackingStoreWin::ScrollBackingStore");
- // TODO(darin): this doesn't work if delta x() and y() are both non-zero!
- DCHECK(delta.x() == 0 || delta.y() == 0);
-
- float scale = gfx::win::GetDeviceScaleFactor();
- gfx::Rect screen_rect = gfx::ToEnclosingRect(
- gfx::ScaleRect(clip_rect, scale));
- int dx = static_cast<int>(delta.x() * scale);
- int dy = static_cast<int>(delta.y() * scale);
- RECT damaged_rect, r = screen_rect.ToRECT();
- ScrollDC(hdc_, dx, dy, NULL, &r, NULL, &damaged_rect);
-}
-
-} // namespace content
diff --git a/chromium/content/browser/renderer_host/backing_store_win.h b/chromium/content/browser/renderer_host/backing_store_win.h
deleted file mode 100644
index abce4b19f37..00000000000
--- a/chromium/content/browser/renderer_host/backing_store_win.h
+++ /dev/null
@@ -1,60 +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_RENDERER_HOST_BACKING_STORE_WIN_H_
-#define CONTENT_BROWSER_RENDERER_HOST_BACKING_STORE_WIN_H_
-
-#include <windows.h>
-
-#include "base/basictypes.h"
-#include "base/compiler_specific.h"
-#include "content/browser/renderer_host/backing_store.h"
-
-namespace content {
-
-class BackingStoreWin : public BackingStore {
- public:
- BackingStoreWin(RenderWidgetHost* widget, const gfx::Size& size);
- virtual ~BackingStoreWin();
-
- HDC hdc() { return hdc_; }
-
- // Returns true if we should convert to the monitor profile when painting.
- static bool ColorManagementEnabled();
-
- // BackingStore implementation.
- virtual size_t MemorySize() OVERRIDE;
- virtual void PaintToBackingStore(
- RenderProcessHost* process,
- TransportDIB::Id bitmap,
- const gfx::Rect& bitmap_rect,
- const std::vector<gfx::Rect>& copy_rects,
- float scale_factor,
- const base::Closure& completion_callback,
- bool* scheduled_completion_callback) OVERRIDE;
- virtual bool CopyFromBackingStore(const gfx::Rect& rect,
- skia::PlatformBitmap* output) OVERRIDE;
- virtual void ScrollBackingStore(const gfx::Vector2d& delta,
- const gfx::Rect& clip_rect,
- const gfx::Size& view_size) OVERRIDE;
-
- private:
- // The backing store dc.
- HDC hdc_;
-
- // Handle to the backing store dib.
- HANDLE backing_store_dib_;
-
- // Handle to the original bitmap in the dc.
- HANDLE original_bitmap_;
-
- // Number of bits per pixel of the screen.
- int color_depth_;
-
- DISALLOW_COPY_AND_ASSIGN(BackingStoreWin);
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_RENDERER_HOST_BACKING_STORE_WIN_H_
diff --git a/chromium/content/browser/renderer_host/clipboard_message_filter.cc b/chromium/content/browser/renderer_host/clipboard_message_filter.cc
index bacce45dde4..3ea8071cb1c 100644
--- a/chromium/content/browser/renderer_host/clipboard_message_filter.cc
+++ b/chromium/content/browser/renderer_host/clipboard_message_filter.cc
@@ -9,6 +9,7 @@
#include "base/location.h"
#include "base/memory/scoped_ptr.h"
#include "base/stl_util.h"
+#include "base/strings/utf_string_conversions.h"
#include "content/common/clipboard_messages.h"
#include "content/public/browser/browser_context.h"
#include "ipc/ipc_message_macros.h"
@@ -19,18 +20,8 @@
namespace content {
-
namespace {
-// On Windows, the write must be performed on the UI thread because the
-// clipboard object from the IO thread cannot create windows so it cannot be
-// the "owner" of the clipboard's contents. See http://crbug.com/5823.
-void WriteObjectsOnUIThread(ui::Clipboard::ObjectMap* objects) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- static ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
- clipboard->WriteObjects(ui::CLIPBOARD_TYPE_COPY_PASTE, *objects);
-}
-
enum BitmapPolicy {
kFilterBitmap,
kAllowBitmap,
@@ -61,7 +52,8 @@ void SanitizeObjectMap(ui::Clipboard::ObjectMap* objects,
} // namespace
-ClipboardMessageFilter::ClipboardMessageFilter() {}
+ClipboardMessageFilter::ClipboardMessageFilter()
+ : BrowserMessageFilter(ClipboardMsgStart) {}
void ClipboardMessageFilter::OverrideThreadForMessage(
const IPC::Message& message, BrowserThread::ID* thread) {
@@ -85,10 +77,9 @@ void ClipboardMessageFilter::OverrideThreadForMessage(
#endif
}
-bool ClipboardMessageFilter::OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) {
+bool ClipboardMessageFilter::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(ClipboardMessageFilter, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(ClipboardMessageFilter, message)
IPC_MESSAGE_HANDLER(ClipboardHostMsg_WriteObjectsAsync, OnWriteObjectsAsync)
IPC_MESSAGE_HANDLER(ClipboardHostMsg_WriteObjectsSync, OnWriteObjectsSync)
IPC_MESSAGE_HANDLER(ClipboardHostMsg_GetSequenceNumber, OnGetSequenceNumber)
@@ -97,12 +88,10 @@ bool ClipboardMessageFilter::OnMessageReceived(const IPC::Message& message,
IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadAvailableTypes,
OnReadAvailableTypes)
IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadText, OnReadText)
- IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadAsciiText, OnReadAsciiText)
IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadHTML, OnReadHTML)
IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadRTF, OnReadRTF)
IPC_MESSAGE_HANDLER_DELAY_REPLY(ClipboardHostMsg_ReadImage, OnReadImage)
IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadCustomData, OnReadCustomData)
- IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadData, OnReadData)
#if defined(OS_MACOSX)
IPC_MESSAGE_HANDLER(ClipboardHostMsg_FindPboardWriteStringAsync,
OnFindPboardWriteString)
@@ -141,10 +130,22 @@ void ClipboardMessageFilter::OnWriteObjectsSync(
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
- base::Bind(&WriteObjectsOnUIThread,
+ base::Bind(&ClipboardMessageFilter::WriteObjectsOnUIThread,
base::Owned(long_living_objects.release())));
}
+// On Windows, the write must be performed on the UI thread because the
+// clipboard object from the IO thread cannot create windows so it cannot be
+// the "owner" of the clipboard's contents. See http://crbug.com/5823.
+// TODO(dcheng): Temporarily a member of ClipboardMessageFilter so it can access
+// ui::Clipboard::WriteObjects().
+void ClipboardMessageFilter::WriteObjectsOnUIThread(
+ const ui::Clipboard::ObjectMap* objects) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ static ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
+ clipboard->WriteObjects(ui::CLIPBOARD_TYPE_COPY_PASTE, *objects);
+}
+
void ClipboardMessageFilter::OnWriteObjectsAsync(
const ui::Clipboard::ObjectMap& objects) {
// This async message doesn't support shared-memory based bitmaps; they must
@@ -180,11 +181,33 @@ void ClipboardMessageFilter::OnReadAvailableTypes(
GetClipboard()->ReadAvailableTypes(type, types, contains_filenames);
}
-void ClipboardMessageFilter::OnIsFormatAvailable(
- const ui::Clipboard::FormatType& format,
- ui::ClipboardType type,
- bool* result) {
- *result = GetClipboard()->IsFormatAvailable(format, type);
+void ClipboardMessageFilter::OnIsFormatAvailable(ClipboardFormat format,
+ ui::ClipboardType type,
+ bool* result) {
+ switch (format) {
+ case CLIPBOARD_FORMAT_PLAINTEXT:
+ *result = GetClipboard()->IsFormatAvailable(
+ ui::Clipboard::GetPlainTextWFormatType(), type) ||
+ GetClipboard()->IsFormatAvailable(
+ ui::Clipboard::GetPlainTextFormatType(), type);
+ break;
+ case CLIPBOARD_FORMAT_HTML:
+ *result = GetClipboard()->IsFormatAvailable(
+ ui::Clipboard::GetHtmlFormatType(), type);
+ break;
+ case CLIPBOARD_FORMAT_SMART_PASTE:
+ *result = GetClipboard()->IsFormatAvailable(
+ ui::Clipboard::GetWebKitSmartPasteFormatType(), type);
+ break;
+ case CLIPBOARD_FORMAT_BOOKMARK:
+#if defined(OS_WIN) || defined(OS_MACOSX)
+ *result = GetClipboard()->IsFormatAvailable(
+ ui::Clipboard::GetUrlWFormatType(), type);
+#else
+ *result = false;
+#endif
+ break;
+ }
}
void ClipboardMessageFilter::OnClear(ui::ClipboardType type) {
@@ -193,12 +216,17 @@ void ClipboardMessageFilter::OnClear(ui::ClipboardType type) {
void ClipboardMessageFilter::OnReadText(ui::ClipboardType type,
base::string16* result) {
- GetClipboard()->ReadText(type, result);
-}
-
-void ClipboardMessageFilter::OnReadAsciiText(ui::ClipboardType type,
- std::string* result) {
- GetClipboard()->ReadAsciiText(type, result);
+ if (GetClipboard()->IsFormatAvailable(
+ ui::Clipboard::GetPlainTextWFormatType(), type)) {
+ GetClipboard()->ReadText(type, result);
+ } else if (GetClipboard()->IsFormatAvailable(
+ ui::Clipboard::GetPlainTextFormatType(), type)) {
+ std::string ascii;
+ GetClipboard()->ReadAsciiText(type, &ascii);
+ *result = base::ASCIIToUTF16(ascii);
+ } else {
+ result->clear();
+ }
}
void ClipboardMessageFilter::OnReadHTML(ui::ClipboardType type,
@@ -258,11 +286,6 @@ void ClipboardMessageFilter::OnReadCustomData(ui::ClipboardType clipboard_type,
GetClipboard()->ReadCustomData(clipboard_type, type, result);
}
-void ClipboardMessageFilter::OnReadData(const ui::Clipboard::FormatType& format,
- std::string* data) {
- GetClipboard()->ReadData(format, data);
-}
-
// static
ui::Clipboard* ClipboardMessageFilter::GetClipboard() {
// We have a static instance of the clipboard service for use by all message
diff --git a/chromium/content/browser/renderer_host/clipboard_message_filter.h b/chromium/content/browser/renderer_host/clipboard_message_filter.h
index dd2784be531..3fcb94edc7a 100644
--- a/chromium/content/browser/renderer_host/clipboard_message_filter.h
+++ b/chromium/content/browser/renderer_host/clipboard_message_filter.h
@@ -9,6 +9,7 @@
#include <vector>
#include "base/basictypes.h"
+#include "content/common/clipboard_format.h"
#include "content/public/browser/browser_message_filter.h"
#include "ui/base/clipboard/clipboard.h"
@@ -23,18 +24,18 @@ class ClipboardMessageFilter : public BrowserMessageFilter {
virtual void OverrideThreadForMessage(
const IPC::Message& message,
BrowserThread::ID* thread) OVERRIDE;
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
private:
virtual ~ClipboardMessageFilter();
void OnWriteObjectsAsync(const ui::Clipboard::ObjectMap& objects);
void OnWriteObjectsSync(const ui::Clipboard::ObjectMap& objects,
base::SharedMemoryHandle bitmap_handle);
+ static void WriteObjectsOnUIThread(const ui::Clipboard::ObjectMap* objects);
void OnGetSequenceNumber(const ui::ClipboardType type,
uint64* sequence_number);
- void OnIsFormatAvailable(const ui::Clipboard::FormatType& format,
+ void OnIsFormatAvailable(ClipboardFormat format,
ui::ClipboardType type,
bool* result);
void OnClear(ui::ClipboardType type);
@@ -42,7 +43,6 @@ class ClipboardMessageFilter : public BrowserMessageFilter {
std::vector<base::string16>* types,
bool* contains_filenames);
void OnReadText(ui::ClipboardType type, base::string16* result);
- void OnReadAsciiText(ui::ClipboardType type, std::string* result);
void OnReadHTML(ui::ClipboardType type,
base::string16* markup,
GURL* url,
diff --git a/chromium/content/browser/renderer_host/compositing_iosurface_context_mac.h b/chromium/content/browser/renderer_host/compositing_iosurface_context_mac.h
index da9fc44597d..52a98989293 100644
--- a/chromium/content/browser/renderer_host/compositing_iosurface_context_mac.h
+++ b/chromium/content/browser/renderer_host/compositing_iosurface_context_mac.h
@@ -14,15 +14,24 @@
#include "base/mac/scoped_nsobject.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
+#include "content/public/browser/gpu_data_manager_observer.h"
+#include "ui/gl/scoped_cgl.h"
namespace content {
class CompositingIOSurfaceShaderPrograms;
class CompositingIOSurfaceContext
- : public base::RefCounted<CompositingIOSurfaceContext> {
+ : public base::RefCounted<CompositingIOSurfaceContext>,
+ public content::GpuDataManagerObserver {
public:
- enum { kOffscreenContextWindowNumber = -2 };
+ enum {
+ // The number used to look up the context used for async readback and for
+ // initializing the IOSurface.
+ kOffscreenContextWindowNumber = -2,
+ // The number used to look up the context used by CAOpenGLLayers.
+ kCALayerContextWindowNumber = -3,
+ };
// Get or create a GL context for the specified window with the specified
// surface ordering. Share these GL contexts as much as possible because
@@ -30,42 +39,42 @@ class CompositingIOSurfaceContext
// http://crbug.com/180463
static scoped_refptr<CompositingIOSurfaceContext> Get(int window_number);
- // Mark that all the currently existing GL contexts shouldn't be returned
- // anymore by Get, but rather, new contexts should be created. This is
- // called as a precaution when unexpected GL errors occur.
- static void MarkExistingContextsAsNotShareable();
+ // Mark that all the GL contexts in the same sharegroup as this context as
+ // invalid, so they shouldn't be returned anymore by Get, but rather, new
+ // contexts should be created. This is called as a precaution when unexpected
+ // GL errors occur.
+ void PoisonContextAndSharegroup();
+ bool HasBeenPoisoned() const { return poisoned_; }
CompositingIOSurfaceShaderPrograms* shader_program_cache() const {
return shader_program_cache_.get();
}
- NSOpenGLContext* nsgl_context() const { return nsgl_context_; }
CGLContextObj cgl_context() const { return cgl_context_; }
bool is_vsync_disabled() const { return is_vsync_disabled_; }
int window_number() const { return window_number_; }
- bool IsVendorIntel();
+ // content::GpuDataManagerObserver implementation.
+ virtual void OnGpuSwitching() OVERRIDE;
private:
friend class base::RefCounted<CompositingIOSurfaceContext>;
CompositingIOSurfaceContext(
int window_number,
- NSOpenGLContext* nsgl_context,
+ base::ScopedTypeRef<CGLContextObj> clg_context_strong,
CGLContextObj clg_context,
bool is_vsync_disabled_,
scoped_ptr<CompositingIOSurfaceShaderPrograms> shader_program_cache);
- ~CompositingIOSurfaceContext();
+ virtual ~CompositingIOSurfaceContext();
int window_number_;
- base::scoped_nsobject<NSOpenGLContext> nsgl_context_;
- CGLContextObj cgl_context_; // weak, backed by |nsgl_context_|
+ base::ScopedTypeRef<CGLContextObj> cgl_context_strong_;
+ // Weak, backed by |cgl_context_strong_|.
+ CGLContextObj cgl_context_;
+
bool is_vsync_disabled_;
scoped_ptr<CompositingIOSurfaceShaderPrograms> shader_program_cache_;
- bool can_be_shared_;
-
- bool initialized_is_intel_;
- bool is_intel_;
- GLint screen_;
+ bool poisoned_;
// The global map from window number and window ordering to
// context data.
diff --git a/chromium/content/browser/renderer_host/compositing_iosurface_context_mac.mm b/chromium/content/browser/renderer_host/compositing_iosurface_context_mac.mm
index 4c6cbf1c12a..a66bf5022d3 100644
--- a/chromium/content/browser/renderer_host/compositing_iosurface_context_mac.mm
+++ b/chromium/content/browser/renderer_host/compositing_iosurface_context_mac.mm
@@ -12,6 +12,8 @@
#include "base/debug/trace_event.h"
#include "base/logging.h"
#include "content/browser/renderer_host/compositing_iosurface_shader_programs_mac.h"
+#include "content/browser/gpu/gpu_data_manager_impl.h"
+#include "ui/base/ui_base_switches.h"
#include "ui/gl/gl_switches.h"
#include "ui/gl/gpu_switching_manager.h"
@@ -25,65 +27,68 @@ CompositingIOSurfaceContext::Get(int window_number) {
// Return the context for this window_number, if it exists.
WindowMap::iterator found = window_map()->find(window_number);
if (found != window_map()->end()) {
- DCHECK(found->second->can_be_shared_);
+ DCHECK(!found->second->poisoned_);
return found->second;
}
- std::vector<NSOpenGLPixelFormatAttribute> attributes;
- attributes.push_back(NSOpenGLPFADoubleBuffer);
- // We don't need a depth buffer - try setting its size to 0...
- attributes.push_back(NSOpenGLPFADepthSize); attributes.push_back(0);
- if (ui::GpuSwitchingManager::GetInstance()->SupportsDualGpus())
- attributes.push_back(NSOpenGLPFAAllowOfflineRenderers);
- attributes.push_back(0);
-
- base::scoped_nsobject<NSOpenGLPixelFormat> glPixelFormat(
- [[NSOpenGLPixelFormat alloc] initWithAttributes:&attributes.front()]);
- if (!glPixelFormat) {
- LOG(ERROR) << "NSOpenGLPixelFormat initWithAttributes failed";
+ static bool is_vsync_disabled =
+ CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableGpuVsync);
+
+ base::ScopedTypeRef<CGLContextObj> cgl_context_strong;
+ CGLContextObj cgl_context = NULL;
+ CGLError error = kCGLNoError;
+
+ // Create the pixel format object for the context.
+ std::vector<CGLPixelFormatAttribute> attribs;
+ attribs.push_back(kCGLPFADepthSize);
+ attribs.push_back(static_cast<CGLPixelFormatAttribute>(0));
+ if (ui::GpuSwitchingManager::GetInstance()->SupportsDualGpus()) {
+ attribs.push_back(kCGLPFAAllowOfflineRenderers);
+ attribs.push_back(static_cast<CGLPixelFormatAttribute>(1));
+ }
+ attribs.push_back(static_cast<CGLPixelFormatAttribute>(0));
+ GLint number_virtual_screens = 0;
+ base::ScopedTypeRef<CGLPixelFormatObj> pixel_format;
+ error = CGLChoosePixelFormat(&attribs.front(),
+ pixel_format.InitializeInto(),
+ &number_virtual_screens);
+ if (error != kCGLNoError) {
+ LOG(ERROR) << "Failed to create pixel format object.";
return NULL;
}
// Create all contexts in the same share group so that the textures don't
// need to be recreated when transitioning contexts.
- NSOpenGLContext* share_context = nil;
+ CGLContextObj share_context = NULL;
if (!window_map()->empty())
- share_context = window_map()->begin()->second->nsgl_context();
- base::scoped_nsobject<NSOpenGLContext> nsgl_context(
- [[NSOpenGLContext alloc] initWithFormat:glPixelFormat
- shareContext:share_context]);
- if (!nsgl_context) {
- LOG(ERROR) << "NSOpenGLContext initWithFormat failed";
+ share_context = window_map()->begin()->second->cgl_context();
+ error = CGLCreateContext(
+ pixel_format, share_context, cgl_context_strong.InitializeInto());
+ if (error != kCGLNoError) {
+ LOG(ERROR) << "Failed to create context object.";
return NULL;
}
+ cgl_context = cgl_context_strong;
- CGLContextObj cgl_context = (CGLContextObj)[nsgl_context CGLContextObj];
- if (!cgl_context) {
- LOG(ERROR) << "CGLContextObj failed";
- return NULL;
- }
-
- // Draw at beam vsync.
- bool is_vsync_disabled =
- CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableGpuVsync);
- GLint swapInterval = is_vsync_disabled ? 0 : 1;
- [nsgl_context setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval];
+ // Note that VSync is ignored because CoreAnimation will automatically
+ // rate limit draws.
// Prepare the shader program cache. Precompile the shader programs
// needed to draw the IO Surface for non-offscreen contexts.
- CGLSetCurrentContext(cgl_context);
- scoped_ptr<CompositingIOSurfaceShaderPrograms> shader_program_cache(
- new CompositingIOSurfaceShaderPrograms());
bool prepared = false;
- if (window_number == kOffscreenContextWindowNumber) {
- prepared = true;
- } else {
- prepared = (
- shader_program_cache->UseBlitProgram() &&
- shader_program_cache->UseSolidWhiteProgram());
+ scoped_ptr<CompositingIOSurfaceShaderPrograms> shader_program_cache;
+ {
+ gfx::ScopedCGLSetCurrentContext scoped_set_current_context(cgl_context);
+ shader_program_cache.reset(new CompositingIOSurfaceShaderPrograms());
+ if (window_number == kOffscreenContextWindowNumber) {
+ prepared = true;
+ } else {
+ prepared = (
+ shader_program_cache->UseBlitProgram() &&
+ shader_program_cache->UseSolidWhiteProgram());
+ }
+ glUseProgram(0u);
}
- glUseProgram(0u);
- CGLSetCurrentContext(0);
if (!prepared) {
LOG(ERROR) << "IOSurface failed to compile/link required shader programs.";
return NULL;
@@ -91,46 +96,50 @@ CompositingIOSurfaceContext::Get(int window_number) {
return new CompositingIOSurfaceContext(
window_number,
- nsgl_context.release(),
+ cgl_context_strong,
cgl_context,
is_vsync_disabled,
shader_program_cache.Pass());
}
-// static
-void CompositingIOSurfaceContext::MarkExistingContextsAsNotShareable() {
+void CompositingIOSurfaceContext::PoisonContextAndSharegroup() {
+ if (poisoned_)
+ return;
+
for (WindowMap::iterator it = window_map()->begin();
it != window_map()->end();
++it) {
- it->second->can_be_shared_ = false;
+ it->second->poisoned_ = true;
}
window_map()->clear();
}
CompositingIOSurfaceContext::CompositingIOSurfaceContext(
int window_number,
- NSOpenGLContext* nsgl_context,
+ base::ScopedTypeRef<CGLContextObj> cgl_context_strong,
CGLContextObj cgl_context,
bool is_vsync_disabled,
scoped_ptr<CompositingIOSurfaceShaderPrograms> shader_program_cache)
: window_number_(window_number),
- nsgl_context_(nsgl_context),
+ cgl_context_strong_(cgl_context_strong),
cgl_context_(cgl_context),
is_vsync_disabled_(is_vsync_disabled),
shader_program_cache_(shader_program_cache.Pass()),
- can_be_shared_(true),
- initialized_is_intel_(false),
- is_intel_(false),
- screen_(0) {
+ poisoned_(false) {
DCHECK(window_map()->find(window_number_) == window_map()->end());
window_map()->insert(std::make_pair(window_number_, this));
+
+ GpuDataManager::GetInstance()->AddObserver(this);
}
CompositingIOSurfaceContext::~CompositingIOSurfaceContext() {
- CGLSetCurrentContext(cgl_context_);
- shader_program_cache_->Reset();
- CGLSetCurrentContext(0);
- if (can_be_shared_) {
+ GpuDataManager::GetInstance()->RemoveObserver(this);
+
+ {
+ gfx::ScopedCGLSetCurrentContext scoped_set_current_context(cgl_context_);
+ shader_program_cache_->Reset();
+ }
+ if (!poisoned_) {
DCHECK(window_map()->find(window_number_) != window_map()->end());
DCHECK(window_map()->find(window_number_)->second == this);
window_map()->erase(window_number_);
@@ -141,18 +150,11 @@ CompositingIOSurfaceContext::~CompositingIOSurfaceContext() {
}
}
-bool CompositingIOSurfaceContext::IsVendorIntel() {
- GLint screen;
- CGLGetVirtualScreen(cgl_context(), &screen);
- if (screen != screen_)
- initialized_is_intel_ = false;
- screen_ = screen;
- if (!initialized_is_intel_) {
- is_intel_ = strstr(reinterpret_cast<const char*>(glGetString(GL_VENDOR)),
- "Intel") != NULL;
- initialized_is_intel_ = true;
- }
- return is_intel_;
+void CompositingIOSurfaceContext::OnGpuSwitching() {
+ // Recreate all browser-side GL contexts whenever the GPU switches. If this
+ // is not done, performance will suffer.
+ // http://crbug.com/361493
+ PoisonContextAndSharegroup();
}
// static
diff --git a/chromium/content/browser/renderer_host/compositing_iosurface_layer_mac.h b/chromium/content/browser/renderer_host/compositing_iosurface_layer_mac.h
index a03ab1cb5df..0449ae792c3 100644
--- a/chromium/content/browser/renderer_host/compositing_iosurface_layer_mac.h
+++ b/chromium/content/browser/renderer_host/compositing_iosurface_layer_mac.h
@@ -9,32 +9,63 @@
#include "base/mac/scoped_cftyperef.h"
#include "base/memory/ref_counted.h"
+#include "base/timer/timer.h"
namespace content {
+class CompositingIOSurfaceMac;
class CompositingIOSurfaceContext;
-class RenderWidgetHostViewMac;
-}
+class CompositingIOSurfaceLayerHelper;
+
+class CompositingIOSurfaceLayerClient {
+ public:
+ virtual void AcceleratedLayerDidDrawFrame(bool succeeded) = 0;
+};
+
+} // namespace content
// The CoreAnimation layer for drawing accelerated content.
@interface CompositingIOSurfaceLayer : CAOpenGLLayer {
@private
- content::RenderWidgetHostViewMac* renderWidgetHostView_;
+ content::CompositingIOSurfaceLayerClient* client_;
+ scoped_refptr<content::CompositingIOSurfaceMac> iosurface_;
scoped_refptr<content::CompositingIOSurfaceContext> context_;
+
+ // The browser places back-pressure on the GPU by not acknowledging swap
+ // calls until they appear on the screen. This can lead to hangs if the
+ // view is moved offscreen (among other things). Prevent hangs by always
+ // acknowledging the frame after timeout of 1/6th of a second has passed.
+ scoped_ptr<content::CompositingIOSurfaceLayerHelper> helper_;
+ scoped_ptr<base::DelayTimer<content::CompositingIOSurfaceLayerHelper>> timer_;
+
+ // Used to track when canDrawInCGLContext should return YES. This can be
+ // in response to receiving a new compositor frame, or from any of the events
+ // that cause setNeedsDisplay to be called on the layer.
+ BOOL needs_display_;
+
+ // This is set when a frame is received, and un-set when the frame is drawn.
+ BOOL has_pending_frame_;
+
+ // Incremented every time that this layer is asked to draw but does not have
+ // new content to draw.
+ uint64 did_not_draw_counter_;
}
-@property(nonatomic, readonly)
- scoped_refptr<content::CompositingIOSurfaceContext> context;
+- (content::CompositingIOSurfaceMac*)iosurface;
+- (content::CompositingIOSurfaceContext*)context;
-- (id)initWithRenderWidgetHostViewMac:(content::RenderWidgetHostViewMac*)r;
+- (id)initWithIOSurface:(scoped_refptr<content::CompositingIOSurfaceMac>)
+ iosurface
+ withScaleFactor:(float)scale_factor
+ withClient:(content::CompositingIOSurfaceLayerClient*)client;
-// Update the scale factor of the layer to match the scale factor of the
-// IOSurface.
-- (void)updateScaleFactor;
+// Mark that the client is no longer valid and cannot be called back into.
+- (void)resetClient;
-// Remove this layer from the layer heirarchy, and mark that
-// |renderWidgetHostView_| is no longer valid and may no longer be dereferenced.
-- (void)disableCompositing;
+// Called when a new frame is received.
+- (void)gotNewFrame;
+- (void)setNeedsDisplayAndDisplayAndAck;
+- (void)displayIfNeededAndAck;
@end
#endif // CONTENT_BROWSER_RENDERER_HOST_COMPOSITING_IOSURFACE_LAYER_MAC_H_
diff --git a/chromium/content/browser/renderer_host/compositing_iosurface_layer_mac.mm b/chromium/content/browser/renderer_host/compositing_iosurface_layer_mac.mm
index 0a06d789b80..bda4c5fd9e4 100644
--- a/chromium/content/browser/renderer_host/compositing_iosurface_layer_mac.mm
+++ b/chromium/content/browser/renderer_host/compositing_iosurface_layer_mac.mm
@@ -7,6 +7,7 @@
#include <CoreFoundation/CoreFoundation.h>
#include <OpenGL/gl.h>
+#include "base/mac/mac_util.h"
#include "base/mac/sdk_forward_declarations.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_view_mac.h"
@@ -14,71 +15,184 @@
#include "content/browser/renderer_host/compositing_iosurface_mac.h"
#include "ui/base/cocoa/animation_utils.h"
#include "ui/gfx/size_conversions.h"
+#include "ui/gl/gpu_switching_manager.h"
+
+@interface CompositingIOSurfaceLayer(Private)
+- (void)ackPendingFrame:(bool)success;
+- (void)timerFired;
+@end
+
+namespace content {
+
+// The base::DelayTimer needs a C++ class to operate on, rather than Objective C
+// class. This helper class provides a bridge between the two.
+class CompositingIOSurfaceLayerHelper {
+ public:
+ CompositingIOSurfaceLayerHelper(CompositingIOSurfaceLayer* layer)
+ : layer_(layer) {}
+ void TimerFired() {
+ [layer_ timerFired];
+ }
+ private:
+ CompositingIOSurfaceLayer* layer_;
+};
+
+} // namespace content
@implementation CompositingIOSurfaceLayer
-@synthesize context = context_;
+- (content::CompositingIOSurfaceMac*)iosurface {
+ return iosurface_.get();
+}
+
+- (content::CompositingIOSurfaceContext*)context {
+ return context_.get();
+}
-- (id)initWithRenderWidgetHostViewMac:(content::RenderWidgetHostViewMac*)r {
+- (id)initWithIOSurface:(scoped_refptr<content::CompositingIOSurfaceMac>)
+ iosurface
+ withScaleFactor:(float)scale_factor
+ withClient:(content::CompositingIOSurfaceLayerClient*)client {
if (self = [super init]) {
- renderWidgetHostView_ = r;
+ iosurface_ = iosurface;
+ client_ = client;
+ helper_.reset(new content::CompositingIOSurfaceLayerHelper(self));
+ timer_.reset(new base::DelayTimer<content::CompositingIOSurfaceLayerHelper>(
+ FROM_HERE,
+ base::TimeDelta::FromSeconds(1) / 6,
+ helper_.get(),
+ &content::CompositingIOSurfaceLayerHelper::TimerFired));
+
+ context_ = content::CompositingIOSurfaceContext::Get(
+ content::CompositingIOSurfaceContext::kCALayerContextWindowNumber);
+ DCHECK(context_);
+ needs_display_ = NO;
+ has_pending_frame_ = NO;
+ did_not_draw_counter_ = 0;
- ScopedCAActionDisabler disabler;
[self setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)];
+ [self setAnchorPoint:CGPointMake(0, 0)];
+ // Setting contents gravity is necessary to prevent the layer from being
+ // scaled during dyanmic resizes (especially with devtools open).
[self setContentsGravity:kCAGravityTopLeft];
- [self setFrame:NSRectToCGRect(
- [renderWidgetHostView_->cocoa_view() bounds])];
- [self setNeedsDisplay];
- [self updateScaleFactor];
+ if ([self respondsToSelector:(@selector(setContentsScale:))]) {
+ [self setContentsScale:scale_factor];
+ }
}
return self;
}
-- (void)updateScaleFactor {
- if (!renderWidgetHostView_ ||
- ![self respondsToSelector:(@selector(contentsScale))] ||
- ![self respondsToSelector:(@selector(setContentsScale:))])
- return;
+- (void)resetClient {
+ // Any acks that were waiting on this layer to draw will not occur, so ack
+ // them now to prevent blocking the renderer.
+ [self ackPendingFrame:true];
+ client_ = NULL;
+}
- float current_scale_factor = [self contentsScale];
- float new_scale_factor = current_scale_factor;
- if (renderWidgetHostView_->compositing_iosurface_) {
- new_scale_factor =
- renderWidgetHostView_->compositing_iosurface_->scale_factor();
+- (void)gotNewFrame {
+ has_pending_frame_ = YES;
+ timer_->Reset();
+
+ // A trace value of 2 indicates that there is a pending swap ack. See
+ // canDrawInCGLContext for other value meanings.
+ TRACE_COUNTER_ID1("browser", "PendingSwapAck", self, 2);
+
+ if (context_ && context_->is_vsync_disabled()) {
+ // If vsync is disabled, draw immediately and don't bother trying to use
+ // the isAsynchronous property to ensure smooth animation.
+ [self setNeedsDisplayAndDisplayAndAck];
+ } else {
+ needs_display_ = YES;
+ if (![self isAsynchronous])
+ [self setAsynchronous:YES];
}
+}
+
+// Private methods:
+
+- (void)setNeedsDisplayAndDisplayAndAck {
+ // Workaround for crbug.com/395827
+ if ([self isAsynchronous])
+ [self setAsynchronous:NO];
+
+ [self setNeedsDisplay];
+ [self displayIfNeededAndAck];
+}
+
+- (void)displayIfNeededAndAck {
+ // Workaround for crbug.com/395827
+ if ([self isAsynchronous])
+ [self setAsynchronous:NO];
+
+ [self displayIfNeeded];
- if (new_scale_factor == current_scale_factor)
+ // Calls to setNeedsDisplay can sometimes be ignored, especially if issued
+ // rapidly (e.g, with vsync off). This is unacceptable because the failure
+ // to ack a single frame will hang the renderer. Ensure that the renderer
+ // not be blocked by lying and claiming that we drew the frame.
+ [self ackPendingFrame:true];
+}
+
+- (void)ackPendingFrame:(bool)success {
+ if (!has_pending_frame_)
return;
- ScopedCAActionDisabler disabler;
- [self setContentsScale:new_scale_factor];
+ TRACE_COUNTER_ID1("browser", "PendingSwapAck", self, 0);
+ has_pending_frame_ = NO;
+ if (client_)
+ client_->AcceleratedLayerDidDrawFrame(success);
}
-- (void)disableCompositing{
- ScopedCAActionDisabler disabler;
- [self removeFromSuperlayer];
- renderWidgetHostView_ = nil;
+- (void)timerFired {
+ if (has_pending_frame_)
+ [self setNeedsDisplayAndDisplayAndAck];
}
// The remaining methods implement the CAOpenGLLayer interface.
-- (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat {
- if (!renderWidgetHostView_)
- return nil;
+- (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask {
+ if (!context_)
+ return [super copyCGLPixelFormatForDisplayMask:mask];
+ return CGLRetainPixelFormat(CGLGetPixelFormat(context_->cgl_context()));
+}
- context_ = renderWidgetHostView_->compositing_iosurface_context_;
+- (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat {
if (!context_)
- return nil;
+ return [super copyCGLContextForPixelFormat:pixelFormat];
+ return CGLRetainContext(context_->cgl_context());
+}
- return context_->cgl_context();
+- (void)setNeedsDisplay {
+ needs_display_ = YES;
+ [super setNeedsDisplay];
}
-- (void)releaseCGLContext:(CGLContextObj)glContext {
- if (!context_.get())
- return;
+- (BOOL)canDrawInCGLContext:(CGLContextObj)glContext
+ pixelFormat:(CGLPixelFormatObj)pixelFormat
+ forLayerTime:(CFTimeInterval)timeInterval
+ displayTime:(const CVTimeStamp*)timeStamp {
+ // Add an instantaneous blip to the PendingSwapAck state to indicate
+ // that CoreAnimation asked if a frame is ready. A blip up to to 3 (usually
+ // from 2, indicating that a swap ack is pending) indicates that we requested
+ // a draw. A blip up to 1 (usually from 0, indicating there is no pending swap
+ // ack) indicates that we did not request a draw. This would be more natural
+ // to do with a tracing pseudo-thread
+ // http://crbug.com/366300
+ TRACE_COUNTER_ID1("browser", "PendingSwapAck", self, needs_display_ ? 3 : 1);
+ TRACE_COUNTER_ID1("browser", "PendingSwapAck", self,
+ has_pending_frame_ ? 2 : 0);
+
+ // If we return NO 30 times in a row, switch to being synchronous to avoid
+ // burning CPU cycles on this callback.
+ if (needs_display_) {
+ did_not_draw_counter_ = 0;
+ } else {
+ did_not_draw_counter_ += 1;
+ if (did_not_draw_counter_ > 30)
+ [self setAsynchronous:NO];
+ }
- DCHECK(glContext == context_->cgl_context());
- context_ = nil;
+ return needs_display_;
}
- (void)drawInCGLContext:(CGLContextObj)glContext
@@ -87,47 +201,34 @@
displayTime:(const CVTimeStamp*)timeStamp {
TRACE_EVENT0("browser", "CompositingIOSurfaceLayer::drawInCGLContext");
- if (!context_.get() || !renderWidgetHostView_ ||
- !renderWidgetHostView_->compositing_iosurface_) {
+ if (!iosurface_->HasIOSurface() || context_->cgl_context() != glContext) {
glClearColor(1, 1, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);
return;
}
- DCHECK(glContext == context_->cgl_context());
-
- // Cache a copy of renderWidgetHostView_ because it may be reset if
- // a software frame is received in GetBackingStore.
- content::RenderWidgetHostViewMac* cached_view = renderWidgetHostView_;
-
- // If a resize is in progress then GetBackingStore request a frame of the
- // current window size and block until a frame of the right size comes in.
- // This makes the window content not lag behind the resize (at the cost of
- // blocking on the browser's main thread).
- if (cached_view->render_widget_host_) {
- cached_view->about_to_validate_and_paint_ = true;
- (void)cached_view->render_widget_host_->GetBackingStore(true);
- cached_view->about_to_validate_and_paint_ = false;
- }
-
- // If a transition to software mode has occurred, this layer should be
- // removed from the heirarchy now, so don't draw anything.
- if (!renderWidgetHostView_)
- return;
-
- gfx::Rect window_rect([self frame]);
+ // The correct viewport to cover the layer will be set up by the caller.
+ // Transform this into a window size for DrawIOSurface, where it will be
+ // transformed back into this viewport.
+ GLint viewport[4];
+ glGetIntegerv(GL_VIEWPORT, viewport);
+ gfx::Rect window_rect(viewport[0], viewport[1], viewport[2], viewport[3]);
float window_scale_factor = 1.f;
if ([self respondsToSelector:(@selector(contentsScale))])
window_scale_factor = [self contentsScale];
+ window_rect = ToNearestRect(
+ gfx::ScaleRect(window_rect, 1.f/window_scale_factor));
- CGLSetCurrentContext(glContext);
- if (!renderWidgetHostView_->compositing_iosurface_->DrawIOSurface(
- context_,
- window_rect,
- window_scale_factor,
- false)) {
- renderWidgetHostView_->GotAcceleratedCompositingError();
- }
+ bool draw_succeeded = iosurface_->DrawIOSurface(
+ context_, window_rect, window_scale_factor);
+
+ [self ackPendingFrame:draw_succeeded];
+ needs_display_ = NO;
+
+ [super drawInCGLContext:glContext
+ pixelFormat:pixelFormat
+ forLayerTime:timeInterval
+ displayTime:timeStamp];
}
@end
diff --git a/chromium/content/browser/renderer_host/compositing_iosurface_mac.h b/chromium/content/browser/renderer_host/compositing_iosurface_mac.h
index 315ddc59fcc..08c3c74c286 100644
--- a/chromium/content/browser/renderer_host/compositing_iosurface_mac.h
+++ b/chromium/content/browser/renderer_host/compositing_iosurface_mac.h
@@ -6,26 +6,26 @@
#define CONTENT_BROWSER_RENDERER_HOST_COMPOSITING_IOSURFACE_MAC_H_
#include <deque>
+#include <list>
#include <vector>
#import <Cocoa/Cocoa.h>
-#import <QuartzCore/CVDisplayLink.h>
+#include <IOSurface/IOSurfaceAPI.h>
#include <QuartzCore/QuartzCore.h>
#include "base/callback.h"
+#include "base/lazy_instance.h"
#include "base/mac/scoped_cftyperef.h"
+#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
-#include "base/synchronization/lock.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "media/base/video_frame.h"
-#include "ui/events/latency_info.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/rect_conversions.h"
#include "ui/gfx/size.h"
-class IOSurfaceSupport;
class SkBitmap;
namespace gfx {
@@ -43,19 +43,18 @@ class RenderWidgetHostViewMac;
// This class manages an OpenGL context and IOSurface for the accelerated
// compositing code path. The GL context is attached to
// RenderWidgetHostViewCocoa for blitting the IOSurface.
-class CompositingIOSurfaceMac {
+class CompositingIOSurfaceMac
+ : public base::RefCounted<CompositingIOSurfaceMac> {
public:
- // Returns NULL if IOSurface support is missing or GL APIs fail.
- static CompositingIOSurfaceMac* Create();
- ~CompositingIOSurfaceMac();
+ // Returns NULL if IOSurface or GL API calls fail.
+ static scoped_refptr<CompositingIOSurfaceMac> Create();
// Set IOSurface that will be drawn on the next NSView drawRect.
bool SetIOSurfaceWithContextCurrent(
scoped_refptr<CompositingIOSurfaceContext> current_context,
- uint64 io_surface_handle,
+ IOSurfaceID io_surface_handle,
const gfx::Size& size,
- float scale_factor,
- const ui::LatencyInfo& latency_info);
+ float scale_factor) WARN_UNUSED_RESULT;
// Get the CGL renderer ID currently associated with this context.
int GetRendererID();
@@ -67,8 +66,7 @@ class CompositingIOSurfaceMac {
bool DrawIOSurface(
scoped_refptr<CompositingIOSurfaceContext> drawing_context,
const gfx::Rect& window_rect,
- float window_scale_factor,
- bool flush_drawable);
+ float window_scale_factor) WARN_UNUSED_RESULT;
// Copy the data of the "live" OpenGL texture referring to this IOSurfaceRef
// into |out|. The copied region is specified with |src_pixel_subrect| and
@@ -102,23 +100,20 @@ class CompositingIOSurfaceMac {
const gfx::Size& dip_io_surface_size() const { return dip_io_surface_size_; }
float scale_factor() const { return scale_factor_; }
- // Get vsync scheduling parameters.
- // |interval_numerator/interval_denominator| equates to fractional number of
- // seconds between vsyncs.
- void GetVSyncParameters(base::TimeTicks* timebase,
- uint32* interval_numerator,
- uint32* interval_denominator);
-
// Returns true if asynchronous readback is supported on this system.
bool IsAsynchronousReadbackSupported();
+ // Scan the list of started asynchronous copies and test if each one has
+ // completed. If |block_until_finished| is true, then block until all
+ // pending copies are finished.
+ void CheckIfAllCopiesAreFinished(bool block_until_finished);
+
+ // Returns true if the offscreen context used by this surface has been
+ // poisoned.
+ bool HasBeenPoisoned() const;
+
private:
- friend CVReturn DisplayLinkCallback(CVDisplayLinkRef,
- const CVTimeStamp*,
- const CVTimeStamp*,
- CVOptionFlags,
- CVOptionFlags*,
- void*);
+ friend class base::RefCounted<CompositingIOSurfaceMac>;
// Vertex structure for use in glDraw calls.
struct SurfaceVertex {
@@ -208,10 +203,8 @@ class CompositingIOSurfaceMac {
};
CompositingIOSurfaceMac(
- IOSurfaceSupport* io_surface_support,
const scoped_refptr<CompositingIOSurfaceContext>& context);
-
- void SetupCVDisplayLink();
+ ~CompositingIOSurfaceMac();
// If this IOSurface has moved to a different window, use that window's
// GL context (if multiple visible windows are using the same GL context
@@ -219,30 +212,17 @@ class CompositingIOSurfaceMac {
void SwitchToContextOnNewWindow(NSView* view,
int window_number);
- bool IsVendorIntel();
-
// Returns true if IOSurface is ready to render. False otherwise.
bool MapIOSurfaceToTextureWithContextCurrent(
const scoped_refptr<CompositingIOSurfaceContext>& current_context,
- uint64 io_surface_handle);
+ const gfx::Size pixel_size,
+ float scale_factor,
+ IOSurfaceID io_surface_handle) WARN_UNUSED_RESULT;
void UnrefIOSurfaceWithContextCurrent();
void DrawQuad(const SurfaceQuad& quad);
- // Called on display-link thread.
- void DisplayLinkTick(CVDisplayLinkRef display_link,
- const CVTimeStamp* time);
-
- void CalculateVsyncParametersLockHeld(const CVTimeStamp* time);
-
- // Prevent from spinning on CGLFlushDrawable when it fails to throttle to
- // VSync frequency.
- void RateLimitDraws();
-
- void StartOrContinueDisplayLink();
- void StopDisplayLink();
-
// Copy current frame to |target| video frame. This method must be called
// within a CGL context. Returns a callback that should be called outside
// of the CGL context.
@@ -278,10 +258,6 @@ class CompositingIOSurfaceMac {
const SkBitmap* bitmap_output,
const scoped_refptr<media::VideoFrame>& video_frame_output);
- // Scan the list of started asynchronous copies and test if each one has
- // completed. If |block_until_finished| is true, then block until all
- // pending copies are finished.
- void CheckIfAllCopiesAreFinished(bool block_until_finished);
void CheckIfAllCopiesAreFinishedWithinContext(
bool block_until_finished,
std::vector<base::Closure>* done_callbacks);
@@ -295,17 +271,14 @@ class CompositingIOSurfaceMac {
gfx::Rect IntersectWithIOSurface(const gfx::Rect& rect) const;
- // Cached pointer to IOSurfaceSupport Singleton.
- IOSurfaceSupport* io_surface_support_;
-
// Offscreen context used for all operations other than drawing to the
// screen. This is in the same share group as the contexts used for
// drawing, and is the same for all IOSurfaces in all windows.
scoped_refptr<CompositingIOSurfaceContext> offscreen_context_;
// IOSurface data.
- uint64 io_surface_handle_;
- base::ScopedCFTypeRef<CFTypeRef> io_surface_;
+ IOSurfaceID io_surface_handle_;
+ base::ScopedCFTypeRef<IOSurfaceRef> io_surface_;
// The width and height of the io surface.
gfx::Size pixel_io_surface_size_; // In pixels.
@@ -329,24 +302,28 @@ class CompositingIOSurfaceMac {
// Timer for finishing a copy operation.
base::Timer finish_copy_timer_;
- // CVDisplayLink for querying Vsync timing info and throttling swaps.
- CVDisplayLinkRef display_link_;
-
- // Timer for stopping display link after a timeout with no swaps.
- base::DelayTimer<CompositingIOSurfaceMac> display_link_stop_timer_;
-
- // Lock for sharing data between UI thread and display-link thread.
- base::Lock lock_;
-
- // Vsync timing data.
- base::TimeTicks vsync_timebase_;
- uint32 vsync_interval_numerator_;
- uint32 vsync_interval_denominator_;
-
// Error saved by GetAndSaveGLError
GLint gl_error_;
- ui::LatencyInfo latency_info_;
+ // Aggressive IOSurface eviction logic. When using CoreAnimation, IOSurfaces
+ // are used only transiently to transfer from the GPU process to the browser
+ // process. Once the IOSurface has been drawn to its CALayer, the CALayer
+ // will not need updating again until its view is hidden and re-shown.
+ // Aggressively evict surfaces when more than 8 (the number allowed by the
+ // memory manager for fast tab switching) are allocated.
+ enum {
+ kMaximumUnevictedSurfaces = 8,
+ };
+ typedef std::list<CompositingIOSurfaceMac*> EvictionQueue;
+ void EvictionMarkUpdated();
+ void EvictionMarkEvicted();
+ EvictionQueue::iterator eviction_queue_iterator_;
+ bool eviction_has_been_drawn_since_updated_;
+
+ static void EvictionScheduleDoEvict();
+ static void EvictionDoEvict();
+ static base::LazyInstance<EvictionQueue> eviction_queue_;
+ static bool eviction_scheduled_;
};
} // namespace content
diff --git a/chromium/content/browser/renderer_host/compositing_iosurface_mac.mm b/chromium/content/browser/renderer_host/compositing_iosurface_mac.mm
index e948863ed40..3fd28545d2f 100644
--- a/chromium/content/browser/renderer_host/compositing_iosurface_mac.mm
+++ b/chromium/content/browser/renderer_host/compositing_iosurface_mac.mm
@@ -4,32 +4,31 @@
#include "content/browser/renderer_host/compositing_iosurface_mac.h"
+#include <OpenGL/CGLIOSurface.h>
#include <OpenGL/CGLRenderers.h>
#include <OpenGL/OpenGL.h>
#include "base/bind.h"
#include "base/bind_helpers.h"
-#include "base/command_line.h"
#include "base/debug/trace_event.h"
#include "base/logging.h"
#include "base/mac/mac_util.h"
#include "base/message_loop/message_loop.h"
#include "base/threading/platform_thread.h"
+#include "content/browser/gpu/gpu_data_manager_impl.h"
#include "content/browser/renderer_host/compositing_iosurface_context_mac.h"
#include "content/browser/renderer_host/compositing_iosurface_shader_programs_mac.h"
#include "content/browser/renderer_host/compositing_iosurface_transformer_mac.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_view_mac.h"
#include "content/common/content_constants_internal.h"
-#include "content/port/browser/render_widget_host_view_frame_subscriber.h"
-#include "gpu/command_buffer/service/gpu_switches.h"
+#include "gpu/config/gpu_driver_bug_workaround_type.h"
#include "media/base/video_util.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
#include "ui/gfx/size_conversions.h"
#include "ui/gl/gl_context.h"
-#include "ui/gl/io_surface_support_mac.h"
#ifdef NDEBUG
#define CHECK_GL_ERROR()
@@ -37,11 +36,11 @@
#else
#define CHECK_GL_ERROR() do { \
GLenum gl_error = glGetError(); \
- LOG_IF(ERROR, gl_error != GL_NO_ERROR) << "GL Error :" << gl_error; \
+ LOG_IF(ERROR, gl_error != GL_NO_ERROR) << "GL Error: " << gl_error; \
} while (0)
#define CHECK_AND_SAVE_GL_ERROR() do { \
GLenum gl_error = GetAndSaveGLError(); \
- LOG_IF(ERROR, gl_error != GL_NO_ERROR) << "GL Error :" << gl_error; \
+ LOG_IF(ERROR, gl_error != GL_NO_ERROR) << "GL Error: " << gl_error; \
} while (0)
#endif
@@ -157,18 +156,6 @@ bool MapBufferToVideoFrame(
} // namespace
-CVReturn DisplayLinkCallback(CVDisplayLinkRef display_link,
- const CVTimeStamp* now,
- const CVTimeStamp* output_time,
- CVOptionFlags flags_in,
- CVOptionFlags* flags_out,
- void* context) {
- CompositingIOSurfaceMac* surface =
- static_cast<CompositingIOSurfaceMac*>(context);
- surface->DisplayLinkTick(display_link, output_time);
- return kCVReturnSuccess;
-}
-
CompositingIOSurfaceMac::CopyContext::CopyContext(
const scoped_refptr<CompositingIOSurfaceContext>& context)
: transformer(new CompositingIOSurfaceTransformer(
@@ -230,13 +217,7 @@ void CompositingIOSurfaceMac::CopyContext::PrepareForAsynchronousReadback() {
// static
-CompositingIOSurfaceMac* CompositingIOSurfaceMac::Create() {
- IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize();
- if (!io_surface_support) {
- LOG(ERROR) << "No IOSurface support";
- return NULL;
- }
-
+scoped_refptr<CompositingIOSurfaceMac> CompositingIOSurfaceMac::Create() {
scoped_refptr<CompositingIOSurfaceContext> offscreen_context =
CompositingIOSurfaceContext::Get(
CompositingIOSurfaceContext::kOffscreenContextWindowNumber);
@@ -245,15 +226,12 @@ CompositingIOSurfaceMac* CompositingIOSurfaceMac::Create() {
return NULL;
}
- return new CompositingIOSurfaceMac(io_surface_support,
- offscreen_context);
+ return new CompositingIOSurfaceMac(offscreen_context);
}
CompositingIOSurfaceMac::CompositingIOSurfaceMac(
- IOSurfaceSupport* io_surface_support,
const scoped_refptr<CompositingIOSurfaceContext>& offscreen_context)
- : io_surface_support_(io_surface_support),
- offscreen_context_(offscreen_context),
+ : offscreen_context_(offscreen_context),
io_surface_handle_(0),
scale_factor_(1.f),
texture_(0),
@@ -264,83 +242,32 @@ CompositingIOSurfaceMac::CompositingIOSurfaceMac(
base::Unretained(this),
false),
true),
- display_link_(0),
- display_link_stop_timer_(FROM_HERE, base::TimeDelta::FromSeconds(1),
- this, &CompositingIOSurfaceMac::StopDisplayLink),
- vsync_interval_numerator_(0),
- vsync_interval_denominator_(0),
- gl_error_(GL_NO_ERROR) {
+ gl_error_(GL_NO_ERROR),
+ eviction_queue_iterator_(eviction_queue_.Get().end()),
+ eviction_has_been_drawn_since_updated_(false) {
CHECK(offscreen_context_);
}
-void CompositingIOSurfaceMac::SetupCVDisplayLink() {
- if (display_link_) {
- LOG(ERROR) << "DisplayLink already setup";
- return;
- }
-
- CVDisplayLinkRef display_link;
- CVReturn ret = CVDisplayLinkCreateWithActiveCGDisplays(&display_link);
- if (ret != kCVReturnSuccess) {
- LOG(WARNING) << "CVDisplayLinkCreateWithActiveCGDisplays failed: " << ret;
- return;
- }
-
- display_link_ = display_link;
-
- ret = CVDisplayLinkSetOutputCallback(display_link_,
- &DisplayLinkCallback, this);
- DCHECK(ret == kCVReturnSuccess)
- << "CVDisplayLinkSetOutputCallback failed: " << ret;
-
- StartOrContinueDisplayLink();
-
- CVTimeStamp cv_time;
- ret = CVDisplayLinkGetCurrentTime(display_link_, &cv_time);
- DCHECK(ret == kCVReturnSuccess)
- << "CVDisplayLinkGetCurrentTime failed: " << ret;
-
- {
- base::AutoLock lock(lock_);
- CalculateVsyncParametersLockHeld(&cv_time);
- }
-
- // Stop display link for now, it will be started when needed during Draw.
- StopDisplayLink();
-}
-
-void CompositingIOSurfaceMac::GetVSyncParameters(base::TimeTicks* timebase,
- uint32* interval_numerator,
- uint32* interval_denominator) {
- base::AutoLock lock(lock_);
- *timebase = vsync_timebase_;
- *interval_numerator = vsync_interval_numerator_;
- *interval_denominator = vsync_interval_denominator_;
-}
-
CompositingIOSurfaceMac::~CompositingIOSurfaceMac() {
FailAllCopies();
- CVDisplayLinkRelease(display_link_);
- CGLSetCurrentContext(offscreen_context_->cgl_context());
- DestroyAllCopyContextsWithinContext();
- UnrefIOSurfaceWithContextCurrent();
- CGLSetCurrentContext(0);
+ {
+ gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
+ offscreen_context_->cgl_context());
+ DestroyAllCopyContextsWithinContext();
+ UnrefIOSurfaceWithContextCurrent();
+ }
offscreen_context_ = NULL;
+ DCHECK(eviction_queue_iterator_ == eviction_queue_.Get().end());
}
bool CompositingIOSurfaceMac::SetIOSurfaceWithContextCurrent(
scoped_refptr<CompositingIOSurfaceContext> current_context,
- uint64 io_surface_handle,
+ IOSurfaceID io_surface_handle,
const gfx::Size& size,
- float scale_factor,
- const ui::LatencyInfo& latency_info) {
- pixel_io_surface_size_ = size;
- scale_factor_ = scale_factor;
- dip_io_surface_size_ = gfx::ToFlooredSize(
- gfx::ScaleSize(pixel_io_surface_size_, 1.0 / scale_factor_));
+ float scale_factor) {
bool result = MapIOSurfaceToTextureWithContextCurrent(
- current_context, io_surface_handle);
- latency_info_.MergeWith(latency_info);
+ current_context, size, scale_factor, io_surface_handle);
+ EvictionMarkUpdated();
return result;
}
@@ -356,13 +283,9 @@ int CompositingIOSurfaceMac::GetRendererID() {
bool CompositingIOSurfaceMac::DrawIOSurface(
scoped_refptr<CompositingIOSurfaceContext> drawing_context,
const gfx::Rect& window_rect,
- float window_scale_factor,
- bool flush_drawable) {
+ float window_scale_factor) {
DCHECK_EQ(CGLGetCurrentContext(), drawing_context->cgl_context());
- if (display_link_ == NULL)
- SetupCVDisplayLink();
-
bool has_io_surface = HasIOSurface();
TRACE_EVENT1("browser", "CompositingIOSurfaceMac::DrawIOSurface",
"has_io_surface", has_io_surface);
@@ -431,62 +354,29 @@ bool CompositingIOSurfaceMac::DrawIOSurface(
glClear(GL_COLOR_BUFFER_BIT);
}
- static bool initialized_workaround = false;
- static bool force_on_workaround = false;
- static bool force_off_workaround = false;
- if (!initialized_workaround) {
- force_on_workaround = CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kForceGLFinishWorkaround);
- force_off_workaround = CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kDisableGpuDriverBugWorkarounds);
-
- initialized_workaround = true;
- }
-
- const bool workaround_needed =
- drawing_context->IsVendorIntel() &&
- (!base::mac::IsOSMountainLionOrLater() || base::mac::IsOSMavericks());
- const bool use_glfinish_workaround =
- (workaround_needed || force_on_workaround) && !force_off_workaround;
-
- if (use_glfinish_workaround) {
+ bool workaround_needed =
+ GpuDataManagerImpl::GetInstance()->IsDriverBugWorkaroundActive(
+ gpu::FORCE_GL_FINISH_AFTER_COMPOSITING);
+ if (workaround_needed) {
TRACE_EVENT0("gpu", "glFinish");
- // http://crbug.com/123409 : work around bugs in graphics driver on
- // MacBook Air with Intel HD graphics, and possibly on other models,
- // by forcing the graphics pipeline to be completely drained at this
- // point. This workaround is not necessary on Mountain Lion.
- // http://crbug.com/318877 : work around a bug where the window does
- // not finish rendering its contents before displaying them on Mavericks
- // on Retina MacBook Pro when using the Intel HD graphics GPU.
glFinish();
}
- bool result = true;
- if (flush_drawable) {
- CGLError cgl_error = CGLFlushDrawable(drawing_context->cgl_context());
- if (cgl_error != kCGLNoError) {
- LOG(ERROR) << "CGLFlushDrawable error in DrawIOSurface: " << cgl_error;
- result = false;
- }
- }
-
// Check if any of the drawing calls result in an error.
GetAndSaveGLError();
+ bool result = true;
if (gl_error_ != GL_NO_ERROR) {
LOG(ERROR) << "GL error in DrawIOSurface: " << gl_error_;
result = false;
+ // If there was an error, clear the screen to a light grey to avoid
+ // rendering artifacts. If we're in a really bad way, this too may
+ // generate an error. Clear the GL error afterwards just in case.
+ glClearColor(0.8, 0.8, 0.8, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glGetError();
}
- latency_info_.AddLatencyNumber(
- ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0);
- RenderWidgetHostImpl::CompositorFrameDrawn(latency_info_);
- latency_info_.Clear();
-
- // Try to finish previous copy requests after flush to get better pipelining.
- CheckIfAllCopiesAreFinished(false);
-
- StartOrContinueDisplayLink();
-
+ eviction_has_been_drawn_since_updated_ = true;
return result;
}
@@ -509,12 +399,15 @@ void CompositingIOSurfaceMac::CopyTo(
DCHECK_EQ(output->rowBytesAsPixels(), dst_pixel_size.width())
<< "Stride is required to be equal to width for GPU readback.";
- CGLSetCurrentContext(offscreen_context_->cgl_context());
- const base::Closure copy_done_callback = CopyToSelectedOutputWithinContext(
- src_pixel_subrect, gfx::Rect(dst_pixel_size), false,
- output.get(), NULL,
- base::Bind(&ReverseArgumentOrder, callback, base::Passed(&output)));
- CGLSetCurrentContext(0);
+ base::Closure copy_done_callback;
+ {
+ gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
+ offscreen_context_->cgl_context());
+ copy_done_callback = CopyToSelectedOutputWithinContext(
+ src_pixel_subrect, gfx::Rect(dst_pixel_size), false,
+ output.get(), NULL,
+ base::Bind(&ReverseArgumentOrder, callback, base::Passed(&output)));
+ }
if (!copy_done_callback.is_null())
copy_done_callback.Run();
}
@@ -523,10 +416,13 @@ void CompositingIOSurfaceMac::CopyToVideoFrame(
const gfx::Rect& src_pixel_subrect,
const scoped_refptr<media::VideoFrame>& target,
const base::Callback<void(bool)>& callback) {
- CGLSetCurrentContext(offscreen_context_->cgl_context());
- const base::Closure copy_done_callback = CopyToVideoFrameWithinContext(
- src_pixel_subrect, false, target, callback);
- CGLSetCurrentContext(0);
+ base::Closure copy_done_callback;
+ {
+ gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
+ offscreen_context_->cgl_context());
+ copy_done_callback = CopyToVideoFrameWithinContext(
+ src_pixel_subrect, false, target, callback);
+ }
if (!copy_done_callback.is_null())
copy_done_callback.Run();
}
@@ -555,15 +451,26 @@ base::Closure CompositingIOSurfaceMac::CopyToVideoFrameWithinContext(
bool CompositingIOSurfaceMac::MapIOSurfaceToTextureWithContextCurrent(
const scoped_refptr<CompositingIOSurfaceContext>& current_context,
- uint64 io_surface_handle) {
- if (io_surface_.get() && io_surface_handle == io_surface_handle_)
- return true;
-
+ const gfx::Size pixel_size,
+ float scale_factor,
+ IOSurfaceID io_surface_handle) {
TRACE_EVENT0("browser", "CompositingIOSurfaceMac::MapIOSurfaceToTexture");
- UnrefIOSurfaceWithContextCurrent();
- io_surface_.reset(io_surface_support_->IOSurfaceLookup(
- static_cast<uint32>(io_surface_handle)));
+ if (!io_surface_ || io_surface_handle != io_surface_handle_)
+ UnrefIOSurfaceWithContextCurrent();
+
+ pixel_io_surface_size_ = pixel_size;
+ scale_factor_ = scale_factor;
+ dip_io_surface_size_ = gfx::ToFlooredSize(
+ gfx::ScaleSize(pixel_io_surface_size_, 1.0 / scale_factor_));
+
+ // Early-out if the IOSurface has not changed. Note that because IOSurface
+ // sizes are rounded, the same IOSurface may have two different sizes
+ // associated with it.
+ if (io_surface_ && io_surface_handle == io_surface_handle_)
+ return true;
+
+ io_surface_.reset(IOSurfaceLookup(io_surface_handle));
// Can fail if IOSurface with that ID was already released by the gpu
// process.
if (!io_surface_) {
@@ -575,9 +482,8 @@ bool CompositingIOSurfaceMac::MapIOSurfaceToTextureWithContextCurrent(
// Actual IOSurface size is rounded up to reduce reallocations during window
// resize. Get the actual size to properly map the texture.
- gfx::Size rounded_size(
- io_surface_support_->IOSurfaceGetWidth(io_surface_),
- io_surface_support_->IOSurfaceGetHeight(io_surface_));
+ gfx::Size rounded_size(IOSurfaceGetWidth(io_surface_),
+ IOSurfaceGetHeight(io_surface_));
glGenTextures(1, &texture_);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_);
@@ -585,7 +491,7 @@ bool CompositingIOSurfaceMac::MapIOSurfaceToTextureWithContextCurrent(
glTexParameterf(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
CHECK_AND_SAVE_GL_ERROR();
GLuint plane = 0;
- CGLError cgl_error = io_surface_support_->CGLTexImageIOSurface2D(
+ CGLError cgl_error = CGLTexImageIOSurface2D(
current_context->cgl_context(),
GL_TEXTURE_RECTANGLE_ARB,
GL_RGBA,
@@ -611,12 +517,14 @@ bool CompositingIOSurfaceMac::MapIOSurfaceToTextureWithContextCurrent(
}
void CompositingIOSurfaceMac::UnrefIOSurface() {
- CGLSetCurrentContext(offscreen_context_->cgl_context());
+ gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
+ offscreen_context_->cgl_context());
UnrefIOSurfaceWithContextCurrent();
- CGLSetCurrentContext(0);
}
void CompositingIOSurfaceMac::DrawQuad(const SurfaceQuad& quad) {
+ TRACE_EVENT0("gpu", "CompositingIOSurfaceMac::DrawQuad");
+
glEnableClientState(GL_VERTEX_ARRAY); CHECK_AND_SAVE_GL_ERROR();
glEnableClientState(GL_TEXTURE_COORD_ARRAY); CHECK_AND_SAVE_GL_ERROR();
@@ -633,67 +541,33 @@ void CompositingIOSurfaceMac::UnrefIOSurfaceWithContextCurrent() {
glDeleteTextures(1, &texture_);
texture_ = 0;
}
-
+ pixel_io_surface_size_ = gfx::Size();
+ scale_factor_ = 1;
+ dip_io_surface_size_ = gfx::Size();
io_surface_.reset();
// Forget the ID, because even if it is still around when we want to use it
// again, OSX may have reused the same ID for a new tab and we don't want to
// blit random tab contents.
io_surface_handle_ = 0;
-}
-
-void CompositingIOSurfaceMac::DisplayLinkTick(CVDisplayLinkRef display_link,
- const CVTimeStamp* time) {
- TRACE_EVENT0("gpu", "CompositingIOSurfaceMac::DisplayLinkTick");
- base::AutoLock lock(lock_);
- CalculateVsyncParametersLockHeld(time);
-}
-void CompositingIOSurfaceMac::CalculateVsyncParametersLockHeld(
- const CVTimeStamp* time) {
- lock_.AssertAcquired();
- vsync_interval_numerator_ = static_cast<uint32>(time->videoRefreshPeriod);
- vsync_interval_denominator_ = time->videoTimeScale;
- // Verify that videoRefreshPeriod is 32 bits.
- DCHECK((time->videoRefreshPeriod & ~0xffffFFFFull) == 0ull);
-
- vsync_timebase_ =
- base::TimeTicks::FromInternalValue(time->hostTime / 1000);
-}
-
-void CompositingIOSurfaceMac::StartOrContinueDisplayLink() {
- if (display_link_ == NULL)
- return;
-
- if (!CVDisplayLinkIsRunning(display_link_)) {
- CVDisplayLinkStart(display_link_);
- }
- display_link_stop_timer_.Reset();
-}
-
-void CompositingIOSurfaceMac::StopDisplayLink() {
- if (display_link_ == NULL)
- return;
-
- if (CVDisplayLinkIsRunning(display_link_))
- CVDisplayLinkStop(display_link_);
+ EvictionMarkEvicted();
}
bool CompositingIOSurfaceMac::IsAsynchronousReadbackSupported() {
- const bool forced_synchronous = CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kForceSynchronousGLReadPixels);
- if (forced_synchronous)
- return false;
if (!HasAppleFenceExtension() && HasPixelBufferObjectExtension())
return false;
- // Using PBO crashes on Intel drivers but not on newer Mountain Lion
- // systems. See bug http://crbug.com/152225.
- if (offscreen_context_->IsVendorIntel() &&
- !base::mac::IsOSMountainLionOrLater())
+ if (GpuDataManagerImpl::GetInstance()->IsDriverBugWorkaroundActive(
+ gpu::DISABLE_ASYNC_READPIXELS)) {
return false;
+ }
return true;
}
+bool CompositingIOSurfaceMac::HasBeenPoisoned() const {
+ return offscreen_context_->HasBeenPoisoned();
+}
+
base::Closure CompositingIOSurfaceMac::CopyToSelectedOutputWithinContext(
const gfx::Rect& src_pixel_subrect,
const gfx::Rect& dst_pixel_rect,
@@ -851,11 +725,12 @@ void CompositingIOSurfaceMac::CheckIfAllCopiesAreFinished(
return;
std::vector<base::Closure> done_callbacks;
- CGLContextObj previous_context = CGLGetCurrentContext();
- CGLSetCurrentContext(offscreen_context_->cgl_context());
- CheckIfAllCopiesAreFinishedWithinContext(
- block_until_finished, &done_callbacks);
- CGLSetCurrentContext(previous_context);
+ {
+ gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
+ offscreen_context_->cgl_context());
+ CheckIfAllCopiesAreFinishedWithinContext(
+ block_until_finished, &done_callbacks);
+ }
for (size_t i = 0; i < done_callbacks.size(); ++i)
done_callbacks[i].Run();
}
@@ -1028,4 +903,68 @@ GLenum CompositingIOSurfaceMac::GetAndSaveGLError() {
return gl_error;
}
+void CompositingIOSurfaceMac::EvictionMarkUpdated() {
+ EvictionMarkEvicted();
+ eviction_queue_.Get().push_back(this);
+ eviction_queue_iterator_ = --eviction_queue_.Get().end();
+ eviction_has_been_drawn_since_updated_ = false;
+ EvictionScheduleDoEvict();
+}
+
+void CompositingIOSurfaceMac::EvictionMarkEvicted() {
+ if (eviction_queue_iterator_ == eviction_queue_.Get().end())
+ return;
+ eviction_queue_.Get().erase(eviction_queue_iterator_);
+ eviction_queue_iterator_ = eviction_queue_.Get().end();
+ eviction_has_been_drawn_since_updated_ = false;
+}
+
+// static
+void CompositingIOSurfaceMac::EvictionScheduleDoEvict() {
+ if (eviction_scheduled_)
+ return;
+ if (eviction_queue_.Get().size() <= kMaximumUnevictedSurfaces)
+ return;
+
+ eviction_scheduled_ = true;
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&CompositingIOSurfaceMac::EvictionDoEvict));
+}
+
+// static
+void CompositingIOSurfaceMac::EvictionDoEvict() {
+ eviction_scheduled_ = false;
+ // Walk the list of allocated surfaces from least recently used to most
+ // recently used.
+ for (EvictionQueue::iterator it = eviction_queue_.Get().begin();
+ it != eviction_queue_.Get().end();) {
+ CompositingIOSurfaceMac* surface = *it;
+ ++it;
+
+ // If the number of IOSurfaces allocated is less than the threshold,
+ // stop walking the list of surfaces.
+ if (eviction_queue_.Get().size() <= kMaximumUnevictedSurfaces)
+ break;
+
+ // Don't evict anything that has not yet been drawn.
+ if (!surface->eviction_has_been_drawn_since_updated_)
+ continue;
+
+ // Don't evict anything with pending copy requests.
+ if (!surface->copy_requests_.empty())
+ continue;
+
+ // Evict the surface.
+ surface->UnrefIOSurface();
+ }
+}
+
+// static
+base::LazyInstance<CompositingIOSurfaceMac::EvictionQueue>
+ CompositingIOSurfaceMac::eviction_queue_;
+
+// static
+bool CompositingIOSurfaceMac::eviction_scheduled_ = false;
+
} // namespace content
diff --git a/chromium/content/browser/renderer_host/compositing_iosurface_shader_programs_mac.cc b/chromium/content/browser/renderer_host/compositing_iosurface_shader_programs_mac.cc
index 87f0f341be2..753c180dbf5 100644
--- a/chromium/content/browser/renderer_host/compositing_iosurface_shader_programs_mac.cc
+++ b/chromium/content/browser/renderer_host/compositing_iosurface_shader_programs_mac.cc
@@ -358,8 +358,6 @@ void CompositingIOSurfaceShaderPrograms::Reset() {
for (size_t i = 0; i < arraysize(shader_programs_); ++i) {
if (shader_programs_[i] != 0u) {
glDeleteProgram(shader_programs_[i]);
- DCHECK(glGetError() == GL_NO_ERROR)
- << "when calling glDeleteProgram(shader_programs_[" << i << "])";
shader_programs_[i] = 0u;
}
}
diff --git a/chromium/content/browser/renderer_host/compositing_iosurface_transformer_mac_unittest.cc b/chromium/content/browser/renderer_host/compositing_iosurface_transformer_mac_unittest.cc
index 7d9452fd01e..07c59429184 100644
--- a/chromium/content/browser/renderer_host/compositing_iosurface_transformer_mac_unittest.cc
+++ b/chromium/content/browser/renderer_host/compositing_iosurface_transformer_mac_unittest.cc
@@ -194,7 +194,7 @@ SkBitmap ScaleBitmapWithSkia(const SkBitmap& src,
canvas.scale(static_cast<double>(result.width()) / cropped_src.width(),
static_cast<double>(result.height()) / cropped_src.height());
SkPaint paint;
- paint.setFilterBitmap(true); // Use bilinear filtering.
+ paint.setFilterLevel(SkPaint::kLow_FilterLevel); // Use bilinear filtering.
canvas.drawBitmap(cropped_src, 0, 0, &paint);
return result;
diff --git a/chromium/content/browser/renderer_host/compositor_impl_android.cc b/chromium/content/browser/renderer_host/compositor_impl_android.cc
index 78b23989bd0..47f037d17f4 100644
--- a/chromium/content/browser/renderer_host/compositor_impl_android.cc
+++ b/chromium/content/browser/renderer_host/compositor_impl_android.cc
@@ -6,25 +6,26 @@
#include <android/bitmap.h>
#include <android/native_window_jni.h>
-#include <map>
#include "base/android/jni_android.h"
#include "base/android/scoped_java_ref.h"
#include "base/bind.h"
#include "base/command_line.h"
+#include "base/containers/hash_tables.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread.h"
+#include "base/threading/thread_checker.h"
+#include "cc/base/switches.h"
#include "cc/input/input_handler.h"
#include "cc/layers/layer.h"
#include "cc/output/compositor_frame.h"
#include "cc/output/context_provider.h"
#include "cc/output/output_surface.h"
-#include "cc/resources/scoped_ui_resource.h"
-#include "cc/resources/ui_resource_bitmap.h"
#include "cc/trees/layer_tree_host.h"
+#include "content/browser/android/child_process_launcher_android.h"
#include "content/browser/gpu/browser_gpu_channel_host_factory.h"
#include "content/browser/gpu/gpu_surface_tracker.h"
#include "content/common/gpu/client/command_buffer_proxy_impl.h"
@@ -33,57 +34,39 @@
#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/host_shared_bitmap_manager.h"
#include "content/public/browser/android/compositor_client.h"
-#include "content/public/common/content_switches.h"
-#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "third_party/khronos/GLES2/gl2ext.h"
+#include "third_party/skia/include/core/SkMallocPixelRef.h"
#include "ui/base/android/window_android.h"
#include "ui/gfx/android/device_display_info.h"
-#include "ui/gfx/android/java_bitmap.h"
#include "ui/gfx/frame_time.h"
+#include "ui/gl/android/surface_texture.h"
+#include "ui/gl/android/surface_texture_tracker.h"
#include "webkit/common/gpu/context_provider_in_process.h"
#include "webkit/common/gpu/webgraphicscontext3d_in_process_command_buffer_impl.h"
-namespace gfx {
-class JavaBitmap;
-}
-
namespace {
-// Used for drawing directly to the screen. Bypasses resizing and swaps.
-class DirectOutputSurface : public cc::OutputSurface {
- public:
- DirectOutputSurface(
- const scoped_refptr<cc::ContextProvider>& context_provider)
- : cc::OutputSurface(context_provider) {
- capabilities_.adjust_deadline_for_parent = false;
- }
-
- virtual void Reshape(gfx::Size size, float scale_factor) OVERRIDE {
- surface_size_ = size;
- }
- virtual void SwapBuffers(cc::CompositorFrame*) OVERRIDE {
- context_provider_->Context3d()->shallowFlushCHROMIUM();
- }
-};
+const unsigned int kMaxSwapBuffers = 2U;
// Used to override capabilities_.adjust_deadline_for_parent to false
class OutputSurfaceWithoutParent : public cc::OutputSurface {
public:
- OutputSurfaceWithoutParent(
- const scoped_refptr<
- content::ContextProviderCommandBuffer>& context_provider)
+ OutputSurfaceWithoutParent(const scoped_refptr<
+ content::ContextProviderCommandBuffer>& context_provider)
: cc::OutputSurface(context_provider) {
capabilities_.adjust_deadline_for_parent = false;
}
virtual void SwapBuffers(cc::CompositorFrame* frame) OVERRIDE {
- content::WebGraphicsContext3DCommandBufferImpl* command_buffer_context =
- static_cast<content::WebGraphicsContext3DCommandBufferImpl*>(
- context_provider_->Context3d());
+ content::ContextProviderCommandBuffer* provider_command_buffer =
+ static_cast<content::ContextProviderCommandBuffer*>(
+ context_provider_.get());
content::CommandBufferProxyImpl* command_buffer_proxy =
- command_buffer_context->GetCommandBufferProxy();
+ provider_command_buffer->GetCommandBufferProxy();
DCHECK(command_buffer_proxy);
command_buffer_proxy->SetLatencyInfo(frame->metadata.latency_info);
@@ -91,18 +74,78 @@ class OutputSurfaceWithoutParent : public cc::OutputSurface {
}
};
+class SurfaceTextureTrackerImpl : public gfx::SurfaceTextureTracker {
+ public:
+ SurfaceTextureTrackerImpl() : next_surface_texture_id_(1) {
+ thread_checker_.DetachFromThread();
+ }
+
+ // Overridden from gfx::SurfaceTextureTracker:
+ virtual scoped_refptr<gfx::SurfaceTexture> AcquireSurfaceTexture(
+ int primary_id,
+ int secondary_id) OVERRIDE {
+ base::AutoLock lock(surface_textures_lock_);
+ SurfaceTextureMapKey key(primary_id, secondary_id);
+ SurfaceTextureMap::iterator it = surface_textures_.find(key);
+ if (it == surface_textures_.end())
+ return scoped_refptr<gfx::SurfaceTexture>();
+ scoped_refptr<gfx::SurfaceTexture> surface_texture = it->second;
+ surface_textures_.erase(it);
+ return surface_texture;
+ }
+
+ int AddSurfaceTexture(gfx::SurfaceTexture* surface_texture,
+ int child_process_id) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ int surface_texture_id = next_surface_texture_id_++;
+ if (next_surface_texture_id_ == INT_MAX)
+ next_surface_texture_id_ = 1;
+
+ base::AutoLock lock(surface_textures_lock_);
+ SurfaceTextureMapKey key(surface_texture_id, child_process_id);
+ DCHECK(surface_textures_.find(key) == surface_textures_.end());
+ surface_textures_[key] = surface_texture;
+ content::RegisterChildProcessSurfaceTexture(
+ surface_texture_id,
+ child_process_id,
+ surface_texture->j_surface_texture().obj());
+ return surface_texture_id;
+ }
+
+ void RemoveAllSurfaceTextures(int child_process_id) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ base::AutoLock lock(surface_textures_lock_);
+ SurfaceTextureMap::iterator it = surface_textures_.begin();
+ while (it != surface_textures_.end()) {
+ if (it->first.second == child_process_id) {
+ content::UnregisterChildProcessSurfaceTexture(it->first.first,
+ it->first.second);
+ surface_textures_.erase(it++);
+ } else {
+ ++it;
+ }
+ }
+ }
+
+ private:
+ typedef std::pair<int, int> SurfaceTextureMapKey;
+ typedef base::hash_map<SurfaceTextureMapKey,
+ scoped_refptr<gfx::SurfaceTexture> >
+ SurfaceTextureMap;
+ SurfaceTextureMap surface_textures_;
+ mutable base::Lock surface_textures_lock_;
+ int next_surface_texture_id_;
+ base::ThreadChecker thread_checker_;
+};
+base::LazyInstance<SurfaceTextureTrackerImpl> g_surface_texture_tracker =
+ LAZY_INSTANCE_INITIALIZER;
+
static bool g_initialized = false;
} // anonymous namespace
namespace content {
-typedef std::map<int, base::android::ScopedJavaGlobalRef<jobject> >
- SurfaceMap;
-static base::LazyInstance<SurfaceMap>
- g_surface_map = LAZY_INSTANCE_INITIALIZER;
-static base::LazyInstance<base::Lock> g_surface_map_lock;
-
// static
Compositor* Compositor::Create(CompositorClient* client,
gfx::NativeWindow root_window) {
@@ -112,6 +155,9 @@ Compositor* Compositor::Create(CompositorClient* client,
// static
void Compositor::Initialize() {
DCHECK(!CompositorImpl::IsInitialized());
+ // SurfaceTextureTracker instance must be set before we create a GPU thread
+ // that could be using it to initialize GLImage instances.
+ gfx::SurfaceTextureTracker::InitInstance(g_surface_texture_tracker.Pointer());
g_initialized = true;
}
@@ -121,28 +167,45 @@ bool CompositorImpl::IsInitialized() {
}
// static
-jobject CompositorImpl::GetSurface(int surface_id) {
- base::AutoLock lock(g_surface_map_lock.Get());
- SurfaceMap* surfaces = g_surface_map.Pointer();
- SurfaceMap::iterator it = surfaces->find(surface_id);
- jobject jsurface = it == surfaces->end() ? NULL : it->second.obj();
+int CompositorImpl::CreateSurfaceTexture(int child_process_id) {
+ // Note: this needs to be 0 as the surface texture implemenation will take
+ // ownership of the texture and call glDeleteTextures when the GPU service
+ // attaches the surface texture to a real texture id. glDeleteTextures
+ // silently ignores 0.
+ const int kDummyTextureId = 0;
+ scoped_refptr<gfx::SurfaceTexture> surface_texture =
+ gfx::SurfaceTexture::Create(kDummyTextureId);
+ return g_surface_texture_tracker.Pointer()->AddSurfaceTexture(
+ surface_texture.get(), child_process_id);
+}
- LOG_IF(WARNING, !jsurface) << "No surface for surface id " << surface_id;
- return jsurface;
+// static
+void CompositorImpl::DestroyAllSurfaceTextures(int child_process_id) {
+ g_surface_texture_tracker.Pointer()->RemoveAllSurfaceTextures(
+ child_process_id);
}
CompositorImpl::CompositorImpl(CompositorClient* client,
gfx::NativeWindow root_window)
: root_layer_(cc::Layer::Create()),
has_transparent_background_(false),
+ device_scale_factor_(1),
window_(NULL),
surface_id_(0),
client_(client),
- root_window_(root_window) {
+ root_window_(root_window),
+ did_post_swapbuffers_(false),
+ ignore_schedule_composite_(false),
+ needs_composite_(false),
+ needs_animate_(false),
+ will_composite_immediately_(false),
+ composite_on_vsync_trigger_(DO_NOT_COMPOSITE),
+ pending_swapbuffers_(0U),
+ weak_factory_(this) {
DCHECK(client);
DCHECK(root_window);
ImageTransportFactoryAndroid::AddObserver(this);
- root_window->AttachCompositor();
+ root_window->AttachCompositor(this);
}
CompositorImpl::~CompositorImpl() {
@@ -152,14 +215,140 @@ CompositorImpl::~CompositorImpl() {
SetSurface(NULL);
}
-void CompositorImpl::Composite() {
- if (host_)
- host_->Composite(gfx::FrameTime::Now());
+void CompositorImpl::PostComposite(CompositingTrigger trigger) {
+ DCHECK(needs_composite_);
+ DCHECK(trigger == COMPOSITE_IMMEDIATELY || trigger == COMPOSITE_EVENTUALLY);
+
+ if (will_composite_immediately_ ||
+ (trigger == COMPOSITE_EVENTUALLY && WillComposite())) {
+ // We will already composite soon enough.
+ DCHECK(WillComposite());
+ return;
+ }
+
+ if (DidCompositeThisFrame()) {
+ DCHECK(!WillCompositeThisFrame());
+ if (composite_on_vsync_trigger_ != COMPOSITE_IMMEDIATELY) {
+ composite_on_vsync_trigger_ = trigger;
+ root_window_->RequestVSyncUpdate();
+ }
+ DCHECK(WillComposite());
+ return;
+ }
+
+ base::TimeDelta delay;
+ if (trigger == COMPOSITE_IMMEDIATELY) {
+ will_composite_immediately_ = true;
+ composite_on_vsync_trigger_ = DO_NOT_COMPOSITE;
+ } else {
+ DCHECK(!WillComposite());
+ const base::TimeDelta estimated_composite_time = vsync_period_ / 4;
+ const base::TimeTicks now = base::TimeTicks::Now();
+
+ if (!last_vsync_.is_null() && (now - last_vsync_) < vsync_period_) {
+ base::TimeTicks next_composite =
+ last_vsync_ + vsync_period_ - estimated_composite_time;
+ if (next_composite < now) {
+ // It's too late, we will reschedule composite as needed on the next
+ // vsync.
+ composite_on_vsync_trigger_ = COMPOSITE_EVENTUALLY;
+ root_window_->RequestVSyncUpdate();
+ DCHECK(WillComposite());
+ return;
+ }
+
+ delay = next_composite - now;
+ }
+ }
+ TRACE_EVENT2("cc", "CompositorImpl::PostComposite",
+ "trigger", trigger,
+ "delay", delay.InMillisecondsF());
+
+ DCHECK(composite_on_vsync_trigger_ == DO_NOT_COMPOSITE);
+ if (current_composite_task_)
+ current_composite_task_->Cancel();
+
+ // Unretained because we cancel the task on shutdown.
+ current_composite_task_.reset(new base::CancelableClosure(
+ base::Bind(&CompositorImpl::Composite, base::Unretained(this), trigger)));
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE, current_composite_task_->callback(), delay);
+}
+
+void CompositorImpl::Composite(CompositingTrigger trigger) {
+ BrowserGpuChannelHostFactory* factory =
+ BrowserGpuChannelHostFactory::instance();
+ if (!factory->GetGpuChannel() || factory->GetGpuChannel()->IsLost()) {
+ CauseForGpuLaunch cause =
+ CAUSE_FOR_GPU_LAUNCH_WEBGRAPHICSCONTEXT3DCOMMANDBUFFERIMPL_INITIALIZE;
+ factory->EstablishGpuChannel(
+ cause,
+ base::Bind(&CompositorImpl::OnGpuChannelEstablished,
+ weak_factory_.GetWeakPtr()));
+ return;
+ }
+
+ DCHECK(host_);
+ DCHECK(trigger == COMPOSITE_IMMEDIATELY || trigger == COMPOSITE_EVENTUALLY);
+ DCHECK(needs_composite_);
+ DCHECK(!DidCompositeThisFrame());
+
+ if (trigger == COMPOSITE_IMMEDIATELY)
+ will_composite_immediately_ = false;
+
+ DCHECK_LE(pending_swapbuffers_, kMaxSwapBuffers);
+ if (pending_swapbuffers_ == kMaxSwapBuffers) {
+ TRACE_EVENT0("compositor", "CompositorImpl_SwapLimit");
+ return;
+ }
+
+ // Reset state before Layout+Composite since that might create more
+ // requests to Composite that we need to respect.
+ needs_composite_ = false;
+
+ // Only allow compositing once per vsync.
+ current_composite_task_->Cancel();
+ DCHECK(DidCompositeThisFrame() && !WillComposite());
+
+ // Ignore ScheduleComposite() from layer tree changes during layout and
+ // animation updates that will already be reflected in the current frame
+ // we are about to draw.
+ ignore_schedule_composite_ = true;
+ client_->Layout();
+
+ const base::TimeTicks frame_time = gfx::FrameTime::Now();
+ if (needs_animate_) {
+ needs_animate_ = false;
+ root_window_->Animate(frame_time);
+ }
+ ignore_schedule_composite_ = false;
+
+ did_post_swapbuffers_ = false;
+ host_->Composite(frame_time);
+ if (did_post_swapbuffers_)
+ pending_swapbuffers_++;
+
+ // Need to track vsync to avoid compositing more than once per frame.
+ root_window_->RequestVSyncUpdate();
+}
+
+void CompositorImpl::OnGpuChannelEstablished() {
+ ScheduleComposite();
+}
+
+UIResourceProvider& CompositorImpl::GetUIResourceProvider() {
+ return ui_resource_provider_;
}
void CompositorImpl::SetRootLayer(scoped_refptr<cc::Layer> root_layer) {
- root_layer_->RemoveAllChildren();
- root_layer_->AddChild(root_layer);
+ if (subroot_layer_) {
+ subroot_layer_->RemoveFromParent();
+ subroot_layer_ = NULL;
+ }
+ if (root_layer) {
+ subroot_layer_ = root_layer;
+ root_layer_->AddChild(root_layer);
+ }
}
void CompositorImpl::SetWindowSurface(ANativeWindow* window) {
@@ -189,57 +378,66 @@ void CompositorImpl::SetSurface(jobject surface) {
base::android::ScopedJavaLocalRef<jobject> j_surface(env, surface);
// First, cleanup any existing surface references.
- if (surface_id_) {
- DCHECK(g_surface_map.Get().find(surface_id_) !=
- g_surface_map.Get().end());
- base::AutoLock lock(g_surface_map_lock.Get());
- g_surface_map.Get().erase(surface_id_);
- }
+ if (surface_id_)
+ content::UnregisterViewSurface(surface_id_);
SetWindowSurface(NULL);
// Now, set the new surface if we have one.
ANativeWindow* window = NULL;
- if (surface)
+ if (surface) {
+ // Note: This ensures that any local references used by
+ // ANativeWindow_fromSurface are released immediately. This is needed as a
+ // workaround for https://code.google.com/p/android/issues/detail?id=68174
+ base::android::ScopedJavaLocalFrame scoped_local_reference_frame(env);
window = ANativeWindow_fromSurface(env, surface);
+ }
if (window) {
SetWindowSurface(window);
ANativeWindow_release(window);
- {
- base::AutoLock lock(g_surface_map_lock.Get());
- g_surface_map.Get().insert(std::make_pair(surface_id_, j_surface));
- }
+ content::RegisterViewSurface(surface_id_, j_surface.obj());
}
}
void CompositorImpl::SetVisible(bool visible) {
if (!visible) {
- ui_resource_map_.clear();
+ if (WillComposite())
+ CancelComposite();
+ ui_resource_provider_.SetLayerTreeHost(NULL);
host_.reset();
- client_->UIResourcesAreInvalid();
} else if (!host_) {
+ DCHECK(!WillComposite());
+ needs_composite_ = false;
+ needs_animate_ = false;
+ pending_swapbuffers_ = 0;
cc::LayerTreeSettings settings;
settings.refresh_rate = 60.0;
settings.impl_side_painting = false;
settings.allow_antialiasing = false;
settings.calculate_top_controls_position = false;
settings.top_controls_height = 0.f;
- settings.use_memory_management = false;
settings.highp_threshold_min = 2048;
- host_ = cc::LayerTreeHost::CreateSingleThreaded(this, this, NULL, settings);
+ CommandLine* command_line = CommandLine::ForCurrentProcess();
+ settings.initial_debug_state.SetRecordRenderingStats(
+ command_line->HasSwitch(cc::switches::kEnableGpuBenchmarking));
+ settings.initial_debug_state.show_fps_counter =
+ command_line->HasSwitch(cc::switches::kUIShowFPSCounter);
+
+ host_ = cc::LayerTreeHost::CreateSingleThreaded(
+ this, this, HostSharedBitmapManager::current(), settings);
host_->SetRootLayer(root_layer_);
host_->SetVisible(true);
host_->SetLayerTreeHostClientReady();
host_->SetViewportSize(size_);
host_->set_has_transparent_background(has_transparent_background_);
- // Need to recreate the UI resources because a new LayerTreeHost has been
- // created.
- client_->DidLoseUIResources();
+ host_->SetDeviceScaleFactor(device_scale_factor_);
+ ui_resource_provider_.SetLayerTreeHost(host_.get());
}
}
void CompositorImpl::setDeviceScaleFactor(float factor) {
+ device_scale_factor_ = factor;
if (host_)
host_->SetDeviceScaleFactor(factor);
}
@@ -254,114 +452,27 @@ void CompositorImpl::SetWindowBounds(const gfx::Size& size) {
root_layer_->SetBounds(size);
}
-bool CompositorImpl::CompositeAndReadback(void *pixels, const gfx::Rect& rect) {
+void CompositorImpl::SetHasTransparentBackground(bool flag) {
+ has_transparent_background_ = flag;
if (host_)
- return host_->CompositeAndReadback(pixels, rect);
- else
- return false;
+ host_->set_has_transparent_background(flag);
}
-cc::UIResourceId CompositorImpl::GenerateUIResource(
- const cc::UIResourceBitmap& bitmap) {
- if (!host_)
- return 0;
- scoped_ptr<cc::ScopedUIResource> ui_resource =
- cc::ScopedUIResource::Create(host_.get(), bitmap);
- cc::UIResourceId id = ui_resource->id();
- ui_resource_map_.set(id, ui_resource.Pass());
- return id;
-}
-
-void CompositorImpl::DeleteUIResource(cc::UIResourceId resource_id) {
- UIResourceMap::iterator it = ui_resource_map_.find(resource_id);
- if (it != ui_resource_map_.end())
- ui_resource_map_.erase(it);
-}
-
-blink::WebGLId CompositorImpl::GenerateTexture(gfx::JavaBitmap& bitmap) {
- unsigned int texture_id = BuildBasicTexture();
- blink::WebGraphicsContext3D* context =
- ImageTransportFactoryAndroid::GetInstance()->GetContext3D();
- if (texture_id == 0 || context->isContextLost() ||
- !context->makeContextCurrent())
- return 0;
- blink::WebGLId format = GetGLFormatForBitmap(bitmap);
- blink::WebGLId type = GetGLTypeForBitmap(bitmap);
-
- context->texImage2D(GL_TEXTURE_2D,
- 0,
- format,
- bitmap.size().width(),
- bitmap.size().height(),
- 0,
- format,
- type,
- bitmap.pixels());
- context->shallowFlushCHROMIUM();
- return texture_id;
-}
-
-blink::WebGLId CompositorImpl::GenerateCompressedTexture(gfx::Size& size,
- int data_size,
- void* data) {
- unsigned int texture_id = BuildBasicTexture();
- blink::WebGraphicsContext3D* context =
- ImageTransportFactoryAndroid::GetInstance()->GetContext3D();
- if (texture_id == 0 || context->isContextLost() ||
- !context->makeContextCurrent())
- return 0;
- context->compressedTexImage2D(GL_TEXTURE_2D,
- 0,
- GL_ETC1_RGB8_OES,
- size.width(),
- size.height(),
- 0,
- data_size,
- data);
- context->shallowFlushCHROMIUM();
- return texture_id;
-}
-
-void CompositorImpl::DeleteTexture(blink::WebGLId texture_id) {
- blink::WebGraphicsContext3D* context =
- ImageTransportFactoryAndroid::GetInstance()->GetContext3D();
- if (context->isContextLost() || !context->makeContextCurrent())
+void CompositorImpl::SetNeedsComposite() {
+ if (!host_.get())
return;
- context->deleteTexture(texture_id);
- context->shallowFlushCHROMIUM();
-}
+ DCHECK(!needs_composite_ || WillComposite());
-bool CompositorImpl::CopyTextureToBitmap(blink::WebGLId texture_id,
- gfx::JavaBitmap& bitmap) {
- return CopyTextureToBitmap(texture_id, gfx::Rect(bitmap.size()), bitmap);
-}
-
-bool CompositorImpl::CopyTextureToBitmap(blink::WebGLId texture_id,
- const gfx::Rect& sub_rect,
- gfx::JavaBitmap& bitmap) {
- // The sub_rect should match the bitmap size.
- DCHECK(bitmap.size() == sub_rect.size());
- if (bitmap.size() != sub_rect.size() || texture_id == 0) return false;
-
- GLHelper* helper = ImageTransportFactoryAndroid::GetInstance()->GetGLHelper();
- helper->ReadbackTextureSync(texture_id,
- sub_rect,
- static_cast<unsigned char*> (bitmap.pixels()));
- return true;
+ needs_composite_ = true;
+ PostComposite(COMPOSITE_IMMEDIATELY);
}
static scoped_ptr<WebGraphicsContext3DCommandBufferImpl>
CreateGpuProcessViewContext(
+ const scoped_refptr<GpuChannelHost>& gpu_channel_host,
const blink::WebGraphicsContext3D::Attributes attributes,
int surface_id) {
- BrowserGpuChannelHostFactory* factory =
- BrowserGpuChannelHostFactory::instance();
- CauseForGpuLaunch cause =
- CAUSE_FOR_GPU_LAUNCH_WEBGRAPHICSCONTEXT3DCOMMANDBUFFERIMPL_INITIALIZE;
- scoped_refptr<GpuChannelHost> gpu_channel_host(
- factory->EstablishGpuChannelSync(cause));
- if (!gpu_channel_host)
- return scoped_ptr<WebGraphicsContext3DCommandBufferImpl>();
+ DCHECK(gpu_channel_host);
GURL url("chrome://gpu/Compositor::createContext3D");
static const size_t kBytesPerPixel = 4;
@@ -377,13 +488,22 @@ CreateGpuProcessViewContext(
limits.max_transfer_buffer_size = std::min(
3 * full_screen_texture_size_in_bytes, kDefaultMaxTransferBufferSize);
limits.mapped_memory_reclaim_limit = 2 * 1024 * 1024;
+ bool lose_context_when_out_of_memory = true;
return make_scoped_ptr(
new WebGraphicsContext3DCommandBufferImpl(surface_id,
url,
gpu_channel_host.get(),
attributes,
- false,
- limits));
+ lose_context_when_out_of_memory,
+ limits,
+ NULL));
+}
+
+void CompositorImpl::Layout() {
+ // TODO: If we get this callback from the SingleThreadProxy, we need
+ // to stop calling it ourselves in CompositorImpl::Composite().
+ NOTREACHED();
+ client_->Layout();
}
scoped_ptr<cc::OutputSurface> CompositorImpl::CreateOutputSurface(
@@ -391,13 +511,20 @@ scoped_ptr<cc::OutputSurface> CompositorImpl::CreateOutputSurface(
blink::WebGraphicsContext3D::Attributes attrs;
attrs.shareResources = true;
attrs.noAutomaticFlushes = true;
+ pending_swapbuffers_ = 0;
DCHECK(window_);
DCHECK(surface_id_);
- scoped_refptr<ContextProviderCommandBuffer> context_provider =
- ContextProviderCommandBuffer::Create(
- CreateGpuProcessViewContext(attrs, surface_id_), "BrowserCompositor");
+ scoped_refptr<ContextProviderCommandBuffer> context_provider;
+ BrowserGpuChannelHostFactory* factory =
+ BrowserGpuChannelHostFactory::instance();
+ scoped_refptr<GpuChannelHost> gpu_channel_host = factory->GetGpuChannel();
+ if (gpu_channel_host && !gpu_channel_host->IsLost()) {
+ context_provider = ContextProviderCommandBuffer::Create(
+ CreateGpuProcessViewContext(gpu_channel_host, attrs, surface_id_),
+ "BrowserCompositor");
+ }
if (!context_provider.get()) {
LOG(ERROR) << "Failed to create 3D context for compositor.";
return scoped_ptr<cc::OutputSurface>();
@@ -409,89 +536,96 @@ scoped_ptr<cc::OutputSurface> CompositorImpl::CreateOutputSurface(
void CompositorImpl::OnLostResources() {
client_->DidLoseResources();
-}
-
-scoped_refptr<cc::ContextProvider> CompositorImpl::OffscreenContextProvider() {
- // There is no support for offscreen contexts, or compositor filters that
- // would require them in this compositor instance. If they are needed,
- // then implement a context provider that provides contexts from
- // ImageTransportSurfaceAndroid.
- return NULL;
-}
-
-void CompositorImpl::DidCompleteSwapBuffers() {
- client_->OnSwapBuffersCompleted();
+ ui_resource_provider_.UIResourcesAreInvalid();
}
void CompositorImpl::ScheduleComposite() {
- client_->ScheduleComposite();
+ DCHECK(!needs_composite_ || WillComposite());
+ if (ignore_schedule_composite_)
+ return;
+
+ needs_composite_ = true;
+ // We currently expect layer tree invalidations at most once per frame
+ // during normal operation and therefore try to composite immediately
+ // to minimize latency.
+ PostComposite(COMPOSITE_IMMEDIATELY);
}
void CompositorImpl::ScheduleAnimation() {
- ScheduleComposite();
+ DCHECK(!needs_animate_ || needs_composite_);
+ DCHECK(!needs_composite_ || WillComposite());
+ needs_animate_ = true;
+
+ if (needs_composite_)
+ return;
+
+ TRACE_EVENT0("cc", "CompositorImpl::ScheduleAnimation");
+ needs_composite_ = true;
+ PostComposite(COMPOSITE_EVENTUALLY);
}
void CompositorImpl::DidPostSwapBuffers() {
TRACE_EVENT0("compositor", "CompositorImpl::DidPostSwapBuffers");
- client_->OnSwapBuffersPosted();
+ did_post_swapbuffers_ = true;
+}
+
+void CompositorImpl::DidCompleteSwapBuffers() {
+ TRACE_EVENT0("compositor", "CompositorImpl::DidCompleteSwapBuffers");
+ DCHECK_GT(pending_swapbuffers_, 0U);
+ if (pending_swapbuffers_-- == kMaxSwapBuffers && needs_composite_)
+ PostComposite(COMPOSITE_IMMEDIATELY);
+ client_->OnSwapBuffersCompleted(pending_swapbuffers_);
}
void CompositorImpl::DidAbortSwapBuffers() {
TRACE_EVENT0("compositor", "CompositorImpl::DidAbortSwapBuffers");
- client_->OnSwapBuffersCompleted();
-}
-
-blink::WebGLId CompositorImpl::BuildBasicTexture() {
- blink::WebGraphicsContext3D* context =
- ImageTransportFactoryAndroid::GetInstance()->GetContext3D();
- if (context->isContextLost() || !context->makeContextCurrent())
- return 0;
- blink::WebGLId texture_id = context->createTexture();
- context->bindTexture(GL_TEXTURE_2D, texture_id);
- context->texParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- context->texParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- context->texParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- context->texParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- return texture_id;
-}
-
-blink::WGC3Denum CompositorImpl::GetGLFormatForBitmap(
- gfx::JavaBitmap& bitmap) {
- switch (bitmap.format()) {
- case ANDROID_BITMAP_FORMAT_A_8:
- return GL_ALPHA;
- break;
- case ANDROID_BITMAP_FORMAT_RGBA_4444:
- return GL_RGBA;
- break;
- case ANDROID_BITMAP_FORMAT_RGBA_8888:
- return GL_RGBA;
- break;
- case ANDROID_BITMAP_FORMAT_RGB_565:
- default:
- return GL_RGB;
- }
+ // This really gets called only once from
+ // SingleThreadProxy::DidLoseOutputSurfaceOnImplThread() when the
+ // context was lost.
+ client_->OnSwapBuffersCompleted(0);
}
-blink::WGC3Denum CompositorImpl::GetGLTypeForBitmap(gfx::JavaBitmap& bitmap) {
- switch (bitmap.format()) {
- case ANDROID_BITMAP_FORMAT_A_8:
- return GL_UNSIGNED_BYTE;
- break;
- case ANDROID_BITMAP_FORMAT_RGBA_4444:
- return GL_UNSIGNED_SHORT_4_4_4_4;
- break;
- case ANDROID_BITMAP_FORMAT_RGBA_8888:
- return GL_UNSIGNED_BYTE;
- break;
- case ANDROID_BITMAP_FORMAT_RGB_565:
- default:
- return GL_UNSIGNED_SHORT_5_6_5;
+void CompositorImpl::DidCommit() {
+ root_window_->OnCompositingDidCommit();
+}
+
+void CompositorImpl::AttachLayerForReadback(scoped_refptr<cc::Layer> layer) {
+ root_layer_->AddChild(layer);
+}
+
+void CompositorImpl::RequestCopyOfOutputOnRootLayer(
+ scoped_ptr<cc::CopyOutputRequest> request) {
+ root_layer_->RequestCopyOfOutput(request.Pass());
+}
+
+void CompositorImpl::OnVSync(base::TimeTicks frame_time,
+ base::TimeDelta vsync_period) {
+ vsync_period_ = vsync_period;
+ last_vsync_ = frame_time;
+
+ if (WillCompositeThisFrame()) {
+ // We somehow missed the last vsync interval, so reschedule for deadline.
+ // We cannot schedule immediately, or will get us out-of-phase with new
+ // renderer frames.
+ CancelComposite();
+ composite_on_vsync_trigger_ = COMPOSITE_EVENTUALLY;
+ } else {
+ current_composite_task_.reset();
+ }
+
+ DCHECK(!DidCompositeThisFrame() && !WillCompositeThisFrame());
+ if (composite_on_vsync_trigger_ != DO_NOT_COMPOSITE) {
+ CompositingTrigger trigger = composite_on_vsync_trigger_;
+ composite_on_vsync_trigger_ = DO_NOT_COMPOSITE;
+ PostComposite(trigger);
}
}
-void CompositorImpl::DidCommit() {
- root_window_->OnCompositingDidCommit();
+void CompositorImpl::SetNeedsAnimate() {
+ if (!host_)
+ return;
+
+ host_->SetNeedsAnimate();
}
-} // namespace content
+} // namespace content
diff --git a/chromium/content/browser/renderer_host/compositor_impl_android.h b/chromium/content/browser/renderer_host/compositor_impl_android.h
index a662b1de72e..4792d9058c7 100644
--- a/chromium/content/browser/renderer_host/compositor_impl_android.h
+++ b/chromium/content/browser/renderer_host/compositor_impl_android.h
@@ -6,29 +6,32 @@
#define CONTENT_BROWSER_RENDERER_HOST_COMPOSITOR_IMPL_ANDROID_H_
#include "base/basictypes.h"
+#include "base/cancelable_callback.h"
#include "base/compiler_specific.h"
#include "base/containers/scoped_ptr_hash_map.h"
#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
#include "cc/resources/ui_resource_client.h"
#include "cc/trees/layer_tree_host_client.h"
#include "cc/trees/layer_tree_host_single_thread_client.h"
+#include "content/browser/android/ui_resource_provider_impl.h"
#include "content/browser/renderer_host/image_transport_factory_android.h"
#include "content/common/content_export.h"
-#include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h"
#include "content/public/browser/android/compositor.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "ui/base/android/window_android_compositor.h"
+class SkBitmap;
struct ANativeWindow;
namespace cc {
-class InputHandlerClient;
class Layer;
class LayerTreeHost;
-class ScopedUIResource;
}
namespace content {
class CompositorClient;
-class GraphicsContext;
+class UIResourceProvider;
// -----------------------------------------------------------------------------
// Browser-side compositor that manages a tree of content and UI layers.
@@ -37,16 +40,22 @@ class CONTENT_EXPORT CompositorImpl
: public Compositor,
public cc::LayerTreeHostClient,
public cc::LayerTreeHostSingleThreadClient,
- public ImageTransportFactoryAndroidObserver {
+ public ImageTransportFactoryAndroidObserver,
+ public ui::WindowAndroidCompositor {
public:
CompositorImpl(CompositorClient* client, gfx::NativeWindow root_window);
virtual ~CompositorImpl();
static bool IsInitialized();
- // Returns the Java Surface object for a given view surface id.
- static jobject GetSurface(int surface_id);
+ // Creates a surface texture and returns a surface texture id. Returns -1 on
+ // failure.
+ static int CreateSurfaceTexture(int child_process_id);
+ // Destroy all surface textures associated with |child_process_id|.
+ static void DestroyAllSurfaceTextures(int child_process_id);
+
+ private:
// Compositor implementation.
virtual void SetRootLayer(scoped_refptr<cc::Layer> root) OVERRIDE;
virtual void SetWindowSurface(ANativeWindow* window) OVERRIDE;
@@ -54,38 +63,24 @@ class CONTENT_EXPORT CompositorImpl
virtual void SetVisible(bool visible) OVERRIDE;
virtual void setDeviceScaleFactor(float factor) OVERRIDE;
virtual void SetWindowBounds(const gfx::Size& size) OVERRIDE;
- virtual bool CompositeAndReadback(
- void *pixels, const gfx::Rect& rect) OVERRIDE;
- virtual void Composite() OVERRIDE;
- virtual cc::UIResourceId GenerateUIResource(
- const cc::UIResourceBitmap& bitmap) OVERRIDE;
- virtual void DeleteUIResource(cc::UIResourceId resource_id) OVERRIDE;
- virtual blink::WebGLId GenerateTexture(gfx::JavaBitmap& bitmap) OVERRIDE;
- virtual blink::WebGLId GenerateCompressedTexture(
- gfx::Size& size, int data_size, void* data) OVERRIDE;
- virtual void DeleteTexture(blink::WebGLId texture_id) OVERRIDE;
- virtual bool CopyTextureToBitmap(blink::WebGLId texture_id,
- gfx::JavaBitmap& bitmap) OVERRIDE;
- virtual bool CopyTextureToBitmap(blink::WebGLId texture_id,
- const gfx::Rect& sub_rect,
- gfx::JavaBitmap& bitmap) OVERRIDE;
+ virtual void SetHasTransparentBackground(bool flag) OVERRIDE;
+ virtual void SetNeedsComposite() OVERRIDE;
+ virtual UIResourceProvider& GetUIResourceProvider() OVERRIDE;
// LayerTreeHostClient implementation.
virtual void WillBeginMainFrame(int frame_id) OVERRIDE {}
virtual void DidBeginMainFrame() OVERRIDE {}
- virtual void Animate(double frame_begin_time) OVERRIDE {}
- virtual void Layout() OVERRIDE {}
- virtual void ApplyScrollAndScale(gfx::Vector2d scroll_delta,
+ virtual void Animate(base::TimeTicks frame_begin_time) OVERRIDE {}
+ virtual void Layout() OVERRIDE;
+ virtual void ApplyScrollAndScale(const gfx::Vector2d& scroll_delta,
float page_scale) OVERRIDE {}
virtual scoped_ptr<cc::OutputSurface> CreateOutputSurface(bool fallback)
OVERRIDE;
- virtual void DidInitializeOutputSurface(bool success) OVERRIDE {}
+ virtual void DidInitializeOutputSurface() OVERRIDE {}
virtual void WillCommit() OVERRIDE {}
virtual void DidCommit() OVERRIDE;
virtual void DidCommitAndDrawFrame() OVERRIDE {}
virtual void DidCompleteSwapBuffers() OVERRIDE;
- virtual scoped_refptr<cc::ContextProvider>
- OffscreenContextProvider() OVERRIDE;
// LayerTreeHostSingleThreadClient implementation.
virtual void ScheduleComposite() OVERRIDE;
@@ -96,29 +91,97 @@ class CONTENT_EXPORT CompositorImpl
// ImageTransportFactoryAndroidObserver implementation.
virtual void OnLostResources() OVERRIDE;
- private:
- blink::WebGLId BuildBasicTexture();
- blink::WGC3Denum GetGLFormatForBitmap(gfx::JavaBitmap& bitmap);
- blink::WGC3Denum GetGLTypeForBitmap(gfx::JavaBitmap& bitmap);
-
+ // WindowAndroidCompositor implementation.
+ virtual void AttachLayerForReadback(scoped_refptr<cc::Layer> layer) OVERRIDE;
+ virtual void RequestCopyOfOutputOnRootLayer(
+ scoped_ptr<cc::CopyOutputRequest> request) OVERRIDE;
+ virtual void OnVSync(base::TimeTicks frame_time,
+ base::TimeDelta vsync_period) OVERRIDE;
+ virtual void SetNeedsAnimate() OVERRIDE;
+
+ enum CompositingTrigger {
+ DO_NOT_COMPOSITE,
+ COMPOSITE_IMMEDIATELY,
+ COMPOSITE_EVENTUALLY,
+ };
+ void PostComposite(CompositingTrigger trigger);
+ void Composite(CompositingTrigger trigger);
+
+ bool WillCompositeThisFrame() const {
+ return current_composite_task_ &&
+ !current_composite_task_->callback().is_null();
+ }
+ bool DidCompositeThisFrame() const {
+ return current_composite_task_ &&
+ current_composite_task_->callback().is_null();
+ }
+ bool WillComposite() const {
+ return WillCompositeThisFrame() ||
+ composite_on_vsync_trigger_ != DO_NOT_COMPOSITE;
+ }
+ void CancelComposite() {
+ DCHECK(WillComposite());
+ if (WillCompositeThisFrame())
+ current_composite_task_->Cancel();
+ current_composite_task_.reset();
+ composite_on_vsync_trigger_ = DO_NOT_COMPOSITE;
+ will_composite_immediately_ = false;
+ }
+ cc::UIResourceId GenerateUIResourceFromUIResourceBitmap(
+ const cc::UIResourceBitmap& bitmap,
+ bool is_transient);
+ void OnGpuChannelEstablished();
+
+ // root_layer_ is the persistent internal root layer, while subroot_layer_
+ // is the one attached by the compositor client.
scoped_refptr<cc::Layer> root_layer_;
+ scoped_refptr<cc::Layer> subroot_layer_;
+
scoped_ptr<cc::LayerTreeHost> host_;
+ content::UIResourceProviderImpl ui_resource_provider_;
gfx::Size size_;
bool has_transparent_background_;
+ float device_scale_factor_;
ANativeWindow* window_;
int surface_id_;
CompositorClient* client_;
- scoped_refptr<cc::ContextProvider> null_offscreen_context_provider_;
+ gfx::NativeWindow root_window_;
+
+ // Used locally to track whether a call to LTH::Composite() did result in
+ // a posted SwapBuffers().
+ bool did_post_swapbuffers_;
- typedef base::ScopedPtrHashMap<cc::UIResourceId, cc::ScopedUIResource>
- UIResourceMap;
- UIResourceMap ui_resource_map_;
+ // Used locally to inhibit ScheduleComposite() during Layout().
+ bool ignore_schedule_composite_;
- gfx::NativeWindow root_window_;
+ // Whether we need to composite in general because of any invalidation or
+ // explicit request.
+ bool needs_composite_;
+
+ // Whether we need to update animations on the next composite.
+ bool needs_animate_;
+
+ // Whether we posted a task and are about to composite.
+ bool will_composite_immediately_;
+
+ // How we should schedule Composite during the next vsync.
+ CompositingTrigger composite_on_vsync_trigger_;
+
+ // The Composite operation scheduled for the current vsync interval.
+ scoped_ptr<base::CancelableClosure> current_composite_task_;
+
+ // The number of SwapBuffer calls that have not returned and ACK'd from
+ // the GPU thread.
+ unsigned int pending_swapbuffers_;
+
+ base::TimeDelta vsync_period_;
+ base::TimeTicks last_vsync_;
+
+ base::WeakPtrFactory<CompositorImpl> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(CompositorImpl);
};
diff --git a/chromium/content/browser/aura/compositor_resize_lock.cc b/chromium/content/browser/renderer_host/compositor_resize_lock_aura.cc
index e80baba5642..419b7b326b8 100644
--- a/chromium/content/browser/aura/compositor_resize_lock.cc
+++ b/chromium/content/browser/renderer_host/compositor_resize_lock_aura.cc
@@ -1,30 +1,32 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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/aura/compositor_resize_lock.h"
+#include "content/browser/renderer_host/compositor_resize_lock_aura.h"
#include "base/debug/trace_event.h"
#include "content/public/browser/browser_thread.h"
-#include "ui/aura/root_window.h"
+#include "ui/aura/window_event_dispatcher.h"
+#include "ui/aura/window_tree_host.h"
#include "ui/compositor/compositor.h"
namespace content {
-CompositorResizeLock::CompositorResizeLock(aura::RootWindow* root_window,
- const gfx::Size new_size,
- bool defer_compositor_lock,
- const base::TimeDelta& timeout)
+CompositorResizeLock::CompositorResizeLock(
+ aura::WindowTreeHost* host,
+ const gfx::Size new_size,
+ bool defer_compositor_lock,
+ const base::TimeDelta& timeout)
: ResizeLock(new_size, defer_compositor_lock),
- root_window_(root_window),
+ host_(host),
weak_ptr_factory_(this),
cancelled_(false) {
- DCHECK(root_window_);
+ DCHECK(host_);
TRACE_EVENT_ASYNC_BEGIN2("ui", "CompositorResizeLock", this,
"width", expected_size().width(),
"height", expected_size().height());
- root_window_->HoldPointerMoves();
+ host_->dispatcher()->HoldPointerMoves();
BrowserThread::PostDelayedTask(
BrowserThread::UI, FROM_HERE,
@@ -51,7 +53,7 @@ void CompositorResizeLock::UnlockCompositor() {
void CompositorResizeLock::LockCompositor() {
ResizeLock::LockCompositor();
- compositor_lock_ = root_window_->compositor()->GetCompositorLock();
+ compositor_lock_ = host_->compositor()->GetCompositorLock();
}
void CompositorResizeLock::CancelLock() {
@@ -59,7 +61,7 @@ void CompositorResizeLock::CancelLock() {
return;
cancelled_ = true;
UnlockCompositor();
- root_window_->ReleasePointerMoves();
+ host_->dispatcher()->ReleasePointerMoves();
}
} // namespace content
diff --git a/chromium/content/browser/aura/compositor_resize_lock.h b/chromium/content/browser/renderer_host/compositor_resize_lock_aura.h
index 51787e49393..1c2be391053 100644
--- a/chromium/content/browser/aura/compositor_resize_lock.h
+++ b/chromium/content/browser/renderer_host/compositor_resize_lock_aura.h
@@ -1,28 +1,31 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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_AURA_RESIZE_LOCK_AURA_H_
-#define CONTENT_BROWSER_AURA_RESIZE_LOCK_AURA_H_
+#ifndef CONTENT_BROWSER_RENDERER_HOST_COMPOSITOR_RESIZE_LOCK_AURA_H_
+#define CONTENT_BROWSER_RENDERER_HOST_COMPOSITOR_RESIZE_LOCK_AURA_H_
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
-#include "content/browser/aura/resize_lock.h"
+#include "content/browser/compositor/resize_lock.h"
-namespace aura { class RootWindow; }
-
-namespace ui { class CompositorLock; }
+namespace aura {
+class WindowTreeHost;
+}
+namespace ui {
+class CompositorLock;
+}
namespace content {
// Used to prevent further resizes while a resize is pending.
class CompositorResizeLock : public ResizeLock {
public:
- CompositorResizeLock(aura::RootWindow* root_window,
- const gfx::Size new_size,
- bool defer_compositor_lock,
- const base::TimeDelta& timeout);
+ CompositorResizeLock(aura::WindowTreeHost* host,
+ const gfx::Size new_size,
+ bool defer_compositor_lock,
+ const base::TimeDelta& timeout);
virtual ~CompositorResizeLock();
virtual bool GrabDeferredLock() OVERRIDE;
@@ -33,7 +36,7 @@ class CompositorResizeLock : public ResizeLock {
void CancelLock();
private:
- aura::RootWindow* root_window_;
+ aura::WindowTreeHost* host_;
scoped_refptr<ui::CompositorLock> compositor_lock_;
base::WeakPtrFactory<CompositorResizeLock> weak_ptr_factory_;
bool cancelled_;
@@ -43,4 +46,4 @@ class CompositorResizeLock : public ResizeLock {
} // namespace content
-#endif // CONTENT_BROWSER_AURA_RESIZE_LOCK_AURA_H_
+#endif // CONTENT_BROWSER_RENDERER_HOST_COMPOSITOR_RESIZE_LOCK_AURA_H_
diff --git a/chromium/content/browser/renderer_host/database_message_filter.cc b/chromium/content/browser/renderer_host/database_message_filter.cc
index 55ea305d6e5..7c7b9a36b1b 100644
--- a/chromium/content/browser/renderer_host/database_message_filter.cc
+++ b/chromium/content/browser/renderer_host/database_message_filter.cc
@@ -7,7 +7,6 @@
#include <string>
#include "base/bind.h"
-#include "base/platform_file.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread.h"
@@ -18,6 +17,7 @@
#include "webkit/browser/database/database_util.h"
#include "webkit/browser/database/vfs_backend.h"
#include "webkit/browser/quota/quota_manager.h"
+#include "webkit/browser/quota/quota_manager_proxy.h"
#include "webkit/common/database/database_identifier.h"
#if defined(OS_POSIX)
@@ -25,7 +25,6 @@
#endif
using quota::QuotaManager;
-using quota::QuotaManagerProxy;
using quota::QuotaStatusCode;
using webkit_database::DatabaseTracker;
using webkit_database::DatabaseUtil;
@@ -41,7 +40,8 @@ const int kDelayDeleteRetryMs = 100;
DatabaseMessageFilter::DatabaseMessageFilter(
webkit_database::DatabaseTracker* db_tracker)
- : db_tracker_(db_tracker),
+ : BrowserMessageFilter(DatabaseMsgStart),
+ db_tracker_(db_tracker),
observer_added_(false) {
DCHECK(db_tracker_.get());
}
@@ -56,12 +56,12 @@ void DatabaseMessageFilter::OnChannelClosing() {
}
void DatabaseMessageFilter::AddObserver() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
db_tracker_->AddObserver(this);
}
void DatabaseMessageFilter::RemoveObserver() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
db_tracker_->RemoveObserver(this);
// If the renderer process died without closing all databases,
@@ -86,11 +86,9 @@ void DatabaseMessageFilter::OverrideThreadForMessage(
}
}
-bool DatabaseMessageFilter::OnMessageReceived(
- const IPC::Message& message,
- bool* message_was_ok) {
+bool DatabaseMessageFilter::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(DatabaseMessageFilter, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(DatabaseMessageFilter, message)
IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_OpenFile,
OnDatabaseOpenFile)
IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_DeleteFile,
@@ -106,7 +104,7 @@ bool DatabaseMessageFilter::OnMessageReceived(
IPC_MESSAGE_HANDLER(DatabaseHostMsg_Closed, OnDatabaseClosed)
IPC_MESSAGE_HANDLER(DatabaseHostMsg_HandleSqliteError, OnHandleSqliteError)
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return handled;
}
@@ -117,8 +115,9 @@ void DatabaseMessageFilter::OnDatabaseOpenFile(
const base::string16& vfs_file_name,
int desired_flags,
IPC::Message* reply_msg) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
- base::PlatformFile file_handle = base::kInvalidPlatformFileValue;
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ base::File file;
+ const base::File* tracked_file = NULL;
std::string origin_identifier;
base::string16 database_name;
@@ -128,8 +127,8 @@ void DatabaseMessageFilter::OnDatabaseOpenFile(
// open handles to them in the database tracker to make sure they're
// around for as long as needed.
if (vfs_file_name.empty()) {
- VfsBackend::OpenTempFileInDirectory(db_tracker_->DatabaseDirectory(),
- desired_flags, &file_handle);
+ file = VfsBackend::OpenTempFileInDirectory(db_tracker_->DatabaseDirectory(),
+ desired_flags);
} else if (DatabaseUtil::CrackVfsFileName(vfs_file_name, &origin_identifier,
&database_name, NULL) &&
!db_tracker_->IsDatabaseScheduledForDeletion(origin_identifier,
@@ -137,27 +136,36 @@ void DatabaseMessageFilter::OnDatabaseOpenFile(
base::FilePath db_file = DatabaseUtil::GetFullFilePathForVfsFile(
db_tracker_.get(), vfs_file_name);
if (!db_file.empty()) {
- if (db_tracker_->IsIncognitoProfile()) {
- db_tracker_->GetIncognitoFileHandle(vfs_file_name, &file_handle);
- if (file_handle == base::kInvalidPlatformFileValue) {
- VfsBackend::OpenFile(db_file,
- desired_flags | SQLITE_OPEN_DELETEONCLOSE,
- &file_handle);
- if (!(desired_flags & SQLITE_OPEN_DELETEONCLOSE))
- db_tracker_->SaveIncognitoFileHandle(vfs_file_name, file_handle);
+ if (db_tracker_->IsIncognitoProfile()) {
+ tracked_file = db_tracker_->GetIncognitoFile(vfs_file_name);
+ if (!tracked_file) {
+ file =
+ VfsBackend::OpenFile(db_file,
+ desired_flags | SQLITE_OPEN_DELETEONCLOSE);
+ if (!(desired_flags & SQLITE_OPEN_DELETEONCLOSE)) {
+ tracked_file = db_tracker_->SaveIncognitoFile(vfs_file_name,
+ file.Pass());
}
- } else {
- VfsBackend::OpenFile(db_file, desired_flags, &file_handle);
}
+ } else {
+ file = VfsBackend::OpenFile(db_file, desired_flags);
}
+ }
}
// Then we duplicate the file handle to make it useable in the renderer
// process. The original handle is closed, unless we saved it in the
// database tracker.
- bool auto_close = !db_tracker_->HasSavedIncognitoFileHandle(vfs_file_name);
IPC::PlatformFileForTransit target_handle =
- IPC::GetFileHandleForProcess(file_handle, PeerHandle(), auto_close);
+ IPC::InvalidPlatformFileForTransit();
+ if (file.IsValid()) {
+ target_handle = IPC::TakeFileHandleForProcess(file.Pass(), PeerHandle());
+ } else if (tracked_file) {
+ DCHECK(tracked_file->IsValid());
+ target_handle =
+ IPC::GetFileHandleForProcess(tracked_file->GetPlatformFile(),
+ PeerHandle(), false);
+ }
DatabaseHostMsg_OpenFile::WriteReplyParams(reply_msg, target_handle);
Send(reply_msg);
@@ -175,7 +183,7 @@ void DatabaseMessageFilter::DatabaseDeleteFile(
bool sync_dir,
IPC::Message* reply_msg,
int reschedule_count) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
// Return an error if the file name is invalid or if the file could not
// be deleted after kNumDeleteRetries attempts.
@@ -186,7 +194,7 @@ void DatabaseMessageFilter::DatabaseDeleteFile(
// In order to delete a journal file in incognito mode, we only need to
// close the open handle to it that's stored in the database tracker.
if (db_tracker_->IsIncognitoProfile()) {
- const base::string16 wal_suffix(ASCIIToUTF16("-wal"));
+ const base::string16 wal_suffix(base::ASCIIToUTF16("-wal"));
base::string16 sqlite_suffix;
// WAL files can be deleted without having previously been opened.
@@ -195,7 +203,8 @@ void DatabaseMessageFilter::DatabaseDeleteFile(
NULL, NULL, &sqlite_suffix) &&
sqlite_suffix == wal_suffix) {
error_code = SQLITE_OK;
- } else if (db_tracker_->CloseIncognitoFileHandle(vfs_file_name)) {
+ } else {
+ db_tracker_->CloseIncognitoFileHandle(vfs_file_name);
error_code = SQLITE_OK;
}
} else {
@@ -220,7 +229,7 @@ void DatabaseMessageFilter::DatabaseDeleteFile(
void DatabaseMessageFilter::OnDatabaseGetFileAttributes(
const base::string16& vfs_file_name,
IPC::Message* reply_msg) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
int32 attributes = -1;
base::FilePath db_file =
DatabaseUtil::GetFullFilePathForVfsFile(db_tracker_.get(), vfs_file_name);
@@ -234,7 +243,7 @@ void DatabaseMessageFilter::OnDatabaseGetFileAttributes(
void DatabaseMessageFilter::OnDatabaseGetFileSize(
const base::string16& vfs_file_name, IPC::Message* reply_msg) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
int64 size = 0;
base::FilePath db_file =
DatabaseUtil::GetFullFilePathForVfsFile(db_tracker_.get(), vfs_file_name);
@@ -247,7 +256,7 @@ void DatabaseMessageFilter::OnDatabaseGetFileSize(
void DatabaseMessageFilter::OnDatabaseGetSpaceAvailable(
const std::string& origin_identifier, IPC::Message* reply_msg) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(db_tracker_->quota_manager_proxy());
QuotaManager* quota_manager =
@@ -284,10 +293,10 @@ void DatabaseMessageFilter::OnDatabaseOpened(
const base::string16& database_name,
const base::string16& description,
int64 estimated_size) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
if (!DatabaseUtil::IsValidOriginIdentifier(origin_identifier)) {
- RecordAction(UserMetricsAction("BadMessageTerminate_DBMF"));
+ RecordAction(base::UserMetricsAction("BadMessageTerminate_DBMF"));
BadMessageReceived();
return;
}
@@ -303,10 +312,10 @@ void DatabaseMessageFilter::OnDatabaseOpened(
void DatabaseMessageFilter::OnDatabaseModified(
const std::string& origin_identifier,
const base::string16& database_name) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
if (!database_connections_.IsDatabaseOpened(
origin_identifier, database_name)) {
- RecordAction(UserMetricsAction("BadMessageTerminate_DBMF"));
+ RecordAction(base::UserMetricsAction("BadMessageTerminate_DBMF"));
BadMessageReceived();
return;
}
@@ -317,10 +326,10 @@ void DatabaseMessageFilter::OnDatabaseModified(
void DatabaseMessageFilter::OnDatabaseClosed(
const std::string& origin_identifier,
const base::string16& database_name) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
if (!database_connections_.IsDatabaseOpened(
origin_identifier, database_name)) {
- RecordAction(UserMetricsAction("BadMessageTerminate_DBMF"));
+ RecordAction(base::UserMetricsAction("BadMessageTerminate_DBMF"));
BadMessageReceived();
return;
}
@@ -333,9 +342,9 @@ void DatabaseMessageFilter::OnHandleSqliteError(
const std::string& origin_identifier,
const base::string16& database_name,
int error) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
if (!DatabaseUtil::IsValidOriginIdentifier(origin_identifier)) {
- RecordAction(UserMetricsAction("BadMessageTerminate_DBMF"));
+ RecordAction(base::UserMetricsAction("BadMessageTerminate_DBMF"));
BadMessageReceived();
return;
}
@@ -347,7 +356,7 @@ void DatabaseMessageFilter::OnDatabaseSizeChanged(
const std::string& origin_identifier,
const base::string16& database_name,
int64 database_size) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
if (database_connections_.IsOriginUsed(origin_identifier)) {
Send(new DatabaseMsg_UpdateSize(origin_identifier, database_name,
database_size));
@@ -357,7 +366,7 @@ void DatabaseMessageFilter::OnDatabaseSizeChanged(
void DatabaseMessageFilter::OnDatabaseScheduledForDeletion(
const std::string& origin_identifier,
const base::string16& database_name) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
Send(new DatabaseMsg_CloseImmediately(origin_identifier, database_name));
}
diff --git a/chromium/content/browser/renderer_host/database_message_filter.h b/chromium/content/browser/renderer_host/database_message_filter.h
index 50243356339..aa99e03c4e0 100644
--- a/chromium/content/browser/renderer_host/database_message_filter.h
+++ b/chromium/content/browser/renderer_host/database_message_filter.h
@@ -25,8 +25,7 @@ class DatabaseMessageFilter
virtual void OverrideThreadForMessage(
const IPC::Message& message,
BrowserThread::ID* thread) OVERRIDE;
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
webkit_database::DatabaseTracker* database_tracker() const {
return db_tracker_.get();
diff --git a/chromium/content/browser/renderer_host/delegated_frame_evictor.cc b/chromium/content/browser/renderer_host/delegated_frame_evictor.cc
index 8993d113898..33d6b2c479f 100644
--- a/chromium/content/browser/renderer_host/delegated_frame_evictor.cc
+++ b/chromium/content/browser/renderer_host/delegated_frame_evictor.cc
@@ -4,6 +4,8 @@
#include "content/browser/renderer_host/delegated_frame_evictor.h"
+#include "base/logging.h"
+
namespace content {
DelegatedFrameEvictor::DelegatedFrameEvictor(
@@ -23,8 +25,23 @@ void DelegatedFrameEvictor::DiscardedFrame() {
}
void DelegatedFrameEvictor::SetVisible(bool visible) {
- if (has_frame_)
- RendererFrameManager::GetInstance()->SetFrameVisibility(this, visible);
+ if (has_frame_) {
+ if (visible) {
+ RendererFrameManager::GetInstance()->LockFrame(this);
+ } else {
+ RendererFrameManager::GetInstance()->UnlockFrame(this);
+ }
+ }
+}
+
+void DelegatedFrameEvictor::LockFrame() {
+ DCHECK(has_frame_);
+ RendererFrameManager::GetInstance()->LockFrame(this);
+}
+
+void DelegatedFrameEvictor::UnlockFrame() {
+ DCHECK(has_frame_);
+ RendererFrameManager::GetInstance()->UnlockFrame(this);
}
void DelegatedFrameEvictor::EvictCurrentFrame() {
diff --git a/chromium/content/browser/renderer_host/delegated_frame_evictor.h b/chromium/content/browser/renderer_host/delegated_frame_evictor.h
index c54f2c4770b..64df6f7bc8c 100644
--- a/chromium/content/browser/renderer_host/delegated_frame_evictor.h
+++ b/chromium/content/browser/renderer_host/delegated_frame_evictor.h
@@ -25,6 +25,9 @@ class CONTENT_EXPORT DelegatedFrameEvictor : public RendererFrameManagerClient {
void SwappedFrame(bool visible);
void DiscardedFrame();
void SetVisible(bool visible);
+ void LockFrame();
+ void UnlockFrame();
+ bool HasFrame() { return has_frame_; }
private:
// RendererFrameManagerClient implementation.
diff --git a/chromium/content/browser/renderer_host/dip_util.cc b/chromium/content/browser/renderer_host/dip_util.cc
index f855abf4af5..30490158a3c 100644
--- a/chromium/content/browser/renderer_host/dip_util.cc
+++ b/chromium/content/browser/renderer_host/dip_util.cc
@@ -5,6 +5,7 @@
#include "content/browser/renderer_host/dip_util.h"
#include "content/public/browser/render_widget_host_view.h"
+#include "ui/base/layout.h"
#include "ui/gfx/display.h"
#include "ui/gfx/point.h"
#include "ui/gfx/point_conversions.h"
@@ -15,33 +16,25 @@
#include "ui/gfx/size_conversions.h"
namespace content {
-namespace {
-float GetScaleForView(const RenderWidgetHostView* view) {
- return ui::GetImageScale(GetScaleFactorForView(view));
-}
-
-} // namespace
-
-ui::ScaleFactor GetScaleFactorForView(const RenderWidgetHostView* view) {
+float GetScaleFactorForView(const RenderWidgetHostView* view) {
return ui::GetScaleFactorForNativeView(view ? view->GetNativeView() : NULL);
}
gfx::Point ConvertViewPointToDIP(const RenderWidgetHostView* view,
const gfx::Point& point_in_pixel) {
return gfx::ToFlooredPoint(
- gfx::ScalePoint(point_in_pixel, 1.0f / GetScaleForView(view)));
+ gfx::ScalePoint(point_in_pixel, 1.0f / GetScaleFactorForView(view)));
}
gfx::Size ConvertViewSizeToPixel(const RenderWidgetHostView* view,
const gfx::Size& size_in_dip) {
- return gfx::ToFlooredSize(
- gfx::ScaleSize(size_in_dip, GetScaleForView(view)));
+ return ConvertSizeToPixel(GetScaleFactorForView(view), size_in_dip);
}
gfx::Rect ConvertViewRectToPixel(const RenderWidgetHostView* view,
const gfx::Rect& rect_in_dip) {
- return ConvertRectToPixel(GetScaleForView(view), rect_in_dip);
+ return ConvertRectToPixel(GetScaleFactorForView(view), rect_in_dip);
}
gfx::Size ConvertSizeToDIP(float scale_factor,
@@ -56,6 +49,10 @@ gfx::Rect ConvertRectToDIP(float scale_factor,
gfx::ScaleRect(rect_in_pixel, 1.0f / scale_factor));
}
+gfx::Size ConvertSizeToPixel(float scale_factor,
+ const gfx::Size& size_in_dip) {
+ return gfx::ToFlooredSize(gfx::ScaleSize(size_in_dip, scale_factor));
+}
gfx::Rect ConvertRectToPixel(float scale_factor,
const gfx::Rect& rect_in_dip) {
diff --git a/chromium/content/browser/renderer_host/dip_util.h b/chromium/content/browser/renderer_host/dip_util.h
index a7e1876ec79..27e7dd19e9e 100644
--- a/chromium/content/browser/renderer_host/dip_util.h
+++ b/chromium/content/browser/renderer_host/dip_util.h
@@ -6,7 +6,6 @@
#define CONTENT_BROWSER_RENDERER_HOST_DIP_UTIL_H_
#include "content/common/content_export.h"
-#include "ui/base/layout.h"
namespace gfx {
class Point;
@@ -18,9 +17,8 @@ namespace content {
class RenderWidgetHostView;
// Returns scale factor of the display nearest to |view|.
-// Returns ui::SCALE_FACTOR_100P if the platform does not support DIP.
-CONTENT_EXPORT ui::ScaleFactor GetScaleFactorForView(
- const RenderWidgetHostView* view);
+// Returns 1.0f if the platform does not support DIP.
+CONTENT_EXPORT float GetScaleFactorForView(const RenderWidgetHostView* view);
// Utility functions that convert point/size/rect between DIP and pixel
// coordinate system.
@@ -35,6 +33,8 @@ CONTENT_EXPORT gfx::Size ConvertSizeToDIP(
float scale_factor, const gfx::Size& size_in_pixel);
CONTENT_EXPORT gfx::Rect ConvertRectToDIP(
float scale_factor, const gfx::Rect& rect_in_pixel);
+CONTENT_EXPORT gfx::Size ConvertSizeToPixel(
+ float scale_factor, const gfx::Size& size_in_pixel);
CONTENT_EXPORT gfx::Rect ConvertRectToPixel(
float scale_factor, const gfx::Rect& rect_in_dip);
diff --git a/chromium/content/browser/renderer_host/display_link_mac.cc b/chromium/content/browser/renderer_host/display_link_mac.cc
new file mode 100644
index 00000000000..b4592a9f3b7
--- /dev/null
+++ b/chromium/content/browser/renderer_host/display_link_mac.cc
@@ -0,0 +1,160 @@
+// 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/renderer_host/display_link_mac.h"
+
+#include "base/debug/trace_event.h"
+#include "base/logging.h"
+
+namespace base {
+
+template<>
+struct ScopedTypeRefTraits<CVDisplayLinkRef> {
+ static void Retain(CVDisplayLinkRef object) {
+ CVDisplayLinkRetain(object);
+ }
+ static void Release(CVDisplayLinkRef object) {
+ CVDisplayLinkRelease(object);
+ }
+};
+
+} // namespace base
+
+namespace content {
+
+// static
+scoped_refptr<DisplayLinkMac> DisplayLinkMac::GetForDisplay(
+ CGDirectDisplayID display_id) {
+ // Return the existing display link for this display, if it exists.
+ DisplayMap::iterator found = display_map_.Get().find(display_id);
+ if (found != display_map_.Get().end()) {
+ return found->second;
+ }
+
+ CVReturn ret = kCVReturnSuccess;
+
+ base::ScopedTypeRef<CVDisplayLinkRef> display_link;
+ ret = CVDisplayLinkCreateWithCGDisplay(
+ display_id,
+ display_link.InitializeInto());
+ if (ret != kCVReturnSuccess) {
+ LOG(ERROR) << "CVDisplayLinkCreateWithActiveCGDisplays failed: " << ret;
+ return NULL;
+ }
+
+ scoped_refptr<DisplayLinkMac> display_link_mac;
+ display_link_mac = new DisplayLinkMac(display_id, display_link);
+
+ ret = CVDisplayLinkSetOutputCallback(
+ display_link_mac->display_link_,
+ &DisplayLinkCallback,
+ display_link_mac.get());
+ if (ret != kCVReturnSuccess) {
+ LOG(ERROR) << "CVDisplayLinkSetOutputCallback failed: " << ret;
+ return NULL;
+ }
+
+ return display_link_mac;
+}
+
+DisplayLinkMac::DisplayLinkMac(
+ CGDirectDisplayID display_id,
+ base::ScopedTypeRef<CVDisplayLinkRef> display_link)
+ : display_id_(display_id),
+ display_link_(display_link),
+ stop_timer_(
+ FROM_HERE, base::TimeDelta::FromSeconds(1),
+ this, &DisplayLinkMac::StopDisplayLink),
+ timebase_and_interval_valid_(false) {
+ DCHECK(display_map_.Get().find(display_id) == display_map_.Get().end());
+ display_map_.Get().insert(std::make_pair(display_id_, this));
+}
+
+DisplayLinkMac::~DisplayLinkMac() {
+ if (CVDisplayLinkIsRunning(display_link_))
+ CVDisplayLinkStop(display_link_);
+
+ DisplayMap::iterator found = display_map_.Get().find(display_id_);
+ DCHECK(found != display_map_.Get().end());
+ DCHECK(found->second == this);
+ display_map_.Get().erase(found);
+}
+
+bool DisplayLinkMac::GetVSyncParameters(
+ base::TimeTicks* timebase, base::TimeDelta* interval) {
+ StartOrContinueDisplayLink();
+
+ base::AutoLock lock(lock_);
+ if (!timebase_and_interval_valid_)
+ return false;
+
+ *timebase = timebase_;
+ *interval = interval_;
+ return true;
+}
+
+void DisplayLinkMac::Tick(const CVTimeStamp* cv_time) {
+ TRACE_EVENT0("browser", "DisplayLinkMac::GetVSyncParameters");
+ base::AutoLock lock(lock_);
+
+ // Verify that videoRefreshPeriod is 32 bits.
+ DCHECK((cv_time->videoRefreshPeriod & ~0xffffFFFFull) == 0ull);
+
+ // Verify that the numerator and denominator make some sense.
+ uint32 numerator = static_cast<uint32>(cv_time->videoRefreshPeriod);
+ uint32 denominator = cv_time->videoTimeScale;
+ if (numerator <= 0 || denominator <= 0) {
+ LOG(WARNING) << "Unexpected numerator or denominator, bailing.";
+ return;
+ }
+
+ timebase_ = base::TimeTicks::FromInternalValue(
+ cv_time->hostTime / 1000);
+ interval_ = base::TimeDelta::FromMicroseconds(
+ 1000000 * static_cast<int64>(numerator) / denominator);
+ timebase_and_interval_valid_ = true;
+}
+
+void DisplayLinkMac::StartOrContinueDisplayLink() {
+ // Reset the timer, so that the display link won't be turned off for another
+ // second.
+ stop_timer_.Reset();
+
+ if (CVDisplayLinkIsRunning(display_link_))
+ return;
+
+ CVReturn ret = CVDisplayLinkStart(display_link_);
+ if (ret != kCVReturnSuccess) {
+ LOG(ERROR) << "CVDisplayLinkStart failed: " << ret;
+ }
+}
+
+void DisplayLinkMac::StopDisplayLink() {
+ if (!CVDisplayLinkIsRunning(display_link_))
+ return;
+
+ CVReturn ret = CVDisplayLinkStop(display_link_);
+ if (ret != kCVReturnSuccess) {
+ LOG(ERROR) << "CVDisplayLinkStop failed: " << ret;
+ }
+}
+
+CVReturn DisplayLinkMac::DisplayLinkCallback(
+ CVDisplayLinkRef display_link,
+ const CVTimeStamp* now,
+ const CVTimeStamp* output_time,
+ CVOptionFlags flags_in,
+ CVOptionFlags* flags_out,
+ void* context) {
+ DisplayLinkMac* display_link_mac = static_cast<DisplayLinkMac*>(context);
+ display_link_mac->Tick(output_time);
+ return kCVReturnSuccess;
+}
+
+// static
+base::LazyInstance<DisplayLinkMac::DisplayMap>
+ DisplayLinkMac::display_map_ = LAZY_INSTANCE_INITIALIZER;
+
+} // content
+
diff --git a/chromium/content/browser/renderer_host/display_link_mac.h b/chromium/content/browser/renderer_host/display_link_mac.h
new file mode 100644
index 00000000000..1833f8f2d3f
--- /dev/null
+++ b/chromium/content/browser/renderer_host/display_link_mac.h
@@ -0,0 +1,76 @@
+// 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_RENDERER_HOST_DISPLAY_LINK_MAC_H_
+#define CONTENT_BROWSER_RENDERER_HOST_DISPLAY_LINK_MAC_H_
+
+#include <QuartzCore/CVDisplayLink.h>
+#include <map>
+
+#include "base/lazy_instance.h"
+#include "base/mac/scoped_typeref.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+
+namespace content {
+
+class DisplayLinkMac : public base::RefCounted<DisplayLinkMac> {
+ public:
+ static scoped_refptr<DisplayLinkMac> GetForDisplay(
+ CGDirectDisplayID display_id);
+
+ // Get vsync scheduling parameters.
+ bool GetVSyncParameters(
+ base::TimeTicks* timebase,
+ base::TimeDelta* interval);
+
+ private:
+ friend class base::RefCounted<DisplayLinkMac>;
+
+ DisplayLinkMac(
+ CGDirectDisplayID display_id,
+ base::ScopedTypeRef<CVDisplayLinkRef> display_link);
+ virtual ~DisplayLinkMac();
+
+ void StartOrContinueDisplayLink();
+ void StopDisplayLink();
+ void Tick(const CVTimeStamp* time);
+
+ static CVReturn DisplayLinkCallback(
+ CVDisplayLinkRef display_link,
+ const CVTimeStamp* now,
+ const CVTimeStamp* output_time,
+ CVOptionFlags flags_in,
+ CVOptionFlags* flags_out,
+ void* context);
+
+ // The display that this display link is attached to.
+ CGDirectDisplayID display_id_;
+
+ // CVDisplayLink for querying VSync timing info.
+ base::ScopedTypeRef<CVDisplayLinkRef> display_link_;
+
+ // Timer for stopping the display link if it has not been queried in
+ // the last second.
+ base::DelayTimer<DisplayLinkMac> stop_timer_;
+
+ // VSync parameters computed during Tick.
+ bool timebase_and_interval_valid_;
+ base::TimeTicks timebase_;
+ base::TimeDelta interval_;
+
+ // Lock for sharing data between UI thread and display-link thread.
+ base::Lock lock_;
+
+ // Each display link instance consumes a non-negligible number of cycles, so
+ // make all display links on the same screen share the same object.
+ typedef std::map<CGDirectDisplayID, DisplayLinkMac*> DisplayMap;
+ static base::LazyInstance<DisplayMap> display_map_;
+};
+
+} // content
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_DISPLAY_LINK_MAC_H_
diff --git a/chromium/content/browser/renderer_host/event_with_latency_info.h b/chromium/content/browser/renderer_host/event_with_latency_info.h
new file mode 100644
index 00000000000..483c4cffc39
--- /dev/null
+++ b/chromium/content/browser/renderer_host/event_with_latency_info.h
@@ -0,0 +1,59 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_EVENT_WITH_LATENCY_INFO_H_
+#define CONTENT_BROWSER_RENDERER_HOST_EVENT_WITH_LATENCY_INFO_H_
+
+#include "ui/events/latency_info.h"
+
+#include "content/common/input/web_input_event_traits.h"
+
+namespace blink {
+class WebGestureEvent;
+class WebMouseEvent;
+class WebMouseWheelEvent;
+class WebTouchEvent;
+}
+
+namespace content {
+
+template <typename T>
+class EventWithLatencyInfo {
+ public:
+ T event;
+ ui::LatencyInfo latency;
+
+ EventWithLatencyInfo(const T& e, const ui::LatencyInfo& l)
+ : event(e), latency(l) {}
+
+ EventWithLatencyInfo() {}
+
+ bool CanCoalesceWith(const EventWithLatencyInfo& other)
+ const WARN_UNUSED_RESULT {
+ return WebInputEventTraits::CanCoalesce(other.event, event);
+ }
+
+ void CoalesceWith(const EventWithLatencyInfo& other) {
+ WebInputEventTraits::Coalesce(other.event, &event);
+ // When coalescing two input events, we keep the oldest LatencyInfo
+ // for Telemetry latency test since it will represent the longest
+ // latency.
+ if (other.latency.trace_id >= 0 &&
+ (latency.trace_id < 0 || other.latency.trace_id < latency.trace_id))
+ latency = other.latency;
+ }
+};
+
+typedef EventWithLatencyInfo<blink::WebGestureEvent>
+ GestureEventWithLatencyInfo;
+typedef EventWithLatencyInfo<blink::WebMouseWheelEvent>
+ MouseWheelEventWithLatencyInfo;
+typedef EventWithLatencyInfo<blink::WebMouseEvent>
+ MouseEventWithLatencyInfo;
+typedef EventWithLatencyInfo<blink::WebTouchEvent>
+ TouchEventWithLatencyInfo;
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_EVENT_WITH_LATENCY_INFO_H_
diff --git a/chromium/content/browser/renderer_host/file_utilities_message_filter.cc b/chromium/content/browser/renderer_host/file_utilities_message_filter.cc
index 5daa6ff2217..d819c6dcc9c 100644
--- a/chromium/content/browser/renderer_host/file_utilities_message_filter.cc
+++ b/chromium/content/browser/renderer_host/file_utilities_message_filter.cc
@@ -11,7 +11,8 @@
namespace content {
FileUtilitiesMessageFilter::FileUtilitiesMessageFilter(int process_id)
- : process_id_(process_id) {
+ : BrowserMessageFilter(FileUtilitiesMsgStart),
+ process_id_(process_id) {
}
FileUtilitiesMessageFilter::~FileUtilitiesMessageFilter() {
@@ -24,10 +25,10 @@ void FileUtilitiesMessageFilter::OverrideThreadForMessage(
*thread = BrowserThread::FILE;
}
-bool FileUtilitiesMessageFilter::OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) {
+bool FileUtilitiesMessageFilter::OnMessageReceived(
+ const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(FileUtilitiesMessageFilter, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(FileUtilitiesMessageFilter, message)
IPC_MESSAGE_HANDLER(FileUtilitiesMsg_GetFileInfo, OnGetFileInfo)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
@@ -36,10 +37,10 @@ bool FileUtilitiesMessageFilter::OnMessageReceived(const IPC::Message& message,
void FileUtilitiesMessageFilter::OnGetFileInfo(
const base::FilePath& path,
- base::PlatformFileInfo* result,
- base::PlatformFileError* status) {
- *result = base::PlatformFileInfo();
- *status = base::PLATFORM_FILE_OK;
+ base::File::Info* result,
+ base::File::Error* status) {
+ *result = base::File::Info();
+ *status = base::File::FILE_OK;
// Get file metadata only when the child process has been granted
// permission to read the file.
@@ -49,7 +50,7 @@ void FileUtilitiesMessageFilter::OnGetFileInfo(
}
if (!base::GetFileInfo(path, result))
- *status = base::PLATFORM_FILE_ERROR_FAILED;
+ *status = base::File::FILE_ERROR_FAILED;
}
} // namespace content
diff --git a/chromium/content/browser/renderer_host/file_utilities_message_filter.h b/chromium/content/browser/renderer_host/file_utilities_message_filter.h
index 948e6c20b4a..ecfbfe02ce9 100644
--- a/chromium/content/browser/renderer_host/file_utilities_message_filter.h
+++ b/chromium/content/browser/renderer_host/file_utilities_message_filter.h
@@ -6,13 +6,9 @@
#define CONTENT_BROWSER_RENDERER_HOST_FILE_UTILITIES_MESSAGE_FILTER_H_
#include "base/basictypes.h"
+#include "base/files/file.h"
#include "base/files/file_path.h"
#include "content/public/browser/browser_message_filter.h"
-#include "ipc/ipc_platform_file.h"
-
-namespace base {
-struct PlatformFileInfo;
-}
namespace IPC {
class Message;
@@ -28,17 +24,16 @@ class FileUtilitiesMessageFilter : public BrowserMessageFilter {
virtual void OverrideThreadForMessage(
const IPC::Message& message,
BrowserThread::ID* thread) OVERRIDE;
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
private:
virtual ~FileUtilitiesMessageFilter();
typedef void (*FileInfoWriteFunc)(IPC::Message* reply_msg,
- const base::PlatformFileInfo& file_info);
+ const base::File::Info& file_info);
void OnGetFileInfo(const base::FilePath& path,
- base::PlatformFileInfo* result,
- base::PlatformFileError* status);
+ base::File::Info* result,
+ base::File::Error* status);
// The ID of this process.
int process_id_;
diff --git a/chromium/content/browser/renderer_host/gamepad_browser_message_filter.cc b/chromium/content/browser/renderer_host/gamepad_browser_message_filter.cc
index 6b5972e5a0a..ef66a5cbd30 100644
--- a/chromium/content/browser/renderer_host/gamepad_browser_message_filter.cc
+++ b/chromium/content/browser/renderer_host/gamepad_browser_message_filter.cc
@@ -10,51 +10,52 @@
namespace content {
GamepadBrowserMessageFilter::GamepadBrowserMessageFilter()
- : is_started_(false) {
+ : BrowserMessageFilter(GamepadMsgStart),
+ is_started_(false) {
}
GamepadBrowserMessageFilter::~GamepadBrowserMessageFilter() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (is_started_)
- GamepadService::GetInstance()->RemoveConsumer();
+ GamepadService::GetInstance()->RemoveConsumer(this);
}
bool GamepadBrowserMessageFilter::OnMessageReceived(
- const IPC::Message& message,
- bool* message_was_ok) {
+ const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(GamepadBrowserMessageFilter,
- message,
- *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(GamepadBrowserMessageFilter, message)
IPC_MESSAGE_HANDLER(GamepadHostMsg_StartPolling, OnGamepadStartPolling)
IPC_MESSAGE_HANDLER(GamepadHostMsg_StopPolling, OnGamepadStopPolling)
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return handled;
}
+void GamepadBrowserMessageFilter::OnGamepadConnected(
+ unsigned index,
+ const blink::WebGamepad& gamepad) {
+ Send(new GamepadMsg_GamepadConnected(index, gamepad));
+}
+
+void GamepadBrowserMessageFilter::OnGamepadDisconnected(
+ unsigned index,
+ const blink::WebGamepad& gamepad) {
+ Send(new GamepadMsg_GamepadDisconnected(index, gamepad));
+}
+
void GamepadBrowserMessageFilter::OnGamepadStartPolling(
base::SharedMemoryHandle* renderer_handle) {
GamepadService* service = GamepadService::GetInstance();
- if (!is_started_) {
- is_started_ = true;
- service->AddConsumer();
- *renderer_handle = service->GetSharedMemoryHandleForProcess(PeerHandle());
- } else {
- // Currently we only expect the renderer to tell us once to start.
- NOTREACHED();
- }
+ CHECK(!is_started_);
+ is_started_ = true;
+ service->ConsumerBecameActive(this);
+ *renderer_handle = service->GetSharedMemoryHandleForProcess(PeerHandle());
}
void GamepadBrowserMessageFilter::OnGamepadStopPolling() {
- // TODO(scottmg): Probably get rid of this message. We can't trust it will
- // arrive anyway if the renderer crashes, etc.
- if (is_started_) {
- is_started_ = false;
- GamepadService::GetInstance()->RemoveConsumer();
- } else {
- NOTREACHED();
- }
+ CHECK(is_started_);
+ is_started_ = false;
+ GamepadService::GetInstance()->ConsumerBecameInactive(this);
}
} // namespace content
diff --git a/chromium/content/browser/renderer_host/gamepad_browser_message_filter.h b/chromium/content/browser/renderer_host/gamepad_browser_message_filter.h
index 8661e71d7dc..fefbf424e4d 100644
--- a/chromium/content/browser/renderer_host/gamepad_browser_message_filter.h
+++ b/chromium/content/browser/renderer_host/gamepad_browser_message_filter.h
@@ -7,6 +7,7 @@
#include "base/compiler_specific.h"
#include "base/memory/shared_memory.h"
+#include "content/browser/gamepad/gamepad_consumer.h"
#include "content/public/browser/browser_message_filter.h"
namespace content {
@@ -14,13 +15,22 @@ namespace content {
class GamepadService;
class RenderProcessHost;
-class GamepadBrowserMessageFilter : public BrowserMessageFilter {
+class GamepadBrowserMessageFilter :
+ public BrowserMessageFilter,
+ public GamepadConsumer {
public:
GamepadBrowserMessageFilter();
// BrowserMessageFilter implementation.
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+
+ // GamepadConsumer implementation.
+ virtual void OnGamepadConnected(
+ unsigned index,
+ const blink::WebGamepad& gamepad) OVERRIDE;
+ virtual void OnGamepadDisconnected(
+ unsigned index,
+ const blink::WebGamepad& gamepad) OVERRIDE;
private:
virtual ~GamepadBrowserMessageFilter();
diff --git a/chromium/content/browser/renderer_host/gpu_message_filter.cc b/chromium/content/browser/renderer_host/gpu_message_filter.cc
index 9068a4072ec..1cbfaf08174 100644
--- a/chromium/content/browser/renderer_host/gpu_message_filter.cc
+++ b/chromium/content/browser/renderer_host/gpu_message_filter.cc
@@ -11,11 +11,12 @@
#include "base/bind.h"
#include "base/command_line.h"
#include "content/browser/gpu/browser_gpu_channel_host_factory.h"
+#include "content/browser/gpu/gpu_data_manager_impl_private.h"
#include "content/browser/gpu/gpu_process_host.h"
#include "content/browser/gpu/gpu_surface_tracker.h"
#include "content/browser/renderer_host/render_widget_helper.h"
#include "content/common/gpu/gpu_messages.h"
-#include "content/port/browser/render_widget_host_view_frame_subscriber.h"
+#include "content/public/browser/render_widget_host_view_frame_subscriber.h"
#include "content/public/common/content_switches.h"
#include "gpu/command_buffer/service/gpu_switches.h"
@@ -53,76 +54,42 @@ struct GpuMessageFilter::FrameSubscription {
GpuMessageFilter::GpuMessageFilter(int render_process_id,
RenderWidgetHelper* render_widget_helper)
- : gpu_process_id_(0),
+ : BrowserMessageFilter(GpuMsgStart),
+ gpu_process_id_(0),
render_process_id_(render_process_id),
- share_contexts_(false),
render_widget_helper_(render_widget_helper),
weak_ptr_factory_(this) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
-#if defined(USE_AURA) || defined(OS_ANDROID)
- // We use the GPU process for UI on Aura, and we need to share renderer GL
- // contexts with the compositor context.
- share_contexts_ = true;
-#else
- // Share contexts when compositing webview plugin or using share groups
- // for asynchronous texture uploads.
- if (!CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kDisableBrowserPluginCompositing) ||
- CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kEnableShareGroupAsyncTextureUpload))
- share_contexts_ = true;
-#endif
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
GpuMessageFilter::~GpuMessageFilter() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
EndAllFrameSubscriptions();
}
-bool GpuMessageFilter::OnMessageReceived(
- const IPC::Message& message,
- bool* message_was_ok) {
+bool GpuMessageFilter::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(GpuMessageFilter, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(GpuMessageFilter, message)
IPC_MESSAGE_HANDLER_DELAY_REPLY(GpuHostMsg_EstablishGpuChannel,
OnEstablishGpuChannel)
IPC_MESSAGE_HANDLER_DELAY_REPLY(GpuHostMsg_CreateViewCommandBuffer,
OnCreateViewCommandBuffer)
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return handled;
}
-void GpuMessageFilter::SurfaceUpdated(int32 surface_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- typedef std::vector<linked_ptr<CreateViewCommandBufferRequest> > RequestList;
- RequestList retry_requests;
- retry_requests.swap(pending_requests_);
- for (RequestList::iterator it = retry_requests.begin();
- it != retry_requests.end(); ++it) {
- if ((*it)->surface_id != surface_id) {
- pending_requests_.push_back(*it);
- } else {
- linked_ptr<CreateViewCommandBufferRequest> request = *it;
- OnCreateViewCommandBuffer(request->surface_id,
- request->init_params,
- request->reply.release());
- }
- }
-}
-
void GpuMessageFilter::BeginFrameSubscription(
int route_id,
scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
linked_ptr<FrameSubscription> subscription(
new FrameSubscription(route_id, subscriber.Pass()));
BeginFrameSubscriptionInternal(subscription);
}
void GpuMessageFilter::EndFrameSubscription(int route_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
FrameSubscriptionList frame_subscription_list;
frame_subscription_list.swap(frame_subscription_list_);
for (FrameSubscriptionList::iterator it = frame_subscription_list.begin();
@@ -137,7 +104,7 @@ void GpuMessageFilter::EndFrameSubscription(int route_id) {
void GpuMessageFilter::OnEstablishGpuChannel(
CauseForGpuLaunch cause_for_gpu_launch,
IPC::Message* reply_ptr) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
scoped_ptr<IPC::Message> reply(reply_ptr);
// TODO(apatrick): Eventually, this will return the route ID of a
@@ -163,9 +130,10 @@ void GpuMessageFilter::OnEstablishGpuChannel(
BeginAllFrameSubscriptions();
}
+ bool share_contexts = true;
host->EstablishGpuChannel(
render_process_id_,
- share_contexts_,
+ share_contexts,
base::Bind(&GpuMessageFilter::EstablishChannelCallback,
weak_ptr_factory_.GetWeakPtr(),
base::Passed(&reply)));
@@ -174,8 +142,9 @@ void GpuMessageFilter::OnEstablishGpuChannel(
void GpuMessageFilter::OnCreateViewCommandBuffer(
int32 surface_id,
const GPUCreateCommandBufferConfig& init_params,
+ int32 route_id,
IPC::Message* reply_ptr) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
scoped_ptr<IPC::Message> reply(reply_ptr);
GpuSurfaceTracker* surface_tracker = GpuSurfaceTracker::Get();
@@ -192,16 +161,10 @@ void GpuMessageFilter::OnCreateViewCommandBuffer(
<< " tried to access a surface for renderer " << renderer_id;
}
- if (compositing_surface.parent_gpu_process_id &&
- compositing_surface.parent_gpu_process_id != gpu_process_id_) {
- // If the current handle for the surface is using a different (older) gpu
- // host, it means the GPU process died and we need to wait until the UI
- // re-allocates the surface in the new process.
- linked_ptr<CreateViewCommandBufferRequest> request(
- new CreateViewCommandBufferRequest(
- surface_id, init_params, reply.Pass()));
- pending_requests_.push_back(request);
- return;
+ if (compositing_surface.parent_client_id &&
+ !GpuDataManagerImpl::GetInstance()->CanUseGpuBrowserCompositor()) {
+ // For the renderer to fall back to software also.
+ compositing_surface = gfx::GLSurfaceHandle();
}
GpuProcessHost* host = GpuProcessHost::FromID(gpu_process_id_);
@@ -219,6 +182,7 @@ void GpuMessageFilter::OnCreateViewCommandBuffer(
surface_id,
render_process_id_,
init_params,
+ route_id,
base::Bind(&GpuMessageFilter::CreateCommandBufferCallback,
weak_ptr_factory_.GetWeakPtr(),
base::Passed(&reply)));
@@ -228,7 +192,7 @@ void GpuMessageFilter::EstablishChannelCallback(
scoped_ptr<IPC::Message> reply,
const IPC::ChannelHandle& channel,
const gpu::GPUInfo& gpu_info) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
GpuHostMsg_EstablishGpuChannel::WriteReplyParams(
reply.get(), render_process_id_, channel, gpu_info);
@@ -236,9 +200,9 @@ void GpuMessageFilter::EstablishChannelCallback(
}
void GpuMessageFilter::CreateCommandBufferCallback(
- scoped_ptr<IPC::Message> reply, int32 route_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- GpuHostMsg_CreateViewCommandBuffer::WriteReplyParams(reply.get(), route_id);
+ scoped_ptr<IPC::Message> reply, CreateCommandBufferResult result) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ GpuHostMsg_CreateViewCommandBuffer::WriteReplyParams(reply.get(), result);
Send(reply.release());
}
diff --git a/chromium/content/browser/renderer_host/gpu_message_filter.h b/chromium/content/browser/renderer_host/gpu_message_filter.h
index 5492ad989a0..559415e70fe 100644
--- a/chromium/content/browser/renderer_host/gpu_message_filter.h
+++ b/chromium/content/browser/renderer_host/gpu_message_filter.h
@@ -13,6 +13,7 @@
#include "base/memory/weak_ptr.h"
#include "base/sequenced_task_runner_helpers.h"
#include "content/common/gpu/gpu_process_launch_causes.h"
+#include "content/common/gpu/gpu_result_codes.h"
#include "content/public/browser/browser_message_filter.h"
#include "ui/gfx/native_widget_types.h"
@@ -36,12 +37,7 @@ class GpuMessageFilter : public BrowserMessageFilter {
RenderWidgetHelper* render_widget_helper);
// BrowserMessageFilter methods:
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
-
- // Signals that the handle for a surface id was updated, and it may be time to
- // unblock existing CreateViewCommandBuffer requests using that surface.
- void SurfaceUpdated(int32 surface_id);
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
// This set of API is used to subscribe to frame presentation events.
// See RenderWidgetHostViewFrameSubscriber for more details.
@@ -64,13 +60,14 @@ class GpuMessageFilter : public BrowserMessageFilter {
void OnCreateViewCommandBuffer(
int32 surface_id,
const GPUCreateCommandBufferConfig& init_params,
+ int32 route_id,
IPC::Message* reply);
// Helper callbacks for the message handlers.
void EstablishChannelCallback(scoped_ptr<IPC::Message> reply,
const IPC::ChannelHandle& channel,
const gpu::GPUInfo& gpu_info);
void CreateCommandBufferCallback(scoped_ptr<IPC::Message> reply,
- int32 route_id);
+ CreateCommandBufferResult result);
void BeginAllFrameSubscriptions();
void EndAllFrameSubscriptions();
@@ -81,10 +78,8 @@ class GpuMessageFilter : public BrowserMessageFilter {
int gpu_process_id_;
int render_process_id_;
- bool share_contexts_;
scoped_refptr<RenderWidgetHelper> render_widget_helper_;
- std::vector<linked_ptr<CreateViewCommandBufferRequest> > pending_requests_;
base::WeakPtrFactory<GpuMessageFilter> weak_ptr_factory_;
diff --git a/chromium/content/browser/renderer_host/gtk_im_context_wrapper.cc b/chromium/content/browser/renderer_host/gtk_im_context_wrapper.cc
deleted file mode 100644
index d4b77894f5a..00000000000
--- a/chromium/content/browser/renderer_host/gtk_im_context_wrapper.cc
+++ /dev/null
@@ -1,665 +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/renderer_host/gtk_im_context_wrapper.h"
-
-#include <gdk/gdk.h>
-#include <gdk/gdkkeysyms.h>
-#include <gtk/gtk.h>
-
-#include <algorithm>
-
-#include "base/logging.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "content/browser/renderer_host/render_widget_host_impl.h"
-#include "content/browser/renderer_host/render_widget_host_view_gtk.h"
-#include "content/public/browser/native_web_keyboard_event.h"
-#include "third_party/WebKit/public/web/WebCompositionUnderline.h"
-#include "ui/base/ime/composition_text_util_pango.h"
-#include "ui/gfx/gtk_util.h"
-#include "ui/gfx/rect.h"
-
-namespace content {
-namespace {
-
-// Copied from third_party/WebKit/Source/WebCore/page/EventHandler.cpp
-//
-// Match key code of composition keydown event on windows.
-// IE sends VK_PROCESSKEY which has value 229;
-//
-// Please refer to following documents for detals:
-// - Virtual-Key Codes
-// http://msdn.microsoft.com/en-us/library/ms645540(VS.85).aspx
-// - How the IME System Works
-// http://msdn.microsoft.com/en-us/library/cc194848.aspx
-// - ImmGetVirtualKey Function
-// http://msdn.microsoft.com/en-us/library/dd318570(VS.85).aspx
-const int kCompositionEventKeyCode = 229;
-
-} // namespace
-
-// ui::CompositionUnderline should be identical to
-// blink::WebCompositionUnderline, so that we can do reinterpret_cast safely.
-// TODO(suzhe): remove it after migrating all code in chrome to use
-// ui::CompositionUnderline.
-COMPILE_ASSERT(sizeof(ui::CompositionUnderline) ==
- sizeof(blink::WebCompositionUnderline),
- ui_CompositionUnderline__WebKit_WebCompositionUnderline_diff);
-
-GtkIMContextWrapper::GtkIMContextWrapper(RenderWidgetHostViewGtk* host_view)
- : host_view_(host_view),
- context_(gtk_im_multicontext_new()),
- context_simple_(gtk_im_context_simple_new()),
- is_focused_(false),
- is_composing_text_(false),
- is_enabled_(false),
- is_in_key_event_handler_(false),
- is_composition_changed_(false),
- suppress_next_commit_(false),
- last_key_code_(0),
- last_key_was_up_(false),
- last_key_filtered_no_result_(false) {
- DCHECK(context_);
- DCHECK(context_simple_);
-
- // context_ and context_simple_ share the same callback handlers.
- // All data come from them are treated equally.
- // context_ is for full input method support.
- // context_simple_ is for supporting dead/compose keys when input method is
- // disabled by webkit, eg. in password input box.
- g_signal_connect(context_, "preedit-start",
- G_CALLBACK(HandlePreeditStartThunk), this);
- g_signal_connect(context_, "preedit-end",
- G_CALLBACK(HandlePreeditEndThunk), this);
- g_signal_connect(context_, "preedit-changed",
- G_CALLBACK(HandlePreeditChangedThunk), this);
- g_signal_connect(context_, "commit",
- G_CALLBACK(HandleCommitThunk), this);
- g_signal_connect(context_, "retrieve-surrounding",
- G_CALLBACK(HandleRetrieveSurroundingThunk), this);
-
- g_signal_connect(context_simple_, "preedit-start",
- G_CALLBACK(HandlePreeditStartThunk), this);
- g_signal_connect(context_simple_, "preedit-end",
- G_CALLBACK(HandlePreeditEndThunk), this);
- g_signal_connect(context_simple_, "preedit-changed",
- G_CALLBACK(HandlePreeditChangedThunk), this);
- g_signal_connect(context_simple_, "commit",
- G_CALLBACK(HandleCommitThunk), this);
- g_signal_connect(context_simple_, "retrieve-surrounding",
- G_CALLBACK(HandleRetrieveSurroundingThunk), this);
-
- GtkWidget* widget = host_view->GetNativeView();
- DCHECK(widget);
-
- g_signal_connect(widget, "realize",
- G_CALLBACK(HandleHostViewRealizeThunk), this);
- g_signal_connect(widget, "unrealize",
- G_CALLBACK(HandleHostViewUnrealizeThunk), this);
-
- // Set client window if the widget is already realized.
- HandleHostViewRealize(widget);
-}
-
-GtkIMContextWrapper::~GtkIMContextWrapper() {
- if (context_)
- g_object_unref(context_);
- if (context_simple_)
- g_object_unref(context_simple_);
-}
-
-void GtkIMContextWrapper::ProcessKeyEvent(GdkEventKey* event) {
- suppress_next_commit_ = false;
-
- // Indicates preedit-changed and commit signal handlers that we are
- // processing a key event.
- is_in_key_event_handler_ = true;
- // Reset this flag so that we can know if preedit is changed after
- // processing this key event.
- is_composition_changed_ = false;
- // Clear it so that we can know if something needs committing after
- // processing this key event.
- commit_text_.clear();
-
- // According to Document Object Model (DOM) Level 3 Events Specification
- // Appendix A: Keyboard events and key identifiers
- // http://www.w3.org/TR/DOM-Level-3-Events/keyset.html:
- // The event sequence would be:
- // 1. keydown
- // 2. textInput
- // 3. keyup
- //
- // So keydown must be sent to webkit before sending input method result,
- // while keyup must be sent afterwards.
- // However on Windows, if a keydown event has been processed by IME, its
- // virtual keycode will be changed to VK_PROCESSKEY(0xE5) before being sent
- // to application.
- // To emulate the windows behavior as much as possible, we need to send the
- // key event to the GtkIMContext object first, and decide whether or not to
- // send the original key event to webkit according to the result from IME.
- //
- // If IME is enabled by WebKit, this event will be dispatched to context_
- // to get full IME support. Otherwise it'll be dispatched to
- // context_simple_, so that dead/compose keys can still work.
- //
- // It sends a "commit" signal when it has a character to be inserted
- // even when we use a US keyboard so that we can send a Char event
- // (or an IME event) to the renderer in our "commit"-signal handler.
- // We should send a KeyDown (or a KeyUp) event before dispatching this
- // event to the GtkIMContext object (and send a Char event) so that WebKit
- // can dispatch the JavaScript events in the following order: onkeydown(),
- // onkeypress(), and onkeyup(). (Many JavaScript pages assume this.)
- gboolean filtered = false;
- if (is_enabled_) {
- filtered = gtk_im_context_filter_keypress(context_, event);
- } else {
- filtered = gtk_im_context_filter_keypress(context_simple_, event);
- }
-
- // Reset this flag here, as it's only used in input method callbacks.
- is_in_key_event_handler_ = false;
-
- NativeWebKeyboardEvent wke(reinterpret_cast<GdkEvent*>(event));
-
- // If the key event was handled by the input method, then we need to prevent
- // RenderView::UnhandledKeyboardEvent() from processing it.
- // Otherwise unexpected result may occur. For example if it's a
- // Backspace key event, the browser may go back to previous page.
- // We just send all keyup events to the browser to avoid breaking the
- // browser's MENU key function, which is actually the only keyup event
- // handled in the browser.
- if (filtered && event->type == GDK_KEY_PRESS)
- wke.skip_in_browser = true;
-
- const int key_code = wke.windowsKeyCode;
- const bool has_result = HasInputMethodResult();
-
- // Send filtered keydown event before sending IME result.
- // In order to workaround http://crosbug.com/6582, we only send a filtered
- // keydown event if it generated any input method result.
- if (event->type == GDK_KEY_PRESS && filtered && has_result)
- ProcessFilteredKeyPressEvent(&wke);
-
- // Send IME results. In most cases, it's only available if the key event
- // is filtered by IME. But in rare cases, an unfiltered key event may also
- // generate IME results.
- // Any IME results generated by a unfiltered key down event must be sent
- // before the key down event, to avoid some tricky issues. For example,
- // when using latin-post input method, pressing 'a' then Backspace, may
- // generate following events in sequence:
- // 1. keydown 'a' (filtered)
- // 2. preedit changed to "a"
- // 3. keyup 'a' (unfiltered)
- // 4. keydown Backspace (unfiltered)
- // 5. commit "a"
- // 6. preedit end
- // 7. keyup Backspace (unfiltered)
- //
- // In this case, the input box will be in a strange state if keydown
- // Backspace is sent to webkit before commit "a" and preedit end.
- if (has_result)
- ProcessInputMethodResult(event, filtered);
-
- // Send unfiltered keydown and keyup events after sending IME result.
- if (event->type == GDK_KEY_PRESS && !filtered) {
- ProcessUnfilteredKeyPressEvent(&wke);
- } else if (event->type == GDK_KEY_RELEASE) {
- // In order to workaround http://crosbug.com/6582, we need to suppress
- // the keyup event if corresponding keydown event was suppressed, or
- // the last key event was a keyup event with the same keycode.
- const bool suppress = (last_key_code_ == key_code) &&
- (last_key_was_up_ || last_key_filtered_no_result_);
-
- if (!suppress)
- host_view_->ForwardKeyboardEvent(wke);
- }
-
- last_key_code_ = key_code;
- last_key_was_up_ = (event->type == GDK_KEY_RELEASE);
- last_key_filtered_no_result_ = (filtered && !has_result);
-}
-
-void GtkIMContextWrapper::UpdateInputMethodState(
- ui::TextInputType type,
- bool can_compose_inline) {
- suppress_next_commit_ = false;
-
- // The renderer has updated its IME status.
- // Control the GtkIMContext object according to this status.
- if (!context_)
- return;
-
- DCHECK(!is_in_key_event_handler_);
-
- bool is_enabled = (type != ui::TEXT_INPUT_TYPE_NONE &&
- type != ui::TEXT_INPUT_TYPE_PASSWORD);
- if (is_enabled_ != is_enabled) {
- is_enabled_ = is_enabled;
- if (is_enabled && is_focused_)
- gtk_im_context_focus_in(context_);
- else
- gtk_im_context_focus_out(context_);
- }
-
- if (is_enabled) {
- // If the focused element supports inline rendering of composition text,
- // we receive and send related events to it. Otherwise, the events related
- // to the updates of composition text are directed to the candidate window.
- gtk_im_context_set_use_preedit(context_, can_compose_inline);
- }
-}
-
-void GtkIMContextWrapper::UpdateCaretBounds(
- const gfx::Rect& caret_bounds) {
- if (is_enabled_) {
- // Updates the position of the IME candidate window.
- // The position sent from the renderer is a relative one, so we need to
- // attach the GtkIMContext object to this window before changing the
- // position.
- GdkRectangle cursor_rect(caret_bounds.ToGdkRectangle());
- gtk_im_context_set_cursor_location(context_, &cursor_rect);
- }
-}
-
-void GtkIMContextWrapper::OnFocusIn() {
- if (is_focused_)
- return;
-
- // Tracks the focused state so that we can give focus to the
- // GtkIMContext object correctly later when IME is enabled by WebKit.
- is_focused_ = true;
-
- last_key_code_ = 0;
- last_key_was_up_ = false;
- last_key_filtered_no_result_ = false;
-
- // Notify the GtkIMContext object of this focus-in event only if IME is
- // enabled by WebKit.
- if (is_enabled_)
- gtk_im_context_focus_in(context_);
-
- // context_simple_ is always enabled.
- // Actually it doesn't care focus state at all.
- gtk_im_context_focus_in(context_simple_);
-
- // Enables RenderWidget's IME related events, so that we can be notified
- // when WebKit wants to enable or disable IME.
- if (host_view_->GetRenderWidgetHost()) {
- RenderWidgetHostImpl::From(
- host_view_->GetRenderWidgetHost())->SetInputMethodActive(true);
- }
-}
-
-void GtkIMContextWrapper::OnFocusOut() {
- if (!is_focused_)
- return;
-
- // Tracks the focused state so that we won't give focus to the
- // GtkIMContext object unexpectly.
- is_focused_ = false;
-
- // Notify the GtkIMContext object of this focus-out event only if IME is
- // enabled by WebKit.
- if (is_enabled_) {
- // To reset the GtkIMContext object and prevent data loss.
- ConfirmComposition();
- gtk_im_context_focus_out(context_);
- }
-
- // To make sure it'll be in correct state when focused in again.
- gtk_im_context_reset(context_simple_);
- gtk_im_context_focus_out(context_simple_);
-
- is_composing_text_ = false;
-
- // Disable RenderWidget's IME related events to save bandwidth.
- if (host_view_->GetRenderWidgetHost()) {
- RenderWidgetHostImpl::From(
- host_view_->GetRenderWidgetHost())->SetInputMethodActive(false);
- }
-}
-
-GtkWidget* GtkIMContextWrapper::BuildInputMethodsGtkMenu() {
- GtkWidget* submenu = gtk_menu_new();
- gtk_im_multicontext_append_menuitems(GTK_IM_MULTICONTEXT(context_),
- GTK_MENU_SHELL(submenu));
- return submenu;
-}
-
-void GtkIMContextWrapper::CancelComposition() {
- if (!is_enabled_)
- return;
-
- DCHECK(!is_in_key_event_handler_);
-
- // To prevent any text from being committed when resetting the |context_|;
- is_in_key_event_handler_ = true;
- suppress_next_commit_ = true;
-
- gtk_im_context_reset(context_);
- gtk_im_context_reset(context_simple_);
-
- if (is_focused_) {
- // Some input methods may not honour the reset call. Focusing out/in the
- // |context_| to make sure it gets reset correctly.
- gtk_im_context_focus_out(context_);
- gtk_im_context_focus_in(context_);
- }
-
- is_composing_text_ = false;
- composition_.Clear();
- commit_text_.clear();
-
- is_in_key_event_handler_ = false;
-}
-
-bool GtkIMContextWrapper::NeedCommitByForwardingCharEvent() const {
- // If there is no composition text and has only one character to be
- // committed, then the character will be send to webkit as a Char event
- // instead of a confirmed composition text.
- // It should be fine to handle BMP character only, as non-BMP characters
- // can always be committed as confirmed composition text.
- return !is_composing_text_ && commit_text_.length() == 1;
-}
-
-bool GtkIMContextWrapper::HasInputMethodResult() const {
- return commit_text_.length() || is_composition_changed_;
-}
-
-void GtkIMContextWrapper::ProcessFilteredKeyPressEvent(
- NativeWebKeyboardEvent* wke) {
- // If IME has filtered this event, then replace virtual key code with
- // VK_PROCESSKEY. See comment in ProcessKeyEvent() for details.
- // It's only required for keydown events.
- // To emulate windows behavior, when input method is enabled, if the commit
- // text can be emulated by a Char event, then don't do this replacement.
- if (!NeedCommitByForwardingCharEvent()) {
- wke->windowsKeyCode = kCompositionEventKeyCode;
- // keyidentifier must be updated accordingly, otherwise this key event may
- // still be processed by webkit.
- wke->setKeyIdentifierFromWindowsKeyCode();
- }
- host_view_->ForwardKeyboardEvent(*wke);
-}
-
-void GtkIMContextWrapper::ProcessUnfilteredKeyPressEvent(
- NativeWebKeyboardEvent* wke) {
- // Send keydown event as it, because it's not filtered by IME.
- host_view_->ForwardKeyboardEvent(*wke);
-
- // IME is disabled by WebKit or the GtkIMContext object cannot handle
- // this key event.
- // This case is caused by two reasons:
- // 1. The given key event is a control-key event, (e.g. return, page up,
- // page down, tab, arrows, etc.) or;
- // 2. The given key event is not a control-key event but printable
- // characters aren't assigned to the event, (e.g. alt+d, etc.)
- // Create a Char event manually from this key event and send it to the
- // renderer when this Char event contains a printable character which
- // should be processed by WebKit.
- // isSystemKey will be set to true if this key event has Alt modifier,
- // see WebInputEventFactory::keyboardEvent() for details.
- if (wke->text[0]) {
- wke->type = blink::WebInputEvent::Char;
- wke->skip_in_browser = true;
- host_view_->ForwardKeyboardEvent(*wke);
- }
-}
-
-void GtkIMContextWrapper::ProcessInputMethodResult(const GdkEventKey* event,
- bool filtered) {
- RenderWidgetHostImpl* host = RenderWidgetHostImpl::From(
- host_view_->GetRenderWidgetHost());
- if (!host)
- return;
-
- bool committed = false;
- // We do commit before preedit change, so that we can optimize some
- // unnecessary preedit changes.
- if (commit_text_.length()) {
- if (filtered && NeedCommitByForwardingCharEvent()) {
- // Send a Char event when we input a composed character without IMEs
- // so that this event is to be dispatched to onkeypress() handlers,
- // autofill, etc.
- // Only commit text generated by a filtered key down event can be sent
- // as a Char event, because a unfiltered key down event will probably
- // generate another Char event.
- // TODO(james.su@gmail.com): Is it necessary to support non BMP chars
- // here?
- NativeWebKeyboardEvent char_event(commit_text_[0],
- event->state,
- base::Time::Now().ToDoubleT());
- char_event.skip_in_browser = true;
- host_view_->ForwardKeyboardEvent(char_event);
- } else {
- committed = true;
- // Send an IME event.
- // Unlike a Char event, an IME event is NOT dispatched to onkeypress()
- // handlers or autofill.
- host->ImeConfirmComposition(
- commit_text_,gfx::Range::InvalidRange(),false);
- // Set this flag to false, as this composition session has been
- // finished.
- is_composing_text_ = false;
- }
- }
-
- // Send preedit text only if it's changed.
- // If a text has been committed, then we don't need to send the empty
- // preedit text again.
- if (is_composition_changed_) {
- if (composition_.text.length()) {
- // Another composition session has been started.
- is_composing_text_ = true;
- // TODO(suzhe): convert both renderer_host and renderer to use
- // ui::CompositionText.
- const std::vector<blink::WebCompositionUnderline>& underlines =
- reinterpret_cast<const std::vector<blink::WebCompositionUnderline>&>(
- composition_.underlines);
- host->ImeSetComposition(composition_.text, underlines,
- composition_.selection.start(),
- composition_.selection.end());
- } else if (!committed) {
- host->ImeCancelComposition();
- }
- }
-}
-
-void GtkIMContextWrapper::ConfirmComposition() {
- if (!is_enabled_)
- return;
-
- DCHECK(!is_in_key_event_handler_);
-
- if (is_composing_text_) {
- if (host_view_->GetRenderWidgetHost()) {
- RenderWidgetHostImpl::From(
- host_view_->GetRenderWidgetHost())->ImeConfirmComposition(
- base::string16(), gfx::Range::InvalidRange(), false);
- }
-
- // Reset the input method.
- CancelComposition();
- }
-}
-
-void GtkIMContextWrapper::HandleCommit(const base::string16& text) {
- if (suppress_next_commit_)
- return;
-
- // Append the text to the buffer, because commit signal might be fired
- // multiple times when processing a key event.
- commit_text_.append(text);
- // Nothing needs to do, if it's currently in ProcessKeyEvent()
- // handler, which will send commit text to webkit later. Otherwise,
- // we need send it here.
- // It's possible that commit signal is fired without a key event, for
- // example when user input via a voice or handwriting recognition software.
- // In this case, the text must be committed directly.
- if (!is_in_key_event_handler_ && host_view_->GetRenderWidgetHost()) {
- // Workaround http://crbug.com/45478 by sending fake key down/up events.
- SendFakeCompositionKeyEvent(blink::WebInputEvent::RawKeyDown);
- RenderWidgetHostImpl::From(
- host_view_->GetRenderWidgetHost())->ImeConfirmComposition(
- text, gfx::Range::InvalidRange(), false);
- SendFakeCompositionKeyEvent(blink::WebInputEvent::KeyUp);
- }
-}
-
-void GtkIMContextWrapper::HandlePreeditStart() {
- // Ignore preedit related signals triggered by CancelComposition() method.
- if (suppress_next_commit_)
- return;
- is_composing_text_ = true;
-}
-
-void GtkIMContextWrapper::HandlePreeditChanged(const gchar* text,
- PangoAttrList* attrs,
- int cursor_position) {
- // Ignore preedit related signals triggered by CancelComposition() method.
- if (suppress_next_commit_)
- return;
-
- // Don't set is_composition_changed_ to false if there is no change, because
- // this handler might be called multiple times with the same data.
- is_composition_changed_ = true;
- composition_.Clear();
-
- ui::ExtractCompositionTextFromGtkPreedit(text, attrs, cursor_position,
- &composition_);
-
- // TODO(suzhe): due to a bug of webkit, we currently can't use selection range
- // with composition string. See: https://bugs.webkit.org/show_bug.cgi?id=40805
- composition_.selection = gfx::Range(cursor_position);
-
- // In case we are using a buggy input method which doesn't fire
- // "preedit_start" signal.
- if (composition_.text.length())
- is_composing_text_ = true;
-
- // Nothing needs to do, if it's currently in ProcessKeyEvent()
- // handler, which will send preedit text to webkit later.
- // Otherwise, we need send it here if it's been changed.
- if (!is_in_key_event_handler_ && is_composing_text_ &&
- host_view_->GetRenderWidgetHost()) {
- // Workaround http://crbug.com/45478 by sending fake key down/up events.
- SendFakeCompositionKeyEvent(blink::WebInputEvent::RawKeyDown);
- // TODO(suzhe): convert both renderer_host and renderer to use
- // ui::CompositionText.
- const std::vector<blink::WebCompositionUnderline>& underlines =
- reinterpret_cast<const std::vector<blink::WebCompositionUnderline>&>(
- composition_.underlines);
- RenderWidgetHostImpl::From(
- host_view_->GetRenderWidgetHost())->ImeSetComposition(
- composition_.text, underlines, composition_.selection.start(),
- composition_.selection.end());
- SendFakeCompositionKeyEvent(blink::WebInputEvent::KeyUp);
- }
-}
-
-void GtkIMContextWrapper::HandlePreeditEnd() {
- if (composition_.text.length()) {
- // The composition session has been finished.
- composition_.Clear();
- is_composition_changed_ = true;
-
- // If there is still a preedit text when firing "preedit-end" signal,
- // we need inform webkit to clear it.
- // It's only necessary when it's not in ProcessKeyEvent ().
- if (!is_in_key_event_handler_ && host_view_->GetRenderWidgetHost()) {
- RenderWidgetHostImpl::From(
- host_view_->GetRenderWidgetHost())->ImeCancelComposition();
- }
- }
-
- // Don't set is_composing_text_ to false here, because "preedit_end"
- // signal may be fired before "commit" signal.
-}
-
-gboolean GtkIMContextWrapper::HandleRetrieveSurrounding(GtkIMContext* context) {
- if (!is_enabled_)
- return TRUE;
-
- std::string text;
- size_t cursor_index = 0;
-
- if (!is_enabled_ || !host_view_->RetrieveSurrounding(&text, &cursor_index)) {
- gtk_im_context_set_surrounding(context, "", 0, 0);
- return TRUE;
- }
-
- gtk_im_context_set_surrounding(context, text.c_str(), text.length(),
- cursor_index);
-
- return TRUE;
-}
-
-void GtkIMContextWrapper::HandleHostViewRealize(GtkWidget* widget) {
- // We should only set im context's client window once, because when setting
- // client window.im context may destroy and recreate its internal states and
- // objects.
- GdkWindow* gdk_window = gtk_widget_get_window(widget);
- if (gdk_window) {
- gtk_im_context_set_client_window(context_, gdk_window);
- gtk_im_context_set_client_window(context_simple_, gdk_window);
- }
-}
-
-void GtkIMContextWrapper::HandleHostViewUnrealize() {
- gtk_im_context_set_client_window(context_, NULL);
- gtk_im_context_set_client_window(context_simple_, NULL);
-}
-
-void GtkIMContextWrapper::SendFakeCompositionKeyEvent(
- blink::WebInputEvent::Type type) {
- NativeWebKeyboardEvent fake_event;
- fake_event.windowsKeyCode = kCompositionEventKeyCode;
- fake_event.skip_in_browser = true;
- fake_event.type = type;
- host_view_->ForwardKeyboardEvent(fake_event);
-}
-
-void GtkIMContextWrapper::HandleCommitThunk(
- GtkIMContext* context, gchar* text, GtkIMContextWrapper* self) {
- self->HandleCommit(UTF8ToUTF16(text));
-}
-
-void GtkIMContextWrapper::HandlePreeditStartThunk(
- GtkIMContext* context, GtkIMContextWrapper* self) {
- self->HandlePreeditStart();
-}
-
-void GtkIMContextWrapper::HandlePreeditChangedThunk(
- GtkIMContext* context, GtkIMContextWrapper* self) {
- gchar* text = NULL;
- PangoAttrList* attrs = NULL;
- gint cursor_position = 0;
- gtk_im_context_get_preedit_string(context, &text, &attrs, &cursor_position);
- self->HandlePreeditChanged(text, attrs, cursor_position);
- g_free(text);
- pango_attr_list_unref(attrs);
-}
-
-void GtkIMContextWrapper::HandlePreeditEndThunk(
- GtkIMContext* context, GtkIMContextWrapper* self) {
- self->HandlePreeditEnd();
-}
-
-gboolean GtkIMContextWrapper::HandleRetrieveSurroundingThunk(
- GtkIMContext* context, GtkIMContextWrapper* self) {
- return self->HandleRetrieveSurrounding(context);
-}
-
-void GtkIMContextWrapper::HandleHostViewRealizeThunk(
- GtkWidget* widget, GtkIMContextWrapper* self) {
- self->HandleHostViewRealize(widget);
-}
-
-void GtkIMContextWrapper::HandleHostViewUnrealizeThunk(
- GtkWidget* widget, GtkIMContextWrapper* self) {
- self->HandleHostViewUnrealize();
-}
-
-} // namespace content
diff --git a/chromium/content/browser/renderer_host/gtk_im_context_wrapper.h b/chromium/content/browser/renderer_host/gtk_im_context_wrapper.h
deleted file mode 100644
index 8bd027f0e89..00000000000
--- a/chromium/content/browser/renderer_host/gtk_im_context_wrapper.h
+++ /dev/null
@@ -1,201 +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_RENDERER_HOST_GTK_IM_CONTEXT_WRAPPER_H_
-#define CONTENT_BROWSER_RENDERER_HOST_GTK_IM_CONTEXT_WRAPPER_H_
-
-#include <gdk/gdk.h>
-#include <pango/pango-attributes.h>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/gtest_prod_util.h"
-#include "base/strings/string16.h"
-#include "third_party/WebKit/public/web/WebInputEvent.h"
-#include "ui/base/ime/composition_text.h"
-#include "ui/base/ime/text_input_type.h"
-
-typedef struct _GtkIMContext GtkIMContext;
-typedef struct _GtkWidget GtkWidget;
-
-namespace gfx {
-class Rect;
-}
-
-namespace content {
-class RenderWidgetHostViewGtk;
-struct NativeWebKeyboardEvent;
-
-// This class is a convenience wrapper for GtkIMContext.
-// It creates and manages two GtkIMContext instances, one is GtkIMMulticontext,
-// for plain text input box, another is GtkIMContextSimple, for password input
-// box.
-//
-// This class is in charge of dispatching key events to these two GtkIMContext
-// instances and handling signals emitted by them. Key events then will be
-// forwarded to renderer along with input method results via corresponding host
-// view.
-//
-// This class is used solely by RenderWidgetHostViewGtk.
-class GtkIMContextWrapper {
- public:
- explicit GtkIMContextWrapper(RenderWidgetHostViewGtk* host_view);
- ~GtkIMContextWrapper();
-
- // Processes a gdk key event received by |host_view|.
- void ProcessKeyEvent(GdkEventKey* event);
-
- void UpdateInputMethodState(ui::TextInputType type,
- bool can_compose_inline);
- void UpdateCaretBounds(const gfx::Rect& caret_bounds);
- void OnFocusIn();
- void OnFocusOut();
- bool is_focused() const { return is_focused_; }
-
- GtkWidget* BuildInputMethodsGtkMenu();
-
- void CancelComposition();
-
- void ConfirmComposition();
-
- private:
- // Check if a text needs commit by forwarding a char event instead of
- // by confirming as a composition text.
- bool NeedCommitByForwardingCharEvent() const;
-
- // Check if the input method returned any result, eg. preedit and commit text.
- bool HasInputMethodResult() const;
-
- void ProcessFilteredKeyPressEvent(NativeWebKeyboardEvent* wke);
- void ProcessUnfilteredKeyPressEvent(NativeWebKeyboardEvent* wke);
-
- // Processes result returned from input method after filtering a key event.
- // |filtered| indicates if the key event was filtered by the input method.
- void ProcessInputMethodResult(const GdkEventKey* event, bool filtered);
-
- // Real code of "commit" signal handler.
- void HandleCommit(const base::string16& text);
-
- // Real code of "preedit-start" signal handler.
- void HandlePreeditStart();
-
- // Real code of "preedit-changed" signal handler.
- void HandlePreeditChanged(const gchar* text,
- PangoAttrList* attrs,
- int cursor_position);
-
- // Real code of "preedit-end" signal handler.
- void HandlePreeditEnd();
-
- // Real code of "retrieve-surrounding" signal handler.
- gboolean HandleRetrieveSurrounding(GtkIMContext* context);
-
- // Real code of "realize" signal handler, used for setting im context's client
- // window.
- void HandleHostViewRealize(GtkWidget* widget);
-
- // Real code of "unrealize" signal handler, used for unsetting im context's
- // client window.
- void HandleHostViewUnrealize();
-
- // Sends a fake composition key event with specified event type. A composition
- // key event is a key event with special key code 229.
- void SendFakeCompositionKeyEvent(blink::WebInputEvent::Type type);
-
- // Signal handlers of GtkIMContext object.
- static void HandleCommitThunk(GtkIMContext* context, gchar* text,
- GtkIMContextWrapper* self);
- static void HandlePreeditStartThunk(GtkIMContext* context,
- GtkIMContextWrapper* self);
- static void HandlePreeditChangedThunk(GtkIMContext* context,
- GtkIMContextWrapper* self);
- static void HandlePreeditEndThunk(GtkIMContext* context,
- GtkIMContextWrapper* self);
- static gboolean HandleRetrieveSurroundingThunk(GtkIMContext* context,
- GtkIMContextWrapper* self);
-
- // Signal handlers connecting to |host_view_|'s native view widget.
- static void HandleHostViewRealizeThunk(GtkWidget* widget,
- GtkIMContextWrapper* self);
- static void HandleHostViewUnrealizeThunk(GtkWidget* widget,
- GtkIMContextWrapper* self);
-
- // The parent object.
- RenderWidgetHostViewGtk* host_view_;
-
- // The GtkIMContext object.
- // In terms of the DOM event specification Appendix A
- // <http://www.w3.org/TR/DOM-Level-3-Events/keyset.html>,
- // GTK uses a GtkIMContext object for the following two purposes:
- // 1. Composing Latin characters (A.1.2), and;
- // 2. Composing CJK characters with an IME (A.1.3).
- // Many JavaScript pages assume composed Latin characters are dispatched to
- // their onkeypress() handlers but not dispatched CJK characters composed
- // with an IME. To emulate this behavior, we should monitor the status of
- // this GtkIMContext object and prevent sending Char events when a
- // GtkIMContext object sends a "commit" signal with the CJK characters
- // composed by an IME.
- GtkIMContext* context_;
-
- // A GtkIMContextSimple object, for supporting dead/compose keys when input
- // method is disabled, eg. in password input box.
- GtkIMContext* context_simple_;
-
- // Whether or not this widget is focused.
- bool is_focused_;
-
- // Whether or not the above GtkIMContext is composing a text with an IME.
- // This flag is used in "commit" signal handler of the GtkIMContext object,
- // which determines how to submit the result text to WebKit according to this
- // flag.
- // If this flag is true or there are more than one characters in the result,
- // then the result text will be committed to WebKit as a confirmed
- // composition. Otherwise, it'll be forwarded as a key event.
- //
- // The GtkIMContext object sends a "preedit_start" before it starts composing
- // a text and a "preedit_end" signal after it finishes composing it.
- // "preedit_start" signal is monitored to turn it on.
- // We don't monitor "preedit_end" signal to turn it off, because an input
- // method may fire "preedit_end" signal before "commit" signal.
- // A buggy input method may not fire "preedit_start" and/or "preedit_end"
- // at all, so this flag will also be set to true when "preedit_changed" signal
- // is fired with non-empty preedit text.
- bool is_composing_text_;
-
- // Whether or not the IME is enabled.
- bool is_enabled_;
-
- // Whether or not it's currently running inside key event handler.
- // If it's true, then preedit-changed and commit handler will backup the
- // preedit or commit text instead of sending them down to webkit.
- // key event handler will send them later.
- bool is_in_key_event_handler_;
-
- // The most recent composition text information retrieved from context_;
- ui::CompositionText composition_;
-
- // Whether or not the composition has been changed since last key event.
- bool is_composition_changed_;
-
- // Stores a copy of the most recent commit text received by commit signal
- // handler.
- base::string16 commit_text_;
-
- // If it's true then the next "commit" signal will be suppressed.
- // It's only used to workaround http://crbug.com/50485.
- // TODO(suzhe): Remove it after input methods get fixed.
- bool suppress_next_commit_;
-
- // Information of the last key event, for working around
- // http://crosbug.com/6582
- int last_key_code_;
- bool last_key_was_up_;
- bool last_key_filtered_no_result_;
-
- DISALLOW_COPY_AND_ASSIGN(GtkIMContextWrapper);
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_RENDERER_HOST_GTK_IM_CONTEXT_WRAPPER_H_
diff --git a/chromium/content/browser/renderer_host/gtk_key_bindings_handler.cc b/chromium/content/browser/renderer_host/gtk_key_bindings_handler.cc
deleted file mode 100644
index 3830364a0e8..00000000000
--- a/chromium/content/browser/renderer_host/gtk_key_bindings_handler.cc
+++ /dev/null
@@ -1,293 +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/renderer_host/gtk_key_bindings_handler.h"
-
-#include <gdk/gdkkeysyms.h>
-
-#include <string>
-
-#include "base/logging.h"
-#include "base/strings/string_util.h"
-#include "content/public/browser/native_web_keyboard_event.h"
-
-namespace content {
-
-GtkKeyBindingsHandler::GtkKeyBindingsHandler(GtkWidget* parent_widget)
- : handler_(CreateNewHandler()) {
- DCHECK(GTK_IS_FIXED(parent_widget));
- // We need add the |handler_| object into gtk widget hierarchy, so that
- // gtk_bindings_activate_event() can find correct display and keymaps from
- // the |handler_| object.
- gtk_fixed_put(GTK_FIXED(parent_widget), handler_.get(), -1, -1);
-}
-
-GtkKeyBindingsHandler::~GtkKeyBindingsHandler() {
- handler_.Destroy();
-}
-
-bool GtkKeyBindingsHandler::Match(const NativeWebKeyboardEvent& wke,
- EditCommands* edit_commands) {
- if (wke.type == blink::WebInputEvent::Char || !wke.os_event)
- return false;
-
- edit_commands_.clear();
- // If this key event matches a predefined key binding, corresponding signal
- // will be emitted.
- gtk_bindings_activate_event(GTK_OBJECT(handler_.get()), &wke.os_event->key);
-
- bool matched = !edit_commands_.empty();
- if (edit_commands)
- edit_commands->swap(edit_commands_);
- return matched;
-}
-
-GtkWidget* GtkKeyBindingsHandler::CreateNewHandler() {
- Handler* handler =
- static_cast<Handler*>(g_object_new(HandlerGetType(), NULL));
-
- handler->owner = this;
-
- // We don't need to show the |handler| object on screen, so set its size to
- // zero.
- gtk_widget_set_size_request(GTK_WIDGET(handler), 0, 0);
-
- // Prevents it from handling any events by itself.
- gtk_widget_set_sensitive(GTK_WIDGET(handler), FALSE);
- gtk_widget_set_events(GTK_WIDGET(handler), 0);
- gtk_widget_set_can_focus(GTK_WIDGET(handler), TRUE);
-
- return GTK_WIDGET(handler);
-}
-
-void GtkKeyBindingsHandler::EditCommandMatched(
- const std::string& name, const std::string& value) {
- edit_commands_.push_back(EditCommand(name, value));
-}
-
-void GtkKeyBindingsHandler::HandlerInit(Handler *self) {
- self->owner = NULL;
-}
-
-void GtkKeyBindingsHandler::HandlerClassInit(HandlerClass *klass) {
- GtkTextViewClass* text_view_class = GTK_TEXT_VIEW_CLASS(klass);
- GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);
-
- // Overrides all virtual methods related to editor key bindings.
- text_view_class->backspace = BackSpace;
- text_view_class->copy_clipboard = CopyClipboard;
- text_view_class->cut_clipboard = CutClipboard;
- text_view_class->delete_from_cursor = DeleteFromCursor;
- text_view_class->insert_at_cursor = InsertAtCursor;
- text_view_class->move_cursor = MoveCursor;
- text_view_class->paste_clipboard = PasteClipboard;
- text_view_class->set_anchor = SetAnchor;
- text_view_class->toggle_overwrite = ToggleOverwrite;
- widget_class->show_help = ShowHelp;
-
- // "move-focus", "move-viewport", "select-all" and "toggle-cursor-visible"
- // have no corresponding virtual methods. Since glib 2.18 (gtk 2.14),
- // g_signal_override_class_handler() is introduced to override a signal
- // handler.
- g_signal_override_class_handler("move-focus",
- G_TYPE_FROM_CLASS(klass),
- G_CALLBACK(MoveFocus));
-
- g_signal_override_class_handler("move-viewport",
- G_TYPE_FROM_CLASS(klass),
- G_CALLBACK(MoveViewport));
-
- g_signal_override_class_handler("select-all",
- G_TYPE_FROM_CLASS(klass),
- G_CALLBACK(SelectAll));
-
- g_signal_override_class_handler("toggle-cursor-visible",
- G_TYPE_FROM_CLASS(klass),
- G_CALLBACK(ToggleCursorVisible));
-}
-
-GType GtkKeyBindingsHandler::HandlerGetType() {
- static volatile gsize type_id_volatile = 0;
- if (g_once_init_enter(&type_id_volatile)) {
- GType type_id = g_type_register_static_simple(
- GTK_TYPE_TEXT_VIEW,
- g_intern_static_string("GtkKeyBindingsHandler"),
- sizeof(HandlerClass),
- reinterpret_cast<GClassInitFunc>(HandlerClassInit),
- sizeof(Handler),
- reinterpret_cast<GInstanceInitFunc>(HandlerInit),
- static_cast<GTypeFlags>(0));
- g_once_init_leave(&type_id_volatile, type_id);
- }
- return type_id_volatile;
-}
-
-GtkKeyBindingsHandler* GtkKeyBindingsHandler::GetHandlerOwner(
- GtkTextView* text_view) {
- Handler* handler = G_TYPE_CHECK_INSTANCE_CAST(
- text_view, HandlerGetType(), Handler);
- DCHECK(handler);
- return handler->owner;
-}
-
-void GtkKeyBindingsHandler::BackSpace(GtkTextView* text_view) {
- GetHandlerOwner(text_view)
- ->EditCommandMatched("DeleteBackward", std::string());
-}
-
-void GtkKeyBindingsHandler::CopyClipboard(GtkTextView* text_view) {
- GetHandlerOwner(text_view)->EditCommandMatched("Copy", std::string());
-}
-
-void GtkKeyBindingsHandler::CutClipboard(GtkTextView* text_view) {
- GetHandlerOwner(text_view)->EditCommandMatched("Cut", std::string());
-}
-
-void GtkKeyBindingsHandler::DeleteFromCursor(
- GtkTextView* text_view, GtkDeleteType type, gint count) {
- if (!count)
- return;
-
- const char *commands[3] = { NULL, NULL, NULL };
- switch (type) {
- case GTK_DELETE_CHARS:
- commands[0] = (count > 0 ? "DeleteForward" : "DeleteBackward");
- break;
- case GTK_DELETE_WORD_ENDS:
- commands[0] = (count > 0 ? "DeleteWordForward" : "DeleteWordBackward");
- break;
- case GTK_DELETE_WORDS:
- if (count > 0) {
- commands[0] = "MoveWordForward";
- commands[1] = "DeleteWordBackward";
- } else {
- commands[0] = "MoveWordBackward";
- commands[1] = "DeleteWordForward";
- }
- break;
- case GTK_DELETE_DISPLAY_LINES:
- commands[0] = "MoveToBeginningOfLine";
- commands[1] = "DeleteToEndOfLine";
- break;
- case GTK_DELETE_DISPLAY_LINE_ENDS:
- commands[0] = (count > 0 ? "DeleteToEndOfLine" :
- "DeleteToBeginningOfLine");
- break;
- case GTK_DELETE_PARAGRAPH_ENDS:
- commands[0] = (count > 0 ? "DeleteToEndOfParagraph" :
- "DeleteToBeginningOfParagraph");
- break;
- case GTK_DELETE_PARAGRAPHS:
- commands[0] = "MoveToBeginningOfParagraph";
- commands[1] = "DeleteToEndOfParagraph";
- break;
- default:
- // GTK_DELETE_WHITESPACE has no corresponding editor command.
- return;
- }
-
- GtkKeyBindingsHandler* owner = GetHandlerOwner(text_view);
- if (count < 0)
- count = -count;
- for (; count > 0; --count) {
- for (const char* const* p = commands; *p; ++p)
- owner->EditCommandMatched(*p, std::string());
- }
-}
-
-void GtkKeyBindingsHandler::InsertAtCursor(GtkTextView* text_view,
- const gchar* str) {
- if (str && *str)
- GetHandlerOwner(text_view)->EditCommandMatched("InsertText", str);
-}
-
-void GtkKeyBindingsHandler::MoveCursor(
- GtkTextView* text_view, GtkMovementStep step, gint count,
- gboolean extend_selection) {
- if (!count)
- return;
-
- std::string command;
- switch (step) {
- case GTK_MOVEMENT_LOGICAL_POSITIONS:
- command = (count > 0 ? "MoveForward" : "MoveBackward");
- break;
- case GTK_MOVEMENT_VISUAL_POSITIONS:
- command = (count > 0 ? "MoveRight" : "MoveLeft");
- break;
- case GTK_MOVEMENT_WORDS:
- command = (count > 0 ? "MoveWordRight" : "MoveWordLeft");
- break;
- case GTK_MOVEMENT_DISPLAY_LINES:
- command = (count > 0 ? "MoveDown" : "MoveUp");
- break;
- case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
- command = (count > 0 ? "MoveToEndOfLine" : "MoveToBeginningOfLine");
- break;
- case GTK_MOVEMENT_PARAGRAPH_ENDS:
- command = (count > 0 ? "MoveToEndOfParagraph" :
- "MoveToBeginningOfParagraph");
- break;
- case GTK_MOVEMENT_PAGES:
- command = (count > 0 ? "MovePageDown" : "MovePageUp");
- break;
- case GTK_MOVEMENT_BUFFER_ENDS:
- command = (count > 0 ? "MoveToEndOfDocument" :
- "MoveToBeginningOfDocument");
- break;
- default:
- // GTK_MOVEMENT_PARAGRAPHS and GTK_MOVEMENT_HORIZONTAL_PAGES have
- // no corresponding editor commands.
- return;
- }
-
- GtkKeyBindingsHandler* owner = GetHandlerOwner(text_view);
- if (extend_selection)
- command.append("AndModifySelection");
- if (count < 0)
- count = -count;
- for (; count > 0; --count)
- owner->EditCommandMatched(command, std::string());
-}
-
-void GtkKeyBindingsHandler::MoveViewport(
- GtkTextView* text_view, GtkScrollStep step, gint count) {
- // Not supported by webkit.
-}
-
-void GtkKeyBindingsHandler::PasteClipboard(GtkTextView* text_view) {
- GetHandlerOwner(text_view)->EditCommandMatched("Paste", std::string());
-}
-
-void GtkKeyBindingsHandler::SelectAll(GtkTextView* text_view, gboolean select) {
- if (select)
- GetHandlerOwner(text_view)->EditCommandMatched("SelectAll", std::string());
- else
- GetHandlerOwner(text_view)->EditCommandMatched("Unselect", std::string());
-}
-
-void GtkKeyBindingsHandler::SetAnchor(GtkTextView* text_view) {
- GetHandlerOwner(text_view)->EditCommandMatched("SetMark", std::string());
-}
-
-void GtkKeyBindingsHandler::ToggleCursorVisible(GtkTextView* text_view) {
- // Not supported by webkit.
-}
-
-void GtkKeyBindingsHandler::ToggleOverwrite(GtkTextView* text_view) {
- // Not supported by webkit.
-}
-
-gboolean GtkKeyBindingsHandler::ShowHelp(GtkWidget* widget,
- GtkWidgetHelpType arg1) {
- // Just for disabling the default handler.
- return FALSE;
-}
-
-void GtkKeyBindingsHandler::MoveFocus(GtkWidget* widget,
- GtkDirectionType arg1) {
- // Just for disabling the default handler.
-}
-
-} // namespace content
diff --git a/chromium/content/browser/renderer_host/gtk_key_bindings_handler.h b/chromium/content/browser/renderer_host/gtk_key_bindings_handler.h
deleted file mode 100644
index 9006f5d0727..00000000000
--- a/chromium/content/browser/renderer_host/gtk_key_bindings_handler.h
+++ /dev/null
@@ -1,132 +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_RENDERER_HOST_GTK_KEY_BINDINGS_HANDLER_H_
-#define CONTENT_BROWSER_RENDERER_HOST_GTK_KEY_BINDINGS_HANDLER_H_
-
-#include <gtk/gtk.h>
-
-#include <string>
-
-#include "content/common/edit_command.h"
-#include "content/common/content_export.h"
-#include "ui/base/gtk/owned_widget_gtk.h"
-
-namespace content {
-struct NativeWebKeyboardEvent;
-
-// This class is a convenience class for handling editor key bindings defined
-// in gtk keyboard theme.
-// In gtk, only GtkEntry and GtkTextView support customizing editor key bindings
-// through keyboard theme. And in gtk keyboard theme definition file, each key
-// binding must be bound to a specific class or object. So existing keyboard
-// themes only define editor key bindings exactly for GtkEntry and GtkTextView.
-// Then, the only way for us to intercept editor key bindings defined in
-// keyboard theme, is to create a GtkEntry or GtkTextView object and call
-// gtk_bindings_activate_event() against it for the key events. If a key event
-// matches a predefined key binding, corresponding signal will be emitted.
-// GtkTextView is used here because it supports more key bindings than GtkEntry,
-// but in order to minimize the side effect of using a GtkTextView object, a new
-// class derived from GtkTextView is used, which overrides all signals related
-// to key bindings, to make sure GtkTextView won't receive them.
-//
-// See third_party/WebKit/Source/WebCore/editing/EditorCommand.cpp for detailed
-// definition of webkit edit commands.
-// See webkit/glue/editor_client_impl.cc for key bindings predefined in our
-// webkit glue.
-class CONTENT_EXPORT GtkKeyBindingsHandler {
- public:
- explicit GtkKeyBindingsHandler(GtkWidget* parent_widget);
- ~GtkKeyBindingsHandler();
-
- // Matches a key event against predefined gtk key bindings, false will be
- // returned if the key event doesn't correspond to a predefined key binding.
- // Edit commands matched with |wke| will be stored in |edit_commands|.
- bool Match(const NativeWebKeyboardEvent& wke,
- EditCommands* edit_commands);
-
- private:
- // Object structure of Handler class, which is derived from GtkTextView.
- struct Handler {
- GtkTextView parent_object;
- GtkKeyBindingsHandler *owner;
- };
-
- // Class structure of Handler class.
- struct HandlerClass {
- GtkTextViewClass parent_class;
- };
-
- // Creates a new instance of Handler class.
- GtkWidget* CreateNewHandler();
-
- // Adds an edit command to the key event.
- void EditCommandMatched(const std::string& name, const std::string& value);
-
- // Initializes Handler structure.
- static void HandlerInit(Handler *self);
-
- // Initializes HandlerClass structure.
- static void HandlerClassInit(HandlerClass *klass);
-
- // Registeres Handler class to GObject type system and return its type id.
- static GType HandlerGetType();
-
- // Gets the GtkKeyBindingsHandler object which owns the Handler object.
- static GtkKeyBindingsHandler* GetHandlerOwner(GtkTextView* text_view);
-
- // Handler of "backspace" signal.
- static void BackSpace(GtkTextView* text_view);
-
- // Handler of "copy-clipboard" signal.
- static void CopyClipboard(GtkTextView* text_view);
-
- // Handler of "cut-clipboard" signal.
- static void CutClipboard(GtkTextView* text_view);
-
- // Handler of "delete-from-cursor" signal.
- static void DeleteFromCursor(GtkTextView* text_view, GtkDeleteType type,
- gint count);
-
- // Handler of "insert-at-cursor" signal.
- static void InsertAtCursor(GtkTextView* text_view, const gchar* str);
-
- // Handler of "move-cursor" signal.
- static void MoveCursor(GtkTextView* text_view, GtkMovementStep step,
- gint count, gboolean extend_selection);
-
- // Handler of "move-viewport" signal.
- static void MoveViewport(GtkTextView* text_view, GtkScrollStep step,
- gint count);
-
- // Handler of "paste-clipboard" signal.
- static void PasteClipboard(GtkTextView* text_view);
-
- // Handler of "select-all" signal.
- static void SelectAll(GtkTextView* text_view, gboolean select);
-
- // Handler of "set-anchor" signal.
- static void SetAnchor(GtkTextView* text_view);
-
- // Handler of "toggle-cursor-visible" signal.
- static void ToggleCursorVisible(GtkTextView* text_view);
-
- // Handler of "toggle-overwrite" signal.
- static void ToggleOverwrite(GtkTextView* text_view);
-
- // Handler of "show-help" signal.
- static gboolean ShowHelp(GtkWidget* widget, GtkWidgetHelpType arg1);
-
- // Handler of "move-focus" signal.
- static void MoveFocus(GtkWidget* widget, GtkDirectionType arg1);
-
- ui::OwnedWidgetGtk handler_;
-
- // Buffer to store the match results.
- EditCommands edit_commands_;
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_RENDERER_HOST_GTK_KEY_BINDINGS_HANDLER_H_
diff --git a/chromium/content/browser/renderer_host/gtk_key_bindings_handler_unittest.cc b/chromium/content/browser/renderer_host/gtk_key_bindings_handler_unittest.cc
deleted file mode 100644
index fed67d5e370..00000000000
--- a/chromium/content/browser/renderer_host/gtk_key_bindings_handler_unittest.cc
+++ /dev/null
@@ -1,226 +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/renderer_host/gtk_key_bindings_handler.h"
-
-#include <gdk/gdkkeysyms.h>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/file_util.h"
-#include "base/logging.h"
-#include "base/path_service.h"
-#include "base/strings/string_util.h"
-#include "content/common/edit_command.h"
-#include "content/public/browser/native_web_keyboard_event.h"
-#include "content/public/common/content_paths.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace content {
-
-class GtkKeyBindingsHandlerTest : public testing::Test {
- protected:
- struct EditCommand {
- const char* name;
- const char* value;
- };
-
- GtkKeyBindingsHandlerTest()
- : window_(gtk_window_new(GTK_WINDOW_TOPLEVEL)),
- handler_(NULL) {
- base::FilePath gtkrc;
- PathService::Get(DIR_TEST_DATA, &gtkrc);
- gtkrc = gtkrc.AppendASCII("gtk_key_bindings_test_gtkrc");
- EXPECT_TRUE(base::PathExists(gtkrc));
-
- gtk_rc_parse(gtkrc.value().c_str());
-
- GtkWidget* fixed = gtk_fixed_new();
- handler_ = new GtkKeyBindingsHandler(fixed);
- gtk_container_add(GTK_CONTAINER(window_), fixed);
- gtk_widget_show(fixed);
- gtk_widget_show(window_);
- }
- virtual ~GtkKeyBindingsHandlerTest() {
- gtk_widget_destroy(window_);
- delete handler_;
- }
-
- NativeWebKeyboardEvent NewNativeWebKeyboardEvent(guint keyval, guint state) {
- GdkKeymap* keymap =
- gdk_keymap_get_for_display(gtk_widget_get_display(window_));
-
- GdkKeymapKey *keys = NULL;
- gint n_keys = 0;
- if (gdk_keymap_get_entries_for_keyval(keymap, keyval, &keys, &n_keys)) {
- GdkEventKey event;
- event.type = GDK_KEY_PRESS;
- event.window = NULL;
- event.send_event = 0;
- event.time = 0;
- event.state = state;
- event.keyval = keyval;
- event.length = 0;
- event.string = NULL;
- event.hardware_keycode = keys[0].keycode;
- event.group = keys[0].group;
- event.is_modifier = 0;
- g_free(keys);
- return NativeWebKeyboardEvent(reinterpret_cast<GdkEvent*>(&event));
- }
- LOG(ERROR) << "Failed to create key event for keyval:" << keyval;
- return NativeWebKeyboardEvent();
- }
-
- void TestKeyBinding(const NativeWebKeyboardEvent& event,
- const EditCommand expected_result[],
- size_t size) {
- EditCommands result;
- ASSERT_TRUE(handler_->Match(event, &result));
- ASSERT_EQ(size, result.size());
- for (size_t i = 0; i < size; ++i) {
- ASSERT_STREQ(expected_result[i].name, result[i].name.c_str());
- ASSERT_STREQ(expected_result[i].value, result[i].value.c_str());
- }
- }
-
- protected:
- GtkWidget* window_;
- GtkKeyBindingsHandler* handler_;
-};
-
-TEST_F(GtkKeyBindingsHandlerTest, MoveCursor) {
- static const EditCommand kEditCommands[] = {
- // "move-cursor" (logical-positions, -2, 0)
- { "MoveBackward", "" },
- { "MoveBackward", "" },
- // "move-cursor" (logical-positions, 2, 0)
- { "MoveForward", "" },
- { "MoveForward", "" },
- // "move-cursor" (visual-positions, -1, 1)
- { "MoveLeftAndModifySelection", "" },
- // "move-cursor" (visual-positions, 1, 1)
- { "MoveRightAndModifySelection", "" },
- // "move-cursor" (words, -1, 0)
- { "MoveWordLeft", "" },
- // "move-cursor" (words, 1, 0)
- { "MoveWordRight", "" },
- // "move-cursor" (display-lines, -1, 0)
- { "MoveUp", "" },
- // "move-cursor" (display-lines, 1, 0)
- { "MoveDown", "" },
- // "move-cursor" (display-line-ends, -1, 0)
- { "MoveToBeginningOfLine", "" },
- // "move-cursor" (display-line-ends, 1, 0)
- { "MoveToEndOfLine", "" },
- // "move-cursor" (paragraph-ends, -1, 0)
- { "MoveToBeginningOfParagraph", "" },
- // "move-cursor" (paragraph-ends, 1, 0)
- { "MoveToEndOfParagraph", "" },
- // "move-cursor" (pages, -1, 0)
- { "MovePageUp", "" },
- // "move-cursor" (pages, 1, 0)
- { "MovePageDown", "" },
- // "move-cursor" (buffer-ends, -1, 0)
- { "MoveToBeginningOfDocument", "" },
- // "move-cursor" (buffer-ends, 1, 0)
- { "MoveToEndOfDocument", "" }
- };
-
- TestKeyBinding(NewNativeWebKeyboardEvent(GDK_1, GDK_CONTROL_MASK),
- kEditCommands, arraysize(kEditCommands));
-}
-
-TEST_F(GtkKeyBindingsHandlerTest, DeleteFromCursor) {
- static const EditCommand kEditCommands[] = {
- // "delete-from-cursor" (chars, -2)
- { "DeleteBackward", "" },
- { "DeleteBackward", "" },
- // "delete-from-cursor" (chars, 2)
- { "DeleteForward", "" },
- { "DeleteForward", "" },
- // "delete-from-cursor" (word-ends, -1)
- { "DeleteWordBackward", "" },
- // "delete-from-cursor" (word-ends, 1)
- { "DeleteWordForward", "" },
- // "delete-from-cursor" (words, -1)
- { "MoveWordBackward", "" },
- { "DeleteWordForward", "" },
- // "delete-from-cursor" (words, 1)
- { "MoveWordForward", "" },
- { "DeleteWordBackward", "" },
- // "delete-from-cursor" (display-lines, -1)
- { "MoveToBeginningOfLine", "" },
- { "DeleteToEndOfLine", "" },
- // "delete-from-cursor" (display-lines, 1)
- { "MoveToBeginningOfLine", "" },
- { "DeleteToEndOfLine", "" },
- // "delete-from-cursor" (display-line-ends, -1)
- { "DeleteToBeginningOfLine", "" },
- // "delete-from-cursor" (display-line-ends, 1)
- { "DeleteToEndOfLine", "" },
- // "delete-from-cursor" (paragraph-ends, -1)
- { "DeleteToBeginningOfParagraph", "" },
- // "delete-from-cursor" (paragraph-ends, 1)
- { "DeleteToEndOfParagraph", "" },
- // "delete-from-cursor" (paragraphs, -1)
- { "MoveToBeginningOfParagraph", "" },
- { "DeleteToEndOfParagraph", "" },
- // "delete-from-cursor" (paragraphs, 1)
- { "MoveToBeginningOfParagraph", "" },
- { "DeleteToEndOfParagraph", "" },
- };
-
- TestKeyBinding(NewNativeWebKeyboardEvent(GDK_2, GDK_CONTROL_MASK),
- kEditCommands, arraysize(kEditCommands));
-}
-
-TEST_F(GtkKeyBindingsHandlerTest, OtherActions) {
- static const EditCommand kBackspace[] = {
- { "DeleteBackward", "" }
- };
- TestKeyBinding(NewNativeWebKeyboardEvent(GDK_3, GDK_CONTROL_MASK),
- kBackspace, arraysize(kBackspace));
-
- static const EditCommand kCopyClipboard[] = {
- { "Copy", "" }
- };
- TestKeyBinding(NewNativeWebKeyboardEvent(GDK_4, GDK_CONTROL_MASK),
- kCopyClipboard, arraysize(kCopyClipboard));
-
- static const EditCommand kCutClipboard[] = {
- { "Cut", "" }
- };
- TestKeyBinding(NewNativeWebKeyboardEvent(GDK_5, GDK_CONTROL_MASK),
- kCutClipboard, arraysize(kCutClipboard));
-
- static const EditCommand kInsertAtCursor[] = {
- { "InsertText", "hello" }
- };
- TestKeyBinding(NewNativeWebKeyboardEvent(GDK_6, GDK_CONTROL_MASK),
- kInsertAtCursor, arraysize(kInsertAtCursor));
-
- static const EditCommand kPasteClipboard[] = {
- { "Paste", "" }
- };
- TestKeyBinding(NewNativeWebKeyboardEvent(GDK_7, GDK_CONTROL_MASK),
- kPasteClipboard, arraysize(kPasteClipboard));
-
- static const EditCommand kSelectAll[] = {
- { "Unselect", "" },
- { "SelectAll", "" }
- };
- TestKeyBinding(NewNativeWebKeyboardEvent(GDK_8, GDK_CONTROL_MASK),
- kSelectAll, arraysize(kSelectAll));
-
- static const EditCommand kSetAnchor[] = {
- { "SetMark", "" }
- };
- TestKeyBinding(NewNativeWebKeyboardEvent(GDK_9, GDK_CONTROL_MASK),
- kSetAnchor, arraysize(kSetAnchor));
-}
-
-} // namespace content
diff --git a/chromium/content/browser/renderer_host/gtk_plugin_container.cc b/chromium/content/browser/renderer_host/gtk_plugin_container.cc
deleted file mode 100644
index 0b83f2ee69f..00000000000
--- a/chromium/content/browser/renderer_host/gtk_plugin_container.cc
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright (c) 2009 The Chromium Authors. 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/renderer_host/gtk_plugin_container.h"
-
-#include <gtk/gtk.h>
-
-#include "base/basictypes.h"
-
-namespace content {
-
-namespace {
-
-// NOTE: This class doesn't have constructors/destructors, it is created
-// through GLib's object management.
-class GtkPluginContainer : public GtkSocket {
- public:
- // Sets the requested size of the widget.
- void set_size(int width, int height) {
- width_ = width;
- height_ = height;
- }
-
- // Casts a widget into a GtkPluginContainer, after checking the type.
- template <class T>
- static GtkPluginContainer *CastChecked(T *instance) {
- return G_TYPE_CHECK_INSTANCE_CAST(instance, GetType(), GtkPluginContainer);
- }
-
- // Create and register our custom container type with GTK.
- static GType GetType() {
- static GType type = 0; // We only want to register our type once.
- if (!type) {
- static const GTypeInfo info = {
- sizeof(GtkSocketClass),
- NULL, NULL,
- static_cast<GClassInitFunc>(&ClassInit),
- NULL, NULL,
- sizeof(GtkPluginContainer),
- 0, &InstanceInit,
- };
- type = g_type_register_static(GTK_TYPE_SOCKET,
- "GtkPluginContainer",
- &info,
- static_cast<GTypeFlags>(0));
- }
- return type;
- }
-
- // Implementation of the class initializer.
- static void ClassInit(gpointer klass, gpointer class_data_unusued) {
- GtkWidgetClass* widget_class = reinterpret_cast<GtkWidgetClass*>(klass);
- widget_class->size_request = &HandleSizeRequest;
- }
-
- // Implementation of the instance initializer (constructor).
- static void InstanceInit(GTypeInstance *instance, gpointer klass) {
- GtkPluginContainer *container = CastChecked(instance);
- container->set_size(0, 0);
- }
-
- // Report our allocation size during size requisition.
- static void HandleSizeRequest(GtkWidget* widget,
- GtkRequisition* requisition) {
- GtkPluginContainer *container = CastChecked(widget);
- requisition->width = container->width_;
- requisition->height = container->height_;
- }
-
- int width_;
- int height_;
- DISALLOW_IMPLICIT_CONSTRUCTORS(GtkPluginContainer);
-};
-
-} // namespace
-
-// Create a new instance of our GTK widget object.
-GtkWidget* gtk_plugin_container_new() {
- return GTK_WIDGET(g_object_new(GtkPluginContainer::GetType(), NULL));
-}
-
-void gtk_plugin_container_set_size(GtkWidget *widget, int width, int height) {
- GtkPluginContainer::CastChecked(widget)->set_size(width, height);
- // Signal the parent that the size request has changed.
- gtk_widget_queue_resize_no_redraw(widget);
-}
-
-} // namespace content
diff --git a/chromium/content/browser/renderer_host/gtk_plugin_container.h b/chromium/content/browser/renderer_host/gtk_plugin_container.h
deleted file mode 100644
index 1713cd4bb5a..00000000000
--- a/chromium/content/browser/renderer_host/gtk_plugin_container.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (c) 2009 The Chromium Authors. 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_RENDERER_HOST_GTK_PLUGIN_CONTAINER_H_
-#define CONTENT_BROWSER_RENDERER_HOST_GTK_PLUGIN_CONTAINER_H_
-
-// Windowed plugins are embedded via XEmbed, which is implemented by
-// GtkPlug/GtkSocket. But we want to control sizing and positioning
-// directly, so we need a subclass of GtkSocket that sidesteps the
-// size_request handler.
-//
-// The custom size_request handler just reports the size set by
-// gtk_plugin_container_set_size.
-
-typedef struct _GtkWidget GtkWidget;
-
-namespace content {
-
-// Return a new GtkPluginContainer.
-// Intentionally GTK-style here since we're creating a custom GTK widget.
-// This is a GtkSocket subclass; see its documentation for available methods.
-GtkWidget* gtk_plugin_container_new();
-
-// Sets the size of the GtkPluginContainer.
-void gtk_plugin_container_set_size(GtkWidget *widget, int width, int height);
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_RENDERER_HOST_GTK_PLUGIN_CONTAINER_H_
diff --git a/chromium/content/browser/renderer_host/gtk_plugin_container_manager.cc b/chromium/content/browser/renderer_host/gtk_plugin_container_manager.cc
deleted file mode 100644
index 861491111b5..00000000000
--- a/chromium/content/browser/renderer_host/gtk_plugin_container_manager.cc
+++ /dev/null
@@ -1,161 +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/renderer_host/gtk_plugin_container_manager.h"
-
-#include <gtk/gtk.h>
-
-#include "base/logging.h"
-#include "content/browser/renderer_host/gtk_plugin_container.h"
-#include "content/common/webplugin_geometry.h"
-#include "ui/gfx/gtk_compat.h"
-#include "ui/gfx/gtk_util.h"
-
-namespace content {
-
-GtkPluginContainerManager::GtkPluginContainerManager() : host_widget_(NULL) {}
-
-GtkPluginContainerManager::~GtkPluginContainerManager() {}
-
-GtkWidget* GtkPluginContainerManager::CreatePluginContainer(
- gfx::PluginWindowHandle id) {
- DCHECK(host_widget_);
- GtkWidget *widget = gtk_plugin_container_new();
- plugin_window_to_widget_map_.insert(std::make_pair(id, widget));
-
- // The Realize callback is responsible for adding the plug into the socket.
- // The reason is 2-fold:
- // - the plug can't be added until the socket is realized, but this may not
- // happen until the socket is attached to a top-level window, which isn't the
- // case for background tabs.
- // - when dragging tabs, the socket gets unrealized, which breaks the XEMBED
- // connection. We need to make it again when the tab is reattached, and the
- // socket gets realized again.
- //
- // Note, the RealizeCallback relies on the plugin_window_to_widget_map_ to
- // have the mapping.
- g_signal_connect(widget, "realize",
- G_CALLBACK(RealizeCallback), this);
-
- // Don't destroy the widget when the plug is removed.
- g_signal_connect(widget, "plug-removed",
- G_CALLBACK(gtk_true), NULL);
-
- gtk_container_add(GTK_CONTAINER(host_widget_), widget);
- gtk_widget_show(widget);
-
- return widget;
-}
-
-void GtkPluginContainerManager::DestroyPluginContainer(
- gfx::PluginWindowHandle id) {
- DCHECK(host_widget_);
- GtkWidget* widget = MapIDToWidget(id);
- if (widget)
- gtk_widget_destroy(widget);
-
- plugin_window_to_widget_map_.erase(id);
-}
-
-void GtkPluginContainerManager::MovePluginContainer(
- const WebPluginGeometry& move) {
- DCHECK(host_widget_);
- GtkWidget *widget = MapIDToWidget(move.window);
- if (!widget)
- return;
-
- DCHECK(gtk_widget_get_has_window(widget));
-
- if (!move.visible) {
- gtk_widget_hide(widget);
- return;
- }
-
- gtk_widget_show(widget);
-
- if (!move.rects_valid)
- return;
-
- // TODO(piman): if the widget hasn't been realized (e.g. the tab has been
- // torn off and the parent gtk widget has been detached from the hierarchy),
- // we lose the cutout information.
- if (gtk_widget_get_realized(widget)) {
- GdkRectangle clip_rect = move.clip_rect.ToGdkRectangle();
- GdkRegion* clip_region = gdk_region_rectangle(&clip_rect);
- gfx::SubtractRectanglesFromRegion(clip_region, move.cutout_rects);
- gdk_window_shape_combine_region(gtk_widget_get_window(widget),
- clip_region, 0, 0);
- gdk_region_destroy(clip_region);
- }
-
- // Update the window position. Resizing is handled by WebPluginDelegate.
- // TODO(deanm): Verify that we only need to move and not resize.
- // TODO(evanm): we should cache the last shape and position and skip all
- // of this business in the common case where nothing has changed.
- int current_x, current_y;
-
- // Until the above TODO is resolved, we can grab the last position
- // off of the GtkFixed with a bit of hackery.
- GValue value = {0};
- g_value_init(&value, G_TYPE_INT);
- gtk_container_child_get_property(GTK_CONTAINER(host_widget_), widget,
- "x", &value);
- current_x = g_value_get_int(&value);
- gtk_container_child_get_property(GTK_CONTAINER(host_widget_), widget,
- "y", &value);
- current_y = g_value_get_int(&value);
- g_value_unset(&value);
-
- if (move.window_rect.x() != current_x ||
- move.window_rect.y() != current_y) {
- // Calling gtk_fixed_move unnecessarily is a no-no, as it causes the
- // parent window to repaint!
- gtk_fixed_move(GTK_FIXED(host_widget_),
- widget,
- move.window_rect.x(),
- move.window_rect.y());
- }
-
- gtk_plugin_container_set_size(widget,
- move.window_rect.width(),
- move.window_rect.height());
-}
-
-GtkWidget* GtkPluginContainerManager::MapIDToWidget(
- gfx::PluginWindowHandle id) {
- PluginWindowToWidgetMap::const_iterator i =
- plugin_window_to_widget_map_.find(id);
- if (i != plugin_window_to_widget_map_.end())
- return i->second;
-
- LOG(ERROR) << "Request for widget host for unknown window id " << id;
-
- return NULL;
-}
-
-gfx::PluginWindowHandle GtkPluginContainerManager::MapWidgetToID(
- GtkWidget* widget) {
- for (PluginWindowToWidgetMap::const_iterator i =
- plugin_window_to_widget_map_.begin();
- i != plugin_window_to_widget_map_.end(); ++i) {
- if (i->second == widget)
- return i->first;
- }
-
- LOG(ERROR) << "Request for id for unknown widget";
- return 0;
-}
-
-// static
-void GtkPluginContainerManager::RealizeCallback(GtkWidget* widget,
- void* user_data) {
- GtkPluginContainerManager* plugin_container_manager =
- static_cast<GtkPluginContainerManager*>(user_data);
-
- gfx::PluginWindowHandle id = plugin_container_manager->MapWidgetToID(widget);
- if (id)
- gtk_socket_add_id(GTK_SOCKET(widget), id);
-}
-
-} // namespace content
diff --git a/chromium/content/browser/renderer_host/gtk_plugin_container_manager.h b/chromium/content/browser/renderer_host/gtk_plugin_container_manager.h
deleted file mode 100644
index bd0e7c1017a..00000000000
--- a/chromium/content/browser/renderer_host/gtk_plugin_container_manager.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_RENDERER_HOST_GTK_PLUGIN_CONTAINER_MANAGER_H_
-#define CONTENT_BROWSER_RENDERER_HOST_GTK_PLUGIN_CONTAINER_MANAGER_H_
-
-#include <gtk/gtk.h>
-#include <map>
-
-#include "ui/gfx/native_widget_types.h"
-
-typedef struct _GtkWidget GtkWidget;
-
-namespace content {
-struct WebPluginGeometry;
-
-// Helper class that creates and manages plugin containers (GtkSocket).
-class GtkPluginContainerManager {
- public:
- GtkPluginContainerManager();
- ~GtkPluginContainerManager();
-
- // Sets the widget that will host the plugin containers. Must be a GtkFixed.
- void set_host_widget(GtkWidget *widget) { host_widget_ = widget; }
-
- // Creates a new plugin container, for a given plugin XID.
- GtkWidget* CreatePluginContainer(gfx::PluginWindowHandle id);
-
- // Destroys a plugin container, given the plugin XID.
- void DestroyPluginContainer(gfx::PluginWindowHandle id);
-
- // Takes an update from WebKit about a plugin's position and side and moves
- // the plugin accordingly.
- void MovePluginContainer(const WebPluginGeometry& move);
-
- private:
- // Maps a plugin XID to the corresponding container widget.
- GtkWidget* MapIDToWidget(gfx::PluginWindowHandle id);
-
- // Maps a container widget to the corresponding plugin XID.
- gfx::PluginWindowHandle MapWidgetToID(GtkWidget* widget);
-
- // Callback for when the plugin container gets realized, at which point it
- // plugs the plugin XID.
- static void RealizeCallback(GtkWidget *widget, void *user_data);
-
- // Parent of the plugin containers.
- GtkWidget* host_widget_;
-
- // A map that associates plugin containers to the plugin XID.
- typedef std::map<gfx::PluginWindowHandle, GtkWidget*> PluginWindowToWidgetMap;
- PluginWindowToWidgetMap plugin_window_to_widget_map_;
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_RENDERER_HOST_GTK_PLUGIN_CONTAINER_MANAGER_H_
diff --git a/chromium/content/browser/renderer_host/gtk_window_utils.cc b/chromium/content/browser/renderer_host/gtk_window_utils.cc
deleted file mode 100644
index 240c59d2882..00000000000
--- a/chromium/content/browser/renderer_host/gtk_window_utils.cc
+++ /dev/null
@@ -1,89 +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/renderer_host/gtk_window_utils.h"
-
-#include <X11/Xlib.h>
-#include <gdk/gdkx.h>
-#include <gtk/gtk.h>
-
-#include <vector>
-
-#include "third_party/WebKit/public/platform/WebScreenInfo.h"
-#include "ui/base/x/x11_util.h"
-#include "ui/gfx/gtk_compat.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/x/x11_types.h"
-
-namespace content {
-
-namespace {
-
-// Returns the region of |window|'s desktop that isn't occupied by docks or
-// panels. Returns an empty rect on failure.
-gfx::Rect GetWorkArea(Window window) {
- Window root = ui::GetX11RootWindow();
- int desktop = -1;
- if (!ui::GetIntProperty(window, "_NET_WM_DESKTOP", &desktop)) {
- // Hack for ion3: _NET_WM_DESKTOP doesn't get set on individual windows, but
- // _NET_CURRENT_DESKTOP and _NET_WORKAREA do get set.
- ui::GetIntProperty(root, "_NET_CURRENT_DESKTOP", &desktop);
- }
- if (desktop < 0)
- return gfx::Rect();
-
- std::vector<int> property;
- if (!ui::GetIntArrayProperty(root, "_NET_WORKAREA", &property))
- return gfx::Rect();
-
- size_t start_index = 4 * desktop;
- if (property.size() < start_index + 4)
- return gfx::Rect();
-
- return gfx::Rect(property[start_index], property[start_index + 1],
- property[start_index + 2], property[start_index + 3]);
-}
-
-blink::WebScreenInfo GetScreenInfo(XDisplay* display, int screenNumber) {
- // XDisplayWidth() and XDisplayHeight() return cached values. To ensure that
- // we return the correct dimensions after the screen is resized, query the
- // root window's geometry each time.
- Window root = RootWindow(display, screenNumber);
- Window root_ret;
- int x, y;
- unsigned int width, height, border, depth;
- XGetGeometry(
- display, root, &root_ret, &x, &y, &width, &height, &border, &depth);
-
- blink::WebScreenInfo results;
- results.depthPerComponent = 8; // Assume 8bpp, which is usually right.
- results.depth = depth;
- results.isMonochrome = depth == 1;
- results.rect = blink::WebRect(x, y, width, height);
- results.availableRect = results.rect;
- return results;
-}
-
-} // namespace
-
-void GetScreenInfoFromNativeWindow(
- GdkWindow* gdk_window, blink::WebScreenInfo* results) {
- GdkScreen* screen = gdk_window_get_screen(gdk_window);
- *results = GetScreenInfo(gdk_x11_drawable_get_xdisplay(gdk_window),
- gdk_x11_screen_get_screen_number(screen));
-
- int monitor_number = gdk_screen_get_monitor_at_window(screen, gdk_window);
- GdkRectangle monitor_rect;
- gdk_screen_get_monitor_geometry(screen, monitor_number, &monitor_rect);
- results->rect = blink::WebRect(monitor_rect.x, monitor_rect.y,
- monitor_rect.width, monitor_rect.height);
-
- gfx::Rect available_rect = results->rect;
- gfx::Rect work_area = GetWorkArea(GDK_WINDOW_XID(gdk_window));
- if (!work_area.IsEmpty())
- available_rect.Intersect(work_area);
- results->availableRect = available_rect;
-}
-
-} // namespace content
diff --git a/chromium/content/browser/renderer_host/gtk_window_utils.h b/chromium/content/browser/renderer_host/gtk_window_utils.h
deleted file mode 100644
index 93f1d68da68..00000000000
--- a/chromium/content/browser/renderer_host/gtk_window_utils.h
+++ /dev/null
@@ -1,23 +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_RENDERER_HOST_GTK_WINDOW_UTILS_H_
-#define CONTENT_BROWSER_RENDERER_HOST_GTK_WINDOW_UTILS_H_
-
-#include "content/common/content_export.h"
-
-typedef struct _GdkDrawable GdkWindow;
-
-namespace blink {
-struct WebScreenInfo;
-}
-
-namespace content {
-
-CONTENT_EXPORT void GetScreenInfoFromNativeWindow(
- GdkWindow* gdk_window, blink::WebScreenInfo* results);
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_RENDERER_HOST_GTK_WINDOW_UTILS_H_
diff --git a/chromium/content/browser/renderer_host/image_transport_factory_android.cc b/chromium/content/browser/renderer_host/image_transport_factory_android.cc
index 47ff2c5bb9c..cf3ff8b1b7c 100644
--- a/chromium/content/browser/renderer_host/image_transport_factory_android.cc
+++ b/chromium/content/browser/renderer_host/image_transport_factory_android.cc
@@ -10,6 +10,7 @@
#include "content/common/gpu/client/gl_helper.h"
#include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h"
#include "content/common/gpu/gpu_process_launch_causes.h"
+#include "gpu/command_buffer/client/gles2_implementation.h"
#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "ui/gfx/android/device_display_info.h"
@@ -37,15 +38,6 @@ class CmdBufferImageTransportFactory : public ImageTransportFactoryAndroid {
CmdBufferImageTransportFactory();
virtual ~CmdBufferImageTransportFactory();
- virtual uint32_t InsertSyncPoint() OVERRIDE;
- virtual void WaitSyncPoint(uint32_t sync_point) OVERRIDE;
- virtual uint32_t CreateTexture() OVERRIDE;
- virtual void DeleteTexture(uint32_t id) OVERRIDE;
- virtual void AcquireTexture(
- uint32 texture_id, const signed char* mailbox_name) OVERRIDE;
- virtual blink::WebGraphicsContext3D* GetContext3D() OVERRIDE {
- return context_.get();
- }
virtual GLHelper* GetGLHelper() OVERRIDE;
virtual uint32 GetChannelID() OVERRIDE {
return BrowserGpuChannelHostFactory::instance()->GetGpuChannelId();
@@ -81,13 +73,15 @@ CmdBufferImageTransportFactory::CmdBufferImageTransportFactory() {
3 * full_screen_texture_size_in_bytes, kDefaultMaxTransferBufferSize);
limits.mapped_memory_reclaim_limit =
WebGraphicsContext3DCommandBufferImpl::kNoLimit;
+ bool lose_context_when_out_of_memory = false;
context_.reset(
new WebGraphicsContext3DCommandBufferImpl(0, // offscreen
url,
gpu_channel_host.get(),
attrs,
- false,
- limits));
+ lose_context_when_out_of_memory,
+ limits,
+ NULL));
context_->setContextLostCallback(context_lost_listener_.get());
if (context_->makeContextCurrent())
context_->pushGroupMarkerEXT(
@@ -99,52 +93,9 @@ CmdBufferImageTransportFactory::~CmdBufferImageTransportFactory() {
context_->setContextLostCallback(NULL);
}
-uint32_t CmdBufferImageTransportFactory::InsertSyncPoint() {
- if (!context_->makeContextCurrent()) {
- LOG(ERROR) << "Failed to make helper context current.";
- return 0;
- }
- return context_->insertSyncPoint();
-}
-
-void CmdBufferImageTransportFactory::WaitSyncPoint(uint32_t sync_point) {
- if (!context_->makeContextCurrent()) {
- LOG(ERROR) << "Failed to make helper context current.";
- return;
- }
- context_->waitSyncPoint(sync_point);
-}
-
-uint32_t CmdBufferImageTransportFactory::CreateTexture() {
- if (!context_->makeContextCurrent()) {
- LOG(ERROR) << "Failed to make helper context current.";
- return false;
- }
- return context_->createTexture();
-}
-
-void CmdBufferImageTransportFactory::DeleteTexture(uint32_t id) {
- if (!context_->makeContextCurrent()) {
- LOG(ERROR) << "Failed to make helper context current.";
- return;
- }
- context_->deleteTexture(id);
-}
-
-void CmdBufferImageTransportFactory::AcquireTexture(
- uint32 texture_id, const signed char* mailbox_name) {
- if (!context_->makeContextCurrent()) {
- LOG(ERROR) << "Failed to make helper context current.";
- return;
- }
- context_->bindTexture(GL_TEXTURE_2D, texture_id);
- context_->consumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox_name);
- context_->shallowFlushCHROMIUM();
-}
-
GLHelper* CmdBufferImageTransportFactory::GetGLHelper() {
if (!gl_helper_)
- gl_helper_.reset(new GLHelper(context_.get(),
+ gl_helper_.reset(new GLHelper(context_->GetImplementation(),
context_->GetContextSupport()));
return gl_helper_.get();
diff --git a/chromium/content/browser/renderer_host/image_transport_factory_android.h b/chromium/content/browser/renderer_host/image_transport_factory_android.h
index 54434fc103f..3be7a678e69 100644
--- a/chromium/content/browser/renderer_host/image_transport_factory_android.h
+++ b/chromium/content/browser/renderer_host/image_transport_factory_android.h
@@ -12,8 +12,10 @@ namespace gfx {
class GLShareGroup;
}
-namespace blink {
-class WebGraphicsContext3D;
+namespace gpu {
+namespace gles2 {
+class GLES2Interface;
+}
}
namespace content {
@@ -32,14 +34,6 @@ class ImageTransportFactoryAndroid {
static ImageTransportFactoryAndroid* GetInstance();
- virtual uint32_t InsertSyncPoint() = 0;
- virtual void WaitSyncPoint(uint32_t sync_point) = 0;
- virtual uint32_t CreateTexture() = 0;
- virtual void DeleteTexture(uint32_t id) = 0;
- virtual void AcquireTexture(
- uint32 texture_id, const signed char* mailbox_name) = 0;
-
- virtual blink::WebGraphicsContext3D* GetContext3D() = 0;
virtual GLHelper* GetGLHelper() = 0;
virtual uint32 GetChannelID() = 0;
diff --git a/chromium/content/browser/renderer_host/ime_adapter_android.cc b/chromium/content/browser/renderer_host/ime_adapter_android.cc
index 92d972f22c6..afbc43400f7 100644
--- a/chromium/content/browser/renderer_host/ime_adapter_android.cc
+++ b/chromium/content/browser/renderer_host/ime_adapter_android.cc
@@ -4,17 +4,27 @@
#include "content/browser/renderer_host/ime_adapter_android.h"
+#include <algorithm>
#include <android/input.h>
+#include <vector>
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
+#include "content/browser/frame_host/frame_tree.h"
+#include "content/browser/frame_host/frame_tree_node.h"
+#include "content/browser/frame_host/render_frame_host_impl.h"
+#include "content/browser/renderer_host/render_view_host_delegate.h"
+#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_view_android.h"
+#include "content/common/frame_messages.h"
#include "content/common/view_messages.h"
+#include "content/public/browser/browser_thread.h"
#include "content/public/browser/native_web_keyboard_event.h"
+#include "content/public/browser/web_contents.h"
#include "jni/ImeAdapter_jni.h"
#include "third_party/WebKit/public/web/WebCompositionUnderline.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
@@ -46,7 +56,7 @@ NativeWebKeyboardEvent NativeWebKeyboardEventFromKeyEvent(
else if (action == AKEY_EVENT_ACTION_UP)
type = blink::WebInputEvent::KeyUp;
return NativeWebKeyboardEvent(java_key_event, type, modifiers,
- time_ms, key_code, unicode_char, is_system_key);
+ time_ms / 1000.0, key_code, unicode_char, is_system_key);
}
} // anonymous namespace
@@ -79,6 +89,48 @@ bool RegisterImeAdapter(JNIEnv* env) {
return true;
}
+// Callback from Java to convert BackgroundColorSpan data to a
+// blink::WebCompositionUnderline instance, and append it to |underlines_ptr|.
+void AppendBackgroundColorSpan(JNIEnv*,
+ jclass,
+ jlong underlines_ptr,
+ jint start,
+ jint end,
+ jint background_color) {
+ DCHECK(start >= 0);
+ DCHECK(end >= 0);
+ // Do not check |background_color|.
+ std::vector<blink::WebCompositionUnderline>* underlines =
+ reinterpret_cast<std::vector<blink::WebCompositionUnderline>*>(
+ underlines_ptr);
+ underlines->push_back(
+ blink::WebCompositionUnderline(static_cast<unsigned>(start),
+ static_cast<unsigned>(end),
+ SK_ColorTRANSPARENT,
+ false,
+ static_cast<unsigned>(background_color)));
+}
+
+// Callback from Java to convert UnderlineSpan data to a
+// blink::WebCompositionUnderline instance, and append it to |underlines_ptr|.
+void AppendUnderlineSpan(JNIEnv*,
+ jclass,
+ jlong underlines_ptr,
+ jint start,
+ jint end) {
+ DCHECK(start >= 0);
+ DCHECK(end >= 0);
+ std::vector<blink::WebCompositionUnderline>* underlines =
+ reinterpret_cast<std::vector<blink::WebCompositionUnderline>*>(
+ underlines_ptr);
+ underlines->push_back(
+ blink::WebCompositionUnderline(static_cast<unsigned>(start),
+ static_cast<unsigned>(end),
+ SK_ColorBLACK,
+ false,
+ SK_ColorTRANSPARENT));
+}
+
ImeAdapterAndroid::ImeAdapterAndroid(RenderWidgetHostViewAndroid* rwhva)
: rwhva_(rwhva) {
}
@@ -122,7 +174,7 @@ bool ImeAdapterAndroid::SendKeyEvent(JNIEnv* env, jobject,
// Send a Char event, but without an os_event since we don't want to
// roundtrip back to java such synthetic event.
NativeWebKeyboardEvent char_event(blink::WebInputEvent::Char, modifiers,
- time_ms, key_code, unicode_char,
+ time_ms / 1000.0, key_code, unicode_char,
is_system_key);
char_event.skip_in_browser = key_down_text_insertion;
rwhva_->SendKeyEvent(char_event);
@@ -130,17 +182,32 @@ bool ImeAdapterAndroid::SendKeyEvent(JNIEnv* env, jobject,
return true;
}
-void ImeAdapterAndroid::SetComposingText(JNIEnv* env, jobject, jstring text,
+void ImeAdapterAndroid::SetComposingText(JNIEnv* env,
+ jobject obj,
+ jobject text,
+ jstring text_str,
int new_cursor_pos) {
RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl();
if (!rwhi)
return;
- base::string16 text16 = ConvertJavaStringToUTF16(env, text);
+ base::string16 text16 = ConvertJavaStringToUTF16(env, text_str);
+
std::vector<blink::WebCompositionUnderline> underlines;
- underlines.push_back(
- blink::WebCompositionUnderline(0, text16.length(), SK_ColorBLACK,
- false));
+ // Iterate over spans in |text|, dispatch those that we care about (e.g.,
+ // BackgroundColorSpan) to a matching callback (e.g.,
+ // AppendBackgroundColorSpan()), and populate |underlines|.
+ Java_ImeAdapter_populateUnderlinesFromSpans(
+ env, obj, text, reinterpret_cast<jlong>(&underlines));
+
+ // Default to plain underline if we didn't find any span that we care about.
+ if (underlines.empty()) {
+ underlines.push_back(blink::WebCompositionUnderline(
+ 0, text16.length(), SK_ColorBLACK, false, SK_ColorTRANSPARENT));
+ }
+ // Sort spans by |.startOffset|.
+ std::sort(underlines.begin(), underlines.end());
+
// new_cursor_position is as described in the Android API for
// InputConnection#setComposingText, whereas the parameters for
// ImeSetComposition are relative to the start of the composition.
@@ -150,12 +217,12 @@ void ImeAdapterAndroid::SetComposingText(JNIEnv* env, jobject, jstring text,
rwhi->ImeSetComposition(text16, underlines, new_cursor_pos, new_cursor_pos);
}
-void ImeAdapterAndroid::CommitText(JNIEnv* env, jobject, jstring text) {
+void ImeAdapterAndroid::CommitText(JNIEnv* env, jobject, jstring text_str) {
RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl();
if (!rwhi)
return;
- base::string16 text16 = ConvertJavaStringToUTF16(env, text);
+ base::string16 text16 = ConvertJavaStringToUTF16(env, text_str);
rwhi->ImeConfirmComposition(text16, gfx::Range::InvalidRange(), false);
}
@@ -179,78 +246,76 @@ void ImeAdapterAndroid::CancelComposition() {
Java_ImeAdapter_cancelComposition(AttachCurrentThread(), obj.obj());
}
+void ImeAdapterAndroid::FocusedNodeChanged(bool is_editable_node) {
+ base::android::ScopedJavaLocalRef<jobject> obj =
+ java_ime_adapter_.get(AttachCurrentThread());
+ if (!obj.is_null()) {
+ Java_ImeAdapter_focusedNodeChanged(AttachCurrentThread(),
+ obj.obj(),
+ is_editable_node);
+ }
+}
+
void ImeAdapterAndroid::SetEditableSelectionOffsets(JNIEnv*, jobject,
int start, int end) {
- RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl();
- if (!rwhi)
+ RenderFrameHost* rfh = GetFocusedFrame();
+ if (!rfh)
return;
- rwhi->Send(new ViewMsg_SetEditableSelectionOffsets(rwhi->GetRoutingID(),
+ rfh->Send(new FrameMsg_SetEditableSelectionOffsets(rfh->GetRoutingID(),
start, end));
}
void ImeAdapterAndroid::SetComposingRegion(JNIEnv*, jobject,
int start, int end) {
- RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl();
- if (!rwhi)
+ RenderFrameHost* rfh = GetFocusedFrame();
+ if (!rfh)
return;
std::vector<blink::WebCompositionUnderline> underlines;
- underlines.push_back(
- blink::WebCompositionUnderline(0, end - start, SK_ColorBLACK, false));
+ underlines.push_back(blink::WebCompositionUnderline(
+ 0, end - start, SK_ColorBLACK, false, SK_ColorTRANSPARENT));
- rwhi->Send(new ViewMsg_SetCompositionFromExistingText(
- rwhi->GetRoutingID(), start, end, underlines));
+ rfh->Send(new FrameMsg_SetCompositionFromExistingText(
+ rfh->GetRoutingID(), start, end, underlines));
}
void ImeAdapterAndroid::DeleteSurroundingText(JNIEnv*, jobject,
int before, int after) {
- RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl();
- if (!rwhi)
- return;
-
- rwhi->Send(new ViewMsg_ExtendSelectionAndDelete(rwhi->GetRoutingID(),
- before, after));
+ RenderFrameHostImpl* rfh =
+ static_cast<RenderFrameHostImpl*>(GetFocusedFrame());
+ if (rfh)
+ rfh->ExtendSelectionAndDelete(before, after);
}
void ImeAdapterAndroid::Unselect(JNIEnv* env, jobject) {
- RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl();
- if (!rwhi)
- return;
-
- rwhi->Unselect();
+ WebContents* wc = GetWebContents();
+ if (wc)
+ wc->Unselect();
}
void ImeAdapterAndroid::SelectAll(JNIEnv* env, jobject) {
- RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl();
- if (!rwhi)
- return;
-
- rwhi->SelectAll();
+ WebContents* wc = GetWebContents();
+ if (wc)
+ wc->SelectAll();
}
void ImeAdapterAndroid::Cut(JNIEnv* env, jobject) {
- RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl();
- if (!rwhi)
- return;
-
- rwhi->Cut();
+ WebContents* wc = GetWebContents();
+ if (wc)
+ wc->Cut();
}
void ImeAdapterAndroid::Copy(JNIEnv* env, jobject) {
- RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl();
- if (!rwhi)
- return;
-
- rwhi->Copy();
+ WebContents* wc = GetWebContents();
+ if (wc)
+ wc->Copy();
}
void ImeAdapterAndroid::Paste(JNIEnv* env, jobject) {
- RenderWidgetHostImpl* rwhi = GetRenderWidgetHostImpl();
- if (!rwhi)
- return;
-
- rwhi->Paste();
+ WebContents* wc = GetWebContents();
+ if (wc)
+ wc->Paste();
}
void ImeAdapterAndroid::ResetImeAdapter(JNIEnv* env, jobject) {
@@ -258,6 +323,7 @@ void ImeAdapterAndroid::ResetImeAdapter(JNIEnv* env, jobject) {
}
RenderWidgetHostImpl* ImeAdapterAndroid::GetRenderWidgetHostImpl() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(rwhva_);
RenderWidgetHost* rwh = rwhva_->GetRenderWidgetHost();
if (!rwh)
@@ -266,4 +332,28 @@ RenderWidgetHostImpl* ImeAdapterAndroid::GetRenderWidgetHostImpl() {
return RenderWidgetHostImpl::From(rwh);
}
+RenderFrameHost* ImeAdapterAndroid::GetFocusedFrame() {
+ RenderWidgetHostImpl* rwh = GetRenderWidgetHostImpl();
+ if (!rwh)
+ return NULL;
+ if (!rwh->IsRenderView())
+ return NULL;
+ RenderViewHost* rvh = RenderViewHost::From(rwh);
+ FrameTreeNode* focused_frame =
+ rvh->GetDelegate()->GetFrameTree()->GetFocusedFrame();
+ if (!focused_frame)
+ return NULL;
+
+ return focused_frame->current_frame_host();
+}
+
+WebContents* ImeAdapterAndroid::GetWebContents() {
+ RenderWidgetHostImpl* rwh = GetRenderWidgetHostImpl();
+ if (!rwh)
+ return NULL;
+ if (!rwh->IsRenderView())
+ return NULL;
+ return WebContents::FromRenderViewHost(RenderViewHost::From(rwh));
+}
+
} // namespace content
diff --git a/chromium/content/browser/renderer_host/ime_adapter_android.h b/chromium/content/browser/renderer_host/ime_adapter_android.h
index ce3c335c41b..a23696da37d 100644
--- a/chromium/content/browser/renderer_host/ime_adapter_android.h
+++ b/chromium/content/browser/renderer_host/ime_adapter_android.h
@@ -7,12 +7,14 @@
#include <jni.h>
-#include "base/android/jni_helper.h"
+#include "base/android/jni_weak_ref.h"
namespace content {
+class RenderFrameHost;
class RenderWidgetHostImpl;
class RenderWidgetHostViewAndroid;
+class WebContents;
struct NativeWebKeyboardEvent;
// This class is in charge of dispatching key events from the java side
@@ -40,8 +42,12 @@ class ImeAdapterAndroid {
long timestamp_ms,
int native_key_code,
int unicode_char);
- void SetComposingText(JNIEnv*, jobject, jstring text, int new_cursor_pos);
- void CommitText(JNIEnv*, jobject, jstring text);
+ void SetComposingText(JNIEnv*,
+ jobject obj,
+ jobject text,
+ jstring text_str,
+ int new_cursor_pos);
+ void CommitText(JNIEnv*, jobject, jstring text_str);
void FinishComposingText(JNIEnv* env, jobject);
void AttachImeAdapter(JNIEnv*, jobject java_object);
void SetEditableSelectionOffsets(JNIEnv*, jobject, int start, int end);
@@ -56,9 +62,12 @@ class ImeAdapterAndroid {
// Called from native -> java
void CancelComposition();
+ void FocusedNodeChanged(bool is_editable_node);
private:
RenderWidgetHostImpl* GetRenderWidgetHostImpl();
+ RenderFrameHost* GetFocusedFrame();
+ WebContents* GetWebContents();
RenderWidgetHostViewAndroid* rwhva_;
JavaObjectWeakGlobalRef java_ime_adapter_;
diff --git a/chromium/content/browser/renderer_host/input/gesture_event_filter.cc b/chromium/content/browser/renderer_host/input/gesture_event_queue.cc
index 2761d9687d0..b16923be17f 100644
--- a/chromium/content/browser/renderer_host/input/gesture_event_filter.cc
+++ b/chromium/content/browser/renderer_host/input/gesture_event_queue.cc
@@ -1,10 +1,10 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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/renderer_host/input/gesture_event_filter.h"
+#include "content/browser/renderer_host/input/gesture_event_queue.h"
-#include "base/command_line.h"
+#include "base/debug/trace_event.h"
#include "base/strings/string_number_conversions.h"
#include "content/browser/renderer_host/input/input_router.h"
#include "content/browser/renderer_host/input/touchpad_tap_suppression_controller.h"
@@ -15,44 +15,36 @@ using blink::WebGestureEvent;
using blink::WebInputEvent;
namespace content {
-namespace {
-
-// Default debouncing interval duration: if a scroll is in progress, non-scroll
-// events during this interval are deferred to either its end or discarded on
-// receipt of another GestureScrollUpdate.
-static const int kDebouncingIntervalTimeMs = 30;
-
-} // namespace
-
-GestureEventFilter::GestureEventFilter(
- GestureEventFilterClient* client,
- TouchpadTapSuppressionControllerClient* touchpad_client)
- : client_(client),
- fling_in_progress_(false),
- scrolling_in_progress_(false),
- ignore_next_ack_(false),
- combined_scroll_pinch_(gfx::Transform()),
- touchpad_tap_suppression_controller_(
- new TouchpadTapSuppressionController(touchpad_client)),
- touchscreen_tap_suppression_controller_(
- new TouchscreenTapSuppressionController(this)),
- debounce_interval_time_ms_(kDebouncingIntervalTimeMs),
- debounce_enabled_(true) {
+
+GestureEventQueue::Config::Config() {
+}
+
+GestureEventQueue::GestureEventQueue(
+ GestureEventQueueClient* client,
+ TouchpadTapSuppressionControllerClient* touchpad_client,
+ const Config& config)
+ : client_(client),
+ fling_in_progress_(false),
+ scrolling_in_progress_(false),
+ ignore_next_ack_(false),
+ touchpad_tap_suppression_controller_(
+ touchpad_client,
+ config.touchpad_tap_suppression_config),
+ touchscreen_tap_suppression_controller_(
+ this,
+ config.touchscreen_tap_suppression_config),
+ debounce_interval_(config.debounce_interval) {
DCHECK(client);
- DCHECK(touchpad_tap_suppression_controller_);
- if (CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kDisableGestureDebounce)) {
- debounce_enabled_ = false;
- }
+ DCHECK(touchpad_client);
}
-GestureEventFilter::~GestureEventFilter() { }
+GestureEventQueue::~GestureEventQueue() { }
-bool GestureEventFilter::ShouldDiscardFlingCancelEvent(
+bool GestureEventQueue::ShouldDiscardFlingCancelEvent(
const GestureEventWithLatencyInfo& gesture_event) const {
if (coalesced_gesture_events_.empty() && fling_in_progress_)
return false;
- GestureEventQueue::const_reverse_iterator it =
+ GestureQueue::const_reverse_iterator it =
coalesced_gesture_events_.rbegin();
while (it != coalesced_gesture_events_.rend()) {
if (it->event.type == WebInputEvent::GestureFlingStart)
@@ -64,18 +56,18 @@ bool GestureEventFilter::ShouldDiscardFlingCancelEvent(
return true;
}
-bool GestureEventFilter::ShouldForwardForBounceReduction(
+bool GestureEventQueue::ShouldForwardForBounceReduction(
const GestureEventWithLatencyInfo& gesture_event) {
- if (!debounce_enabled_)
+ if (debounce_interval_ <= base::TimeDelta())
return true;
switch (gesture_event.event.type) {
case WebInputEvent::GestureScrollUpdate:
if (!scrolling_in_progress_) {
debounce_deferring_timer_.Start(
FROM_HERE,
- base::TimeDelta::FromMilliseconds(debounce_interval_time_ms_),
+ debounce_interval_,
this,
- &GestureEventFilter::SendScrollEndingEventsNow);
+ &GestureEventQueue::SendScrollEndingEventsNow);
} else {
// Extend the bounce interval.
debounce_deferring_timer_.Reset();
@@ -95,64 +87,52 @@ bool GestureEventFilter::ShouldForwardForBounceReduction(
}
return true;
}
-
- NOTREACHED();
- return false;
}
// NOTE: The filters are applied successively. This simplifies the change.
-bool GestureEventFilter::ShouldForward(
+bool GestureEventQueue::ShouldForward(
const GestureEventWithLatencyInfo& gesture_event) {
- return ShouldForwardForZeroVelocityFlingStart(gesture_event) &&
- ShouldForwardForBounceReduction(gesture_event) &&
- ShouldForwardForGFCFiltering(gesture_event) &&
- ShouldForwardForTapSuppression(gesture_event) &&
- ShouldForwardForCoalescing(gesture_event);
+ TRACE_EVENT0("input", "GestureEventQueue::ShouldForward");
+ return ShouldForwardForBounceReduction(gesture_event) &&
+ ShouldForwardForGFCFiltering(gesture_event) &&
+ ShouldForwardForTapSuppression(gesture_event) &&
+ ShouldForwardForCoalescing(gesture_event);
}
-bool GestureEventFilter::ShouldForwardForZeroVelocityFlingStart(
- const GestureEventWithLatencyInfo& gesture_event) const {
- return gesture_event.event.type != WebInputEvent::GestureFlingStart ||
- gesture_event.event.sourceDevice != WebGestureEvent::Touchpad ||
- gesture_event.event.data.flingStart.velocityX != 0 ||
- gesture_event.event.data.flingStart.velocityY != 0;
-}
-
-bool GestureEventFilter::ShouldForwardForGFCFiltering(
+bool GestureEventQueue::ShouldForwardForGFCFiltering(
const GestureEventWithLatencyInfo& gesture_event) const {
return gesture_event.event.type != WebInputEvent::GestureFlingCancel ||
!ShouldDiscardFlingCancelEvent(gesture_event);
}
-bool GestureEventFilter::ShouldForwardForTapSuppression(
+bool GestureEventQueue::ShouldForwardForTapSuppression(
const GestureEventWithLatencyInfo& gesture_event) {
switch (gesture_event.event.type) {
case WebInputEvent::GestureFlingCancel:
- if (gesture_event.event.sourceDevice == WebGestureEvent::Touchscreen)
- touchscreen_tap_suppression_controller_->GestureFlingCancel();
+ if (gesture_event.event.sourceDevice ==
+ blink::WebGestureDeviceTouchscreen)
+ touchscreen_tap_suppression_controller_.GestureFlingCancel();
else
- touchpad_tap_suppression_controller_->GestureFlingCancel();
+ touchpad_tap_suppression_controller_.GestureFlingCancel();
return true;
case WebInputEvent::GestureTapDown:
- return !touchscreen_tap_suppression_controller_->
- ShouldDeferGestureTapDown(gesture_event);
case WebInputEvent::GestureShowPress:
- return !touchscreen_tap_suppression_controller_->
- ShouldDeferGestureShowPress(gesture_event);
+ case WebInputEvent::GestureTapUnconfirmed:
case WebInputEvent::GestureTapCancel:
case WebInputEvent::GestureTap:
- case WebInputEvent::GestureTapUnconfirmed:
case WebInputEvent::GestureDoubleTap:
- return !touchscreen_tap_suppression_controller_->
- ShouldSuppressGestureTapEnd();
+ if (gesture_event.event.sourceDevice ==
+ blink::WebGestureDeviceTouchscreen) {
+ return !touchscreen_tap_suppression_controller_.FilterTapEvent(
+ gesture_event);
+ }
+ return true;
default:
return true;
}
- NOTREACHED();
- return false;
}
-bool GestureEventFilter::ShouldForwardForCoalescing(
+bool GestureEventQueue::ShouldForwardForCoalescing(
const GestureEventWithLatencyInfo& gesture_event) {
switch (gesture_event.event.type) {
case WebInputEvent::GestureFlingCancel:
@@ -168,35 +148,51 @@ bool GestureEventFilter::ShouldForwardForCoalescing(
default:
break;
}
- EnqueueEvent(gesture_event);
+ coalesced_gesture_events_.push_back(gesture_event);
return ShouldHandleEventNow();
}
-void GestureEventFilter::ProcessGestureAck(InputEventAckState ack_result,
+void GestureEventQueue::ProcessGestureAck(InputEventAckState ack_result,
WebInputEvent::Type type,
const ui::LatencyInfo& latency) {
+ TRACE_EVENT0("input", "GestureEventQueue::ProcessGestureAck");
+
if (coalesced_gesture_events_.empty()) {
DLOG(ERROR) << "Received unexpected ACK for event type " << type;
return;
}
- // Ack'ing an event may enqueue additional gesture events. By ack'ing the
- // event before the forwarding of queued events below, such additional events
- // can be coalesced with existing queued events prior to dispatch.
+ // It's possible that the ack for the second event in an in-flight, coalesced
+ // Gesture{Scroll,Pinch}Update pair is received prior to the first event ack.
+ // TODO(jdduke): Unify GSU/GPU pairs into a single event, crbug.com/359115.
+ size_t event_index = 0;
+ if (ignore_next_ack_ &&
+ coalesced_gesture_events_.size() > 1 &&
+ coalesced_gesture_events_[0].event.type != type &&
+ coalesced_gesture_events_[1].event.type == type) {
+ event_index = 1;
+ }
GestureEventWithLatencyInfo event_with_latency =
- coalesced_gesture_events_.front();
+ coalesced_gesture_events_[event_index];
DCHECK_EQ(event_with_latency.event.type, type);
event_with_latency.latency.AddNewLatencyFrom(latency);
+
+ // Ack'ing an event may enqueue additional gesture events. By ack'ing the
+ // event before the forwarding of queued events below, such additional events
+ // can be coalesced with existing queued events prior to dispatch.
client_->OnGestureEventAck(event_with_latency, ack_result);
const bool processed = (INPUT_EVENT_ACK_STATE_CONSUMED == ack_result);
if (type == WebInputEvent::GestureFlingCancel) {
- if (event_with_latency.event.sourceDevice == WebGestureEvent::Touchscreen)
- touchscreen_tap_suppression_controller_->GestureFlingCancelAck(processed);
+ if (event_with_latency.event.sourceDevice ==
+ blink::WebGestureDeviceTouchscreen)
+ touchscreen_tap_suppression_controller_.GestureFlingCancelAck(processed);
else
- touchpad_tap_suppression_controller_->GestureFlingCancelAck(processed);
+ touchpad_tap_suppression_controller_.GestureFlingCancelAck(processed);
}
- coalesced_gesture_events_.pop_front();
+ DCHECK_LT(event_index, coalesced_gesture_events_.size());
+ coalesced_gesture_events_.erase(coalesced_gesture_events_.begin() +
+ event_index);
if (ignore_next_ack_) {
ignore_next_ack_ = false;
@@ -209,8 +205,7 @@ void GestureEventFilter::ProcessGestureAck(InputEventAckState ack_result,
const GestureEventWithLatencyInfo& first_gesture_event =
coalesced_gesture_events_.front();
- // TODO(yusufo): Introduce GesturePanScroll so that these can be combined
- // into one gesture and kept inside the queue that way.
+ // TODO(jdduke): Unify GSU/GPU pairs into a single event, crbug.com/359115.
// Check for the coupled GesturePinchUpdate before sending either event,
// handling the case where the first GestureScrollUpdate ack is synchronous.
GestureEventWithLatencyInfo second_gesture_event;
@@ -228,33 +223,35 @@ void GestureEventFilter::ProcessGestureAck(InputEventAckState ack_result,
}
TouchpadTapSuppressionController*
- GestureEventFilter::GetTouchpadTapSuppressionController() {
- return touchpad_tap_suppression_controller_.get();
+ GestureEventQueue::GetTouchpadTapSuppressionController() {
+ return &touchpad_tap_suppression_controller_;
}
-bool GestureEventFilter::HasQueuedGestureEvents() const {
+bool GestureEventQueue::ExpectingGestureAck() const {
return !coalesced_gesture_events_.empty();
}
-void GestureEventFilter::FlingHasBeenHalted() {
+void GestureEventQueue::FlingHasBeenHalted() {
fling_in_progress_ = false;
}
-bool GestureEventFilter::ShouldHandleEventNow() const {
+bool GestureEventQueue::ShouldHandleEventNow() const {
return coalesced_gesture_events_.size() == 1;
}
-void GestureEventFilter::ForwardGestureEvent(
+void GestureEventQueue::ForwardGestureEvent(
const GestureEventWithLatencyInfo& gesture_event) {
if (ShouldForwardForCoalescing(gesture_event))
client_->SendGestureEventImmediately(gesture_event);
}
-void GestureEventFilter::SendScrollEndingEventsNow() {
+void GestureEventQueue::SendScrollEndingEventsNow() {
scrolling_in_progress_ = false;
- GestureEventQueue debouncing_deferral_queue;
+ if (debouncing_deferral_queue_.empty())
+ return;
+ GestureQueue debouncing_deferral_queue;
debouncing_deferral_queue.swap(debouncing_deferral_queue_);
- for (GestureEventQueue::const_iterator it = debouncing_deferral_queue.begin();
+ for (GestureQueue::const_iterator it = debouncing_deferral_queue.begin();
it != debouncing_deferral_queue.end(); it++) {
if (ShouldForwardForGFCFiltering(*it) &&
ShouldForwardForTapSuppression(*it) &&
@@ -264,30 +261,30 @@ void GestureEventFilter::SendScrollEndingEventsNow() {
}
}
-void GestureEventFilter::MergeOrInsertScrollAndPinchEvent(
+void GestureEventQueue::MergeOrInsertScrollAndPinchEvent(
const GestureEventWithLatencyInfo& gesture_event) {
- if (coalesced_gesture_events_.size() <= 1) {
- EnqueueEvent(gesture_event);
+ const size_t unsent_events_count =
+ coalesced_gesture_events_.size() - EventsInFlightCount();
+ if (!unsent_events_count) {
+ coalesced_gesture_events_.push_back(gesture_event);
return;
}
+
GestureEventWithLatencyInfo* last_event = &coalesced_gesture_events_.back();
if (last_event->CanCoalesceWith(gesture_event)) {
last_event->CoalesceWith(gesture_event);
- if (!combined_scroll_pinch_.IsIdentity()) {
- combined_scroll_pinch_.ConcatTransform(
- GetTransformForEvent(gesture_event));
- }
return;
}
- if (coalesced_gesture_events_.size() == 2 ||
- (coalesced_gesture_events_.size() == 3 && ignore_next_ack_) ||
- !ShouldTryMerging(gesture_event, *last_event)) {
- EnqueueEvent(gesture_event);
+
+ if (!ShouldTryMerging(gesture_event, *last_event)) {
+ coalesced_gesture_events_.push_back(gesture_event);
return;
}
+
GestureEventWithLatencyInfo scroll_event;
GestureEventWithLatencyInfo pinch_event;
scroll_event.event.modifiers |= gesture_event.event.modifiers;
+ scroll_event.event.sourceDevice = gesture_event.event.sourceDevice;
scroll_event.event.timeStampSeconds = gesture_event.event.timeStampSeconds;
// Keep the oldest LatencyInfo.
DCHECK_LE(last_event->latency.trace_id, gesture_event.latency.trace_id);
@@ -302,28 +299,32 @@ void GestureEventFilter::MergeOrInsertScrollAndPinchEvent(
WebInputEvent::GesturePinchUpdate ?
gesture_event.event.y : last_event->event.y;
- combined_scroll_pinch_.ConcatTransform(GetTransformForEvent(gesture_event));
- GestureEventWithLatencyInfo* second_last_event = &coalesced_gesture_events_
- [coalesced_gesture_events_.size() - 2];
- if (ShouldTryMerging(gesture_event, *second_last_event)) {
- // Keep the oldest LatencyInfo.
- DCHECK_LE(second_last_event->latency.trace_id,
- scroll_event.latency.trace_id);
- scroll_event.latency = second_last_event->latency;
- pinch_event.latency = second_last_event->latency;
- coalesced_gesture_events_.pop_back();
- } else {
- DCHECK(combined_scroll_pinch_ == GetTransformForEvent(gesture_event));
- combined_scroll_pinch_.
- PreconcatTransform(GetTransformForEvent(*last_event));
+ gfx::Transform combined_scroll_pinch = GetTransformForEvent(*last_event);
+ // Only include the second-to-last event in the coalesced pair if it exists
+ // and can be combined with the new event.
+ if (unsent_events_count > 1) {
+ const GestureEventWithLatencyInfo& second_last_event =
+ coalesced_gesture_events_[coalesced_gesture_events_.size() - 2];
+ if (ShouldTryMerging(gesture_event, second_last_event)) {
+ // Keep the oldest LatencyInfo.
+ DCHECK_LE(second_last_event.latency.trace_id,
+ scroll_event.latency.trace_id);
+ scroll_event.latency = second_last_event.latency;
+ pinch_event.latency = second_last_event.latency;
+ combined_scroll_pinch.PreconcatTransform(
+ GetTransformForEvent(second_last_event));
+ coalesced_gesture_events_.pop_back();
+ }
}
+ combined_scroll_pinch.ConcatTransform(GetTransformForEvent(gesture_event));
coalesced_gesture_events_.pop_back();
+
float combined_scale =
- SkMScalarToFloat(combined_scroll_pinch_.matrix().get(0, 0));
+ SkMScalarToFloat(combined_scroll_pinch.matrix().get(0, 0));
float combined_scroll_pinch_x =
- SkMScalarToFloat(combined_scroll_pinch_.matrix().get(0, 3));
+ SkMScalarToFloat(combined_scroll_pinch.matrix().get(0, 3));
float combined_scroll_pinch_y =
- SkMScalarToFloat(combined_scroll_pinch_.matrix().get(1, 3));
+ SkMScalarToFloat(combined_scroll_pinch.matrix().get(1, 3));
scroll_event.event.data.scrollUpdate.deltaX =
(combined_scroll_pinch_x + pinch_event.event.x) / combined_scale -
pinch_event.event.x;
@@ -335,7 +336,7 @@ void GestureEventFilter::MergeOrInsertScrollAndPinchEvent(
coalesced_gesture_events_.push_back(pinch_event);
}
-bool GestureEventFilter::ShouldTryMerging(
+bool GestureEventQueue::ShouldTryMerging(
const GestureEventWithLatencyInfo& new_event,
const GestureEventWithLatencyInfo& event_in_queue) const {
DLOG_IF(WARNING,
@@ -344,12 +345,13 @@ bool GestureEventFilter::ShouldTryMerging(
<< "Event time not monotonic?\n";
return (event_in_queue.event.type == WebInputEvent::GestureScrollUpdate ||
event_in_queue.event.type == WebInputEvent::GesturePinchUpdate) &&
- event_in_queue.event.modifiers == new_event.event.modifiers;
+ event_in_queue.event.modifiers == new_event.event.modifiers &&
+ event_in_queue.event.sourceDevice == new_event.event.sourceDevice;
}
-gfx::Transform GestureEventFilter::GetTransformForEvent(
+gfx::Transform GestureEventQueue::GetTransformForEvent(
const GestureEventWithLatencyInfo& gesture_event) const {
- gfx::Transform gesture_transform = gfx::Transform();
+ gfx::Transform gesture_transform;
if (gesture_event.event.type == WebInputEvent::GestureScrollUpdate) {
gesture_transform.Translate(gesture_event.event.data.scrollUpdate.deltaX,
gesture_event.event.data.scrollUpdate.deltaY);
@@ -362,12 +364,15 @@ gfx::Transform GestureEventFilter::GetTransformForEvent(
return gesture_transform;
}
-void GestureEventFilter::EnqueueEvent(
- const GestureEventWithLatencyInfo& gesture_event) {
- coalesced_gesture_events_.push_back(gesture_event);
- // Scroll and pinch events contributing to |combined_scroll_pinch_| will be
- // manually added to the queue in |MergeOrInsertScrollAndPinchEvent()|.
- combined_scroll_pinch_ = gfx::Transform();
+size_t GestureEventQueue::EventsInFlightCount() const {
+ if (coalesced_gesture_events_.empty())
+ return 0;
+
+ if (!ignore_next_ack_)
+ return 1;
+
+ DCHECK_GT(coalesced_gesture_events_.size(), 1U);
+ return 2;
}
} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/gesture_event_filter.h b/chromium/content/browser/renderer_host/input/gesture_event_queue.h
index d0517008bfc..8bc433bfb85 100644
--- a/chromium/content/browser/renderer_host/input/gesture_event_filter.h
+++ b/chromium/content/browser/renderer_host/input/gesture_event_queue.h
@@ -1,34 +1,34 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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_RENDERER_HOST_INPUT_GESTURE_EVENT_FILTER_H_
-#define CONTENT_BROWSER_RENDERER_HOST_INPUT_GESTURE_EVENT_FILTER_H_
+#ifndef CONTENT_BROWSER_RENDERER_HOST_INPUT_GESTURE_EVENT_QUEUE_H_
+#define CONTENT_BROWSER_RENDERER_HOST_INPUT_GESTURE_EVENT_QUEUE_H_
#include <deque>
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "base/timer/timer.h"
+#include "content/browser/renderer_host/event_with_latency_info.h"
+#include "content/browser/renderer_host/input/tap_suppression_controller.h"
+#include "content/browser/renderer_host/input/touchpad_tap_suppression_controller.h"
+#include "content/browser/renderer_host/input/touchscreen_tap_suppression_controller.h"
#include "content/common/content_export.h"
-#include "content/port/browser/event_with_latency_info.h"
-#include "content/port/common/input_event_ack_state.h"
+#include "content/common/input/input_event_ack_state.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
#include "ui/gfx/transform.h"
namespace content {
-class GestureEventFilterTest;
+class GestureEventQueueTest;
class InputRouter;
class MockRenderWidgetHost;
-class TouchpadTapSuppressionController;
-class TouchpadTapSuppressionControllerClient;
-class TouchscreenTapSuppressionController;
-// Interface with which the GestureEventFilter can forward gesture events, and
+// Interface with which the GestureEventQueue can forward gesture events, and
// dispatch gesture event responses.
-class CONTENT_EXPORT GestureEventFilterClient {
+class CONTENT_EXPORT GestureEventQueueClient {
public:
- virtual ~GestureEventFilterClient() {}
+ virtual ~GestureEventQueueClient() {}
virtual void SendGestureEventImmediately(
const GestureEventWithLatencyInfo& event) = 0;
@@ -40,17 +40,16 @@ class CONTENT_EXPORT GestureEventFilterClient {
// Maintains WebGestureEvents in a queue before forwarding them to the renderer
// to apply a sequence of filters on them:
-// 1. Zero-velocity fling-starts from touchpad are filtered.
-// 2. The sequence is filtered for bounces. A bounce is when the finger lifts
+// 1. The sequence is filtered for bounces. A bounce is when the finger lifts
// from the screen briefly during an in-progress scroll. Ifco this happens,
// non-GestureScrollUpdate events are queued until the de-bounce interval
// passes or another GestureScrollUpdate event occurs.
-// 3. Unnecessary GestureFlingCancel events are filtered. These are
+// 2. Unnecessary GestureFlingCancel events are filtered. These are
// GestureFlingCancels that have no corresponding GestureFlingStart in the
// queue.
-// 4. Taps immediately after a GestureFlingCancel (caused by the same tap) are
+// 3. Taps immediately after a GestureFlingCancel (caused by the same tap) are
// filtered.
-// 5. Whenever possible, events in the queue are coalesced to have as few events
+// 4. Whenever possible, events in the queue are coalesced to have as few events
// as possible and therefore maximize the chance that the event stream can be
// handled entirely by the compositor thread.
// Events in the queue are forwarded to the renderer one by one; i.e., each
@@ -59,15 +58,33 @@ class CONTENT_EXPORT GestureEventFilterClient {
// sent together.
// TODO(rjkroege): Possibly refactor into a filter chain:
// http://crbug.com/148443.
-class CONTENT_EXPORT GestureEventFilter {
+class CONTENT_EXPORT GestureEventQueue {
public:
- // Both |client| and |touchpad_client| must outlive the GestureEventFilter.
- GestureEventFilter(GestureEventFilterClient* client,
- TouchpadTapSuppressionControllerClient* touchpad_client);
- ~GestureEventFilter();
+ struct CONTENT_EXPORT Config {
+ Config();
+
+ // Controls touchpad-related tap suppression, disabled by default.
+ TapSuppressionController::Config touchpad_tap_suppression_config;
+
+ // Controls touchscreen-related tap suppression, disabled by default.
+ TapSuppressionController::Config touchscreen_tap_suppression_config;
+
+ // Determines whether non-scroll gesture events are "debounced" during an
+ // active scroll sequence, suppressing brief scroll interruptions.
+ // Zero by default (disabled).
+ base::TimeDelta debounce_interval;
+ };
+
+ // Both |client| and |touchpad_client| must outlive the GestureEventQueue.
+ GestureEventQueue(GestureEventQueueClient* client,
+ TouchpadTapSuppressionControllerClient* touchpad_client,
+ const Config& config);
+ ~GestureEventQueue();
// Returns |true| if the caller should immediately forward the provided
// |GestureEventWithLatencyInfo| argument to the renderer.
+ // If this function returns false, then the event may be queued and forwared
+ // at a later point.
bool ShouldForward(const GestureEventWithLatencyInfo&);
// Indicates that the caller has received an acknowledgement from the renderer
@@ -84,26 +101,27 @@ class CONTENT_EXPORT GestureEventFilter {
// Returns the |TouchpadTapSuppressionController| instance.
TouchpadTapSuppressionController* GetTouchpadTapSuppressionController();
- // Returns whether there are any gesture event in the queue.
- bool HasQueuedGestureEvents() const;
-
void ForwardGestureEvent(const GestureEventWithLatencyInfo& gesture_event);
- void set_debounce_enabled_for_testing(bool enabled) {
- debounce_enabled_ = enabled;
+ // Whether the queue is expecting a gesture event ack.
+ bool ExpectingGestureAck() const;
+
+ bool empty() const {
+ return coalesced_gesture_events_.empty() &&
+ debouncing_deferral_queue_.empty();
}
- void set_debounce_interval_time_ms_for_testing(int interval_time_ms) {
- debounce_interval_time_ms_ = interval_time_ms;
+ void set_debounce_interval_time_ms_for_testing(int interval_ms) {
+ debounce_interval_ = base::TimeDelta::FromMilliseconds(interval_ms);
}
private:
- friend class GestureEventFilterTest;
+ friend class GestureEventQueueTest;
friend class MockRenderWidgetHost;
// TODO(mohsen): There are a bunch of ShouldForward.../ShouldDiscard...
// methods that are getting confusing. This should be somehow fixed. Maybe
- // while refactoring GEF: http://crbug.com/148443.
+ // while refactoring GEQ: http://crbug.com/148443.
// Inovked on the expiration of the debounce interval to release
// deferred events.
@@ -123,10 +141,6 @@ class CONTENT_EXPORT GestureEventFilter {
void MergeOrInsertScrollAndPinchEvent(
const GestureEventWithLatencyInfo& gesture_event);
- // Sub-filter for removing zero-velocity fling-starts from touchpad.
- bool ShouldForwardForZeroVelocityFlingStart(
- const GestureEventWithLatencyInfo& gesture_event) const;
-
// Sub-filter for removing bounces from in-progress scrolls.
bool ShouldForwardForBounceReduction(
const GestureEventWithLatencyInfo& gesture_event);
@@ -158,12 +172,12 @@ class CONTENT_EXPORT GestureEventFilter {
gfx::Transform GetTransformForEvent(
const GestureEventWithLatencyInfo& gesture_event) const;
- // Adds |gesture_event| to the |coalesced_gesture_events_|, resetting the
- // accumulation of |combined_scroll_pinch_|.
- void EnqueueEvent(const GestureEventWithLatencyInfo& gesture_event);
+ // The number of sent events for which we're awaiting an ack. These events
+ // remain at the head of the queue until ack'ed.
+ size_t EventsInFlightCount() const;
// The receiver of all forwarded gesture events.
- GestureEventFilterClient* client_;
+ GestureEventQueueClient* client_;
// True if a GestureFlingStart is in progress on the renderer or
// queued without a subsequent queued GestureFlingCancel event.
@@ -176,25 +190,19 @@ class CONTENT_EXPORT GestureEventFilter {
// for an ACK, so the next gesture ACK should be ignored.
bool ignore_next_ack_;
- // Transform that holds the combined transform matrix for the current
- // scroll-pinch sequence at the end of the queue.
- gfx::Transform combined_scroll_pinch_;
-
// An object tracking the state of touchpad on the delivery of mouse events to
// the renderer to filter mouse immediately after a touchpad fling canceling
// tap.
- // TODO(mohsen): Move touchpad tap suppression out of GestureEventFilter since
- // GEF is meant to only be used for touchscreen gesture events.
- scoped_ptr<TouchpadTapSuppressionController>
- touchpad_tap_suppression_controller_;
+ // TODO(mohsen): Move touchpad tap suppression out of GestureEventQueue since
+ // GEQ is meant to only be used for touchscreen gesture events.
+ TouchpadTapSuppressionController touchpad_tap_suppression_controller_;
// An object tracking the state of touchscreen on the delivery of gesture tap
// events to the renderer to filter taps immediately after a touchscreen fling
// canceling tap.
- scoped_ptr<TouchscreenTapSuppressionController>
- touchscreen_tap_suppression_controller_;
+ TouchscreenTapSuppressionController touchscreen_tap_suppression_controller_;
- typedef std::deque<GestureEventWithLatencyInfo> GestureEventQueue;
+ typedef std::deque<GestureEventWithLatencyInfo> GestureQueue;
// Queue of coalesced gesture events not yet sent to the renderer. If
// |ignore_next_ack_| is false, then the event at the front of the queue has
@@ -202,25 +210,21 @@ class CONTENT_EXPORT GestureEventFilter {
// If |ignore_next_ack_| is true, then the two events at the front of the
// queue have been sent, and the second is awaiting an ACK. All other events
// have yet to be sent.
- GestureEventQueue coalesced_gesture_events_;
+ GestureQueue coalesced_gesture_events_;
// Timer to release a previously deferred gesture event.
- base::OneShotTimer<GestureEventFilter> debounce_deferring_timer_;
+ base::OneShotTimer<GestureEventQueue> debounce_deferring_timer_;
// Queue of events that have been deferred for debounce.
- GestureEventQueue debouncing_deferral_queue_;
-
- // Time window in which to debounce scroll/fling ends.
- // TODO(rjkroege): Make this dynamically configurable.
- int debounce_interval_time_ms_;
+ GestureQueue debouncing_deferral_queue_;
- // Whether scroll-ending events should be deferred when a scroll is active.
- // Defaults to true.
- bool debounce_enabled_;
+ // Time window in which to debounce scroll/fling ends. Note that an interval
+ // of zero effectively disables debouncing.
+ base::TimeDelta debounce_interval_;
- DISALLOW_COPY_AND_ASSIGN(GestureEventFilter);
+ DISALLOW_COPY_AND_ASSIGN(GestureEventQueue);
};
} // namespace content
-#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_GESTURE_EVENT_FILTER_H_
+#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_GESTURE_EVENT_QUEUE_H_
diff --git a/chromium/content/browser/renderer_host/input/gesture_event_filter_unittest.cc b/chromium/content/browser/renderer_host/input/gesture_event_queue_unittest.cc
index 421fd701310..0c49a847578 100644
--- a/chromium/content/browser/renderer_host/input/gesture_event_filter_unittest.cc
+++ b/chromium/content/browser/renderer_host/input/gesture_event_queue_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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.
@@ -9,41 +9,42 @@
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/time/time.h"
-#include "content/browser/renderer_host/input/gesture_event_filter.h"
+#include "content/browser/renderer_host/input/gesture_event_queue.h"
#include "content/browser/renderer_host/input/touchpad_tap_suppression_controller.h"
+#include "content/common/input/input_event_ack_state.h"
#include "content/common/input/synthetic_web_input_event_builders.h"
-#include "content/port/common/input_event_ack_state.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
using base::TimeDelta;
+using blink::WebGestureDevice;
using blink::WebGestureEvent;
using blink::WebInputEvent;
namespace content {
-class GestureEventFilterTest : public testing::Test,
- public GestureEventFilterClient,
- public TouchpadTapSuppressionControllerClient {
+class GestureEventQueueTest : public testing::Test,
+ public GestureEventQueueClient,
+ public TouchpadTapSuppressionControllerClient {
public:
- GestureEventFilterTest()
+ GestureEventQueueTest()
: acked_gesture_event_count_(0),
sent_gesture_event_count_(0) {}
- virtual ~GestureEventFilterTest() {}
+ virtual ~GestureEventQueueTest() {}
// testing::Test
virtual void SetUp() OVERRIDE {
- filter_.reset(new GestureEventFilter(this, this));
+ queue_.reset(new GestureEventQueue(this, this, DefaultConfig()));
}
virtual void TearDown() OVERRIDE {
// Process all pending tasks to avoid leaks.
RunUntilIdle();
- filter_.reset();
+ queue_.reset();
}
- // GestureEventFilterClient
+ // GestureEventQueueClient
virtual void SendGestureEventImmediately(
const GestureEventWithLatencyInfo& event) OVERRIDE {
++sent_gesture_event_count_;
@@ -68,12 +69,19 @@ class GestureEventFilterTest : public testing::Test,
}
protected:
+ static GestureEventQueue::Config DefaultConfig() {
+ return GestureEventQueue::Config();
+ }
+
+ void SetUpForDebounce(int interval_ms) {
+ queue()->set_debounce_interval_time_ms_for_testing(interval_ms);
+ }
- // Returns the result of |GestureEventFilter::ShouldForward()|.
+ // Returns the result of |GestureEventQueue::ShouldForward()|.
bool SimulateGestureEvent(const WebGestureEvent& gesture) {
GestureEventWithLatencyInfo gesture_with_latency(gesture,
ui::LatencyInfo());
- if (filter()->ShouldForward(gesture_with_latency)) {
+ if (queue()->ShouldForward(gesture_with_latency)) {
SendGestureEventImmediately(gesture_with_latency);
return true;
}
@@ -81,7 +89,7 @@ class GestureEventFilterTest : public testing::Test,
}
void SimulateGestureEvent(WebInputEvent::Type type,
- WebGestureEvent::SourceDevice sourceDevice) {
+ WebGestureDevice sourceDevice) {
SimulateGestureEvent(
SyntheticWebGestureEventBuilder::Build(type, sourceDevice));
}
@@ -95,17 +103,17 @@ class GestureEventFilterTest : public testing::Test,
float anchorX,
float anchorY,
int modifiers) {
- SimulateGestureEvent(
- SyntheticWebGestureEventBuilder::BuildPinchUpdate(scale,
- anchorX,
- anchorY,
- modifiers));
+ SimulateGestureEvent(SyntheticWebGestureEventBuilder::BuildPinchUpdate(
+ scale,
+ anchorX,
+ anchorY,
+ modifiers,
+ blink::WebGestureDeviceTouchscreen));
}
- void SimulateGestureFlingStartEvent(
- float velocityX,
- float velocityY,
- WebGestureEvent::SourceDevice sourceDevice) {
+ void SimulateGestureFlingStartEvent(float velocityX,
+ float velocityY,
+ WebGestureDevice sourceDevice) {
SimulateGestureEvent(
SyntheticWebGestureEventBuilder::BuildFling(velocityX,
velocityY,
@@ -114,7 +122,7 @@ class GestureEventFilterTest : public testing::Test,
void SendInputEventACK(WebInputEvent::Type type,
InputEventAckState ack) {
- filter()->ProcessGestureAck(ack, type, ui::LatencyInfo());
+ queue()->ProcessGestureAck(ack, type, ui::LatencyInfo());
}
void RunUntilIdle() {
@@ -137,63 +145,55 @@ class GestureEventFilterTest : public testing::Test,
return last_acked_event_;
}
- void DisableDebounce() {
- filter()->set_debounce_enabled_for_testing(false);
- }
-
- void set_debounce_interval_time_ms(int ms) {
- filter()->set_debounce_interval_time_ms_for_testing(ms);
- }
-
void set_synchronous_ack(InputEventAckState ack_result) {
sync_ack_result_.reset(new InputEventAckState(ack_result));
}
void set_sync_followup_event(WebInputEvent::Type type,
- WebGestureEvent::SourceDevice sourceDevice) {
+ WebGestureDevice sourceDevice) {
sync_followup_event_.reset(new WebGestureEvent(
SyntheticWebGestureEventBuilder::Build(type, sourceDevice)));
}
unsigned GestureEventQueueSize() {
- return filter()->coalesced_gesture_events_.size();
+ return queue()->coalesced_gesture_events_.size();
}
WebGestureEvent GestureEventSecondFromLastQueueEvent() {
- return filter()->coalesced_gesture_events_.at(
+ return queue()->coalesced_gesture_events_.at(
GestureEventQueueSize() - 2).event;
}
WebGestureEvent GestureEventLastQueueEvent() {
- return filter()->coalesced_gesture_events_.back().event;
+ return queue()->coalesced_gesture_events_.back().event;
}
unsigned GestureEventDebouncingQueueSize() {
- return filter()->debouncing_deferral_queue_.size();
+ return queue()->debouncing_deferral_queue_.size();
}
WebGestureEvent GestureEventQueueEventAt(int i) {
- return filter()->coalesced_gesture_events_.at(i).event;
+ return queue()->coalesced_gesture_events_.at(i).event;
}
bool ScrollingInProgress() {
- return filter()->scrolling_in_progress_;
+ return queue()->scrolling_in_progress_;
}
bool FlingInProgress() {
- return filter()->fling_in_progress_;
+ return queue()->fling_in_progress_;
}
bool WillIgnoreNextACK() {
- return filter()->ignore_next_ack_;
+ return queue()->ignore_next_ack_;
}
- GestureEventFilter* filter() const {
- return filter_.get();
+ GestureEventQueue* queue() const {
+ return queue_.get();
}
private:
- scoped_ptr<GestureEventFilter> filter_;
+ scoped_ptr<GestureEventQueue> queue_;
size_t acked_gesture_event_count_;
size_t sent_gesture_event_count_;
WebGestureEvent last_acked_event_;
@@ -204,22 +204,18 @@ class GestureEventFilterTest : public testing::Test,
#if GTEST_HAS_PARAM_TEST
// This is for tests that are to be run for all source devices.
-class GestureEventFilterWithSourceTest
- : public GestureEventFilterTest,
- public testing::WithParamInterface<WebGestureEvent::SourceDevice> {
-};
+class GestureEventQueueWithSourceTest
+ : public GestureEventQueueTest,
+ public testing::WithParamInterface<WebGestureDevice> {};
#endif // GTEST_HAS_PARAM_TEST
-TEST_F(GestureEventFilterTest, CoalescesScrollGestureEvents) {
- // Turn off debounce handling for test isolation.
- DisableDebounce();
-
+TEST_F(GestureEventQueueTest, CoalescesScrollGestureEvents) {
// Test coalescing of only GestureScrollUpdate events.
// Simulate gesture events.
// Sent.
SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
- WebGestureEvent::Touchscreen);
+ blink::WebGestureDeviceTouchscreen);
EXPECT_EQ(1U, GetAndResetSentGestureEventCount());
// Enqueued.
@@ -229,6 +225,7 @@ TEST_F(GestureEventFilterTest, CoalescesScrollGestureEvents) {
WebGestureEvent merged_event = GestureEventLastQueueEvent();
EXPECT_EQ(2U, GestureEventQueueSize());
EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type);
+ EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);
// Coalesced.
SimulateGestureScrollUpdateEvent(8, -6, 0);
@@ -239,6 +236,7 @@ TEST_F(GestureEventFilterTest, CoalescesScrollGestureEvents) {
EXPECT_EQ(0, merged_event.modifiers);
EXPECT_EQ(16, merged_event.data.scrollUpdate.deltaX);
EXPECT_EQ(-11, merged_event.data.scrollUpdate.deltaY);
+ EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);
// Enqueued.
SimulateGestureScrollUpdateEvent(8, -7, 1);
@@ -247,10 +245,11 @@ TEST_F(GestureEventFilterTest, CoalescesScrollGestureEvents) {
merged_event = GestureEventLastQueueEvent();
EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type);
EXPECT_EQ(1, merged_event.modifiers);
+ EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);
// Different.
SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
- WebGestureEvent::Touchscreen);
+ blink::WebGestureDeviceTouchscreen);
// Check that only the first event was sent.
EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
@@ -284,20 +283,62 @@ TEST_F(GestureEventFilterTest, CoalescesScrollGestureEvents) {
EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
}
-TEST_F(GestureEventFilterTest, CoalescesScrollAndPinchEvents) {
- // Turn off debounce handling for test isolation.
- DisableDebounce();
+TEST_F(GestureEventQueueTest,
+ DoesNotCoalesceScrollGestureEventsFromDifferentDevices) {
+ // Test that GestureScrollUpdate events from Touchscreen and Touchpad do not
+ // coalesce.
+
+ // Sent.
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(1U, GetAndResetSentGestureEventCount());
+
+ // Enqueued.
+ SimulateGestureScrollUpdateEvent(8, -5, 0);
+
+ // Make sure that the queue contains what we think it should.
+ EXPECT_EQ(2U, GestureEventQueueSize());
+ EXPECT_EQ(blink::WebGestureDeviceTouchscreen,
+ GestureEventLastQueueEvent().sourceDevice);
+
+ // Coalesced.
+ SimulateGestureScrollUpdateEvent(8, -6, 0);
+ EXPECT_EQ(2U, GestureEventQueueSize());
+ EXPECT_EQ(blink::WebGestureDeviceTouchscreen,
+ GestureEventLastQueueEvent().sourceDevice);
+
+ // Enqueued.
+ SimulateGestureEvent(WebInputEvent::GestureScrollUpdate,
+ blink::WebGestureDeviceTouchpad);
+ EXPECT_EQ(3U, GestureEventQueueSize());
+ EXPECT_EQ(blink::WebGestureDeviceTouchpad,
+ GestureEventLastQueueEvent().sourceDevice);
+
+ // Coalesced.
+ SimulateGestureEvent(WebInputEvent::GestureScrollUpdate,
+ blink::WebGestureDeviceTouchpad);
+ EXPECT_EQ(3U, GestureEventQueueSize());
+ EXPECT_EQ(blink::WebGestureDeviceTouchpad,
+ GestureEventLastQueueEvent().sourceDevice);
+
+ // Enqueued.
+ SimulateGestureScrollUpdateEvent(8, -7, 0);
+ EXPECT_EQ(4U, GestureEventQueueSize());
+ EXPECT_EQ(blink::WebGestureDeviceTouchscreen,
+ GestureEventLastQueueEvent().sourceDevice);
+}
+TEST_F(GestureEventQueueTest, CoalescesScrollAndPinchEvents) {
// Test coalescing of only GestureScrollUpdate events.
// Simulate gesture events.
// Sent.
SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
- WebGestureEvent::Touchscreen);
+ blink::WebGestureDeviceTouchscreen);
// Sent.
SimulateGestureEvent(WebInputEvent::GesturePinchBegin,
- WebGestureEvent::Touchscreen);
+ blink::WebGestureDeviceTouchscreen);
// Enqueued.
SimulateGestureScrollUpdateEvent(8, -4, 1);
@@ -315,11 +356,13 @@ TEST_F(GestureEventFilterTest, CoalescesScrollAndPinchEvents) {
EXPECT_EQ(WebInputEvent::GesturePinchUpdate, merged_event.type);
EXPECT_EQ(1.5, merged_event.data.pinchUpdate.scale);
EXPECT_EQ(1, merged_event.modifiers);
+ EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);
merged_event = GestureEventSecondFromLastQueueEvent();
EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type);
EXPECT_EQ(8, merged_event.data.scrollUpdate.deltaX);
EXPECT_EQ(-4, merged_event.data.scrollUpdate.deltaY);
EXPECT_EQ(1, merged_event.modifiers);
+ EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);
// Enqueued.
SimulateGestureScrollUpdateEvent(6, -3, 1);
@@ -330,11 +373,13 @@ TEST_F(GestureEventFilterTest, CoalescesScrollAndPinchEvents) {
EXPECT_EQ(WebInputEvent::GesturePinchUpdate, merged_event.type);
EXPECT_EQ(1.5, merged_event.data.pinchUpdate.scale);
EXPECT_EQ(1, merged_event.modifiers);
+ EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);
merged_event = GestureEventSecondFromLastQueueEvent();
EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type);
EXPECT_EQ(12, merged_event.data.scrollUpdate.deltaX);
EXPECT_EQ(-6, merged_event.data.scrollUpdate.deltaY);
EXPECT_EQ(1, merged_event.modifiers);
+ EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);
// Enqueued.
SimulateGesturePinchUpdateEvent(2, 60, 60, 1);
@@ -345,11 +390,13 @@ TEST_F(GestureEventFilterTest, CoalescesScrollAndPinchEvents) {
EXPECT_EQ(WebInputEvent::GesturePinchUpdate, merged_event.type);
EXPECT_EQ(3, merged_event.data.pinchUpdate.scale);
EXPECT_EQ(1, merged_event.modifiers);
+ EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);
merged_event = GestureEventSecondFromLastQueueEvent();
EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type);
EXPECT_EQ(12, merged_event.data.scrollUpdate.deltaX);
EXPECT_EQ(-6, merged_event.data.scrollUpdate.deltaY);
EXPECT_EQ(1, merged_event.modifiers);
+ EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);
// Enqueued.
SimulateGesturePinchUpdateEvent(2, 60, 60, 1);
@@ -360,11 +407,13 @@ TEST_F(GestureEventFilterTest, CoalescesScrollAndPinchEvents) {
EXPECT_EQ(WebInputEvent::GesturePinchUpdate, merged_event.type);
EXPECT_EQ(6, merged_event.data.pinchUpdate.scale);
EXPECT_EQ(1, merged_event.modifiers);
+ EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);
merged_event = GestureEventSecondFromLastQueueEvent();
EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type);
EXPECT_EQ(12, merged_event.data.scrollUpdate.deltaX);
EXPECT_EQ(-6, merged_event.data.scrollUpdate.deltaY);
EXPECT_EQ(1, merged_event.modifiers);
+ EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);
// Check that only the first event was sent.
EXPECT_EQ(1U, GetAndResetSentGestureEventCount());
@@ -384,11 +433,13 @@ TEST_F(GestureEventFilterTest, CoalescesScrollAndPinchEvents) {
EXPECT_EQ(WebInputEvent::GesturePinchUpdate, merged_event.type);
EXPECT_EQ(6, merged_event.data.pinchUpdate.scale);
EXPECT_EQ(1, merged_event.modifiers);
+ EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);
merged_event = GestureEventSecondFromLastQueueEvent();
EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type);
EXPECT_EQ(13, merged_event.data.scrollUpdate.deltaX);
EXPECT_EQ(-7, merged_event.data.scrollUpdate.deltaY);
EXPECT_EQ(1, merged_event.modifiers);
+ EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);
// At this point ACKs shouldn't be getting ignored.
EXPECT_FALSE(WillIgnoreNextACK());
@@ -412,10 +463,12 @@ TEST_F(GestureEventFilterTest, CoalescesScrollAndPinchEvents) {
EXPECT_EQ(1, merged_event.data.scrollUpdate.deltaX);
EXPECT_EQ(-1, merged_event.data.scrollUpdate.deltaY);
EXPECT_EQ(1, merged_event.modifiers);
+ EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);
merged_event = GestureEventSecondFromLastQueueEvent();
EXPECT_EQ(WebInputEvent::GesturePinchUpdate, merged_event.type);
EXPECT_EQ(6, merged_event.data.pinchUpdate.scale);
EXPECT_EQ(1, merged_event.modifiers);
+ EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);
// Enqueued.
SimulateGestureScrollUpdateEvent(2, -2, 1);
@@ -427,10 +480,12 @@ TEST_F(GestureEventFilterTest, CoalescesScrollAndPinchEvents) {
EXPECT_EQ(3, merged_event.data.scrollUpdate.deltaX);
EXPECT_EQ(-3, merged_event.data.scrollUpdate.deltaY);
EXPECT_EQ(1, merged_event.modifiers);
+ EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);
merged_event = GestureEventSecondFromLastQueueEvent();
EXPECT_EQ(WebInputEvent::GesturePinchUpdate, merged_event.type);
EXPECT_EQ(6, merged_event.data.pinchUpdate.scale);
EXPECT_EQ(1, merged_event.modifiers);
+ EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);
// Enqueued.
SimulateGesturePinchUpdateEvent(0.5, 60, 60, 1);
@@ -441,11 +496,13 @@ TEST_F(GestureEventFilterTest, CoalescesScrollAndPinchEvents) {
EXPECT_EQ(WebInputEvent::GesturePinchUpdate, merged_event.type);
EXPECT_EQ(0.5, merged_event.data.pinchUpdate.scale);
EXPECT_EQ(1, merged_event.modifiers);
+ EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);
merged_event = GestureEventSecondFromLastQueueEvent();
EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type);
EXPECT_EQ(3, merged_event.data.scrollUpdate.deltaX);
EXPECT_EQ(-3, merged_event.data.scrollUpdate.deltaY);
EXPECT_EQ(1, merged_event.modifiers);
+ EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);
// Check that the ACK gets ignored.
SendInputEventACK(WebInputEvent::GestureScrollUpdate,
@@ -466,10 +523,12 @@ TEST_F(GestureEventFilterTest, CoalescesScrollAndPinchEvents) {
EXPECT_EQ(2, merged_event.data.scrollUpdate.deltaX);
EXPECT_EQ(-2, merged_event.data.scrollUpdate.deltaY);
EXPECT_EQ(2, merged_event.modifiers);
+ EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);
merged_event = GestureEventSecondFromLastQueueEvent();
EXPECT_EQ(WebInputEvent::GesturePinchUpdate, merged_event.type);
EXPECT_EQ(0.5, merged_event.data.pinchUpdate.scale);
EXPECT_EQ(1, merged_event.modifiers);
+ EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);
// Check that the ACK sends the next scroll pinch pair.
SendInputEventACK(WebInputEvent::GesturePinchUpdate,
@@ -501,15 +560,12 @@ TEST_F(GestureEventFilterTest, CoalescesScrollAndPinchEvents) {
EXPECT_EQ(0U, GestureEventQueueSize());
}
-TEST_F(GestureEventFilterTest, CoalescesMultiplePinchEventSequences) {
- // Turn off debounce handling for test isolation.
- DisableDebounce();
-
+TEST_F(GestureEventQueueTest, CoalescesMultiplePinchEventSequences) {
// Simulate a pinch sequence.
SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
- WebGestureEvent::Touchscreen);
+ blink::WebGestureDeviceTouchscreen);
SimulateGestureEvent(WebInputEvent::GesturePinchBegin,
- WebGestureEvent::Touchscreen);
+ blink::WebGestureDeviceTouchscreen);
SimulateGestureScrollUpdateEvent(8, -4, 1);
// Make sure that the queue contains what we think it should.
@@ -549,13 +605,13 @@ TEST_F(GestureEventFilterTest, CoalescesMultiplePinchEventSequences) {
// Now start another sequence before the previous sequence has been ack'ed.
SimulateGestureEvent(WebInputEvent::GesturePinchEnd,
- WebGestureEvent::Touchscreen);
+ blink::WebGestureDeviceTouchscreen);
SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
- WebGestureEvent::Touchscreen);
+ blink::WebGestureDeviceTouchscreen);
SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
- WebGestureEvent::Touchscreen);
+ blink::WebGestureDeviceTouchscreen);
SimulateGestureEvent(WebInputEvent::GesturePinchBegin,
- WebGestureEvent::Touchscreen);
+ blink::WebGestureDeviceTouchscreen);
SimulateGestureScrollUpdateEvent(8, -4, 1);
// Make sure that the queue contains what we think it should.
@@ -594,25 +650,234 @@ TEST_F(GestureEventFilterTest, CoalescesMultiplePinchEventSequences) {
EXPECT_EQ(1, merged_event.modifiers);
}
+TEST_F(GestureEventQueueTest, CoalescesPinchSequencesWithEarlyAck) {
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ SendInputEventACK(WebInputEvent::GestureScrollBegin,
+ INPUT_EVENT_ACK_STATE_CONSUMED);
+
+ SimulateGestureEvent(WebInputEvent::GesturePinchBegin,
+ blink::WebGestureDeviceTouchscreen);
+ SendInputEventACK(WebInputEvent::GesturePinchBegin,
+ INPUT_EVENT_ACK_STATE_CONSUMED);
+ // ScrollBegin and PinchBegin have been sent
+ EXPECT_EQ(2U, GetAndResetSentGestureEventCount());
+ EXPECT_EQ(0U, GestureEventQueueSize());
+
+ SimulateGestureScrollUpdateEvent(5, 5, 1);
+ EXPECT_EQ(1U, GetAndResetSentGestureEventCount());
+ EXPECT_EQ(WebInputEvent::GestureScrollUpdate,
+ GestureEventLastQueueEvent().type);
+ EXPECT_EQ(1U, GestureEventQueueSize());
+
+
+ SimulateGesturePinchUpdateEvent(2, 60, 60, 1);
+ EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
+ EXPECT_EQ(WebInputEvent::GesturePinchUpdate,
+ GestureEventLastQueueEvent().type);
+ EXPECT_EQ(2U, GestureEventQueueSize());
+
+ SimulateGesturePinchUpdateEvent(3, 60, 60, 1);
+ EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
+ EXPECT_EQ(WebInputEvent::GesturePinchUpdate,
+ GestureEventLastQueueEvent().type);
+ EXPECT_EQ(2U, GestureEventQueueSize());
+
+ SimulateGestureScrollUpdateEvent(5, 5, 1);
+ EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
+ // The coalesced pinch/scroll pair will have been re-arranged, with the
+ // pinch following the scroll.
+ EXPECT_EQ(WebInputEvent::GesturePinchUpdate,
+ GestureEventLastQueueEvent().type);
+ EXPECT_EQ(3U, GestureEventQueueSize());
+
+ SimulateGesturePinchUpdateEvent(4, 60, 60, 1);
+ EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
+ EXPECT_EQ(3U, GestureEventQueueSize());
+
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_EQ(2U, GetAndResetSentGestureEventCount());
+ EXPECT_EQ(2U, GestureEventQueueSize());
+
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_EQ(WebInputEvent::GestureScrollUpdate, last_acked_event().type);
+
+ SendInputEventACK(WebInputEvent::GesturePinchUpdate,
+ INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_EQ(WebInputEvent::GesturePinchUpdate, last_acked_event().type);
+ EXPECT_EQ(2.f * 3.f * 4.f, last_acked_event().data.pinchUpdate.scale);
+
+ EXPECT_EQ(0U, GestureEventQueueSize());
+}
+
+TEST_F(GestureEventQueueTest,
+ DoesNotCoalescePinchGestureEventsWithDifferentModifiers) {
+ // Insert an event to force queueing of gestures.
+ SimulateGestureEvent(WebInputEvent::GestureTapCancel,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(1U, GetAndResetSentGestureEventCount());
+ EXPECT_EQ(1U, GestureEventQueueSize());
+
+ SimulateGestureScrollUpdateEvent(5, 5, 1);
+ EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
+ EXPECT_EQ(2U, GestureEventQueueSize());
+
+ SimulateGesturePinchUpdateEvent(3, 60, 60, 1);
+ EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
+ EXPECT_EQ(3U, GestureEventQueueSize());
+
+ SimulateGestureScrollUpdateEvent(10, 15, 1);
+ EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
+ EXPECT_EQ(3U, GestureEventQueueSize());
+
+ SimulateGesturePinchUpdateEvent(4, 60, 60, 1);
+ EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
+ EXPECT_EQ(3U, GestureEventQueueSize());
+
+ // Using different modifiers should prevent coalescing.
+ SimulateGesturePinchUpdateEvent(5, 60, 60, 2);
+ EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
+ EXPECT_EQ(4U, GestureEventQueueSize());
+
+ SimulateGesturePinchUpdateEvent(6, 60, 60, 3);
+ EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
+ EXPECT_EQ(5U, GestureEventQueueSize());
+
+ SendInputEventACK(WebInputEvent::GestureTapCancel,
+ INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_EQ(2U, GetAndResetSentGestureEventCount());
+ EXPECT_EQ(4U, GestureEventQueueSize());
+
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_EQ(WebInputEvent::GestureScrollUpdate, last_acked_event().type);
+ EXPECT_EQ(3U, GestureEventQueueSize());
+
+ SendInputEventACK(WebInputEvent::GesturePinchUpdate,
+ INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_EQ(WebInputEvent::GesturePinchUpdate, last_acked_event().type);
+ EXPECT_EQ(3.f * 4.f, last_acked_event().data.pinchUpdate.scale);
+ EXPECT_EQ(2U, GestureEventQueueSize());
+ EXPECT_EQ(1U, GetAndResetSentGestureEventCount());
+
+ SendInputEventACK(WebInputEvent::GesturePinchUpdate,
+ INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_EQ(WebInputEvent::GesturePinchUpdate, last_acked_event().type);
+ EXPECT_EQ(5.f, last_acked_event().data.pinchUpdate.scale);
+ EXPECT_EQ(1U, GestureEventQueueSize());
+ EXPECT_EQ(1U, GetAndResetSentGestureEventCount());
+
+ SendInputEventACK(WebInputEvent::GesturePinchUpdate,
+ INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_EQ(WebInputEvent::GesturePinchUpdate, last_acked_event().type);
+ EXPECT_EQ(6.f, last_acked_event().data.pinchUpdate.scale);
+ EXPECT_EQ(0U, GestureEventQueueSize());
+}
+
+TEST_F(GestureEventQueueTest, CoalescesScrollAndPinchEventsIdentity) {
+ // Insert an event to force queueing of gestures.
+ SimulateGestureEvent(WebInputEvent::GestureTapCancel,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(1U, GetAndResetSentGestureEventCount());
+ EXPECT_EQ(1U, GestureEventQueueSize());
+
+ // Ensure that coalescing yields an identity transform for any pinch/scroll
+ // pair combined with its inverse.
+ SimulateGestureScrollUpdateEvent(5, 5, 1);
+ EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
+ EXPECT_EQ(2U, GestureEventQueueSize());
+
+ SimulateGesturePinchUpdateEvent(5, 10, 10, 1);
+ EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
+ EXPECT_EQ(3U, GestureEventQueueSize());
+
+ SimulateGesturePinchUpdateEvent(.2f, 10, 10, 1);
+ EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
+ EXPECT_EQ(3U, GestureEventQueueSize());
+
+ SimulateGestureScrollUpdateEvent(-5, -5, 1);
+ EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
+ EXPECT_EQ(3U, GestureEventQueueSize());
+
+ SendInputEventACK(WebInputEvent::GestureTapCancel,
+ INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_EQ(2U, GetAndResetSentGestureEventCount());
+ EXPECT_EQ(2U, GestureEventQueueSize());
+
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_EQ(WebInputEvent::GestureScrollUpdate, last_acked_event().type);
+ EXPECT_EQ(0.f, last_acked_event().data.scrollUpdate.deltaX);
+ EXPECT_EQ(0.f, last_acked_event().data.scrollUpdate.deltaY);
+
+ SendInputEventACK(WebInputEvent::GesturePinchUpdate,
+ INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_EQ(WebInputEvent::GesturePinchUpdate, last_acked_event().type);
+ EXPECT_EQ(1.f, last_acked_event().data.pinchUpdate.scale);
+ EXPECT_EQ(0U, GestureEventQueueSize());
+
+ // Insert an event to force queueing of gestures.
+ SimulateGestureEvent(WebInputEvent::GestureTapCancel,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(1U, GetAndResetSentGestureEventCount());
+ EXPECT_EQ(1U, GestureEventQueueSize());
+
+ // Ensure that coalescing yields an identity transform for any pinch/scroll
+ // pair combined with its inverse.
+ SimulateGesturePinchUpdateEvent(2, 10, 10, 1);
+ EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
+ EXPECT_EQ(2U, GestureEventQueueSize());
+
+ SimulateGestureScrollUpdateEvent(20, 20, 1);
+ EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
+ EXPECT_EQ(3U, GestureEventQueueSize());
+
+ SimulateGesturePinchUpdateEvent(0.5f, 20, 20, 1);
+ EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
+ EXPECT_EQ(3U, GestureEventQueueSize());
+
+ SimulateGestureScrollUpdateEvent(-5, -5, 1);
+ EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
+ EXPECT_EQ(3U, GestureEventQueueSize());
+
+ SendInputEventACK(WebInputEvent::GestureTapCancel,
+ INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_EQ(2U, GetAndResetSentGestureEventCount());
+ EXPECT_EQ(2U, GestureEventQueueSize());
+
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_EQ(WebInputEvent::GestureScrollUpdate, last_acked_event().type);
+ EXPECT_EQ(0.f, last_acked_event().data.scrollUpdate.deltaX);
+ EXPECT_EQ(0.f, last_acked_event().data.scrollUpdate.deltaY);
+
+ SendInputEventACK(WebInputEvent::GesturePinchUpdate,
+ INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_EQ(WebInputEvent::GesturePinchUpdate, last_acked_event().type);
+ EXPECT_EQ(1.f, last_acked_event().data.pinchUpdate.scale);
+}
+
// Tests a single event with an synchronous ack.
-TEST_F(GestureEventFilterTest, SimpleSyncAck) {
+TEST_F(GestureEventQueueTest, SimpleSyncAck) {
set_synchronous_ack(INPUT_EVENT_ACK_STATE_CONSUMED);
SimulateGestureEvent(WebInputEvent::GestureTapDown,
- WebGestureEvent::Touchscreen);
+ blink::WebGestureDeviceTouchscreen);
EXPECT_EQ(1U, GetAndResetSentGestureEventCount());
EXPECT_EQ(0U, GestureEventQueueSize());
EXPECT_EQ(1U, GetAndResetAckedGestureEventCount());
}
// Tests an event with an synchronous ack which enqueues an additional event.
-TEST_F(GestureEventFilterTest, SyncAckQueuesEvent) {
+TEST_F(GestureEventQueueTest, SyncAckQueuesEvent) {
scoped_ptr<WebGestureEvent> queued_event;
set_synchronous_ack(INPUT_EVENT_ACK_STATE_CONSUMED);
set_sync_followup_event(WebInputEvent::GestureShowPress,
- WebGestureEvent::Touchscreen);
+ blink::WebGestureDeviceTouchscreen);
// This event enqueues the show press event.
SimulateGestureEvent(WebInputEvent::GestureTapDown,
- WebGestureEvent::Touchscreen);
+ blink::WebGestureDeviceTouchscreen);
EXPECT_EQ(2U, GetAndResetSentGestureEventCount());
EXPECT_EQ(1U, GestureEventQueueSize());
EXPECT_EQ(1U, GetAndResetAckedGestureEventCount());
@@ -625,19 +890,16 @@ TEST_F(GestureEventFilterTest, SyncAckQueuesEvent) {
}
// Tests an event with an async ack followed by an event with a sync ack.
-TEST_F(GestureEventFilterTest, AsyncThenSyncAck) {
- // Turn off debounce handling for test isolation.
- DisableDebounce();
-
+TEST_F(GestureEventQueueTest, AsyncThenSyncAck) {
SimulateGestureEvent(WebInputEvent::GestureTapDown,
- WebGestureEvent::Touchscreen);
+ blink::WebGestureDeviceTouchscreen);
EXPECT_EQ(1U, GetAndResetSentGestureEventCount());
EXPECT_EQ(1U, GestureEventQueueSize());
EXPECT_EQ(0U, GetAndResetAckedGestureEventCount());
SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
- WebGestureEvent::Touchscreen);
+ blink::WebGestureDeviceTouchscreen);
set_synchronous_ack(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
EXPECT_EQ(2U, GestureEventQueueSize());
@@ -650,16 +912,13 @@ TEST_F(GestureEventFilterTest, AsyncThenSyncAck) {
EXPECT_EQ(2U, GetAndResetAckedGestureEventCount());
}
-TEST_F(GestureEventFilterTest, CoalescesScrollAndPinchEventWithSyncAck) {
- // Turn off debounce handling for test isolation.
- DisableDebounce();
-
+TEST_F(GestureEventQueueTest, CoalescesScrollAndPinchEventWithSyncAck) {
// Simulate a pinch sequence.
SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
- WebGestureEvent::Touchscreen);
+ blink::WebGestureDeviceTouchscreen);
EXPECT_EQ(1U, GetAndResetSentGestureEventCount());
SimulateGestureEvent(WebInputEvent::GesturePinchBegin,
- WebGestureEvent::Touchscreen);
+ blink::WebGestureDeviceTouchscreen);
EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
SimulateGestureScrollUpdateEvent(8, -4, 1);
@@ -697,11 +956,9 @@ TEST_F(GestureEventFilterTest, CoalescesScrollAndPinchEventWithSyncAck) {
}
#if GTEST_HAS_PARAM_TEST
-TEST_P(GestureEventFilterWithSourceTest, GestureFlingCancelsFiltered) {
- WebGestureEvent::SourceDevice source_device = GetParam();
+TEST_P(GestureEventQueueWithSourceTest, GestureFlingCancelsFiltered) {
+ WebGestureDevice source_device = GetParam();
- // Turn off debounce handling for test isolation.
- DisableDebounce();
// GFC without previous GFS is dropped.
SimulateGestureEvent(WebInputEvent::GestureFlingCancel, source_device);
EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
@@ -788,44 +1045,44 @@ TEST_P(GestureEventFilterWithSourceTest, GestureFlingCancelsFiltered) {
}
INSTANTIATE_TEST_CASE_P(AllSources,
- GestureEventFilterWithSourceTest,
- testing::Values(WebGestureEvent::Touchscreen,
- WebGestureEvent::Touchpad));
+ GestureEventQueueWithSourceTest,
+ testing::Values(blink::WebGestureDeviceTouchscreen,
+ blink::WebGestureDeviceTouchpad));
#endif // GTEST_HAS_PARAM_TEST
// Test that a GestureScrollEnd | GestureFlingStart are deferred during the
// debounce interval, that Scrolls are not and that the deferred events are
// sent after that timer fires.
-TEST_F(GestureEventFilterTest, DebounceDefersFollowingGestureEvents) {
- set_debounce_interval_time_ms(3);
+TEST_F(GestureEventQueueTest, DebounceDefersFollowingGestureEvents) {
+ SetUpForDebounce(3);
SimulateGestureEvent(WebInputEvent::GestureScrollUpdate,
- WebGestureEvent::Touchscreen);
+ blink::WebGestureDeviceTouchscreen);
EXPECT_EQ(1U, GetAndResetSentGestureEventCount());
EXPECT_EQ(1U, GestureEventQueueSize());
EXPECT_EQ(0U, GestureEventDebouncingQueueSize());
EXPECT_TRUE(ScrollingInProgress());
SimulateGestureEvent(WebInputEvent::GestureScrollUpdate,
- WebGestureEvent::Touchscreen);
+ blink::WebGestureDeviceTouchscreen);
EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
EXPECT_EQ(2U, GestureEventQueueSize());
EXPECT_EQ(0U, GestureEventDebouncingQueueSize());
EXPECT_TRUE(ScrollingInProgress());
SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
- WebGestureEvent::Touchscreen);
+ blink::WebGestureDeviceTouchscreen);
EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
EXPECT_EQ(2U, GestureEventQueueSize());
EXPECT_EQ(1U, GestureEventDebouncingQueueSize());
- SimulateGestureFlingStartEvent(0, 10, WebGestureEvent::Touchscreen);
+ SimulateGestureFlingStartEvent(0, 10, blink::WebGestureDeviceTouchscreen);
EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
EXPECT_EQ(2U, GestureEventQueueSize());
EXPECT_EQ(2U, GestureEventDebouncingQueueSize());
SimulateGestureEvent(WebInputEvent::GestureTapDown,
- WebGestureEvent::Touchscreen);
+ blink::WebGestureDeviceTouchscreen);
EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
EXPECT_EQ(2U, GestureEventQueueSize());
EXPECT_EQ(3U, GestureEventDebouncingQueueSize());
@@ -859,12 +1116,13 @@ TEST_F(GestureEventFilterTest, DebounceDefersFollowingGestureEvents) {
// Test that non-scroll events are deferred while scrolling during the debounce
// interval and are discarded if a GestureScrollUpdate event arrives before the
// interval end.
-TEST_F(GestureEventFilterTest, DebounceDropsDeferredEvents) {
- set_debounce_interval_time_ms(3);
+TEST_F(GestureEventQueueTest, DebounceDropsDeferredEvents) {
+ SetUpForDebounce(3);
+
EXPECT_FALSE(ScrollingInProgress());
SimulateGestureEvent(WebInputEvent::GestureScrollUpdate,
- WebGestureEvent::Touchscreen);
+ blink::WebGestureDeviceTouchscreen);
EXPECT_EQ(1U, GetAndResetSentGestureEventCount());
EXPECT_EQ(1U, GestureEventQueueSize());
EXPECT_EQ(0U, GestureEventDebouncingQueueSize());
@@ -872,13 +1130,13 @@ TEST_F(GestureEventFilterTest, DebounceDropsDeferredEvents) {
// This event should get discarded.
SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
- WebGestureEvent::Touchscreen);
+ blink::WebGestureDeviceTouchscreen);
EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
EXPECT_EQ(1U, GestureEventQueueSize());
EXPECT_EQ(1U, GestureEventDebouncingQueueSize());
SimulateGestureEvent(WebInputEvent::GestureScrollUpdate,
- WebGestureEvent::Touchscreen);
+ blink::WebGestureDeviceTouchscreen);
EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
EXPECT_EQ(2U, GestureEventQueueSize());
EXPECT_EQ(0U, GestureEventDebouncingQueueSize());
@@ -896,17 +1154,4 @@ TEST_F(GestureEventFilterTest, DebounceDropsDeferredEvents) {
}
}
-TEST_F(GestureEventFilterTest, DropZeroVelocityFlings) {
- WebGestureEvent gesture_event;
- gesture_event.type = WebInputEvent::GestureFlingStart;
- gesture_event.sourceDevice = WebGestureEvent::Touchpad;
- gesture_event.data.flingStart.velocityX = 0.f;
- gesture_event.data.flingStart.velocityY = 0.f;
- ASSERT_EQ(0U, GetAndResetSentGestureEventCount());
- ASSERT_EQ(0U, GestureEventQueueSize());
- EXPECT_FALSE(SimulateGestureEvent(gesture_event));
- EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
- EXPECT_EQ(0U, GestureEventQueueSize());
-}
-
} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/input_ack_handler.h b/chromium/content/browser/renderer_host/input/input_ack_handler.h
index 7917bffa80d..0e34b824c84 100644
--- a/chromium/content/browser/renderer_host/input/input_ack_handler.h
+++ b/chromium/content/browser/renderer_host/input/input_ack_handler.h
@@ -6,8 +6,8 @@
#define CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_ACK_HANDLER_H_
#include "base/basictypes.h"
-#include "content/port/browser/event_with_latency_info.h"
-#include "content/port/common/input_event_ack_state.h"
+#include "content/browser/renderer_host/event_with_latency_info.h"
+#include "content/common/input/input_event_ack_state.h"
#include "content/public/browser/native_web_keyboard_event.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
diff --git a/chromium/content/browser/renderer_host/input/input_router.h b/chromium/content/browser/renderer_host/input/input_router.h
index 2dbb4ed00a6..1b30815c7ce 100644
--- a/chromium/content/browser/renderer_host/input/input_router.h
+++ b/chromium/content/browser/renderer_host/input/input_router.h
@@ -6,8 +6,8 @@
#define CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_ROUTER_H_
#include "base/basictypes.h"
-#include "content/port/browser/event_with_latency_info.h"
-#include "content/port/common/input_event_ack_state.h"
+#include "content/browser/renderer_host/event_with_latency_info.h"
+#include "content/common/input/input_event_ack_state.h"
#include "content/public/browser/native_web_keyboard_event.h"
#include "ipc/ipc_listener.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
@@ -63,6 +63,8 @@ class InputRouter : public IPC::Listener {
MOBILE_VIEWPORT = 1 << 1
};
virtual void OnViewUpdated(int view_flags) = 0;
+
+ virtual bool HasPendingEvents() const = 0;
};
} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/input_router_client.h b/chromium/content/browser/renderer_host/input/input_router_client.h
index fefcb10c7c2..3b0e6a9c351 100644
--- a/chromium/content/browser/renderer_host/input/input_router_client.h
+++ b/chromium/content/browser/renderer_host/input/input_router_client.h
@@ -5,9 +5,9 @@
#ifndef CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_ROUTER_CLIENT_H_
#define CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_ROUTER_CLIENT_H_
+#include "content/browser/renderer_host/event_with_latency_info.h"
#include "content/common/content_export.h"
-#include "content/port/browser/event_with_latency_info.h"
-#include "content/port/common/input_event_ack_state.h"
+#include "content/common/input/input_event_ack_state.h"
#include "content/public/browser/native_web_keyboard_event.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
@@ -17,7 +17,7 @@ struct LatencyInfo;
namespace content {
-class OverscrollController;
+struct DidOverscrollParams;
class CONTENT_EXPORT InputRouterClient {
public:
@@ -42,11 +42,6 @@ class CONTENT_EXPORT InputRouterClient {
// Called when the renderer notifies that it has touch event handlers.
virtual void OnHasTouchEventHandlers(bool has_handlers) = 0;
- // Returns an optional OverscrollController. If non-NULL, the controller
- // will be fed events and event acks by the router, when appropriate.
- // TODO(jdduke): crbug.com/306133 - Move the controller to the router.
- virtual OverscrollController* GetOverscrollController() const = 0;
-
// Certain router implementations require periodic flushing of queued events.
// When this method is called, the client should ensure a timely call, either
// synchronous or asynchronous, of |Flush| on the InputRouter.
@@ -56,6 +51,10 @@ class CONTENT_EXPORT InputRouterClient {
// of the call to Flush. The call will typically be asynchronous with
// respect to the call to |Flush| on the InputRouter.
virtual void DidFlush() = 0;
+
+ // Called when the router has received an overscroll notification from the
+ // renderer.
+ virtual void DidOverscroll(const DidOverscrollParams& params) = 0;
};
} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/input_router_config_helper.cc b/chromium/content/browser/renderer_host/input/input_router_config_helper.cc
new file mode 100644
index 00000000000..31b17cfab70
--- /dev/null
+++ b/chromium/content/browser/renderer_host/input/input_router_config_helper.cc
@@ -0,0 +1,140 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/input/input_router_config_helper.h"
+
+#include "base/command_line.h"
+#include "content/public/common/content_switches.h"
+#include "ui/events/gesture_detection/gesture_detector.h"
+
+#if defined(USE_AURA)
+#include "ui/events/gestures/gesture_configuration.h"
+#include "ui/events/gestures/unified_gesture_detector_enabled.h"
+#elif defined(OS_ANDROID)
+#include "ui/gfx/android/view_configuration.h"
+#include "ui/gfx/screen.h"
+#endif
+
+namespace content {
+namespace {
+
+#if defined(USE_AURA)
+
+GestureEventQueue::Config GetGestureEventQueueConfig() {
+ GestureEventQueue::Config config;
+
+ config.debounce_interval = base::TimeDelta::FromMilliseconds(
+ ui::GestureConfiguration::scroll_debounce_interval_in_ms());
+
+ config.touchscreen_tap_suppression_config.enabled = true;
+ config.touchscreen_tap_suppression_config.max_cancel_to_down_time =
+ base::TimeDelta::FromMilliseconds(
+ ui::GestureConfiguration::fling_max_cancel_to_down_time_in_ms());
+
+ config.touchscreen_tap_suppression_config.max_tap_gap_time =
+ base::TimeDelta::FromMilliseconds(static_cast<int>(
+ ui::GestureConfiguration::semi_long_press_time_in_seconds() * 1000));
+
+ config.touchpad_tap_suppression_config.enabled = true;
+ config.touchpad_tap_suppression_config.max_cancel_to_down_time =
+ base::TimeDelta::FromMilliseconds(
+ ui::GestureConfiguration::fling_max_cancel_to_down_time_in_ms());
+
+ config.touchpad_tap_suppression_config.max_tap_gap_time =
+ base::TimeDelta::FromMilliseconds(static_cast<int>(
+ ui::GestureConfiguration::fling_max_tap_gap_time_in_ms() * 1000));
+
+ return config;
+}
+
+TouchEventQueue::Config GetTouchEventQueueConfig() {
+ TouchEventQueue::Config config;
+
+ config.touchmove_slop_suppression_length_dips =
+ ui::GestureConfiguration::max_touch_move_in_pixels_for_click();
+
+ config.touchmove_slop_suppression_region_includes_boundary =
+ ui::IsUnifiedGestureDetectorEnabled();
+
+ return config;
+}
+
+#elif defined(OS_ANDROID)
+
+// Default time allowance for the touch ack delay before the touch sequence is
+// cancelled.
+const int kTouchAckTimeoutDelayMs = 200;
+
+GestureEventQueue::Config GetGestureEventQueueConfig() {
+ GestureEventQueue::Config config;
+
+ config.touchscreen_tap_suppression_config.enabled = true;
+ config.touchscreen_tap_suppression_config.max_cancel_to_down_time =
+ base::TimeDelta::FromMilliseconds(
+ gfx::ViewConfiguration::GetTapTimeoutInMs());
+ config.touchscreen_tap_suppression_config.max_tap_gap_time =
+ base::TimeDelta::FromMilliseconds(
+ gfx::ViewConfiguration::GetLongPressTimeoutInMs());
+
+ return config;
+}
+
+TouchEventQueue::Config GetTouchEventQueueConfig() {
+ TouchEventQueue::Config config;
+
+ config.touch_ack_timeout_delay =
+ base::TimeDelta::FromMilliseconds(kTouchAckTimeoutDelayMs);
+ config.touch_ack_timeout_supported = true;
+
+ const double touch_slop_length_pixels =
+ static_cast<double>(gfx::ViewConfiguration::GetTouchSlopInPixels());
+ const double device_scale_factor =
+ gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().device_scale_factor();
+ config.touchmove_slop_suppression_length_dips =
+ touch_slop_length_pixels / device_scale_factor;
+
+ return config;
+}
+
+#else
+
+GestureEventQueue::Config GetGestureEventQueueConfig() {
+ return GestureEventQueue::Config();
+}
+
+TouchEventQueue::Config GetTouchEventQueueConfig() {
+ TouchEventQueue::Config config;
+ config.touchmove_slop_suppression_length_dips =
+ ui::GestureDetector::Config().touch_slop;
+ return config;
+}
+
+#endif
+
+TouchEventQueue::TouchScrollingMode GetTouchScrollingMode() {
+ std::string modeString =
+ CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kTouchScrollingMode);
+ if (modeString == switches::kTouchScrollingModeAsyncTouchmove)
+ return TouchEventQueue::TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE;
+ if (modeString == switches::kTouchScrollingModeSyncTouchmove)
+ return TouchEventQueue::TOUCH_SCROLLING_MODE_SYNC_TOUCHMOVE;
+ if (modeString == switches::kTouchScrollingModeTouchcancel)
+ return TouchEventQueue::TOUCH_SCROLLING_MODE_TOUCHCANCEL;
+ if (modeString != "")
+ LOG(ERROR) << "Invalid --touch-scrolling-mode option: " << modeString;
+ return TouchEventQueue::TOUCH_SCROLLING_MODE_DEFAULT;
+}
+
+} // namespace
+
+InputRouterImpl::Config GetInputRouterConfigForPlatform() {
+ InputRouterImpl::Config config;
+ config.gesture_config = GetGestureEventQueueConfig();
+ config.touch_config = GetTouchEventQueueConfig();
+ config.touch_config.touch_scrolling_mode = GetTouchScrollingMode();
+ return config;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/input_router_config_helper.h b/chromium/content/browser/renderer_host/input/input_router_config_helper.h
new file mode 100644
index 00000000000..46fbbd09118
--- /dev/null
+++ b/chromium/content/browser/renderer_host/input/input_router_config_helper.h
@@ -0,0 +1,18 @@
+// 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_RENDERER_HOST_INPUT_INPUT_ROUTER_CONFIG_HELPER_H_
+#define CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_ROUTER_CONFIG_HELPER_H_
+
+#include "content/browser/renderer_host/input/input_router_impl.h"
+
+namespace content {
+
+// Return an InputRouter configuration with parameters tailored to the current
+// platform.
+InputRouterImpl::Config GetInputRouterConfigForPlatform();
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_ROUTER_CONFIG_HELPER_H_
diff --git a/chromium/content/browser/renderer_host/input/input_router_impl.cc b/chromium/content/browser/renderer_host/input/input_router_impl.cc
index 8e498fd5e46..a509465a653 100644
--- a/chromium/content/browser/renderer_host/input/input_router_impl.cc
+++ b/chromium/content/browser/renderer_host/input/input_router_impl.cc
@@ -4,23 +4,24 @@
#include "content/browser/renderer_host/input/input_router_impl.h"
+#include <math.h>
+
#include "base/auto_reset.h"
#include "base/command_line.h"
#include "base/metrics/histogram.h"
#include "base/strings/string_number_conversions.h"
-#include "content/browser/renderer_host/input/gesture_event_filter.h"
+#include "content/browser/renderer_host/input/gesture_event_queue.h"
#include "content/browser/renderer_host/input/input_ack_handler.h"
#include "content/browser/renderer_host/input/input_router_client.h"
#include "content/browser/renderer_host/input/touch_event_queue.h"
#include "content/browser/renderer_host/input/touchpad_tap_suppression_controller.h"
-#include "content/browser/renderer_host/overscroll_controller.h"
#include "content/common/content_constants_internal.h"
#include "content/common/edit_command.h"
+#include "content/common/input/input_event_ack_state.h"
#include "content/common/input/touch_action.h"
-#include "content/common/input/web_input_event_traits.h"
+#include "content/common/input/web_touch_event_traits.h"
#include "content/common/input_messages.h"
#include "content/common/view_messages.h"
-#include "content/port/common/input_event_ack_state.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/user_metrics.h"
@@ -41,39 +42,6 @@ using blink::WebMouseWheelEvent;
namespace content {
namespace {
-bool GetTouchAckTimeoutDelayMs(size_t* touch_ack_timeout_delay_ms) {
- CommandLine* parsed_command_line = CommandLine::ForCurrentProcess();
- if (!parsed_command_line->HasSwitch(switches::kTouchAckTimeoutDelayMs))
- return false;
-
- std::string timeout_string = parsed_command_line->GetSwitchValueASCII(
- switches::kTouchAckTimeoutDelayMs);
- size_t timeout_value;
- if (!base::StringToSizeT(timeout_string, &timeout_value))
- return false;
-
- *touch_ack_timeout_delay_ms = timeout_value;
- return true;
-}
-
-GestureEventWithLatencyInfo MakeGestureEvent(WebInputEvent::Type type,
- double timestamp_seconds,
- int x,
- int y,
- int modifiers,
- const ui::LatencyInfo latency) {
- WebGestureEvent result;
-
- result.type = type;
- result.x = x;
- result.y = y;
- result.sourceDevice = WebGestureEvent::Touchscreen;
- result.timeStampSeconds = timestamp_seconds;
- result.modifiers = modifiers;
-
- return GestureEventWithLatencyInfo(result, latency);
-}
-
const char* GetEventAckName(InputEventAckState ack_result) {
switch(ack_result) {
case INPUT_EVENT_ACK_STATE_UNKNOWN: return "UNKNOWN";
@@ -88,10 +56,14 @@ const char* GetEventAckName(InputEventAckState ack_result) {
} // namespace
+InputRouterImpl::Config::Config() {
+}
+
InputRouterImpl::InputRouterImpl(IPC::Sender* sender,
InputRouterClient* client,
InputAckHandler* ack_handler,
- int routing_id)
+ int routing_id,
+ const Config& config)
: sender_(sender),
client_(client),
ack_handler_(ack_handler),
@@ -100,24 +72,23 @@ InputRouterImpl::InputRouterImpl(IPC::Sender* sender,
move_caret_pending_(false),
mouse_move_pending_(false),
mouse_wheel_pending_(false),
- has_touch_handler_(false),
- touch_ack_timeout_enabled_(false),
- touch_ack_timeout_delay_ms_(std::numeric_limits<size_t>::max()),
+ current_view_flags_(0),
current_ack_source_(ACK_SOURCE_NONE),
- gesture_event_filter_(new GestureEventFilter(this, this)) {
+ flush_requested_(false),
+ touch_event_queue_(this, config.touch_config),
+ gesture_event_queue_(this, this, config.gesture_config) {
DCHECK(sender);
DCHECK(client);
DCHECK(ack_handler);
- touch_event_queue_.reset(new TouchEventQueue(this));
- touch_ack_timeout_enabled_ =
- GetTouchAckTimeoutDelayMs(&touch_ack_timeout_delay_ms_);
- touch_event_queue_->SetAckTimeoutEnabled(touch_ack_timeout_enabled_,
- touch_ack_timeout_delay_ms_);
+ UpdateTouchAckTimeoutEnabled();
}
InputRouterImpl::~InputRouterImpl() {}
-void InputRouterImpl::Flush() {}
+void InputRouterImpl::Flush() {
+ flush_requested_ = true;
+ SignalFlushedIfNecessary();
+}
bool InputRouterImpl::SendInput(scoped_ptr<IPC::Message> message) {
DCHECK(IPC_MESSAGE_ID_CLASS(message->type()) == InputMsgStart);
@@ -137,20 +108,12 @@ bool InputRouterImpl::SendInput(scoped_ptr<IPC::Message> message) {
void InputRouterImpl::SendMouseEvent(
const MouseEventWithLatencyInfo& mouse_event) {
- // Order is important here; we need to convert all MouseEvents before they
- // propagate further, e.g., to the tap suppression controller.
- if (CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kSimulateTouchScreenWithMouse)) {
- SimulateTouchGestureWithMouse(mouse_event);
- return;
- }
-
if (mouse_event.event.type == WebInputEvent::MouseDown &&
- gesture_event_filter_->GetTouchpadTapSuppressionController()->
+ gesture_event_queue_.GetTouchpadTapSuppressionController()->
ShouldDeferMouseDown(mouse_event))
return;
if (mouse_event.event.type == WebInputEvent::MouseUp &&
- gesture_event_filter_->GetTouchpadTapSuppressionController()->
+ gesture_event_queue_.GetTouchpadTapSuppressionController()->
ShouldSuppressMouseUp())
return;
@@ -159,26 +122,39 @@ void InputRouterImpl::SendMouseEvent(
void InputRouterImpl::SendWheelEvent(
const MouseWheelEventWithLatencyInfo& wheel_event) {
- // If there's already a mouse wheel event waiting to be sent to the renderer,
- // add the new deltas to that event. Not doing so (e.g., by dropping the old
- // event, as for mouse moves) results in very slow scrolling on the Mac (on
- // which many, very small wheel events are sent).
+ SendWheelEvent(QueuedWheelEvent(wheel_event, false));
+}
+
+void InputRouterImpl::SendWheelEvent(const QueuedWheelEvent& wheel_event) {
if (mouse_wheel_pending_) {
+ // If there's already a mouse wheel event waiting to be sent to the
+ // renderer, add the new deltas to that event. Not doing so (e.g., by
+ // dropping the old event, as for mouse moves) results in very slow
+ // scrolling on the Mac (on which many, very small wheel events are sent).
+ // Note that we can't coalesce wheel events for pinches because the GEQ
+ // expects one ACK for each (but it's fine to coalesce non-pinch wheels
+ // into a pinch one). Note that the GestureEventQueue ensures we only
+ // ever have a single pinch event queued here.
if (coalesced_mouse_wheel_events_.empty() ||
- !coalesced_mouse_wheel_events_.back().CanCoalesceWith(wheel_event)) {
+ wheel_event.synthesized_from_pinch ||
+ !coalesced_mouse_wheel_events_.back().event.CanCoalesceWith(
+ wheel_event.event)) {
coalesced_mouse_wheel_events_.push_back(wheel_event);
} else {
- coalesced_mouse_wheel_events_.back().CoalesceWith(wheel_event);
+ coalesced_mouse_wheel_events_.back().event.CoalesceWith(
+ wheel_event.event);
}
return;
}
+
mouse_wheel_pending_ = true;
current_wheel_event_ = wheel_event;
HISTOGRAM_COUNTS_100("Renderer.WheelQueueSize",
coalesced_mouse_wheel_events_.size());
- FilterAndSendWebInputEvent(wheel_event.event, wheel_event.latency, false);
+ FilterAndSendWebInputEvent(
+ wheel_event.event.event, wheel_event.event.latency, false);
}
void InputRouterImpl::SendKeyboardEvent(const NativeWebKeyboardEvent& key_event,
@@ -190,33 +166,34 @@ void InputRouterImpl::SendKeyboardEvent(const NativeWebKeyboardEvent& key_event,
key_queue_.push_back(key_event);
HISTOGRAM_COUNTS_100("Renderer.KeyboardQueueSize", key_queue_.size());
- gesture_event_filter_->FlingHasBeenHalted();
+ gesture_event_queue_.FlingHasBeenHalted();
// Only forward the non-native portions of our event.
FilterAndSendWebInputEvent(key_event, latency_info, is_keyboard_shortcut);
}
void InputRouterImpl::SendGestureEvent(
- const GestureEventWithLatencyInfo& gesture_event) {
- if (touch_action_filter_.FilterGestureEvent(gesture_event.event))
+ const GestureEventWithLatencyInfo& original_gesture_event) {
+ input_stream_validator_.Validate(original_gesture_event.event);
+
+ GestureEventWithLatencyInfo gesture_event(original_gesture_event);
+
+ if (touch_action_filter_.FilterGestureEvent(&gesture_event.event))
return;
- touch_event_queue_->OnGestureScrollEvent(gesture_event);
+ if (gesture_event.event.sourceDevice == blink::WebGestureDeviceTouchscreen)
+ touch_event_queue_.OnGestureScrollEvent(gesture_event);
- if (!IsInOverscrollGesture() &&
- !gesture_event_filter_->ShouldForward(gesture_event)) {
- OverscrollController* controller = client_->GetOverscrollController();
- if (controller)
- controller->DiscardingGestureEvent(gesture_event.event);
+ if (!gesture_event_queue_.ShouldForward(gesture_event))
return;
- }
- FilterAndSendWebInputEvent(gesture_event.event, gesture_event.latency, false);
+ SendGestureEventImmediately(gesture_event);
}
void InputRouterImpl::SendTouchEvent(
const TouchEventWithLatencyInfo& touch_event) {
- touch_event_queue_->QueueEvent(touch_event);
+ input_stream_validator_.Validate(touch_event.event);
+ touch_event_queue_.QueueEvent(touch_event);
}
// Forwards MouseEvent without passing it through
@@ -243,11 +220,26 @@ void InputRouterImpl::SendMouseEventImmediately(
void InputRouterImpl::SendTouchEventImmediately(
const TouchEventWithLatencyInfo& touch_event) {
+ if (WebTouchEventTraits::IsTouchSequenceStart(touch_event.event)) {
+ touch_action_filter_.ResetTouchAction();
+ // Note that if the previous touch-action was TOUCH_ACTION_NONE, enabling
+ // the timeout here will not take effect until the *following* touch
+ // sequence. This is a desirable side-effect, giving the renderer a chance
+ // to send a touch-action response without racing against the ack timeout.
+ UpdateTouchAckTimeoutEnabled();
+ }
+
FilterAndSendWebInputEvent(touch_event.event, touch_event.latency, false);
}
void InputRouterImpl::SendGestureEventImmediately(
const GestureEventWithLatencyInfo& gesture_event) {
+ if (gesture_event.event.type == WebInputEvent::GesturePinchUpdate &&
+ gesture_event.event.sourceDevice == blink::WebGestureDeviceTouchpad) {
+ SendSyntheticWheelEventForPinch(gesture_event);
+ return;
+ }
+
FilterAndSendWebInputEvent(gesture_event.event, gesture_event.latency, false);
}
@@ -258,26 +250,23 @@ const NativeWebKeyboardEvent* InputRouterImpl::GetLastKeyboardEvent() const {
}
bool InputRouterImpl::ShouldForwardTouchEvent() const {
- // Always send a touch event if the renderer has a touch-event handler. It is
- // possible that a renderer stops listening to touch-events while there are
- // still events in the touch-queue. In such cases, the new events should still
- // get into the queue.
- return has_touch_handler_ || !touch_event_queue_->empty();
+ // Always send a touch event if the renderer has a touch-event handler or
+ // there are pending touch events.
+ return touch_event_queue_.has_handlers() || !touch_event_queue_.empty();
}
void InputRouterImpl::OnViewUpdated(int view_flags) {
- bool fixed_page_scale = (view_flags & FIXED_PAGE_SCALE) != 0;
- bool mobile_viewport = (view_flags & MOBILE_VIEWPORT) != 0;
- touch_event_queue_->SetAckTimeoutEnabled(
- touch_ack_timeout_enabled_ && !(fixed_page_scale || mobile_viewport),
- touch_ack_timeout_delay_ms_);
+ current_view_flags_ = view_flags;
+
+ // A fixed page scale or mobile viewport should disable the touch ack timeout.
+ UpdateTouchAckTimeoutEnabled();
}
bool InputRouterImpl::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
- bool message_is_ok = true;
- IPC_BEGIN_MESSAGE_MAP_EX(InputRouterImpl, message, message_is_ok)
+ IPC_BEGIN_MESSAGE_MAP(InputRouterImpl, message)
IPC_MESSAGE_HANDLER(InputHostMsg_HandleInputEvent_ACK, OnInputEventAck)
+ IPC_MESSAGE_HANDLER(InputHostMsg_DidOverscroll, OnDidOverscroll)
IPC_MESSAGE_HANDLER(ViewHostMsg_MoveCaret_ACK, OnMsgMoveCaretAck)
IPC_MESSAGE_HANDLER(ViewHostMsg_SelectRange_ACK, OnSelectRangeAck)
IPC_MESSAGE_HANDLER(ViewHostMsg_HasTouchEventHandlers,
@@ -287,21 +276,25 @@ bool InputRouterImpl::OnMessageReceived(const IPC::Message& message) {
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
- if (!message_is_ok)
- ack_handler_->OnUnexpectedEventAck(InputAckHandler::BAD_ACK_MESSAGE);
-
return handled;
}
void InputRouterImpl::OnTouchEventAck(const TouchEventWithLatencyInfo& event,
InputEventAckState ack_result) {
+ // Touchstart events sent to the renderer indicate a new touch sequence, but
+ // in some cases we may filter out sending the touchstart - catch those here.
+ if (WebTouchEventTraits::IsTouchSequenceStart(event.event) &&
+ ack_result == INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS) {
+ touch_action_filter_.ResetTouchAction();
+ UpdateTouchAckTimeoutEnabled();
+ }
ack_handler_->OnTouchEventAck(event, ack_result);
}
void InputRouterImpl::OnGestureEventAck(
const GestureEventWithLatencyInfo& event,
InputEventAckState ack_result) {
- ProcessAckForOverscroll(event.event, ack_result);
+ touch_event_queue_.OnGestureEventAck(event, ack_result);
ack_handler_->OnGestureEventAck(event, ack_result);
}
@@ -340,19 +333,6 @@ void InputRouterImpl::FilterAndSendWebInputEvent(
"type",
WebInputEventTraits::GetName(input_event.type));
- // Transmit any pending wheel events on a non-wheel event. This ensures that
- // final PhaseEnded wheel event is received, which is necessary to terminate
- // rubber-banding, for example.
- if (input_event.type != WebInputEvent::MouseWheel) {
- WheelEventQueue mouse_wheel_events;
- mouse_wheel_events.swap(coalesced_mouse_wheel_events_);
- for (size_t i = 0; i < mouse_wheel_events.size(); ++i) {
- OfferToHandlers(mouse_wheel_events[i].event,
- mouse_wheel_events[i].latency,
- false);
- }
- }
-
// Any input event cancels a pending mouse move event.
next_mouse_move_.reset();
@@ -362,16 +342,23 @@ void InputRouterImpl::FilterAndSendWebInputEvent(
void InputRouterImpl::OfferToHandlers(const WebInputEvent& input_event,
const ui::LatencyInfo& latency_info,
bool is_keyboard_shortcut) {
- if (OfferToOverscrollController(input_event, latency_info))
- return;
+ output_stream_validator_.Validate(input_event);
if (OfferToClient(input_event, latency_info))
return;
OfferToRenderer(input_event, latency_info, is_keyboard_shortcut);
+ // Touch events should always indicate in the event whether they are
+ // cancelable (respect ACK disposition) or not.
+ bool ignores_ack = WebInputEventTraits::IgnoresAckDisposition(input_event);
+ if (WebInputEvent::isTouchEventType(input_event.type)) {
+ DCHECK(!ignores_ack ==
+ static_cast<const blink::WebTouchEvent&>(input_event).cancelable);
+ }
+
// If we don't care about the ack disposition, send the ack immediately.
- if (WebInputEventTraits::IgnoresAckDisposition(input_event.type)) {
+ if (ignores_ack) {
ProcessInputEventAck(input_event.type,
INPUT_EVENT_ACK_STATE_IGNORED,
latency_info,
@@ -379,41 +366,6 @@ void InputRouterImpl::OfferToHandlers(const WebInputEvent& input_event,
}
}
-bool InputRouterImpl::OfferToOverscrollController(
- const WebInputEvent& input_event,
- const ui::LatencyInfo& latency_info) {
- OverscrollController* controller = client_->GetOverscrollController();
- if (!controller)
- return false;
-
- OverscrollController::Disposition disposition =
- controller->DispatchEvent(input_event, latency_info);
-
- bool consumed = disposition == OverscrollController::CONSUMED;
-
- if (disposition == OverscrollController::SHOULD_FORWARD_TO_GESTURE_FILTER) {
- DCHECK(WebInputEvent::isGestureEventType(input_event.type));
- const blink::WebGestureEvent& gesture_event =
- static_cast<const blink::WebGestureEvent&>(input_event);
- // An ACK is expected for the event, so mark it as consumed.
- consumed = !gesture_event_filter_->ShouldForward(
- GestureEventWithLatencyInfo(gesture_event, latency_info));
- }
-
- if (consumed) {
- InputEventAckState overscroll_ack =
- WebInputEvent::isTouchEventType(input_event.type) ?
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED : INPUT_EVENT_ACK_STATE_CONSUMED;
- ProcessInputEventAck(input_event.type,
- overscroll_ack,
- latency_info,
- OVERSCROLL_CONTROLLER);
- // WARNING: |this| may be deleted at this point.
- }
-
- return consumed;
-}
-
bool InputRouterImpl::OfferToClient(const WebInputEvent& input_event,
const ui::LatencyInfo& latency_info) {
bool consumed = false;
@@ -443,32 +395,72 @@ bool InputRouterImpl::OfferToClient(const WebInputEvent& input_event,
bool InputRouterImpl::OfferToRenderer(const WebInputEvent& input_event,
const ui::LatencyInfo& latency_info,
bool is_keyboard_shortcut) {
- input_event_start_time_ = TimeTicks::Now();
if (Send(new InputMsg_HandleInputEvent(
routing_id(), &input_event, latency_info, is_keyboard_shortcut))) {
- // Only increment the event count if we require an ACK for |input_event|.
- if (!WebInputEventTraits::IgnoresAckDisposition(input_event.type))
+ // Ack messages for ignored ack event types should never be sent by the
+ // renderer. Consequently, such event types should not affect event time
+ // or in-flight event count metrics.
+ if (!WebInputEventTraits::IgnoresAckDisposition(input_event)) {
+ input_event_start_time_ = TimeTicks::Now();
client_->IncrementInFlightEventCount();
+ }
return true;
}
return false;
}
-void InputRouterImpl::OnInputEventAck(WebInputEvent::Type event_type,
- InputEventAckState ack_result,
- const ui::LatencyInfo& latency_info) {
+void InputRouterImpl::SendSyntheticWheelEventForPinch(
+ const GestureEventWithLatencyInfo& pinch_event) {
+ // We match typical trackpad behavior on Windows by sending fake wheel events
+ // with the ctrl modifier set when we see trackpad pinch gestures. Ideally
+ // we'd someday get a standard 'pinch' event and send that instead.
+
+ WebMouseWheelEvent wheelEvent;
+ wheelEvent.type = WebInputEvent::MouseWheel;
+ wheelEvent.timeStampSeconds = pinch_event.event.timeStampSeconds;
+ wheelEvent.windowX = wheelEvent.x = pinch_event.event.x;
+ wheelEvent.windowY = wheelEvent.y = pinch_event.event.y;
+ wheelEvent.globalX = pinch_event.event.globalX;
+ wheelEvent.globalY = pinch_event.event.globalY;
+ wheelEvent.modifiers =
+ pinch_event.event.modifiers | WebInputEvent::ControlKey;
+ wheelEvent.deltaX = 0;
+ // The function to convert scales to deltaY values is designed to be
+ // compatible with websites existing use of wheel events, and with existing
+ // Windows trackpad behavior. In particular, we want:
+ // - deltas should accumulate via addition: f(s1*s2)==f(s1)+f(s2)
+ // - deltas should invert via negation: f(1/s) == -f(s)
+ // - zoom in should be positive: f(s) > 0 iff s > 1
+ // - magnitude roughly matches wheels: f(2) > 25 && f(2) < 100
+ // - a formula that's relatively easy to use from JavaScript
+ // Note that 'wheel' event deltaY values have their sign inverted. So to
+ // convert a wheel deltaY back to a scale use Math.exp(-deltaY/100).
+ DCHECK_GT(pinch_event.event.data.pinchUpdate.scale, 0);
+ wheelEvent.deltaY = 100.0f * log(pinch_event.event.data.pinchUpdate.scale);
+ wheelEvent.hasPreciseScrollingDeltas = true;
+ wheelEvent.wheelTicksX = 0;
+ wheelEvent.wheelTicksY =
+ pinch_event.event.data.pinchUpdate.scale > 1 ? 1 : -1;
+
+ SendWheelEvent(QueuedWheelEvent(
+ MouseWheelEventWithLatencyInfo(wheelEvent, pinch_event.latency), true));
+}
+
+void InputRouterImpl::OnInputEventAck(
+ const InputHostMsg_HandleInputEvent_ACK_Params& ack) {
+ client_->DecrementInFlightEventCount();
+
// Log the time delta for processing an input event.
TimeDelta delta = TimeTicks::Now() - input_event_start_time_;
UMA_HISTOGRAM_TIMES("MPArch.IIR_InputEventDelta", delta);
- // A synthetic ack will already have been sent for this event, and it will
- // not have affected the in-flight event count.
- if (WebInputEventTraits::IgnoresAckDisposition(event_type))
- return;
-
- client_->DecrementInFlightEventCount();
+ if (ack.overscroll) {
+ DCHECK(ack.type == WebInputEvent::MouseWheel ||
+ ack.type == WebInputEvent::GestureScrollUpdate);
+ OnDidOverscroll(*ack.overscroll);
+ }
- ProcessInputEventAck(event_type, ack_result, latency_info, RENDERER);
+ ProcessInputEventAck(ack.type, ack.state, ack.latency, RENDERER);
// WARNING: |this| may be deleted at this point.
// This is used only for testing, and the other end does not use the
@@ -478,13 +470,17 @@ void InputRouterImpl::OnInputEventAck(WebInputEvent::Type event_type,
// (ProcessInputEventAck) method, but not on other platforms; using
// 'void' instead is just as safe (since NotificationSource
// is not actually typesafe) and avoids this error.
- int type = static_cast<int>(event_type);
+ int type = static_cast<int>(ack.type);
NotificationService::current()->Notify(
NOTIFICATION_RENDER_WIDGET_HOST_DID_RECEIVE_INPUT_EVENT_ACK,
Source<void>(this),
Details<int>(&type));
}
+void InputRouterImpl::OnDidOverscroll(const DidOverscrollParams& params) {
+ client_->DidOverscroll(params);
+}
+
void InputRouterImpl::OnMsgMoveCaretAck() {
move_caret_pending_ = false;
if (next_move_caret_)
@@ -498,20 +494,32 @@ void InputRouterImpl::OnSelectRangeAck() {
}
void InputRouterImpl::OnHasTouchEventHandlers(bool has_handlers) {
- if (has_touch_handler_ == has_handlers)
- return;
- has_touch_handler_ = has_handlers;
+ TRACE_EVENT1("input", "InputRouterImpl::OnHasTouchEventHandlers",
+ "has_handlers", has_handlers);
+
+ // Lack of a touch handler indicates that the page either has no touch-action
+ // modifiers or that all its touch-action modifiers are auto. Resetting the
+ // touch-action here allows forwarding of subsequent gestures even if the
+ // underlying touches never reach the router.
+ // TODO(jdduke): Reset touch-action only at the end of a touch sequence to
+ // prevent potentially strange mid-sequence behavior, crbug.com/375940.
if (!has_handlers)
- touch_event_queue_->FlushQueue();
+ touch_action_filter_.ResetTouchAction();
+
+ touch_event_queue_.OnHasTouchEventHandlers(has_handlers);
client_->OnHasTouchEventHandlers(has_handlers);
}
-void InputRouterImpl::OnSetTouchAction(
- content::TouchAction touch_action) {
+void InputRouterImpl::OnSetTouchAction(TouchAction touch_action) {
// Synthetic touchstart events should get filtered out in RenderWidget.
- DCHECK(touch_event_queue_->IsPendingAckTouchStart());
+ DCHECK(touch_event_queue_.IsPendingAckTouchStart());
+ TRACE_EVENT1("input", "InputRouterImpl::OnSetTouchAction",
+ "action", touch_action);
touch_action_filter_.OnSetTouchAction(touch_action);
+
+ // TOUCH_ACTION_NONE should disable the touch ack timeout.
+ UpdateTouchAckTimeoutEnabled();
}
void InputRouterImpl::ProcessInputEventAck(
@@ -546,6 +554,8 @@ void InputRouterImpl::ProcessInputEventAck(
} else if (event_type != WebInputEvent::Undefined) {
ack_handler_->OnUnexpectedEventAck(InputAckHandler::BAD_ACK_MESSAGE);
}
+
+ SignalFlushedIfNecessary();
}
void InputRouterImpl::ProcessKeyboardAck(blink::WebInputEvent::Type type,
@@ -574,6 +584,7 @@ void InputRouterImpl::ProcessMouseAck(blink::WebInputEvent::Type type,
if (type != WebInputEvent::MouseMove)
return;
+ DCHECK(mouse_move_pending_);
mouse_move_pending_ = false;
if (next_mouse_move_) {
@@ -586,20 +597,31 @@ void InputRouterImpl::ProcessMouseAck(blink::WebInputEvent::Type type,
void InputRouterImpl::ProcessWheelAck(InputEventAckState ack_result,
const ui::LatencyInfo& latency) {
- ProcessAckForOverscroll(current_wheel_event_.event, ack_result);
-
// TODO(miletus): Add renderer side latency to each uncoalesced mouse
// wheel event and add terminal component to each of them.
- current_wheel_event_.latency.AddNewLatencyFrom(latency);
- // Process the unhandled wheel event here before calling SendWheelEvent()
- // since it will mutate current_wheel_event_.
- ack_handler_->OnWheelEventAck(current_wheel_event_, ack_result);
+ current_wheel_event_.event.latency.AddNewLatencyFrom(latency);
+
+ if (current_wheel_event_.synthesized_from_pinch) {
+ // Ack the GesturePinchUpdate event that generated this wheel event.
+ ProcessInputEventAck(WebInputEvent::GesturePinchUpdate,
+ ack_result,
+ current_wheel_event_.event.latency,
+ current_ack_source_);
+ } else {
+ // Process the unhandled wheel event here before calling SendWheelEvent()
+ // since it will mutate current_wheel_event_.
+ ack_handler_->OnWheelEventAck(current_wheel_event_.event, ack_result);
+ }
+
+ // Mark the wheel event complete only after the ACKs have been handled above.
+ // For example, ACKing the GesturePinchUpdate could cause another
+ // GesturePinchUpdate to be sent, which should queue a wheel event rather than
+ // send it immediately.
mouse_wheel_pending_ = false;
- // Now send the next (coalesced) mouse wheel event.
+ // Send the next (coalesced or synthetic) mouse wheel event.
if (!coalesced_mouse_wheel_events_.empty()) {
- MouseWheelEventWithLatencyInfo next_wheel_event =
- coalesced_mouse_wheel_events_.front();
+ QueuedWheelEvent next_wheel_event = coalesced_mouse_wheel_events_.front();
coalesced_mouse_wheel_events_.pop_front();
SendWheelEvent(next_wheel_event);
}
@@ -608,122 +630,70 @@ void InputRouterImpl::ProcessWheelAck(InputEventAckState ack_result,
void InputRouterImpl::ProcessGestureAck(WebInputEvent::Type type,
InputEventAckState ack_result,
const ui::LatencyInfo& latency) {
- // If |ack_result| originated from the overscroll controller, only
- // feed |gesture_event_filter_| the ack if it was expecting one.
- if (current_ack_source_ == OVERSCROLL_CONTROLLER &&
- !gesture_event_filter_->HasQueuedGestureEvents()) {
+ if (!gesture_event_queue_.ExpectingGestureAck())
return;
- }
- // |gesture_event_filter_| will forward to OnGestureEventAck when appropriate.
- gesture_event_filter_->ProcessGestureAck(ack_result, type, latency);
+ // |gesture_event_queue_| will forward to OnGestureEventAck when appropriate.
+ gesture_event_queue_.ProcessGestureAck(ack_result, type, latency);
}
void InputRouterImpl::ProcessTouchAck(
InputEventAckState ack_result,
const ui::LatencyInfo& latency) {
// |touch_event_queue_| will forward to OnTouchEventAck when appropriate.
- touch_event_queue_->ProcessTouchAck(ack_result, latency);
+ touch_event_queue_.ProcessTouchAck(ack_result, latency);
}
-void InputRouterImpl::ProcessAckForOverscroll(const WebInputEvent& event,
- InputEventAckState ack_result) {
- // Acks sent from the overscroll controller need not be fed back into the
- // overscroll controller.
- if (current_ack_source_ == OVERSCROLL_CONTROLLER)
+void InputRouterImpl::UpdateTouchAckTimeoutEnabled() {
+ // Mobile sites tend to be well-behaved with respect to touch handling, so
+ // they have less need for the touch timeout fallback.
+ const bool fixed_page_scale = (current_view_flags_ & FIXED_PAGE_SCALE) != 0;
+ const bool mobile_viewport = (current_view_flags_ & MOBILE_VIEWPORT) != 0;
+
+ // TOUCH_ACTION_NONE will prevent scrolling, in which case the timeout serves
+ // little purpose. It's also a strong signal that touch handling is critical
+ // to page functionality, so the timeout could do more harm than good.
+ const bool touch_action_none =
+ touch_action_filter_.allowed_touch_action() == TOUCH_ACTION_NONE;
+
+ const bool touch_ack_timeout_enabled = !fixed_page_scale &&
+ !mobile_viewport &&
+ !touch_action_none;
+ touch_event_queue_.SetAckTimeoutEnabled(touch_ack_timeout_enabled);
+}
+
+void InputRouterImpl::SignalFlushedIfNecessary() {
+ if (!flush_requested_)
return;
- OverscrollController* controller = client_->GetOverscrollController();
- if (!controller)
+ if (HasPendingEvents())
return;
- controller->ReceivedEventACK(
- event, (INPUT_EVENT_ACK_STATE_CONSUMED == ack_result));
-}
-
-void InputRouterImpl::SimulateTouchGestureWithMouse(
- const MouseEventWithLatencyInfo& event) {
- const WebMouseEvent& mouse_event = event.event;
- int x = mouse_event.x, y = mouse_event.y;
- float dx = mouse_event.movementX, dy = mouse_event.movementY;
- static int startX = 0, startY = 0;
-
- switch (mouse_event.button) {
- case WebMouseEvent::ButtonLeft:
- if (mouse_event.type == WebInputEvent::MouseDown) {
- startX = x;
- startY = y;
- SendGestureEvent(MakeGestureEvent(
- WebInputEvent::GestureScrollBegin, mouse_event.timeStampSeconds,
- x, y, 0, event.latency));
- }
- if (dx != 0 || dy != 0) {
- GestureEventWithLatencyInfo gesture_event = MakeGestureEvent(
- WebInputEvent::GestureScrollUpdate, mouse_event.timeStampSeconds,
- x, y, 0, event.latency);
- gesture_event.event.data.scrollUpdate.deltaX = dx;
- gesture_event.event.data.scrollUpdate.deltaY = dy;
- SendGestureEvent(gesture_event);
- }
- if (mouse_event.type == WebInputEvent::MouseUp) {
- SendGestureEvent(MakeGestureEvent(
- WebInputEvent::GestureScrollEnd, mouse_event.timeStampSeconds,
- x, y, 0, event.latency));
- }
- break;
- case WebMouseEvent::ButtonMiddle:
- if (mouse_event.type == WebInputEvent::MouseDown) {
- startX = x;
- startY = y;
- SendGestureEvent(MakeGestureEvent(
- WebInputEvent::GestureShowPress, mouse_event.timeStampSeconds,
- x, y, 0, event.latency));
- SendGestureEvent(MakeGestureEvent(
- WebInputEvent::GestureTapDown, mouse_event.timeStampSeconds,
- x, y, 0, event.latency));
- }
- if (mouse_event.type == WebInputEvent::MouseUp) {
- SendGestureEvent(MakeGestureEvent(
- WebInputEvent::GestureTap, mouse_event.timeStampSeconds,
- x, y, 0, event.latency));
- }
- break;
- case WebMouseEvent::ButtonRight:
- if (mouse_event.type == WebInputEvent::MouseDown) {
- startX = x;
- startY = y;
- SendGestureEvent(MakeGestureEvent(
- WebInputEvent::GestureScrollBegin, mouse_event.timeStampSeconds,
- x, y, 0, event.latency));
- SendGestureEvent(MakeGestureEvent(
- WebInputEvent::GesturePinchBegin, mouse_event.timeStampSeconds,
- x, y, 0, event.latency));
- }
- if (dx != 0 || dy != 0) {
- dx = pow(dy < 0 ? 0.998f : 1.002f, fabs(dy));
- GestureEventWithLatencyInfo gesture_event = MakeGestureEvent(
- WebInputEvent::GesturePinchUpdate, mouse_event.timeStampSeconds,
- startX, startY, 0, event.latency);
- gesture_event.event.data.pinchUpdate.scale = dx;
- SendGestureEvent(gesture_event);
- }
- if (mouse_event.type == WebInputEvent::MouseUp) {
- SendGestureEvent(MakeGestureEvent(
- WebInputEvent::GesturePinchEnd, mouse_event.timeStampSeconds,
- x, y, 0, event.latency));
- SendGestureEvent(MakeGestureEvent(
- WebInputEvent::GestureScrollEnd, mouse_event.timeStampSeconds,
- x, y, 0, event.latency));
- }
- break;
- case WebMouseEvent::ButtonNone:
- break;
- }
+ flush_requested_ = false;
+ client_->DidFlush();
+}
+
+bool InputRouterImpl::HasPendingEvents() const {
+ return !touch_event_queue_.empty() ||
+ !gesture_event_queue_.empty() ||
+ !key_queue_.empty() ||
+ mouse_move_pending_ ||
+ mouse_wheel_pending_ ||
+ select_range_pending_ ||
+ move_caret_pending_;
+}
+
+InputRouterImpl::QueuedWheelEvent::QueuedWheelEvent()
+ : synthesized_from_pinch(false) {
+}
+
+InputRouterImpl::QueuedWheelEvent::QueuedWheelEvent(
+ const MouseWheelEventWithLatencyInfo& event,
+ bool synthesized_from_pinch)
+ : event(event), synthesized_from_pinch(synthesized_from_pinch) {
}
-bool InputRouterImpl::IsInOverscrollGesture() const {
- OverscrollController* controller = client_->GetOverscrollController();
- return controller && controller->overscroll_mode() != OVERSCROLL_NONE;
+InputRouterImpl::QueuedWheelEvent::~QueuedWheelEvent() {
}
} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/input_router_impl.h b/chromium/content/browser/renderer_host/input/input_router_impl.h
index e12b7510ccf..e06113b3e58 100644
--- a/chromium/content/browser/renderer_host/input/input_router_impl.h
+++ b/chromium/content/browser/renderer_host/input/input_router_impl.h
@@ -10,13 +10,16 @@
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "base/time/time.h"
-#include "content/browser/renderer_host/input/gesture_event_filter.h"
+#include "content/browser/renderer_host/input/gesture_event_queue.h"
#include "content/browser/renderer_host/input/input_router.h"
#include "content/browser/renderer_host/input/touch_action_filter.h"
#include "content/browser/renderer_host/input/touch_event_queue.h"
#include "content/browser/renderer_host/input/touchpad_tap_suppression_controller.h"
+#include "content/common/input/input_event_stream_validator.h"
#include "content/public/browser/native_web_keyboard_event.h"
+struct InputHostMsg_HandleInputEvent_ACK_Params;
+
namespace IPC {
class Sender;
}
@@ -31,18 +34,26 @@ class InputAckHandler;
class InputRouterClient;
class OverscrollController;
class RenderWidgetHostImpl;
+struct DidOverscrollParams;
// A default implementation for browser input event routing.
class CONTENT_EXPORT InputRouterImpl
: public NON_EXPORTED_BASE(InputRouter),
- public NON_EXPORTED_BASE(GestureEventFilterClient),
+ public NON_EXPORTED_BASE(GestureEventQueueClient),
public NON_EXPORTED_BASE(TouchEventQueueClient),
public NON_EXPORTED_BASE(TouchpadTapSuppressionControllerClient) {
public:
+ struct CONTENT_EXPORT Config {
+ Config();
+ GestureEventQueue::Config gesture_config;
+ TouchEventQueue::Config touch_config;
+ };
+
InputRouterImpl(IPC::Sender* sender,
InputRouterClient* client,
InputAckHandler* ack_handler,
- int routing_id);
+ int routing_id,
+ const Config& config);
virtual ~InputRouterImpl();
// InputRouter
@@ -63,13 +74,13 @@ class CONTENT_EXPORT InputRouterImpl
virtual const NativeWebKeyboardEvent* GetLastKeyboardEvent() const OVERRIDE;
virtual bool ShouldForwardTouchEvent() const OVERRIDE;
virtual void OnViewUpdated(int view_flags) OVERRIDE;
+ virtual bool HasPendingEvents() const OVERRIDE;
// IPC::Listener
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
private:
friend class InputRouterImplTest;
- friend class MockRenderWidgetHost;
// TouchpadTapSuppressionControllerClient
virtual void SendMouseEventImmediately(
@@ -117,21 +128,39 @@ private:
const ui::LatencyInfo& latency_info,
bool is_keyboard_shortcut);
+ // A data structure that attaches some metadata to a WebMouseWheelEvent
+ // and its latency info.
+ struct QueuedWheelEvent {
+ QueuedWheelEvent();
+ QueuedWheelEvent(const MouseWheelEventWithLatencyInfo& event,
+ bool synthesized_from_pinch);
+ ~QueuedWheelEvent();
+
+ MouseWheelEventWithLatencyInfo event;
+ bool synthesized_from_pinch;
+ };
+
+ // Enqueue or send a mouse wheel event.
+ void SendWheelEvent(const QueuedWheelEvent& wheel_event);
+
+ // Given a Touchpad GesturePinchUpdate event, create and send a synthetic
+ // wheel event for it.
+ void SendSyntheticWheelEventForPinch(
+ const GestureEventWithLatencyInfo& pinch_event);
+
// IPC message handlers
- void OnInputEventAck(blink::WebInputEvent::Type event_type,
- InputEventAckState ack_result,
- const ui::LatencyInfo& latency_info);
+ void OnInputEventAck(const InputHostMsg_HandleInputEvent_ACK_Params& ack);
+ void OnDidOverscroll(const DidOverscrollParams& params);
void OnMsgMoveCaretAck();
void OnSelectRangeAck();
void OnHasTouchEventHandlers(bool has_handlers);
- void OnSetTouchAction(content::TouchAction touch_action);
+ void OnSetTouchAction(TouchAction touch_action);
// Indicates the source of an ack provided to |ProcessInputEventAck()|.
// The source is tracked by |current_ack_source_|, which aids in ack routing.
enum AckSource {
RENDERER,
CLIENT,
- OVERSCROLL_CONTROLLER,
IGNORING_DISPOSITION,
ACK_SOURCE_NONE
};
@@ -155,7 +184,7 @@ private:
void ProcessWheelAck(InputEventAckState ack_result,
const ui::LatencyInfo& latency);
- // Forwards the event ack to |gesture_event_filter|, potentially triggering
+ // Forwards the event ack to |gesture_event_queue|, potentially triggering
// dispatch of queued gesture events.
void ProcessGestureAck(blink::WebInputEvent::Type type,
InputEventAckState ack_result,
@@ -166,12 +195,16 @@ private:
void ProcessTouchAck(InputEventAckState ack_result,
const ui::LatencyInfo& latency);
- // Forwards |ack_result| to the client's OverscrollController, if necessary.
- void ProcessAckForOverscroll(const blink::WebInputEvent& event,
- InputEventAckState ack_result);
+ // Called when a touch timeout-affecting bit has changed, in turn toggling the
+ // touch ack timeout feature of the |touch_event_queue_| as appropriate. Input
+ // to that determination includes current view properties and the allowed
+ // touch action. Note that this will only affect platforms that have a
+ // non-zero touch timeout configuration.
+ void UpdateTouchAckTimeoutEnabled();
- void SimulateTouchGestureWithMouse(
- const MouseEventWithLatencyInfo& mouse_event);
+ // If a flush has been requested, signals a completed flush to the client if
+ // all events have been dispatched (i.e., |HasPendingEvents()| is false).
+ void SignalFlushedIfNecessary();
bool IsInOverscrollGesture() const;
@@ -206,9 +239,7 @@ private:
// (Similar to |mouse_move_pending_|.) True if a mouse wheel event was sent
// and we are waiting for a corresponding ack.
bool mouse_wheel_pending_;
- MouseWheelEventWithLatencyInfo current_wheel_event_;
-
- typedef std::deque<MouseWheelEventWithLatencyInfo> WheelEventQueue;
+ QueuedWheelEvent current_wheel_event_;
// (Similar to |next_mouse_move_|.) The next mouse wheel events to send.
// Unlike mouse moves, mouse wheel events received while one is pending are
@@ -217,35 +248,34 @@ private:
// high rate; not waiting for the ack results in jankiness, and using the same
// mechanism as for mouse moves (just dropping old events when multiple ones
// would be queued) results in very slow scrolling.
+ typedef std::deque<QueuedWheelEvent> WheelEventQueue;
WheelEventQueue coalesced_mouse_wheel_events_;
- // The time when an input event was sent to the RenderWidget.
- base::TimeTicks input_event_start_time_;
-
- // Queue of keyboard events that we need to track.
- typedef std::deque<NativeWebKeyboardEvent> KeyQueue;
-
// A queue of keyboard events. We can't trust data from the renderer so we
// stuff key events into a queue and pop them out on ACK, feeding our copy
// back to whatever unhandled handler instead of the returned version.
+ typedef std::deque<NativeWebKeyboardEvent> KeyQueue;
KeyQueue key_queue_;
- // Keeps track of whether the webpage has any touch event handler. If it does,
- // then touch events are sent to the renderer. Otherwise, the touch events are
- // not sent to the renderer.
- bool has_touch_handler_;
+ // The time when an input event was sent to the client.
+ base::TimeTicks input_event_start_time_;
- // Whether touch ack timeout handling has been enabled via the command line.
- bool touch_ack_timeout_enabled_;
- size_t touch_ack_timeout_delay_ms_;
+ // Cached flags from |OnViewUpdated()|, defaults to 0.
+ int current_view_flags_;
// The source of the ack within the scope of |ProcessInputEventAck()|.
// Defaults to ACK_SOURCE_NONE.
AckSource current_ack_source_;
- scoped_ptr<TouchEventQueue> touch_event_queue_;
- scoped_ptr<GestureEventFilter> gesture_event_filter_;
+ // Whether a call to |Flush()| has yet been accompanied by a |DidFlush()| call
+ // to the client_ after all events have been dispatched/acked.
+ bool flush_requested_;
+
+ TouchEventQueue touch_event_queue_;
+ GestureEventQueue gesture_event_queue_;
TouchActionFilter touch_action_filter_;
+ InputEventStreamValidator input_stream_validator_;
+ InputEventStreamValidator output_stream_validator_;
DISALLOW_COPY_AND_ASSIGN(InputRouterImpl);
};
diff --git a/chromium/content/browser/renderer_host/input/input_router_impl_perftest.cc b/chromium/content/browser/renderer_host/input/input_router_impl_perftest.cc
new file mode 100644
index 00000000000..0a8c89fd176
--- /dev/null
+++ b/chromium/content/browser/renderer_host/input/input_router_impl_perftest.cc
@@ -0,0 +1,387 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "content/browser/renderer_host/input/input_ack_handler.h"
+#include "content/browser/renderer_host/input/input_router_client.h"
+#include "content/browser/renderer_host/input/input_router_impl.h"
+#include "content/common/input/web_input_event_traits.h"
+#include "content/common/input_messages.h"
+#include "content/common/view_messages.h"
+#include "ipc/ipc_sender.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/perf/perf_test.h"
+#include "ui/gfx/geometry/vector2d_f.h"
+
+using base::TimeDelta;
+using blink::WebGestureEvent;
+using blink::WebInputEvent;
+using blink::WebTouchEvent;
+using blink::WebTouchPoint;
+
+namespace content {
+
+namespace {
+
+class NullInputAckHandler : public InputAckHandler {
+ public:
+ NullInputAckHandler() : ack_count_(0) {}
+ virtual ~NullInputAckHandler() {}
+
+ // InputAckHandler
+ virtual void OnKeyboardEventAck(const NativeWebKeyboardEvent& event,
+ InputEventAckState ack_result) OVERRIDE {
+ ++ack_count_;
+ }
+ virtual void OnWheelEventAck(const MouseWheelEventWithLatencyInfo& event,
+ InputEventAckState ack_result) OVERRIDE {
+ ++ack_count_;
+ }
+ virtual void OnTouchEventAck(const TouchEventWithLatencyInfo& event,
+ InputEventAckState ack_result) OVERRIDE {
+ ++ack_count_;
+ }
+ virtual void OnGestureEventAck(const GestureEventWithLatencyInfo& event,
+ InputEventAckState ack_result) OVERRIDE {
+ ++ack_count_;
+ }
+ virtual void OnUnexpectedEventAck(UnexpectedEventAckType type) OVERRIDE {
+ ++ack_count_;
+ }
+
+ size_t GetAndResetAckCount() {
+ size_t ack_count = ack_count_;
+ ack_count_ = 0;
+ return ack_count;
+ }
+
+ size_t ack_count() const { return ack_count_; }
+
+ private:
+ size_t ack_count_;
+};
+
+class NullInputRouterClient : public InputRouterClient {
+ public:
+ NullInputRouterClient() {}
+ virtual ~NullInputRouterClient() {}
+
+ // InputRouterClient
+ virtual InputEventAckState FilterInputEvent(
+ const blink::WebInputEvent& input_event,
+ const ui::LatencyInfo& latency_info) OVERRIDE {
+ return INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
+ }
+ virtual void IncrementInFlightEventCount() OVERRIDE {}
+ virtual void DecrementInFlightEventCount() OVERRIDE {}
+ virtual void OnHasTouchEventHandlers(bool has_handlers) OVERRIDE {}
+ virtual void DidFlush() OVERRIDE {}
+ virtual void SetNeedsFlush() OVERRIDE {}
+ virtual void DidOverscroll(const DidOverscrollParams& params) OVERRIDE {}
+};
+
+class NullIPCSender : public IPC::Sender {
+ public:
+ NullIPCSender() : sent_count_(0) {}
+ virtual ~NullIPCSender() {}
+
+ virtual bool Send(IPC::Message* message) OVERRIDE {
+ delete message;
+ ++sent_count_;
+ return true;
+ }
+
+ size_t GetAndResetSentEventCount() {
+ size_t message_count = sent_count_;
+ sent_count_ = 0;
+ return message_count;
+ }
+
+ bool HasMessages() const { return sent_count_ > 0; }
+
+ private:
+ size_t sent_count_;
+};
+
+// TODO(jdduke): Use synthetic gesture pipeline, crbug.com/344598.
+typedef std::vector<WebGestureEvent> Gestures;
+Gestures BuildScrollSequence(size_t steps,
+ gfx::Vector2dF origin,
+ gfx::Vector2dF distance) {
+ Gestures gestures;
+ const gfx::Vector2dF delta = ScaleVector2d(distance, 1.f / steps);
+
+ WebGestureEvent gesture;
+ gesture.type = WebInputEvent::GestureScrollBegin;
+ gesture.x = origin.x();
+ gesture.y = origin.y();
+ gestures.push_back(gesture);
+
+ gesture.type = WebInputEvent::GestureScrollUpdate;
+ gesture.data.scrollUpdate.deltaX = delta.x();
+ gesture.data.scrollUpdate.deltaY = delta.y();
+ for (size_t i = 0; i < steps; ++i) {
+ gesture.x += delta.x();
+ gesture.y += delta.y();
+ gestures.push_back(gesture);
+ }
+
+ gesture.type = WebInputEvent::GestureScrollEnd;
+ gestures.push_back(gesture);
+ return gestures;
+}
+
+typedef std::vector<WebTouchEvent> Touches;
+Touches BuildTouchSequence(size_t steps,
+ gfx::Vector2dF origin,
+ gfx::Vector2dF distance) {
+ Touches touches;
+ const gfx::Vector2dF delta = ScaleVector2d(distance, 1.f / steps);
+
+ WebTouchEvent touch;
+ touch.touchesLength = 1;
+ touch.type = WebInputEvent::TouchStart;
+ touch.touches[0].id = 0;
+ touch.touches[0].state = WebTouchPoint::StatePressed;
+ touch.touches[0].position.x = origin.x();
+ touch.touches[0].position.y = origin.y();
+ touch.touches[0].screenPosition.x = origin.x();
+ touch.touches[0].screenPosition.y = origin.y();
+ touches.push_back(touch);
+
+ touch.type = WebInputEvent::TouchMove;
+ touch.touches[0].state = WebTouchPoint::StateMoved;
+ for (size_t i = 0; i < steps; ++i) {
+ touch.touches[0].position.x += delta.x();
+ touch.touches[0].position.y += delta.y();
+ touch.touches[0].screenPosition.x += delta.x();
+ touch.touches[0].screenPosition.y += delta.y();
+ touches.push_back(touch);
+ }
+
+ touch.type = WebInputEvent::TouchEnd;
+ touch.touches[0].state = WebTouchPoint::StateReleased;
+ touches.push_back(touch);
+ return touches;
+}
+
+class InputEventTimer {
+ public:
+ InputEventTimer(const char* test_name, int64 event_count)
+ : test_name_(test_name),
+ event_count_(event_count),
+ start_(base::TimeTicks::Now()) {}
+
+ ~InputEventTimer() {
+ perf_test::PrintResult(
+ "avg_time_per_event",
+ "",
+ test_name_,
+ static_cast<size_t>(((base::TimeTicks::Now() - start_) / event_count_)
+ .InMicroseconds()),
+ "us",
+ true);
+ }
+
+ private:
+ const char* test_name_;
+ int64 event_count_;
+ base::TimeTicks start_;
+ DISALLOW_COPY_AND_ASSIGN(InputEventTimer);
+};
+
+} // namespace
+
+class InputRouterImplPerfTest : public testing::Test {
+ public:
+ InputRouterImplPerfTest() : last_input_id_(0) {}
+ virtual ~InputRouterImplPerfTest() {}
+
+ protected:
+ // testing::Test
+ virtual void SetUp() OVERRIDE {
+ sender_.reset(new NullIPCSender());
+ client_.reset(new NullInputRouterClient());
+ ack_handler_.reset(new NullInputAckHandler());
+ input_router_.reset(new InputRouterImpl(sender_.get(),
+ client_.get(),
+ ack_handler_.get(),
+ MSG_ROUTING_NONE,
+ InputRouterImpl::Config()));
+ }
+
+ virtual void TearDown() OVERRIDE {
+ base::MessageLoop::current()->RunUntilIdle();
+
+ input_router_.reset();
+ ack_handler_.reset();
+ client_.reset();
+ sender_.reset();
+ }
+
+ void SendEvent(const WebGestureEvent& gesture,
+ const ui::LatencyInfo& latency) {
+ input_router_->SendGestureEvent(
+ GestureEventWithLatencyInfo(gesture, latency));
+ }
+
+ void SendEvent(const WebTouchEvent& touch, const ui::LatencyInfo& latency) {
+ input_router_->SendTouchEvent(TouchEventWithLatencyInfo(touch, latency));
+ }
+
+ void SendEventAckIfNecessary(const blink::WebInputEvent& event,
+ InputEventAckState ack_result) {
+ if (WebInputEventTraits::IgnoresAckDisposition(event))
+ return;
+ InputHostMsg_HandleInputEvent_ACK_Params ack;
+ ack.type = event.type;
+ ack.state = ack_result;
+ InputHostMsg_HandleInputEvent_ACK response(0, ack);
+ input_router_->OnMessageReceived(response);
+ }
+
+ void OnHasTouchEventHandlers(bool has_handlers) {
+ input_router_->OnMessageReceived(
+ ViewHostMsg_HasTouchEventHandlers(0, has_handlers));
+ }
+
+ size_t GetAndResetSentEventCount() {
+ return sender_->GetAndResetSentEventCount();
+ }
+
+ size_t GetAndResetAckCount() { return ack_handler_->GetAndResetAckCount(); }
+
+ size_t AckCount() const { return ack_handler_->ack_count(); }
+
+ int64 NextLatencyID() { return ++last_input_id_; }
+
+ ui::LatencyInfo CreateLatencyInfo() {
+ ui::LatencyInfo latency;
+ latency.AddLatencyNumber(
+ ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT, 1, 0);
+ latency.AddLatencyNumber(
+ ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_RWH_COMPONENT,
+ 1,
+ NextLatencyID());
+ return latency;
+ }
+
+ // TODO(jdduke): Use synthetic gesture pipeline, crbug.com/344598.
+ template <typename EventType>
+ void SimulateEventSequence(const char* test_name,
+ const std::vector<EventType>& events,
+ bool ack_delay,
+ size_t iterations) {
+ OnHasTouchEventHandlers(true);
+
+ const size_t event_count = events.size();
+ const size_t total_event_count = event_count * iterations;
+
+ InputEventTimer timer(test_name, total_event_count);
+ while (iterations--) {
+ size_t i = 0, ack_i = 0;
+ if (ack_delay)
+ SendEvent(events[i++], CreateLatencyInfo());
+
+ for (; i < event_count; ++i, ++ack_i) {
+ SendEvent(events[i], CreateLatencyInfo());
+ SendEventAckIfNecessary(events[ack_i], INPUT_EVENT_ACK_STATE_CONSUMED);
+ }
+
+ if (ack_delay)
+ SendEventAckIfNecessary(events.back(), INPUT_EVENT_ACK_STATE_CONSUMED);
+
+ EXPECT_EQ(event_count, GetAndResetSentEventCount());
+ EXPECT_EQ(event_count, GetAndResetAckCount());
+ }
+ }
+
+ void SimulateTouchAndScrollEventSequence(const char* test_name,
+ size_t steps,
+ gfx::Vector2dF origin,
+ gfx::Vector2dF distance,
+ size_t iterations) {
+ OnHasTouchEventHandlers(true);
+
+ Gestures gestures = BuildScrollSequence(steps, origin, distance);
+ Touches touches = BuildTouchSequence(steps, origin, distance);
+ ASSERT_EQ(touches.size(), gestures.size());
+
+ const size_t event_count = gestures.size();
+ const size_t total_event_count = event_count * iterations * 2;
+
+ InputEventTimer timer(test_name, total_event_count);
+ while (iterations--) {
+ for (size_t i = 0; i < event_count; ++i) {
+ SendEvent(touches[i], CreateLatencyInfo());
+ // Touches may not be forwarded after the scroll sequence has begun, so
+ // only ack if necessary.
+ if (!AckCount()) {
+ SendEventAckIfNecessary(touches[i],
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ }
+
+ SendEvent(gestures[i], CreateLatencyInfo());
+ SendEventAckIfNecessary(gestures[i], INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_EQ(2U, GetAndResetAckCount());
+ }
+ }
+ }
+
+ private:
+ int64 last_input_id_;
+ scoped_ptr<NullIPCSender> sender_;
+ scoped_ptr<NullInputRouterClient> client_;
+ scoped_ptr<NullInputAckHandler> ack_handler_;
+ scoped_ptr<InputRouterImpl> input_router_;
+ base::MessageLoopForUI message_loop_;
+};
+
+const size_t kDefaultSteps(100);
+const size_t kDefaultIterations(100);
+const gfx::Vector2dF kDefaultOrigin(100, 100);
+const gfx::Vector2dF kDefaultDistance(500, 500);
+
+TEST_F(InputRouterImplPerfTest, TouchSwipe) {
+ SimulateEventSequence(
+ "TouchSwipe ",
+ BuildTouchSequence(kDefaultSteps, kDefaultOrigin, kDefaultDistance),
+ false,
+ kDefaultIterations);
+}
+
+TEST_F(InputRouterImplPerfTest, TouchSwipeDelayedAck) {
+ SimulateEventSequence(
+ "TouchSwipeDelayedAck ",
+ BuildTouchSequence(kDefaultSteps, kDefaultOrigin, kDefaultDistance),
+ true,
+ kDefaultIterations);
+}
+
+TEST_F(InputRouterImplPerfTest, GestureScroll) {
+ SimulateEventSequence(
+ "GestureScroll ",
+ BuildScrollSequence(kDefaultSteps, kDefaultOrigin, kDefaultDistance),
+ false,
+ kDefaultIterations);
+}
+
+TEST_F(InputRouterImplPerfTest, GestureScrollDelayedAck) {
+ SimulateEventSequence(
+ "GestureScrollDelayedAck ",
+ BuildScrollSequence(kDefaultSteps, kDefaultOrigin, kDefaultDistance),
+ true,
+ kDefaultIterations);
+}
+
+TEST_F(InputRouterImplPerfTest, TouchSwipeToGestureScroll) {
+ SimulateTouchAndScrollEventSequence("TouchSwipeToGestureScroll ",
+ kDefaultSteps,
+ kDefaultOrigin,
+ kDefaultDistance,
+ kDefaultIterations);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/input_router_impl_unittest.cc b/chromium/content/browser/renderer_host/input/input_router_impl_unittest.cc
index f51264b0038..1fa1a7d305a 100644
--- a/chromium/content/browser/renderer_host/input/input_router_impl_unittest.cc
+++ b/chromium/content/browser/renderer_host/input/input_router_impl_unittest.cc
@@ -2,11 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <math.h>
+
#include "base/basictypes.h"
#include "base/command_line.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/utf_string_conversions.h"
-#include "content/browser/renderer_host/input/gesture_event_filter.h"
+#include "content/browser/renderer_host/input/gesture_event_queue.h"
#include "content/browser/renderer_host/input/input_router_client.h"
#include "content/browser/renderer_host/input/input_router_impl.h"
#include "content/browser/renderer_host/input/mock_input_ack_handler.h"
@@ -14,6 +16,7 @@
#include "content/common/content_constants_internal.h"
#include "content/common/edit_command.h"
#include "content/common/input/synthetic_web_input_event_builders.h"
+#include "content/common/input/touch_action.h"
#include "content/common/input/web_input_event_traits.h"
#include "content/common/input_messages.h"
#include "content/common/view_messages.h"
@@ -23,12 +26,13 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/keycodes/keyboard_codes.h"
-#if defined(OS_WIN) || defined(USE_AURA)
+#if defined(USE_AURA)
#include "content/browser/renderer_host/ui_events_helper.h"
#include "ui/events/event.h"
#endif
using base::TimeDelta;
+using blink::WebGestureDevice;
using blink::WebGestureEvent;
using blink::WebKeyboardEvent;
using blink::WebInputEvent;
@@ -50,6 +54,29 @@ const WebInputEvent* GetInputEventFromMessage(const IPC::Message& message) {
return reinterpret_cast<const WebInputEvent*>(data);
}
+WebInputEvent& GetEventWithType(WebInputEvent::Type type) {
+ WebInputEvent* event = NULL;
+ if (WebInputEvent::isMouseEventType(type)) {
+ static WebMouseEvent mouse;
+ event = &mouse;
+ } else if (WebInputEvent::isTouchEventType(type)) {
+ static WebTouchEvent touch;
+ event = &touch;
+ } else if (WebInputEvent::isKeyboardEventType(type)) {
+ static WebKeyboardEvent key;
+ event = &key;
+ } else if (WebInputEvent::isGestureEventType(type)) {
+ static WebGestureEvent gesture;
+ event = &gesture;
+ } else if (type == WebInputEvent::MouseWheel) {
+ static WebMouseWheelEvent wheel;
+ event = &wheel;
+ }
+ CHECK(event);
+ event->type = type;
+ return *event;
+}
+
bool GetIsShortcutFromHandleInputEventMessage(const IPC::Message* msg) {
InputMsg_HandleInputEvent::Schema::Param param;
InputMsg_HandleInputEvent::Read(msg, &param);
@@ -75,7 +102,7 @@ void ExpectIPCMessageWithArg2(const IPC::Message* msg,
EXPECT_EQ(arg2, param.b);
}
-#if defined(OS_WIN) || defined(USE_AURA)
+#if defined(USE_AURA)
bool TouchEventsAreEquivalent(const ui::TouchEvent& first,
const ui::TouchEvent& second) {
if (first.type() != second.type())
@@ -103,7 +130,12 @@ bool EventListIsSubset(const ScopedVector<ui::TouchEvent>& subset,
return true;
}
-#endif // defined(OS_WIN) || defined(USE_AURA)
+#endif // defined(USE_AURA)
+
+// Expected function used for converting pinch scales to deltaY values.
+float PinchScaleToWheelDelta(float scale) {
+ return 100.0 * log(scale);
+}
} // namespace
@@ -119,10 +151,13 @@ class InputRouterImplTest : public testing::Test {
process_.reset(new MockRenderProcessHost(browser_context_.get()));
client_.reset(new MockInputRouterClient());
ack_handler_.reset(new MockInputAckHandler());
- input_router_.reset(new InputRouterImpl(
- process_.get(), client_.get(), ack_handler_.get(), MSG_ROUTING_NONE));
- input_router_->gesture_event_filter_->set_debounce_enabled_for_testing(
- false);
+ CommandLine* command_line = CommandLine::ForCurrentProcess();
+ command_line->AppendSwitch(switches::kValidateInputEventStream);
+ input_router_.reset(new InputRouterImpl(process_.get(),
+ client_.get(),
+ ack_handler_.get(),
+ MSG_ROUTING_NONE,
+ config_));
client_->set_input_router(input_router());
ack_handler_->set_input_router(input_router());
}
@@ -137,6 +172,14 @@ class InputRouterImplTest : public testing::Test {
browser_context_.reset();
}
+ void SetUpForTouchAckTimeoutTest(int timeout_ms) {
+ config_.touch_config.touch_ack_timeout_delay =
+ base::TimeDelta::FromMilliseconds(timeout_ms);
+ config_.touch_config.touch_ack_timeout_supported = true;
+ TearDown();
+ SetUp();
+ }
+
void SimulateKeyboardEvent(WebInputEvent::Type type, bool is_shortcut) {
WebKeyboardEvent event = SyntheticWebKeyboardEventBuilder::Build(type);
NativeWebKeyboardEvent native_event;
@@ -153,10 +196,9 @@ class InputRouterImplTest : public testing::Test {
ui::LatencyInfo()));
}
- void SimulateMouseMove(int x, int y, int modifiers) {
+ void SimulateMouseEvent(WebInputEvent::Type type, int x, int y) {
input_router_->SendMouseEvent(MouseEventWithLatencyInfo(
- SyntheticWebMouseEventBuilder::Build(
- WebInputEvent::MouseMove, x, y, modifiers),
+ SyntheticWebMouseEventBuilder::Build(type, x, y, 0),
ui::LatencyInfo()));
}
@@ -171,7 +213,7 @@ class InputRouterImplTest : public testing::Test {
}
void SimulateGestureEvent(WebInputEvent::Type type,
- WebGestureEvent::SourceDevice sourceDevice) {
+ WebGestureDevice sourceDevice) {
SimulateGestureEvent(
SyntheticWebGestureEventBuilder::Build(type, sourceDevice));
}
@@ -184,47 +226,21 @@ class InputRouterImplTest : public testing::Test {
void SimulateGesturePinchUpdateEvent(float scale,
float anchorX,
float anchorY,
- int modifiers) {
- SimulateGestureEvent(
- SyntheticWebGestureEventBuilder::BuildPinchUpdate(scale,
- anchorX,
- anchorY,
- modifiers));
+ int modifiers,
+ WebGestureDevice sourceDevice) {
+ SimulateGestureEvent(SyntheticWebGestureEventBuilder::BuildPinchUpdate(
+ scale, anchorX, anchorY, modifiers, sourceDevice));
}
- void SimulateGestureFlingStartEvent(
- float velocityX,
- float velocityY,
- WebGestureEvent::SourceDevice sourceDevice) {
+ void SimulateGestureFlingStartEvent(float velocityX,
+ float velocityY,
+ WebGestureDevice sourceDevice) {
SimulateGestureEvent(
SyntheticWebGestureEventBuilder::BuildFling(velocityX,
velocityY,
sourceDevice));
}
- void SimulateTouchEvent(WebInputEvent::Type type) {
- touch_event_.ResetPoints();
- int index = PressTouchPoint(0, 0);
- switch (type) {
- case WebInputEvent::TouchStart:
- // Already handled by |PressTouchPoint()|.
- break;
- case WebInputEvent::TouchMove:
- MoveTouchPoint(index, 5, 5);
- break;
- case WebInputEvent::TouchEnd:
- ReleaseTouchPoint(index);
- break;
- case WebInputEvent::TouchCancel:
- CancelTouchPoint(index);
- break;
- default:
- FAIL() << "Invalid touch event type.";
- break;
- }
- SendTouchEvent();
- }
-
void SetTouchTimestamp(base::TimeDelta timestamp) {
touch_event_.SetTimestamp(timestamp);
}
@@ -250,12 +266,13 @@ class InputRouterImplTest : public testing::Test {
void CancelTouchPoint(int index) {
touch_event_.CancelPoint(index);
}
+
void SendInputEventACK(blink::WebInputEvent::Type type,
InputEventAckState ack_result) {
- scoped_ptr<IPC::Message> response(
- new InputHostMsg_HandleInputEvent_ACK(0, type, ack_result,
- ui::LatencyInfo()));
- input_router_->OnMessageReceived(*response);
+ InputHostMsg_HandleInputEvent_ACK_Params ack;
+ ack.type = type;
+ ack.state = ack_result;
+ input_router_->OnMessageReceived(InputHostMsg_HandleInputEvent_ACK(0, ack));
}
InputRouterImpl* input_router() const {
@@ -263,11 +280,33 @@ class InputRouterImplTest : public testing::Test {
}
bool TouchEventQueueEmpty() const {
- return input_router()->touch_event_queue_->empty();
+ return input_router()->touch_event_queue_.empty();
}
bool TouchEventTimeoutEnabled() const {
- return input_router()->touch_event_queue_->ack_timeout_enabled();
+ return input_router()->touch_event_queue_.ack_timeout_enabled();
+ }
+
+ void Flush() const {
+ return input_router_->Flush();
+ }
+
+ size_t GetAndResetDidFlushCount() {
+ return client_->GetAndResetDidFlushCount();
+ }
+
+ bool HasPendingEvents() const {
+ return input_router_->HasPendingEvents();
+ }
+
+ void OnHasTouchEventHandlers(bool has_handlers) {
+ input_router_->OnMessageReceived(
+ ViewHostMsg_HasTouchEventHandlers(0, has_handlers));
+ }
+
+ void OnSetTouchAction(content::TouchAction touch_action) {
+ input_router_->OnMessageReceived(
+ InputHostMsg_SetTouchAction(0, touch_action));
}
size_t GetSentMessageCountAndResetSink() {
@@ -276,6 +315,13 @@ class InputRouterImplTest : public testing::Test {
return count;
}
+ static void RunTasksAndWait(base::TimeDelta delay) {
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE, base::MessageLoop::QuitClosure(), delay);
+ base::MessageLoop::current()->Run();
+ }
+
+ InputRouterImpl::Config config_;
scoped_ptr<MockRenderProcessHost> process_;
scoped_ptr<MockInputRouterClient> client_;
scoped_ptr<MockInputAckHandler> ack_handler_;
@@ -455,10 +501,21 @@ TEST_F(InputRouterImplTest, CoalescesWheelEvents) {
SimulateWheelEvent(0, -10, 0, false); // enqueued
SimulateWheelEvent(8, -6, 0, false); // coalesced into previous event
SimulateWheelEvent(9, -7, 1, false); // enqueued, different modifiers
+ SimulateWheelEvent(0, -10, 0, false); // enqueued, different modifiers
+ // Explicitly verify that PhaseEnd isn't coalesced to avoid bugs like
+ // https://crbug.com/154740.
+ SimulateWheelEventWithPhase(WebMouseWheelEvent::PhaseEnded); // enqueued
// Check that only the first event was sent.
EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
InputMsg_HandleInputEvent::ID));
+ const WebInputEvent* input_event =
+ GetInputEventFromMessage(*process_->sink().GetMessageAt(0));
+ ASSERT_EQ(WebInputEvent::MouseWheel, input_event->type);
+ const WebMouseWheelEvent* wheel_event =
+ static_cast<const WebMouseWheelEvent*>(input_event);
+ EXPECT_EQ(0, wheel_event->deltaX);
+ EXPECT_EQ(-5, wheel_event->deltaY);
EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
// Check that the ACK sends the second message immediately.
@@ -471,65 +528,66 @@ TEST_F(InputRouterImplTest, CoalescesWheelEvents) {
EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
InputMsg_HandleInputEvent::ID));
+ input_event = GetInputEventFromMessage(*process_->sink().GetMessageAt(0));
+ ASSERT_EQ(WebInputEvent::MouseWheel, input_event->type);
+ wheel_event = static_cast<const WebMouseWheelEvent*>(input_event);
+ EXPECT_EQ(8, wheel_event->deltaX);
+ EXPECT_EQ(-10 + -6, wheel_event->deltaY); // coalesced
EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
- // One more time.
+ // Ack the second event (which had the third coalesced into it).
SendInputEventACK(WebInputEvent::MouseWheel,
INPUT_EVENT_ACK_STATE_CONSUMED);
base::MessageLoop::current()->RunUntilIdle();
EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
InputMsg_HandleInputEvent::ID));
+ input_event = GetInputEventFromMessage(*process_->sink().GetMessageAt(0));
+ ASSERT_EQ(WebInputEvent::MouseWheel, input_event->type);
+ wheel_event = static_cast<const WebMouseWheelEvent*>(input_event);
+ EXPECT_EQ(9, wheel_event->deltaX);
+ EXPECT_EQ(-7, wheel_event->deltaY);
EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
- // After the final ack, the queue should be empty.
+ // Ack the fourth event.
SendInputEventACK(WebInputEvent::MouseWheel,
INPUT_EVENT_ACK_STATE_CONSUMED);
base::MessageLoop::current()->RunUntilIdle();
EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
- EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
-}
-
-TEST_F(InputRouterImplTest,
- CoalescesWheelEventsQueuedPhaseEndIsNotDropped) {
- // Send an initial gesture begin and ACK it.
- SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
- WebGestureEvent::Touchpad);
+ EXPECT_TRUE(
+ process_->sink().GetUniqueMessageMatching(InputMsg_HandleInputEvent::ID));
+ input_event = GetInputEventFromMessage(*process_->sink().GetMessageAt(0));
+ ASSERT_EQ(WebInputEvent::MouseWheel, input_event->type);
+ wheel_event = static_cast<const WebMouseWheelEvent*>(input_event);
+ EXPECT_EQ(0, wheel_event->deltaX);
+ EXPECT_EQ(-10, wheel_event->deltaY);
EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
- SendInputEventACK(WebInputEvent::GestureScrollBegin,
- INPUT_EVENT_ACK_STATE_CONSUMED);
- base::MessageLoop::current()->RunUntilIdle();
- // Send a wheel event, should get sent directly.
- SimulateWheelEvent(0, -5, 0, false);
+ // Ack the fifth event.
+ SendInputEventACK(WebInputEvent::MouseWheel, INPUT_EVENT_ACK_STATE_CONSUMED);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
+ EXPECT_TRUE(
+ process_->sink().GetUniqueMessageMatching(InputMsg_HandleInputEvent::ID));
+ input_event = GetInputEventFromMessage(*process_->sink().GetMessageAt(0));
+ ASSERT_EQ(WebInputEvent::MouseWheel, input_event->type);
+ wheel_event = static_cast<const WebMouseWheelEvent*>(input_event);
+ EXPECT_EQ(0, wheel_event->deltaX);
+ EXPECT_EQ(0, wheel_event->deltaY);
+ EXPECT_EQ(WebMouseWheelEvent::PhaseEnded, wheel_event->phase);
EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
- // Send a wheel phase end event before an ACK is received for the previous
- // wheel event, which should get queued.
- SimulateWheelEventWithPhase(WebMouseWheelEvent::PhaseEnded);
+ // After the final ack, the queue should be empty.
+ SendInputEventACK(WebInputEvent::MouseWheel, INPUT_EVENT_ACK_STATE_CONSUMED);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
-
- // A gesture event should now result in the queued phase ended event being
- // transmitted before it.
- SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
- WebGestureEvent::Touchpad);
-
- // Verify the events that were sent.
- const WebInputEvent* input_event =
- GetInputEventFromMessage(*process_->sink().GetMessageAt(0));
- ASSERT_EQ(WebInputEvent::MouseWheel, input_event->type);
- const WebMouseWheelEvent* wheel_event =
- static_cast<const WebMouseWheelEvent*>(input_event);
- ASSERT_EQ(WebMouseWheelEvent::PhaseEnded, wheel_event->phase);
-
- input_event = GetInputEventFromMessage(*process_->sink().GetMessageAt(1));
- EXPECT_EQ(WebInputEvent::GestureScrollEnd, input_event->type);
-
- ASSERT_EQ(2U, GetSentMessageCountAndResetSink());
}
// Tests that touch-events are queued properly.
TEST_F(InputRouterImplTest, TouchEventQueue) {
+ OnHasTouchEventHandlers(true);
+
PressTouchPoint(1, 1);
SendTouchEvent();
EXPECT_TRUE(client_->GetAndResetFilterEventCalled());
@@ -564,7 +622,7 @@ TEST_F(InputRouterImplTest, TouchEventQueue) {
// Tests that the touch-queue is emptied if a page stops listening for touch
// events.
TEST_F(InputRouterImplTest, TouchEventQueueFlush) {
- input_router_->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, true));
+ OnHasTouchEventHandlers(true);
EXPECT_TRUE(client_->has_touch_handler());
EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
EXPECT_TRUE(TouchEventQueueEmpty());
@@ -580,14 +638,14 @@ TEST_F(InputRouterImplTest, TouchEventQueueFlush) {
// The page stops listening for touch-events. The touch-event queue should now
// be emptied, but none of the queued touch-events should be sent to the
// renderer.
- input_router_->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, false));
+ OnHasTouchEventHandlers(false);
EXPECT_FALSE(client_->has_touch_handler());
EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
EXPECT_TRUE(TouchEventQueueEmpty());
EXPECT_FALSE(input_router_->ShouldForwardTouchEvent());
}
-#if defined(OS_WIN) || defined(USE_AURA)
+#if defined(USE_AURA)
// Tests that the acked events have correct state. (ui::Events are used only on
// windows and aura)
TEST_F(InputRouterImplTest, AckedTouchEventState) {
@@ -614,12 +672,12 @@ TEST_F(InputRouterImplTest, AckedTouchEventState) {
// Move the finger.
timestamp += base::TimeDelta::FromSeconds(10);
- MoveTouchPoint(0, 5, 5);
+ MoveTouchPoint(0, 500, 500);
SetTouchTimestamp(timestamp);
SendTouchEvent();
EXPECT_FALSE(TouchEventQueueEmpty());
expected_events.push_back(new ui::TouchEvent(ui::ET_TOUCH_MOVED,
- gfx::Point(5, 5), 0, timestamp));
+ gfx::Point(500, 500), 0, timestamp));
// Now press a second finger.
timestamp += base::TimeDelta::FromSeconds(10);
@@ -672,7 +730,7 @@ TEST_F(InputRouterImplTest, AckedTouchEventState) {
EXPECT_TRUE(TouchEventQueueEmpty());
EXPECT_EQ(0U, expected_events.size());
}
-#endif // defined(OS_WIN) || defined(USE_AURA)
+#endif // defined(USE_AURA)
TEST_F(InputRouterImplTest, UnhandledWheelEvent) {
// Simulate wheel events.
@@ -703,49 +761,141 @@ TEST_F(InputRouterImplTest, UnhandledWheelEvent) {
}
TEST_F(InputRouterImplTest, TouchTypesIgnoringAck) {
- int start_type = static_cast<int>(WebInputEvent::TouchStart);
- int end_type = static_cast<int>(WebInputEvent::TouchCancel);
- for (int i = start_type; i <= end_type; ++i) {
- WebInputEvent::Type type = static_cast<WebInputEvent::Type>(i);
- if (!WebInputEventTraits::IgnoresAckDisposition(type))
- continue;
+ OnHasTouchEventHandlers(true);
+ // Only acks for TouchCancel should always be ignored.
+ ASSERT_FALSE(WebInputEventTraits::IgnoresAckDisposition(
+ GetEventWithType(WebInputEvent::TouchStart)));
+ ASSERT_FALSE(WebInputEventTraits::IgnoresAckDisposition(
+ GetEventWithType(WebInputEvent::TouchMove)));
+ ASSERT_FALSE(WebInputEventTraits::IgnoresAckDisposition(
+ GetEventWithType(WebInputEvent::TouchEnd)));
+
+ // Precede the TouchCancel with an appropriate TouchStart;
+ PressTouchPoint(1, 1);
+ SendTouchEvent();
+ SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_CONSUMED);
+ ASSERT_EQ(1U, GetSentMessageCountAndResetSink());
+ ASSERT_EQ(1U, ack_handler_->GetAndResetAckCount());
+ ASSERT_EQ(0, client_->in_flight_event_count());
+
+ // The TouchCancel ack is always ignored.
+ CancelTouchPoint(0);
+ SendTouchEvent();
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
+ EXPECT_EQ(0, client_->in_flight_event_count());
+ EXPECT_FALSE(HasPendingEvents());
+ SendInputEventACK(WebInputEvent::TouchCancel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
+ EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
+ EXPECT_FALSE(HasPendingEvents());
+}
- // The TouchEventQueue requires an initial TouchStart for it to begin
- // forwarding other touch event types.
- if (type != WebInputEvent::TouchStart) {
- SimulateTouchEvent(WebInputEvent::TouchStart);
- SendInputEventACK(WebInputEvent::TouchStart,
- INPUT_EVENT_ACK_STATE_CONSUMED);
- ASSERT_EQ(1U, GetSentMessageCountAndResetSink());
- ASSERT_EQ(1U, ack_handler_->GetAndResetAckCount());
- ASSERT_EQ(0, client_->in_flight_event_count());
+TEST_F(InputRouterImplTest, GestureTypesIgnoringAck) {
+ // We test every gesture type, ensuring that the stream of gestures is valid.
+ const int kEventTypesLength = 29;
+ WebInputEvent::Type eventTypes[kEventTypesLength] = {
+ WebInputEvent::GestureTapDown,
+ WebInputEvent::GestureShowPress,
+ WebInputEvent::GestureTapCancel,
+ WebInputEvent::GestureScrollBegin,
+ WebInputEvent::GestureFlingStart,
+ WebInputEvent::GestureFlingCancel,
+ WebInputEvent::GestureTapDown,
+ WebInputEvent::GestureTap,
+ WebInputEvent::GestureTapDown,
+ WebInputEvent::GestureLongPress,
+ WebInputEvent::GestureTapCancel,
+ WebInputEvent::GestureLongTap,
+ WebInputEvent::GestureTapDown,
+ WebInputEvent::GestureTapUnconfirmed,
+ WebInputEvent::GestureTapCancel,
+ WebInputEvent::GestureTapDown,
+ WebInputEvent::GestureDoubleTap,
+ WebInputEvent::GestureTapDown,
+ WebInputEvent::GestureTapCancel,
+ WebInputEvent::GestureTwoFingerTap,
+ WebInputEvent::GestureTapDown,
+ WebInputEvent::GestureTapCancel,
+ WebInputEvent::GestureScrollBegin,
+ WebInputEvent::GestureScrollUpdate,
+ WebInputEvent::GestureScrollUpdateWithoutPropagation,
+ WebInputEvent::GesturePinchBegin,
+ WebInputEvent::GesturePinchUpdate,
+ WebInputEvent::GesturePinchEnd,
+ WebInputEvent::GestureScrollEnd};
+ for (int i = 0; i < kEventTypesLength; ++i) {
+ WebInputEvent::Type type = eventTypes[i];
+ if (!WebInputEventTraits::IgnoresAckDisposition(GetEventWithType(type))) {
+ SimulateGestureEvent(type, blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
+ EXPECT_EQ(1, client_->in_flight_event_count());
+ EXPECT_TRUE(HasPendingEvents());
+
+ SendInputEventACK(type, INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
+ EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
+ EXPECT_EQ(0, client_->in_flight_event_count());
+ EXPECT_FALSE(HasPendingEvents());
+ continue;
}
- SimulateTouchEvent(type);
+ SimulateGestureEvent(type, blink::WebGestureDeviceTouchscreen);
EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
EXPECT_EQ(0, client_->in_flight_event_count());
- SendInputEventACK(type, INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
- EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
+ EXPECT_FALSE(HasPendingEvents());
}
}
-TEST_F(InputRouterImplTest, GestureTypesIgnoringAck) {
- int start_type = static_cast<int>(WebInputEvent::GestureScrollBegin);
- int end_type = static_cast<int>(WebInputEvent::GesturePinchUpdate);
+TEST_F(InputRouterImplTest, MouseTypesIgnoringAck) {
+ int start_type = static_cast<int>(WebInputEvent::MouseDown);
+ int end_type = static_cast<int>(WebInputEvent::ContextMenu);
+ ASSERT_LT(start_type, end_type);
for (int i = start_type; i <= end_type; ++i) {
WebInputEvent::Type type = static_cast<WebInputEvent::Type>(i);
- if (!WebInputEventTraits::IgnoresAckDisposition(type))
- continue;
+ int expected_in_flight_event_count =
+ WebInputEventTraits::IgnoresAckDisposition(GetEventWithType(type)) ? 0
+ : 1;
- SimulateGestureEvent(type, WebGestureEvent::Touchscreen);
+ // Note: Mouse event acks are never forwarded to the ack handler, so the key
+ // result here is that ignored ack types don't affect the in-flight count.
+ SimulateMouseEvent(type, 0, 0);
EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
- EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
- EXPECT_EQ(0, client_->in_flight_event_count());
- SendInputEventACK(type, INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
+ EXPECT_EQ(expected_in_flight_event_count, client_->in_flight_event_count());
+ if (expected_in_flight_event_count) {
+ SendInputEventACK(type, INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
+ EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
+ EXPECT_EQ(0, client_->in_flight_event_count());
+ }
+ }
+}
+
+// Guard against breaking changes to the list of ignored event ack types in
+// |WebInputEventTraits::IgnoresAckDisposition|.
+TEST_F(InputRouterImplTest, RequiredEventAckTypes) {
+ const WebInputEvent::Type kRequiredEventAckTypes[] = {
+ WebInputEvent::MouseMove,
+ WebInputEvent::MouseWheel,
+ WebInputEvent::RawKeyDown,
+ WebInputEvent::KeyDown,
+ WebInputEvent::KeyUp,
+ WebInputEvent::Char,
+ WebInputEvent::GestureScrollUpdate,
+ WebInputEvent::GestureFlingStart,
+ WebInputEvent::GestureFlingCancel,
+ WebInputEvent::GesturePinchUpdate,
+ WebInputEvent::TouchStart,
+ WebInputEvent::TouchMove
+ };
+ for (size_t i = 0; i < arraysize(kRequiredEventAckTypes); ++i) {
+ const WebInputEvent::Type required_ack_type = kRequiredEventAckTypes[i];
+ EXPECT_FALSE(WebInputEventTraits::IgnoresAckDisposition(
+ GetEventWithType(required_ack_type)));
}
}
@@ -755,122 +905,140 @@ TEST_F(InputRouterImplTest, GestureTypesIgnoringAckInterleaved) {
// Interleave a few events that do and do not ignore acks, ensuring that
// ack-ignoring events aren't dispatched until all prior events which observe
// their ack disposition have been dispatched.
- SimulateGestureEvent(WebInputEvent::GesturePinchUpdate,
- WebGestureEvent::Touchscreen);
+
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ ASSERT_EQ(1U, GetSentMessageCountAndResetSink());
+ EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
+ EXPECT_EQ(0, client_->in_flight_event_count());
+
+ SimulateGestureEvent(WebInputEvent::GestureScrollUpdate,
+ blink::WebGestureDeviceTouchscreen);
ASSERT_EQ(1U, GetSentMessageCountAndResetSink());
EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
EXPECT_EQ(1, client_->in_flight_event_count());
SimulateGestureEvent(WebInputEvent::GestureTapDown,
- WebGestureEvent::Touchscreen);
+ blink::WebGestureDeviceTouchscreen);
EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
EXPECT_EQ(1, client_->in_flight_event_count());
- SimulateGestureEvent(WebInputEvent::GesturePinchUpdate,
- WebGestureEvent::Touchscreen);
+ SimulateGestureEvent(WebInputEvent::GestureScrollUpdate,
+ blink::WebGestureDeviceTouchscreen);
EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
SimulateGestureEvent(WebInputEvent::GestureShowPress,
- WebGestureEvent::Touchscreen);
+ blink::WebGestureDeviceTouchscreen);
EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
- SimulateGestureEvent(WebInputEvent::GesturePinchUpdate,
- WebGestureEvent::Touchscreen);
+ SimulateGestureEvent(WebInputEvent::GestureScrollUpdate,
+ blink::WebGestureDeviceTouchscreen);
EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
SimulateGestureEvent(WebInputEvent::GestureTapCancel,
- WebGestureEvent::Touchscreen);
+ blink::WebGestureDeviceTouchscreen);
EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
- // Now ack each event. Ack-ignoring events should not be dispatched until all
- // prior events which observe ack disposition have been fired, at which
- // point they should be sent immediately. They should also have no effect
- // on the in-flight event count.
- SendInputEventACK(WebInputEvent::GesturePinchUpdate,
+ // Now ack each ack-respecting event. Ack-ignoring events should not be
+ // dispatched until all prior events which observe ack disposition have been
+ // fired, at which point they should be sent immediately. They should also
+ // have no effect on the in-flight event count.
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(2U, GetSentMessageCountAndResetSink());
EXPECT_EQ(2U, ack_handler_->GetAndResetAckCount());
EXPECT_EQ(1, client_->in_flight_event_count());
- // For events which ignore ack disposition, non-synthetic acks are ignored.
- SendInputEventACK(WebInputEvent::GestureTapDown,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
- EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
- EXPECT_EQ(1, client_->in_flight_event_count());
-
- SendInputEventACK(WebInputEvent::GesturePinchUpdate,
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(2U, GetSentMessageCountAndResetSink());
EXPECT_EQ(2U, ack_handler_->GetAndResetAckCount());
EXPECT_EQ(1, client_->in_flight_event_count());
- // For events which ignore ack disposition, non-synthetic acks are ignored.
- SendInputEventACK(WebInputEvent::GestureShowPress,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
- EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
- EXPECT_EQ(1, client_->in_flight_event_count());
-
- SendInputEventACK(WebInputEvent::GesturePinchUpdate,
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
EXPECT_EQ(2U, ack_handler_->GetAndResetAckCount());
EXPECT_EQ(0, client_->in_flight_event_count());
-
- // For events which ignore ack disposition, non-synthetic acks are ignored.
- SendInputEventACK(WebInputEvent::GestureTapCancel,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
- EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
- EXPECT_EQ(0, client_->in_flight_event_count());
}
// Test that GestureShowPress events don't get out of order due to
// ignoring their acks.
TEST_F(InputRouterImplTest, GestureShowPressIsInOrder) {
- SimulateGestureEvent(WebInputEvent::GestureTap,
- WebGestureEvent::Touchscreen);
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
+
+ // GesturePinchBegin ignores its ack.
+ SimulateGestureEvent(WebInputEvent::GesturePinchBegin,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
+
+ // GesturePinchUpdate waits for an ack.
+ // This also verifies that GesturePinchUpdates for touchscreen are sent
+ // to the renderer (in contrast to the TrackpadPinchUpdate test).
+ SimulateGestureEvent(WebInputEvent::GesturePinchUpdate,
+ blink::WebGestureDeviceTouchscreen);
EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
SimulateGestureEvent(WebInputEvent::GestureShowPress,
- WebGestureEvent::Touchscreen);
-
+ blink::WebGestureDeviceTouchscreen);
EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
// The ShowPress, though it ignores ack, is still stuck in the queue
- // behind the Tap which requires an ack.
+ // behind the PinchUpdate which requires an ack.
EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
SimulateGestureEvent(WebInputEvent::GestureShowPress,
- WebGestureEvent::Touchscreen);
-
+ blink::WebGestureDeviceTouchscreen);
EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
// ShowPress has entered the queue.
EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
- SendInputEventACK(WebInputEvent::GestureTap,
+ SendInputEventACK(WebInputEvent::GesturePinchUpdate,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
-
// Now that the Tap has been ACKed, the ShowPress events should receive
- // synthetics acks, and fire immediately.
+ // synthetic acks, and fire immediately.
EXPECT_EQ(2U, GetSentMessageCountAndResetSink());
EXPECT_EQ(3U, ack_handler_->GetAndResetAckCount());
}
-// Test that touch ack timeout behavior is properly configured via the command
-// line, and toggled by the view update flags.
+// Test that touch ack timeout behavior is properly toggled by view update flags
+// and allowed touch actions.
TEST_F(InputRouterImplTest, TouchAckTimeoutConfigured) {
- CommandLine::ForCurrentProcess()->AppendSwitchASCII(
- switches::kTouchAckTimeoutDelayMs, "5");
- TearDown();
- SetUp();
+ const int timeout_ms = 1;
+ SetUpForTouchAckTimeoutTest(timeout_ms);
+ ASSERT_TRUE(TouchEventTimeoutEnabled());
+
+ // Verify that the touch ack timeout fires upon the delayed ack.
+ PressTouchPoint(1, 1);
+ SendTouchEvent();
+ EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ RunTasksAndWait(base::TimeDelta::FromMilliseconds(timeout_ms + 1));
+
+ // The timed-out event should have been ack'ed.
+ EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
+ EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
+
+ // Ack'ing the timed-out event should fire a TouchCancel.
+ SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // The remainder of the touch sequence should be dropped.
+ ReleaseTouchPoint(0);
+ SendTouchEvent();
+ EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
+ EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
ASSERT_TRUE(TouchEventTimeoutEnabled());
// A fixed page scale or mobile viewport should disable the touch timeout.
@@ -889,6 +1057,669 @@ TEST_F(InputRouterImplTest, TouchAckTimeoutConfigured) {
input_router()->OnViewUpdated(InputRouter::VIEW_FLAGS_NONE);
EXPECT_TRUE(TouchEventTimeoutEnabled());
+
+ // TOUCH_ACTION_NONE (and no other touch-action) should disable the timeout.
+ OnHasTouchEventHandlers(true);
+ PressTouchPoint(1, 1);
+ SendTouchEvent();
+ OnSetTouchAction(TOUCH_ACTION_PAN_Y);
+ EXPECT_TRUE(TouchEventTimeoutEnabled());
+ ReleaseTouchPoint(0);
+ SendTouchEvent();
+ SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_CONSUMED);
+ SendInputEventACK(WebInputEvent::TouchEnd, INPUT_EVENT_ACK_STATE_CONSUMED);
+
+ PressTouchPoint(1, 1);
+ SendTouchEvent();
+ OnSetTouchAction(TOUCH_ACTION_NONE);
+ EXPECT_FALSE(TouchEventTimeoutEnabled());
+ ReleaseTouchPoint(0);
+ SendTouchEvent();
+ SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_CONSUMED);
+ SendInputEventACK(WebInputEvent::TouchEnd, INPUT_EVENT_ACK_STATE_CONSUMED);
+
+ // As the touch-action is reset by a new touch sequence, the timeout behavior
+ // should be restored.
+ PressTouchPoint(1, 1);
+ SendTouchEvent();
+ EXPECT_TRUE(TouchEventTimeoutEnabled());
+}
+
+// Test that a touch sequenced preceded by TOUCH_ACTION_NONE is not affected by
+// the touch timeout.
+TEST_F(InputRouterImplTest,
+ TouchAckTimeoutDisabledForTouchSequenceAfterTouchActionNone) {
+ const int timeout_ms = 1;
+ SetUpForTouchAckTimeoutTest(timeout_ms);
+ ASSERT_TRUE(TouchEventTimeoutEnabled());
+ OnHasTouchEventHandlers(true);
+
+ // Start a touch sequence.
+ PressTouchPoint(1, 1);
+ SendTouchEvent();
+
+ // TOUCH_ACTION_NONE should disable the timeout.
+ OnSetTouchAction(TOUCH_ACTION_NONE);
+ SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_FALSE(TouchEventTimeoutEnabled());
+
+ // End the touch sequence.
+ ReleaseTouchPoint(0);
+ SendTouchEvent();
+ SendInputEventACK(WebInputEvent::TouchEnd, INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_FALSE(TouchEventTimeoutEnabled());
+ ack_handler_->GetAndResetAckCount();
+ GetSentMessageCountAndResetSink();
+
+ // Start another touch sequence. While this does restore the touch timeout
+ // the timeout will not apply until the *next* touch sequence. This affords a
+ // touch-action response from the renderer without racing against the timeout.
+ PressTouchPoint(1, 1);
+ SendTouchEvent();
+ EXPECT_TRUE(TouchEventTimeoutEnabled());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Delay the ack. The timeout should *not* fire.
+ RunTasksAndWait(base::TimeDelta::FromMilliseconds(timeout_ms + 1));
+ EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
+ EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
+
+ // Finally send the ack. The touch sequence should not have been cancelled.
+ SendInputEventACK(WebInputEvent::TouchStart,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_TRUE(TouchEventTimeoutEnabled());
+ EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
+ EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
+
+ // End the sequence.
+ ReleaseTouchPoint(0);
+ SendTouchEvent();
+ SendInputEventACK(WebInputEvent::TouchEnd, INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // A new touch sequence should (finally) be subject to the timeout.
+ PressTouchPoint(1, 1);
+ SendTouchEvent();
+ EXPECT_TRUE(TouchEventTimeoutEnabled());
+ EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Wait for the touch ack timeout to fire.
+ RunTasksAndWait(base::TimeDelta::FromMilliseconds(timeout_ms + 1));
+ EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
+}
+
+// Test that TouchActionFilter::ResetTouchAction is called before the
+// first touch event for a touch sequence reaches the renderer.
+TEST_F(InputRouterImplTest, TouchActionResetBeforeEventReachesRenderer) {
+ OnHasTouchEventHandlers(true);
+
+ // Sequence 1.
+ PressTouchPoint(1, 1);
+ SendTouchEvent();
+ OnSetTouchAction(TOUCH_ACTION_NONE);
+ MoveTouchPoint(0, 50, 50);
+ SendTouchEvent();
+ ReleaseTouchPoint(0);
+ SendTouchEvent();
+
+ // Sequence 2.
+ PressTouchPoint(1, 1);
+ SendTouchEvent();
+ MoveTouchPoint(0, 50, 50);
+ SendTouchEvent();
+ ReleaseTouchPoint(0);
+ SendTouchEvent();
+
+ SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_CONSUMED);
+ SendInputEventACK(WebInputEvent::TouchMove, INPUT_EVENT_ACK_STATE_CONSUMED);
+
+ // Ensure touch action is still none, as the next touch start hasn't been
+ // acked yet. ScrollBegin and ScrollEnd don't require acks.
+ EXPECT_EQ(3U, GetSentMessageCountAndResetSink());
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
+ SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
+
+ // This allows the next touch sequence to start.
+ SendInputEventACK(WebInputEvent::TouchEnd, INPUT_EVENT_ACK_STATE_CONSUMED);
+
+ // Ensure touch action has been set to auto, as a new touch sequence has
+ // started.
+ SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_CONSUMED);
+ SendInputEventACK(WebInputEvent::TouchMove, INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_EQ(3U, GetSentMessageCountAndResetSink());
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ SendInputEventACK(WebInputEvent::TouchEnd, INPUT_EVENT_ACK_STATE_CONSUMED);
+}
+
+// Test that TouchActionFilter::ResetTouchAction is called when a new touch
+// sequence has no consumer.
+TEST_F(InputRouterImplTest, TouchActionResetWhenTouchHasNoConsumer) {
+ OnHasTouchEventHandlers(true);
+
+ // Sequence 1.
+ PressTouchPoint(1, 1);
+ SendTouchEvent();
+ MoveTouchPoint(0, 50, 50);
+ SendTouchEvent();
+ OnSetTouchAction(TOUCH_ACTION_NONE);
+ SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_CONSUMED);
+ SendInputEventACK(WebInputEvent::TouchMove, INPUT_EVENT_ACK_STATE_CONSUMED);
+
+ ReleaseTouchPoint(0);
+ SendTouchEvent();
+
+ // Sequence 2
+ PressTouchPoint(1, 1);
+ SendTouchEvent();
+ MoveTouchPoint(0, 50, 50);
+ SendTouchEvent();
+ ReleaseTouchPoint(0);
+ SendTouchEvent();
+
+ // Ensure we have touch-action:none. ScrollBegin and ScrollEnd don't require
+ // acks.
+ EXPECT_EQ(3U, GetSentMessageCountAndResetSink());
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
+ SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
+
+ SendInputEventACK(WebInputEvent::TouchEnd, INPUT_EVENT_ACK_STATE_CONSUMED);
+ SendInputEventACK(WebInputEvent::TouchStart,
+ INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
+
+ // Ensure touch action has been set to auto, as the touch had no consumer.
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+}
+
+// Test that TouchActionFilter::ResetTouchAction is called when the touch
+// handler is removed.
+TEST_F(InputRouterImplTest, TouchActionResetWhenTouchHandlerRemoved) {
+ // Touch sequence with touch handler.
+ OnHasTouchEventHandlers(true);
+ PressTouchPoint(1, 1);
+ SendTouchEvent();
+ MoveTouchPoint(0, 50, 50);
+ SendTouchEvent();
+ OnSetTouchAction(TOUCH_ACTION_NONE);
+ ReleaseTouchPoint(0);
+ SendTouchEvent();
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Ensure we have touch-action:none, suppressing scroll events.
+ SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ SendInputEventACK(WebInputEvent::TouchMove,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
+
+ SendInputEventACK(WebInputEvent::TouchEnd,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
+
+ // Sequence without a touch handler. Note that in this case, the view may not
+ // necessarily forward touches to the router (as no touch handler exists).
+ OnHasTouchEventHandlers(false);
+
+ // Ensure touch action has been set to auto, as the touch handler has been
+ // removed.
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+}
+
+// Test that the double tap gesture depends on the touch action of the first
+// tap.
+TEST_F(InputRouterImplTest, DoubleTapGestureDependsOnFirstTap) {
+ OnHasTouchEventHandlers(true);
+
+ // Sequence 1.
+ PressTouchPoint(1, 1);
+ SendTouchEvent();
+ OnSetTouchAction(TOUCH_ACTION_NONE);
+ SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_CONSUMED);
+
+ ReleaseTouchPoint(0);
+ SendTouchEvent();
+
+ // Sequence 2
+ PressTouchPoint(1, 1);
+ SendTouchEvent();
+
+ // First tap.
+ EXPECT_EQ(2U, GetSentMessageCountAndResetSink());
+ SimulateGestureEvent(WebInputEvent::GestureTapDown,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // The GestureTapUnconfirmed is converted into a tap, as the touch action is
+ // none.
+ SimulateGestureEvent(WebInputEvent::GestureTapUnconfirmed,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ // This test will become invalid if GestureTap stops requiring an ack.
+ ASSERT_FALSE(WebInputEventTraits::IgnoresAckDisposition(
+ GetEventWithType(WebInputEvent::GestureTap)));
+ EXPECT_EQ(2, client_->in_flight_event_count());
+ SendInputEventACK(WebInputEvent::GestureTap,
+ INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_EQ(1, client_->in_flight_event_count());
+
+ // This tap gesture is dropped, since the GestureTapUnconfirmed was turned
+ // into a tap.
+ SimulateGestureEvent(WebInputEvent::GestureTap,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
+
+ SendInputEventACK(WebInputEvent::TouchEnd, INPUT_EVENT_ACK_STATE_CONSUMED);
+ SendInputEventACK(WebInputEvent::TouchStart,
+ INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
+
+ // Second Tap.
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ SimulateGestureEvent(WebInputEvent::GestureTapDown,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Although the touch-action is now auto, the double tap still won't be
+ // dispatched, because the first tap occured when the touch-action was none.
+ SimulateGestureEvent(WebInputEvent::GestureDoubleTap,
+ blink::WebGestureDeviceTouchscreen);
+ // This test will become invalid if GestureDoubleTap stops requiring an ack.
+ ASSERT_FALSE(WebInputEventTraits::IgnoresAckDisposition(
+ GetEventWithType(WebInputEvent::GestureDoubleTap)));
+ EXPECT_EQ(1, client_->in_flight_event_count());
+ SendInputEventACK(WebInputEvent::GestureTap, INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_EQ(0, client_->in_flight_event_count());
+}
+
+// Test that the router will call the client's |DidFlush| after all events have
+// been dispatched following a call to |Flush|.
+TEST_F(InputRouterImplTest, InputFlush) {
+ EXPECT_FALSE(HasPendingEvents());
+
+ // Flushing an empty router should immediately trigger DidFlush.
+ Flush();
+ EXPECT_EQ(1U, GetAndResetDidFlushCount());
+ EXPECT_FALSE(HasPendingEvents());
+
+ // Queue a TouchStart.
+ OnHasTouchEventHandlers(true);
+ PressTouchPoint(1, 1);
+ SendTouchEvent();
+ EXPECT_TRUE(HasPendingEvents());
+
+ // DidFlush should be called only after the event is ack'ed.
+ Flush();
+ EXPECT_EQ(0U, GetAndResetDidFlushCount());
+ SendInputEventACK(WebInputEvent::TouchStart,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetAndResetDidFlushCount());
+
+ // Ensure different types of enqueued events will prevent the DidFlush call
+ // until all such events have been fully dispatched.
+ MoveTouchPoint(0, 50, 50);
+ SendTouchEvent();
+ ASSERT_TRUE(HasPendingEvents());
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ SimulateGestureEvent(WebInputEvent::GestureScrollUpdate,
+ blink::WebGestureDeviceTouchscreen);
+ SimulateGestureEvent(WebInputEvent::GesturePinchBegin,
+ blink::WebGestureDeviceTouchscreen);
+ SimulateGestureEvent(WebInputEvent::GesturePinchUpdate,
+ blink::WebGestureDeviceTouchscreen);
+ Flush();
+ EXPECT_EQ(0U, GetAndResetDidFlushCount());
+
+ // Repeated flush calls should have no effect.
+ Flush();
+ EXPECT_EQ(0U, GetAndResetDidFlushCount());
+
+ // There are still pending gestures.
+ SendInputEventACK(WebInputEvent::TouchMove,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(0U, GetAndResetDidFlushCount());
+ EXPECT_TRUE(HasPendingEvents());
+
+ // One more gesture to go.
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_EQ(0U, GetAndResetDidFlushCount());
+ EXPECT_TRUE(HasPendingEvents());
+
+ // The final ack'ed gesture should trigger the DidFlush.
+ SendInputEventACK(WebInputEvent::GesturePinchUpdate,
+ INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_EQ(1U, GetAndResetDidFlushCount());
+ EXPECT_FALSE(HasPendingEvents());
+}
+
+// Test that GesturePinchUpdate is handled specially for trackpad
+TEST_F(InputRouterImplTest, TouchpadPinchUpdate) {
+ // GesturePinchUpdate for trackpad sends synthetic wheel events.
+ // Note that the Touchscreen case is verified as NOT doing this as
+ // part of the ShowPressIsInOrder test.
+
+ SimulateGesturePinchUpdateEvent(
+ 1.5f, 20, 25, 0, blink::WebGestureDeviceTouchpad);
+
+ // Verify we actually sent a special wheel event to the renderer.
+ const WebInputEvent* input_event =
+ GetInputEventFromMessage(*process_->sink().GetMessageAt(0));
+ ASSERT_EQ(WebInputEvent::MouseWheel, input_event->type);
+ const WebMouseWheelEvent* wheel_event =
+ static_cast<const WebMouseWheelEvent*>(input_event);
+ EXPECT_EQ(20, wheel_event->x);
+ EXPECT_EQ(25, wheel_event->y);
+ EXPECT_EQ(20, wheel_event->globalX);
+ EXPECT_EQ(25, wheel_event->globalY);
+ EXPECT_EQ(20, wheel_event->windowX);
+ EXPECT_EQ(25, wheel_event->windowY);
+ EXPECT_EQ(PinchScaleToWheelDelta(1.5), wheel_event->deltaY);
+ EXPECT_EQ(0, wheel_event->deltaX);
+ EXPECT_EQ(1, wheel_event->hasPreciseScrollingDeltas);
+ EXPECT_EQ(1, wheel_event->wheelTicksY);
+ EXPECT_EQ(0, wheel_event->wheelTicksX);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Indicate that the wheel event was unhandled.
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+
+ // Check that the correct unhandled pinch event was received.
+ EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
+ ASSERT_EQ(WebInputEvent::GesturePinchUpdate, ack_handler_->ack_event_type());
+ EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, ack_handler_->ack_state());
+ EXPECT_EQ(1.5f, ack_handler_->acked_gesture_event().data.pinchUpdate.scale);
+ EXPECT_EQ(0, client_->in_flight_event_count());
+
+ // Second a second pinch event.
+ SimulateGesturePinchUpdateEvent(
+ 0.3f, 20, 25, 0, blink::WebGestureDeviceTouchpad);
+ input_event = GetInputEventFromMessage(*process_->sink().GetMessageAt(0));
+ ASSERT_EQ(WebInputEvent::MouseWheel, input_event->type);
+ wheel_event = static_cast<const WebMouseWheelEvent*>(input_event);
+ EXPECT_FLOAT_EQ(PinchScaleToWheelDelta(0.3f), wheel_event->deltaY);
+ EXPECT_EQ(1, wheel_event->hasPreciseScrollingDeltas);
+ EXPECT_EQ(-1, wheel_event->wheelTicksY);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Indicate that the wheel event was handled this time.
+ SendInputEventACK(WebInputEvent::MouseWheel, INPUT_EVENT_ACK_STATE_CONSUMED);
+
+ // Check that the correct HANDLED pinch event was received.
+ EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
+ EXPECT_EQ(WebInputEvent::GesturePinchUpdate, ack_handler_->ack_event_type());
+ EXPECT_EQ(INPUT_EVENT_ACK_STATE_CONSUMED, ack_handler_->ack_state());
+ EXPECT_FLOAT_EQ(0.3f,
+ ack_handler_->acked_gesture_event().data.pinchUpdate.scale);
+}
+
+// Test that touchpad pinch events are coalesced property, with their synthetic
+// wheel events getting the right ACKs.
+TEST_F(InputRouterImplTest, TouchpadPinchCoalescing) {
+ // Send the first pinch.
+ SimulateGesturePinchUpdateEvent(
+ 1.5f, 20, 25, 0, blink::WebGestureDeviceTouchpad);
+
+ // Verify we sent the wheel event to the renderer.
+ const WebInputEvent* input_event =
+ GetInputEventFromMessage(*process_->sink().GetMessageAt(0));
+ ASSERT_EQ(WebInputEvent::MouseWheel, input_event->type);
+ const WebMouseWheelEvent* wheel_event =
+ static_cast<const WebMouseWheelEvent*>(input_event);
+ EXPECT_EQ(PinchScaleToWheelDelta(1.5f), wheel_event->deltaY);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
+ EXPECT_EQ(1, client_->in_flight_event_count());
+
+ // Send a second pinch, this should be queued in the GestureEventQueue.
+ SimulateGesturePinchUpdateEvent(
+ 1.6f, 20, 25, 0, blink::WebGestureDeviceTouchpad);
+ EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
+ EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
+
+ // Send a third pinch, this should be coalesced into the second in the
+ // GestureEventQueue.
+ SimulateGesturePinchUpdateEvent(
+ 1.7f, 20, 25, 0, blink::WebGestureDeviceTouchpad);
+ EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
+ EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
+
+ // Indicate that the first wheel event was unhandled and verify the ACK.
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
+ EXPECT_EQ(WebInputEvent::GesturePinchUpdate, ack_handler_->ack_event_type());
+ EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, ack_handler_->ack_state());
+ EXPECT_EQ(1.5f, ack_handler_->acked_gesture_event().data.pinchUpdate.scale);
+
+ // Verify a second wheel event was sent representing the 2nd and 3rd pinch
+ // events.
+ EXPECT_EQ(1, client_->in_flight_event_count());
+ input_event = GetInputEventFromMessage(*process_->sink().GetMessageAt(0));
+ ASSERT_EQ(WebInputEvent::MouseWheel, input_event->type);
+ wheel_event = static_cast<const WebMouseWheelEvent*>(input_event);
+ EXPECT_FLOAT_EQ(PinchScaleToWheelDelta(1.6f * 1.7f),
+ PinchScaleToWheelDelta(1.6f) + PinchScaleToWheelDelta(1.7f));
+ EXPECT_FLOAT_EQ(PinchScaleToWheelDelta(1.6f * 1.7f), wheel_event->deltaY);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
+
+ // Indicate that the second wheel event was handled and verify the ACK.
+ SendInputEventACK(WebInputEvent::MouseWheel, INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
+ EXPECT_EQ(WebInputEvent::GesturePinchUpdate, ack_handler_->ack_event_type());
+ EXPECT_EQ(INPUT_EVENT_ACK_STATE_CONSUMED, ack_handler_->ack_state());
+ EXPECT_FLOAT_EQ(1.6f * 1.7f,
+ ack_handler_->acked_gesture_event().data.pinchUpdate.scale);
+}
+
+// Test interleaving pinch and wheel events.
+TEST_F(InputRouterImplTest, TouchpadPinchAndWheel) {
+ // Simulate queued wheel and pinch events events.
+ // Note that in practice interleaving pinch and wheel events should be rare
+ // (eg. requires the use of a mouse and trackpad at the same time).
+
+ // Use the control modifier to match the synthetic wheel events so that
+ // they're elligble for coalescing.
+ int mod = WebInputEvent::ControlKey;
+
+ // Event 1: sent directly.
+ SimulateWheelEvent(0, -5, mod, true);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Event 2: enqueued in InputRouter.
+ SimulateWheelEvent(0, -10, mod, true);
+ EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
+
+ // Event 3: enqueued in InputRouter, not coalesced into #2.
+ SimulateGesturePinchUpdateEvent(
+ 1.5f, 20, 25, 0, blink::WebGestureDeviceTouchpad);
+ EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
+
+ // Event 4: enqueued in GestureEventQueue.
+ SimulateGesturePinchUpdateEvent(
+ 1.2f, 20, 25, 0, blink::WebGestureDeviceTouchpad);
+ EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
+
+ // Event 5: coalesced into wheel event for #3.
+ SimulateWheelEvent(2, 0, mod, true);
+ EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
+
+ // Send ack for #1.
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
+ EXPECT_EQ(WebInputEvent::MouseWheel, ack_handler_->ack_event_type());
+
+ // Verify we sent #2.
+ ASSERT_EQ(1U, process_->sink().message_count());
+ const WebInputEvent* input_event =
+ GetInputEventFromMessage(*process_->sink().GetMessageAt(0));
+ ASSERT_EQ(WebInputEvent::MouseWheel, input_event->type);
+ const WebMouseWheelEvent* wheel_event =
+ static_cast<const WebMouseWheelEvent*>(input_event);
+ EXPECT_EQ(0, wheel_event->deltaX);
+ EXPECT_EQ(-10, wheel_event->deltaY);
+ EXPECT_EQ(mod, wheel_event->modifiers);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Send ack for #2.
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
+ EXPECT_EQ(WebInputEvent::MouseWheel, ack_handler_->ack_event_type());
+
+ // Verify we sent #3 (with #5 coalesced in).
+ ASSERT_EQ(1U, process_->sink().message_count());
+ input_event = GetInputEventFromMessage(*process_->sink().GetMessageAt(0));
+ ASSERT_EQ(WebInputEvent::MouseWheel, input_event->type);
+ wheel_event = static_cast<const WebMouseWheelEvent*>(input_event);
+ EXPECT_EQ(2, wheel_event->deltaX);
+ EXPECT_EQ(PinchScaleToWheelDelta(1.5f), wheel_event->deltaY);
+ EXPECT_EQ(mod, wheel_event->modifiers);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Send ack for #3.
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
+ EXPECT_EQ(WebInputEvent::GesturePinchUpdate, ack_handler_->ack_event_type());
+
+ // Verify we sent #4.
+ ASSERT_EQ(1U, process_->sink().message_count());
+ input_event = GetInputEventFromMessage(*process_->sink().GetMessageAt(0));
+ ASSERT_EQ(WebInputEvent::MouseWheel, input_event->type);
+ wheel_event = static_cast<const WebMouseWheelEvent*>(input_event);
+ EXPECT_EQ(0, wheel_event->deltaX);
+ EXPECT_FLOAT_EQ(PinchScaleToWheelDelta(1.2f), wheel_event->deltaY);
+ EXPECT_EQ(mod, wheel_event->modifiers);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Send ack for #4.
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
+ EXPECT_EQ(WebInputEvent::GesturePinchUpdate, ack_handler_->ack_event_type());
+}
+
+// Test proper handling of touchpad Gesture{Pinch,Scroll}Update sequences.
+TEST_F(InputRouterImplTest, TouchpadPinchAndScrollUpdate) {
+ // The first scroll should be sent immediately.
+ SimulateGestureEvent(WebInputEvent::GestureScrollUpdate,
+ blink::WebGestureDeviceTouchpad);
+ ASSERT_EQ(1U, GetSentMessageCountAndResetSink());
+ EXPECT_EQ(1, client_->in_flight_event_count());
+
+ // Subsequent scroll and pinch events should remain queued, coalescing as
+ // more trackpad events arrive.
+ SimulateGestureEvent(WebInputEvent::GesturePinchUpdate,
+ blink::WebGestureDeviceTouchpad);
+ ASSERT_EQ(0U, GetSentMessageCountAndResetSink());
+ EXPECT_EQ(1, client_->in_flight_event_count());
+
+ SimulateGestureEvent(WebInputEvent::GestureScrollUpdate,
+ blink::WebGestureDeviceTouchpad);
+ ASSERT_EQ(0U, GetSentMessageCountAndResetSink());
+ EXPECT_EQ(1, client_->in_flight_event_count());
+
+ SimulateGestureEvent(WebInputEvent::GesturePinchUpdate,
+ blink::WebGestureDeviceTouchpad);
+ ASSERT_EQ(0U, GetSentMessageCountAndResetSink());
+ EXPECT_EQ(1, client_->in_flight_event_count());
+
+ SimulateGestureEvent(WebInputEvent::GestureScrollUpdate,
+ blink::WebGestureDeviceTouchpad);
+ ASSERT_EQ(0U, GetSentMessageCountAndResetSink());
+ EXPECT_EQ(1, client_->in_flight_event_count());
+
+ // Ack'ing the first scroll should trigger both the coalesced scroll and the
+ // coalesced pinch events (which is sent to the renderer as a wheel event).
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
+ EXPECT_EQ(2U, GetSentMessageCountAndResetSink());
+ EXPECT_EQ(2, client_->in_flight_event_count());
+
+ // Ack the second scroll.
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
+ EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
+ EXPECT_EQ(1, client_->in_flight_event_count());
+
+ // Ack the wheel event.
+ SendInputEventACK(WebInputEvent::MouseWheel, INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
+ EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
+ EXPECT_EQ(0, client_->in_flight_event_count());
+}
+
+// Test proper routing of overscroll notifications received either from
+// event acks or from |DidOverscroll| IPC messages.
+TEST_F(InputRouterImplTest, OverscrollDispatch) {
+ DidOverscrollParams overscroll;
+ overscroll.accumulated_overscroll = gfx::Vector2dF(-14, 14);
+ overscroll.latest_overscroll_delta = gfx::Vector2dF(-7, 0);
+ overscroll.current_fling_velocity = gfx::Vector2dF(-1, 0);
+
+ input_router_->OnMessageReceived(InputHostMsg_DidOverscroll(0, overscroll));
+ DidOverscrollParams client_overscroll = client_->GetAndResetOverscroll();
+ EXPECT_EQ(overscroll.accumulated_overscroll,
+ client_overscroll.accumulated_overscroll);
+ EXPECT_EQ(overscroll.latest_overscroll_delta,
+ client_overscroll.latest_overscroll_delta);
+ EXPECT_EQ(overscroll.current_fling_velocity,
+ client_overscroll.current_fling_velocity);
+
+ DidOverscrollParams wheel_overscroll;
+ wheel_overscroll.accumulated_overscroll = gfx::Vector2dF(7, -7);
+ wheel_overscroll.latest_overscroll_delta = gfx::Vector2dF(3, 0);
+ wheel_overscroll.current_fling_velocity = gfx::Vector2dF(1, 0);
+
+ SimulateWheelEvent(3, 0, 0, false);
+ InputHostMsg_HandleInputEvent_ACK_Params ack;
+ ack.type = WebInputEvent::MouseWheel;
+ ack.state = INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
+ ack.overscroll.reset(new DidOverscrollParams(wheel_overscroll));
+ input_router_->OnMessageReceived(InputHostMsg_HandleInputEvent_ACK(0, ack));
+
+ client_overscroll = client_->GetAndResetOverscroll();
+ EXPECT_EQ(wheel_overscroll.accumulated_overscroll,
+ client_overscroll.accumulated_overscroll);
+ EXPECT_EQ(wheel_overscroll.latest_overscroll_delta,
+ client_overscroll.latest_overscroll_delta);
+ EXPECT_EQ(wheel_overscroll.current_fling_velocity,
+ client_overscroll.current_fling_velocity);
}
} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/mock_input_ack_handler.cc b/chromium/content/browser/renderer_host/input/mock_input_ack_handler.cc
index 0ab903f8c84..30928098855 100644
--- a/chromium/content/browser/renderer_host/input/mock_input_ack_handler.cc
+++ b/chromium/content/browser/renderer_host/input/mock_input_ack_handler.cc
@@ -18,10 +18,12 @@ using blink::WebTouchPoint;
namespace content {
MockInputAckHandler::MockInputAckHandler()
- : input_router_(NULL),
- ack_count_(0),
- unexpected_event_ack_called_(false),
- ack_state_(INPUT_EVENT_ACK_STATE_UNKNOWN) {}
+ : input_router_(NULL),
+ ack_count_(0),
+ unexpected_event_ack_called_(false),
+ ack_event_type_(WebInputEvent::Undefined),
+ ack_state_(INPUT_EVENT_ACK_STATE_UNKNOWN) {
+}
MockInputAckHandler::~MockInputAckHandler() {}
@@ -30,7 +32,7 @@ void MockInputAckHandler::OnKeyboardEventAck(
InputEventAckState ack_result) {
VLOG(1) << __FUNCTION__ << " called!";
acked_key_event_ = event;
- RecordAckCalled(ack_result);
+ RecordAckCalled(event.type, ack_result);
}
void MockInputAckHandler::OnWheelEventAck(
@@ -38,7 +40,7 @@ void MockInputAckHandler::OnWheelEventAck(
InputEventAckState ack_result) {
VLOG(1) << __FUNCTION__ << " called!";
acked_wheel_event_ = event.event;
- RecordAckCalled(ack_result);
+ RecordAckCalled(event.event.type, ack_result);
}
void MockInputAckHandler::OnTouchEventAck(
@@ -46,7 +48,7 @@ void MockInputAckHandler::OnTouchEventAck(
InputEventAckState ack_result) {
VLOG(1) << __FUNCTION__ << " called!";
acked_touch_event_ = event;
- RecordAckCalled(ack_result);
+ RecordAckCalled(event.event.type, ack_result);
if (touch_followup_event_)
input_router_->SendTouchEvent(*touch_followup_event_);
if (gesture_followup_event_)
@@ -58,7 +60,7 @@ void MockInputAckHandler::OnGestureEventAck(
InputEventAckState ack_result) {
VLOG(1) << __FUNCTION__ << " called!";
acked_gesture_event_ = event.event;
- RecordAckCalled(ack_result);
+ RecordAckCalled(event.event.type, ack_result);
}
void MockInputAckHandler::OnUnexpectedEventAck(UnexpectedEventAckType type) {
@@ -72,7 +74,9 @@ size_t MockInputAckHandler::GetAndResetAckCount() {
return ack_count;
}
-void MockInputAckHandler::RecordAckCalled(InputEventAckState ack_result) {
+void MockInputAckHandler::RecordAckCalled(blink::WebInputEvent::Type type,
+ InputEventAckState ack_result) {
+ ack_event_type_ = type;
++ack_count_;
ack_state_ = ack_result;
}
diff --git a/chromium/content/browser/renderer_host/input/mock_input_ack_handler.h b/chromium/content/browser/renderer_host/input/mock_input_ack_handler.h
index 88ca9e186fc..875964a8080 100644
--- a/chromium/content/browser/renderer_host/input/mock_input_ack_handler.h
+++ b/chromium/content/browser/renderer_host/input/mock_input_ack_handler.h
@@ -47,6 +47,8 @@ class MockInputAckHandler : public InputAckHandler {
}
InputEventAckState ack_state() const { return ack_state_; }
+ blink::WebInputEvent::Type ack_event_type() const { return ack_event_type_; }
+
const NativeWebKeyboardEvent& acked_keyboard_event() const {
return acked_key_event_;
}
@@ -61,12 +63,14 @@ class MockInputAckHandler : public InputAckHandler {
}
private:
- void RecordAckCalled(InputEventAckState ack_result);
+ void RecordAckCalled(blink::WebInputEvent::Type eventType,
+ InputEventAckState ack_result);
InputRouter* input_router_;
size_t ack_count_;
bool unexpected_event_ack_called_;
+ blink::WebInputEvent::Type ack_event_type_;
InputEventAckState ack_state_;
NativeWebKeyboardEvent acked_key_event_;
blink::WebMouseWheelEvent acked_wheel_event_;
diff --git a/chromium/content/browser/renderer_host/input/mock_input_router_client.cc b/chromium/content/browser/renderer_host/input/mock_input_router_client.cc
index 436f0b06372..068af8c9206 100644
--- a/chromium/content/browser/renderer_host/input/mock_input_router_client.cc
+++ b/chromium/content/browser/renderer_host/input/mock_input_router_client.cc
@@ -24,7 +24,7 @@ MockInputRouterClient::MockInputRouterClient()
has_touch_handler_(false),
filter_state_(INPUT_EVENT_ACK_STATE_NOT_CONSUMED),
filter_input_event_called_(false),
- did_flush_called_(false),
+ did_flush_called_count_(0),
set_needs_flush_called_(false) {}
MockInputRouterClient::~MockInputRouterClient() {}
@@ -50,22 +50,34 @@ void MockInputRouterClient::OnHasTouchEventHandlers(
has_touch_handler_ = has_handlers;
}
+void MockInputRouterClient::SetNeedsFlush() {
+ set_needs_flush_called_ = true;
+}
+
+void MockInputRouterClient::DidFlush() {
+ ++did_flush_called_count_;
+}
+
+void MockInputRouterClient::DidOverscroll(const DidOverscrollParams& params) {
+ overscroll_ = params;
+}
+
bool MockInputRouterClient::GetAndResetFilterEventCalled() {
bool filter_input_event_called = filter_input_event_called_;
filter_input_event_called_ = false;
return filter_input_event_called;
}
-OverscrollController* MockInputRouterClient::GetOverscrollController() const {
- return NULL;
+size_t MockInputRouterClient::GetAndResetDidFlushCount() {
+ size_t did_flush_called_count = did_flush_called_count_;
+ did_flush_called_count_ = 0;
+ return did_flush_called_count;
}
-void MockInputRouterClient::DidFlush() {
- did_flush_called_ = true;
-}
-
-void MockInputRouterClient::SetNeedsFlush() {
- set_needs_flush_called_ = true;
+DidOverscrollParams MockInputRouterClient::GetAndResetOverscroll() {
+ DidOverscrollParams overscroll;
+ std::swap(overscroll_, overscroll);
+ return overscroll;
}
} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/mock_input_router_client.h b/chromium/content/browser/renderer_host/input/mock_input_router_client.h
index d2ea81d45cc..f0b31676ffd 100644
--- a/chromium/content/browser/renderer_host/input/mock_input_router_client.h
+++ b/chromium/content/browser/renderer_host/input/mock_input_router_client.h
@@ -7,6 +7,7 @@
#include "base/memory/scoped_ptr.h"
#include "content/browser/renderer_host/input/input_router_client.h"
+#include "content/common/input/did_overscroll_params.h"
namespace content {
@@ -25,11 +26,13 @@ class MockInputRouterClient : public InputRouterClient {
virtual void IncrementInFlightEventCount() OVERRIDE;
virtual void DecrementInFlightEventCount() OVERRIDE;
virtual void OnHasTouchEventHandlers(bool has_handlers) OVERRIDE;
- virtual OverscrollController* GetOverscrollController() const OVERRIDE;
- virtual void DidFlush() OVERRIDE;
virtual void SetNeedsFlush() OVERRIDE;
+ virtual void DidFlush() OVERRIDE;
+ virtual void DidOverscroll(const DidOverscrollParams& params) OVERRIDE;
bool GetAndResetFilterEventCalled();
+ size_t GetAndResetDidFlushCount();
+ DidOverscrollParams GetAndResetOverscroll();
void set_input_router(InputRouter* input_router) {
input_router_ = input_router;
@@ -46,9 +49,6 @@ class MockInputRouterClient : public InputRouterClient {
filter_state_ = INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS;
}
- bool did_flush_called() const { return did_flush_called_; }
- bool needs_flush_called() const { return set_needs_flush_called_; }
-
private:
InputRouter* input_router_;
int in_flight_event_count_;
@@ -59,8 +59,10 @@ class MockInputRouterClient : public InputRouterClient {
bool filter_input_event_called_;
scoped_ptr<InputEvent> last_filter_event_;
- bool did_flush_called_;
+ size_t did_flush_called_count_;
bool set_needs_flush_called_;
+
+ DidOverscrollParams overscroll_;
};
} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/motion_event_android.cc b/chromium/content/browser/renderer_host/input/motion_event_android.cc
new file mode 100644
index 00000000000..7409955bc9c
--- /dev/null
+++ b/chromium/content/browser/renderer_host/input/motion_event_android.cc
@@ -0,0 +1,330 @@
+// 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/renderer_host/input/motion_event_android.h"
+
+#include "base/android/jni_android.h"
+#include "jni/MotionEvent_jni.h"
+
+using base::android::AttachCurrentThread;
+using namespace JNI_MotionEvent;
+
+namespace content {
+namespace {
+
+int ToAndroidAction(MotionEventAndroid::Action action) {
+ switch (action) {
+ case MotionEventAndroid::ACTION_DOWN:
+ return ACTION_DOWN;
+ case MotionEventAndroid::ACTION_UP:
+ return ACTION_UP;
+ case MotionEventAndroid::ACTION_MOVE:
+ return ACTION_MOVE;
+ case MotionEventAndroid::ACTION_CANCEL:
+ return ACTION_CANCEL;
+ case MotionEventAndroid::ACTION_POINTER_DOWN:
+ return ACTION_POINTER_DOWN;
+ case MotionEventAndroid::ACTION_POINTER_UP:
+ return ACTION_POINTER_UP;
+ };
+ NOTREACHED() << "Invalid Android MotionEvent type for gesture detection: "
+ << action;
+ return ACTION_CANCEL;
+}
+
+MotionEventAndroid::Action FromAndroidAction(int android_action) {
+ switch (android_action) {
+ case ACTION_DOWN:
+ return MotionEventAndroid::ACTION_DOWN;
+ case ACTION_UP:
+ return MotionEventAndroid::ACTION_UP;
+ case ACTION_MOVE:
+ return MotionEventAndroid::ACTION_MOVE;
+ case ACTION_CANCEL:
+ return MotionEventAndroid::ACTION_CANCEL;
+ case ACTION_POINTER_DOWN:
+ return MotionEventAndroid::ACTION_POINTER_DOWN;
+ case ACTION_POINTER_UP:
+ return MotionEventAndroid::ACTION_POINTER_UP;
+ default:
+ NOTREACHED() << "Invalid Android MotionEvent type for gesture detection: "
+ << android_action;
+ };
+ return MotionEventAndroid::ACTION_CANCEL;
+}
+
+int64 ToAndroidTime(base::TimeTicks time) {
+ return (time - base::TimeTicks()).InMilliseconds();
+}
+
+base::TimeTicks FromAndroidTime(int64 time_ms) {
+ return base::TimeTicks() + base::TimeDelta::FromMilliseconds(time_ms);
+}
+
+} // namespace
+
+MotionEventAndroid::MotionEventAndroid(float pix_to_dip,
+ JNIEnv* env,
+ jobject event,
+ jlong time_ms,
+ jint android_action,
+ jint pointer_count,
+ jint history_size,
+ jint action_index,
+ jfloat pos_x_0_pixels,
+ jfloat pos_y_0_pixels,
+ jfloat pos_x_1_pixels,
+ jfloat pos_y_1_pixels,
+ jint pointer_id_0,
+ jint pointer_id_1,
+ jfloat touch_major_0_pixels,
+ jfloat touch_major_1_pixels,
+ jfloat raw_pos_x_pixels,
+ jfloat raw_pos_y_pixels)
+ : cached_time_(FromAndroidTime(time_ms)),
+ cached_action_(FromAndroidAction(android_action)),
+ cached_pointer_count_(pointer_count),
+ cached_history_size_(history_size),
+ cached_action_index_(action_index),
+ pix_to_dip_(pix_to_dip),
+ should_recycle_(false) {
+ DCHECK_GT(pointer_count, 0);
+ DCHECK_GE(history_size, 0);
+
+ event_.Reset(env, event);
+ DCHECK(event_.obj());
+
+ cached_positions_[0] = ToDips(gfx::PointF(pos_x_0_pixels, pos_y_0_pixels));
+ cached_positions_[1] = ToDips(gfx::PointF(pos_x_1_pixels, pos_y_1_pixels));
+ cached_pointer_ids_[0] = pointer_id_0;
+ cached_pointer_ids_[1] = pointer_id_1;
+ cached_touch_majors_[0] = ToDips(touch_major_0_pixels);
+ cached_touch_majors_[1] = ToDips(touch_major_1_pixels);
+ cached_raw_position_offset_ =
+ ToDips(gfx::PointF(raw_pos_x_pixels, raw_pos_y_pixels)) -
+ cached_positions_[0];
+}
+
+MotionEventAndroid::MotionEventAndroid(float pix_to_dip,
+ JNIEnv* env,
+ jobject event)
+ : cached_time_(FromAndroidTime(Java_MotionEvent_getEventTime(env, event))),
+ cached_action_(
+ FromAndroidAction(Java_MotionEvent_getActionMasked(env, event))),
+ cached_pointer_count_(Java_MotionEvent_getPointerCount(env, event)),
+ cached_history_size_(Java_MotionEvent_getHistorySize(env, event)),
+ cached_action_index_(Java_MotionEvent_getActionIndex(env, event)),
+ pix_to_dip_(pix_to_dip),
+ should_recycle_(true) {
+ event_.Reset(env, event);
+ DCHECK(event_.obj());
+
+ for (size_t i = 0; i < MAX_POINTERS_TO_CACHE; ++i) {
+ if (i < cached_pointer_count_) {
+ cached_positions_[i] =
+ ToDips(gfx::PointF(Java_MotionEvent_getXF_I(env, event, i),
+ Java_MotionEvent_getYF_I(env, event, i)));
+ cached_pointer_ids_[i] = Java_MotionEvent_getPointerId(env, event, i);
+ cached_touch_majors_[i] =
+ ToDips(Java_MotionEvent_getTouchMajorF_I(env, event, i));
+ } else {
+ cached_pointer_ids_[i] = 0;
+ cached_touch_majors_[i] = 0.f;
+ }
+ }
+
+ cached_raw_position_offset_ =
+ ToDips(gfx::PointF(Java_MotionEvent_getRawX(env, event),
+ Java_MotionEvent_getRawY(env, event))) -
+ cached_positions_[0];
+}
+
+MotionEventAndroid::MotionEventAndroid(const MotionEventAndroid& other)
+ : event_(Obtain(other)),
+ cached_time_(other.cached_time_),
+ cached_action_(other.cached_action_),
+ cached_pointer_count_(other.cached_pointer_count_),
+ cached_history_size_(other.cached_history_size_),
+ cached_action_index_(other.cached_action_index_),
+ cached_raw_position_offset_(other.cached_raw_position_offset_),
+ pix_to_dip_(other.pix_to_dip_),
+ should_recycle_(true) {
+ DCHECK(event_.obj());
+ for (size_t i = 0; i < MAX_POINTERS_TO_CACHE; ++i) {
+ cached_positions_[i] = other.cached_positions_[i];
+ cached_pointer_ids_[i] = other.cached_pointer_ids_[i];
+ cached_touch_majors_[i] = other.cached_touch_majors_[i];
+ }
+}
+
+MotionEventAndroid::~MotionEventAndroid() {
+ if (should_recycle_)
+ Java_MotionEvent_recycle(AttachCurrentThread(), event_.obj());
+}
+
+int MotionEventAndroid::GetId() const {
+ return 0;
+}
+
+MotionEventAndroid::Action MotionEventAndroid::GetAction() const {
+ return cached_action_;
+}
+
+int MotionEventAndroid::GetActionIndex() const {
+ return cached_action_index_;
+}
+
+size_t MotionEventAndroid::GetPointerCount() const {
+ return cached_pointer_count_;
+}
+
+int MotionEventAndroid::GetPointerId(size_t pointer_index) const {
+ DCHECK_LT(pointer_index, cached_pointer_count_);
+ if (pointer_index < MAX_POINTERS_TO_CACHE)
+ return cached_pointer_ids_[pointer_index];
+ return Java_MotionEvent_getPointerId(
+ AttachCurrentThread(), event_.obj(), pointer_index);
+}
+
+float MotionEventAndroid::GetX(size_t pointer_index) const {
+ DCHECK_LT(pointer_index, cached_pointer_count_);
+ if (pointer_index < MAX_POINTERS_TO_CACHE)
+ return cached_positions_[pointer_index].x();
+ return ToDips(Java_MotionEvent_getXF_I(
+ AttachCurrentThread(), event_.obj(), pointer_index));
+}
+
+float MotionEventAndroid::GetY(size_t pointer_index) const {
+ DCHECK_LT(pointer_index, cached_pointer_count_);
+ if (pointer_index < MAX_POINTERS_TO_CACHE)
+ return cached_positions_[pointer_index].y();
+ return ToDips(Java_MotionEvent_getYF_I(
+ AttachCurrentThread(), event_.obj(), pointer_index));
+}
+
+float MotionEventAndroid::GetRawX(size_t pointer_index) const {
+ return GetX(pointer_index) + cached_raw_position_offset_.x();
+}
+
+float MotionEventAndroid::GetRawY(size_t pointer_index) const {
+ return GetY(pointer_index) + cached_raw_position_offset_.y();
+}
+
+float MotionEventAndroid::GetTouchMajor(size_t pointer_index) const {
+ DCHECK_LT(pointer_index, cached_pointer_count_);
+ if (pointer_index < MAX_POINTERS_TO_CACHE)
+ return cached_touch_majors_[pointer_index];
+ return ToDips(Java_MotionEvent_getTouchMajorF_I(
+ AttachCurrentThread(), event_.obj(), pointer_index));
+}
+
+float MotionEventAndroid::GetPressure(size_t pointer_index) const {
+ DCHECK_LT(pointer_index, cached_pointer_count_);
+ return Java_MotionEvent_getPressureF_I(
+ AttachCurrentThread(), event_.obj(), pointer_index);
+}
+
+base::TimeTicks MotionEventAndroid::GetEventTime() const {
+ return cached_time_;
+}
+
+size_t MotionEventAndroid::GetHistorySize() const {
+ return cached_history_size_;
+}
+
+base::TimeTicks MotionEventAndroid::GetHistoricalEventTime(
+ size_t historical_index) const {
+ return FromAndroidTime(Java_MotionEvent_getHistoricalEventTime(
+ AttachCurrentThread(), event_.obj(), historical_index));
+}
+
+float MotionEventAndroid::GetHistoricalTouchMajor(
+ size_t pointer_index,
+ size_t historical_index) const {
+ return ToDips(Java_MotionEvent_getHistoricalTouchMajorF_I_I(
+ AttachCurrentThread(), event_.obj(), pointer_index, historical_index));
+}
+
+float MotionEventAndroid::GetHistoricalX(size_t pointer_index,
+ size_t historical_index) const {
+ return ToDips(Java_MotionEvent_getHistoricalXF_I_I(
+ AttachCurrentThread(), event_.obj(), pointer_index, historical_index));
+}
+
+float MotionEventAndroid::GetHistoricalY(size_t pointer_index,
+ size_t historical_index) const {
+ return ToDips(Java_MotionEvent_getHistoricalYF_I_I(
+ AttachCurrentThread(), event_.obj(), pointer_index, historical_index));
+}
+
+scoped_ptr<ui::MotionEvent> MotionEventAndroid::Clone() const {
+ return scoped_ptr<MotionEvent>(new MotionEventAndroid(*this));
+}
+
+scoped_ptr<ui::MotionEvent> MotionEventAndroid::Cancel() const {
+ // The input coordinates to |MotionEventAndroid| are always in device pixels,
+ // but the cached coordinates are in DIPs.
+ const gfx::PointF position_pixels =
+ gfx::ScalePoint(cached_positions_[0], 1.f / pix_to_dip_);
+ return scoped_ptr<MotionEvent>(
+ new MotionEventAndroid(pix_to_dip_,
+ AttachCurrentThread(),
+ Obtain(GetDownTime(),
+ GetEventTime(),
+ MotionEventAndroid::ACTION_CANCEL,
+ position_pixels.x(),
+ position_pixels.y()).obj()));
+}
+
+float MotionEventAndroid::GetTouchMinor(size_t pointer_index) const {
+ return ToDips(Java_MotionEvent_getTouchMinorF_I(
+ AttachCurrentThread(), event_.obj(), pointer_index));
+}
+
+float MotionEventAndroid::GetOrientation() const {
+ return Java_MotionEvent_getOrientationF(AttachCurrentThread(), event_.obj());
+}
+
+base::TimeTicks MotionEventAndroid::GetDownTime() const {
+ return FromAndroidTime(
+ Java_MotionEvent_getDownTime(AttachCurrentThread(), event_.obj()));
+}
+
+float MotionEventAndroid::ToDips(float pixels) const {
+ return pixels * pix_to_dip_;
+}
+
+gfx::PointF MotionEventAndroid::ToDips(const gfx::PointF& point_pixels) const {
+ return gfx::ScalePoint(point_pixels, pix_to_dip_);
+}
+
+// static
+bool MotionEventAndroid::RegisterMotionEventAndroid(JNIEnv* env) {
+ return JNI_MotionEvent::RegisterNativesImpl(env);
+}
+
+// static
+base::android::ScopedJavaLocalRef<jobject> MotionEventAndroid::Obtain(
+ const MotionEventAndroid& event) {
+ return Java_MotionEvent_obtainAVME_AVME(AttachCurrentThread(),
+ event.event_.obj());
+}
+
+// static
+base::android::ScopedJavaLocalRef<jobject> MotionEventAndroid::Obtain(
+ base::TimeTicks down_time,
+ base::TimeTicks event_time,
+ Action action,
+ float x_pixels,
+ float y_pixels) {
+ return Java_MotionEvent_obtainAVME_J_J_I_F_F_I(AttachCurrentThread(),
+ ToAndroidTime(down_time),
+ ToAndroidTime(event_time),
+ ToAndroidAction(action),
+ x_pixels,
+ y_pixels,
+ 0);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/motion_event_android.h b/chromium/content/browser/renderer_host/input/motion_event_android.h
new file mode 100644
index 00000000000..0362b0454e9
--- /dev/null
+++ b/chromium/content/browser/renderer_host/input/motion_event_android.h
@@ -0,0 +1,125 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_INPUT_MOTION_EVENT_ANDROID_H_
+#define CONTENT_BROWSER_RENDERER_HOST_INPUT_MOTION_EVENT_ANDROID_H_
+
+#include <jni.h>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/time/time.h"
+#include "ui/events/gesture_detection/motion_event.h"
+#include "ui/gfx/geometry/point_f.h"
+
+namespace content {
+
+// Implementation of ui::MotionEvent wrapping a native Android MotionEvent.
+// All *input* coordinates are in device pixels (as with Android MotionEvent),
+// while all *output* coordinates are in DIPs (as with WebTouchEvent).
+class MotionEventAndroid : public ui::MotionEvent {
+ public:
+ // Forcing the caller to provide all cached values upon construction
+ // eliminates the need to perform a JNI call to retrieve values individually.
+ MotionEventAndroid(float pix_to_dip,
+ JNIEnv* env,
+ jobject event,
+ jlong time_ms,
+ jint android_action,
+ jint pointer_count,
+ jint history_size,
+ jint action_index,
+ jfloat pos_x_0_pixels,
+ jfloat pos_y_0_pixels,
+ jfloat pos_x_1_pixels,
+ jfloat pos_y_1_pixels,
+ jint pointer_id_0,
+ jint pointer_id_1,
+ jfloat touch_major_0_pixels,
+ jfloat touch_major_1_pixels,
+ jfloat raw_pos_x_pixels,
+ jfloat raw_pos_y_pixels);
+ virtual ~MotionEventAndroid();
+
+ // ui::MotionEvent methods.
+ virtual int GetId() const OVERRIDE;
+ virtual Action GetAction() const OVERRIDE;
+ virtual int GetActionIndex() const OVERRIDE;
+ virtual size_t GetPointerCount() const OVERRIDE;
+ virtual int GetPointerId(size_t pointer_index) const OVERRIDE;
+ virtual float GetX(size_t pointer_index) const OVERRIDE;
+ virtual float GetY(size_t pointer_index) const OVERRIDE;
+ virtual float GetRawX(size_t pointer_index) const OVERRIDE;
+ virtual float GetRawY(size_t pointer_index) const OVERRIDE;
+ virtual float GetTouchMajor(size_t pointer_index) const OVERRIDE;
+ virtual float GetPressure(size_t pointer_index) const OVERRIDE;
+ virtual base::TimeTicks GetEventTime() const OVERRIDE;
+ virtual size_t GetHistorySize() const OVERRIDE;
+ virtual base::TimeTicks GetHistoricalEventTime(
+ size_t historical_index) const OVERRIDE;
+ virtual float GetHistoricalTouchMajor(size_t pointer_index,
+ size_t historical_index) const OVERRIDE;
+ virtual float GetHistoricalX(size_t pointer_index,
+ size_t historical_index) const OVERRIDE;
+ virtual float GetHistoricalY(size_t pointer_index,
+ size_t historical_index) const OVERRIDE;
+ virtual scoped_ptr<MotionEvent> Clone() const OVERRIDE;
+ virtual scoped_ptr<MotionEvent> Cancel() const OVERRIDE;
+
+ // Additional Android MotionEvent methods.
+ float GetTouchMinor() const { return GetTouchMinor(0); }
+ float GetTouchMinor(size_t pointer_index) const;
+ float GetOrientation() const;
+ base::TimeTicks GetDownTime() const;
+
+ static bool RegisterMotionEventAndroid(JNIEnv* env);
+
+ static base::android::ScopedJavaLocalRef<jobject> Obtain(
+ const MotionEventAndroid& event);
+ static base::android::ScopedJavaLocalRef<jobject> Obtain(
+ base::TimeTicks down_time,
+ base::TimeTicks event_time,
+ Action action,
+ float x_pixels,
+ float y_pixels);
+
+ private:
+ MotionEventAndroid();
+ MotionEventAndroid(float pix_to_dip, JNIEnv* env, jobject event);
+ MotionEventAndroid(const MotionEventAndroid&);
+ MotionEventAndroid& operator=(const MotionEventAndroid&);
+
+ float ToDips(float pixels) const;
+ gfx::PointF ToDips(const gfx::PointF& pixels) const;
+
+ // Cache pointer coords, id's and major lengths for the most common
+ // touch-related scenarios, i.e., scrolling and pinching. This prevents
+ // redundant JNI fetches for the same bits.
+ enum { MAX_POINTERS_TO_CACHE = 2 };
+
+ // The Java reference to the underlying MotionEvent.
+ base::android::ScopedJavaGlobalRef<jobject> event_;
+
+ base::TimeTicks cached_time_;
+ Action cached_action_;
+ size_t cached_pointer_count_;
+ size_t cached_history_size_;
+ int cached_action_index_;
+ gfx::PointF cached_positions_[MAX_POINTERS_TO_CACHE];
+ int cached_pointer_ids_[MAX_POINTERS_TO_CACHE];
+ float cached_touch_majors_[MAX_POINTERS_TO_CACHE];
+ gfx::Vector2dF cached_raw_position_offset_;
+
+ // Used to convert pixel coordinates from the Java-backed MotionEvent to
+ // DIP coordinates cached/returned by the MotionEventAndroid.
+ const float pix_to_dip_;
+
+ // Whether |event_| should be recycled on destruction. This will only be true
+ // for those events generated via |Obtain(...)|.
+ bool should_recycle_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_MOTION_EVENT_ANDROID_H_
diff --git a/chromium/content/browser/renderer_host/input/motion_event_web.cc b/chromium/content/browser/renderer_host/input/motion_event_web.cc
new file mode 100644
index 00000000000..d1ff8a3c84f
--- /dev/null
+++ b/chromium/content/browser/renderer_host/input/motion_event_web.cc
@@ -0,0 +1,160 @@
+// 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/renderer_host/input/motion_event_web.h"
+
+#include "base/logging.h"
+#include "content/common/input/web_touch_event_traits.h"
+
+using blink::WebInputEvent;
+using blink::WebTouchEvent;
+using blink::WebTouchPoint;
+
+namespace content {
+namespace {
+
+ui::MotionEvent::Action GetActionFrom(const WebTouchEvent& event) {
+ DCHECK(event.touchesLength);
+ switch (event.type) {
+ case WebInputEvent::TouchStart:
+ if (WebTouchEventTraits::AllTouchPointsHaveState(
+ event, WebTouchPoint::StatePressed))
+ return ui::MotionEvent::ACTION_DOWN;
+ else
+ return ui::MotionEvent::ACTION_POINTER_DOWN;
+ case WebInputEvent::TouchEnd:
+ if (WebTouchEventTraits::AllTouchPointsHaveState(
+ event, WebTouchPoint::StateReleased))
+ return ui::MotionEvent::ACTION_UP;
+ else
+ return ui::MotionEvent::ACTION_POINTER_UP;
+ case WebInputEvent::TouchCancel:
+ DCHECK(WebTouchEventTraits::AllTouchPointsHaveState(
+ event, WebTouchPoint::StateCancelled));
+ return ui::MotionEvent::ACTION_CANCEL;
+ case WebInputEvent::TouchMove:
+ return ui::MotionEvent::ACTION_MOVE;
+ default:
+ break;
+ };
+ NOTREACHED()
+ << "Unable to derive a valid MotionEvent::Action from the WebTouchEvent.";
+ return ui::MotionEvent::ACTION_CANCEL;
+}
+
+int GetActionIndexFrom(const WebTouchEvent& event) {
+ for (size_t i = 0; i < event.touchesLength; ++i) {
+ if (event.touches[i].state != WebTouchPoint::StateUndefined &&
+ event.touches[i].state != WebTouchPoint::StateStationary)
+ return i;
+ }
+ return -1;
+}
+
+} // namespace
+
+MotionEventWeb::MotionEventWeb(const WebTouchEvent& event)
+ : event_(event),
+ cached_action_(GetActionFrom(event)),
+ cached_action_index_(GetActionIndexFrom(event)) {
+ DCHECK_GT(GetPointerCount(), 0U);
+}
+
+MotionEventWeb::~MotionEventWeb() {}
+
+int MotionEventWeb::GetId() const {
+ return 0;
+}
+
+MotionEventWeb::Action MotionEventWeb::GetAction() const {
+ return cached_action_;
+}
+
+int MotionEventWeb::GetActionIndex() const { return cached_action_index_; }
+
+size_t MotionEventWeb::GetPointerCount() const { return event_.touchesLength; }
+
+int MotionEventWeb::GetPointerId(size_t pointer_index) const {
+ DCHECK_LT(pointer_index, GetPointerCount());
+ return event_.touches[pointer_index].id;
+}
+
+float MotionEventWeb::GetX(size_t pointer_index) const {
+ DCHECK_LT(pointer_index, GetPointerCount());
+ return event_.touches[pointer_index].position.x;
+}
+
+float MotionEventWeb::GetY(size_t pointer_index) const {
+ DCHECK_LT(pointer_index, GetPointerCount());
+ return event_.touches[pointer_index].position.y;
+}
+
+float MotionEventWeb::GetRawX(size_t pointer_index) const {
+ DCHECK_LT(pointer_index, GetPointerCount());
+ return event_.touches[pointer_index].screenPosition.x;
+}
+
+float MotionEventWeb::GetRawY(size_t pointer_index) const {
+ DCHECK_LT(pointer_index, GetPointerCount());
+ return event_.touches[pointer_index].screenPosition.y;
+}
+
+float MotionEventWeb::GetTouchMajor(size_t pointer_index) const {
+ DCHECK_LT(pointer_index, GetPointerCount());
+ // TODO(jdduke): We should be a bit more careful about axes here.
+ return 2.f * std::max(event_.touches[pointer_index].radiusX,
+ event_.touches[pointer_index].radiusY);
+}
+
+float MotionEventWeb::GetPressure(size_t pointer_index) const {
+ return 0.f;
+}
+
+base::TimeTicks MotionEventWeb::GetEventTime() const {
+ return base::TimeTicks() +
+ base::TimeDelta::FromMicroseconds(event_.timeStampSeconds *
+ base::Time::kMicrosecondsPerSecond);
+}
+
+size_t MotionEventWeb::GetHistorySize() const { return 0; }
+
+base::TimeTicks MotionEventWeb::GetHistoricalEventTime(
+ size_t historical_index) const {
+ NOTIMPLEMENTED();
+ return base::TimeTicks();
+}
+
+float MotionEventWeb::GetHistoricalTouchMajor(size_t pointer_index,
+ size_t historical_index) const {
+ NOTIMPLEMENTED();
+ return 0.f;
+}
+
+float MotionEventWeb::GetHistoricalX(size_t pointer_index,
+ size_t historical_index) const {
+ NOTIMPLEMENTED();
+ return 0.f;
+}
+
+float MotionEventWeb::GetHistoricalY(size_t pointer_index,
+ size_t historical_index) const {
+ NOTIMPLEMENTED();
+ return 0.f;
+}
+
+scoped_ptr<ui::MotionEvent> MotionEventWeb::Clone() const {
+ return scoped_ptr<MotionEvent>(new MotionEventWeb(event_));
+}
+
+scoped_ptr<ui::MotionEvent> MotionEventWeb::Cancel() const {
+ WebTouchEvent cancel_event(event_);
+ WebTouchEventTraits::ResetTypeAndTouchStates(
+ blink::WebInputEvent::TouchCancel,
+ // TODO(rbyers): Shouldn't we use a fresh timestamp?
+ event_.timeStampSeconds,
+ &cancel_event);
+ return scoped_ptr<MotionEvent>(new MotionEventWeb(cancel_event));
+}
+
+} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/motion_event_web.h b/chromium/content/browser/renderer_host/input/motion_event_web.h
new file mode 100644
index 00000000000..f4ae25890fe
--- /dev/null
+++ b/chromium/content/browser/renderer_host/input/motion_event_web.h
@@ -0,0 +1,57 @@
+// 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_RENDERER_HOST_INPUT_MOTION_EVENT_WEB_H_
+#define CONTENT_BROWSER_RENDERER_HOST_INPUT_MOTION_EVENT_WEB_H_
+
+#include "third_party/WebKit/public/web/WebInputEvent.h"
+#include "ui/events/gesture_detection/motion_event.h"
+
+namespace content {
+
+// Implementation of ui::MotionEvent wrapping a WebTouchEvent.
+class MotionEventWeb : public ui::MotionEvent {
+ public:
+ explicit MotionEventWeb(const blink::WebTouchEvent& event);
+ virtual ~MotionEventWeb();
+
+ // ui::MotionEvent
+ virtual int GetId() const OVERRIDE;
+ virtual Action GetAction() const OVERRIDE;
+ virtual int GetActionIndex() const OVERRIDE;
+ virtual size_t GetPointerCount() const OVERRIDE;
+ virtual int GetPointerId(size_t pointer_index) const OVERRIDE;
+ virtual float GetX(size_t pointer_index) const OVERRIDE;
+ virtual float GetY(size_t pointer_index) const OVERRIDE;
+ virtual float GetRawX(size_t pointer_index) const OVERRIDE;
+ virtual float GetRawY(size_t pointer_index) const OVERRIDE;
+ virtual float GetTouchMajor(size_t pointer_index) const OVERRIDE;
+ virtual float GetPressure(size_t pointer_index) const OVERRIDE;
+ virtual base::TimeTicks GetEventTime() const OVERRIDE;
+ virtual size_t GetHistorySize() const OVERRIDE;
+ virtual base::TimeTicks GetHistoricalEventTime(
+ size_t historical_index) const OVERRIDE;
+ virtual float GetHistoricalTouchMajor(
+ size_t pointer_index,
+ size_t historical_index) const OVERRIDE;
+ virtual float GetHistoricalX(
+ size_t pointer_index,
+ size_t historical_index) const OVERRIDE;
+ virtual float GetHistoricalY(
+ size_t pointer_index,
+ size_t historical_index) const OVERRIDE;
+ virtual scoped_ptr<MotionEvent> Clone() const OVERRIDE;
+ virtual scoped_ptr<MotionEvent> Cancel() const OVERRIDE;
+
+ private:
+ blink::WebTouchEvent event_;
+ Action cached_action_;
+ int cached_action_index_;
+
+ DISALLOW_COPY_AND_ASSIGN(MotionEventWeb);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_MOTION_EVENT_WEB_H_
diff --git a/chromium/content/browser/renderer_host/input/synthetic_gesture.cc b/chromium/content/browser/renderer_host/input/synthetic_gesture.cc
index 8b2b3bedbe6..bc887e1dfe2 100644
--- a/chromium/content/browser/renderer_host/input/synthetic_gesture.cc
+++ b/chromium/content/browser/renderer_host/input/synthetic_gesture.cc
@@ -43,4 +43,10 @@ scoped_ptr<SyntheticGesture> SyntheticGesture::Create(
return scoped_ptr<SyntheticGesture>();
}
+// static
+double SyntheticGesture::ConvertTimestampToSeconds(
+ const base::TimeTicks& timestamp) {
+ return (timestamp - base::TimeTicks()).InSecondsF();
+}
+
} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/synthetic_gesture.h b/chromium/content/browser/renderer_host/input/synthetic_gesture.h
index c10c2fea214..1d605ff9383 100644
--- a/chromium/content/browser/renderer_host/input/synthetic_gesture.h
+++ b/chromium/content/browser/renderer_host/input/synthetic_gesture.h
@@ -9,7 +9,6 @@
#include "base/time/time.h"
#include "content/common/content_export.h"
#include "content/common/input/synthetic_gesture_params.h"
-#include "third_party/WebKit/public/web/WebInputEvent.h"
namespace content {
@@ -38,17 +37,18 @@ class CONTENT_EXPORT SyntheticGesture {
GESTURE_RUNNING,
GESTURE_FINISHED,
GESTURE_SOURCE_TYPE_NOT_IMPLEMENTED,
- GESTURE_SOURCE_TYPE_NOT_SUPPORTED_BY_PLATFORM,
- GESTURE_RESULT_MAX = GESTURE_SOURCE_TYPE_NOT_SUPPORTED_BY_PLATFORM
+ GESTURE_RESULT_MAX = GESTURE_SOURCE_TYPE_NOT_IMPLEMENTED
};
// Update the state of the gesture and forward the appropriate events to the
// platform. This function is called repeatedly by the synthetic gesture
// controller until it stops returning GESTURE_RUNNING.
virtual Result ForwardInputEvents(
- const base::TimeDelta& interval, SyntheticGestureTarget* target) = 0;
+ const base::TimeTicks& timestamp, SyntheticGestureTarget* target) = 0;
+
+ protected:
+ static double ConvertTimestampToSeconds(const base::TimeTicks& timestamp);
- private:
DISALLOW_COPY_AND_ASSIGN(SyntheticGesture);
};
diff --git a/chromium/content/browser/renderer_host/input/synthetic_gesture_controller.cc b/chromium/content/browser/renderer_host/input/synthetic_gesture_controller.cc
index 0529354ac38..8314d948ba7 100644
--- a/chromium/content/browser/renderer_host/input/synthetic_gesture_controller.cc
+++ b/chromium/content/browser/renderer_host/input/synthetic_gesture_controller.cc
@@ -19,62 +19,81 @@ SyntheticGestureController::SyntheticGestureController(
SyntheticGestureController::~SyntheticGestureController() {}
void SyntheticGestureController::QueueSyntheticGesture(
- scoped_ptr<SyntheticGesture> synthetic_gesture) {
+ scoped_ptr<SyntheticGesture> synthetic_gesture,
+ const OnGestureCompleteCallback& completion_callback) {
DCHECK(synthetic_gesture);
- pending_gesture_queue_.push_back(synthetic_gesture.release());
+ bool was_empty = pending_gesture_queue_.IsEmpty();
- // Start forwarding input events if the queue was previously empty.
- if (pending_gesture_queue_.size() == 1)
- StartGesture(*pending_gesture_queue_.front());
+ pending_gesture_queue_.Push(synthetic_gesture.Pass(), completion_callback);
+
+ if (was_empty)
+ StartGesture(*pending_gesture_queue_.FrontGesture());
}
void SyntheticGestureController::Flush(base::TimeTicks timestamp) {
- if (pending_gesture_queue_.empty())
+ TRACE_EVENT0("input", "SyntheticGestureController::Flush");
+ if (pending_gesture_queue_.IsEmpty())
return;
- if (last_tick_time_.is_null()) {
- last_tick_time_ = timestamp;
- gesture_target_->SetNeedsFlush();
+ if (pending_gesture_result_)
return;
- }
- base::TimeDelta interval = timestamp - last_tick_time_;
- last_tick_time_ = timestamp;
+ SyntheticGesture* gesture = pending_gesture_queue_.FrontGesture();
SyntheticGesture::Result result =
- pending_gesture_queue_.front()->ForwardInputEvents(interval,
- gesture_target_.get());
+ gesture->ForwardInputEvents(timestamp, gesture_target_.get());
if (result == SyntheticGesture::GESTURE_RUNNING) {
gesture_target_->SetNeedsFlush();
return;
}
- StopGesture(*pending_gesture_queue_.front(), result);
- pending_gesture_queue_.erase(pending_gesture_queue_.begin());
+ // It's possible that all events generated by the gesture have been fully
+ // dispatched at this point, in which case |OnDidFlushInput()| was called
+ // before |pending_gesture_result_| was initialized. Requesting another flush
+ // will trigger the necessary gesture-ending call to |OnDidFlushInput()|.
+ pending_gesture_result_.reset(new SyntheticGesture::Result(result));
+ gesture_target_->SetNeedsFlush();
+}
- if (!pending_gesture_queue_.empty()) {
- StartGesture(*pending_gesture_queue_.front());
- } else {
- // Reset last_tick_time_ so that we don't use an old value when a new
- // gestures is queued.
- last_tick_time_ = base::TimeTicks();
- }
+void SyntheticGestureController::OnDidFlushInput() {
+ if (!pending_gesture_result_)
+ return;
+
+ DCHECK(!pending_gesture_queue_.IsEmpty());
+ StopGesture(*pending_gesture_queue_.FrontGesture(),
+ pending_gesture_queue_.FrontCallback(),
+ *pending_gesture_result_.Pass());
+ pending_gesture_queue_.Pop();
+
+ if (!pending_gesture_queue_.IsEmpty())
+ StartGesture(*pending_gesture_queue_.FrontGesture());
}
void SyntheticGestureController::StartGesture(const SyntheticGesture& gesture) {
- TRACE_EVENT_ASYNC_BEGIN0("benchmark", "SyntheticGestureController::running",
+ TRACE_EVENT_ASYNC_BEGIN0("input,benchmark",
+ "SyntheticGestureController::running",
&gesture);
gesture_target_->SetNeedsFlush();
}
void SyntheticGestureController::StopGesture(
- const SyntheticGesture& gesture, SyntheticGesture::Result result) {
+ const SyntheticGesture& gesture,
+ const OnGestureCompleteCallback& completion_callback,
+ SyntheticGesture::Result result) {
DCHECK_NE(result, SyntheticGesture::GESTURE_RUNNING);
- TRACE_EVENT_ASYNC_END0("benchmark", "SyntheticGestureController::running",
+ TRACE_EVENT_ASYNC_END0("input,benchmark",
+ "SyntheticGestureController::running",
&gesture);
- gesture_target_->OnSyntheticGestureCompleted(result);
+ completion_callback.Run(result);
+}
+
+SyntheticGestureController::GestureAndCallbackQueue::GestureAndCallbackQueue() {
+}
+
+SyntheticGestureController::GestureAndCallbackQueue::
+ ~GestureAndCallbackQueue() {
}
} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/synthetic_gesture_controller.h b/chromium/content/browser/renderer_host/input/synthetic_gesture_controller.h
index ed9cec77653..0a42f316756 100644
--- a/chromium/content/browser/renderer_host/input/synthetic_gesture_controller.h
+++ b/chromium/content/browser/renderer_host/input/synthetic_gesture_controller.h
@@ -5,6 +5,10 @@
#ifndef CONTENT_BROWSER_RENDERER_HOST_INPUT_SYNTHETIC_GESTURE_CONTROLLER_H_
#define CONTENT_BROWSER_RENDERER_HOST_INPUT_SYNTHETIC_GESTURE_CONTROLLER_H_
+#include <queue>
+#include <utility>
+
+#include "base/callback.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "base/time/time.h"
@@ -25,21 +29,57 @@ class CONTENT_EXPORT SyntheticGestureController {
scoped_ptr<SyntheticGestureTarget> gesture_target);
virtual ~SyntheticGestureController();
+ typedef base::Callback<void(SyntheticGesture::Result)>
+ OnGestureCompleteCallback;
void QueueSyntheticGesture(
- scoped_ptr<SyntheticGesture> synthetic_gesture);
+ scoped_ptr<SyntheticGesture> synthetic_gesture,
+ const OnGestureCompleteCallback& completion_callback);
// Forward input events of the currently processed gesture.
void Flush(base::TimeTicks timestamp);
+ // To be called when all events generated from the current gesture have been
+ // fully flushed from the input pipeline (i.e., sent, processed and ack'ed).
+ void OnDidFlushInput();
+
private:
void StartGesture(const SyntheticGesture& gesture);
void StopGesture(const SyntheticGesture& gesture,
+ const OnGestureCompleteCallback& completion_callback,
SyntheticGesture::Result result);
scoped_ptr<SyntheticGestureTarget> gesture_target_;
- ScopedVector<SyntheticGesture> pending_gesture_queue_;
+ scoped_ptr<SyntheticGesture::Result> pending_gesture_result_;
- base::TimeTicks last_tick_time_;
+ // A queue of gesture/callback pairs. Implemented as two queues to
+ // simplify the ownership of SyntheticGesture pointers.
+ class GestureAndCallbackQueue {
+ public:
+ GestureAndCallbackQueue();
+ ~GestureAndCallbackQueue();
+ void Push(scoped_ptr<SyntheticGesture> gesture,
+ const OnGestureCompleteCallback& callback) {
+ gestures_.push_back(gesture.release());
+ callbacks_.push(callback);
+ }
+ void Pop() {
+ gestures_.erase(gestures_.begin());
+ callbacks_.pop();
+ }
+ SyntheticGesture* FrontGesture() {
+ return gestures_.front();
+ }
+ OnGestureCompleteCallback& FrontCallback() {
+ return callbacks_.front();
+ }
+ bool IsEmpty() {
+ CHECK(gestures_.empty() == callbacks_.empty());
+ return gestures_.empty();
+ }
+ private:
+ ScopedVector<SyntheticGesture> gestures_;
+ std::queue<OnGestureCompleteCallback> callbacks_;
+ } pending_gesture_queue_;
DISALLOW_COPY_AND_ASSIGN(SyntheticGestureController);
};
diff --git a/chromium/content/browser/renderer_host/input/synthetic_gesture_controller_unittest.cc b/chromium/content/browser/renderer_host/input/synthetic_gesture_controller_unittest.cc
index f449e5774ca..b79e797aad2 100644
--- a/chromium/content/browser/renderer_host/input/synthetic_gesture_controller_unittest.cc
+++ b/chromium/content/browser/renderer_host/input/synthetic_gesture_controller_unittest.cc
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/bind.h"
#include "base/memory/scoped_ptr.h"
#include "base/time/time.h"
#include "content/browser/renderer_host/input/synthetic_gesture.h"
@@ -11,7 +12,6 @@
#include "content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.h"
#include "content/browser/renderer_host/input/synthetic_tap_gesture.h"
#include "content/browser/renderer_host/render_widget_host_delegate.h"
-#include "content/common/input/input_event.h"
#include "content/common/input/synthetic_pinch_gesture_params.h"
#include "content/common/input/synthetic_smooth_scroll_gesture_params.h"
#include "content/common/input/synthetic_tap_gesture_params.h"
@@ -25,13 +25,19 @@
#include "ui/gfx/vector2d.h"
#include "ui/gfx/vector2d_f.h"
+using blink::WebInputEvent;
+using blink::WebMouseEvent;
+using blink::WebMouseWheelEvent;
+using blink::WebTouchEvent;
+
namespace content {
namespace {
const int kFlushInputRateInMs = 16;
const int kPointerAssumedStoppedTimeMs = 43;
-const int kTouchSlopInDips = 7;
+const float kTouchSlopInDips = 7.0f;
+const float kMinScalingSpanInDips = 27.5f;
class MockSyntheticGesture : public SyntheticGesture {
public:
@@ -43,7 +49,7 @@ class MockSyntheticGesture : public SyntheticGesture {
}
virtual ~MockSyntheticGesture() {}
- virtual Result ForwardInputEvents(const base::TimeDelta& interval,
+ virtual Result ForwardInputEvents(const base::TimeTicks& timestamp,
SyntheticGestureTarget* target) OVERRIDE {
step_count_++;
if (step_count_ == num_steps_) {
@@ -67,23 +73,13 @@ class MockSyntheticGesture : public SyntheticGesture {
class MockSyntheticGestureTarget : public SyntheticGestureTarget {
public:
MockSyntheticGestureTarget()
- : num_success_(0),
- num_failure_(0),
- flush_requested_(false),
+ : flush_requested_(false),
pointer_assumed_stopped_time_ms_(kPointerAssumedStoppedTimeMs) {}
virtual ~MockSyntheticGestureTarget() {}
// SyntheticGestureTarget:
- virtual void DispatchInputEventToPlatform(const InputEvent& event) OVERRIDE {}
-
- virtual void OnSyntheticGestureCompleted(
- SyntheticGesture::Result result) OVERRIDE {
- DCHECK_NE(result, SyntheticGesture::GESTURE_RUNNING);
- if (result == SyntheticGesture::GESTURE_FINISHED)
- num_success_++;
- else
- num_failure_++;
- }
+ virtual void DispatchInputEventToPlatform(
+ const WebInputEvent& event) OVERRIDE {}
virtual void SetNeedsFlush() OVERRIDE {
flush_requested_ = true;
@@ -93,11 +89,6 @@ class MockSyntheticGestureTarget : public SyntheticGestureTarget {
GetDefaultSyntheticGestureSourceType() const OVERRIDE {
return SyntheticGestureParams::TOUCH_INPUT;
}
- virtual bool SupportsSyntheticGestureSourceType(
- SyntheticGestureParams::GestureSourceType gesture_source_type)
- const OVERRIDE {
- return true;
- }
virtual base::TimeDelta PointerAssumedStoppedTime() const OVERRIDE {
return base::TimeDelta::FromMilliseconds(pointer_assumed_stopped_time_ms_);
@@ -107,86 +98,92 @@ class MockSyntheticGestureTarget : public SyntheticGestureTarget {
pointer_assumed_stopped_time_ms_ = time_ms;
}
- virtual int GetTouchSlopInDips() const OVERRIDE {
+ virtual float GetTouchSlopInDips() const OVERRIDE {
return kTouchSlopInDips;
}
- int num_success() const { return num_success_; }
- int num_failure() const { return num_failure_; }
+ virtual float GetMinScalingSpanInDips() const OVERRIDE {
+ return kMinScalingSpanInDips;
+ }
bool flush_requested() const { return flush_requested_; }
void ClearFlushRequest() { flush_requested_ = false; }
private:
- int num_success_;
- int num_failure_;
-
bool flush_requested_;
int pointer_assumed_stopped_time_ms_;
};
-class MockSyntheticSmoothScrollGestureTarget
- : public MockSyntheticGestureTarget {
+class MockScrollGestureTarget : public MockSyntheticGestureTarget {
public:
- MockSyntheticSmoothScrollGestureTarget() {}
- virtual ~MockSyntheticSmoothScrollGestureTarget() {}
+ MockScrollGestureTarget() : total_abs_scroll_distance_length_(0) {}
+ virtual ~MockScrollGestureTarget() {}
- gfx::Vector2dF scroll_distance() const { return scroll_distance_; }
+ gfx::Vector2dF start_to_end_distance() const {
+ return start_to_end_distance_;
+ }
+ float total_abs_scroll_distance_length() const {
+ return total_abs_scroll_distance_length_;
+ }
protected:
- gfx::Vector2dF scroll_distance_;
+ gfx::Vector2dF start_to_end_distance_;
+ float total_abs_scroll_distance_length_;
};
-class MockSyntheticSmoothScrollMouseTarget
- : public MockSyntheticSmoothScrollGestureTarget {
+class MockScrollMouseTarget : public MockScrollGestureTarget {
public:
- MockSyntheticSmoothScrollMouseTarget() {}
- virtual ~MockSyntheticSmoothScrollMouseTarget() {}
-
- virtual void DispatchInputEventToPlatform(const InputEvent& event) OVERRIDE {
- const blink::WebInputEvent* web_event = event.web_event.get();
- ASSERT_EQ(web_event->type, blink::WebInputEvent::MouseWheel);
- const blink::WebMouseWheelEvent* mouse_wheel_event =
- static_cast<const blink::WebMouseWheelEvent*>(web_event);
- scroll_distance_ -= gfx::Vector2dF(mouse_wheel_event->deltaX,
- mouse_wheel_event->deltaY);
+ MockScrollMouseTarget() {}
+ virtual ~MockScrollMouseTarget() {}
+
+ virtual void DispatchInputEventToPlatform(
+ const WebInputEvent& event) OVERRIDE {
+ ASSERT_EQ(event.type, WebInputEvent::MouseWheel);
+ const WebMouseWheelEvent& mouse_wheel_event =
+ static_cast<const WebMouseWheelEvent&>(event);
+ gfx::Vector2dF delta(mouse_wheel_event.deltaX, mouse_wheel_event.deltaY);
+ start_to_end_distance_ += delta;
+ total_abs_scroll_distance_length_ += delta.Length();
}
};
-class MockSyntheticSmoothScrollTouchTarget
- : public MockSyntheticSmoothScrollGestureTarget {
+class MockScrollTouchTarget : public MockScrollGestureTarget {
public:
- MockSyntheticSmoothScrollTouchTarget()
- : started_(false) {}
- virtual ~MockSyntheticSmoothScrollTouchTarget() {}
+ MockScrollTouchTarget() : started_(false) {}
+ virtual ~MockScrollTouchTarget() {}
- virtual void DispatchInputEventToPlatform(const InputEvent& event) OVERRIDE {
- const blink::WebInputEvent* web_event = event.web_event.get();
- ASSERT_TRUE(blink::WebInputEvent::isTouchEventType(web_event->type));
- const blink::WebTouchEvent* touch_event =
- static_cast<const blink::WebTouchEvent*>(web_event);
- ASSERT_EQ(touch_event->touchesLength, (unsigned int)1);
+ virtual void DispatchInputEventToPlatform(
+ const WebInputEvent& event) OVERRIDE {
+ ASSERT_TRUE(WebInputEvent::isTouchEventType(event.type));
+ const WebTouchEvent& touch_event = static_cast<const WebTouchEvent&>(event);
+ ASSERT_EQ(touch_event.touchesLength, 1U);
if (!started_) {
- ASSERT_EQ(touch_event->type, blink::WebInputEvent::TouchStart);
- anchor_.SetPoint(touch_event->touches[0].position.x,
- touch_event->touches[0].position.y);
+ ASSERT_EQ(touch_event.type, WebInputEvent::TouchStart);
+ start_.SetPoint(touch_event.touches[0].position.x,
+ touch_event.touches[0].position.y);
+ last_touch_point_ = start_;
started_ = true;
} else {
- ASSERT_NE(touch_event->type, blink::WebInputEvent::TouchStart);
- ASSERT_NE(touch_event->type, blink::WebInputEvent::TouchCancel);
- // Ignore move events.
-
- if (touch_event->type == blink::WebInputEvent::TouchEnd)
- scroll_distance_ =
- anchor_ - gfx::PointF(touch_event->touches[0].position.x,
- touch_event->touches[0].position.y);
+ ASSERT_NE(touch_event.type, WebInputEvent::TouchStart);
+ ASSERT_NE(touch_event.type, WebInputEvent::TouchCancel);
+
+ gfx::PointF touch_point(touch_event.touches[0].position.x,
+ touch_event.touches[0].position.y);
+ gfx::Vector2dF delta = touch_point - last_touch_point_;
+ total_abs_scroll_distance_length_ += delta.Length();
+
+ if (touch_event.type == WebInputEvent::TouchEnd)
+ start_to_end_distance_ = touch_point - start_;
+
+ last_touch_point_ = touch_point;
}
}
protected:
- gfx::Point anchor_;
+ gfx::Point start_;
+ gfx::PointF last_touch_point_;
bool started_;
};
@@ -199,36 +196,35 @@ class MockSyntheticPinchTouchTarget : public MockSyntheticGestureTarget {
};
MockSyntheticPinchTouchTarget()
- : total_num_pixels_covered_(0),
+ : initial_pointer_distance_(0),
last_pointer_distance_(0),
zoom_direction_(ZOOM_DIRECTION_UNKNOWN),
started_(false) {}
virtual ~MockSyntheticPinchTouchTarget() {}
- virtual void DispatchInputEventToPlatform(const InputEvent& event) OVERRIDE {
- const blink::WebInputEvent* web_event = event.web_event.get();
- ASSERT_TRUE(blink::WebInputEvent::isTouchEventType(web_event->type));
- const blink::WebTouchEvent* touch_event =
- static_cast<const blink::WebTouchEvent*>(web_event);
- ASSERT_EQ(touch_event->touchesLength, (unsigned int)2);
+ virtual void DispatchInputEventToPlatform(
+ const WebInputEvent& event) OVERRIDE {
+ ASSERT_TRUE(WebInputEvent::isTouchEventType(event.type));
+ const WebTouchEvent& touch_event = static_cast<const WebTouchEvent&>(event);
+ ASSERT_EQ(touch_event.touchesLength, 2U);
if (!started_) {
- ASSERT_EQ(touch_event->type, blink::WebInputEvent::TouchStart);
+ ASSERT_EQ(touch_event.type, WebInputEvent::TouchStart);
- start_0_ = gfx::Point(touch_event->touches[0].position);
- start_1_ = gfx::Point(touch_event->touches[1].position);
+ start_0_ = gfx::PointF(touch_event.touches[0].position);
+ start_1_ = gfx::PointF(touch_event.touches[1].position);
last_pointer_distance_ = (start_0_ - start_1_).Length();
+ initial_pointer_distance_ = last_pointer_distance_;
+ EXPECT_GE(initial_pointer_distance_, GetMinScalingSpanInDips());
started_ = true;
} else {
- ASSERT_NE(touch_event->type, blink::WebInputEvent::TouchStart);
- ASSERT_NE(touch_event->type, blink::WebInputEvent::TouchCancel);
+ ASSERT_NE(touch_event.type, WebInputEvent::TouchStart);
+ ASSERT_NE(touch_event.type, WebInputEvent::TouchCancel);
- gfx::PointF current_0 = gfx::Point(touch_event->touches[0].position);
- gfx::PointF current_1 = gfx::Point(touch_event->touches[1].position);
+ gfx::PointF current_0 = gfx::PointF(touch_event.touches[0].position);
+ gfx::PointF current_1 = gfx::PointF(touch_event.touches[1].position);
- total_num_pixels_covered_ =
- (current_0 - start_0_).Length() + (current_1 - start_1_).Length();
float pointer_distance = (current_0 - current_1).Length();
if (last_pointer_distance_ != pointer_distance) {
@@ -245,9 +241,24 @@ class MockSyntheticPinchTouchTarget : public MockSyntheticGestureTarget {
}
}
- float total_num_pixels_covered() const { return total_num_pixels_covered_; }
ZoomDirection zoom_direction() const { return zoom_direction_; }
+ float ComputeScaleFactor() const {
+ switch (zoom_direction_) {
+ case ZOOM_IN:
+ return last_pointer_distance_ /
+ (initial_pointer_distance_ + 2 * GetTouchSlopInDips());
+ case ZOOM_OUT:
+ return last_pointer_distance_ /
+ (initial_pointer_distance_ - 2 * GetTouchSlopInDips());
+ case ZOOM_DIRECTION_UNKNOWN:
+ return 1.0f;
+ default:
+ NOTREACHED();
+ return 0.0f;
+ }
+ }
+
private:
ZoomDirection ComputeZoomDirection(float last_pointer_distance,
float current_pointer_distance) {
@@ -256,7 +267,7 @@ class MockSyntheticPinchTouchTarget : public MockSyntheticGestureTarget {
: ZOOM_OUT;
}
- float total_num_pixels_covered_;
+ float initial_pointer_distance_;
float last_pointer_distance_;
ZoomDirection zoom_direction_;
gfx::PointF start_0_;
@@ -270,7 +281,8 @@ class MockSyntheticTapGestureTarget : public MockSyntheticGestureTarget {
virtual ~MockSyntheticTapGestureTarget() {}
bool GestureFinished() const { return state_ == FINISHED; }
- gfx::Point position() const { return position_; }
+ gfx::PointF position() const { return position_; }
+ base::TimeDelta GetDuration() const { return stop_time_ - start_time_; }
protected:
enum GestureState {
@@ -279,7 +291,9 @@ class MockSyntheticTapGestureTarget : public MockSyntheticGestureTarget {
FINISHED
};
- gfx::Point position_;
+ gfx::PointF position_;
+ base::TimeDelta start_time_;
+ base::TimeDelta stop_time_;
GestureState state_;
};
@@ -288,22 +302,25 @@ class MockSyntheticTapTouchTarget : public MockSyntheticTapGestureTarget {
MockSyntheticTapTouchTarget() {}
virtual ~MockSyntheticTapTouchTarget() {}
- virtual void DispatchInputEventToPlatform(const InputEvent& event) OVERRIDE {
- const blink::WebInputEvent* web_event = event.web_event.get();
- ASSERT_TRUE(blink::WebInputEvent::isTouchEventType(web_event->type));
- const blink::WebTouchEvent* touch_event =
- static_cast<const blink::WebTouchEvent*>(web_event);
- ASSERT_EQ(touch_event->touchesLength, (unsigned int)1);
+ virtual void DispatchInputEventToPlatform(
+ const WebInputEvent& event) OVERRIDE {
+ ASSERT_TRUE(WebInputEvent::isTouchEventType(event.type));
+ const WebTouchEvent& touch_event = static_cast<const WebTouchEvent&>(event);
+ ASSERT_EQ(touch_event.touchesLength, 1U);
switch (state_) {
case NOT_STARTED:
- EXPECT_EQ(touch_event->type, blink::WebInputEvent::TouchStart);
- position_ = gfx::Point(touch_event->touches[0].position);
+ EXPECT_EQ(touch_event.type, WebInputEvent::TouchStart);
+ position_ = gfx::PointF(touch_event.touches[0].position);
+ start_time_ = base::TimeDelta::FromMilliseconds(
+ static_cast<int64>(touch_event.timeStampSeconds * 1000));
state_ = STARTED;
break;
case STARTED:
- EXPECT_EQ(touch_event->type, blink::WebInputEvent::TouchEnd);
- EXPECT_EQ(position_, gfx::Point(touch_event->touches[0].position));
+ EXPECT_EQ(touch_event.type, WebInputEvent::TouchEnd);
+ EXPECT_EQ(position_, gfx::PointF(touch_event.touches[0].position));
+ stop_time_ = base::TimeDelta::FromMilliseconds(
+ static_cast<int64>(touch_event.timeStampSeconds * 1000));
state_ = FINISHED;
break;
case FINISHED:
@@ -318,25 +335,28 @@ class MockSyntheticTapMouseTarget : public MockSyntheticTapGestureTarget {
MockSyntheticTapMouseTarget() {}
virtual ~MockSyntheticTapMouseTarget() {}
- virtual void DispatchInputEventToPlatform(const InputEvent& event) OVERRIDE {
- const blink::WebInputEvent* web_event = event.web_event.get();
- ASSERT_TRUE(blink::WebInputEvent::isMouseEventType(web_event->type));
- const blink::WebMouseEvent* mouse_event =
- static_cast<const blink::WebMouseEvent*>(web_event);
+ virtual void DispatchInputEventToPlatform(
+ const WebInputEvent& event) OVERRIDE {
+ ASSERT_TRUE(WebInputEvent::isMouseEventType(event.type));
+ const WebMouseEvent& mouse_event = static_cast<const WebMouseEvent&>(event);
switch (state_) {
case NOT_STARTED:
- EXPECT_EQ(mouse_event->type, blink::WebInputEvent::MouseDown);
- EXPECT_EQ(mouse_event->button, blink::WebMouseEvent::ButtonLeft);
- EXPECT_EQ(mouse_event->clickCount, 1);
- position_ = gfx::Point(mouse_event->x, mouse_event->y);
+ EXPECT_EQ(mouse_event.type, WebInputEvent::MouseDown);
+ EXPECT_EQ(mouse_event.button, WebMouseEvent::ButtonLeft);
+ EXPECT_EQ(mouse_event.clickCount, 1);
+ position_ = gfx::PointF(mouse_event.x, mouse_event.y);
+ start_time_ = base::TimeDelta::FromMilliseconds(
+ static_cast<int64>(mouse_event.timeStampSeconds * 1000));
state_ = STARTED;
break;
case STARTED:
- EXPECT_EQ(mouse_event->type, blink::WebInputEvent::MouseUp);
- EXPECT_EQ(mouse_event->button, blink::WebMouseEvent::ButtonLeft);
- EXPECT_EQ(mouse_event->clickCount, 1);
- EXPECT_EQ(position_, gfx::Point(mouse_event->x, mouse_event->y));
+ EXPECT_EQ(mouse_event.type, WebInputEvent::MouseUp);
+ EXPECT_EQ(mouse_event.button, WebMouseEvent::ButtonLeft);
+ EXPECT_EQ(mouse_event.clickCount, 1);
+ EXPECT_EQ(position_, gfx::PointF(mouse_event.x, mouse_event.y));
+ stop_time_ = base::TimeDelta::FromMilliseconds(
+ static_cast<int64>(mouse_event.timeStampSeconds * 1000));
state_ = FINISHED;
break;
case FINISHED:
@@ -355,7 +375,6 @@ class SyntheticGestureControllerTest : public testing::Test {
template<typename MockGestureTarget>
void CreateControllerAndTarget() {
target_ = new MockGestureTarget();
-
controller_.reset(new SyntheticGestureController(
scoped_ptr<SyntheticGestureTarget>(target_)));
}
@@ -363,6 +382,8 @@ class SyntheticGestureControllerTest : public testing::Test {
virtual void SetUp() OVERRIDE {
start_time_ = base::TimeTicks::Now();
time_ = start_time_;
+ num_success_ = 0;
+ num_failure_ = 0;
}
virtual void TearDown() OVERRIDE {
@@ -371,20 +392,39 @@ class SyntheticGestureControllerTest : public testing::Test {
time_ = base::TimeTicks();
}
+ void QueueSyntheticGesture(scoped_ptr<SyntheticGesture> gesture) {
+ controller_->QueueSyntheticGesture(gesture.Pass(),
+ base::Bind(&SyntheticGestureControllerTest::OnSyntheticGestureCompleted,
+ base::Unretained(this)));
+ }
+
void FlushInputUntilComplete() {
while (target_->flush_requested()) {
- target_->ClearFlushRequest();
- time_ += base::TimeDelta::FromMilliseconds(kFlushInputRateInMs);
- controller_->Flush(time_);
+ while (target_->flush_requested()) {
+ target_->ClearFlushRequest();
+ time_ += base::TimeDelta::FromMilliseconds(kFlushInputRateInMs);
+ controller_->Flush(time_);
+ }
+ controller_->OnDidFlushInput();
}
}
+ void OnSyntheticGestureCompleted(SyntheticGesture::Result result) {
+ DCHECK_NE(result, SyntheticGesture::GESTURE_RUNNING);
+ if (result == SyntheticGesture::GESTURE_FINISHED)
+ num_success_++;
+ else
+ num_failure_++;
+ }
+
base::TimeDelta GetTotalTime() const { return time_ - start_time_; }
MockSyntheticGestureTarget* target_;
scoped_ptr<SyntheticGestureController> controller_;
base::TimeTicks start_time_;
base::TimeTicks time_;
+ int num_success_;
+ int num_failure_;
};
TEST_F(SyntheticGestureControllerTest, SingleGesture) {
@@ -393,12 +433,12 @@ TEST_F(SyntheticGestureControllerTest, SingleGesture) {
bool finished;
scoped_ptr<MockSyntheticGesture> gesture(
new MockSyntheticGesture(&finished, 3));
- controller_->QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
+ QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
FlushInputUntilComplete();
EXPECT_TRUE(finished);
- EXPECT_EQ(1, target_->num_success());
- EXPECT_EQ(0, target_->num_failure());
+ EXPECT_EQ(1, num_success_);
+ EXPECT_EQ(0, num_failure_);
}
TEST_F(SyntheticGestureControllerTest, GestureFailed) {
@@ -407,12 +447,12 @@ TEST_F(SyntheticGestureControllerTest, GestureFailed) {
bool finished;
scoped_ptr<MockSyntheticGesture> gesture(
new MockSyntheticGesture(&finished, 0));
- controller_->QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
+ QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
FlushInputUntilComplete();
EXPECT_TRUE(finished);
- EXPECT_EQ(1, target_->num_failure());
- EXPECT_EQ(0, target_->num_success());
+ EXPECT_EQ(1, num_failure_);
+ EXPECT_EQ(0, num_success_);
}
TEST_F(SyntheticGestureControllerTest, SuccessiveGestures) {
@@ -425,20 +465,20 @@ TEST_F(SyntheticGestureControllerTest, SuccessiveGestures) {
new MockSyntheticGesture(&finished_2, 4));
// Queue first gesture and wait for it to finish
- controller_->QueueSyntheticGesture(gesture_1.PassAs<SyntheticGesture>());
+ QueueSyntheticGesture(gesture_1.PassAs<SyntheticGesture>());
FlushInputUntilComplete();
EXPECT_TRUE(finished_1);
- EXPECT_EQ(1, target_->num_success());
- EXPECT_EQ(0, target_->num_failure());
+ EXPECT_EQ(1, num_success_);
+ EXPECT_EQ(0, num_failure_);
// Queue second gesture.
- controller_->QueueSyntheticGesture(gesture_2.PassAs<SyntheticGesture>());
+ QueueSyntheticGesture(gesture_2.PassAs<SyntheticGesture>());
FlushInputUntilComplete();
EXPECT_TRUE(finished_2);
- EXPECT_EQ(2, target_->num_success());
- EXPECT_EQ(0, target_->num_failure());
+ EXPECT_EQ(2, num_success_);
+ EXPECT_EQ(0, num_failure_);
}
TEST_F(SyntheticGestureControllerTest, TwoGesturesInFlight) {
@@ -450,15 +490,46 @@ TEST_F(SyntheticGestureControllerTest, TwoGesturesInFlight) {
scoped_ptr<MockSyntheticGesture> gesture_2(
new MockSyntheticGesture(&finished_2, 4));
- controller_->QueueSyntheticGesture(gesture_1.PassAs<SyntheticGesture>());
- controller_->QueueSyntheticGesture(gesture_2.PassAs<SyntheticGesture>());
+ QueueSyntheticGesture(gesture_1.PassAs<SyntheticGesture>());
+ QueueSyntheticGesture(gesture_2.PassAs<SyntheticGesture>());
FlushInputUntilComplete();
EXPECT_TRUE(finished_1);
EXPECT_TRUE(finished_2);
- EXPECT_EQ(2, target_->num_success());
- EXPECT_EQ(0, target_->num_failure());
+ EXPECT_EQ(2, num_success_);
+ EXPECT_EQ(0, num_failure_);
+}
+
+TEST_F(SyntheticGestureControllerTest, GestureCompletedOnDidFlushInput) {
+ CreateControllerAndTarget<MockSyntheticGestureTarget>();
+
+ bool finished_1, finished_2;
+ scoped_ptr<MockSyntheticGesture> gesture_1(
+ new MockSyntheticGesture(&finished_1, 2));
+ scoped_ptr<MockSyntheticGesture> gesture_2(
+ new MockSyntheticGesture(&finished_2, 4));
+
+ QueueSyntheticGesture(gesture_1.PassAs<SyntheticGesture>());
+ QueueSyntheticGesture(gesture_2.PassAs<SyntheticGesture>());
+
+ while (target_->flush_requested()) {
+ target_->ClearFlushRequest();
+ time_ += base::TimeDelta::FromMilliseconds(kFlushInputRateInMs);
+ controller_->Flush(time_);
+ }
+ EXPECT_EQ(0, num_success_);
+ controller_->OnDidFlushInput();
+ EXPECT_EQ(1, num_success_);
+
+ while (target_->flush_requested()) {
+ target_->ClearFlushRequest();
+ time_ += base::TimeDelta::FromMilliseconds(kFlushInputRateInMs);
+ controller_->Flush(time_);
+ }
+ EXPECT_EQ(1, num_success_);
+ controller_->OnDidFlushInput();
+ EXPECT_EQ(2, num_success_);
}
gfx::Vector2d AddTouchSlopToVector(const gfx::Vector2d& vector,
@@ -480,51 +551,59 @@ gfx::Vector2d AddTouchSlopToVector(const gfx::Vector2d& vector,
return gfx::Vector2d(x, y);
}
-TEST_F(SyntheticGestureControllerTest, SmoothScrollGestureTouchVertical) {
- CreateControllerAndTarget<MockSyntheticSmoothScrollTouchTarget>();
+TEST_F(SyntheticGestureControllerTest, SingleScrollGestureTouchVertical) {
+ CreateControllerAndTarget<MockScrollTouchTarget>();
SyntheticSmoothScrollGestureParams params;
params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
- params.distance = gfx::Vector2d(0, 123);
params.anchor.SetPoint(89, 32);
+ params.distances.push_back(gfx::Vector2d(0, 123));
scoped_ptr<SyntheticSmoothScrollGesture> gesture(
new SyntheticSmoothScrollGesture(params));
- controller_->QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
+ QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
FlushInputUntilComplete();
- MockSyntheticSmoothScrollGestureTarget* smooth_scroll_target =
- static_cast<MockSyntheticSmoothScrollGestureTarget*>(target_);
- EXPECT_EQ(1, target_->num_success());
- EXPECT_EQ(0, target_->num_failure());
- EXPECT_EQ(AddTouchSlopToVector(params.distance, target_),
- smooth_scroll_target->scroll_distance());
+ MockScrollGestureTarget* scroll_target =
+ static_cast<MockScrollGestureTarget*>(target_);
+ EXPECT_EQ(1, num_success_);
+ EXPECT_EQ(0, num_failure_);
+ // TODO(dominikg): Remove adjustment when crbug.com/332418 is fixed.
+ EXPECT_EQ(AddTouchSlopToVector(params.distances[0], target_),
+ scroll_target->start_to_end_distance() - gfx::Vector2dF(0, 0.001f));
}
-TEST_F(SyntheticGestureControllerTest, SmoothScrollGestureTouchHorizontal) {
- CreateControllerAndTarget<MockSyntheticSmoothScrollTouchTarget>();
+TEST_F(SyntheticGestureControllerTest, SingleScrollGestureTouchHorizontal) {
+ CreateControllerAndTarget<MockScrollTouchTarget>();
SyntheticSmoothScrollGestureParams params;
params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
- params.distance = gfx::Vector2d(-234, 0);
params.anchor.SetPoint(12, -23);
+ params.distances.push_back(gfx::Vector2d(-234, 0));
scoped_ptr<SyntheticSmoothScrollGesture> gesture(
new SyntheticSmoothScrollGesture(params));
- controller_->QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
+ QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
FlushInputUntilComplete();
- MockSyntheticSmoothScrollGestureTarget* smooth_scroll_target =
- static_cast<MockSyntheticSmoothScrollGestureTarget*>(target_);
- EXPECT_EQ(1, target_->num_success());
- EXPECT_EQ(0, target_->num_failure());
- EXPECT_EQ(AddTouchSlopToVector(params.distance, target_),
- smooth_scroll_target->scroll_distance());
+ MockScrollGestureTarget* scroll_target =
+ static_cast<MockScrollGestureTarget*>(target_);
+ EXPECT_EQ(1, num_success_);
+ EXPECT_EQ(0, num_failure_);
+ // TODO(dominikg): Use vector comparison when crbug.com/332418 is fixed.
+ //EXPECT_EQ(AddTouchSlopToVector(params.distances[0], target_),
+ // scroll_target->start_to_end_distance());
+ EXPECT_EQ(AddTouchSlopToVector(params.distances[0], target_).x(),
+ scroll_target->start_to_end_distance().x());
+ EXPECT_LT(AddTouchSlopToVector(params.distances[0], target_).y(),
+ scroll_target->start_to_end_distance().y());
+ EXPECT_GE(AddTouchSlopToVector(params.distances[0], target_).y(),
+ scroll_target->start_to_end_distance().y() - 0.001f);
}
-void CheckIsWithinRange(float scroll_distance,
- int target_distance,
- SyntheticGestureTarget* target) {
+void CheckIsWithinRangeSingle(float scroll_distance,
+ int target_distance,
+ SyntheticGestureTarget* target) {
if (target_distance > 0) {
EXPECT_LE(target_distance, scroll_distance);
EXPECT_LE(scroll_distance, target_distance + target->GetTouchSlopInDips());
@@ -534,168 +613,289 @@ void CheckIsWithinRange(float scroll_distance,
}
}
-void CheckScrollDistanceIsWithinRange(const gfx::Vector2dF& scroll_distance,
- const gfx::Vector2d& target_distance,
- SyntheticGestureTarget* target) {
- CheckIsWithinRange(scroll_distance.x(), target_distance.x(), target);
- CheckIsWithinRange(scroll_distance.y(), target_distance.y(), target);
+void CheckSingleScrollDistanceIsWithinRange(
+ const gfx::Vector2dF& scroll_distance,
+ const gfx::Vector2d& target_distance,
+ SyntheticGestureTarget* target) {
+ CheckIsWithinRangeSingle(scroll_distance.x(), target_distance.x(), target);
+ CheckIsWithinRangeSingle(scroll_distance.y(), target_distance.y(), target);
}
-TEST_F(SyntheticGestureControllerTest, SmoothScrollGestureTouchDiagonal) {
- CreateControllerAndTarget<MockSyntheticSmoothScrollTouchTarget>();
+TEST_F(SyntheticGestureControllerTest, SingleScrollGestureTouchDiagonal) {
+ CreateControllerAndTarget<MockScrollTouchTarget>();
SyntheticSmoothScrollGestureParams params;
params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
- params.distance = gfx::Vector2d(413, -83);
params.anchor.SetPoint(0, 7);
+ params.distances.push_back(gfx::Vector2d(413, -83));
scoped_ptr<SyntheticSmoothScrollGesture> gesture(
new SyntheticSmoothScrollGesture(params));
- controller_->QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
+ QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
FlushInputUntilComplete();
- MockSyntheticSmoothScrollGestureTarget* smooth_scroll_target =
- static_cast<MockSyntheticSmoothScrollGestureTarget*>(target_);
- EXPECT_EQ(1, target_->num_success());
- EXPECT_EQ(0, target_->num_failure());
- CheckScrollDistanceIsWithinRange(
- smooth_scroll_target->scroll_distance(), params.distance, target_);
+ MockScrollGestureTarget* scroll_target =
+ static_cast<MockScrollGestureTarget*>(target_);
+ EXPECT_EQ(1, num_success_);
+ EXPECT_EQ(0, num_failure_);
+ CheckSingleScrollDistanceIsWithinRange(
+ scroll_target->start_to_end_distance(), params.distances[0], target_);
}
-TEST_F(SyntheticGestureControllerTest, SmoothScrollGestureTouchLongStop) {
- CreateControllerAndTarget<MockSyntheticSmoothScrollTouchTarget>();
+TEST_F(SyntheticGestureControllerTest, SingleScrollGestureTouchLongStop) {
+ CreateControllerAndTarget<MockScrollTouchTarget>();
// Create a smooth scroll with a short distance and set the pointer assumed
// stopped time high, so that the stopping should dominate the time the
// gesture is active.
SyntheticSmoothScrollGestureParams params;
params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
- params.distance = gfx::Vector2d(21, -12);
- params.prevent_fling = true;
params.anchor.SetPoint(-98, -23);
+ params.distances.push_back(gfx::Vector2d(21, -12));
+ params.prevent_fling = true;
target_->set_pointer_assumed_stopped_time_ms(543);
scoped_ptr<SyntheticSmoothScrollGesture> gesture(
new SyntheticSmoothScrollGesture(params));
- controller_->QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
+ QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
FlushInputUntilComplete();
- MockSyntheticSmoothScrollGestureTarget* smooth_scroll_target =
- static_cast<MockSyntheticSmoothScrollGestureTarget*>(target_);
- EXPECT_EQ(1, target_->num_success());
- EXPECT_EQ(0, target_->num_failure());
- CheckScrollDistanceIsWithinRange(
- smooth_scroll_target->scroll_distance(), params.distance, target_);
+ MockScrollGestureTarget* scroll_target =
+ static_cast<MockScrollGestureTarget*>(target_);
+ EXPECT_EQ(1, num_success_);
+ EXPECT_EQ(0, num_failure_);
+ CheckSingleScrollDistanceIsWithinRange(
+ scroll_target->start_to_end_distance(), params.distances[0], target_);
EXPECT_GE(GetTotalTime(), target_->PointerAssumedStoppedTime());
}
-TEST_F(SyntheticGestureControllerTest, SmoothScrollGestureTouchFling) {
- CreateControllerAndTarget<MockSyntheticSmoothScrollTouchTarget>();
+TEST_F(SyntheticGestureControllerTest, SingleScrollGestureTouchFling) {
+ CreateControllerAndTarget<MockScrollTouchTarget>();
// Create a smooth scroll with a short distance and set the pointer assumed
// stopped time high. Disable 'prevent_fling' and check that the gesture
// finishes without waiting before it stops.
SyntheticSmoothScrollGestureParams params;
params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
- params.distance = gfx::Vector2d(-43, 19);
- params.prevent_fling = false;
params.anchor.SetPoint(-89, 78);
+ params.distances.push_back(gfx::Vector2d(-43, 19));
+ params.prevent_fling = false;
target_->set_pointer_assumed_stopped_time_ms(543);
scoped_ptr<SyntheticSmoothScrollGesture> gesture(
new SyntheticSmoothScrollGesture(params));
- controller_->QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
+ QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
FlushInputUntilComplete();
- MockSyntheticSmoothScrollGestureTarget* smooth_scroll_target =
- static_cast<MockSyntheticSmoothScrollGestureTarget*>(target_);
- EXPECT_EQ(1, target_->num_success());
- EXPECT_EQ(0, target_->num_failure());
- CheckScrollDistanceIsWithinRange(
- smooth_scroll_target->scroll_distance(), params.distance, target_);
+ MockScrollGestureTarget* scroll_target =
+ static_cast<MockScrollGestureTarget*>(target_);
+ EXPECT_EQ(1, num_success_);
+ EXPECT_EQ(0, num_failure_);
+ CheckSingleScrollDistanceIsWithinRange(
+ scroll_target->start_to_end_distance(), params.distances[0], target_);
EXPECT_LE(GetTotalTime(), target_->PointerAssumedStoppedTime());
}
-TEST_F(SyntheticGestureControllerTest, SmoothScrollGestureTouchZeroDistance) {
- CreateControllerAndTarget<MockSyntheticSmoothScrollTouchTarget>();
+TEST_F(SyntheticGestureControllerTest, SingleScrollGestureTouchZeroDistance) {
+ CreateControllerAndTarget<MockScrollTouchTarget>();
SyntheticSmoothScrollGestureParams params;
params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
- params.distance = gfx::Vector2d(0, 0);
params.anchor.SetPoint(-32, 43);
+ params.distances.push_back(gfx::Vector2d(0, 0));
scoped_ptr<SyntheticSmoothScrollGesture> gesture(
new SyntheticSmoothScrollGesture(params));
- controller_->QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
+ QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
FlushInputUntilComplete();
- MockSyntheticSmoothScrollGestureTarget* smooth_scroll_target =
- static_cast<MockSyntheticSmoothScrollGestureTarget*>(target_);
- EXPECT_EQ(1, target_->num_success());
- EXPECT_EQ(0, target_->num_failure());
- EXPECT_EQ(gfx::Vector2dF(0, 0), smooth_scroll_target->scroll_distance());
+ MockScrollGestureTarget* scroll_target =
+ static_cast<MockScrollGestureTarget*>(target_);
+ EXPECT_EQ(1, num_success_);
+ EXPECT_EQ(0, num_failure_);
+ EXPECT_EQ(gfx::Vector2dF(0, 0), scroll_target->start_to_end_distance());
}
-TEST_F(SyntheticGestureControllerTest, SmoothScrollGestureMouseVertical) {
- CreateControllerAndTarget<MockSyntheticSmoothScrollMouseTarget>();
+TEST_F(SyntheticGestureControllerTest, SingleScrollGestureMouseVertical) {
+ CreateControllerAndTarget<MockScrollMouseTarget>();
SyntheticSmoothScrollGestureParams params;
params.gesture_source_type = SyntheticGestureParams::MOUSE_INPUT;
- params.distance = gfx::Vector2d(0, -234);
params.anchor.SetPoint(432, 89);
+ params.distances.push_back(gfx::Vector2d(0, -234));
+
+ scoped_ptr<SyntheticSmoothScrollGesture> gesture(
+ new SyntheticSmoothScrollGesture(params));
+ QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
+ FlushInputUntilComplete();
+
+ MockScrollGestureTarget* scroll_target =
+ static_cast<MockScrollGestureTarget*>(target_);
+ EXPECT_EQ(1, num_success_);
+ EXPECT_EQ(0, num_failure_);
+ EXPECT_EQ(params.distances[0], scroll_target->start_to_end_distance());
+}
+
+TEST_F(SyntheticGestureControllerTest, SingleScrollGestureMouseHorizontal) {
+ CreateControllerAndTarget<MockScrollMouseTarget>();
+
+ SyntheticSmoothScrollGestureParams params;
+ params.gesture_source_type = SyntheticGestureParams::MOUSE_INPUT;
+ params.anchor.SetPoint(90, 12);
+ params.distances.push_back(gfx::Vector2d(345, 0));
+
+ scoped_ptr<SyntheticSmoothScrollGesture> gesture(
+ new SyntheticSmoothScrollGesture(params));
+ QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
+ FlushInputUntilComplete();
+
+ MockScrollGestureTarget* scroll_target =
+ static_cast<MockScrollGestureTarget*>(target_);
+ EXPECT_EQ(1, num_success_);
+ EXPECT_EQ(0, num_failure_);
+ EXPECT_EQ(params.distances[0], scroll_target->start_to_end_distance());
+}
+
+TEST_F(SyntheticGestureControllerTest, SingleScrollGestureMouseDiagonal) {
+ CreateControllerAndTarget<MockScrollMouseTarget>();
+
+ SyntheticSmoothScrollGestureParams params;
+ params.gesture_source_type = SyntheticGestureParams::MOUSE_INPUT;
+ params.anchor.SetPoint(90, 12);
+ params.distances.push_back(gfx::Vector2d(-194, 303));
scoped_ptr<SyntheticSmoothScrollGesture> gesture(
new SyntheticSmoothScrollGesture(params));
- controller_->QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
+ QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
FlushInputUntilComplete();
- MockSyntheticSmoothScrollGestureTarget* smooth_scroll_target =
- static_cast<MockSyntheticSmoothScrollGestureTarget*>(target_);
- EXPECT_EQ(1, target_->num_success());
- EXPECT_EQ(0, target_->num_failure());
- EXPECT_EQ(params.distance, smooth_scroll_target->scroll_distance());
+ MockScrollGestureTarget* scroll_target =
+ static_cast<MockScrollGestureTarget*>(target_);
+ EXPECT_EQ(1, num_success_);
+ EXPECT_EQ(0, num_failure_);
+ EXPECT_EQ(params.distances[0], scroll_target->start_to_end_distance());
}
-TEST_F(SyntheticGestureControllerTest, SmoothScrollGestureMouseHorizontal) {
- CreateControllerAndTarget<MockSyntheticSmoothScrollMouseTarget>();
+TEST_F(SyntheticGestureControllerTest, MultiScrollGestureMouse) {
+ CreateControllerAndTarget<MockScrollMouseTarget>();
SyntheticSmoothScrollGestureParams params;
params.gesture_source_type = SyntheticGestureParams::MOUSE_INPUT;
- params.distance = gfx::Vector2d(345, 0);
params.anchor.SetPoint(90, 12);
+ params.distances.push_back(gfx::Vector2d(-129, 212));
+ params.distances.push_back(gfx::Vector2d(8, -9));
scoped_ptr<SyntheticSmoothScrollGesture> gesture(
new SyntheticSmoothScrollGesture(params));
- controller_->QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
+ QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
FlushInputUntilComplete();
- MockSyntheticSmoothScrollGestureTarget* smooth_scroll_target =
- static_cast<MockSyntheticSmoothScrollGestureTarget*>(target_);
- EXPECT_EQ(1, target_->num_success());
- EXPECT_EQ(0, target_->num_failure());
- EXPECT_EQ(params.distance, smooth_scroll_target->scroll_distance());
+ MockScrollGestureTarget* scroll_target =
+ static_cast<MockScrollGestureTarget*>(target_);
+ EXPECT_EQ(1, num_success_);
+ EXPECT_EQ(0, num_failure_);
+ EXPECT_EQ(params.distances[0] + params.distances[1],
+ scroll_target->start_to_end_distance());
}
-TEST_F(SyntheticGestureControllerTest, SmoothScrollGestureMouseDiagonal) {
- CreateControllerAndTarget<MockSyntheticSmoothScrollMouseTarget>();
+TEST_F(SyntheticGestureControllerTest, MultiScrollGestureMouseHorizontal) {
+ CreateControllerAndTarget<MockScrollMouseTarget>();
SyntheticSmoothScrollGestureParams params;
params.gesture_source_type = SyntheticGestureParams::MOUSE_INPUT;
- params.distance = gfx::Vector2d(-194, 303);
params.anchor.SetPoint(90, 12);
+ params.distances.push_back(gfx::Vector2d(-129, 0));
+ params.distances.push_back(gfx::Vector2d(79, 0));
+
+ scoped_ptr<SyntheticSmoothScrollGesture> gesture(
+ new SyntheticSmoothScrollGesture(params));
+ QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
+ FlushInputUntilComplete();
+
+ MockScrollGestureTarget* scroll_target =
+ static_cast<MockScrollGestureTarget*>(target_);
+ EXPECT_EQ(1, num_success_);
+ EXPECT_EQ(0, num_failure_);
+ // This check only works for horizontal or vertical scrolls because of
+ // floating point precision issues with diagonal scrolls.
+ EXPECT_FLOAT_EQ(params.distances[0].Length() + params.distances[1].Length(),
+ scroll_target->total_abs_scroll_distance_length());
+ EXPECT_EQ(params.distances[0] + params.distances[1],
+ scroll_target->start_to_end_distance());
+}
+
+void CheckIsWithinRangeMulti(float scroll_distance,
+ int target_distance,
+ SyntheticGestureTarget* target) {
+ if (target_distance > 0) {
+ EXPECT_GE(scroll_distance, target_distance - target->GetTouchSlopInDips());
+ EXPECT_LE(scroll_distance, target_distance + target->GetTouchSlopInDips());
+ } else {
+ EXPECT_LE(scroll_distance, target_distance + target->GetTouchSlopInDips());
+ EXPECT_GE(scroll_distance, target_distance - target->GetTouchSlopInDips());
+ }
+}
+
+void CheckMultiScrollDistanceIsWithinRange(
+ const gfx::Vector2dF& scroll_distance,
+ const gfx::Vector2d& target_distance,
+ SyntheticGestureTarget* target) {
+ CheckIsWithinRangeMulti(scroll_distance.x(), target_distance.x(), target);
+ CheckIsWithinRangeMulti(scroll_distance.y(), target_distance.y(), target);
+}
+
+TEST_F(SyntheticGestureControllerTest, MultiScrollGestureTouch) {
+ CreateControllerAndTarget<MockScrollTouchTarget>();
+
+ SyntheticSmoothScrollGestureParams params;
+ params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
+ params.anchor.SetPoint(8, -13);
+ params.distances.push_back(gfx::Vector2d(234, 133));
+ params.distances.push_back(gfx::Vector2d(-9, 78));
+
+ scoped_ptr<SyntheticSmoothScrollGesture> gesture(
+ new SyntheticSmoothScrollGesture(params));
+ QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
+ FlushInputUntilComplete();
+
+ MockScrollGestureTarget* scroll_target =
+ static_cast<MockScrollGestureTarget*>(target_);
+ EXPECT_EQ(1, num_success_);
+ EXPECT_EQ(0, num_failure_);
+ CheckMultiScrollDistanceIsWithinRange(
+ scroll_target->start_to_end_distance(),
+ params.distances[0] + params.distances[1],
+ target_);
+}
+
+TEST_F(SyntheticGestureControllerTest, MultiScrollGestureTouchVertical) {
+ CreateControllerAndTarget<MockScrollTouchTarget>();
+
+ SyntheticSmoothScrollGestureParams params;
+ params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
+ params.anchor.SetPoint(234, -13);
+ params.distances.push_back(gfx::Vector2d(0, 133));
+ params.distances.push_back(gfx::Vector2d(0, 78));
scoped_ptr<SyntheticSmoothScrollGesture> gesture(
new SyntheticSmoothScrollGesture(params));
- controller_->QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
+ QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
FlushInputUntilComplete();
- MockSyntheticSmoothScrollGestureTarget* smooth_scroll_target =
- static_cast<MockSyntheticSmoothScrollGestureTarget*>(target_);
- EXPECT_EQ(1, target_->num_success());
- EXPECT_EQ(0, target_->num_failure());
- EXPECT_EQ(params.distance, smooth_scroll_target->scroll_distance());
+ MockScrollGestureTarget* scroll_target =
+ static_cast<MockScrollGestureTarget*>(target_);
+ EXPECT_EQ(1, num_success_);
+ EXPECT_EQ(0, num_failure_);
+ // TODO(dominikg): Remove adjustment when crbug.com/332418 is fixed.
+ EXPECT_FLOAT_EQ(
+ params.distances[0].Length() + params.distances[1].Length() +
+ target_->GetTouchSlopInDips(),
+ scroll_target->total_abs_scroll_distance_length() - 0.001f);
+ EXPECT_EQ(AddTouchSlopToVector(params.distances[0] + params.distances[1],
+ target_),
+ scroll_target->start_to_end_distance() - gfx::Vector2dF(0, 0.001f));
}
TEST_F(SyntheticGestureControllerTest, PinchGestureTouchZoomIn) {
@@ -703,22 +903,20 @@ TEST_F(SyntheticGestureControllerTest, PinchGestureTouchZoomIn) {
SyntheticPinchGestureParams params;
params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
- params.zoom_in = true;
- params.total_num_pixels_covered = 345;
+ params.scale_factor = 2.3f;
params.anchor.SetPoint(54, 89);
scoped_ptr<SyntheticPinchGesture> gesture(new SyntheticPinchGesture(params));
- controller_->QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
+ QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
FlushInputUntilComplete();
MockSyntheticPinchTouchTarget* pinch_target =
static_cast<MockSyntheticPinchTouchTarget*>(target_);
- EXPECT_EQ(1, target_->num_success());
- EXPECT_EQ(0, target_->num_failure());
+ EXPECT_EQ(1, num_success_);
+ EXPECT_EQ(0, num_failure_);
EXPECT_EQ(pinch_target->zoom_direction(),
MockSyntheticPinchTouchTarget::ZOOM_IN);
- EXPECT_EQ(params.total_num_pixels_covered + 2 * target_->GetTouchSlopInDips(),
- pinch_target->total_num_pixels_covered());
+ EXPECT_FLOAT_EQ(params.scale_factor, pinch_target->ComputeScaleFactor());
}
TEST_F(SyntheticGestureControllerTest, PinchGestureTouchZoomOut) {
@@ -726,43 +924,40 @@ TEST_F(SyntheticGestureControllerTest, PinchGestureTouchZoomOut) {
SyntheticPinchGestureParams params;
params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
- params.zoom_in = false;
- params.total_num_pixels_covered = 456;
+ params.scale_factor = 0.4f;
params.anchor.SetPoint(-12, 93);
scoped_ptr<SyntheticPinchGesture> gesture(new SyntheticPinchGesture(params));
- controller_->QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
+ QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
FlushInputUntilComplete();
MockSyntheticPinchTouchTarget* pinch_target =
static_cast<MockSyntheticPinchTouchTarget*>(target_);
- EXPECT_EQ(1, target_->num_success());
- EXPECT_EQ(0, target_->num_failure());
+ EXPECT_EQ(1, num_success_);
+ EXPECT_EQ(0, num_failure_);
EXPECT_EQ(pinch_target->zoom_direction(),
MockSyntheticPinchTouchTarget::ZOOM_OUT);
- EXPECT_EQ(params.total_num_pixels_covered + 2 * target_->GetTouchSlopInDips(),
- pinch_target->total_num_pixels_covered());
+ EXPECT_FLOAT_EQ(params.scale_factor, pinch_target->ComputeScaleFactor());
}
-TEST_F(SyntheticGestureControllerTest, PinchGestureTouchZeroPixelsCovered) {
+TEST_F(SyntheticGestureControllerTest, PinchGestureTouchNoScaling) {
CreateControllerAndTarget<MockSyntheticPinchTouchTarget>();
SyntheticPinchGestureParams params;
params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
- params.zoom_in = true;
- params.total_num_pixels_covered = 0;
+ params.scale_factor = 1.0f;
scoped_ptr<SyntheticPinchGesture> gesture(new SyntheticPinchGesture(params));
- controller_->QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
+ QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
FlushInputUntilComplete();
MockSyntheticPinchTouchTarget* pinch_target =
static_cast<MockSyntheticPinchTouchTarget*>(target_);
- EXPECT_EQ(1, target_->num_success());
- EXPECT_EQ(0, target_->num_failure());
+ EXPECT_EQ(1, num_success_);
+ EXPECT_EQ(0, num_failure_);
EXPECT_EQ(pinch_target->zoom_direction(),
MockSyntheticPinchTouchTarget::ZOOM_DIRECTION_UNKNOWN);
- EXPECT_EQ(0, pinch_target->total_num_pixels_covered());
+ EXPECT_EQ(params.scale_factor, pinch_target->ComputeScaleFactor());
}
TEST_F(SyntheticGestureControllerTest, TapGestureTouch) {
@@ -774,15 +969,16 @@ TEST_F(SyntheticGestureControllerTest, TapGestureTouch) {
params.position.SetPoint(87, -124);
scoped_ptr<SyntheticTapGesture> gesture(new SyntheticTapGesture(params));
- controller_->QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
+ QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
FlushInputUntilComplete();
MockSyntheticTapTouchTarget* tap_target =
static_cast<MockSyntheticTapTouchTarget*>(target_);
- EXPECT_EQ(1, target_->num_success());
- EXPECT_EQ(0, target_->num_failure());
+ EXPECT_EQ(1, num_success_);
+ EXPECT_EQ(0, num_failure_);
EXPECT_TRUE(tap_target->GestureFinished());
EXPECT_EQ(tap_target->position(), params.position);
+ EXPECT_EQ(tap_target->GetDuration().InMilliseconds(), params.duration_ms);
EXPECT_GE(GetTotalTime(),
base::TimeDelta::FromMilliseconds(params.duration_ms));
}
@@ -796,15 +992,16 @@ TEST_F(SyntheticGestureControllerTest, TapGestureMouse) {
params.position.SetPoint(98, 123);
scoped_ptr<SyntheticTapGesture> gesture(new SyntheticTapGesture(params));
- controller_->QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
+ QueueSyntheticGesture(gesture.PassAs<SyntheticGesture>());
FlushInputUntilComplete();
MockSyntheticTapMouseTarget* tap_target =
static_cast<MockSyntheticTapMouseTarget*>(target_);
- EXPECT_EQ(1, target_->num_success());
- EXPECT_EQ(0, target_->num_failure());
+ EXPECT_EQ(1, num_success_);
+ EXPECT_EQ(0, num_failure_);
EXPECT_TRUE(tap_target->GestureFinished());
EXPECT_EQ(tap_target->position(), params.position);
+ EXPECT_EQ(tap_target->GetDuration().InMilliseconds(), params.duration_ms);
EXPECT_GE(GetTotalTime(),
base::TimeDelta::FromMilliseconds(params.duration_ms));
}
diff --git a/chromium/content/browser/renderer_host/input/synthetic_gesture_target.h b/chromium/content/browser/renderer_host/input/synthetic_gesture_target.h
index 01487fc2772..14724bf67f9 100644
--- a/chromium/content/browser/renderer_host/input/synthetic_gesture_target.h
+++ b/chromium/content/browser/renderer_host/input/synthetic_gesture_target.h
@@ -10,9 +10,11 @@
#include "content/common/content_export.h"
#include "content/common/input/synthetic_gesture_params.h"
-namespace content {
+namespace blink {
+class WebInputEvent;
+}
-class InputEvent;
+namespace content {
// Interface between the synthetic gesture controller and the RenderWidgetHost.
class CONTENT_EXPORT SyntheticGestureTarget {
@@ -23,11 +25,8 @@ class CONTENT_EXPORT SyntheticGestureTarget {
// Allows synthetic gestures to insert input events in the highest level of
// input processing on the target platform (e.g. Java on Android), so that
// the event traverses the entire input processing stack.
- virtual void DispatchInputEventToPlatform(const InputEvent& event) = 0;
-
- // Called by SyntheticGestureController when a gesture has finished.
- virtual void OnSyntheticGestureCompleted(
- SyntheticGesture::Result result) = 0;
+ virtual void DispatchInputEventToPlatform(
+ const blink::WebInputEvent& event) = 0;
// Called by SyntheticGestureController to request a flush at a time
// appropriate for the platform, e.g. aligned with vsync.
@@ -37,17 +36,17 @@ class CONTENT_EXPORT SyntheticGestureTarget {
virtual SyntheticGestureParams::GestureSourceType
GetDefaultSyntheticGestureSourceType() const = 0;
- // Check if a particular gesture type is supported by the target.
- virtual bool SupportsSyntheticGestureSourceType(
- SyntheticGestureParams::GestureSourceType gesture_source_type) const = 0;
-
// After how much time of inaction does the target assume that a pointer has
// stopped moving.
virtual base::TimeDelta PointerAssumedStoppedTime() const = 0;
// Returns the maximum number of DIPs a touch pointer can move without being
// considered moving by the platform.
- virtual int GetTouchSlopInDips() const = 0;
+ virtual float GetTouchSlopInDips() const = 0;
+
+ // Returns the minimum number of DIPs two touch pointers have to be apart
+ // to perform a pinch-zoom.
+ virtual float GetMinScalingSpanInDips() const = 0;
};
} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/synthetic_gesture_target_android.cc b/chromium/content/browser/renderer_host/input/synthetic_gesture_target_android.cc
index cf5181ab93c..a1e5dea6b77 100644
--- a/chromium/content/browser/renderer_host/input/synthetic_gesture_target_android.cc
+++ b/chromium/content/browser/renderer_host/input/synthetic_gesture_target_android.cc
@@ -32,14 +32,17 @@ bool SyntheticGestureTargetAndroid::RegisterTouchEventSynthesizer(JNIEnv* env) {
void SyntheticGestureTargetAndroid::TouchSetPointer(
JNIEnv* env, int index, int x, int y, int id) {
+ TRACE_EVENT0("input", "SyntheticGestureTargetAndroid::TouchSetPointer");
Java_TouchEventSynthesizer_setPointer(env, touch_event_synthesizer_.obj(),
index, x, y, id);
}
void SyntheticGestureTargetAndroid::TouchInject(
- JNIEnv* env, Action action, int pointer_count) {
+ JNIEnv* env, Action action, int pointer_count, int64 time_in_ms) {
+ TRACE_EVENT0("input", "SyntheticGestureTargetAndroid::TouchInject");
Java_TouchEventSynthesizer_inject(env, touch_event_synthesizer_.obj(),
- static_cast<int>(action), pointer_count);
+ static_cast<int>(action), pointer_count,
+ time_in_ms);
}
void SyntheticGestureTargetAndroid::DispatchWebTouchEventToPlatform(
@@ -70,7 +73,18 @@ void SyntheticGestureTargetAndroid::DispatchWebTouchEventToPlatform(
TouchSetPointer(env, i, point->position.x, point->position.y, point->id);
}
- TouchInject(env, action, num_touches);
+ TouchInject(env, action, num_touches,
+ static_cast<int64>(web_touch.timeStampSeconds * 1000.0));
+}
+
+void SyntheticGestureTargetAndroid::DispatchWebMouseWheelEventToPlatform(
+ const blink::WebMouseWheelEvent& web_wheel, const ui::LatencyInfo&) {
+ CHECK(false);
+}
+
+void SyntheticGestureTargetAndroid::DispatchWebMouseEventToPlatform(
+ const blink::WebMouseEvent& web_mouse, const ui::LatencyInfo&) {
+ CHECK(false);
}
SyntheticGestureParams::GestureSourceType
@@ -78,15 +92,17 @@ SyntheticGestureTargetAndroid::GetDefaultSyntheticGestureSourceType() const {
return SyntheticGestureParams::TOUCH_INPUT;
}
-bool SyntheticGestureTargetAndroid::SupportsSyntheticGestureSourceType(
- SyntheticGestureParams::GestureSourceType gesture_source_type) const {
- return gesture_source_type == SyntheticGestureParams::TOUCH_INPUT;
+float SyntheticGestureTargetAndroid::GetTouchSlopInDips() const {
+ float device_scale_factor =
+ gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().device_scale_factor();
+ return gfx::ViewConfiguration::GetTouchSlopInPixels() / device_scale_factor;
}
-int SyntheticGestureTargetAndroid::GetTouchSlopInDips() const {
+float SyntheticGestureTargetAndroid::GetMinScalingSpanInDips() const {
float device_scale_factor =
gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().device_scale_factor();
- return gfx::ViewConfiguration::GetTouchSlopInPixels() / device_scale_factor;
+ return
+ gfx::ViewConfiguration::GetMinScalingSpanInPixels() / device_scale_factor;
}
} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/synthetic_gesture_target_android.h b/chromium/content/browser/renderer_host/input/synthetic_gesture_target_android.h
index b8c5a5d8142..dcf146ffb92 100644
--- a/chromium/content/browser/renderer_host/input/synthetic_gesture_target_android.h
+++ b/chromium/content/browser/renderer_host/input/synthetic_gesture_target_android.h
@@ -22,18 +22,24 @@ class SyntheticGestureTargetAndroid : public SyntheticGestureTargetBase {
static bool RegisterTouchEventSynthesizer(JNIEnv* env);
+ // SyntheticGestureTargetBase:
virtual void DispatchWebTouchEventToPlatform(
const blink::WebTouchEvent& web_touch,
const ui::LatencyInfo& latency_info) OVERRIDE;
+ virtual void DispatchWebMouseWheelEventToPlatform(
+ const blink::WebMouseWheelEvent& web_wheel,
+ const ui::LatencyInfo& latency_info) OVERRIDE;
+ virtual void DispatchWebMouseEventToPlatform(
+ const blink::WebMouseEvent& web_mouse,
+ const ui::LatencyInfo& latency_info) OVERRIDE;
// SyntheticGestureTarget:
virtual SyntheticGestureParams::GestureSourceType
GetDefaultSyntheticGestureSourceType() const OVERRIDE;
- virtual bool SupportsSyntheticGestureSourceType(
- SyntheticGestureParams::GestureSourceType gesture_source_type) const
- OVERRIDE;
- virtual int GetTouchSlopInDips() const OVERRIDE;
+ virtual float GetTouchSlopInDips() const OVERRIDE;
+
+ virtual float GetMinScalingSpanInDips() const OVERRIDE;
private:
// Enum values below need to be kept in sync with TouchEventSynthesizer.java
@@ -46,7 +52,8 @@ class SyntheticGestureTargetAndroid : public SyntheticGestureTargetBase {
};
void TouchSetPointer(JNIEnv* env, int index, int x, int y, int id);
- void TouchInject(JNIEnv* env, Action action, int pointer_count);
+ void TouchInject(
+ JNIEnv* env, Action action, int pointer_count, int64 time_in_ms);
base::android::ScopedJavaGlobalRef<jobject> touch_event_synthesizer_;
diff --git a/chromium/content/browser/renderer_host/input/synthetic_gesture_target_aura.cc b/chromium/content/browser/renderer_host/input/synthetic_gesture_target_aura.cc
index f9ab85895c7..0b4d9c8c97f 100644
--- a/chromium/content/browser/renderer_host/input/synthetic_gesture_target_aura.cc
+++ b/chromium/content/browser/renderer_host/input/synthetic_gesture_target_aura.cc
@@ -7,10 +7,9 @@
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_view_aura.h"
#include "content/browser/renderer_host/ui_events_helper.h"
-#include "content/common/input/input_event.h"
-#include "ui/aura/client/screen_position_client.h"
-#include "ui/aura/root_window.h"
#include "ui/aura/window.h"
+#include "ui/aura/window_tree_host.h"
+#include "ui/events/event_processor.h"
#include "ui/events/gestures/gesture_configuration.h"
using blink::WebTouchEvent;
@@ -26,35 +25,21 @@ SyntheticGestureTargetAura::SyntheticGestureTargetAura(
void SyntheticGestureTargetAura::DispatchWebTouchEventToPlatform(
const WebTouchEvent& web_touch,
const ui::LatencyInfo& latency_info) {
- aura::Window* window = render_widget_host()->GetView()->GetNativeView();
- aura::Window* root_window = window->GetRootWindow();
- aura::client::ScreenPositionClient* position_client =
- aura::client::GetScreenPositionClient(root_window);
- DCHECK(position_client);
-
TouchEventWithLatencyInfo touch_with_latency(web_touch, latency_info);
-
- // SyntheticGesture may skip calculating screenPosition, so we will fill it
- // in here. "screenPosition" is converted from "position".
- const size_t num_touches = touch_with_latency.event.touchesLength;
- for (size_t i = 0; i < num_touches; ++ i) {
- blink::WebTouchPoint* point = &touch_with_latency.event.touches[i];
- gfx::Point position(point->position.x, point->position.y);
- position_client->ConvertPointToScreen(window, &position);
- point->screenPosition.x = position.x();
- point->screenPosition.y = position.y();
- }
-
ScopedVector<ui::TouchEvent> events;
bool conversion_success = MakeUITouchEventsFromWebTouchEvents(
- touch_with_latency, &events, SCREEN_COORDINATES);
+ touch_with_latency, &events, LOCAL_COORDINATES);
DCHECK(conversion_success);
- aura::RootWindowHostDelegate* root_window_host_delegate =
- GetRootWindowHostDelegate();
+ aura::Window* window = GetWindow();
+ aura::WindowTreeHost* host = window->GetHost();
for (ScopedVector<ui::TouchEvent>::iterator iter = events.begin(),
end = events.end(); iter != end; ++iter) {
- root_window_host_delegate->OnHostTouchEvent(*iter);
+ (*iter)->ConvertLocationToTarget(window, host->window());
+ ui::EventDispatchDetails details =
+ host->event_processor()->OnEventFromSource(*iter);
+ if (details.dispatcher_destroyed)
+ break;
}
}
@@ -63,11 +48,16 @@ void SyntheticGestureTargetAura::DispatchWebMouseWheelEventToPlatform(
const ui::LatencyInfo&) {
gfx::Point location(web_wheel.x, web_wheel.y);
ui::MouseEvent mouse_event(
- ui::ET_MOUSEWHEEL, location, location, ui::EF_NONE);
+ ui::ET_MOUSEWHEEL, location, location, ui::EF_NONE, ui::EF_NONE);
ui::MouseWheelEvent wheel_event(
mouse_event, web_wheel.deltaX, web_wheel.deltaY);
- GetRootWindowHostDelegate()->OnHostMouseEvent(&wheel_event);
+ aura::Window* window = GetWindow();
+ wheel_event.ConvertLocationToTarget(window, window->GetRootWindow());
+ ui::EventDispatchDetails details =
+ window->GetHost()->event_processor()->OnEventFromSource(&wheel_event);
+ if (details.dispatcher_destroyed)
+ return;
}
namespace {
@@ -127,37 +117,35 @@ void SyntheticGestureTargetAura::DispatchWebMouseEventToPlatform(
gfx::Point location(web_mouse.x, web_mouse.y);
ui::EventType event_type = WebMouseEventTypeToEventType(web_mouse.type);
int flags = WebMouseEventButtonToFlags(web_mouse.button);
- ui::MouseEvent mouse_event(event_type, location, location, flags);
-
- GetRootWindowHostDelegate()->OnHostMouseEvent(&mouse_event);
+ ui::MouseEvent mouse_event(event_type, location, location, flags, flags);
+
+ aura::Window* window = GetWindow();
+ mouse_event.ConvertLocationToTarget(window, window->GetRootWindow());
+ ui::EventDispatchDetails details =
+ window->GetHost()->event_processor()->OnEventFromSource(&mouse_event);
+ if (details.dispatcher_destroyed)
+ return;
}
SyntheticGestureParams::GestureSourceType
SyntheticGestureTargetAura::GetDefaultSyntheticGestureSourceType() const {
- return SyntheticGestureParams::MOUSE_INPUT;
+ return SyntheticGestureParams::TOUCH_INPUT;
}
-bool SyntheticGestureTargetAura::SupportsSyntheticGestureSourceType(
- SyntheticGestureParams::GestureSourceType gesture_source_type) const {
- return gesture_source_type == SyntheticGestureParams::TOUCH_INPUT ||
- gesture_source_type == SyntheticGestureParams::MOUSE_INPUT;
-}
-
-int SyntheticGestureTargetAura::GetTouchSlopInDips() const {
+float SyntheticGestureTargetAura::GetTouchSlopInDips() const {
// - 1 because Aura considers a pointer to be moving if it has moved at least
// 'max_touch_move_in_pixels_for_click' pixels.
return ui::GestureConfiguration::max_touch_move_in_pixels_for_click() - 1;
}
-aura::RootWindowHostDelegate*
-SyntheticGestureTargetAura::GetRootWindowHostDelegate() const {
- aura::Window* window = render_widget_host()->GetView()->GetNativeView();
- aura::Window* root_window = window->GetRootWindow();
- aura::RootWindowHostDelegate* root_window_host_delegate =
- root_window->GetDispatcher()->AsRootWindowHostDelegate();
- DCHECK(root_window_host_delegate);
- return root_window_host_delegate;
+float SyntheticGestureTargetAura::GetMinScalingSpanInDips() const {
+ return ui::GestureConfiguration::min_distance_for_pinch_scroll_in_pixels();
}
+aura::Window* SyntheticGestureTargetAura::GetWindow() const {
+ aura::Window* window = render_widget_host()->GetView()->GetNativeView();
+ DCHECK(window);
+ return window;
+}
} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/synthetic_gesture_target_aura.h b/chromium/content/browser/renderer_host/input/synthetic_gesture_target_aura.h
index 9befda688f4..f3634d61162 100644
--- a/chromium/content/browser/renderer_host/input/synthetic_gesture_target_aura.h
+++ b/chromium/content/browser/renderer_host/input/synthetic_gesture_target_aura.h
@@ -10,13 +10,16 @@
#include "content/common/input/synthetic_gesture_params.h"
namespace aura {
-class RootWindowHostDelegate;
+class Window;
+class WindowEventDispatcher;
+
+namespace client {
+class ScreenPositionClient;
}
+} // namespace aura
namespace content {
-class InputEvent;
-
// SyntheticGestureTarget implementation for aura
class SyntheticGestureTargetAura : public SyntheticGestureTargetBase {
public:
@@ -36,14 +39,13 @@ class SyntheticGestureTargetAura : public SyntheticGestureTargetBase {
// SyntheticGestureTarget:
virtual SyntheticGestureParams::GestureSourceType
GetDefaultSyntheticGestureSourceType() const OVERRIDE;
- virtual bool SupportsSyntheticGestureSourceType(
- SyntheticGestureParams::GestureSourceType gesture_source_type) const
- OVERRIDE;
- virtual int GetTouchSlopInDips() const OVERRIDE;
+ virtual float GetTouchSlopInDips() const OVERRIDE;
+
+ virtual float GetMinScalingSpanInDips() const OVERRIDE;
private:
- aura::RootWindowHostDelegate* GetRootWindowHostDelegate() const;
+ aura::Window* GetWindow() const;
DISALLOW_COPY_AND_ASSIGN(SyntheticGestureTargetAura);
};
diff --git a/chromium/content/browser/renderer_host/input/synthetic_gesture_target_base.cc b/chromium/content/browser/renderer_host/input/synthetic_gesture_target_base.cc
index 0938eebb649..e1fd3b06f31 100644
--- a/chromium/content/browser/renderer_host/input/synthetic_gesture_target_base.cc
+++ b/chromium/content/browser/renderer_host/input/synthetic_gesture_target_base.cc
@@ -7,7 +7,6 @@
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_view_base.h"
#include "content/browser/renderer_host/ui_events_helper.h"
-#include "content/common/input/input_event.h"
#include "content/common/input_messages.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
#include "ui/events/event.h"
@@ -15,6 +14,7 @@
using blink::WebInputEvent;
using blink::WebTouchEvent;
+using blink::WebTouchPoint;
using blink::WebMouseEvent;
using blink::WebMouseWheelEvent;
@@ -22,13 +22,13 @@ namespace content {
namespace {
// This value was determined experimentally. It was sufficient to not cause a
-// fling on Android.
-const int kPointerAssumedStoppedTimeMs = 50;
+// fling on Android and Aura.
+const int kPointerAssumedStoppedTimeMs = 100;
// SyntheticGestureTargetBase passes input events straight on to the renderer
// without going through a gesture recognition framework. There is thus no touch
// slop.
-const int kTouchSlopInDips = 0;
+const float kTouchSlopInDips = 0.0f;
} // namespace
@@ -42,29 +42,41 @@ SyntheticGestureTargetBase::~SyntheticGestureTargetBase() {
}
void SyntheticGestureTargetBase::DispatchInputEventToPlatform(
- const InputEvent& event) {
- const WebInputEvent* web_event = event.web_event.get();
- if (WebInputEvent::isTouchEventType(web_event->type)) {
- DCHECK(SupportsSyntheticGestureSourceType(
- SyntheticGestureParams::TOUCH_INPUT));
-
- const WebTouchEvent* web_touch =
- static_cast<const WebTouchEvent*>(web_event);
- DispatchWebTouchEventToPlatform(*web_touch, event.latency_info);
- } else if (web_event->type == WebInputEvent::MouseWheel) {
- DCHECK(SupportsSyntheticGestureSourceType(
- SyntheticGestureParams::MOUSE_INPUT));
-
- const WebMouseWheelEvent* web_wheel =
- static_cast<const WebMouseWheelEvent*>(web_event);
- DispatchWebMouseWheelEventToPlatform(*web_wheel, event.latency_info);
- } else if (WebInputEvent::isMouseEventType(web_event->type)) {
- DCHECK(SupportsSyntheticGestureSourceType(
- SyntheticGestureParams::MOUSE_INPUT));
-
- const WebMouseEvent* web_mouse =
- static_cast<const WebMouseEvent*>(web_event);
- DispatchWebMouseEventToPlatform(*web_mouse, event.latency_info);
+ const WebInputEvent& event) {
+ TRACE_EVENT1("input",
+ "SyntheticGestureTarget::DispatchInputEventToPlatform",
+ "type", WebInputEventTraits::GetName(event.type));
+
+ ui::LatencyInfo latency_info;
+ latency_info.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_UI_COMPONENT, 0, 0);
+
+ if (WebInputEvent::isTouchEventType(event.type)) {
+ const WebTouchEvent& web_touch =
+ static_cast<const WebTouchEvent&>(event);
+
+ // Check that all touch pointers are within the content bounds.
+ if (web_touch.type == WebInputEvent::TouchStart) {
+ for (unsigned i = 0; i < web_touch.touchesLength; i++)
+ CHECK(web_touch.touches[i].state != WebTouchPoint::StatePressed ||
+ PointIsWithinContents(web_touch.touches[i].position.x,
+ web_touch.touches[i].position.y))
+ << "Touch coordinates are not within content bounds on TouchStart.";
+ }
+
+ DispatchWebTouchEventToPlatform(web_touch, latency_info);
+ } else if (event.type == WebInputEvent::MouseWheel) {
+ const WebMouseWheelEvent& web_wheel =
+ static_cast<const WebMouseWheelEvent&>(event);
+ CHECK(PointIsWithinContents(web_wheel.x, web_wheel.y))
+ << "Mouse wheel position is not within content bounds.";
+ DispatchWebMouseWheelEventToPlatform(web_wheel, latency_info);
+ } else if (WebInputEvent::isMouseEventType(event.type)) {
+ const WebMouseEvent& web_mouse =
+ static_cast<const WebMouseEvent&>(event);
+ CHECK(event.type != WebInputEvent::MouseDown ||
+ PointIsWithinContents(web_mouse.x, web_mouse.y))
+ << "Mouse pointer is not within content bounds on MouseDown.";
+ DispatchWebMouseEventToPlatform(web_mouse, latency_info);
} else {
NOTREACHED();
}
@@ -73,26 +85,22 @@ void SyntheticGestureTargetBase::DispatchInputEventToPlatform(
void SyntheticGestureTargetBase::DispatchWebTouchEventToPlatform(
const blink::WebTouchEvent& web_touch,
const ui::LatencyInfo& latency_info) {
- host_->ForwardTouchEventWithLatencyInfo(web_touch, latency_info);
+ // We assume that platforms supporting touch have their own implementation of
+ // SyntheticGestureTarget to route the events through their respective input
+ // stack.
+ CHECK(false) << "Touch events not supported for this browser.";
}
void SyntheticGestureTargetBase::DispatchWebMouseWheelEventToPlatform(
const blink::WebMouseWheelEvent& web_wheel,
const ui::LatencyInfo& latency_info) {
- host_->ForwardWheelEventWithLatencyInfo(
- MouseWheelEventWithLatencyInfo(web_wheel, latency_info));
+ host_->ForwardWheelEventWithLatencyInfo(web_wheel, latency_info);
}
void SyntheticGestureTargetBase::DispatchWebMouseEventToPlatform(
const blink::WebMouseEvent& web_mouse,
const ui::LatencyInfo& latency_info) {
- host_->ForwardMouseEventWithLatencyInfo(
- MouseEventWithLatencyInfo(web_mouse, latency_info));
-}
-
-void SyntheticGestureTargetBase::OnSyntheticGestureCompleted(
- SyntheticGesture::Result result) {
- host_->Send(new InputMsg_SyntheticGestureCompleted(host_->GetRoutingID()));
+ host_->ForwardMouseEventWithLatencyInfo(web_mouse, latency_info);
}
void SyntheticGestureTargetBase::SetNeedsFlush() {
@@ -104,19 +112,26 @@ SyntheticGestureTargetBase::GetDefaultSyntheticGestureSourceType() const {
return SyntheticGestureParams::MOUSE_INPUT;
}
-bool SyntheticGestureTargetBase::SupportsSyntheticGestureSourceType(
- SyntheticGestureParams::GestureSourceType gesture_source_type) const {
- return gesture_source_type == SyntheticGestureParams::MOUSE_INPUT ||
- gesture_source_type == SyntheticGestureParams::TOUCH_INPUT;
-}
-
base::TimeDelta SyntheticGestureTargetBase::PointerAssumedStoppedTime()
const {
return base::TimeDelta::FromMilliseconds(kPointerAssumedStoppedTimeMs);
}
-int SyntheticGestureTargetBase::GetTouchSlopInDips() const {
+float SyntheticGestureTargetBase::GetTouchSlopInDips() const {
return kTouchSlopInDips;
}
+float SyntheticGestureTargetBase::GetMinScalingSpanInDips() const {
+ // The minimum scaling distance is only relevant for touch gestures and the
+ // base target doesn't support touch.
+ NOTREACHED();
+ return 0.0f;
+}
+
+bool SyntheticGestureTargetBase::PointIsWithinContents(int x, int y) const {
+ gfx::Rect bounds = host_->GetView()->GetViewBounds();
+ bounds -= bounds.OffsetFromOrigin(); // Translate the bounds to (0,0).
+ return bounds.Contains(x, y);
+}
+
} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/synthetic_gesture_target_base.h b/chromium/content/browser/renderer_host/input/synthetic_gesture_target_base.h
index efb8d76b57e..a66a8d34cae 100644
--- a/chromium/content/browser/renderer_host/input/synthetic_gesture_target_base.h
+++ b/chromium/content/browser/renderer_host/input/synthetic_gesture_target_base.h
@@ -40,27 +40,26 @@ class SyntheticGestureTargetBase : public SyntheticGestureTarget {
const ui::LatencyInfo& latency_info);
// SyntheticGestureTarget:
- virtual void DispatchInputEventToPlatform(const InputEvent& event) OVERRIDE;
-
- virtual void OnSyntheticGestureCompleted(
- SyntheticGesture::Result result) OVERRIDE;
+ virtual void DispatchInputEventToPlatform(
+ const blink::WebInputEvent& event) OVERRIDE;
virtual void SetNeedsFlush() OVERRIDE;
virtual SyntheticGestureParams::GestureSourceType
GetDefaultSyntheticGestureSourceType() const OVERRIDE;
- virtual bool SupportsSyntheticGestureSourceType(
- SyntheticGestureParams::GestureSourceType gesture_source_type) const
- OVERRIDE;
virtual base::TimeDelta PointerAssumedStoppedTime() const OVERRIDE;
- virtual int GetTouchSlopInDips() const OVERRIDE;
+ virtual float GetTouchSlopInDips() const OVERRIDE;
+
+ virtual float GetMinScalingSpanInDips() const OVERRIDE;
protected:
RenderWidgetHostImpl* render_widget_host() const { return host_; }
private:
+ bool PointIsWithinContents(int x, int y) const;
+
RenderWidgetHostImpl* host_;
DISALLOW_COPY_AND_ASSIGN(SyntheticGestureTargetBase);
diff --git a/chromium/content/browser/renderer_host/input/synthetic_pinch_gesture.cc b/chromium/content/browser/renderer_host/input/synthetic_pinch_gesture.cc
index dfe5cf474e7..53e15d78fda 100644
--- a/chromium/content/browser/renderer_host/input/synthetic_pinch_gesture.cc
+++ b/chromium/content/browser/renderer_host/input/synthetic_pinch_gesture.cc
@@ -7,7 +7,6 @@
#include <cmath>
#include "base/logging.h"
-#include "content/common/input/input_event.h"
#include "ui/events/latency_info.h"
namespace content {
@@ -15,33 +14,30 @@ namespace content {
SyntheticPinchGesture::SyntheticPinchGesture(
const SyntheticPinchGestureParams& params)
: params_(params),
- current_y_0_(0.0f),
- current_y_1_(0.0f),
- target_y_0_(0.0f),
- target_y_1_(0.0f),
+ start_y_0_(0.0f),
+ start_y_1_(0.0f),
+ max_pointer_delta_0_(0.0f),
gesture_source_type_(SyntheticGestureParams::DEFAULT_INPUT),
state_(SETUP) {
- DCHECK_GE(params_.total_num_pixels_covered, 0);
+ DCHECK_GT(params_.scale_factor, 0.0f);
}
SyntheticPinchGesture::~SyntheticPinchGesture() {}
SyntheticGesture::Result SyntheticPinchGesture::ForwardInputEvents(
- const base::TimeDelta& interval, SyntheticGestureTarget* target) {
+ const base::TimeTicks& timestamp, SyntheticGestureTarget* target) {
if (state_ == SETUP) {
gesture_source_type_ = params_.gesture_source_type;
if (gesture_source_type_ == SyntheticGestureParams::DEFAULT_INPUT)
gesture_source_type_ = target->GetDefaultSyntheticGestureSourceType();
- if (!target->SupportsSyntheticGestureSourceType(gesture_source_type_))
- return SyntheticGesture::GESTURE_SOURCE_TYPE_NOT_SUPPORTED_BY_PLATFORM;
-
state_ = STARTED;
+ start_time_ = timestamp;
}
DCHECK_NE(gesture_source_type_, SyntheticGestureParams::DEFAULT_INPUT);
if (gesture_source_type_ == SyntheticGestureParams::TOUCH_INPUT)
- ForwardTouchInputEvents(interval, target);
+ ForwardTouchInputEvents(timestamp, target);
else
return SyntheticGesture::GESTURE_SOURCE_TYPE_NOT_IMPLEMENTED;
@@ -50,26 +46,27 @@ SyntheticGesture::Result SyntheticPinchGesture::ForwardInputEvents(
}
void SyntheticPinchGesture::ForwardTouchInputEvents(
- const base::TimeDelta& interval, SyntheticGestureTarget* target) {
+ const base::TimeTicks& timestamp, SyntheticGestureTarget* target) {
switch (state_) {
case STARTED:
// Check for an early finish.
- if (params_.total_num_pixels_covered == 0) {
+ if (params_.scale_factor == 1.0f) {
state_ = DONE;
break;
}
- SetupCoordinates(target);
- PressTouchPoints(target);
+ SetupCoordinatesAndStopTime(target);
+ PressTouchPoints(target, timestamp);
state_ = MOVING;
break;
- case MOVING:
- UpdateTouchPoints(interval);
- MoveTouchPoints(target);
- if (HasReachedTarget()) {
- ReleaseTouchPoints(target);
+ case MOVING: {
+ base::TimeTicks event_timestamp = ClampTimestamp(timestamp);
+ float delta = GetDeltaForPointer0AtTime(event_timestamp);
+ MoveTouchPoints(target, delta, event_timestamp);
+ if (HasReachedTarget(event_timestamp)) {
+ ReleaseTouchPoints(target, event_timestamp);
state_ = DONE;
}
- break;
+ } break;
case SETUP:
NOTREACHED() << "State SETUP invalid for synthetic pinch.";
case DONE:
@@ -77,90 +74,92 @@ void SyntheticPinchGesture::ForwardTouchInputEvents(
}
}
-void SyntheticPinchGesture::UpdateTouchPoints(base::TimeDelta interval) {
- // Compute the delta for the first pointer. The other one moves exactly
- // the same but in the opposite direction.
- float delta = GetDeltaForPointer0(interval);
- current_y_0_ += delta;
- current_y_1_ -= delta;
+void SyntheticPinchGesture::PressTouchPoints(SyntheticGestureTarget* target,
+ const base::TimeTicks& timestamp) {
+ touch_event_.PressPoint(params_.anchor.x(), start_y_0_);
+ touch_event_.PressPoint(params_.anchor.x(), start_y_1_);
+ ForwardTouchEvent(target, timestamp);
}
-void SyntheticPinchGesture::PressTouchPoints(SyntheticGestureTarget* target) {
- touch_event_.PressPoint(params_.anchor.x(), current_y_0_);
- touch_event_.PressPoint(params_.anchor.x(), current_y_1_);
- ForwardTouchEvent(target);
-}
+void SyntheticPinchGesture::MoveTouchPoints(SyntheticGestureTarget* target,
+ float delta,
+ const base::TimeTicks& timestamp) {
+ // The two pointers move in opposite directions.
+ float current_y_0 = start_y_0_ + delta;
+ float current_y_1 = start_y_1_ - delta;
-void SyntheticPinchGesture::MoveTouchPoints(SyntheticGestureTarget* target) {
- // The current pointer positions are stored as float but the pointer
- // coordinates of the input event are integers. Floor both positions so that
- // in case of an odd distance one of the pointers (the one whose position goes
- // down) moves one pixel further than the other. The explicit flooring is only
- // needed for negative values.
- touch_event_.MovePoint(0, params_.anchor.x(), floor(current_y_0_));
- touch_event_.MovePoint(1, params_.anchor.x(), floor(current_y_1_));
- ForwardTouchEvent(target);
+ touch_event_.MovePoint(0, params_.anchor.x(), current_y_0);
+ touch_event_.MovePoint(1, params_.anchor.x(), current_y_1);
+ ForwardTouchEvent(target, timestamp);
}
-void SyntheticPinchGesture::ReleaseTouchPoints(SyntheticGestureTarget* target) {
+void SyntheticPinchGesture::ReleaseTouchPoints(
+ SyntheticGestureTarget* target, const base::TimeTicks& timestamp) {
touch_event_.ReleasePoint(0);
touch_event_.ReleasePoint(1);
- ForwardTouchEvent(target);
+ ForwardTouchEvent(target, timestamp);
}
-
-void SyntheticPinchGesture::ForwardTouchEvent(SyntheticGestureTarget* target)
- const {
- target->DispatchInputEventToPlatform(
- InputEvent(touch_event_, ui::LatencyInfo(), false));
+void SyntheticPinchGesture::ForwardTouchEvent(
+ SyntheticGestureTarget* target, const base::TimeTicks& timestamp) {
+ touch_event_.timeStampSeconds = ConvertTimestampToSeconds(timestamp);
+ target->DispatchInputEventToPlatform(touch_event_);
}
-void SyntheticPinchGesture::SetupCoordinates(SyntheticGestureTarget* target) {
- const float kTouchSlopInDips = target->GetTouchSlopInDips();
- float inner_distance_to_anchor = 2 * kTouchSlopInDips;
- float outer_distance_to_anchor = inner_distance_to_anchor +
- params_.total_num_pixels_covered / 2.0f +
- kTouchSlopInDips;
-
- // Move pointers away from each other to zoom in
- // or towards each other to zoom out.
- if (params_.zoom_in) {
- current_y_0_ = params_.anchor.y() - inner_distance_to_anchor;
- current_y_1_ = params_.anchor.y() + inner_distance_to_anchor;
- target_y_0_ = params_.anchor.y() - outer_distance_to_anchor;
- target_y_1_ = params_.anchor.y() + outer_distance_to_anchor;
- } else {
- current_y_0_ = params_.anchor.y() - outer_distance_to_anchor;
- current_y_1_ = params_.anchor.y() + outer_distance_to_anchor;
- target_y_0_ = params_.anchor.y() - inner_distance_to_anchor;
- target_y_1_ = params_.anchor.y() + inner_distance_to_anchor;
+void SyntheticPinchGesture::SetupCoordinatesAndStopTime(
+ SyntheticGestureTarget* target) {
+ // To achieve the specified scaling factor, the ratio of the final to the
+ // initial span (distance between the pointers) has to be equal to the scaling
+ // factor. Since we're moving both pointers at the same speed, each pointer's
+ // distance to the anchor is half the span.
+ float initial_distance_to_anchor, final_distance_to_anchor;
+ if (params_.scale_factor > 1.0f) { // zooming in
+ initial_distance_to_anchor = target->GetMinScalingSpanInDips() / 2.0f;
+ final_distance_to_anchor =
+ (initial_distance_to_anchor + target->GetTouchSlopInDips()) *
+ params_.scale_factor;
+ } else { // zooming out
+ final_distance_to_anchor = target->GetMinScalingSpanInDips() / 2.0f;
+ initial_distance_to_anchor =
+ (final_distance_to_anchor / params_.scale_factor) +
+ target->GetTouchSlopInDips();
}
-}
-float SyntheticPinchGesture::GetDeltaForPointer0(
- const base::TimeDelta& interval) const {
- float total_abs_delta =
- params_.relative_pointer_speed_in_pixels_s * interval.InSecondsF();
+ start_y_0_ = params_.anchor.y() - initial_distance_to_anchor;
+ start_y_1_ = params_.anchor.y() + initial_distance_to_anchor;
- // Make sure we're not moving too far in the final step.
- total_abs_delta =
- std::min(total_abs_delta, ComputeAbsoluteRemainingDistance());
+ max_pointer_delta_0_ = initial_distance_to_anchor - final_distance_to_anchor;
- float abs_delta_pointer_0 = total_abs_delta / 2;
- return params_.zoom_in ? -abs_delta_pointer_0 : abs_delta_pointer_0;
+ int64 total_duration_in_us = static_cast<int64>(
+ 1e6 * (static_cast<double>(std::abs(2 * max_pointer_delta_0_)) /
+ params_.relative_pointer_speed_in_pixels_s));
+ DCHECK_GT(total_duration_in_us, 0);
+ stop_time_ =
+ start_time_ + base::TimeDelta::FromMicroseconds(total_duration_in_us);
}
-float SyntheticPinchGesture::ComputeAbsoluteRemainingDistance() const {
- float distance_0 = params_.zoom_in ? (current_y_0_ - target_y_0_)
- : (target_y_0_ - current_y_0_);
- DCHECK_GE(distance_0, 0);
+float SyntheticPinchGesture::GetDeltaForPointer0AtTime(
+ const base::TimeTicks& timestamp) const {
+ // Make sure the final delta is correct. Using the computation below can lead
+ // to issues with floating point precision.
+ if (HasReachedTarget(timestamp))
+ return max_pointer_delta_0_;
+
+ float total_abs_delta = params_.relative_pointer_speed_in_pixels_s *
+ (timestamp - start_time_).InSecondsF();
+ float abs_delta_pointer_0 = total_abs_delta / 2.0f;
+ return (params_.scale_factor > 1.0f) ? -abs_delta_pointer_0
+ : abs_delta_pointer_0;
+}
- // Both pointers move the same overall distance at the same speed.
- return 2 * distance_0;
+base::TimeTicks SyntheticPinchGesture::ClampTimestamp(
+ const base::TimeTicks& timestamp) const {
+ return std::min(timestamp, stop_time_);
}
-bool SyntheticPinchGesture::HasReachedTarget() const {
- return ComputeAbsoluteRemainingDistance() == 0;
+bool SyntheticPinchGesture::HasReachedTarget(const base::TimeTicks& timestamp)
+ const {
+ return timestamp >= stop_time_;
}
} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/synthetic_pinch_gesture.h b/chromium/content/browser/renderer_host/input/synthetic_pinch_gesture.h
index 97405ce1267..76ee1da7b26 100644
--- a/chromium/content/browser/renderer_host/input/synthetic_pinch_gesture.h
+++ b/chromium/content/browser/renderer_host/input/synthetic_pinch_gesture.h
@@ -5,6 +5,7 @@
#ifndef CONTENT_BROWSER_RENDERER_HOST_INPUT_SYNTHETIC_PINCH_GESTURE_H_
#define CONTENT_BROWSER_RENDERER_HOST_INPUT_SYNTHETIC_PINCH_GESTURE_H_
+#include "base/time/time.h"
#include "content/browser/renderer_host/input/synthetic_gesture.h"
#include "content/browser/renderer_host/input/synthetic_gesture_target.h"
#include "content/common/content_export.h"
@@ -20,7 +21,8 @@ class CONTENT_EXPORT SyntheticPinchGesture : public SyntheticGesture {
virtual ~SyntheticPinchGesture();
virtual SyntheticGesture::Result ForwardInputEvents(
- const base::TimeDelta& interval, SyntheticGestureTarget* target) OVERRIDE;
+ const base::TimeTicks& timestamp,
+ SyntheticGestureTarget* target) OVERRIDE;
private:
enum GestureState {
@@ -30,29 +32,35 @@ class CONTENT_EXPORT SyntheticPinchGesture : public SyntheticGesture {
DONE
};
- void ForwardTouchInputEvents(
- const base::TimeDelta& interval, SyntheticGestureTarget* target);
+ void ForwardTouchInputEvents(const base::TimeTicks& timestamp,
+ SyntheticGestureTarget* target);
- void UpdateTouchPoints(base::TimeDelta interval);
- void PressTouchPoints(SyntheticGestureTarget* target);
- void MoveTouchPoints(SyntheticGestureTarget* target);
- void ReleaseTouchPoints(SyntheticGestureTarget* target);
- void ForwardTouchEvent(SyntheticGestureTarget* target) const;
+ void UpdateTouchPoints(const base::TimeTicks& timestamp);
+ void PressTouchPoints(SyntheticGestureTarget* target,
+ const base::TimeTicks& timestamp);
+ void MoveTouchPoints(SyntheticGestureTarget* target, float delta,
+ const base::TimeTicks& timestamp);
+ void ReleaseTouchPoints(SyntheticGestureTarget* target,
+ const base::TimeTicks& timestamp);
+ void ForwardTouchEvent(SyntheticGestureTarget* target,
+ const base::TimeTicks& timestamp);
- void SetupCoordinates(SyntheticGestureTarget* target);
- float GetDeltaForPointer0(const base::TimeDelta& interval) const;
- float ComputeAbsoluteRemainingDistance() const;
- bool HasReachedTarget() const;
+ void SetupCoordinatesAndStopTime(SyntheticGestureTarget* target);
+ float GetDeltaForPointer0AtTime(const base::TimeTicks& timestamp) const;
+ base::TimeTicks ClampTimestamp(const base::TimeTicks& timestamp) const;
+ bool HasReachedTarget(const base::TimeTicks& timestamp) const;
SyntheticPinchGestureParams params_;
- float current_y_0_;
- float current_y_1_;
- float target_y_0_;
- float target_y_1_;
+ float start_y_0_;
+ float start_y_1_;
+ float max_pointer_delta_0_;
SyntheticGestureParams::GestureSourceType gesture_source_type_;
GestureState state_;
SyntheticWebTouchEvent touch_event_;
+ base::TimeTicks start_time_;
+ base::TimeTicks stop_time_;
+ private:
DISALLOW_COPY_AND_ASSIGN(SyntheticPinchGesture);
};
diff --git a/chromium/content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.cc b/chromium/content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.cc
index b2fb4bb7eb3..b6b281406c3 100644
--- a/chromium/content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.cc
+++ b/chromium/content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.cc
@@ -5,8 +5,6 @@
#include "content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.h"
#include "base/logging.h"
-#include "content/common/input/input_event.h"
-#include "ui/events/latency_info.h"
#include "ui/gfx/point_f.h"
namespace content {
@@ -24,6 +22,11 @@ gfx::Vector2d CeilFromZero(const gfx::Vector2dF& vector) {
return gfx::Vector2d(x, y);
}
+gfx::Vector2dF ProjectScalarOntoVector(
+ float scalar, const gfx::Vector2d& vector) {
+ return gfx::ScaleVector2d(vector, scalar / vector.Length());
+}
+
} // namespace
SyntheticSmoothScrollGesture::SyntheticSmoothScrollGesture(
@@ -35,23 +38,22 @@ SyntheticSmoothScrollGesture::SyntheticSmoothScrollGesture(
SyntheticSmoothScrollGesture::~SyntheticSmoothScrollGesture() {}
SyntheticGesture::Result SyntheticSmoothScrollGesture::ForwardInputEvents(
- const base::TimeDelta& interval, SyntheticGestureTarget* target) {
+ const base::TimeTicks& timestamp, SyntheticGestureTarget* target) {
if (state_ == SETUP) {
gesture_source_type_ = params_.gesture_source_type;
if (gesture_source_type_ == SyntheticGestureParams::DEFAULT_INPUT)
gesture_source_type_ = target->GetDefaultSyntheticGestureSourceType();
- if (!target->SupportsSyntheticGestureSourceType(gesture_source_type_))
- return SyntheticGesture::GESTURE_SOURCE_TYPE_NOT_SUPPORTED_BY_PLATFORM;
-
state_ = STARTED;
+ current_scroll_segment_ = -1;
+ current_scroll_segment_stop_time_ = timestamp;
}
DCHECK_NE(gesture_source_type_, SyntheticGestureParams::DEFAULT_INPUT);
if (gesture_source_type_ == SyntheticGestureParams::TOUCH_INPUT)
- ForwardTouchInputEvents(interval, target);
+ ForwardTouchInputEvents(timestamp, target);
else if (gesture_source_type_ == SyntheticGestureParams::MOUSE_INPUT)
- ForwardMouseInputEvents(interval, target);
+ ForwardMouseInputEvents(timestamp, target);
else
return SyntheticGesture::GESTURE_SOURCE_TYPE_NOT_IMPLEMENTED;
@@ -60,38 +62,53 @@ SyntheticGesture::Result SyntheticSmoothScrollGesture::ForwardInputEvents(
}
void SyntheticSmoothScrollGesture::ForwardTouchInputEvents(
- const base::TimeDelta& interval, SyntheticGestureTarget* target) {
+ const base::TimeTicks& timestamp, SyntheticGestureTarget* target) {
+ base::TimeTicks event_timestamp = timestamp;
switch (state_) {
case STARTED:
- // Check for an early finish.
- if (params_.distance.IsZero()) {
+ if (ScrollIsNoOp()) {
state_ = DONE;
break;
}
- AddTouchSlopToDistance(target);
- PressTouchPoint(target);
+ AddTouchSlopToFirstDistance(target);
+ ComputeNextScrollSegment();
+ current_scroll_segment_start_position_ = params_.anchor;
+ PressTouchPoint(target, event_timestamp);
state_ = MOVING;
break;
- case MOVING:
- total_delta_ += GetPositionDelta(interval);
- MoveTouchPoint(target);
+ case MOVING: {
+ event_timestamp = ClampTimestamp(timestamp);
+ gfx::Vector2dF delta = GetPositionDeltaAtTime(event_timestamp);
+ MoveTouchPoint(target, delta, event_timestamp);
- if (HasScrolledEntireDistance()) {
- if (params_.prevent_fling) {
+ if (FinishedCurrentScrollSegment(event_timestamp)) {
+ if (!IsLastScrollSegment()) {
+ current_scroll_segment_start_position_ +=
+ params_.distances[current_scroll_segment_];
+ ComputeNextScrollSegment();
+ } else if (params_.prevent_fling) {
state_ = STOPPING;
} else {
- ReleaseTouchPoint(target);
+ ReleaseTouchPoint(target, event_timestamp);
state_ = DONE;
}
}
- break;
+ } break;
case STOPPING:
- total_stopping_wait_time_ += interval;
- if (total_stopping_wait_time_ >= target->PointerAssumedStoppedTime()) {
+ if (timestamp - current_scroll_segment_stop_time_ >=
+ target->PointerAssumedStoppedTime()) {
+ event_timestamp = current_scroll_segment_stop_time_ +
+ target->PointerAssumedStoppedTime();
// Send one last move event, but don't change the location. Without this
// we'd still sometimes cause a fling on Android.
- MoveTouchPoint(target);
- ReleaseTouchPoint(target);
+
+ // Required to suppress flings on Aura, see
+ // |UpdateWebTouchPointFromUIEvent|, remove when crbug.com/332418
+ // is fixed.
+ touch_event_.touches[0].position.y += 0.001f;
+
+ ForwardTouchEvent(target, event_timestamp);
+ ReleaseTouchPoint(target, event_timestamp);
state_ = DONE;
}
break;
@@ -105,32 +122,41 @@ void SyntheticSmoothScrollGesture::ForwardTouchInputEvents(
}
void SyntheticSmoothScrollGesture::ForwardMouseInputEvents(
- const base::TimeDelta& interval, SyntheticGestureTarget* target) {
+ const base::TimeTicks& timestamp, SyntheticGestureTarget* target) {
switch (state_) {
case STARTED:
- // Check for an early finish.
- if (params_.distance.IsZero()) {
+ if (ScrollIsNoOp()) {
state_ = DONE;
break;
}
+ ComputeNextScrollSegment();
state_ = MOVING;
// Fall through to forward the first event.
- case MOVING:
- {
- // Even though WebMouseWheelEvents take floating point deltas,
- // internally the scroll position is stored as an integer. We therefore
- // keep track of the discrete delta which is consistent with the
- // internal scrolling state. This ensures that when the gesture has
- // finished we've scrolled exactly the specified distance.
- total_delta_ += GetPositionDelta(interval);
- gfx::Vector2d delta_discrete =
- FloorTowardZero(total_delta_ - total_delta_discrete_);
- ForwardMouseWheelEvent(target, delta_discrete);
- total_delta_discrete_ += delta_discrete;
+ case MOVING: {
+ // Even though WebMouseWheelEvents take floating point deltas,
+ // internally the scroll position is stored as an integer. We therefore
+ // keep track of the discrete delta which is consistent with the
+ // internal scrolling state. This ensures that when the gesture has
+ // finished we've scrolled exactly the specified distance.
+ base::TimeTicks event_timestamp = ClampTimestamp(timestamp);
+ gfx::Vector2dF current_scroll_segment_total_delta =
+ GetPositionDeltaAtTime(event_timestamp);
+ gfx::Vector2d delta_discrete =
+ FloorTowardZero(current_scroll_segment_total_delta -
+ current_scroll_segment_total_delta_discrete_);
+ ForwardMouseWheelEvent(target, delta_discrete, event_timestamp);
+ current_scroll_segment_total_delta_discrete_ += delta_discrete;
+
+ if (FinishedCurrentScrollSegment(event_timestamp)) {
+ if (!IsLastScrollSegment()) {
+ current_scroll_segment_total_delta_discrete_ = gfx::Vector2d();
+ ComputeNextScrollSegment();
+ ForwardMouseInputEvents(timestamp, target);
+ } else {
+ state_ = DONE;
+ }
}
- if (HasScrolledEntireDistance())
- state_ = DONE;
- break;
+ } break;
case SETUP:
NOTREACHED()
<< "State STARTED invalid for synthetic scroll using touch input.";
@@ -140,84 +166,114 @@ void SyntheticSmoothScrollGesture::ForwardMouseInputEvents(
case DONE:
NOTREACHED()
<< "State DONE invalid for synthetic scroll using touch input.";
- }
+ }
}
void SyntheticSmoothScrollGesture::ForwardTouchEvent(
- SyntheticGestureTarget* target) const {
- target->DispatchInputEventToPlatform(
- InputEvent(touch_event_, ui::LatencyInfo(), false));
+ SyntheticGestureTarget* target, const base::TimeTicks& timestamp) {
+ touch_event_.timeStampSeconds = ConvertTimestampToSeconds(timestamp);
+
+ target->DispatchInputEventToPlatform(touch_event_);
}
void SyntheticSmoothScrollGesture::ForwardMouseWheelEvent(
- SyntheticGestureTarget* target, const gfx::Vector2dF& delta) const {
+ SyntheticGestureTarget* target,
+ const gfx::Vector2dF& delta,
+ const base::TimeTicks& timestamp) const {
blink::WebMouseWheelEvent mouse_wheel_event =
SyntheticWebMouseWheelEventBuilder::Build(delta.x(), delta.y(), 0, false);
mouse_wheel_event.x = params_.anchor.x();
mouse_wheel_event.y = params_.anchor.y();
- target->DispatchInputEventToPlatform(
- InputEvent(mouse_wheel_event, ui::LatencyInfo(), false));
+ mouse_wheel_event.timeStampSeconds = ConvertTimestampToSeconds(timestamp);
+
+ target->DispatchInputEventToPlatform(mouse_wheel_event);
}
void SyntheticSmoothScrollGesture::PressTouchPoint(
- SyntheticGestureTarget* target) {
+ SyntheticGestureTarget* target, const base::TimeTicks& timestamp) {
+ DCHECK_EQ(current_scroll_segment_, 0);
touch_event_.PressPoint(params_.anchor.x(), params_.anchor.y());
- ForwardTouchEvent(target);
+ ForwardTouchEvent(target, timestamp);
}
void SyntheticSmoothScrollGesture::MoveTouchPoint(
- SyntheticGestureTarget* target) {
- gfx::PointF touch_position = params_.anchor + total_delta_;
+ SyntheticGestureTarget* target,
+ const gfx::Vector2dF& delta,
+ const base::TimeTicks& timestamp) {
+ DCHECK_GE(current_scroll_segment_, 0);
+ DCHECK_LT(current_scroll_segment_,
+ static_cast<int>(params_.distances.size()));
+ gfx::PointF touch_position = current_scroll_segment_start_position_ + delta;
touch_event_.MovePoint(0, touch_position.x(), touch_position.y());
- ForwardTouchEvent(target);
+ ForwardTouchEvent(target, timestamp);
}
void SyntheticSmoothScrollGesture::ReleaseTouchPoint(
- SyntheticGestureTarget* target) {
+ SyntheticGestureTarget* target, const base::TimeTicks& timestamp) {
+ DCHECK_EQ(current_scroll_segment_,
+ static_cast<int>(params_.distances.size()) - 1);
touch_event_.ReleasePoint(0);
- ForwardTouchEvent(target);
+ ForwardTouchEvent(target, timestamp);
}
-void SyntheticSmoothScrollGesture::AddTouchSlopToDistance(
+void SyntheticSmoothScrollGesture::AddTouchSlopToFirstDistance(
SyntheticGestureTarget* target) {
- // Android uses euclidean distance to compute if a touch pointer has moved
- // beyond the slop, while Aura uses Manhattan distance. We're using Euclidean
- // distance and round up to the nearest integer.
- // For vertical and horizontal scrolls (the common case), both methods produce
- // the same result.
- gfx::Vector2dF touch_slop_delta = ProjectLengthOntoScrollDirection(
- target->GetTouchSlopInDips());
- params_.distance += CeilFromZero(touch_slop_delta);
-}
-
-gfx::Vector2dF SyntheticSmoothScrollGesture::GetPositionDelta(
- const base::TimeDelta& interval) const {
- float delta_length = params_.speed_in_pixels_s * interval.InSecondsF();
-
- // Make sure we're not scrolling too far.
- gfx::Vector2dF remaining_delta = ComputeRemainingDelta();
- if (delta_length > remaining_delta.Length())
- // In order to scroll in a certain direction we need to move the
- // touch pointer/mouse wheel in the opposite direction.
- return -remaining_delta;
- else
- return -ProjectLengthOntoScrollDirection(delta_length);
+ DCHECK_GE(params_.distances.size(), 1ul);
+ gfx::Vector2d& first_scroll_distance = params_.distances[0];
+ DCHECK_GT(first_scroll_distance.Length(), 0);
+ first_scroll_distance += CeilFromZero(ProjectScalarOntoVector(
+ target->GetTouchSlopInDips(), first_scroll_distance));
+}
+
+gfx::Vector2dF SyntheticSmoothScrollGesture::GetPositionDeltaAtTime(
+ const base::TimeTicks& timestamp) const {
+ // Make sure the final delta is correct. Using the computation below can lead
+ // to issues with floating point precision.
+ if (FinishedCurrentScrollSegment(timestamp))
+ return params_.distances[current_scroll_segment_];
+
+ float delta_length =
+ params_.speed_in_pixels_s *
+ (timestamp - current_scroll_segment_start_time_).InSecondsF();
+ return ProjectScalarOntoVector(delta_length,
+ params_.distances[current_scroll_segment_]);
+}
+
+void SyntheticSmoothScrollGesture::ComputeNextScrollSegment() {
+ current_scroll_segment_++;
+ DCHECK_LT(current_scroll_segment_,
+ static_cast<int>(params_.distances.size()));
+ int64 total_duration_in_us = static_cast<int64>(
+ 1e6 * (params_.distances[current_scroll_segment_].Length() /
+ params_.speed_in_pixels_s));
+ DCHECK_GT(total_duration_in_us, 0);
+ current_scroll_segment_start_time_ = current_scroll_segment_stop_time_;
+ current_scroll_segment_stop_time_ =
+ current_scroll_segment_start_time_ +
+ base::TimeDelta::FromMicroseconds(total_duration_in_us);
+}
+
+base::TimeTicks SyntheticSmoothScrollGesture::ClampTimestamp(
+ const base::TimeTicks& timestamp) const {
+ return std::min(timestamp, current_scroll_segment_stop_time_);
}
-gfx::Vector2dF SyntheticSmoothScrollGesture::ProjectLengthOntoScrollDirection(
- float delta_length) const {
- const float kTotalLength = params_.distance.Length();
- return ScaleVector2d(params_.distance, delta_length / kTotalLength);
+bool SyntheticSmoothScrollGesture::FinishedCurrentScrollSegment(
+ const base::TimeTicks& timestamp) const {
+ return timestamp >= current_scroll_segment_stop_time_;
}
-gfx::Vector2dF SyntheticSmoothScrollGesture::ComputeRemainingDelta() const {
- return params_.distance + total_delta_;
+bool SyntheticSmoothScrollGesture::IsLastScrollSegment() const {
+ DCHECK_LT(current_scroll_segment_,
+ static_cast<int>(params_.distances.size()));
+ return current_scroll_segment_ ==
+ static_cast<int>(params_.distances.size()) - 1;
}
-bool SyntheticSmoothScrollGesture::HasScrolledEntireDistance() const {
- return ComputeRemainingDelta().IsZero();
+bool SyntheticSmoothScrollGesture::ScrollIsNoOp() const {
+ return params_.distances.size() == 0 || params_.distances[0].IsZero();
}
} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.h b/chromium/content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.h
index 53b4da54683..a4078550e0e 100644
--- a/chromium/content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.h
+++ b/chromium/content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.h
@@ -17,6 +17,13 @@
namespace content {
+// Simulates scrolling given a sequence of scroll distances as a continuous
+// gestures (i.e. when synthesizing touch events, the touch pointer is not
+// lifted when changing scroll direction).
+// If no distance is provided or the first one is 0, no touch events are
+// generated.
+// When synthesizing touch events, the first distance is extended to compensate
+// for the touch slop.
class CONTENT_EXPORT SyntheticSmoothScrollGesture : public SyntheticGesture {
public:
explicit SyntheticSmoothScrollGesture(
@@ -24,7 +31,8 @@ class CONTENT_EXPORT SyntheticSmoothScrollGesture : public SyntheticGesture {
virtual ~SyntheticSmoothScrollGesture();
virtual SyntheticGesture::Result ForwardInputEvents(
- const base::TimeDelta& interval, SyntheticGestureTarget* target) OVERRIDE;
+ const base::TimeTicks& timestamp,
+ SyntheticGestureTarget* target) OVERRIDE;
private:
enum GestureState {
@@ -36,31 +44,44 @@ class CONTENT_EXPORT SyntheticSmoothScrollGesture : public SyntheticGesture {
};
void ForwardTouchInputEvents(
- const base::TimeDelta& interval, SyntheticGestureTarget* target);
+ const base::TimeTicks& timestamp, SyntheticGestureTarget* target);
void ForwardMouseInputEvents(
- const base::TimeDelta& interval, SyntheticGestureTarget* target);
+ const base::TimeTicks& timestamp, SyntheticGestureTarget* target);
- void ForwardTouchEvent(SyntheticGestureTarget* target) const;
+ void ForwardTouchEvent(SyntheticGestureTarget* target,
+ const base::TimeTicks& timestamp);
void ForwardMouseWheelEvent(SyntheticGestureTarget* target,
- const gfx::Vector2dF& delta) const;
+ const gfx::Vector2dF& delta,
+ const base::TimeTicks& timestamp) const;
- void PressTouchPoint(SyntheticGestureTarget* target);
- void MoveTouchPoint(SyntheticGestureTarget* target);
- void ReleaseTouchPoint(SyntheticGestureTarget* target);
+ void PressTouchPoint(SyntheticGestureTarget* target,
+ const base::TimeTicks& timestamp);
+ void MoveTouchPoint(SyntheticGestureTarget* target,
+ const gfx::Vector2dF& delta,
+ const base::TimeTicks& timestamp);
+ void ReleaseTouchPoint(SyntheticGestureTarget* target,
+ const base::TimeTicks& timestamp);
- void AddTouchSlopToDistance(SyntheticGestureTarget* target);
- gfx::Vector2dF GetPositionDelta(const base::TimeDelta& interval) const;
- gfx::Vector2dF ProjectLengthOntoScrollDirection(float delta_length) const;
- gfx::Vector2dF ComputeRemainingDelta() const;
- bool HasScrolledEntireDistance() const;
+ void AddTouchSlopToFirstDistance(SyntheticGestureTarget* target);
+ gfx::Vector2dF GetPositionDeltaAtTime(const base::TimeTicks& timestamp)
+ const;
+ void ComputeNextScrollSegment();
+ base::TimeTicks ClampTimestamp(const base::TimeTicks& timestamp) const;
+ bool FinishedCurrentScrollSegment(const base::TimeTicks& timestamp) const;
+ bool IsLastScrollSegment() const;
+ bool ScrollIsNoOp() const;
SyntheticSmoothScrollGestureParams params_;
- gfx::Vector2dF total_delta_;
- gfx::Vector2d total_delta_discrete_;
+ // Used for mouse input.
+ gfx::Vector2d current_scroll_segment_total_delta_discrete_;
+ // Used for touch input.
+ gfx::Point current_scroll_segment_start_position_;
SyntheticWebTouchEvent touch_event_;
SyntheticGestureParams::GestureSourceType gesture_source_type_;
GestureState state_;
- base::TimeDelta total_stopping_wait_time_;
+ int current_scroll_segment_;
+ base::TimeTicks current_scroll_segment_start_time_;
+ base::TimeTicks current_scroll_segment_stop_time_;
DISALLOW_COPY_AND_ASSIGN(SyntheticSmoothScrollGesture);
};
diff --git a/chromium/content/browser/renderer_host/input/synthetic_tap_gesture.cc b/chromium/content/browser/renderer_host/input/synthetic_tap_gesture.cc
index 1f15d82f013..e20412a0fcf 100644
--- a/chromium/content/browser/renderer_host/input/synthetic_tap_gesture.cc
+++ b/chromium/content/browser/renderer_host/input/synthetic_tap_gesture.cc
@@ -5,20 +5,10 @@
#include "content/browser/renderer_host/input/synthetic_tap_gesture.h"
#include "base/logging.h"
-#include "content/common/input/input_event.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
#include "ui/events/latency_info.h"
namespace content {
-namespace {
-
-void DispatchEventToPlatform(SyntheticGestureTarget* target,
- const blink::WebInputEvent& event) {
- target->DispatchInputEventToPlatform(
- InputEvent(event, ui::LatencyInfo(), false));
-}
-
-} // namespace
SyntheticTapGesture::SyntheticTapGesture(
const SyntheticTapGestureParams& params)
@@ -31,22 +21,19 @@ SyntheticTapGesture::SyntheticTapGesture(
SyntheticTapGesture::~SyntheticTapGesture() {}
SyntheticGesture::Result SyntheticTapGesture::ForwardInputEvents(
- const base::TimeDelta& interval, SyntheticGestureTarget* target) {
+ const base::TimeTicks& timestamp, SyntheticGestureTarget* target) {
if (state_ == SETUP) {
gesture_source_type_ = params_.gesture_source_type;
if (gesture_source_type_ == SyntheticGestureParams::DEFAULT_INPUT)
gesture_source_type_ = target->GetDefaultSyntheticGestureSourceType();
- if (!target->SupportsSyntheticGestureSourceType(gesture_source_type_))
- return SyntheticGesture::GESTURE_SOURCE_TYPE_NOT_SUPPORTED_BY_PLATFORM;
-
state_ = PRESS;
}
DCHECK_NE(gesture_source_type_, SyntheticGestureParams::DEFAULT_INPUT);
if (gesture_source_type_ == SyntheticGestureParams::TOUCH_INPUT ||
gesture_source_type_ == SyntheticGestureParams::MOUSE_INPUT)
- ForwardTouchOrMouseInputEvents(interval, target);
+ ForwardTouchOrMouseInputEvents(timestamp, target);
else
return SyntheticGesture::GESTURE_SOURCE_TYPE_NOT_IMPLEMENTED;
@@ -55,23 +42,22 @@ SyntheticGesture::Result SyntheticTapGesture::ForwardInputEvents(
}
void SyntheticTapGesture::ForwardTouchOrMouseInputEvents(
- const base::TimeDelta& interval, SyntheticGestureTarget* target) {
+ const base::TimeTicks& timestamp, SyntheticGestureTarget* target) {
switch (state_) {
case PRESS:
- Press(target);
+ Press(target, timestamp);
// Release immediately if duration is 0.
if (params_.duration_ms == 0) {
- Release(target);
+ Release(target, timestamp);
state_ = DONE;
} else {
+ start_time_ = timestamp;
state_ = WAITING_TO_RELEASE;
}
break;
case WAITING_TO_RELEASE:
- total_waiting_time_ += interval;
- if (total_waiting_time_ >=
- base::TimeDelta::FromMilliseconds(params_.duration_ms)) {
- Release(target);
+ if (timestamp - start_time_ >= GetDuration()) {
+ Release(target, start_time_ + GetDuration());
state_ = DONE;
}
break;
@@ -82,10 +68,12 @@ void SyntheticTapGesture::ForwardTouchOrMouseInputEvents(
}
}
-void SyntheticTapGesture::Press(SyntheticGestureTarget* target) {
+void SyntheticTapGesture::Press(SyntheticGestureTarget* target,
+ const base::TimeTicks& timestamp) {
if (gesture_source_type_ == SyntheticGestureParams::TOUCH_INPUT) {
touch_event_.PressPoint(params_.position.x(), params_.position.y());
- DispatchEventToPlatform(target, touch_event_);
+ touch_event_.timeStampSeconds = ConvertTimestampToSeconds(timestamp);
+ target->DispatchInputEventToPlatform(touch_event_);
} else if (gesture_source_type_ == SyntheticGestureParams::MOUSE_INPUT) {
blink::WebMouseEvent mouse_event =
SyntheticWebMouseEventBuilder::Build(blink::WebInputEvent::MouseDown,
@@ -93,16 +81,19 @@ void SyntheticTapGesture::Press(SyntheticGestureTarget* target) {
params_.position.y(),
0);
mouse_event.clickCount = 1;
- DispatchEventToPlatform(target, mouse_event);
+ mouse_event.timeStampSeconds = ConvertTimestampToSeconds(timestamp);
+ target->DispatchInputEventToPlatform(mouse_event);
} else {
NOTREACHED() << "Invalid gesture source type for synthetic tap gesture.";
}
}
-void SyntheticTapGesture::Release(SyntheticGestureTarget* target) {
+void SyntheticTapGesture::Release(SyntheticGestureTarget* target,
+ const base::TimeTicks& timestamp) {
if (gesture_source_type_ == SyntheticGestureParams::TOUCH_INPUT) {
touch_event_.ReleasePoint(0);
- DispatchEventToPlatform(target, touch_event_);
+ touch_event_.timeStampSeconds = ConvertTimestampToSeconds(timestamp);
+ target->DispatchInputEventToPlatform(touch_event_);
} else if (gesture_source_type_ == SyntheticGestureParams::MOUSE_INPUT) {
blink::WebMouseEvent mouse_event =
SyntheticWebMouseEventBuilder::Build(blink::WebInputEvent::MouseUp,
@@ -110,10 +101,15 @@ void SyntheticTapGesture::Release(SyntheticGestureTarget* target) {
params_.position.y(),
0);
mouse_event.clickCount = 1;
- DispatchEventToPlatform(target, mouse_event);
+ mouse_event.timeStampSeconds = ConvertTimestampToSeconds(timestamp);
+ target->DispatchInputEventToPlatform(mouse_event);
} else {
NOTREACHED() << "Invalid gesture source type for synthetic tap gesture.";
}
}
+base::TimeDelta SyntheticTapGesture::GetDuration() const {
+ return base::TimeDelta::FromMilliseconds(params_.duration_ms);
+}
+
} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/synthetic_tap_gesture.h b/chromium/content/browser/renderer_host/input/synthetic_tap_gesture.h
index 22217f99090..e67d1984d98 100644
--- a/chromium/content/browser/renderer_host/input/synthetic_tap_gesture.h
+++ b/chromium/content/browser/renderer_host/input/synthetic_tap_gesture.h
@@ -19,7 +19,8 @@ class CONTENT_EXPORT SyntheticTapGesture : public SyntheticGesture {
virtual ~SyntheticTapGesture();
virtual SyntheticGesture::Result ForwardInputEvents(
- const base::TimeDelta& interval, SyntheticGestureTarget* target) OVERRIDE;
+ const base::TimeTicks& timestamp,
+ SyntheticGestureTarget* target) OVERRIDE;
private:
enum GestureState {
@@ -29,14 +30,17 @@ class CONTENT_EXPORT SyntheticTapGesture : public SyntheticGesture {
DONE
};
- void ForwardTouchOrMouseInputEvents(const base::TimeDelta& interval,
+ void ForwardTouchOrMouseInputEvents(const base::TimeTicks& timestamp,
SyntheticGestureTarget* target);
- void Press(SyntheticGestureTarget* target);
- void Release(SyntheticGestureTarget* target);
+ void Press(SyntheticGestureTarget* target, const base::TimeTicks& timestamp);
+ void Release(SyntheticGestureTarget* target,
+ const base::TimeTicks& timestamp);
+
+ base::TimeDelta GetDuration() const;
SyntheticTapGestureParams params_;
- base::TimeDelta total_waiting_time_;
+ base::TimeTicks start_time_;
SyntheticWebTouchEvent touch_event_;
SyntheticGestureParams::GestureSourceType gesture_source_type_;
GestureState state_;
diff --git a/chromium/content/browser/renderer_host/input/tap_suppression_controller.cc b/chromium/content/browser/renderer_host/input/tap_suppression_controller.cc
index 641af6ad108..c6b17e6ecf6 100644
--- a/chromium/content/browser/renderer_host/input/tap_suppression_controller.cc
+++ b/chromium/content/browser/renderer_host/input/tap_suppression_controller.cc
@@ -10,16 +10,27 @@
namespace content {
+TapSuppressionController::Config::Config()
+ : enabled(false),
+ max_cancel_to_down_time(base::TimeDelta::FromMilliseconds(180)),
+ max_tap_gap_time(base::TimeDelta::FromMilliseconds(500)) {
+}
+
TapSuppressionController::TapSuppressionController(
- TapSuppressionControllerClient* client)
+ TapSuppressionControllerClient* client,
+ const Config& config)
: client_(client),
- state_(TapSuppressionController::NOTHING) {
+ state_(config.enabled ? NOTHING : DISABLED),
+ max_cancel_to_down_time_(config.max_cancel_to_down_time),
+ max_tap_gap_time_(config.max_tap_gap_time) {
}
TapSuppressionController::~TapSuppressionController() {}
void TapSuppressionController::GestureFlingCancel() {
switch (state_) {
+ case DISABLED:
+ break;
case NOTHING:
case GFC_IN_PROGRESS:
case LAST_CANCEL_STOPPED_FLING:
@@ -33,6 +44,7 @@ void TapSuppressionController::GestureFlingCancel() {
void TapSuppressionController::GestureFlingCancelAck(bool processed) {
base::TimeTicks event_time = Now();
switch (state_) {
+ case DISABLED:
case NOTHING:
break;
case GFC_IN_PROGRESS:
@@ -57,23 +69,21 @@ void TapSuppressionController::GestureFlingCancelAck(bool processed) {
bool TapSuppressionController::ShouldDeferTapDown() {
base::TimeTicks event_time = Now();
switch (state_) {
+ case DISABLED:
case NOTHING:
return false;
case GFC_IN_PROGRESS:
state_ = TAP_DOWN_STASHED;
- StartTapDownTimer(
- base::TimeDelta::FromMilliseconds(client_->MaxTapGapTimeInMs()));
+ StartTapDownTimer(max_tap_gap_time_);
return true;
case TAP_DOWN_STASHED:
NOTREACHED() << "TapDown on TAP_DOWN_STASHED state";
state_ = NOTHING;
return false;
case LAST_CANCEL_STOPPED_FLING:
- if ((event_time - fling_cancel_time_).InMilliseconds()
- < client_->MaxCancelToDownTimeInMs()) {
+ if ((event_time - fling_cancel_time_) < max_cancel_to_down_time_) {
state_ = TAP_DOWN_STASHED;
- StartTapDownTimer(
- base::TimeDelta::FromMilliseconds(client_->MaxTapGapTimeInMs()));
+ StartTapDownTimer(max_tap_gap_time_);
return true;
} else {
state_ = NOTHING;
@@ -86,6 +96,7 @@ bool TapSuppressionController::ShouldDeferTapDown() {
bool TapSuppressionController::ShouldSuppressTapEnd() {
switch (state_) {
+ case DISABLED:
case NOTHING:
case GFC_IN_PROGRESS:
return false;
@@ -115,7 +126,10 @@ void TapSuppressionController::StopTapDownTimer() {
void TapSuppressionController::TapDownTimerExpired() {
switch (state_) {
+ case DISABLED:
case NOTHING:
+ NOTREACHED() << "Timer fired on invalid state.";
+ break;
case GFC_IN_PROGRESS:
case LAST_CANCEL_STOPPED_FLING:
NOTREACHED() << "Timer fired on invalid state.";
diff --git a/chromium/content/browser/renderer_host/input/tap_suppression_controller.h b/chromium/content/browser/renderer_host/input/tap_suppression_controller.h
index ef662f32f7b..93977f950c3 100644
--- a/chromium/content/browser/renderer_host/input/tap_suppression_controller.h
+++ b/chromium/content/browser/renderer_host/input/tap_suppression_controller.h
@@ -19,7 +19,22 @@ class TapSuppressionControllerClient;
// GestureFlingCancel are suppressed.
class CONTENT_EXPORT TapSuppressionController {
public:
- explicit TapSuppressionController(TapSuppressionControllerClient* client);
+ struct CONTENT_EXPORT Config {
+ Config();
+
+ // Defaults to false, in which case no suppression is performed.
+ bool enabled;
+
+ // The maximum time allowed between a GestureFlingCancel and its
+ // corresponding tap down.
+ base::TimeDelta max_cancel_to_down_time;
+
+ // The maximum time allowed between a single tap's down and up events.
+ base::TimeDelta max_tap_gap_time;
+ };
+
+ TapSuppressionController(TapSuppressionControllerClient* client,
+ const Config& config);
virtual ~TapSuppressionController();
// Should be called whenever a GestureFlingCancel event is received.
@@ -49,17 +64,20 @@ class CONTENT_EXPORT TapSuppressionController {
friend class MockTapSuppressionController;
enum State {
+ DISABLED,
NOTHING,
GFC_IN_PROGRESS,
TAP_DOWN_STASHED,
LAST_CANCEL_STOPPED_FLING,
};
-
TapSuppressionControllerClient* client_;
base::OneShotTimer<TapSuppressionController> tap_down_timer_;
State state_;
+ base::TimeDelta max_cancel_to_down_time_;
+ base::TimeDelta max_tap_gap_time_;
+
// TODO(rjkroege): During debugging, the event times did not prove reliable.
// Replace the use of base::TimeTicks with an accurate event time when they
// become available post http://crbug.com/119556.
diff --git a/chromium/content/browser/renderer_host/input/tap_suppression_controller_client.h b/chromium/content/browser/renderer_host/input/tap_suppression_controller_client.h
index fcea0e1f58e..075fa08d3b5 100644
--- a/chromium/content/browser/renderer_host/input/tap_suppression_controller_client.h
+++ b/chromium/content/browser/renderer_host/input/tap_suppression_controller_client.h
@@ -13,14 +13,6 @@ class TapSuppressionControllerClient {
public:
virtual ~TapSuppressionControllerClient() {}
- // Derived classes should implement this function to return the maximum time
- // they allow between a GestureFlingCancel and its corresponding tap down.
- virtual int MaxCancelToDownTimeInMs() = 0;
-
- // Derived classes should implement this function to return the maximum time
- // they allow between a single tap's down and up events.
- virtual int MaxTapGapTimeInMs() = 0;
-
// Called whenever the deferred tap down (if saved) should be dropped totally.
virtual void DropStashedTapDown() = 0;
diff --git a/chromium/content/browser/renderer_host/input/tap_suppression_controller_unittest.cc b/chromium/content/browser/renderer_host/input/tap_suppression_controller_unittest.cc
index d436f45a4a5..702dc6f10b8 100644
--- a/chromium/content/browser/renderer_host/input/tap_suppression_controller_unittest.cc
+++ b/chromium/content/browser/renderer_host/input/tap_suppression_controller_unittest.cc
@@ -15,6 +15,7 @@ namespace content {
class MockTapSuppressionController : public TapSuppressionController,
public TapSuppressionControllerClient {
public:
+ using TapSuppressionController::DISABLED;
using TapSuppressionController::NOTHING;
using TapSuppressionController::GFC_IN_PROGRESS;
using TapSuppressionController::TAP_DOWN_STASHED;
@@ -32,14 +33,11 @@ class MockTapSuppressionController : public TapSuppressionController,
STASHED_TAP_DOWN_FORWARDED = 1 << 7,
};
- MockTapSuppressionController()
- : TapSuppressionController(this),
- max_cancel_to_down_time_in_ms_(1),
- max_tap_gap_time_in_ms_(1),
+ MockTapSuppressionController(const TapSuppressionController::Config& config)
+ : TapSuppressionController(this, config),
last_actions_(NONE),
time_(),
- timer_started_(false) {
- }
+ timer_started_(false) {}
virtual ~MockTapSuppressionController() {}
@@ -86,14 +84,6 @@ class MockTapSuppressionController : public TapSuppressionController,
}
}
- void set_max_cancel_to_down_time_in_ms(int val) {
- max_cancel_to_down_time_in_ms_ = val;
- }
-
- void set_max_tap_gap_time_in_ms(int val) {
- max_tap_gap_time_in_ms_ = val;
- }
-
State state() { return state_; }
int last_actions() { return last_actions_; }
@@ -114,14 +104,6 @@ class MockTapSuppressionController : public TapSuppressionController,
private:
// TapSuppressionControllerClient implementation
- virtual int MaxCancelToDownTimeInMs() OVERRIDE {
- return max_cancel_to_down_time_in_ms_;
- }
-
- virtual int MaxTapGapTimeInMs() OVERRIDE {
- return max_tap_gap_time_in_ms_;
- }
-
virtual void DropStashedTapDown() OVERRIDE {
last_actions_ |= TAP_DOWN_DROPPED;
}
@@ -136,9 +118,6 @@ class MockTapSuppressionController : public TapSuppressionController,
using TapSuppressionController::ShouldDeferTapDown;
using TapSuppressionController::ShouldSuppressTapEnd;
- int max_cancel_to_down_time_in_ms_;
- int max_tap_gap_time_in_ms_;
-
int last_actions_;
base::TimeTicks time_;
@@ -158,22 +137,28 @@ class TapSuppressionControllerTest : public testing::Test {
protected:
// testing::Test
virtual void SetUp() {
- tap_suppression_controller_.reset(new MockTapSuppressionController());
+ tap_suppression_controller_.reset(
+ new MockTapSuppressionController(GetConfig()));
}
virtual void TearDown() {
tap_suppression_controller_.reset();
}
+ static TapSuppressionController::Config GetConfig() {
+ TapSuppressionController::Config config;
+ config.enabled = true;
+ config.max_cancel_to_down_time = base::TimeDelta::FromMilliseconds(10);
+ config.max_tap_gap_time = base::TimeDelta::FromMilliseconds(10);
+ return config;
+ }
+
scoped_ptr<MockTapSuppressionController> tap_suppression_controller_;
};
// Test TapSuppressionController for when GestureFlingCancel Ack comes before
// TapDown and everything happens without any delays.
TEST_F(TapSuppressionControllerTest, GFCAckBeforeTapFast) {
- tap_suppression_controller_->set_max_cancel_to_down_time_in_ms(10);
- tap_suppression_controller_->set_max_tap_gap_time_in_ms(10);
-
// Send GestureFlingCancel.
tap_suppression_controller_->SendGestureFlingCancel();
EXPECT_EQ(MockTapSuppressionController::NONE,
@@ -207,9 +192,6 @@ TEST_F(TapSuppressionControllerTest, GFCAckBeforeTapFast) {
// Test TapSuppressionController for when GestureFlingCancel Ack comes before
// TapDown, but there is a small delay between TapDown and TapUp.
TEST_F(TapSuppressionControllerTest, GFCAckBeforeTapInsufficientlyLateTapUp) {
- tap_suppression_controller_->set_max_cancel_to_down_time_in_ms(10);
- tap_suppression_controller_->set_max_tap_gap_time_in_ms(10);
-
// Send GestureFlingCancel.
tap_suppression_controller_->SendGestureFlingCancel();
EXPECT_EQ(MockTapSuppressionController::NONE,
@@ -251,9 +233,6 @@ TEST_F(TapSuppressionControllerTest, GFCAckBeforeTapInsufficientlyLateTapUp) {
// Test TapSuppressionController for when GestureFlingCancel Ack comes before
// TapDown, but there is a long delay between TapDown and TapUp.
TEST_F(TapSuppressionControllerTest, GFCAckBeforeTapSufficientlyLateTapUp) {
- tap_suppression_controller_->set_max_cancel_to_down_time_in_ms(10);
- tap_suppression_controller_->set_max_tap_gap_time_in_ms(10);
-
// Send GestureFlingCancel.
tap_suppression_controller_->SendGestureFlingCancel();
EXPECT_EQ(MockTapSuppressionController::NONE,
@@ -294,9 +273,6 @@ TEST_F(TapSuppressionControllerTest, GFCAckBeforeTapSufficientlyLateTapUp) {
// Test TapSuppressionController for when GestureFlingCancel Ack comes before
// TapDown, but there is a small delay between the Ack and TapDown.
TEST_F(TapSuppressionControllerTest, GFCAckBeforeTapInsufficientlyLateTapDown) {
- tap_suppression_controller_->set_max_cancel_to_down_time_in_ms(10);
- tap_suppression_controller_->set_max_tap_gap_time_in_ms(10);
-
// Send GestureFlingCancel.
tap_suppression_controller_->SendGestureFlingCancel();
EXPECT_EQ(MockTapSuppressionController::NONE,
@@ -338,9 +314,6 @@ TEST_F(TapSuppressionControllerTest, GFCAckBeforeTapInsufficientlyLateTapDown) {
// Test TapSuppressionController for when GestureFlingCancel Ack comes before
// TapDown, but there is a long delay between the Ack and TapDown.
TEST_F(TapSuppressionControllerTest, GFCAckBeforeTapSufficientlyLateTapDown) {
- tap_suppression_controller_->set_max_cancel_to_down_time_in_ms(10);
- tap_suppression_controller_->set_max_tap_gap_time_in_ms(10);
-
// Send GestureFlingCancel.
tap_suppression_controller_->SendGestureFlingCancel();
EXPECT_EQ(MockTapSuppressionController::NONE,
@@ -381,9 +354,6 @@ TEST_F(TapSuppressionControllerTest, GFCAckBeforeTapSufficientlyLateTapDown) {
// Test TapSuppressionController for when unprocessed GestureFlingCancel Ack
// comes after TapDown and everything happens without any delay.
TEST_F(TapSuppressionControllerTest, GFCAckUnprocessedAfterTapFast) {
- tap_suppression_controller_->set_max_cancel_to_down_time_in_ms(10);
- tap_suppression_controller_->set_max_tap_gap_time_in_ms(10);
-
// Send GestureFlingCancel.
tap_suppression_controller_->SendGestureFlingCancel();
EXPECT_EQ(MockTapSuppressionController::NONE,
@@ -417,9 +387,6 @@ TEST_F(TapSuppressionControllerTest, GFCAckUnprocessedAfterTapFast) {
// Test TapSuppressionController for when processed GestureFlingCancel Ack comes
// after TapDown and everything happens without any delay.
TEST_F(TapSuppressionControllerTest, GFCAckProcessedAfterTapFast) {
- tap_suppression_controller_->set_max_cancel_to_down_time_in_ms(10);
- tap_suppression_controller_->set_max_tap_gap_time_in_ms(10);
-
// Send GestureFlingCancel.
tap_suppression_controller_->SendGestureFlingCancel();
EXPECT_EQ(MockTapSuppressionController::NONE,
@@ -453,9 +420,6 @@ TEST_F(TapSuppressionControllerTest, GFCAckProcessedAfterTapFast) {
// Test TapSuppressionController for when GestureFlingCancel Ack comes after
// TapDown and there is a small delay between the Ack and TapUp.
TEST_F(TapSuppressionControllerTest, GFCAckAfterTapInsufficientlyLateTapUp) {
- tap_suppression_controller_->set_max_cancel_to_down_time_in_ms(10);
- tap_suppression_controller_->set_max_tap_gap_time_in_ms(10);
-
// Send GestureFlingCancel.
tap_suppression_controller_->SendGestureFlingCancel();
EXPECT_EQ(MockTapSuppressionController::NONE,
@@ -497,9 +461,6 @@ TEST_F(TapSuppressionControllerTest, GFCAckAfterTapInsufficientlyLateTapUp) {
// Test TapSuppressionController for when GestureFlingCancel Ack comes after
// TapDown and there is a long delay between the Ack and TapUp.
TEST_F(TapSuppressionControllerTest, GFCAckAfterTapSufficientlyLateTapUp) {
- tap_suppression_controller_->set_max_cancel_to_down_time_in_ms(10);
- tap_suppression_controller_->set_max_tap_gap_time_in_ms(10);
-
// Send GestureFlingCancel.
tap_suppression_controller_->SendGestureFlingCancel();
EXPECT_EQ(MockTapSuppressionController::NONE,
@@ -537,4 +498,40 @@ TEST_F(TapSuppressionControllerTest, GFCAckAfterTapSufficientlyLateTapUp) {
tap_suppression_controller_->state());
}
+// Test that no suppression occurs if the TapSuppressionController is disabled.
+TEST_F(TapSuppressionControllerTest, NoSuppressionIfDisabled) {
+ TapSuppressionController::Config disabled_config;
+ disabled_config.enabled = false;
+ tap_suppression_controller_.reset(
+ new MockTapSuppressionController(disabled_config));
+
+ // Send GestureFlingCancel.
+ tap_suppression_controller_->SendGestureFlingCancel();
+ EXPECT_EQ(MockTapSuppressionController::NONE,
+ tap_suppression_controller_->last_actions());
+ EXPECT_EQ(MockTapSuppressionController::DISABLED,
+ tap_suppression_controller_->state());
+
+ // Send GestureFlingCancel Ack.
+ tap_suppression_controller_->SendGestureFlingCancelAck(true);
+ EXPECT_EQ(MockTapSuppressionController::NONE,
+ tap_suppression_controller_->last_actions());
+ EXPECT_EQ(MockTapSuppressionController::DISABLED,
+ tap_suppression_controller_->state());
+
+ // Send TapDown. This TapDown should not be suppressed.
+ tap_suppression_controller_->SendTapDown();
+ EXPECT_EQ(MockTapSuppressionController::TAP_DOWN_FORWARDED,
+ tap_suppression_controller_->last_actions());
+ EXPECT_EQ(MockTapSuppressionController::DISABLED,
+ tap_suppression_controller_->state());
+
+ // Send TapUp. This TapUp should not be suppressed.
+ tap_suppression_controller_->SendTapUp();
+ EXPECT_EQ(MockTapSuppressionController::TAP_UP_FORWARDED,
+ tap_suppression_controller_->last_actions());
+ EXPECT_EQ(MockTapSuppressionController::DISABLED,
+ tap_suppression_controller_->state());
+}
+
} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/touch_action_browsertest.cc b/chromium/content/browser/renderer_host/input/touch_action_browsertest.cc
new file mode 100644
index 00000000000..9d9fb83785d
--- /dev/null
+++ b/chromium/content/browser/renderer_host/input/touch_action_browsertest.cc
@@ -0,0 +1,216 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/auto_reset.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/browser/renderer_host/input/synthetic_gesture.h"
+#include "content/browser/renderer_host/input/synthetic_gesture_controller.h"
+#include "content/browser/renderer_host/input/synthetic_gesture_target.h"
+#include "content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.h"
+#include "content/browser/renderer_host/input/touch_event_queue.h"
+#include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "content/browser/renderer_host/render_widget_host_view_base.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/common/input/synthetic_gesture_params.h"
+#include "content/common/input/synthetic_smooth_scroll_gesture_params.h"
+#include "content/common/input_messages.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host_view.h"
+#include "content/public/common/content_switches.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 "third_party/WebKit/public/web/WebInputEvent.h"
+#include "ui/events/event_switches.h"
+#include "ui/events/latency_info.h"
+
+using blink::WebInputEvent;
+
+namespace {
+
+const char kTouchActionDataURL[] =
+ "data:text/html;charset=utf-8,"
+ "<!DOCTYPE html>"
+ "<meta name='viewport' content='width=device-width'/>"
+ "<style>"
+ "html, body {"
+ " margin: 0;"
+ "}"
+ ".box {"
+ " height: 96px;"
+ " width: 96px;"
+ " border: 2px solid blue;"
+ "}"
+ ".spacer { height: 1000px; }"
+ ".ta-none { touch-action: none; }"
+ "</style>"
+ "<div class=box></div>"
+ "<div class='box ta-none'></div>"
+ "<div class=spacer></div>"
+ "<script>"
+ " window.eventCounts = "
+ " {touchstart:0, touchmove:0, touchend: 0, touchcancel:0};"
+ " function countEvent(e) { eventCounts[e.type]++; }"
+ " for (var evt in eventCounts) { "
+ " document.addEventListener(evt, countEvent); "
+ " }"
+ " document.title='ready';"
+ "</script>";
+
+} // namespace
+
+namespace content {
+
+
+class TouchActionBrowserTest : public ContentBrowserTest {
+ public:
+ TouchActionBrowserTest() {}
+ virtual ~TouchActionBrowserTest() {}
+
+ RenderWidgetHostImpl* GetWidgetHost() {
+ return RenderWidgetHostImpl::From(shell()->web_contents()->
+ GetRenderViewHost());
+ }
+
+ void OnSyntheticGestureCompleted(SyntheticGesture::Result result) {
+ EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result);
+ runner_->Quit();
+ }
+
+ protected:
+ void LoadURL() {
+ const GURL data_url(kTouchActionDataURL);
+ NavigateToURL(shell(), data_url);
+
+ RenderWidgetHostImpl* host = GetWidgetHost();
+ host->GetView()->SetSize(gfx::Size(400, 400));
+
+ base::string16 ready_title(base::ASCIIToUTF16("ready"));
+ TitleWatcher watcher(shell()->web_contents(), ready_title);
+ ignore_result(watcher.WaitAndGetTitle());
+ }
+
+ // ContentBrowserTest:
+ virtual void SetUpCommandLine(CommandLine* cmd) OVERRIDE {
+ cmd->AppendSwitchASCII(switches::kTouchEvents,
+ switches::kTouchEventsEnabled);
+ // TODO(rbyers): Remove this switch once touch-action ships.
+ // http://crbug.com/241964
+ cmd->AppendSwitch(switches::kEnableExperimentalWebPlatformFeatures);
+ }
+
+ int ExecuteScriptAndExtractInt(const std::string& script) {
+ int value = 0;
+ EXPECT_TRUE(content::ExecuteScriptAndExtractInt(
+ shell()->web_contents(),
+ "domAutomationController.send(" + script + ")",
+ &value));
+ return value;
+ }
+
+ int GetScrollTop() {
+ return ExecuteScriptAndExtractInt("document.documentElement.scrollTop");
+ }
+
+ // Generate touch events for a synthetic scroll from |point| for |distance|.
+ // Returns true if the page scrolled by the desired amount, and false if
+ // it didn't scroll at all.
+ bool DoTouchScroll(const gfx::Point& point, const gfx::Vector2d& distance) {
+ EXPECT_EQ(0, GetScrollTop());
+
+ int scrollHeight = ExecuteScriptAndExtractInt(
+ "document.documentElement.scrollHeight");
+ EXPECT_EQ(1200, scrollHeight);
+
+ SyntheticSmoothScrollGestureParams params;
+ params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
+ params.anchor = point;
+ params.distances.push_back(-distance);
+
+ runner_ = new MessageLoopRunner();
+
+ scoped_ptr<SyntheticSmoothScrollGesture> gesture(
+ new SyntheticSmoothScrollGesture(params));
+ GetWidgetHost()->QueueSyntheticGesture(
+ gesture.PassAs<SyntheticGesture>(),
+ base::Bind(&TouchActionBrowserTest::OnSyntheticGestureCompleted,
+ base::Unretained(this)));
+
+ // Runs until we get the OnSyntheticGestureCompleted callback
+ runner_->Run();
+ runner_ = NULL;
+
+ // Check the scroll offset
+ int scrollTop = GetScrollTop();
+ if (scrollTop == 0)
+ return false;
+
+ EXPECT_EQ(distance.y(), scrollTop);
+ return true;
+ }
+
+ private:
+ scoped_refptr<MessageLoopRunner> runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(TouchActionBrowserTest);
+};
+
+// TouchActionBrowserTest.DefaultAuto fails under ThreadSanitizer v2, see
+// http://crbug.com/348539 and is flaky on XP, see
+// http://crbug.com/354763
+//
+// Mac doesn't yet have a gesture recognizer, so can't support turning touch
+// events into scroll gestures.
+// Will be fixed with http://crbug.com/337142
+//
+// Verify the test infrastructure works - we can touch-scroll the page and get a
+// touchcancel as expected.
+IN_PROC_BROWSER_TEST_F(TouchActionBrowserTest, DISABLED_DefaultAuto) {
+ LoadURL();
+
+ bool scrolled = DoTouchScroll(gfx::Point(50, 50), gfx::Vector2d(0, 45));
+ EXPECT_TRUE(scrolled);
+
+ EXPECT_EQ(1, ExecuteScriptAndExtractInt("eventCounts.touchstart"));
+ EXPECT_EQ(1, ExecuteScriptAndExtractInt("eventCounts.touchmove"));
+ if (TouchEventQueue::TOUCH_SCROLLING_MODE_DEFAULT ==
+ TouchEventQueue::TOUCH_SCROLLING_MODE_TOUCHCANCEL) {
+ EXPECT_EQ(0, ExecuteScriptAndExtractInt("eventCounts.touchend"));
+ EXPECT_EQ(1, ExecuteScriptAndExtractInt("eventCounts.touchcancel"));
+ } else {
+ EXPECT_EQ(1, ExecuteScriptAndExtractInt("eventCounts.touchend"));
+ EXPECT_EQ(0, ExecuteScriptAndExtractInt("eventCounts.touchcancel"));
+ }
+}
+
+// Verify that touching a touch-action: none region disables scrolling and
+// enables all touch events to be sent.
+// Disabled on MacOS because it doesn't support touch input.
+// Flaky on OS_CHROMEOS http://crbug.com/376695.
+// Also flaky on Linux Tests (TSan v2) http://crbug.com/376668.
+#if defined(OS_MACOSX) || defined(OS_CHROMEOS)
+#define MAYBE_TouchActionNone DISABLED_TouchActionNone
+#elif defined(THREAD_SANITIZER) && defined(OS_LINUX)
+#define MAYBE_TouchActionNone DISABLED_TouchActionNone
+#else
+#define MAYBE_TouchActionNone TouchActionNone
+#endif
+IN_PROC_BROWSER_TEST_F(TouchActionBrowserTest, MAYBE_TouchActionNone) {
+ LoadURL();
+
+ bool scrolled = DoTouchScroll(gfx::Point(50, 150), gfx::Vector2d(0, 45));
+ EXPECT_FALSE(scrolled);
+
+ EXPECT_EQ(1, ExecuteScriptAndExtractInt("eventCounts.touchstart"));
+ EXPECT_GT(ExecuteScriptAndExtractInt("eventCounts.touchmove"), 1);
+ EXPECT_EQ(1, ExecuteScriptAndExtractInt("eventCounts.touchend"));
+ EXPECT_EQ(0, ExecuteScriptAndExtractInt("eventCounts.touchcancel"));
+}
+
+} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/touch_action_filter.cc b/chromium/content/browser/renderer_host/input/touch_action_filter.cc
index 665ff15bd30..53d47c4234c 100644
--- a/chromium/content/browser/renderer_host/input/touch_action_filter.cc
+++ b/chromium/content/browser/renderer_host/input/touch_action_filter.cc
@@ -4,6 +4,9 @@
#include "content/browser/renderer_host/input/touch_action_filter.h"
+#include <math.h>
+
+#include "base/logging.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
using blink::WebInputEvent;
@@ -13,31 +16,108 @@ namespace content {
TouchActionFilter::TouchActionFilter() :
drop_scroll_gesture_events_(false),
+ drop_pinch_gesture_events_(false),
+ drop_current_tap_ending_event_(false),
+ allow_current_double_tap_event_(true),
allowed_touch_action_(TOUCH_ACTION_AUTO) {
}
-bool TouchActionFilter::FilterGestureEvent(
- const WebGestureEvent& gesture_event) {
+bool TouchActionFilter::FilterGestureEvent(WebGestureEvent* gesture_event) {
// Filter for allowable touch actions first (eg. before the TouchEventQueue
// can decide to send a touch cancel event).
- // TODO(rbyers): Add touch-action control over for pinch. crbug.com/247566.
- switch(gesture_event.type) {
+ switch(gesture_event->type) {
case WebInputEvent::GestureScrollBegin:
- if (allowed_touch_action_ == TOUCH_ACTION_NONE)
- drop_scroll_gesture_events_ = true;
- // FALL THROUGH
+ DCHECK(!drop_scroll_gesture_events_);
+ drop_scroll_gesture_events_ = ShouldSuppressScroll(*gesture_event);
+ return drop_scroll_gesture_events_;
+
case WebInputEvent::GestureScrollUpdate:
if (drop_scroll_gesture_events_)
return true;
+ else {
+ if (allowed_touch_action_ == TOUCH_ACTION_PAN_X) {
+ gesture_event->data.scrollUpdate.deltaY = 0;
+ gesture_event->data.scrollUpdate.velocityY = 0;
+ } else if (allowed_touch_action_ == TOUCH_ACTION_PAN_Y) {
+ gesture_event->data.scrollUpdate.deltaX = 0;
+ gesture_event->data.scrollUpdate.velocityX = 0;
+ }
+ }
break;
- case WebInputEvent::GestureScrollEnd:
case WebInputEvent::GestureFlingStart:
- allowed_touch_action_ = content::TOUCH_ACTION_AUTO;
- if (drop_scroll_gesture_events_) {
- drop_scroll_gesture_events_ = false;
+ if (gesture_event->sourceDevice != blink::WebGestureDeviceTouchscreen)
+ break;
+ if (!drop_scroll_gesture_events_) {
+ if (allowed_touch_action_ == TOUCH_ACTION_PAN_X)
+ gesture_event->data.flingStart.velocityY = 0;
+ if (allowed_touch_action_ == TOUCH_ACTION_PAN_Y)
+ gesture_event->data.flingStart.velocityX = 0;
+ }
+ return FilterScrollEndingGesture();
+
+ case WebInputEvent::GestureScrollEnd:
+ return FilterScrollEndingGesture();
+
+ case WebInputEvent::GesturePinchBegin:
+ DCHECK(!drop_pinch_gesture_events_);
+ if (allowed_touch_action_ == TOUCH_ACTION_AUTO ||
+ allowed_touch_action_ & TOUCH_ACTION_PINCH_ZOOM) {
+ // Pinch events are always bracketed by scroll events, and the W3C
+ // standard touch-action provides no way to disable scrolling without
+ // also disabling pinching (validated by the IPC ENUM traits).
+ DCHECK(allowed_touch_action_ == TOUCH_ACTION_AUTO ||
+ allowed_touch_action_ == TOUCH_ACTION_MANIPULATION);
+ DCHECK(!drop_scroll_gesture_events_);
+ } else {
+ drop_pinch_gesture_events_ = true;
+ }
+ return drop_pinch_gesture_events_;
+
+ case WebInputEvent::GesturePinchUpdate:
+ return drop_pinch_gesture_events_;
+
+ case WebInputEvent::GesturePinchEnd:
+ if (drop_pinch_gesture_events_) {
+ drop_pinch_gesture_events_ = false;
return true;
}
+ DCHECK(!drop_scroll_gesture_events_);
+ break;
+
+ // The double tap gesture is a tap ending event. If a double tap gesture is
+ // filtered out, replace it with a tap event.
+ case WebInputEvent::GestureDoubleTap:
+ DCHECK_EQ(1, gesture_event->data.tap.tapCount);
+ if (!allow_current_double_tap_event_)
+ gesture_event->type = WebInputEvent::GestureTap;
+ allow_current_double_tap_event_ = true;
+ break;
+
+ // If double tap is disabled, there's no reason for the tap delay.
+ case WebInputEvent::GestureTapUnconfirmed:
+ DCHECK_EQ(1, gesture_event->data.tap.tapCount);
+ allow_current_double_tap_event_ =
+ allowed_touch_action_ == TOUCH_ACTION_AUTO;
+ if (!allow_current_double_tap_event_) {
+ gesture_event->type = WebInputEvent::GestureTap;
+ drop_current_tap_ending_event_ = true;
+ }
+ break;
+
+ case WebInputEvent::GestureTap:
+ allow_current_double_tap_event_ =
+ allowed_touch_action_ == TOUCH_ACTION_AUTO;
+ // Fall through.
+ case WebInputEvent::GestureTapCancel:
+ if (drop_current_tap_ending_event_) {
+ drop_current_tap_ending_event_ = false;
+ return true;
+ }
+ break;
+
+ case WebInputEvent::GestureTapDown:
+ DCHECK(!drop_current_tap_ending_event_);
break;
default:
@@ -49,15 +129,69 @@ bool TouchActionFilter::FilterGestureEvent(
return false;
}
-void TouchActionFilter::OnSetTouchAction(
- content::TouchAction touch_action) {
+bool TouchActionFilter::FilterScrollEndingGesture() {
+ DCHECK(!drop_pinch_gesture_events_);
+ if (drop_scroll_gesture_events_) {
+ drop_scroll_gesture_events_ = false;
+ return true;
+ }
+ return false;
+}
+
+void TouchActionFilter::OnSetTouchAction(TouchAction touch_action) {
// For multiple fingers, we take the intersection of the touch actions for
- // all fingers that have gone down during this action.
- // TODO(rbyers): What exact multi-finger semantic do we want? This is left
- // as implementation-defined in the pointer events specification.
- // crbug.com/247566.
- if (touch_action == content::TOUCH_ACTION_NONE)
- allowed_touch_action_ = content::TOUCH_ACTION_NONE;
+ // all fingers that have gone down during this action. In the majority of
+ // real-world scenarios the touch action for all fingers will be the same.
+ // This is left as implementation-defined in the pointer events
+ // specification because of the relationship to gestures (which are off
+ // limits for the spec). I believe the following are desirable properties
+ // of this choice:
+ // 1. Not sensitive to finger touch order. Behavior of putting two fingers
+ // down "at once" will be deterministic.
+ // 2. Only subtractive - eg. can't trigger scrolling on a element that
+ // otherwise has scrolling disabling by the addition of a finger.
+ allowed_touch_action_ = Intersect(allowed_touch_action_, touch_action);
+}
+
+void TouchActionFilter::ResetTouchAction() {
+ // Note that resetting the action mid-sequence is tolerated. Gestures that had
+ // their begin event(s) suppressed will be suppressed until the next sequence.
+ allowed_touch_action_ = TOUCH_ACTION_AUTO;
+}
+
+bool TouchActionFilter::ShouldSuppressScroll(
+ const blink::WebGestureEvent& gesture_event) {
+ DCHECK_EQ(gesture_event.type, WebInputEvent::GestureScrollBegin);
+ if (allowed_touch_action_ == TOUCH_ACTION_AUTO)
+ return false;
+ if (allowed_touch_action_ == TOUCH_ACTION_NONE)
+ return true;
+
+ // If there's no hint or it's perfectly diagonal, then allow the scroll.
+ if (fabs(gesture_event.data.scrollBegin.deltaXHint) ==
+ fabs(gesture_event.data.scrollBegin.deltaYHint))
+ return false;
+
+ // Determine the primary initial axis of the scroll, and check whether
+ // panning along that axis is permitted.
+ if (fabs(gesture_event.data.scrollBegin.deltaXHint) >
+ fabs(gesture_event.data.scrollBegin.deltaYHint))
+ return !(allowed_touch_action_ & TOUCH_ACTION_PAN_X);
+ return !(allowed_touch_action_ & TOUCH_ACTION_PAN_Y);
+}
+
+TouchAction TouchActionFilter::Intersect(TouchAction ta1, TouchAction ta2) {
+ if (ta1 == TOUCH_ACTION_NONE || ta2 == TOUCH_ACTION_NONE)
+ return TOUCH_ACTION_NONE;
+ if (ta1 == TOUCH_ACTION_AUTO)
+ return ta2;
+ if (ta2 == TOUCH_ACTION_AUTO)
+ return ta1;
+
+ // Only the true flags are left - take their intersection.
+ if (!(ta1 & ta2))
+ return TOUCH_ACTION_NONE;
+ return static_cast<TouchAction>(ta1 & ta2);
}
}
diff --git a/chromium/content/browser/renderer_host/input/touch_action_filter.h b/chromium/content/browser/renderer_host/input/touch_action_filter.h
index 6485c45a935..2eedc064209 100644
--- a/chromium/content/browser/renderer_host/input/touch_action_filter.h
+++ b/chromium/content/browser/renderer_host/input/touch_action_filter.h
@@ -25,18 +25,45 @@ public:
// Returns true if the supplied gesture event should be dropped based on
// the current touch-action state.
- bool FilterGestureEvent(const blink::WebGestureEvent& gesture_event);
+ bool FilterGestureEvent(blink::WebGestureEvent* gesture_event);
// Called when a set-touch-action message is received from the renderer
// for a touch start event that is currently in flight.
void OnSetTouchAction(content::TouchAction touch_action);
+ // Must be called at least once between when the last gesture events for the
+ // previous touch sequence have passed through the touch action filter and the
+ // time the touch start for the next touch sequence has reached the
+ // renderer. It may be called multiple times during this interval.
+ void ResetTouchAction();
+
+ TouchAction allowed_touch_action() const { return allowed_touch_action_; }
+
+ // Return the intersection of two TouchAction values.
+ static TouchAction Intersect(TouchAction ta1, TouchAction ta2);
+
private:
+ bool ShouldSuppressScroll(const blink::WebGestureEvent& gesture_event);
+ bool FilterScrollEndingGesture();
+
// Whether GestureScroll events should be discarded due to touch-action.
bool drop_scroll_gesture_events_;
+ // Whether GesturePinch events should be discarded due to touch-action.
+ bool drop_pinch_gesture_events_;
+
+ // Whether a tap ending event in this sequence should be discarded because a
+ // previous GestureTapUnconfirmed event was turned into a GestureTap.
+ bool drop_current_tap_ending_event_;
+
+ // True iff the touch action of the last TapUnconfirmed or Tap event was
+ // TOUCH_ACTION_AUTO. The double tap event depends on the touch action of the
+ // previous tap or tap unconfirmed. Only valid between a TapUnconfirmed or Tap
+ // and the next DoubleTap.
+ bool allow_current_double_tap_event_;
+
// What touch actions are currently permitted.
- content::TouchAction allowed_touch_action_;
+ TouchAction allowed_touch_action_;
DISALLOW_COPY_AND_ASSIGN(TouchActionFilter);
};
diff --git a/chromium/content/browser/renderer_host/input/touch_action_filter_unittest.cc b/chromium/content/browser/renderer_host/input/touch_action_filter_unittest.cc
index a07ef09dfbe..aac1fb664a0 100644
--- a/chromium/content/browser/renderer_host/input/touch_action_filter_unittest.cc
+++ b/chromium/content/browser/renderer_host/input/touch_action_filter_unittest.cc
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "content/browser/renderer_host/event_with_latency_info.h"
#include "content/browser/renderer_host/input/touch_action_filter.h"
+#include "content/common/input/input_event_ack_state.h"
#include "content/common/input/synthetic_web_input_event_builders.h"
-#include "content/port/browser/event_with_latency_info.h"
-#include "content/port/common/input_event_ack_state.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
@@ -17,84 +17,617 @@ namespace content {
TEST(TouchActionFilterTest, SimpleFilter) {
TouchActionFilter filter;
- const WebGestureEvent scroll_begin = SyntheticWebGestureEventBuilder::Build(
- WebInputEvent::GestureScrollBegin, WebGestureEvent::Touchscreen);
- const WebGestureEvent scroll_update =
- SyntheticWebGestureEventBuilder::BuildScrollUpdate(0, 10, 0);
- const WebGestureEvent scroll_end = SyntheticWebGestureEventBuilder::Build(
- WebInputEvent::GestureScrollEnd, WebGestureEvent::Touchscreen);
- const WebGestureEvent tap = SyntheticWebGestureEventBuilder::Build(
- WebInputEvent::GestureTap, WebGestureEvent::Touchscreen);
+ WebGestureEvent scroll_begin =
+ SyntheticWebGestureEventBuilder::BuildScrollBegin(2, 3);
+ const float kDeltaX = 5;
+ const float kDeltaY = 10;
+ WebGestureEvent scroll_update =
+ SyntheticWebGestureEventBuilder::BuildScrollUpdate(kDeltaX, kDeltaY, 0);
+ WebGestureEvent scroll_end = SyntheticWebGestureEventBuilder::Build(
+ WebInputEvent::GestureScrollEnd, blink::WebGestureDeviceTouchscreen);
+ WebGestureEvent tap = SyntheticWebGestureEventBuilder::Build(
+ WebInputEvent::GestureTap, blink::WebGestureDeviceTouchscreen);
// No events filtered by default.
- EXPECT_FALSE(filter.FilterGestureEvent(scroll_begin));
- EXPECT_FALSE(filter.FilterGestureEvent(scroll_update));
- EXPECT_FALSE(filter.FilterGestureEvent(scroll_end));
- EXPECT_FALSE(filter.FilterGestureEvent(tap));
+ filter.ResetTouchAction();
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_begin));
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_update));
+ EXPECT_EQ(kDeltaX, scroll_update.data.scrollUpdate.deltaX);
+ EXPECT_EQ(kDeltaY, scroll_update.data.scrollUpdate.deltaY);
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_end));
+ filter.ResetTouchAction();
+ EXPECT_FALSE(filter.FilterGestureEvent(&tap));
// TOUCH_ACTION_AUTO doesn't cause any filtering.
+ filter.ResetTouchAction();
filter.OnSetTouchAction(TOUCH_ACTION_AUTO);
- EXPECT_FALSE(filter.FilterGestureEvent(scroll_begin));
- EXPECT_FALSE(filter.FilterGestureEvent(scroll_update));
- EXPECT_FALSE(filter.FilterGestureEvent(scroll_end));
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_begin));
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_update));
+ EXPECT_EQ(kDeltaX, scroll_update.data.scrollUpdate.deltaX);
+ EXPECT_EQ(kDeltaY, scroll_update.data.scrollUpdate.deltaY);
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_end));
// TOUCH_ACTION_NONE filters out all scroll events, but no other events.
+ filter.ResetTouchAction();
filter.OnSetTouchAction(TOUCH_ACTION_NONE);
- EXPECT_FALSE(filter.FilterGestureEvent(tap));
- EXPECT_TRUE(filter.FilterGestureEvent(scroll_begin));
- EXPECT_TRUE(filter.FilterGestureEvent(scroll_update));
- EXPECT_TRUE(filter.FilterGestureEvent(scroll_update));
- EXPECT_TRUE(filter.FilterGestureEvent(scroll_end));
+ EXPECT_FALSE(filter.FilterGestureEvent(&tap));
+ EXPECT_TRUE(filter.FilterGestureEvent(&scroll_begin));
+ EXPECT_TRUE(filter.FilterGestureEvent(&scroll_update));
+ EXPECT_TRUE(filter.FilterGestureEvent(&scroll_update));
+ EXPECT_EQ(kDeltaX, scroll_update.data.scrollUpdate.deltaX);
+ EXPECT_EQ(kDeltaY, scroll_update.data.scrollUpdate.deltaY);
+ EXPECT_TRUE(filter.FilterGestureEvent(&scroll_end));
- // After the end of a gesture the state is reset.
- EXPECT_FALSE(filter.FilterGestureEvent(scroll_begin));
- EXPECT_FALSE(filter.FilterGestureEvent(scroll_update));
- EXPECT_FALSE(filter.FilterGestureEvent(scroll_end));
+ // When a new touch sequence begins, the state is reset.
+ filter.ResetTouchAction();
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_begin));
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_update));
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_end));
// Setting touch action doesn't impact any in-progress gestures.
- EXPECT_FALSE(filter.FilterGestureEvent(scroll_begin));
+ filter.ResetTouchAction();
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_begin));
filter.OnSetTouchAction(TOUCH_ACTION_NONE);
- EXPECT_FALSE(filter.FilterGestureEvent(scroll_update));
- EXPECT_FALSE(filter.FilterGestureEvent(scroll_end));
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_update));
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_end));
// And the state is still cleared for the next gesture.
- EXPECT_FALSE(filter.FilterGestureEvent(scroll_begin));
- EXPECT_FALSE(filter.FilterGestureEvent(scroll_end));
+ filter.ResetTouchAction();
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_begin));
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_end));
// Changing the touch action during a gesture has no effect.
+ filter.ResetTouchAction();
filter.OnSetTouchAction(TOUCH_ACTION_NONE);
- EXPECT_TRUE(filter.FilterGestureEvent(scroll_begin));
+ EXPECT_TRUE(filter.FilterGestureEvent(&scroll_begin));
filter.OnSetTouchAction(TOUCH_ACTION_AUTO);
- EXPECT_TRUE(filter.FilterGestureEvent(scroll_update));
- EXPECT_TRUE(filter.FilterGestureEvent(scroll_update));
- EXPECT_TRUE(filter.FilterGestureEvent(scroll_end));
+ EXPECT_TRUE(filter.FilterGestureEvent(&scroll_update));
+ EXPECT_TRUE(filter.FilterGestureEvent(&scroll_update));
+ EXPECT_EQ(kDeltaX, scroll_update.data.scrollUpdate.deltaX);
+ EXPECT_EQ(kDeltaY, scroll_update.data.scrollUpdate.deltaY);
+ EXPECT_TRUE(filter.FilterGestureEvent(&scroll_end));
+ filter.ResetTouchAction();
+}
+
+TEST(TouchActionFilterTest, Fling) {
+ TouchActionFilter filter;
+
+ WebGestureEvent scroll_begin =
+ SyntheticWebGestureEventBuilder::BuildScrollBegin(2, 3);
+ WebGestureEvent scroll_update =
+ SyntheticWebGestureEventBuilder::BuildScrollUpdate(5, 10, 0);
+ const float kFlingX = 7;
+ const float kFlingY = -4;
+ WebGestureEvent fling_start = SyntheticWebGestureEventBuilder::BuildFling(
+ kFlingX, kFlingY, blink::WebGestureDeviceTouchscreen);
+ WebGestureEvent pad_fling = SyntheticWebGestureEventBuilder::BuildFling(
+ kFlingX, kFlingY, blink::WebGestureDeviceTouchpad);
+
+ // TOUCH_ACTION_NONE filters out fling events.
+ filter.ResetTouchAction();
+ filter.OnSetTouchAction(TOUCH_ACTION_NONE);
+ EXPECT_TRUE(filter.FilterGestureEvent(&scroll_begin));
+ EXPECT_TRUE(filter.FilterGestureEvent(&scroll_update));
+ EXPECT_TRUE(filter.FilterGestureEvent(&fling_start));
+ EXPECT_EQ(kFlingX, fling_start.data.flingStart.velocityX);
+ EXPECT_EQ(kFlingY, fling_start.data.flingStart.velocityY);
+
+ // touchpad flings aren't filtered.
+ filter.ResetTouchAction();
+ filter.OnSetTouchAction(TOUCH_ACTION_NONE);
+ EXPECT_TRUE(filter.FilterGestureEvent(&scroll_begin));
+ EXPECT_TRUE(filter.FilterGestureEvent(&scroll_update));
+ EXPECT_FALSE(filter.FilterGestureEvent(&pad_fling));
+ EXPECT_TRUE(filter.FilterGestureEvent(&fling_start));
+ filter.ResetTouchAction();
+}
+
+TEST(TouchActionFilterTest, PanX) {
+ TouchActionFilter filter;
+ const float kDX = 5;
+ const float kDY = 10;
+ const float kFlingX = 7;
+ const float kFlingY = -4;
+ WebGestureEvent scroll_end = SyntheticWebGestureEventBuilder::Build(
+ WebInputEvent::GestureScrollEnd, blink::WebGestureDeviceTouchscreen);
+
+ {
+ // Scrolls with no direction hint are permitted in the X axis.
+ filter.ResetTouchAction();
+ filter.OnSetTouchAction(TOUCH_ACTION_PAN_X);
+
+ WebGestureEvent scroll_begin =
+ SyntheticWebGestureEventBuilder::BuildScrollBegin(0, 0);
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_begin));
+
+ WebGestureEvent scroll_update =
+ SyntheticWebGestureEventBuilder::BuildScrollUpdate(kDX, kDY, 0);
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_update));
+ EXPECT_EQ(kDX, scroll_update.data.scrollUpdate.deltaX);
+ EXPECT_EQ(0, scroll_update.data.scrollUpdate.deltaY);
+
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_end));
+ }
+
+ {
+ // Scrolls hinted mostly in the X axis are permitted in that axis.
+ filter.ResetTouchAction();
+ filter.OnSetTouchAction(TOUCH_ACTION_PAN_X);
+ WebGestureEvent scroll_begin =
+ SyntheticWebGestureEventBuilder::BuildScrollBegin(-7, 6);
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_begin));
+
+ WebGestureEvent scroll_update =
+ SyntheticWebGestureEventBuilder::BuildScrollUpdate(kDX, kDY, 0);
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_update));
+ EXPECT_EQ(kDX, scroll_update.data.scrollUpdate.deltaX);
+ EXPECT_EQ(0, scroll_update.data.scrollUpdate.deltaY);
+
+ WebGestureEvent scroll_update2 =
+ SyntheticWebGestureEventBuilder::BuildScrollUpdate(-4, -2, 0);
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_update2));
+ EXPECT_EQ(-4, scroll_update2.data.scrollUpdate.deltaX);
+ EXPECT_EQ(0, scroll_update2.data.scrollUpdate.deltaY);
+
+ WebGestureEvent fling_start = SyntheticWebGestureEventBuilder::BuildFling(
+ kFlingX, kFlingY, blink::WebGestureDeviceTouchscreen);
+ EXPECT_FALSE(filter.FilterGestureEvent(&fling_start));
+ EXPECT_EQ(kFlingX, fling_start.data.flingStart.velocityX);
+ EXPECT_EQ(0, fling_start.data.flingStart.velocityY);
+ }
+
+ {
+ // Scrolls hinted mostly in the Y direction are suppressed entirely.
+ filter.ResetTouchAction();
+ filter.OnSetTouchAction(TOUCH_ACTION_PAN_X);
+ WebGestureEvent scroll_begin =
+ SyntheticWebGestureEventBuilder::BuildScrollBegin(-7, 8);
+ EXPECT_TRUE(filter.FilterGestureEvent(&scroll_begin));
+
+ WebGestureEvent scroll_update =
+ SyntheticWebGestureEventBuilder::BuildScrollUpdate(kDX, kDY, 0);
+ EXPECT_TRUE(filter.FilterGestureEvent(&scroll_update));
+ EXPECT_EQ(kDX, scroll_update.data.scrollUpdate.deltaX);
+ EXPECT_EQ(kDY, scroll_update.data.scrollUpdate.deltaY);
+
+ EXPECT_TRUE(filter.FilterGestureEvent(&scroll_end));
+ }
+ filter.ResetTouchAction();
+}
+
+TEST(TouchActionFilterTest, PanY) {
+ TouchActionFilter filter;
+ const float kDX = 5;
+ const float kDY = 10;
+ const float kFlingX = 7;
+ const float kFlingY = -4;
+ WebGestureEvent scroll_end = SyntheticWebGestureEventBuilder::Build(
+ WebInputEvent::GestureScrollEnd, blink::WebGestureDeviceTouchscreen);
+
+ {
+ // Scrolls with no direction hint are permitted in the Y axis.
+ filter.ResetTouchAction();
+ filter.OnSetTouchAction(TOUCH_ACTION_PAN_Y);
+
+ WebGestureEvent scroll_begin =
+ SyntheticWebGestureEventBuilder::BuildScrollBegin(0, 0);
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_begin));
+
+ WebGestureEvent scroll_update =
+ SyntheticWebGestureEventBuilder::BuildScrollUpdate(kDX, kDY, 0);
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_update));
+ EXPECT_EQ(0, scroll_update.data.scrollUpdate.deltaX);
+ EXPECT_EQ(kDY, scroll_update.data.scrollUpdate.deltaY);
+
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_end));
+ }
+
+ {
+ // Scrolls hinted mostly in the Y axis are permitted in that axis.
+ filter.ResetTouchAction();
+ filter.OnSetTouchAction(TOUCH_ACTION_PAN_Y);
+ WebGestureEvent scroll_begin =
+ SyntheticWebGestureEventBuilder::BuildScrollBegin(-6, 7);
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_begin));
+
+ WebGestureEvent scroll_update =
+ SyntheticWebGestureEventBuilder::BuildScrollUpdate(kDX, kDY, 0);
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_update));
+ EXPECT_EQ(0, scroll_update.data.scrollUpdate.deltaX);
+ EXPECT_EQ(kDY, scroll_update.data.scrollUpdate.deltaY);
+
+ WebGestureEvent scroll_update2 =
+ SyntheticWebGestureEventBuilder::BuildScrollUpdate(-4, -2, 0);
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_update2));
+ EXPECT_EQ(0, scroll_update2.data.scrollUpdate.deltaX);
+ EXPECT_EQ(-2, scroll_update2.data.scrollUpdate.deltaY);
+
+ WebGestureEvent fling_start = SyntheticWebGestureEventBuilder::BuildFling(
+ kFlingX, kFlingY, blink::WebGestureDeviceTouchscreen);
+ EXPECT_FALSE(filter.FilterGestureEvent(&fling_start));
+ EXPECT_EQ(0, fling_start.data.flingStart.velocityX);
+ EXPECT_EQ(kFlingY, fling_start.data.flingStart.velocityY);
+ }
+
+ {
+ // Scrolls hinted mostly in the X direction are suppressed entirely.
+ filter.ResetTouchAction();
+ filter.OnSetTouchAction(TOUCH_ACTION_PAN_Y);
+ WebGestureEvent scroll_begin =
+ SyntheticWebGestureEventBuilder::BuildScrollBegin(-8, 7);
+ EXPECT_TRUE(filter.FilterGestureEvent(&scroll_begin));
+
+ WebGestureEvent scroll_update =
+ SyntheticWebGestureEventBuilder::BuildScrollUpdate(kDX, kDY, 0);
+ EXPECT_TRUE(filter.FilterGestureEvent(&scroll_update));
+ EXPECT_EQ(kDX, scroll_update.data.scrollUpdate.deltaX);
+ EXPECT_EQ(kDY, scroll_update.data.scrollUpdate.deltaY);
+
+ EXPECT_TRUE(filter.FilterGestureEvent(&scroll_end));
+ }
+ filter.ResetTouchAction();
+}
+
+TEST(TouchActionFilterTest, PanXY) {
+ TouchActionFilter filter;
+ const float kDX = 5;
+ const float kDY = 10;
+ const float kFlingX = 7;
+ const float kFlingY = -4;
+
+ {
+ // Scrolls hinted in the X axis are permitted and unmodified.
+ filter.ResetTouchAction();
+ filter.OnSetTouchAction(TOUCH_ACTION_PAN_X_Y);
+ WebGestureEvent scroll_begin =
+ SyntheticWebGestureEventBuilder::BuildScrollBegin(-7, 6);
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_begin));
+
+ WebGestureEvent scroll_update =
+ SyntheticWebGestureEventBuilder::BuildScrollUpdate(kDX, kDY, 0);
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_update));
+ EXPECT_EQ(kDX, scroll_update.data.scrollUpdate.deltaX);
+ EXPECT_EQ(kDY, scroll_update.data.scrollUpdate.deltaY);
+
+ WebGestureEvent fling_start = SyntheticWebGestureEventBuilder::BuildFling(
+ kFlingX, kFlingY, blink::WebGestureDeviceTouchscreen);
+ EXPECT_FALSE(filter.FilterGestureEvent(&fling_start));
+ EXPECT_EQ(kFlingX, fling_start.data.flingStart.velocityX);
+ EXPECT_EQ(kFlingY, fling_start.data.flingStart.velocityY);
+ }
+
+ {
+ // Scrolls hinted in the Y axis are permitted and unmodified.
+ filter.ResetTouchAction();
+ filter.OnSetTouchAction(TOUCH_ACTION_PAN_X_Y);
+ WebGestureEvent scroll_begin =
+ SyntheticWebGestureEventBuilder::BuildScrollBegin(-6, 7);
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_begin));
+
+ WebGestureEvent scroll_update =
+ SyntheticWebGestureEventBuilder::BuildScrollUpdate(kDX, kDY, 0);
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_update));
+ EXPECT_EQ(kDX, scroll_update.data.scrollUpdate.deltaX);
+ EXPECT_EQ(kDY, scroll_update.data.scrollUpdate.deltaY);
+
+ WebGestureEvent fling_start = SyntheticWebGestureEventBuilder::BuildFling(
+ kFlingX, kFlingY, blink::WebGestureDeviceTouchscreen);
+ EXPECT_FALSE(filter.FilterGestureEvent(&fling_start));
+ EXPECT_EQ(kFlingX, fling_start.data.flingStart.velocityX);
+ EXPECT_EQ(kFlingY, fling_start.data.flingStart.velocityY);
+ }
+ filter.ResetTouchAction();
+}
+
+TEST(TouchActionFilterTest, Intersect) {
+ EXPECT_EQ(TOUCH_ACTION_NONE,
+ TouchActionFilter::Intersect(TOUCH_ACTION_NONE, TOUCH_ACTION_AUTO));
+ EXPECT_EQ(TOUCH_ACTION_NONE,
+ TouchActionFilter::Intersect(TOUCH_ACTION_AUTO, TOUCH_ACTION_NONE));
+ EXPECT_EQ(TOUCH_ACTION_PAN_X,
+ TouchActionFilter::Intersect(TOUCH_ACTION_AUTO, TOUCH_ACTION_PAN_X));
+ EXPECT_EQ(TOUCH_ACTION_PAN_Y,
+ TouchActionFilter::Intersect(TOUCH_ACTION_PAN_Y, TOUCH_ACTION_AUTO));
+ EXPECT_EQ(TOUCH_ACTION_AUTO,
+ TouchActionFilter::Intersect(TOUCH_ACTION_AUTO, TOUCH_ACTION_AUTO));
+ EXPECT_EQ(TOUCH_ACTION_PAN_X,
+ TouchActionFilter::Intersect(TOUCH_ACTION_PAN_X_Y, TOUCH_ACTION_PAN_X));
+ EXPECT_EQ(TOUCH_ACTION_PAN_Y,
+ TouchActionFilter::Intersect(TOUCH_ACTION_PAN_Y, TOUCH_ACTION_PAN_X_Y));
+ EXPECT_EQ(TOUCH_ACTION_PAN_X_Y,
+ TouchActionFilter::Intersect(TOUCH_ACTION_PAN_X_Y, TOUCH_ACTION_AUTO));
+ EXPECT_EQ(TOUCH_ACTION_NONE,
+ TouchActionFilter::Intersect(TOUCH_ACTION_PAN_X, TOUCH_ACTION_PAN_Y));
}
TEST(TouchActionFilterTest, MultiTouch) {
TouchActionFilter filter;
- const WebGestureEvent scroll_begin = SyntheticWebGestureEventBuilder::Build(
- WebInputEvent::GestureScrollBegin, WebGestureEvent::Touchscreen);
- const WebGestureEvent scroll_update =
- SyntheticWebGestureEventBuilder::BuildScrollUpdate(0, 10, 0);
- const WebGestureEvent scrollEnd = SyntheticWebGestureEventBuilder::Build(
- WebInputEvent::GestureScrollEnd, WebGestureEvent::Touchscreen);
+ WebGestureEvent scroll_begin =
+ SyntheticWebGestureEventBuilder::BuildScrollBegin(2, 3);
+ const float kDeltaX = 5;
+ const float kDeltaY = 10;
+ WebGestureEvent scroll_update =
+ SyntheticWebGestureEventBuilder::BuildScrollUpdate(kDeltaX, kDeltaY, 0);
+ WebGestureEvent scroll_end = SyntheticWebGestureEventBuilder::Build(
+ WebInputEvent::GestureScrollEnd, blink::WebGestureDeviceTouchscreen);
// For multiple points, the intersection is what matters.
+ filter.ResetTouchAction();
filter.OnSetTouchAction(TOUCH_ACTION_NONE);
filter.OnSetTouchAction(TOUCH_ACTION_AUTO);
- EXPECT_TRUE(filter.FilterGestureEvent(scroll_begin));
- EXPECT_TRUE(filter.FilterGestureEvent(scroll_update));
- EXPECT_TRUE(filter.FilterGestureEvent(scroll_update));
- EXPECT_TRUE(filter.FilterGestureEvent(scrollEnd));
+ EXPECT_TRUE(filter.FilterGestureEvent(&scroll_begin));
+ EXPECT_TRUE(filter.FilterGestureEvent(&scroll_update));
+ EXPECT_TRUE(filter.FilterGestureEvent(&scroll_update));
+ EXPECT_EQ(kDeltaX, scroll_update.data.scrollUpdate.deltaX);
+ EXPECT_EQ(kDeltaY, scroll_update.data.scrollUpdate.deltaY);
+ EXPECT_TRUE(filter.FilterGestureEvent(&scroll_end));
+
+ // Intersection of PAN_X and PAN_Y is NONE.
+ filter.ResetTouchAction();
+ filter.OnSetTouchAction(TOUCH_ACTION_PAN_X);
+ filter.OnSetTouchAction(TOUCH_ACTION_PAN_Y);
+ filter.OnSetTouchAction(TOUCH_ACTION_PAN_X_Y);
+ EXPECT_TRUE(filter.FilterGestureEvent(&scroll_begin));
+ EXPECT_TRUE(filter.FilterGestureEvent(&scroll_update));
+ EXPECT_TRUE(filter.FilterGestureEvent(&scroll_end));
+ filter.ResetTouchAction();
+}
+
+TEST(TouchActionFilterTest, Pinch) {
+ TouchActionFilter filter;
+
+ WebGestureEvent scroll_begin =
+ SyntheticWebGestureEventBuilder::BuildScrollBegin(2, 3);
+ WebGestureEvent pinch_begin = SyntheticWebGestureEventBuilder::Build(
+ WebInputEvent::GesturePinchBegin, blink::WebGestureDeviceTouchscreen);
+ WebGestureEvent pinch_update =
+ SyntheticWebGestureEventBuilder::BuildPinchUpdate(
+ 1.2f, 5, 5, 0, blink::WebGestureDeviceTouchscreen);
+ WebGestureEvent pinch_end = SyntheticWebGestureEventBuilder::Build(
+ WebInputEvent::GesturePinchEnd, blink::WebGestureDeviceTouchscreen);
+ WebGestureEvent scroll_end = SyntheticWebGestureEventBuilder::Build(
+ WebInputEvent::GestureScrollEnd, blink::WebGestureDeviceTouchscreen);
+ // Pinch is allowed with touch-action: auto.
+ filter.ResetTouchAction();
filter.OnSetTouchAction(TOUCH_ACTION_AUTO);
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_begin));
+ EXPECT_FALSE(filter.FilterGestureEvent(&pinch_begin));
+ EXPECT_FALSE(filter.FilterGestureEvent(&pinch_update));
+ EXPECT_FALSE(filter.FilterGestureEvent(&pinch_end));
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_end));
+
+ // Pinch is not allowed with touch-action: none.
+ filter.ResetTouchAction();
+ filter.OnSetTouchAction(TOUCH_ACTION_NONE);
+ EXPECT_TRUE(filter.FilterGestureEvent(&scroll_begin));
+ EXPECT_TRUE(filter.FilterGestureEvent(&pinch_begin));
+ EXPECT_TRUE(filter.FilterGestureEvent(&pinch_update));
+ EXPECT_TRUE(filter.FilterGestureEvent(&pinch_end));
+ EXPECT_TRUE(filter.FilterGestureEvent(&scroll_end));
+
+ // Pinch is not allowed with touch-action: pan-x pan-y.
+ filter.ResetTouchAction();
+ filter.OnSetTouchAction(TOUCH_ACTION_PAN_X_Y);
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_begin));
+ EXPECT_TRUE(filter.FilterGestureEvent(&pinch_begin));
+ EXPECT_TRUE(filter.FilterGestureEvent(&pinch_update));
+ EXPECT_TRUE(filter.FilterGestureEvent(&pinch_end));
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_end));
+
+ // Pinch is allowed with touch-action: manipulation.
+ filter.ResetTouchAction();
+ filter.OnSetTouchAction(TOUCH_ACTION_MANIPULATION);
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_begin));
+ EXPECT_FALSE(filter.FilterGestureEvent(&pinch_begin));
+ EXPECT_FALSE(filter.FilterGestureEvent(&pinch_update));
+ EXPECT_FALSE(filter.FilterGestureEvent(&pinch_end));
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_end));
+
+ // Pinch state is automatically reset at the end of a scroll.
+ filter.ResetTouchAction();
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_begin));
+ EXPECT_FALSE(filter.FilterGestureEvent(&pinch_begin));
+ EXPECT_FALSE(filter.FilterGestureEvent(&pinch_update));
+ EXPECT_FALSE(filter.FilterGestureEvent(&pinch_end));
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_end));
+
+ // Pinching can become disallowed during a single scroll gesture, but
+ // can't become allowed again until the scroll terminates.
+ // Note that the current TouchEventQueue design makes this scenario
+ // impossible in practice (no touch events are sent to the renderer
+ // while scrolling) and so no SetTouchAction can occur. But this
+ // could change in the future, so it's still worth verifying in this
+ // unit test.
+ filter.ResetTouchAction();
+ filter.OnSetTouchAction(TOUCH_ACTION_AUTO);
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_begin));
+ EXPECT_FALSE(filter.FilterGestureEvent(&pinch_begin));
+ EXPECT_FALSE(filter.FilterGestureEvent(&pinch_update));
+ EXPECT_FALSE(filter.FilterGestureEvent(&pinch_end));
+ filter.OnSetTouchAction(TOUCH_ACTION_NONE);
+ EXPECT_TRUE(filter.FilterGestureEvent(&pinch_begin));
+ EXPECT_TRUE(filter.FilterGestureEvent(&pinch_update));
+ EXPECT_TRUE(filter.FilterGestureEvent(&pinch_end));
+ filter.OnSetTouchAction(TOUCH_ACTION_AUTO);
+ EXPECT_TRUE(filter.FilterGestureEvent(&pinch_begin));
+ EXPECT_TRUE(filter.FilterGestureEvent(&pinch_update));
+ EXPECT_TRUE(filter.FilterGestureEvent(&pinch_end));
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_end));
+
+ // Once a pinch has started, any change in state won't affect the current
+ // pinch gesture, but can affect a future one within the same scroll.
+ filter.ResetTouchAction();
+ filter.OnSetTouchAction(TOUCH_ACTION_AUTO);
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_begin));
+ EXPECT_FALSE(filter.FilterGestureEvent(&pinch_begin));
+ filter.OnSetTouchAction(TOUCH_ACTION_NONE);
+ EXPECT_FALSE(filter.FilterGestureEvent(&pinch_update));
+ EXPECT_FALSE(filter.FilterGestureEvent(&pinch_end));
+ EXPECT_TRUE(filter.FilterGestureEvent(&pinch_begin));
+ EXPECT_TRUE(filter.FilterGestureEvent(&pinch_update));
+ EXPECT_TRUE(filter.FilterGestureEvent(&pinch_end));
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_end));
+ filter.ResetTouchAction();
+}
+
+TEST(TouchActionFilterTest, DoubleTapWithTouchActionAuto) {
+ TouchActionFilter filter;
+
+ WebGestureEvent tap_down = SyntheticWebGestureEventBuilder::Build(
+ WebInputEvent::GestureTapDown, blink::WebGestureDeviceTouchscreen);
+ WebGestureEvent unconfirmed_tap = SyntheticWebGestureEventBuilder::Build(
+ WebInputEvent::GestureTapUnconfirmed, blink::WebGestureDeviceTouchscreen);
+ WebGestureEvent tap_cancel = SyntheticWebGestureEventBuilder::Build(
+ WebInputEvent::GestureTapCancel, blink::WebGestureDeviceTouchscreen);
+ WebGestureEvent double_tap = SyntheticWebGestureEventBuilder::Build(
+ WebInputEvent::GestureDoubleTap, blink::WebGestureDeviceTouchscreen);
+
+ // Double tap is allowed with touch action auto.
+ filter.ResetTouchAction();
+ EXPECT_FALSE(filter.FilterGestureEvent(&tap_down));
+ EXPECT_FALSE(filter.FilterGestureEvent(&unconfirmed_tap));
+ EXPECT_EQ(unconfirmed_tap.type, WebInputEvent::GestureTapUnconfirmed);
+ // The tap cancel will come as part of the next touch sequence.
+ filter.ResetTouchAction();
+ // Changing the touch action for the second tap doesn't effect the behaviour
+ // of the event.
filter.OnSetTouchAction(TOUCH_ACTION_NONE);
+ EXPECT_FALSE(filter.FilterGestureEvent(&tap_cancel));
+ EXPECT_FALSE(filter.FilterGestureEvent(&tap_down));
+ EXPECT_FALSE(filter.FilterGestureEvent(&double_tap));
+ filter.ResetTouchAction();
+}
+
+TEST(TouchActionFilterTest, DoubleTap) {
+ TouchActionFilter filter;
+
+ WebGestureEvent tap_down = SyntheticWebGestureEventBuilder::Build(
+ WebInputEvent::GestureTapDown, blink::WebGestureDeviceTouchscreen);
+ WebGestureEvent unconfirmed_tap = SyntheticWebGestureEventBuilder::Build(
+ WebInputEvent::GestureTapUnconfirmed, blink::WebGestureDeviceTouchscreen);
+ WebGestureEvent tap_cancel = SyntheticWebGestureEventBuilder::Build(
+ WebInputEvent::GestureTapCancel, blink::WebGestureDeviceTouchscreen);
+ WebGestureEvent double_tap = SyntheticWebGestureEventBuilder::Build(
+ WebInputEvent::GestureDoubleTap, blink::WebGestureDeviceTouchscreen);
+
+ // Double tap is disabled with any touch action other than auto.
+ filter.ResetTouchAction();
+ filter.OnSetTouchAction(TOUCH_ACTION_MANIPULATION);
+ EXPECT_FALSE(filter.FilterGestureEvent(&tap_down));
+ EXPECT_FALSE(filter.FilterGestureEvent(&unconfirmed_tap));
+ EXPECT_EQ(WebInputEvent::GestureTap, unconfirmed_tap.type);
+ // Changing the touch action for the second tap doesn't effect the behaviour
+ // of the event. The tap cancel will come as part of the next touch sequence.
+ filter.ResetTouchAction();
filter.OnSetTouchAction(TOUCH_ACTION_AUTO);
- EXPECT_TRUE(filter.FilterGestureEvent(scroll_begin));
- EXPECT_TRUE(filter.FilterGestureEvent(scroll_update));
- EXPECT_TRUE(filter.FilterGestureEvent(scroll_update));
- EXPECT_TRUE(filter.FilterGestureEvent(scrollEnd));
+ EXPECT_TRUE(filter.FilterGestureEvent(&tap_cancel));
+ EXPECT_FALSE(filter.FilterGestureEvent(&tap_down));
+ EXPECT_FALSE(filter.FilterGestureEvent(&double_tap));
+ EXPECT_EQ(WebInputEvent::GestureTap, double_tap.type);
+ filter.ResetTouchAction();
+}
+
+TEST(TouchActionFilterTest, SingleTapWithTouchActionAuto) {
+ TouchActionFilter filter;
+
+ WebGestureEvent tap_down = SyntheticWebGestureEventBuilder::Build(
+ WebInputEvent::GestureTapDown, blink::WebGestureDeviceTouchscreen);
+ WebGestureEvent unconfirmed_tap1 = SyntheticWebGestureEventBuilder::Build(
+ WebInputEvent::GestureTapUnconfirmed, blink::WebGestureDeviceTouchscreen);
+ WebGestureEvent tap = SyntheticWebGestureEventBuilder::Build(
+ WebInputEvent::GestureTap, blink::WebGestureDeviceTouchscreen);
+
+ // Single tap is allowed with touch action auto.
+ filter.ResetTouchAction();
+ EXPECT_FALSE(filter.FilterGestureEvent(&tap_down));
+ EXPECT_FALSE(filter.FilterGestureEvent(&unconfirmed_tap1));
+ EXPECT_EQ(WebInputEvent::GestureTapUnconfirmed, unconfirmed_tap1.type);
+ EXPECT_FALSE(filter.FilterGestureEvent(&tap));
+ filter.ResetTouchAction();
+}
+
+TEST(TouchActionFilterTest, SingleTap) {
+ TouchActionFilter filter;
+
+ WebGestureEvent tap_down = SyntheticWebGestureEventBuilder::Build(
+ WebInputEvent::GestureTapDown, blink::WebGestureDeviceTouchscreen);
+ WebGestureEvent unconfirmed_tap1 = SyntheticWebGestureEventBuilder::Build(
+ WebInputEvent::GestureTapUnconfirmed, blink::WebGestureDeviceTouchscreen);
+ WebGestureEvent tap = SyntheticWebGestureEventBuilder::Build(
+ WebInputEvent::GestureTap, blink::WebGestureDeviceTouchscreen);
+
+ // With touch action other than auto, tap unconfirmed is turned into tap.
+ filter.ResetTouchAction();
+ filter.OnSetTouchAction(TOUCH_ACTION_NONE);
+ EXPECT_FALSE(filter.FilterGestureEvent(&tap_down));
+ EXPECT_FALSE(filter.FilterGestureEvent(&unconfirmed_tap1));
+ EXPECT_EQ(WebInputEvent::GestureTap, unconfirmed_tap1.type);
+ EXPECT_TRUE(filter.FilterGestureEvent(&tap));
+ filter.ResetTouchAction();
+}
+
+TEST(TouchActionFilterTest, TouchActionResetsOnResetTouchAction) {
+ TouchActionFilter filter;
+
+ WebGestureEvent tap = SyntheticWebGestureEventBuilder::Build(
+ WebInputEvent::GestureTap, blink::WebGestureDeviceTouchscreen);
+ WebGestureEvent scroll_begin =
+ SyntheticWebGestureEventBuilder::BuildScrollBegin(2, 3);
+ WebGestureEvent scroll_end = SyntheticWebGestureEventBuilder::Build(
+ WebInputEvent::GestureScrollEnd, blink::WebGestureDeviceTouchscreen);
+
+ filter.ResetTouchAction();
+ filter.OnSetTouchAction(TOUCH_ACTION_NONE);
+ EXPECT_TRUE(filter.FilterGestureEvent(&scroll_begin));
+ EXPECT_TRUE(filter.FilterGestureEvent(&scroll_end));
+
+ filter.ResetTouchAction();
+ filter.OnSetTouchAction(TOUCH_ACTION_NONE);
+ EXPECT_FALSE(filter.FilterGestureEvent(&tap));
+
+ filter.ResetTouchAction();
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_begin));
+}
+
+TEST(TouchActionFilterTest, TouchActionResetMidSequence) {
+ TouchActionFilter filter;
+
+ WebGestureEvent scroll_begin =
+ SyntheticWebGestureEventBuilder::BuildScrollBegin(2, 3);
+ WebGestureEvent pinch_begin = SyntheticWebGestureEventBuilder::Build(
+ WebInputEvent::GesturePinchBegin, blink::WebGestureDeviceTouchscreen);
+ WebGestureEvent pinch_update =
+ SyntheticWebGestureEventBuilder::BuildPinchUpdate(
+ 1.2f, 5, 5, 0, blink::WebGestureDeviceTouchscreen);
+ WebGestureEvent pinch_end = SyntheticWebGestureEventBuilder::Build(
+ WebInputEvent::GesturePinchEnd, blink::WebGestureDeviceTouchscreen);
+ WebGestureEvent scroll_end = SyntheticWebGestureEventBuilder::Build(
+ WebInputEvent::GestureScrollEnd, blink::WebGestureDeviceTouchscreen);
+
+ filter.OnSetTouchAction(TOUCH_ACTION_NONE);
+ EXPECT_TRUE(filter.FilterGestureEvent(&scroll_begin));
+ EXPECT_TRUE(filter.FilterGestureEvent(&pinch_begin));
+ EXPECT_TRUE(filter.FilterGestureEvent(&pinch_update));
+
+ // Even though the allowed action is auto after the reset, the remaining
+ // scroll and pinch events should be suppressed.
+ filter.ResetTouchAction();
+ EXPECT_TRUE(filter.FilterGestureEvent(&pinch_update));
+ EXPECT_TRUE(filter.FilterGestureEvent(&pinch_end));
+ EXPECT_TRUE(filter.FilterGestureEvent(&scroll_end));
+
+ // A new scroll and pinch sequence should be allowed.
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_begin));
+ EXPECT_FALSE(filter.FilterGestureEvent(&pinch_begin));
+ EXPECT_FALSE(filter.FilterGestureEvent(&pinch_update));
+
+ // Resetting from auto to auto mid-stream should have no effect.
+ filter.ResetTouchAction();
+ EXPECT_FALSE(filter.FilterGestureEvent(&pinch_update));
+ EXPECT_FALSE(filter.FilterGestureEvent(&pinch_end));
+ EXPECT_FALSE(filter.FilterGestureEvent(&scroll_end));
}
} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/touch_emulator.cc b/chromium/content/browser/renderer_host/input/touch_emulator.cc
new file mode 100644
index 00000000000..c7d5cc605a7
--- /dev/null
+++ b/chromium/content/browser/renderer_host/input/touch_emulator.cc
@@ -0,0 +1,389 @@
+// 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/renderer_host/input/touch_emulator.h"
+
+#include "content/browser/renderer_host/input/motion_event_web.h"
+#include "content/browser/renderer_host/input/web_input_event_util.h"
+#include "content/common/input/web_touch_event_traits.h"
+#include "content/public/common/content_client.h"
+#include "content/public/common/content_switches.h"
+#include "grit/content_resources.h"
+#include "third_party/WebKit/public/platform/WebCursorInfo.h"
+#include "ui/events/gesture_detection/gesture_config_helper.h"
+#include "ui/gfx/image/image.h"
+#include "ui/gfx/screen.h"
+
+using blink::WebGestureEvent;
+using blink::WebInputEvent;
+using blink::WebKeyboardEvent;
+using blink::WebMouseEvent;
+using blink::WebMouseWheelEvent;
+using blink::WebTouchEvent;
+using blink::WebTouchPoint;
+
+namespace content {
+
+namespace {
+
+ui::GestureProvider::Config GetGestureProviderConfig() {
+ // TODO(dgozman): Use different configs to emulate mobile/desktop as
+ // requested by renderer.
+ ui::GestureProvider::Config config = ui::DefaultGestureProviderConfig();
+ config.gesture_begin_end_types_enabled = false;
+ config.gesture_detector_config.swipe_enabled = false;
+ config.gesture_detector_config.two_finger_tap_enabled = false;
+ return config;
+}
+
+// Time between two consecutive mouse moves, during which second mouse move
+// is not converted to touch.
+const double kMouseMoveDropIntervalSeconds = 5.f / 1000;
+
+} // namespace
+
+TouchEmulator::TouchEmulator(TouchEmulatorClient* client)
+ : client_(client),
+ gesture_provider_(GetGestureProviderConfig(), this),
+ enabled_(false),
+ allow_pinch_(false) {
+ DCHECK(client_);
+ ResetState();
+
+ bool use_2x = gfx::Screen::GetNativeScreen()->
+ GetPrimaryDisplay().device_scale_factor() > 1.5f;
+ float cursor_scale_factor = use_2x ? 2.f : 1.f;
+ InitCursorFromResource(&touch_cursor_,
+ cursor_scale_factor,
+ use_2x ? IDR_DEVTOOLS_TOUCH_CURSOR_ICON_2X :
+ IDR_DEVTOOLS_TOUCH_CURSOR_ICON);
+ InitCursorFromResource(&pinch_cursor_,
+ cursor_scale_factor,
+ use_2x ? IDR_DEVTOOLS_PINCH_CURSOR_ICON_2X :
+ IDR_DEVTOOLS_PINCH_CURSOR_ICON);
+
+ WebCursor::CursorInfo cursor_info;
+ cursor_info.type = blink::WebCursorInfo::TypePointer;
+ pointer_cursor_.InitFromCursorInfo(cursor_info);
+
+ // TODO(dgozman): Use synthetic secondary touch to support multi-touch.
+ gesture_provider_.SetMultiTouchZoomSupportEnabled(false);
+ // TODO(dgozman): Enable double tap if requested by the renderer.
+ // TODO(dgozman): Don't break double-tap-based pinch with shift handling.
+ gesture_provider_.SetDoubleTapSupportForPlatformEnabled(false);
+}
+
+TouchEmulator::~TouchEmulator() {
+ // We cannot cleanup properly in destructor, as we need roundtrip to the
+ // renderer for ack. Instead, the owner should call Disable, and only
+ // destroy this object when renderer is dead.
+}
+
+void TouchEmulator::ResetState() {
+ last_mouse_event_was_move_ = false;
+ last_mouse_move_timestamp_ = 0;
+ mouse_pressed_ = false;
+ shift_pressed_ = false;
+ touch_active_ = false;
+ suppress_next_fling_cancel_ = false;
+ pinch_scale_ = 1.f;
+ pinch_gesture_active_ = false;
+}
+
+void TouchEmulator::Enable(bool allow_pinch) {
+ if (!enabled_) {
+ enabled_ = true;
+ ResetState();
+ }
+ allow_pinch_ = allow_pinch;
+ UpdateCursor();
+}
+
+void TouchEmulator::Disable() {
+ if (!enabled_)
+ return;
+
+ enabled_ = false;
+ UpdateCursor();
+ CancelTouch();
+}
+
+void TouchEmulator::InitCursorFromResource(
+ WebCursor* cursor, float scale, int resource_id) {
+ gfx::Image& cursor_image =
+ content::GetContentClient()->GetNativeImageNamed(resource_id);
+ WebCursor::CursorInfo cursor_info;
+ cursor_info.type = blink::WebCursorInfo::TypeCustom;
+ cursor_info.image_scale_factor = scale;
+ cursor_info.custom_image = cursor_image.AsBitmap();
+ cursor_info.hotspot =
+ gfx::Point(cursor_image.Width() / 2, cursor_image.Height() / 2);
+#if defined(OS_WIN)
+ cursor_info.external_handle = 0;
+#endif
+
+ cursor->InitFromCursorInfo(cursor_info);
+}
+
+bool TouchEmulator::HandleMouseEvent(const WebMouseEvent& mouse_event) {
+ if (!enabled_)
+ return false;
+
+ if (mouse_event.button != WebMouseEvent::ButtonLeft)
+ return true;
+
+ if (mouse_event.type == WebInputEvent::MouseMove) {
+ if (last_mouse_event_was_move_ &&
+ mouse_event.timeStampSeconds < last_mouse_move_timestamp_ +
+ kMouseMoveDropIntervalSeconds)
+ return true;
+
+ last_mouse_event_was_move_ = true;
+ last_mouse_move_timestamp_ = mouse_event.timeStampSeconds;
+ } else {
+ last_mouse_event_was_move_ = false;
+ }
+
+ if (mouse_event.type == WebInputEvent::MouseDown)
+ mouse_pressed_ = true;
+ else if (mouse_event.type == WebInputEvent::MouseUp)
+ mouse_pressed_ = false;
+
+ UpdateShiftPressed((mouse_event.modifiers & WebInputEvent::ShiftKey) != 0);
+
+ if (FillTouchEventAndPoint(mouse_event) &&
+ gesture_provider_.OnTouchEvent(MotionEventWeb(touch_event_))) {
+ client_->ForwardTouchEvent(touch_event_);
+ }
+
+ // Do not pass mouse events to the renderer.
+ return true;
+}
+
+bool TouchEmulator::HandleMouseWheelEvent(const WebMouseWheelEvent& event) {
+ if (!enabled_)
+ return false;
+
+ // Send mouse wheel for easy scrolling when there is no active touch.
+ return touch_active_;
+}
+
+bool TouchEmulator::HandleKeyboardEvent(const WebKeyboardEvent& event) {
+ if (!enabled_)
+ return false;
+
+ if (!UpdateShiftPressed((event.modifiers & WebInputEvent::ShiftKey) != 0))
+ return false;
+
+ if (!mouse_pressed_)
+ return false;
+
+ // Note: The necessary pinch events will be lazily inserted by
+ // |OnGestureEvent| depending on the state of |shift_pressed_|, using the
+ // scroll stream as the event driver.
+ if (shift_pressed_) {
+ // TODO(dgozman): Add secondary touch point and set anchor.
+ } else {
+ // TODO(dgozman): Remove secondary touch point and anchor.
+ }
+
+ // Never block keyboard events.
+ return false;
+}
+
+bool TouchEmulator::HandleTouchEventAck(InputEventAckState ack_result) {
+ const bool event_consumed = ack_result == INPUT_EVENT_ACK_STATE_CONSUMED;
+ gesture_provider_.OnTouchEventAck(event_consumed);
+ // TODO(dgozman): Disable emulation when real touch events are available.
+ return true;
+}
+
+void TouchEmulator::OnGestureEvent(const ui::GestureEventData& gesture) {
+ WebGestureEvent gesture_event =
+ CreateWebGestureEventFromGestureEventData(gesture);
+
+ switch (gesture_event.type) {
+ case WebInputEvent::Undefined:
+ NOTREACHED() << "Undefined WebInputEvent type";
+ // Bail without sending the junk event to the client.
+ return;
+
+ case WebInputEvent::GestureScrollBegin:
+ client_->ForwardGestureEvent(gesture_event);
+ // PinchBegin must always follow ScrollBegin.
+ if (InPinchGestureMode())
+ PinchBegin(gesture_event);
+ break;
+
+ case WebInputEvent::GestureScrollUpdate:
+ if (InPinchGestureMode()) {
+ // Convert scrolls to pinches while shift is pressed.
+ if (!pinch_gesture_active_)
+ PinchBegin(gesture_event);
+ else
+ PinchUpdate(gesture_event);
+ } else {
+ // Pass scroll update further. If shift was released, end the pinch.
+ if (pinch_gesture_active_)
+ PinchEnd(gesture_event);
+ client_->ForwardGestureEvent(gesture_event);
+ }
+ break;
+
+ case WebInputEvent::GestureScrollEnd:
+ // PinchEnd must precede ScrollEnd.
+ if (pinch_gesture_active_)
+ PinchEnd(gesture_event);
+ client_->ForwardGestureEvent(gesture_event);
+ break;
+
+ case WebInputEvent::GestureFlingStart:
+ // PinchEnd must precede FlingStart.
+ if (pinch_gesture_active_)
+ PinchEnd(gesture_event);
+ if (InPinchGestureMode()) {
+ // No fling in pinch mode. Forward scroll end instead of fling start.
+ suppress_next_fling_cancel_ = true;
+ ScrollEnd(gesture_event);
+ } else {
+ suppress_next_fling_cancel_ = false;
+ client_->ForwardGestureEvent(gesture_event);
+ }
+ break;
+
+ case WebInputEvent::GestureFlingCancel:
+ // If fling start was suppressed, we should not send fling cancel either.
+ if (!suppress_next_fling_cancel_)
+ client_->ForwardGestureEvent(gesture_event);
+ suppress_next_fling_cancel_ = false;
+ break;
+
+ default:
+ // Everything else goes through.
+ client_->ForwardGestureEvent(gesture_event);
+ }
+}
+
+void TouchEmulator::CancelTouch() {
+ if (!touch_active_)
+ return;
+
+ WebTouchEventTraits::ResetTypeAndTouchStates(
+ WebInputEvent::TouchCancel,
+ (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF(),
+ &touch_event_);
+ touch_active_ = false;
+ if (gesture_provider_.OnTouchEvent(MotionEventWeb(touch_event_)))
+ client_->ForwardTouchEvent(touch_event_);
+}
+
+void TouchEmulator::UpdateCursor() {
+ if (!enabled_)
+ client_->SetCursor(pointer_cursor_);
+ else
+ client_->SetCursor(InPinchGestureMode() ? pinch_cursor_ : touch_cursor_);
+}
+
+bool TouchEmulator::UpdateShiftPressed(bool shift_pressed) {
+ if (shift_pressed_ == shift_pressed)
+ return false;
+ shift_pressed_ = shift_pressed;
+ UpdateCursor();
+ return true;
+}
+
+void TouchEmulator::PinchBegin(const WebGestureEvent& event) {
+ DCHECK(InPinchGestureMode());
+ DCHECK(!pinch_gesture_active_);
+ pinch_gesture_active_ = true;
+ pinch_anchor_ = gfx::Point(event.x, event.y);
+ pinch_scale_ = 1.f;
+ FillPinchEvent(event);
+ pinch_event_.type = WebInputEvent::GesturePinchBegin;
+ client_->ForwardGestureEvent(pinch_event_);
+}
+
+void TouchEmulator::PinchUpdate(const WebGestureEvent& event) {
+ DCHECK(pinch_gesture_active_);
+ int dy = pinch_anchor_.y() - event.y;
+ float scale = exp(dy * 0.002f);
+ FillPinchEvent(event);
+ pinch_event_.type = WebInputEvent::GesturePinchUpdate;
+ pinch_event_.data.pinchUpdate.scale = scale / pinch_scale_;
+ client_->ForwardGestureEvent(pinch_event_);
+ pinch_scale_ = scale;
+}
+
+void TouchEmulator::PinchEnd(const WebGestureEvent& event) {
+ DCHECK(pinch_gesture_active_);
+ pinch_gesture_active_ = false;
+ FillPinchEvent(event);
+ pinch_event_.type = WebInputEvent::GesturePinchEnd;
+ client_->ForwardGestureEvent(pinch_event_);
+}
+
+void TouchEmulator::FillPinchEvent(const WebInputEvent& event) {
+ pinch_event_.timeStampSeconds = event.timeStampSeconds;
+ pinch_event_.modifiers = event.modifiers;
+ pinch_event_.sourceDevice = blink::WebGestureDeviceTouchscreen;
+ pinch_event_.x = pinch_anchor_.x();
+ pinch_event_.y = pinch_anchor_.y();
+}
+
+void TouchEmulator::ScrollEnd(const WebGestureEvent& event) {
+ WebGestureEvent scroll_event;
+ scroll_event.timeStampSeconds = event.timeStampSeconds;
+ scroll_event.modifiers = event.modifiers;
+ scroll_event.sourceDevice = blink::WebGestureDeviceTouchscreen;
+ scroll_event.type = WebInputEvent::GestureScrollEnd;
+ client_->ForwardGestureEvent(scroll_event);
+}
+
+bool TouchEmulator::FillTouchEventAndPoint(const WebMouseEvent& mouse_event) {
+ if (mouse_event.type != WebInputEvent::MouseDown &&
+ mouse_event.type != WebInputEvent::MouseMove &&
+ mouse_event.type != WebInputEvent::MouseUp) {
+ return false;
+ }
+
+ WebInputEvent::Type eventType;
+ switch (mouse_event.type) {
+ case WebInputEvent::MouseDown:
+ eventType = WebInputEvent::TouchStart;
+ touch_active_ = true;
+ break;
+ case WebInputEvent::MouseMove:
+ eventType = WebInputEvent::TouchMove;
+ break;
+ case WebInputEvent::MouseUp:
+ eventType = WebInputEvent::TouchEnd;
+ touch_active_ = false;
+ break;
+ default:
+ eventType = WebInputEvent::Undefined;
+ NOTREACHED();
+ }
+ touch_event_.touchesLength = 1;
+ touch_event_.modifiers = mouse_event.modifiers;
+ WebTouchEventTraits::ResetTypeAndTouchStates(
+ eventType, mouse_event.timeStampSeconds, &touch_event_);
+
+ WebTouchPoint& point = touch_event_.touches[0];
+ point.id = 0;
+ point.radiusX = point.radiusY = 1.f;
+ point.force = 1.f;
+ point.rotationAngle = 0.f;
+ point.position.x = mouse_event.x;
+ point.screenPosition.x = mouse_event.globalX;
+ point.position.y = mouse_event.y;
+ point.screenPosition.y = mouse_event.globalY;
+
+ return true;
+}
+
+bool TouchEmulator::InPinchGestureMode() const {
+ return shift_pressed_ && allow_pinch_;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/touch_emulator.h b/chromium/content/browser/renderer_host/input/touch_emulator.h
new file mode 100644
index 00000000000..af1e056db30
--- /dev/null
+++ b/chromium/content/browser/renderer_host/input/touch_emulator.h
@@ -0,0 +1,100 @@
+// 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_RENDERER_HOST_INPUT_TOUCH_EMULATOR_H_
+#define CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCH_EMULATOR_H_
+
+#include "content/browser/renderer_host/input/touch_emulator_client.h"
+#include "content/common/cursors/webcursor.h"
+#include "content/common/input/input_event_ack_state.h"
+#include "third_party/WebKit/public/web/WebInputEvent.h"
+#include "ui/events/gesture_detection/filtered_gesture_provider.h"
+
+namespace content {
+
+// Emulates touch input with mouse and keyboard.
+class CONTENT_EXPORT TouchEmulator : public ui::GestureProviderClient {
+ public:
+ explicit TouchEmulator(TouchEmulatorClient* client);
+ virtual ~TouchEmulator();
+
+ void Enable(bool allow_pinch);
+ void Disable();
+
+ // Returns |true| if the event was consumed.
+ // TODO(dgozman): maybe pass latency info together with events.
+ bool HandleMouseEvent(const blink::WebMouseEvent& event);
+ bool HandleMouseWheelEvent(const blink::WebMouseWheelEvent& event);
+ bool HandleKeyboardEvent(const blink::WebKeyboardEvent& event);
+
+ // Returns |true| if the event ack was consumed. Consumed ack should not
+ // propagate any further.
+ bool HandleTouchEventAck(InputEventAckState ack_result);
+
+ // Cancel any touches, for example, when focus is lost.
+ void CancelTouch();
+
+ private:
+ // ui::GestureProviderClient implementation.
+ virtual void OnGestureEvent(const ui::GestureEventData& gesture) OVERRIDE;
+
+ void InitCursorFromResource(WebCursor* cursor, float scale, int resource_id);
+ void ResetState();
+ void UpdateCursor();
+ bool UpdateShiftPressed(bool shift_pressed);
+
+ // Whether we should convert scrolls into pinches.
+ bool InPinchGestureMode() const;
+
+ bool FillTouchEventAndPoint(const blink::WebMouseEvent& mouse_event);
+ void FillPinchEvent(const blink::WebInputEvent& event);
+
+ // The following methods generate and pass gesture events to the renderer.
+ void PinchBegin(const blink::WebGestureEvent& event);
+ void PinchUpdate(const blink::WebGestureEvent& event);
+ void PinchEnd(const blink::WebGestureEvent& event);
+ void ScrollEnd(const blink::WebGestureEvent& event);
+
+ TouchEmulatorClient* const client_;
+ ui::FilteredGestureProvider gesture_provider_;
+
+ // Disabled emulator does only process touch acks left from previous
+ // emulation. It does not intercept any events.
+ bool enabled_;
+ bool allow_pinch_;
+
+ // While emulation is on, default cursor is touch. Pressing shift changes
+ // cursor to the pinch one.
+ WebCursor pointer_cursor_;
+ WebCursor touch_cursor_;
+ WebCursor pinch_cursor_;
+
+ // These are used to drop extra mouse move events coming too quickly, so
+ // we don't handle too much touches in gesture provider.
+ bool last_mouse_event_was_move_;
+ double last_mouse_move_timestamp_;
+
+ bool mouse_pressed_;
+ bool shift_pressed_;
+
+ blink::WebTouchEvent touch_event_;
+ bool touch_active_;
+
+ // Whether we should suppress next fling cancel. This may happen when we
+ // did not send fling start in pinch mode.
+ bool suppress_next_fling_cancel_;
+
+ blink::WebGestureEvent pinch_event_;
+ // Point which does not move while pinch-zooming.
+ gfx::Point pinch_anchor_;
+ // The cumulative scale change from the start of pinch gesture.
+ float pinch_scale_;
+ bool pinch_gesture_active_;
+
+ DISALLOW_COPY_AND_ASSIGN(TouchEmulator);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCH_EMULATOR_H_
diff --git a/chromium/content/browser/renderer_host/input/touch_emulator_client.h b/chromium/content/browser/renderer_host/input/touch_emulator_client.h
new file mode 100644
index 00000000000..92f382f5f9c
--- /dev/null
+++ b/chromium/content/browser/renderer_host/input/touch_emulator_client.h
@@ -0,0 +1,26 @@
+// 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_RENDERER_HOST_INPUT_TOUCH_EMULATOR_CLIENT_H_
+#define CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCH_EMULATOR_CLIENT_H_
+
+#include "content/common/content_export.h"
+#include "content/common/cursors/webcursor.h"
+#include "third_party/WebKit/public/web/WebInputEvent.h"
+
+namespace content {
+
+// Emulates touch input with mouse and keyboard.
+class CONTENT_EXPORT TouchEmulatorClient {
+ public:
+ virtual ~TouchEmulatorClient() {}
+
+ virtual void ForwardGestureEvent(const blink::WebGestureEvent& event) = 0;
+ virtual void ForwardTouchEvent(const blink::WebTouchEvent& event) = 0;
+ virtual void SetCursor(const WebCursor& cursor) = 0;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCH_EMULATOR_CLIENT_H_
diff --git a/chromium/content/browser/renderer_host/input/touch_emulator_unittest.cc b/chromium/content/browser/renderer_host/input/touch_emulator_unittest.cc
new file mode 100644
index 00000000000..8fdfd54cce2
--- /dev/null
+++ b/chromium/content/browser/renderer_host/input/touch_emulator_unittest.cc
@@ -0,0 +1,349 @@
+// 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 <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/time/time.h"
+#include "content/browser/renderer_host/input/touch_emulator.h"
+#include "content/browser/renderer_host/input/touch_emulator_client.h"
+#include "content/common/input/web_input_event_traits.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/gesture_detection/gesture_config_helper.h"
+
+#if defined(USE_AURA)
+#include "ui/aura/env.h"
+#include "ui/aura/test/test_screen.h"
+#endif
+
+using blink::WebGestureEvent;
+using blink::WebInputEvent;
+using blink::WebKeyboardEvent;
+using blink::WebMouseEvent;
+using blink::WebMouseWheelEvent;
+using blink::WebTouchEvent;
+using blink::WebTouchPoint;
+
+namespace content {
+
+class TouchEmulatorTest : public testing::Test,
+ public TouchEmulatorClient {
+ public:
+ TouchEmulatorTest()
+ : shift_pressed_(false),
+ mouse_pressed_(false),
+ last_mouse_x_(-1),
+ last_mouse_y_(-1) {
+ last_event_time_seconds_ =
+ (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF();
+ event_time_delta_seconds_ = 0.1;
+ }
+
+ virtual ~TouchEmulatorTest() {}
+
+ // testing::Test
+ virtual void SetUp() OVERRIDE {
+#if defined(USE_AURA)
+ aura::Env::CreateInstance(true);
+ screen_.reset(aura::TestScreen::Create(gfx::Size()));
+ gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, screen_.get());
+#endif
+
+ emulator_.reset(new TouchEmulator(this));
+ emulator_->Enable(true /* allow_pinch */);
+ }
+
+ virtual void TearDown() OVERRIDE {
+ emulator_->Disable();
+ EXPECT_EQ("", ExpectedEvents());
+
+#if defined(USE_AURA)
+ aura::Env::DeleteInstance();
+ screen_.reset();
+#endif
+ }
+
+ virtual void ForwardGestureEvent(
+ const blink::WebGestureEvent& event) OVERRIDE {
+ forwarded_events_.push_back(event.type);
+ }
+
+ virtual void ForwardTouchEvent(
+ const blink::WebTouchEvent& event) OVERRIDE {
+ forwarded_events_.push_back(event.type);
+ EXPECT_EQ(1U, event.touchesLength);
+ EXPECT_EQ(last_mouse_x_, event.touches[0].position.x);
+ EXPECT_EQ(last_mouse_y_, event.touches[0].position.y);
+ int expectedCancelable = event.type != WebInputEvent::TouchCancel;
+ EXPECT_EQ(expectedCancelable, event.cancelable);
+ emulator()->HandleTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
+ }
+
+ virtual void SetCursor(const WebCursor& cursor) OVERRIDE {}
+
+ protected:
+ TouchEmulator* emulator() const {
+ return emulator_.get();
+ }
+
+ int modifiers() const {
+ return shift_pressed_ ? WebInputEvent::ShiftKey : 0;
+ }
+
+ std::string ExpectedEvents() {
+ std::string result;
+ for (size_t i = 0; i < forwarded_events_.size(); ++i) {
+ if (i != 0)
+ result += " ";
+ result += WebInputEventTraits::GetName(forwarded_events_[i]);
+ }
+ forwarded_events_.clear();
+ return result;
+ }
+
+ double GetNextEventTimeSeconds() {
+ last_event_time_seconds_ += event_time_delta_seconds_;
+ return last_event_time_seconds_;
+ }
+
+ void set_event_time_delta_seconds_(double delta) {
+ event_time_delta_seconds_ = delta;
+ }
+
+ void SendKeyboardEvent(WebInputEvent::Type type) {
+ WebKeyboardEvent event;
+ event.timeStampSeconds = GetNextEventTimeSeconds();
+ event.type = type;
+ event.modifiers = modifiers();
+ emulator()->HandleKeyboardEvent(event);
+ }
+
+ void PressShift() {
+ DCHECK(!shift_pressed_);
+ shift_pressed_ = true;
+ SendKeyboardEvent(WebInputEvent::KeyDown);
+ }
+
+ void ReleaseShift() {
+ DCHECK(shift_pressed_);
+ shift_pressed_ = false;
+ SendKeyboardEvent(WebInputEvent::KeyUp);
+ }
+
+ void SendMouseEvent(WebInputEvent::Type type, int x, int y) {
+ WebMouseEvent event;
+ event.timeStampSeconds = GetNextEventTimeSeconds();
+ event.type = type;
+ event.button = mouse_pressed_ ? WebMouseEvent::ButtonLeft :
+ WebMouseEvent::ButtonNone;
+ event.modifiers = modifiers();
+ last_mouse_x_ = x;
+ last_mouse_y_ = y;
+ event.x = event.windowX = event.globalX = x;
+ event.y = event.windowY = event.globalY = y;
+ emulator()->HandleMouseEvent(event);
+ }
+
+ bool SendMouseWheelEvent() {
+ WebMouseWheelEvent event;
+ event.type = WebInputEvent::MouseWheel;
+ event.timeStampSeconds = GetNextEventTimeSeconds();
+ // Return whether mouse wheel is forwarded.
+ return !emulator()->HandleMouseWheelEvent(event);
+ }
+
+ void MouseDown(int x, int y) {
+ DCHECK(!mouse_pressed_);
+ if (x != last_mouse_x_ || y != last_mouse_y_)
+ SendMouseEvent(WebInputEvent::MouseMove, x, y);
+ mouse_pressed_ = true;
+ SendMouseEvent(WebInputEvent::MouseDown, x, y);
+ }
+
+ void MouseDrag(int x, int y) {
+ DCHECK(mouse_pressed_);
+ SendMouseEvent(WebInputEvent::MouseMove, x, y);
+ }
+
+ void MouseMove(int x, int y) {
+ DCHECK(!mouse_pressed_);
+ SendMouseEvent(WebInputEvent::MouseMove, x, y);
+ }
+
+ void MouseUp(int x, int y) {
+ DCHECK(mouse_pressed_);
+ if (x != last_mouse_x_ || y != last_mouse_y_)
+ SendMouseEvent(WebInputEvent::MouseMove, x, y);
+ SendMouseEvent(WebInputEvent::MouseUp, x, y);
+ mouse_pressed_ = false;
+ }
+
+ private:
+ scoped_ptr<TouchEmulator> emulator_;
+ std::vector<WebInputEvent::Type> forwarded_events_;
+#if defined(USE_AURA)
+ scoped_ptr<gfx::Screen> screen_;
+#endif
+ double last_event_time_seconds_;
+ double event_time_delta_seconds_;
+ bool shift_pressed_;
+ bool mouse_pressed_;
+ int last_mouse_x_;
+ int last_mouse_y_;
+ base::MessageLoopForUI message_loop_;
+};
+
+
+TEST_F(TouchEmulatorTest, NoTouches) {
+ MouseMove(100, 200);
+ MouseMove(300, 300);
+ EXPECT_EQ("", ExpectedEvents());
+}
+
+TEST_F(TouchEmulatorTest, Touch) {
+ MouseMove(100, 200);
+ EXPECT_EQ("", ExpectedEvents());
+ MouseDown(100, 200);
+ EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents());
+ MouseUp(200, 200);
+ EXPECT_EQ(
+ "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate"
+ " TouchEnd GestureScrollEnd",
+ ExpectedEvents());
+}
+
+TEST_F(TouchEmulatorTest, MultipleTouches) {
+ MouseMove(100, 200);
+ EXPECT_EQ("", ExpectedEvents());
+ MouseDown(100, 200);
+ EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents());
+ MouseUp(200, 200);
+ EXPECT_EQ(
+ "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate"
+ " TouchEnd GestureScrollEnd",
+ ExpectedEvents());
+ MouseMove(300, 200);
+ MouseMove(200, 200);
+ EXPECT_EQ("", ExpectedEvents());
+ MouseDown(300, 200);
+ EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents());
+ MouseDrag(300, 300);
+ EXPECT_EQ(
+ "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate",
+ ExpectedEvents());
+ MouseDrag(300, 400);
+ EXPECT_EQ("TouchMove GestureScrollUpdate", ExpectedEvents());
+ MouseUp(300, 500);
+ EXPECT_EQ(
+ "TouchMove GestureScrollUpdate TouchEnd GestureScrollEnd",
+ ExpectedEvents());
+}
+
+TEST_F(TouchEmulatorTest, Pinch) {
+ MouseDown(100, 200);
+ EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents());
+ MouseDrag(200, 200);
+ EXPECT_EQ(
+ "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate",
+ ExpectedEvents());
+ PressShift();
+ EXPECT_EQ("", ExpectedEvents());
+ MouseDrag(300, 200);
+ EXPECT_EQ("TouchMove GesturePinchBegin", ExpectedEvents());
+ ReleaseShift();
+ EXPECT_EQ("", ExpectedEvents());
+ MouseDrag(400, 200);
+ EXPECT_EQ(
+ "TouchMove GesturePinchEnd GestureScrollUpdate",
+ ExpectedEvents());
+ MouseUp(400, 200);
+ EXPECT_EQ("TouchEnd GestureScrollEnd", ExpectedEvents());
+}
+
+TEST_F(TouchEmulatorTest, DisableAndReenable) {
+ MouseDown(100, 200);
+ EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents());
+ MouseDrag(200, 200);
+ EXPECT_EQ(
+ "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate",
+ ExpectedEvents());
+ PressShift();
+ MouseDrag(300, 200);
+ EXPECT_EQ("TouchMove GesturePinchBegin", ExpectedEvents());
+
+ // Disable while pinch is in progress.
+ emulator()->Disable();
+ EXPECT_EQ("TouchCancel GesturePinchEnd GestureScrollEnd", ExpectedEvents());
+ MouseUp(300, 200);
+ ReleaseShift();
+ MouseMove(300, 300);
+ EXPECT_EQ("", ExpectedEvents());
+
+ emulator()->Enable(true /* allow_pinch */);
+ MouseDown(300, 300);
+ EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents());
+ MouseDrag(300, 400);
+ EXPECT_EQ(
+ "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate",
+ ExpectedEvents());
+
+ // Disable while scroll is in progress.
+ emulator()->Disable();
+ EXPECT_EQ("TouchCancel GestureScrollEnd", ExpectedEvents());
+}
+
+TEST_F(TouchEmulatorTest, MouseMovesDropped) {
+ MouseMove(100, 200);
+ EXPECT_EQ("", ExpectedEvents());
+ MouseDown(100, 200);
+ EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents());
+
+ // Mouse move after mouse down is never dropped.
+ set_event_time_delta_seconds_(0.001);
+ MouseDrag(200, 200);
+ EXPECT_EQ(
+ "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate",
+ ExpectedEvents());
+
+ // The following mouse moves are dropped.
+ MouseDrag(300, 200);
+ EXPECT_EQ("", ExpectedEvents());
+ MouseDrag(350, 200);
+ EXPECT_EQ("", ExpectedEvents());
+
+ // Dispatching again.
+ set_event_time_delta_seconds_(0.1);
+ MouseDrag(400, 200);
+ EXPECT_EQ(
+ "TouchMove GestureScrollUpdate",
+ ExpectedEvents());
+ MouseUp(400, 200);
+ EXPECT_EQ(
+ "TouchEnd GestureScrollEnd",
+ ExpectedEvents());
+}
+
+TEST_F(TouchEmulatorTest, MouseWheel) {
+ MouseMove(100, 200);
+ EXPECT_EQ("", ExpectedEvents());
+ EXPECT_TRUE(SendMouseWheelEvent());
+ MouseDown(100, 200);
+ EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents());
+ EXPECT_FALSE(SendMouseWheelEvent());
+ MouseUp(100, 200);
+ EXPECT_EQ("TouchEnd GestureShowPress GestureTap", ExpectedEvents());
+ EXPECT_TRUE(SendMouseWheelEvent());
+ MouseDown(300, 200);
+ EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents());
+ EXPECT_FALSE(SendMouseWheelEvent());
+ emulator()->Disable();
+ EXPECT_EQ("TouchCancel GestureTapCancel", ExpectedEvents());
+ EXPECT_TRUE(SendMouseWheelEvent());
+ emulator()->Enable(true /* allow_pinch */);
+ EXPECT_TRUE(SendMouseWheelEvent());
+}
+
+} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/touch_event_queue.cc b/chromium/content/browser/renderer_host/input/touch_event_queue.cc
index 3524b18250e..bbac01c3ebf 100644
--- a/chromium/content/browser/renderer_host/input/touch_event_queue.cc
+++ b/chromium/content/browser/renderer_host/input/touch_event_queue.cc
@@ -5,67 +5,79 @@
#include "content/browser/renderer_host/input/touch_event_queue.h"
#include "base/auto_reset.h"
-#include "base/command_line.h"
#include "base/debug/trace_event.h"
#include "base/stl_util.h"
#include "content/browser/renderer_host/input/timeout_monitor.h"
-#include "content/common/input/web_input_event_traits.h"
-#include "content/public/common/content_switches.h"
+#include "content/common/input/web_touch_event_traits.h"
+#include "ui/gfx/geometry/point_f.h"
using blink::WebInputEvent;
using blink::WebTouchEvent;
using blink::WebTouchPoint;
+using ui::LatencyInfo;
namespace content {
namespace {
-const InputEventAckState kDefaultNotForwardedAck =
- INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS;
+// Time interval at which touchmove events will be forwarded to the client while
+// scrolling is active and possible.
+const double kAsyncTouchMoveIntervalSec = .2;
-typedef std::vector<TouchEventWithLatencyInfo> WebTouchEventWithLatencyList;
+// A slop region just larger than that used by many web applications. When
+// touchmove's are being sent asynchronously, movement outside this region will
+// trigger an immediate async touchmove to cancel potential tap-related logic.
+const double kApplicationSlopRegionLengthDipsSqared = 15. * 15.;
+
+// Using a small epsilon when comparing slop distances allows pixel perfect
+// slop determination when using fractional DIP coordinates (assuming the slop
+// region and DPI scale are reasonably proportioned).
+const float kSlopEpsilon = .05f;
TouchEventWithLatencyInfo ObtainCancelEventForTouchEvent(
const TouchEventWithLatencyInfo& event_to_cancel) {
TouchEventWithLatencyInfo event = event_to_cancel;
- event.event.type = WebInputEvent::TouchCancel;
- for (size_t i = 0; i < event.event.touchesLength; i++)
- event.event.touches[i].state = WebTouchPoint::StateCancelled;
+ WebTouchEventTraits::ResetTypeAndTouchStates(
+ WebInputEvent::TouchCancel,
+ // TODO(rbyers): Shouldn't we use a fresh timestamp?
+ event.event.timeStampSeconds,
+ &event.event);
return event;
}
-bool IsNewTouchGesture(const WebTouchEvent& event) {
- if (event.type != WebInputEvent::TouchStart)
- return false;
- if (!event.touchesLength)
- return false;
- for (size_t i = 0; i < event.touchesLength; i++) {
- if (event.touches[i].state != WebTouchPoint::StatePressed)
- return false;
- }
- return true;
+bool ShouldTouchTriggerTimeout(const WebTouchEvent& event) {
+ return (event.type == WebInputEvent::TouchStart ||
+ event.type == WebInputEvent::TouchMove) &&
+ !WebInputEventTraits::IgnoresAckDisposition(event);
}
-bool ShouldTouchTypeTriggerTimeout(WebInputEvent::Type type) {
- return type == WebInputEvent::TouchStart ||
- type == WebInputEvent::TouchMove;
+bool OutsideApplicationSlopRegion(const WebTouchEvent& event,
+ const gfx::PointF& anchor) {
+ return (gfx::PointF(event.touches[0].position) - anchor).LengthSquared() >
+ kApplicationSlopRegionLengthDipsSqared;
}
} // namespace
+
+// Cancels a touch sequence if a touchstart or touchmove ack response is
+// sufficiently delayed.
class TouchEventQueue::TouchTimeoutHandler {
public:
- TouchTimeoutHandler(TouchEventQueue* touch_queue, size_t timeout_delay_ms)
+ TouchTimeoutHandler(TouchEventQueue* touch_queue,
+ base::TimeDelta timeout_delay)
: touch_queue_(touch_queue),
- timeout_delay_(base::TimeDelta::FromMilliseconds(timeout_delay_ms)),
+ timeout_delay_(timeout_delay),
pending_ack_state_(PENDING_ACK_NONE),
timeout_monitor_(base::Bind(&TouchTimeoutHandler::OnTimeOut,
- base::Unretained(this))) {}
+ base::Unretained(this))) {
+ DCHECK(timeout_delay != base::TimeDelta());
+ }
~TouchTimeoutHandler() {}
void Start(const TouchEventWithLatencyInfo& event) {
DCHECK_EQ(pending_ack_state_, PENDING_ACK_NONE);
- DCHECK(ShouldTouchTypeTriggerTimeout(event.event.type));
+ DCHECK(ShouldTouchTriggerTimeout(event.event));
timeout_event_ = event;
timeout_monitor_.Restart(timeout_delay_);
}
@@ -80,9 +92,7 @@ class TouchEventQueue::TouchTimeoutHandler {
SetPendingAckState(PENDING_ACK_CANCEL_EVENT);
TouchEventWithLatencyInfo cancel_event =
ObtainCancelEventForTouchEvent(timeout_event_);
- touch_queue_->UpdateTouchAckStates(
- cancel_event.event, kDefaultNotForwardedAck);
- touch_queue_->client_->SendTouchEventImmediately(cancel_event);
+ touch_queue_->SendTouchEventImmediately(cancel_event);
} else {
SetPendingAckState(PENDING_ACK_NONE);
touch_queue_->UpdateTouchAckStates(timeout_event_.event, ack_result);
@@ -95,14 +105,23 @@ class TouchEventQueue::TouchTimeoutHandler {
return false;
}
- bool HasTimeoutEvent() const {
- return pending_ack_state_ != PENDING_ACK_NONE;
+ bool FilterEvent(const WebTouchEvent& event) {
+ return HasTimeoutEvent();
}
bool IsTimeoutTimerRunning() const {
return timeout_monitor_.IsRunning();
}
+ void Reset() {
+ pending_ack_state_ = PENDING_ACK_NONE;
+ timeout_monitor_.Stop();
+ }
+
+ void set_timeout_delay(base::TimeDelta timeout_delay) {
+ timeout_delay_ = timeout_delay;
+ }
+
private:
enum PendingAckState {
PENDING_ACK_NONE,
@@ -121,7 +140,7 @@ class TouchEventQueue::TouchTimeoutHandler {
DCHECK(HasTimeoutEvent());
if (ack_result != INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS)
return true;
- return !IsNewTouchGesture(timeout_event_.event);
+ return !WebTouchEventTraits::IsTouchSequenceStart(timeout_event_.event);
}
void SetPendingAckState(PendingAckState new_pending_ack_state) {
@@ -147,6 +166,10 @@ class TouchEventQueue::TouchTimeoutHandler {
pending_ack_state_ = new_pending_ack_state;
}
+ bool HasTimeoutEvent() const {
+ return pending_ack_state_ != PENDING_ACK_NONE;
+ }
+
TouchEventQueue* touch_queue_;
@@ -163,6 +186,60 @@ class TouchEventQueue::TouchTimeoutHandler {
TimeoutMonitor timeout_monitor_;
};
+// Provides touchmove slop suppression for a single touch that remains within
+// a given slop region, unless the touchstart is preventDefault'ed.
+// TODO(jdduke): Use a flag bundled with each TouchEvent declaring whether it
+// has exceeded the slop region, removing duplicated slop determination logic.
+class TouchEventQueue::TouchMoveSlopSuppressor {
+ public:
+ TouchMoveSlopSuppressor(double slop_suppression_length_dips)
+ : slop_suppression_length_dips_squared_(slop_suppression_length_dips *
+ slop_suppression_length_dips),
+ suppressing_touchmoves_(false) {}
+
+ bool FilterEvent(const WebTouchEvent& event) {
+ if (WebTouchEventTraits::IsTouchSequenceStart(event)) {
+ touch_sequence_start_position_ =
+ gfx::PointF(event.touches[0].position);
+ suppressing_touchmoves_ = slop_suppression_length_dips_squared_ != 0;
+ }
+
+ if (event.type == WebInputEvent::TouchEnd ||
+ event.type == WebInputEvent::TouchCancel)
+ suppressing_touchmoves_ = false;
+
+ if (event.type != WebInputEvent::TouchMove)
+ return false;
+
+ if (suppressing_touchmoves_) {
+ // Movement with a secondary pointer should terminate suppression.
+ if (event.touchesLength > 1) {
+ suppressing_touchmoves_ = false;
+ } else if (event.touchesLength == 1) {
+ // Movement outside of the slop region should terminate suppression.
+ gfx::PointF position(event.touches[0].position);
+ if ((position - touch_sequence_start_position_).LengthSquared() >
+ slop_suppression_length_dips_squared_)
+ suppressing_touchmoves_ = false;
+ }
+ }
+ return suppressing_touchmoves_;
+ }
+
+ void ConfirmTouchEvent(InputEventAckState ack_result) {
+ if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED)
+ suppressing_touchmoves_ = false;
+ }
+
+ bool suppressing_touchmoves() const { return suppressing_touchmoves_; }
+
+ private:
+ double slop_suppression_length_dips_squared_;
+ gfx::PointF touch_sequence_start_position_;
+ bool suppressing_touchmoves_;
+
+ DISALLOW_COPY_AND_ASSIGN(TouchMoveSlopSuppressor);
+};
// This class represents a single coalesced touch event. However, it also keeps
// track of all the original touch-events that were coalesced into a single
@@ -171,25 +248,27 @@ class TouchEventQueue::TouchTimeoutHandler {
// the Client receives the event with their original timestamp.
class CoalescedWebTouchEvent {
public:
- CoalescedWebTouchEvent(const TouchEventWithLatencyInfo& event,
- bool ignore_ack)
- : coalesced_event_(event),
- ignore_ack_(ignore_ack) {
- events_.push_back(event);
- TRACE_EVENT_ASYNC_BEGIN0(
- "input", "TouchEventQueue::QueueEvent", this);
+ // Events for which |async| is true will not be ack'ed to the client after the
+ // corresponding ack is received following dispatch.
+ CoalescedWebTouchEvent(const TouchEventWithLatencyInfo& event, bool async)
+ : coalesced_event_(event) {
+ if (async)
+ coalesced_event_.event.cancelable = false;
+ else
+ events_to_ack_.push_back(event);
+
+ TRACE_EVENT_ASYNC_BEGIN0("input", "TouchEventQueue::QueueEvent", this);
}
~CoalescedWebTouchEvent() {
- TRACE_EVENT_ASYNC_END0(
- "input", "TouchEventQueue::QueueEvent", this);
+ TRACE_EVENT_ASYNC_END0("input", "TouchEventQueue::QueueEvent", this);
}
// Coalesces the event with the existing event if possible. Returns whether
// the event was coalesced.
bool CoalesceEventIfPossible(
const TouchEventWithLatencyInfo& event_with_latency) {
- if (ignore_ack_)
+ if (!WillDispatchAckToClient())
return false;
if (!coalesced_event_.CanCoalesceWith(event_with_latency))
@@ -198,48 +277,84 @@ class CoalescedWebTouchEvent {
TRACE_EVENT_INSTANT0(
"input", "TouchEventQueue::MoveCoalesced", TRACE_EVENT_SCOPE_THREAD);
coalesced_event_.CoalesceWith(event_with_latency);
- events_.push_back(event_with_latency);
+ events_to_ack_.push_back(event_with_latency);
return true;
}
- const TouchEventWithLatencyInfo& coalesced_event() const {
- return coalesced_event_;
- }
+ void UpdateLatencyInfoForAck(const ui::LatencyInfo& renderer_latency_info) {
+ if (!WillDispatchAckToClient())
+ return;
- WebTouchEventWithLatencyList::iterator begin() {
- return events_.begin();
+ for (WebTouchEventWithLatencyList::iterator iter = events_to_ack_.begin(),
+ end = events_to_ack_.end();
+ iter != end;
+ ++iter) {
+ iter->latency.AddNewLatencyFrom(renderer_latency_info);
+ }
}
- WebTouchEventWithLatencyList::iterator end() {
- return events_.end();
- }
+ void DispatchAckToClient(InputEventAckState ack_result,
+ TouchEventQueueClient* client) {
+ DCHECK(client);
+ if (!WillDispatchAckToClient())
+ return;
- size_t size() const { return events_.size(); }
+ for (WebTouchEventWithLatencyList::const_iterator
+ iter = events_to_ack_.begin(),
+ end = events_to_ack_.end();
+ iter != end;
+ ++iter) {
+ client->OnTouchEventAck(*iter, ack_result);
+ }
+ }
- bool ignore_ack() const { return ignore_ack_; }
+ const TouchEventWithLatencyInfo& coalesced_event() const {
+ return coalesced_event_;
+ }
private:
+ bool WillDispatchAckToClient() const { return !events_to_ack_.empty(); }
+
// This is the event that is forwarded to the renderer.
TouchEventWithLatencyInfo coalesced_event_;
- // This is the list of the original events that were coalesced.
- WebTouchEventWithLatencyList events_;
-
- // If |ignore_ack_| is true, don't send this touch event to client
- // when the event is acked.
- bool ignore_ack_;
+ // This is the list of the original events that were coalesced, each requiring
+ // future ack dispatch to the client.
+ typedef std::vector<TouchEventWithLatencyInfo> WebTouchEventWithLatencyList;
+ WebTouchEventWithLatencyList events_to_ack_;
DISALLOW_COPY_AND_ASSIGN(CoalescedWebTouchEvent);
};
-TouchEventQueue::TouchEventQueue(TouchEventQueueClient* client)
+TouchEventQueue::Config::Config()
+ : touchmove_slop_suppression_length_dips(0),
+ touchmove_slop_suppression_region_includes_boundary(true),
+ touch_scrolling_mode(TOUCH_SCROLLING_MODE_DEFAULT),
+ touch_ack_timeout_delay(base::TimeDelta::FromMilliseconds(200)),
+ touch_ack_timeout_supported(false) {
+}
+
+TouchEventQueue::TouchEventQueue(TouchEventQueueClient* client,
+ const Config& config)
: client_(client),
dispatching_touch_ack_(NULL),
dispatching_touch_(false),
- no_touch_to_renderer_(false),
- renderer_is_consuming_touch_gesture_(false),
- ack_timeout_enabled_(false) {
+ touch_filtering_state_(TOUCH_FILTERING_STATE_DEFAULT),
+ ack_timeout_enabled_(config.touch_ack_timeout_supported),
+ touchmove_slop_suppressor_(new TouchMoveSlopSuppressor(
+ config.touchmove_slop_suppression_length_dips +
+ (config.touchmove_slop_suppression_region_includes_boundary
+ ? kSlopEpsilon
+ : -kSlopEpsilon))),
+ send_touch_events_async_(false),
+ needs_async_touchmove_for_outer_slop_region_(false),
+ last_sent_touch_timestamp_sec_(0),
+ touch_scrolling_mode_(config.touch_scrolling_mode) {
DCHECK(client);
+ if (ack_timeout_enabled_) {
+ timeout_handler_.reset(
+ new TouchTimeoutHandler(this, config.touch_ack_timeout_delay));
+ }
}
TouchEventQueue::~TouchEventQueue() {
@@ -248,13 +363,26 @@ TouchEventQueue::~TouchEventQueue() {
}
void TouchEventQueue::QueueEvent(const TouchEventWithLatencyInfo& event) {
+ TRACE_EVENT0("input", "TouchEventQueue::QueueEvent");
+
// If the queueing of |event| was triggered by an ack dispatch, defer
// processing the event until the dispatch has finished.
if (touch_queue_.empty() && !dispatching_touch_ack_) {
+ // Optimization of the case without touch handlers. Removing this path
+ // yields identical results, but this avoids unnecessary allocations.
+ PreFilterResult filter_result = FilterBeforeForwarding(event.event);
+ if (filter_result != FORWARD_TO_RENDERER) {
+ client_->OnTouchEventAck(event,
+ filter_result == ACK_WITH_NO_CONSUMER_EXISTS
+ ? INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS
+ : INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ return;
+ }
+
// There is no touch event in the queue. Forward it to the renderer
// immediately.
touch_queue_.push_back(new CoalescedWebTouchEvent(event, false));
- TryForwardNextEventToRenderer();
+ ForwardNextEventToRenderer();
return;
}
@@ -269,22 +397,25 @@ void TouchEventQueue::QueueEvent(const TouchEventWithLatencyInfo& event) {
}
void TouchEventQueue::ProcessTouchAck(InputEventAckState ack_result,
- const ui::LatencyInfo& latency_info) {
+ const LatencyInfo& latency_info) {
+ TRACE_EVENT0("input", "TouchEventQueue::ProcessTouchAck");
+
DCHECK(!dispatching_touch_ack_);
dispatching_touch_ = false;
if (timeout_handler_ && timeout_handler_->ConfirmTouchEvent(ack_result))
return;
+ touchmove_slop_suppressor_->ConfirmTouchEvent(ack_result);
+
if (touch_queue_.empty())
return;
- if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED)
- renderer_is_consuming_touch_gesture_ = true;
+ if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED &&
+ touch_filtering_state_ == FORWARD_TOUCHES_UNTIL_TIMEOUT) {
+ touch_filtering_state_ = FORWARD_ALL_TOUCHES;
+ }
- const WebTouchEvent& acked_event =
- touch_queue_.front()->coalesced_event().event;
- UpdateTouchAckStates(acked_event, ack_result);
PopTouchEventToClient(ack_result, latency_info);
TryForwardNextEventToRenderer();
}
@@ -294,29 +425,108 @@ void TouchEventQueue::TryForwardNextEventToRenderer() {
// If there are queued touch events, then try to forward them to the renderer
// immediately, or ACK the events back to the client if appropriate.
while (!touch_queue_.empty()) {
- const TouchEventWithLatencyInfo& touch =
- touch_queue_.front()->coalesced_event();
- if (IsNewTouchGesture(touch.event))
- renderer_is_consuming_touch_gesture_ = false;
- if (ShouldForwardToRenderer(touch.event)) {
- ForwardToRenderer(touch);
- break;
+ PreFilterResult filter_result =
+ FilterBeforeForwarding(touch_queue_.front()->coalesced_event().event);
+ switch (filter_result) {
+ case ACK_WITH_NO_CONSUMER_EXISTS:
+ PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
+ break;
+ case ACK_WITH_NOT_CONSUMED:
+ PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ break;
+ case FORWARD_TO_RENDERER:
+ ForwardNextEventToRenderer();
+ return;
}
- PopTouchEventToClient(kDefaultNotForwardedAck, ui::LatencyInfo());
}
}
-void TouchEventQueue::ForwardToRenderer(
- const TouchEventWithLatencyInfo& touch) {
+void TouchEventQueue::ForwardNextEventToRenderer() {
+ TRACE_EVENT0("input", "TouchEventQueue::ForwardNextEventToRenderer");
+
+ DCHECK(!empty());
DCHECK(!dispatching_touch_);
+ DCHECK_NE(touch_filtering_state_, DROP_ALL_TOUCHES);
+ TouchEventWithLatencyInfo touch = touch_queue_.front()->coalesced_event();
+
+ if (WebTouchEventTraits::IsTouchSequenceStart(touch.event)) {
+ touch_filtering_state_ =
+ ack_timeout_enabled_ ? FORWARD_TOUCHES_UNTIL_TIMEOUT
+ : FORWARD_ALL_TOUCHES;
+ touch_ack_states_.clear();
+ send_touch_events_async_ = false;
+ touch_sequence_start_position_ =
+ gfx::PointF(touch.event.touches[0].position);
+ }
+
+ if (send_touch_events_async_ &&
+ touch.event.type == WebInputEvent::TouchMove) {
+ // Throttling touchmove's in a continuous touchmove stream while scrolling
+ // reduces the risk of jank. However, it's still important that the web
+ // application be sent touches at key points in the gesture stream,
+ // e.g., when the application slop region is exceeded or touchmove
+ // coalescing fails because of different modifiers.
+ const bool send_touchmove_now =
+ size() > 1 ||
+ (touch.event.timeStampSeconds >=
+ last_sent_touch_timestamp_sec_ + kAsyncTouchMoveIntervalSec) ||
+ (needs_async_touchmove_for_outer_slop_region_ &&
+ OutsideApplicationSlopRegion(touch.event,
+ touch_sequence_start_position_)) ||
+ (pending_async_touchmove_ &&
+ !pending_async_touchmove_->CanCoalesceWith(touch));
+
+ if (!send_touchmove_now) {
+ if (!pending_async_touchmove_) {
+ pending_async_touchmove_.reset(new TouchEventWithLatencyInfo(touch));
+ } else {
+ DCHECK(pending_async_touchmove_->CanCoalesceWith(touch));
+ pending_async_touchmove_->CoalesceWith(touch);
+ }
+ DCHECK_EQ(1U, size());
+ PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ // It's possible (though unlikely) that ack'ing the current touch will
+ // trigger the queueing of another touch event (e.g., a touchcancel). As
+ // forwarding of the queued event will be deferred while the ack is being
+ // dispatched (see |OnTouchEvent()|), try forwarding it now.
+ TryForwardNextEventToRenderer();
+ return;
+ }
+ }
+
+ last_sent_touch_timestamp_sec_ = touch.event.timeStampSeconds;
+
+ // Flush any pending async touch move. If it can be combined with the current
+ // (touchmove) event, great, otherwise send it immediately but separately. Its
+ // ack will trigger forwarding of the original |touch| event.
+ if (pending_async_touchmove_) {
+ if (pending_async_touchmove_->CanCoalesceWith(touch)) {
+ pending_async_touchmove_->CoalesceWith(touch);
+ pending_async_touchmove_->event.cancelable = !send_touch_events_async_;
+ touch = *pending_async_touchmove_.Pass();
+ } else {
+ scoped_ptr<TouchEventWithLatencyInfo> async_move =
+ pending_async_touchmove_.Pass();
+ async_move->event.cancelable = false;
+ touch_queue_.push_front(new CoalescedWebTouchEvent(*async_move, true));
+ SendTouchEventImmediately(*async_move);
+ return;
+ }
+ }
+
+ // Note: Marking touchstart events as not-cancelable prevents them from
+ // blocking subsequent gestures, but it may not be the best long term solution
+ // for tracking touch point dispatch.
+ if (send_touch_events_async_)
+ touch.event.cancelable = false;
+
// A synchronous ack will reset |dispatching_touch_|, in which case
// the touch timeout should not be started.
base::AutoReset<bool> dispatching_touch(&dispatching_touch_, true);
- client_->SendTouchEventImmediately(touch);
- if (ack_timeout_enabled_ &&
- dispatching_touch_ &&
- !renderer_is_consuming_touch_gesture_ &&
- ShouldTouchTypeTriggerTimeout(touch.event.type)) {
+ SendTouchEventImmediately(touch);
+ if (dispatching_touch_ &&
+ touch_filtering_state_ == FORWARD_TOUCHES_UNTIL_TIMEOUT &&
+ ShouldTouchTriggerTimeout(touch.event)) {
DCHECK(timeout_handler_);
timeout_handler_->Start(touch);
}
@@ -324,41 +534,107 @@ void TouchEventQueue::ForwardToRenderer(
void TouchEventQueue::OnGestureScrollEvent(
const GestureEventWithLatencyInfo& gesture_event) {
- blink::WebInputEvent::Type type = gesture_event.event.type;
- if (type == blink::WebInputEvent::GestureScrollBegin) {
- // We assume the scroll event are generated synchronously from
- // dispatching a touch event ack, so that we can fake a cancel
- // event that has the correct touch ids as the touch event that
- // is being acked. If not, we don't do the touch-cancel optimization.
- if (no_touch_to_renderer_ || !dispatching_touch_ack_)
- return;
- no_touch_to_renderer_ = true;
+ if (gesture_event.event.type != blink::WebInputEvent::GestureScrollBegin)
+ return;
- // If we have a timeout event, a cancel has already been dispatched
- // for the current touch stream.
- if (HasTimeoutEvent())
- return;
+ if (touch_filtering_state_ != DROP_ALL_TOUCHES &&
+ touch_filtering_state_ != DROP_TOUCHES_IN_SEQUENCE) {
+ DCHECK(!touchmove_slop_suppressor_->suppressing_touchmoves())
+ << "The renderer should be offered a touchmove before scrolling begins";
+ }
+
+ if (touch_scrolling_mode_ == TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE) {
+ if (touch_filtering_state_ != DROP_ALL_TOUCHES &&
+ touch_filtering_state_ != DROP_TOUCHES_IN_SEQUENCE) {
+ // If no touch points have a consumer, prevent all subsequent touch events
+ // received during the scroll from reaching the renderer. This ensures
+ // that the first touchstart the renderer sees in any given sequence can
+ // always be preventDefault'ed (cancelable == true).
+ // TODO(jdduke): Revisit if touchstarts during scroll are made cancelable.
+ if (touch_ack_states_.empty() ||
+ AllTouchAckStatesHaveState(
+ INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS)) {
+ touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE;
+ return;
+ }
+ }
- // Fake a TouchCancel to cancel the touch points of the touch event
- // that is currently being acked.
- // Note: |dispatching_touch_ack_| is non-null when we reach here, meaning we
- // are in the scope of PopTouchEventToClient() and that no touch event
- // in the queue is waiting for ack from renderer. So we can just insert
- // the touch cancel at the beginning of the queue.
- touch_queue_.push_front(new CoalescedWebTouchEvent(
- ObtainCancelEventForTouchEvent(
- dispatching_touch_ack_->coalesced_event()), true));
- } else if (type == blink::WebInputEvent::GestureScrollEnd ||
- type == blink::WebInputEvent::GestureFlingStart) {
- no_touch_to_renderer_ = false;
+ pending_async_touchmove_.reset();
+ send_touch_events_async_ = true;
+ needs_async_touchmove_for_outer_slop_region_ = true;
+ return;
}
+
+ if (touch_scrolling_mode_ != TOUCH_SCROLLING_MODE_TOUCHCANCEL)
+ return;
+
+ // We assume that scroll events are generated synchronously from
+ // dispatching a touch event ack. This allows us to generate a synthetic
+ // cancel event that has the same touch ids as the touch event that
+ // is being acked. Otherwise, we don't perform the touch-cancel optimization.
+ if (!dispatching_touch_ack_)
+ return;
+
+ if (touch_filtering_state_ == DROP_TOUCHES_IN_SEQUENCE)
+ return;
+
+ touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE;
+
+ // Fake a TouchCancel to cancel the touch points of the touch event
+ // that is currently being acked.
+ // Note: |dispatching_touch_ack_| is non-null when we reach here, meaning we
+ // are in the scope of PopTouchEventToClient() and that no touch event
+ // in the queue is waiting for ack from renderer. So we can just insert
+ // the touch cancel at the beginning of the queue.
+ touch_queue_.push_front(new CoalescedWebTouchEvent(
+ ObtainCancelEventForTouchEvent(
+ dispatching_touch_ack_->coalesced_event()), true));
}
-void TouchEventQueue::FlushQueue() {
+void TouchEventQueue::OnGestureEventAck(
+ const GestureEventWithLatencyInfo& event,
+ InputEventAckState ack_result) {
+ if (touch_scrolling_mode_ != TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE)
+ return;
+
+ if (event.event.type != blink::WebInputEvent::GestureScrollUpdate)
+ return;
+
+ // Throttle sending touchmove events as long as the scroll events are handled.
+ // Note that there's no guarantee that this ACK is for the most recent
+ // gesture event (or even part of the current sequence). Worst case, the
+ // delay in updating the absorption state will result in minor UI glitches.
+ // A valid |pending_async_touchmove_| will be flushed when the next event is
+ // forwarded.
+ send_touch_events_async_ = (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED);
+ if (!send_touch_events_async_)
+ needs_async_touchmove_for_outer_slop_region_ = false;
+}
+
+void TouchEventQueue::OnHasTouchEventHandlers(bool has_handlers) {
DCHECK(!dispatching_touch_ack_);
DCHECK(!dispatching_touch_);
- while (!touch_queue_.empty())
- PopTouchEventToClient(kDefaultNotForwardedAck, ui::LatencyInfo());
+
+ if (has_handlers) {
+ if (touch_filtering_state_ == DROP_ALL_TOUCHES) {
+ // If no touch handler was previously registered, ensure that we don't
+ // send a partial touch sequence to the renderer.
+ DCHECK(touch_queue_.empty());
+ touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE;
+ }
+ } else {
+ // TODO(jdduke): Synthesize a TouchCancel if necessary to update Blink touch
+ // state tracking and/or touch-action filtering (e.g., if the touch handler
+ // was removed mid-sequence), crbug.com/375940.
+ touch_filtering_state_ = DROP_ALL_TOUCHES;
+ pending_async_touchmove_.reset();
+ if (timeout_handler_)
+ timeout_handler_->Reset();
+ if (!touch_queue_.empty())
+ ProcessTouchAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, LatencyInfo());
+ // As there is no touch handler, ack'ing the event should flush the queue.
+ DCHECK(touch_queue_.empty());
+ }
}
bool TouchEventQueue::IsPendingAckTouchStart() const {
@@ -371,22 +647,30 @@ bool TouchEventQueue::IsPendingAckTouchStart() const {
return (event.type == WebInputEvent::TouchStart);
}
-void TouchEventQueue::SetAckTimeoutEnabled(bool enabled,
- size_t ack_timeout_delay_ms) {
- if (!enabled) {
- // Avoid resetting |timeout_handler_|, as an outstanding timeout may
- // be active and must be completed for ack handling consistency.
- ack_timeout_enabled_ = false;
+void TouchEventQueue::SetAckTimeoutEnabled(bool enabled) {
+ // The timeout handler is valid only if explicitly supported in the config.
+ if (!timeout_handler_)
+ return;
+
+ if (ack_timeout_enabled_ == enabled)
return;
- }
- ack_timeout_enabled_ = true;
- if (!timeout_handler_)
- timeout_handler_.reset(new TouchTimeoutHandler(this, ack_timeout_delay_ms));
+ ack_timeout_enabled_ = enabled;
+
+ if (enabled)
+ return;
+
+ if (touch_filtering_state_ == FORWARD_TOUCHES_UNTIL_TIMEOUT)
+ touch_filtering_state_ = FORWARD_ALL_TOUCHES;
+ // Only reset the |timeout_handler_| if the timer is running and has not yet
+ // timed out. This ensures that an already timed out sequence is properly
+ // flushed by the handler.
+ if (timeout_handler_ && timeout_handler_->IsTimeoutTimerRunning())
+ timeout_handler_->Reset();
}
-bool TouchEventQueue::HasTimeoutEvent() const {
- return timeout_handler_ && timeout_handler_->HasTimeoutEvent();
+bool TouchEventQueue::HasPendingAsyncTouchMoveForTesting() const {
+ return pending_async_touchmove_;
}
bool TouchEventQueue::IsTimeoutRunningForTesting() const {
@@ -398,43 +682,85 @@ TouchEventQueue::GetLatestEventForTesting() const {
return touch_queue_.back()->coalesced_event();
}
+void TouchEventQueue::FlushQueue() {
+ DCHECK(!dispatching_touch_ack_);
+ DCHECK(!dispatching_touch_);
+ pending_async_touchmove_.reset();
+ if (touch_filtering_state_ != DROP_ALL_TOUCHES)
+ touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE;
+ while (!touch_queue_.empty())
+ PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
+}
+
+void TouchEventQueue::PopTouchEventToClient(InputEventAckState ack_result) {
+ AckTouchEventToClient(ack_result, PopTouchEvent());
+}
+
void TouchEventQueue::PopTouchEventToClient(
InputEventAckState ack_result,
- const ui::LatencyInfo& renderer_latency_info) {
- DCHECK(!dispatching_touch_ack_);
- if (touch_queue_.empty())
- return;
- scoped_ptr<CoalescedWebTouchEvent> acked_event(touch_queue_.front());
- touch_queue_.pop_front();
+ const LatencyInfo& renderer_latency_info) {
+ scoped_ptr<CoalescedWebTouchEvent> acked_event = PopTouchEvent();
+ acked_event->UpdateLatencyInfoForAck(renderer_latency_info);
+ AckTouchEventToClient(ack_result, acked_event.Pass());
+}
- if (acked_event->ignore_ack())
- return;
+void TouchEventQueue::AckTouchEventToClient(
+ InputEventAckState ack_result,
+ scoped_ptr<CoalescedWebTouchEvent> acked_event) {
+ DCHECK(acked_event);
+ DCHECK(!dispatching_touch_ack_);
+ UpdateTouchAckStates(acked_event->coalesced_event().event, ack_result);
// Note that acking the touch-event may result in multiple gestures being sent
// to the renderer, or touch-events being queued.
- base::AutoReset<CoalescedWebTouchEvent*>
- dispatching_touch_ack(&dispatching_touch_ack_, acked_event.get());
+ base::AutoReset<const CoalescedWebTouchEvent*> dispatching_touch_ack(
+ &dispatching_touch_ack_, acked_event.get());
+ acked_event->DispatchAckToClient(ack_result, client_);
+}
- for (WebTouchEventWithLatencyList::iterator iter = acked_event->begin(),
- end = acked_event->end();
- iter != end; ++iter) {
- iter->latency.AddNewLatencyFrom(renderer_latency_info);
- client_->OnTouchEventAck((*iter), ack_result);
+scoped_ptr<CoalescedWebTouchEvent> TouchEventQueue::PopTouchEvent() {
+ DCHECK(!touch_queue_.empty());
+ scoped_ptr<CoalescedWebTouchEvent> event(touch_queue_.front());
+ touch_queue_.pop_front();
+ return event.Pass();
+}
+
+void TouchEventQueue::SendTouchEventImmediately(
+ const TouchEventWithLatencyInfo& touch) {
+ if (needs_async_touchmove_for_outer_slop_region_) {
+ // Any event other than a touchmove (e.g., touchcancel or secondary
+ // touchstart) after a scroll has started will interrupt the need to send a
+ // an outer slop-region exceeding touchmove.
+ if (touch.event.type != WebInputEvent::TouchMove ||
+ OutsideApplicationSlopRegion(touch.event,
+ touch_sequence_start_position_))
+ needs_async_touchmove_for_outer_slop_region_ = false;
}
+
+ client_->SendTouchEventImmediately(touch);
}
-bool TouchEventQueue::ShouldForwardToRenderer(
- const WebTouchEvent& event) const {
- if (HasTimeoutEvent())
- return false;
+TouchEventQueue::PreFilterResult
+TouchEventQueue::FilterBeforeForwarding(const WebTouchEvent& event) {
+ if (timeout_handler_ && timeout_handler_->FilterEvent(event))
+ return ACK_WITH_NO_CONSUMER_EXISTS;
- if (no_touch_to_renderer_ &&
- event.type != blink::WebInputEvent::TouchCancel)
- return false;
+ if (touchmove_slop_suppressor_->FilterEvent(event))
+ return ACK_WITH_NOT_CONSUMED;
+
+ if (touch_filtering_state_ == DROP_ALL_TOUCHES)
+ return ACK_WITH_NO_CONSUMER_EXISTS;
+
+ if (touch_filtering_state_ == DROP_TOUCHES_IN_SEQUENCE &&
+ event.type != WebInputEvent::TouchCancel) {
+ if (WebTouchEventTraits::IsTouchSequenceStart(event))
+ return FORWARD_TO_RENDERER;
+ return ACK_WITH_NO_CONSUMER_EXISTS;
+ }
// Touch press events should always be forwarded to the renderer.
if (event.type == WebInputEvent::TouchStart)
- return true;
+ return FORWARD_TO_RENDERER;
for (unsigned int i = 0; i < event.touchesLength; ++i) {
const WebTouchPoint& point = event.touches[i];
@@ -445,15 +771,15 @@ bool TouchEventQueue::ShouldForwardToRenderer(
if (touch_ack_states_.count(point.id) > 0) {
if (touch_ack_states_.find(point.id)->second !=
INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS)
- return true;
+ return FORWARD_TO_RENDERER;
} else {
// If the ACK status of a point is unknown, then the event should be
// forwarded to the renderer.
- return true;
+ return FORWARD_TO_RENDERER;
}
}
- return false;
+ return ACK_WITH_NO_CONSUMER_EXISTS;
}
void TouchEventQueue::UpdateTouchAckStates(const WebTouchEvent& event,
@@ -477,4 +803,20 @@ void TouchEventQueue::UpdateTouchAckStates(const WebTouchEvent& event,
}
}
+bool TouchEventQueue::AllTouchAckStatesHaveState(
+ InputEventAckState ack_state) const {
+ if (touch_ack_states_.empty())
+ return false;
+
+ for (TouchPointAckStates::const_iterator iter = touch_ack_states_.begin(),
+ end = touch_ack_states_.end();
+ iter != end;
+ ++iter) {
+ if (iter->second != ack_state)
+ return false;
+ }
+
+ return true;
+}
+
} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/touch_event_queue.h b/chromium/content/browser/renderer_host/input/touch_event_queue.h
index ea7eb71fe5a..ecb8fe83b40 100644
--- a/chromium/content/browser/renderer_host/input/touch_event_queue.h
+++ b/chromium/content/browser/renderer_host/input/touch_event_queue.h
@@ -9,10 +9,12 @@
#include <map>
#include "base/basictypes.h"
+#include "base/time/time.h"
+#include "content/browser/renderer_host/event_with_latency_info.h"
#include "content/common/content_export.h"
-#include "content/port/browser/event_with_latency_info.h"
-#include "content/port/common/input_event_ack_state.h"
+#include "content/common/input/input_event_ack_state.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
+#include "ui/gfx/geometry/point_f.h"
namespace content {
@@ -35,9 +37,52 @@ class CONTENT_EXPORT TouchEventQueueClient {
// A queue for throttling and coalescing touch-events.
class CONTENT_EXPORT TouchEventQueue {
public:
+ // Different ways of dealing with touch events during scrolling.
+ // TODO(rbyers): Remove this once we're confident that touch move absorption
+ // is OK. http://crbug.com/350430
+ enum TouchScrollingMode {
+ // Send a touchcancel on scroll start and no further touch events for the
+ // duration of the scroll. Chrome Android's traditional behavior.
+ TOUCH_SCROLLING_MODE_TOUCHCANCEL,
+ // Send touchmove events throughout a scroll, blocking on each ACK and
+ // using the disposition to determine whether a scroll update should be
+ // sent. Mobile Safari's default overflow scroll behavior.
+ TOUCH_SCROLLING_MODE_SYNC_TOUCHMOVE,
+ // Send touchmove events throughout a scroll, but throttle sending and
+ // ignore the ACK as long as scrolling remains possible. Unconsumed scroll
+ // events return touchmove events to being dispatched synchronously.
+ TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE,
+ TOUCH_SCROLLING_MODE_DEFAULT = TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE
+ };
+
+ struct CONTENT_EXPORT Config {
+ Config();
+
+ // Determines the bounds of the (square) touchmove slop suppression region.
+ // Defaults to 0 (disabled).
+ double touchmove_slop_suppression_length_dips;
+
+ // Whether the touchmove slop suppression region is boundary inclusive.
+ // Defaults to true.
+ // TODO(jdduke): Remove when unified GR enabled, crbug.com/332418.
+ bool touchmove_slop_suppression_region_includes_boundary;
+
+ // Determines the type of touch scrolling.
+ // Defaults to TouchEventQueue:::TOUCH_SCROLLING_MODE_DEFAULT.
+ TouchEventQueue::TouchScrollingMode touch_scrolling_mode;
+
+ // Controls whether touch ack timeouts will trigger touch cancellation.
+ // Defaults to 200ms.
+ base::TimeDelta touch_ack_timeout_delay;
+
+ // Whether the platform supports touch ack timeout behavior.
+ // Defaults to false (disabled).
+ bool touch_ack_timeout_supported;
+ };
// The |client| must outlive the TouchEventQueue.
- explicit TouchEventQueue(TouchEventQueueClient* client);
+ TouchEventQueue(TouchEventQueueClient* client, const Config& config);
+
~TouchEventQueue();
// Adds an event to the queue. The event may be coalesced with previously
@@ -58,17 +103,21 @@ class CONTENT_EXPORT TouchEventQueue {
// resume the normal flow of sending touch events to the renderer.
void OnGestureScrollEvent(const GestureEventWithLatencyInfo& gesture_event);
- // Empties the queue of touch events. This may result in any number of gesture
- // events being sent to the renderer.
- void FlushQueue();
+ void OnGestureEventAck(
+ const GestureEventWithLatencyInfo& event,
+ InputEventAckState ack_result);
+
+ // Notifies the queue whether the renderer has at least one touch handler.
+ void OnHasTouchEventHandlers(bool has_handlers);
// Returns whether the currently pending touch event (waiting ACK) is for
// a touch start event.
bool IsPendingAckTouchStart() const;
// Sets whether a delayed touch ack will cancel and flush the current
- // touch sequence.
- void SetAckTimeoutEnabled(bool enabled, size_t ack_timeout_delay_ms);
+ // touch sequence. Note that, if the timeout was previously disabled, enabling
+ // it will take effect only for the following touch sequence.
+ void SetAckTimeoutEnabled(bool enabled);
bool empty() const WARN_UNUSED_RESULT {
return touch_queue_.empty();
@@ -82,29 +131,62 @@ class CONTENT_EXPORT TouchEventQueue {
return ack_timeout_enabled_;
}
+ bool has_handlers() const {
+ return touch_filtering_state_ != DROP_ALL_TOUCHES;
+ }
+
private:
class TouchTimeoutHandler;
+ class TouchMoveSlopSuppressor;
friend class TouchTimeoutHandler;
friend class TouchEventQueueTest;
- bool HasTimeoutEvent() const;
+ bool HasPendingAsyncTouchMoveForTesting() const;
bool IsTimeoutRunningForTesting() const;
const TouchEventWithLatencyInfo& GetLatestEventForTesting() const;
- // Walks the queue, checking each event for |ShouldForwardToRenderer()|.
- // If true, forwards the touch event and stops processing further events.
- // If false, acks the event with |INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS|.
+ // Empties the queue of touch events. This may result in any number of gesture
+ // events being sent to the renderer.
+ void FlushQueue();
+
+ // Walks the queue, checking each event with |FilterBeforeForwarding()|.
+ // If allowed, forwards the touch event and stops processing further events.
+ // Otherwise, acks the event with |INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS|.
void TryForwardNextEventToRenderer();
- // Pops the touch-event from the top of the queue and sends it to the
- // TouchEventQueueClient. This reduces the size of the queue by one.
+ // Forwards the event at the head of the queue to the renderer.
+ void ForwardNextEventToRenderer();
+
+ // Pops the touch-event from the head of the queue and acks it to the client.
+ void PopTouchEventToClient(InputEventAckState ack_result);
+
+ // Pops the touch-event from the top of the queue and acks it to the client,
+ // updating the event with |renderer_latency_info|.
void PopTouchEventToClient(InputEventAckState ack_result,
const ui::LatencyInfo& renderer_latency_info);
- bool ShouldForwardToRenderer(const blink::WebTouchEvent& event) const;
+ // Ack all coalesced events in |acked_event| to the client with |ack_result|.
+ void AckTouchEventToClient(InputEventAckState ack_result,
+ scoped_ptr<CoalescedWebTouchEvent> acked_event);
+
+ // Safely pop the head of the queue.
+ scoped_ptr<CoalescedWebTouchEvent> PopTouchEvent();
+
+ // Dispatch |touch| to the client.
+ void SendTouchEventImmediately(const TouchEventWithLatencyInfo& touch);
+
+ enum PreFilterResult {
+ ACK_WITH_NO_CONSUMER_EXISTS,
+ ACK_WITH_NOT_CONSUMED,
+ FORWARD_TO_RENDERER,
+ };
+ // Filter touches prior to forwarding to the renderer, e.g., if the renderer
+ // has no touch handler.
+ PreFilterResult FilterBeforeForwarding(const blink::WebTouchEvent& event);
void ForwardToRenderer(const TouchEventWithLatencyInfo& event);
void UpdateTouchAckStates(const blink::WebTouchEvent& event,
InputEventAckState ack_result);
+ bool AllTouchAckStatesHaveState(InputEventAckState ack_state) const;
// Handles touch event forwarding and ack'ed event dispatch.
@@ -117,26 +199,52 @@ class CONTENT_EXPORT TouchEventQueue {
typedef std::map<int, InputEventAckState> TouchPointAckStates;
TouchPointAckStates touch_ack_states_;
+ // Position of the first touch in the most recent sequence forwarded to the
+ // client.
+ gfx::PointF touch_sequence_start_position_;
+
// Used to defer touch forwarding when ack dispatch triggers |QueueEvent()|.
// If not NULL, |dispatching_touch_ack_| is the touch event of which the ack
// is being dispatched.
- CoalescedWebTouchEvent* dispatching_touch_ack_;
+ const CoalescedWebTouchEvent* dispatching_touch_ack_;
// Used to prevent touch timeout scheduling if we receive a synchronous
// ack after forwarding a touch event to the client.
bool dispatching_touch_;
- // Don't send touch events to the renderer while scrolling.
- bool no_touch_to_renderer_;
-
- // Whether an event in the current (multi)touch sequence was consumed by the
- // renderer. The touch timeout will never be activated when this is true.
- bool renderer_is_consuming_touch_gesture_;
-
- // Optional handler for timed-out touch event acks, disabled by default.
+ enum TouchFilteringState {
+ FORWARD_ALL_TOUCHES, // Don't filter at all - the default.
+ FORWARD_TOUCHES_UNTIL_TIMEOUT, // Don't filter unless we get an ACK timeout.
+ DROP_TOUCHES_IN_SEQUENCE, // Filter all events until a new touch
+ // sequence is received.
+ DROP_ALL_TOUCHES, // Filter all events, e.g., no touch handler.
+ TOUCH_FILTERING_STATE_DEFAULT = FORWARD_ALL_TOUCHES,
+ };
+ TouchFilteringState touch_filtering_state_;
+
+ // Optional handler for timed-out touch event acks.
bool ack_timeout_enabled_;
scoped_ptr<TouchTimeoutHandler> timeout_handler_;
+ // Suppression of TouchMove's within a slop region when a sequence has not yet
+ // been preventDefaulted.
+ scoped_ptr<TouchMoveSlopSuppressor> touchmove_slop_suppressor_;
+
+ // Whether touch events should remain buffered and dispatched asynchronously
+ // while a scroll sequence is active. In this mode, touchmove's are throttled
+ // and ack'ed immediately, but remain buffered in |pending_async_touchmove_|
+ // until a sufficient time period has elapsed since the last sent touch event.
+ // For details see the design doc at http://goo.gl/lVyJAa.
+ bool send_touch_events_async_;
+ bool needs_async_touchmove_for_outer_slop_region_;
+ scoped_ptr<TouchEventWithLatencyInfo> pending_async_touchmove_;
+ double last_sent_touch_timestamp_sec_;
+
+ // How touch events are handled during scrolling. For now this is a global
+ // setting for experimentation, but we may evolve it into an app-controlled
+ // mode.
+ const TouchScrollingMode touch_scrolling_mode_;
+
DISALLOW_COPY_AND_ASSIGN(TouchEventQueue);
};
diff --git a/chromium/content/browser/renderer_host/input/touch_event_queue_unittest.cc b/chromium/content/browser/renderer_host/input/touch_event_queue_unittest.cc
index 875b798546c..06a3a9f40f3 100644
--- a/chromium/content/browser/renderer_host/input/touch_event_queue_unittest.cc
+++ b/chromium/content/browser/renderer_host/input/touch_event_queue_unittest.cc
@@ -19,8 +19,13 @@ using blink::WebTouchPoint;
namespace content {
namespace {
-const size_t kDefaultTouchTimeoutDelayMs = 10;
+
+const double kMinSecondsBetweenThrottledTouchmoves = 0.2;
+
+base::TimeDelta DefaultTouchTimeoutDelay() {
+ return base::TimeDelta::FromMilliseconds(1);
}
+} // namespace
class TouchEventQueueTest : public testing::Test,
public TouchEventQueueClient {
@@ -28,14 +33,15 @@ class TouchEventQueueTest : public testing::Test,
TouchEventQueueTest()
: sent_event_count_(0),
acked_event_count_(0),
- last_acked_event_state_(INPUT_EVENT_ACK_STATE_UNKNOWN) {}
+ last_acked_event_state_(INPUT_EVENT_ACK_STATE_UNKNOWN),
+ slop_length_dips_(0),
+ slop_includes_boundary_(true),
+ touch_scrolling_mode_(TouchEventQueue::TOUCH_SCROLLING_MODE_DEFAULT) {}
virtual ~TouchEventQueueTest() {}
// testing::Test
- virtual void SetUp() OVERRIDE {
- queue_.reset(new TouchEventQueue(this));
- }
+ virtual void SetUp() OVERRIDE { ResetQueueWithConfig(CreateConfig()); }
virtual void TearDown() OVERRIDE {
queue_.reset();
@@ -47,7 +53,7 @@ class TouchEventQueueTest : public testing::Test,
++sent_event_count_;
last_sent_event_ = event.event;
if (sync_ack_result_)
- SendTouchEventACK(*sync_ack_result_.Pass());
+ SendTouchEventAck(*sync_ack_result_.Pass());
}
virtual void OnTouchEventAck(
@@ -71,9 +77,32 @@ class TouchEventQueueTest : public testing::Test,
}
protected:
+ TouchEventQueue::Config CreateConfig() {
+ TouchEventQueue::Config config;
+ config.touch_scrolling_mode = touch_scrolling_mode_;
+ config.touchmove_slop_suppression_length_dips = slop_length_dips_;
+ config.touchmove_slop_suppression_region_includes_boundary =
+ slop_includes_boundary_;
+ return config;
+ }
+
+ void SetTouchScrollingMode(TouchEventQueue::TouchScrollingMode mode) {
+ touch_scrolling_mode_ = mode;
+ ResetQueueWithConfig(CreateConfig());
+ }
+
+ void SetUpForTouchMoveSlopTesting(double slop_length_dips,
+ bool slop_includes_boundary) {
+ slop_length_dips_ = slop_length_dips;
+ slop_includes_boundary_ = slop_includes_boundary;
+ ResetQueueWithConfig(CreateConfig());
+ }
- void SetUpForTimeoutTesting(size_t timeout_delay_ms) {
- queue_->SetAckTimeoutEnabled(true, timeout_delay_ms);
+ void SetUpForTimeoutTesting(base::TimeDelta timeout_delay) {
+ TouchEventQueue::Config config = CreateConfig();
+ config.touch_ack_timeout_delay = timeout_delay;
+ config.touch_ack_timeout_supported = true;
+ ResetQueueWithConfig(config);
}
void SendTouchEvent(const WebTouchEvent& event) {
@@ -87,10 +116,18 @@ class TouchEventQueueTest : public testing::Test,
GestureEventWithLatencyInfo(event, ui::LatencyInfo()));
}
- void SendTouchEventACK(InputEventAckState ack_result) {
+ void SendTouchEventAck(InputEventAckState ack_result) {
queue_->ProcessTouchAck(ack_result, ui::LatencyInfo());
}
+ void SendGestureEventAck(WebInputEvent::Type type,
+ InputEventAckState ack_result) {
+ blink::WebGestureEvent gesture_event;
+ gesture_event.type = type;
+ GestureEventWithLatencyInfo event(gesture_event, ui::LatencyInfo());
+ queue_->OnGestureEventAck(event, ack_result);
+ }
+
void SetFollowupEvent(const WebTouchEvent& event) {
followup_touch_event_.reset(new WebTouchEvent(event));
}
@@ -103,17 +140,22 @@ class TouchEventQueueTest : public testing::Test,
sync_ack_result_.reset(new InputEventAckState(sync_ack_result));
}
- void PressTouchPoint(int x, int y) {
+ void PressTouchPoint(float x, float y) {
touch_event_.PressPoint(x, y);
SendTouchEvent();
}
- void MoveTouchPoint(int index, int x, int y) {
+ void MoveTouchPoint(int index, float x, float y) {
touch_event_.MovePoint(index, x, y);
SendTouchEvent();
}
- void MoveTouchPoints(int index0, int x0, int y0, int index1, int x1, int y1) {
+ void MoveTouchPoints(int index0,
+ float x0,
+ float y0,
+ int index1,
+ float x1,
+ float y1) {
touch_event_.MovePoint(index0, x0, y0);
touch_event_.MovePoint(index1, x1, y1);
SendTouchEvent();
@@ -129,6 +171,10 @@ class TouchEventQueueTest : public testing::Test,
SendTouchEvent();
}
+ void AdvanceTouchTime(double seconds) {
+ touch_event_.timeStampSeconds += seconds;
+ }
+
size_t GetAndResetAckedEventCount() {
size_t count = acked_event_count_;
acked_event_count_ = 0;
@@ -145,20 +191,18 @@ class TouchEventQueueTest : public testing::Test,
return queue_->IsPendingAckTouchStart();
}
- void Flush() {
- queue_->FlushQueue();
+ void OnHasTouchEventHandlers(bool has_handlers) {
+ queue_->OnHasTouchEventHandlers(has_handlers);
}
- void SetEnableTouchForwarding(bool enabled) {
- queue_->no_touch_to_renderer_ = !enabled;
- }
+ void SetAckTimeoutDisabled() { queue_->SetAckTimeoutEnabled(false); }
- bool WillForwardTouchEvents() {
- return !queue_->no_touch_to_renderer_ && !queue_->HasTimeoutEvent();
- }
+ bool IsTimeoutEnabled() const { return queue_->ack_timeout_enabled(); }
- bool IsTimeoutRunning() {
- return queue_->IsTimeoutRunningForTesting();
+ bool IsTimeoutRunning() const { return queue_->IsTimeoutRunningForTesting(); }
+
+ bool HasPendingAsyncTouchMove() const {
+ return queue_->HasPendingAsyncTouchMoveForTesting();
}
size_t queued_event_count() const {
@@ -181,12 +225,10 @@ class TouchEventQueueTest : public testing::Test,
return last_acked_event_state_;
}
- void set_no_touch_to_renderer(bool no_touch) {
- queue_->no_touch_to_renderer_ = no_touch;
- }
-
- bool no_touch_to_renderer() const {
- return queue_->no_touch_to_renderer_;
+ static void RunTasksAndWait(base::TimeDelta delay) {
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE, base::MessageLoop::QuitClosure(), delay);
+ base::MessageLoop::current()->Run();
}
private:
@@ -195,6 +237,11 @@ class TouchEventQueueTest : public testing::Test,
touch_event_.ResetPoints();
}
+ void ResetQueueWithConfig(const TouchEventQueue::Config& config) {
+ queue_.reset(new TouchEventQueue(this, config));
+ queue_->OnHasTouchEventHandlers(true);
+ }
+
scoped_ptr<TouchEventQueue> queue_;
size_t sent_event_count_;
size_t acked_event_count_;
@@ -205,6 +252,9 @@ class TouchEventQueueTest : public testing::Test,
scoped_ptr<WebTouchEvent> followup_touch_event_;
scoped_ptr<WebGestureEvent> followup_gesture_event_;
scoped_ptr<InputEventAckState> sync_ack_result_;
+ double slop_length_dips_;
+ bool slop_includes_boundary_;
+ TouchEventQueue::TouchScrollingMode touch_scrolling_mode_;
base::MessageLoopForUI message_loop_;
};
@@ -221,24 +271,26 @@ TEST_F(TouchEventQueueTest, Basic) {
EXPECT_EQ(0U, GetAndResetSentEventCount());
// Receive an ACK for the first touch-event.
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(WebInputEvent::TouchStart, acked_event().type);
+ EXPECT_TRUE(acked_event().cancelable);
// Receive an ACK for the second touch-event.
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(WebInputEvent::TouchMove, acked_event().type);
+ EXPECT_TRUE(acked_event().cancelable);
}
// Tests that the touch-queue is emptied if a page stops listening for touch
// events.
-TEST_F(TouchEventQueueTest, Flush) {
- Flush();
+TEST_F(TouchEventQueueTest, QueueFlushedWhenHandlersRemoved) {
+ OnHasTouchEventHandlers(true);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(0U, GetAndResetSentEventCount());
@@ -259,7 +311,7 @@ TEST_F(TouchEventQueueTest, Flush) {
// Receive an ACK for the first touch-event. One of the queued touch-event
// should be forwarded.
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(31U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
@@ -267,12 +319,84 @@ TEST_F(TouchEventQueueTest, Flush) {
// Flush the queue. The touch-event queue should now be emptied, but none of
// the queued touch-events should be sent to the renderer.
- Flush();
+ OnHasTouchEventHandlers(false);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(31U, GetAndResetAckedEventCount());
}
+// Tests that addition of a touch handler during a touch sequence will not cause
+// the remaining sequence to be forwarded.
+TEST_F(TouchEventQueueTest, ActiveSequenceNotForwardedWhenHandlersAdded) {
+ OnHasTouchEventHandlers(false);
+
+ // Send a touch-press event while there is no handler.
+ PressTouchPoint(1, 1);
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+ EXPECT_EQ(0U, queued_event_count());
+
+ OnHasTouchEventHandlers(true);
+
+ // The remaining touch sequence should not be forwarded.
+ MoveTouchPoint(0, 5, 5);
+ ReleaseTouchPoint(0);
+ EXPECT_EQ(2U, GetAndResetAckedEventCount());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+ EXPECT_EQ(0U, queued_event_count());
+
+ // A new touch sequence should resume forwarding.
+ PressTouchPoint(1, 1);
+ EXPECT_EQ(1U, queued_event_count());
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+}
+
+// Tests that removal of a touch handler during a touch sequence will prevent
+// the remaining sequence from being forwarded, even if another touch handler is
+// registered during the same touch sequence.
+TEST_F(TouchEventQueueTest, ActiveSequenceDroppedWhenHandlersRemoved) {
+ // Send a touch-press event.
+ PressTouchPoint(1, 1);
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, queued_event_count());
+
+ // Queue a touch-move event.
+ MoveTouchPoint(0, 5, 5);
+ EXPECT_EQ(2U, queued_event_count());
+ EXPECT_EQ(0U, GetAndResetAckedEventCount());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+
+ // Touch handle deregistration should flush the queue.
+ OnHasTouchEventHandlers(false);
+ EXPECT_EQ(2U, GetAndResetAckedEventCount());
+ EXPECT_EQ(0U, queued_event_count());
+
+ // The ack should be ignored as the touch queue is now empty.
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
+ EXPECT_EQ(0U, GetAndResetAckedEventCount());
+ EXPECT_EQ(0U, queued_event_count());
+
+ // Events should be dropped while there is no touch handler.
+ MoveTouchPoint(0, 10, 10);
+ EXPECT_EQ(0U, queued_event_count());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+
+ // Simulate touch handler registration in the middle of a touch sequence.
+ OnHasTouchEventHandlers(true);
+
+ // The touch end for the interrupted sequence should be dropped.
+ ReleaseTouchPoint(0);
+ EXPECT_EQ(0U, queued_event_count());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+
+ // A new touch sequence should be forwarded properly.
+ PressTouchPoint(1, 1);
+ EXPECT_EQ(1U, queued_event_count());
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+}
+
// Tests that touch-events are coalesced properly in the queue.
TEST_F(TouchEventQueueTest, Coalesce) {
// Send a touch-press event.
@@ -290,7 +414,7 @@ TEST_F(TouchEventQueueTest, Coalesce) {
EXPECT_EQ(3U, queued_event_count());
// ACK the press. Coalesced touch-move events should be sent.
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(2U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
@@ -298,14 +422,14 @@ TEST_F(TouchEventQueueTest, Coalesce) {
EXPECT_EQ(INPUT_EVENT_ACK_STATE_CONSUMED, acked_event_state());
// ACK the moves.
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(10U, GetAndResetAckedEventCount());
EXPECT_EQ(WebInputEvent::TouchMove, acked_event().type);
// ACK the release.
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
@@ -327,7 +451,7 @@ TEST_F(TouchEventQueueTest, SentTouchEventDoesNotCoalesce) {
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(2U, queued_event_count());
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, queued_event_count());
@@ -389,16 +513,16 @@ TEST_F(TouchEventQueueTest, AckAfterQueueFlushed) {
// Receive an ACK for the press. This should cause the queued touch-move to
// be sent to the renderer.
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, queued_event_count());
- Flush();
+ OnHasTouchEventHandlers(false);
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(0U, queued_event_count());
// Now receive an ACK for the move.
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(0U, queued_event_count());
}
@@ -420,7 +544,7 @@ TEST_F(TouchEventQueueTest, NoConsumer) {
// Receive an ACK for the first touch-event. This should release the queued
// touch-event, but it should not be sent to the renderer.
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(WebInputEvent::TouchMove, acked_event().type);
EXPECT_EQ(2U, GetAndResetAckedEventCount());
@@ -449,24 +573,27 @@ TEST_F(TouchEventQueueTest, NoConsumer) {
// touch-move event, the touch-end event and the second touch-press event.
EXPECT_EQ(4U, queued_event_count());
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(WebInputEvent::TouchEnd, acked_event().type);
EXPECT_EQ(4U, GetAndResetAckedEventCount());
EXPECT_EQ(1U, queued_event_count());
// ACK the second press event as NO_CONSUMER too.
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(WebInputEvent::TouchStart, acked_event().type);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(0U, queued_event_count());
- // Send a second press event. Even though the first touch had NO_CONSUMER,
- // this press event should reach the renderer.
+ // Send a second press event. Even though the first touch press had
+ // NO_CONSUMER, this press event should reach the renderer.
PressTouchPoint(1, 1);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, queued_event_count());
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_EQ(WebInputEvent::TouchStart, acked_event().type);
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
}
TEST_F(TouchEventQueueTest, ConsumerIgnoreMultiFinger) {
@@ -496,12 +623,12 @@ TEST_F(TouchEventQueueTest, ConsumerIgnoreMultiFinger) {
// ACK the first press as CONSUMED. This should cause the first touch-move of
// the first touch-point to be dispatched.
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(3U, queued_event_count());
// ACK the first move as CONSUMED.
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(2U, queued_event_count());
@@ -509,12 +636,12 @@ TEST_F(TouchEventQueueTest, ConsumerIgnoreMultiFinger) {
// touch-move event (which contains both touch points). Although the second
// touch-point does not need to be sent to the renderer, the first touch-point
// did move, and so the coalesced touch-event will be sent to the renderer.
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, queued_event_count());
// ACK the coalesced move as NOT_CONSUMED.
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(0U, queued_event_count());
@@ -535,11 +662,11 @@ TEST_F(TouchEventQueueTest, ConsumerIgnoreMultiFinger) {
MoveTouchPoints(0, 15, 15, 1, 25, 25);
EXPECT_EQ(0U, GetAndResetSentEventCount());
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, queued_event_count());
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(0U, queued_event_count());
@@ -561,14 +688,14 @@ TEST_F(TouchEventQueueTest, ConsumerIgnoreMultiFinger) {
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(4U, queued_event_count());
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(2U, queued_event_count());
EXPECT_EQ(WebInputEvent::TouchMove, acked_event().type);
// ACK the press with NO_CONSUMED_EXISTS. This should release the queued
// touch-move events to the view.
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(WebInputEvent::TouchMove, acked_event().type);
@@ -598,7 +725,7 @@ TEST_F(TouchEventQueueTest, AckWithFollowupEvents) {
// Receive an ACK for the press. This should cause the followup touch-move to
// be sent to the renderer.
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
@@ -611,7 +738,7 @@ TEST_F(TouchEventQueueTest, AckWithFollowupEvents) {
// Receive an ACK for the touch-move followup event. This should cause the
// subsequent touch move event be sent to the renderer.
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
@@ -642,7 +769,7 @@ TEST_F(TouchEventQueueTest, SynchronousAcks) {
// TouchCancel (first inserting a TouchStart so the TouchCancel will be sent)
PressTouchPoint(1, 1);
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
@@ -685,8 +812,8 @@ TEST_F(TouchEventQueueTest, ImmediateAckWithFollowupEvents) {
// Tests basic TouchEvent forwarding suppression.
TEST_F(TouchEventQueueTest, NoTouchBasic) {
// Disable TouchEvent forwarding.
- SetEnableTouchForwarding(false);
- MoveTouchPoint(0, 30, 5);
+ OnHasTouchEventHandlers(false);
+ PressTouchPoint(30, 5);
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
@@ -700,36 +827,32 @@ TEST_F(TouchEventQueueTest, NoTouchBasic) {
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
- // TouchStart should not be sent to renderer.
- PressTouchPoint(5, 5);
- EXPECT_EQ(0U, GetAndResetSentEventCount());
- EXPECT_EQ(1U, GetAndResetAckedEventCount());
-
// Enable TouchEvent forwarding.
- SetEnableTouchForwarding(true);
+ OnHasTouchEventHandlers(true);
PressTouchPoint(80, 10);
EXPECT_EQ(1U, GetAndResetSentEventCount());
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
MoveTouchPoint(0, 80, 20);
EXPECT_EQ(1U, GetAndResetSentEventCount());
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
ReleaseTouchPoint(0);
EXPECT_EQ(1U, GetAndResetSentEventCount());
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
}
// Tests that no TouchEvents are sent to renderer during scrolling.
-TEST_F(TouchEventQueueTest, NoTouchOnScroll) {
+TEST_F(TouchEventQueueTest, TouchCancelOnScroll) {
+ SetTouchScrollingMode(TouchEventQueue::TOUCH_SCROLLING_MODE_TOUCHCANCEL);
// Queue a TouchStart.
PressTouchPoint(0, 1);
EXPECT_EQ(1U, GetAndResetSentEventCount());
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
MoveTouchPoint(0, 20, 5);
@@ -746,17 +869,16 @@ TEST_F(TouchEventQueueTest, NoTouchOnScroll) {
WebGestureEvent followup_scroll;
followup_scroll.type = WebInputEvent::GestureScrollBegin;
SetFollowupEvent(followup_scroll);
- ASSERT_TRUE(WillForwardTouchEvents());
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_FALSE(WillForwardTouchEvents());
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(2U, queued_event_count());
EXPECT_EQ(WebInputEvent::TouchCancel, sent_event().type);
+ EXPECT_FALSE(sent_event().cancelable);
EXPECT_EQ(WebInputEvent::TouchStart, latest_event().type);
// Acking the TouchCancel will result in dispatch of the next TouchStart.
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
// The synthetic TouchCancel should not reach client, only the TouchStart.
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(0U, GetAndResetSentEventCount());
@@ -770,7 +892,6 @@ TEST_F(TouchEventQueueTest, NoTouchOnScroll) {
// GestureScrollUpdates should not change affect touch forwarding.
SendGestureEvent(WebInputEvent::GestureScrollUpdate);
- EXPECT_FALSE(WillForwardTouchEvents());
// TouchEnd should not be sent to the renderer.
ReleaseTouchPoint(0);
@@ -778,24 +899,71 @@ TEST_F(TouchEventQueueTest, NoTouchOnScroll) {
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, acked_event_state());
- // GestureScrollEnd will resume the sending of TouchEvents to renderer.
- SendGestureEvent(blink::WebInputEvent::GestureScrollEnd);
- EXPECT_TRUE(WillForwardTouchEvents());
+ ReleaseTouchPoint(0);
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+ EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, acked_event_state());
- // Now TouchEvents should be forwarded normally.
+ // Touch events from a new gesture sequence should be forwarded normally.
PressTouchPoint(80, 10);
EXPECT_EQ(1U, GetAndResetSentEventCount());
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
MoveTouchPoint(0, 80, 20);
EXPECT_EQ(1U, GetAndResetSentEventCount());
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
ReleaseTouchPoint(0);
EXPECT_EQ(1U, GetAndResetSentEventCount());
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+}
+
+// Tests that a scroll event will not insert a synthetic TouchCancel if there
+// was no consumer for the current touch sequence.
+TEST_F(TouchEventQueueTest, NoTouchCancelOnScrollIfNoConsumer) {
+ SetTouchScrollingMode(TouchEventQueue::TOUCH_SCROLLING_MODE_TOUCHCANCEL);
+
+ // Queue a TouchStart.
+ PressTouchPoint(0, 1);
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+ EXPECT_EQ(WebInputEvent::TouchStart, sent_event().type);
+
+ // Queue a TouchMove that turns into a GestureScrollBegin.
+ WebGestureEvent followup_scroll;
+ followup_scroll.type = WebInputEvent::GestureScrollBegin;
+ SetFollowupEvent(followup_scroll);
+ MoveTouchPoint(0, 20, 5);
+
+ // The TouchMove has no consumer, and should be ack'ed immediately. However,
+ // *no* synthetic TouchCancel should be inserted as the touch sequence
+ // had no consumer.
+ EXPECT_EQ(0U, queued_event_count());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+ EXPECT_EQ(0U, queued_event_count());
+ EXPECT_EQ(WebInputEvent::TouchStart, sent_event().type);
+
+ // Subsequent TouchMove's should not be sent to the renderer.
+ MoveTouchPoint(0, 30, 5);
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+ EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, acked_event_state());
+
+ // TouchEnd should not be sent to the renderer.
+ ReleaseTouchPoint(0);
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+ EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, acked_event_state());
+
+ // Touch events from a new gesture sequence should be forwarded normally.
+ PressTouchPoint(80, 10);
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
}
@@ -815,7 +983,7 @@ TEST_F(TouchEventQueueTest, PendingStart) {
EXPECT_TRUE(IsPendingAckTouchStart());
// Ack the touchstart (#1).
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, queued_event_count());
EXPECT_FALSE(IsPendingAckTouchStart());
@@ -825,7 +993,7 @@ TEST_F(TouchEventQueueTest, PendingStart) {
EXPECT_FALSE(IsPendingAckTouchStart());
// Ack the touchmove (#2).
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, queued_event_count());
EXPECT_TRUE(IsPendingAckTouchStart());
@@ -835,45 +1003,45 @@ TEST_F(TouchEventQueueTest, PendingStart) {
EXPECT_TRUE(IsPendingAckTouchStart());
// Ack the touchstart for the second point (#3).
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, queued_event_count());
EXPECT_TRUE(IsPendingAckTouchStart());
// Ack the touchstart for the third point (#4).
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(0U, queued_event_count());
EXPECT_FALSE(IsPendingAckTouchStart());
}
// Tests that the touch timeout is started when sending certain touch types.
TEST_F(TouchEventQueueTest, TouchTimeoutTypes) {
- SetUpForTimeoutTesting(kDefaultTouchTimeoutDelayMs);
+ SetUpForTimeoutTesting(DefaultTouchTimeoutDelay());
// Sending a TouchStart will start the timeout.
PressTouchPoint(0, 1);
EXPECT_TRUE(IsTimeoutRunning());
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_FALSE(IsTimeoutRunning());
// A TouchMove should start the timeout.
MoveTouchPoint(0, 5, 5);
EXPECT_TRUE(IsTimeoutRunning());
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_FALSE(IsTimeoutRunning());
// A TouchEnd should not start the timeout.
ReleaseTouchPoint(0);
EXPECT_FALSE(IsTimeoutRunning());
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_FALSE(IsTimeoutRunning());
// A TouchCancel should not start the timeout.
PressTouchPoint(0, 1);
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
ASSERT_FALSE(IsTimeoutRunning());
CancelTouchPoint(0);
EXPECT_FALSE(IsTimeoutRunning());
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_FALSE(IsTimeoutRunning());
}
@@ -881,7 +1049,7 @@ TEST_F(TouchEventQueueTest, TouchTimeoutTypes) {
// disabling touch forwarding until the next TouchStart is received after
// the timeout events are ack'ed.
TEST_F(TouchEventQueueTest, TouchTimeoutBasic) {
- SetUpForTimeoutTesting(kDefaultTouchTimeoutDelayMs);
+ SetUpForTimeoutTesting(DefaultTouchTimeoutDelay());
// Queue a TouchStart.
GetAndResetSentEventCount();
@@ -890,52 +1058,52 @@ TEST_F(TouchEventQueueTest, TouchTimeoutBasic) {
ASSERT_EQ(1U, GetAndResetSentEventCount());
ASSERT_EQ(0U, GetAndResetAckedEventCount());
EXPECT_TRUE(IsTimeoutRunning());
- EXPECT_TRUE(WillForwardTouchEvents());
// Delay the ack.
- base::MessageLoop::current()->PostDelayedTask(
- FROM_HERE,
- base::MessageLoop::QuitClosure(),
- base::TimeDelta::FromMilliseconds(kDefaultTouchTimeoutDelayMs * 2));
- base::MessageLoop::current()->Run();
+ RunTasksAndWait(DefaultTouchTimeoutDelay() * 2);
// The timeout should have fired, synthetically ack'ing the timed-out event.
// TouchEvent forwarding is disabled until the ack is received for the
// timed-out event and the future cancel event.
EXPECT_FALSE(IsTimeoutRunning());
- EXPECT_FALSE(WillForwardTouchEvents());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Ack'ing the original event should trigger a cancel event.
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_FALSE(IsTimeoutRunning());
- EXPECT_FALSE(WillForwardTouchEvents());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
EXPECT_EQ(1U, GetAndResetSentEventCount());
+ EXPECT_EQ(WebInputEvent::TouchCancel, sent_event().type);
+ EXPECT_FALSE(sent_event().cancelable);
// Touch events should not be forwarded until we receive the cancel acks.
- PressTouchPoint(0, 1);
+ MoveTouchPoint(0, 1, 1);
+ ASSERT_EQ(0U, GetAndResetSentEventCount());
+ ASSERT_EQ(1U, GetAndResetAckedEventCount());
+
+ ReleaseTouchPoint(0);
ASSERT_EQ(0U, GetAndResetSentEventCount());
ASSERT_EQ(1U, GetAndResetAckedEventCount());
// The synthetic TouchCancel ack should not reach the client, but should
// resume touch forwarding.
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
- EXPECT_TRUE(WillForwardTouchEvents());
// Subsequent events should be handled normally.
PressTouchPoint(0, 1);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
+ EXPECT_EQ(WebInputEvent::TouchStart, sent_event().type);
+ EXPECT_TRUE(sent_event().cancelable);
}
// Tests that the timeout is never started if the renderer consumes
// a TouchEvent from the current touch sequence.
TEST_F(TouchEventQueueTest, NoTouchTimeoutIfRendererIsConsumingGesture) {
- SetUpForTimeoutTesting(kDefaultTouchTimeoutDelayMs);
+ SetUpForTimeoutTesting(DefaultTouchTimeoutDelay());
// Queue a TouchStart.
PressTouchPoint(0, 1);
@@ -943,32 +1111,61 @@ TEST_F(TouchEventQueueTest, NoTouchTimeoutIfRendererIsConsumingGesture) {
// Mark the event as consumed. This should prevent the timeout from
// being activated on subsequent TouchEvents in this gesture.
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_FALSE(IsTimeoutRunning());
// A TouchMove should not start the timeout.
MoveTouchPoint(0, 5, 5);
EXPECT_FALSE(IsTimeoutRunning());
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
// A secondary TouchStart should not start the timeout.
PressTouchPoint(1, 0);
EXPECT_FALSE(IsTimeoutRunning());
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
// A TouchEnd should not start the timeout.
ReleaseTouchPoint(1);
EXPECT_FALSE(IsTimeoutRunning());
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
// A TouchCancel should not start the timeout.
CancelTouchPoint(0);
EXPECT_FALSE(IsTimeoutRunning());
}
+// Tests that the timeout is never started if the renderer consumes
+// a TouchEvent from the current touch sequence.
+TEST_F(TouchEventQueueTest, NoTouchTimeoutIfDisabledAfterTouchStart) {
+ SetUpForTimeoutTesting(DefaultTouchTimeoutDelay());
+
+ // Queue a TouchStart.
+ PressTouchPoint(0, 1);
+ ASSERT_TRUE(IsTimeoutRunning());
+
+ // Send the ack immediately. The timeout should not have fired.
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_FALSE(IsTimeoutRunning());
+ EXPECT_TRUE(IsTimeoutEnabled());
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+ // Now explicitly disable the timeout.
+ SetAckTimeoutDisabled();
+ EXPECT_FALSE(IsTimeoutRunning());
+ EXPECT_FALSE(IsTimeoutEnabled());
+
+ // A TouchMove should not start or trigger the timeout.
+ MoveTouchPoint(0, 5, 5);
+ EXPECT_FALSE(IsTimeoutRunning());
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ RunTasksAndWait(DefaultTouchTimeoutDelay() * 2);
+ EXPECT_EQ(0U, GetAndResetAckedEventCount());
+}
+
// Tests that the timeout is never started if the ack is synchronous.
TEST_F(TouchEventQueueTest, NoTouchTimeoutIfAckIsSynchronous) {
- SetUpForTimeoutTesting(kDefaultTouchTimeoutDelayMs);
+ SetUpForTimeoutTesting(DefaultTouchTimeoutDelay());
// Queue a TouchStart.
SetSyncAckResult(INPUT_EVENT_ACK_STATE_CONSUMED);
@@ -977,15 +1174,43 @@ TEST_F(TouchEventQueueTest, NoTouchTimeoutIfAckIsSynchronous) {
EXPECT_FALSE(IsTimeoutRunning());
}
+// Tests that the timeout is disabled if the touch handler disappears.
+TEST_F(TouchEventQueueTest, NoTouchTimeoutIfTouchHandlerRemoved) {
+ SetUpForTimeoutTesting(DefaultTouchTimeoutDelay());
+
+ // Queue a TouchStart.
+ PressTouchPoint(0, 1);
+ ASSERT_TRUE(IsTimeoutRunning());
+
+ // Unload the touch handler.
+ OnHasTouchEventHandlers(false);
+ EXPECT_FALSE(IsTimeoutRunning());
+}
+
+// Tests that the timeout does not fire if explicitly disabled while an event
+// is in-flight.
+TEST_F(TouchEventQueueTest, NoTouchTimeoutIfDisabledWhileTimerIsActive) {
+ SetUpForTimeoutTesting(DefaultTouchTimeoutDelay());
+
+ // Queue a TouchStart.
+ PressTouchPoint(0, 1);
+ ASSERT_TRUE(IsTimeoutRunning());
+
+ // Verify that disabling the timeout also turns off the timer.
+ SetAckTimeoutDisabled();
+ EXPECT_FALSE(IsTimeoutRunning());
+ RunTasksAndWait(DefaultTouchTimeoutDelay() * 2);
+ EXPECT_EQ(0U, GetAndResetAckedEventCount());
+}
+
// Tests that a TouchCancel timeout plays nice when the timed out touch stream
// turns into a scroll gesture sequence.
TEST_F(TouchEventQueueTest, TouchTimeoutWithFollowupGesture) {
- SetUpForTimeoutTesting(kDefaultTouchTimeoutDelayMs);
+ SetUpForTimeoutTesting(DefaultTouchTimeoutDelay());
// Queue a TouchStart.
PressTouchPoint(0, 1);
EXPECT_TRUE(IsTimeoutRunning());
- EXPECT_TRUE(WillForwardTouchEvents());
EXPECT_EQ(1U, GetAndResetSentEventCount());
// The cancelled sequence may turn into a scroll gesture.
@@ -994,45 +1219,38 @@ TEST_F(TouchEventQueueTest, TouchTimeoutWithFollowupGesture) {
SetFollowupEvent(followup_scroll);
// Delay the ack.
- base::MessageLoop::current()->PostDelayedTask(
- FROM_HERE,
- base::MessageLoop::QuitClosure(),
- base::TimeDelta::FromMilliseconds(kDefaultTouchTimeoutDelayMs * 2));
- base::MessageLoop::current()->Run();
+ RunTasksAndWait(DefaultTouchTimeoutDelay() * 2);
// The timeout should have fired, disabling touch forwarding until both acks
// are received, acking the timed out event.
EXPECT_FALSE(IsTimeoutRunning());
- EXPECT_FALSE(WillForwardTouchEvents());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Ack the original event, triggering a TouchCancel.
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_FALSE(IsTimeoutRunning());
- EXPECT_FALSE(WillForwardTouchEvents());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
// Ack the cancel event. Normally, this would resume touch forwarding,
// but we're still within a scroll gesture so it remains disabled.
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_FALSE(IsTimeoutRunning());
- EXPECT_FALSE(WillForwardTouchEvents());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
- // Try to forward a touch event.
+ // Try to forward touch events for the current sequence.
GetAndResetSentEventCount();
GetAndResetAckedEventCount();
- PressTouchPoint(0, 1);
+ MoveTouchPoint(0, 1, 1);
+ ReleaseTouchPoint(0);
EXPECT_FALSE(IsTimeoutRunning());
EXPECT_EQ(0U, GetAndResetSentEventCount());
- EXPECT_EQ(1U, GetAndResetAckedEventCount());
+ EXPECT_EQ(2U, GetAndResetAckedEventCount());
// Now end the scroll sequence, resuming touch handling.
SendGestureEvent(blink::WebInputEvent::GestureScrollEnd);
- EXPECT_TRUE(WillForwardTouchEvents());
PressTouchPoint(0, 1);
EXPECT_TRUE(IsTimeoutRunning());
EXPECT_EQ(1U, GetAndResetSentEventCount());
@@ -1043,12 +1261,11 @@ TEST_F(TouchEventQueueTest, TouchTimeoutWithFollowupGesture) {
// turns into a scroll gesture sequence, but the original event acks are
// significantly delayed.
TEST_F(TouchEventQueueTest, TouchTimeoutWithFollowupGestureAndDelayedAck) {
- SetUpForTimeoutTesting(kDefaultTouchTimeoutDelayMs);
+ SetUpForTimeoutTesting(DefaultTouchTimeoutDelay());
// Queue a TouchStart.
PressTouchPoint(0, 1);
EXPECT_TRUE(IsTimeoutRunning());
- EXPECT_TRUE(WillForwardTouchEvents());
EXPECT_EQ(1U, GetAndResetSentEventCount());
// The cancelled sequence may turn into a scroll gesture.
@@ -1057,23 +1274,18 @@ TEST_F(TouchEventQueueTest, TouchTimeoutWithFollowupGestureAndDelayedAck) {
SetFollowupEvent(followup_scroll);
// Delay the ack.
- base::MessageLoop::current()->PostDelayedTask(
- FROM_HERE,
- base::MessageLoop::QuitClosure(),
- base::TimeDelta::FromMilliseconds(kDefaultTouchTimeoutDelayMs * 2));
- base::MessageLoop::current()->Run();
+ RunTasksAndWait(DefaultTouchTimeoutDelay() * 2);
// The timeout should have fired, disabling touch forwarding until both acks
// are received and acking the timed out event.
EXPECT_FALSE(IsTimeoutRunning());
- EXPECT_FALSE(WillForwardTouchEvents());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Try to forward a touch event.
GetAndResetSentEventCount();
GetAndResetAckedEventCount();
- PressTouchPoint(0, 1);
+ MoveTouchPoint(0, 1, 1);
EXPECT_FALSE(IsTimeoutRunning());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
@@ -1081,18 +1293,19 @@ TEST_F(TouchEventQueueTest, TouchTimeoutWithFollowupGestureAndDelayedAck) {
// Now end the scroll sequence. Events will not be forwarded until the two
// outstanding touch acks are received.
SendGestureEvent(blink::WebInputEvent::GestureScrollEnd);
- PressTouchPoint(0, 1);
+ MoveTouchPoint(0, 2, 2);
+ ReleaseTouchPoint(0);
EXPECT_FALSE(IsTimeoutRunning());
EXPECT_EQ(0U, GetAndResetSentEventCount());
- EXPECT_EQ(1U, GetAndResetAckedEventCount());
+ EXPECT_EQ(2U, GetAndResetAckedEventCount());
// Ack the original event, triggering a cancel.
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
// Ack the cancel event, resuming touch forwarding.
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
@@ -1104,39 +1317,33 @@ TEST_F(TouchEventQueueTest, TouchTimeoutWithFollowupGestureAndDelayedAck) {
// Tests that a delayed TouchEvent ack will not trigger a TouchCancel timeout if
// the timed-out event had no consumer.
TEST_F(TouchEventQueueTest, NoCancelOnTouchTimeoutWithoutConsumer) {
- SetUpForTimeoutTesting(kDefaultTouchTimeoutDelayMs);
+ SetUpForTimeoutTesting(DefaultTouchTimeoutDelay());
// Queue a TouchStart.
PressTouchPoint(0, 1);
ASSERT_EQ(1U, GetAndResetSentEventCount());
ASSERT_EQ(0U, GetAndResetAckedEventCount());
EXPECT_TRUE(IsTimeoutRunning());
- EXPECT_TRUE(WillForwardTouchEvents());
// Delay the ack.
- base::MessageLoop::current()->PostDelayedTask(
- FROM_HERE,
- base::MessageLoop::QuitClosure(),
- base::TimeDelta::FromMilliseconds(kDefaultTouchTimeoutDelayMs * 2));
- base::MessageLoop::current()->Run();
+ RunTasksAndWait(DefaultTouchTimeoutDelay() * 2);
// The timeout should have fired, synthetically ack'ing the timed out event.
// TouchEvent forwarding is disabled until the original ack is received.
EXPECT_FALSE(IsTimeoutRunning());
- EXPECT_FALSE(WillForwardTouchEvents());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Touch events should not be forwarded until we receive the original ack.
- PressTouchPoint(0, 1);
+ MoveTouchPoint(0, 1, 1);
+ ReleaseTouchPoint(0);
ASSERT_EQ(0U, GetAndResetSentEventCount());
- ASSERT_EQ(1U, GetAndResetAckedEventCount());
+ ASSERT_EQ(2U, GetAndResetAckedEventCount());
// Ack'ing the original event should not trigger a cancel event, as the
// TouchStart had no consumer. However, it should re-enable touch forwarding.
- SendTouchEventACK(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
EXPECT_FALSE(IsTimeoutRunning());
- EXPECT_TRUE(WillForwardTouchEvents());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
EXPECT_EQ(0U, GetAndResetSentEventCount());
@@ -1145,4 +1352,678 @@ TEST_F(TouchEventQueueTest, NoCancelOnTouchTimeoutWithoutConsumer) {
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
}
+
+// Tests that TouchMove's are dropped if within the boundary-inclusive slop
+// suppression region for an unconsumed TouchStart.
+TEST_F(TouchEventQueueTest, TouchMoveSuppressionIncludingSlopBoundary) {
+ const double kSlopLengthDips = 10.;
+ const double kHalfSlopLengthDips = kSlopLengthDips / 2;
+ const bool slop_includes_boundary = true;
+ SetUpForTouchMoveSlopTesting(kSlopLengthDips, slop_includes_boundary);
+
+ // Queue a TouchStart.
+ PressTouchPoint(0, 0);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ ASSERT_EQ(1U, GetAndResetSentEventCount());
+ ASSERT_EQ(1U, GetAndResetAckedEventCount());
+
+ // TouchMove's within the region should be suppressed.
+ MoveTouchPoint(0, 0, kHalfSlopLengthDips);
+ EXPECT_EQ(0U, queued_event_count());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+ EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, acked_event_state());
+
+ MoveTouchPoint(0, kHalfSlopLengthDips, 0);
+ EXPECT_EQ(0U, queued_event_count());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+ EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, acked_event_state());
+
+ MoveTouchPoint(0, -kHalfSlopLengthDips, 0);
+ EXPECT_EQ(0U, queued_event_count());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+ EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, acked_event_state());
+
+ MoveTouchPoint(0, -kSlopLengthDips, 0);
+ EXPECT_EQ(0U, queued_event_count());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+ EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, acked_event_state());
+
+ MoveTouchPoint(0, 0, kSlopLengthDips);
+ EXPECT_EQ(0U, queued_event_count());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+ EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, acked_event_state());
+
+ // As soon as a TouchMove exceeds the (Euclidean) distance, no more
+ // TouchMove's should be suppressed.
+ const double kFortyFiveDegreeSlopLengthXY =
+ kSlopLengthDips * std::sqrt(2.) / 2.;
+ MoveTouchPoint(0, kFortyFiveDegreeSlopLengthXY + .2,
+ kFortyFiveDegreeSlopLengthXY + .2);
+ EXPECT_EQ(1U, queued_event_count());
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ EXPECT_EQ(0U, GetAndResetAckedEventCount());
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+ // Even TouchMove's within the original slop region should now be forwarded.
+ MoveTouchPoint(0, 0, 0);
+ EXPECT_EQ(1U, queued_event_count());
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ EXPECT_EQ(0U, GetAndResetAckedEventCount());
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+ // A new touch sequence should reset suppression.
+ ReleaseTouchPoint(0);
+ PressTouchPoint(0, 0);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ ASSERT_EQ(2U, GetAndResetSentEventCount());
+ ASSERT_EQ(2U, GetAndResetAckedEventCount());
+ ASSERT_EQ(0U, queued_event_count());
+
+ // The slop region is boundary-inclusive.
+ MoveTouchPoint(0, kSlopLengthDips - 1., 0);
+ EXPECT_EQ(0U, queued_event_count());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+ MoveTouchPoint(0, kSlopLengthDips, 0);
+ EXPECT_EQ(0U, queued_event_count());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+}
+
+// Tests that TouchMove's are dropped if within the boundary-exclusive slop
+// suppression region for an unconsumed TouchStart.
+TEST_F(TouchEventQueueTest, TouchMoveSuppressionExcludingSlopBoundary) {
+ const double kSlopLengthDips = 10.;
+ const double kHalfSlopLengthDips = kSlopLengthDips / 2;
+ const bool slop_includes_boundary = false;
+ SetUpForTouchMoveSlopTesting(kSlopLengthDips, slop_includes_boundary);
+
+ // Queue a TouchStart.
+ PressTouchPoint(0, 0);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ ASSERT_EQ(1U, GetAndResetSentEventCount());
+ ASSERT_EQ(1U, GetAndResetAckedEventCount());
+
+ // TouchMove's within the region should be suppressed.
+ MoveTouchPoint(0, 0, kHalfSlopLengthDips);
+ EXPECT_EQ(0U, queued_event_count());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+ EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, acked_event_state());
+
+ MoveTouchPoint(0, kSlopLengthDips - 0.2f, 0);
+ EXPECT_EQ(0U, queued_event_count());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+ EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, acked_event_state());
+
+ // As soon as a TouchMove reaches the (Euclidean) slop distance, no more
+ // TouchMove's should be suppressed.
+ MoveTouchPoint(0, kSlopLengthDips, 0);
+ EXPECT_EQ(1U, queued_event_count());
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ EXPECT_EQ(0U, GetAndResetAckedEventCount());
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+ MoveTouchPoint(0, kHalfSlopLengthDips, 0);
+ EXPECT_EQ(1U, queued_event_count());
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ EXPECT_EQ(0U, GetAndResetAckedEventCount());
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+}
+
+// Tests that TouchMove's are not dropped within the slop suppression region if
+// the touchstart was consumed.
+TEST_F(TouchEventQueueTest, NoTouchMoveSuppressionAfterTouchConsumed) {
+ const double kSlopLengthDips = 10.;
+ const double kHalfSlopLengthDips = kSlopLengthDips / 2;
+ const bool slop_includes_boundary = true;
+ SetUpForTouchMoveSlopTesting(kSlopLengthDips, slop_includes_boundary);
+
+ // Queue a TouchStart.
+ PressTouchPoint(0, 0);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
+ ASSERT_EQ(1U, GetAndResetSentEventCount());
+ ASSERT_EQ(1U, GetAndResetAckedEventCount());
+
+ // TouchMove's within the region should not be suppressed, as a touch was
+ // consumed.
+ MoveTouchPoint(0, 0, kHalfSlopLengthDips);
+ EXPECT_EQ(1U, queued_event_count());
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ EXPECT_EQ(0U, GetAndResetAckedEventCount());
+}
+
+// Tests that TouchMove's are not dropped due to incorrect handling of DPI
+// scaling.
+TEST_F(TouchEventQueueTest, TouchMoveSuppressionWithDIPScaling) {
+ const float kSlopLengthPixels = 7.f;
+ const float kDPIScale = 3.f;
+ const bool slop_includes_boundary = true;
+ SetUpForTouchMoveSlopTesting(kSlopLengthPixels / kDPIScale,
+ slop_includes_boundary);
+
+ // Queue a TouchStart.
+ PressTouchPoint(0, 0);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ ASSERT_EQ(1U, GetAndResetSentEventCount());
+ ASSERT_EQ(1U, GetAndResetAckedEventCount());
+
+ // TouchMove's along the slop boundary should be suppresed.
+ MoveTouchPoint(0, 0, kSlopLengthPixels / kDPIScale);
+ EXPECT_EQ(0U, queued_event_count());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+ // Reset the touch sequence.
+ ReleaseTouchPoint(0);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ GetAndResetSentEventCount();
+ GetAndResetAckedEventCount();
+
+ // Queue a TouchStart.
+ PressTouchPoint(0, 0);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ ASSERT_EQ(1U, GetAndResetSentEventCount());
+ ASSERT_EQ(1U, GetAndResetAckedEventCount());
+
+ // TouchMove's outside the region should not be suppressed.
+ const float kPixelCoordOutsideSlopRegion = kSlopLengthPixels + 0.5f;
+ MoveTouchPoint(0, 0, kPixelCoordOutsideSlopRegion / kDPIScale);
+ EXPECT_EQ(1U, queued_event_count());
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ EXPECT_EQ(0U, GetAndResetAckedEventCount());
+}
+
+// Tests that TouchMove's are not dropped if a secondary pointer is present
+// during any movement.
+TEST_F(TouchEventQueueTest, NoTouchMoveSuppressionAfterMultiTouch) {
+ const double kSlopLengthDips = 10.;
+ const double kHalfSlopLengthDips = kSlopLengthDips / 2;
+ const bool slop_includes_boundary = true;
+ SetUpForTouchMoveSlopTesting(kSlopLengthDips, slop_includes_boundary);
+
+ // Queue a TouchStart.
+ PressTouchPoint(0, 0);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ ASSERT_EQ(1U, GetAndResetSentEventCount());
+ ASSERT_EQ(1U, GetAndResetAckedEventCount());
+
+ // TouchMove's within the region should be suppressed.
+ MoveTouchPoint(0, 0, kHalfSlopLengthDips);
+ EXPECT_EQ(0U, queued_event_count());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+ // Simulate a secondary pointer press.
+ PressTouchPoint(kSlopLengthDips, 0);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+ // TouchMove with a secondary pointer should not be suppressed.
+ MoveTouchPoint(1, kSlopLengthDips, 0);
+ EXPECT_EQ(1U, queued_event_count());
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+ // Release the secondary pointer.
+ ReleaseTouchPoint(0);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+ // TouchMove's should not should be suppressed, even with the original
+ // unmoved pointer.
+ MoveTouchPoint(0, 0, 0);
+ EXPECT_EQ(1U, queued_event_count());
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ EXPECT_EQ(0U, GetAndResetAckedEventCount());
+}
+
+// Tests that secondary touch points can be forwarded even if the primary touch
+// point had no consumer.
+TEST_F(TouchEventQueueTest, SecondaryTouchForwardedAfterPrimaryHadNoConsumer) {
+ // Queue a TouchStart.
+ PressTouchPoint(0, 0);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
+ ASSERT_EQ(1U, GetAndResetSentEventCount());
+ ASSERT_EQ(1U, GetAndResetAckedEventCount());
+
+ // Events should not be forwarded, as the point had no consumer.
+ MoveTouchPoint(0, 0, 15);
+ EXPECT_EQ(0U, queued_event_count());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+ // Simulate a secondary pointer press.
+ PressTouchPoint(20, 0);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+ // TouchMove with a secondary pointer should not be suppressed.
+ MoveTouchPoint(1, 25, 0);
+ EXPECT_EQ(1U, queued_event_count());
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+}
+
+// Tests that no touch points will be forwarded after scrolling begins while no
+// touch points have a consumer.
+TEST_F(TouchEventQueueTest, NoForwardingAfterScrollWithNoTouchConsumers) {
+ // Queue a TouchStart.
+ PressTouchPoint(0, 0);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
+ ASSERT_EQ(1U, GetAndResetSentEventCount());
+ ASSERT_EQ(1U, GetAndResetAckedEventCount());
+
+ WebGestureEvent followup_scroll;
+ followup_scroll.type = WebInputEvent::GestureScrollBegin;
+ SetFollowupEvent(followup_scroll);
+ MoveTouchPoint(0, 20, 5);
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+ EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, acked_event_state());
+
+ // The secondary pointer press should not be forwarded.
+ PressTouchPoint(20, 0);
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+ EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, acked_event_state());
+
+ // Neither should any further touchmoves be forwarded.
+ MoveTouchPoint(1, 25, 0);
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+ EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, acked_event_state());
+}
+
+TEST_F(TouchEventQueueTest, SyncTouchMoveDoesntCancelTouchOnScroll) {
+ SetTouchScrollingMode(TouchEventQueue::TOUCH_SCROLLING_MODE_SYNC_TOUCHMOVE);
+ // Queue a TouchStart.
+ PressTouchPoint(0, 1);
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+ MoveTouchPoint(0, 20, 5);
+ EXPECT_EQ(1U, queued_event_count());
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+
+ // GestureScrollBegin doesn't insert a synthetic TouchCancel.
+ WebGestureEvent followup_scroll;
+ followup_scroll.type = WebInputEvent::GestureScrollBegin;
+ SetFollowupEvent(followup_scroll);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+ EXPECT_EQ(0U, queued_event_count());
+}
+
+TEST_F(TouchEventQueueTest, AsyncTouch) {
+ SetTouchScrollingMode(TouchEventQueue::TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE);
+
+ // Queue a TouchStart.
+ PressTouchPoint(0, 1);
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+ for (int i = 0; i < 3; ++i) {
+ SendGestureEventAck(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+
+ MoveTouchPoint(0, 10, 5);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_FALSE(HasPendingAsyncTouchMove());
+ EXPECT_TRUE(sent_event().cancelable);
+ EXPECT_EQ(0U, queued_event_count());
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+
+ // Consuming a scroll event will throttle subsequent touchmoves.
+ SendGestureEventAck(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_CONSUMED);
+ MoveTouchPoint(0, 10, 5);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_TRUE(HasPendingAsyncTouchMove());
+ EXPECT_EQ(0U, queued_event_count());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+ }
+}
+
+// Ensure that touchmove's are appropriately throttled during a typical
+// scroll sequences that transitions between scrolls consumed and unconsumed.
+TEST_F(TouchEventQueueTest, AsyncTouchThrottledAfterScroll) {
+ SetTouchScrollingMode(TouchEventQueue::TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE);
+
+ // Process a TouchStart
+ PressTouchPoint(0, 1);
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+ // Now send the first touch move and associated GestureScrollBegin,
+ // but don't ACK the gesture event yet.
+ MoveTouchPoint(0, 0, 5);
+ WebGestureEvent followup_scroll;
+ followup_scroll.type = WebInputEvent::GestureScrollBegin;
+ SetFollowupEvent(followup_scroll);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+ // Now queue a second touchmove and verify it's not (yet) dispatched.
+ MoveTouchPoint(0, 0, 10);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_TRUE(HasPendingAsyncTouchMove());
+ EXPECT_EQ(0U, queued_event_count());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+ // Queuing the final touchend should flush the pending, async touchmove.
+ ReleaseTouchPoint(0);
+ EXPECT_EQ(WebInputEvent::TouchMove, sent_event().type);
+ EXPECT_FALSE(HasPendingAsyncTouchMove());
+ EXPECT_FALSE(sent_event().cancelable);
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ EXPECT_EQ(2U, queued_event_count());
+
+ // Ack the flushed, async touchmove. The ack should not reach the client, but
+ // it should trigger sending of the (now non-cancelable) touchend.
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(WebInputEvent::TouchEnd, sent_event().type);
+ EXPECT_FALSE(sent_event().cancelable);
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ EXPECT_EQ(0U, GetAndResetAckedEventCount());
+ EXPECT_EQ(1U, queued_event_count());
+
+ // Ack the touchend.
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(0U, queued_event_count());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+ // Now mark the scroll as not consumed (which would cause future
+ // touchmoves in the active sequence to be sent if there was one).
+ SendGestureEventAck(WebInputEvent::GestureScrollBegin,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+
+ // Start a new touch sequence and verify that throttling has been reset.
+ // Touch moves after the start of scrolling will again be throttled.
+ PressTouchPoint(0, 0);
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+ MoveTouchPoint(0, 0, 5);
+ SetFollowupEvent(followup_scroll);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+ MoveTouchPoint(0, 0, 10);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_TRUE(HasPendingAsyncTouchMove());
+ EXPECT_EQ(0U, queued_event_count());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+ // As soon as a touchmove exceeds the outer slop region it will be forwarded
+ // immediately.
+ MoveTouchPoint(0, 0, 20);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_FALSE(HasPendingAsyncTouchMove());
+ EXPECT_FALSE(sent_event().cancelable);
+ EXPECT_EQ(0U, queued_event_count());
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+ // Subsequent touchmove's should be deferred.
+ MoveTouchPoint(0, 0, 25);
+ EXPECT_TRUE(HasPendingAsyncTouchMove());
+ EXPECT_EQ(0U, queued_event_count());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+ // The pending touchmove should be flushed with the the new touchmove if
+ // sufficient time has passed.
+ AdvanceTouchTime(kMinSecondsBetweenThrottledTouchmoves + 0.1);
+ MoveTouchPoint(0, 0, 15);
+ EXPECT_FALSE(HasPendingAsyncTouchMove());
+ EXPECT_FALSE(sent_event().cancelable);
+ EXPECT_EQ(1U, queued_event_count());
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ EXPECT_EQ(0U, GetAndResetAckedEventCount());
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(0U, queued_event_count());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+ // Non-touchmove events should always flush any pending touchmove events.
+ MoveTouchPoint(0, 0, 25);
+ EXPECT_TRUE(HasPendingAsyncTouchMove());
+ EXPECT_EQ(0U, queued_event_count());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+ PressTouchPoint(30, 30);
+ EXPECT_FALSE(HasPendingAsyncTouchMove());
+ EXPECT_FALSE(sent_event().cancelable);
+ EXPECT_EQ(WebInputEvent::TouchMove, sent_event().type);
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ EXPECT_EQ(2U, queued_event_count());
+
+ // Ack'ing the flushed, async touchmove will dispatch the touchstart. Note
+ // that the flushed touchmove's ack will not reach the client (its
+ // constituent events have already been ack'ed).
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_FALSE(sent_event().cancelable);
+ EXPECT_EQ(WebInputEvent::TouchStart, sent_event().type);
+ EXPECT_EQ(1U, queued_event_count());
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ EXPECT_EQ(0U, GetAndResetAckedEventCount());
+
+ // Ack the touchstart.
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(0U, queued_event_count());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+ // Send a secondary touchmove.
+ MoveTouchPoint(1, 0, 25);
+ EXPECT_TRUE(HasPendingAsyncTouchMove());
+ EXPECT_EQ(0U, queued_event_count());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+ // An unconsumed scroll should resume synchronous touch handling.
+ SendGestureEventAck(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+
+ // The pending touchmove should be coalesced with the next (now synchronous)
+ // touchmove.
+ MoveTouchPoint(0, 0, 25);
+ EXPECT_TRUE(sent_event().cancelable);
+ EXPECT_FALSE(HasPendingAsyncTouchMove());
+ EXPECT_EQ(WebInputEvent::TouchMove, sent_event().type);
+ EXPECT_EQ(WebTouchPoint::StateMoved, sent_event().touches[0].state);
+ EXPECT_EQ(WebTouchPoint::StateMoved, sent_event().touches[1].state);
+ EXPECT_EQ(1U, queued_event_count());
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ EXPECT_EQ(0U, GetAndResetAckedEventCount());
+
+ // Subsequent touches will queue until the preceding, synchronous touches are
+ // ack'ed.
+ ReleaseTouchPoint(1);
+ EXPECT_EQ(2U, queued_event_count());
+ ReleaseTouchPoint(0);
+ EXPECT_EQ(3U, queued_event_count());
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_TRUE(sent_event().cancelable);
+ EXPECT_EQ(WebInputEvent::TouchEnd, sent_event().type);
+ EXPECT_EQ(2U, queued_event_count());
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_TRUE(sent_event().cancelable);
+ EXPECT_EQ(WebInputEvent::TouchEnd, sent_event().type);
+ EXPECT_EQ(1U, queued_event_count());
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(0U, queued_event_count());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+}
+
+// Ensure that async touch dispatch and touch ack timeout interactions work
+// appropriately.
+TEST_F(TouchEventQueueTest, AsyncTouchWithAckTimeout) {
+ SetTouchScrollingMode(TouchEventQueue::TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE);
+ SetUpForTimeoutTesting(DefaultTouchTimeoutDelay());
+
+ // The touchstart should start the timeout.
+ PressTouchPoint(0, 0);
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ EXPECT_TRUE(IsTimeoutRunning());
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+ EXPECT_FALSE(IsTimeoutRunning());
+
+ // The start of a scroll gesture should trigger async touch event dispatch.
+ MoveTouchPoint(0, 1, 1);
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ EXPECT_TRUE(IsTimeoutRunning());
+ WebGestureEvent followup_scroll;
+ followup_scroll.type = WebInputEvent::GestureScrollBegin;
+ SetFollowupEvent(followup_scroll);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_FALSE(IsTimeoutRunning());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+ SendGestureEventAck(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_CONSUMED);
+
+ // An async touch should fire after the throttling interval has expired, but
+ // it should not start the touch ack timeout.
+ MoveTouchPoint(0, 5, 5);
+ EXPECT_TRUE(HasPendingAsyncTouchMove());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+ AdvanceTouchTime(kMinSecondsBetweenThrottledTouchmoves + 0.1);
+ MoveTouchPoint(0, 5, 5);
+ EXPECT_FALSE(IsTimeoutRunning());
+ EXPECT_FALSE(HasPendingAsyncTouchMove());
+ EXPECT_FALSE(sent_event().cancelable);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+
+ // An unconsumed scroll event will resume synchronous touchmoves, which are
+ // subject to the ack timeout.
+ SendGestureEventAck(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ MoveTouchPoint(0, 20, 5);
+ EXPECT_TRUE(IsTimeoutRunning());
+ EXPECT_TRUE(sent_event().cancelable);
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+
+ // The timeout should fire, disabling touch forwarding until both acks are
+ // received and acking the timed out event.
+ RunTasksAndWait(DefaultTouchTimeoutDelay() * 2);
+ EXPECT_FALSE(IsTimeoutRunning());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+
+ // Ack'ing the original event should trigger a cancel event.
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_FALSE(sent_event().cancelable);
+ EXPECT_EQ(0U, GetAndResetAckedEventCount());
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+
+ // Subsequent touchmove's should not be forwarded, even as the scroll gesture
+ // goes from unconsumed to consumed.
+ SendGestureEventAck(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ MoveTouchPoint(0, 20, 5);
+ EXPECT_FALSE(HasPendingAsyncTouchMove());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+
+ SendGestureEventAck(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ MoveTouchPoint(0, 25, 5);
+ EXPECT_FALSE(HasPendingAsyncTouchMove());
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+}
+
+// Ensure that if the touch ack for an async touchmove triggers a follow-up
+// touch event, that follow-up touch will be forwarded appropriately.
+TEST_F(TouchEventQueueTest, AsyncTouchWithTouchCancelAfterAck) {
+ SetTouchScrollingMode(TouchEventQueue::TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE);
+
+ PressTouchPoint(0, 0);
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+ // The start of a scroll gesture should trigger async touch event dispatch.
+ MoveTouchPoint(0, 1, 1);
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ WebGestureEvent followup_scroll;
+ followup_scroll.type = WebInputEvent::GestureScrollBegin;
+ SetFollowupEvent(followup_scroll);
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+ EXPECT_EQ(0U, queued_event_count());
+
+ // The async touchmove should be ack'ed immediately, but not forwarded.
+ // However, because the ack triggers a touchcancel, both the pending touch and
+ // the queued touchcancel should be flushed.
+ WebTouchEvent followup_cancel;
+ followup_cancel.type = WebInputEvent::TouchCancel;
+ followup_cancel.touchesLength = 1;
+ followup_cancel.touches[0].state = WebTouchPoint::StateCancelled;
+ SetFollowupEvent(followup_cancel);
+ MoveTouchPoint(0, 5, 5);
+ EXPECT_EQ(2U, queued_event_count());
+ EXPECT_FALSE(sent_event().cancelable);
+ EXPECT_FALSE(HasPendingAsyncTouchMove());
+ EXPECT_EQ(WebInputEvent::TouchMove, acked_event().type);
+ EXPECT_EQ(WebInputEvent::TouchMove, sent_event().type);
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+
+ // The ack for the asnc touchmove should not reach the client, as it has
+ // already been ack'ed.
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_FALSE(sent_event().cancelable);
+ EXPECT_EQ(1U, queued_event_count());
+ EXPECT_EQ(WebInputEvent::TouchCancel, sent_event().type);
+ EXPECT_EQ(0U, GetAndResetAckedEventCount());
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+
+ SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(0U, queued_event_count());
+ EXPECT_EQ(WebInputEvent::TouchCancel, acked_event().type);
+ EXPECT_EQ(1U, GetAndResetAckedEventCount());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+}
+
} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/touch_input_browsertest.cc b/chromium/content/browser/renderer_host/input/touch_input_browsertest.cc
index 859e1a4af45..758f3ec6a29 100644
--- a/chromium/content/browser/renderer_host/input/touch_input_browsertest.cc
+++ b/chromium/content/browser/renderer_host/input/touch_input_browsertest.cc
@@ -5,6 +5,7 @@
#include "base/auto_reset.h"
#include "base/command_line.h"
#include "base/run_loop.h"
+#include "content/browser/gpu/compositor_util.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/input/synthetic_web_input_event_builders.h"
@@ -13,9 +14,9 @@
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/common/content_switches.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/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
#include "ui/events/event_switches.h"
#include "ui/events/latency_info.h"
@@ -82,7 +83,8 @@ namespace content {
class InputEventMessageFilter : public BrowserMessageFilter {
public:
InputEventMessageFilter()
- : type_(WebInputEvent::Undefined),
+ : BrowserMessageFilter(InputMsgStart),
+ type_(WebInputEvent::Undefined),
state_(INPUT_EVENT_ACK_STATE_UNKNOWN) {}
void WaitForAck(WebInputEvent::Type type) {
@@ -106,13 +108,12 @@ class InputEventMessageFilter : public BrowserMessageFilter {
}
// BrowserMessageFilter:
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE {
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
if (message.type() == InputHostMsg_HandleInputEvent_ACK::ID) {
- ui::LatencyInfo latency;
- WebInputEvent::Type type = WebInputEvent::Undefined;
- InputEventAckState ack = INPUT_EVENT_ACK_STATE_UNKNOWN;
- InputHostMsg_HandleInputEvent_ACK::Read(&message, &type, &ack, &latency);
+ InputHostMsg_HandleInputEvent_ACK::Param params;
+ InputHostMsg_HandleInputEvent_ACK::Read(&message, &params);
+ WebInputEvent::Type type = params.a.type;
+ InputEventAckState ack = params.a.state;
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(&InputEventMessageFilter::ReceivedEventAck,
this, type, ack));
@@ -127,8 +128,7 @@ class InputEventMessageFilter : public BrowserMessageFilter {
DISALLOW_COPY_AND_ASSIGN(InputEventMessageFilter);
};
-class TouchInputBrowserTest : public ContentBrowserTest,
- public testing::WithParamInterface<std::string> {
+class TouchInputBrowserTest : public ContentBrowserTest {
public:
TouchInputBrowserTest() {}
virtual ~TouchInputBrowserTest() {}
@@ -159,37 +159,21 @@ class TouchInputBrowserTest : public ContentBrowserTest,
host->GetProcess()->AddFilter(filter_);
}
- // ContentBrowserTest:
- virtual void SetUp() OVERRIDE {
- // We expect real pixel output for these tests.
- UseRealGLContexts();
-
- // On legacy windows, these tests need real GL bindings to pass.
-#if defined(OS_WIN) && !defined(USE_AURA)
- UseRealGLBindings();
-#endif
-
- ContentBrowserTest::SetUp();
- }
-
virtual void SetUpCommandLine(CommandLine* cmd) OVERRIDE {
cmd->AppendSwitchASCII(switches::kTouchEvents,
switches::kTouchEventsEnabled);
- cmd->AppendSwitch(GetParam());
}
scoped_refptr<InputEventMessageFilter> filter_;
};
-// Touch input event tests don't work on Mac with the legacy software renderer.
-// These can be enabled when software compositing is enabled.
-// http://crbug.com/268038
#if defined(OS_MACOSX)
+// TODO(ccameron): Failing on mac: crbug.com/346363
#define MAYBE_TouchNoHandler DISABLED_TouchNoHandler
#else
#define MAYBE_TouchNoHandler TouchNoHandler
#endif
-IN_PROC_BROWSER_TEST_P(TouchInputBrowserTest, MAYBE_TouchNoHandler) {
+IN_PROC_BROWSER_TEST_F(TouchInputBrowserTest, MAYBE_TouchNoHandler) {
LoadURLAndAddFilter();
SyntheticWebTouchEvent touch;
@@ -199,7 +183,7 @@ IN_PROC_BROWSER_TEST_P(TouchInputBrowserTest, MAYBE_TouchNoHandler) {
GetWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo());
filter()->WaitForAck(WebInputEvent::TouchStart);
- if (GetParam() == std::string(switches::kEnableThreadedCompositing)) {
+ if (content::IsThreadedCompositingEnabled()) {
EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS,
filter()->last_ack_state());
} else {
@@ -215,15 +199,7 @@ IN_PROC_BROWSER_TEST_P(TouchInputBrowserTest, MAYBE_TouchNoHandler) {
touch.ResetPoints();
}
-// Touch input event tests don't work on Mac with the legacy software renderer.
-// These can be enabled when software compositing is enabled.
-// http://crbug.com/268038
-#if defined(OS_MACOSX)
-#define MAYBE_TouchHandlerNoConsume DISABLED_TouchHandlerNoConsume
-#else
-#define MAYBE_TouchHandlerNoConsume TouchHandlerNoConsume
-#endif
-IN_PROC_BROWSER_TEST_P(TouchInputBrowserTest, MAYBE_TouchHandlerNoConsume) {
+IN_PROC_BROWSER_TEST_F(TouchInputBrowserTest, TouchHandlerNoConsume) {
LoadURLAndAddFilter();
SyntheticWebTouchEvent touch;
@@ -240,15 +216,7 @@ IN_PROC_BROWSER_TEST_P(TouchInputBrowserTest, MAYBE_TouchHandlerNoConsume) {
touch.ResetPoints();
}
-// Touch input event tests don't work on Mac with the legacy software renderer.
-// These can be enabled when software compositing is enabled.
-// http://crbug.com/268038
-#if defined(OS_MACOSX)
-#define MAYBE_TouchHandlerConsume DISABLED_TouchHandlerConsume
-#else
-#define MAYBE_TouchHandlerConsume TouchHandlerConsume
-#endif
-IN_PROC_BROWSER_TEST_P(TouchInputBrowserTest, MAYBE_TouchHandlerConsume) {
+IN_PROC_BROWSER_TEST_F(TouchInputBrowserTest, TouchHandlerConsume) {
LoadURLAndAddFilter();
SyntheticWebTouchEvent touch;
@@ -264,15 +232,13 @@ IN_PROC_BROWSER_TEST_P(TouchInputBrowserTest, MAYBE_TouchHandlerConsume) {
filter()->WaitForAck(WebInputEvent::TouchEnd);
}
-// Touch input event tests don't work on Mac with the legacy software renderer.
-// These can be enabled when software compositing is enabled.
-// http://crbug.com/268038
#if defined(OS_MACOSX)
+// TODO(ccameron): Failing on mac: crbug.com/346363
#define MAYBE_MultiPointTouchPress DISABLED_MultiPointTouchPress
#else
#define MAYBE_MultiPointTouchPress MultiPointTouchPress
#endif
-IN_PROC_BROWSER_TEST_P(TouchInputBrowserTest, MAYBE_MultiPointTouchPress) {
+IN_PROC_BROWSER_TEST_F(TouchInputBrowserTest, MAYBE_MultiPointTouchPress) {
LoadURLAndAddFilter();
SyntheticWebTouchEvent touch;
@@ -281,7 +247,7 @@ IN_PROC_BROWSER_TEST_P(TouchInputBrowserTest, MAYBE_MultiPointTouchPress) {
touch.PressPoint(25, 25);
GetWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo());
filter()->WaitForAck(WebInputEvent::TouchStart);
- if (GetParam() == std::string(switches::kEnableThreadedCompositing)) {
+ if (content::IsThreadedCompositingEnabled()) {
EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS,
filter()->last_ack_state());
} else {
@@ -296,12 +262,4 @@ IN_PROC_BROWSER_TEST_P(TouchInputBrowserTest, MAYBE_MultiPointTouchPress) {
EXPECT_EQ(INPUT_EVENT_ACK_STATE_CONSUMED, filter()->last_ack_state());
}
-INSTANTIATE_TEST_CASE_P(WithoutInputHandlerProxy, TouchInputBrowserTest,
- ::testing::Values(std::string(switches::kDisableThreadedCompositing)));
-
-#if !defined(OS_MACOSX)
-INSTANTIATE_TEST_CASE_P(WithInputHandlerProxy, TouchInputBrowserTest,
- ::testing::Values(std::string(switches::kEnableThreadedCompositing)));
-#endif
-
} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/touchpad_tap_suppression_controller.cc b/chromium/content/browser/renderer_host/input/touchpad_tap_suppression_controller.cc
index d9207792027..d3097420d16 100644
--- a/chromium/content/browser/renderer_host/input/touchpad_tap_suppression_controller.cc
+++ b/chromium/content/browser/renderer_host/input/touchpad_tap_suppression_controller.cc
@@ -4,45 +4,43 @@
#include "content/browser/renderer_host/input/touchpad_tap_suppression_controller.h"
-#include "content/browser/renderer_host/input/tap_suppression_controller.h"
-#include "content/browser/renderer_host/input/tap_suppression_controller_client.h"
-
-// The default implementation of the TouchpadTapSuppressionController does not
-// suppress taps. Touchpad tap suppression is needed only on CrOS.
-
namespace content {
TouchpadTapSuppressionController::TouchpadTapSuppressionController(
- TouchpadTapSuppressionControllerClient* /* client */)
- : client_(NULL) {}
+ TouchpadTapSuppressionControllerClient* client,
+ const TapSuppressionController::Config& config)
+ : client_(client), controller_(this, config) {
+}
TouchpadTapSuppressionController::~TouchpadTapSuppressionController() {}
-void TouchpadTapSuppressionController::GestureFlingCancel() {}
-
-void TouchpadTapSuppressionController::GestureFlingCancelAck(
- bool /*processed*/) {
+void TouchpadTapSuppressionController::GestureFlingCancel() {
+ controller_.GestureFlingCancel();
}
-bool TouchpadTapSuppressionController::ShouldDeferMouseDown(
- const MouseEventWithLatencyInfo& /*event*/) {
- return false;
+void TouchpadTapSuppressionController::GestureFlingCancelAck(bool processed) {
+ controller_.GestureFlingCancelAck(processed);
}
-bool TouchpadTapSuppressionController::ShouldSuppressMouseUp() { return false; }
-
-int TouchpadTapSuppressionController::MaxCancelToDownTimeInMs() {
- return 0;
+bool TouchpadTapSuppressionController::ShouldDeferMouseDown(
+ const MouseEventWithLatencyInfo& event) {
+ bool should_defer = controller_.ShouldDeferTapDown();
+ if (should_defer)
+ stashed_mouse_down_ = event;
+ return should_defer;
}
-int TouchpadTapSuppressionController::MaxTapGapTimeInMs() {
- return 0;
+bool TouchpadTapSuppressionController::ShouldSuppressMouseUp() {
+ return controller_.ShouldSuppressTapEnd();
}
void TouchpadTapSuppressionController::DropStashedTapDown() {
}
void TouchpadTapSuppressionController::ForwardStashedTapDown() {
+ // Mouse downs are not handled by gesture event filter; so, they are
+ // immediately forwarded to the renderer.
+ client_->SendMouseEventImmediately(stashed_mouse_down_);
}
} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/touchpad_tap_suppression_controller.h b/chromium/content/browser/renderer_host/input/touchpad_tap_suppression_controller.h
index 64b2ed4bc2a..da74dfd6538 100644
--- a/chromium/content/browser/renderer_host/input/touchpad_tap_suppression_controller.h
+++ b/chromium/content/browser/renderer_host/input/touchpad_tap_suppression_controller.h
@@ -5,10 +5,10 @@
#ifndef CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCHPAD_TAP_SUPPRESSION_CONTROLLER_H_
#define CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCHPAD_TAP_SUPPRESSION_CONTROLLER_H_
-#include "base/memory/scoped_ptr.h"
+#include "content/browser/renderer_host/event_with_latency_info.h"
+#include "content/browser/renderer_host/input/tap_suppression_controller.h"
#include "content/browser/renderer_host/input/tap_suppression_controller_client.h"
#include "content/common/content_export.h"
-#include "content/port/browser/event_with_latency_info.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
namespace content {
@@ -27,8 +27,9 @@ class CONTENT_EXPORT TouchpadTapSuppressionControllerClient {
class TouchpadTapSuppressionController : public TapSuppressionControllerClient {
public:
// The |client| must outlive the TouchpadTapSupressionController.
- explicit TouchpadTapSuppressionController(
- TouchpadTapSuppressionControllerClient* client);
+ TouchpadTapSuppressionController(
+ TouchpadTapSuppressionControllerClient* client,
+ const TapSuppressionController::Config& config);
virtual ~TouchpadTapSuppressionController();
// Should be called on arrival of GestureFlingCancel events.
@@ -51,8 +52,6 @@ class TouchpadTapSuppressionController : public TapSuppressionControllerClient {
friend class MockRenderWidgetHost;
// TapSuppressionControllerClient implementation.
- virtual int MaxCancelToDownTimeInMs() OVERRIDE;
- virtual int MaxTapGapTimeInMs() OVERRIDE;
virtual void DropStashedTapDown() OVERRIDE;
virtual void ForwardStashedTapDown() OVERRIDE;
@@ -60,7 +59,7 @@ class TouchpadTapSuppressionController : public TapSuppressionControllerClient {
MouseEventWithLatencyInfo stashed_mouse_down_;
// The core controller of tap suppression.
- scoped_ptr<TapSuppressionController> controller_;
+ TapSuppressionController controller_;
DISALLOW_COPY_AND_ASSIGN(TouchpadTapSuppressionController);
};
diff --git a/chromium/content/browser/renderer_host/input/touchpad_tap_suppression_controller_aura.cc b/chromium/content/browser/renderer_host/input/touchpad_tap_suppression_controller_aura.cc
deleted file mode 100644
index 991097d3f75..00000000000
--- a/chromium/content/browser/renderer_host/input/touchpad_tap_suppression_controller_aura.cc
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/renderer_host/input/touchpad_tap_suppression_controller.h"
-
-#include "content/browser/renderer_host/input/tap_suppression_controller.h"
-#include "content/browser/renderer_host/input/tap_suppression_controller_client.h"
-#include "ui/events/gestures/gesture_configuration.h"
-
-namespace content {
-
-TouchpadTapSuppressionController::TouchpadTapSuppressionController(
- TouchpadTapSuppressionControllerClient* client)
- : client_(client),
- controller_(new TapSuppressionController(this)) {
-}
-
-TouchpadTapSuppressionController::~TouchpadTapSuppressionController() {}
-
-void TouchpadTapSuppressionController::GestureFlingCancel() {
- controller_->GestureFlingCancel();
-}
-
-void TouchpadTapSuppressionController::GestureFlingCancelAck(bool processed) {
- controller_->GestureFlingCancelAck(processed);
-}
-
-bool TouchpadTapSuppressionController::ShouldDeferMouseDown(
- const MouseEventWithLatencyInfo& event) {
- bool should_defer = controller_->ShouldDeferTapDown();
- if (should_defer)
- stashed_mouse_down_ = event;
- return should_defer;
-}
-
-bool TouchpadTapSuppressionController::ShouldSuppressMouseUp() {
- return controller_->ShouldSuppressTapEnd();
-}
-
-int TouchpadTapSuppressionController::MaxCancelToDownTimeInMs() {
- return ui::GestureConfiguration::fling_max_cancel_to_down_time_in_ms();
-}
-
-int TouchpadTapSuppressionController::MaxTapGapTimeInMs() {
- return ui::GestureConfiguration::fling_max_tap_gap_time_in_ms();
-}
-
-void TouchpadTapSuppressionController::DropStashedTapDown() {
-}
-
-void TouchpadTapSuppressionController::ForwardStashedTapDown() {
- // Mouse downs are not handled by gesture event filter; so, they are
- // immediately forwarded to the renderer.
- client_->SendMouseEventImmediately(stashed_mouse_down_);
-}
-
-} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/touchscreen_tap_suppression_controller.cc b/chromium/content/browser/renderer_host/input/touchscreen_tap_suppression_controller.cc
index 39fca2f854e..872cfb05b6b 100644
--- a/chromium/content/browser/renderer_host/input/touchscreen_tap_suppression_controller.cc
+++ b/chromium/content/browser/renderer_host/input/touchscreen_tap_suppression_controller.cc
@@ -4,75 +4,58 @@
#include "content/browser/renderer_host/input/touchscreen_tap_suppression_controller.h"
-#include "content/browser/renderer_host/input/gesture_event_filter.h"
-#include "content/browser/renderer_host/input/tap_suppression_controller.h"
-#include "ui/events/gestures/gesture_configuration.h"
+#include "content/browser/renderer_host/input/gesture_event_queue.h"
-#if defined(OS_ANDROID)
-#include "ui/gfx/android/view_configuration.h"
-#endif
+using blink::WebInputEvent;
namespace content {
TouchscreenTapSuppressionController::TouchscreenTapSuppressionController(
- GestureEventFilter* gef)
- : gesture_event_filter_(gef),
- controller_(new TapSuppressionController(this)) {
+ GestureEventQueue* geq,
+ const TapSuppressionController::Config& config)
+ : gesture_event_queue_(geq), controller_(this, config) {
}
TouchscreenTapSuppressionController::~TouchscreenTapSuppressionController() {}
void TouchscreenTapSuppressionController::GestureFlingCancel() {
- controller_->GestureFlingCancel();
+ controller_.GestureFlingCancel();
}
void TouchscreenTapSuppressionController::GestureFlingCancelAck(
bool processed) {
- controller_->GestureFlingCancelAck(processed);
+ controller_.GestureFlingCancelAck(processed);
}
-bool TouchscreenTapSuppressionController::ShouldDeferGestureTapDown(
+bool TouchscreenTapSuppressionController::FilterTapEvent(
const GestureEventWithLatencyInfo& event) {
- bool should_defer = controller_->ShouldDeferTapDown();
- if (should_defer)
- stashed_tap_down_.reset(new GestureEventWithLatencyInfo(event));
- return should_defer;
+ switch (event.event.type) {
+ case WebInputEvent::GestureTapDown:
+ if (!controller_.ShouldDeferTapDown())
+ return false;
+ stashed_tap_down_.reset(new GestureEventWithLatencyInfo(event));
+ return true;
+
+ case WebInputEvent::GestureShowPress:
+ if (!stashed_tap_down_)
+ return false;
+ stashed_show_press_.reset(new GestureEventWithLatencyInfo(event));
+ return true;
+
+ case WebInputEvent::GestureTapUnconfirmed:
+ return stashed_tap_down_;
+
+ case WebInputEvent::GestureTapCancel:
+ case WebInputEvent::GestureTap:
+ case WebInputEvent::GestureDoubleTap:
+ return controller_.ShouldSuppressTapEnd();
+
+ default:
+ break;
+ }
+ return false;
}
-bool TouchscreenTapSuppressionController::ShouldDeferGestureShowPress(
- const GestureEventWithLatencyInfo& event) {
- if (!stashed_tap_down_)
- return false;
-
- stashed_show_press_.reset(new GestureEventWithLatencyInfo(event));
- return true;
-}
-
-bool TouchscreenTapSuppressionController::ShouldSuppressGestureTapEnd() {
- return controller_->ShouldSuppressTapEnd();
-}
-
-#if defined(OS_ANDROID)
-// TODO(jdduke): Enable ui::GestureConfiguration on Android and initialize
-// with parameters from ViewConfiguration.
-int TouchscreenTapSuppressionController::MaxCancelToDownTimeInMs() {
- return gfx::ViewConfiguration::GetTapTimeoutInMs();
-}
-
-int TouchscreenTapSuppressionController::MaxTapGapTimeInMs() {
- return gfx::ViewConfiguration::GetLongPressTimeoutInMs();
-}
-#else
-int TouchscreenTapSuppressionController::MaxCancelToDownTimeInMs() {
- return ui::GestureConfiguration::fling_max_cancel_to_down_time_in_ms();
-}
-
-int TouchscreenTapSuppressionController::MaxTapGapTimeInMs() {
- return static_cast<int>(
- ui::GestureConfiguration::semi_long_press_time_in_seconds() * 1000);
-}
-#endif
-
void TouchscreenTapSuppressionController::DropStashedTapDown() {
stashed_tap_down_.reset();
stashed_show_press_.reset();
@@ -82,9 +65,9 @@ void TouchscreenTapSuppressionController::ForwardStashedTapDown() {
DCHECK(stashed_tap_down_);
ScopedGestureEvent tap_down = stashed_tap_down_.Pass();
ScopedGestureEvent show_press = stashed_show_press_.Pass();
- gesture_event_filter_->ForwardGestureEvent(*tap_down);
+ gesture_event_queue_->ForwardGestureEvent(*tap_down);
if (show_press)
- gesture_event_filter_->ForwardGestureEvent(*show_press);
+ gesture_event_queue_->ForwardGestureEvent(*show_press);
}
} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/touchscreen_tap_suppression_controller.h b/chromium/content/browser/renderer_host/input/touchscreen_tap_suppression_controller.h
index 0857bd74672..706e9d8610e 100644
--- a/chromium/content/browser/renderer_host/input/touchscreen_tap_suppression_controller.h
+++ b/chromium/content/browser/renderer_host/input/touchscreen_tap_suppression_controller.h
@@ -6,20 +6,22 @@
#define CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCHSCREEN_TAP_SUPPRESSION_CONTROLLER_H_
#include "base/memory/scoped_ptr.h"
-#include "content/browser/renderer_host/input/gesture_event_filter.h"
+#include "content/browser/renderer_host/event_with_latency_info.h"
+#include "content/browser/renderer_host/input/tap_suppression_controller.h"
#include "content/browser/renderer_host/input/tap_suppression_controller_client.h"
namespace content {
-class GestureEventFilter;
-class TapSuppressionController;
+class GestureEventQueue;
// Controls the suppression of touchscreen taps immediately following the
// dispatch of a GestureFlingCancel event.
class TouchscreenTapSuppressionController
: public TapSuppressionControllerClient {
public:
- explicit TouchscreenTapSuppressionController(GestureEventFilter* gef);
+ TouchscreenTapSuppressionController(
+ GestureEventQueue* geq,
+ const TapSuppressionController::Config& config);
virtual ~TouchscreenTapSuppressionController();
// Should be called on arrival of GestureFlingCancel events.
@@ -29,33 +31,23 @@ class TouchscreenTapSuppressionController
// |processed| is true if the GestureFlingCancel successfully stopped a fling.
void GestureFlingCancelAck(bool processed);
- // Should be called on arrival of GestureTapDown events. Returns true if the
- // caller should stop normal handling of the GestureTapDown.
- bool ShouldDeferGestureTapDown(const GestureEventWithLatencyInfo& event);
-
- // Should be called on arrival of GestureShowPress events. Returns true if the
- // caller should stop normal handling of the GestureShowPress.
- bool ShouldDeferGestureShowPress(const GestureEventWithLatencyInfo& event);
-
- // Should be called on arrival of tap-ending gesture events. Returns true if
- // the caller should stop normal handling of the GestureTap.
- bool ShouldSuppressGestureTapEnd();
+ // Should be called on arrival of any tap-related events. Returns true if the
+ // caller should stop normal handling of the gesture.
+ bool FilterTapEvent(const GestureEventWithLatencyInfo& event);
private:
// TapSuppressionControllerClient implementation.
- virtual int MaxCancelToDownTimeInMs() OVERRIDE;
- virtual int MaxTapGapTimeInMs() OVERRIDE;
virtual void DropStashedTapDown() OVERRIDE;
virtual void ForwardStashedTapDown() OVERRIDE;
- GestureEventFilter* gesture_event_filter_;
+ GestureEventQueue* gesture_event_queue_;
typedef scoped_ptr<GestureEventWithLatencyInfo> ScopedGestureEvent;
ScopedGestureEvent stashed_tap_down_;
ScopedGestureEvent stashed_show_press_;
// The core controller of tap suppression.
- scoped_ptr<TapSuppressionController> controller_;
+ TapSuppressionController controller_;
DISALLOW_COPY_AND_ASSIGN(TouchscreenTapSuppressionController);
};
diff --git a/chromium/content/browser/renderer_host/input/touchscreen_tap_suppression_controller_stub.cc b/chromium/content/browser/renderer_host/input/touchscreen_tap_suppression_controller_stub.cc
deleted file mode 100644
index 198dcb5de9a..00000000000
--- a/chromium/content/browser/renderer_host/input/touchscreen_tap_suppression_controller_stub.cc
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/renderer_host/input/touchscreen_tap_suppression_controller.h"
-
-#include "content/browser/renderer_host/input/tap_suppression_controller.h"
-
-// This is the stub implementation of TouchscreenTapSuppressionController which
-// is used on platforms that do not need tap suppression for touchscreen.
-
-namespace content {
-
-TouchscreenTapSuppressionController::TouchscreenTapSuppressionController(
- GestureEventFilter* /*gef*/)
- : gesture_event_filter_(NULL) {}
-
-TouchscreenTapSuppressionController::~TouchscreenTapSuppressionController() {}
-
-void TouchscreenTapSuppressionController::GestureFlingCancel() {}
-
-void TouchscreenTapSuppressionController::GestureFlingCancelAck(
- bool /*processed*/) {
-}
-
-bool TouchscreenTapSuppressionController::ShouldDeferGestureTapDown(
- const GestureEventWithLatencyInfo& /*event*/) {
- return false;
-}
-
-bool TouchscreenTapSuppressionController::ShouldDeferGestureShowPress(
- const GestureEventWithLatencyInfo& /*event*/) {
- return false;
-}
-
-bool TouchscreenTapSuppressionController::ShouldSuppressGestureTapEnd() {
- return false;
-}
-
-int TouchscreenTapSuppressionController::MaxCancelToDownTimeInMs() {
- return 0;
-}
-
-int TouchscreenTapSuppressionController::MaxTapGapTimeInMs() {
- return 0;
-}
-
-void TouchscreenTapSuppressionController::DropStashedTapDown() {}
-
-void TouchscreenTapSuppressionController::ForwardStashedTapDown() {}
-
-} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/web_input_event_builders_android.cc b/chromium/content/browser/renderer_host/input/web_input_event_builders_android.cc
index 43e9bb86572..2d34bc4db6d 100644
--- a/chromium/content/browser/renderer_host/input/web_input_event_builders_android.cc
+++ b/chromium/content/browser/renderer_host/input/web_input_event_builders_android.cc
@@ -5,18 +5,21 @@
#include "content/browser/renderer_host/input/web_input_event_builders_android.h"
#include "base/logging.h"
+#include "content/browser/renderer_host/input/motion_event_android.h"
#include "content/browser/renderer_host/input/web_input_event_util.h"
#include "content/browser/renderer_host/input/web_input_event_util_posix.h"
#include "ui/events/keycodes/keyboard_code_conversion_android.h"
#include "ui/events/keycodes/keyboard_codes_posix.h"
-namespace content {
-
using blink::WebInputEvent;
using blink::WebKeyboardEvent;
using blink::WebGestureEvent;
using blink::WebMouseEvent;
using blink::WebMouseWheelEvent;
+using blink::WebTouchEvent;
+using blink::WebTouchPoint;
+
+namespace content {
WebKeyboardEvent WebKeyboardEventBuilder::Build(WebInputEvent::Type type,
int modifiers,
@@ -125,7 +128,7 @@ WebGestureEvent WebGestureEventBuilder::Build(WebInputEvent::Type type,
result.x = x;
result.y = y;
result.timeStampSeconds = time_sec;
- result.sourceDevice = WebGestureEvent::Touchscreen;
+ result.sourceDevice = blink::WebGestureDeviceTouchscreen;
return result;
}
diff --git a/chromium/content/browser/renderer_host/input/web_input_event_builders_android.h b/chromium/content/browser/renderer_host/input/web_input_event_builders_android.h
index 10595725335..49b0a65baaa 100644
--- a/chromium/content/browser/renderer_host/input/web_input_event_builders_android.h
+++ b/chromium/content/browser/renderer_host/input/web_input_event_builders_android.h
@@ -5,19 +5,23 @@
#ifndef CONTENT_BROWSER_RENDERER_HOST_INPUT_WEB_INPUT_EVENT_BUILDERS_ANDROID_H_
#define CONTENT_BROWSER_RENDERER_HOST_INPUT_WEB_INPUT_EVENT_BUILDERS_ANDROID_H_
+#include <jni.h>
+
#include "third_party/WebKit/public/web/WebInputEvent.h"
namespace content {
+class MotionEventAndroid;
+
class WebMouseEventBuilder {
public:
static blink::WebMouseEvent Build(blink::WebInputEvent::Type type,
- blink::WebMouseEvent::Button button,
- double time_sec,
- int window_x,
- int window_y,
- int modifiers,
- int click_count);
+ blink::WebMouseEvent::Button button,
+ double time_sec,
+ int window_x,
+ int window_y,
+ int modifiers,
+ int click_count);
};
class WebMouseWheelEventBuilder {
@@ -30,27 +34,27 @@ class WebMouseWheelEventBuilder {
};
static blink::WebMouseWheelEvent Build(Direction direction,
- double time_sec,
- int window_x,
- int window_y);
+ double time_sec,
+ int window_x,
+ int window_y);
};
class WebKeyboardEventBuilder {
public:
static blink::WebKeyboardEvent Build(blink::WebInputEvent::Type type,
- int modifiers,
- double time_sec,
- int keycode,
- int unicode_character,
- bool is_system_key);
+ int modifiers,
+ double time_sec,
+ int keycode,
+ int unicode_character,
+ bool is_system_key);
};
class WebGestureEventBuilder {
public:
static blink::WebGestureEvent Build(blink::WebInputEvent::Type type,
- double time_sec,
- int x,
- int y);
+ double time_sec,
+ int x,
+ int y);
};
} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/web_input_event_builders_gtk.cc b/chromium/content/browser/renderer_host/input/web_input_event_builders_gtk.cc
deleted file mode 100644
index d5c2bded6e1..00000000000
--- a/chromium/content/browser/renderer_host/input/web_input_event_builders_gtk.cc
+++ /dev/null
@@ -1,603 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/renderer_host/input/web_input_event_builders_gtk.h"
-
-#include <gdk/gdk.h>
-#include <gdk/gdkkeysyms.h>
-#include <gtk/gtk.h>
-
-#include "base/logging.h"
-#include "content/browser/renderer_host/input/web_input_event_util_posix.h"
-#include "third_party/WebKit/public/web/WebInputEvent.h"
-#include "ui/events/keycodes/keyboard_code_conversion_gtk.h"
-
-using blink::WebInputEvent;
-using blink::WebMouseEvent;
-using blink::WebMouseWheelEvent;
-using blink::WebKeyboardEvent;
-
-namespace {
-
-// For click count tracking.
-static int num_clicks = 0;
-static GdkWindow* last_click_event_window = 0;
-static gint last_click_time = 0;
-static gint last_click_x = 0;
-static gint last_click_y = 0;
-static WebMouseEvent::Button last_click_button = WebMouseEvent::ButtonNone;
-
-bool ShouldForgetPreviousClick(GdkWindow* window, gint time, gint x, gint y) {
- static GtkSettings* settings = gtk_settings_get_default();
-
- if (window != last_click_event_window)
- return true;
-
- gint double_click_time = 250;
- gint double_click_distance = 5;
- g_object_get(G_OBJECT(settings),
- "gtk-double-click-time",
- &double_click_time,
- "gtk-double-click-distance",
- &double_click_distance,
- NULL);
- return (time - last_click_time) > double_click_time ||
- std::abs(x - last_click_x) > double_click_distance ||
- std::abs(y - last_click_y) > double_click_distance;
-}
-
-void ResetClickCountState() {
- num_clicks = 0;
- last_click_event_window = 0;
- last_click_time = 0;
- last_click_x = 0;
- last_click_y = 0;
- last_click_button = blink::WebMouseEvent::ButtonNone;
-}
-
-bool IsKeyPadKeyval(guint keyval) {
- // Keypad keyvals all fall into one range.
- return keyval >= GDK_KP_Space && keyval <= GDK_KP_9;
-}
-
-double GdkEventTimeToWebEventTime(guint32 time) {
- // Convert from time in ms to time in sec.
- return time / 1000.0;
-}
-
-int GdkStateToWebEventModifiers(guint state) {
- int modifiers = 0;
- if (state & GDK_SHIFT_MASK)
- modifiers |= WebInputEvent::ShiftKey;
- if (state & GDK_CONTROL_MASK)
- modifiers |= WebInputEvent::ControlKey;
- if (state & GDK_MOD1_MASK)
- modifiers |= WebInputEvent::AltKey;
- if (state & GDK_META_MASK)
- modifiers |= WebInputEvent::MetaKey;
- if (state & GDK_BUTTON1_MASK)
- modifiers |= WebInputEvent::LeftButtonDown;
- if (state & GDK_BUTTON2_MASK)
- modifiers |= WebInputEvent::MiddleButtonDown;
- if (state & GDK_BUTTON3_MASK)
- modifiers |= WebInputEvent::RightButtonDown;
- if (state & GDK_LOCK_MASK)
- modifiers |= WebInputEvent::CapsLockOn;
- if (state & GDK_MOD2_MASK)
- modifiers |= WebInputEvent::NumLockOn;
- return modifiers;
-}
-
-ui::KeyboardCode GdkEventToWindowsKeyCode(const GdkEventKey* event) {
- static const unsigned int kHardwareCodeToGDKKeyval[] = {
- 0, // 0x00:
- 0, // 0x01:
- 0, // 0x02:
- 0, // 0x03:
- 0, // 0x04:
- 0, // 0x05:
- 0, // 0x06:
- 0, // 0x07:
- 0, // 0x08:
- 0, // 0x09: GDK_Escape
- GDK_1, // 0x0A: GDK_1
- GDK_2, // 0x0B: GDK_2
- GDK_3, // 0x0C: GDK_3
- GDK_4, // 0x0D: GDK_4
- GDK_5, // 0x0E: GDK_5
- GDK_6, // 0x0F: GDK_6
- GDK_7, // 0x10: GDK_7
- GDK_8, // 0x11: GDK_8
- GDK_9, // 0x12: GDK_9
- GDK_0, // 0x13: GDK_0
- GDK_minus, // 0x14: GDK_minus
- GDK_equal, // 0x15: GDK_equal
- 0, // 0x16: GDK_BackSpace
- 0, // 0x17: GDK_Tab
- GDK_q, // 0x18: GDK_q
- GDK_w, // 0x19: GDK_w
- GDK_e, // 0x1A: GDK_e
- GDK_r, // 0x1B: GDK_r
- GDK_t, // 0x1C: GDK_t
- GDK_y, // 0x1D: GDK_y
- GDK_u, // 0x1E: GDK_u
- GDK_i, // 0x1F: GDK_i
- GDK_o, // 0x20: GDK_o
- GDK_p, // 0x21: GDK_p
- GDK_bracketleft, // 0x22: GDK_bracketleft
- GDK_bracketright, // 0x23: GDK_bracketright
- 0, // 0x24: GDK_Return
- 0, // 0x25: GDK_Control_L
- GDK_a, // 0x26: GDK_a
- GDK_s, // 0x27: GDK_s
- GDK_d, // 0x28: GDK_d
- GDK_f, // 0x29: GDK_f
- GDK_g, // 0x2A: GDK_g
- GDK_h, // 0x2B: GDK_h
- GDK_j, // 0x2C: GDK_j
- GDK_k, // 0x2D: GDK_k
- GDK_l, // 0x2E: GDK_l
- GDK_semicolon, // 0x2F: GDK_semicolon
- GDK_apostrophe, // 0x30: GDK_apostrophe
- GDK_grave, // 0x31: GDK_grave
- 0, // 0x32: GDK_Shift_L
- GDK_backslash, // 0x33: GDK_backslash
- GDK_z, // 0x34: GDK_z
- GDK_x, // 0x35: GDK_x
- GDK_c, // 0x36: GDK_c
- GDK_v, // 0x37: GDK_v
- GDK_b, // 0x38: GDK_b
- GDK_n, // 0x39: GDK_n
- GDK_m, // 0x3A: GDK_m
- GDK_comma, // 0x3B: GDK_comma
- GDK_period, // 0x3C: GDK_period
- GDK_slash, // 0x3D: GDK_slash
- 0, // 0x3E: GDK_Shift_R
- 0, // 0x3F:
- 0, // 0x40:
- 0, // 0x41:
- 0, // 0x42:
- 0, // 0x43:
- 0, // 0x44:
- 0, // 0x45:
- 0, // 0x46:
- 0, // 0x47:
- 0, // 0x48:
- 0, // 0x49:
- 0, // 0x4A:
- 0, // 0x4B:
- 0, // 0x4C:
- 0, // 0x4D:
- 0, // 0x4E:
- 0, // 0x4F:
- 0, // 0x50:
- 0, // 0x51:
- 0, // 0x52:
- 0, // 0x53:
- 0, // 0x54:
- 0, // 0x55:
- 0, // 0x56:
- 0, // 0x57:
- 0, // 0x58:
- 0, // 0x59:
- 0, // 0x5A:
- 0, // 0x5B:
- 0, // 0x5C:
- 0, // 0x5D:
- 0, // 0x5E:
- 0, // 0x5F:
- 0, // 0x60:
- 0, // 0x61:
- 0, // 0x62:
- 0, // 0x63:
- 0, // 0x64:
- 0, // 0x65:
- 0, // 0x66:
- 0, // 0x67:
- 0, // 0x68:
- 0, // 0x69:
- 0, // 0x6A:
- 0, // 0x6B:
- 0, // 0x6C:
- 0, // 0x6D:
- 0, // 0x6E:
- 0, // 0x6F:
- 0, // 0x70:
- 0, // 0x71:
- 0, // 0x72:
- GDK_Super_L, // 0x73: GDK_Super_L
- GDK_Super_R, // 0x74: GDK_Super_R
- };
-
- // |windows_key_code| has to include a valid virtual-key code even when we
- // use non-US layouts, e.g. even when we type an 'A' key of a US keyboard
- // on the Hebrew layout, |windows_key_code| should be VK_A.
- // On the other hand, |event->keyval| value depends on the current
- // GdkKeymap object, i.e. when we type an 'A' key of a US keyboard on
- // the Hebrew layout, |event->keyval| becomes GDK_hebrew_shin and this
- // ui::WindowsKeyCodeForGdkKeyCode() call returns 0.
- // To improve compatibilty with Windows, we use |event->hardware_keycode|
- // for retrieving its Windows key-code for the keys when the
- // WebCore::windows_key_codeForEvent() call returns 0.
- // We shouldn't use |event->hardware_keycode| for keys that GdkKeymap
- // objects cannot change because |event->hardware_keycode| doesn't change
- // even when we change the layout options, e.g. when we swap a control
- // key and a caps-lock key, GTK doesn't swap their
- // |event->hardware_keycode| values but swap their |event->keyval| values.
- ui::KeyboardCode windows_key_code =
- ui::WindowsKeyCodeForGdkKeyCode(event->keyval);
- if (windows_key_code)
- return windows_key_code;
-
- if (event->hardware_keycode < arraysize(kHardwareCodeToGDKKeyval)) {
- int keyval = kHardwareCodeToGDKKeyval[event->hardware_keycode];
- if (keyval)
- return ui::WindowsKeyCodeForGdkKeyCode(keyval);
- }
-
- // This key is one that keyboard-layout drivers cannot change.
- // Use |event->keyval| to retrieve its |windows_key_code| value.
- return ui::WindowsKeyCodeForGdkKeyCode(event->keyval);
-}
-
-// Normalizes event->state to make it Windows/Mac compatible. Since the way
-// of setting modifier mask on X is very different than Windows/Mac as shown
-// in http://crbug.com/127142#c8, the normalization is necessary.
-guint NormalizeEventState(const GdkEventKey* event) {
- guint mask = 0;
- switch (GdkEventToWindowsKeyCode(event)) {
- case ui::VKEY_CONTROL:
- case ui::VKEY_LCONTROL:
- case ui::VKEY_RCONTROL:
- mask = GDK_CONTROL_MASK;
- break;
- case ui::VKEY_SHIFT:
- case ui::VKEY_LSHIFT:
- case ui::VKEY_RSHIFT:
- mask = GDK_SHIFT_MASK;
- break;
- case ui::VKEY_MENU:
- case ui::VKEY_LMENU:
- case ui::VKEY_RMENU:
- mask = GDK_MOD1_MASK;
- break;
- case ui::VKEY_CAPITAL:
- mask = GDK_LOCK_MASK;
- break;
- default:
- return event->state;
- }
- if (event->type == GDK_KEY_PRESS)
- return event->state | mask;
- return event->state & ~mask;
-}
-
-// Gets the corresponding control character of a specified key code. See:
-// http://en.wikipedia.org/wiki/Control_characters
-// We emulate Windows behavior here.
-int GetControlCharacter(ui::KeyboardCode windows_key_code, bool shift) {
- if (windows_key_code >= ui::VKEY_A && windows_key_code <= ui::VKEY_Z) {
- // ctrl-A ~ ctrl-Z map to \x01 ~ \x1A
- return windows_key_code - ui::VKEY_A + 1;
- }
- if (shift) {
- // following graphics chars require shift key to input.
- switch (windows_key_code) {
- // ctrl-@ maps to \x00 (Null byte)
- case ui::VKEY_2:
- return 0;
- // ctrl-^ maps to \x1E (Record separator, Information separator two)
- case ui::VKEY_6:
- return 0x1E;
- // ctrl-_ maps to \x1F (Unit separator, Information separator one)
- case ui::VKEY_OEM_MINUS:
- return 0x1F;
- // Returns 0 for all other keys to avoid inputting unexpected chars.
- default:
- return 0;
- }
- } else {
- switch (windows_key_code) {
- // ctrl-[ maps to \x1B (Escape)
- case ui::VKEY_OEM_4:
- return 0x1B;
- // ctrl-\ maps to \x1C (File separator, Information separator four)
- case ui::VKEY_OEM_5:
- return 0x1C;
- // ctrl-] maps to \x1D (Group separator, Information separator three)
- case ui::VKEY_OEM_6:
- return 0x1D;
- // ctrl-Enter maps to \x0A (Line feed)
- case ui::VKEY_RETURN:
- return 0x0A;
- // Returns 0 for all other keys to avoid inputting unexpected chars.
- default:
- return 0;
- }
- }
-}
-
-} // namespace
-
-namespace content {
-
-// WebKeyboardEvent -----------------------------------------------------------
-
-WebKeyboardEvent WebKeyboardEventBuilder::Build(const GdkEventKey* event) {
- WebKeyboardEvent result;
-
- result.timeStampSeconds = GdkEventTimeToWebEventTime(event->time);
- result.modifiers = GdkStateToWebEventModifiers(NormalizeEventState(event));
-
- switch (event->type) {
- case GDK_KEY_RELEASE:
- result.type = WebInputEvent::KeyUp;
- break;
- case GDK_KEY_PRESS:
- result.type = WebInputEvent::RawKeyDown;
- break;
- default:
- NOTREACHED();
- }
-
- // According to MSDN:
- // http://msdn.microsoft.com/en-us/library/ms646286(VS.85).aspx
- // Key events with Alt modifier and F10 are system key events.
- // We just emulate this behavior. It's necessary to prevent webkit from
- // processing keypress event generated by alt-d, etc.
- // F10 is not special on Linux, so don't treat it as system key.
- if (result.modifiers & WebInputEvent::AltKey)
- result.isSystemKey = true;
-
- // The key code tells us which physical key was pressed (for example, the
- // A key went down or up). It does not determine whether A should be lower
- // or upper case. This is what text does, which should be the keyval.
- ui::KeyboardCode windows_key_code = GdkEventToWindowsKeyCode(event);
- result.windowsKeyCode = GetWindowsKeyCodeWithoutLocation(windows_key_code);
- result.modifiers |= GetLocationModifiersFromWindowsKeyCode(windows_key_code);
- result.nativeKeyCode = event->hardware_keycode;
-
- if (result.windowsKeyCode == ui::VKEY_RETURN) {
- // We need to treat the enter key as a key press of character \r. This
- // is apparently just how webkit handles it and what it expects.
- result.unmodifiedText[0] = '\r';
- } else {
- // FIXME: fix for non BMP chars
- result.unmodifiedText[0] =
- static_cast<int>(gdk_keyval_to_unicode(event->keyval));
- }
-
- // If ctrl key is pressed down, then control character shall be input.
- if (result.modifiers & WebInputEvent::ControlKey) {
- result.text[0] =
- GetControlCharacter(ui::KeyboardCode(result.windowsKeyCode),
- result.modifiers & WebInputEvent::ShiftKey);
- } else {
- result.text[0] = result.unmodifiedText[0];
- }
-
- result.setKeyIdentifierFromWindowsKeyCode();
-
- // FIXME: Do we need to set IsAutoRepeat?
- if (IsKeyPadKeyval(event->keyval))
- result.modifiers |= WebInputEvent::IsKeyPad;
-
- return result;
-}
-
-WebKeyboardEvent WebKeyboardEventBuilder::Build(wchar_t character,
- int state,
- double timeStampSeconds) {
- // keyboardEvent(const GdkEventKey*) depends on the GdkEventKey object and
- // it is hard to use/ it from signal handlers which don't use GdkEventKey
- // objects (e.g. GtkIMContext signal handlers.) For such handlers, this
- // function creates a WebInputEvent::Char event without using a
- // GdkEventKey object.
- WebKeyboardEvent result;
- result.type = blink::WebInputEvent::Char;
- result.timeStampSeconds = timeStampSeconds;
- result.modifiers = GdkStateToWebEventModifiers(state);
- result.windowsKeyCode = character;
- result.nativeKeyCode = character;
- result.text[0] = character;
- result.unmodifiedText[0] = character;
-
- // According to MSDN:
- // http://msdn.microsoft.com/en-us/library/ms646286(VS.85).aspx
- // Key events with Alt modifier and F10 are system key events.
- // We just emulate this behavior. It's necessary to prevent webkit from
- // processing keypress event generated by alt-d, etc.
- // F10 is not special on Linux, so don't treat it as system key.
- if (result.modifiers & WebInputEvent::AltKey)
- result.isSystemKey = true;
-
- return result;
-}
-
-// WebMouseEvent --------------------------------------------------------------
-
-WebMouseEvent WebMouseEventBuilder::Build(const GdkEventButton* event) {
- WebMouseEvent result;
-
- result.timeStampSeconds = GdkEventTimeToWebEventTime(event->time);
-
- result.modifiers = GdkStateToWebEventModifiers(event->state);
- result.x = static_cast<int>(event->x);
- result.y = static_cast<int>(event->y);
- result.windowX = result.x;
- result.windowY = result.y;
- result.globalX = static_cast<int>(event->x_root);
- result.globalY = static_cast<int>(event->y_root);
- result.clickCount = 0;
-
- switch (event->type) {
- case GDK_BUTTON_PRESS:
- result.type = WebInputEvent::MouseDown;
- break;
- case GDK_BUTTON_RELEASE:
- result.type = WebInputEvent::MouseUp;
- break;
- case GDK_3BUTTON_PRESS:
- case GDK_2BUTTON_PRESS:
- default:
- NOTREACHED();
- }
-
- result.button = WebMouseEvent::ButtonNone;
- if (event->button == 1)
- result.button = WebMouseEvent::ButtonLeft;
- else if (event->button == 2)
- result.button = WebMouseEvent::ButtonMiddle;
- else if (event->button == 3)
- result.button = WebMouseEvent::ButtonRight;
-
- if (result.type == WebInputEvent::MouseDown) {
- bool forgetPreviousClick = ShouldForgetPreviousClick(
- event->window, event->time, event->x, event->y);
-
- if (!forgetPreviousClick && result.button == last_click_button) {
- ++num_clicks;
- } else {
- num_clicks = 1;
-
- last_click_event_window = event->window;
- last_click_x = event->x;
- last_click_y = event->y;
- last_click_button = result.button;
- }
- last_click_time = event->time;
- }
- result.clickCount = num_clicks;
-
- return result;
-}
-
-WebMouseEvent WebMouseEventBuilder::Build(const GdkEventMotion* event) {
- WebMouseEvent result;
-
- result.timeStampSeconds = GdkEventTimeToWebEventTime(event->time);
- result.modifiers = GdkStateToWebEventModifiers(event->state);
- result.x = static_cast<int>(event->x);
- result.y = static_cast<int>(event->y);
- result.windowX = result.x;
- result.windowY = result.y;
- result.globalX = static_cast<int>(event->x_root);
- result.globalY = static_cast<int>(event->y_root);
-
- switch (event->type) {
- case GDK_MOTION_NOTIFY:
- result.type = WebInputEvent::MouseMove;
- break;
- default:
- NOTREACHED();
- }
-
- result.button = WebMouseEvent::ButtonNone;
- if (event->state & GDK_BUTTON1_MASK)
- result.button = WebMouseEvent::ButtonLeft;
- else if (event->state & GDK_BUTTON2_MASK)
- result.button = WebMouseEvent::ButtonMiddle;
- else if (event->state & GDK_BUTTON3_MASK)
- result.button = WebMouseEvent::ButtonRight;
-
- if (ShouldForgetPreviousClick(event->window, event->time, event->x, event->y))
- ResetClickCountState();
-
- return result;
-}
-
-WebMouseEvent WebMouseEventBuilder::Build(const GdkEventCrossing* event) {
- WebMouseEvent result;
-
- result.timeStampSeconds = GdkEventTimeToWebEventTime(event->time);
- result.modifiers = GdkStateToWebEventModifiers(event->state);
- result.x = static_cast<int>(event->x);
- result.y = static_cast<int>(event->y);
- result.windowX = result.x;
- result.windowY = result.y;
- result.globalX = static_cast<int>(event->x_root);
- result.globalY = static_cast<int>(event->y_root);
-
- switch (event->type) {
- case GDK_ENTER_NOTIFY:
- case GDK_LEAVE_NOTIFY:
- // Note that if we sent MouseEnter or MouseLeave to WebKit, it
- // wouldn't work - they don't result in the proper JavaScript events.
- // MouseMove does the right thing.
- result.type = WebInputEvent::MouseMove;
- break;
- default:
- NOTREACHED();
- }
-
- result.button = WebMouseEvent::ButtonNone;
- if (event->state & GDK_BUTTON1_MASK)
- result.button = WebMouseEvent::ButtonLeft;
- else if (event->state & GDK_BUTTON2_MASK)
- result.button = WebMouseEvent::ButtonMiddle;
- else if (event->state & GDK_BUTTON3_MASK)
- result.button = WebMouseEvent::ButtonRight;
-
- if (ShouldForgetPreviousClick(event->window, event->time, event->x, event->y))
- ResetClickCountState();
-
- return result;
-}
-
-// WebMouseWheelEvent ---------------------------------------------------------
-
-float WebMouseWheelEventBuilder::ScrollbarPixelsPerTick() {
- // How much should we scroll per mouse wheel event?
- // - Windows uses 3 lines by default and obeys a system setting.
- // - Mozilla has a pref that lets you either use the "system" number of lines
- // to scroll, or lets the user override it.
- // For the "system" number of lines, it appears they've hardcoded 3.
- // See case NS_MOUSE_SCROLL in content/events/src/nsEventStateManager.cpp
- // and InitMouseScrollEvent in widget/src/gtk2/nsCommonWidget.cpp .
- // - Gtk makes the scroll amount a function of the size of the scroll bar,
- // which is not available to us here.
- // Instead, we pick a number that empirically matches Firefox's behavior.
- return 160.0f / 3.0f;
-}
-
-WebMouseWheelEvent WebMouseWheelEventBuilder::Build(
- const GdkEventScroll* event) {
- WebMouseWheelEvent result;
-
- result.type = WebInputEvent::MouseWheel;
- result.button = WebMouseEvent::ButtonNone;
-
- result.timeStampSeconds = GdkEventTimeToWebEventTime(event->time);
- result.modifiers = GdkStateToWebEventModifiers(event->state);
- result.x = static_cast<int>(event->x);
- result.y = static_cast<int>(event->y);
- result.windowX = result.x;
- result.windowY = result.y;
- result.globalX = static_cast<int>(event->x_root);
- result.globalY = static_cast<int>(event->y_root);
-
- static const float scrollbarPixelsPerTick = ScrollbarPixelsPerTick();
- switch (event->direction) {
- case GDK_SCROLL_UP:
- result.deltaY = scrollbarPixelsPerTick;
- result.wheelTicksY = 1;
- break;
- case GDK_SCROLL_DOWN:
- result.deltaY = -scrollbarPixelsPerTick;
- result.wheelTicksY = -1;
- break;
- case GDK_SCROLL_LEFT:
- result.deltaX = scrollbarPixelsPerTick;
- result.wheelTicksX = 1;
- break;
- case GDK_SCROLL_RIGHT:
- result.deltaX = -scrollbarPixelsPerTick;
- result.wheelTicksX = -1;
- break;
- }
-
- return result;
-}
-
-} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/web_input_event_builders_gtk.h b/chromium/content/browser/renderer_host/input/web_input_event_builders_gtk.h
deleted file mode 100644
index 0d0250b1746..00000000000
--- a/chromium/content/browser/renderer_host/input/web_input_event_builders_gtk.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.
-
-#ifndef CONTENT_BROWSER_RENDERER_HOST_INPUT_WEB_INPUT_EVENT_BUILDERS_GTK_H_
-#define CONTENT_BROWSER_RENDERER_HOST_INPUT_WEB_INPUT_EVENT_BUILDERS_GTK_H_
-
-#include "content/common/content_export.h"
-#include "third_party/WebKit/public/web/WebInputEvent.h"
-#include "ui/events/keycodes/keyboard_codes.h"
-
-typedef struct _GdkEventButton GdkEventButton;
-typedef struct _GdkEventMotion GdkEventMotion;
-typedef struct _GdkEventCrossing GdkEventCrossing;
-typedef struct _GdkEventScroll GdkEventScroll;
-typedef struct _GdkEventKey GdkEventKey;
-
-namespace content {
-
-class CONTENT_EXPORT WebKeyboardEventBuilder {
- public:
- static blink::WebKeyboardEvent Build(const GdkEventKey* event);
- static blink::WebKeyboardEvent Build(wchar_t character,
- int state,
- double time_secs);
-};
-
-class CONTENT_EXPORT WebMouseEventBuilder {
- public:
- static blink::WebMouseEvent Build(const GdkEventButton* event);
- static blink::WebMouseEvent Build(const GdkEventMotion* event);
- static blink::WebMouseEvent Build(const GdkEventCrossing* event);
-};
-
-class CONTENT_EXPORT WebMouseWheelEventBuilder {
- public:
- static float ScrollbarPixelsPerTick();
- static blink::WebMouseWheelEvent Build(
- const GdkEventScroll* event);
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_WEB_INPUT_EVENT_BUILDERS_GTK_H
diff --git a/chromium/content/browser/renderer_host/input/web_input_event_builders_gtk_unittest.cc b/chromium/content/browser/renderer_host/input/web_input_event_builders_gtk_unittest.cc
deleted file mode 100644
index b5e2d423a08..00000000000
--- a/chromium/content/browser/renderer_host/input/web_input_event_builders_gtk_unittest.cc
+++ /dev/null
@@ -1,171 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/renderer_host/input/web_input_event_builders_gtk.h"
-
-#include <gdk/gdk.h>
-#include <gdk/gdkkeysyms.h>
-
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/WebKit/public/web/WebInputEvent.h"
-#include "ui/events/keycodes/keyboard_code_conversion_gtk.h"
-
-using blink::WebInputEvent;
-using blink::WebKeyboardEvent;
-using blink::WebMouseEvent;
-using content::WebMouseEventBuilder;
-using content::WebKeyboardEventBuilder;
-
-namespace {
-
-TEST(WebMouseEventBuilderTest, DoubleClick) {
- GdkEventButton first_click;
- first_click.type = GDK_BUTTON_PRESS;
- first_click.window = static_cast<GdkWindow*>(GINT_TO_POINTER(1));
- first_click.x = first_click.y = first_click.x_root = first_click.y_root = 100;
- first_click.state = 0;
- first_click.time = 0;
- first_click.button = 1;
-
- // Single click works.
- WebMouseEvent first_click_events =
- WebMouseEventBuilder::Build(&first_click);
- EXPECT_EQ(1, first_click_events.clickCount);
-
- // Make sure double click works.
- GdkEventButton second_click = first_click;
- second_click.time = first_click.time + 100;
- WebMouseEvent second_click_events =
- WebMouseEventBuilder::Build(&second_click);
- EXPECT_EQ(2, second_click_events.clickCount);
-
- // Reset the click count.
- first_click.time += 10000;
- first_click_events = WebMouseEventBuilder::Build(&first_click);
- EXPECT_EQ(1, first_click_events.clickCount);
-
- // Two clicks with a long gap in between aren't counted as a double click.
- second_click = first_click;
- second_click.time = first_click.time + 1000;
- second_click_events = WebMouseEventBuilder::Build(&second_click);
- EXPECT_EQ(1, second_click_events.clickCount);
-
- // Reset the click count.
- first_click.time += 10000;
- first_click_events = WebMouseEventBuilder::Build(&first_click);
- EXPECT_EQ(1, first_click_events.clickCount);
-
- // Two clicks far apart (horizontally) aren't counted as a double click.
- second_click = first_click;
- second_click.time = first_click.time + 1;
- second_click.x = first_click.x + 100;
- second_click_events = WebMouseEventBuilder::Build(&second_click);
- EXPECT_EQ(1, second_click_events.clickCount);
-
- // Reset the click count.
- first_click.time += 10000;
- first_click_events = WebMouseEventBuilder::Build(&first_click);
- EXPECT_EQ(1, first_click_events.clickCount);
-
- // Two clicks far apart (vertically) aren't counted as a double click.
- second_click = first_click;
- second_click.time = first_click.time + 1;
- second_click.x = first_click.y + 100;
- second_click_events = WebMouseEventBuilder::Build(&second_click);
- EXPECT_EQ(1, second_click_events.clickCount);
-
- // Reset the click count.
- first_click.time += 10000;
- first_click_events = WebMouseEventBuilder::Build(&first_click);
- EXPECT_EQ(1, first_click_events.clickCount);
-
- // Two clicks on different windows aren't a double click.
- second_click = first_click;
- second_click.time = first_click.time + 1;
- second_click.window = static_cast<GdkWindow*>(GINT_TO_POINTER(2));
- second_click_events = WebMouseEventBuilder::Build(&second_click);
- EXPECT_EQ(1, second_click_events.clickCount);
-}
-
-TEST(WebMouseEventBuilderTest, MouseUpClickCount) {
- GdkEventButton mouse_down;
- memset(&mouse_down, 0, sizeof(mouse_down));
- mouse_down.type = GDK_BUTTON_PRESS;
- mouse_down.window = static_cast<GdkWindow*>(GINT_TO_POINTER(1));
- mouse_down.x = mouse_down.y = mouse_down.x_root = mouse_down.y_root = 100;
- mouse_down.time = 0;
- mouse_down.button = 1;
-
- // Properly set the last click time, so that the internal state won't be
- // affected by previous tests.
- WebMouseEventBuilder::Build(&mouse_down);
-
- mouse_down.time += 10000;
- GdkEventButton mouse_up = mouse_down;
- mouse_up.type = GDK_BUTTON_RELEASE;
- WebMouseEvent mouse_down_event;
- WebMouseEvent mouse_up_event;
-
- // Click for three times.
- for (int i = 1; i < 4; ++i) {
- mouse_down.time += 100;
- mouse_down_event = WebMouseEventBuilder::Build(&mouse_down);
- EXPECT_EQ(i, mouse_down_event.clickCount);
-
- mouse_up.time = mouse_down.time + 50;
- mouse_up_event = WebMouseEventBuilder::Build(&mouse_up);
- EXPECT_EQ(i, mouse_up_event.clickCount);
- }
-
- // Reset the click count.
- mouse_down.time += 10000;
- mouse_down_event = WebMouseEventBuilder::Build(&mouse_down);
- EXPECT_EQ(1, mouse_down_event.clickCount);
-
- // Moving the cursor for a significant distance will reset the click count to
- // 0.
- GdkEventMotion mouse_move;
- memset(&mouse_move, 0, sizeof(mouse_move));
- mouse_move.type = GDK_MOTION_NOTIFY;
- mouse_move.window = mouse_down.window;
- mouse_move.time = mouse_down.time;
- mouse_move.x = mouse_move.y = mouse_move.x_root = mouse_move.y_root =
- mouse_down.x + 100;
- WebMouseEventBuilder::Build(&mouse_move);
-
- mouse_up.time = mouse_down.time + 50;
- mouse_up_event = WebMouseEventBuilder::Build(&mouse_up);
- EXPECT_EQ(0, mouse_up_event.clickCount);
-
- // Reset the click count.
- mouse_down.time += 10000;
- mouse_down_event = WebMouseEventBuilder::Build(&mouse_down);
- EXPECT_EQ(1, mouse_down_event.clickCount);
-
- // Moving the cursor with a significant delay will reset the click count to 0.
- mouse_move.time = mouse_down.time + 1000;
- mouse_move.x = mouse_move.y = mouse_move.x_root = mouse_move.y_root =
- mouse_down.x;
- WebMouseEventBuilder::Build(&mouse_move);
-
- mouse_up.time = mouse_move.time + 50;
- mouse_up_event = WebMouseEventBuilder::Build(&mouse_up);
- EXPECT_EQ(0, mouse_up_event.clickCount);
-}
-
-TEST(WebKeyboardEventBuilderTest, NumPadConversion) {
- // Construct a GDK input event for the numpad "5" key.
- char five[] = "5";
- GdkEventKey gdk_event;
- memset(&gdk_event, 0, sizeof(GdkEventKey));
- gdk_event.type = GDK_KEY_PRESS;
- gdk_event.keyval = GDK_KP_5;
- gdk_event.string = five;
-
- // Numpad flag should be set on the WebKeyboardEvent.
- WebKeyboardEvent web_event = WebKeyboardEventBuilder::Build(&gdk_event);
- EXPECT_TRUE(web_event.modifiers & WebInputEvent::IsKeyPad);
-}
-
-} // anonymous namespace
diff --git a/chromium/content/browser/renderer_host/input/web_input_event_builders_win.cc b/chromium/content/browser/renderer_host/input/web_input_event_builders_win.cc
index 8c63e44db09..19e8fc61771 100644
--- a/chromium/content/browser/renderer_host/input/web_input_event_builders_win.cc
+++ b/chromium/content/browser/renderer_host/input/web_input_event_builders_win.cc
@@ -102,15 +102,15 @@ static void SetToggleKeyState(WebInputEvent* event) {
event->modifiers |= WebInputEvent::CapsLockOn;
}
-WebKeyboardEvent WebKeyboardEventBuilder::Build(HWND hwnd, UINT message,
- WPARAM wparam, LPARAM lparam) {
+WebKeyboardEvent WebKeyboardEventBuilder::Build(HWND hwnd,
+ UINT message,
+ WPARAM wparam,
+ LPARAM lparam,
+ DWORD time_ms) {
WebKeyboardEvent result;
- // TODO(pkasting): http://b/1117926 Are we guaranteed that the message that
- // GetMessageTime() refers to is the same one that we're passed in? Perhaps
- // one of the construction parameters should be the time passed by the
- // caller, who would know for sure.
- result.timeStampSeconds = ::GetMessageTime() / 1000.0;
+ DCHECK(time_ms);
+ result.timeStampSeconds = time_ms / 1000.0;
result.windowsKeyCode = static_cast<int>(wparam);
// Record the scan code (along with other context bits) for this key event.
@@ -181,8 +181,11 @@ static LPARAM GetRelativeCursorPos(HWND hwnd) {
return MAKELPARAM(pos.x, pos.y);
}
-WebMouseEvent WebMouseEventBuilder::Build(HWND hwnd, UINT message,
- WPARAM wparam, LPARAM lparam) {
+WebMouseEvent WebMouseEventBuilder::Build(HWND hwnd,
+ UINT message,
+ WPARAM wparam,
+ LPARAM lparam,
+ DWORD time_ms) {
WebMouseEvent result;
switch (message) {
@@ -235,11 +238,8 @@ WebMouseEvent WebMouseEventBuilder::Build(HWND hwnd, UINT message,
NOTREACHED();
}
- // TODO(pkasting): http://b/1117926 Are we guaranteed that the message that
- // GetMessageTime() refers to is the same one that we're passed in? Perhaps
- // one of the construction parameters should be the time passed by the
- // caller, who would know for sure.
- result.timeStampSeconds = ::GetMessageTime() / 1000.0;
+ DCHECK(time_ms);
+ result.timeStampSeconds = time_ms / 1000.0;
// set position fields:
@@ -312,18 +312,17 @@ WebMouseEvent WebMouseEventBuilder::Build(HWND hwnd, UINT message,
// WebMouseWheelEvent ---------------------------------------------------------
-WebMouseWheelEvent
-WebMouseWheelEventBuilder::Build(HWND hwnd, UINT message,
- WPARAM wparam, LPARAM lparam) {
+WebMouseWheelEvent WebMouseWheelEventBuilder::Build(HWND hwnd,
+ UINT message,
+ WPARAM wparam,
+ LPARAM lparam,
+ DWORD time_ms) {
WebMouseWheelEvent result;
result.type = WebInputEvent::MouseWheel;
- // TODO(pkasting): http://b/1117926 Are we guaranteed that the message that
- // GetMessageTime() refers to is the same one that we're passed in? Perhaps
- // one of the construction parameters should be the time passed by the
- // caller, who would know for sure.
- result.timeStampSeconds = ::GetMessageTime() / 1000.0;
+ DCHECK(time_ms);
+ result.timeStampSeconds = time_ms / 1000.0;
result.button = WebMouseEvent::ButtonNone;
@@ -339,9 +338,9 @@ WebMouseWheelEventBuilder::Build(HWND hwnd, UINT message,
// for key state since we are synthesizing the input event.
get_key_state_func = GetAsyncKeyState;
key_state = 0;
- if (get_key_state_func(VK_SHIFT))
+ if (get_key_state_func(VK_SHIFT) & 0x8000)
key_state |= MK_SHIFT;
- if (get_key_state_func(VK_CONTROL))
+ if (get_key_state_func(VK_CONTROL) & 0x8000)
key_state |= MK_CONTROL;
// NOTE: There doesn't seem to be a way to query the mouse button state
// in this case.
diff --git a/chromium/content/browser/renderer_host/input/web_input_event_builders_win.h b/chromium/content/browser/renderer_host/input/web_input_event_builders_win.h
index d3ec4a7b86f..4cd6d0dea96 100644
--- a/chromium/content/browser/renderer_host/input/web_input_event_builders_win.h
+++ b/chromium/content/browser/renderer_host/input/web_input_event_builders_win.h
@@ -13,20 +13,29 @@ namespace content {
class WebKeyboardEventBuilder {
public:
- static blink::WebKeyboardEvent Build(HWND hwnd, UINT message,
- WPARAM wparam, LPARAM lparam);
+ static blink::WebKeyboardEvent Build(HWND hwnd,
+ UINT message,
+ WPARAM wparam,
+ LPARAM lparam,
+ DWORD time_ms);
};
class WebMouseEventBuilder {
public:
- static blink::WebMouseEvent Build(HWND hwnd, UINT message,
- WPARAM wparam, LPARAM lparam);
+ static blink::WebMouseEvent Build(HWND hwnd,
+ UINT message,
+ WPARAM wparam,
+ LPARAM lparam,
+ DWORD time_ms);
};
class WebMouseWheelEventBuilder {
public:
- static blink::WebMouseWheelEvent Build(HWND hwnd, UINT message,
- WPARAM wparam, LPARAM lparam);
+ static blink::WebMouseWheelEvent Build(HWND hwnd,
+ UINT message,
+ WPARAM wparam,
+ LPARAM lparam,
+ DWORD time_ms);
};
} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/web_input_event_util.cc b/chromium/content/browser/renderer_host/input/web_input_event_util.cc
index 804c0b126c1..524cf001431 100644
--- a/chromium/content/browser/renderer_host/input/web_input_event_util.cc
+++ b/chromium/content/browser/renderer_host/input/web_input_event_util.cc
@@ -5,7 +5,15 @@
#include "content/browser/renderer_host/input/web_input_event_util.h"
#include "base/strings/string_util.h"
-#include "third_party/WebKit/public/web/WebInputEvent.h"
+#include "content/common/input/web_touch_event_traits.h"
+#include "ui/events/gesture_detection/gesture_event_data.h"
+#include "ui/events/gesture_detection/motion_event.h"
+
+using blink::WebGestureEvent;
+using blink::WebInputEvent;
+using blink::WebTouchEvent;
+using blink::WebTouchPoint;
+using ui::MotionEvent;
namespace {
@@ -105,7 +113,7 @@ const char* GetKeyIdentifier(ui::KeyboardCode key_code) {
case ui::VKEY_UP:
return "Up";
case ui::VKEY_DELETE:
- return "U+007F"; // Standard says that DEL becomes U+007F.
+ return "U+007F"; // Standard says that DEL becomes U+007F.
case ui::VKEY_MEDIA_NEXT_TRACK:
return "MediaNextTrack";
case ui::VKEY_MEDIA_PREV_TRACK:
@@ -125,6 +133,67 @@ const char* GetKeyIdentifier(ui::KeyboardCode key_code) {
};
}
+WebInputEvent::Type ToWebInputEventType(MotionEvent::Action action) {
+ switch (action) {
+ case MotionEvent::ACTION_DOWN:
+ return WebInputEvent::TouchStart;
+ case MotionEvent::ACTION_MOVE:
+ return WebInputEvent::TouchMove;
+ case MotionEvent::ACTION_UP:
+ return WebInputEvent::TouchEnd;
+ case MotionEvent::ACTION_CANCEL:
+ return WebInputEvent::TouchCancel;
+ case MotionEvent::ACTION_POINTER_DOWN:
+ return WebInputEvent::TouchStart;
+ case MotionEvent::ACTION_POINTER_UP:
+ return WebInputEvent::TouchEnd;
+ }
+ NOTREACHED() << "Invalid MotionEvent::Action.";
+ return WebInputEvent::Undefined;
+}
+
+// Note that |is_action_pointer| is meaningful only in the context of
+// |ACTION_POINTER_UP| and |ACTION_POINTER_DOWN|; other actions map directly to
+// WebTouchPoint::State.
+WebTouchPoint::State ToWebTouchPointState(MotionEvent::Action action,
+ bool is_action_pointer) {
+ switch (action) {
+ case MotionEvent::ACTION_DOWN:
+ return WebTouchPoint::StatePressed;
+ case MotionEvent::ACTION_MOVE:
+ return WebTouchPoint::StateMoved;
+ case MotionEvent::ACTION_UP:
+ return WebTouchPoint::StateReleased;
+ case MotionEvent::ACTION_CANCEL:
+ return WebTouchPoint::StateCancelled;
+ case MotionEvent::ACTION_POINTER_DOWN:
+ return is_action_pointer ? WebTouchPoint::StatePressed
+ : WebTouchPoint::StateStationary;
+ case MotionEvent::ACTION_POINTER_UP:
+ return is_action_pointer ? WebTouchPoint::StateReleased
+ : WebTouchPoint::StateStationary;
+ }
+ NOTREACHED() << "Invalid MotionEvent::Action.";
+ return WebTouchPoint::StateUndefined;
+}
+
+WebTouchPoint CreateWebTouchPoint(const MotionEvent& event,
+ size_t pointer_index) {
+ WebTouchPoint touch;
+ touch.id = event.GetPointerId(pointer_index);
+ touch.state = ToWebTouchPointState(
+ event.GetAction(),
+ static_cast<int>(pointer_index) == event.GetActionIndex());
+ touch.position.x = event.GetX(pointer_index);
+ touch.position.y = event.GetY(pointer_index);
+ touch.screenPosition.x = event.GetRawX(pointer_index);
+ touch.screenPosition.y = event.GetRawY(pointer_index);
+ touch.radiusX = touch.radiusY = event.GetTouchMajor(pointer_index) * 0.5f;
+ touch.force = event.GetPressure(pointer_index);
+
+ return touch;
+}
+
} // namespace
namespace content {
@@ -137,9 +206,130 @@ void UpdateWindowsKeyCodeAndKeyIdentifier(blink::WebKeyboardEvent* event,
if (id) {
base::strlcpy(event->keyIdentifier, id, sizeof(event->keyIdentifier) - 1);
} else {
- base::snprintf(event->keyIdentifier, sizeof(event->keyIdentifier), "U+%04X",
+ base::snprintf(event->keyIdentifier,
+ sizeof(event->keyIdentifier),
+ "U+%04X",
base::ToUpperASCII(static_cast<int>(windows_key_code)));
}
}
+blink::WebTouchEvent CreateWebTouchEventFromMotionEvent(
+ const ui::MotionEvent& event) {
+ blink::WebTouchEvent result;
+
+ WebTouchEventTraits::ResetType(
+ ToWebInputEventType(event.GetAction()),
+ (event.GetEventTime() - base::TimeTicks()).InSecondsF(),
+ &result);
+
+ result.touchesLength =
+ std::min(event.GetPointerCount(),
+ static_cast<size_t>(WebTouchEvent::touchesLengthCap));
+ DCHECK_GT(result.touchesLength, 0U);
+
+ for (size_t i = 0; i < result.touchesLength; ++i)
+ result.touches[i] = CreateWebTouchPoint(event, i);
+
+ return result;
+}
+
+WebGestureEvent CreateWebGestureEventFromGestureEventData(
+ const ui::GestureEventData& data) {
+ WebGestureEvent gesture;
+ gesture.x = data.x;
+ gesture.y = data.y;
+ gesture.globalX = data.raw_x;
+ gesture.globalY = data.raw_y;
+ gesture.timeStampSeconds = (data.time - base::TimeTicks()).InSecondsF();
+ gesture.sourceDevice = blink::WebGestureDeviceTouchscreen;
+
+ switch (data.type()) {
+ case ui::ET_GESTURE_SHOW_PRESS:
+ gesture.type = WebInputEvent::GestureShowPress;
+ gesture.data.showPress.width = data.details.bounding_box_f().width();
+ gesture.data.showPress.height = data.details.bounding_box_f().height();
+ break;
+ case ui::ET_GESTURE_DOUBLE_TAP:
+ gesture.type = WebInputEvent::GestureDoubleTap;
+ DCHECK_EQ(1, data.details.tap_count());
+ gesture.data.tap.tapCount = data.details.tap_count();
+ gesture.data.tap.width = data.details.bounding_box_f().width();
+ gesture.data.tap.height = data.details.bounding_box_f().height();
+ break;
+ case ui::ET_GESTURE_TAP:
+ gesture.type = WebInputEvent::GestureTap;
+ DCHECK_EQ(1, data.details.tap_count());
+ gesture.data.tap.tapCount = data.details.tap_count();
+ gesture.data.tap.width = data.details.bounding_box_f().width();
+ gesture.data.tap.height = data.details.bounding_box_f().height();
+ break;
+ case ui::ET_GESTURE_TAP_UNCONFIRMED:
+ gesture.type = WebInputEvent::GestureTapUnconfirmed;
+ DCHECK_EQ(1, data.details.tap_count());
+ gesture.data.tap.tapCount = data.details.tap_count();
+ gesture.data.tap.width = data.details.bounding_box_f().width();
+ gesture.data.tap.height = data.details.bounding_box_f().height();
+ break;
+ case ui::ET_GESTURE_LONG_PRESS:
+ gesture.type = WebInputEvent::GestureLongPress;
+ gesture.data.longPress.width = data.details.bounding_box_f().width();
+ gesture.data.longPress.height = data.details.bounding_box_f().height();
+ break;
+ case ui::ET_GESTURE_LONG_TAP:
+ gesture.type = WebInputEvent::GestureLongTap;
+ gesture.data.longPress.width = data.details.bounding_box_f().width();
+ gesture.data.longPress.height = data.details.bounding_box_f().height();
+ break;
+ case ui::ET_GESTURE_SCROLL_BEGIN:
+ gesture.type = WebInputEvent::GestureScrollBegin;
+ gesture.data.scrollBegin.deltaXHint = data.details.scroll_x_hint();
+ gesture.data.scrollBegin.deltaYHint = data.details.scroll_y_hint();
+ break;
+ case ui::ET_GESTURE_SCROLL_UPDATE:
+ gesture.type = WebInputEvent::GestureScrollUpdate;
+ gesture.data.scrollUpdate.deltaX = data.details.scroll_x();
+ gesture.data.scrollUpdate.deltaY = data.details.scroll_y();
+ break;
+ case ui::ET_GESTURE_SCROLL_END:
+ gesture.type = WebInputEvent::GestureScrollEnd;
+ break;
+ case ui::ET_SCROLL_FLING_START:
+ gesture.type = WebInputEvent::GestureFlingStart;
+ gesture.data.flingStart.velocityX = data.details.velocity_x();
+ gesture.data.flingStart.velocityY = data.details.velocity_y();
+ break;
+ case ui::ET_SCROLL_FLING_CANCEL:
+ gesture.type = WebInputEvent::GestureFlingCancel;
+ break;
+ case ui::ET_GESTURE_PINCH_BEGIN:
+ gesture.type = WebInputEvent::GesturePinchBegin;
+ break;
+ case ui::ET_GESTURE_PINCH_UPDATE:
+ gesture.type = WebInputEvent::GesturePinchUpdate;
+ gesture.data.pinchUpdate.scale = data.details.scale();
+ break;
+ case ui::ET_GESTURE_PINCH_END:
+ gesture.type = WebInputEvent::GesturePinchEnd;
+ break;
+ case ui::ET_GESTURE_TAP_CANCEL:
+ gesture.type = WebInputEvent::GestureTapCancel;
+ break;
+ case ui::ET_GESTURE_TAP_DOWN:
+ gesture.type = WebInputEvent::GestureTapDown;
+ gesture.data.tapDown.width = data.details.bounding_box_f().width();
+ gesture.data.tapDown.height = data.details.bounding_box_f().height();
+ break;
+ case ui::ET_GESTURE_BEGIN:
+ case ui::ET_GESTURE_END:
+ NOTREACHED() << "ET_GESTURE_BEGIN and ET_GESTURE_END are only produced "
+ << "in Aura, and should never end up here.";
+ break;
+ default:
+ NOTREACHED() << "ui::EventType provided wasn't a valid gesture event.";
+ break;
+ }
+
+ return gesture;
+}
+
} // namespace content
diff --git a/chromium/content/browser/renderer_host/input/web_input_event_util.h b/chromium/content/browser/renderer_host/input/web_input_event_util.h
index f0680f24b53..4143a1cbce4 100644
--- a/chromium/content/browser/renderer_host/input/web_input_event_util.h
+++ b/chromium/content/browser/renderer_host/input/web_input_event_util.h
@@ -6,10 +6,12 @@
#define CONTENT_BROWSER_RENDERER_HOST_INPUT_WEB_INPUT_EVENT_UTIL_H_
#include "content/common/content_export.h"
+#include "third_party/WebKit/public/web/WebInputEvent.h"
#include "ui/events/keycodes/keyboard_codes.h"
-namespace blink {
-class WebKeyboardEvent;
+namespace ui {
+struct GestureEventData;
+class MotionEvent;
}
namespace content {
@@ -20,6 +22,16 @@ CONTENT_EXPORT void UpdateWindowsKeyCodeAndKeyIdentifier(
blink::WebKeyboardEvent* event,
ui::KeyboardCode windows_key_code);
-}
+// Creates a WebTouchEvent from |event|, scaling all size components from
+// |event| by |scale|.
+CONTENT_EXPORT blink::WebTouchEvent CreateWebTouchEventFromMotionEvent(
+ const ui::MotionEvent& event);
+
+// Creates a WebGestureEvent from |event|, scaling all size components from
+// |event| by |scale|.
+CONTENT_EXPORT blink::WebGestureEvent CreateWebGestureEventFromGestureEventData(
+ const ui::GestureEventData& data);
+
+} // namespace content
#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_WEB_INPUT_EVENT_UTIL_H_
diff --git a/chromium/content/browser/renderer_host/java/OWNERS b/chromium/content/browser/renderer_host/java/OWNERS
index d378671071d..0cebb68c10c 100644
--- a/chromium/content/browser/renderer_host/java/OWNERS
+++ b/chromium/content/browser/renderer_host/java/OWNERS
@@ -1,2 +1,2 @@
-steveblock@chromium.org
+mnaganov@chromium.org
torne@chromium.org
diff --git a/chromium/content/browser/renderer_host/java/gin_java_bound_object.cc b/chromium/content/browser/renderer_host/java/gin_java_bound_object.cc
new file mode 100644
index 00000000000..70abba842c9
--- /dev/null
+++ b/chromium/content/browser/renderer_host/java/gin_java_bound_object.cc
@@ -0,0 +1,192 @@
+// 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/renderer_host/java/gin_java_bound_object.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/browser/renderer_host/java/jni_helper.h"
+
+using base::android::AttachCurrentThread;
+using base::android::ScopedJavaLocalRef;
+
+namespace content {
+
+namespace {
+
+const char kJavaLangClass[] = "java/lang/Class";
+const char kJavaLangObject[] = "java/lang/Object";
+const char kJavaLangReflectMethod[] = "java/lang/reflect/Method";
+const char kGetClass[] = "getClass";
+const char kGetMethods[] = "getMethods";
+const char kIsAnnotationPresent[] = "isAnnotationPresent";
+const char kReturningJavaLangClass[] = "()Ljava/lang/Class;";
+const char kReturningJavaLangReflectMethodArray[] =
+ "()[Ljava/lang/reflect/Method;";
+const char kTakesJavaLangClassReturningBoolean[] = "(Ljava/lang/Class;)Z";
+
+} // namespace
+
+
+// static
+GinJavaBoundObject* GinJavaBoundObject::CreateNamed(
+ const JavaObjectWeakGlobalRef& ref,
+ const base::android::JavaRef<jclass>& safe_annotation_clazz) {
+ return new GinJavaBoundObject(ref, safe_annotation_clazz);
+}
+
+// static
+GinJavaBoundObject* GinJavaBoundObject::CreateTransient(
+ const JavaObjectWeakGlobalRef& ref,
+ const base::android::JavaRef<jclass>& safe_annotation_clazz,
+ RenderFrameHost* holder) {
+ std::set<RenderFrameHost*> holders;
+ holders.insert(holder);
+ return new GinJavaBoundObject(ref, safe_annotation_clazz, holders);
+}
+
+GinJavaBoundObject::GinJavaBoundObject(
+ const JavaObjectWeakGlobalRef& ref,
+ const base::android::JavaRef<jclass>& safe_annotation_clazz)
+ : ref_(ref),
+ names_count_(1),
+ object_get_class_method_id_(NULL),
+ are_methods_set_up_(false),
+ safe_annotation_clazz_(safe_annotation_clazz) {
+}
+
+GinJavaBoundObject::GinJavaBoundObject(
+ const JavaObjectWeakGlobalRef& ref,
+ const base::android::JavaRef<jclass>& safe_annotation_clazz,
+ const std::set<RenderFrameHost*> holders)
+ : ref_(ref),
+ names_count_(0),
+ holders_(holders),
+ object_get_class_method_id_(NULL),
+ are_methods_set_up_(false),
+ safe_annotation_clazz_(safe_annotation_clazz) {
+}
+
+GinJavaBoundObject::~GinJavaBoundObject() {
+}
+
+std::set<std::string> GinJavaBoundObject::GetMethodNames() {
+ EnsureMethodsAreSetUp();
+ std::set<std::string> result;
+ for (JavaMethodMap::const_iterator it = methods_.begin();
+ it != methods_.end();
+ ++it) {
+ result.insert(it->first);
+ }
+ return result;
+}
+
+bool GinJavaBoundObject::HasMethod(const std::string& method_name) {
+ EnsureMethodsAreSetUp();
+ return methods_.find(method_name) != methods_.end();
+}
+
+const JavaMethod* GinJavaBoundObject::FindMethod(
+ const std::string& method_name,
+ size_t num_parameters) {
+ EnsureMethodsAreSetUp();
+
+ // Get all methods with the correct name.
+ std::pair<JavaMethodMap::const_iterator, JavaMethodMap::const_iterator>
+ iters = methods_.equal_range(method_name);
+ if (iters.first == iters.second) {
+ return NULL;
+ }
+
+ // LIVECONNECT_COMPLIANCE: We just take the first method with the correct
+ // number of arguments, while the spec proposes using cost-based algorithm:
+ // https://jdk6.java.net/plugin2/liveconnect/#OVERLOADED_METHODS
+ for (JavaMethodMap::const_iterator iter = iters.first; iter != iters.second;
+ ++iter) {
+ if (iter->second->num_parameters() == num_parameters) {
+ return iter->second.get();
+ }
+ }
+
+ return NULL;
+}
+
+bool GinJavaBoundObject::IsObjectGetClassMethod(const JavaMethod* method) {
+ EnsureMethodsAreSetUp();
+ // As java.lang.Object.getClass is declared to be final, it is sufficient to
+ // compare methodIDs.
+ return method->id() == object_get_class_method_id_;
+}
+
+const base::android::JavaRef<jclass>&
+GinJavaBoundObject::GetSafeAnnotationClass() {
+ return safe_annotation_clazz_;
+}
+
+base::android::ScopedJavaLocalRef<jclass> GinJavaBoundObject::GetLocalClassRef(
+ JNIEnv* env) {
+ if (!object_get_class_method_id_) {
+ object_get_class_method_id_ = GetMethodIDFromClassName(
+ env, kJavaLangObject, kGetClass, kReturningJavaLangClass);
+ }
+ ScopedJavaLocalRef<jobject> obj = GetLocalRef(env);
+ if (obj.obj()) {
+ return base::android::ScopedJavaLocalRef<jclass>(
+ env,
+ static_cast<jclass>(
+ env->CallObjectMethod(obj.obj(), object_get_class_method_id_)));
+ } else {
+ return base::android::ScopedJavaLocalRef<jclass>();
+ }
+}
+
+void GinJavaBoundObject::EnsureMethodsAreSetUp() {
+ if (are_methods_set_up_)
+ return;
+ are_methods_set_up_ = true;
+
+ JNIEnv* env = AttachCurrentThread();
+
+ ScopedJavaLocalRef<jclass> clazz = GetLocalClassRef(env);
+ if (clazz.is_null()) {
+ return;
+ }
+
+ ScopedJavaLocalRef<jobjectArray> methods(env, static_cast<jobjectArray>(
+ env->CallObjectMethod(clazz.obj(), GetMethodIDFromClassName(
+ env,
+ kJavaLangClass,
+ kGetMethods,
+ kReturningJavaLangReflectMethodArray))));
+
+ size_t num_methods = env->GetArrayLength(methods.obj());
+ // Java objects always have public methods.
+ DCHECK(num_methods);
+
+ for (size_t i = 0; i < num_methods; ++i) {
+ ScopedJavaLocalRef<jobject> java_method(
+ env,
+ env->GetObjectArrayElement(methods.obj(), i));
+
+ if (!safe_annotation_clazz_.is_null()) {
+ jboolean safe = env->CallBooleanMethod(java_method.obj(),
+ GetMethodIDFromClassName(
+ env,
+ kJavaLangReflectMethod,
+ kIsAnnotationPresent,
+ kTakesJavaLangClassReturningBoolean),
+ safe_annotation_clazz_.obj());
+
+ if (!safe)
+ continue;
+ }
+
+ JavaMethod* method = new JavaMethod(java_method);
+ methods_.insert(std::make_pair(method->name(), method));
+ }
+}
+
+} // namespace content
diff --git a/chromium/content/browser/renderer_host/java/gin_java_bound_object.h b/chromium/content/browser/renderer_host/java/gin_java_bound_object.h
new file mode 100644
index 00000000000..610b0c42899
--- /dev/null
+++ b/chromium/content/browser/renderer_host/java/gin_java_bound_object.h
@@ -0,0 +1,96 @@
+// 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_RENDERER_HOST_JAVA_GIN_JAVA_BOUND_OBJECT_H_
+#define CONTENT_BROWSER_RENDERER_HOST_JAVA_GIN_JAVA_BOUND_OBJECT_H_
+
+#include <map>
+#include <set>
+
+#include "base/android/jni_weak_ref.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/id_map.h"
+#include "base/memory/linked_ptr.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/values.h"
+#include "content/browser/renderer_host/java/java_method.h"
+
+namespace content {
+
+class RenderFrameHost;
+
+class GinJavaBoundObject
+ : public base::RefCountedThreadSafe<GinJavaBoundObject> {
+ public:
+ // Using scoped_refptr<> as a value type is somewhat not IDMap had been
+ // designed for (it returns T* from lookups, so we have to de-ref it), but on
+ // the other hand, this allows us to manage lifetime of GinJavaBoundObject
+ // automatically.
+ typedef IDMap<scoped_refptr<GinJavaBoundObject>, IDMapOwnPointer> ObjectMap;
+ typedef ObjectMap::KeyType ObjectID;
+
+ static GinJavaBoundObject* CreateNamed(
+ const JavaObjectWeakGlobalRef& ref,
+ const base::android::JavaRef<jclass>& safe_annotation_clazz);
+ static GinJavaBoundObject* CreateTransient(
+ const JavaObjectWeakGlobalRef& ref,
+ const base::android::JavaRef<jclass>& safe_annotation_clazz,
+ RenderFrameHost* holder);
+
+ JavaObjectWeakGlobalRef& GetWeakRef() { return ref_; }
+ base::android::ScopedJavaLocalRef<jobject> GetLocalRef(JNIEnv* env) {
+ return ref_.get(env);
+ }
+
+ bool IsNamed() { return names_count_ > 0; }
+ void AddName() { ++names_count_; }
+ void RemoveName() { --names_count_; }
+
+ bool HasHolders() { return !holders_.empty(); }
+ void AddHolder(RenderFrameHost* holder) { holders_.insert(holder); }
+ void RemoveHolder(RenderFrameHost* holder) { holders_.erase(holder); }
+
+ // The following methods are called on the background thread.
+ std::set<std::string> GetMethodNames();
+ bool HasMethod(const std::string& method_name);
+ const JavaMethod* FindMethod(const std::string& method_name,
+ size_t num_parameters);
+ bool IsObjectGetClassMethod(const JavaMethod* method);
+ const base::android::JavaRef<jclass>& GetSafeAnnotationClass();
+ base::android::ScopedJavaLocalRef<jclass> GetLocalClassRef(JNIEnv* env);
+
+ private:
+ friend class base::RefCountedThreadSafe<GinJavaBoundObject>;
+
+ GinJavaBoundObject(
+ const JavaObjectWeakGlobalRef& ref,
+ const base::android::JavaRef<jclass>& safe_annotation_clazz);
+ GinJavaBoundObject(
+ const JavaObjectWeakGlobalRef& ref,
+ const base::android::JavaRef<jclass>& safe_annotation_clazz,
+ const std::set<RenderFrameHost*> holders);
+ ~GinJavaBoundObject();
+
+ // The following methods are called on the background thread.
+ void EnsureMethodsAreSetUp();
+
+ JavaObjectWeakGlobalRef ref_;
+
+ // An object must be kept in retained_object_set_ either if it has
+ // names or if it has a non-empty holders set.
+ int names_count_;
+ std::set<RenderFrameHost*> holders_;
+
+ // The following fields are accessed on the background thread.
+ typedef std::multimap<std::string, linked_ptr<JavaMethod> > JavaMethodMap;
+ JavaMethodMap methods_;
+ jmethodID object_get_class_method_id_;
+ bool are_methods_set_up_;
+ base::android::ScopedJavaGlobalRef<jclass> safe_annotation_clazz_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_JAVA_GIN_JAVA_BOUND_OBJECT_H_
diff --git a/chromium/content/browser/renderer_host/java/gin_java_bound_object_delegate.cc b/chromium/content/browser/renderer_host/java/gin_java_bound_object_delegate.cc
new file mode 100644
index 00000000000..92b1ccc2fc1
--- /dev/null
+++ b/chromium/content/browser/renderer_host/java/gin_java_bound_object_delegate.cc
@@ -0,0 +1,43 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/java/gin_java_bound_object_delegate.h"
+
+namespace content {
+
+GinJavaBoundObjectDelegate::GinJavaBoundObjectDelegate(
+ scoped_refptr<GinJavaBoundObject> object)
+ : object_(object) {
+}
+
+GinJavaBoundObjectDelegate::~GinJavaBoundObjectDelegate() {
+}
+
+base::android::ScopedJavaLocalRef<jobject>
+GinJavaBoundObjectDelegate::GetLocalRef(JNIEnv* env) {
+ return object_->GetLocalRef(env);
+}
+
+base::android::ScopedJavaLocalRef<jclass>
+GinJavaBoundObjectDelegate::GetLocalClassRef(JNIEnv* env) {
+ return object_->GetLocalClassRef(env);
+}
+
+const JavaMethod* GinJavaBoundObjectDelegate::FindMethod(
+ const std::string& method_name,
+ size_t num_parameters) {
+ return object_->FindMethod(method_name, num_parameters);
+}
+
+bool GinJavaBoundObjectDelegate::IsObjectGetClassMethod(
+ const JavaMethod* method) {
+ return object_->IsObjectGetClassMethod(method);
+}
+
+const base::android::JavaRef<jclass>&
+GinJavaBoundObjectDelegate::GetSafeAnnotationClass() {
+ return object_->GetSafeAnnotationClass();
+}
+
+} // namespace content
diff --git a/chromium/content/browser/renderer_host/java/gin_java_bound_object_delegate.h b/chromium/content/browser/renderer_host/java/gin_java_bound_object_delegate.h
new file mode 100644
index 00000000000..9852d91e733
--- /dev/null
+++ b/chromium/content/browser/renderer_host/java/gin_java_bound_object_delegate.h
@@ -0,0 +1,38 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_JAVA_GIN_JAVA_BOUND_OBJECT_DELEGATE_H_
+#define CONTENT_BROWSER_RENDERER_HOST_JAVA_GIN_JAVA_BOUND_OBJECT_DELEGATE_H_
+
+#include "base/memory/ref_counted.h"
+#include "content/browser/renderer_host/java/gin_java_bound_object.h"
+#include "content/browser/renderer_host/java/gin_java_method_invocation_helper.h"
+
+namespace content {
+
+class GinJavaBoundObjectDelegate
+ : public GinJavaMethodInvocationHelper::ObjectDelegate {
+ public:
+ GinJavaBoundObjectDelegate(scoped_refptr<GinJavaBoundObject> object);
+ virtual ~GinJavaBoundObjectDelegate();
+
+ virtual base::android::ScopedJavaLocalRef<jobject> GetLocalRef(
+ JNIEnv* env) OVERRIDE;
+ virtual base::android::ScopedJavaLocalRef<jclass> GetLocalClassRef(
+ JNIEnv* env) OVERRIDE;
+ virtual const JavaMethod* FindMethod(const std::string& method_name,
+ size_t num_parameters) OVERRIDE;
+ virtual bool IsObjectGetClassMethod(const JavaMethod* method) OVERRIDE;
+ virtual const base::android::JavaRef<jclass>& GetSafeAnnotationClass()
+ OVERRIDE;
+
+ private:
+ scoped_refptr<GinJavaBoundObject> object_;
+
+ DISALLOW_COPY_AND_ASSIGN(GinJavaBoundObjectDelegate);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_JAVA_GIN_JAVA_BOUND_OBJECT_DELEGATE_H_
diff --git a/chromium/content/browser/renderer_host/java/gin_java_method_invocation_helper.cc b/chromium/content/browser/renderer_host/java/gin_java_method_invocation_helper.cc
new file mode 100644
index 00000000000..d3fb1386544
--- /dev/null
+++ b/chromium/content/browser/renderer_host/java/gin_java_method_invocation_helper.cc
@@ -0,0 +1,341 @@
+// 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/renderer_host/java/gin_java_method_invocation_helper.h"
+
+#include <unistd.h>
+
+#include "base/android/event_log.h"
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/float_util.h"
+#include "content/browser/renderer_host/java/gin_java_script_to_java_types_coercion.h"
+#include "content/browser/renderer_host/java/java_method.h"
+#include "content/browser/renderer_host/java/jni_helper.h"
+#include "content/common/android/gin_java_bridge_value.h"
+#include "content/public/browser/browser_thread.h"
+
+using base::android::AttachCurrentThread;
+using base::android::ScopedJavaLocalRef;
+
+namespace content {
+
+namespace {
+
+const char kObjectIsGone[] = "Java object is gone";
+const char kMethodNotFound[] = "Method not found";
+const char kAccessToObjectGetClassIsBlocked[] =
+ "Access to java.lang.Object.getClass is blocked";
+const char kJavaExceptionRaised[] =
+ "Java exception has been raised during method invocation";
+
+// See frameworks/base/core/java/android/webkit/EventLogTags.logtags
+const int kObjectGetClassInvocationAttemptLogTag = 70151;
+
+} // namespace
+
+GinJavaMethodInvocationHelper::GinJavaMethodInvocationHelper(
+ scoped_ptr<ObjectDelegate> object,
+ const std::string& method_name,
+ const base::ListValue& arguments)
+ : object_(object.Pass()),
+ method_name_(method_name),
+ arguments_(arguments.DeepCopy()) {
+}
+
+GinJavaMethodInvocationHelper::~GinJavaMethodInvocationHelper() {}
+
+void GinJavaMethodInvocationHelper::Init(DispatcherDelegate* dispatcher) {
+ // Build on the UI thread a map of object_id -> WeakRef for Java objects from
+ // |arguments_|. Then we can use this map on the background thread without
+ // accessing |dispatcher|.
+ BuildObjectRefsFromListValue(dispatcher, arguments_.get());
+}
+
+// As V8ValueConverter has finite recursion depth when serializing
+// JavaScript values, we don't bother about having a recursion threshold here.
+void GinJavaMethodInvocationHelper::BuildObjectRefsFromListValue(
+ DispatcherDelegate* dispatcher,
+ const base::Value* list_value) {
+ DCHECK(list_value->IsType(base::Value::TYPE_LIST));
+ const base::ListValue* list;
+ list_value->GetAsList(&list);
+ for (base::ListValue::const_iterator iter = list->begin();
+ iter != list->end();
+ ++iter) {
+ if (AppendObjectRef(dispatcher, *iter))
+ continue;
+ if ((*iter)->IsType(base::Value::TYPE_LIST)) {
+ BuildObjectRefsFromListValue(dispatcher, *iter);
+ } else if ((*iter)->IsType(base::Value::TYPE_DICTIONARY)) {
+ BuildObjectRefsFromDictionaryValue(dispatcher, *iter);
+ }
+ }
+}
+
+void GinJavaMethodInvocationHelper::BuildObjectRefsFromDictionaryValue(
+ DispatcherDelegate* dispatcher,
+ const base::Value* dict_value) {
+ DCHECK(dict_value->IsType(base::Value::TYPE_DICTIONARY));
+ const base::DictionaryValue* dict;
+ dict_value->GetAsDictionary(&dict);
+ for (base::DictionaryValue::Iterator iter(*dict);
+ !iter.IsAtEnd();
+ iter.Advance()) {
+ if (AppendObjectRef(dispatcher, &iter.value()))
+ continue;
+ if (iter.value().IsType(base::Value::TYPE_LIST)) {
+ BuildObjectRefsFromListValue(dispatcher, &iter.value());
+ } else if (iter.value().IsType(base::Value::TYPE_DICTIONARY)) {
+ BuildObjectRefsFromDictionaryValue(dispatcher, &iter.value());
+ }
+ }
+}
+
+bool GinJavaMethodInvocationHelper::AppendObjectRef(
+ DispatcherDelegate* dispatcher,
+ const base::Value* raw_value) {
+ if (!GinJavaBridgeValue::ContainsGinJavaBridgeValue(raw_value))
+ return false;
+ scoped_ptr<const GinJavaBridgeValue> value(
+ GinJavaBridgeValue::FromValue(raw_value));
+ if (!value->IsType(GinJavaBridgeValue::TYPE_OBJECT_ID))
+ return false;
+ GinJavaBoundObject::ObjectID object_id;
+ if (value->GetAsObjectID(&object_id)) {
+ ObjectRefs::iterator iter = object_refs_.find(object_id);
+ if (iter == object_refs_.end()) {
+ JavaObjectWeakGlobalRef object_ref(
+ dispatcher->GetObjectWeakRef(object_id));
+ if (!object_ref.is_empty()) {
+ object_refs_.insert(std::make_pair(object_id, object_ref));
+ }
+ }
+ }
+ return true;
+}
+
+void GinJavaMethodInvocationHelper::Invoke() {
+ JNIEnv* env = AttachCurrentThread();
+ const JavaMethod* method =
+ object_->FindMethod(method_name_, arguments_->GetSize());
+ if (!method) {
+ SetInvocationFailure(kMethodNotFound);
+ return;
+ }
+
+ if (object_->IsObjectGetClassMethod(method)) {
+ base::android::EventLogWriteInt(kObjectGetClassInvocationAttemptLogTag,
+ getuid());
+ SetInvocationFailure(kAccessToObjectGetClassIsBlocked);
+ return;
+ }
+
+ ScopedJavaLocalRef<jobject> obj;
+ ScopedJavaLocalRef<jclass> cls;
+ if (method->is_static()) {
+ cls = object_->GetLocalClassRef(env);
+ } else {
+ obj = object_->GetLocalRef(env);
+ }
+ if (obj.is_null() && cls.is_null()) {
+ SetInvocationFailure(kObjectIsGone);
+ return;
+ }
+
+ std::vector<jvalue> parameters(method->num_parameters());
+ for (size_t i = 0; i < method->num_parameters(); ++i) {
+ const base::Value* argument;
+ arguments_->Get(i, &argument);
+ parameters[i] = CoerceJavaScriptValueToJavaValue(
+ env, argument, method->parameter_type(i), true, object_refs_);
+ }
+ if (method->is_static()) {
+ InvokeMethod(
+ NULL, cls.obj(), method->return_type(), method->id(), &parameters[0]);
+ } else {
+ InvokeMethod(
+ obj.obj(), NULL, method->return_type(), method->id(), &parameters[0]);
+ }
+
+ // Now that we're done with the jvalue, release any local references created
+ // by CoerceJavaScriptValueToJavaValue().
+ for (size_t i = 0; i < method->num_parameters(); ++i) {
+ ReleaseJavaValueIfRequired(env, &parameters[i], method->parameter_type(i));
+ }
+}
+
+void GinJavaMethodInvocationHelper::SetInvocationFailure(
+ const char* error_message) {
+ holds_primitive_result_ = true;
+ primitive_result_.reset(new base::ListValue());
+ error_message_ = error_message;
+}
+
+void GinJavaMethodInvocationHelper::SetPrimitiveResult(
+ const base::ListValue& result_wrapper) {
+ holds_primitive_result_ = true;
+ primitive_result_.reset(result_wrapper.DeepCopy());
+}
+
+void GinJavaMethodInvocationHelper::SetObjectResult(
+ const base::android::JavaRef<jobject>& object,
+ const base::android::JavaRef<jclass>& safe_annotation_clazz) {
+ holds_primitive_result_ = false;
+ object_result_.Reset(object);
+ safe_annotation_clazz_.Reset(safe_annotation_clazz);
+}
+
+bool GinJavaMethodInvocationHelper::HoldsPrimitiveResult() {
+ return holds_primitive_result_;
+}
+
+const base::ListValue& GinJavaMethodInvocationHelper::GetPrimitiveResult() {
+ return *primitive_result_.get();
+}
+
+const base::android::JavaRef<jobject>&
+GinJavaMethodInvocationHelper::GetObjectResult() {
+ return object_result_;
+}
+
+const base::android::JavaRef<jclass>&
+GinJavaMethodInvocationHelper::GetSafeAnnotationClass() {
+ return safe_annotation_clazz_;
+}
+
+const std::string& GinJavaMethodInvocationHelper::GetErrorMessage() {
+ return error_message_;
+}
+
+void GinJavaMethodInvocationHelper::InvokeMethod(jobject object,
+ jclass clazz,
+ const JavaType& return_type,
+ jmethodID id,
+ jvalue* parameters) {
+ DCHECK(object || clazz);
+ JNIEnv* env = AttachCurrentThread();
+ base::ListValue result_wrapper;
+ switch (return_type.type) {
+ case JavaType::TypeBoolean:
+ result_wrapper.AppendBoolean(
+ object ? env->CallBooleanMethodA(object, id, parameters)
+ : env->CallStaticBooleanMethodA(clazz, id, parameters));
+ break;
+ case JavaType::TypeByte:
+ result_wrapper.AppendInteger(
+ object ? env->CallByteMethodA(object, id, parameters)
+ : env->CallStaticByteMethodA(clazz, id, parameters));
+ break;
+ case JavaType::TypeChar:
+ result_wrapper.AppendInteger(
+ object ? env->CallCharMethodA(object, id, parameters)
+ : env->CallStaticCharMethodA(clazz, id, parameters));
+ break;
+ case JavaType::TypeShort:
+ result_wrapper.AppendInteger(
+ object ? env->CallShortMethodA(object, id, parameters)
+ : env->CallStaticShortMethodA(clazz, id, parameters));
+ break;
+ case JavaType::TypeInt:
+ result_wrapper.AppendInteger(
+ object ? env->CallIntMethodA(object, id, parameters)
+ : env->CallStaticIntMethodA(clazz, id, parameters));
+ break;
+ case JavaType::TypeLong:
+ result_wrapper.AppendDouble(
+ object ? env->CallLongMethodA(object, id, parameters)
+ : env->CallStaticLongMethodA(clazz, id, parameters));
+ break;
+ case JavaType::TypeFloat: {
+ float result = object
+ ? env->CallFloatMethodA(object, id, parameters)
+ : env->CallStaticFloatMethodA(clazz, id, parameters);
+ if (base::IsFinite(result)) {
+ result_wrapper.AppendDouble(result);
+ } else {
+ result_wrapper.Append(
+ GinJavaBridgeValue::CreateNonFiniteValue(result).release());
+ }
+ break;
+ }
+ case JavaType::TypeDouble: {
+ double result = object
+ ? env->CallDoubleMethodA(object, id, parameters)
+ : env->CallStaticDoubleMethodA(clazz, id, parameters);
+ if (base::IsFinite(result)) {
+ result_wrapper.AppendDouble(result);
+ } else {
+ result_wrapper.Append(
+ GinJavaBridgeValue::CreateNonFiniteValue(result).release());
+ }
+ break;
+ }
+ case JavaType::TypeVoid:
+ if (object)
+ env->CallVoidMethodA(object, id, parameters);
+ else
+ env->CallStaticVoidMethodA(clazz, id, parameters);
+ result_wrapper.Append(
+ GinJavaBridgeValue::CreateUndefinedValue().release());
+ break;
+ case JavaType::TypeArray:
+ // LIVECONNECT_COMPLIANCE: Existing behavior is to not call methods that
+ // return arrays. Spec requires calling the method and converting the
+ // result to a JavaScript array.
+ result_wrapper.Append(
+ GinJavaBridgeValue::CreateUndefinedValue().release());
+ break;
+ case JavaType::TypeString: {
+ jstring java_string = static_cast<jstring>(
+ object ? env->CallObjectMethodA(object, id, parameters)
+ : env->CallStaticObjectMethodA(clazz, id, parameters));
+ // If an exception was raised, we must clear it before calling most JNI
+ // methods. ScopedJavaLocalRef is liable to make such calls, so we test
+ // first.
+ if (base::android::ClearException(env)) {
+ SetInvocationFailure(kJavaExceptionRaised);
+ return;
+ }
+ ScopedJavaLocalRef<jstring> scoped_java_string(env, java_string);
+ if (!scoped_java_string.obj()) {
+ // LIVECONNECT_COMPLIANCE: Existing behavior is to return undefined.
+ // Spec requires returning a null string.
+ result_wrapper.Append(
+ GinJavaBridgeValue::CreateUndefinedValue().release());
+ break;
+ }
+ result_wrapper.AppendString(
+ base::android::ConvertJavaStringToUTF8(scoped_java_string));
+ break;
+ }
+ case JavaType::TypeObject: {
+ // If an exception was raised, we must clear it before calling most JNI
+ // methods. ScopedJavaLocalRef is liable to make such calls, so we test
+ // first.
+ jobject java_object =
+ object ? env->CallObjectMethodA(object, id, parameters)
+ : env->CallStaticObjectMethodA(clazz, id, parameters);
+ if (base::android::ClearException(env)) {
+ SetInvocationFailure(kJavaExceptionRaised);
+ return;
+ }
+ ScopedJavaLocalRef<jobject> scoped_java_object(env, java_object);
+ if (!scoped_java_object.obj()) {
+ result_wrapper.Append(base::Value::CreateNullValue());
+ break;
+ }
+ SetObjectResult(scoped_java_object, object_->GetSafeAnnotationClass());
+ return;
+ }
+ }
+ // This is for all cases except JavaType::TypeObject.
+ if (!base::android::ClearException(env)) {
+ SetPrimitiveResult(result_wrapper);
+ } else {
+ SetInvocationFailure(kJavaExceptionRaised);
+ }
+}
+
+} // namespace content
diff --git a/chromium/content/browser/renderer_host/java/gin_java_method_invocation_helper.h b/chromium/content/browser/renderer_host/java/gin_java_method_invocation_helper.h
new file mode 100644
index 00000000000..89805ca0696
--- /dev/null
+++ b/chromium/content/browser/renderer_host/java/gin_java_method_invocation_helper.h
@@ -0,0 +1,116 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_JAVA_GIN_JAVA_METHOD_INVOCATION_HELPER_H_
+#define CONTENT_BROWSER_RENDERER_HOST_JAVA_GIN_JAVA_METHOD_INVOCATION_HELPER_H_
+
+#include <map>
+
+#include "base/android/jni_weak_ref.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/memory/ref_counted.h"
+#include "base/values.h"
+#include "content/browser/renderer_host/java/gin_java_bound_object.h"
+#include "content/browser/renderer_host/java/java_type.h"
+#include "content/common/content_export.h"
+
+namespace content {
+
+class JavaMethod;
+
+// Instances of this class are created and initialized on the UI thread, do
+// their work on the background thread, and then again examined on the UI
+// thread. They don't work on both threads simultaneously, though.
+class CONTENT_EXPORT GinJavaMethodInvocationHelper
+ : public base::RefCountedThreadSafe<GinJavaMethodInvocationHelper> {
+ public:
+ // DispatcherDelegate is used on the UI thread
+ class DispatcherDelegate {
+ public:
+ DispatcherDelegate() {}
+ virtual ~DispatcherDelegate() {}
+ virtual JavaObjectWeakGlobalRef GetObjectWeakRef(
+ GinJavaBoundObject::ObjectID object_id) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DispatcherDelegate);
+ };
+
+ // ObjectDelegate is used in the background thread
+ class ObjectDelegate {
+ public:
+ ObjectDelegate() {}
+ virtual ~ObjectDelegate() {}
+ virtual base::android::ScopedJavaLocalRef<jobject> GetLocalRef(
+ JNIEnv* env) = 0;
+ virtual base::android::ScopedJavaLocalRef<jclass> GetLocalClassRef(
+ JNIEnv* env) = 0;
+ virtual const JavaMethod* FindMethod(const std::string& method_name,
+ size_t num_parameters) = 0;
+ virtual bool IsObjectGetClassMethod(const JavaMethod* method) = 0;
+ virtual const base::android::JavaRef<jclass>& GetSafeAnnotationClass() = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ObjectDelegate);
+ };
+
+ GinJavaMethodInvocationHelper(scoped_ptr<ObjectDelegate> object,
+ const std::string& method_name,
+ const base::ListValue& arguments);
+ void Init(DispatcherDelegate* dispatcher);
+
+ // Called on the background thread
+ void Invoke();
+
+ // Called on the UI thread
+ bool HoldsPrimitiveResult();
+ const base::ListValue& GetPrimitiveResult();
+ const base::android::JavaRef<jobject>& GetObjectResult();
+ const base::android::JavaRef<jclass>& GetSafeAnnotationClass();
+ const std::string& GetErrorMessage();
+
+ private:
+ friend class base::RefCountedThreadSafe<GinJavaMethodInvocationHelper>;
+ ~GinJavaMethodInvocationHelper();
+
+ // Called on the UI thread
+ void BuildObjectRefsFromListValue(DispatcherDelegate* dispatcher,
+ const base::Value* list_value);
+ void BuildObjectRefsFromDictionaryValue(DispatcherDelegate* dispatcher,
+ const base::Value* dict_value);
+
+ bool AppendObjectRef(DispatcherDelegate* dispatcher,
+ const base::Value* raw_value);
+
+ // Called on the background thread.
+ void InvokeMethod(jobject object,
+ jclass clazz,
+ const JavaType& return_type,
+ jmethodID id,
+ jvalue* parameters);
+ void SetInvocationFailure(const char* error_message);
+ void SetPrimitiveResult(const base::ListValue& result_wrapper);
+ void SetObjectResult(
+ const base::android::JavaRef<jobject>& object,
+ const base::android::JavaRef<jclass>& safe_annotation_clazz);
+
+ typedef std::map<GinJavaBoundObject::ObjectID,
+ JavaObjectWeakGlobalRef> ObjectRefs;
+
+ scoped_ptr<ObjectDelegate> object_;
+ const std::string method_name_;
+ scoped_ptr<base::ListValue> arguments_;
+ ObjectRefs object_refs_;
+ bool holds_primitive_result_;
+ scoped_ptr<base::ListValue> primitive_result_;
+ std::string error_message_;
+ base::android::ScopedJavaGlobalRef<jobject> object_result_;
+ base::android::ScopedJavaGlobalRef<jclass> safe_annotation_clazz_;
+
+ DISALLOW_COPY_AND_ASSIGN(GinJavaMethodInvocationHelper);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_JAVA_GIN_JAVA_METHOD_INVOCATION_HELPER_H_
diff --git a/chromium/content/browser/renderer_host/java/gin_java_method_invocation_helper_unittest.cc b/chromium/content/browser/renderer_host/java/gin_java_method_invocation_helper_unittest.cc
new file mode 100644
index 00000000000..d7634410dee
--- /dev/null
+++ b/chromium/content/browser/renderer_host/java/gin_java_method_invocation_helper_unittest.cc
@@ -0,0 +1,285 @@
+// 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/renderer_host/java/gin_java_method_invocation_helper.h"
+
+#include "base/android/jni_android.h"
+#include "content/common/android/gin_java_bridge_value.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+namespace {
+
+class NullObjectDelegate
+ : public GinJavaMethodInvocationHelper::ObjectDelegate {
+ public:
+ NullObjectDelegate() {}
+
+ virtual ~NullObjectDelegate() {}
+
+ virtual base::android::ScopedJavaLocalRef<jobject> GetLocalRef(
+ JNIEnv* env) OVERRIDE {
+ return base::android::ScopedJavaLocalRef<jobject>();
+ }
+
+ virtual base::android::ScopedJavaLocalRef<jclass> GetLocalClassRef(
+ JNIEnv* env) OVERRIDE {
+ return base::android::ScopedJavaLocalRef<jclass>();
+ }
+
+ virtual const JavaMethod* FindMethod(const std::string& method_name,
+ size_t num_parameters) OVERRIDE {
+ return NULL;
+ }
+
+ virtual bool IsObjectGetClassMethod(const JavaMethod* method) OVERRIDE {
+ return false;
+ }
+
+ virtual const base::android::JavaRef<jclass>& GetSafeAnnotationClass()
+ OVERRIDE {
+ return safe_annotation_class_;
+ }
+
+ private:
+ base::android::ScopedJavaLocalRef<jclass> safe_annotation_class_;
+
+ DISALLOW_COPY_AND_ASSIGN(NullObjectDelegate);
+};
+
+class NullDispatcherDelegate
+ : public GinJavaMethodInvocationHelper::DispatcherDelegate {
+ public:
+ NullDispatcherDelegate() {}
+
+ virtual ~NullDispatcherDelegate() {}
+
+ virtual JavaObjectWeakGlobalRef GetObjectWeakRef(
+ GinJavaBoundObject::ObjectID object_id) OVERRIDE {
+ return JavaObjectWeakGlobalRef();
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(NullDispatcherDelegate);
+};
+
+} // namespace
+
+class GinJavaMethodInvocationHelperTest : public testing::Test {
+};
+
+namespace {
+
+class CountingDispatcherDelegate
+ : public GinJavaMethodInvocationHelper::DispatcherDelegate {
+ public:
+ CountingDispatcherDelegate() {}
+
+ virtual ~CountingDispatcherDelegate() {}
+
+ virtual JavaObjectWeakGlobalRef GetObjectWeakRef(
+ GinJavaBoundObject::ObjectID object_id) OVERRIDE {
+ counters_[object_id]++;
+ return JavaObjectWeakGlobalRef();
+ }
+
+ void AssertInvocationsCount(GinJavaBoundObject::ObjectID begin_object_id,
+ GinJavaBoundObject::ObjectID end_object_id) {
+ EXPECT_EQ(end_object_id - begin_object_id,
+ static_cast<int>(counters_.size()));
+ for (GinJavaBoundObject::ObjectID i = begin_object_id;
+ i < end_object_id; ++i) {
+ EXPECT_LT(0, counters_[i]) << "ObjectID: " << i;
+ }
+ }
+
+ private:
+ typedef std::map<GinJavaBoundObject::ObjectID, int> Counters;
+ Counters counters_;
+
+ DISALLOW_COPY_AND_ASSIGN(CountingDispatcherDelegate);
+};
+
+} // namespace
+
+TEST_F(GinJavaMethodInvocationHelperTest, RetrievalOfObjectsNoObjects) {
+ base::ListValue no_objects;
+ for (int i = 0; i < 10; ++i) {
+ no_objects.AppendInteger(i);
+ }
+
+ scoped_refptr<GinJavaMethodInvocationHelper> helper =
+ new GinJavaMethodInvocationHelper(
+ scoped_ptr<GinJavaMethodInvocationHelper::ObjectDelegate>(
+ new NullObjectDelegate()),
+ "foo",
+ no_objects);
+ CountingDispatcherDelegate counter;
+ helper->Init(&counter);
+ counter.AssertInvocationsCount(0, 0);
+}
+
+TEST_F(GinJavaMethodInvocationHelperTest, RetrievalOfObjectsHaveObjects) {
+ base::ListValue objects;
+ objects.AppendInteger(100);
+ objects.Append(GinJavaBridgeValue::CreateObjectIDValue(1).release());
+ base::ListValue* sub_list = new base::ListValue();
+ sub_list->AppendInteger(200);
+ sub_list->Append(GinJavaBridgeValue::CreateObjectIDValue(2).release());
+ objects.Append(sub_list);
+ base::DictionaryValue* sub_dict = new base::DictionaryValue();
+ sub_dict->SetInteger("1", 300);
+ sub_dict->Set("2", GinJavaBridgeValue::CreateObjectIDValue(3).release());
+ objects.Append(sub_dict);
+ base::ListValue* sub_list_with_dict = new base::ListValue();
+ base::DictionaryValue* sub_sub_dict = new base::DictionaryValue();
+ sub_sub_dict->Set("1", GinJavaBridgeValue::CreateObjectIDValue(4).release());
+ sub_list_with_dict->Append(sub_sub_dict);
+ objects.Append(sub_list_with_dict);
+ base::DictionaryValue* sub_dict_with_list = new base::DictionaryValue();
+ base::ListValue* sub_sub_list = new base::ListValue();
+ sub_sub_list->Append(GinJavaBridgeValue::CreateObjectIDValue(5).release());
+ sub_dict_with_list->Set("1", sub_sub_list);
+ objects.Append(sub_dict_with_list);
+
+ scoped_refptr<GinJavaMethodInvocationHelper> helper =
+ new GinJavaMethodInvocationHelper(
+ scoped_ptr<GinJavaMethodInvocationHelper::ObjectDelegate>(
+ new NullObjectDelegate()),
+ "foo",
+ objects);
+ CountingDispatcherDelegate counter;
+ helper->Init(&counter);
+ counter.AssertInvocationsCount(1, 6);
+}
+
+TEST_F(GinJavaMethodInvocationHelperTest, HandleObjectIsGone) {
+ base::ListValue no_objects;
+ scoped_refptr<GinJavaMethodInvocationHelper> helper =
+ new GinJavaMethodInvocationHelper(
+ scoped_ptr<GinJavaMethodInvocationHelper::ObjectDelegate>(
+ new NullObjectDelegate()),
+ "foo",
+ no_objects);
+ NullDispatcherDelegate dispatcher;
+ helper->Init(&dispatcher);
+ EXPECT_TRUE(helper->GetErrorMessage().empty());
+ helper->Invoke();
+ EXPECT_TRUE(helper->HoldsPrimitiveResult());
+ EXPECT_TRUE(helper->GetPrimitiveResult().empty());
+ EXPECT_FALSE(helper->GetErrorMessage().empty());
+}
+
+namespace {
+
+class MethodNotFoundObjectDelegate : public NullObjectDelegate {
+ public:
+ MethodNotFoundObjectDelegate() : find_method_called_(false) {}
+
+ virtual ~MethodNotFoundObjectDelegate() {}
+
+ virtual base::android::ScopedJavaLocalRef<jobject> GetLocalRef(
+ JNIEnv* env) OVERRIDE {
+ return base::android::ScopedJavaLocalRef<jobject>(
+ env, static_cast<jobject>(env->FindClass("java/lang/String")));
+ }
+
+ virtual const JavaMethod* FindMethod(const std::string& method_name,
+ size_t num_parameters) OVERRIDE {
+ find_method_called_ = true;
+ return NULL;
+ }
+
+ bool find_method_called() const { return find_method_called_; }
+
+ protected:
+ bool find_method_called_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MethodNotFoundObjectDelegate);
+};
+
+} // namespace
+
+TEST_F(GinJavaMethodInvocationHelperTest, HandleMethodNotFound) {
+ base::ListValue no_objects;
+ MethodNotFoundObjectDelegate* object_delegate =
+ new MethodNotFoundObjectDelegate();
+ scoped_refptr<GinJavaMethodInvocationHelper> helper =
+ new GinJavaMethodInvocationHelper(
+ scoped_ptr<GinJavaMethodInvocationHelper::ObjectDelegate>(
+ object_delegate),
+ "foo",
+ no_objects);
+ NullDispatcherDelegate dispatcher;
+ helper->Init(&dispatcher);
+ EXPECT_FALSE(object_delegate->find_method_called());
+ EXPECT_TRUE(helper->GetErrorMessage().empty());
+ helper->Invoke();
+ EXPECT_TRUE(object_delegate->find_method_called());
+ EXPECT_TRUE(helper->HoldsPrimitiveResult());
+ EXPECT_TRUE(helper->GetPrimitiveResult().empty());
+ EXPECT_FALSE(helper->GetErrorMessage().empty());
+}
+
+namespace {
+
+class GetClassObjectDelegate : public MethodNotFoundObjectDelegate {
+ public:
+ GetClassObjectDelegate() : get_class_called_(false) {}
+
+ virtual ~GetClassObjectDelegate() {}
+
+ virtual const JavaMethod* FindMethod(const std::string& method_name,
+ size_t num_parameters) OVERRIDE {
+ find_method_called_ = true;
+ return kFakeGetClass;
+ }
+
+ virtual bool IsObjectGetClassMethod(const JavaMethod* method) OVERRIDE {
+ get_class_called_ = true;
+ return kFakeGetClass == method;
+ }
+
+ bool get_class_called() const { return get_class_called_; }
+
+ private:
+ static const JavaMethod* kFakeGetClass;
+ bool get_class_called_;
+
+ DISALLOW_COPY_AND_ASSIGN(GetClassObjectDelegate);
+};
+
+// We don't expect GinJavaMethodInvocationHelper to actually invoke the
+// method, since the point of the test is to verify whether calls to
+// 'getClass' get blocked.
+const JavaMethod* GetClassObjectDelegate::kFakeGetClass =
+ (JavaMethod*)0xdeadbeef;
+
+} // namespace
+
+TEST_F(GinJavaMethodInvocationHelperTest, HandleGetClassInvocation) {
+ base::ListValue no_objects;
+ GetClassObjectDelegate* object_delegate =
+ new GetClassObjectDelegate();
+ scoped_refptr<GinJavaMethodInvocationHelper> helper =
+ new GinJavaMethodInvocationHelper(
+ scoped_ptr<GinJavaMethodInvocationHelper::ObjectDelegate>(
+ object_delegate),
+ "foo",
+ no_objects);
+ NullDispatcherDelegate dispatcher;
+ helper->Init(&dispatcher);
+ EXPECT_FALSE(object_delegate->find_method_called());
+ EXPECT_FALSE(object_delegate->get_class_called());
+ EXPECT_TRUE(helper->GetErrorMessage().empty());
+ helper->Invoke();
+ EXPECT_TRUE(object_delegate->find_method_called());
+ EXPECT_TRUE(object_delegate->get_class_called());
+ EXPECT_TRUE(helper->HoldsPrimitiveResult());
+ EXPECT_TRUE(helper->GetPrimitiveResult().empty());
+ EXPECT_FALSE(helper->GetErrorMessage().empty());
+}
+
+} // namespace content
diff --git a/chromium/content/browser/renderer_host/java/gin_java_script_to_java_types_coercion.cc b/chromium/content/browser/renderer_host/java/gin_java_script_to_java_types_coercion.cc
new file mode 100644
index 00000000000..8f7c29f16eb
--- /dev/null
+++ b/chromium/content/browser/renderer_host/java/gin_java_script_to_java_types_coercion.cc
@@ -0,0 +1,705 @@
+// 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/renderer_host/java/gin_java_script_to_java_types_coercion.h"
+
+#include <unistd.h>
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/common/android/gin_java_bridge_value.h"
+
+using base::android::ConvertUTF8ToJavaString;
+
+namespace content {
+
+namespace {
+
+const char kJavaLangString[] = "java/lang/String";
+const char kUndefined[] = "undefined";
+
+double RoundDoubleTowardsZero(const double& x) {
+ if (std::isnan(x)) {
+ return 0.0;
+ }
+ return x > 0.0 ? floor(x) : ceil(x);
+}
+
+// Rounds to jlong using Java's type conversion rules.
+jlong RoundDoubleToLong(const double& x) {
+ double intermediate = RoundDoubleTowardsZero(x);
+ // The int64 limits can not be converted exactly to double values, so we
+ // compare to custom constants. kint64max is 2^63 - 1, but the spacing
+ // between double values in the the range 2^62 to 2^63 is 2^10. The cast is
+ // required to silence a spurious gcc warning for integer overflow.
+ const int64 kLimit = (GG_INT64_C(1) << 63) - static_cast<uint64>(1 << 10);
+ DCHECK(kLimit > 0);
+ const double kLargestDoubleLessThanInt64Max = kLimit;
+ const double kSmallestDoubleGreaterThanInt64Min = -kLimit;
+ if (intermediate > kLargestDoubleLessThanInt64Max) {
+ return kint64max;
+ }
+ if (intermediate < kSmallestDoubleGreaterThanInt64Min) {
+ return kint64min;
+ }
+ return static_cast<jlong>(intermediate);
+}
+
+// Rounds to jint using Java's type conversion rules.
+jint RoundDoubleToInt(const double& x) {
+ double intermediate = RoundDoubleTowardsZero(x);
+ // The int32 limits cast exactly to double values.
+ intermediate = std::min(intermediate, static_cast<double>(kint32max));
+ intermediate = std::max(intermediate, static_cast<double>(kint32min));
+ return static_cast<jint>(intermediate);
+}
+
+jvalue CoerceJavaScriptIntegerToJavaValue(JNIEnv* env,
+ const base::Value* value,
+ const JavaType& target_type,
+ bool coerce_to_string) {
+ // See http://jdk6.java.net/plugin2/liveconnect/#JS_NUMBER_VALUES.
+
+ // For conversion to numeric types, we need to replicate Java's type
+ // conversion rules. This requires that for integer values, we simply discard
+ // all but the lowest n buts, where n is the number of bits in the target
+ // type.
+ jvalue result;
+ int int_value;
+ value->GetAsInteger(&int_value);
+ switch (target_type.type) {
+ case JavaType::TypeByte:
+ result.b = static_cast<jbyte>(int_value);
+ break;
+ case JavaType::TypeChar:
+ result.c = static_cast<jchar>(int_value);
+ break;
+ case JavaType::TypeShort:
+ result.s = static_cast<jshort>(int_value);
+ break;
+ case JavaType::TypeInt:
+ result.i = int_value;
+ break;
+ case JavaType::TypeLong:
+ result.j = int_value;
+ break;
+ case JavaType::TypeFloat:
+ result.f = int_value;
+ break;
+ case JavaType::TypeDouble:
+ result.d = int_value;
+ break;
+ case JavaType::TypeObject:
+ // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to null. Spec
+ // requires handling object equivalents of primitive types.
+ result.l = NULL;
+ break;
+ case JavaType::TypeString:
+ result.l = coerce_to_string
+ ? ConvertUTF8ToJavaString(
+ env, base::Int64ToString(int_value)).Release()
+ : NULL;
+ break;
+ case JavaType::TypeBoolean:
+ // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec
+ // requires converting to false for 0 or NaN, true otherwise.
+ result.z = JNI_FALSE;
+ break;
+ case JavaType::TypeArray:
+ // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to null. Spec
+ // requires raising a JavaScript exception.
+ result.l = NULL;
+ break;
+ case JavaType::TypeVoid:
+ // Conversion to void must never happen.
+ NOTREACHED();
+ break;
+ }
+ return result;
+}
+
+jvalue CoerceJavaScriptDoubleToJavaValue(JNIEnv* env,
+ double double_value,
+ const JavaType& target_type,
+ bool coerce_to_string) {
+ // See http://jdk6.java.net/plugin2/liveconnect/#JS_NUMBER_VALUES.
+ // For conversion to numeric types, we need to replicate Java's type
+ // conversion rules.
+ jvalue result;
+ switch (target_type.type) {
+ case JavaType::TypeByte:
+ result.b = static_cast<jbyte>(RoundDoubleToInt(double_value));
+ break;
+ case JavaType::TypeChar:
+ // LIVECONNECT_COMPLIANCE: Existing behavior is to convert double to 0.
+ // Spec requires converting doubles similarly to how we convert doubles to
+ // other numeric types.
+ result.c = 0;
+ break;
+ case JavaType::TypeShort:
+ result.s = static_cast<jshort>(RoundDoubleToInt(double_value));
+ break;
+ case JavaType::TypeInt:
+ result.i = RoundDoubleToInt(double_value);
+ break;
+ case JavaType::TypeLong:
+ result.j = RoundDoubleToLong(double_value);
+ break;
+ case JavaType::TypeFloat:
+ result.f = static_cast<jfloat>(double_value);
+ break;
+ case JavaType::TypeDouble:
+ result.d = double_value;
+ break;
+ case JavaType::TypeObject:
+ // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to null. Spec
+ // requires handling object equivalents of primitive types.
+ result.l = NULL;
+ break;
+ case JavaType::TypeString:
+ result.l =
+ coerce_to_string
+ ? ConvertUTF8ToJavaString(
+ env, base::StringPrintf("%.6lg", double_value)).Release()
+ : NULL;
+ break;
+ case JavaType::TypeBoolean:
+ // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec
+ // requires converting to false for 0 or NaN, true otherwise.
+ result.z = JNI_FALSE;
+ break;
+ case JavaType::TypeArray:
+ // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to null. Spec
+ // requires raising a JavaScript exception.
+ result.l = NULL;
+ break;
+ case JavaType::TypeVoid:
+ // Conversion to void must never happen.
+ NOTREACHED();
+ break;
+ }
+ return result;
+}
+
+jvalue CoerceJavaScriptBooleanToJavaValue(JNIEnv* env,
+ const base::Value* value,
+ const JavaType& target_type,
+ bool coerce_to_string) {
+ // See http://jdk6.java.net/plugin2/liveconnect/#JS_BOOLEAN_VALUES.
+ bool boolean_value;
+ value->GetAsBoolean(&boolean_value);
+ jvalue result;
+ switch (target_type.type) {
+ case JavaType::TypeBoolean:
+ result.z = boolean_value ? JNI_TRUE : JNI_FALSE;
+ break;
+ case JavaType::TypeObject:
+ // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
+ // requires handling java.lang.Boolean and java.lang.Object.
+ result.l = NULL;
+ break;
+ case JavaType::TypeString:
+ result.l = coerce_to_string
+ ? ConvertUTF8ToJavaString(
+ env, boolean_value ? "true" : "false").Release()
+ : NULL;
+ break;
+ case JavaType::TypeByte:
+ case JavaType::TypeChar:
+ case JavaType::TypeShort:
+ case JavaType::TypeInt:
+ case JavaType::TypeLong:
+ case JavaType::TypeFloat:
+ case JavaType::TypeDouble: {
+ // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec
+ // requires converting to 0 or 1.
+ jvalue null_value = {0};
+ result = null_value;
+ break;
+ }
+ case JavaType::TypeArray:
+ // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
+ // requires raising a JavaScript exception.
+ result.l = NULL;
+ break;
+ case JavaType::TypeVoid:
+ // Conversion to void must never happen.
+ NOTREACHED();
+ break;
+ }
+ return result;
+}
+
+jvalue CoerceJavaScriptStringToJavaValue(JNIEnv* env,
+ const base::Value* value,
+ const JavaType& target_type) {
+ // See http://jdk6.java.net/plugin2/liveconnect/#JS_STRING_VALUES.
+ jvalue result;
+ switch (target_type.type) {
+ case JavaType::TypeString: {
+ std::string string_result;
+ value->GetAsString(&string_result);
+ result.l = ConvertUTF8ToJavaString(env, string_result).Release();
+ break;
+ }
+ case JavaType::TypeObject:
+ // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
+ // requires handling java.lang.Object.
+ result.l = NULL;
+ break;
+ case JavaType::TypeByte:
+ case JavaType::TypeShort:
+ case JavaType::TypeInt:
+ case JavaType::TypeLong:
+ case JavaType::TypeFloat:
+ case JavaType::TypeDouble: {
+ // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec
+ // requires using valueOf() method of corresponding object type.
+ jvalue null_value = {0};
+ result = null_value;
+ break;
+ }
+ case JavaType::TypeChar:
+ // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec
+ // requires using java.lang.Short.decode().
+ result.c = 0;
+ break;
+ case JavaType::TypeBoolean:
+ // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec
+ // requires converting the empty string to false, otherwise true.
+ result.z = JNI_FALSE;
+ break;
+ case JavaType::TypeArray:
+ // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
+ // requires raising a JavaScript exception.
+ result.l = NULL;
+ break;
+ case JavaType::TypeVoid:
+ // Conversion to void must never happen.
+ NOTREACHED();
+ break;
+ }
+ return result;
+}
+
+// Note that this only handles primitive types and strings.
+jobject CreateJavaArray(JNIEnv* env, const JavaType& type, jsize length) {
+ switch (type.type) {
+ case JavaType::TypeBoolean:
+ return env->NewBooleanArray(length);
+ case JavaType::TypeByte:
+ return env->NewByteArray(length);
+ case JavaType::TypeChar:
+ return env->NewCharArray(length);
+ case JavaType::TypeShort:
+ return env->NewShortArray(length);
+ case JavaType::TypeInt:
+ return env->NewIntArray(length);
+ case JavaType::TypeLong:
+ return env->NewLongArray(length);
+ case JavaType::TypeFloat:
+ return env->NewFloatArray(length);
+ case JavaType::TypeDouble:
+ return env->NewDoubleArray(length);
+ case JavaType::TypeString: {
+ base::android::ScopedJavaLocalRef<jclass> clazz(
+ base::android::GetClass(env, kJavaLangString));
+ return env->NewObjectArray(length, clazz.obj(), NULL);
+ }
+ case JavaType::TypeVoid:
+ // Conversion to void must never happen.
+ case JavaType::TypeArray:
+ case JavaType::TypeObject:
+ // Not handled.
+ NOTREACHED();
+ }
+ return NULL;
+}
+
+// Sets the specified element of the supplied array to the value of the
+// supplied jvalue. Requires that the type of the array matches that of the
+// jvalue. Handles only primitive types and strings. Note that in the case of a
+// string, the array takes a new reference to the string object.
+void SetArrayElement(JNIEnv* env,
+ jobject array,
+ const JavaType& type,
+ jsize index,
+ const jvalue& value) {
+ switch (type.type) {
+ case JavaType::TypeBoolean:
+ env->SetBooleanArrayRegion(static_cast<jbooleanArray>(array), index, 1,
+ &value.z);
+ break;
+ case JavaType::TypeByte:
+ env->SetByteArrayRegion(static_cast<jbyteArray>(array), index, 1,
+ &value.b);
+ break;
+ case JavaType::TypeChar:
+ env->SetCharArrayRegion(static_cast<jcharArray>(array), index, 1,
+ &value.c);
+ break;
+ case JavaType::TypeShort:
+ env->SetShortArrayRegion(static_cast<jshortArray>(array), index, 1,
+ &value.s);
+ break;
+ case JavaType::TypeInt:
+ env->SetIntArrayRegion(static_cast<jintArray>(array), index, 1,
+ &value.i);
+ break;
+ case JavaType::TypeLong:
+ env->SetLongArrayRegion(static_cast<jlongArray>(array), index, 1,
+ &value.j);
+ break;
+ case JavaType::TypeFloat:
+ env->SetFloatArrayRegion(static_cast<jfloatArray>(array), index, 1,
+ &value.f);
+ break;
+ case JavaType::TypeDouble:
+ env->SetDoubleArrayRegion(static_cast<jdoubleArray>(array), index, 1,
+ &value.d);
+ break;
+ case JavaType::TypeString:
+ env->SetObjectArrayElement(static_cast<jobjectArray>(array), index,
+ value.l);
+ break;
+ case JavaType::TypeVoid:
+ // Conversion to void must never happen.
+ case JavaType::TypeArray:
+ case JavaType::TypeObject:
+ // Not handled.
+ NOTREACHED();
+ }
+ base::android::CheckException(env);
+}
+
+jvalue CoerceJavaScriptNullOrUndefinedToJavaValue(JNIEnv* env,
+ const base::Value* value,
+ const JavaType& target_type,
+ bool coerce_to_string) {
+ bool is_undefined = false;
+ scoped_ptr<const GinJavaBridgeValue> gin_value;
+ if (GinJavaBridgeValue::ContainsGinJavaBridgeValue(value)) {
+ gin_value = GinJavaBridgeValue::FromValue(value);
+ if (gin_value->IsType(GinJavaBridgeValue::TYPE_UNDEFINED)) {
+ is_undefined = true;
+ }
+ }
+ jvalue result;
+ switch (target_type.type) {
+ case JavaType::TypeObject:
+ result.l = NULL;
+ break;
+ case JavaType::TypeString:
+ // LIVECONNECT_COMPLIANCE: Existing behavior is to convert undefined to
+ // "undefined". Spec requires converting undefined to NULL.
+ result.l = (coerce_to_string && is_undefined)
+ ? ConvertUTF8ToJavaString(env, kUndefined).Release()
+ : NULL;
+ break;
+ case JavaType::TypeByte:
+ case JavaType::TypeChar:
+ case JavaType::TypeShort:
+ case JavaType::TypeInt:
+ case JavaType::TypeLong:
+ case JavaType::TypeFloat:
+ case JavaType::TypeDouble: {
+ jvalue null_value = {0};
+ result = null_value;
+ break;
+ }
+ case JavaType::TypeBoolean:
+ result.z = JNI_FALSE;
+ break;
+ case JavaType::TypeArray:
+ // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
+ // requires raising a JavaScript exception.
+ result.l = NULL;
+ break;
+ case JavaType::TypeVoid:
+ // Conversion to void must never happen.
+ NOTREACHED();
+ break;
+ }
+ return result;
+}
+
+jobject CoerceJavaScriptListToArray(JNIEnv* env,
+ const base::Value* value,
+ const JavaType& target_type,
+ const ObjectRefs& object_refs) {
+ DCHECK_EQ(JavaType::TypeArray, target_type.type);
+ const JavaType& target_inner_type = *target_type.inner_type.get();
+ // LIVECONNECT_COMPLIANCE: Existing behavior is to return null for
+ // multi-dimensional arrays. Spec requires handling multi-demensional arrays.
+ if (target_inner_type.type == JavaType::TypeArray) {
+ return NULL;
+ }
+
+ // LIVECONNECT_COMPLIANCE: Existing behavior is to return null for object
+ // arrays. Spec requires handling object arrays.
+ if (target_inner_type.type == JavaType::TypeObject) {
+ return NULL;
+ }
+
+ const base::ListValue* list_value;
+ value->GetAsList(&list_value);
+ // Create the Java array.
+ jsize length = static_cast<jsize>(list_value->GetSize());
+ jobject result = CreateJavaArray(env, target_inner_type, length);
+ if (!result) {
+ return NULL;
+ }
+ scoped_ptr<base::Value> null_value(base::Value::CreateNullValue());
+ for (jsize i = 0; i < length; ++i) {
+ const base::Value* value_element = null_value.get();
+ list_value->Get(i, &value_element);
+ jvalue element = CoerceJavaScriptValueToJavaValue(
+ env, value_element, target_inner_type, false, object_refs);
+ SetArrayElement(env, result, target_inner_type, i, element);
+ // CoerceJavaScriptValueToJavaValue() creates new local references to
+ // strings, objects and arrays. Of these, only strings can occur here.
+ // SetArrayElement() causes the array to take its own reference to the
+ // string, so we can now release the local reference.
+ DCHECK_NE(JavaType::TypeObject, target_inner_type.type);
+ DCHECK_NE(JavaType::TypeArray, target_inner_type.type);
+ ReleaseJavaValueIfRequired(env, &element, target_inner_type);
+ }
+
+ return result;
+}
+
+jobject CoerceJavaScriptDictionaryToArray(JNIEnv* env,
+ const base::Value* value,
+ const JavaType& target_type,
+ const ObjectRefs& object_refs) {
+ DCHECK_EQ(JavaType::TypeArray, target_type.type);
+
+ const JavaType& target_inner_type = *target_type.inner_type.get();
+ // LIVECONNECT_COMPLIANCE: Existing behavior is to return null for
+ // multi-dimensional arrays. Spec requires handling multi-demensional arrays.
+ if (target_inner_type.type == JavaType::TypeArray) {
+ return NULL;
+ }
+
+ // LIVECONNECT_COMPLIANCE: Existing behavior is to return null for object
+ // arrays. Spec requires handling object arrays.
+ if (target_inner_type.type == JavaType::TypeObject) {
+ return NULL;
+ }
+
+ const base::DictionaryValue* dictionary_value;
+ value->GetAsDictionary(&dictionary_value);
+ const base::Value* length_value;
+ // If the object does not have a length property, return null.
+ if (!dictionary_value->Get("length", &length_value)) {
+ return NULL;
+ }
+
+ // If the length property does not have numeric type, or is outside the valid
+ // range for a Java array length, return null.
+ jsize length = -1;
+ if (length_value->IsType(base::Value::TYPE_INTEGER)) {
+ int int_length;
+ length_value->GetAsInteger(&int_length);
+ if (int_length >= 0 && int_length <= kint32max) {
+ length = static_cast<jsize>(int_length);
+ }
+ } else if (length_value->IsType(base::Value::TYPE_DOUBLE)) {
+ double double_length;
+ length_value->GetAsDouble(&double_length);
+ if (double_length >= 0.0 && double_length <= kint32max) {
+ length = static_cast<jsize>(double_length);
+ }
+ }
+ if (length == -1) {
+ return NULL;
+ }
+
+ jobject result = CreateJavaArray(env, target_inner_type, length);
+ if (!result) {
+ return NULL;
+ }
+ scoped_ptr<base::Value> null_value(base::Value::CreateNullValue());
+ for (jsize i = 0; i < length; ++i) {
+ const std::string key(base::IntToString(i));
+ const base::Value* value_element = null_value.get();
+ if (dictionary_value->HasKey(key)) {
+ dictionary_value->Get(key, &value_element);
+ }
+ jvalue element = CoerceJavaScriptValueToJavaValue(
+ env, value_element, target_inner_type, false, object_refs);
+ SetArrayElement(env, result, target_inner_type, i, element);
+ // CoerceJavaScriptValueToJavaValue() creates new local references to
+ // strings, objects and arrays. Of these, only strings can occur here.
+ // SetArrayElement() causes the array to take its own reference to the
+ // string, so we can now release the local reference.
+ DCHECK_NE(JavaType::TypeObject, target_inner_type.type);
+ DCHECK_NE(JavaType::TypeArray, target_inner_type.type);
+ ReleaseJavaValueIfRequired(env, &element, target_inner_type);
+ }
+
+ return result;
+}
+
+jvalue CoerceJavaScriptObjectToJavaValue(JNIEnv* env,
+ const base::Value* value,
+ const JavaType& target_type,
+ bool coerce_to_string,
+ const ObjectRefs& object_refs) {
+ // This covers both JavaScript objects (including arrays) and Java objects.
+ // See http://jdk6.java.net/plugin2/liveconnect/#JS_OTHER_OBJECTS,
+ // http://jdk6.java.net/plugin2/liveconnect/#JS_ARRAY_VALUES and
+ // http://jdk6.java.net/plugin2/liveconnect/#JS_JAVA_OBJECTS
+ jvalue result;
+ switch (target_type.type) {
+ case JavaType::TypeObject: {
+ if (GinJavaBridgeValue::ContainsGinJavaBridgeValue(value)) {
+ scoped_ptr<const GinJavaBridgeValue> gin_value(
+ GinJavaBridgeValue::FromValue(value));
+ DCHECK(gin_value);
+ DCHECK(gin_value->IsType(GinJavaBridgeValue::TYPE_OBJECT_ID));
+ base::android::ScopedJavaLocalRef<jobject> obj;
+ GinJavaBoundObject::ObjectID object_id;
+ if (gin_value->GetAsObjectID(&object_id)) {
+ ObjectRefs::const_iterator iter = object_refs.find(object_id);
+ if (iter != object_refs.end()) {
+ obj.Reset(iter->second.get(env));
+ }
+ }
+ result.l = obj.Release();
+ } else {
+ // LIVECONNECT_COMPLIANCE: Existing behavior is to pass null. Spec
+ // requires converting if the target type is
+ // netscape.javascript.JSObject, otherwise raising a JavaScript
+ // exception.
+ result.l = NULL;
+ }
+ break;
+ }
+ case JavaType::TypeString:
+ // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to
+ // "undefined". Spec requires calling toString() on the Java object.
+ result.l = coerce_to_string
+ ? ConvertUTF8ToJavaString(env, kUndefined).Release()
+ : NULL;
+ break;
+ case JavaType::TypeByte:
+ case JavaType::TypeShort:
+ case JavaType::TypeInt:
+ case JavaType::TypeLong:
+ case JavaType::TypeFloat:
+ case JavaType::TypeDouble:
+ case JavaType::TypeChar: {
+ // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec
+ // requires raising a JavaScript exception.
+ jvalue null_value = {0};
+ result = null_value;
+ break;
+ }
+ case JavaType::TypeBoolean:
+ // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec
+ // requires raising a JavaScript exception.
+ result.z = JNI_FALSE;
+ break;
+ case JavaType::TypeArray:
+ if (value->IsType(base::Value::TYPE_DICTIONARY)) {
+ result.l = CoerceJavaScriptDictionaryToArray(
+ env, value, target_type, object_refs);
+ } else if (value->IsType(base::Value::TYPE_LIST)) {
+ result.l =
+ CoerceJavaScriptListToArray(env, value, target_type, object_refs);
+ } else {
+ result.l = NULL;
+ }
+ break;
+ case JavaType::TypeVoid:
+ // Conversion to void must never happen.
+ NOTREACHED();
+ break;
+ }
+ return result;
+}
+
+jvalue CoerceGinJavaBridgeValueToJavaValue(JNIEnv* env,
+ const base::Value* value,
+ const JavaType& target_type,
+ bool coerce_to_string,
+ const ObjectRefs& object_refs) {
+ DCHECK(GinJavaBridgeValue::ContainsGinJavaBridgeValue(value));
+ scoped_ptr<const GinJavaBridgeValue> gin_value(
+ GinJavaBridgeValue::FromValue(value));
+ switch (gin_value->GetType()) {
+ case GinJavaBridgeValue::TYPE_UNDEFINED:
+ return CoerceJavaScriptNullOrUndefinedToJavaValue(
+ env, value, target_type, coerce_to_string);
+ case GinJavaBridgeValue::TYPE_NONFINITE: {
+ float float_value;
+ gin_value->GetAsNonFinite(&float_value);
+ return CoerceJavaScriptDoubleToJavaValue(
+ env, float_value, target_type, coerce_to_string);
+ }
+ case GinJavaBridgeValue::TYPE_OBJECT_ID:
+ return CoerceJavaScriptObjectToJavaValue(
+ env, value, target_type, coerce_to_string, object_refs);
+ default:
+ NOTREACHED();
+ }
+ return jvalue();
+}
+
+} // namespace
+
+
+void ReleaseJavaValueIfRequired(JNIEnv* env,
+ jvalue* value,
+ const JavaType& type) {
+ if (type.type == JavaType::TypeString || type.type == JavaType::TypeObject ||
+ type.type == JavaType::TypeArray) {
+ env->DeleteLocalRef(value->l);
+ value->l = NULL;
+ }
+}
+
+jvalue CoerceJavaScriptValueToJavaValue(JNIEnv* env,
+ const base::Value* value,
+ const JavaType& target_type,
+ bool coerce_to_string,
+ const ObjectRefs& object_refs) {
+ // Note that in all these conversions, the relevant field of the jvalue must
+ // always be explicitly set, as jvalue does not initialize its fields.
+
+ switch (value->GetType()) {
+ case base::Value::TYPE_INTEGER:
+ return CoerceJavaScriptIntegerToJavaValue(
+ env, value, target_type, coerce_to_string);
+ case base::Value::TYPE_DOUBLE: {
+ double double_value;
+ value->GetAsDouble(&double_value);
+ return CoerceJavaScriptDoubleToJavaValue(
+ env, double_value, target_type, coerce_to_string);
+ }
+ case base::Value::TYPE_BOOLEAN:
+ return CoerceJavaScriptBooleanToJavaValue(
+ env, value, target_type, coerce_to_string);
+ case base::Value::TYPE_STRING:
+ return CoerceJavaScriptStringToJavaValue(env, value, target_type);
+ case base::Value::TYPE_DICTIONARY:
+ case base::Value::TYPE_LIST:
+ return CoerceJavaScriptObjectToJavaValue(
+ env, value, target_type, coerce_to_string, object_refs);
+ case base::Value::TYPE_NULL:
+ return CoerceJavaScriptNullOrUndefinedToJavaValue(
+ env, value, target_type, coerce_to_string);
+ case base::Value::TYPE_BINARY:
+ return CoerceGinJavaBridgeValueToJavaValue(
+ env, value, target_type, coerce_to_string, object_refs);
+ }
+ NOTREACHED();
+ return jvalue();
+}
+
+} // namespace content
diff --git a/chromium/content/browser/renderer_host/java/gin_java_script_to_java_types_coercion.h b/chromium/content/browser/renderer_host/java/gin_java_script_to_java_types_coercion.h
new file mode 100644
index 00000000000..1df7345ecc6
--- /dev/null
+++ b/chromium/content/browser/renderer_host/java/gin_java_script_to_java_types_coercion.h
@@ -0,0 +1,33 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_JAVA_GIN_JAVA_SCRIPT_TO_JAVA_TYPES_COERCION_H_
+#define CONTENT_BROWSER_RENDERER_HOST_JAVA_GIN_JAVA_SCRIPT_TO_JAVA_TYPES_COERCION_H_
+
+#include <map>
+
+#include "base/android/jni_weak_ref.h"
+#include "base/values.h"
+#include "content/browser/renderer_host/java/gin_java_bound_object.h"
+#include "content/browser/renderer_host/java/java_type.h"
+
+namespace content {
+
+typedef std::map<GinJavaBoundObject::ObjectID, JavaObjectWeakGlobalRef>
+ ObjectRefs;
+
+jvalue CoerceJavaScriptValueToJavaValue(
+ JNIEnv* env,
+ const base::Value* value,
+ const JavaType& target_type,
+ bool coerce_to_string,
+ const ObjectRefs& object_refs);
+
+void ReleaseJavaValueIfRequired(JNIEnv* env,
+ jvalue* value,
+ const JavaType& type);
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_JAVA_GIN_JAVA_SCRIPT_TO_JAVA_TYPES_COERCION_H_
diff --git a/chromium/content/browser/renderer_host/java/java_bound_object.cc b/chromium/content/browser/renderer_host/java/java_bound_object.cc
index e2ad87a15c3..f812c429683 100644
--- a/chromium/content/browser/renderer_host/java/java_bound_object.cc
+++ b/chromium/content/browser/renderer_host/java/java_bound_object.cc
@@ -7,6 +7,7 @@
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/memory/singleton.h"
+#include "base/numerics/safe_conversions.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.h"
@@ -38,6 +39,7 @@ namespace {
const char kJavaLangClass[] = "java/lang/Class";
const char kJavaLangObject[] = "java/lang/Object";
const char kJavaLangReflectMethod[] = "java/lang/reflect/Method";
+const char kJavaLangSecurityExceptionClass[] = "java/lang/SecurityException";
const char kGetClass[] = "getClass";
const char kGetMethods[] = "getMethods";
const char kIsAnnotationPresent[] = "isAnnotationPresent";
@@ -45,6 +47,9 @@ const char kReturningJavaLangClass[] = "()Ljava/lang/Class;";
const char kReturningJavaLangReflectMethodArray[] =
"()[Ljava/lang/reflect/Method;";
const char kTakesJavaLangClassReturningBoolean[] = "(Ljava/lang/Class;)Z";
+// This is an exception message, so no need to localize.
+const char kAccessToObjectGetClassIsBlocked[] =
+ "Access to java.lang.Object.getClass is blocked";
// Our special NPObject type. We extend an NPObject with a pointer to a
// JavaBoundObject. We also add static methods for each of the NPObject
@@ -64,6 +69,8 @@ struct JavaNPObject : public NPObject {
static bool HasProperty(NPObject* np_object, NPIdentifier np_identifier);
static bool GetProperty(NPObject* np_object, NPIdentifier np_identifier,
NPVariant *result);
+ static bool Enumerate(NPObject* object, NPIdentifier** values,
+ uint32_t* count);
};
const NPClass JavaNPObject::kNPClass = {
@@ -78,6 +85,8 @@ const NPClass JavaNPObject::kNPClass = {
JavaNPObject::GetProperty,
NULL, // NPSetProperty,
NULL, // NPRemoveProperty
+ JavaNPObject::Enumerate,
+ NULL,
};
NPObject* JavaNPObject::Allocate(NPP npp, NPClass* np_class) {
@@ -120,6 +129,19 @@ bool JavaNPObject::GetProperty(NPObject* np_object,
return false;
}
+bool JavaNPObject::Enumerate(NPObject* np_object, NPIdentifier** values,
+ uint32_t* count) {
+ JavaNPObject* obj = reinterpret_cast<JavaNPObject*>(np_object);
+ if (!obj->bound_object->CanEnumerateMethods()) return false;
+ std::vector<std::string> method_names = obj->bound_object->GetMethodNames();
+ *count = base::saturated_cast<uint32_t>(method_names.size());
+ *values = static_cast<NPIdentifier*>(calloc(*count, sizeof(NPIdentifier)));
+ for (uint32_t i = 0; i < *count; ++i) {
+ (*values)[i] = WebBindings::getStringIdentifier(method_names[i].c_str());
+ }
+ return true;
+}
+
// Calls a Java method through JNI. If the Java method raises an uncaught
// exception, it is cleared and this method returns false. Otherwise, this
// method returns true and the Java method's return value is provided as an
@@ -127,45 +149,70 @@ bool JavaNPObject::GetProperty(NPObject* np_object,
// return value is simply converted to the corresponding NPAPI type.
bool CallJNIMethod(
jobject object,
+ jclass clazz,
const JavaType& return_type,
jmethodID id,
jvalue* parameters,
NPVariant* result,
const JavaRef<jclass>& safe_annotation_clazz,
- const base::WeakPtr<JavaBridgeDispatcherHostManager>& manager) {
+ const base::WeakPtr<JavaBridgeDispatcherHostManager>& manager,
+ bool can_enumerate_methods) {
+ DCHECK(object || clazz);
JNIEnv* env = AttachCurrentThread();
switch (return_type.type) {
case JavaType::TypeBoolean:
- BOOLEAN_TO_NPVARIANT(env->CallBooleanMethodA(object, id, parameters),
- *result);
+ BOOLEAN_TO_NPVARIANT(
+ object ? env->CallBooleanMethodA(object, id, parameters)
+ : env->CallStaticBooleanMethodA(clazz, id, parameters),
+ *result);
break;
case JavaType::TypeByte:
- INT32_TO_NPVARIANT(env->CallByteMethodA(object, id, parameters), *result);
+ INT32_TO_NPVARIANT(
+ object ? env->CallByteMethodA(object, id, parameters)
+ : env->CallStaticByteMethodA(clazz, id, parameters),
+ *result);
break;
case JavaType::TypeChar:
- INT32_TO_NPVARIANT(env->CallCharMethodA(object, id, parameters), *result);
+ INT32_TO_NPVARIANT(
+ object ? env->CallCharMethodA(object, id, parameters)
+ : env->CallStaticCharMethodA(clazz, id, parameters),
+ *result);
break;
case JavaType::TypeShort:
- INT32_TO_NPVARIANT(env->CallShortMethodA(object, id, parameters),
- *result);
+ INT32_TO_NPVARIANT(
+ object ? env->CallShortMethodA(object, id, parameters)
+ : env->CallStaticShortMethodA(clazz, id, parameters),
+ *result);
break;
case JavaType::TypeInt:
- INT32_TO_NPVARIANT(env->CallIntMethodA(object, id, parameters), *result);
+ INT32_TO_NPVARIANT(object
+ ? env->CallIntMethodA(object, id, parameters)
+ : env->CallStaticIntMethodA(clazz, id, parameters),
+ *result);
break;
case JavaType::TypeLong:
- DOUBLE_TO_NPVARIANT(env->CallLongMethodA(object, id, parameters),
- *result);
+ DOUBLE_TO_NPVARIANT(
+ object ? env->CallLongMethodA(object, id, parameters)
+ : env->CallStaticLongMethodA(clazz, id, parameters),
+ *result);
break;
case JavaType::TypeFloat:
- DOUBLE_TO_NPVARIANT(env->CallFloatMethodA(object, id, parameters),
- *result);
+ DOUBLE_TO_NPVARIANT(
+ object ? env->CallFloatMethodA(object, id, parameters)
+ : env->CallStaticFloatMethodA(clazz, id, parameters),
+ *result);
break;
case JavaType::TypeDouble:
- DOUBLE_TO_NPVARIANT(env->CallDoubleMethodA(object, id, parameters),
- *result);
+ DOUBLE_TO_NPVARIANT(
+ object ? env->CallDoubleMethodA(object, id, parameters)
+ : env->CallStaticDoubleMethodA(clazz, id, parameters),
+ *result);
break;
case JavaType::TypeVoid:
- env->CallVoidMethodA(object, id, parameters);
+ if (object)
+ env->CallVoidMethodA(object, id, parameters);
+ else
+ env->CallStaticVoidMethodA(clazz, id, parameters);
VOID_TO_NPVARIANT(*result);
break;
case JavaType::TypeArray:
@@ -176,7 +223,8 @@ bool CallJNIMethod(
break;
case JavaType::TypeString: {
jstring java_string = static_cast<jstring>(
- env->CallObjectMethodA(object, id, parameters));
+ object ? env->CallObjectMethodA(object, id, parameters)
+ : env->CallStaticObjectMethodA(clazz, id, parameters));
// If an exception was raised, we must clear it before calling most JNI
// methods. ScopedJavaLocalRef is liable to make such calls, so we test
// first.
@@ -204,7 +252,9 @@ bool CallJNIMethod(
// If an exception was raised, we must clear it before calling most JNI
// methods. ScopedJavaLocalRef is liable to make such calls, so we test
// first.
- jobject java_object = env->CallObjectMethodA(object, id, parameters);
+ jobject java_object =
+ object ? env->CallObjectMethodA(object, id, parameters)
+ : env->CallStaticObjectMethodA(clazz, id, parameters);
if (base::android::ClearException(env)) {
return false;
}
@@ -215,7 +265,8 @@ bool CallJNIMethod(
}
OBJECT_TO_NPVARIANT(JavaBoundObject::Create(scoped_java_object,
safe_annotation_clazz,
- manager),
+ manager,
+ can_enumerate_methods),
*result);
break;
}
@@ -778,7 +829,8 @@ jvalue CoerceJavaScriptValueToJavaValue(const NPVariant& variant,
NPObject* JavaBoundObject::Create(
const JavaRef<jobject>& object,
const JavaRef<jclass>& safe_annotation_clazz,
- const base::WeakPtr<JavaBridgeDispatcherHostManager>& manager) {
+ const base::WeakPtr<JavaBridgeDispatcherHostManager>& manager,
+ bool can_enumerate_methods) {
// The first argument (a plugin's instance handle) is passed through to the
// allocate function directly, and we don't use it, so it's ok to be 0.
// The object is created with a ref count of one.
@@ -786,17 +838,21 @@ NPObject* JavaBoundObject::Create(
&JavaNPObject::kNPClass));
// The NPObject takes ownership of the JavaBoundObject.
reinterpret_cast<JavaNPObject*>(np_object)->bound_object =
- new JavaBoundObject(object, safe_annotation_clazz, manager);
+ new JavaBoundObject(
+ object, safe_annotation_clazz, manager, can_enumerate_methods);
return np_object;
}
JavaBoundObject::JavaBoundObject(
const JavaRef<jobject>& object,
const JavaRef<jclass>& safe_annotation_clazz,
- const base::WeakPtr<JavaBridgeDispatcherHostManager>& manager)
+ const base::WeakPtr<JavaBridgeDispatcherHostManager>& manager,
+ bool can_enumerate_methods)
: java_object_(AttachCurrentThread(), object.obj()),
manager_(manager),
are_methods_set_up_(false),
+ object_get_class_method_id_(NULL),
+ can_enumerate_methods_(can_enumerate_methods),
safe_annotation_clazz_(safe_annotation_clazz) {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
@@ -824,6 +880,17 @@ ScopedJavaLocalRef<jobject> JavaBoundObject::GetJavaObject(NPObject* object) {
return jbo->java_object_.get(AttachCurrentThread());
}
+std::vector<std::string> JavaBoundObject::GetMethodNames() const {
+ EnsureMethodsAreSetUp();
+ std::vector<std::string> result;
+ for (JavaMethodMap::const_iterator it = methods_.begin();
+ it != methods_.end();
+ it = methods_.upper_bound(it->first)) {
+ result.push_back(it->first);
+ }
+ return result;
+}
+
bool JavaBoundObject::HasMethod(const std::string& name) const {
EnsureMethodsAreSetUp();
return methods_.find(name) != methods_.end();
@@ -853,6 +920,16 @@ bool JavaBoundObject::Invoke(const std::string& name, const NPVariant* args,
return false;
}
+ // Block access to java.lang.Object.getClass.
+ // As it is declared to be final, it is sufficient to compare methodIDs.
+ if (method->id() == object_get_class_method_id_) {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&JavaBoundObject::ThrowSecurityException,
+ kAccessToObjectGetClassIsBlocked));
+ return false;
+ }
+
// Coerce
std::vector<jvalue> parameters(arg_count);
for (size_t i = 0; i < arg_count; ++i) {
@@ -861,20 +938,27 @@ bool JavaBoundObject::Invoke(const std::string& name, const NPVariant* args,
true);
}
- ScopedJavaLocalRef<jobject> obj = java_object_.get(AttachCurrentThread());
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj;
+ ScopedJavaLocalRef<jclass> cls;
bool ok = false;
- if (!obj.is_null()) {
+ if (method->is_static()) {
+ cls = GetLocalClassRef(env);
+ } else {
+ obj = java_object_.get(env);
+ }
+ if (!obj.is_null() || !cls.is_null()) {
// Call
- ok = CallJNIMethod(obj.obj(), method->return_type(),
+ ok = CallJNIMethod(obj.obj(), cls.obj(), method->return_type(),
method->id(), &parameters[0], result,
safe_annotation_clazz_,
- manager_);
+ manager_,
+ can_enumerate_methods_);
}
// Now that we're done with the jvalue, release any local references created
// by CoerceJavaScriptValueToJavaValue().
- JNIEnv* env = AttachCurrentThread();
for (size_t i = 0; i < arg_count; ++i) {
ReleaseJavaValueIfRequired(env, &parameters[i], method->parameter_type(i));
}
@@ -882,25 +966,34 @@ bool JavaBoundObject::Invoke(const std::string& name, const NPVariant* args,
return ok;
}
+ScopedJavaLocalRef<jclass> JavaBoundObject::GetLocalClassRef(
+ JNIEnv* env) const {
+ if (!object_get_class_method_id_) {
+ object_get_class_method_id_ = GetMethodIDFromClassName(
+ env, kJavaLangObject, kGetClass, kReturningJavaLangClass);
+ }
+
+ ScopedJavaLocalRef<jobject> obj = java_object_.get(env);
+ if (!obj.is_null()) {
+ return ScopedJavaLocalRef<jclass>(env, static_cast<jclass>(
+ env->CallObjectMethod(obj.obj(), object_get_class_method_id_)));
+ } else {
+ return ScopedJavaLocalRef<jclass>();
+ }
+}
+
void JavaBoundObject::EnsureMethodsAreSetUp() const {
if (are_methods_set_up_)
return;
are_methods_set_up_ = true;
JNIEnv* env = AttachCurrentThread();
- ScopedJavaLocalRef<jobject> obj = java_object_.get(env);
- if (obj.is_null()) {
+ ScopedJavaLocalRef<jclass> clazz = GetLocalClassRef(env);
+ if (clazz.is_null()) {
return;
}
- ScopedJavaLocalRef<jclass> clazz(env, static_cast<jclass>(
- env->CallObjectMethod(obj.obj(), GetMethodIDFromClassName(
- env,
- kJavaLangObject,
- kGetClass,
- kReturningJavaLangClass))));
-
ScopedJavaLocalRef<jobjectArray> methods(env, static_cast<jobjectArray>(
env->CallObjectMethod(clazz.obj(), GetMethodIDFromClassName(
env,
@@ -935,4 +1028,13 @@ void JavaBoundObject::EnsureMethodsAreSetUp() const {
}
}
+// static
+void JavaBoundObject::ThrowSecurityException(const char* message) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ JNIEnv* env = AttachCurrentThread();
+ base::android::ScopedJavaLocalRef<jclass> clazz(
+ env, env->FindClass(kJavaLangSecurityExceptionClass));
+ env->ThrowNew(clazz.obj(), message);
+}
+
} // namespace content
diff --git a/chromium/content/browser/renderer_host/java/java_bound_object.h b/chromium/content/browser/renderer_host/java/java_bound_object.h
index ff97fdb2b6c..f29aed6df1b 100644
--- a/chromium/content/browser/renderer_host/java/java_bound_object.h
+++ b/chromium/content/browser/renderer_host/java/java_bound_object.h
@@ -9,7 +9,7 @@
#include <map>
#include <string>
-#include "base/android/jni_helper.h"
+#include "base/android/jni_weak_ref.h"
#include "base/android/scoped_java_ref.h"
#include "base/memory/linked_ptr.h"
#include "base/memory/weak_ptr.h"
@@ -29,17 +29,16 @@ class JavaBridgeDispatcherHostManager;
// created and destroyed on different threads.
class JavaBoundObject {
public:
- // Takes a Java object and creates a JavaBoundObject around it. The
- // |require_annotation| flag specifies whether or not only methods with the
- // JavascriptInterface annotation are exposed to JavaScript. This property
- // propagates to all Objects that get implicitly exposed as return values as
- // well. Returns an NPObject with a ref count of one which owns the
+ // Takes a Java object and creates a JavaBoundObject around it. If
+ // |safe_annotation_clazz| annotation is non-null, the method is exposed
+ // to JavaScript. Returns an NPObject with a ref count of one which owns the
// JavaBoundObject.
// See also comment below for |manager_|.
static NPObject* Create(
const base::android::JavaRef<jobject>& object,
const base::android::JavaRef<jclass>& safe_annotation_clazz,
- const base::WeakPtr<JavaBridgeDispatcherHostManager>& manager);
+ const base::WeakPtr<JavaBridgeDispatcherHostManager>& manager,
+ bool can_enumerate_methods);
virtual ~JavaBoundObject();
@@ -50,17 +49,23 @@ class JavaBoundObject {
NPObject* object);
// Methods to implement the NPObject callbacks.
+ bool CanEnumerateMethods() const { return can_enumerate_methods_; }
+ std::vector<std::string> GetMethodNames() const;
bool HasMethod(const std::string& name) const;
bool Invoke(const std::string& name, const NPVariant* args, size_t arg_count,
NPVariant* result);
private:
- explicit JavaBoundObject(
+ JavaBoundObject(
const base::android::JavaRef<jobject>& object,
const base::android::JavaRef<jclass>& safe_annotation_clazz,
- const base::WeakPtr<JavaBridgeDispatcherHostManager>& manager_);
+ const base::WeakPtr<JavaBridgeDispatcherHostManager>& manager,
+ bool can_enumerate_methods);
void EnsureMethodsAreSetUp() const;
+ base::android::ScopedJavaLocalRef<jclass> GetLocalClassRef(JNIEnv* env) const;
+
+ static void ThrowSecurityException(const char* message);
// The weak ref to the underlying Java object that this JavaBoundObject
// instance represents.
@@ -78,6 +83,8 @@ class JavaBoundObject {
typedef std::multimap<std::string, linked_ptr<JavaMethod> > JavaMethodMap;
mutable JavaMethodMap methods_;
mutable bool are_methods_set_up_;
+ mutable jmethodID object_get_class_method_id_;
+ const bool can_enumerate_methods_;
base::android::ScopedJavaGlobalRef<jclass> safe_annotation_clazz_;
diff --git a/chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host.cc b/chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host.cc
index 562517a1140..922b03a55e8 100644
--- a/chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host.cc
+++ b/chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host.cc
@@ -8,12 +8,12 @@
#include "base/bind.h"
#include "base/lazy_instance.h"
#include "content/browser/renderer_host/java/java_bridge_channel_host.h"
-#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/child/child_process.h"
#include "content/child/npapi/npobject_stub.h"
#include "content/child/npapi/npobject_util.h" // For CreateNPVariantParam()
#include "content/common/java_bridge_messages.h"
#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "third_party/WebKit/public/web/WebBindings.h"
@@ -49,8 +49,8 @@ base::LazyInstance<JavaBridgeThread> g_background_thread =
} // namespace
JavaBridgeDispatcherHost::JavaBridgeDispatcherHost(
- RenderViewHost* render_view_host)
- : render_view_host_(render_view_host) {
+ RenderFrameHost* render_frame_host)
+ : render_frame_host_(render_frame_host) {
}
JavaBridgeDispatcherHost::~JavaBridgeDispatcherHost() {
@@ -65,7 +65,7 @@ void JavaBridgeDispatcherHost::AddNamedObject(const base::string16& name,
CreateNPVariantParam(object, &variant_param);
Send(new JavaBridgeMsg_AddNamedObject(
- render_view_host_->GetRoutingID(), name, variant_param));
+ render_frame_host_->GetRoutingID(), name, variant_param));
}
void JavaBridgeDispatcherHost::RemoveNamedObject(const base::string16& name) {
@@ -75,11 +75,11 @@ void JavaBridgeDispatcherHost::RemoveNamedObject(const base::string16& name) {
// the NPObjectStub to be deleted, which will drop its reference to the
// original NPObject.
Send(new JavaBridgeMsg_RemoveNamedObject(
- render_view_host_->GetRoutingID(), name));
+ render_frame_host_->GetRoutingID(), name));
}
-void JavaBridgeDispatcherHost::RenderViewDeleted() {
- render_view_host_ = NULL;
+void JavaBridgeDispatcherHost::RenderFrameDeleted() {
+ render_frame_host_ = NULL;
}
void JavaBridgeDispatcherHost::OnGetChannelHandle(IPC::Message* reply_msg) {
@@ -89,8 +89,8 @@ void JavaBridgeDispatcherHost::OnGetChannelHandle(IPC::Message* reply_msg) {
}
void JavaBridgeDispatcherHost::Send(IPC::Message* msg) {
- if (render_view_host_) {
- render_view_host_->Send(msg);
+ if (render_frame_host_) {
+ render_frame_host_->Send(msg);
return;
}
@@ -132,7 +132,7 @@ void JavaBridgeDispatcherHost::CreateNPVariantParam(NPObject* object,
g_background_thread.Get().message_loop()->PostTask(
FROM_HERE,
base::Bind(&JavaBridgeDispatcherHost::CreateObjectStub, this, object,
- render_view_host_->GetProcess()->GetID(), route_id));
+ render_frame_host_->GetProcess()->GetID(), route_id));
}
void JavaBridgeDispatcherHost::CreateObjectStub(NPObject* object,
diff --git a/chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host.h b/chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host.h
index 21e47c0d3b7..a656f07afb0 100644
--- a/chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host.h
+++ b/chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host.h
@@ -20,18 +20,18 @@ class Message;
namespace content {
class NPChannelBase;
-class RenderViewHost;
+class RenderFrameHost;
struct NPVariant_Param;
-// This class handles injecting Java objects into a single RenderView. The Java
+// This class handles injecting Java objects into a single RenderFrame. The Java
// object itself lives in the browser process on a background thread, while a
// proxy object is created in the renderer. An instance of this class exists
// for each RenderViewHost.
class JavaBridgeDispatcherHost
: public base::RefCountedThreadSafe<JavaBridgeDispatcherHost> {
public:
- // We hold a weak pointer to the RenderViewhost. It must outlive this object.
- explicit JavaBridgeDispatcherHost(RenderViewHost* render_view_host);
+ // We hold a weak pointer to the RenderFrameHost. It must outlive this object.
+ explicit JavaBridgeDispatcherHost(RenderFrameHost* render_frame_host);
// Injects |object| into the main frame of the corresponding RenderView. A
// proxy object is created in the renderer and when the main frame's window
@@ -45,23 +45,22 @@ class JavaBridgeDispatcherHost
void AddNamedObject(const base::string16& name, NPObject* object);
void RemoveNamedObject(const base::string16& name);
- // Since this object is ref-counted, it might outlive render_view_host_.
- void RenderViewDeleted();
+ // Since this object is ref-counted, it might outlive render_frame_host.
+ void RenderFrameDeleted();
void OnGetChannelHandle(IPC::Message* reply_msg);
+ void Send(IPC::Message* msg);
private:
friend class base::RefCountedThreadSafe<JavaBridgeDispatcherHost>;
virtual ~JavaBridgeDispatcherHost();
- void Send(IPC::Message* msg);
-
void GetChannelHandle(IPC::Message* reply_msg);
void CreateNPVariantParam(NPObject* object, NPVariant_Param* param);
void CreateObjectStub(NPObject* object, int render_process_id, int route_id);
scoped_refptr<NPChannelBase> channel_;
- RenderViewHost* render_view_host_;
+ RenderFrameHost* render_frame_host_;
std::vector<base::WeakPtr<NPObjectStub> > stubs_;
DISALLOW_COPY_AND_ASSIGN(JavaBridgeDispatcherHost);
diff --git a/chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.cc b/chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.cc
index 02cf4dcc060..635a1d13c2c 100644
--- a/chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.cc
+++ b/chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.cc
@@ -5,7 +5,7 @@
#include "content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.h"
#include "base/android/jni_android.h"
-#include "base/android/jni_helper.h"
+#include "base/android/jni_weak_ref.h"
#include "base/android/scoped_java_ref.h"
#include "base/bind.h"
#include "base/logging.h"
@@ -13,14 +13,21 @@
#include "content/browser/renderer_host/java/java_bound_object.h"
#include "content/browser/renderer_host/java/java_bridge_dispatcher_host.h"
#include "content/common/android/hash_set.h"
+#include "content/common/java_bridge_messages.h"
#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_frame_host.h"
#include "third_party/WebKit/public/web/WebBindings.h"
namespace content {
JavaBridgeDispatcherHostManager::JavaBridgeDispatcherHostManager(
- WebContents* web_contents)
- : WebContentsObserver(web_contents) {
+ WebContents* web_contents,
+ jobject retained_object_set)
+ : WebContentsObserver(web_contents),
+ retained_object_set_(base::android::AttachCurrentThread(),
+ retained_object_set),
+ allow_object_contents_inspection_(true) {
+ DCHECK(retained_object_set);
}
JavaBridgeDispatcherHostManager::~JavaBridgeDispatcherHostManager() {
@@ -46,27 +53,6 @@ void JavaBridgeDispatcherHostManager::AddNamedObject(const base::string16& name,
}
}
-void JavaBridgeDispatcherHostManager::SetRetainedObjectSet(
- const JavaObjectWeakGlobalRef& retained_object_set) {
- // It's an error to replace the retained_object_set_ after it's been set,
- // so we check that it hasn't already been here.
- // TODO(benm): It'd be better to pass the set in the constructor to avoid
- // the chance of this happening; but that's tricky as this get's constructed
- // before ContentViewCore (which owns the set). Best solution may be to move
- // ownership of the JavaBridgerDispatchHostManager from WebContents to
- // ContentViewCore?
- JNIEnv* env = base::android::AttachCurrentThread();
- base::android::ScopedJavaLocalRef<jobject> new_retained_object_set =
- retained_object_set.get(env);
- base::android::ScopedJavaLocalRef<jobject> current_retained_object_set =
- retained_object_set_.get(env);
- if (!env->IsSameObject(new_retained_object_set.obj(),
- current_retained_object_set.obj())) {
- DCHECK(current_retained_object_set.is_null());
- retained_object_set_ = retained_object_set;
- }
-}
-
void JavaBridgeDispatcherHostManager::RemoveNamedObject(
const base::string16& name) {
ObjectMap::iterator iter = objects_.find(name);
@@ -83,36 +69,31 @@ void JavaBridgeDispatcherHostManager::RemoveNamedObject(
}
}
-void JavaBridgeDispatcherHostManager::OnGetChannelHandle(
- RenderViewHost* render_view_host, IPC::Message* reply_msg) {
- instances_[render_view_host]->OnGetChannelHandle(reply_msg);
-}
-
-void JavaBridgeDispatcherHostManager::RenderViewCreated(
- RenderViewHost* render_view_host) {
+void JavaBridgeDispatcherHostManager::RenderFrameCreated(
+ RenderFrameHost* render_frame_host) {
// Creates a JavaBridgeDispatcherHost for the specified RenderViewHost and
// adds all currently registered named objects to the new instance.
scoped_refptr<JavaBridgeDispatcherHost> instance =
- new JavaBridgeDispatcherHost(render_view_host);
+ new JavaBridgeDispatcherHost(render_frame_host);
for (ObjectMap::const_iterator iter = objects_.begin();
iter != objects_.end(); ++iter) {
instance->AddNamedObject(iter->first, iter->second);
}
- instances_[render_view_host] = instance;
+ instances_[render_frame_host] = instance;
}
-void JavaBridgeDispatcherHostManager::RenderViewDeleted(
- RenderViewHost* render_view_host) {
- if (!instances_.count(render_view_host)) // Needed for tests.
+void JavaBridgeDispatcherHostManager::RenderFrameDeleted(
+ RenderFrameHost* render_frame_host) {
+ if (!instances_.count(render_frame_host)) // Needed for tests.
return;
- instances_[render_view_host]->RenderViewDeleted();
- instances_.erase(render_view_host);
+ instances_[render_frame_host]->RenderFrameDeleted();
+ instances_.erase(render_frame_host);
}
void JavaBridgeDispatcherHostManager::DocumentAvailableInMainFrame() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Called when the window object has been cleared in the main frame.
JNIEnv* env = base::android::AttachCurrentThread();
base::android::ScopedJavaLocalRef<jobject> retained_object_set =
@@ -130,9 +111,28 @@ void JavaBridgeDispatcherHostManager::DocumentAvailableInMainFrame() {
}
}
+bool JavaBridgeDispatcherHostManager::OnMessageReceived(
+ const IPC::Message& message,
+ RenderFrameHost* render_frame_host) {
+ DCHECK(render_frame_host);
+ if (!instances_.count(render_frame_host))
+ return false;
+ scoped_refptr<JavaBridgeDispatcherHost> instance =
+ instances_[render_frame_host];
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(JavaBridgeDispatcherHostManager, message)
+ IPC_MESSAGE_FORWARD_DELAY_REPLY(
+ JavaBridgeHostMsg_GetChannelHandle,
+ instance.get(),
+ JavaBridgeDispatcherHost::OnGetChannelHandle)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
void JavaBridgeDispatcherHostManager::JavaBoundObjectCreated(
const base::android::JavaRef<jobject>& object) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
JNIEnv* env = base::android::AttachCurrentThread();
base::android::ScopedJavaLocalRef<jobject> retained_object_set =
@@ -144,7 +144,7 @@ void JavaBridgeDispatcherHostManager::JavaBoundObjectCreated(
void JavaBridgeDispatcherHostManager::JavaBoundObjectDestroyed(
const base::android::JavaRef<jobject>& object) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
JNIEnv* env = base::android::AttachCurrentThread();
base::android::ScopedJavaLocalRef<jobject> retained_object_set =
@@ -154,4 +154,9 @@ void JavaBridgeDispatcherHostManager::JavaBoundObjectDestroyed(
}
}
+void JavaBridgeDispatcherHostManager::SetAllowObjectContentsInspection(
+ bool allow) {
+ allow_object_contents_inspection_ = allow;
+}
+
} // namespace content
diff --git a/chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.h b/chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.h
index 29523b57396..8d03576be57 100644
--- a/chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.h
+++ b/chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.h
@@ -7,7 +7,7 @@
#include <map>
-#include "base/android/jni_helper.h"
+#include "base/android/jni_weak_ref.h"
#include "base/android/scoped_java_ref.h"
#include "base/compiler_specific.h"
#include "base/memory/ref_counted.h"
@@ -19,50 +19,54 @@ struct NPObject;
namespace content {
class JavaBridgeDispatcherHost;
-class RenderViewHost;
+class RenderFrameHost;
-// This class handles injecting Java objects into all of the RenderViews
+// This class handles injecting Java objects into all of the RenderFrames
// associated with a WebContents. It manages a set of JavaBridgeDispatcherHost
-// objects, one per RenderViewHost.
+// objects, one per RenderFrameHost.
class JavaBridgeDispatcherHostManager
: public WebContentsObserver,
public base::SupportsWeakPtr<JavaBridgeDispatcherHostManager> {
public:
- explicit JavaBridgeDispatcherHostManager(WebContents* web_contents);
+ JavaBridgeDispatcherHostManager(WebContents* web_contents,
+ jobject retained_object_set);
virtual ~JavaBridgeDispatcherHostManager();
// These methods add or remove the object to each JavaBridgeDispatcherHost.
// Each one holds a reference to the NPObject while the object is bound to
- // the corresponding RenderView. See JavaBridgeDispatcherHost for details.
+ // the corresponding RenderFrame. See JavaBridgeDispatcherHost for details.
void AddNamedObject(const base::string16& name, NPObject* object);
void RemoveNamedObject(const base::string16& name);
- void OnGetChannelHandle(RenderViewHost* render_view_host,
- IPC::Message* reply_msg);
-
- // Every time a JavaBoundObject backed by a real Java object is
- // created/destroyed, we insert/remove a strong ref to that Java object into
- // this set so that it doesn't get garbage collected while it's still
- // potentially in use. Although the set is managed native side, it's owned
- // and defined in Java so that pushing refs into it does not create new GC
- // roots that would prevent ContentViewCore from being garbage collected.
- void SetRetainedObjectSet(const JavaObjectWeakGlobalRef& retained_object_set);
-
// WebContentsObserver overrides
- virtual void RenderViewCreated(RenderViewHost* render_view_host) OVERRIDE;
- virtual void RenderViewDeleted(RenderViewHost* render_view_host) OVERRIDE;
+ virtual void RenderFrameCreated(RenderFrameHost* render_frame_host) OVERRIDE;
+ virtual void RenderFrameDeleted(RenderFrameHost* render_frame_host) OVERRIDE;
virtual void DocumentAvailableInMainFrame() OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message,
+ RenderFrameHost* render_frame_host) OVERRIDE;
void JavaBoundObjectCreated(const base::android::JavaRef<jobject>& object);
void JavaBoundObjectDestroyed(const base::android::JavaRef<jobject>& object);
+ bool GetAllowObjectContentsInspection() const {
+ return allow_object_contents_inspection_;
+ }
+ void SetAllowObjectContentsInspection(bool allow);
+
private:
- typedef std::map<RenderViewHost*, scoped_refptr<JavaBridgeDispatcherHost> >
+ typedef std::map<RenderFrameHost*, scoped_refptr<JavaBridgeDispatcherHost> >
InstanceMap;
InstanceMap instances_;
typedef std::map<base::string16, NPObject*> ObjectMap;
ObjectMap objects_;
+ // Every time a JavaBoundObject backed by a real Java object is
+ // created/destroyed, we insert/remove a strong ref to that Java object into
+ // this set so that it doesn't get garbage collected while it's still
+ // potentially in use. Although the set is managed native side, it's owned
+ // and defined in Java so that pushing refs into it does not create new GC
+ // roots that would prevent ContentViewCore from being garbage collected.
JavaObjectWeakGlobalRef retained_object_set_;
+ bool allow_object_contents_inspection_;
DISALLOW_COPY_AND_ASSIGN(JavaBridgeDispatcherHostManager);
};
diff --git a/chromium/content/browser/renderer_host/java/java_method.cc b/chromium/content/browser/renderer_host/java/java_method.cc
index 5a654fe1300..03bdb2d7e06 100644
--- a/chromium/content/browser/renderer_host/java/java_method.cc
+++ b/chromium/content/browser/renderer_host/java/java_method.cc
@@ -115,6 +115,11 @@ size_t JavaMethod::num_parameters() const {
return num_parameters_;
}
+bool JavaMethod::is_static() const {
+ EnsureTypesAndIDAreSetUp();
+ return is_static_;
+}
+
const JavaType& JavaMethod::parameter_type(size_t index) const {
EnsureTypesAndIDAreSetUp();
return parameter_types_[index];
@@ -212,7 +217,7 @@ void JavaMethod::EnsureTypesAndIDAreSetUp() const {
kJavaLangReflectMethod,
kGetModifiers,
kReturningInteger));
- bool is_static = env->CallStaticBooleanMethod(
+ is_static_ = env->CallStaticBooleanMethod(
g_java_lang_reflect_modifier_class.Get().obj(),
MethodID::Get<MethodID::TYPE_STATIC>(
env, g_java_lang_reflect_modifier_class.Get().obj(), kIsStatic,
@@ -226,7 +231,7 @@ void JavaMethod::EnsureTypesAndIDAreSetUp() const {
kJavaLangReflectMethod,
kGetDeclaringClass,
kReturningJavaLangClass))));
- id_ = is_static ?
+ id_ = is_static_ ?
MethodID::Get<MethodID::TYPE_STATIC>(
env, declaring_class.obj(), name_.c_str(), signature.c_str()) :
MethodID::Get<MethodID::TYPE_INSTANCE>(
diff --git a/chromium/content/browser/renderer_host/java/java_method.h b/chromium/content/browser/renderer_host/java/java_method.h
index 6356f70069d..1d59bacc5a0 100644
--- a/chromium/content/browser/renderer_host/java/java_method.h
+++ b/chromium/content/browser/renderer_host/java/java_method.h
@@ -23,6 +23,7 @@ class JavaMethod {
const std::string& name() const { return name_; }
size_t num_parameters() const;
+ bool is_static() const;
const JavaType& parameter_type(size_t index) const;
const JavaType& return_type() const;
jmethodID id() const;
@@ -37,6 +38,7 @@ class JavaMethod {
mutable size_t num_parameters_;
mutable std::vector<JavaType> parameter_types_;
mutable JavaType return_type_;
+ mutable bool is_static_;
mutable jmethodID id_;
DISALLOW_IMPLICIT_CONSTRUCTORS(JavaMethod);
diff --git a/chromium/content/browser/renderer_host/legacy_render_widget_host_win.cc b/chromium/content/browser/renderer_host/legacy_render_widget_host_win.cc
new file mode 100644
index 00000000000..86799c9bc99
--- /dev/null
+++ b/chromium/content/browser/renderer_host/legacy_render_widget_host_win.cc
@@ -0,0 +1,333 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/legacy_render_widget_host_win.h"
+
+#include "base/command_line.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/win/windows_version.h"
+#include "content/browser/accessibility/browser_accessibility_manager_win.h"
+#include "content/browser/accessibility/browser_accessibility_win.h"
+#include "content/public/browser/browser_accessibility_state.h"
+#include "content/public/common/content_switches.h"
+#include "ui/base/touch/touch_enabled.h"
+#include "ui/base/view_prop.h"
+#include "ui/base/win/internal_constants.h"
+#include "ui/base/win/window_event_target.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/win/dpi.h"
+
+namespace content {
+
+// A custom MSAA object id used to determine if a screen reader or some
+// other client is listening on MSAA events - if so, we enable full web
+// accessibility support.
+const int kIdScreenReaderHoneyPot = 1;
+
+LegacyRenderWidgetHostHWND::~LegacyRenderWidgetHostHWND() {
+ ::DestroyWindow(hwnd());
+}
+
+// static
+scoped_ptr<LegacyRenderWidgetHostHWND> LegacyRenderWidgetHostHWND::Create(
+ HWND parent) {
+ // content_unittests passes in the desktop window as the parent. We allow
+ // the LegacyRenderWidgetHostHWND instance to be created in this case for
+ // these tests to pass.
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableLegacyIntermediateWindow) ||
+ (!GetWindowEventTarget(parent) && parent != ::GetDesktopWindow()))
+ return scoped_ptr<LegacyRenderWidgetHostHWND>();
+
+ scoped_ptr<LegacyRenderWidgetHostHWND> legacy_window_instance;
+ legacy_window_instance.reset(new LegacyRenderWidgetHostHWND(parent));
+ // If we failed to create the child, or if the switch to disable the legacy
+ // window is passed in, then return NULL.
+ if (!::IsWindow(legacy_window_instance->hwnd()))
+ return scoped_ptr<LegacyRenderWidgetHostHWND>();
+
+ legacy_window_instance->Init();
+ return legacy_window_instance.Pass();
+}
+
+void LegacyRenderWidgetHostHWND::UpdateParent(HWND parent) {
+ ::SetParent(hwnd(), parent);
+ // If the new parent is the desktop Window, then we disable the child window
+ // to ensure that it does not receive any input events. It should not because
+ // of WS_EX_TRANSPARENT. This is only for safety.
+ if (parent == ::GetDesktopWindow()) {
+ ::EnableWindow(hwnd(), FALSE);
+ } else {
+ ::EnableWindow(hwnd(), TRUE);
+ }
+}
+
+HWND LegacyRenderWidgetHostHWND::GetParent() {
+ return ::GetParent(hwnd());
+}
+
+void LegacyRenderWidgetHostHWND::OnManagerDeleted() {
+ manager_ = NULL;
+}
+
+void LegacyRenderWidgetHostHWND::Show() {
+ ::ShowWindow(hwnd(), SW_SHOW);
+}
+
+void LegacyRenderWidgetHostHWND::Hide() {
+ ::ShowWindow(hwnd(), SW_HIDE);
+}
+
+void LegacyRenderWidgetHostHWND::SetBounds(const gfx::Rect& bounds) {
+ gfx::Rect bounds_in_pixel = gfx::win::DIPToScreenRect(bounds);
+ ::SetWindowPos(hwnd(), NULL, bounds_in_pixel.x(), bounds_in_pixel.y(),
+ bounds_in_pixel.width(), bounds_in_pixel.height(), 0);
+}
+
+void LegacyRenderWidgetHostHWND::OnFinalMessage(HWND hwnd) {
+ if (manager_)
+ manager_->OnAccessibleHwndDeleted();
+}
+
+LegacyRenderWidgetHostHWND::LegacyRenderWidgetHostHWND(HWND parent)
+ : manager_(NULL),
+ mouse_tracking_enabled_(false) {
+ RECT rect = {0};
+ Base::Create(parent, rect, L"Chrome Legacy Window",
+ WS_CHILDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
+ WS_EX_TRANSPARENT);
+}
+
+bool LegacyRenderWidgetHostHWND::Init() {
+ if (base::win::GetVersion() >= base::win::VERSION_WIN7 &&
+ ui::AreTouchEventsEnabled())
+ RegisterTouchWindow(hwnd(), TWF_WANTPALM);
+
+ HRESULT hr = ::CreateStdAccessibleObject(
+ hwnd(), OBJID_WINDOW, IID_IAccessible,
+ reinterpret_cast<void **>(window_accessible_.Receive()));
+ DCHECK(SUCCEEDED(hr));
+
+ if (!BrowserAccessibilityState::GetInstance()->IsAccessibleBrowser()) {
+ // Attempt to detect screen readers or other clients who want full
+ // accessibility support, by seeing if they respond to this event.
+ NotifyWinEvent(EVENT_SYSTEM_ALERT, hwnd(), kIdScreenReaderHoneyPot,
+ CHILDID_SELF);
+ }
+
+ return !!SUCCEEDED(hr);
+}
+
+// static
+ui::WindowEventTarget* LegacyRenderWidgetHostHWND::GetWindowEventTarget(
+ HWND parent) {
+ return reinterpret_cast<ui::WindowEventTarget*>(ui::ViewProp::GetValue(
+ parent, ui::WindowEventTarget::kWin32InputEventTarget));
+}
+
+LRESULT LegacyRenderWidgetHostHWND::OnEraseBkGnd(UINT message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ return 1;
+}
+
+LRESULT LegacyRenderWidgetHostHWND::OnGetObject(UINT message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ // Only the lower 32 bits of l_param are valid when checking the object id
+ // because it sometimes gets sign-extended incorrectly (but not always).
+ DWORD obj_id = static_cast<DWORD>(static_cast<DWORD_PTR>(l_param));
+
+ if (kIdScreenReaderHoneyPot == obj_id) {
+ // When an MSAA client has responded to our fake event on this id,
+ // enable screen reader support.
+ BrowserAccessibilityState::GetInstance()->OnScreenReaderDetected();
+ return static_cast<LRESULT>(0L);
+ }
+
+ if (OBJID_CLIENT != obj_id || !manager_)
+ return static_cast<LRESULT>(0L);
+
+ base::win::ScopedComPtr<IAccessible> root(
+ manager_->GetRoot()->ToBrowserAccessibilityWin());
+ return LresultFromObject(IID_IAccessible, w_param,
+ static_cast<IAccessible*>(root.Detach()));
+}
+
+// We send keyboard/mouse/touch messages to the parent window via SendMessage.
+// While this works, this has the side effect of converting input messages into
+// sent messages which changes their priority and could technically result
+// in these messages starving other messages in the queue. Additionally
+// keyboard/mouse hooks would not see these messages. The alternative approach
+// is to set and release capture as needed on the parent to ensure that it
+// receives all mouse events. However that was shelved due to possible issues
+// with capture changes.
+LRESULT LegacyRenderWidgetHostHWND::OnKeyboardRange(UINT message,
+ WPARAM w_param,
+ LPARAM l_param,
+ BOOL& handled) {
+ if (GetWindowEventTarget(GetParent())) {
+ return GetWindowEventTarget(GetParent())->HandleKeyboardMessage(
+ message, w_param, l_param);
+ }
+ return 0;
+}
+
+LRESULT LegacyRenderWidgetHostHWND::OnMouseRange(UINT message,
+ WPARAM w_param,
+ LPARAM l_param,
+ BOOL& handled) {
+ if (message == WM_MOUSEMOVE) {
+ if (!mouse_tracking_enabled_) {
+ mouse_tracking_enabled_ = true;
+ TRACKMOUSEEVENT tme;
+ tme.cbSize = sizeof(tme);
+ tme.dwFlags = TME_LEAVE;
+ tme.hwndTrack = hwnd();
+ tme.dwHoverTime = 0;
+ TrackMouseEvent(&tme);
+ }
+ }
+ // The offsets for WM_NCXXX and WM_MOUSEWHEEL and WM_MOUSEHWHEEL messages are
+ // in screen coordinates. We should not be converting them to parent
+ // coordinates.
+ if ((message >= WM_MOUSEFIRST && message <= WM_MOUSELAST) &&
+ (message != WM_MOUSEWHEEL && message != WM_MOUSEHWHEEL)) {
+ POINT mouse_coords;
+ mouse_coords.x = GET_X_LPARAM(l_param);
+ mouse_coords.y = GET_Y_LPARAM(l_param);
+ ::MapWindowPoints(hwnd(), GetParent(), &mouse_coords, 1);
+ l_param = MAKELPARAM(mouse_coords.x, mouse_coords.y);
+ }
+ if (GetWindowEventTarget(GetParent())) {
+ return GetWindowEventTarget(GetParent())->HandleMouseMessage(
+ message, w_param, l_param);
+ }
+ return 0;
+}
+
+LRESULT LegacyRenderWidgetHostHWND::OnMouseLeave(UINT message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ mouse_tracking_enabled_ = false;
+ if ((::GetCapture() != GetParent()) && GetWindowEventTarget(GetParent())) {
+ // We should send a WM_MOUSELEAVE to the parent window only if the mouse
+ // has moved outside the bounds of the parent.
+ POINT cursor_pos;
+ ::GetCursorPos(&cursor_pos);
+ if (::WindowFromPoint(cursor_pos) != GetParent()) {
+ return GetWindowEventTarget(GetParent())->HandleMouseMessage(
+ message, w_param, l_param);
+ }
+ }
+ return 0;
+}
+
+LRESULT LegacyRenderWidgetHostHWND::OnMouseActivate(UINT message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ // Don't pass this to DefWindowProc. That results in the WM_MOUSEACTIVATE
+ // message going all the way to the parent which then messes up state
+ // related to focused views, etc. This is because it treats this as if
+ // it lost activation.
+ // Our dummy window should not interfere with focus and activation in
+ // the parent. Return MA_ACTIVATE here ensures that focus state in the parent
+ // is preserved. The only exception is if the parent was created with the
+ // WS_EX_NOACTIVATE style.
+ if (::GetWindowLong(GetParent(), GWL_EXSTYLE) & WS_EX_NOACTIVATE)
+ return MA_NOACTIVATE;
+ // On Windows, if we select the menu item by touch and if the window at the
+ // location is another window on the same thread, that window gets a
+ // WM_MOUSEACTIVATE message and ends up activating itself, which is not
+ // correct. We workaround this by setting a property on the window at the
+ // current cursor location. We check for this property in our
+ // WM_MOUSEACTIVATE handler and don't activate the window if the property is
+ // set.
+ if (::GetProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow)) {
+ ::RemoveProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow);
+ return MA_NOACTIVATE;
+ }
+ return MA_ACTIVATE;
+}
+
+LRESULT LegacyRenderWidgetHostHWND::OnTouch(UINT message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ if (GetWindowEventTarget(GetParent())) {
+ return GetWindowEventTarget(GetParent())->HandleTouchMessage(
+ message, w_param, l_param);
+ }
+ return 0;
+}
+
+LRESULT LegacyRenderWidgetHostHWND::OnScroll(UINT message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ if (GetWindowEventTarget(GetParent())) {
+ return GetWindowEventTarget(GetParent())->HandleScrollMessage(
+ message, w_param, l_param);
+ }
+ return 0;
+}
+
+LRESULT LegacyRenderWidgetHostHWND::OnNCHitTest(UINT message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ if (GetWindowEventTarget(GetParent())) {
+ LRESULT hit_test = GetWindowEventTarget(
+ GetParent())->HandleNcHitTestMessage(message, w_param, l_param);
+ // If the parent returns HTNOWHERE which can happen for popup windows, etc
+ // we return HTCLIENT.
+ if (hit_test == HTNOWHERE)
+ hit_test = HTCLIENT;
+ return hit_test;
+ }
+ return HTNOWHERE;
+}
+
+LRESULT LegacyRenderWidgetHostHWND::OnNCPaint(UINT message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ return 0;
+}
+
+LRESULT LegacyRenderWidgetHostHWND::OnPaint(UINT message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ PAINTSTRUCT ps = {0};
+ ::BeginPaint(hwnd(), &ps);
+ ::EndPaint(hwnd(), &ps);
+ return 0;
+}
+
+LRESULT LegacyRenderWidgetHostHWND::OnSetCursor(UINT message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ return 0;
+}
+
+LRESULT LegacyRenderWidgetHostHWND::OnNCCalcSize(UINT message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ // Prevent scrollbars, etc from drawing.
+ return 0;
+}
+
+LRESULT LegacyRenderWidgetHostHWND::OnSize(UINT message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ // Certain trackpad drivers on Windows have bugs where in they don't generate
+ // WM_MOUSEWHEEL messages for the trackpoint and trackpad scrolling gestures
+ // unless there is an entry for Chrome with the class name of the Window.
+ // Additionally others check if the window WS_VSCROLL/WS_HSCROLL styles and
+ // generate the legacy WM_VSCROLL/WM_HSCROLL messages.
+ // We add these styles to ensure that trackpad/trackpoint scrolling
+ // work.
+ long current_style = ::GetWindowLong(hwnd(), GWL_STYLE);
+ ::SetWindowLong(hwnd(), GWL_STYLE,
+ current_style | WS_VSCROLL | WS_HSCROLL);
+ return 0;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/renderer_host/legacy_render_widget_host_win.h b/chromium/content/browser/renderer_host/legacy_render_widget_host_win.h
new file mode 100644
index 00000000000..814c200c275
--- /dev/null
+++ b/chromium/content/browser/renderer_host/legacy_render_widget_host_win.h
@@ -0,0 +1,151 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_LEGACY_RENDER_WIDGET_HOST_WIN_H_
+#define CONTENT_BROWSER_RENDERER_HOST_LEGACY_RENDER_WIDGET_HOST_WIN_H_
+
+#include <atlbase.h>
+#include <atlwin.h>
+#include <atlcrack.h>
+#include <oleacc.h>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/win/scoped_comptr.h"
+#include "content/common/content_export.h"
+#include "ui/gfx/rect.h"
+
+namespace ui {
+class WindowEventTarget;
+}
+
+namespace content {
+class BrowserAccessibilityManagerWin;
+
+// Reasons for the existence of this class outlined below:-
+// 1. Some screen readers expect every tab / every unique web content container
+// to be in its own HWND with class name Chrome_RenderWidgetHostHWND.
+// With Aura there is one main HWND which comprises the whole browser window
+// or the whole desktop. So, we need a fake HWND with the window class as
+// Chrome_RenderWidgetHostHWND as the root of the accessibility tree for
+// each tab.
+// 2. There are legacy drivers for trackpads/trackpoints which have special
+// code for sending mouse wheel and scroll events to the
+// Chrome_RenderWidgetHostHWND window.
+// 3. Windowless NPAPI plugins like Flash and Silverlight which expect the
+// container window to have the same bounds as the web page. In Aura, the
+// default container window is the whole window which includes the web page
+// WebContents, etc. This causes the plugin mouse event calculations to
+// fail.
+// We should look to get rid of this code when all of the above are fixed.
+
+// This class implements a child HWND with the same size as the content area,
+// that delegates its accessibility implementation to the root of the
+// BrowserAccessibilityManager tree. This HWND is hooked up as the parent of
+// the root object in the BrowserAccessibilityManager tree, so when any
+// accessibility client calls ::WindowFromAccessibleObject, they get this
+// HWND instead of the DesktopWindowTreeHostWin.
+class CONTENT_EXPORT LegacyRenderWidgetHostHWND
+ : public ATL::CWindowImpl<LegacyRenderWidgetHostHWND,
+ NON_EXPORTED_BASE(ATL::CWindow),
+ ATL::CWinTraits<WS_CHILD> > {
+ public:
+ DECLARE_WND_CLASS_EX(L"Chrome_RenderWidgetHostHWND", CS_DBLCLKS, 0);
+
+ typedef ATL::CWindowImpl<LegacyRenderWidgetHostHWND,
+ NON_EXPORTED_BASE(ATL::CWindow),
+ ATL::CWinTraits<WS_CHILD> > Base;
+
+ ~LegacyRenderWidgetHostHWND();
+
+ // Creates and returns an instance of the LegacyRenderWidgetHostHWND class on
+ // successful creation of a child window parented to the parent window passed
+ // in.
+ static scoped_ptr<LegacyRenderWidgetHostHWND> Create(HWND parent);
+
+ BEGIN_MSG_MAP_EX(LegacyRenderWidgetHostHWND)
+ MESSAGE_HANDLER_EX(WM_GETOBJECT, OnGetObject)
+ MESSAGE_RANGE_HANDLER(WM_KEYFIRST, WM_KEYLAST, OnKeyboardRange)
+ MESSAGE_HANDLER_EX(WM_PAINT, OnPaint)
+ MESSAGE_HANDLER_EX(WM_NCPAINT, OnNCPaint)
+ MESSAGE_HANDLER_EX(WM_ERASEBKGND, OnEraseBkGnd)
+ MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseRange)
+ MESSAGE_HANDLER_EX(WM_MOUSELEAVE, OnMouseLeave)
+ MESSAGE_HANDLER_EX(WM_MOUSEACTIVATE, OnMouseActivate)
+ MESSAGE_HANDLER_EX(WM_SETCURSOR, OnSetCursor)
+ MESSAGE_HANDLER_EX(WM_TOUCH, OnTouch)
+ MESSAGE_HANDLER_EX(WM_HSCROLL, OnScroll)
+ MESSAGE_HANDLER_EX(WM_VSCROLL, OnScroll)
+ MESSAGE_HANDLER_EX(WM_NCHITTEST, OnNCHitTest)
+ MESSAGE_RANGE_HANDLER(WM_NCMOUSEMOVE, WM_NCXBUTTONDBLCLK,
+ OnMouseRange)
+ MESSAGE_HANDLER_EX(WM_NCCALCSIZE, OnNCCalcSize)
+ MESSAGE_HANDLER_EX(WM_SIZE, OnSize)
+ END_MSG_MAP()
+
+ HWND hwnd() { return m_hWnd; }
+
+ // Called when the child window is to be reparented to a new window.
+ // The |parent| parameter contains the new parent window.
+ void UpdateParent(HWND parent);
+ HWND GetParent();
+
+ IAccessible* window_accessible() { return window_accessible_; }
+
+ void set_browser_accessibility_manager(
+ content::BrowserAccessibilityManagerWin* manager) {
+ manager_ = manager;
+ }
+
+ void OnManagerDeleted();
+
+ // Functions to show and hide the window.
+ void Show();
+ void Hide();
+
+ // Resizes the window to the bounds passed in.
+ void SetBounds(const gfx::Rect& bounds);
+
+ protected:
+ virtual void OnFinalMessage(HWND hwnd) OVERRIDE;
+
+ private:
+ LegacyRenderWidgetHostHWND(HWND parent);
+
+ bool Init();
+
+ // Returns the target to which the windows input events are forwarded.
+ static ui::WindowEventTarget* GetWindowEventTarget(HWND parent);
+
+ LRESULT OnEraseBkGnd(UINT message, WPARAM w_param, LPARAM l_param);
+ LRESULT OnGetObject(UINT message, WPARAM w_param, LPARAM l_param);
+ LRESULT OnKeyboardRange(UINT message, WPARAM w_param, LPARAM l_param,
+ BOOL& handled);
+ LRESULT OnMouseLeave(UINT message, WPARAM w_param, LPARAM l_param);
+ LRESULT OnMouseRange(UINT message, WPARAM w_param, LPARAM l_param,
+ BOOL& handled);
+ LRESULT OnMouseActivate(UINT message, WPARAM w_param, LPARAM l_param);
+ LRESULT OnTouch(UINT message, WPARAM w_param, LPARAM l_param);
+ LRESULT OnScroll(UINT message, WPARAM w_param, LPARAM l_param);
+ LRESULT OnNCHitTest(UINT message, WPARAM w_param, LPARAM l_param);
+
+ LRESULT OnNCPaint(UINT message, WPARAM w_param, LPARAM l_param);
+ LRESULT OnPaint(UINT message, WPARAM w_param, LPARAM l_param);
+ LRESULT OnSetCursor(UINT message, WPARAM w_param, LPARAM l_param);
+ LRESULT OnNCCalcSize(UINT message, WPARAM w_param, LPARAM l_param);
+ LRESULT OnSize(UINT message, WPARAM w_param, LPARAM l_param);
+
+ content::BrowserAccessibilityManagerWin* manager_;
+ base::win::ScopedComPtr<IAccessible> window_accessible_;
+
+ // Set to true if we turned on mouse tracking.
+ bool mouse_tracking_enabled_;
+
+ DISALLOW_COPY_AND_ASSIGN(LegacyRenderWidgetHostHWND);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_LEGACY_RENDER_WIDGET_HOST_WIN_H_
+
diff --git a/chromium/content/browser/renderer_host/media/DEPS b/chromium/content/browser/renderer_host/media/DEPS
index 6f4140f1e51..c3a5b96fc44 100644
--- a/chromium/content/browser/renderer_host/media/DEPS
+++ b/chromium/content/browser/renderer_host/media/DEPS
@@ -1,9 +1,3 @@
include_rules = [
"+media",
-
- # TODO: this is temporary, this directory doesn't belong under renderer_host
- # since it depends on web_contents.
- "+content/browser/web_contents",
- "+content/public/browser/web_contents.h",
- "+content/public/browser/web_contents_view.h",
]
diff --git a/chromium/content/browser/renderer_host/media/OWNERS b/chromium/content/browser/renderer_host/media/OWNERS
index bc20fb9af2a..d32059427a5 100644
--- a/chromium/content/browser/renderer_host/media/OWNERS
+++ b/chromium/content/browser/renderer_host/media/OWNERS
@@ -1,7 +1,7 @@
acolwell@chromium.org
dalecurtis@chromium.org
ddorwin@chromium.org
-fischman@chromium.org
+perkj@chromium.org
scherkus@chromium.org
shadi@chromium.org
tommi@chromium.org
@@ -9,21 +9,3 @@ vrk@chromium.org
wjia@chromium.org
xhwang@chromium.org
xians@chromium.org
-
-# Tab capture OWNERS.
-per-file audio*=miu@chromium.org
-per-file web_contents*=hclam@chromium.org
-per-file web_contents*=justinlin@chromium.org
-per-file web_contents*=miu@chromium.org
-per-file web_contents*=nick@chromium.org
-per-file video_capture_device_impl*=hclam@chromium.org
-per-file video_capture_device_impl*=miu@chromium.org
-per-file video_capture_device_impl*=nick@chromium.org
-per-file video_capture_oracle*=hclam@chromium.org
-per-file video_capture_oracle*=justinlin@chromium.org
-per-file video_capture_oracle*=miu@chromium.org
-per-file video_capture_oracle*=nick@chromium.org
-
-# Screen capture OWNERS.
-per-file desktop_capture_*=sergeyu@chromium.org
-per-file desktop_capture_*=wez@chromium.org
diff --git a/chromium/content/browser/renderer_host/media/audio_input_device_manager.cc b/chromium/content/browser/renderer_host/media/audio_input_device_manager.cc
index cfe00caea32..17b4d7d251d 100644
--- a/chromium/content/browser/renderer_host/media/audio_input_device_manager.cc
+++ b/chromium/content/browser/renderer_host/media/audio_input_device_manager.cc
@@ -8,7 +8,6 @@
#include "base/memory/scoped_ptr.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/media_stream_request.h"
-#include "media/audio/audio_device_name.h"
#include "media/audio/audio_input_ipc.h"
#include "media/audio/audio_manager_base.h"
#include "media/audio/audio_parameters.h"
@@ -30,14 +29,6 @@ AudioInputDeviceManager::AudioInputDeviceManager(
next_capture_session_id_(kFirstSessionId),
use_fake_device_(false),
audio_manager_(audio_manager) {
- // TODO(xians): Remove this fake_device after the unittests do not need it.
- StreamDeviceInfo fake_device(MEDIA_DEVICE_AUDIO_CAPTURE,
- media::AudioManagerBase::kDefaultDeviceName,
- media::AudioManagerBase::kDefaultDeviceId,
- 44100, media::CHANNEL_LAYOUT_STEREO,
- 0);
- fake_device.session_id = kFakeOpenSessionId;
- devices_.push_back(fake_device);
}
AudioInputDeviceManager::~AudioInputDeviceManager() {
@@ -45,7 +36,7 @@ AudioInputDeviceManager::~AudioInputDeviceManager() {
const StreamDeviceInfo* AudioInputDeviceManager::GetOpenedDeviceInfoById(
int session_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
StreamDeviceList::iterator device = GetDevice(session_id);
if (device == devices_.end())
return NULL;
@@ -55,12 +46,12 @@ const StreamDeviceInfo* AudioInputDeviceManager::GetOpenedDeviceInfoById(
void AudioInputDeviceManager::Register(
MediaStreamProviderListener* listener,
- base::MessageLoopProxy* device_thread_loop) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(!listener_);
- DCHECK(!device_loop_.get());
+ DCHECK(!device_task_runner_);
listener_ = listener;
- device_loop_ = device_thread_loop;
+ device_task_runner_ = device_task_runner;
}
void AudioInputDeviceManager::Unregister() {
@@ -69,20 +60,20 @@ void AudioInputDeviceManager::Unregister() {
}
void AudioInputDeviceManager::EnumerateDevices(MediaStreamType stream_type) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(listener_);
- device_loop_->PostTask(
+ device_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AudioInputDeviceManager::EnumerateOnDeviceThread,
this, stream_type));
}
int AudioInputDeviceManager::Open(const StreamDeviceInfo& device) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
// Generate a new id for this device.
int session_id = next_capture_session_id_++;
- device_loop_->PostTask(
+ device_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AudioInputDeviceManager::OpenOnDeviceThread,
this, session_id, device));
@@ -91,7 +82,7 @@ int AudioInputDeviceManager::Open(const StreamDeviceInfo& device) {
}
void AudioInputDeviceManager::Close(int session_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(listener_);
StreamDeviceList::iterator device = GetDevice(session_id);
if (device == devices_.end())
@@ -109,12 +100,12 @@ void AudioInputDeviceManager::Close(int session_id) {
}
void AudioInputDeviceManager::UseFakeDevice() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
use_fake_device_ = true;
}
bool AudioInputDeviceManager::ShouldUseFakeDevice() const {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
return use_fake_device_;
}
@@ -123,19 +114,17 @@ void AudioInputDeviceManager::EnumerateOnDeviceThread(
SCOPED_UMA_HISTOGRAM_TIMER(
"Media.AudioInputDeviceManager.EnumerateOnDeviceThreadTime");
DCHECK(IsOnDeviceThread());
+ DCHECK_EQ(MEDIA_DEVICE_AUDIO_CAPTURE, stream_type);
media::AudioDeviceNames device_names;
-
- switch (stream_type) {
- case MEDIA_DEVICE_AUDIO_CAPTURE:
- // AudioManager is guaranteed to outlive MediaStreamManager in
- // BrowserMainloop.
- audio_manager_->GetAudioInputDeviceNames(&device_names);
- break;
-
- default:
- NOTREACHED();
- break;
+ if (use_fake_device_) {
+ // Use the fake devices.
+ GetFakeDeviceNames(&device_names);
+ } else {
+ // Enumerate the devices on the OS.
+ // AudioManager is guaranteed to outlive MediaStreamManager in
+ // BrowserMainloop.
+ audio_manager_->GetAudioInputDeviceNames(&device_names);
}
scoped_ptr<StreamDeviceInfoArray> devices(new StreamDeviceInfoArray());
@@ -146,14 +135,6 @@ void AudioInputDeviceManager::EnumerateOnDeviceThread(
stream_type, it->device_name, it->unique_id));
}
- // If the |use_fake_device_| flag is on, inject the fake device if there is
- // no available device on the OS.
- if (use_fake_device_ && devices->empty()) {
- devices->push_back(StreamDeviceInfo(
- stream_type, media::AudioManagerBase::kDefaultDeviceName,
- media::AudioManagerBase::kDefaultDeviceId));
- }
-
// Return the device list through the listener by posting a task on
// IO thread since MediaStreamManager handles the callback asynchronously.
BrowserThread::PostTask(
@@ -215,7 +196,7 @@ void AudioInputDeviceManager::OpenOnDeviceThread(
void AudioInputDeviceManager::DevicesEnumeratedOnIOThread(
MediaStreamType stream_type,
scoped_ptr<StreamDeviceInfoArray> devices) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
// Ensure that |devices| gets deleted on exit.
if (listener_)
listener_->DevicesEnumerated(stream_type, *devices);
@@ -223,7 +204,7 @@ void AudioInputDeviceManager::DevicesEnumeratedOnIOThread(
void AudioInputDeviceManager::OpenedOnIOThread(int session_id,
const StreamDeviceInfo& info) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK_EQ(session_id, info.session_id);
DCHECK(GetDevice(session_id) == devices_.end());
@@ -235,13 +216,13 @@ void AudioInputDeviceManager::OpenedOnIOThread(int session_id,
void AudioInputDeviceManager::ClosedOnIOThread(MediaStreamType stream_type,
int session_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (listener_)
listener_->Closed(stream_type, session_id);
}
bool AudioInputDeviceManager::IsOnDeviceThread() const {
- return device_loop_->BelongsToCurrentThread();
+ return device_task_runner_->BelongsToCurrentThread();
}
AudioInputDeviceManager::StreamDeviceList::iterator
@@ -255,4 +236,18 @@ AudioInputDeviceManager::GetDevice(int session_id) {
return devices_.end();
}
+void AudioInputDeviceManager::GetFakeDeviceNames(
+ media::AudioDeviceNames* device_names) {
+ static const char kFakeDeviceName1[] = "Fake Audio 1";
+ static const char kFakeDeviceId1[] = "fake_audio_1";
+ static const char kFakeDeviceName2[] = "Fake Audio 2";
+ static const char kFakeDeviceId2[] = "fake_audio_2";
+ DCHECK(device_names->empty());
+ DCHECK(use_fake_device_);
+ device_names->push_back(media::AudioDeviceName(kFakeDeviceName1,
+ kFakeDeviceId1));
+ device_names->push_back(media::AudioDeviceName(kFakeDeviceName2,
+ kFakeDeviceId2));
+}
+
} // namespace content
diff --git a/chromium/content/browser/renderer_host/media/audio_input_device_manager.h b/chromium/content/browser/renderer_host/media/audio_input_device_manager.h
index 133673f2a5f..e8d44cb807a 100644
--- a/chromium/content/browser/renderer_host/media/audio_input_device_manager.h
+++ b/chromium/content/browser/renderer_host/media/audio_input_device_manager.h
@@ -22,6 +22,7 @@
#include "content/common/content_export.h"
#include "content/common/media/media_stream_options.h"
#include "content/public/common/media_stream_request.h"
+#include "media/audio/audio_device_name.h"
namespace media {
class AudioManager;
@@ -45,7 +46,8 @@ class CONTENT_EXPORT AudioInputDeviceManager : public MediaStreamProvider {
// MediaStreamProvider implementation, called on IO thread.
virtual void Register(MediaStreamProviderListener* listener,
- base::MessageLoopProxy* device_thread_loop) OVERRIDE;
+ const scoped_refptr<base::SingleThreadTaskRunner>&
+ device_task_runner) OVERRIDE;
virtual void Unregister() OVERRIDE;
virtual void EnumerateDevices(MediaStreamType stream_type) OVERRIDE;
virtual int Open(const StreamDeviceInfo& device) OVERRIDE;
@@ -55,6 +57,10 @@ class CONTENT_EXPORT AudioInputDeviceManager : public MediaStreamProvider {
bool ShouldUseFakeDevice() const;
private:
+ // Used by the unittests to get a list of fake devices.
+ friend class MediaStreamDispatcherHostTest;
+ void GetFakeDeviceNames(media::AudioDeviceNames* device_names);
+
typedef std::vector<StreamDeviceInfo> StreamDeviceList;
virtual ~AudioInputDeviceManager();
@@ -90,7 +96,7 @@ class CONTENT_EXPORT AudioInputDeviceManager : public MediaStreamProvider {
media::AudioManager* const audio_manager_; // Weak.
// The message loop of media stream device thread that this object runs on.
- scoped_refptr<base::MessageLoopProxy> device_loop_;
+ scoped_refptr<base::SingleThreadTaskRunner> device_task_runner_;
DISALLOW_COPY_AND_ASSIGN(AudioInputDeviceManager);
};
diff --git a/chromium/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc b/chromium/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc
index 76cb1794a96..c2963eefd36 100644
--- a/chromium/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc
+++ b/chromium/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc
@@ -8,6 +8,7 @@
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
+#include "base/synchronization/waitable_event.h"
#include "content/browser/browser_thread_impl.h"
#include "content/browser/renderer_host/media/audio_input_device_manager.h"
#include "content/public/common/media_stream_request.h"
@@ -32,7 +33,7 @@ class MockAudioInputDeviceManagerListener
MOCK_METHOD2(Closed, void(MediaStreamType, const int));
MOCK_METHOD2(DevicesEnumerated, void(MediaStreamType,
const StreamDeviceInfoArray&));
- MOCK_METHOD3(Error, void(MediaStreamType, int, MediaStreamProviderError));
+ MOCK_METHOD2(Aborted, void(MediaStreamType, int));
StreamDeviceInfoArray devices_;
@@ -40,23 +41,34 @@ class MockAudioInputDeviceManagerListener
DISALLOW_COPY_AND_ASSIGN(MockAudioInputDeviceManagerListener);
};
-class AudioInputDeviceManagerTest : public testing::Test {
- public:
- AudioInputDeviceManagerTest() {}
+// TODO(henrika): there are special restrictions for Android since
+// AudioInputDeviceManager::Open() must be called on the audio thread.
+// This test suite must be modified to run on Android.
+#if defined(OS_ANDROID)
+#define MAYBE_AudioInputDeviceManagerTest DISABLED_AudioInputDeviceManagerTest
+#else
+#define MAYBE_AudioInputDeviceManagerTest AudioInputDeviceManagerTest
+#endif
- // Returns true iff machine has an audio input device.
- bool CanRunAudioInputDeviceTests() {
- return audio_manager_->HasAudioInputDevices();
- }
+class MAYBE_AudioInputDeviceManagerTest : public testing::Test {
+ public:
+ MAYBE_AudioInputDeviceManagerTest() {}
protected:
virtual void SetUp() OVERRIDE {
// The test must run on Browser::IO.
- message_loop_.reset(new base::MessageLoop(base::MessageLoop::TYPE_IO));
+ message_loop_.reset(new base::MessageLoopForIO);
io_thread_.reset(new BrowserThreadImpl(BrowserThread::IO,
message_loop_.get()));
audio_manager_.reset(media::AudioManager::CreateForTesting());
+ // Wait for audio thread initialization to complete. Otherwise the
+ // enumeration type may not have been set yet.
+ base::WaitableEvent event(false, false);
+ audio_manager_->GetTaskRunner()->PostTask(FROM_HERE, base::Bind(
+ &base::WaitableEvent::Signal, base::Unretained(&event)));
+ event.Wait();
manager_ = new AudioInputDeviceManager(audio_manager_.get());
+ manager_->UseFakeDevice();
audio_input_listener_.reset(new MockAudioInputDeviceManagerListener());
manager_->Register(audio_input_listener_.get(),
message_loop_->message_loop_proxy().get());
@@ -85,13 +97,11 @@ class AudioInputDeviceManagerTest : public testing::Test {
StreamDeviceInfoArray devices_;
private:
- DISALLOW_COPY_AND_ASSIGN(AudioInputDeviceManagerTest);
+ DISALLOW_COPY_AND_ASSIGN(MAYBE_AudioInputDeviceManagerTest);
};
// Opens and closes the devices.
-TEST_F(AudioInputDeviceManagerTest, OpenAndCloseDevice) {
- if (!CanRunAudioInputDeviceTests())
- return;
+TEST_F(MAYBE_AudioInputDeviceManagerTest, OpenAndCloseDevice) {
ASSERT_FALSE(devices_.empty());
@@ -120,10 +130,7 @@ TEST_F(AudioInputDeviceManagerTest, OpenAndCloseDevice) {
}
// Opens multiple devices at one time and closes them later.
-TEST_F(AudioInputDeviceManagerTest, OpenMultipleDevices) {
- if (!CanRunAudioInputDeviceTests())
- return;
-
+TEST_F(MAYBE_AudioInputDeviceManagerTest, OpenMultipleDevices) {
ASSERT_FALSE(devices_.empty());
InSequence s;
@@ -166,9 +173,7 @@ TEST_F(AudioInputDeviceManagerTest, OpenMultipleDevices) {
}
// Opens a non-existing device.
-TEST_F(AudioInputDeviceManagerTest, OpenNotExistingDevice) {
- if (!CanRunAudioInputDeviceTests())
- return;
+TEST_F(MAYBE_AudioInputDeviceManagerTest, OpenNotExistingDevice) {
InSequence s;
MediaStreamType stream_type = MEDIA_DEVICE_AUDIO_CAPTURE;
@@ -189,10 +194,7 @@ TEST_F(AudioInputDeviceManagerTest, OpenNotExistingDevice) {
}
// Opens default device twice.
-TEST_F(AudioInputDeviceManagerTest, OpenDeviceTwice) {
- if (!CanRunAudioInputDeviceTests())
- return;
-
+TEST_F(MAYBE_AudioInputDeviceManagerTest, OpenDeviceTwice) {
ASSERT_FALSE(devices_.empty());
InSequence s;
@@ -225,10 +227,7 @@ TEST_F(AudioInputDeviceManagerTest, OpenDeviceTwice) {
}
// Accesses then closes the sessions after opening the devices.
-TEST_F(AudioInputDeviceManagerTest, AccessAndCloseSession) {
- if (!CanRunAudioInputDeviceTests())
- return;
-
+TEST_F(MAYBE_AudioInputDeviceManagerTest, AccessAndCloseSession) {
ASSERT_FALSE(devices_.empty());
InSequence s;
@@ -261,9 +260,7 @@ TEST_F(AudioInputDeviceManagerTest, AccessAndCloseSession) {
}
// Access an invalid session.
-TEST_F(AudioInputDeviceManagerTest, AccessInvalidSession) {
- if (!CanRunAudioInputDeviceTests())
- return;
+TEST_F(MAYBE_AudioInputDeviceManagerTest, AccessInvalidSession) {
InSequence s;
// Opens the first device.
diff --git a/chromium/content/browser/renderer_host/media/audio_input_renderer_host.cc b/chromium/content/browser/renderer_host/media/audio_input_renderer_host.cc
index e3d88cc702b..2dcc56d0918 100644
--- a/chromium/content/browser/renderer_host/media/audio_input_renderer_host.cc
+++ b/chromium/content/browser/renderer_host/media/audio_input_renderer_host.cc
@@ -7,14 +7,17 @@
#include "base/bind.h"
#include "base/memory/shared_memory.h"
#include "base/metrics/histogram.h"
+#include "base/numerics/safe_math.h"
#include "base/process/process.h"
+#include "base/strings/stringprintf.h"
+#include "content/browser/media/capture/web_contents_audio_input_stream.h"
+#include "content/browser/media/capture/web_contents_capture_util.h"
#include "content/browser/media/media_internals.h"
#include "content/browser/renderer_host/media/audio_input_device_manager.h"
#include "content/browser/renderer_host/media/audio_input_sync_writer.h"
#include "content/browser/renderer_host/media/media_stream_manager.h"
-#include "content/browser/renderer_host/media/web_contents_audio_input_stream.h"
-#include "content/browser/renderer_host/media/web_contents_capture_util.h"
#include "media/audio/audio_manager_base.h"
+#include "media/base/audio_bus.h"
namespace content {
@@ -54,7 +57,8 @@ AudioInputRendererHost::AudioInputRendererHost(
MediaStreamManager* media_stream_manager,
AudioMirroringManager* audio_mirroring_manager,
media::UserInputMonitor* user_input_monitor)
- : audio_manager_(audio_manager),
+ : BrowserMessageFilter(AudioMsgStart),
+ audio_manager_(audio_manager),
media_stream_manager_(media_stream_manager),
audio_mirroring_manager_(audio_mirroring_manager),
user_input_monitor_(user_input_monitor),
@@ -62,11 +66,12 @@ AudioInputRendererHost::AudioInputRendererHost(
media::AudioLogFactory::AUDIO_INPUT_CONTROLLER)) {}
AudioInputRendererHost::~AudioInputRendererHost() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(audio_entries_.empty());
}
void AudioInputRendererHost::OnChannelClosing() {
- // Since the IPC channel is gone, close all requested audio streams.
+ // Since the IPC sender is gone, close all requested audio streams.
DeleteEntries();
}
@@ -96,25 +101,36 @@ void AudioInputRendererHost::OnRecording(
make_scoped_refptr(controller)));
}
-void AudioInputRendererHost::OnError(media::AudioInputController* controller) {
+void AudioInputRendererHost::OnError(media::AudioInputController* controller,
+ media::AudioInputController::ErrorCode error_code) {
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(
&AudioInputRendererHost::DoHandleError,
this,
- make_scoped_refptr(controller)));
+ make_scoped_refptr(controller),
+ error_code));
}
void AudioInputRendererHost::OnData(media::AudioInputController* controller,
- const uint8* data,
- uint32 size) {
+ const media::AudioBus* data) {
NOTREACHED() << "Only low-latency mode is supported.";
}
+void AudioInputRendererHost::OnLog(media::AudioInputController* controller,
+ const std::string& message) {
+ BrowserThread::PostTask(BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&AudioInputRendererHost::DoLog,
+ this,
+ make_scoped_refptr(controller),
+ message));
+}
+
void AudioInputRendererHost::DoCompleteCreation(
media::AudioInputController* controller) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
AudioEntry* entry = LookupByController(controller);
if (!entry)
@@ -122,13 +138,13 @@ void AudioInputRendererHost::DoCompleteCreation(
if (!PeerHandle()) {
NOTREACHED() << "Renderer process handle is invalid.";
- DeleteEntryOnError(entry);
+ DeleteEntryOnError(entry, INVALID_PEER_HANDLE);
return;
}
- if (!entry->controller->LowLatencyMode()) {
- NOTREACHED() << "Only low-latency mode is supported.";
- DeleteEntryOnError(entry);
+ if (!entry->controller->SharedMemoryAndSyncSocketMode()) {
+ NOTREACHED() << "Only shared-memory/sync-socket mode is supported.";
+ DeleteEntryOnError(entry, INVALID_LATENCY_MODE);
return;
}
@@ -139,7 +155,7 @@ void AudioInputRendererHost::DoCompleteCreation(
&foreign_memory_handle)) {
// If we failed to map and share the shared memory then close the audio
// stream and send an error message.
- DeleteEntryOnError(entry);
+ DeleteEntryOnError(entry, MEMORY_SHARING_FAILED);
return;
}
@@ -156,7 +172,7 @@ void AudioInputRendererHost::DoCompleteCreation(
// the construction of audio input stream.
if (!writer->PrepareForeignSocketHandle(PeerHandle(),
&foreign_socket_handle)) {
- DeleteEntryOnError(entry);
+ DeleteEntryOnError(entry, SYNC_SOCKET_ERROR);
return;
}
@@ -168,33 +184,61 @@ void AudioInputRendererHost::DoCompleteCreation(
void AudioInputRendererHost::DoSendRecordingMessage(
media::AudioInputController* controller) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
// TODO(henrika): See crbug.com/115262 for details on why this method
// should be implemented.
}
void AudioInputRendererHost::DoHandleError(
- media::AudioInputController* controller) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ media::AudioInputController* controller,
+ media::AudioInputController::ErrorCode error_code) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ // Log all errors even it is ignored later.
+ MediaStreamManager::SendMessageToNativeLog(
+ base::StringPrintf("AudioInputController error: %d", error_code));
+
+ // This is a fix for crbug.com/357501. The error can be triggered when closing
+ // the lid on Macs, which causes more problems than it fixes.
+ // Also, in crbug.com/357569, the goal is to remove usage of the error since
+ // it was added to solve a crash on Windows that no longer can be reproduced.
+ if (error_code == media::AudioInputController::NO_DATA_ERROR) {
+ DVLOG(1) << "AudioInputRendererHost@" << this << "::DoHandleError: "
+ << "NO_DATA_ERROR ignored.";
+ return;
+ }
AudioEntry* entry = LookupByController(controller);
if (!entry)
return;
audio_log_->OnError(entry->stream_id);
- DeleteEntryOnError(entry);
+ DeleteEntryOnError(entry, AUDIO_INPUT_CONTROLLER_ERROR);
+}
+
+void AudioInputRendererHost::DoLog(media::AudioInputController* controller,
+ const std::string& message) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ AudioEntry* entry = LookupByController(controller);
+ if (!entry)
+ return;
+
+ // Add stream ID and current audio level reported by AIC to native log.
+ std::string log_string =
+ base::StringPrintf("[stream_id=%d] ", entry->stream_id);
+ log_string += message;
+ MediaStreamManager::SendMessageToNativeLog(log_string);
+ DVLOG(1) << log_string;
}
-bool AudioInputRendererHost::OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) {
+bool AudioInputRendererHost::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(AudioInputRendererHost, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(AudioInputRendererHost, message)
IPC_MESSAGE_HANDLER(AudioInputHostMsg_CreateStream, OnCreateStream)
IPC_MESSAGE_HANDLER(AudioInputHostMsg_RecordStream, OnRecordStream)
IPC_MESSAGE_HANDLER(AudioInputHostMsg_CloseStream, OnCloseStream)
IPC_MESSAGE_HANDLER(AudioInputHostMsg_SetVolume, OnSetVolume)
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return handled;
}
@@ -204,7 +248,7 @@ void AudioInputRendererHost::OnCreateStream(
int render_view_id,
int session_id,
const AudioInputHostMsg_CreateStream_Config& config) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DVLOG(1) << "AudioInputRendererHost@" << this
<< "::OnCreateStream(stream_id=" << stream_id
@@ -214,7 +258,7 @@ void AudioInputRendererHost::OnCreateStream(
// media::AudioParameters is validated in the deserializer.
if (LookupById(stream_id) != NULL) {
- SendErrorMessage(stream_id);
+ SendErrorMessage(stream_id, STREAM_ALREADY_EXISTS);
return;
}
@@ -229,42 +273,46 @@ void AudioInputRendererHost::OnCreateStream(
}
// Check if we have the permission to open the device and which device to use.
+ std::string device_name;
std::string device_id = media::AudioManagerBase::kDefaultDeviceId;
if (audio_params.format() != media::AudioParameters::AUDIO_FAKE) {
const StreamDeviceInfo* info = media_stream_manager_->
audio_input_device_manager()->GetOpenedDeviceInfoById(session_id);
if (!info) {
- SendErrorMessage(stream_id);
+ SendErrorMessage(stream_id, PERMISSION_DENIED);
DLOG(WARNING) << "No permission has been granted to input stream with "
<< "session_id=" << session_id;
return;
}
device_id = info->device.id;
+ device_name = info->device.name;
}
// Create a new AudioEntry structure.
scoped_ptr<AudioEntry> entry(new AudioEntry());
- const uint32 segment_size = (sizeof(media::AudioInputBufferParameters) +
- audio_params.GetBytesPerBuffer());
+ const uint32 segment_size =
+ (sizeof(media::AudioInputBufferParameters) +
+ media::AudioBus::CalculateMemorySize(audio_params));
entry->shared_memory_segment_count = config.shared_memory_count;
// Create the shared memory and share it with the renderer process
// using a new SyncWriter object.
- if (!entry->shared_memory.CreateAndMapAnonymous(
- segment_size * entry->shared_memory_segment_count)) {
+ base::CheckedNumeric<uint32> size = segment_size;
+ size *= entry->shared_memory_segment_count;
+ if (!size.IsValid() ||
+ !entry->shared_memory.CreateAndMapAnonymous(size.ValueOrDie())) {
// If creation of shared memory failed then send an error message.
- SendErrorMessage(stream_id);
+ SendErrorMessage(stream_id, SHARED_MEMORY_CREATE_FAILED);
return;
}
- scoped_ptr<AudioInputSyncWriter> writer(
- new AudioInputSyncWriter(&entry->shared_memory,
- entry->shared_memory_segment_count));
+ scoped_ptr<AudioInputSyncWriter> writer(new AudioInputSyncWriter(
+ &entry->shared_memory, entry->shared_memory_segment_count, audio_params));
if (!writer->Init()) {
- SendErrorMessage(stream_id);
+ SendErrorMessage(stream_id, SYNC_WRITER_INIT_FAILED);
return;
}
@@ -273,12 +321,13 @@ void AudioInputRendererHost::OnCreateStream(
entry->writer.reset(writer.release());
if (WebContentsCaptureUtil::IsWebContentsDeviceId(device_id)) {
entry->controller = media::AudioInputController::CreateForStream(
- audio_manager_->GetMessageLoop(),
+ audio_manager_->GetTaskRunner(),
this,
- WebContentsAudioInputStream::Create(device_id,
- audio_params,
- audio_manager_->GetWorkerLoop(),
- audio_mirroring_manager_),
+ WebContentsAudioInputStream::Create(
+ device_id,
+ audio_params,
+ audio_manager_->GetWorkerTaskRunner(),
+ audio_mirroring_manager_),
entry->writer.get(),
user_input_monitor_);
} else {
@@ -295,7 +344,7 @@ void AudioInputRendererHost::OnCreateStream(
}
if (!entry->controller.get()) {
- SendErrorMessage(stream_id);
+ SendErrorMessage(stream_id, STREAM_CREATE_ERROR);
return;
}
@@ -309,15 +358,17 @@ void AudioInputRendererHost::OnCreateStream(
entry->stream_id = stream_id;
audio_entries_.insert(std::make_pair(stream_id, entry.release()));
- audio_log_->OnCreated(stream_id, audio_params, device_id, std::string());
+ MediaStreamManager::SendMessageToNativeLog(
+ "Audio input stream created successfully. Device name: " + device_name);
+ audio_log_->OnCreated(stream_id, audio_params, device_id);
}
void AudioInputRendererHost::OnRecordStream(int stream_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
AudioEntry* entry = LookupById(stream_id);
if (!entry) {
- SendErrorMessage(stream_id);
+ SendErrorMessage(stream_id, INVALID_AUDIO_ENTRY);
return;
}
@@ -326,7 +377,7 @@ void AudioInputRendererHost::OnRecordStream(int stream_id) {
}
void AudioInputRendererHost::OnCloseStream(int stream_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
AudioEntry* entry = LookupById(stream_id);
@@ -335,11 +386,11 @@ void AudioInputRendererHost::OnCloseStream(int stream_id) {
}
void AudioInputRendererHost::OnSetVolume(int stream_id, double volume) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
AudioEntry* entry = LookupById(stream_id);
if (!entry) {
- SendErrorMessage(stream_id);
+ SendErrorMessage(stream_id, INVALID_AUDIO_ENTRY);
return;
}
@@ -347,13 +398,16 @@ void AudioInputRendererHost::OnSetVolume(int stream_id, double volume) {
audio_log_->OnSetVolume(stream_id, volume);
}
-void AudioInputRendererHost::SendErrorMessage(int stream_id) {
+void AudioInputRendererHost::SendErrorMessage(
+ int stream_id, ErrorCode error_code) {
+ MediaStreamManager::SendMessageToNativeLog(
+ base::StringPrintf("AudioInputRendererHost error: %d", error_code));
Send(new AudioInputMsg_NotifyStreamStateChanged(
stream_id, media::AudioInputIPCDelegate::kError));
}
void AudioInputRendererHost::DeleteEntries() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
for (AudioEntryMap::iterator i = audio_entries_.begin();
i != audio_entries_.end(); ++i) {
@@ -362,7 +416,7 @@ void AudioInputRendererHost::DeleteEntries() {
}
void AudioInputRendererHost::CloseAndDeleteStream(AudioEntry* entry) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!entry->pending_close) {
entry->controller->Close(base::Bind(&AudioInputRendererHost::DeleteEntry,
@@ -373,7 +427,7 @@ void AudioInputRendererHost::CloseAndDeleteStream(AudioEntry* entry) {
}
void AudioInputRendererHost::DeleteEntry(AudioEntry* entry) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
// Delete the entry when this method goes out of scope.
scoped_ptr<AudioEntry> entry_deleter(entry);
@@ -382,18 +436,19 @@ void AudioInputRendererHost::DeleteEntry(AudioEntry* entry) {
audio_entries_.erase(entry->stream_id);
}
-void AudioInputRendererHost::DeleteEntryOnError(AudioEntry* entry) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+void AudioInputRendererHost::DeleteEntryOnError(AudioEntry* entry,
+ ErrorCode error_code) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
// Sends the error message first before we close the stream because
// |entry| is destroyed in DeleteEntry().
- SendErrorMessage(entry->stream_id);
+ SendErrorMessage(entry->stream_id, error_code);
CloseAndDeleteStream(entry);
}
AudioInputRendererHost::AudioEntry* AudioInputRendererHost::LookupById(
int stream_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
AudioEntryMap::iterator i = audio_entries_.find(stream_id);
if (i != audio_entries_.end())
@@ -403,7 +458,7 @@ AudioInputRendererHost::AudioEntry* AudioInputRendererHost::LookupById(
AudioInputRendererHost::AudioEntry* AudioInputRendererHost::LookupByController(
media::AudioInputController* controller) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
// Iterate the map of entries.
// TODO(hclam): Implement a faster look up method.
diff --git a/chromium/content/browser/renderer_host/media/audio_input_renderer_host.h b/chromium/content/browser/renderer_host/media/audio_input_renderer_host.h
index 7587b144add..7bd6692eabb 100644
--- a/chromium/content/browser/renderer_host/media/audio_input_renderer_host.h
+++ b/chromium/content/browser/renderer_host/media/audio_input_renderer_host.h
@@ -56,6 +56,49 @@ class CONTENT_EXPORT AudioInputRendererHost
: public BrowserMessageFilter,
public media::AudioInputController::EventHandler {
public:
+
+ // Error codes to make native loggin more clear. These error codes are added
+ // to generic error strings to provide a higher degree of details.
+ // Changing these values can lead to problems when matching native debug
+ // logs with the actual cause of error.
+ enum ErrorCode {
+ // An unspecified error occured.
+ UNKNOWN_ERROR = 0,
+
+ // Failed to look up audio intry for the provided stream id.
+ INVALID_AUDIO_ENTRY, // = 1
+
+ // A stream with the specified stream id already exists.
+ STREAM_ALREADY_EXISTS, // = 2
+
+ // The page does not have permission to open the specified capture device.
+ PERMISSION_DENIED, // = 3
+
+ // Failed to create shared memory.
+ SHARED_MEMORY_CREATE_FAILED, // = 4
+
+ // Failed to initialize the AudioInputSyncWriter instance.
+ SYNC_WRITER_INIT_FAILED, // = 5
+
+ // Failed to create native audio input stream.
+ STREAM_CREATE_ERROR, // = 6
+
+ // Renderer process handle is invalid.
+ INVALID_PEER_HANDLE, // = 7
+
+ // Only low-latency mode is supported.
+ INVALID_LATENCY_MODE, // = 8
+
+ // Failed to map and share the shared memory.
+ MEMORY_SHARING_FAILED, // = 9
+
+ // Unable to prepare the foreign socket handle.
+ SYNC_SOCKET_ERROR, // = 10
+
+ // This error message comes from the AudioInputController instance.
+ AUDIO_INPUT_CONTROLLER_ERROR, // = 11
+ };
+
// Called from UI thread from the owner of this object.
// |user_input_monitor| is used for typing detection and can be NULL.
AudioInputRendererHost(media::AudioManager* audio_manager,
@@ -66,16 +109,17 @@ class CONTENT_EXPORT AudioInputRendererHost
// BrowserMessageFilter implementation.
virtual void OnChannelClosing() OVERRIDE;
virtual void OnDestruct() const OVERRIDE;
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
// AudioInputController::EventHandler implementation.
virtual void OnCreated(media::AudioInputController* controller) OVERRIDE;
virtual void OnRecording(media::AudioInputController* controller) OVERRIDE;
- virtual void OnError(media::AudioInputController* controller) OVERRIDE;
+ virtual void OnError(media::AudioInputController* controller,
+ media::AudioInputController::ErrorCode error_code) OVERRIDE;
virtual void OnData(media::AudioInputController* controller,
- const uint8* data,
- uint32 size) OVERRIDE;
+ const media::AudioBus* data) OVERRIDE;
+ virtual void OnLog(media::AudioInputController* controller,
+ const std::string& message) OVERRIDE;
private:
// TODO(henrika): extend test suite (compare AudioRenderHost)
@@ -120,10 +164,15 @@ class CONTENT_EXPORT AudioInputRendererHost
void DoSendRecordingMessage(media::AudioInputController* controller);
// Handle error coming from audio stream.
- void DoHandleError(media::AudioInputController* controller);
+ void DoHandleError(media::AudioInputController* controller,
+ media::AudioInputController::ErrorCode error_code);
+
+ // Log audio level of captured audio stream.
+ void DoLog(media::AudioInputController* controller,
+ const std::string& message);
// Send an error message to the renderer.
- void SendErrorMessage(int stream_id);
+ void SendErrorMessage(int stream_id, ErrorCode error_code);
// Delete all audio entry and all audio streams
void DeleteEntries();
@@ -136,7 +185,7 @@ class CONTENT_EXPORT AudioInputRendererHost
void DeleteEntry(AudioEntry* entry);
// Delete audio entry and close the related audio input stream.
- void DeleteEntryOnError(AudioEntry* entry);
+ void DeleteEntryOnError(AudioEntry* entry, ErrorCode error_code);
// A helper method to look up a AudioEntry identified by |stream_id|.
// Returns NULL if not found.
diff --git a/chromium/content/browser/renderer_host/media/audio_input_sync_writer.cc b/chromium/content/browser/renderer_host/media/audio_input_sync_writer.cc
index 369e0a87f20..117fbe8fedb 100644
--- a/chromium/content/browser/renderer_host/media/audio_input_sync_writer.cc
+++ b/chromium/content/browser/renderer_host/media/audio_input_sync_writer.cc
@@ -7,19 +7,39 @@
#include <algorithm>
#include "base/memory/shared_memory.h"
+#include "content/browser/renderer_host/media/media_stream_manager.h"
+
+using media::AudioBus;
namespace content {
-AudioInputSyncWriter::AudioInputSyncWriter(
- base::SharedMemory* shared_memory,
- int shared_memory_segment_count)
+AudioInputSyncWriter::AudioInputSyncWriter(base::SharedMemory* shared_memory,
+ int shared_memory_segment_count,
+ const media::AudioParameters& params)
: shared_memory_(shared_memory),
shared_memory_segment_count_(shared_memory_segment_count),
- current_segment_id_(0) {
+ current_segment_id_(0),
+ creation_time_(base::Time::Now()),
+ audio_bus_memory_size_(AudioBus::CalculateMemorySize(params)) {
DCHECK_GT(shared_memory_segment_count, 0);
DCHECK_EQ(shared_memory->requested_size() % shared_memory_segment_count, 0u);
shared_memory_segment_size_ =
shared_memory->requested_size() / shared_memory_segment_count;
+ DVLOG(1) << "SharedMemory::requested_size: "
+ << shared_memory->requested_size();
+ DVLOG(1) << "shared_memory_segment_count: " << shared_memory_segment_count;
+ DVLOG(1) << "audio_bus_memory_size: " << audio_bus_memory_size_;
+
+ // Create vector of audio buses by wrapping existing blocks of memory.
+ uint8* ptr = static_cast<uint8*>(shared_memory_->memory());
+ for (int i = 0; i < shared_memory_segment_count; ++i) {
+ media::AudioInputBuffer* buffer =
+ reinterpret_cast<media::AudioInputBuffer*>(ptr);
+ scoped_ptr<media::AudioBus> audio_bus =
+ media::AudioBus::WrapMemory(params, buffer->audio);
+ audio_buses_.push_back(audio_bus.release());
+ ptr += shared_memory_segment_size_;
+ }
}
AudioInputSyncWriter::~AudioInputSyncWriter() {}
@@ -29,23 +49,49 @@ void AudioInputSyncWriter::UpdateRecordedBytes(uint32 bytes) {
socket_->Send(&bytes, sizeof(bytes));
}
-uint32 AudioInputSyncWriter::Write(const void* data,
- uint32 size,
- double volume,
- bool key_pressed) {
+void AudioInputSyncWriter::Write(const media::AudioBus* data,
+ double volume,
+ bool key_pressed) {
+#if !defined(OS_ANDROID)
+ static const base::TimeDelta kLogDelayThreadhold =
+ base::TimeDelta::FromMilliseconds(500);
+
+ std::ostringstream oss;
+ if (last_write_time_.is_null()) {
+ // This is the first time Write is called.
+ base::TimeDelta interval = base::Time::Now() - creation_time_;
+ oss << "Audio input data received for the first time: delay = "
+ << interval.InMilliseconds() << "ms.";
+
+ } else {
+ base::TimeDelta interval = base::Time::Now() - last_write_time_;
+ if (interval > kLogDelayThreadhold) {
+ oss << "Audio input data delay unexpectedly long: delay = "
+ << interval.InMilliseconds() << "ms.";
+ }
+ }
+ if (!oss.str().empty())
+ MediaStreamManager::SendMessageToNativeLog(oss.str());
+
+ last_write_time_ = base::Time::Now();
+#endif
+
+ // Write audio parameters to shared memory.
uint8* ptr = static_cast<uint8*>(shared_memory_->memory());
ptr += current_segment_id_ * shared_memory_segment_size_;
media::AudioInputBuffer* buffer =
reinterpret_cast<media::AudioInputBuffer*>(ptr);
buffer->params.volume = volume;
- buffer->params.size = size;
+ buffer->params.size = audio_bus_memory_size_;
buffer->params.key_pressed = key_pressed;
- memcpy(buffer->audio, data, size);
+
+ // Copy data from the native audio layer into shared memory using pre-
+ // allocated audio buses.
+ media::AudioBus* audio_bus = audio_buses_[current_segment_id_];
+ data->CopyTo(audio_bus);
if (++current_segment_id_ >= shared_memory_segment_count_)
current_segment_id_ = 0;
-
- return size;
}
void AudioInputSyncWriter::Close() {
diff --git a/chromium/content/browser/renderer_host/media/audio_input_sync_writer.h b/chromium/content/browser/renderer_host/media/audio_input_sync_writer.h
index d16911f20c3..424d7907f11 100644
--- a/chromium/content/browser/renderer_host/media/audio_input_sync_writer.h
+++ b/chromium/content/browser/renderer_host/media/audio_input_sync_writer.h
@@ -5,10 +5,17 @@
#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_AUDIO_INPUT_SYNC_WRITER_H_
#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_AUDIO_INPUT_SYNC_WRITER_H_
-#include "base/file_descriptor_posix.h"
+#include "base/memory/scoped_vector.h"
#include "base/process/process.h"
#include "base/sync_socket.h"
+#include "base/time/time.h"
#include "media/audio/audio_input_controller.h"
+#include "media/audio/audio_parameters.h"
+#include "media/base/audio_bus.h"
+
+#if defined(OS_POSIX)
+#include "base/file_descriptor_posix.h"
+#endif
namespace base {
class SharedMemory;
@@ -22,16 +29,16 @@ namespace content {
class AudioInputSyncWriter : public media::AudioInputController::SyncWriter {
public:
explicit AudioInputSyncWriter(base::SharedMemory* shared_memory,
- int shared_memory_segment_count);
+ int shared_memory_segment_count,
+ const media::AudioParameters& params);
virtual ~AudioInputSyncWriter();
- // media::AudioOutputController::SyncWriter implementation.
+ // media::AudioInputController::SyncWriter implementation.
virtual void UpdateRecordedBytes(uint32 bytes) OVERRIDE;
- virtual uint32 Write(const void* data,
- uint32 size,
- double volume,
- bool key_pressed) OVERRIDE;
+ virtual void Write(const media::AudioBus* data,
+ double volume,
+ bool key_pressed) OVERRIDE;
virtual void Close() OVERRIDE;
bool Init();
@@ -55,6 +62,19 @@ class AudioInputSyncWriter : public media::AudioInputController::SyncWriter {
// PrepareForeignSocketHandle() is called and ran successfully.
scoped_ptr<base::CancelableSyncSocket> foreign_socket_;
+ // The time of the creation of this object.
+ base::Time creation_time_;
+
+ // The time of the last Write call.
+ base::Time last_write_time_;
+
+ // Size in bytes of each audio bus.
+ const int audio_bus_memory_size_;
+
+ // Vector of audio buses allocated during construction and deleted in the
+ // destructor.
+ ScopedVector<media::AudioBus> audio_buses_;
+
DISALLOW_IMPLICIT_CONSTRUCTORS(AudioInputSyncWriter);
};
diff --git a/chromium/content/browser/renderer_host/media/audio_renderer_host.cc b/chromium/content/browser/renderer_host/media/audio_renderer_host.cc
index 6282a13fada..7b07fd99c3b 100644
--- a/chromium/content/browser/renderer_host/media/audio_renderer_host.cc
+++ b/chromium/content/browser/renderer_host/media/audio_renderer_host.cc
@@ -10,9 +10,9 @@
#include "base/metrics/histogram.h"
#include "base/process/process.h"
#include "content/browser/browser_main_loop.h"
+#include "content/browser/media/capture/audio_mirroring_manager.h"
#include "content/browser/media/media_internals.h"
#include "content/browser/renderer_host/media/audio_input_device_manager.h"
-#include "content/browser/renderer_host/media/audio_mirroring_manager.h"
#include "content/browser/renderer_host/media/audio_sync_reader.h"
#include "content/browser/renderer_host/media/media_stream_manager.h"
#include "content/common/media/audio_messages.h"
@@ -34,9 +34,9 @@ class AudioRendererHost::AudioEntry
AudioEntry(AudioRendererHost* host,
int stream_id,
int render_view_id,
+ int render_frame_id,
const media::AudioParameters& params,
const std::string& output_device_id,
- const std::string& input_device_id,
scoped_ptr<base::SharedMemory> shared_memory,
scoped_ptr<media::AudioOutputController::SyncReader> reader);
virtual ~AudioEntry();
@@ -49,6 +49,8 @@ class AudioRendererHost::AudioEntry
return render_view_id_;
}
+ int render_frame_id() const { return render_frame_id_; }
+
media::AudioOutputController* controller() const { return controller_.get(); }
base::SharedMemory* shared_memory() {
@@ -59,11 +61,13 @@ class AudioRendererHost::AudioEntry
return reader_.get();
}
+ bool playing() const { return playing_; }
+ void set_playing(bool playing) { playing_ = playing; }
+
private:
// media::AudioOutputController::EventHandler implementation.
virtual void OnCreated() OVERRIDE;
virtual void OnPlaying() OVERRIDE;
- virtual void OnPowerMeasured(float power_dbfs, bool clipped) OVERRIDE;
virtual void OnPaused() OVERRIDE;
virtual void OnError() OVERRIDE;
virtual void OnDeviceChange(int new_buffer_size, int new_sample_rate)
@@ -72,34 +76,43 @@ class AudioRendererHost::AudioEntry
AudioRendererHost* const host_;
const int stream_id_;
- // The routing ID of the source render view.
+ // The routing ID of the source render view/frame.
const int render_view_id_;
+ const int render_frame_id_;
- // The AudioOutputController that manages the audio stream.
- const scoped_refptr<media::AudioOutputController> controller_;
-
- // Shared memory for transmission of the audio data.
+ // Shared memory for transmission of the audio data. Used by |reader_|.
const scoped_ptr<base::SharedMemory> shared_memory_;
- // The synchronous reader to be used by the controller.
+ // The synchronous reader to be used by |controller_|.
const scoped_ptr<media::AudioOutputController::SyncReader> reader_;
+
+ // The AudioOutputController that manages the audio stream.
+ const scoped_refptr<media::AudioOutputController> controller_;
+
+ bool playing_;
};
AudioRendererHost::AudioEntry::AudioEntry(
- AudioRendererHost* host, int stream_id, int render_view_id,
+ AudioRendererHost* host,
+ int stream_id,
+ int render_view_id,
+ int render_frame_id,
const media::AudioParameters& params,
const std::string& output_device_id,
- const std::string& input_device_id,
scoped_ptr<base::SharedMemory> shared_memory,
scoped_ptr<media::AudioOutputController::SyncReader> reader)
: host_(host),
stream_id_(stream_id),
render_view_id_(render_view_id),
- controller_(media::AudioOutputController::Create(
- host->audio_manager_, this, params, output_device_id,
- input_device_id, reader.get())),
+ render_frame_id_(render_frame_id),
shared_memory_(shared_memory.Pass()),
- reader_(reader.Pass()) {
+ reader_(reader.Pass()),
+ controller_(media::AudioOutputController::Create(host->audio_manager_,
+ this,
+ params,
+ output_device_id,
+ reader_.get())),
+ playing_(false) {
DCHECK(controller_.get());
}
@@ -114,12 +127,14 @@ AudioRendererHost::AudioRendererHost(
AudioMirroringManager* mirroring_manager,
MediaInternals* media_internals,
MediaStreamManager* media_stream_manager)
- : render_process_id_(render_process_id),
+ : BrowserMessageFilter(AudioMsgStart),
+ render_process_id_(render_process_id),
audio_manager_(audio_manager),
mirroring_manager_(mirroring_manager),
audio_log_(media_internals->CreateAudioLog(
media::AudioLogFactory::AUDIO_OUTPUT_CONTROLLER)),
- media_stream_manager_(media_stream_manager) {
+ media_stream_manager_(media_stream_manager),
+ num_playing_streams_(0) {
DCHECK(audio_manager_);
DCHECK(media_stream_manager_);
}
@@ -140,7 +155,7 @@ void AudioRendererHost::GetOutputControllers(
}
void AudioRendererHost::OnChannelClosing() {
- // Since the IPC channel is gone, close all requested audio streams.
+ // Since the IPC sender is gone, close all requested audio streams.
while (!audio_entries_.empty()) {
// Note: OnCloseStream() removes the entries from audio_entries_.
OnCloseStream(audio_entries_.begin()->first);
@@ -162,29 +177,20 @@ void AudioRendererHost::AudioEntry::OnPlaying() {
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
- base::Bind(
- base::IgnoreResult(&AudioRendererHost::Send), host_,
- new AudioMsg_NotifyStreamStateChanged(
- stream_id_, media::AudioOutputIPCDelegate::kPlaying)));
-}
-
-void AudioRendererHost::AudioEntry::OnPowerMeasured(float power_dbfs,
- bool clipped) {
- BrowserThread::PostTask(
- BrowserThread::IO,
- FROM_HERE,
- base::Bind(&AudioRendererHost::DoNotifyAudioPowerLevel, host_,
- stream_id_, power_dbfs, clipped));
+ base::Bind(&AudioRendererHost::DoNotifyStreamStateChanged,
+ host_,
+ stream_id_,
+ true));
}
void AudioRendererHost::AudioEntry::OnPaused() {
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
- base::Bind(
- base::IgnoreResult(&AudioRendererHost::Send), host_,
- new AudioMsg_NotifyStreamStateChanged(
- stream_id_, media::AudioOutputIPCDelegate::kPaused)));
+ base::Bind(&AudioRendererHost::DoNotifyStreamStateChanged,
+ host_,
+ stream_id_,
+ false));
}
void AudioRendererHost::AudioEntry::OnError() {
@@ -205,7 +211,7 @@ void AudioRendererHost::AudioEntry::OnDeviceChange(int new_buffer_size,
}
void AudioRendererHost::DoCompleteCreation(int stream_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!PeerHandle()) {
DLOG(WARNING) << "Renderer process handle is invalid.";
@@ -253,9 +259,48 @@ void AudioRendererHost::DoCompleteCreation(int stream_id) {
entry->shared_memory()->requested_size()));
}
+void AudioRendererHost::DoNotifyStreamStateChanged(int stream_id,
+ bool is_playing) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+ AudioEntry* const entry = LookupById(stream_id);
+ if (!entry)
+ return;
+
+ Send(new AudioMsg_NotifyStreamStateChanged(
+ stream_id,
+ is_playing ? media::AudioOutputIPCDelegate::kPlaying
+ : media::AudioOutputIPCDelegate::kPaused));
+
+ MediaObserver* const media_observer =
+ GetContentClient()->browser()->GetMediaObserver();
+ if (media_observer) {
+ if (is_playing) {
+ media_observer->OnAudioStreamPlaying(
+ render_process_id_,
+ entry->render_frame_id(),
+ entry->stream_id(),
+ base::Bind(&media::AudioOutputController::ReadCurrentPowerAndClip,
+ entry->controller()));
+ if (!entry->playing()) {
+ entry->set_playing(true);
+ base::AtomicRefCountInc(&num_playing_streams_);
+ }
+ } else {
+ media_observer->OnAudioStreamStopped(render_process_id_,
+ entry->render_frame_id(),
+ entry->stream_id());
+ if (entry->playing()) {
+ entry->set_playing(false);
+ base::AtomicRefCountDec(&num_playing_streams_);
+ }
+ }
+ }
+}
+
RenderViewHost::AudioOutputControllerList
AudioRendererHost::DoGetOutputControllers(int render_view_id) const {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
RenderViewHost::AudioOutputControllerList controllers;
AudioEntryMap::const_iterator it = audio_entries_.begin();
@@ -268,91 +313,53 @@ AudioRendererHost::DoGetOutputControllers(int render_view_id) const {
return controllers;
}
-void AudioRendererHost::DoNotifyAudioPowerLevel(int stream_id,
- float power_dbfs,
- bool clipped) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-
- MediaObserver* const media_observer =
- GetContentClient()->browser()->GetMediaObserver();
- if (media_observer) {
- AudioEntry* const entry = LookupById(stream_id);
- if (entry) {
- media_observer->OnAudioStreamPlayingChanged(
- render_process_id_, entry->render_view_id(), entry->stream_id(),
- true, power_dbfs, clipped);
- }
- }
-}
-
///////////////////////////////////////////////////////////////////////////////
// IPC Messages handler
-bool AudioRendererHost::OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) {
+bool AudioRendererHost::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(AudioRendererHost, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(AudioRendererHost, message)
IPC_MESSAGE_HANDLER(AudioHostMsg_CreateStream, OnCreateStream)
IPC_MESSAGE_HANDLER(AudioHostMsg_PlayStream, OnPlayStream)
IPC_MESSAGE_HANDLER(AudioHostMsg_PauseStream, OnPauseStream)
IPC_MESSAGE_HANDLER(AudioHostMsg_CloseStream, OnCloseStream)
IPC_MESSAGE_HANDLER(AudioHostMsg_SetVolume, OnSetVolume)
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return handled;
}
void AudioRendererHost::OnCreateStream(
- int stream_id, int render_view_id, int session_id,
+ int stream_id, int render_view_id, int render_frame_id, int session_id,
const media::AudioParameters& params) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DVLOG(1) << "AudioRendererHost@" << this
<< "::OnCreateStream(stream_id=" << stream_id
<< ", render_view_id=" << render_view_id
<< ", session_id=" << session_id << ")";
DCHECK_GT(render_view_id, 0);
+ DCHECK_GT(render_frame_id, 0);
// media::AudioParameters is validated in the deserializer.
- int input_channels = params.input_channels();
- if (input_channels < 0 ||
- input_channels > media::limits::kMaxChannels ||
- LookupById(stream_id) != NULL) {
+ if (LookupById(stream_id) != NULL) {
SendErrorMessage(stream_id);
return;
}
- // When the |input_channels| is valid, clients are trying to create a unified
- // IO stream which opens an input device mapping to the |session_id|.
// Initialize the |output_device_id| to an empty string which indicates that
// the default device should be used. If a StreamDeviceInfo instance was found
// though, then we use the matched output device.
- std::string input_device_id, output_device_id;
+ std::string output_device_id;
const StreamDeviceInfo* info = media_stream_manager_->
audio_input_device_manager()->GetOpenedDeviceInfoById(session_id);
if (info)
output_device_id = info->device.matched_output_device_id;
- if (input_channels > 0) {
- if (!info) {
- SendErrorMessage(stream_id);
- DLOG(WARNING) << "No permission has been granted to input stream with "
- << "session_id=" << session_id;
- return;
- }
-
- input_device_id = info->device.id;
- }
-
- // Calculate output and input memory size.
- int output_memory_size = AudioBus::CalculateMemorySize(params);
- int frames = params.frames_per_buffer();
- int input_memory_size = AudioBus::CalculateMemorySize(input_channels, frames);
-
// Create the shared memory and share with the renderer process.
// For synchronized I/O (if input_channels > 0) then we allocate
// extra memory after the output data for the input data.
- uint32 shared_memory_size = output_memory_size + input_memory_size;
+ uint32 shared_memory_size = AudioBus::CalculateMemorySize(params);
scoped_ptr<base::SharedMemory> shared_memory(new base::SharedMemory());
if (!shared_memory->CreateAndMapAnonymous(shared_memory_size)) {
SendErrorMessage(stream_id);
@@ -360,7 +367,7 @@ void AudioRendererHost::OnCreateStream(
}
scoped_ptr<AudioSyncReader> reader(
- new AudioSyncReader(shared_memory.get(), params, input_channels));
+ new AudioSyncReader(shared_memory.get(), params));
if (!reader->Init()) {
SendErrorMessage(stream_id);
return;
@@ -369,22 +376,27 @@ void AudioRendererHost::OnCreateStream(
MediaObserver* const media_observer =
GetContentClient()->browser()->GetMediaObserver();
if (media_observer)
- media_observer->OnCreatingAudioStream(render_process_id_, render_view_id);
+ media_observer->OnCreatingAudioStream(render_process_id_, render_frame_id);
scoped_ptr<AudioEntry> entry(new AudioEntry(
- this, stream_id, render_view_id, params, output_device_id,
- input_device_id, shared_memory.Pass(),
+ this,
+ stream_id,
+ render_view_id,
+ render_frame_id,
+ params,
+ output_device_id,
+ shared_memory.Pass(),
reader.PassAs<media::AudioOutputController::SyncReader>()));
if (mirroring_manager_) {
mirroring_manager_->AddDiverter(
render_process_id_, entry->render_view_id(), entry->controller());
}
audio_entries_.insert(std::make_pair(stream_id, entry.release()));
- audio_log_->OnCreated(stream_id, params, input_device_id, output_device_id);
+ audio_log_->OnCreated(stream_id, params, output_device_id);
}
void AudioRendererHost::OnPlayStream(int stream_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
AudioEntry* entry = LookupById(stream_id);
if (!entry) {
@@ -397,7 +409,7 @@ void AudioRendererHost::OnPlayStream(int stream_id) {
}
void AudioRendererHost::OnPauseStream(int stream_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
AudioEntry* entry = LookupById(stream_id);
if (!entry) {
@@ -410,7 +422,7 @@ void AudioRendererHost::OnPauseStream(int stream_id) {
}
void AudioRendererHost::OnSetVolume(int stream_id, double volume) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
AudioEntry* entry = LookupById(stream_id);
if (!entry) {
@@ -431,7 +443,7 @@ void AudioRendererHost::SendErrorMessage(int stream_id) {
}
void AudioRendererHost::OnCloseStream(int stream_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
// Prevent oustanding callbacks from attempting to close/delete the same
// AudioEntry twice.
@@ -452,20 +464,22 @@ void AudioRendererHost::OnCloseStream(int stream_id) {
}
void AudioRendererHost::DeleteEntry(scoped_ptr<AudioEntry> entry) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
// At this point, make the final "say" in audio playback state.
MediaObserver* const media_observer =
GetContentClient()->browser()->GetMediaObserver();
if (media_observer) {
- media_observer->OnAudioStreamPlayingChanged(
- render_process_id_, entry->render_view_id(), entry->stream_id(),
- false, -std::numeric_limits<float>::infinity(), false);
+ media_observer->OnAudioStreamStopped(render_process_id_,
+ entry->render_frame_id(),
+ entry->stream_id());
+ if (entry->playing())
+ base::AtomicRefCountDec(&num_playing_streams_);
}
}
void AudioRendererHost::ReportErrorAndClose(int stream_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
// Make sure this isn't a stray callback executing after the stream has been
// closed, so error notifications aren't sent after clients believe the stream
@@ -480,10 +494,14 @@ void AudioRendererHost::ReportErrorAndClose(int stream_id) {
}
AudioRendererHost::AudioEntry* AudioRendererHost::LookupById(int stream_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
AudioEntryMap::const_iterator i = audio_entries_.find(stream_id);
return i != audio_entries_.end() ? i->second : NULL;
}
+bool AudioRendererHost::HasActiveAudio() {
+ return !base::AtomicRefCountIsZero(&num_playing_streams_);
+}
+
} // namespace content
diff --git a/chromium/content/browser/renderer_host/media/audio_renderer_host.h b/chromium/content/browser/renderer_host/media/audio_renderer_host.h
index 147436619e2..38317ef8094 100644
--- a/chromium/content/browser/renderer_host/media/audio_renderer_host.h
+++ b/chromium/content/browser/renderer_host/media/audio_renderer_host.h
@@ -39,6 +39,7 @@
#include <map>
+#include "base/atomic_ref_count.h"
#include "base/gtest_prod_util.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
@@ -82,8 +83,11 @@ class CONTENT_EXPORT AudioRendererHost : public BrowserMessageFilter {
// BrowserMessageFilter implementation.
virtual void OnChannelClosing() OVERRIDE;
virtual void OnDestruct() const OVERRIDE;
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+
+ // Returns true if any streams managed by this host are actively playing. Can
+ // be called from any thread.
+ bool HasActiveAudio();
private:
friend class AudioRendererHostTest;
@@ -112,6 +116,7 @@ class CONTENT_EXPORT AudioRendererHost : public BrowserMessageFilter {
// message.
void OnCreateStream(int stream_id,
int render_view_id,
+ int render_frame_id,
int session_id,
const media::AudioParameters& params);
@@ -132,12 +137,12 @@ class CONTENT_EXPORT AudioRendererHost : public BrowserMessageFilter {
// NotifyStreamCreated message to the peer.
void DoCompleteCreation(int stream_id);
+ // Send playing/paused status to the renderer.
+ void DoNotifyStreamStateChanged(int stream_id, bool is_playing);
+
RenderViewHost::AudioOutputControllerList DoGetOutputControllers(
int render_view_id) const;
- // Propagate measured power level of the audio signal to MediaObserver.
- void DoNotifyAudioPowerLevel(int stream_id, float power_dbfs, bool clipped);
-
// Send an error message to the renderer.
void SendErrorMessage(int stream_id);
@@ -165,6 +170,9 @@ class CONTENT_EXPORT AudioRendererHost : public BrowserMessageFilter {
// A map of stream IDs to audio sources.
AudioEntryMap audio_entries_;
+ // The number of streams in the playing state.
+ base::AtomicRefCount num_playing_streams_;
+
DISALLOW_COPY_AND_ASSIGN(AudioRendererHost);
};
diff --git a/chromium/content/browser/renderer_host/media/audio_renderer_host_unittest.cc b/chromium/content/browser/renderer_host/media/audio_renderer_host_unittest.cc
index ff36a2797bc..343c9890810 100644
--- a/chromium/content/browser/renderer_host/media/audio_renderer_host_unittest.cc
+++ b/chromium/content/browser/renderer_host/media/audio_renderer_host_unittest.cc
@@ -3,19 +3,22 @@
// found in the LICENSE file.
#include "base/bind.h"
+#include "base/command_line.h"
#include "base/memory/scoped_ptr.h"
#include "base/run_loop.h"
#include "base/sync_socket.h"
+#include "content/browser/media/capture/audio_mirroring_manager.h"
#include "content/browser/media/media_internals.h"
#include "content/browser/renderer_host/media/audio_input_device_manager.h"
-#include "content/browser/renderer_host/media/audio_mirroring_manager.h"
#include "content/browser/renderer_host/media/audio_renderer_host.h"
#include "content/browser/renderer_host/media/media_stream_manager.h"
#include "content/common/media/audio_messages.h"
+#include "content/public/common/content_switches.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "ipc/ipc_message_utils.h"
#include "media/audio/audio_manager.h"
-#include "media/base/bind_to_loop.h"
+#include "media/base/bind_to_current_loop.h"
+#include "media/base/media_switches.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -27,6 +30,7 @@ using ::testing::NotNull;
namespace {
const int kRenderProcessId = 1;
const int kRenderViewId = 4;
+const int kRenderFrameId = 5;
const int kStreamId = 50;
} // namespace
@@ -86,9 +90,9 @@ class MockAudioRendererHost : public AudioRendererHost {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(MockAudioRendererHost, *message)
IPC_MESSAGE_HANDLER(AudioMsg_NotifyStreamCreated,
- OnStreamCreated)
+ OnNotifyStreamCreated)
IPC_MESSAGE_HANDLER(AudioMsg_NotifyStreamStateChanged,
- OnStreamStateChanged)
+ OnNotifyStreamStateChanged)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
EXPECT_TRUE(handled);
@@ -97,15 +101,14 @@ class MockAudioRendererHost : public AudioRendererHost {
return true;
}
- void OnStreamCreated(const IPC::Message& msg,
- int stream_id,
- base::SharedMemoryHandle handle,
+ void OnNotifyStreamCreated(int stream_id,
+ base::SharedMemoryHandle handle,
#if defined(OS_WIN)
- base::SyncSocket::Handle socket_handle,
+ base::SyncSocket::Handle socket_handle,
#else
- base::FileDescriptor socket_descriptor,
+ base::FileDescriptor socket_descriptor,
#endif
- uint32 length) {
+ uint32 length) {
// Maps the shared memory.
shared_memory_.reset(new base::SharedMemory(handle, false));
CHECK(shared_memory_->Map(length));
@@ -125,9 +128,8 @@ class MockAudioRendererHost : public AudioRendererHost {
OnStreamCreated(stream_id, length);
}
- void OnStreamStateChanged(const IPC::Message& msg,
- int stream_id,
- media::AudioOutputIPCDelegate::State state) {
+ void OnNotifyStreamStateChanged(int stream_id,
+ media::AudioOutputIPCDelegate::State state) {
switch (state) {
case media::AudioOutputIPCDelegate::kPlaying:
OnStreamPlaying(stream_id);
@@ -155,8 +157,9 @@ class AudioRendererHostTest : public testing::Test {
public:
AudioRendererHostTest() {
audio_manager_.reset(media::AudioManager::CreateForTesting());
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kUseFakeDeviceForMediaStream);
media_stream_manager_.reset(new MediaStreamManager(audio_manager_.get()));
- media_stream_manager_->UseFakeDevice();
host_ = new MockAudioRendererHost(audio_manager_.get(),
&mirroring_manager_,
MediaInternals::GetInstance(),
@@ -208,7 +211,8 @@ class AudioRendererHostTest : public testing::Test {
media::AudioParameters::kAudioCDSampleRate, 16,
media::AudioParameters::kAudioCDSampleRate / 10);
}
- host_->OnCreateStream(kStreamId, kRenderViewId, session_id, params);
+ host_->OnCreateStream(kStreamId, kRenderViewId, kRenderFrameId, session_id,
+ params);
// At some point in the future, a corresponding RemoveDiverter() call must
// be made.
@@ -265,7 +269,7 @@ class AudioRendererHostTest : public testing::Test {
base::RunLoop().RunUntilIdle();
base::RunLoop run_loop;
- audio_manager_->GetMessageLoop()->PostTask(
+ audio_manager_->GetTaskRunner()->PostTask(
FROM_HERE, media::BindToCurrentLoop(run_loop.QuitClosure()));
run_loop.Run();
}
diff --git a/chromium/content/browser/renderer_host/media/audio_sync_reader.cc b/chromium/content/browser/renderer_host/media/audio_sync_reader.cc
index a51d3d1e357..3daacca566e 100644
--- a/chromium/content/browser/renderer_host/media/audio_sync_reader.cc
+++ b/chromium/content/browser/renderer_host/media/audio_sync_reader.cc
@@ -18,10 +18,8 @@ using media::AudioBus;
namespace content {
AudioSyncReader::AudioSyncReader(base::SharedMemory* shared_memory,
- const media::AudioParameters& params,
- int input_channels)
+ const media::AudioParameters& params)
: shared_memory_(shared_memory),
- input_channels_(input_channels),
mute_audio_(CommandLine::ForCurrentProcess()->HasSwitch(
switches::kMuteAudio)),
packet_size_(shared_memory_->requested_size()),
@@ -34,18 +32,7 @@ AudioSyncReader::AudioSyncReader(base::SharedMemory* shared_memory,
maximum_wait_time_(base::TimeDelta::FromMilliseconds(20)),
#endif
buffer_index_(0) {
- int input_memory_size = 0;
- int output_memory_size = AudioBus::CalculateMemorySize(params);
- if (input_channels_ > 0) {
- // The input storage is after the output storage.
- int frames = params.frames_per_buffer();
- input_memory_size = AudioBus::CalculateMemorySize(input_channels_, frames);
- char* input_data =
- static_cast<char*>(shared_memory_->memory()) + output_memory_size;
- input_bus_ = AudioBus::WrapMemory(input_channels_, frames, input_data);
- input_bus_->Zero();
- }
- DCHECK_EQ(packet_size_, output_memory_size + input_memory_size);
+ DCHECK_EQ(packet_size_, AudioBus::CalculateMemorySize(params));
output_bus_ = AudioBus::WrapMemory(params, shared_memory->memory());
output_bus_->Zero();
}
@@ -71,7 +58,7 @@ void AudioSyncReader::UpdatePendingBytes(uint32 bytes) {
++buffer_index_;
}
-void AudioSyncReader::Read(const AudioBus* source, AudioBus* dest) {
+void AudioSyncReader::Read(AudioBus* dest) {
++renderer_callback_count_;
if (!WaitUntilDataIsReady()) {
++renderer_missed_callback_count_;
@@ -79,15 +66,6 @@ void AudioSyncReader::Read(const AudioBus* source, AudioBus* dest) {
return;
}
- // Copy optional synchronized live audio input for consumption by renderer
- // process.
- if (input_bus_) {
- if (source)
- source->CopyTo(input_bus_.get());
- else
- input_bus_->Zero();
- }
-
if (mute_audio_)
dest->Zero();
else
diff --git a/chromium/content/browser/renderer_host/media/audio_sync_reader.h b/chromium/content/browser/renderer_host/media/audio_sync_reader.h
index f3a2833b14e..d596d1b4c0e 100644
--- a/chromium/content/browser/renderer_host/media/audio_sync_reader.h
+++ b/chromium/content/browser/renderer_host/media/audio_sync_reader.h
@@ -5,7 +5,6 @@
#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_AUDIO_SYNC_READER_H_
#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_AUDIO_SYNC_READER_H_
-#include "base/file_descriptor_posix.h"
#include "base/process/process.h"
#include "base/sync_socket.h"
#include "base/synchronization/lock.h"
@@ -13,6 +12,10 @@
#include "media/audio/audio_output_controller.h"
#include "media/base/audio_bus.h"
+#if defined(OS_POSIX)
+#include "base/file_descriptor_posix.h"
+#endif
+
namespace base {
class SharedMemory;
}
@@ -26,15 +29,13 @@ namespace content {
class AudioSyncReader : public media::AudioOutputController::SyncReader {
public:
AudioSyncReader(base::SharedMemory* shared_memory,
- const media::AudioParameters& params,
- int input_channels);
+ const media::AudioParameters& params);
virtual ~AudioSyncReader();
// media::AudioOutputController::SyncReader implementations.
virtual void UpdatePendingBytes(uint32 bytes) OVERRIDE;
- virtual void Read(const media::AudioBus* source,
- media::AudioBus* dest) OVERRIDE;
+ virtual void Read(media::AudioBus* dest) OVERRIDE;
virtual void Close() OVERRIDE;
bool Init();
@@ -52,9 +53,6 @@ class AudioSyncReader : public media::AudioOutputController::SyncReader {
const base::SharedMemory* const shared_memory_;
- // Number of input channels for synchronized I/O.
- const int input_channels_;
-
// Mutes all incoming samples. This is used to prevent audible sound
// during automated testing.
const bool mute_audio_;
@@ -69,9 +67,6 @@ class AudioSyncReader : public media::AudioOutputController::SyncReader {
// Shared memory wrapper used for transferring audio data to Read() callers.
scoped_ptr<media::AudioBus> output_bus_;
- // Shared memory wrapper used for transferring audio data from Read() callers.
- scoped_ptr<media::AudioBus> input_bus_;
-
// Maximum amount of audio data which can be transferred in one Read() call.
const int packet_size_;
diff --git a/chromium/content/browser/renderer_host/media/device_request_message_filter.cc b/chromium/content/browser/renderer_host/media/device_request_message_filter.cc
index 89e632f07d8..db0d175e754 100644
--- a/chromium/content/browser/renderer_host/media/device_request_message_filter.cc
+++ b/chromium/content/browser/renderer_host/media/device_request_message_filter.cc
@@ -5,26 +5,21 @@
#include "content/browser/renderer_host/media/device_request_message_filter.h"
#include "content/browser/browser_main_loop.h"
+#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/renderer_host/media/media_stream_manager.h"
#include "content/common/media/media_stream_messages.h"
#include "content/public/browser/resource_context.h"
-// Clears the MediaStreamDevice.name from all devices in |device_list|.
-static void ClearDeviceLabels(content::StreamDeviceInfoArray* devices) {
- for (content::StreamDeviceInfoArray::iterator device_itr = devices->begin();
- device_itr != devices->end();
- ++device_itr) {
- device_itr->device.name.clear();
- }
-}
-
namespace content {
DeviceRequestMessageFilter::DeviceRequestMessageFilter(
ResourceContext* resource_context,
- MediaStreamManager* media_stream_manager)
- : resource_context_(resource_context),
- media_stream_manager_(media_stream_manager) {
+ MediaStreamManager* media_stream_manager,
+ int render_process_id)
+ : BrowserMessageFilter(MediaStreamMsgStart),
+ resource_context_(resource_context),
+ media_stream_manager_(media_stream_manager),
+ render_process_id_(render_process_id) {
DCHECK(resource_context);
DCHECK(media_stream_manager);
}
@@ -62,7 +57,7 @@ void DeviceRequestMessageFilter::DevicesEnumerated(
int page_request_id,
const std::string& label,
const StreamDeviceInfoArray& new_devices) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
// Look up the DeviceRequest by id.
DeviceRequestList::iterator request_it = requests_.begin();
@@ -94,12 +89,6 @@ void DeviceRequestMessageFilter::DevicesEnumerated(
return;
}
- // Query for mic and camera permissions.
- if (!resource_context_->AllowMicAccess(request_it->origin))
- ClearDeviceLabels(audio_devices);
- if (!resource_context_->AllowCameraAccess(request_it->origin))
- ClearDeviceLabels(video_devices);
-
// Both audio and video devices are ready for copying.
StreamDeviceInfoArray all_devices = *audio_devices;
all_devices.insert(
@@ -112,13 +101,13 @@ void DeviceRequestMessageFilter::DevicesEnumerated(
requests_.erase(request_it);
}
-bool DeviceRequestMessageFilter::OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) {
+bool DeviceRequestMessageFilter::OnMessageReceived(
+ const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(DeviceRequestMessageFilter, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(DeviceRequestMessageFilter, message)
IPC_MESSAGE_HANDLER(MediaStreamHostMsg_GetSources, OnGetSources)
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return handled;
}
@@ -134,16 +123,24 @@ void DeviceRequestMessageFilter::OnChannelClosing() {
void DeviceRequestMessageFilter::OnGetSources(int request_id,
const GURL& security_origin) {
+ if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanRequestURL(
+ render_process_id_, security_origin)) {
+ LOG(ERROR) << "Disallowed URL in DRMF::OnGetSources: " << security_origin;
+ return;
+ }
+
// Make request to get audio devices.
const std::string& audio_label = media_stream_manager_->EnumerateDevices(
this, -1, -1, resource_context_->GetMediaDeviceIDSalt(), -1,
- MEDIA_DEVICE_AUDIO_CAPTURE, security_origin);
+ MEDIA_DEVICE_AUDIO_CAPTURE, security_origin,
+ resource_context_->AllowMicAccess(security_origin));
DCHECK(!audio_label.empty());
// Make request for video devices.
const std::string& video_label = media_stream_manager_->EnumerateDevices(
this, -1, -1, resource_context_->GetMediaDeviceIDSalt(), -1,
- MEDIA_DEVICE_VIDEO_CAPTURE, security_origin);
+ MEDIA_DEVICE_VIDEO_CAPTURE, security_origin,
+ resource_context_->AllowCameraAccess(security_origin));
DCHECK(!video_label.empty());
requests_.push_back(DeviceRequest(
diff --git a/chromium/content/browser/renderer_host/media/device_request_message_filter.h b/chromium/content/browser/renderer_host/media/device_request_message_filter.h
index 2a95c8286ab..2ec54ccfc37 100644
--- a/chromium/content/browser/renderer_host/media/device_request_message_filter.h
+++ b/chromium/content/browser/renderer_host/media/device_request_message_filter.h
@@ -24,7 +24,8 @@ class CONTENT_EXPORT DeviceRequestMessageFilter : public BrowserMessageFilter,
public MediaStreamRequester {
public:
DeviceRequestMessageFilter(ResourceContext* resource_context,
- MediaStreamManager* media_stream_manager);
+ MediaStreamManager* media_stream_manager,
+ int render_process_id);
// MediaStreamRequester implementation.
// TODO(vrk): Replace MediaStreamRequester interface with a single callback so
@@ -34,8 +35,10 @@ class CONTENT_EXPORT DeviceRequestMessageFilter : public BrowserMessageFilter,
int render_view_id, int page_request_id, const std::string& label,
const StreamDeviceInfoArray& audio_devices,
const StreamDeviceInfoArray& video_devices) OVERRIDE {}
- virtual void StreamGenerationFailed(int render_view_id,
- int page_request_id) OVERRIDE {}
+ virtual void StreamGenerationFailed(
+ int render_view_id,
+ int page_request_id,
+ content::MediaStreamRequestResult result) OVERRIDE {}
virtual void DeviceStopped(int render_view_id,
const std::string& label,
const StreamDeviceInfo& device) OVERRIDE {}
@@ -50,8 +53,7 @@ class CONTENT_EXPORT DeviceRequestMessageFilter : public BrowserMessageFilter,
const StreamDeviceInfoArray& devices) OVERRIDE;
// BrowserMessageFilter implementation.
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
virtual void OnChannelClosing() OVERRIDE;
protected:
@@ -69,6 +71,8 @@ class CONTENT_EXPORT DeviceRequestMessageFilter : public BrowserMessageFilter,
// List of all requests.
DeviceRequestList requests_;
+ int render_process_id_;
+
DISALLOW_COPY_AND_ASSIGN(DeviceRequestMessageFilter);
};
diff --git a/chromium/content/browser/renderer_host/media/device_request_message_filter_unittest.cc b/chromium/content/browser/renderer_host/media/device_request_message_filter_unittest.cc
index 95b9cd854f6..6d6a9e34dad 100644
--- a/chromium/content/browser/renderer_host/media/device_request_message_filter_unittest.cc
+++ b/chromium/content/browser/renderer_host/media/device_request_message_filter_unittest.cc
@@ -25,15 +25,15 @@ class MockMediaStreamManager : public MediaStreamManager {
virtual ~MockMediaStreamManager() {}
- MOCK_METHOD7(EnumerateDevices,
+ MOCK_METHOD8(EnumerateDevices,
std::string(MediaStreamRequester* requester,
int render_process_id,
int render_view_id,
const ResourceContext::SaltCallback& rc,
int page_request_id,
MediaStreamType type,
- const GURL& security_origin));
- MOCK_METHOD1(CancelRequest, void(const std::string& label));
+ const GURL& security_origin,
+ bool have_permission));
std::string DoEnumerateDevices(MediaStreamRequester* requester,
int render_process_id,
@@ -41,7 +41,8 @@ class MockMediaStreamManager : public MediaStreamManager {
const ResourceContext::SaltCallback& rc,
int page_request_id,
MediaStreamType type,
- const GURL& security_origin) {
+ const GURL& security_origin,
+ bool have_permission) {
if (type == MEDIA_DEVICE_AUDIO_CAPTURE) {
return kAudioLabel;
} else {
@@ -54,7 +55,7 @@ class MockDeviceRequestMessageFilter : public DeviceRequestMessageFilter {
public:
MockDeviceRequestMessageFilter(MockResourceContext* context,
MockMediaStreamManager* manager)
- : DeviceRequestMessageFilter(context, manager), received_id_(-1) {}
+ : DeviceRequestMessageFilter(context, manager, 0), received_id_(-1) {}
StreamDeviceInfoArray requested_devices() { return requested_devices_; }
int received_id() { return received_id_; }
@@ -95,10 +96,12 @@ class DeviceRequestMessageFilterTest : public testing::Test {
AddVideoDevices(number_video_devices);
GURL origin("https://test.com");
EXPECT_CALL(*media_stream_manager_,
- EnumerateDevices(_, _, _, _, _, MEDIA_DEVICE_AUDIO_CAPTURE, _))
+ EnumerateDevices(_, _, _, _, _, MEDIA_DEVICE_AUDIO_CAPTURE,
+ _, _))
.Times(1);
EXPECT_CALL(*media_stream_manager_,
- EnumerateDevices(_, _, _, _, _, MEDIA_DEVICE_VIDEO_CAPTURE, _))
+ EnumerateDevices(_, _, _, _, _, MEDIA_DEVICE_VIDEO_CAPTURE,
+ _, _))
.Times(1);
// Send message to get devices. Should trigger 2 EnumerateDevice() requests.
const int kRequestId = 123;
@@ -110,10 +113,6 @@ class DeviceRequestMessageFilterTest : public testing::Test {
EXPECT_EQ(0u, host_->requested_devices().size());
// After the video device callback is fired, |message| should be populated.
- EXPECT_CALL(*media_stream_manager_, CancelRequest(kAudioLabel))
- .Times(1);
- EXPECT_CALL(*media_stream_manager_, CancelRequest(kVideoLabel))
- .Times(1);
FireVideoDeviceCallback();
EXPECT_EQ(static_cast<size_t>(number_audio_devices + number_video_devices),
host_->requested_devices().size());
@@ -121,25 +120,16 @@ class DeviceRequestMessageFilterTest : public testing::Test {
EXPECT_EQ(kRequestId, host_->received_id());
}
- bool AreLabelsPresent(MediaStreamType type) {
- const StreamDeviceInfoArray& devices = host_->requested_devices();
- for (size_t i = 0; i < devices.size(); i++) {
- if (devices[i].device.type == type && !devices[i].device.name.empty())
- return true;
- }
- return false;
- }
-
protected:
virtual ~DeviceRequestMessageFilterTest() {}
virtual void SetUp() OVERRIDE {
- message_loop_.reset(new base::MessageLoop(base::MessageLoop::TYPE_IO));
+ message_loop_.reset(new base::MessageLoopForIO);
io_thread_.reset(
new TestBrowserThread(BrowserThread::IO, message_loop_.get()));
media_stream_manager_.reset(new MockMediaStreamManager());
- ON_CALL(*media_stream_manager_, EnumerateDevices(_, _, _, _, _, _, _))
+ ON_CALL(*media_stream_manager_, EnumerateDevices(_, _, _, _, _, _, _, _))
.WillByDefault(Invoke(media_stream_manager_.get(),
&MockMediaStreamManager::DoEnumerateDevices));
@@ -180,11 +170,7 @@ class DeviceRequestMessageFilterTest : public testing::Test {
}
void SendGetSourcesMessage(int request_id, const GURL& origin) {
- // Since we're not actually sending IPC messages, this is a throw-away
- // value.
- bool message_was_ok;
- host_->OnMessageReceived(MediaStreamHostMsg_GetSources(request_id, origin),
- &message_was_ok);
+ host_->OnMessageReceived(MediaStreamHostMsg_GetSources(request_id, origin));
}
void FireAudioDeviceCallback() {
@@ -224,36 +210,4 @@ TEST_F(DeviceRequestMessageFilterTest, TestGetSources_NoDevices) {
RunTest(0, 0);
}
-TEST_F(DeviceRequestMessageFilterTest, TestGetSources_DenyMicDenyCamera) {
- resource_context_->set_mic_access(false);
- resource_context_->set_camera_access(false);
- RunTest(3, 3);
- EXPECT_FALSE(AreLabelsPresent(MEDIA_DEVICE_AUDIO_CAPTURE));
- EXPECT_FALSE(AreLabelsPresent(MEDIA_DEVICE_VIDEO_CAPTURE));
-}
-
-TEST_F(DeviceRequestMessageFilterTest, TestGetSources_AllowMicDenyCamera) {
- resource_context_->set_mic_access(true);
- resource_context_->set_camera_access(false);
- RunTest(3, 3);
- EXPECT_TRUE(AreLabelsPresent(MEDIA_DEVICE_AUDIO_CAPTURE));
- EXPECT_FALSE(AreLabelsPresent(MEDIA_DEVICE_VIDEO_CAPTURE));
-}
-
-TEST_F(DeviceRequestMessageFilterTest, TestGetSources_DenyMicAllowCamera) {
- resource_context_->set_mic_access(false);
- resource_context_->set_camera_access(true);
- RunTest(3, 3);
- EXPECT_FALSE(AreLabelsPresent(MEDIA_DEVICE_AUDIO_CAPTURE));
- EXPECT_TRUE(AreLabelsPresent(MEDIA_DEVICE_VIDEO_CAPTURE));
-}
-
-TEST_F(DeviceRequestMessageFilterTest, TestGetSources_AllowMicAllowCamera) {
- resource_context_->set_mic_access(true);
- resource_context_->set_camera_access(true);
- RunTest(3, 3);
- EXPECT_TRUE(AreLabelsPresent(MEDIA_DEVICE_AUDIO_CAPTURE));
- EXPECT_TRUE(AreLabelsPresent(MEDIA_DEVICE_VIDEO_CAPTURE));
-}
-
}; // namespace content
diff --git a/chromium/content/browser/renderer_host/media/media_capture_devices_impl.cc b/chromium/content/browser/renderer_host/media/media_capture_devices_impl.cc
new file mode 100644
index 00000000000..9cc8b12e470
--- /dev/null
+++ b/chromium/content/browser/renderer_host/media/media_capture_devices_impl.cc
@@ -0,0 +1,99 @@
+// 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/renderer_host/media/media_capture_devices_impl.h"
+
+#include "content/browser/browser_main_loop.h"
+#include "content/browser/renderer_host/media/media_stream_manager.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace content {
+
+namespace {
+
+void EnsureMonitorCaptureDevices() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&MediaStreamManager::EnsureDeviceMonitorStarted,
+ base::Unretained(
+ BrowserMainLoop::GetInstance()->media_stream_manager())));
+}
+
+} // namespace
+
+MediaCaptureDevices* MediaCaptureDevices::GetInstance() {
+ return MediaCaptureDevicesImpl::GetInstance();
+}
+
+MediaCaptureDevicesImpl* MediaCaptureDevicesImpl::GetInstance() {
+ return Singleton<MediaCaptureDevicesImpl>::get();
+}
+
+const MediaStreamDevices&
+MediaCaptureDevicesImpl::GetAudioCaptureDevices() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (!devices_enumerated_) {
+ EnsureMonitorCaptureDevices();
+ devices_enumerated_ = true;
+ }
+ return audio_devices_;
+}
+
+const MediaStreamDevices&
+MediaCaptureDevicesImpl::GetVideoCaptureDevices() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (!devices_enumerated_) {
+ EnsureMonitorCaptureDevices();
+ devices_enumerated_ = true;
+ }
+ return video_devices_;
+}
+
+void MediaCaptureDevicesImpl::OnAudioCaptureDevicesChanged(
+ const MediaStreamDevices& devices) {
+ if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ UpdateAudioDevicesOnUIThread(devices);
+ } else {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&MediaCaptureDevicesImpl::UpdateAudioDevicesOnUIThread,
+ base::Unretained(this), devices));
+ }
+}
+
+void MediaCaptureDevicesImpl::OnVideoCaptureDevicesChanged(
+ const MediaStreamDevices& devices) {
+ if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ UpdateVideoDevicesOnUIThread(devices);
+ } else {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&MediaCaptureDevicesImpl::UpdateVideoDevicesOnUIThread,
+ base::Unretained(this), devices));
+ }
+}
+
+MediaCaptureDevicesImpl::MediaCaptureDevicesImpl()
+ : devices_enumerated_(false) {
+}
+
+MediaCaptureDevicesImpl::~MediaCaptureDevicesImpl() {
+}
+
+void MediaCaptureDevicesImpl::UpdateAudioDevicesOnUIThread(
+ const MediaStreamDevices& devices) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ devices_enumerated_ = true;
+ audio_devices_ = devices;
+}
+
+void MediaCaptureDevicesImpl::UpdateVideoDevicesOnUIThread(
+ const MediaStreamDevices& devices) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ devices_enumerated_ = true;
+ video_devices_ = devices;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/renderer_host/media/media_capture_devices_impl.h b/chromium/content/browser/renderer_host/media/media_capture_devices_impl.h
new file mode 100644
index 00000000000..078f659608b
--- /dev/null
+++ b/chromium/content/browser/renderer_host/media/media_capture_devices_impl.h
@@ -0,0 +1,49 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_CAPTURE_DEVICES_H
+#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_CAPTURE_DEVICES_H
+
+#include "base/memory/singleton.h"
+#include "content/public/browser/media_capture_devices.h"
+
+namespace content {
+
+class MediaCaptureDevicesImpl : public MediaCaptureDevices {
+ public:
+ static MediaCaptureDevicesImpl* GetInstance();
+
+ // Overriden from MediaCaptureDevices
+ virtual const MediaStreamDevices& GetAudioCaptureDevices() OVERRIDE;
+ virtual const MediaStreamDevices& GetVideoCaptureDevices() OVERRIDE;
+
+ // Called by MediaStreamManager to notify the change of media capture
+ // devices, these 2 methods are called in IO thread.
+ void OnAudioCaptureDevicesChanged(const MediaStreamDevices& devices);
+ void OnVideoCaptureDevicesChanged(const MediaStreamDevices& devices);
+
+ private:
+ friend struct DefaultSingletonTraits<MediaCaptureDevicesImpl>;
+ MediaCaptureDevicesImpl();
+ virtual ~MediaCaptureDevicesImpl();
+
+ void UpdateAudioDevicesOnUIThread(const content::MediaStreamDevices& devices);
+ void UpdateVideoDevicesOnUIThread(const content::MediaStreamDevices& devices);
+
+ // Flag to indicate if device enumeration has been done/doing.
+ // Only accessed on UI thread.
+ bool devices_enumerated_;
+
+ // A list of cached audio capture devices.
+ MediaStreamDevices audio_devices_;
+
+ // A list of cached video capture devices.
+ MediaStreamDevices video_devices_;
+
+ DISALLOW_COPY_AND_ASSIGN(MediaCaptureDevicesImpl);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_CAPTURE_DEVICES_H
diff --git a/chromium/content/browser/renderer_host/media/media_stream_dispatcher_host.cc b/chromium/content/browser/renderer_host/media/media_stream_dispatcher_host.cc
index 68f1c362c70..91fad34fe5c 100644
--- a/chromium/content/browser/renderer_host/media/media_stream_dispatcher_host.cc
+++ b/chromium/content/browser/renderer_host/media/media_stream_dispatcher_host.cc
@@ -5,9 +5,10 @@
#include "content/browser/renderer_host/media/media_stream_dispatcher_host.h"
#include "content/browser/browser_main_loop.h"
-#include "content/browser/renderer_host/media/web_contents_capture_util.h"
+#include "content/browser/child_process_security_policy_impl.h"
#include "content/common/media/media_stream_messages.h"
#include "content/common/media/media_stream_options.h"
+#include "content/public/browser/render_process_host.h"
#include "url/gurl.h"
namespace content {
@@ -15,10 +16,13 @@ namespace content {
MediaStreamDispatcherHost::MediaStreamDispatcherHost(
int render_process_id,
const ResourceContext::SaltCallback& salt_callback,
- MediaStreamManager* media_stream_manager)
- : render_process_id_(render_process_id),
+ MediaStreamManager* media_stream_manager,
+ ResourceContext* resource_context)
+ : BrowserMessageFilter(MediaStreamMsgStart),
+ render_process_id_(render_process_id),
salt_callback_(salt_callback),
- media_stream_manager_(media_stream_manager) {
+ media_stream_manager_(media_stream_manager),
+ resource_context_(resource_context) {
}
void MediaStreamDispatcherHost::StreamGenerated(
@@ -27,7 +31,7 @@ void MediaStreamDispatcherHost::StreamGenerated(
const std::string& label,
const StreamDeviceInfoArray& audio_devices,
const StreamDeviceInfoArray& video_devices) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DVLOG(1) << "MediaStreamDispatcherHost::StreamGenerated("
<< ", {label = " << label << "})";
@@ -36,21 +40,25 @@ void MediaStreamDispatcherHost::StreamGenerated(
video_devices));
}
-void MediaStreamDispatcherHost::StreamGenerationFailed(int render_view_id,
- int page_request_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+void MediaStreamDispatcherHost::StreamGenerationFailed(
+ int render_view_id,
+ int page_request_id,
+ content::MediaStreamRequestResult result) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DVLOG(1) << "MediaStreamDispatcherHost::StreamGenerationFailed("
- << ", {page_request_id = " << page_request_id << "})";
+ << ", {page_request_id = " << page_request_id << "}"
+ << ", { result= " << result << "})";
Send(new MediaStreamMsg_StreamGenerationFailed(render_view_id,
- page_request_id));
+ page_request_id,
+ result));
}
void MediaStreamDispatcherHost::DeviceStopped(int render_view_id,
const std::string& label,
const StreamDeviceInfo& device) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DVLOG(1) << "MediaStreamDispatcherHost::DeviceStopped("
<< "{label = " << label << "}, "
<< "{type = " << device.device.type << "}, "
@@ -64,7 +72,7 @@ void MediaStreamDispatcherHost::DevicesEnumerated(
int page_request_id,
const std::string& label,
const StreamDeviceInfoArray& devices) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DVLOG(1) << "MediaStreamDispatcherHost::DevicesEnumerated("
<< ", {page_request_id = " << page_request_id << "})";
@@ -77,7 +85,7 @@ void MediaStreamDispatcherHost::DeviceOpened(
int page_request_id,
const std::string& label,
const StreamDeviceInfo& video_device) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DVLOG(1) << "MediaStreamDispatcherHost::DeviceOpened("
<< ", {page_request_id = " << page_request_id << "})";
@@ -85,10 +93,9 @@ void MediaStreamDispatcherHost::DeviceOpened(
render_view_id, page_request_id, label, video_device));
}
-bool MediaStreamDispatcherHost::OnMessageReceived(
- const IPC::Message& message, bool* message_was_ok) {
+bool MediaStreamDispatcherHost::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(MediaStreamDispatcherHost, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(MediaStreamDispatcherHost, message)
IPC_MESSAGE_HANDLER(MediaStreamHostMsg_GenerateStream, OnGenerateStream)
IPC_MESSAGE_HANDLER(MediaStreamHostMsg_CancelGenerateStream,
OnCancelGenerateStream)
@@ -103,14 +110,14 @@ bool MediaStreamDispatcherHost::OnMessageReceived(
IPC_MESSAGE_HANDLER(MediaStreamHostMsg_CloseDevice,
OnCloseDevice)
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return handled;
}
void MediaStreamDispatcherHost::OnChannelClosing() {
DVLOG(1) << "MediaStreamDispatcherHost::OnChannelClosing";
- // Since the IPC channel is gone, close all requesting/requested streams.
+ // Since the IPC sender is gone, close all requesting/requested streams.
media_stream_manager_->CancelAllRequests(render_process_id_);
}
@@ -121,18 +128,23 @@ void MediaStreamDispatcherHost::OnGenerateStream(
int render_view_id,
int page_request_id,
const StreamOptions& components,
- const GURL& security_origin) {
+ const GURL& security_origin,
+ bool user_gesture) {
DVLOG(1) << "MediaStreamDispatcherHost::OnGenerateStream("
<< render_view_id << ", "
<< page_request_id << ", ["
<< " audio:" << components.audio_requested
<< " video:" << components.video_requested
<< " ], "
- << security_origin.spec() << ")";
+ << security_origin.spec()
+ << ", " << user_gesture << ")";
+
+ if (!IsURLAllowed(security_origin))
+ return;
media_stream_manager_->GenerateStream(
this, render_process_id_, render_view_id, salt_callback_,
- page_request_id, components, security_origin);
+ page_request_id, components, security_origin, user_gesture);
}
void MediaStreamDispatcherHost::OnCancelGenerateStream(int render_view_id,
@@ -158,16 +170,32 @@ void MediaStreamDispatcherHost::OnEnumerateDevices(
int render_view_id,
int page_request_id,
MediaStreamType type,
- const GURL& security_origin) {
+ const GURL& security_origin,
+ bool hide_labels_if_no_access) {
DVLOG(1) << "MediaStreamDispatcherHost::OnEnumerateDevices("
<< render_view_id << ", "
<< page_request_id << ", "
<< type << ", "
<< security_origin.spec() << ")";
+ if (!IsURLAllowed(security_origin))
+ return;
+
+ DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE ||
+ type == MEDIA_DEVICE_VIDEO_CAPTURE ||
+ type == MEDIA_DEVICE_AUDIO_OUTPUT);
+ bool have_permission = true;
+ if (hide_labels_if_no_access) {
+ bool audio_type = type == MEDIA_DEVICE_AUDIO_CAPTURE ||
+ type == MEDIA_DEVICE_AUDIO_OUTPUT;
+ have_permission = audio_type ?
+ resource_context_->AllowMicAccess(security_origin) :
+ resource_context_->AllowCameraAccess(security_origin);
+ }
+
media_stream_manager_->EnumerateDevices(
this, render_process_id_, render_view_id, salt_callback_,
- page_request_id, type, security_origin);
+ page_request_id, type, security_origin, have_permission);
}
void MediaStreamDispatcherHost::OnCancelEnumerateDevices(
@@ -193,10 +221,12 @@ void MediaStreamDispatcherHost::OnOpenDevice(
<< type << ", "
<< security_origin.spec() << ")";
+ if (!IsURLAllowed(security_origin))
+ return;
+
media_stream_manager_->OpenDevice(
this, render_process_id_, render_view_id, salt_callback_,
page_request_id, device_id, type, security_origin);
-
}
void MediaStreamDispatcherHost::OnCloseDevice(
@@ -209,4 +239,14 @@ void MediaStreamDispatcherHost::OnCloseDevice(
media_stream_manager_->CancelRequest(label);
}
+bool MediaStreamDispatcherHost::IsURLAllowed(const GURL& url) {
+ if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanRequestURL(
+ render_process_id_, url)) {
+ LOG(ERROR) << "MSDH: Renderer requested a URL it's not allowed to use.";
+ return false;
+ }
+
+ return true;
+}
+
} // namespace content
diff --git a/chromium/content/browser/renderer_host/media/media_stream_dispatcher_host.h b/chromium/content/browser/renderer_host/media/media_stream_dispatcher_host.h
index 556a6c187c0..b599c39c951 100644
--- a/chromium/content/browser/renderer_host/media/media_stream_dispatcher_host.h
+++ b/chromium/content/browser/renderer_host/media/media_stream_dispatcher_host.h
@@ -19,6 +19,7 @@
namespace content {
class MediaStreamManager;
+class ResourceContext;
// MediaStreamDispatcherHost is a delegate for Media Stream API messages used by
// MediaStreamImpl. It's the complement of MediaStreamDispatcher
@@ -29,7 +30,8 @@ class CONTENT_EXPORT MediaStreamDispatcherHost : public BrowserMessageFilter,
MediaStreamDispatcherHost(
int render_process_id,
const ResourceContext::SaltCallback& salt_callback,
- MediaStreamManager* media_stream_manager);
+ MediaStreamManager* media_stream_manager,
+ ResourceContext* resource_context);
// MediaStreamRequester implementation.
virtual void StreamGenerated(
@@ -38,8 +40,10 @@ class CONTENT_EXPORT MediaStreamDispatcherHost : public BrowserMessageFilter,
const std::string& label,
const StreamDeviceInfoArray& audio_devices,
const StreamDeviceInfoArray& video_devices) OVERRIDE;
- virtual void StreamGenerationFailed(int render_view_id,
- int page_request_id) OVERRIDE;
+ virtual void StreamGenerationFailed(
+ int render_view_id,
+ int page_request_id,
+ content::MediaStreamRequestResult result) OVERRIDE;
virtual void DeviceStopped(int render_view_id,
const std::string& label,
const StreamDeviceInfo& device) OVERRIDE;
@@ -53,8 +57,7 @@ class CONTENT_EXPORT MediaStreamDispatcherHost : public BrowserMessageFilter,
const StreamDeviceInfo& video_device) OVERRIDE;
// BrowserMessageFilter implementation.
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
virtual void OnChannelClosing() OVERRIDE;
protected:
@@ -66,7 +69,8 @@ class CONTENT_EXPORT MediaStreamDispatcherHost : public BrowserMessageFilter,
void OnGenerateStream(int render_view_id,
int page_request_id,
const StreamOptions& components,
- const GURL& security_origin);
+ const GURL& security_origin,
+ bool user_gesture);
void OnCancelGenerateStream(int render_view_id,
int page_request_id);
void OnStopStreamDevice(int render_view_id,
@@ -75,7 +79,8 @@ class CONTENT_EXPORT MediaStreamDispatcherHost : public BrowserMessageFilter,
void OnEnumerateDevices(int render_view_id,
int page_request_id,
MediaStreamType type,
- const GURL& security_origin);
+ const GURL& security_origin,
+ bool hide_labels_if_no_access);
void OnCancelEnumerateDevices(int render_view_id,
int page_request_id);
@@ -91,12 +96,17 @@ class CONTENT_EXPORT MediaStreamDispatcherHost : public BrowserMessageFilter,
void StoreRequest(int render_view_id,
int page_request_id,
- const std::string& label);;
+ const std::string& label);
+
+ bool IsURLAllowed(const GURL& url);
int render_process_id_;
ResourceContext::SaltCallback salt_callback_;
MediaStreamManager* media_stream_manager_;
+ // Owned by ProfileIOData which is guaranteed to outlive MSDH.
+ ResourceContext* const resource_context_;
+
DISALLOW_COPY_AND_ASSIGN(MediaStreamDispatcherHost);
};
diff --git a/chromium/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc b/chromium/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc
index c8132d177cd..a7edd38fc25 100644
--- a/chromium/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc
+++ b/chromium/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc
@@ -7,15 +7,19 @@
#include "base/bind.h"
#include "base/callback_helpers.h"
+#include "base/command_line.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "content/browser/browser_thread_impl.h"
+#include "content/browser/renderer_host/media/audio_input_device_manager.h"
#include "content/browser/renderer_host/media/media_stream_dispatcher_host.h"
#include "content/browser/renderer_host/media/media_stream_manager.h"
#include "content/browser/renderer_host/media/media_stream_ui_proxy.h"
+#include "content/browser/renderer_host/media/video_capture_manager.h"
#include "content/common/media/media_stream_messages.h"
#include "content/common/media/media_stream_options.h"
#include "content/public/browser/media_device_id.h"
+#include "content/public/common/content_switches.h"
#include "content/public/test/mock_resource_context.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_browser_thread_bundle.h"
@@ -23,7 +27,8 @@
#include "content/test/test_content_client.h"
#include "ipc/ipc_message_macros.h"
#include "media/audio/mock_audio_manager.h"
-#include "media/video/capture/fake_video_capture_device.h"
+#include "media/base/media_switches.h"
+#include "media/video/capture/fake_video_capture_device_factory.h"
#include "net/url_request/url_request_context.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -31,6 +36,7 @@
using ::testing::_;
using ::testing::DeleteArg;
using ::testing::DoAll;
+using ::testing::InSequence;
using ::testing::Return;
using ::testing::SaveArg;
@@ -46,15 +52,20 @@ class MockMediaStreamDispatcherHost : public MediaStreamDispatcherHost,
MockMediaStreamDispatcherHost(
const ResourceContext::SaltCallback salt_callback,
const scoped_refptr<base::MessageLoopProxy>& message_loop,
- MediaStreamManager* manager)
- : MediaStreamDispatcherHost(kProcessId, salt_callback, manager),
- message_loop_(message_loop) {}
+ MediaStreamManager* manager,
+ ResourceContext* resource_context)
+ : MediaStreamDispatcherHost(kProcessId, salt_callback, manager,
+ resource_context),
+ message_loop_(message_loop),
+ current_ipc_(NULL) {}
// A list of mock methods.
MOCK_METHOD4(OnStreamGenerated,
void(int routing_id, int request_id, int audio_array_size,
int video_array_size));
- MOCK_METHOD2(OnStreamGenerationFailed, void(int routing_id, int request_id));
+ MOCK_METHOD3(OnStreamGenerationFailed, void(int routing_id,
+ int request_id,
+ MediaStreamRequestResult result));
MOCK_METHOD1(OnDeviceStopped, void(int routing_id));
MOCK_METHOD2(OnDeviceOpened, void(int routing_id, int request_id));
@@ -66,7 +77,7 @@ class MockMediaStreamDispatcherHost : public MediaStreamDispatcherHost,
const base::Closure& quit_closure) {
quit_closures_.push(quit_closure);
MediaStreamDispatcherHost::OnGenerateStream(
- render_view_id, page_request_id, components, security_origin);
+ render_view_id, page_request_id, components, security_origin, false);
}
void OnStopStreamDevice(int render_view_id,
@@ -89,10 +100,12 @@ class MockMediaStreamDispatcherHost : public MediaStreamDispatcherHost,
int page_request_id,
MediaStreamType type,
const GURL& security_origin,
+ bool hide_labels_if_no_access,
const base::Closure& quit_closure) {
quit_closures_.push(quit_closure);
MediaStreamDispatcherHost::OnEnumerateDevices(
- render_view_id, page_request_id, type, security_origin);
+ render_view_id, page_request_id, type, security_origin,
+ hide_labels_if_no_access);
}
std::string label_;
@@ -109,35 +122,36 @@ class MockMediaStreamDispatcherHost : public MediaStreamDispatcherHost,
// conversation between this object and the renderer.
virtual bool Send(IPC::Message* message) OVERRIDE {
CHECK(message);
+ current_ipc_ = message;
// In this method we dispatch the messages to the according handlers as if
// we are the renderer.
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(MockMediaStreamDispatcherHost, *message)
- IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerated, OnStreamGenerated)
+ IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerated,
+ OnStreamGeneratedInternal)
IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerationFailed,
- OnStreamGenerationFailed)
- IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceStopped, OnDeviceStopped)
- IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceOpened, OnDeviceOpened)
- IPC_MESSAGE_HANDLER(MediaStreamMsg_DevicesEnumerated,
- OnDevicesEnumerated)
+ OnStreamGenerationFailedInternal)
+ IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceStopped, OnDeviceStoppedInternal)
+ IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceOpened, OnDeviceOpenedInternal)
+ IPC_MESSAGE_HANDLER(MediaStreamMsg_DevicesEnumerated, OnDevicesEnumerated)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
EXPECT_TRUE(handled);
delete message;
+ current_ipc_ = NULL;
return true;
}
// These handler methods do minimal things and delegate to the mock methods.
- void OnStreamGenerated(
- const IPC::Message& msg,
+ void OnStreamGeneratedInternal(
int request_id,
std::string label,
StreamDeviceInfoArray audio_device_list,
StreamDeviceInfoArray video_device_list) {
- OnStreamGenerated(msg.routing_id(), request_id, audio_device_list.size(),
- video_device_list.size());
+ OnStreamGenerated(current_ipc_->routing_id(), request_id,
+ audio_device_list.size(), video_device_list.size());
// Notify that the event have occurred.
base::Closure quit_closure = quit_closures_.front();
quit_closures_.pop();
@@ -148,8 +162,10 @@ class MockMediaStreamDispatcherHost : public MediaStreamDispatcherHost,
video_devices_ = video_device_list;
}
- void OnStreamGenerationFailed(const IPC::Message& msg, int request_id) {
- OnStreamGenerationFailed(msg.routing_id(), request_id);
+ void OnStreamGenerationFailedInternal(
+ int request_id,
+ content::MediaStreamRequestResult result) {
+ OnStreamGenerationFailed(current_ipc_->routing_id(), request_id, result);
if (!quit_closures_.empty()) {
base::Closure quit_closure = quit_closures_.front();
quit_closures_.pop();
@@ -159,21 +175,19 @@ class MockMediaStreamDispatcherHost : public MediaStreamDispatcherHost,
label_= "";
}
- void OnDeviceStopped(const IPC::Message& msg,
- const std::string& label,
- const content::StreamDeviceInfo& device) {
+ void OnDeviceStoppedInternal(const std::string& label,
+ const content::StreamDeviceInfo& device) {
if (IsVideoMediaType(device.device.type))
EXPECT_TRUE(StreamDeviceInfo::IsEqual(device, video_devices_[0]));
- if (IsAudioMediaType(device.device.type))
+ if (IsAudioInputMediaType(device.device.type))
EXPECT_TRUE(StreamDeviceInfo::IsEqual(device, audio_devices_[0]));
- OnDeviceStopped(msg.routing_id());
+ OnDeviceStopped(current_ipc_->routing_id());
}
- void OnDeviceOpened(const IPC::Message& msg,
- int request_id,
- const std::string& label,
- const StreamDeviceInfo& device) {
+ void OnDeviceOpenedInternal(int request_id,
+ const std::string& label,
+ const StreamDeviceInfo& device) {
base::Closure quit_closure = quit_closures_.front();
quit_closures_.pop();
message_loop_->PostTask(FROM_HERE, base::ResetAndReturn(&quit_closure));
@@ -181,8 +195,7 @@ class MockMediaStreamDispatcherHost : public MediaStreamDispatcherHost,
opened_device_ = device;
}
- void OnDevicesEnumerated(const IPC::Message& msg,
- int request_id,
+ void OnDevicesEnumerated(int request_id,
const StreamDeviceInfoArray& devices) {
base::Closure quit_closure = quit_closures_.front();
quit_closures_.pop();
@@ -191,13 +204,16 @@ class MockMediaStreamDispatcherHost : public MediaStreamDispatcherHost,
}
scoped_refptr<base::MessageLoopProxy> message_loop_;
-
+ IPC::Message* current_ipc_;
std::queue<base::Closure> quit_closures_;
};
class MockMediaStreamUIProxy : public FakeMediaStreamUIProxy {
public:
- MOCK_METHOD1(OnStarted, void(const base::Closure& stop));
+ MOCK_METHOD2(
+ OnStarted,
+ void(const base::Closure& stop,
+ const MediaStreamUIProxy::WindowIdCallback& window_id_callback));
};
class MediaStreamDispatcherHostTest : public testing::Test {
@@ -208,15 +224,28 @@ class MediaStreamDispatcherHostTest : public testing::Test {
origin_("https://test.com") {
audio_manager_.reset(
new media::MockAudioManager(base::MessageLoopProxy::current()));
+ // Make sure we use fake devices to avoid long delays.
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kUseFakeDeviceForMediaStream);
// Create our own MediaStreamManager.
media_stream_manager_.reset(new MediaStreamManager(audio_manager_.get()));
- // Make sure we use fake devices to avoid long delays.
- media_stream_manager_->UseFakeDevice();
+ video_capture_device_factory_ =
+ static_cast<media::FakeVideoCaptureDeviceFactory*>(
+ media_stream_manager_->video_capture_manager()
+ ->video_capture_device_factory());
+ DCHECK(video_capture_device_factory_);
+
+ MockResourceContext* mock_resource_context =
+ static_cast<MockResourceContext*>(
+ browser_context_.GetResourceContext());
+ mock_resource_context->set_mic_access(true);
+ mock_resource_context->set_camera_access(true);
host_ = new MockMediaStreamDispatcherHost(
- browser_context_.GetResourceContext()->GetMediaDeviceIDSalt(),
+ mock_resource_context->GetMediaDeviceIDSalt(),
base::MessageLoopProxy::current(),
- media_stream_manager_.get());
+ media_stream_manager_.get(),
+ mock_resource_context);
// Use the fake content client and browser.
content_client_.reset(new TestContentClient());
@@ -228,10 +257,11 @@ class MediaStreamDispatcherHostTest : public testing::Test {
}
virtual void SetUp() OVERRIDE {
- media::FakeVideoCaptureDevice::GetDeviceNames(&physical_video_devices_);
+ video_capture_device_factory_->GetDeviceNames(&physical_video_devices_);
ASSERT_GT(physical_video_devices_.size(), 0u);
- audio_manager_->GetAudioInputDeviceNames(&physical_audio_devices_);
+ media_stream_manager_->audio_input_device_manager()->GetFakeDeviceNames(
+ &physical_audio_devices_);
ASSERT_GT(physical_audio_devices_.size(), 0u);
}
@@ -243,7 +273,7 @@ class MediaStreamDispatcherHostTest : public testing::Test {
virtual void SetupFakeUI(bool expect_started) {
scoped_ptr<MockMediaStreamUIProxy> stream_ui(new MockMediaStreamUIProxy());
if (expect_started) {
- EXPECT_CALL(*stream_ui, OnStarted(_));
+ EXPECT_CALL(*stream_ui, OnStarted(_, _));
}
media_stream_manager_->UseFakeUI(
stream_ui.PassAs<FakeMediaStreamUIProxy>());
@@ -271,12 +301,16 @@ class MediaStreamDispatcherHostTest : public testing::Test {
EXPECT_TRUE(DoesEveryDeviceMapToRawId(host_->video_devices_, origin_));
}
- void GenerateStreamAndWaitForFailure(int render_view_id,
- int page_request_id,
- const StreamOptions& options) {
+ void GenerateStreamAndWaitForFailure(
+ int render_view_id,
+ int page_request_id,
+ const StreamOptions& options,
+ MediaStreamRequestResult expected_result) {
base::RunLoop run_loop;
EXPECT_CALL(*host_.get(),
- OnStreamGenerationFailed(render_view_id, page_request_id));
+ OnStreamGenerationFailed(render_view_id,
+ page_request_id,
+ expected_result));
host_->OnGenerateStream(render_view_id, page_request_id, options, origin_,
run_loop.QuitClosure());
run_loop.Run();
@@ -296,10 +330,11 @@ class MediaStreamDispatcherHostTest : public testing::Test {
void EnumerateDevicesAndWaitForResult(int render_view_id,
int page_request_id,
- MediaStreamType type) {
+ MediaStreamType type,
+ bool hide_labels_if_no_access) {
base::RunLoop run_loop;
host_->OnEnumerateDevices(render_view_id, page_request_id, type, origin_,
- run_loop.QuitClosure());
+ hide_labels_if_no_access, run_loop.QuitClosure());
run_loop.Run();
ASSERT_FALSE(host_->enumerated_devices_.empty());
EXPECT_FALSE(DoesContainRawIds(host_->enumerated_devices_));
@@ -358,6 +393,24 @@ class MediaStreamDispatcherHostTest : public testing::Test {
return true;
}
+ // Returns true if all devices have labels, false otherwise.
+ bool DoesContainLabels(const StreamDeviceInfoArray& devices) {
+ for (size_t i = 0; i < devices.size(); ++i) {
+ if (devices[i].device.name.empty())
+ return false;
+ }
+ return true;
+ }
+
+ // Returns true if no devices have labels, false otherwise.
+ bool DoesNotContainLabels(const StreamDeviceInfoArray& devices) {
+ for (size_t i = 0; i < devices.size(); ++i) {
+ if (!devices[i].device.name.empty())
+ return false;
+ }
+ return true;
+ }
+
void AddSourceIdConstraint(const std::string& source_id,
StreamOptions::Constraints* constraints) {
constraints->push_back(StreamOptions::Constraint(kMediaStreamSourceInfoId,
@@ -374,6 +427,7 @@ class MediaStreamDispatcherHostTest : public testing::Test {
media::AudioDeviceNames physical_audio_devices_;
media::VideoCaptureDevice::Names physical_video_devices_;
GURL origin_;
+ media::FakeVideoCaptureDeviceFactory* video_capture_device_factory_;
};
TEST_F(MediaStreamDispatcherHostTest, GenerateStreamWithVideoOnly) {
@@ -399,7 +453,11 @@ TEST_F(MediaStreamDispatcherHostTest, GenerateStreamWithAudioOnly) {
TEST_F(MediaStreamDispatcherHostTest, GenerateStreamWithNothing) {
StreamOptions options(false, false);
- GenerateStreamAndWaitForFailure(kRenderId, kPageRequestId, options);
+ GenerateStreamAndWaitForFailure(
+ kRenderId,
+ kPageRequestId,
+ options,
+ MEDIA_DEVICE_INVALID_STATE);
}
TEST_F(MediaStreamDispatcherHostTest, GenerateStreamWithAudioAndVideo) {
@@ -510,12 +568,15 @@ TEST_F(MediaStreamDispatcherHostTest, GenerateStreamsWithoutWaiting) {
// Generate first stream.
SetupFakeUI(true);
- EXPECT_CALL(*host_.get(), OnStreamGenerated(kRenderId, kPageRequestId, 0, 1));
-
- // Generate second stream.
- EXPECT_CALL(*host_.get(),
- OnStreamGenerated(kRenderId, kPageRequestId + 1, 0, 1));
-
+ {
+ InSequence s;
+ EXPECT_CALL(*host_.get(),
+ OnStreamGenerated(kRenderId, kPageRequestId, 0, 1));
+
+ // Generate second stream.
+ EXPECT_CALL(*host_.get(),
+ OnStreamGenerated(kRenderId, kPageRequestId + 1, 0, 1));
+ }
base::RunLoop run_loop1;
base::RunLoop run_loop2;
host_->OnGenerateStream(kRenderId, kPageRequestId, options, origin_,
@@ -611,7 +672,11 @@ TEST_F(MediaStreamDispatcherHostTest,
StreamOptions options(true, true);
AddSourceIdConstraint("invalid source id", &options.mandatory_video);
- GenerateStreamAndWaitForFailure(kRenderId, kPageRequestId, options);
+ GenerateStreamAndWaitForFailure(
+ kRenderId,
+ kPageRequestId,
+ options,
+ MEDIA_DEVICE_NO_HARDWARE);
}
// Test that generating a stream with an invalid mandatory audio source id fail.
@@ -620,7 +685,11 @@ TEST_F(MediaStreamDispatcherHostTest,
StreamOptions options(true, true);
AddSourceIdConstraint("invalid source id", &options.mandatory_audio);
- GenerateStreamAndWaitForFailure(kRenderId, kPageRequestId, options);
+ GenerateStreamAndWaitForFailure(
+ kRenderId,
+ kPageRequestId,
+ options,
+ MEDIA_DEVICE_NO_HARDWARE);
}
// Test that generating a stream with an invalid optional video source id
@@ -646,17 +715,14 @@ TEST_F(MediaStreamDispatcherHostTest,
}
TEST_F(MediaStreamDispatcherHostTest, GenerateStreamsNoAvailableVideoDevice) {
- size_t number_of_fake_devices = physical_video_devices_.size();
- media::FakeVideoCaptureDevice::SetNumberOfFakeDevices(0);
- media::FakeVideoCaptureDevice::GetDeviceNames(&physical_video_devices_);
+ physical_video_devices_.clear();
+ video_capture_device_factory_->set_number_of_devices(0);
+ video_capture_device_factory_->GetDeviceNames(&physical_video_devices_);
StreamOptions options(true, true);
- SetupFakeUI(true);
- GenerateStreamAndWaitForResult(kRenderId, kPageRequestId, options);
- EXPECT_TRUE(host_->video_devices_.empty());
-
- // Reset the number of fake devices for next test.
- media::FakeVideoCaptureDevice::SetNumberOfFakeDevices(number_of_fake_devices);
+ SetupFakeUI(false);
+ GenerateStreamAndWaitForFailure(kRenderId, kPageRequestId, options,
+ MEDIA_DEVICE_NO_HARDWARE);
}
// Test that if a OnStopStreamDevice message is received for a device that has
@@ -721,6 +787,30 @@ TEST_F(MediaStreamDispatcherHostTest, StopDeviceInStreamAndRestart) {
request2_devices[1]));
}
+TEST_F(MediaStreamDispatcherHostTest,
+ GenerateTwoStreamsAndStopDeviceWhileWaitingForSecondStream) {
+ StreamOptions options(false, true);
+
+ SetupFakeUI(true);
+ GenerateStreamAndWaitForResult(kRenderId, kPageRequestId, options);
+ EXPECT_EQ(host_->video_devices_.size(), 1u);
+
+ // Generate a second stream.
+ EXPECT_CALL(*host_.get(),
+ OnStreamGenerated(kRenderId, kPageRequestId + 1, 0, 1));
+
+ base::RunLoop run_loop1;
+ host_->OnGenerateStream(kRenderId, kPageRequestId + 1, options, origin_,
+ run_loop1.QuitClosure());
+
+ // Stop the video stream device from stream 1 while waiting for the
+ // second stream to be generated.
+ host_->OnStopStreamDevice(kRenderId, host_->video_devices_[0].device.id);
+ run_loop1.Run();
+
+ EXPECT_EQ(host_->video_devices_.size(), 1u);
+}
+
TEST_F(MediaStreamDispatcherHostTest, CancelPendingStreamsOnChannelClosing) {
StreamOptions options(false, true);
@@ -758,7 +848,7 @@ TEST_F(MediaStreamDispatcherHostTest, CloseFromUI) {
base::Closure close_callback;
scoped_ptr<MockMediaStreamUIProxy> stream_ui(new MockMediaStreamUIProxy());
- EXPECT_CALL(*stream_ui, OnStarted(_))
+ EXPECT_CALL(*stream_ui, OnStarted(_, _))
.WillOnce(SaveArg<0>(&close_callback));
media_stream_manager_->UseFakeUI(stream_ui.PassAs<FakeMediaStreamUIProxy>());
@@ -776,14 +866,13 @@ TEST_F(MediaStreamDispatcherHostTest, CloseFromUI) {
// Test that the dispatcher is notified if a video device that is in use is
// being unplugged.
TEST_F(MediaStreamDispatcherHostTest, VideoDeviceUnplugged) {
- size_t number_of_fake_devices = physical_video_devices_.size();
StreamOptions options(true, true);
SetupFakeUI(true);
GenerateStreamAndWaitForResult(kRenderId, kPageRequestId, options);
EXPECT_EQ(host_->audio_devices_.size(), 1u);
EXPECT_EQ(host_->video_devices_.size(), 1u);
- media::FakeVideoCaptureDevice::SetNumberOfFakeDevices(0);
+ video_capture_device_factory_->set_number_of_devices(0);
base::RunLoop run_loop;
EXPECT_CALL(*host_.get(), OnDeviceStopped(kRenderId))
@@ -792,18 +881,56 @@ TEST_F(MediaStreamDispatcherHostTest, VideoDeviceUnplugged) {
base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE);
run_loop.Run();
-
- media::FakeVideoCaptureDevice::SetNumberOfFakeDevices(number_of_fake_devices);
}
TEST_F(MediaStreamDispatcherHostTest, EnumerateAudioDevices) {
EnumerateDevicesAndWaitForResult(kRenderId, kPageRequestId,
- MEDIA_DEVICE_AUDIO_CAPTURE);
+ MEDIA_DEVICE_AUDIO_CAPTURE, true);
+ EXPECT_TRUE(DoesContainLabels(host_->enumerated_devices_));
}
TEST_F(MediaStreamDispatcherHostTest, EnumerateVideoDevices) {
EnumerateDevicesAndWaitForResult(kRenderId, kPageRequestId,
- MEDIA_DEVICE_VIDEO_CAPTURE);
+ MEDIA_DEVICE_VIDEO_CAPTURE, true);
+ EXPECT_TRUE(DoesContainLabels(host_->enumerated_devices_));
+}
+
+TEST_F(MediaStreamDispatcherHostTest, EnumerateAudioDevicesNoAccessHideLabels) {
+ MockResourceContext* mock_resource_context =
+ static_cast<MockResourceContext*>(browser_context_.GetResourceContext());
+ mock_resource_context->set_mic_access(false);
+ EnumerateDevicesAndWaitForResult(kRenderId, kPageRequestId,
+ MEDIA_DEVICE_AUDIO_CAPTURE, true);
+ EXPECT_TRUE(DoesNotContainLabels(host_->enumerated_devices_));
+}
+
+TEST_F(MediaStreamDispatcherHostTest, EnumerateVideoDevicesNoAccessHideLabels) {
+ MockResourceContext* mock_resource_context =
+ static_cast<MockResourceContext*>(browser_context_.GetResourceContext());
+ mock_resource_context->set_camera_access(false);
+ EnumerateDevicesAndWaitForResult(kRenderId, kPageRequestId,
+ MEDIA_DEVICE_VIDEO_CAPTURE, true);
+ EXPECT_TRUE(DoesNotContainLabels(host_->enumerated_devices_));
+}
+
+TEST_F(MediaStreamDispatcherHostTest,
+ EnumerateAudioDevicesNoAccessNoHideLabels) {
+ MockResourceContext* mock_resource_context =
+ static_cast<MockResourceContext*>(browser_context_.GetResourceContext());
+ mock_resource_context->set_mic_access(false);
+ EnumerateDevicesAndWaitForResult(kRenderId, kPageRequestId,
+ MEDIA_DEVICE_AUDIO_CAPTURE, false);
+ EXPECT_TRUE(DoesContainLabels(host_->enumerated_devices_));
+}
+
+TEST_F(MediaStreamDispatcherHostTest,
+ EnumerateVideoDevicesNoAccessNoHideLabels) {
+ MockResourceContext* mock_resource_context =
+ static_cast<MockResourceContext*>(browser_context_.GetResourceContext());
+ mock_resource_context->set_camera_access(false);
+ EnumerateDevicesAndWaitForResult(kRenderId, kPageRequestId,
+ MEDIA_DEVICE_VIDEO_CAPTURE, false);
+ EXPECT_TRUE(DoesContainLabels(host_->enumerated_devices_));
}
}; // namespace content
diff --git a/chromium/content/browser/renderer_host/media/media_stream_manager.cc b/chromium/content/browser/renderer_host/media/media_stream_manager.cc
index f6f62fd4d76..2b337cafa78 100644
--- a/chromium/content/browser/renderer_host/media/media_stream_manager.cc
+++ b/chromium/content/browser/renderer_host/media/media_stream_manager.cc
@@ -11,25 +11,33 @@
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
+#include "base/power_monitor/power_monitor.h"
#include "base/rand_util.h"
#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
#include "base/threading/thread.h"
+#include "content/browser/browser_main_loop.h"
+#include "content/browser/media/capture/web_contents_capture_util.h"
#include "content/browser/renderer_host/media/audio_input_device_manager.h"
#include "content/browser/renderer_host/media/device_request_message_filter.h"
+#include "content/browser/renderer_host/media/media_capture_devices_impl.h"
#include "content/browser/renderer_host/media/media_stream_requester.h"
#include "content/browser/renderer_host/media/media_stream_ui_proxy.h"
#include "content/browser/renderer_host/media/video_capture_manager.h"
-#include "content/browser/renderer_host/media/web_contents_capture_util.h"
+#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/media_device_id.h"
#include "content/public/browser/media_observer.h"
#include "content/public/browser/media_request_state.h"
+#include "content/public/browser/render_process_host.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/media_stream_request.h"
#include "media/audio/audio_manager_base.h"
#include "media/audio/audio_parameters.h"
#include "media/base/channel_layout.h"
+#include "media/base/media_switches.h"
+#include "media/video/capture/video_capture_device_factory.h"
#include "url/gurl.h"
#if defined(OS_WIN)
@@ -38,6 +46,13 @@
namespace content {
+// Forward declaration of DeviceMonitorMac and its only useable method.
+class DeviceMonitorMac {
+ public:
+ void StartMonitoring(
+ const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner);
+};
+
namespace {
// Creates a random label used to identify requests.
std::string RandomLabel() {
@@ -78,7 +93,7 @@ void ParseStreamType(const StreamOptions& options,
}
} else {
// This is normal audio device capture.
- *audio_type = content::MEDIA_DEVICE_AUDIO_CAPTURE;
+ *audio_type = MEDIA_DEVICE_AUDIO_CAPTURE;
}
}
if (options.video_requested) {
@@ -98,16 +113,63 @@ void ParseStreamType(const StreamOptions& options,
}
} else {
// This is normal video device capture.
- *video_type = content::MEDIA_DEVICE_VIDEO_CAPTURE;
+ *video_type = MEDIA_DEVICE_VIDEO_CAPTURE;
}
}
}
+// Private helper method for SendMessageToNativeLog() that obtains the global
+// MediaStreamManager instance on the UI thread before sending |message| to the
+// webrtcLoggingPrivate API.
+void DoAddLogMessage(const std::string& message) {
+ // Must be on the UI thread to access BrowserMainLoop.
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ // May be null in tests.
+ // TODO(vrk): Handle this more elegantly by having native log messages become
+ // no-ops until MediaStreamManager is aware that a renderer process has
+ // started logging. crbug.com/333894
+ if (content::BrowserMainLoop::GetInstance()) {
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&MediaStreamManager::AddLogMessageOnIOThread,
+ base::Unretained(content::BrowserMainLoop::GetInstance()
+ ->media_stream_manager()),
+ message));
+ }
+}
+
+// Private helper method to generate a string for the log message that lists the
+// human readable names of |devices|.
+std::string GetLogMessageString(MediaStreamType stream_type,
+ const StreamDeviceInfoArray& devices) {
+ std::string output_string =
+ base::StringPrintf("Getting devices for stream type %d:\n", stream_type);
+ if (devices.empty()) {
+ output_string += "No devices found.";
+ } else {
+ for (StreamDeviceInfoArray::const_iterator it = devices.begin();
+ it != devices.end(); ++it) {
+ output_string += " " + it->device.name + "\n";
+ }
+ }
+ return output_string;
+}
+
// Needed for MediaStreamManager::GenerateStream below.
std::string ReturnEmptySalt() {
return std::string();
}
+// Clears the MediaStreamDevice.name from all devices in |devices|.
+static void ClearDeviceLabels(content::StreamDeviceInfoArray* devices) {
+ for (content::StreamDeviceInfoArray::iterator device_itr = devices->begin();
+ device_itr != devices->end();
+ ++device_itr) {
+ device_itr->device.name.clear();
+ }
+}
+
} // namespace
@@ -124,6 +186,8 @@ class MediaStreamManager::DeviceRequest {
int requesting_view_id,
int page_request_id,
const GURL& security_origin,
+ bool have_permission,
+ bool user_gesture,
MediaStreamRequestType request_type,
const StreamOptions& options,
const ResourceContext::SaltCallback& salt_callback)
@@ -132,6 +196,8 @@ class MediaStreamManager::DeviceRequest {
requesting_view_id(requesting_view_id),
page_request_id(page_request_id),
security_origin(security_origin),
+ have_permission(have_permission),
+ user_gesture(user_gesture),
request_type(request_type),
options(options),
salt_callback(salt_callback),
@@ -143,7 +209,9 @@ class MediaStreamManager::DeviceRequest {
~DeviceRequest() {}
void SetAudioType(MediaStreamType audio_type) {
- DCHECK(IsAudioMediaType(audio_type) || audio_type == MEDIA_NO_SERVICE);
+ DCHECK(IsAudioInputMediaType(audio_type) ||
+ audio_type == MEDIA_DEVICE_AUDIO_OUTPUT ||
+ audio_type == MEDIA_NO_SERVICE);
audio_type_ = audio_type;
}
@@ -165,6 +233,7 @@ class MediaStreamManager::DeviceRequest {
requesting_view_id,
page_request_id,
security_origin,
+ user_gesture,
request_type,
requested_audio_device_id,
requested_video_device_id,
@@ -182,6 +251,7 @@ class MediaStreamManager::DeviceRequest {
target_render_view_id,
page_request_id,
security_origin,
+ user_gesture,
request_type,
"",
"",
@@ -222,7 +292,7 @@ class MediaStreamManager::DeviceRequest {
media_observer->OnMediaRequestStateChanged(
ui_request_->render_process_id, ui_request_->render_view_id,
- ui_request_->page_request_id,
+ ui_request_->page_request_id, ui_request_->security_origin,
MediaStreamDevice(stream_type, device_id, device_id), new_state);
}
@@ -250,6 +320,12 @@ class MediaStreamManager::DeviceRequest {
const GURL security_origin;
+ // This is used when enumerating devices; if we don't have device access
+ // permission, we remove the device label.
+ bool have_permission;
+
+ const bool user_gesture;
+
const MediaStreamRequestType request_type;
const StreamOptions options;
@@ -304,22 +380,34 @@ MediaStreamManager::MediaStreamManager(media::AudioManager* audio_manager)
base::Bind(&MediaStreamManager::InitializeDeviceManagersOnIOThread,
base::Unretained(this)));
}
+
+ base::PowerMonitor* power_monitor = base::PowerMonitor::Get();
+ // BrowserMainLoop always creates the PowerMonitor instance before creating
+ // MediaStreamManager, but power_monitor may be NULL in unit tests.
+ if (power_monitor)
+ power_monitor->AddObserver(this);
}
MediaStreamManager::~MediaStreamManager() {
DVLOG(1) << "~MediaStreamManager";
DCHECK(requests_.empty());
- DCHECK(!device_thread_.get());
+ DCHECK(!device_task_runner_);
+
+ base::PowerMonitor* power_monitor = base::PowerMonitor::Get();
+ // The PowerMonitor instance owned by BrowserMainLoops always outlives the
+ // MediaStreamManager, but it may be NULL in unit tests.
+ if (power_monitor)
+ power_monitor->RemoveObserver(this);
}
VideoCaptureManager* MediaStreamManager::video_capture_manager() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(video_capture_manager_.get());
return video_capture_manager_.get();
}
AudioInputDeviceManager* MediaStreamManager::audio_input_device_manager() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(audio_input_device_manager_.get());
return audio_input_device_manager_.get();
}
@@ -331,7 +419,7 @@ std::string MediaStreamManager::MakeMediaAccessRequest(
const StreamOptions& options,
const GURL& security_origin,
const MediaRequestResponseCallback& callback) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
// TODO(perkj): The argument list with NULL parameters to DeviceRequest
// suggests that this is the wrong design. Can this be refactored?
@@ -340,6 +428,8 @@ std::string MediaStreamManager::MakeMediaAccessRequest(
render_view_id,
page_request_id,
security_origin,
+ true,
+ false, // user gesture
MEDIA_DEVICE_ACCESS,
options,
base::Bind(&ReturnEmptySalt));
@@ -365,8 +455,9 @@ void MediaStreamManager::GenerateStream(MediaStreamRequester* requester,
const ResourceContext::SaltCallback& sc,
int page_request_id,
const StreamOptions& options,
- const GURL& security_origin) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ const GURL& security_origin,
+ bool user_gesture) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DVLOG(1) << "GenerateStream()";
if (CommandLine::ForCurrentProcess()->HasSwitch(
switches::kUseFakeUIForMediaStream)) {
@@ -378,6 +469,8 @@ void MediaStreamManager::GenerateStream(MediaStreamRequester* requester,
render_view_id,
page_request_id,
security_origin,
+ true,
+ user_gesture,
MEDIA_GENERATE_STREAM,
options,
sc);
@@ -412,7 +505,7 @@ void MediaStreamManager::CancelRequest(int render_process_id,
}
void MediaStreamManager::CancelRequest(const std::string& label) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DVLOG(1) << "CancelRequest({label = " << label << "})";
DeviceRequest* request = FindRequest(label);
if (!request) {
@@ -420,7 +513,13 @@ void MediaStreamManager::CancelRequest(const std::string& label) {
LOG(ERROR) << "The request with label = " << label << " does not exist.";
return;
}
+
if (request->request_type == MEDIA_ENUMERATE_DEVICES) {
+ // It isn't an ideal use of "CancelRequest" to make it a requirement
+ // for enumeration requests to be deleted via "CancelRequest" _after_
+ // the request has been successfully fulfilled.
+ // See note in FinalizeEnumerateDevices for a recommendation on how
+ // we should refactor this.
DeleteRequest(label);
return;
}
@@ -460,7 +559,7 @@ void MediaStreamManager::CancelAllRequests(int render_process_id) {
void MediaStreamManager::StopStreamDevice(int render_process_id,
int render_view_id,
const std::string& device_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DVLOG(1) << "StopStreamDevice({render_view_id = " << render_view_id << "} "
<< ", {device_id = " << device_id << "})";
// Find the first request for this |render_process_id| and |render_view_id|
@@ -494,6 +593,11 @@ void MediaStreamManager::StopDevice(MediaStreamType type, int session_id) {
while (request_it != requests_.end()) {
DeviceRequest* request = request_it->second;
StreamDeviceInfoArray* devices = &request->devices;
+ if (devices->empty()) {
+ // There is no device in use yet by this request.
+ ++request_it;
+ continue;
+ }
StreamDeviceInfoArray::iterator device_it = devices->begin();
while (device_it != devices->end()) {
if (device_it->device.type != type ||
@@ -501,11 +605,15 @@ void MediaStreamManager::StopDevice(MediaStreamType type, int session_id) {
++device_it;
continue;
}
+
if (request->state(type) == MEDIA_REQUEST_STATE_DONE)
CloseDevice(type, session_id);
device_it = devices->erase(device_it);
}
- // If this request doesn't have any active devices, remove the request.
+
+ // If this request doesn't have any active devices after a device
+ // has been stopped above, remove the request. Note that the request is
+ // only deleted if a device as been removed from |devices|.
if (devices->empty()) {
std::string label = request_it->first;
++request_it;
@@ -544,21 +652,25 @@ std::string MediaStreamManager::EnumerateDevices(
const ResourceContext::SaltCallback& sc,
int page_request_id,
MediaStreamType type,
- const GURL& security_origin) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ const GURL& security_origin,
+ bool have_permission) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(requester);
DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE ||
- type == MEDIA_DEVICE_VIDEO_CAPTURE);
+ type == MEDIA_DEVICE_VIDEO_CAPTURE ||
+ type == MEDIA_DEVICE_AUDIO_OUTPUT);
DeviceRequest* request = new DeviceRequest(requester,
render_process_id,
render_view_id,
page_request_id,
security_origin,
+ have_permission,
+ false, // user gesture
MEDIA_ENUMERATE_DEVICES,
StreamOptions(),
sc);
- if (IsAudioMediaType(type))
+ if (IsAudioInputMediaType(type) || type == MEDIA_DEVICE_AUDIO_OUTPUT)
request->SetAudioType(type);
else if (IsVideoMediaType(type))
request->SetVideoType(type);
@@ -577,11 +689,26 @@ std::string MediaStreamManager::EnumerateDevices(
}
void MediaStreamManager::DoEnumerateDevices(const std::string& label) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DeviceRequest* request = FindRequest(label);
if (!request)
return; // This can happen if the request has been canceled.
+ if (request->audio_type() == MEDIA_DEVICE_AUDIO_OUTPUT) {
+ DCHECK_EQ(MEDIA_NO_SERVICE, request->video_type());
+ DCHECK_GE(active_enumeration_ref_count_[MEDIA_DEVICE_AUDIO_OUTPUT], 0);
+ request->SetState(MEDIA_DEVICE_AUDIO_OUTPUT, MEDIA_REQUEST_STATE_REQUESTED);
+ if (active_enumeration_ref_count_[MEDIA_DEVICE_AUDIO_OUTPUT] == 0) {
+ ++active_enumeration_ref_count_[MEDIA_DEVICE_AUDIO_OUTPUT];
+ device_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&MediaStreamManager::EnumerateAudioOutputDevices,
+ base::Unretained(this),
+ label));
+ }
+ return;
+ }
+
MediaStreamType type;
EnumerationCache* cache;
if (request->audio_type() == MEDIA_DEVICE_AUDIO_CAPTURE) {
@@ -590,11 +717,12 @@ void MediaStreamManager::DoEnumerateDevices(const std::string& label) {
cache = &audio_enumeration_cache_;
} else {
DCHECK_EQ(MEDIA_DEVICE_VIDEO_CAPTURE, request->video_type());
+ DCHECK_EQ(MEDIA_NO_SERVICE, request->audio_type());
type = MEDIA_DEVICE_VIDEO_CAPTURE;
cache = &video_enumeration_cache_;
}
- if (cache->valid) {
+ if (!EnumerationRequired(cache, type)) {
// Cached device list of this type exists. Just send it out.
request->SetState(type, MEDIA_REQUEST_STATE_REQUESTED);
request->devices = cache->devices;
@@ -605,6 +733,57 @@ void MediaStreamManager::DoEnumerateDevices(const std::string& label) {
DVLOG(1) << "Enumerate Devices ({label = " << label << "})";
}
+void MediaStreamManager::EnumerateAudioOutputDevices(
+ const std::string& label) {
+ DCHECK(device_task_runner_->BelongsToCurrentThread());
+
+ scoped_ptr<media::AudioDeviceNames> device_names(
+ new media::AudioDeviceNames());
+ audio_manager_->GetAudioOutputDeviceNames(device_names.get());
+ StreamDeviceInfoArray devices;
+ for (media::AudioDeviceNames::iterator it = device_names->begin();
+ it != device_names->end(); ++it) {
+ StreamDeviceInfo device(MEDIA_DEVICE_AUDIO_OUTPUT,
+ it->device_name,
+ it->unique_id);
+ devices.push_back(device);
+ }
+
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&MediaStreamManager::AudioOutputDevicesEnumerated,
+ base::Unretained(this),
+ devices));
+}
+
+void MediaStreamManager::AudioOutputDevicesEnumerated(
+ const StreamDeviceInfoArray& devices) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ DVLOG(1) << "AudioOutputDevicesEnumerated()";
+
+ std::string log_message = "New device enumeration result:\n" +
+ GetLogMessageString(MEDIA_DEVICE_AUDIO_OUTPUT,
+ devices);
+ SendMessageToNativeLog(log_message);
+
+ // Publish the result for all requests waiting for device list(s).
+ for (DeviceRequests::iterator it = requests_.begin(); it != requests_.end();
+ ++it) {
+ if (it->second->state(MEDIA_DEVICE_AUDIO_OUTPUT) ==
+ MEDIA_REQUEST_STATE_REQUESTED &&
+ it->second->audio_type() == MEDIA_DEVICE_AUDIO_OUTPUT) {
+ DCHECK_EQ(MEDIA_ENUMERATE_DEVICES, it->second->request_type);
+ it->second->SetState(MEDIA_DEVICE_AUDIO_OUTPUT,
+ MEDIA_REQUEST_STATE_PENDING_APPROVAL);
+ it->second->devices = devices;
+ FinalizeEnumerateDevices(it->first, it->second);
+ }
+ }
+
+ --active_enumeration_ref_count_[MEDIA_DEVICE_AUDIO_OUTPUT];
+ DCHECK_GE(active_enumeration_ref_count_[MEDIA_DEVICE_AUDIO_OUTPUT], 0);
+}
+
void MediaStreamManager::OpenDevice(MediaStreamRequester* requester,
int render_process_id,
int render_view_id,
@@ -613,12 +792,12 @@ void MediaStreamManager::OpenDevice(MediaStreamRequester* requester,
const std::string& device_id,
MediaStreamType type,
const GURL& security_origin) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE ||
type == MEDIA_DEVICE_VIDEO_CAPTURE);
DVLOG(1) << "OpenDevice ({page_request_id = " << page_request_id << "})";
StreamOptions options;
- if (IsAudioMediaType(type)) {
+ if (IsAudioInputMediaType(type)) {
options.audio_requested = true;
options.mandatory_audio.push_back(
StreamOptions::Constraint(kMediaStreamSourceInfoId, device_id));
@@ -634,6 +813,8 @@ void MediaStreamManager::OpenDevice(MediaStreamRequester* requester,
render_view_id,
page_request_id,
security_origin,
+ true,
+ false, // user gesture
MEDIA_OPEN_DEVICE,
options,
sc);
@@ -650,10 +831,41 @@ void MediaStreamManager::OpenDevice(MediaStreamRequester* requester,
base::Unretained(this), label));
}
+bool MediaStreamManager::TranslateSourceIdToDeviceId(
+ MediaStreamType stream_type,
+ const ResourceContext::SaltCallback& sc,
+ const GURL& security_origin,
+ const std::string& source_id,
+ std::string* device_id) const {
+ DCHECK(stream_type == MEDIA_DEVICE_AUDIO_CAPTURE ||
+ stream_type == MEDIA_DEVICE_VIDEO_CAPTURE);
+ // The source_id can be empty if the constraint is set but empty.
+ if (source_id.empty())
+ return false;
+
+ const EnumerationCache* cache =
+ stream_type == MEDIA_DEVICE_AUDIO_CAPTURE ?
+ &audio_enumeration_cache_ : &video_enumeration_cache_;
+
+ // If device monitoring hasn't started, the |device_guid| is not valid.
+ if (!cache->valid)
+ return false;
+
+ for (StreamDeviceInfoArray::const_iterator it = cache->devices.begin();
+ it != cache->devices.end();
+ ++it) {
+ if (content::DoesMediaDeviceIDMatchHMAC(sc, security_origin, source_id,
+ it->device.id)) {
+ *device_id = it->device.id;
+ return true;
+ }
+ }
+ return false;
+}
+
void MediaStreamManager::EnsureDeviceMonitorStarted() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- if (!monitoring_started_)
- StartMonitoring();
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ StartMonitoring();
}
void MediaStreamManager::StopRemovedDevices(
@@ -709,25 +921,50 @@ void MediaStreamManager::StopRemovedDevice(const MediaStreamDevice& device) {
it != session_ids.end(); ++it) {
StopDevice(device.type, *it);
}
+
+ std::ostringstream oss;
+ oss << "Media input device removed: type = " <<
+ (device.type == MEDIA_DEVICE_AUDIO_CAPTURE ? "audio" : "video") <<
+ ", id = " << device.id << ", name = " << device.name;
+ AddLogMessageOnIOThread(oss.str());
}
void MediaStreamManager::StartMonitoring() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ if (monitoring_started_)
+ return;
+
if (!base::SystemMonitor::Get())
return;
- if (!monitoring_started_) {
- monitoring_started_ = true;
- base::SystemMonitor::Get()->AddDevicesChangedObserver(this);
+ monitoring_started_ = true;
+ base::SystemMonitor::Get()->AddDevicesChangedObserver(this);
+
+ // Enumerate both the audio and video devices to cache the device lists
+ // and send them to media observer.
+ ++active_enumeration_ref_count_[MEDIA_DEVICE_AUDIO_CAPTURE];
+ audio_input_device_manager_->EnumerateDevices(MEDIA_DEVICE_AUDIO_CAPTURE);
+ ++active_enumeration_ref_count_[MEDIA_DEVICE_VIDEO_CAPTURE];
+ video_capture_manager_->EnumerateDevices(MEDIA_DEVICE_VIDEO_CAPTURE);
+
+#if defined(OS_MACOSX)
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&MediaStreamManager::StartMonitoringOnUIThread,
+ base::Unretained(this)));
+#endif
+}
- // Enumerate both the audio and video devices to cache the device lists
- // and send them to media observer.
- ++active_enumeration_ref_count_[MEDIA_DEVICE_AUDIO_CAPTURE];
- audio_input_device_manager_->EnumerateDevices(MEDIA_DEVICE_AUDIO_CAPTURE);
- ++active_enumeration_ref_count_[MEDIA_DEVICE_VIDEO_CAPTURE];
- video_capture_manager_->EnumerateDevices(MEDIA_DEVICE_VIDEO_CAPTURE);
+#if defined(OS_MACOSX)
+void MediaStreamManager::StartMonitoringOnUIThread() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ BrowserMainLoop* browser_main_loop = content::BrowserMainLoop::GetInstance();
+ if (browser_main_loop) {
+ browser_main_loop->device_monitor_mac()
+ ->StartMonitoring(audio_manager_->GetWorkerTaskRunner());
}
}
+#endif
void MediaStreamManager::StopMonitoring() {
DCHECK_EQ(base::MessageLoop::current(), io_loop_);
@@ -756,7 +993,7 @@ bool MediaStreamManager::GetRequestedDeviceCaptureId(
StreamOptions::GetConstraintsByName(*mandatory,
kMediaStreamSourceInfoId, &source_ids);
if (source_ids.size() > 1) {
- LOG(ERROR) << "Only one mandatory audio " << kMediaStreamSourceInfoId
+ LOG(ERROR) << "Only one mandatory " << kMediaStreamSourceInfoId
<< " is supported.";
return false;
}
@@ -767,7 +1004,7 @@ bool MediaStreamManager::GetRequestedDeviceCaptureId(
request->salt_callback,
request->security_origin,
source_ids[0], device_id)) {
- LOG(WARNING) << "Invalid mandatory audio " << kMediaStreamSourceInfoId
+ LOG(WARNING) << "Invalid mandatory " << kMediaStreamSourceInfoId
<< " = " << source_ids[0] << ".";
return false;
}
@@ -796,6 +1033,7 @@ void MediaStreamManager::TranslateDeviceIdToSourceId(
DeviceRequest* request,
MediaStreamDevice* device) {
if (request->audio_type() == MEDIA_DEVICE_AUDIO_CAPTURE ||
+ request->audio_type() == MEDIA_DEVICE_AUDIO_OUTPUT ||
request->video_type() == MEDIA_DEVICE_VIDEO_CAPTURE) {
device->id = content::GetHMACForMediaDeviceID(
request->salt_callback,
@@ -804,101 +1042,97 @@ void MediaStreamManager::TranslateDeviceIdToSourceId(
}
}
-bool MediaStreamManager::TranslateSourceIdToDeviceId(
- MediaStreamType stream_type,
- const ResourceContext::SaltCallback& sc,
- const GURL& security_origin,
- const std::string& source_id,
- std::string* device_id) const {
- DCHECK(stream_type == MEDIA_DEVICE_AUDIO_CAPTURE ||
- stream_type == MEDIA_DEVICE_VIDEO_CAPTURE);
- // The source_id can be empty if the constraint is set but empty.
- if (source_id.empty())
- return false;
-
- const EnumerationCache* cache =
- stream_type == MEDIA_DEVICE_AUDIO_CAPTURE ?
- &audio_enumeration_cache_ : &video_enumeration_cache_;
+void MediaStreamManager::ClearEnumerationCache(EnumerationCache* cache) {
+ DCHECK_EQ(base::MessageLoop::current(), io_loop_);
+ cache->valid = false;
+}
- // If device monitoring hasn't started, the |device_guid| is not valid.
- if (!cache->valid)
+bool MediaStreamManager::EnumerationRequired(EnumerationCache* cache,
+ MediaStreamType stream_type) {
+ DCHECK_EQ(base::MessageLoop::current(), io_loop_);
+ if (stream_type == MEDIA_NO_SERVICE)
return false;
- for (StreamDeviceInfoArray::const_iterator it = cache->devices.begin();
- it != cache->devices.end();
- ++it) {
- if (content::DoesMediaDeviceIDMatchHMAC(sc, security_origin, source_id,
- it->device.id)) {
- *device_id = it->device.id;
- return true;
- }
- }
- return false;
-}
+ DCHECK(stream_type == MEDIA_DEVICE_AUDIO_CAPTURE ||
+ stream_type == MEDIA_DEVICE_VIDEO_CAPTURE);
-void MediaStreamManager::ClearEnumerationCache(EnumerationCache* cache) {
- DCHECK_EQ(base::MessageLoop::current(), io_loop_);
- cache->valid = false;
+#if defined(OS_ANDROID)
+ // There's no SystemMonitor on Android that notifies us when devices are
+ // added or removed, so we need to populate the cache on every request.
+ // Fortunately, there is an already up-to-date cache in the browser side
+ // audio manager that we can rely on, so the performance impact of
+ // invalidating the cache like this, is minimal.
+ if (stream_type == MEDIA_DEVICE_AUDIO_CAPTURE) {
+ // Make sure the cache is marked as invalid so that FinalizeEnumerateDevices
+ // will be called at the end of the enumeration.
+ ClearEnumerationCache(cache);
+ }
+#endif
+ // If the cache isn't valid, we need to start a full enumeration.
+ return !cache->valid;
}
void MediaStreamManager::StartEnumeration(DeviceRequest* request) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
// Start monitoring the devices when doing the first enumeration.
- if (!monitoring_started_ && base::SystemMonitor::Get()) {
- StartMonitoring();
- }
+ StartMonitoring();
// Start enumeration for devices of all requested device types.
- if (request->audio_type() != MEDIA_NO_SERVICE) {
- const MediaStreamType stream_type = request->audio_type();
- request->SetState(stream_type, MEDIA_REQUEST_STATE_REQUESTED);
- DCHECK_GE(active_enumeration_ref_count_[stream_type], 0);
- if (active_enumeration_ref_count_[stream_type] == 0) {
- ++active_enumeration_ref_count_[stream_type];
- GetDeviceManager(stream_type)->EnumerateDevices(stream_type);
- }
- }
- if (request->video_type() != MEDIA_NO_SERVICE) {
- const MediaStreamType stream_type = request->video_type();
- request->SetState(stream_type, MEDIA_REQUEST_STATE_REQUESTED);
- DCHECK_GE(active_enumeration_ref_count_[stream_type], 0);
- if (active_enumeration_ref_count_[stream_type] == 0) {
- ++active_enumeration_ref_count_[stream_type];
- GetDeviceManager(stream_type)->EnumerateDevices(stream_type);
+ const MediaStreamType streams[] = { request->audio_type(),
+ request->video_type() };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(streams); ++i) {
+ if (streams[i] == MEDIA_NO_SERVICE)
+ continue;
+ request->SetState(streams[i], MEDIA_REQUEST_STATE_REQUESTED);
+ DCHECK_GE(active_enumeration_ref_count_[streams[i]], 0);
+ if (active_enumeration_ref_count_[streams[i]] == 0) {
+ ++active_enumeration_ref_count_[streams[i]];
+ GetDeviceManager(streams[i])->EnumerateDevices(streams[i]);
}
}
}
std::string MediaStreamManager::AddRequest(DeviceRequest* request) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
// Create a label for this request and verify it is unique.
std::string unique_label;
do {
unique_label = RandomLabel();
- } while (requests_.find(unique_label) != requests_.end());
+ } while (FindRequest(unique_label) != NULL);
- requests_.insert(std::make_pair(unique_label, request));
+ requests_.push_back(std::make_pair(unique_label, request));
return unique_label;
}
MediaStreamManager::DeviceRequest*
MediaStreamManager::FindRequest(const std::string& label) const {
- DeviceRequests::const_iterator request_it = requests_.find(label);
- return request_it == requests_.end() ? NULL : request_it->second;
+ for (DeviceRequests::const_iterator request_it = requests_.begin();
+ request_it != requests_.end(); ++request_it) {
+ if (request_it->first == label)
+ return request_it->second;
+ }
+ return NULL;
}
void MediaStreamManager::DeleteRequest(const std::string& label) {
- DeviceRequests::iterator it = requests_.find(label);
- scoped_ptr<DeviceRequest> request(it->second);
- requests_.erase(it);
+ DVLOG(1) << "DeleteRequest({label= " << label << "})";
+ for (DeviceRequests::iterator request_it = requests_.begin();
+ request_it != requests_.end(); ++request_it) {
+ if (request_it->first == label) {
+ scoped_ptr<DeviceRequest> request(request_it->second);
+ requests_.erase(request_it);
+ return;
+ }
+ }
+ NOTREACHED();
}
void MediaStreamManager::PostRequestToUI(const std::string& label,
DeviceRequest* request) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(request->UIRequest());
DVLOG(1) << "PostRequestToUI({label= " << label << "})";
@@ -906,7 +1140,7 @@ void MediaStreamManager::PostRequestToUI(const std::string& label,
const MediaStreamType video_type = request->video_type();
// Post the request to UI and set the state.
- if (IsAudioMediaType(audio_type))
+ if (IsAudioInputMediaType(audio_type))
request->SetState(audio_type, MEDIA_REQUEST_STATE_PENDING_APPROVAL);
if (IsVideoMediaType(video_type))
request->SetState(video_type, MEDIA_REQUEST_STATE_PENDING_APPROVAL);
@@ -945,7 +1179,7 @@ void MediaStreamManager::PostRequestToUI(const std::string& label,
}
void MediaStreamManager::SetupRequest(const std::string& label) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DeviceRequest* request = FindRequest(label);
if (!request) {
DVLOG(1) << "SetupRequest label " << label << " doesn't exist!!";
@@ -955,7 +1189,9 @@ void MediaStreamManager::SetupRequest(const std::string& label) {
if (!request->security_origin.is_valid()) {
LOG(ERROR) << "Invalid security origin. "
<< request->security_origin;
- FinalizeRequestFailed(label, request);
+ FinalizeRequestFailed(label,
+ request,
+ MEDIA_DEVICE_INVALID_SECURITY_ORIGIN);
return;
}
@@ -969,26 +1205,45 @@ void MediaStreamManager::SetupRequest(const std::string& label) {
audio_type == MEDIA_TAB_AUDIO_CAPTURE ||
video_type == MEDIA_TAB_VIDEO_CAPTURE;
if (is_web_contents_capture && !SetupTabCaptureRequest(request)) {
- FinalizeRequestFailed(label, request);
+ FinalizeRequestFailed(label,
+ request,
+ MEDIA_DEVICE_TAB_CAPTURE_FAILURE);
return;
}
bool is_screen_capture =
video_type == MEDIA_DESKTOP_VIDEO_CAPTURE;
if (is_screen_capture && !SetupScreenCaptureRequest(request)) {
- FinalizeRequestFailed(label, request);
+ FinalizeRequestFailed(label,
+ request,
+ MEDIA_DEVICE_SCREEN_CAPTURE_FAILURE);
return;
}
if (!is_web_contents_capture && !is_screen_capture) {
- if ((!audio_enumeration_cache_.valid && IsAudioMediaType(audio_type)) ||
- (!video_enumeration_cache_.valid && IsVideoMediaType(video_type))) {
+ if (EnumerationRequired(&audio_enumeration_cache_, audio_type) ||
+ EnumerationRequired(&video_enumeration_cache_, video_type)) {
// Enumerate the devices if there is no valid device lists to be used.
StartEnumeration(request);
return;
+ } else {
+ // Cache is valid, so log the cached devices for MediaStream requests.
+ if (request->request_type == MEDIA_GENERATE_STREAM) {
+ std::string log_message("Using cached devices for request.\n");
+ if (audio_type != MEDIA_NO_SERVICE) {
+ log_message +=
+ GetLogMessageString(audio_type, audio_enumeration_cache_.devices);
+ }
+ if (video_type != MEDIA_NO_SERVICE) {
+ log_message +=
+ GetLogMessageString(video_type, video_enumeration_cache_.devices);
+ }
+ SendMessageToNativeLog(log_message);
+ }
}
+
if (!SetupDeviceCaptureRequest(request)) {
- FinalizeRequestFailed(label, request);
+ FinalizeRequestFailed(label, request, MEDIA_DEVICE_NO_HARDWARE);
return;
}
}
@@ -1003,7 +1258,7 @@ bool MediaStreamManager::SetupDeviceCaptureRequest(DeviceRequest* request) {
std::string audio_device_id;
if (request->options.audio_requested &&
!GetRequestedDeviceCaptureId(request, request->audio_type(),
- &audio_device_id)) {
+ &audio_device_id)) {
return false;
}
@@ -1166,7 +1421,7 @@ void MediaStreamManager::FinalizeGenerateStream(const std::string& label,
for (StreamDeviceInfoArray::const_iterator device_it =
requested_devices.begin();
device_it != requested_devices.end(); ++device_it) {
- if (IsAudioMediaType(device_it->device.type)) {
+ if (IsAudioInputMediaType(device_it->device.type)) {
audio_devices.push_back(*device_it);
} else if (IsVideoMediaType(device_it->device.type)) {
video_devices.push_back(*device_it);
@@ -1183,11 +1438,13 @@ void MediaStreamManager::FinalizeGenerateStream(const std::string& label,
void MediaStreamManager::FinalizeRequestFailed(
const std::string& label,
- DeviceRequest* request) {
+ DeviceRequest* request,
+ content::MediaStreamRequestResult result) {
if (request->requester)
request->requester->StreamGenerationFailed(
request->requesting_view_id,
- request->page_request_id);
+ request->page_request_id,
+ result);
if (request->request_type == MEDIA_DEVICE_ACCESS &&
!request->callback.is_null()) {
@@ -1207,22 +1464,55 @@ void MediaStreamManager::FinalizeOpenDevice(const std::string& label,
void MediaStreamManager::FinalizeEnumerateDevices(const std::string& label,
DeviceRequest* request) {
- if (!request->security_origin.is_valid()) {
- request->requester->DevicesEnumerated(
- request->requesting_view_id,
- request->page_request_id,
- label,
- StreamDeviceInfoArray());
- return;
- }
- for (StreamDeviceInfoArray::iterator it = request->devices.begin();
- it != request->devices.end(); ++it) {
- TranslateDeviceIdToSourceId(request, &it->device);
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ DCHECK_EQ(request->request_type, MEDIA_ENUMERATE_DEVICES);
+
+ if (request->security_origin.is_valid()) {
+ for (StreamDeviceInfoArray::iterator it = request->devices.begin();
+ it != request->devices.end(); ++it) {
+ TranslateDeviceIdToSourceId(request, &it->device);
+ }
+ } else {
+ request->devices.clear();
}
- request->requester->DevicesEnumerated(request->requesting_view_id,
- request->page_request_id,
- label,
- request->devices);
+
+ if (!request->have_permission)
+ ClearDeviceLabels(&request->devices);
+
+ request->requester->DevicesEnumerated(
+ request->requesting_view_id,
+ request->page_request_id,
+ label,
+ request->devices);
+
+ // TODO(tommi):
+ // Ideally enumeration requests should be deleted once they have been served
+ // (as any request). However, this implementation mixes requests and
+ // notifications together so enumeration requests are kept open by some
+ // implementations (only Pepper?) and enumerations are done again when
+ // device notifications are fired.
+ // Implementations that just want to request the device list and be done
+ // (e.g. DeviceRequestMessageFilter), they must (confusingly) call
+ // CancelRequest() after the request has been fulfilled. This is not
+ // obvious, not consistent in this class (see e.g. FinalizeMediaAccessRequest)
+ // and can lead to subtle bugs (requests not deleted at all deleted too
+ // early).
+ //
+ // Basically, it is not clear that using requests as an additional layer on
+ // top of device notifications is necessary or good.
+ //
+ // To add to this, MediaStreamManager currently relies on the external
+ // implementations of MediaStreamRequester to delete enumeration requests via
+ // CancelRequest and e.g. DeviceRequestMessageFilter does this. However the
+ // Pepper implementation does not seem to to this at all (and from what I can
+ // see, it is the only implementation that uses an enumeration request as a
+ // notification mechanism).
+ //
+ // We should decouple notifications from enumeration requests and once that
+ // has been done, remove the requirement to call CancelRequest() to delete
+ // enumeration requests and uncomment the following line:
+ //
+ // DeleteRequest(label);
}
void MediaStreamManager::FinalizeMediaAccessRequest(
@@ -1237,23 +1527,14 @@ void MediaStreamManager::FinalizeMediaAccessRequest(
}
void MediaStreamManager::InitializeDeviceManagersOnIOThread() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- if (device_thread_)
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ if (device_task_runner_)
return;
- device_thread_.reset(new base::Thread("MediaStreamDeviceThread"));
-#if defined(OS_WIN)
- device_thread_->init_com_with_mta(true);
-#endif
- CHECK(device_thread_->Start());
+ device_task_runner_ = audio_manager_->GetWorkerTaskRunner();
audio_input_device_manager_ = new AudioInputDeviceManager(audio_manager_);
- audio_input_device_manager_->Register(
- this, device_thread_->message_loop_proxy().get());
-
- video_capture_manager_ = new VideoCaptureManager();
- video_capture_manager_->Register(this,
- device_thread_->message_loop_proxy().get());
+ audio_input_device_manager_->Register(this, device_task_runner_);
// We want to be notified of IO message loop destruction to delete the thread
// and the device managers.
@@ -1261,15 +1542,19 @@ void MediaStreamManager::InitializeDeviceManagersOnIOThread() {
io_loop_->AddDestructionObserver(this);
if (CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kUseFakeDeviceForMediaStream)) {
- DVLOG(1) << "Using fake device";
- UseFakeDevice();
+ switches::kUseFakeDeviceForMediaStream)) {
+ audio_input_device_manager()->UseFakeDevice();
}
+
+ video_capture_manager_ =
+ new VideoCaptureManager(media::VideoCaptureDeviceFactory::CreateFactory(
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI)));
+ video_capture_manager_->Register(this, device_task_runner_);
}
void MediaStreamManager::Opened(MediaStreamType stream_type,
int capture_session_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DVLOG(1) << "Opened({stream_type = " << stream_type << "} "
<< "{capture_session_id = " << capture_session_id << "})";
// Find the request(s) containing this device and mark it as used.
@@ -1289,7 +1574,7 @@ void MediaStreamManager::Opened(MediaStreamType stream_type,
// We've found a matching request.
request->SetState(device_it->device.type, MEDIA_REQUEST_STATE_DONE);
- if (IsAudioMediaType(device_it->device.type)) {
+ if (IsAudioInputMediaType(device_it->device.type)) {
// Store the native audio parameters in the device struct.
// TODO(xians): Handle the tab capture sample rate/channel layout
// in AudioInputDeviceManager::Open().
@@ -1331,20 +1616,29 @@ void MediaStreamManager::HandleRequestDone(const std::string& label,
if (request->ui_proxy.get()) {
request->ui_proxy->OnStarted(
base::Bind(&MediaStreamManager::StopMediaStreamFromBrowser,
- base::Unretained(this), label));
+ base::Unretained(this),
+ label),
+ base::Bind(&MediaStreamManager::OnMediaStreamUIWindowId,
+ base::Unretained(this),
+ request->video_type(),
+ request->devices));
}
}
void MediaStreamManager::Closed(MediaStreamType stream_type,
int capture_session_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
}
void MediaStreamManager::DevicesEnumerated(
MediaStreamType stream_type, const StreamDeviceInfoArray& devices) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DVLOG(1) << "DevicesEnumerated("
- << ", {stream_type = " << stream_type << "})";
+ << "{stream_type = " << stream_type << "})" << std::endl;
+
+ std::string log_message = "New device enumeration result:\n" +
+ GetLogMessageString(stream_type, devices);
+ SendMessageToNativeLog(log_message);
// Only cache the device list when the device list has been changed.
bool need_update_clients = false;
@@ -1384,6 +1678,7 @@ void MediaStreamManager::DevicesEnumerated(
label_list.push_back(it->first);
}
}
+
for (std::list<std::string>::iterator it = label_list.begin();
it != label_list.end(); ++it) {
DeviceRequest* request = FindRequest(*it);
@@ -1404,10 +1699,13 @@ void MediaStreamManager::DevicesEnumerated(
// the device lists to handle the request.
break;
}
- if (!SetupDeviceCaptureRequest(request))
- FinalizeRequestFailed(*it, request);
- else
+ if (!SetupDeviceCaptureRequest(request)) {
+ FinalizeRequestFailed(*it,
+ request,
+ MEDIA_DEVICE_NO_HARDWARE);
+ } else {
PostRequestToUI(*it, request);
+ }
break;
}
}
@@ -1416,10 +1714,80 @@ void MediaStreamManager::DevicesEnumerated(
DCHECK_GE(active_enumeration_ref_count_[stream_type], 0);
}
+void MediaStreamManager::Aborted(MediaStreamType stream_type,
+ int capture_session_id) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ DVLOG(1) << "Aborted({stream_type = " << stream_type << "} "
+ << "{capture_session_id = " << capture_session_id << "})";
+ StopDevice(stream_type, capture_session_id);
+}
+
+// static
+void MediaStreamManager::SendMessageToNativeLog(const std::string& message) {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(DoAddLogMessage, message));
+}
+
+void MediaStreamManager::OnSuspend() {
+ SendMessageToNativeLog("Power state suspended.");
+}
+
+void MediaStreamManager::OnResume() {
+ SendMessageToNativeLog("Power state resumed.");
+}
+
+void MediaStreamManager::AddLogMessageOnIOThread(const std::string& message) {
+ // Get render process ids on the IO thread.
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+ // Grab all unique process ids that request a MediaStream or have a
+ // MediaStream running.
+ std::set<int> requesting_process_ids;
+ for (DeviceRequests::const_iterator it = requests_.begin();
+ it != requests_.end(); ++it) {
+ DeviceRequest* request = it->second;
+ if (request->request_type == MEDIA_GENERATE_STREAM)
+ requesting_process_ids.insert(request->requesting_process_id);
+ }
+
+ // MediaStreamManager is a singleton in BrowserMainLoop, which owns the UI
+ // thread. MediaStreamManager has the same lifetime as the UI thread, so it is
+ // safe to use base::Unretained.
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&MediaStreamManager::AddLogMessageOnUIThread,
+ base::Unretained(this),
+ requesting_process_ids,
+ message));
+}
+
+void MediaStreamManager::AddLogMessageOnUIThread(
+ const std::set<int>& requesting_process_ids,
+ const std::string& message) {
+#if defined(ENABLE_WEBRTC)
+ // Must be on the UI thread to access RenderProcessHost from process ID.
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ for (std::set<int>::const_iterator it = requesting_process_ids.begin();
+ it != requesting_process_ids.end(); ++it) {
+ // Log the message to all renderers that are requesting a MediaStream or
+ // have a MediaStream running.
+ content::RenderProcessHostImpl* render_process_host_impl =
+ static_cast<content::RenderProcessHostImpl*>(
+ content::RenderProcessHost::FromID(*it));
+ if (render_process_host_impl)
+ render_process_host_impl->WebRtcLogMessage(message);
+ }
+#endif
+}
+
void MediaStreamManager::HandleAccessRequestResponse(
const std::string& label,
- const MediaStreamDevices& devices) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ const MediaStreamDevices& devices,
+ content::MediaStreamRequestResult result) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DVLOG(1) << "HandleAccessRequestResponse("
<< ", {label = " << label << "})";
@@ -1435,10 +1803,11 @@ void MediaStreamManager::HandleAccessRequestResponse(
}
// Handle the case when the request was denied.
- if (devices.empty()) {
- FinalizeRequestFailed(label, request);
+ if (result != MEDIA_DEVICE_OK) {
+ FinalizeRequestFailed(label, request, result);
return;
}
+ DCHECK(!devices.empty());
// Process all newly-accepted devices for this request.
bool found_audio = false;
@@ -1508,7 +1877,7 @@ void MediaStreamManager::HandleAccessRequestResponse(
}
// Check whether we've received all stream types requested.
- if (!found_audio && IsAudioMediaType(request->audio_type())) {
+ if (!found_audio && IsAudioInputMediaType(request->audio_type())) {
request->SetState(request->audio_type(), MEDIA_REQUEST_STATE_ERROR);
DVLOG(1) << "Set no audio found label " << label;
}
@@ -1521,7 +1890,7 @@ void MediaStreamManager::HandleAccessRequestResponse(
}
void MediaStreamManager::StopMediaStreamFromBrowser(const std::string& label) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DeviceRequest* request = FindRequest(label);
if (!request)
@@ -1540,14 +1909,8 @@ void MediaStreamManager::StopMediaStreamFromBrowser(const std::string& label) {
CancelRequest(label);
}
-void MediaStreamManager::UseFakeDevice() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- video_capture_manager()->UseFakeDevice();
- audio_input_device_manager()->UseFakeDevice();
-}
-
void MediaStreamManager::UseFakeUI(scoped_ptr<FakeMediaStreamUIProxy> fake_ui) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
use_fake_ui_ = true;
fake_ui_ = fake_ui.Pass();
}
@@ -1556,12 +1919,12 @@ void MediaStreamManager::WillDestroyCurrentMessageLoop() {
DVLOG(3) << "MediaStreamManager::WillDestroyCurrentMessageLoop()";
DCHECK_EQ(base::MessageLoop::current(), io_loop_);
DCHECK(requests_.empty());
- if (device_thread_) {
+ if (device_task_runner_) {
StopMonitoring();
video_capture_manager_->Unregister();
audio_input_device_manager_->Unregister();
- device_thread_.reset();
+ device_task_runner_ = NULL;
}
audio_input_device_manager_ = NULL;
@@ -1571,11 +1934,9 @@ void MediaStreamManager::WillDestroyCurrentMessageLoop() {
void MediaStreamManager::NotifyDevicesChanged(
MediaStreamType stream_type,
const StreamDeviceInfoArray& devices) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
MediaObserver* media_observer =
GetContentClient()->browser()->GetMediaObserver();
- if (media_observer == NULL)
- return;
// Map the devices to MediaStreamDevices.
MediaStreamDevices new_devices;
@@ -1584,19 +1945,25 @@ void MediaStreamManager::NotifyDevicesChanged(
new_devices.push_back(it->device);
}
- if (IsAudioMediaType(stream_type)) {
- media_observer->OnAudioCaptureDevicesChanged(new_devices);
+ if (IsAudioInputMediaType(stream_type)) {
+ MediaCaptureDevicesImpl::GetInstance()->OnAudioCaptureDevicesChanged(
+ new_devices);
+ if (media_observer)
+ media_observer->OnAudioCaptureDevicesChanged();
} else if (IsVideoMediaType(stream_type)) {
- media_observer->OnVideoCaptureDevicesChanged(new_devices);
+ MediaCaptureDevicesImpl::GetInstance()->OnVideoCaptureDevicesChanged(
+ new_devices);
+ if (media_observer)
+ media_observer->OnVideoCaptureDevicesChanged();
} else {
NOTREACHED();
}
}
bool MediaStreamManager::RequestDone(const DeviceRequest& request) const {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
- const bool requested_audio = IsAudioMediaType(request.audio_type());
+ const bool requested_audio = IsAudioInputMediaType(request.audio_type());
const bool requested_video = IsVideoMediaType(request.video_type());
const bool audio_done =
@@ -1620,7 +1987,7 @@ MediaStreamProvider* MediaStreamManager::GetDeviceManager(
MediaStreamType stream_type) {
if (IsVideoMediaType(stream_type)) {
return video_capture_manager();
- } else if (IsAudioMediaType(stream_type)) {
+ } else if (IsAudioInputMediaType(stream_type)) {
return audio_input_device_manager();
}
NOTREACHED();
@@ -1629,7 +1996,7 @@ MediaStreamProvider* MediaStreamManager::GetDeviceManager(
void MediaStreamManager::OnDevicesChanged(
base::SystemMonitor::DeviceType device_type) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
// NOTE: This method is only called in response to physical audio/video device
// changes (from the operating system).
@@ -1650,4 +2017,25 @@ void MediaStreamManager::OnDevicesChanged(
GetDeviceManager(stream_type)->EnumerateDevices(stream_type);
}
+void MediaStreamManager::OnMediaStreamUIWindowId(MediaStreamType video_type,
+ StreamDeviceInfoArray devices,
+ gfx::NativeViewId window_id) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ if (!window_id)
+ return;
+
+ // Pass along for desktop capturing. Ignored for other stream types.
+ if (video_type == MEDIA_DESKTOP_VIDEO_CAPTURE) {
+ for (StreamDeviceInfoArray::iterator it = devices.begin();
+ it != devices.end();
+ ++it) {
+ if (it->device.type == MEDIA_DESKTOP_VIDEO_CAPTURE) {
+ video_capture_manager_->SetDesktopCaptureWindowId(it->session_id,
+ window_id);
+ break;
+ }
+ }
+ }
+}
+
} // namespace content
diff --git a/chromium/content/browser/renderer_host/media/media_stream_manager.h b/chromium/content/browser/renderer_host/media/media_stream_manager.h
index 590d24171b6..1de659c3937 100644
--- a/chromium/content/browser/renderer_host/media/media_stream_manager.h
+++ b/chromium/content/browser/renderer_host/media/media_stream_manager.h
@@ -17,19 +17,25 @@
// 5. MediaStreamManager will call the proper media device manager to open the
// device and let the MediaStreamRequester know it has been done.
+// If either user or test harness selects --use-fake-device-for-media-stream,
+// a fake video device or devices are used instead of real ones.
+
// When enumeration and open are done in separate operations,
// MediaStreamUIController is not involved as in steps.
#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_STREAM_MANAGER_H_
#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_STREAM_MANAGER_H_
-#include <map>
+#include <list>
+#include <set>
#include <string>
+#include <utility>
#include "base/basictypes.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
+#include "base/power_monitor/power_observer.h"
#include "base/system_monitor/system_monitor.h"
#include "content/browser/renderer_host/media/media_stream_provider.h"
#include "content/common/content_export.h"
@@ -37,10 +43,6 @@
#include "content/public/browser/media_request_state.h"
#include "content/public/browser/resource_context.h"
-namespace base {
-class Thread;
-}
-
namespace media {
class AudioManager;
}
@@ -60,6 +62,7 @@ class VideoCaptureManager;
class CONTENT_EXPORT MediaStreamManager
: public MediaStreamProviderListener,
public base::MessageLoop::DestructionObserver,
+ public base::PowerObserver,
public base::SystemMonitor::DevicesChangedObserver {
public:
// Callback to deliver the result of a media request.
@@ -100,7 +103,8 @@ class CONTENT_EXPORT MediaStreamManager
const ResourceContext::SaltCallback& sc,
int page_request_id,
const StreamOptions& components,
- const GURL& security_origin);
+ const GURL& security_origin,
+ bool user_gesture);
void CancelRequest(int render_process_id,
int render_view_id,
@@ -125,13 +129,15 @@ class CONTENT_EXPORT MediaStreamManager
// and video devices and also start monitoring device changes, such as
// plug/unplug. The new device lists will be delivered via media observer to
// MediaCaptureDevicesDispatcher.
+ // If |have_permission| is false, we remove the device label from the result.
virtual std::string EnumerateDevices(MediaStreamRequester* requester,
int render_process_id,
int render_view_id,
const ResourceContext::SaltCallback& sc,
int page_request_id,
MediaStreamType type,
- const GURL& security_origin);
+ const GURL& security_origin,
+ bool have_permission);
// Open a device identified by |device_id|. |type| must be either
// MEDIA_DEVICE_AUDIO_CAPTURE or MEDIA_DEVICE_VIDEO_CAPTURE.
@@ -145,6 +151,16 @@ class CONTENT_EXPORT MediaStreamManager
MediaStreamType type,
const GURL& security_origin);
+ // Finds and returns the device id corresponding to the given
+ // |source_id|. Returns true if there was a raw device id that matched the
+ // given |source_id|, false if nothing matched it.
+ bool TranslateSourceIdToDeviceId(
+ MediaStreamType stream_type,
+ const ResourceContext::SaltCallback& rc,
+ const GURL& security_origin,
+ const std::string& source_id,
+ std::string* device_id) const;
+
// Called by UI to make sure the device monitor is started so that UI receive
// notifications about device changes.
void EnsureDeviceMonitorStarted();
@@ -156,16 +172,13 @@ class CONTENT_EXPORT MediaStreamManager
int capture_session_id) OVERRIDE;
virtual void DevicesEnumerated(MediaStreamType stream_type,
const StreamDeviceInfoArray& devices) OVERRIDE;
+ virtual void Aborted(MediaStreamType stream_type,
+ int capture_session_id) OVERRIDE;
// Implements base::SystemMonitor::DevicesChangedObserver.
virtual void OnDevicesChanged(
base::SystemMonitor::DeviceType device_type) OVERRIDE;
- // Used by unit test to make sure fake devices are used instead of a real
- // devices, which is needed for server based testing or certain tests (which
- // can pass --use-fake-device-for-media-stream).
- void UseFakeDevice();
-
// Called by the tests to specify a fake UI that should be used for next
// generated stream (or when using --use-fake-ui-for-media-stream).
void UseFakeUI(scoped_ptr<FakeMediaStreamUIProxy> fake_ui);
@@ -185,6 +198,20 @@ class CONTENT_EXPORT MediaStreamManager
// too late. (see http://crbug.com/247525#c14).
virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
+ // Sends log messages to the render process hosts whose corresponding render
+ // processes are making device requests, to be used by the
+ // webrtcLoggingPrivate API if requested.
+ void AddLogMessageOnIOThread(const std::string& message);
+
+ // Adds |message| to native logs for outstanding device requests, for use by
+ // render processes hosts whose corresponding render processes are requesting
+ // logging from webrtcLoggingPrivate API. Safe to call from any thread.
+ static void SendMessageToNativeLog(const std::string& message);
+
+ // base::PowerObserver overrides.
+ virtual void OnSuspend() OVERRIDE;
+ virtual void OnResume() OVERRIDE;
+
protected:
// Used for testing.
MediaStreamManager();
@@ -202,7 +229,10 @@ class CONTENT_EXPORT MediaStreamManager
StreamDeviceInfoArray devices;
};
- typedef std::map<std::string, DeviceRequest*> DeviceRequests;
+ // |DeviceRequests| is a list to ensure requests are processed in the order
+ // they arrive. The first member of the pair is the label of the
+ // |DeviceRequest|.
+ typedef std::list<std::pair<std::string, DeviceRequest*> > DeviceRequests;
// Initializes the device managers on IO thread. Auto-starts the device
// thread and registers this as a listener with the device managers.
@@ -214,11 +244,17 @@ class CONTENT_EXPORT MediaStreamManager
const StreamDeviceInfoArray& devices);
void HandleAccessRequestResponse(const std::string& label,
- const MediaStreamDevices& devices);
+ const MediaStreamDevices& devices,
+ content::MediaStreamRequestResult result);
void StopMediaStreamFromBrowser(const std::string& label);
void DoEnumerateDevices(const std::string& label);
+ // Enumerates audio output devices. No caching.
+ void EnumerateAudioOutputDevices(const std::string& label);
+
+ void AudioOutputDevicesEnumerated(const StreamDeviceInfoArray& devices);
+
// Helpers.
// Checks if all devices that was requested in the request identififed by
// |label| has been opened and set the request state accordingly.
@@ -240,6 +276,12 @@ class CONTENT_EXPORT MediaStreamManager
DeviceRequest* FindRequest(const std::string& label) const;
void DeleteRequest(const std::string& label);
void ClearEnumerationCache(EnumerationCache* cache);
+ // Returns true if the |cache| is invalid, false if it's invalid or if
+ // the |stream_type| is MEDIA_NO_SERVICE.
+ // On Android, this function will always return true for
+ // MEDIA_DEVICE_AUDIO_CAPTURE since we don't have a SystemMonitor to tell
+ // us about audio device changes.
+ bool EnumerationRequired(EnumerationCache* cache, MediaStreamType type);
// Prepare the request with label |label| by starting device enumeration if
// needed.
void SetupRequest(const std::string& label);
@@ -271,7 +313,8 @@ class CONTENT_EXPORT MediaStreamManager
void FinalizeGenerateStream(const std::string& label,
DeviceRequest* request);
void FinalizeRequestFailed(const std::string& label,
- DeviceRequest* request);
+ DeviceRequest* request,
+ content::MediaStreamRequestResult result);
void FinalizeOpenDevice(const std::string& label,
DeviceRequest* request);
void FinalizeMediaAccessRequest(const std::string& label,
@@ -294,6 +337,9 @@ class CONTENT_EXPORT MediaStreamManager
// Helpers to start and stop monitoring devices.
void StartMonitoring();
void StopMonitoring();
+#if defined(OS_MACOSX)
+ void StartMonitoringOnUIThread();
+#endif
// Finds the requested device id from constraints. The requested device type
// must be MEDIA_DEVICE_AUDIO_CAPTURE or MEDIA_DEVICE_VIDEO_CAPTURE.
@@ -304,18 +350,23 @@ class CONTENT_EXPORT MediaStreamManager
void TranslateDeviceIdToSourceId(DeviceRequest* request,
MediaStreamDevice* device);
- // Finds and returns the device id corresponding to the given
- // |source_id|. Returns true if there was a raw device id that matched the
- // given |source_id|, false if nothing matched it.
- bool TranslateSourceIdToDeviceId(
- MediaStreamType stream_type,
- const ResourceContext::SaltCallback& rc,
- const GURL& security_origin,
- const std::string& source_id,
- std::string* device_id) const;
+ // Helper method that sends log messages to the render process hosts whose
+ // corresponding render processes are in |render_process_ids|, to be used by
+ // the webrtcLoggingPrivate API if requested.
+ void AddLogMessageOnUIThread(const std::set<int>& render_process_ids,
+ const std::string& message);
+
+ // Handles the callback from MediaStreamUIProxy to receive the UI window id,
+ // used for excluding the notification window in desktop capturing.
+ void OnMediaStreamUIWindowId(MediaStreamType video_type,
+ StreamDeviceInfoArray devices,
+ gfx::NativeViewId window_id);
- // Device thread shared by VideoCaptureManager and AudioInputDeviceManager.
- scoped_ptr<base::Thread> device_thread_;
+ // Task runner shared by VideoCaptureManager and AudioInputDeviceManager and
+ // used for enumerating audio output devices.
+ // Note: Enumeration tasks may take seconds to complete so must never be run
+ // on any of the BrowserThreads (UI, IO, etc). See http://crbug.com/256945.
+ scoped_refptr<base::SingleThreadTaskRunner> device_task_runner_;
media::AudioManager* const audio_manager_; // not owned
scoped_refptr<AudioInputDeviceManager> audio_input_device_manager_;
@@ -333,7 +384,7 @@ class CONTENT_EXPORT MediaStreamManager
// AudioInputDeviceManager, in order to only enumerate when necessary.
int active_enumeration_ref_count_[NUM_MEDIA_TYPES];
- // All non-closed request.
+ // All non-closed request. Must be accessed on IO thread.
DeviceRequests requests_;
// Hold a pointer to the IO loop to check we delete the device thread and
diff --git a/chromium/content/browser/renderer_host/media/media_stream_manager_unittest.cc b/chromium/content/browser/renderer_host/media/media_stream_manager_unittest.cc
index be4d498f87d..e01b945b815 100644
--- a/chromium/content/browser/renderer_host/media/media_stream_manager_unittest.cc
+++ b/chromium/content/browser/renderer_host/media/media_stream_manager_unittest.cc
@@ -5,15 +5,18 @@
#include <string>
#include "base/bind.h"
+#include "base/command_line.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "content/browser/browser_thread_impl.h"
#include "content/browser/renderer_host/media/media_stream_manager.h"
#include "content/browser/renderer_host/media/media_stream_ui_proxy.h"
#include "content/common/media/media_stream_options.h"
+#include "content/public/common/content_switches.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "media/audio/audio_manager_base.h"
#include "media/audio/fake_audio_log_factory.h"
+#include "media/base/media_switches.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -75,13 +78,12 @@ class MediaStreamManagerTest : public ::testing::Test {
MediaStreamManagerTest()
: thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
message_loop_(base::MessageLoopProxy::current()) {
- // Create our own MediaStreamManager.
+ // Create our own MediaStreamManager. Use fake devices to run on the bots.
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kUseFakeDeviceForMediaStream);
audio_manager_.reset(new MockAudioManager());
media_stream_manager_.reset(new MediaStreamManager(audio_manager_.get()));
-
- // Use fake devices in order to run on the bots.
- media_stream_manager_->UseFakeDevice();
- }
+}
virtual ~MediaStreamManagerTest() {
}
diff --git a/chromium/content/browser/renderer_host/media/media_stream_provider.h b/chromium/content/browser/renderer_host/media/media_stream_provider.h
index 3389388d8df..3413a99bd34 100644
--- a/chromium/content/browser/renderer_host/media/media_stream_provider.h
+++ b/chromium/content/browser/renderer_host/media/media_stream_provider.h
@@ -20,7 +20,7 @@
#include "content/common/media/media_stream_options.h"
namespace base {
-class MessageLoopProxy;
+class SingleThreadTaskRunner;
}
namespace content {
@@ -41,17 +41,19 @@ enum { kInvalidMediaCaptureSessionId = 0xFFFFFFFF };
class CONTENT_EXPORT MediaStreamProviderListener {
public:
// Called by a MediaStreamProvider when a stream has been opened.
- virtual void Opened(MediaStreamType stream_type,
- int capture_session_id) = 0;
+ virtual void Opened(MediaStreamType stream_type, int capture_session_id) = 0;
// Called by a MediaStreamProvider when a stream has been closed.
- virtual void Closed(MediaStreamType stream_type,
- int capture_session_id) = 0;
+ virtual void Closed(MediaStreamType stream_type, int capture_session_id) = 0;
// Called by a MediaStreamProvider when available devices has been enumerated.
virtual void DevicesEnumerated(MediaStreamType stream_type,
const StreamDeviceInfoArray& devices) = 0;
+ // Called by a MediaStreamProvider when the device has been aborted due to
+ // device error.
+ virtual void Aborted(MediaStreamType stream_type, int capture_session_id) = 0;
+
protected:
virtual ~MediaStreamProviderListener() {}
};
@@ -62,7 +64,8 @@ class CONTENT_EXPORT MediaStreamProvider
public:
// Registers a listener and a device message loop.
virtual void Register(MediaStreamProviderListener* listener,
- base::MessageLoopProxy* device_thread_loop) = 0;
+ const scoped_refptr<base::SingleThreadTaskRunner>&
+ device_task_runner) = 0;
// Unregisters the previously registered listener.
virtual void Unregister() = 0;
diff --git a/chromium/content/browser/renderer_host/media/media_stream_requester.h b/chromium/content/browser/renderer_host/media/media_stream_requester.h
index 146ef1a489e..337effade97 100644
--- a/chromium/content/browser/renderer_host/media/media_stream_requester.h
+++ b/chromium/content/browser/renderer_host/media/media_stream_requester.h
@@ -24,8 +24,10 @@ class CONTENT_EXPORT MediaStreamRequester {
const StreamDeviceInfoArray& audio_devices,
const StreamDeviceInfoArray& video_devices) = 0;
// Called if GenerateStream failed.
- virtual void StreamGenerationFailed(int render_view_id,
- int page_request_id) = 0;
+ virtual void StreamGenerationFailed(
+ int render_view_id,
+ int page_request_id,
+ content::MediaStreamRequestResult result) = 0;
// Called if a device has been stopped by a user from UI or the device
// has become unavailable. |render_view_id| is the render view that requested
// the device and |label| is the label of the request|.
diff --git a/chromium/content/browser/renderer_host/media/media_stream_track_metrics_host.cc b/chromium/content/browser/renderer_host/media/media_stream_track_metrics_host.cc
new file mode 100644
index 00000000000..9bae1f72839
--- /dev/null
+++ b/chromium/content/browser/renderer_host/media/media_stream_track_metrics_host.cc
@@ -0,0 +1,86 @@
+// 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/renderer_host/media/media_stream_track_metrics_host.h"
+
+#include "base/metrics/histogram.h"
+#include "content/common/media/media_stream_track_metrics_host_messages.h"
+
+// We use a histogram with a maximum bucket of 16 hours to infinity
+// for track durations.
+#define UMA_HISTOGRAM_TIMES_16H(name, sample) \
+ UMA_HISTOGRAM_CUSTOM_TIMES(name, sample, \
+ base::TimeDelta::FromMilliseconds(100), \
+ base::TimeDelta::FromHours(16), \
+ 50);
+
+namespace content {
+
+MediaStreamTrackMetricsHost::MediaStreamTrackMetricsHost()
+ : BrowserMessageFilter(MediaStreamTrackMetricsHostMsgStart) {
+}
+
+MediaStreamTrackMetricsHost::~MediaStreamTrackMetricsHost() {
+ // Our render process has exited. We won't receive any more IPC
+ // messages from it. Assume all tracks ended now.
+ for (TrackMap::iterator it = tracks_.begin();
+ it != tracks_.end();
+ ++it) {
+ TrackInfo& info = it->second;
+ ReportDuration(info);
+ }
+ tracks_.clear();
+}
+
+bool MediaStreamTrackMetricsHost::OnMessageReceived(
+ const IPC::Message& message) {
+ bool handled = true;
+
+ IPC_BEGIN_MESSAGE_MAP(MediaStreamTrackMetricsHost, message)
+ IPC_MESSAGE_HANDLER(MediaStreamTrackMetricsHost_AddTrack, OnAddTrack)
+ IPC_MESSAGE_HANDLER(MediaStreamTrackMetricsHost_RemoveTrack, OnRemoveTrack)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+
+ return handled;
+}
+
+void MediaStreamTrackMetricsHost::OnAddTrack(uint64 id,
+ bool is_audio,
+ bool is_remote) {
+ DCHECK(tracks_.find(id) == tracks_.end());
+ TrackInfo info = {is_audio, is_remote, base::TimeTicks::Now()};
+ tracks_[id] = info;
+}
+
+void MediaStreamTrackMetricsHost::OnRemoveTrack(uint64 id) {
+ DCHECK(tracks_.find(id) != tracks_.end());
+
+ TrackInfo& info = tracks_[id];
+ ReportDuration(info);
+ tracks_.erase(id);
+}
+
+void MediaStreamTrackMetricsHost::ReportDuration(const TrackInfo& info) {
+ base::TimeDelta duration = base::TimeTicks::Now() - info.timestamp;
+ if (info.is_remote) {
+ if (info.is_audio) {
+ DVLOG(3) << "WebRTC.ReceivedAudioTrackDuration: " << duration.InSeconds();
+ UMA_HISTOGRAM_TIMES_16H("WebRTC.ReceivedAudioTrackDuration", duration);
+ } else {
+ DVLOG(3) << "WebRTC.ReceivedVideoTrackDuration: " << duration.InSeconds();
+ UMA_HISTOGRAM_TIMES_16H("WebRTC.ReceivedVideoTrackDuration", duration);
+ }
+ } else {
+ if (info.is_audio) {
+ DVLOG(3) << "WebRTC.SentAudioTrackDuration: " << duration.InSeconds();
+ UMA_HISTOGRAM_TIMES_16H("WebRTC.SentAudioTrackDuration", duration);
+ } else {
+ DVLOG(3) << "WebRTC.SentVideoTrackDuration: " << duration.InSeconds();
+ UMA_HISTOGRAM_TIMES_16H("WebRTC.SentVideoTrackDuration", duration);
+ }
+ }
+}
+
+} // namespace content
diff --git a/chromium/content/browser/renderer_host/media/media_stream_track_metrics_host.h b/chromium/content/browser/renderer_host/media/media_stream_track_metrics_host.h
new file mode 100644
index 00000000000..081b0d51137
--- /dev/null
+++ b/chromium/content/browser/renderer_host/media/media_stream_track_metrics_host.h
@@ -0,0 +1,63 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_STREAM_TRACK_METRICS_HOST_H_
+#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_STREAM_TRACK_METRICS_HOST_H_
+
+#include <map>
+#include <string>
+
+#include "base/time/time.h"
+#include "content/public/browser/browser_message_filter.h"
+
+namespace content {
+
+// Responsible for logging metrics about audio and video track
+// lifetimes. These are based on messages from the renderer that are
+// sent when tracks are created and destroyed. Unfortunately we can't
+// reliably log the lifetime metric in the renderer because that
+// process may be destroyed at any time by the fast shutdown path (see
+// RenderProcessHost::FastShutdownIfPossible).
+//
+// There is one instance of this class per render process.
+//
+// If the renderer process goes away without sending messages that
+// tracks were removed, this class instead infers that the tracks were
+// removed.
+class MediaStreamTrackMetricsHost
+ : public BrowserMessageFilter {
+ public:
+ explicit MediaStreamTrackMetricsHost();
+
+ protected:
+ virtual ~MediaStreamTrackMetricsHost();
+
+ // BrowserMessageFilter override.
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+
+ private:
+ void OnAddTrack(uint64 id, bool is_audio, bool is_remote);
+ void OnRemoveTrack(uint64 id);
+
+ // Information for a track we're keeping in |tracks_|. |is_audio|
+ // specifies whether it's an audio or video track, |is_remote|
+ // specifies whether it's remote (received over a PeerConnection) or
+ // local (sent over a PeerConnection). |timestamp| specifies when
+ // the track was connected.
+ struct TrackInfo {
+ bool is_audio;
+ bool is_remote;
+ base::TimeTicks timestamp;
+ };
+
+ void ReportDuration(const TrackInfo& info);
+
+ // Values are unique (per renderer) track IDs.
+ typedef std::map<uint64, TrackInfo> TrackMap;
+ TrackMap tracks_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_STREAM_TRACK_METRICS_HOST_H_
diff --git a/chromium/content/browser/renderer_host/media/media_stream_ui_controller_unittest.cc b/chromium/content/browser/renderer_host/media/media_stream_ui_controller_unittest.cc
index f65936eff74..048b078c2bf 100644
--- a/chromium/content/browser/renderer_host/media/media_stream_ui_controller_unittest.cc
+++ b/chromium/content/browser/renderer_host/media/media_stream_ui_controller_unittest.cc
@@ -43,7 +43,7 @@ class MediaStreamDeviceUIControllerTest
protected:
virtual void SetUp() {
- message_loop_.reset(new base::MessageLoop(base::MessageLoop::TYPE_IO));
+ message_loop_.reset(new base::MessageLoopForIO);
ui_thread_.reset(new BrowserThreadImpl(BrowserThread::UI,
message_loop_.get()));
io_thread_.reset(new BrowserThreadImpl(BrowserThread::IO,
diff --git a/chromium/content/browser/renderer_host/media/media_stream_ui_proxy.cc b/chromium/content/browser/renderer_host/media/media_stream_ui_proxy.cc
index b91ccb12095..1c924241aa3 100644
--- a/chromium/content/browser/renderer_host/media/media_stream_ui_proxy.cc
+++ b/chromium/content/browser/renderer_host/media/media_stream_ui_proxy.cc
@@ -4,9 +4,11 @@
#include "content/browser/renderer_host/media/media_stream_ui_proxy.h"
+#include "base/command_line.h"
#include "content/browser/renderer_host/render_view_host_delegate.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/public/browser/browser_thread.h"
+#include "content/public/common/content_switches.h"
#include "media/video/capture/fake_video_capture_device.h"
namespace content {
@@ -18,10 +20,11 @@ class MediaStreamUIProxy::Core {
~Core();
void RequestAccess(const MediaStreamRequest& request);
- void OnStarted();
+ void OnStarted(gfx::NativeViewId* window_id);
private:
void ProcessAccessRequestResponse(const MediaStreamDevices& devices,
+ content::MediaStreamRequestResult result,
scoped_ptr<MediaStreamUI> stream_ui);
void ProcessStopRequestFromUI();
@@ -45,12 +48,12 @@ MediaStreamUIProxy::Core::Core(const base::WeakPtr<MediaStreamUIProxy>& proxy,
}
MediaStreamUIProxy::Core::~Core() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
void MediaStreamUIProxy::Core::RequestAccess(
const MediaStreamRequest& request) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
RenderViewHostDelegate* render_delegate;
@@ -63,7 +66,9 @@ void MediaStreamUIProxy::Core::RequestAccess(
// Tab may have gone away.
if (!host || !host->GetDelegate()) {
ProcessAccessRequestResponse(
- MediaStreamDevices(), scoped_ptr<MediaStreamUI>());
+ MediaStreamDevices(),
+ MEDIA_DEVICE_INVALID_STATE,
+ scoped_ptr<MediaStreamUI>());
return;
}
@@ -75,28 +80,29 @@ void MediaStreamUIProxy::Core::RequestAccess(
weak_factory_.GetWeakPtr()));
}
-void MediaStreamUIProxy::Core::OnStarted() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+void MediaStreamUIProxy::Core::OnStarted(gfx::NativeViewId* window_id) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (ui_) {
- ui_->OnStarted(base::Bind(&Core::ProcessStopRequestFromUI,
- base::Unretained(this)));
+ *window_id = ui_->OnStarted(
+ base::Bind(&Core::ProcessStopRequestFromUI, base::Unretained(this)));
}
}
void MediaStreamUIProxy::Core::ProcessAccessRequestResponse(
const MediaStreamDevices& devices,
+ content::MediaStreamRequestResult result,
scoped_ptr<MediaStreamUI> stream_ui) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
ui_ = stream_ui.Pass();
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&MediaStreamUIProxy::ProcessAccessRequestResponse,
- proxy_, devices));
+ proxy_, devices, result));
}
void MediaStreamUIProxy::Core::ProcessStopRequestFromUI() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
@@ -118,19 +124,19 @@ scoped_ptr<MediaStreamUIProxy> MediaStreamUIProxy::CreateForTests(
MediaStreamUIProxy::MediaStreamUIProxy(
RenderViewHostDelegate* test_render_delegate)
: weak_factory_(this) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
core_.reset(new Core(weak_factory_.GetWeakPtr(), test_render_delegate));
}
MediaStreamUIProxy::~MediaStreamUIProxy() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, core_.release());
}
void MediaStreamUIProxy::RequestAccess(
const MediaStreamRequest& request,
const ResponseCallback& response_callback) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
response_callback_ = response_callback;
BrowserThread::PostTask(
@@ -138,27 +144,45 @@ void MediaStreamUIProxy::RequestAccess(
base::Bind(&Core::RequestAccess, base::Unretained(core_.get()), request));
}
-void MediaStreamUIProxy::OnStarted(const base::Closure& stop_callback) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+void MediaStreamUIProxy::OnStarted(const base::Closure& stop_callback,
+ const WindowIdCallback& window_id_callback) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
stop_callback_ = stop_callback;
- BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- base::Bind(&Core::OnStarted, base::Unretained(core_.get())));
+
+ // Owned by the PostTaskAndReply callback.
+ gfx::NativeViewId* window_id = new gfx::NativeViewId(0);
+
+ BrowserThread::PostTaskAndReply(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&Core::OnStarted, base::Unretained(core_.get()), window_id),
+ base::Bind(&MediaStreamUIProxy::OnWindowId,
+ weak_factory_.GetWeakPtr(),
+ window_id_callback,
+ base::Owned(window_id)));
+}
+
+void MediaStreamUIProxy::OnWindowId(const WindowIdCallback& window_id_callback,
+ gfx::NativeViewId* window_id) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ if (!window_id_callback.is_null())
+ window_id_callback.Run(*window_id);
}
void MediaStreamUIProxy::ProcessAccessRequestResponse(
- const MediaStreamDevices& devices) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ const MediaStreamDevices& devices,
+ content::MediaStreamRequestResult result) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(!response_callback_.is_null());
ResponseCallback cb = response_callback_;
response_callback_.Reset();
- cb.Run(devices);
+ cb.Run(devices, result);
}
void MediaStreamUIProxy::ProcessStopRequestFromUI() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(!stop_callback_.is_null());
base::Closure cb = stop_callback_;
@@ -180,20 +204,33 @@ void FakeMediaStreamUIProxy::SetAvailableDevices(
void FakeMediaStreamUIProxy::RequestAccess(
const MediaStreamRequest& request,
const ResponseCallback& response_callback) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
response_callback_ = response_callback;
+ if (CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kUseFakeUIForMediaStream) == "deny") {
+ // Immediately deny the request.
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&MediaStreamUIProxy::ProcessAccessRequestResponse,
+ weak_factory_.GetWeakPtr(),
+ MediaStreamDevices(),
+ MEDIA_DEVICE_PERMISSION_DENIED));
+ return;
+ }
+
MediaStreamDevices devices_to_use;
bool accepted_audio = false;
bool accepted_video = false;
+
// Use the first capture device of the same media type in the list for the
// fake UI.
for (MediaStreamDevices::const_iterator it = devices_.begin();
it != devices_.end(); ++it) {
if (!accepted_audio &&
- IsAudioMediaType(request.audio_type) &&
- IsAudioMediaType(it->type) &&
+ IsAudioInputMediaType(request.audio_type) &&
+ IsAudioInputMediaType(it->type) &&
(request.requested_audio_device_id.empty() ||
request.requested_audio_device_id == it->id)) {
devices_to_use.push_back(*it);
@@ -208,13 +245,24 @@ void FakeMediaStreamUIProxy::RequestAccess(
}
}
+ // Fail the request if a device exist for the requested type.
+ if ((request.audio_type != MEDIA_NO_SERVICE && !accepted_audio) ||
+ (request.video_type != MEDIA_NO_SERVICE && !accepted_video)) {
+ devices_to_use.clear();
+ }
+
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&MediaStreamUIProxy::ProcessAccessRequestResponse,
- weak_factory_.GetWeakPtr(), devices_to_use));
+ weak_factory_.GetWeakPtr(),
+ devices_to_use,
+ devices_to_use.empty() ?
+ MEDIA_DEVICE_NO_HARDWARE :
+ MEDIA_DEVICE_OK));
}
-void FakeMediaStreamUIProxy::OnStarted(const base::Closure& stop_callback) {
-}
+void FakeMediaStreamUIProxy::OnStarted(
+ const base::Closure& stop_callback,
+ const WindowIdCallback& window_id_callback) {}
} // namespace content
diff --git a/chromium/content/browser/renderer_host/media/media_stream_ui_proxy.h b/chromium/content/browser/renderer_host/media/media_stream_ui_proxy.h
index 62bbeb84495..01be16676cd 100644
--- a/chromium/content/browser/renderer_host/media/media_stream_ui_proxy.h
+++ b/chromium/content/browser/renderer_host/media/media_stream_ui_proxy.h
@@ -21,7 +21,11 @@ class RenderViewHostDelegate;
class CONTENT_EXPORT MediaStreamUIProxy {
public:
typedef base::Callback<
- void (const MediaStreamDevices& devices)> ResponseCallback;
+ void (const MediaStreamDevices& devices,
+ content::MediaStreamRequestResult result)>
+ ResponseCallback;
+
+ typedef base::Callback<void(gfx::NativeViewId window_id)> WindowIdCallback;
static scoped_ptr<MediaStreamUIProxy> Create();
static scoped_ptr<MediaStreamUIProxy> CreateForTests(
@@ -39,8 +43,10 @@ class CONTENT_EXPORT MediaStreamUIProxy {
// Notifies the UI that the MediaStream has been started. Must be called after
// access has been approved using RequestAccess(). |stop_callback| is be
// called on the IO thread after the user has requests the stream to be
- // stopped.
- virtual void OnStarted(const base::Closure& stop_callback);
+ // stopped. |window_id_callback| is called on the IO thread with the platform-
+ // dependent window ID of the UI.
+ virtual void OnStarted(const base::Closure& stop_callback,
+ const WindowIdCallback& window_id_callback);
void SetRenderViewHostDelegateForTests(RenderViewHostDelegate* delegate);
@@ -52,8 +58,12 @@ class CONTENT_EXPORT MediaStreamUIProxy {
friend class Core;
friend class FakeMediaStreamUIProxy;
- void ProcessAccessRequestResponse(const MediaStreamDevices& devices);
+ void ProcessAccessRequestResponse(
+ const MediaStreamDevices& devices,
+ content::MediaStreamRequestResult result);
void ProcessStopRequestFromUI();
+ void OnWindowId(const WindowIdCallback& window_id_callback,
+ gfx::NativeViewId* window_id);
scoped_ptr<Core> core_;
ResponseCallback response_callback_;
@@ -75,7 +85,8 @@ class CONTENT_EXPORT FakeMediaStreamUIProxy : public MediaStreamUIProxy {
virtual void RequestAccess(
const MediaStreamRequest& request,
const ResponseCallback& response_callback) OVERRIDE;
- virtual void OnStarted(const base::Closure& stop_callback) OVERRIDE;
+ virtual void OnStarted(const base::Closure& stop_callback,
+ const WindowIdCallback& window_id_callback) OVERRIDE;
private:
MediaStreamDevices devices_;
diff --git a/chromium/content/browser/renderer_host/media/media_stream_ui_proxy_unittest.cc b/chromium/content/browser/renderer_host/media/media_stream_ui_proxy_unittest.cc
index bff55c618cc..4529b4999d9 100644
--- a/chromium/content/browser/renderer_host/media/media_stream_ui_proxy_unittest.cc
+++ b/chromium/content/browser/renderer_host/media/media_stream_ui_proxy_unittest.cc
@@ -13,6 +13,7 @@
#include "ui/gfx/rect.h"
using testing::_;
+using testing::Return;
using testing::SaveArg;
namespace content {
@@ -38,18 +39,20 @@ class MockRenderViewHostDelegate : public RenderViewHostDelegate {
class MockResponseCallback {
public:
- MOCK_METHOD1(OnAccessRequestResponse,
- void(const MediaStreamDevices& devices));
+ MOCK_METHOD2(OnAccessRequestResponse,
+ void(const MediaStreamDevices& devices,
+ content::MediaStreamRequestResult result));
};
class MockMediaStreamUI : public MediaStreamUI {
public:
- MOCK_METHOD1(OnStarted, void(const base::Closure& stop));
+ MOCK_METHOD1(OnStarted, gfx::NativeViewId(const base::Closure& stop));
};
class MockStopStreamHandler {
public:
MOCK_METHOD0(OnStop, void());
+ MOCK_METHOD1(OnWindowId, void(gfx::NativeViewId window_id));
};
@@ -92,7 +95,7 @@ MATCHER_P(SameRequest, expected, "") {
}
TEST_F(MediaStreamUIProxyTest, Deny) {
- MediaStreamRequest request(0, 0, 0, GURL("http://origin/"),
+ MediaStreamRequest request(0, 0, 0, GURL("http://origin/"), false,
MEDIA_GENERATE_STREAM, std::string(),
std::string(),
MEDIA_DEVICE_AUDIO_CAPTURE,
@@ -107,10 +110,10 @@ TEST_F(MediaStreamUIProxyTest, Deny) {
ASSERT_FALSE(callback.is_null());
MediaStreamDevices devices;
- callback.Run(devices, scoped_ptr<MediaStreamUI>());
+ callback.Run(devices, MEDIA_DEVICE_OK, scoped_ptr<MediaStreamUI>());
MediaStreamDevices response;
- EXPECT_CALL(response_callback_, OnAccessRequestResponse(_))
+ EXPECT_CALL(response_callback_, OnAccessRequestResponse(_, _))
.WillOnce(SaveArg<0>(&response));
message_loop_.RunUntilIdle();
@@ -118,7 +121,7 @@ TEST_F(MediaStreamUIProxyTest, Deny) {
}
TEST_F(MediaStreamUIProxyTest, AcceptAndStart) {
- MediaStreamRequest request(0, 0, 0, GURL("http://origin/"),
+ MediaStreamRequest request(0, 0, 0, GURL("http://origin/"), false,
MEDIA_GENERATE_STREAM, std::string(),
std::string(),
MEDIA_DEVICE_AUDIO_CAPTURE,
@@ -136,23 +139,23 @@ TEST_F(MediaStreamUIProxyTest, AcceptAndStart) {
devices.push_back(
MediaStreamDevice(MEDIA_DEVICE_AUDIO_CAPTURE, "Mic", "Mic"));
scoped_ptr<MockMediaStreamUI> ui(new MockMediaStreamUI());
- EXPECT_CALL(*ui, OnStarted(_));
- callback.Run(devices, ui.PassAs<MediaStreamUI>());
+ EXPECT_CALL(*ui, OnStarted(_)).WillOnce(Return(0));
+ callback.Run(devices, MEDIA_DEVICE_OK, ui.PassAs<MediaStreamUI>());
MediaStreamDevices response;
- EXPECT_CALL(response_callback_, OnAccessRequestResponse(_))
+ EXPECT_CALL(response_callback_, OnAccessRequestResponse(_, _))
.WillOnce(SaveArg<0>(&response));
message_loop_.RunUntilIdle();
EXPECT_FALSE(response.empty());
- proxy_->OnStarted(base::Closure());
+ proxy_->OnStarted(base::Closure(), MediaStreamUIProxy::WindowIdCallback());
message_loop_.RunUntilIdle();
}
// Verify that the proxy can be deleted before the request is processed.
TEST_F(MediaStreamUIProxyTest, DeleteBeforeAccepted) {
- MediaStreamRequest request(0, 0, 0, GURL("http://origin/"),
+ MediaStreamRequest request(0, 0, 0, GURL("http://origin/"), false,
MEDIA_GENERATE_STREAM, std::string(),
std::string(),
MEDIA_DEVICE_AUDIO_CAPTURE,
@@ -170,11 +173,11 @@ TEST_F(MediaStreamUIProxyTest, DeleteBeforeAccepted) {
MediaStreamDevices devices;
scoped_ptr<MediaStreamUI> ui;
- callback.Run(devices, ui.Pass());
+ callback.Run(devices, MEDIA_DEVICE_OK, ui.Pass());
}
TEST_F(MediaStreamUIProxyTest, StopFromUI) {
- MediaStreamRequest request(0, 0, 0, GURL("http://origin/"),
+ MediaStreamRequest request(0, 0, 0, GURL("http://origin/"), false,
MEDIA_GENERATE_STREAM, std::string(),
std::string(),
MEDIA_DEVICE_AUDIO_CAPTURE,
@@ -195,11 +198,11 @@ TEST_F(MediaStreamUIProxyTest, StopFromUI) {
MediaStreamDevice(MEDIA_DEVICE_AUDIO_CAPTURE, "Mic", "Mic"));
scoped_ptr<MockMediaStreamUI> ui(new MockMediaStreamUI());
EXPECT_CALL(*ui, OnStarted(_))
- .WillOnce(SaveArg<0>(&stop_callback));
- callback.Run(devices, ui.PassAs<MediaStreamUI>());
+ .WillOnce(testing::DoAll(SaveArg<0>(&stop_callback), Return(0)));
+ callback.Run(devices, MEDIA_DEVICE_OK, ui.PassAs<MediaStreamUI>());
MediaStreamDevices response;
- EXPECT_CALL(response_callback_, OnAccessRequestResponse(_))
+ EXPECT_CALL(response_callback_, OnAccessRequestResponse(_, _))
.WillOnce(SaveArg<0>(&response));
message_loop_.RunUntilIdle();
@@ -207,7 +210,8 @@ TEST_F(MediaStreamUIProxyTest, StopFromUI) {
MockStopStreamHandler stop_handler;
proxy_->OnStarted(base::Bind(&MockStopStreamHandler::OnStop,
- base::Unretained(&stop_handler)));
+ base::Unretained(&stop_handler)),
+ MediaStreamUIProxy::WindowIdCallback());
message_loop_.RunUntilIdle();
ASSERT_FALSE(stop_callback.is_null());
@@ -216,4 +220,42 @@ TEST_F(MediaStreamUIProxyTest, StopFromUI) {
message_loop_.RunUntilIdle();
}
+TEST_F(MediaStreamUIProxyTest, WindowIdCallbackCalled) {
+ MediaStreamRequest request(0,
+ 0,
+ 0,
+ GURL("http://origin/"),
+ false,
+ MEDIA_GENERATE_STREAM,
+ std::string(),
+ std::string(),
+ MEDIA_NO_SERVICE,
+ MEDIA_DESKTOP_VIDEO_CAPTURE);
+ proxy_->RequestAccess(
+ request,
+ base::Bind(&MockResponseCallback::OnAccessRequestResponse,
+ base::Unretained(&response_callback_)));
+ MediaResponseCallback callback;
+ EXPECT_CALL(delegate_, RequestMediaAccessPermission(SameRequest(request), _))
+ .WillOnce(SaveArg<1>(&callback));
+ message_loop_.RunUntilIdle();
+
+ const int kWindowId = 1;
+ scoped_ptr<MockMediaStreamUI> ui(new MockMediaStreamUI());
+ EXPECT_CALL(*ui, OnStarted(_)).WillOnce(Return(kWindowId));
+
+ callback.Run(
+ MediaStreamDevices(), MEDIA_DEVICE_OK, ui.PassAs<MediaStreamUI>());
+ EXPECT_CALL(response_callback_, OnAccessRequestResponse(_, _));
+
+ MockStopStreamHandler handler;
+ EXPECT_CALL(handler, OnWindowId(kWindowId));
+
+ proxy_->OnStarted(
+ base::Bind(&MockStopStreamHandler::OnStop, base::Unretained(&handler)),
+ base::Bind(&MockStopStreamHandler::OnWindowId,
+ base::Unretained(&handler)));
+ message_loop_.RunUntilIdle();
+}
+
} // content
diff --git a/chromium/content/browser/renderer_host/media/midi_dispatcher_host.cc b/chromium/content/browser/renderer_host/media/midi_dispatcher_host.cc
deleted file mode 100644
index 2181dccdd72..00000000000
--- a/chromium/content/browser/renderer_host/media/midi_dispatcher_host.cc
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/renderer_host/media/midi_dispatcher_host.h"
-
-#include "base/bind.h"
-#include "content/browser/child_process_security_policy_impl.h"
-#include "content/browser/renderer_host/render_view_host_impl.h"
-#include "content/common/media/midi_messages.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/browser_thread.h"
-#include "url/gurl.h"
-
-namespace content {
-
-MIDIDispatcherHost::MIDIDispatcherHost(int render_process_id,
- BrowserContext* browser_context)
- : render_process_id_(render_process_id),
- browser_context_(browser_context) {
-}
-
-MIDIDispatcherHost::~MIDIDispatcherHost() {
-}
-
-bool MIDIDispatcherHost::OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) {
- bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(MIDIDispatcherHost, message, *message_was_ok)
- IPC_MESSAGE_HANDLER(MIDIHostMsg_RequestSysExPermission,
- OnRequestSysExPermission)
- IPC_MESSAGE_HANDLER(MIDIHostMsg_CancelSysExPermissionRequest,
- OnCancelSysExPermissionRequest)
- IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
- return handled;
-}
-
-void MIDIDispatcherHost::OverrideThreadForMessage(
- const IPC::Message& message, BrowserThread::ID* thread) {
- if (IPC_MESSAGE_CLASS(message) == MIDIMsgStart)
- *thread = BrowserThread::UI;
-}
-
-void MIDIDispatcherHost::OnRequestSysExPermission(int render_view_id,
- int bridge_id,
- const GURL& origin) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- browser_context_->RequestMIDISysExPermission(
- render_process_id_,
- render_view_id,
- bridge_id,
- origin,
- base::Bind(&MIDIDispatcherHost::WasSysExPermissionGranted,
- base::Unretained(this),
- render_view_id,
- bridge_id));
-}
-
-void MIDIDispatcherHost::OnCancelSysExPermissionRequest(
- int render_view_id,
- int bridge_id,
- const GURL& requesting_frame) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DVLOG(1) << __FUNCTION__ << " " << render_process_id_ << ":" << render_view_id
- << ":" << bridge_id;
- browser_context_->CancelMIDISysExPermissionRequest(
- render_process_id_, render_view_id, bridge_id, requesting_frame);
-}
-void MIDIDispatcherHost::WasSysExPermissionGranted(int render_view_id,
- int bridge_id,
- bool success) {
- ChildProcessSecurityPolicyImpl::GetInstance()->GrantSendMIDISysExMessage(
- render_process_id_);
- Send(new MIDIMsg_SysExPermissionApproved(render_view_id, bridge_id, success));
-}
-
-} // namespace content
diff --git a/chromium/content/browser/renderer_host/media/midi_dispatcher_host.h b/chromium/content/browser/renderer_host/media/midi_dispatcher_host.h
deleted file mode 100644
index 068f71918f5..00000000000
--- a/chromium/content/browser/renderer_host/media/midi_dispatcher_host.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_MIDI_DISPATCHER_HOST_H_
-#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_MIDI_DISPATCHER_HOST_H_
-
-#include "content/public/browser/browser_message_filter.h"
-
-class GURL;
-
-namespace content {
-
-class BrowserContext;
-
-// MIDIDispatcherHost handles permissions for using system exclusive messages.
-// It works as BrowserMessageFilter to handle IPC messages between
-// MIDIDispatcher running as a RenderViewObserver.
-class MIDIDispatcherHost : public BrowserMessageFilter {
- public:
- MIDIDispatcherHost(int render_process_id, BrowserContext* browser_context);
-
- // BrowserMessageFilter implementation.
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
- virtual void OverrideThreadForMessage(
- const IPC::Message& message, BrowserThread::ID* thread) OVERRIDE;
-
- protected:
- virtual ~MIDIDispatcherHost();
-
- private:
- void OnRequestSysExPermission(int render_view_id,
- int bridge_id,
- const GURL& origin);
- void OnCancelSysExPermissionRequest(int render_view_id,
- int bridge_id,
- const GURL& requesting_frame);
- void WasSysExPermissionGranted(int render_view_id,
- int bridge_id,
- bool success);
-
- int render_process_id_;
- BrowserContext* browser_context_;
-
- DISALLOW_COPY_AND_ASSIGN(MIDIDispatcherHost);
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_MIDI_DISPATCHER_HOST_H_
diff --git a/chromium/content/browser/renderer_host/media/mock_media_observer.h b/chromium/content/browser/renderer_host/media/mock_media_observer.h
index c3543f3002b..04678c9235a 100644
--- a/chromium/content/browser/renderer_host/media/mock_media_observer.h
+++ b/chromium/content/browser/renderer_host/media/mock_media_observer.h
@@ -20,22 +20,11 @@ class MockMediaObserver : public MediaObserver {
MockMediaObserver();
virtual ~MockMediaObserver();
- MOCK_METHOD1(OnAudioCaptureDevicesChanged,
- void(const MediaStreamDevices& devices));
- MOCK_METHOD1(OnVideoCaptureDevicesChanged,
- void(const MediaStreamDevices& devices));
- MOCK_METHOD5(OnMediaRequestStateChanged,
+ MOCK_METHOD6(OnMediaRequestStateChanged,
void(int render_process_id, int render_view_id,
- int page_request_id,
+ int page_request_id, const GURL& security_origin,
const MediaStreamDevice& device,
const MediaRequestState state));
- MOCK_METHOD6(OnAudioStreamPlayingChanged,
- void(int render_process_id,
- int render_view_id,
- int stream_id,
- bool is_playing,
- float power_dbfs,
- bool clipped));
};
} // namespace content
diff --git a/chromium/content/browser/renderer_host/media/peer_connection_tracker_host.cc b/chromium/content/browser/renderer_host/media/peer_connection_tracker_host.cc
index b986055e700..a5e0ba3fb0a 100644
--- a/chromium/content/browser/renderer_host/media/peer_connection_tracker_host.cc
+++ b/chromium/content/browser/renderer_host/media/peer_connection_tracker_host.cc
@@ -9,13 +9,13 @@
namespace content {
PeerConnectionTrackerHost::PeerConnectionTrackerHost(int render_process_id)
- : render_process_id_(render_process_id) {}
+ : BrowserMessageFilter(PeerConnectionTrackerMsgStart),
+ render_process_id_(render_process_id) {}
-bool PeerConnectionTrackerHost::OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) {
+bool PeerConnectionTrackerHost::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(PeerConnectionTrackerHost, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(PeerConnectionTrackerHost, message)
IPC_MESSAGE_HANDLER(PeerConnectionTrackerHost_AddPeerConnection,
OnAddPeerConnection)
IPC_MESSAGE_HANDLER(PeerConnectionTrackerHost_RemovePeerConnection,
@@ -23,8 +23,9 @@ bool PeerConnectionTrackerHost::OnMessageReceived(const IPC::Message& message,
IPC_MESSAGE_HANDLER(PeerConnectionTrackerHost_UpdatePeerConnection,
OnUpdatePeerConnection)
IPC_MESSAGE_HANDLER(PeerConnectionTrackerHost_AddStats, OnAddStats)
+ IPC_MESSAGE_HANDLER(PeerConnectionTrackerHost_GetUserMedia, OnGetUserMedia)
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return handled;
}
@@ -66,4 +67,19 @@ void PeerConnectionTrackerHost::OnAddStats(int lid,
WebRTCInternals::GetInstance()->OnAddStats(peer_pid(), lid, value);
}
+void PeerConnectionTrackerHost::OnGetUserMedia(
+ const std::string& origin,
+ bool audio,
+ bool video,
+ const std::string& audio_constraints,
+ const std::string& video_constraints) {
+ WebRTCInternals::GetInstance()->OnGetUserMedia(render_process_id_,
+ peer_pid(),
+ origin,
+ audio,
+ video,
+ audio_constraints,
+ video_constraints);
+}
+
} // namespace content
diff --git a/chromium/content/browser/renderer_host/media/peer_connection_tracker_host.h b/chromium/content/browser/renderer_host/media/peer_connection_tracker_host.h
index 2803e7be602..6a411e8ba00 100644
--- a/chromium/content/browser/renderer_host/media/peer_connection_tracker_host.h
+++ b/chromium/content/browser/renderer_host/media/peer_connection_tracker_host.h
@@ -23,8 +23,7 @@ class PeerConnectionTrackerHost : public BrowserMessageFilter {
PeerConnectionTrackerHost(int render_process_id);
// content::BrowserMessageFilter override.
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
virtual void OverrideThreadForMessage(const IPC::Message& message,
BrowserThread::ID* thread) OVERRIDE;
@@ -38,6 +37,11 @@ class PeerConnectionTrackerHost : public BrowserMessageFilter {
void OnUpdatePeerConnection(
int lid, const std::string& type, const std::string& value);
void OnAddStats(int lid, const base::ListValue& value);
+ void OnGetUserMedia(const std::string& origin,
+ bool audio,
+ bool video,
+ const std::string& audio_constraints,
+ const std::string& video_constraints);
int render_process_id_;
diff --git a/chromium/content/browser/renderer_host/media/video_capture_controller.cc b/chromium/content/browser/renderer_host/media/video_capture_controller.cc
index 1840af2c3af..4775a77c061 100644
--- a/chromium/content/browser/renderer_host/media/video_capture_controller.cc
+++ b/chromium/content/browser/renderer_host/media/video_capture_controller.cc
@@ -4,21 +4,22 @@
#include "content/browser/renderer_host/media/video_capture_controller.h"
+#include <map>
#include <set>
#include "base/bind.h"
#include "base/debug/trace_event.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/sparse_histogram.h"
#include "base/stl_util.h"
#include "content/browser/renderer_host/media/media_stream_manager.h"
#include "content/browser/renderer_host/media/video_capture_manager.h"
#include "content/public/browser/browser_thread.h"
+#include "gpu/command_buffer/common/mailbox_holder.h"
#include "media/base/video_frame.h"
#include "media/base/video_util.h"
#include "media/base/yuv_convert.h"
-
-#if !defined(AVOID_LIBYUV_FOR_ANDROID_WEBVIEW)
#include "third_party/libyuv/include/libyuv.h"
-#endif
using media::VideoCaptureFormat;
@@ -26,6 +27,13 @@ namespace content {
namespace {
+static const int kInfiniteRatio = 99999;
+
+#define UMA_HISTOGRAM_ASPECT_RATIO(name, width, height) \
+ UMA_HISTOGRAM_SPARSE_SLOWLY( \
+ name, \
+ (height) ? ((width) * 100) / (height) : kInfiniteRatio);
+
// The number of buffers that VideoCaptureBufferPool should allocate.
const int kNoOfBuffers = 3;
@@ -74,8 +82,10 @@ struct VideoCaptureController::ControllerClient {
// Buffers that are currently known to this client.
std::set<int> known_buffers;
- // Buffers currently held by this client.
- std::set<int> active_buffers;
+ // Buffers currently held by this client, and syncpoint callback to call when
+ // they are returned from the client.
+ typedef std::map<int, scoped_refptr<media::VideoFrame> > ActiveBufferMap;
+ ActiveBufferMap active_buffers;
// State of capture session, controlled by VideoCaptureManager directly. This
// transitions to true as soon as StopSession() occurs, at which point the
@@ -110,18 +120,18 @@ class VideoCaptureController::VideoCaptureDeviceClient
virtual scoped_refptr<Buffer> ReserveOutputBuffer(
media::VideoFrame::Format format,
const gfx::Size& size) OVERRIDE;
- virtual void OnIncomingCapturedFrame(const uint8* data,
- int length,
- base::Time timestamp,
- int rotation,
- const VideoCaptureFormat& frame_format)
- OVERRIDE;
- virtual void OnIncomingCapturedBuffer(const scoped_refptr<Buffer>& buffer,
- media::VideoFrame::Format format,
- const gfx::Size& dimensions,
- base::Time timestamp,
- int frame_rate) OVERRIDE;
- virtual void OnError() OVERRIDE;
+ virtual void OnIncomingCapturedData(const uint8* data,
+ int length,
+ const VideoCaptureFormat& frame_format,
+ int rotation,
+ base::TimeTicks timestamp) OVERRIDE;
+ virtual void OnIncomingCapturedVideoFrame(
+ const scoped_refptr<Buffer>& buffer,
+ const VideoCaptureFormat& buffer_format,
+ const scoped_refptr<media::VideoFrame>& frame,
+ base::TimeTicks timestamp) OVERRIDE;
+ virtual void OnError(const std::string& reason) OVERRIDE;
+ virtual void OnLog(const std::string& message) OVERRIDE;
private:
scoped_refptr<Buffer> DoReserveOutputBuffer(media::VideoFrame::Format format,
@@ -132,6 +142,8 @@ class VideoCaptureController::VideoCaptureDeviceClient
// The pool of shared-memory buffers used for capturing.
const scoped_refptr<VideoCaptureBufferPool> buffer_pool_;
+
+ bool first_frame_;
};
VideoCaptureController::VideoCaptureController()
@@ -143,7 +155,7 @@ VideoCaptureController::VideoCaptureController()
VideoCaptureController::VideoCaptureDeviceClient::VideoCaptureDeviceClient(
const base::WeakPtr<VideoCaptureController>& controller,
const scoped_refptr<VideoCaptureBufferPool>& buffer_pool)
- : controller_(controller), buffer_pool_(buffer_pool) {}
+ : controller_(controller), buffer_pool_(buffer_pool), first_frame_(true) {}
VideoCaptureController::VideoCaptureDeviceClient::~VideoCaptureDeviceClient() {}
@@ -164,7 +176,7 @@ void VideoCaptureController::AddClient(
base::ProcessHandle render_process,
media::VideoCaptureSessionId session_id,
const media::VideoCaptureParams& params) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DVLOG(1) << "VideoCaptureController::AddClient, id " << id.device_id
<< ", " << params.requested_format.frame_size.ToString()
<< ", " << params.requested_format.frame_rate
@@ -198,7 +210,7 @@ void VideoCaptureController::AddClient(
int VideoCaptureController::RemoveClient(
const VideoCaptureControllerID& id,
VideoCaptureControllerEventHandler* event_handler) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DVLOG(1) << "VideoCaptureController::RemoveClient, id " << id.device_id;
ControllerClient* client = FindClient(id, event_handler, controller_clients_);
@@ -206,11 +218,11 @@ int VideoCaptureController::RemoveClient(
return kInvalidMediaCaptureSessionId;
// Take back all buffers held by the |client|.
- for (std::set<int>::iterator buffer_it = client->active_buffers.begin();
+ for (ControllerClient::ActiveBufferMap::iterator buffer_it =
+ client->active_buffers.begin();
buffer_it != client->active_buffers.end();
++buffer_it) {
- int buffer_id = *buffer_it;
- buffer_pool_->RelinquishConsumerHold(buffer_id, 1);
+ buffer_pool_->RelinquishConsumerHold(buffer_it->first, 1);
}
client->active_buffers.clear();
@@ -222,7 +234,7 @@ int VideoCaptureController::RemoveClient(
}
void VideoCaptureController::StopSession(int session_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DVLOG(1) << "VideoCaptureController::StopSession, id " << session_id;
ControllerClient* client = FindClient(session_id, controller_clients_);
@@ -236,24 +248,34 @@ void VideoCaptureController::StopSession(int session_id) {
void VideoCaptureController::ReturnBuffer(
const VideoCaptureControllerID& id,
VideoCaptureControllerEventHandler* event_handler,
- int buffer_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ int buffer_id,
+ const std::vector<uint32>& sync_points) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
ControllerClient* client = FindClient(id, event_handler, controller_clients_);
// If this buffer is not held by this client, or this client doesn't exist
// in controller, do nothing.
- if (!client || !client->active_buffers.erase(buffer_id)) {
+ ControllerClient::ActiveBufferMap::iterator iter;
+ if (!client || (iter = client->active_buffers.find(buffer_id)) ==
+ client->active_buffers.end()) {
NOTREACHED();
return;
}
+ scoped_refptr<media::VideoFrame> frame = iter->second;
+ client->active_buffers.erase(iter);
+
+ if (frame->format() == media::VideoFrame::NATIVE_TEXTURE) {
+ for (size_t i = 0; i < sync_points.size(); i++)
+ frame->AppendReleaseSyncPoint(sync_points[i]);
+ }
buffer_pool_->RelinquishConsumerHold(buffer_id, 1);
}
const media::VideoCaptureFormat&
VideoCaptureController::GetVideoCaptureFormat() const {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
return video_capture_format_;
}
@@ -264,13 +286,13 @@ VideoCaptureController::VideoCaptureDeviceClient::ReserveOutputBuffer(
return DoReserveOutputBuffer(format, size);
}
-void VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedFrame(
+void VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedData(
const uint8* data,
int length,
- base::Time timestamp,
+ const VideoCaptureFormat& frame_format,
int rotation,
- const VideoCaptureFormat& frame_format) {
- TRACE_EVENT0("video", "VideoCaptureController::OnIncomingCapturedFrame");
+ base::TimeTicks timestamp) {
+ TRACE_EVENT0("video", "VideoCaptureController::OnIncomingCapturedData");
if (!frame_format.IsValid())
return;
@@ -298,13 +320,21 @@ void VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedFrame(
destination_height = new_unrotated_width;
}
const gfx::Size dimensions(destination_width, destination_height);
+ if (!media::VideoFrame::IsValidConfig(media::VideoFrame::I420,
+ dimensions,
+ gfx::Rect(dimensions),
+ dimensions)) {
+ return;
+ }
+
scoped_refptr<Buffer> buffer =
DoReserveOutputBuffer(media::VideoFrame::I420, dimensions);
if (!buffer)
return;
-#if !defined(AVOID_LIBYUV_FOR_ANDROID_WEBVIEW)
- uint8* yplane = reinterpret_cast<uint8*>(buffer->data());
+ uint8* yplane = NULL;
+ bool flip = false;
+ yplane = reinterpret_cast<uint8*>(buffer->data());
uint8* uplane =
yplane +
media::VideoFrame::PlaneAllocationSize(
@@ -351,7 +381,14 @@ void VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedFrame(
origin_colorspace = libyuv::FOURCC_UYVY;
break;
case media::PIXEL_FORMAT_RGB24:
- origin_colorspace = libyuv::FOURCC_RAW;
+ origin_colorspace = libyuv::FOURCC_24BG;
+#if defined(OS_WIN)
+ // TODO(wjia): Currently, for RGB24 on WIN, capture device always
+ // passes in positive src_width and src_height. Remove this hardcoded
+ // value when nagative src_height is supported. The negative src_height
+ // indicates that vertical flipping is needed.
+ flip = true;
+#endif
break;
case media::PIXEL_FORMAT_ARGB:
origin_colorspace = libyuv::FOURCC_ARGB;
@@ -363,106 +400,115 @@ void VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedFrame(
NOTREACHED();
}
- int need_convert_rgb24_on_win = false;
-#if defined(OS_WIN)
- // TODO(wjia): Use libyuv::ConvertToI420 since support for image inversion
- // (vertical flipping) has been added. Use negative src_height as indicator.
- if (frame_format.pixel_format == media::PIXEL_FORMAT_RGB24) {
- // Rotation is not supported in kRGB24 and OS_WIN case.
- DCHECK(!rotation);
- need_convert_rgb24_on_win = true;
- }
-#endif
- if (need_convert_rgb24_on_win) {
- int rgb_stride = -3 * (new_unrotated_width + chopped_width);
- const uint8* rgb_src =
- data + 3 * (new_unrotated_width + chopped_width) *
- (new_unrotated_height - 1 + chopped_height);
- media::ConvertRGB24ToYUV(rgb_src,
- yplane,
- uplane,
- vplane,
- new_unrotated_width,
- new_unrotated_height,
- rgb_stride,
- yplane_stride,
- uv_plane_stride);
- } else {
- libyuv::ConvertToI420(data,
- length,
- yplane,
- yplane_stride,
- uplane,
- uv_plane_stride,
- vplane,
- uv_plane_stride,
- crop_x,
- crop_y,
- new_unrotated_width + chopped_width,
- new_unrotated_height,
- new_unrotated_width,
- new_unrotated_height,
- rotation_mode,
- origin_colorspace);
- }
-#else
- // Libyuv is not linked in for Android WebView builds, but video capture is
- // not used in those builds either. Whenever libyuv is added in that build,
- // address all these #ifdef parts, see http://crbug.com/299611 .
- NOTREACHED();
-#endif // if !defined(AVOID_LIBYUV_FOR_ANDROID_WEBVIEW)
+ libyuv::ConvertToI420(data,
+ length,
+ yplane,
+ yplane_stride,
+ uplane,
+ uv_plane_stride,
+ vplane,
+ uv_plane_stride,
+ crop_x,
+ crop_y,
+ frame_format.frame_size.width(),
+ (flip ? -frame_format.frame_size.height() :
+ frame_format.frame_size.height()),
+ new_unrotated_width,
+ new_unrotated_height,
+ rotation_mode,
+ origin_colorspace);
+ scoped_refptr<media::VideoFrame> frame =
+ media::VideoFrame::WrapExternalPackedMemory(
+ media::VideoFrame::I420,
+ dimensions,
+ gfx::Rect(dimensions),
+ dimensions,
+ yplane,
+ media::VideoFrame::AllocationSize(media::VideoFrame::I420,
+ dimensions),
+ base::SharedMemory::NULLHandle(),
+ base::TimeDelta(),
+ base::Closure());
+ DCHECK(frame);
+
+ VideoCaptureFormat format(
+ dimensions, frame_format.frame_rate, media::PIXEL_FORMAT_I420);
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(
- &VideoCaptureController::DoIncomingCapturedI420BufferOnIOThread,
+ &VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread,
controller_,
buffer,
- dimensions,
- frame_format.frame_rate,
+ format,
+ frame,
timestamp));
+
+ if (first_frame_) {
+ UMA_HISTOGRAM_COUNTS("Media.VideoCapture.Width",
+ frame_format.frame_size.width());
+ UMA_HISTOGRAM_COUNTS("Media.VideoCapture.Height",
+ frame_format.frame_size.height());
+ UMA_HISTOGRAM_ASPECT_RATIO("Media.VideoCapture.AspectRatio",
+ frame_format.frame_size.width(),
+ frame_format.frame_size.height());
+ UMA_HISTOGRAM_COUNTS("Media.VideoCapture.FrameRate",
+ frame_format.frame_rate);
+ UMA_HISTOGRAM_ENUMERATION("Media.VideoCapture.PixelFormat",
+ frame_format.pixel_format,
+ media::PIXEL_FORMAT_MAX);
+ first_frame_ = false;
+ }
}
-void VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedBuffer(
+void
+VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedVideoFrame(
const scoped_refptr<Buffer>& buffer,
- media::VideoFrame::Format format,
- const gfx::Size& dimensions,
- base::Time timestamp,
- int frame_rate) {
- // The capture pipeline expects I420 for now.
- DCHECK_EQ(format, media::VideoFrame::I420)
- << "Non-I420 output buffer returned";
-
+ const VideoCaptureFormat& buffer_format,
+ const scoped_refptr<media::VideoFrame>& frame,
+ base::TimeTicks timestamp) {
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(
- &VideoCaptureController::DoIncomingCapturedI420BufferOnIOThread,
+ &VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread,
controller_,
buffer,
- dimensions,
- frame_rate,
+ buffer_format,
+ frame,
timestamp));
}
-void VideoCaptureController::VideoCaptureDeviceClient::OnError() {
+void VideoCaptureController::VideoCaptureDeviceClient::OnError(
+ const std::string& reason) {
+ MediaStreamManager::SendMessageToNativeLog(
+ "Error on video capture: " + reason);
BrowserThread::PostTask(BrowserThread::IO,
FROM_HERE,
base::Bind(&VideoCaptureController::DoErrorOnIOThread, controller_));
}
+void VideoCaptureController::VideoCaptureDeviceClient::OnLog(
+ const std::string& message) {
+ MediaStreamManager::SendMessageToNativeLog("Video capture: " + message);
+}
+
scoped_refptr<media::VideoCaptureDevice::Client::Buffer>
VideoCaptureController::VideoCaptureDeviceClient::DoReserveOutputBuffer(
media::VideoFrame::Format format,
const gfx::Size& dimensions) {
- // The capture pipeline expects I420 for now.
- DCHECK_EQ(format, media::VideoFrame::I420)
- << "Non-I420 output buffer requested";
+ size_t frame_bytes = 0;
+ if (format == media::VideoFrame::NATIVE_TEXTURE) {
+ DCHECK_EQ(dimensions.width(), 0);
+ DCHECK_EQ(dimensions.height(), 0);
+ } else {
+ // The capture pipeline expects I420 for now.
+ DCHECK_EQ(format, media::VideoFrame::I420)
+ << "Non-I420 output buffer format " << format << " requested";
+ frame_bytes = media::VideoFrame::AllocationSize(format, dimensions);
+ }
int buffer_id_to_drop = VideoCaptureBufferPool::kInvalidId;
- const size_t frame_bytes =
- media::VideoFrame::AllocationSize(format, dimensions);
-
int buffer_id =
buffer_pool_->ReserveForProducer(frame_bytes, &buffer_id_to_drop);
if (buffer_id == VideoCaptureBufferPool::kInvalidId)
@@ -489,17 +535,14 @@ VideoCaptureController::~VideoCaptureController() {
controller_clients_.end());
}
-void VideoCaptureController::DoIncomingCapturedI420BufferOnIOThread(
- scoped_refptr<media::VideoCaptureDevice::Client::Buffer> buffer,
- const gfx::Size& dimensions,
- int frame_rate,
- base::Time timestamp) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+void VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread(
+ const scoped_refptr<media::VideoCaptureDevice::Client::Buffer>& buffer,
+ const media::VideoCaptureFormat& buffer_format,
+ const scoped_refptr<media::VideoFrame>& frame,
+ base::TimeTicks timestamp) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK_NE(buffer->id(), VideoCaptureBufferPool::kInvalidId);
- VideoCaptureFormat frame_format(
- dimensions, frame_rate, media::PIXEL_FORMAT_I420);
-
int count = 0;
if (state_ == VIDEO_CAPTURE_STATE_STARTED) {
for (ControllerClients::iterator client_it = controller_clients_.begin();
@@ -508,19 +551,30 @@ void VideoCaptureController::DoIncomingCapturedI420BufferOnIOThread(
if (client->session_closed)
continue;
- bool is_new_buffer = client->known_buffers.insert(buffer->id()).second;
- if (is_new_buffer) {
- // On the first use of a buffer on a client, share the memory handle.
- size_t memory_size = 0;
- base::SharedMemoryHandle remote_handle = buffer_pool_->ShareToProcess(
- buffer->id(), client->render_process_handle, &memory_size);
- client->event_handler->OnBufferCreated(
- client->controller_id, remote_handle, memory_size, buffer->id());
+ if (frame->format() == media::VideoFrame::NATIVE_TEXTURE) {
+ client->event_handler->OnMailboxBufferReady(client->controller_id,
+ buffer->id(),
+ *frame->mailbox_holder(),
+ buffer_format,
+ timestamp);
+ } else {
+ bool is_new_buffer = client->known_buffers.insert(buffer->id()).second;
+ if (is_new_buffer) {
+ // On the first use of a buffer on a client, share the memory handle.
+ size_t memory_size = 0;
+ base::SharedMemoryHandle remote_handle = buffer_pool_->ShareToProcess(
+ buffer->id(), client->render_process_handle, &memory_size);
+ client->event_handler->OnBufferCreated(
+ client->controller_id, remote_handle, memory_size, buffer->id());
+ }
+
+ client->event_handler->OnBufferReady(
+ client->controller_id, buffer->id(), buffer_format, timestamp);
}
- client->event_handler->OnBufferReady(
- client->controller_id, buffer->id(), timestamp, frame_format);
- bool inserted = client->active_buffers.insert(buffer->id()).second;
+ bool inserted =
+ client->active_buffers.insert(std::make_pair(buffer->id(), frame))
+ .second;
DCHECK(inserted) << "Unexpected duplicate buffer: " << buffer->id();
count++;
}
@@ -530,7 +584,7 @@ void VideoCaptureController::DoIncomingCapturedI420BufferOnIOThread(
}
void VideoCaptureController::DoErrorOnIOThread() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
state_ = VIDEO_CAPTURE_STATE_ERROR;
for (ControllerClients::iterator client_it = controller_clients_.begin();
@@ -545,7 +599,7 @@ void VideoCaptureController::DoErrorOnIOThread() {
void VideoCaptureController::DoBufferDestroyedOnIOThread(
int buffer_id_to_drop) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
for (ControllerClients::iterator client_it = controller_clients_.begin();
client_it != controller_clients_.end(); ++client_it) {
@@ -589,7 +643,7 @@ VideoCaptureController::FindClient(
}
int VideoCaptureController::GetClientCount() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
return controller_clients_.size();
}
diff --git a/chromium/content/browser/renderer_host/media/video_capture_controller.h b/chromium/content/browser/renderer_host/media/video_capture_controller.h
index 8658cdeb278..9b8287002ec 100644
--- a/chromium/content/browser/renderer_host/media/video_capture_controller.h
+++ b/chromium/content/browser/renderer_host/media/video_capture_controller.h
@@ -58,7 +58,6 @@
#include "content/browser/renderer_host/media/video_capture_controller_event_handler.h"
#include "content/common/content_export.h"
#include "content/common/media/video_capture.h"
-#include "media/video/capture/video_capture.h"
#include "media/video/capture/video_capture_device.h"
#include "media/video/capture/video_capture_types.h"
@@ -98,11 +97,15 @@ class CONTENT_EXPORT VideoCaptureController {
// prematurely closed.
void StopSession(int session_id);
- // Return a buffer previously given in
- // VideoCaptureControllerEventHandler::OnBufferReady.
+ // Return a buffer with id |buffer_id| previously given in
+ // VideoCaptureControllerEventHandler::OnBufferReady. In the case that the
+ // buffer was backed by a texture, |sync_point| will be waited on before
+ // destroying or recycling the texture, to synchronize with texture users in
+ // the renderer process.
void ReturnBuffer(const VideoCaptureControllerID& id,
VideoCaptureControllerEventHandler* event_handler,
- int buffer_id);
+ int buffer_id,
+ const std::vector<uint32>& sync_points);
const media::VideoCaptureFormat& GetVideoCaptureFormat() const;
@@ -113,11 +116,11 @@ class CONTENT_EXPORT VideoCaptureController {
typedef std::list<ControllerClient*> ControllerClients;
// Worker functions on IO thread. Called by the VideoCaptureDeviceClient.
- void DoIncomingCapturedI420BufferOnIOThread(
- scoped_refptr<media::VideoCaptureDevice::Client::Buffer> buffer,
- const gfx::Size& dimensions,
- int frame_rate,
- base::Time timestamp);
+ void DoIncomingCapturedVideoFrameOnIOThread(
+ const scoped_refptr<media::VideoCaptureDevice::Client::Buffer>& buffer,
+ const media::VideoCaptureFormat& format,
+ const scoped_refptr<media::VideoFrame>& frame,
+ base::TimeTicks timestamp);
void DoErrorOnIOThread();
void DoDeviceStoppedOnIOThread();
void DoBufferDestroyedOnIOThread(int buffer_id_to_drop);
diff --git a/chromium/content/browser/renderer_host/media/video_capture_controller_event_handler.h b/chromium/content/browser/renderer_host/media/video_capture_controller_event_handler.h
index 6559a530b1d..3e709476a12 100644
--- a/chromium/content/browser/renderer_host/media/video_capture_controller_event_handler.h
+++ b/chromium/content/browser/renderer_host/media/video_capture_controller_event_handler.h
@@ -8,7 +8,14 @@
#include "base/memory/shared_memory.h"
#include "base/time/time.h"
#include "content/common/content_export.h"
-#include "media/video/capture/video_capture_types.h"
+
+namespace gpu {
+struct MailboxHolder;
+} // namespace gpu
+
+namespace media {
+class VideoCaptureFormat;
+} // namespace media
namespace content {
@@ -41,11 +48,17 @@ class CONTENT_EXPORT VideoCaptureControllerEventHandler {
int buffer_id) = 0;
// A buffer has been filled with I420 video.
- virtual void OnBufferReady(
- const VideoCaptureControllerID& id,
- int buffer_id,
- base::Time timestamp,
- const media::VideoCaptureFormat& format) = 0;
+ virtual void OnBufferReady(const VideoCaptureControllerID& id,
+ int buffer_id,
+ const media::VideoCaptureFormat& format,
+ base::TimeTicks timestamp) = 0;
+
+ // A texture mailbox buffer has been filled with data.
+ virtual void OnMailboxBufferReady(const VideoCaptureControllerID& id,
+ int buffer_id,
+ const gpu::MailboxHolder& mailbox_holder,
+ const media::VideoCaptureFormat& format,
+ base::TimeTicks timestamp) = 0;
// The capture session has ended and no more frames will be sent.
virtual void OnEnded(const VideoCaptureControllerID& id) = 0;
diff --git a/chromium/content/browser/renderer_host/media/video_capture_controller_unittest.cc b/chromium/content/browser/renderer_host/media/video_capture_controller_unittest.cc
index 20cc4e41a89..f650397de7b 100644
--- a/chromium/content/browser/renderer_host/media/video_capture_controller_unittest.cc
+++ b/chromium/content/browser/renderer_host/media/video_capture_controller_unittest.cc
@@ -18,7 +18,7 @@
#include "content/browser/renderer_host/media/video_capture_manager.h"
#include "content/common/media/media_stream_options.h"
#include "content/public/test/test_browser_thread_bundle.h"
-#include "media/base/video_frame.h"
+#include "gpu/command_buffer/common/mailbox_holder.h"
#include "media/base/video_util.h"
#include "media/video/capture/video_capture_types.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -42,6 +42,7 @@ class MockVideoCaptureControllerEventHandler
MOCK_METHOD1(DoBufferCreated, void(const VideoCaptureControllerID&));
MOCK_METHOD1(DoBufferDestroyed, void(const VideoCaptureControllerID&));
MOCK_METHOD1(DoBufferReady, void(const VideoCaptureControllerID&));
+ MOCK_METHOD1(DoMailboxBufferReady, void(const VideoCaptureControllerID&));
MOCK_METHOD1(DoEnded, void(const VideoCaptureControllerID&));
MOCK_METHOD1(DoError, void(const VideoCaptureControllerID&));
@@ -59,12 +60,35 @@ class MockVideoCaptureControllerEventHandler
}
virtual void OnBufferReady(const VideoCaptureControllerID& id,
int buffer_id,
- base::Time timestamp,
- const media::VideoCaptureFormat& format) OVERRIDE {
+ const media::VideoCaptureFormat& format,
+ base::TimeTicks timestamp) OVERRIDE {
DoBufferReady(id);
- base::MessageLoop::current()->PostTask(FROM_HERE,
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&VideoCaptureController::ReturnBuffer,
+ base::Unretained(controller_),
+ id,
+ this,
+ buffer_id,
+ std::vector<uint32>()));
+ }
+ virtual void OnMailboxBufferReady(const VideoCaptureControllerID& id,
+ int buffer_id,
+ const gpu::MailboxHolder& mailbox_holder,
+ const media::VideoCaptureFormat& format,
+ base::TimeTicks timestamp) OVERRIDE {
+ DoMailboxBufferReady(id);
+ // Use a very different syncpoint value when returning a new syncpoint.
+ std::vector<uint32> release_sync_points;
+ release_sync_points.push_back(~mailbox_holder.sync_point);
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
base::Bind(&VideoCaptureController::ReturnBuffer,
- base::Unretained(controller_), id, this, buffer_id));
+ base::Unretained(controller_),
+ id,
+ this,
+ buffer_id,
+ release_sync_points));
}
virtual void OnEnded(const VideoCaptureControllerID& id) OVERRIDE {
DoEnded(id);
@@ -99,7 +123,37 @@ class VideoCaptureControllerTest : public testing::Test {
base::RunLoop().RunUntilIdle();
}
- TestBrowserThreadBundle bindle_;
+ scoped_refptr<media::VideoFrame> WrapI420Buffer(
+ const scoped_refptr<media::VideoCaptureDevice::Client::Buffer>& buffer,
+ gfx::Size dimensions) {
+ return media::VideoFrame::WrapExternalPackedMemory(
+ media::VideoFrame::I420,
+ dimensions,
+ gfx::Rect(dimensions),
+ dimensions,
+ reinterpret_cast<uint8*>(buffer->data()),
+ media::VideoFrame::AllocationSize(media::VideoFrame::I420, dimensions),
+ base::SharedMemory::NULLHandle(),
+ base::TimeDelta(),
+ base::Closure());
+ }
+
+ scoped_refptr<media::VideoFrame> WrapMailboxBuffer(
+ const scoped_refptr<media::VideoCaptureDevice::Client::Buffer>& buffer,
+ scoped_ptr<gpu::MailboxHolder> holder,
+ const media::VideoFrame::ReleaseMailboxCB& release_cb,
+ gfx::Size dimensions) {
+ return media::VideoFrame::WrapNativeTexture(
+ holder.Pass(),
+ release_cb,
+ dimensions,
+ gfx::Rect(dimensions),
+ dimensions,
+ base::TimeDelta(),
+ media::VideoFrame::ReadPixelsCB());
+ }
+
+ TestBrowserThreadBundle bundle_;
scoped_ptr<MockVideoCaptureControllerEventHandler> client_a_;
scoped_ptr<MockVideoCaptureControllerEventHandler> client_b_;
scoped_ptr<VideoCaptureController> controller_;
@@ -138,7 +192,7 @@ TEST_F(VideoCaptureControllerTest, AddAndRemoveClients) {
session_100);
// Clients in controller: [A/1]
ASSERT_EQ(1, controller_->GetClientCount())
- << "Adding client A/1 should bump client count.";;
+ << "Adding client A/1 should bump client count.";
controller_->AddClient(client_a_route_2,
client_a_.get(),
base::kNullProcessHandle,
@@ -209,6 +263,13 @@ TEST_F(VideoCaptureControllerTest, AddAndRemoveClients) {
<< "Client count should return to zero after all clients are gone.";
}
+static void CacheSyncPoint(std::vector<uint32>* called_release_sync_points,
+ const std::vector<uint32>& release_sync_points) {
+ DCHECK(called_release_sync_points->empty());
+ called_release_sync_points->assign(release_sync_points.begin(),
+ release_sync_points.end());
+}
+
// This test will connect and disconnect several clients while simulating an
// active capture device being started and generating frames. It runs on one
// thread and is intended to behave deterministically.
@@ -276,11 +337,13 @@ TEST_F(VideoCaptureControllerTest, NormalCaptureMultipleClients) {
EXPECT_CALL(*client_a_, DoBufferCreated(client_a_route_2)).Times(1);
EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_2)).Times(1);
}
- device_->OnIncomingCapturedBuffer(buffer,
- media::VideoFrame::I420,
- capture_resolution,
- base::Time(),
- device_format.frame_rate);
+ device_->OnIncomingCapturedVideoFrame(
+ buffer,
+ media::VideoCaptureFormat(capture_resolution,
+ device_format.frame_rate,
+ media::PIXEL_FORMAT_I420),
+ WrapI420Buffer(buffer, capture_resolution),
+ base::TimeTicks());
buffer = NULL;
base::RunLoop().RunUntilIdle();
@@ -294,11 +357,13 @@ TEST_F(VideoCaptureControllerTest, NormalCaptureMultipleClients) {
device_->ReserveOutputBuffer(media::VideoFrame::I420, capture_resolution);
ASSERT_TRUE(buffer);
memset(buffer->data(), buffer_no++, buffer->size());
- device_->OnIncomingCapturedBuffer(buffer,
- media::VideoFrame::I420,
- capture_resolution,
- base::Time(),
- device_format.frame_rate);
+ device_->OnIncomingCapturedVideoFrame(
+ buffer,
+ media::VideoCaptureFormat(capture_resolution,
+ device_format.frame_rate,
+ media::PIXEL_FORMAT_I420),
+ WrapI420Buffer(buffer, capture_resolution),
+ base::TimeTicks());
buffer = NULL;
// The buffer should be delivered to the clients in any order.
@@ -323,11 +388,13 @@ TEST_F(VideoCaptureControllerTest, NormalCaptureMultipleClients) {
capture_resolution);
ASSERT_TRUE(buffer);
memset(buffer->data(), buffer_no++, buffer->size());
- device_->OnIncomingCapturedBuffer(buffer,
- media::VideoFrame::I420,
- capture_resolution,
- base::Time(),
- device_format.frame_rate);
+ device_->OnIncomingCapturedVideoFrame(
+ buffer,
+ media::VideoCaptureFormat(capture_resolution,
+ device_format.frame_rate,
+ media::PIXEL_FORMAT_I420),
+ WrapI420Buffer(buffer, capture_resolution),
+ base::TimeTicks());
buffer = NULL;
}
// ReserveOutputBuffer ought to fail now, because the pool is depleted.
@@ -361,11 +428,13 @@ TEST_F(VideoCaptureControllerTest, NormalCaptureMultipleClients) {
device_->ReserveOutputBuffer(media::VideoFrame::I420, capture_resolution);
ASSERT_TRUE(buffer);
memset(buffer->data(), buffer_no++, buffer->size());
- device_->OnIncomingCapturedBuffer(buffer,
- media::VideoFrame::I420,
- capture_resolution,
- base::Time(),
- device_format.frame_rate);
+ device_->OnIncomingCapturedVideoFrame(
+ buffer,
+ media::VideoCaptureFormat(capture_resolution,
+ device_format.frame_rate,
+ media::PIXEL_FORMAT_I420),
+ WrapI420Buffer(buffer, capture_resolution),
+ base::TimeTicks());
buffer = NULL;
buffer =
device_->ReserveOutputBuffer(media::VideoFrame::I420, capture_resolution);
@@ -377,11 +446,13 @@ TEST_F(VideoCaptureControllerTest, NormalCaptureMultipleClients) {
}
ASSERT_TRUE(buffer);
memset(buffer->data(), buffer_no++, buffer->size());
- device_->OnIncomingCapturedBuffer(buffer,
- media::VideoFrame::I420,
- capture_resolution,
- base::Time(),
- device_format.frame_rate);
+ device_->OnIncomingCapturedVideoFrame(
+ buffer,
+ media::VideoCaptureFormat(capture_resolution,
+ device_format.frame_rate,
+ media::PIXEL_FORMAT_I420),
+ WrapI420Buffer(buffer, capture_resolution),
+ base::TimeTicks());
buffer = NULL;
// B2 is the only client left, and is the only one that should
// get the buffer.
@@ -389,6 +460,66 @@ TEST_F(VideoCaptureControllerTest, NormalCaptureMultipleClients) {
base::RunLoop().RunUntilIdle();
Mock::VerifyAndClearExpectations(client_a_.get());
Mock::VerifyAndClearExpectations(client_b_.get());
+
+ // Allocate all buffers from the buffer pool, half as SHM buffer and half as
+ // mailbox buffers. Make sure of different counts though.
+ int shm_buffers = kPoolSize / 2;
+ int mailbox_buffers = kPoolSize - shm_buffers;
+ if (shm_buffers == mailbox_buffers) {
+ shm_buffers--;
+ mailbox_buffers++;
+ }
+
+ for (int i = 0; i < shm_buffers; ++i) {
+ buffer = device_->ReserveOutputBuffer(media::VideoFrame::I420,
+ capture_resolution);
+ ASSERT_TRUE(buffer);
+ device_->OnIncomingCapturedVideoFrame(
+ buffer,
+ media::VideoCaptureFormat(capture_resolution,
+ device_format.frame_rate,
+ media::PIXEL_FORMAT_I420),
+ WrapI420Buffer(buffer, capture_resolution),
+ base::TimeTicks());
+ buffer = NULL;
+ }
+ std::vector<uint32> mailbox_syncpoints(mailbox_buffers);
+ std::vector<std::vector<uint32> > release_syncpoint_vectors(mailbox_buffers);
+ for (int i = 0; i < mailbox_buffers; ++i) {
+ buffer = device_->ReserveOutputBuffer(media::VideoFrame::NATIVE_TEXTURE,
+ gfx::Size(0, 0));
+ ASSERT_TRUE(buffer);
+ mailbox_syncpoints[i] = i;
+ device_->OnIncomingCapturedVideoFrame(
+ buffer,
+ media::VideoCaptureFormat(capture_resolution,
+ device_format.frame_rate,
+ media::PIXEL_FORMAT_TEXTURE),
+ WrapMailboxBuffer(
+ buffer,
+ make_scoped_ptr(new gpu::MailboxHolder(
+ gpu::Mailbox(), 0, mailbox_syncpoints[i])),
+ base::Bind(&CacheSyncPoint, &release_syncpoint_vectors[i]),
+ capture_resolution),
+ base::TimeTicks());
+ buffer = NULL;
+ }
+ // ReserveOutputBuffers ought to fail now regardless of buffer format, because
+ // the pool is depleted.
+ ASSERT_FALSE(device_->ReserveOutputBuffer(media::VideoFrame::I420,
+ capture_resolution));
+ ASSERT_FALSE(device_->ReserveOutputBuffer(media::VideoFrame::NATIVE_TEXTURE,
+ gfx::Size(0, 0)));
+ EXPECT_CALL(*client_b_, DoBufferReady(client_b_route_2)).Times(shm_buffers);
+ EXPECT_CALL(*client_b_, DoMailboxBufferReady(client_b_route_2))
+ .Times(mailbox_buffers);
+ base::RunLoop().RunUntilIdle();
+ for (size_t i = 0; i < mailbox_syncpoints.size(); ++i) {
+ // See: MockVideoCaptureControllerEventHandler::OnMailboxBufferReady()
+ ASSERT_EQ(1u, release_syncpoint_vectors[i].size());
+ ASSERT_EQ(mailbox_syncpoints[i], ~release_syncpoint_vectors[i][0]);
+ }
+ Mock::VerifyAndClearExpectations(client_b_.get());
}
// Exercises the OnError() codepath of VideoCaptureController, and tests the
@@ -407,7 +538,7 @@ TEST_F(VideoCaptureControllerTest, ErrorBeforeDeviceCreation) {
// Start with one client.
controller_->AddClient(
route_id, client_a_.get(), base::kNullProcessHandle, 100, session_100);
- device_->OnError();
+ device_->OnError("Test Error");
EXPECT_CALL(*client_a_, DoError(route_id)).Times(1);
base::RunLoop().RunUntilIdle();
Mock::VerifyAndClearExpectations(client_a_.get());
@@ -424,8 +555,12 @@ TEST_F(VideoCaptureControllerTest, ErrorBeforeDeviceCreation) {
device_->ReserveOutputBuffer(media::VideoFrame::I420, capture_resolution);
ASSERT_TRUE(buffer);
- device_->OnIncomingCapturedBuffer(
- buffer, media::VideoFrame::I420, capture_resolution, base::Time(), 30);
+ device_->OnIncomingCapturedVideoFrame(
+ buffer,
+ media::VideoCaptureFormat(
+ capture_resolution, 30, media::PIXEL_FORMAT_I420),
+ WrapI420Buffer(buffer, capture_resolution),
+ base::TimeTicks());
buffer = NULL;
base::RunLoop().RunUntilIdle();
@@ -459,12 +594,13 @@ TEST_F(VideoCaptureControllerTest, ErrorAfterDeviceCreation) {
device_->ReserveOutputBuffer(media::VideoFrame::I420, dims);
ASSERT_TRUE(buffer);
- device_->OnError();
- device_->OnIncomingCapturedBuffer(buffer,
- media::VideoFrame::I420,
- dims,
- base::Time(),
- device_format.frame_rate);
+ device_->OnError("Test error");
+ device_->OnIncomingCapturedVideoFrame(
+ buffer,
+ media::VideoCaptureFormat(
+ dims, device_format.frame_rate, media::PIXEL_FORMAT_I420),
+ WrapI420Buffer(buffer, dims),
+ base::TimeTicks());
buffer = NULL;
EXPECT_CALL(*client_a_, DoError(route_id)).Times(1);
diff --git a/chromium/content/browser/renderer_host/media/video_capture_host.cc b/chromium/content/browser/renderer_host/media/video_capture_host.cc
index 039d9815ab6..a7fdf9d76e1 100644
--- a/chromium/content/browser/renderer_host/media/video_capture_host.cc
+++ b/chromium/content/browser/renderer_host/media/video_capture_host.cc
@@ -14,19 +14,26 @@
namespace content {
VideoCaptureHost::VideoCaptureHost(MediaStreamManager* media_stream_manager)
- : media_stream_manager_(media_stream_manager) {
+ : BrowserMessageFilter(VideoCaptureMsgStart),
+ media_stream_manager_(media_stream_manager) {
}
VideoCaptureHost::~VideoCaptureHost() {}
void VideoCaptureHost::OnChannelClosing() {
- // Since the IPC channel is gone, close all requested VideoCaptureDevices.
- for (EntryMap::iterator it = entries_.begin(); it != entries_.end(); it++) {
+ // Since the IPC sender is gone, close all requested VideoCaptureDevices.
+ for (EntryMap::iterator it = entries_.begin(); it != entries_.end(); ) {
const base::WeakPtr<VideoCaptureController>& controller = it->second;
if (controller) {
VideoCaptureControllerID controller_id(it->first);
media_stream_manager_->video_capture_manager()->StopCaptureForClient(
- controller.get(), controller_id, this);
+ controller.get(), controller_id, this, false);
+ ++it;
+ } else {
+ // Remove the entry for this controller_id so that when the controller
+ // is added, the controller will be notified to stop for this client
+ // in DoControllerAddedOnIOThread.
+ entries_.erase(it++);
}
}
}
@@ -69,13 +76,35 @@ void VideoCaptureHost::OnBufferDestroyed(
void VideoCaptureHost::OnBufferReady(
const VideoCaptureControllerID& controller_id,
int buffer_id,
- base::Time timestamp,
- const media::VideoCaptureFormat& frame_format) {
+ const media::VideoCaptureFormat& frame_format,
+ base::TimeTicks timestamp) {
BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
+ BrowserThread::IO,
+ FROM_HERE,
base::Bind(&VideoCaptureHost::DoSendFilledBufferOnIOThread,
- this, controller_id, buffer_id, timestamp,
- frame_format));
+ this,
+ controller_id,
+ buffer_id,
+ frame_format,
+ timestamp));
+}
+
+void VideoCaptureHost::OnMailboxBufferReady(
+ const VideoCaptureControllerID& controller_id,
+ int buffer_id,
+ const gpu::MailboxHolder& mailbox_holder,
+ const media::VideoCaptureFormat& frame_format,
+ base::TimeTicks timestamp) {
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&VideoCaptureHost::DoSendFilledMailboxBufferOnIOThread,
+ this,
+ controller_id,
+ buffer_id,
+ mailbox_holder,
+ frame_format,
+ timestamp));
}
void VideoCaptureHost::OnEnded(const VideoCaptureControllerID& controller_id) {
@@ -90,7 +119,7 @@ void VideoCaptureHost::DoSendNewBufferOnIOThread(
base::SharedMemoryHandle handle,
int length,
int buffer_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (entries_.find(controller_id) == entries_.end())
return;
@@ -102,7 +131,7 @@ void VideoCaptureHost::DoSendNewBufferOnIOThread(
void VideoCaptureHost::DoSendFreeBufferOnIOThread(
const VideoCaptureControllerID& controller_id,
int buffer_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (entries_.find(controller_id) == entries_.end())
return;
@@ -112,53 +141,72 @@ void VideoCaptureHost::DoSendFreeBufferOnIOThread(
void VideoCaptureHost::DoSendFilledBufferOnIOThread(
const VideoCaptureControllerID& controller_id,
- int buffer_id, base::Time timestamp,
- const media::VideoCaptureFormat& format) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ int buffer_id,
+ const media::VideoCaptureFormat& format,
+ base::TimeTicks timestamp) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+ if (entries_.find(controller_id) == entries_.end())
+ return;
+
+ Send(new VideoCaptureMsg_BufferReady(
+ controller_id.device_id, buffer_id, format, timestamp));
+}
+
+void VideoCaptureHost::DoSendFilledMailboxBufferOnIOThread(
+ const VideoCaptureControllerID& controller_id,
+ int buffer_id,
+ const gpu::MailboxHolder& mailbox_holder,
+ const media::VideoCaptureFormat& format,
+ base::TimeTicks timestamp) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (entries_.find(controller_id) == entries_.end())
return;
- Send(new VideoCaptureMsg_BufferReady(controller_id.device_id, buffer_id,
- timestamp, format));
+ Send(new VideoCaptureMsg_MailboxBufferReady(
+ controller_id.device_id, buffer_id, mailbox_holder, format, timestamp));
}
void VideoCaptureHost::DoHandleErrorOnIOThread(
const VideoCaptureControllerID& controller_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (entries_.find(controller_id) == entries_.end())
return;
Send(new VideoCaptureMsg_StateChanged(controller_id.device_id,
VIDEO_CAPTURE_STATE_ERROR));
- DeleteVideoCaptureControllerOnIOThread(controller_id);
+ DeleteVideoCaptureControllerOnIOThread(controller_id, true);
}
void VideoCaptureHost::DoEndedOnIOThread(
const VideoCaptureControllerID& controller_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DVLOG(1) << "VideoCaptureHost::DoEndedOnIOThread";
if (entries_.find(controller_id) == entries_.end())
return;
Send(new VideoCaptureMsg_StateChanged(controller_id.device_id,
VIDEO_CAPTURE_STATE_ENDED));
- DeleteVideoCaptureControllerOnIOThread(controller_id);
+ DeleteVideoCaptureControllerOnIOThread(controller_id, false);
}
///////////////////////////////////////////////////////////////////////////////
// IPC Messages handler.
-bool VideoCaptureHost::OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) {
+bool VideoCaptureHost::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(VideoCaptureHost, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(VideoCaptureHost, message)
IPC_MESSAGE_HANDLER(VideoCaptureHostMsg_Start, OnStartCapture)
IPC_MESSAGE_HANDLER(VideoCaptureHostMsg_Pause, OnPauseCapture)
IPC_MESSAGE_HANDLER(VideoCaptureHostMsg_Stop, OnStopCapture)
IPC_MESSAGE_HANDLER(VideoCaptureHostMsg_BufferReady, OnReceiveEmptyBuffer)
+ IPC_MESSAGE_HANDLER(VideoCaptureHostMsg_GetDeviceSupportedFormats,
+ OnGetDeviceSupportedFormats)
+ IPC_MESSAGE_HANDLER(VideoCaptureHostMsg_GetDeviceFormatsInUse,
+ OnGetDeviceFormatsInUse)
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return handled;
}
@@ -166,7 +214,7 @@ bool VideoCaptureHost::OnMessageReceived(const IPC::Message& message,
void VideoCaptureHost::OnStartCapture(int device_id,
media::VideoCaptureSessionId session_id,
const media::VideoCaptureParams& params) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DVLOG(1) << "VideoCaptureHost::OnStartCapture:"
<< " session_id=" << session_id
<< ", device_id=" << device_id
@@ -175,7 +223,11 @@ void VideoCaptureHost::OnStartCapture(int device_id,
<< " (" << (params.allow_resolution_change ? "variable" : "constant")
<< ")";
VideoCaptureControllerID controller_id(device_id);
- DCHECK(entries_.find(controller_id) == entries_.end());
+ if (entries_.find(controller_id) != entries_.end()) {
+ Send(new VideoCaptureMsg_StateChanged(device_id,
+ VIDEO_CAPTURE_STATE_ERROR));
+ return;
+ }
entries_[controller_id] = base::WeakPtr<VideoCaptureController>();
media_stream_manager_->video_capture_manager()->StartCaptureForClient(
@@ -202,13 +254,13 @@ void VideoCaptureHost::OnControllerAdded(
void VideoCaptureHost::DoControllerAddedOnIOThread(
int device_id,
const base::WeakPtr<VideoCaptureController>& controller) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
VideoCaptureControllerID controller_id(device_id);
EntryMap::iterator it = entries_.find(controller_id);
if (it == entries_.end()) {
if (controller) {
media_stream_manager_->video_capture_manager()->StopCaptureForClient(
- controller.get(), controller_id, this);
+ controller.get(), controller_id, this, false);
}
return;
}
@@ -225,38 +277,75 @@ void VideoCaptureHost::DoControllerAddedOnIOThread(
}
void VideoCaptureHost::OnStopCapture(int device_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DVLOG(1) << "VideoCaptureHost::OnStopCapture, device_id " << device_id;
VideoCaptureControllerID controller_id(device_id);
Send(new VideoCaptureMsg_StateChanged(device_id,
VIDEO_CAPTURE_STATE_STOPPED));
- DeleteVideoCaptureControllerOnIOThread(controller_id);
+ DeleteVideoCaptureControllerOnIOThread(controller_id, false);
}
void VideoCaptureHost::OnPauseCapture(int device_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DVLOG(1) << "VideoCaptureHost::OnPauseCapture, device_id " << device_id;
// Not used.
Send(new VideoCaptureMsg_StateChanged(device_id, VIDEO_CAPTURE_STATE_ERROR));
}
-void VideoCaptureHost::OnReceiveEmptyBuffer(int device_id, int buffer_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+void VideoCaptureHost::OnReceiveEmptyBuffer(
+ int device_id,
+ int buffer_id,
+ const std::vector<uint32>& sync_points) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
VideoCaptureControllerID controller_id(device_id);
EntryMap::iterator it = entries_.find(controller_id);
if (it != entries_.end()) {
const base::WeakPtr<VideoCaptureController>& controller = it->second;
if (controller)
- controller->ReturnBuffer(controller_id, this, buffer_id);
+ controller->ReturnBuffer(controller_id, this, buffer_id, sync_points);
}
}
+void VideoCaptureHost::OnGetDeviceSupportedFormats(
+ int device_id,
+ media::VideoCaptureSessionId capture_session_id) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ DVLOG(1) << "VideoCaptureHost::OnGetDeviceFormats, capture_session_id "
+ << capture_session_id;
+ media::VideoCaptureFormats device_supported_formats;
+ if (!media_stream_manager_->video_capture_manager()
+ ->GetDeviceSupportedFormats(capture_session_id,
+ &device_supported_formats)) {
+ DLOG(WARNING)
+ << "Could not retrieve device supported formats for device_id="
+ << device_id << " capture_session_id=" << capture_session_id;
+ }
+ Send(new VideoCaptureMsg_DeviceSupportedFormatsEnumerated(
+ device_id, device_supported_formats));
+}
+
+void VideoCaptureHost::OnGetDeviceFormatsInUse(
+ int device_id,
+ media::VideoCaptureSessionId capture_session_id) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ DVLOG(1) << "VideoCaptureHost::OnGetDeviceFormatsInUse, capture_session_id "
+ << capture_session_id;
+ media::VideoCaptureFormats formats_in_use;
+ if (!media_stream_manager_->video_capture_manager()->GetDeviceFormatsInUse(
+ capture_session_id, &formats_in_use)) {
+ DVLOG(1) << "Could not retrieve device format(s) in use for device_id="
+ << device_id << " capture_session_id=" << capture_session_id;
+ }
+ Send(new VideoCaptureMsg_DeviceFormatsInUseReceived(device_id,
+ formats_in_use));
+}
+
void VideoCaptureHost::DeleteVideoCaptureControllerOnIOThread(
- const VideoCaptureControllerID& controller_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ const VideoCaptureControllerID& controller_id, bool on_error) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
EntryMap::iterator it = entries_.find(controller_id);
if (it == entries_.end())
@@ -264,7 +353,7 @@ void VideoCaptureHost::DeleteVideoCaptureControllerOnIOThread(
if (it->second) {
media_stream_manager_->video_capture_manager()->StopCaptureForClient(
- it->second.get(), controller_id, this);
+ it->second.get(), controller_id, this, on_error);
}
entries_.erase(it);
}
diff --git a/chromium/content/browser/renderer_host/media/video_capture_host.h b/chromium/content/browser/renderer_host/media/video_capture_host.h
index b71aac3715c..caecb1ae228 100644
--- a/chromium/content/browser/renderer_host/media/video_capture_host.h
+++ b/chromium/content/browser/renderer_host/media/video_capture_host.h
@@ -73,8 +73,7 @@ class CONTENT_EXPORT VideoCaptureHost
// BrowserMessageFilter implementation.
virtual void OnChannelClosing() OVERRIDE;
virtual void OnDestruct() const OVERRIDE;
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
// VideoCaptureControllerEventHandler implementation.
virtual void OnError(const VideoCaptureControllerID& id) OVERRIDE;
@@ -84,11 +83,15 @@ class CONTENT_EXPORT VideoCaptureHost
int buffer_id) OVERRIDE;
virtual void OnBufferDestroyed(const VideoCaptureControllerID& id,
int buffer_id) OVERRIDE;
- virtual void OnBufferReady(
- const VideoCaptureControllerID& id,
- int buffer_id,
- base::Time timestamp,
- const media::VideoCaptureFormat& format) OVERRIDE;
+ virtual void OnBufferReady(const VideoCaptureControllerID& id,
+ int buffer_id,
+ const media::VideoCaptureFormat& format,
+ base::TimeTicks timestamp) OVERRIDE;
+ virtual void OnMailboxBufferReady(const VideoCaptureControllerID& id,
+ int buffer_id,
+ const gpu::MailboxHolder& mailbox_holder,
+ const media::VideoCaptureFormat& format,
+ base::TimeTicks timestamp) OVERRIDE;
virtual void OnEnded(const VideoCaptureControllerID& id) OVERRIDE;
private:
@@ -121,9 +124,24 @@ class CONTENT_EXPORT VideoCaptureHost
// IPC message: Receive an empty buffer from renderer. Send it to device
// referenced by |device_id|.
- void OnReceiveEmptyBuffer(int device_id, int buffer_id);
+ void OnReceiveEmptyBuffer(int device_id,
+ int buffer_id,
+ const std::vector<uint32>& sync_points);
+
+ // IPC message: Get supported formats referenced by |capture_session_id|.
+ // |device_id| is needed for message back-routing purposes.
+ void OnGetDeviceSupportedFormats(
+ int device_id,
+ media::VideoCaptureSessionId capture_session_id);
+
+ // IPC message: Get a device's currently in use format(s), referenced by
+ // |capture_session_id|. |device_id| is needed for message back-routing
+ // purposes.
+ void OnGetDeviceFormatsInUse(
+ int device_id,
+ media::VideoCaptureSessionId capture_session_id);
- // Send a newly created buffer to the VideoCaptureMessageFilter.
+ // Sends a newly created buffer to the VideoCaptureMessageFilter.
void DoSendNewBufferOnIOThread(
const VideoCaptureControllerID& controller_id,
base::SharedMemoryHandle handle,
@@ -134,20 +152,30 @@ class CONTENT_EXPORT VideoCaptureHost
const VideoCaptureControllerID& controller_id,
int buffer_id);
- // Send a filled buffer to the VideoCaptureMessageFilter.
+ // Sends a filled buffer to the VideoCaptureMessageFilter.
void DoSendFilledBufferOnIOThread(
const VideoCaptureControllerID& controller_id,
int buffer_id,
- base::Time timestamp,
- const media::VideoCaptureFormat& format);
+ const media::VideoCaptureFormat& format,
+ base::TimeTicks timestamp);
+
+ // Sends a filled texture mailbox buffer to the VideoCaptureMessageFilter.
+ void DoSendFilledMailboxBufferOnIOThread(
+ const VideoCaptureControllerID& controller_id,
+ int buffer_id,
+ const gpu::MailboxHolder& mailbox_holder,
+ const media::VideoCaptureFormat& format,
+ base::TimeTicks timestamp);
- // Handle error coming from VideoCaptureDevice.
+ // Handles error coming from VideoCaptureDevice.
void DoHandleErrorOnIOThread(const VideoCaptureControllerID& controller_id);
void DoEndedOnIOThread(const VideoCaptureControllerID& controller_id);
+ // Deletes the controller and notifies the VideoCaptureManager. |on_error| is
+ // true if this is triggered by VideoCaptureControllerEventHandler::OnError.
void DeleteVideoCaptureControllerOnIOThread(
- const VideoCaptureControllerID& controller_id);
+ const VideoCaptureControllerID& controller_id, bool on_error);
MediaStreamManager* media_stream_manager_;
diff --git a/chromium/content/browser/renderer_host/media/video_capture_host_unittest.cc b/chromium/content/browser/renderer_host/media/video_capture_host_unittest.cc
index c7ffcd5c69f..6088081bf7b 100644
--- a/chromium/content/browser/renderer_host/media/video_capture_host_unittest.cc
+++ b/chromium/content/browser/renderer_host/media/video_capture_host_unittest.cc
@@ -6,7 +6,9 @@
#include <string>
#include "base/bind.h"
+#include "base/command_line.h"
#include "base/file_util.h"
+#include "base/files/scoped_file.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
@@ -19,11 +21,13 @@
#include "content/browser/renderer_host/media/video_capture_host.h"
#include "content/browser/renderer_host/media/video_capture_manager.h"
#include "content/common/media/video_capture_messages.h"
+#include "content/public/common/content_switches.h"
#include "content/public/test/mock_resource_context.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "content/test/test_content_browser_client.h"
#include "media/audio/audio_manager.h"
+#include "media/base/media_switches.h"
#include "media/base/video_frame.h"
#include "media/video/capture/video_capture_types.h"
#include "net/url_request/url_request_context.h"
@@ -71,7 +75,7 @@ class DumpVideo {
}
private:
- file_util::ScopedFILE file_;
+ base::ScopedFILE file_;
int expected_size_;
};
@@ -87,8 +91,10 @@ class MockMediaStreamRequester : public MediaStreamRequester {
const std::string& label,
const StreamDeviceInfoArray& audio_devices,
const StreamDeviceInfoArray& video_devices));
- MOCK_METHOD2(StreamGenerationFailed, void(int render_view_id,
- int page_request_id));
+ MOCK_METHOD3(StreamGenerationFailed,
+ void(int render_view_id,
+ int page_request_id,
+ content::MediaStreamRequestResult result));
MOCK_METHOD3(DeviceStopped, void(int render_view_id,
const std::string& label,
const StreamDeviceInfo& device));
@@ -114,13 +120,23 @@ class MockVideoCaptureHost : public VideoCaptureHost {
// A list of mock methods.
MOCK_METHOD4(OnNewBufferCreated,
- void(int device_id, base::SharedMemoryHandle handle,
- int length, int buffer_id));
+ void(int device_id,
+ base::SharedMemoryHandle handle,
+ int length,
+ int buffer_id));
MOCK_METHOD2(OnBufferFreed,
void(int device_id, int buffer_id));
MOCK_METHOD4(OnBufferFilled,
- void(int device_id, int buffer_id, base::Time timestamp,
- const media::VideoCaptureFormat& format));
+ void(int device_id,
+ int buffer_id,
+ const media::VideoCaptureFormat& format,
+ base::TimeTicks timestamp));
+ MOCK_METHOD5(OnMailboxBufferFilled,
+ void(int device_id,
+ int buffer_id,
+ const gpu::MailboxHolder& mailbox_holder,
+ const media::VideoCaptureFormat& format,
+ base::TimeTicks timestamp));
MOCK_METHOD2(OnStateChanged, void(int device_id, VideoCaptureState state));
// Use class DumpVideo to write I420 video to file.
@@ -136,7 +152,7 @@ class MockVideoCaptureHost : public VideoCaptureHost {
void ReturnReceivedDibs(int device_id) {
int handle = GetReceivedDib();
while (handle) {
- this->OnReceiveEmptyBuffer(device_id, handle);
+ this->OnReceiveEmptyBuffer(device_id, handle, std::vector<uint32>());
handle = GetReceivedDib();
}
}
@@ -171,6 +187,8 @@ class MockVideoCaptureHost : public VideoCaptureHost {
IPC_MESSAGE_HANDLER(VideoCaptureMsg_NewBuffer, OnNewBufferCreatedDispatch)
IPC_MESSAGE_HANDLER(VideoCaptureMsg_FreeBuffer, OnBufferFreedDispatch)
IPC_MESSAGE_HANDLER(VideoCaptureMsg_BufferReady, OnBufferFilledDispatch)
+ IPC_MESSAGE_HANDLER(VideoCaptureMsg_MailboxBufferReady,
+ OnMailboxBufferFilledDispatch)
IPC_MESSAGE_HANDLER(VideoCaptureMsg_StateChanged, OnStateChangedDispatch)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
@@ -203,8 +221,8 @@ class MockVideoCaptureHost : public VideoCaptureHost {
void OnBufferFilledDispatch(int device_id,
int buffer_id,
- base::Time timestamp,
- const media::VideoCaptureFormat& frame_format) {
+ const media::VideoCaptureFormat& frame_format,
+ base::TimeTicks timestamp) {
base::SharedMemory* dib = filled_dib_[buffer_id];
ASSERT_TRUE(dib != NULL);
if (dump_video_) {
@@ -220,9 +238,23 @@ class MockVideoCaptureHost : public VideoCaptureHost {
dumper_.NewVideoFrame(dib->memory());
}
- OnBufferFilled(device_id, buffer_id, timestamp, frame_format);
+ OnBufferFilled(device_id, buffer_id, frame_format, timestamp);
if (return_buffers_) {
- VideoCaptureHost::OnReceiveEmptyBuffer(device_id, buffer_id);
+ VideoCaptureHost::OnReceiveEmptyBuffer(
+ device_id, buffer_id, std::vector<uint32>());
+ }
+ }
+
+ void OnMailboxBufferFilledDispatch(int device_id,
+ int buffer_id,
+ const gpu::MailboxHolder& mailbox_holder,
+ const media::VideoCaptureFormat& format,
+ base::TimeTicks timestamp) {
+ OnMailboxBufferFilled(
+ device_id, buffer_id, mailbox_holder, format, timestamp);
+ if (return_buffers_) {
+ VideoCaptureHost::OnReceiveEmptyBuffer(
+ device_id, buffer_id, std::vector<uint32>());
}
}
@@ -255,10 +287,11 @@ class VideoCaptureHostTest : public testing::Test {
SetBrowserClientForTesting(&browser_client_);
// Create our own MediaStreamManager.
audio_manager_.reset(media::AudioManager::CreateForTesting());
- media_stream_manager_.reset(new MediaStreamManager(audio_manager_.get()));
#ifndef TEST_REAL_CAPTURE_DEVICE
- media_stream_manager_->UseFakeDevice();
+ base::CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kUseFakeDeviceForMediaStream);
#endif
+ media_stream_manager_.reset(new MediaStreamManager(audio_manager_.get()));
media_stream_manager_->UseFakeUI(scoped_ptr<FakeMediaStreamUIProxy>());
// Create a Host and connect it to a simulated IPC channel.
@@ -276,7 +309,7 @@ class VideoCaptureHostTest : public testing::Test {
CloseSession();
- // Simulate closing the IPC channel.
+ // Simulate closing the IPC sender.
host_->OnChannelClosing();
// Release the reference to the mock object. The object will be destructed
@@ -303,7 +336,8 @@ class VideoCaptureHostTest : public testing::Test {
browser_context_.GetResourceContext()->GetMediaDeviceIDSalt(),
page_request_id,
MEDIA_DEVICE_VIDEO_CAPTURE,
- security_origin);
+ security_origin,
+ true);
EXPECT_CALL(stream_requester_, DevicesEnumerated(render_view_id,
page_request_id,
label,
diff --git a/chromium/content/browser/renderer_host/media/video_capture_manager.cc b/chromium/content/browser/renderer_host/media/video_capture_manager.cc
index a581580bf38..3e9e0bfa864 100644
--- a/chromium/content/browser/renderer_host/media/video_capture_manager.cc
+++ b/chromium/content/browser/renderer_host/media/video_capture_manager.cc
@@ -7,32 +7,71 @@
#include <set>
#include "base/bind.h"
-#include "base/command_line.h"
+#include "base/bind_helpers.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/stl_util.h"
#include "base/task_runner_util.h"
#include "base/threading/sequenced_worker_pool.h"
+#include "content/browser/media/capture/web_contents_video_capture_device.h"
#include "content/browser/renderer_host/media/video_capture_controller.h"
#include "content/browser/renderer_host/media/video_capture_controller_event_handler.h"
-#include "content/browser/renderer_host/media/web_contents_video_capture_device.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/desktop_media_id.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/media_stream_request.h"
-#include "media/base/media_switches.h"
+#include "media/base/bind_to_current_loop.h"
#include "media/base/scoped_histogram_timer.h"
-#include "media/video/capture/fake_video_capture_device.h"
-#include "media/video/capture/file_video_capture_device.h"
#include "media/video/capture/video_capture_device.h"
+#include "media/video/capture/video_capture_device_factory.h"
#if defined(ENABLE_SCREEN_CAPTURE)
-#include "content/browser/renderer_host/media/desktop_capture_device.h"
+#include "content/browser/media/capture/desktop_capture_device.h"
#if defined(USE_AURA)
-#include "content/browser/renderer_host/media/desktop_capture_device_aura.h"
+#include "content/browser/media/capture/desktop_capture_device_aura.h"
#endif
#endif
+namespace {
+
+// Compares two VideoCaptureFormat by checking smallest frame_size area, then
+// by _largest_ frame_rate. Used to order a VideoCaptureFormats vector so that
+// the first entry for a given resolution has the largest frame rate, as needed
+// by the ConsolidateCaptureFormats() method.
+bool IsCaptureFormatSmaller(const media::VideoCaptureFormat& format1,
+ const media::VideoCaptureFormat& format2) {
+ if (format1.frame_size.GetArea() == format2.frame_size.GetArea())
+ return format1.frame_rate > format2.frame_rate;
+ return format1.frame_size.GetArea() < format2.frame_size.GetArea();
+}
+
+bool IsCaptureFormatSizeEqual(const media::VideoCaptureFormat& format1,
+ const media::VideoCaptureFormat& format2) {
+ return format1.frame_size.GetArea() == format2.frame_size.GetArea();
+}
+
+// This function receives a list of capture formats, removes duplicated
+// resolutions while keeping the highest frame rate for each, and forcing I420
+// pixel format.
+void ConsolidateCaptureFormats(media::VideoCaptureFormats* formats) {
+ if (formats->empty())
+ return;
+ std::sort(formats->begin(), formats->end(), IsCaptureFormatSmaller);
+ // Due to the ordering imposed, the largest frame_rate is kept while removing
+ // duplicated resolutions.
+ media::VideoCaptureFormats::iterator last =
+ std::unique(formats->begin(), formats->end(), IsCaptureFormatSizeEqual);
+ formats->erase(last, formats->end());
+ // Mark all formats as I420, since this is what the renderer side will get
+ // anyhow: the actual pixel format is decided at the device level.
+ for (media::VideoCaptureFormats::iterator it = formats->begin();
+ it != formats->end(); ++it) {
+ it->pixel_format = media::PIXEL_FORMAT_I420;
+ }
+}
+
+} // namespace
+
namespace content {
VideoCaptureManager::DeviceEntry::DeviceEntry(
@@ -55,23 +94,25 @@ VideoCaptureManager::DeviceInfo::DeviceInfo(
VideoCaptureManager::DeviceInfo::~DeviceInfo() {}
-VideoCaptureManager::VideoCaptureManager()
+VideoCaptureManager::VideoCaptureManager(
+ scoped_ptr<media::VideoCaptureDeviceFactory> factory)
: listener_(NULL),
new_capture_session_id_(1),
- artificial_device_source_for_testing_(DISABLED) {
+ video_capture_device_factory_(factory.Pass()) {
}
VideoCaptureManager::~VideoCaptureManager() {
DCHECK(devices_.empty());
}
-void VideoCaptureManager::Register(MediaStreamProviderListener* listener,
- base::MessageLoopProxy* device_thread_loop) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+void VideoCaptureManager::Register(
+ MediaStreamProviderListener* listener,
+ const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(!listener_);
- DCHECK(!device_loop_.get());
+ DCHECK(!device_task_runner_.get());
listener_ = listener;
- device_loop_ = device_thread_loop;
+ device_task_runner_ = device_task_runner;
}
void VideoCaptureManager::Unregister() {
@@ -80,23 +121,40 @@ void VideoCaptureManager::Unregister() {
}
void VideoCaptureManager::EnumerateDevices(MediaStreamType stream_type) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DVLOG(1) << "VideoCaptureManager::EnumerateDevices, type " << stream_type;
DCHECK(listener_);
- base::PostTaskAndReplyWithResult(
- device_loop_, FROM_HERE,
- base::Bind(&VideoCaptureManager::GetAvailableDevicesInfoOnDeviceThread,
- this, stream_type, devices_info_cache_),
- base::Bind(&VideoCaptureManager::OnDevicesInfoEnumerated, this,
- stream_type));
+ DCHECK_EQ(stream_type, MEDIA_DEVICE_VIDEO_CAPTURE);
+
+ // Bind a callback to ConsolidateDevicesInfoOnDeviceThread() with an argument
+ // for another callback to OnDevicesInfoEnumerated() to be run in the current
+ // loop, i.e. IO loop. Pass a timer for UMA histogram collection.
+ base::Callback<void(scoped_ptr<media::VideoCaptureDevice::Names>)>
+ devices_enumerated_callback =
+ base::Bind(&VideoCaptureManager::ConsolidateDevicesInfoOnDeviceThread,
+ this,
+ media::BindToCurrentLoop(base::Bind(
+ &VideoCaptureManager::OnDevicesInfoEnumerated,
+ this,
+ stream_type,
+ base::Owned(new base::ElapsedTimer()))),
+ stream_type,
+ devices_info_cache_);
+ // OK to use base::Unretained() since we own the VCDFactory and |this| is
+ // bound in |devices_enumerated_callback|.
+ device_task_runner_->PostTask(FROM_HERE,
+ base::Bind(&media::VideoCaptureDeviceFactory::EnumerateDeviceNames,
+ base::Unretained(video_capture_device_factory_.get()),
+ devices_enumerated_callback));
}
int VideoCaptureManager::Open(const StreamDeviceInfo& device_info) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(listener_);
// Generate a new id for the session being opened.
- const int capture_session_id = new_capture_session_id_++;
+ const media::VideoCaptureSessionId capture_session_id =
+ new_capture_session_id_++;
DCHECK(sessions_.find(capture_session_id) == sessions_.end());
DVLOG(1) << "VideoCaptureManager::Open, id " << capture_session_id;
@@ -114,12 +172,11 @@ int VideoCaptureManager::Open(const StreamDeviceInfo& device_info) {
}
void VideoCaptureManager::Close(int capture_session_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(listener_);
DVLOG(1) << "VideoCaptureManager::Close, id " << capture_session_id;
- std::map<int, MediaStreamDevice>::iterator session_it =
- sessions_.find(capture_session_id);
+ SessionMap::iterator session_it = sessions_.find(capture_session_id);
if (session_it == sessions_.end()) {
NOTREACHED();
return;
@@ -144,16 +201,8 @@ void VideoCaptureManager::Close(int capture_session_id) {
sessions_.erase(session_it);
}
-void VideoCaptureManager::UseFakeDevice() {
- if (CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kUseFileForFakeVideoCapture)) {
- artificial_device_source_for_testing_ = Y4M_FILE;
- } else {
- artificial_device_source_for_testing_ = TEST_PATTERN;
- }
-}
-
void VideoCaptureManager::DoStartDeviceOnDeviceThread(
+ media::VideoCaptureSessionId session_id,
DeviceEntry* entry,
const media::VideoCaptureParams& params,
scoped_ptr<media::VideoCaptureDevice::Client> device_client) {
@@ -168,20 +217,8 @@ void VideoCaptureManager::DoStartDeviceOnDeviceThread(
// held in the browser-side VideoCaptureDevice::Name structure.
DeviceInfo* found = FindDeviceInfoById(entry->id, devices_info_cache_);
if (found) {
- switch (artificial_device_source_for_testing_) {
- case DISABLED:
- video_capture_device.reset(
- media::VideoCaptureDevice::Create(found->name));
- break;
- case TEST_PATTERN:
- video_capture_device.reset(
- media::FakeVideoCaptureDevice::Create(found->name));
- break;
- case Y4M_FILE:
- video_capture_device.reset(
- media::FileVideoCaptureDevice::Create(found->name));
- break;
- }
+ video_capture_device =
+ video_capture_device_factory_->Create(found->name);
}
break;
}
@@ -201,6 +238,11 @@ void VideoCaptureManager::DoStartDeviceOnDeviceThread(
if (id.type != DesktopMediaID::TYPE_NONE &&
id.type != DesktopMediaID::TYPE_AURA_WINDOW) {
video_capture_device = DesktopCaptureDevice::Create(id);
+ if (notification_window_ids_.find(session_id) !=
+ notification_window_ids_.end()) {
+ static_cast<DesktopCaptureDevice*>(video_capture_device.get())
+ ->SetNotificationWindowId(notification_window_ids_[session_id]);
+ }
}
#endif // defined(ENABLE_SCREEN_CAPTURE)
break;
@@ -212,7 +254,7 @@ void VideoCaptureManager::DoStartDeviceOnDeviceThread(
}
if (!video_capture_device) {
- device_client->OnError();
+ device_client->OnError("Could not create capture device");
return;
}
@@ -227,7 +269,7 @@ void VideoCaptureManager::StartCaptureForClient(
VideoCaptureControllerID client_id,
VideoCaptureControllerEventHandler* client_handler,
const DoneCB& done_cb) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DVLOG(1) << "VideoCaptureManager::StartCaptureForClient, "
<< params.requested_format.frame_size.ToString() << ", "
<< params.requested_format.frame_rate << ", #" << session_id << ")";
@@ -245,11 +287,12 @@ void VideoCaptureManager::StartCaptureForClient(
DVLOG(1) << "VideoCaptureManager starting device (type = "
<< entry->stream_type << ", id = " << entry->id << ")";
- device_loop_->PostTask(
+ device_task_runner_->PostTask(
FROM_HERE,
base::Bind(
&VideoCaptureManager::DoStartDeviceOnDeviceThread,
this,
+ session_id,
entry,
params,
base::Passed(entry->video_capture_controller->NewDeviceClient())));
@@ -263,8 +306,9 @@ void VideoCaptureManager::StartCaptureForClient(
void VideoCaptureManager::StopCaptureForClient(
VideoCaptureController* controller,
VideoCaptureControllerID client_id,
- VideoCaptureControllerEventHandler* client_handler) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ VideoCaptureControllerEventHandler* client_handler,
+ bool aborted_due_to_error) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(controller);
DCHECK(client_handler);
@@ -273,9 +317,20 @@ void VideoCaptureManager::StopCaptureForClient(
NOTREACHED();
return;
}
+ if (aborted_due_to_error) {
+ SessionMap::iterator it;
+ for (it = sessions_.begin(); it != sessions_.end(); ++it) {
+ if (it->second.type == entry->stream_type &&
+ it->second.id == entry->id) {
+ listener_->Aborted(it->second.type, it->first);
+ break;
+ }
+ }
+ }
// Detach client from controller.
- int session_id = controller->RemoveClient(client_id, client_handler);
+ media::VideoCaptureSessionId session_id =
+ controller->RemoveClient(client_id, client_handler);
DVLOG(1) << "VideoCaptureManager::StopCaptureForClient, session_id = "
<< session_id;
@@ -283,32 +338,81 @@ void VideoCaptureManager::StopCaptureForClient(
DestroyDeviceEntryIfNoClients(entry);
}
-void VideoCaptureManager::GetDeviceSupportedFormats(
- int capture_session_id,
+bool VideoCaptureManager::GetDeviceSupportedFormats(
+ media::VideoCaptureSessionId capture_session_id,
media::VideoCaptureFormats* supported_formats) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- supported_formats->clear();
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ DCHECK(supported_formats->empty());
- std::map<int, MediaStreamDevice>::iterator it =
- sessions_.find(capture_session_id);
- DCHECK(it != sessions_.end());
+ SessionMap::iterator it = sessions_.find(capture_session_id);
+ if (it == sessions_.end())
+ return false;
DVLOG(1) << "GetDeviceSupportedFormats for device: " << it->second.name;
- DeviceInfo* device_in_use =
+ // Return all available formats of the device, regardless its started state.
+ DeviceInfo* existing_device =
FindDeviceInfoById(it->second.id, devices_info_cache_);
+ if (existing_device)
+ *supported_formats = existing_device->supported_formats;
+ return true;
+}
+
+bool VideoCaptureManager::GetDeviceFormatsInUse(
+ media::VideoCaptureSessionId capture_session_id,
+ media::VideoCaptureFormats* formats_in_use) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ DCHECK(formats_in_use->empty());
+
+ SessionMap::iterator it = sessions_.find(capture_session_id);
+ if (it == sessions_.end())
+ return false;
+ DVLOG(1) << "GetDeviceFormatsInUse for device: " << it->second.name;
+
+ // Return the currently in-use format(s) of the device, if it's started.
+ DeviceEntry* device_in_use =
+ GetDeviceEntryForMediaStreamDevice(it->second);
if (device_in_use) {
- DeviceEntry* const existing_device =
- GetDeviceEntryForMediaStreamDevice(it->second);
- if (!existing_device) {
- // If the device is not in use, return all its cached supported formats.
- *supported_formats = device_in_use->supported_formats;
- return;
- }
- // Otherwise, get the video capture parameters in use from the controller
- // associated to the device.
- supported_formats->push_back(
- existing_device->video_capture_controller->GetVideoCaptureFormat());
+ // Currently only one format-in-use is supported at the VCC level.
+ formats_in_use->push_back(
+ device_in_use->video_capture_controller->GetVideoCaptureFormat());
+ }
+ return true;
+}
+
+void VideoCaptureManager::SetDesktopCaptureWindowId(
+ media::VideoCaptureSessionId session_id,
+ gfx::NativeViewId window_id) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ SessionMap::iterator session_it = sessions_.find(session_id);
+ if (session_it == sessions_.end()) {
+ device_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &VideoCaptureManager::SaveDesktopCaptureWindowIdOnDeviceThread,
+ this,
+ session_id,
+ window_id));
+ return;
}
+
+ DeviceEntry* const existing_device =
+ GetDeviceEntryForMediaStreamDevice(session_it->second);
+ if (!existing_device)
+ return;
+
+ DCHECK_EQ(MEDIA_DESKTOP_VIDEO_CAPTURE, existing_device->stream_type);
+ DesktopMediaID id = DesktopMediaID::Parse(existing_device->id);
+ if (id.type == DesktopMediaID::TYPE_NONE ||
+ id.type == DesktopMediaID::TYPE_AURA_WINDOW) {
+ return;
+ }
+
+ device_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&VideoCaptureManager::SetDesktopCaptureWindowIdOnDeviceThread,
+ this,
+ existing_device,
+ window_id));
}
void VideoCaptureManager::DoStopDeviceOnDeviceThread(DeviceEntry* entry) {
@@ -320,9 +424,10 @@ void VideoCaptureManager::DoStopDeviceOnDeviceThread(DeviceEntry* entry) {
entry->video_capture_device.reset();
}
-void VideoCaptureManager::OnOpened(MediaStreamType stream_type,
- int capture_session_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+void VideoCaptureManager::OnOpened(
+ MediaStreamType stream_type,
+ media::VideoCaptureSessionId capture_session_id) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!listener_) {
// Listener has been removed.
return;
@@ -330,9 +435,10 @@ void VideoCaptureManager::OnOpened(MediaStreamType stream_type,
listener_->Opened(stream_type, capture_session_id);
}
-void VideoCaptureManager::OnClosed(MediaStreamType stream_type,
- int capture_session_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+void VideoCaptureManager::OnClosed(
+ MediaStreamType stream_type,
+ media::VideoCaptureSessionId capture_session_id) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!listener_) {
// Listener has been removed.
return;
@@ -342,9 +448,12 @@ void VideoCaptureManager::OnClosed(MediaStreamType stream_type,
void VideoCaptureManager::OnDevicesInfoEnumerated(
MediaStreamType stream_type,
+ base::ElapsedTimer* timer,
const DeviceInfos& new_devices_info_cache) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ UMA_HISTOGRAM_TIMES(
+ "Media.VideoCaptureManager.GetAvailableDevicesInfoOnDeviceThreadTime",
+ timer->Elapsed());
if (!listener_) {
// Listener has been removed.
return;
@@ -363,44 +472,15 @@ void VideoCaptureManager::OnDevicesInfoEnumerated(
}
bool VideoCaptureManager::IsOnDeviceThread() const {
- return device_loop_->BelongsToCurrentThread();
+ return device_task_runner_->BelongsToCurrentThread();
}
-VideoCaptureManager::DeviceInfos
-VideoCaptureManager::GetAvailableDevicesInfoOnDeviceThread(
+void VideoCaptureManager::ConsolidateDevicesInfoOnDeviceThread(
+ base::Callback<void(const DeviceInfos&)> on_devices_enumerated_callback,
MediaStreamType stream_type,
- const DeviceInfos& old_device_info_cache) {
- SCOPED_UMA_HISTOGRAM_TIMER(
- "Media.VideoCaptureManager.GetAvailableDevicesInfoOnDeviceThreadTime");
+ const DeviceInfos& old_device_info_cache,
+ scoped_ptr<media::VideoCaptureDevice::Names> names_snapshot) {
DCHECK(IsOnDeviceThread());
- media::VideoCaptureDevice::Names names_snapshot;
- switch (stream_type) {
- case MEDIA_DEVICE_VIDEO_CAPTURE:
- // Cache the latest enumeration of video capture devices.
- // We'll refer to this list again in OnOpen to avoid having to
- // enumerate the devices again.
- switch (artificial_device_source_for_testing_) {
- case DISABLED:
- media::VideoCaptureDevice::GetDeviceNames(&names_snapshot);
- break;
- case TEST_PATTERN:
- media::FakeVideoCaptureDevice::GetDeviceNames(&names_snapshot);
- break;
- case Y4M_FILE:
- media::FileVideoCaptureDevice::GetDeviceNames(&names_snapshot);
- break;
- }
- break;
-
- case MEDIA_DESKTOP_VIDEO_CAPTURE:
- // Do nothing.
- break;
-
- default:
- NOTREACHED();
- break;
- }
-
// Construct |new_devices_info_cache| with the cached devices that are still
// present in the system, and remove their names from |names_snapshot|, so we
// keep there the truly new devices.
@@ -409,11 +489,11 @@ VideoCaptureManager::GetAvailableDevicesInfoOnDeviceThread(
old_device_info_cache.begin();
it_device_info != old_device_info_cache.end(); ++it_device_info) {
for (media::VideoCaptureDevice::Names::iterator it =
- names_snapshot.begin();
- it != names_snapshot.end(); ++it) {
+ names_snapshot->begin();
+ it != names_snapshot->end(); ++it) {
if (it_device_info->name.id() == it->id()) {
new_devices_info_cache.push_back(*it_device_info);
- names_snapshot.erase(it);
+ names_snapshot->erase(it);
break;
}
}
@@ -421,33 +501,23 @@ VideoCaptureManager::GetAvailableDevicesInfoOnDeviceThread(
// Get the supported capture formats for the new devices in |names_snapshot|.
for (media::VideoCaptureDevice::Names::const_iterator it =
- names_snapshot.begin();
- it != names_snapshot.end(); ++it) {
+ names_snapshot->begin();
+ it != names_snapshot->end(); ++it) {
media::VideoCaptureFormats supported_formats;
DeviceInfo device_info(*it, media::VideoCaptureFormats());
- switch (artificial_device_source_for_testing_) {
- case DISABLED:
- media::VideoCaptureDevice::GetDeviceSupportedFormats(
- *it, &(device_info.supported_formats));
- break;
- case TEST_PATTERN:
- media::FakeVideoCaptureDevice::GetDeviceSupportedFormats(
- *it, &(device_info.supported_formats));
- break;
- case Y4M_FILE:
- media::FileVideoCaptureDevice::GetDeviceSupportedFormats(
- *it, &(device_info.supported_formats));
- break;
- }
+ video_capture_device_factory_->GetDeviceSupportedFormats(
+ *it, &(device_info.supported_formats));
+ ConsolidateCaptureFormats(&device_info.supported_formats);
new_devices_info_cache.push_back(device_info);
}
- return new_devices_info_cache;
+
+ on_devices_enumerated_callback.Run(new_devices_info_cache);
}
VideoCaptureManager::DeviceEntry*
VideoCaptureManager::GetDeviceEntryForMediaStreamDevice(
const MediaStreamDevice& device_info) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
for (DeviceEntries::iterator it = devices_.begin();
it != devices_.end(); ++it) {
@@ -462,9 +532,9 @@ VideoCaptureManager::GetDeviceEntryForMediaStreamDevice(
VideoCaptureManager::DeviceEntry*
VideoCaptureManager::GetDeviceEntryForController(
- const VideoCaptureController* controller) {
+ const VideoCaptureController* controller) const {
// Look up |controller| in |devices_|.
- for (DeviceEntries::iterator it = devices_.begin();
+ for (DeviceEntries::const_iterator it = devices_.begin();
it != devices_.end(); ++it) {
if ((*it)->video_capture_controller.get() == controller) {
return *it;
@@ -474,7 +544,7 @@ VideoCaptureManager::GetDeviceEntryForController(
}
void VideoCaptureManager::DestroyDeviceEntryIfNoClients(DeviceEntry* entry) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
// Removal of the last client stops the device.
if (entry->video_capture_controller->GetClientCount() == 0) {
DVLOG(1) << "VideoCaptureManager stopping device (type = "
@@ -486,7 +556,7 @@ void VideoCaptureManager::DestroyDeviceEntryIfNoClients(DeviceEntry* entry) {
// DeviceEntry, VideoCaptureController, and VideoCaptureDevice.
devices_.erase(entry);
entry->video_capture_controller.reset();
- device_loop_->PostTask(
+ device_task_runner_->PostTask(
FROM_HERE,
base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this,
base::Owned(entry)));
@@ -494,11 +564,10 @@ void VideoCaptureManager::DestroyDeviceEntryIfNoClients(DeviceEntry* entry) {
}
VideoCaptureManager::DeviceEntry* VideoCaptureManager::GetOrCreateDeviceEntry(
- int capture_session_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ media::VideoCaptureSessionId capture_session_id) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
- std::map<int, MediaStreamDevice>::iterator session_it =
- sessions_.find(capture_session_id);
+ SessionMap::iterator session_it = sessions_.find(capture_session_id);
if (session_it == sessions_.end()) {
return NULL;
}
@@ -533,4 +602,25 @@ VideoCaptureManager::DeviceInfo* VideoCaptureManager::FindDeviceInfoById(
return NULL;
}
+void VideoCaptureManager::SetDesktopCaptureWindowIdOnDeviceThread(
+ DeviceEntry* entry,
+ gfx::NativeViewId window_id) {
+ DCHECK(IsOnDeviceThread());
+ DCHECK(entry->stream_type == MEDIA_DESKTOP_VIDEO_CAPTURE);
+#if defined(ENABLE_SCREEN_CAPTURE)
+ DesktopCaptureDevice* device =
+ static_cast<DesktopCaptureDevice*>(entry->video_capture_device.get());
+ device->SetNotificationWindowId(window_id);
+#endif
+}
+
+void VideoCaptureManager::SaveDesktopCaptureWindowIdOnDeviceThread(
+ media::VideoCaptureSessionId session_id,
+ gfx::NativeViewId window_id) {
+ DCHECK(IsOnDeviceThread());
+ DCHECK(notification_window_ids_.find(session_id) ==
+ notification_window_ids_.end());
+ notification_window_ids_[session_id] = window_id;
+}
+
} // namespace content
diff --git a/chromium/content/browser/renderer_host/media/video_capture_manager.h b/chromium/content/browser/renderer_host/media/video_capture_manager.h
index 5f3630eb679..7f69d56a904 100644
--- a/chromium/content/browser/renderer_host/media/video_capture_manager.h
+++ b/chromium/content/browser/renderer_host/media/video_capture_manager.h
@@ -18,12 +18,15 @@
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
#include "base/process/process_handle.h"
+#include "base/timer/elapsed_timer.h"
#include "content/browser/renderer_host/media/media_stream_provider.h"
#include "content/browser/renderer_host/media/video_capture_controller_event_handler.h"
#include "content/common/content_export.h"
#include "content/common/media/media_stream_options.h"
#include "media/video/capture/video_capture_device.h"
+#include "media/video/capture/video_capture_device_factory.h"
#include "media/video/capture/video_capture_types.h"
namespace content {
@@ -37,11 +40,13 @@ class CONTENT_EXPORT VideoCaptureManager : public MediaStreamProvider {
typedef base::Callback<
void(const base::WeakPtr<VideoCaptureController>&)> DoneCB;
- VideoCaptureManager();
+ explicit VideoCaptureManager(
+ scoped_ptr<media::VideoCaptureDeviceFactory> factory);
// Implements MediaStreamProvider.
virtual void Register(MediaStreamProviderListener* listener,
- base::MessageLoopProxy* device_thread_loop) OVERRIDE;
+ const scoped_refptr<base::SingleThreadTaskRunner>&
+ device_task_runner) OVERRIDE;
virtual void Unregister() OVERRIDE;
@@ -51,11 +56,6 @@ class CONTENT_EXPORT VideoCaptureManager : public MediaStreamProvider {
virtual void Close(int capture_session_id) OVERRIDE;
- // Used by unit test to make sure a fake device is used instead of a real
- // video capture device. Due to timing requirements, the function must be
- // called before EnumerateDevices and Open.
- void UseFakeDevice();
-
// Called by VideoCaptureHost to locate a capture device for |capture_params|,
// adding the Host as a client of the device's controller if successful. The
// value of |session_id| controls which device is selected;
@@ -81,12 +81,32 @@ class CONTENT_EXPORT VideoCaptureManager : public MediaStreamProvider {
// function.
void StopCaptureForClient(VideoCaptureController* controller,
VideoCaptureControllerID client_id,
- VideoCaptureControllerEventHandler* client_handler);
-
- // Retrieves the available capture supported formats for a particular device.
- // The supported formats are cached during device(s) enumeration.
- void GetDeviceSupportedFormats(int capture_session_id,
- media::VideoCaptureFormats* supported_formats);
+ VideoCaptureControllerEventHandler* client_handler,
+ bool aborted_due_to_error);
+
+ // Retrieves all capture supported formats for a particular device. Returns
+ // false if the |capture_session_id| is not found. The supported formats are
+ // cached during device(s) enumeration, and depending on the underlying
+ // implementation, could be an empty list.
+ bool GetDeviceSupportedFormats(
+ media::VideoCaptureSessionId capture_session_id,
+ media::VideoCaptureFormats* supported_formats);
+
+ // Retrieves the format(s) currently in use. Returns false if the
+ // |capture_session_id| is not found. Returns true and |formats_in_use|
+ // otherwise. |formats_in_use| is empty if the device is not in use.
+ bool GetDeviceFormatsInUse(media::VideoCaptureSessionId capture_session_id,
+ media::VideoCaptureFormats* formats_in_use);
+
+ // Sets the platform-dependent window ID for the desktop capture notification
+ // UI for the given session.
+ void SetDesktopCaptureWindowId(media::VideoCaptureSessionId session_id,
+ gfx::NativeViewId window_id);
+
+ // Gets a weak reference to the device factory, used for tests.
+ media::VideoCaptureDeviceFactory* video_capture_device_factory() const {
+ return video_capture_device_factory_.get();
+ }
private:
virtual ~VideoCaptureManager();
@@ -105,67 +125,80 @@ class CONTENT_EXPORT VideoCaptureManager : public MediaStreamProvider {
};
typedef std::vector<DeviceInfo> DeviceInfos;
- // Check to see if |entry| has no clients left on its controller. If so,
+ // Checks to see if |entry| has no clients left on its controller. If so,
// remove it from the list of devices, and delete it asynchronously. |entry|
// may be freed by this function.
void DestroyDeviceEntryIfNoClients(DeviceEntry* entry);
// Helpers to report an event to our Listener.
- void OnOpened(MediaStreamType type, int capture_session_id);
- void OnClosed(MediaStreamType type, int capture_session_id);
- void OnDevicesInfoEnumerated(
- MediaStreamType stream_type,
- const DeviceInfos& new_devices_info_cache);
-
- // Find a DeviceEntry by its device ID and type, if it is already opened.
+ void OnOpened(MediaStreamType type,
+ media::VideoCaptureSessionId capture_session_id);
+ void OnClosed(MediaStreamType type,
+ media::VideoCaptureSessionId capture_session_id);
+ void OnDevicesInfoEnumerated(MediaStreamType stream_type,
+ base::ElapsedTimer* timer,
+ const DeviceInfos& new_devices_info_cache);
+
+ // Finds a DeviceEntry by its device ID and type, if it is already opened.
DeviceEntry* GetDeviceEntryForMediaStreamDevice(
const MediaStreamDevice& device_info);
- // Find a DeviceEntry entry for the indicated session, creating a fresh one
+ // Finds a DeviceEntry entry for the indicated session, creating a fresh one
// if necessary. Returns NULL if the session id is invalid.
- DeviceEntry* GetOrCreateDeviceEntry(int capture_session_id);
+ DeviceEntry* GetOrCreateDeviceEntry(
+ media::VideoCaptureSessionId capture_session_id);
- // Find the DeviceEntry that owns a particular controller pointer.
+ // Finds the DeviceEntry that owns a particular controller pointer.
DeviceEntry* GetDeviceEntryForController(
- const VideoCaptureController* controller);
+ const VideoCaptureController* controller) const;
bool IsOnDeviceThread() const;
- // Queries the Names of the devices in the system; the formats supported by
- // the new devices are also queried, and consolidated with the copy of the
- // local device info cache passed. The consolidated list of devices and
- // supported formats is returned.
- DeviceInfos GetAvailableDevicesInfoOnDeviceThread(
+ // Consolidates the cached devices list with the list of currently connected
+ // devices in the system |names_snapshot|. Retrieves the supported formats of
+ // the new devices and sends the new cache to OnDevicesInfoEnumerated().
+ void ConsolidateDevicesInfoOnDeviceThread(
+ base::Callback<void(const DeviceInfos&)> on_devices_enumerated_callback,
MediaStreamType stream_type,
- const DeviceInfos& old_device_info_cache);
+ const DeviceInfos& old_device_info_cache,
+ scoped_ptr<media::VideoCaptureDevice::Names> names_snapshot);
- // Create and Start a new VideoCaptureDevice, storing the result in
+ // Creates and Starts a new VideoCaptureDevice, storing the result in
// |entry->video_capture_device|. Ownership of |client| passes to
// the device.
void DoStartDeviceOnDeviceThread(
+ media::VideoCaptureSessionId session_id,
DeviceEntry* entry,
const media::VideoCaptureParams& params,
scoped_ptr<media::VideoCaptureDevice::Client> client);
- // Stop and destroy the VideoCaptureDevice held in
+ // Stops and destroys the VideoCaptureDevice held in
// |entry->video_capture_device|.
void DoStopDeviceOnDeviceThread(DeviceEntry* entry);
DeviceInfo* FindDeviceInfoById(const std::string& id,
DeviceInfos& device_vector);
+ void SetDesktopCaptureWindowIdOnDeviceThread(DeviceEntry* entry,
+ gfx::NativeViewId window_id);
+
+ void SaveDesktopCaptureWindowIdOnDeviceThread(
+ media::VideoCaptureSessionId session_id,
+ gfx::NativeViewId window_id);
+
// The message loop of media stream device thread, where VCD's live.
- scoped_refptr<base::MessageLoopProxy> device_loop_;
+ scoped_refptr<base::SingleThreadTaskRunner> device_task_runner_;
// Only accessed on Browser::IO thread.
MediaStreamProviderListener* listener_;
- int new_capture_session_id_;
+ media::VideoCaptureSessionId new_capture_session_id_;
+ typedef std::map<media::VideoCaptureSessionId, MediaStreamDevice> SessionMap;
// An entry is kept in this map for every session that has been created via
// the Open() entry point. The keys are session_id's. This map is used to
// determine which device to use when StartCaptureForClient() occurs. Used
// only on the IO thread.
- std::map<int, MediaStreamDevice> sessions_;
+ SessionMap sessions_;
// An entry, kept in a map, that owns a VideoCaptureDevice and its associated
// VideoCaptureController. VideoCaptureManager owns all VideoCaptureDevices
@@ -194,24 +227,22 @@ class CONTENT_EXPORT VideoCaptureManager : public MediaStreamProvider {
typedef std::set<DeviceEntry*> DeviceEntries;
DeviceEntries devices_;
+ // Device creation factory injected on construction from MediaStreamManager or
+ // from the test harness.
+ scoped_ptr<media::VideoCaptureDeviceFactory> video_capture_device_factory_;
+
// Local cache of the enumerated video capture devices' names and capture
// supported formats. A snapshot of the current devices and their capabilities
- // is composed in GetAvailableDevicesInfoOnDeviceThread() --coming
- // from EnumerateDevices()--, and this snapshot is used to update this list in
- // OnDevicesInfoEnumerated(). GetDeviceSupportedFormats() will
+ // is composed in VideoCaptureDeviceFactory::EnumerateDeviceNames() and
+ // ConsolidateDevicesInfoOnDeviceThread(), and this snapshot is used to update
+ // this list in OnDevicesInfoEnumerated(). GetDeviceSupportedFormats() will
// use this list if the device is not started, otherwise it will retrieve the
// active device capture format from the VideoCaptureController associated.
DeviceInfos devices_info_cache_;
- // For unit testing and for performance/quality tests, a test device can be
- // used instead of a real one. The device can be a simple fake device (a
- // rolling pacman), or a file that is played in a loop continuously. This only
- // applies to the MEDIA_DEVICE_VIDEO_CAPTURE device type.
- enum {
- DISABLED,
- TEST_PATTERN,
- Y4M_FILE
- } artificial_device_source_for_testing_;
+ // Accessed on the device thread only.
+ std::map<media::VideoCaptureSessionId, gfx::NativeViewId>
+ notification_window_ids_;
DISALLOW_COPY_AND_ASSIGN(VideoCaptureManager);
};
diff --git a/chromium/content/browser/renderer_host/media/video_capture_manager_unittest.cc b/chromium/content/browser/renderer_host/media/video_capture_manager_unittest.cc
index bc89159e011..8599924b052 100644
--- a/chromium/content/browser/renderer_host/media/video_capture_manager_unittest.cc
+++ b/chromium/content/browser/renderer_host/media/video_capture_manager_unittest.cc
@@ -16,8 +16,7 @@
#include "content/browser/renderer_host/media/video_capture_controller_event_handler.h"
#include "content/browser/renderer_host/media/video_capture_manager.h"
#include "content/common/media/media_stream_options.h"
-#include "media/video/capture/fake_video_capture_device.h"
-#include "media/video/capture/video_capture_device.h"
+#include "media/video/capture/fake_video_capture_device_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -39,8 +38,7 @@ class MockMediaStreamProviderListener : public MediaStreamProviderListener {
MOCK_METHOD2(Closed, void(MediaStreamType, int));
MOCK_METHOD2(DevicesEnumerated, void(MediaStreamType,
const StreamDeviceInfoArray&));
- MOCK_METHOD3(Error, void(MediaStreamType, int,
- MediaStreamProviderError));
+ MOCK_METHOD2(Aborted, void(MediaStreamType, int));
}; // class MockMediaStreamProviderListener
// Needed as an input argument to StartCaptureForClient().
@@ -53,11 +51,15 @@ class MockFrameObserver : public VideoCaptureControllerEventHandler {
int length, int buffer_id) OVERRIDE {}
virtual void OnBufferDestroyed(const VideoCaptureControllerID& id,
int buffer_id) OVERRIDE {}
- virtual void OnBufferReady(
- const VideoCaptureControllerID& id,
- int buffer_id,
- base::Time timestamp,
- const media::VideoCaptureFormat& format) OVERRIDE {}
+ virtual void OnBufferReady(const VideoCaptureControllerID& id,
+ int buffer_id,
+ const media::VideoCaptureFormat& format,
+ base::TimeTicks timestamp) OVERRIDE {}
+ virtual void OnMailboxBufferReady(const VideoCaptureControllerID& id,
+ int buffer_id,
+ const gpu::MailboxHolder& mailbox_holder,
+ const media::VideoCaptureFormat& format,
+ base::TimeTicks timestamp) OVERRIDE {}
virtual void OnEnded(const VideoCaptureControllerID& id) OVERRIDE {}
void OnGotControllerCallback(VideoCaptureControllerID) {}
@@ -72,11 +74,16 @@ class VideoCaptureManagerTest : public testing::Test {
protected:
virtual void SetUp() OVERRIDE {
listener_.reset(new MockMediaStreamProviderListener());
- message_loop_.reset(new base::MessageLoop(base::MessageLoop::TYPE_IO));
+ message_loop_.reset(new base::MessageLoopForIO);
io_thread_.reset(new BrowserThreadImpl(BrowserThread::IO,
message_loop_.get()));
- vcm_ = new VideoCaptureManager();
- vcm_->UseFakeDevice();
+ vcm_ = new VideoCaptureManager(scoped_ptr<media::VideoCaptureDeviceFactory>(
+ new media::FakeVideoCaptureDeviceFactory()));
+ video_capture_device_factory_ =
+ static_cast<media::FakeVideoCaptureDeviceFactory*>(
+ vcm_->video_capture_device_factory());
+ const int32 kNumberOfFakeDevices = 2;
+ video_capture_device_factory_->set_number_of_devices(kNumberOfFakeDevices);
vcm_->Register(listener_.get(), message_loop_->message_loop_proxy().get());
frame_observer_.reset(new MockFrameObserver());
}
@@ -123,7 +130,7 @@ class VideoCaptureManagerTest : public testing::Test {
void StopClient(VideoCaptureControllerID client_id) {
ASSERT_TRUE(1 == controllers_.count(client_id));
vcm_->StopCaptureForClient(controllers_[client_id], client_id,
- frame_observer_.get());
+ frame_observer_.get(), false);
controllers_.erase(client_id);
}
@@ -134,6 +141,7 @@ class VideoCaptureManagerTest : public testing::Test {
scoped_ptr<base::MessageLoop> message_loop_;
scoped_ptr<BrowserThreadImpl> io_thread_;
scoped_ptr<MockFrameObserver> frame_observer_;
+ media::FakeVideoCaptureDeviceFactory* video_capture_device_factory_;
private:
DISALLOW_COPY_AND_ASSIGN(VideoCaptureManagerTest);
@@ -147,9 +155,9 @@ TEST_F(VideoCaptureManagerTest, CreateAndClose) {
InSequence s;
EXPECT_CALL(*listener_, DevicesEnumerated(MEDIA_DEVICE_VIDEO_CAPTURE, _))
- .Times(1).WillOnce(SaveArg<1>(&devices));
- EXPECT_CALL(*listener_, Opened(MEDIA_DEVICE_VIDEO_CAPTURE, _)).Times(1);
- EXPECT_CALL(*listener_, Closed(MEDIA_DEVICE_VIDEO_CAPTURE, _)).Times(1);
+ .WillOnce(SaveArg<1>(&devices));
+ EXPECT_CALL(*listener_, Opened(MEDIA_DEVICE_VIDEO_CAPTURE, _));
+ EXPECT_CALL(*listener_, Closed(MEDIA_DEVICE_VIDEO_CAPTURE, _));
vcm_->EnumerateDevices(MEDIA_DEVICE_VIDEO_CAPTURE);
@@ -167,13 +175,42 @@ TEST_F(VideoCaptureManagerTest, CreateAndClose) {
vcm_->Unregister();
}
+// Try to open, start, and abort a device.
+TEST_F(VideoCaptureManagerTest, CreateAndAbort) {
+ StreamDeviceInfoArray devices;
+
+ InSequence s;
+ EXPECT_CALL(*listener_, DevicesEnumerated(MEDIA_DEVICE_VIDEO_CAPTURE, _))
+ .WillOnce(SaveArg<1>(&devices));
+ EXPECT_CALL(*listener_, Opened(MEDIA_DEVICE_VIDEO_CAPTURE, _));
+ EXPECT_CALL(*listener_, Aborted(MEDIA_DEVICE_VIDEO_CAPTURE, _));
+
+ vcm_->EnumerateDevices(MEDIA_DEVICE_VIDEO_CAPTURE);
+
+ // Wait to get device callback.
+ message_loop_->RunUntilIdle();
+
+ int video_session_id = vcm_->Open(devices.front());
+ VideoCaptureControllerID client_id = StartClient(video_session_id, true);
+
+ // Wait for device opened.
+ message_loop_->RunUntilIdle();
+
+ vcm_->StopCaptureForClient(controllers_[client_id], client_id,
+ frame_observer_.get(), true);
+
+ // Wait to check callbacks before removing the listener.
+ message_loop_->RunUntilIdle();
+ vcm_->Unregister();
+}
+
// Open the same device twice.
TEST_F(VideoCaptureManagerTest, OpenTwice) {
StreamDeviceInfoArray devices;
InSequence s;
EXPECT_CALL(*listener_, DevicesEnumerated(MEDIA_DEVICE_VIDEO_CAPTURE, _))
- .Times(1).WillOnce(SaveArg<1>(&devices));
+ .WillOnce(SaveArg<1>(&devices));
EXPECT_CALL(*listener_, Opened(MEDIA_DEVICE_VIDEO_CAPTURE, _)).Times(2);
EXPECT_CALL(*listener_, Closed(MEDIA_DEVICE_VIDEO_CAPTURE, _)).Times(2);
@@ -201,58 +238,63 @@ TEST_F(VideoCaptureManagerTest, OpenTwice) {
TEST_F(VideoCaptureManagerTest, ConnectAndDisconnectDevices) {
StreamDeviceInfoArray devices;
int number_of_devices_keep =
- media::FakeVideoCaptureDevice::NumberOfFakeDevices();
+ video_capture_device_factory_->number_of_devices();
InSequence s;
EXPECT_CALL(*listener_, DevicesEnumerated(MEDIA_DEVICE_VIDEO_CAPTURE, _))
- .Times(1).WillOnce(SaveArg<1>(&devices));
+ .WillOnce(SaveArg<1>(&devices));
vcm_->EnumerateDevices(MEDIA_DEVICE_VIDEO_CAPTURE);
message_loop_->RunUntilIdle();
ASSERT_EQ(devices.size(), 2u);
// Simulate we remove 1 fake device.
- media::FakeVideoCaptureDevice::SetNumberOfFakeDevices(1);
+ video_capture_device_factory_->set_number_of_devices(1);
EXPECT_CALL(*listener_, DevicesEnumerated(MEDIA_DEVICE_VIDEO_CAPTURE, _))
- .Times(1).WillOnce(SaveArg<1>(&devices));
+ .WillOnce(SaveArg<1>(&devices));
vcm_->EnumerateDevices(MEDIA_DEVICE_VIDEO_CAPTURE);
message_loop_->RunUntilIdle();
ASSERT_EQ(devices.size(), 1u);
// Simulate we add 2 fake devices.
- media::FakeVideoCaptureDevice::SetNumberOfFakeDevices(3);
+ video_capture_device_factory_->set_number_of_devices(3);
EXPECT_CALL(*listener_, DevicesEnumerated(MEDIA_DEVICE_VIDEO_CAPTURE, _))
- .Times(1).WillOnce(SaveArg<1>(&devices));
+ .WillOnce(SaveArg<1>(&devices));
vcm_->EnumerateDevices(MEDIA_DEVICE_VIDEO_CAPTURE);
message_loop_->RunUntilIdle();
ASSERT_EQ(devices.size(), 3u);
vcm_->Unregister();
- media::FakeVideoCaptureDevice::SetNumberOfFakeDevices(number_of_devices_keep);
+ video_capture_device_factory_->set_number_of_devices(number_of_devices_keep);
}
// Enumerate devices and open the first, then check the list of supported
-// formats. Then start the opened device. The capability list should be reduced
-// to just one format, and this should be the one used when configuring-starting
-// the device. Finally stop the device and check that the capabilities have been
-// restored.
+// formats. Then start the opened device. The capability list should stay the
+// same. Finally stop the device and check that the capabilities stay unchanged.
TEST_F(VideoCaptureManagerTest, ManipulateDeviceAndCheckCapabilities) {
StreamDeviceInfoArray devices;
+ // Before enumerating the devices, requesting formats should return false.
+ int video_session_id = 0;
+ media::VideoCaptureFormats supported_formats;
+ supported_formats.clear();
+ EXPECT_FALSE(
+ vcm_->GetDeviceSupportedFormats(video_session_id, &supported_formats));
+
InSequence s;
EXPECT_CALL(*listener_, DevicesEnumerated(MEDIA_DEVICE_VIDEO_CAPTURE, _))
- .Times(1).WillOnce(SaveArg<1>(&devices));
+ .WillOnce(SaveArg<1>(&devices));
vcm_->EnumerateDevices(MEDIA_DEVICE_VIDEO_CAPTURE);
message_loop_->RunUntilIdle();
+ ASSERT_GE(devices.size(), 2u);
- EXPECT_CALL(*listener_, Opened(MEDIA_DEVICE_VIDEO_CAPTURE, _)).Times(1);
- int video_session_id = vcm_->Open(devices.front());
+ EXPECT_CALL(*listener_, Opened(MEDIA_DEVICE_VIDEO_CAPTURE, _));
+ video_session_id = vcm_->Open(devices.front());
message_loop_->RunUntilIdle();
- // When the device has been opened, we should see all the devices'
- // supported formats.
- media::VideoCaptureFormats supported_formats;
- vcm_->GetDeviceSupportedFormats(video_session_id, &supported_formats);
- ASSERT_EQ(devices.size(), 2u);
+ // Right after opening the device, we should see all its formats.
+ supported_formats.clear();
+ EXPECT_TRUE(
+ vcm_->GetDeviceSupportedFormats(video_session_id, &supported_formats));
ASSERT_GT(supported_formats.size(), 1u);
EXPECT_GT(supported_formats[0].frame_size.width(), 1);
EXPECT_GT(supported_formats[0].frame_size.height(), 1);
@@ -263,19 +305,24 @@ TEST_F(VideoCaptureManagerTest, ManipulateDeviceAndCheckCapabilities) {
VideoCaptureControllerID client_id = StartClient(video_session_id, true);
message_loop_->RunUntilIdle();
- // After StartClient(), device's supported formats should be reduced to one.
- vcm_->GetDeviceSupportedFormats(video_session_id, &supported_formats);
- ASSERT_EQ(supported_formats.size(), 1u);
+ // After StartClient(), device's supported formats should stay the same.
+ supported_formats.clear();
+ EXPECT_TRUE(
+ vcm_->GetDeviceSupportedFormats(video_session_id, &supported_formats));
+ ASSERT_GE(supported_formats.size(), 2u);
EXPECT_GT(supported_formats[0].frame_size.width(), 1);
EXPECT_GT(supported_formats[0].frame_size.height(), 1);
EXPECT_GT(supported_formats[0].frame_rate, 1);
+ EXPECT_GT(supported_formats[1].frame_size.width(), 1);
+ EXPECT_GT(supported_formats[1].frame_size.height(), 1);
+ EXPECT_GT(supported_formats[1].frame_rate, 1);
- EXPECT_CALL(*listener_, Closed(MEDIA_DEVICE_VIDEO_CAPTURE, _)).Times(1);
+ EXPECT_CALL(*listener_, Closed(MEDIA_DEVICE_VIDEO_CAPTURE, _));
StopClient(client_id);
- // After StopClient(), the device's list of supported formats should be
- // restored to the original one.
- vcm_->GetDeviceSupportedFormats(video_session_id, &supported_formats);
- ASSERT_GT(supported_formats.size(), 1u);
+ supported_formats.clear();
+ EXPECT_TRUE(
+ vcm_->GetDeviceSupportedFormats(video_session_id, &supported_formats));
+ ASSERT_GE(supported_formats.size(), 2u);
EXPECT_GT(supported_formats[0].frame_size.width(), 1);
EXPECT_GT(supported_formats[0].frame_size.height(), 1);
EXPECT_GT(supported_formats[0].frame_rate, 1);
@@ -288,13 +335,63 @@ TEST_F(VideoCaptureManagerTest, ManipulateDeviceAndCheckCapabilities) {
vcm_->Unregister();
}
+// Enumerate devices and open the first, then check the formats currently in
+// use, which should be an empty vector. Then start the opened device. The
+// format(s) in use should be just one format (the one used when configuring-
+// starting the device). Finally stop the device and check that the formats in
+// use is an empty vector.
+TEST_F(VideoCaptureManagerTest, StartDeviceAndGetDeviceFormatInUse) {
+ StreamDeviceInfoArray devices;
+
+ InSequence s;
+ EXPECT_CALL(*listener_, DevicesEnumerated(MEDIA_DEVICE_VIDEO_CAPTURE, _))
+ .WillOnce(SaveArg<1>(&devices));
+ vcm_->EnumerateDevices(MEDIA_DEVICE_VIDEO_CAPTURE);
+ message_loop_->RunUntilIdle();
+ ASSERT_GE(devices.size(), 2u);
+
+ EXPECT_CALL(*listener_, Opened(MEDIA_DEVICE_VIDEO_CAPTURE, _));
+ int video_session_id = vcm_->Open(devices.front());
+ message_loop_->RunUntilIdle();
+
+ // Right after opening the device, we should see no format in use.
+ media::VideoCaptureFormats formats_in_use;
+ EXPECT_TRUE(vcm_->GetDeviceFormatsInUse(video_session_id, &formats_in_use));
+ EXPECT_TRUE(formats_in_use.empty());
+
+ VideoCaptureControllerID client_id = StartClient(video_session_id, true);
+ message_loop_->RunUntilIdle();
+ // After StartClient(), |formats_in_use| should contain one valid format.
+ EXPECT_TRUE(vcm_->GetDeviceFormatsInUse(video_session_id, &formats_in_use));
+ EXPECT_EQ(formats_in_use.size(), 1u);
+ if (formats_in_use.size()) {
+ media::VideoCaptureFormat& format_in_use = formats_in_use.front();
+ EXPECT_TRUE(format_in_use.IsValid());
+ EXPECT_GT(format_in_use.frame_size.width(), 1);
+ EXPECT_GT(format_in_use.frame_size.height(), 1);
+ EXPECT_GT(format_in_use.frame_rate, 1);
+ }
+ formats_in_use.clear();
+
+ EXPECT_CALL(*listener_, Closed(MEDIA_DEVICE_VIDEO_CAPTURE, _));
+ StopClient(client_id);
+ message_loop_->RunUntilIdle();
+ // After StopClient(), the device's formats in use should be empty again.
+ EXPECT_TRUE(vcm_->GetDeviceFormatsInUse(video_session_id, &formats_in_use));
+ EXPECT_TRUE(formats_in_use.empty());
+
+ vcm_->Close(video_session_id);
+ message_loop_->RunUntilIdle();
+ vcm_->Unregister();
+}
+
// Open two different devices.
TEST_F(VideoCaptureManagerTest, OpenTwo) {
StreamDeviceInfoArray devices;
InSequence s;
EXPECT_CALL(*listener_, DevicesEnumerated(MEDIA_DEVICE_VIDEO_CAPTURE, _))
- .Times(1).WillOnce(SaveArg<1>(&devices));
+ .WillOnce(SaveArg<1>(&devices));
EXPECT_CALL(*listener_, Opened(MEDIA_DEVICE_VIDEO_CAPTURE, _)).Times(2);
EXPECT_CALL(*listener_, Closed(MEDIA_DEVICE_VIDEO_CAPTURE, _)).Times(2);
@@ -323,10 +420,10 @@ TEST_F(VideoCaptureManagerTest, OpenNotExisting) {
InSequence s;
EXPECT_CALL(*listener_, DevicesEnumerated(MEDIA_DEVICE_VIDEO_CAPTURE, _))
- .Times(1).WillOnce(SaveArg<1>(&devices));
- EXPECT_CALL(*listener_, Opened(MEDIA_DEVICE_VIDEO_CAPTURE, _)).Times(1);
- EXPECT_CALL(*frame_observer_, OnError(_)).Times(1);
- EXPECT_CALL(*listener_, Closed(MEDIA_DEVICE_VIDEO_CAPTURE, _)).Times(1);
+ .WillOnce(SaveArg<1>(&devices));
+ EXPECT_CALL(*listener_, Opened(MEDIA_DEVICE_VIDEO_CAPTURE, _));
+ EXPECT_CALL(*frame_observer_, OnError(_));
+ EXPECT_CALL(*listener_, Closed(MEDIA_DEVICE_VIDEO_CAPTURE, _));
vcm_->EnumerateDevices(MEDIA_DEVICE_VIDEO_CAPTURE);
@@ -365,9 +462,9 @@ TEST_F(VideoCaptureManagerTest, CloseWithoutStop) {
InSequence s;
EXPECT_CALL(*listener_, DevicesEnumerated(MEDIA_DEVICE_VIDEO_CAPTURE, _))
- .Times(1).WillOnce(SaveArg<1>(&devices));
- EXPECT_CALL(*listener_, Opened(MEDIA_DEVICE_VIDEO_CAPTURE, _)).Times(1);
- EXPECT_CALL(*listener_, Closed(MEDIA_DEVICE_VIDEO_CAPTURE, _)).Times(1);
+ .WillOnce(SaveArg<1>(&devices));
+ EXPECT_CALL(*listener_, Opened(MEDIA_DEVICE_VIDEO_CAPTURE, _));
+ EXPECT_CALL(*listener_, Closed(MEDIA_DEVICE_VIDEO_CAPTURE, _));
vcm_->EnumerateDevices(MEDIA_DEVICE_VIDEO_CAPTURE);
@@ -388,4 +485,7 @@ TEST_F(VideoCaptureManagerTest, CloseWithoutStop) {
vcm_->Unregister();
}
+// TODO(mcasas): Add a test to check consolidation of the supported formats
+// provided by the device when http://crbug.com/323913 is closed.
+
} // namespace content
diff --git a/chromium/content/browser/renderer_host/media/webrtc_identity_service_host.cc b/chromium/content/browser/renderer_host/media/webrtc_identity_service_host.cc
index 16f7b6e278b..0cdd5184f2c 100644
--- a/chromium/content/browser/renderer_host/media/webrtc_identity_service_host.cc
+++ b/chromium/content/browser/renderer_host/media/webrtc_identity_service_host.cc
@@ -16,7 +16,8 @@ namespace content {
WebRTCIdentityServiceHost::WebRTCIdentityServiceHost(
int renderer_process_id,
scoped_refptr<WebRTCIdentityStore> identity_store)
- : renderer_process_id_(renderer_process_id),
+ : BrowserMessageFilter(WebRTCIdentityMsgStart),
+ renderer_process_id_(renderer_process_id),
identity_store_(identity_store),
weak_factory_(this) {}
@@ -25,14 +26,13 @@ WebRTCIdentityServiceHost::~WebRTCIdentityServiceHost() {
cancel_callback_.Run();
}
-bool WebRTCIdentityServiceHost::OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) {
+bool WebRTCIdentityServiceHost::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(WebRTCIdentityServiceHost, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(WebRTCIdentityServiceHost, message)
IPC_MESSAGE_HANDLER(WebRTCIdentityMsg_RequestIdentity, OnRequestIdentity)
IPC_MESSAGE_HANDLER(WebRTCIdentityMsg_CancelRequest, OnCancelRequest)
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return handled;
}
diff --git a/chromium/content/browser/renderer_host/media/webrtc_identity_service_host.h b/chromium/content/browser/renderer_host/media/webrtc_identity_service_host.h
index 079e3077a65..7bc8233b9cd 100644
--- a/chromium/content/browser/renderer_host/media/webrtc_identity_service_host.h
+++ b/chromium/content/browser/renderer_host/media/webrtc_identity_service_host.h
@@ -33,8 +33,7 @@ class CONTENT_EXPORT WebRTCIdentityServiceHost : public BrowserMessageFilter {
virtual ~WebRTCIdentityServiceHost();
// content::BrowserMessageFilter override.
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
private:
// |sequence_number| is the same as in the OnRequestIdentity call.
diff --git a/chromium/content/browser/renderer_host/media/webrtc_identity_service_host_unittest.cc b/chromium/content/browser/renderer_host/media/webrtc_identity_service_host_unittest.cc
index 9e336d1db62..a785a22b993 100644
--- a/chromium/content/browser/renderer_host/media/webrtc_identity_service_host_unittest.cc
+++ b/chromium/content/browser/renderer_host/media/webrtc_identity_service_host_unittest.cc
@@ -73,10 +73,8 @@ class WebRTCIdentityServiceHostForTest : public WebRTCIdentityServiceHost {
return true;
}
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE {
- return WebRTCIdentityServiceHost::OnMessageReceived(message,
- message_was_ok);
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
+ return WebRTCIdentityServiceHost::OnMessageReceived(message);
}
IPC::Message GetLastMessage() { return messages_.back(); }
@@ -103,20 +101,15 @@ class WebRTCIdentityServiceHostTest : public ::testing::Test {
host_(new WebRTCIdentityServiceHostForTest(store_.get())) {}
void SendRequestToHost() {
- bool ok;
host_->OnMessageReceived(
WebRTCIdentityMsg_RequestIdentity(FAKE_SEQUENCE_NUMBER,
GURL(FAKE_ORIGIN),
FAKE_IDENTITY_NAME,
- FAKE_COMMON_NAME),
- &ok);
- ASSERT_TRUE(ok);
+ FAKE_COMMON_NAME));
}
void SendCancelRequestToHost() {
- bool ok;
- host_->OnMessageReceived(WebRTCIdentityMsg_CancelRequest(), &ok);
- ASSERT_TRUE(ok);
+ host_->OnMessageReceived(WebRTCIdentityMsg_CancelRequest());
}
void VerifyRequestFailedMessage(int error) {
diff --git a/chromium/content/browser/renderer_host/memory_benchmark_message_filter.cc b/chromium/content/browser/renderer_host/memory_benchmark_message_filter.cc
index d5598528940..607325376f7 100644
--- a/chromium/content/browser/renderer_host/memory_benchmark_message_filter.cc
+++ b/chromium/content/browser/renderer_host/memory_benchmark_message_filter.cc
@@ -12,16 +12,14 @@
namespace content {
-MemoryBenchmarkMessageFilter::MemoryBenchmarkMessageFilter() {
+MemoryBenchmarkMessageFilter::MemoryBenchmarkMessageFilter()
+ : BrowserMessageFilter(MemoryBenchmarkMsgStart) {
}
bool MemoryBenchmarkMessageFilter::OnMessageReceived(
- const IPC::Message& message,
- bool* message_was_ok) {
+ const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(MemoryBenchmarkMessageFilter,
- message,
- *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(MemoryBenchmarkMessageFilter, message)
IPC_MESSAGE_HANDLER(MemoryBenchmarkHostMsg_HeapProfilerDump,
OnHeapProfilerDump)
IPC_MESSAGE_UNHANDLED(handled = false)
diff --git a/chromium/content/browser/renderer_host/memory_benchmark_message_filter.h b/chromium/content/browser/renderer_host/memory_benchmark_message_filter.h
index da5a6aa4b5d..7255ab915e6 100644
--- a/chromium/content/browser/renderer_host/memory_benchmark_message_filter.h
+++ b/chromium/content/browser/renderer_host/memory_benchmark_message_filter.h
@@ -15,8 +15,7 @@ class MemoryBenchmarkMessageFilter : public BrowserMessageFilter {
public:
MemoryBenchmarkMessageFilter();
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
private:
virtual ~MemoryBenchmarkMessageFilter();
diff --git a/chromium/content/browser/renderer_host/native_web_keyboard_event_aura.cc b/chromium/content/browser/renderer_host/native_web_keyboard_event_aura.cc
index c33f12ffc74..8757b9be943 100644
--- a/chromium/content/browser/renderer_host/native_web_keyboard_event_aura.cc
+++ b/chromium/content/browser/renderer_host/native_web_keyboard_event_aura.cc
@@ -34,21 +34,24 @@ namespace content {
NativeWebKeyboardEvent::NativeWebKeyboardEvent()
: os_event(NULL),
- skip_in_browser(false) {
+ skip_in_browser(false),
+ match_edit_command(false) {
}
NativeWebKeyboardEvent::NativeWebKeyboardEvent(gfx::NativeEvent native_event)
: WebKeyboardEvent(MakeWebKeyboardEvent(
static_cast<ui::KeyEvent*>(native_event))),
os_event(CopyEvent(native_event)),
- skip_in_browser(false) {
+ skip_in_browser(false),
+ match_edit_command(false) {
}
NativeWebKeyboardEvent::NativeWebKeyboardEvent(
const NativeWebKeyboardEvent& other)
: WebKeyboardEvent(other),
os_event(CopyEvent(other.os_event)),
- skip_in_browser(other.skip_in_browser) {
+ skip_in_browser(other.skip_in_browser),
+ match_edit_command(false) {
}
NativeWebKeyboardEvent::NativeWebKeyboardEvent(
@@ -58,7 +61,8 @@ NativeWebKeyboardEvent::NativeWebKeyboardEvent(
int state,
double time_stamp_seconds)
: os_event(NULL),
- skip_in_browser(false) {
+ skip_in_browser(false),
+ match_edit_command(false) {
switch (key_event_type) {
case ui::ET_KEY_PRESSED:
type = is_char ? blink::WebInputEvent::Char :
@@ -88,7 +92,7 @@ NativeWebKeyboardEvent& NativeWebKeyboardEvent::operator=(
delete os_event;
os_event = CopyEvent(other.os_event);
skip_in_browser = other.skip_in_browser;
-
+ match_edit_command = other.match_edit_command;
return *this;
}
diff --git a/chromium/content/browser/renderer_host/native_web_keyboard_event_gtk.cc b/chromium/content/browser/renderer_host/native_web_keyboard_event_gtk.cc
deleted file mode 100644
index 74ce6a36cbb..00000000000
--- a/chromium/content/browser/renderer_host/native_web_keyboard_event_gtk.cc
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/public/browser/native_web_keyboard_event.h"
-
-#include <gdk/gdk.h>
-
-#include "content/browser/renderer_host/input/web_input_event_builders_gtk.h"
-
-namespace {
-
-void CopyEventTo(gfx::NativeEvent in, gfx::NativeEvent* out) {
- *out = in ? gdk_event_copy(in) : NULL;
-}
-
-void FreeEvent(gfx::NativeEvent event) {
- if (event)
- gdk_event_free(event);
-}
-
-} // namespace
-
-namespace content {
-
-NativeWebKeyboardEvent::NativeWebKeyboardEvent()
- : os_event(NULL),
- skip_in_browser(false),
- match_edit_command(false) {
-}
-
-NativeWebKeyboardEvent::NativeWebKeyboardEvent(gfx::NativeEvent native_event)
- : WebKeyboardEvent(WebKeyboardEventBuilder::Build(&native_event->key)),
- skip_in_browser(false),
- match_edit_command(false) {
- CopyEventTo(native_event, &os_event);
-}
-
-NativeWebKeyboardEvent::NativeWebKeyboardEvent(wchar_t character,
- int state,
- double time_stamp_seconds)
- : WebKeyboardEvent(WebKeyboardEventBuilder::Build(character,
- state,
- time_stamp_seconds)),
- os_event(NULL),
- skip_in_browser(false),
- match_edit_command(false) {
-}
-
-NativeWebKeyboardEvent::NativeWebKeyboardEvent(
- const NativeWebKeyboardEvent& other)
- : WebKeyboardEvent(other),
- skip_in_browser(other.skip_in_browser),
- match_edit_command(other.match_edit_command) {
- CopyEventTo(other.os_event, &os_event);
-}
-
-NativeWebKeyboardEvent& NativeWebKeyboardEvent::operator=(
- const NativeWebKeyboardEvent& other) {
- WebKeyboardEvent::operator=(other);
-
- FreeEvent(os_event);
- CopyEventTo(other.os_event, &os_event);
-
- skip_in_browser = other.skip_in_browser;
- match_edit_command = other.match_edit_command;
-
- return *this;
-}
-
-NativeWebKeyboardEvent::~NativeWebKeyboardEvent() {
- FreeEvent(os_event);
-}
-
-} // namespace content
diff --git a/chromium/content/browser/renderer_host/native_web_keyboard_event_win.cc b/chromium/content/browser/renderer_host/native_web_keyboard_event_win.cc
deleted file mode 100644
index 0f54a893c86..00000000000
--- a/chromium/content/browser/renderer_host/native_web_keyboard_event_win.cc
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/public/browser/native_web_keyboard_event.h"
-
-#include "content/browser/renderer_host/input/web_input_event_builders_win.h"
-
-using blink::WebKeyboardEvent;
-
-namespace content {
-
-NativeWebKeyboardEvent::NativeWebKeyboardEvent()
- : skip_in_browser(false) {
- memset(&os_event, 0, sizeof(os_event));
-}
-
-NativeWebKeyboardEvent::NativeWebKeyboardEvent(gfx::NativeEvent native_event)
- : WebKeyboardEvent(
- WebKeyboardEventBuilder::Build(native_event.hwnd,
- native_event.message,
- native_event.wParam,
- native_event.lParam)),
- os_event(native_event),
- skip_in_browser(false) {
-}
-
-NativeWebKeyboardEvent::NativeWebKeyboardEvent(
- const NativeWebKeyboardEvent& other)
- : WebKeyboardEvent(other),
- os_event(other.os_event),
- skip_in_browser(other.skip_in_browser) {
-}
-
-NativeWebKeyboardEvent& NativeWebKeyboardEvent::operator=(
- const NativeWebKeyboardEvent& other) {
- WebKeyboardEvent::operator=(other);
-
- os_event = other.os_event;
- skip_in_browser = other.skip_in_browser;
-
- return *this;
-}
-
-NativeWebKeyboardEvent::~NativeWebKeyboardEvent() {
- // Noop under windows
-}
-
-} // namespace content
diff --git a/chromium/content/browser/renderer_host/overscroll_controller.cc b/chromium/content/browser/renderer_host/overscroll_controller.cc
index 4c33aa8c20e..3a6e7576563 100644
--- a/chromium/content/browser/renderer_host/overscroll_controller.cc
+++ b/chromium/content/browser/renderer_host/overscroll_controller.cc
@@ -34,9 +34,7 @@ OverscrollController::OverscrollController()
OverscrollController::~OverscrollController() {
}
-OverscrollController::Disposition OverscrollController::DispatchEvent(
- const blink::WebInputEvent& event,
- const ui::LatencyInfo& latency_info) {
+bool OverscrollController::WillHandleEvent(const blink::WebInputEvent& event) {
if (scroll_state_ != STATE_UNKNOWN) {
switch (event.type) {
case blink::WebInputEvent::GestureScrollEnd:
@@ -67,41 +65,24 @@ OverscrollController::Disposition OverscrollController::DispatchEvent(
if (DispatchEventCompletesAction(event)) {
CompleteAction();
- // If the overscroll was caused by touch-scrolling, then the gesture event
- // that completes the action needs to be sent to the renderer, because the
- // touch-scrolls maintain state in the renderer side (in the compositor, for
- // example), and the event that completes this action needs to be sent to
- // the renderer so that those states can be updated/reset appropriately.
- if (blink::WebInputEvent::isGestureEventType(event.type)) {
- // A gesture-event isn't sent to the GestureEventFilter when overscroll is
- // in progress. So dispatch the event through the RenderWidgetHost so that
- // it can reach the GestureEventFilter.
- return SHOULD_FORWARD_TO_GESTURE_FILTER;
- }
-
- return SHOULD_FORWARD_TO_RENDERER;
+ // Let the event be dispatched to the renderer.
+ return false;
}
if (overscroll_mode_ != OVERSCROLL_NONE && DispatchEventResetsState(event)) {
SetOverscrollMode(OVERSCROLL_NONE);
- if (blink::WebInputEvent::isGestureEventType(event.type)) {
- // A gesture-event isn't sent to the GestureEventFilter when overscroll is
- // in progress. So dispatch the event through the RenderWidgetHost so that
- // it can reach the GestureEventFilter.
- return SHOULD_FORWARD_TO_GESTURE_FILTER;
- }
// Let the event be dispatched to the renderer.
- return SHOULD_FORWARD_TO_RENDERER;
+ return false;
}
if (overscroll_mode_ != OVERSCROLL_NONE) {
// Consume the event only if it updates the overscroll state.
if (ProcessEventForOverscroll(event))
- return CONSUMED;
+ return true;
}
- return SHOULD_FORWARD_TO_RENDERER;
+ return false;
}
void OverscrollController::ReceivedEventACK(const blink::WebInputEvent& event,
diff --git a/chromium/content/browser/renderer_host/overscroll_controller.h b/chromium/content/browser/renderer_host/overscroll_controller.h
index c68eb9b1573..5577ce97f3d 100644
--- a/chromium/content/browser/renderer_host/overscroll_controller.h
+++ b/chromium/content/browser/renderer_host/overscroll_controller.h
@@ -15,7 +15,7 @@ struct LatencyInfo;
namespace content {
-class MockRenderWidgetHost;
+class RenderWidgetHostViewAuraOverscrollTest;
class OverscrollControllerDelegate;
// Indicates the direction that the scroll is heading in relative to the screen,
@@ -38,18 +38,11 @@ class OverscrollController {
OverscrollController();
virtual ~OverscrollController();
- // The result of |DispatchEvent()|, indicating either how the event was
- // handled, or how it should be handled by the caller.
- enum Disposition {
- CONSUMED,
- SHOULD_FORWARD_TO_RENDERER,
- SHOULD_FORWARD_TO_GESTURE_FILTER
- };
// This must be called when dispatching any event from the
// RenderWidgetHostView so that the state of the overscroll gesture can be
- // updated properly.
- Disposition DispatchEvent(const blink::WebInputEvent& event,
- const ui::LatencyInfo& latency_info);
+ // updated properly. Returns true if the event was handled, in which case
+ // further processing should cease.
+ bool WillHandleEvent(const blink::WebInputEvent& event);
// This must be called when the ACK for any event comes in. This updates the
// overscroll gesture status as appropriate.
@@ -73,7 +66,7 @@ class OverscrollController {
void Cancel();
private:
- friend class MockRenderWidgetHost;
+ friend class RenderWidgetHostViewAuraOverscrollTest;
// Different scrolling states.
enum ScrollState {
diff --git a/chromium/content/browser/renderer_host/p2p/socket_dispatcher_host.cc b/chromium/content/browser/renderer_host/p2p/socket_dispatcher_host.cc
index 1608ae0f40c..bb8c1fb9610 100644
--- a/chromium/content/browser/renderer_host/p2p/socket_dispatcher_host.cc
+++ b/chromium/content/browser/renderer_host/p2p/socket_dispatcher_host.cc
@@ -26,7 +26,7 @@ const size_t kMaximumPacketSize = 32768;
class P2PSocketDispatcherHost::DnsRequest {
public:
- typedef base::Callback<void(const net::IPAddressNumber&)> DoneCallback;
+ typedef base::Callback<void(const net::IPAddressList&)> DoneCallback;
DnsRequest(int32 request_id, net::HostResolver* host_resolver)
: request_id_(request_id),
@@ -42,7 +42,8 @@ class P2PSocketDispatcherHost::DnsRequest {
// Return an error if it's an empty string.
if (host_name_.empty()) {
- done_callback_.Run(net::IPAddressNumber());
+ net::IPAddressList address_list;
+ done_callback_.Run(address_list);
return;
}
@@ -67,15 +68,20 @@ class P2PSocketDispatcherHost::DnsRequest {
private:
void OnDone(int result) {
+ net::IPAddressList list;
if (result != net::OK) {
LOG(ERROR) << "Failed to resolve address for " << host_name_
<< ", errorcode: " << result;
- done_callback_.Run(net::IPAddressNumber());
+ done_callback_.Run(list);
return;
}
DCHECK(!addresses_.empty());
- done_callback_.Run(addresses_.front().address());
+ for (net::AddressList::iterator iter = addresses_.begin();
+ iter != addresses_.end(); ++iter) {
+ list.push_back(iter->address());
+ }
+ done_callback_.Run(list);
}
int32 request_id_;
@@ -90,13 +96,16 @@ class P2PSocketDispatcherHost::DnsRequest {
P2PSocketDispatcherHost::P2PSocketDispatcherHost(
content::ResourceContext* resource_context,
net::URLRequestContextGetter* url_context)
- : resource_context_(resource_context),
+ : BrowserMessageFilter(P2PMsgStart),
+ resource_context_(resource_context),
url_context_(url_context),
- monitoring_networks_(false) {
+ monitoring_networks_(false),
+ dump_incoming_rtp_packet_(false),
+ dump_outgoing_rtp_packet_(false) {
}
void P2PSocketDispatcherHost::OnChannelClosing() {
- // Since the IPC channel is gone, close pending connections.
+ // Since the IPC sender is gone, close pending connections.
STLDeleteContainerPairSecondPointers(sockets_.begin(), sockets_.end());
sockets_.clear();
@@ -113,10 +122,9 @@ void P2PSocketDispatcherHost::OnDestruct() const {
BrowserThread::DeleteOnIOThread::Destruct(this);
}
-bool P2PSocketDispatcherHost::OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) {
+bool P2PSocketDispatcherHost::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(P2PSocketDispatcherHost, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(P2PSocketDispatcherHost, message)
IPC_MESSAGE_HANDLER(P2PHostMsg_StartNetworkNotifications,
OnStartNetworkNotifications)
IPC_MESSAGE_HANDLER(P2PHostMsg_StopNetworkNotifications,
@@ -126,9 +134,10 @@ bool P2PSocketDispatcherHost::OnMessageReceived(const IPC::Message& message,
IPC_MESSAGE_HANDLER(P2PHostMsg_AcceptIncomingTcpConnection,
OnAcceptIncomingTcpConnection)
IPC_MESSAGE_HANDLER(P2PHostMsg_Send, OnSend)
+ IPC_MESSAGE_HANDLER(P2PHostMsg_SetOption, OnSetOption)
IPC_MESSAGE_HANDLER(P2PHostMsg_DestroySocket, OnDestroySocket)
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return handled;
}
@@ -139,6 +148,38 @@ void P2PSocketDispatcherHost::OnIPAddressChanged() {
&P2PSocketDispatcherHost::DoGetNetworkList, this));
}
+void P2PSocketDispatcherHost::StartRtpDump(
+ bool incoming,
+ bool outgoing,
+ const RenderProcessHost::WebRtcRtpPacketCallback& packet_callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ if ((!dump_incoming_rtp_packet_ && incoming) ||
+ (!dump_outgoing_rtp_packet_ && outgoing)) {
+ if (incoming)
+ dump_incoming_rtp_packet_ = true;
+
+ if (outgoing)
+ dump_outgoing_rtp_packet_ = true;
+
+ packet_callback_ = packet_callback;
+ for (SocketsMap::iterator it = sockets_.begin(); it != sockets_.end(); ++it)
+ it->second->StartRtpDump(incoming, outgoing, packet_callback);
+ }
+}
+
+void P2PSocketDispatcherHost::StopRtpDumpOnUIThread(bool incoming,
+ bool outgoing) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&P2PSocketDispatcherHost::StopRtpDumpOnIOThread,
+ this,
+ incoming,
+ outgoing));
+}
+
P2PSocketDispatcherHost::~P2PSocketDispatcherHost() {
DCHECK(sockets_.empty());
DCHECK(dns_requests_.empty());
@@ -152,8 +193,7 @@ P2PSocketHost* P2PSocketDispatcherHost::LookupSocket(int socket_id) {
return (it == sockets_.end()) ? NULL : it->second;
}
-void P2PSocketDispatcherHost::OnStartNetworkNotifications(
- const IPC::Message& msg) {
+void P2PSocketDispatcherHost::OnStartNetworkNotifications() {
if (!monitoring_networks_) {
net::NetworkChangeNotifier::AddIPAddressObserver(this);
monitoring_networks_ = true;
@@ -164,8 +204,7 @@ void P2PSocketDispatcherHost::OnStartNetworkNotifications(
&P2PSocketDispatcherHost::DoGetNetworkList, this));
}
-void P2PSocketDispatcherHost::OnStopNetworkNotifications(
- const IPC::Message& msg) {
+void P2PSocketDispatcherHost::OnStopNetworkNotifications() {
if (monitoring_networks_) {
net::NetworkChangeNotifier::RemoveIPAddressObserver(this);
monitoring_networks_ = false;
@@ -185,7 +224,7 @@ void P2PSocketDispatcherHost::OnGetHostAddress(const std::string& host_name,
void P2PSocketDispatcherHost::OnCreateSocket(
P2PSocketType type, int socket_id,
const net::IPEndPoint& local_address,
- const net::IPEndPoint& remote_address) {
+ const P2PHostAndIPEndPoint& remote_address) {
if (LookupSocket(socket_id)) {
LOG(ERROR) << "Received P2PHostMsg_CreateSocket for socket "
"that already exists.";
@@ -202,6 +241,12 @@ void P2PSocketDispatcherHost::OnCreateSocket(
if (socket->Init(local_address, remote_address)) {
sockets_[socket_id] = socket.release();
+
+ if (dump_incoming_rtp_packet_ || dump_outgoing_rtp_packet_) {
+ sockets_[socket_id]->StartRtpDump(dump_incoming_rtp_packet_,
+ dump_outgoing_rtp_packet_,
+ packet_callback_);
+ }
}
}
@@ -224,7 +269,7 @@ void P2PSocketDispatcherHost::OnAcceptIncomingTcpConnection(
void P2PSocketDispatcherHost::OnSend(int socket_id,
const net::IPEndPoint& socket_address,
const std::vector<char>& data,
- net::DiffServCodePoint dscp,
+ const talk_base::PacketOptions& options,
uint64 packet_id) {
P2PSocketHost* socket = LookupSocket(socket_id);
if (!socket) {
@@ -241,7 +286,19 @@ void P2PSocketDispatcherHost::OnSend(int socket_id,
return;
}
- socket->Send(socket_address, data, dscp, packet_id);
+ socket->Send(socket_address, data, options, packet_id);
+}
+
+void P2PSocketDispatcherHost::OnSetOption(int socket_id,
+ P2PSocketOption option,
+ int value) {
+ P2PSocketHost* socket = LookupSocket(socket_id);
+ if (!socket) {
+ LOG(ERROR) << "Received P2PHostMsg_SetOption for invalid socket_id.";
+ return;
+ }
+
+ socket->SetOption(option, value);
}
void P2PSocketDispatcherHost::OnDestroySocket(int socket_id) {
@@ -256,7 +313,8 @@ void P2PSocketDispatcherHost::OnDestroySocket(int socket_id) {
void P2PSocketDispatcherHost::DoGetNetworkList() {
net::NetworkInterfaceList list;
- net::GetNetworkList(&list);
+ net::GetNetworkList(&list, net::EXCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES |
+ net::INCLUDE_ONLY_TEMP_IPV6_ADDRESS_IF_POSSIBLE);
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE, base::Bind(
&P2PSocketDispatcherHost::SendNetworkList, this, list));
@@ -269,11 +327,29 @@ void P2PSocketDispatcherHost::SendNetworkList(
void P2PSocketDispatcherHost::OnAddressResolved(
DnsRequest* request,
- const net::IPAddressNumber& result) {
- Send(new P2PMsg_GetHostAddressResult(request->request_id(), result));
+ const net::IPAddressList& addresses) {
+ Send(new P2PMsg_GetHostAddressResult(request->request_id(), addresses));
dns_requests_.erase(request);
delete request;
}
+void P2PSocketDispatcherHost::StopRtpDumpOnIOThread(bool incoming,
+ bool outgoing) {
+ if ((dump_incoming_rtp_packet_ && incoming) ||
+ (dump_outgoing_rtp_packet_ && outgoing)) {
+ if (incoming)
+ dump_incoming_rtp_packet_ = false;
+
+ if (outgoing)
+ dump_outgoing_rtp_packet_ = false;
+
+ if (!dump_incoming_rtp_packet_ && !dump_outgoing_rtp_packet_)
+ packet_callback_.Reset();
+
+ for (SocketsMap::iterator it = sockets_.begin(); it != sockets_.end(); ++it)
+ it->second->StopRtpDump(incoming, outgoing);
+ }
+}
+
} // namespace content
diff --git a/chromium/content/browser/renderer_host/p2p/socket_dispatcher_host.h b/chromium/content/browser/renderer_host/p2p/socket_dispatcher_host.h
index 813da993b9a..21376bdd320 100644
--- a/chromium/content/browser/renderer_host/p2p/socket_dispatcher_host.h
+++ b/chromium/content/browser/renderer_host/p2p/socket_dispatcher_host.h
@@ -11,9 +11,10 @@
#include <vector>
#include "content/browser/renderer_host/p2p/socket_host_throttler.h"
+#include "content/common/p2p_socket_type.h"
#include "content/public/browser/browser_message_filter.h"
#include "content/public/browser/browser_thread.h"
-#include "content/public/common/p2p_socket_type.h"
+#include "content/public/browser/render_process_host.h"
#include "net/base/ip_endpoint.h"
#include "net/base/network_change_notifier.h"
@@ -21,6 +22,10 @@ namespace net {
class URLRequestContextGetter;
}
+namespace talk_base {
+struct PacketOptions;
+}
+
namespace content {
class P2PSocketHost;
@@ -36,12 +41,20 @@ class P2PSocketDispatcherHost
// content::BrowserMessageFilter overrides.
virtual void OnChannelClosing() OVERRIDE;
virtual void OnDestruct() const OVERRIDE;
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
// net::NetworkChangeNotifier::IPAddressObserver interface.
virtual void OnIPAddressChanged() OVERRIDE;
+ // Starts the RTP packet header dumping. Must be called on the IO thread.
+ void StartRtpDump(
+ bool incoming,
+ bool outgoing,
+ const RenderProcessHost::WebRtcRtpPacketCallback& packet_callback);
+
+ // Stops the RTP packet header dumping. Must be Called on the UI thread.
+ void StopRtpDumpOnUIThread(bool incoming, bool outgoing);
+
protected:
virtual ~P2PSocketDispatcherHost();
@@ -56,31 +69,33 @@ class P2PSocketDispatcherHost
P2PSocketHost* LookupSocket(int socket_id);
// Handlers for the messages coming from the renderer.
- void OnStartNetworkNotifications(const IPC::Message& msg);
- void OnStopNetworkNotifications(const IPC::Message& msg);
-
+ void OnStartNetworkNotifications();
+ void OnStopNetworkNotifications();
void OnGetHostAddress(const std::string& host_name,
int32 request_id);
void OnCreateSocket(P2PSocketType type,
int socket_id,
const net::IPEndPoint& local_address,
- const net::IPEndPoint& remote_address);
+ const P2PHostAndIPEndPoint& remote_address);
void OnAcceptIncomingTcpConnection(int listen_socket_id,
const net::IPEndPoint& remote_address,
int connected_socket_id);
void OnSend(int socket_id,
const net::IPEndPoint& socket_address,
const std::vector<char>& data,
- net::DiffServCodePoint dscp,
+ const talk_base::PacketOptions& options,
uint64 packet_id);
+ void OnSetOption(int socket_id, P2PSocketOption option, int value);
void OnDestroySocket(int socket_id);
void DoGetNetworkList();
void SendNetworkList(const net::NetworkInterfaceList& list);
void OnAddressResolved(DnsRequest* request,
- const net::IPAddressNumber& result);
+ const net::IPAddressList& addresses);
+
+ void StopRtpDumpOnIOThread(bool incoming, bool outgoing);
content::ResourceContext* resource_context_;
scoped_refptr<net::URLRequestContextGetter> url_context_;
@@ -92,6 +107,10 @@ class P2PSocketDispatcherHost
std::set<DnsRequest*> dns_requests_;
P2PMessageThrottler throttler_;
+ bool dump_incoming_rtp_packet_;
+ bool dump_outgoing_rtp_packet_;
+ RenderProcessHost::WebRtcRtpPacketCallback packet_callback_;
+
DISALLOW_COPY_AND_ASSIGN(P2PSocketDispatcherHost);
};
diff --git a/chromium/content/browser/renderer_host/p2p/socket_host.cc b/chromium/content/browser/renderer_host/p2p/socket_host.cc
index 4b45e99adad..007ab77a200 100644
--- a/chromium/content/browser/renderer_host/p2p/socket_host.cc
+++ b/chromium/content/browser/renderer_host/p2p/socket_host.cc
@@ -8,18 +8,407 @@
#include "content/browser/renderer_host/p2p/socket_host_tcp.h"
#include "content/browser/renderer_host/p2p/socket_host_tcp_server.h"
#include "content/browser/renderer_host/p2p/socket_host_udp.h"
+#include "content/browser/renderer_host/render_process_host_impl.h"
+#include "content/public/browser/browser_thread.h"
+#include "crypto/hmac.h"
+#include "third_party/libjingle/source/talk/base/asyncpacketsocket.h"
+#include "third_party/libjingle/source/talk/base/byteorder.h"
+#include "third_party/libjingle/source/talk/base/messagedigest.h"
+#include "third_party/libjingle/source/talk/p2p/base/stun.h"
namespace {
+
const uint32 kStunMagicCookie = 0x2112A442;
+const int kMinRtpHdrLen = 12;
+const int kRtpExtnHdrLen = 4;
+const int kDtlsRecordHeaderLen = 13;
+const int kTurnChannelHdrLen = 4;
+const int kAbsSendTimeExtnLen = 3;
+const int kOneByteHdrLen = 1;
+
+// Fake auth tag written by the render process if external authentication is
+// enabled. HMAC in packet will be compared against this value before updating
+// packet with actual HMAC value.
+static const unsigned char kFakeAuthTag[10] = {
+ 0xba, 0xdd, 0xba, 0xdd, 0xba, 0xdd, 0xba, 0xdd, 0xba, 0xdd
+};
+
+bool IsTurnChannelData(const char* data) {
+ return ((*data & 0xC0) == 0x40);
+}
+
+bool IsDtlsPacket(const char* data, int len) {
+ const uint8* u = reinterpret_cast<const uint8*>(data);
+ return (len >= kDtlsRecordHeaderLen && (u[0] > 19 && u[0] < 64));
+}
+
+bool IsRtcpPacket(const char* data) {
+ int type = (static_cast<uint8>(data[1]) & 0x7F);
+ return (type >= 64 && type < 96);
+}
+
+bool IsTurnSendIndicationPacket(const char* data) {
+ uint16 type = talk_base::GetBE16(data);
+ return (type == cricket::TURN_SEND_INDICATION);
+}
+
+bool IsRtpPacket(const char* data, int len) {
+ return ((*data & 0xC0) == 0x80);
+}
+
+// Verifies rtp header and message length.
+bool ValidateRtpHeader(const char* rtp, int length, size_t* header_length) {
+ if (header_length)
+ *header_length = 0;
+
+ int cc_count = rtp[0] & 0x0F;
+ int rtp_hdr_len_without_extn = kMinRtpHdrLen + 4 * cc_count;
+ if (rtp_hdr_len_without_extn > length) {
+ return false;
+ }
+
+ // If extension bit is not set, we are done with header processing, as input
+ // length is verified above.
+ if (!(rtp[0] & 0x10)) {
+ if (header_length)
+ *header_length = rtp_hdr_len_without_extn;
+
+ return true;
+ }
+
+ rtp += rtp_hdr_len_without_extn;
+
+ // Getting extension profile length.
+ // Length is in 32 bit words.
+ uint16 extn_length = talk_base::GetBE16(rtp + 2) * 4;
+
+ // Verify input length against total header size.
+ if (rtp_hdr_len_without_extn + kRtpExtnHdrLen + extn_length > length) {
+ return false;
+ }
+
+ if (header_length)
+ *header_length = rtp_hdr_len_without_extn + kRtpExtnHdrLen + extn_length;
+ return true;
+}
+
+void UpdateAbsSendTimeExtnValue(char* extn_data, int len,
+ uint32 abs_send_time) {
+ // Absolute send time in RTP streams.
+ //
+ // The absolute send time is signaled to the receiver in-band using the
+ // general mechanism for RTP header extensions [RFC5285]. The payload
+ // of this extension (the transmitted value) is a 24-bit unsigned integer
+ // containing the sender's current time in seconds as a fixed point number
+ // with 18 bits fractional part.
+ //
+ // The form of the absolute send time extension block:
+ //
+ // 0 1 2 3
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | ID | len=2 | absolute send time |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ DCHECK_EQ(len, kAbsSendTimeExtnLen);
+ // Now() has resolution ~1-15ms, using HighResNow(). But it is warned not to
+ // use it unless necessary, as it is expensive than Now().
+ uint32 now_second = abs_send_time;
+ if (!now_second) {
+ uint64 now_us =
+ (base::TimeTicks::HighResNow() - base::TimeTicks()).InMicroseconds();
+ // Convert second to 24-bit unsigned with 18 bit fractional part
+ now_second =
+ ((now_us << 18) / base::Time::kMicrosecondsPerSecond) & 0x00FFFFFF;
+ }
+ // TODO(mallinath) - Add SetBE24 to byteorder.h in libjingle.
+ extn_data[0] = static_cast<uint8>(now_second >> 16);
+ extn_data[1] = static_cast<uint8>(now_second >> 8);
+ extn_data[2] = static_cast<uint8>(now_second);
+}
+
+// Assumes |len| is actual packet length + tag length. Updates HMAC at end of
+// the RTP packet.
+void UpdateRtpAuthTag(char* rtp, int len,
+ const talk_base::PacketOptions& options) {
+ // If there is no key, return.
+ if (options.packet_time_params.srtp_auth_key.empty())
+ return;
+
+ size_t tag_length = options.packet_time_params.srtp_auth_tag_len;
+ char* auth_tag = rtp + (len - tag_length);
+
+ // We should have a fake HMAC value @ auth_tag.
+ DCHECK_EQ(0, memcmp(auth_tag, kFakeAuthTag, tag_length));
+
+ crypto::HMAC hmac(crypto::HMAC::SHA1);
+ if (!hmac.Init(reinterpret_cast<const unsigned char*>(
+ &options.packet_time_params.srtp_auth_key[0]),
+ options.packet_time_params.srtp_auth_key.size())) {
+ NOTREACHED();
+ return;
+ }
+
+ if (hmac.DigestLength() < tag_length) {
+ NOTREACHED();
+ return;
+ }
+
+ // Copy ROC after end of rtp packet.
+ memcpy(auth_tag, &options.packet_time_params.srtp_packet_index, 4);
+ // Authentication of a RTP packet will have RTP packet + ROC size.
+ int auth_required_length = len - tag_length + 4;
+
+ unsigned char output[64];
+ if (!hmac.Sign(base::StringPiece(rtp, auth_required_length),
+ output, sizeof(output))) {
+ NOTREACHED();
+ return;
+ }
+ // Copy HMAC from output to packet. This is required as auth tag length
+ // may not be equal to the actual HMAC length.
+ memcpy(auth_tag, output, tag_length);
+}
+
} // namespace
namespace content {
-P2PSocketHost::P2PSocketHost(IPC::Sender* message_sender,
- int id)
+namespace packet_processing_helpers {
+
+bool ApplyPacketOptions(char* data, int length,
+ const talk_base::PacketOptions& options,
+ uint32 abs_send_time) {
+ DCHECK(data != NULL);
+ DCHECK(length > 0);
+ // if there is no valid |rtp_sendtime_extension_id| and |srtp_auth_key| in
+ // PacketOptions, nothing to be updated in this packet.
+ if (options.packet_time_params.rtp_sendtime_extension_id == -1 &&
+ options.packet_time_params.srtp_auth_key.empty()) {
+ return true;
+ }
+
+ DCHECK(!IsDtlsPacket(data, length));
+ DCHECK(!IsRtcpPacket(data));
+
+ // If there is a srtp auth key present then packet must be a RTP packet.
+ // RTP packet may have been wrapped in a TURN Channel Data or
+ // TURN send indication.
+ int rtp_start_pos;
+ int rtp_length;
+ if (!GetRtpPacketStartPositionAndLength(
+ data, length, &rtp_start_pos, &rtp_length)) {
+ // This method should never return false.
+ NOTREACHED();
+ return false;
+ }
+
+ // Skip to rtp packet.
+ char* start = data + rtp_start_pos;
+ // If packet option has non default value (-1) for sendtime extension id,
+ // then we should parse the rtp packet to update the timestamp. Otherwise
+ // just calculate HMAC and update packet with it.
+ if (options.packet_time_params.rtp_sendtime_extension_id != -1) {
+ UpdateRtpAbsSendTimeExtn(
+ start, rtp_length,
+ options.packet_time_params.rtp_sendtime_extension_id, abs_send_time);
+ }
+
+ UpdateRtpAuthTag(start, rtp_length, options);
+ return true;
+}
+
+bool GetRtpPacketStartPositionAndLength(const char* packet,
+ int length,
+ int* rtp_start_pos,
+ int* rtp_packet_length) {
+ int rtp_begin, rtp_length;
+ if (IsTurnChannelData(packet)) {
+ // Turn Channel Message header format.
+ // 0 1 2 3
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | Channel Number | Length |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | |
+ // / Application Data /
+ // / /
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ if (length < kTurnChannelHdrLen) {
+ return false;
+ }
+
+ rtp_begin = kTurnChannelHdrLen;
+ rtp_length = talk_base::GetBE16(&packet[2]);
+ if (length < rtp_length + kTurnChannelHdrLen) {
+ return false;
+ }
+ } else if (IsTurnSendIndicationPacket(packet)) {
+ if (length <= P2PSocketHost::kStunHeaderSize) {
+ // Message must be greater than 20 bytes, if it's carrying any payload.
+ return false;
+ }
+ // Validate STUN message length.
+ int stun_msg_len = talk_base::GetBE16(&packet[2]);
+ if (stun_msg_len + P2PSocketHost::kStunHeaderSize != length) {
+ return false;
+ }
+
+ // First skip mandatory stun header which is of 20 bytes.
+ rtp_begin = P2PSocketHost::kStunHeaderSize;
+ // Loop through STUN attributes until we find STUN DATA attribute.
+ const char* start = packet + rtp_begin;
+ bool data_attr_present = false;
+ while ((packet + rtp_begin) - start < stun_msg_len) {
+ // Keep reading STUN attributes until we hit DATA attribute.
+ // Attribute will be a TLV structure.
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | Type | Length |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | Value (variable) ....
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // The value in the length field MUST contain the length of the Value
+ // part of the attribute, prior to padding, measured in bytes. Since
+ // STUN aligns attributes on 32-bit boundaries, attributes whose content
+ // is not a multiple of 4 bytes are padded with 1, 2, or 3 bytes of
+ // padding so that its value contains a multiple of 4 bytes. The
+ // padding bits are ignored, and may be any value.
+ uint16 attr_type, attr_length;
+ // Getting attribute type and length.
+ attr_type = talk_base::GetBE16(&packet[rtp_begin]);
+ attr_length = talk_base::GetBE16(
+ &packet[rtp_begin + sizeof(attr_type)]);
+ // Checking for bogus attribute length.
+ if (length < attr_length + rtp_begin) {
+ return false;
+ }
+
+ if (attr_type != cricket::STUN_ATTR_DATA) {
+ rtp_begin += sizeof(attr_type) + sizeof(attr_length) + attr_length;
+ if ((attr_length % 4) != 0) {
+ rtp_begin += (4 - (attr_length % 4));
+ }
+ continue;
+ }
+
+ data_attr_present = true;
+ rtp_begin += 4; // Skip STUN_DATA_ATTR header.
+ rtp_length = attr_length;
+ // One final check of length before exiting.
+ if (length < rtp_length + rtp_begin) {
+ return false;
+ }
+ // We found STUN_DATA_ATTR. We can skip parsing rest of the packet.
+ break;
+ }
+
+ if (!data_attr_present) {
+ // There is no data attribute present in the message. We can't do anything
+ // with the message.
+ return false;
+ }
+
+ } else {
+ // This is a raw RTP packet.
+ rtp_begin = 0;
+ rtp_length = length;
+ }
+
+ // Making sure we have a valid RTP packet at the end.
+ if (!(rtp_length < kMinRtpHdrLen) &&
+ IsRtpPacket(packet + rtp_begin, rtp_length) &&
+ ValidateRtpHeader(packet + rtp_begin, rtp_length, NULL)) {
+ *rtp_start_pos = rtp_begin;
+ *rtp_packet_length = rtp_length;
+ return true;
+ }
+ return false;
+}
+
+// ValidateRtpHeader must be called before this method to make sure, we have
+// a sane rtp packet.
+bool UpdateRtpAbsSendTimeExtn(char* rtp, int length,
+ int extension_id, uint32 abs_send_time) {
+ // 0 1 2 3
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // |V=2|P|X| CC |M| PT | sequence number |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | timestamp |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | synchronization source (SSRC) identifier |
+ // +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ // | contributing source (CSRC) identifiers |
+ // | .... |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ // Return if extension bit is not set.
+ if (!(rtp[0] & 0x10)) {
+ return true;
+ }
+
+ int cc_count = rtp[0] & 0x0F;
+ int rtp_hdr_len_without_extn = kMinRtpHdrLen + 4 * cc_count;
+
+ rtp += rtp_hdr_len_without_extn;
+
+ // Getting extension profile ID and length.
+ uint16 profile_id = talk_base::GetBE16(rtp);
+ // Length is in 32 bit words.
+ uint16 extn_length = talk_base::GetBE16(rtp + 2) * 4;
+
+ rtp += kRtpExtnHdrLen; // Moving past extn header.
+
+ bool found = false;
+ // WebRTC is using one byte header extension.
+ // TODO(mallinath) - Handle two byte header extension.
+ if (profile_id == 0xBEDE) { // OneByte extension header
+ // 0
+ // 0 1 2 3 4 5 6 7
+ // +-+-+-+-+-+-+-+-+
+ // | ID | len |
+ // +-+-+-+-+-+-+-+-+
+
+ // 0 1 2 3
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | 0xBE | 0xDE | length=3 |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | ID | L=0 | data | ID | L=1 | data...
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // ...data | 0 (pad) | 0 (pad) | ID | L=3 |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | data |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ char* extn_start = rtp;
+ while (rtp - extn_start < extn_length) {
+ const int id = (*rtp & 0xF0) >> 4;
+ const int len = (*rtp & 0x0F) + 1;
+ // The 4-bit length is the number minus one of data bytes of this header
+ // extension element following the one-byte header.
+ if (id == extension_id) {
+ UpdateAbsSendTimeExtnValue(rtp + kOneByteHdrLen, len, abs_send_time);
+ found = true;
+ break;
+ }
+ rtp += kOneByteHdrLen + len;
+ // Counting padding bytes.
+ while ((*rtp == 0) && (rtp - extn_start < extn_length)) {
+ ++rtp;
+ }
+ }
+ }
+ return found;
+}
+
+} // packet_processing_helpers
+
+P2PSocketHost::P2PSocketHost(IPC::Sender* message_sender, int socket_id)
: message_sender_(message_sender),
- id_(id),
- state_(STATE_UNINITIALIZED) {
+ id_(socket_id),
+ state_(STATE_UNINITIALIZED),
+ dump_incoming_rtp_packet_(false),
+ dump_outgoing_rtp_packet_(false),
+ weak_ptr_factory_(this) {
}
P2PSocketHost::~P2PSocketHost() { }
@@ -72,34 +461,125 @@ bool P2PSocketHost::IsRequestOrResponse(StunMessageType type) {
}
// static
-P2PSocketHost* P2PSocketHost::Create(
- IPC::Sender* message_sender, int id, P2PSocketType type,
- net::URLRequestContextGetter* url_context,
- P2PMessageThrottler* throttler) {
+P2PSocketHost* P2PSocketHost::Create(IPC::Sender* message_sender,
+ int socket_id,
+ P2PSocketType type,
+ net::URLRequestContextGetter* url_context,
+ P2PMessageThrottler* throttler) {
switch (type) {
case P2P_SOCKET_UDP:
- return new P2PSocketHostUdp(message_sender, id, throttler);
+ return new P2PSocketHostUdp(message_sender, socket_id, throttler);
case P2P_SOCKET_TCP_SERVER:
return new P2PSocketHostTcpServer(
- message_sender, id, P2P_SOCKET_TCP_CLIENT);
+ message_sender, socket_id, P2P_SOCKET_TCP_CLIENT);
case P2P_SOCKET_STUN_TCP_SERVER:
return new P2PSocketHostTcpServer(
- message_sender, id, P2P_SOCKET_STUN_TCP_CLIENT);
+ message_sender, socket_id, P2P_SOCKET_STUN_TCP_CLIENT);
case P2P_SOCKET_TCP_CLIENT:
case P2P_SOCKET_SSLTCP_CLIENT:
case P2P_SOCKET_TLS_CLIENT:
- return new P2PSocketHostTcp(message_sender, id, type, url_context);
+ return new P2PSocketHostTcp(message_sender, socket_id, type, url_context);
case P2P_SOCKET_STUN_TCP_CLIENT:
case P2P_SOCKET_STUN_SSLTCP_CLIENT:
case P2P_SOCKET_STUN_TLS_CLIENT:
- return new P2PSocketHostStunTcp(message_sender, id, type, url_context);
+ return new P2PSocketHostStunTcp(
+ message_sender, socket_id, type, url_context);
}
NOTREACHED();
return NULL;
}
+void P2PSocketHost::StartRtpDump(
+ bool incoming,
+ bool outgoing,
+ const RenderProcessHost::WebRtcRtpPacketCallback& packet_callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK(!packet_callback.is_null());
+ DCHECK(incoming || outgoing);
+
+ if (incoming)
+ dump_incoming_rtp_packet_ = true;
+
+ if (outgoing)
+ dump_outgoing_rtp_packet_ = true;
+
+ packet_dump_callback_ = packet_callback;
+}
+
+void P2PSocketHost::StopRtpDump(bool incoming, bool outgoing) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK(incoming || outgoing);
+
+ if (incoming)
+ dump_incoming_rtp_packet_ = false;
+
+ if (outgoing)
+ dump_outgoing_rtp_packet_ = false;
+
+ if (!dump_incoming_rtp_packet_ && !dump_outgoing_rtp_packet_)
+ packet_dump_callback_.Reset();
+}
+
+void P2PSocketHost::DumpRtpPacket(const char* packet,
+ size_t length,
+ bool incoming) {
+ if (IsDtlsPacket(packet, length) || IsRtcpPacket(packet))
+ return;
+
+ int rtp_packet_pos = 0;
+ int rtp_packet_length = length;
+ if (!packet_processing_helpers::GetRtpPacketStartPositionAndLength(
+ packet, length, &rtp_packet_pos, &rtp_packet_length))
+ return;
+
+ packet += rtp_packet_pos;
+
+ size_t header_length = 0;
+ bool valid = ValidateRtpHeader(packet, rtp_packet_length, &header_length);
+ if (!valid) {
+ DCHECK(false);
+ return;
+ }
+
+ scoped_ptr<uint8[]> header_buffer(new uint8[header_length]);
+ memcpy(header_buffer.get(), packet, header_length);
+
+ // Posts to the IO thread as the data members should be accessed on the IO
+ // thread only.
+ BrowserThread::PostTask(BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&P2PSocketHost::DumpRtpPacketOnIOThread,
+ weak_ptr_factory_.GetWeakPtr(),
+ Passed(&header_buffer),
+ header_length,
+ rtp_packet_length,
+ incoming));
+}
+
+void P2PSocketHost::DumpRtpPacketOnIOThread(scoped_ptr<uint8[]> packet_header,
+ size_t header_length,
+ size_t packet_length,
+ bool incoming) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ if ((incoming && !dump_incoming_rtp_packet_) ||
+ (!incoming && !dump_outgoing_rtp_packet_) ||
+ packet_dump_callback_.is_null()) {
+ return;
+ }
+
+ // |packet_dump_callback_| must be called on the UI thread.
+ BrowserThread::PostTask(BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(packet_dump_callback_,
+ Passed(&packet_header),
+ header_length,
+ packet_length,
+ incoming));
+}
+
} // namespace content
diff --git a/chromium/content/browser/renderer_host/p2p/socket_host.h b/chromium/content/browser/renderer_host/p2p/socket_host.h
index a11488451af..bfcbbd229f1 100644
--- a/chromium/content/browser/renderer_host/p2p/socket_host.h
+++ b/chromium/content/browser/renderer_host/p2p/socket_host.h
@@ -5,8 +5,10 @@
#ifndef CONTENT_BROWSER_RENDERER_HOST_P2P_SOCKET_HOST_H_
#define CONTENT_BROWSER_RENDERER_HOST_P2P_SOCKET_HOST_H_
+#include "base/memory/weak_ptr.h"
#include "content/common/content_export.h"
-#include "content/public/common/p2p_socket_type.h"
+#include "content/common/p2p_socket_type.h"
+#include "content/public/browser/render_process_host.h"
#include "net/base/ip_endpoint.h"
#include "net/udp/datagram_socket.h"
@@ -18,16 +20,44 @@ namespace net {
class URLRequestContextGetter;
}
+namespace talk_base {
+struct PacketOptions;
+}
+
namespace content {
class P2PMessageThrottler;
+namespace packet_processing_helpers {
+
+// This method can handle only RTP packet, otherwise this method must not be
+// called. It will try to do, 1. update absolute send time extension header
+// if present with current time and 2. update HMAC in RTP packet.
+// If abs_send_time is 0, ApplyPacketOption will get current time from system.
+CONTENT_EXPORT bool ApplyPacketOptions(char* data, int length,
+ const talk_base::PacketOptions& options,
+ uint32 abs_send_time);
+
+// Helper method which finds RTP ofset and length if the packet is encapsulated
+// in a TURN Channel Message or TURN Send Indication message.
+CONTENT_EXPORT bool GetRtpPacketStartPositionAndLength(const char* data,
+ int length,
+ int* rtp_start_pos,
+ int* rtp_packet_length);
+// Helper method which updates absoulute send time extension if present.
+CONTENT_EXPORT bool UpdateRtpAbsSendTimeExtn(char* rtp, int length,
+ int extension_id,
+ uint32 abs_send_time);
+
+} // packet_processing_helpers
+
// Base class for P2P sockets.
class CONTENT_EXPORT P2PSocketHost {
public:
static const int kStunHeaderSize = 20;
// Creates P2PSocketHost of the specific type.
static P2PSocketHost* Create(IPC::Sender* message_sender,
- int id, P2PSocketType type,
+ int socket_id,
+ P2PSocketType type,
net::URLRequestContextGetter* url_context,
P2PMessageThrottler* throttler);
@@ -35,17 +65,25 @@ class CONTENT_EXPORT P2PSocketHost {
// Initalizes the socket. Returns false when initiazations fails.
virtual bool Init(const net::IPEndPoint& local_address,
- const net::IPEndPoint& remote_address) = 0;
+ const P2PHostAndIPEndPoint& remote_address) = 0;
// Sends |data| on the socket to |to|.
virtual void Send(const net::IPEndPoint& to,
const std::vector<char>& data,
- net::DiffServCodePoint dscp,
+ const talk_base::PacketOptions& options,
uint64 packet_id) = 0;
virtual P2PSocketHost* AcceptIncomingTcpConnection(
const net::IPEndPoint& remote_address, int id) = 0;
+ virtual bool SetOption(P2PSocketOption option, int value) = 0;
+
+ void StartRtpDump(
+ bool incoming,
+ bool outgoing,
+ const RenderProcessHost::WebRtcRtpPacketCallback& packet_callback);
+ void StopRtpDump(bool incoming, bool outgoing);
+
protected:
friend class P2PSocketHostTcpTestBase;
@@ -83,7 +121,7 @@ class CONTENT_EXPORT P2PSocketHost {
STATE_ERROR,
};
- P2PSocketHost(IPC::Sender* message_sender, int id);
+ P2PSocketHost(IPC::Sender* message_sender, int socket_id);
// Verifies that the packet |data| has a valid STUN header. In case
// of success stores type of the message in |type|.
@@ -91,9 +129,23 @@ class CONTENT_EXPORT P2PSocketHost {
StunMessageType* type);
static bool IsRequestOrResponse(StunMessageType type);
+ // Calls |packet_dump_callback_| to record the RTP header.
+ void DumpRtpPacket(const char* packet, size_t length, bool incoming);
+
+ // A helper to dump the packet on the IO thread.
+ void DumpRtpPacketOnIOThread(scoped_ptr<uint8[]> packet_header,
+ size_t header_length,
+ size_t packet_length,
+ bool incoming);
+
IPC::Sender* message_sender_;
int id_;
State state_;
+ bool dump_incoming_rtp_packet_;
+ bool dump_outgoing_rtp_packet_;
+ RenderProcessHost::WebRtcRtpPacketCallback packet_dump_callback_;
+
+ base::WeakPtrFactory<P2PSocketHost> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(P2PSocketHost);
};
diff --git a/chromium/content/browser/renderer_host/p2p/socket_host_tcp.cc b/chromium/content/browser/renderer_host/p2p/socket_host_tcp.cc
index e367e7f3b53..978f6269f5d 100644
--- a/chromium/content/browser/renderer_host/p2p/socket_host_tcp.cc
+++ b/chromium/content/browser/renderer_host/p2p/socket_host_tcp.cc
@@ -18,6 +18,7 @@
#include "net/socket/tcp_client_socket.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
+#include "third_party/libjingle/source/talk/base/asyncpacketsocket.h"
namespace {
@@ -44,9 +45,11 @@ bool IsPseudoTlsClientSocket(content::P2PSocketType type) {
namespace content {
P2PSocketHostTcpBase::P2PSocketHostTcpBase(
- IPC::Sender* message_sender, int id,
- P2PSocketType type, net::URLRequestContextGetter* url_context)
- : P2PSocketHost(message_sender, id),
+ IPC::Sender* message_sender,
+ int socket_id,
+ P2PSocketType type,
+ net::URLRequestContextGetter* url_context)
+ : P2PSocketHost(message_sender, socket_id),
write_pending_(false),
connected_(false),
type_(type),
@@ -65,7 +68,7 @@ bool P2PSocketHostTcpBase::InitAccepted(const net::IPEndPoint& remote_address,
DCHECK(socket);
DCHECK_EQ(state_, STATE_UNINITIALIZED);
- remote_address_ = remote_address;
+ remote_address_.ip_address = remote_address;
// TODO(ronghuawu): Add FakeSSLServerSocket.
socket_.reset(socket);
state_ = STATE_OPEN;
@@ -74,14 +77,14 @@ bool P2PSocketHostTcpBase::InitAccepted(const net::IPEndPoint& remote_address,
}
bool P2PSocketHostTcpBase::Init(const net::IPEndPoint& local_address,
- const net::IPEndPoint& remote_address) {
+ const P2PHostAndIPEndPoint& remote_address) {
DCHECK_EQ(state_, STATE_UNINITIALIZED);
remote_address_ = remote_address;
state_ = STATE_CONNECTING;
net::HostPortPair dest_host_port_pair =
- net::HostPortPair::FromIPEndPoint(remote_address);
+ net::HostPortPair::FromIPEndPoint(remote_address.ip_address);
// TODO(mallinath) - We are ignoring local_address altogether. We should
// find a way to inject this into ProxyResolvingClientSocket. This could be
// a problem on multi-homed host.
@@ -173,7 +176,10 @@ void P2PSocketHostTcpBase::StartTls() {
// Default ssl config.
const net::SSLConfig ssl_config;
net::HostPortPair dest_host_port_pair =
- net::HostPortPair::FromIPEndPoint(remote_address_);
+ net::HostPortPair::FromIPEndPoint(remote_address_.ip_address);
+ if (!remote_address_.hostname.empty())
+ dest_host_port_pair.set_host(remote_address_.hostname);
+
net::ClientSocketFactory* socket_factory =
net::ClientSocketFactory::GetDefaultFactory();
DCHECK(socket_factory);
@@ -201,12 +207,12 @@ void P2PSocketHostTcpBase::ProcessTlsSslConnectDone(int status) {
void P2PSocketHostTcpBase::OnOpen() {
state_ = STATE_OPEN;
// Setting socket send and receive buffer size.
- if (!socket_->SetReceiveBufferSize(kRecvSocketBufferSize)) {
+ if (net::OK != socket_->SetReceiveBufferSize(kRecvSocketBufferSize)) {
LOG(WARNING) << "Failed to set socket receive buffer size to "
<< kRecvSocketBufferSize;
}
- if (!socket_->SetSendBufferSize(kSendSocketBufferSize)) {
+ if (net::OK != socket_->SetSendBufferSize(kSendSocketBufferSize)) {
LOG(WARNING) << "Failed to set socket send buffer size to "
<< kSendSocketBufferSize;
}
@@ -273,7 +279,7 @@ void P2PSocketHostTcpBase::OnPacket(const std::vector<char>& data) {
connected_ = true;
} else if (!stun || type == STUN_DATA_INDICATION) {
LOG(ERROR) << "Received unexpected data packet from "
- << remote_address_.ToString()
+ << remote_address_.ip_address.ToString()
<< " before STUN binding is finished. "
<< "Terminating connection.";
OnError();
@@ -282,14 +288,17 @@ void P2PSocketHostTcpBase::OnPacket(const std::vector<char>& data) {
}
message_sender_->Send(new P2PMsg_OnDataReceived(
- id_, remote_address_, data, base::TimeTicks::Now()));
+ id_, remote_address_.ip_address, data, base::TimeTicks::Now()));
+
+ if (dump_incoming_rtp_packet_)
+ DumpRtpPacket(&data[0], data.size(), true);
}
// Note: dscp is not actually used on TCP sockets as this point,
// but may be honored in the future.
void P2PSocketHostTcpBase::Send(const net::IPEndPoint& to,
const std::vector<char>& data,
- net::DiffServCodePoint dscp,
+ const talk_base::PacketOptions& options,
uint64 packet_id) {
if (!socket_) {
// The Send message may be sent after the an OnError message was
@@ -297,7 +306,7 @@ void P2PSocketHostTcpBase::Send(const net::IPEndPoint& to,
return;
}
- if (!(to == remote_address_)) {
+ if (!(to == remote_address_.ip_address)) {
// Renderer should use this socket only to send data to |remote_address_|.
NOTREACHED();
OnError();
@@ -315,7 +324,7 @@ void P2PSocketHostTcpBase::Send(const net::IPEndPoint& to,
}
}
- DoSend(to, data);
+ DoSend(to, data, options);
}
void P2PSocketHostTcpBase::WriteOrQueue(
@@ -404,10 +413,26 @@ void P2PSocketHostTcpBase::DidCompleteRead(int result) {
}
}
-P2PSocketHostTcp::P2PSocketHostTcp(
- IPC::Sender* message_sender, int id,
- P2PSocketType type, net::URLRequestContextGetter* url_context)
- : P2PSocketHostTcpBase(message_sender, id, type, url_context) {
+bool P2PSocketHostTcpBase::SetOption(P2PSocketOption option, int value) {
+ DCHECK_EQ(STATE_OPEN, state_);
+ switch (option) {
+ case P2P_SOCKET_OPT_RCVBUF:
+ return socket_->SetReceiveBufferSize(value) == net::OK;
+ case P2P_SOCKET_OPT_SNDBUF:
+ return socket_->SetSendBufferSize(value) == net::OK;
+ case P2P_SOCKET_OPT_DSCP:
+ return false; // For TCP sockets DSCP setting is not available.
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+P2PSocketHostTcp::P2PSocketHostTcp(IPC::Sender* message_sender,
+ int socket_id,
+ P2PSocketType type,
+ net::URLRequestContextGetter* url_context)
+ : P2PSocketHostTcpBase(message_sender, socket_id, type, url_context) {
DCHECK(type == P2P_SOCKET_TCP_CLIENT ||
type == P2P_SOCKET_SSLTCP_CLIENT ||
type == P2P_SOCKET_TLS_CLIENT);
@@ -432,21 +457,29 @@ int P2PSocketHostTcp::ProcessInput(char* input, int input_len) {
}
void P2PSocketHostTcp::DoSend(const net::IPEndPoint& to,
- const std::vector<char>& data) {
+ const std::vector<char>& data,
+ const talk_base::PacketOptions& options) {
int size = kPacketHeaderSize + data.size();
scoped_refptr<net::DrainableIOBuffer> buffer =
new net::DrainableIOBuffer(new net::IOBuffer(size), size);
*reinterpret_cast<uint16*>(buffer->data()) = base::HostToNet16(data.size());
memcpy(buffer->data() + kPacketHeaderSize, &data[0], data.size());
+ packet_processing_helpers::ApplyPacketOptions(
+ buffer->data() + kPacketHeaderSize,
+ buffer->BytesRemaining() - kPacketHeaderSize,
+ options, 0);
+
WriteOrQueue(buffer);
}
// P2PSocketHostStunTcp
P2PSocketHostStunTcp::P2PSocketHostStunTcp(
- IPC::Sender* message_sender, int id,
- P2PSocketType type, net::URLRequestContextGetter* url_context)
- : P2PSocketHostTcpBase(message_sender, id, type, url_context) {
+ IPC::Sender* message_sender,
+ int socket_id,
+ P2PSocketType type,
+ net::URLRequestContextGetter* url_context)
+ : P2PSocketHostTcpBase(message_sender, socket_id, type, url_context) {
DCHECK(type == P2P_SOCKET_STUN_TCP_CLIENT ||
type == P2P_SOCKET_STUN_SSLTCP_CLIENT ||
type == P2P_SOCKET_STUN_TLS_CLIENT);
@@ -477,7 +510,8 @@ int P2PSocketHostStunTcp::ProcessInput(char* input, int input_len) {
}
void P2PSocketHostStunTcp::DoSend(const net::IPEndPoint& to,
- const std::vector<char>& data) {
+ const std::vector<char>& data,
+ const talk_base::PacketOptions& options) {
// Each packet is expected to have header (STUN/TURN ChannelData), where
// header contains message type and and length of message.
if (data.size() < kPacketHeaderSize + kPacketLengthOffset) {
@@ -504,12 +538,18 @@ void P2PSocketHostStunTcp::DoSend(const net::IPEndPoint& to,
new net::DrainableIOBuffer(new net::IOBuffer(size), size);
memcpy(buffer->data(), &data[0], data.size());
+ packet_processing_helpers::ApplyPacketOptions(
+ buffer->data(), data.size(), options, 0);
+
if (pad_bytes) {
char padding[4] = {0};
DCHECK_LE(pad_bytes, 4);
memcpy(buffer->data() + data.size(), padding, pad_bytes);
}
WriteOrQueue(buffer);
+
+ if (dump_outgoing_rtp_packet_)
+ DumpRtpPacket(buffer->data(), data.size(), false);
}
int P2PSocketHostStunTcp::GetExpectedPacketSize(
diff --git a/chromium/content/browser/renderer_host/p2p/socket_host_tcp.h b/chromium/content/browser/renderer_host/p2p/socket_host_tcp.h
index b84efc7c282..f5ff8633b06 100644
--- a/chromium/content/browser/renderer_host/p2p/socket_host_tcp.h
+++ b/chromium/content/browser/renderer_host/p2p/socket_host_tcp.h
@@ -13,7 +13,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "content/browser/renderer_host/p2p/socket_host.h"
-#include "content/public/common/p2p_socket_type.h"
+#include "content/common/p2p_socket_type.h"
#include "net/base/completion_callback.h"
#include "net/base/ip_endpoint.h"
@@ -29,7 +29,7 @@ namespace content {
class CONTENT_EXPORT P2PSocketHostTcpBase : public P2PSocketHost {
public:
P2PSocketHostTcpBase(IPC::Sender* message_sender,
- int id,
+ int socket_id,
P2PSocketType type,
net::URLRequestContextGetter* url_context);
virtual ~P2PSocketHostTcpBase();
@@ -39,19 +39,21 @@ class CONTENT_EXPORT P2PSocketHostTcpBase : public P2PSocketHost {
// P2PSocketHost overrides.
virtual bool Init(const net::IPEndPoint& local_address,
- const net::IPEndPoint& remote_address) OVERRIDE;
+ const P2PHostAndIPEndPoint& remote_address) OVERRIDE;
virtual void Send(const net::IPEndPoint& to,
const std::vector<char>& data,
- net::DiffServCodePoint dscp,
+ const talk_base::PacketOptions& options,
uint64 packet_id) OVERRIDE;
virtual P2PSocketHost* AcceptIncomingTcpConnection(
const net::IPEndPoint& remote_address, int id) OVERRIDE;
+ virtual bool SetOption(P2PSocketOption option, int value) OVERRIDE;
protected:
// Derived classes will provide the implementation.
virtual int ProcessInput(char* input, int input_len) = 0;
virtual void DoSend(const net::IPEndPoint& to,
- const std::vector<char>& data) = 0;
+ const std::vector<char>& data,
+ const talk_base::PacketOptions& options) = 0;
void WriteOrQueue(scoped_refptr<net::DrainableIOBuffer>& buffer);
void OnPacket(const std::vector<char>& data);
@@ -80,7 +82,7 @@ class CONTENT_EXPORT P2PSocketHostTcpBase : public P2PSocketHost {
void OnOpen();
void DoSendSocketCreateMsg();
- net::IPEndPoint remote_address_;
+ P2PHostAndIPEndPoint remote_address_;
scoped_ptr<net::StreamSocket> socket_;
scoped_refptr<net::GrowableIOBuffer> read_buffer_;
@@ -99,7 +101,7 @@ class CONTENT_EXPORT P2PSocketHostTcpBase : public P2PSocketHost {
class CONTENT_EXPORT P2PSocketHostTcp : public P2PSocketHostTcpBase {
public:
P2PSocketHostTcp(IPC::Sender* message_sender,
- int id,
+ int socket_id,
P2PSocketType type,
net::URLRequestContextGetter* url_context);
virtual ~P2PSocketHostTcp();
@@ -107,7 +109,8 @@ class CONTENT_EXPORT P2PSocketHostTcp : public P2PSocketHostTcpBase {
protected:
virtual int ProcessInput(char* input, int input_len) OVERRIDE;
virtual void DoSend(const net::IPEndPoint& to,
- const std::vector<char>& data) OVERRIDE;
+ const std::vector<char>& data,
+ const talk_base::PacketOptions& options) OVERRIDE;
private:
DISALLOW_COPY_AND_ASSIGN(P2PSocketHostTcp);
};
@@ -119,7 +122,7 @@ class CONTENT_EXPORT P2PSocketHostTcp : public P2PSocketHostTcpBase {
class CONTENT_EXPORT P2PSocketHostStunTcp : public P2PSocketHostTcpBase {
public:
P2PSocketHostStunTcp(IPC::Sender* message_sender,
- int id,
+ int socket_id,
P2PSocketType type,
net::URLRequestContextGetter* url_context);
@@ -128,7 +131,8 @@ class CONTENT_EXPORT P2PSocketHostStunTcp : public P2PSocketHostTcpBase {
protected:
virtual int ProcessInput(char* input, int input_len) OVERRIDE;
virtual void DoSend(const net::IPEndPoint& to,
- const std::vector<char>& data) OVERRIDE;
+ const std::vector<char>& data,
+ const talk_base::PacketOptions& options) OVERRIDE;
private:
int GetExpectedPacketSize(const char* data, int len, int* pad_bytes);
diff --git a/chromium/content/browser/renderer_host/p2p/socket_host_tcp_server.cc b/chromium/content/browser/renderer_host/p2p/socket_host_tcp_server.cc
index fc6c7f6c729..2418de9e87c 100644
--- a/chromium/content/browser/renderer_host/p2p/socket_host_tcp_server.cc
+++ b/chromium/content/browser/renderer_host/p2p/socket_host_tcp_server.cc
@@ -20,14 +20,14 @@ const int kListenBacklog = 5;
namespace content {
-P2PSocketHostTcpServer::P2PSocketHostTcpServer(
- IPC::Sender* message_sender, int id, P2PSocketType client_type)
- : P2PSocketHost(message_sender, id),
+P2PSocketHostTcpServer::P2PSocketHostTcpServer(IPC::Sender* message_sender,
+ int socket_id,
+ P2PSocketType client_type)
+ : P2PSocketHost(message_sender, socket_id),
client_type_(client_type),
socket_(new net::TCPServerSocket(NULL, net::NetLog::Source())),
- accept_callback_(
- base::Bind(&P2PSocketHostTcpServer::OnAccepted,
- base::Unretained(this))) {
+ accept_callback_(base::Bind(&P2PSocketHostTcpServer::OnAccepted,
+ base::Unretained(this))) {
}
P2PSocketHostTcpServer::~P2PSocketHostTcpServer() {
@@ -41,7 +41,7 @@ P2PSocketHostTcpServer::~P2PSocketHostTcpServer() {
}
bool P2PSocketHostTcpServer::Init(const net::IPEndPoint& local_address,
- const net::IPEndPoint& remote_address) {
+ const P2PHostAndIPEndPoint& remote_address) {
DCHECK_EQ(state_, STATE_UNINITIALIZED);
int result = socket_->Listen(local_address, kListenBacklog);
@@ -116,7 +116,7 @@ void P2PSocketHostTcpServer::OnAccepted(int result) {
void P2PSocketHostTcpServer::Send(const net::IPEndPoint& to,
const std::vector<char>& data,
- net::DiffServCodePoint dscp,
+ const talk_base::PacketOptions& options,
uint64 packet_id) {
NOTREACHED();
OnError();
@@ -135,12 +135,18 @@ P2PSocketHost* P2PSocketHostTcpServer::AcceptIncomingTcpConnection(
if (client_type_ == P2P_SOCKET_TCP_CLIENT) {
result.reset(new P2PSocketHostTcp(message_sender_, id, client_type_, NULL));
} else {
- result.reset(new P2PSocketHostStunTcp(
- message_sender_, id, client_type_, NULL));
+ result.reset(
+ new P2PSocketHostStunTcp(message_sender_, id, client_type_, NULL));
}
if (!result->InitAccepted(remote_address, socket))
return NULL;
return result.release();
}
+bool P2PSocketHostTcpServer::SetOption(P2PSocketOption option,
+ int value) {
+ // Currently we don't have use case tcp server sockets are used for p2p.
+ return false;
+}
+
} // namespace content
diff --git a/chromium/content/browser/renderer_host/p2p/socket_host_tcp_server.h b/chromium/content/browser/renderer_host/p2p/socket_host_tcp_server.h
index 2581e83ee7e..e050b00fa06 100644
--- a/chromium/content/browser/renderer_host/p2p/socket_host_tcp_server.h
+++ b/chromium/content/browser/renderer_host/p2p/socket_host_tcp_server.h
@@ -13,7 +13,7 @@
#include "base/message_loop/message_loop.h"
#include "content/browser/renderer_host/p2p/socket_host.h"
#include "content/common/content_export.h"
-#include "content/public/common/p2p_socket_type.h"
+#include "content/common/p2p_socket_type.h"
#include "ipc/ipc_sender.h"
#include "net/base/completion_callback.h"
#include "net/socket/tcp_server_socket.h"
@@ -28,19 +28,22 @@ class CONTENT_EXPORT P2PSocketHostTcpServer : public P2PSocketHost {
public:
typedef std::map<net::IPEndPoint, net::StreamSocket*> AcceptedSocketsMap;
- P2PSocketHostTcpServer(IPC::Sender* message_sender, int id,
+ P2PSocketHostTcpServer(IPC::Sender* message_sender,
+ int socket_id,
P2PSocketType client_type);
virtual ~P2PSocketHostTcpServer();
// P2PSocketHost overrides.
virtual bool Init(const net::IPEndPoint& local_address,
- const net::IPEndPoint& remote_address) OVERRIDE;
+ const P2PHostAndIPEndPoint& remote_address) OVERRIDE;
virtual void Send(const net::IPEndPoint& to,
const std::vector<char>& data,
- net::DiffServCodePoint dscp,
+ const talk_base::PacketOptions& options,
uint64 packet_id) OVERRIDE;
virtual P2PSocketHost* AcceptIncomingTcpConnection(
const net::IPEndPoint& remote_address, int id) OVERRIDE;
+ virtual bool SetOption(P2PSocketOption option, int value) OVERRIDE;
+
private:
friend class P2PSocketHostTcpServerTest;
diff --git a/chromium/content/browser/renderer_host/p2p/socket_host_tcp_server_unittest.cc b/chromium/content/browser/renderer_host/p2p/socket_host_tcp_server_unittest.cc
index 39b864edb63..270fad90cb1 100644
--- a/chromium/content/browser/renderer_host/p2p/socket_host_tcp_server_unittest.cc
+++ b/chromium/content/browser/renderer_host/p2p/socket_host_tcp_server_unittest.cc
@@ -91,16 +91,19 @@ class P2PSocketHostTcpServerTest : public testing::Test {
protected:
virtual void SetUp() OVERRIDE {
socket_ = new FakeServerSocket();
- socket_host_.reset(new P2PSocketHostTcpServer(
- &sender_, 0, P2P_SOCKET_TCP_CLIENT));
+ socket_host_.reset(
+ new P2PSocketHostTcpServer(&sender_, 0, P2P_SOCKET_TCP_CLIENT));
socket_host_->socket_.reset(socket_);
EXPECT_CALL(sender_, Send(
MatchMessage(static_cast<uint32>(P2PMsg_OnSocketCreated::ID))))
.WillOnce(DoAll(DeleteArg<0>(), Return(true)));
+ P2PHostAndIPEndPoint dest;
+ dest.ip_address = ParseAddress(kTestIpAddress1, kTestPort1);
+
socket_host_->Init(ParseAddress(kTestLocalIpAddress, 0),
- ParseAddress(kTestIpAddress1, kTestPort1));
+ dest);
EXPECT_TRUE(socket_->listening());
}
diff --git a/chromium/content/browser/renderer_host/p2p/socket_host_tcp_unittest.cc b/chromium/content/browser/renderer_host/p2p/socket_host_tcp_unittest.cc
index cae88bd1c70..24b77c3adbe 100644
--- a/chromium/content/browser/renderer_host/p2p/socket_host_tcp_unittest.cc
+++ b/chromium/content/browser/renderer_host/p2p/socket_host_tcp_unittest.cc
@@ -31,18 +31,18 @@ class P2PSocketHostTcpTestBase : public testing::Test {
.WillOnce(DoAll(DeleteArg<0>(), Return(true)));
if (socket_type_ == P2P_SOCKET_TCP_CLIENT) {
- socket_host_.reset(new P2PSocketHostTcp(
- &sender_, 0, P2P_SOCKET_TCP_CLIENT, NULL));
+ socket_host_.reset(
+ new P2PSocketHostTcp(&sender_, 0, P2P_SOCKET_TCP_CLIENT, NULL));
} else {
socket_host_.reset(new P2PSocketHostStunTcp(
- &sender_, 0, P2P_SOCKET_STUN_TCP_CLIENT, NULL));
+ &sender_, 0, P2P_SOCKET_STUN_TCP_CLIENT, NULL));
}
socket_ = new FakeSocket(&sent_data_);
socket_->SetLocalAddress(ParseAddress(kTestLocalIpAddress, kTestPort1));
socket_host_->socket_.reset(socket_);
- dest_ = ParseAddress(kTestIpAddress1, kTestPort1);
+ dest_.ip_address = ParseAddress(kTestIpAddress1, kTestPort1);
local_address_ = ParseAddress(kTestLocalIpAddress, kTestPort1);
@@ -65,10 +65,7 @@ class P2PSocketHostTcpTestBase : public testing::Test {
MockIPCSender sender_;
net::IPEndPoint local_address_;
-
- net::IPEndPoint dest_;
- net::IPEndPoint dest2_;
-
+ P2PHostAndIPEndPoint dest_;
P2PSocketType socket_type_;
};
@@ -92,17 +89,18 @@ TEST_F(P2PSocketHostTcpTest, SendStunNoAuth) {
.Times(3)
.WillRepeatedly(DoAll(DeleteArg<0>(), Return(true)));
+ talk_base::PacketOptions options;
std::vector<char> packet1;
CreateStunRequest(&packet1);
- socket_host_->Send(dest_, packet1, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest_.ip_address, packet1, options, 0);
std::vector<char> packet2;
CreateStunResponse(&packet2);
- socket_host_->Send(dest_, packet2, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest_.ip_address, packet2, options, 0);
std::vector<char> packet3;
CreateStunError(&packet3);
- socket_host_->Send(dest_, packet3, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest_.ip_address, packet3, options, 0);
std::string expected_data;
expected_data.append(IntToSize(packet1.size()));
@@ -123,17 +121,18 @@ TEST_F(P2PSocketHostTcpTest, ReceiveStun) {
.Times(3)
.WillRepeatedly(DoAll(DeleteArg<0>(), Return(true)));
+ talk_base::PacketOptions options;
std::vector<char> packet1;
CreateStunRequest(&packet1);
- socket_host_->Send(dest_, packet1, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest_.ip_address, packet1, options, 0);
std::vector<char> packet2;
CreateStunResponse(&packet2);
- socket_host_->Send(dest_, packet2, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest_.ip_address, packet2, options, 0);
std::vector<char> packet3;
CreateStunError(&packet3);
- socket_host_->Send(dest_, packet3, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest_.ip_address, packet3, options, 0);
std::string received_data;
received_data.append(IntToSize(packet1.size()));
@@ -169,9 +168,10 @@ TEST_F(P2PSocketHostTcpTest, SendDataNoAuth) {
MatchMessage(static_cast<uint32>(P2PMsg_OnError::ID))))
.WillOnce(DoAll(DeleteArg<0>(), Return(true)));
+ talk_base::PacketOptions options;
std::vector<char> packet;
CreateRandomPacket(&packet);
- socket_host_->Send(dest_, packet, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest_.ip_address, packet, options, 0);
EXPECT_EQ(0U, sent_data_.size());
}
@@ -194,10 +194,11 @@ TEST_F(P2PSocketHostTcpTest, SendAfterStunRequest) {
.WillOnce(DoAll(DeleteArg<0>(), Return(true)));
socket_->AppendInputData(&received_data[0], received_data.size());
+ talk_base::PacketOptions options;
// Now we should be able to send any data to |dest_|.
std::vector<char> packet;
CreateRandomPacket(&packet);
- socket_host_->Send(dest_, packet, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest_.ip_address, packet, options, 0);
std::string expected_data;
expected_data.append(IntToSize(packet.size()));
@@ -217,14 +218,15 @@ TEST_F(P2PSocketHostTcpTest, AsyncWrites) {
.Times(2)
.WillRepeatedly(DoAll(DeleteArg<0>(), Return(true)));
+ talk_base::PacketOptions options;
std::vector<char> packet1;
CreateStunRequest(&packet1);
- socket_host_->Send(dest_, packet1, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest_.ip_address, packet1, options, 0);
std::vector<char> packet2;
CreateStunResponse(&packet2);
- socket_host_->Send(dest_, packet2, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest_.ip_address, packet2, options, 0);
message_loop.RunUntilIdle();
@@ -237,6 +239,37 @@ TEST_F(P2PSocketHostTcpTest, AsyncWrites) {
EXPECT_EQ(expected_data, sent_data_);
}
+TEST_F(P2PSocketHostTcpTest, SendDataWithPacketOptions) {
+ std::vector<char> request_packet;
+ CreateStunRequest(&request_packet);
+
+ std::string received_data;
+ received_data.append(IntToSize(request_packet.size()));
+ received_data.append(request_packet.begin(), request_packet.end());
+
+ EXPECT_CALL(sender_, Send(
+ MatchMessage(static_cast<uint32>(P2PMsg_OnSendComplete::ID))))
+ .WillOnce(DoAll(DeleteArg<0>(), Return(true)));
+ EXPECT_CALL(sender_, Send(MatchPacketMessage(request_packet)))
+ .WillOnce(DoAll(DeleteArg<0>(), Return(true)));
+ socket_->AppendInputData(&received_data[0], received_data.size());
+
+ talk_base::PacketOptions options;
+ options.packet_time_params.rtp_sendtime_extension_id = 3;
+ // Now we should be able to send any data to |dest_|.
+ std::vector<char> packet;
+ CreateRandomPacket(&packet);
+ // Make it a RTP packet.
+ *reinterpret_cast<uint16*>(&*packet.begin()) = base::HostToNet16(0x8000);
+ socket_host_->Send(dest_.ip_address, packet, options, 0);
+
+ std::string expected_data;
+ expected_data.append(IntToSize(packet.size()));
+ expected_data.append(packet.begin(), packet.end());
+
+ EXPECT_EQ(expected_data, sent_data_);
+}
+
// Verify that we can send STUN message and that they are formatted
// properly.
TEST_F(P2PSocketHostStunTcpTest, SendStunNoAuth) {
@@ -245,17 +278,18 @@ TEST_F(P2PSocketHostStunTcpTest, SendStunNoAuth) {
.Times(3)
.WillRepeatedly(DoAll(DeleteArg<0>(), Return(true)));
+ talk_base::PacketOptions options;
std::vector<char> packet1;
CreateStunRequest(&packet1);
- socket_host_->Send(dest_, packet1, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest_.ip_address, packet1, options, 0);
std::vector<char> packet2;
CreateStunResponse(&packet2);
- socket_host_->Send(dest_, packet2, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest_.ip_address, packet2, options, 0);
std::vector<char> packet3;
CreateStunError(&packet3);
- socket_host_->Send(dest_, packet3, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest_.ip_address, packet3, options, 0);
std::string expected_data;
expected_data.append(packet1.begin(), packet1.end());
@@ -273,17 +307,18 @@ TEST_F(P2PSocketHostStunTcpTest, ReceiveStun) {
.Times(3)
.WillRepeatedly(DoAll(DeleteArg<0>(), Return(true)));
+ talk_base::PacketOptions options;
std::vector<char> packet1;
CreateStunRequest(&packet1);
- socket_host_->Send(dest_, packet1, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest_.ip_address, packet1, options, 0);
std::vector<char> packet2;
CreateStunResponse(&packet2);
- socket_host_->Send(dest_, packet2, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest_.ip_address, packet2, options, 0);
std::vector<char> packet3;
CreateStunError(&packet3);
- socket_host_->Send(dest_, packet3, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest_.ip_address, packet3, options, 0);
std::string received_data;
received_data.append(packet1.begin(), packet1.end());
@@ -316,9 +351,10 @@ TEST_F(P2PSocketHostStunTcpTest, SendDataNoAuth) {
MatchMessage(static_cast<uint32>(P2PMsg_OnError::ID))))
.WillOnce(DoAll(DeleteArg<0>(), Return(true)));
+ talk_base::PacketOptions options;
std::vector<char> packet;
CreateRandomPacket(&packet);
- socket_host_->Send(dest_, packet, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest_.ip_address, packet, options, 0);
EXPECT_EQ(0U, sent_data_.size());
}
@@ -334,13 +370,14 @@ TEST_F(P2PSocketHostStunTcpTest, AsyncWrites) {
.Times(2)
.WillRepeatedly(DoAll(DeleteArg<0>(), Return(true)));
+ talk_base::PacketOptions options;
std::vector<char> packet1;
CreateStunRequest(&packet1);
- socket_host_->Send(dest_, packet1, net::DSCP_NO_CHANGE,0);
+ socket_host_->Send(dest_.ip_address, packet1, options, 0);
std::vector<char> packet2;
CreateStunResponse(&packet2);
- socket_host_->Send(dest_, packet2, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest_.ip_address, packet2, options, 0);
message_loop.RunUntilIdle();
diff --git a/chromium/content/browser/renderer_host/p2p/socket_host_test_utils.cc b/chromium/content/browser/renderer_host/p2p/socket_host_test_utils.cc
index 67c461a6b97..ba40aecf4a4 100644
--- a/chromium/content/browser/renderer_host/p2p/socket_host_test_utils.cc
+++ b/chromium/content/browser/renderer_host/p2p/socket_host_test_utils.cc
@@ -103,13 +103,14 @@ void FakeSocket::DoAsyncWrite(scoped_refptr<net::IOBuffer> buf, int buf_len,
callback.Run(buf_len);
}
-bool FakeSocket::SetReceiveBufferSize(int32 size) {
+int FakeSocket::SetReceiveBufferSize(int32 size) {
NOTIMPLEMENTED();
- return false;
+ return net::ERR_NOT_IMPLEMENTED;
}
-bool FakeSocket::SetSendBufferSize(int32 size) {
+
+int FakeSocket::SetSendBufferSize(int32 size) {
NOTIMPLEMENTED();
- return false;
+ return net::ERR_NOT_IMPLEMENTED;
}
int FakeSocket::Connect(const net::CompletionCallback& callback) {
diff --git a/chromium/content/browser/renderer_host/p2p/socket_host_test_utils.h b/chromium/content/browser/renderer_host/p2p/socket_host_test_utils.h
index 86a93a81e50..b27b55b9982 100644
--- a/chromium/content/browser/renderer_host/p2p/socket_host_test_utils.h
+++ b/chromium/content/browser/renderer_host/p2p/socket_host_test_utils.h
@@ -45,8 +45,8 @@ class FakeSocket : public net::StreamSocket {
const net::CompletionCallback& callback) OVERRIDE;
virtual int Write(net::IOBuffer* buf, int buf_len,
const net::CompletionCallback& callback) OVERRIDE;
- virtual bool SetReceiveBufferSize(int32 size) OVERRIDE;
- virtual bool SetSendBufferSize(int32 size) OVERRIDE;
+ virtual int SetReceiveBufferSize(int32 size) OVERRIDE;
+ virtual int SetSendBufferSize(int32 size) OVERRIDE;
virtual int Connect(const net::CompletionCallback& callback) OVERRIDE;
virtual void Disconnect() OVERRIDE;
virtual bool IsConnected() const OVERRIDE;
diff --git a/chromium/content/browser/renderer_host/p2p/socket_host_udp.cc b/chromium/content/browser/renderer_host/p2p/socket_host_udp.cc
index 8a14669a7ff..5dad882629f 100644
--- a/chromium/content/browser/renderer_host/p2p/socket_host_udp.cc
+++ b/chromium/content/browser/renderer_host/p2p/socket_host_udp.cc
@@ -5,16 +5,17 @@
#include "content/browser/renderer_host/p2p/socket_host_udp.h"
#include "base/bind.h"
-#include "base/command_line.h"
#include "base/debug/trace_event.h"
#include "base/stl_util.h"
#include "content/browser/renderer_host/p2p/socket_host_throttler.h"
#include "content/common/p2p_messages.h"
-#include "content/public/common/content_switches.h"
+#include "content/public/browser/content_browser_client.h"
+#include "content/public/common/content_client.h"
#include "ipc/ipc_sender.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/base/net_util.h"
+#include "third_party/libjingle/source/talk/base/asyncpacketsocket.h"
namespace {
@@ -40,12 +41,8 @@ bool IsTransientError(int error) {
error == net::ERR_ADDRESS_INVALID ||
error == net::ERR_ACCESS_DENIED ||
error == net::ERR_CONNECTION_RESET ||
- error == net::ERR_OUT_OF_MEMORY;
-}
-
-bool AllowUDPWithoutSTUN() {
- return CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kDisableP2PSocketSTUNFilter);
+ error == net::ERR_OUT_OF_MEMORY ||
+ error == net::ERR_INTERNET_DISCONNECTED;
}
} // namespace
@@ -55,12 +52,12 @@ namespace content {
P2PSocketHostUdp::PendingPacket::PendingPacket(
const net::IPEndPoint& to,
const std::vector<char>& content,
- net::DiffServCodePoint dscp_,
+ const talk_base::PacketOptions& options,
uint64 id)
: to(to),
data(new net::IOBuffer(content.size())),
size(content.size()),
- dscp(dscp_),
+ packet_options(options),
id(id) {
memcpy(data->data(), &content[0], size);
}
@@ -69,10 +66,12 @@ P2PSocketHostUdp::PendingPacket::~PendingPacket() {
}
P2PSocketHostUdp::P2PSocketHostUdp(IPC::Sender* message_sender,
- int id,
+ int socket_id,
P2PMessageThrottler* throttler)
- : P2PSocketHost(message_sender, id),
- socket_(new net::UDPServerSocket(NULL, net::NetLog::Source())),
+ : P2PSocketHost(message_sender, socket_id),
+ socket_(
+ new net::UDPServerSocket(GetContentClient()->browser()->GetNetLog(),
+ net::NetLog::Source())),
send_pending_(false),
last_dscp_(net::DSCP_CS0),
throttler_(throttler) {
@@ -86,7 +85,7 @@ P2PSocketHostUdp::~P2PSocketHostUdp() {
}
bool P2PSocketHostUdp::Init(const net::IPEndPoint& local_address,
- const net::IPEndPoint& remote_address) {
+ const P2PHostAndIPEndPoint& remote_address) {
DCHECK_EQ(state_, STATE_UNINITIALIZED);
int result = socket_->Listen(local_address);
@@ -97,7 +96,7 @@ bool P2PSocketHostUdp::Init(const net::IPEndPoint& local_address,
}
// Setting recv socket buffer size.
- if (!socket_->SetReceiveBufferSize(kRecvSocketBufferSize)) {
+ if (socket_->SetReceiveBufferSize(kRecvSocketBufferSize) != net::OK) {
LOG(WARNING) << "Failed to set socket receive buffer size to "
<< kRecvSocketBufferSize;
}
@@ -162,7 +161,7 @@ void P2PSocketHostUdp::HandleReadResult(int result) {
if (!ContainsKey(connected_peers_, recv_address_)) {
P2PSocketHost::StunMessageType type;
bool stun = GetStunPacketType(&*data.begin(), data.size(), &type);
- if ((stun && IsRequestOrResponse(type)) || AllowUDPWithoutSTUN()) {
+ if ((stun && IsRequestOrResponse(type))) {
connected_peers_.insert(recv_address_);
} else if (!stun || type == STUN_DATA_INDICATION) {
LOG(ERROR) << "Received unexpected data packet from "
@@ -174,6 +173,9 @@ void P2PSocketHostUdp::HandleReadResult(int result) {
message_sender_->Send(new P2PMsg_OnDataReceived(
id_, recv_address_, data, base::TimeTicks::Now()));
+
+ if (dump_incoming_rtp_packet_)
+ DumpRtpPacket(&data[0], data.size(), true);
} else if (result < 0 && !IsTransientError(result)) {
LOG(ERROR) << "Error when reading from UDP socket: " << result;
OnError();
@@ -182,7 +184,7 @@ void P2PSocketHostUdp::HandleReadResult(int result) {
void P2PSocketHostUdp::Send(const net::IPEndPoint& to,
const std::vector<char>& data,
- net::DiffServCodePoint dscp,
+ const talk_base::PacketOptions& options,
uint64 packet_id) {
if (!socket_) {
// The Send message may be sent after the an OnError message was
@@ -190,7 +192,7 @@ void P2PSocketHostUdp::Send(const net::IPEndPoint& to,
return;
}
- if (!ContainsKey(connected_peers_, to) && !AllowUDPWithoutSTUN()) {
+ if (!ContainsKey(connected_peers_, to)) {
P2PSocketHost::StunMessageType type = P2PSocketHost::StunMessageType();
bool stun = GetStunPacketType(&*data.begin(), data.size(), &type);
if (!stun || type == STUN_DATA_INDICATION) {
@@ -208,9 +210,10 @@ void P2PSocketHostUdp::Send(const net::IPEndPoint& to,
}
if (send_pending_) {
- send_queue_.push_back(PendingPacket(to, data, dscp, packet_id));
+ send_queue_.push_back(PendingPacket(to, data, options, packet_id));
} else {
- PendingPacket packet(to, data, dscp, packet_id);
+ // TODO(mallinath: Remove unnecessary memcpy in this case.
+ PendingPacket packet(to, data, options, packet_id);
DoSend(packet);
}
}
@@ -222,11 +225,13 @@ void P2PSocketHostUdp::DoSend(const PendingPacket& packet) {
// 1. If the outgoing packet is set to DSCP_NO_CHANGE
// 2. If no change in DSCP value from last packet
// 3. If there is any error in setting DSCP on socket.
- if (packet.dscp != net::DSCP_NO_CHANGE &&
- last_dscp_ != packet.dscp && last_dscp_ != net::DSCP_NO_CHANGE) {
- int result = socket_->SetDiffServCodePoint(packet.dscp);
+ net::DiffServCodePoint dscp =
+ static_cast<net::DiffServCodePoint>(packet.packet_options.dscp);
+ if (dscp != net::DSCP_NO_CHANGE && last_dscp_ != dscp &&
+ last_dscp_ != net::DSCP_NO_CHANGE) {
+ int result = socket_->SetDiffServCodePoint(dscp);
if (result == net::OK) {
- last_dscp_ = packet.dscp;
+ last_dscp_ = dscp;
} else if (!IsTransientError(result) && last_dscp_ != net::DSCP_CS0) {
// We receieved a non-transient error, and it seems we have
// not changed the DSCP in the past, disable DSCP as it unlikely
@@ -234,6 +239,8 @@ void P2PSocketHostUdp::DoSend(const PendingPacket& packet) {
last_dscp_ = net::DSCP_NO_CHANGE;
}
}
+ packet_processing_helpers::ApplyPacketOptions(
+ packet.data->data(), packet.size, packet.packet_options, 0);
int result = socket_->SendTo(
packet.data.get(),
packet.size,
@@ -257,6 +264,9 @@ void P2PSocketHostUdp::DoSend(const PendingPacket& packet) {
} else {
HandleSendResult(packet.id, result);
}
+
+ if (dump_outgoing_rtp_packet_)
+ DumpRtpPacket(packet.data->data(), packet.size, false);
}
void P2PSocketHostUdp::OnSend(uint64 packet_id, int result) {
@@ -277,15 +287,16 @@ void P2PSocketHostUdp::OnSend(uint64 packet_id, int result) {
void P2PSocketHostUdp::HandleSendResult(uint64 packet_id, int result) {
TRACE_EVENT_ASYNC_END1("p2p", "Send", packet_id,
"result", result);
- if (result > 0) {
- message_sender_->Send(new P2PMsg_OnSendComplete(id_));
- } else if (IsTransientError(result)) {
+ if (result < 0) {
+ if (!IsTransientError(result)) {
+ LOG(ERROR) << "Error when sending data in UDP socket: " << result;
+ OnError();
+ return;
+ }
VLOG(0) << "sendto() has failed twice returning a "
- " transient error. Dropping the packet.";
- } else if (result < 0) {
- LOG(ERROR) << "Error when sending data in UDP socket: " << result;
- OnError();
+ " transient error. Dropping the packet.";
}
+ message_sender_->Send(new P2PMsg_OnSendComplete(id_));
}
P2PSocketHost* P2PSocketHostUdp::AcceptIncomingTcpConnection(
@@ -295,4 +306,20 @@ P2PSocketHost* P2PSocketHostUdp::AcceptIncomingTcpConnection(
return NULL;
}
+bool P2PSocketHostUdp::SetOption(P2PSocketOption option, int value) {
+ DCHECK_EQ(STATE_OPEN, state_);
+ switch (option) {
+ case P2P_SOCKET_OPT_RCVBUF:
+ return socket_->SetReceiveBufferSize(value) == net::OK;
+ case P2P_SOCKET_OPT_SNDBUF:
+ return socket_->SetSendBufferSize(value) == net::OK;
+ case P2P_SOCKET_OPT_DSCP:
+ return (net::OK == socket_->SetDiffServCodePoint(
+ static_cast<net::DiffServCodePoint>(value))) ? true : false;
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
} // namespace content
diff --git a/chromium/content/browser/renderer_host/p2p/socket_host_udp.h b/chromium/content/browser/renderer_host/p2p/socket_host_udp.h
index 96b22444829..b22795c059d 100644
--- a/chromium/content/browser/renderer_host/p2p/socket_host_udp.h
+++ b/chromium/content/browser/renderer_host/p2p/socket_host_udp.h
@@ -15,9 +15,10 @@
#include "base/message_loop/message_loop.h"
#include "content/browser/renderer_host/p2p/socket_host.h"
#include "content/common/content_export.h"
-#include "content/public/common/p2p_socket_type.h"
+#include "content/common/p2p_socket_type.h"
#include "net/base/ip_endpoint.h"
#include "net/udp/udp_server_socket.h"
+#include "third_party/libjingle/source/talk/base/asyncpacketsocket.h"
namespace content {
@@ -25,19 +26,21 @@ class P2PMessageThrottler;
class CONTENT_EXPORT P2PSocketHostUdp : public P2PSocketHost {
public:
- P2PSocketHostUdp(IPC::Sender* message_sender, int id,
+ P2PSocketHostUdp(IPC::Sender* message_sender,
+ int socket_id,
P2PMessageThrottler* throttler);
virtual ~P2PSocketHostUdp();
// P2PSocketHost overrides.
virtual bool Init(const net::IPEndPoint& local_address,
- const net::IPEndPoint& remote_address) OVERRIDE;
+ const P2PHostAndIPEndPoint& remote_address) OVERRIDE;
virtual void Send(const net::IPEndPoint& to,
const std::vector<char>& data,
- net::DiffServCodePoint dscp,
+ const talk_base::PacketOptions& options,
uint64 packet_id) OVERRIDE;
virtual P2PSocketHost* AcceptIncomingTcpConnection(
const net::IPEndPoint& remote_address, int id) OVERRIDE;
+ virtual bool SetOption(P2PSocketOption option, int value) OVERRIDE;
private:
friend class P2PSocketHostUdpTest;
@@ -47,13 +50,13 @@ class CONTENT_EXPORT P2PSocketHostUdp : public P2PSocketHost {
struct PendingPacket {
PendingPacket(const net::IPEndPoint& to,
const std::vector<char>& content,
- net::DiffServCodePoint dscp,
+ const talk_base::PacketOptions& options,
uint64 id);
~PendingPacket();
net::IPEndPoint to;
scoped_refptr<net::IOBuffer> data;
int size;
- net::DiffServCodePoint dscp;
+ talk_base::PacketOptions packet_options;
uint64 id;
};
diff --git a/chromium/content/browser/renderer_host/p2p/socket_host_udp_unittest.cc b/chromium/content/browser/renderer_host/p2p/socket_host_udp_unittest.cc
index 3ccacfc2889..6235cb07d9f 100644
--- a/chromium/content/browser/renderer_host/p2p/socket_host_udp_unittest.cc
+++ b/chromium/content/browser/renderer_host/p2p/socket_host_udp_unittest.cc
@@ -94,12 +94,12 @@ class FakeDatagramServerSocket : public net::DatagramServerSocket {
return buf_len;
}
- virtual bool SetReceiveBufferSize(int32 size) OVERRIDE {
- return true;
+ virtual int SetReceiveBufferSize(int32 size) OVERRIDE {
+ return net::OK;
}
- virtual bool SetSendBufferSize(int32 size) OVERRIDE {
- return true;
+ virtual int SetSendBufferSize(int32 size) OVERRIDE {
+ return net::OK;
}
void ReceivePacket(const net::IPEndPoint& address, std::vector<char> data) {
@@ -160,6 +160,10 @@ class FakeDatagramServerSocket : public net::DatagramServerSocket {
return net::ERR_NOT_IMPLEMENTED;
}
+ virtual void DetachFromThread() OVERRIDE {
+ NOTIMPLEMENTED();
+ }
+
private:
net::IPEndPoint address_;
std::deque<UDPPacket>* sent_packets_;
@@ -188,7 +192,7 @@ class P2PSocketHostUdpTest : public testing::Test {
socket_host_->socket_.reset(socket_);
local_address_ = ParseAddress(kTestLocalIpAddress, kTestPort1);
- socket_host_->Init(local_address_, net::IPEndPoint());
+ socket_host_->Init(local_address_, P2PHostAndIPEndPoint());
dest1_ = ParseAddress(kTestIpAddress1, kTestPort1);
dest2_ = ParseAddress(kTestIpAddress2, kTestPort2);
@@ -217,17 +221,18 @@ TEST_F(P2PSocketHostUdpTest, SendStunNoAuth) {
.Times(3)
.WillRepeatedly(DoAll(DeleteArg<0>(), Return(true)));
+ talk_base::PacketOptions options;
std::vector<char> packet1;
CreateStunRequest(&packet1);
- socket_host_->Send(dest1_, packet1, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest1_, packet1, options, 0);
std::vector<char> packet2;
CreateStunResponse(&packet2);
- socket_host_->Send(dest1_, packet2, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest1_, packet2, options, 0);
std::vector<char> packet3;
CreateStunError(&packet3);
- socket_host_->Send(dest1_, packet3, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest1_, packet3, options, 0);
ASSERT_EQ(sent_packets_.size(), 3U);
ASSERT_EQ(sent_packets_[0].second, packet1);
@@ -242,9 +247,10 @@ TEST_F(P2PSocketHostUdpTest, SendDataNoAuth) {
MatchMessage(static_cast<uint32>(P2PMsg_OnError::ID))))
.WillOnce(DoAll(DeleteArg<0>(), Return(true)));
+ talk_base::PacketOptions options;
std::vector<char> packet;
CreateRandomPacket(&packet);
- socket_host_->Send(dest1_, packet, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest1_, packet, options, 0);
ASSERT_EQ(sent_packets_.size(), 0U);
}
@@ -264,9 +270,11 @@ TEST_F(P2PSocketHostUdpTest, SendAfterStunRequest) {
EXPECT_CALL(sender_, Send(
MatchMessage(static_cast<uint32>(P2PMsg_OnSendComplete::ID))))
.WillOnce(DoAll(DeleteArg<0>(), Return(true)));
+
+ talk_base::PacketOptions options;
std::vector<char> packet;
CreateRandomPacket(&packet);
- socket_host_->Send(dest1_, packet, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest1_, packet, options, 0);
ASSERT_EQ(1U, sent_packets_.size());
ASSERT_EQ(dest1_, sent_packets_[0].first);
@@ -287,9 +295,11 @@ TEST_F(P2PSocketHostUdpTest, SendAfterStunResponse) {
EXPECT_CALL(sender_, Send(
MatchMessage(static_cast<uint32>(P2PMsg_OnSendComplete::ID))))
.WillOnce(DoAll(DeleteArg<0>(), Return(true)));
+
+ talk_base::PacketOptions options;
std::vector<char> packet;
CreateRandomPacket(&packet);
- socket_host_->Send(dest1_, packet, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest1_, packet, options, 0);
ASSERT_EQ(1U, sent_packets_.size());
ASSERT_EQ(dest1_, sent_packets_[0].first);
@@ -307,12 +317,13 @@ TEST_F(P2PSocketHostUdpTest, SendAfterStunResponseDifferentHost) {
socket_->ReceivePacket(dest1_, request_packet);
// Should fail when trying to send the same packet to |dest2_|.
+ talk_base::PacketOptions options;
std::vector<char> packet;
CreateRandomPacket(&packet);
EXPECT_CALL(sender_, Send(
MatchMessage(static_cast<uint32>(P2PMsg_OnError::ID))))
.WillOnce(DoAll(DeleteArg<0>(), Return(true)));
- socket_host_->Send(dest2_, packet, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest2_, packet, options, 0);
}
// Verify throttler not allowing unlimited sending of ICE messages to
@@ -323,15 +334,16 @@ TEST_F(P2PSocketHostUdpTest, ThrottleAfterLimit) {
.Times(2)
.WillRepeatedly(DoAll(DeleteArg<0>(), Return(true)));
+ talk_base::PacketOptions options;
std::vector<char> packet1;
CreateStunRequest(&packet1);
throttler_.SetSendIceBandwidth(packet1.size() * 2);
- socket_host_->Send(dest1_, packet1, net::DSCP_NO_CHANGE, 0);
- socket_host_->Send(dest2_, packet1, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest1_, packet1, options, 0);
+ socket_host_->Send(dest2_, packet1, options, 0);
net::IPEndPoint dest3 = ParseAddress(kTestIpAddress1, 2222);
// This packet must be dropped by the throttler.
- socket_host_->Send(dest3, packet1, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest3, packet1, options, 0);
ASSERT_EQ(sent_packets_.size(), 2U);
}
@@ -351,25 +363,26 @@ TEST_F(P2PSocketHostUdpTest, ThrottleAfterLimitAfterReceive) {
.Times(4)
.WillRepeatedly(DoAll(DeleteArg<0>(), Return(true)));
+ talk_base::PacketOptions options;
std::vector<char> packet1;
CreateStunRequest(&packet1);
throttler_.SetSendIceBandwidth(packet1.size());
// |dest1_| is known address, throttling will not be applied.
- socket_host_->Send(dest1_, packet1, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest1_, packet1, options, 0);
// Trying to send the packet to dest1_ in the same window. It should go.
- socket_host_->Send(dest1_, packet1, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest1_, packet1, options, 0);
// Throttler should allow this packet to go through.
- socket_host_->Send(dest2_, packet1, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest2_, packet1, options, 0);
net::IPEndPoint dest3 = ParseAddress(kTestIpAddress1, 2223);
// This packet will be dropped, as limit only for a single packet.
- socket_host_->Send(dest3, packet1, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest3, packet1, options, 0);
net::IPEndPoint dest4 = ParseAddress(kTestIpAddress1, 2224);
// This packet should also be dropped.
- socket_host_->Send(dest4, packet1, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest4, packet1, options, 0);
// |dest1| is known, we can send as many packets to it.
- socket_host_->Send(dest1_, packet1, net::DSCP_NO_CHANGE, 0);
+ socket_host_->Send(dest1_, packet1, options, 0);
ASSERT_EQ(sent_packets_.size(), 4U);
}
diff --git a/chromium/content/browser/renderer_host/p2p/socket_host_unittest.cc b/chromium/content/browser/renderer_host/p2p/socket_host_unittest.cc
new file mode 100644
index 00000000000..1404ced5bc6
--- /dev/null
+++ b/chromium/content/browser/renderer_host/p2p/socket_host_unittest.cc
@@ -0,0 +1,376 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/p2p/socket_host.h"
+
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "content/browser/renderer_host/p2p/socket_host_test_utils.h"
+#include "net/base/ip_endpoint.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+static unsigned char kFakeTag[4] = { 0xba, 0xdd, 0xba, 0xdd };
+static unsigned char kTestKey[] = "12345678901234567890";
+static unsigned char kTestAstValue[3] = { 0xaa, 0xbb, 0xcc };
+
+// Rtp message with invalid length.
+static unsigned char kRtpMsgWithInvalidLength[] = {
+ 0x94, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0xAA, 0xBB, 0xCC, 0XDD, // SSRC
+ 0xDD, 0xCC, 0xBB, 0xAA // Only 1 CSRC, but CC count is 4.
+};
+
+// Rtp message with single byte header extension, invalid extension length.
+static unsigned char kRtpMsgWithInvalidExtnLength[] = {
+ 0x90, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0xBE, 0xDE, 0x0A, 0x00 // Extn length - 0x0A00
+};
+
+// Valid rtp Message with 2 byte header extension.
+static unsigned char kRtpMsgWith2ByteExtnHeader[] = {
+ 0x90, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0xAA, 0xBB, 0xCC, 0XDD, // SSRC
+ 0x10, 0x00, 0x00, 0x01, // 2 Byte header extension
+ 0x01, 0x00, 0x00, 0x00
+};
+
+// Stun Indication message with Zero length
+static unsigned char kTurnSendIndicationMsgWithNoAttributes[] = {
+ 0x00, 0x16, 0x00, 0x00, // Zero length
+ 0x21, 0x12, 0xA4, 0x42, // magic cookie
+ '0', '1', '2', '3', // transaction id
+ '4', '5', '6', '7',
+ '8', '9', 'a', 'b',
+};
+
+// Stun Send Indication message with invalid length in stun header.
+static unsigned char kTurnSendIndicationMsgWithInvalidLength[] = {
+ 0x00, 0x16, 0xFF, 0x00, // length of 0xFF00
+ 0x21, 0x12, 0xA4, 0x42, // magic cookie
+ '0', '1', '2', '3', // transaction id
+ '4', '5', '6', '7',
+ '8', '9', 'a', 'b',
+};
+
+// Stun Send Indication message with no DATA attribute in message.
+static unsigned char kTurnSendIndicatinMsgWithNoDataAttribute[] = {
+ 0x00, 0x16, 0x00, 0x08, // length of
+ 0x21, 0x12, 0xA4, 0x42, // magic cookie
+ '0', '1', '2', '3', // transaction id
+ '4', '5', '6', '7',
+ '8', '9', 'a', 'b',
+ 0x00, 0x20, 0x00, 0x04, // Mapped address.
+ 0x00, 0x00, 0x00, 0x00
+};
+
+// A valid STUN indication message with a valid RTP header in data attribute
+// payload field and no extension bit set.
+static unsigned char kTurnSendIndicationMsgWithoutRtpExtension[] = {
+ 0x00, 0x16, 0x00, 0x18, // length of
+ 0x21, 0x12, 0xA4, 0x42, // magic cookie
+ '0', '1', '2', '3', // transaction id
+ '4', '5', '6', '7',
+ '8', '9', 'a', 'b',
+ 0x00, 0x20, 0x00, 0x04, // Mapped address.
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x13, 0x00, 0x0C, // Data attribute.
+ 0x80, 0x00, 0x00, 0x00, // RTP packet.
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+};
+
+// A valid STUN indication message with a valid RTP header and a extension
+// header.
+static unsigned char kTurnSendIndicationMsgWithAbsSendTimeExtension[] = {
+ 0x00, 0x16, 0x00, 0x24, // length of
+ 0x21, 0x12, 0xA4, 0x42, // magic cookie
+ '0', '1', '2', '3', // transaction id
+ '4', '5', '6', '7',
+ '8', '9', 'a', 'b',
+ 0x00, 0x20, 0x00, 0x04, // Mapped address.
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x13, 0x00, 0x18, // Data attribute.
+ 0x90, 0x00, 0x00, 0x00, // RTP packet.
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0xBE, 0xDE, 0x00, 0x02,
+ 0x22, 0xaa, 0xbb, 0xcc,
+ 0x32, 0xaa, 0xbb, 0xcc,
+};
+
+// A valid TURN channel header, but not RTP packet.
+static unsigned char kTurnChannelMsgNoRtpPacket[] = {
+ 0x40, 0x00, 0x00, 0x04,
+ 0xaa, 0xbb, 0xcc, 0xdd,
+};
+
+// Turn ChannelMessage with zero length of payload.
+static unsigned char kTurnChannelMsgWithZeroLength[] = {
+ 0x40, 0x00, 0x00, 0x00
+};
+
+// Turn ChannelMessage, wrapping a RTP packet without extension.
+static unsigned char kTurnChannelMsgWithRtpPacket[] = {
+ 0x40, 0x00, 0x00, 0x0C,
+ 0x80, 0x00, 0x00, 0x00, // RTP packet.
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+};
+
+// Turn ChannelMessage, wrapping a RTP packet with AbsSendTime Extension.
+static unsigned char kTurnChannelMsgWithAbsSendTimeExtension[] = {
+ 0x40, 0x00, 0x00, 0x14,
+ 0x90, 0x00, 0x00, 0x00, // RTP packet.
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0xBE, 0xDE, 0x00, 0x01,
+ 0x32, 0xaa, 0xbb, 0xcc,
+};
+
+// RTP packet with single byte extension header of length 4 bytes.
+// Extension id = 3 and length = 3
+static unsigned char kRtpMsgWithAbsSendTimeExtension[] = {
+ 0x90, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0xBE, 0xDE, 0x00, 0x02,
+ 0x22, 0x00, 0x02, 0x1c,
+ 0x32, 0xaa, 0xbb, 0xcc,
+};
+
+// Index of AbsSendTimeExtn data in message |kRtpMsgWithAbsSendTimeExtension|.
+static const int kAstIndexInRtpMsg = 21;
+
+namespace content {
+
+// This test verifies parsing of all invalid raw packets.
+TEST(P2PSocketHostTest, TestInvalidRawRtpMessages) {
+ int start_pos = INT_MAX, rtp_length = INT_MAX;
+ EXPECT_FALSE(packet_processing_helpers::GetRtpPacketStartPositionAndLength(
+ reinterpret_cast<char*>(kRtpMsgWithInvalidLength),
+ sizeof(kRtpMsgWithInvalidLength),
+ &start_pos, &rtp_length));
+ EXPECT_EQ(INT_MAX, start_pos);
+ EXPECT_EQ(INT_MAX, rtp_length);
+
+ EXPECT_FALSE(packet_processing_helpers::GetRtpPacketStartPositionAndLength(
+ reinterpret_cast<char*>(kRtpMsgWithInvalidExtnLength),
+ sizeof(kRtpMsgWithInvalidExtnLength),
+ &start_pos, &rtp_length));
+ EXPECT_EQ(INT_MAX, start_pos);
+ EXPECT_EQ(INT_MAX, rtp_length);
+}
+
+// Verify invalid TURN send indication messages. Messages are proper STUN
+// messages with incorrect values in attributes.
+TEST(P2PSocketHostTest, TestInvalidTurnSendIndicationMessages) {
+ // Initializing out params to very large value.
+ int start_pos = INT_MAX, rtp_length = INT_MAX;
+ EXPECT_FALSE(packet_processing_helpers::GetRtpPacketStartPositionAndLength(
+ reinterpret_cast<char*>(kTurnSendIndicationMsgWithNoAttributes),
+ sizeof(kTurnSendIndicationMsgWithNoAttributes),
+ &start_pos, &rtp_length));
+ EXPECT_EQ(INT_MAX, start_pos);
+ EXPECT_EQ(INT_MAX, rtp_length);
+
+ EXPECT_FALSE(packet_processing_helpers::GetRtpPacketStartPositionAndLength(
+ reinterpret_cast<char*>(kTurnSendIndicationMsgWithInvalidLength),
+ sizeof(kTurnSendIndicationMsgWithInvalidLength),
+ &start_pos, &rtp_length));
+ EXPECT_EQ(INT_MAX, start_pos);
+ EXPECT_EQ(INT_MAX, rtp_length);
+
+ EXPECT_FALSE(packet_processing_helpers::GetRtpPacketStartPositionAndLength(
+ reinterpret_cast<char*>(kTurnSendIndicatinMsgWithNoDataAttribute),
+ sizeof(kTurnSendIndicatinMsgWithNoDataAttribute),
+ &start_pos, &rtp_length));
+ EXPECT_EQ(INT_MAX, start_pos);
+ EXPECT_EQ(INT_MAX, rtp_length);
+}
+
+// This test verifies incorrectly formed TURN channel messages.
+TEST(P2PSocketHostTest, TestInvalidTurnChannelMessages) {
+ int start_pos = INT_MAX, rtp_length = INT_MAX;
+ EXPECT_FALSE(packet_processing_helpers::GetRtpPacketStartPositionAndLength(
+ reinterpret_cast<char*>(kTurnChannelMsgNoRtpPacket),
+ sizeof(kTurnChannelMsgNoRtpPacket),
+ &start_pos, &rtp_length));
+ EXPECT_EQ(INT_MAX, start_pos);
+ EXPECT_EQ(INT_MAX, rtp_length);
+
+ EXPECT_FALSE(packet_processing_helpers::GetRtpPacketStartPositionAndLength(
+ reinterpret_cast<char*>(kTurnChannelMsgWithZeroLength),
+ sizeof(kTurnChannelMsgWithZeroLength),
+ &start_pos, &rtp_length));
+ EXPECT_EQ(INT_MAX, start_pos);
+ EXPECT_EQ(INT_MAX, rtp_length);
+}
+
+// This test verifies parsing of a valid RTP packet which has 2byte header
+// extension instead of a 1 byte header extension.
+TEST(P2PSocketHostTest, TestValid2ByteExtnHdrRtpMessage) {
+ int start_pos = INT_MAX, rtp_length = INT_MAX;
+ EXPECT_TRUE(packet_processing_helpers::GetRtpPacketStartPositionAndLength(
+ reinterpret_cast<char*>(kRtpMsgWith2ByteExtnHeader),
+ sizeof(kRtpMsgWith2ByteExtnHeader),
+ &start_pos, &rtp_length));
+ EXPECT_EQ(20, rtp_length);
+ EXPECT_EQ(0, start_pos);
+}
+
+// This test verifies parsing of a valid RTP packet which has 1 byte header
+// AbsSendTime extension in it.
+TEST(P2PSocketHostTest, TestValidRtpPacketWithAbsSendTimeExtension) {
+ int start_pos = INT_MAX, rtp_length = INT_MAX;
+ EXPECT_TRUE(packet_processing_helpers::GetRtpPacketStartPositionAndLength(
+ reinterpret_cast<char*>(kRtpMsgWithAbsSendTimeExtension),
+ sizeof(kRtpMsgWithAbsSendTimeExtension),
+ &start_pos, &rtp_length));
+ EXPECT_EQ(24, rtp_length);
+ EXPECT_EQ(0, start_pos);
+}
+
+// This test verifies parsing of a valid TURN Send Indication messages.
+TEST(P2PSocketHostTest, TestValidTurnSendIndicationMessages) {
+ int start_pos = INT_MAX, rtp_length = INT_MAX;
+ EXPECT_TRUE(packet_processing_helpers::GetRtpPacketStartPositionAndLength(
+ reinterpret_cast<char*>(kTurnSendIndicationMsgWithoutRtpExtension),
+ sizeof(kTurnSendIndicationMsgWithoutRtpExtension),
+ &start_pos, &rtp_length));
+ EXPECT_EQ(12, rtp_length);
+ EXPECT_EQ(32, start_pos);
+
+ start_pos = INT_MAX, rtp_length = INT_MAX;
+ EXPECT_TRUE(packet_processing_helpers::GetRtpPacketStartPositionAndLength(
+ reinterpret_cast<char*>(kTurnSendIndicationMsgWithAbsSendTimeExtension),
+ sizeof(kTurnSendIndicationMsgWithAbsSendTimeExtension),
+ &start_pos, &rtp_length));
+ EXPECT_EQ(24, rtp_length);
+ EXPECT_EQ(32, start_pos);
+}
+
+// This test verifies parsing of valid TURN Channel Messages.
+TEST(P2PSocketHostTest, TestValidTurnChannelMessages) {
+ int start_pos = -1, rtp_length = -1;
+ EXPECT_TRUE(packet_processing_helpers::GetRtpPacketStartPositionAndLength(
+ reinterpret_cast<char*>(kTurnChannelMsgWithRtpPacket),
+ sizeof(kTurnChannelMsgWithRtpPacket), &start_pos, &rtp_length));
+ EXPECT_EQ(12, rtp_length);
+ EXPECT_EQ(4, start_pos);
+
+ start_pos = -1, rtp_length = -1;
+ EXPECT_TRUE(packet_processing_helpers::GetRtpPacketStartPositionAndLength(
+ reinterpret_cast<char*>(kTurnChannelMsgWithAbsSendTimeExtension),
+ sizeof(kTurnChannelMsgWithAbsSendTimeExtension),
+ &start_pos, &rtp_length));
+ EXPECT_EQ(20, rtp_length);
+ EXPECT_EQ(4, start_pos);
+}
+
+// Verify handling of a 2 byte extension header RTP messsage. Currently we don't
+// handle this kind of message.
+TEST(P2PSocketHostTest, TestUpdateAbsSendTimeExtensionIn2ByteHeaderExtn) {
+ EXPECT_FALSE(packet_processing_helpers::UpdateRtpAbsSendTimeExtn(
+ reinterpret_cast<char*>(kRtpMsgWith2ByteExtnHeader),
+ sizeof(kRtpMsgWith2ByteExtnHeader), 3, 0));
+}
+
+// Verify finding an extension ID in the TURN send indication message.
+TEST(P2PSocketHostTest, TestUpdateAbsSendTimeExtensionInTurnSendIndication) {
+ EXPECT_TRUE(packet_processing_helpers::UpdateRtpAbsSendTimeExtn(
+ reinterpret_cast<char*>(kTurnSendIndicationMsgWithoutRtpExtension),
+ sizeof(kTurnSendIndicationMsgWithoutRtpExtension), 3, 0));
+
+ EXPECT_TRUE(packet_processing_helpers::UpdateRtpAbsSendTimeExtn(
+ reinterpret_cast<char*>(kTurnSendIndicationMsgWithAbsSendTimeExtension),
+ sizeof(kTurnSendIndicationMsgWithAbsSendTimeExtension), 3, 0));
+}
+
+// Test without any packet options variables set. This method should return
+// without HMAC value in the packet.
+TEST(P2PSocketHostTest, TestApplyPacketOptionsWithDefaultValues) {
+ unsigned char fake_tag[4] = { 0xba, 0xdd, 0xba, 0xdd };
+ talk_base::PacketOptions options;
+ std::vector<char> rtp_packet;
+ rtp_packet.resize(sizeof(kRtpMsgWithAbsSendTimeExtension) + 4); // tag length
+ memcpy(&rtp_packet[0], kRtpMsgWithAbsSendTimeExtension,
+ sizeof(kRtpMsgWithAbsSendTimeExtension));
+ memcpy(&rtp_packet[sizeof(kRtpMsgWithAbsSendTimeExtension)], fake_tag, 4);
+ EXPECT_TRUE(
+ packet_processing_helpers::ApplyPacketOptions(
+ &rtp_packet[0], rtp_packet.size(), options, 0));
+ // Making sure we have't updated the HMAC.
+ EXPECT_EQ(0, memcmp(&rtp_packet[sizeof(kRtpMsgWithAbsSendTimeExtension)],
+ fake_tag, 4));
+
+ // Verify AbsouluteSendTime extension field is not modified.
+ EXPECT_EQ(0, memcmp(&rtp_packet[kAstIndexInRtpMsg],
+ kTestAstValue, sizeof(kTestAstValue)));
+}
+
+// Veirfy HMAC is updated when packet option parameters are set.
+TEST(P2PSocketHostTest, TestApplyPacketOptionsWithAuthParams) {
+ talk_base::PacketOptions options;
+ options.packet_time_params.srtp_auth_key.resize(20);
+ options.packet_time_params.srtp_auth_key.assign(
+ kTestKey, kTestKey + sizeof(kTestKey));
+ options.packet_time_params.srtp_auth_tag_len = 4;
+
+ std::vector<char> rtp_packet;
+ rtp_packet.resize(sizeof(kRtpMsgWithAbsSendTimeExtension) + 4); // tag length
+ memcpy(&rtp_packet[0], kRtpMsgWithAbsSendTimeExtension,
+ sizeof(kRtpMsgWithAbsSendTimeExtension));
+ memcpy(&rtp_packet[sizeof(kRtpMsgWithAbsSendTimeExtension)], kFakeTag, 4);
+ EXPECT_TRUE(packet_processing_helpers::ApplyPacketOptions(
+ &rtp_packet[0], rtp_packet.size(), options, 0));
+ // HMAC should be different from fake_tag.
+ EXPECT_NE(0, memcmp(&rtp_packet[sizeof(kRtpMsgWithAbsSendTimeExtension)],
+ kFakeTag, sizeof(kFakeTag)));
+
+ // Verify AbsouluteSendTime extension field is not modified.
+ EXPECT_EQ(0, memcmp(&rtp_packet[kAstIndexInRtpMsg],
+ kTestAstValue, sizeof(kTestAstValue)));
+}
+
+// Verify finding an extension ID in a raw rtp message.
+TEST(P2PSocketHostTest, TestUpdateAbsSendTimeExtensionInRtpPacket) {
+ EXPECT_TRUE(packet_processing_helpers::UpdateRtpAbsSendTimeExtn(
+ reinterpret_cast<char*>(kRtpMsgWithAbsSendTimeExtension),
+ sizeof(kRtpMsgWithAbsSendTimeExtension), 3, 0));
+}
+
+// Verify we update both AbsSendTime extension header and HMAC.
+TEST(P2PSocketHostTest, TestApplyPacketOptionsWithAuthParamsAndAbsSendTime) {
+ talk_base::PacketOptions options;
+ options.packet_time_params.srtp_auth_key.resize(20);
+ options.packet_time_params.srtp_auth_key.assign(
+ kTestKey, kTestKey + sizeof(kTestKey));
+ options.packet_time_params.srtp_auth_tag_len = 4;
+ options.packet_time_params.rtp_sendtime_extension_id = 3;
+ // 3 is also present in the test message.
+
+ std::vector<char> rtp_packet;
+ rtp_packet.resize(sizeof(kRtpMsgWithAbsSendTimeExtension) + 4); // tag length
+ memcpy(&rtp_packet[0], kRtpMsgWithAbsSendTimeExtension,
+ sizeof(kRtpMsgWithAbsSendTimeExtension));
+ memcpy(&rtp_packet[sizeof(kRtpMsgWithAbsSendTimeExtension)], kFakeTag, 4);
+ EXPECT_TRUE(packet_processing_helpers::ApplyPacketOptions(
+ &rtp_packet[0], rtp_packet.size(), options, 0xccbbaa));
+ // HMAC should be different from fake_tag.
+ EXPECT_NE(0, memcmp(&rtp_packet[sizeof(kRtpMsgWithAbsSendTimeExtension)],
+ kFakeTag, sizeof(kFakeTag)));
+
+ // ApplyPackets should have the new timestamp passed as input.
+ unsigned char timestamp_array[3] = { 0xcc, 0xbb, 0xaa };
+ EXPECT_EQ(0, memcmp(&rtp_packet[kAstIndexInRtpMsg],
+ timestamp_array, sizeof(timestamp_array)));
+}
+
+} // namespace content
diff --git a/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_impl.cc b/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_impl.cc
index c3c2c6f3932..403b129a469 100644
--- a/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_impl.cc
+++ b/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_impl.cc
@@ -4,12 +4,13 @@
#include "content/browser/renderer_host/pepper/browser_ppapi_host_impl.h"
+#include "base/metrics/sparse_histogram.h"
#include "content/browser/renderer_host/pepper/pepper_message_filter.h"
#include "content/browser/tracing/trace_message_filter.h"
#include "content/common/pepper_renderer_instance_data.h"
-#include "content/public/browser/render_view_host.h"
#include "content/public/common/process_type.h"
#include "ipc/ipc_message_macros.h"
+#include "ppapi/proxy/ppapi_messages.h"
namespace content {
@@ -24,8 +25,11 @@ BrowserPpapiHost* BrowserPpapiHost::CreateExternalPluginProcess(
const base::FilePath& profile_directory) {
// The plugin name and path shouldn't be needed for external plugins.
BrowserPpapiHostImpl* browser_ppapi_host =
- new BrowserPpapiHostImpl(sender, permissions, std::string(),
- base::FilePath(), profile_directory,
+ new BrowserPpapiHostImpl(sender,
+ permissions,
+ std::string(),
+ base::FilePath(),
+ profile_directory,
false /* in_process */,
true /* external_plugin */);
browser_ppapi_host->set_plugin_process_handle(plugin_child_process);
@@ -55,7 +59,7 @@ BrowserPpapiHostImpl::BrowserPpapiHostImpl(
in_process_(in_process),
external_plugin_(external_plugin),
ssl_context_helper_(new SSLContextHelper()) {
- message_filter_ = new HostMessageFilter(ppapi_host_.get());
+ message_filter_ = new HostMessageFilter(ppapi_host_.get(), this);
ppapi_host_->AddHostFactoryFilter(scoped_ptr<ppapi::host::HostFactory>(
new ContentBrowserPepperHostFactory(this)));
}
@@ -76,7 +80,7 @@ ppapi::host::PpapiHost* BrowserPpapiHostImpl::GetPpapiHost() {
base::ProcessHandle BrowserPpapiHostImpl::GetPluginProcessHandle() const {
// Handle should previously have been set before use.
- DCHECK(plugin_process_handle_ != base::kNullProcessHandle);
+ DCHECK(in_process_ || plugin_process_handle_ != base::kNullProcessHandle);
return plugin_process_handle_;
}
@@ -84,19 +88,19 @@ bool BrowserPpapiHostImpl::IsValidInstance(PP_Instance instance) const {
return instance_map_.find(instance) != instance_map_.end();
}
-bool BrowserPpapiHostImpl::GetRenderViewIDsForInstance(
+bool BrowserPpapiHostImpl::GetRenderFrameIDsForInstance(
PP_Instance instance,
int* render_process_id,
- int* render_view_id) const {
+ int* render_frame_id) const {
InstanceMap::const_iterator found = instance_map_.find(instance);
if (found == instance_map_.end()) {
*render_process_id = 0;
- *render_view_id = 0;
+ *render_frame_id = 0;
return false;
}
*render_process_id = found->second.render_process_id;
- *render_view_id = found->second.render_view_id;
+ *render_frame_id = found->second.render_frame_id;
return true;
}
@@ -126,6 +130,11 @@ GURL BrowserPpapiHostImpl::GetPluginURLForInstance(PP_Instance instance) {
return found->second.plugin_url;
}
+void BrowserPpapiHostImpl::SetOnKeepaliveCallback(
+ const BrowserPpapiHost::OnKeepaliveCallback& callback) {
+ on_keepalive_callback_ = callback;
+}
+
void BrowserPpapiHostImpl::AddInstance(
PP_Instance instance,
const PepperRendererInstanceData& instance_data) {
@@ -142,26 +151,71 @@ void BrowserPpapiHostImpl::DeleteInstance(PP_Instance instance) {
instance_map_.erase(found);
}
+BrowserPpapiHostImpl::HostMessageFilter::HostMessageFilter(
+ ppapi::host::PpapiHost* ppapi_host,
+ BrowserPpapiHostImpl* browser_ppapi_host_impl)
+ : ppapi_host_(ppapi_host),
+ browser_ppapi_host_impl_(browser_ppapi_host_impl) {}
+
bool BrowserPpapiHostImpl::HostMessageFilter::OnMessageReceived(
const IPC::Message& msg) {
// Don't forward messages if our owner object has been destroyed.
if (!ppapi_host_)
return false;
- /* TODO(brettw) when we add messages, here, the code should look like this:
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP(BrowserPpapiHostImpl, msg)
- // Add necessary message handlers here.
- IPC_MESSAGE_UNHANDLED(handled = ppapi_host_->OnMessageReceived(msg))
+ IPC_BEGIN_MESSAGE_MAP(BrowserPpapiHostImpl::HostMessageFilter, msg)
+ // Add necessary message handlers here.
+ IPC_MESSAGE_HANDLER(PpapiHostMsg_Keepalive, OnKeepalive)
+ IPC_MESSAGE_HANDLER(PpapiHostMsg_LogInterfaceUsage,
+ OnHostMsgLogInterfaceUsage)
+ IPC_MESSAGE_UNHANDLED(handled = ppapi_host_->OnMessageReceived(msg))
IPC_END_MESSAGE_MAP();
return handled;
- */
- return ppapi_host_->OnMessageReceived(msg);
}
void BrowserPpapiHostImpl::HostMessageFilter::OnHostDestroyed() {
DCHECK(ppapi_host_);
ppapi_host_ = NULL;
+ browser_ppapi_host_impl_ = NULL;
+}
+
+BrowserPpapiHostImpl::HostMessageFilter::~HostMessageFilter() {}
+
+void BrowserPpapiHostImpl::HostMessageFilter::OnKeepalive() {
+ if (browser_ppapi_host_impl_)
+ browser_ppapi_host_impl_->OnKeepalive();
+}
+
+void BrowserPpapiHostImpl::HostMessageFilter::OnHostMsgLogInterfaceUsage(
+ int hash) const {
+ UMA_HISTOGRAM_SPARSE_SLOWLY("Pepper.InterfaceUsed", hash);
+}
+
+void BrowserPpapiHostImpl::OnKeepalive() {
+ // An instance has been active. The on_keepalive_callback_ will be
+ // used to permit the content embedder to handle this, e.g. by tracking
+ // activity and shutting down processes that go idle.
+ //
+ // Currently embedders do not need to distinguish between instances having
+ // different idle state, and thus this implementation handles all instances
+ // for this module together.
+
+ if (on_keepalive_callback_.is_null())
+ return;
+
+ BrowserPpapiHost::OnKeepaliveInstanceData instance_data(instance_map_.size());
+
+ InstanceMap::iterator instance = instance_map_.begin();
+ int i = 0;
+ while (instance != instance_map_.end()) {
+ instance_data[i].render_process_id = instance->second.render_process_id;
+ instance_data[i].render_frame_id = instance->second.render_frame_id;
+ instance_data[i].document_url = instance->second.document_url;
+ ++instance;
+ ++i;
+ }
+ on_keepalive_callback_.Run(instance_data, profile_data_directory_);
}
} // namespace content
diff --git a/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_impl.h b/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_impl.h
index 5d953422c1b..16fec3d2907 100644
--- a/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_impl.h
+++ b/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_impl.h
@@ -12,13 +12,14 @@
#include "base/compiler_specific.h"
#include "base/files/file_path.h"
#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
#include "content/browser/renderer_host/pepper/content_browser_pepper_host_factory.h"
#include "content/browser/renderer_host/pepper/ssl_context_helper.h"
#include "content/common/content_export.h"
#include "content/common/pepper_renderer_instance_data.h"
#include "content/public/browser/browser_ppapi_host.h"
#include "content/public/common/process_type.h"
-#include "ipc/ipc_channel_proxy.h"
+#include "ipc/message_filter.h"
#include "ppapi/host/ppapi_host.h"
namespace content {
@@ -30,34 +31,35 @@ class CONTENT_EXPORT BrowserPpapiHostImpl : public BrowserPpapiHost {
// when this object is created).
// |external_plugin| signfies that this is a proxy created for an embedder's
// plugin, i.e. using BrowserPpapiHost::CreateExternalPluginProcess.
- BrowserPpapiHostImpl(
- IPC::Sender* sender,
- const ppapi::PpapiPermissions& permissions,
- const std::string& plugin_name,
- const base::FilePath& plugin_path,
- const base::FilePath& profile_data_directory,
- bool in_process,
- bool external_plugin);
+ BrowserPpapiHostImpl(IPC::Sender* sender,
+ const ppapi::PpapiPermissions& permissions,
+ const std::string& plugin_name,
+ const base::FilePath& plugin_path,
+ const base::FilePath& profile_data_directory,
+ bool in_process,
+ bool external_plugin);
virtual ~BrowserPpapiHostImpl();
// BrowserPpapiHost.
virtual ppapi::host::PpapiHost* GetPpapiHost() OVERRIDE;
virtual base::ProcessHandle GetPluginProcessHandle() const OVERRIDE;
virtual bool IsValidInstance(PP_Instance instance) const OVERRIDE;
- virtual bool GetRenderViewIDsForInstance(PP_Instance instance,
- int* render_process_id,
- int* render_view_id) const OVERRIDE;
+ virtual bool GetRenderFrameIDsForInstance(PP_Instance instance,
+ int* render_process_id,
+ int* render_frame_id) const
+ OVERRIDE;
virtual const std::string& GetPluginName() OVERRIDE;
virtual const base::FilePath& GetPluginPath() OVERRIDE;
virtual const base::FilePath& GetProfileDataDirectory() OVERRIDE;
virtual GURL GetDocumentURLForInstance(PP_Instance instance) OVERRIDE;
virtual GURL GetPluginURLForInstance(PP_Instance instance) OVERRIDE;
+ virtual void SetOnKeepaliveCallback(
+ const BrowserPpapiHost::OnKeepaliveCallback& callback) OVERRIDE;
void set_plugin_process_handle(base::ProcessHandle handle) {
plugin_process_handle_ = handle;
}
- bool in_process() const { return in_process_; }
bool external_plugin() const { return external_plugin_; }
// These two functions are notifications that an instance has been created
@@ -67,7 +69,7 @@ class CONTENT_EXPORT BrowserPpapiHostImpl : public BrowserPpapiHost {
const PepperRendererInstanceData& instance_data);
void DeleteInstance(PP_Instance instance);
- scoped_refptr<IPC::ChannelProxy::MessageFilter> message_filter() {
+ scoped_refptr<IPC::MessageFilter> message_filter() {
return message_filter_;
}
@@ -81,21 +83,30 @@ class CONTENT_EXPORT BrowserPpapiHostImpl : public BrowserPpapiHost {
// Implementing MessageFilter on BrowserPpapiHostImpl makes it ref-counted,
// preventing us from returning these to embedders without holding a
// reference. To avoid that, define a message filter object.
- class HostMessageFilter : public IPC::ChannelProxy::MessageFilter {
+ class HostMessageFilter : public IPC::MessageFilter {
public:
- explicit HostMessageFilter(ppapi::host::PpapiHost* ppapi_host)
- : ppapi_host_(ppapi_host) {}
- // IPC::ChannelProxy::MessageFilter.
+ HostMessageFilter(ppapi::host::PpapiHost* ppapi_host,
+ BrowserPpapiHostImpl* browser_ppapi_host_impl);
+
+ // IPC::MessageFilter.
virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE;
void OnHostDestroyed();
private:
- virtual ~HostMessageFilter() {}
+ virtual ~HostMessageFilter();
+
+ void OnKeepalive();
+ void OnHostMsgLogInterfaceUsage(int hash) const;
+ // Non owning pointers cleared in OnHostDestroyed()
ppapi::host::PpapiHost* ppapi_host_;
+ BrowserPpapiHostImpl* browser_ppapi_host_impl_;
};
+ // Reports plugin activity to the callback set with SetOnKeepaliveCallback.
+ void OnKeepalive();
+
scoped_ptr<ppapi::host::PpapiHost> ppapi_host_;
base::ProcessHandle plugin_process_handle_;
std::string plugin_name_;
@@ -118,6 +129,8 @@ class CONTENT_EXPORT BrowserPpapiHostImpl : public BrowserPpapiHost {
scoped_refptr<HostMessageFilter> message_filter_;
+ BrowserPpapiHost::OnKeepaliveCallback on_keepalive_callback_;
+
DISALLOW_COPY_AND_ASSIGN(BrowserPpapiHostImpl);
};
diff --git a/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_test.cc b/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_test.cc
index ecbcd0e120a..428f63544b0 100644
--- a/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_test.cc
+++ b/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_test.cc
@@ -8,21 +8,19 @@
namespace content {
-BrowserPpapiHostTest::BrowserPpapiHostTest()
- : sink_() {
- ppapi_host_.reset(new BrowserPpapiHostImpl(
- &sink_,
- ppapi::PpapiPermissions::AllPermissions(),
- std::string(),
- base::FilePath(),
- base::FilePath(),
- false /* in_process */,
- false /* external_plugin */));
+BrowserPpapiHostTest::BrowserPpapiHostTest() : sink_() {
+ ppapi_host_.reset(
+ new BrowserPpapiHostImpl(&sink_,
+ ppapi::PpapiPermissions::AllPermissions(),
+ std::string(),
+ base::FilePath(),
+ base::FilePath(),
+ false /* in_process */,
+ false /* external_plugin */));
ppapi_host_->set_plugin_process_handle(base::GetCurrentProcessHandle());
}
-BrowserPpapiHostTest::~BrowserPpapiHostTest() {
-}
+BrowserPpapiHostTest::~BrowserPpapiHostTest() {}
BrowserPpapiHost* BrowserPpapiHostTest::GetBrowserPpapiHost() {
return ppapi_host_.get();
diff --git a/chromium/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.cc b/chromium/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.cc
index afa539608af..e1ce0320b6e 100644
--- a/chromium/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.cc
+++ b/chromium/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.cc
@@ -38,22 +38,19 @@ namespace {
const size_t kMaxSocketsAllowed = 1024;
bool CanCreateSocket() {
- return
- PepperTCPServerSocketMessageFilter::GetNumInstances() +
- PepperTCPSocketMessageFilter::GetNumInstances() +
- PepperUDPSocketMessageFilter::GetNumInstances() <
- kMaxSocketsAllowed;
+ return PepperTCPServerSocketMessageFilter::GetNumInstances() +
+ PepperTCPSocketMessageFilter::GetNumInstances() +
+ PepperUDPSocketMessageFilter::GetNumInstances() <
+ kMaxSocketsAllowed;
}
} // namespace
ContentBrowserPepperHostFactory::ContentBrowserPepperHostFactory(
BrowserPpapiHostImpl* host)
- : host_(host) {
-}
+ : host_(host) {}
-ContentBrowserPepperHostFactory::~ContentBrowserPepperHostFactory() {
-}
+ContentBrowserPepperHostFactory::~ContentBrowserPepperHostFactory() {}
scoped_ptr<ResourceHost> ContentBrowserPepperHostFactory::CreateResourceHost(
ppapi::host::PpapiHost* host,
@@ -69,13 +66,13 @@ scoped_ptr<ResourceHost> ContentBrowserPepperHostFactory::CreateResourceHost(
// Public interfaces.
switch (message.type()) {
case PpapiHostMsg_FileIO_Create::ID: {
- return scoped_ptr<ResourceHost>(new PepperFileIOHost(
- host_, instance, params.pp_resource()));
+ return scoped_ptr<ResourceHost>(
+ new PepperFileIOHost(host_, instance, params.pp_resource()));
}
case PpapiHostMsg_FileSystem_Create::ID: {
PP_FileSystemType file_system_type;
- if (!ppapi::UnpackMessage<PpapiHostMsg_FileSystem_Create>(message,
- &file_system_type)) {
+ if (!ppapi::UnpackMessage<PpapiHostMsg_FileSystem_Create>(
+ message, &file_system_type)) {
NOTREACHED();
return scoped_ptr<ResourceHost>();
}
@@ -83,25 +80,27 @@ scoped_ptr<ResourceHost> ContentBrowserPepperHostFactory::CreateResourceHost(
host_, instance, params.pp_resource(), file_system_type));
}
case PpapiHostMsg_Gamepad_Create::ID: {
- return scoped_ptr<ResourceHost>(new PepperGamepadHost(
- host_, instance, params.pp_resource()));
+ return scoped_ptr<ResourceHost>(
+ new PepperGamepadHost(host_, instance, params.pp_resource()));
}
case PpapiHostMsg_NetworkProxy_Create::ID: {
- return scoped_ptr<ResourceHost>(new PepperNetworkProxyHost(
- host_, instance, params.pp_resource()));
+ return scoped_ptr<ResourceHost>(
+ new PepperNetworkProxyHost(host_, instance, params.pp_resource()));
}
case PpapiHostMsg_HostResolver_Create::ID: {
scoped_refptr<ResourceMessageFilter> host_resolver(
new PepperHostResolverMessageFilter(host_, instance, false));
- return scoped_ptr<ResourceHost>(new MessageFilterHost(
- host_->GetPpapiHost(), instance, params.pp_resource(),
- host_resolver));
+ return scoped_ptr<ResourceHost>(
+ new MessageFilterHost(host_->GetPpapiHost(),
+ instance,
+ params.pp_resource(),
+ host_resolver));
}
- case PpapiHostMsg_FileRef_CreateInternal::ID: {
+ case PpapiHostMsg_FileRef_CreateForFileAPI::ID: {
PP_Resource file_system;
std::string internal_path;
- if (!UnpackMessage<PpapiHostMsg_FileRef_CreateInternal>(
- message, &file_system, &internal_path)) {
+ if (!UnpackMessage<PpapiHostMsg_FileRef_CreateForFileAPI>(
+ message, &file_system, &internal_path)) {
NOTREACHED();
return scoped_ptr<ResourceHost>();
}
@@ -135,9 +134,11 @@ scoped_ptr<ResourceHost> ContentBrowserPepperHostFactory::CreateResourceHost(
case PpapiHostMsg_Printing_Create::ID: {
scoped_ptr<PepperPrintSettingsManager> manager(
new PepperPrintSettingsManagerImpl());
- return scoped_ptr<ResourceHost>(new PepperPrintingHost(
- host_->GetPpapiHost(), instance,
- params.pp_resource(), manager.Pass()));
+ return scoped_ptr<ResourceHost>(
+ new PepperPrintingHost(host_->GetPpapiHost(),
+ instance,
+ params.pp_resource(),
+ manager.Pass()));
}
case PpapiHostMsg_TrueTypeFontSingleton_Create::ID: {
return scoped_ptr<ResourceHost>(new PepperTrueTypeFontListHost(
@@ -171,16 +172,18 @@ scoped_ptr<ResourceHost> ContentBrowserPepperHostFactory::CreateResourceHost(
if (CanCreateSocket()) {
scoped_refptr<ResourceMessageFilter> tcp_server_socket(
new PepperTCPServerSocketMessageFilter(this, host_, instance, true));
- return scoped_ptr<ResourceHost>(new MessageFilterHost(
- host_->GetPpapiHost(), instance, params.pp_resource(),
- tcp_server_socket));
+ return scoped_ptr<ResourceHost>(
+ new MessageFilterHost(host_->GetPpapiHost(),
+ instance,
+ params.pp_resource(),
+ tcp_server_socket));
} else {
return scoped_ptr<ResourceHost>();
}
}
if (message.type() == PpapiHostMsg_TCPSocket_CreatePrivate::ID) {
- return CreateNewTCPSocket(instance, params.pp_resource(),
- ppapi::TCP_SOCKET_VERSION_PRIVATE);
+ return CreateNewTCPSocket(
+ instance, params.pp_resource(), ppapi::TCP_SOCKET_VERSION_PRIVATE);
}
if (message.type() == PpapiHostMsg_UDPSocket_CreatePrivate::ID) {
if (CanCreateSocket()) {
@@ -203,9 +206,11 @@ scoped_ptr<ResourceHost> ContentBrowserPepperHostFactory::CreateResourceHost(
case PpapiHostMsg_FlashFile_Create::ID: {
scoped_refptr<ResourceMessageFilter> file_filter(
new PepperFlashFileMessageFilter(instance, host_));
- return scoped_ptr<ResourceHost>(new MessageFilterHost(
- host_->GetPpapiHost(), instance, params.pp_resource(),
- file_filter));
+ return scoped_ptr<ResourceHost>(
+ new MessageFilterHost(host_->GetPpapiHost(),
+ instance,
+ params.pp_resource(),
+ file_filter));
}
}
}
@@ -221,10 +226,10 @@ ContentBrowserPepperHostFactory::CreateAcceptedTCPSocket(
if (!CanCreateSocket())
return scoped_ptr<ResourceHost>();
scoped_refptr<ResourceMessageFilter> tcp_socket(
- new PepperTCPSocketMessageFilter(host_, instance, version,
- socket.Pass()));
- return scoped_ptr<ResourceHost>(new MessageFilterHost(
- host_->GetPpapiHost(), instance, 0, tcp_socket));
+ new PepperTCPSocketMessageFilter(
+ host_, instance, version, socket.Pass()));
+ return scoped_ptr<ResourceHost>(
+ new MessageFilterHost(host_->GetPpapiHost(), instance, 0, tcp_socket));
}
scoped_ptr<ppapi::host::ResourceHost>
@@ -244,8 +249,8 @@ ContentBrowserPepperHostFactory::CreateNewTCPSocket(
host_->GetPpapiHost(), instance, resource, tcp_socket));
}
-const ppapi::PpapiPermissions&
-ContentBrowserPepperHostFactory::GetPermissions() const {
+const ppapi::PpapiPermissions& ContentBrowserPepperHostFactory::GetPermissions()
+ const {
return host_->GetPpapiHost()->permissions();
}
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_browser_font_singleton_host.cc b/chromium/content/browser/renderer_host/pepper/pepper_browser_font_singleton_host.cc
index 99a8e04fc70..40c13dfee01 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_browser_font_singleton_host.cc
+++ b/chromium/content/browser/renderer_host/pepper/pepper_browser_font_singleton_host.cc
@@ -38,11 +38,9 @@ class FontMessageFilter : public ppapi::host::ResourceMessageFilter {
DISALLOW_COPY_AND_ASSIGN(FontMessageFilter);
};
-FontMessageFilter::FontMessageFilter() {
-}
+FontMessageFilter::FontMessageFilter() {}
-FontMessageFilter::~FontMessageFilter() {
-}
+FontMessageFilter::~FontMessageFilter() {}
scoped_refptr<base::TaskRunner> FontMessageFilter::OverrideTaskRunnerForMessage(
const IPC::Message& msg) {
@@ -57,11 +55,11 @@ scoped_refptr<base::TaskRunner> FontMessageFilter::OverrideTaskRunnerForMessage(
int32_t FontMessageFilter::OnResourceMessageReceived(
const IPC::Message& msg,
ppapi::host::HostMessageContext* context) {
- IPC_BEGIN_MESSAGE_MAP(FontMessageFilter, msg)
+ PPAPI_BEGIN_MESSAGE_MAP(FontMessageFilter, msg)
PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
PpapiHostMsg_BrowserFontSingleton_GetFontFamilies,
OnHostMsgGetFontFamilies)
- IPC_END_MESSAGE_MAP()
+ PPAPI_END_MESSAGE_MAP()
return PP_ERROR_FAILED;
}
@@ -105,7 +103,6 @@ PepperBrowserFontSingletonHost::PepperBrowserFontSingletonHost(
new FontMessageFilter()));
}
-PepperBrowserFontSingletonHost::~PepperBrowserFontSingletonHost() {
-}
+PepperBrowserFontSingletonHost::~PepperBrowserFontSingletonHost() {}
} // namespace content
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_external_file_ref_backend.cc b/chromium/content/browser/renderer_host/pepper/pepper_external_file_ref_backend.cc
index 4517e3725c0..a6b61981ac5 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_external_file_ref_backend.cc
+++ b/chromium/content/browser/renderer_host/pepper/pepper_external_file_ref_backend.cc
@@ -20,20 +20,20 @@ namespace content {
PepperExternalFileRefBackend::PepperExternalFileRefBackend(
ppapi::host::PpapiHost* host,
int render_process_id,
- const base::FilePath& path) : host_(host),
- path_(path),
- render_process_id_(render_process_id),
- weak_factory_(this) {
+ const base::FilePath& path)
+ : host_(host),
+ path_(path),
+ render_process_id_(render_process_id),
+ weak_factory_(this) {
task_runner_ =
BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE);
}
-PepperExternalFileRefBackend::~PepperExternalFileRefBackend() {
-}
+PepperExternalFileRefBackend::~PepperExternalFileRefBackend() {}
int32_t PepperExternalFileRefBackend::MakeDirectory(
ppapi::host::ReplyMessageContext reply_context,
- bool make_ancestors) {
+ int32_t make_directory_flags) {
// This operation isn't supported for external filesystems.
return PP_ERROR_NOACCESS;
}
@@ -88,7 +88,8 @@ int32_t PepperExternalFileRefBackend::ReadDirectoryEntries(
int32_t PepperExternalFileRefBackend::GetAbsolutePath(
ppapi::host::ReplyMessageContext reply_context) {
- host_->SendReply(reply_context,
+ host_->SendReply(
+ reply_context,
PpapiPluginMsg_FileRef_GetAbsolutePathReply(path_.AsUTF8Unsafe()));
// Use PP_OK_COMPLETIONPENDING instead of PP_OK since we've already sent our
@@ -105,8 +106,8 @@ base::FilePath PepperExternalFileRefBackend::GetExternalFilePath() const {
}
int32_t PepperExternalFileRefBackend::CanRead() const {
- if (!ChildProcessSecurityPolicyImpl::GetInstance()->
- CanReadFile(render_process_id_, path_)) {
+ if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(
+ render_process_id_, path_)) {
return PP_ERROR_NOACCESS;
}
return PP_OK;
@@ -123,8 +124,8 @@ int32_t PepperExternalFileRefBackend::CanCreate() const {
}
int32_t PepperExternalFileRefBackend::CanReadWrite() const {
- if (!ChildProcessSecurityPolicyImpl::GetInstance()->
- CanCreateReadWriteFile(render_process_id_, path_)) {
+ if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanCreateReadWriteFile(
+ render_process_id_, path_)) {
return PP_ERROR_NOACCESS;
}
return PP_OK;
@@ -133,20 +134,20 @@ int32_t PepperExternalFileRefBackend::CanReadWrite() const {
void PepperExternalFileRefBackend::DidFinish(
ppapi::host::ReplyMessageContext reply_context,
const IPC::Message& msg,
- base::PlatformFileError error) {
- reply_context.params.set_result(ppapi::PlatformFileErrorToPepperError(error));
+ base::File::Error error) {
+ reply_context.params.set_result(ppapi::FileErrorToPepperError(error));
host_->SendReply(reply_context, msg);
}
void PepperExternalFileRefBackend::GetMetadataComplete(
ppapi::host::ReplyMessageContext reply_context,
- const base::PlatformFileError error,
- const base::PlatformFileInfo& file_info) {
- reply_context.params.set_result(ppapi::PlatformFileErrorToPepperError(error));
+ const base::File::Error error,
+ const base::File::Info& file_info) {
+ reply_context.params.set_result(ppapi::FileErrorToPepperError(error));
PP_FileInfo pp_file_info;
- if (error == base::PLATFORM_FILE_OK) {
- ppapi::PlatformFileInfoToPepperFileInfo(
+ if (error == base::File::FILE_OK) {
+ ppapi::FileInfoToPepperFileInfo(
file_info, PP_FILESYSTEMTYPE_EXTERNAL, &pp_file_info);
} else {
memset(&pp_file_info, 0, sizeof(pp_file_info));
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_external_file_ref_backend.h b/chromium/content/browser/renderer_host/pepper/pepper_external_file_ref_backend.h
index 525749d7893..4d2980ae421 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_external_file_ref_backend.h
+++ b/chromium/content/browser/renderer_host/pepper/pepper_external_file_ref_backend.h
@@ -7,6 +7,7 @@
#include <string>
+#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/memory/ref_counted.h"
#include "base/task_runner.h"
@@ -28,7 +29,7 @@ class PepperExternalFileRefBackend : public PepperFileRefBackend {
// PepperFileRefBackend overrides.
virtual int32_t MakeDirectory(ppapi::host::ReplyMessageContext context,
- bool make_ancestors) OVERRIDE;
+ int32_t make_directory_flags) OVERRIDE;
virtual int32_t Touch(ppapi::host::ReplyMessageContext context,
PP_Time last_accessed_time,
PP_Time last_modified_time) OVERRIDE;
@@ -36,8 +37,8 @@ class PepperExternalFileRefBackend : public PepperFileRefBackend {
virtual int32_t Rename(ppapi::host::ReplyMessageContext context,
PepperFileRefHost* new_file_ref) OVERRIDE;
virtual int32_t Query(ppapi::host::ReplyMessageContext context) OVERRIDE;
- virtual int32_t ReadDirectoryEntries(
- ppapi::host::ReplyMessageContext context) OVERRIDE;
+ virtual int32_t ReadDirectoryEntries(ppapi::host::ReplyMessageContext context)
+ OVERRIDE;
virtual int32_t GetAbsolutePath(ppapi::host::ReplyMessageContext context)
OVERRIDE;
virtual fileapi::FileSystemURL GetFileSystemURL() const OVERRIDE;
@@ -52,13 +53,12 @@ class PepperExternalFileRefBackend : public PepperFileRefBackend {
// Generic reply callback.
void DidFinish(ppapi::host::ReplyMessageContext reply_context,
const IPC::Message& msg,
- base::PlatformFileError error);
+ base::File::Error error);
// Operation specific callbacks.
- void GetMetadataComplete(
- ppapi::host::ReplyMessageContext reply_context,
- base::PlatformFileError error,
- const base::PlatformFileInfo& file_info);
+ void GetMetadataComplete(ppapi::host::ReplyMessageContext reply_context,
+ base::File::Error error,
+ const base::File::Info& file_info);
ppapi::host::PpapiHost* host_;
base::FilePath path_;
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_file_io_host.cc b/chromium/content/browser/renderer_host/pepper/pepper_file_io_host.cc
index 66a0ad2ae40..e350138f8a7 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_file_io_host.cc
+++ b/chromium/content/browser/renderer_host/pepper/pepper_file_io_host.cc
@@ -41,17 +41,10 @@ using ppapi::PPTimeToTime;
namespace {
-int32_t ErrorOrByteNumber(int32_t pp_error, int32_t byte_number) {
- // On the plugin side, some callbacks expect a parameter that means different
- // things depending on whether it is negative or not. We translate for those
- // callbacks here.
- return pp_error == PP_OK ? byte_number : pp_error;
-}
-
-PepperFileIOHost::UIThreadStuff
-GetUIThreadStuffForInternalFileSystems(int render_process_id) {
+PepperFileIOHost::UIThreadStuff GetUIThreadStuffForInternalFileSystems(
+ int render_process_id) {
PepperFileIOHost::UIThreadStuff stuff;
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
RenderProcessHost* host = RenderProcessHost::FromID(render_process_id);
if (host) {
stuff.resolved_render_process_id = base::GetProcId(host->GetHandle());
@@ -63,7 +56,7 @@ GetUIThreadStuffForInternalFileSystems(int render_process_id) {
}
base::ProcessId GetResolvedRenderProcessId(int render_process_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
RenderProcessHost* host = RenderProcessHost::FromID(render_process_id);
if (!host)
return base::kNullProcessId;
@@ -72,13 +65,42 @@ base::ProcessId GetResolvedRenderProcessId(int render_process_id) {
bool GetPluginAllowedToCallRequestOSFileHandle(int render_process_id,
const GURL& document_url) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
ContentBrowserClient* client = GetContentClient()->browser();
RenderProcessHost* host = RenderProcessHost::FromID(render_process_id);
+ if (!host)
+ return false;
return client->IsPluginAllowedToCallRequestOSFileHandle(
host->GetBrowserContext(), document_url);
}
+bool FileOpenForWrite(int32_t open_flags) {
+ return (open_flags & (PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_APPEND)) != 0;
+}
+
+void FileCloser(base::File auto_close) {
+}
+
+void DidCloseFile(const base::Closure& on_close_callback) {
+ if (!on_close_callback.is_null())
+ on_close_callback.Run();
+}
+
+void DidOpenFile(base::WeakPtr<PepperFileIOHost> file_host,
+ fileapi::FileSystemOperation::OpenFileCallback callback,
+ base::File file,
+ const base::Closure& on_close_callback) {
+ if (file_host) {
+ callback.Run(file.Pass(), on_close_callback);
+ } else {
+ BrowserThread::PostTaskAndReply(
+ BrowserThread::FILE,
+ FROM_HERE,
+ base::Bind(&FileCloser, base::Passed(&file)),
+ base::Bind(&DidCloseFile, on_close_callback));
+ }
+}
+
} // namespace
PepperFileIOHost::PepperFileIOHost(BrowserPpapiHostImpl* host,
@@ -87,45 +109,34 @@ PepperFileIOHost::PepperFileIOHost(BrowserPpapiHostImpl* host,
: ResourceHost(host->GetPpapiHost(), instance, resource),
browser_ppapi_host_(host),
render_process_host_(NULL),
- file_(base::kInvalidPlatformFileValue),
+ file_(BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)),
open_flags_(0),
file_system_type_(PP_FILESYSTEMTYPE_INVALID),
max_written_offset_(0),
- check_quota_(false),
- weak_factory_(this) {
+ check_quota_(false) {
int unused;
- if (!host->GetRenderViewIDsForInstance(instance,
- &render_process_id_,
- &unused)) {
+ if (!host->GetRenderFrameIDsForInstance(
+ instance, &render_process_id_, &unused)) {
render_process_id_ = -1;
}
- file_message_loop_ = BrowserThread::GetMessageLoopProxyForThread(
- BrowserThread::FILE);
}
-PepperFileIOHost::~PepperFileIOHost() {
- OnHostMsgClose(NULL);
-}
+PepperFileIOHost::~PepperFileIOHost() {}
int32_t PepperFileIOHost::OnResourceMessageReceived(
const IPC::Message& msg,
ppapi::host::HostMessageContext* context) {
- IPC_BEGIN_MESSAGE_MAP(PepperFileIOHost, msg)
- PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Open,
- OnHostMsgOpen)
- PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Touch,
- OnHostMsgTouch)
- PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Write,
- OnHostMsgWrite)
+ PPAPI_BEGIN_MESSAGE_MAP(PepperFileIOHost, msg)
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Open, OnHostMsgOpen)
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Touch, OnHostMsgTouch)
PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_SetLength,
OnHostMsgSetLength)
PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileIO_Flush,
OnHostMsgFlush)
- PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileIO_Close,
- OnHostMsgClose)
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Close, OnHostMsgClose)
PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileIO_RequestOSFileHandle,
OnHostMsgRequestOSFileHandle)
- IPC_END_MESSAGE_MAP()
+ PPAPI_END_MESSAGE_MAP()
return PP_ERROR_FAILED;
}
@@ -133,8 +144,7 @@ PepperFileIOHost::UIThreadStuff::UIThreadStuff() {
resolved_render_process_id = base::kNullProcessId;
}
-PepperFileIOHost::UIThreadStuff::~UIThreadStuff() {
-}
+PepperFileIOHost::UIThreadStuff::~UIThreadStuff() {}
int32_t PepperFileIOHost::OnHostMsgOpen(
ppapi::host::HostMessageContext* context,
@@ -165,20 +175,38 @@ int32_t PepperFileIOHost::OnHostMsgOpen(
file_system_type_ = file_ref_host->GetFileSystemType();
file_system_url_ = file_ref_host->GetFileSystemURL();
- if (file_system_type_ != PP_FILESYSTEMTYPE_EXTERNAL) {
+ // For external file systems, if there is a valid FileSystemURL, then treat
+ // it like internal file systems and access it via the FileSystemURL.
+ bool is_internal_type = (file_system_type_ != PP_FILESYSTEMTYPE_EXTERNAL) ||
+ file_system_url_.is_valid();
+
+ if (is_internal_type) {
if (!file_system_url_.is_valid())
return PP_ERROR_BADARGUMENT;
- if (!CanOpenFileSystemURLWithPepperFlags(open_flags,
- render_process_id_,
- file_system_url_))
+
+ // Not all external file systems are fully supported yet.
+ // Whitelist the supported ones.
+ if (file_system_url_.mount_type() == fileapi::kFileSystemTypeExternal) {
+ switch (file_system_url_.type()) {
+ case fileapi::kFileSystemTypeNativeMedia:
+ case fileapi::kFileSystemTypeDeviceMedia:
+ case fileapi::kFileSystemTypePicasa:
+ case fileapi::kFileSystemTypeItunes:
+ case fileapi::kFileSystemTypeIphoto:
+ break;
+ default:
+ return PP_ERROR_NOACCESS;
+ }
+ }
+ if (!CanOpenFileSystemURLWithPepperFlags(
+ open_flags, render_process_id_, file_system_url_))
return PP_ERROR_NOACCESS;
BrowserThread::PostTaskAndReplyWithResult(
BrowserThread::UI,
FROM_HERE,
- base::Bind(&GetUIThreadStuffForInternalFileSystems,
- render_process_id_),
+ base::Bind(&GetUIThreadStuffForInternalFileSystems, render_process_id_),
base::Bind(&PepperFileIOHost::GotUIThreadStuffForInternalFileSystems,
- weak_factory_.GetWeakPtr(),
+ AsWeakPtr(),
context->MakeReplyMessageContext(),
platform_file_flags));
} else {
@@ -190,7 +218,7 @@ int32_t PepperFileIOHost::OnHostMsgOpen(
FROM_HERE,
base::Bind(&GetResolvedRenderProcessId, render_process_id_),
base::Bind(&PepperFileIOHost::GotResolvedRenderProcessId,
- weak_factory_.GetWeakPtr(),
+ AsWeakPtr(),
context->MakeReplyMessageContext(),
path,
platform_file_flags));
@@ -203,70 +231,74 @@ void PepperFileIOHost::GotUIThreadStuffForInternalFileSystems(
ppapi::host::ReplyMessageContext reply_context,
int platform_file_flags,
UIThreadStuff ui_thread_stuff) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
file_system_context_ = ui_thread_stuff.file_system_context;
resolved_render_process_id_ = ui_thread_stuff.resolved_render_process_id;
if (resolved_render_process_id_ == base::kNullProcessId ||
!file_system_context_.get()) {
reply_context.params.set_result(PP_ERROR_FAILED);
- host()->SendReply(reply_context, PpapiPluginMsg_FileIO_OpenReply());
+ SendOpenErrorReply(reply_context);
return;
}
if (!file_system_context_->GetFileSystemBackend(file_system_url_.type())) {
reply_context.params.set_result(PP_ERROR_FAILED);
- host()->SendReply(reply_context, PpapiPluginMsg_FileIO_OpenReply());
+ SendOpenErrorReply(reply_context);
return;
}
DCHECK(file_system_host_.get());
DCHECK(file_system_host_->GetFileSystemOperationRunner());
+
file_system_host_->GetFileSystemOperationRunner()->OpenFile(
file_system_url_,
platform_file_flags,
- base::Bind(&PepperFileIOHost::DidOpenInternalFile,
- weak_factory_.GetWeakPtr(),
- reply_context));
+ base::Bind(&DidOpenFile,
+ AsWeakPtr(),
+ base::Bind(&PepperFileIOHost::DidOpenInternalFile,
+ AsWeakPtr(),
+ reply_context)));
}
void PepperFileIOHost::DidOpenInternalFile(
ppapi::host::ReplyMessageContext reply_context,
- base::PlatformFileError result,
- base::PlatformFile file,
+ base::File file,
const base::Closure& on_close_callback) {
- if (result == base::PLATFORM_FILE_OK) {
+ if (file.IsValid()) {
on_close_callback_ = on_close_callback;
- check_quota_ = file_system_host_ && file_system_host_->ChecksQuota();
- if (check_quota_) {
+ if (FileOpenForWrite(open_flags_) && file_system_host_->ChecksQuota()) {
+ check_quota_ = true;
file_system_host_->OpenQuotaFile(
this,
file_system_url_,
base::Bind(&PepperFileIOHost::DidOpenQuotaFile,
- weak_factory_.GetWeakPtr(),
+ AsWeakPtr(),
reply_context,
- file));
+ base::Passed(&file)));
return;
}
}
- ExecutePlatformOpenFileCallback(
- reply_context, result, base::PassPlatformFile(&file), true);
+ DCHECK(!file_.IsValid());
+ base::File::Error error =
+ file.IsValid() ? base::File::FILE_OK : file.error_details();
+ file_.SetFile(file.Pass());
+ OnOpenProxyCallback(reply_context, error);
}
void PepperFileIOHost::GotResolvedRenderProcessId(
ppapi::host::ReplyMessageContext reply_context,
base::FilePath path,
- int platform_file_flags,
+ int file_flags,
base::ProcessId resolved_render_process_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
resolved_render_process_id_ = resolved_render_process_id;
- base::FileUtilProxy::CreateOrOpen(
- file_message_loop_,
+ file_.CreateOrOpen(
path,
- platform_file_flags,
- base::Bind(&PepperFileIOHost::ExecutePlatformOpenFileCallback,
- weak_factory_.GetWeakPtr(),
+ file_flags,
+ base::Bind(&PepperFileIOHost::OnOpenProxyCallback,
+ AsWeakPtr(),
reply_context));
}
@@ -279,66 +311,16 @@ int32_t PepperFileIOHost::OnHostMsgTouch(
if (rv != PP_OK)
return rv;
- if (!base::FileUtilProxy::Touch(
- file_message_loop_,
- file_,
+ if (!file_.SetTimes(
PPTimeToTime(last_access_time),
PPTimeToTime(last_modified_time),
base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback,
- weak_factory_.GetWeakPtr(),
- context->MakeReplyMessageContext())))
+ AsWeakPtr(),
+ context->MakeReplyMessageContext()))) {
return PP_ERROR_FAILED;
-
- state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
- return PP_OK_COMPLETIONPENDING;
-}
-
-int32_t PepperFileIOHost::OnHostMsgWrite(
- ppapi::host::HostMessageContext* context,
- int64_t offset,
- const std::string& buffer) {
- int32_t rv = state_manager_.CheckOperationState(
- FileIOStateManager::OPERATION_WRITE, true);
- if (rv != PP_OK)
- return rv;
- if (offset < 0)
- return PP_ERROR_BADARGUMENT;
-
- if (check_quota_) {
- int64_t actual_offset =
- (open_flags_ & PP_FILEOPENFLAG_APPEND) ? max_written_offset_ : offset;
-
- uint64_t max_offset = actual_offset + buffer.size();
- if (max_offset > static_cast<uint64_t>(std::numeric_limits<int64_t>::max()))
- return PP_ERROR_FAILED; // max_offset overflows.
- int64_t amount = static_cast<int64_t>(max_offset) - max_written_offset_;
-
- // Quota request amounts are restricted to 32 bits so we can use atomics
- // when we move this code to the plugin side of the proxy.
- if (amount > std::numeric_limits<int32_t>::max())
- return PP_ERROR_NOQUOTA;
-
- if (amount > 0) {
- int32_t result = file_system_host_->RequestQuota(
- static_cast<int32_t>(amount),
- base::Bind(&PepperFileIOHost::GotWriteQuota,
- weak_factory_.GetWeakPtr(),
- context->MakeReplyMessageContext(),
- offset, buffer));
- if (result == PP_OK_COMPLETIONPENDING) {
- state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_WRITE);
- return result;
- }
- // RequestQuota returns either PP_OK_COMPLETIONPENDING or the requested
- // quota amount.
- DCHECK(result > 0);
- }
}
- if (!CallWrite(context->MakeReplyMessageContext(), offset, buffer))
- return PP_ERROR_FAILED;
-
- state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_WRITE);
+ state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
return PP_OK_COMPLETIONPENDING;
}
@@ -352,33 +334,16 @@ int32_t PepperFileIOHost::OnHostMsgSetLength(
if (length < 0)
return PP_ERROR_BADARGUMENT;
- if (check_quota_) {
- int64_t amount = length - max_written_offset_;
- // Quota request amounts are restricted to 32 bits so we can use atomics
- // when we move this code to the plugin side of the proxy.
- if (amount > std::numeric_limits<int32_t>::max())
- return PP_ERROR_NOQUOTA;
-
- if (amount > 0) {
- int32_t result = file_system_host_->RequestQuota(
- static_cast<int32_t>(amount),
- base::Bind(&PepperFileIOHost::GotSetLengthQuota,
- weak_factory_.GetWeakPtr(),
- context->MakeReplyMessageContext(),
- length));
- if (result == PP_OK_COMPLETIONPENDING) {
- state_manager_.SetPendingOperation(
- FileIOStateManager::OPERATION_EXCLUSIVE);
- return result;
- }
- // RequestQuota returns either PP_OK_COMPLETIONPENDING or the requested
- // quota amount.
- DCHECK(result > 0);
- }
- }
+ // Quota checks are performed on the plugin side, in order to use the same
+ // quota reservation and request system as Write.
- if (!CallSetLength(context->MakeReplyMessageContext(), length))
+ if (!file_.SetLength(
+ length,
+ base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback,
+ AsWeakPtr(),
+ context->MakeReplyMessageContext()))) {
return PP_ERROR_FAILED;
+ }
state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
return PP_OK_COMPLETIONPENDING;
@@ -391,111 +356,45 @@ int32_t PepperFileIOHost::OnHostMsgFlush(
if (rv != PP_OK)
return rv;
- if (!base::FileUtilProxy::Flush(
- file_message_loop_,
- file_,
+ if (!file_.Flush(
base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback,
- weak_factory_.GetWeakPtr(),
- context->MakeReplyMessageContext())))
+ AsWeakPtr(),
+ context->MakeReplyMessageContext()))) {
return PP_ERROR_FAILED;
+ }
state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
return PP_OK_COMPLETIONPENDING;
}
int32_t PepperFileIOHost::OnHostMsgClose(
- ppapi::host::HostMessageContext* context) {
+ ppapi::host::HostMessageContext* context,
+ const ppapi::FileGrowth& file_growth) {
if (check_quota_) {
- file_system_host_->CloseQuotaFile(this);
+ file_system_host_->CloseQuotaFile(this, file_growth);
check_quota_ = false;
}
- if (file_ != base::kInvalidPlatformFileValue) {
- base::FileUtilProxy::Close(
- file_message_loop_,
- file_,
- base::Bind(&PepperFileIOHost::DidCloseFile,
- weak_factory_.GetWeakPtr()));
- file_ = base::kInvalidPlatformFileValue;
+ if (file_.IsValid()) {
+ file_.Close(base::Bind(&PepperFileIOHost::DidCloseFile,
+ AsWeakPtr()));
}
return PP_OK;
}
void PepperFileIOHost::DidOpenQuotaFile(
ppapi::host::ReplyMessageContext reply_context,
- base::PlatformFile file,
+ base::File file,
int64_t max_written_offset) {
+ DCHECK(!file_.IsValid());
+ DCHECK(file.IsValid());
max_written_offset_ = max_written_offset;
- DCHECK_LE(0, max_written_offset_);
+ file_.SetFile(file.Pass());
- ExecutePlatformOpenFileCallback(
- reply_context, base::PLATFORM_FILE_OK, base::PassPlatformFile(&file),
- true);
+ OnOpenProxyCallback(reply_context, base::File::FILE_OK);
}
-void PepperFileIOHost::GotWriteQuota(
- ppapi::host::ReplyMessageContext reply_context,
- int64_t offset,
- const std::string& buffer,
- int32_t granted) {
- if (granted == 0) {
- reply_context.params.set_result(PP_ERROR_NOQUOTA);
- } else if (!CallWrite(reply_context, offset, buffer)) {
- reply_context.params.set_result(PP_ERROR_FAILED);
- } else {
- max_written_offset_ += granted;
- return;
- }
- // Return the error result set above.
- host()->SendReply(reply_context, PpapiPluginMsg_FileIO_GeneralReply());
- state_manager_.SetOperationFinished();
-}
-
-void PepperFileIOHost::GotSetLengthQuota(
- ppapi::host::ReplyMessageContext reply_context,
- int64_t length,
- int32_t granted) {
- if (granted == 0) {
- reply_context.params.set_result(PP_ERROR_NOQUOTA);
- } else if (!CallSetLength(reply_context, length)) {
- reply_context.params.set_result(PP_ERROR_FAILED);
- } else {
- max_written_offset_ += granted;
- return;
- }
- // Return the error result set above.
- host()->SendReply(reply_context, PpapiPluginMsg_FileIO_GeneralReply());
- state_manager_.SetOperationFinished();
-}
-
-bool PepperFileIOHost::CallWrite(
- ppapi::host::ReplyMessageContext reply_context,
- int64_t offset,
- const std::string& buffer) {
- return base::FileUtilProxy::Write(
- file_message_loop_,
- file_,
- offset,
- buffer.c_str(),
- buffer.size(),
- base::Bind(&PepperFileIOHost::ExecutePlatformWriteCallback,
- weak_factory_.GetWeakPtr(),
- reply_context));
-}
-
-bool PepperFileIOHost::CallSetLength(
- ppapi::host::ReplyMessageContext reply_context,
- int64_t length) {
- return base::FileUtilProxy::Truncate(
- file_message_loop_,
- file_,
- length,
- base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback,
- weak_factory_.GetWeakPtr(),
- reply_context));
-}
-
-void PepperFileIOHost::DidCloseFile(base::PlatformFileError error) {
+void PepperFileIOHost::DidCloseFile(base::File::Error /*error*/) {
// Silently ignore if we fail to close the file.
if (!on_close_callback_.is_null()) {
on_close_callback_.Run();
@@ -517,7 +416,7 @@ int32_t PepperFileIOHost::OnHostMsgRequestOSFileHandle(
render_process_id_,
document_url),
base::Bind(&PepperFileIOHost::GotPluginAllowedToCallRequestOSFileHandle,
- weak_factory_.GetWeakPtr(),
+ AsWeakPtr(),
context->MakeReplyMessageContext()));
return PP_OK_COMPLETIONPENDING;
}
@@ -525,7 +424,7 @@ int32_t PepperFileIOHost::OnHostMsgRequestOSFileHandle(
void PepperFileIOHost::GotPluginAllowedToCallRequestOSFileHandle(
ppapi::host::ReplyMessageContext reply_context,
bool plugin_allowed) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!browser_ppapi_host_->external_plugin() ||
host()->permissions().HasPermission(ppapi::PERMISSION_PRIVATE) ||
plugin_allowed) {
@@ -540,70 +439,57 @@ void PepperFileIOHost::GotPluginAllowedToCallRequestOSFileHandle(
void PepperFileIOHost::ExecutePlatformGeneralCallback(
ppapi::host::ReplyMessageContext reply_context,
- base::PlatformFileError error_code) {
- reply_context.params.set_result(
- ppapi::PlatformFileErrorToPepperError(error_code));
+ base::File::Error error_code) {
+ reply_context.params.set_result(ppapi::FileErrorToPepperError(error_code));
host()->SendReply(reply_context, PpapiPluginMsg_FileIO_GeneralReply());
state_manager_.SetOperationFinished();
}
-void PepperFileIOHost::ExecutePlatformOpenFileCallback(
+void PepperFileIOHost::OnOpenProxyCallback(
ppapi::host::ReplyMessageContext reply_context,
- base::PlatformFileError error_code,
- base::PassPlatformFile file,
- bool unused_created) {
- int32_t pp_error = ppapi::PlatformFileErrorToPepperError(error_code);
- if (pp_error == PP_OK)
- state_manager_.SetOpenSucceed();
+ base::File::Error error_code) {
+ int32_t pp_error = ppapi::FileErrorToPepperError(error_code);
+ if (file_.IsValid() && !AddFileToReplyContext(open_flags_, &reply_context))
+ pp_error = PP_ERROR_FAILED;
- DCHECK(file_ == base::kInvalidPlatformFileValue);
- file_ = file.ReleaseValue();
-
- if (file_ != base::kInvalidPlatformFileValue) {
- int32_t flags_to_send = open_flags_;
- if (!host()->permissions().HasPermission(ppapi::PERMISSION_DEV)) {
- // IMPORTANT: Clear PP_FILEOPENFLAG_WRITE and PP_FILEOPENFLAG_APPEND so
- // the plugin can't write and so bypass our quota checks.
- flags_to_send =
- open_flags_ & ~(PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_APPEND);
- }
- if (!AddFileToReplyContext(flags_to_send, &reply_context))
- pp_error = PP_ERROR_FAILED;
+ PP_Resource quota_file_system = 0;
+ if (pp_error == PP_OK) {
+ state_manager_.SetOpenSucceed();
+ // A non-zero resource id signals the plugin side to check quota.
+ if (check_quota_)
+ quota_file_system = file_system_host_->pp_resource();
}
+
reply_context.params.set_result(pp_error);
- host()->SendReply(reply_context, PpapiPluginMsg_FileIO_OpenReply());
+ host()->SendReply(
+ reply_context,
+ PpapiPluginMsg_FileIO_OpenReply(quota_file_system, max_written_offset_));
state_manager_.SetOperationFinished();
}
-void PepperFileIOHost::ExecutePlatformWriteCallback(
- ppapi::host::ReplyMessageContext reply_context,
- base::PlatformFileError error_code,
- int bytes_written) {
- // On the plugin side, the callback expects a parameter with different meaning
- // depends on whether is negative or not. It is the result here. We translate
- // for the callback.
- int32_t pp_error = ppapi::PlatformFileErrorToPepperError(error_code);
- reply_context.params.set_result(ErrorOrByteNumber(pp_error, bytes_written));
- host()->SendReply(reply_context, PpapiPluginMsg_FileIO_GeneralReply());
- state_manager_.SetOperationFinished();
+void PepperFileIOHost::SendOpenErrorReply(
+ ppapi::host::ReplyMessageContext reply_context) {
+ host()->SendReply(reply_context, PpapiPluginMsg_FileIO_OpenReply(0, 0));
}
bool PepperFileIOHost::AddFileToReplyContext(
int32_t open_flags,
ppapi::host::ReplyMessageContext* reply_context) const {
- base::ProcessId plugin_process_id;
- if (browser_ppapi_host_->in_process()) {
+ base::ProcessId plugin_process_id =
+ base::GetProcId(browser_ppapi_host_->GetPluginProcessHandle());
+ if (plugin_process_id == base::kNullProcessId)
plugin_process_id = resolved_render_process_id_;
- } else {
- plugin_process_id = base::GetProcId(
- browser_ppapi_host_->GetPluginProcessHandle());
- }
- IPC::PlatformFileForTransit transit_file = BrokerGetFileHandleForProcess(
- file_, plugin_process_id, false);
+
+ IPC::PlatformFileForTransit transit_file =
+ BrokerGetFileHandleForProcess(file_.GetPlatformFile(), plugin_process_id,
+ false);
if (transit_file == IPC::InvalidPlatformFileForTransit())
return false;
+
ppapi::proxy::SerializedHandle file_handle;
- file_handle.set_file_handle(transit_file, open_flags);
+ // A non-zero resource id signals NaClIPCAdapter to create a NaClQuotaDesc.
+ PP_Resource quota_file_io = check_quota_ ? pp_resource() : 0;
+ file_handle.set_file_handle(transit_file, open_flags, quota_file_io);
reply_context->params.AppendHandle(file_handle);
return true;
}
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_file_io_host.h b/chromium/content/browser/renderer_host/pepper/pepper_file_io_host.h
index 9394e6c7f28..abef036c95b 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_file_io_host.h
+++ b/chromium/content/browser/renderer_host/pepper/pepper_file_io_host.h
@@ -9,6 +9,8 @@
#include "base/basictypes.h"
#include "base/callback_forward.h"
+#include "base/files/file.h"
+#include "base/files/file_proxy.h"
#include "base/memory/weak_ptr.h"
#include "base/platform_file.h"
#include "content/browser/renderer_host/pepper/browser_ppapi_host_impl.h"
@@ -22,14 +24,17 @@
#include "url/gurl.h"
#include "webkit/browser/fileapi/file_system_context.h"
+namespace ppapi {
+struct FileGrowth;
+}
+
namespace content {
class PepperFileSystemBrowserHost;
class PepperFileIOHost : public ppapi::host::ResourceHost,
public base::SupportsWeakPtr<PepperFileIOHost> {
public:
- typedef base::Callback<void (base::PlatformFileError)>
- NotifyCloseFileCallback;
+ typedef base::Callback<void(base::File::Error)> NotifyCloseFileCallback;
PepperFileIOHost(BrowserPpapiHostImpl* host,
PP_Instance instance,
@@ -41,18 +46,13 @@ class PepperFileIOHost : public ppapi::host::ResourceHost,
const IPC::Message& msg,
ppapi::host::HostMessageContext* context) OVERRIDE;
- // Direct access for PepperFileSystemBrowserHost.
- int64_t max_written_offset() const { return max_written_offset_; }
- void set_max_written_offset(int64_t max_written_offset) {
- max_written_offset_ = max_written_offset;
- }
-
struct UIThreadStuff {
UIThreadStuff();
~UIThreadStuff();
base::ProcessId resolved_render_process_id;
scoped_refptr<fileapi::FileSystemContext> file_system_context;
};
+
private:
int32_t OnHostMsgOpen(ppapi::host::HostMessageContext* context,
PP_Resource file_ref_resource,
@@ -60,12 +60,10 @@ class PepperFileIOHost : public ppapi::host::ResourceHost,
int32_t OnHostMsgTouch(ppapi::host::HostMessageContext* context,
PP_Time last_access_time,
PP_Time last_modified_time);
- int32_t OnHostMsgWrite(ppapi::host::HostMessageContext* context,
- int64_t offset,
- const std::string& buffer);
int32_t OnHostMsgSetLength(ppapi::host::HostMessageContext* context,
int64_t length);
- int32_t OnHostMsgClose(ppapi::host::HostMessageContext* context);
+ int32_t OnHostMsgClose(ppapi::host::HostMessageContext* context,
+ const ppapi::FileGrowth& file_growth);
int32_t OnHostMsgFlush(ppapi::host::HostMessageContext* context);
int32_t OnHostMsgRequestOSFileHandle(
ppapi::host::HostMessageContext* context);
@@ -74,55 +72,39 @@ class PepperFileIOHost : public ppapi::host::ResourceHost,
ppapi::host::ReplyMessageContext reply_context,
bool plugin_allowed);
- // Callback handlers. These mostly convert the PlatformFileError to the
+ // Callback handlers. These mostly convert the File::Error to the
// PP_Error code and send back the reply. Note that the argument
// ReplyMessageContext is copied so that we have a closure containing all
// necessary information to reply.
void ExecutePlatformGeneralCallback(
ppapi::host::ReplyMessageContext reply_context,
- base::PlatformFileError error_code);
- void ExecutePlatformOpenFileCallback(
- ppapi::host::ReplyMessageContext reply_context,
- base::PlatformFileError error_code,
- base::PassPlatformFile file,
- bool unused_created);
- void ExecutePlatformWriteCallback(
- ppapi::host::ReplyMessageContext reply_context,
- base::PlatformFileError error_code,
- int bytes_written);
+ base::File::Error error_code);
+
+ void OnOpenProxyCallback(ppapi::host::ReplyMessageContext reply_context,
+ base::File::Error error_code);
void GotUIThreadStuffForInternalFileSystems(
ppapi::host::ReplyMessageContext reply_context,
int platform_file_flags,
UIThreadStuff ui_thread_stuff);
- void DidOpenInternalFile(
- ppapi::host::ReplyMessageContext reply_context,
- base::PlatformFileError result,
- base::PlatformFile file,
- const base::Closure& on_close_callback);
+ void DidOpenInternalFile(ppapi::host::ReplyMessageContext reply_context,
+ base::File file,
+ const base::Closure& on_close_callback);
void GotResolvedRenderProcessId(
ppapi::host::ReplyMessageContext reply_context,
base::FilePath path,
- int platform_file_flags,
+ int file_flags,
base::ProcessId resolved_render_process_id);
void DidOpenQuotaFile(ppapi::host::ReplyMessageContext reply_context,
- base::PlatformFile file,
+ base::File file,
int64_t max_written_offset);
- void GotWriteQuota(ppapi::host::ReplyMessageContext reply_context,
- int64_t offset,
- const std::string& buffer,
- int32_t granted);
- void GotSetLengthQuota(ppapi::host::ReplyMessageContext reply_context,
- int64_t length,
- int32_t granted);
- bool CallWrite(ppapi::host::ReplyMessageContext reply_context,
- int64_t offset,
- const std::string& buffer);
bool CallSetLength(ppapi::host::ReplyMessageContext reply_context,
int64_t length);
- void DidCloseFile(base::PlatformFileError error);
+ void DidCloseFile(base::File::Error error);
+
+ void SendOpenErrorReply(ppapi::host::ReplyMessageContext reply_context);
// Adds file_ to |reply_context| with the specified |open_flags|.
bool AddFileToReplyContext(
@@ -135,7 +117,7 @@ class PepperFileIOHost : public ppapi::host::ResourceHost,
int render_process_id_;
base::ProcessId resolved_render_process_id_;
- base::PlatformFile file_;
+ base::FileProxy file_;
int32_t open_flags_;
// The file system type specified in the Open() call. This will be
@@ -153,10 +135,6 @@ class PepperFileIOHost : public ppapi::host::ResourceHost,
ppapi::FileIOStateManager state_manager_;
- scoped_refptr<base::MessageLoopProxy> file_message_loop_;
-
- base::WeakPtrFactory<PepperFileIOHost> weak_factory_;
-
DISALLOW_COPY_AND_ASSIGN(PepperFileIOHost);
};
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_file_ref_host.cc b/chromium/content/browser/renderer_host/pepper/pepper_file_ref_host.cc
index 75350a03632..a8d362d36e8 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_file_ref_host.cc
+++ b/chromium/content/browser/renderer_host/pepper/pepper_file_ref_host.cc
@@ -23,8 +23,7 @@ using ppapi::host::ResourceHost;
namespace content {
-PepperFileRefBackend::~PepperFileRefBackend() {
-}
+PepperFileRefBackend::~PepperFileRefBackend() {}
PepperFileRefHost::PepperFileRefHost(BrowserPpapiHost* host,
PP_Instance instance,
@@ -39,9 +38,8 @@ PepperFileRefHost::PepperFileRefHost(BrowserPpapiHost* host,
int render_process_id;
int unused;
- if (!host->GetRenderViewIDsForInstance(instance,
- &render_process_id,
- &unused)) {
+ if (!host->GetRenderFrameIDsForInstance(
+ instance, &render_process_id, &unused)) {
return;
}
@@ -64,16 +62,22 @@ PepperFileRefHost::PepperFileRefHost(BrowserPpapiHost* host,
fs_type_ = file_system_host->GetType();
if ((fs_type_ != PP_FILESYSTEMTYPE_LOCALPERSISTENT) &&
(fs_type_ != PP_FILESYSTEMTYPE_LOCALTEMPORARY) &&
+ (fs_type_ != PP_FILESYSTEMTYPE_EXTERNAL) &&
(fs_type_ != PP_FILESYSTEMTYPE_ISOLATED)) {
DLOG(ERROR) << "Unsupported filesystem type: " << fs_type_;
return;
}
+ if ((fs_type_ == PP_FILESYSTEMTYPE_EXTERNAL) &&
+ (!file_system_host->GetRootUrl().is_valid())) {
+ DLOG(ERROR) << "Native external filesystems are not supported by this "
+ << "constructor.";
+ return;
+ }
- backend_.reset(new PepperInternalFileRefBackend(
- host->GetPpapiHost(),
- render_process_id,
- file_system_host->AsWeakPtr(),
- path));
+ backend_.reset(new PepperInternalFileRefBackend(host->GetPpapiHost(),
+ render_process_id,
+ file_system_host->AsWeakPtr(),
+ path));
}
PepperFileRefHost::PepperFileRefHost(BrowserPpapiHost* host,
@@ -88,23 +92,18 @@ PepperFileRefHost::PepperFileRefHost(BrowserPpapiHost* host,
int render_process_id;
int unused;
- if (!host->GetRenderViewIDsForInstance(instance,
- &render_process_id,
- &unused)) {
+ if (!host->GetRenderFrameIDsForInstance(
+ instance, &render_process_id, &unused)) {
return;
}
- backend_.reset(new PepperExternalFileRefBackend(host->GetPpapiHost(),
- render_process_id,
- external_path));
+ backend_.reset(new PepperExternalFileRefBackend(
+ host->GetPpapiHost(), render_process_id, external_path));
}
-PepperFileRefHost::~PepperFileRefHost() {
-}
+PepperFileRefHost::~PepperFileRefHost() {}
-bool PepperFileRefHost::IsFileRefHost() {
- return true;
-}
+bool PepperFileRefHost::IsFileRefHost() { return true; }
PP_FileSystemType PepperFileRefHost::GetFileSystemType() const {
return fs_type_;
@@ -157,35 +156,29 @@ int32_t PepperFileRefHost::OnResourceMessageReceived(
if (!backend_)
return PP_ERROR_FAILED;
- IPC_BEGIN_MESSAGE_MAP(PepperFileRefHost, msg)
+ PPAPI_BEGIN_MESSAGE_MAP(PepperFileRefHost, msg)
PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileRef_MakeDirectory,
- OnMakeDirectory);
- PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileRef_Touch,
- OnTouch);
- PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileRef_Delete,
- OnDelete);
- PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileRef_Rename,
- OnRename);
- PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileRef_Query,
- OnQuery);
+ OnMakeDirectory)
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileRef_Touch, OnTouch)
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileRef_Delete, OnDelete)
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileRef_Rename, OnRename)
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileRef_Query, OnQuery)
PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
- PpapiHostMsg_FileRef_ReadDirectoryEntries,
- OnReadDirectoryEntries);
+ PpapiHostMsg_FileRef_ReadDirectoryEntries, OnReadDirectoryEntries)
PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileRef_GetAbsolutePath,
- OnGetAbsolutePath);
-
- IPC_END_MESSAGE_MAP()
+ OnGetAbsolutePath)
+ PPAPI_END_MESSAGE_MAP()
return PP_ERROR_FAILED;
}
int32_t PepperFileRefHost::OnMakeDirectory(
ppapi::host::HostMessageContext* context,
- bool make_ancestors) {
+ int32_t make_directory_flags) {
int32_t rv = CanCreate();
if (rv != PP_OK)
return rv;
return backend_->MakeDirectory(context->MakeReplyMessageContext(),
- make_ancestors);
+ make_directory_flags);
}
int32_t PepperFileRefHost::OnTouch(ppapi::host::HostMessageContext* context,
@@ -196,9 +189,8 @@ int32_t PepperFileRefHost::OnTouch(ppapi::host::HostMessageContext* context,
int32_t rv = CanCreate();
if (rv != PP_OK)
return rv;
- return backend_->Touch(context->MakeReplyMessageContext(),
- last_access_time,
- last_modified_time);
+ return backend_->Touch(
+ context->MakeReplyMessageContext(), last_access_time, last_modified_time);
}
int32_t PepperFileRefHost::OnDelete(ppapi::host::HostMessageContext* context) {
@@ -229,8 +221,7 @@ int32_t PepperFileRefHost::OnRename(ppapi::host::HostMessageContext* context,
if (rv != PP_OK)
return rv;
- return backend_->Rename(context->MakeReplyMessageContext(),
- file_ref_host);
+ return backend_->Rename(context->MakeReplyMessageContext(), file_ref_host);
}
int32_t PepperFileRefHost::OnQuery(ppapi::host::HostMessageContext* context) {
@@ -251,7 +242,7 @@ int32_t PepperFileRefHost::OnReadDirectoryEntries(
int32_t PepperFileRefHost::OnGetAbsolutePath(
ppapi::host::HostMessageContext* context) {
if (!host_->GetPpapiHost()->permissions().HasPermission(
- ppapi::PERMISSION_PRIVATE))
+ ppapi::PERMISSION_PRIVATE))
return PP_ERROR_NOACCESS;
return backend_->GetAbsolutePath(context->MakeReplyMessageContext());
}
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_file_ref_host.h b/chromium/content/browser/renderer_host/pepper/pepper_file_ref_host.h
index 10d9a3ee76e..d850504d175 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_file_ref_host.h
+++ b/chromium/content/browser/renderer_host/pepper/pepper_file_ref_host.h
@@ -29,7 +29,7 @@ class PepperFileRefBackend {
virtual ~PepperFileRefBackend();
virtual int32_t MakeDirectory(ppapi::host::ReplyMessageContext context,
- bool make_ancestors) = 0;
+ int32_t make_directory_flags) = 0;
virtual int32_t Touch(ppapi::host::ReplyMessageContext context,
PP_Time last_accessed_time,
PP_Time last_modified_time) = 0;
@@ -39,8 +39,7 @@ class PepperFileRefBackend {
virtual int32_t Query(ppapi::host::ReplyMessageContext context) = 0;
virtual int32_t ReadDirectoryEntries(
ppapi::host::ReplyMessageContext context) = 0;
- virtual int32_t GetAbsolutePath(
- ppapi::host::ReplyMessageContext context) = 0;
+ virtual int32_t GetAbsolutePath(ppapi::host::ReplyMessageContext context) = 0;
virtual fileapi::FileSystemURL GetFileSystemURL() const = 0;
virtual base::FilePath GetExternalFilePath() const = 0;
@@ -89,7 +88,7 @@ class CONTENT_EXPORT PepperFileRefHost
private:
int32_t OnMakeDirectory(ppapi::host::HostMessageContext* context,
- bool make_ancestors);
+ int32_t make_directory_flags);
int32_t OnTouch(ppapi::host::HostMessageContext* context,
PP_Time last_access_time,
PP_Time last_modified_time);
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_file_system_browser_host.cc b/chromium/content/browser/renderer_host/pepper/pepper_file_system_browser_host.cc
index 740e9b861f8..5eec4105002 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_file_system_browser_host.cc
+++ b/chromium/content/browser/renderer_host/pepper/pepper_file_system_browser_host.cc
@@ -23,7 +23,7 @@
#include "ppapi/shared_impl/file_type_conversion.h"
#include "webkit/browser/fileapi/file_system_operation_runner.h"
#include "webkit/browser/fileapi/isolated_context.h"
-#include "webkit/browser/quota/quota_manager.h"
+#include "webkit/browser/quota/quota_manager_proxy.h"
#include "webkit/common/fileapi/file_system_util.h"
#include "webkit/common/quota/quota_types.h"
@@ -34,9 +34,9 @@ namespace {
// This is the minimum amount of quota we reserve per file system.
const int64_t kMinimumQuotaReservationSize = 1024 * 1024; // 1 MB
-scoped_refptr<fileapi::FileSystemContext>
-GetFileSystemContextFromRenderId(int render_process_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+scoped_refptr<fileapi::FileSystemContext> GetFileSystemContextFromRenderId(
+ int render_process_id) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
RenderProcessHost* host = RenderProcessHost::FromID(render_process_id);
if (!host)
return NULL;
@@ -48,16 +48,6 @@ GetFileSystemContextFromRenderId(int render_process_id) {
} // namespace
-PepperFileSystemBrowserHost::QuotaRequest::QuotaRequest(
- int32_t amount_arg,
- const RequestQuotaCallback& callback_arg)
- : amount(amount_arg),
- callback(callback_arg) {
-}
-
-PepperFileSystemBrowserHost::QuotaRequest::~QuotaRequest() {
-}
-
PepperFileSystemBrowserHost::PepperFileSystemBrowserHost(BrowserPpapiHost* host,
PP_Instance instance,
PP_Resource resource,
@@ -70,10 +60,17 @@ PepperFileSystemBrowserHost::PepperFileSystemBrowserHost(BrowserPpapiHost* host,
file_system_context_(NULL),
reserved_quota_(0),
reserving_quota_(false),
- weak_factory_(this) {
-}
+ weak_factory_(this) {}
PepperFileSystemBrowserHost::~PepperFileSystemBrowserHost() {
+ // If |files_| is not empty, the plugin failed to close some files. It must
+ // have crashed.
+ if (!files_.empty()) {
+ file_system_context_->default_file_task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&QuotaReservation::OnClientCrash, quota_reservation_));
+ }
+
// All FileRefs and FileIOs that reference us must have been destroyed. Cancel
// all pending file system operations.
if (file_system_operation_runner_)
@@ -85,8 +82,8 @@ void PepperFileSystemBrowserHost::OpenExisting(const GURL& root_url,
root_url_ = root_url;
int render_process_id = 0;
int unused;
- if (!browser_ppapi_host_->GetRenderViewIDsForInstance(
- pp_instance(), &render_process_id, &unused)) {
+ if (!browser_ppapi_host_->GetRenderFrameIDsForInstance(
+ pp_instance(), &render_process_id, &unused)) {
NOTREACHED();
}
called_open_ = true;
@@ -97,26 +94,26 @@ void PepperFileSystemBrowserHost::OpenExisting(const GURL& root_url,
FROM_HERE,
base::Bind(&GetFileSystemContextFromRenderId, render_process_id),
base::Bind(&PepperFileSystemBrowserHost::OpenExistingFileSystem,
- weak_factory_.GetWeakPtr(), callback));
+ weak_factory_.GetWeakPtr(),
+ callback));
}
int32_t PepperFileSystemBrowserHost::OnResourceMessageReceived(
const IPC::Message& msg,
ppapi::host::HostMessageContext* context) {
- IPC_BEGIN_MESSAGE_MAP(PepperFileSystemBrowserHost, msg)
- PPAPI_DISPATCH_HOST_RESOURCE_CALL(
- PpapiHostMsg_FileSystem_Open,
- OnHostMsgOpen)
+ PPAPI_BEGIN_MESSAGE_MAP(PepperFileSystemBrowserHost, msg)
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileSystem_Open,
+ OnHostMsgOpen)
PPAPI_DISPATCH_HOST_RESOURCE_CALL(
PpapiHostMsg_FileSystem_InitIsolatedFileSystem,
OnHostMsgInitIsolatedFileSystem)
- IPC_END_MESSAGE_MAP()
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileSystem_ReserveQuota,
+ OnHostMsgReserveQuota)
+ PPAPI_END_MESSAGE_MAP()
return PP_ERROR_FAILED;
}
-bool PepperFileSystemBrowserHost::IsFileSystemHost() {
- return true;
-}
+bool PepperFileSystemBrowserHost::IsFileSystemHost() { return true; }
void PepperFileSystemBrowserHost::OpenQuotaFile(
PepperFileIOHost* file_io_host,
@@ -129,10 +126,7 @@ void PepperFileSystemBrowserHost::OpenQuotaFile(
base::PostTaskAndReplyWithResult(
file_system_context_->default_file_task_runner(),
FROM_HERE,
- base::Bind(&QuotaReservation::OpenFile,
- quota_reservation_,
- id,
- url),
+ base::Bind(&QuotaReservation::OpenFile, quota_reservation_, id, url),
callback);
} else {
NOTREACHED();
@@ -140,12 +134,11 @@ void PepperFileSystemBrowserHost::OpenQuotaFile(
}
void PepperFileSystemBrowserHost::CloseQuotaFile(
- PepperFileIOHost* file_io_host) {
+ PepperFileIOHost* file_io_host,
+ const ppapi::FileGrowth& file_growth) {
int32_t id = file_io_host->pp_resource();
- int64_t max_written_offset = 0;
FileMap::iterator it = files_.find(id);
if (it != files_.end()) {
- max_written_offset = file_io_host->max_written_offset();
files_.erase(it);
} else {
NOTREACHED();
@@ -154,29 +147,8 @@ void PepperFileSystemBrowserHost::CloseQuotaFile(
file_system_context_->default_file_task_runner()->PostTask(
FROM_HERE,
- base::Bind(&QuotaReservation::CloseFile,
- quota_reservation_,
- id,
- max_written_offset));
-}
-
-int32_t PepperFileSystemBrowserHost::RequestQuota(
- int32_t amount,
- const RequestQuotaCallback& callback) {
- DCHECK(amount >= 0);
- if (!reserving_quota_ && reserved_quota_ >= amount) {
- reserved_quota_ -= amount;
- return amount;
- }
-
- // Queue up a pending quota request.
- pending_quota_requests_.push(QuotaRequest(amount, callback));
-
- // Reserve more quota if we haven't already.
- if (!reserving_quota_)
- ReserveQuota(amount);
-
- return PP_OK_COMPLETIONPENDING;
+ base::Bind(
+ &QuotaReservation::CloseFile, quota_reservation_, id, file_growth));
}
int32_t PepperFileSystemBrowserHost::OnHostMsgOpen(
@@ -197,10 +169,9 @@ int32_t PepperFileSystemBrowserHost::OnHostMsgOpen(
int render_process_id = 0;
int unused;
- if (!browser_ppapi_host_->GetRenderViewIDsForInstance(pp_instance(),
- &render_process_id,
- &unused)) {
- return PP_ERROR_FAILED;
+ if (!browser_ppapi_host_->GetRenderFrameIDsForInstance(
+ pp_instance(), &render_process_id, &unused)) {
+ return PP_ERROR_FAILED;
}
BrowserThread::PostTaskAndReplyWithResult(
@@ -239,15 +210,17 @@ void PepperFileSystemBrowserHost::OpenFileSystem(
scoped_refptr<fileapi::FileSystemContext> file_system_context) {
if (!file_system_context.get()) {
OpenFileSystemComplete(
- reply_context, GURL(), std::string(), base::PLATFORM_FILE_ERROR_FAILED);
+ reply_context, GURL(), std::string(), base::File::FILE_ERROR_FAILED);
return;
}
SetFileSystemContext(file_system_context);
- GURL origin = browser_ppapi_host_->GetDocumentURLForInstance(
- pp_instance()).GetOrigin();
- file_system_context_->OpenFileSystem(origin, file_system_type,
+ GURL origin =
+ browser_ppapi_host_->GetDocumentURLForInstance(pp_instance()).GetOrigin();
+ file_system_context_->OpenFileSystem(
+ origin,
+ file_system_type,
fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
base::Bind(&PepperFileSystemBrowserHost::OpenFileSystemComplete,
weak_factory_.GetWeakPtr(),
@@ -258,8 +231,8 @@ void PepperFileSystemBrowserHost::OpenFileSystemComplete(
ppapi::host::ReplyMessageContext reply_context,
const GURL& root,
const std::string& /* unused */,
- base::PlatformFileError error) {
- int32 pp_error = ppapi::PlatformFileErrorToPepperError(error);
+ base::File::Error error) {
+ int32 pp_error = ppapi::FileErrorToPepperError(error);
if (pp_error == PP_OK) {
opened_ = true;
root_url_ = root;
@@ -289,7 +262,8 @@ void PepperFileSystemBrowserHost::OpenIsolatedFileSystem(
root_url_ = GURL(fileapi::GetIsolatedFileSystemRootURIString(
browser_ppapi_host_->GetDocumentURLForInstance(pp_instance()).GetOrigin(),
- fsid, ppapi::IsolatedFileSystemTypeToRootName(type)));
+ fsid,
+ ppapi::IsolatedFileSystemTypeToRootName(type)));
if (!root_url_.is_valid()) {
SendReplyForIsolatedFileSystem(reply_context, fsid, PP_ERROR_FAILED);
return;
@@ -314,8 +288,8 @@ void PepperFileSystemBrowserHost::OpenPluginPrivateFileSystem(
ppapi::host::ReplyMessageContext reply_context,
const std::string& fsid,
scoped_refptr<fileapi::FileSystemContext> file_system_context) {
- GURL origin = browser_ppapi_host_->GetDocumentURLForInstance(
- pp_instance()).GetOrigin();
+ GURL origin =
+ browser_ppapi_host_->GetDocumentURLForInstance(pp_instance()).GetOrigin();
if (!origin.is_valid()) {
SendReplyForIsolatedFileSystem(reply_context, fsid, PP_ERROR_FAILED);
return;
@@ -328,18 +302,23 @@ void PepperFileSystemBrowserHost::OpenPluginPrivateFileSystem(
}
file_system_context->OpenPluginPrivateFileSystem(
- origin, fileapi::kFileSystemTypePluginPrivate, fsid, plugin_id,
+ origin,
+ fileapi::kFileSystemTypePluginPrivate,
+ fsid,
+ plugin_id,
fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
base::Bind(
&PepperFileSystemBrowserHost::OpenPluginPrivateFileSystemComplete,
- weak_factory_.GetWeakPtr(), reply_context, fsid));
+ weak_factory_.GetWeakPtr(),
+ reply_context,
+ fsid));
}
void PepperFileSystemBrowserHost::OpenPluginPrivateFileSystemComplete(
ppapi::host::ReplyMessageContext reply_context,
const std::string& fsid,
- base::PlatformFileError error) {
- int32 pp_error = ppapi::PlatformFileErrorToPepperError(error);
+ base::File::Error error) {
+ int32 pp_error = ppapi::FileErrorToPepperError(error);
if (pp_error == PP_OK)
opened_ = true;
SendReplyForIsolatedFileSystem(reply_context, fsid, pp_error);
@@ -360,16 +339,16 @@ int32_t PepperFileSystemBrowserHost::OnHostMsgInitIsolatedFileSystem(
int render_process_id = 0;
int unused;
- if (!browser_ppapi_host_->GetRenderViewIDsForInstance(pp_instance(),
- &render_process_id,
- &unused)) {
+ if (!browser_ppapi_host_->GetRenderFrameIDsForInstance(
+ pp_instance(), &render_process_id, &unused)) {
fileapi::IsolatedContext::GetInstance()->RevokeFileSystem(fsid);
return PP_ERROR_FAILED;
}
root_url_ = GURL(fileapi::GetIsolatedFileSystemRootURIString(
browser_ppapi_host_->GetDocumentURLForInstance(pp_instance()).GetOrigin(),
- fsid, ppapi::IsolatedFileSystemTypeToRootName(type)));
+ fsid,
+ ppapi::IsolatedFileSystemTypeToRootName(type)));
BrowserThread::PostTaskAndReplyWithResult(
BrowserThread::UI,
@@ -377,7 +356,35 @@ int32_t PepperFileSystemBrowserHost::OnHostMsgInitIsolatedFileSystem(
base::Bind(&GetFileSystemContextFromRenderId, render_process_id),
base::Bind(&PepperFileSystemBrowserHost::OpenIsolatedFileSystem,
weak_factory_.GetWeakPtr(),
- context->MakeReplyMessageContext(), fsid, type));
+ context->MakeReplyMessageContext(),
+ fsid,
+ type));
+ return PP_OK_COMPLETIONPENDING;
+}
+
+int32_t PepperFileSystemBrowserHost::OnHostMsgReserveQuota(
+ ppapi::host::HostMessageContext* context,
+ int64_t amount,
+ const ppapi::FileGrowthMap& file_growths) {
+ DCHECK(ChecksQuota());
+ DCHECK_GT(amount, 0);
+
+ if (reserving_quota_)
+ return PP_ERROR_INPROGRESS;
+ reserving_quota_ = true;
+
+ int64_t reservation_amount =
+ std::max<int64_t>(kMinimumQuotaReservationSize, amount);
+ file_system_context_->default_file_task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&QuotaReservation::ReserveQuota,
+ quota_reservation_,
+ reservation_amount,
+ file_growths,
+ base::Bind(&PepperFileSystemBrowserHost::GotReservedQuota,
+ weak_factory_.GetWeakPtr(),
+ context->MakeReplyMessageContext())));
+
return PP_OK_COMPLETIONPENDING;
}
@@ -402,7 +409,7 @@ void PepperFileSystemBrowserHost::SendReplyForIsolatedFileSystem(
void PepperFileSystemBrowserHost::SetFileSystemContext(
scoped_refptr<fileapi::FileSystemContext> file_system_context) {
file_system_context_ = file_system_context;
- if (type_ != PP_FILESYSTEMTYPE_EXTERNAL) {
+ if (type_ != PP_FILESYSTEMTYPE_EXTERNAL || root_url_.is_valid()) {
file_system_operation_runner_ =
file_system_context_->CreateFileSystemOperationRunner();
}
@@ -413,7 +420,7 @@ bool PepperFileSystemBrowserHost::ShouldCreateQuotaReservation() const {
if (!ppapi::FileSystemTypeHasQuota(type_))
return false;
- // For file system types with quota, ome origins have unlimited storage.
+ // For file system types with quota, some origins have unlimited storage.
quota::QuotaManagerProxy* quota_manager_proxy =
file_system_context_->quota_manager_proxy();
CHECK(quota_manager_proxy);
@@ -447,67 +454,18 @@ void PepperFileSystemBrowserHost::GotQuotaReservation(
callback.Run();
}
-void PepperFileSystemBrowserHost::ReserveQuota(int32_t amount) {
- DCHECK(!reserving_quota_);
- reserving_quota_ = true;
-
- // Get the max_written_offset for each open file.
- QuotaReservation::OffsetMap max_written_offsets;
- for (FileMap::iterator it = files_.begin(); it != files_.end(); ++ it) {
- max_written_offsets.insert(
- std::make_pair(it->first, it->second->max_written_offset()));
- }
-
- int64_t reservation_amount = std::max<int64_t>(kMinimumQuotaReservationSize,
- amount);
- file_system_context_->default_file_task_runner()->PostTask(
- FROM_HERE,
- base::Bind(&QuotaReservation::ReserveQuota,
- quota_reservation_,
- reservation_amount,
- max_written_offsets,
- base::Bind(&PepperFileSystemBrowserHost::GotReservedQuota,
- weak_factory_.GetWeakPtr())));
-}
-
void PepperFileSystemBrowserHost::GotReservedQuota(
+ ppapi::host::ReplyMessageContext reply_context,
int64_t amount,
- const QuotaReservation::OffsetMap& max_written_offsets) {
+ const ppapi::FileSizeMap& file_sizes) {
DCHECK(reserving_quota_);
reserving_quota_ = false;
reserved_quota_ = amount;
- // Update open files with their new base sizes. This won't write over any
- // updates since the files are waiting for quota and can't write.
- for (FileMap::iterator it = files_.begin(); it != files_.end(); ++ it) {
- QuotaReservation::OffsetMap::const_iterator offset_it =
- max_written_offsets.find(it->first);
- if (offset_it != max_written_offsets.end())
- it->second->set_max_written_offset(offset_it->second);
- else
- NOTREACHED();
- }
-
- DCHECK(!pending_quota_requests_.empty());
- // If we can't grant the first request after refreshing reserved_quota_, then
- // fail all pending quota requests to avoid an infinite refresh/fail loop.
- bool fail_all = reserved_quota_ < pending_quota_requests_.front().amount;
- while (!pending_quota_requests_.empty()) {
- QuotaRequest& request = pending_quota_requests_.front();
- if (fail_all) {
- request.callback.Run(0);
- pending_quota_requests_.pop();
- } else if (reserved_quota_ >= request.amount) {
- reserved_quota_ -= request.amount;
- request.callback.Run(request.amount);
- pending_quota_requests_.pop();
- } else {
- // Refresh the quota reservation for the first pending request that we
- // can't satisfy.
- ReserveQuota(request.amount);
- break;
- }
- }
+ reply_context.params.set_result(PP_OK);
+ host()->SendReply(
+ reply_context,
+ PpapiPluginMsg_FileSystem_ReserveQuotaReply(amount, file_sizes));
}
std::string PepperFileSystemBrowserHost::GetPluginMimeType() const {
@@ -526,19 +484,21 @@ std::string PepperFileSystemBrowserHost::GeneratePluginId(
// types). If we bring this API to stable, we might have to make it more
// general.
- if (!net::IsMimeType(mime_type))
+ std::string top_level_type;
+ std::string subtype;
+ if (!net::ParseMimeTypeWithoutParameter(
+ mime_type, &top_level_type, &subtype) ||
+ !net::IsValidTopLevelMimeType(top_level_type))
return std::string();
- std::string output = mime_type;
// Replace a slash used for type/subtype separator with an underscore.
- // NOTE: This assumes there is only one slash in the MIME type.
- ReplaceFirstSubstringAfterOffset(&output, 0, "/", "_");
+ std::string output = top_level_type + "_" + subtype;
// Verify |output| contains only alphabets, digits, or "._-".
- for (std::string::const_iterator it = output.begin();
- it != output.end(); ++it) {
- if (!IsAsciiAlpha(*it) && !IsAsciiDigit(*it) &&
- *it != '.' && *it != '_' && *it != '-') {
+ for (std::string::const_iterator it = output.begin(); it != output.end();
+ ++it) {
+ if (!IsAsciiAlpha(*it) && !IsAsciiDigit(*it) && *it != '.' && *it != '_' &&
+ *it != '-') {
LOG(WARNING) << "Failed to generate a plugin id.";
return std::string();
}
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_file_system_browser_host.h b/chromium/content/browser/renderer_host/pepper/pepper_file_system_browser_host.h
index f97ddde671d..f6f75fb9aa5 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_file_system_browser_host.h
+++ b/chromium/content/browser/renderer_host/pepper/pepper_file_system_browser_host.h
@@ -18,6 +18,7 @@
#include "ppapi/c/private/ppb_isolated_file_system_private.h"
#include "ppapi/host/host_message_context.h"
#include "ppapi/host/resource_host.h"
+#include "ppapi/shared_impl/file_growth.h"
#include "url/gurl.h"
#include "webkit/browser/fileapi/file_system_context.h"
@@ -72,26 +73,12 @@ class CONTENT_EXPORT PepperFileSystemBrowserHost
const OpenQuotaFileCallback& callback);
// Closes the file. This must be called after OpenQuotaFile and before the
// PepperFileIOHost is destroyed.
- void CloseQuotaFile(PepperFileIOHost* file_io_host);
- // Requests the given amount of quota. Returns the amount requested or
- // PP_OK_COMPLETIONPENDING, in which case the amount granted is returned in
- // the callback. Requests can't partially succeed so the amount granted is
- // either 0 or the amount of the request. Requesting an amount of 0 will
- // return immediately with a 0 result.
- typedef base::Callback<void(int32_t)> RequestQuotaCallback;
- int32_t RequestQuota(int32_t amount,
- const RequestQuotaCallback& callback);
+ void CloseQuotaFile(PepperFileIOHost* file_io_host,
+ const ppapi::FileGrowth& file_growth);
+
private:
friend class PepperFileSystemBrowserHostTest;
- struct QuotaRequest {
- QuotaRequest(int32_t amount, const RequestQuotaCallback& callback);
- ~QuotaRequest();
-
- int32_t amount;
- RequestQuotaCallback callback;
- };
-
void OpenExistingFileSystem(
const base::Closure& callback,
scoped_refptr<fileapi::FileSystemContext> file_system_context);
@@ -99,11 +86,10 @@ class CONTENT_EXPORT PepperFileSystemBrowserHost
ppapi::host::ReplyMessageContext reply_context,
fileapi::FileSystemType file_system_type,
scoped_refptr<fileapi::FileSystemContext> file_system_context);
- void OpenFileSystemComplete(
- ppapi::host::ReplyMessageContext reply_context,
- const GURL& root,
- const std::string& name,
- base::PlatformFileError error);
+ void OpenFileSystemComplete(ppapi::host::ReplyMessageContext reply_context,
+ const GURL& root,
+ const std::string& name,
+ base::File::Error error);
void OpenIsolatedFileSystem(
ppapi::host::ReplyMessageContext reply_context,
const std::string& fsid,
@@ -116,7 +102,7 @@ class CONTENT_EXPORT PepperFileSystemBrowserHost
void OpenPluginPrivateFileSystemComplete(
ppapi::host::ReplyMessageContext reply_context,
const std::string& fsid,
- base::PlatformFileError error);
+ base::File::Error error);
int32_t OnHostMsgOpen(ppapi::host::HostMessageContext* context,
int64_t expected_size);
@@ -124,10 +110,12 @@ class CONTENT_EXPORT PepperFileSystemBrowserHost
ppapi::host::HostMessageContext* context,
const std::string& fsid,
PP_IsolatedFileSystemType_Private type);
+ int32_t OnHostMsgReserveQuota(ppapi::host::HostMessageContext* context,
+ int64_t amount,
+ const ppapi::FileGrowthMap& file_growths);
- void SendReplyForFileSystem(
- ppapi::host::ReplyMessageContext reply_context,
- int32_t pp_error);
+ void SendReplyForFileSystem(ppapi::host::ReplyMessageContext reply_context,
+ int32_t pp_error);
void SendReplyForIsolatedFileSystem(
ppapi::host::ReplyMessageContext reply_context,
const std::string& fsid,
@@ -138,13 +126,15 @@ class CONTENT_EXPORT PepperFileSystemBrowserHost
bool ShouldCreateQuotaReservation() const;
void CreateQuotaReservation(const base::Closure& callback);
- void GotQuotaReservation(
- const base::Closure& callback,
- scoped_refptr<QuotaReservation> quota_reservation);
+ void GotQuotaReservation(const base::Closure& callback,
+ scoped_refptr<QuotaReservation> quota_reservation);
- void ReserveQuota(int32_t amount);
- void GotReservedQuota(int64_t amount,
- const QuotaReservation::OffsetMap& max_written_offsets);
+ void GotReservedQuota(ppapi::host::ReplyMessageContext reply_context,
+ int64_t amount,
+ const ppapi::FileSizeMap& file_sizes);
+ void DidOpenQuotaFile(PP_Resource file_io_resource,
+ const OpenQuotaFileCallback& callback,
+ int64_t max_written_offset);
std::string GetPluginMimeType() const;
@@ -155,7 +145,7 @@ class CONTENT_EXPORT PepperFileSystemBrowserHost
PP_FileSystemType type_;
bool called_open_; // whether open has been called.
- bool opened_; // whether open succeeded.
+ bool opened_; // whether open succeeded.
GURL root_url_;
scoped_refptr<fileapi::FileSystemContext> file_system_context_;
@@ -167,7 +157,6 @@ class CONTENT_EXPORT PepperFileSystemBrowserHost
// destroyed.
typedef std::map<int32_t, PepperFileIOHost*> FileMap;
FileMap files_;
- std::queue<QuotaRequest> pending_quota_requests_;
int64_t reserved_quota_;
bool reserving_quota_;
// Access only on the FileSystemContext's default_file_task_runner().
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_file_system_browser_host_unittest.cc b/chromium/content/browser/renderer_host/pepper/pepper_file_system_browser_host_unittest.cc
index fa07e3938ab..8aa144cf426 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_file_system_browser_host_unittest.cc
+++ b/chromium/content/browser/renderer_host/pepper/pepper_file_system_browser_host_unittest.cc
@@ -15,9 +15,8 @@
namespace content {
-class PepperFileSystemBrowserHostTest
- : public testing::Test,
- public BrowserPpapiHostTest {
+class PepperFileSystemBrowserHostTest : public testing::Test,
+ public BrowserPpapiHostTest {
public:
PepperFileSystemBrowserHostTest() {}
virtual ~PepperFileSystemBrowserHostTest() {}
@@ -25,14 +24,13 @@ class PepperFileSystemBrowserHostTest
virtual void SetUp() OVERRIDE {
PP_Instance pp_instance = 12345;
PP_Resource pp_resource = 67890;
- host_.reset(new PepperFileSystemBrowserHost(
- GetBrowserPpapiHost(), pp_instance, pp_resource,
- PP_FILESYSTEMTYPE_ISOLATED));
+ host_.reset(new PepperFileSystemBrowserHost(GetBrowserPpapiHost(),
+ pp_instance,
+ pp_resource,
+ PP_FILESYSTEMTYPE_ISOLATED));
}
- virtual void TearDown() OVERRIDE {
- host_.reset();
- }
+ virtual void TearDown() OVERRIDE { host_.reset(); }
protected:
std::string GeneratePluginId(const std::string& mime_type) {
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_flash_file_message_filter.cc b/chromium/content/browser/renderer_host/pepper/pepper_flash_file_message_filter.cc
index ba8a8b91368..d4413b20c9c 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_flash_file_message_filter.cc
+++ b/chromium/content/browser/renderer_host/pepper/pepper_flash_file_message_filter.cc
@@ -6,6 +6,7 @@
#include "base/bind.h"
#include "base/file_util.h"
+#include "base/files/file.h"
#include "base/files/file_enumerator.h"
#include "base/threading/sequenced_worker_pool.h"
#include "content/browser/child_process_security_policy_impl.h"
@@ -27,13 +28,13 @@ namespace content {
namespace {
bool CanRead(int process_id, const base::FilePath& path) {
- return ChildProcessSecurityPolicyImpl::GetInstance()->
- CanReadFile(process_id, path);
+ return ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(process_id,
+ path);
}
bool CanCreateReadWrite(int process_id, const base::FilePath& path) {
- return ChildProcessSecurityPolicyImpl::GetInstance()->
- CanCreateReadWriteFile(process_id, path);
+ return ChildProcessSecurityPolicyImpl::GetInstance()->CanCreateReadWriteFile(
+ process_id, path);
}
} // namespace
@@ -43,7 +44,7 @@ PepperFlashFileMessageFilter::PepperFlashFileMessageFilter(
BrowserPpapiHost* host)
: plugin_process_handle_(host->GetPluginProcessHandle()) {
int unused;
- host->GetRenderViewIDsForInstance(instance, &render_process_id_, &unused);
+ host->GetRenderFrameIDsForInstance(instance, &render_process_id_, &unused);
base::FilePath profile_data_directory = host->GetProfileDataDirectory();
std::string plugin_name = host->GetPluginName();
@@ -59,8 +60,7 @@ PepperFlashFileMessageFilter::PepperFlashFileMessageFilter(
}
}
-PepperFlashFileMessageFilter::~PepperFlashFileMessageFilter() {
-}
+PepperFlashFileMessageFilter::~PepperFlashFileMessageFilter() {}
// static
base::FilePath PepperFlashFileMessageFilter::GetDataDirName(
@@ -83,9 +83,9 @@ PepperFlashFileMessageFilter::OverrideTaskRunnerForMessage(
}
int32_t PepperFlashFileMessageFilter::OnResourceMessageReceived(
- const IPC::Message& msg,
- ppapi::host::HostMessageContext* context) {
- IPC_BEGIN_MESSAGE_MAP(PepperFlashFileMessageFilter, msg)
+ const IPC::Message& msg,
+ ppapi::host::HostMessageContext* context) {
+ PPAPI_BEGIN_MESSAGE_MAP(PepperFlashFileMessageFilter, msg)
PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FlashFile_OpenFile,
OnOpenFile)
PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FlashFile_RenameFile,
@@ -99,9 +99,8 @@ int32_t PepperFlashFileMessageFilter::OnResourceMessageReceived(
PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FlashFile_GetDirContents,
OnGetDirContents)
PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
- PpapiHostMsg_FlashFile_CreateTemporaryFile,
- OnCreateTemporaryFile)
- IPC_END_MESSAGE_MAP()
+ PpapiHostMsg_FlashFile_CreateTemporaryFile, OnCreateTemporaryFile)
+ PPAPI_END_MESSAGE_MAP()
return PP_ERROR_FAILED;
}
@@ -110,43 +109,36 @@ int32_t PepperFlashFileMessageFilter::OnOpenFile(
const ppapi::PepperFilePath& path,
int pp_open_flags) {
base::FilePath full_path = ValidateAndConvertPepperFilePath(
- path,
- base::Bind(&CanOpenWithPepperFlags, pp_open_flags));
+ path, base::Bind(&CanOpenWithPepperFlags, pp_open_flags));
if (full_path.empty()) {
- return ppapi::PlatformFileErrorToPepperError(
- base::PLATFORM_FILE_ERROR_ACCESS_DENIED);
+ return ppapi::FileErrorToPepperError(base::File::FILE_ERROR_ACCESS_DENIED);
}
int platform_file_flags = 0;
- if (!ppapi::PepperFileOpenFlagsToPlatformFileFlags(
- pp_open_flags, &platform_file_flags)) {
- return base::PLATFORM_FILE_ERROR_FAILED;
+ if (!ppapi::PepperFileOpenFlagsToPlatformFileFlags(pp_open_flags,
+ &platform_file_flags)) {
+ return base::File::FILE_ERROR_FAILED;
}
- base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
- base::PlatformFile file_handle = base::CreatePlatformFile(
- full_path, platform_file_flags, NULL, &error);
- if (error != base::PLATFORM_FILE_OK) {
- DCHECK_EQ(file_handle, base::kInvalidPlatformFileValue);
- return ppapi::PlatformFileErrorToPepperError(error);
+ base::File file(full_path, platform_file_flags);
+ if (!file.IsValid()) {
+ return ppapi::FileErrorToPepperError(file.error_details());
}
// Make sure we didn't try to open a directory: directory fd shouldn't be
// passed to untrusted processes because they open security holes.
- base::PlatformFileInfo info;
- if (!base::GetPlatformFileInfo(file_handle, &info) || info.is_directory) {
+ base::File::Info info;
+ if (!file.GetInfo(&info) || info.is_directory) {
// When in doubt, throw it out.
- base::ClosePlatformFile(file_handle);
- return ppapi::PlatformFileErrorToPepperError(
- base::PLATFORM_FILE_ERROR_ACCESS_DENIED);
+ return ppapi::FileErrorToPepperError(base::File::FILE_ERROR_ACCESS_DENIED);
}
- IPC::PlatformFileForTransit file = IPC::GetFileHandleForProcess(file_handle,
- plugin_process_handle_, true);
+ IPC::PlatformFileForTransit transit_file =
+ IPC::TakeFileHandleForProcess(file.Pass(), plugin_process_handle_);
ppapi::host::ReplyMessageContext reply_context =
context->MakeReplyMessageContext();
reply_context.params.AppendHandle(ppapi::proxy::SerializedHandle(
- ppapi::proxy::SerializedHandle::FILE, file));
+ ppapi::proxy::SerializedHandle::FILE, transit_file));
SendReply(reply_context, IPC::Message());
return PP_OK_COMPLETIONPENDING;
}
@@ -160,84 +152,77 @@ int32_t PepperFlashFileMessageFilter::OnRenameFile(
base::FilePath to_full_path = ValidateAndConvertPepperFilePath(
to_path, base::Bind(&CanCreateReadWrite));
if (from_full_path.empty() || to_full_path.empty()) {
- return ppapi::PlatformFileErrorToPepperError(
- base::PLATFORM_FILE_ERROR_ACCESS_DENIED);
+ return ppapi::FileErrorToPepperError(base::File::FILE_ERROR_ACCESS_DENIED);
}
bool result = base::Move(from_full_path, to_full_path);
- return ppapi::PlatformFileErrorToPepperError(result ?
- base::PLATFORM_FILE_OK : base::PLATFORM_FILE_ERROR_ACCESS_DENIED);
+ return ppapi::FileErrorToPepperError(
+ result ? base::File::FILE_OK : base::File::FILE_ERROR_ACCESS_DENIED);
}
int32_t PepperFlashFileMessageFilter::OnDeleteFileOrDir(
ppapi::host::HostMessageContext* context,
const ppapi::PepperFilePath& path,
bool recursive) {
- base::FilePath full_path = ValidateAndConvertPepperFilePath(
- path, base::Bind(&CanCreateReadWrite));
+ base::FilePath full_path =
+ ValidateAndConvertPepperFilePath(path, base::Bind(&CanCreateReadWrite));
if (full_path.empty()) {
- return ppapi::PlatformFileErrorToPepperError(
- base::PLATFORM_FILE_ERROR_ACCESS_DENIED);
+ return ppapi::FileErrorToPepperError(base::File::FILE_ERROR_ACCESS_DENIED);
}
bool result = base::DeleteFile(full_path, recursive);
- return ppapi::PlatformFileErrorToPepperError(result ?
- base::PLATFORM_FILE_OK : base::PLATFORM_FILE_ERROR_ACCESS_DENIED);
+ return ppapi::FileErrorToPepperError(
+ result ? base::File::FILE_OK : base::File::FILE_ERROR_ACCESS_DENIED);
}
int32_t PepperFlashFileMessageFilter::OnCreateDir(
ppapi::host::HostMessageContext* context,
const ppapi::PepperFilePath& path) {
- base::FilePath full_path = ValidateAndConvertPepperFilePath(
- path, base::Bind(&CanCreateReadWrite));
+ base::FilePath full_path =
+ ValidateAndConvertPepperFilePath(path, base::Bind(&CanCreateReadWrite));
if (full_path.empty()) {
- return ppapi::PlatformFileErrorToPepperError(
- base::PLATFORM_FILE_ERROR_ACCESS_DENIED);
+ return ppapi::FileErrorToPepperError(base::File::FILE_ERROR_ACCESS_DENIED);
}
bool result = base::CreateDirectory(full_path);
- return ppapi::PlatformFileErrorToPepperError(result ?
- base::PLATFORM_FILE_OK : base::PLATFORM_FILE_ERROR_ACCESS_DENIED);
+ return ppapi::FileErrorToPepperError(
+ result ? base::File::FILE_OK : base::File::FILE_ERROR_ACCESS_DENIED);
}
int32_t PepperFlashFileMessageFilter::OnQueryFile(
ppapi::host::HostMessageContext* context,
const ppapi::PepperFilePath& path) {
- base::FilePath full_path = ValidateAndConvertPepperFilePath(
- path, base::Bind(&CanRead));
+ base::FilePath full_path =
+ ValidateAndConvertPepperFilePath(path, base::Bind(&CanRead));
if (full_path.empty()) {
- return ppapi::PlatformFileErrorToPepperError(
- base::PLATFORM_FILE_ERROR_ACCESS_DENIED);
+ return ppapi::FileErrorToPepperError(base::File::FILE_ERROR_ACCESS_DENIED);
}
- base::PlatformFileInfo info;
+ base::File::Info info;
bool result = base::GetFileInfo(full_path, &info);
context->reply_msg = PpapiPluginMsg_FlashFile_QueryFileReply(info);
- return ppapi::PlatformFileErrorToPepperError(result ?
- base::PLATFORM_FILE_OK : base::PLATFORM_FILE_ERROR_ACCESS_DENIED);
+ return ppapi::FileErrorToPepperError(
+ result ? base::File::FILE_OK : base::File::FILE_ERROR_ACCESS_DENIED);
}
int32_t PepperFlashFileMessageFilter::OnGetDirContents(
ppapi::host::HostMessageContext* context,
const ppapi::PepperFilePath& path) {
- base::FilePath full_path = ValidateAndConvertPepperFilePath(
- path, base::Bind(&CanRead));
+ base::FilePath full_path =
+ ValidateAndConvertPepperFilePath(path, base::Bind(&CanRead));
if (full_path.empty()) {
- return ppapi::PlatformFileErrorToPepperError(
- base::PLATFORM_FILE_ERROR_ACCESS_DENIED);
+ return ppapi::FileErrorToPepperError(base::File::FILE_ERROR_ACCESS_DENIED);
}
ppapi::DirContents contents;
- base::FileEnumerator enumerator(full_path, false,
- base::FileEnumerator::FILES |
- base::FileEnumerator::DIRECTORIES |
- base::FileEnumerator::INCLUDE_DOT_DOT);
+ base::FileEnumerator enumerator(full_path,
+ false,
+ base::FileEnumerator::FILES |
+ base::FileEnumerator::DIRECTORIES |
+ base::FileEnumerator::INCLUDE_DOT_DOT);
while (!enumerator.Next().empty()) {
base::FileEnumerator::FileInfo info = enumerator.GetInfo();
- ppapi::DirEntry entry = {
- info.GetName(),
- info.IsDirectory()
- };
+ ppapi::DirEntry entry = {info.GetName(), info.IsDirectory()};
contents.push_back(entry);
}
@@ -247,42 +232,35 @@ int32_t PepperFlashFileMessageFilter::OnGetDirContents(
int32_t PepperFlashFileMessageFilter::OnCreateTemporaryFile(
ppapi::host::HostMessageContext* context) {
- ppapi::PepperFilePath dir_path(
- ppapi::PepperFilePath::DOMAIN_MODULE_LOCAL, base::FilePath());
+ ppapi::PepperFilePath dir_path(ppapi::PepperFilePath::DOMAIN_MODULE_LOCAL,
+ base::FilePath());
base::FilePath validated_dir_path = ValidateAndConvertPepperFilePath(
dir_path, base::Bind(&CanCreateReadWrite));
if (validated_dir_path.empty() ||
(!base::DirectoryExists(validated_dir_path) &&
!base::CreateDirectory(validated_dir_path))) {
- return ppapi::PlatformFileErrorToPepperError(
- base::PLATFORM_FILE_ERROR_ACCESS_DENIED);
+ return ppapi::FileErrorToPepperError(base::File::FILE_ERROR_ACCESS_DENIED);
}
base::FilePath file_path;
if (!base::CreateTemporaryFileInDir(validated_dir_path, &file_path)) {
- return ppapi::PlatformFileErrorToPepperError(
- base::PLATFORM_FILE_ERROR_FAILED);
+ return ppapi::FileErrorToPepperError(base::File::FILE_ERROR_FAILED);
}
- base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
- base::PlatformFile file_handle = base::CreatePlatformFile(
- file_path,
- base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_TEMPORARY |
- base::PLATFORM_FILE_DELETE_ON_CLOSE,
- NULL, &error);
-
- if (error != base::PLATFORM_FILE_OK) {
- DCHECK_EQ(file_handle, base::kInvalidPlatformFileValue);
- return ppapi::PlatformFileErrorToPepperError(error);
- }
+ base::File file(file_path,
+ base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_READ |
+ base::File::FLAG_WRITE | base::File::FLAG_TEMPORARY |
+ base::File::FLAG_DELETE_ON_CLOSE);
+
+ if (!file.IsValid())
+ return ppapi::FileErrorToPepperError(file.error_details());
- IPC::PlatformFileForTransit file = IPC::GetFileHandleForProcess(file_handle,
- plugin_process_handle_, true);
+ IPC::PlatformFileForTransit transit_file =
+ IPC::TakeFileHandleForProcess(file.Pass(), plugin_process_handle_);
ppapi::host::ReplyMessageContext reply_context =
context->MakeReplyMessageContext();
reply_context.params.AppendHandle(ppapi::proxy::SerializedHandle(
- ppapi::proxy::SerializedHandle::FILE, file));
+ ppapi::proxy::SerializedHandle::FILE, transit_file));
SendReply(reply_context, IPC::Message());
return PP_OK_COMPLETIONPENDING;
}
@@ -301,8 +279,7 @@ base::FilePath PepperFlashFileMessageFilter::ValidateAndConvertPepperFilePath(
case ppapi::PepperFilePath::DOMAIN_MODULE_LOCAL:
// This filter provides the module name portion of the path to prevent
// plugins from accessing each other's data.
- if (!plugin_data_directory_.empty() &&
- !pepper_path.path().IsAbsolute() &&
+ if (!plugin_data_directory_.empty() && !pepper_path.path().IsAbsolute() &&
!pepper_path.path().ReferencesParent())
file_path = plugin_data_directory_.Append(pepper_path.path());
break;
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_flash_file_message_filter.h b/chromium/content/browser/renderer_host/pepper/pepper_flash_file_message_filter.h
index f89d7cf5907..ab21076262b 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_flash_file_message_filter.h
+++ b/chromium/content/browser/renderer_host/pepper/pepper_flash_file_message_filter.h
@@ -35,8 +35,7 @@ class BrowserPpapiHost;
// All file messages are handled by BrowserThread's blocking pool.
class PepperFlashFileMessageFilter : public ppapi::host::ResourceMessageFilter {
public:
- PepperFlashFileMessageFilter(PP_Instance instance,
- BrowserPpapiHost* host);
+ PepperFlashFileMessageFilter(PP_Instance instance, BrowserPpapiHost* host);
static base::FilePath GetDataDirName(const base::FilePath& profile_path);
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_gamepad_host.cc b/chromium/content/browser/renderer_host/pepper/pepper_gamepad_host.cc
index 818bf24a274..21d02d9bec8 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_gamepad_host.cc
+++ b/chromium/content/browser/renderer_host/pepper/pepper_gamepad_host.cc
@@ -23,8 +23,7 @@ PepperGamepadHost::PepperGamepadHost(BrowserPpapiHost* host,
browser_ppapi_host_(host),
gamepad_service_(GamepadService::GetInstance()),
is_started_(false),
- weak_factory_(this) {
-}
+ weak_factory_(this) {}
PepperGamepadHost::PepperGamepadHost(GamepadService* gamepad_service,
BrowserPpapiHost* host,
@@ -34,21 +33,20 @@ PepperGamepadHost::PepperGamepadHost(GamepadService* gamepad_service,
browser_ppapi_host_(host),
gamepad_service_(gamepad_service),
is_started_(false),
- weak_factory_(this) {
-}
+ weak_factory_(this) {}
PepperGamepadHost::~PepperGamepadHost() {
if (is_started_)
- gamepad_service_->RemoveConsumer();
+ gamepad_service_->RemoveConsumer(this);
}
int32_t PepperGamepadHost::OnResourceMessageReceived(
const IPC::Message& msg,
ppapi::host::HostMessageContext* context) {
- IPC_BEGIN_MESSAGE_MAP(PepperGamepadHost, msg)
+ PPAPI_BEGIN_MESSAGE_MAP(PepperGamepadHost, msg)
PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_Gamepad_RequestMemory,
OnRequestMemory)
- IPC_END_MESSAGE_MAP()
+ PPAPI_END_MESSAGE_MAP()
return PP_ERROR_FAILED;
}
@@ -57,7 +55,7 @@ int32_t PepperGamepadHost::OnRequestMemory(
if (is_started_)
return PP_ERROR_FAILED;
- gamepad_service_->AddConsumer();
+ gamepad_service_->ConsumerBecameActive(this);
is_started_ = true;
// Don't send the shared memory back until the user has interacted with the
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_gamepad_host.h b/chromium/content/browser/renderer_host/pepper/pepper_gamepad_host.h
index 1db73aa694a..c9f2c18fbc6 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_gamepad_host.h
+++ b/chromium/content/browser/renderer_host/pepper/pepper_gamepad_host.h
@@ -7,6 +7,7 @@
#include "base/compiler_specific.h"
#include "base/memory/weak_ptr.h"
+#include "content/browser/gamepad/gamepad_consumer.h"
#include "content/common/content_export.h"
#include "ppapi/host/resource_host.h"
@@ -21,7 +22,9 @@ namespace content {
class BrowserPpapiHost;
class GamepadService;
-class CONTENT_EXPORT PepperGamepadHost : public ppapi::host::ResourceHost {
+class CONTENT_EXPORT PepperGamepadHost :
+ public ppapi::host::ResourceHost,
+ public GamepadConsumer {
public:
PepperGamepadHost(BrowserPpapiHost* host,
PP_Instance instance,
@@ -40,6 +43,14 @@ class CONTENT_EXPORT PepperGamepadHost : public ppapi::host::ResourceHost {
const IPC::Message& msg,
ppapi::host::HostMessageContext* context) OVERRIDE;
+ // GamepadConsumer implementation.
+ virtual void OnGamepadConnected(
+ unsigned index,
+ const blink::WebGamepad& gamepad) OVERRIDE {}
+ virtual void OnGamepadDisconnected(
+ unsigned index,
+ const blink::WebGamepad& gamepad) OVERRIDE {}
+
private:
int32_t OnRequestMemory(ppapi::host::HostMessageContext* context);
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_gamepad_host_unittest.cc b/chromium/content/browser/renderer_host/pepper/pepper_gamepad_host_unittest.cc
index 43ff1170354..f86c944a46d 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_gamepad_host_unittest.cc
+++ b/chromium/content/browser/renderer_host/pepper/pepper_gamepad_host_unittest.cc
@@ -22,14 +22,11 @@ namespace content {
namespace {
-class PepperGamepadHostTest
- : public testing::Test,
- public BrowserPpapiHostTest {
+class PepperGamepadHostTest : public testing::Test,
+ public BrowserPpapiHostTest {
public:
- PepperGamepadHostTest() {
- }
- virtual ~PepperGamepadHostTest() {
- }
+ PepperGamepadHostTest() {}
+ virtual ~PepperGamepadHostTest() {}
void ConstructService(const blink::WebGamepads& test_data) {
service_.reset(new GamepadServiceTestConstructor(test_data));
@@ -55,7 +52,7 @@ inline ptrdiff_t AddressDiff(const void* a, const void* b) {
TEST_F(PepperGamepadHostTest, ValidateHardwareBuffersMatch) {
// Hardware buffer.
COMPILE_ASSERT(sizeof(ppapi::ContentGamepadHardwareBuffer) ==
- sizeof(GamepadHardwareBuffer),
+ sizeof(GamepadHardwareBuffer),
gamepad_hardware_buffers_must_match);
ppapi::ContentGamepadHardwareBuffer ppapi_buf;
GamepadHardwareBuffer content_buf;
@@ -67,8 +64,7 @@ TEST_F(PepperGamepadHostTest, ValidateHardwareBuffersMatch) {
TEST_F(PepperGamepadHostTest, ValidateGamepadsMatch) {
// Gamepads.
- COMPILE_ASSERT(sizeof(ppapi::WebKitGamepads) ==
- sizeof(blink::WebGamepads),
+ COMPILE_ASSERT(sizeof(ppapi::WebKitGamepads) == sizeof(blink::WebGamepads),
gamepads_data_must_match);
ppapi::WebKitGamepads ppapi_gamepads;
blink::WebGamepads web_gamepads;
@@ -88,8 +84,7 @@ TEST_F(PepperGamepadHostTest, ValidateGamepadsMatch) {
TEST_F(PepperGamepadHostTest, ValidateGamepadMatch) {
// Gamepad.
- COMPILE_ASSERT(sizeof(ppapi::WebKitGamepad) ==
- sizeof(blink::WebGamepad),
+ COMPILE_ASSERT(sizeof(ppapi::WebKitGamepad) == sizeof(blink::WebGamepad),
gamepad_data_must_match);
ppapi::WebKitGamepad ppapi_gamepad;
blink::WebGamepad web_gamepad;
@@ -135,16 +130,15 @@ TEST_F(PepperGamepadHostTest, WaitForReply) {
PP_Instance pp_instance = 12345;
PP_Resource pp_resource = 67890;
- PepperGamepadHost gamepad_host(gamepad_service(), GetBrowserPpapiHost(),
- pp_instance, pp_resource);
+ PepperGamepadHost gamepad_host(
+ gamepad_service(), GetBrowserPpapiHost(), pp_instance, pp_resource);
// Synthesize a request for gamepad data.
ppapi::host::HostMessageContext context(
ppapi::proxy::ResourceMessageCallParams(pp_resource, 1));
EXPECT_EQ(PP_OK_COMPLETIONPENDING,
gamepad_host.OnResourceMessageReceived(
- PpapiHostMsg_Gamepad_RequestMemory(),
- &context));
+ PpapiHostMsg_Gamepad_RequestMemory(), &context));
// Wait for the gamepad background thread to read twice to make sure we
// don't get a message yet (see below for why).
@@ -164,7 +158,8 @@ TEST_F(PepperGamepadHostTest, WaitForReply) {
// ensures that it was able to issue callbacks for the first read (if it
// issued one) before we try to check for it.
blink::WebGamepads button_down_data = default_data;
- button_down_data.items[0].buttons[0] = 1.f;
+ button_down_data.items[0].buttons[0].value = 1.f;
+ button_down_data.items[0].buttons[0].pressed = true;
fetcher->SetTestData(button_down_data);
fetcher->WaitForDataRead();
fetcher->WaitForDataRead();
@@ -190,15 +185,16 @@ TEST_F(PepperGamepadHostTest, WaitForReply) {
EXPECT_EQ(button_down_data.items[0].buttonsLength,
buffer->buffer.items[0].buttons_length);
for (size_t i = 0; i < ppapi::WebKitGamepad::kButtonsLengthCap; i++) {
- EXPECT_EQ(button_down_data.items[0].buttons[i],
- buffer->buffer.items[0].buttons[i]);
+ EXPECT_EQ(button_down_data.items[0].buttons[i].value,
+ buffer->buffer.items[0].buttons[i].value);
+ EXPECT_EQ(button_down_data.items[0].buttons[i].pressed,
+ buffer->buffer.items[0].buttons[i].pressed);
}
// Duplicate requests should be denied.
EXPECT_EQ(PP_ERROR_FAILED,
gamepad_host.OnResourceMessageReceived(
- PpapiHostMsg_Gamepad_RequestMemory(),
- &context));
+ PpapiHostMsg_Gamepad_RequestMemory(), &context));
}
} // namespace content
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_host_resolver_message_filter.cc b/chromium/content/browser/renderer_host/pepper/pepper_host_resolver_message_filter.cc
index bdbdf871e27..3789ffa8593 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_host_resolver_message_filter.cc
+++ b/chromium/content/browser/renderer_host/pepper/pepper_host_resolver_message_filter.cc
@@ -67,9 +67,8 @@ void CreateNetAddressListFromAddressList(
PP_NetAddress_Private address;
for (size_t i = 0; i < list.size(); ++i) {
- if (!ppapi::NetAddressPrivateImpl::IPEndPointToNetAddress(list[i].address(),
- list[i].port(),
- &address)) {
+ if (!ppapi::NetAddressPrivateImpl::IPEndPointToNetAddress(
+ list[i].address(), list[i].port(), &address)) {
net_address_list->clear();
return;
}
@@ -86,19 +85,16 @@ PepperHostResolverMessageFilter::PepperHostResolverMessageFilter(
: external_plugin_(host->external_plugin()),
private_api_(private_api),
render_process_id_(0),
- render_view_id_(0) {
+ render_frame_id_(0) {
DCHECK(host);
- if (!host->GetRenderViewIDsForInstance(
- instance,
- &render_process_id_,
- &render_view_id_)) {
+ if (!host->GetRenderFrameIDsForInstance(
+ instance, &render_process_id_, &render_frame_id_)) {
NOTREACHED();
}
}
-PepperHostResolverMessageFilter::~PepperHostResolverMessageFilter() {
-}
+PepperHostResolverMessageFilter::~PepperHostResolverMessageFilter() {}
scoped_refptr<base::TaskRunner>
PepperHostResolverMessageFilter::OverrideTaskRunnerForMessage(
@@ -111,10 +107,10 @@ PepperHostResolverMessageFilter::OverrideTaskRunnerForMessage(
int32_t PepperHostResolverMessageFilter::OnResourceMessageReceived(
const IPC::Message& msg,
ppapi::host::HostMessageContext* context) {
- IPC_BEGIN_MESSAGE_MAP(PepperHostResolverMessageFilter, msg)
- PPAPI_DISPATCH_HOST_RESOURCE_CALL(
- PpapiHostMsg_HostResolver_Resolve, OnMsgResolve)
- IPC_END_MESSAGE_MAP()
+ PPAPI_BEGIN_MESSAGE_MAP(PepperHostResolverMessageFilter, msg)
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_HostResolver_Resolve,
+ OnMsgResolve)
+ PPAPI_END_MESSAGE_MAP()
return PP_ERROR_FAILED;
}
@@ -122,18 +118,16 @@ int32_t PepperHostResolverMessageFilter::OnMsgResolve(
const ppapi::host::HostMessageContext* context,
const ppapi::HostPortPair& host_port,
const PP_HostResolver_Private_Hint& hint) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Check plugin permissions.
SocketPermissionRequest request(
SocketPermissionRequest::RESOLVE_HOST, host_port.host, host_port.port);
- RenderViewHost* render_view_host =
- RenderViewHost::FromID(render_process_id_, render_view_id_);
- if (!render_view_host ||
- !pepper_socket_utils::CanUseSocketAPIs(external_plugin_,
+ if (!pepper_socket_utils::CanUseSocketAPIs(external_plugin_,
private_api_,
&request,
- render_view_host)) {
+ render_process_id_,
+ render_frame_id_)) {
return PP_ERROR_NOACCESS;
}
@@ -146,8 +140,10 @@ int32_t PepperHostResolverMessageFilter::OnMsgResolve(
return PP_ERROR_FAILED;
BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::Bind(&PepperHostResolverMessageFilter::DoResolve, this,
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&PepperHostResolverMessageFilter::DoResolve,
+ this,
context->MakeReplyMessageContext(),
host_port,
hint,
@@ -160,7 +156,7 @@ void PepperHostResolverMessageFilter::DoResolve(
const ppapi::HostPortPair& host_port,
const PP_HostResolver_Private_Hint& hint,
ResourceContext* resource_context) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
net::HostResolver* host_resolver = resource_context->GetHostResolver();
if (!host_resolver) {
@@ -210,8 +206,8 @@ void PepperHostResolverMessageFilter::SendResolveReply(
ReplyMessageContext reply_context = context;
reply_context.params.set_result(result);
SendReply(reply_context,
- PpapiPluginMsg_HostResolver_ResolveReply(
- canonical_name, net_address_list));
+ PpapiPluginMsg_HostResolver_ResolveReply(canonical_name,
+ net_address_list));
}
void PepperHostResolverMessageFilter::SendResolveError(
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_host_resolver_message_filter.h b/chromium/content/browser/renderer_host/pepper/pepper_host_resolver_message_filter.h
index cc936036d3e..8fb404df4e3 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_host_resolver_message_filter.h
+++ b/chromium/content/browser/renderer_host/pepper/pepper_host_resolver_message_filter.h
@@ -79,7 +79,7 @@ class CONTENT_EXPORT PepperHostResolverMessageFilter
bool external_plugin_;
bool private_api_;
int render_process_id_;
- int render_view_id_;
+ int render_frame_id_;
DISALLOW_COPY_AND_ASSIGN(PepperHostResolverMessageFilter);
};
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_internal_file_ref_backend.cc b/chromium/content/browser/renderer_host/pepper/pepper_internal_file_ref_backend.cc
index e57a237c503..bc7f2b6a119 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_internal_file_ref_backend.cc
+++ b/chromium/content/browser/renderer_host/pepper/pepper_internal_file_ref_backend.cc
@@ -13,7 +13,6 @@
#include "content/browser/fileapi/browser_file_system_helper.h"
#include "content/browser/renderer_host/pepper/pepper_file_system_browser_host.h"
#include "content/public/browser/browser_context.h"
-#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/storage_partition.h"
#include "net/base/escape.h"
@@ -21,6 +20,7 @@
#include "ppapi/c/pp_file_info.h"
#include "ppapi/c/pp_instance.h"
#include "ppapi/c/pp_resource.h"
+#include "ppapi/c/ppb_file_ref.h"
#include "ppapi/host/dispatch_host_message.h"
#include "ppapi/host/ppapi_host.h"
#include "ppapi/proxy/ppapi_messages.h"
@@ -47,22 +47,22 @@ PepperInternalFileRefBackend::PepperInternalFileRefBackend(
PpapiHost* host,
int render_process_id,
base::WeakPtr<PepperFileSystemBrowserHost> fs_host,
- const std::string& path) : host_(host),
- render_process_id_(render_process_id),
- fs_host_(fs_host),
- fs_type_(fs_host->GetType()),
- path_(path),
- weak_factory_(this) {
+ const std::string& path)
+ : host_(host),
+ render_process_id_(render_process_id),
+ fs_host_(fs_host),
+ fs_type_(fs_host->GetType()),
+ path_(path),
+ weak_factory_(this) {
ppapi::NormalizeInternalPath(&path_);
}
-PepperInternalFileRefBackend::~PepperInternalFileRefBackend() {
-}
+PepperInternalFileRefBackend::~PepperInternalFileRefBackend() {}
fileapi::FileSystemURL PepperInternalFileRefBackend::GetFileSystemURL() const {
if (!fs_url_.is_valid() && fs_host_.get() && fs_host_->IsOpened()) {
- GURL fs_path = fs_host_->GetRootUrl().Resolve(
- net::EscapePath(path_.substr(1)));
+ GURL fs_path =
+ fs_host_->GetRootUrl().Resolve(net::EscapePath(path_.substr(1)));
scoped_refptr<fileapi::FileSystemContext> fs_context =
GetFileSystemContext();
if (fs_context.get())
@@ -85,21 +85,21 @@ PepperInternalFileRefBackend::GetFileSystemContext() const {
void PepperInternalFileRefBackend::DidFinish(
ppapi::host::ReplyMessageContext context,
const IPC::Message& msg,
- base::PlatformFileError error) {
- context.params.set_result(ppapi::PlatformFileErrorToPepperError(error));
+ base::File::Error error) {
+ context.params.set_result(ppapi::FileErrorToPepperError(error));
host_->SendReply(context, msg);
}
int32_t PepperInternalFileRefBackend::MakeDirectory(
ppapi::host::ReplyMessageContext reply_context,
- bool make_ancestors) {
+ int32_t make_directory_flags) {
if (!GetFileSystemURL().is_valid())
return PP_ERROR_FAILED;
GetFileSystemContext()->operation_runner()->CreateDirectory(
GetFileSystemURL(),
- false,
- make_ancestors,
+ !!(make_directory_flags & PP_MAKEDIRECTORYFLAG_EXCLUSIVE),
+ !!(make_directory_flags & PP_MAKEDIRECTORYFLAG_WITH_ANCESTORS),
base::Bind(&PepperInternalFileRefBackend::DidFinish,
weak_factory_.GetWeakPtr(),
reply_context,
@@ -178,13 +178,13 @@ int32_t PepperInternalFileRefBackend::Query(
void PepperInternalFileRefBackend::GetMetadataComplete(
ppapi::host::ReplyMessageContext reply_context,
- base::PlatformFileError error,
- const base::PlatformFileInfo& file_info) {
- reply_context.params.set_result(ppapi::PlatformFileErrorToPepperError(error));
+ base::File::Error error,
+ const base::File::Info& file_info) {
+ reply_context.params.set_result(ppapi::FileErrorToPepperError(error));
PP_FileInfo pp_file_info;
- if (error == base::PLATFORM_FILE_OK)
- ppapi::PlatformFileInfoToPepperFileInfo(file_info, fs_type_, &pp_file_info);
+ if (error == base::File::FILE_OK)
+ ppapi::FileInfoToPepperFileInfo(file_info, fs_type_, &pp_file_info);
else
memset(&pp_file_info, 0, sizeof(pp_file_info));
@@ -197,33 +197,41 @@ int32_t PepperInternalFileRefBackend::ReadDirectoryEntries(
if (!GetFileSystemURL().is_valid())
return PP_ERROR_FAILED;
+ fileapi::FileSystemOperation::FileEntryList* accumulated_file_list =
+ new fileapi::FileSystemOperation::FileEntryList;
GetFileSystemContext()->operation_runner()->ReadDirectory(
GetFileSystemURL(),
base::Bind(&PepperInternalFileRefBackend::ReadDirectoryComplete,
weak_factory_.GetWeakPtr(),
- reply_context));
+ reply_context,
+ base::Owned(accumulated_file_list)));
return PP_OK_COMPLETIONPENDING;
}
void PepperInternalFileRefBackend::ReadDirectoryComplete(
ppapi::host::ReplyMessageContext context,
- base::PlatformFileError error,
+ fileapi::FileSystemOperation::FileEntryList* accumulated_file_list,
+ base::File::Error error,
const fileapi::FileSystemOperation::FileEntryList& file_list,
bool has_more) {
- // The current filesystem backend always returns false.
- DCHECK(!has_more);
+ accumulated_file_list->insert(
+ accumulated_file_list->end(), file_list.begin(), file_list.end());
+ if (has_more)
+ return;
- context.params.set_result(ppapi::PlatformFileErrorToPepperError(error));
+ context.params.set_result(ppapi::FileErrorToPepperError(error));
std::vector<ppapi::FileRefCreateInfo> infos;
std::vector<PP_FileType> file_types;
- if (error == base::PLATFORM_FILE_OK && fs_host_.get()) {
+ if (error == base::File::FILE_OK && fs_host_.get()) {
std::string dir_path = path_;
if (dir_path.empty() || dir_path[dir_path.size() - 1] != '/')
dir_path += '/';
for (fileapi::FileSystemOperation::FileEntryList::const_iterator it =
- file_list.begin(); it != file_list.end(); ++it) {
+ accumulated_file_list->begin();
+ it != accumulated_file_list->end();
+ ++it) {
if (it->is_directory)
file_types.push_back(PP_FILETYPE_DIRECTORY);
else
@@ -240,14 +248,15 @@ void PepperInternalFileRefBackend::ReadDirectoryComplete(
}
}
- host_->SendReply(context,
+ host_->SendReply(
+ context,
PpapiPluginMsg_FileRef_ReadDirectoryEntriesReply(infos, file_types));
}
int32_t PepperInternalFileRefBackend::GetAbsolutePath(
ppapi::host::ReplyMessageContext reply_context) {
host_->SendReply(reply_context,
- PpapiPluginMsg_FileRef_GetAbsolutePathReply(path_));
+ PpapiPluginMsg_FileRef_GetAbsolutePathReply(path_));
return PP_OK_COMPLETIONPENDING;
}
@@ -255,8 +264,8 @@ int32_t PepperInternalFileRefBackend::CanRead() const {
fileapi::FileSystemURL url = GetFileSystemURL();
if (!FileSystemURLIsValid(GetFileSystemContext().get(), url))
return PP_ERROR_FAILED;
- if (!ChildProcessSecurityPolicyImpl::GetInstance()->
- CanReadFileSystemFile(render_process_id_, url)) {
+ if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFileSystemFile(
+ render_process_id_, url)) {
return PP_ERROR_NOACCESS;
}
return PP_OK;
@@ -266,8 +275,8 @@ int32_t PepperInternalFileRefBackend::CanWrite() const {
fileapi::FileSystemURL url = GetFileSystemURL();
if (!FileSystemURLIsValid(GetFileSystemContext().get(), url))
return PP_ERROR_FAILED;
- if (!ChildProcessSecurityPolicyImpl::GetInstance()->
- CanWriteFileSystemFile(render_process_id_, url)) {
+ if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanWriteFileSystemFile(
+ render_process_id_, url)) {
return PP_ERROR_NOACCESS;
}
return PP_OK;
@@ -277,8 +286,8 @@ int32_t PepperInternalFileRefBackend::CanCreate() const {
fileapi::FileSystemURL url = GetFileSystemURL();
if (!FileSystemURLIsValid(GetFileSystemContext().get(), url))
return PP_ERROR_FAILED;
- if (!ChildProcessSecurityPolicyImpl::GetInstance()->
- CanCreateFileSystemFile(render_process_id_, url)) {
+ if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanCreateFileSystemFile(
+ render_process_id_, url)) {
return PP_ERROR_NOACCESS;
}
return PP_OK;
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_internal_file_ref_backend.h b/chromium/content/browser/renderer_host/pepper/pepper_internal_file_ref_backend.h
index 93dbab97a3e..4214de01785 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_internal_file_ref_backend.h
+++ b/chromium/content/browser/renderer_host/pepper/pepper_internal_file_ref_backend.h
@@ -32,7 +32,7 @@ class PepperInternalFileRefBackend : public PepperFileRefBackend {
// PepperFileRefBackend overrides.
virtual int32_t MakeDirectory(ppapi::host::ReplyMessageContext context,
- bool make_ancestors) OVERRIDE;
+ int32_t make_directory_flags) OVERRIDE;
virtual int32_t Touch(ppapi::host::ReplyMessageContext context,
PP_Time last_accessed_time,
PP_Time last_modified_time) OVERRIDE;
@@ -40,8 +40,8 @@ class PepperInternalFileRefBackend : public PepperFileRefBackend {
virtual int32_t Rename(ppapi::host::ReplyMessageContext context,
PepperFileRefHost* new_file_ref) OVERRIDE;
virtual int32_t Query(ppapi::host::ReplyMessageContext context) OVERRIDE;
- virtual int32_t ReadDirectoryEntries(
- ppapi::host::ReplyMessageContext context) OVERRIDE;
+ virtual int32_t ReadDirectoryEntries(ppapi::host::ReplyMessageContext context)
+ OVERRIDE;
virtual int32_t GetAbsolutePath(ppapi::host::ReplyMessageContext context)
OVERRIDE;
virtual fileapi::FileSystemURL GetFileSystemURL() const OVERRIDE;
@@ -56,16 +56,16 @@ class PepperInternalFileRefBackend : public PepperFileRefBackend {
// Generic reply callback.
void DidFinish(ppapi::host::ReplyMessageContext reply_context,
const IPC::Message& msg,
- base::PlatformFileError error);
+ base::File::Error error);
// Operation specific callbacks.
- void GetMetadataComplete(
- ppapi::host::ReplyMessageContext reply_context,
- base::PlatformFileError error,
- const base::PlatformFileInfo& file_info);
+ void GetMetadataComplete(ppapi::host::ReplyMessageContext reply_context,
+ base::File::Error error,
+ const base::File::Info& file_info);
void ReadDirectoryComplete(
ppapi::host::ReplyMessageContext context,
- base::PlatformFileError error,
+ fileapi::FileSystemOperation::FileEntryList* accumulated_file_list,
+ base::File::Error error,
const fileapi::FileSystemOperation::FileEntryList& file_list,
bool has_more);
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_lookup_request.h b/chromium/content/browser/renderer_host/pepper/pepper_lookup_request.h
index da116fd640c..b36bd5e2f4e 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_lookup_request.h
+++ b/chromium/content/browser/renderer_host/pepper/pepper_lookup_request.h
@@ -14,7 +14,7 @@
namespace content {
-template<class T>
+template <class T>
class PepperLookupRequest {
public:
typedef base::Callback<void(int, const net::AddressList&, const T&)>
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_message_filter.cc b/chromium/content/browser/renderer_host/pepper/pepper_message_filter.cc
index 1394d956f0d..0eaa5ec9bc1 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_message_filter.cc
+++ b/chromium/content/browser/renderer_host/pepper/pepper_message_filter.cc
@@ -11,19 +11,18 @@
namespace content {
-PepperMessageFilter::PepperMessageFilter() {}
+PepperMessageFilter::PepperMessageFilter()
+ : BrowserMessageFilter(PpapiMsgStart) {}
+
PepperMessageFilter::~PepperMessageFilter() {}
-bool PepperMessageFilter::OnMessageReceived(const IPC::Message& msg,
- bool* message_was_ok) {
+bool PepperMessageFilter::OnMessageReceived(const IPC::Message& msg) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(PepperMessageFilter, msg, *message_was_ok)
- // X509 certificate messages.
+ IPC_BEGIN_MESSAGE_MAP(PepperMessageFilter, msg)
IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBX509Certificate_ParseDER,
- OnX509CertificateParseDER);
-
- IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ OnX509CertificateParseDER)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
return handled;
}
@@ -32,7 +31,7 @@ void PepperMessageFilter::OnX509CertificateParseDER(
bool* succeeded,
ppapi::PPB_X509Certificate_Fields* result) {
*succeeded = (der.size() != 0 && pepper_socket_utils::GetCertificateFields(
- &der[0], der.size(), result));
+ &der[0], der.size(), result));
}
} // namespace content
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_message_filter.h b/chromium/content/browser/renderer_host/pepper/pepper_message_filter.h
index 3f5010ed40e..68dac47fc8d 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_message_filter.h
+++ b/chromium/content/browser/renderer_host/pepper/pepper_message_filter.h
@@ -24,8 +24,7 @@ class PepperMessageFilter : public BrowserMessageFilter {
PepperMessageFilter();
// BrowserMessageFilter methods.
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
protected:
virtual ~PepperMessageFilter();
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_network_monitor_host.cc b/chromium/content/browser/renderer_host/pepper/pepper_network_monitor_host.cc
index b53a60b779b..bc5795b6e41 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_network_monitor_host.cc
+++ b/chromium/content/browser/renderer_host/pepper/pepper_network_monitor_host.cc
@@ -13,47 +13,49 @@
#include "ppapi/proxy/ppapi_messages.h"
#include "ppapi/shared_impl/private/net_address_private_impl.h"
-
namespace content {
namespace {
bool CanUseNetworkMonitor(bool external_plugin,
int render_process_id,
- int render_view_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ int render_frame_id) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
SocketPermissionRequest request = SocketPermissionRequest(
SocketPermissionRequest::NETWORK_STATE, std::string(), 0);
- return pepper_socket_utils::CanUseSocketAPIs(
- external_plugin, false /* private_api */, &request, render_process_id,
- render_view_id);
+ return pepper_socket_utils::CanUseSocketAPIs(external_plugin,
+ false /* private_api */,
+ &request,
+ render_process_id,
+ render_frame_id);
}
scoped_ptr<net::NetworkInterfaceList> GetNetworkList() {
scoped_ptr<net::NetworkInterfaceList> list(new net::NetworkInterfaceList());
- net::GetNetworkList(list.get());
+ net::GetNetworkList(list.get(), net::INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES);
return list.Pass();
}
} // namespace
-PepperNetworkMonitorHost::PepperNetworkMonitorHost(
- BrowserPpapiHostImpl* host,
- PP_Instance instance,
- PP_Resource resource)
+PepperNetworkMonitorHost::PepperNetworkMonitorHost(BrowserPpapiHostImpl* host,
+ PP_Instance instance,
+ PP_Resource resource)
: ResourceHost(host->GetPpapiHost(), instance, resource),
weak_factory_(this) {
int render_process_id;
- int render_view_id;
- host->GetRenderViewIDsForInstance(instance,
- &render_process_id,
- &render_view_id);
+ int render_frame_id;
+ host->GetRenderFrameIDsForInstance(
+ instance, &render_process_id, &render_frame_id);
BrowserThread::PostTaskAndReplyWithResult(
- BrowserThread::UI, FROM_HERE,
- base::Bind(&CanUseNetworkMonitor, host->external_plugin(),
- render_process_id, render_view_id),
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&CanUseNetworkMonitor,
+ host->external_plugin(),
+ render_process_id,
+ render_frame_id),
base::Bind(&PepperNetworkMonitorHost::OnPermissionCheckResult,
weak_factory_.GetWeakPtr()));
}
@@ -62,9 +64,7 @@ PepperNetworkMonitorHost::~PepperNetworkMonitorHost() {
net::NetworkChangeNotifier::RemoveIPAddressObserver(this);
}
-void PepperNetworkMonitorHost::OnIPAddressChanged() {
- GetAndSendNetworkList();
-}
+void PepperNetworkMonitorHost::OnIPAddressChanged() { GetAndSendNetworkList(); }
void PepperNetworkMonitorHost::OnPermissionCheckResult(
bool can_use_network_monitor) {
@@ -79,11 +79,12 @@ void PepperNetworkMonitorHost::OnPermissionCheckResult(
}
void PepperNetworkMonitorHost::GetAndSendNetworkList() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
// Call GetNetworkList() on a thread that allows blocking IO.
base::PostTaskAndReplyWithResult(
- BrowserThread::GetBlockingPool(), FROM_HERE,
+ BrowserThread::GetBlockingPool(),
+ FROM_HERE,
base::Bind(&GetNetworkList),
base::Bind(&PepperNetworkMonitorHost::SendNetworkList,
weak_factory_.GetWeakPtr()));
@@ -91,7 +92,7 @@ void PepperNetworkMonitorHost::GetAndSendNetworkList() {
void PepperNetworkMonitorHost::SendNetworkList(
scoped_ptr<net::NetworkInterfaceList> list) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
scoped_ptr<ppapi::proxy::SerializedNetworkList> list_copy(
new ppapi::proxy::SerializedNetworkList(list->size()));
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_network_monitor_host.h b/chromium/content/browser/renderer_host/pepper/pepper_network_monitor_host.h
index e27f8a2ac21..12241c5c5b4 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_network_monitor_host.h
+++ b/chromium/content/browser/renderer_host/pepper/pepper_network_monitor_host.h
@@ -22,10 +22,9 @@ class CONTENT_EXPORT PepperNetworkMonitorHost
: public ppapi::host::ResourceHost,
public net::NetworkChangeNotifier::IPAddressObserver {
public:
- PepperNetworkMonitorHost(
- BrowserPpapiHostImpl* host,
- PP_Instance instance,
- PP_Resource resource);
+ PepperNetworkMonitorHost(BrowserPpapiHostImpl* host,
+ PP_Instance instance,
+ PP_Resource resource);
virtual ~PepperNetworkMonitorHost();
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_network_proxy_host.cc b/chromium/content/browser/renderer_host/pepper/pepper_network_proxy_host.cc
index 13f0fc3733f..4b3d817b66e 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_network_proxy_host.cc
+++ b/chromium/content/browser/renderer_host/pepper/pepper_network_proxy_host.cc
@@ -30,15 +30,15 @@ PepperNetworkProxyHost::PepperNetworkProxyHost(BrowserPpapiHostImpl* host,
is_allowed_(false),
waiting_for_ui_thread_data_(true),
weak_factory_(this) {
- int render_process_id(0), render_view_id(0);
- host->GetRenderViewIDsForInstance(instance,
- &render_process_id,
- &render_view_id);
+ int render_process_id(0), render_frame_id(0);
+ host->GetRenderFrameIDsForInstance(
+ instance, &render_process_id, &render_frame_id);
BrowserThread::PostTaskAndReplyWithResult(
- BrowserThread::UI, FROM_HERE,
+ BrowserThread::UI,
+ FROM_HERE,
base::Bind(&GetUIThreadDataOnUIThread,
render_process_id,
- render_view_id,
+ render_frame_id,
host->external_plugin()),
base::Bind(&PepperNetworkProxyHost::DidGetUIThreadData,
weak_factory_.GetWeakPtr()));
@@ -55,38 +55,33 @@ PepperNetworkProxyHost::~PepperNetworkProxyHost() {
}
}
-PepperNetworkProxyHost::UIThreadData::UIThreadData()
- : is_allowed(false) {
-}
+PepperNetworkProxyHost::UIThreadData::UIThreadData() : is_allowed(false) {}
-PepperNetworkProxyHost::UIThreadData::~UIThreadData() {
-}
+PepperNetworkProxyHost::UIThreadData::~UIThreadData() {}
// static
PepperNetworkProxyHost::UIThreadData
PepperNetworkProxyHost::GetUIThreadDataOnUIThread(int render_process_id,
- int render_view_id,
+ int render_frame_id,
bool is_external_plugin) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
PepperNetworkProxyHost::UIThreadData result;
RenderProcessHost* render_process_host =
RenderProcessHost::FromID(render_process_id);
if (render_process_host && render_process_host->GetBrowserContext()) {
- result.context_getter = render_process_host->GetBrowserContext()->
- GetRequestContextForRenderProcess(render_process_id);
+ result.context_getter =
+ render_process_host->GetBrowserContext()
+ ->GetRequestContextForRenderProcess(render_process_id);
}
- RenderViewHost* render_view_host =
- RenderViewHost::FromID(render_process_id, render_view_id);
- if (render_view_host) {
- SocketPermissionRequest request(
- content::SocketPermissionRequest::RESOLVE_PROXY, std::string(), 0);
- result.is_allowed = pepper_socket_utils::CanUseSocketAPIs(
- is_external_plugin,
- false /* is_private_api */,
- &request,
- render_view_host);
- }
+ SocketPermissionRequest request(
+ content::SocketPermissionRequest::RESOLVE_PROXY, std::string(), 0);
+ result.is_allowed =
+ pepper_socket_utils::CanUseSocketAPIs(is_external_plugin,
+ false /* is_private_api */,
+ &request,
+ render_process_id,
+ render_frame_id);
return result;
}
@@ -109,10 +104,10 @@ void PepperNetworkProxyHost::DidGetUIThreadData(
int32_t PepperNetworkProxyHost::OnResourceMessageReceived(
const IPC::Message& msg,
ppapi::host::HostMessageContext* context) {
- IPC_BEGIN_MESSAGE_MAP(PepperNetworkProxyHost, msg)
- PPAPI_DISPATCH_HOST_RESOURCE_CALL(
- PpapiHostMsg_NetworkProxy_GetProxyForURL, OnMsgGetProxyForURL)
- IPC_END_MESSAGE_MAP()
+ PPAPI_BEGIN_MESSAGE_MAP(PepperNetworkProxyHost, msg)
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_NetworkProxy_GetProxyForURL,
+ OnMsgGetProxyForURL)
+ PPAPI_END_MESSAGE_MAP()
return PP_ERROR_FAILED;
}
@@ -121,12 +116,11 @@ int32_t PepperNetworkProxyHost::OnMsgGetProxyForURL(
const std::string& url) {
GURL gurl(url);
if (gurl.is_valid()) {
- UnsentRequest request = { gurl, context->MakeReplyMessageContext() };
+ UnsentRequest request = {gurl, context->MakeReplyMessageContext()};
unsent_requests_.push(request);
TryToSendUnsentRequests();
} else {
- SendFailureReply(PP_ERROR_BADARGUMENT,
- context->MakeReplyMessageContext());
+ SendFailureReply(PP_ERROR_BADARGUMENT, context->MakeReplyMessageContext());
}
return PP_OK_COMPLETIONPENDING;
}
@@ -145,7 +139,7 @@ void PepperNetworkProxyHost::TryToSendUnsentRequests() {
// Everything looks valid, so try to resolve the proxy.
net::ProxyInfo* proxy_info = new net::ProxyInfo;
net::ProxyService::PacRequest* pending_request = NULL;
- base::Callback<void (int)> callback =
+ base::Callback<void(int)> callback =
base::Bind(&PepperNetworkProxyHost::OnResolveProxyCompleted,
weak_factory_.GetWeakPtr(),
request.reply_context,
@@ -187,9 +181,8 @@ void PepperNetworkProxyHost::SendFailureReply(
int32_t error,
ppapi::host::ReplyMessageContext context) {
context.params.set_result(error);
- host()->SendReply(context,
- PpapiPluginMsg_NetworkProxy_GetProxyForURLReply(
- std::string()));
+ host()->SendReply(
+ context, PpapiPluginMsg_NetworkProxy_GetProxyForURLReply(std::string()));
}
} // namespace content
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_network_proxy_host.h b/chromium/content/browser/renderer_host/pepper/pepper_network_proxy_host.h
index 38e36cb13e6..28f52b66273 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_network_proxy_host.h
+++ b/chromium/content/browser/renderer_host/pepper/pepper_network_proxy_host.h
@@ -50,7 +50,7 @@ class CONTENT_EXPORT PepperNetworkProxyHost : public ppapi::host::ResourceHost {
scoped_refptr<net::URLRequestContextGetter> context_getter;
};
static UIThreadData GetUIThreadDataOnUIThread(int render_process_id,
- int render_view_id,
+ int render_frame_id,
bool is_external_plugin);
void DidGetUIThreadData(const UIThreadData&);
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_print_settings_manager.cc b/chromium/content/browser/renderer_host/pepper/pepper_print_settings_manager.cc
index 84aaddfd2f7..de5f5fec949 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_print_settings_manager.cc
+++ b/chromium/content/browser/renderer_host/pepper/pepper_print_settings_manager.cc
@@ -17,37 +17,37 @@ namespace {
// Print units conversion functions.
int32_t DeviceUnitsInPoints(int32_t device_units,
int32_t device_units_per_inch) {
- return printing::ConvertUnit(device_units, device_units_per_inch,
- printing::kPointsPerInch);
+ return printing::ConvertUnit(
+ device_units, device_units_per_inch, printing::kPointsPerInch);
}
PP_Size PrintSizeToPPPrintSize(const gfx::Size& print_size,
int32_t device_units_per_inch) {
PP_Size result;
result.width = DeviceUnitsInPoints(print_size.width(), device_units_per_inch);
- result.height = DeviceUnitsInPoints(print_size.height(),
- device_units_per_inch);
+ result.height =
+ DeviceUnitsInPoints(print_size.height(), device_units_per_inch);
return result;
}
PP_Rect PrintAreaToPPPrintArea(const gfx::Rect& print_area,
int32_t device_units_per_inch) {
PP_Rect result;
- result.point.x = DeviceUnitsInPoints(print_area.origin().x(),
- device_units_per_inch);
- result.point.y = DeviceUnitsInPoints(print_area.origin().y(),
- device_units_per_inch);
- result.size = PrintSizeToPPPrintSize(print_area.size(),
- device_units_per_inch);
+ result.point.x =
+ DeviceUnitsInPoints(print_area.origin().x(), device_units_per_inch);
+ result.point.y =
+ DeviceUnitsInPoints(print_area.origin().y(), device_units_per_inch);
+ result.size =
+ PrintSizeToPPPrintSize(print_area.size(), device_units_per_inch);
return result;
}
PepperPrintSettingsManager::Result ComputeDefaultPrintSettings() {
// This function should run on the UI thread because |PrintingContext| methods
// call into platform APIs.
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
scoped_ptr<printing::PrintingContext> context(
- printing::PrintingContext::Create(std::string()));
+ printing::PrintingContext::Create(std::string()));
if (!context.get() ||
context->UseDefaultSettings() != printing::PrintingContext::OK) {
return PepperPrintSettingsManager::Result(PP_PrintSettings_Dev(),
@@ -55,19 +55,19 @@ PepperPrintSettingsManager::Result ComputeDefaultPrintSettings() {
}
const printing::PrintSettings& print_settings = context->settings();
const printing::PageSetup& page_setup =
- print_settings.page_setup_device_units();
+ print_settings.page_setup_device_units();
int device_units_per_inch = print_settings.device_units_per_inch();
if (device_units_per_inch <= 0) {
return PepperPrintSettingsManager::Result(PP_PrintSettings_Dev(),
PP_ERROR_FAILED);
}
PP_PrintSettings_Dev settings;
- settings.printable_area = PrintAreaToPPPrintArea(
- page_setup.printable_area(), device_units_per_inch);
- settings.content_area = PrintAreaToPPPrintArea(
- page_setup.content_area(), device_units_per_inch);
- settings.paper_size = PrintSizeToPPPrintSize(
- page_setup.physical_size(), device_units_per_inch);
+ settings.printable_area = PrintAreaToPPPrintArea(page_setup.printable_area(),
+ device_units_per_inch);
+ settings.content_area =
+ PrintAreaToPPPrintArea(page_setup.content_area(), device_units_per_inch);
+ settings.paper_size =
+ PrintSizeToPPPrintSize(page_setup.physical_size(), device_units_per_inch);
settings.dpi = print_settings.dpi();
// The remainder of the attributes are hard-coded to the defaults as set
@@ -94,8 +94,11 @@ PepperPrintSettingsManager::Result ComputeDefaultPrintSettings() {
void PepperPrintSettingsManagerImpl::GetDefaultPrintSettings(
PepperPrintSettingsManager::Callback callback) {
- BrowserThread::PostTaskAndReplyWithResult(BrowserThread::UI, FROM_HERE,
- base::Bind(ComputeDefaultPrintSettings), callback);
+ BrowserThread::PostTaskAndReplyWithResult(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(ComputeDefaultPrintSettings),
+ callback);
}
} // namespace content
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_printing_host.cc b/chromium/content/browser/renderer_host/pepper/pepper_printing_host.cc
index c11939c34a8..047f1203d72 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_printing_host.cc
+++ b/chromium/content/browser/renderer_host/pepper/pepper_printing_host.cc
@@ -20,20 +20,18 @@ PepperPrintingHost::PepperPrintingHost(
scoped_ptr<PepperPrintSettingsManager> print_settings_manager)
: ResourceHost(host, instance, resource),
print_settings_manager_(print_settings_manager.Pass()),
- weak_factory_(this) {
-}
+ weak_factory_(this) {}
-PepperPrintingHost::~PepperPrintingHost() {
-}
+PepperPrintingHost::~PepperPrintingHost() {}
int32_t PepperPrintingHost::OnResourceMessageReceived(
const IPC::Message& msg,
ppapi::host::HostMessageContext* context) {
- IPC_BEGIN_MESSAGE_MAP(PepperPrintingHost, msg)
+ PPAPI_BEGIN_MESSAGE_MAP(PepperPrintingHost, msg)
PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
PpapiHostMsg_Printing_GetDefaultPrintSettings,
OnGetDefaultPrintSettings)
- IPC_END_MESSAGE_MAP()
+ PPAPI_END_MESSAGE_MAP()
return PP_ERROR_FAILED;
}
@@ -50,9 +48,9 @@ void PepperPrintingHost::PrintSettingsCallback(
ppapi::host::ReplyMessageContext reply_context,
PepperPrintSettingsManager::Result result) {
reply_context.params.set_result(result.second);
- host()->SendReply(reply_context,
+ host()->SendReply(
+ reply_context,
PpapiPluginMsg_Printing_GetDefaultPrintSettingsReply(result.first));
}
-
} // namespace content
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_printing_host.h b/chromium/content/browser/renderer_host/pepper/pepper_printing_host.h
index 4c302ee7f5a..ec956292b2d 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_printing_host.h
+++ b/chromium/content/browser/renderer_host/pepper/pepper_printing_host.h
@@ -30,12 +30,10 @@ class CONTENT_EXPORT PepperPrintingHost : public ppapi::host::ResourceHost {
ppapi::host::HostMessageContext* context) OVERRIDE;
private:
- int32_t OnGetDefaultPrintSettings(
- ppapi::host::HostMessageContext* context);
+ int32_t OnGetDefaultPrintSettings(ppapi::host::HostMessageContext* context);
- void PrintSettingsCallback(
- ppapi::host::ReplyMessageContext reply_context,
- PepperPrintSettingsManager::Result result);
+ void PrintSettingsCallback(ppapi::host::ReplyMessageContext reply_context,
+ PepperPrintSettingsManager::Result result);
scoped_ptr<PepperPrintSettingsManager> print_settings_manager_;
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_printing_host_unittest.cc b/chromium/content/browser/renderer_host/pepper/pepper_printing_host_unittest.cc
index a46db10b384..d9780425d20 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_printing_host_unittest.cc
+++ b/chromium/content/browser/renderer_host/pepper/pepper_printing_host_unittest.cc
@@ -18,8 +18,7 @@ namespace content {
namespace {
// Mock implementation of |PepperPrintSettingsManager| for test purposes.
-class MockPepperPrintSettingsManager
- : public PepperPrintSettingsManager {
+class MockPepperPrintSettingsManager : public PepperPrintSettingsManager {
public:
MockPepperPrintSettingsManager(const PP_PrintSettings_Dev& settings);
virtual ~MockPepperPrintSettingsManager() {}
@@ -27,6 +26,7 @@ class MockPepperPrintSettingsManager
// PepperPrintSettingsManager implementation.
virtual void GetDefaultPrintSettings(
PepperPrintSettingsManager::Callback callback) OVERRIDE;
+
private:
PP_PrintSettings_Dev settings_;
@@ -35,23 +35,19 @@ class MockPepperPrintSettingsManager
MockPepperPrintSettingsManager::MockPepperPrintSettingsManager(
const PP_PrintSettings_Dev& settings)
- : settings_(settings) {
-}
+ : settings_(settings) {}
void MockPepperPrintSettingsManager::GetDefaultPrintSettings(
PepperPrintSettingsManager::Callback callback) {
callback.Run(PepperPrintSettingsManager::Result(settings_, PP_OK));
}
-class PepperPrintingHostTest
- : public testing::Test,
- public BrowserPpapiHostTest {
+class PepperPrintingHostTest : public testing::Test,
+ public BrowserPpapiHostTest {
public:
- PepperPrintingHostTest() {
- }
+ PepperPrintingHostTest() {}
- virtual ~PepperPrintingHostTest() {
- }
+ virtual ~PepperPrintingHostTest() {}
DISALLOW_COPY_AND_ASSIGN(PepperPrintingHostTest);
};
@@ -61,9 +57,8 @@ bool PP_SizeEqual(const PP_Size& lhs, const PP_Size& rhs) {
}
bool PP_RectEqual(const PP_Rect& lhs, const PP_Rect& rhs) {
- return lhs.point.x == rhs.point.x &&
- lhs.point.y == rhs.point.y &&
- PP_SizeEqual(lhs.size, rhs.size);
+ return lhs.point.x == rhs.point.x && lhs.point.y == rhs.point.y &&
+ PP_SizeEqual(lhs.size, rhs.size);
}
} // namespace
@@ -71,22 +66,22 @@ bool PP_RectEqual(const PP_Rect& lhs, const PP_Rect& rhs) {
TEST_F(PepperPrintingHostTest, GetDefaultPrintSettings) {
PP_Instance pp_instance = 12345;
PP_Resource pp_resource = 67890;
- PP_PrintSettings_Dev expected_settings = {
- { { 0, 0 }, { 500, 515 } },
- { { 25, 35 }, { 300, 720 } },
- { 600, 700 },
- 200,
- PP_PRINTORIENTATION_NORMAL,
- PP_PRINTSCALINGOPTION_NONE,
- PP_FALSE,
- PP_PRINTOUTPUTFORMAT_PDF
- };
+ PP_PrintSettings_Dev expected_settings = {{{0, 0}, {500, 515}},
+ {{25, 35}, {300, 720}},
+ {600, 700},
+ 200,
+ PP_PRINTORIENTATION_NORMAL,
+ PP_PRINTSCALINGOPTION_NONE,
+ PP_FALSE,
+ PP_PRINTOUTPUTFORMAT_PDF};
// Construct the resource host.
scoped_ptr<PepperPrintSettingsManager> manager(
new MockPepperPrintSettingsManager(expected_settings));
PepperPrintingHost printing(GetBrowserPpapiHost()->GetPpapiHost(),
- pp_instance, pp_resource, manager.Pass());
+ pp_instance,
+ pp_resource,
+ manager.Pass());
// Simulate a message being received.
ppapi::proxy::ResourceMessageCallParams call_params(pp_resource, 1);
@@ -99,7 +94,8 @@ TEST_F(PepperPrintingHostTest, GetDefaultPrintSettings) {
ppapi::proxy::ResourceMessageReplyParams reply_params;
IPC::Message reply_msg;
ASSERT_TRUE(sink().GetFirstResourceReplyMatching(
- PpapiPluginMsg_Printing_GetDefaultPrintSettingsReply::ID, &reply_params,
+ PpapiPluginMsg_Printing_GetDefaultPrintSettingsReply::ID,
+ &reply_params,
&reply_msg));
// Validation of reply.
@@ -115,8 +111,8 @@ TEST_F(PepperPrintingHostTest, GetDefaultPrintSettings) {
actual_settings.printable_area));
EXPECT_TRUE(PP_RectEqual(expected_settings.content_area,
actual_settings.content_area));
- EXPECT_TRUE(PP_SizeEqual(expected_settings.paper_size,
- actual_settings.paper_size));
+ EXPECT_TRUE(
+ PP_SizeEqual(expected_settings.paper_size, actual_settings.paper_size));
EXPECT_EQ(expected_settings.dpi, actual_settings.dpi);
EXPECT_EQ(expected_settings.orientation, actual_settings.orientation);
EXPECT_EQ(expected_settings.print_scaling_option,
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_renderer_connection.cc b/chromium/content/browser/renderer_host/pepper/pepper_renderer_connection.cc
index 1c00388870c..8c233bbc6a7 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_renderer_connection.cc
+++ b/chromium/content/browser/renderer_host/pepper/pepper_renderer_connection.cc
@@ -19,18 +19,18 @@
#include "ppapi/host/resource_host.h"
#include "ppapi/proxy/ppapi_message_utils.h"
#include "ppapi/proxy/ppapi_messages.h"
-#include "ppapi/proxy/ppapi_message_utils.h"
#include "ppapi/proxy/resource_message_params.h"
namespace content {
namespace {
+const uint32 kFilteredMessageClasses[] = {PpapiMsgStart, ViewMsgStart, };
+
// Responsible for creating the pending resource hosts, holding their IDs until
// all of them have been created for a single message, and sending the reply to
// say that the hosts have been created.
-class PendingHostCreator
- : public base::RefCounted<PendingHostCreator> {
+class PendingHostCreator : public base::RefCounted<PendingHostCreator> {
public:
PendingHostCreator(BrowserPpapiHostImpl* host,
BrowserMessageFilter* connection,
@@ -87,7 +87,9 @@ PendingHostCreator::~PendingHostCreator() {
} // namespace
PepperRendererConnection::PepperRendererConnection(int render_process_id)
- : render_process_id_(render_process_id) {
+ : BrowserMessageFilter(kFilteredMessageClasses,
+ arraysize(kFilteredMessageClasses)),
+ render_process_id_(render_process_id) {
// Only give the renderer permission for stable APIs.
in_process_host_.reset(new BrowserPpapiHostImpl(this,
ppapi::PpapiPermissions(),
@@ -98,12 +100,11 @@ PepperRendererConnection::PepperRendererConnection(int render_process_id)
false /* external_plugin */));
}
-PepperRendererConnection::~PepperRendererConnection() {
-}
+PepperRendererConnection::~PepperRendererConnection() {}
BrowserPpapiHostImpl* PepperRendererConnection::GetHostForChildProcess(
int child_process_id) const {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
// Find the plugin which this message refers to. Check NaCl plugins first.
BrowserPpapiHostImpl* host = static_cast<BrowserPpapiHostImpl*>(
@@ -131,13 +132,12 @@ BrowserPpapiHostImpl* PepperRendererConnection::GetHostForChildProcess(
return host;
}
-bool PepperRendererConnection::OnMessageReceived(const IPC::Message& msg,
- bool* message_was_ok) {
+bool PepperRendererConnection::OnMessageReceived(const IPC::Message& msg) {
if (in_process_host_->GetPpapiHost()->OnMessageReceived(msg))
return true;
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(PepperRendererConnection, msg, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(PepperRendererConnection, msg)
IPC_MESSAGE_HANDLER(PpapiHostMsg_CreateResourceHostsFromHost,
OnMsgCreateResourceHostsFromHost)
IPC_MESSAGE_HANDLER(ViewHostMsg_DidCreateInProcessInstance,
@@ -145,7 +145,7 @@ bool PepperRendererConnection::OnMessageReceived(const IPC::Message& msg,
IPC_MESSAGE_HANDLER(ViewHostMsg_DidDeleteInProcessInstance,
OnMsgDidDeleteInProcessInstance)
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return handled;
}
@@ -168,12 +168,12 @@ void PepperRendererConnection::OnMsgCreateResourceHostsFromHost(
const IPC::Message& nested_msg = nested_msgs[i];
scoped_ptr<ppapi::host::ResourceHost> resource_host;
if (host->IsValidInstance(instance)) {
- if (nested_msg.type() == PpapiHostMsg_FileRef_CreateExternal::ID) {
- // FileRef_CreateExternal is only permitted from the renderer. Because
+ if (nested_msg.type() == PpapiHostMsg_FileRef_CreateForRawFS::ID) {
+ // FileRef_CreateForRawFS is only permitted from the renderer. Because
// of this, we handle this message here and not in
// content_browser_pepper_host_factory.cc.
base::FilePath external_path;
- if (ppapi::UnpackMessage<PpapiHostMsg_FileRef_CreateExternal>(
+ if (ppapi::UnpackMessage<PpapiHostMsg_FileRef_CreateForRawFS>(
nested_msg, &external_path)) {
resource_host.reset(new PepperFileRefHost(
host, instance, params.pp_resource(), external_path));
@@ -187,21 +187,18 @@ void PepperRendererConnection::OnMsgCreateResourceHostsFromHost(
if (ppapi::UnpackMessage<PpapiHostMsg_FileSystem_CreateFromRenderer>(
nested_msg, &root_url, &file_system_type)) {
PepperFileSystemBrowserHost* browser_host =
- new PepperFileSystemBrowserHost(host,
- instance,
- params.pp_resource(),
- file_system_type);
+ new PepperFileSystemBrowserHost(
+ host, instance, params.pp_resource(), file_system_type);
resource_host.reset(browser_host);
// Open the file system resource host. This is an asynchronous
// operation, and we must only add the pending resource host and
// send the message once it completes.
browser_host->OpenExisting(
GURL(root_url),
- base::Bind(
- &PendingHostCreator::AddPendingResourceHost,
- creator,
- i,
- base::Passed(&resource_host)));
+ base::Bind(&PendingHostCreator::AddPendingResourceHost,
+ creator,
+ i,
+ base::Passed(&resource_host)));
// Do not fall through; the fall-through case adds the pending
// resource host to the list. We must do this asynchronously.
continue;
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_renderer_connection.h b/chromium/content/browser/renderer_host/pepper/pepper_renderer_connection.h
index 157827a9cfd..5683ab9ed0c 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_renderer_connection.h
+++ b/chromium/content/browser/renderer_host/pepper/pepper_renderer_connection.h
@@ -36,8 +36,7 @@ class PepperRendererConnection : public BrowserMessageFilter {
explicit PepperRendererConnection(int render_process_id);
// BrowserMessageFilter overrides.
- virtual bool OnMessageReceived(const IPC::Message& msg,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE;
private:
virtual ~PepperRendererConnection();
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_security_helper.cc b/chromium/content/browser/renderer_host/pepper/pepper_security_helper.cc
index 4e1306eb816..ab516283d58 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_security_helper.cc
+++ b/chromium/content/browser/renderer_host/pepper/pepper_security_helper.cc
@@ -12,8 +12,10 @@ namespace content {
namespace {
-template <typename CanRead, typename CanWrite,
- typename CanCreate, typename CanCreateReadWrite,
+template <typename CanRead,
+ typename CanWrite,
+ typename CanCreate,
+ typename CanCreateReadWrite,
typename FileID>
bool CanOpenFileWithPepperFlags(CanRead can_read,
CanWrite can_write,
@@ -58,27 +60,32 @@ bool CanOpenFileWithPepperFlags(CanRead can_read,
return true;
}
-
}
-bool CanOpenWithPepperFlags(int pp_open_flags, int child_id,
+bool CanOpenWithPepperFlags(int pp_open_flags,
+ int child_id,
const base::FilePath& file) {
return CanOpenFileWithPepperFlags(
&ChildProcessSecurityPolicyImpl::CanReadFile,
&ChildProcessSecurityPolicyImpl::CanCreateReadWriteFile,
&ChildProcessSecurityPolicyImpl::CanCreateReadWriteFile,
&ChildProcessSecurityPolicyImpl::CanCreateReadWriteFile,
- pp_open_flags, child_id, file);
+ pp_open_flags,
+ child_id,
+ file);
}
-bool CanOpenFileSystemURLWithPepperFlags(int pp_open_flags, int child_id,
+bool CanOpenFileSystemURLWithPepperFlags(int pp_open_flags,
+ int child_id,
const fileapi::FileSystemURL& url) {
return CanOpenFileWithPepperFlags(
&ChildProcessSecurityPolicyImpl::CanReadFileSystemFile,
&ChildProcessSecurityPolicyImpl::CanWriteFileSystemFile,
&ChildProcessSecurityPolicyImpl::CanCreateFileSystemFile,
&ChildProcessSecurityPolicyImpl::CanCreateReadWriteFileSystemFile,
- pp_open_flags, child_id, url);
+ pp_open_flags,
+ child_id,
+ url);
}
} // namespace content
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_socket_utils.cc b/chromium/content/browser/renderer_host/pepper/pepper_socket_utils.cc
index 2f109cddb71..dda97a1718b 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_socket_utils.cc
+++ b/chromium/content/browser/renderer_host/pepper/pepper_socket_utils.cc
@@ -9,9 +9,10 @@
#include "base/logging.h"
#include "base/memory/ref_counted.h"
+#include "base/strings/string_util.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
-#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/site_instance.h"
#include "content/public/common/content_client.h"
#include "net/cert/x509_certificate.h"
@@ -25,13 +26,12 @@ namespace pepper_socket_utils {
SocketPermissionRequest CreateSocketPermissionRequest(
SocketPermissionRequest::OperationType type,
const PP_NetAddress_Private& net_addr) {
- std::string host = ppapi::NetAddressPrivateImpl::DescribeNetAddress(net_addr,
- false);
+ std::string host =
+ ppapi::NetAddressPrivateImpl::DescribeNetAddress(net_addr, false);
int port = 0;
std::vector<unsigned char> address;
- ppapi::NetAddressPrivateImpl::NetAddressToIPEndPoint(net_addr,
- &address,
- &port);
+ ppapi::NetAddressPrivateImpl::NetAddressToIPEndPoint(
+ net_addr, &address, &port);
return SocketPermissionRequest(type, host, port);
}
@@ -39,22 +39,8 @@ bool CanUseSocketAPIs(bool external_plugin,
bool private_api,
const SocketPermissionRequest* params,
int render_process_id,
- int render_view_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- RenderViewHost* render_view_host = RenderViewHost::FromID(render_process_id,
- render_view_id);
- return render_view_host && CanUseSocketAPIs(external_plugin,
- private_api,
- params,
- render_view_host);
-}
-
-bool CanUseSocketAPIs(bool external_plugin,
- bool private_api,
- const SocketPermissionRequest* params,
- RenderViewHost* render_view_host) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
+ int render_frame_id) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!external_plugin) {
// Always allow socket APIs for out-process plugins (other than external
// plugins instantiated by the embeeder through
@@ -62,9 +48,11 @@ bool CanUseSocketAPIs(bool external_plugin,
return true;
}
- if (!render_view_host)
+ RenderFrameHost* render_frame_host =
+ RenderFrameHost::FromID(render_process_id, render_frame_id);
+ if (!render_frame_host)
return false;
- SiteInstance* site_instance = render_view_host->GetSiteInstance();
+ SiteInstance* site_instance = render_frame_host->GetSiteInstance();
if (!site_instance)
return false;
if (!GetContentClient()->browser()->AllowPepperSocketAPI(
@@ -84,43 +72,48 @@ bool GetCertificateFields(const net::X509Certificate& cert,
ppapi::PPB_X509Certificate_Fields* fields) {
const net::CertPrincipal& issuer = cert.issuer();
fields->SetField(PP_X509CERTIFICATE_PRIVATE_ISSUER_COMMON_NAME,
- new base::StringValue(issuer.common_name));
+ new base::StringValue(issuer.common_name));
fields->SetField(PP_X509CERTIFICATE_PRIVATE_ISSUER_LOCALITY_NAME,
- new base::StringValue(issuer.locality_name));
+ new base::StringValue(issuer.locality_name));
fields->SetField(PP_X509CERTIFICATE_PRIVATE_ISSUER_STATE_OR_PROVINCE_NAME,
- new base::StringValue(issuer.state_or_province_name));
+ new base::StringValue(issuer.state_or_province_name));
fields->SetField(PP_X509CERTIFICATE_PRIVATE_ISSUER_COUNTRY_NAME,
- new base::StringValue(issuer.country_name));
- fields->SetField(PP_X509CERTIFICATE_PRIVATE_ISSUER_ORGANIZATION_NAME,
+ new base::StringValue(issuer.country_name));
+ fields->SetField(
+ PP_X509CERTIFICATE_PRIVATE_ISSUER_ORGANIZATION_NAME,
new base::StringValue(JoinString(issuer.organization_names, '\n')));
- fields->SetField(PP_X509CERTIFICATE_PRIVATE_ISSUER_ORGANIZATION_UNIT_NAME,
+ fields->SetField(
+ PP_X509CERTIFICATE_PRIVATE_ISSUER_ORGANIZATION_UNIT_NAME,
new base::StringValue(JoinString(issuer.organization_unit_names, '\n')));
const net::CertPrincipal& subject = cert.subject();
fields->SetField(PP_X509CERTIFICATE_PRIVATE_SUBJECT_COMMON_NAME,
- new base::StringValue(subject.common_name));
+ new base::StringValue(subject.common_name));
fields->SetField(PP_X509CERTIFICATE_PRIVATE_SUBJECT_LOCALITY_NAME,
- new base::StringValue(subject.locality_name));
+ new base::StringValue(subject.locality_name));
fields->SetField(PP_X509CERTIFICATE_PRIVATE_SUBJECT_STATE_OR_PROVINCE_NAME,
- new base::StringValue(subject.state_or_province_name));
+ new base::StringValue(subject.state_or_province_name));
fields->SetField(PP_X509CERTIFICATE_PRIVATE_SUBJECT_COUNTRY_NAME,
- new base::StringValue(subject.country_name));
- fields->SetField(PP_X509CERTIFICATE_PRIVATE_SUBJECT_ORGANIZATION_NAME,
+ new base::StringValue(subject.country_name));
+ fields->SetField(
+ PP_X509CERTIFICATE_PRIVATE_SUBJECT_ORGANIZATION_NAME,
new base::StringValue(JoinString(subject.organization_names, '\n')));
- fields->SetField(PP_X509CERTIFICATE_PRIVATE_SUBJECT_ORGANIZATION_UNIT_NAME,
+ fields->SetField(
+ PP_X509CERTIFICATE_PRIVATE_SUBJECT_ORGANIZATION_UNIT_NAME,
new base::StringValue(JoinString(subject.organization_unit_names, '\n')));
const std::string& serial_number = cert.serial_number();
fields->SetField(PP_X509CERTIFICATE_PRIVATE_SERIAL_NUMBER,
- base::BinaryValue::CreateWithCopiedBuffer(serial_number.data(),
- serial_number.length()));
+ base::BinaryValue::CreateWithCopiedBuffer(
+ serial_number.data(), serial_number.length()));
fields->SetField(PP_X509CERTIFICATE_PRIVATE_VALIDITY_NOT_BEFORE,
- new base::FundamentalValue(cert.valid_start().ToDoubleT()));
+ new base::FundamentalValue(cert.valid_start().ToDoubleT()));
fields->SetField(PP_X509CERTIFICATE_PRIVATE_VALIDITY_NOT_AFTER,
- new base::FundamentalValue(cert.valid_expiry().ToDoubleT()));
+ new base::FundamentalValue(cert.valid_expiry().ToDoubleT()));
std::string der;
net::X509Certificate::GetDEREncoded(cert.os_cert_handle(), &der);
- fields->SetField(PP_X509CERTIFICATE_PRIVATE_RAW,
+ fields->SetField(
+ PP_X509CERTIFICATE_PRIVATE_RAW,
base::BinaryValue::CreateWithCopiedBuffer(der.data(), der.length()));
return true;
}
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_socket_utils.h b/chromium/content/browser/renderer_host/pepper/pepper_socket_utils.h
index 1a1a26cac19..e7bc1e3eb04 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_socket_utils.h
+++ b/chromium/content/browser/renderer_host/pepper/pepper_socket_utils.h
@@ -20,8 +20,6 @@ class PPB_X509Certificate_Fields;
namespace content {
-class RenderViewHost;
-
namespace pepper_socket_utils {
SocketPermissionRequest CreateSocketPermissionRequest(
@@ -35,13 +33,7 @@ bool CanUseSocketAPIs(bool external_plugin,
bool private_api,
const SocketPermissionRequest* params,
int render_process_id,
- int render_view_id);
-
-// TODO (ygorshenin@): remove this method.
-bool CanUseSocketAPIs(bool external_plugin,
- bool private_api,
- const SocketPermissionRequest* params,
- RenderViewHost* render_view_host);
+ int render_frame_id);
// Extracts the certificate field data from a net::X509Certificate into
// PPB_X509Certificate_Fields.
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.cc b/chromium/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.cc
index 063c11ed784..87ba64f54c1 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.cc
+++ b/chromium/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.cc
@@ -49,13 +49,12 @@ PepperTCPServerSocketMessageFilter::PepperTCPServerSocketMessageFilter(
external_plugin_(host->external_plugin()),
private_api_(private_api),
render_process_id_(0),
- render_view_id_(0) {
+ render_frame_id_(0) {
++g_num_instances;
DCHECK(factory_);
DCHECK(ppapi_host_);
- if (!host->GetRenderViewIDsForInstance(instance,
- &render_process_id_,
- &render_view_id_)) {
+ if (!host->GetRenderFrameIDsForInstance(
+ instance, &render_process_id_, &render_frame_id_)) {
NOTREACHED();
}
}
@@ -85,14 +84,14 @@ PepperTCPServerSocketMessageFilter::OverrideTaskRunnerForMessage(
int32_t PepperTCPServerSocketMessageFilter::OnResourceMessageReceived(
const IPC::Message& msg,
ppapi::host::HostMessageContext* context) {
- IPC_BEGIN_MESSAGE_MAP(PepperTCPServerSocketMessageFilter, msg)
- PPAPI_DISPATCH_HOST_RESOURCE_CALL(
- PpapiHostMsg_TCPServerSocket_Listen, OnMsgListen)
- PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
- PpapiHostMsg_TCPServerSocket_Accept, OnMsgAccept)
+ PPAPI_BEGIN_MESSAGE_MAP(PepperTCPServerSocketMessageFilter, msg)
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_TCPServerSocket_Listen,
+ OnMsgListen)
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_TCPServerSocket_Accept,
+ OnMsgAccept)
PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
PpapiHostMsg_TCPServerSocket_StopListening, OnMsgStopListening)
- IPC_END_MESSAGE_MAP()
+ PPAPI_END_MESSAGE_MAP()
return PP_ERROR_FAILED;
}
@@ -100,7 +99,7 @@ int32_t PepperTCPServerSocketMessageFilter::OnMsgListen(
const ppapi::host::HostMessageContext* context,
const PP_NetAddress_Private& addr,
int32_t backlog) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(context);
SocketPermissionRequest request =
@@ -110,20 +109,24 @@ int32_t PepperTCPServerSocketMessageFilter::OnMsgListen(
private_api_,
&request,
render_process_id_,
- render_view_id_)) {
+ render_frame_id_)) {
return PP_ERROR_NOACCESS;
}
BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::Bind(&PepperTCPServerSocketMessageFilter::DoListen, this,
- context->MakeReplyMessageContext(), addr, backlog));
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&PepperTCPServerSocketMessageFilter::DoListen,
+ this,
+ context->MakeReplyMessageContext(),
+ addr,
+ backlog));
return PP_OK_COMPLETIONPENDING;
}
int32_t PepperTCPServerSocketMessageFilter::OnMsgAccept(
const ppapi::host::HostMessageContext* context) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(context);
if (state_ != STATE_LISTENING)
@@ -136,7 +139,8 @@ int32_t PepperTCPServerSocketMessageFilter::OnMsgAccept(
&accepted_socket_,
&accepted_address_,
base::Bind(&PepperTCPServerSocketMessageFilter::OnAcceptCompleted,
- base::Unretained(this), reply_context));
+ base::Unretained(this),
+ reply_context));
if (net_result != net::ERR_IO_PENDING)
OnAcceptCompleted(reply_context, net_result);
return PP_OK_COMPLETIONPENDING;
@@ -144,7 +148,7 @@ int32_t PepperTCPServerSocketMessageFilter::OnMsgAccept(
int32_t PepperTCPServerSocketMessageFilter::OnMsgStopListening(
const ppapi::host::HostMessageContext* context) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(context);
state_ = STATE_CLOSED;
@@ -156,7 +160,7 @@ void PepperTCPServerSocketMessageFilter::DoListen(
const ppapi::host::ReplyMessageContext& context,
const PP_NetAddress_Private& addr,
int32_t backlog) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
net::IPAddressNumber address;
int port;
@@ -215,9 +219,8 @@ void PepperTCPServerSocketMessageFilter::OnListenCompleted(
state_ = STATE_BEFORE_LISTENING;
return;
}
- if (!NetAddressPrivateImpl::IPEndPointToNetAddress(end_point.address(),
- end_point.port(),
- &addr)) {
+ if (!NetAddressPrivateImpl::IPEndPointToNetAddress(
+ end_point.address(), end_point.port(), &addr)) {
SendListenError(context, PP_ERROR_FAILED);
state_ = STATE_BEFORE_LISTENING;
return;
@@ -249,9 +252,8 @@ void PepperTCPServerSocketMessageFilter::OnAcceptCompleted(
PP_NetAddress_Private local_addr = NetAddressPrivateImpl::kInvalidNetAddress;
PP_NetAddress_Private remote_addr = NetAddressPrivateImpl::kInvalidNetAddress;
- int32_t pp_result =
- NetErrorToPepperError(accepted_socket_->GetLocalAddress(
- &ip_end_point_local));
+ int32_t pp_result = NetErrorToPepperError(
+ accepted_socket_->GetLocalAddress(&ip_end_point_local));
if (pp_result != PP_OK) {
SendAcceptError(context, pp_result);
return;
@@ -269,18 +271,17 @@ void PepperTCPServerSocketMessageFilter::OnAcceptCompleted(
}
scoped_ptr<ppapi::host::ResourceHost> host =
- factory_->CreateAcceptedTCPSocket(
- instance_, ppapi::TCP_SOCKET_VERSION_PRIVATE,
- accepted_socket_.Pass());
+ factory_->CreateAcceptedTCPSocket(instance_,
+ ppapi::TCP_SOCKET_VERSION_PRIVATE,
+ accepted_socket_.Pass());
if (!host) {
SendAcceptError(context, PP_ERROR_NOSPACE);
return;
}
int pending_resource_id = ppapi_host_->AddPendingResourceHost(host.Pass());
if (pending_resource_id) {
- SendAcceptReply(context, PP_OK, pending_resource_id,
- local_addr,
- remote_addr);
+ SendAcceptReply(
+ context, PP_OK, pending_resource_id, local_addr, remote_addr);
} else {
SendAcceptError(context, PP_ERROR_NOSPACE);
}
@@ -299,8 +300,8 @@ void PepperTCPServerSocketMessageFilter::SendListenReply(
void PepperTCPServerSocketMessageFilter::SendListenError(
const ppapi::host::ReplyMessageContext& context,
int32_t pp_result) {
- SendListenReply(context, pp_result,
- NetAddressPrivateImpl::kInvalidNetAddress);
+ SendListenReply(
+ context, pp_result, NetAddressPrivateImpl::kInvalidNetAddress);
}
void PepperTCPServerSocketMessageFilter::SendAcceptReply(
@@ -311,8 +312,9 @@ void PepperTCPServerSocketMessageFilter::SendAcceptReply(
const PP_NetAddress_Private& remote_addr) {
ppapi::host::ReplyMessageContext reply_context(context);
reply_context.params.set_result(pp_result);
- SendReply(reply_context, PpapiPluginMsg_TCPServerSocket_AcceptReply(
- pending_resource_id, local_addr, remote_addr));
+ SendReply(reply_context,
+ PpapiPluginMsg_TCPServerSocket_AcceptReply(
+ pending_resource_id, local_addr, remote_addr));
}
void PepperTCPServerSocketMessageFilter::SendAcceptError(
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.h b/chromium/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.h
index 5d17333c9e8..43262fc8c1a 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.h
+++ b/chromium/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.h
@@ -33,11 +33,10 @@ class ContentBrowserPepperHostFactory;
class CONTENT_EXPORT PepperTCPServerSocketMessageFilter
: public ppapi::host::ResourceMessageFilter {
public:
- PepperTCPServerSocketMessageFilter(
- ContentBrowserPepperHostFactory* factory,
- BrowserPpapiHostImpl* host,
- PP_Instance instance,
- bool private_api);
+ PepperTCPServerSocketMessageFilter(ContentBrowserPepperHostFactory* factory,
+ BrowserPpapiHostImpl* host,
+ PP_Instance instance,
+ bool private_api);
static size_t GetNumInstances();
@@ -105,7 +104,7 @@ class CONTENT_EXPORT PepperTCPServerSocketMessageFilter
const bool external_plugin_;
const bool private_api_;
int render_process_id_;
- int render_view_id_;
+ int render_frame_id_;
DISALLOW_COPY_AND_ASSIGN(PepperTCPServerSocketMessageFilter);
};
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket.cc b/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket.cc
index c5b752aa7f5..474a2fd6bd5 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket.cc
+++ b/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket.cc
@@ -37,12 +37,11 @@ using ppapi::NetAddressPrivateImpl;
namespace content {
-PepperTCPSocket::PepperTCPSocket(
- PepperMessageFilter* manager,
- int32 routing_id,
- uint32 plugin_dispatcher_id,
- uint32 socket_id,
- bool private_api)
+PepperTCPSocket::PepperTCPSocket(PepperMessageFilter* manager,
+ int32 routing_id,
+ uint32 plugin_dispatcher_id,
+ uint32 socket_id,
+ bool private_api)
: manager_(manager),
routing_id_(routing_id),
plugin_dispatcher_id_(plugin_dispatcher_id),
@@ -53,13 +52,12 @@ PepperTCPSocket::PepperTCPSocket(
DCHECK(manager);
}
-PepperTCPSocket::PepperTCPSocket(
- PepperMessageFilter* manager,
- int32 routing_id,
- uint32 plugin_dispatcher_id,
- uint32 socket_id,
- net::StreamSocket* socket,
- bool private_api)
+PepperTCPSocket::PepperTCPSocket(PepperMessageFilter* manager,
+ int32 routing_id,
+ uint32 plugin_dispatcher_id,
+ uint32 socket_id,
+ net::StreamSocket* socket,
+ bool private_api)
: manager_(manager),
routing_id_(routing_id),
plugin_dispatcher_id_(plugin_dispatcher_id),
@@ -78,7 +76,7 @@ PepperTCPSocket::~PepperTCPSocket() {
}
void PepperTCPSocket::Connect(const std::string& host, uint16_t port) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (connection_state_ != BEFORE_CONNECT) {
SendConnectACKError(PP_ERROR_FAILED);
@@ -101,7 +99,7 @@ void PepperTCPSocket::Connect(const std::string& host, uint16_t port) {
void PepperTCPSocket::ConnectWithNetAddress(
const PP_NetAddress_Private& net_addr) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (connection_state_ != BEFORE_CONNECT) {
SendConnectACKError(PP_ERROR_FAILED);
@@ -110,8 +108,8 @@ void PepperTCPSocket::ConnectWithNetAddress(
net::IPAddressNumber address;
int port;
- if (!NetAddressPrivateImpl::NetAddressToIPEndPoint(net_addr, &address,
- &port)) {
+ if (!NetAddressPrivateImpl::NetAddressToIPEndPoint(
+ net_addr, &address, &port)) {
SendConnectACKError(PP_ERROR_ADDRESS_INVALID);
return;
}
@@ -128,7 +126,7 @@ void PepperTCPSocket::SSLHandshake(
uint16_t server_port,
const std::vector<std::vector<char> >& trusted_certs,
const std::vector<std::vector<char> >& untrusted_certs) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
// Allow to do SSL handshake only if currently the socket has been connected
// and there isn't pending read or write.
@@ -159,15 +157,14 @@ void PepperTCPSocket::SSLHandshake(
return;
}
- int net_result = socket_->Connect(
- base::Bind(&PepperTCPSocket::OnSSLHandshakeCompleted,
- base::Unretained(this)));
+ int net_result = socket_->Connect(base::Bind(
+ &PepperTCPSocket::OnSSLHandshakeCompleted, base::Unretained(this)));
if (net_result != net::ERR_IO_PENDING)
OnSSLHandshakeCompleted(net_result);
}
void PepperTCPSocket::Read(int32 bytes_to_read) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!IsConnected() || end_of_file_reached_) {
SendReadACKError(PP_ERROR_FAILED);
@@ -195,7 +192,7 @@ void PepperTCPSocket::Read(int32 bytes_to_read) {
}
void PepperTCPSocket::Write(const std::string& data) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!IsConnected()) {
SendWriteACKError(PP_ERROR_FAILED);
@@ -223,7 +220,7 @@ void PepperTCPSocket::Write(const std::string& data) {
void PepperTCPSocket::SetOption(PP_TCPSocket_Option name,
const ppapi::SocketOptionData& value) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!IsConnected() || IsSsl()) {
SendSetOptionACK(PP_ERROR_FAILED);
@@ -242,8 +239,8 @@ void PepperTCPSocket::SetOption(PP_TCPSocket_Option name,
return;
}
- SendSetOptionACK(
- tcp_socket->SetNoDelay(boolean_value) ? PP_OK : PP_ERROR_FAILED);
+ SendSetOptionACK(tcp_socket->SetNoDelay(boolean_value) ? PP_OK
+ : PP_ERROR_FAILED);
return;
}
case PP_TCPSOCKET_OPTION_SEND_BUFFER_SIZE:
@@ -254,21 +251,22 @@ void PepperTCPSocket::SetOption(PP_TCPSocket_Option name,
return;
}
- bool result = false;
+ int net_result = net::OK;
if (name == PP_TCPSOCKET_OPTION_SEND_BUFFER_SIZE) {
if (integer_value > ppapi::TCPSocketShared::kMaxSendBufferSize) {
SendSetOptionACK(PP_ERROR_BADARGUMENT);
return;
}
- result = tcp_socket->SetSendBufferSize(integer_value);
+ net_result = tcp_socket->SetSendBufferSize(integer_value);
} else {
if (integer_value > ppapi::TCPSocketShared::kMaxReceiveBufferSize) {
SendSetOptionACK(PP_ERROR_BADARGUMENT);
return;
}
- result = tcp_socket->SetReceiveBufferSize(integer_value);
+ net_result = tcp_socket->SetReceiveBufferSize(integer_value);
}
- SendSetOptionACK(result ? PP_OK : PP_ERROR_FAILED);
+ // TODO(wtc): Add error mapping.
+ SendSetOptionACK((net_result == net::OK) ? PP_OK : PP_ERROR_FAILED);
return;
}
default: {
@@ -282,18 +280,20 @@ void PepperTCPSocket::SetOption(PP_TCPSocket_Option name,
void PepperTCPSocket::StartConnect(const net::AddressList& addresses) {
DCHECK(connection_state_ == CONNECT_IN_PROGRESS);
- socket_.reset(new net::TCPClientSocket(addresses, NULL,
- net::NetLog::Source()));
+ socket_.reset(
+ new net::TCPClientSocket(addresses, NULL, net::NetLog::Source()));
int net_result = socket_->Connect(
- base::Bind(&PepperTCPSocket::OnConnectCompleted,
- base::Unretained(this)));
+ base::Bind(&PepperTCPSocket::OnConnectCompleted, base::Unretained(this)));
if (net_result != net::ERR_IO_PENDING)
OnConnectCompleted(net_result);
}
void PepperTCPSocket::SendConnectACKError(int32_t error) {
manager_->Send(new PpapiMsg_PPBTCPSocket_ConnectACK(
- routing_id_, plugin_dispatcher_id_, socket_id_, error,
+ routing_id_,
+ plugin_dispatcher_id_,
+ socket_id_,
+ error,
NetAddressPrivateImpl::kInvalidNetAddress,
NetAddressPrivateImpl::kInvalidNetAddress));
}
@@ -304,43 +304,48 @@ bool PepperTCPSocket::GetCertificateFields(
ppapi::PPB_X509Certificate_Fields* fields) {
const net::CertPrincipal& issuer = cert.issuer();
fields->SetField(PP_X509CERTIFICATE_PRIVATE_ISSUER_COMMON_NAME,
- new base::StringValue(issuer.common_name));
+ new base::StringValue(issuer.common_name));
fields->SetField(PP_X509CERTIFICATE_PRIVATE_ISSUER_LOCALITY_NAME,
- new base::StringValue(issuer.locality_name));
+ new base::StringValue(issuer.locality_name));
fields->SetField(PP_X509CERTIFICATE_PRIVATE_ISSUER_STATE_OR_PROVINCE_NAME,
- new base::StringValue(issuer.state_or_province_name));
+ new base::StringValue(issuer.state_or_province_name));
fields->SetField(PP_X509CERTIFICATE_PRIVATE_ISSUER_COUNTRY_NAME,
- new base::StringValue(issuer.country_name));
- fields->SetField(PP_X509CERTIFICATE_PRIVATE_ISSUER_ORGANIZATION_NAME,
+ new base::StringValue(issuer.country_name));
+ fields->SetField(
+ PP_X509CERTIFICATE_PRIVATE_ISSUER_ORGANIZATION_NAME,
new base::StringValue(JoinString(issuer.organization_names, '\n')));
- fields->SetField(PP_X509CERTIFICATE_PRIVATE_ISSUER_ORGANIZATION_UNIT_NAME,
+ fields->SetField(
+ PP_X509CERTIFICATE_PRIVATE_ISSUER_ORGANIZATION_UNIT_NAME,
new base::StringValue(JoinString(issuer.organization_unit_names, '\n')));
const net::CertPrincipal& subject = cert.subject();
fields->SetField(PP_X509CERTIFICATE_PRIVATE_SUBJECT_COMMON_NAME,
- new base::StringValue(subject.common_name));
+ new base::StringValue(subject.common_name));
fields->SetField(PP_X509CERTIFICATE_PRIVATE_SUBJECT_LOCALITY_NAME,
- new base::StringValue(subject.locality_name));
+ new base::StringValue(subject.locality_name));
fields->SetField(PP_X509CERTIFICATE_PRIVATE_SUBJECT_STATE_OR_PROVINCE_NAME,
- new base::StringValue(subject.state_or_province_name));
+ new base::StringValue(subject.state_or_province_name));
fields->SetField(PP_X509CERTIFICATE_PRIVATE_SUBJECT_COUNTRY_NAME,
- new base::StringValue(subject.country_name));
- fields->SetField(PP_X509CERTIFICATE_PRIVATE_SUBJECT_ORGANIZATION_NAME,
+ new base::StringValue(subject.country_name));
+ fields->SetField(
+ PP_X509CERTIFICATE_PRIVATE_SUBJECT_ORGANIZATION_NAME,
new base::StringValue(JoinString(subject.organization_names, '\n')));
- fields->SetField(PP_X509CERTIFICATE_PRIVATE_SUBJECT_ORGANIZATION_UNIT_NAME,
+ fields->SetField(
+ PP_X509CERTIFICATE_PRIVATE_SUBJECT_ORGANIZATION_UNIT_NAME,
new base::StringValue(JoinString(subject.organization_unit_names, '\n')));
const std::string& serial_number = cert.serial_number();
fields->SetField(PP_X509CERTIFICATE_PRIVATE_SERIAL_NUMBER,
- base::BinaryValue::CreateWithCopiedBuffer(serial_number.data(),
- serial_number.length()));
+ base::BinaryValue::CreateWithCopiedBuffer(
+ serial_number.data(), serial_number.length()));
fields->SetField(PP_X509CERTIFICATE_PRIVATE_VALIDITY_NOT_BEFORE,
- new base::FundamentalValue(cert.valid_start().ToDoubleT()));
+ new base::FundamentalValue(cert.valid_start().ToDoubleT()));
fields->SetField(PP_X509CERTIFICATE_PRIVATE_VALIDITY_NOT_AFTER,
- new base::FundamentalValue(cert.valid_expiry().ToDoubleT()));
+ new base::FundamentalValue(cert.valid_expiry().ToDoubleT()));
std::string der;
net::X509Certificate::GetDEREncoded(cert.os_cert_handle(), &der);
- fields->SetField(PP_X509CERTIFICATE_PRIVATE_RAW,
+ fields->SetField(
+ PP_X509CERTIFICATE_PRIVATE_RAW,
base::BinaryValue::CreateWithCopiedBuffer(der.data(), der.length()));
return true;
}
@@ -359,7 +364,7 @@ bool PepperTCPSocket::GetCertificateFields(
void PepperTCPSocket::SendReadACKError(int32_t error) {
manager_->Send(new PpapiMsg_PPBTCPSocket_ReadACK(
- routing_id_, plugin_dispatcher_id_, socket_id_, error, std::string()));
+ routing_id_, plugin_dispatcher_id_, socket_id_, error, std::string()));
}
void PepperTCPSocket::SendWriteACKError(int32_t error) {
@@ -379,12 +384,12 @@ void PepperTCPSocket::SendSSLHandshakeACK(bool succeeded) {
if (ssl_info.cert.get())
GetCertificateFields(*ssl_info.cert.get(), &certificate_fields);
}
- manager_->Send(new PpapiMsg_PPBTCPSocket_SSLHandshakeACK(
- routing_id_,
- plugin_dispatcher_id_,
- socket_id_,
- succeeded,
- certificate_fields));
+ manager_->Send(
+ new PpapiMsg_PPBTCPSocket_SSLHandshakeACK(routing_id_,
+ plugin_dispatcher_id_,
+ socket_id_,
+ succeeded,
+ certificate_fields));
}
void PepperTCPSocket::SendSetOptionACK(int32_t result) {
@@ -414,12 +419,12 @@ void PepperTCPSocket::OnConnectCompleted(int net_result) {
net::IPEndPoint ip_end_point_local;
net::IPEndPoint ip_end_point_remote;
- pp_result = NetErrorToPepperError(
- socket_->GetLocalAddress(&ip_end_point_local));
+ pp_result =
+ NetErrorToPepperError(socket_->GetLocalAddress(&ip_end_point_local));
if (pp_result != PP_OK)
break;
- pp_result = NetErrorToPepperError(
- socket_->GetPeerAddress(&ip_end_point_remote));
+ pp_result =
+ NetErrorToPepperError(socket_->GetPeerAddress(&ip_end_point_remote));
if (pp_result != PP_OK)
break;
@@ -439,9 +444,12 @@ void PepperTCPSocket::OnConnectCompleted(int net_result) {
break;
}
- manager_->Send(new PpapiMsg_PPBTCPSocket_ConnectACK(
- routing_id_, plugin_dispatcher_id_, socket_id_, PP_OK,
- local_addr, remote_addr));
+ manager_->Send(new PpapiMsg_PPBTCPSocket_ConnectACK(routing_id_,
+ plugin_dispatcher_id_,
+ socket_id_,
+ PP_OK,
+ local_addr,
+ remote_addr));
connection_state_ = CONNECTED;
return;
} while (false);
@@ -463,7 +471,10 @@ void PepperTCPSocket::OnReadCompleted(int net_result) {
if (net_result > 0) {
manager_->Send(new PpapiMsg_PPBTCPSocket_ReadACK(
- routing_id_, plugin_dispatcher_id_, socket_id_, PP_OK,
+ routing_id_,
+ plugin_dispatcher_id_,
+ socket_id_,
+ PP_OK,
std::string(read_buffer_->data(), net_result)));
} else if (net_result == 0) {
end_of_file_reached_ = true;
@@ -490,9 +501,11 @@ void PepperTCPSocket::OnWriteCompleted(int net_result) {
}
if (net_result >= 0) {
- manager_->Send(new PpapiMsg_PPBTCPSocket_WriteACK(
- routing_id_, plugin_dispatcher_id_, socket_id_,
- write_buffer_->BytesConsumed()));
+ manager_->Send(
+ new PpapiMsg_PPBTCPSocket_WriteACK(routing_id_,
+ plugin_dispatcher_id_,
+ socket_id_,
+ write_buffer_->BytesConsumed()));
} else {
SendWriteACKError(NetErrorToPepperError(net_result));
}
@@ -506,9 +519,9 @@ bool PepperTCPSocket::IsConnected() const {
}
bool PepperTCPSocket::IsSsl() const {
- return connection_state_ == SSL_HANDSHAKE_IN_PROGRESS ||
- connection_state_ == SSL_CONNECTED ||
- connection_state_ == SSL_HANDSHAKE_FAILED;
+ return connection_state_ == SSL_HANDSHAKE_IN_PROGRESS ||
+ connection_state_ == SSL_CONNECTED ||
+ connection_state_ == SSL_HANDSHAKE_FAILED;
}
void PepperTCPSocket::DoWrite() {
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.cc b/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.cc
index f943f39eb9e..38248f42341 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.cc
+++ b/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.cc
@@ -56,7 +56,7 @@ PepperTCPSocketMessageFilter::PepperTCPSocketMessageFilter(
: version_(version),
external_plugin_(host->external_plugin()),
render_process_id_(0),
- render_view_id_(0),
+ render_frame_id_(0),
ppapi_host_(host->GetPpapiHost()),
factory_(factory),
instance_(instance),
@@ -69,9 +69,8 @@ PepperTCPSocketMessageFilter::PepperTCPSocketMessageFilter(
pending_accept_(false) {
DCHECK(host);
++g_num_instances;
- if (!host->GetRenderViewIDsForInstance(instance,
- &render_process_id_,
- &render_view_id_)) {
+ if (!host->GetRenderFrameIDsForInstance(
+ instance, &render_process_id_, &render_frame_id_)) {
NOTREACHED();
}
}
@@ -84,7 +83,7 @@ PepperTCPSocketMessageFilter::PepperTCPSocketMessageFilter(
: version_(version),
external_plugin_(host->external_plugin()),
render_process_id_(0),
- render_view_id_(0),
+ render_frame_id_(0),
ppapi_host_(host->GetPpapiHost()),
factory_(NULL),
instance_(instance),
@@ -99,9 +98,8 @@ PepperTCPSocketMessageFilter::PepperTCPSocketMessageFilter(
DCHECK_NE(version, ppapi::TCP_SOCKET_VERSION_1_0);
++g_num_instances;
- if (!host->GetRenderViewIDsForInstance(instance,
- &render_process_id_,
- &render_view_id_)) {
+ if (!host->GetRenderFrameIDsForInstance(
+ instance, &render_process_id_, &render_frame_id_)) {
NOTREACHED();
}
}
@@ -142,36 +140,33 @@ PepperTCPSocketMessageFilter::OverrideTaskRunnerForMessage(
int32_t PepperTCPSocketMessageFilter::OnResourceMessageReceived(
const IPC::Message& msg,
ppapi::host::HostMessageContext* context) {
- IPC_BEGIN_MESSAGE_MAP(PepperTCPSocketMessageFilter, msg)
- PPAPI_DISPATCH_HOST_RESOURCE_CALL(
- PpapiHostMsg_TCPSocket_Bind, OnMsgBind)
- PPAPI_DISPATCH_HOST_RESOURCE_CALL(
- PpapiHostMsg_TCPSocket_Connect, OnMsgConnect)
+ PPAPI_BEGIN_MESSAGE_MAP(PepperTCPSocketMessageFilter, msg)
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_TCPSocket_Bind, OnMsgBind)
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_TCPSocket_Connect,
+ OnMsgConnect)
PPAPI_DISPATCH_HOST_RESOURCE_CALL(
PpapiHostMsg_TCPSocket_ConnectWithNetAddress,
OnMsgConnectWithNetAddress)
- PPAPI_DISPATCH_HOST_RESOURCE_CALL(
- PpapiHostMsg_TCPSocket_SSLHandshake, OnMsgSSLHandshake)
- PPAPI_DISPATCH_HOST_RESOURCE_CALL(
- PpapiHostMsg_TCPSocket_Read, OnMsgRead)
- PPAPI_DISPATCH_HOST_RESOURCE_CALL(
- PpapiHostMsg_TCPSocket_Write, OnMsgWrite)
- PPAPI_DISPATCH_HOST_RESOURCE_CALL(
- PpapiHostMsg_TCPSocket_Listen, OnMsgListen)
- PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
- PpapiHostMsg_TCPSocket_Accept, OnMsgAccept)
- PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
- PpapiHostMsg_TCPSocket_Close, OnMsgClose)
- PPAPI_DISPATCH_HOST_RESOURCE_CALL(
- PpapiHostMsg_TCPSocket_SetOption, OnMsgSetOption)
- IPC_END_MESSAGE_MAP()
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_TCPSocket_SSLHandshake,
+ OnMsgSSLHandshake)
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_TCPSocket_Read, OnMsgRead)
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_TCPSocket_Write, OnMsgWrite)
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_TCPSocket_Listen,
+ OnMsgListen)
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_TCPSocket_Accept,
+ OnMsgAccept)
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_TCPSocket_Close,
+ OnMsgClose)
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_TCPSocket_SetOption,
+ OnMsgSetOption)
+ PPAPI_END_MESSAGE_MAP()
return PP_ERROR_FAILED;
}
int32_t PepperTCPSocketMessageFilter::OnMsgBind(
const ppapi::host::HostMessageContext* context,
const PP_NetAddress_Private& net_addr) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
// This is only supported by PPB_TCPSocket v1.1 or above.
if (version_ != ppapi::TCP_SOCKET_VERSION_1_1_OR_ABOVE) {
@@ -179,18 +174,22 @@ int32_t PepperTCPSocketMessageFilter::OnMsgBind(
return PP_ERROR_NOACCESS;
}
- if (!pepper_socket_utils::CanUseSocketAPIs(
- external_plugin_, false /* private_api */, NULL, render_process_id_,
- render_view_id_)) {
+ if (!pepper_socket_utils::CanUseSocketAPIs(external_plugin_,
+ false /* private_api */,
+ NULL,
+ render_process_id_,
+ render_frame_id_)) {
return PP_ERROR_NOACCESS;
}
bind_input_addr_ = net_addr;
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::Bind(&PepperTCPSocketMessageFilter::DoBind, this,
- context->MakeReplyMessageContext(), net_addr));
+ BrowserThread::PostTask(BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&PepperTCPSocketMessageFilter::DoBind,
+ this,
+ context->MakeReplyMessageContext(),
+ net_addr));
return PP_OK_COMPLETIONPENDING;
}
@@ -198,7 +197,7 @@ int32_t PepperTCPSocketMessageFilter::OnMsgConnect(
const ppapi::host::HostMessageContext* context,
const std::string& host,
uint16_t port) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
// This is only supported by PPB_TCPSocket_Private.
if (!IsPrivateAPI()) {
@@ -206,12 +205,13 @@ int32_t PepperTCPSocketMessageFilter::OnMsgConnect(
return PP_ERROR_NOACCESS;
}
- SocketPermissionRequest request(SocketPermissionRequest::TCP_CONNECT,
- host,
- port);
- if (!pepper_socket_utils::CanUseSocketAPIs(
- external_plugin_, true /* private_api */, &request,
- render_process_id_, render_view_id_)) {
+ SocketPermissionRequest request(
+ SocketPermissionRequest::TCP_CONNECT, host, port);
+ if (!pepper_socket_utils::CanUseSocketAPIs(external_plugin_,
+ true /* private_api */,
+ &request,
+ render_process_id_,
+ render_frame_id_)) {
return PP_ERROR_NOACCESS;
}
@@ -223,32 +223,40 @@ int32_t PepperTCPSocketMessageFilter::OnMsgConnect(
if (!browser_context || !browser_context->GetResourceContext())
return PP_ERROR_FAILED;
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::Bind(&PepperTCPSocketMessageFilter::DoConnect, this,
- context->MakeReplyMessageContext(),
- host, port, browser_context->GetResourceContext()));
+ BrowserThread::PostTask(BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&PepperTCPSocketMessageFilter::DoConnect,
+ this,
+ context->MakeReplyMessageContext(),
+ host,
+ port,
+ browser_context->GetResourceContext()));
return PP_OK_COMPLETIONPENDING;
}
int32_t PepperTCPSocketMessageFilter::OnMsgConnectWithNetAddress(
const ppapi::host::HostMessageContext* context,
const PP_NetAddress_Private& net_addr) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
content::SocketPermissionRequest request =
pepper_socket_utils::CreateSocketPermissionRequest(
content::SocketPermissionRequest::TCP_CONNECT, net_addr);
- if (!pepper_socket_utils::CanUseSocketAPIs(external_plugin_, IsPrivateAPI(),
- &request, render_process_id_,
- render_view_id_)) {
+ if (!pepper_socket_utils::CanUseSocketAPIs(external_plugin_,
+ IsPrivateAPI(),
+ &request,
+ render_process_id_,
+ render_frame_id_)) {
return PP_ERROR_NOACCESS;
}
BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::Bind(&PepperTCPSocketMessageFilter::DoConnectWithNetAddress, this,
- context->MakeReplyMessageContext(), net_addr));
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&PepperTCPSocketMessageFilter::DoConnectWithNetAddress,
+ this,
+ context->MakeReplyMessageContext(),
+ net_addr));
return PP_OK_COMPLETIONPENDING;
}
@@ -258,7 +266,7 @@ int32_t PepperTCPSocketMessageFilter::OnMsgSSLHandshake(
uint16_t server_port,
const std::vector<std::vector<char> >& trusted_certs,
const std::vector<std::vector<char> >& untrusted_certs) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
// Allow to do SSL handshake only if currently the socket has been connected
// and there isn't pending read or write.
@@ -282,9 +290,11 @@ int32_t PepperTCPSocketMessageFilter::OnMsgSSLHandshake(
ssl_context.cert_verifier = ssl_context_helper_->GetCertVerifier();
ssl_context.transport_security_state =
ssl_context_helper_->GetTransportSecurityState();
- ssl_socket_ = factory->CreateSSLClientSocket(
- handle.Pass(), host_port_pair, ssl_context_helper_->ssl_config(),
- ssl_context);
+ ssl_socket_ =
+ factory->CreateSSLClientSocket(handle.Pass(),
+ host_port_pair,
+ ssl_context_helper_->ssl_config(),
+ ssl_context);
if (!ssl_socket_) {
LOG(WARNING) << "Failed to create an SSL client socket.";
state_.CompletePendingTransition(false);
@@ -297,7 +307,8 @@ int32_t PepperTCPSocketMessageFilter::OnMsgSSLHandshake(
context->MakeReplyMessageContext());
int net_result = ssl_socket_->Connect(
base::Bind(&PepperTCPSocketMessageFilter::OnSSLHandshakeCompleted,
- base::Unretained(this), reply_context));
+ base::Unretained(this),
+ reply_context));
if (net_result != net::ERR_IO_PENDING)
OnSSLHandshakeCompleted(reply_context, net_result);
return PP_OK_COMPLETIONPENDING;
@@ -306,7 +317,7 @@ int32_t PepperTCPSocketMessageFilter::OnMsgSSLHandshake(
int32_t PepperTCPSocketMessageFilter::OnMsgRead(
const ppapi::host::HostMessageContext* context,
int32_t bytes_to_read) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!state_.IsConnected() || end_of_file_reached_)
return PP_ERROR_FAILED;
if (read_buffer_.get())
@@ -323,18 +334,20 @@ int32_t PepperTCPSocketMessageFilter::OnMsgRead(
int net_result = net::ERR_FAILED;
if (socket_) {
DCHECK_EQ(state_.state(), TCPSocketState::CONNECTED);
- net_result = socket_->Read(
- read_buffer_.get(),
- bytes_to_read,
- base::Bind(&PepperTCPSocketMessageFilter::OnReadCompleted,
- base::Unretained(this), reply_context));
+ net_result =
+ socket_->Read(read_buffer_.get(),
+ bytes_to_read,
+ base::Bind(&PepperTCPSocketMessageFilter::OnReadCompleted,
+ base::Unretained(this),
+ reply_context));
} else if (ssl_socket_) {
DCHECK_EQ(state_.state(), TCPSocketState::SSL_CONNECTED);
net_result = ssl_socket_->Read(
read_buffer_.get(),
bytes_to_read,
base::Bind(&PepperTCPSocketMessageFilter::OnReadCompleted,
- base::Unretained(this), reply_context));
+ base::Unretained(this),
+ reply_context));
}
if (net_result != net::ERR_IO_PENDING)
OnReadCompleted(reply_context, net_result);
@@ -344,7 +357,7 @@ int32_t PepperTCPSocketMessageFilter::OnMsgRead(
int32_t PepperTCPSocketMessageFilter::OnMsgWrite(
const ppapi::host::HostMessageContext* context,
const std::string& data) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!state_.IsConnected())
return PP_ERROR_FAILED;
@@ -368,7 +381,7 @@ int32_t PepperTCPSocketMessageFilter::OnMsgWrite(
int32_t PepperTCPSocketMessageFilter::OnMsgListen(
const ppapi::host::HostMessageContext* context,
int32_t backlog) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
// This is only supported by PPB_TCPSocket v1.1 or above.
if (version_ != ppapi::TCP_SOCKET_VERSION_1_1_OR_ABOVE) {
@@ -379,22 +392,26 @@ int32_t PepperTCPSocketMessageFilter::OnMsgListen(
content::SocketPermissionRequest request =
pepper_socket_utils::CreateSocketPermissionRequest(
content::SocketPermissionRequest::TCP_LISTEN, bind_input_addr_);
- if (!pepper_socket_utils::CanUseSocketAPIs(
- external_plugin_, false /* private_api */, &request,
- render_process_id_, render_view_id_)) {
+ if (!pepper_socket_utils::CanUseSocketAPIs(external_plugin_,
+ false /* private_api */,
+ &request,
+ render_process_id_,
+ render_frame_id_)) {
return PP_ERROR_NOACCESS;
}
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::Bind(&PepperTCPSocketMessageFilter::DoListen, this,
- context->MakeReplyMessageContext(), backlog));
+ BrowserThread::PostTask(BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&PepperTCPSocketMessageFilter::DoListen,
+ this,
+ context->MakeReplyMessageContext(),
+ backlog));
return PP_OK_COMPLETIONPENDING;
}
int32_t PepperTCPSocketMessageFilter::OnMsgAccept(
const ppapi::host::HostMessageContext* context) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (pending_accept_)
return PP_ERROR_INPROGRESS;
@@ -408,7 +425,8 @@ int32_t PepperTCPSocketMessageFilter::OnMsgAccept(
&accepted_socket_,
&accepted_address_,
base::Bind(&PepperTCPSocketMessageFilter::OnAcceptCompleted,
- base::Unretained(this), reply_context));
+ base::Unretained(this),
+ reply_context));
if (net_result != net::ERR_IO_PENDING)
OnAcceptCompleted(reply_context, net_result);
return PP_OK_COMPLETIONPENDING;
@@ -416,7 +434,7 @@ int32_t PepperTCPSocketMessageFilter::OnMsgAccept(
int32_t PepperTCPSocketMessageFilter::OnMsgClose(
const ppapi::host::HostMessageContext* context) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (state_.state() == TCPSocketState::CLOSED)
return PP_OK;
@@ -434,7 +452,7 @@ int32_t PepperTCPSocketMessageFilter::OnMsgSetOption(
const ppapi::host::HostMessageContext* context,
PP_TCPSocket_Option name,
const ppapi::SocketOptionData& value) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
switch (name) {
case PP_TCPSOCKET_OPTION_NO_DELAY: {
@@ -455,17 +473,18 @@ int32_t PepperTCPSocketMessageFilter::OnMsgSetOption(
if (!value.GetInt32(&integer_value) || integer_value <= 0)
return PP_ERROR_BADARGUMENT;
- bool result = false;
+ int net_result = net::ERR_UNEXPECTED;
if (name == PP_TCPSOCKET_OPTION_SEND_BUFFER_SIZE) {
if (integer_value > TCPSocketResourceBase::kMaxSendBufferSize)
return PP_ERROR_BADARGUMENT;
- result = socket_->SetSendBufferSize(integer_value);
+ net_result = socket_->SetSendBufferSize(integer_value);
} else {
if (integer_value > TCPSocketResourceBase::kMaxReceiveBufferSize)
return PP_ERROR_BADARGUMENT;
- result = socket_->SetReceiveBufferSize(integer_value);
+ net_result = socket_->SetReceiveBufferSize(integer_value);
}
- return result ? PP_OK : PP_ERROR_FAILED;
+ // TODO(wtc): Add error mapping code.
+ return (net_result == net::OK) ? PP_OK : PP_ERROR_FAILED;
}
default: {
NOTREACHED();
@@ -477,7 +496,7 @@ int32_t PepperTCPSocketMessageFilter::OnMsgSetOption(
void PepperTCPSocketMessageFilter::DoBind(
const ppapi::host::ReplyMessageContext& context,
const PP_NetAddress_Private& net_addr) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (state_.IsPending(TCPSocketState::BIND)) {
SendBindError(context, PP_ERROR_INPROGRESS);
@@ -492,8 +511,8 @@ void PepperTCPSocketMessageFilter::DoBind(
do {
net::IPAddressNumber address;
int port;
- if (!NetAddressPrivateImpl::NetAddressToIPEndPoint(net_addr, &address,
- &port)) {
+ if (!NetAddressPrivateImpl::NetAddressToIPEndPoint(
+ net_addr, &address, &port)) {
pp_result = PP_ERROR_ADDRESS_INVALID;
break;
}
@@ -513,8 +532,8 @@ void PepperTCPSocketMessageFilter::DoBind(
break;
net::IPEndPoint ip_end_point_local;
- pp_result = NetErrorToPepperError(
- socket_->GetLocalAddress(&ip_end_point_local));
+ pp_result =
+ NetErrorToPepperError(socket_->GetLocalAddress(&ip_end_point_local));
if (pp_result != PP_OK)
break;
@@ -543,7 +562,7 @@ void PepperTCPSocketMessageFilter::DoConnect(
const std::string& host,
uint16_t port,
ResourceContext* resource_context) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!state_.IsValidTransition(TCPSocketState::CONNECT)) {
SendConnectError(context, PP_ERROR_FAILED);
@@ -554,14 +573,15 @@ void PepperTCPSocketMessageFilter::DoConnect(
address_index_ = 0;
address_list_.clear();
net::HostResolver::RequestInfo request_info(net::HostPortPair(host, port));
- resolver_.reset(new net::SingleRequestHostResolver(
- resource_context->GetHostResolver()));
+ resolver_.reset(
+ new net::SingleRequestHostResolver(resource_context->GetHostResolver()));
int net_result = resolver_->Resolve(
request_info,
net::DEFAULT_PRIORITY,
&address_list_,
base::Bind(&PepperTCPSocketMessageFilter::OnResolveCompleted,
- base::Unretained(this), context),
+ base::Unretained(this),
+ context),
net::BoundNetLog());
if (net_result != net::ERR_IO_PENDING)
OnResolveCompleted(context, net_result);
@@ -570,7 +590,7 @@ void PepperTCPSocketMessageFilter::DoConnect(
void PepperTCPSocketMessageFilter::DoConnectWithNetAddress(
const ppapi::host::ReplyMessageContext& context,
const PP_NetAddress_Private& net_addr) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!state_.IsValidTransition(TCPSocketState::CONNECT)) {
SendConnectError(context, PP_ERROR_FAILED);
@@ -581,8 +601,8 @@ void PepperTCPSocketMessageFilter::DoConnectWithNetAddress(
net::IPAddressNumber address;
int port;
- if (!NetAddressPrivateImpl::NetAddressToIPEndPoint(net_addr, &address,
- &port)) {
+ if (!NetAddressPrivateImpl::NetAddressToIPEndPoint(
+ net_addr, &address, &port)) {
state_.CompletePendingTransition(false);
SendConnectError(context, PP_ERROR_ADDRESS_INVALID);
return;
@@ -597,7 +617,7 @@ void PepperTCPSocketMessageFilter::DoConnectWithNetAddress(
void PepperTCPSocketMessageFilter::DoWrite(
const ppapi::host::ReplyMessageContext& context) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(write_buffer_base_.get());
DCHECK(write_buffer_.get());
DCHECK_GT(write_buffer_->BytesRemaining(), 0);
@@ -610,14 +630,16 @@ void PepperTCPSocketMessageFilter::DoWrite(
write_buffer_.get(),
write_buffer_->BytesRemaining(),
base::Bind(&PepperTCPSocketMessageFilter::OnWriteCompleted,
- base::Unretained(this), context));
+ base::Unretained(this),
+ context));
} else if (ssl_socket_) {
DCHECK_EQ(state_.state(), TCPSocketState::SSL_CONNECTED);
net_result = ssl_socket_->Write(
write_buffer_.get(),
write_buffer_->BytesRemaining(),
base::Bind(&PepperTCPSocketMessageFilter::OnWriteCompleted,
- base::Unretained(this), context));
+ base::Unretained(this),
+ context));
}
if (net_result != net::ERR_IO_PENDING)
OnWriteCompleted(context, net_result);
@@ -626,7 +648,7 @@ void PepperTCPSocketMessageFilter::DoWrite(
void PepperTCPSocketMessageFilter::DoListen(
const ppapi::host::ReplyMessageContext& context,
int32_t backlog) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (state_.IsPending(TCPSocketState::LISTEN)) {
SendListenReply(context, PP_ERROR_INPROGRESS);
@@ -645,7 +667,7 @@ void PepperTCPSocketMessageFilter::DoListen(
void PepperTCPSocketMessageFilter::OnResolveCompleted(
const ppapi::host::ReplyMessageContext& context,
int net_result) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!state_.IsPending(TCPSocketState::CONNECT)) {
DCHECK(state_.state() == TCPSocketState::CLOSED);
@@ -664,7 +686,7 @@ void PepperTCPSocketMessageFilter::OnResolveCompleted(
void PepperTCPSocketMessageFilter::StartConnect(
const ppapi::host::ReplyMessageContext& context) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(state_.IsPending(TCPSocketState::CONNECT));
DCHECK_LT(address_index_, address_list_.size());
@@ -676,7 +698,8 @@ void PepperTCPSocketMessageFilter::StartConnect(
net_result = socket_->Connect(
address_list_[address_index_],
base::Bind(&PepperTCPSocketMessageFilter::OnConnectCompleted,
- base::Unretained(this), context));
+ base::Unretained(this),
+ context));
}
if (net_result != net::ERR_IO_PENDING)
OnConnectCompleted(context, net_result);
@@ -685,7 +708,7 @@ void PepperTCPSocketMessageFilter::StartConnect(
void PepperTCPSocketMessageFilter::OnConnectCompleted(
const ppapi::host::ReplyMessageContext& context,
int net_result) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!state_.IsPending(TCPSocketState::CONNECT)) {
DCHECK(state_.state() == TCPSocketState::CLOSED);
@@ -700,12 +723,12 @@ void PepperTCPSocketMessageFilter::OnConnectCompleted(
net::IPEndPoint ip_end_point_local;
net::IPEndPoint ip_end_point_remote;
- pp_result = NetErrorToPepperError(
- socket_->GetLocalAddress(&ip_end_point_local));
+ pp_result =
+ NetErrorToPepperError(socket_->GetLocalAddress(&ip_end_point_local));
if (pp_result != PP_OK)
break;
- pp_result = NetErrorToPepperError(
- socket_->GetPeerAddress(&ip_end_point_remote));
+ pp_result =
+ NetErrorToPepperError(socket_->GetPeerAddress(&ip_end_point_remote));
if (pp_result != PP_OK)
break;
@@ -758,7 +781,7 @@ void PepperTCPSocketMessageFilter::OnConnectCompleted(
void PepperTCPSocketMessageFilter::OnSSLHandshakeCompleted(
const ppapi::host::ReplyMessageContext& context,
int net_result) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!state_.IsPending(TCPSocketState::SSL_CONNECT)) {
DCHECK(state_.state() == TCPSocketState::CLOSED);
@@ -773,13 +796,12 @@ void PepperTCPSocketMessageFilter::OnSSLHandshakeCompleted(
void PepperTCPSocketMessageFilter::OnReadCompleted(
const ppapi::host::ReplyMessageContext& context,
int net_result) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(read_buffer_.get());
if (net_result > 0) {
- SendReadReply(context,
- PP_OK,
- std::string(read_buffer_->data(), net_result));
+ SendReadReply(
+ context, PP_OK, std::string(read_buffer_->data(), net_result));
} else if (net_result == 0) {
end_of_file_reached_ = true;
SendReadReply(context, PP_OK, std::string());
@@ -792,7 +814,7 @@ void PepperTCPSocketMessageFilter::OnReadCompleted(
void PepperTCPSocketMessageFilter::OnWriteCompleted(
const ppapi::host::ReplyMessageContext& context,
int net_result) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(write_buffer_base_.get());
DCHECK(write_buffer_.get());
@@ -818,7 +840,7 @@ void PepperTCPSocketMessageFilter::OnWriteCompleted(
void PepperTCPSocketMessageFilter::OnAcceptCompleted(
const ppapi::host::ReplyMessageContext& context,
int net_result) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(pending_accept_);
pending_accept_ = false;
@@ -834,9 +856,8 @@ void PepperTCPSocketMessageFilter::OnAcceptCompleted(
PP_NetAddress_Private local_addr = NetAddressPrivateImpl::kInvalidNetAddress;
PP_NetAddress_Private remote_addr = NetAddressPrivateImpl::kInvalidNetAddress;
- int32_t pp_result =
- NetErrorToPepperError(accepted_socket_->GetLocalAddress(
- &ip_end_point_local));
+ int32_t pp_result = NetErrorToPepperError(
+ accepted_socket_->GetLocalAddress(&ip_end_point_local));
if (pp_result != PP_OK) {
SendAcceptError(context, pp_result);
return;
@@ -971,7 +992,9 @@ void PepperTCPSocketMessageFilter::SendAcceptReply(
void PepperTCPSocketMessageFilter::SendAcceptError(
const ppapi::host::ReplyMessageContext& context,
int32_t pp_error) {
- SendAcceptReply(context, pp_error, 0,
+ SendAcceptReply(context,
+ pp_error,
+ 0,
NetAddressPrivateImpl::kInvalidNetAddress,
NetAddressPrivateImpl::kInvalidNetAddress);
}
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h b/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h
index fb34b988d3c..5c4e066b360 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h
+++ b/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h
@@ -49,18 +49,16 @@ class ResourceContext;
class CONTENT_EXPORT PepperTCPSocketMessageFilter
: public ppapi::host::ResourceMessageFilter {
public:
- PepperTCPSocketMessageFilter(
- ContentBrowserPepperHostFactory* factory,
- BrowserPpapiHostImpl* host,
- PP_Instance instance,
- ppapi::TCPSocketVersion version);
+ PepperTCPSocketMessageFilter(ContentBrowserPepperHostFactory* factory,
+ BrowserPpapiHostImpl* host,
+ PP_Instance instance,
+ ppapi::TCPSocketVersion version);
// Used for creating already connected sockets.
- PepperTCPSocketMessageFilter(
- BrowserPpapiHostImpl* host,
- PP_Instance instance,
- ppapi::TCPSocketVersion version,
- scoped_ptr<net::TCPSocket> socket);
+ PepperTCPSocketMessageFilter(BrowserPpapiHostImpl* host,
+ PP_Instance instance,
+ ppapi::TCPSocketVersion version,
+ scoped_ptr<net::TCPSocket> socket);
static size_t GetNumInstances();
@@ -106,9 +104,8 @@ class CONTENT_EXPORT PepperTCPSocketMessageFilter
const std::string& host,
uint16_t port,
ResourceContext* resource_context);
- void DoConnectWithNetAddress(
- const ppapi::host::ReplyMessageContext& context,
- const PP_NetAddress_Private& net_addr);
+ void DoConnectWithNetAddress(const ppapi::host::ReplyMessageContext& context,
+ const PP_NetAddress_Private& net_addr);
void DoWrite(const ppapi::host::ReplyMessageContext& context);
void DoListen(const ppapi::host::ReplyMessageContext& context,
int32_t backlog);
@@ -169,7 +166,7 @@ class CONTENT_EXPORT PepperTCPSocketMessageFilter
const bool external_plugin_;
int render_process_id_;
- int render_view_id_;
+ int render_frame_id_;
// The following fields are used only on the IO thread.
// Non-owning ptr.
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_truetype_font_list_host.cc b/chromium/content/browser/renderer_host/pepper/pepper_truetype_font_list_host.cc
index ceeb73e3df1..7479046cfe7 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_truetype_font_list_host.cc
+++ b/chromium/content/browser/renderer_host/pepper/pepper_truetype_font_list_host.cc
@@ -6,7 +6,7 @@
#include <algorithm>
-#include "base/safe_numerics.h"
+#include "base/numerics/safe_conversions.h"
#include "base/threading/sequenced_worker_pool.h"
#include "content/browser/renderer_host/pepper/pepper_truetype_font_list.h"
#include "content/common/font_list.h"
@@ -44,11 +44,9 @@ class FontMessageFilter : public ppapi::host::ResourceMessageFilter {
DISALLOW_COPY_AND_ASSIGN(FontMessageFilter);
};
-FontMessageFilter::FontMessageFilter() {
-}
+FontMessageFilter::FontMessageFilter() {}
-FontMessageFilter::~FontMessageFilter() {
-}
+FontMessageFilter::~FontMessageFilter() {}
scoped_refptr<base::TaskRunner> FontMessageFilter::OverrideTaskRunnerForMessage(
const IPC::Message& msg) {
@@ -63,14 +61,14 @@ scoped_refptr<base::TaskRunner> FontMessageFilter::OverrideTaskRunnerForMessage(
int32_t FontMessageFilter::OnResourceMessageReceived(
const IPC::Message& msg,
ppapi::host::HostMessageContext* context) {
- IPC_BEGIN_MESSAGE_MAP(FontMessageFilter, msg)
+ PPAPI_BEGIN_MESSAGE_MAP(FontMessageFilter, msg)
PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
PpapiHostMsg_TrueTypeFontSingleton_GetFontFamilies,
OnHostMsgGetFontFamilies)
PPAPI_DISPATCH_HOST_RESOURCE_CALL(
PpapiHostMsg_TrueTypeFontSingleton_GetFontsInFamily,
OnHostMsgGetFontsInFamily)
- IPC_END_MESSAGE_MAP()
+ PPAPI_END_MESSAGE_MAP()
return PP_ERROR_FAILED;
}
@@ -84,7 +82,7 @@ int32_t FontMessageFilter::OnHostMsgGetFontFamilies(
context->reply_msg =
PpapiPluginMsg_TrueTypeFontSingleton_GetFontFamiliesReply(font_families);
- return base::checked_numeric_cast<int32_t>(font_families.size());
+ return base::checked_cast<int32_t>(font_families.size());
}
int32_t FontMessageFilter::OnHostMsgGetFontsInFamily(
@@ -97,21 +95,19 @@ int32_t FontMessageFilter::OnHostMsgGetFontsInFamily(
context->reply_msg =
PpapiPluginMsg_TrueTypeFontSingleton_GetFontsInFamilyReply(
fonts_in_family);
- return base::checked_numeric_cast<int32_t>(fonts_in_family.size());
+ return base::checked_cast<int32_t>(fonts_in_family.size());
}
} // namespace
-PepperTrueTypeFontListHost::PepperTrueTypeFontListHost(
- BrowserPpapiHost* host,
- PP_Instance instance,
- PP_Resource resource)
+PepperTrueTypeFontListHost::PepperTrueTypeFontListHost(BrowserPpapiHost* host,
+ PP_Instance instance,
+ PP_Resource resource)
: ResourceHost(host->GetPpapiHost(), instance, resource) {
AddFilter(scoped_refptr<ppapi::host::ResourceMessageFilter>(
new FontMessageFilter()));
}
-PepperTrueTypeFontListHost::~PepperTrueTypeFontListHost() {
-}
+PepperTrueTypeFontListHost::~PepperTrueTypeFontListHost() {}
} // namespace content
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_truetype_font_list_win.cc b/chromium/content/browser/renderer_host/pepper/pepper_truetype_font_list_win.cc
index 3530e1e1b5e..8e275904081 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_truetype_font_list_win.cc
+++ b/chromium/content/browser/renderer_host/pepper/pepper_truetype_font_list_win.cc
@@ -27,7 +27,7 @@ static int CALLBACK EnumFontFamiliesProc(ENUMLOGFONTEXW* logical_font,
const LOGFONTW& lf = logical_font->elfLogFont;
if (lf.lfFaceName[0] && lf.lfFaceName[0] != '@' &&
lf.lfOutPrecision == OUT_STROKE_PRECIS) { // Outline fonts only.
- std::string face_name(UTF16ToUTF8(lf.lfFaceName));
+ std::string face_name(base::UTF16ToUTF8(lf.lfFaceName));
font_families->push_back(face_name);
}
}
@@ -44,13 +44,12 @@ static int CALLBACK EnumFontsInFamilyProc(ENUMLOGFONTEXW* logical_font,
if (lf.lfFaceName[0] && lf.lfFaceName[0] != '@' &&
lf.lfOutPrecision == OUT_STROKE_PRECIS) { // Outline fonts only.
ppapi::proxy::SerializedTrueTypeFontDesc desc;
- desc.family = UTF16ToUTF8(lf.lfFaceName);
+ desc.family = base::UTF16ToUTF8(lf.lfFaceName);
if (lf.lfItalic)
desc.style = PP_TRUETYPEFONTSTYLE_ITALIC;
desc.weight = static_cast<PP_TrueTypeFontWeight_Dev>(lf.lfWeight);
desc.width = PP_TRUETYPEFONTWIDTH_NORMAL; // TODO(bbudge) support widths.
- desc.charset =
- static_cast<PP_TrueTypeFontCharset_Dev>(lf.lfCharSet);
+ desc.charset = static_cast<PP_TrueTypeFontCharset_Dev>(lf.lfCharSet);
fonts_in_family->push_back(desc);
}
}
@@ -64,8 +63,11 @@ void GetFontFamilies_SlowBlocking(FontFamilyList* font_families) {
memset(&logfont, 0, sizeof(logfont));
logfont.lfCharSet = DEFAULT_CHARSET;
base::win::ScopedCreateDC hdc(::CreateCompatibleDC(NULL));
- ::EnumFontFamiliesExW(hdc, &logfont, (FONTENUMPROCW)&EnumFontFamiliesProc,
- (LPARAM)font_families, 0);
+ ::EnumFontFamiliesExW(hdc,
+ &logfont,
+ (FONTENUMPROCW) & EnumFontFamiliesProc,
+ (LPARAM)font_families,
+ 0);
}
void GetFontsInFamily_SlowBlocking(const std::string& family,
@@ -73,11 +75,14 @@ void GetFontsInFamily_SlowBlocking(const std::string& family,
LOGFONTW logfont;
memset(&logfont, 0, sizeof(logfont));
logfont.lfCharSet = DEFAULT_CHARSET;
- base::string16 family16 = UTF8ToUTF16(family);
+ base::string16 family16 = base::UTF8ToUTF16(family);
memcpy(&logfont.lfFaceName, &family16[0], sizeof(logfont.lfFaceName));
base::win::ScopedCreateDC hdc(::CreateCompatibleDC(NULL));
- ::EnumFontFamiliesExW(hdc, &logfont, (FONTENUMPROCW)&EnumFontsInFamilyProc,
- (LPARAM)fonts_in_family, 0);
+ ::EnumFontFamiliesExW(hdc,
+ &logfont,
+ (FONTENUMPROCW) & EnumFontsInFamilyProc,
+ (LPARAM)fonts_in_family,
+ 0);
}
} // namespace content
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.cc b/chromium/content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.cc
index 3127ed278fd..a4ed3dca46e 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.cc
+++ b/chromium/content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.cc
@@ -49,13 +49,12 @@ PepperUDPSocketMessageFilter::PepperUDPSocketMessageFilter(
external_plugin_(host->external_plugin()),
private_api_(private_api),
render_process_id_(0),
- render_view_id_(0) {
+ render_frame_id_(0) {
++g_num_instances;
DCHECK(host);
- if (!host->GetRenderViewIDsForInstance(instance,
- &render_process_id_,
- &render_view_id_)) {
+ if (!host->GetRenderFrameIDsForInstance(
+ instance, &render_process_id_, &render_frame_id_)) {
NOTREACHED();
}
}
@@ -88,18 +87,17 @@ PepperUDPSocketMessageFilter::OverrideTaskRunnerForMessage(
int32_t PepperUDPSocketMessageFilter::OnResourceMessageReceived(
const IPC::Message& msg,
ppapi::host::HostMessageContext* context) {
- IPC_BEGIN_MESSAGE_MAP(PepperUDPSocketMessageFilter, msg)
- PPAPI_DISPATCH_HOST_RESOURCE_CALL(
- PpapiHostMsg_UDPSocket_SetOption, OnMsgSetOption)
- PPAPI_DISPATCH_HOST_RESOURCE_CALL(
- PpapiHostMsg_UDPSocket_Bind, OnMsgBind)
- PPAPI_DISPATCH_HOST_RESOURCE_CALL(
- PpapiHostMsg_UDPSocket_RecvFrom, OnMsgRecvFrom)
- PPAPI_DISPATCH_HOST_RESOURCE_CALL(
- PpapiHostMsg_UDPSocket_SendTo, OnMsgSendTo)
- PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
- PpapiHostMsg_UDPSocket_Close, OnMsgClose)
- IPC_END_MESSAGE_MAP()
+ PPAPI_BEGIN_MESSAGE_MAP(PepperUDPSocketMessageFilter, msg)
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_UDPSocket_SetOption,
+ OnMsgSetOption)
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_UDPSocket_Bind, OnMsgBind)
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_UDPSocket_RecvFrom,
+ OnMsgRecvFrom)
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_UDPSocket_SendTo,
+ OnMsgSendTo)
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_UDPSocket_Close,
+ OnMsgClose)
+ PPAPI_END_MESSAGE_MAP()
return PP_ERROR_FAILED;
}
@@ -107,7 +105,7 @@ int32_t PepperUDPSocketMessageFilter::OnMsgSetOption(
const ppapi::host::HostMessageContext* context,
PP_UDPSocket_Option name,
const ppapi::SocketOptionData& value) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (closed_)
return PP_ERROR_FAILED;
@@ -140,21 +138,22 @@ int32_t PepperUDPSocketMessageFilter::OnMsgSetOption(
if (!value.GetInt32(&integer_value) || integer_value <= 0)
return PP_ERROR_BADARGUMENT;
- bool result = false;
+ int net_result = net::ERR_UNEXPECTED;
if (name == PP_UDPSOCKET_OPTION_SEND_BUFFER_SIZE) {
if (integer_value >
- ppapi::proxy::UDPSocketResourceBase::kMaxSendBufferSize) {
+ ppapi::proxy::UDPSocketResourceBase::kMaxSendBufferSize) {
return PP_ERROR_BADARGUMENT;
}
- result = socket_->SetSendBufferSize(integer_value);
+ net_result = socket_->SetSendBufferSize(integer_value);
} else {
if (integer_value >
- ppapi::proxy::UDPSocketResourceBase::kMaxReceiveBufferSize) {
+ ppapi::proxy::UDPSocketResourceBase::kMaxReceiveBufferSize) {
return PP_ERROR_BADARGUMENT;
}
- result = socket_->SetReceiveBufferSize(integer_value);
+ net_result = socket_->SetReceiveBufferSize(integer_value);
}
- return result ? PP_OK : PP_ERROR_FAILED;
+ // TODO(wtc): Add error mapping code.
+ return (net_result == net::OK) ? PP_OK : PP_ERROR_FAILED;
}
default: {
NOTREACHED();
@@ -166,30 +165,33 @@ int32_t PepperUDPSocketMessageFilter::OnMsgSetOption(
int32_t PepperUDPSocketMessageFilter::OnMsgBind(
const ppapi::host::HostMessageContext* context,
const PP_NetAddress_Private& addr) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(context);
SocketPermissionRequest request =
pepper_socket_utils::CreateSocketPermissionRequest(
SocketPermissionRequest::UDP_BIND, addr);
- if (!pepper_socket_utils::CanUseSocketAPIs(external_plugin_, private_api_,
- &request, render_process_id_,
- render_view_id_)) {
+ if (!pepper_socket_utils::CanUseSocketAPIs(external_plugin_,
+ private_api_,
+ &request,
+ render_process_id_,
+ render_frame_id_)) {
return PP_ERROR_NOACCESS;
}
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::Bind(&PepperUDPSocketMessageFilter::DoBind, this,
- context->MakeReplyMessageContext(),
- addr));
+ BrowserThread::PostTask(BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&PepperUDPSocketMessageFilter::DoBind,
+ this,
+ context->MakeReplyMessageContext(),
+ addr));
return PP_OK_COMPLETIONPENDING;
}
int32_t PepperUDPSocketMessageFilter::OnMsgRecvFrom(
const ppapi::host::HostMessageContext* context,
int32_t num_bytes) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(context);
DCHECK(socket_.get());
@@ -229,28 +231,33 @@ int32_t PepperUDPSocketMessageFilter::OnMsgSendTo(
const ppapi::host::HostMessageContext* context,
const std::string& data,
const PP_NetAddress_Private& addr) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(context);
SocketPermissionRequest request =
pepper_socket_utils::CreateSocketPermissionRequest(
SocketPermissionRequest::UDP_SEND_TO, addr);
- if (!pepper_socket_utils::CanUseSocketAPIs(external_plugin_, private_api_,
- &request, render_process_id_,
- render_view_id_)) {
+ if (!pepper_socket_utils::CanUseSocketAPIs(external_plugin_,
+ private_api_,
+ &request,
+ render_process_id_,
+ render_frame_id_)) {
return PP_ERROR_NOACCESS;
}
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::Bind(&PepperUDPSocketMessageFilter::DoSendTo, this,
- context->MakeReplyMessageContext(), data, addr));
+ BrowserThread::PostTask(BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&PepperUDPSocketMessageFilter::DoSendTo,
+ this,
+ context->MakeReplyMessageContext(),
+ data,
+ addr));
return PP_OK_COMPLETIONPENDING;
}
int32_t PepperUDPSocketMessageFilter::OnMsgClose(
const ppapi::host::HostMessageContext* context) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
Close();
return PP_OK;
}
@@ -258,15 +265,15 @@ int32_t PepperUDPSocketMessageFilter::OnMsgClose(
void PepperUDPSocketMessageFilter::DoBind(
const ppapi::host::ReplyMessageContext& context,
const PP_NetAddress_Private& addr) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (closed_ || socket_.get()) {
SendBindError(context, PP_ERROR_FAILED);
return;
}
- scoped_ptr<net::UDPServerSocket> socket(new net::UDPServerSocket(
- NULL, net::NetLog::Source()));
+ scoped_ptr<net::UDPServerSocket> socket(
+ new net::UDPServerSocket(NULL, net::NetLog::Source()));
net::IPAddressNumber address;
int port;
@@ -280,8 +287,8 @@ void PepperUDPSocketMessageFilter::DoBind(
if (allow_broadcast_)
socket->AllowBroadcast();
- int32_t pp_result = NetErrorToPepperError(
- socket->Listen(net::IPEndPoint(address, port)));
+ int32_t pp_result =
+ NetErrorToPepperError(socket->Listen(net::IPEndPoint(address, port)));
if (pp_result != PP_OK) {
SendBindError(context, pp_result);
return;
@@ -294,11 +301,9 @@ void PepperUDPSocketMessageFilter::DoBind(
return;
}
- PP_NetAddress_Private net_address =
- NetAddressPrivateImpl::kInvalidNetAddress;
- if (!NetAddressPrivateImpl::IPEndPointToNetAddress(bound_address.address(),
- bound_address.port(),
- &net_address)) {
+ PP_NetAddress_Private net_address = NetAddressPrivateImpl::kInvalidNetAddress;
+ if (!NetAddressPrivateImpl::IPEndPointToNetAddress(
+ bound_address.address(), bound_address.port(), &net_address)) {
SendBindError(context, PP_ERROR_ADDRESS_INVALID);
return;
}
@@ -313,7 +318,7 @@ void PepperUDPSocketMessageFilter::DoSendTo(
const ppapi::host::ReplyMessageContext& context,
const std::string& data,
const PP_NetAddress_Private& addr) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(socket_.get());
if (closed_ || !socket_.get()) {
@@ -329,7 +334,7 @@ void PepperUDPSocketMessageFilter::DoSendTo(
size_t num_bytes = data.size();
if (num_bytes == 0 ||
num_bytes > static_cast<size_t>(
- ppapi::proxy::UDPSocketResourceBase::kMaxWriteSize)) {
+ ppapi::proxy::UDPSocketResourceBase::kMaxWriteSize)) {
// Size of |data| is checked on the plugin side.
NOTREACHED();
SendSendToError(context, PP_ERROR_BADARGUMENT);
@@ -360,7 +365,7 @@ void PepperUDPSocketMessageFilter::DoSendTo(
}
void PepperUDPSocketMessageFilter::Close() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (socket_.get() && !closed_)
socket_->Close();
closed_ = true;
@@ -369,7 +374,7 @@ void PepperUDPSocketMessageFilter::Close() {
void PepperUDPSocketMessageFilter::OnRecvFromCompleted(
const ppapi::host::ReplyMessageContext& context,
int net_result) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(recvfrom_buffer_.get());
int32_t pp_result = NetErrorToPepperError(net_result);
@@ -384,8 +389,8 @@ void PepperUDPSocketMessageFilter::OnRecvFromCompleted(
}
if (pp_result >= 0) {
- SendRecvFromReply(context, PP_OK,
- std::string(recvfrom_buffer_->data(), pp_result), addr);
+ SendRecvFromReply(
+ context, PP_OK, std::string(recvfrom_buffer_->data(), pp_result), addr);
} else {
SendRecvFromError(context, pp_result);
}
@@ -396,7 +401,7 @@ void PepperUDPSocketMessageFilter::OnRecvFromCompleted(
void PepperUDPSocketMessageFilter::OnSendToCompleted(
const ppapi::host::ReplyMessageContext& context,
int net_result) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(sendto_buffer_.get());
int32_t pp_result = NetErrorToPepperError(net_result);
@@ -423,8 +428,7 @@ void PepperUDPSocketMessageFilter::SendRecvFromReply(
const PP_NetAddress_Private& addr) {
ppapi::host::ReplyMessageContext reply_context(context);
reply_context.params.set_result(result);
- SendReply(reply_context,
- PpapiPluginMsg_UDPSocket_RecvFromReply(data, addr));
+ SendReply(reply_context, PpapiPluginMsg_UDPSocket_RecvFromReply(data, addr));
}
void PepperUDPSocketMessageFilter::SendSendToReply(
@@ -433,8 +437,7 @@ void PepperUDPSocketMessageFilter::SendSendToReply(
int32_t bytes_written) {
ppapi::host::ReplyMessageContext reply_context(context);
reply_context.params.set_result(result);
- SendReply(reply_context,
- PpapiPluginMsg_UDPSocket_SendToReply(bytes_written));
+ SendReply(reply_context, PpapiPluginMsg_UDPSocket_SendToReply(bytes_written));
}
void PepperUDPSocketMessageFilter::SendBindError(
diff --git a/chromium/content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.h b/chromium/content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.h
index 2df4ceec63c..8ca7f0800bc 100644
--- a/chromium/content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.h
+++ b/chromium/content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.h
@@ -63,10 +63,9 @@ class CONTENT_EXPORT PepperUDPSocketMessageFilter
const IPC::Message& msg,
ppapi::host::HostMessageContext* context) OVERRIDE;
- int32_t OnMsgSetOption(
- const ppapi::host::HostMessageContext* context,
- PP_UDPSocket_Option name,
- const ppapi::SocketOptionData& value);
+ int32_t OnMsgSetOption(const ppapi::host::HostMessageContext* context,
+ PP_UDPSocket_Option name,
+ const ppapi::SocketOptionData& value);
int32_t OnMsgBind(const ppapi::host::HostMessageContext* context,
const PP_NetAddress_Private& addr);
int32_t OnMsgRecvFrom(const ppapi::host::HostMessageContext* context,
@@ -121,7 +120,7 @@ class CONTENT_EXPORT PepperUDPSocketMessageFilter
bool private_api_;
int render_process_id_;
- int render_view_id_;
+ int render_frame_id_;
DISALLOW_COPY_AND_ASSIGN(PepperUDPSocketMessageFilter);
};
diff --git a/chromium/content/browser/renderer_host/pepper/quota_reservation.cc b/chromium/content/browser/renderer_host/pepper/quota_reservation.cc
index 221b374f110..85a404cac36 100644
--- a/chromium/content/browser/renderer_host/pepper/quota_reservation.cc
+++ b/chromium/content/browser/renderer_host/pepper/quota_reservation.cc
@@ -19,8 +19,8 @@ scoped_refptr<QuotaReservation> QuotaReservation::Create(
scoped_refptr<fileapi::FileSystemContext> file_system_context,
const GURL& origin_url,
fileapi::FileSystemType type) {
- return scoped_refptr<QuotaReservation>(new QuotaReservation(
- file_system_context, origin_url, type));
+ return scoped_refptr<QuotaReservation>(
+ new QuotaReservation(file_system_context, origin_url, type));
}
QuotaReservation::QuotaReservation(
@@ -30,8 +30,7 @@ QuotaReservation::QuotaReservation(
: file_system_context_(file_system_context) {
quota_reservation_ =
file_system_context->CreateQuotaReservationOnFileTaskRunner(
- origin_url,
- file_system_type);
+ origin_url, file_system_type);
}
// For unit testing only.
@@ -39,13 +38,12 @@ QuotaReservation::QuotaReservation(
scoped_refptr<fileapi::QuotaReservation> quota_reservation,
const GURL& /* origin_url */,
fileapi::FileSystemType /* file_system_type */)
- : quota_reservation_(quota_reservation) {
-}
+ : quota_reservation_(quota_reservation) {}
QuotaReservation::~QuotaReservation() {
// We should have no open files at this point.
DCHECK(files_.size() == 0);
- for (FileMap::iterator it = files_.begin(); it != files_.end(); ++ it)
+ for (FileMap::iterator it = files_.begin(); it != files_.end(); ++it)
delete it->second;
}
@@ -53,10 +51,10 @@ int64_t QuotaReservation::OpenFile(int32_t id,
const fileapi::FileSystemURL& url) {
base::FilePath platform_file_path;
if (file_system_context_) {
- base::PlatformFileError error =
+ base::File::Error error =
file_system_context_->operation_runner()->SyncGetPlatformPath(
url, &platform_file_path);
- if (error != base::PLATFORM_FILE_OK) {
+ if (error != base::File::FILE_OK) {
NOTREACHED();
return 0;
}
@@ -70,7 +68,7 @@ int64_t QuotaReservation::OpenFile(int32_t id,
std::pair<FileMap::iterator, bool> insert_result =
files_.insert(std::make_pair(id, file_handle.get()));
if (insert_result.second) {
- int64_t max_written_offset = file_handle->base_file_size();
+ int64_t max_written_offset = file_handle->GetMaxWrittenOffset();
ignore_result(file_handle.release());
return max_written_offset;
}
@@ -79,10 +77,11 @@ int64_t QuotaReservation::OpenFile(int32_t id,
}
void QuotaReservation::CloseFile(int32_t id,
- int64_t max_written_offset) {
+ const ppapi::FileGrowth& file_growth) {
FileMap::iterator it = files_.find(id);
if (it != files_.end()) {
- it->second->UpdateMaxWrittenOffset(max_written_offset);
+ it->second->UpdateMaxWrittenOffset(file_growth.max_written_offset);
+ it->second->AddAppendModeWriteAmount(file_growth.append_mode_write_amount);
delete it->second;
files_.erase(it);
} else {
@@ -90,54 +89,50 @@ void QuotaReservation::CloseFile(int32_t id,
}
}
-void QuotaReservation::ReserveQuota(
- int64_t amount,
- const OffsetMap& max_written_offsets,
- const ReserveQuotaCallback& callback) {
- for (FileMap::iterator it = files_.begin(); it != files_.end(); ++ it) {
- OffsetMap::const_iterator offset_it = max_written_offsets.find(it->first);
- if (offset_it != max_written_offsets.end())
- it->second->UpdateMaxWrittenOffset(offset_it->second);
- else
+void QuotaReservation::ReserveQuota(int64_t amount,
+ const ppapi::FileGrowthMap& file_growths,
+ const ReserveQuotaCallback& callback) {
+ for (FileMap::iterator it = files_.begin(); it != files_.end(); ++it) {
+ ppapi::FileGrowthMap::const_iterator growth_it =
+ file_growths.find(it->first);
+ if (growth_it != file_growths.end()) {
+ it->second->UpdateMaxWrittenOffset(growth_it->second.max_written_offset);
+ it->second->AddAppendModeWriteAmount(
+ growth_it->second.append_mode_write_amount);
+ } else {
NOTREACHED();
+ }
}
quota_reservation_->RefreshReservation(
- amount,
- base::Bind(&QuotaReservation::GotReservedQuota,
- this,
- callback));
+ amount, base::Bind(&QuotaReservation::GotReservedQuota, this, callback));
}
-void QuotaReservation::GotReservedQuota(
- const ReserveQuotaCallback& callback,
- base::PlatformFileError error) {
- OffsetMap max_written_offsets;
- for (FileMap::iterator it = files_.begin(); it != files_.end(); ++ it) {
- max_written_offsets.insert(
- std::make_pair(it->first, it->second->base_file_size()));
- }
+void QuotaReservation::OnClientCrash() { quota_reservation_->OnClientCrash(); }
+
+void QuotaReservation::GotReservedQuota(const ReserveQuotaCallback& callback,
+ base::File::Error error) {
+ ppapi::FileSizeMap file_sizes;
+ for (FileMap::iterator it = files_.begin(); it != files_.end(); ++it)
+ file_sizes[it->first] = it->second->GetMaxWrittenOffset();
if (file_system_context_) {
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
- base::Bind(callback,
- quota_reservation_->remaining_quota(),
- max_written_offsets));
+ base::Bind(
+ callback, quota_reservation_->remaining_quota(), file_sizes));
} else {
// Unit testing code path.
- callback.Run(quota_reservation_->remaining_quota(), max_written_offsets);
+ callback.Run(quota_reservation_->remaining_quota(), file_sizes);
}
}
void QuotaReservation::DeleteOnCorrectThread() const {
- if (file_system_context_ &&
- !file_system_context_->
- default_file_task_runner()->RunsTasksOnCurrentThread()) {
- file_system_context_->default_file_task_runner()->DeleteSoon(
- FROM_HERE,
- this);
+ if (file_system_context_ && !file_system_context_->default_file_task_runner()
+ ->RunsTasksOnCurrentThread()) {
+ file_system_context_->default_file_task_runner()->DeleteSoon(FROM_HERE,
+ this);
} else {
// We're on the right thread to delete, or unit test.
delete this;
diff --git a/chromium/content/browser/renderer_host/pepper/quota_reservation.h b/chromium/content/browser/renderer_host/pepper/quota_reservation.h
index 845b868d345..33490918f93 100644
--- a/chromium/content/browser/renderer_host/pepper/quota_reservation.h
+++ b/chromium/content/browser/renderer_host/pepper/quota_reservation.h
@@ -9,9 +9,9 @@
#include "base/basictypes.h"
#include "base/callback.h"
-#include "base/platform_file.h"
#include "content/common/content_export.h"
#include "ppapi/c/pp_stdint.h" // For int64_t on Windows.
+#include "ppapi/shared_impl/file_growth.h"
#include "url/gurl.h"
#include "webkit/browser/fileapi/file_system_context.h"
@@ -43,15 +43,20 @@ class CONTENT_EXPORT QuotaReservation
// Opens a file with the given id and path and returns its current size.
int64_t OpenFile(int32_t id, const fileapi::FileSystemURL& url);
// Closes the file opened by OpenFile with the given id.
- void CloseFile(int32_t id, int64_t max_written_offset);
+ void CloseFile(int32_t id, const ppapi::FileGrowth& file_growth);
// Refreshes the quota reservation to a new amount. A map that associates file
// ids with maximum written offsets is provided as input. The callback will
// receive a similar map with the updated file sizes.
- typedef std::map<int32_t, int64_t> OffsetMap;
- typedef base::Callback<void(int64_t, const OffsetMap&)> ReserveQuotaCallback;
+ typedef base::Callback<void(int64_t, const ppapi::FileSizeMap&)>
+ ReserveQuotaCallback;
void ReserveQuota(int64_t amount,
- const OffsetMap& max_written_offsets,
+ const ppapi::FileGrowthMap& file_growth,
const ReserveQuotaCallback& callback);
+
+ // Notifies underlying QuotaReservation that the associated client crashed,
+ // and that the reserved quota is no longer traceable.
+ void OnClientCrash();
+
private:
friend class base::RefCountedThreadSafe<QuotaReservation,
QuotaReservationDeleter>;
@@ -66,15 +71,14 @@ class CONTENT_EXPORT QuotaReservation
// For unit testing only. A QuotaReservation intended for unit testing will
// have file_system_context_ == NULL.
- QuotaReservation(
- scoped_refptr<fileapi::QuotaReservation> quota_reservation,
- const GURL& origin_url,
- fileapi::FileSystemType file_system_type);
+ QuotaReservation(scoped_refptr<fileapi::QuotaReservation> quota_reservation,
+ const GURL& origin_url,
+ fileapi::FileSystemType file_system_type);
~QuotaReservation();
void GotReservedQuota(const ReserveQuotaCallback& callback,
- base::PlatformFileError error);
+ base::File::Error error);
void DeleteOnCorrectThread() const;
diff --git a/chromium/content/browser/renderer_host/pepper/quota_reservation_unittest.cc b/chromium/content/browser/renderer_host/pepper/quota_reservation_unittest.cc
index 3f760e55664..e949074efb8 100644
--- a/chromium/content/browser/renderer_host/pepper/quota_reservation_unittest.cc
+++ b/chromium/content/browser/renderer_host/pepper/quota_reservation_unittest.cc
@@ -7,13 +7,13 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/file_util.h"
+#include "base/files/file.h"
#include "base/files/scoped_temp_dir.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "webkit/browser/fileapi/quota/quota_reservation.h"
-using fileapi::FileSystemType;
using fileapi::QuotaReservationManager;
namespace content {
@@ -21,7 +21,7 @@ namespace content {
namespace {
const char kOrigin[] = "http://example.com";
-const FileSystemType kType = fileapi::kFileSystemTypeTemporary;
+const fileapi::FileSystemType kType = fileapi::kFileSystemTypeTemporary;
const base::FilePath::StringType file1_name = FILE_PATH_LITERAL("file1");
const base::FilePath::StringType file2_name = FILE_PATH_LITERAL("file2");
@@ -37,28 +37,26 @@ class FakeBackend : public QuotaReservationManager::QuotaBackend {
virtual void ReserveQuota(
const GURL& origin,
- FileSystemType type,
+ fileapi::FileSystemType type,
int64 delta,
const QuotaReservationManager::ReserveQuotaCallback& callback) OVERRIDE {
base::MessageLoopProxy::current()->PostTask(
FROM_HERE,
- base::Bind(base::IgnoreResult(callback), base::PLATFORM_FILE_OK));
+ base::Bind(base::IgnoreResult(callback), base::File::FILE_OK, delta));
}
virtual void ReleaseReservedQuota(const GURL& origin,
- FileSystemType type,
- int64 size) OVERRIDE {
- }
+ fileapi::FileSystemType type,
+ int64 size) OVERRIDE {}
virtual void CommitQuotaUsage(const GURL& origin,
- FileSystemType type,
- int64 delta) OVERRIDE {
- }
+ fileapi::FileSystemType type,
+ int64 delta) OVERRIDE {}
virtual void IncrementDirtyCount(const GURL& origin,
- FileSystemType type) OVERRIDE {}
+ fileapi::FileSystemType type) OVERRIDE {}
virtual void DecrementDirtyCount(const GURL& origin,
- FileSystemType type) OVERRIDE {}
+ fileapi::FileSystemType type) OVERRIDE {}
private:
DISALLOW_COPY_AND_ASSIGN(FakeBackend);
@@ -80,6 +78,7 @@ class QuotaReservationTest : public testing::Test {
virtual void TearDown() OVERRIDE {
reservation_manager_.reset();
+ base::RunLoop().RunUntilIdle();
}
base::FilePath MakeFilePath(const base::FilePath::StringType& file_name) {
@@ -95,22 +94,17 @@ class QuotaReservationTest : public testing::Test {
scoped_refptr<QuotaReservation> CreateQuotaReservation(
scoped_refptr<fileapi::QuotaReservation> reservation,
const GURL& origin,
- FileSystemType type) {
+ fileapi::FileSystemType type) {
// Sets reservation_ as a side effect.
return scoped_refptr<QuotaReservation>(
new QuotaReservation(reservation, origin, type));
}
void SetFileSize(const base::FilePath::StringType& file_name, int64 size) {
- bool created = false;
- base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
- base::PlatformFile file = CreatePlatformFile(
- MakeFilePath(file_name),
- base::PLATFORM_FILE_OPEN_ALWAYS | base::PLATFORM_FILE_WRITE,
- &created, &error);
- ASSERT_EQ(base::PLATFORM_FILE_OK, error);
- ASSERT_TRUE(base::TruncatePlatformFile(file, size));
- ASSERT_TRUE(base::ClosePlatformFile(file));
+ base::File file(MakeFilePath(file_name),
+ base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_WRITE);
+ ASSERT_TRUE(file.IsValid());
+ ASSERT_TRUE(file.SetLength(size));
}
QuotaReservationManager* reservation_manager() {
@@ -125,124 +119,126 @@ class QuotaReservationTest : public testing::Test {
DISALLOW_COPY_AND_ASSIGN(QuotaReservationTest);
};
-void GotReservedQuota(
- int64* reserved_quota_ptr,
- QuotaReservation::OffsetMap* maximum_written_offsets_ptr,
- int64 reserved_quota,
- const QuotaReservation::OffsetMap& maximum_written_offsets) {
+void GotReservedQuota(int64* reserved_quota_ptr,
+ ppapi::FileGrowthMap* file_growths_ptr,
+ int64 reserved_quota,
+ const ppapi::FileSizeMap& maximum_written_offsets) {
*reserved_quota_ptr = reserved_quota;
- *maximum_written_offsets_ptr = maximum_written_offsets;
+
+ file_growths_ptr->clear();
+ for (ppapi::FileSizeMap::const_iterator it = maximum_written_offsets.begin();
+ it != maximum_written_offsets.end();
+ ++it)
+ (*file_growths_ptr)[it->first] = ppapi::FileGrowth(it->second, 0);
}
-void ReserveQuota(
- scoped_refptr<QuotaReservation> quota_reservation,
- int64 amount,
- int64* reserved_quota,
- QuotaReservation::OffsetMap* max_written_offsets) {
- quota_reservation->ReserveQuota(amount,
- *max_written_offsets,
- base::Bind(&GotReservedQuota,
- reserved_quota,
- max_written_offsets));
+void ReserveQuota(scoped_refptr<QuotaReservation> quota_reservation,
+ int64 amount,
+ int64* reserved_quota,
+ ppapi::FileGrowthMap* file_growths) {
+ quota_reservation->ReserveQuota(
+ amount,
+ *file_growths,
+ base::Bind(&GotReservedQuota, reserved_quota, file_growths));
base::RunLoop().RunUntilIdle();
}
// Tests that:
// 1) We can reserve quota with no files open.
// 2) Open a file, grow it, close it, and reserve quota with correct sizes.
-TEST_F(QuotaReservationTest, DISABLED_ReserveQuota) {
+TEST_F(QuotaReservationTest, ReserveQuota) {
GURL origin(kOrigin);
- FileSystemType type = kType;
+ fileapi::FileSystemType type = kType;
scoped_refptr<fileapi::QuotaReservation> reservation(
- reservation_manager()->CreateReservation(origin, type));
+ reservation_manager()->CreateReservation(origin, type));
scoped_refptr<QuotaReservation> test =
CreateQuotaReservation(reservation, origin, type);
// Reserve quota with no files open.
int64 amount = 100;
int64 reserved_quota;
- QuotaReservation::OffsetMap max_written_offsets;
- ReserveQuota(test, amount, &reserved_quota, &max_written_offsets);
+ ppapi::FileGrowthMap file_growths;
+ ReserveQuota(test, amount, &reserved_quota, &file_growths);
EXPECT_EQ(amount, reserved_quota);
- EXPECT_EQ(0U, max_written_offsets.size());
+ EXPECT_EQ(0U, file_growths.size());
// Open a file, refresh the reservation, extend the file, and close it.
int64 file_size = 10;
SetFileSize(file1_name, file_size);
- int64 open_file_size = test->OpenFile(kFile1ID,
- MakeFileSystemURL(file1_name));
+ int64 open_file_size =
+ test->OpenFile(kFile1ID, MakeFileSystemURL(file1_name));
EXPECT_EQ(file_size, open_file_size);
- max_written_offsets[kFile1ID] = file_size; // 1 file open.
- ReserveQuota(test, amount, &reserved_quota, &max_written_offsets);
+ file_growths[kFile1ID] = ppapi::FileGrowth(file_size, 0); // 1 file open.
+ ReserveQuota(test, amount, &reserved_quota, &file_growths);
EXPECT_EQ(amount, reserved_quota);
- EXPECT_EQ(1U, max_written_offsets.size());
- EXPECT_EQ(file_size, max_written_offsets[kFile1ID]);
+ EXPECT_EQ(1U, file_growths.size());
+ EXPECT_EQ(file_size, file_growths[kFile1ID].max_written_offset);
int64 new_file_size = 30;
SetFileSize(file1_name, new_file_size);
EXPECT_EQ(amount, reservation->remaining_quota());
- test->CloseFile(kFile1ID, new_file_size);
+ test->CloseFile(kFile1ID, ppapi::FileGrowth(new_file_size, 0));
EXPECT_EQ(amount - (new_file_size - file_size),
reservation->remaining_quota());
}
// Tests that:
// 1) We can open and close multiple files.
-TEST_F(QuotaReservationTest, DISABLED_MultipleFiles) {
+TEST_F(QuotaReservationTest, MultipleFiles) {
GURL origin(kOrigin);
- FileSystemType type = kType;
+ fileapi::FileSystemType type = kType;
scoped_refptr<fileapi::QuotaReservation> reservation(
- reservation_manager()->CreateReservation(origin, type));
+ reservation_manager()->CreateReservation(origin, type));
scoped_refptr<QuotaReservation> test =
CreateQuotaReservation(reservation, origin, type);
// Open some files of different sizes.
int64 file1_size = 10;
SetFileSize(file1_name, file1_size);
- int64 open_file1_size = test->OpenFile(kFile1ID,
- MakeFileSystemURL(file1_name));
+ int64 open_file1_size =
+ test->OpenFile(kFile1ID, MakeFileSystemURL(file1_name));
EXPECT_EQ(file1_size, open_file1_size);
int64 file2_size = 20;
SetFileSize(file2_name, file2_size);
- int64 open_file2_size = test->OpenFile(kFile2ID,
- MakeFileSystemURL(file2_name));
+ int64 open_file2_size =
+ test->OpenFile(kFile2ID, MakeFileSystemURL(file2_name));
EXPECT_EQ(file2_size, open_file2_size);
int64 file3_size = 30;
SetFileSize(file3_name, file3_size);
- int64 open_file3_size = test->OpenFile(kFile3ID,
- MakeFileSystemURL(file3_name));
+ int64 open_file3_size =
+ test->OpenFile(kFile3ID, MakeFileSystemURL(file3_name));
EXPECT_EQ(file3_size, open_file3_size);
// Reserve quota.
int64 amount = 100;
int64 reserved_quota;
- QuotaReservation::OffsetMap max_written_offsets;
- max_written_offsets[kFile1ID] = file1_size; // 3 files open.
- max_written_offsets[kFile2ID] = file2_size;
- max_written_offsets[kFile3ID] = file3_size;
+ ppapi::FileGrowthMap file_growths;
+ file_growths[kFile1ID] = ppapi::FileGrowth(file1_size, 0); // 3 files open.
+ file_growths[kFile2ID] = ppapi::FileGrowth(file2_size, 0);
+ file_growths[kFile3ID] = ppapi::FileGrowth(file3_size, 0);
- ReserveQuota(test, amount, &reserved_quota, &max_written_offsets);
+ ReserveQuota(test, amount, &reserved_quota, &file_growths);
EXPECT_EQ(amount, reserved_quota);
- EXPECT_EQ(3U, max_written_offsets.size());
- EXPECT_EQ(file1_size, max_written_offsets[kFile1ID]);
- EXPECT_EQ(file2_size, max_written_offsets[kFile2ID]);
- EXPECT_EQ(file3_size, max_written_offsets[kFile3ID]);
+ EXPECT_EQ(3U, file_growths.size());
+ EXPECT_EQ(file1_size, file_growths[kFile1ID].max_written_offset);
+ EXPECT_EQ(file2_size, file_growths[kFile2ID].max_written_offset);
+ EXPECT_EQ(file3_size, file_growths[kFile3ID].max_written_offset);
- test->CloseFile(kFile2ID, file2_size);
+ test->CloseFile(kFile2ID, ppapi::FileGrowth(file2_size, 0));
- max_written_offsets.erase(max_written_offsets.find(kFile2ID));
- ReserveQuota(test, amount, &reserved_quota, &max_written_offsets);
+ file_growths.erase(kFile2ID);
+ ReserveQuota(test, amount, &reserved_quota, &file_growths);
EXPECT_EQ(amount, reserved_quota);
- EXPECT_EQ(2U, max_written_offsets.size());
- EXPECT_EQ(file1_size, max_written_offsets[kFile1ID]);
- EXPECT_EQ(file3_size, max_written_offsets[kFile3ID]);
+ EXPECT_EQ(2U, file_growths.size());
+ EXPECT_EQ(file1_size, file_growths[kFile1ID].max_written_offset);
+ EXPECT_EQ(file3_size, file_growths[kFile3ID].max_written_offset);
- test->CloseFile(kFile1ID, file1_size);
- test->CloseFile(kFile3ID, file3_size);
+ test->CloseFile(kFile1ID, ppapi::FileGrowth(file1_size, 0));
+ test->CloseFile(kFile3ID, ppapi::FileGrowth(file3_size, 0));
}
} // namespace content
diff --git a/chromium/content/browser/renderer_host/pepper/ssl_context_helper.cc b/chromium/content/browser/renderer_host/pepper/ssl_context_helper.cc
index 3b92e6d0c16..a5ab3c806ae 100644
--- a/chromium/content/browser/renderer_host/pepper/ssl_context_helper.cc
+++ b/chromium/content/browser/renderer_host/pepper/ssl_context_helper.cc
@@ -9,11 +9,9 @@
namespace content {
-SSLContextHelper::SSLContextHelper() {
-}
+SSLContextHelper::SSLContextHelper() {}
-SSLContextHelper::~SSLContextHelper() {
-}
+SSLContextHelper::~SSLContextHelper() {}
net::CertVerifier* SSLContextHelper::GetCertVerifier() {
if (!cert_verifier_)
diff --git a/chromium/content/browser/renderer_host/popup_menu_helper_mac.h b/chromium/content/browser/renderer_host/popup_menu_helper_mac.h
index 332fecdc54f..8a6f3176c7e 100644
--- a/chromium/content/browser/renderer_host/popup_menu_helper_mac.h
+++ b/chromium/content/browser/renderer_host/popup_menu_helper_mac.h
@@ -13,6 +13,12 @@
#include "content/public/browser/notification_registrar.h"
#include "ui/gfx/rect.h"
+#ifdef __OBJC__
+@class WebMenuRunner;
+#else
+class WebMenuRunner;
+#endif
+
namespace content {
class RenderViewHost;
class RenderViewHostImpl;
@@ -24,6 +30,7 @@ class PopupMenuHelper : public NotificationObserver {
// Creates a PopupMenuHelper that will notify |render_view_host| when a user
// selects or cancels the popup.
explicit PopupMenuHelper(RenderViewHost* render_view_host);
+ void Hide();
// Shows the popup menu and notifies the RenderViewHost of the selection/
// cancel.
@@ -48,8 +55,9 @@ class PopupMenuHelper : public NotificationObserver {
const NotificationDetails& details) OVERRIDE;
NotificationRegistrar notification_registrar_;
-
RenderViewHostImpl* render_view_host_;
+ WebMenuRunner* menu_runner_;
+ bool popup_was_hidden_;
DISALLOW_COPY_AND_ASSIGN(PopupMenuHelper);
};
diff --git a/chromium/content/browser/renderer_host/popup_menu_helper_mac.mm b/chromium/content/browser/renderer_host/popup_menu_helper_mac.mm
index d5b9ed876d5..1fb28bae96b 100644
--- a/chromium/content/browser/renderer_host/popup_menu_helper_mac.mm
+++ b/chromium/content/browser/renderer_host/popup_menu_helper_mac.mm
@@ -25,7 +25,9 @@ bool g_allow_showing_popup_menus = true;
} // namespace
PopupMenuHelper::PopupMenuHelper(RenderViewHost* render_view_host)
- : render_view_host_(static_cast<RenderViewHostImpl*>(render_view_host)) {
+ : render_view_host_(static_cast<RenderViewHostImpl*>(render_view_host)),
+ menu_runner_(nil),
+ popup_was_hidden_(false) {
notification_registrar_.Add(
this, NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
Source<RenderWidgetHost>(render_view_host));
@@ -55,10 +57,9 @@ void PopupMenuHelper::ShowPopupMenu(
[rwhvm->cocoa_view() retain]);
// Display the menu.
- base::scoped_nsobject<WebMenuRunner> menu_runner;
- menu_runner.reset([[WebMenuRunner alloc] initWithItems:items
- fontSize:item_font_size
- rightAligned:right_aligned]);
+ menu_runner_ = [[WebMenuRunner alloc] initWithItems:items
+ fontSize:item_font_size
+ rightAligned:right_aligned];
{
// Make sure events can be pumped while the menu is up.
@@ -73,23 +74,35 @@ void PopupMenuHelper::ShowPopupMenu(
base::mac::ScopedSendingEvent sending_event_scoper;
// Now run a SYNCHRONOUS NESTED EVENT LOOP until the pop-up is finished.
- [menu_runner runMenuInView:cocoa_view
- withBounds:[cocoa_view flipRectToNSRect:bounds]
- initialIndex:selected_item];
+ [menu_runner_ runMenuInView:cocoa_view
+ withBounds:[cocoa_view flipRectToNSRect:bounds]
+ initialIndex:selected_item];
}
if (!render_view_host_) {
// Bad news, the RenderViewHost got deleted while we were off running the
// menu. Nothing to do.
+ [menu_runner_ release];
+ menu_runner_ = nil;
return;
}
- if ([menu_runner menuItemWasChosen]) {
- render_view_host_->DidSelectPopupMenuItem(
- [menu_runner indexOfSelectedItem]);
- } else {
- render_view_host_->DidCancelPopupMenu();
+ if (!popup_was_hidden_) {
+ if ([menu_runner_ menuItemWasChosen]) {
+ render_view_host_->DidSelectPopupMenuItem(
+ [menu_runner_ indexOfSelectedItem]);
+ } else {
+ render_view_host_->DidCancelPopupMenu();
+ }
}
+ [menu_runner_ release];
+ menu_runner_ = nil;
+}
+
+void PopupMenuHelper::Hide() {
+ if (menu_runner_)
+ [menu_runner_ hide];
+ popup_was_hidden_ = true;
}
// static
diff --git a/chromium/content/browser/renderer_host/render_message_filter.cc b/chromium/content/browser/renderer_host/render_message_filter.cc
index fc084e1d2f3..cb1617089fc 100644
--- a/chromium/content/browser/renderer_host/render_message_filter.cc
+++ b/chromium/content/browser/renderer_host/render_message_filter.cc
@@ -34,7 +34,7 @@
#include "content/common/cookie_data.h"
#include "content/common/desktop_notification_messages.h"
#include "content/common/frame_messages.h"
-#include "content/common/gpu/client/gpu_memory_buffer_impl.h"
+#include "content/common/host_shared_bitmap_manager.h"
#include "content/common/media/media_param_traits.h"
#include "content/common/view_messages.h"
#include "content/public/browser/browser_child_process_host.h"
@@ -61,7 +61,7 @@
#include "net/base/mime_util.h"
#include "net/base/request_priority.h"
#include "net/cookies/canonical_cookie.h"
-#include "net/cookies/cookie_monster.h"
+#include "net/cookies/cookie_store.h"
#include "net/http/http_cache.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
@@ -70,9 +70,7 @@
#include "ui/gfx/color_profile.h"
#if defined(OS_MACOSX)
-#include "content/common/gpu/client/gpu_memory_buffer_impl_io_surface.h"
#include "content/common/mac/font_descriptor.h"
-#include "ui/gl/io_surface_support_mac.h"
#else
#include "gpu/GLES2/gl2extchromium.h"
#include "third_party/khronos/GLES2/gl2.h"
@@ -82,8 +80,8 @@
#include "base/file_descriptor_posix.h"
#endif
#if defined(OS_WIN)
-#include "content/browser/renderer_host/backing_store_win.h"
#include "content/common/font_cache_dispatcher_win.h"
+#include "content/common/sandbox_win.h"
#endif
#if defined(OS_ANDROID)
#include "media/base/android/webaudio_media_codec_bridge.h"
@@ -102,6 +100,13 @@ const int kPluginsRefreshThresholdInSeconds = 3;
// usage only once and send it as a response for both queries.
static const int64 kCPUUsageSampleIntervalMs = 900;
+const uint32 kFilteredMessageClasses[] = {
+ ChildProcessMsgStart,
+ DesktopNotificationMsgStart,
+ FrameMsgStart,
+ ViewMsgStart,
+};
+
#if defined(OS_WIN)
// On Windows, |g_color_profile| can run on an arbitrary background thread.
// We avoid races by using LazyInstance's constructor lock to initialize the
@@ -210,23 +215,6 @@ class OpenChannelToPpapiBrokerCallback
int routing_id_;
};
-#if defined(OS_MACOSX)
-void AddBooleanValue(CFMutableDictionaryRef dictionary,
- const CFStringRef key,
- bool value) {
- CFDictionaryAddValue(
- dictionary, key, value ? kCFBooleanTrue : kCFBooleanFalse);
-}
-
-void AddIntegerValue(CFMutableDictionaryRef dictionary,
- const CFStringRef key,
- int32 value) {
- base::ScopedCFTypeRef<CFNumberRef> number(
- CFNumberCreate(NULL, kCFNumberSInt32Type, &value));
- CFDictionaryAddValue(dictionary, key, number.get());
-}
-#endif
-
} // namespace
class RenderMessageFilter::OpenChannelToNpapiPluginCallback
@@ -313,7 +301,6 @@ class RenderMessageFilter::OpenChannelToNpapiPluginCallback
RenderMessageFilter::RenderMessageFilter(
int render_process_id,
- bool is_guest,
PluginServiceImpl* plugin_service,
BrowserContext* browser_context,
net::URLRequestContextGetter* request_context,
@@ -321,7 +308,9 @@ RenderMessageFilter::RenderMessageFilter(
media::AudioManager* audio_manager,
MediaInternals* media_internals,
DOMStorageContextWrapper* dom_storage_context)
- : resource_dispatcher_host_(ResourceDispatcherHostImpl::Get()),
+ : BrowserMessageFilter(
+ kFilteredMessageClasses, arraysize(kFilteredMessageClasses)),
+ resource_dispatcher_host_(ResourceDispatcherHostImpl::Get()),
plugin_service_(plugin_service),
profile_data_directory_(browser_context->GetPath()),
request_context_(request_context),
@@ -330,7 +319,6 @@ RenderMessageFilter::RenderMessageFilter(
incognito_(browser_context->IsOffTheRecord()),
dom_storage_context_(dom_storage_context),
render_process_id_(render_process_id),
- is_guest_(is_guest),
cpu_usage_(0),
audio_manager_(audio_manager),
media_internals_(media_internals) {
@@ -341,8 +329,9 @@ RenderMessageFilter::RenderMessageFilter(
RenderMessageFilter::~RenderMessageFilter() {
// This function should be called on the IO thread.
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(plugin_host_clients_.empty());
+ HostSharedBitmapManager::current()->ProcessRemoved(PeerHandle());
}
void RenderMessageFilter::OnChannelClosing() {
@@ -377,10 +366,9 @@ void RenderMessageFilter::OnChannelConnected(int32 peer_id) {
cpu_usage_sample_time_ = base::TimeTicks::Now();
}
-bool RenderMessageFilter::OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) {
+bool RenderMessageFilter::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(RenderMessageFilter, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(RenderMessageFilter, message)
#if defined(OS_WIN)
IPC_MESSAGE_HANDLER(ViewHostMsg_PreCacheFontCharacters,
OnPreCacheFontCharacters)
@@ -415,16 +403,21 @@ bool RenderMessageFilter::OnMessageReceived(const IPC::Message& message,
IPC_MESSAGE_HANDLER(ViewHostMsg_OpenChannelToPpapiBroker,
OnOpenChannelToPpapiBroker)
#endif
+ IPC_MESSAGE_HANDLER_GENERIC(ViewHostMsg_SwapCompositorFrame,
+ render_widget_helper_->DidReceiveBackingStoreMsg(message))
IPC_MESSAGE_HANDLER_GENERIC(ViewHostMsg_UpdateRect,
render_widget_helper_->DidReceiveBackingStoreMsg(message))
- IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateIsDelayed, OnUpdateIsDelayed)
IPC_MESSAGE_HANDLER(DesktopNotificationHostMsg_CheckPermission,
OnCheckNotificationPermission)
IPC_MESSAGE_HANDLER(ChildProcessHostMsg_SyncAllocateSharedMemory,
OnAllocateSharedMemory)
- IPC_MESSAGE_HANDLER(ChildProcessHostMsg_SyncAllocateGpuMemoryBuffer,
- OnAllocateGpuMemoryBuffer)
-#if defined(OS_POSIX) && !defined(TOOLKIT_GTK) && !defined(OS_ANDROID)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(
+ ChildProcessHostMsg_SyncAllocateSharedBitmap, OnAllocateSharedBitmap)
+ IPC_MESSAGE_HANDLER(ChildProcessHostMsg_AllocatedSharedBitmap,
+ OnAllocatedSharedBitmap)
+ IPC_MESSAGE_HANDLER(ChildProcessHostMsg_DeletedSharedBitmap,
+ OnDeletedSharedBitmap)
+#if defined(OS_POSIX) && !defined(OS_ANDROID)
IPC_MESSAGE_HANDLER(ViewHostMsg_AllocTransportDIB, OnAllocTransportDIB)
IPC_MESSAGE_HANDLER(ViewHostMsg_FreeTransportDIB, OnFreeTransportDIB)
#endif
@@ -445,7 +438,7 @@ bool RenderMessageFilter::OnMessageReceived(const IPC::Message& message,
IPC_MESSAGE_HANDLER(ViewHostMsg_RunWebAudioMediaCodec, OnWebAudioMediaCodec)
#endif
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return handled;
}
@@ -461,11 +454,9 @@ base::TaskRunner* RenderMessageFilter::OverrideTaskRunnerForMessage(
if (message.type() == ViewHostMsg_GetMonitorColorProfile::ID)
return BrowserThread::GetBlockingPool();
#endif
-#if defined(OS_MACOSX)
- // OSX CoreAudio calls must all happen on the main thread.
+ // Always query audio device parameters on the audio thread.
if (message.type() == ViewHostMsg_GetAudioHardwareConfig::ID)
- return audio_manager_->GetMessageLoop().get();
-#endif
+ return audio_manager_->GetTaskRunner().get();
return NULL;
}
@@ -506,7 +497,6 @@ void RenderMessageFilter::OnCreateWindow(
params.opener_suppressed,
resource_context_,
render_process_id_,
- is_guest_,
params.opener_id,
&no_javascript_access);
@@ -549,7 +539,7 @@ void RenderMessageFilter::OnCreateFullscreenWidget(int opener_id,
void RenderMessageFilter::OnGetProcessMemorySizes(size_t* private_bytes,
size_t* shared_bytes) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
using base::ProcessMetrics;
#if !defined(OS_MACOSX) || defined(OS_IOS)
scoped_ptr<ProcessMetrics> metrics(ProcessMetrics::CreateProcessMetrics(
@@ -564,7 +554,7 @@ void RenderMessageFilter::OnGetProcessMemorySizes(size_t* private_bytes,
}
}
-void RenderMessageFilter::OnSetCookie(const IPC::Message& message,
+void RenderMessageFilter::OnSetCookie(int render_frame_id,
const GURL& url,
const GURL& first_party_for_cookies,
const std::string& cookie) {
@@ -575,17 +565,17 @@ void RenderMessageFilter::OnSetCookie(const IPC::Message& message,
net::CookieOptions options;
if (GetContentClient()->browser()->AllowSetCookie(
- url, first_party_for_cookies, cookie,
- resource_context_, render_process_id_, message.routing_id(),
- &options)) {
- net::URLRequestContext* context = GetRequestContextForURL(url);
+ url, first_party_for_cookies, cookie, resource_context_,
+ render_process_id_, render_frame_id, &options)) {
+ net::CookieStore* cookie_store = GetCookieStoreForURL(url);
// Pass a null callback since we don't care about when the 'set' completes.
- context->cookie_store()->SetCookieWithOptionsAsync(
- url, cookie, options, net::CookieMonster::SetCookiesCallback());
+ cookie_store->SetCookieWithOptionsAsync(
+ url, cookie, options, net::CookieStore::SetCookiesCallback());
}
}
-void RenderMessageFilter::OnGetCookies(const GURL& url,
+void RenderMessageFilter::OnGetCookies(int render_frame_id,
+ const GURL& url,
const GURL& first_party_for_cookies,
IPC::Message* reply_msg) {
ChildProcessSecurityPolicyImpl* policy =
@@ -601,12 +591,11 @@ void RenderMessageFilter::OnGetCookies(const GURL& url,
base::strlcpy(url_buf, url.spec().c_str(), arraysize(url_buf));
base::debug::Alias(url_buf);
- net::URLRequestContext* context = GetRequestContextForURL(url);
- net::CookieMonster* cookie_monster =
- context->cookie_store()->GetCookieMonster();
- cookie_monster->GetAllCookiesForURLAsync(
- url, base::Bind(&RenderMessageFilter::CheckPolicyForCookies, this, url,
- first_party_for_cookies, reply_msg));
+ net::CookieStore* cookie_store = GetCookieStoreForURL(url);
+ cookie_store->GetAllCookiesForURLAsync(
+ url, base::Bind(&RenderMessageFilter::CheckPolicyForCookies, this,
+ render_frame_id, url, first_party_for_cookies,
+ reply_msg));
}
void RenderMessageFilter::OnGetRawCookies(
@@ -628,10 +617,8 @@ void RenderMessageFilter::OnGetRawCookies(
// We check policy here to avoid sending back cookies that would not normally
// be applied to outbound requests for the given URL. Since this cookie info
// is visible in the developer tools, it is helpful to make it match reality.
- net::URLRequestContext* context = GetRequestContextForURL(url);
- net::CookieMonster* cookie_monster =
- context->cookie_store()->GetCookieMonster();
- cookie_monster->GetAllCookiesForURLAsync(
+ net::CookieStore* cookie_store = GetCookieStoreForURL(url);
+ cookie_store->GetAllCookiesForURLAsync(
url, base::Bind(&RenderMessageFilter::SendGetRawCookiesResponse,
this, reply_msg));
}
@@ -643,11 +630,12 @@ void RenderMessageFilter::OnDeleteCookie(const GURL& url,
if (!policy->CanAccessCookiesForOrigin(render_process_id_, url))
return;
- net::URLRequestContext* context = GetRequestContextForURL(url);
- context->cookie_store()->DeleteCookieAsync(url, cookie_name, base::Closure());
+ net::CookieStore* cookie_store = GetCookieStoreForURL(url);
+ cookie_store->DeleteCookieAsync(url, cookie_name, base::Closure());
}
void RenderMessageFilter::OnCookiesEnabled(
+ int render_frame_id,
const GURL& url,
const GURL& first_party_for_cookies,
bool* cookies_enabled) {
@@ -656,7 +644,7 @@ void RenderMessageFilter::OnCookiesEnabled(
// host.
*cookies_enabled = GetContentClient()->browser()->AllowGetCookie(
url, first_party_for_cookies, net::CookieList(), resource_context_,
- render_process_id_, MSG_ROUTING_CONTROL);
+ render_process_id_, render_frame_id);
}
#if defined(OS_MACOSX)
@@ -859,21 +847,29 @@ void RenderMessageFilter::OnGetAudioHardwareConfig(
#if defined(OS_WIN)
void RenderMessageFilter::OnGetMonitorColorProfile(std::vector<char>* profile) {
DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO));
- if (BackingStoreWin::ColorManagementEnabled())
- return;
*profile = g_color_profile.Get().profile();
}
#endif
-void RenderMessageFilter::OnDownloadUrl(const IPC::Message& message,
+void RenderMessageFilter::OnDownloadUrl(int render_view_id,
const GURL& url,
const Referrer& referrer,
- const base::string16& suggested_name) {
+ const base::string16& suggested_name,
+ const bool use_prompt) {
scoped_ptr<DownloadSaveInfo> save_info(new DownloadSaveInfo());
save_info->suggested_name = suggested_name;
+ save_info->prompt_for_save_location = use_prompt;
+
+ // There may be a special cookie store that we could use for this download,
+ // rather than the default one. Since this feature is generally only used for
+ // proper render views, and not downloads, we do not need to retrieve the
+ // special cookie store here, but just initialize the request to use the
+ // default cookie store.
+ // TODO(tburkard): retrieve the appropriate special cookie store, if this
+ // is ever to be used for downloads as well.
scoped_ptr<net::URLRequest> request(
resource_context_->GetRequestContext()->CreateRequest(
- url, net::DEFAULT_PRIORITY, NULL));
+ url, net::DEFAULT_PRIORITY, NULL, NULL));
RecordDownloadSource(INITIATED_BY_RENDERER);
resource_dispatcher_host_->BeginDownload(
request.Pass(),
@@ -881,7 +877,7 @@ void RenderMessageFilter::OnDownloadUrl(const IPC::Message& message,
true, // is_content_initiated
resource_context_,
render_process_id_,
- message.routing_id(),
+ render_view_id,
false,
save_info.Pass(),
content::DownloadItem::kInvalidId,
@@ -906,20 +902,70 @@ void RenderMessageFilter::OnAllocateSharedMemory(
buffer_size, PeerHandle(), handle);
}
-net::URLRequestContext* RenderMessageFilter::GetRequestContextForURL(
+void RenderMessageFilter::AllocateSharedBitmapOnFileThread(
+ uint32 buffer_size,
+ const cc::SharedBitmapId& id,
+ IPC::Message* reply_msg) {
+ base::SharedMemoryHandle handle;
+ HostSharedBitmapManager::current()->AllocateSharedBitmapForChild(
+ PeerHandle(), buffer_size, id, &handle);
+ ChildProcessHostMsg_SyncAllocateSharedBitmap::WriteReplyParams(reply_msg,
+ handle);
+ Send(reply_msg);
+}
+
+void RenderMessageFilter::OnAllocateSharedBitmap(uint32 buffer_size,
+ const cc::SharedBitmapId& id,
+ IPC::Message* reply_msg) {
+ BrowserThread::PostTask(
+ BrowserThread::FILE_USER_BLOCKING,
+ FROM_HERE,
+ base::Bind(&RenderMessageFilter::AllocateSharedBitmapOnFileThread,
+ this,
+ buffer_size,
+ id,
+ reply_msg));
+}
+
+void RenderMessageFilter::OnAllocatedSharedBitmap(
+ size_t buffer_size,
+ const base::SharedMemoryHandle& handle,
+ const cc::SharedBitmapId& id) {
+ HostSharedBitmapManager::current()->ChildAllocatedSharedBitmap(
+ buffer_size, handle, PeerHandle(), id);
+}
+
+void RenderMessageFilter::OnDeletedSharedBitmap(const cc::SharedBitmapId& id) {
+ HostSharedBitmapManager::current()->ChildDeletedSharedBitmap(id);
+}
+
+net::CookieStore* RenderMessageFilter::GetCookieStoreForURL(
const GURL& url) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
net::URLRequestContext* context =
GetContentClient()->browser()->OverrideRequestContextForURL(
url, resource_context_);
- if (!context)
- context = request_context_->GetURLRequestContext();
- return context;
+ // If we should use a special URLRequestContext rather than the default one,
+ // return the cookie store of that special URLRequestContext.
+ if (context)
+ return context->cookie_store();
+
+ // Otherwise, if there is a special cookie store to be used for this process,
+ // return that cookie store.
+ net::CookieStore* cookie_store =
+ GetContentClient()->browser()->OverrideCookieStoreForRenderProcess(
+ render_process_id_);
+ if (cookie_store)
+ return cookie_store;
+
+ // Otherwise, return the cookie store of the default request context used
+ // for this renderer.
+ return request_context_->GetURLRequestContext()->cookie_store();
}
-#if defined(OS_POSIX) && !defined(TOOLKIT_GTK) && !defined(OS_ANDROID)
+#if defined(OS_POSIX) && !defined(OS_ANDROID)
void RenderMessageFilter::OnAllocTransportDIB(
uint32 size, bool cache_in_browser, TransportDIB::Handle* handle) {
render_widget_helper_->AllocTransportDIB(size, cache_in_browser, handle);
@@ -1034,18 +1080,19 @@ void RenderMessageFilter::OnMediaLogEvents(
}
void RenderMessageFilter::CheckPolicyForCookies(
+ int render_frame_id,
const GURL& url,
const GURL& first_party_for_cookies,
IPC::Message* reply_msg,
const net::CookieList& cookie_list) {
- net::URLRequestContext* context = GetRequestContextForURL(url);
+ net::CookieStore* cookie_store = GetCookieStoreForURL(url);
// Check the policy for get cookies, and pass cookie_list to the
// TabSpecificContentSetting for logging purpose.
if (GetContentClient()->browser()->AllowGetCookie(
url, first_party_for_cookies, cookie_list, resource_context_,
- render_process_id_, reply_msg->routing_id())) {
+ render_process_id_, render_frame_id)) {
// Gets the cookies from cookie store if allowed.
- context->cookie_store()->GetCookiesWithOptionsAsync(
+ cookie_store->GetCookiesWithOptionsAsync(
url, net::CookieOptions(),
base::Bind(&RenderMessageFilter::SendGetCookiesResponse,
this, reply_msg));
@@ -1072,24 +1119,11 @@ void RenderMessageFilter::SendGetRawCookiesResponse(
void RenderMessageFilter::OnCompletedOpenChannelToNpapiPlugin(
OpenChannelToNpapiPluginCallback* client) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(ContainsKey(plugin_host_clients_, client));
plugin_host_clients_.erase(client);
}
-void RenderMessageFilter::OnUpdateIsDelayed(const IPC::Message& msg) {
- // When not in accelerated compositing mode, in certain cases (e.g. waiting
- // for a resize or if no backing store) the RenderWidgetHost is blocking the
- // UI thread for some time, waiting for an UpdateRect from the renderer. If we
- // are going to switch to accelerated compositing, the GPU process may need
- // round-trips to the UI thread before finishing the frame, causing deadlocks
- // if we delay the UpdateRect until we receive the OnSwapBuffersComplete. So
- // the renderer sent us this message, so that we can unblock the UI thread.
- // We will simply re-use the UpdateRect unblock mechanism, just with a
- // different message.
- render_widget_helper_->DidReceiveBackingStoreMsg(msg);
-}
-
void RenderMessageFilter::OnAre3DAPIsBlocked(int render_view_id,
const GURL& top_origin_url,
ThreeDAPIType requester,
@@ -1139,6 +1173,11 @@ void RenderMessageFilter::OnDidLose3DContext(
#if defined(OS_WIN)
void RenderMessageFilter::OnPreCacheFontCharacters(const LOGFONT& font,
const base::string16& str) {
+ // TODO(scottmg): pdf/ppapi still require the renderer to be able to precache
+ // GDI fonts (http://crbug.com/383227), even when using DirectWrite.
+ // Eventually this shouldn't be added and should be moved to
+ // FontCacheDispatcher too. http://crbug.com/356346.
+
// First, comments from FontCacheDispatcher::OnPreCacheFont do apply here too.
// Except that for True Type fonts,
// GetTextMetrics will not load the font in memory.
@@ -1181,79 +1220,4 @@ void RenderMessageFilter::OnWebAudioMediaCodec(
}
#endif
-void RenderMessageFilter::OnAllocateGpuMemoryBuffer(
- uint32 width,
- uint32 height,
- uint32 internalformat,
- gfx::GpuMemoryBufferHandle* handle) {
- if (!GpuMemoryBufferImpl::IsFormatValid(internalformat)) {
- handle->type = gfx::EMPTY_BUFFER;
- return;
- }
-
-#if defined(OS_MACOSX)
- if (GpuMemoryBufferImplIOSurface::IsFormatSupported(internalformat)) {
- IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize();
- if (io_surface_support) {
- base::ScopedCFTypeRef<CFMutableDictionaryRef> properties;
- properties.reset(
- CFDictionaryCreateMutable(kCFAllocatorDefault,
- 0,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks));
- AddIntegerValue(properties,
- io_surface_support->GetKIOSurfaceWidth(),
- width);
- AddIntegerValue(properties,
- io_surface_support->GetKIOSurfaceHeight(),
- height);
- AddIntegerValue(properties,
- io_surface_support->GetKIOSurfaceBytesPerElement(),
- GpuMemoryBufferImpl::BytesPerPixel(internalformat));
- AddIntegerValue(properties,
- io_surface_support->GetKIOSurfacePixelFormat(),
- GpuMemoryBufferImplIOSurface::PixelFormat(
- internalformat));
- // TODO(reveman): Remove this when using a mach_port_t to transfer
- // IOSurface to renderer process. crbug.com/323304
- AddBooleanValue(properties,
- io_surface_support->GetKIOSurfaceIsGlobal(),
- true);
-
- base::ScopedCFTypeRef<CFTypeRef> io_surface(
- io_surface_support->IOSurfaceCreate(properties));
- if (io_surface) {
- handle->type = gfx::IO_SURFACE_BUFFER;
- handle->io_surface_id = io_surface_support->IOSurfaceGetID(io_surface);
-
- // TODO(reveman): This makes the assumption that the renderer will
- // grab a reference to the surface before sending another message.
- // crbug.com/325045
- last_io_surface_ = io_surface;
- return;
- }
- }
- }
-#endif
-
- uint64 stride = static_cast<uint64>(width) *
- GpuMemoryBufferImpl::BytesPerPixel(internalformat);
- if (stride > std::numeric_limits<uint32>::max()) {
- handle->type = gfx::EMPTY_BUFFER;
- return;
- }
-
- uint64 buffer_size = stride * static_cast<uint64>(height);
- if (buffer_size > std::numeric_limits<size_t>::max()) {
- handle->type = gfx::EMPTY_BUFFER;
- return;
- }
-
- // Fallback to fake GpuMemoryBuffer that is backed by shared memory and
- // requires an upload before it can be used as a texture.
- handle->type = gfx::SHARED_MEMORY_BUFFER;
- ChildProcessHostImpl::AllocateSharedMemory(
- static_cast<size_t>(buffer_size), PeerHandle(), &handle->handle);
-}
-
} // namespace content
diff --git a/chromium/content/browser/renderer_host/render_message_filter.h b/chromium/content/browser/renderer_host/render_message_filter.h
index ed879dc585a..95a80acbf0c 100644
--- a/chromium/content/browser/renderer_host/render_message_filter.h
+++ b/chromium/content/browser/renderer_host/render_message_filter.h
@@ -19,9 +19,11 @@
#include "base/sequenced_task_runner_helpers.h"
#include "base/strings/string16.h"
#include "build/build_config.h"
+#include "cc/resources/shared_bitmap_manager.h"
#include "content/common/pepper_renderer_instance_data.h"
#include "content/public/browser/browser_message_filter.h"
#include "content/public/common/three_d_api_types.h"
+#include "ipc/message_filter.h"
#include "media/audio/audio_parameters.h"
#include "media/base/channel_layout.h"
#include "net/cookies/canonical_cookie.h"
@@ -30,7 +32,6 @@
#include "ui/surface/transport_dib.h"
#if defined(OS_MACOSX)
-#include "base/mac/scoped_cftyperef.h"
#include "content/common/mac/font_loader.h"
#endif
@@ -51,17 +52,13 @@ class SharedMemory;
class TaskRunner;
}
-namespace gfx {
-class Rect;
-struct GpuMemoryBufferHandle;
-}
-
namespace media {
class AudioManager;
struct MediaLogEvent;
}
namespace net {
+class CookieStore;
class KeygenHandler;
class URLRequestContext;
class URLRequestContextGetter;
@@ -84,7 +81,6 @@ class RenderMessageFilter : public BrowserMessageFilter {
public:
// Create the filter.
RenderMessageFilter(int render_process_id,
- bool is_guest,
PluginServiceImpl * plugin_service,
BrowserContext* browser_context,
net::URLRequestContextGetter* request_context,
@@ -93,13 +89,12 @@ class RenderMessageFilter : public BrowserMessageFilter {
MediaInternals* media_internals,
DOMStorageContextWrapper* dom_storage_context);
- // IPC::ChannelProxy::MessageFilter methods:
+ // IPC::MessageFilter methods:
virtual void OnChannelClosing() OVERRIDE;
virtual void OnChannelConnected(int32 peer_pid) OVERRIDE;
// BrowserMessageFilter methods:
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
virtual void OnDestruct() const OVERRIDE;
virtual base::TaskRunner* OverrideTaskRunnerForMessage(
const IPC::Message& message) OVERRIDE;
@@ -108,10 +103,10 @@ class RenderMessageFilter : public BrowserMessageFilter {
int render_process_id() const { return render_process_id_; }
- // Returns the correct net::URLRequestContext depending on what type of url is
+ // Returns the correct net::CookieStore depending on what type of url is
// given.
// Only call on the IO thread.
- net::URLRequestContext* GetRequestContextForURL(const GURL& url);
+ net::CookieStore* GetCookieStoreForURL(const GURL& url);
private:
friend class BrowserThread;
@@ -134,11 +129,12 @@ class RenderMessageFilter : public BrowserMessageFilter {
void OnCreateFullscreenWidget(int opener_id,
int* route_id,
int* surface_id);
- void OnSetCookie(const IPC::Message& message,
+ void OnSetCookie(int render_frame_id,
const GURL& url,
const GURL& first_party_for_cookies,
const std::string& cookie);
- void OnGetCookies(const GURL& url,
+ void OnGetCookies(int render_frame_id,
+ const GURL& url,
const GURL& first_party_for_cookies,
IPC::Message* reply_msg);
void OnGetRawCookies(const GURL& url,
@@ -146,7 +142,8 @@ class RenderMessageFilter : public BrowserMessageFilter {
IPC::Message* reply_msg);
void OnDeleteCookie(const GURL& url,
const std::string& cookieName);
- void OnCookiesEnabled(const GURL& url,
+ void OnCookiesEnabled(int render_frame_id,
+ const GURL& url,
const GURL& first_party_for_cookies,
bool* cookies_enabled);
@@ -189,10 +186,11 @@ class RenderMessageFilter : public BrowserMessageFilter {
void OnOpenChannelToPpapiBroker(int routing_id,
const base::FilePath& path);
void OnGenerateRoutingID(int* route_id);
- void OnDownloadUrl(const IPC::Message& message,
+ void OnDownloadUrl(int render_view_id,
const GURL& url,
const Referrer& referrer,
- const base::string16& suggested_name);
+ const base::string16& suggested_name,
+ const bool use_prompt);
void OnCheckNotificationPermission(const GURL& source_origin,
int* permission_level);
@@ -211,6 +209,16 @@ class RenderMessageFilter : public BrowserMessageFilter {
// in the renderer on POSIX due to the sandbox.
void OnAllocateSharedMemory(uint32 buffer_size,
base::SharedMemoryHandle* handle);
+ void AllocateSharedBitmapOnFileThread(uint32 buffer_size,
+ const cc::SharedBitmapId& id,
+ IPC::Message* reply_msg);
+ void OnAllocateSharedBitmap(uint32 buffer_size,
+ const cc::SharedBitmapId& id,
+ IPC::Message* reply_msg);
+ void OnAllocatedSharedBitmap(size_t buffer_size,
+ const base::SharedMemoryHandle& handle,
+ const cc::SharedBitmapId& id);
+ void OnDeletedSharedBitmap(const cc::SharedBitmapId& id);
void OnResolveProxy(const GURL& url, IPC::Message* reply_msg);
// Browser side transport DIB allocation
@@ -230,7 +238,8 @@ class RenderMessageFilter : public BrowserMessageFilter {
void OnMediaLogEvents(const std::vector<media::MediaLogEvent>&);
// Check the policy for getting cookies. Gets the cookies if allowed.
- void CheckPolicyForCookies(const GURL& url,
+ void CheckPolicyForCookies(int render_frame_id,
+ const GURL& url,
const GURL& first_party_for_cookies,
IPC::Message* reply_msg,
const net::CookieList& cookie_list);
@@ -247,7 +256,6 @@ class RenderMessageFilter : public BrowserMessageFilter {
void OnCompletedOpenChannelToNpapiPlugin(
OpenChannelToNpapiPluginCallback* client);
- void OnUpdateIsDelayed(const IPC::Message& msg);
void OnAre3DAPIsBlocked(int render_view_id,
const GURL& top_origin_url,
ThreeDAPIType requester,
@@ -262,11 +270,6 @@ class RenderMessageFilter : public BrowserMessageFilter {
uint32_t data_size);
#endif
- void OnAllocateGpuMemoryBuffer(uint32 width,
- uint32 height,
- uint32 internalformat,
- gfx::GpuMemoryBufferHandle* handle);
-
// Cached resource request dispatcher host and plugin service, guaranteed to
// be non-null if Init succeeds. We do not own the objects, they are managed
// by the BrowserProcess, which has a wider scope than we do.
@@ -293,8 +296,6 @@ class RenderMessageFilter : public BrowserMessageFilter {
int render_process_id_;
- bool is_guest_;
-
std::set<OpenChannelToNpapiPluginCallback*> plugin_host_clients_;
// Records the last time we sampled CPU usage of the renderer process.
@@ -307,10 +308,6 @@ class RenderMessageFilter : public BrowserMessageFilter {
media::AudioManager* audio_manager_;
MediaInternals* media_internals_;
-#if defined(OS_MACOSX)
- base::ScopedCFTypeRef<CFTypeRef> last_io_surface_;
-#endif
-
DISALLOW_COPY_AND_ASSIGN(RenderMessageFilter);
};
diff --git a/chromium/content/browser/renderer_host/render_process_host_browsertest.cc b/chromium/content/browser/renderer_host/render_process_host_browsertest.cc
index 95cfcba2007..b77f20fa44b 100644
--- a/chromium/content/browser/renderer_host/render_process_host_browsertest.cc
+++ b/chromium/content/browser/renderer_host/render_process_host_browsertest.cc
@@ -4,14 +4,14 @@
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/common/child_process_messages.h"
-#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_process_host_observer.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
-#include "content/public/test/test_notification_tracker.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/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
namespace content {
@@ -29,7 +29,26 @@ int RenderProcessHostCount() {
return count;
}
-class RenderProcessHostTest : public ContentBrowserTest {};
+class RenderProcessHostTest : public ContentBrowserTest,
+ public RenderProcessHostObserver {
+ public:
+ RenderProcessHostTest() : process_exits_(0), host_destructions_(0) {}
+
+ protected:
+ // RenderProcessHostObserver:
+ virtual void RenderProcessExited(RenderProcessHost* host,
+ base::ProcessHandle handle,
+ base::TerminationStatus status,
+ int exit_code) OVERRIDE {
+ ++process_exits_;
+ }
+ virtual void RenderProcessHostDestroyed(RenderProcessHost* host) OVERRIDE {
+ ++host_destructions_;
+ }
+
+ int process_exits_;
+ int host_destructions_;
+};
// Sometimes the renderer process's ShutdownRequest (corresponding to the
// ViewMsg_WasSwappedOut from a previous navigation) doesn't arrive until after
@@ -45,9 +64,9 @@ IN_PROC_BROWSER_TEST_F(RenderProcessHostTest,
RenderProcessHost* rph =
shell()->web_contents()->GetRenderViewHost()->GetProcess();
- TestNotificationTracker termination_watcher;
- termination_watcher.ListenFor(NOTIFICATION_RENDERER_PROCESS_CLOSED,
- Source<RenderProcessHost>(rph));
+ host_destructions_ = 0;
+ process_exits_ = 0;
+ rph->AddObserver(this);
ChildProcessHostMsg_ShutdownRequest msg;
rph->OnMessageReceived(msg);
@@ -56,7 +75,9 @@ IN_PROC_BROWSER_TEST_F(RenderProcessHostTest,
// that time to happen.
NavigateToURL(CreateBrowser(), test_url);
- EXPECT_EQ(0U, termination_watcher.size());
+ EXPECT_EQ(0, process_exits_);
+ if (!host_destructions_)
+ rph->RemoveObserver(this);
}
IN_PROC_BROWSER_TEST_F(RenderProcessHostTest,
@@ -71,7 +92,8 @@ IN_PROC_BROWSER_TEST_F(RenderProcessHostTest,
RenderProcessHost* rph =
shell()->web_contents()->GetRenderViewHost()->GetProcess();
// Make it believe it's a guest.
- reinterpret_cast<RenderProcessHostImpl*>(rph)->SetIsGuestForTesting(true);
+ reinterpret_cast<RenderProcessHostImpl*>(rph)->
+ set_is_isolated_guest_for_testing(true);
EXPECT_EQ(1, RenderProcessHostCount());
// Navigate to a different page.
@@ -86,5 +108,89 @@ IN_PROC_BROWSER_TEST_F(RenderProcessHostTest,
EXPECT_EQ(2, RenderProcessHostCount());
}
+class ShellCloser : public RenderProcessHostObserver {
+ public:
+ ShellCloser(Shell* shell, std::string* logging_string)
+ : shell_(shell), logging_string_(logging_string) {}
+
+ protected:
+ // RenderProcessHostObserver:
+ virtual void RenderProcessExited(RenderProcessHost* host,
+ base::ProcessHandle handle,
+ base::TerminationStatus status,
+ int exit_code) OVERRIDE {
+ logging_string_->append("ShellCloser::RenderProcessExited ");
+ shell_->Close();
+ }
+
+ virtual void RenderProcessHostDestroyed(RenderProcessHost* host) OVERRIDE {
+ logging_string_->append("ShellCloser::RenderProcessHostDestroyed ");
+ }
+
+ Shell* shell_;
+ std::string* logging_string_;
+};
+
+class ObserverLogger : public RenderProcessHostObserver {
+ public:
+ explicit ObserverLogger(std::string* logging_string)
+ : logging_string_(logging_string), host_destroyed_(false) {}
+
+ bool host_destroyed() { return host_destroyed_; }
+
+ protected:
+ // RenderProcessHostObserver:
+ virtual void RenderProcessExited(RenderProcessHost* host,
+ base::ProcessHandle handle,
+ base::TerminationStatus status,
+ int exit_code) OVERRIDE {
+ logging_string_->append("ObserverLogger::RenderProcessExited ");
+ }
+
+ virtual void RenderProcessHostDestroyed(RenderProcessHost* host) OVERRIDE {
+ logging_string_->append("ObserverLogger::RenderProcessHostDestroyed ");
+ host_destroyed_ = true;
+ }
+
+ std::string* logging_string_;
+ bool host_destroyed_;
+};
+
+IN_PROC_BROWSER_TEST_F(RenderProcessHostTest,
+ AllProcessExitedCallsBeforeAnyHostDestroyedCalls) {
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+
+ GURL test_url = embedded_test_server()->GetURL("/simple_page.html");
+ NavigateToURL(shell(), test_url);
+
+ std::string logging_string;
+ ShellCloser shell_closer(shell(), &logging_string);
+ ObserverLogger observer_logger(&logging_string);
+ RenderProcessHost* rph =
+ shell()->web_contents()->GetRenderViewHost()->GetProcess();
+
+ // Ensure that the ShellCloser observer is first, so that it will have first
+ // dibs on the ProcessExited callback.
+ rph->AddObserver(&shell_closer);
+ rph->AddObserver(&observer_logger);
+
+ // This will crash the render process, and start all the callbacks.
+ NavigateToURL(shell(), GURL(kChromeUICrashURL));
+
+ // The key here is that all the RenderProcessExited callbacks precede all the
+ // RenderProcessHostDestroyed callbacks.
+ EXPECT_EQ("ShellCloser::RenderProcessExited "
+ "ObserverLogger::RenderProcessExited "
+ "ShellCloser::RenderProcessHostDestroyed "
+ "ObserverLogger::RenderProcessHostDestroyed ", logging_string);
+
+ // If the test fails, and somehow the RPH is still alive somehow, at least
+ // deregister the observers so that the test fails and doesn't also crash.
+ if (!observer_logger.host_destroyed()) {
+ rph->RemoveObserver(&shell_closer);
+ rph->RemoveObserver(&observer_logger);
+ }
+}
+
} // namespace
} // namespace content
diff --git a/chromium/content/browser/renderer_host/render_process_host_impl.cc b/chromium/content/browser/renderer_host/render_process_host_impl.cc
index 5d975e9119d..2eafd8c9c47 100644
--- a/chromium/content/browser/renderer_host/render_process_host_impl.cc
+++ b/chromium/content/browser/renderer_host/render_process_host_impl.cc
@@ -21,14 +21,16 @@
#include "base/callback.h"
#include "base/command_line.h"
#include "base/debug/trace_event.h"
+#include "base/files/file.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram.h"
+#include "base/numerics/safe_math.h"
#include "base/path_service.h"
-#include "base/platform_file.h"
#include "base/rand_util.h"
#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/supports_user_data.h"
#include "base/sys_info.h"
@@ -38,13 +40,14 @@
#include "cc/base/switches.h"
#include "content/browser/appcache/appcache_dispatcher_host.h"
#include "content/browser/appcache/chrome_appcache_service.h"
+#include "content/browser/battery_status/battery_status_message_filter.h"
+#include "content/browser/browser_child_process_host_impl.h"
#include "content/browser/browser_main.h"
#include "content/browser/browser_main_loop.h"
-#include "content/browser/browser_plugin/browser_plugin_geolocation_permission_context.h"
#include "content/browser/browser_plugin/browser_plugin_message_filter.h"
#include "content/browser/child_process_security_policy_impl.h"
-#include "content/browser/device_orientation/device_motion_message_filter.h"
-#include "content/browser/device_orientation/device_orientation_message_filter.h"
+#include "content/browser/device_sensors/device_motion_message_filter.h"
+#include "content/browser/device_sensors/device_orientation_message_filter.h"
#include "content/browser/dom_storage/dom_storage_context_wrapper.h"
#include "content/browser/dom_storage/dom_storage_message_filter.h"
#include "content/browser/download/mhtml_generation_manager.h"
@@ -60,12 +63,15 @@
#include "content/browser/indexed_db/indexed_db_dispatcher_host.h"
#include "content/browser/loader/resource_message_filter.h"
#include "content/browser/loader/resource_scheduler_filter.h"
-#include "content/browser/media/android/browser_demuxer_android.h"
+#include "content/browser/media/capture/audio_mirroring_manager.h"
#include "content/browser/media/media_internals.h"
+#include "content/browser/media/midi_host.h"
#include "content/browser/message_port_message_filter.h"
#include "content/browser/mime_registry_message_filter.h"
+#include "content/browser/mojo/mojo_application_host.h"
#include "content/browser/plugin_service_impl.h"
#include "content/browser/profiler_message_filter.h"
+#include "content/browser/push_messaging_message_filter.h"
#include "content/browser/quota_dispatcher_host.h"
#include "content/browser/renderer_host/clipboard_message_filter.h"
#include "content/browser/renderer_host/database_message_filter.h"
@@ -73,12 +79,9 @@
#include "content/browser/renderer_host/gamepad_browser_message_filter.h"
#include "content/browser/renderer_host/gpu_message_filter.h"
#include "content/browser/renderer_host/media/audio_input_renderer_host.h"
-#include "content/browser/renderer_host/media/audio_mirroring_manager.h"
#include "content/browser/renderer_host/media/audio_renderer_host.h"
#include "content/browser/renderer_host/media/device_request_message_filter.h"
#include "content/browser/renderer_host/media/media_stream_dispatcher_host.h"
-#include "content/browser/renderer_host/media/midi_dispatcher_host.h"
-#include "content/browser/renderer_host/media/midi_host.h"
#include "content/browser/renderer_host/media/peer_connection_tracker_host.h"
#include "content/browser/renderer_host/media/video_capture_host.h"
#include "content/browser/renderer_host/memory_benchmark_message_filter.h"
@@ -96,7 +99,7 @@
#include "content/browser/resolve_proxy_msg_helper.h"
#include "content/browser/service_worker/service_worker_context_wrapper.h"
#include "content/browser/service_worker/service_worker_dispatcher_host.h"
-#include "content/browser/speech/input_tag_speech_dispatcher_host.h"
+#include "content/browser/shared_worker/shared_worker_message_filter.h"
#include "content/browser/speech/speech_recognition_dispatcher_host.h"
#include "content/browser/storage_partition_impl.h"
#include "content/browser/streams/stream_context.h"
@@ -107,10 +110,13 @@
#include "content/browser/worker_host/worker_storage_partition.h"
#include "content/common/child_process_host_impl.h"
#include "content/common/child_process_messages.h"
+#include "content/common/content_switches_internal.h"
+#include "content/common/gpu/client/gpu_memory_buffer_impl.h"
+#include "content/common/gpu/client/gpu_memory_buffer_impl_shm.h"
#include "content/common/gpu/gpu_messages.h"
+#include "content/common/mojo/mojo_messages.h"
#include "content/common/resource_messages.h"
#include "content/common/view_messages.h"
-#include "content/port/browser/render_widget_host_view_frame_subscriber.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/notification_service.h"
@@ -119,41 +125,63 @@
#include "content/public/browser/render_process_host_observer.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/browser/render_widget_host_iterator.h"
+#include "content/public/browser/render_widget_host_view_frame_subscriber.h"
#include "content/public/browser/resource_context.h"
#include "content/public/browser/user_metrics.h"
+#include "content/public/browser/worker_service.h"
#include "content/public/common/content_constants.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/process_type.h"
#include "content/public/common/result_codes.h"
+#include "content/public/common/sandboxed_process_launcher_delegate.h"
#include "content/public/common/url_constants.h"
#include "gpu/command_buffer/service/gpu_switches.h"
#include "ipc/ipc_channel.h"
#include "ipc/ipc_logging.h"
-#include "ipc/ipc_platform_file.h"
#include "ipc/ipc_switches.h"
#include "media/base/media_switches.h"
+#include "mojo/common/common_type_converters.h"
#include "net/url_request/url_request_context_getter.h"
#include "ppapi/shared_impl/ppapi_switches.h"
+#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/ui_base_switches.h"
#include "ui/events/event_switches.h"
#include "ui/gfx/switches.h"
#include "ui/gl/gl_switches.h"
+#include "ui/native_theme/native_theme_switches.h"
#include "webkit/browser/fileapi/sandbox_file_system_backend.h"
#include "webkit/common/resource_type.h"
+#if defined(OS_ANDROID)
+#include "content/browser/media/android/browser_demuxer_android.h"
+#include "content/browser/renderer_host/compositor_impl_android.h"
+#include "content/common/gpu/client/gpu_memory_buffer_impl_surface_texture.h"
+#endif
+
+#if defined(OS_MACOSX)
+#include "content/common/gpu/client/gpu_memory_buffer_impl_io_surface.h"
+#endif
+
#if defined(OS_WIN)
+#include "base/strings/string_number_conversions.h"
#include "base/win/scoped_com_initializer.h"
#include "content/common/font_cache_dispatcher_win.h"
#include "content/common/sandbox_win.h"
-#include "content/public/common/sandboxed_process_launcher_delegate.h"
+#include "ui/gfx/win/dpi.h"
+#endif
+
+#if defined(OS_MACOSX)
+#include "content/public/common/sandbox_type_mac.h"
#endif
#if defined(ENABLE_WEBRTC)
+#include "content/browser/media/webrtc_internals.h"
+#include "content/browser/renderer_host/media/media_stream_track_metrics_host.h"
#include "content/browser/renderer_host/media/webrtc_identity_service_host.h"
+#include "content/common/media/aec_dump_messages.h"
+#include "content/common/media/media_stream_messages.h"
#endif
-#include "third_party/skia/include/core/SkBitmap.h"
-
extern bool g_exited_main_message_loop;
static const char* kSiteProcessMapKeyName = "content_site_process_map";
@@ -194,13 +222,33 @@ void GetContexts(
request.resource_type);
}
+#if defined(ENABLE_WEBRTC)
+// Creates a file used for diagnostic echo canceller recordings for handing
+// over to the renderer.
+IPC::PlatformFileForTransit CreateAecDumpFileForProcess(
+ base::FilePath file_path,
+ base::ProcessHandle process) {
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ base::File dump_file(file_path,
+ base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_APPEND);
+ if (!dump_file.IsValid()) {
+ VLOG(1) << "Could not open AEC dump file, error=" <<
+ dump_file.error_details();
+ return IPC::InvalidPlatformFileForTransit();
+ }
+ return IPC::TakeFileHandleForProcess(dump_file.Pass(), process);
+}
+
+// Does nothing. Just to avoid races between enable and disable.
+void DisableAecDumpOnFileThread() {
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+}
+#endif
+
// the global list of all renderer processes
base::LazyInstance<IDMap<RenderProcessHost> >::Leaky
g_all_hosts = LAZY_INSTANCE_INITIALIZER;
-base::LazyInstance<scoped_refptr<BrowserPluginGeolocationPermissionContext> >
- g_browser_plugin_geolocation_context = LAZY_INSTANCE_INITIALIZER;
-
// Map of site to process, to ensure we only have one RenderProcessHost per
// site in process-per-site mode. Each map is specific to a BrowserContext.
class SiteProcessMap : public base::SupportsUserData::Data {
@@ -256,39 +304,89 @@ SiteProcessMap* GetSiteProcessMapForBrowserContext(BrowserContext* context) {
return map;
}
-#if defined(OS_WIN)
// NOTE: changes to this class need to be reviewed by the security team.
class RendererSandboxedProcessLauncherDelegate
: public content::SandboxedProcessLauncherDelegate {
public:
- RendererSandboxedProcessLauncherDelegate() {}
- virtual ~RendererSandboxedProcessLauncherDelegate() {}
+ RendererSandboxedProcessLauncherDelegate(IPC::ChannelProxy* channel)
+#if defined(OS_POSIX)
+ : ipc_fd_(channel->TakeClientFileDescriptor())
+#endif // OS_POSIX
+ {}
- virtual void ShouldSandbox(bool* in_sandbox) OVERRIDE {
-#if !defined (GOOGLE_CHROME_BUILD)
- if (CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kInProcessPlugins)) {
- *in_sandbox = false;
- }
-#endif
- }
+ virtual ~RendererSandboxedProcessLauncherDelegate() {}
+#if defined(OS_WIN)
virtual void PreSpawnTarget(sandbox::TargetPolicy* policy,
bool* success) {
AddBaseHandleClosePolicy(policy);
GetContentClient()->browser()->PreSpawnRenderer(policy, success);
}
-};
+
+#elif defined(OS_POSIX)
+ virtual bool ShouldUseZygote() OVERRIDE {
+ const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
+ CommandLine::StringType renderer_prefix =
+ browser_command_line.GetSwitchValueNative(switches::kRendererCmdPrefix);
+ return renderer_prefix.empty();
+ }
+ virtual int GetIpcFd() OVERRIDE {
+ return ipc_fd_;
+ }
+#if defined(OS_MACOSX)
+ virtual SandboxType GetSandboxType() OVERRIDE {
+ return SANDBOX_TYPE_RENDERER;
+ }
+#endif
#endif // OS_WIN
-} // namespace
+ private:
+#if defined(OS_POSIX)
+ int ipc_fd_;
+#endif // OS_POSIX
+};
-RendererMainThreadFactoryFunction g_renderer_main_thread_factory = NULL;
+#if defined(OS_MACOSX)
+void AddBooleanValue(CFMutableDictionaryRef dictionary,
+ const CFStringRef key,
+ bool value) {
+ CFDictionaryAddValue(
+ dictionary, key, value ? kCFBooleanTrue : kCFBooleanFalse);
+}
-void RenderProcessHost::RegisterRendererMainThreadFactory(
- RendererMainThreadFactoryFunction create) {
- g_renderer_main_thread_factory = create;
+void AddIntegerValue(CFMutableDictionaryRef dictionary,
+ const CFStringRef key,
+ int32 value) {
+ base::ScopedCFTypeRef<CFNumberRef> number(
+ CFNumberCreate(NULL, kCFNumberSInt32Type, &value));
+ CFDictionaryAddValue(dictionary, key, number.get());
}
+#endif
+
+const char kSessionStorageHolderKey[] = "kSessionStorageHolderKey";
+
+class SessionStorageHolder : public base::SupportsUserData::Data {
+ public:
+ SessionStorageHolder() {}
+ virtual ~SessionStorageHolder() {}
+
+ void Hold(const SessionStorageNamespaceMap& sessions, int view_route_id) {
+ session_storage_namespaces_awaiting_close_[view_route_id] = sessions;
+ }
+
+ void Release(int old_route_id) {
+ session_storage_namespaces_awaiting_close_.erase(old_route_id);
+ }
+
+ private:
+ std::map<int, SessionStorageNamespaceMap >
+ session_storage_namespaces_awaiting_close_;
+ DISALLOW_COPY_AND_ASSIGN(SessionStorageHolder);
+};
+
+} // namespace
+
+RendererMainThreadFactoryFunction g_renderer_main_thread_factory = NULL;
base::MessageLoop* g_in_process_thread;
@@ -351,30 +449,29 @@ void RenderProcessHost::SetMaxRendererProcessCount(size_t count) {
RenderProcessHostImpl::RenderProcessHostImpl(
BrowserContext* browser_context,
StoragePartitionImpl* storage_partition_impl,
- bool supports_browser_plugin,
- bool is_guest)
- : fast_shutdown_started_(false),
- deleting_soon_(false),
+ bool is_isolated_guest)
+ : fast_shutdown_started_(false),
+ deleting_soon_(false),
#ifndef NDEBUG
- is_self_deleted_(false),
+ is_self_deleted_(false),
#endif
- pending_views_(0),
- visible_widgets_(0),
- backgrounded_(true),
- cached_dibs_cleaner_(
- FROM_HERE, base::TimeDelta::FromSeconds(5),
- this, &RenderProcessHostImpl::ClearTransportDIBCache),
- is_initialized_(false),
- id_(ChildProcessHostImpl::GenerateChildProcessUniqueId()),
- browser_context_(browser_context),
- storage_partition_impl_(storage_partition_impl),
- sudden_termination_allowed_(true),
- ignore_input_events_(false),
- supports_browser_plugin_(supports_browser_plugin),
- is_guest_(is_guest),
- gpu_observer_registered_(false),
- power_monitor_broadcaster_(this),
- geolocation_dispatcher_host_(NULL) {
+ pending_views_(0),
+ mojo_activation_required_(false),
+ visible_widgets_(0),
+ backgrounded_(true),
+ is_initialized_(false),
+ id_(ChildProcessHostImpl::GenerateChildProcessUniqueId()),
+ browser_context_(browser_context),
+ storage_partition_impl_(storage_partition_impl),
+ sudden_termination_allowed_(true),
+ ignore_input_events_(false),
+ is_isolated_guest_(is_isolated_guest),
+ gpu_observer_registered_(false),
+ delayed_cleanup_needed_(false),
+ within_process_died_observer_(false),
+ power_monitor_broadcaster_(this),
+ worker_ref_count_(0),
+ weak_factory_(this) {
widget_helper_ = new RenderWidgetHelper();
ChildProcessSecurityPolicyImpl::GetInstance()->Add(GetID());
@@ -424,12 +521,21 @@ void RenderProcessHostImpl::ShutDownInProcessRenderer() {
}
}
+void RenderProcessHostImpl::RegisterRendererMainThreadFactory(
+ RendererMainThreadFactoryFunction create) {
+ g_renderer_main_thread_factory = create;
+}
+
RenderProcessHostImpl::~RenderProcessHostImpl() {
#ifndef NDEBUG
DCHECK(is_self_deleted_)
<< "RenderProcessHostImpl is destroyed by something other than itself";
#endif
+ // Make sure to clean up the in-process renderer before the channel, otherwise
+ // it may still run and have its IPCs fail, causing asserts.
+ in_process_renderer_.reset();
+
ChildProcessSecurityPolicyImpl::GetInstance()->Remove(GetID());
if (gpu_observer_registered_) {
@@ -444,7 +550,6 @@ RenderProcessHostImpl::~RenderProcessHostImpl() {
queued_messages_.pop();
}
- ClearTransportDIBCache();
UnregisterHost(GetID());
if (!CommandLine::ForCurrentProcess()->HasSwitch(
@@ -452,6 +557,10 @@ RenderProcessHostImpl::~RenderProcessHostImpl() {
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&RemoveShaderInfo, GetID()));
}
+
+#if defined(OS_ANDROID)
+ CompositorImpl::DestroyAllSurfaceTextures(GetID());
+#endif
}
void RenderProcessHostImpl::EnableSendQueue() {
@@ -489,15 +598,18 @@ bool RenderProcessHostImpl::Init() {
// Setup the IPC channel.
const std::string channel_id =
IPC::Channel::GenerateVerifiedChannelID(std::string());
- channel_.reset(
- new IPC::ChannelProxy(channel_id,
- IPC::Channel::MODE_SERVER,
- this,
- BrowserThread::GetMessageLoopProxyForThread(
- BrowserThread::IO).get()));
+ channel_ = IPC::ChannelProxy::Create(
+ channel_id,
+ IPC::Channel::MODE_SERVER,
+ this,
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO).get());
+
+ // Setup the Mojo channel.
+ mojo_application_host_.reset(new MojoApplicationHost());
+ mojo_application_host_->Init();
// Call the embedder first so that their IPC filters have priority.
- GetContentClient()->browser()->RenderProcessHostCreated(this);
+ GetContentClient()->browser()->RenderProcessWillLaunch(this);
CreateMessageFilters();
@@ -538,13 +650,7 @@ bool RenderProcessHostImpl::Init() {
// As long as there's no renderer prefix, we can use the zygote process
// at this stage.
child_process_launcher_.reset(new ChildProcessLauncher(
-#if defined(OS_WIN)
- new RendererSandboxedProcessLauncherDelegate,
-#elif defined(OS_POSIX)
- renderer_prefix.empty(),
- base::EnvironmentMap(),
- channel_->TakeClientFileDescriptor(),
-#endif
+ new RendererSandboxedProcessLauncherDelegate(channel_.get()),
cmd_line,
GetID(),
this));
@@ -561,24 +667,35 @@ bool RenderProcessHostImpl::Init() {
return true;
}
+void RenderProcessHostImpl::MaybeActivateMojo() {
+ // TODO(darin): Following security review, we can unconditionally initialize
+ // Mojo in all renderers. We will then be able to directly call Activate()
+ // from OnProcessLaunched.
+ if (!mojo_activation_required_)
+ return; // Waiting on someone to require Mojo.
+
+ if (!GetHandle())
+ return; // Waiting on renderer startup.
+
+ if (!mojo_application_host_->did_activate())
+ mojo_application_host_->Activate(this, GetHandle());
+}
+
void RenderProcessHostImpl::CreateMessageFilters() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
AddFilter(new ResourceSchedulerFilter(GetID()));
- MediaInternals* media_internals = MediaInternals::GetInstance();;
+ MediaInternals* media_internals = MediaInternals::GetInstance();
media::AudioManager* audio_manager =
BrowserMainLoop::GetInstance()->audio_manager();
// Add BrowserPluginMessageFilter to ensure it gets the first stab at messages
// from guests.
- if (supports_browser_plugin_) {
- scoped_refptr<BrowserPluginMessageFilter> bp_message_filter(
- new BrowserPluginMessageFilter(GetID(), IsGuest()));
- AddFilter(bp_message_filter.get());
- }
+ scoped_refptr<BrowserPluginMessageFilter> bp_message_filter(
+ new BrowserPluginMessageFilter(GetID()));
+ AddFilter(bp_message_filter.get());
scoped_refptr<RenderMessageFilter> render_message_filter(
new RenderMessageFilter(
GetID(),
- IsGuest(),
#if defined(ENABLE_PLUGINS)
PluginServiceImpl::GetInstance(),
#else
@@ -610,6 +727,7 @@ void RenderProcessHostImpl::CreateMessageFilters() {
storage_partition_impl_->GetAppCacheService(),
ChromeBlobStorageContext::GetFor(browser_context),
storage_partition_impl_->GetFileSystemContext(),
+ storage_partition_impl_->GetServiceWorkerContext(),
get_contexts_callback);
AddFilter(resource_message_filter);
@@ -630,8 +748,7 @@ void RenderProcessHostImpl::CreateMessageFilters() {
media_stream_manager);
AddFilter(audio_renderer_host_);
AddFilter(
- new MIDIHost(GetID(), BrowserMainLoop::GetInstance()->midi_manager()));
- AddFilter(new MIDIDispatcherHost(GetID(), browser_context));
+ new MidiHost(GetID(), BrowserMainLoop::GetInstance()->midi_manager()));
AddFilter(new VideoCaptureHost(media_stream_manager));
AddFilter(new AppCacheDispatcherHost(
storage_partition_impl_->GetAppCacheService(),
@@ -641,26 +758,11 @@ void RenderProcessHostImpl::CreateMessageFilters() {
GetID(),
storage_partition_impl_->GetDOMStorageContext()));
AddFilter(new IndexedDBDispatcherHost(
- storage_partition_impl_->GetIndexedDBContext()));
-
- scoped_refptr<ServiceWorkerDispatcherHost> service_worker_filter =
- new ServiceWorkerDispatcherHost(GetID());
- service_worker_filter->Init(
- storage_partition_impl_->GetServiceWorkerContext());
- AddFilter(service_worker_filter);
+ GetID(),
+ storage_partition_impl_->GetURLRequestContext(),
+ storage_partition_impl_->GetIndexedDBContext(),
+ ChromeBlobStorageContext::GetFor(browser_context)));
- if (IsGuest()) {
- if (!g_browser_plugin_geolocation_context.Get().get()) {
- g_browser_plugin_geolocation_context.Get() =
- new BrowserPluginGeolocationPermissionContext();
- }
- geolocation_dispatcher_host_ = GeolocationDispatcherHost::New(
- GetID(), g_browser_plugin_geolocation_context.Get().get());
- } else {
- geolocation_dispatcher_host_ = GeolocationDispatcherHost::New(
- GetID(), browser_context->GetGeolocationPermissionContext());
- }
- AddFilter(geolocation_dispatcher_host_);
gpu_message_filter_ = new GpuMessageFilter(GetID(), widget_helper_.get());
AddFilter(gpu_message_filter_);
#if defined(ENABLE_WEBRTC)
@@ -671,19 +773,17 @@ void RenderProcessHostImpl::CreateMessageFilters() {
AddFilter(new MediaStreamDispatcherHost(
GetID(),
browser_context->GetResourceContext()->GetMediaDeviceIDSalt(),
- media_stream_manager));
- AddFilter(
- new DeviceRequestMessageFilter(resource_context, media_stream_manager));
+ media_stream_manager,
+ resource_context));
+ AddFilter(new DeviceRequestMessageFilter(
+ resource_context, media_stream_manager, GetID()));
+ AddFilter(new MediaStreamTrackMetricsHost());
#endif
#if defined(ENABLE_PLUGINS)
AddFilter(new PepperRendererConnection(GetID()));
#endif
-#if defined(ENABLE_INPUT_SPEECH)
- AddFilter(new InputTagSpeechDispatcherHost(
- IsGuest(), GetID(), storage_partition_impl_->GetURLRequestContext()));
-#endif
AddFilter(new SpeechRecognitionDispatcherHost(
- IsGuest(), GetID(), storage_partition_impl_->GetURLRequestContext()));
+ GetID(), storage_partition_impl_->GetURLRequestContext()));
AddFilter(new FileAPIMessageFilter(
GetID(),
storage_partition_impl_->GetURLRequestContext(),
@@ -697,6 +797,10 @@ void RenderProcessHostImpl::CreateMessageFilters() {
#if defined(OS_MACOSX)
AddFilter(new TextInputClientMessageFilter(GetID()));
#elif defined(OS_WIN)
+ // The FontCacheDispatcher is required only when we're using GDI rendering.
+ // TODO(scottmg): pdf/ppapi still require the renderer to be able to precache
+ // GDI fonts (http://crbug.com/383227), even when using DirectWrite. This
+ // should eventually be if (!ShouldUseDirectWrite()) guarded.
channel_->AddFilter(new FontCacheDispatcher());
#elif defined(OS_ANDROID)
browser_demuxer_android_ = new BrowserDemuxerAndroid();
@@ -718,30 +822,57 @@ void RenderProcessHostImpl::CreateMessageFilters() {
base::Bind(&GetRequestContext, request_context,
media_request_context, ResourceType::SUB_RESOURCE));
- AddFilter(new WebSocketDispatcherHost(websocket_request_context_callback));
+ AddFilter(
+ new WebSocketDispatcherHost(GetID(), websocket_request_context_callback));
message_port_message_filter_ = new MessagePortMessageFilter(
base::Bind(&RenderWidgetHelper::GetNextRoutingID,
base::Unretained(widget_helper_.get())));
AddFilter(message_port_message_filter_);
- AddFilter(new WorkerMessageFilter(
- GetID(),
- resource_context,
- WorkerStoragePartition(
- storage_partition_impl_->GetURLRequestContext(),
- storage_partition_impl_->GetMediaURLRequestContext(),
- storage_partition_impl_->GetAppCacheService(),
- storage_partition_impl_->GetQuotaManager(),
- storage_partition_impl_->GetFileSystemContext(),
- storage_partition_impl_->GetDatabaseTracker(),
- storage_partition_impl_->GetIndexedDBContext()),
- message_port_message_filter_));
+ scoped_refptr<ServiceWorkerDispatcherHost> service_worker_filter =
+ new ServiceWorkerDispatcherHost(GetID(), message_port_message_filter_);
+ service_worker_filter->Init(
+ storage_partition_impl_->GetServiceWorkerContext());
+ AddFilter(service_worker_filter);
+
+ // If "--enable-embedded-shared-worker" is set, we use
+ // SharedWorkerMessageFilter in stead of WorkerMessageFilter.
+ if (WorkerService::EmbeddedSharedWorkerEnabled()) {
+ AddFilter(new SharedWorkerMessageFilter(
+ GetID(),
+ resource_context,
+ WorkerStoragePartition(
+ storage_partition_impl_->GetURLRequestContext(),
+ storage_partition_impl_->GetMediaURLRequestContext(),
+ storage_partition_impl_->GetAppCacheService(),
+ storage_partition_impl_->GetQuotaManager(),
+ storage_partition_impl_->GetFileSystemContext(),
+ storage_partition_impl_->GetDatabaseTracker(),
+ storage_partition_impl_->GetIndexedDBContext(),
+ storage_partition_impl_->GetServiceWorkerContext()),
+ message_port_message_filter_));
+ } else {
+ AddFilter(new WorkerMessageFilter(
+ GetID(),
+ resource_context,
+ WorkerStoragePartition(
+ storage_partition_impl_->GetURLRequestContext(),
+ storage_partition_impl_->GetMediaURLRequestContext(),
+ storage_partition_impl_->GetAppCacheService(),
+ storage_partition_impl_->GetQuotaManager(),
+ storage_partition_impl_->GetFileSystemContext(),
+ storage_partition_impl_->GetDatabaseTracker(),
+ storage_partition_impl_->GetIndexedDBContext(),
+ storage_partition_impl_->GetServiceWorkerContext()),
+ message_port_message_filter_));
+ }
#if defined(ENABLE_WEBRTC)
- AddFilter(new P2PSocketDispatcherHost(
+ p2p_socket_dispatcher_host_ = new P2PSocketDispatcherHost(
resource_context,
- browser_context->GetRequestContextForRenderProcess(GetID())));
+ browser_context->GetRequestContextForRenderProcess(GetID()));
+ AddFilter(p2p_socket_dispatcher_host_);
#endif
AddFilter(new TraceMessageFilter());
@@ -762,6 +893,8 @@ void RenderProcessHostImpl::CreateMessageFilters() {
AddFilter(new MemoryBenchmarkMessageFilter());
#endif
AddFilter(new VibrationMessageFilter());
+ AddFilter(new PushMessagingMessageFilter(GetID()));
+ AddFilter(new BatteryStatusMessageFilter());
}
int RenderProcessHostImpl::GetNextRoutingID() {
@@ -774,6 +907,10 @@ void RenderProcessHostImpl::ResumeDeferredNavigation(
widget_helper_->ResumeDeferredNavigation(request_id);
}
+void RenderProcessHostImpl::NotifyTimezoneChange() {
+ Send(new ViewMsg_TimezoneChange());
+}
+
void RenderProcessHostImpl::AddRoute(
int32 routing_id,
IPC::Listener* listener) {
@@ -865,14 +1002,39 @@ int RenderProcessHostImpl::VisibleWidgetCount() const {
return visible_widgets_;
}
-bool RenderProcessHostImpl::IsGuest() const {
- return is_guest_;
+bool RenderProcessHostImpl::IsIsolatedGuest() const {
+ return is_isolated_guest_;
}
StoragePartition* RenderProcessHostImpl::GetStoragePartition() const {
return storage_partition_impl_;
}
+static void AppendCompositorCommandLineFlags(CommandLine* command_line) {
+ if (IsPinchVirtualViewportEnabled())
+ command_line->AppendSwitch(cc::switches::kEnablePinchVirtualViewport);
+
+ if (IsThreadedCompositingEnabled())
+ command_line->AppendSwitch(switches::kEnableThreadedCompositing);
+
+ if (IsDelegatedRendererEnabled())
+ command_line->AppendSwitch(switches::kEnableDelegatedRenderer);
+
+ if (IsImplSidePaintingEnabled())
+ command_line->AppendSwitch(switches::kEnableImplSidePainting);
+
+ if (content::IsGpuRasterizationEnabled())
+ command_line->AppendSwitch(switches::kEnableGpuRasterization);
+
+ if (content::IsForceGpuRasterizationEnabled())
+ command_line->AppendSwitch(switches::kForceGpuRasterization);
+
+ // Appending disable-gpu-feature switches due to software rendering list.
+ GpuDataManagerImpl* gpu_data_manager = GpuDataManagerImpl::GetInstance();
+ DCHECK(gpu_data_manager);
+ gpu_data_manager->AppendRendererCommandLine(command_line);
+}
+
void RenderProcessHostImpl::AppendRendererCommandLine(
CommandLine* command_line) const {
// Pass the process type first, so it shows first in process listings.
@@ -898,22 +1060,18 @@ void RenderProcessHostImpl::AppendRendererCommandLine(
field_trial_states);
}
- if (content::IsThreadedCompositingEnabled())
- command_line->AppendSwitch(switches::kEnableThreadedCompositing);
-
- if (content::IsDelegatedRendererEnabled())
- command_line->AppendSwitch(switches::kEnableDelegatedRenderer);
-
- if (content::IsDeadlineSchedulingEnabled())
- command_line->AppendSwitch(switches::kEnableDeadlineScheduling);
-
GetContentClient()->browser()->AppendExtraCommandLineSwitches(
command_line, GetID());
- // Appending disable-gpu-feature switches due to software rendering list.
- GpuDataManagerImpl* gpu_data_manager = GpuDataManagerImpl::GetInstance();
- DCHECK(gpu_data_manager);
- gpu_data_manager->AppendRendererCommandLine(command_line);
+ if (content::IsPinchToZoomEnabled())
+ command_line->AppendSwitch(switches::kEnablePinch);
+
+#if defined(OS_WIN)
+ command_line->AppendSwitchASCII(switches::kDeviceScaleFactor,
+ base::DoubleToString(gfx::GetDPIScale()));
+#endif
+
+ AppendCompositorCommandLineFlags(command_line);
}
void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer(
@@ -922,116 +1080,102 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer(
// Propagate the following switches to the renderer command line (along
// with any associated values) if present in the browser command line.
static const char* const kSwitchNames[] = {
+ switches::kAllowInsecureWebSocketFromHttpsOrigin,
+ switches::kAllowLoopbackInPeerConnection,
switches::kAudioBufferSize,
switches::kAuditAllHandles,
switches::kAuditHandles,
+ switches::kBlinkPlatformLogChannels,
switches::kBlockCrossSiteDocuments,
switches::kDefaultTileWidth,
switches::kDefaultTileHeight,
switches::kDisable3DAPIs,
- switches::kDisableAcceleratedCompositing,
switches::kDisableAcceleratedFixedRootBackground,
- switches::kDisableAcceleratedScrollableFrames,
+ switches::kDisableAcceleratedOverflowScroll,
switches::kDisableAcceleratedVideoDecode,
switches::kDisableApplicationCache,
- switches::kDisableAudio,
switches::kDisableBreakpad,
- switches::kDisableCompositedScrollingForFrames,
switches::kDisableCompositingForFixedPosition,
switches::kDisableCompositingForTransition,
switches::kDisableDatabases,
- switches::kDisableDeadlineScheduling,
- switches::kDisableDelegatedRenderer,
switches::kDisableDesktopNotifications,
- switches::kDisableDeviceMotion,
- switches::kDisableDeviceOrientation,
switches::kDisableDirectNPAPIRequests,
+ switches::kDisableDistanceFieldText,
+ switches::kDisableFastTextAutosizing,
switches::kDisableFileSystem,
- switches::kDisableFiltersOverIPC,
- switches::kDisableFullScreen,
- switches::kDisableGeolocation,
- switches::kDisableGpu,
switches::kDisableGpuCompositing,
switches::kDisableGpuVsync,
+ switches::kDisableLowResTiling,
switches::kDisableHistogramCustomizer,
+ switches::kDisableLCDText,
switches::kDisableLayerSquashing,
switches::kDisableLocalStorage,
switches::kDisableLogging,
- switches::kDisableOpusPlayback,
+ switches::kDisableMediaSource,
+ switches::kDisableOverlayScrollbar,
switches::kDisablePinch,
switches::kDisablePrefixedEncryptedMedia,
+ switches::kDisableRepaintAfterLayout,
switches::kDisableSeccompFilterSandbox,
switches::kDisableSessionStorage,
switches::kDisableSharedWorkers,
- switches::kDisableSpeechInput,
- switches::kDisableThreadedCompositing,
switches::kDisableTouchAdjustment,
switches::kDisableTouchDragDrop,
switches::kDisableTouchEditing,
- switches::kDisableUniversalAcceleratedOverflowScroll,
- switches::kDisableUnprefixedMediaSource,
- switches::kDisableVp8AlphaPlayback,
- switches::kDisableWebAnimationsCSS,
- switches::kDisableWebKitMediaSource,
+ switches::kDisableZeroCopy,
switches::kDomAutomationController,
switches::kEnableAcceleratedFixedRootBackground,
switches::kEnableAcceleratedOverflowScroll,
- switches::kEnableAcceleratedScrollableFrames,
- switches::kEnableAccessibilityLogging,
switches::kEnableBeginFrameScheduling,
- switches::kEnableBrowserPluginForAllViewTypes,
- switches::kEnableCompositedScrollingForFrames,
+ switches::kEnableBleedingEdgeRenderingFastPaths,
switches::kEnableCompositingForFixedPosition,
switches::kEnableCompositingForTransition,
- switches::kEnableDCHECK,
- switches::kEnableDeadlineScheduling,
switches::kEnableDeferredImageDecoding,
- switches::kEnableDelegatedRenderer,
- switches::kEnableEac3Playback,
+ switches::kEnableDistanceFieldText,
switches::kEnableEncryptedMedia,
switches::kEnableExperimentalCanvasFeatures,
switches::kEnableExperimentalWebPlatformFeatures,
- switches::kEnableExperimentalWebSocket,
switches::kEnableFastTextAutosizing,
- switches::kEnableGpuBenchmarking,
switches::kEnableGPUClientLogging,
switches::kEnableGpuClientTracing,
switches::kEnableGPUServiceLogging,
switches::kEnableHighDpiCompositingForFixedPosition,
- switches::kEnableHTMLImports,
+ switches::kEnableLowResTiling,
switches::kEnableInbandTextTracks,
- switches::kEnableInputModeAttribute,
+ switches::kEnableLCDText,
switches::kEnableLayerSquashing,
switches::kEnableLogging,
- switches::kEnableMP3StreamParser,
switches::kEnableMemoryBenchmarking,
+ switches::kEnableOneCopy,
switches::kEnableOverlayFullscreenVideo,
- switches::kEnableOverlayScrollbars,
+ switches::kEnableOverlayScrollbar,
switches::kEnableOverscrollNotifications,
switches::kEnablePinch,
+ switches::kEnablePreciseMemoryInfo,
switches::kEnablePreparsedJsCaching,
- switches::kEnablePruneGpuCommandBuffers,
switches::kEnableRepaintAfterLayout,
+ switches::kEnableSeccompFilterSandbox,
switches::kEnableServiceWorker,
switches::kEnableSkiaBenchmarking,
- switches::kEnableSoftwareCompositing,
switches::kEnableSpeechSynthesis,
switches::kEnableStatsTable,
switches::kEnableStrictSiteIsolation,
- switches::kEnableThreadedCompositing,
- switches::kEnableUniversalAcceleratedOverflowScroll,
+ switches::kEnableTargetedStyleRecalc,
switches::kEnableTouchDragDrop,
switches::kEnableTouchEditing,
switches::kEnableViewport,
switches::kEnableViewportMeta,
switches::kMainFrameResizesAreOrientationChanges,
switches::kEnableVtune,
- switches::kEnableWebAnimationsCSS,
switches::kEnableWebAnimationsSVG,
switches::kEnableWebGLDraftExtensions,
+ switches::kEnableWebGLImageChromium,
switches::kEnableWebMIDI,
+ switches::kEnableZeroCopy,
switches::kForceDeviceScaleFactor,
switches::kFullMemoryCrashReport,
+ switches::kIgnoreResolutionLimitsForAcceleratedVideoDecode,
+ switches::kIPCConnectionTimeout,
switches::kJavaScriptFlags,
switches::kLoggingLevel,
switches::kMaxUntiledLayerWidth,
@@ -1039,6 +1183,7 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer(
switches::kMemoryMetrics,
switches::kNoReferrers,
switches::kNoSandbox,
+ switches::kNumRasterThreads,
switches::kPpapiInProcess,
switches::kProfilerTiming,
switches::kReduceSecurityForTesting,
@@ -1048,40 +1193,30 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer(
switches::kShowPaintRects,
switches::kSitePerProcess,
switches::kStatsCollectionController,
- switches::kTestSandbox,
+ switches::kTestType,
switches::kTouchEvents,
switches::kTraceToConsole,
+ switches::kUseDiscardableMemory,
// This flag needs to be propagated to the renderer process for
// --in-process-webgl.
switches::kUseGL,
switches::kUseMobileUserAgent,
- switches::kUserAgent,
switches::kV,
switches::kVideoThreads,
switches::kVModule,
- switches::kWebCoreLogChannels,
- switches::kWebGLCommandBufferSizeKb,
// Please keep these in alphabetical order. Compositor switches here should
// also be added to chrome/browser/chromeos/login/chrome_restart_request.cc.
- cc::switches::kBackgroundColorInsteadOfCheckerboard,
cc::switches::kCompositeToMailbox,
cc::switches::kDisableCompositedAntialiasing,
cc::switches::kDisableCompositorTouchHitTesting,
- cc::switches::kDisableImplSidePainting,
- cc::switches::kDisableLCDText,
- cc::switches::kDisableMapImage,
+ cc::switches::kDisableMainFrameBeforeActivation,
+ cc::switches::kDisableMainFrameBeforeDraw,
cc::switches::kDisableThreadedAnimation,
- cc::switches::kEnableGPURasterization,
- cc::switches::kEnableImplSidePainting,
- cc::switches::kEnableLCDText,
- cc::switches::kEnableMapImage,
- cc::switches::kEnablePartialSwap,
- cc::switches::kEnablePerTilePainting,
- cc::switches::kEnablePinchVirtualViewport,
+ cc::switches::kEnableGpuBenchmarking,
+ cc::switches::kEnableMainFrameBeforeActivation,
cc::switches::kEnableTopControlsPositionCalculation,
cc::switches::kMaxTilesForInterestArea,
cc::switches::kMaxUnusedResourceMemoryUsagePercentage,
- cc::switches::kNumRasterThreads,
cc::switches::kShowCompositedLayerBorders,
cc::switches::kShowFPSCounter,
cc::switches::kShowLayerAnimationBounds,
@@ -1096,28 +1231,15 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer(
cc::switches::kTopControlsHeight,
cc::switches::kTopControlsHideThreshold,
cc::switches::kTopControlsShowThreshold,
- cc::switches::kTraceOverdraw,
#if defined(ENABLE_PLUGINS)
switches::kEnablePepperTesting,
- switches::kDisablePepper3d,
#endif
#if defined(ENABLE_WEBRTC)
- switches::kEnableAudioTrackProcessing,
+ switches::kDisableAudioTrackProcessing,
switches::kDisableDeviceEnumeration,
- switches::kDisableSCTPDataChannels,
switches::kDisableWebRtcHWDecoding,
switches::kDisableWebRtcHWEncoding,
- switches::kEnableWebRtcAecRecordings,
switches::kEnableWebRtcHWVp8Encoding,
- switches::kEnableWebRtcTcpServerSocket,
-#endif
-#if !defined (GOOGLE_CHROME_BUILD)
- // These are unsupported and not fully tested modes, so don't enable them
- // for official Google Chrome builds.
- switches::kInProcessPlugins,
-#endif // GOOGLE_CHROME_BUILD
-#if defined(GOOGLE_TV)
- switches::kUseExternalVideoSurfaceThresholdInPixels,
#endif
#if defined(OS_ANDROID)
switches::kDisableGestureRequirementForMediaPlayback,
@@ -1125,26 +1247,16 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer(
switches::kDisableWebRTC,
switches::kEnableLowEndDeviceMode,
switches::kEnableSpeechRecognition,
- switches::kHideScrollbars,
switches::kMediaDrmEnableNonCompositing,
switches::kNetworkCountryIso,
-#endif
-#if defined(OS_ANDROID) && defined(ARCH_CPU_X86)
- switches::kEnableWebAudio,
-#else
- // Need to be able to disable webaudio on other platforms where it
- // is enabled by default.
switches::kDisableWebAudio,
#endif
#if defined(OS_MACOSX)
// Allow this to be set when invoking the browser and relayed along.
switches::kEnableSandboxLogging,
#endif
-#if defined(OS_POSIX)
- switches::kChildCleanExit,
-#endif
#if defined(OS_WIN)
- switches::kEnableDirectWrite,
+ switches::kDisableDirectWrite,
switches::kEnableHighResolutionTime,
#endif
};
@@ -1164,13 +1276,10 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer(
if (GetBrowserContext()->IsOffTheRecord() &&
!browser_cmd.HasSwitch(switches::kDisableDatabases)) {
renderer_cmd->AppendSwitch(switches::kDisableDatabases);
-#if defined(OS_ANDROID)
- renderer_cmd->AppendSwitch(switches::kDisableMediaHistoryLogging);
-#endif
}
// Enforce the extra command line flags for impl-side painting.
- if (cc::switches::IsImplSidePaintingEnabled() &&
+ if (IsImplSidePaintingEnabled() &&
!browser_cmd.HasSwitch(switches::kEnableDeferredImageDecoding))
renderer_cmd->AppendSwitch(switches::kEnableDeferredImageDecoding);
}
@@ -1205,6 +1314,12 @@ bool RenderProcessHostImpl::FastShutdownIfPossible() {
if (!SuddenTerminationAllowed())
return false;
+ if (worker_ref_count_ != 0) {
+ if (survive_for_worker_start_time_.is_null())
+ survive_for_worker_start_time_ = base::TimeTicks::Now();
+ return false;
+ }
+
// Set this before ProcessDied() so observers can tell if the render process
// died due to fast shutdown versus another cause.
fast_shutdown_started_ = true;
@@ -1216,89 +1331,13 @@ bool RenderProcessHostImpl::FastShutdownIfPossible() {
void RenderProcessHostImpl::DumpHandles() {
#if defined(OS_WIN)
Send(new ChildProcessMsg_DumpHandles());
- return;
-#endif
-
- NOTIMPLEMENTED();
-}
-
-// This is a platform specific function for mapping a transport DIB given its id
-TransportDIB* RenderProcessHostImpl::MapTransportDIB(
- TransportDIB::Id dib_id) {
-#if defined(OS_WIN)
- // On Windows we need to duplicate the handle from the remote process
- HANDLE section;
- DuplicateHandle(GetHandle(), dib_id.handle, GetCurrentProcess(), &section,
- STANDARD_RIGHTS_REQUIRED | FILE_MAP_READ | FILE_MAP_WRITE,
- FALSE, 0);
- return TransportDIB::Map(section);
-#elif defined(TOOLKIT_GTK)
- return TransportDIB::Map(dib_id.shmkey);
-#elif defined(OS_ANDROID)
- return TransportDIB::Map(dib_id);
-#else
- // On POSIX, the browser allocates all DIBs and keeps a file descriptor around
- // for each.
- return widget_helper_->MapTransportDIB(dib_id);
-#endif
-}
-
-TransportDIB* RenderProcessHostImpl::GetTransportDIB(
- TransportDIB::Id dib_id) {
- if (!TransportDIB::is_valid_id(dib_id))
- return NULL;
-
- const std::map<TransportDIB::Id, TransportDIB*>::iterator
- i = cached_dibs_.find(dib_id);
- if (i != cached_dibs_.end()) {
- cached_dibs_cleaner_.Reset();
- return i->second;
- }
-
- TransportDIB* dib = MapTransportDIB(dib_id);
- if (!dib)
- return NULL;
-
- if (cached_dibs_.size() >= MAX_MAPPED_TRANSPORT_DIBS) {
- // Clean a single entry from the cache
- std::map<TransportDIB::Id, TransportDIB*>::iterator smallest_iterator;
- size_t smallest_size = std::numeric_limits<size_t>::max();
-
- for (std::map<TransportDIB::Id, TransportDIB*>::iterator
- i = cached_dibs_.begin(); i != cached_dibs_.end(); ++i) {
- if (i->second->size() <= smallest_size) {
- smallest_iterator = i;
- smallest_size = i->second->size();
- }
- }
-
-#if defined(TOOLKIT_GTK)
- smallest_iterator->second->Detach();
-#else
- delete smallest_iterator->second;
-#endif
- cached_dibs_.erase(smallest_iterator);
- }
-
- cached_dibs_[dib_id] = dib;
- cached_dibs_cleaner_.Reset();
- return dib;
-}
-
-void RenderProcessHostImpl::ClearTransportDIBCache() {
-#if defined(TOOLKIT_GTK)
- std::map<TransportDIB::Id, TransportDIB*>::const_iterator dib =
- cached_dibs_.begin();
- for (; dib != cached_dibs_.end(); ++dib)
- dib->second->Detach();
#else
- STLDeleteContainerPairSecondPointers(
- cached_dibs_.begin(), cached_dibs_.end());
+ NOTIMPLEMENTED();
#endif
- cached_dibs_.clear();
}
bool RenderProcessHostImpl::Send(IPC::Message* msg) {
+ TRACE_EVENT0("renderer_host", "RenderProcessHostImpl::Send");
if (!channel_) {
if (!is_initialized_) {
queued_messages_.push(msg);
@@ -1327,8 +1366,7 @@ bool RenderProcessHostImpl::OnMessageReceived(const IPC::Message& msg) {
mark_child_process_activity_time();
if (msg.routing_id() == MSG_ROUTING_CONTROL) {
// Dispatch control messages.
- bool msg_is_ok = true;
- IPC_BEGIN_MESSAGE_MAP_EX(RenderProcessHostImpl, msg, msg_is_ok)
+ IPC_BEGIN_MESSAGE_MAP(RenderProcessHostImpl, msg)
IPC_MESSAGE_HANDLER(ChildProcessHostMsg_ShutdownRequest,
OnShutdownRequest)
IPC_MESSAGE_HANDLER(ChildProcessHostMsg_DumpHandlesDone,
@@ -1338,18 +1376,21 @@ bool RenderProcessHostImpl::OnMessageReceived(const IPC::Message& msg) {
IPC_MESSAGE_HANDLER(ViewHostMsg_UserMetricsRecordAction,
OnUserMetricsRecordAction)
IPC_MESSAGE_HANDLER(ViewHostMsg_SavedPageAsMHTML, OnSavedPageAsMHTML)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(
+ ChildProcessHostMsg_SyncAllocateGpuMemoryBuffer,
+ OnAllocateGpuMemoryBuffer)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_Close_ACK, OnCloseACK)
+#if defined(ENABLE_WEBRTC)
+ IPC_MESSAGE_HANDLER(AecDumpMsg_RegisterAecDumpConsumer,
+ OnRegisterAecDumpConsumer)
+ IPC_MESSAGE_HANDLER(AecDumpMsg_UnregisterAecDumpConsumer,
+ OnUnregisterAecDumpConsumer)
+#endif
// Adding single handlers for your service here is fine, but once your
// service needs more than one handler, please extract them into a new
// message filter and add that filter to CreateMessageFilters().
- IPC_END_MESSAGE_MAP_EX()
-
- if (!msg_is_ok) {
- // The message had a handler, but its de-serialization failed.
- // We consider this a capital crime. Kill the renderer if we have one.
- LOG(ERROR) << "bad message " << msg.type() << " terminating renderer.";
- RecordAction(UserMetricsAction("BadMessageTerminate_BRPH"));
- ReceivedBadMessage();
- }
+ IPC_END_MESSAGE_MAP()
+
return true;
}
@@ -1366,11 +1407,10 @@ bool RenderProcessHostImpl::OnMessageReceived(const IPC::Message& msg) {
// If this is a SwapBuffers, we need to ack it if we're not going to handle
// it so that the GPU process doesn't get stuck in unscheduled state.
- bool msg_is_ok = true;
- IPC_BEGIN_MESSAGE_MAP_EX(RenderProcessHostImpl, msg, msg_is_ok)
+ IPC_BEGIN_MESSAGE_MAP(RenderProcessHostImpl, msg)
IPC_MESSAGE_HANDLER(ViewHostMsg_CompositorSurfaceBuffersSwapped,
OnCompositorSurfaceBuffersSwappedNoHost)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return true;
}
return listener->OnMessageReceived(msg);
@@ -1385,14 +1425,21 @@ void RenderProcessHostImpl::OnChannelConnected(int32 peer_pid) {
tracked_objects::ThreadData::Status status =
tracked_objects::ThreadData::status();
Send(new ChildProcessMsg_SetProfilerStatus(status));
-
- Send(new ViewMsg_SetRendererProcessID(GetID()));
}
void RenderProcessHostImpl::OnChannelError() {
ProcessDied(true /* already_dead */);
}
+void RenderProcessHostImpl::OnBadMessageReceived(const IPC::Message& message) {
+ // Message de-serialization failed. We consider this a capital crime. Kill the
+ // renderer if we have one.
+ LOG(ERROR) << "bad message " << message.type() << " terminating renderer.";
+ BrowserChildProcessHostImpl::HistogramBadMessageTerminated(
+ PROCESS_TYPE_RENDERER);
+ ReceivedBadMessage();
+}
+
BrowserContext* RenderProcessHostImpl::GetBrowserContext() const {
return browser_context_;
}
@@ -1419,8 +1466,34 @@ bool RenderProcessHostImpl::IgnoreInputEvents() const {
}
void RenderProcessHostImpl::Cleanup() {
+ // If within_process_died_observer_ is true, one of our observers performed an
+ // action that caused us to die (e.g. http://crbug.com/339504). Therefore,
+ // delay the destruction until all of the observer callbacks have been made,
+ // and guarantee that the RenderProcessHostDestroyed observer callback is
+ // always the last callback fired.
+ if (within_process_died_observer_) {
+ delayed_cleanup_needed_ = true;
+ return;
+ }
+ delayed_cleanup_needed_ = false;
+
+ // Records the time when the process starts surviving for workers for UMA.
+ if (listeners_.IsEmpty() && worker_ref_count_ > 0 &&
+ survive_for_worker_start_time_.is_null()) {
+ survive_for_worker_start_time_ = base::TimeTicks::Now();
+ }
+
// When there are no other owners of this object, we can delete ourselves.
- if (listeners_.IsEmpty()) {
+ if (listeners_.IsEmpty() && worker_ref_count_ == 0) {
+ if (!survive_for_worker_start_time_.is_null()) {
+ UMA_HISTOGRAM_LONG_TIMES(
+ "SharedWorker.RendererSurviveForWorkerTime",
+ base::TimeTicks::Now() - survive_for_worker_start_time_);
+ }
+ // We cannot clean up twice; if this fails, there is an issue with our
+ // control flow.
+ DCHECK(!deleting_soon_);
+
DCHECK_EQ(0, pending_views_);
FOR_EACH_OBSERVER(RenderProcessHostObserver,
observers_,
@@ -1443,7 +1516,7 @@ void RenderProcessHostImpl::Cleanup() {
channel_.reset();
gpu_message_filter_ = NULL;
message_port_message_filter_ = NULL;
- geolocation_dispatcher_host_ = NULL;
+ RemoveUserData(kSessionStorageHolderKey);
// Remove ourself from the list of renderer processes so that we can't be
// reused in between now and when the Delete task runs.
@@ -1472,19 +1545,66 @@ base::TimeDelta RenderProcessHostImpl::GetChildProcessIdleTime() const {
return base::TimeTicks::Now() - child_process_activity_time_;
}
-void RenderProcessHostImpl::SurfaceUpdated(int32 surface_id) {
- if (!gpu_message_filter_)
- return;
- BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
- &GpuMessageFilter::SurfaceUpdated,
- gpu_message_filter_,
- surface_id));
-}
-
void RenderProcessHostImpl::ResumeRequestsForView(int route_id) {
widget_helper_->ResumeRequestsForView(route_id);
}
+void RenderProcessHostImpl::FilterURL(bool empty_allowed, GURL* url) {
+ FilterURL(this, empty_allowed, url);
+}
+
+#if defined(ENABLE_WEBRTC)
+void RenderProcessHostImpl::EnableAecDump(const base::FilePath& file) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ // Enable AEC dump for each registered consumer.
+ for (std::vector<int>::iterator it = aec_dump_consumers_.begin();
+ it != aec_dump_consumers_.end(); ++it) {
+ EnableAecDumpForId(file, *it);
+ }
+}
+
+void RenderProcessHostImpl::DisableAecDump() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ // Posting on the FILE thread and then replying back on the UI thread is only
+ // for avoiding races between enable and disable. Nothing is done on the FILE
+ // thread.
+ BrowserThread::PostTaskAndReply(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&DisableAecDumpOnFileThread),
+ base::Bind(&RenderProcessHostImpl::SendDisableAecDumpToRenderer,
+ weak_factory_.GetWeakPtr()));
+}
+
+void RenderProcessHostImpl::SetWebRtcLogMessageCallback(
+ base::Callback<void(const std::string&)> callback) {
+ webrtc_log_message_callback_ = callback;
+}
+
+RenderProcessHostImpl::WebRtcStopRtpDumpCallback
+RenderProcessHostImpl::StartRtpDump(
+ bool incoming,
+ bool outgoing,
+ const WebRtcRtpPacketCallback& packet_callback) {
+ if (!p2p_socket_dispatcher_host_)
+ return WebRtcStopRtpDumpCallback();
+
+ BrowserThread::PostTask(BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&P2PSocketDispatcherHost::StartRtpDump,
+ p2p_socket_dispatcher_host_,
+ incoming,
+ outgoing,
+ packet_callback));
+
+ if (stop_rtp_dump_callback_.is_null()) {
+ stop_rtp_dump_callback_ =
+ base::Bind(&P2PSocketDispatcherHost::StopRtpDumpOnUIThread,
+ p2p_socket_dispatcher_host_);
+ }
+ return stop_rtp_dump_callback_;
+}
+#endif
+
IPC::ChannelProxy* RenderProcessHostImpl::GetChannel() {
return channel_.get();
}
@@ -1525,6 +1645,53 @@ void RenderProcessHostImpl::UnregisterHost(int host_id) {
}
// static
+void RenderProcessHostImpl::FilterURL(RenderProcessHost* rph,
+ bool empty_allowed,
+ GURL* url) {
+ ChildProcessSecurityPolicyImpl* policy =
+ ChildProcessSecurityPolicyImpl::GetInstance();
+
+ if (empty_allowed && url->is_empty())
+ return;
+
+ // The browser process should never hear the swappedout:// URL from any
+ // of the renderer's messages. Check for this in debug builds, but don't
+ // let it crash a release browser.
+ DCHECK(GURL(kSwappedOutURL) != *url);
+
+ if (!url->is_valid()) {
+ // Have to use about:blank for the denied case, instead of an empty GURL.
+ // This is because the browser treats navigation to an empty GURL as a
+ // navigation to the home page. This is often a privileged page
+ // (chrome://newtab/) which is exactly what we don't want.
+ *url = GURL(url::kAboutBlankURL);
+ RecordAction(base::UserMetricsAction("FilterURLTermiate_Invalid"));
+ return;
+ }
+
+ if (url->SchemeIs(url::kAboutScheme)) {
+ // The renderer treats all URLs in the about: scheme as being about:blank.
+ // Canonicalize about: URLs to about:blank.
+ *url = GURL(url::kAboutBlankURL);
+ RecordAction(base::UserMetricsAction("FilterURLTermiate_About"));
+ }
+
+ // Do not allow browser plugin guests to navigate to non-web URLs, since they
+ // cannot swap processes or grant bindings.
+ bool non_web_url_in_guest = rph->IsIsolatedGuest() &&
+ !(url->is_valid() && policy->IsWebSafeScheme(url->scheme()));
+
+ if (non_web_url_in_guest || !policy->CanRequestURL(rph->GetID(), *url)) {
+ // If this renderer is not permitted to request this URL, we invalidate the
+ // URL. This prevents us from storing the blocked URL and becoming confused
+ // later.
+ VLOG(1) << "Blocked URL " << url->spec();
+ *url = GURL(url::kAboutBlankURL);
+ RecordAction(base::UserMetricsAction("FilterURLTermiate_Blocked"));
+ }
+}
+
+// static
bool RenderProcessHostImpl::IsSuitableHost(
RenderProcessHost* host,
BrowserContext* browser_context,
@@ -1539,7 +1706,7 @@ bool RenderProcessHostImpl::IsSuitableHost(
// and non-guest storage gets mixed. In the future, we might consider enabling
// the sharing of guests, in this case this check should be removed and
// InSameStoragePartition should handle the possible sharing.
- if (host->IsGuest())
+ if (host->IsIsolatedGuest())
return false;
// Check whether the given host and the intended site_url will be using the
@@ -1570,24 +1737,30 @@ void RenderProcessHost::SetRunRendererInProcess(bool value) {
g_run_renderer_in_process_ = value;
CommandLine* command_line = CommandLine::ForCurrentProcess();
- if (value && !command_line->HasSwitch(switches::kLang)) {
- // Modify the current process' command line to include the browser locale,
- // as the renderer expects this flag to be set.
- const std::string locale =
- GetContentClient()->browser()->GetApplicationLocale();
- command_line->AppendSwitchASCII(switches::kLang, locale);
+ if (value) {
+ if (!command_line->HasSwitch(switches::kLang)) {
+ // Modify the current process' command line to include the browser locale,
+ // as the renderer expects this flag to be set.
+ const std::string locale =
+ GetContentClient()->browser()->GetApplicationLocale();
+ command_line->AppendSwitchASCII(switches::kLang, locale);
+ }
+ // TODO(piman): we should really send configuration through bools rather
+ // than by parsing strings, i.e. sending an IPC rather than command line
+ // args. crbug.com/314909
+ AppendCompositorCommandLineFlags(command_line);
}
}
// static
RenderProcessHost::iterator RenderProcessHost::AllHostsIterator() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
return iterator(g_all_hosts.Pointer());
}
// static
RenderProcessHost* RenderProcessHost::FromID(int render_process_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
return g_all_hosts.Get().Lookup(render_process_id);
}
@@ -1630,11 +1803,12 @@ RenderProcessHost* RenderProcessHost::GetExistingProcessHost(
iterator iter(AllHostsIterator());
while (!iter.IsAtEnd()) {
- if (RenderProcessHostImpl::IsSuitableHost(
+ if (GetContentClient()->browser()->MayReuseHost(iter.GetCurrentValue()) &&
+ RenderProcessHostImpl::IsSuitableHost(
iter.GetCurrentValue(),
- browser_context, site_url))
+ browser_context, site_url)) {
suitable_renderers.push_back(iter.GetCurrentValue());
-
+ }
iter.Advance();
}
@@ -1665,7 +1839,7 @@ bool RenderProcessHost::ShouldUseProcessPerSite(
// Note: DevTools pages have WebUI type but should not reuse the same host.
if (WebUIControllerFactoryRegistry::GetInstance()->UseWebUIForURL(
browser_context, url) &&
- !url.SchemeIs(chrome::kChromeDevToolsScheme)) {
+ !url.SchemeIs(kChromeDevToolsScheme)) {
return true;
}
@@ -1687,10 +1861,12 @@ RenderProcessHost* RenderProcessHostImpl::GetProcessHostForSite(
std::string site = SiteInstance::GetSiteForURL(browser_context, url)
.possibly_invalid_spec();
RenderProcessHost* host = map->FindProcess(site);
- if (host && !IsSuitableHost(host, browser_context, url)) {
+ if (host && (!GetContentClient()->browser()->MayReuseHost(host) ||
+ !IsSuitableHost(host, browser_context, url))) {
// The registered process does not have an appropriate set of bindings for
// the url. Remove it from the map so we can register a better one.
- RecordAction(UserMetricsAction("BindingsMismatch_GetProcessHostPerSite"));
+ RecordAction(
+ base::UserMetricsAction("BindingsMismatch_GetProcessHostPerSite"));
map->RemoveProcess(host);
host = NULL;
}
@@ -1722,6 +1898,13 @@ void RenderProcessHostImpl::ProcessDied(bool already_dead) {
// calls to a renderer. If we don't have a valid channel here it means we
// already handled the error.
+ // It should not be possible for us to be called re-entrantly.
+ DCHECK(!within_process_died_observer_);
+
+ // It should not be possible for a process death notification to come in while
+ // we are dying.
+ DCHECK(!deleting_soon_);
+
// child_process_launcher_ can be NULL in single process mode or if fast
// termination happened.
int exit_code = 0;
@@ -1732,16 +1915,21 @@ void RenderProcessHostImpl::ProcessDied(bool already_dead) {
base::TERMINATION_STATUS_NORMAL_TERMINATION;
RendererClosedDetails details(GetHandle(), status, exit_code);
+ within_process_died_observer_ = true;
NotificationService::current()->Notify(
NOTIFICATION_RENDERER_PROCESS_CLOSED,
Source<RenderProcessHost>(this),
Details<RendererClosedDetails>(&details));
+ FOR_EACH_OBSERVER(RenderProcessHostObserver,
+ observers_,
+ RenderProcessExited(this, GetHandle(), status, exit_code));
+ within_process_died_observer_ = false;
child_process_launcher_.reset();
channel_.reset();
gpu_message_filter_ = NULL;
message_port_message_filter_ = NULL;
- geolocation_dispatcher_host_ = NULL;
+ RemoveUserData(kSessionStorageHolderKey);
IDMap<IPC::Listener>::iterator iter(&listeners_);
while (!iter.IsAtEnd()) {
@@ -1752,9 +1940,14 @@ void RenderProcessHostImpl::ProcessDied(bool already_dead) {
iter.Advance();
}
- ClearTransportDIBCache();
+ mojo_application_host_.reset();
+
+ // It's possible that one of the calls out to the observers might have caused
+ // this object to be no longer needed.
+ if (delayed_cleanup_needed_)
+ Cleanup();
- // this object is not deleted at this point and may be reused later.
+ // This object is not deleted at this point and might be reused later.
// TODO(darin): clean this up
}
@@ -1792,6 +1985,32 @@ void RenderProcessHostImpl::EndFrameSubscription(int route_id) {
route_id));
}
+#if defined(ENABLE_WEBRTC)
+void RenderProcessHostImpl::WebRtcLogMessage(const std::string& message) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (!webrtc_log_message_callback_.is_null())
+ webrtc_log_message_callback_.Run(message);
+}
+#endif
+
+void RenderProcessHostImpl::ReleaseOnCloseACK(
+ RenderProcessHost* host,
+ const SessionStorageNamespaceMap& sessions,
+ int view_route_id) {
+ DCHECK(host);
+ if (sessions.empty())
+ return;
+ SessionStorageHolder* holder = static_cast<SessionStorageHolder*>
+ (host->GetUserData(kSessionStorageHolderKey));
+ if (!holder) {
+ holder = new SessionStorageHolder();
+ host->SetUserData(
+ kSessionStorageHolderKey,
+ holder);
+ }
+ holder->Hold(sessions, view_route_id);
+}
+
void RenderProcessHostImpl::OnShutdownRequest() {
// Don't shut down if there are active RenderViews, or if there are pending
// RenderViews being swapped back in.
@@ -1826,6 +2045,10 @@ void RenderProcessHostImpl::SetBackgrounded(bool backgrounded) {
if (!child_process_launcher_.get() || child_process_launcher_->IsStarting())
return;
+ // Don't background processes which have active audio streams.
+ if (backgrounded_ && audio_renderer_host_->HasActiveAudio())
+ return;
+
#if defined(OS_WIN)
// The cbstext.dll loads as a global GetMessage hook in the browser process
// and intercepts/unintercepts the kernel32 API SetPriorityClass in a
@@ -1837,7 +2060,17 @@ void RenderProcessHostImpl::SetBackgrounded(bool backgrounded) {
return;
#endif // OS_WIN
+ // Notify the child process of background state.
+ Send(new ChildProcessMsg_SetProcessBackgrounded(backgrounded));
+
+#if !defined(OS_WIN)
+ // Backgrounding may require elevated privileges not available to renderer
+ // processes, so control backgrounding from the process host.
+
+ // Windows Vista+ has a fancy process backgrounding mode that can only be set
+ // from within the process.
child_process_launcher_->SetProcessBackgrounded(backgrounded);
+#endif // !OS_WIN
}
void RenderProcessHostImpl::OnProcessLaunched() {
@@ -1854,7 +2087,7 @@ void RenderProcessHostImpl::OnProcessLaunched() {
return;
}
- child_process_launcher_->SetProcessBackgrounded(backgrounded_);
+ SetBackgrounded(backgrounded_);
}
// NOTE: This needs to be before sending queued messages because
@@ -1869,10 +2102,20 @@ void RenderProcessHostImpl::OnProcessLaunched() {
Source<RenderProcessHost>(this),
NotificationService::NoDetails());
+ // Allow Mojo to be setup before the renderer sees any Chrome IPC messages.
+ // This way, Mojo can be safely used from the renderer in response to any
+ // Chrome IPC message.
+ MaybeActivateMojo();
+
while (!queued_messages_.empty()) {
Send(queued_messages_.front());
queued_messages_.pop();
}
+
+#if defined(ENABLE_WEBRTC)
+ if (WebRTCInternals::GetInstance()->aec_dump_enabled())
+ EnableAecDump(WebRTCInternals::GetInstance()->aec_dump_file_path());
+#endif
}
scoped_refptr<AudioRendererHost>
@@ -1885,6 +2128,14 @@ void RenderProcessHostImpl::OnUserMetricsRecordAction(
RecordComputedAction(action);
}
+void RenderProcessHostImpl::OnCloseACK(int old_route_id) {
+ SessionStorageHolder* holder = static_cast<SessionStorageHolder*>
+ (GetUserData(kSessionStorageHolderKey));
+ if (!holder)
+ return;
+ holder->Release(old_route_id);
+}
+
void RenderProcessHostImpl::OnSavedPageAsMHTML(int job_id, int64 data_size) {
MHTMLGenerationManager::GetInstance()->MHTMLGenerated(job_id, data_size);
}
@@ -1893,6 +2144,9 @@ void RenderProcessHostImpl::OnCompositorSurfaceBuffersSwappedNoHost(
const ViewHostMsg_CompositorSurfaceBuffersSwapped_Params& params) {
TRACE_EVENT0("renderer_host",
"RenderWidgetHostImpl::OnCompositorSurfaceBuffersSwappedNoHost");
+ if (!ui::LatencyInfo::Verify(params.latency_info,
+ "ViewHostMsg_CompositorSurfaceBuffersSwapped"))
+ return;
AcceleratedSurfaceMsg_BufferPresented_Params ack_params;
ack_params.sync_point = 0;
RenderWidgetHostImpl::AcknowledgeBufferPresent(params.route_id,
@@ -1917,4 +2171,207 @@ void RenderProcessHostImpl::OnGpuSwitching() {
}
}
+#if defined(ENABLE_WEBRTC)
+void RenderProcessHostImpl::OnRegisterAecDumpConsumer(int id) {
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(
+ &RenderProcessHostImpl::RegisterAecDumpConsumerOnUIThread,
+ weak_factory_.GetWeakPtr(),
+ id));
+}
+
+void RenderProcessHostImpl::OnUnregisterAecDumpConsumer(int id) {
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(
+ &RenderProcessHostImpl::UnregisterAecDumpConsumerOnUIThread,
+ weak_factory_.GetWeakPtr(),
+ id));
+}
+
+void RenderProcessHostImpl::RegisterAecDumpConsumerOnUIThread(int id) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ aec_dump_consumers_.push_back(id);
+ if (WebRTCInternals::GetInstance()->aec_dump_enabled()) {
+ EnableAecDumpForId(WebRTCInternals::GetInstance()->aec_dump_file_path(),
+ id);
+ }
+}
+
+void RenderProcessHostImpl::UnregisterAecDumpConsumerOnUIThread(int id) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ for (std::vector<int>::iterator it = aec_dump_consumers_.begin();
+ it != aec_dump_consumers_.end(); ++it) {
+ if (*it == id) {
+ aec_dump_consumers_.erase(it);
+ break;
+ }
+ }
+}
+
+#if defined(OS_WIN)
+#define IntToStringType base::IntToString16
+#else
+#define IntToStringType base::IntToString
+#endif
+
+void RenderProcessHostImpl::EnableAecDumpForId(const base::FilePath& file,
+ int id) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ base::FilePath unique_file =
+ file.AddExtension(IntToStringType(GetID()))
+ .AddExtension(IntToStringType(id));
+ BrowserThread::PostTaskAndReplyWithResult(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&CreateAecDumpFileForProcess, unique_file, GetHandle()),
+ base::Bind(&RenderProcessHostImpl::SendAecDumpFileToRenderer,
+ weak_factory_.GetWeakPtr(),
+ id));
+}
+
+#undef IntToStringType
+
+void RenderProcessHostImpl::SendAecDumpFileToRenderer(
+ int id,
+ IPC::PlatformFileForTransit file_for_transit) {
+ if (file_for_transit == IPC::InvalidPlatformFileForTransit())
+ return;
+ Send(new AecDumpMsg_EnableAecDump(id, file_for_transit));
+}
+
+void RenderProcessHostImpl::SendDisableAecDumpToRenderer() {
+ Send(new AecDumpMsg_DisableAecDump());
+}
+#endif
+
+void RenderProcessHostImpl::IncrementWorkerRefCount() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ ++worker_ref_count_;
+}
+
+void RenderProcessHostImpl::DecrementWorkerRefCount() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK_GT(worker_ref_count_, 0);
+ --worker_ref_count_;
+ if (worker_ref_count_ == 0)
+ Cleanup();
+}
+
+void RenderProcessHostImpl::ConnectTo(
+ const base::StringPiece& service_name,
+ mojo::ScopedMessagePipeHandle handle) {
+ mojo_activation_required_ = true;
+ MaybeActivateMojo();
+
+ mojo_application_host_->service_provider()->ConnectToService(
+ mojo::String::From(service_name),
+ std::string(),
+ handle.Pass(),
+ mojo::String());
+}
+
+void RenderProcessHostImpl::OnAllocateGpuMemoryBuffer(uint32 width,
+ uint32 height,
+ uint32 internalformat,
+ uint32 usage,
+ IPC::Message* reply) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (!GpuMemoryBufferImpl::IsFormatValid(internalformat) ||
+ !GpuMemoryBufferImpl::IsUsageValid(usage)) {
+ GpuMemoryBufferAllocated(reply, gfx::GpuMemoryBufferHandle());
+ return;
+ }
+ base::CheckedNumeric<int> size = width;
+ size *= height;
+ if (!size.IsValid()) {
+ GpuMemoryBufferAllocated(reply, gfx::GpuMemoryBufferHandle());
+ return;
+ }
+
+#if defined(OS_MACOSX)
+ // TODO(reveman): This should be moved to
+ // GpuMemoryBufferImpl::AllocateForChildProcess and
+ // GpuMemoryBufferImplIOSurface. crbug.com/325045, crbug.com/323304
+ if (GpuMemoryBufferImplIOSurface::IsConfigurationSupported(internalformat,
+ usage)) {
+ base::ScopedCFTypeRef<CFMutableDictionaryRef> properties;
+ properties.reset(
+ CFDictionaryCreateMutable(kCFAllocatorDefault,
+ 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+ AddIntegerValue(properties, kIOSurfaceWidth, width);
+ AddIntegerValue(properties, kIOSurfaceHeight, height);
+ AddIntegerValue(properties,
+ kIOSurfaceBytesPerElement,
+ GpuMemoryBufferImpl::BytesPerPixel(internalformat));
+ AddIntegerValue(
+ properties,
+ kIOSurfacePixelFormat,
+ GpuMemoryBufferImplIOSurface::PixelFormat(internalformat));
+ // TODO(reveman): Remove this when using a mach_port_t to transfer
+ // IOSurface to renderer process. crbug.com/323304
+ AddBooleanValue(
+ properties, kIOSurfaceIsGlobal, true);
+
+ base::ScopedCFTypeRef<IOSurfaceRef> io_surface(IOSurfaceCreate(properties));
+ if (io_surface) {
+ gfx::GpuMemoryBufferHandle handle;
+ handle.type = gfx::IO_SURFACE_BUFFER;
+ handle.io_surface_id = IOSurfaceGetID(io_surface);
+
+ // TODO(reveman): This makes the assumption that the renderer will
+ // grab a reference to the surface before sending another message.
+ // crbug.com/325045
+ last_io_surface_ = io_surface;
+ GpuMemoryBufferAllocated(reply, handle);
+ return;
+ }
+ }
+#endif
+
+#if defined(OS_ANDROID)
+ // TODO(reveman): This should be moved to
+ // GpuMemoryBufferImpl::AllocateForChildProcess and
+ // GpuMemoryBufferImplSurfaceTexture when adding support for out-of-process
+ // GPU service. crbug.com/368716
+ if (GpuMemoryBufferImplSurfaceTexture::IsConfigurationSupported(
+ internalformat, usage)) {
+ // Each surface texture is associated with a render process id. This allows
+ // the GPU service and Java Binder IPC to verify that a renderer is not
+ // trying to use a surface texture it doesn't own.
+ int surface_texture_id = CompositorImpl::CreateSurfaceTexture(GetID());
+ if (surface_texture_id != -1) {
+ gfx::GpuMemoryBufferHandle handle;
+ handle.type = gfx::SURFACE_TEXTURE_BUFFER;
+ handle.surface_texture_id =
+ gfx::SurfaceTextureId(surface_texture_id, GetID());
+ GpuMemoryBufferAllocated(reply, handle);
+ return;
+ }
+ }
+#endif
+
+ GpuMemoryBufferImpl::AllocateForChildProcess(
+ gfx::Size(width, height),
+ internalformat,
+ usage,
+ GetHandle(),
+ base::Bind(&RenderProcessHostImpl::GpuMemoryBufferAllocated,
+ weak_factory_.GetWeakPtr(),
+ reply));
+}
+
+void RenderProcessHostImpl::GpuMemoryBufferAllocated(
+ IPC::Message* reply,
+ const gfx::GpuMemoryBufferHandle& handle) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ ChildProcessHostMsg_SyncAllocateGpuMemoryBuffer::WriteReplyParams(reply,
+ handle);
+ Send(reply);
+}
+
} // namespace content
diff --git a/chromium/content/browser/renderer_host/render_process_host_impl.h b/chromium/content/browser/renderer_host/render_process_host_impl.h
index 49ce3634163..2728626246e 100644
--- a/chromium/content/browser/renderer_host/render_process_host_impl.h
+++ b/chromium/content/browser/renderer_host/render_process_host_impl.h
@@ -14,34 +14,44 @@
#include "base/process/process.h"
#include "base/timer/timer.h"
#include "content/browser/child_process_launcher.h"
-#include "content/browser/geolocation/geolocation_dispatcher_host.h"
+#include "content/browser/dom_storage/session_storage_namespace_impl.h"
#include "content/browser/power_monitor_message_broadcaster.h"
#include "content/common/content_export.h"
-#include "content/public/browser/global_request_id.h"
#include "content/public/browser/gpu_data_manager_observer.h"
#include "content/public/browser/render_process_host.h"
#include "ipc/ipc_channel_proxy.h"
-#include "ui/surface/transport_dib.h"
+#include "ipc/ipc_platform_file.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+
+#if defined(OS_MACOSX)
+#include <IOSurface/IOSurfaceAPI.h>
+#include "base/mac/scoped_cftyperef.h"
+#endif
-class CommandLine;
struct ViewHostMsg_CompositorSurfaceBuffersSwapped_Params;
namespace base {
+class CommandLine;
class MessageLoop;
}
namespace gfx {
class Size;
+struct GpuMemoryBufferHandle;
}
namespace content {
class AudioRendererHost;
class BrowserDemuxerAndroid;
-class GeolocationDispatcherHost;
class GpuMessageFilter;
class MessagePortMessageFilter;
+class MojoApplicationHost;
+#if defined(ENABLE_WEBRTC)
+class P2PSocketDispatcherHost;
+#endif
class PeerConnectionTrackerHost;
class RendererMainThread;
+class RenderProcessHostMojoImpl;
class RenderWidgetHelper;
class RenderWidgetHost;
class RenderWidgetHostImpl;
@@ -49,6 +59,9 @@ class RenderWidgetHostViewFrameSubscriber;
class StoragePartition;
class StoragePartitionImpl;
+typedef base::Thread* (*RendererMainThreadFactoryFunction)(
+ const std::string& id);
+
// Implements a concrete RenderProcessHost for the browser process for talking
// to actual renderer processes (as opposed to mocks).
//
@@ -75,8 +88,7 @@ class CONTENT_EXPORT RenderProcessHostImpl
public:
RenderProcessHostImpl(BrowserContext* browser_context,
StoragePartitionImpl* storage_partition_impl,
- bool supports_browser_plugin,
- bool is_guest);
+ bool is_isolated_guest);
virtual ~RenderProcessHostImpl();
// RenderProcessHost implementation (public portion).
@@ -94,13 +106,11 @@ class CONTENT_EXPORT RenderProcessHostImpl
virtual void WidgetRestored() OVERRIDE;
virtual void WidgetHidden() OVERRIDE;
virtual int VisibleWidgetCount() const OVERRIDE;
- virtual bool IsGuest() const OVERRIDE;
+ virtual bool IsIsolatedGuest() const OVERRIDE;
virtual StoragePartition* GetStoragePartition() const OVERRIDE;
virtual bool FastShutdownIfPossible() OVERRIDE;
virtual void DumpHandles() OVERRIDE;
virtual base::ProcessHandle GetHandle() const OVERRIDE;
- virtual TransportDIB* GetTransportDIB(TransportDIB::Id dib_id) OVERRIDE;
- virtual TransportDIB* MapTransportDIB(TransportDIB::Id dib_id) OVERRIDE;
virtual BrowserContext* GetBrowserContext() const OVERRIDE;
virtual bool InSameStoragePartition(
StoragePartition* partition) const OVERRIDE;
@@ -118,8 +128,21 @@ class CONTENT_EXPORT RenderProcessHostImpl
virtual bool FastShutdownForPageCount(size_t count) OVERRIDE;
virtual bool FastShutdownStarted() const OVERRIDE;
virtual base::TimeDelta GetChildProcessIdleTime() const OVERRIDE;
- virtual void SurfaceUpdated(int32 surface_id) OVERRIDE;
virtual void ResumeRequestsForView(int route_id) OVERRIDE;
+ virtual void FilterURL(bool empty_allowed, GURL* url) OVERRIDE;
+#if defined(ENABLE_WEBRTC)
+ virtual void EnableAecDump(const base::FilePath& file) OVERRIDE;
+ virtual void DisableAecDump() OVERRIDE;
+ virtual void SetWebRtcLogMessageCallback(
+ base::Callback<void(const std::string&)> callback) OVERRIDE;
+ virtual WebRtcStopRtpDumpCallback StartRtpDump(
+ bool incoming,
+ bool outgoing,
+ const WebRtcRtpPacketCallback& packet_callback) OVERRIDE;
+#endif
+ virtual void ResumeDeferredNavigation(const GlobalRequestID& request_id)
+ OVERRIDE;
+ virtual void NotifyTimezoneChange() OVERRIDE;
// IPC::Sender via RenderProcessHost.
virtual bool Send(IPC::Message* msg) OVERRIDE;
@@ -128,16 +151,13 @@ class CONTENT_EXPORT RenderProcessHostImpl
virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE;
virtual void OnChannelConnected(int32 peer_pid) OVERRIDE;
virtual void OnChannelError() OVERRIDE;
+ virtual void OnBadMessageReceived(const IPC::Message& message) OVERRIDE;
// ChildProcessLauncher::Client implementation.
virtual void OnProcessLaunched() OVERRIDE;
scoped_refptr<AudioRendererHost> audio_renderer_host() const;
- // Tells the ResourceDispatcherHost to resume a deferred navigation without
- // transferring it to a new renderer process.
- void ResumeDeferredNavigation(const GlobalRequestID& request_id);
-
// Call this function when it is evident that the child process is actively
// performing some operation, for example if we just received an IPC message.
void mark_child_process_activity_time() {
@@ -155,16 +175,27 @@ class CONTENT_EXPORT RenderProcessHostImpl
scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber);
void EndFrameSubscription(int route_id);
- scoped_refptr<GeolocationDispatcherHost>
- geolocation_dispatcher_host() const {
- return make_scoped_refptr(geolocation_dispatcher_host_);
- }
+#if defined(ENABLE_WEBRTC)
+ // Fires the webrtc log message callback with |message|, if callback is set.
+ void WebRtcLogMessage(const std::string& message);
+#endif
+
+ // Used to extend the lifetime of the sessions until the render view
+ // in the renderer is fully closed. This is static because its also called
+ // with mock hosts as input in test cases.
+ static void ReleaseOnCloseACK(
+ RenderProcessHost* host,
+ const SessionStorageNamespaceMap& sessions,
+ int view_route_id);
// Register/unregister the host identified by the host id in the global host
// list.
static void RegisterHost(int host_id, RenderProcessHost* host);
static void UnregisterHost(int host_id);
+ // Implementation of FilterURL below that can be shared with the mock class.
+ static void FilterURL(RenderProcessHost* rph, bool empty_allowed, GURL* url);
+
// Returns true if |host| is suitable for launching a new view with |site_url|
// in the given |browser_context|.
static bool IsSuitableHost(RenderProcessHost* host,
@@ -196,6 +227,9 @@ class CONTENT_EXPORT RenderProcessHostImpl
// This forces a renderer that is running "in process" to shut down.
static void ShutDownInProcessRenderer();
+ static void RegisterRendererMainThreadFactory(
+ RendererMainThreadFactoryFunction create);
+
#if defined(OS_ANDROID)
const scoped_refptr<BrowserDemuxerAndroid>& browser_demuxer_android() {
return browser_demuxer_android_;
@@ -206,8 +240,27 @@ class CONTENT_EXPORT RenderProcessHostImpl
return message_port_message_filter_;
}
- void SetIsGuestForTesting(bool is_guest) {
- is_guest_ = is_guest;
+ void set_is_isolated_guest_for_testing(bool is_isolated_guest) {
+ is_isolated_guest_ = is_isolated_guest;
+ }
+
+ // Called when the existence of the other renderer process which is connected
+ // to the Worker in this renderer process has changed.
+ // It is only called when "enable-embedded-shared-worker" flag is set.
+ void IncrementWorkerRefCount();
+ void DecrementWorkerRefCount();
+
+ // Establish a connection to a renderer-provided service. See
+ // content/common/mojo/mojo_service_names.h for a list of services.
+ void ConnectTo(const base::StringPiece& service_name,
+ mojo::ScopedMessagePipeHandle handle);
+
+ template <typename Interface>
+ void ConnectTo(const base::StringPiece& service_name,
+ mojo::InterfacePtr<Interface>* ptr) {
+ mojo::MessagePipe pipe;
+ ptr->Bind(pipe.handle0.Pass());
+ ConnectTo(service_name, pipe.handle1.Pass());
}
protected:
@@ -234,6 +287,8 @@ class CONTENT_EXPORT RenderProcessHostImpl
private:
friend class VisitRelayingRenderProcessHost;
+ void MaybeActivateMojo();
+
// Creates and adds the IO thread message filters.
void CreateMessageFilters();
@@ -243,6 +298,7 @@ class CONTENT_EXPORT RenderProcessHostImpl
void SuddenTerminationChanged(bool enabled);
void OnUserMetricsRecordAction(const std::string& action);
void OnSavedPageAsMHTML(int job_id, int64 mhtml_file_size);
+ void OnCloseACK(int old_route_id);
// CompositorSurfaceBuffersSwapped handler when there's no RWH.
void OnCompositorSurfaceBuffersSwappedNoHost(
@@ -250,13 +306,14 @@ class CONTENT_EXPORT RenderProcessHostImpl
// Generates a command line to be used to spawn a renderer and appends the
// results to |*command_line|.
- void AppendRendererCommandLine(CommandLine* command_line) const;
+ void AppendRendererCommandLine(base::CommandLine* command_line) const;
// Copies applicable command line switches from the given |browser_cmd| line
// flags to the output |renderer_cmd| line flags. Not all switches will be
// copied over.
- void PropagateBrowserCommandLineToRenderer(const CommandLine& browser_cmd,
- CommandLine* renderer_cmd) const;
+ void PropagateBrowserCommandLineToRenderer(
+ const base::CommandLine& browser_cmd,
+ base::CommandLine* renderer_cmd) const;
// Callers can reduce the RenderProcess' priority.
void SetBackgrounded(bool backgrounded);
@@ -266,6 +323,30 @@ class CONTENT_EXPORT RenderProcessHostImpl
virtual void OnGpuSwitching() OVERRIDE;
+#if defined(ENABLE_WEBRTC)
+ void OnRegisterAecDumpConsumer(int id);
+ void OnUnregisterAecDumpConsumer(int id);
+ void RegisterAecDumpConsumerOnUIThread(int id);
+ void UnregisterAecDumpConsumerOnUIThread(int id);
+ void EnableAecDumpForId(const base::FilePath& file, int id);
+ // Sends |file_for_transit| to the render process.
+ void SendAecDumpFileToRenderer(int id,
+ IPC::PlatformFileForTransit file_for_transit);
+ void SendDisableAecDumpToRenderer();
+#endif
+
+ // GpuMemoryBuffer allocation handler.
+ void OnAllocateGpuMemoryBuffer(uint32 width,
+ uint32 height,
+ uint32 internalformat,
+ uint32 usage,
+ IPC::Message* reply);
+ void GpuMemoryBufferAllocated(IPC::Message* reply,
+ const gfx::GpuMemoryBufferHandle& handle);
+
+ scoped_ptr<MojoApplicationHost> mojo_application_host_;
+ bool mojo_activation_required_;
+
// The registered IPC listener objects. When this list is empty, we should
// delete ourselves.
IDMap<IPC::Listener> listeners_;
@@ -293,18 +374,6 @@ class CONTENT_EXPORT RenderProcessHostImpl
// The filter for MessagePort messages coming from the renderer.
scoped_refptr<MessagePortMessageFilter> message_port_message_filter_;
- // A map of transport DIB ids to cached TransportDIBs
- std::map<TransportDIB::Id, TransportDIB*> cached_dibs_;
-
- enum {
- // This is the maximum size of |cached_dibs_|
- MAX_MAPPED_TRANSPORT_DIBS = 3,
- };
-
- void ClearTransportDIBCache();
- // This is used to clear our cache five seconds after the last use.
- base::DelayTimer<RenderProcessHostImpl> cached_dibs_cleaner_;
-
// Used in single-process mode.
scoped_ptr<base::Thread> in_process_renderer_;
@@ -347,13 +416,9 @@ class CONTENT_EXPORT RenderProcessHostImpl
// Records the last time we regarded the child process active.
base::TimeTicks child_process_activity_time_;
- // Indicates whether this is a RenderProcessHost that has permission to embed
- // Browser Plugins.
- bool supports_browser_plugin_;
-
// Indicates whether this is a RenderProcessHost of a Browser Plugin guest
// renderer.
- bool is_guest_;
+ bool is_isolated_guest_;
// Forwards messages between WebRTCInternals in the browser process
// and PeerConnectionTracker in the renderer process.
@@ -363,6 +428,14 @@ class CONTENT_EXPORT RenderProcessHostImpl
// than once.
bool gpu_observer_registered_;
+ // Set if a call to Cleanup is required once the RenderProcessHostImpl is no
+ // longer within the RenderProcessHostObserver::RenderProcessExited callbacks.
+ bool delayed_cleanup_needed_;
+
+ // Indicates whether RenderProcessHostImpl is currently iterating and calling
+ // through RenderProcessHostObserver::RenderProcessExited.
+ bool within_process_died_observer_;
+
// Forwards power state messages to the renderer process.
PowerMonitorMessageBroadcaster power_monitor_broadcaster_;
@@ -372,8 +445,27 @@ class CONTENT_EXPORT RenderProcessHostImpl
scoped_refptr<BrowserDemuxerAndroid> browser_demuxer_android_;
#endif
- // Message filter for geolocation messages.
- GeolocationDispatcherHost* geolocation_dispatcher_host_;
+#if defined(ENABLE_WEBRTC)
+ base::Callback<void(const std::string&)> webrtc_log_message_callback_;
+
+ scoped_refptr<P2PSocketDispatcherHost> p2p_socket_dispatcher_host_;
+
+ // Must be accessed on UI thread.
+ std::vector<int> aec_dump_consumers_;
+
+ WebRtcStopRtpDumpCallback stop_rtp_dump_callback_;
+#endif
+
+ int worker_ref_count_;
+
+ // Records the time when the process starts surviving for workers for UMA.
+ base::TimeTicks survive_for_worker_start_time_;
+
+ base::WeakPtrFactory<RenderProcessHostImpl> weak_factory_;
+
+#if defined(OS_MACOSX)
+ base::ScopedCFTypeRef<IOSurfaceRef> last_io_surface_;
+#endif
DISALLOW_COPY_AND_ASSIGN(RenderProcessHostImpl);
};
diff --git a/chromium/content/browser/renderer_host/render_process_host_unittest.cc b/chromium/content/browser/renderer_host/render_process_host_unittest.cc
index 6a830f9d61b..474a72bd166 100644
--- a/chromium/content/browser/renderer_host/render_process_host_unittest.cc
+++ b/chromium/content/browser/renderer_host/render_process_host_unittest.cc
@@ -15,7 +15,7 @@ TEST_F(RenderProcessHostUnitTest, GuestsAreNotSuitableHosts) {
GURL test_url("http://foo.com");
MockRenderProcessHost guest_host(browser_context());
- guest_host.SetIsGuest(true);
+ guest_host.set_is_isolated_guest(true);
EXPECT_FALSE(RenderProcessHostImpl::IsSuitableHost(
&guest_host, browser_context(), test_url));
diff --git a/chromium/content/browser/renderer_host/render_sandbox_host_linux.cc b/chromium/content/browser/renderer_host/render_sandbox_host_linux.cc
index 19cbc5b4b35..10f3fea0b6b 100644
--- a/chromium/content/browser/renderer_host/render_sandbox_host_linux.cc
+++ b/chromium/content/browser/renderer_host/render_sandbox_host_linux.cc
@@ -4,675 +4,16 @@
#include "content/browser/renderer_host/render_sandbox_host_linux.h"
-#include <fcntl.h>
-#include <fontconfig/fontconfig.h>
-#include <stdint.h>
-#include <sys/poll.h>
#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/uio.h>
-#include <time.h>
-#include <unistd.h>
-#include <vector>
-
-#include "base/command_line.h"
-#include "base/linux_util.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/shared_memory.h"
#include "base/memory/singleton.h"
-#include "base/pickle.h"
#include "base/posix/eintr_wrapper.h"
-#include "base/posix/unix_domain_socket_linux.h"
-#include "base/process/launch.h"
-#include "base/process/process_metrics.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "content/child/webkitplatformsupport_impl.h"
-#include "content/common/font_config_ipc_linux.h"
-#include "content/common/sandbox_linux/sandbox_linux.h"
-#include "content/common/set_process_title.h"
-#include "content/public/common/content_switches.h"
-#include "skia/ext/skia_utils_base.h"
-#include "third_party/WebKit/public/platform/linux/WebFontInfo.h"
-#include "third_party/WebKit/public/web/WebKit.h"
-#include "third_party/npapi/bindings/npapi_extensions.h"
-#include "third_party/skia/include/ports/SkFontConfigInterface.h"
-#include "ui/gfx/font_render_params_linux.h"
-
-using blink::WebCString;
-using blink::WebFontInfo;
-using blink::WebUChar;
-using blink::WebUChar32;
namespace content {
-// http://code.google.com/p/chromium/wiki/LinuxSandboxIPC
-
-// BEWARE: code in this file run across *processes* (not just threads).
-
-// This code runs in a child process
-class SandboxIPCProcess {
- public:
- // lifeline_fd: this is the read end of a pipe which the browser process
- // holds the other end of. If the browser process dies, its descriptors are
- // closed and we will noticed an EOF on the pipe. That's our signal to exit.
- // browser_socket: the browser's end of the sandbox IPC socketpair. From the
- // point of view of the renderer, it's talking to the browser but this
- // object actually services the requests.
- // sandbox_cmd: the path of the sandbox executable
- SandboxIPCProcess(int lifeline_fd, int browser_socket,
- std::string sandbox_cmd)
- : lifeline_fd_(lifeline_fd),
- browser_socket_(browser_socket) {
- if (!sandbox_cmd.empty()) {
- sandbox_cmd_.push_back(sandbox_cmd);
- sandbox_cmd_.push_back(base::kFindInodeSwitch);
- }
-
- // FontConfig doesn't provide a standard property to control subpixel
- // positioning, so we pass the current setting through to WebKit.
- WebFontInfo::setSubpixelPositioning(
- gfx::GetDefaultWebkitSubpixelPositioning());
-
- CommandLine& command_line = *CommandLine::ForCurrentProcess();
- command_line.AppendSwitchASCII(switches::kProcessType,
- switches::kSandboxIPCProcess);
-
- // Update the process title. The argv was already cached by the call to
- // SetProcessTitleFromCommandLine in content_main_runner.cc, so we can pass
- // NULL here (we don't have the original argv at this point).
- SetProcessTitleFromCommandLine(NULL);
- }
-
- ~SandboxIPCProcess();
-
- void Run() {
- struct pollfd pfds[2];
- pfds[0].fd = lifeline_fd_;
- pfds[0].events = POLLIN;
- pfds[1].fd = browser_socket_;
- pfds[1].events = POLLIN;
-
- int failed_polls = 0;
- for (;;) {
- const int r = HANDLE_EINTR(poll(pfds, 2, -1));
- if (r < 1) {
- LOG(WARNING) << "poll errno:" << errno;
- if (failed_polls++ == 3) {
- LOG(FATAL) << "poll failing. Sandbox host aborting.";
- return;
- }
- continue;
- }
-
- failed_polls = 0;
-
- if (pfds[0].revents) {
- // our parent died so we should too.
- _exit(0);
- }
-
- if (pfds[1].revents) {
- HandleRequestFromRenderer(browser_socket_);
- }
- }
- }
-
- private:
- void EnsureWebKitInitialized();
-
- // ---------------------------------------------------------------------------
- // Requests from the renderer...
-
- void HandleRequestFromRenderer(int fd) {
- std::vector<int> fds;
-
- // A FontConfigIPC::METHOD_MATCH message could be kMaxFontFamilyLength
- // bytes long (this is the largest message type).
- // 128 bytes padding are necessary so recvmsg() does not return MSG_TRUNC
- // error for a maximum length message.
- char buf[FontConfigIPC::kMaxFontFamilyLength + 128];
-
- const ssize_t len = UnixDomainSocket::RecvMsg(fd, buf, sizeof(buf), &fds);
- if (len == -1) {
- // TODO: should send an error reply, or the sender might block forever.
- NOTREACHED()
- << "Sandbox host message is larger than kMaxFontFamilyLength";
- return;
- }
- if (fds.empty())
- return;
-
- Pickle pickle(buf, len);
- PickleIterator iter(pickle);
-
- int kind;
- if (!pickle.ReadInt(&iter, &kind))
- goto error;
-
- if (kind == FontConfigIPC::METHOD_MATCH) {
- HandleFontMatchRequest(fd, pickle, iter, fds);
- } else if (kind == FontConfigIPC::METHOD_OPEN) {
- HandleFontOpenRequest(fd, pickle, iter, fds);
- } else if (kind == LinuxSandbox::METHOD_GET_FONT_FAMILY_FOR_CHAR) {
- HandleGetFontFamilyForChar(fd, pickle, iter, fds);
- } else if (kind == LinuxSandbox::METHOD_LOCALTIME) {
- HandleLocaltime(fd, pickle, iter, fds);
- } else if (kind == LinuxSandbox::METHOD_GET_CHILD_WITH_INODE) {
- HandleGetChildWithInode(fd, pickle, iter, fds);
- } else if (kind == LinuxSandbox::METHOD_GET_STYLE_FOR_STRIKE) {
- HandleGetStyleForStrike(fd, pickle, iter, fds);
- } else if (kind == LinuxSandbox::METHOD_MAKE_SHARED_MEMORY_SEGMENT) {
- HandleMakeSharedMemorySegment(fd, pickle, iter, fds);
- } else if (kind == LinuxSandbox::METHOD_MATCH_WITH_FALLBACK) {
- HandleMatchWithFallback(fd, pickle, iter, fds);
- }
-
- error:
- for (std::vector<int>::const_iterator
- i = fds.begin(); i != fds.end(); ++i) {
- close(*i);
- }
- }
-
- int FindOrAddPath(const SkString& path) {
- int count = paths_.count();
- for (int i = 0; i < count; ++i) {
- if (path == *paths_[i])
- return i;
- }
- *paths_.append() = new SkString(path);
- return count;
- }
-
- void HandleFontMatchRequest(int fd, const Pickle& pickle, PickleIterator iter,
- std::vector<int>& fds) {
- uint32_t requested_style;
- std::string family;
- if (!pickle.ReadString(&iter, &family) ||
- !pickle.ReadUInt32(&iter, &requested_style))
- return;
-
- SkFontConfigInterface::FontIdentity result_identity;
- SkString result_family;
- SkTypeface::Style result_style;
- SkFontConfigInterface* fc =
- SkFontConfigInterface::GetSingletonDirectInterface();
- const bool r = fc->matchFamilyName(
- family.c_str(), static_cast<SkTypeface::Style>(requested_style),
- &result_identity, &result_family, &result_style);
-
- Pickle reply;
- if (!r) {
- reply.WriteBool(false);
- } else {
- // Stash away the returned path, so we can give it an ID (index)
- // which will later be given to us in a request to open the file.
- int index = FindOrAddPath(result_identity.fString);
- result_identity.fID = static_cast<uint32_t>(index);
-
- reply.WriteBool(true);
- skia::WriteSkString(&reply, result_family);
- skia::WriteSkFontIdentity(&reply, result_identity);
- reply.WriteUInt32(result_style);
- }
- SendRendererReply(fds, reply, -1);
- }
-
- void HandleFontOpenRequest(int fd, const Pickle& pickle, PickleIterator iter,
- std::vector<int>& fds) {
- uint32_t index;
- if (!pickle.ReadUInt32(&iter, &index))
- return;
- if (index >= static_cast<uint32_t>(paths_.count()))
- return;
- const int result_fd = open(paths_[index]->c_str(), O_RDONLY);
-
- Pickle reply;
- if (result_fd == -1) {
- reply.WriteBool(false);
- } else {
- reply.WriteBool(true);
- }
-
- // The receiver will have its own access to the file, so we will close it
- // after this send.
- SendRendererReply(fds, reply, result_fd);
-
- if (result_fd >= 0) {
- int err = IGNORE_EINTR(close(result_fd));
- DCHECK(!err);
- }
- }
-
- void HandleGetFontFamilyForChar(int fd, const Pickle& pickle,
- PickleIterator iter,
- std::vector<int>& fds) {
- // The other side of this call is
- // chrome/renderer/renderer_sandbox_support_linux.cc
-
- EnsureWebKitInitialized();
- WebUChar32 c;
- if (!pickle.ReadInt(&iter, &c))
- return;
-
- std::string preferred_locale;
- if (!pickle.ReadString(&iter, &preferred_locale))
- return;
-
- blink::WebFontFamily family;
- WebFontInfo::familyForChar(c, preferred_locale.c_str(), &family);
-
- Pickle reply;
- if (family.name.data()) {
- reply.WriteString(family.name.data());
- } else {
- reply.WriteString(std::string());
- }
- reply.WriteBool(family.isBold);
- reply.WriteBool(family.isItalic);
- SendRendererReply(fds, reply, -1);
- }
-
- void HandleGetStyleForStrike(int fd, const Pickle& pickle,
- PickleIterator iter,
- std::vector<int>& fds) {
- std::string family;
- int sizeAndStyle;
-
- if (!pickle.ReadString(&iter, &family) ||
- !pickle.ReadInt(&iter, &sizeAndStyle)) {
- return;
- }
-
- EnsureWebKitInitialized();
- blink::WebFontRenderStyle style;
- WebFontInfo::renderStyleForStrike(family.c_str(), sizeAndStyle, &style);
-
- Pickle reply;
- reply.WriteInt(style.useBitmaps);
- reply.WriteInt(style.useAutoHint);
- reply.WriteInt(style.useHinting);
- reply.WriteInt(style.hintStyle);
- reply.WriteInt(style.useAntiAlias);
- reply.WriteInt(style.useSubpixelRendering);
- reply.WriteInt(style.useSubpixelPositioning);
-
- SendRendererReply(fds, reply, -1);
- }
-
- void HandleLocaltime(int fd, const Pickle& pickle, PickleIterator iter,
- std::vector<int>& fds) {
- // The other side of this call is in zygote_main_linux.cc
-
- std::string time_string;
- if (!pickle.ReadString(&iter, &time_string) ||
- time_string.size() != sizeof(time_t)) {
- return;
- }
-
- time_t time;
- memcpy(&time, time_string.data(), sizeof(time));
- // We use localtime here because we need the tm_zone field to be filled
- // out. Since we are a single-threaded process, this is safe.
- const struct tm* expanded_time = localtime(&time);
-
- std::string result_string;
- const char* time_zone_string = "";
- if (expanded_time != NULL) {
- result_string = std::string(reinterpret_cast<const char*>(expanded_time),
- sizeof(struct tm));
- time_zone_string = expanded_time->tm_zone;
- }
-
- Pickle reply;
- reply.WriteString(result_string);
- reply.WriteString(time_zone_string);
- SendRendererReply(fds, reply, -1);
- }
-
- void HandleGetChildWithInode(int fd, const Pickle& pickle,
- PickleIterator iter,
- std::vector<int>& fds) {
- // The other side of this call is in zygote_main_linux.cc
- if (sandbox_cmd_.empty()) {
- LOG(ERROR) << "Not in the sandbox, this should not be called";
- return;
- }
-
- uint64_t inode;
- if (!pickle.ReadUInt64(&iter, &inode))
- return;
-
- base::ProcessId pid = 0;
- std::string inode_output;
-
- std::vector<std::string> sandbox_cmd = sandbox_cmd_;
- sandbox_cmd.push_back(base::Int64ToString(inode));
- CommandLine get_inode_cmd(sandbox_cmd);
- if (base::GetAppOutput(get_inode_cmd, &inode_output))
- base::StringToInt(inode_output, &pid);
-
- if (!pid) {
- // Even though the pid is invalid, we still need to reply to the zygote
- // and not just return here.
- LOG(ERROR) << "Could not get pid";
- }
-
- Pickle reply;
- reply.WriteInt(pid);
- SendRendererReply(fds, reply, -1);
- }
-
- void HandleMakeSharedMemorySegment(int fd, const Pickle& pickle,
- PickleIterator iter,
- std::vector<int>& fds) {
- base::SharedMemoryCreateOptions options;
- uint32_t size;
- if (!pickle.ReadUInt32(&iter, &size))
- return;
- options.size = size;
- if (!pickle.ReadBool(&iter, &options.executable))
- return;
- int shm_fd = -1;
- base::SharedMemory shm;
- if (shm.Create(options))
- shm_fd = shm.handle().fd;
- Pickle reply;
- SendRendererReply(fds, reply, shm_fd);
- }
-
- void HandleMatchWithFallback(int fd, const Pickle& pickle,
- PickleIterator iter,
- std::vector<int>& fds) {
- // Unlike the other calls, for which we are an indirection in front of
- // WebKit or Skia, this call is always made via this sandbox helper
- // process. Therefore the fontconfig code goes in here directly.
-
- std::string face;
- bool is_bold, is_italic;
- uint32 charset;
-
- if (!pickle.ReadString(&iter, &face) ||
- face.empty() ||
- !pickle.ReadBool(&iter, &is_bold) ||
- !pickle.ReadBool(&iter, &is_italic) ||
- !pickle.ReadUInt32(&iter, &charset)) {
- return;
- }
-
- FcLangSet* langset = FcLangSetCreate();
- MSCharSetToFontconfig(langset, charset);
-
- FcPattern* pattern = FcPatternCreate();
- // TODO(agl): FC_FAMILy needs to change
- FcPatternAddString(pattern, FC_FAMILY, (FcChar8*) face.c_str());
- if (is_bold)
- FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
- if (is_italic)
- FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
- FcPatternAddLangSet(pattern, FC_LANG, langset);
- FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
- FcConfigSubstitute(NULL, pattern, FcMatchPattern);
- FcDefaultSubstitute(pattern);
-
- FcResult result;
- FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
- int font_fd = -1;
- int good_enough_index = -1;
- bool good_enough_index_set = false;
-
- if (font_set) {
- for (int i = 0; i < font_set->nfont; ++i) {
- FcPattern* current = font_set->fonts[i];
-
- // Older versions of fontconfig have a bug where they cannot select
- // only scalable fonts so we have to manually filter the results.
- FcBool is_scalable;
- if (FcPatternGetBool(current, FC_SCALABLE, 0,
- &is_scalable) != FcResultMatch ||
- !is_scalable) {
- continue;
- }
-
- FcChar8* c_filename;
- if (FcPatternGetString(current, FC_FILE, 0, &c_filename) !=
- FcResultMatch) {
- continue;
- }
-
- // We only want to return sfnt (TrueType) based fonts. We don't have a
- // very good way of detecting this so we'll filter based on the
- // filename.
- bool is_sfnt = false;
- static const char kSFNTExtensions[][5] = {
- ".ttf", ".otc", ".TTF", ".ttc", ""
- };
- const size_t filename_len = strlen(reinterpret_cast<char*>(c_filename));
- for (unsigned j = 0; ; j++) {
- if (kSFNTExtensions[j][0] == 0) {
- // None of the extensions matched.
- break;
- }
- const size_t ext_len = strlen(kSFNTExtensions[j]);
- if (filename_len > ext_len &&
- memcmp(c_filename + filename_len - ext_len,
- kSFNTExtensions[j], ext_len) == 0) {
- is_sfnt = true;
- break;
- }
- }
-
- if (!is_sfnt)
- continue;
-
- // This font is good enough to pass muster, but we might be able to do
- // better with subsequent ones.
- if (!good_enough_index_set) {
- good_enough_index = i;
- good_enough_index_set = true;
- }
-
- FcValue matrix;
- bool have_matrix = FcPatternGet(current, FC_MATRIX, 0, &matrix) == 0;
-
- if (is_italic && have_matrix) {
- // we asked for an italic font, but fontconfig is giving us a
- // non-italic font with a transformation matrix.
- continue;
- }
-
- FcValue embolden;
- const bool have_embolden =
- FcPatternGet(current, FC_EMBOLDEN, 0, &embolden) == 0;
-
- if (is_bold && have_embolden) {
- // we asked for a bold font, but fontconfig gave us a non-bold font
- // and asked us to apply fake bolding.
- continue;
- }
-
- font_fd = open(reinterpret_cast<char*>(c_filename), O_RDONLY);
- if (font_fd >= 0)
- break;
- }
- }
-
- if (font_fd == -1 && good_enough_index_set) {
- // We didn't find a font that we liked, so we fallback to something
- // acceptable.
- FcPattern* current = font_set->fonts[good_enough_index];
- FcChar8* c_filename;
- FcPatternGetString(current, FC_FILE, 0, &c_filename);
- font_fd = open(reinterpret_cast<char*>(c_filename), O_RDONLY);
- }
-
- if (font_set)
- FcFontSetDestroy(font_set);
- FcPatternDestroy(pattern);
-
- Pickle reply;
- SendRendererReply(fds, reply, font_fd);
-
- if (font_fd >= 0) {
- if (IGNORE_EINTR(close(font_fd)) < 0)
- PLOG(ERROR) << "close";
- }
- }
-
- // MSCharSetToFontconfig translates a Microsoft charset identifier to a
- // fontconfig language set by appending to |langset|.
- static void MSCharSetToFontconfig(FcLangSet* langset, unsigned fdwCharSet) {
- // We have need to translate raw fdwCharSet values into terms that
- // fontconfig can understand. (See the description of fdwCharSet in the MSDN
- // documentation for CreateFont:
- // http://msdn.microsoft.com/en-us/library/dd183499(VS.85).aspx )
- //
- // Although the argument is /called/ 'charset', the actual values conflate
- // character sets (which are sets of Unicode code points) and character
- // encodings (which are algorithms for turning a series of bits into a
- // series of code points.) Sometimes the values will name a language,
- // sometimes they'll name an encoding. In the latter case I'm assuming that
- // they mean the set of code points in the domain of that encoding.
- //
- // fontconfig deals with ISO 639-1 language codes:
- // http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
- //
- // So, for each of the documented fdwCharSet values I've had to take a
- // guess at the set of ISO 639-1 languages intended.
-
- switch (fdwCharSet) {
- case NPCharsetAnsi:
- // These values I don't really know what to do with, so I'm going to map
- // them to English also.
- case NPCharsetDefault:
- case NPCharsetMac:
- case NPCharsetOEM:
- case NPCharsetSymbol:
- FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("en"));
- break;
- case NPCharsetBaltic:
- // The three baltic languages.
- FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("et"));
- FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("lv"));
- FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("lt"));
- break;
- // TODO(jungshik): Would we be better off mapping Big5 to zh-tw
- // and GB2312 to zh-cn? Fontconfig has 4 separate orthography
- // files (zh-{cn,tw,hk,mo}.
- case NPCharsetChineseBIG5:
- case NPCharsetGB2312:
- FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("zh"));
- break;
- case NPCharsetEastEurope:
- // A scattering of eastern European languages.
- FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("pl"));
- FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("cs"));
- FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("sk"));
- FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("hu"));
- FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("hr"));
- break;
- case NPCharsetGreek:
- FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("el"));
- break;
- case NPCharsetHangul:
- case NPCharsetJohab:
- // Korean
- FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ko"));
- break;
- case NPCharsetRussian:
- FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ru"));
- break;
- case NPCharsetShiftJIS:
- // Japanese
- FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ja"));
- break;
- case NPCharsetTurkish:
- FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("tr"));
- break;
- case NPCharsetVietnamese:
- FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("vi"));
- break;
- case NPCharsetArabic:
- FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ar"));
- break;
- case NPCharsetHebrew:
- FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("he"));
- break;
- case NPCharsetThai:
- FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("th"));
- break;
- // default:
- // Don't add any languages in that case that we don't recognise the
- // constant.
- }
- }
-
- void SendRendererReply(const std::vector<int>& fds, const Pickle& reply,
- int reply_fd) {
- struct msghdr msg;
- memset(&msg, 0, sizeof(msg));
- struct iovec iov = {const_cast<void*>(reply.data()), reply.size()};
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
-
- char control_buffer[CMSG_SPACE(sizeof(int))];
-
- if (reply_fd != -1) {
- struct stat st;
- if (fstat(reply_fd, &st) == 0 && S_ISDIR(st.st_mode)) {
- LOG(FATAL) << "Tried to send a directory descriptor over sandbox IPC";
- // We must never send directory descriptors to a sandboxed process
- // because they can use openat with ".." elements in the path in order
- // to escape the sandbox and reach the real filesystem.
- }
-
- struct cmsghdr *cmsg;
- msg.msg_control = control_buffer;
- msg.msg_controllen = sizeof(control_buffer);
- cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(int));
- memcpy(CMSG_DATA(cmsg), &reply_fd, sizeof(reply_fd));
- msg.msg_controllen = cmsg->cmsg_len;
- }
-
- if (HANDLE_EINTR(sendmsg(fds[0], &msg, MSG_DONTWAIT)) < 0)
- PLOG(ERROR) << "sendmsg";
- }
-
- // ---------------------------------------------------------------------------
-
- const int lifeline_fd_;
- const int browser_socket_;
- std::vector<std::string> sandbox_cmd_;
- scoped_ptr<WebKitPlatformSupportImpl> webkit_platform_support_;
- SkTDArray<SkString*> paths_;
-};
-
-SandboxIPCProcess::~SandboxIPCProcess() {
- paths_.deleteAll();
- if (webkit_platform_support_)
- blink::shutdownWithoutV8();
-}
-
-void SandboxIPCProcess::EnsureWebKitInitialized() {
- if (webkit_platform_support_)
- return;
- webkit_platform_support_.reset(new WebKitPlatformSupportImpl);
- blink::initializeWithoutV8(webkit_platform_support_.get());
-}
-
-// -----------------------------------------------------------------------------
-
// Runs on the main thread at startup.
RenderSandboxHostLinux::RenderSandboxHostLinux()
- : initialized_(false),
- renderer_socket_(0),
- childs_lifeline_fd_(0),
- pid_(0) {
+ : initialized_(false), renderer_socket_(0), childs_lifeline_fd_(0) {
}
// static
@@ -680,7 +21,7 @@ RenderSandboxHostLinux* RenderSandboxHostLinux::GetInstance() {
return Singleton<RenderSandboxHostLinux>::get();
}
-void RenderSandboxHostLinux::Init(const std::string& sandbox_path) {
+void RenderSandboxHostLinux::Init() {
DCHECK(!initialized_);
initialized_ = true;
@@ -688,52 +29,45 @@ void RenderSandboxHostLinux::Init(const std::string& sandbox_path) {
// We use SOCK_SEQPACKET rather than SOCK_DGRAM to prevent the renderer from
// sending datagrams to other sockets on the system. The sandbox may prevent
// the renderer from calling socket() to create new sockets, but it'll still
- // inherit some sockets. With PF_UNIX+SOCK_DGRAM, it can call sendmsg to send
+ // inherit some sockets. With AF_UNIX+SOCK_DGRAM, it can call sendmsg to send
// a datagram to any (abstract) socket on the same system. With
// SOCK_SEQPACKET, this is prevented.
-#if defined(OS_FREEBSD) || defined(OS_OPENBSD)
- // The BSDs often don't support SOCK_SEQPACKET yet, so fall back to
- // SOCK_DGRAM if necessary.
- if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds) != 0)
- CHECK(socketpair(AF_UNIX, SOCK_DGRAM, 0, fds) == 0);
-#else
CHECK(socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds) == 0);
-#endif
renderer_socket_ = fds[0];
+ // The SandboxIPC client is not expected to read from |renderer_socket_|.
+ // Instead, it reads from a temporary socket sent with the request.
+ PCHECK(0 == shutdown(renderer_socket_, SHUT_RD)) << "shutdown";
+
const int browser_socket = fds[1];
+ // The SandboxIPC handler is not expected to write to |browser_socket|.
+ // Instead, it replies on a temporary socket provided by the caller.
+ PCHECK(0 == shutdown(browser_socket, SHUT_WR)) << "shutdown";
int pipefds[2];
CHECK(0 == pipe(pipefds));
const int child_lifeline_fd = pipefds[0];
childs_lifeline_fd_ = pipefds[1];
- // We need to be monothreaded before we fork().
-#if !defined(TOOLKIT_GTK)
- // Exclude gtk port as TestSuite in base/tests/test_suite.cc is calling
- // gtk_init.
- // TODO(oshima): Remove ifdef when above issues are resolved.
- DCHECK_EQ(1, base::GetNumberOfThreads(base::GetCurrentProcessHandle()));
-#endif
- pid_ = fork();
- if (pid_ == 0) {
- if (IGNORE_EINTR(close(fds[0])) < 0)
- DPLOG(ERROR) << "close";
- if (IGNORE_EINTR(close(pipefds[1])) < 0)
- DPLOG(ERROR) << "close";
+ ipc_handler_.reset(
+ new SandboxIPCHandler(child_lifeline_fd, browser_socket));
+ ipc_thread_.reset(
+ new base::DelegateSimpleThread(ipc_handler_.get(), "sandbox_ipc_thread"));
+ ipc_thread_->Start();
+}
- SandboxIPCProcess handler(child_lifeline_fd, browser_socket, sandbox_path);
- handler.Run();
- _exit(0);
- }
+bool RenderSandboxHostLinux::ShutdownIPCChannel() {
+ return IGNORE_EINTR(close(childs_lifeline_fd_)) == 0;
}
RenderSandboxHostLinux::~RenderSandboxHostLinux() {
if (initialized_) {
+ if (!ShutdownIPCChannel())
+ LOG(ERROR) << "ShutdownIPCChannel failed";
if (IGNORE_EINTR(close(renderer_socket_)) < 0)
PLOG(ERROR) << "close";
- if (IGNORE_EINTR(close(childs_lifeline_fd_)) < 0)
- PLOG(ERROR) << "close";
+
+ ipc_thread_->Join();
}
}
diff --git a/chromium/content/browser/renderer_host/render_sandbox_host_linux.h b/chromium/content/browser/renderer_host/render_sandbox_host_linux.h
index d9f7edb39e0..c7f4aeb01f2 100644
--- a/chromium/content/browser/renderer_host/render_sandbox_host_linux.h
+++ b/chromium/content/browser/renderer_host/render_sandbox_host_linux.h
@@ -2,14 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// http://code.google.com/p/chromium/wiki/LinuxSandboxIPC
-
#ifndef CONTENT_BROWSER_RENDERER_HOST_RENDER_SANDBOX_HOST_LINUX_H_
#define CONTENT_BROWSER_RENDERER_HOST_RENDER_SANDBOX_HOST_LINUX_H_
#include <string>
#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/simple_thread.h"
+#include "content/browser/renderer_host/sandbox_ipc_linux.h"
#include "content/common/content_export.h"
template <typename T> struct DefaultSingletonTraits;
@@ -29,11 +30,7 @@ class CONTENT_EXPORT RenderSandboxHostLinux {
DCHECK(initialized_);
return renderer_socket_;
}
- pid_t pid() const {
- DCHECK(initialized_);
- return pid_;
- }
- void Init(const std::string& sandbox_path);
+ void Init();
private:
friend struct DefaultSingletonTraits<RenderSandboxHostLinux>;
@@ -41,12 +38,16 @@ class CONTENT_EXPORT RenderSandboxHostLinux {
RenderSandboxHostLinux();
~RenderSandboxHostLinux();
+ bool ShutdownIPCChannel();
+
// Whether Init() has been called yet.
bool initialized_;
int renderer_socket_;
int childs_lifeline_fd_;
- pid_t pid_;
+
+ scoped_ptr<SandboxIPCHandler> ipc_handler_;
+ scoped_ptr<base::DelegateSimpleThread> ipc_thread_;
DISALLOW_COPY_AND_ASSIGN(RenderSandboxHostLinux);
};
diff --git a/chromium/content/browser/renderer_host/render_view_host_browsertest.cc b/chromium/content/browser/renderer_host/render_view_host_browsertest.cc
index 386ff521afb..6015c04306e 100644
--- a/chromium/content/browser/renderer_host/render_view_host_browsertest.cc
+++ b/chromium/content/browser/renderer_host/render_view_host_browsertest.cc
@@ -6,18 +6,20 @@
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "base/values.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/common/view_messages.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/content_paths.h"
+#include "content/public/common/frame_navigate_params.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/shell/browser/shell.h"
-#include "content/test/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
+#include "net/base/filename_util.h"
#include "net/base/host_port_pair.h"
-#include "net/base/net_util.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
namespace content {
@@ -100,19 +102,63 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostTest, BasicRenderFrameHost) {
GURL test_url = embedded_test_server()->GetURL("/simple_page.html");
NavigateToURL(shell(), test_url);
- RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
- shell()->web_contents()->GetRenderViewHost());
- EXPECT_TRUE(rvh->main_render_frame_host());
+ FrameTreeNode* old_root = static_cast<WebContentsImpl*>(
+ shell()->web_contents())->GetFrameTree()->root();
+ EXPECT_TRUE(old_root->current_frame_host());
ShellAddedObserver new_shell_observer;
EXPECT_TRUE(ExecuteScript(shell()->web_contents(), "window.open();"));
Shell* new_shell = new_shell_observer.GetShell();
- RenderViewHostImpl* new_rvh = static_cast<RenderViewHostImpl*>(
- new_shell->web_contents()->GetRenderViewHost());
+ FrameTreeNode* new_root = static_cast<WebContentsImpl*>(
+ new_shell->web_contents())->GetFrameTree()->root();
- EXPECT_TRUE(new_rvh->main_render_frame_host());
- EXPECT_NE(rvh->main_render_frame_host()->routing_id(),
- new_rvh->main_render_frame_host()->routing_id());
+ EXPECT_TRUE(new_root->current_frame_host());
+ EXPECT_NE(old_root->current_frame_host()->routing_id(),
+ new_root->current_frame_host()->routing_id());
+}
+
+IN_PROC_BROWSER_TEST_F(RenderViewHostTest, IsFocusedElementEditable) {
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+
+ GURL test_url = embedded_test_server()->GetURL("/touch_selection.html");
+ NavigateToURL(shell(), test_url);
+
+ RenderViewHost* rvh = shell()->web_contents()->GetRenderViewHost();
+ EXPECT_FALSE(rvh->IsFocusedElementEditable());
+ EXPECT_TRUE(ExecuteScript(shell()->web_contents(), "focus_textfield();"));
+ EXPECT_TRUE(rvh->IsFocusedElementEditable());
+}
+
+IN_PROC_BROWSER_TEST_F(RenderViewHostTest, ReleaseSessionOnCloseACK) {
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+ GURL test_url = embedded_test_server()->GetURL(
+ "/access-session-storage.html");
+ NavigateToURL(shell(), test_url);
+
+ // Make a new Shell, a seperate tab with it's own session namespace and
+ // have it start loading a url but still be in progress.
+ ShellAddedObserver new_shell_observer;
+ EXPECT_TRUE(ExecuteScript(shell()->web_contents(), "window.open();"));
+ Shell* new_shell = new_shell_observer.GetShell();
+ new_shell->LoadURL(test_url);
+ RenderViewHost* rvh = new_shell->web_contents()->GetRenderViewHost();
+ SiteInstance* site_instance = rvh->GetSiteInstance();
+ scoped_refptr<SessionStorageNamespace> session_namespace =
+ rvh->GetDelegate()->GetSessionStorageNamespace(site_instance);
+ EXPECT_FALSE(session_namespace->HasOneRef());
+
+ // Close it, or rather start the close operation. The session namespace
+ // should remain until RPH gets an ACK from the renderer about having
+ // closed the view.
+ new_shell->Close();
+ EXPECT_FALSE(session_namespace->HasOneRef());
+
+ // Do something that causes ipc queues to flush and tasks in
+ // flight to complete such that we should have received the ACK.
+ NavigateToURL(shell(), test_url);
+
+ // Verify we have the only remaining reference to the namespace.
+ EXPECT_TRUE(session_namespace->HasOneRef());
}
} // namespace content
diff --git a/chromium/content/browser/renderer_host/render_view_host_delegate.cc b/chromium/content/browser/renderer_host/render_view_host_delegate.cc
index 4786909c928..88e590b097f 100644
--- a/chromium/content/browser/renderer_host/render_view_host_delegate.cc
+++ b/chromium/content/browser/renderer_host/render_view_host_delegate.cc
@@ -13,26 +13,11 @@ RenderViewHostDelegateView* RenderViewHostDelegate::GetDelegateView() {
return NULL;
}
-RenderViewHostDelegate::RendererManagement*
-RenderViewHostDelegate::GetRendererManagementDelegate() {
- return NULL;
-}
-
bool RenderViewHostDelegate::OnMessageReceived(RenderViewHost* render_view_host,
const IPC::Message& message) {
return false;
}
-bool RenderViewHostDelegate::AddMessageToConsole(
- int32 level, const base::string16& message, int32 line_no,
- const base::string16& source_id) {
- return false;
-}
-
-const GURL& RenderViewHostDelegate::GetURL() const {
- return GURL::EmptyGURL();
-}
-
WebContents* RenderViewHostDelegate::GetAsWebContents() {
return NULL;
}
@@ -50,8 +35,17 @@ SessionStorageNamespace* RenderViewHostDelegate::GetSessionStorageNamespace(
return NULL;
}
+SessionStorageNamespaceMap
+RenderViewHostDelegate::GetSessionStorageNamespaceMap() {
+ return SessionStorageNamespaceMap();
+}
+
FrameTree* RenderViewHostDelegate::GetFrameTree() {
return NULL;
}
+bool RenderViewHostDelegate::IsNeverVisible() {
+ return false;
+}
+
} // namespace content
diff --git a/chromium/content/browser/renderer_host/render_view_host_delegate.h b/chromium/content/browser/renderer_host/render_view_host_delegate.h
index a258c367405..fd8aeae8625 100644
--- a/chromium/content/browser/renderer_host/render_view_host_delegate.h
+++ b/chromium/content/browser/renderer_host/render_view_host_delegate.h
@@ -9,11 +9,10 @@
#include "base/basictypes.h"
#include "base/callback.h"
-#include "base/i18n/rtl.h"
#include "base/process/kill.h"
#include "base/strings/string16.h"
+#include "content/browser/dom_storage/session_storage_namespace_impl.h"
#include "content/common/content_export.h"
-#include "content/public/common/javascript_message_type.h"
#include "content/public/common/media_stream_request.h"
#include "content/public/common/page_transition_types.h"
#include "net/base/load_states.h"
@@ -22,10 +21,8 @@
class GURL;
class SkBitmap;
-class WebKeyboardEvent;
struct ViewHostMsg_CreateWindow_Params;
-struct ViewHostMsg_DidFailProvisionalLoadWithError_Params;
-struct ViewHostMsg_FrameNavigate_Params;
+struct FrameHostMsg_DidCommitProvisionalLoad_Params;
struct ViewMsg_PostMessage_Params;
struct WebPreferences;
@@ -47,6 +44,7 @@ class Size;
namespace content {
class BrowserContext;
+class CrossSiteTransferringRequest;
class FrameTree;
class PageState;
class RenderViewHost;
@@ -55,14 +53,12 @@ class SessionStorageNamespace;
class SiteInstance;
class WebContents;
class WebContentsImpl;
-struct ContextMenuParams;
+struct AXEventNotificationDetails;
struct FileChooserParams;
struct GlobalRequestID;
struct NativeWebKeyboardEvent;
struct Referrer;
struct RendererPreferences;
-struct ResourceRedirectDetails;
-struct ResourceRequestDetails;
//
// RenderViewHostDelegate
@@ -77,54 +73,14 @@ struct ResourceRequestDetails;
// listener here to serve that need.
class CONTENT_EXPORT RenderViewHostDelegate {
public:
- // RendererManagerment -------------------------------------------------------
- // Functions for managing switching of Renderers. For WebContents, this is
- // implemented by the RenderFrameHostManager.
-
- class CONTENT_EXPORT RendererManagement {
- public:
- // Notification whether we should close the page, after an explicit call to
- // AttemptToClosePage. This is called before a cross-site request or before
- // a tab/window is closed (as indicated by the first parameter) to allow the
- // appropriate renderer to approve or deny the request. |proceed| indicates
- // whether the user chose to proceed. |proceed_time| is the time when the
- // request was allowed to proceed.
- virtual void ShouldClosePage(
- bool for_cross_site_transition,
- bool proceed,
- const base::TimeTicks& proceed_time) = 0;
-
- // The |pending_render_view_host| is ready to commit a page. The delegate
- // should ensure that the old RenderViewHost runs its unload handler first
- // and determine whether a RenderViewHost transfer is needed.
- virtual void OnCrossSiteResponse(
- RenderViewHost* pending_render_view_host,
- const GlobalRequestID& global_request_id,
- bool is_transfer,
- const std::vector<GURL>& transfer_url_chain,
- const Referrer& referrer,
- PageTransition page_transition,
- int64 frame_id,
- bool should_replace_current_entry) = 0;
-
- protected:
- virtual ~RendererManagement() {}
- };
-
- // ---------------------------------------------------------------------------
-
// Returns the current delegate associated with a feature. May return NULL if
// there is no corresponding delegate.
virtual RenderViewHostDelegateView* GetDelegateView();
- virtual RendererManagement* GetRendererManagementDelegate();
// This is used to give the delegate a chance to filter IPC messages.
virtual bool OnMessageReceived(RenderViewHost* render_view_host,
const IPC::Message& message);
- // Gets the URL that is currently being displayed, if there is one.
- virtual const GURL& GetURL() const;
-
// Return this object cast to a WebContents, if it is one. If the object is
// not a WebContents, returns NULL. DEPRECATED: Be sure to include brettw or
// jam as reviewers before you use this method. http://crbug.com/82582
@@ -151,118 +107,27 @@ class CONTENT_EXPORT RenderViewHostDelegate {
// RenderView is going to be destroyed
virtual void RenderViewDeleted(RenderViewHost* render_view_host) {}
- // The RenderView processed a redirect during a provisional load.
- //
- // TODO(creis): Remove this method and have the pre-rendering code listen to
- // WebContentsObserver::DidGetRedirectForResourceRequest instead.
- // See http://crbug.com/78512.
- virtual void DidRedirectProvisionalLoad(
- RenderViewHost* render_view_host,
- int32 page_id,
- const GURL& source_url,
- const GURL& target_url) {}
-
- // A provisional load in the RenderView failed.
- virtual void DidFailProvisionalLoadWithError(
- RenderViewHost* render_view_host,
- const ViewHostMsg_DidFailProvisionalLoadWithError_Params& params) {}
-
- // A response has been received for a resource request.
- virtual void DidGetResourceResponseStart(
- const ResourceRequestDetails& details) {}
-
- // A redirect was received while requesting a resource.
- virtual void DidGetRedirectForResourceRequest(
- const ResourceRedirectDetails& details) {}
-
- // The RenderView was navigated to a different page.
- virtual void DidNavigate(RenderViewHost* render_view_host,
- const ViewHostMsg_FrameNavigate_Params& params) {}
-
// The state for the page changed and should be updated.
virtual void UpdateState(RenderViewHost* render_view_host,
int32 page_id,
const PageState& state) {}
- // The page's title was changed and should be updated.
- virtual void UpdateTitle(RenderViewHost* render_view_host,
- int32 page_id,
- const base::string16& title,
- base::i18n::TextDirection title_direction) {}
-
- // The page's encoding was changed and should be updated.
- virtual void UpdateEncoding(RenderViewHost* render_view_host,
- const std::string& encoding) {}
-
// The destination URL has changed should be updated
virtual void UpdateTargetURL(int32 page_id, const GURL& url) {}
// The page is trying to close the RenderView's representation in the client.
virtual void Close(RenderViewHost* render_view_host) {}
- // The RenderViewHost has been swapped out.
- virtual void SwappedOut(RenderViewHost* render_view_host) {}
-
// The page is trying to move the RenderView's representation in the client.
virtual void RequestMove(const gfx::Rect& new_bounds) {}
- // The RenderView began loading a new page. This corresponds to WebKit's
- // notion of the throbber starting.
- virtual void DidStartLoading(RenderViewHost* render_view_host) {}
-
- // The RenderView stopped loading a page. This corresponds to WebKit's
- // notion of the throbber stopping.
- virtual void DidStopLoading(RenderViewHost* render_view_host) {}
-
// The pending page load was canceled.
virtual void DidCancelLoading() {}
- // The RenderView made progress loading a page's top frame.
- // |progress| is a value between 0 (nothing loaded) to 1.0 (top frame
- // entirely loaded).
- virtual void DidChangeLoadProgress(double progress) {}
-
- // The RenderView set its opener to null, disowning it for the lifetime of
- // the window.
- virtual void DidDisownOpener(RenderViewHost* rvh) {}
-
- // Another page accessed the initial empty document of this RenderView,
- // which means it is no longer safe to display a pending URL without
- // risking a URL spoof.
- virtual void DidAccessInitialDocument() {}
-
// The RenderView's main frame document element is ready. This happens when
// the document has finished parsing.
virtual void DocumentAvailableInMainFrame(RenderViewHost* render_view_host) {}
- // The onload handler in the RenderView's main frame has completed.
- virtual void DocumentOnLoadCompletedInMainFrame(
- RenderViewHost* render_view_host,
- int32 page_id) {}
-
- // The page wants to open a URL with the specified disposition.
- virtual void RequestOpenURL(RenderViewHost* rvh,
- const GURL& url,
- const Referrer& referrer,
- WindowOpenDisposition disposition,
- int64 source_frame_id,
- bool is_redirect,
- bool user_gesture) {}
-
- // The page wants to transfer the request to a new renderer.
- // |redirect_chain| contains any redirect URLs (excluding |url|) that happened
- // before the transfer.
- virtual void RequestTransferURL(
- const GURL& url,
- const std::vector<GURL>& redirect_chain,
- const Referrer& referrer,
- PageTransition page_transition,
- WindowOpenDisposition disposition,
- int64 source_frame_id,
- const GlobalRequestID& old_request_id,
- bool is_redirect,
- bool user_gesture) {}
-
// The page wants to close the active view in this tab.
virtual void RouteCloseEvent(RenderViewHost* rvh) {}
@@ -271,26 +136,6 @@ class CONTENT_EXPORT RenderViewHostDelegate {
RenderViewHost* rvh,
const ViewMsg_PostMessage_Params& params) {}
- // A javascript message, confirmation or prompt should be shown.
- virtual void RunJavaScriptMessage(RenderViewHost* rvh,
- const base::string16& message,
- const base::string16& default_prompt,
- const GURL& frame_url,
- JavaScriptMessageType type,
- IPC::Message* reply_msg,
- bool* did_suppress_message) {}
-
- virtual void RunBeforeUnloadConfirm(RenderViewHost* rvh,
- const base::string16& message,
- bool is_reload,
- IPC::Message* reply_msg) {}
-
- // A message was added to to the console.
- virtual bool AddMessageToConsole(int32 level,
- const base::string16& message,
- int32 line_no,
- const base::string16& source_id);
-
// Return a dummy RendererPreferences object that will be used by the renderer
// associated with the owning RenderViewHost.
virtual RendererPreferences GetRendererPrefs(
@@ -328,9 +173,6 @@ class CONTENT_EXPORT RenderViewHostDelegate {
uint64 upload_position,
uint64 upload_size) {}
- // Notification that a worker process has crashed.
- virtual void WorkerCrashed() {}
-
// The page wants the hosting window to activate/deactivate itself (it
// called the JavaScript window.focus()/blur() method).
virtual void Activate() {}
@@ -424,10 +266,6 @@ class CONTENT_EXPORT RenderViewHostDelegate {
// Show the newly created full screen widget. Similar to above.
virtual void ShowCreatedFullscreenWidget(int route_id) {}
- // A context menu should be shown, to be built using the context information
- // provided in the supplied params.
- virtual void ShowContextMenu(const ContextMenuParams& params) {}
-
// The render view has requested access to media devices listed in
// |request|, and the client should grant or deny that permission by
// calling |callback|.
@@ -440,6 +278,13 @@ class CONTENT_EXPORT RenderViewHostDelegate {
virtual SessionStorageNamespace* GetSessionStorageNamespace(
SiteInstance* instance);
+ // Returns a copy of the map of all session storage namespaces related
+ // to this view.
+ virtual SessionStorageNamespaceMap GetSessionStorageNamespaceMap();
+
+ // Returns true if the RenderViewHost will never be visible.
+ virtual bool IsNeverVisible();
+
// Returns the FrameTree the render view should use. Guaranteed to be constant
// for the lifetime of the render view.
//
@@ -447,6 +292,10 @@ class CONTENT_EXPORT RenderViewHostDelegate {
// created by the RenderViewHost.
virtual FrameTree* GetFrameTree();
+ // Invoked when an accessibility event is received from the renderer.
+ virtual void AccessibilityEventReceived(
+ const std::vector<AXEventNotificationDetails>& details) {}
+
protected:
virtual ~RenderViewHostDelegate() {}
};
diff --git a/chromium/content/browser/renderer_host/render_view_host_delegate_view.h b/chromium/content/browser/renderer_host/render_view_host_delegate_view.h
new file mode 100644
index 00000000000..fb2babdc87f
--- /dev/null
+++ b/chromium/content/browser/renderer_host/render_view_host_delegate_view.h
@@ -0,0 +1,82 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_RENDER_VIEW_HOST_DELEGATE_VIEW_H_
+#define CONTENT_BROWSER_RENDERER_HOST_RENDER_VIEW_HOST_DELEGATE_VIEW_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "content/common/content_export.h"
+#include "content/common/drag_event_source_info.h"
+#include "third_party/WebKit/public/web/WebDragOperation.h"
+
+class SkBitmap;
+
+namespace gfx {
+class ImageSkia;
+class Rect;
+class Vector2d;
+}
+
+namespace content {
+class RenderFrameHost;
+struct ContextMenuParams;
+struct DropData;
+struct MenuItem;
+
+// This class provides a way for the RenderViewHost to reach out to its
+// delegate's view. It only needs to be implemented by embedders if they don't
+// use the default WebContentsView implementations.
+class CONTENT_EXPORT RenderViewHostDelegateView {
+ public:
+ // A context menu should be shown, to be built using the context information
+ // provided in the supplied params.
+ virtual void ShowContextMenu(RenderFrameHost* render_frame_host,
+ const ContextMenuParams& params) {}
+
+ // Shows a popup menu with the specified items.
+ // This method should call RenderViewHost::DidSelectPopupMenuItem[s]() or
+ // RenderViewHost::DidCancelPopupMenu() based on the user action.
+ virtual void ShowPopupMenu(const gfx::Rect& bounds,
+ int item_height,
+ double item_font_size,
+ int selected_item,
+ const std::vector<MenuItem>& items,
+ bool right_aligned,
+ bool allow_multiple_selection) {};
+
+ // Hides a popup menu opened by ShowPopupMenu().
+ virtual void HidePopupMenu() {};
+
+ // The user started dragging content of the specified type within the
+ // RenderView. Contextual information about the dragged content is supplied
+ // by DropData. If the delegate's view cannot start the drag for /any/
+ // reason, it must inform the renderer that the drag has ended; otherwise,
+ // this results in bugs like http://crbug.com/157134.
+ virtual void StartDragging(const DropData& drop_data,
+ blink::WebDragOperationsMask allowed_ops,
+ const gfx::ImageSkia& image,
+ const gfx::Vector2d& image_offset,
+ const DragEventSourceInfo& event_info) {}
+
+ // The page wants to update the mouse cursor during a drag & drop operation.
+ // |operation| describes the current operation (none, move, copy, link.)
+ virtual void UpdateDragCursor(blink::WebDragOperation operation) {}
+
+ // Notification that view for this delegate got the focus.
+ virtual void GotFocus() {}
+
+ // Callback to inform the browser that the page is returning the focus to
+ // the browser's chrome. If reverse is true, it means the focus was
+ // retrieved by doing a Shift-Tab.
+ virtual void TakeFocus(bool reverse) {}
+
+ protected:
+ virtual ~RenderViewHostDelegateView() {}
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_RENDER_VIEW_HOST_DELEGATE_VIEW_H_
diff --git a/chromium/content/browser/renderer_host/render_view_host_factory.cc b/chromium/content/browser/renderer_host/render_view_host_factory.cc
index fe51c7efe69..5cffd134b8e 100644
--- a/chromium/content/browser/renderer_host/render_view_host_factory.cc
+++ b/chromium/content/browser/renderer_host/render_view_host_factory.cc
@@ -16,19 +16,17 @@ RenderViewHostFactory* RenderViewHostFactory::factory_ = NULL;
RenderViewHost* RenderViewHostFactory::Create(
SiteInstance* instance,
RenderViewHostDelegate* delegate,
- RenderFrameHostDelegate* frame_delegate,
RenderWidgetHostDelegate* widget_delegate,
int routing_id,
int main_frame_routing_id,
bool swapped_out,
bool hidden) {
if (factory_) {
- return factory_->CreateRenderViewHost(instance, delegate, frame_delegate,
- widget_delegate, routing_id,
- main_frame_routing_id, swapped_out);
+ return factory_->CreateRenderViewHost(instance, delegate, widget_delegate,
+ routing_id, main_frame_routing_id,
+ swapped_out);
}
- return new RenderViewHostImpl(instance, delegate, frame_delegate,
- widget_delegate, routing_id,
+ return new RenderViewHostImpl(instance, delegate, widget_delegate, routing_id,
main_frame_routing_id, swapped_out, hidden);
}
diff --git a/chromium/content/browser/renderer_host/render_view_host_factory.h b/chromium/content/browser/renderer_host/render_view_host_factory.h
index cf6a0f1b3fb..734f61fbbbe 100644
--- a/chromium/content/browser/renderer_host/render_view_host_factory.h
+++ b/chromium/content/browser/renderer_host/render_view_host_factory.h
@@ -27,7 +27,6 @@ class RenderViewHostFactory {
static RenderViewHost* Create(
SiteInstance* instance,
RenderViewHostDelegate* delegate,
- RenderFrameHostDelegate* frame_delegate,
RenderWidgetHostDelegate* widget_delegate,
int routing_id,
int main_frame_routing_id,
@@ -48,7 +47,6 @@ class RenderViewHostFactory {
virtual RenderViewHost* CreateRenderViewHost(
SiteInstance* instance,
RenderViewHostDelegate* delegate,
- RenderFrameHostDelegate* frame_delegate,
RenderWidgetHostDelegate* widget_delegate,
int routing_id,
int main_frame_routing_id,
diff --git a/chromium/content/browser/renderer_host/render_view_host_impl.cc b/chromium/content/browser/renderer_host/render_view_host_impl.cc
index 5c246dbb186..e14b364cadf 100644
--- a/chromium/content/browser/renderer_host/render_view_host_impl.cc
+++ b/chromium/content/browser/renderer_host/render_view_host_impl.cc
@@ -23,11 +23,11 @@
#include "base/time/time.h"
#include "base/values.h"
#include "cc/base/switches.h"
+#include "content/browser/accessibility/browser_accessibility_manager.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/cross_site_request_manager.h"
#include "content/browser/dom_storage/session_storage_namespace_impl.h"
#include "content/browser/frame_host/frame_tree.h"
-#include "content/browser/frame_host/render_frame_host_factory.h"
#include "content/browser/gpu/compositor_util.h"
#include "content/browser/gpu/gpu_data_manager_impl.h"
#include "content/browser/gpu/gpu_process_host.h"
@@ -35,30 +35,36 @@
#include "content/browser/host_zoom_map_impl.h"
#include "content/browser/loader/resource_dispatcher_host_impl.h"
#include "content/browser/renderer_host/dip_util.h"
+#include "content/browser/renderer_host/input/timeout_monitor.h"
#include "content/browser/renderer_host/media/audio_renderer_host.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/renderer_host/render_view_host_delegate.h"
+#include "content/browser/renderer_host/render_view_host_delegate_view.h"
+#include "content/browser/renderer_host/render_widget_host_view_base.h"
#include "content/common/accessibility_messages.h"
#include "content/common/browser_plugin/browser_plugin_messages.h"
-#include "content/common/desktop_notification_messages.h"
+#include "content/common/content_switches_internal.h"
#include "content/common/drag_messages.h"
+#include "content/common/frame_messages.h"
#include "content/common/input_messages.h"
#include "content/common/inter_process_time_ticks_converter.h"
+#include "content/common/mojo/mojo_service_names.h"
#include "content/common/speech_recognition_messages.h"
#include "content/common/swapped_out_messages.h"
#include "content/common/view_messages.h"
-#include "content/port/browser/render_view_host_delegate_view.h"
-#include "content/port/browser/render_widget_host_view_port.h"
+#include "content/common/web_ui_setup.mojom.h"
+#include "content/public/browser/ax_event_notification_details.h"
#include "content/public/browser/browser_accessibility_state.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_message_filter.h"
#include "content/public/browser/content_browser_client.h"
-#include "content/public/browser/dom_operation_notification_details.h"
#include "content/public/browser/native_web_keyboard_event.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
+#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_widget_host_iterator.h"
+#include "content/public/browser/storage_partition.h"
#include "content/public/browser/user_metrics.h"
#include "content/public/common/bindings_policy.h"
#include "content/public/common/content_constants.h"
@@ -66,28 +72,33 @@
#include "content/public/common/context_menu_params.h"
#include "content/public/common/drop_data.h"
#include "content/public/common/result_codes.h"
-#include "content/public/common/url_constants.h"
#include "content/public/common/url_utils.h"
+#include "net/base/filename_util.h"
#include "net/base/net_util.h"
#include "net/base/network_change_notifier.h"
#include "net/url_request/url_request_context_getter.h"
#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/accessibility/ax_tree.h"
#include "ui/base/touch/touch_device.h"
#include "ui/base/touch/touch_enabled.h"
#include "ui/base/ui_base_switches.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/native_widget_types.h"
+#include "ui/native_theme/native_theme_switches.h"
#include "ui/shell_dialogs/selected_file_info.h"
+#include "url/url_constants.h"
#include "webkit/browser/fileapi/isolated_context.h"
#if defined(OS_MACOSX)
#include "content/browser/renderer_host/popup_menu_helper_mac.h"
-#elif defined(OS_ANDROID)
-#include "content/browser/media/android/browser_media_player_manager.h"
#elif defined(OS_WIN)
#include "base/win/win_util.h"
#endif
+#if defined(ENABLE_BROWSER_CDMS)
+#include "content/browser/media/media_web_contents_observer.h"
+#endif
+
using base::TimeDelta;
using blink::WebConsoleMessage;
using blink::WebDragOperation;
@@ -100,25 +111,7 @@ using blink::WebPluginAction;
namespace content {
namespace {
-// Delay to wait on closing the WebContents for a beforeunload/unload handler to
-// fire.
-const int kUnloadTimeoutMS = 1000;
-
-// Translate a WebKit text direction into a base::i18n one.
-base::i18n::TextDirection WebTextDirectionToChromeTextDirection(
- blink::WebTextDirection dir) {
- switch (dir) {
- case blink::WebTextDirectionLeftToRight:
- return base::i18n::LEFT_TO_RIGHT;
- case blink::WebTextDirectionRightToLeft:
- return base::i18n::RIGHT_TO_LEFT;
- default:
- NOTREACHED();
- return base::i18n::UNKNOWN_DIRECTION;
- }
-}
-
-#if defined(OS_WIN) && defined(USE_AURA)
+#if defined(OS_WIN)
const int kVirtualKeyboardDisplayWaitTimeoutMs = 100;
const int kMaxVirtualKeyboardDisplayRetries = 5;
@@ -143,10 +136,23 @@ void DismissVirtualKeyboardTask() {
} // namespace
+// static
+const int RenderViewHostImpl::kUnloadTimeoutMS = 1000;
+
///////////////////////////////////////////////////////////////////////////////
// RenderViewHost, public:
// static
+bool RenderViewHostImpl::IsRVHStateActive(RenderViewHostImplState rvh_state) {
+ if (rvh_state == STATE_DEFAULT ||
+ rvh_state == STATE_WAITING_FOR_UNLOAD_ACK ||
+ rvh_state == STATE_WAITING_FOR_COMMIT ||
+ rvh_state == STATE_WAITING_FOR_CLOSE)
+ return true;
+ return false;
+}
+
+// static
RenderViewHost* RenderViewHost::FromID(int render_process_id,
int render_view_id) {
return RenderViewHostImpl::FromID(render_process_id, render_view_id);
@@ -158,14 +164,6 @@ RenderViewHost* RenderViewHost::From(RenderWidgetHost* rwh) {
return static_cast<RenderViewHostImpl*>(RenderWidgetHostImpl::From(rwh));
}
-// static
-void RenderViewHost::FilterURL(const RenderProcessHost* process,
- bool empty_allowed,
- GURL* url) {
- RenderViewHostImpl::FilterURL(ChildProcessSecurityPolicyImpl::GetInstance(),
- process, empty_allowed, url);
-}
-
///////////////////////////////////////////////////////////////////////////////
// RenderViewHostImpl, public:
@@ -182,7 +180,6 @@ RenderViewHostImpl* RenderViewHostImpl::FromID(int render_process_id,
RenderViewHostImpl::RenderViewHostImpl(
SiteInstance* instance,
RenderViewHostDelegate* delegate,
- RenderFrameHostDelegate* frame_delegate,
RenderWidgetHostDelegate* widget_delegate,
int routing_id,
int main_frame_routing_id,
@@ -192,42 +189,33 @@ RenderViewHostImpl::RenderViewHostImpl(
instance->GetProcess(),
routing_id,
hidden),
+ frames_ref_count_(0),
delegate_(delegate),
instance_(static_cast<SiteInstanceImpl*>(instance)),
waiting_for_drag_context_response_(false),
enabled_bindings_(0),
navigations_suspended_(false),
- has_accessed_initial_document_(false),
- is_swapped_out_(swapped_out),
- is_subframe_(false),
- main_frame_id_(-1),
+ main_frame_routing_id_(main_frame_routing_id),
run_modal_reply_msg_(NULL),
run_modal_opener_id_(MSG_ROUTING_NONE),
is_waiting_for_beforeunload_ack_(false),
- is_waiting_for_unload_ack_(false),
- has_timed_out_on_unload_(false),
unload_ack_is_for_cross_site_transition_(false),
- are_javascript_messages_suppressed_(false),
sudden_termination_allowed_(false),
render_view_termination_status_(base::TERMINATION_STATUS_STILL_RUNNING),
- virtual_keyboard_requested_(false) {
+ virtual_keyboard_requested_(false),
+ weak_factory_(this),
+ is_focused_element_editable_(false) {
DCHECK(instance_.get());
CHECK(delegate_); // http://crbug.com/82827
- if (main_frame_routing_id == MSG_ROUTING_NONE)
- main_frame_routing_id = GetProcess()->GetNextRoutingID();
-
- main_render_frame_host_ = RenderFrameHostFactory::Create(
- this, frame_delegate, delegate_->GetFrameTree(),
- delegate_->GetFrameTree()->root(),
- main_frame_routing_id, is_swapped_out_);
- delegate_->GetFrameTree()->root()->set_render_frame_host(
- main_render_frame_host_.get(), false);
-
GetProcess()->EnableSendQueue();
- if (!swapped_out)
+ if (swapped_out) {
+ rvh_state_ = STATE_SWAPPED_OUT;
+ } else {
+ rvh_state_ = STATE_DEFAULT;
instance_->increment_active_view_count();
+ }
if (ResourceDispatcherHostImpl::Get()) {
BrowserThread::PostTask(
@@ -237,9 +225,12 @@ RenderViewHostImpl::RenderViewHostImpl(
GetProcess()->GetID(), GetRoutingID()));
}
-#if defined(OS_ANDROID)
- media_player_manager_.reset(BrowserMediaPlayerManager::Create(this));
+#if defined(ENABLE_BROWSER_CDMS)
+ media_web_contents_observer_.reset(new MediaWebContentsObserver(this));
#endif
+
+ unload_event_monitor_timeout_.reset(new TimeoutMonitor(base::Bind(
+ &RenderViewHostImpl::OnSwappedOut, weak_factory_.GetWeakPtr(), true)));
}
RenderViewHostImpl::~RenderViewHostImpl() {
@@ -259,7 +250,7 @@ RenderViewHostImpl::~RenderViewHostImpl() {
// If this was swapped out, it already decremented the active view
// count of the SiteInstance it belongs to.
- if (!is_swapped_out_)
+ if (IsRVHStateActive(rvh_state_))
instance_->decrement_active_view_count();
}
@@ -274,7 +265,9 @@ SiteInstance* RenderViewHostImpl::GetSiteInstance() const {
bool RenderViewHostImpl::CreateRenderView(
const base::string16& frame_name,
int opener_route_id,
- int32 max_page_id) {
+ int proxy_route_id,
+ int32 max_page_id,
+ bool window_was_created_with_opener) {
TRACE_EVENT0("renderer_host", "RenderViewHostImpl::CreateRenderView");
DCHECK(!IsRenderViewLive()) << "Creating view twice";
@@ -303,25 +296,27 @@ bool RenderViewHostImpl::CreateRenderView(
delegate_->GetRendererPrefs(GetProcess()->GetBrowserContext());
params.web_preferences = delegate_->GetWebkitPrefs();
params.view_id = GetRoutingID();
- params.main_frame_routing_id = main_render_frame_host()->routing_id();
+ params.main_frame_routing_id = main_frame_routing_id_;
params.surface_id = surface_id();
params.session_storage_namespace_id =
delegate_->GetSessionStorageNamespace(instance_)->id();
params.frame_name = frame_name;
// Ensure the RenderView sets its opener correctly.
params.opener_route_id = opener_route_id;
- params.swapped_out = is_swapped_out_;
+ params.swapped_out = !IsRVHStateActive(rvh_state_);
+ params.proxy_routing_id = proxy_route_id;
params.hidden = is_hidden();
+ params.never_visible = delegate_->IsNeverVisible();
+ params.window_was_created_with_opener = window_was_created_with_opener;
params.next_page_id = next_page_id;
GetWebScreenInfo(&params.screen_info);
params.accessibility_mode = accessibility_mode();
- params.allow_partial_swap = !GetProcess()->IsGuest();
Send(new ViewMsg_New(params));
// If it's enabled, tell the renderer to set up the Javascript bindings for
// sending messages back to the browser.
- if (GetProcess()->IsGuest())
+ if (GetProcess()->IsIsolatedGuest())
DCHECK_EQ(0, enabled_bindings_);
Send(new ViewMsg_AllowBindings(GetRoutingID(), enabled_bindings_));
// Let our delegate know that we created a RenderView.
@@ -334,10 +329,6 @@ bool RenderViewHostImpl::IsRenderViewLive() const {
return GetProcess()->HasConnection() && renderer_initialized_;
}
-bool RenderViewHostImpl::IsSubframe() const {
- return is_subframe_;
-}
-
void RenderViewHostImpl::SyncRendererPrefs() {
Send(new ViewMsg_SetRendererPrefs(GetRoutingID(),
delegate_->GetRendererPrefs(
@@ -372,10 +363,8 @@ WebPreferences RenderViewHostImpl::GetWebkitPrefs(const GURL& url) {
!command_line.HasSwitch(switches::kDisableLocalStorage);
prefs.databases_enabled =
!command_line.HasSwitch(switches::kDisableDatabases);
-#if defined(OS_ANDROID) && defined(ARCH_CPU_X86)
- prefs.webaudio_enabled =
- command_line.HasSwitch(switches::kEnableWebAudio);
-#else
+#if defined(OS_ANDROID)
+ // WebAudio is enabled by default on x86 and ARM.
prefs.webaudio_enabled =
!command_line.HasSwitch(switches::kDisableWebAudio);
#endif
@@ -385,6 +374,9 @@ WebPreferences RenderViewHostImpl::GetWebkitPrefs(const GURL& url) {
!command_line.HasSwitch(switches::kDisable3DAPIs) &&
!command_line.HasSwitch(switches::kDisableExperimentalWebGL);
+ prefs.pepper_3d_enabled =
+ !command_line.HasSwitch(switches::kDisablePepper3d);
+
prefs.flash_3d_enabled =
GpuProcessHost::gpu_enabled() &&
!command_line.HasSwitch(switches::kDisableFlash3d);
@@ -395,55 +387,17 @@ WebPreferences RenderViewHostImpl::GetWebkitPrefs(const GURL& url) {
GpuProcessHost::gpu_enabled() &&
!command_line.HasSwitch(switches::kDisableFlashStage3d);
- prefs.gl_multisampling_enabled =
- !command_line.HasSwitch(switches::kDisableGLMultisampling);
- prefs.privileged_webgl_extensions_enabled =
- command_line.HasSwitch(switches::kEnablePrivilegedWebGLExtensions);
prefs.site_specific_quirks_enabled =
!command_line.HasSwitch(switches::kDisableSiteSpecificQuirks);
prefs.allow_file_access_from_file_urls =
command_line.HasSwitch(switches::kAllowFileAccessFromFiles);
- prefs.accelerated_compositing_for_overflow_scroll_enabled = false;
- if (command_line.HasSwitch(switches::kEnableAcceleratedOverflowScroll))
- prefs.accelerated_compositing_for_overflow_scroll_enabled = true;
- if (command_line.HasSwitch(switches::kDisableAcceleratedOverflowScroll))
- prefs.accelerated_compositing_for_overflow_scroll_enabled = false;
-
- prefs.layer_squashing_enabled = false;
+ prefs.layer_squashing_enabled = true;
if (command_line.HasSwitch(switches::kEnableLayerSquashing))
prefs.layer_squashing_enabled = true;
if (command_line.HasSwitch(switches::kDisableLayerSquashing))
prefs.layer_squashing_enabled = false;
- prefs.accelerated_compositing_for_scrollable_frames_enabled = false;
- if (command_line.HasSwitch(switches::kEnableAcceleratedScrollableFrames))
- prefs.accelerated_compositing_for_scrollable_frames_enabled = true;
- if (command_line.HasSwitch(switches::kDisableAcceleratedScrollableFrames))
- prefs.accelerated_compositing_for_scrollable_frames_enabled = false;
-
- prefs.composited_scrolling_for_frames_enabled = false;
- if (command_line.HasSwitch(switches::kEnableCompositedScrollingForFrames))
- prefs.composited_scrolling_for_frames_enabled = true;
- if (command_line.HasSwitch(switches::kDisableCompositedScrollingForFrames))
- prefs.composited_scrolling_for_frames_enabled = false;
-
- prefs.universal_accelerated_compositing_for_overflow_scroll_enabled = false;
- if (command_line.HasSwitch(
- switches::kEnableUniversalAcceleratedOverflowScroll))
- prefs.universal_accelerated_compositing_for_overflow_scroll_enabled = true;
- if (command_line.HasSwitch(
- switches::kDisableUniversalAcceleratedOverflowScroll))
- prefs.universal_accelerated_compositing_for_overflow_scroll_enabled = false;
-
- prefs.show_paint_rects =
- command_line.HasSwitch(switches::kShowPaintRects);
- prefs.accelerated_compositing_enabled =
- GpuProcessHost::gpu_enabled() &&
- !command_line.HasSwitch(switches::kDisableAcceleratedCompositing);
- prefs.force_compositing_mode =
- content::IsForceCompositingModeEnabled() &&
- !command_line.HasSwitch(switches::kDisableForceCompositingMode);
prefs.accelerated_2d_canvas_enabled =
GpuProcessHost::gpu_enabled() &&
!command_line.HasSwitch(switches::kDisableAccelerated2dCanvas);
@@ -452,38 +406,22 @@ WebPreferences RenderViewHostImpl::GetWebkitPrefs(const GURL& url) {
prefs.accelerated_2d_canvas_msaa_sample_count =
atoi(command_line.GetSwitchValueASCII(
switches::kAcceleratedCanvas2dMSAASampleCount).c_str());
- prefs.accelerated_filters_enabled =
- GpuProcessHost::gpu_enabled() &&
- command_line.HasSwitch(switches::kEnableAcceleratedFilters);
- prefs.accelerated_compositing_for_3d_transforms_enabled =
- prefs.accelerated_compositing_for_animation_enabled =
- !command_line.HasSwitch(switches::kDisableAcceleratedLayers);
- prefs.accelerated_compositing_for_plugins_enabled =
- !command_line.HasSwitch(switches::kDisableAcceleratedPlugins);
- prefs.accelerated_compositing_for_video_enabled =
- !command_line.HasSwitch(switches::kDisableAcceleratedVideo);
- prefs.fullscreen_enabled =
- !command_line.HasSwitch(switches::kDisableFullScreen);
- prefs.lazy_layout_enabled =
- command_line.HasSwitch(switches::kEnableExperimentalWebPlatformFeatures);
+ prefs.deferred_filters_enabled =
+ !command_line.HasSwitch(switches::kDisableDeferredFilters);
+ prefs.container_culling_enabled =
+ command_line.HasSwitch(switches::kEnableContainerCulling);
prefs.region_based_columns_enabled =
command_line.HasSwitch(switches::kEnableRegionBasedColumns);
- prefs.threaded_html_parser =
- !command_line.HasSwitch(switches::kDisableThreadedHTMLParser);
- prefs.experimental_websocket_enabled =
- command_line.HasSwitch(switches::kEnableExperimentalWebSocket);
- if (command_line.HasSwitch(cc::switches::kEnablePinchVirtualViewport)) {
+
+ if (IsPinchVirtualViewportEnabled()) {
prefs.pinch_virtual_viewport_enabled = true;
prefs.pinch_overlay_scrollbar_thickness = 10;
}
- prefs.use_solid_color_scrollbars = command_line.HasSwitch(
- switches::kEnableOverlayScrollbars);
+ prefs.use_solid_color_scrollbars = ui::IsOverlayScrollbarEnabled();
#if defined(OS_ANDROID)
prefs.user_gesture_required_for_media_playback = !command_line.HasSwitch(
switches::kDisableGestureRequirementForMediaPlayback);
- prefs.user_gesture_required_for_media_fullscreen = !command_line.HasSwitch(
- switches::kDisableGestureRequirementForMediaFullscreen);
#endif
prefs.touch_enabled = ui::AreTouchEventsEnabled();
@@ -511,9 +449,6 @@ WebPreferences RenderViewHostImpl::GetWebkitPrefs(const GURL& url) {
if (command_line.HasSwitch(switches::kDisableSmoothScrolling))
prefs.enable_scroll_animator = false;
- prefs.visual_word_movement_enabled =
- command_line.HasSwitch(switches::kEnableVisualWordMovement);
-
// Certain GPU features might have been blacklisted.
GpuDataManagerImpl::GetInstance()->UpdateRendererWebPrefs(&prefs);
@@ -523,30 +458,12 @@ WebPreferences RenderViewHostImpl::GetWebkitPrefs(const GURL& url) {
prefs.javascript_enabled = true;
}
- prefs.is_online = !net::NetworkChangeNotifier::IsOffline();
-
-#if !defined(USE_AURA)
- // Force accelerated compositing and 2d canvas off for chrome: and about:
- // pages (unless it's specifically allowed).
- if ((url.SchemeIs(chrome::kChromeUIScheme) ||
- (url.SchemeIs(chrome::kAboutScheme) &&
- url.spec() != kAboutBlankURL)) &&
- !command_line.HasSwitch(switches::kAllowWebUICompositing)) {
- prefs.accelerated_compositing_enabled = false;
- prefs.accelerated_2d_canvas_enabled = false;
- }
-#endif
+ prefs.connection_type = net::NetworkChangeNotifier::GetConnectionType();
+ prefs.is_online =
+ prefs.connection_type != net::NetworkChangeNotifier::CONNECTION_NONE;
- prefs.fixed_position_creates_stacking_context = !command_line.HasSwitch(
- switches::kDisableFixedPositionCreatesStackingContext);
-
-#if defined(OS_CHROMEOS)
prefs.gesture_tap_highlight_enabled = !command_line.HasSwitch(
switches::kDisableGestureTapHighlight);
-#else
- prefs.gesture_tap_highlight_enabled = command_line.HasSwitch(
- switches::kEnableGestureTapHighlight);
-#endif
prefs.number_of_cpu_cores = base::SysInfo::NumberOfProcessors();
@@ -562,83 +479,22 @@ WebPreferences RenderViewHostImpl::GetWebkitPrefs(const GURL& url) {
prefs.deferred_image_decoding_enabled =
command_line.HasSwitch(switches::kEnableDeferredImageDecoding) ||
- cc::switches::IsImplSidePaintingEnabled();
+ content::IsImplSidePaintingEnabled();
prefs.spatial_navigation_enabled = command_line.HasSwitch(
switches::kEnableSpatialNavigation);
GetContentClient()->browser()->OverrideWebkitPrefs(this, url, &prefs);
-
- // Disable compositing in guests until we have compositing path implemented
- // for guests.
- bool guest_compositing_enabled = !command_line.HasSwitch(
- switches::kDisableBrowserPluginCompositing);
- if (GetProcess()->IsGuest() && !guest_compositing_enabled) {
- prefs.force_compositing_mode = false;
- prefs.accelerated_compositing_enabled = false;
- }
-
return prefs;
}
-void RenderViewHostImpl::Navigate(const ViewMsg_Navigate_Params& params) {
+void RenderViewHostImpl::Navigate(const FrameMsg_Navigate_Params& params) {
TRACE_EVENT0("renderer_host", "RenderViewHostImpl::Navigate");
- // Browser plugin guests are not allowed to navigate outside web-safe schemes,
- // so do not grant them the ability to request additional URLs.
- if (!GetProcess()->IsGuest()) {
- ChildProcessSecurityPolicyImpl::GetInstance()->GrantRequestURL(
- GetProcess()->GetID(), params.url);
- if (params.url.SchemeIs(chrome::kDataScheme) &&
- params.base_url_for_data_url.SchemeIs(chrome::kFileScheme)) {
- // If 'data:' is used, and we have a 'file:' base url, grant access to
- // local files.
- ChildProcessSecurityPolicyImpl::GetInstance()->GrantRequestURL(
- GetProcess()->GetID(), params.base_url_for_data_url);
- }
- }
-
- // Only send the message if we aren't suspended at the start of a cross-site
- // request.
- if (navigations_suspended_) {
- // Shouldn't be possible to have a second navigation while suspended, since
- // navigations will only be suspended during a cross-site request. If a
- // second navigation occurs, WebContentsImpl will cancel this pending RVH
- // create a new pending RVH.
- DCHECK(!suspended_nav_params_.get());
- suspended_nav_params_.reset(new ViewMsg_Navigate_Params(params));
- } else {
- // Get back to a clean state, in case we start a new navigation without
- // completing a RVH swap or unload handler.
- SetSwappedOut(false);
-
- Send(new ViewMsg_Navigate(GetRoutingID(), params));
- }
-
- // Force the throbber to start. We do this because WebKit's "started
- // loading" message will be received asynchronously from the UI of the
- // browser. But we want to keep the throbber in sync with what's happening
- // in the UI. For example, we want to start throbbing immediately when the
- // user naivgates even if the renderer is delayed. There is also an issue
- // with the throbber starting because the WebUI (which controls whether the
- // favicon is displayed) happens synchronously. If the start loading
- // messages was asynchronous, then the default favicon would flash in.
- //
- // WebKit doesn't send throb notifications for JavaScript URLs, so we
- // don't want to either.
- if (!params.url.SchemeIs(kJavaScriptScheme))
- delegate_->DidStartLoading(this);
+ delegate_->GetFrameTree()->GetMainFrame()->Navigate(params);
}
void RenderViewHostImpl::NavigateToURL(const GURL& url) {
- ViewMsg_Navigate_Params params;
- params.page_id = -1;
- params.pending_history_list_offset = -1;
- params.current_history_list_offset = -1;
- params.current_history_list_length = 0;
- params.url = url;
- params.transition = PAGE_TRANSITION_LINK;
- params.navigation_type = ViewMsg_Navigate_Type::NORMAL;
- Navigate(params);
+ delegate_->GetFrameTree()->GetMainFrame()->NavigateToURL(url);
}
void RenderViewHostImpl::SetNavigationsSuspended(
@@ -652,11 +508,12 @@ void RenderViewHostImpl::SetNavigationsSuspended(
// There's navigation message params waiting to be sent. Now that we're not
// suspended anymore, resume navigation by sending them. If we were swapped
// out, we should also stop filtering out the IPC messages now.
- SetSwappedOut(false);
+ SetState(STATE_DEFAULT);
DCHECK(!proceed_time.is_null());
suspended_nav_params_->browser_navigation_start = proceed_time;
- Send(new ViewMsg_Navigate(GetRoutingID(), *suspended_nav_params_.get()));
+ Send(new FrameMsg_Navigate(
+ main_frame_routing_id_, *suspended_nav_params_.get()));
suspended_nav_params_.reset();
}
}
@@ -668,118 +525,16 @@ void RenderViewHostImpl::CancelSuspendedNavigations() {
navigations_suspended_ = false;
}
-void RenderViewHostImpl::FirePageBeforeUnload(bool for_cross_site_transition) {
- if (!IsRenderViewLive()) {
- // This RenderViewHostImpl doesn't have a live renderer, so just
- // skip running the onbeforeunload handler.
- is_waiting_for_beforeunload_ack_ = true; // Checked by OnShouldCloseACK.
- unload_ack_is_for_cross_site_transition_ = for_cross_site_transition;
- base::TimeTicks now = base::TimeTicks::Now();
- OnShouldCloseACK(true, now, now);
- return;
- }
-
- // This may be called more than once (if the user clicks the tab close button
- // several times, or if she clicks the tab close button then the browser close
- // button), and we only send the message once.
- if (is_waiting_for_beforeunload_ack_) {
- // Some of our close messages could be for the tab, others for cross-site
- // transitions. We always want to think it's for closing the tab if any
- // of the messages were, since otherwise it might be impossible to close
- // (if there was a cross-site "close" request pending when the user clicked
- // the close button). We want to keep the "for cross site" flag only if
- // both the old and the new ones are also for cross site.
- unload_ack_is_for_cross_site_transition_ =
- unload_ack_is_for_cross_site_transition_ && for_cross_site_transition;
- } else {
- // Start the hang monitor in case the renderer hangs in the beforeunload
- // handler.
- is_waiting_for_beforeunload_ack_ = true;
- unload_ack_is_for_cross_site_transition_ = for_cross_site_transition;
- // Increment the in-flight event count, to ensure that input events won't
- // cancel the timeout timer.
- increment_in_flight_event_count();
- StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS));
- send_should_close_start_time_ = base::TimeTicks::Now();
- Send(new ViewMsg_ShouldClose(GetRoutingID()));
- }
-}
-
-void RenderViewHostImpl::OnCrossSiteResponse(
- const GlobalRequestID& global_request_id,
- bool is_transfer,
- const std::vector<GURL>& transfer_url_chain,
- const Referrer& referrer,
- PageTransition page_transition,
- int64 frame_id,
- bool should_replace_current_entry) {
- RenderViewHostDelegate::RendererManagement* manager =
- delegate_->GetRendererManagementDelegate();
- if (manager) {
- manager->OnCrossSiteResponse(this, global_request_id, is_transfer,
- transfer_url_chain, referrer, page_transition,
- frame_id, should_replace_current_entry);
- }
-}
-
void RenderViewHostImpl::SuppressDialogsUntilSwapOut() {
Send(new ViewMsg_SuppressDialogsUntilSwapOut(GetRoutingID()));
}
-void RenderViewHostImpl::SwapOut() {
- // This will be set back to false in OnSwapOutACK, just before we replace
- // this RVH with the pending RVH.
- is_waiting_for_unload_ack_ = true;
- // Start the hang monitor in case the renderer hangs in the unload handler.
- // Increment the in-flight event count, to ensure that input events won't
- // cancel the timeout timer.
- increment_in_flight_event_count();
- StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS));
-
- if (IsRenderViewLive()) {
- Send(new ViewMsg_SwapOut(GetRoutingID()));
- } else {
- // This RenderViewHost doesn't have a live renderer, so just skip the unload
- // event.
- OnSwappedOut(true);
- }
-}
-
-void RenderViewHostImpl::OnSwapOutACK() {
- OnSwappedOut(false);
-}
-
void RenderViewHostImpl::OnSwappedOut(bool timed_out) {
- // Stop the hang monitor now that the unload handler has finished.
- decrement_in_flight_event_count();
- StopHangMonitorTimeout();
- is_waiting_for_unload_ack_ = false;
- has_timed_out_on_unload_ = timed_out;
- delegate_->SwappedOut(this);
-}
-
-void RenderViewHostImpl::WasSwappedOut() {
- // Don't bother reporting hung state anymore.
- StopHangMonitorTimeout();
-
- // If we have timed out on running the unload handler, we consider
- // the process hung and we should terminate it if there are no other tabs
- // using the process. If there are other views using this process, the
- // unresponsive renderer timeout will catch it.
- bool hung = has_timed_out_on_unload_;
-
- // Now that we're no longer the active RVH in the tab, start filtering out
- // most IPC messages. Usually the renderer will have stopped sending
- // messages as of OnSwapOutACK. However, we may have timed out waiting
- // for that message, and additional IPC messages may keep streaming in.
- // We filter them out, as long as that won't cause problems (e.g., we
- // still allow synchronous messages through).
- SetSwappedOut(true);
-
- // If we are not running the renderer in process and no other tab is using
- // the hung process, consider it eligible to be killed, assuming it is a real
- // process (unit tests don't have real processes).
- if (hung) {
+ // Ignore spurious swap out ack.
+ if (!IsWaitingForUnloadACK())
+ return;
+ unload_event_monitor_timeout_->Stop();
+ if (timed_out) {
base::ProcessHandle process_handle = GetProcess()->GetHandle();
int views = 0;
@@ -816,13 +571,49 @@ void RenderViewHostImpl::WasSwappedOut() {
}
}
- // Inform the renderer that it can exit if no one else is using it.
+ switch (rvh_state_) {
+ case STATE_WAITING_FOR_UNLOAD_ACK:
+ SetState(STATE_WAITING_FOR_COMMIT);
+ break;
+ case STATE_PENDING_SWAP_OUT:
+ SetState(STATE_SWAPPED_OUT);
+ break;
+ case STATE_PENDING_SHUTDOWN:
+ DCHECK(!pending_shutdown_on_swap_out_.is_null());
+ pending_shutdown_on_swap_out_.Run();
+ break;
+ default:
+ NOTREACHED();
+ }
+}
+
+void RenderViewHostImpl::WasSwappedOut(
+ const base::Closure& pending_delete_on_swap_out) {
Send(new ViewMsg_WasSwappedOut(GetRoutingID()));
+ if (rvh_state_ == STATE_WAITING_FOR_UNLOAD_ACK) {
+ SetState(STATE_PENDING_SWAP_OUT);
+ if (!instance_->active_view_count())
+ SetPendingShutdown(pending_delete_on_swap_out);
+ } else if (rvh_state_ == STATE_WAITING_FOR_COMMIT) {
+ SetState(STATE_SWAPPED_OUT);
+ } else if (rvh_state_ == STATE_DEFAULT) {
+ // When the RenderView is not live, the RenderFrameHostManager will call
+ // CommitPending directly, without calling SwapOut on the old RVH. This will
+ // cause WasSwappedOut to be called directly on the live old RVH.
+ DCHECK(!IsRenderViewLive());
+ SetState(STATE_SWAPPED_OUT);
+ } else {
+ NOTREACHED();
+ }
+}
+
+void RenderViewHostImpl::SetPendingShutdown(const base::Closure& on_swap_out) {
+ pending_shutdown_on_swap_out_ = on_swap_out;
+ SetState(STATE_PENDING_SHUTDOWN);
}
void RenderViewHostImpl::ClosePage() {
- // Start the hang monitor in case the renderer hangs in the unload handler.
- is_waiting_for_unload_ack_ = true;
+ SetState(STATE_WAITING_FOR_CLOSE);
StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS));
if (IsRenderViewLive()) {
@@ -849,7 +640,6 @@ void RenderViewHostImpl::ClosePage() {
void RenderViewHostImpl::ClosePageIgnoringUnloadEvents() {
StopHangMonitorTimeout();
is_waiting_for_beforeunload_ack_ = false;
- is_waiting_for_unload_ack_ = false;
sudden_termination_allowed_ = true;
delegate_->Close(this);
@@ -866,6 +656,27 @@ void RenderViewHostImpl::SetHasPendingCrossSiteRequest(
GetProcess()->GetID(), GetRoutingID(), has_pending_request);
}
+void RenderViewHostImpl::SetWebUIHandle(mojo::ScopedMessagePipeHandle handle) {
+ // Never grant any bindings to browser plugin guests.
+ if (GetProcess()->IsIsolatedGuest()) {
+ NOTREACHED() << "Never grant bindings to a guest process.";
+ return;
+ }
+
+ if ((enabled_bindings_ & BINDINGS_POLICY_WEB_UI) == 0) {
+ NOTREACHED() << "You must grant bindings before setting the handle";
+ return;
+ }
+
+ DCHECK(renderer_initialized_);
+
+ WebUISetupPtr web_ui_setup;
+ static_cast<RenderProcessHostImpl*>(GetProcess())->ConnectTo(
+ kRendererService_WebUISetup, &web_ui_setup);
+
+ web_ui_setup->SetWebUIHandle(GetRoutingID(), handle.Pass());
+}
+
#if defined(OS_ANDROID)
void RenderViewHostImpl::ActivateNearestFindResult(int request_id,
float x,
@@ -877,10 +688,6 @@ void RenderViewHostImpl::ActivateNearestFindResult(int request_id,
void RenderViewHostImpl::RequestFindMatchRects(int current_version) {
Send(new ViewMsg_FindMatchRects(GetRoutingID(), current_version));
}
-
-void RenderViewHostImpl::DisableFullscreenEncryptedMediaPlayback() {
- media_player_manager_->DisableFullscreenEncryptedMediaPlayback();
-}
#endif
void RenderViewHostImpl::DragTargetDragEnter(
@@ -896,33 +703,35 @@ void RenderViewHostImpl::DragTargetDragEnter(
// The URL could have been cobbled together from any highlighted text string,
// and can't be interpreted as a capability.
DropData filtered_data(drop_data);
- FilterURL(policy, GetProcess(), true, &filtered_data.url);
+ GetProcess()->FilterURL(true, &filtered_data.url);
+ if (drop_data.did_originate_from_renderer) {
+ filtered_data.filenames.clear();
+ }
// The filenames vector, on the other hand, does represent a capability to
// access the given files.
fileapi::IsolatedContext::FileInfoSet files;
- for (std::vector<DropData::FileInfo>::iterator iter(
+ for (std::vector<ui::FileInfo>::iterator iter(
filtered_data.filenames.begin());
- iter != filtered_data.filenames.end(); ++iter) {
+ iter != filtered_data.filenames.end();
+ ++iter) {
// A dragged file may wind up as the value of an input element, or it
// may be used as the target of a navigation instead. We don't know
// which will happen at this point, so generously grant both access
// and request permissions to the specific file to cover both cases.
// We do not give it the permission to request all file:// URLs.
- base::FilePath path =
- base::FilePath::FromUTF8Unsafe(UTF16ToUTF8(iter->path));
// Make sure we have the same display_name as the one we register.
if (iter->display_name.empty()) {
std::string name;
- files.AddPath(path, &name);
- iter->display_name = UTF8ToUTF16(name);
+ files.AddPath(iter->path, &name);
+ iter->display_name = base::FilePath::FromUTF8Unsafe(name);
} else {
- files.AddPathWithName(path, UTF16ToUTF8(iter->display_name));
+ files.AddPathWithName(iter->path, iter->display_name.AsUTF8Unsafe());
}
policy->GrantRequestSpecificFileURL(renderer_id,
- net::FilePathToFileURL(path));
+ net::FilePathToFileURL(iter->path));
// If the renderer already has permission to read these paths, we don't need
// to re-grant them. This prevents problems with DnD for files in the CrOS
@@ -930,8 +739,8 @@ void RenderViewHostImpl::DragTargetDragEnter(
// directories, but dragging a file would cause the read/write access to be
// overwritten with read-only access, making them impossible to delete or
// rename until the renderer was killed.
- if (!policy->CanReadFile(renderer_id, path))
- policy->GrantReadFile(renderer_id, path);
+ if (!policy->CanReadFile(renderer_id, iter->path))
+ policy->GrantReadFile(renderer_id, iter->path);
}
fileapi::IsolatedContext* isolated_context =
@@ -943,7 +752,30 @@ void RenderViewHostImpl::DragTargetDragEnter(
// Grant the permission iff the ID is valid.
policy->GrantReadFileSystem(renderer_id, filesystem_id);
}
- filtered_data.filesystem_id = UTF8ToUTF16(filesystem_id);
+ filtered_data.filesystem_id = base::UTF8ToUTF16(filesystem_id);
+
+ fileapi::FileSystemContext* file_system_context =
+ BrowserContext::GetStoragePartition(
+ GetProcess()->GetBrowserContext(),
+ GetSiteInstance())->GetFileSystemContext();
+ for (size_t i = 0; i < filtered_data.file_system_files.size(); ++i) {
+ fileapi::FileSystemURL file_system_url =
+ file_system_context->CrackURL(filtered_data.file_system_files[i].url);
+
+ std::string register_name;
+ std::string filesystem_id = isolated_context->RegisterFileSystemForPath(
+ file_system_url.type(), file_system_url.filesystem_id(),
+ file_system_url.path(), &register_name);
+ policy->GrantReadFileSystem(renderer_id, filesystem_id);
+
+ // Note: We are using the origin URL provided by the sender here. It may be
+ // different from the receiver's.
+ filtered_data.file_system_files[i].url = GURL(
+ fileapi::GetIsolatedFileSystemRootURIString(
+ file_system_url.origin(),
+ filesystem_id,
+ std::string()).append(register_name));
+ }
Send(new DragMsg_TargetDragEnter(GetRoutingID(), filtered_data, client_pt,
screen_pt, operations_allowed,
@@ -971,110 +803,26 @@ void RenderViewHostImpl::DragTargetDrop(
key_modifiers));
}
-void RenderViewHostImpl::DesktopNotificationPermissionRequestDone(
- int callback_context) {
- Send(new DesktopNotificationMsg_PermissionRequestDone(
- GetRoutingID(), callback_context));
-}
-
-void RenderViewHostImpl::DesktopNotificationPostDisplay(int callback_context) {
- Send(new DesktopNotificationMsg_PostDisplay(GetRoutingID(),
- callback_context));
-}
-
-void RenderViewHostImpl::DesktopNotificationPostError(
- int notification_id,
- const base::string16& message) {
- Send(new DesktopNotificationMsg_PostError(
- GetRoutingID(), notification_id, message));
-}
-
-void RenderViewHostImpl::DesktopNotificationPostClose(int notification_id,
- bool by_user) {
- Send(new DesktopNotificationMsg_PostClose(
- GetRoutingID(), notification_id, by_user));
-}
-
-void RenderViewHostImpl::DesktopNotificationPostClick(int notification_id) {
- Send(new DesktopNotificationMsg_PostClick(GetRoutingID(), notification_id));
-}
-
-void RenderViewHostImpl::ExecuteJavascriptInWebFrame(
- const base::string16& frame_xpath,
- const base::string16& jscript) {
- Send(new ViewMsg_ScriptEvalRequest(GetRoutingID(), frame_xpath, jscript,
- 0, false));
-}
-
-void RenderViewHostImpl::ExecuteJavascriptInWebFrameCallbackResult(
- const base::string16& frame_xpath,
- const base::string16& jscript,
- const JavascriptResultCallback& callback) {
- static int next_id = 1;
- int key = next_id++;
- Send(new ViewMsg_ScriptEvalRequest(GetRoutingID(), frame_xpath, jscript,
- key, true));
- javascript_callbacks_.insert(std::make_pair(key, callback));
-}
-
-void RenderViewHostImpl::JavaScriptDialogClosed(
- IPC::Message* reply_msg,
- bool success,
- const base::string16& user_input) {
- GetProcess()->SetIgnoreInputEvents(false);
- bool is_waiting =
- is_waiting_for_beforeunload_ack_ || is_waiting_for_unload_ack_;
-
- // If we are executing as part of (before)unload event handling, we don't
- // want to use the regular hung_renderer_delay_ms_ if the user has agreed to
- // leave the current page. In this case, use the regular timeout value used
- // during the (before)unload handling.
- if (is_waiting) {
- StartHangMonitorTimeout(TimeDelta::FromMilliseconds(
- success ? kUnloadTimeoutMS : hung_renderer_delay_ms_));
- }
-
- ViewHostMsg_RunJavaScriptMessage::WriteReplyParams(reply_msg,
- success, user_input);
- Send(reply_msg);
-
- // If we are waiting for an unload or beforeunload ack and the user has
- // suppressed messages, kill the tab immediately; a page that's spamming
- // alerts in onbeforeunload is presumably malicious, so there's no point in
- // continuing to run its script and dragging out the process.
- // This must be done after sending the reply since RenderView can't close
- // correctly while waiting for a response.
- if (is_waiting && are_javascript_messages_suppressed_)
- delegate_->RendererUnresponsive(
- this, is_waiting_for_beforeunload_ack_, is_waiting_for_unload_ack_);
-}
-
void RenderViewHostImpl::DragSourceEndedAt(
int client_x, int client_y, int screen_x, int screen_y,
WebDragOperation operation) {
- Send(new DragMsg_SourceEndedOrMoved(
- GetRoutingID(),
- gfx::Point(client_x, client_y),
- gfx::Point(screen_x, screen_y),
- true, operation));
-}
-
-void RenderViewHostImpl::DragSourceMovedTo(
- int client_x, int client_y, int screen_x, int screen_y) {
- Send(new DragMsg_SourceEndedOrMoved(
- GetRoutingID(),
- gfx::Point(client_x, client_y),
- gfx::Point(screen_x, screen_y),
- false, WebDragOperationNone));
+ Send(new DragMsg_SourceEnded(GetRoutingID(),
+ gfx::Point(client_x, client_y),
+ gfx::Point(screen_x, screen_y),
+ operation));
}
void RenderViewHostImpl::DragSourceSystemDragEnded() {
Send(new DragMsg_SourceSystemDragEnded(GetRoutingID()));
}
+RenderFrameHost* RenderViewHostImpl::GetMainFrame() {
+ return RenderFrameHost::FromID(GetProcess()->GetID(), main_frame_routing_id_);
+}
+
void RenderViewHostImpl::AllowBindings(int bindings_flags) {
// Never grant any bindings to browser plugin guests.
- if (GetProcess()->IsGuest()) {
+ if (GetProcess()->IsIsolatedGuest()) {
NOTREACHED() << "Never grant bindings to a guest process.";
return;
}
@@ -1089,7 +837,9 @@ void RenderViewHostImpl::AllowBindings(int bindings_flags) {
// than this single active view.
RenderProcessHostImpl* process =
static_cast<RenderProcessHostImpl*>(GetProcess());
- if (process->GetActiveViewCount() > 1)
+ // --single-process only has one renderer.
+ if (process->GetActiveViewCount() > 1 &&
+ !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
return;
}
@@ -1116,7 +866,8 @@ void RenderViewHostImpl::SetWebUIProperty(const std::string& name,
if (enabled_bindings_ & BINDINGS_POLICY_WEB_UI) {
Send(new ViewMsg_SetWebUIProperty(GetRoutingID(), name, value));
} else {
- RecordAction(UserMetricsAction("BindingsMismatchTerminate_RVH_WebUI"));
+ RecordAction(
+ base::UserMetricsAction("BindingsMismatchTerminate_RVH_WebUI"));
base::KillProcess(
GetProcess()->GetHandle(), content::RESULT_CODE_KILLED, false);
}
@@ -1197,7 +948,7 @@ bool RenderViewHostImpl::OnMessageReceived(const IPC::Message& msg) {
// Filter out most IPC messages if this renderer is swapped out.
// We still want to handle certain ACKs to keep our state consistent.
- if (is_swapped_out_) {
+ if (IsSwappedOut()) {
if (!SwappedOutMessages::CanHandleWhileSwappedOut(msg)) {
// If this is a synchronous message and we decided not to handle it,
// we must send an error reply, or else the renderer will be stuck
@@ -1216,8 +967,7 @@ bool RenderViewHostImpl::OnMessageReceived(const IPC::Message& msg) {
return true;
bool handled = true;
- bool msg_is_ok = true;
- IPC_BEGIN_MESSAGE_MAP_EX(RenderViewHostImpl, msg, msg_is_ok)
+ IPC_BEGIN_MESSAGE_MAP(RenderViewHostImpl, msg)
IPC_MESSAGE_HANDLER(ViewHostMsg_ShowView, OnShowView)
IPC_MESSAGE_HANDLER(ViewHostMsg_ShowWidget, OnShowWidget)
IPC_MESSAGE_HANDLER(ViewHostMsg_ShowFullscreenWidget,
@@ -1225,96 +975,48 @@ bool RenderViewHostImpl::OnMessageReceived(const IPC::Message& msg) {
IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_RunModal, OnRunModal)
IPC_MESSAGE_HANDLER(ViewHostMsg_RenderViewReady, OnRenderViewReady)
IPC_MESSAGE_HANDLER(ViewHostMsg_RenderProcessGone, OnRenderProcessGone)
- IPC_MESSAGE_HANDLER(ViewHostMsg_DidRedirectProvisionalLoad,
- OnDidRedirectProvisionalLoad)
- IPC_MESSAGE_HANDLER(ViewHostMsg_DidFailProvisionalLoadWithError,
- OnDidFailProvisionalLoadWithError)
- IPC_MESSAGE_HANDLER_GENERIC(ViewHostMsg_FrameNavigate, OnNavigate(msg))
IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateState, OnUpdateState)
- IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateTitle, OnUpdateTitle)
- IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateEncoding, OnUpdateEncoding)
IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateTargetURL, OnUpdateTargetURL)
IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateInspectorSetting,
OnUpdateInspectorSetting)
IPC_MESSAGE_HANDLER(ViewHostMsg_Close, OnClose)
IPC_MESSAGE_HANDLER(ViewHostMsg_RequestMove, OnRequestMove)
- IPC_MESSAGE_HANDLER(ViewHostMsg_DidStartLoading, OnDidStartLoading)
- IPC_MESSAGE_HANDLER(ViewHostMsg_DidStopLoading, OnDidStopLoading)
- IPC_MESSAGE_HANDLER(ViewHostMsg_DidChangeLoadProgress,
- OnDidChangeLoadProgress)
- IPC_MESSAGE_HANDLER(ViewHostMsg_DidDisownOpener, OnDidDisownOpener)
IPC_MESSAGE_HANDLER(ViewHostMsg_DocumentAvailableInMainFrame,
OnDocumentAvailableInMainFrame)
- IPC_MESSAGE_HANDLER(ViewHostMsg_DocumentOnLoadCompletedInMainFrame,
- OnDocumentOnLoadCompletedInMainFrame)
- IPC_MESSAGE_HANDLER(ViewHostMsg_ContextMenu, OnContextMenu)
IPC_MESSAGE_HANDLER(ViewHostMsg_ToggleFullscreen, OnToggleFullscreen)
- IPC_MESSAGE_HANDLER(ViewHostMsg_OpenURL, OnOpenURL)
IPC_MESSAGE_HANDLER(ViewHostMsg_DidContentsPreferredSizeChange,
OnDidContentsPreferredSizeChange)
IPC_MESSAGE_HANDLER(ViewHostMsg_DidChangeScrollOffset,
OnDidChangeScrollOffset)
- IPC_MESSAGE_HANDLER(ViewHostMsg_DidChangeScrollbarsForMainFrame,
- OnDidChangeScrollbarsForMainFrame)
- IPC_MESSAGE_HANDLER(ViewHostMsg_DidChangeScrollOffsetPinningForMainFrame,
- OnDidChangeScrollOffsetPinningForMainFrame)
- IPC_MESSAGE_HANDLER(ViewHostMsg_DidChangeNumWheelEvents,
- OnDidChangeNumWheelEvents)
IPC_MESSAGE_HANDLER(ViewHostMsg_RouteCloseEvent,
OnRouteCloseEvent)
IPC_MESSAGE_HANDLER(ViewHostMsg_RouteMessageEvent, OnRouteMessageEvent)
- IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_RunJavaScriptMessage,
- OnRunJavaScriptMessage)
- IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_RunBeforeUnloadConfirm,
- OnRunBeforeUnloadConfirm)
IPC_MESSAGE_HANDLER(DragHostMsg_StartDragging, OnStartDragging)
IPC_MESSAGE_HANDLER(DragHostMsg_UpdateDragCursor, OnUpdateDragCursor)
IPC_MESSAGE_HANDLER(DragHostMsg_TargetDrop_ACK, OnTargetDropACK)
IPC_MESSAGE_HANDLER(ViewHostMsg_TakeFocus, OnTakeFocus)
IPC_MESSAGE_HANDLER(ViewHostMsg_FocusedNodeChanged, OnFocusedNodeChanged)
- IPC_MESSAGE_HANDLER(ViewHostMsg_AddMessageToConsole, OnAddMessageToConsole)
- IPC_MESSAGE_HANDLER(ViewHostMsg_ShouldClose_ACK, OnShouldCloseACK)
IPC_MESSAGE_HANDLER(ViewHostMsg_ClosePage_ACK, OnClosePageACK)
- IPC_MESSAGE_HANDLER(ViewHostMsg_SwapOut_ACK, OnSwapOutACK)
- IPC_MESSAGE_HANDLER(ViewHostMsg_SelectionChanged, OnSelectionChanged)
- IPC_MESSAGE_HANDLER(ViewHostMsg_SelectionBoundsChanged,
- OnSelectionBoundsChanged)
- IPC_MESSAGE_HANDLER(ViewHostMsg_ScriptEvalResponse, OnScriptEvalResponse)
IPC_MESSAGE_HANDLER(ViewHostMsg_DidZoomURL, OnDidZoomURL)
- IPC_MESSAGE_HANDLER(DesktopNotificationHostMsg_RequestPermission,
- OnRequestDesktopNotificationPermission)
- IPC_MESSAGE_HANDLER(DesktopNotificationHostMsg_Show,
- OnShowDesktopNotification)
- IPC_MESSAGE_HANDLER(DesktopNotificationHostMsg_Cancel,
- OnCancelDesktopNotification)
#if defined(OS_MACOSX) || defined(OS_ANDROID)
IPC_MESSAGE_HANDLER(ViewHostMsg_ShowPopup, OnShowPopup)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_HidePopup, OnHidePopup)
#endif
IPC_MESSAGE_HANDLER(ViewHostMsg_RunFileChooser, OnRunFileChooser)
- IPC_MESSAGE_HANDLER(ViewHostMsg_DidAccessInitialDocument,
- OnDidAccessInitialDocument)
- IPC_MESSAGE_HANDLER(ViewHostMsg_DomOperationResponse,
- OnDomOperationResponse)
IPC_MESSAGE_HANDLER(AccessibilityHostMsg_Events, OnAccessibilityEvents)
+ IPC_MESSAGE_HANDLER(AccessibilityHostMsg_LocationChanges,
+ OnAccessibilityLocationChanges)
IPC_MESSAGE_HANDLER(ViewHostMsg_FocusedNodeTouched, OnFocusedNodeTouched)
// Have the super handle all other messages.
IPC_MESSAGE_UNHANDLED(
handled = RenderWidgetHostImpl::OnMessageReceived(msg))
- IPC_END_MESSAGE_MAP_EX()
-
- if (!msg_is_ok) {
- // The message had a handler, but its de-serialization failed.
- // Kill the renderer.
- RecordAction(UserMetricsAction("BadMessageTerminate_RVH"));
- GetProcess()->ReceivedBadMessage();
- }
+ IPC_END_MESSAGE_MAP()
return handled;
}
void RenderViewHostImpl::Init() {
RenderWidgetHostImpl::Init();
- main_render_frame_host()->Init();
}
void RenderViewHostImpl::Shutdown() {
@@ -1333,6 +1035,15 @@ void RenderViewHostImpl::Shutdown() {
run_modal_opener_id_ = MSG_ROUTING_NONE;
}
+ // We can't release the SessionStorageNamespace until our peer
+ // in the renderer has wound down.
+ if (GetProcess()->HasConnection()) {
+ RenderProcessHostImpl::ReleaseOnCloseACK(
+ GetProcess(),
+ delegate_->GetSessionStorageNamespaceMap(),
+ GetRoutingID());
+ }
+
RenderWidgetHostImpl::Shutdown();
}
@@ -1346,12 +1057,9 @@ void RenderViewHostImpl::CreateNewWindow(
const ViewHostMsg_CreateWindow_Params& params,
SessionStorageNamespace* session_storage_namespace) {
ViewHostMsg_CreateWindow_Params validated_params(params);
- ChildProcessSecurityPolicyImpl* policy =
- ChildProcessSecurityPolicyImpl::GetInstance();
- FilterURL(policy, GetProcess(), false, &validated_params.target_url);
- FilterURL(policy, GetProcess(), false, &validated_params.opener_url);
- FilterURL(policy, GetProcess(), true,
- &validated_params.opener_security_origin);
+ GetProcess()->FilterURL(false, &validated_params.target_url);
+ GetProcess()->FilterURL(false, &validated_params.opener_url);
+ GetProcess()->FilterURL(true, &validated_params.opener_security_origin);
delegate_->CreateNewWindow(
GetProcess()->GetID(), route_id, main_frame_route_id, validated_params,
@@ -1371,7 +1079,7 @@ void RenderViewHostImpl::OnShowView(int route_id,
WindowOpenDisposition disposition,
const gfx::Rect& initial_pos,
bool user_gesture) {
- if (!is_swapped_out_) {
+ if (IsRVHStateActive(rvh_state_)) {
delegate_->ShowCreatedWindow(
route_id, disposition, initial_pos, user_gesture);
}
@@ -1380,13 +1088,13 @@ void RenderViewHostImpl::OnShowView(int route_id,
void RenderViewHostImpl::OnShowWidget(int route_id,
const gfx::Rect& initial_pos) {
- if (!is_swapped_out_)
+ if (IsRVHStateActive(rvh_state_))
delegate_->ShowCreatedWidget(route_id, initial_pos);
Send(new ViewMsg_Move_ACK(route_id));
}
void RenderViewHostImpl::OnShowFullscreenWidget(int route_id) {
- if (!is_swapped_out_)
+ if (IsRVHStateActive(rvh_state_))
delegate_->ShowCreatedFullscreenWidget(route_id);
Send(new ViewMsg_Move_ACK(route_id));
}
@@ -1396,7 +1104,7 @@ void RenderViewHostImpl::OnRunModal(int opener_id, IPC::Message* reply_msg) {
run_modal_reply_msg_ = reply_msg;
run_modal_opener_id_ = opener_id;
- RecordAction(UserMetricsAction("ShowModalDialog"));
+ RecordAction(base::UserMetricsAction("ShowModalDialog"));
RenderViewHostImpl* opener =
RenderViewHostImpl::FromID(GetProcess()->GetID(), run_modal_opener_id_);
@@ -1424,11 +1132,10 @@ void RenderViewHostImpl::OnRenderProcessGone(int status, int exit_code) {
render_view_termination_status_ =
static_cast<base::TerminationStatus>(status);
- // Reset frame tree state.
- // TODO(creis): Once subframes can be in different processes, we'll need to
- // clear just the FrameTreeNodes affected by the crash (and their subtrees).
- main_frame_id_ = -1;
- delegate_->GetFrameTree()->SwapMainFrame(main_render_frame_host_.get());
+ // Reset frame tree state associated with this process. This must happen
+ // before RenderViewTerminated because observers expect the subframes of any
+ // affected frames to be cleared first.
+ delegate_->GetFrameTree()->RenderProcessGone(this);
// Our base class RenderWidgetHost needs to reset some stuff.
RendererExited(render_view_termination_status_, exit_code);
@@ -1438,122 +1145,6 @@ void RenderViewHostImpl::OnRenderProcessGone(int status, int exit_code) {
exit_code);
}
-void RenderViewHostImpl::OnDidStartProvisionalLoadForFrame(
- int64 frame_id,
- int64 parent_frame_id,
- bool is_main_frame,
- const GURL& url) {
- NOTREACHED();
-}
-
-void RenderViewHostImpl::OnDidRedirectProvisionalLoad(
- int32 page_id,
- const GURL& source_url,
- const GURL& target_url) {
- delegate_->DidRedirectProvisionalLoad(
- this, page_id, source_url, target_url);
-}
-
-void RenderViewHostImpl::OnDidFailProvisionalLoadWithError(
- const ViewHostMsg_DidFailProvisionalLoadWithError_Params& params) {
- delegate_->DidFailProvisionalLoadWithError(this, params);
-}
-
-// Called when the renderer navigates. For every frame loaded, we'll get this
-// notification containing parameters identifying the navigation.
-//
-// Subframes are identified by the page transition type. For subframes loaded
-// as part of a wider page load, the page_id will be the same as for the top
-// level frame. If the user explicitly requests a subframe navigation, we will
-// get a new page_id because we need to create a new navigation entry for that
-// action.
-void RenderViewHostImpl::OnNavigate(const IPC::Message& msg) {
- // Read the parameters out of the IPC message directly to avoid making another
- // copy when we filter the URLs.
- PickleIterator iter(msg);
- ViewHostMsg_FrameNavigate_Params validated_params;
- if (!IPC::ParamTraits<ViewHostMsg_FrameNavigate_Params>::
- Read(&msg, &iter, &validated_params))
- return;
-
- // If we're waiting for a cross-site beforeunload ack from this renderer and
- // we receive a Navigate message from the main frame, then the renderer was
- // navigating already and sent it before hearing the ViewMsg_Stop message.
- // We do not want to cancel the pending navigation in this case, since the
- // old page will soon be stopped. Instead, treat this as a beforeunload ack
- // to allow the pending navigation to continue.
- if (is_waiting_for_beforeunload_ack_ &&
- unload_ack_is_for_cross_site_transition_ &&
- PageTransitionIsMainFrame(validated_params.transition)) {
- OnShouldCloseACK(true, send_should_close_start_time_,
- base::TimeTicks::Now());
- return;
- }
-
- // If we're waiting for an unload ack from this renderer and we receive a
- // Navigate message, then the renderer was navigating before it received the
- // unload request. It will either respond to the unload request soon or our
- // timer will expire. Either way, we should ignore this message, because we
- // have already committed to closing this renderer.
- if (is_waiting_for_unload_ack_)
- return;
-
- // Cache the main frame id, so we can use it for creating the frame tree
- // root node when needed.
- if (PageTransitionIsMainFrame(validated_params.transition)) {
- if (main_frame_id_ == -1) {
- main_frame_id_ = validated_params.frame_id;
- } else {
- // TODO(nasko): We plan to remove the usage of frame_id in navigation
- // and move to routing ids. This is in place to ensure that a
- // renderer is not misbehaving and sending us incorrect data.
- DCHECK_EQ(main_frame_id_, validated_params.frame_id);
- }
- }
- RenderProcessHost* process = GetProcess();
-
- // Attempts to commit certain off-limits URL should be caught more strictly
- // than our FilterURL checks below. If a renderer violates this policy, it
- // should be killed.
- if (!CanCommitURL(validated_params.url)) {
- VLOG(1) << "Blocked URL " << validated_params.url.spec();
- validated_params.url = GURL(kAboutBlankURL);
- RecordAction(UserMetricsAction("CanCommitURL_BlockedAndKilled"));
- // Kills the process.
- process->ReceivedBadMessage();
- }
-
- // Now that something has committed, we don't need to track whether the
- // initial page has been accessed.
- has_accessed_initial_document_ = false;
-
- ChildProcessSecurityPolicyImpl* policy =
- ChildProcessSecurityPolicyImpl::GetInstance();
- // Without this check, an evil renderer can trick the browser into creating
- // a navigation entry for a banned URL. If the user clicks the back button
- // followed by the forward button (or clicks reload, or round-trips through
- // session restore, etc), we'll think that the browser commanded the
- // renderer to load the URL and grant the renderer the privileges to request
- // the URL. To prevent this attack, we block the renderer from inserting
- // banned URLs into the navigation controller in the first place.
- FilterURL(policy, process, false, &validated_params.url);
- FilterURL(policy, process, true, &validated_params.referrer.url);
- for (std::vector<GURL>::iterator it(validated_params.redirects.begin());
- it != validated_params.redirects.end(); ++it) {
- FilterURL(policy, process, false, &(*it));
- }
- FilterURL(policy, process, true, &validated_params.searchable_form_url);
-
- // Without this check, the renderer can trick the browser into using
- // filenames it can't access in a future session restore.
- if (!CanAccessFilesOfPageState(validated_params.page_state)) {
- GetProcess()->ReceivedBadMessage();
- return;
- }
-
- delegate_->DidNavigate(this, validated_params);
-}
-
void RenderViewHostImpl::OnUpdateState(int32 page_id, const PageState& state) {
// Without this check, the renderer can trick the browser into using
// filenames it can't access in a future session restore.
@@ -1565,26 +1156,8 @@ void RenderViewHostImpl::OnUpdateState(int32 page_id, const PageState& state) {
delegate_->UpdateState(this, page_id, state);
}
-void RenderViewHostImpl::OnUpdateTitle(
- int32 page_id,
- const base::string16& title,
- blink::WebTextDirection title_direction) {
- if (title.length() > kMaxTitleChars) {
- NOTREACHED() << "Renderer sent too many characters in title.";
- return;
- }
-
- delegate_->UpdateTitle(this, page_id, title,
- WebTextDirectionToChromeTextDirection(
- title_direction));
-}
-
-void RenderViewHostImpl::OnUpdateEncoding(const std::string& encoding_name) {
- delegate_->UpdateEncoding(this, encoding_name);
-}
-
void RenderViewHostImpl::OnUpdateTargetURL(int32 page_id, const GURL& url) {
- if (!is_swapped_out_)
+ if (IsRVHStateActive(rvh_state_))
delegate_->UpdateTargetURL(page_id, url);
// Send a notification back to the renderer that we are ready to
@@ -1605,73 +1178,33 @@ void RenderViewHostImpl::OnClose() {
}
void RenderViewHostImpl::OnRequestMove(const gfx::Rect& pos) {
- if (!is_swapped_out_)
+ if (IsRVHStateActive(rvh_state_))
delegate_->RequestMove(pos);
Send(new ViewMsg_Move_ACK(GetRoutingID()));
}
-void RenderViewHostImpl::OnDidStartLoading() {
- delegate_->DidStartLoading(this);
-}
-
-void RenderViewHostImpl::OnDidStopLoading() {
- delegate_->DidStopLoading(this);
-}
-
-void RenderViewHostImpl::OnDidChangeLoadProgress(double load_progress) {
- delegate_->DidChangeLoadProgress(load_progress);
-}
-
-void RenderViewHostImpl::OnDidDisownOpener() {
- delegate_->DidDisownOpener(this);
-}
-
-void RenderViewHostImpl::OnDocumentAvailableInMainFrame() {
+void RenderViewHostImpl::OnDocumentAvailableInMainFrame(
+ bool uses_temporary_zoom_level) {
delegate_->DocumentAvailableInMainFrame(this);
-}
-
-void RenderViewHostImpl::OnDocumentOnLoadCompletedInMainFrame(
- int32 page_id) {
- delegate_->DocumentOnLoadCompletedInMainFrame(this, page_id);
-}
-
-void RenderViewHostImpl::OnContextMenu(const ContextMenuParams& params) {
- // Validate the URLs in |params|. If the renderer can't request the URLs
- // directly, don't show them in the context menu.
- ContextMenuParams validated_params(params);
- RenderProcessHost* process = GetProcess();
- ChildProcessSecurityPolicyImpl* policy =
- ChildProcessSecurityPolicyImpl::GetInstance();
- // We don't validate |unfiltered_link_url| so that this field can be used
- // when users want to copy the original link URL.
- FilterURL(policy, process, true, &validated_params.link_url);
- FilterURL(policy, process, true, &validated_params.src_url);
- FilterURL(policy, process, false, &validated_params.page_url);
- FilterURL(policy, process, true, &validated_params.frame_url);
+ if (!uses_temporary_zoom_level)
+ return;
- delegate_->ShowContextMenu(validated_params);
+ HostZoomMapImpl* host_zoom_map = static_cast<HostZoomMapImpl*>(
+ HostZoomMap::GetForBrowserContext(GetProcess()->GetBrowserContext()));
+ host_zoom_map->SetTemporaryZoomLevel(GetProcess()->GetID(),
+ GetRoutingID(),
+ host_zoom_map->GetDefaultZoomLevel());
}
void RenderViewHostImpl::OnToggleFullscreen(bool enter_fullscreen) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
delegate_->ToggleFullscreenMode(enter_fullscreen);
// We need to notify the contents that its fullscreen state has changed. This
// is done as part of the resize message.
WasResized();
}
-void RenderViewHostImpl::OnOpenURL(
- const ViewHostMsg_OpenURL_Params& params) {
- GURL validated_url(params.url);
- FilterURL(ChildProcessSecurityPolicyImpl::GetInstance(),
- GetProcess(), false, &validated_url);
-
- delegate_->RequestOpenURL(
- this, validated_url, params.referrer, params.disposition, params.frame_id,
- params.should_replace_current_entry, params.user_gesture);
-}
-
void RenderViewHostImpl::OnDidContentsPreferredSizeChange(
const gfx::Size& new_size) {
delegate_->UpdatePreferredSize(new_size);
@@ -1686,35 +1219,6 @@ void RenderViewHostImpl::OnDidChangeScrollOffset() {
view_->ScrollOffsetChanged();
}
-void RenderViewHostImpl::OnDidChangeScrollbarsForMainFrame(
- bool has_horizontal_scrollbar, bool has_vertical_scrollbar) {
- if (view_)
- view_->SetHasHorizontalScrollbar(has_horizontal_scrollbar);
-}
-
-void RenderViewHostImpl::OnDidChangeScrollOffsetPinningForMainFrame(
- bool is_pinned_to_left, bool is_pinned_to_right) {
- if (view_)
- view_->SetScrollOffsetPinning(is_pinned_to_left, is_pinned_to_right);
-}
-
-void RenderViewHostImpl::OnDidChangeNumWheelEvents(int count) {
-}
-
-void RenderViewHostImpl::OnSelectionChanged(const base::string16& text,
- size_t offset,
- const gfx::Range& range) {
- if (view_)
- view_->SelectionChanged(text, offset, range);
-}
-
-void RenderViewHostImpl::OnSelectionBoundsChanged(
- const ViewHostMsg_SelectionBounds_Params& params) {
- if (view_) {
- view_->SelectionBoundsChanged(params);
- }
-}
-
void RenderViewHostImpl::OnRouteCloseEvent() {
// Have the delegate route this to the active RenderViewHost.
delegate_->RouteCloseEvent(this);
@@ -1726,32 +1230,6 @@ void RenderViewHostImpl::OnRouteMessageEvent(
delegate_->RouteMessageEvent(this, params);
}
-void RenderViewHostImpl::OnRunJavaScriptMessage(
- const base::string16& message,
- const base::string16& default_prompt,
- const GURL& frame_url,
- JavaScriptMessageType type,
- IPC::Message* reply_msg) {
- // While a JS message dialog is showing, tabs in the same process shouldn't
- // process input events.
- GetProcess()->SetIgnoreInputEvents(true);
- StopHangMonitorTimeout();
- delegate_->RunJavaScriptMessage(this, message, default_prompt, frame_url,
- type, reply_msg,
- &are_javascript_messages_suppressed_);
-}
-
-void RenderViewHostImpl::OnRunBeforeUnloadConfirm(const GURL& frame_url,
- const base::string16& message,
- bool is_reload,
- IPC::Message* reply_msg) {
- // While a JS before unload dialog is showing, tabs in the same process
- // shouldn't process input events.
- GetProcess()->SetIgnoreInputEvents(true);
- StopHangMonitorTimeout();
- delegate_->RunBeforeUnloadConfirm(this, message, is_reload, reply_msg);
-}
-
void RenderViewHostImpl::OnStartDragging(
const DropData& drop_data,
WebDragOperationsMask drag_operations_mask,
@@ -1768,9 +1246,9 @@ void RenderViewHostImpl::OnStartDragging(
ChildProcessSecurityPolicyImpl::GetInstance();
// Allow drag of Javascript URLs to enable bookmarklet drag to bookmark bar.
- if (!filtered_data.url.SchemeIs(kJavaScriptScheme))
- FilterURL(policy, process, true, &filtered_data.url);
- FilterURL(policy, process, false, &filtered_data.html_base_url);
+ if (!filtered_data.url.SchemeIs(url::kJavaScriptScheme))
+ process->FilterURL(true, &filtered_data.url);
+ process->FilterURL(false, &filtered_data.html_base_url);
// Filter out any paths that the renderer didn't have access to. This prevents
// the following attack on a malicious renderer:
// 1. StartDragging IPC sent with renderer-specified filesystem paths that it
@@ -1780,14 +1258,27 @@ void RenderViewHostImpl::OnStartDragging(
// still fire though, which causes read permissions to be granted to the
// renderer for any file paths in the drop.
filtered_data.filenames.clear();
- for (std::vector<DropData::FileInfo>::const_iterator it =
+ for (std::vector<ui::FileInfo>::const_iterator it =
drop_data.filenames.begin();
- it != drop_data.filenames.end(); ++it) {
- base::FilePath path(base::FilePath::FromUTF8Unsafe(UTF16ToUTF8(it->path)));
- if (policy->CanReadFile(GetProcess()->GetID(), path))
+ it != drop_data.filenames.end();
+ ++it) {
+ if (policy->CanReadFile(GetProcess()->GetID(), it->path))
filtered_data.filenames.push_back(*it);
}
- float scale = ui::GetImageScale(GetScaleFactorForView(GetView()));
+
+ fileapi::FileSystemContext* file_system_context =
+ BrowserContext::GetStoragePartition(
+ GetProcess()->GetBrowserContext(),
+ GetSiteInstance())->GetFileSystemContext();
+ filtered_data.file_system_files.clear();
+ for (size_t i = 0; i < drop_data.file_system_files.size(); ++i) {
+ fileapi::FileSystemURL file_system_url =
+ file_system_context->CrackURL(drop_data.file_system_files[i].url);
+ if (policy->CanReadFileSystemFile(GetProcess()->GetID(), file_system_url))
+ filtered_data.file_system_files.push_back(drop_data.file_system_files[i]);
+ }
+
+ float scale = GetScaleFactorForView(GetView());
gfx::ImageSkia image(gfx::ImageSkiaRep(bitmap, scale));
view->StartDragging(filtered_data, drag_operations_mask, image,
bitmap_offset_in_dip, event_info);
@@ -1813,7 +1304,10 @@ void RenderViewHostImpl::OnTakeFocus(bool reverse) {
}
void RenderViewHostImpl::OnFocusedNodeChanged(bool is_editable_node) {
-#if defined(OS_WIN) && defined(USE_AURA)
+ is_focused_element_editable_ = is_editable_node;
+ if (view_)
+ view_->FocusedNodeChanged(is_editable_node);
+#if defined(OS_WIN)
if (!is_editable_node && virtual_keyboard_requested_) {
virtual_keyboard_requested_ = false;
BrowserThread::PostDelayedTask(
@@ -1828,72 +1322,10 @@ void RenderViewHostImpl::OnFocusedNodeChanged(bool is_editable_node) {
Details<const bool>(&is_editable_node));
}
-void RenderViewHostImpl::OnAddMessageToConsole(
- int32 level,
- const base::string16& message,
- int32 line_no,
- const base::string16& source_id) {
- if (delegate_->AddMessageToConsole(level, message, line_no, source_id))
- return;
-
- // Pass through log level only on WebUI pages to limit console spew.
- int32 resolved_level = HasWebUIScheme(delegate_->GetURL()) ? level : 0;
-
- if (resolved_level >= ::logging::GetMinLogLevel()) {
- logging::LogMessage("CONSOLE", line_no, resolved_level).stream() << "\"" <<
- message << "\", source: " << source_id << " (" << line_no << ")";
- }
-}
-
void RenderViewHostImpl::OnUserGesture() {
delegate_->OnUserGesture();
}
-void RenderViewHostImpl::OnShouldCloseACK(
- bool proceed,
- const base::TimeTicks& renderer_before_unload_start_time,
- const base::TimeTicks& renderer_before_unload_end_time) {
- decrement_in_flight_event_count();
- StopHangMonitorTimeout();
- // If this renderer navigated while the beforeunload request was in flight, we
- // may have cleared this state in OnNavigate, in which case we can ignore
- // this message.
- if (!is_waiting_for_beforeunload_ack_ || is_swapped_out_)
- return;
-
- is_waiting_for_beforeunload_ack_ = false;
-
- RenderViewHostDelegate::RendererManagement* management_delegate =
- delegate_->GetRendererManagementDelegate();
- if (management_delegate) {
- base::TimeTicks before_unload_end_time;
- if (!send_should_close_start_time_.is_null() &&
- !renderer_before_unload_start_time.is_null() &&
- !renderer_before_unload_end_time.is_null()) {
- // When passing TimeTicks across process boundaries, we need to compensate
- // for any skew between the processes. Here we are converting the
- // renderer's notion of before_unload_end_time to TimeTicks in the browser
- // process. See comments in inter_process_time_ticks_converter.h for more.
- InterProcessTimeTicksConverter converter(
- LocalTimeTicks::FromTimeTicks(send_should_close_start_time_),
- LocalTimeTicks::FromTimeTicks(base::TimeTicks::Now()),
- RemoteTimeTicks::FromTimeTicks(renderer_before_unload_start_time),
- RemoteTimeTicks::FromTimeTicks(renderer_before_unload_end_time));
- LocalTimeTicks browser_before_unload_end_time =
- converter.ToLocalTimeTicks(
- RemoteTimeTicks::FromTimeTicks(renderer_before_unload_end_time));
- before_unload_end_time = browser_before_unload_end_time.ToTimeTicks();
- }
- management_delegate->ShouldClosePage(
- unload_ack_is_for_cross_site_transition_, proceed,
- before_unload_end_time);
- }
-
- // If canceled, notify the delegate to cancel its pending navigation entry.
- if (!proceed)
- delegate_->DidCancelLoading();
-}
-
void RenderViewHostImpl::OnClosePageACK() {
decrement_in_flight_event_count();
ClosePageIgnoringUnloadEvents();
@@ -1901,7 +1333,7 @@ void RenderViewHostImpl::OnClosePageACK() {
void RenderViewHostImpl::NotifyRendererUnresponsive() {
delegate_->RendererUnresponsive(
- this, is_waiting_for_beforeunload_ack_, is_waiting_for_unload_ack_);
+ this, is_waiting_for_beforeunload_ack_, IsWaitingForUnloadACK());
}
void RenderViewHostImpl::NotifyRendererResponsive() {
@@ -1998,69 +1430,20 @@ void RenderViewHostImpl::DidCancelPopupMenu() {
}
#endif
-void RenderViewHostImpl::SendOrientationChangeEvent(int orientation) {
- Send(new ViewMsg_OrientationChangeEvent(GetRoutingID(), orientation));
+bool RenderViewHostImpl::IsWaitingForUnloadACK() const {
+ return rvh_state_ == STATE_WAITING_FOR_UNLOAD_ACK ||
+ rvh_state_ == STATE_WAITING_FOR_CLOSE ||
+ rvh_state_ == STATE_PENDING_SHUTDOWN ||
+ rvh_state_ == STATE_PENDING_SWAP_OUT;
}
-void RenderViewHostImpl::ToggleSpeechInput() {
- Send(new InputTagSpeechMsg_ToggleSpeechInput(GetRoutingID()));
-}
-
-bool RenderViewHostImpl::CanCommitURL(const GURL& url) {
- // TODO(creis): We should also check for WebUI pages here. Also, when the
- // out-of-process iframes implementation is ready, we should check for
- // cross-site URLs that are not allowed to commit in this process.
-
- // Give the client a chance to disallow URLs from committing.
- return GetContentClient()->browser()->CanCommitURL(GetProcess(), url);
-}
-
-void RenderViewHostImpl::FilterURL(ChildProcessSecurityPolicyImpl* policy,
- const RenderProcessHost* process,
- bool empty_allowed,
- GURL* url) {
- if (empty_allowed && url->is_empty())
+void RenderViewHostImpl::OnTextSurroundingSelectionResponse(
+ const base::string16& content,
+ size_t start_offset,
+ size_t end_offset) {
+ if (!view_)
return;
-
- // The browser process should never hear the swappedout:// URL from any
- // of the renderer's messages. Check for this in debug builds, but don't
- // let it crash a release browser.
- DCHECK(GURL(kSwappedOutURL) != *url);
-
- if (!url->is_valid()) {
- // Have to use about:blank for the denied case, instead of an empty GURL.
- // This is because the browser treats navigation to an empty GURL as a
- // navigation to the home page. This is often a privileged page
- // (chrome://newtab/) which is exactly what we don't want.
- *url = GURL(kAboutBlankURL);
- RecordAction(UserMetricsAction("FilterURLTermiate_Invalid"));
- return;
- }
-
- if (url->SchemeIs(chrome::kAboutScheme)) {
- // The renderer treats all URLs in the about: scheme as being about:blank.
- // Canonicalize about: URLs to about:blank.
- *url = GURL(kAboutBlankURL);
- RecordAction(UserMetricsAction("FilterURLTermiate_About"));
- }
-
- // Do not allow browser plugin guests to navigate to non-web URLs, since they
- // cannot swap processes or grant bindings.
- bool non_web_url_in_guest = process->IsGuest() &&
- !(url->is_valid() && policy->IsWebSafeScheme(url->scheme()));
-
- if (non_web_url_in_guest || !policy->CanRequestURL(process->GetID(), *url)) {
- // If this renderer is not permitted to request this URL, we invalidate the
- // URL. This prevents us from storing the blocked URL and becoming confused
- // later.
- VLOG(1) << "Blocked URL " << url->spec();
- *url = GURL(kAboutBlankURL);
- RecordAction(UserMetricsAction("FilterURLTermiate_Blocked"));
- }
-}
-
-void RenderViewHostImpl::SetAltErrorPageURL(const GURL& url) {
- Send(new ViewMsg_SetAltErrorPageURL(GetRoutingID(), url));
+ view_->OnTextSurroundingSelectionResponse(content, start_offset, end_offset);
}
void RenderViewHostImpl::ExitFullscreen() {
@@ -2075,13 +1458,13 @@ WebPreferences RenderViewHostImpl::GetWebkitPreferences() {
void RenderViewHostImpl::DisownOpener() {
// This should only be called when swapped out.
- DCHECK(is_swapped_out_);
+ DCHECK(IsSwappedOut());
Send(new ViewMsg_DisownOpener(GetRoutingID()));
}
void RenderViewHostImpl::SetAccessibilityCallbackForTesting(
- const base::Callback<void(blink::WebAXEvent)>& callback) {
+ const base::Callback<void(ui::AXEvent, int)>& callback) {
accessibility_testing_callback_ = callback;
}
@@ -2089,10 +1472,6 @@ void RenderViewHostImpl::UpdateWebkitPreferences(const WebPreferences& prefs) {
Send(new ViewMsg_UpdateWebPreferences(GetRoutingID(), prefs));
}
-void RenderViewHostImpl::NotifyTimezoneChange() {
- Send(new ViewMsg_TimezoneChange(GetRoutingID()));
-}
-
void RenderViewHostImpl::GetAudioOutputControllers(
const GetAudioOutputControllersCallback& callback) const {
AudioRendererHost* audio_host =
@@ -2100,27 +1479,17 @@ void RenderViewHostImpl::GetAudioOutputControllers(
audio_host->GetOutputControllers(GetRoutingID(), callback);
}
-void RenderViewHostImpl::ClearFocusedNode() {
- Send(new ViewMsg_ClearFocusedNode(GetRoutingID()));
-}
-
-void RenderViewHostImpl::Zoom(PageZoom zoom) {
- Send(new ViewMsg_Zoom(GetRoutingID(), zoom));
-}
-
-void RenderViewHostImpl::ReloadFrame() {
- Send(new ViewMsg_ReloadFrame(GetRoutingID()));
+void RenderViewHostImpl::ClearFocusedElement() {
+ is_focused_element_editable_ = false;
+ Send(new ViewMsg_ClearFocusedElement(GetRoutingID()));
}
-void RenderViewHostImpl::Find(int request_id,
- const base::string16& search_text,
- const blink::WebFindOptions& options) {
- Send(new ViewMsg_Find(GetRoutingID(), request_id, search_text, options));
+bool RenderViewHostImpl::IsFocusedElementEditable() {
+ return is_focused_element_editable_;
}
-void RenderViewHostImpl::InsertCSS(const base::string16& frame_xpath,
- const std::string& css) {
- Send(new ViewMsg_CSSInsertRequest(GetRoutingID(), frame_xpath, css));
+void RenderViewHostImpl::Zoom(PageZoom zoom) {
+ Send(new ViewMsg_Zoom(GetRoutingID(), zoom));
}
void RenderViewHostImpl::DisableScrollbarsForThreshold(const gfx::Size& size) {
@@ -2142,20 +1511,14 @@ void RenderViewHostImpl::DisableAutoResize(const gfx::Size& new_size) {
Send(new ViewMsg_DisableAutoResize(GetRoutingID(), new_size));
}
-void RenderViewHostImpl::ExecuteCustomContextMenuCommand(
- int action, const CustomContextMenuContext& context) {
- Send(new ViewMsg_CustomContextMenuAction(GetRoutingID(), context, action));
-}
-
-void RenderViewHostImpl::NotifyContextMenuClosed(
- const CustomContextMenuContext& context) {
- Send(new ViewMsg_ContextMenuClosed(GetRoutingID(), context));
-}
-
void RenderViewHostImpl::CopyImageAt(int x, int y) {
Send(new ViewMsg_CopyImageAt(GetRoutingID(), x, y));
}
+void RenderViewHostImpl::SaveImageAt(int x, int y) {
+ Send(new ViewMsg_SaveImageAt(GetRoutingID(), x, y));
+}
+
void RenderViewHostImpl::ExecuteMediaPlayerActionAtLocation(
const gfx::Point& location, const blink::WebMediaPlayerAction& action) {
Send(new ViewMsg_MediaPlayerActionAt(GetRoutingID(), location, action));
@@ -2170,14 +1533,31 @@ void RenderViewHostImpl::NotifyMoveOrResizeStarted() {
Send(new ViewMsg_MoveOrResizeStarted(GetRoutingID()));
}
-void RenderViewHostImpl::StopFinding(StopFindAction action) {
- Send(new ViewMsg_StopFinding(GetRoutingID(), action));
-}
-
void RenderViewHostImpl::OnAccessibilityEvents(
const std::vector<AccessibilityHostMsg_EventParams>& params) {
- if (view_ && !is_swapped_out_)
- view_->OnAccessibilityEvents(params);
+ if ((accessibility_mode() != AccessibilityModeOff) && view_ &&
+ IsRVHStateActive(rvh_state_)) {
+ if (accessibility_mode() & AccessibilityModeFlagPlatform) {
+ view_->CreateBrowserAccessibilityManagerIfNeeded();
+ BrowserAccessibilityManager* manager =
+ view_->GetBrowserAccessibilityManager();
+ if (manager)
+ manager->OnAccessibilityEvents(params);
+ }
+
+ std::vector<AXEventNotificationDetails> details;
+ for (unsigned int i = 0; i < params.size(); ++i) {
+ const AccessibilityHostMsg_EventParams& param = params[i];
+ AXEventNotificationDetails detail(param.update.nodes,
+ param.event_type,
+ param.id,
+ GetProcess()->GetID(),
+ GetRoutingID());
+ details.push_back(detail);
+ }
+
+ delegate_->AccessibilityEventReceived(details);
+ }
// Always send an ACK or the renderer can be in a bad state.
Send(new AccessibilityMsg_Events_ACK(GetRoutingID()));
@@ -2189,86 +1569,47 @@ void RenderViewHostImpl::OnAccessibilityEvents(
for (unsigned i = 0; i < params.size(); i++) {
const AccessibilityHostMsg_EventParams& param = params[i];
- blink::WebAXEvent src_type = param.event_type;
- if (src_type == blink::WebAXEventLayoutComplete ||
- src_type == blink::WebAXEventLoadComplete) {
- MakeAccessibilityNodeDataTree(param.nodes, &accessibility_tree_);
- }
- accessibility_testing_callback_.Run(src_type);
+ if (static_cast<int>(param.event_type) < 0)
+ continue;
+ if (!ax_tree_)
+ ax_tree_.reset(new ui::AXTree(param.update));
+ else
+ CHECK(ax_tree_->Unserialize(param.update)) << ax_tree_->error();
+ accessibility_testing_callback_.Run(param.event_type, param.id);
}
}
-void RenderViewHostImpl::OnScriptEvalResponse(int id,
- const base::ListValue& result) {
- const base::Value* result_value;
- if (!result.Get(0, &result_value)) {
- // Programming error or rogue renderer.
- NOTREACHED() << "Got bad arguments for OnScriptEvalResponse";
- return;
- }
-
- std::map<int, JavascriptResultCallback>::iterator it =
- javascript_callbacks_.find(id);
- if (it != javascript_callbacks_.end()) {
- // ExecuteJavascriptInWebFrameCallbackResult was used; do callback.
- it->second.Run(result_value);
- javascript_callbacks_.erase(it);
- } else {
- NOTREACHED() << "Received script response for unknown request";
+void RenderViewHostImpl::OnAccessibilityLocationChanges(
+ const std::vector<AccessibilityHostMsg_LocationChangeParams>& params) {
+ if (view_ && IsRVHStateActive(rvh_state_)) {
+ if (accessibility_mode() & AccessibilityModeFlagPlatform) {
+ view_->CreateBrowserAccessibilityManagerIfNeeded();
+ BrowserAccessibilityManager* manager =
+ view_->GetBrowserAccessibilityManager();
+ if (manager)
+ manager->OnLocationChanges(params);
+ }
+ // TODO(aboxhall): send location change events to web contents observers too
}
}
void RenderViewHostImpl::OnDidZoomURL(double zoom_level,
- bool remember,
const GURL& url) {
HostZoomMapImpl* host_zoom_map = static_cast<HostZoomMapImpl*>(
HostZoomMap::GetForBrowserContext(GetProcess()->GetBrowserContext()));
- if (remember) {
- host_zoom_map->
- SetZoomLevelForHost(net::GetHostOrSpecFromURL(url), zoom_level);
- } else {
- host_zoom_map->SetTemporaryZoomLevel(
- GetProcess()->GetID(), GetRoutingID(), zoom_level);
- }
-}
-void RenderViewHostImpl::OnRequestDesktopNotificationPermission(
- const GURL& source_origin, int callback_context) {
- GetContentClient()->browser()->RequestDesktopNotificationPermission(
- source_origin, callback_context, GetProcess()->GetID(), GetRoutingID());
-}
-
-void RenderViewHostImpl::OnShowDesktopNotification(
- const ShowDesktopNotificationHostMsgParams& params) {
- GetContentClient()->browser()->ShowDesktopNotification(
- params, GetProcess()->GetID(), GetRoutingID(), false);
-}
-
-void RenderViewHostImpl::OnCancelDesktopNotification(int notification_id) {
- GetContentClient()->browser()->CancelDesktopNotification(
- GetProcess()->GetID(), GetRoutingID(), notification_id);
+ host_zoom_map->SetZoomLevelForView(GetProcess()->GetID(),
+ GetRoutingID(),
+ zoom_level,
+ net::GetHostOrSpecFromURL(url));
}
void RenderViewHostImpl::OnRunFileChooser(const FileChooserParams& params) {
delegate_->RunFileChooser(this, params);
}
-void RenderViewHostImpl::OnDidAccessInitialDocument() {
- has_accessed_initial_document_ = true;
- delegate_->DidAccessInitialDocument();
-}
-
-void RenderViewHostImpl::OnDomOperationResponse(
- const std::string& json_string, int automation_id) {
- DomOperationNotificationDetails details(json_string, automation_id);
- NotificationService::current()->Notify(
- NOTIFICATION_DOM_OPERATION_RESPONSE,
- Source<RenderViewHost>(this),
- Details<DomOperationNotificationDetails>(&details));
-}
-
void RenderViewHostImpl::OnFocusedNodeTouched(bool editable) {
-#if defined(OS_WIN) && defined(USE_AURA)
+#if defined(OS_WIN)
if (editable) {
virtual_keyboard_requested_ = base::win::DisplayVirtualKeyboard();
} else {
@@ -2292,24 +1633,33 @@ void RenderViewHostImpl::OnShowPopup(
params.allow_multiple_selection);
}
}
+
+void RenderViewHostImpl::OnHidePopup() {
+ RenderViewHostDelegateView* view = delegate_->GetDelegateView();
+ if (view)
+ view->HidePopupMenu();
+}
#endif
-void RenderViewHostImpl::SetSwappedOut(bool is_swapped_out) {
+void RenderViewHostImpl::SetState(RenderViewHostImplState rvh_state) {
// We update the number of RenderViews in a SiteInstance when the
- // swapped out status of this RenderView gets flipped.
- if (is_swapped_out_ && !is_swapped_out)
+ // swapped out status of this RenderView gets flipped to/from live.
+ if (!IsRVHStateActive(rvh_state_) && IsRVHStateActive(rvh_state))
instance_->increment_active_view_count();
- else if (!is_swapped_out_ && is_swapped_out)
+ else if (IsRVHStateActive(rvh_state_) && !IsRVHStateActive(rvh_state))
instance_->decrement_active_view_count();
- is_swapped_out_ = is_swapped_out;
+ // Whenever we change the RVH state to and from live or swapped out state, we
+ // should not be waiting for beforeunload or unload acks. We clear them here
+ // to be safe, since they can cause navigations to be ignored in OnNavigate.
+ if (rvh_state == STATE_DEFAULT ||
+ rvh_state == STATE_SWAPPED_OUT ||
+ rvh_state_ == STATE_DEFAULT ||
+ rvh_state_ == STATE_SWAPPED_OUT) {
+ is_waiting_for_beforeunload_ack_ = false;
+ }
+ rvh_state_ = rvh_state;
- // Whenever we change swap out state, we should not be waiting for
- // beforeunload or unload acks. We clear them here to be safe, since they
- // can cause navigations to be ignored in OnNavigate.
- is_waiting_for_beforeunload_ack_ = false;
- is_waiting_for_unload_ack_ = false;
- has_timed_out_on_unload_ = false;
}
bool RenderViewHostImpl::CanAccessFilesOfPageState(
@@ -2329,10 +1679,11 @@ bool RenderViewHostImpl::CanAccessFilesOfPageState(
void RenderViewHostImpl::AttachToFrameTree() {
FrameTree* frame_tree = delegate_->GetFrameTree();
- frame_tree->SwapMainFrame(main_render_frame_host_.get());
- if (main_frame_id() != FrameTreeNode::kInvalidFrameId) {
- frame_tree->OnFirstNavigationAfterSwap(main_frame_id());
- }
+ frame_tree->ResetForMainFrameSwap();
+}
+
+void RenderViewHostImpl::SelectWordAroundCaret() {
+ Send(new ViewMsg_SelectWordAroundCaret(GetRoutingID()));
}
} // namespace content
diff --git a/chromium/content/browser/renderer_host/render_view_host_impl.h b/chromium/content/browser/renderer_host/render_view_host_impl.h
index e92b9397c70..5f7344f7f5b 100644
--- a/chromium/content/browser/renderer_host/render_view_host_impl.h
+++ b/chromium/content/browser/renderer_host/render_view_host_impl.h
@@ -9,40 +9,34 @@
#include <string>
#include <vector>
+#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/process/kill.h"
-#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/site_instance_impl.h"
-#include "content/common/accessibility_node_data.h"
#include "content/common/drag_event_source_info.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/render_view_host.h"
-#include "content/public/common/javascript_message_type.h"
-#include "content/public/common/page_transition_types.h"
#include "content/public/common/window_container_type.h"
#include "net/base/load_states.h"
#include "third_party/WebKit/public/web/WebAXEnums.h"
#include "third_party/WebKit/public/web/WebConsoleMessage.h"
#include "third_party/WebKit/public/web/WebPopupType.h"
-#include "third_party/WebKit/public/web/WebTextDirection.h"
#include "third_party/skia/include/core/SkColor.h"
+#include "ui/accessibility/ax_node_data.h"
#include "ui/base/window_open_disposition.h"
class SkBitmap;
-class ViewMsg_Navigate;
+class FrameMsg_Navigate;
struct AccessibilityHostMsg_EventParams;
+struct AccessibilityHostMsg_LocationChangeParams;
struct MediaPlayerAction;
struct ViewHostMsg_CreateWindow_Params;
-struct ViewHostMsg_DidFailProvisionalLoadWithError_Params;
-struct ViewHostMsg_OpenURL_Params;
-struct ViewHostMsg_SelectionBounds_Params;
struct ViewHostMsg_ShowPopup_Params;
-struct ViewMsg_Navigate_Params;
+struct FrameMsg_Navigate_Params;
struct ViewMsg_PostMessage_Params;
-struct ViewMsg_StopFinding_Params;
namespace base {
class ListValue;
@@ -53,24 +47,21 @@ class Range;
}
namespace ui {
+class AXTree;
struct SelectedFileInfo;
}
namespace content {
-class BrowserMediaPlayerManager;
+class MediaWebContentsObserver;
class ChildProcessSecurityPolicyImpl;
class PageState;
-class RenderFrameHostDelegate;
-class RenderFrameHostImpl;
class RenderWidgetHostDelegate;
class SessionStorageNamespace;
class SessionStorageNamespaceImpl;
class TestRenderViewHost;
-struct ContextMenuParams;
+class TimeoutMonitor;
struct FileChooserParams;
-struct Referrer;
-struct ShowDesktopNotificationHostMsgParams;
#if defined(COMPILER_MSVC)
// RenderViewHostImpl is the bottom of a diamond-shaped hierarchy,
@@ -102,6 +93,39 @@ class CONTENT_EXPORT RenderViewHostImpl
: public RenderViewHost,
public RenderWidgetHostImpl {
public:
+ // Keeps track of the state of the RenderViewHostImpl, particularly with
+ // respect to swap out.
+ enum RenderViewHostImplState {
+ // The standard state for a RVH handling the communication with a
+ // RenderView.
+ STATE_DEFAULT = 0,
+ // The RVH has sent the SwapOut request to the renderer, but has not
+ // received the SwapOutACK yet. The new page has not been committed yet
+ // either.
+ STATE_WAITING_FOR_UNLOAD_ACK,
+ // The RVH received the SwapOutACK from the RenderView, but the new page has
+ // not been committed yet.
+ STATE_WAITING_FOR_COMMIT,
+ // The RVH is waiting for the CloseACK from the RenderView.
+ STATE_WAITING_FOR_CLOSE,
+ // The RVH has not received the SwapOutACK yet, but the new page has
+ // committed in a different RVH. The number of active views of the RVH
+ // SiteInstanceImpl is not zero. Upon reception of the SwapOutACK, the RVH
+ // will be swapped out.
+ STATE_PENDING_SWAP_OUT,
+ // The RVH has not received the SwapOutACK yet, but the new page has
+ // committed in a different RVH. The number of active views of the RVH
+ // SiteInstanceImpl is zero. Upon reception of the SwapOutACK, the RVH will
+ // be shutdown.
+ STATE_PENDING_SHUTDOWN,
+ // The RVH is swapped out, and it is being used as a placeholder to allow
+ // for cross-process communication.
+ STATE_SWAPPED_OUT,
+ };
+ // Helper function to determine whether the RVH state should contribute to the
+ // number of active views of a SiteInstance or not.
+ static bool IsRVHStateActive(RenderViewHostImplState rvh_state);
+
// Convenience function, just like RenderViewHost::FromID.
static RenderViewHostImpl* FromID(int render_process_id, int render_view_id);
@@ -119,7 +143,6 @@ class CONTENT_EXPORT RenderViewHostImpl
RenderViewHostImpl(
SiteInstance* instance,
RenderViewHostDelegate* delegate,
- RenderFrameHostDelegate* frame_delegate,
RenderWidgetHostDelegate* widget_delegate,
int routing_id,
int main_frame_routing_id,
@@ -128,19 +151,13 @@ class CONTENT_EXPORT RenderViewHostImpl
virtual ~RenderViewHostImpl();
// RenderViewHost implementation.
+ virtual RenderFrameHost* GetMainFrame() OVERRIDE;
virtual void AllowBindings(int binding_flags) OVERRIDE;
- virtual void ClearFocusedNode() OVERRIDE;
+ virtual void ClearFocusedElement() OVERRIDE;
+ virtual bool IsFocusedElementEditable() OVERRIDE;
virtual void ClosePage() OVERRIDE;
virtual void CopyImageAt(int x, int y) OVERRIDE;
- virtual void DesktopNotificationPermissionRequestDone(
- int callback_context) OVERRIDE;
- virtual void DesktopNotificationPostDisplay(int callback_context) OVERRIDE;
- virtual void DesktopNotificationPostError(
- int notification_id,
- const base::string16& message) OVERRIDE;
- virtual void DesktopNotificationPostClose(int notification_id,
- bool by_user) OVERRIDE;
- virtual void DesktopNotificationPostClick(int notification_id) OVERRIDE;
+ virtual void SaveImageAt(int x, int y) OVERRIDE;
virtual void DirectoryEnumerationFinished(
int request_id,
const std::vector<base::FilePath>& files) OVERRIDE;
@@ -148,8 +165,6 @@ class CONTENT_EXPORT RenderViewHostImpl
virtual void DragSourceEndedAt(
int client_x, int client_y, int screen_x, int screen_y,
blink::WebDragOperation operation) OVERRIDE;
- virtual void DragSourceMovedTo(
- int client_x, int client_y, int screen_x, int screen_y) OVERRIDE;
virtual void DragSourceSystemDragEnded() OVERRIDE;
virtual void DragTargetDragEnter(
const DropData& drop_data,
@@ -170,59 +185,38 @@ class CONTENT_EXPORT RenderViewHostImpl
const gfx::Size& max_size) OVERRIDE;
virtual void DisableAutoResize(const gfx::Size& new_size) OVERRIDE;
virtual void EnablePreferredSizeMode() OVERRIDE;
- virtual void ExecuteCustomContextMenuCommand(
- int action, const CustomContextMenuContext& context) OVERRIDE;
virtual void ExecuteMediaPlayerActionAtLocation(
const gfx::Point& location,
const blink::WebMediaPlayerAction& action) OVERRIDE;
- virtual void ExecuteJavascriptInWebFrame(
- const base::string16& frame_xpath,
- const base::string16& jscript) OVERRIDE;
- virtual void ExecuteJavascriptInWebFrameCallbackResult(
- const base::string16& frame_xpath,
- const base::string16& jscript,
- const JavascriptResultCallback& callback) OVERRIDE;
virtual void ExecutePluginActionAtLocation(
const gfx::Point& location,
const blink::WebPluginAction& action) OVERRIDE;
virtual void ExitFullscreen() OVERRIDE;
- virtual void Find(int request_id, const base::string16& search_text,
- const blink::WebFindOptions& options) OVERRIDE;
- virtual void StopFinding(StopFindAction action) OVERRIDE;
- virtual void FirePageBeforeUnload(bool for_cross_site_transition) OVERRIDE;
virtual void FilesSelectedInChooser(
const std::vector<ui::SelectedFileInfo>& files,
FileChooserParams::Mode permissions) OVERRIDE;
virtual RenderViewHostDelegate* GetDelegate() const OVERRIDE;
virtual int GetEnabledBindings() const OVERRIDE;
virtual SiteInstance* GetSiteInstance() const OVERRIDE;
- virtual void InsertCSS(const base::string16& frame_xpath,
- const std::string& css) OVERRIDE;
virtual bool IsRenderViewLive() const OVERRIDE;
- virtual bool IsSubframe() const OVERRIDE;
- virtual void NotifyContextMenuClosed(
- const CustomContextMenuContext& context) OVERRIDE;
virtual void NotifyMoveOrResizeStarted() OVERRIDE;
- virtual void ReloadFrame() OVERRIDE;
- virtual void SetAltErrorPageURL(const GURL& url) OVERRIDE;
virtual void SetWebUIProperty(const std::string& name,
const std::string& value) OVERRIDE;
virtual void Zoom(PageZoom zoom) OVERRIDE;
virtual void SyncRendererPrefs() OVERRIDE;
- virtual void ToggleSpeechInput() OVERRIDE;
virtual WebPreferences GetWebkitPreferences() OVERRIDE;
virtual void UpdateWebkitPreferences(
const WebPreferences& prefs) OVERRIDE;
- virtual void NotifyTimezoneChange() OVERRIDE;
virtual void GetAudioOutputControllers(
const GetAudioOutputControllersCallback& callback) const OVERRIDE;
+ virtual void SetWebUIHandle(mojo::ScopedMessagePipeHandle handle) OVERRIDE;
+ virtual void SelectWordAroundCaret() OVERRIDE;
#if defined(OS_ANDROID)
virtual void ActivateNearestFindResult(int request_id,
float x,
float y) OVERRIDE;
virtual void RequestFindMatchRects(int current_version) OVERRIDE;
- virtual void DisableFullscreenEncryptedMediaPlayback() OVERRIDE;
#endif
void set_delegate(RenderViewHostDelegate* d) {
@@ -236,9 +230,15 @@ class CONTENT_EXPORT RenderViewHostImpl
// The |opener_route_id| parameter indicates which RenderView created this
// (MSG_ROUTING_NONE if none). If |max_page_id| is larger than -1, the
// RenderView is told to start issuing page IDs at |max_page_id| + 1.
+ // |window_was_created_with_opener| is true if this top-level frame was
+ // created with an opener. (The opener may have been closed since.)
+ // The |proxy_route_id| is only used when creating a RenderView in swapped out
+ // state.
virtual bool CreateRenderView(const base::string16& frame_name,
int opener_route_id,
- int32 max_page_id);
+ int proxy_route_id,
+ int32 max_page_id,
+ bool window_was_created_with_opener);
base::TerminationStatus render_view_termination_status() const {
return render_view_termination_status_;
@@ -254,9 +254,13 @@ class CONTENT_EXPORT RenderViewHostImpl
// If a cross-site request is in progress, we may be suspended while waiting
// for the onbeforeunload handler, so this function might buffer the message
// rather than sending it.
- void Navigate(const ViewMsg_Navigate_Params& message);
+ // TODO(nasko): Remove this method once all callers are converted to use
+ // RenderFrameHostImpl.
+ void Navigate(const FrameMsg_Navigate_Params& message);
// Load the specified URL, this is a shortcut for Navigate().
+ // TODO(nasko): Remove this method once all callers are converted to use
+ // RenderFrameHostImpl.
void NavigateToURL(const GURL& url);
// Returns whether navigation messages are currently suspended for this
@@ -283,29 +287,12 @@ class CONTENT_EXPORT RenderViewHostImpl
// RenderViewHost.
void CancelSuspendedNavigations();
- // Whether the initial empty page of this view has been accessed by another
- // page, making it unsafe to show the pending URL. Always false after the
- // first commit.
- bool has_accessed_initial_document() {
- return has_accessed_initial_document_;
- }
-
// Whether this RenderViewHost has been swapped out to be displayed by a
// different process.
- bool is_swapped_out() const { return is_swapped_out_; }
-
- // Called on the pending RenderViewHost when the network response is ready to
- // commit. We should ensure that the old RenderViewHost runs its unload
- // handler and determine whether a transfer to a different RenderViewHost is
- // needed.
- void OnCrossSiteResponse(
- const GlobalRequestID& global_request_id,
- bool is_transfer,
- const std::vector<GURL>& transfer_url_chain,
- const Referrer& referrer,
- PageTransition page_transition,
- int64 frame_id,
- bool should_replace_current_entry);
+ bool IsSwappedOut() const { return rvh_state_ == STATE_SWAPPED_OUT; }
+
+ // The current state of this RVH.
+ RenderViewHostImplState rvh_state() const { return rvh_state_; }
// Tells the renderer that this RenderView will soon be swapped out, and thus
// not to create any new modal dialogs until it happens. This must be done
@@ -313,23 +300,19 @@ class CONTENT_EXPORT RenderViewHostImpl
// longer on the stack when we attempt to swap it out.
void SuppressDialogsUntilSwapOut();
- // Tells the renderer that this RenderView is being swapped out for one in a
- // different renderer process. It should run its unload handler and move to
- // a blank document. The renderer should preserve the Frame object until it
- // exits, in case we come back. The renderer can exit if it has no other
- // active RenderViews, but not until WasSwappedOut is called (when it is no
- // longer visible).
- void SwapOut();
-
// Called when either the SwapOut request has been acknowledged or has timed
// out.
void OnSwappedOut(bool timed_out);
- // Called to notify the renderer that it has been visibly swapped out and
- // replaced by another RenderViewHost, after an earlier call to SwapOut.
- // It is now safe for the process to exit if there are no other active
- // RenderViews.
- void WasSwappedOut();
+ // Called when the RenderFrameHostManager has swapped in a new
+ // RenderFrameHost. Should |this| RVH switch to the pending shutdown state,
+ // |pending_delete_on_swap_out| will be executed upon reception of the
+ // SwapOutACK, or when the unload timer times out.
+ void WasSwappedOut(const base::Closure& pending_delete_on_swap_out);
+
+ // Set |this| as pending shutdown. |on_swap_out| will be called
+ // when the SwapOutACK is received, or when the unload timer times out.
+ void SetPendingShutdown(const base::Closure& on_swap_out);
// Close the page ignoring whether it has unload events registers.
// This is called after the beforeunload and unload events have fired
@@ -347,12 +330,6 @@ class CONTENT_EXPORT RenderViewHostImpl
// and cleared when we hear the response or commit.
void SetHasPendingCrossSiteRequest(bool has_pending_request);
- // Notifies the RenderView that the JavaScript message that was shown was
- // closed by the user.
- void JavaScriptDialogClosed(IPC::Message* reply_msg,
- bool success,
- const base::string16& user_input);
-
// Tells the renderer view to focus the first (last if reverse is true) node.
void SetInitialFocus(bool reverse);
@@ -414,28 +391,19 @@ class CONTENT_EXPORT RenderViewHostImpl
void DidCancelPopupMenu();
#endif
-#if defined(OS_ANDROID)
- BrowserMediaPlayerManager* media_player_manager() {
- return media_player_manager_.get();
+#if defined(ENABLE_BROWSER_CDMS)
+ MediaWebContentsObserver* media_web_contents_observer() {
+ return media_web_contents_observer_.get();
}
+#endif
+#if defined(OS_ANDROID)
void DidSelectPopupMenuItems(const std::vector<int>& selected_indices);
void DidCancelPopupMenu();
#endif
- // User rotated the screen. Calls the "onorientationchange" Javascript hook.
- void SendOrientationChangeEvent(int orientation);
-
- // Sets a bit indicating whether the RenderView is responsible for displaying
- // a subframe in a different process from its parent page.
- void set_is_subframe(bool is_subframe) {
- is_subframe_ = is_subframe;
- }
-
- // TODO(creis): Remove this when we replace frame IDs with RenderFrameHost
- // routing IDs.
- int64 main_frame_id() const {
- return main_frame_id_;
+ int main_frame_routing_id() const {
+ return main_frame_routing_id_;
}
// Set the opener to null in the renderer process.
@@ -446,14 +414,15 @@ class CONTENT_EXPORT RenderViewHostImpl
// renderer process, and the accessibility tree it sent can be
// retrieved using accessibility_tree_for_testing().
void SetAccessibilityCallbackForTesting(
- const base::Callback<void(blink::WebAXEvent)>& callback);
+ const base::Callback<void(ui::AXEvent, int)>& callback);
// Only valid if SetAccessibilityCallbackForTesting was called and
// the callback was run at least once. Returns a snapshot of the
// accessibility tree received from the renderer as of the last time
- // a LoadComplete or LayoutComplete accessibility notification was received.
- const AccessibilityNodeDataTreeNode& accessibility_tree_for_testing() {
- return accessibility_tree_;
+ // an accessibility notification was received.
+ const ui::AXTree& ax_tree_for_testing() {
+ CHECK(ax_tree_.get());
+ return *ax_tree_.get();
}
// Set accessibility callbacks.
@@ -468,22 +437,12 @@ class CONTENT_EXPORT RenderViewHostImpl
return is_waiting_for_beforeunload_ack_;
}
- bool is_waiting_for_unload_ack() {
- return is_waiting_for_unload_ack_;
- }
+ // Whether the RVH is waiting for the unload ack from the renderer.
+ bool IsWaitingForUnloadACK() const;
- // Returns whether the given URL is allowed to commit in the current process.
- // This is a more conservative check than FilterURL, since it will be used to
- // kill processes that commit unauthorized URLs.
- bool CanCommitURL(const GURL& url);
-
- // Checks that the given renderer can request |url|, if not it sets it to
- // about:blank.
- // empty_allowed must be set to false for navigations for security reasons.
- static void FilterURL(ChildProcessSecurityPolicyImpl* policy,
- const RenderProcessHost* process,
- bool empty_allowed,
- GURL* url);
+ void OnTextSurroundingSelectionResponse(const base::string16& content,
+ size_t start_offset,
+ size_t end_offset);
// Update the FrameTree to use this RenderViewHost's main frame
// RenderFrameHost. Called when the RenderViewHost is committed.
@@ -492,15 +451,17 @@ class CONTENT_EXPORT RenderViewHostImpl
// RenderFrameHost.
void AttachToFrameTree();
- // The following IPC handlers are public so RenderFrameHost can call them,
- // while we transition the code to not use RenderViewHost.
- //
- // TODO(nasko): Remove those methods once we are done moving navigation
- // into RenderFrameHost.
- void OnDidStartProvisionalLoadForFrame(int64 frame_id,
- int64 parent_frame_id,
- bool main_frame,
- const GURL& url);
+ // Increases the refcounting on this RVH. This is done by the FrameTree on
+ // creation of a RenderFrameHost.
+ void increment_ref_count() { ++frames_ref_count_; }
+
+ // Decreases the refcounting on this RVH. This is done by the FrameTree on
+ // destruction of a RenderFrameHost.
+ void decrement_ref_count() { --frames_ref_count_; }
+
+ // Returns the refcount on this RVH, that is the number of RenderFrameHosts
+ // currently using it.
+ int ref_count() { return frames_ref_count_; }
// NOTE: Do not add functions that just send an IPC message that are called in
// one or two places. Have the caller send the IPC message directly (unless
@@ -529,53 +490,17 @@ class CONTENT_EXPORT RenderViewHostImpl
void OnRunModal(int opener_id, IPC::Message* reply_msg);
void OnRenderViewReady();
void OnRenderProcessGone(int status, int error_code);
- void OnDidRedirectProvisionalLoad(int32 page_id,
- const GURL& source_url,
- const GURL& target_url);
- void OnDidFailProvisionalLoadWithError(
- const ViewHostMsg_DidFailProvisionalLoadWithError_Params& params);
- void OnNavigate(const IPC::Message& msg);
void OnUpdateState(int32 page_id, const PageState& state);
- void OnUpdateTitle(int32 page_id,
- const base::string16& title,
- blink::WebTextDirection title_direction);
- void OnUpdateEncoding(const std::string& encoding);
void OnUpdateTargetURL(int32 page_id, const GURL& url);
void OnClose();
void OnRequestMove(const gfx::Rect& pos);
- void OnDidStartLoading();
- void OnDidStopLoading();
- void OnDidChangeLoadProgress(double load_progress);
- void OnDidDisownOpener();
- void OnDocumentAvailableInMainFrame();
- void OnDocumentOnLoadCompletedInMainFrame(int32 page_id);
- void OnContextMenu(const ContextMenuParams& params);
+ void OnDocumentAvailableInMainFrame(bool uses_temporary_zoom_level);
void OnToggleFullscreen(bool enter_fullscreen);
- void OnOpenURL(const ViewHostMsg_OpenURL_Params& params);
void OnDidContentsPreferredSizeChange(const gfx::Size& new_size);
void OnDidChangeScrollOffset();
- void OnDidChangeScrollbarsForMainFrame(bool has_horizontal_scrollbar,
- bool has_vertical_scrollbar);
- void OnDidChangeScrollOffsetPinningForMainFrame(bool is_pinned_to_left,
- bool is_pinned_to_right);
- void OnDidChangeNumWheelEvents(int count);
- void OnSelectionChanged(const base::string16& text,
- size_t offset,
- const gfx::Range& range);
- void OnSelectionBoundsChanged(
- const ViewHostMsg_SelectionBounds_Params& params);
void OnPasteFromSelectionClipboard();
void OnRouteCloseEvent();
void OnRouteMessageEvent(const ViewMsg_PostMessage_Params& params);
- void OnRunJavaScriptMessage(const base::string16& message,
- const base::string16& default_prompt,
- const GURL& frame_url,
- JavaScriptMessageType type,
- IPC::Message* reply_msg);
- void OnRunBeforeUnloadConfirm(const GURL& frame_url,
- const base::string16& message,
- bool is_reload,
- IPC::Message* reply_msg);
void OnStartDragging(const DropData& drop_data,
blink::WebDragOperationsMask operations_allowed,
const SkBitmap& bitmap,
@@ -585,63 +510,44 @@ class CONTENT_EXPORT RenderViewHostImpl
void OnTargetDropACK();
void OnTakeFocus(bool reverse);
void OnFocusedNodeChanged(bool is_editable_node);
- void OnAddMessageToConsole(int32 level,
- const base::string16& message,
- int32 line_no,
- const base::string16& source_id);
void OnUpdateInspectorSetting(const std::string& key,
const std::string& value);
- void OnShouldCloseACK(
- bool proceed,
- const base::TimeTicks& renderer_before_unload_start_time,
- const base::TimeTicks& renderer_before_unload_end_time);
void OnClosePageACK();
- void OnSwapOutACK();
void OnAccessibilityEvents(
const std::vector<AccessibilityHostMsg_EventParams>& params);
- void OnScriptEvalResponse(int id, const base::ListValue& result);
- void OnDidZoomURL(double zoom_level, bool remember, const GURL& url);
- void OnRequestDesktopNotificationPermission(const GURL& origin,
- int callback_id);
- void OnShowDesktopNotification(
- const ShowDesktopNotificationHostMsgParams& params);
- void OnCancelDesktopNotification(int notification_id);
+ void OnAccessibilityLocationChanges(
+ const std::vector<AccessibilityHostMsg_LocationChangeParams>& params);
+ void OnDidZoomURL(double zoom_level, const GURL& url);
void OnRunFileChooser(const FileChooserParams& params);
- void OnDidAccessInitialDocument();
- void OnDomOperationResponse(const std::string& json_string,
- int automation_id);
void OnFocusedNodeTouched(bool editable);
#if defined(OS_MACOSX) || defined(OS_ANDROID)
void OnShowPopup(const ViewHostMsg_ShowPopup_Params& params);
+ void OnHidePopup();
#endif
- // TODO(nasko): Remove this accessor once RenderFrameHost moves into the frame
- // tree.
- RenderFrameHostImpl* main_render_frame_host() const {
- return main_render_frame_host_.get();
- }
-
private:
+ // TODO(nasko): Temporarily friend RenderFrameHostImpl, so we don't duplicate
+ // utility functions and state needed in both classes, while we move frame
+ // specific code away from this class.
+ friend class RenderFrameHostImpl;
friend class TestRenderViewHost;
FRIEND_TEST_ALL_PREFIXES(RenderViewHostTest, BasicRenderFrameHost);
FRIEND_TEST_ALL_PREFIXES(RenderViewHostTest, RoutingIdSane);
- // Sets whether this RenderViewHost is swapped out in favor of another,
- // and clears any waiting state that is no longer relevant.
- void SetSwappedOut(bool is_swapped_out);
+ // TODO(creis): Move to a private namespace on RenderFrameHostImpl.
+ // Delay to wait on closing the WebContents for a beforeunload/unload handler
+ // to fire.
+ static const int kUnloadTimeoutMS;
+
+ // Updates the state of this RenderViewHost and clears any waiting state
+ // that is no longer relevant.
+ void SetState(RenderViewHostImplState rvh_state);
bool CanAccessFilesOfPageState(const PageState& state) const;
- // All RenderViewHosts must have a RenderFrameHost for its main frame.
- // Currently the RenderFrameHost is created in lock step on construction
- // and a pointer to the main frame is given to the FrameTreeNode
- // when the RenderViewHost commits (see AttachToFrameTree()).
- //
- // TODO(ajwong): Make this reference non-owning. The root FrameTreeNode of
- // the FrameTree should be responsible for owning the main frame's
- // RenderFrameHost.
- scoped_ptr<RenderFrameHostImpl> main_render_frame_host_;
+ // The number of RenderFrameHosts which have a reference to this RVH.
+ int frames_ref_count_;
// Our delegate, which wants to know about changes in the RenderView.
RenderViewHostDelegate* delegate_;
@@ -663,32 +569,22 @@ class CONTENT_EXPORT RenderViewHostImpl
// them. This will be true when a RenderViewHost is created for a cross-site
// request, until we hear back from the onbeforeunload handler of the old
// RenderViewHost.
+ // TODO(nasko): Move to RenderFrameHost, as this is per-frame state.
bool navigations_suspended_;
// We only buffer the params for a suspended navigation while we have a
// pending RVH for a WebContentsImpl. There will only ever be one suspended
// navigation, because WebContentsImpl will destroy the pending RVH and create
// a new one if a second navigation occurs.
- scoped_ptr<ViewMsg_Navigate_Params> suspended_nav_params_;
+ // TODO(nasko): Move to RenderFrameHost, as this is per-frame state.
+ scoped_ptr<FrameMsg_Navigate_Params> suspended_nav_params_;
- // Whether the initial empty page of this view has been accessed by another
- // page, making it unsafe to show the pending URL. Usually false unless
- // another window tries to modify the blank page. Always false after the
- // first commit.
- bool has_accessed_initial_document_;
+ // The current state of this RVH.
+ // TODO(nasko): Move to RenderFrameHost, as this is per-frame state.
+ RenderViewHostImplState rvh_state_;
- // Whether this RenderViewHost is currently swapped out, such that the view is
- // being rendered by another process.
- bool is_swapped_out_;
-
- // Whether this RenderView is responsible for displaying a subframe in a
- // different process from its parent page.
- bool is_subframe_;
-
- // The frame id of the main (top level) frame. This value is set on the
- // initial navigation of a RenderView and reset when the RenderView's
- // process is terminated (in RenderProcessGone).
- int64 main_frame_id_;
+ // Routing ID for the main frame's RenderFrameHost.
+ int main_frame_routing_id_;
// If we were asked to RunModal, then this will hold the reply_msg that we
// must return to the renderer to unblock it.
@@ -698,36 +594,26 @@ class CONTENT_EXPORT RenderViewHostImpl
// Set to true when there is a pending ViewMsg_ShouldClose message. This
// ensures we don't spam the renderer with multiple beforeunload requests.
- // When either this value or is_waiting_for_unload_ack_ is true, the value of
+ // When either this value or IsWaitingForUnloadACK is true, the value of
// unload_ack_is_for_cross_site_transition_ indicates whether this is for a
// cross-site transition or a tab close attempt.
+ // TODO(clamy): Remove this boolean and add one more state to the state
+ // machine.
+ // TODO(nasko): Move to RenderFrameHost, as this is per-frame state.
bool is_waiting_for_beforeunload_ack_;
- // Set to true when there is a pending ViewMsg_Close message. Also see
- // is_waiting_for_beforeunload_ack_, unload_ack_is_for_cross_site_transition_.
- bool is_waiting_for_unload_ack_;
-
- // Set to true when waiting for ViewHostMsg_SwapOut_ACK has timed out.
- bool has_timed_out_on_unload_;
-
// Valid only when is_waiting_for_beforeunload_ack_ or
- // is_waiting_for_unload_ack_ is true. This tells us if the unload request
+ // IsWaitingForUnloadACK is true. This tells us if the unload request
// is for closing the entire tab ( = false), or only this RenderViewHost in
// the case of a cross-site transition ( = true).
+ // TODO(nasko): Move to RenderFrameHost, as this is per-frame state.
bool unload_ack_is_for_cross_site_transition_;
- bool are_javascript_messages_suppressed_;
-
- // The mapping of pending javascript calls created by
- // ExecuteJavascriptInWebFrameCallbackResult and their corresponding
- // callbacks.
- std::map<int, JavascriptResultCallback> javascript_callbacks_;
-
// Accessibility callback for testing.
- base::Callback<void(blink::WebAXEvent)> accessibility_testing_callback_;
+ base::Callback<void(ui::AXEvent, int)> accessibility_testing_callback_;
// The most recently received accessibility tree - for testing only.
- AccessibilityNodeDataTreeNode accessibility_tree_;
+ scoped_ptr<ui::AXTree> ax_tree_;
// True if the render view can be shut down suddenly.
bool sudden_termination_allowed_;
@@ -735,17 +621,30 @@ class CONTENT_EXPORT RenderViewHostImpl
// The termination status of the last render view that terminated.
base::TerminationStatus render_view_termination_status_;
- // When the last ShouldClose message was sent.
- base::TimeTicks send_should_close_start_time_;
-
// Set to true if we requested the on screen keyboard to be displayed.
bool virtual_keyboard_requested_;
-#if defined(OS_ANDROID)
- // Manages all the android mediaplayer objects and handling IPCs for video.
- scoped_ptr<BrowserMediaPlayerManager> media_player_manager_;
+#if defined(ENABLE_BROWSER_CDMS)
+ // Manages all the media player and CDM managers and forwards IPCs to them.
+ scoped_ptr<MediaWebContentsObserver> media_web_contents_observer_;
#endif
+ // Used to swap out or shutdown this RVH when the unload event is taking too
+ // long to execute, depending on the number of active views in the
+ // SiteInstance.
+ // TODO(nasko): Move to RenderFrameHost, as this is per-frame state.
+ scoped_ptr<TimeoutMonitor> unload_event_monitor_timeout_;
+
+ // Called after receiving the SwapOutACK when the RVH is in state pending
+ // shutdown. Also called if the unload timer times out.
+ // TODO(nasko): Move to RenderFrameHost, as this is per-frame state.
+ base::Closure pending_shutdown_on_swap_out_;
+
+ base::WeakPtrFactory<RenderViewHostImpl> weak_factory_;
+
+ // True if the current focused element is editable.
+ bool is_focused_element_editable_;
+
DISALLOW_COPY_AND_ASSIGN(RenderViewHostImpl);
};
diff --git a/chromium/content/browser/renderer_host/render_view_host_unittest.cc b/chromium/content/browser/renderer_host/render_view_host_unittest.cc
index e81e67fc34a..6889cd6f0bd 100644
--- a/chromium/content/browser/renderer_host/render_view_host_unittest.cc
+++ b/chromium/content/browser/renderer_host/render_view_host_unittest.cc
@@ -5,9 +5,10 @@
#include "base/path_service.h"
#include "base/strings/utf_string_conversions.h"
#include "content/browser/child_process_security_policy_impl.h"
+#include "content/browser/frame_host/render_frame_host_impl.h"
+#include "content/browser/renderer_host/render_view_host_delegate_view.h"
#include "content/common/input_messages.h"
#include "content/common/view_messages.h"
-#include "content/port/browser/render_view_host_delegate_view.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/common/bindings_policy.h"
#include "content/public/common/drop_data.h"
@@ -17,7 +18,7 @@
#include "content/test/test_content_browser_client.h"
#include "content/test/test_render_view_host.h"
#include "content/test/test_web_contents.h"
-#include "net/base/net_util.h"
+#include "net/base/filename_util.h"
#include "third_party/WebKit/public/web/WebDragOperation.h"
namespace content {
@@ -28,7 +29,7 @@ class RenderViewHostTestBrowserClient : public TestContentBrowserClient {
virtual ~RenderViewHostTestBrowserClient() {}
virtual bool IsHandledURL(const GURL& url) OVERRIDE {
- return url.scheme() == chrome::kFileScheme;
+ return url.scheme() == url::kFileScheme;
}
private:
@@ -62,7 +63,8 @@ class RenderViewHostTest : public RenderViewHostImplTestHarness {
TEST_F(RenderViewHostTest, FilterAbout) {
test_rvh()->SendNavigate(1, GURL("about:cache"));
ASSERT_TRUE(controller().GetVisibleEntry());
- EXPECT_EQ(GURL(kAboutBlankURL), controller().GetVisibleEntry()->GetURL());
+ EXPECT_EQ(GURL(url::kAboutBlankURL),
+ controller().GetVisibleEntry()->GetURL());
}
// Create a full screen popup RenderWidgetHost and View.
@@ -71,10 +73,9 @@ TEST_F(RenderViewHostTest, CreateFullscreenWidget) {
test_rvh()->CreateNewFullscreenWidget(routing_id);
}
-// Makes sure that RenderViewHost::is_waiting_for_unload_ack_ is false when
-// reloading a page. If is_waiting_for_unload_ack_ is not false when reloading
-// the contents may get closed out even though the user pressed the reload
-// button.
+// Makes sure that the RenderViewHost is not waiting for an unload ack when
+// reloading a page. If this is not the case, when reloading, the contents may
+// get closed out even though the user pressed the reload button.
TEST_F(RenderViewHostTest, ResetUnloadOnReload) {
const GURL url1("http://foo1");
const GURL url2("http://foo2");
@@ -84,12 +85,11 @@ TEST_F(RenderViewHostTest, ResetUnloadOnReload) {
// . go to a page.
// . go to a new page, preferably one that takes a while to resolve, such
// as one on a site that doesn't exist.
- // . After this step is_waiting_for_unload_ack_ has been set to true on
- // the first RVH.
+ // . After this step IsWaitingForUnloadACK returns true on the first RVH.
// . click stop before the page has been commited.
// . click reload.
- // . is_waiting_for_unload_ack_ is still true, and the if the hang monitor
- // fires the contents gets closed.
+ // . IsWaitingForUnloadACK still returns true, and if the hang monitor fires
+ // the contents gets closed.
NavigateAndCommit(url1);
controller().LoadURL(
@@ -97,10 +97,10 @@ TEST_F(RenderViewHostTest, ResetUnloadOnReload) {
// Simulate the ClosePage call which is normally sent by the net::URLRequest.
rvh()->ClosePage();
// Needed so that navigations are not suspended on the RVH.
- test_rvh()->SendShouldCloseACK(true);
+ test_rvh()->SendBeforeUnloadACK(true);
contents()->Stop();
controller().Reload(false);
- EXPECT_FALSE(test_rvh()->is_waiting_for_unload_ack());
+ EXPECT_FALSE(test_rvh()->IsWaitingForUnloadACK());
}
// Ensure we do not grant bindings to a process shared with unprivileged views.
@@ -117,13 +117,6 @@ class MockDraggingRenderViewHostDelegateView
: public RenderViewHostDelegateView {
public:
virtual ~MockDraggingRenderViewHostDelegateView() {}
- virtual void ShowPopupMenu(const gfx::Rect& bounds,
- int item_height,
- double item_font_size,
- int selected_item,
- const std::vector<MenuItem>& items,
- bool right_aligned,
- bool allow_multiple_selection) OVERRIDE {}
virtual void StartDragging(const DropData& drop_data,
blink::WebDragOperationsMask allowed_ops,
const gfx::ImageSkia& image,
@@ -160,8 +153,8 @@ TEST_F(RenderViewHostTest, StartDragging) {
drop_data.url = file_url;
drop_data.html_base_url = file_url;
test_rvh()->TestOnStartDragging(drop_data);
- EXPECT_EQ(GURL(kAboutBlankURL), delegate_view.drag_url());
- EXPECT_EQ(GURL(kAboutBlankURL), delegate_view.html_base_url());
+ EXPECT_EQ(GURL(url::kAboutBlankURL), delegate_view.drag_url());
+ EXPECT_EQ(GURL(url::kAboutBlankURL), delegate_view.html_base_url());
GURL http_url = GURL("http://www.domain.com/index.html");
drop_data.url = http_url;
@@ -198,8 +191,8 @@ TEST_F(RenderViewHostTest, DragEnteredFileURLsStillBlocked) {
GURL dragged_file_url = net::FilePathToFileURL(dragged_file_path);
GURL sensitive_file_url = net::FilePathToFileURL(sensitive_file_path);
dropped_data.url = highlighted_file_url;
- dropped_data.filenames.push_back(DropData::FileInfo(
- UTF8ToUTF16(dragged_file_path.AsUTF8Unsafe()), base::string16()));
+ dropped_data.filenames.push_back(
+ ui::FileInfo(dragged_file_path, base::FilePath()));
rvh()->DragTargetDragEnter(dropped_data, client_point, screen_point,
blink::WebDragOperationNone, 0);
@@ -216,48 +209,6 @@ TEST_F(RenderViewHostTest, DragEnteredFileURLsStillBlocked) {
EXPECT_FALSE(policy->CanReadFile(id, sensitive_file_path));
}
-// The test that follow trigger DCHECKS in debug build.
-#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
-
-// Test that when we fail to de-serialize a message, RenderViewHost calls the
-// ReceivedBadMessage() handler.
-TEST_F(RenderViewHostTest, BadMessageHandlerRenderViewHost) {
- EXPECT_EQ(0, process()->bad_msg_count());
- // craft an incorrect ViewHostMsg_UpdateTargetURL message. The real one has
- // two payload items but the one we construct has none.
- IPC::Message message(0, ViewHostMsg_UpdateTargetURL::ID,
- IPC::Message::PRIORITY_NORMAL);
- test_rvh()->OnMessageReceived(message);
- EXPECT_EQ(1, process()->bad_msg_count());
-}
-
-// Test that when we fail to de-serialize a message, RenderWidgetHost calls the
-// ReceivedBadMessage() handler.
-TEST_F(RenderViewHostTest, BadMessageHandlerRenderWidgetHost) {
- EXPECT_EQ(0, process()->bad_msg_count());
- // craft an incorrect ViewHostMsg_UpdateRect message. The real one has
- // one payload item but the one we construct has none.
- IPC::Message message(0, ViewHostMsg_UpdateRect::ID,
- IPC::Message::PRIORITY_NORMAL);
- test_rvh()->OnMessageReceived(message);
- EXPECT_EQ(1, process()->bad_msg_count());
-}
-
-// Test that OnInputEventAck() detects bad messages.
-TEST_F(RenderViewHostTest, BadMessageHandlerInputEventAck) {
- EXPECT_EQ(0, process()->bad_msg_count());
- // InputHostMsg_HandleInputEvent_ACK is defined taking 0 params but
- // the code actually expects it to have at least one int para, this this
- // bogus message will not fail at de-serialization but should fail in
- // OnInputEventAck() processing.
- IPC::Message message(0, InputHostMsg_HandleInputEvent_ACK::ID,
- IPC::Message::PRIORITY_NORMAL);
- test_rvh()->OnMessageReceived(message);
- EXPECT_EQ(1, process()->bad_msg_count());
-}
-
-#endif
-
TEST_F(RenderViewHostTest, MessageWithBadHistoryItemFiles) {
base::FilePath file_path;
EXPECT_TRUE(PathService::Get(base::DIR_TEMP, &file_path));
@@ -288,10 +239,10 @@ TEST_F(RenderViewHostTest, NavigationWithBadHistoryItemFiles) {
}
TEST_F(RenderViewHostTest, RoutingIdSane) {
- EXPECT_EQ(test_rvh()->GetProcess(),
- test_rvh()->main_render_frame_host()->GetProcess());
- EXPECT_NE(test_rvh()->GetRoutingID(),
- test_rvh()->main_render_frame_host()->routing_id());
+ RenderFrameHostImpl* root_rfh =
+ contents()->GetFrameTree()->root()->current_frame_host();
+ EXPECT_EQ(test_rvh()->GetProcess(), root_rfh->GetProcess());
+ EXPECT_NE(test_rvh()->GetRoutingID(), root_rfh->routing_id());
}
} // namespace content
diff --git a/chromium/content/browser/renderer_host/render_widget_helper.cc b/chromium/content/browser/renderer_host/render_widget_helper.cc
index 10e4e0ffa1f..169347f3288 100644
--- a/chromium/content/browser/renderer_host/render_widget_helper.cc
+++ b/chromium/content/browser/renderer_host/render_widget_helper.cc
@@ -26,7 +26,7 @@ base::LazyInstance<WidgetHelperMap> g_widget_helpers =
void AddWidgetHelper(int render_process_id,
const scoped_refptr<RenderWidgetHelper>& widget_helper) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
// We don't care if RenderWidgetHelpers overwrite an existing process_id. Just
// want this to be up to date.
g_widget_helpers.Get()[render_process_id] = widget_helper.get();
@@ -85,7 +85,7 @@ RenderWidgetHelper::RenderWidgetHelper()
}
RenderWidgetHelper::~RenderWidgetHelper() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
// Delete this RWH from the map if it is found.
WidgetHelperMap& widget_map = g_widget_helpers.Get();
@@ -97,7 +97,7 @@ RenderWidgetHelper::~RenderWidgetHelper() {
// object, so we should not be destroyed unless pending_paints_ is empty!
DCHECK(pending_paints_.empty());
-#if defined(OS_POSIX) && !defined(TOOLKIT_GTK) && !defined(OS_ANDROID)
+#if defined(OS_POSIX) && !defined(OS_ANDROID)
ClearAllocatedDIBs();
#endif
}
@@ -121,7 +121,7 @@ int RenderWidgetHelper::GetNextRoutingID() {
// static
RenderWidgetHelper* RenderWidgetHelper::FromProcessHostID(
int render_process_host_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
WidgetHelperMap::const_iterator ci = g_widget_helpers.Get().find(
render_process_host_id);
return (ci == g_widget_helpers.Get().end())? NULL : ci->second;
@@ -342,19 +342,7 @@ void RenderWidgetHelper::OnCreateFullscreenWidgetOnUI(int opener_id,
host->CreateNewFullscreenWidget(route_id);
}
-#if defined(OS_POSIX) && !defined(TOOLKIT_GTK) && !defined(OS_ANDROID)
-TransportDIB* RenderWidgetHelper::MapTransportDIB(TransportDIB::Id dib_id) {
- base::AutoLock locked(allocated_dibs_lock_);
-
- const std::map<TransportDIB::Id, int>::iterator
- i = allocated_dibs_.find(dib_id);
- if (i == allocated_dibs_.end())
- return NULL;
-
- base::FileDescriptor fd(dup(i->second), true);
- return TransportDIB::Map(fd);
-}
-
+#if defined(OS_POSIX) && !defined(OS_ANDROID)
void RenderWidgetHelper::AllocTransportDIB(uint32 size,
bool cache_in_browser,
TransportDIB::Handle* result) {
diff --git a/chromium/content/browser/renderer_host/render_widget_helper.h b/chromium/content/browser/renderer_host/render_widget_helper.h
index a81b1ded47d..af5c6612b5e 100644
--- a/chromium/content/browser/renderer_host/render_widget_helper.h
+++ b/chromium/content/browser/renderer_host/render_widget_helper.h
@@ -30,10 +30,12 @@ namespace base {
class TimeDelta;
}
+struct GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params;
struct ViewHostMsg_CreateWindow_Params;
struct ViewMsg_SwapOut_Params;
namespace content {
+class GpuProcessHost;
class ResourceDispatcherHostImpl;
class SessionStorageNamespace;
@@ -134,11 +136,6 @@ class RenderWidgetHelper
// created by CreateNewWindow which initially blocked the requests.
void ResumeRequestsForView(int route_id);
-#if defined(OS_POSIX) && !defined(TOOLKIT_GTK) && !defined(OS_ANDROID)
- // Given the id of a transport DIB, return a mapping to it or NULL on error.
- TransportDIB* MapTransportDIB(TransportDIB::Id dib_id);
-#endif
-
// IO THREAD ONLY -----------------------------------------------------------
// Called on the IO thread when a BackingStore message is received.
@@ -172,6 +169,12 @@ class RenderWidgetHelper
void FreeTransportDIB(TransportDIB::Id dib_id);
#endif
+#if defined(OS_MACOSX)
+ static void OnNativeSurfaceBuffersSwappedOnIOThread(
+ GpuProcessHost* gpu_process_host,
+ const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params);
+#endif
+
private:
// A class used to proxy a paint message. PaintMsgProxy objects are created
// on the IO thread and destroyed on the UI thread.
diff --git a/chromium/content/browser/renderer_host/render_widget_helper_mac.mm b/chromium/content/browser/renderer_host/render_widget_helper_mac.mm
new file mode 100644
index 00000000000..824b2265d70
--- /dev/null
+++ b/chromium/content/browser/renderer_host/render_widget_helper_mac.mm
@@ -0,0 +1,64 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/render_widget_helper.h"
+
+#import <Cocoa/Cocoa.h>
+#include <IOSurface/IOSurfaceAPI.h>
+
+#include "base/bind.h"
+#include "content/browser/compositor/browser_compositor_view_mac.h"
+#include "content/browser/gpu/gpu_process_host.h"
+#include "content/browser/gpu/gpu_surface_tracker.h"
+#include "content/common/gpu/gpu_messages.h"
+
+namespace {
+
+void OnNativeSurfaceBuffersSwappedOnUIThread(
+ base::ScopedCFTypeRef<IOSurfaceRef> io_surface,
+ const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ gfx::AcceleratedWidget native_widget =
+ content::GpuSurfaceTracker::Get()->AcquireNativeWidget(params.surface_id);
+ IOSurfaceID io_surface_handle = static_cast<IOSurfaceID>(
+ params.surface_handle);
+ [native_widget gotAcceleratedIOSurfaceFrame:io_surface_handle
+ withOutputSurfaceID:params.surface_id
+ withPixelSize:params.size
+ withScaleFactor:params.scale_factor];
+}
+
+} // namespace
+
+namespace content {
+
+void RenderWidgetHelper::OnNativeSurfaceBuffersSwappedOnIOThread(
+ GpuProcessHost* gpu_process_host,
+ const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ // Immediately acknowledge this frame on the IO thread instead of the UI
+ // thread. The UI thread will wait on the GPU process. If the UI thread
+ // were to be responsible for acking swaps, then there would be a cycle
+ // and a potential deadlock.
+ // TODO(ccameron): This immediate ack circumvents GPU back-pressure that
+ // is necessary to throttle renderers. Fix that.
+ AcceleratedSurfaceMsg_BufferPresented_Params ack_params;
+ ack_params.sync_point = 0;
+ ack_params.renderer_id = 0;
+ gpu_process_host->Send(new AcceleratedSurfaceMsg_BufferPresented(
+ params.route_id, ack_params));
+
+ // Open the IOSurface handle before returning, to ensure that it is not
+ // closed as soon as the frame is acknowledged.
+ base::ScopedCFTypeRef<IOSurfaceRef> io_surface(IOSurfaceLookup(
+ static_cast<uint32>(params.surface_handle)));
+
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&OnNativeSurfaceBuffersSwappedOnUIThread, io_surface, params));
+}
+
+} // namespace content
diff --git a/chromium/content/browser/renderer_host/render_widget_host_browsertest.cc b/chromium/content/browser/renderer_host/render_widget_host_browsertest.cc
index f9b3fd3519e..483778167b2 100644
--- a/chromium/content/browser/renderer_host/render_widget_host_browsertest.cc
+++ b/chromium/content/browser/renderer_host/render_widget_host_browsertest.cc
@@ -7,10 +7,10 @@
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_paths.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/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
-#include "net/base/net_util.h"
+#include "net/base/filename_util.h"
#include "third_party/skia/include/core/SkBitmap.h"
namespace content {
@@ -19,57 +19,12 @@ class RenderWidgetHostBrowserTest : public ContentBrowserTest {
public:
RenderWidgetHostBrowserTest() {}
- virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
+ virtual void SetUpOnMainThread() OVERRIDE {
ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &test_dir_));
}
- void GetSnapshotFromRendererCallback(const base::Closure& quit_closure,
- bool* snapshot_valid,
- bool success,
- const SkBitmap& bitmap) {
- quit_closure.Run();
- EXPECT_EQ(success, true);
-
- const int row_bytes = bitmap.rowBytesAsPixels();
- SkColor* pixels = reinterpret_cast<SkColor*>(bitmap.getPixels());
- for (int i = 0; i < bitmap.width(); ++i) {
- for (int j = 0; j < bitmap.height(); ++j) {
- if (pixels[j * row_bytes + i] != SK_ColorRED) {
- return;
- }
- }
- }
- *snapshot_valid = true;
- }
-
protected:
base::FilePath test_dir_;
};
-// Disabled on Windows and CrOS because it is flaky: crbug.com/272379.
-// Disabled on Ozone due to flake: crbug.com/315392.
-#if defined(OS_WIN) || defined(OS_CHROMEOS) || defined(USE_OZONE)
-#define MAYBE_GetSnapshotFromRendererTest DISABLED_GetSnapshotFromRendererTest
-#else
-#define MAYBE_GetSnapshotFromRendererTest GetSnapshotFromRendererTest
-#endif
-IN_PROC_BROWSER_TEST_F(RenderWidgetHostBrowserTest,
- MAYBE_GetSnapshotFromRendererTest) {
- base::RunLoop run_loop;
-
- NavigateToURL(shell(), GURL(net::FilePathToFileURL(
- test_dir_.AppendASCII("rwh_simple.html"))));
-
- bool snapshot_valid = false;
- RenderViewHost* const rwh = shell()->web_contents()->GetRenderViewHost();
- rwh->GetSnapshotFromRenderer(gfx::Rect(), base::Bind(
- &RenderWidgetHostBrowserTest::GetSnapshotFromRendererCallback,
- base::Unretained(this),
- run_loop.QuitClosure(),
- &snapshot_valid));
- run_loop.Run();
-
- EXPECT_EQ(snapshot_valid, true);
-}
-
} // namespace content
diff --git a/chromium/content/browser/renderer_host/render_widget_host_delegate.cc b/chromium/content/browser/renderer_host/render_widget_host_delegate.cc
index efc1915c87e..23687d0f24d 100644
--- a/chromium/content/browser/renderer_host/render_widget_host_delegate.cc
+++ b/chromium/content/browser/renderer_host/render_widget_host_delegate.cc
@@ -12,12 +12,22 @@ bool RenderWidgetHostDelegate::PreHandleKeyboardEvent(
return false;
}
-bool RenderWidgetHostDelegate::PreHandleWheelEvent(
+bool RenderWidgetHostDelegate::HandleWheelEvent(
const blink::WebMouseWheelEvent& event) {
return false;
}
-#if defined(OS_WIN) && defined(USE_AURA)
+bool RenderWidgetHostDelegate::PreHandleGestureEvent(
+ const blink::WebGestureEvent& event) {
+ return false;
+}
+
+bool RenderWidgetHostDelegate::HandleGestureEvent(
+ const blink::WebGestureEvent& event) {
+ return false;
+}
+
+#if defined(OS_WIN)
gfx::NativeViewAccessible
RenderWidgetHostDelegate::GetParentNativeViewAccessible() {
return NULL;
diff --git a/chromium/content/browser/renderer_host/render_widget_host_delegate.h b/chromium/content/browser/renderer_host/render_widget_host_delegate.h
index 22f5a19a489..91ce94316f9 100644
--- a/chromium/content/browser/renderer_host/render_widget_host_delegate.h
+++ b/chromium/content/browser/renderer_host/render_widget_host_delegate.h
@@ -11,6 +11,7 @@
namespace blink {
class WebMouseWheelEvent;
+class WebGestureEvent;
}
namespace content {
@@ -41,15 +42,27 @@ class CONTENT_EXPORT RenderWidgetHostDelegate {
// event (used for keyboard shortcuts).
virtual void HandleKeyboardEvent(const NativeWebKeyboardEvent& event) {}
- // Callback to give the browser a chance to handle the specified mouse wheel
+ // Callback to inform the browser that the renderer did not process the
+ // specified mouse wheel event. Returns true if the browser has handled
+ // the event itself.
+ virtual bool HandleWheelEvent(const blink::WebMouseWheelEvent& event);
+
+ // Callback to give the browser a chance to handle the specified gesture
// event before sending it to the renderer.
// Returns true if the |event| was handled.
- virtual bool PreHandleWheelEvent(const blink::WebMouseWheelEvent& event);
+ virtual bool PreHandleGestureEvent(const blink::WebGestureEvent& event);
+
+ // Callback to inform the browser that the renderer did not process the
+ // specified gesture event. Returns true if the |event| was handled.
+ virtual bool HandleGestureEvent(const blink::WebGestureEvent& event);
// Notifies that screen rects were sent to renderer process.
virtual void DidSendScreenRects(RenderWidgetHostImpl* rwh) {}
-#if defined(OS_WIN) && defined(USE_AURA)
+ // Notifies that RenderWidgetHost will toggle touch emulation.
+ virtual void OnTouchEmulationEnabled(bool enabled) {}
+
+#if defined(OS_WIN)
// Returns the widget's parent's NativeViewAccessible.
virtual gfx::NativeViewAccessible GetParentNativeViewAccessible();
#endif
diff --git a/chromium/content/browser/renderer_host/render_widget_host_impl.cc b/chromium/content/browser/renderer_host/render_widget_host_impl.cc
index fa181508f38..eb03476699a 100644
--- a/chromium/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/chromium/content/browser/renderer_host/render_widget_host_impl.cc
@@ -20,32 +20,37 @@
#include "base/metrics/histogram.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/thread_task_runner_handle.h"
+#include "cc/base/switches.h"
#include "cc/output/compositor_frame.h"
#include "cc/output/compositor_frame_ack.h"
+#include "content/browser/accessibility/accessibility_mode_helper.h"
#include "content/browser/accessibility/browser_accessibility_state_impl.h"
+#include "content/browser/browser_plugin/browser_plugin_guest.h"
#include "content/browser/gpu/compositor_util.h"
#include "content/browser/gpu/gpu_process_host.h"
#include "content/browser/gpu/gpu_process_host_ui_shim.h"
#include "content/browser/gpu/gpu_surface_tracker.h"
-#include "content/browser/renderer_host/backing_store.h"
-#include "content/browser/renderer_host/backing_store_manager.h"
#include "content/browser/renderer_host/dip_util.h"
+#include "content/browser/renderer_host/input/input_router_config_helper.h"
#include "content/browser/renderer_host/input/input_router_impl.h"
#include "content/browser/renderer_host/input/synthetic_gesture.h"
#include "content/browser/renderer_host/input/synthetic_gesture_controller.h"
#include "content/browser/renderer_host/input/synthetic_gesture_target.h"
#include "content/browser/renderer_host/input/timeout_monitor.h"
-#include "content/browser/renderer_host/overscroll_controller.h"
+#include "content/browser/renderer_host/input/touch_emulator.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/renderer_host/render_widget_helper.h"
#include "content/browser/renderer_host/render_widget_host_delegate.h"
+#include "content/browser/renderer_host/render_widget_host_view_base.h"
#include "content/common/accessibility_messages.h"
#include "content/common/content_constants_internal.h"
+#include "content/common/cursors/webcursor.h"
#include "content/common/gpu/gpu_messages.h"
+#include "content/common/host_shared_bitmap_manager.h"
#include "content/common/input_messages.h"
#include "content/common/view_messages.h"
-#include "content/port/browser/render_widget_host_view_port.h"
#include "content/public/browser/native_web_keyboard_event.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
@@ -63,14 +68,9 @@
#include "ui/gfx/skbitmap_operations.h"
#include "ui/gfx/vector2d_conversions.h"
#include "ui/snapshot/snapshot.h"
-#include "webkit/common/cursors/webcursor.h"
#include "webkit/common/webpreferences.h"
-#if defined(TOOLKIT_GTK)
-#include "content/browser/renderer_host/backing_store_gtk.h"
-#elif defined(OS_MACOSX)
-#include "content/browser/renderer_host/backing_store_mac.h"
-#elif defined(OS_WIN)
+#if defined(OS_WIN)
#include "content/common/plugin_constants_win.h"
#endif
@@ -101,6 +101,22 @@ typedef base::hash_map<RenderWidgetHostID, RenderWidgetHostImpl*>
base::LazyInstance<RoutingIDWidgetMap> g_routing_id_widget_map =
LAZY_INSTANCE_INITIALIZER;
+int GetInputRouterViewFlagsFromCompositorFrameMetadata(
+ const cc::CompositorFrameMetadata metadata) {
+ int view_flags = InputRouter::VIEW_FLAGS_NONE;
+
+ if (metadata.min_page_scale_factor == metadata.max_page_scale_factor)
+ view_flags |= InputRouter::FIXED_PAGE_SCALE;
+
+ const float window_width_dip =
+ std::ceil(metadata.page_scale_factor * metadata.viewport_size.width());
+ const float content_width_css = metadata.root_layer_size.width();
+ if (content_width_css <= window_width_dip)
+ view_flags |= InputRouter::MOBILE_VIEWPORT;
+
+ return view_flags;
+}
+
// Implements the RenderWidgetHostIterator interface. It keeps a list of
// RenderWidgetHosts, and makes sure it returns a live RenderWidgetHost at each
// iteration (or NULL if there isn't any left).
@@ -138,17 +154,6 @@ class RenderWidgetHostIteratorImpl : public RenderWidgetHostIterator {
} // namespace
-
-// static
-void RenderWidgetHost::RemoveAllBackingStores() {
- BackingStoreManager::RemoveAllBackingStores();
-}
-
-// static
-size_t RenderWidgetHost::BackingStoreMemorySize() {
- return BackingStoreManager::MemorySize();
-}
-
///////////////////////////////////////////////////////////////////////////////
// RenderWidgetHostImpl
@@ -166,7 +171,6 @@ RenderWidgetHostImpl::RenderWidgetHostImpl(RenderWidgetHostDelegate* delegate,
is_loading_(false),
is_hidden_(hidden),
is_fullscreen_(false),
- is_accelerated_compositing_active_(false),
repaint_ack_pending_(false),
resize_ack_pending_(false),
screen_info_out_of_date_(false),
@@ -178,8 +182,6 @@ RenderWidgetHostImpl::RenderWidgetHostImpl(RenderWidgetHostDelegate* delegate,
is_unresponsive_(false),
in_flight_event_count_(0),
in_get_backing_store_(false),
- abort_get_backing_store_(false),
- view_being_painted_(false),
ignore_input_events_(false),
input_method_active_(false),
text_direction_updated_(false),
@@ -211,8 +213,6 @@ RenderWidgetHostImpl::RenderWidgetHostImpl(RenderWidgetHostDelegate* delegate,
DCHECK(surface_id_);
}
- is_threaded_compositing_enabled_ = IsThreadedCompositingEnabled();
-
std::pair<RoutingIDWidgetMap::iterator, bool> result =
g_routing_id_widget_map.Get().insert(std::make_pair(
RenderWidgetHostID(process->GetID(), routing_id_), this));
@@ -227,15 +227,15 @@ RenderWidgetHostImpl::RenderWidgetHostImpl(RenderWidgetHostDelegate* delegate,
accessibility_mode_ =
BrowserAccessibilityStateImpl::GetInstance()->accessibility_mode();
- input_router_.reset(new InputRouterImpl(process_, this, this, routing_id_));
+ input_router_.reset(new InputRouterImpl(
+ process_, this, this, routing_id_, GetInputRouterConfigForPlatform()));
-#if defined(USE_AURA)
- bool overscroll_enabled = CommandLine::ForCurrentProcess()->
- GetSwitchValueASCII(switches::kOverscrollHistoryNavigation) != "0";
- SetOverscrollControllerEnabled(overscroll_enabled);
-#endif
+ touch_emulator_.reset();
- if (GetProcess()->IsGuest() || !CommandLine::ForCurrentProcess()->HasSwitch(
+ RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
+ IsRenderView() ? RenderViewHost::From(this) : NULL);
+ if (BrowserPluginGuest::IsGuest(rvh) ||
+ !CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableHangMonitor)) {
hang_monitor_timeout_.reset(new TimeoutMonitor(
base::Bind(&RenderWidgetHostImpl::RendererIsUnresponsive,
@@ -246,9 +246,6 @@ RenderWidgetHostImpl::RenderWidgetHostImpl(RenderWidgetHostDelegate* delegate,
RenderWidgetHostImpl::~RenderWidgetHostImpl() {
SetView(NULL);
- // Clear our current or cached backing store if either remains.
- BackingStoreManager::RemoveBackingStore(this);
-
GpuSurfaceTracker::Get()->RemoveSurface(surface_id_);
surface_id_ = 0;
@@ -271,7 +268,7 @@ RenderWidgetHost* RenderWidgetHost::FromID(
RenderWidgetHostImpl* RenderWidgetHostImpl::FromID(
int32 process_id,
int32 routing_id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
RoutingIDWidgetMap* widgets = g_routing_id_widget_map.Pointer();
RoutingIDWidgetMap::iterator it = widgets->find(
RenderWidgetHostID(process_id, routing_id));
@@ -294,7 +291,8 @@ scoped_ptr<RenderWidgetHostIterator> RenderWidgetHost::GetRenderWidgetHosts() {
// Add only active RenderViewHosts.
RenderViewHost* rvh = RenderViewHost::From(widget);
- if (!static_cast<RenderViewHostImpl*>(rvh)->is_swapped_out())
+ if (RenderViewHostImpl::IsRVHStateActive(
+ static_cast<RenderViewHostImpl*>(rvh)->rvh_state()))
hosts->Add(widget);
}
@@ -320,13 +318,11 @@ RenderWidgetHostImpl* RenderWidgetHostImpl::From(RenderWidgetHost* rwh) {
return rwh->AsRenderWidgetHostImpl();
}
-void RenderWidgetHostImpl::SetView(RenderWidgetHostView* view) {
- view_ = RenderWidgetHostViewPort::FromRWHV(view);
+void RenderWidgetHostImpl::SetView(RenderWidgetHostViewBase* view) {
+ view_ = view;
- if (!view_) {
- GpuSurfaceTracker::Get()->SetSurfaceHandle(
- surface_id_, gfx::GLSurfaceHandle());
- }
+ GpuSurfaceTracker::Get()->SetSurfaceHandle(
+ surface_id_, GetCompositingSurface());
synthetic_gesture_controller_.reset();
}
@@ -359,12 +355,6 @@ gfx::GLSurfaceHandle RenderWidgetHostImpl::GetCompositingSurface() {
return gfx::GLSurfaceHandle();
}
-void RenderWidgetHostImpl::CompositingSurfaceUpdated() {
- GpuSurfaceTracker::Get()->SetSurfaceHandle(
- surface_id_, GetCompositingSurface());
- process_->SurfaceUpdated(surface_id_);
-}
-
void RenderWidgetHostImpl::ResetSizeAndRepaintPendingFlags() {
resize_ack_pending_ = false;
if (repaint_ack_pending_) {
@@ -397,13 +387,6 @@ void RenderWidgetHostImpl::SendScreenRects() {
waiting_for_screen_rects_ack_ = true;
}
-void RenderWidgetHostImpl::SetOverscrollControllerEnabled(bool enabled) {
- if (!enabled)
- overscroll_controller_.reset();
- else if (!overscroll_controller_)
- overscroll_controller_.reset(new OverscrollController());
-}
-
void RenderWidgetHostImpl::SuppressNextCharEvents() {
suppress_next_char_events_ = true;
}
@@ -456,8 +439,7 @@ bool RenderWidgetHostImpl::IsRenderView() const {
bool RenderWidgetHostImpl::OnMessageReceived(const IPC::Message &msg) {
bool handled = true;
- bool msg_is_ok = true;
- IPC_BEGIN_MESSAGE_MAP_EX(RenderWidgetHostImpl, msg, msg_is_ok)
+ IPC_BEGIN_MESSAGE_MAP(RenderWidgetHostImpl, msg)
IPC_MESSAGE_HANDLER(InputHostMsg_QueueSyntheticGesture,
OnQueueSyntheticGesture)
IPC_MESSAGE_HANDLER(ViewHostMsg_RenderViewReady, OnRenderViewReady)
@@ -467,42 +449,42 @@ bool RenderWidgetHostImpl::OnMessageReceived(const IPC::Message &msg) {
OnUpdateScreenRectsAck)
IPC_MESSAGE_HANDLER(ViewHostMsg_RequestMove, OnRequestMove)
IPC_MESSAGE_HANDLER(ViewHostMsg_SetTooltipText, OnSetTooltipText)
- IPC_MESSAGE_HANDLER(ViewHostMsg_PaintAtSize_ACK, OnPaintAtSizeAck)
-#if defined(OS_MACOSX)
- IPC_MESSAGE_HANDLER(ViewHostMsg_CompositorSurfaceBuffersSwapped,
- OnCompositorSurfaceBuffersSwapped)
-#endif
IPC_MESSAGE_HANDLER_GENERIC(ViewHostMsg_SwapCompositorFrame,
- msg_is_ok = OnSwapCompositorFrame(msg))
- IPC_MESSAGE_HANDLER(ViewHostMsg_DidOverscroll, OnOverscrolled)
+ OnSwapCompositorFrame(msg))
+ IPC_MESSAGE_HANDLER(ViewHostMsg_DidStopFlinging, OnFlingingStopped)
IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateRect, OnUpdateRect)
- IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateIsDelayed, OnUpdateIsDelayed)
IPC_MESSAGE_HANDLER(ViewHostMsg_Focus, OnFocus)
IPC_MESSAGE_HANDLER(ViewHostMsg_Blur, OnBlur)
IPC_MESSAGE_HANDLER(ViewHostMsg_SetCursor, OnSetCursor)
- IPC_MESSAGE_HANDLER(ViewHostMsg_TextInputTypeChanged,
- OnTextInputTypeChanged)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_SetTouchEventEmulationEnabled,
+ OnSetTouchEventEmulationEnabled)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_TextInputStateChanged,
+ OnTextInputStateChanged)
IPC_MESSAGE_HANDLER(ViewHostMsg_ImeCancelComposition,
OnImeCancelComposition)
- IPC_MESSAGE_HANDLER(ViewHostMsg_DidActivateAcceleratedCompositing,
- OnDidActivateAcceleratedCompositing)
IPC_MESSAGE_HANDLER(ViewHostMsg_LockMouse, OnLockMouse)
IPC_MESSAGE_HANDLER(ViewHostMsg_UnlockMouse, OnUnlockMouse)
IPC_MESSAGE_HANDLER(ViewHostMsg_ShowDisambiguationPopup,
OnShowDisambiguationPopup)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_SelectionChanged, OnSelectionChanged)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_SelectionBoundsChanged,
+ OnSelectionBoundsChanged)
#if defined(OS_WIN)
IPC_MESSAGE_HANDLER(ViewHostMsg_WindowlessPluginDummyWindowCreated,
OnWindowlessPluginDummyWindowCreated)
IPC_MESSAGE_HANDLER(ViewHostMsg_WindowlessPluginDummyWindowDestroyed,
OnWindowlessPluginDummyWindowDestroyed)
#endif
- IPC_MESSAGE_HANDLER(ViewHostMsg_Snapshot, OnSnapshot)
-#if defined(OS_MACOSX) || defined(OS_WIN) || defined(USE_AURA)
+#if defined(OS_MACOSX)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_CompositorSurfaceBuffersSwapped,
+ OnCompositorSurfaceBuffersSwapped)
+#endif
+#if defined(OS_MACOSX) || defined(USE_AURA)
IPC_MESSAGE_HANDLER(ViewHostMsg_ImeCompositionRangeChanged,
OnImeCompositionRangeChanged)
#endif
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
if (!handled && input_router_ && input_router_->OnMessageReceived(msg))
return true;
@@ -510,11 +492,6 @@ bool RenderWidgetHostImpl::OnMessageReceived(const IPC::Message &msg) {
if (!handled && view_ && view_->OnMessageReceived(msg))
return true;
- if (!msg_is_ok) {
- // The message de-serialization failed. Kill the renderer process.
- RecordAction(UserMetricsAction("BadMessageTerminate_RWH"));
- GetProcess()->ReceivedBadMessage();
- }
return handled;
}
@@ -555,19 +532,9 @@ void RenderWidgetHostImpl::WasShown() {
SendScreenRects();
- BackingStore* backing_store = BackingStoreManager::Lookup(this);
- // If we already have a backing store for this widget, then we don't need to
- // repaint on restore _unless_ we know that our backing store is invalid.
- // When accelerated compositing is on, we must always repaint, even when
- // the backing store exists.
- bool needs_repainting;
- if (needs_repainting_on_restore_ || !backing_store ||
- is_accelerated_compositing_active()) {
- needs_repainting = true;
- needs_repainting_on_restore_ = false;
- } else {
- needs_repainting = false;
- }
+ // Always repaint on restore.
+ bool needs_repainting = true;
+ needs_repainting_on_restore_ = false;
Send(new ViewMsg_WasShown(routing_id_, needs_repainting));
process_->WidgetRestored();
@@ -604,8 +571,7 @@ void RenderWidgetHostImpl::WasResized() {
return;
}
- gfx::Rect view_bounds = view_->GetViewBounds();
- gfx::Size new_size(view_bounds.size());
+ gfx::Size new_size(view_->GetRequestedRendererSize());
gfx::Size old_physical_backing_size = physical_backing_size_;
physical_backing_size_ = view_->GetPhysicalBackingSize();
@@ -613,13 +579,16 @@ void RenderWidgetHostImpl::WasResized() {
is_fullscreen_ = IsFullscreen();
float old_overdraw_bottom_height = overdraw_bottom_height_;
overdraw_bottom_height_ = view_->GetOverdrawBottomHeight();
+ gfx::Size old_visible_viewport_size = visible_viewport_size_;
+ visible_viewport_size_ = view_->GetVisibleViewportSize();
bool size_changed = new_size != last_requested_size_;
bool side_payload_changed =
screen_info_out_of_date_ ||
old_physical_backing_size != physical_backing_size_ ||
was_fullscreen != is_fullscreen_ ||
- old_overdraw_bottom_height != overdraw_bottom_height_;
+ old_overdraw_bottom_height != overdraw_bottom_height_ ||
+ old_visible_viewport_size != visible_viewport_size_;
if (!size_changed && !side_payload_changed)
return;
@@ -639,6 +608,7 @@ void RenderWidgetHostImpl::WasResized() {
params.new_size = new_size;
params.physical_backing_size = physical_backing_size_;
params.overdraw_bottom_height = overdraw_bottom_height_;
+ params.visible_viewport_size = visible_viewport_size_;
params.resizer_rect = GetRootWindowResizerRect();
params.is_fullscreen = is_fullscreen_;
if (!Send(new ViewMsg_Resize(routing_id_, params))) {
@@ -667,14 +637,16 @@ void RenderWidgetHostImpl::Blur() {
if (IsMouseLocked())
view_->UnlockMouse();
- // If there is a pending overscroll, then that should be cancelled.
- if (overscroll_controller_)
- overscroll_controller_->Cancel();
+ if (touch_emulator_)
+ touch_emulator_->CancelTouch();
Send(new InputMsg_SetFocus(routing_id_, false));
}
void RenderWidgetHostImpl::LostCapture() {
+ if (touch_emulator_)
+ touch_emulator_->CancelTouch();
+
Send(new InputMsg_MouseCaptureLost(routing_id_));
}
@@ -704,89 +676,66 @@ void RenderWidgetHostImpl::SetIsLoading(bool is_loading) {
void RenderWidgetHostImpl::CopyFromBackingStore(
const gfx::Rect& src_subrect,
const gfx::Size& accelerated_dst_size,
- const base::Callback<void(bool, const SkBitmap&)>& callback) {
- if (view_ && is_accelerated_compositing_active_) {
+ const base::Callback<void(bool, const SkBitmap&)>& callback,
+ const SkBitmap::Config& bitmap_config) {
+ if (view_) {
TRACE_EVENT0("browser",
"RenderWidgetHostImpl::CopyFromBackingStore::FromCompositingSurface");
gfx::Rect accelerated_copy_rect = src_subrect.IsEmpty() ?
gfx::Rect(view_->GetViewBounds().size()) : src_subrect;
- view_->CopyFromCompositingSurface(accelerated_copy_rect,
- accelerated_dst_size,
- callback);
+ view_->CopyFromCompositingSurface(
+ accelerated_copy_rect, accelerated_dst_size, callback, bitmap_config);
return;
}
- BackingStore* backing_store = GetBackingStore(false);
- if (!backing_store) {
- callback.Run(false, SkBitmap());
- return;
- }
+ callback.Run(false, SkBitmap());
+}
- TRACE_EVENT0("browser",
- "RenderWidgetHostImpl::CopyFromBackingStore::FromBackingStore");
- gfx::Rect copy_rect = src_subrect.IsEmpty() ?
- gfx::Rect(backing_store->size()) : src_subrect;
- // When the result size is equal to the backing store size, copy from the
- // backing store directly to the output canvas.
- skia::PlatformBitmap output;
- bool result = backing_store->CopyFromBackingStore(copy_rect, &output);
- callback.Run(result, output.GetBitmap());
-}
-
-#if defined(TOOLKIT_GTK)
-bool RenderWidgetHostImpl::CopyFromBackingStoreToGtkWindow(
- const gfx::Rect& dest_rect, GdkWindow* target) {
- BackingStore* backing_store = GetBackingStore(false);
- if (!backing_store)
- return false;
- (static_cast<BackingStoreGtk*>(backing_store))->PaintToRect(
- dest_rect, target);
- return true;
+bool RenderWidgetHostImpl::CanCopyFromBackingStore() {
+ if (view_)
+ return view_->IsSurfaceAvailableForCopy();
+ return false;
}
-#elif defined(OS_MACOSX)
-gfx::Size RenderWidgetHostImpl::GetBackingStoreSize() {
- BackingStore* backing_store = GetBackingStore(false);
- return backing_store ? backing_store->size() : gfx::Size();
+
+#if defined(OS_ANDROID)
+void RenderWidgetHostImpl::LockBackingStore() {
+ if (view_)
+ view_->LockCompositingSurface();
}
-bool RenderWidgetHostImpl::CopyFromBackingStoreToCGContext(
- const CGRect& dest_rect, CGContextRef target) {
- BackingStore* backing_store = GetBackingStore(false);
- if (!backing_store)
- return false;
- (static_cast<BackingStoreMac*>(backing_store))->
- CopyFromBackingStoreToCGContext(dest_rect, target);
- return true;
+void RenderWidgetHostImpl::UnlockBackingStore() {
+ if (view_)
+ view_->UnlockCompositingSurface();
}
#endif
-void RenderWidgetHostImpl::PaintAtSize(TransportDIB::Handle dib_handle,
- int tag,
- const gfx::Size& page_size,
- const gfx::Size& desired_size) {
- // Ask the renderer to create a bitmap regardless of whether it's
- // hidden, being resized, redrawn, etc. It resizes the web widget
- // to the page_size and then scales it to the desired_size.
- Send(new ViewMsg_PaintAtSize(routing_id_, dib_handle, tag,
- page_size, desired_size));
-}
-
-bool RenderWidgetHostImpl::TryGetBackingStore(const gfx::Size& desired_size,
- BackingStore** backing_store) {
- // Check if the view has an accelerated surface of the desired size.
- if (view_->HasAcceleratedSurface(desired_size)) {
- *backing_store = NULL;
- return true;
- }
+void RenderWidgetHostImpl::PauseForPendingResizeOrRepaints() {
+ TRACE_EVENT0("browser",
+ "RenderWidgetHostImpl::PauseForPendingResizeOrRepaints");
+
+ if (!CanPauseForPendingResizeOrRepaints())
+ return;
- // Check for a software backing store of the desired size.
- *backing_store = BackingStoreManager::GetBackingStore(this, desired_size);
- return !!*backing_store;
+ WaitForSurface();
}
-BackingStore* RenderWidgetHostImpl::GetBackingStore(bool force_create) {
+bool RenderWidgetHostImpl::CanPauseForPendingResizeOrRepaints() {
+ // Do not pause if the view is hidden.
+ if (is_hidden())
+ return false;
+
+ // Do not pause if there is not a paint or resize already coming.
+ if (!repaint_ack_pending_ && !resize_ack_pending_)
+ return false;
+
+ return true;
+}
+
+void RenderWidgetHostImpl::WaitForSurface() {
+ TRACE_EVENT0("browser", "RenderWidgetHostImpl::WaitForSurface");
+
if (!view_)
- return NULL;
+ return;
// The view_size will be current_size_ for auto-sized views and otherwise the
// size of the view_. (For auto-sized views, current_size_ is updated during
@@ -796,35 +745,36 @@ BackingStore* RenderWidgetHostImpl::GetBackingStore(bool force_create) {
// Get the desired size from the current view bounds.
gfx::Rect view_rect = view_->GetViewBounds();
if (view_rect.IsEmpty())
- return NULL;
+ return;
view_size = view_rect.size();
}
- TRACE_EVENT2("renderer_host", "RenderWidgetHostImpl::GetBackingStore",
- "width", base::IntToString(view_size.width()),
- "height", base::IntToString(view_size.height()));
+ TRACE_EVENT2("renderer_host",
+ "RenderWidgetHostImpl::WaitForBackingStore",
+ "width",
+ base::IntToString(view_size.width()),
+ "height",
+ base::IntToString(view_size.height()));
// We should not be asked to paint while we are hidden. If we are hidden,
// then it means that our consumer failed to call WasShown. If we're not
// force creating the backing store, it's OK since we can feel free to give
// out our cached one if we have it.
- DCHECK(!is_hidden_ || !force_create) <<
- "GetBackingStore called while hidden!";
+ DCHECK(!is_hidden_) << "WaitForSurface called while hidden!";
// We should never be called recursively; this can theoretically lead to
// infinite recursion and almost certainly leads to lower performance.
- DCHECK(!in_get_backing_store_) << "GetBackingStore called recursively!";
+ DCHECK(!in_get_backing_store_) << "WaitForSurface called recursively!";
base::AutoReset<bool> auto_reset_in_get_backing_store(
&in_get_backing_store_, true);
- // We might have a cached backing store that we can reuse!
- BackingStore* backing_store = NULL;
- if (TryGetBackingStore(view_size, &backing_store) || !force_create)
- return backing_store;
+ // We might have a surface that we can use!
+ if (view_->HasAcceleratedSurface(view_size))
+ return;
// We do not have a suitable backing store in the cache, so send out a
// request to the renderer to paint the view if required.
- if (!repaint_ack_pending_ && !resize_ack_pending_ && !view_being_painted_) {
+ if (!repaint_ack_pending_ && !resize_ack_pending_) {
repaint_start_time_ = TimeTicks::Now();
repaint_ack_pending_ = true;
TRACE_EVENT_ASYNC_BEGIN0(
@@ -835,11 +785,7 @@ BackingStore* RenderWidgetHostImpl::GetBackingStore(bool force_create) {
TimeDelta max_delay = TimeDelta::FromMilliseconds(kPaintMsgTimeoutMS);
TimeTicks end_time = TimeTicks::Now() + max_delay;
do {
- TRACE_EVENT0("renderer_host", "GetBackingStore::WaitForUpdate");
-
-#if defined(OS_MACOSX)
- view_->AboutToWaitForBackingStoreMsg();
-#endif
+ TRACE_EVENT0("renderer_host", "WaitForSurface::WaitForUpdate");
// When we have asked the RenderWidget to resize, and we are still waiting
// on a response, block for a little while to see if we can't get a response
@@ -855,13 +801,10 @@ BackingStore* RenderWidgetHostImpl::GetBackingStore(bool force_create) {
// Break now if we got a backing store or accelerated surface of the
// correct size.
- if (TryGetBackingStore(view_size, &backing_store) ||
- abort_get_backing_store_) {
- abort_get_backing_store_ = false;
- return backing_store;
- }
+ if (view_->HasAcceleratedSurface(view_size))
+ return;
} else {
- TRACE_EVENT0("renderer_host", "GetBackingStore::Timeout");
+ TRACE_EVENT0("renderer_host", "WaitForSurface::Timeout");
break;
}
@@ -871,28 +814,11 @@ BackingStore* RenderWidgetHostImpl::GetBackingStore(bool force_create) {
// BackingStore messages to get to the latest.
max_delay = end_time - TimeTicks::Now();
} while (max_delay > TimeDelta::FromSeconds(0));
-
- // We have failed to get a backing store of view_size. Fall back on
- // current_size_ to avoid a white flash while resizing slow pages.
- if (view_size != current_size_)
- TryGetBackingStore(current_size_, &backing_store);
- return backing_store;
-}
-
-BackingStore* RenderWidgetHostImpl::AllocBackingStore(const gfx::Size& size) {
- if (!view_)
- return NULL;
- return view_->AllocBackingStore(size);
-}
-
-void RenderWidgetHostImpl::DonePaintingToBackingStore() {
- Send(new ViewMsg_UpdateRect_ACK(GetRoutingID()));
}
bool RenderWidgetHostImpl::ScheduleComposite() {
- if (is_hidden_ || !is_accelerated_compositing_active_ ||
- current_size_.IsEmpty() || repaint_ack_pending_ ||
- resize_ack_pending_ || view_being_painted_) {
+ if (is_hidden_ || current_size_.IsEmpty() || repaint_ack_pending_ ||
+ resize_ack_pending_) {
return false;
}
@@ -923,28 +849,47 @@ void RenderWidgetHostImpl::StopHangMonitorTimeout() {
}
void RenderWidgetHostImpl::EnableFullAccessibilityMode() {
- SetAccessibilityMode(AccessibilityModeComplete);
+ AddAccessibilityMode(AccessibilityModeComplete);
+}
+
+bool RenderWidgetHostImpl::IsFullAccessibilityModeForTesting() {
+ return accessibility_mode() == AccessibilityModeComplete;
+}
+
+void RenderWidgetHostImpl::EnableTreeOnlyAccessibilityMode() {
+ AddAccessibilityMode(AccessibilityModeTreeOnly);
+}
+
+bool RenderWidgetHostImpl::IsTreeOnlyAccessibilityModeForTesting() {
+ return accessibility_mode() == AccessibilityModeTreeOnly;
}
void RenderWidgetHostImpl::ForwardMouseEvent(const WebMouseEvent& mouse_event) {
- ForwardMouseEventWithLatencyInfo(MouseEventWithLatencyInfo(
- mouse_event, CreateRWHLatencyInfoIfNotExist(NULL, mouse_event.type)));
+ ForwardMouseEventWithLatencyInfo(mouse_event, ui::LatencyInfo());
}
void RenderWidgetHostImpl::ForwardMouseEventWithLatencyInfo(
- const MouseEventWithLatencyInfo& mouse_event) {
+ const blink::WebMouseEvent& mouse_event,
+ const ui::LatencyInfo& ui_latency) {
TRACE_EVENT2("input", "RenderWidgetHostImpl::ForwardMouseEvent",
- "x", mouse_event.event.x, "y", mouse_event.event.y);
+ "x", mouse_event.x, "y", mouse_event.y);
+
+ ui::LatencyInfo latency_info =
+ CreateRWHLatencyInfoIfNotExist(&ui_latency, mouse_event.type);
for (size_t i = 0; i < mouse_event_callbacks_.size(); ++i) {
- if (mouse_event_callbacks_[i].Run(mouse_event.event))
+ if (mouse_event_callbacks_[i].Run(mouse_event))
return;
}
if (IgnoreInputEvents())
return;
- input_router_->SendMouseEvent(mouse_event);
+ if (touch_emulator_ && touch_emulator_->HandleMouseEvent(mouse_event))
+ return;
+
+ input_router_->SendMouseEvent(MouseEventWithLatencyInfo(mouse_event,
+ latency_info));
}
void RenderWidgetHostImpl::OnPointerEventActivate() {
@@ -952,20 +897,25 @@ void RenderWidgetHostImpl::OnPointerEventActivate() {
void RenderWidgetHostImpl::ForwardWheelEvent(
const WebMouseWheelEvent& wheel_event) {
- ForwardWheelEventWithLatencyInfo(MouseWheelEventWithLatencyInfo(
- wheel_event, CreateRWHLatencyInfoIfNotExist(NULL, wheel_event.type)));
+ ForwardWheelEventWithLatencyInfo(wheel_event, ui::LatencyInfo());
}
void RenderWidgetHostImpl::ForwardWheelEventWithLatencyInfo(
- const MouseWheelEventWithLatencyInfo& wheel_event) {
+ const blink::WebMouseWheelEvent& wheel_event,
+ const ui::LatencyInfo& ui_latency) {
TRACE_EVENT0("input", "RenderWidgetHostImpl::ForwardWheelEvent");
+
+ ui::LatencyInfo latency_info =
+ CreateRWHLatencyInfoIfNotExist(&ui_latency, wheel_event.type);
+
if (IgnoreInputEvents())
return;
- if (delegate_->PreHandleWheelEvent(wheel_event.event))
+ if (touch_emulator_ && touch_emulator_->HandleMouseWheelEvent(wheel_event))
return;
- input_router_->SendWheelEvent(wheel_event);
+ input_router_->SendWheelEvent(MouseWheelEventWithLatencyInfo(wheel_event,
+ latency_info));
}
void RenderWidgetHostImpl::ForwardGestureEvent(
@@ -981,6 +931,9 @@ void RenderWidgetHostImpl::ForwardGestureEventWithLatencyInfo(
if (IgnoreInputEvents())
return;
+ if (delegate_->PreHandleGestureEvent(gesture_event))
+ return;
+
ui::LatencyInfo latency_info =
CreateRWHLatencyInfoIfNotExist(&ui_latency, gesture_event.type);
@@ -1002,8 +955,7 @@ void RenderWidgetHostImpl::ForwardGestureEventWithLatencyInfo(
GetLatencyComponentId(),
original_component.sequence_number,
original_component.event_time,
- original_component.event_count,
- true);
+ original_component.event_count);
}
}
@@ -1011,6 +963,11 @@ void RenderWidgetHostImpl::ForwardGestureEventWithLatencyInfo(
input_router_->SendGestureEvent(gesture_with_latency);
}
+void RenderWidgetHostImpl::ForwardTouchEvent(
+ const blink::WebTouchEvent& touch_event) {
+ ForwardTouchEventWithLatencyInfo(touch_event, ui::LatencyInfo());
+}
+
void RenderWidgetHostImpl::ForwardTouchEventWithLatencyInfo(
const blink::WebTouchEvent& touch_event,
const ui::LatencyInfo& ui_latency) {
@@ -1086,12 +1043,35 @@ void RenderWidgetHostImpl::ForwardKeyboardEvent(
suppress_next_char_events_ = false;
}
+ if (touch_emulator_ && touch_emulator_->HandleKeyboardEvent(key_event))
+ return;
+
input_router_->SendKeyboardEvent(
key_event,
CreateRWHLatencyInfoIfNotExist(NULL, key_event.type),
is_shortcut);
}
+void RenderWidgetHostImpl::QueueSyntheticGesture(
+ scoped_ptr<SyntheticGesture> synthetic_gesture,
+ const base::Callback<void(SyntheticGesture::Result)>& on_complete) {
+ if (!synthetic_gesture_controller_ && view_) {
+ synthetic_gesture_controller_.reset(
+ new SyntheticGestureController(
+ view_->CreateSyntheticGestureTarget().Pass()));
+ }
+ if (synthetic_gesture_controller_) {
+ synthetic_gesture_controller_->QueueSyntheticGesture(
+ synthetic_gesture.Pass(), on_complete);
+ }
+}
+
+void RenderWidgetHostImpl::SetCursor(const WebCursor& cursor) {
+ if (!view_)
+ return;
+ view_->UpdateCursor(cursor);
+}
+
void RenderWidgetHostImpl::SendCursorVisibilityState(bool is_visible) {
Send(new InputMsg_CursorVisibilityChange(GetRoutingID(), is_visible));
}
@@ -1157,10 +1137,10 @@ void RenderWidgetHostImpl::RemoveMouseEventCallback(
void RenderWidgetHostImpl::GetWebScreenInfo(blink::WebScreenInfo* result) {
TRACE_EVENT0("renderer_host", "RenderWidgetHostImpl::GetWebScreenInfo");
- if (GetView())
- static_cast<RenderWidgetHostViewPort*>(GetView())->GetScreenInfo(result);
+ if (view_)
+ view_->GetScreenInfo(result);
else
- RenderWidgetHostViewPort::GetDefaultScreenInfo(result);
+ RenderWidgetHostViewBase::GetDefaultScreenInfo(result);
screen_info_out_of_date_ = false;
}
@@ -1182,42 +1162,18 @@ void RenderWidgetHostImpl::InvalidateScreenInfo() {
screen_info_.reset();
}
-void RenderWidgetHostImpl::GetSnapshotFromRenderer(
- const gfx::Rect& src_subrect,
- const base::Callback<void(bool, const SkBitmap&)>& callback) {
- TRACE_EVENT0("browser", "RenderWidgetHostImpl::GetSnapshotFromRenderer");
- if (!view_) {
- callback.Run(false, SkBitmap());
- return;
- }
-
- pending_snapshots_.push(callback);
-
- gfx::Rect copy_rect = src_subrect.IsEmpty() ?
- gfx::Rect(view_->GetViewBounds().size()) : src_subrect;
-
- gfx::Rect copy_rect_in_pixel = ConvertViewRectToPixel(view_, copy_rect);
- Send(new ViewMsg_Snapshot(GetRoutingID(), copy_rect_in_pixel));
+void RenderWidgetHostImpl::OnSelectionChanged(const base::string16& text,
+ size_t offset,
+ const gfx::Range& range) {
+ if (view_)
+ view_->SelectionChanged(text, offset, range);
}
-void RenderWidgetHostImpl::OnSnapshot(bool success,
- const SkBitmap& bitmap) {
- if (pending_snapshots_.size() == 0) {
- LOG(ERROR) << "RenderWidgetHostImpl::OnSnapshot: "
- "Received a snapshot that was not requested.";
- return;
- }
-
- base::Callback<void(bool, const SkBitmap&)> callback =
- pending_snapshots_.front();
- pending_snapshots_.pop();
-
- if (!success) {
- callback.Run(success, SkBitmap());
- return;
+void RenderWidgetHostImpl::OnSelectionBoundsChanged(
+ const ViewHostMsg_SelectionBounds_Params& params) {
+ if (view_) {
+ view_->SelectionBoundsChanged(params);
}
-
- callback.Run(success, bitmap);
}
void RenderWidgetHostImpl::UpdateVSyncParameters(base::TimeTicks timebase,
@@ -1233,20 +1189,13 @@ void RenderWidgetHostImpl::RendererExited(base::TerminationStatus status,
waiting_for_screen_rects_ack_ = false;
- // Reset to ensure that input routing works with a new renderer.
- input_router_.reset(new InputRouterImpl(process_, this, this, routing_id_));
-
- if (overscroll_controller_)
- overscroll_controller_->Reset();
-
- // Must reset these to ensure that keyboard events work with a new renderer.
+ // Must reset these to ensure that keyboard events work with a new renderer.
suppress_next_char_events_ = false;
// Reset some fields in preparation for recovering from a crash.
ResetSizeAndRepaintPendingFlags();
current_size_.SetSize(0, 0);
is_hidden_ = false;
- is_accelerated_compositing_active_ = false;
// Reset this to ensure the hung renderer mechanism is working properly.
in_flight_event_count_ = 0;
@@ -1258,7 +1207,12 @@ void RenderWidgetHostImpl::RendererExited(base::TerminationStatus status,
view_ = NULL; // The View should be deleted by RenderProcessGone.
}
- BackingStoreManager::RemoveBackingStore(this);
+ // Reconstruct the input router to ensure that it has fresh state for a new
+ // renderer. Otherwise it may be stuck waiting for the old renderer to ack an
+ // event. (In particular, the above call to view_->RenderProcessGone will
+ // destroy the aura window, which may dispatch a synthetic mouse move.)
+ input_router_.reset(new InputRouterImpl(
+ process_, this, this, routing_id_, GetInputRouterConfigForPlatform()));
synthetic_gesture_controller_.reset();
}
@@ -1321,12 +1275,6 @@ void RenderWidgetHostImpl::ImeCancelComposition() {
std::vector<blink::WebCompositionUnderline>(), 0, 0));
}
-void RenderWidgetHostImpl::ExtendSelectionAndDelete(
- size_t before,
- size_t after) {
- Send(new ViewMsg_ExtendSelectionAndDelete(GetRoutingID(), before, after));
-}
-
gfx::Rect RenderWidgetHostImpl::GetRootWindowResizerRect() const {
return gfx::Rect();
}
@@ -1460,19 +1408,20 @@ void RenderWidgetHostImpl::OnRequestMove(const gfx::Rect& pos) {
}
}
-void RenderWidgetHostImpl::OnPaintAtSizeAck(int tag, const gfx::Size& size) {
- std::pair<int, gfx::Size> details = std::make_pair(tag, size);
- NotificationService::current()->Notify(
- NOTIFICATION_RENDER_WIDGET_HOST_DID_RECEIVE_PAINT_AT_SIZE_ACK,
- Source<RenderWidgetHost>(this),
- Details<std::pair<int, gfx::Size> >(&details));
-}
-
#if defined(OS_MACOSX)
void RenderWidgetHostImpl::OnCompositorSurfaceBuffersSwapped(
const ViewHostMsg_CompositorSurfaceBuffersSwapped_Params& params) {
+ // This trace event is used in
+ // chrome/browser/extensions/api/cast_streaming/performance_test.cc
TRACE_EVENT0("renderer_host",
"RenderWidgetHostImpl::OnCompositorSurfaceBuffersSwapped");
+ // This trace event is used in
+ // chrome/browser/extensions/api/cast_streaming/performance_test.cc
+ UNSHIPPED_TRACE_EVENT0("test_fps",
+ TRACE_DISABLED_BY_DEFAULT("OnSwapCompositorFrame"));
+ if (!ui::LatencyInfo::Verify(params.latency_info,
+ "ViewHostMsg_CompositorSurfaceBuffersSwapped"))
+ return;
if (!view_) {
AcceleratedSurfaceMsg_BufferPresented_Params ack_params;
ack_params.sync_point = 0;
@@ -1488,6 +1437,8 @@ void RenderWidgetHostImpl::OnCompositorSurfaceBuffersSwapped(
gpu_params.size = params.size;
gpu_params.scale_factor = params.scale_factor;
gpu_params.latency_info = params.latency_info;
+ for (size_t i = 0; i < gpu_params.latency_info.size(); i++)
+ AddLatencyInfoComponentIds(&gpu_params.latency_info[i]);
view_->AcceleratedSurfaceBuffersSwapped(gpu_params,
params.gpu_process_host_id);
view_->DidReceiveRendererFrame();
@@ -1496,6 +1447,10 @@ void RenderWidgetHostImpl::OnCompositorSurfaceBuffersSwapped(
bool RenderWidgetHostImpl::OnSwapCompositorFrame(
const IPC::Message& message) {
+ // This trace event is used in
+ // chrome/browser/extensions/api/cast_streaming/performance_test.cc
+ UNSHIPPED_TRACE_EVENT0("test_fps",
+ TRACE_DISABLED_BY_DEFAULT("OnSwapCompositorFrame"));
ViewHostMsg_SwapCompositorFrame::Param param;
if (!ViewHostMsg_SwapCompositorFrame::Read(&message, &param))
return false;
@@ -1503,12 +1458,11 @@ bool RenderWidgetHostImpl::OnSwapCompositorFrame(
uint32 output_surface_id = param.a;
param.b.AssignTo(frame.get());
- bool fixed_page_scale =
- frame->metadata.min_page_scale_factor ==
- frame->metadata.max_page_scale_factor;
- int updated_view_flags = fixed_page_scale ? InputRouter::FIXED_PAGE_SCALE
- : InputRouter::VIEW_FLAGS_NONE;
- input_router_->OnViewUpdated(updated_view_flags);
+ for (size_t i = 0; i < frame->metadata.latency_info.size(); i++)
+ AddLatencyInfoComponentIds(&frame->metadata.latency_info[i]);
+
+ input_router_->OnViewUpdated(
+ GetInputRouterViewFlagsFromCompositorFrameMetadata(frame->metadata));
if (view_) {
view_->OnSwapCompositorFrame(output_surface_id, frame.Pass());
@@ -1525,17 +1479,15 @@ bool RenderWidgetHostImpl::OnSwapCompositorFrame(
} else if (frame->software_frame_data) {
ack.last_software_frame_id = frame->software_frame_data->id;
}
- SendSwapCompositorFrameAck(routing_id_, process_->GetID(),
- output_surface_id, ack);
+ SendSwapCompositorFrameAck(routing_id_, output_surface_id,
+ process_->GetID(), ack);
}
return true;
}
-void RenderWidgetHostImpl::OnOverscrolled(
- gfx::Vector2dF accumulated_overscroll,
- gfx::Vector2dF current_fling_velocity) {
+void RenderWidgetHostImpl::OnFlingingStopped() {
if (view_)
- view_->OnOverscrolled(accumulated_overscroll, current_fling_velocity);
+ view_->DidStopFlinging();
}
void RenderWidgetHostImpl::OnUpdateRect(
@@ -1571,47 +1523,7 @@ void RenderWidgetHostImpl::OnUpdateRect(
DCHECK(!params.view_size.IsEmpty());
- bool was_async = false;
-
- // If this is a GPU UpdateRect, params.bitmap is invalid and dib will be NULL.
- TransportDIB* dib = process_->GetTransportDIB(params.bitmap);
-
- // If gpu process does painting, scroll_rect and copy_rects are always empty
- // and backing store is never used.
- if (dib) {
- DCHECK(!params.bitmap_rect.IsEmpty());
- gfx::Size pixel_size = gfx::ToFlooredSize(
- gfx::ScaleSize(params.bitmap_rect.size(), params.scale_factor));
- const size_t size = pixel_size.height() * pixel_size.width() * 4;
- if (dib->size() < size) {
- DLOG(WARNING) << "Transport DIB too small for given rectangle";
- RecordAction(UserMetricsAction("BadMessageTerminate_RWH1"));
- GetProcess()->ReceivedBadMessage();
- } else {
- // Scroll the backing store.
- if (!params.scroll_rect.IsEmpty()) {
- ScrollBackingStoreRect(params.scroll_delta,
- params.scroll_rect,
- params.view_size);
- }
-
- // Paint the backing store. This will update it with the
- // renderer-supplied bits. The view will read out of the backing store
- // later to actually draw to the screen.
- was_async = PaintBackingStoreRect(
- params.bitmap,
- params.bitmap_rect,
- params.copy_rects,
- params.view_size,
- params.scale_factor,
- base::Bind(&RenderWidgetHostImpl::DidUpdateBackingStore,
- weak_factory_.GetWeakPtr(), params, paint_start));
- }
- }
-
- if (!was_async) {
- DidUpdateBackingStore(params, paint_start);
- }
+ DidUpdateBackingStore(params, paint_start);
if (should_auto_resize_) {
bool post_callback = new_auto_size_.IsEmpty();
@@ -1631,32 +1543,19 @@ void RenderWidgetHostImpl::OnUpdateRect(
UMA_HISTOGRAM_TIMES("MPArch.RWH_OnMsgUpdateRect", delta);
}
-void RenderWidgetHostImpl::OnUpdateIsDelayed() {
- if (in_get_backing_store_)
- abort_get_backing_store_ = true;
-}
-
void RenderWidgetHostImpl::DidUpdateBackingStore(
const ViewHostMsg_UpdateRect_Params& params,
const TimeTicks& paint_start) {
TRACE_EVENT0("renderer_host", "RenderWidgetHostImpl::DidUpdateBackingStore");
TimeTicks update_start = TimeTicks::Now();
- if (params.needs_ack) {
- // ACK early so we can prefetch the next PaintRect if there is a next one.
- // This must be done AFTER we're done painting with the bitmap supplied by
- // the renderer. This ACK is a signal to the renderer that the backing store
- // can be re-used, so the bitmap may be invalid after this call.
- Send(new ViewMsg_UpdateRect_ACK(routing_id_));
- }
-
// Move the plugins if the view hasn't already been destroyed. Plugin moves
// will not be re-issued, so must move them now, regardless of whether we
// paint or not. MovePluginWindows attempts to move the plugin windows and
// in the process could dispatch other window messages which could cause the
// view to be destroyed.
if (view_)
- view_->MovePluginWindows(params.scroll_offset, params.plugin_window_moves);
+ view_->MovePluginWindows(params.plugin_window_moves);
NotificationService::current()->Notify(
NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE,
@@ -1669,15 +1568,6 @@ void RenderWidgetHostImpl::DidUpdateBackingStore(
if (is_hidden_)
return;
- // Now paint the view. Watch out: it might be destroyed already.
- if (view_ && !is_accelerated_compositing_active_) {
- view_being_painted_ = true;
- view_->DidUpdateBackingStore(params.scroll_rect, params.scroll_delta,
- params.copy_rects, params.latency_info);
- view_->DidReceiveRendererFrame();
- view_being_painted_ = false;
- }
-
// If we got a resize ack, then perhaps we have another resize to send?
bool is_resize_ack =
ViewHostMsg_UpdateRect_Flags::is_resize_ack(params.flags);
@@ -1688,60 +1578,62 @@ void RenderWidgetHostImpl::DidUpdateBackingStore(
TimeTicks now = TimeTicks::Now();
TimeDelta delta = now - update_start;
UMA_HISTOGRAM_TIMES("MPArch.RWH_DidUpdateBackingStore", delta);
-
- // Measures the time from receiving the MsgUpdateRect IPC to completing the
- // DidUpdateBackingStore() method. On platforms which have asynchronous
- // painting, such as Linux, this is the sum of MPArch.RWH_OnMsgUpdateRect,
- // MPArch.RWH_DidUpdateBackingStore, and the time spent asynchronously
- // waiting for the paint to complete.
- //
- // On other platforms, this will be equivalent to MPArch.RWH_OnMsgUpdateRect.
- delta = now - paint_start;
- UMA_HISTOGRAM_TIMES("MPArch.RWH_TotalPaintTime", delta);
}
void RenderWidgetHostImpl::OnQueueSyntheticGesture(
const SyntheticGesturePacket& gesture_packet) {
- if (!synthetic_gesture_controller_) {
- if (!view_)
- return;
- synthetic_gesture_controller_.reset(
- new SyntheticGestureController(
- view_->CreateSyntheticGestureTarget().Pass()));
+ // Only allow untrustworthy gestures if explicitly enabled.
+ if (!CommandLine::ForCurrentProcess()->HasSwitch(
+ cc::switches::kEnableGpuBenchmarking)) {
+ RecordAction(base::UserMetricsAction("BadMessageTerminate_RWH7"));
+ GetProcess()->ReceivedBadMessage();
+ return;
}
- synthetic_gesture_controller_->QueueSyntheticGesture(
- SyntheticGesture::Create(*gesture_packet.gesture_params()));
+ QueueSyntheticGesture(
+ SyntheticGesture::Create(*gesture_packet.gesture_params()),
+ base::Bind(&RenderWidgetHostImpl::OnSyntheticGestureCompleted,
+ weak_factory_.GetWeakPtr()));
}
void RenderWidgetHostImpl::OnFocus() {
// Only RenderViewHost can deal with that message.
- RecordAction(UserMetricsAction("BadMessageTerminate_RWH4"));
+ RecordAction(base::UserMetricsAction("BadMessageTerminate_RWH4"));
GetProcess()->ReceivedBadMessage();
}
void RenderWidgetHostImpl::OnBlur() {
// Only RenderViewHost can deal with that message.
- RecordAction(UserMetricsAction("BadMessageTerminate_RWH5"));
+ RecordAction(base::UserMetricsAction("BadMessageTerminate_RWH5"));
GetProcess()->ReceivedBadMessage();
}
void RenderWidgetHostImpl::OnSetCursor(const WebCursor& cursor) {
- if (!view_) {
- return;
+ SetCursor(cursor);
+}
+
+void RenderWidgetHostImpl::OnSetTouchEventEmulationEnabled(
+ bool enabled, bool allow_pinch) {
+ if (delegate_)
+ delegate_->OnTouchEmulationEnabled(enabled);
+
+ if (enabled) {
+ if (!touch_emulator_)
+ touch_emulator_.reset(new TouchEmulator(this));
+ touch_emulator_->Enable(allow_pinch);
+ } else {
+ if (touch_emulator_)
+ touch_emulator_->Disable();
}
- view_->UpdateCursor(cursor);
}
-void RenderWidgetHostImpl::OnTextInputTypeChanged(
- ui::TextInputType type,
- ui::TextInputMode input_mode,
- bool can_compose_inline) {
+void RenderWidgetHostImpl::OnTextInputStateChanged(
+ const ViewHostMsg_TextInputState_Params& params) {
if (view_)
- view_->TextInputTypeChanged(type, input_mode, can_compose_inline);
+ view_->TextInputStateChanged(params);
}
-#if defined(OS_MACOSX) || defined(OS_WIN) || defined(USE_AURA)
+#if defined(OS_MACOSX) || defined(USE_AURA)
void RenderWidgetHostImpl::OnImeCompositionRangeChanged(
const gfx::Range& range,
const std::vector<gfx::Rect>& character_bounds) {
@@ -1755,15 +1647,6 @@ void RenderWidgetHostImpl::OnImeCancelComposition() {
view_->ImeCancelComposition();
}
-void RenderWidgetHostImpl::OnDidActivateAcceleratedCompositing(bool activated) {
- TRACE_EVENT1("renderer_host",
- "RenderWidgetHostImpl::OnDidActivateAcceleratedCompositing",
- "activated", activated);
- is_accelerated_compositing_active_ = activated;
- if (view_)
- view_->OnAcceleratedCompositingStateChange();
-}
-
void RenderWidgetHostImpl::OnLockMouse(bool user_gesture,
bool last_unlocked_by_target,
bool privileged) {
@@ -1792,19 +1675,24 @@ void RenderWidgetHostImpl::OnUnlockMouse() {
void RenderWidgetHostImpl::OnShowDisambiguationPopup(
const gfx::Rect& rect,
const gfx::Size& size,
- const TransportDIB::Id& id) {
+ const cc::SharedBitmapId& id) {
DCHECK(!rect.IsEmpty());
DCHECK(!size.IsEmpty());
- TransportDIB* dib = process_->GetTransportDIB(id);
- DCHECK(dib->memory());
- DCHECK(dib->size() == SkBitmap::ComputeSize(SkBitmap::kARGB_8888_Config,
- size.width(), size.height()));
+ scoped_ptr<cc::SharedBitmap> bitmap =
+ HostSharedBitmapManager::current()->GetSharedBitmapFromId(size, id);
+ if (!bitmap) {
+ RecordAction(base::UserMetricsAction("BadMessageTerminate_RWH6"));
+ GetProcess()->ReceivedBadMessage();
+ return;
+ }
+
+ DCHECK(bitmap->pixels());
SkBitmap zoomed_bitmap;
zoomed_bitmap.setConfig(SkBitmap::kARGB_8888_Config,
size.width(), size.height());
- zoomed_bitmap.setPixels(dib->memory());
+ zoomed_bitmap.setPixels(bitmap->pixels());
#if defined(OS_ANDROID)
if (view_)
@@ -1814,8 +1702,7 @@ void RenderWidgetHostImpl::OnShowDisambiguationPopup(
#endif
zoomed_bitmap.setPixels(0);
- Send(new ViewMsg_ReleaseDisambiguationPopupDIB(GetRoutingID(),
- dib->handle()));
+ Send(new ViewMsg_ReleaseDisambiguationPopupBitmap(GetRoutingID(), id));
}
#if defined(OS_WIN)
@@ -1832,8 +1719,12 @@ void RenderWidgetHostImpl::OnWindowlessPluginDummyWindowCreated(
return;
}
+#if defined(USE_AURA)
SetParent(hwnd,
reinterpret_cast<HWND>(view_->GetParentForWindowlessPlugin()));
+#else
+ SetParent(hwnd, reinterpret_cast<HWND>(GetNativeViewId()));
+#endif
dummy_windows_for_activation_.push_back(hwnd);
}
@@ -1851,72 +1742,6 @@ void RenderWidgetHostImpl::OnWindowlessPluginDummyWindowDestroyed(
}
#endif
-bool RenderWidgetHostImpl::PaintBackingStoreRect(
- TransportDIB::Id bitmap,
- const gfx::Rect& bitmap_rect,
- const std::vector<gfx::Rect>& copy_rects,
- const gfx::Size& view_size,
- float scale_factor,
- const base::Closure& completion_callback) {
- // The view may be destroyed already.
- if (!view_)
- return false;
-
- if (is_hidden_) {
- // Don't bother updating the backing store when we're hidden. Just mark it
- // as being totally invalid. This will cause a complete repaint when the
- // view is restored.
- needs_repainting_on_restore_ = true;
- return false;
- }
-
- bool needs_full_paint = false;
- bool scheduled_completion_callback = false;
- BackingStoreManager::PrepareBackingStore(this, view_size, bitmap, bitmap_rect,
- copy_rects, scale_factor,
- completion_callback,
- &needs_full_paint,
- &scheduled_completion_callback);
- if (needs_full_paint) {
- repaint_start_time_ = TimeTicks::Now();
- DCHECK(!repaint_ack_pending_);
- repaint_ack_pending_ = true;
- TRACE_EVENT_ASYNC_BEGIN0(
- "renderer_host", "RenderWidgetHostImpl::repaint_ack_pending_", this);
- Send(new ViewMsg_Repaint(routing_id_, view_size));
- }
-
- return scheduled_completion_callback;
-}
-
-void RenderWidgetHostImpl::ScrollBackingStoreRect(const gfx::Vector2d& delta,
- const gfx::Rect& clip_rect,
- const gfx::Size& view_size) {
- if (is_hidden_) {
- // Don't bother updating the backing store when we're hidden. Just mark it
- // as being totally invalid. This will cause a complete repaint when the
- // view is restored.
- needs_repainting_on_restore_ = true;
- return;
- }
-
- // TODO(darin): do we need to do something else if our backing store is not
- // the same size as the advertised view? maybe we just assume there is a
- // full paint on its way?
- BackingStore* backing_store = BackingStoreManager::Lookup(this);
- if (!backing_store || (backing_store->size() != view_size))
- return;
- backing_store->ScrollBackingStore(delta, clip_rect, view_size);
-}
-
-void RenderWidgetHostImpl::Replace(const base::string16& word) {
- Send(new InputMsg_Replace(routing_id_, word));
-}
-
-void RenderWidgetHostImpl::ReplaceMisspelling(const base::string16& word) {
- Send(new InputMsg_ReplaceMisspelling(routing_id_, word));
-}
-
void RenderWidgetHostImpl::SetIgnoreInputEvents(bool ignore_input_events) {
ignore_input_events_ = ignore_input_events;
}
@@ -1968,31 +1793,28 @@ void RenderWidgetHostImpl::IncrementInFlightEventCount() {
}
void RenderWidgetHostImpl::DecrementInFlightEventCount() {
- DCHECK(in_flight_event_count_ >= 0);
+ DCHECK_GE(in_flight_event_count_, 0);
// Cancel pending hung renderer checks since the renderer is responsive.
if (decrement_in_flight_event_count() <= 0)
StopHangMonitorTimeout();
}
void RenderWidgetHostImpl::OnHasTouchEventHandlers(bool has_handlers) {
- if (has_touch_handler_ == has_handlers)
- return;
has_touch_handler_ = has_handlers;
-#if defined(OS_ANDROID)
- if (view_)
- view_->HasTouchEventHandlers(has_touch_handler_);
-#endif
-}
-
-OverscrollController* RenderWidgetHostImpl::GetOverscrollController() const {
- return overscroll_controller_.get();
}
void RenderWidgetHostImpl::DidFlush() {
+ if (synthetic_gesture_controller_)
+ synthetic_gesture_controller_->OnDidFlushInput();
if (view_)
view_->OnDidFlushInput();
}
+void RenderWidgetHostImpl::DidOverscroll(const DidOverscrollParams& params) {
+ if (view_)
+ view_->DidOverscroll(params);
+}
+
void RenderWidgetHostImpl::OnKeyboardEventAck(
const NativeWebKeyboardEvent& event,
InputEventAckState ack_result) {
@@ -2026,9 +1848,13 @@ void RenderWidgetHostImpl::OnWheelEventAck(
ui::INPUT_EVENT_LATENCY_TERMINATED_MOUSE_COMPONENT, 0, 0);
}
- const bool processed = (INPUT_EVENT_ACK_STATE_CONSUMED == ack_result);
- if (!processed && !is_hidden() && view_)
- view_->UnhandledWheelEvent(wheel_event.event);
+ if (!is_hidden() && view_) {
+ if (ack_result != INPUT_EVENT_ACK_STATE_CONSUMED &&
+ delegate_->HandleWheelEvent(wheel_event.event)) {
+ ack_result = INPUT_EVENT_ACK_STATE_CONSUMED;
+ }
+ view_->WheelEventAck(wheel_event.event, ack_result);
+ }
}
void RenderWidgetHostImpl::OnGestureEventAck(
@@ -2043,32 +1869,50 @@ void RenderWidgetHostImpl::OnGestureEventAck(
ui::INPUT_EVENT_LATENCY_TERMINATED_GESTURE_COMPONENT, 0 ,0);
}
+ if (ack_result != INPUT_EVENT_ACK_STATE_CONSUMED) {
+ if (delegate_->HandleGestureEvent(event.event))
+ ack_result = INPUT_EVENT_ACK_STATE_CONSUMED;
+ }
+
if (view_)
- view_->GestureEventAck(event.event.type, ack_result);
+ view_->GestureEventAck(event.event, ack_result);
}
void RenderWidgetHostImpl::OnTouchEventAck(
const TouchEventWithLatencyInfo& event,
InputEventAckState ack_result) {
TouchEventWithLatencyInfo touch_event = event;
- // TouchEvent latency does not end when acked since it could later on
- // become gesture events.
touch_event.latency.AddLatencyNumber(
ui::INPUT_EVENT_LATENCY_ACKED_TOUCH_COMPONENT, 0, 0);
+ // TouchEvent latency ends at ack if it didn't cause any rendering.
+ if (!touch_event.latency.FindLatency(
+ ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT, 0, NULL)) {
+ touch_event.latency.AddLatencyNumber(
+ ui::INPUT_EVENT_LATENCY_TERMINATED_TOUCH_COMPONENT, 0, 0);
+ }
ComputeTouchLatency(touch_event.latency);
+
+ if (touch_emulator_ && touch_emulator_->HandleTouchEventAck(ack_result))
+ return;
+
if (view_)
view_->ProcessAckedTouchEvent(touch_event, ack_result);
}
void RenderWidgetHostImpl::OnUnexpectedEventAck(UnexpectedEventAckType type) {
if (type == BAD_ACK_MESSAGE) {
- RecordAction(UserMetricsAction("BadMessageTerminate_RWH2"));
+ RecordAction(base::UserMetricsAction("BadMessageTerminate_RWH2"));
process_->ReceivedBadMessage();
} else if (type == UNEXPECTED_EVENT_TYPE) {
suppress_next_char_events_ = false;
}
}
+void RenderWidgetHostImpl::OnSyntheticGestureCompleted(
+ SyntheticGesture::Result result) {
+ Send(new InputMsg_SyntheticGestureCompleted(GetRoutingID()));
+}
+
const gfx::Vector2d& RenderWidgetHostImpl::GetLastScrollOffset() const {
return last_scroll_offset_;
}
@@ -2089,8 +1933,8 @@ void RenderWidgetHostImpl::Stop() {
Send(new ViewMsg_Stop(GetRoutingID()));
}
-void RenderWidgetHostImpl::SetBackground(const SkBitmap& background) {
- Send(new ViewMsg_SetBackground(GetRoutingID(), background));
+void RenderWidgetHostImpl::SetBackgroundOpaque(bool opaque) {
+ Send(new ViewMsg_SetBackgroundOpaque(GetRoutingID(), opaque));
}
void RenderWidgetHostImpl::SetEditCommandsForNextKeyEvent(
@@ -2098,17 +1942,37 @@ void RenderWidgetHostImpl::SetEditCommandsForNextKeyEvent(
Send(new InputMsg_SetEditCommandsForNextKeyEvent(GetRoutingID(), commands));
}
+void RenderWidgetHostImpl::AddAccessibilityMode(AccessibilityMode mode) {
+ SetAccessibilityMode(
+ content::AddAccessibilityModeTo(accessibility_mode_, mode));
+}
+
+void RenderWidgetHostImpl::RemoveAccessibilityMode(AccessibilityMode mode) {
+ SetAccessibilityMode(
+ content::RemoveAccessibilityModeFrom(accessibility_mode_, mode));
+}
+
+void RenderWidgetHostImpl::ResetAccessibilityMode() {
+ SetAccessibilityMode(
+ BrowserAccessibilityStateImpl::GetInstance()->accessibility_mode());
+}
+
void RenderWidgetHostImpl::SetAccessibilityMode(AccessibilityMode mode) {
accessibility_mode_ = mode;
Send(new ViewMsg_SetAccessibilityMode(GetRoutingID(), mode));
}
+void RenderWidgetHostImpl::AccessibilitySetFocus(int object_id) {
+ Send(new AccessibilityMsg_SetFocus(GetRoutingID(), object_id));
+ view_->OnAccessibilitySetFocus(object_id);
+}
+
void RenderWidgetHostImpl::AccessibilityDoDefaultAction(int object_id) {
Send(new AccessibilityMsg_DoDefaultAction(GetRoutingID(), object_id));
}
-void RenderWidgetHostImpl::AccessibilitySetFocus(int object_id) {
- Send(new AccessibilityMsg_SetFocus(GetRoutingID(), object_id));
+void RenderWidgetHostImpl::AccessibilityShowMenu(int object_id) {
+ view_->AccessibilityShowMenu(object_id);
}
void RenderWidgetHostImpl::AccessibilityScrollToMakeVisible(
@@ -2129,11 +1993,29 @@ void RenderWidgetHostImpl::AccessibilitySetTextSelection(
GetRoutingID(), object_id, start_offset, end_offset));
}
-void RenderWidgetHostImpl::FatalAccessibilityTreeError() {
+bool RenderWidgetHostImpl::AccessibilityViewHasFocus() const {
+ return view_->HasFocus();
+}
+
+gfx::Rect RenderWidgetHostImpl::AccessibilityGetViewBounds() const {
+ return view_->GetViewBounds();
+}
+
+gfx::Point RenderWidgetHostImpl::AccessibilityOriginInScreen(
+ const gfx::Rect& bounds) const {
+ return view_->AccessibilityOriginInScreen(bounds);
+}
+
+void RenderWidgetHostImpl::AccessibilityHitTest(const gfx::Point& point) {
+ Send(new AccessibilityMsg_HitTest(GetRoutingID(), point));
+}
+
+void RenderWidgetHostImpl::AccessibilityFatalError() {
Send(new AccessibilityMsg_FatalError(GetRoutingID()));
+ view_->SetBrowserAccessibilityManager(NULL);
}
-#if defined(OS_WIN) && defined(USE_AURA)
+#if defined(OS_WIN)
void RenderWidgetHostImpl::SetParentNativeViewAccessible(
gfx::NativeViewAccessible accessible_parent) {
if (view_)
@@ -2156,68 +2038,10 @@ void RenderWidgetHostImpl::ScrollFocusedEditableNodeIntoRect(
Send(new InputMsg_ScrollFocusedEditableNodeIntoRect(GetRoutingID(), rect));
}
-void RenderWidgetHostImpl::SelectRange(const gfx::Point& start,
- const gfx::Point& end) {
- Send(new InputMsg_SelectRange(GetRoutingID(), start, end));
-}
-
void RenderWidgetHostImpl::MoveCaret(const gfx::Point& point) {
Send(new InputMsg_MoveCaret(GetRoutingID(), point));
}
-void RenderWidgetHostImpl::Undo() {
- Send(new InputMsg_Undo(GetRoutingID()));
- RecordAction(UserMetricsAction("Undo"));
-}
-
-void RenderWidgetHostImpl::Redo() {
- Send(new InputMsg_Redo(GetRoutingID()));
- RecordAction(UserMetricsAction("Redo"));
-}
-
-void RenderWidgetHostImpl::Cut() {
- Send(new InputMsg_Cut(GetRoutingID()));
- RecordAction(UserMetricsAction("Cut"));
-}
-
-void RenderWidgetHostImpl::Copy() {
- Send(new InputMsg_Copy(GetRoutingID()));
- RecordAction(UserMetricsAction("Copy"));
-}
-
-void RenderWidgetHostImpl::CopyToFindPboard() {
-#if defined(OS_MACOSX)
- // Windows/Linux don't have the concept of a find pasteboard.
- Send(new InputMsg_CopyToFindPboard(GetRoutingID()));
- RecordAction(UserMetricsAction("CopyToFindPboard"));
-#endif
-}
-
-void RenderWidgetHostImpl::Paste() {
- Send(new InputMsg_Paste(GetRoutingID()));
- RecordAction(UserMetricsAction("Paste"));
-}
-
-void RenderWidgetHostImpl::PasteAndMatchStyle() {
- Send(new InputMsg_PasteAndMatchStyle(GetRoutingID()));
- RecordAction(UserMetricsAction("PasteAndMatchStyle"));
-}
-
-void RenderWidgetHostImpl::Delete() {
- Send(new InputMsg_Delete(GetRoutingID()));
- RecordAction(UserMetricsAction("DeleteSelection"));
-}
-
-void RenderWidgetHostImpl::SelectAll() {
- Send(new InputMsg_SelectAll(GetRoutingID()));
- RecordAction(UserMetricsAction("SelectAll"));
-}
-
-void RenderWidgetHostImpl::Unselect() {
- Send(new InputMsg_Unselect(GetRoutingID()));
- RecordAction(UserMetricsAction("Unselect"));
-}
-
bool RenderWidgetHostImpl::GotResponseToLockMouseRequest(bool allowed) {
if (!allowed) {
RejectMouseLockOrUnlockIfNecessary();
@@ -2277,29 +2101,6 @@ void RenderWidgetHostImpl::SendReclaimCompositorResources(
new ViewMsg_ReclaimCompositorResources(route_id, output_surface_id, ack));
}
-void RenderWidgetHostImpl::AcknowledgeSwapBuffersToRenderer() {
- if (!is_threaded_compositing_enabled_)
- Send(new ViewMsg_SwapBuffers_ACK(routing_id_));
-}
-
-#if defined(USE_AURA)
-
-void RenderWidgetHostImpl::ParentChanged(gfx::NativeViewId new_parent) {
-#if defined(OS_WIN)
- HWND hwnd = reinterpret_cast<HWND>(new_parent);
- if (!hwnd)
- hwnd = GetDesktopWindow();
- // On Windows GetParentForWindowlessPlugin returns the dummy window used as
- // the parent for windowless NPAPI plugins. Reparenting this window to the
- // new parent should be good enough.
- if (view_ && view_->GetParentForWindowlessPlugin())
- SetParent(reinterpret_cast<HWND>(view_->GetParentForWindowlessPlugin()),
- reinterpret_cast<HWND>(new_parent));
-#endif
-}
-
-#endif
-
void RenderWidgetHostImpl::DelayedAutoResized() {
gfx::Size new_size = new_auto_size_;
// Clear the new_auto_size_ since the empty value is used as a flag to
@@ -2335,12 +2136,10 @@ void RenderWidgetHostImpl::ComputeTouchLatency(
base::TimeDelta ui_delta =
rwh_component.event_time - ui_component.event_time;
- rendering_stats_.touch_ui_count++;
- rendering_stats_.total_touch_ui_latency += ui_delta;
UMA_HISTOGRAM_CUSTOM_COUNTS(
"Event.Latency.Browser.TouchUI",
ui_delta.InMicroseconds(),
- 0,
+ 1,
20000,
100);
@@ -2350,19 +2149,13 @@ void RenderWidgetHostImpl::ComputeTouchLatency(
DCHECK(acked_component.event_count == 1);
base::TimeDelta acked_delta =
acked_component.event_time - rwh_component.event_time;
- rendering_stats_.touch_acked_count++;
- rendering_stats_.total_touch_acked_latency += acked_delta;
UMA_HISTOGRAM_CUSTOM_COUNTS(
"Event.Latency.Browser.TouchAcked",
acked_delta.InMicroseconds(),
- 0,
+ 1,
1000000,
100);
}
-
- if (CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kEnableGpuBenchmarking))
- Send(new ViewMsg_SetBrowserRenderingStats(routing_id_, rendering_stats_));
}
void RenderWidgetHostImpl::FrameSwapped(const ui::LatencyInfo& latency_info) {
@@ -2370,8 +2163,22 @@ void RenderWidgetHostImpl::FrameSwapped(const ui::LatencyInfo& latency_info) {
if (latency_info.FindLatency(ui::WINDOW_SNAPSHOT_FRAME_NUMBER_COMPONENT,
GetLatencyComponentId(),
&window_snapshot_component)) {
- WindowSnapshotReachedScreen(
- static_cast<int>(window_snapshot_component.sequence_number));
+ int sequence_number = static_cast<int>(
+ window_snapshot_component.sequence_number);
+#if defined(OS_MACOSX)
+ // On Mac, when using CoreAnmation, there is a delay between when content
+ // is drawn to the screen, and when the snapshot will actually pick up
+ // that content. Insert a manual delay of 1/6th of a second (to simulate
+ // 10 frames at 60 fps) before actually taking the snapshot.
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&RenderWidgetHostImpl::WindowSnapshotReachedScreen,
+ weak_factory_.GetWeakPtr(),
+ sequence_number),
+ base::TimeDelta::FromSecondsD(1. / 6));
+#else
+ WindowSnapshotReachedScreen(sequence_number);
+#endif
}
ui::LatencyInfo::LatencyComponent rwh_component;
@@ -2385,11 +2192,6 @@ void RenderWidgetHostImpl::FrameSwapped(const ui::LatencyInfo& latency_info) {
return;
}
- rendering_stats_.input_event_count += rwh_component.event_count;
- rendering_stats_.total_input_latency +=
- rwh_component.event_count *
- (swap_component.event_time - rwh_component.event_time);
-
ui::LatencyInfo::LatencyComponent original_component;
if (latency_info.FindLatency(
ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT,
@@ -2404,74 +2206,126 @@ void RenderWidgetHostImpl::FrameSwapped(const ui::LatencyInfo& latency_info) {
UMA_HISTOGRAM_CUSTOM_COUNTS(
"Event.Latency.TouchToScrollUpdateSwap",
delta.InMicroseconds(),
- 0,
+ 1,
1000000,
100);
}
- rendering_stats_.scroll_update_count += original_component.event_count;
- rendering_stats_.total_scroll_update_latency +=
- original_component.event_count *
- (swap_component.event_time - original_component.event_time);
}
-
- if (CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kEnableGpuBenchmarking))
- Send(new ViewMsg_SetBrowserRenderingStats(routing_id_, rendering_stats_));
}
void RenderWidgetHostImpl::DidReceiveRendererFrame() {
view_->DidReceiveRendererFrame();
}
+void RenderWidgetHostImpl::WindowSnapshotAsyncCallback(
+ int routing_id,
+ int snapshot_id,
+ gfx::Size snapshot_size,
+ scoped_refptr<base::RefCountedBytes> png_data) {
+ if (!png_data) {
+ std::vector<unsigned char> png_vector;
+ Send(new ViewMsg_WindowSnapshotCompleted(
+ routing_id, snapshot_id, gfx::Size(), png_vector));
+ return;
+ }
+
+ Send(new ViewMsg_WindowSnapshotCompleted(
+ routing_id, snapshot_id, snapshot_size, png_data->data()));
+}
+
void RenderWidgetHostImpl::WindowSnapshotReachedScreen(int snapshot_id) {
- DCHECK(base::MessageLoop::current()->IsType(base::MessageLoop::TYPE_UI));
+ DCHECK(base::MessageLoopForUI::IsCurrent());
std::vector<unsigned char> png;
// This feature is behind the kEnableGpuBenchmarking command line switch
// because it poses security concerns and should only be used for testing.
const CommandLine& command_line = *CommandLine::ForCurrentProcess();
- if (command_line.HasSwitch(switches::kEnableGpuBenchmarking)) {
- gfx::Rect view_bounds = GetView()->GetViewBounds();
- gfx::Rect snapshot_bounds(view_bounds.size());
- gfx::Size snapshot_size = snapshot_bounds.size();
-
- if (ui::GrabViewSnapshot(GetView()->GetNativeView(),
- &png, snapshot_bounds)) {
- Send(new ViewMsg_WindowSnapshotCompleted(
- GetRoutingID(), snapshot_id, snapshot_size, png));
- return;
- }
+ if (!command_line.HasSwitch(cc::switches::kEnableGpuBenchmarking)) {
+ Send(new ViewMsg_WindowSnapshotCompleted(
+ GetRoutingID(), snapshot_id, gfx::Size(), png));
+ return;
}
- Send(new ViewMsg_WindowSnapshotCompleted(
- GetRoutingID(), snapshot_id, gfx::Size(), png));
+ gfx::Rect view_bounds = GetView()->GetViewBounds();
+ gfx::Rect snapshot_bounds(view_bounds.size());
+ gfx::Size snapshot_size = snapshot_bounds.size();
+
+ if (ui::GrabViewSnapshot(
+ GetView()->GetNativeView(), &png, snapshot_bounds)) {
+ Send(new ViewMsg_WindowSnapshotCompleted(
+ GetRoutingID(), snapshot_id, snapshot_size, png));
+ return;
+ }
+
+ ui::GrabViewSnapshotAsync(
+ GetView()->GetNativeView(),
+ snapshot_bounds,
+ base::ThreadTaskRunnerHandle::Get(),
+ base::Bind(&RenderWidgetHostImpl::WindowSnapshotAsyncCallback,
+ weak_factory_.GetWeakPtr(),
+ GetRoutingID(),
+ snapshot_id,
+ snapshot_size));
}
// static
void RenderWidgetHostImpl::CompositorFrameDrawn(
- const ui::LatencyInfo& latency_info) {
- std::set<RenderWidgetHostImpl*> rwhi_set;
-
- for (ui::LatencyInfo::LatencyMap::const_iterator b =
- latency_info.latency_components.begin();
- b != latency_info.latency_components.end();
- ++b) {
- if (b->first.first == ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT ||
- b->first.first == ui::WINDOW_SNAPSHOT_FRAME_NUMBER_COMPONENT) {
- // Matches with GetLatencyComponentId
- int routing_id = b->first.second & 0xffffffff;
- int process_id = (b->first.second >> 32) & 0xffffffff;
- RenderWidgetHost* rwh =
- RenderWidgetHost::FromID(process_id, routing_id);
- if (!rwh) {
- continue;
+ const std::vector<ui::LatencyInfo>& latency_info) {
+ for (size_t i = 0; i < latency_info.size(); i++) {
+ std::set<RenderWidgetHostImpl*> rwhi_set;
+ for (ui::LatencyInfo::LatencyMap::const_iterator b =
+ latency_info[i].latency_components.begin();
+ b != latency_info[i].latency_components.end();
+ ++b) {
+ if (b->first.first == ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT ||
+ b->first.first == ui::WINDOW_SNAPSHOT_FRAME_NUMBER_COMPONENT) {
+ // Matches with GetLatencyComponentId
+ int routing_id = b->first.second & 0xffffffff;
+ int process_id = (b->first.second >> 32) & 0xffffffff;
+ RenderWidgetHost* rwh =
+ RenderWidgetHost::FromID(process_id, routing_id);
+ if (!rwh) {
+ continue;
+ }
+ RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(rwh);
+ if (rwhi_set.insert(rwhi).second)
+ rwhi->FrameSwapped(latency_info[i]);
}
- RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(rwh);
- if (rwhi_set.insert(rwhi).second)
- rwhi->FrameSwapped(latency_info);
}
}
}
+void RenderWidgetHostImpl::AddLatencyInfoComponentIds(
+ ui::LatencyInfo* latency_info) {
+ ui::LatencyInfo::LatencyMap new_components;
+ ui::LatencyInfo::LatencyMap::iterator lc =
+ latency_info->latency_components.begin();
+ while (lc != latency_info->latency_components.end()) {
+ ui::LatencyComponentType component_type = lc->first.first;
+ if (component_type == ui::WINDOW_SNAPSHOT_FRAME_NUMBER_COMPONENT) {
+ // Generate a new component entry with the correct component ID
+ ui::LatencyInfo::LatencyMap::key_type key =
+ std::make_pair(component_type, GetLatencyComponentId());
+ new_components[key] = lc->second;
+
+ // Remove the old entry
+ latency_info->latency_components.erase(lc++);
+ } else {
+ ++lc;
+ }
+ }
+
+ // Add newly generated components into the latency info
+ for (lc = new_components.begin(); lc != new_components.end(); ++lc) {
+ latency_info->latency_components[lc->first] = lc->second;
+ }
+}
+
+SkBitmap::Config RenderWidgetHostImpl::PreferredReadbackFormat() {
+ if (view_)
+ return view_->PreferredReadbackFormat();
+ return SkBitmap::kARGB_8888_Config;
+}
+
} // namespace content
diff --git a/chromium/content/browser/renderer_host/render_widget_host_impl.h b/chromium/content/browser/renderer_host/render_widget_host_impl.h
index 22dbdbe0084..65fa860df34 100644
--- a/chromium/content/browser/renderer_host/render_widget_host_impl.h
+++ b/chromium/content/browser/renderer_host/render_widget_host_impl.h
@@ -23,13 +23,16 @@
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "build/build_config.h"
+#include "cc/resources/shared_bitmap.h"
+#include "content/browser/accessibility/browser_accessibility_manager.h"
+#include "content/browser/renderer_host/event_with_latency_info.h"
#include "content/browser/renderer_host/input/input_ack_handler.h"
#include "content/browser/renderer_host/input/input_router_client.h"
-#include "content/common/browser_rendering_stats.h"
+#include "content/browser/renderer_host/input/synthetic_gesture.h"
+#include "content/browser/renderer_host/input/touch_emulator_client.h"
+#include "content/common/input/input_event_ack_state.h"
#include "content/common/input/synthetic_gesture_packet.h"
#include "content/common/view_message_enums.h"
-#include "content/port/browser/event_with_latency_info.h"
-#include "content/port/common/input_event_ack_state.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/common/page_zoom.h"
#include "ipc/ipc_listener.h"
@@ -38,12 +41,12 @@
#include "ui/events/latency_info.h"
#include "ui/gfx/native_widget_types.h"
-class WebCursor;
struct AcceleratedSurfaceMsg_BufferPresented_Params;
+struct ViewHostMsg_BeginSmoothScroll_Params;
struct ViewHostMsg_CompositorSurfaceBuffersSwapped_Params;
-struct ViewHostMsg_UpdateRect_Params;
+struct ViewHostMsg_SelectionBounds_Params;
struct ViewHostMsg_TextInputState_Params;
-struct ViewHostMsg_BeginSmoothScroll_Params;
+struct ViewHostMsg_UpdateRect_Params;
namespace base {
class TimeTicks;
@@ -76,22 +79,25 @@ class WebLayer;
#endif
namespace content {
-class BackingStore;
class InputRouter;
class MockRenderWidgetHost;
-class OverscrollController;
class RenderWidgetHostDelegate;
-class RenderWidgetHostViewPort;
+class RenderWidgetHostViewBase;
class SyntheticGestureController;
class TimeoutMonitor;
+class TouchEmulator;
+class WebCursor;
struct EditCommand;
// This implements the RenderWidgetHost interface that is exposed to
// embedders of content, and adds things only visible to content.
-class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost,
- public InputRouterClient,
- public InputAckHandler,
- public IPC::Listener {
+class CONTENT_EXPORT RenderWidgetHostImpl
+ : virtual public RenderWidgetHost,
+ public InputRouterClient,
+ public InputAckHandler,
+ public TouchEmulatorClient,
+ public IPC::Listener,
+ public BrowserAccessibilityDelegate {
public:
// routing_id can be MSG_ROUTING_NONE, in which case the next available
// routing id is taken from the RenderProcessHost.
@@ -121,16 +127,6 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost,
}
// RenderWidgetHost implementation.
- virtual void Undo() OVERRIDE;
- virtual void Redo() OVERRIDE;
- virtual void Cut() OVERRIDE;
- virtual void Copy() OVERRIDE;
- virtual void CopyToFindPboard() OVERRIDE;
- virtual void Paste() OVERRIDE;
- virtual void PasteAndMatchStyle() OVERRIDE;
- virtual void Delete() OVERRIDE;
- virtual void SelectAll() OVERRIDE;
- virtual void Unselect() OVERRIDE;
virtual void UpdateTextDirection(blink::WebTextDirection direction) OVERRIDE;
virtual void NotifyTextDirection() OVERRIDE;
virtual void Focus() OVERRIDE;
@@ -139,16 +135,17 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost,
virtual void CopyFromBackingStore(
const gfx::Rect& src_rect,
const gfx::Size& accelerated_dst_size,
- const base::Callback<void(bool, const SkBitmap&)>& callback) OVERRIDE;
-#if defined(TOOLKIT_GTK)
- virtual bool CopyFromBackingStoreToGtkWindow(const gfx::Rect& dest_rect,
- GdkWindow* target) OVERRIDE;
-#elif defined(OS_MACOSX)
- virtual gfx::Size GetBackingStoreSize() OVERRIDE;
- virtual bool CopyFromBackingStoreToCGContext(const CGRect& dest_rect,
- CGContextRef target) OVERRIDE;
+ const base::Callback<void(bool, const SkBitmap&)>& callback,
+ const SkBitmap::Config& bitmap_config) OVERRIDE;
+ virtual bool CanCopyFromBackingStore() OVERRIDE;
+#if defined(OS_ANDROID)
+ virtual void LockBackingStore() OVERRIDE;
+ virtual void UnlockBackingStore() OVERRIDE;
#endif
virtual void EnableFullAccessibilityMode() OVERRIDE;
+ virtual bool IsFullAccessibilityModeForTesting() OVERRIDE;
+ virtual void EnableTreeOnlyAccessibilityMode() OVERRIDE;
+ virtual bool IsTreeOnlyAccessibilityModeForTesting() OVERRIDE;
virtual void ForwardMouseEvent(
const blink::WebMouseEvent& mouse_event) OVERRIDE;
virtual void ForwardWheelEvent(
@@ -161,12 +158,6 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost,
virtual RenderWidgetHostView* GetView() const OVERRIDE;
virtual bool IsLoading() const OVERRIDE;
virtual bool IsRenderView() const OVERRIDE;
- virtual void PaintAtSize(TransportDIB::Handle dib_handle,
- int tag,
- const gfx::Size& page_size,
- const gfx::Size& desired_size) OVERRIDE;
- virtual void Replace(const base::string16& word) OVERRIDE;
- virtual void ReplaceMisspelling(const base::string16& word) OVERRIDE;
virtual void ResizeRectChanged(const gfx::Rect& new_rect) OVERRIDE;
virtual void RestartHangMonitorTimeout() OVERRIDE;
virtual void SetIgnoreInputEvents(bool ignore_input_events) OVERRIDE;
@@ -181,9 +172,25 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost,
virtual void RemoveMouseEventCallback(
const MouseEventCallback& callback) OVERRIDE;
virtual void GetWebScreenInfo(blink::WebScreenInfo* result) OVERRIDE;
- virtual void GetSnapshotFromRenderer(
- const gfx::Rect& src_subrect,
- const base::Callback<void(bool, const SkBitmap&)>& callback) OVERRIDE;
+
+ virtual SkBitmap::Config PreferredReadbackFormat() OVERRIDE;
+
+ // BrowserAccessibilityDelegate
+ virtual void AccessibilitySetFocus(int acc_obj_id) OVERRIDE;
+ virtual void AccessibilityDoDefaultAction(int acc_obj_id) OVERRIDE;
+ virtual void AccessibilityShowMenu(int acc_obj_id) OVERRIDE;
+ virtual void AccessibilityScrollToMakeVisible(
+ int acc_obj_id, gfx::Rect subfocus) OVERRIDE;
+ virtual void AccessibilityScrollToPoint(
+ int acc_obj_id, gfx::Point point) OVERRIDE;
+ virtual void AccessibilitySetTextSelection(
+ int acc_obj_id, int start_offset, int end_offset) OVERRIDE;
+ virtual bool AccessibilityViewHasFocus() const OVERRIDE;
+ virtual gfx::Rect AccessibilityGetViewBounds() const OVERRIDE;
+ virtual gfx::Point AccessibilityOriginInScreen(const gfx::Rect& bounds)
+ const OVERRIDE;
+ virtual void AccessibilityHitTest(const gfx::Point& point) OVERRIDE;
+ virtual void AccessibilityFatalError() OVERRIDE;
const NativeWebKeyboardEvent* GetLastKeyboardEvent() const;
@@ -196,7 +203,7 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost,
void InvalidateScreenInfo();
// Sets the View of this RenderWidgetHost.
- void SetView(RenderWidgetHostView* view);
+ void SetView(RenderWidgetHostViewBase* view);
int surface_id() const { return surface_id_; }
@@ -235,42 +242,23 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost,
// Noifies the RenderWidget of the current mouse cursor visibility state.
void SendCursorVisibilityState(bool is_visible);
- // Tells us whether the page is rendered directly via the GPU process.
- bool is_accelerated_compositing_active() {
- return is_accelerated_compositing_active_;
- }
-
// Notifies the RenderWidgetHost that the View was destroyed.
void ViewDestroyed();
// Indicates if the page has finished loading.
void SetIsLoading(bool is_loading);
- // Check for the existance of a BackingStore of the given |desired_size| and
- // return it if it exists. If the BackingStore is GPU, true is returned and
- // |*backing_store| is set to NULL.
- bool TryGetBackingStore(const gfx::Size& desired_size,
- BackingStore** backing_store);
-
- // Get access to the widget's backing store matching the size of the widget's
- // view. If you pass |force_create| as true, then GetBackingStore may block
- // for the renderer to send a new frame. Otherwise, NULL will be returned if
- // the backing store doesn't already exist. It will also return NULL if the
- // backing store could not be created.
- //
- // Mac only: NULL may also be returned if the last frame was GPU accelerated.
- // Call GetView()->HasAcceleratedSurface to determine if the last frame was
- // accelerated.
- BackingStore* GetBackingStore(bool force_create);
+ // Pause for a moment to wait for pending repaint or resize messages sent to
+ // the renderer to arrive. If pending resize messages are for an old window
+ // size, then also pump through a new resize message if there is time.
+ void PauseForPendingResizeOrRepaints();
- // Allocate a new backing store of the given size. Returns NULL on failure
- // (for example, if we don't currently have a RenderWidgetHostView.)
- BackingStore* AllocBackingStore(const gfx::Size& size);
+ // Whether pausing may be useful.
+ bool CanPauseForPendingResizeOrRepaints();
- // When a backing store does asynchronous painting, it will call this function
- // when it is done with the DIB. We will then forward a message to the
- // renderer to send another paint.
- void DonePaintingToBackingStore();
+ // Wait for a surface matching the size of the widget's view, possibly
+ // blocking until the renderer sends a new frame.
+ void WaitForSurface();
// GPU accelerated version of GetBackingStore function. This will
// trigger a re-composite to the view. It may fail if a resize is pending, or
@@ -288,7 +276,6 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost,
// Forwards the given message to the renderer. These are called by the view
// when it has received a message.
- void ForwardGestureEvent(const blink::WebGestureEvent& gesture_event);
void ForwardGestureEventWithLatencyInfo(
const blink::WebGestureEvent& gesture_event,
const ui::LatencyInfo& ui_latency);
@@ -296,9 +283,24 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost,
const blink::WebTouchEvent& touch_event,
const ui::LatencyInfo& ui_latency);
void ForwardMouseEventWithLatencyInfo(
- const MouseEventWithLatencyInfo& mouse_event);
+ const blink::WebMouseEvent& mouse_event,
+ const ui::LatencyInfo& ui_latency);
void ForwardWheelEventWithLatencyInfo(
- const MouseWheelEventWithLatencyInfo& wheel_event);
+ const blink::WebMouseWheelEvent& wheel_event,
+ const ui::LatencyInfo& ui_latency);
+
+ // TouchEmulatorClient overrides.
+ virtual void ForwardGestureEvent(
+ const blink::WebGestureEvent& gesture_event) OVERRIDE;
+ virtual void ForwardTouchEvent(
+ const blink::WebTouchEvent& touch_event) OVERRIDE;
+ virtual void SetCursor(const WebCursor& cursor) OVERRIDE;
+
+ // Queues a synthetic gesture for testing purposes. Invokes the on_complete
+ // callback when the gesture is finished running.
+ void QueueSyntheticGesture(
+ scoped_ptr<SyntheticGesture> synthetic_gesture,
+ const base::Callback<void(SyntheticGesture::Result)>& on_complete);
void CancelUpdateTextDirection();
@@ -352,10 +354,6 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost,
// Cancels an ongoing composition.
void ImeCancelComposition();
- // Deletes the current selection plus the specified number of characters
- // before and after the selection or caret.
- void ExtendSelectionAndDelete(size_t before, size_t after);
-
// This is for derived classes to give us access to the resizer rect.
// And to also expose it to the RenderWidgetHostView.
virtual gfx::Rect GetRootWindowResizerRect() const;
@@ -381,8 +379,8 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost,
// perform an action. See OnUserGesture for more details.
void StartUserGesture();
- // Set the RenderView background.
- void SetBackground(const SkBitmap& background);
+ // Set the RenderView background transparency.
+ void SetBackgroundOpaque(bool opaque);
// Notifies the renderer that the next key event is bound to one or more
// pre-defined edit commands
@@ -394,37 +392,20 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost,
return accessibility_mode_;
}
- // Send a message to the renderer process to change the accessibility mode.
- void SetAccessibilityMode(AccessibilityMode mode);
-
- // Relay a request from assistive technology to perform the default action
- // on a given node.
- void AccessibilityDoDefaultAction(int object_id);
-
- // Relay a request from assistive technology to set focus to a given node.
- void AccessibilitySetFocus(int object_id);
+ // Adds the given accessibility mode to the current accessibility mode bitmap.
+ void AddAccessibilityMode(AccessibilityMode mode);
- // Relay a request from assistive technology to make a given object
- // visible by scrolling as many scrollable containers as necessary.
- // In addition, if it's not possible to make the entire object visible,
- // scroll so that the |subfocus| rect is visible at least. The subfocus
- // rect is in local coordinates of the object itself.
- void AccessibilityScrollToMakeVisible(
- int acc_obj_id, gfx::Rect subfocus);
+ // Removes the given accessibility mode from the current accessibility mode
+ // bitmap, managing the bits that are shared with other modes such that a
+ // bit will only be turned off when all modes that depend on it have been
+ // removed.
+ void RemoveAccessibilityMode(AccessibilityMode mode);
- // Relay a request from assistive technology to move a given object
- // to a specific location, in the WebContents area coordinate space, i.e.
- // (0, 0) is the top-left corner of the WebContents.
- void AccessibilityScrollToPoint(int acc_obj_id, gfx::Point point);
+ // Resets the accessibility mode to the default setting in
+ // BrowserStateAccessibilityImpl.
+ void ResetAccessibilityMode();
- // Relay a request from assistive technology to set text selection.
- void AccessibilitySetTextSelection(
- int acc_obj_id, int start_offset, int end_offset);
-
- // Kill the renderer because we got a fatal accessibility error.
- void FatalAccessibilityTreeError();
-
-#if defined(OS_WIN) && defined(USE_AURA)
+#if defined(OS_WIN)
void SetParentNativeViewAccessible(
gfx::NativeViewAccessible accessible_parent);
gfx::NativeViewAccessible GetParentNativeViewAccessible() const;
@@ -439,9 +420,6 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost,
// editable divs).
void ScrollFocusedEditableNodeIntoRect(const gfx::Rect& rect);
- // Requests the renderer to select the region between two points.
- void SelectRange(const gfx::Point& start, const gfx::Point& end);
-
// Requests the renderer to move the caret selection towards the point.
void MoveCaret(const gfx::Point& point);
@@ -477,26 +455,6 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost,
int renderer_host_id,
const cc::CompositorFrameAck& ack);
- // Called by the view in response to AcceleratedSurfaceBuffersSwapped for
- // platforms that support deferred GPU process descheduling. This does
- // nothing if the compositor thread is enabled.
- // TODO(jbates) Once the compositor thread is always on, this can be removed.
- void AcknowledgeSwapBuffersToRenderer();
-
- bool is_threaded_compositing_enabled() const {
- return is_threaded_compositing_enabled_;
- }
-
-#if defined(USE_AURA)
- // Called by the view when the parent changes. If a parent isn't available,
- // NULL is used.
- void ParentChanged(gfx::NativeViewId new_parent);
-#endif
-
- // Signals that the compositing surface was updated, e.g. after a lost context
- // event.
- void CompositingSurfaceUpdated();
-
void set_allow_privileged_mouse_lock(bool allow) {
allow_privileged_mouse_lock_ = allow;
}
@@ -513,13 +471,6 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost,
// Update the renderer's cache of the screen rect of the view and window.
void SendScreenRects();
- OverscrollController* overscroll_controller() const {
- return overscroll_controller_.get();
- }
-
- // Sets whether the overscroll controller should be enabled for this page.
- void SetOverscrollControllerEnabled(bool enabled);
-
// Suppreses future char events until a keydown. See
// suppress_next_char_events_.
void SuppressNextCharEvents();
@@ -542,11 +493,25 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost,
// subsystem.
int64 GetLatencyComponentId();
- static void CompositorFrameDrawn(const ui::LatencyInfo& latency_info);
+ static void CompositorFrameDrawn(
+ const std::vector<ui::LatencyInfo>& latency_info);
// Don't check whether we expected a resize ack during layout tests.
static void DisableResizeAckCheckForTesting();
+ void WindowSnapshotAsyncCallback(
+ int routing_id,
+ int snapshot_id,
+ gfx::Size snapshot_size,
+ scoped_refptr<base::RefCountedBytes> png_data);
+
+ // LatencyComponents generated in the renderer must have component IDs
+ // provided to them by the browser process. This function adds the correct
+ // component ID where necessary.
+ void AddLatencyInfoComponentIds(ui::LatencyInfo* latency_info);
+
+ InputRouter* input_router() { return input_router_.get(); }
+
protected:
virtual RenderWidgetHostImpl* AsRenderWidgetHostImpl() OVERRIDE;
@@ -613,15 +578,12 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost,
int increment_in_flight_event_count() { return ++in_flight_event_count_; }
int decrement_in_flight_event_count() { return --in_flight_event_count_; }
- // Returns whether an overscroll gesture is in progress.
- bool IsInOverscrollGesture() const;
-
// The View associated with the RenderViewHost. The lifetime of this object
// is associated with the lifetime of the Render process. If the Renderer
// crashes, its View is destroyed and this pointer becomes NULL, even though
// render_view_host_ lives on to load another URL (creating a new View while
// doing so).
- RenderWidgetHostViewPort* view_;
+ RenderWidgetHostViewBase* view_;
// true if a renderer has once been valid. We use this flag to display a sad
// tab only when we lose our renderer and not if a paint occurs during
@@ -653,43 +615,45 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost,
void OnRequestMove(const gfx::Rect& pos);
void OnSetTooltipText(const base::string16& tooltip_text,
blink::WebTextDirection text_direction_hint);
- void OnPaintAtSizeAck(int tag, const gfx::Size& size);
#if defined(OS_MACOSX)
void OnCompositorSurfaceBuffersSwapped(
const ViewHostMsg_CompositorSurfaceBuffersSwapped_Params& params);
#endif
bool OnSwapCompositorFrame(const IPC::Message& message);
- void OnOverscrolled(gfx::Vector2dF accumulated_overscroll,
- gfx::Vector2dF current_fling_velocity);
+ void OnFlingingStopped();
void OnUpdateRect(const ViewHostMsg_UpdateRect_Params& params);
- void OnUpdateIsDelayed();
void OnQueueSyntheticGesture(const SyntheticGesturePacket& gesture_packet);
virtual void OnFocus();
virtual void OnBlur();
void OnSetCursor(const WebCursor& cursor);
- void OnTextInputTypeChanged(ui::TextInputType type,
- ui::TextInputMode input_mode,
- bool can_compose_inline);
-#if defined(OS_MACOSX) || defined(OS_WIN) || defined(USE_AURA)
+ void OnSetTouchEventEmulationEnabled(bool enabled, bool allow_pinch);
+ void OnTextInputStateChanged(
+ const ViewHostMsg_TextInputState_Params& params);
+
+#if defined(OS_MACOSX) || defined(USE_AURA)
void OnImeCompositionRangeChanged(
const gfx::Range& range,
const std::vector<gfx::Rect>& character_bounds);
#endif
void OnImeCancelComposition();
- void OnDidActivateAcceleratedCompositing(bool activated);
void OnLockMouse(bool user_gesture,
bool last_unlocked_by_target,
bool privileged);
void OnUnlockMouse();
void OnShowDisambiguationPopup(const gfx::Rect& rect,
const gfx::Size& size,
- const TransportDIB::Id& id);
+ const cc::SharedBitmapId& id);
#if defined(OS_WIN)
void OnWindowlessPluginDummyWindowCreated(
gfx::NativeViewId dummy_activation_window);
void OnWindowlessPluginDummyWindowDestroyed(
gfx::NativeViewId dummy_activation_window);
#endif
+ void OnSelectionChanged(const base::string16& text,
+ size_t offset,
+ const gfx::Range& range);
+ void OnSelectionBoundsChanged(
+ const ViewHostMsg_SelectionBounds_Params& params);
void OnSnapshot(bool success, const SkBitmap& bitmap);
// Called (either immediately or asynchronously) after we're done with our
@@ -698,24 +662,6 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost,
void DidUpdateBackingStore(const ViewHostMsg_UpdateRect_Params& params,
const base::TimeTicks& paint_start);
- // Paints the given bitmap to the current backing store at the given
- // location. Returns true if the passed callback was asynchronously
- // scheduled in the future (and thus the caller must manually synchronously
- // call the callback function).
- bool PaintBackingStoreRect(TransportDIB::Id bitmap,
- const gfx::Rect& bitmap_rect,
- const std::vector<gfx::Rect>& copy_rects,
- const gfx::Size& view_size,
- float scale_factor,
- const base::Closure& completion_callback);
-
- // Scrolls the given |clip_rect| in the backing by the given dx/dy amount. The
- // |dib| and its corresponding location |bitmap_rect| in the backing store
- // is the newly painted pixels by the renderer.
- void ScrollBackingStoreRect(const gfx::Vector2d& delta,
- const gfx::Rect& clip_rect,
- const gfx::Size& view_size);
-
// Give key press listeners a chance to handle this key press. This allow
// widgets that don't have focus to still handle key presses.
bool KeyPressListenersHandleEvent(const NativeWebKeyboardEvent& event);
@@ -727,8 +673,8 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost,
virtual void IncrementInFlightEventCount() OVERRIDE;
virtual void DecrementInFlightEventCount() OVERRIDE;
virtual void OnHasTouchEventHandlers(bool has_handlers) OVERRIDE;
- virtual OverscrollController* GetOverscrollController() const OVERRIDE;
virtual void DidFlush() OVERRIDE;
+ virtual void DidOverscroll(const DidOverscrollParams& params) OVERRIDE;
// InputAckHandler
virtual void OnKeyboardEventAck(const NativeWebKeyboardEvent& event,
@@ -741,12 +687,17 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost,
InputEventAckState ack_result) OVERRIDE;
virtual void OnUnexpectedEventAck(UnexpectedEventAckType type) OVERRIDE;
+ void OnSyntheticGestureCompleted(SyntheticGesture::Result result);
+
// Called when there is a new auto resize (using a post to avoid a stack
// which may get in recursive loops).
void DelayedAutoResized();
void WindowSnapshotReachedScreen(int snapshot_id);
+ // Send a message to the renderer process to change the accessibility mode.
+ void SetAccessibilityMode(AccessibilityMode AccessibilityMode);
+
// Our delegate, which wants to know mainly about keyboard events.
// It will remain non-NULL until DetachDelegate() is called.
RenderWidgetHostDelegate* delegate_;
@@ -771,12 +722,6 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost,
// Indicates whether a page is fullscreen or not.
bool is_fullscreen_;
- // True when a page is rendered directly via the GPU process.
- bool is_accelerated_compositing_active_;
-
- // True if threaded compositing is enabled on this view.
- bool is_threaded_compositing_enabled_;
-
// Set if we are waiting for a repaint ack for the view.
bool repaint_ack_pending_;
@@ -801,6 +746,11 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost,
// the browser, for example by an on-screen-keyboard (in DPI-adjusted pixels).
float overdraw_bottom_height_;
+ // The size of the visible viewport, which may be smaller than the view if the
+ // view is partially occluded (e.g. by a virtual keyboard). The size is in
+ // DPI-adjusted pixels.
+ gfx::Size visible_viewport_size_;
+
// The size we last sent as requested size to the renderer. |current_size_|
// is only updated once the resize message has been ack'd. This on the other
// hand is updated when the resize message is sent. This is very similar to
@@ -850,12 +800,6 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost,
// Flag to detect recursive calls to GetBackingStore().
bool in_get_backing_store_;
- // Flag to trigger the GetBackingStore method to abort early.
- bool abort_get_backing_store_;
-
- // Set when we call DidPaintRect/DidScrollRect on the view.
- bool view_being_painted_;
-
// Used for UMA histogram logging to measure the time for a repaint view
// operation to finish.
base::TimeTicks repaint_start_time_;
@@ -905,24 +849,19 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost,
scoped_ptr<SyntheticGestureController> synthetic_gesture_controller_;
+ scoped_ptr<TouchEmulator> touch_emulator_;
+
// Receives and handles all input events.
scoped_ptr<InputRouter> input_router_;
- scoped_ptr<OverscrollController> overscroll_controller_;
-
scoped_ptr<TimeoutMonitor> hang_monitor_timeout_;
#if defined(OS_WIN)
std::list<HWND> dummy_windows_for_activation_;
#endif
- // List of callbacks for pending snapshot requests to the renderer.
- std::queue<base::Callback<void(bool, const SkBitmap&)> > pending_snapshots_;
-
int64 last_input_number_;
- BrowserRenderingStats rendering_stats_;
-
DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostImpl);
};
diff --git a/chromium/content/browser/renderer_host/render_widget_host_unittest.cc b/chromium/content/browser/renderer_host/render_widget_host_unittest.cc
index 967c8142715..710719c7706 100644
--- a/chromium/content/browser/renderer_host/render_widget_host_unittest.cc
+++ b/chromium/content/browser/renderer_host/render_widget_host_unittest.cc
@@ -4,28 +4,18 @@
#include "base/basictypes.h"
#include "base/bind.h"
+#include "base/command_line.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/shared_memory.h"
#include "base/timer/timer.h"
#include "content/browser/browser_thread_impl.h"
-#include "content/browser/renderer_host/backing_store.h"
-#include "content/browser/renderer_host/input/gesture_event_filter.h"
#include "content/browser/renderer_host/input/input_router_impl.h"
-#include "content/browser/renderer_host/input/tap_suppression_controller.h"
-#include "content/browser/renderer_host/input/tap_suppression_controller_client.h"
-#include "content/browser/renderer_host/input/touch_event_queue.h"
-#include "content/browser/renderer_host/overscroll_controller.h"
-#include "content/browser/renderer_host/overscroll_controller_delegate.h"
#include "content/browser/renderer_host/render_widget_host_delegate.h"
+#include "content/browser/renderer_host/render_widget_host_view_base.h"
#include "content/common/input/synthetic_web_input_event_builders.h"
#include "content/common/input_messages.h"
#include "content/common/view_messages.h"
-#include "content/port/browser/render_widget_host_view_port.h"
-#include "content/public/browser/notification_details.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
-#include "content/public/browser/notification_source.h"
-#include "content/public/browser/notification_types.h"
+#include "content/public/common/content_switches.h"
#include "content/public/test/mock_render_process_host.h"
#include "content/public/test/test_browser_context.h"
#include "content/test/test_render_view_host.h"
@@ -34,85 +24,32 @@
#include "ui/gfx/canvas.h"
#include "ui/gfx/screen.h"
+#if defined(OS_ANDROID)
+#include "content/browser/renderer_host/render_widget_host_view_android.h"
+#endif
+
#if defined(USE_AURA)
+#include "content/browser/compositor/image_transport_factory.h"
#include "content/browser/renderer_host/render_widget_host_view_aura.h"
+#include "content/browser/renderer_host/ui_events_helper.h"
#include "ui/aura/env.h"
#include "ui/aura/test/test_screen.h"
-#endif
-
-#if defined(OS_WIN) || defined(USE_AURA)
-#include "content/browser/renderer_host/ui_events_helper.h"
+#include "ui/compositor/test/in_process_context_factory.h"
#include "ui/events/event.h"
#endif
using base::TimeDelta;
+using blink::WebGestureDevice;
using blink::WebGestureEvent;
using blink::WebInputEvent;
using blink::WebKeyboardEvent;
+using blink::WebMouseEvent;
using blink::WebMouseWheelEvent;
using blink::WebTouchEvent;
using blink::WebTouchPoint;
namespace content {
-// TestOverscrollDelegate ------------------------------------------------------
-
-class TestOverscrollDelegate : public OverscrollControllerDelegate {
- public:
- explicit TestOverscrollDelegate(RenderWidgetHostView* view)
- : view_(view),
- current_mode_(OVERSCROLL_NONE),
- completed_mode_(OVERSCROLL_NONE),
- delta_x_(0.f),
- delta_y_(0.f) {
- }
-
- virtual ~TestOverscrollDelegate() {}
-
- OverscrollMode current_mode() const { return current_mode_; }
- OverscrollMode completed_mode() const { return completed_mode_; }
- float delta_x() const { return delta_x_; }
- float delta_y() const { return delta_y_; }
-
- void Reset() {
- current_mode_ = OVERSCROLL_NONE;
- completed_mode_ = OVERSCROLL_NONE;
- delta_x_ = delta_y_ = 0.f;
- }
-
- private:
- // Overridden from OverscrollControllerDelegate:
- virtual gfx::Rect GetVisibleBounds() const OVERRIDE {
- return view_->IsShowing() ? view_->GetViewBounds() : gfx::Rect();
- }
-
- virtual void OnOverscrollUpdate(float delta_x, float delta_y) OVERRIDE {
- delta_x_ = delta_x;
- delta_y_ = delta_y;
- }
-
- virtual void OnOverscrollComplete(OverscrollMode overscroll_mode) OVERRIDE {
- EXPECT_EQ(current_mode_, overscroll_mode);
- completed_mode_ = overscroll_mode;
- current_mode_ = OVERSCROLL_NONE;
- }
-
- virtual void OnOverscrollModeChange(OverscrollMode old_mode,
- OverscrollMode new_mode) OVERRIDE {
- EXPECT_EQ(current_mode_, old_mode);
- current_mode_ = new_mode;
- delta_x_ = delta_y_ = 0.f;
- }
-
- RenderWidgetHostView* view_;
- OverscrollMode current_mode_;
- OverscrollMode completed_mode_;
- float delta_x_;
- float delta_y_;
-
- DISALLOW_COPY_AND_ASSIGN(TestOverscrollDelegate);
-};
-
// MockInputRouter -------------------------------------------------------------
class MockInputRouter : public InputRouter {
@@ -167,6 +104,7 @@ class MockInputRouter : public InputRouter {
}
virtual bool ShouldForwardTouchEvent() const OVERRIDE { return true; }
virtual void OnViewUpdated(int view_flags) OVERRIDE {}
+ virtual bool HasPendingEvents() const OVERRIDE { return false; }
// IPC::Listener
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
@@ -199,11 +137,10 @@ class MockRenderWidgetHost : public RenderWidgetHostImpl {
int routing_id)
: RenderWidgetHostImpl(delegate, process, routing_id, false),
unresponsive_timer_fired_(false) {
- input_router_impl_ = static_cast<InputRouterImpl*>(input_router_.get());
+ acked_touch_event_type_ = blink::WebInputEvent::Undefined;
}
// Allow poking at a few private members.
- using RenderWidgetHostImpl::OnPaintAtSizeAck;
using RenderWidgetHostImpl::OnUpdateRect;
using RenderWidgetHostImpl::RendererExited;
using RenderWidgetHostImpl::last_requested_size_;
@@ -211,6 +148,14 @@ class MockRenderWidgetHost : public RenderWidgetHostImpl {
using RenderWidgetHostImpl::resize_ack_pending_;
using RenderWidgetHostImpl::input_router_;
+ virtual void OnTouchEventAck(
+ const TouchEventWithLatencyInfo& event,
+ InputEventAckState ack_result) OVERRIDE {
+ // Sniff touch acks.
+ acked_touch_event_type_ = event.event.type;
+ RenderWidgetHostImpl::OnTouchEventAck(event, ack_result);
+ }
+
bool unresponsive_timer_fired() const {
return unresponsive_timer_fired_;
}
@@ -219,97 +164,21 @@ class MockRenderWidgetHost : public RenderWidgetHostImpl {
hung_renderer_delay_ms_ = delay_ms;
}
- unsigned GestureEventLastQueueEventSize() const {
- return gesture_event_filter()->coalesced_gesture_events_.size();
- }
-
- WebGestureEvent GestureEventSecondFromLastQueueEvent() const {
- return gesture_event_filter()->coalesced_gesture_events_.at(
- GestureEventLastQueueEventSize() - 2).event;
- }
-
- WebGestureEvent GestureEventLastQueueEvent() const {
- return gesture_event_filter()->coalesced_gesture_events_.back().event;
- }
-
- unsigned GestureEventDebouncingQueueSize() const {
- return gesture_event_filter()->debouncing_deferral_queue_.size();
- }
-
- WebGestureEvent GestureEventQueueEventAt(int i) const {
- return gesture_event_filter()->coalesced_gesture_events_.at(i).event;
- }
-
- bool ScrollingInProgress() const {
- return gesture_event_filter()->scrolling_in_progress_;
- }
-
- bool FlingInProgress() const {
- return gesture_event_filter()->fling_in_progress_;
- }
-
- bool WillIgnoreNextACK() const {
- return gesture_event_filter()->ignore_next_ack_;
- }
-
- void SetupForOverscrollControllerTest() {
- SetOverscrollControllerEnabled(true);
- overscroll_delegate_.reset(new TestOverscrollDelegate(GetView()));
- overscroll_controller_->set_delegate(overscroll_delegate_.get());
- }
-
void DisableGestureDebounce() {
- gesture_event_filter()->set_debounce_enabled_for_testing(false);
- }
-
- void set_debounce_interval_time_ms(int delay_ms) {
- gesture_event_filter()->
- set_debounce_interval_time_ms_for_testing(delay_ms);
- }
-
- bool TouchEventQueueEmpty() const {
- return touch_event_queue()->empty();
- }
-
- bool ScrollStateIsContentScrolling() const {
- return scroll_state() == OverscrollController::STATE_CONTENT_SCROLLING;
- }
-
- bool ScrollStateIsOverscrolling() const {
- return scroll_state() == OverscrollController::STATE_OVERSCROLLING;
+ input_router_.reset(new InputRouterImpl(
+ process_, this, this, routing_id_, InputRouterImpl::Config()));
}
- bool ScrollStateIsUnknown() const {
- return scroll_state() == OverscrollController::STATE_UNKNOWN;
- }
-
- OverscrollController::ScrollState scroll_state() const {
- return overscroll_controller_->scroll_state_;
- }
-
- OverscrollMode overscroll_mode() const {
- return overscroll_controller_->overscroll_mode_;
- }
-
- float overscroll_delta_x() const {
- return overscroll_controller_->overscroll_delta_x_;
- }
-
- float overscroll_delta_y() const {
- return overscroll_controller_->overscroll_delta_y_;
- }
-
- TestOverscrollDelegate* overscroll_delegate() {
- return overscroll_delegate_.get();
+ WebInputEvent::Type acked_touch_event_type() const {
+ return acked_touch_event_type_;
}
void SetupForInputRouterTest() {
- mock_input_router_ = new MockInputRouter(this);
- input_router_.reset(mock_input_router_);
+ input_router_.reset(new MockInputRouter(this));
}
MockInputRouter* mock_input_router() {
- return mock_input_router_;
+ return static_cast<MockInputRouter*>(input_router_.get());
}
protected:
@@ -317,28 +186,8 @@ class MockRenderWidgetHost : public RenderWidgetHostImpl {
unresponsive_timer_fired_ = true;
}
- const TouchEventQueue* touch_event_queue() const {
- return input_router_impl_->touch_event_queue_.get();
- }
-
- const GestureEventFilter* gesture_event_filter() const {
- return input_router_impl_->gesture_event_filter_.get();
- }
-
- GestureEventFilter* gesture_event_filter() {
- return input_router_impl_->gesture_event_filter_.get();
- }
-
- private:
bool unresponsive_timer_fired_;
-
- // |input_router_impl_| and |mock_input_router_| are owned by
- // RenderWidgetHostImpl. The handles below are provided for convenience so
- // that we don't have to reinterpret_cast it all the time.
- InputRouterImpl* input_router_impl_;
- MockInputRouter* mock_input_router_;
-
- scoped_ptr<TestOverscrollDelegate> overscroll_delegate_;
+ WebInputEvent::Type acked_touch_event_type_;
DISALLOW_COPY_AND_ASSIGN(MockRenderWidgetHost);
};
@@ -351,12 +200,10 @@ class RenderWidgetHostProcess : public MockRenderProcessHost {
public:
explicit RenderWidgetHostProcess(BrowserContext* browser_context)
: MockRenderProcessHost(browser_context),
- current_update_buf_(NULL),
update_msg_should_reply_(false),
update_msg_reply_flags_(0) {
}
virtual ~RenderWidgetHostProcess() {
- delete current_update_buf_;
}
void set_update_msg_should_reply(bool reply) {
@@ -376,8 +223,6 @@ class RenderWidgetHostProcess : public MockRenderProcessHost {
const base::TimeDelta& max_delay,
IPC::Message* msg) OVERRIDE;
- TransportDIB* current_update_buf_;
-
// Set to true when WaitForBackingStoreMsg should return a successful update
// message reply. False implies timeout.
bool update_msg_should_reply_;
@@ -391,20 +236,10 @@ class RenderWidgetHostProcess : public MockRenderProcessHost {
void RenderWidgetHostProcess::InitUpdateRectParams(
ViewHostMsg_UpdateRect_Params* params) {
- // Create the shared backing store.
const int w = 100, h = 100;
- const size_t pixel_size = w * h * 4;
-
- if (!current_update_buf_)
- current_update_buf_ = TransportDIB::Create(pixel_size, 0);
- params->bitmap = current_update_buf_->id();
- params->bitmap_rect = gfx::Rect(0, 0, w, h);
- params->scroll_delta = gfx::Vector2d();
- params->copy_rects.push_back(params->bitmap_rect);
+
params->view_size = gfx::Size(w, h);
params->flags = update_msg_reply_flags_;
- params->needs_ack = true;
- params->scale_factor = 1;
}
bool RenderWidgetHostProcess::WaitForBackingStoreMsg(
@@ -431,6 +266,7 @@ class TestView : public TestRenderWidgetHostView {
public:
explicit TestView(RenderWidgetHostImpl* rwh)
: TestRenderWidgetHostView(rwh),
+ unhandled_wheel_event_count_(0),
acked_event_count_(0),
gesture_event_type_(-1),
use_fake_physical_backing_size_(false),
@@ -452,6 +288,9 @@ class TestView : public TestRenderWidgetHostView {
const WebMouseWheelEvent& unhandled_wheel_event() const {
return unhandled_wheel_event_;
}
+ int unhandled_wheel_event_count() const {
+ return unhandled_wheel_event_count_;
+ }
int gesture_event_type() const { return gesture_event_type_; }
InputEventAckState ack_result() const { return ack_result_; }
@@ -472,12 +311,16 @@ class TestView : public TestRenderWidgetHostView {
acked_event_ = touch.event;
++acked_event_count_;
}
- virtual void UnhandledWheelEvent(const WebMouseWheelEvent& event) OVERRIDE {
+ virtual void WheelEventAck(const WebMouseWheelEvent& event,
+ InputEventAckState ack_result) OVERRIDE {
+ if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED)
+ return;
+ unhandled_wheel_event_count_++;
unhandled_wheel_event_ = event;
}
- virtual void GestureEventAck(int gesture_event_type,
+ virtual void GestureEventAck(const WebGestureEvent& event,
InputEventAckState ack_result) OVERRIDE {
- gesture_event_type_ = gesture_event_type;
+ gesture_event_type_ = event.type;
ack_result_ = ack_result;
}
virtual gfx::Size GetPhysicalBackingSize() const OVERRIDE {
@@ -485,9 +328,21 @@ class TestView : public TestRenderWidgetHostView {
return mock_physical_backing_size_;
return TestRenderWidgetHostView::GetPhysicalBackingSize();
}
+#if defined(USE_AURA)
+ virtual ~TestView() {
+ // Simulate the mouse exit event dispatched when an aura window is
+ // destroyed. (MakeWebMouseEventFromAuraEvent translates ET_MOUSE_EXITED
+ // into WebInputEvent::MouseMove.)
+ rwh_->input_router()->SendMouseEvent(
+ MouseEventWithLatencyInfo(
+ SyntheticWebMouseEventBuilder::Build(WebInputEvent::MouseMove),
+ ui::LatencyInfo()));
+ }
+#endif
protected:
WebMouseWheelEvent unhandled_wheel_event_;
+ int unhandled_wheel_event_count_;
WebTouchEvent acked_event_;
int acked_event_count_;
int gesture_event_type_;
@@ -508,7 +363,9 @@ class MockRenderWidgetHostDelegate : public RenderWidgetHostDelegate {
prehandle_keyboard_event_called_(false),
prehandle_keyboard_event_type_(WebInputEvent::Undefined),
unhandled_keyboard_event_called_(false),
- unhandled_keyboard_event_type_(WebInputEvent::Undefined) {
+ unhandled_keyboard_event_type_(WebInputEvent::Undefined),
+ handle_wheel_event_(false),
+ handle_wheel_event_called_(false) {
}
virtual ~MockRenderWidgetHostDelegate() {}
@@ -534,6 +391,14 @@ class MockRenderWidgetHostDelegate : public RenderWidgetHostDelegate {
prehandle_keyboard_event_ = handle;
}
+ void set_handle_wheel_event(bool handle) {
+ handle_wheel_event_ = handle;
+ }
+
+ bool handle_wheel_event_called() {
+ return handle_wheel_event_called_;
+ }
+
protected:
virtual bool PreHandleKeyboardEvent(const NativeWebKeyboardEvent& event,
bool* is_keyboard_shortcut) OVERRIDE {
@@ -548,6 +413,12 @@ class MockRenderWidgetHostDelegate : public RenderWidgetHostDelegate {
unhandled_keyboard_event_called_ = true;
}
+ virtual bool HandleWheelEvent(
+ const blink::WebMouseWheelEvent& event) OVERRIDE {
+ handle_wheel_event_called_ = true;
+ return handle_wheel_event_;
+ }
+
private:
bool prehandle_keyboard_event_;
bool prehandle_keyboard_event_called_;
@@ -555,41 +426,9 @@ class MockRenderWidgetHostDelegate : public RenderWidgetHostDelegate {
bool unhandled_keyboard_event_called_;
WebInputEvent::Type unhandled_keyboard_event_type_;
-};
-// MockPaintingObserver --------------------------------------------------------
-
-class MockPaintingObserver : public NotificationObserver {
- public:
- void WidgetDidReceivePaintAtSizeAck(RenderWidgetHostImpl* host,
- int tag,
- const gfx::Size& size) {
- host_ = reinterpret_cast<MockRenderWidgetHost*>(host);
- tag_ = tag;
- size_ = size;
- }
-
- virtual void Observe(int type,
- const NotificationSource& source,
- const NotificationDetails& details) OVERRIDE {
- if (type == NOTIFICATION_RENDER_WIDGET_HOST_DID_RECEIVE_PAINT_AT_SIZE_ACK) {
- std::pair<int, gfx::Size>* size_ack_details =
- Details<std::pair<int, gfx::Size> >(details).ptr();
- WidgetDidReceivePaintAtSizeAck(
- RenderWidgetHostImpl::From(Source<RenderWidgetHost>(source).ptr()),
- size_ack_details->first,
- size_ack_details->second);
- }
- }
-
- MockRenderWidgetHost* host() const { return host_; }
- int tag() const { return tag_; }
- gfx::Size size() const { return size_; }
-
- private:
- MockRenderWidgetHost* host_;
- int tag_;
- gfx::Size size_;
+ bool handle_wheel_event_;
+ bool handle_wheel_event_called_;
};
// RenderWidgetHostTest --------------------------------------------------------
@@ -599,7 +438,10 @@ class RenderWidgetHostTest : public testing::Test {
RenderWidgetHostTest()
: process_(NULL),
handle_key_press_event_(false),
- handle_mouse_event_(false) {
+ handle_mouse_event_(false),
+ simulated_event_time_delta_seconds_(0) {
+ last_simulated_event_time_seconds_ =
+ (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF();
}
virtual ~RenderWidgetHostTest() {
}
@@ -614,12 +456,17 @@ class RenderWidgetHostTest : public testing::Test {
protected:
// testing::Test
virtual void SetUp() {
+ CommandLine* command_line = CommandLine::ForCurrentProcess();
+ command_line->AppendSwitch(switches::kValidateInputEventStream);
+
browser_context_.reset(new TestBrowserContext());
delegate_.reset(new MockRenderWidgetHostDelegate());
process_ = new RenderWidgetHostProcess(browser_context_.get());
#if defined(USE_AURA)
- aura::Env::CreateInstance();
- screen_.reset(aura::TestScreen::Create());
+ ImageTransportFactory::InitializeForUnitTests(
+ scoped_ptr<ui::ContextFactory>(new ui::InProcessContextFactory));
+ aura::Env::CreateInstance(true);
+ screen_.reset(aura::TestScreen::Create(gfx::Size()));
gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, screen_.get());
#endif
host_.reset(
@@ -627,6 +474,7 @@ class RenderWidgetHostTest : public testing::Test {
view_.reset(new TestView(host_.get()));
host_->SetView(view_.get());
host_->Init();
+ host_->DisableGestureDebounce();
}
virtual void TearDown() {
view_.reset();
@@ -638,24 +486,39 @@ class RenderWidgetHostTest : public testing::Test {
#if defined(USE_AURA)
aura::Env::DeleteInstance();
screen_.reset();
+ ImageTransportFactory::Terminate();
#endif
// Process all pending tasks to avoid leaks.
base::MessageLoop::current()->RunUntilIdle();
}
+ int64 GetLatencyComponentId() {
+ return host_->GetLatencyComponentId();
+ }
+
void SendInputEventACK(WebInputEvent::Type type,
InputEventAckState ack_result) {
- scoped_ptr<IPC::Message> response(
- new InputHostMsg_HandleInputEvent_ACK(0, type, ack_result,
- ui::LatencyInfo()));
- host_->OnMessageReceived(*response);
+ InputHostMsg_HandleInputEvent_ACK_Params ack;
+ ack.type = type;
+ ack.state = ack_result;
+ host_->OnMessageReceived(InputHostMsg_HandleInputEvent_ACK(0, ack));
+ }
+
+ double GetNextSimulatedEventTimeSeconds() {
+ last_simulated_event_time_seconds_ += simulated_event_time_delta_seconds_;
+ return last_simulated_event_time_seconds_;
}
void SimulateKeyboardEvent(WebInputEvent::Type type) {
- WebKeyboardEvent event = SyntheticWebKeyboardEventBuilder::Build(type);
- NativeWebKeyboardEvent native_event;
- memcpy(&native_event, &event, sizeof(event));
+ SimulateKeyboardEvent(type, 0);
+ }
+
+ void SimulateKeyboardEvent(WebInputEvent::Type type, int modifiers) {
+ WebKeyboardEvent event = SyntheticWebKeyboardEventBuilder::Build(type);
+ event.modifiers = modifiers;
+ NativeWebKeyboardEvent native_event;
+ memcpy(&native_event, &event, sizeof(event));
host_->ForwardKeyboardEvent(native_event);
}
@@ -663,60 +526,55 @@ class RenderWidgetHostTest : public testing::Test {
host_->ForwardMouseEvent(SyntheticWebMouseEventBuilder::Build(type));
}
+ void SimulateMouseEventWithLatencyInfo(WebInputEvent::Type type,
+ const ui::LatencyInfo& ui_latency) {
+ host_->ForwardMouseEventWithLatencyInfo(
+ SyntheticWebMouseEventBuilder::Build(type),
+ ui_latency);
+ }
+
void SimulateWheelEvent(float dX, float dY, int modifiers, bool precise) {
host_->ForwardWheelEvent(
SyntheticWebMouseWheelEventBuilder::Build(dX, dY, modifiers, precise));
}
- void SimulateMouseMove(int x, int y, int modifiers) {
- host_->ForwardMouseEvent(
- SyntheticWebMouseEventBuilder::Build(WebInputEvent::MouseMove,
- x,
- y,
- modifiers));
+ void SimulateWheelEventWithLatencyInfo(float dX,
+ float dY,
+ int modifiers,
+ bool precise,
+ const ui::LatencyInfo& ui_latency) {
+ host_->ForwardWheelEventWithLatencyInfo(
+ SyntheticWebMouseWheelEventBuilder::Build(dX, dY, modifiers, precise),
+ ui_latency);
}
- void SimulateWheelEventWithPhase(WebMouseWheelEvent::Phase phase) {
- host_->ForwardWheelEvent(SyntheticWebMouseWheelEventBuilder::Build(phase));
+ void SimulateMouseMove(int x, int y, int modifiers) {
+ SimulateMouseEvent(WebInputEvent::MouseMove, x, y, modifiers, false);
}
- // Inject provided synthetic WebGestureEvent instance.
- void SimulateGestureEventCore(const WebGestureEvent& gesture_event) {
- host_->ForwardGestureEvent(gesture_event);
+ void SimulateMouseEvent(
+ WebInputEvent::Type type, int x, int y, int modifiers, bool pressed) {
+ WebMouseEvent event =
+ SyntheticWebMouseEventBuilder::Build(type, x, y, modifiers);
+ if (pressed)
+ event.button = WebMouseEvent::ButtonLeft;
+ event.timeStampSeconds = GetNextSimulatedEventTimeSeconds();
+ host_->ForwardMouseEvent(event);
}
// Inject simple synthetic WebGestureEvent instances.
void SimulateGestureEvent(WebInputEvent::Type type,
- WebGestureEvent::SourceDevice sourceDevice) {
- SimulateGestureEventCore(
+ WebGestureDevice sourceDevice) {
+ host_->ForwardGestureEvent(
SyntheticWebGestureEventBuilder::Build(type, sourceDevice));
}
- void SimulateGestureScrollUpdateEvent(float dX, float dY, int modifiers) {
- SimulateGestureEventCore(
- SyntheticWebGestureEventBuilder::BuildScrollUpdate(dX, dY, modifiers));
- }
-
- void SimulateGesturePinchUpdateEvent(float scale,
- float anchorX,
- float anchorY,
- int modifiers) {
- SimulateGestureEventCore(
- SyntheticWebGestureEventBuilder::BuildPinchUpdate(scale,
- anchorX,
- anchorY,
- modifiers));
- }
-
- // Inject synthetic GestureFlingStart events.
- void SimulateGestureFlingStartEvent(
- float velocityX,
- float velocityY,
- WebGestureEvent::SourceDevice sourceDevice) {
- SimulateGestureEventCore(
- SyntheticWebGestureEventBuilder::BuildFling(velocityX,
- velocityY,
- sourceDevice));
+ void SimulateGestureEventWithLatencyInfo(WebInputEvent::Type type,
+ WebGestureDevice sourceDevice,
+ const ui::LatencyInfo& ui_latency) {
+ host_->ForwardGestureEventWithLatencyInfo(
+ SyntheticWebGestureEventBuilder::Build(type, sourceDevice),
+ ui_latency);
}
// Set the timestamp for the touch-event.
@@ -763,6 +621,8 @@ class RenderWidgetHostTest : public testing::Test {
scoped_ptr<gfx::Screen> screen_;
bool handle_key_press_event_;
bool handle_mouse_event_;
+ double last_simulated_event_time_seconds_;
+ double simulated_event_time_delta_seconds_;
private:
SyntheticWebTouchEvent touch_event_;
@@ -776,8 +636,7 @@ class RenderWidgetHostTest : public testing::Test {
// This is for tests that are to be run for all source devices.
class RenderWidgetHostWithSourceTest
: public RenderWidgetHostTest,
- public testing::WithParamInterface<WebGestureEvent::SourceDevice> {
-};
+ public testing::WithParamInterface<WebGestureDevice> {};
#endif // GTEST_HAS_PARAM_TEST
} // namespace
@@ -914,134 +773,39 @@ TEST_F(RenderWidgetHostTest, ResizeThenCrash) {
host_->SetView(view_.get());
}
-// Tests setting custom background
-TEST_F(RenderWidgetHostTest, Background) {
+// Unable to include render_widget_host_view_mac.h and compile.
#if !defined(OS_MACOSX)
- scoped_ptr<RenderWidgetHostView> view(
- RenderWidgetHostView::CreateViewForWidget(host_.get()));
-#if defined(OS_LINUX) || defined(USE_AURA)
+// Tests setting background transparency.
+TEST_F(RenderWidgetHostTest, Background) {
+ scoped_ptr<RenderWidgetHostViewBase> view;
+#if defined(USE_AURA)
+ view.reset(new RenderWidgetHostViewAura(host_.get()));
// TODO(derat): Call this on all platforms: http://crbug.com/102450.
- // InitAsChild doesn't seem to work if NULL parent is passed on Windows,
- // which leads to DCHECK failure in RenderWidgetHostView::Destroy.
- // When you enable this for OS_WIN, enable |view.release()->Destroy()|
- // below.
view->InitAsChild(NULL);
+#elif defined(OS_ANDROID)
+ view.reset(new RenderWidgetHostViewAndroid(host_.get(), NULL));
#endif
host_->SetView(view.get());
- // Create a checkerboard background to test with.
- gfx::Canvas canvas(gfx::Size(4, 4), 1.0f, true);
- canvas.FillRect(gfx::Rect(0, 0, 2, 2), SK_ColorBLACK);
- canvas.FillRect(gfx::Rect(2, 0, 2, 2), SK_ColorWHITE);
- canvas.FillRect(gfx::Rect(0, 2, 2, 2), SK_ColorWHITE);
- canvas.FillRect(gfx::Rect(2, 2, 2, 2), SK_ColorBLACK);
- const SkBitmap& background =
- canvas.sk_canvas()->getDevice()->accessBitmap(false);
-
- // Set the background and make sure we get back a copy.
- view->SetBackground(background);
- EXPECT_EQ(4, view->GetBackground().width());
- EXPECT_EQ(4, view->GetBackground().height());
- EXPECT_EQ(background.getSize(), view->GetBackground().getSize());
- background.lockPixels();
- view->GetBackground().lockPixels();
- EXPECT_TRUE(0 == memcmp(background.getPixels(),
- view->GetBackground().getPixels(),
- background.getSize()));
- view->GetBackground().unlockPixels();
- background.unlockPixels();
+ EXPECT_TRUE(view->GetBackgroundOpaque());
+ view->SetBackgroundOpaque(false);
+ EXPECT_FALSE(view->GetBackgroundOpaque());
const IPC::Message* set_background =
- process_->sink().GetUniqueMessageMatching(ViewMsg_SetBackground::ID);
+ process_->sink().GetUniqueMessageMatching(
+ ViewMsg_SetBackgroundOpaque::ID);
ASSERT_TRUE(set_background);
- Tuple1<SkBitmap> sent_background;
- ViewMsg_SetBackground::Read(set_background, &sent_background);
- EXPECT_EQ(background.getSize(), sent_background.a.getSize());
- background.lockPixels();
- sent_background.a.lockPixels();
- EXPECT_TRUE(0 == memcmp(background.getPixels(),
- sent_background.a.getPixels(),
- background.getSize()));
- sent_background.a.unlockPixels();
- background.unlockPixels();
-
-#if defined(OS_LINUX) || defined(USE_AURA)
+ Tuple1<bool> sent_background;
+ ViewMsg_SetBackgroundOpaque::Read(set_background, &sent_background);
+ EXPECT_FALSE(sent_background.a);
+
+#if defined(USE_AURA)
// See the comment above |InitAsChild(NULL)|.
host_->SetView(NULL);
- static_cast<RenderWidgetHostViewPort*>(view.release())->Destroy();
-#endif
-
-#else
- // TODO(port): Mac does not have gfx::Canvas. Maybe we can just change this
- // test to use SkCanvas directly?
+ static_cast<RenderWidgetHostViewBase*>(view.release())->Destroy();
#endif
-
- // TODO(aa): It would be nice to factor out the painting logic so that we
- // could test that, but it appears that would mean painting everything twice
- // since windows HDC structures are opaque.
-}
-
-// Tests getting the backing store with the renderer not setting repaint ack
-// flags.
-TEST_F(RenderWidgetHostTest, GetBackingStore_NoRepaintAck) {
- // First set the view size to match what the renderer is rendering.
- ViewHostMsg_UpdateRect_Params params;
- process_->InitUpdateRectParams(&params);
- view_->set_bounds(gfx::Rect(params.view_size));
-
- // We don't currently have a backing store, and if the renderer doesn't send
- // one in time, we should get nothing.
- process_->set_update_msg_should_reply(false);
- BackingStore* backing = host_->GetBackingStore(true);
- EXPECT_FALSE(backing);
- // The widget host should have sent a request for a repaint, and there should
- // be no paint ACK.
- EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_Repaint::ID));
- EXPECT_FALSE(process_->sink().GetUniqueMessageMatching(
- ViewMsg_UpdateRect_ACK::ID));
-
- // Allowing the renderer to reply in time should give is a backing store.
- process_->sink().ClearMessages();
- process_->set_update_msg_should_reply(true);
- process_->set_update_msg_reply_flags(0);
- backing = host_->GetBackingStore(true);
- EXPECT_TRUE(backing);
- // The widget host should NOT have sent a request for a repaint, since there
- // was an ACK already pending.
- EXPECT_FALSE(process_->sink().GetUniqueMessageMatching(ViewMsg_Repaint::ID));
- EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
- ViewMsg_UpdateRect_ACK::ID));
-}
-
-// Tests getting the backing store with the renderer sending a repaint ack.
-TEST_F(RenderWidgetHostTest, GetBackingStore_RepaintAck) {
- // First set the view size to match what the renderer is rendering.
- ViewHostMsg_UpdateRect_Params params;
- process_->InitUpdateRectParams(&params);
- view_->set_bounds(gfx::Rect(params.view_size));
-
- // Doing a request request with the update message allowed should work and
- // the repaint ack should work.
- process_->set_update_msg_should_reply(true);
- process_->set_update_msg_reply_flags(
- ViewHostMsg_UpdateRect_Flags::IS_REPAINT_ACK);
- BackingStore* backing = host_->GetBackingStore(true);
- EXPECT_TRUE(backing);
- // We still should not have sent out a repaint request since the last flags
- // didn't have the repaint ack set, and the pending flag will still be set.
- EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_Repaint::ID));
- EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
- ViewMsg_UpdateRect_ACK::ID));
-
- // Asking again for the backing store should just re-use the existing one
- // and not send any messagse.
- process_->sink().ClearMessages();
- backing = host_->GetBackingStore(true);
- EXPECT_TRUE(backing);
- EXPECT_FALSE(process_->sink().GetUniqueMessageMatching(ViewMsg_Repaint::ID));
- EXPECT_FALSE(process_->sink().GetUniqueMessageMatching(
- ViewMsg_UpdateRect_ACK::ID));
}
+#endif
// Test that we don't paint when we're hidden, but we still send the ACK. Most
// of the rest of the painting is tested in the GetBackingStore* ones.
@@ -1059,10 +823,6 @@ TEST_F(RenderWidgetHostTest, HiddenPaint) {
process_->InitUpdateRectParams(&params);
host_->OnUpdateRect(params);
- // It should have sent out the ACK.
- EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
- ViewMsg_UpdateRect_ACK::ID));
-
// Now unhide.
process_->sink().ClearMessages();
host_->WasShown();
@@ -1077,27 +837,6 @@ TEST_F(RenderWidgetHostTest, HiddenPaint) {
EXPECT_TRUE(needs_repaint.a);
}
-TEST_F(RenderWidgetHostTest, PaintAtSize) {
- const int kPaintAtSizeTag = 42;
- host_->PaintAtSize(TransportDIB::GetFakeHandleForTest(), kPaintAtSizeTag,
- gfx::Size(40, 60), gfx::Size(20, 30));
- EXPECT_TRUE(
- process_->sink().GetUniqueMessageMatching(ViewMsg_PaintAtSize::ID));
-
- NotificationRegistrar registrar;
- MockPaintingObserver observer;
- registrar.Add(
- &observer,
- NOTIFICATION_RENDER_WIDGET_HOST_DID_RECEIVE_PAINT_AT_SIZE_ACK,
- Source<RenderWidgetHost>(host_.get()));
-
- host_->OnPaintAtSizeAck(kPaintAtSizeTag, gfx::Size(20, 30));
- EXPECT_EQ(host_.get(), observer.host());
- EXPECT_EQ(kPaintAtSizeTag, observer.tag());
- EXPECT_EQ(20, observer.size().width());
- EXPECT_EQ(30, observer.size().height());
-}
-
TEST_F(RenderWidgetHostTest, IgnoreKeyEventsHandledByRenderer) {
// Simulate a keyboard event.
SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
@@ -1166,12 +905,36 @@ TEST_F(RenderWidgetHostTest, UnhandledWheelEvent) {
// Send the simulated response from the renderer back.
SendInputEventACK(WebInputEvent::MouseWheel,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_TRUE(delegate_->handle_wheel_event_called());
+ EXPECT_EQ(1, view_->unhandled_wheel_event_count());
EXPECT_EQ(-5, view_->unhandled_wheel_event().deltaX);
}
+TEST_F(RenderWidgetHostTest, HandleWheelEvent) {
+ // Indicate that we're going to handle this wheel event
+ delegate_->set_handle_wheel_event(true);
+
+ SimulateWheelEvent(-5, 0, 0, true);
+
+ // Make sure we sent the input event to the renderer.
+ EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
+ InputMsg_HandleInputEvent::ID));
+ process_->sink().ClearMessages();
+
+ // Send the simulated response from the renderer back.
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+
+ // ensure the wheel event handler was invoked
+ EXPECT_TRUE(delegate_->handle_wheel_event_called());
+
+ // and that it suppressed the unhandled wheel event handler.
+ EXPECT_EQ(0, view_->unhandled_wheel_event_count());
+}
+
TEST_F(RenderWidgetHostTest, UnhandledGestureEvent) {
- SimulateGestureEvent(WebInputEvent::GestureScrollUpdate,
- WebGestureEvent::Touchscreen);
+ SimulateGestureEvent(WebInputEvent::GestureTwoFingerTap,
+ blink::WebGestureDeviceTouchscreen);
// Make sure we sent the input event to the renderer.
EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
@@ -1179,9 +942,9 @@ TEST_F(RenderWidgetHostTest, UnhandledGestureEvent) {
process_->sink().ClearMessages();
// Send the simulated response from the renderer back.
- SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ SendInputEventACK(WebInputEvent::GestureTwoFingerTap,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(WebInputEvent::GestureScrollUpdate, view_->gesture_event_type());
+ EXPECT_EQ(WebInputEvent::GestureTwoFingerTap, view_->gesture_event_type());
EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, view_->ack_result());
}
@@ -1266,1091 +1029,168 @@ TEST_F(RenderWidgetHostTest, MultipleInputEvents) {
EXPECT_TRUE(host_->unresponsive_timer_fired());
}
-// This test is not valid for Windows because getting the shared memory
-// size doesn't work.
-#if !defined(OS_WIN)
-TEST_F(RenderWidgetHostTest, IncorrectBitmapScaleFactor) {
- ViewHostMsg_UpdateRect_Params params;
- process_->InitUpdateRectParams(&params);
- params.scale_factor = params.scale_factor * 2;
-
- EXPECT_EQ(0, process_->bad_msg_count());
- host_->OnUpdateRect(params);
- EXPECT_EQ(1, process_->bad_msg_count());
-}
-#endif
-
-// Tests that scroll ACKs are correctly handled by the overscroll-navigation
-// controller.
-TEST_F(RenderWidgetHostTest, WheelScrollEventOverscrolls) {
- host_->SetupForOverscrollControllerTest();
- process_->sink().ClearMessages();
-
- // Simulate wheel events.
- SimulateWheelEvent(-5, 0, 0, true); // sent directly
- SimulateWheelEvent(-1, 1, 0, true); // enqueued
- SimulateWheelEvent(-10, -3, 0, true); // coalesced into previous event
- SimulateWheelEvent(-15, -1, 0, true); // coalesced into previous event
- SimulateWheelEvent(-30, -3, 0, true); // coalesced into previous event
- SimulateWheelEvent(-20, 6, 1, true); // enqueued, different modifiers
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(1U, process_->sink().message_count());
- process_->sink().ClearMessages();
-
- // Receive ACK the first wheel event as not processed.
- SendInputEventACK(WebInputEvent::MouseWheel,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(1U, process_->sink().message_count());
- process_->sink().ClearMessages();
-
- // Receive ACK for the second (coalesced) event as not processed. This will
- // start a back navigation. However, this will also cause the queued next
- // event to be sent to the renderer. But since overscroll navigation has
- // started, that event will also be included in the overscroll computation
- // instead of being sent to the renderer. So the result will be an overscroll
- // back navigation, and no event will be sent to the renderer.
- SendInputEventACK(WebInputEvent::MouseWheel,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_WEST, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_WEST, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(-81.f, host_->overscroll_delta_x());
- EXPECT_EQ(-31.f, host_->overscroll_delegate()->delta_x());
- EXPECT_EQ(0.f, host_->overscroll_delegate()->delta_y());
- EXPECT_EQ(0U, process_->sink().message_count());
-
- // Send a mouse-move event. This should cancel the overscroll navigation.
- SimulateMouseMove(5, 10, 0);
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(1U, process_->sink().message_count());
-}
-
-// Tests that if some scroll events are consumed towards the start, then
-// subsequent scrolls do not horizontal overscroll.
-TEST_F(RenderWidgetHostTest, WheelScrollConsumedDoNotHorizOverscroll) {
- host_->SetupForOverscrollControllerTest();
- process_->sink().ClearMessages();
-
- // Simulate wheel events.
- SimulateWheelEvent(-5, 0, 0, true); // sent directly
- SimulateWheelEvent(-1, -1, 0, true); // enqueued
- SimulateWheelEvent(-10, -3, 0, true); // coalesced into previous event
- SimulateWheelEvent(-15, -1, 0, true); // coalesced into previous event
- SimulateWheelEvent(-30, -3, 0, true); // coalesced into previous event
- SimulateWheelEvent(-20, 6, 1, true); // enqueued, different modifiers
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(1U, process_->sink().message_count());
- process_->sink().ClearMessages();
-
- // Receive ACK the first wheel event as processed.
- SendInputEventACK(WebInputEvent::MouseWheel,
- INPUT_EVENT_ACK_STATE_CONSUMED);
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(1U, process_->sink().message_count());
- process_->sink().ClearMessages();
-
- // Receive ACK for the second (coalesced) event as not processed. This should
- // not initiate overscroll, since the beginning of the scroll has been
- // consumed. The queued event with different modifiers should be sent to the
- // renderer.
- SendInputEventACK(WebInputEvent::MouseWheel,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(1U, process_->sink().message_count());
-
- process_->sink().ClearMessages();
- SendInputEventACK(WebInputEvent::MouseWheel,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(0U, process_->sink().message_count());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
-
- // Indicate the end of the scrolling from the touchpad.
- SimulateGestureFlingStartEvent(-1200.f, 0.f, WebGestureEvent::Touchpad);
- EXPECT_EQ(1U, process_->sink().message_count());
-
- // Start another scroll. This time, do not consume any scroll events.
- process_->sink().ClearMessages();
- SimulateWheelEvent(0, -5, 0, true); // sent directly
- SimulateWheelEvent(0, -1, 0, true); // enqueued
- SimulateWheelEvent(-10, -3, 0, true); // coalesced into previous event
- SimulateWheelEvent(-15, -1, 0, true); // coalesced into previous event
- SimulateWheelEvent(-30, -3, 0, true); // coalesced into previous event
- SimulateWheelEvent(-20, 6, 1, true); // enqueued, different modifiers
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(1U, process_->sink().message_count());
- process_->sink().ClearMessages();
-
- // Receive ACK for the first wheel and the subsequent coalesced event as not
- // processed. This should start a back-overscroll.
- SendInputEventACK(WebInputEvent::MouseWheel,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(1U, process_->sink().message_count());
- process_->sink().ClearMessages();
- SendInputEventACK(WebInputEvent::MouseWheel,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_WEST, host_->overscroll_mode());
+std::string GetInputMessageTypes(RenderWidgetHostProcess* process) {
+ std::string result;
+ for (size_t i = 0; i < process->sink().message_count(); ++i) {
+ const IPC::Message *message = process->sink().GetMessageAt(i);
+ EXPECT_EQ(InputMsg_HandleInputEvent::ID, message->type());
+ InputMsg_HandleInputEvent::Param params;
+ EXPECT_TRUE(InputMsg_HandleInputEvent::Read(message, &params));
+ const WebInputEvent* event = params.a;
+ if (i != 0)
+ result += " ";
+ result += WebInputEventTraits::GetName(event->type);
+ }
+ process->sink().ClearMessages();
+ return result;
}
-// Tests that wheel-scrolling correctly turns overscroll on and off.
-TEST_F(RenderWidgetHostTest, WheelScrollOverscrollToggle) {
- host_->SetupForOverscrollControllerTest();
- process_->sink().ClearMessages();
-
- // Send a wheel event. ACK the event as not processed. This should not
- // initiate an overscroll gesture since it doesn't cross the threshold yet.
- SimulateWheelEvent(10, 0, 0, true);
- EXPECT_EQ(1U, process_->sink().message_count());
- SendInputEventACK(WebInputEvent::MouseWheel,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
- process_->sink().ClearMessages();
-
- // Scroll some more so as to not overscroll.
- SimulateWheelEvent(10, 0, 0, true);
- EXPECT_EQ(1U, process_->sink().message_count());
- SendInputEventACK(WebInputEvent::MouseWheel,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
- process_->sink().ClearMessages();
-
- // Scroll some more to initiate an overscroll.
- SimulateWheelEvent(40, 0, 0, true);
- EXPECT_EQ(1U, process_->sink().message_count());
- SendInputEventACK(WebInputEvent::MouseWheel,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(60.f, host_->overscroll_delta_x());
- EXPECT_EQ(10.f, host_->overscroll_delegate()->delta_x());
- EXPECT_EQ(0.f, host_->overscroll_delegate()->delta_y());
- process_->sink().ClearMessages();
-
- // Scroll in the reverse direction enough to abort the overscroll.
- SimulateWheelEvent(-20, 0, 0, true);
- EXPECT_EQ(0U, process_->sink().message_count());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
-
- // Continue to scroll in the reverse direction.
- SimulateWheelEvent(-20, 0, 0, true);
- EXPECT_EQ(1U, process_->sink().message_count());
- SendInputEventACK(WebInputEvent::MouseWheel,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
- process_->sink().ClearMessages();
-
- // Continue to scroll in the reverse direction enough to initiate overscroll
- // in that direction.
- SimulateWheelEvent(-55, 0, 0, true);
- EXPECT_EQ(1U, process_->sink().message_count());
- SendInputEventACK(WebInputEvent::MouseWheel,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_WEST, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_WEST, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(-75.f, host_->overscroll_delta_x());
- EXPECT_EQ(-25.f, host_->overscroll_delegate()->delta_x());
- EXPECT_EQ(0.f, host_->overscroll_delegate()->delta_y());
-}
-
-TEST_F(RenderWidgetHostTest, ScrollEventsOverscrollWithFling) {
- host_->SetupForOverscrollControllerTest();
- process_->sink().ClearMessages();
-
- // Send a wheel event. ACK the event as not processed. This should not
- // initiate an overscroll gesture since it doesn't cross the threshold yet.
- SimulateWheelEvent(10, 0, 0, true);
- EXPECT_EQ(1U, process_->sink().message_count());
- SendInputEventACK(WebInputEvent::MouseWheel,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
- process_->sink().ClearMessages();
-
- // Scroll some more so as to not overscroll.
- SimulateWheelEvent(20, 0, 0, true);
- EXPECT_EQ(1U, process_->sink().message_count());
- SendInputEventACK(WebInputEvent::MouseWheel,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
- process_->sink().ClearMessages();
-
- // Scroll some more to initiate an overscroll.
- SimulateWheelEvent(30, 0, 0, true);
- EXPECT_EQ(1U, process_->sink().message_count());
- SendInputEventACK(WebInputEvent::MouseWheel,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(60.f, host_->overscroll_delta_x());
- EXPECT_EQ(10.f, host_->overscroll_delegate()->delta_x());
- EXPECT_EQ(0.f, host_->overscroll_delegate()->delta_y());
- process_->sink().ClearMessages();
- EXPECT_EQ(0U, host_->GestureEventLastQueueEventSize());
-
- // Send a fling start, but with a small velocity, so that the overscroll is
- // aborted. The fling should proceed to the renderer, through the gesture
- // event filter.
- SimulateGestureFlingStartEvent(0.f, 0.1f, WebGestureEvent::Touchpad);
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(1U, host_->GestureEventLastQueueEventSize());
- EXPECT_EQ(1U, process_->sink().message_count());
-}
-
-// Same as ScrollEventsOverscrollWithFling, but with zero velocity. Checks that
-// the zero-velocity fling does not reach the renderer.
-TEST_F(RenderWidgetHostTest, ScrollEventsOverscrollWithZeroFling) {
- host_->SetupForOverscrollControllerTest();
- process_->sink().ClearMessages();
-
- // Send a wheel event. ACK the event as not processed. This should not
- // initiate an overscroll gesture since it doesn't cross the threshold yet.
- SimulateWheelEvent(10, 0, 0, true);
- EXPECT_EQ(1U, process_->sink().message_count());
- SendInputEventACK(WebInputEvent::MouseWheel,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
- process_->sink().ClearMessages();
-
- // Scroll some more so as to not overscroll.
- SimulateWheelEvent(20, 0, 0, true);
- EXPECT_EQ(1U, process_->sink().message_count());
- SendInputEventACK(WebInputEvent::MouseWheel,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
- process_->sink().ClearMessages();
-
- // Scroll some more to initiate an overscroll.
- SimulateWheelEvent(30, 0, 0, true);
- EXPECT_EQ(1U, process_->sink().message_count());
- SendInputEventACK(WebInputEvent::MouseWheel,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(60.f, host_->overscroll_delta_x());
- EXPECT_EQ(10.f, host_->overscroll_delegate()->delta_x());
- EXPECT_EQ(0.f, host_->overscroll_delegate()->delta_y());
- process_->sink().ClearMessages();
- EXPECT_EQ(0U, host_->GestureEventLastQueueEventSize());
-
- // Send a fling start, but with a small velocity, so that the overscroll is
- // aborted. The fling should proceed to the renderer, through the gesture
- // event filter.
- SimulateGestureFlingStartEvent(10.f, 0.f, WebGestureEvent::Touchpad);
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(1U, host_->GestureEventLastQueueEventSize());
- EXPECT_EQ(1U, process_->sink().message_count());
-}
-
-// Tests that a fling in the opposite direction of the overscroll cancels the
-// overscroll nav instead of completing it.
-TEST_F(RenderWidgetHostTest, ReverseFlingCancelsOverscroll) {
- host_->SetupForOverscrollControllerTest();
- host_->DisableGestureDebounce();
+TEST_F(RenderWidgetHostTest, TouchEmulator) {
+ simulated_event_time_delta_seconds_ = 0.1;
+ // Immediately ack all touches instead of sending them to the renderer.
+ host_->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, false));
+ host_->OnMessageReceived(
+ ViewHostMsg_SetTouchEventEmulationEnabled(0, true, true));
process_->sink().ClearMessages();
view_->set_bounds(gfx::Rect(0, 0, 400, 200));
view_->Show();
- {
- // Start and end a gesture in the same direction without processing the
- // gesture events in the renderer. This should initiate and complete an
- // overscroll navigation.
- SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
- WebGestureEvent::Touchscreen);
- SimulateGestureScrollUpdateEvent(300, -5, 0);
- SendInputEventACK(WebInputEvent::GestureScrollBegin,
- INPUT_EVENT_ACK_STATE_CONSUMED);
- SendInputEventACK(WebInputEvent::GestureScrollUpdate,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_delegate()->current_mode());
- process_->sink().ClearMessages();
-
- SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
- WebGestureEvent::Touchscreen);
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_delegate()->completed_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(1U, process_->sink().message_count());
- SendInputEventACK(WebInputEvent::GestureScrollEnd,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- }
-
- {
- // Start over, except instead of ending the gesture with ScrollEnd, end it
- // with a FlingStart, with velocity in the reverse direction. This should
- // initiate an overscroll navigation, but it should be cancelled because of
- // the fling in the opposite direction.
- host_->overscroll_delegate()->Reset();
- SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
- WebGestureEvent::Touchscreen);
- SimulateGestureScrollUpdateEvent(-300, -5, 0);
- SendInputEventACK(WebInputEvent::GestureScrollBegin,
- INPUT_EVENT_ACK_STATE_CONSUMED);
- SendInputEventACK(WebInputEvent::GestureScrollUpdate,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_WEST, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_WEST, host_->overscroll_delegate()->current_mode());
- process_->sink().ClearMessages();
-
- SimulateGestureFlingStartEvent(100, 0, WebGestureEvent::Touchscreen);
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->completed_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(1U, process_->sink().message_count());
- }
-}
-
-// Tests that touch-scroll events are handled correctly by the overscroll
-// controller. This also tests that the overscroll controller and the
-// gesture-event filter play nice with each other.
-TEST_F(RenderWidgetHostTest, GestureScrollOverscrolls) {
- // Turn off debounce handling for test isolation.
- host_->SetupForOverscrollControllerTest();
- host_->DisableGestureDebounce();
- process_->sink().ClearMessages();
-
- SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
- WebGestureEvent::Touchscreen);
- SendInputEventACK(WebInputEvent::GestureScrollBegin,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
-
- // Send another gesture event and ACK as not being processed. This should
- // initiate the navigation gesture.
- SimulateGestureScrollUpdateEvent(55, -5, 0);
- SendInputEventACK(WebInputEvent::GestureScrollUpdate,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(55.f, host_->overscroll_delta_x());
- EXPECT_EQ(-5.f, host_->overscroll_delta_y());
- EXPECT_EQ(5.f, host_->overscroll_delegate()->delta_x());
- EXPECT_EQ(-5.f, host_->overscroll_delegate()->delta_y());
- EXPECT_EQ(0U, host_->GestureEventLastQueueEventSize());
- process_->sink().ClearMessages();
-
- // Send another gesture update event. This event should be consumed by the
- // controller, and not be forwarded to the renderer. The gesture-event filter
- // should not also receive this event.
- SimulateGestureScrollUpdateEvent(10, -5, 0);
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(65.f, host_->overscroll_delta_x());
- EXPECT_EQ(-10.f, host_->overscroll_delta_y());
- EXPECT_EQ(15.f, host_->overscroll_delegate()->delta_x());
- EXPECT_EQ(-10.f, host_->overscroll_delegate()->delta_y());
+ SimulateMouseEvent(WebInputEvent::MouseMove, 10, 10, 0, false);
EXPECT_EQ(0U, process_->sink().message_count());
- EXPECT_EQ(0U, host_->GestureEventLastQueueEventSize());
-
- // Now send a scroll end. This should cancel the overscroll gesture, and send
- // the event to the renderer. The gesture-event filter should receive this
- // event.
- SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
- WebGestureEvent::Touchscreen);
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(1U, process_->sink().message_count());
- // The scroll end event will have received a synthetic ack from the input
- // router.
- EXPECT_EQ(0U, host_->GestureEventLastQueueEventSize());
-}
-
-// Tests that if the page is scrolled because of a scroll-gesture, then that
-// particular scroll sequence never generates overscroll if the scroll direction
-// is horizontal.
-TEST_F(RenderWidgetHostTest, GestureScrollConsumedHorizontal) {
- // Turn off debounce handling for test isolation.
- host_->SetupForOverscrollControllerTest();
- host_->DisableGestureDebounce();
- process_->sink().ClearMessages();
- SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
- WebGestureEvent::Touchscreen);
- SimulateGestureScrollUpdateEvent(10, 0, 0);
-
- // Start scrolling on content. ACK both events as being processed.
- SendInputEventACK(WebInputEvent::GestureScrollBegin,
- INPUT_EVENT_ACK_STATE_CONSUMED);
+ // Mouse press becomes touch start which in turn becomes tap.
+ SimulateMouseEvent(WebInputEvent::MouseDown, 10, 10, 0, true);
+ EXPECT_EQ(WebInputEvent::TouchStart, host_->acked_touch_event_type());
+ EXPECT_EQ("GestureTapDown", GetInputMessageTypes(process_));
+
+ // Mouse drag generates touch move, cancels tap and starts scroll.
+ SimulateMouseEvent(WebInputEvent::MouseMove, 10, 30, 0, true);
+ EXPECT_EQ(WebInputEvent::TouchMove, host_->acked_touch_event_type());
+ EXPECT_EQ(
+ "GestureTapCancel GestureScrollBegin GestureScrollUpdate",
+ GetInputMessageTypes(process_));
SendInputEventACK(WebInputEvent::GestureScrollUpdate,
INPUT_EVENT_ACK_STATE_CONSUMED);
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
- process_->sink().ClearMessages();
-
- // Send another gesture event and ACK as not being processed. This should
- // not initiate overscroll because the beginning of the scroll event did
- // scroll some content on the page. Since there was no overscroll, the event
- // should reach the renderer.
- SimulateGestureScrollUpdateEvent(55, 0, 0);
- EXPECT_EQ(1U, process_->sink().message_count());
- SendInputEventACK(WebInputEvent::GestureScrollUpdate,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
-}
-
-// Tests that the overscroll controller plays nice with touch-scrolls and the
-// gesture event filter with debounce filtering turned on.
-TEST_F(RenderWidgetHostTest, GestureScrollDebounceOverscrolls) {
- host_->SetupForOverscrollControllerTest();
- host_->set_debounce_interval_time_ms(100);
- process_->sink().ClearMessages();
-
- // Start scrolling. Receive ACK as it being processed.
- SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
- WebGestureEvent::Touchscreen);
- EXPECT_EQ(1U, process_->sink().message_count());
- process_->sink().ClearMessages();
- SendInputEventACK(WebInputEvent::GestureScrollBegin,
- INPUT_EVENT_ACK_STATE_CONSUMED);
-
- // Send update events.
- SimulateGestureScrollUpdateEvent(25, 0, 0);
- EXPECT_EQ(1U, host_->GestureEventLastQueueEventSize());
- EXPECT_EQ(0U, host_->GestureEventDebouncingQueueSize());
- EXPECT_TRUE(host_->ScrollingInProgress());
- EXPECT_EQ(1U, process_->sink().message_count());
- process_->sink().ClearMessages();
-
- // Quickly end and restart the scroll gesture. These two events should get
- // discarded.
- SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
- WebGestureEvent::Touchscreen);
EXPECT_EQ(0U, process_->sink().message_count());
- EXPECT_EQ(1U, host_->GestureEventLastQueueEventSize());
- EXPECT_EQ(1U, host_->GestureEventDebouncingQueueSize());
-
- SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
- WebGestureEvent::Touchscreen);
- EXPECT_EQ(0U, process_->sink().message_count());
- EXPECT_EQ(1U, host_->GestureEventLastQueueEventSize());
- EXPECT_EQ(2U, host_->GestureEventDebouncingQueueSize());
-
- // Send another update event. This should get into the queue.
- SimulateGestureScrollUpdateEvent(30, 0, 0);
- EXPECT_EQ(0U, process_->sink().message_count());
- EXPECT_EQ(2U, host_->GestureEventLastQueueEventSize());
- EXPECT_EQ(0U, host_->GestureEventDebouncingQueueSize());
- EXPECT_TRUE(host_->ScrollingInProgress());
-
- // Receive an ACK for the first scroll-update event as not being processed.
- // This will contribute to the overscroll gesture, but not enough for the
- // overscroll controller to start consuming gesture events. This also cause
- // the queued gesture event to be forwarded to the renderer.
- SendInputEventACK(WebInputEvent::GestureScrollUpdate,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(1U, host_->GestureEventLastQueueEventSize());
- EXPECT_EQ(0U, host_->GestureEventDebouncingQueueSize());
- EXPECT_EQ(1U, process_->sink().message_count());
- process_->sink().ClearMessages();
- // Send another update event. This should get into the queue.
- SimulateGestureScrollUpdateEvent(10, 0, 0);
+ // Mouse drag with shift becomes pinch.
+ SimulateMouseEvent(
+ WebInputEvent::MouseMove, 10, 40, WebInputEvent::ShiftKey, true);
+ EXPECT_EQ(WebInputEvent::TouchMove, host_->acked_touch_event_type());
+ EXPECT_EQ("GesturePinchBegin",
+ GetInputMessageTypes(process_));
EXPECT_EQ(0U, process_->sink().message_count());
- EXPECT_EQ(2U, host_->GestureEventLastQueueEventSize());
- EXPECT_EQ(0U, host_->GestureEventDebouncingQueueSize());
- EXPECT_TRUE(host_->ScrollingInProgress());
-
- // Receive an ACK for the second scroll-update event as not being processed.
- // This will now initiate an overscroll. This will also cause the queued
- // gesture event to be released. But instead of going to the renderer, it will
- // be consumed by the overscroll controller.
- SendInputEventACK(WebInputEvent::GestureScrollUpdate,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(65.f, host_->overscroll_delta_x());
- EXPECT_EQ(15.f, host_->overscroll_delegate()->delta_x());
- EXPECT_EQ(0.f, host_->overscroll_delegate()->delta_y());
- EXPECT_EQ(0U, process_->sink().message_count());
- EXPECT_EQ(0U, host_->GestureEventLastQueueEventSize());
- EXPECT_EQ(0U, host_->GestureEventDebouncingQueueSize());
-}
-// Tests that the gesture debounce timer plays nice with the overscroll
-// controller.
-TEST_F(RenderWidgetHostTest, GestureScrollDebounceTimerOverscroll) {
- host_->SetupForOverscrollControllerTest();
- host_->set_debounce_interval_time_ms(10);
- process_->sink().ClearMessages();
-
- // Start scrolling. Receive ACK as it being processed.
- SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
- WebGestureEvent::Touchscreen);
- EXPECT_EQ(1U, process_->sink().message_count());
- process_->sink().ClearMessages();
- SendInputEventACK(WebInputEvent::GestureScrollBegin,
+ SimulateMouseEvent(
+ WebInputEvent::MouseMove, 10, 50, WebInputEvent::ShiftKey, true);
+ EXPECT_EQ(WebInputEvent::TouchMove, host_->acked_touch_event_type());
+ EXPECT_EQ("GesturePinchUpdate",
+ GetInputMessageTypes(process_));
+ SendInputEventACK(WebInputEvent::GesturePinchUpdate,
INPUT_EVENT_ACK_STATE_CONSUMED);
-
- // Send update events.
- SimulateGestureScrollUpdateEvent(55, 0, 0);
- EXPECT_EQ(1U, host_->GestureEventLastQueueEventSize());
- EXPECT_EQ(0U, host_->GestureEventDebouncingQueueSize());
- EXPECT_TRUE(host_->ScrollingInProgress());
- EXPECT_EQ(1U, process_->sink().message_count());
- process_->sink().ClearMessages();
-
- // Send an end event. This should get in the debounce queue.
- SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
- WebGestureEvent::Touchscreen);
EXPECT_EQ(0U, process_->sink().message_count());
- EXPECT_EQ(1U, host_->GestureEventLastQueueEventSize());
- EXPECT_EQ(1U, host_->GestureEventDebouncingQueueSize());
- // Receive ACK for the scroll-update event.
+ // Mouse drag without shift becomes scroll again.
+ SimulateMouseEvent(WebInputEvent::MouseMove, 10, 60, 0, true);
+ EXPECT_EQ(WebInputEvent::TouchMove, host_->acked_touch_event_type());
+ EXPECT_EQ("GesturePinchEnd GestureScrollUpdate",
+ GetInputMessageTypes(process_));
SendInputEventACK(WebInputEvent::GestureScrollUpdate,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(55.f, host_->overscroll_delta_x());
- EXPECT_EQ(5.f, host_->overscroll_delegate()->delta_x());
- EXPECT_EQ(0.f, host_->overscroll_delegate()->delta_y());
- EXPECT_EQ(0U, host_->GestureEventLastQueueEventSize());
- EXPECT_EQ(1U, host_->GestureEventDebouncingQueueSize());
+ INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(0U, process_->sink().message_count());
- // Let the timer for the debounce queue fire. That should release the queued
- // scroll-end event. Since overscroll has started, but there hasn't been
- // enough overscroll to complete the gesture, the overscroll controller
- // will reset the state. The scroll-end should therefore be dispatched to the
- // renderer, and the gesture-event-filter should await an ACK for it.
- base::MessageLoop::current()->PostDelayedTask(
- FROM_HERE,
- base::MessageLoop::QuitClosure(),
- TimeDelta::FromMilliseconds(15));
- base::MessageLoop::current()->Run();
-
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
- // The scroll end event will have received a synthetic ack from the input
- // router.
- EXPECT_EQ(0U, host_->GestureEventLastQueueEventSize());
- EXPECT_EQ(0U, host_->GestureEventDebouncingQueueSize());
- EXPECT_EQ(1U, process_->sink().message_count());
-}
-
-// Tests that when touch-events are dispatched to the renderer, the overscroll
-// gesture deals with them correctly.
-TEST_F(RenderWidgetHostTest, OverscrollWithTouchEvents) {
- host_->SetupForOverscrollControllerTest();
- host_->set_debounce_interval_time_ms(10);
- host_->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, true));
- process_->sink().ClearMessages();
- view_->set_bounds(gfx::Rect(0, 0, 400, 200));
- view_->Show();
-
- // The test sends an intermingled sequence of touch and gesture events.
-
- PressTouchPoint(0, 1);
- SendTouchEvent();
- EXPECT_EQ(1U, process_->sink().message_count());
- process_->sink().ClearMessages();
- SendInputEventACK(WebInputEvent::TouchStart,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
-
- MoveTouchPoint(0, 20, 5);
- SendTouchEvent();
- EXPECT_EQ(1U, process_->sink().message_count());
- process_->sink().ClearMessages();
- SendInputEventACK(WebInputEvent::TouchMove,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
-
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
-
- SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
- WebGestureEvent::Touchscreen);
- SimulateGestureScrollUpdateEvent(20, 0, 0);
- SendInputEventACK(WebInputEvent::GestureScrollBegin,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ SimulateMouseEvent(WebInputEvent::MouseMove, 10, 70, 0, true);
+ EXPECT_EQ(WebInputEvent::TouchMove, host_->acked_touch_event_type());
+ EXPECT_EQ("GestureScrollUpdate",
+ GetInputMessageTypes(process_));
SendInputEventACK(WebInputEvent::GestureScrollUpdate,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
- process_->sink().ClearMessages();
-
- // Another touch move event should reach the renderer since overscroll hasn't
- // started yet.
- MoveTouchPoint(0, 65, 10);
- SendTouchEvent();
- EXPECT_EQ(1U, process_->sink().message_count());
- process_->sink().ClearMessages();
-
- SendInputEventACK(WebInputEvent::TouchMove,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- SimulateGestureScrollUpdateEvent(45, 0, 0);
- SendInputEventACK(WebInputEvent::GestureScrollUpdate,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(65.f, host_->overscroll_delta_x());
- EXPECT_EQ(15.f, host_->overscroll_delegate()->delta_x());
- EXPECT_EQ(0.f, host_->overscroll_delegate()->delta_y());
- EXPECT_TRUE(host_->TouchEventQueueEmpty());
- process_->sink().ClearMessages();
-
- // Send another touch event. The page should get the touch-move event, even
- // though overscroll has started.
- MoveTouchPoint(0, 55, 5);
- SendTouchEvent();
- EXPECT_EQ(1U, process_->sink().message_count());
- EXPECT_FALSE(host_->TouchEventQueueEmpty());
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(65.f, host_->overscroll_delta_x());
- EXPECT_EQ(15.f, host_->overscroll_delegate()->delta_x());
- EXPECT_EQ(0.f, host_->overscroll_delegate()->delta_y());
-
- SendInputEventACK(WebInputEvent::TouchMove,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_TRUE(host_->TouchEventQueueEmpty());
- process_->sink().ClearMessages();
-
- SimulateGestureScrollUpdateEvent(-10, 0, 0);
- EXPECT_EQ(0U, process_->sink().message_count());
- EXPECT_TRUE(host_->TouchEventQueueEmpty());
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(55.f, host_->overscroll_delta_x());
- EXPECT_EQ(5.f, host_->overscroll_delegate()->delta_x());
- EXPECT_EQ(0.f, host_->overscroll_delegate()->delta_y());
-
- MoveTouchPoint(0, 255, 5);
- SendTouchEvent();
- EXPECT_EQ(1U, process_->sink().message_count());
- EXPECT_FALSE(host_->TouchEventQueueEmpty());
- process_->sink().ClearMessages();
- SendInputEventACK(WebInputEvent::TouchMove,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
-
- SimulateGestureScrollUpdateEvent(200, 0, 0);
- EXPECT_EQ(0U, process_->sink().message_count());
- EXPECT_TRUE(host_->TouchEventQueueEmpty());
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(255.f, host_->overscroll_delta_x());
- EXPECT_EQ(205.f, host_->overscroll_delegate()->delta_x());
- EXPECT_EQ(0.f, host_->overscroll_delegate()->delta_y());
-
- // The touch-end/cancel event should always reach the renderer if the page has
- // touch handlers.
- ReleaseTouchPoint(0);
- SendTouchEvent();
- EXPECT_EQ(1U, process_->sink().message_count());
- EXPECT_FALSE(host_->TouchEventQueueEmpty());
- process_->sink().ClearMessages();
-
- SendInputEventACK(WebInputEvent::TouchEnd,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(0U, process_->sink().message_count());
- EXPECT_TRUE(host_->TouchEventQueueEmpty());
-
- SimulateGestureEvent(blink::WebInputEvent::GestureScrollEnd,
- WebGestureEvent::Touchscreen);
- base::MessageLoop::current()->PostDelayedTask(
- FROM_HERE,
- base::MessageLoop::QuitClosure(),
- TimeDelta::FromMilliseconds(10));
- base::MessageLoop::current()->Run();
- EXPECT_EQ(1U, process_->sink().message_count());
- EXPECT_TRUE(host_->TouchEventQueueEmpty());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_delegate()->completed_mode());
-}
-
-// Tests that touch-gesture end is dispatched to the renderer at the end of a
-// touch-gesture initiated overscroll.
-TEST_F(RenderWidgetHostTest, TouchGestureEndDispatchedAfterOverscrollComplete) {
- host_->SetupForOverscrollControllerTest();
- host_->set_debounce_interval_time_ms(10);
- host_->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, true));
- process_->sink().ClearMessages();
- view_->set_bounds(gfx::Rect(0, 0, 400, 200));
- view_->Show();
-
- // Start scrolling. Receive ACK as it being processed.
- SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
- WebGestureEvent::Touchscreen);
- EXPECT_EQ(1U, process_->sink().message_count());
- // The scroll begin event will have received a synthetic ack from the input
- // router.
- EXPECT_EQ(0U, host_->GestureEventLastQueueEventSize());
- process_->sink().ClearMessages();
- SendInputEventACK(WebInputEvent::GestureScrollBegin,
INPUT_EVENT_ACK_STATE_CONSUMED);
- EXPECT_EQ(0U, host_->GestureEventLastQueueEventSize());
EXPECT_EQ(0U, process_->sink().message_count());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
-
- // Send update events.
- SimulateGestureScrollUpdateEvent(55, -5, 0);
- EXPECT_EQ(1U, host_->GestureEventLastQueueEventSize());
- EXPECT_EQ(0U, host_->GestureEventDebouncingQueueSize());
- EXPECT_TRUE(host_->ScrollingInProgress());
- EXPECT_EQ(1U, process_->sink().message_count());
- process_->sink().ClearMessages();
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
- SendInputEventACK(WebInputEvent::GestureScrollUpdate,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(0U, host_->GestureEventLastQueueEventSize());
+ SimulateMouseEvent(WebInputEvent::MouseUp, 10, 70, 0, true);
+ EXPECT_EQ(WebInputEvent::TouchEnd, host_->acked_touch_event_type());
+ EXPECT_EQ("GestureScrollEnd", GetInputMessageTypes(process_));
EXPECT_EQ(0U, process_->sink().message_count());
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(55.f, host_->overscroll_delta_x());
- EXPECT_EQ(5.f, host_->overscroll_delegate()->delta_x());
- EXPECT_EQ(-5.f, host_->overscroll_delegate()->delta_y());
-
- // Send end event.
- SimulateGestureEvent(blink::WebInputEvent::GestureScrollEnd,
- WebGestureEvent::Touchscreen);
- EXPECT_EQ(0U, process_->sink().message_count());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->completed_mode());
- EXPECT_EQ(0U, host_->GestureEventLastQueueEventSize());
- EXPECT_EQ(1U, host_->GestureEventDebouncingQueueSize());
- base::MessageLoop::current()->PostDelayedTask(
- FROM_HERE,
- base::MessageLoop::QuitClosure(),
- TimeDelta::FromMilliseconds(10));
- base::MessageLoop::current()->Run();
- EXPECT_EQ(1U, process_->sink().message_count());
- process_->sink().ClearMessages();
- // The scroll end event will have received a synthetic ack from the input
- // router.
- EXPECT_EQ(0U, host_->GestureEventLastQueueEventSize());
- EXPECT_EQ(0U, host_->GestureEventDebouncingQueueSize());
- SendInputEventACK(blink::WebInputEvent::GestureScrollEnd,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ // Mouse move does nothing.
+ SimulateMouseEvent(WebInputEvent::MouseMove, 10, 80, 0, false);
EXPECT_EQ(0U, process_->sink().message_count());
- EXPECT_EQ(0U, host_->GestureEventLastQueueEventSize());
- EXPECT_EQ(0U, host_->GestureEventDebouncingQueueSize());
- // Start scrolling. Receive ACK as it being processed.
- SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
- WebGestureEvent::Touchscreen);
- EXPECT_EQ(1U, process_->sink().message_count());
- // The scroll begin event will have received a synthetic ack from the input
- // router.
- EXPECT_EQ(0U, host_->GestureEventLastQueueEventSize());
- process_->sink().ClearMessages();
- SendInputEventACK(WebInputEvent::GestureScrollBegin,
- INPUT_EVENT_ACK_STATE_CONSUMED);
- EXPECT_EQ(0U, host_->GestureEventLastQueueEventSize());
+ // Another mouse down continues scroll.
+ SimulateMouseEvent(WebInputEvent::MouseDown, 10, 80, 0, true);
+ EXPECT_EQ(WebInputEvent::TouchStart, host_->acked_touch_event_type());
+ EXPECT_EQ("GestureTapDown", GetInputMessageTypes(process_));
EXPECT_EQ(0U, process_->sink().message_count());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
-
- // Send update events.
- SimulateGestureScrollUpdateEvent(235, -5, 0);
- EXPECT_EQ(1U, host_->GestureEventLastQueueEventSize());
- EXPECT_EQ(0U, host_->GestureEventDebouncingQueueSize());
- EXPECT_TRUE(host_->ScrollingInProgress());
- EXPECT_EQ(1U, process_->sink().message_count());
- process_->sink().ClearMessages();
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
+ SimulateMouseEvent(WebInputEvent::MouseMove, 10, 100, 0, true);
+ EXPECT_EQ(WebInputEvent::TouchMove, host_->acked_touch_event_type());
+ EXPECT_EQ(
+ "GestureTapCancel GestureScrollBegin GestureScrollUpdate",
+ GetInputMessageTypes(process_));
SendInputEventACK(WebInputEvent::GestureScrollUpdate,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(0U, host_->GestureEventLastQueueEventSize());
- EXPECT_EQ(0U, process_->sink().message_count());
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(235.f, host_->overscroll_delta_x());
- EXPECT_EQ(185.f, host_->overscroll_delegate()->delta_x());
- EXPECT_EQ(-5.f, host_->overscroll_delegate()->delta_y());
-
- // Send end event.
- SimulateGestureEvent(blink::WebInputEvent::GestureScrollEnd,
- WebGestureEvent::Touchscreen);
+ INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(0U, process_->sink().message_count());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_delegate()->completed_mode());
- EXPECT_EQ(0U, host_->GestureEventLastQueueEventSize());
- EXPECT_EQ(1U, host_->GestureEventDebouncingQueueSize());
-
- base::MessageLoop::current()->PostDelayedTask(
- FROM_HERE,
- base::MessageLoop::QuitClosure(),
- TimeDelta::FromMilliseconds(10));
- base::MessageLoop::current()->Run();
- EXPECT_EQ(1U, process_->sink().message_count());
- process_->sink().ClearMessages();
- // The scroll end event will have received a synthetic ack from the input
- // router.
- EXPECT_EQ(0U, host_->GestureEventLastQueueEventSize());
- EXPECT_EQ(0U, host_->GestureEventDebouncingQueueSize());
- SendInputEventACK(blink::WebInputEvent::GestureScrollEnd,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ // Another pinch.
+ SimulateMouseEvent(
+ WebInputEvent::MouseMove, 10, 110, WebInputEvent::ShiftKey, true);
+ EXPECT_EQ(WebInputEvent::TouchMove, host_->acked_touch_event_type());
+ EXPECT_EQ("GesturePinchBegin",
+ GetInputMessageTypes(process_));
EXPECT_EQ(0U, process_->sink().message_count());
- EXPECT_EQ(0U, host_->GestureEventLastQueueEventSize());
- EXPECT_EQ(0U, host_->GestureEventDebouncingQueueSize());
-}
-
-TEST_F(RenderWidgetHostTest, OverscrollDirectionChange) {
- host_->SetupForOverscrollControllerTest();
- host_->set_debounce_interval_time_ms(100);
- process_->sink().ClearMessages();
- // Start scrolling. Receive ACK as it being processed.
- SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
- WebGestureEvent::Touchscreen);
- EXPECT_EQ(1U, process_->sink().message_count());
- process_->sink().ClearMessages();
- SendInputEventACK(WebInputEvent::GestureScrollBegin,
+ SimulateMouseEvent(
+ WebInputEvent::MouseMove, 10, 120, WebInputEvent::ShiftKey, true);
+ EXPECT_EQ(WebInputEvent::TouchMove, host_->acked_touch_event_type());
+ EXPECT_EQ("GesturePinchUpdate",
+ GetInputMessageTypes(process_));
+ SendInputEventACK(WebInputEvent::GesturePinchUpdate,
INPUT_EVENT_ACK_STATE_CONSUMED);
-
- // Send update events and receive ack as not consumed.
- SimulateGestureScrollUpdateEvent(125, -5, 0);
- EXPECT_EQ(1U, host_->GestureEventLastQueueEventSize());
- EXPECT_EQ(0U, host_->GestureEventDebouncingQueueSize());
- EXPECT_TRUE(host_->ScrollingInProgress());
- EXPECT_EQ(1U, process_->sink().message_count());
- process_->sink().ClearMessages();
-
- SendInputEventACK(WebInputEvent::GestureScrollUpdate,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_delegate()->current_mode());
EXPECT_EQ(0U, process_->sink().message_count());
- // Send another update event, but in the reverse direction. The overscroll
- // controller will consume the event, and reset the overscroll mode.
- SimulateGestureScrollUpdateEvent(-260, 0, 0);
- EXPECT_EQ(0U, process_->sink().message_count());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
-
- // Since the overscroll mode has been reset, the next scroll update events
- // should reach the renderer.
- SimulateGestureScrollUpdateEvent(-20, 0, 0);
- EXPECT_EQ(1U, process_->sink().message_count());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
-}
-
-// Tests that if a mouse-move event completes the overscroll gesture, future
-// move events do reach the renderer.
-TEST_F(RenderWidgetHostTest, OverscrollMouseMoveCompletion) {
- host_->SetupForOverscrollControllerTest();
- host_->DisableGestureDebounce();
- process_->sink().ClearMessages();
- view_->set_bounds(gfx::Rect(0, 0, 400, 200));
- view_->Show();
-
- SimulateWheelEvent(5, 0, 0, true); // sent directly
- SimulateWheelEvent(-1, 0, 0, true); // enqueued
- SimulateWheelEvent(-10, -3, 0, true); // coalesced into previous event
- SimulateWheelEvent(-15, -1, 0, true); // coalesced into previous event
- SimulateWheelEvent(-30, -3, 0, true); // coalesced into previous event
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(1U, process_->sink().message_count());
- process_->sink().ClearMessages();
-
- // Receive ACK the first wheel event as not processed.
- SendInputEventACK(WebInputEvent::MouseWheel,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(1U, process_->sink().message_count());
- process_->sink().ClearMessages();
-
- // Receive ACK for the second (coalesced) event as not processed. This will
- // start an overcroll gesture.
- SendInputEventACK(WebInputEvent::MouseWheel,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_WEST, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_WEST, host_->overscroll_delegate()->current_mode());
+ // Turn off emulation during a pinch.
+ host_->OnMessageReceived(
+ ViewHostMsg_SetTouchEventEmulationEnabled(0, false, false));
+ EXPECT_EQ(WebInputEvent::TouchCancel, host_->acked_touch_event_type());
+ EXPECT_EQ("GesturePinchEnd GestureScrollEnd",
+ GetInputMessageTypes(process_));
EXPECT_EQ(0U, process_->sink().message_count());
- // Send a mouse-move event. This should cancel the overscroll navigation
- // (since the amount overscrolled is not above the threshold), and so the
- // mouse-move should reach the renderer.
- SimulateMouseMove(5, 10, 0);
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->completed_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(1U, process_->sink().message_count());
- process_->sink().ClearMessages();
-
+ // Mouse event should pass untouched.
+ SimulateMouseEvent(
+ WebInputEvent::MouseMove, 10, 10, WebInputEvent::ShiftKey, true);
+ EXPECT_EQ("MouseMove", GetInputMessageTypes(process_));
SendInputEventACK(WebInputEvent::MouseMove,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
-
- // Moving the mouse more should continue to send the events to the renderer.
- SimulateMouseMove(5, 10, 0);
- SendInputEventACK(WebInputEvent::MouseMove,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(1U, process_->sink().message_count());
- process_->sink().ClearMessages();
-
- // Now try with gestures.
- SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
- WebGestureEvent::Touchscreen);
- SimulateGestureScrollUpdateEvent(300, -5, 0);
- SendInputEventACK(WebInputEvent::GestureScrollBegin,
INPUT_EVENT_ACK_STATE_CONSUMED);
- SendInputEventACK(WebInputEvent::GestureScrollUpdate,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_delegate()->current_mode());
- process_->sink().ClearMessages();
-
- // Overscroll gesture is in progress. Send a mouse-move now. This should
- // complete the gesture (because the amount overscrolled is above the
- // threshold).
- SimulateMouseMove(5, 10, 0);
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_delegate()->completed_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(1U, process_->sink().message_count());
- process_->sink().ClearMessages();
- SendInputEventACK(WebInputEvent::MouseMove,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
-
- SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
- WebGestureEvent::Touchscreen);
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(1U, process_->sink().message_count());
- process_->sink().ClearMessages();
- SendInputEventACK(WebInputEvent::GestureScrollEnd,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
-
- // Move mouse some more. The mouse-move events should reach the renderer.
- SimulateMouseMove(5, 10, 0);
- EXPECT_EQ(1U, process_->sink().message_count());
-
- SendInputEventACK(WebInputEvent::MouseMove,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- process_->sink().ClearMessages();
-}
-
-// Tests that if a page scrolled, then the overscroll controller's states are
-// reset after the end of the scroll.
-TEST_F(RenderWidgetHostTest, OverscrollStateResetsAfterScroll) {
- host_->SetupForOverscrollControllerTest();
- host_->DisableGestureDebounce();
- process_->sink().ClearMessages();
- view_->set_bounds(gfx::Rect(0, 0, 400, 200));
- view_->Show();
-
- SimulateWheelEvent(0, 5, 0, true); // sent directly
- SimulateWheelEvent(0, 30, 0, true); // enqueued
- SimulateWheelEvent(0, 40, 0, true); // coalesced into previous event
- SimulateWheelEvent(0, 10, 0, true); // coalesced into previous event
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(1U, process_->sink().message_count());
- process_->sink().ClearMessages();
-
- // The first wheel event is consumed. Dispatches the queued wheel event.
- SendInputEventACK(WebInputEvent::MouseWheel,
- INPUT_EVENT_ACK_STATE_CONSUMED);
- EXPECT_TRUE(host_->ScrollStateIsContentScrolling());
- EXPECT_EQ(1U, process_->sink().message_count());
- process_->sink().ClearMessages();
-
- // The second wheel event is consumed.
- SendInputEventACK(WebInputEvent::MouseWheel,
- INPUT_EVENT_ACK_STATE_CONSUMED);
- EXPECT_TRUE(host_->ScrollStateIsContentScrolling());
-
- // Touchpad scroll can end with a zero-velocity fling. But it is not
- // dispatched, but it should still reset the overscroll controller state.
- SimulateGestureFlingStartEvent(0.f, 0.f, WebGestureEvent::Touchpad);
- EXPECT_TRUE(host_->ScrollStateIsUnknown());
EXPECT_EQ(0U, process_->sink().message_count());
- SimulateWheelEvent(-5, 0, 0, true); // sent directly
- SimulateWheelEvent(-60, 0, 0, true); // enqueued
- SimulateWheelEvent(-100, 0, 0, true); // coalesced into previous event
- EXPECT_EQ(1U, process_->sink().message_count());
- EXPECT_TRUE(host_->ScrollStateIsUnknown());
- process_->sink().ClearMessages();
-
- // The first wheel scroll did not scroll content. Overscroll should not start
- // yet, since enough hasn't been scrolled.
- SendInputEventACK(WebInputEvent::MouseWheel,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_TRUE(host_->ScrollStateIsUnknown());
- EXPECT_EQ(1U, process_->sink().message_count());
- process_->sink().ClearMessages();
-
- SendInputEventACK(WebInputEvent::MouseWheel,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_WEST, host_->overscroll_mode());
- EXPECT_TRUE(host_->ScrollStateIsOverscrolling());
+ // Turn on emulation.
+ host_->OnMessageReceived(
+ ViewHostMsg_SetTouchEventEmulationEnabled(0, true, true));
EXPECT_EQ(0U, process_->sink().message_count());
- SimulateGestureFlingStartEvent(0.f, 0.f, WebGestureEvent::Touchpad);
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_WEST, host_->overscroll_delegate()->completed_mode());
- EXPECT_TRUE(host_->ScrollStateIsUnknown());
+ // Another touch.
+ SimulateMouseEvent(WebInputEvent::MouseDown, 10, 10, 0, true);
+ EXPECT_EQ(WebInputEvent::TouchStart, host_->acked_touch_event_type());
+ EXPECT_EQ("GestureTapDown", GetInputMessageTypes(process_));
EXPECT_EQ(0U, process_->sink().message_count());
- process_->sink().ClearMessages();
-}
-
-TEST_F(RenderWidgetHostTest, OverscrollResetsOnBlur) {
- host_->SetupForOverscrollControllerTest();
- process_->sink().ClearMessages();
- view_->set_bounds(gfx::Rect(0, 0, 400, 200));
- view_->Show();
- // Start an overscroll with gesture scroll. In the middle of the scroll, blur
- // the host.
- SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
- WebGestureEvent::Touchscreen);
- SimulateGestureScrollUpdateEvent(300, -5, 0);
- SendInputEventACK(WebInputEvent::GestureScrollBegin,
- INPUT_EVENT_ACK_STATE_CONSUMED);
+ // Scroll.
+ SimulateMouseEvent(WebInputEvent::MouseMove, 10, 30, 0, true);
+ EXPECT_EQ(WebInputEvent::TouchMove, host_->acked_touch_event_type());
+ EXPECT_EQ(
+ "GestureTapCancel GestureScrollBegin GestureScrollUpdate",
+ GetInputMessageTypes(process_));
SendInputEventACK(WebInputEvent::GestureScrollUpdate,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_delegate()->current_mode());
-
- host_->Blur();
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->completed_mode());
- EXPECT_EQ(0.f, host_->overscroll_delegate()->delta_x());
- EXPECT_EQ(0.f, host_->overscroll_delegate()->delta_y());
- process_->sink().ClearMessages();
+ INPUT_EVENT_ACK_STATE_CONSUMED);
- SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
- WebGestureEvent::Touchscreen);
- EXPECT_EQ(0U, process_->sink().message_count());
+ // Turn off emulation during a scroll.
+ host_->OnMessageReceived(
+ ViewHostMsg_SetTouchEventEmulationEnabled(0, false, false));
+ EXPECT_EQ(WebInputEvent::TouchCancel, host_->acked_touch_event_type());
- // Start a scroll gesture again. This should correctly start the overscroll
- // after the threshold.
- SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
- WebGestureEvent::Touchscreen);
- SimulateGestureScrollUpdateEvent(300, -5, 0);
- SendInputEventACK(WebInputEvent::GestureScrollUpdate,
- INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_mode());
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->completed_mode());
-
- SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
- WebGestureEvent::Touchscreen);
- EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode());
- EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_delegate()->completed_mode());
- process_->sink().ClearMessages();
+ EXPECT_EQ("GestureScrollEnd", GetInputMessageTypes(process_));
+ EXPECT_EQ(0U, process_->sink().message_count());
}
#define TEST_InputRouterRoutes_NOARGS(INPUTMSG) \
@@ -2360,33 +1200,44 @@ TEST_F(RenderWidgetHostTest, OverscrollResetsOnBlur) {
EXPECT_TRUE(host_->mock_input_router()->send_event_called_); \
}
-TEST_InputRouterRoutes_NOARGS(Undo);
-TEST_InputRouterRoutes_NOARGS(Redo);
-TEST_InputRouterRoutes_NOARGS(Cut);
-TEST_InputRouterRoutes_NOARGS(Copy);
-#if defined(OS_MACOSX)
-TEST_InputRouterRoutes_NOARGS(CopyToFindPboard);
-#endif
-TEST_InputRouterRoutes_NOARGS(Paste);
-TEST_InputRouterRoutes_NOARGS(PasteAndMatchStyle);
-TEST_InputRouterRoutes_NOARGS(Delete);
-TEST_InputRouterRoutes_NOARGS(SelectAll);
-TEST_InputRouterRoutes_NOARGS(Unselect);
TEST_InputRouterRoutes_NOARGS(Focus);
TEST_InputRouterRoutes_NOARGS(Blur);
TEST_InputRouterRoutes_NOARGS(LostCapture);
#undef TEST_InputRouterRoutes_NOARGS
+#define TEST_InputRouterRoutes_NOARGS_FromRFH(INPUTMSG) \
+ TEST_F(RenderWidgetHostTest, InputRouterRoutes##INPUTMSG) { \
+ host_->SetupForInputRouterTest(); \
+ host_->Send(new INPUTMSG(host_->GetRoutingID())); \
+ EXPECT_TRUE(host_->mock_input_router()->send_event_called_); \
+ }
+
+TEST_InputRouterRoutes_NOARGS_FromRFH(InputMsg_Undo);
+TEST_InputRouterRoutes_NOARGS_FromRFH(InputMsg_Redo);
+TEST_InputRouterRoutes_NOARGS_FromRFH(InputMsg_Cut);
+TEST_InputRouterRoutes_NOARGS_FromRFH(InputMsg_Copy);
+#if defined(OS_MACOSX)
+TEST_InputRouterRoutes_NOARGS_FromRFH(InputMsg_CopyToFindPboard);
+#endif
+TEST_InputRouterRoutes_NOARGS_FromRFH(InputMsg_Paste);
+TEST_InputRouterRoutes_NOARGS_FromRFH(InputMsg_PasteAndMatchStyle);
+TEST_InputRouterRoutes_NOARGS_FromRFH(InputMsg_Delete);
+TEST_InputRouterRoutes_NOARGS_FromRFH(InputMsg_SelectAll);
+TEST_InputRouterRoutes_NOARGS_FromRFH(InputMsg_Unselect);
+
+#undef TEST_InputRouterRoutes_NOARGS_FromRFH
+
TEST_F(RenderWidgetHostTest, InputRouterRoutesReplace) {
host_->SetupForInputRouterTest();
- host_->Replace(base::string16());
+ host_->Send(new InputMsg_Replace(host_->GetRoutingID(), base::string16()));
EXPECT_TRUE(host_->mock_input_router()->send_event_called_);
}
TEST_F(RenderWidgetHostTest, InputRouterRoutesReplaceMisspelling) {
host_->SetupForInputRouterTest();
- host_->ReplaceMisspelling(base::string16());
+ host_->Send(new InputMsg_ReplaceMisspelling(host_->GetRoutingID(),
+ base::string16()));
EXPECT_TRUE(host_->mock_input_router()->send_event_called_);
}
@@ -2405,7 +1256,7 @@ TEST_F(RenderWidgetHostTest, IgnoreInputEvent) {
EXPECT_FALSE(host_->mock_input_router()->sent_wheel_event_);
SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
- WebGestureEvent::Touchscreen);
+ blink::WebGestureDeviceTouchpad);
EXPECT_FALSE(host_->mock_input_router()->sent_gesture_event_);
PressTouchPoint(100, 100);
@@ -2504,4 +1355,86 @@ TEST_F(RenderWidgetHostTest, InputRouterReceivesHasTouchEventHandlers) {
EXPECT_TRUE(host_->mock_input_router()->message_received_);
}
+
+void CheckLatencyInfoComponentInMessage(RenderWidgetHostProcess* process,
+ int64 component_id,
+ WebInputEvent::Type input_type) {
+ const IPC::Message* message = process->sink().GetUniqueMessageMatching(
+ InputMsg_HandleInputEvent::ID);
+ ASSERT_TRUE(message);
+ InputMsg_HandleInputEvent::Param params;
+ EXPECT_TRUE(InputMsg_HandleInputEvent::Read(message, &params));
+ ui::LatencyInfo latency_info = params.b;
+ EXPECT_TRUE(latency_info.FindLatency(
+ ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT,
+ component_id,
+ NULL));
+ process->sink().ClearMessages();
+}
+
+// Tests that after input event passes through RWHI through ForwardXXXEvent()
+// or ForwardXXXEventWithLatencyInfo(), LatencyInfo component
+// ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT will always present in the
+// event's LatencyInfo.
+TEST_F(RenderWidgetHostTest, InputEventRWHLatencyComponent) {
+ host_->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, true));
+ process_->sink().ClearMessages();
+
+ // Tests RWHI::ForwardWheelEvent().
+ SimulateWheelEvent(-5, 0, 0, true);
+ CheckLatencyInfoComponentInMessage(
+ process_, GetLatencyComponentId(), WebInputEvent::MouseWheel);
+ SendInputEventACK(WebInputEvent::MouseWheel, INPUT_EVENT_ACK_STATE_CONSUMED);
+
+ // Tests RWHI::ForwardWheelEventWithLatencyInfo().
+ SimulateWheelEventWithLatencyInfo(-5, 0, 0, true, ui::LatencyInfo());
+ CheckLatencyInfoComponentInMessage(
+ process_, GetLatencyComponentId(), WebInputEvent::MouseWheel);
+ SendInputEventACK(WebInputEvent::MouseWheel, INPUT_EVENT_ACK_STATE_CONSUMED);
+
+ // Tests RWHI::ForwardMouseEvent().
+ SimulateMouseEvent(WebInputEvent::MouseMove);
+ CheckLatencyInfoComponentInMessage(
+ process_, GetLatencyComponentId(), WebInputEvent::MouseMove);
+ SendInputEventACK(WebInputEvent::MouseMove, INPUT_EVENT_ACK_STATE_CONSUMED);
+
+ // Tests RWHI::ForwardMouseEventWithLatencyInfo().
+ SimulateMouseEventWithLatencyInfo(WebInputEvent::MouseMove,
+ ui::LatencyInfo());
+ CheckLatencyInfoComponentInMessage(
+ process_, GetLatencyComponentId(), WebInputEvent::MouseMove);
+ SendInputEventACK(WebInputEvent::MouseMove, INPUT_EVENT_ACK_STATE_CONSUMED);
+
+ // Tests RWHI::ForwardGestureEvent().
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ CheckLatencyInfoComponentInMessage(
+ process_, GetLatencyComponentId(), WebInputEvent::GestureScrollBegin);
+
+ // Tests RWHI::ForwardGestureEventWithLatencyInfo().
+ SimulateGestureEventWithLatencyInfo(WebInputEvent::GestureScrollUpdate,
+ blink::WebGestureDeviceTouchscreen,
+ ui::LatencyInfo());
+ CheckLatencyInfoComponentInMessage(
+ process_, GetLatencyComponentId(), WebInputEvent::GestureScrollUpdate);
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_CONSUMED);
+
+ // Tests RWHI::ForwardTouchEventWithLatencyInfo().
+ PressTouchPoint(0, 1);
+ SendTouchEvent();
+ CheckLatencyInfoComponentInMessage(
+ process_, GetLatencyComponentId(), WebInputEvent::TouchStart);
+ SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_CONSUMED);
+}
+
+TEST_F(RenderWidgetHostTest, RendererExitedResetsInputRouter) {
+ // RendererExited will delete the view.
+ host_->SetView(new TestView(host_.get()));
+ host_->RendererExited(base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
+
+ // Make sure the input router is in a fresh state.
+ ASSERT_FALSE(host_->input_router()->HasPendingEvents());
+}
+
} // namespace content
diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_android.cc b/chromium/content/browser/renderer_host/render_widget_host_view_android.cc
index e3e7fb802de..f4217357c75 100644
--- a/chromium/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/chromium/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -6,19 +6,20 @@
#include <android/bitmap.h>
+#include "base/android/sys_utils.h"
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
+#include "base/metrics/histogram.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/worker_pool.h"
#include "cc/base/latency_info_swap_promise.h"
#include "cc/layers/delegated_frame_provider.h"
#include "cc/layers/delegated_renderer_layer.h"
#include "cc/layers/layer.h"
-#include "cc/layers/texture_layer.h"
#include "cc/output/compositor_frame.h"
#include "cc/output/compositor_frame_ack.h"
#include "cc/output/copy_output_request.h"
@@ -30,28 +31,38 @@
#include "content/browser/android/in_process/synchronous_compositor_impl.h"
#include "content/browser/android/overscroll_glow.h"
#include "content/browser/devtools/render_view_devtools_agent_host.h"
+#include "content/browser/gpu/compositor_util.h"
#include "content/browser/gpu/gpu_data_manager_impl.h"
#include "content/browser/gpu/gpu_process_host_ui_shim.h"
#include "content/browser/gpu/gpu_surface_tracker.h"
+#include "content/browser/media/media_web_contents_observer.h"
#include "content/browser/renderer_host/compositor_impl_android.h"
#include "content/browser/renderer_host/dip_util.h"
#include "content/browser/renderer_host/image_transport_factory_android.h"
#include "content/browser/renderer_host/input/synthetic_gesture_target_android.h"
+#include "content/browser/renderer_host/input/web_input_event_util.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
+#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/common/gpu/client/gl_helper.h"
#include "content/common/gpu/gpu_messages.h"
+#include "content/common/input/did_overscroll_params.h"
#include "content/common/input_messages.h"
#include "content/common/view_messages.h"
+#include "content/public/browser/browser_thread.h"
#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/common/content_switches.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
#include "gpu/config/gpu_driver_bug_workaround_type.h"
#include "skia/ext/image_operations.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "third_party/khronos/GLES2/gl2ext.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "ui/base/android/window_android.h"
+#include "ui/base/android/window_android_compositor.h"
+#include "ui/events/gesture_detection/gesture_config_helper.h"
+#include "ui/events/gesture_detection/motion_event.h"
#include "ui/gfx/android/device_display_info.h"
#include "ui/gfx/android/java_bitmap.h"
#include "ui/gfx/display.h"
@@ -64,23 +75,12 @@ namespace {
const int kUndefinedOutputSurfaceId = -1;
-void InsertSyncPointAndAckForCompositor(
- int renderer_host_id,
- uint32 output_surface_id,
- int route_id,
- const gpu::Mailbox& return_mailbox,
- const gfx::Size return_size) {
- cc::CompositorFrameAck ack;
- ack.gl_frame_data.reset(new cc::GLFrameData());
- if (!return_mailbox.IsZero()) {
- ack.gl_frame_data->mailbox = return_mailbox;
- ack.gl_frame_data->size = return_size;
- ack.gl_frame_data->sync_point =
- ImageTransportFactoryAndroid::GetInstance()->InsertSyncPoint();
- }
- RenderWidgetHostImpl::SendSwapCompositorFrameAck(
- route_id, output_surface_id, renderer_host_id, ack);
-}
+// Used to accomodate finite precision when comparing scaled viewport and
+// content widths. While this value may seem large, width=device-width on an N7
+// V1 saw errors of ~0.065 between computed window and content widths.
+const float kMobileViewportWidthEpsilon = 0.15f;
+
+static const char kAsyncReadBackString[] = "Compositing.CopyFromSurfaceTime";
// Sends an acknowledgement to the renderer of a processed IME event.
void SendImeEventAck(RenderWidgetHostImpl* host) {
@@ -91,46 +91,110 @@ void CopyFromCompositingSurfaceFinished(
const base::Callback<void(bool, const SkBitmap&)>& callback,
scoped_ptr<cc::SingleReleaseCallback> release_callback,
scoped_ptr<SkBitmap> bitmap,
+ const base::TimeTicks& start_time,
scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock,
bool result) {
bitmap_pixels_lock.reset();
release_callback->Run(0, false);
+ UMA_HISTOGRAM_TIMES(kAsyncReadBackString,
+ base::TimeTicks::Now() - start_time);
callback.Run(result, *bitmap);
}
-bool UsingDelegatedRenderer() {
- return CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kEnableDelegatedRenderer);
+ui::LatencyInfo CreateLatencyInfo(const blink::WebInputEvent& event) {
+ ui::LatencyInfo latency_info;
+ // The latency number should only be added if the timestamp is valid.
+ if (event.timeStampSeconds) {
+ const int64 time_micros = static_cast<int64>(
+ event.timeStampSeconds * base::Time::kMicrosecondsPerSecond);
+ latency_info.AddLatencyNumberWithTimestamp(
+ ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT,
+ 0,
+ 0,
+ base::TimeTicks() + base::TimeDelta::FromMicroseconds(time_micros),
+ 1);
+ }
+ return latency_info;
+}
+
+OverscrollGlow::DisplayParameters CreateOverscrollDisplayParameters(
+ const cc::CompositorFrameMetadata& frame_metadata) {
+ const float scale_factor =
+ frame_metadata.page_scale_factor * frame_metadata.device_scale_factor;
+
+ // Compute the size and offsets for each edge, where each effect is sized to
+ // the viewport and offset by the distance of each viewport edge to the
+ // respective content edge.
+ OverscrollGlow::DisplayParameters params;
+ params.size = gfx::ScaleSize(frame_metadata.viewport_size, scale_factor);
+ params.edge_offsets[EdgeEffect::EDGE_TOP] =
+ -frame_metadata.root_scroll_offset.y() * scale_factor;
+ params.edge_offsets[EdgeEffect::EDGE_LEFT] =
+ -frame_metadata.root_scroll_offset.x() * scale_factor;
+ params.edge_offsets[EdgeEffect::EDGE_BOTTOM] =
+ (frame_metadata.root_layer_size.height() -
+ frame_metadata.root_scroll_offset.y() -
+ frame_metadata.viewport_size.height()) * scale_factor;
+ params.edge_offsets[EdgeEffect::EDGE_RIGHT] =
+ (frame_metadata.root_layer_size.width() -
+ frame_metadata.root_scroll_offset.x() -
+ frame_metadata.viewport_size.width()) * scale_factor;
+ params.device_scale_factor = frame_metadata.device_scale_factor;
+
+ return params;
+}
+
+ui::GestureProvider::Config CreateGestureProviderConfig() {
+ ui::GestureProvider::Config config = ui::DefaultGestureProviderConfig();
+ config.disable_click_delay =
+ CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableClickDelay);
+ return config;
+}
+
+bool HasFixedPageScale(const cc::CompositorFrameMetadata& frame_metadata) {
+ return frame_metadata.min_page_scale_factor ==
+ frame_metadata.max_page_scale_factor;
+}
+
+bool HasMobileViewport(const cc::CompositorFrameMetadata& frame_metadata) {
+ float window_width_dip =
+ frame_metadata.page_scale_factor * frame_metadata.viewport_size.width();
+ float content_width_css = frame_metadata.root_layer_size.width();
+ return content_width_css <= window_width_dip + kMobileViewportWidthEpsilon;
}
} // anonymous namespace
+RenderWidgetHostViewAndroid::LastFrameInfo::LastFrameInfo(
+ uint32 output_id,
+ scoped_ptr<cc::CompositorFrame> output_frame)
+ : output_surface_id(output_id), frame(output_frame.Pass()) {}
+
+RenderWidgetHostViewAndroid::LastFrameInfo::~LastFrameInfo() {}
+
RenderWidgetHostViewAndroid::RenderWidgetHostViewAndroid(
RenderWidgetHostImpl* widget_host,
ContentViewCoreImpl* content_view_core)
: host_(widget_host),
needs_begin_frame_(false),
- are_layers_attached_(true),
+ is_showing_(!widget_host->is_hidden()),
content_view_core_(NULL),
ime_adapter_android_(this),
cached_background_color_(SK_ColorWHITE),
- texture_id_in_layer_(0),
last_output_surface_id_(kUndefinedOutputSurfaceId),
weak_ptr_factory_(this),
- overscroll_effect_enabled_(
- !CommandLine::ForCurrentProcess()->
- HasSwitch(switches::kDisableOverscrollEdgeEffect)),
+ overscroll_effect_enabled_(!CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableOverscrollEdgeEffect)),
overscroll_effect_(OverscrollGlow::Create(overscroll_effect_enabled_)),
+ gesture_provider_(CreateGestureProviderConfig(), this),
flush_input_requested_(false),
accelerated_surface_route_id_(0),
using_synchronous_compositor_(SynchronousCompositorImpl::FromID(
widget_host->GetProcess()->GetID(),
- widget_host->GetRoutingID()) != NULL) {
- if (!UsingDelegatedRenderer()) {
- texture_layer_ = cc::TextureLayer::Create(NULL);
- layer_ = texture_layer_;
- }
-
+ widget_host->GetRoutingID()) != NULL),
+ frame_evictor_(new DelegatedFrameEvictor(this)),
+ locks_on_frame_count_(0),
+ observing_root_window_(false) {
host_->SetView(this);
SetContentViewCore(content_view_core);
ImageTransportFactoryAndroid::AddObserver(this);
@@ -140,14 +204,6 @@ RenderWidgetHostViewAndroid::~RenderWidgetHostViewAndroid() {
ImageTransportFactoryAndroid::RemoveObserver(this);
SetContentViewCore(NULL);
DCHECK(ack_callbacks_.empty());
- if (texture_id_in_layer_) {
- ImageTransportFactoryAndroid::GetInstance()->DeleteTexture(
- texture_id_in_layer_);
- }
-
- if (texture_layer_.get())
- texture_layer_->ClearClient();
-
if (resource_collection_.get())
resource_collection_->SetClient(NULL);
}
@@ -162,8 +218,6 @@ bool RenderWidgetHostViewAndroid::OnMessageReceived(
OnDidChangeBodyBackgroundColor)
IPC_MESSAGE_HANDLER(ViewHostMsg_SetNeedsBeginFrame,
OnSetNeedsBeginFrame)
- IPC_MESSAGE_HANDLER(ViewHostMsg_TextInputStateChanged,
- OnTextInputStateChanged)
IPC_MESSAGE_HANDLER(ViewHostMsg_SmartClipDataExtracted,
OnSmartClipDataExtracted)
IPC_MESSAGE_UNHANDLED(handled = false)
@@ -196,8 +250,11 @@ void RenderWidgetHostViewAndroid::WasShown() {
host_->WasShown();
- if (content_view_core_ && !using_synchronous_compositor_)
+ if (content_view_core_ && !using_synchronous_compositor_) {
content_view_core_->GetWindowAndroid()->AddObserver(this);
+ content_view_core_->GetWindowAndroid()->RequestVSyncUpdate();
+ observing_root_window_ = true;
+ }
}
void RenderWidgetHostViewAndroid::WasHidden() {
@@ -210,8 +267,10 @@ void RenderWidgetHostViewAndroid::WasHidden() {
// utilization.
host_->WasHidden();
- if (content_view_core_ && !using_synchronous_compositor_)
+ if (content_view_core_ && !using_synchronous_compositor_) {
content_view_core_->GetWindowAndroid()->RemoveObserver(this);
+ observing_root_window_ = false;
+ }
}
void RenderWidgetHostViewAndroid::WasResized() {
@@ -222,86 +281,48 @@ void RenderWidgetHostViewAndroid::SetSize(const gfx::Size& size) {
// Ignore the given size as only the Java code has the power to
// resize the view on Android.
default_size_ = size;
- WasResized();
}
void RenderWidgetHostViewAndroid::SetBounds(const gfx::Rect& rect) {
SetSize(rect.size());
}
-blink::WebGLId RenderWidgetHostViewAndroid::GetScaledContentTexture(
+void RenderWidgetHostViewAndroid::GetScaledContentBitmap(
float scale,
- gfx::Size* out_size) {
- gfx::Size size(gfx::ToCeiledSize(
- gfx::ScaleSize(texture_size_in_layer_, scale)));
-
- if (!CompositorImpl::IsInitialized() ||
- texture_id_in_layer_ == 0 ||
- texture_size_in_layer_.IsEmpty() ||
- size.IsEmpty()) {
- if (out_size)
- out_size->SetSize(0, 0);
-
- return 0;
+ SkBitmap::Config bitmap_config,
+ gfx::Rect src_subrect,
+ const base::Callback<void(bool, const SkBitmap&)>& result_callback) {
+ if (!IsSurfaceAvailableForCopy()) {
+ result_callback.Run(false, SkBitmap());
+ return;
}
- if (out_size)
- *out_size = size;
-
- GLHelper* helper = ImageTransportFactoryAndroid::GetInstance()->GetGLHelper();
- return helper->CopyAndScaleTexture(texture_id_in_layer_,
- texture_size_in_layer_,
- size,
- true,
- GLHelper::SCALER_QUALITY_FAST);
-}
-
-bool RenderWidgetHostViewAndroid::PopulateBitmapWithContents(jobject jbitmap) {
- if (!CompositorImpl::IsInitialized() ||
- texture_id_in_layer_ == 0 ||
- texture_size_in_layer_.IsEmpty())
- return false;
-
- gfx::JavaBitmap bitmap(jbitmap);
-
- // TODO(dtrainor): Eventually add support for multiple formats here.
- DCHECK(bitmap.format() == ANDROID_BITMAP_FORMAT_RGBA_8888);
-
- GLHelper* helper = ImageTransportFactoryAndroid::GetInstance()->GetGLHelper();
-
- blink::WebGLId texture = helper->CopyAndScaleTexture(
- texture_id_in_layer_,
- texture_size_in_layer_,
- bitmap.size(),
- true,
- GLHelper::SCALER_QUALITY_FAST);
- if (texture == 0)
- return false;
-
- helper->ReadbackTextureSync(texture,
- gfx::Rect(bitmap.size()),
- static_cast<unsigned char*> (bitmap.pixels()));
-
- blink::WebGraphicsContext3D* context =
- ImageTransportFactoryAndroid::GetInstance()->GetContext3D();
- context->deleteTexture(texture);
-
- return true;
+ gfx::Size bounds = layer_->bounds();
+ if (src_subrect.IsEmpty())
+ src_subrect = gfx::Rect(bounds);
+ DCHECK_LE(src_subrect.width() + src_subrect.x(), bounds.width());
+ DCHECK_LE(src_subrect.height() + src_subrect.y(), bounds.height());
+ const gfx::Display& display =
+ gfx::Screen::GetNativeScreen()->GetPrimaryDisplay();
+ float device_scale_factor = display.device_scale_factor();
+ DCHECK_GT(device_scale_factor, 0);
+ gfx::Size dst_size(
+ gfx::ToCeiledSize(gfx::ScaleSize(bounds, scale / device_scale_factor)));
+ CopyFromCompositingSurface(
+ src_subrect, dst_size, result_callback, bitmap_config);
}
bool RenderWidgetHostViewAndroid::HasValidFrame() const {
if (!content_view_core_)
return false;
+ if (!layer_)
+ return false;
+
if (texture_size_in_layer_.IsEmpty())
return false;
- if (UsingDelegatedRenderer()) {
- if (!delegated_renderer_layer_.get())
- return false;
- } else {
- if (texture_id_in_layer_ == 0)
- return false;
- }
+ if (!frame_evictor_->HasFrame())
+ return false;
return true;
}
@@ -322,7 +343,6 @@ RenderWidgetHostViewAndroid::GetNativeViewAccessible() {
}
void RenderWidgetHostViewAndroid::MovePluginWindows(
- const gfx::Vector2d& scroll_offset,
const std::vector<WebPluginGeometry>& moves) {
// We don't have plugin windows on Android. Do nothing. Note: this is called
// from RenderWidgetHost::OnUpdateRect which is itself invoked while
@@ -332,7 +352,6 @@ void RenderWidgetHostViewAndroid::MovePluginWindows(
void RenderWidgetHostViewAndroid::Focus() {
host_->Focus();
host_->SetInputMethodActive(true);
- ResetClipping();
if (overscroll_effect_enabled_)
overscroll_effect_->Enable();
}
@@ -356,22 +375,26 @@ bool RenderWidgetHostViewAndroid::IsSurfaceAvailableForCopy() const {
}
void RenderWidgetHostViewAndroid::Show() {
- if (are_layers_attached_)
+ if (is_showing_)
return;
- are_layers_attached_ = true;
- AttachLayers();
+ is_showing_ = true;
+ if (layer_)
+ layer_->SetHideLayerAndSubtree(false);
+ frame_evictor_->SetVisible(true);
WasShown();
}
void RenderWidgetHostViewAndroid::Hide() {
- if (!are_layers_attached_)
+ if (!is_showing_)
return;
- are_layers_attached_ = false;
- RemoveLayers();
+ is_showing_ = false;
+ if (layer_ && locks_on_frame_count_ == 0)
+ layer_->SetHideLayerAndSubtree(true);
+ frame_evictor_->SetVisible(false);
WasHidden();
}
@@ -379,18 +402,70 @@ bool RenderWidgetHostViewAndroid::IsShowing() {
// ContentViewCoreImpl represents the native side of the Java
// ContentViewCore. It being NULL means that it is not attached
// to the View system yet, so we treat this RWHVA as hidden.
- return are_layers_attached_ && content_view_core_;
+ return is_showing_ && content_view_core_;
+}
+
+void RenderWidgetHostViewAndroid::LockCompositingSurface() {
+ DCHECK(HasValidFrame());
+ DCHECK(host_);
+ DCHECK(frame_evictor_->HasFrame());
+ frame_evictor_->LockFrame();
+ locks_on_frame_count_++;
+}
+
+void RenderWidgetHostViewAndroid::UnlockCompositingSurface() {
+ if (!frame_evictor_->HasFrame() || locks_on_frame_count_ == 0)
+ return;
+
+ DCHECK(HasValidFrame());
+ frame_evictor_->UnlockFrame();
+ locks_on_frame_count_--;
+
+ if (locks_on_frame_count_ == 0) {
+ if (last_frame_info_) {
+ InternalSwapCompositorFrame(last_frame_info_->output_surface_id,
+ last_frame_info_->frame.Pass());
+ last_frame_info_.reset();
+ }
+
+ if (!is_showing_ && layer_)
+ layer_->SetHideLayerAndSubtree(true);
+ }
+}
+
+void RenderWidgetHostViewAndroid::SetTextSurroundingSelectionCallback(
+ const TextSurroundingSelectionCallback& callback) {
+ // Only one outstanding request is allowed at any given time.
+ DCHECK(!callback.is_null());
+ text_surrounding_selection_callback_ = callback;
+}
+
+void RenderWidgetHostViewAndroid::OnTextSurroundingSelectionResponse(
+ const base::string16& content,
+ size_t start_offset,
+ size_t end_offset) {
+ if (text_surrounding_selection_callback_.is_null())
+ return;
+ text_surrounding_selection_callback_.Run(content, start_offset, end_offset);
+ text_surrounding_selection_callback_.Reset();
+}
+
+void RenderWidgetHostViewAndroid::ReleaseLocksOnSurface() {
+ if (!frame_evictor_->HasFrame()) {
+ DCHECK_EQ(locks_on_frame_count_, 0u);
+ return;
+ }
+ while (locks_on_frame_count_ > 0) {
+ UnlockCompositingSurface();
+ }
+ RunAckCallbacks();
}
gfx::Rect RenderWidgetHostViewAndroid::GetViewBounds() const {
if (!content_view_core_)
return gfx::Rect(default_size_);
- gfx::Size size = content_view_core_->GetViewportSizeDip();
- gfx::Size offset = content_view_core_->GetViewportSizeOffsetDip();
- size.Enlarge(-offset.width(), -offset.height());
-
- return gfx::Rect(size);
+ return gfx::Rect(content_view_core_->GetViewSize());
}
gfx::Size RenderWidgetHostViewAndroid::GetPhysicalBackingSize() const {
@@ -416,24 +491,17 @@ void RenderWidgetHostViewAndroid::SetIsLoading(bool is_loading) {
// is TabContentsDelegate.
}
-void RenderWidgetHostViewAndroid::TextInputTypeChanged(
- ui::TextInputType type,
- ui::TextInputMode input_mode,
- bool can_compose_inline) {
- // Unused on Android, which uses OnTextInputChanged instead.
-}
-
-int RenderWidgetHostViewAndroid::GetNativeImeAdapter() {
- return reinterpret_cast<int>(&ime_adapter_android_);
+long RenderWidgetHostViewAndroid::GetNativeImeAdapter() {
+ return reinterpret_cast<intptr_t>(&ime_adapter_android_);
}
-void RenderWidgetHostViewAndroid::OnTextInputStateChanged(
+void RenderWidgetHostViewAndroid::TextInputStateChanged(
const ViewHostMsg_TextInputState_Params& params) {
- // If an acknowledgement is required for this event, regardless of how we exit
- // from this method, we must acknowledge that we processed the input state
- // change.
+ // If the change is not originated from IME (e.g. Javascript, autofill),
+ // send back the renderer an acknowledgement, regardless of how we exit from
+ // this method.
base::ScopedClosureRunner ack_caller;
- if (params.require_ack)
+ if (params.is_non_ime_change)
ack_caller.Reset(base::Bind(&SendImeEventAck, host_));
if (!IsShowing())
@@ -444,7 +512,7 @@ void RenderWidgetHostViewAndroid::OnTextInputStateChanged(
static_cast<int>(params.type),
params.value, params.selection_start, params.selection_end,
params.composition_start, params.composition_end,
- params.show_ime_if_needed, params.require_ack);
+ params.show_ime_if_needed, params.is_non_ime_change);
}
void RenderWidgetHostViewAndroid::OnDidChangeBodyBackgroundColor(
@@ -457,36 +525,16 @@ void RenderWidgetHostViewAndroid::OnDidChangeBodyBackgroundColor(
content_view_core_->OnBackgroundColorChanged(color);
}
-void RenderWidgetHostViewAndroid::SendBeginFrame(
- const cc::BeginFrameArgs& args) {
- TRACE_EVENT0("cc", "RenderWidgetHostViewAndroid::SendBeginFrame");
- if (!host_)
+void RenderWidgetHostViewAndroid::OnSetNeedsBeginFrame(bool enabled) {
+ if (enabled == needs_begin_frame_)
return;
- if (flush_input_requested_) {
- flush_input_requested_ = false;
- host_->FlushInput();
- content_view_core_->RemoveBeginFrameSubscriber();
- }
-
- host_->Send(new ViewMsg_BeginFrame(host_->GetRoutingID(), args));
-}
-
-void RenderWidgetHostViewAndroid::OnSetNeedsBeginFrame(
- bool enabled) {
TRACE_EVENT1("cc", "RenderWidgetHostViewAndroid::OnSetNeedsBeginFrame",
"enabled", enabled);
- // ContentViewCoreImpl handles multiple subscribers to the BeginFrame, so
- // we have to make sure calls to ContentViewCoreImpl's
- // {Add,Remove}BeginFrameSubscriber are balanced, even if
- // RenderWidgetHostViewAndroid's may not be.
- if (content_view_core_ && needs_begin_frame_ != enabled) {
- if (enabled)
- content_view_core_->AddBeginFrameSubscriber();
- else
- content_view_core_->RemoveBeginFrameSubscriber();
- needs_begin_frame_ = enabled;
- }
+ if (content_view_core_ && enabled)
+ content_view_core_->GetWindowAndroid()->RequestVSyncUpdate();
+
+ needs_begin_frame_ = enabled;
}
void RenderWidgetHostViewAndroid::OnStartContentIntent(
@@ -496,7 +544,7 @@ void RenderWidgetHostViewAndroid::OnStartContentIntent(
}
void RenderWidgetHostViewAndroid::OnSmartClipDataExtracted(
- const string16& result) {
+ const base::string16& result) {
// Custom serialization over IPC isn't allowed normally for security reasons.
// Since this feature is only used in (single-process) WebView, there are no
// security issues. Enforce that it's only called in single process mode.
@@ -505,16 +553,51 @@ void RenderWidgetHostViewAndroid::OnSmartClipDataExtracted(
content_view_core_->OnSmartClipDataExtracted(result);
}
+bool RenderWidgetHostViewAndroid::OnTouchEvent(
+ const ui::MotionEvent& event) {
+ if (!host_)
+ return false;
+
+ if (!gesture_provider_.OnTouchEvent(event))
+ return false;
+
+ // Short-circuit touch forwarding if no touch handlers exist.
+ if (!host_->ShouldForwardTouchEvent()) {
+ const bool event_consumed = false;
+ gesture_provider_.OnTouchEventAck(event_consumed);
+ return true;
+ }
+
+ SendTouchEvent(CreateWebTouchEventFromMotionEvent(event));
+ return true;
+}
+
+void RenderWidgetHostViewAndroid::ResetGestureDetection() {
+ const ui::MotionEvent* current_down_event =
+ gesture_provider_.GetCurrentDownEvent();
+ if (!current_down_event)
+ return;
+
+ scoped_ptr<ui::MotionEvent> cancel_event = current_down_event->Cancel();
+ DCHECK(cancel_event);
+ OnTouchEvent(*cancel_event);
+}
+
+void RenderWidgetHostViewAndroid::SetDoubleTapSupportEnabled(bool enabled) {
+ gesture_provider_.SetDoubleTapSupportForPlatformEnabled(enabled);
+}
+
+void RenderWidgetHostViewAndroid::SetMultiTouchZoomSupportEnabled(
+ bool enabled) {
+ gesture_provider_.SetMultiTouchZoomSupportEnabled(enabled);
+}
+
void RenderWidgetHostViewAndroid::ImeCancelComposition() {
ime_adapter_android_.CancelComposition();
}
-void RenderWidgetHostViewAndroid::DidUpdateBackingStore(
- const gfx::Rect& scroll_rect,
- const gfx::Vector2d& scroll_delta,
- const std::vector<gfx::Rect>& copy_rects,
- const ui::LatencyInfo& latency_info) {
- NOTIMPLEMENTED();
+void RenderWidgetHostViewAndroid::FocusedNodeChanged(bool is_editable_node) {
+ ime_adapter_android_.FocusedNodeChanged(is_editable_node);
}
void RenderWidgetHostViewAndroid::RenderProcessGone(
@@ -553,7 +636,7 @@ void RenderWidgetHostViewAndroid::SelectionChanged(const base::string16& text,
return;
}
- std::string utf8_selection = UTF16ToUTF8(text.substr(pos, n));
+ std::string utf8_selection = base::UTF16ToUTF8(text.substr(pos, n));
content_view_core_->OnSelectionChanged(utf8_selection);
}
@@ -568,56 +651,68 @@ void RenderWidgetHostViewAndroid::SelectionBoundsChanged(
void RenderWidgetHostViewAndroid::ScrollOffsetChanged() {
}
-BackingStore* RenderWidgetHostViewAndroid::AllocBackingStore(
- const gfx::Size& size) {
- NOTIMPLEMENTED();
- return NULL;
-}
-
-void RenderWidgetHostViewAndroid::SetBackground(const SkBitmap& background) {
- RenderWidgetHostViewBase::SetBackground(background);
- host_->Send(new ViewMsg_SetBackground(host_->GetRoutingID(), background));
+void RenderWidgetHostViewAndroid::SetBackgroundOpaque(bool opaque) {
+ RenderWidgetHostViewBase::SetBackgroundOpaque(opaque);
+ host_->SetBackgroundOpaque(opaque);
}
void RenderWidgetHostViewAndroid::CopyFromCompositingSurface(
const gfx::Rect& src_subrect,
const gfx::Size& dst_size,
- const base::Callback<void(bool, const SkBitmap&)>& callback) {
+ const base::Callback<void(bool, const SkBitmap&)>& callback,
+ const SkBitmap::Config bitmap_config) {
+ if (!IsReadbackConfigSupported(bitmap_config)) {
+ callback.Run(false, SkBitmap());
+ return;
+ }
+ base::TimeTicks start_time = base::TimeTicks::Now();
if (!using_synchronous_compositor_ && !IsSurfaceAvailableForCopy()) {
callback.Run(false, SkBitmap());
return;
}
-
const gfx::Display& display =
gfx::Screen::GetNativeScreen()->GetPrimaryDisplay();
float device_scale_factor = display.device_scale_factor();
-
- DCHECK_EQ(device_scale_factor,
- ui::GetImageScale(GetScaleFactorForView(this)));
-
- const gfx::Size& dst_size_in_pixel = ConvertViewSizeToPixel(this, dst_size);
+ gfx::Size dst_size_in_pixel =
+ ConvertRectToPixel(device_scale_factor, gfx::Rect(dst_size)).size();
gfx::Rect src_subrect_in_pixel =
ConvertRectToPixel(device_scale_factor, src_subrect);
if (using_synchronous_compositor_) {
- SynchronousCopyContents(src_subrect_in_pixel, dst_size_in_pixel, callback);
+ SynchronousCopyContents(src_subrect_in_pixel, dst_size_in_pixel, callback,
+ bitmap_config);
+ UMA_HISTOGRAM_TIMES("Compositing.CopyFromSurfaceTimeSynchronous",
+ base::TimeTicks::Now() - start_time);
return;
}
scoped_ptr<cc::CopyOutputRequest> request;
- if (src_subrect_in_pixel.size() == dst_size_in_pixel) {
- request = cc::CopyOutputRequest::CreateBitmapRequest(base::Bind(
- &RenderWidgetHostViewAndroid::PrepareBitmapCopyOutputResult,
- dst_size_in_pixel,
- callback));
- } else {
- request = cc::CopyOutputRequest::CreateRequest(base::Bind(
- &RenderWidgetHostViewAndroid::PrepareTextureCopyOutputResult,
- dst_size_in_pixel,
- callback));
- }
+ scoped_refptr<cc::Layer> readback_layer;
+ DCHECK(content_view_core_);
+ DCHECK(content_view_core_->GetWindowAndroid());
+ ui::WindowAndroidCompositor* compositor =
+ content_view_core_->GetWindowAndroid()->GetCompositor();
+ DCHECK(compositor);
+ DCHECK(frame_provider_);
+ scoped_refptr<cc::DelegatedRendererLayer> delegated_layer =
+ cc::DelegatedRendererLayer::Create(frame_provider_);
+ delegated_layer->SetBounds(content_size_in_layer_);
+ delegated_layer->SetHideLayerAndSubtree(true);
+ delegated_layer->SetIsDrawable(true);
+ delegated_layer->SetContentsOpaque(true);
+ compositor->AttachLayerForReadback(delegated_layer);
+
+ readback_layer = delegated_layer;
+ request = cc::CopyOutputRequest::CreateRequest(
+ base::Bind(&RenderWidgetHostViewAndroid::
+ PrepareTextureCopyOutputResultForDelegatedReadback,
+ dst_size_in_pixel,
+ bitmap_config,
+ start_time,
+ readback_layer,
+ callback));
request->set_area(src_subrect_in_pixel);
- layer_->RequestCopyOfOutput(request.Pass());
+ readback_layer->RequestCopyOfOutput(request.Pass());
}
void RenderWidgetHostViewAndroid::CopyFromCompositingSurfaceToVideoFrame(
@@ -646,11 +741,9 @@ RenderWidgetHostViewAndroid::CreateSyntheticGestureTarget() {
host_, content_view_core_->CreateTouchEventSynthesizer()));
}
-void RenderWidgetHostViewAndroid::OnAcceleratedCompositingStateChange() {
-}
-
void RenderWidgetHostViewAndroid::SendDelegatedFrameAck(
uint32 output_surface_id) {
+ DCHECK(host_);
cc::CompositorFrameAck ack;
if (resource_collection_.get())
resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources);
@@ -660,15 +753,30 @@ void RenderWidgetHostViewAndroid::SendDelegatedFrameAck(
ack);
}
+void RenderWidgetHostViewAndroid::SendReturnedDelegatedResources(
+ uint32 output_surface_id) {
+ DCHECK(resource_collection_);
+
+ cc::CompositorFrameAck ack;
+ resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources);
+ DCHECK(!ack.resources.empty());
+
+ RenderWidgetHostImpl::SendReclaimCompositorResources(
+ host_->GetRoutingID(),
+ output_surface_id,
+ host_->GetProcess()->GetID(),
+ ack);
+}
+
void RenderWidgetHostViewAndroid::UnusedResourcesAreAvailable() {
- // TODO(danakj): If no ack is pending, collect and send resources now.
+ if (ack_callbacks_.size())
+ return;
+ SendReturnedDelegatedResources(last_output_surface_id_);
}
void RenderWidgetHostViewAndroid::DestroyDelegatedContent() {
- if (are_layers_attached_)
- RemoveLayers();
+ RemoveLayers();
frame_provider_ = NULL;
- delegated_renderer_layer_ = NULL;
layer_ = NULL;
}
@@ -678,14 +786,12 @@ void RenderWidgetHostViewAndroid::SwapDelegatedFrame(
bool has_content = !texture_size_in_layer_.IsEmpty();
if (output_surface_id != last_output_surface_id_) {
- // TODO(danakj): Lose all resources and send them back here, such as:
- // resource_collection_->LoseAllResources();
- // 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;
}
DestroyDelegatedContent();
@@ -693,6 +799,14 @@ void RenderWidgetHostViewAndroid::SwapDelegatedFrame(
last_output_surface_id_ = output_surface_id;
}
+ // DelegatedRendererLayerImpl applies the inverse device_scale_factor of the
+ // renderer frame, assuming that the browser compositor will scale
+ // it back up to device scale. But on Android we put our browser layers in
+ // physical pixels and set our browser CC device_scale_factor to 1, so this
+ // suppresses the transform. This line may need to be removed when fixing
+ // http://crbug.com/384134 or http://crbug.com/310763
+ frame_data->device_scale_factor = 1.0f;
+
if (!has_content) {
DestroyDelegatedContent();
} else {
@@ -702,26 +816,21 @@ void RenderWidgetHostViewAndroid::SwapDelegatedFrame(
}
if (!frame_provider_ ||
texture_size_in_layer_ != frame_provider_->frame_size()) {
- if (are_layers_attached_)
- RemoveLayers();
+ RemoveLayers();
frame_provider_ = new cc::DelegatedFrameProvider(
resource_collection_.get(), frame_data.Pass());
- delegated_renderer_layer_ =
- cc::DelegatedRendererLayer::Create(frame_provider_);
- layer_ = delegated_renderer_layer_;
- if (are_layers_attached_)
- AttachLayers();
+ layer_ = cc::DelegatedRendererLayer::Create(frame_provider_);
+ AttachLayers();
} else {
frame_provider_->SetFrameData(frame_data.Pass());
}
}
- if (delegated_renderer_layer_.get()) {
- delegated_renderer_layer_->SetDisplaySize(texture_size_in_layer_);
- delegated_renderer_layer_->SetIsDrawable(true);
- delegated_renderer_layer_->SetContentsOpaque(true);
- delegated_renderer_layer_->SetBounds(content_size_in_layer_);
- delegated_renderer_layer_->SetNeedsDisplay();
+ if (layer_.get()) {
+ layer_->SetIsDrawable(true);
+ layer_->SetContentsOpaque(true);
+ layer_->SetBounds(content_size_in_layer_);
+ layer_->SetNeedsDisplay();
}
base::Closure ack_callback =
@@ -729,10 +838,9 @@ void RenderWidgetHostViewAndroid::SwapDelegatedFrame(
weak_ptr_factory_.GetWeakPtr(),
output_surface_id);
+ ack_callbacks_.push(ack_callback);
if (host_->is_hidden())
- ack_callback.Run();
- else
- ack_callbacks_.push(ack_callback);
+ RunAckCallbacks();
}
void RenderWidgetHostViewAndroid::ComputeContentsSize(
@@ -746,68 +854,78 @@ void RenderWidgetHostViewAndroid::ComputeContentsSize(
content_size_in_layer_ =
gfx::Size(texture_size_in_layer_.width() - offset.x(),
texture_size_in_layer_.height() - offset.y());
- // Content size changes should be reflected in associated animation effects.
- UpdateAnimationSize(frame_metadata);
+
+ overscroll_effect_->UpdateDisplayParameters(
+ CreateOverscrollDisplayParameters(frame_metadata));
}
-void RenderWidgetHostViewAndroid::OnSwapCompositorFrame(
+void RenderWidgetHostViewAndroid::InternalSwapCompositorFrame(
uint32 output_surface_id,
scoped_ptr<cc::CompositorFrame> frame) {
- // Always let ContentViewCore know about the new frame first, so it can decide
- // to schedule a Draw immediately when it sees the texture layer invalidation.
- UpdateContentViewCoreFrameMetadata(frame->metadata);
-
- if (frame->delegated_frame_data) {
- DCHECK(UsingDelegatedRenderer());
-
- DCHECK(frame->delegated_frame_data);
- DCHECK(!frame->delegated_frame_data->render_pass_list.empty());
-
- cc::RenderPass* root_pass =
- frame->delegated_frame_data->render_pass_list.back();
- texture_size_in_layer_ = root_pass->output_rect.size();
- ComputeContentsSize(frame->metadata);
-
- SwapDelegatedFrame(output_surface_id, frame->delegated_frame_data.Pass());
+ if (!frame->delegated_frame_data) {
+ LOG(ERROR) << "Non-delegated renderer path no longer supported";
return;
}
- DCHECK(!UsingDelegatedRenderer());
-
- if (!frame->gl_frame_data || frame->gl_frame_data->mailbox.IsZero())
+ if (locks_on_frame_count_ > 0) {
+ DCHECK(HasValidFrame());
+ RetainFrame(output_surface_id, frame.Pass());
return;
+ }
- if (output_surface_id != last_output_surface_id_) {
- current_mailbox_ = gpu::Mailbox();
- last_output_surface_id_ = kUndefinedOutputSurfaceId;
+ if (layer_ && layer_->layer_tree_host()) {
+ for (size_t i = 0; i < frame->metadata.latency_info.size(); i++) {
+ scoped_ptr<cc::SwapPromise> swap_promise(
+ new cc::LatencyInfoSwapPromise(frame->metadata.latency_info[i]));
+ layer_->layer_tree_host()->QueueSwapPromise(swap_promise.Pass());
+ }
}
- base::Closure callback = base::Bind(&InsertSyncPointAndAckForCompositor,
- host_->GetProcess()->GetID(),
- output_surface_id,
- host_->GetRoutingID(),
- current_mailbox_,
- texture_size_in_layer_);
- ImageTransportFactoryAndroid::GetInstance()->WaitSyncPoint(
- frame->gl_frame_data->sync_point);
+ DCHECK(!frame->delegated_frame_data->render_pass_list.empty());
- texture_size_in_layer_ = frame->gl_frame_data->size;
+ cc::RenderPass* root_pass =
+ frame->delegated_frame_data->render_pass_list.back();
+ texture_size_in_layer_ = root_pass->output_rect.size();
ComputeContentsSize(frame->metadata);
- if (layer_->layer_tree_host()) {
- scoped_ptr<cc::SwapPromise> swap_promise(
- new cc::LatencyInfoSwapPromise(frame->metadata.latency_info));
- layer_->layer_tree_host()->QueueSwapPromise(swap_promise.Pass());
+ SwapDelegatedFrame(output_surface_id, frame->delegated_frame_data.Pass());
+ frame_evictor_->SwappedFrame(!host_->is_hidden());
+
+ OnFrameMetadataUpdated(frame->metadata);
+}
+
+void RenderWidgetHostViewAndroid::OnSwapCompositorFrame(
+ uint32 output_surface_id,
+ scoped_ptr<cc::CompositorFrame> frame) {
+ InternalSwapCompositorFrame(output_surface_id, frame.Pass());
+}
+
+void RenderWidgetHostViewAndroid::RetainFrame(
+ uint32 output_surface_id,
+ scoped_ptr<cc::CompositorFrame> frame) {
+ DCHECK(locks_on_frame_count_);
+
+ // Store the incoming frame so that it can be swapped when all the locks have
+ // been released. If there is already a stored frame, then replace and skip
+ // the previous one but make sure we still eventually send the ACK. Holding
+ // the ACK also blocks the renderer when its max_frames_pending is reached.
+ if (last_frame_info_) {
+ base::Closure ack_callback =
+ base::Bind(&RenderWidgetHostViewAndroid::SendDelegatedFrameAck,
+ weak_ptr_factory_.GetWeakPtr(),
+ last_frame_info_->output_surface_id);
+
+ ack_callbacks_.push(ack_callback);
}
- BuffersSwapped(frame->gl_frame_data->mailbox, output_surface_id, callback);
+ last_frame_info_.reset(new LastFrameInfo(output_surface_id, frame.Pass()));
}
void RenderWidgetHostViewAndroid::SynchronousFrameMetadata(
const cc::CompositorFrameMetadata& frame_metadata) {
// This is a subset of OnSwapCompositorFrame() used in the synchronous
// compositor flow.
- UpdateContentViewCoreFrameMetadata(frame_metadata);
+ OnFrameMetadataUpdated(frame_metadata);
ComputeContentsSize(frame_metadata);
// DevTools ScreenCast support for Android WebView.
@@ -824,10 +942,16 @@ void RenderWidgetHostViewAndroid::SynchronousFrameMetadata(
}
}
+void RenderWidgetHostViewAndroid::SetOverlayVideoMode(bool enabled) {
+ if (layer_)
+ layer_->SetContentsOpaque(!enabled);
+}
+
void RenderWidgetHostViewAndroid::SynchronousCopyContents(
const gfx::Rect& src_subrect_in_pixel,
const gfx::Size& dst_size_in_pixel,
- const base::Callback<void(bool, const SkBitmap&)>& callback) {
+ const base::Callback<void(bool, const SkBitmap&)>& callback,
+ const SkBitmap::Config config) {
SynchronousCompositor* compositor =
SynchronousCompositorImpl::FromID(host_->GetProcess()->GetID(),
host_->GetRoutingID());
@@ -837,7 +961,7 @@ void RenderWidgetHostViewAndroid::SynchronousCopyContents(
}
SkBitmap bitmap;
- bitmap.setConfig(SkBitmap::kARGB_8888_Config,
+ bitmap.setConfig(config,
dst_size_in_pixel.width(),
dst_size_in_pixel.height());
bitmap.allocPixels();
@@ -849,21 +973,38 @@ void RenderWidgetHostViewAndroid::SynchronousCopyContents(
callback.Run(true, bitmap);
}
-void RenderWidgetHostViewAndroid::UpdateContentViewCoreFrameMetadata(
+void RenderWidgetHostViewAndroid::OnFrameMetadataUpdated(
const cc::CompositorFrameMetadata& frame_metadata) {
- if (content_view_core_) {
- // All offsets and sizes are in CSS pixels.
- content_view_core_->UpdateFrameInfo(
- frame_metadata.root_scroll_offset,
- frame_metadata.page_scale_factor,
- gfx::Vector2dF(frame_metadata.min_page_scale_factor,
- frame_metadata.max_page_scale_factor),
- frame_metadata.root_layer_size,
- frame_metadata.viewport_size,
- frame_metadata.location_bar_offset,
- frame_metadata.location_bar_content_translation,
- frame_metadata.overdraw_bottom_height);
+
+ // Disable double tap zoom for pages that have a width=device-width or
+ // narrower viewport (indicating that this is a mobile-optimized or responsive
+ // web design, so text will be legible without zooming). Also disable
+ // double tap and pinch for pages that prevent zooming in or out.
+ bool has_mobile_viewport = HasMobileViewport(frame_metadata);
+ bool has_fixed_page_scale = HasFixedPageScale(frame_metadata);
+ gesture_provider_.SetDoubleTapSupportForPageEnabled(
+ !has_fixed_page_scale && !has_mobile_viewport);
+
+ if (!content_view_core_)
+ return;
+ // All offsets and sizes are in CSS pixels.
+ content_view_core_->UpdateFrameInfo(
+ frame_metadata.root_scroll_offset,
+ frame_metadata.page_scale_factor,
+ gfx::Vector2dF(frame_metadata.min_page_scale_factor,
+ frame_metadata.max_page_scale_factor),
+ frame_metadata.root_layer_size,
+ frame_metadata.viewport_size,
+ frame_metadata.location_bar_offset,
+ frame_metadata.location_bar_content_translation,
+ frame_metadata.overdraw_bottom_height);
+#if defined(VIDEO_HOLE)
+ if (host_ && host_->IsRenderView()) {
+ RenderViewHostImpl* rvhi = static_cast<RenderViewHostImpl*>(
+ RenderViewHost::From(host_));
+ rvhi->media_web_contents_observer()->OnFrameInfoUpdated();
}
+#endif // defined(VIDEO_HOLE)
}
void RenderWidgetHostViewAndroid::AcceleratedSurfaceInitialized(int host_id,
@@ -877,34 +1018,6 @@ void RenderWidgetHostViewAndroid::AcceleratedSurfaceBuffersSwapped(
NOTREACHED() << "Need --composite-to-mailbox or --enable-delegated-renderer";
}
-void RenderWidgetHostViewAndroid::BuffersSwapped(
- const gpu::Mailbox& mailbox,
- uint32_t output_surface_id,
- const base::Closure& ack_callback) {
- ImageTransportFactoryAndroid* factory =
- ImageTransportFactoryAndroid::GetInstance();
-
- if (!texture_id_in_layer_) {
- texture_id_in_layer_ = factory->CreateTexture();
- texture_layer_->SetTextureId(texture_id_in_layer_);
- texture_layer_->SetIsDrawable(true);
- texture_layer_->SetContentsOpaque(true);
- }
-
- ImageTransportFactoryAndroid::GetInstance()->AcquireTexture(
- texture_id_in_layer_, mailbox.name);
-
- ResetClipping();
-
- current_mailbox_ = mailbox;
- last_output_surface_id_ = output_surface_id;
-
- if (host_->is_hidden())
- ack_callback.Run();
- else
- ack_callbacks_.push(ack_callback);
-}
-
void RenderWidgetHostViewAndroid::AttachLayers() {
if (!content_view_core_)
return;
@@ -914,6 +1027,7 @@ void RenderWidgetHostViewAndroid::AttachLayers() {
content_view_core_->AttachLayer(layer_);
if (overscroll_effect_enabled_)
overscroll_effect_->Enable();
+ layer_->SetHideLayerAndSubtree(!is_showing_);
}
void RenderWidgetHostViewAndroid::RemoveLayers() {
@@ -926,20 +1040,12 @@ void RenderWidgetHostViewAndroid::RemoveLayers() {
overscroll_effect_->Disable();
}
-bool RenderWidgetHostViewAndroid::Animate(base::TimeTicks frame_time) {
- return overscroll_effect_->Animate(frame_time);
+void RenderWidgetHostViewAndroid::SetNeedsAnimate() {
+ content_view_core_->GetWindowAndroid()->SetNeedsAnimate();
}
-void RenderWidgetHostViewAndroid::UpdateAnimationSize(
- const cc::CompositorFrameMetadata& frame_metadata) {
- // Disable edge effects for axes on which scrolling is impossible.
- gfx::SizeF ceiled_viewport_size =
- gfx::ToCeiledSize(frame_metadata.viewport_size);
- overscroll_effect_->set_horizontal_overscroll_enabled(
- ceiled_viewport_size.width() < frame_metadata.root_layer_size.width());
- overscroll_effect_->set_vertical_overscroll_enabled(
- ceiled_viewport_size.height() < frame_metadata.root_layer_size.height());
- overscroll_effect_->set_size(content_size_in_layer_);
+bool RenderWidgetHostViewAndroid::Animate(base::TimeTicks frame_time) {
+ return overscroll_effect_->Animate(frame_time);
}
void RenderWidgetHostViewAndroid::AcceleratedSurfacePostSubBuffer(
@@ -953,18 +1059,13 @@ void RenderWidgetHostViewAndroid::AcceleratedSurfaceSuspend() {
}
void RenderWidgetHostViewAndroid::AcceleratedSurfaceRelease() {
- // This tells us we should free the frontbuffer.
- if (texture_id_in_layer_) {
- texture_layer_->SetTextureId(0);
- texture_layer_->SetIsDrawable(false);
- ImageTransportFactoryAndroid::GetInstance()->DeleteTexture(
- texture_id_in_layer_);
- texture_id_in_layer_ = 0;
- current_mailbox_ = gpu::Mailbox();
- last_output_surface_id_ = kUndefinedOutputSurfaceId;
- }
- if (delegated_renderer_layer_.get())
+ NOTREACHED();
+}
+
+void RenderWidgetHostViewAndroid::EvictDelegatedFrame() {
+ if (layer_.get())
DestroyDelegatedContent();
+ frame_evictor_->DiscardedFrame();
}
bool RenderWidgetHostViewAndroid::HasAcceleratedSurface(
@@ -996,40 +1097,23 @@ gfx::GLSurfaceHandle RenderWidgetHostViewAndroid::GetCompositingSurface() {
void RenderWidgetHostViewAndroid::ProcessAckedTouchEvent(
const TouchEventWithLatencyInfo& touch, InputEventAckState ack_result) {
- if (content_view_core_)
- content_view_core_->ConfirmTouchEvent(ack_result);
-}
-
-void RenderWidgetHostViewAndroid::SetHasHorizontalScrollbar(
- bool has_horizontal_scrollbar) {
- // intentionally empty, like RenderWidgetHostViewViews
-}
-
-void RenderWidgetHostViewAndroid::SetScrollOffsetPinning(
- bool is_pinned_to_left, bool is_pinned_to_right) {
- // intentionally empty, like RenderWidgetHostViewViews
-}
-
-void RenderWidgetHostViewAndroid::UnhandledWheelEvent(
- const blink::WebMouseWheelEvent& event) {
- // intentionally empty, like RenderWidgetHostViewViews
+ const bool event_consumed = ack_result == INPUT_EVENT_ACK_STATE_CONSUMED;
+ gesture_provider_.OnTouchEventAck(event_consumed);
}
void RenderWidgetHostViewAndroid::GestureEventAck(
- int gesture_event_type,
+ const blink::WebGestureEvent& event,
InputEventAckState ack_result) {
- if (gesture_event_type == blink::WebInputEvent::GestureScrollUpdate &&
- ack_result == INPUT_EVENT_ACK_STATE_CONSUMED) {
- content_view_core_->OnScrollUpdateGestureConsumed();
- }
- if (gesture_event_type == blink::WebInputEvent::GestureFlingStart &&
- ack_result == INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS) {
- content_view_core_->UnhandledFlingStartEvent();
- }
+ if (content_view_core_)
+ content_view_core_->OnGestureEventAck(event, ack_result);
}
InputEventAckState RenderWidgetHostViewAndroid::FilterInputEvent(
const blink::WebInputEvent& input_event) {
+ if (content_view_core_ &&
+ content_view_core_->FilterInputEvent(input_event))
+ return INPUT_EVENT_ACK_STATE_CONSUMED;
+
if (!host_)
return INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
@@ -1054,12 +1138,12 @@ InputEventAckState RenderWidgetHostViewAndroid::FilterInputEvent(
void RenderWidgetHostViewAndroid::OnSetNeedsFlushInput() {
if (flush_input_requested_ || !content_view_core_)
return;
+ TRACE_EVENT0("input", "RenderWidgetHostViewAndroid::OnSetNeedsFlushInput");
flush_input_requested_ = true;
- content_view_core_->AddBeginFrameSubscriber();
+ content_view_core_->GetWindowAndroid()->RequestVSyncUpdate();
}
-void RenderWidgetHostViewAndroid::OnAccessibilityEvents(
- const std::vector<AccessibilityHostMsg_EventParams>& params) {
+void RenderWidgetHostViewAndroid::CreateBrowserAccessibilityManagerIfNeeded() {
if (!host_ || host_->accessibility_mode() != AccessibilityModeComplete)
return;
@@ -1069,62 +1153,10 @@ void RenderWidgetHostViewAndroid::OnAccessibilityEvents(
obj = content_view_core_->GetJavaObject();
SetBrowserAccessibilityManager(
new BrowserAccessibilityManagerAndroid(
- obj, BrowserAccessibilityManagerAndroid::GetEmptyDocument(), this));
+ obj,
+ BrowserAccessibilityManagerAndroid::GetEmptyDocument(),
+ host_));
}
- GetBrowserAccessibilityManager()->OnAccessibilityEvents(params);
-}
-
-void RenderWidgetHostViewAndroid::SetAccessibilityFocus(int acc_obj_id) {
- if (!host_)
- return;
-
- host_->AccessibilitySetFocus(acc_obj_id);
-}
-
-void RenderWidgetHostViewAndroid::AccessibilityDoDefaultAction(int acc_obj_id) {
- if (!host_)
- return;
-
- host_->AccessibilityDoDefaultAction(acc_obj_id);
-}
-
-void RenderWidgetHostViewAndroid::AccessibilityScrollToMakeVisible(
- int acc_obj_id, gfx::Rect subfocus) {
- if (!host_)
- return;
-
- host_->AccessibilityScrollToMakeVisible(acc_obj_id, subfocus);
-}
-
-void RenderWidgetHostViewAndroid::AccessibilityScrollToPoint(
- int acc_obj_id, gfx::Point point) {
- if (!host_)
- return;
-
- host_->AccessibilityScrollToPoint(acc_obj_id, point);
-}
-
-void RenderWidgetHostViewAndroid::AccessibilitySetTextSelection(
- int acc_obj_id, int start_offset, int end_offset) {
- if (!host_)
- return;
-
- host_->AccessibilitySetTextSelection(
- acc_obj_id, start_offset, end_offset);
-}
-
-gfx::Point RenderWidgetHostViewAndroid::GetLastTouchEventLocation() const {
- NOTIMPLEMENTED();
- // Only used on Win8
- return gfx::Point();
-}
-
-void RenderWidgetHostViewAndroid::FatalAccessibilityTreeError() {
- if (!host_)
- return;
-
- host_->FatalAccessibilityTreeError();
- SetBrowserAccessibilityManager(NULL);
}
bool RenderWidgetHostViewAndroid::LockMouse() {
@@ -1147,9 +1179,16 @@ void RenderWidgetHostViewAndroid::SendKeyEvent(
void RenderWidgetHostViewAndroid::SendTouchEvent(
const blink::WebTouchEvent& event) {
if (host_)
- host_->ForwardTouchEventWithLatencyInfo(event, ui::LatencyInfo());
-}
+ host_->ForwardTouchEventWithLatencyInfo(event, CreateLatencyInfo(event));
+ // Send a proactive BeginFrame on the next vsync to reduce latency.
+ // This is good enough as long as the first touch event has Begin semantics
+ // and the actual scroll happens on the next vsync.
+ // TODO: Is this actually still needed?
+ if (content_view_core_) {
+ content_view_core_->GetWindowAndroid()->RequestVSyncUpdate();
+ }
+}
void RenderWidgetHostViewAndroid::SendMouseEvent(
const blink::WebMouseEvent& event) {
@@ -1170,13 +1209,7 @@ void RenderWidgetHostViewAndroid::SendGestureEvent(
overscroll_effect_->Enable();
if (host_)
- host_->ForwardGestureEvent(event);
-}
-
-void RenderWidgetHostViewAndroid::SelectRange(const gfx::Point& start,
- const gfx::Point& end) {
- if (host_)
- host_->SelectRange(start, end);
+ host_->ForwardGestureEventWithLatencyInfo(event, CreateLatencyInfo(event));
}
void RenderWidgetHostViewAndroid::MoveCaret(const gfx::Point& point) {
@@ -1184,77 +1217,47 @@ void RenderWidgetHostViewAndroid::MoveCaret(const gfx::Point& point) {
host_->MoveCaret(point);
}
-void RenderWidgetHostViewAndroid::RequestContentClipping(
- const gfx::Rect& clipping,
- const gfx::Size& content_size) {
- // A focused view provides its own clipping.
- if (HasFocus())
- return;
-
- ClipContents(clipping, content_size);
-}
-
-void RenderWidgetHostViewAndroid::ResetClipping() {
- ClipContents(gfx::Rect(gfx::Point(), content_size_in_layer_),
- content_size_in_layer_);
-}
-
-void RenderWidgetHostViewAndroid::ClipContents(const gfx::Rect& clipping,
- const gfx::Size& content_size) {
- if (!texture_id_in_layer_ || content_size_in_layer_.IsEmpty())
- return;
-
- gfx::Size clipped_content(content_size_in_layer_);
- clipped_content.SetToMin(clipping.size());
- texture_layer_->SetBounds(clipped_content);
- texture_layer_->SetNeedsDisplay();
-
- if (texture_size_in_layer_.IsEmpty()) {
- texture_layer_->SetUV(gfx::PointF(), gfx::PointF());
- return;
- }
-
- gfx::PointF offset(
- clipping.x() + content_size_in_layer_.width() - content_size.width(),
- clipping.y() + content_size_in_layer_.height() - content_size.height());
- offset.SetToMax(gfx::PointF());
-
- gfx::Vector2dF uv_scale(1.f / texture_size_in_layer_.width(),
- 1.f / texture_size_in_layer_.height());
- texture_layer_->SetUV(
- gfx::PointF(offset.x() * uv_scale.x(),
- offset.y() * uv_scale.y()),
- gfx::PointF((offset.x() + clipped_content.width()) * uv_scale.x(),
- (offset.y() + clipped_content.height()) * uv_scale.y()));
-}
-
SkColor RenderWidgetHostViewAndroid::GetCachedBackgroundColor() const {
return cached_background_color_;
}
-void RenderWidgetHostViewAndroid::OnOverscrolled(
- gfx::Vector2dF accumulated_overscroll,
- gfx::Vector2dF current_fling_velocity) {
- if (!content_view_core_ || !are_layers_attached_)
+void RenderWidgetHostViewAndroid::DidOverscroll(
+ const DidOverscrollParams& params) {
+ if (!content_view_core_ || !layer_ || !is_showing_)
return;
- if (overscroll_effect_->OnOverscrolled(content_view_core_->GetLayer(),
- base::TimeTicks::Now(),
- accumulated_overscroll,
- current_fling_velocity)) {
- content_view_core_->SetNeedsAnimate();
+ const float device_scale_factor = content_view_core_->GetDpiScale();
+ if (overscroll_effect_->OnOverscrolled(
+ content_view_core_->GetLayer(),
+ base::TimeTicks::Now(),
+ gfx::ScaleVector2d(params.accumulated_overscroll,
+ device_scale_factor),
+ gfx::ScaleVector2d(params.latest_overscroll_delta,
+ device_scale_factor),
+ gfx::ScaleVector2d(params.current_fling_velocity,
+ device_scale_factor))) {
+ SetNeedsAnimate();
}
}
+void RenderWidgetHostViewAndroid::DidStopFlinging() {
+ if (content_view_core_)
+ content_view_core_->DidStopFlinging();
+}
+
void RenderWidgetHostViewAndroid::SetContentViewCore(
ContentViewCoreImpl* content_view_core) {
- RunAckCallbacks();
-
- if (are_layers_attached_)
- RemoveLayers();
-
- if (content_view_core_ && !using_synchronous_compositor_)
+ RemoveLayers();
+ if (observing_root_window_ && content_view_core_) {
content_view_core_->GetWindowAndroid()->RemoveObserver(this);
+ observing_root_window_ = false;
+ }
+
+ bool resize = false;
+ if (content_view_core != content_view_core_) {
+ ReleaseLocksOnSurface();
+ resize = true;
+ }
content_view_core_ = content_view_core;
@@ -1266,16 +1269,16 @@ void RenderWidgetHostViewAndroid::SetContentViewCore(
SetContentViewCore(obj);
}
- if (are_layers_attached_) {
- AttachLayers();
- if (content_view_core_ && !using_synchronous_compositor_)
- content_view_core_->GetWindowAndroid()->AddObserver(this);
+ AttachLayers();
+ if (content_view_core_ && !using_synchronous_compositor_) {
+ content_view_core_->GetWindowAndroid()->AddObserver(this);
+ observing_root_window_ = true;
+ if (needs_begin_frame_)
+ content_view_core_->GetWindowAndroid()->RequestVSyncUpdate();
}
- // Ensure ContentsViewCore is aware of the current touch handling state, eg.
- // in case we've already been running JS for the page as part of preload.
- if (content_view_core_ && host_)
- content_view_core_->HasTouchEventHandlers(host_->has_touch_handler());
+ if (resize && content_view_core_)
+ WasResized();
}
void RenderWidgetHostViewAndroid::RunAckCallbacks() {
@@ -1285,10 +1288,9 @@ void RenderWidgetHostViewAndroid::RunAckCallbacks() {
}
}
-void RenderWidgetHostViewAndroid::HasTouchEventHandlers(
- bool need_touch_events) {
- if (content_view_core_)
- content_view_core_->HasTouchEventHandlers(need_touch_events);
+void RenderWidgetHostViewAndroid::OnGestureEvent(
+ const ui::GestureEventData& gesture) {
+ SendGestureEvent(CreateWebGestureEventFromGestureEventData(gesture));
}
void RenderWidgetHostViewAndroid::OnCompositingDidCommit() {
@@ -1301,21 +1303,68 @@ void RenderWidgetHostViewAndroid::OnDetachCompositor() {
RunAckCallbacks();
}
+void RenderWidgetHostViewAndroid::OnVSync(base::TimeTicks frame_time,
+ base::TimeDelta vsync_period) {
+ TRACE_EVENT0("cc", "RenderWidgetHostViewAndroid::OnVSync");
+ if (!host_)
+ return;
+
+ if (flush_input_requested_) {
+ flush_input_requested_ = false;
+ host_->FlushInput();
+ }
+
+ TRACE_EVENT0("cc", "RenderWidgetHostViewAndroid::SendBeginFrame");
+ base::TimeTicks display_time = frame_time + vsync_period;
+
+ // TODO(brianderson): Use adaptive draw-time estimation.
+ base::TimeDelta estimated_browser_composite_time =
+ base::TimeDelta::FromMicroseconds(
+ (1.0f * base::Time::kMicrosecondsPerSecond) / (3.0f * 60));
+
+ base::TimeTicks deadline = display_time - estimated_browser_composite_time;
+
+ host_->Send(new ViewMsg_BeginFrame(
+ host_->GetRoutingID(),
+ cc::BeginFrameArgs::Create(frame_time, deadline, vsync_period)));
+
+ if (needs_begin_frame_)
+ content_view_core_->GetWindowAndroid()->RequestVSyncUpdate();
+}
+
+void RenderWidgetHostViewAndroid::OnAnimate(base::TimeTicks begin_frame_time) {
+ if (Animate(begin_frame_time))
+ SetNeedsAnimate();
+}
+
void RenderWidgetHostViewAndroid::OnLostResources() {
- if (texture_layer_.get())
- texture_layer_->SetIsDrawable(false);
- if (delegated_renderer_layer_.get())
+ ReleaseLocksOnSurface();
+ if (layer_.get())
DestroyDelegatedContent();
- texture_id_in_layer_ = 0;
- RunAckCallbacks();
+ DCHECK(ack_callbacks_.empty());
+}
+
+// static
+void
+RenderWidgetHostViewAndroid::PrepareTextureCopyOutputResultForDelegatedReadback(
+ const gfx::Size& dst_size_in_pixel,
+ const SkBitmap::Config config,
+ const base::TimeTicks& start_time,
+ scoped_refptr<cc::Layer> readback_layer,
+ const base::Callback<void(bool, const SkBitmap&)>& callback,
+ scoped_ptr<cc::CopyOutputResult> result) {
+ readback_layer->RemoveFromParent();
+ PrepareTextureCopyOutputResult(
+ dst_size_in_pixel, config, start_time, callback, result.Pass());
}
// static
void RenderWidgetHostViewAndroid::PrepareTextureCopyOutputResult(
const gfx::Size& dst_size_in_pixel,
+ const SkBitmap::Config bitmap_config,
+ const base::TimeTicks& start_time,
const base::Callback<void(bool, const SkBitmap&)>& callback,
scoped_ptr<cc::CopyOutputResult> result) {
- DCHECK(result->HasTexture());
base::ScopedClosureRunner scoped_callback_runner(
base::Bind(callback, false, SkBitmap()));
@@ -1323,8 +1372,9 @@ void RenderWidgetHostViewAndroid::PrepareTextureCopyOutputResult(
return;
scoped_ptr<SkBitmap> bitmap(new SkBitmap);
- bitmap->setConfig(SkBitmap::kARGB_8888_Config,
- dst_size_in_pixel.width(), dst_size_in_pixel.height(),
+ bitmap->setConfig(bitmap_config,
+ dst_size_in_pixel.width(),
+ dst_size_in_pixel.height(),
0, kOpaque_SkAlphaType);
if (!bitmap->allocPixels())
return;
@@ -1332,6 +1382,7 @@ void RenderWidgetHostViewAndroid::PrepareTextureCopyOutputResult(
ImageTransportFactoryAndroid* factory =
ImageTransportFactoryAndroid::GetInstance();
GLHelper* gl_helper = factory->GetGLHelper();
+
if (!gl_helper)
return;
@@ -1349,45 +1400,45 @@ void RenderWidgetHostViewAndroid::PrepareTextureCopyOutputResult(
ignore_result(scoped_callback_runner.Release());
gl_helper->CropScaleReadbackAndCleanMailbox(
- texture_mailbox.name(),
+ texture_mailbox.mailbox(),
texture_mailbox.sync_point(),
result->size(),
gfx::Rect(result->size()),
dst_size_in_pixel,
pixels,
+ bitmap_config,
base::Bind(&CopyFromCompositingSurfaceFinished,
callback,
base::Passed(&release_callback),
base::Passed(&bitmap),
- base::Passed(&bitmap_pixels_lock)));
+ start_time,
+ base::Passed(&bitmap_pixels_lock)),
+ GLHelper::SCALER_QUALITY_GOOD);
}
-// static
-void RenderWidgetHostViewAndroid::PrepareBitmapCopyOutputResult(
- const gfx::Size& dst_size_in_pixel,
- const base::Callback<void(bool, const SkBitmap&)>& callback,
- scoped_ptr<cc::CopyOutputResult> result) {
- DCHECK(result->HasBitmap());
- base::ScopedClosureRunner scoped_callback_runner(
- base::Bind(callback, false, SkBitmap()));
-
- if (!result->HasBitmap() || result->IsEmpty() || result->size().IsEmpty())
- return;
-
- scoped_ptr<SkBitmap> source = result->TakeBitmap();
- DCHECK(source);
- if (!source)
- return;
-
- DCHECK_EQ(source->width(), dst_size_in_pixel.width());
- DCHECK_EQ(source->height(), dst_size_in_pixel.height());
+bool RenderWidgetHostViewAndroid::IsReadbackConfigSupported(
+ SkBitmap::Config bitmap_config) {
+ ImageTransportFactoryAndroid* factory =
+ ImageTransportFactoryAndroid::GetInstance();
+ GLHelper* gl_helper = factory->GetGLHelper();
+ if (!gl_helper)
+ return false;
+ return gl_helper->IsReadbackConfigSupported(bitmap_config);
+}
- ignore_result(scoped_callback_runner.Release());
- callback.Run(true, *source);
+SkBitmap::Config RenderWidgetHostViewAndroid::PreferredReadbackFormat() {
+ // Define the criteria here. If say the 16 texture readback is
+ // supported we should go with that (this degrades quality)
+ // or stick back to the default format.
+ if (base::android::SysUtils::IsLowEndDevice()) {
+ if (IsReadbackConfigSupported(SkBitmap::kRGB_565_Config))
+ return SkBitmap::kRGB_565_Config;
+ }
+ return SkBitmap::kARGB_8888_Config;
}
// static
-void RenderWidgetHostViewPort::GetDefaultScreenInfo(
+void RenderWidgetHostViewBase::GetDefaultScreenInfo(
blink::WebScreenInfo* results) {
const gfx::Display& display =
gfx::Screen::GetNativeScreen()->GetPrimaryDisplay();
@@ -1395,20 +1446,11 @@ void RenderWidgetHostViewPort::GetDefaultScreenInfo(
// TODO(husky): Remove any system controls from availableRect.
results->availableRect = display.work_area();
results->deviceScaleFactor = display.device_scale_factor();
+ results->orientationAngle = display.RotationAsDegree();
gfx::DeviceDisplayInfo info;
results->depth = info.GetBitsPerPixel();
results->depthPerComponent = info.GetBitsPerComponent();
results->isMonochrome = (results->depthPerComponent == 0);
}
-////////////////////////////////////////////////////////////////////////////////
-// RenderWidgetHostView, public:
-
-// static
-RenderWidgetHostView*
-RenderWidgetHostView::CreateViewForWidget(RenderWidgetHost* widget) {
- RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(widget);
- return new RenderWidgetHostViewAndroid(rwhi, NULL);
-}
-
} // namespace content
diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_android.h b/chromium/content/browser/renderer_host/render_widget_host_view_android.h
index 62bd7669647..4707734a8e8 100644
--- a/chromium/content/browser/renderer_host/render_widget_host_view_android.h
+++ b/chromium/content/browser/renderer_host/render_widget_host_view_android.h
@@ -15,16 +15,18 @@
#include "base/memory/weak_ptr.h"
#include "base/process/process.h"
#include "cc/layers/delegated_frame_resource_collection.h"
-#include "cc/layers/texture_layer_client.h"
#include "cc/output/begin_frame_args.h"
#include "content/browser/accessibility/browser_accessibility_manager.h"
+#include "content/browser/renderer_host/delegated_frame_evictor.h"
#include "content/browser/renderer_host/image_transport_factory_android.h"
#include "content/browser/renderer_host/ime_adapter_android.h"
#include "content/browser/renderer_host/render_widget_host_view_base.h"
+#include "content/common/content_export.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
#include "ui/base/android/window_android_observer.h"
+#include "ui/events/gesture_detection/filtered_gesture_provider.h"
#include "ui/gfx/size.h"
#include "ui/gfx/vector2d_f.h"
@@ -38,8 +40,6 @@ class CopyOutputResult;
class DelegatedFrameProvider;
class DelegatedRendererLayer;
class Layer;
-class SingleReleaseCallback;
-class TextureLayer;
}
namespace blink {
@@ -53,17 +53,19 @@ class ContentViewCoreImpl;
class OverscrollGlow;
class RenderWidgetHost;
class RenderWidgetHostImpl;
+struct DidOverscrollParams;
struct NativeWebKeyboardEvent;
// -----------------------------------------------------------------------------
// See comments in render_widget_host_view.h about this class and its members.
// -----------------------------------------------------------------------------
-class RenderWidgetHostViewAndroid
+class CONTENT_EXPORT RenderWidgetHostViewAndroid
: public RenderWidgetHostViewBase,
- public BrowserAccessibilityDelegate,
public cc::DelegatedFrameResourceCollectionClient,
public ImageTransportFactoryAndroidObserver,
- public ui::WindowAndroidObserver {
+ public ui::GestureProviderClient,
+ public ui::WindowAndroidObserver,
+ public DelegatedFrameEvictorClient {
public:
RenderWidgetHostViewAndroid(RenderWidgetHostImpl* widget,
ContentViewCoreImpl* content_view_core);
@@ -85,7 +87,6 @@ class RenderWidgetHostViewAndroid
virtual gfx::NativeViewId GetNativeViewId() const OVERRIDE;
virtual gfx::NativeViewAccessible GetNativeViewAccessible() OVERRIDE;
virtual void MovePluginWindows(
- const gfx::Vector2d& scroll_offset,
const std::vector<WebPluginGeometry>& moves) OVERRIDE;
virtual void Focus() OVERRIDE;
virtual void Blur() OVERRIDE;
@@ -99,15 +100,10 @@ class RenderWidgetHostViewAndroid
virtual float GetOverdrawBottomHeight() const OVERRIDE;
virtual void UpdateCursor(const WebCursor& cursor) OVERRIDE;
virtual void SetIsLoading(bool is_loading) OVERRIDE;
- virtual void TextInputTypeChanged(ui::TextInputType type,
- ui::TextInputMode input_mode,
- bool can_compose_inline) OVERRIDE;
+ virtual void TextInputStateChanged(
+ const ViewHostMsg_TextInputState_Params& params) OVERRIDE;
virtual void ImeCancelComposition() OVERRIDE;
- virtual void DidUpdateBackingStore(
- const gfx::Rect& scroll_rect,
- const gfx::Vector2d& scroll_delta,
- const std::vector<gfx::Rect>& copy_rects,
- const ui::LatencyInfo& latency_info) OVERRIDE;
+ virtual void FocusedNodeChanged(bool is_editable_node) OVERRIDE;
virtual void RenderProcessGone(base::TerminationStatus status,
int error_code) OVERRIDE;
virtual void Destroy() OVERRIDE;
@@ -118,8 +114,6 @@ class RenderWidgetHostViewAndroid
virtual void SelectionBoundsChanged(
const ViewHostMsg_SelectionBounds_Params& params) OVERRIDE;
virtual void ScrollOffsetChanged() OVERRIDE;
- virtual BackingStore* AllocBackingStore(const gfx::Size& size) OVERRIDE;
- virtual void OnAcceleratedCompositingStateChange() OVERRIDE;
virtual void AcceleratedSurfaceInitialized(int host_id,
int route_id) OVERRIDE;
virtual void AcceleratedSurfaceBuffersSwapped(
@@ -131,11 +125,12 @@ class RenderWidgetHostViewAndroid
virtual void AcceleratedSurfaceSuspend() OVERRIDE;
virtual void AcceleratedSurfaceRelease() OVERRIDE;
virtual bool HasAcceleratedSurface(const gfx::Size& desired_size) OVERRIDE;
- virtual void SetBackground(const SkBitmap& background) OVERRIDE;
+ virtual void SetBackgroundOpaque(bool transparent) OVERRIDE;
virtual void CopyFromCompositingSurface(
const gfx::Rect& src_subrect,
const gfx::Size& dst_size,
- const base::Callback<void(bool, const SkBitmap&)>& callback) OVERRIDE;
+ const base::Callback<void(bool, const SkBitmap&)>& callback,
+ const SkBitmap::Config config) OVERRIDE;
virtual void CopyFromCompositingSurfaceToVideoFrame(
const gfx::Rect& src_subrect,
const scoped_refptr<media::VideoFrame>& target,
@@ -146,56 +141,51 @@ class RenderWidgetHostViewAndroid
virtual gfx::GLSurfaceHandle GetCompositingSurface() OVERRIDE;
virtual void ProcessAckedTouchEvent(const TouchEventWithLatencyInfo& touch,
InputEventAckState ack_result) OVERRIDE;
- virtual void SetHasHorizontalScrollbar(
- bool has_horizontal_scrollbar) OVERRIDE;
- virtual void SetScrollOffsetPinning(
- bool is_pinned_to_left, bool is_pinned_to_right) OVERRIDE;
- virtual void UnhandledWheelEvent(
- const blink::WebMouseWheelEvent& event) OVERRIDE;
virtual InputEventAckState FilterInputEvent(
const blink::WebInputEvent& input_event) OVERRIDE;
virtual void OnSetNeedsFlushInput() OVERRIDE;
- virtual void GestureEventAck(int gesture_event_type,
+ virtual void GestureEventAck(const blink::WebGestureEvent& event,
InputEventAckState ack_result) OVERRIDE;
- virtual void OnAccessibilityEvents(
- const std::vector<AccessibilityHostMsg_EventParams>&
- params) OVERRIDE;
+ virtual void CreateBrowserAccessibilityManagerIfNeeded() OVERRIDE;
virtual bool LockMouse() OVERRIDE;
virtual void UnlockMouse() OVERRIDE;
- virtual void HasTouchEventHandlers(bool need_touch_events) OVERRIDE;
virtual void OnSwapCompositorFrame(
uint32 output_surface_id,
scoped_ptr<cc::CompositorFrame> frame) OVERRIDE;
- virtual void OnOverscrolled(gfx::Vector2dF accumulated_overscroll,
- gfx::Vector2dF current_fling_velocity) OVERRIDE;
+ virtual void DidOverscroll(const DidOverscrollParams& params) OVERRIDE;
+ virtual void DidStopFlinging() OVERRIDE;
virtual void ShowDisambiguationPopup(const gfx::Rect& target_rect,
const SkBitmap& zoomed_bitmap) OVERRIDE;
virtual scoped_ptr<SyntheticGestureTarget> CreateSyntheticGestureTarget()
OVERRIDE;
-
- // Implementation of BrowserAccessibilityDelegate:
- virtual void SetAccessibilityFocus(int acc_obj_id) OVERRIDE;
- virtual void AccessibilityDoDefaultAction(int acc_obj_id) OVERRIDE;
- virtual void AccessibilityScrollToMakeVisible(
- int acc_obj_id, gfx::Rect subfocus) OVERRIDE;
- virtual void AccessibilityScrollToPoint(
- int acc_obj_id, gfx::Point point) OVERRIDE;
- virtual void AccessibilitySetTextSelection(
- int acc_obj_id, int start_offset, int end_offset) OVERRIDE;
- virtual gfx::Point GetLastTouchEventLocation() const OVERRIDE;
- virtual void FatalAccessibilityTreeError() OVERRIDE;
+ virtual void LockCompositingSurface() OVERRIDE;
+ virtual void UnlockCompositingSurface() OVERRIDE;
+ virtual void OnTextSurroundingSelectionResponse(const base::string16& content,
+ size_t start_offset,
+ size_t end_offset) OVERRIDE;
// cc::DelegatedFrameResourceCollectionClient implementation.
virtual void UnusedResourcesAreAvailable() OVERRIDE;
+ // ui::GestureProviderClient implementation.
+ virtual void OnGestureEvent(const ui::GestureEventData& gesture) OVERRIDE;
+
// ui::WindowAndroidObserver implementation.
virtual void OnCompositingDidCommit() OVERRIDE;
virtual void OnAttachCompositor() OVERRIDE {}
virtual void OnDetachCompositor() OVERRIDE;
+ virtual void OnVSync(base::TimeTicks frame_time,
+ base::TimeDelta vsync_period) OVERRIDE;
+ virtual void OnAnimate(base::TimeTicks begin_frame_time) OVERRIDE;
// ImageTransportFactoryAndroidObserver implementation.
virtual void OnLostResources() OVERRIDE;
+ // DelegatedFrameEvictor implementation
+ virtual void EvictDelegatedFrame() OVERRIDE;
+
+ virtual SkBitmap::Config PreferredReadbackFormat() OVERRIDE;
+
// Non-virtual methods
void SetContentViewCore(ContentViewCoreImpl* content_view_core);
SkColor GetCachedBackgroundColor() const;
@@ -204,51 +194,52 @@ class RenderWidgetHostViewAndroid
void SendMouseEvent(const blink::WebMouseEvent& event);
void SendMouseWheelEvent(const blink::WebMouseWheelEvent& event);
void SendGestureEvent(const blink::WebGestureEvent& event);
- void SendBeginFrame(const cc::BeginFrameArgs& args);
- void OnTextInputStateChanged(const ViewHostMsg_TextInputState_Params& params);
void OnDidChangeBodyBackgroundColor(SkColor color);
void OnStartContentIntent(const GURL& content_url);
void OnSetNeedsBeginFrame(bool enabled);
- void OnSmartClipDataExtracted(const string16& result);
+ void OnSmartClipDataExtracted(const base::string16& result);
+
+ bool OnTouchEvent(const ui::MotionEvent& event);
+ void ResetGestureDetection();
+ void SetDoubleTapSupportEnabled(bool enabled);
+ void SetMultiTouchZoomSupportEnabled(bool enabled);
- int GetNativeImeAdapter();
+ long GetNativeImeAdapter();
void WasResized();
- blink::WebGLId GetScaledContentTexture(float scale, gfx::Size* out_size);
- bool PopulateBitmapWithContents(jobject jbitmap);
+ void GetScaledContentBitmap(
+ float scale,
+ SkBitmap::Config bitmap_config,
+ gfx::Rect src_subrect,
+ const base::Callback<void(bool, const SkBitmap&)>& result_callback);
bool HasValidFrame() const;
- // Select all text between the given coordinates.
- void SelectRange(const gfx::Point& start, const gfx::Point& end);
-
void MoveCaret(const gfx::Point& point);
- void RequestContentClipping(const gfx::Rect& clipping,
- const gfx::Size& content_size);
-
- // Returns true when animation ticks are still needed. This avoids a separate
- // round-trip for requesting follow-up animation.
- bool Animate(base::TimeTicks frame_time);
-
void SynchronousFrameMetadata(
const cc::CompositorFrameMetadata& frame_metadata);
- private:
- void BuffersSwapped(const gpu::Mailbox& mailbox,
- uint32_t output_surface_id,
- const base::Closure& ack_callback);
+ void SetOverlayVideoMode(bool enabled);
+
+ typedef base::Callback<
+ void(const base::string16& content, int start_offset, int end_offset)>
+ TextSurroundingSelectionCallback;
+ void SetTextSurroundingSelectionCallback(
+ const TextSurroundingSelectionCallback& callback);
+ private:
void RunAckCallbacks();
void DestroyDelegatedContent();
void SwapDelegatedFrame(uint32 output_surface_id,
scoped_ptr<cc::DelegatedFrameData> frame_data);
void SendDelegatedFrameAck(uint32 output_surface_id);
+ void SendReturnedDelegatedResources(uint32 output_surface_id);
- void UpdateContentViewCoreFrameMetadata(
+ void OnFrameMetadataUpdated(
const cc::CompositorFrameMetadata& frame_metadata);
void ComputeContentsSize(const cc::CompositorFrameMetadata& frame_metadata);
void ResetClipping();
@@ -257,16 +248,19 @@ class RenderWidgetHostViewAndroid
void AttachLayers();
void RemoveLayers();
- void UpdateAnimationSize(const cc::CompositorFrameMetadata& frame_metadata);
-
// Called after async screenshot task completes. Scales and crops the result
// of the copy.
static void PrepareTextureCopyOutputResult(
const gfx::Size& dst_size_in_pixel,
+ const SkBitmap::Config config,
+ const base::TimeTicks& start_time,
const base::Callback<void(bool, const SkBitmap&)>& callback,
scoped_ptr<cc::CopyOutputResult> result);
- static void PrepareBitmapCopyOutputResult(
+ static void PrepareTextureCopyOutputResultForDelegatedReadback(
const gfx::Size& dst_size_in_pixel,
+ const SkBitmap::Config config,
+ const base::TimeTicks& start_time,
+ scoped_refptr<cc::Layer> readback_layer,
const base::Callback<void(bool, const SkBitmap&)>& callback,
scoped_ptr<cc::CopyOutputResult> result);
@@ -274,7 +268,26 @@ class RenderWidgetHostViewAndroid
void SynchronousCopyContents(
const gfx::Rect& src_subrect_in_pixel,
const gfx::Size& dst_size_in_pixel,
- const base::Callback<void(bool, const SkBitmap&)>& callback);
+ const base::Callback<void(bool, const SkBitmap&)>& callback,
+ const SkBitmap::Config config);
+
+ bool IsReadbackConfigSupported(SkBitmap::Config bitmap_config);
+
+ // If we have locks on a frame during a ContentViewCore swap or a context
+ // lost, the frame is no longer valid and we can safely release all the locks.
+ // Use this method to release all the locks.
+ void ReleaseLocksOnSurface();
+
+ // Drop any incoming frames from the renderer when there are locks on the
+ // current frame.
+ void RetainFrame(uint32 output_surface_id,
+ scoped_ptr<cc::CompositorFrame> frame);
+
+ void InternalSwapCompositorFrame(uint32 output_surface_id,
+ scoped_ptr<cc::CompositorFrame> frame);
+
+ void SetNeedsAnimate();
+ bool Animate(base::TimeTicks frame_time);
// The model object.
RenderWidgetHostImpl* host_;
@@ -282,11 +295,7 @@ class RenderWidgetHostViewAndroid
// Used to track whether this render widget needs a BeginFrame.
bool needs_begin_frame_;
- // Whether or not this widget is potentially attached to the view hierarchy.
- // This view may not actually be attached if this is true, but it should be
- // treated as such, because as soon as a ContentViewCore is set the layer
- // will be attached automatically.
- bool are_layers_attached_;
+ bool is_showing_;
// ContentViewCoreImpl is our interface to the view system.
ContentViewCoreImpl* content_view_core_;
@@ -296,20 +305,9 @@ class RenderWidgetHostViewAndroid
// Body background color of the underlying document.
SkColor cached_background_color_;
- // The texture layer for this view when using browser-side compositing.
- scoped_refptr<cc::TextureLayer> texture_layer_;
-
scoped_refptr<cc::DelegatedFrameResourceCollection> resource_collection_;
scoped_refptr<cc::DelegatedFrameProvider> frame_provider_;
- scoped_refptr<cc::DelegatedRendererLayer> delegated_renderer_layer_;
-
- // The layer used for rendering the contents of this view.
- // It is either owned by texture_layer_ or surface_texture_transport_
- // depending on the mode.
- scoped_refptr<cc::Layer> layer_;
-
- // The most recent texture id that was pushed to the texture layer.
- unsigned int texture_id_in_layer_;
+ scoped_refptr<cc::DelegatedRendererLayer> layer_;
// The most recent texture size that was pushed to the texture layer.
gfx::Size texture_size_in_layer_;
@@ -317,8 +315,8 @@ class RenderWidgetHostViewAndroid
// The most recent content size that was pushed to the texture layer.
gfx::Size content_size_in_layer_;
- // The mailbox of the previously received frame.
- gpu::Mailbox current_mailbox_;
+ // The device scale of the last received frame.
+ float device_scale_factor_;
// The output surface id of the last received frame.
uint32_t last_output_surface_id_;
@@ -332,6 +330,10 @@ class RenderWidgetHostViewAndroid
// Note: |overscroll_effect_| will never be NULL, even if it's never enabled.
scoped_ptr<OverscrollGlow> overscroll_effect_;
+ // Provides gesture synthesis given a stream of touch events (derived from
+ // Android MotionEvent's) and touch event acks.
+ ui::FilteredGestureProvider gesture_provider_;
+
bool flush_input_requested_;
int accelerated_surface_route_id_;
@@ -341,6 +343,23 @@ class RenderWidgetHostViewAndroid
const bool using_synchronous_compositor_;
+ scoped_ptr<DelegatedFrameEvictor> frame_evictor_;
+
+ size_t locks_on_frame_count_;
+ bool observing_root_window_;
+
+ struct LastFrameInfo {
+ LastFrameInfo(uint32 output_id,
+ scoped_ptr<cc::CompositorFrame> output_frame);
+ ~LastFrameInfo();
+ uint32 output_surface_id;
+ scoped_ptr<cc::CompositorFrame> frame;
+ };
+
+ scoped_ptr<LastFrameInfo> last_frame_info_;
+
+ TextSurroundingSelectionCallback text_surrounding_selection_callback_;
+
DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewAndroid);
};
diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_aura.cc b/chromium/content/browser/renderer_host/render_widget_host_view_aura.cc
index 7f98b5d8ead..3c10f51e145 100644
--- a/chromium/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/chromium/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -13,60 +13,55 @@
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/string_number_conversions.h"
-#include "cc/layers/delegated_frame_provider.h"
-#include "cc/output/compositor_frame.h"
-#include "cc/output/compositor_frame_ack.h"
+#include "cc/layers/layer.h"
#include "cc/output/copy_output_request.h"
#include "cc/output/copy_output_result.h"
#include "cc/resources/texture_mailbox.h"
#include "cc/trees/layer_tree_settings.h"
#include "content/browser/accessibility/browser_accessibility_manager.h"
#include "content/browser/accessibility/browser_accessibility_state_impl.h"
-#include "content/browser/aura/compositor_resize_lock.h"
+#include "content/browser/frame_host/frame_tree.h"
+#include "content/browser/frame_host/frame_tree_node.h"
+#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/browser/gpu/compositor_util.h"
-#include "content/browser/renderer_host/backing_store_aura.h"
+#include "content/browser/renderer_host/compositor_resize_lock_aura.h"
#include "content/browser/renderer_host/dip_util.h"
#include "content/browser/renderer_host/input/synthetic_gesture_target_aura.h"
#include "content/browser/renderer_host/overscroll_controller.h"
#include "content/browser/renderer_host/render_view_host_delegate.h"
+#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/renderer_host/ui_events_helper.h"
#include "content/browser/renderer_host/web_input_event_aura.h"
#include "content/common/gpu/client/gl_helper.h"
#include "content/common/gpu/gpu_messages.h"
#include "content/common/view_messages.h"
-#include "content/port/browser/render_widget_host_view_frame_subscriber.h"
-#include "content/port/browser/render_widget_host_view_port.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/browser/overscroll_configuration.h"
#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host_view_frame_subscriber.h"
#include "content/public/browser/user_metrics.h"
#include "content/public/common/content_switches.h"
-#include "media/base/video_util.h"
-#include "skia/ext/image_operations.h"
#include "third_party/WebKit/public/platform/WebScreenInfo.h"
#include "third_party/WebKit/public/web/WebCompositionUnderline.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
-#include "ui/aura/client/activation_client.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/client/cursor_client.h"
#include "ui/aura/client/cursor_client_observer.h"
#include "ui/aura/client/focus_client.h"
#include "ui/aura/client/screen_position_client.h"
-#include "ui/aura/client/tooltip_client.h"
#include "ui/aura/client/window_tree_client.h"
-#include "ui/aura/client/window_types.h"
#include "ui/aura/env.h"
-#include "ui/aura/root_window.h"
#include "ui/aura/window.h"
+#include "ui/aura/window_event_dispatcher.h"
#include "ui/aura/window_observer.h"
#include "ui/aura/window_tracker.h"
+#include "ui/aura/window_tree_host.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
#include "ui/base/hit_test.h"
#include "ui/base/ime/input_method.h"
#include "ui/base/ui_base_types.h"
-#include "ui/compositor/layer.h"
+#include "ui/compositor/compositor_vsync_manager.h"
#include "ui/events/event.h"
#include "ui/events/event_utils.h"
#include "ui/events/gestures/gesture_recognizer.h"
@@ -76,32 +71,40 @@
#include "ui/gfx/screen.h"
#include "ui/gfx/size_conversions.h"
#include "ui/gfx/skia_util.h"
+#include "ui/wm/public/activation_client.h"
+#include "ui/wm/public/scoped_tooltip_disabler.h"
+#include "ui/wm/public/tooltip_client.h"
+#include "ui/wm/public/transient_window_client.h"
+#include "ui/wm/public/window_types.h"
#if defined(OS_WIN)
-#include "base/win/windows_version.h"
#include "content/browser/accessibility/browser_accessibility_manager_win.h"
#include "content/browser/accessibility/browser_accessibility_win.h"
+#include "content/browser/renderer_host/legacy_render_widget_host_win.h"
#include "content/common/plugin_constants_win.h"
#include "ui/base/win/hidden_window.h"
#include "ui/gfx/gdi_util.h"
#include "ui/gfx/win/dpi.h"
#endif
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+#include "content/common/input_messages.h"
+#include "ui/events/linux/text_edit_command_auralinux.h"
+#include "ui/events/linux/text_edit_key_bindings_delegate_auralinux.h"
+#endif
+
using gfx::RectToSkIRect;
using gfx::SkIRectToRect;
using blink::WebScreenInfo;
+using blink::WebInputEvent;
+using blink::WebGestureEvent;
using blink::WebTouchEvent;
namespace content {
namespace {
-void MailboxReleaseCallback(scoped_ptr<base::SharedMemory> shared_memory,
- unsigned sync_point, bool lost_resource) {
- // NOTE: shared_memory will get released when we go out of scope.
-}
-
// In mouse lock mode, we need to prevent the (invisible) cursor from hitting
// the border of the view, in order to get valid movement information. However,
// forcing the cursor back to the center of the view after each mouse move
@@ -145,9 +148,8 @@ BOOL CALLBACK ShowWindowsCallback(HWND window, LPARAM param) {
reinterpret_cast<RenderWidgetHostViewAura*>(param);
if (GetProp(window, kWidgetOwnerProperty) == widget &&
- widget->GetNativeView()->GetDispatcher()) {
- HWND parent = widget->GetNativeView()->GetDispatcher()->host()->
- GetAcceleratedWidget();
+ widget->GetNativeView()->GetHost()) {
+ HWND parent = widget->GetNativeView()->GetHost()->GetAcceleratedWidget();
SetParent(window, parent);
}
return TRUE;
@@ -169,8 +171,8 @@ BOOL CALLBACK SetCutoutRectsCallback(HWND window, LPARAM param) {
if (GetProp(window, kWidgetOwnerProperty) == params->widget) {
// First calculate the offset of this plugin from the root window, since
// the cutouts are relative to the root window.
- HWND parent = params->widget->GetNativeView()->GetDispatcher()->
- host()->GetAcceleratedWidget();
+ HWND parent =
+ params->widget->GetNativeView()->GetHost()->GetAcceleratedWidget();
POINT offset;
offset.x = offset.y = 0;
MapWindowPoints(window, parent, &offset, 1);
@@ -267,6 +269,16 @@ bool CanRendererHandleEvent(const ui::MouseEvent* event) {
default:
break;
}
+#elif defined(USE_X11)
+ // Renderer only supports standard mouse buttons, so ignore programmable
+ // buttons.
+ switch (event->type()) {
+ case ui::ET_MOUSE_PRESSED:
+ case ui::ET_MOUSE_RELEASED:
+ return event->IsAnyButton();
+ default:
+ break;
+ }
#endif
return true;
}
@@ -294,17 +306,16 @@ void GetScreenInfoForWindow(WebScreenInfo* results, aura::Window* window) {
results->depth = 24;
results->depthPerComponent = 8;
results->deviceScaleFactor = display.device_scale_factor();
-}
-bool ShouldSendPinchGesture() {
-#if defined(OS_WIN)
- if (base::win::GetVersion() >= base::win::VERSION_WIN8)
- return true;
-#endif
- static bool pinch_allowed =
- CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableViewport) ||
- CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnablePinch);
- return pinch_allowed;
+ // The Display rotation and the WebScreenInfo orientation are not the same
+ // angle. The former is the physical display rotation while the later is the
+ // rotation required by the content to be shown properly on the screen, in
+ // other words, relative to the physical display.
+ results->orientationAngle = display.RotationAsDegree();
+ if (results->orientationAngle == 90)
+ results->orientationAngle = 270;
+ else if (results->orientationAngle == 270)
+ results->orientationAngle = 90;
}
bool PointerEventActivates(const ui::Event& event) {
@@ -320,79 +331,21 @@ bool PointerEventActivates(const ui::Event& event) {
return false;
}
-// Swap ack for the renderer when kCompositeToMailbox is enabled.
-void SendCompositorFrameAck(
- int32 route_id,
- uint32 output_surface_id,
- int renderer_host_id,
- const gpu::Mailbox& received_mailbox,
- const gfx::Size& received_size,
- bool skip_frame,
- const scoped_refptr<ui::Texture>& texture_to_produce) {
- cc::CompositorFrameAck ack;
- ack.gl_frame_data.reset(new cc::GLFrameData());
- DCHECK(!texture_to_produce.get() || !skip_frame);
- if (texture_to_produce.get()) {
- std::string mailbox_name = texture_to_produce->Produce();
- std::copy(mailbox_name.data(),
- mailbox_name.data() + mailbox_name.length(),
- reinterpret_cast<char*>(ack.gl_frame_data->mailbox.name));
- ack.gl_frame_data->size = texture_to_produce->size();
- ack.gl_frame_data->sync_point =
- content::ImageTransportFactory::GetInstance()->InsertSyncPoint();
- } else if (skip_frame) {
- // Skip the frame, i.e. tell the producer to reuse the same buffer that
- // we just received.
- ack.gl_frame_data->size = received_size;
- ack.gl_frame_data->mailbox = received_mailbox;
- }
-
- RenderWidgetHostImpl::SendSwapCompositorFrameAck(
- route_id, output_surface_id, renderer_host_id, ack);
-}
-
-void AcknowledgeBufferForGpu(
- int32 route_id,
- int gpu_host_id,
- const std::string& received_mailbox,
- bool skip_frame,
- const scoped_refptr<ui::Texture>& texture_to_produce) {
- AcceleratedSurfaceMsg_BufferPresented_Params ack;
- uint32 sync_point = 0;
- DCHECK(!texture_to_produce.get() || !skip_frame);
- if (texture_to_produce.get()) {
- ack.mailbox_name = texture_to_produce->Produce();
- sync_point =
- content::ImageTransportFactory::GetInstance()->InsertSyncPoint();
- } else if (skip_frame) {
- ack.mailbox_name = received_mailbox;
- ack.sync_point = 0;
- }
-
- ack.sync_point = sync_point;
- RenderWidgetHostImpl::AcknowledgeBufferPresent(
- route_id, gpu_host_id, ack);
-}
-
} // namespace
// We need to watch for mouse events outside a Web Popup or its parent
// and dismiss the popup for certain events.
-class RenderWidgetHostViewAura::EventFilterForPopupExit :
- public ui::EventHandler {
+class RenderWidgetHostViewAura::EventFilterForPopupExit
+ : public ui::EventHandler {
public:
explicit EventFilterForPopupExit(RenderWidgetHostViewAura* rwhva)
: rwhva_(rwhva) {
DCHECK(rwhva_);
- aura::Window* root_window = rwhva_->window_->GetRootWindow();
- DCHECK(root_window);
- root_window->AddPreTargetHandler(this);
+ aura::Env::GetInstance()->AddPreTargetHandler(this);
}
virtual ~EventFilterForPopupExit() {
- aura::Window* root_window = rwhva_->window_->GetRootWindow();
- DCHECK(root_window);
- root_window->RemovePreTargetHandler(this);
+ aura::Env::GetInstance()->RemovePreTargetHandler(this);
}
// Overridden from ui::EventHandler
@@ -400,6 +353,10 @@ class RenderWidgetHostViewAura::EventFilterForPopupExit :
rwhva_->ApplyEventFilterForPopupExit(event);
}
+ virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE {
+ rwhva_->ApplyEventFilterForPopupExit(event);
+ }
+
private:
RenderWidgetHostViewAura* rwhva_;
@@ -407,12 +364,14 @@ class RenderWidgetHostViewAura::EventFilterForPopupExit :
};
void RenderWidgetHostViewAura::ApplyEventFilterForPopupExit(
- ui::MouseEvent* event) {
- if (in_shutdown_ || is_fullscreen_)
+ ui::LocatedEvent* event) {
+ if (in_shutdown_ || is_fullscreen_ || !event->target())
return;
- if (event->type() != ui::ET_MOUSE_PRESSED || !event->target())
+ if (event->type() != ui::ET_MOUSE_PRESSED &&
+ event->type() != ui::ET_TOUCH_PRESSED) {
return;
+ }
aura::Window* target = static_cast<aura::Window*>(event->target());
if (target != window_ &&
@@ -446,7 +405,8 @@ class RenderWidgetHostViewAura::WindowObserver : public aura::WindowObserver {
view_->AddedToRootWindow();
}
- virtual void OnWindowRemovingFromRootWindow(aura::Window* window) OVERRIDE {
+ virtual void OnWindowRemovingFromRootWindow(aura::Window* window,
+ aura::Window* new_root) OVERRIDE {
if (window == view_->window_)
view_->RemovingFromRootWindow();
}
@@ -463,6 +423,7 @@ class RenderWidgetHostViewAura::WindowObserver : public aura::WindowObserver {
RenderWidgetHostViewAura::RenderWidgetHostViewAura(RenderWidgetHost* host)
: host_(RenderWidgetHostImpl::From(host)),
window_(new aura::Window(this)),
+ delegated_frame_host_(new DelegatedFrameHost(this)),
in_shutdown_(false),
in_bounds_changed_(false),
is_fullscreen_(false),
@@ -474,18 +435,11 @@ RenderWidgetHostViewAura::RenderWidgetHostViewAura(RenderWidgetHost* host)
can_compose_inline_(true),
has_composition_text_(false),
accept_return_character_(false),
- last_output_surface_id_(0),
- pending_delegated_ack_count_(0),
- skipped_frames_(false),
- last_swapped_surface_scale_factor_(1.f),
+ last_swapped_software_frame_scale_factor_(1.f),
paint_canvas_(NULL),
synthetic_move_sent_(false),
- accelerated_compositing_state_changed_(false),
- can_lock_compositor_(YES),
cursor_visibility_state_in_renderer_(UNKNOWN),
- paint_observer_(NULL),
touch_editing_client_(NULL),
- delegated_frame_evictor_(new DelegatedFrameEvictor(this)),
weak_ptr_factory_(this) {
host_->SetView(this);
window_observer_.reset(new WindowObserver(this));
@@ -493,12 +447,12 @@ RenderWidgetHostViewAura::RenderWidgetHostViewAura(RenderWidgetHost* host)
aura::client::SetActivationDelegate(window_, this);
aura::client::SetActivationChangeObserver(window_, this);
aura::client::SetFocusChangeObserver(window_, this);
+ window_->set_layer_owner_delegate(delegated_frame_host_.get());
gfx::Screen::GetScreenFor(window_)->AddObserver(this);
- software_frame_manager_.reset(new SoftwareFrameManager(
- weak_ptr_factory_.GetWeakPtr()));
-#if defined(OS_WIN)
- plugin_parent_window_ = NULL;
-#endif
+
+ bool overscroll_enabled = CommandLine::ForCurrentProcess()->
+ GetSwitchValueASCII(switches::kOverscrollHistoryNavigation) != "0";
+ SetOverscrollControllerEnabled(overscroll_enabled);
}
////////////////////////////////////////////////////////////////////////////////
@@ -506,7 +460,8 @@ RenderWidgetHostViewAura::RenderWidgetHostViewAura(RenderWidgetHost* host)
void RenderWidgetHostViewAura::InitAsChild(
gfx::NativeView parent_view) {
- window_->Init(ui::LAYER_TEXTURED);
+ window_->SetType(ui::wm::WINDOW_TYPE_CONTROL);
+ window_->Init(aura::WINDOW_LAYER_TEXTURED);
window_->SetName("RenderWidgetHostViewAura");
}
@@ -516,6 +471,9 @@ void RenderWidgetHostViewAura::InitAsPopup(
popup_parent_host_view_ =
static_cast<RenderWidgetHostViewAura*>(parent_host_view);
+ // TransientWindowClient may be NULL during tests.
+ aura::client::TransientWindowClient* transient_window_client =
+ aura::client::GetTransientWindowClient();
RenderWidgetHostViewAura* old_child =
popup_parent_host_view_->popup_child_host_view_;
if (old_child) {
@@ -523,12 +481,15 @@ void RenderWidgetHostViewAura::InitAsPopup(
// similar mechanism to ensure a second popup doesn't cause the first one
// to never get a chance to filter events. See crbug.com/160589.
DCHECK(old_child->popup_parent_host_view_ == popup_parent_host_view_);
- popup_parent_host_view_->window_->RemoveTransientChild(old_child->window_);
+ if (transient_window_client) {
+ transient_window_client->RemoveTransientChild(
+ popup_parent_host_view_->window_, old_child->window_);
+ }
old_child->popup_parent_host_view_ = NULL;
}
popup_parent_host_view_->popup_child_host_view_ = this;
- window_->SetType(aura::client::WINDOW_TYPE_MENU);
- window_->Init(ui::LAYER_TEXTURED);
+ window_->SetType(ui::wm::WINDOW_TYPE_MENU);
+ window_->Init(aura::WINDOW_LAYER_TEXTURED);
window_->SetName("RenderWidgetHostViewAura");
aura::Window* root = popup_parent_host_view_->window_->GetRootWindow();
@@ -536,17 +497,26 @@ void RenderWidgetHostViewAura::InitAsPopup(
// Setting the transient child allows for the popup to get mouse events when
// in a system modal dialog.
// This fixes crbug.com/328593.
- popup_parent_host_view_->window_->AddTransientChild(window_);
+ if (transient_window_client) {
+ transient_window_client->AddTransientChild(
+ popup_parent_host_view_->window_, window_);
+ }
SetBounds(bounds_in_screen);
Show();
+#if !defined(OS_WIN) && !defined(OS_CHROMEOS)
+ if (NeedsInputGrab())
+ window_->SetCapture();
+#endif
+
+ event_filter_for_popup_exit_.reset(new EventFilterForPopupExit(this));
}
void RenderWidgetHostViewAura::InitAsFullscreen(
RenderWidgetHostView* reference_host_view) {
is_fullscreen_ = true;
- window_->SetType(aura::client::WINDOW_TYPE_NORMAL);
- window_->Init(ui::LAYER_TEXTURED);
+ window_->SetType(ui::wm::WINDOW_TYPE_NORMAL);
+ window_->Init(aura::WINDOW_LAYER_TEXTURED);
window_->SetName("RenderWidgetHostViewAura");
window_->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
@@ -578,8 +548,6 @@ void RenderWidgetHostViewAura::WasShown() {
if (!host_->is_hidden())
return;
host_->WasShown();
- software_frame_manager_->SetVisibility(true);
- delegated_frame_evictor_->SetVisible(true);
aura::Window* root = window_->GetRootWindow();
if (root) {
@@ -589,23 +557,21 @@ void RenderWidgetHostViewAura::WasShown() {
NotifyRendererOfCursorVisibilityState(cursor_client->IsCursorVisible());
}
- if (!current_surface_.get() && host_->is_accelerated_compositing_active() &&
- !released_front_lock_.get()) {
- ui::Compositor* compositor = GetCompositor();
- if (compositor)
- released_front_lock_ = compositor->GetCompositorLock();
- }
+ delegated_frame_host_->WasShown();
#if defined(OS_WIN)
+ if (legacy_render_widget_host_HWND_) {
+ // Reparent the legacy Chrome_RenderWidgetHostHWND window to the parent
+ // window before reparenting any plugins. This ensures that the plugin
+ // windows stay on top of the child Zorder in the parent and receive
+ // mouse events, etc.
+ legacy_render_widget_host_HWND_->UpdateParent(
+ GetNativeView()->GetHost()->GetAcceleratedWidget());
+ legacy_render_widget_host_HWND_->SetBounds(
+ window_->GetBoundsInRootWindow());
+ }
LPARAM lparam = reinterpret_cast<LPARAM>(this);
EnumChildWindows(ui::GetHiddenWindow(), ShowWindowsCallback, lparam);
-
- if (::IsWindow(plugin_parent_window_)) {
- gfx::Rect window_bounds = window_->GetBoundsInRootWindow();
- ::SetWindowPos(plugin_parent_window_, NULL, window_bounds.x(),
- window_bounds.y(), window_bounds.width(),
- window_bounds.height(), 0);
- }
#endif
}
@@ -613,21 +579,20 @@ void RenderWidgetHostViewAura::WasHidden() {
if (!host_ || host_->is_hidden())
return;
host_->WasHidden();
- software_frame_manager_->SetVisibility(false);
- delegated_frame_evictor_->SetVisible(false);
- released_front_lock_ = NULL;
+ delegated_frame_host_->WasHidden();
#if defined(OS_WIN)
constrained_rects_.clear();
- aura::WindowEventDispatcher* dispatcher = window_->GetDispatcher();
- if (dispatcher) {
- HWND parent = dispatcher->host()->GetAcceleratedWidget();
+ aura::WindowTreeHost* host = window_->GetHost();
+ if (host) {
+ HWND parent = host->GetAcceleratedWidget();
LPARAM lparam = reinterpret_cast<LPARAM>(this);
-
EnumChildWindows(parent, HideWindowsCallback, lparam);
+ // We reparent the legacy Chrome_RenderWidgetHostHWND window to the global
+ // hidden window on the same lines as Windowed plugin windows.
+ if (legacy_render_widget_host_HWND_)
+ legacy_render_widget_host_HWND_->UpdateParent(ui::GetHiddenWindow());
}
- if (::IsWindow(plugin_parent_window_))
- ::SetWindowPos(plugin_parent_window_, NULL, 0, 0, 0, 0, 0);
#endif
}
@@ -656,95 +621,28 @@ void RenderWidgetHostViewAura::SetBounds(const gfx::Rect& rect) {
InternalSetBounds(gfx::Rect(relative_origin, rect.size()));
}
-void RenderWidgetHostViewAura::MaybeCreateResizeLock() {
- if (!ShouldCreateResizeLock())
- return;
- DCHECK(window_->GetDispatcher());
- DCHECK(window_->GetDispatcher()->compositor());
-
- // Listen to changes in the compositor lock state.
- ui::Compositor* compositor = window_->GetDispatcher()->compositor();
- if (!compositor->HasObserver(this))
- compositor->AddObserver(this);
-
- bool defer_compositor_lock =
- can_lock_compositor_ == NO_PENDING_RENDERER_FRAME ||
- can_lock_compositor_ == NO_PENDING_COMMIT;
-
- if (can_lock_compositor_ == YES)
- can_lock_compositor_ = YES_DID_LOCK;
-
- resize_lock_ = CreateResizeLock(defer_compositor_lock);
-}
-
-bool RenderWidgetHostViewAura::ShouldCreateResizeLock() {
- // On Windows while resizing, the the resize locks makes us mis-paint a white
- // vertical strip (including the non-client area) if the content composition
- // is lagging the UI composition. So here we disable the throttling so that
- // the UI bits can draw ahead of the content thereby reducing the amount of
- // whiteout. Because this causes the content to be drawn at wrong sizes while
- // resizing we compensate by blocking the UI thread in Compositor::Draw() by
- // issuing a FinishAllRendering() if we are resizing.
-#if defined (OS_WIN)
- return false;
-#endif
-
- if (resize_lock_)
- return false;
-
- if (host_->should_auto_resize())
- return false;
- if (!host_->is_accelerated_compositing_active())
- return false;
-
- gfx::Size desired_size = window_->bounds().size();
- if (desired_size == current_frame_size_)
- return false;
-
- aura::WindowEventDispatcher* dispatcher = window_->GetDispatcher();
- if (!dispatcher)
- return false;
-
- ui::Compositor* compositor = dispatcher->compositor();
- if (!compositor)
- return false;
-
- return true;
-}
-
-scoped_ptr<ResizeLock> RenderWidgetHostViewAura::CreateResizeLock(
- bool defer_compositor_lock) {
- gfx::Size desired_size = window_->bounds().size();
- return scoped_ptr<ResizeLock>(new CompositorResizeLock(
- window_->GetDispatcher(),
- desired_size,
- defer_compositor_lock,
- base::TimeDelta::FromMilliseconds(kResizeLockTimeoutMs)));
-}
-
gfx::NativeView RenderWidgetHostViewAura::GetNativeView() const {
return window_;
}
gfx::NativeViewId RenderWidgetHostViewAura::GetNativeViewId() const {
#if defined(OS_WIN)
- aura::WindowEventDispatcher* dispatcher = window_->GetDispatcher();
- if (dispatcher)
- return reinterpret_cast<gfx::NativeViewId>(
- dispatcher->host()->GetAcceleratedWidget());
+ aura::WindowTreeHost* host = window_->GetHost();
+ if (host)
+ return reinterpret_cast<gfx::NativeViewId>(host->GetAcceleratedWidget());
#endif
return static_cast<gfx::NativeViewId>(NULL);
}
gfx::NativeViewAccessible RenderWidgetHostViewAura::GetNativeViewAccessible() {
#if defined(OS_WIN)
- aura::WindowEventDispatcher* dispatcher = window_->GetDispatcher();
- if (!dispatcher)
+ aura::WindowTreeHost* host = window_->GetHost();
+ if (!host)
return static_cast<gfx::NativeViewAccessible>(NULL);
- HWND hwnd = dispatcher->host()->GetAcceleratedWidget();
+ HWND hwnd = host->GetAcceleratedWidget();
- BrowserAccessibilityManager* manager =
- GetOrCreateBrowserAccessibilityManager();
+ CreateBrowserAccessibilityManagerIfNeeded();
+ BrowserAccessibilityManager* manager = GetBrowserAccessibilityManager();
if (manager)
return manager->GetRoot()->ToBrowserAccessibilityWin();
#endif
@@ -753,49 +651,33 @@ gfx::NativeViewAccessible RenderWidgetHostViewAura::GetNativeViewAccessible() {
return static_cast<gfx::NativeViewAccessible>(NULL);
}
-BrowserAccessibilityManager*
-RenderWidgetHostViewAura::GetOrCreateBrowserAccessibilityManager() {
- BrowserAccessibilityManager* manager = GetBrowserAccessibilityManager();
- if (manager)
- return manager;
-
-#if defined(OS_WIN)
- aura::WindowEventDispatcher* dispatcher = window_->GetDispatcher();
- if (!dispatcher)
- return NULL;
- HWND hwnd = dispatcher->host()->GetAcceleratedWidget();
-
- // The accessible_parent may be NULL at this point. The WebContents will pass
- // it down to this instance (by way of the RenderViewHost and
- // RenderWidgetHost) when it is known. This instance will then set it on its
- // BrowserAccessibilityManager.
- gfx::NativeViewAccessible accessible_parent =
- host_->GetParentNativeViewAccessible();
-
- manager = new BrowserAccessibilityManagerWin(
- hwnd, accessible_parent,
- BrowserAccessibilityManagerWin::GetEmptyDocument(), this);
-#else
- manager = BrowserAccessibilityManager::Create(
- BrowserAccessibilityManager::GetEmptyDocument(), this);
-#endif
-
- SetBrowserAccessibilityManager(manager);
- return manager;
+ui::TextInputClient* RenderWidgetHostViewAura::GetTextInputClient() {
+ return this;
}
void RenderWidgetHostViewAura::SetKeyboardFocus() {
#if defined(OS_WIN)
if (CanFocus()) {
- aura::WindowEventDispatcher* dispatcher = window_->GetDispatcher();
- if (dispatcher)
- ::SetFocus(dispatcher->host()->GetAcceleratedWidget());
+ aura::WindowTreeHost* host = window_->GetHost();
+ if (host)
+ ::SetFocus(host->GetAcceleratedWidget());
}
#endif
}
+RenderFrameHostImpl* RenderWidgetHostViewAura::GetFocusedFrame() {
+ if (!host_->IsRenderView())
+ return NULL;
+ RenderViewHost* rvh = RenderViewHost::From(host_);
+ FrameTreeNode* focused_frame =
+ rvh->GetDelegate()->GetFrameTree()->GetFocusedFrame();
+ if (!focused_frame)
+ return NULL;
+
+ return focused_frame->current_frame_host();
+}
+
void RenderWidgetHostViewAura::MovePluginWindows(
- const gfx::Vector2d& scroll_offset,
const std::vector<WebPluginGeometry>& plugin_window_moves) {
#if defined(OS_WIN)
// We need to clip the rectangle to the tab's viewport, otherwise we will draw
@@ -804,17 +686,16 @@ void RenderWidgetHostViewAura::MovePluginWindows(
DCHECK(plugin_window_moves.empty());
return;
}
- HWND parent = window_->GetDispatcher()->host()->GetAcceleratedWidget();
+ HWND parent = window_->GetHost()->GetAcceleratedWidget();
gfx::Rect view_bounds = window_->GetBoundsInRootWindow();
std::vector<WebPluginGeometry> moves = plugin_window_moves;
- gfx::Rect view_port(scroll_offset.x(), scroll_offset.y(), view_bounds.width(),
- view_bounds.height());
+ gfx::Rect view_port(view_bounds.size());
for (size_t i = 0; i < moves.size(); ++i) {
gfx::Rect clip(moves[i].clip_rect);
gfx::Vector2d view_port_offset(
- moves[i].window_rect.OffsetFromOrigin() + scroll_offset);
+ moves[i].window_rect.OffsetFromOrigin());
clip.Offset(view_port_offset);
clip.Intersect(view_port);
clip.Offset(-view_port_offset);
@@ -866,17 +747,25 @@ bool RenderWidgetHostViewAura::HasFocus() const {
}
bool RenderWidgetHostViewAura::IsSurfaceAvailableForCopy() const {
- return CanCopyToBitmap() || !!host_->GetBackingStore(false);
+ return delegated_frame_host_->CanCopyToBitmap();
}
void RenderWidgetHostViewAura::Show() {
window_->Show();
WasShown();
+#if defined(OS_WIN)
+ if (legacy_render_widget_host_HWND_)
+ legacy_render_widget_host_HWND_->Show();
+#endif
}
void RenderWidgetHostViewAura::Hide() {
window_->Hide();
WasHidden();
+#if defined(OS_WIN)
+ if (legacy_render_widget_host_HWND_)
+ legacy_render_widget_host_HWND_->Hide();
+#endif
}
bool RenderWidgetHostViewAura::IsShowing() {
@@ -884,20 +773,26 @@ bool RenderWidgetHostViewAura::IsShowing() {
}
gfx::Rect RenderWidgetHostViewAura::GetViewBounds() const {
- // This is the size that we want the renderer to produce. While we're waiting
- // for the correct frame (i.e. during a resize), don't change the size so that
- // we don't pipeline more resizes than we can handle.
- gfx::Rect bounds(window_->GetBoundsInScreen());
- if (resize_lock_.get())
- return gfx::Rect(bounds.origin(), resize_lock_->expected_size());
- else
- return bounds;
+ return window_->GetBoundsInScreen();
+}
+
+void RenderWidgetHostViewAura::SetBackgroundOpaque(bool opaque) {
+ RenderWidgetHostViewBase::SetBackgroundOpaque(opaque);
+ host_->SetBackgroundOpaque(opaque);
+ window_->layer()->SetFillsBoundsOpaquely(opaque);
+}
+
+gfx::Size RenderWidgetHostViewAura::GetVisibleViewportSize() const {
+ gfx::Rect requested_rect(GetRequestedRendererSize());
+ requested_rect.Inset(insets_);
+ return requested_rect.size();
}
-void RenderWidgetHostViewAura::SetBackground(const SkBitmap& background) {
- RenderWidgetHostViewBase::SetBackground(background);
- host_->SetBackground(background);
- window_->layer()->SetFillsBoundsOpaquely(background.isOpaque());
+void RenderWidgetHostViewAura::SetInsets(const gfx::Insets& insets) {
+ if (insets != insets_) {
+ insets_ = insets;
+ host_->WasResized();
+ }
}
void RenderWidgetHostViewAura::UpdateCursor(const WebCursor& cursor) {
@@ -909,27 +804,27 @@ void RenderWidgetHostViewAura::UpdateCursor(const WebCursor& cursor) {
}
void RenderWidgetHostViewAura::SetIsLoading(bool is_loading) {
- if (is_loading_ && !is_loading && paint_observer_)
- paint_observer_->OnPageLoadComplete();
is_loading_ = is_loading;
UpdateCursorIfOverSelf();
}
-void RenderWidgetHostViewAura::TextInputTypeChanged(
- ui::TextInputType type,
- ui::TextInputMode input_mode,
- bool can_compose_inline) {
- if (text_input_type_ != type ||
- text_input_mode_ != input_mode ||
- can_compose_inline_ != can_compose_inline) {
- text_input_type_ = type;
- text_input_mode_ = input_mode;
- can_compose_inline_ = can_compose_inline;
+void RenderWidgetHostViewAura::TextInputStateChanged(
+ const ViewHostMsg_TextInputState_Params& params) {
+ if (text_input_type_ != params.type ||
+ text_input_mode_ != params.mode ||
+ can_compose_inline_ != params.can_compose_inline) {
+ text_input_type_ = params.type;
+ text_input_mode_ = params.mode;
+ can_compose_inline_ = params.can_compose_inline;
if (GetInputMethod())
GetInputMethod()->OnTextInputTypeChanged(this);
if (touch_editing_client_)
touch_editing_client_->OnTextInputTypeChanged(text_input_type_);
}
+ if (params.show_ime_if_needed && params.type != ui::TEXT_INPUT_TYPE_NONE) {
+ if (GetInputMethod())
+ GetInputMethod()->ShowImeIfNeeded();
+ }
}
void RenderWidgetHostViewAura::ImeCancelComposition() {
@@ -944,58 +839,6 @@ void RenderWidgetHostViewAura::ImeCompositionRangeChanged(
composition_character_bounds_ = character_bounds;
}
-void RenderWidgetHostViewAura::DidUpdateBackingStore(
- const gfx::Rect& scroll_rect,
- const gfx::Vector2d& scroll_delta,
- const std::vector<gfx::Rect>& copy_rects,
- const ui::LatencyInfo& latency_info) {
- if (accelerated_compositing_state_changed_)
- UpdateExternalTexture();
-
- software_latency_info_.MergeWith(latency_info);
-
- // Use the state of the RenderWidgetHost and not the window as the two may
- // differ. In particular if the window is hidden but the renderer isn't and we
- // ignore the update and the window is made visible again the layer isn't
- // marked as dirty and we show the wrong thing.
- // We do this after UpdateExternalTexture() so that when we become visible
- // we're not drawing a stale texture.
- if (host_->is_hidden())
- return;
-
- gfx::Rect clip_rect;
- if (paint_canvas_) {
- SkRect sk_clip_rect;
- if (paint_canvas_->sk_canvas()->getClipBounds(&sk_clip_rect))
- clip_rect = gfx::ToEnclosingRect(gfx::SkRectToRectF(sk_clip_rect));
- }
-
- if (!scroll_rect.IsEmpty())
- SchedulePaintIfNotInClip(scroll_rect, clip_rect);
-
-#if defined(OS_WIN)
- aura::WindowEventDispatcher* dispatcher = window_->GetDispatcher();
-#endif
- for (size_t i = 0; i < copy_rects.size(); ++i) {
- gfx::Rect rect = gfx::SubtractRects(copy_rects[i], scroll_rect);
- if (rect.IsEmpty())
- continue;
-
- SchedulePaintIfNotInClip(rect, clip_rect);
-
-#if defined(OS_WIN)
- if (dispatcher) {
- // Send the invalid rect in screen coordinates.
- gfx::Rect screen_rect = GetViewBounds();
- gfx::Rect invalid_screen_rect(rect);
- invalid_screen_rect.Offset(screen_rect.x(), screen_rect.y());
- HWND hwnd = dispatcher->host()->GetAcceleratedWidget();
- PaintPluginWindowsHelper(hwnd, invalid_screen_rect);
- }
-#endif // defined(OS_WIN)
- }
-}
-
void RenderWidgetHostViewAura::RenderProcessGone(base::TerminationStatus status,
int error_code) {
UpdateCursorIfOverSelf();
@@ -1031,15 +874,27 @@ void RenderWidgetHostViewAura::SelectionChanged(const base::string16& text,
#if defined(USE_X11) && !defined(OS_CHROMEOS)
if (text.empty() || range.is_empty())
return;
+ size_t pos = range.GetMin() - offset;
+ size_t n = range.length();
+
+ DCHECK(pos + n <= text.length()) << "The text can not fully cover range.";
+ if (pos >= text.length()) {
+ NOTREACHED() << "The text can not cover range.";
+ return;
+ }
// Set the CLIPBOARD_TYPE_SELECTION to the ui::Clipboard.
ui::ScopedClipboardWriter clipboard_writer(
ui::Clipboard::GetForCurrentThread(),
ui::CLIPBOARD_TYPE_SELECTION);
- clipboard_writer.WriteText(text);
+ clipboard_writer.WriteText(text.substr(pos, n));
#endif // defined(USE_X11) && !defined(OS_CHROMEOS)
}
+gfx::Size RenderWidgetHostViewAura::GetRequestedRendererSize() const {
+ return delegated_frame_host_->GetRequestedRendererSize();
+}
+
void RenderWidgetHostViewAura::SelectionBoundsChanged(
const ViewHostMsg_SelectionBounds_Params& params) {
if (selection_anchor_rect_ == params.anchor_rect &&
@@ -1068,281 +923,118 @@ void RenderWidgetHostViewAura::ScrollOffsetChanged() {
cursor_client->DisableMouseEvents();
}
-BackingStore* RenderWidgetHostViewAura::AllocBackingStore(
- const gfx::Size& size) {
- return new BackingStoreAura(host_, size);
-}
-
void RenderWidgetHostViewAura::CopyFromCompositingSurface(
const gfx::Rect& src_subrect,
const gfx::Size& dst_size,
- const base::Callback<void(bool, const SkBitmap&)>& callback) {
- if (!CanCopyToBitmap()) {
- callback.Run(false, SkBitmap());
- return;
- }
-
- const gfx::Size& dst_size_in_pixel = ConvertViewSizeToPixel(this, dst_size);
- scoped_ptr<cc::CopyOutputRequest> request =
- cc::CopyOutputRequest::CreateRequest(base::Bind(
- &RenderWidgetHostViewAura::CopyFromCompositingSurfaceHasResult,
- dst_size_in_pixel,
- callback));
- gfx::Rect src_subrect_in_pixel =
- ConvertRectToPixel(current_device_scale_factor_, src_subrect);
- request->set_area(src_subrect_in_pixel);
- window_->layer()->RequestCopyOfOutput(request.Pass());
+ const base::Callback<void(bool, const SkBitmap&)>& callback,
+ const SkBitmap::Config config) {
+ delegated_frame_host_->CopyFromCompositingSurface(
+ src_subrect, dst_size, callback, config);
}
void RenderWidgetHostViewAura::CopyFromCompositingSurfaceToVideoFrame(
const gfx::Rect& src_subrect,
const scoped_refptr<media::VideoFrame>& target,
const base::Callback<void(bool)>& callback) {
- if (!CanCopyToVideoFrame()) {
- callback.Run(false);
- return;
- }
-
- // Try get a texture to reuse.
- scoped_refptr<OwnedMailbox> subscriber_texture;
- if (frame_subscriber_) {
- 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<cc::CopyOutputRequest> request =
- cc::CopyOutputRequest::CreateRequest(base::Bind(
- &RenderWidgetHostViewAura::
- CopyFromCompositingSurfaceHasResultForVideo,
- AsWeakPtr(), // For caching the ReadbackYUVInterface on this class.
- subscriber_texture,
- target,
- callback));
- gfx::Rect src_subrect_in_pixel =
- ConvertRectToPixel(current_device_scale_factor_, src_subrect);
- request->set_area(src_subrect_in_pixel);
- if (subscriber_texture) {
- request->SetTextureMailbox(cc::TextureMailbox(
- subscriber_texture->mailbox(), subscriber_texture->sync_point()));
- }
- window_->layer()->RequestCopyOfOutput(request.Pass());
-}
-
-bool RenderWidgetHostViewAura::CanCopyToBitmap() const {
- return GetCompositor() && window_->layer()->has_external_content();
+ delegated_frame_host_->CopyFromCompositingSurfaceToVideoFrame(
+ src_subrect, target, callback);
}
bool RenderWidgetHostViewAura::CanCopyToVideoFrame() const {
- return GetCompositor() &&
- window_->layer()->has_external_content() &&
- host_->is_accelerated_compositing_active();
+ return delegated_frame_host_->CanCopyToVideoFrame();
}
bool RenderWidgetHostViewAura::CanSubscribeFrame() const {
- return true;
+ return delegated_frame_host_->CanSubscribeFrame();
}
void RenderWidgetHostViewAura::BeginFrameSubscription(
scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) {
- frame_subscriber_ = subscriber.Pass();
+ delegated_frame_host_->BeginFrameSubscription(subscriber.Pass());
}
void RenderWidgetHostViewAura::EndFrameSubscription() {
- idle_frame_subscriber_textures_.clear();
- frame_subscriber_.reset();
-}
-
-void RenderWidgetHostViewAura::OnAcceleratedCompositingStateChange() {
- // Delay processing the state change until we either get a software frame if
- // switching to software mode or receive a buffers swapped notification
- // if switching to accelerated mode.
- // Sometimes (e.g. on a page load) the renderer will spuriously disable then
- // re-enable accelerated compositing, causing us to flash.
- // TODO(piman): factor the enable/disable accelerated compositing message into
- // the UpdateRect/AcceleratedSurfaceBuffersSwapped messages so that we have
- // fewer inconsistent temporary states.
- accelerated_compositing_state_changed_ = true;
+ delegated_frame_host_->EndFrameSubscription();
}
void RenderWidgetHostViewAura::AcceleratedSurfaceInitialized(int host_id,
int route_id) {
}
-bool RenderWidgetHostViewAura::ShouldSkipFrame(gfx::Size size_in_dip) const {
- if (can_lock_compositor_ == NO_PENDING_RENDERER_FRAME ||
- can_lock_compositor_ == NO_PENDING_COMMIT ||
- !resize_lock_.get())
- return false;
+void RenderWidgetHostViewAura::SnapToPhysicalPixelBoundary() {
+ // The top left corner of our view in window coordinates might not land on a
+ // device pixel boundary if we have a non-integer device scale. In that case,
+ // to avoid the web contents area looking blurry we translate the web contents
+ // in the +x, +y direction to land on the nearest pixel boundary. This may
+ // cause the bottom and right edges to be clipped slightly, but that's ok.
+ gfx::Point view_offset_dips = window_->GetBoundsInRootWindow().origin();
+ gfx::PointF view_offset = view_offset_dips;
+ view_offset.Scale(current_device_scale_factor_);
+ gfx::PointF view_offset_snapped(std::ceil(view_offset.x()),
+ std::ceil(view_offset.y()));
- return size_in_dip != resize_lock_->expected_size();
+ gfx::Vector2dF fudge = view_offset_snapped - view_offset;
+ fudge.Scale(1.0 / current_device_scale_factor_);
+ GetLayer()->SetSubpixelPositionOffset(fudge);
}
void RenderWidgetHostViewAura::InternalSetBounds(const gfx::Rect& rect) {
if (HasDisplayPropertyChanged(window_))
host_->InvalidateScreenInfo();
+ SnapToPhysicalPixelBoundary();
// Don't recursively call SetBounds if this bounds update is the result of
// a Window::SetBoundsInternal call.
if (!in_bounds_changed_)
window_->SetBounds(rect);
host_->WasResized();
- MaybeCreateResizeLock();
+ delegated_frame_host_->WasResized();
if (touch_editing_client_) {
touch_editing_client_->OnSelectionOrCursorChanged(selection_anchor_rect_,
selection_focus_rect_);
}
#if defined(OS_WIN)
- // Create the dummy plugin parent window which will be passed as the
- // container window to windowless plugins.
+ // Create the legacy dummy window which corresponds to the bounds of the
+ // webcontents. This will be passed as the container window for windowless
+ // plugins.
// Plugins like Flash assume the container window which is returned via the
// NPNVnetscapeWindow property corresponds to the bounds of the webpage.
// This is not true in Aura where we have only HWND which is the main Aura
// window. If we return this window to plugins like Flash then it causes the
// coordinate translations done by these plugins to break.
- if (!plugin_parent_window_ && GetNativeViewId()) {
- plugin_parent_window_ = ::CreateWindowEx(
- 0, L"Static", NULL, WS_CHILDWINDOW, 0, 0, 0, 0,
- reinterpret_cast<HWND>(GetNativeViewId()), NULL, NULL, NULL);
- if (::IsWindow(plugin_parent_window_))
- ::SetProp(plugin_parent_window_, content::kPluginDummyParentProperty,
- reinterpret_cast<HANDLE>(true));
- }
- if (::IsWindow(plugin_parent_window_)) {
- gfx::Rect window_bounds = window_->GetBoundsInRootWindow();
- ::SetWindowPos(plugin_parent_window_, NULL, window_bounds.x(),
- window_bounds.y(), window_bounds.width(),
- window_bounds.height(), 0);
- }
-#endif
-}
-
-void RenderWidgetHostViewAura::CheckResizeLock() {
- if (!resize_lock_ || resize_lock_->expected_size() != current_frame_size_)
- 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();
- ui::Compositor* compositor = GetCompositor();
- if (compositor) {
- if (!compositor->HasObserver(this))
- compositor->AddObserver(this);
- }
-}
-
-void RenderWidgetHostViewAura::UpdateExternalTexture() {
- // Delay processing accelerated compositing state change till here where we
- // act upon the state change. (Clear the external texture if switching to
- // software mode or set the external texture if going to accelerated mode).
- if (accelerated_compositing_state_changed_)
- accelerated_compositing_state_changed_ = false;
-
- bool is_compositing_active = host_->is_accelerated_compositing_active();
- if (is_compositing_active && current_surface_.get()) {
- window_->layer()->SetExternalTexture(current_surface_.get());
- current_frame_size_ = ConvertSizeToDIP(
- current_surface_->device_scale_factor(), current_surface_->size());
- CheckResizeLock();
- software_frame_manager_->DiscardCurrentFrame();
- } else if (is_compositing_active &&
- software_frame_manager_->HasCurrentFrame()) {
- cc::TextureMailbox mailbox;
- scoped_ptr<cc::SingleReleaseCallback> callback;
- software_frame_manager_->GetCurrentFrameMailbox(&mailbox, &callback);
- window_->layer()->SetTextureMailbox(mailbox,
- callback.Pass(),
- last_swapped_surface_scale_factor_);
- current_frame_size_ = ConvertSizeToDIP(last_swapped_surface_scale_factor_,
- mailbox.shared_memory_size());
- CheckResizeLock();
- } else {
- window_->layer()->SetShowPaintedContent();
- resize_lock_.reset();
- host_->WasResized();
- software_frame_manager_->DiscardCurrentFrame();
- }
-}
-
-bool RenderWidgetHostViewAura::SwapBuffersPrepare(
- const gfx::Rect& surface_rect,
- float surface_scale_factor,
- const gfx::Rect& damage_rect,
- const std::string& mailbox_name,
- const BufferPresentedCallback& ack_callback) {
- if (last_swapped_surface_size_ != surface_rect.size()) {
- // The surface could have shrunk since we skipped an update, in which
- // case we can expect a full update.
- DLOG_IF(ERROR, damage_rect != surface_rect) << "Expected full damage rect";
- skipped_damage_.setEmpty();
- last_swapped_surface_size_ = surface_rect.size();
- last_swapped_surface_scale_factor_ = surface_scale_factor;
- }
-
- if (ShouldSkipFrame(ConvertSizeToDIP(surface_scale_factor,
- surface_rect.size())) ||
- mailbox_name.empty()) {
- skipped_damage_.op(RectToSkIRect(damage_rect), SkRegion::kUnion_Op);
- ack_callback.Run(true, scoped_refptr<ui::Texture>());
- return false;
- }
-
- ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
- current_surface_ =
- factory->CreateTransportClient(surface_scale_factor);
- if (!current_surface_.get()) {
- LOG(ERROR) << "Failed to create ImageTransport texture";
- ack_callback.Run(true, scoped_refptr<ui::Texture>());
- return false;
- }
-
- current_surface_->Consume(mailbox_name, surface_rect.size());
- released_front_lock_ = NULL;
- UpdateExternalTexture();
-
- return true;
-}
-
-void RenderWidgetHostViewAura::SwapBuffersCompleted(
- const BufferPresentedCallback& ack_callback,
- const scoped_refptr<ui::Texture>& texture_to_return) {
- ui::Compositor* compositor = GetCompositor();
- if (!compositor) {
- ack_callback.Run(false, texture_to_return);
- } else {
- AddOnCommitCallbackAndDisableLocks(
- base::Bind(ack_callback, false, texture_to_return));
+ // Additonally the legacy dummy window is needed for accessibility and for
+ // scrolling to work in legacy drivers for trackpoints/trackpads, etc.
+ if (GetNativeViewId()) {
+ if (!legacy_render_widget_host_HWND_) {
+ legacy_render_widget_host_HWND_ = LegacyRenderWidgetHostHWND::Create(
+ reinterpret_cast<HWND>(GetNativeViewId()));
+ BrowserAccessibilityManagerWin* manager =
+ static_cast<BrowserAccessibilityManagerWin*>(
+ GetBrowserAccessibilityManager());
+ if (manager)
+ manager->SetAccessibleHWND(legacy_render_widget_host_HWND_.get());
+ }
+ if (legacy_render_widget_host_HWND_) {
+ legacy_render_widget_host_HWND_->SetBounds(
+ window_->GetBoundsInRootWindow());
+ }
}
- DidReceiveFrameFromRenderer();
+ if (mouse_locked_)
+ UpdateMouseLockRegion();
+#endif
}
-void RenderWidgetHostViewAura::DidReceiveFrameFromRenderer() {
- if (frame_subscriber() && CanCopyToVideoFrame()) {
- const base::Time present_time = base::Time::Now();
- scoped_refptr<media::VideoFrame> frame;
- RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback callback;
- if (frame_subscriber()->ShouldCaptureFrame(present_time,
- &frame, &callback)) {
- CopyFromCompositingSurfaceToVideoFrame(
- gfx::Rect(current_frame_size_),
- frame,
- base::Bind(callback, present_time));
- }
- }
+#if defined(OS_WIN)
+bool RenderWidgetHostViewAura::UsesNativeWindowFrame() const {
+ return (legacy_render_widget_host_HWND_ != NULL);
}
-#if defined(OS_WIN)
void RenderWidgetHostViewAura::UpdateConstrainedWindowRects(
const std::vector<gfx::Rect>& rects) {
// Check this before setting constrained_rects_, so that next time they're set
// and we have a root window we don't early return.
- if (!window_->GetDispatcher())
+ if (!window_->GetHost())
return;
if (rects == constrained_rects_)
@@ -1350,7 +1042,7 @@ void RenderWidgetHostViewAura::UpdateConstrainedWindowRects(
constrained_rects_ = rects;
- HWND parent = window_->GetDispatcher()->host()->GetAcceleratedWidget();
+ HWND parent = window_->GetHost()->GetAcceleratedWidget();
CutoutRectsParams params;
params.widget = this;
params.cutout_rects = constrained_rects_;
@@ -1358,273 +1050,20 @@ void RenderWidgetHostViewAura::UpdateConstrainedWindowRects(
LPARAM lparam = reinterpret_cast<LPARAM>(&params);
EnumChildWindows(parent, SetCutoutRectsCallback, lparam);
}
+
+void RenderWidgetHostViewAura::UpdateMouseLockRegion() {
+ // Clip the cursor if chrome is running on regular desktop.
+ if (gfx::Screen::GetScreenFor(window_) == gfx::Screen::GetNativeScreen()) {
+ RECT window_rect = window_->GetBoundsInScreen().ToRECT();
+ ::ClipCursor(&window_rect);
+ }
+}
#endif
void RenderWidgetHostViewAura::AcceleratedSurfaceBuffersSwapped(
const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params_in_pixel,
int gpu_host_id) {
- BufferPresentedCallback ack_callback = base::Bind(
- &AcknowledgeBufferForGpu,
- params_in_pixel.route_id,
- gpu_host_id,
- params_in_pixel.mailbox_name);
- BuffersSwapped(params_in_pixel.size,
- gfx::Rect(params_in_pixel.size),
- params_in_pixel.scale_factor,
- params_in_pixel.mailbox_name,
- params_in_pixel.latency_info,
- ack_callback);
-}
-
-void RenderWidgetHostViewAura::SwapDelegatedFrame(
- uint32 output_surface_id,
- scoped_ptr<cc::DelegatedFrameData> frame_data,
- float frame_device_scale_factor,
- const ui::LatencyInfo& latency_info) {
- DCHECK_NE(0u, frame_data->render_pass_list.size());
-
- cc::RenderPass* root_pass = frame_data->render_pass_list.back();
-
- gfx::Size frame_size = root_pass->output_rect.size();
- gfx::Size frame_size_in_dip =
- ConvertSizeToDIP(frame_device_scale_factor, frame_size);
-
- gfx::Rect damage_rect = gfx::ToEnclosingRect(root_pass->damage_rect);
- damage_rect.Intersect(gfx::Rect(frame_size));
- gfx::Rect damage_rect_in_dip =
- ConvertRectToDIP(frame_device_scale_factor, damage_rect);
-
- software_frame_manager_->DiscardCurrentFrame();
-
- if (ShouldSkipFrame(frame_size_in_dip)) {
- cc::CompositorFrameAck ack;
- cc::TransferableResource::ReturnResources(frame_data->resource_list,
- &ack.resources);
- RenderWidgetHostImpl::SendSwapCompositorFrameAck(
- host_->GetRoutingID(), output_surface_id,
- host_->GetProcess()->GetID(), 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();
- 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();
-
- // 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;
- }
- if (frame_size.IsEmpty()) {
- DCHECK_EQ(0u, frame_data->resource_list.size());
- EvictDelegatedFrame();
- } else {
- if (!resource_collection_) {
- 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_) {
- frame_provider_ = new cc::DelegatedFrameProvider(
- resource_collection_.get(), frame_data.Pass());
- window_->layer()->SetShowDelegatedContent(frame_provider_.get(),
- frame_size_in_dip);
- } else {
- frame_provider_->SetFrameData(frame_data.Pass());
- }
- }
- released_front_lock_ = NULL;
- current_frame_size_ = frame_size_in_dip;
- CheckResizeLock();
-
- if (paint_observer_)
- paint_observer_->OnUpdateCompositorContent();
- window_->SchedulePaintInRect(damage_rect_in_dip);
-
- pending_delegated_ack_count_++;
-
- ui::Compositor* compositor = GetCompositor();
- if (!compositor) {
- SendDelegatedFrameAck(output_surface_id);
- } else {
- compositor->SetLatencyInfo(latency_info);
- AddOnCommitCallbackAndDisableLocks(
- base::Bind(&RenderWidgetHostViewAura::SendDelegatedFrameAck,
- AsWeakPtr(),
- output_surface_id));
- }
- DidReceiveFrameFromRenderer();
- if (frame_provider_.get())
- delegated_frame_evictor_->SwappedFrame(!host_->is_hidden());
- // Note: the frame may have been evicted immediately.
-}
-
-void RenderWidgetHostViewAura::SendDelegatedFrameAck(uint32 output_surface_id) {
- cc::CompositorFrameAck ack;
- if (resource_collection_)
- resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources);
- RenderWidgetHostImpl::SendSwapCompositorFrameAck(host_->GetRoutingID(),
- output_surface_id,
- host_->GetProcess()->GetID(),
- ack);
- DCHECK_GT(pending_delegated_ack_count_, 0);
- pending_delegated_ack_count_--;
-}
-
-void RenderWidgetHostViewAura::UnusedResourcesAreAvailable() {
- if (pending_delegated_ack_count_)
- return;
-
- SendReturnedDelegatedResources(last_output_surface_id_);
-}
-
-void RenderWidgetHostViewAura::SendReturnedDelegatedResources(
- uint32 output_surface_id) {
- cc::CompositorFrameAck ack;
- if (resource_collection_)
- resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources);
- DCHECK(!ack.resources.empty());
- RenderWidgetHostImpl::SendReclaimCompositorResources(
- host_->GetRoutingID(),
- output_surface_id,
- host_->GetProcess()->GetID(),
- ack);
-}
-
-void RenderWidgetHostViewAura::EvictDelegatedFrame() {
- window_->layer()->SetShowPaintedContent();
- frame_provider_ = NULL;
- delegated_frame_evictor_->DiscardedFrame();
-}
-
-void RenderWidgetHostViewAura::SwapSoftwareFrame(
- uint32 output_surface_id,
- scoped_ptr<cc::SoftwareFrameData> frame_data,
- float frame_device_scale_factor,
- const ui::LatencyInfo& latency_info) {
- const gfx::Size& frame_size = frame_data->size;
- const gfx::Rect& damage_rect = frame_data->damage_rect;
- gfx::Size frame_size_in_dip =
- ConvertSizeToDIP(frame_device_scale_factor, frame_size);
- if (ShouldSkipFrame(frame_size_in_dip)) {
- ReleaseSoftwareFrame(output_surface_id, frame_data->id);
- SendSoftwareFrameAck(output_surface_id);
- return;
- }
-
- if (!software_frame_manager_->SwapToNewFrame(
- output_surface_id,
- frame_data.get(),
- frame_device_scale_factor,
- host_->GetProcess()->GetHandle())) {
- ReleaseSoftwareFrame(output_surface_id, frame_data->id);
- SendSoftwareFrameAck(output_surface_id);
- return;
- }
-
- if (last_swapped_surface_size_ != frame_size) {
- DLOG_IF(ERROR, damage_rect != gfx::Rect(frame_size))
- << "Expected full damage rect";
- }
- last_swapped_surface_size_ = frame_size;
- last_swapped_surface_scale_factor_ = frame_device_scale_factor;
-
- cc::TextureMailbox mailbox;
- scoped_ptr<cc::SingleReleaseCallback> callback;
- software_frame_manager_->GetCurrentFrameMailbox(&mailbox, &callback);
- DCHECK(mailbox.IsSharedMemory());
- current_frame_size_ = frame_size_in_dip;
-
- released_front_lock_ = NULL;
- CheckResizeLock();
- window_->layer()->SetTextureMailbox(mailbox,
- callback.Pass(),
- frame_device_scale_factor);
- window_->SchedulePaintInRect(
- ConvertRectToDIP(frame_device_scale_factor, damage_rect));
-
- ui::Compositor* compositor = GetCompositor();
- if (compositor) {
- compositor->SetLatencyInfo(latency_info);
- AddOnCommitCallbackAndDisableLocks(
- base::Bind(&RenderWidgetHostViewAura::SendSoftwareFrameAck,
- AsWeakPtr(),
- output_surface_id));
- } else {
- SendSoftwareFrameAck(output_surface_id);
- }
- if (paint_observer_)
- paint_observer_->OnUpdateCompositorContent();
- DidReceiveFrameFromRenderer();
-
- software_frame_manager_->SwapToNewFrameComplete(!host_->is_hidden());
-}
-
-void RenderWidgetHostViewAura::SendSoftwareFrameAck(uint32 output_surface_id) {
- unsigned software_frame_id = 0;
- if (released_software_frame_ &&
- released_software_frame_->output_surface_id == output_surface_id) {
- software_frame_id = released_software_frame_->frame_id;
- released_software_frame_.reset();
- }
-
- cc::CompositorFrameAck ack;
- ack.last_software_frame_id = software_frame_id;
- RenderWidgetHostImpl::SendSwapCompositorFrameAck(
- host_->GetRoutingID(), output_surface_id,
- host_->GetProcess()->GetID(), ack);
- SendReclaimSoftwareFrames();
-}
-
-void RenderWidgetHostViewAura::SendReclaimSoftwareFrames() {
- if (!released_software_frame_)
- return;
- cc::CompositorFrameAck ack;
- ack.last_software_frame_id = released_software_frame_->frame_id;
- RenderWidgetHostImpl::SendReclaimCompositorResources(
- host_->GetRoutingID(),
- released_software_frame_->output_surface_id,
- host_->GetProcess()->GetID(),
- ack);
- released_software_frame_.reset();
-}
-
-void RenderWidgetHostViewAura::ReleaseSoftwareFrame(
- uint32 output_surface_id,
- unsigned software_frame_id) {
- SendReclaimSoftwareFrames();
- DCHECK(!released_software_frame_);
- released_software_frame_.reset(new ReleasedFrameInfo(
- output_surface_id, software_frame_id));
+ // Oldschool composited mode is no longer supported.
}
void RenderWidgetHostViewAura::OnSwapCompositorFrame(
@@ -1632,47 +1071,21 @@ void RenderWidgetHostViewAura::OnSwapCompositorFrame(
scoped_ptr<cc::CompositorFrame> frame) {
TRACE_EVENT0("content", "RenderWidgetHostViewAura::OnSwapCompositorFrame");
if (frame->delegated_frame_data) {
- SwapDelegatedFrame(output_surface_id,
- frame->delegated_frame_data.Pass(),
- frame->metadata.device_scale_factor,
- frame->metadata.latency_info);
+ delegated_frame_host_->SwapDelegatedFrame(
+ output_surface_id,
+ frame->delegated_frame_data.Pass(),
+ frame->metadata.device_scale_factor,
+ frame->metadata.latency_info);
return;
}
if (frame->software_frame_data) {
- SwapSoftwareFrame(output_surface_id,
- frame->software_frame_data.Pass(),
- frame->metadata.device_scale_factor,
- frame->metadata.latency_info);
+ DLOG(ERROR) << "Unable to use software frame in aura";
+ RecordAction(
+ base::UserMetricsAction("BadMessageTerminate_SharedMemoryAura"));
+ host_->GetProcess()->ReceivedBadMessage();
return;
}
-
- if (!frame->gl_frame_data || frame->gl_frame_data->mailbox.IsZero())
- return;
-
- BufferPresentedCallback ack_callback = base::Bind(
- &SendCompositorFrameAck,
- host_->GetRoutingID(), output_surface_id, host_->GetProcess()->GetID(),
- frame->gl_frame_data->mailbox, frame->gl_frame_data->size);
-
- if (!frame->gl_frame_data->sync_point) {
- LOG(ERROR) << "CompositorFrame without sync point. Skipping frame...";
- ack_callback.Run(true, scoped_refptr<ui::Texture>());
- return;
- }
-
- ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
- factory->WaitSyncPoint(frame->gl_frame_data->sync_point);
-
- std::string mailbox_name(
- reinterpret_cast<const char*>(frame->gl_frame_data->mailbox.name),
- sizeof(frame->gl_frame_data->mailbox.name));
- BuffersSwapped(frame->gl_frame_data->size,
- frame->gl_frame_data->sub_buffer_rect,
- frame->metadata.device_scale_factor,
- mailbox_name,
- frame->metadata.latency_info,
- ack_callback);
}
#if defined(OS_WIN)
@@ -1686,120 +1099,24 @@ void RenderWidgetHostViewAura::SetParentNativeViewAccessible(
gfx::NativeViewId RenderWidgetHostViewAura::GetParentForWindowlessPlugin()
const {
- return reinterpret_cast<gfx::NativeViewId>(plugin_parent_window_);
-}
-#endif
-
-void RenderWidgetHostViewAura::BuffersSwapped(
- const gfx::Size& surface_size,
- const gfx::Rect& damage_rect,
- float surface_scale_factor,
- const std::string& mailbox_name,
- const ui::LatencyInfo& latency_info,
- const BufferPresentedCallback& ack_callback) {
- scoped_refptr<ui::Texture> previous_texture(current_surface_);
- const gfx::Rect surface_rect = gfx::Rect(surface_size);
- software_frame_manager_->DiscardCurrentFrame();
-
- if (!SwapBuffersPrepare(surface_rect,
- surface_scale_factor,
- damage_rect,
- mailbox_name,
- ack_callback)) {
- return;
- }
-
- SkRegion damage(RectToSkIRect(damage_rect));
- if (!skipped_damage_.isEmpty()) {
- damage.op(skipped_damage_, SkRegion::kUnion_Op);
- skipped_damage_.setEmpty();
- }
-
- DCHECK(surface_rect.Contains(SkIRectToRect(damage.getBounds())));
- ui::Texture* current_texture = current_surface_.get();
-
- const gfx::Size surface_size_in_pixel = surface_size;
- DLOG_IF(ERROR, previous_texture.get() &&
- previous_texture->size() != current_texture->size() &&
- SkIRectToRect(damage.getBounds()) != surface_rect) <<
- "Expected full damage rect after size change";
- if (previous_texture.get() && !previous_damage_.isEmpty() &&
- previous_texture->size() == current_texture->size()) {
- ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
- GLHelper* gl_helper = factory->GetGLHelper();
- gl_helper->CopySubBufferDamage(
- current_texture->PrepareTexture(),
- previous_texture->PrepareTexture(),
- damage,
- previous_damage_);
- }
- previous_damage_ = damage;
-
- ui::Compositor* compositor = GetCompositor();
- if (compositor) {
- // Co-ordinates come in OpenGL co-ordinate space.
- // We need to convert to layer space.
- gfx::Rect rect_to_paint =
- ConvertRectToDIP(surface_scale_factor,
- gfx::Rect(damage_rect.x(),
- surface_size_in_pixel.height() -
- damage_rect.y() - damage_rect.height(),
- damage_rect.width(),
- damage_rect.height()));
-
- // Damage may not have been DIP aligned, so inflate damage to compensate
- // for any round-off error.
- rect_to_paint.Inset(-1, -1);
- rect_to_paint.Intersect(window_->bounds());
-
- if (paint_observer_)
- paint_observer_->OnUpdateCompositorContent();
- window_->SchedulePaintInRect(rect_to_paint);
- compositor->SetLatencyInfo(latency_info);
+ if (legacy_render_widget_host_HWND_) {
+ return reinterpret_cast<gfx::NativeViewId>(
+ legacy_render_widget_host_HWND_->hwnd());
}
-
- SwapBuffersCompleted(ack_callback, previous_texture);
+ return NULL;
}
+#endif
void RenderWidgetHostViewAura::AcceleratedSurfacePostSubBuffer(
const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params_in_pixel,
int gpu_host_id) {
- gfx::Rect damage_rect(params_in_pixel.x,
- params_in_pixel.y,
- params_in_pixel.width,
- params_in_pixel.height);
- BufferPresentedCallback ack_callback =
- base::Bind(&AcknowledgeBufferForGpu,
- params_in_pixel.route_id,
- gpu_host_id,
- params_in_pixel.mailbox_name);
- BuffersSwapped(params_in_pixel.surface_size,
- damage_rect,
- params_in_pixel.surface_scale_factor,
- params_in_pixel.mailbox_name,
- params_in_pixel.latency_info,
- ack_callback);
+ // Oldschool composited mode is no longer supported.
}
void RenderWidgetHostViewAura::AcceleratedSurfaceSuspend() {
}
void RenderWidgetHostViewAura::AcceleratedSurfaceRelease() {
- // This really tells us to release the frontbuffer.
- if (current_surface_.get()) {
- ui::Compositor* compositor = GetCompositor();
- if (compositor) {
- // We need to wait for a commit to clear to guarantee that all we
- // will not issue any more GL referencing the previous surface.
- AddOnCommitCallbackAndDisableLocks(
- base::Bind(&RenderWidgetHostViewAura::SetSurfaceNotInUseByCompositor,
- AsWeakPtr(),
- current_surface_)); // Hold a ref so the texture will not
- // get deleted until after commit.
- }
- current_surface_ = NULL;
- UpdateExternalTexture();
- }
}
bool RenderWidgetHostViewAura::HasAcceleratedSurface(
@@ -1810,304 +1127,67 @@ bool RenderWidgetHostViewAura::HasAcceleratedSurface(
return false;
}
-void RenderWidgetHostViewAura::SetSurfaceNotInUseByCompositor(
- scoped_refptr<ui::Texture>) {
+void RenderWidgetHostViewAura::GetScreenInfo(WebScreenInfo* results) {
+ GetScreenInfoForWindow(results, window_->GetRootWindow() ? window_ : NULL);
}
-// static
-void RenderWidgetHostViewAura::CopyFromCompositingSurfaceHasResult(
- const gfx::Size& dst_size_in_pixel,
- const base::Callback<void(bool, const SkBitmap&)>& callback,
- scoped_ptr<cc::CopyOutputResult> result) {
- if (result->IsEmpty() || result->size().IsEmpty()) {
- callback.Run(false, SkBitmap());
- return;
- }
+gfx::Rect RenderWidgetHostViewAura::GetBoundsInRootWindow() {
+ aura::Window* top_level = window_->GetToplevelWindow();
+ gfx::Rect bounds(top_level->GetBoundsInScreen());
- if (result->HasTexture()) {
- PrepareTextureCopyOutputResult(dst_size_in_pixel, callback, result.Pass());
- return;
+#if defined(OS_WIN)
+ // TODO(zturner,iyengar): This will break when we remove support for NPAPI and
+ // remove the legacy hwnd, so a better fix will need to be decided when that
+ // happens.
+ if (UsesNativeWindowFrame()) {
+ // aura::Window doesn't take into account non-client area of native windows
+ // (e.g. HWNDs), so for that case ask Windows directly what the bounds are.
+ aura::WindowTreeHost* host = top_level->GetHost();
+ if (!host)
+ return top_level->GetBoundsInScreen();
+ RECT window_rect = {0};
+ HWND hwnd = host->GetAcceleratedWidget();
+ ::GetWindowRect(hwnd, &window_rect);
+ bounds = gfx::Rect(window_rect);
+
+ // Maximized windows are outdented from the work area by the frame thickness
+ // even though this "frame" is not painted. This confuses code (and people)
+ // that think of a maximized window as corresponding exactly to the work
+ // area. Correct for this by subtracting the frame thickness back off.
+ if (::IsZoomed(hwnd)) {
+ bounds.Inset(GetSystemMetrics(SM_CXSIZEFRAME),
+ GetSystemMetrics(SM_CYSIZEFRAME));
+
+ bounds.Inset(GetSystemMetrics(SM_CXPADDEDBORDER),
+ GetSystemMetrics(SM_CXPADDEDBORDER));
+ }
}
- DCHECK(result->HasBitmap());
- PrepareBitmapCopyOutputResult(dst_size_in_pixel, callback, result.Pass());
-}
-
-static void CopyFromCompositingSurfaceFinished(
- const base::Callback<void(bool, const SkBitmap&)>& callback,
- scoped_ptr<cc::SingleReleaseCallback> release_callback,
- scoped_ptr<SkBitmap> bitmap,
- scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock,
- bool result) {
- bitmap_pixels_lock.reset();
- release_callback->Run(0, false);
- callback.Run(result, *bitmap);
-}
-
-// static
-void RenderWidgetHostViewAura::PrepareTextureCopyOutputResult(
- const gfx::Size& dst_size_in_pixel,
- const base::Callback<void(bool, const SkBitmap&)>& callback,
- scoped_ptr<cc::CopyOutputResult> result) {
- base::ScopedClosureRunner scoped_callback_runner(
- base::Bind(callback, false, SkBitmap()));
-
- DCHECK(result->HasTexture());
- if (!result->HasTexture())
- return;
-
- scoped_ptr<SkBitmap> bitmap(new SkBitmap);
- bitmap->setConfig(SkBitmap::kARGB_8888_Config,
- dst_size_in_pixel.width(), dst_size_in_pixel.height(),
- 0, kOpaque_SkAlphaType);
- if (!bitmap->allocPixels())
- return;
-
- ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
- GLHelper* gl_helper = factory->GetGLHelper();
- if (!gl_helper)
- return;
-
- scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock(
- new SkAutoLockPixels(*bitmap));
- uint8* pixels = static_cast<uint8*>(bitmap->getPixels());
-
- cc::TextureMailbox texture_mailbox;
- scoped_ptr<cc::SingleReleaseCallback> release_callback;
- result->TakeTexture(&texture_mailbox, &release_callback);
- DCHECK(texture_mailbox.IsTexture());
- if (!texture_mailbox.IsTexture())
- return;
-
- ignore_result(scoped_callback_runner.Release());
+ bounds = gfx::win::ScreenToDIPRect(bounds);
+#endif
- gl_helper->CropScaleReadbackAndCleanMailbox(
- texture_mailbox.name(),
- texture_mailbox.sync_point(),
- result->size(),
- gfx::Rect(result->size()),
- dst_size_in_pixel,
- pixels,
- base::Bind(&CopyFromCompositingSurfaceFinished,
- callback,
- base::Passed(&release_callback),
- base::Passed(&bitmap),
- base::Passed(&bitmap_pixels_lock)));
+ return bounds;
}
-// static
-void RenderWidgetHostViewAura::PrepareBitmapCopyOutputResult(
- const gfx::Size& dst_size_in_pixel,
- const base::Callback<void(bool, const SkBitmap&)>& callback,
- scoped_ptr<cc::CopyOutputResult> result) {
- DCHECK(result->HasBitmap());
-
- base::ScopedClosureRunner scoped_callback_runner(
- base::Bind(callback, false, SkBitmap()));
- if (!result->HasBitmap())
- return;
-
- scoped_ptr<SkBitmap> source = result->TakeBitmap();
- DCHECK(source);
- if (!source)
- return;
-
- ignore_result(scoped_callback_runner.Release());
-
- SkBitmap bitmap = skia::ImageOperations::Resize(
- *source,
- skia::ImageOperations::RESIZE_BEST,
- dst_size_in_pixel.width(),
- dst_size_in_pixel.height());
- callback.Run(true, bitmap);
-}
-
-void RenderWidgetHostViewAura::CopyFromCompositingSurfaceFinishedForVideo(
- base::WeakPtr<RenderWidgetHostViewAura> rwhva,
- const base::Callback<void(bool)>& callback,
- scoped_refptr<OwnedMailbox> subscriber_texture,
- scoped_ptr<cc::SingleReleaseCallback> release_callback,
- bool result) {
- callback.Run(result);
-
- GLHelper* gl_helper = ImageTransportFactory::GetInstance()->GetGLHelper();
- uint32 sync_point = gl_helper ? gl_helper->InsertSyncPoint() : 0;
- if (release_callback) {
- DCHECK(!subscriber_texture);
- release_callback->Run(sync_point, false);
- } else {
- // If there's no release callback, then the texture is from
- // idle_frame_subscriber_textures_ and we can put it back there.
- DCHECK(subscriber_texture);
- subscriber_texture->UpdateSyncPoint(sync_point);
- if (rwhva && rwhva->frame_subscriber_ && subscriber_texture->texture_id())
- rwhva->idle_frame_subscriber_textures_.push_back(subscriber_texture);
- subscriber_texture = NULL;
+void RenderWidgetHostViewAura::WheelEventAck(
+ const blink::WebMouseWheelEvent& event,
+ InputEventAckState ack_result) {
+ if (overscroll_controller_) {
+ overscroll_controller_->ReceivedEventACK(
+ event, (INPUT_EVENT_ACK_STATE_CONSUMED == ack_result));
}
}
-// static
-void RenderWidgetHostViewAura::CopyFromCompositingSurfaceHasResultForVideo(
- base::WeakPtr<RenderWidgetHostViewAura> rwhva,
- scoped_refptr<OwnedMailbox> subscriber_texture,
- scoped_refptr<media::VideoFrame> video_frame,
- const base::Callback<void(bool)>& callback,
- scoped_ptr<cc::CopyOutputResult> result) {
- base::ScopedClosureRunner scoped_callback_runner(base::Bind(callback, false));
-
- if (!rwhva)
- 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 coded_size() and the result's size() are both physical
- // pixels.
- gfx::Rect region_in_frame =
- media::ComputeLetterboxRegion(gfx::Rect(video_frame->coded_size()),
- 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;
-
- // We only handle texture readbacks for now. If the compositor is in software
- // mode, we could produce a software-backed VideoFrame here as well.
- if (!result->HasTexture()) {
- DCHECK(result->HasBitmap());
- scoped_ptr<SkBitmap> bitmap = result->TakeBitmap();
- // Scale the bitmap to the required size, if necessary.
- SkBitmap scaled_bitmap;
- if (result->size().width() != region_in_frame.width() ||
- result->size().height() != region_in_frame.height()) {
- 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);
+void RenderWidgetHostViewAura::GestureEventAck(
+ const blink::WebGestureEvent& event,
+ InputEventAckState ack_result) {
+ if (touch_editing_client_)
+ touch_editing_client_->GestureEventAck(event.type);
- media::CopyRGBToVideoFrame(
- reinterpret_cast<uint8*>(scaled_bitmap.getPixels()),
- scaled_bitmap.rowBytes(),
- region_in_frame,
- video_frame.get());
- }
- ignore_result(scoped_callback_runner.Release());
- callback.Run(true);
- return;
+ if (overscroll_controller_) {
+ overscroll_controller_->ReceivedEventACK(
+ event, (INPUT_EVENT_ACK_STATE_CONSUMED == ack_result));
}
-
- ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
- GLHelper* gl_helper = factory->GetGLHelper();
- if (!gl_helper)
- return;
-
- cc::TextureMailbox texture_mailbox;
- scoped_ptr<cc::SingleReleaseCallback> release_callback;
- result->TakeTexture(&texture_mailbox, &release_callback);
- DCHECK(texture_mailbox.IsTexture());
- if (!texture_mailbox.IsTexture())
- return;
-
- gfx::Rect result_rect(result->size());
-
- content::ReadbackYUVInterface* yuv_readback_pipeline =
- rwhva->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 =
- 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;
-
- rwhva->yuv_readback_pipeline_.reset(
- gl_helper->CreateReadbackPipelineYUV(quality,
- result_rect.size(),
- result_rect,
- video_frame->coded_size(),
- region_in_frame,
- true,
- true));
- yuv_readback_pipeline = rwhva->yuv_readback_pipeline_.get();
- }
-
- ignore_result(scoped_callback_runner.Release());
- base::Callback<void(bool result)> finished_callback = base::Bind(
- &RenderWidgetHostViewAura::CopyFromCompositingSurfaceFinishedForVideo,
- rwhva->AsWeakPtr(),
- callback,
- subscriber_texture,
- base::Passed(&release_callback));
- yuv_readback_pipeline->ReadbackYUV(
- texture_mailbox.name(),
- texture_mailbox.sync_point(),
- video_frame.get(),
- finished_callback);
-}
-
-void RenderWidgetHostViewAura::GetScreenInfo(WebScreenInfo* results) {
- GetScreenInfoForWindow(results, window_->GetRootWindow() ? window_ : NULL);
-}
-
-gfx::Rect RenderWidgetHostViewAura::GetBoundsInRootWindow() {
-#if defined(OS_WIN)
- // aura::Window::GetBoundsInScreen doesn't take non-client area into
- // account.
- RECT window_rect = {0};
-
- aura::Window* top_level = window_->GetToplevelWindow();
- aura::WindowEventDispatcher* dispatcher = top_level->GetDispatcher();
- if (!dispatcher)
- return top_level->GetBoundsInScreen();
- HWND hwnd = dispatcher->host()->GetAcceleratedWidget();
- ::GetWindowRect(hwnd, &window_rect);
- gfx::Rect rect(window_rect);
-
- // Maximized windows are outdented from the work area by the frame thickness
- // even though this "frame" is not painted. This confuses code (and people)
- // that think of a maximized window as corresponding exactly to the work area.
- // Correct for this by subtracting the frame thickness back off.
- if (::IsZoomed(hwnd)) {
- rect.Inset(GetSystemMetrics(SM_CXSIZEFRAME),
- GetSystemMetrics(SM_CYSIZEFRAME));
- }
-
- return gfx::win::ScreenToDIPRect(rect);
-#else
- return window_->GetToplevelWindow()->GetBoundsInScreen();
-#endif
-}
-
-void RenderWidgetHostViewAura::GestureEventAck(int gesture_event_type,
- InputEventAckState ack_result) {
- if (touch_editing_client_)
- touch_editing_client_->GestureEventAck(gesture_event_type);
}
void RenderWidgetHostViewAura::ProcessAckedTouchEvent(
@@ -2117,16 +1197,16 @@ void RenderWidgetHostViewAura::ProcessAckedTouchEvent(
SCREEN_COORDINATES))
return;
- aura::WindowEventDispatcher* dispatcher = window_->GetDispatcher();
- // |dispatcher| is NULL during tests.
- if (!dispatcher)
+ aura::WindowTreeHost* host = window_->GetHost();
+ // |host| is NULL during tests.
+ if (!host)
return;
ui::EventResult result = (ack_result ==
INPUT_EVENT_ACK_STATE_CONSUMED) ? ui::ER_HANDLED : ui::ER_UNHANDLED;
for (ScopedVector<ui::TouchEvent>::iterator iter = events.begin(),
end = events.end(); iter != end; ++iter) {
- dispatcher->ProcessedTouchEvent((*iter), window_, result);
+ host->dispatcher()->ProcessedTouchEvent((*iter), window_, result);
}
}
@@ -2136,32 +1216,51 @@ RenderWidgetHostViewAura::CreateSyntheticGestureTarget() {
new SyntheticGestureTargetAura(host_));
}
-void RenderWidgetHostViewAura::SetHasHorizontalScrollbar(
- bool has_horizontal_scrollbar) {
- // Not needed. Mac-only.
-}
+InputEventAckState RenderWidgetHostViewAura::FilterInputEvent(
+ const blink::WebInputEvent& input_event) {
+ bool consumed = false;
+ if (input_event.type == WebInputEvent::GestureFlingStart) {
+ const WebGestureEvent& gesture_event =
+ static_cast<const WebGestureEvent&>(input_event);
+ // Zero-velocity touchpad flings are an Aura-specific signal that the
+ // touchpad scroll has ended, and should not be forwarded to the renderer.
+ if (gesture_event.sourceDevice == blink::WebGestureDeviceTouchpad &&
+ !gesture_event.data.flingStart.velocityX &&
+ !gesture_event.data.flingStart.velocityY) {
+ consumed = true;
+ }
+ }
-void RenderWidgetHostViewAura::SetScrollOffsetPinning(
- bool is_pinned_to_left, bool is_pinned_to_right) {
- // Not needed. Mac-only.
+ if (overscroll_controller_)
+ consumed |= overscroll_controller_->WillHandleEvent(input_event);
+
+ return consumed && !WebTouchEvent::isTouchEventType(input_event.type)
+ ? INPUT_EVENT_ACK_STATE_CONSUMED
+ : INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
}
-void RenderWidgetHostViewAura::OnAccessibilityEvents(
- const std::vector<AccessibilityHostMsg_EventParams>& params) {
- BrowserAccessibilityManager* manager =
- GetOrCreateBrowserAccessibilityManager();
- if (manager)
- manager->OnAccessibilityEvents(params);
+void RenderWidgetHostViewAura::CreateBrowserAccessibilityManagerIfNeeded() {
+#if defined(OS_WIN)
+ if (!GetBrowserAccessibilityManager()) {
+ gfx::NativeViewAccessible accessible_parent =
+ host_->GetParentNativeViewAccessible();
+ LegacyRenderWidgetHostHWND* parent_hwnd =
+ legacy_render_widget_host_HWND_.get();
+ SetBrowserAccessibilityManager(new BrowserAccessibilityManagerWin(
+ legacy_render_widget_host_HWND_.get(), accessible_parent,
+ BrowserAccessibilityManagerWin::GetEmptyDocument(), host_));
+ }
+#else
+ if (!GetBrowserAccessibilityManager()) {
+ SetBrowserAccessibilityManager(
+ BrowserAccessibilityManager::Create(
+ BrowserAccessibilityManager::GetEmptyDocument(), host_));
+ }
+#endif
}
gfx::GLSurfaceHandle RenderWidgetHostViewAura::GetCompositingSurface() {
- if (shared_surface_handle_.is_null()) {
- ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
- shared_surface_handle_ = factory->CreateSharedSurfaceHandle();
- if (!shared_surface_handle_.is_null())
- factory->AddObserver(this);
- }
- return shared_surface_handle_;
+ return ImageTransportFactory::GetInstance()->GetSharedSurfaceHandle();
}
bool RenderWidgetHostViewAura::LockMouse() {
@@ -2175,6 +1274,8 @@ bool RenderWidgetHostViewAura::LockMouse() {
mouse_locked_ = true;
#if !defined(OS_WIN)
window_->SetCapture();
+#else
+ UpdateMouseLockRegion();
#endif
aura::client::CursorClient* cursor_client =
aura::client::GetCursorClient(root_window);
@@ -2187,14 +1288,13 @@ bool RenderWidgetHostViewAura::LockMouse() {
synthetic_move_sent_ = true;
window_->MoveCursorTo(gfx::Rect(window_->bounds().size()).CenterPoint());
}
- if (aura::client::GetTooltipClient(root_window))
- aura::client::GetTooltipClient(root_window)->SetTooltipsEnabled(false);
-
- root_window->GetDispatcher()->host()->ConfineCursorToRootWindow();
+ tooltip_disabler_.reset(new aura::client::ScopedTooltipDisabler(root_window));
return true;
}
void RenderWidgetHostViewAura::UnlockMouse() {
+ tooltip_disabler_.reset();
+
aura::Window* root_window = window_->GetRootWindow();
if (!mouse_locked_ || !root_window)
return;
@@ -2203,6 +1303,8 @@ void RenderWidgetHostViewAura::UnlockMouse() {
#if !defined(OS_WIN)
window_->ReleaseCapture();
+#else
+ ::ClipCursor(NULL);
#endif
window_->MoveCursorTo(unlocked_mouse_position_);
aura::client::CursorClient* cursor_client =
@@ -2212,11 +1314,7 @@ void RenderWidgetHostViewAura::UnlockMouse() {
cursor_client->ShowCursor();
}
- if (aura::client::GetTooltipClient(root_window))
- aura::client::GetTooltipClient(root_window)->SetTooltipsEnabled(true);
-
host_->LostMouseLock();
- root_window->GetDispatcher()->host()->UnConfineCursor();
}
////////////////////////////////////////////////////////////////////////////////
@@ -2226,17 +1324,20 @@ void RenderWidgetHostViewAura::SetCompositionText(
if (!host_)
return;
- // ui::CompositionUnderline should be identical to
- // blink::WebCompositionUnderline, so that we can do reinterpret_cast safely.
- COMPILE_ASSERT(sizeof(ui::CompositionUnderline) ==
- sizeof(blink::WebCompositionUnderline),
- ui_CompositionUnderline__WebKit_WebCompositionUnderline_diff);
-
// TODO(suzhe): convert both renderer_host and renderer to use
// ui::CompositionText.
- const std::vector<blink::WebCompositionUnderline>& underlines =
- reinterpret_cast<const std::vector<blink::WebCompositionUnderline>&>(
- composition.underlines);
+ std::vector<blink::WebCompositionUnderline> underlines;
+ underlines.reserve(composition.underlines.size());
+ for (std::vector<ui::CompositionUnderline>::const_iterator it =
+ composition.underlines.begin();
+ it != composition.underlines.end(); ++it) {
+ underlines.push_back(
+ blink::WebCompositionUnderline(static_cast<unsigned>(it->start_offset),
+ static_cast<unsigned>(it->end_offset),
+ it->color,
+ it->thick,
+ it->background_color));
+ }
// TODO(suzhe): due to a bug of webkit, we can't use selection range with
// composition string. See: https://bugs.webkit.org/show_bug.cgi?id=37788
@@ -2268,7 +1369,7 @@ void RenderWidgetHostViewAura::InsertText(const base::string16& text) {
has_composition_text_ = false;
}
-void RenderWidgetHostViewAura::InsertChar(char16 ch, int flags) {
+void RenderWidgetHostViewAura::InsertChar(base::char16 ch, int flags) {
if (popup_child_host_view_ && popup_child_host_view_->NeedsInputGrab()) {
popup_child_host_view_->InsertChar(ch, flags);
return;
@@ -2283,7 +1384,7 @@ void RenderWidgetHostViewAura::InsertChar(char16 ch, int flags) {
ch,
flags,
now);
- host_->ForwardKeyboardEvent(webkit_event);
+ ForwardKeyboardEvent(webkit_event);
}
}
@@ -2440,8 +1541,9 @@ bool RenderWidgetHostViewAura::ChangeTextDirectionAndLayoutAlignment(
void RenderWidgetHostViewAura::ExtendSelectionAndDelete(
size_t before, size_t after) {
- if (host_)
- host_->ExtendSelectionAndDelete(before, after);
+ RenderFrameHostImpl* rfh = GetFocusedFrame();
+ if (rfh)
+ rfh->ExtendSelectionAndDelete(before, after);
}
void RenderWidgetHostViewAura::EnsureCaretInRect(const gfx::Rect& rect) {
@@ -2467,19 +1569,16 @@ void RenderWidgetHostViewAura::OnCandidateWindowHidden() {
host_->CandidateWindowHidden();
}
-////////////////////////////////////////////////////////////////////////////////
-// RenderWidgetHostViewAura, gfx::DisplayObserver implementation:
+bool RenderWidgetHostViewAura::IsEditingCommandEnabled(int command_id) {
+ return false;
+}
-void RenderWidgetHostViewAura::OnDisplayBoundsChanged(
- const gfx::Display& display) {
- gfx::Screen* screen = gfx::Screen::GetScreenFor(window_);
- if (display.id() == screen->GetDisplayNearestWindow(window_).id()) {
- UpdateScreenInfo(window_);
- current_cursor_.SetDisplayInfo(display);
- UpdateCursorIfOverSelf();
- }
+void RenderWidgetHostViewAura::ExecuteEditingCommand(int command_id) {
}
+////////////////////////////////////////////////////////////////////////////////
+// RenderWidgetHostViewAura, gfx::DisplayObserver implementation:
+
void RenderWidgetHostViewAura::OnDisplayAdded(
const gfx::Display& new_display) {
}
@@ -2488,6 +1587,17 @@ void RenderWidgetHostViewAura::OnDisplayRemoved(
const gfx::Display& old_display) {
}
+void RenderWidgetHostViewAura::OnDisplayMetricsChanged(
+ const gfx::Display& display, uint32_t metrics) {
+ // The screen info should be updated regardless of the metric change.
+ gfx::Screen* screen = gfx::Screen::GetScreenFor(window_);
+ if (display.id() == screen->GetDisplayNearestWindow(window_).id()) {
+ UpdateScreenInfo(window_);
+ current_cursor_.SetDisplayInfo(display);
+ UpdateCursorIfOverSelf();
+ }
+}
+
////////////////////////////////////////////////////////////////////////////////
// RenderWidgetHostViewAura, aura::WindowDelegate implementation:
@@ -2502,11 +1612,15 @@ gfx::Size RenderWidgetHostViewAura::GetMaximumSize() const {
void RenderWidgetHostViewAura::OnBoundsChanged(const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds) {
base::AutoReset<bool> in_bounds_changed(&in_bounds_changed_, true);
- // We care about this only in fullscreen mode, where there is no
- // WebContentsViewAura. We are sized via SetSize() or SetBounds() by
- // WebContentsViewAura in other cases.
- if (is_fullscreen_)
- SetSize(new_bounds.size());
+ // We care about this whenever RenderWidgetHostViewAura is not owned by a
+ // WebContentsViewAura since changes to the Window's bounds need to be
+ // messaged to the renderer. WebContentsViewAura invokes SetSize() or
+ // SetBounds() itself. No matter how we got here, any redundant calls are
+ // harmless.
+ SetSize(new_bounds.size());
+
+ if (GetInputMethod())
+ GetInputMethod()->OnCaretBoundsChanged(this);
}
gfx::NativeCursor RenderWidgetHostViewAura::GetCursor(const gfx::Point& point) {
@@ -2533,32 +1647,15 @@ bool RenderWidgetHostViewAura::CanFocus() {
void RenderWidgetHostViewAura::OnCaptureLost() {
host_->LostCapture();
if (touch_editing_client_)
- touch_editing_client_->EndTouchEditing();
+ touch_editing_client_->EndTouchEditing(false);
}
void RenderWidgetHostViewAura::OnPaint(gfx::Canvas* canvas) {
- bool has_backing_store = !!host_->GetBackingStore(false);
- if (has_backing_store) {
- paint_canvas_ = canvas;
- BackingStoreAura* backing_store = static_cast<BackingStoreAura*>(
- host_->GetBackingStore(true));
- paint_canvas_ = NULL;
- backing_store->SkiaShowRect(gfx::Point(), canvas);
-
- if (paint_observer_)
- paint_observer_->OnPaintComplete();
- ui::Compositor* compositor = GetCompositor();
- if (compositor) {
- compositor->SetLatencyInfo(software_latency_info_);
- software_latency_info_.Clear();
- }
- } else {
- // For non-opaque windows, we don't draw anything, since we depend on the
- // canvas coming from the compositor to already be initialized as
- // transparent.
- if (window_->layer()->fills_bounds_opaquely())
- canvas->DrawColor(SK_ColorWHITE);
- }
+ // For non-opaque windows, we don't draw anything, since we depend on the
+ // canvas coming from the compositor to already be initialized as
+ // transparent.
+ if (window_->layer()->fills_bounds_opaquely())
+ canvas->DrawColor(SK_ColorWHITE);
}
void RenderWidgetHostViewAura::OnDeviceScaleFactorChanged(
@@ -2566,11 +1663,6 @@ void RenderWidgetHostViewAura::OnDeviceScaleFactorChanged(
if (!host_)
return;
- BackingStoreAura* backing_store = static_cast<BackingStoreAura*>(
- host_->GetBackingStore(false));
- if (backing_store) // NULL in hardware path.
- backing_store->ScaleFactorChanged(device_scale_factor);
-
UpdateScreenInfo(window_);
const gfx::Display display = gfx::Screen::GetScreenFor(window_)->
@@ -2579,7 +1671,7 @@ void RenderWidgetHostViewAura::OnDeviceScaleFactorChanged(
current_cursor_.SetDisplayInfo(display);
}
-void RenderWidgetHostViewAura::OnWindowDestroying() {
+void RenderWidgetHostViewAura::OnWindowDestroying(aura::Window* window) {
#if defined(OS_WIN)
HWND parent = NULL;
// If the tab was hidden and it's closed, host_->is_hidden would have been
@@ -2587,7 +1679,7 @@ void RenderWidgetHostViewAura::OnWindowDestroying() {
if (!window_->GetRootWindow() || host_->is_hidden()) {
parent = ui::GetHiddenWindow();
} else {
- parent = window_->GetDispatcher()->host()->GetAcceleratedWidget();
+ parent = window_->GetHost()->GetAcceleratedWidget();
}
LPARAM lparam = reinterpret_cast<LPARAM>(this);
EnumChildWindows(parent, WindowDestroyingCallback, lparam);
@@ -2599,9 +1691,12 @@ void RenderWidgetHostViewAura::OnWindowDestroying() {
ui::InputMethod* input_method = GetInputMethod();
if (input_method)
input_method->DetachTextInputClient(this);
+
+ if (overscroll_controller_)
+ overscroll_controller_->Reset();
}
-void RenderWidgetHostViewAura::OnWindowDestroyed() {
+void RenderWidgetHostViewAura::OnWindowDestroyed(aura::Window* window) {
host_->ViewDestroyed();
delete this;
}
@@ -2616,60 +1711,6 @@ bool RenderWidgetHostViewAura::HasHitTestMask() const {
void RenderWidgetHostViewAura::GetHitTestMask(gfx::Path* mask) const {
}
-void RenderWidgetHostViewAura::DidRecreateLayer(ui::Layer *old_layer,
- ui::Layer *new_layer) {
- float mailbox_scale_factor;
- cc::TextureMailbox old_mailbox =
- old_layer->GetTextureMailbox(&mailbox_scale_factor);
- scoped_refptr<ui::Texture> old_texture = old_layer->external_texture();
- // The new_layer is the one that will be used by our Window, so that's the one
- // that should keep our texture. old_layer will be returned to the
- // RecreateLayer caller, and should have a copy.
- if (old_texture.get()) {
- ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
- GLHelper* gl_helper = factory->GetGLHelper();
- scoped_refptr<ui::Texture> new_texture;
- if (host_->is_accelerated_compositing_active() &&
- gl_helper && current_surface_.get()) {
- blink::WebGLId texture_id =
- gl_helper->CopyTexture(current_surface_->PrepareTexture(),
- current_surface_->size());
- if (texture_id) {
- new_texture = factory->CreateOwnedTexture(
- current_surface_->size(),
- current_surface_->device_scale_factor(), texture_id);
- }
- }
- if (new_texture.get())
- old_layer->SetExternalTexture(new_texture.get());
- else
- old_layer->SetShowPaintedContent();
- new_layer->SetExternalTexture(old_texture.get());
- } else if (old_mailbox.IsSharedMemory()) {
- base::SharedMemory* old_buffer = old_mailbox.shared_memory();
- const size_t size = old_mailbox.shared_memory_size_in_bytes();
-
- scoped_ptr<base::SharedMemory> new_buffer(new base::SharedMemory);
- new_buffer->CreateAndMapAnonymous(size);
-
- if (old_buffer->memory() && new_buffer->memory()) {
- memcpy(new_buffer->memory(), old_buffer->memory(), size);
- base::SharedMemory* new_buffer_raw_ptr = new_buffer.get();
- scoped_ptr<cc::SingleReleaseCallback> callback =
- cc::SingleReleaseCallback::Create(base::Bind(MailboxReleaseCallback,
- Passed(&new_buffer)));
- cc::TextureMailbox new_mailbox(new_buffer_raw_ptr,
- old_mailbox.shared_memory_size());
- new_layer->SetTextureMailbox(new_mailbox,
- callback.Pass(),
- mailbox_scale_factor);
- }
- } else if (frame_provider_.get()) {
- new_layer->SetShowDelegatedContent(frame_provider_.get(),
- current_frame_size_);
- }
-}
-
////////////////////////////////////////////////////////////////////////////////
// RenderWidgetHostViewAura, ui::EventHandler implementation:
@@ -2724,10 +1765,10 @@ void RenderWidgetHostViewAura::OnKeyEvent(ui::KeyEvent* event) {
event->is_char() ? event->GetCharacter() : event->key_code(),
event->flags(),
ui::EventTimeForNow().InSecondsF());
- host_->ForwardKeyboardEvent(webkit_event);
+ ForwardKeyboardEvent(webkit_event);
} else {
NativeWebKeyboardEvent webkit_event(event);
- host_->ForwardKeyboardEvent(webkit_event);
+ ForwardKeyboardEvent(webkit_event);
}
}
event->SetHandled();
@@ -2799,8 +1840,8 @@ void RenderWidgetHostViewAura::OnMouseEvent(ui::MouseEvent* event) {
// RootWindow). But this event interferes with the overscroll gesture. So,
// ignore such synthetic mouse-move events if an overscroll gesture is in
// progress.
- if (host_->overscroll_controller() &&
- host_->overscroll_controller()->overscroll_mode() != OVERSCROLL_NONE &&
+ if (overscroll_controller_ &&
+ overscroll_controller_->overscroll_mode() != OVERSCROLL_NONE &&
event->flags() & ui::EF_IS_SYNTHESIZED &&
(event->type() == ui::ET_MOUSE_ENTERED ||
event->type() == ui::ET_MOUSE_EXITED ||
@@ -2814,9 +1855,9 @@ void RenderWidgetHostViewAura::OnMouseEvent(ui::MouseEvent* event) {
// We get mouse wheel/scroll messages even if we are not in the foreground.
// So here we check if we have any owned popup windows in the foreground and
// dismiss them.
- aura::WindowEventDispatcher* dispatcher = window_->GetDispatcher();
- if (dispatcher) {
- HWND parent = dispatcher->host()->GetAcceleratedWidget();
+ aura::WindowTreeHost* host = window_->GetHost();
+ if (host) {
+ HWND parent = host->GetAcceleratedWidget();
HWND toplevel_hwnd = ::GetAncestor(parent, GA_ROOT);
EnumThreadWindows(GetCurrentThreadId(),
DismissOwnedPopups,
@@ -2852,14 +1893,17 @@ void RenderWidgetHostViewAura::OnMouseEvent(ui::MouseEvent* event) {
break;
}
- // Needed to propagate mouse event to native_tab_contents_view_aura.
+ // Needed to propagate mouse event to |window_->parent()->delegate()|, but
+ // note that it might be something other than a WebContentsViewAura instance.
// TODO(pkotwicz): Find a better way of doing this.
// In fullscreen mode which is typically used by flash, don't forward
// the mouse events to the parent. The renderer and the plugin process
// handle these events.
if (!is_fullscreen_ && window_->parent()->delegate() &&
- !(event->flags() & ui::EF_FROM_TOUCH))
+ !(event->flags() & ui::EF_FROM_TOUCH)) {
+ event->ConvertLocationToTarget(window_, window_->parent());
window_->parent()->delegate()->OnMouseEvent(event);
+ }
if (!IsXButtonUpEvent(event))
event->SetHandled();
@@ -2883,14 +1927,14 @@ void RenderWidgetHostViewAura::OnScrollEvent(ui::ScrollEvent* event) {
blink::WebMouseWheelEvent mouse_wheel_event =
MakeWebMouseWheelEvent(event);
host_->ForwardWheelEvent(mouse_wheel_event);
- RecordAction(UserMetricsAction("TrackpadScroll"));
+ RecordAction(base::UserMetricsAction("TrackpadScroll"));
} else if (event->type() == ui::ET_SCROLL_FLING_START ||
event->type() == ui::ET_SCROLL_FLING_CANCEL) {
blink::WebGestureEvent gesture_event =
MakeWebGestureEvent(event);
host_->ForwardGestureEvent(gesture_event);
if (event->type() == ui::ET_SCROLL_FLING_START)
- RecordAction(UserMetricsAction("TrackpadScrollFling"));
+ RecordAction(base::UserMetricsAction("TrackpadScrollFling"));
}
event->SetHandled();
@@ -2923,10 +1967,9 @@ void RenderWidgetHostViewAura::OnTouchEvent(ui::TouchEvent* event) {
void RenderWidgetHostViewAura::OnGestureEvent(ui::GestureEvent* event) {
TRACE_EVENT0("input", "RenderWidgetHostViewAura::OnGestureEvent");
- // Pinch gestures are currently disabled by default. See crbug.com/128477.
if ((event->type() == ui::ET_GESTURE_PINCH_BEGIN ||
event->type() == ui::ET_GESTURE_PINCH_UPDATE ||
- event->type() == ui::ET_GESTURE_PINCH_END) && !ShouldSendPinchGesture()) {
+ event->type() == ui::ET_GESTURE_PINCH_END) && !pinch_zoom_enabled_) {
event->SetHandled();
return;
}
@@ -2935,7 +1978,7 @@ void RenderWidgetHostViewAura::OnGestureEvent(ui::GestureEvent* event) {
return;
RenderViewHostDelegate* delegate = NULL;
- if (popup_type_ == blink::WebPopupTypeNone && !is_fullscreen_)
+ if (host_->IsRenderView())
delegate = RenderViewHost::From(host_)->GetDelegate();
if (delegate && event->type() == ui::ET_GESTURE_BEGIN &&
@@ -2949,7 +1992,7 @@ void RenderWidgetHostViewAura::OnGestureEvent(ui::GestureEvent* event) {
// event to stop any in-progress flings.
blink::WebGestureEvent fling_cancel = gesture;
fling_cancel.type = blink::WebInputEvent::GestureFlingCancel;
- fling_cancel.sourceDevice = blink::WebGestureEvent::Touchscreen;
+ fling_cancel.sourceDevice = blink::WebGestureDeviceTouchscreen;
host_->ForwardGestureEvent(fling_cancel);
}
@@ -2959,9 +2002,9 @@ void RenderWidgetHostViewAura::OnGestureEvent(ui::GestureEvent* event) {
if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN ||
event->type() == ui::ET_GESTURE_SCROLL_UPDATE ||
event->type() == ui::ET_GESTURE_SCROLL_END) {
- RecordAction(UserMetricsAction("TouchscreenScroll"));
+ RecordAction(base::UserMetricsAction("TouchscreenScroll"));
} else if (event->type() == ui::ET_SCROLL_FLING_START) {
- RecordAction(UserMetricsAction("TouchscreenScrollFling"));
+ RecordAction(base::UserMetricsAction("TouchscreenScrollFling"));
}
}
@@ -2979,10 +2022,10 @@ void RenderWidgetHostViewAura::OnGestureEvent(ui::GestureEvent* event) {
// RenderWidgetHostViewAura, aura::client::ActivationDelegate implementation:
bool RenderWidgetHostViewAura::ShouldActivate() const {
- aura::WindowEventDispatcher* dispatcher = window_->GetDispatcher();
- if (!dispatcher)
+ aura::WindowTreeHost* host = window_->GetHost();
+ if (!host)
return true;
- const ui::Event* event = dispatcher->current_event();
+ const ui::Event* event = host->dispatcher()->current_event();
if (!event)
return true;
return is_fullscreen_;
@@ -2996,7 +2039,7 @@ void RenderWidgetHostViewAura::OnWindowActivated(aura::Window* gained_active,
aura::Window* lost_active) {
DCHECK(window_ == gained_active || window_ == lost_active);
if (window_ == gained_active) {
- const ui::Event* event = window_->GetDispatcher()->current_event();
+ const ui::Event* event = window_->GetHost()->dispatcher()->current_event();
if (event && PointerEventActivates(*event))
host_->OnPointerEventActivate();
}
@@ -3039,6 +2082,10 @@ void RenderWidgetHostViewAura::OnWindowFocused(aura::Window* gained_focus,
} else {
host_->SetInputMethodActive(false);
}
+
+ BrowserAccessibilityManager* manager = GetBrowserAccessibilityManager();
+ if (manager)
+ manager->OnWindowFocused();
} else if (window_ == lost_focus) {
host_->SetActive(false);
host_->Blur();
@@ -3047,7 +2094,14 @@ void RenderWidgetHostViewAura::OnWindowFocused(aura::Window* gained_focus,
host_->SetInputMethodActive(false);
if (touch_editing_client_)
- touch_editing_client_->EndTouchEditing();
+ touch_editing_client_->EndTouchEditing(false);
+
+ if (overscroll_controller_)
+ overscroll_controller_->Cancel();
+
+ BrowserAccessibilityManager* manager = GetBrowserAccessibilityManager();
+ if (manager)
+ manager->OnWindowBlurred();
// If we lose the focus while fullscreen, close the window; Pepper Flash
// won't do it for us (unlike NPAPI Flash). However, we do not close the
@@ -3076,186 +2130,27 @@ void RenderWidgetHostViewAura::OnWindowFocused(aura::Window* gained_focus,
}
////////////////////////////////////////////////////////////////////////////////
-// RenderWidgetHostViewAura, aura::RootWindowObserver implementation:
+// RenderWidgetHostViewAura, aura::WindowTreeHostObserver implementation:
-void RenderWidgetHostViewAura::OnRootWindowHostMoved(
- const aura::RootWindow* root,
- const gfx::Point& new_origin) {
- TRACE_EVENT1("ui", "RenderWidgetHostViewAura::OnRootWindowHostMoved",
+void RenderWidgetHostViewAura::OnHostMoved(const aura::WindowTreeHost* host,
+ const gfx::Point& new_origin) {
+ TRACE_EVENT1("ui", "RenderWidgetHostViewAura::OnHostMoved",
"new_origin", new_origin.ToString());
UpdateScreenInfo(window_);
}
////////////////////////////////////////////////////////////////////////////////
-// RenderWidgetHostViewAura, SoftwareFrameManagerClient implementation:
-
-void RenderWidgetHostViewAura::SoftwareFrameWasFreed(
- uint32 output_surface_id, unsigned frame_id) {
- ReleaseSoftwareFrame(output_surface_id, frame_id);
-}
-
-void RenderWidgetHostViewAura::ReleaseReferencesToSoftwareFrame() {
- ui::Compositor* compositor = GetCompositor();
- if (compositor) {
- AddOnCommitCallbackAndDisableLocks(base::Bind(
- &RenderWidgetHostViewAura::SendReclaimSoftwareFrames, AsWeakPtr()));
- }
- UpdateExternalTexture();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// RenderWidgetHostViewAura, ui::CompositorObserver implementation:
-
-void RenderWidgetHostViewAura::OnCompositingDidCommit(
- ui::Compositor* compositor) {
- if (can_lock_compositor_ == NO_PENDING_COMMIT) {
- can_lock_compositor_ = YES;
- if (resize_lock_.get() && resize_lock_->GrabDeferredLock())
- can_lock_compositor_ = YES_DID_LOCK;
- }
- RunOnCommitCallbacks();
- if (resize_lock_ && resize_lock_->expected_size() == current_frame_size_) {
- resize_lock_.reset();
- host_->WasResized();
- // 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 RenderWidgetHostViewAura::OnCompositingStarted(
- ui::Compositor* compositor, base::TimeTicks start_time) {
- last_draw_ended_ = start_time;
-}
-
-void RenderWidgetHostViewAura::OnCompositingEnded(
- ui::Compositor* compositor) {
- if (paint_observer_)
- paint_observer_->OnCompositingComplete();
-}
-
-void RenderWidgetHostViewAura::OnCompositingAborted(
- ui::Compositor* compositor) {
-}
-
-void RenderWidgetHostViewAura::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 RenderWidgetHostViewAura::OnUpdateVSyncParameters(
- ui::Compositor* compositor,
- base::TimeTicks timebase,
- base::TimeDelta interval) {
- if (IsShowing()) {
- if (IsDeadlineSchedulingEnabled()) {
- // The deadline scheduler has logic to stagger the draws of the
- // Renderer and Browser built-in, so send it an accurate timebase.
- host_->UpdateVSyncParameters(timebase, interval);
- } else if (!last_draw_ended_.is_null()) {
- // For the non-deadline scheduler, we send the Renderer an offset
- // vsync timebase to avoid its draws racing the Browser's draws.
- host_->UpdateVSyncParameters(last_draw_ended_, interval);
- }
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// RenderWidgetHostViewAura, BrowserAccessibilityDelegate implementation:
-
-void RenderWidgetHostViewAura::SetAccessibilityFocus(int acc_obj_id) {
- if (!host_)
- return;
-
- host_->AccessibilitySetFocus(acc_obj_id);
-}
-
-void RenderWidgetHostViewAura::AccessibilityDoDefaultAction(int acc_obj_id) {
- if (!host_)
- return;
-
- host_->AccessibilityDoDefaultAction(acc_obj_id);
-}
-
-void RenderWidgetHostViewAura::AccessibilityScrollToMakeVisible(
- int acc_obj_id, gfx::Rect subfocus) {
- if (!host_)
- return;
-
- host_->AccessibilityScrollToMakeVisible(acc_obj_id, subfocus);
-}
-
-void RenderWidgetHostViewAura::AccessibilityScrollToPoint(
- int acc_obj_id, gfx::Point point) {
- if (!host_)
- return;
-
- host_->AccessibilityScrollToPoint(acc_obj_id, point);
-}
-
-void RenderWidgetHostViewAura::AccessibilitySetTextSelection(
- int acc_obj_id, int start_offset, int end_offset) {
- if (!host_)
- return;
-
- host_->AccessibilitySetTextSelection(
- acc_obj_id, start_offset, end_offset);
-}
-
-gfx::Point RenderWidgetHostViewAura::GetLastTouchEventLocation() const {
- // Only needed for Win 8 non-aura.
- return gfx::Point();
-}
-
-void RenderWidgetHostViewAura::FatalAccessibilityTreeError() {
- host_->FatalAccessibilityTreeError();
- SetBrowserAccessibilityManager(NULL);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// RenderWidgetHostViewAura, ImageTransportFactoryObserver implementation:
-
-void RenderWidgetHostViewAura::OnLostResources() {
- current_surface_ = NULL;
- UpdateExternalTexture();
-
- idle_frame_subscriber_textures_.clear();
-
- // Make sure all ImageTransportClients are deleted now that the context those
- // are using is becoming invalid. This sends pending ACKs and needs to happen
- // after calling UpdateExternalTexture() which syncs with the impl thread.
- RunOnCommitCallbacks();
-
- DCHECK(!shared_surface_handle_.is_null());
- ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
- factory->DestroySharedSurfaceHandle(shared_surface_handle_);
- shared_surface_handle_ = factory->CreateSharedSurfaceHandle();
- host_->CompositingSurfaceUpdated();
- host_->ScheduleComposite();
-}
-
-////////////////////////////////////////////////////////////////////////////////
// RenderWidgetHostViewAura, private:
RenderWidgetHostViewAura::~RenderWidgetHostViewAura() {
- if (paint_observer_)
- paint_observer_->OnViewDestroyed();
if (touch_editing_client_)
touch_editing_client_->OnViewDestroyed();
- if (!shared_surface_handle_.is_null()) {
- ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
- factory->DestroySharedSurfaceHandle(shared_surface_handle_);
- factory->RemoveObserver(this);
- }
+
+ delegated_frame_host_.reset();
window_observer_.reset();
- if (window_->GetDispatcher())
- window_->GetDispatcher()->RemoveRootWindowObserver(this);
+ if (window_->GetHost())
+ window_->GetHost()->RemoveObserver(this);
UnlockMouse();
if (popup_parent_host_view_) {
DCHECK(popup_parent_host_view_->popup_child_host_view_ == NULL ||
@@ -3267,6 +2162,7 @@ RenderWidgetHostViewAura::~RenderWidgetHostViewAura() {
popup_child_host_view_->popup_parent_host_view_ == this);
popup_child_host_view_->popup_parent_host_view_ = NULL;
}
+ event_filter_for_popup_exit_.reset();
aura::client::SetTooltipText(window_, NULL);
gfx::Screen::GetScreenFor(window_)->RemoveObserver(this);
@@ -3275,12 +2171,8 @@ RenderWidgetHostViewAura::~RenderWidgetHostViewAura() {
// associated with the window, but just in case.
DetachFromInputMethod();
- if (resource_collection_.get())
- resource_collection_->SetClient(NULL);
-
#if defined(OS_WIN)
- if (::IsWindow(plugin_parent_window_))
- ::DestroyWindow(plugin_parent_window_);
+ legacy_render_widget_host_HWND_.reset(NULL);
#endif
}
@@ -3291,20 +2183,15 @@ void RenderWidgetHostViewAura::UpdateCursorIfOverSelf() {
if (!root_window)
return;
- gfx::Rect screen_rect = GetViewBounds();
- gfx::Point local_point = screen_point;
- local_point.Offset(-screen_rect.x(), -screen_rect.y());
+ gfx::Point root_window_point = screen_point;
+ aura::client::ScreenPositionClient* screen_position_client =
+ aura::client::GetScreenPositionClient(root_window);
+ if (screen_position_client) {
+ screen_position_client->ConvertPointFromScreen(
+ root_window, &root_window_point);
+ }
-#if defined(OS_WIN)
- // If there's another toplevel window above us at this point (for example a
- // menu), we don't want to update the cursor.
- POINT windows_point = { screen_point.x(), screen_point.y() };
- aura::WindowEventDispatcher* dispatcher = root_window->GetDispatcher();
- if (dispatcher->host()->GetAcceleratedWidget() !=
- ::WindowFromPoint(windows_point))
- return;
-#endif
- if (root_window->GetEventHandlerForPoint(local_point) != window_)
+ if (root_window->GetEventHandlerForPoint(root_window_point) != window_)
return;
gfx::NativeCursor cursor = current_cursor_.GetNativeCursor();
@@ -3385,6 +2272,13 @@ void RenderWidgetHostViewAura::NotifyRendererOfCursorVisibilityState(
host_->SendCursorVisibilityState(is_visible);
}
+void RenderWidgetHostViewAura::SetOverscrollControllerEnabled(bool enabled) {
+ if (!enabled)
+ overscroll_controller_.reset();
+ else if (!overscroll_controller_)
+ overscroll_controller_.reset(new OverscrollController());
+}
+
void RenderWidgetHostViewAura::SchedulePaintIfNotInClip(
const gfx::Rect& rect,
const gfx::Rect& clip) {
@@ -3409,33 +2303,9 @@ bool RenderWidgetHostViewAura::ShouldMoveToCenter() {
global_mouse_position_.y() > rect.bottom() - border_y;
}
-void RenderWidgetHostViewAura::RunOnCommitCallbacks() {
- for (std::vector<base::Closure>::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 RenderWidgetHostViewAura::AddOnCommitCallbackAndDisableLocks(
- const base::Closure& callback) {
- ui::Compositor* compositor = GetCompositor();
- DCHECK(compositor);
-
- if (!compositor->HasObserver(this))
- compositor->AddObserver(this);
-
- can_lock_compositor_ = NO_PENDING_COMMIT;
- on_compositing_did_commit_callbacks_.push_back(callback);
-}
-
void RenderWidgetHostViewAura::AddedToRootWindow() {
- window_->GetDispatcher()->AddRootWindowObserver(this);
- host_->ParentChanged(GetNativeViewId());
+ window_->GetHost()->AddObserver(this);
UpdateScreenInfo(window_);
- if (popup_type_ != blink::WebPopupTypeNone)
- event_filter_for_popup_exit_.reset(new EventFilterForPopupExit(this));
aura::client::CursorClient* cursor_client =
aura::client::GetCursorClient(window_->GetRootWindow());
@@ -3443,13 +2313,21 @@ void RenderWidgetHostViewAura::AddedToRootWindow() {
cursor_client->AddObserver(this);
NotifyRendererOfCursorVisibilityState(cursor_client->IsCursorVisible());
}
- if (current_surface_.get())
- UpdateExternalTexture();
if (HasFocus()) {
ui::InputMethod* input_method = GetInputMethod();
if (input_method)
input_method->SetFocusedTextInputClient(this);
}
+
+#if defined(OS_WIN)
+ // The parent may have changed here. Ensure that the legacy window is
+ // reparented accordingly.
+ if (legacy_render_widget_host_HWND_)
+ legacy_render_widget_host_HWND_->UpdateParent(
+ reinterpret_cast<HWND>(GetNativeViewId()));
+#endif
+
+ delegated_frame_host_->AddedToWindow();
}
void RenderWidgetHostViewAura::RemovingFromRootWindow() {
@@ -3460,28 +2338,15 @@ void RenderWidgetHostViewAura::RemovingFromRootWindow() {
DetachFromInputMethod();
- event_filter_for_popup_exit_.reset();
- window_->GetDispatcher()->RemoveRootWindowObserver(this);
- host_->ParentChanged(0);
- ui::Compositor* compositor = GetCompositor();
- if (current_surface_.get()) {
- // We can't get notification for commits after this point, which would
- // guarantee that the compositor isn't using an old texture any more, so
- // instead we force the layer to stop using any external resources which
- // synchronizes with the compositor thread, and makes it safe to run the
- // callback.
- window_->layer()->SetShowPaintedContent();
- }
- RunOnCommitCallbacks();
- resize_lock_.reset();
- host_->WasResized();
- if (compositor && compositor->HasObserver(this))
- compositor->RemoveObserver(this);
-}
+ window_->GetHost()->RemoveObserver(this);
+ delegated_frame_host_->RemovingFromWindow();
-ui::Compositor* RenderWidgetHostViewAura::GetCompositor() const {
- aura::WindowEventDispatcher* dispatcher = window_->GetDispatcher();
- return dispatcher ? dispatcher->compositor() : NULL;
+#if defined(OS_WIN)
+ // Update the legacy window's parent temporarily to the desktop window. It
+ // will eventually get reparented to the right root.
+ if (legacy_render_widget_host_HWND_)
+ legacy_render_widget_host_HWND_->UpdateParent(::GetDesktopWindow());
+#endif
}
void RenderWidgetHostViewAura::DetachFromInputMethod() {
@@ -3490,17 +2355,96 @@ void RenderWidgetHostViewAura::DetachFromInputMethod() {
input_method->SetFocusedTextInputClient(NULL);
}
+void RenderWidgetHostViewAura::ForwardKeyboardEvent(
+ const NativeWebKeyboardEvent& event) {
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+ ui::TextEditKeyBindingsDelegateAuraLinux* keybinding_delegate =
+ ui::GetTextEditKeyBindingsDelegate();
+ std::vector<ui::TextEditCommandAuraLinux> commands;
+ if (!event.skip_in_browser &&
+ keybinding_delegate &&
+ event.os_event &&
+ keybinding_delegate->MatchEvent(*event.os_event, &commands)) {
+ // Transform from ui/ types to content/ types.
+ EditCommands edit_commands;
+ for (std::vector<ui::TextEditCommandAuraLinux>::const_iterator it =
+ commands.begin(); it != commands.end(); ++it) {
+ edit_commands.push_back(EditCommand(it->GetCommandString(),
+ it->argument()));
+ }
+ host_->Send(new InputMsg_SetEditCommandsForNextKeyEvent(
+ host_->GetRoutingID(), edit_commands));
+ NativeWebKeyboardEvent copy_event(event);
+ copy_event.match_edit_command = true;
+ host_->ForwardKeyboardEvent(copy_event);
+ return;
+ }
+#endif
+
+ host_->ForwardKeyboardEvent(event);
+}
+
+SkBitmap::Config RenderWidgetHostViewAura::PreferredReadbackFormat() {
+ return SkBitmap::kARGB_8888_Config;
+}
+
////////////////////////////////////////////////////////////////////////////////
-// RenderWidgetHostView, public:
+// DelegatedFrameHost, public:
-// static
-RenderWidgetHostView* RenderWidgetHostView::CreateViewForWidget(
- RenderWidgetHost* widget) {
- return new RenderWidgetHostViewAura(widget);
+ui::Compositor* RenderWidgetHostViewAura::GetCompositor() const {
+ aura::WindowTreeHost* host = window_->GetHost();
+ return host ? host->compositor() : NULL;
+}
+
+ui::Layer* RenderWidgetHostViewAura::GetLayer() {
+ return window_->layer();
+}
+
+RenderWidgetHostImpl* RenderWidgetHostViewAura::GetHost() {
+ return host_;
+}
+
+void RenderWidgetHostViewAura::SchedulePaintInRect(
+ const gfx::Rect& damage_rect_in_dip) {
+ window_->SchedulePaintInRect(damage_rect_in_dip);
+}
+
+bool RenderWidgetHostViewAura::IsVisible() {
+ return IsShowing();
+}
+
+gfx::Size RenderWidgetHostViewAura::DesiredFrameSize() {
+ return window_->bounds().size();
+}
+
+float RenderWidgetHostViewAura::CurrentDeviceScaleFactor() {
+ return current_device_scale_factor_;
}
+gfx::Size RenderWidgetHostViewAura::ConvertViewSizeToPixel(
+ const gfx::Size& size) {
+ return content::ConvertViewSizeToPixel(this, size);
+}
+
+scoped_ptr<ResizeLock> RenderWidgetHostViewAura::CreateResizeLock(
+ bool defer_compositor_lock) {
+ gfx::Size desired_size = window_->bounds().size();
+ return scoped_ptr<ResizeLock>(new CompositorResizeLock(
+ window_->GetHost(),
+ desired_size,
+ defer_compositor_lock,
+ base::TimeDelta::FromMilliseconds(kResizeLockTimeoutMs)));
+}
+
+DelegatedFrameHost* RenderWidgetHostViewAura::GetDelegatedFrameHost() const {
+ return delegated_frame_host_.get();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// RenderWidgetHostViewBase, public:
+
// static
-void RenderWidgetHostViewPort::GetDefaultScreenInfo(WebScreenInfo* results) {
+void RenderWidgetHostViewBase::GetDefaultScreenInfo(WebScreenInfo* results) {
GetScreenInfoForWindow(results, NULL);
}
diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_aura.h b/chromium/content/browser/renderer_host/render_widget_host_view_aura.h
index 041b61dfd77..8eab68b8851 100644
--- a/chromium/content/browser/renderer_host/render_widget_host_view_aura.h
+++ b/chromium/content/browser/renderer_host/render_widget_host_view_aura.h
@@ -6,6 +6,7 @@
#define CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_AURA_H_
#include <map>
+#include <set>
#include <string>
#include <vector>
@@ -15,36 +16,36 @@
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
-#include "cc/layers/delegated_frame_provider.h"
-#include "cc/layers/delegated_frame_resource_collection.h"
-#include "cc/resources/texture_mailbox.h"
#include "content/browser/accessibility/browser_accessibility_manager.h"
-#include "content/browser/aura/image_transport_factory.h"
-#include "content/browser/aura/owned_mailbox.h"
-#include "content/browser/renderer_host/delegated_frame_evictor.h"
+#include "content/browser/compositor/delegated_frame_host.h"
+#include "content/browser/compositor/image_transport_factory.h"
+#include "content/browser/compositor/owned_mailbox.h"
#include "content/browser/renderer_host/render_widget_host_view_base.h"
-#include "content/browser/renderer_host/software_frame_manager.h"
#include "content/common/content_export.h"
-#include "content/common/gpu/client/gl_helper.h"
+#include "content/common/cursors/webcursor.h"
#include "third_party/skia/include/core/SkRegion.h"
-#include "ui/aura/client/activation_change_observer.h"
-#include "ui/aura/client/activation_delegate.h"
#include "ui/aura/client/cursor_client_observer.h"
#include "ui/aura/client/focus_change_observer.h"
-#include "ui/aura/root_window_observer.h"
#include "ui/aura/window_delegate.h"
+#include "ui/aura/window_tree_host_observer.h"
#include "ui/base/ime/text_input_client.h"
-#include "ui/compositor/compositor.h"
-#include "ui/compositor/compositor_observer.h"
#include "ui/gfx/display_observer.h"
+#include "ui/gfx/insets.h"
#include "ui/gfx/rect.h"
-#include "webkit/common/cursors/webcursor.h"
+#include "ui/wm/public/activation_change_observer.h"
+#include "ui/wm/public/activation_delegate.h"
+
+struct ViewHostMsg_TextInputState_Params;
namespace aura {
class WindowTracker;
+namespace client {
+class ScopedTooltipDisabler;
+}
}
namespace cc {
+class CopyOutputRequest;
class CopyOutputResult;
class DelegatedFrameData;
}
@@ -54,59 +55,41 @@ class Canvas;
class Display;
}
+namespace gpu {
+struct Mailbox;
+}
+
namespace ui {
class CompositorLock;
class InputMethod;
+class LocatedEvent;
class Texture;
}
namespace content {
+#if defined(OS_WIN)
+class LegacyRenderWidgetHostHWND;
+#endif
+
+class OverscrollController;
+class RenderFrameHostImpl;
class RenderWidgetHostImpl;
class RenderWidgetHostView;
-class ResizeLock;
// RenderWidgetHostView class hierarchy described in render_widget_host_view.h.
class CONTENT_EXPORT RenderWidgetHostViewAura
: public RenderWidgetHostViewBase,
- public ui::CompositorObserver,
+ public DelegatedFrameHostClient,
public ui::TextInputClient,
public gfx::DisplayObserver,
- public aura::RootWindowObserver,
+ public aura::WindowTreeHostObserver,
public aura::WindowDelegate,
public aura::client::ActivationDelegate,
public aura::client::ActivationChangeObserver,
public aura::client::FocusChangeObserver,
public aura::client::CursorClientObserver,
- public ImageTransportFactoryObserver,
- public BrowserAccessibilityDelegate,
- public SoftwareFrameManagerClient,
- public DelegatedFrameEvictorClient,
- public base::SupportsWeakPtr<RenderWidgetHostViewAura>,
- public cc::DelegatedFrameResourceCollectionClient {
+ public base::SupportsWeakPtr<RenderWidgetHostViewAura> {
public:
- // Used to notify whenever the paint-content of the view changes.
- class PaintObserver {
- public:
- PaintObserver() {}
- virtual ~PaintObserver() {}
-
- // This is called when painting of the page is completed.
- virtual void OnPaintComplete() = 0;
-
- // This is called when compositor painting of the page is completed.
- virtual void OnCompositingComplete() = 0;
-
- // This is called when the contents for compositor painting changes.
- virtual void OnUpdateCompositorContent() = 0;
-
- // This is called loading the page has completed.
- virtual void OnPageLoadComplete() = 0;
-
- // This is called when the view is destroyed, so that the observer can
- // perform any necessary clean-up.
- virtual void OnViewDestroyed() = 0;
- };
-
// Displays and controls touch editing elements such as selection handles.
class TouchEditingClient {
public:
@@ -115,8 +98,9 @@ class CONTENT_EXPORT RenderWidgetHostViewAura
// Tells the client to start showing touch editing handles.
virtual void StartTouchEditing() = 0;
- // Notifies the client that touch editing is no longer needed.
- virtual void EndTouchEditing() = 0;
+ // Notifies the client that touch editing is no longer needed. |quick|
+ // determines whether the handles should fade out quickly or slowly.
+ virtual void EndTouchEditing(bool quick) = 0;
// Notifies the client that the selection bounds need to be updated.
virtual void OnSelectionOrCursorChanged(const gfx::Rect& anchor,
@@ -140,14 +124,12 @@ class CONTENT_EXPORT RenderWidgetHostViewAura
virtual ~TouchEditingClient() {}
};
- void set_paint_observer(PaintObserver* observer) {
- paint_observer_ = observer;
- }
-
void set_touch_editing_client(TouchEditingClient* client) {
touch_editing_client_ = client;
}
+ explicit RenderWidgetHostViewAura(RenderWidgetHost* host);
+
// RenderWidgetHostView implementation.
virtual void InitAsChild(gfx::NativeView parent_view) OVERRIDE;
virtual RenderWidgetHost* GetRenderWidgetHost() const OVERRIDE;
@@ -156,15 +138,18 @@ class CONTENT_EXPORT RenderWidgetHostViewAura
virtual gfx::NativeView GetNativeView() const OVERRIDE;
virtual gfx::NativeViewId GetNativeViewId() const OVERRIDE;
virtual gfx::NativeViewAccessible GetNativeViewAccessible() OVERRIDE;
+ virtual ui::TextInputClient* GetTextInputClient() OVERRIDE;
virtual bool HasFocus() const OVERRIDE;
virtual bool IsSurfaceAvailableForCopy() const OVERRIDE;
virtual void Show() OVERRIDE;
virtual void Hide() OVERRIDE;
virtual bool IsShowing() OVERRIDE;
virtual gfx::Rect GetViewBounds() const OVERRIDE;
- virtual void SetBackground(const SkBitmap& background) OVERRIDE;
+ virtual void SetBackgroundOpaque(bool opaque) OVERRIDE;
+ virtual gfx::Size GetVisibleViewportSize() const OVERRIDE;
+ virtual void SetInsets(const gfx::Insets& insets) OVERRIDE;
- // Overridden from RenderWidgetHostViewPort:
+ // Overridden from RenderWidgetHostViewBase:
virtual void InitAsPopup(RenderWidgetHostView* parent_host_view,
const gfx::Rect& pos) OVERRIDE;
virtual void InitAsFullscreen(
@@ -172,24 +157,17 @@ class CONTENT_EXPORT RenderWidgetHostViewAura
virtual void WasShown() OVERRIDE;
virtual void WasHidden() OVERRIDE;
virtual void MovePluginWindows(
- const gfx::Vector2d& scroll_offset,
const std::vector<WebPluginGeometry>& moves) OVERRIDE;
virtual void Focus() OVERRIDE;
virtual void Blur() OVERRIDE;
virtual void UpdateCursor(const WebCursor& cursor) OVERRIDE;
virtual void SetIsLoading(bool is_loading) OVERRIDE;
- virtual void TextInputTypeChanged(ui::TextInputType type,
- ui::TextInputMode input_mode,
- bool can_compose_inline) OVERRIDE;
+ virtual void TextInputStateChanged(
+ const ViewHostMsg_TextInputState_Params& params) OVERRIDE;
virtual void ImeCancelComposition() OVERRIDE;
virtual void ImeCompositionRangeChanged(
const gfx::Range& range,
const std::vector<gfx::Rect>& character_bounds) OVERRIDE;
- virtual void DidUpdateBackingStore(
- const gfx::Rect& scroll_rect,
- const gfx::Vector2d& scroll_delta,
- const std::vector<gfx::Rect>& copy_rects,
- const ui::LatencyInfo& latency_info) OVERRIDE;
virtual void RenderProcessGone(base::TerminationStatus status,
int error_code) OVERRIDE;
virtual void Destroy() OVERRIDE;
@@ -197,14 +175,15 @@ class CONTENT_EXPORT RenderWidgetHostViewAura
virtual void SelectionChanged(const base::string16& text,
size_t offset,
const gfx::Range& range) OVERRIDE;
+ virtual gfx::Size GetRequestedRendererSize() const OVERRIDE;
virtual void SelectionBoundsChanged(
const ViewHostMsg_SelectionBounds_Params& params) OVERRIDE;
virtual void ScrollOffsetChanged() OVERRIDE;
- virtual BackingStore* AllocBackingStore(const gfx::Size& size) OVERRIDE;
virtual void CopyFromCompositingSurface(
const gfx::Rect& src_subrect,
const gfx::Size& dst_size,
- const base::Callback<void(bool, const SkBitmap&)>& callback) OVERRIDE;
+ const base::Callback<void(bool, const SkBitmap&)>& callback,
+ const SkBitmap::Config config) OVERRIDE;
virtual void CopyFromCompositingSurfaceToVideoFrame(
const gfx::Rect& src_subrect,
const scoped_refptr<media::VideoFrame>& target,
@@ -214,7 +193,6 @@ class CONTENT_EXPORT RenderWidgetHostViewAura
virtual void BeginFrameSubscription(
scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) OVERRIDE;
virtual void EndFrameSubscription() OVERRIDE;
- virtual void OnAcceleratedCompositingStateChange() OVERRIDE;
virtual void AcceleratedSurfaceInitialized(int host_id,
int route_id) OVERRIDE;
virtual void AcceleratedSurfaceBuffersSwapped(
@@ -228,26 +206,25 @@ class CONTENT_EXPORT RenderWidgetHostViewAura
virtual bool HasAcceleratedSurface(const gfx::Size& desired_size) OVERRIDE;
virtual void GetScreenInfo(blink::WebScreenInfo* results) OVERRIDE;
virtual gfx::Rect GetBoundsInRootWindow() OVERRIDE;
- virtual void GestureEventAck(int gesture_event_type,
+ virtual void WheelEventAck(const blink::WebMouseWheelEvent& event,
+ InputEventAckState ack_result) OVERRIDE;
+ virtual void GestureEventAck(const blink::WebGestureEvent& event,
InputEventAckState ack_result) OVERRIDE;
virtual void ProcessAckedTouchEvent(
const TouchEventWithLatencyInfo& touch,
InputEventAckState ack_result) OVERRIDE;
virtual scoped_ptr<SyntheticGestureTarget> CreateSyntheticGestureTarget()
OVERRIDE;
- virtual void SetHasHorizontalScrollbar(
- bool has_horizontal_scrollbar) OVERRIDE;
- virtual void SetScrollOffsetPinning(
- bool is_pinned_to_left, bool is_pinned_to_right) OVERRIDE;
+ virtual InputEventAckState FilterInputEvent(
+ const blink::WebInputEvent& input_event) OVERRIDE;
virtual gfx::GLSurfaceHandle GetCompositingSurface() OVERRIDE;
- virtual void OnAccessibilityEvents(
- const std::vector<AccessibilityHostMsg_EventParams>&
- params) OVERRIDE;
+ virtual void CreateBrowserAccessibilityManagerIfNeeded() OVERRIDE;
virtual bool LockMouse() OVERRIDE;
virtual void UnlockMouse() OVERRIDE;
virtual void OnSwapCompositorFrame(
uint32 output_surface_id,
scoped_ptr<cc::CompositorFrame> frame) OVERRIDE;
+
#if defined(OS_WIN)
virtual void SetParentNativeViewAccessible(
gfx::NativeViewAccessible accessible_parent) OVERRIDE;
@@ -260,7 +237,7 @@ class CONTENT_EXPORT RenderWidgetHostViewAura
virtual void ConfirmCompositionText() OVERRIDE;
virtual void ClearCompositionText() OVERRIDE;
virtual void InsertText(const base::string16& text) OVERRIDE;
- virtual void InsertChar(char16 ch, int flags) OVERRIDE;
+ virtual void InsertChar(base::char16 ch, int flags) OVERRIDE;
virtual gfx::NativeWindow GetAttachedWindow() const OVERRIDE;
virtual ui::TextInputType GetTextInputType() const OVERRIDE;
virtual ui::TextInputMode GetTextInputMode() const OVERRIDE;
@@ -284,11 +261,14 @@ class CONTENT_EXPORT RenderWidgetHostViewAura
virtual void OnCandidateWindowShown() OVERRIDE;
virtual void OnCandidateWindowUpdated() OVERRIDE;
virtual void OnCandidateWindowHidden() OVERRIDE;
+ virtual bool IsEditingCommandEnabled(int command_id) OVERRIDE;
+ virtual void ExecuteEditingCommand(int command_id) OVERRIDE;
// Overridden from gfx::DisplayObserver:
- virtual void OnDisplayBoundsChanged(const gfx::Display& display) OVERRIDE;
virtual void OnDisplayAdded(const gfx::Display& new_display) OVERRIDE;
virtual void OnDisplayRemoved(const gfx::Display& old_display) OVERRIDE;
+ virtual void OnDisplayMetricsChanged(const gfx::Display& display,
+ uint32_t metrics) OVERRIDE;
// Overridden from aura::WindowDelegate:
virtual gfx::Size GetMinimumSize() const OVERRIDE;
@@ -304,13 +284,11 @@ class CONTENT_EXPORT RenderWidgetHostViewAura
virtual void OnCaptureLost() OVERRIDE;
virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE;
- virtual void OnWindowDestroying() OVERRIDE;
- virtual void OnWindowDestroyed() OVERRIDE;
+ virtual void OnWindowDestroying(aura::Window* window) OVERRIDE;
+ virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE;
virtual void OnWindowTargetVisibilityChanged(bool visible) OVERRIDE;
virtual bool HasHitTestMask() const OVERRIDE;
virtual void GetHitTestMask(gfx::Path* mask) const OVERRIDE;
- virtual void DidRecreateLayer(ui::Layer *old_layer,
- ui::Layer *new_layer) OVERRIDE;
// Overridden from ui::EventHandler:
virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE;
@@ -333,62 +311,45 @@ class CONTENT_EXPORT RenderWidgetHostViewAura
virtual void OnWindowFocused(aura::Window* gained_focus,
aura::Window* lost_focus) OVERRIDE;
- // Overridden from aura::RootWindowObserver:
- virtual void OnRootWindowHostMoved(const aura::RootWindow* root,
- const gfx::Point& new_origin) OVERRIDE;
-
- // SoftwareFrameManagerClient implementation:
- virtual void SoftwareFrameWasFreed(
- uint32 output_surface_id, unsigned frame_id) OVERRIDE;
- virtual void ReleaseReferencesToSoftwareFrame() OVERRIDE;
-
- bool CanCopyToBitmap() const;
+ // Overridden from aura::WindowTreeHostObserver:
+ virtual void OnHostMoved(const aura::WindowTreeHost* host,
+ const gfx::Point& new_origin) OVERRIDE;
#if defined(OS_WIN)
// Sets the cutout rects from constrained windows. These are rectangles that
// windowed NPAPI plugins shouldn't paint in. Overwrites any previous cutout
// rects.
void UpdateConstrainedWindowRects(const std::vector<gfx::Rect>& rects);
+
+ // Updates the cursor clip region. Used for mouse locking.
+ void UpdateMouseLockRegion();
#endif
// Method to indicate if this instance is shutting down or closing.
// TODO(shrikant): Discuss around to see if it makes sense to add this method
// as part of RenderWidgetHostView.
- bool IsClosing() const { return in_shutdown_; };
+ bool IsClosing() const { return in_shutdown_; }
- protected:
- friend class RenderWidgetHostView;
- virtual ~RenderWidgetHostViewAura();
-
- // Should be constructed via RenderWidgetHostView::CreateViewForWidget.
- explicit RenderWidgetHostViewAura(RenderWidgetHost* host);
+ // Sets whether the overscroll controller should be enabled for this page.
+ void SetOverscrollControllerEnabled(bool enabled);
- RenderWidgetHostViewFrameSubscriber* frame_subscriber() const {
- return frame_subscriber_.get();
+ OverscrollController* overscroll_controller() const {
+ return overscroll_controller_.get();
}
- virtual bool ShouldCreateResizeLock();
- virtual scoped_ptr<ResizeLock> CreateResizeLock(bool defer_compositor_lock);
+ protected:
+ virtual ~RenderWidgetHostViewAura();
// Exposed for tests.
aura::Window* window() { return window_; }
- gfx::Size current_frame_size() const { return current_frame_size_; }
-
- // Overridden from ui::CompositorObserver:
- virtual void OnCompositingDidCommit(ui::Compositor* compositor) OVERRIDE;
- virtual void OnCompositingStarted(ui::Compositor* compositor,
- base::TimeTicks start_time) OVERRIDE;
- virtual void OnCompositingEnded(ui::Compositor* compositor) OVERRIDE;
- virtual void OnCompositingAborted(ui::Compositor* compositor) OVERRIDE;
- virtual void OnCompositingLockStateChanged(
- ui::Compositor* compositor) OVERRIDE;
- virtual void OnUpdateVSyncParameters(ui::Compositor* compositor,
- base::TimeTicks timebase,
- base::TimeDelta interval) OVERRIDE;
+ virtual SkBitmap::Config PreferredReadbackFormat() OVERRIDE;
+ virtual DelegatedFrameHost* GetDelegatedFrameHost() const OVERRIDE;
private:
FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest, SetCompositionText);
FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest, TouchEventState);
+ FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest,
+ TouchEventPositionsArentRounded);
FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest, TouchEventSyncAsync);
FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest, SwapNotifiesWindow);
FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest,
@@ -396,42 +357,34 @@ class CONTENT_EXPORT RenderWidgetHostViewAura
FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest, OutputSurfaceIdChange);
FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest,
DiscardDelegatedFrames);
+ FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest,
+ DiscardDelegatedFramesWithLocking);
FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest, SoftwareDPIChange);
+ FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest,
+ UpdateCursorIfOverSelf);
+ FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraCopyRequestTest,
+ DestroyedAfterCopyRequest);
+ FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest,
+ VisibleViewportTest);
+ FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest,
+ OverscrollResetsOnBlur);
class WindowObserver;
friend class WindowObserver;
- // Overridden from ImageTransportFactoryObserver:
- virtual void OnLostResources() OVERRIDE;
-
- // Overridden from BrowserAccessibilityDelegate:
- virtual void SetAccessibilityFocus(int acc_obj_id) OVERRIDE;
- virtual void AccessibilityDoDefaultAction(int acc_obj_id) OVERRIDE;
- virtual void AccessibilityScrollToMakeVisible(
- int acc_obj_id, gfx::Rect subfocus) OVERRIDE;
- virtual void AccessibilityScrollToPoint(
- int acc_obj_id, gfx::Point point) OVERRIDE;
- virtual void AccessibilitySetTextSelection(
- int acc_obj_id, int start_offset, int end_offset) OVERRIDE;
- virtual gfx::Point GetLastTouchEventLocation() const OVERRIDE;
- virtual void FatalAccessibilityTreeError() OVERRIDE;
-
void UpdateCursorIfOverSelf();
- bool ShouldSkipFrame(gfx::Size size_in_dip) const;
+
+ void SnapToPhysicalPixelBoundary();
// Set the bounds of the window and handle size changes. Assumes the caller
// has already adjusted the origin of |rect| to conform to whatever coordinate
// space is required by the aura::Window.
void InternalSetBounds(const gfx::Rect& rect);
- // 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();
+#if defined(OS_WIN)
+ bool UsesNativeWindowFrame() const;
+#endif
- void UpdateExternalTexture();
ui::InputMethod* GetInputMethod() const;
// Returns whether the widget needs an input grab to work properly.
@@ -457,56 +410,36 @@ class CONTENT_EXPORT RenderWidgetHostViewAura
// moved to center.
bool ShouldMoveToCenter();
- // Run all on compositing commit callbacks.
- void RunOnCommitCallbacks();
-
- // Add on compositing commit callback.
- void AddOnCommitCallbackAndDisableLocks(const base::Closure& callback);
-
- // Called after |window_| is parented to a RootWindow.
+ // Called after |window_| is parented to a WindowEventDispatcher.
void AddedToRootWindow();
- // Called prior to removing |window_| from a RootWindow.
+ // Called prior to removing |window_| from a WindowEventDispatcher.
void RemovingFromRootWindow();
- // Called after commit for the last reference to the texture going away
- // after it was released as the frontbuffer.
- void SetSurfaceNotInUseByCompositor(scoped_refptr<ui::Texture>);
-
- // 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 base::Callback<void(bool, const SkBitmap&)>& callback,
- scoped_ptr<cc::CopyOutputResult> result);
- static void PrepareTextureCopyOutputResult(
- const gfx::Size& dst_size_in_pixel,
- const base::Callback<void(bool, const SkBitmap&)>& callback,
- scoped_ptr<cc::CopyOutputResult> result);
- static void PrepareBitmapCopyOutputResult(
- const gfx::Size& dst_size_in_pixel,
- const base::Callback<void(bool, const SkBitmap&)>& callback,
- scoped_ptr<cc::CopyOutputResult> result);
- static void CopyFromCompositingSurfaceHasResultForVideo(
- base::WeakPtr<RenderWidgetHostViewAura> rwhva,
- scoped_refptr<OwnedMailbox> subscriber_texture,
- scoped_refptr<media::VideoFrame> video_frame,
- const base::Callback<void(bool)>& callback,
- scoped_ptr<cc::CopyOutputResult> result);
- static void CopyFromCompositingSurfaceFinishedForVideo(
- base::WeakPtr<RenderWidgetHostViewAura> rwhva,
- const base::Callback<void(bool)>& callback,
- scoped_refptr<OwnedMailbox> subscriber_texture,
- scoped_ptr<cc::SingleReleaseCallback> release_callback,
- bool result);
-
- ui::Compositor* GetCompositor() const;
+ // DelegatedFrameHostClient implementation.
+ virtual ui::Compositor* GetCompositor() const OVERRIDE;
+ virtual ui::Layer* GetLayer() OVERRIDE;
+ virtual RenderWidgetHostImpl* GetHost() OVERRIDE;
+ virtual void SchedulePaintInRect(
+ const gfx::Rect& damage_rect_in_dip) OVERRIDE;
+ virtual bool IsVisible() OVERRIDE;
+ virtual scoped_ptr<ResizeLock> CreateResizeLock(
+ bool defer_compositor_lock) OVERRIDE;
+ virtual gfx::Size DesiredFrameSize() OVERRIDE;
+ virtual float CurrentDeviceScaleFactor() OVERRIDE;
+ virtual gfx::Size ConvertViewSizeToPixel(const gfx::Size& size) OVERRIDE;
// Detaches |this| from the input method object.
void DetachFromInputMethod();
- // Dismisses a Web Popup on mouse press outside the popup and its parent.
- void ApplyEventFilterForPopupExit(ui::MouseEvent* event);
+ // Before calling RenderWidgetHost::ForwardKeyboardEvent(), this method
+ // calls our keybindings handler against the event and send matched
+ // edit commands to renderer instead.
+ void ForwardKeyboardEvent(const NativeWebKeyboardEvent& event);
+
+ // Dismisses a Web Popup on a mouse or touch press outside the popup and its
+ // parent.
+ void ApplyEventFilterForPopupExit(ui::LocatedEvent* event);
// Converts |rect| from window coordinate to screen coordinate.
gfx::Rect ConvertRectToScreen(const gfx::Rect& rect) const;
@@ -514,63 +447,18 @@ class CONTENT_EXPORT RenderWidgetHostViewAura
// Converts |rect| from screen coordinate to window coordinate.
gfx::Rect ConvertRectFromScreen(const gfx::Rect& rect) const;
- typedef base::Callback<void(bool, const scoped_refptr<ui::Texture>&)>
- BufferPresentedCallback;
-
- // The common entry point for buffer updates from renderer
- // and GPU process.
- void BuffersSwapped(const gfx::Size& surface_size,
- const gfx::Rect& damage_rect,
- float surface_scale_factor,
- const std::string& mailbox_name,
- const ui::LatencyInfo& latency_info,
- const BufferPresentedCallback& ack_callback);
-
- bool SwapBuffersPrepare(const gfx::Rect& surface_rect,
- float surface_scale_factor,
- const gfx::Rect& damage_rect,
- const std::string& mailbox_name,
- const BufferPresentedCallback& ack_callback);
-
- void SwapBuffersCompleted(
- const BufferPresentedCallback& ack_callback,
- const scoped_refptr<ui::Texture>& texture_to_return);
-
- void SwapDelegatedFrame(
- uint32 output_surface_id,
- scoped_ptr<cc::DelegatedFrameData> frame_data,
- float frame_device_scale_factor,
- const ui::LatencyInfo& latency_info);
- void SendDelegatedFrameAck(uint32 output_surface_id);
- void SendReturnedDelegatedResources(uint32 output_surface_id);
-
- // DelegatedFrameEvictorClient implementation.
- virtual void EvictDelegatedFrame() OVERRIDE;
-
- // cc::DelegatedFrameProviderClient implementation.
- virtual void UnusedResourcesAreAvailable() OVERRIDE;
-
- void SwapSoftwareFrame(uint32 output_surface_id,
- scoped_ptr<cc::SoftwareFrameData> frame_data,
- float frame_device_scale_factor,
- const ui::LatencyInfo& latency_info);
- void SendSoftwareFrameAck(uint32 output_surface_id);
- void SendReclaimSoftwareFrames();
- void ReleaseSoftwareFrame(uint32 output_surface_id,
- unsigned software_frame_id);
-
- void DidReceiveFrameFromRenderer();
-
- BrowserAccessibilityManager* GetOrCreateBrowserAccessibilityManager();
-
// Helper function to set keyboard focus to the main window.
void SetKeyboardFocus();
+ RenderFrameHostImpl* GetFocusedFrame();
+
// The model object.
RenderWidgetHostImpl* host_;
aura::Window* window_;
+ scoped_ptr<DelegatedFrameHost> delegated_frame_host_;
+
scoped_ptr<WindowObserver> window_observer_;
// Are we in the process of closing? Tracked so fullscreen views can avoid
@@ -627,48 +515,9 @@ class CONTENT_EXPORT RenderWidgetHostViewAura
// Current tooltip text.
base::string16 tooltip_;
- std::vector<base::Closure> on_compositing_did_commit_callbacks_;
-
- // The current frontbuffer texture.
- scoped_refptr<ui::Texture> current_surface_;
-
- // This holds the current software framebuffer, if any.
- scoped_ptr<SoftwareFrameManager> software_frame_manager_;
-
- // With delegated renderer, this is the last output surface, used to
- // disambiguate resources with the same id coming from different output
- // surfaces.
- uint32 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_;
-
- // The damage in the previously presented buffer.
- SkRegion previous_damage_;
-
- // Pending damage from previous frames that we skipped.
- SkRegion skipped_damage_;
-
- // True after a delegated frame has been skipped, until a frame is not
- // skipped.
- bool skipped_frames_;
-
- // 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<cc::DelegatedFrameResourceCollection> resource_collection_;
-
- // Provides delegated frame updates to the cc::DelegatedRendererLayer.
- scoped_refptr<cc::DelegatedFrameProvider> frame_provider_;
-
- // The size of the last frame that was swapped (even if we skipped it).
- // Used to determine when the skipped_damage_ needs to be reset due to
- // size changes between front- and backbuffer.
- gfx::Size last_swapped_surface_size_;
- float last_swapped_surface_scale_factor_;
-
- gfx::GLSurfaceHandle shared_surface_handle_;
+ // The size and scale of the last software compositing frame that was swapped.
+ gfx::Size last_swapped_software_frame_size_;
+ float last_swapped_software_frame_scale_factor_;
// If non-NULL we're in OnPaint() and this is the supplied canvas.
gfx::Canvas* paint_canvas_;
@@ -688,40 +537,10 @@ class CONTENT_EXPORT RenderWidgetHostViewAura
// events vs. normal mouse move events.
bool synthetic_move_sent_;
- // Signals that the accelerated compositing has been turned on or off.
- // This is used to signal to turn off the external texture as soon as the
- // software backing store is updated.
- bool accelerated_compositing_state_changed_;
-
- // 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<ResizeLock> resize_lock_;
-
- // Keeps track of the current frame size.
- gfx::Size current_frame_size_;
-
- // This lock is for waiting for a front surface to become available to draw.
- scoped_refptr<ui::CompositorLock> released_front_lock_;
-
// Used to track the state of the window we're created from. Only used when
// created fullscreen.
scoped_ptr<aura::WindowTracker> host_tracker_;
- enum CanLockCompositorState {
- YES,
- // 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_;
-
// Used to track the last cursor visibility update that was sent to the
// renderer via NotifyRendererOfCursorVisibilityState().
enum CursorVisibilityState {
@@ -731,11 +550,6 @@ class CONTENT_EXPORT RenderWidgetHostViewAura
};
CursorVisibilityState cursor_visibility_state_in_renderer_;
- // An observer to notify that the paint content of the view has changed. The
- // observer is not owned by the view, and must remove itself as an oberver
- // when it is being destroyed.
- PaintObserver* paint_observer_;
-
#if defined(OS_WIN)
// The list of rectangles from constrained windows over this view. Windowed
// NPAPI plugins shouldn't draw over them.
@@ -749,36 +563,25 @@ class CONTENT_EXPORT RenderWidgetHostViewAura
PluginWindowMoves plugin_window_moves_;
#endif
- base::TimeTicks last_draw_ended_;
+ TouchEditingClient* touch_editing_client_;
- // Subscriber that listens to frame presentation events.
- scoped_ptr<RenderWidgetHostViewFrameSubscriber> frame_subscriber_;
- std::vector<scoped_refptr<OwnedMailbox> > idle_frame_subscriber_textures_;
+ scoped_ptr<OverscrollController> overscroll_controller_;
- // YUV readback pipeline.
- scoped_ptr<content::ReadbackYUVInterface>
- yuv_readback_pipeline_;
+ gfx::Insets insets_;
- TouchEditingClient* touch_editing_client_;
+ std::vector<ui::LatencyInfo> software_latency_info_;
- ui::LatencyInfo software_latency_info_;
-
- struct ReleasedFrameInfo {
- ReleasedFrameInfo(uint32 output_id, unsigned software_frame_id)
- : output_surface_id(output_id), frame_id(software_frame_id) {}
- uint32 output_surface_id;
- unsigned frame_id;
- };
- scoped_ptr<ReleasedFrameInfo> released_software_frame_;
- scoped_ptr<DelegatedFrameEvictor> delegated_frame_evictor_;
+ scoped_ptr<aura::client::ScopedTooltipDisabler> tooltip_disabler_;
base::WeakPtrFactory<RenderWidgetHostViewAura> weak_ptr_factory_;
#if defined(OS_WIN)
- // The dummy HWND which corresponds to the bounds of the web page. This is
- // passed to windowless plugins like Flash/Silverlight, etc as the
- // container window.
- HWND plugin_parent_window_;
+ // The LegacyRenderWidgetHostHWND class provides a dummy HWND which is used
+ // for accessibility, as the container for windowless plugins like
+ // Flash/Silverlight, etc and for legacy drivers for trackpoints/trackpads,
+ // etc.
+ scoped_ptr<content::LegacyRenderWidgetHostHWND>
+ legacy_render_widget_host_HWND_;
#endif
DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewAura);
};
diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/chromium/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
index 120956067df..d05bd44da8b 100644
--- a/chromium/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
+++ b/chromium/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -5,20 +5,28 @@
#include "content/browser/renderer_host/render_widget_host_view_aura.h"
#include "base/basictypes.h"
+#include "base/command_line.h"
#include "base/memory/shared_memory.h"
#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "cc/output/compositor_frame.h"
#include "cc/output/compositor_frame_metadata.h"
-#include "cc/output/gl_frame_data.h"
-#include "content/browser/aura/resize_lock.h"
+#include "cc/output/copy_output_request.h"
#include "content/browser/browser_thread_impl.h"
+#include "content/browser/compositor/resize_lock.h"
+#include "content/browser/renderer_host/overscroll_controller.h"
+#include "content/browser/renderer_host/overscroll_controller_delegate.h"
#include "content/browser/renderer_host/render_widget_host_delegate.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "content/common/gpu/client/gl_helper.h"
#include "content/common/gpu/gpu_messages.h"
+#include "content/common/host_shared_bitmap_manager.h"
+#include "content/common/input/synthetic_web_input_event_builders.h"
#include "content/common/input_messages.h"
#include "content/common/view_messages.h"
#include "content/public/browser/render_widget_host_view.h"
+#include "content/public/browser/render_widget_host_view_frame_subscriber.h"
#include "content/public/test/mock_render_process_host.h"
#include "content/public/test/test_browser_context.h"
#include "ipc/ipc_test_sink.h"
@@ -29,21 +37,32 @@
#include "ui/aura/client/window_tree_client.h"
#include "ui/aura/env.h"
#include "ui/aura/layout_manager.h"
-#include "ui/aura/root_window.h"
#include "ui/aura/test/aura_test_helper.h"
+#include "ui/aura/test/event_generator.h"
#include "ui/aura/test/test_cursor_client.h"
#include "ui/aura/test/test_screen.h"
#include "ui/aura/test/test_window_delegate.h"
#include "ui/aura/window.h"
+#include "ui/aura/window_event_dispatcher.h"
#include "ui/aura/window_observer.h"
#include "ui/base/ui_base_types.h"
#include "ui/compositor/compositor.h"
-#include "ui/compositor/test/test_context_factory.h"
+#include "ui/compositor/test/draw_waiter_for_test.h"
+#include "ui/compositor/test/in_process_context_factory.h"
#include "ui/events/event.h"
#include "ui/events/event_utils.h"
+#include "ui/events/gestures/gesture_configuration.h"
+#include "ui/wm/core/default_activation_client.h"
using testing::_;
+using blink::WebGestureEvent;
+using blink::WebInputEvent;
+using blink::WebMouseEvent;
+using blink::WebMouseWheelEvent;
+using blink::WebTouchEvent;
+using blink::WebTouchPoint;
+
namespace content {
namespace {
@@ -76,6 +95,61 @@ class TestScreenPositionClient
}
};
+class TestOverscrollDelegate : public OverscrollControllerDelegate {
+ public:
+ explicit TestOverscrollDelegate(RenderWidgetHostView* view)
+ : view_(view),
+ current_mode_(OVERSCROLL_NONE),
+ completed_mode_(OVERSCROLL_NONE),
+ delta_x_(0.f),
+ delta_y_(0.f) {}
+
+ virtual ~TestOverscrollDelegate() {}
+
+ OverscrollMode current_mode() const { return current_mode_; }
+ OverscrollMode completed_mode() const { return completed_mode_; }
+ float delta_x() const { return delta_x_; }
+ float delta_y() const { return delta_y_; }
+
+ void Reset() {
+ current_mode_ = OVERSCROLL_NONE;
+ completed_mode_ = OVERSCROLL_NONE;
+ delta_x_ = delta_y_ = 0.f;
+ }
+
+ private:
+ // Overridden from OverscrollControllerDelegate:
+ virtual gfx::Rect GetVisibleBounds() const OVERRIDE {
+ return view_->IsShowing() ? view_->GetViewBounds() : gfx::Rect();
+ }
+
+ virtual void OnOverscrollUpdate(float delta_x, float delta_y) OVERRIDE {
+ delta_x_ = delta_x;
+ delta_y_ = delta_y;
+ }
+
+ virtual void OnOverscrollComplete(OverscrollMode overscroll_mode) OVERRIDE {
+ EXPECT_EQ(current_mode_, overscroll_mode);
+ completed_mode_ = overscroll_mode;
+ current_mode_ = OVERSCROLL_NONE;
+ }
+
+ virtual void OnOverscrollModeChange(OverscrollMode old_mode,
+ OverscrollMode new_mode) OVERRIDE {
+ EXPECT_EQ(current_mode_, old_mode);
+ current_mode_ = new_mode;
+ delta_x_ = delta_y_ = 0.f;
+ }
+
+ RenderWidgetHostView* view_;
+ OverscrollMode current_mode_;
+ OverscrollMode completed_mode_;
+ float delta_x_;
+ float delta_y_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestOverscrollDelegate);
+};
+
class MockRenderWidgetHostDelegate : public RenderWidgetHostDelegate {
public:
MockRenderWidgetHostDelegate() {}
@@ -113,6 +187,34 @@ class TestWindowObserver : public aura::WindowObserver {
DISALLOW_COPY_AND_ASSIGN(TestWindowObserver);
};
+class FakeFrameSubscriber : public RenderWidgetHostViewFrameSubscriber {
+ public:
+ FakeFrameSubscriber(gfx::Size size, base::Callback<void(bool)> callback)
+ : size_(size), callback_(callback) {}
+
+ virtual bool ShouldCaptureFrame(base::TimeTicks present_time,
+ scoped_refptr<media::VideoFrame>* storage,
+ DeliverFrameCallback* callback) OVERRIDE {
+ *storage = media::VideoFrame::CreateFrame(media::VideoFrame::YV12,
+ size_,
+ gfx::Rect(size_),
+ size_,
+ base::TimeDelta());
+ *callback = base::Bind(&FakeFrameSubscriber::CallbackMethod, callback_);
+ return true;
+ }
+
+ static void CallbackMethod(base::Callback<void(bool)> callback,
+ base::TimeTicks timestamp,
+ bool success) {
+ callback.Run(success);
+ }
+
+ private:
+ gfx::Size size_;
+ base::Callback<void(bool)> callback_;
+};
+
class FakeRenderWidgetHostViewAura : public RenderWidgetHostViewAura {
public:
FakeRenderWidgetHostViewAura(RenderWidgetHost* widget)
@@ -120,20 +222,38 @@ class FakeRenderWidgetHostViewAura : public RenderWidgetHostViewAura {
virtual ~FakeRenderWidgetHostViewAura() {}
- virtual bool ShouldCreateResizeLock() OVERRIDE {
- gfx::Size desired_size = window()->bounds().size();
- return desired_size != current_frame_size();
- }
-
- virtual scoped_ptr<ResizeLock> CreateResizeLock(bool defer_compositor_lock)
- OVERRIDE {
+ virtual scoped_ptr<ResizeLock> CreateResizeLock(
+ bool defer_compositor_lock) OVERRIDE {
gfx::Size desired_size = window()->bounds().size();
return scoped_ptr<ResizeLock>(
new FakeResizeLock(desired_size, defer_compositor_lock));
}
void RunOnCompositingDidCommit() {
- OnCompositingDidCommit(window()->GetDispatcher()->compositor());
+ GetDelegatedFrameHost()->OnCompositingDidCommitForTesting(
+ window()->GetHost()->compositor());
+ }
+
+ virtual bool ShouldCreateResizeLock() OVERRIDE {
+ return GetDelegatedFrameHost()->ShouldCreateResizeLockForTesting();
+ }
+
+ virtual void RequestCopyOfOutput(scoped_ptr<cc::CopyOutputRequest> request)
+ OVERRIDE {
+ last_copy_request_ = request.Pass();
+ if (last_copy_request_->has_texture_mailbox()) {
+ // Give the resulting texture a size.
+ GLHelper* gl_helper = ImageTransportFactory::GetInstance()->GetGLHelper();
+ GLuint texture = gl_helper->ConsumeMailboxToTexture(
+ last_copy_request_->texture_mailbox().mailbox(),
+ last_copy_request_->texture_mailbox().sync_point());
+ gl_helper->ResizeTexture(texture, window()->bounds().size());
+ gl_helper->DeleteTexture(texture);
+ }
+ }
+
+ cc::DelegatedFrameProvider* frame_provider() const {
+ return GetDelegatedFrameHost()->FrameProviderForTesting();
}
// A lock that doesn't actually do anything to the compositor, and does not
@@ -146,18 +266,58 @@ class FakeRenderWidgetHostViewAura : public RenderWidgetHostViewAura {
bool has_resize_lock_;
gfx::Size last_frame_size_;
+ scoped_ptr<cc::CopyOutputRequest> last_copy_request_;
+};
+
+// A layout manager that always resizes a child to the root window size.
+class FullscreenLayoutManager : public aura::LayoutManager {
+ public:
+ explicit FullscreenLayoutManager(aura::Window* owner) : owner_(owner) {}
+ virtual ~FullscreenLayoutManager() {}
+
+ // Overridden from aura::LayoutManager:
+ virtual void OnWindowResized() OVERRIDE {
+ aura::Window::Windows::const_iterator i;
+ for (i = owner_->children().begin(); i != owner_->children().end(); ++i) {
+ (*i)->SetBounds(gfx::Rect());
+ }
+ }
+ virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE {
+ child->SetBounds(gfx::Rect());
+ }
+ virtual void OnWillRemoveWindowFromLayout(aura::Window* child) OVERRIDE {}
+ virtual void OnWindowRemovedFromLayout(aura::Window* child) OVERRIDE {}
+ virtual void OnChildWindowVisibilityChanged(aura::Window* child,
+ bool visible) OVERRIDE {}
+ virtual void SetChildBounds(aura::Window* child,
+ const gfx::Rect& requested_bounds) OVERRIDE {
+ SetChildBoundsDirect(child, gfx::Rect(owner_->bounds().size()));
+ }
+
+ private:
+ aura::Window* owner_;
+ DISALLOW_COPY_AND_ASSIGN(FullscreenLayoutManager);
+};
+
+class MockWindowObserver : public aura::WindowObserver {
+ public:
+ MOCK_METHOD2(OnWindowPaintScheduled, void(aura::Window*, const gfx::Rect&));
};
+} // namespace
+
class RenderWidgetHostViewAuraTest : public testing::Test {
public:
RenderWidgetHostViewAuraTest()
: browser_thread_for_ui_(BrowserThread::UI, &message_loop_) {}
- virtual void SetUp() {
+ void SetUpEnvironment() {
+ ui::ContextFactory* context_factory = new ui::InProcessContextFactory;
ImageTransportFactory::InitializeForUnitTests(
- scoped_ptr<ui::ContextFactory>(new ui::TestContextFactory));
+ scoped_ptr<ui::ContextFactory>(context_factory));
aura_test_helper_.reset(new aura::test::AuraTestHelper(&message_loop_));
- aura_test_helper_->SetUp();
+ aura_test_helper_->SetUp(context_factory);
+ new wm::DefaultActivationClient(aura_test_helper_->root_window());
browser_context_.reset(new TestBrowserContext);
process_host_ = new MockRenderProcessHost(browser_context_.get());
@@ -166,8 +326,7 @@ class RenderWidgetHostViewAuraTest : public testing::Test {
parent_host_ = new RenderWidgetHostImpl(
&delegate_, process_host_, MSG_ROUTING_NONE, false);
- parent_view_ = static_cast<RenderWidgetHostViewAura*>(
- RenderWidgetHostView::CreateViewForWidget(parent_host_));
+ parent_view_ = new RenderWidgetHostViewAura(parent_host_);
parent_view_->InitAsChild(NULL);
aura::client::ParentWindowWithContext(parent_view_->GetNativeView(),
aura_test_helper_->root_window(),
@@ -176,12 +335,10 @@ class RenderWidgetHostViewAuraTest : public testing::Test {
widget_host_ = new RenderWidgetHostImpl(
&delegate_, process_host_, MSG_ROUTING_NONE, false);
widget_host_->Init();
- widget_host_->OnMessageReceived(
- ViewHostMsg_DidActivateAcceleratedCompositing(0, true));
view_ = new FakeRenderWidgetHostViewAura(widget_host_);
}
- virtual void TearDown() {
+ void TearDownEnvironment() {
sink_ = NULL;
process_host_ = NULL;
if (view_)
@@ -199,6 +356,10 @@ class RenderWidgetHostViewAuraTest : public testing::Test {
ImageTransportFactory::Terminate();
}
+ virtual void SetUp() { SetUpEnvironment(); }
+
+ virtual void TearDown() { TearDownEnvironment(); }
+
protected:
base::MessageLoopForUI message_loop_;
BrowserThreadImpl browser_thread_for_ui_;
@@ -223,46 +384,239 @@ class RenderWidgetHostViewAuraTest : public testing::Test {
DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewAuraTest);
};
-// A layout manager that always resizes a child to the root window size.
-class FullscreenLayoutManager : public aura::LayoutManager {
+class RenderWidgetHostViewAuraOverscrollTest
+ : public RenderWidgetHostViewAuraTest {
public:
- explicit FullscreenLayoutManager(aura::Window* owner)
- : owner_(owner) {}
- virtual ~FullscreenLayoutManager() {}
+ RenderWidgetHostViewAuraOverscrollTest() {}
- // Overridden from aura::LayoutManager:
- virtual void OnWindowResized() OVERRIDE {
- aura::Window::Windows::const_iterator i;
- for (i = owner_->children().begin(); i != owner_->children().end(); ++i) {
- (*i)->SetBounds(gfx::Rect());
- }
+ // We explicitly invoke SetUp to allow gesture debounce customization.
+ virtual void SetUp() {}
+
+ protected:
+ void SetUpOverscrollEnvironmentWithDebounce(int debounce_interval_in_ms) {
+ SetUpOverscrollEnvironmentImpl(debounce_interval_in_ms);
}
- virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE {
- child->SetBounds(gfx::Rect());
+
+ void SetUpOverscrollEnvironment() { SetUpOverscrollEnvironmentImpl(0); }
+
+ void SetUpOverscrollEnvironmentImpl(int debounce_interval_in_ms) {
+ ui::GestureConfiguration::set_scroll_debounce_interval_in_ms(
+ debounce_interval_in_ms);
+
+ RenderWidgetHostViewAuraTest::SetUp();
+
+ view_->SetOverscrollControllerEnabled(true);
+ overscroll_delegate_.reset(new TestOverscrollDelegate(view_));
+ view_->overscroll_controller()->set_delegate(overscroll_delegate_.get());
+
+ view_->InitAsChild(NULL);
+ view_->SetBounds(gfx::Rect(0, 0, 400, 200));
+ view_->Show();
+
+ sink_->ClearMessages();
}
- virtual void OnWillRemoveWindowFromLayout(aura::Window* child) OVERRIDE {
+
+ // TODO(jdduke): Simulate ui::Events, injecting through the view.
+ void SimulateMouseEvent(WebInputEvent::Type type) {
+ widget_host_->ForwardMouseEvent(SyntheticWebMouseEventBuilder::Build(type));
}
- virtual void OnWindowRemovedFromLayout(aura::Window* child) OVERRIDE {
+
+ void SimulateMouseEventWithLatencyInfo(WebInputEvent::Type type,
+ const ui::LatencyInfo& ui_latency) {
+ widget_host_->ForwardMouseEventWithLatencyInfo(
+ SyntheticWebMouseEventBuilder::Build(type), ui_latency);
}
- virtual void OnChildWindowVisibilityChanged(aura::Window* child,
- bool visible) OVERRIDE {
+
+ void SimulateWheelEvent(float dX, float dY, int modifiers, bool precise) {
+ widget_host_->ForwardWheelEvent(
+ SyntheticWebMouseWheelEventBuilder::Build(dX, dY, modifiers, precise));
}
- virtual void SetChildBounds(aura::Window* child,
- const gfx::Rect& requested_bounds) OVERRIDE {
- SetChildBoundsDirect(child, gfx::Rect(owner_->bounds().size()));
+
+ void SimulateWheelEventWithLatencyInfo(float dX,
+ float dY,
+ int modifiers,
+ bool precise,
+ const ui::LatencyInfo& ui_latency) {
+ widget_host_->ForwardWheelEventWithLatencyInfo(
+ SyntheticWebMouseWheelEventBuilder::Build(dX, dY, modifiers, precise),
+ ui_latency);
+ }
+
+ void SimulateMouseMove(int x, int y, int modifiers) {
+ SimulateMouseEvent(WebInputEvent::MouseMove, x, y, modifiers, false);
+ }
+
+ void SimulateMouseEvent(WebInputEvent::Type type,
+ int x,
+ int y,
+ int modifiers,
+ bool pressed) {
+ WebMouseEvent event =
+ SyntheticWebMouseEventBuilder::Build(type, x, y, modifiers);
+ if (pressed)
+ event.button = WebMouseEvent::ButtonLeft;
+ widget_host_->ForwardMouseEvent(event);
+ }
+
+ void SimulateWheelEventWithPhase(WebMouseWheelEvent::Phase phase) {
+ widget_host_->ForwardWheelEvent(
+ SyntheticWebMouseWheelEventBuilder::Build(phase));
+ }
+
+ // Inject provided synthetic WebGestureEvent instance.
+ void SimulateGestureEventCore(const WebGestureEvent& gesture_event) {
+ widget_host_->ForwardGestureEvent(gesture_event);
+ }
+
+ void SimulateGestureEventCoreWithLatencyInfo(
+ const WebGestureEvent& gesture_event,
+ const ui::LatencyInfo& ui_latency) {
+ widget_host_->ForwardGestureEventWithLatencyInfo(gesture_event, ui_latency);
}
+ // Inject simple synthetic WebGestureEvent instances.
+ void SimulateGestureEvent(WebInputEvent::Type type,
+ blink::WebGestureDevice sourceDevice) {
+ SimulateGestureEventCore(
+ SyntheticWebGestureEventBuilder::Build(type, sourceDevice));
+ }
+
+ void SimulateGestureEventWithLatencyInfo(WebInputEvent::Type type,
+ blink::WebGestureDevice sourceDevice,
+ const ui::LatencyInfo& ui_latency) {
+ SimulateGestureEventCoreWithLatencyInfo(
+ SyntheticWebGestureEventBuilder::Build(type, sourceDevice), ui_latency);
+ }
+
+ void SimulateGestureScrollUpdateEvent(float dX, float dY, int modifiers) {
+ SimulateGestureEventCore(
+ SyntheticWebGestureEventBuilder::BuildScrollUpdate(dX, dY, modifiers));
+ }
+
+ void SimulateGesturePinchUpdateEvent(float scale,
+ float anchorX,
+ float anchorY,
+ int modifiers) {
+ SimulateGestureEventCore(SyntheticWebGestureEventBuilder::BuildPinchUpdate(
+ scale,
+ anchorX,
+ anchorY,
+ modifiers,
+ blink::WebGestureDeviceTouchscreen));
+ }
+
+ // Inject synthetic GestureFlingStart events.
+ void SimulateGestureFlingStartEvent(float velocityX,
+ float velocityY,
+ blink::WebGestureDevice sourceDevice) {
+ SimulateGestureEventCore(SyntheticWebGestureEventBuilder::BuildFling(
+ velocityX, velocityY, sourceDevice));
+ }
+
+ void SendInputEventACK(WebInputEvent::Type type,
+ InputEventAckState ack_result) {
+ InputHostMsg_HandleInputEvent_ACK_Params ack;
+ ack.type = type;
+ ack.state = ack_result;
+ InputHostMsg_HandleInputEvent_ACK response(0, ack);
+ widget_host_->OnMessageReceived(response);
+ }
+
+ bool ScrollStateIsContentScrolling() const {
+ return scroll_state() == OverscrollController::STATE_CONTENT_SCROLLING;
+ }
+
+ bool ScrollStateIsOverscrolling() const {
+ return scroll_state() == OverscrollController::STATE_OVERSCROLLING;
+ }
+
+ bool ScrollStateIsUnknown() const {
+ return scroll_state() == OverscrollController::STATE_UNKNOWN;
+ }
+
+ OverscrollController::ScrollState scroll_state() const {
+ return view_->overscroll_controller()->scroll_state_;
+ }
+
+ OverscrollMode overscroll_mode() const {
+ return view_->overscroll_controller()->overscroll_mode_;
+ }
+
+ float overscroll_delta_x() const {
+ return view_->overscroll_controller()->overscroll_delta_x_;
+ }
+
+ float overscroll_delta_y() const {
+ return view_->overscroll_controller()->overscroll_delta_y_;
+ }
+
+ TestOverscrollDelegate* overscroll_delegate() {
+ return overscroll_delegate_.get();
+ }
+
+ void SendTouchEvent() {
+ widget_host_->ForwardTouchEventWithLatencyInfo(touch_event_,
+ ui::LatencyInfo());
+ touch_event_.ResetPoints();
+ }
+
+ void PressTouchPoint(int x, int y) {
+ touch_event_.PressPoint(x, y);
+ SendTouchEvent();
+ }
+
+ void MoveTouchPoint(int index, int x, int y) {
+ touch_event_.MovePoint(index, x, y);
+ SendTouchEvent();
+ }
+
+ void ReleaseTouchPoint(int index) {
+ touch_event_.ReleasePoint(index);
+ SendTouchEvent();
+ }
+
+ size_t GetSentMessageCountAndResetSink() {
+ size_t count = sink_->message_count();
+ sink_->ClearMessages();
+ return count;
+ }
+
+ void AckLastSentInputEventIfNecessary(InputEventAckState ack_result) {
+ if (!sink_->message_count())
+ return;
+
+ InputMsg_HandleInputEvent::Param params;
+ if (!InputMsg_HandleInputEvent::Read(
+ sink_->GetMessageAt(sink_->message_count() - 1), &params)) {
+ return;
+ }
+
+ if (WebInputEventTraits::IgnoresAckDisposition(*params.a))
+ return;
+
+ SendInputEventACK(params.a->type, ack_result);
+ }
+
+ SyntheticWebTouchEvent touch_event_;
+
+ scoped_ptr<TestOverscrollDelegate> overscroll_delegate_;
+
private:
- aura::Window* owner_;
- DISALLOW_COPY_AND_ASSIGN(FullscreenLayoutManager);
+ DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewAuraOverscrollTest);
};
-class MockWindowObserver : public aura::WindowObserver {
+class RenderWidgetHostViewAuraShutdownTest
+ : public RenderWidgetHostViewAuraTest {
public:
- MOCK_METHOD2(OnWindowPaintScheduled, void(aura::Window*, const gfx::Rect&));
-};
+ RenderWidgetHostViewAuraShutdownTest() {}
-} // namespace
+ virtual void TearDown() OVERRIDE {
+ // No TearDownEnvironment here, we do this explicitly during the test.
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewAuraShutdownTest);
+};
// Checks that a fullscreen view has the correct show-state and receives the
// focus.
@@ -331,7 +685,7 @@ TEST_F(RenderWidgetHostViewAuraTest, DestroyFullscreenOnBlur) {
TestWindowObserver observer(window);
aura::test::TestWindowDelegate delegate;
scoped_ptr<aura::Window> sibling(new aura::Window(&delegate));
- sibling->Init(ui::LAYER_TEXTURED);
+ sibling->Init(aura::WINDOW_LAYER_TEXTURED);
sibling->Show();
window->parent()->AddChild(sibling.get());
sibling->Focus();
@@ -342,21 +696,75 @@ TEST_F(RenderWidgetHostViewAuraTest, DestroyFullscreenOnBlur) {
view_ = NULL;
}
+// Checks that a popup view is destroyed when a user clicks outside of the popup
+// view and focus does not change. This is the case when the user clicks on the
+// desktop background on Chrome OS.
+TEST_F(RenderWidgetHostViewAuraTest, DestroyPopupClickOutsidePopup) {
+ parent_view_->SetBounds(gfx::Rect(10, 10, 400, 400));
+ parent_view_->Focus();
+ EXPECT_TRUE(parent_view_->HasFocus());
+
+ view_->InitAsPopup(parent_view_, gfx::Rect(10, 10, 100, 100));
+ aura::Window* window = view_->GetNativeView();
+ ASSERT_TRUE(window != NULL);
+
+ gfx::Point click_point;
+ EXPECT_FALSE(window->GetBoundsInRootWindow().Contains(click_point));
+ aura::Window* parent_window = parent_view_->GetNativeView();
+ EXPECT_FALSE(parent_window->GetBoundsInRootWindow().Contains(click_point));
+
+ TestWindowObserver observer(window);
+ aura::test::EventGenerator generator(window->GetRootWindow(), click_point);
+ generator.ClickLeftButton();
+ ASSERT_TRUE(parent_view_->HasFocus());
+ ASSERT_TRUE(observer.destroyed());
+
+ widget_host_ = NULL;
+ view_ = NULL;
+}
+
+// Checks that a popup view is destroyed when a user taps outside of the popup
+// view and focus does not change. This is the case when the user taps the
+// desktop background on Chrome OS.
+TEST_F(RenderWidgetHostViewAuraTest, DestroyPopupTapOutsidePopup) {
+ parent_view_->SetBounds(gfx::Rect(10, 10, 400, 400));
+ parent_view_->Focus();
+ EXPECT_TRUE(parent_view_->HasFocus());
+
+ view_->InitAsPopup(parent_view_, gfx::Rect(10, 10, 100, 100));
+ aura::Window* window = view_->GetNativeView();
+ ASSERT_TRUE(window != NULL);
+
+ gfx::Point tap_point;
+ EXPECT_FALSE(window->GetBoundsInRootWindow().Contains(tap_point));
+ aura::Window* parent_window = parent_view_->GetNativeView();
+ EXPECT_FALSE(parent_window->GetBoundsInRootWindow().Contains(tap_point));
+
+ TestWindowObserver observer(window);
+ aura::test::EventGenerator generator(window->GetRootWindow(), tap_point);
+ generator.GestureTapAt(tap_point);
+ ASSERT_TRUE(parent_view_->HasFocus());
+ ASSERT_TRUE(observer.destroyed());
+
+ widget_host_ = NULL;
+ view_ = NULL;
+}
+
// Checks that IME-composition-event state is maintained correctly.
TEST_F(RenderWidgetHostViewAuraTest, SetCompositionText) {
view_->InitAsChild(NULL);
view_->Show();
ui::CompositionText composition_text;
- composition_text.text = ASCIIToUTF16("|a|b");
+ composition_text.text = base::ASCIIToUTF16("|a|b");
// Focused segment
composition_text.underlines.push_back(
- ui::CompositionUnderline(0, 3, 0xff000000, true));
+ ui::CompositionUnderline(0, 3, 0xff000000, true, 0x78563412));
- // Non-focused segment
+ // Non-focused segment, with different background color.
composition_text.underlines.push_back(
- ui::CompositionUnderline(3, 4, 0xff000000, false));
+ ui::CompositionUnderline(3, 4, 0xff000000, false, 0xefcdab90));
const ui::CompositionUnderlines& underlines = composition_text.underlines;
@@ -382,6 +790,7 @@ TEST_F(RenderWidgetHostViewAuraTest, SetCompositionText) {
EXPECT_EQ(underlines[i].end_offset, params.b[i].endOffset);
EXPECT_EQ(underlines[i].color, params.b[i].color);
EXPECT_EQ(underlines[i].thick, params.b[i].thick);
+ EXPECT_EQ(underlines[i].background_color, params.b[i].backgroundColor);
}
// highlighted range
EXPECT_EQ(4, params.c) << "Should be the same to the caret pos";
@@ -417,6 +826,7 @@ TEST_F(RenderWidgetHostViewAuraTest, TouchEventState) {
view_->OnTouchEvent(&press);
EXPECT_FALSE(press.handled());
EXPECT_EQ(blink::WebInputEvent::TouchStart, view_->touch_event_.type);
+ EXPECT_TRUE(view_->touch_event_.cancelable);
EXPECT_EQ(1U, view_->touch_event_.touchesLength);
EXPECT_EQ(blink::WebTouchPoint::StatePressed,
view_->touch_event_.touches[0].state);
@@ -424,6 +834,7 @@ TEST_F(RenderWidgetHostViewAuraTest, TouchEventState) {
view_->OnTouchEvent(&move);
EXPECT_FALSE(move.handled());
EXPECT_EQ(blink::WebInputEvent::TouchMove, view_->touch_event_.type);
+ EXPECT_TRUE(view_->touch_event_.cancelable);
EXPECT_EQ(1U, view_->touch_event_.touchesLength);
EXPECT_EQ(blink::WebTouchPoint::StateMoved,
view_->touch_event_.touches[0].state);
@@ -431,6 +842,7 @@ TEST_F(RenderWidgetHostViewAuraTest, TouchEventState) {
view_->OnTouchEvent(&release);
EXPECT_FALSE(release.handled());
EXPECT_EQ(blink::WebInputEvent::TouchEnd, view_->touch_event_.type);
+ EXPECT_TRUE(view_->touch_event_.cancelable);
EXPECT_EQ(0U, view_->touch_event_.touchesLength);
// Now install some touch-event handlers and do the same steps. The touch
@@ -442,6 +854,7 @@ TEST_F(RenderWidgetHostViewAuraTest, TouchEventState) {
view_->OnTouchEvent(&press);
EXPECT_TRUE(press.stopped_propagation());
EXPECT_EQ(blink::WebInputEvent::TouchStart, view_->touch_event_.type);
+ EXPECT_TRUE(view_->touch_event_.cancelable);
EXPECT_EQ(1U, view_->touch_event_.touchesLength);
EXPECT_EQ(blink::WebTouchPoint::StatePressed,
view_->touch_event_.touches[0].state);
@@ -449,6 +862,7 @@ TEST_F(RenderWidgetHostViewAuraTest, TouchEventState) {
view_->OnTouchEvent(&move);
EXPECT_TRUE(move.stopped_propagation());
EXPECT_EQ(blink::WebInputEvent::TouchMove, view_->touch_event_.type);
+ EXPECT_TRUE(view_->touch_event_.cancelable);
EXPECT_EQ(1U, view_->touch_event_.touchesLength);
EXPECT_EQ(blink::WebTouchPoint::StateMoved,
view_->touch_event_.touches[0].state);
@@ -456,6 +870,7 @@ TEST_F(RenderWidgetHostViewAuraTest, TouchEventState) {
view_->OnTouchEvent(&release);
EXPECT_TRUE(release.stopped_propagation());
EXPECT_EQ(blink::WebInputEvent::TouchEnd, view_->touch_event_.type);
+ EXPECT_TRUE(view_->touch_event_.cancelable);
EXPECT_EQ(0U, view_->touch_event_.touchesLength);
// Now start a touch event, and remove the event-handlers before the release.
@@ -678,33 +1093,49 @@ TEST_F(RenderWidgetHostViewAuraTest, CursorVisibilityChange) {
cursor_client.RemoveObserver(view_);
}
-scoped_ptr<cc::CompositorFrame> MakeGLFrame(float scale_factor,
- gfx::Size size,
- gfx::Rect damage) {
- scoped_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame);
- frame->metadata.device_scale_factor = scale_factor;
- frame->gl_frame_data.reset(new cc::GLFrameData);
- frame->gl_frame_data->sync_point = 1;
- memset(frame->gl_frame_data->mailbox.name, '1', 64);
- frame->gl_frame_data->size = size;
- frame->gl_frame_data->sub_buffer_rect = damage;
- return frame.Pass();
-}
+TEST_F(RenderWidgetHostViewAuraTest, UpdateCursorIfOverSelf) {
+ view_->InitAsChild(NULL);
+ aura::client::ParentWindowWithContext(
+ view_->GetNativeView(),
+ parent_view_->GetNativeView()->GetRootWindow(),
+ gfx::Rect());
-scoped_ptr<cc::CompositorFrame> MakeSoftwareFrame(float scale_factor,
- gfx::Size size,
- gfx::Rect damage) {
- scoped_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame);
- frame->metadata.device_scale_factor = scale_factor;
- frame->software_frame_data.reset(new cc::SoftwareFrameData);
- frame->software_frame_data->id = 1;
- frame->software_frame_data->size = size;
- frame->software_frame_data->damage_rect = damage;
- base::SharedMemory shm;
- shm.CreateAndMapAnonymous(size.GetArea() * 4);
- shm.GiveToProcess(base::GetCurrentProcessHandle(),
- &frame->software_frame_data->handle);
- return frame.Pass();
+ // Note that all coordinates in this test are screen coordinates.
+ view_->SetBounds(gfx::Rect(60, 60, 100, 100));
+ view_->Show();
+
+ aura::test::TestCursorClient cursor_client(
+ parent_view_->GetNativeView()->GetRootWindow());
+
+ // Cursor is in the middle of the window.
+ cursor_client.reset_calls_to_set_cursor();
+ aura::Env::GetInstance()->set_last_mouse_location(gfx::Point(110, 110));
+ view_->UpdateCursorIfOverSelf();
+ EXPECT_EQ(1, cursor_client.calls_to_set_cursor());
+
+ // Cursor is near the top of the window.
+ cursor_client.reset_calls_to_set_cursor();
+ aura::Env::GetInstance()->set_last_mouse_location(gfx::Point(80, 65));
+ view_->UpdateCursorIfOverSelf();
+ EXPECT_EQ(1, cursor_client.calls_to_set_cursor());
+
+ // Cursor is near the bottom of the window.
+ cursor_client.reset_calls_to_set_cursor();
+ aura::Env::GetInstance()->set_last_mouse_location(gfx::Point(159, 159));
+ view_->UpdateCursorIfOverSelf();
+ EXPECT_EQ(1, cursor_client.calls_to_set_cursor());
+
+ // Cursor is above the window.
+ cursor_client.reset_calls_to_set_cursor();
+ aura::Env::GetInstance()->set_last_mouse_location(gfx::Point(67, 59));
+ view_->UpdateCursorIfOverSelf();
+ EXPECT_EQ(0, cursor_client.calls_to_set_cursor());
+
+ // Cursor is below the window.
+ cursor_client.reset_calls_to_set_cursor();
+ aura::Env::GetInstance()->set_last_mouse_location(gfx::Point(161, 161));
+ view_->UpdateCursorIfOverSelf();
+ EXPECT_EQ(0, cursor_client.calls_to_set_cursor());
}
scoped_ptr<cc::CompositorFrame> MakeDelegatedFrame(float scale_factor,
@@ -715,10 +1146,8 @@ scoped_ptr<cc::CompositorFrame> MakeDelegatedFrame(float scale_factor,
frame->delegated_frame_data.reset(new cc::DelegatedFrameData);
scoped_ptr<cc::RenderPass> pass = cc::RenderPass::Create();
- pass->SetNew(cc::RenderPass::Id(1, 1),
- gfx::Rect(size),
- gfx::RectF(damage),
- gfx::Transform());
+ pass->SetNew(
+ cc::RenderPass::Id(1, 1), gfx::Rect(size), damage, gfx::Transform());
frame->delegated_frame_data->render_pass_list.push_back(pass.Pass());
return frame.Pass();
}
@@ -746,9 +1175,11 @@ TEST_F(RenderWidgetHostViewAuraTest, FullscreenResize) {
// Resizes are blocked until we swapped a frame of the correct size, and
// we've committed it.
view_->OnSwapCompositorFrame(
- 0, MakeGLFrame(1.f, params.a.new_size, gfx::Rect(params.a.new_size)));
+ 0,
+ MakeDelegatedFrame(
+ 1.f, params.a.new_size, gfx::Rect(params.a.new_size)));
ui::DrawWaiterForTest::WaitForCommit(
- root_window->GetDispatcher()->compositor());
+ root_window->GetHost()->compositor());
}
widget_host_->ResetSizeAndRepaintPendingFlags();
@@ -767,9 +1198,11 @@ TEST_F(RenderWidgetHostViewAuraTest, FullscreenResize) {
gfx::Rect(params.a.screen_info.availableRect).ToString());
EXPECT_EQ("1600x1200", params.a.new_size.ToString());
view_->OnSwapCompositorFrame(
- 0, MakeGLFrame(1.f, params.a.new_size, gfx::Rect(params.a.new_size)));
+ 0,
+ MakeDelegatedFrame(
+ 1.f, params.a.new_size, gfx::Rect(params.a.new_size)));
ui::DrawWaiterForTest::WaitForCommit(
- root_window->GetDispatcher()->compositor());
+ root_window->GetHost()->compositor());
}
}
@@ -789,78 +1222,105 @@ TEST_F(RenderWidgetHostViewAuraTest, SwapNotifiesWindow) {
MockWindowObserver observer;
view_->window_->AddObserver(&observer);
- // Swap a frame through the GPU path.
- GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params;
- params.surface_id = widget_host_->surface_id();
- params.route_id = widget_host_->GetRoutingID();
- params.mailbox_name = std::string(64, '1');
- params.size = view_size;
- params.scale_factor = 1.f;
-
- EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect));
- view_->AcceleratedSurfaceBuffersSwapped(params, 0);
- testing::Mock::VerifyAndClearExpectations(&observer);
-
- // DSF = 2
- params.size = gfx::Size(200, 200);
- params.scale_factor = 2.f;
+ // Delegated renderer path
EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect));
- view_->AcceleratedSurfaceBuffersSwapped(params, 0);
+ view_->OnSwapCompositorFrame(
+ 0, MakeDelegatedFrame(1.f, view_size, view_rect));
testing::Mock::VerifyAndClearExpectations(&observer);
- // Partial frames though GPU path
- GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params post_params;
- post_params.surface_id = widget_host_->surface_id();
- post_params.route_id = widget_host_->GetRoutingID();
- post_params.mailbox_name = std::string(64, '1');
- post_params.surface_size = gfx::Size(200, 200);
- post_params.surface_scale_factor = 2.f;
- post_params.x = 40;
- post_params.y = 40;
- post_params.width = 80;
- post_params.height = 80;
- // rect from params is upside down, and is inflated in RWHVA, just because.
EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_,
- gfx::Rect(19, 39, 42, 42)));
- view_->AcceleratedSurfacePostSubBuffer(post_params, 0);
+ gfx::Rect(5, 5, 5, 5)));
+ view_->OnSwapCompositorFrame(
+ 0, MakeDelegatedFrame(1.f, view_size, gfx::Rect(5, 5, 5, 5)));
testing::Mock::VerifyAndClearExpectations(&observer);
- // Composite-to-mailbox path
- EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect));
- view_->OnSwapCompositorFrame(0, MakeGLFrame(1.f, view_size, view_rect));
- testing::Mock::VerifyAndClearExpectations(&observer);
+ view_->window_->RemoveObserver(&observer);
+}
- // rect from GL frame is upside down, and is inflated in RWHVA, just because.
- EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_,
- gfx::Rect(4, 89, 7, 7)));
+TEST_F(RenderWidgetHostViewAuraTest, Resize) {
+ gfx::Size size1(100, 100);
+ gfx::Size size2(200, 200);
+ gfx::Size size3(300, 300);
+
+ aura::Window* root_window = parent_view_->GetNativeView()->GetRootWindow();
+ view_->InitAsChild(NULL);
+ aura::client::ParentWindowWithContext(
+ view_->GetNativeView(), root_window, gfx::Rect(size1));
+ view_->WasShown();
+ view_->SetSize(size1);
view_->OnSwapCompositorFrame(
- 0, MakeGLFrame(1.f, view_size, gfx::Rect(5, 5, 5, 5)));
- testing::Mock::VerifyAndClearExpectations(&observer);
+ 0, MakeDelegatedFrame(1.f, size1, gfx::Rect(size1)));
+ ui::DrawWaiterForTest::WaitForCommit(
+ root_window->GetHost()->compositor());
+ ViewHostMsg_UpdateRect_Params update_params;
+ update_params.view_size = size1;
+ update_params.scale_factor = 1.f;
+ update_params.flags = ViewHostMsg_UpdateRect_Flags::IS_RESIZE_ACK;
+ widget_host_->OnMessageReceived(
+ ViewHostMsg_UpdateRect(widget_host_->GetRoutingID(), update_params));
+ sink_->ClearMessages();
+ // Resize logic is idle (no pending resize, no pending commit).
+ EXPECT_EQ(size1.ToString(), view_->GetRequestedRendererSize().ToString());
- // Software path
- EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect));
- view_->OnSwapCompositorFrame(0, MakeSoftwareFrame(1.f, view_size, view_rect));
- testing::Mock::VerifyAndClearExpectations(&observer);
+ // Resize renderer, should produce a Resize message
+ view_->SetSize(size2);
+ EXPECT_EQ(size2.ToString(), view_->GetRequestedRendererSize().ToString());
+ EXPECT_EQ(1u, sink_->message_count());
+ {
+ const IPC::Message* msg = sink_->GetMessageAt(0);
+ EXPECT_EQ(ViewMsg_Resize::ID, msg->type());
+ ViewMsg_Resize::Param params;
+ ViewMsg_Resize::Read(msg, &params);
+ EXPECT_EQ(size2.ToString(), params.a.new_size.ToString());
+ }
+ // Send resize ack to observe new Resize messages.
+ update_params.view_size = size2;
+ widget_host_->OnMessageReceived(
+ ViewHostMsg_UpdateRect(widget_host_->GetRoutingID(), update_params));
+ sink_->ClearMessages();
- EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_,
- gfx::Rect(5, 5, 5, 5)));
- view_->OnSwapCompositorFrame(
- 0, MakeSoftwareFrame(1.f, view_size, gfx::Rect(5, 5, 5, 5)));
- testing::Mock::VerifyAndClearExpectations(&observer);
+ // Resize renderer again, before receiving a frame. Should not produce a
+ // Resize message.
+ view_->SetSize(size3);
+ EXPECT_EQ(size2.ToString(), view_->GetRequestedRendererSize().ToString());
+ EXPECT_EQ(0u, sink_->message_count());
- // Delegated renderer path
- EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect));
+ // Receive a frame of the new size, should be skipped and not produce a Resize
+ // message.
view_->OnSwapCompositorFrame(
- 0, MakeDelegatedFrame(1.f, view_size, view_rect));
- testing::Mock::VerifyAndClearExpectations(&observer);
+ 0, MakeDelegatedFrame(1.f, size3, gfx::Rect(size3)));
+ // Expect the frame ack;
+ EXPECT_EQ(1u, sink_->message_count());
+ EXPECT_EQ(ViewMsg_SwapCompositorFrameAck::ID, sink_->GetMessageAt(0)->type());
+ sink_->ClearMessages();
+ EXPECT_EQ(size2.ToString(), view_->GetRequestedRendererSize().ToString());
- EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_,
- gfx::Rect(5, 5, 5, 5)));
+ // Receive a frame of the correct size, should not be skipped and, and should
+ // produce a Resize message after the commit.
view_->OnSwapCompositorFrame(
- 0, MakeDelegatedFrame(1.f, view_size, gfx::Rect(5, 5, 5, 5)));
- testing::Mock::VerifyAndClearExpectations(&observer);
-
- view_->window_->RemoveObserver(&observer);
+ 0, MakeDelegatedFrame(1.f, size2, gfx::Rect(size2)));
+ // No frame ack yet.
+ EXPECT_EQ(0u, sink_->message_count());
+ EXPECT_EQ(size2.ToString(), view_->GetRequestedRendererSize().ToString());
+
+ // Wait for commit, then we should unlock the compositor and send a Resize
+ // message (and a frame ack)
+ ui::DrawWaiterForTest::WaitForCommit(
+ root_window->GetHost()->compositor());
+ EXPECT_EQ(size3.ToString(), view_->GetRequestedRendererSize().ToString());
+ EXPECT_EQ(2u, sink_->message_count());
+ EXPECT_EQ(ViewMsg_SwapCompositorFrameAck::ID, sink_->GetMessageAt(0)->type());
+ {
+ const IPC::Message* msg = sink_->GetMessageAt(1);
+ EXPECT_EQ(ViewMsg_Resize::ID, msg->type());
+ ViewMsg_Resize::Param params;
+ ViewMsg_Resize::Read(msg, &params);
+ EXPECT_EQ(size3.ToString(), params.a.new_size.ToString());
+ }
+ update_params.view_size = size3;
+ widget_host_->OnMessageReceived(
+ ViewHostMsg_UpdateRect(widget_host_->GetRoutingID(), update_params));
+ sink_->ClearMessages();
}
// Skipped frames should not drop their damage.
@@ -897,7 +1357,6 @@ TEST_F(RenderWidgetHostViewAuraTest, SkippedDelegatedFrames) {
// Lock the compositor. Now we should drop frames.
view_rect = gfx::Rect(150, 150);
view_->SetSize(view_rect.size());
- view_->MaybeCreateResizeLock();
// This frame is dropped.
gfx::Rect dropped_damage_rect_1(10, 20, 30, 40);
@@ -934,6 +1393,21 @@ TEST_F(RenderWidgetHostViewAuraTest, SkippedDelegatedFrames) {
view_->RunOnCompositingDidCommit();
+ // Resize to something empty.
+ view_rect = gfx::Rect(100, 0);
+ view_->SetSize(view_rect.size());
+
+ // We're never expecting empty frames, resize to something non-empty.
+ view_rect = gfx::Rect(100, 100);
+ view_->SetSize(view_rect.size());
+
+ // This frame should not be dropped.
+ EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect));
+ view_->OnSwapCompositorFrame(
+ 0, MakeDelegatedFrame(1.f, view_rect.size(), view_rect));
+ testing::Mock::VerifyAndClearExpectations(&observer);
+ view_->RunOnCompositingDidCommit();
+
view_->window_->RemoveObserver(&observer);
}
@@ -988,6 +1462,7 @@ TEST_F(RenderWidgetHostViewAuraTest, DiscardDelegatedFrames) {
size_t renderer_count = max_renderer_frames + 1;
gfx::Rect view_rect(100, 100);
gfx::Size frame_size = view_rect.size();
+ DCHECK_EQ(0u, HostSharedBitmapManager::current()->AllocatedBitmapCount());
scoped_ptr<RenderWidgetHostImpl * []> hosts(
new RenderWidgetHostImpl* [renderer_count]);
@@ -999,8 +1474,6 @@ TEST_F(RenderWidgetHostViewAuraTest, DiscardDelegatedFrames) {
hosts[i] = new RenderWidgetHostImpl(
&delegate_, process_host_, MSG_ROUTING_NONE, false);
hosts[i]->Init();
- hosts[i]->OnMessageReceived(
- ViewHostMsg_DidActivateAcceleratedCompositing(0, true));
views[i] = new FakeRenderWidgetHostViewAura(hosts[i]);
views[i]->InitAsChild(NULL);
aura::client::ParentWindowWithContext(
@@ -1015,37 +1488,37 @@ TEST_F(RenderWidgetHostViewAuraTest, DiscardDelegatedFrames) {
views[i]->WasShown();
views[i]->OnSwapCompositorFrame(
1, MakeDelegatedFrame(1.f, frame_size, view_rect));
- EXPECT_TRUE(views[i]->frame_provider_);
+ EXPECT_TRUE(views[i]->frame_provider());
views[i]->WasHidden();
}
// There should be max_renderer_frames with a frame in it, and one without it.
// Since the logic is LRU eviction, the first one should be without.
- EXPECT_FALSE(views[0]->frame_provider_);
+ EXPECT_FALSE(views[0]->frame_provider());
for (size_t i = 1; i < renderer_count; ++i)
- EXPECT_TRUE(views[i]->frame_provider_);
+ EXPECT_TRUE(views[i]->frame_provider());
// LRU renderer is [0], make it visible, it shouldn't evict anything yet.
views[0]->WasShown();
- EXPECT_FALSE(views[0]->frame_provider_);
- EXPECT_TRUE(views[1]->frame_provider_);
+ EXPECT_FALSE(views[0]->frame_provider());
+ EXPECT_TRUE(views[1]->frame_provider());
// Swap a frame on it, it should evict the next LRU [1].
views[0]->OnSwapCompositorFrame(
1, MakeDelegatedFrame(1.f, frame_size, view_rect));
- EXPECT_TRUE(views[0]->frame_provider_);
- EXPECT_FALSE(views[1]->frame_provider_);
+ EXPECT_TRUE(views[0]->frame_provider());
+ EXPECT_FALSE(views[1]->frame_provider());
views[0]->WasHidden();
// LRU renderer is [1], still hidden. Swap a frame on it, it should evict
// the next LRU [2].
views[1]->OnSwapCompositorFrame(
1, MakeDelegatedFrame(1.f, frame_size, view_rect));
- EXPECT_TRUE(views[0]->frame_provider_);
- EXPECT_TRUE(views[1]->frame_provider_);
- EXPECT_FALSE(views[2]->frame_provider_);
+ EXPECT_TRUE(views[0]->frame_provider());
+ EXPECT_TRUE(views[1]->frame_provider());
+ EXPECT_FALSE(views[2]->frame_provider());
for (size_t i = 3; i < renderer_count; ++i)
- EXPECT_TRUE(views[i]->frame_provider_);
+ EXPECT_TRUE(views[i]->frame_provider());
// Make all renderers but [0] visible and swap a frame on them, keep [0]
// hidden, it becomes the LRU.
@@ -1053,14 +1526,14 @@ TEST_F(RenderWidgetHostViewAuraTest, DiscardDelegatedFrames) {
views[i]->WasShown();
views[i]->OnSwapCompositorFrame(
1, MakeDelegatedFrame(1.f, frame_size, view_rect));
- EXPECT_TRUE(views[i]->frame_provider_);
+ EXPECT_TRUE(views[i]->frame_provider());
}
- EXPECT_FALSE(views[0]->frame_provider_);
+ EXPECT_FALSE(views[0]->frame_provider());
// Swap a frame on [0], it should be evicted immediately.
views[0]->OnSwapCompositorFrame(
1, MakeDelegatedFrame(1.f, frame_size, view_rect));
- EXPECT_FALSE(views[0]->frame_provider_);
+ EXPECT_FALSE(views[0]->frame_provider());
// Make [0] visible, and swap a frame on it. Nothing should be evicted
// although we're above the limit.
@@ -1068,11 +1541,100 @@ TEST_F(RenderWidgetHostViewAuraTest, DiscardDelegatedFrames) {
views[0]->OnSwapCompositorFrame(
1, MakeDelegatedFrame(1.f, frame_size, view_rect));
for (size_t i = 0; i < renderer_count; ++i)
- EXPECT_TRUE(views[i]->frame_provider_);
+ EXPECT_TRUE(views[i]->frame_provider());
// Make [0] hidden, it should evict its frame.
views[0]->WasHidden();
- EXPECT_FALSE(views[0]->frame_provider_);
+ EXPECT_FALSE(views[0]->frame_provider());
+
+ for (size_t i = 0; i < renderer_count - 1; ++i)
+ views[i]->WasHidden();
+
+ // Allocate enough bitmaps so that two frames (proportionally) would be
+ // enough hit the handle limit.
+ int handles_per_frame = 5;
+ RendererFrameManager::GetInstance()->set_max_handles(handles_per_frame * 2);
+
+ for (size_t i = 0; i < (renderer_count - 1) * handles_per_frame; i++) {
+ HostSharedBitmapManager::current()->ChildAllocatedSharedBitmap(
+ 1,
+ base::SharedMemory::NULLHandle(),
+ base::GetCurrentProcessHandle(),
+ cc::SharedBitmap::GenerateId());
+ }
+
+ // Hiding this last bitmap should evict all but two frames.
+ views[renderer_count - 1]->WasHidden();
+ for (size_t i = 0; i < renderer_count; ++i) {
+ if (i + 2 < renderer_count)
+ EXPECT_FALSE(views[i]->frame_provider());
+ else
+ EXPECT_TRUE(views[i]->frame_provider());
+ }
+ HostSharedBitmapManager::current()->ProcessRemoved(
+ base::GetCurrentProcessHandle());
+ RendererFrameManager::GetInstance()->set_max_handles(
+ base::SharedMemory::GetHandleLimit());
+
+ for (size_t i = 0; i < renderer_count; ++i) {
+ views[i]->Destroy();
+ delete hosts[i];
+ }
+}
+
+TEST_F(RenderWidgetHostViewAuraTest, DiscardDelegatedFramesWithLocking) {
+ size_t max_renderer_frames =
+ RendererFrameManager::GetInstance()->max_number_of_saved_frames();
+ ASSERT_LE(2u, max_renderer_frames);
+ size_t renderer_count = max_renderer_frames + 1;
+ gfx::Rect view_rect(100, 100);
+ gfx::Size frame_size = view_rect.size();
+ DCHECK_EQ(0u, HostSharedBitmapManager::current()->AllocatedBitmapCount());
+
+ scoped_ptr<RenderWidgetHostImpl * []> hosts(
+ new RenderWidgetHostImpl* [renderer_count]);
+ scoped_ptr<FakeRenderWidgetHostViewAura * []> views(
+ new FakeRenderWidgetHostViewAura* [renderer_count]);
+
+ // Create a bunch of renderers.
+ for (size_t i = 0; i < renderer_count; ++i) {
+ hosts[i] = new RenderWidgetHostImpl(
+ &delegate_, process_host_, MSG_ROUTING_NONE, false);
+ hosts[i]->Init();
+ views[i] = new FakeRenderWidgetHostViewAura(hosts[i]);
+ views[i]->InitAsChild(NULL);
+ aura::client::ParentWindowWithContext(
+ views[i]->GetNativeView(),
+ parent_view_->GetNativeView()->GetRootWindow(),
+ gfx::Rect());
+ views[i]->SetSize(view_rect.size());
+ }
+
+ // Make each renderer visible and swap a frame on it. No eviction should
+ // occur because all frames are visible.
+ for (size_t i = 0; i < renderer_count; ++i) {
+ views[i]->WasShown();
+ views[i]->OnSwapCompositorFrame(
+ 1, MakeDelegatedFrame(1.f, frame_size, view_rect));
+ EXPECT_TRUE(views[i]->frame_provider());
+ }
+
+ // If we hide [0], then [0] should be evicted.
+ views[0]->WasHidden();
+ EXPECT_FALSE(views[0]->frame_provider());
+
+ // If we lock [0] before hiding it, then [0] should not be evicted.
+ views[0]->WasShown();
+ views[0]->OnSwapCompositorFrame(
+ 1, MakeDelegatedFrame(1.f, frame_size, view_rect));
+ EXPECT_TRUE(views[0]->frame_provider());
+ views[0]->GetDelegatedFrameHost()->LockResources();
+ views[0]->WasHidden();
+ EXPECT_TRUE(views[0]->frame_provider());
+
+ // If we unlock [0] now, then [0] should be evicted.
+ views[0]->GetDelegatedFrameHost()->UnlockResources();
+ EXPECT_FALSE(views[0]->frame_provider());
for (size_t i = 0; i < renderer_count; ++i) {
views[i]->Destroy();
@@ -1098,7 +1660,7 @@ TEST_F(RenderWidgetHostViewAuraTest, SoftwareDPIChange) {
// Save the frame provider.
scoped_refptr<cc::DelegatedFrameProvider> frame_provider =
- view_->frame_provider_;
+ view_->frame_provider();
// This frame will have the same number of physical pixels, but has a new
// scale on it.
@@ -1108,7 +1670,1016 @@ TEST_F(RenderWidgetHostViewAuraTest, SoftwareDPIChange) {
// When we get a new frame with the same frame size in physical pixels, but a
// different scale, we should generate a new frame provider, as the final
// result will need to be scaled differently to the screen.
- EXPECT_NE(frame_provider.get(), view_->frame_provider_.get());
+ EXPECT_NE(frame_provider.get(), view_->frame_provider());
+}
+
+class RenderWidgetHostViewAuraCopyRequestTest
+ : public RenderWidgetHostViewAuraShutdownTest {
+ public:
+ RenderWidgetHostViewAuraCopyRequestTest()
+ : callback_count_(0), result_(false) {}
+
+ void CallbackMethod(const base::Closure& quit_closure, bool result) {
+ result_ = result;
+ callback_count_++;
+ quit_closure.Run();
+ }
+
+ int callback_count_;
+ bool result_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewAuraCopyRequestTest);
+};
+
+TEST_F(RenderWidgetHostViewAuraCopyRequestTest, DestroyedAfterCopyRequest) {
+ base::RunLoop run_loop;
+
+ gfx::Rect view_rect(100, 100);
+ scoped_ptr<cc::CopyOutputRequest> request;
+
+ view_->InitAsChild(NULL);
+ aura::client::ParentWindowWithContext(
+ view_->GetNativeView(),
+ parent_view_->GetNativeView()->GetRootWindow(),
+ gfx::Rect());
+ view_->SetSize(view_rect.size());
+ view_->WasShown();
+
+ scoped_ptr<FakeFrameSubscriber> frame_subscriber(new FakeFrameSubscriber(
+ view_rect.size(),
+ base::Bind(&RenderWidgetHostViewAuraCopyRequestTest::CallbackMethod,
+ base::Unretained(this),
+ run_loop.QuitClosure())));
+
+ EXPECT_EQ(0, callback_count_);
+ EXPECT_FALSE(view_->last_copy_request_);
+
+ view_->BeginFrameSubscription(
+ frame_subscriber.PassAs<RenderWidgetHostViewFrameSubscriber>());
+ view_->OnSwapCompositorFrame(
+ 1, MakeDelegatedFrame(1.f, view_rect.size(), gfx::Rect(view_rect)));
+
+ EXPECT_EQ(0, callback_count_);
+ EXPECT_TRUE(view_->last_copy_request_);
+ EXPECT_TRUE(view_->last_copy_request_->has_texture_mailbox());
+ request = view_->last_copy_request_.Pass();
+
+ // Send back the mailbox included in the request. There's no release callback
+ // since the mailbox came from the RWHVA originally.
+ request->SendTextureResult(view_rect.size(),
+ request->texture_mailbox(),
+ scoped_ptr<cc::SingleReleaseCallback>());
+
+ // This runs until the callback happens.
+ run_loop.Run();
+
+ // The callback should succeed.
+ EXPECT_EQ(1, callback_count_);
+ EXPECT_TRUE(result_);
+
+ view_->OnSwapCompositorFrame(
+ 1, MakeDelegatedFrame(1.f, view_rect.size(), gfx::Rect(view_rect)));
+
+ EXPECT_EQ(1, callback_count_);
+ request = view_->last_copy_request_.Pass();
+
+ // Destroy the RenderWidgetHostViewAura and ImageTransportFactory.
+ TearDownEnvironment();
+
+ // Send back the mailbox included in the request. There's no release callback
+ // since the mailbox came from the RWHVA originally.
+ request->SendTextureResult(view_rect.size(),
+ request->texture_mailbox(),
+ scoped_ptr<cc::SingleReleaseCallback>());
+
+ // Because the copy request callback may be holding state within it, that
+ // state must handle the RWHVA and ImageTransportFactory going away before the
+ // callback is called. This test passes if it does not crash as a result of
+ // these things being destroyed.
+ EXPECT_EQ(2, callback_count_);
+ EXPECT_FALSE(result_);
+}
+
+TEST_F(RenderWidgetHostViewAuraTest, VisibleViewportTest) {
+ gfx::Rect view_rect(100, 100);
+
+ view_->InitAsChild(NULL);
+ aura::client::ParentWindowWithContext(
+ view_->GetNativeView(),
+ parent_view_->GetNativeView()->GetRootWindow(),
+ gfx::Rect());
+ view_->SetSize(view_rect.size());
+ view_->WasShown();
+
+ // Defaults to full height of the view.
+ EXPECT_EQ(100, view_->GetVisibleViewportSize().height());
+
+ widget_host_->ResetSizeAndRepaintPendingFlags();
+ sink_->ClearMessages();
+ view_->SetInsets(gfx::Insets(0, 0, 40, 0));
+
+ EXPECT_EQ(60, view_->GetVisibleViewportSize().height());
+
+ const IPC::Message *message = sink_->GetFirstMessageMatching(
+ ViewMsg_Resize::ID);
+ ASSERT_TRUE(message != NULL);
+
+ ViewMsg_Resize::Param params;
+ ViewMsg_Resize::Read(message, &params);
+ EXPECT_EQ(60, params.a.visible_viewport_size.height());
+}
+
+// Ensures that touch event positions are never truncated to integers.
+TEST_F(RenderWidgetHostViewAuraTest, TouchEventPositionsArentRounded) {
+ const float kX = 30.58f;
+ const float kY = 50.23f;
+
+ view_->InitAsChild(NULL);
+ view_->Show();
+
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED,
+ gfx::PointF(kX, kY),
+ 0,
+ ui::EventTimeForNow());
+
+ view_->OnTouchEvent(&press);
+ EXPECT_EQ(blink::WebInputEvent::TouchStart, view_->touch_event_.type);
+ EXPECT_TRUE(view_->touch_event_.cancelable);
+ EXPECT_EQ(1U, view_->touch_event_.touchesLength);
+ EXPECT_EQ(blink::WebTouchPoint::StatePressed,
+ view_->touch_event_.touches[0].state);
+ EXPECT_EQ(kX, view_->touch_event_.touches[0].screenPosition.x);
+ EXPECT_EQ(kX, view_->touch_event_.touches[0].position.x);
+ EXPECT_EQ(kY, view_->touch_event_.touches[0].screenPosition.y);
+ EXPECT_EQ(kY, view_->touch_event_.touches[0].position.y);
+}
+
+// Tests that scroll ACKs are correctly handled by the overscroll-navigation
+// controller.
+TEST_F(RenderWidgetHostViewAuraOverscrollTest, WheelScrollEventOverscrolls) {
+ SetUpOverscrollEnvironment();
+
+ // Simulate wheel events.
+ SimulateWheelEvent(-5, 0, 0, true); // sent directly
+ SimulateWheelEvent(-1, 1, 0, true); // enqueued
+ SimulateWheelEvent(-10, -3, 0, true); // coalesced into previous event
+ SimulateWheelEvent(-15, -1, 0, true); // coalesced into previous event
+ SimulateWheelEvent(-30, -3, 0, true); // coalesced into previous event
+ SimulateWheelEvent(-20, 6, 1, true); // enqueued, different modifiers
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Receive ACK the first wheel event as not processed.
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Receive ACK for the second (coalesced) event as not processed. This will
+ // start a back navigation. However, this will also cause the queued next
+ // event to be sent to the renderer. But since overscroll navigation has
+ // started, that event will also be included in the overscroll computation
+ // instead of being sent to the renderer. So the result will be an overscroll
+ // back navigation, and no event will be sent to the renderer.
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_WEST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_WEST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(-81.f, overscroll_delta_x());
+ EXPECT_EQ(-31.f, overscroll_delegate()->delta_x());
+ EXPECT_EQ(0.f, overscroll_delegate()->delta_y());
+ EXPECT_EQ(0U, sink_->message_count());
+
+ // Send a mouse-move event. This should cancel the overscroll navigation.
+ SimulateMouseMove(5, 10, 0);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, sink_->message_count());
+}
+
+// Tests that if some scroll events are consumed towards the start, then
+// subsequent scrolls do not horizontal overscroll.
+TEST_F(RenderWidgetHostViewAuraOverscrollTest,
+ WheelScrollConsumedDoNotHorizOverscroll) {
+ SetUpOverscrollEnvironment();
+
+ // Simulate wheel events.
+ SimulateWheelEvent(-5, 0, 0, true); // sent directly
+ SimulateWheelEvent(-1, -1, 0, true); // enqueued
+ SimulateWheelEvent(-10, -3, 0, true); // coalesced into previous event
+ SimulateWheelEvent(-15, -1, 0, true); // coalesced into previous event
+ SimulateWheelEvent(-30, -3, 0, true); // coalesced into previous event
+ SimulateWheelEvent(-20, 6, 1, true); // enqueued, different modifiers
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Receive ACK the first wheel event as processed.
+ SendInputEventACK(WebInputEvent::MouseWheel, INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Receive ACK for the second (coalesced) event as not processed. This should
+ // not initiate overscroll, since the beginning of the scroll has been
+ // consumed. The queued event with different modifiers should be sent to the
+ // renderer.
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(0U, sink_->message_count());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+
+ // Indicate the end of the scrolling from the touchpad.
+ SimulateGestureFlingStartEvent(-1200.f, 0.f, blink::WebGestureDeviceTouchpad);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Start another scroll. This time, do not consume any scroll events.
+ SimulateWheelEvent(0, -5, 0, true); // sent directly
+ SimulateWheelEvent(0, -1, 0, true); // enqueued
+ SimulateWheelEvent(-10, -3, 0, true); // coalesced into previous event
+ SimulateWheelEvent(-15, -1, 0, true); // coalesced into previous event
+ SimulateWheelEvent(-30, -3, 0, true); // coalesced into previous event
+ SimulateWheelEvent(-20, 6, 1, true); // enqueued, different modifiers
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Receive ACK for the first wheel and the subsequent coalesced event as not
+ // processed. This should start a back-overscroll.
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_WEST, overscroll_mode());
+}
+
+// Tests that wheel-scrolling correctly turns overscroll on and off.
+TEST_F(RenderWidgetHostViewAuraOverscrollTest, WheelScrollOverscrollToggle) {
+ SetUpOverscrollEnvironment();
+
+ // Send a wheel event. ACK the event as not processed. This should not
+ // initiate an overscroll gesture since it doesn't cross the threshold yet.
+ SimulateWheelEvent(10, 0, 0, true);
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Scroll some more so as to not overscroll.
+ SimulateWheelEvent(10, 0, 0, true);
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Scroll some more to initiate an overscroll.
+ SimulateWheelEvent(40, 0, 0, true);
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(60.f, overscroll_delta_x());
+ EXPECT_EQ(10.f, overscroll_delegate()->delta_x());
+ EXPECT_EQ(0.f, overscroll_delegate()->delta_y());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Scroll in the reverse direction enough to abort the overscroll.
+ SimulateWheelEvent(-20, 0, 0, true);
+ EXPECT_EQ(0U, sink_->message_count());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+
+ // Continue to scroll in the reverse direction.
+ SimulateWheelEvent(-20, 0, 0, true);
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Continue to scroll in the reverse direction enough to initiate overscroll
+ // in that direction.
+ SimulateWheelEvent(-55, 0, 0, true);
+ EXPECT_EQ(1U, sink_->message_count());
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_WEST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_WEST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(-75.f, overscroll_delta_x());
+ EXPECT_EQ(-25.f, overscroll_delegate()->delta_x());
+ EXPECT_EQ(0.f, overscroll_delegate()->delta_y());
+}
+
+TEST_F(RenderWidgetHostViewAuraOverscrollTest,
+ ScrollEventsOverscrollWithFling) {
+ SetUpOverscrollEnvironment();
+
+ // Send a wheel event. ACK the event as not processed. This should not
+ // initiate an overscroll gesture since it doesn't cross the threshold yet.
+ SimulateWheelEvent(10, 0, 0, true);
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Scroll some more so as to not overscroll.
+ SimulateWheelEvent(20, 0, 0, true);
+ EXPECT_EQ(1U, sink_->message_count());
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ sink_->ClearMessages();
+
+ // Scroll some more to initiate an overscroll.
+ SimulateWheelEvent(30, 0, 0, true);
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(60.f, overscroll_delta_x());
+ EXPECT_EQ(10.f, overscroll_delegate()->delta_x());
+ EXPECT_EQ(0.f, overscroll_delegate()->delta_y());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Send a fling start, but with a small velocity, so that the overscroll is
+ // aborted. The fling should proceed to the renderer, through the gesture
+ // event filter.
+ SimulateGestureFlingStartEvent(0.f, 0.1f, blink::WebGestureDeviceTouchpad);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(1U, sink_->message_count());
+}
+
+// Same as ScrollEventsOverscrollWithFling, but with zero velocity. Checks that
+// the zero-velocity fling does not reach the renderer.
+TEST_F(RenderWidgetHostViewAuraOverscrollTest,
+ ScrollEventsOverscrollWithZeroFling) {
+ SetUpOverscrollEnvironment();
+
+ // Send a wheel event. ACK the event as not processed. This should not
+ // initiate an overscroll gesture since it doesn't cross the threshold yet.
+ SimulateWheelEvent(10, 0, 0, true);
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Scroll some more so as to not overscroll.
+ SimulateWheelEvent(20, 0, 0, true);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+
+ // Scroll some more to initiate an overscroll.
+ SimulateWheelEvent(30, 0, 0, true);
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(60.f, overscroll_delta_x());
+ EXPECT_EQ(10.f, overscroll_delegate()->delta_x());
+ EXPECT_EQ(0.f, overscroll_delegate()->delta_y());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Send a fling start, but with a small velocity, so that the overscroll is
+ // aborted. The fling should proceed to the renderer, through the gesture
+ // event filter.
+ SimulateGestureFlingStartEvent(10.f, 0.f, blink::WebGestureDeviceTouchpad);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(1U, sink_->message_count());
+}
+
+// Tests that a fling in the opposite direction of the overscroll cancels the
+// overscroll nav instead of completing it.
+TEST_F(RenderWidgetHostViewAuraOverscrollTest, ReverseFlingCancelsOverscroll) {
+ SetUpOverscrollEnvironment();
+
+ {
+ // Start and end a gesture in the same direction without processing the
+ // gesture events in the renderer. This should initiate and complete an
+ // overscroll navigation.
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ SimulateGestureScrollUpdateEvent(300, -5, 0);
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ sink_->ClearMessages();
+
+ SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->completed_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, sink_->message_count());
+ }
+
+ {
+ // Start over, except instead of ending the gesture with ScrollEnd, end it
+ // with a FlingStart, with velocity in the reverse direction. This should
+ // initiate an overscroll navigation, but it should be cancelled because of
+ // the fling in the opposite direction.
+ overscroll_delegate()->Reset();
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ SimulateGestureScrollUpdateEvent(-300, -5, 0);
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_WEST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_WEST, overscroll_delegate()->current_mode());
+ sink_->ClearMessages();
+
+ SimulateGestureFlingStartEvent(100, 0, blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->completed_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, sink_->message_count());
+ }
+}
+
+// Tests that touch-scroll events are handled correctly by the overscroll
+// controller. This also tests that the overscroll controller and the
+// gesture-event filter play nice with each other.
+TEST_F(RenderWidgetHostViewAuraOverscrollTest, GestureScrollOverscrolls) {
+ SetUpOverscrollEnvironment();
+
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Send another gesture event and ACK as not being processed. This should
+ // initiate the navigation gesture.
+ SimulateGestureScrollUpdateEvent(55, -5, 0);
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(55.f, overscroll_delta_x());
+ EXPECT_EQ(-5.f, overscroll_delta_y());
+ EXPECT_EQ(5.f, overscroll_delegate()->delta_x());
+ EXPECT_EQ(-5.f, overscroll_delegate()->delta_y());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Send another gesture update event. This event should be consumed by the
+ // controller, and not be forwarded to the renderer. The gesture-event filter
+ // should not also receive this event.
+ SimulateGestureScrollUpdateEvent(10, -5, 0);
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(65.f, overscroll_delta_x());
+ EXPECT_EQ(-10.f, overscroll_delta_y());
+ EXPECT_EQ(15.f, overscroll_delegate()->delta_x());
+ EXPECT_EQ(-10.f, overscroll_delegate()->delta_y());
+ EXPECT_EQ(0U, sink_->message_count());
+
+ // Now send a scroll end. This should cancel the overscroll gesture, and send
+ // the event to the renderer. The gesture-event filter should receive this
+ // event.
+ SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, sink_->message_count());
+}
+
+// Tests that if the page is scrolled because of a scroll-gesture, then that
+// particular scroll sequence never generates overscroll if the scroll direction
+// is horizontal.
+TEST_F(RenderWidgetHostViewAuraOverscrollTest,
+ GestureScrollConsumedHorizontal) {
+ SetUpOverscrollEnvironment();
+
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ SimulateGestureScrollUpdateEvent(10, 0, 0);
+
+ // Start scrolling on content. ACK both events as being processed.
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ sink_->ClearMessages();
+
+ // Send another gesture event and ACK as not being processed. This should
+ // not initiate overscroll because the beginning of the scroll event did
+ // scroll some content on the page. Since there was no overscroll, the event
+ // should reach the renderer.
+ SimulateGestureScrollUpdateEvent(55, 0, 0);
+ EXPECT_EQ(1U, sink_->message_count());
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+}
+
+// Tests that the overscroll controller plays nice with touch-scrolls and the
+// gesture event filter with debounce filtering turned on.
+TEST_F(RenderWidgetHostViewAuraOverscrollTest,
+ GestureScrollDebounceOverscrolls) {
+ SetUpOverscrollEnvironmentWithDebounce(100);
+
+ // Start scrolling. Receive ACK as it being processed.
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Send update events.
+ SimulateGestureScrollUpdateEvent(25, 0, 0);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Quickly end and restart the scroll gesture. These two events should get
+ // discarded.
+ SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(0U, sink_->message_count());
+
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(0U, sink_->message_count());
+
+ // Send another update event. This should get into the queue.
+ SimulateGestureScrollUpdateEvent(30, 0, 0);
+ EXPECT_EQ(0U, sink_->message_count());
+
+ // Receive an ACK for the first scroll-update event as not being processed.
+ // This will contribute to the overscroll gesture, but not enough for the
+ // overscroll controller to start consuming gesture events. This also cause
+ // the queued gesture event to be forwarded to the renderer.
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Send another update event. This should get into the queue.
+ SimulateGestureScrollUpdateEvent(10, 0, 0);
+ EXPECT_EQ(0U, sink_->message_count());
+
+ // Receive an ACK for the second scroll-update event as not being processed.
+ // This will now initiate an overscroll. This will also cause the queued
+ // gesture event to be released. But instead of going to the renderer, it will
+ // be consumed by the overscroll controller.
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(65.f, overscroll_delta_x());
+ EXPECT_EQ(15.f, overscroll_delegate()->delta_x());
+ EXPECT_EQ(0.f, overscroll_delegate()->delta_y());
+ EXPECT_EQ(0U, sink_->message_count());
+}
+
+// Tests that the gesture debounce timer plays nice with the overscroll
+// controller.
+TEST_F(RenderWidgetHostViewAuraOverscrollTest,
+ GestureScrollDebounceTimerOverscroll) {
+ SetUpOverscrollEnvironmentWithDebounce(10);
+
+ // Start scrolling. Receive ACK as it being processed.
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Send update events.
+ SimulateGestureScrollUpdateEvent(55, 0, 0);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Send an end event. This should get in the debounce queue.
+ SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(0U, sink_->message_count());
+
+ // Receive ACK for the scroll-update event.
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(55.f, overscroll_delta_x());
+ EXPECT_EQ(5.f, overscroll_delegate()->delta_x());
+ EXPECT_EQ(0.f, overscroll_delegate()->delta_y());
+ EXPECT_EQ(0U, sink_->message_count());
+
+ // Let the timer for the debounce queue fire. That should release the queued
+ // scroll-end event. Since overscroll has started, but there hasn't been
+ // enough overscroll to complete the gesture, the overscroll controller
+ // will reset the state. The scroll-end should therefore be dispatched to the
+ // renderer, and the gesture-event-filter should await an ACK for it.
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::MessageLoop::QuitClosure(),
+ base::TimeDelta::FromMilliseconds(15));
+ base::MessageLoop::current()->Run();
+
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, sink_->message_count());
+}
+
+// Tests that when touch-events are dispatched to the renderer, the overscroll
+// gesture deals with them correctly.
+TEST_F(RenderWidgetHostViewAuraOverscrollTest, OverscrollWithTouchEvents) {
+ SetUpOverscrollEnvironmentWithDebounce(10);
+ widget_host_->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, true));
+ sink_->ClearMessages();
+
+ // The test sends an intermingled sequence of touch and gesture events.
+ PressTouchPoint(0, 1);
+ SendInputEventACK(WebInputEvent::TouchStart,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ MoveTouchPoint(0, 20, 5);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ SendInputEventACK(WebInputEvent::TouchMove,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ SimulateGestureScrollUpdateEvent(20, 0, 0);
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+
+ // Another touch move event should reach the renderer since overscroll hasn't
+ // started yet. Note that touch events sent during the scroll period may
+ // not require an ack (having been marked uncancelable).
+ MoveTouchPoint(0, 65, 10);
+ AckLastSentInputEventIfNecessary(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ SimulateGestureScrollUpdateEvent(45, 0, 0);
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(65.f, overscroll_delta_x());
+ EXPECT_EQ(15.f, overscroll_delegate()->delta_x());
+ EXPECT_EQ(0.f, overscroll_delegate()->delta_y());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Send another touch event. The page should get the touch-move event, even
+ // though overscroll has started.
+ MoveTouchPoint(0, 55, 5);
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(65.f, overscroll_delta_x());
+ EXPECT_EQ(15.f, overscroll_delegate()->delta_x());
+ EXPECT_EQ(0.f, overscroll_delegate()->delta_y());
+ AckLastSentInputEventIfNecessary(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ SimulateGestureScrollUpdateEvent(-10, 0, 0);
+ EXPECT_EQ(0U, sink_->message_count());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(55.f, overscroll_delta_x());
+ EXPECT_EQ(5.f, overscroll_delegate()->delta_x());
+ EXPECT_EQ(0.f, overscroll_delegate()->delta_y());
+
+ PressTouchPoint(255, 5);
+ AckLastSentInputEventIfNecessary(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ SimulateGestureScrollUpdateEvent(200, 0, 0);
+ EXPECT_EQ(0U, sink_->message_count());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(255.f, overscroll_delta_x());
+ EXPECT_EQ(205.f, overscroll_delegate()->delta_x());
+ EXPECT_EQ(0.f, overscroll_delegate()->delta_y());
+
+ // The touch-end/cancel event should always reach the renderer if the page has
+ // touch handlers.
+ ReleaseTouchPoint(1);
+ AckLastSentInputEventIfNecessary(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ ReleaseTouchPoint(0);
+ AckLastSentInputEventIfNecessary(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ SimulateGestureEvent(blink::WebInputEvent::GestureScrollEnd,
+ blink::WebGestureDeviceTouchscreen);
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::MessageLoop::QuitClosure(),
+ base::TimeDelta::FromMilliseconds(10));
+ base::MessageLoop::current()->Run();
+ EXPECT_EQ(1U, sink_->message_count());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->completed_mode());
+}
+
+// Tests that touch-gesture end is dispatched to the renderer at the end of a
+// touch-gesture initiated overscroll.
+TEST_F(RenderWidgetHostViewAuraOverscrollTest,
+ TouchGestureEndDispatchedAfterOverscrollComplete) {
+ SetUpOverscrollEnvironmentWithDebounce(10);
+ widget_host_->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, true));
+ sink_->ClearMessages();
+
+ // Start scrolling. Receive ACK as it being processed.
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ // The scroll begin event will have received a synthetic ack from the input
+ // router.
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+
+ // Send update events.
+ SimulateGestureScrollUpdateEvent(55, -5, 0);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(0U, sink_->message_count());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(55.f, overscroll_delta_x());
+ EXPECT_EQ(5.f, overscroll_delegate()->delta_x());
+ EXPECT_EQ(-5.f, overscroll_delegate()->delta_y());
+
+ // Send end event.
+ SimulateGestureEvent(blink::WebInputEvent::GestureScrollEnd,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(0U, sink_->message_count());
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::MessageLoop::QuitClosure(),
+ base::TimeDelta::FromMilliseconds(10));
+ base::MessageLoop::current()->Run();
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->completed_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Start scrolling. Receive ACK as it being processed.
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+
+ // Send update events.
+ SimulateGestureScrollUpdateEvent(235, -5, 0);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(0U, sink_->message_count());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(235.f, overscroll_delta_x());
+ EXPECT_EQ(185.f, overscroll_delegate()->delta_x());
+ EXPECT_EQ(-5.f, overscroll_delegate()->delta_y());
+
+ // Send end event.
+ SimulateGestureEvent(blink::WebInputEvent::GestureScrollEnd,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(0U, sink_->message_count());
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::MessageLoop::QuitClosure(),
+ base::TimeDelta::FromMilliseconds(10));
+ base::MessageLoop::current()->Run();
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->completed_mode());
+ EXPECT_EQ(1U, sink_->message_count());
+}
+
+TEST_F(RenderWidgetHostViewAuraOverscrollTest, OverscrollDirectionChange) {
+ SetUpOverscrollEnvironmentWithDebounce(100);
+
+ // Start scrolling. Receive ACK as it being processed.
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Send update events and receive ack as not consumed.
+ SimulateGestureScrollUpdateEvent(125, -5, 0);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(0U, sink_->message_count());
+
+ // Send another update event, but in the reverse direction. The overscroll
+ // controller will consume the event, and reset the overscroll mode.
+ SimulateGestureScrollUpdateEvent(-260, 0, 0);
+ EXPECT_EQ(0U, sink_->message_count());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+
+ // Since the overscroll mode has been reset, the next scroll update events
+ // should reach the renderer.
+ SimulateGestureScrollUpdateEvent(-20, 0, 0);
+ EXPECT_EQ(1U, sink_->message_count());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+}
+
+// Tests that if a mouse-move event completes the overscroll gesture, future
+// move events do reach the renderer.
+TEST_F(RenderWidgetHostViewAuraOverscrollTest, OverscrollMouseMoveCompletion) {
+ SetUpOverscrollEnvironment();
+
+ SimulateWheelEvent(5, 0, 0, true); // sent directly
+ SimulateWheelEvent(-1, 0, 0, true); // enqueued
+ SimulateWheelEvent(-10, -3, 0, true); // coalesced into previous event
+ SimulateWheelEvent(-15, -1, 0, true); // coalesced into previous event
+ SimulateWheelEvent(-30, -3, 0, true); // coalesced into previous event
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Receive ACK the first wheel event as not processed.
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Receive ACK for the second (coalesced) event as not processed. This will
+ // start an overcroll gesture.
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_WEST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_WEST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(0U, sink_->message_count());
+
+ // Send a mouse-move event. This should cancel the overscroll navigation
+ // (since the amount overscrolled is not above the threshold), and so the
+ // mouse-move should reach the renderer.
+ SimulateMouseMove(5, 10, 0);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->completed_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ SendInputEventACK(WebInputEvent::MouseMove,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+
+ // Moving the mouse more should continue to send the events to the renderer.
+ SimulateMouseMove(5, 10, 0);
+ SendInputEventACK(WebInputEvent::MouseMove,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Now try with gestures.
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ SimulateGestureScrollUpdateEvent(300, -5, 0);
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ sink_->ClearMessages();
+
+ // Overscroll gesture is in progress. Send a mouse-move now. This should
+ // complete the gesture (because the amount overscrolled is above the
+ // threshold).
+ SimulateMouseMove(5, 10, 0);
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->completed_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ SendInputEventACK(WebInputEvent::MouseMove,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+
+ SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Move mouse some more. The mouse-move events should reach the renderer.
+ SimulateMouseMove(5, 10, 0);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ SendInputEventACK(WebInputEvent::MouseMove,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+}
+
+// Tests that if a page scrolled, then the overscroll controller's states are
+// reset after the end of the scroll.
+TEST_F(RenderWidgetHostViewAuraOverscrollTest,
+ OverscrollStateResetsAfterScroll) {
+ SetUpOverscrollEnvironment();
+
+ SimulateWheelEvent(0, 5, 0, true); // sent directly
+ SimulateWheelEvent(0, 30, 0, true); // enqueued
+ SimulateWheelEvent(0, 40, 0, true); // coalesced into previous event
+ SimulateWheelEvent(0, 10, 0, true); // coalesced into previous event
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // The first wheel event is consumed. Dispatches the queued wheel event.
+ SendInputEventACK(WebInputEvent::MouseWheel, INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_TRUE(ScrollStateIsContentScrolling());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // The second wheel event is consumed.
+ SendInputEventACK(WebInputEvent::MouseWheel, INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_TRUE(ScrollStateIsContentScrolling());
+
+ // Touchpad scroll can end with a zero-velocity fling. But it is not
+ // dispatched, but it should still reset the overscroll controller state.
+ SimulateGestureFlingStartEvent(0.f, 0.f, blink::WebGestureDeviceTouchpad);
+ EXPECT_TRUE(ScrollStateIsUnknown());
+ EXPECT_EQ(0U, sink_->message_count());
+
+ SimulateWheelEvent(-5, 0, 0, true); // sent directly
+ SimulateWheelEvent(-60, 0, 0, true); // enqueued
+ SimulateWheelEvent(-100, 0, 0, true); // coalesced into previous event
+ EXPECT_TRUE(ScrollStateIsUnknown());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // The first wheel scroll did not scroll content. Overscroll should not start
+ // yet, since enough hasn't been scrolled.
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_TRUE(ScrollStateIsUnknown());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_WEST, overscroll_mode());
+ EXPECT_TRUE(ScrollStateIsOverscrolling());
+ EXPECT_EQ(0U, sink_->message_count());
+
+ SimulateGestureFlingStartEvent(0.f, 0.f, blink::WebGestureDeviceTouchpad);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_WEST, overscroll_delegate()->completed_mode());
+ EXPECT_TRUE(ScrollStateIsUnknown());
+ EXPECT_EQ(0U, sink_->message_count());
+}
+
+TEST_F(RenderWidgetHostViewAuraOverscrollTest, OverscrollResetsOnBlur) {
+ SetUpOverscrollEnvironment();
+
+ // Start an overscroll with gesture scroll. In the middle of the scroll, blur
+ // the host.
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ SimulateGestureScrollUpdateEvent(300, -5, 0);
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(2U, GetSentMessageCountAndResetSink());
+
+ view_->OnWindowFocused(NULL, view_->GetAttachedWindow());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->completed_mode());
+ EXPECT_EQ(0.f, overscroll_delegate()->delta_x());
+ EXPECT_EQ(0.f, overscroll_delegate()->delta_y());
+ sink_->ClearMessages();
+
+ SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Start a scroll gesture again. This should correctly start the overscroll
+ // after the threshold.
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ SimulateGestureScrollUpdateEvent(300, -5, 0);
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->completed_mode());
+
+ SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->completed_mode());
+ EXPECT_EQ(3U, sink_->message_count());
}
} // namespace content
diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_base.cc b/chromium/content/browser/renderer_host/render_widget_host_view_base.cc
index 8347b6d57fa..d5f58e0aab7 100644
--- a/chromium/content/browser/renderer_host/render_widget_host_view_base.cc
+++ b/chromium/content/browser/renderer_host/render_widget_host_view_base.cc
@@ -10,7 +10,8 @@
#include "content/browser/renderer_host/input/synthetic_gesture_target_base.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
-#include "content/port/browser/render_widget_host_view_frame_subscriber.h"
+#include "content/common/content_switches_internal.h"
+#include "content/public/browser/render_widget_host_view_frame_subscriber.h"
#include "third_party/WebKit/public/platform/WebScreenInfo.h"
#include "ui/gfx/display.h"
#include "ui/gfx/screen.h"
@@ -33,27 +34,8 @@
#include "ui/gfx/win/hwnd_util.h"
#endif
-#if defined(TOOLKIT_GTK)
-#include <gdk/gdkx.h>
-#include <gtk/gtk.h>
-
-#include "content/browser/renderer_host/gtk_window_utils.h"
-#endif
-
namespace content {
-// static
-RenderWidgetHostViewPort* RenderWidgetHostViewPort::FromRWHV(
- RenderWidgetHostView* rwhv) {
- return static_cast<RenderWidgetHostViewPort*>(rwhv);
-}
-
-// static
-RenderWidgetHostViewPort* RenderWidgetHostViewPort::CreateViewForWidget(
- RenderWidgetHost* widget) {
- return FromRWHV(RenderWidgetHostView::CreateViewForWidget(widget));
-}
-
#if defined(OS_WIN)
namespace {
@@ -206,8 +188,7 @@ void RenderWidgetHostViewBase::MovePluginWindowsHelper(
return;
bool oop_plugins =
- !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess) &&
- !CommandLine::ForCurrentProcess()->HasSwitch(switches::kInProcessPlugins);
+ !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess);
HDWP defer_window_pos_info =
::BeginDeferWindowPos(static_cast<int>(moves.size()));
@@ -292,7 +273,8 @@ void RenderWidgetHostViewBase::MovePluginWindowsHelper(
// Note: System will own the hrgn after we call SetWindowRgn,
// so we don't need to call DeleteObject(hrgn)
- ::SetWindowRgn(window, hrgn, !move.clip_rect.IsEmpty());
+ ::SetWindowRgn(window, hrgn,
+ !move.clip_rect.IsEmpty() && (flags & SWP_NOREDRAW) == 0);
#if defined(USE_AURA)
// When using the software compositor, if the clipping rectangle is empty
@@ -385,11 +367,14 @@ const int kFlushInputRateInUs = 16666;
RenderWidgetHostViewBase::RenderWidgetHostViewBase()
: popup_type_(blink::WebPopupTypeNone),
+ background_opaque_(true),
mouse_locked_(false),
showing_context_menu_(false),
selection_text_offset_(0),
selection_range_(gfx::Range::InvalidRange()),
current_device_scale_factor_(0),
+ current_display_rotation_(gfx::Display::ROTATE_0),
+ pinch_zoom_enabled_(content::IsPinchToZoomEnabled()),
renderer_frame_number_(0) {
}
@@ -401,19 +386,19 @@ bool RenderWidgetHostViewBase::OnMessageReceived(const IPC::Message& msg){
return false;
}
-void RenderWidgetHostViewBase::SetBackground(const SkBitmap& background) {
- background_ = background;
+void RenderWidgetHostViewBase::SetBackgroundOpaque(bool opaque) {
+ background_opaque_ = opaque;
}
-const SkBitmap& RenderWidgetHostViewBase::GetBackground() {
- return background_;
+bool RenderWidgetHostViewBase::GetBackgroundOpaque() {
+ return background_opaque_;
}
gfx::Size RenderWidgetHostViewBase::GetPhysicalBackingSize() const {
gfx::NativeView view = GetNativeView();
gfx::Display display =
gfx::Screen::GetScreenFor(view)->GetDisplayNearestWindow(view);
- return gfx::ToCeiledSize(gfx::ScaleSize(GetViewBounds().size(),
+ return gfx::ToCeiledSize(gfx::ScaleSize(GetRequestedRendererSize(),
display.device_scale_factor()));
}
@@ -430,6 +415,15 @@ void RenderWidgetHostViewBase::SelectionChanged(const base::string16& text,
selection_range_.set_end(range.end());
}
+gfx::Size RenderWidgetHostViewBase::GetRequestedRendererSize() const {
+ return GetViewBounds().size();
+}
+
+ui::TextInputClient* RenderWidgetHostViewBase::GetTextInputClient() {
+ NOTREACHED();
+ return NULL;
+}
+
bool RenderWidgetHostViewBase::IsShowingContextMenu() const {
return showing_context_menu_;
}
@@ -451,11 +445,6 @@ bool RenderWidgetHostViewBase::IsMouseLocked() {
return mouse_locked_;
}
-void RenderWidgetHostViewBase::UnhandledWheelEvent(
- const blink::WebMouseWheelEvent& event) {
- // Most implementations don't need to do anything here.
-}
-
InputEventAckState RenderWidgetHostViewBase::FilterInputEvent(
const blink::WebInputEvent& input_event) {
// By default, input events are simply forwarded to the renderer.
@@ -477,8 +466,15 @@ void RenderWidgetHostViewBase::OnSetNeedsFlushInput() {
&RenderWidgetHostViewBase::FlushInput);
}
-void RenderWidgetHostViewBase::GestureEventAck(int gesture_event_type,
- InputEventAckState ack_result) {}
+void RenderWidgetHostViewBase::WheelEventAck(
+ const blink::WebMouseWheelEvent& event,
+ InputEventAckState ack_result) {
+}
+
+void RenderWidgetHostViewBase::GestureEventAck(
+ const blink::WebGestureEvent& event,
+ InputEventAckState ack_result) {
+}
void RenderWidgetHostViewBase::SetPopupType(blink::WebPopupType popup_type) {
popup_type_ = popup_type;
@@ -493,11 +489,25 @@ BrowserAccessibilityManager*
return browser_accessibility_manager_.get();
}
+void RenderWidgetHostViewBase::CreateBrowserAccessibilityManagerIfNeeded() {
+}
+
void RenderWidgetHostViewBase::SetBrowserAccessibilityManager(
BrowserAccessibilityManager* manager) {
browser_accessibility_manager_.reset(manager);
}
+void RenderWidgetHostViewBase::OnAccessibilitySetFocus(int acc_obj_id) {
+}
+
+void RenderWidgetHostViewBase::AccessibilityShowMenu(int acc_obj_id) {
+}
+
+gfx::Point RenderWidgetHostViewBase::AccessibilityOriginInScreen(
+ const gfx::Rect& bounds) {
+ return bounds.origin();
+}
+
void RenderWidgetHostViewBase::UpdateScreenInfo(gfx::NativeView view) {
RenderWidgetHostImpl* impl = NULL;
if (GetRenderWidgetHost())
@@ -514,18 +524,17 @@ bool RenderWidgetHostViewBase::HasDisplayPropertyChanged(gfx::NativeView view) {
gfx::Display display =
gfx::Screen::GetScreenFor(view)->GetDisplayNearestWindow(view);
if (current_display_area_ == display.work_area() &&
- current_device_scale_factor_ == display.device_scale_factor()) {
+ current_device_scale_factor_ == display.device_scale_factor() &&
+ current_display_rotation_ == display.rotation()) {
return false;
}
+
current_display_area_ = display.work_area();
current_device_scale_factor_ = display.device_scale_factor();
+ current_display_rotation_ = display.rotation();
return true;
}
-void RenderWidgetHostViewBase::ProcessAckedTouchEvent(
- const TouchEventWithLatencyInfo& touch, InputEventAckState ack_result) {
-}
-
scoped_ptr<SyntheticGestureTarget>
RenderWidgetHostViewBase::CreateSyntheticGestureTarget() {
RenderWidgetHostImpl* host =
@@ -570,11 +579,6 @@ void RenderWidgetHostViewBase::EndFrameSubscription() {
render_process_host->EndFrameSubscription(impl->GetRoutingID());
}
-void RenderWidgetHostViewBase::OnOverscrolled(
- gfx::Vector2dF accumulated_overscroll,
- gfx::Vector2dF current_fling_velocity) {
-}
-
uint32 RenderWidgetHostViewBase::RendererFrameNumber() {
return renderer_frame_number_;
}
@@ -592,4 +596,16 @@ void RenderWidgetHostViewBase::FlushInput() {
impl->FlushInput();
}
+SkBitmap::Config RenderWidgetHostViewBase::PreferredReadbackFormat() {
+ return SkBitmap::kARGB_8888_Config;
+}
+
+gfx::Size RenderWidgetHostViewBase::GetVisibleViewportSize() const {
+ return GetViewBounds().size();
+}
+
+void RenderWidgetHostViewBase::SetInsets(const gfx::Insets& insets) {
+ NOTIMPLEMENTED();
+}
+
} // namespace content
diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_base.h b/chromium/content/browser/renderer_host/render_widget_host_view_base.h
index eadf92b87d2..9bf2e60a48a 100644
--- a/chromium/content/browser/renderer_host/render_widget_host_view_base.h
+++ b/chromium/content/browser/renderer_host/render_widget_host_view_base.h
@@ -9,86 +9,97 @@
#include <OpenGL/OpenGL.h>
#endif
-#if defined(TOOLKIT_GTK)
-#include <gdk/gdk.h>
-#endif
-
#include <string>
#include <vector>
#include "base/memory/scoped_ptr.h"
#include "base/callback_forward.h"
+#include "base/process/kill.h"
#include "base/timer/timer.h"
+#include "cc/output/compositor_frame.h"
+#include "content/browser/renderer_host/event_with_latency_info.h"
#include "content/common/content_export.h"
-#include "content/port/browser/render_widget_host_view_port.h"
+#include "content/common/input/input_event_ack_state.h"
+#include "content/public/browser/render_widget_host_view.h"
+#include "ipc/ipc_listener.h"
+#include "third_party/WebKit/public/web/WebPopupType.h"
+#include "third_party/WebKit/public/web/WebTextDirection.h"
+#include "ui/base/ime/text_input_mode.h"
+#include "ui/base/ime/text_input_type.h"
+#include "ui/gfx/display.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/gfx/range/range.h"
#include "ui/gfx/rect.h"
+#include "ui/surface/transport_dib.h"
-namespace content {
+class SkBitmap;
+
+struct AccessibilityHostMsg_EventParams;
+struct GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params;
+struct GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params;
+struct ViewHostMsg_SelectionBounds_Params;
+struct ViewHostMsg_TextInputState_Params;
-class RenderWidgetHostImpl;
-
-// Basic implementation shared by concrete RenderWidgetHostView
-// subclasses.
-//
-// Note that nothing should use this class, except concrete subclasses
-// that are deriving from it, and code that is written specifically to
-// use one of these concrete subclasses (i.e. platform-specific code).
-//
-// To enable embedders that add ports, everything else in content/
-// should use the RenderWidgetHostViewPort interface.
-//
-// RenderWidgetHostView class hierarchy described in render_widget_host_view.h.
-class CONTENT_EXPORT RenderWidgetHostViewBase
- : public RenderWidgetHostViewPort {
+namespace media {
+class VideoFrame;
+}
+
+namespace blink {
+struct WebScreenInfo;
+}
+
+namespace content {
+class BrowserAccessibilityManager;
+class SyntheticGesture;
+class SyntheticGestureTarget;
+class WebCursor;
+struct DidOverscrollParams;
+struct NativeWebKeyboardEvent;
+struct WebPluginGeometry;
+
+// Basic implementation shared by concrete RenderWidgetHostView subclasses.
+class CONTENT_EXPORT RenderWidgetHostViewBase : public RenderWidgetHostView,
+ public IPC::Listener {
public:
virtual ~RenderWidgetHostViewBase();
- // RenderWidgetHostViewPort implementation.
- virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE;
- virtual void SelectionChanged(const base::string16& text,
- size_t offset,
- const gfx::Range& range) OVERRIDE;
- virtual void SetBackground(const SkBitmap& background) OVERRIDE;
- virtual const SkBitmap& GetBackground() OVERRIDE;
- virtual gfx::Size GetPhysicalBackingSize() const OVERRIDE;
- virtual float GetOverdrawBottomHeight() const OVERRIDE;
+ // RenderWidgetHostView implementation.
+ virtual void SetBackgroundOpaque(bool opaque) OVERRIDE;
+ virtual bool GetBackgroundOpaque() OVERRIDE;
+ virtual ui::TextInputClient* GetTextInputClient() OVERRIDE;
virtual bool IsShowingContextMenu() const OVERRIDE;
virtual void SetShowingContextMenu(bool showing_menu) OVERRIDE;
virtual base::string16 GetSelectedText() const OVERRIDE;
virtual bool IsMouseLocked() OVERRIDE;
- virtual void UnhandledWheelEvent(
- const blink::WebMouseWheelEvent& event) OVERRIDE;
- virtual InputEventAckState FilterInputEvent(
- const blink::WebInputEvent& input_event) OVERRIDE;
- virtual void OnSetNeedsFlushInput() OVERRIDE;
- virtual void OnDidFlushInput() OVERRIDE;
- virtual void GestureEventAck(int gesture_event_type,
- InputEventAckState ack_result) OVERRIDE;
- virtual void SetPopupType(blink::WebPopupType popup_type) OVERRIDE;
- virtual blink::WebPopupType GetPopupType() OVERRIDE;
- virtual BrowserAccessibilityManager*
- GetBrowserAccessibilityManager() const OVERRIDE;
- virtual void ProcessAckedTouchEvent(const TouchEventWithLatencyInfo& touch,
- InputEventAckState ack_result) OVERRIDE;
- virtual scoped_ptr<SyntheticGestureTarget> CreateSyntheticGestureTarget()
- OVERRIDE;
- virtual bool CanSubscribeFrame() const OVERRIDE;
+ virtual gfx::Size GetVisibleViewportSize() const OVERRIDE;
+ virtual void SetInsets(const gfx::Insets& insets) OVERRIDE;
virtual void BeginFrameSubscription(
scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) OVERRIDE;
virtual void EndFrameSubscription() OVERRIDE;
- virtual void OnSwapCompositorFrame(
- uint32 output_surface_id,
- scoped_ptr<cc::CompositorFrame> frame) OVERRIDE {}
- virtual void ResizeCompositingSurface(const gfx::Size&) OVERRIDE {}
- virtual void OnOverscrolled(gfx::Vector2dF accumulated_overscroll,
- gfx::Vector2dF current_fling_velocity) OVERRIDE;
- virtual uint32 RendererFrameNumber() OVERRIDE;
- virtual void DidReceiveRendererFrame() OVERRIDE;
+
+ // IPC::Listener implementation:
+ virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE;
+
+ // Called by the host when the input flush has completed.
+ void OnDidFlushInput();
+
+ void SetPopupType(blink::WebPopupType popup_type);
+
+ blink::WebPopupType GetPopupType();
+
+ // Get the BrowserAccessibilityManager if it exists, may return NULL.
+ BrowserAccessibilityManager* GetBrowserAccessibilityManager() const;
void SetBrowserAccessibilityManager(BrowserAccessibilityManager* manager);
+ // Return a value that is incremented each time the renderer swaps a new frame
+ // to the view.
+ uint32 RendererFrameNumber();
+
+ // Called each time the RenderWidgetHost receives a new frame for display from
+ // the renderer.
+ void DidReceiveRendererFrame();
+
// Notification that a resize or move session ended on the native widget.
void UpdateScreenInfo(gfx::NativeView view);
@@ -96,7 +107,251 @@ class CONTENT_EXPORT RenderWidgetHostViewBase
// changed since the last time.
bool HasDisplayPropertyChanged(gfx::NativeView view);
+ //----------------------------------------------------------------------------
+ // The following methods can be overridden by derived classes.
+
+ // Notifies the View that the renderer text selection has changed.
+ virtual void SelectionChanged(const base::string16& text,
+ size_t offset,
+ const gfx::Range& range);
+
+ // The requested size of the renderer. May differ from GetViewBounds().size()
+ // when the view requires additional throttling.
+ virtual gfx::Size GetRequestedRendererSize() const;
+
+ // The size of the view's backing surface in non-DPI-adjusted pixels.
+ virtual gfx::Size GetPhysicalBackingSize() const;
+
+ // The height of the physical backing surface that is overdrawn opaquely in
+ // the browser, for example by an on-screen-keyboard (in DPI-adjusted pixels).
+ virtual float GetOverdrawBottomHeight() const;
+
+ // Called prior to forwarding input event messages to the renderer, giving
+ // the view a chance to perform in-process event filtering or processing.
+ // Return values of |NOT_CONSUMED| or |UNKNOWN| will result in |input_event|
+ // being forwarded.
+ virtual InputEventAckState FilterInputEvent(
+ const blink::WebInputEvent& input_event);
+
+ // Called by the host when it requires an input flush; the flush call should
+ // by synchronized with BeginFrame.
+ virtual void OnSetNeedsFlushInput();
+
+ virtual void WheelEventAck(const blink::WebMouseWheelEvent& event,
+ InputEventAckState ack_result);
+
+ virtual void GestureEventAck(const blink::WebGestureEvent& event,
+ InputEventAckState ack_result);
+
+ // Create a platform specific SyntheticGestureTarget implementation that will
+ // be used to inject synthetic input events.
+ virtual scoped_ptr<SyntheticGestureTarget> CreateSyntheticGestureTarget();
+
+ // Return true if frame subscription is supported on this platform.
+ virtual bool CanSubscribeFrame() const;
+
+ // Create a BrowserAccessibilityManager for this view if it's possible to
+ // create one and if one doesn't exist already. Some ports may not create
+ // one depending on the current state.
+ virtual void CreateBrowserAccessibilityManagerIfNeeded();
+
+ virtual void OnAccessibilitySetFocus(int acc_obj_id);
+ virtual void AccessibilityShowMenu(int acc_obj_id);
+ virtual gfx::Point AccessibilityOriginInScreen(const gfx::Rect& bounds);
+
+ virtual SkBitmap::Config PreferredReadbackFormat();
+
+ // Informs that the focused DOM node has changed.
+ virtual void FocusedNodeChanged(bool is_editable_node) {}
+
+ virtual void OnSwapCompositorFrame(uint32 output_surface_id,
+ scoped_ptr<cc::CompositorFrame> frame) {}
+
+ // Because the associated remote WebKit instance can asynchronously
+ // prevent-default on a dispatched touch event, the touch events are queued in
+ // the GestureRecognizer until invocation of ProcessAckedTouchEvent releases
+ // it to be consumed (when |ack_result| is NOT_CONSUMED OR NO_CONSUMER_EXISTS)
+ // or ignored (when |ack_result| is CONSUMED).
+ virtual void ProcessAckedTouchEvent(const TouchEventWithLatencyInfo& touch,
+ InputEventAckState ack_result) {}
+
+ virtual void DidOverscroll(const DidOverscrollParams& params) {}
+
+ virtual void DidStopFlinging() {}
+
+ //----------------------------------------------------------------------------
+ // The following static methods are implemented by each platform.
+
+ static void GetDefaultScreenInfo(blink::WebScreenInfo* results);
+
+ //----------------------------------------------------------------------------
+ // The following pure virtual methods are implemented by derived classes.
+
+ // Perform all the initialization steps necessary for this object to represent
+ // a popup (such as a <select> dropdown), then shows the popup at |pos|.
+ virtual void InitAsPopup(RenderWidgetHostView* parent_host_view,
+ const gfx::Rect& pos) = 0;
+
+ // Perform all the initialization steps necessary for this object to represent
+ // a full screen window.
+ // |reference_host_view| is the view associated with the creating page that
+ // helps to position the full screen widget on the correct monitor.
+ virtual void InitAsFullscreen(RenderWidgetHostView* reference_host_view) = 0;
+
+ // Notifies the View that it has become visible.
+ virtual void WasShown() = 0;
+
+ // Notifies the View that it has been hidden.
+ virtual void WasHidden() = 0;
+
+ // Moves all plugin windows as described in the given list.
+ // |scroll_offset| is the scroll offset of the render view.
+ virtual void MovePluginWindows(
+ const std::vector<WebPluginGeometry>& moves) = 0;
+
+ // Take focus from the associated View component.
+ virtual void Blur() = 0;
+
+ // Sets the cursor to the one associated with the specified cursor_type
+ virtual void UpdateCursor(const WebCursor& cursor) = 0;
+
+ // Indicates whether the page has finished loading.
+ virtual void SetIsLoading(bool is_loading) = 0;
+
+ // Updates the type of the input method attached to the view.
+ virtual void TextInputStateChanged(
+ const ViewHostMsg_TextInputState_Params& params) = 0;
+
+ // Cancel the ongoing composition of the input method attached to the view.
+ virtual void ImeCancelComposition() = 0;
+
+ // Notifies the View that the renderer has ceased to exist.
+ virtual void RenderProcessGone(base::TerminationStatus status,
+ int error_code) = 0;
+
+ // Tells the View to destroy itself.
+ virtual void Destroy() = 0;
+
+ // Tells the View that the tooltip text for the current mouse position over
+ // the page has changed.
+ virtual void SetTooltipText(const base::string16& tooltip_text) = 0;
+
+ // Notifies the View that the renderer selection bounds has changed.
+ // |start_rect| and |end_rect| are the bounds end of the selection in the
+ // coordinate system of the render view. |start_direction| and |end_direction|
+ // indicates the direction at which the selection was made on touch devices.
+ virtual void SelectionBoundsChanged(
+ const ViewHostMsg_SelectionBounds_Params& params) = 0;
+
+ // Notifies the view that the scroll offset has changed.
+ virtual void ScrollOffsetChanged() = 0;
+
+ // Copies the contents of the compositing surface into the given
+ // (uninitialized) PlatformCanvas if any.
+ // The rectangle region specified with |src_subrect| is copied from the
+ // contents, scaled to |dst_size|, and written to |output|.
+ // |callback| is invoked with true on success, false otherwise. |output| can
+ // be initialized even on failure.
+ // A smaller region than |src_subrect| may be copied if the underlying surface
+ // is smaller than |src_subrect|.
+ // NOTE: |callback| is called asynchronously.
+ virtual void CopyFromCompositingSurface(
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ const base::Callback<void(bool, const SkBitmap&)>& callback,
+ const SkBitmap::Config config) = 0;
+
+ // Copies a given subset of the compositing surface's content into a YV12
+ // VideoFrame, and invokes a callback with a success/fail parameter. |target|
+ // must contain an allocated, YV12 video frame of the intended size. If the
+ // copy rectangle does not match |target|'s size, the copied content will be
+ // scaled and letterboxed with black borders. The copy will happen
+ // asynchronously. This operation will fail if there is no available
+ // compositing surface.
+ virtual void CopyFromCompositingSurfaceToVideoFrame(
+ const gfx::Rect& src_subrect,
+ const scoped_refptr<media::VideoFrame>& target,
+ const base::Callback<void(bool)>& callback) = 0;
+
+ // Returns true if CopyFromCompositingSurfaceToVideoFrame() is likely to
+ // succeed.
+ //
+ // TODO(nick): When VideoFrame copies are broadly implemented, this method
+ // should be renamed to HasCompositingSurface(), or unified with
+ // IsSurfaceAvailableForCopy() and HasAcceleratedSurface().
+ virtual bool CanCopyToVideoFrame() const = 0;
+
+ // Called when an accelerated compositing surface is initialized.
+ virtual void AcceleratedSurfaceInitialized(int host_id, int route_id) = 0;
+ // |params.window| and |params.surface_id| indicate which accelerated
+ // surface's buffers swapped. |params.renderer_id| and |params.route_id|
+ // are used to formulate a reply to the GPU process to prevent it from getting
+ // too far ahead. They may all be zero, in which case no flow control is
+ // enforced; this case is currently used for accelerated plugins.
+ virtual void AcceleratedSurfaceBuffersSwapped(
+ const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params_in_pixel,
+ int gpu_host_id) = 0;
+ // Similar to above, except |params.(x|y|width|height)| define the region
+ // of the surface that changed.
+ virtual void AcceleratedSurfacePostSubBuffer(
+ const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params_in_pixel,
+ int gpu_host_id) = 0;
+
+ // Release the accelerated surface temporarily. It will be recreated on the
+ // next swap buffers or post sub buffer.
+ virtual void AcceleratedSurfaceSuspend() = 0;
+
+ virtual void AcceleratedSurfaceRelease() = 0;
+
+ // Return true if the view has an accelerated surface that contains the last
+ // presented frame for the view. If |desired_size| is non-empty, true is
+ // returned only if the accelerated surface size matches.
+ virtual bool HasAcceleratedSurface(const gfx::Size& desired_size) = 0;
+
+ virtual void GetScreenInfo(blink::WebScreenInfo* results) = 0;
+
+ // Gets the bounds of the window, in screen coordinates.
+ virtual gfx::Rect GetBoundsInRootWindow() = 0;
+
+ virtual gfx::GLSurfaceHandle GetCompositingSurface() = 0;
+
+ virtual void OnTextSurroundingSelectionResponse(const base::string16& content,
+ size_t start_offset,
+ size_t end_offset) {};
+
+#if defined(OS_ANDROID)
+ virtual void ShowDisambiguationPopup(const gfx::Rect& target_rect,
+ const SkBitmap& zoomed_bitmap) = 0;
+
+ // Instructs the view to not drop the surface even when the view is hidden.
+ virtual void LockCompositingSurface() = 0;
+ virtual void UnlockCompositingSurface() = 0;
+#endif
+
+#if defined(OS_MACOSX)
+ // Does any event handling necessary for plugin IME; should be called after
+ // the plugin has already had a chance to process the event. If plugin IME is
+ // not enabled, this is a no-op, so it is always safe to call.
+ // Returns true if the event was handled by IME.
+ virtual bool PostProcessEventForPluginIme(
+ const NativeWebKeyboardEvent& event) = 0;
+#endif
+
+#if defined(OS_MACOSX) || defined(USE_AURA)
+ // Updates the range of the marked text in an IME composition.
+ virtual void ImeCompositionRangeChanged(
+ const gfx::Range& range,
+ const std::vector<gfx::Rect>& character_bounds) = 0;
+#endif
+
#if defined(OS_WIN)
+ virtual void SetParentNativeViewAccessible(
+ gfx::NativeViewAccessible accessible_parent) = 0;
+
+ // Returns an HWND that's given as the parent window for windowless Flash to
+ // workaround crbug.com/301548.
+ virtual gfx::NativeViewId GetParentForWindowlessPlugin() const = 0;
+
// The callback that DetachPluginsHelper calls for each child window. Call
// this directly if you want to do custom filtering on plugin windows first.
static void DetachPluginWindowsCallback(HWND window);
@@ -125,9 +380,8 @@ class CONTENT_EXPORT RenderWidgetHostViewBase
// autofill...).
blink::WebPopupType popup_type_;
- // A custom background to paint behind the web content. This will be tiled
- // horizontally. Can be null, in which case we fall back to painting white.
- SkBitmap background_;
+ // When false, the background of the web content is not fully opaque.
+ bool background_opaque_;
// While the mouse is locked, the cursor is hidden from the user. Mouse events
// are still generated. However, the position they report is the last known
@@ -153,6 +407,13 @@ protected:
// The scale factor of the display the renderer is currently on.
float current_device_scale_factor_;
+ // The orientation of the display the renderer is currently on.
+ gfx::Display::Rotation current_display_rotation_;
+
+ // Whether pinch-to-zoom should be enabled and pinch events forwarded to the
+ // renderer.
+ bool pinch_zoom_enabled_;
+
private:
void FlushInput();
diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_browsertest.cc b/chromium/content/browser/renderer_host/render_widget_host_view_browsertest.cc
index 60172d38635..34327a164fb 100644
--- a/chromium/content/browser/renderer_host/render_widget_host_view_browsertest.cc
+++ b/chromium/content/browser/renderer_host/render_widget_host_view_browsertest.cc
@@ -10,33 +10,29 @@
#include "content/browser/gpu/gpu_data_manager_impl.h"
#include "content/browser/renderer_host/dip_util.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
-#include "content/port/browser/render_widget_host_view_frame_subscriber.h"
-#include "content/port/browser/render_widget_host_view_port.h"
+#include "content/browser/renderer_host/render_widget_host_view_base.h"
#include "content/public/browser/gpu_data_manager.h"
#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host_view_frame_subscriber.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/shell/browser/shell.h"
-#include "content/test/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
#include "media/base/video_frame.h"
#include "media/filters/skcanvas_video_renderer.h"
-#include "net/base/net_util.h"
+#include "net/base/filename_util.h"
#include "third_party/skia/include/core/SkBitmap.h"
-#include "third_party/skia/include/core/SkBitmapDevice.h"
#include "third_party/skia/include/core/SkCanvas.h"
+#include "ui/base/layout.h"
#include "ui/base/ui_base_switches.h"
#include "ui/gfx/size_conversions.h"
#include "ui/gfx/switches.h"
#include "ui/gl/gl_switches.h"
-#if defined(OS_MACOSX)
-#include "ui/gl/io_surface_support_mac.h"
-#endif
-
#if defined(OS_WIN)
#include "base/win/windows_version.h"
#include "ui/gfx/win/dpi.h"
@@ -55,16 +51,6 @@ namespace {
return; \
}
-// Convenience macro: Short-circuit a pass for platforms where setting up
-// high-DPI fails.
-#define PASS_TEST_IF_SCALE_FACTOR_NOT_SUPPORTED(factor) \
- if (ui::GetImageScale( \
- GetScaleFactorForView(GetRenderWidgetHostViewPort())) != factor) { \
- LOG(WARNING) << "Blindly passing this test: failed to set up " \
- "scale factor: " << factor; \
- return false; \
- }
-
// Common base class for browser tests. This is subclassed twice: Once to test
// the browser in forced-compositing mode, and once to test with compositing
// mode disabled.
@@ -75,9 +61,8 @@ class RenderWidgetHostViewBrowserTest : public ContentBrowserTest {
callback_invoke_count_(0),
frames_captured_(0) {}
- virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
+ virtual void SetUpOnMainThread() OVERRIDE {
ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &test_dir_));
- ContentBrowserTest::SetUpInProcessBrowserTestFixture();
}
// Attempts to set up the source surface. Returns false if unsupported on the
@@ -114,11 +99,9 @@ class RenderWidgetHostViewBrowserTest : public ContentBrowserTest {
return rwh;
}
- RenderWidgetHostViewPort* GetRenderWidgetHostViewPort() const {
- RenderWidgetHostViewPort* const view =
- RenderWidgetHostViewPort::FromRWHV(GetRenderViewHost()->GetView());
- CHECK(view);
- return view;
+ RenderWidgetHostViewBase* GetRenderWidgetHostView() const {
+ return static_cast<RenderWidgetHostViewBase*>(
+ GetRenderViewHost()->GetView());
}
// Callback when using CopyFromBackingStore() API.
@@ -147,7 +130,7 @@ class RenderWidgetHostViewBrowserTest : public ContentBrowserTest {
// Callback when using frame subscriber API.
void FrameDelivered(const scoped_refptr<base::MessageLoopProxy>& loop,
base::Closure quit_closure,
- base::Time timestamp,
+ base::TimeTicks timestamp,
bool frame_captured) {
++callback_invoke_count_;
if (frame_captured)
@@ -173,7 +156,8 @@ class RenderWidgetHostViewBrowserTest : public ContentBrowserTest {
base::Bind(
&RenderWidgetHostViewBrowserTest::FinishCopyFromBackingStore,
base::Unretained(this),
- run_loop.QuitClosure()));
+ run_loop.QuitClosure()),
+ SkBitmap::kARGB_8888_Config);
run_loop.Run();
if (frames_captured())
@@ -189,7 +173,7 @@ class RenderWidgetHostViewBrowserTest : public ContentBrowserTest {
protected:
// Waits until the source is available for copying.
void WaitForCopySourceReady() {
- while (!GetRenderWidgetHostViewPort()->IsSurfaceAvailableForCopy())
+ while (!GetRenderWidgetHostView()->IsSurfaceAvailableForCopy())
GiveItSomeTime();
}
@@ -211,43 +195,30 @@ class RenderWidgetHostViewBrowserTest : public ContentBrowserTest {
int frames_captured_;
};
+enum CompositingMode {
+ GL_COMPOSITING,
+ SOFTWARE_COMPOSITING,
+};
+
class CompositingRenderWidgetHostViewBrowserTest
- : public RenderWidgetHostViewBrowserTest {
+ : public RenderWidgetHostViewBrowserTest,
+ public testing::WithParamInterface<CompositingMode> {
public:
- virtual void SetUp() OVERRIDE {
- // We expect real pixel output for these tests.
- UseRealGLContexts();
-
- // On legacy windows, these tests need real GL bindings to pass.
-#if defined(OS_WIN) && !defined(USE_AURA)
- UseRealGLBindings();
-#endif
+ explicit CompositingRenderWidgetHostViewBrowserTest()
+ : compositing_mode_(GetParam()) {}
+ virtual void SetUp() OVERRIDE {
+ if (compositing_mode_ == SOFTWARE_COMPOSITING)
+ UseSoftwareCompositing();
RenderWidgetHostViewBrowserTest::SetUp();
}
- virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
- // Note: Not appending kForceCompositingMode switch here, since not all bots
- // support compositing. Some bots will run with compositing on, and others
- // won't. Therefore, the call to SetUpSourceSurface() later on will detect
- // whether compositing mode is actually on or not. If not, the tests will
- // pass blindly, logging a warning message, since we cannot test what the
- // platform/implementation does not support.
- RenderWidgetHostViewBrowserTest::SetUpCommandLine(command_line);
- }
-
virtual GURL TestUrl() {
return net::FilePathToFileURL(
test_dir().AppendASCII("rwhv_compositing_animation.html"));
}
virtual bool SetUpSourceSurface(const char* wait_message) OVERRIDE {
- if (!IsForceCompositingModeEnabled())
- return false; // See comment in SetUpCommandLine().
-#if defined(OS_MACOSX)
- CHECK(IOSurfaceSupport::Initialize());
-#endif
-
content::DOMMessageQueue message_queue;
NavigateToURL(shell(), TestUrl());
if (wait_message != NULL) {
@@ -258,54 +229,15 @@ class CompositingRenderWidgetHostViewBrowserTest
}
}
-#if !defined(USE_AURA)
- if (!GetRenderWidgetHost()->is_accelerated_compositing_active())
- return false; // Renderer did not turn on accelerated compositing.
-#endif
-
- // Using accelerated compositing, but a compositing surface might not be
- // available yet. So, wait for it.
+ // A frame might not be available yet. So, wait for it.
WaitForCopySourceReady();
return true;
}
-};
-
-class NonCompositingRenderWidgetHostViewBrowserTest
- : public RenderWidgetHostViewBrowserTest {
- public:
- virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
- // Note: Appending the kDisableAcceleratedCompositing switch here, but there
- // are some builds that only use compositing and will ignore this switch.
- // Therefore, the call to SetUpSourceSurface() later on will detect whether
- // compositing mode is actually off. If it's on, the tests will pass
- // blindly, logging a warning message, since we cannot test what the
- // platform/implementation does not support.
- command_line->AppendSwitch(switches::kDisableAcceleratedCompositing);
- RenderWidgetHostViewBrowserTest::SetUpCommandLine(command_line);
- }
- virtual GURL TestUrl() {
- return GURL(kAboutBlankURL);
- }
-
- virtual bool SetUpSourceSurface(const char* wait_message) OVERRIDE {
- if (IsForceCompositingModeEnabled())
- return false; // See comment in SetUpCommandLine().
-
- content::DOMMessageQueue message_queue;
- NavigateToURL(shell(), TestUrl());
- if (wait_message != NULL) {
- std::string result(wait_message);
- if (!message_queue.WaitForMessage(&result)) {
- EXPECT_TRUE(false) << "WaitForMessage " << result << " failed.";
- return false;
- }
- }
+ private:
+ const CompositingMode compositing_mode_;
- WaitForCopySourceReady();
- // Return whether the renderer left accelerated compositing turned off.
- return !GetRenderWidgetHost()->is_accelerated_compositing_active();
- }
+ DISALLOW_COPY_AND_ASSIGN(CompositingRenderWidgetHostViewBrowserTest);
};
class FakeFrameSubscriber : public RenderWidgetHostViewFrameSubscriber {
@@ -315,10 +247,9 @@ class FakeFrameSubscriber : public RenderWidgetHostViewFrameSubscriber {
: callback_(callback) {
}
- virtual bool ShouldCaptureFrame(
- base::Time present_time,
- scoped_refptr<media::VideoFrame>* storage,
- DeliverFrameCallback* callback) OVERRIDE {
+ virtual bool ShouldCaptureFrame(base::TimeTicks present_time,
+ scoped_refptr<media::VideoFrame>* storage,
+ DeliverFrameCallback* callback) OVERRIDE {
// Only allow one frame capture to be made. Otherwise, the compositor could
// start multiple captures, unbounded, and eventually its own limiter logic
// will begin invoking |callback| with a |false| result. This flakes out
@@ -342,21 +273,14 @@ class FakeFrameSubscriber : public RenderWidgetHostViewFrameSubscriber {
// The CopyFromBackingStore() API should work on all platforms when compositing
// is enabled.
-IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTest,
- CopyFromBackingStore) {
- RunBasicCopyFromBackingStoreTest();
-}
-
-// The CopyFromBackingStore() API should work on all platforms when compositing
-// is disabled.
-IN_PROC_BROWSER_TEST_F(NonCompositingRenderWidgetHostViewBrowserTest,
+IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTest,
CopyFromBackingStore) {
RunBasicCopyFromBackingStoreTest();
}
// Tests that the callback passed to CopyFromBackingStore is always called,
// even when the RenderWidgetHost is deleting in the middle of an async copy.
-IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTest,
+IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTest,
CopyFromBackingStore_CallbackDespiteDelete) {
SET_UP_SURFACE_OR_PASS_TEST(NULL);
@@ -365,9 +289,11 @@ IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTest,
gfx::Rect(),
frame_size(),
base::Bind(&RenderWidgetHostViewBrowserTest::FinishCopyFromBackingStore,
- base::Unretained(this), run_loop.QuitClosure()));
+ base::Unretained(this),
+ run_loop.QuitClosure()),
+ SkBitmap::kARGB_8888_Config);
// Delete the surface before the callback is run.
- GetRenderWidgetHostViewPort()->AcceleratedSurfaceRelease();
+ GetRenderWidgetHostView()->AcceleratedSurfaceRelease();
run_loop.Run();
EXPECT_EQ(1, callback_invoke_count());
@@ -377,19 +303,18 @@ IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTest,
// always called, even when the RenderWidgetHost is deleting in the middle of
// an async copy.
//
-// Test is flaky on Win Aura. http://crbug.com/276783
-#if (defined(OS_WIN) && defined(USE_AURA)) || \
- (defined(OS_CHROMEOS) && !defined(NDEBUG))
+// Test is flaky on Win. http://crbug.com/276783
+#if defined(OS_WIN) || (defined(OS_CHROMEOS) && !defined(NDEBUG))
#define MAYBE_CopyFromCompositingSurface_CallbackDespiteDelete \
DISABLED_CopyFromCompositingSurface_CallbackDespiteDelete
#else
#define MAYBE_CopyFromCompositingSurface_CallbackDespiteDelete \
CopyFromCompositingSurface_CallbackDespiteDelete
#endif
-IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTest,
+IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTest,
MAYBE_CopyFromCompositingSurface_CallbackDespiteDelete) {
SET_UP_SURFACE_OR_PASS_TEST(NULL);
- RenderWidgetHostViewPort* const view = GetRenderWidgetHostViewPort();
+ RenderWidgetHostViewBase* const view = GetRenderWidgetHostView();
if (!view->CanCopyToVideoFrame()) {
LOG(WARNING) <<
("Blindly passing this test: CopyFromCompositingSurfaceToVideoFrame() "
@@ -411,20 +336,12 @@ IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTest,
EXPECT_EQ(1, callback_invoke_count());
}
-// With compositing turned off, no platforms should support the
-// CopyFromCompositingSurfaceToVideoFrame() API.
-IN_PROC_BROWSER_TEST_F(NonCompositingRenderWidgetHostViewBrowserTest,
- CopyFromCompositingSurfaceToVideoFrameCallbackTest) {
- SET_UP_SURFACE_OR_PASS_TEST(NULL);
- EXPECT_FALSE(GetRenderWidgetHostViewPort()->CanCopyToVideoFrame());
-}
-
// Test basic frame subscription functionality. We subscribe, and then run
// until at least one DeliverFrameCallback has been invoked.
-IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTest,
+IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTest,
FrameSubscriberTest) {
SET_UP_SURFACE_OR_PASS_TEST(NULL);
- RenderWidgetHostViewPort* const view = GetRenderWidgetHostViewPort();
+ RenderWidgetHostViewBase* const view = GetRenderWidgetHostView();
if (!view->CanSubscribeFrame()) {
LOG(WARNING) << ("Blindly passing this test: Frame subscription not "
"supported on this platform.");
@@ -447,9 +364,9 @@ IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTest,
}
// Test that we can copy twice from an accelerated composited page.
-IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTest, CopyTwice) {
+IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTest, CopyTwice) {
SET_UP_SURFACE_OR_PASS_TEST(NULL);
- RenderWidgetHostViewPort* const view = GetRenderWidgetHostViewPort();
+ RenderWidgetHostViewBase* const view = GetRenderWidgetHostView();
if (!view->CanCopyToVideoFrame()) {
LOG(WARNING) << ("Blindly passing this test: "
"CopyFromCompositingSurfaceToVideoFrame() not supported "
@@ -471,14 +388,15 @@ IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTest, CopyTwice) {
base::Unretained(this),
base::MessageLoopProxy::current(),
base::Closure(),
- base::Time::Now()));
+ base::TimeTicks::Now()));
view->CopyFromCompositingSurfaceToVideoFrame(
- gfx::Rect(view->GetViewBounds().size()), second_output,
+ gfx::Rect(view->GetViewBounds().size()),
+ second_output,
base::Bind(&RenderWidgetHostViewBrowserTest::FrameDelivered,
base::Unretained(this),
base::MessageLoopProxy::current(),
run_loop.QuitClosure(),
- base::Time::Now()));
+ base::TimeTicks::Now()));
run_loop.Run();
EXPECT_EQ(2, callback_invoke_count());
@@ -493,6 +411,11 @@ class CompositingRenderWidgetHostViewBrowserTestTabCapture
allowable_error_(0),
test_url_("data:text/html,<!doctype html>") {}
+ virtual void SetUp() OVERRIDE {
+ EnablePixelOutput();
+ CompositingRenderWidgetHostViewBrowserTest::SetUp();
+ }
+
void CopyFromCompositingSurfaceCallback(base::Closure quit_callback,
bool result,
const SkBitmap& bitmap) {
@@ -565,14 +488,12 @@ class CompositingRenderWidgetHostViewBrowserTestTabCapture
media::SkCanvasVideoRenderer video_renderer;
SkBitmap bitmap;
- bitmap.setConfig(SkBitmap::kARGB_8888_Config,
- video_frame->visible_rect().width(),
- video_frame->visible_rect().height(),
- 0, kOpaque_SkAlphaType);
- bitmap.allocPixels();
-
- SkBitmapDevice device(bitmap);
- SkCanvas canvas(&device);
+ bitmap.allocPixels(SkImageInfo::Make(video_frame->visible_rect().width(),
+ video_frame->visible_rect().height(),
+ kPMColor_SkColorType,
+ kPremul_SkAlphaType));
+ bitmap.eraseColor(SK_ColorTRANSPARENT);
+ SkCanvas canvas(bitmap);
video_renderer.Paint(video_frame.get(),
&canvas,
@@ -647,7 +568,7 @@ class CompositingRenderWidgetHostViewBrowserTestTabCapture
if (!ShouldContinueAfterTestURLLoad())
return;
- RenderWidgetHostViewPort* rwhvp = GetRenderWidgetHostViewPort();
+ RenderWidgetHostViewBase* rwhvp = GetRenderWidgetHostView();
if (video_frame && !rwhvp->CanCopyToVideoFrame()) {
// This should only happen on Mac when using the software compositor.
// Otherwise, raise an error. This can be removed when Mac is moved to a
@@ -715,7 +636,10 @@ class CompositingRenderWidgetHostViewBrowserTestTabCapture
CopyFromCompositingSurfaceCallback,
base::Unretained(this),
run_loop.QuitClosure());
- rwhvp->CopyFromCompositingSurface(copy_rect, output_size, callback);
+ rwhvp->CopyFromCompositingSurface(copy_rect,
+ output_size,
+ callback,
+ SkBitmap::kARGB_8888_Config);
}
run_loop.Run();
}
@@ -753,7 +677,7 @@ class CompositingRenderWidgetHostViewBrowserTestTabCapture
std::string test_url_;
};
-IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTestTabCapture,
+IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTestTabCapture,
CopyFromCompositingSurface_Origin_Unscaled) {
gfx::Rect copy_rect(400, 300);
gfx::Size output_size = copy_rect.size();
@@ -767,7 +691,7 @@ IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTestTabCapture,
video_frame);
}
-IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTestTabCapture,
+IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTestTabCapture,
CopyFromCompositingSurface_Origin_Scaled) {
gfx::Rect copy_rect(400, 300);
gfx::Size output_size(200, 100);
@@ -781,7 +705,7 @@ IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTestTabCapture,
video_frame);
}
-IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTestTabCapture,
+IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTestTabCapture,
CopyFromCompositingSurface_Cropped_Unscaled) {
// Grab 60x60 pixels from the center of the tab contents.
gfx::Rect copy_rect(400, 300);
@@ -798,7 +722,7 @@ IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTestTabCapture,
video_frame);
}
-IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTestTabCapture,
+IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTestTabCapture,
CopyFromCompositingSurface_Cropped_Scaled) {
// Grab 60x60 pixels from the center of the tab contents.
gfx::Rect copy_rect(400, 300);
@@ -815,7 +739,7 @@ IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTestTabCapture,
video_frame);
}
-IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTestTabCapture,
+IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTestTabCapture,
CopyFromCompositingSurface_ForVideoFrame) {
// Grab 90x60 pixels from the center of the tab contents.
gfx::Rect copy_rect(400, 300);
@@ -832,7 +756,7 @@ IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTestTabCapture,
video_frame);
}
-IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTestTabCapture,
+IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTestTabCapture,
CopyFromCompositingSurface_ForVideoFrame_Scaled) {
// Grab 90x60 pixels from the center of the tab contents.
gfx::Rect copy_rect(400, 300);
@@ -853,16 +777,14 @@ IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTestTabCapture,
class CompositingRenderWidgetHostViewTabCaptureHighDPI
: public CompositingRenderWidgetHostViewBrowserTestTabCapture {
public:
- CompositingRenderWidgetHostViewTabCaptureHighDPI()
- : kScale(2.f) {
- }
+ CompositingRenderWidgetHostViewTabCaptureHighDPI() : kScale(2.f) {}
- virtual void SetUpCommandLine(CommandLine* cmd) OVERRIDE {
- CompositingRenderWidgetHostViewBrowserTestTabCapture::SetUpCommandLine(cmd);
+ virtual void SetUpOnMainThread() OVERRIDE {
+ CommandLine* cmd = CommandLine::ForCurrentProcess();
cmd->AppendSwitchASCII(switches::kForceDeviceScaleFactor,
base::StringPrintf("%f", scale()));
#if defined(OS_WIN)
- cmd->AppendSwitchASCII(switches::kHighDPISupport, "1");
+ gfx::ForceHighDPISupportForTesting(scale());
gfx::EnableHighDPISupport();
#endif
}
@@ -871,7 +793,13 @@ class CompositingRenderWidgetHostViewTabCaptureHighDPI
private:
virtual bool ShouldContinueAfterTestURLLoad() OVERRIDE {
- PASS_TEST_IF_SCALE_FACTOR_NOT_SUPPORTED(scale());
+ // Short-circuit a pass for platforms where setting up high-DPI fails.
+ if (ui::GetScaleForScaleFactor(ui::GetSupportedScaleFactor(
+ GetScaleFactorForView(GetRenderWidgetHostView()))) != scale()) {
+ LOG(WARNING) << "Blindly passing this test: failed to set up "
+ "scale factor: " << scale();
+ return false;
+ }
return true;
}
@@ -880,7 +808,7 @@ class CompositingRenderWidgetHostViewTabCaptureHighDPI
DISALLOW_COPY_AND_ASSIGN(CompositingRenderWidgetHostViewTabCaptureHighDPI);
};
-IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewTabCaptureHighDPI,
+IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewTabCaptureHighDPI,
CopyFromCompositingSurface) {
gfx::Rect copy_rect(200, 150);
gfx::Size output_size = copy_rect.size();
@@ -895,7 +823,7 @@ IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewTabCaptureHighDPI,
video_frame);
}
-IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewTabCaptureHighDPI,
+IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewTabCaptureHighDPI,
CopyFromCompositingSurfaceVideoFrame) {
gfx::Size html_rect_size(200, 150);
// Grab 90x60 pixels from the center of the tab contents.
@@ -913,6 +841,25 @@ IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewTabCaptureHighDPI,
video_frame);
}
+#if !defined(USE_AURA) && !defined(OS_MACOSX)
+// TODO(danakj): Remove this case when GTK linux is no more and move the
+// values inline to testing::Values() below.
+static const CompositingMode kAllCompositingModes[] = {GL_COMPOSITING};
+#else
+static const CompositingMode kAllCompositingModes[] = {GL_COMPOSITING,
+ SOFTWARE_COMPOSITING};
+#endif
+
+INSTANTIATE_TEST_CASE_P(GLAndSoftwareCompositing,
+ CompositingRenderWidgetHostViewBrowserTest,
+ testing::ValuesIn(kAllCompositingModes));
+INSTANTIATE_TEST_CASE_P(GLAndSoftwareCompositing,
+ CompositingRenderWidgetHostViewBrowserTestTabCapture,
+ testing::ValuesIn(kAllCompositingModes));
+INSTANTIATE_TEST_CASE_P(GLAndSoftwareCompositing,
+ CompositingRenderWidgetHostViewTabCaptureHighDPI,
+ testing::ValuesIn(kAllCompositingModes));
+
#endif // !defined(OS_ANDROID) && !defined(OS_IOS)
} // namespace
diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_gtk.cc b/chromium/content/browser/renderer_host/render_widget_host_view_gtk.cc
deleted file mode 100644
index e50bd045652..00000000000
--- a/chromium/content/browser/renderer_host/render_widget_host_view_gtk.cc
+++ /dev/null
@@ -1,1608 +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/renderer_host/render_widget_host_view_gtk.h"
-
-#include <cairo/cairo.h>
-#include <gdk/gdk.h>
-#include <gdk/gdkkeysyms.h>
-#include <gdk/gdkx.h>
-#include <gtk/gtk.h>
-
-#include <algorithm>
-#include <string>
-
-#include "base/bind_helpers.h"
-#include "base/command_line.h"
-#include "base/debug/trace_event.h"
-#include "base/logging.h"
-#include "base/message_loop/message_loop.h"
-#include "base/metrics/histogram.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/utf_offset_string_conversions.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/time/time.h"
-#include "content/browser/accessibility/browser_accessibility_gtk.h"
-#include "content/browser/accessibility/browser_accessibility_manager_gtk.h"
-#include "content/browser/renderer_host/backing_store_gtk.h"
-#include "content/browser/renderer_host/gtk_im_context_wrapper.h"
-#include "content/browser/renderer_host/gtk_key_bindings_handler.h"
-#include "content/browser/renderer_host/gtk_window_utils.h"
-#include "content/browser/renderer_host/input/web_input_event_builders_gtk.h"
-#include "content/browser/renderer_host/render_view_host_delegate.h"
-#include "content/browser/renderer_host/render_view_host_impl.h"
-#include "content/common/gpu/gpu_messages.h"
-#include "content/common/input_messages.h"
-#include "content/common/view_messages.h"
-#include "content/common/webplugin_geometry.h"
-#include "content/public/browser/native_web_keyboard_event.h"
-#include "content/public/common/content_switches.h"
-#include "skia/ext/platform_canvas.h"
-#include "third_party/WebKit/public/platform/WebScreenInfo.h"
-#include "third_party/WebKit/public/web/WebInputEvent.h"
-#include "ui/base/clipboard/scoped_clipboard_writer.h"
-#include "ui/base/x/active_window_watcher_x.h"
-#include "ui/base/x/x11_util.h"
-#include "ui/gfx/gtk_compat.h"
-#include "ui/gfx/gtk_native_view_id_manager.h"
-#include "ui/gfx/gtk_preserve_window.h"
-#include "ui/gfx/text_elider.h"
-#include "webkit/common/cursors/webcursor_gtk_data.h"
-
-using blink::WebMouseWheelEvent;
-using blink::WebScreenInfo;
-
-namespace content {
-namespace {
-
-// Paint rects on Linux are bounded by the maximum size of a shared memory
-// region. By default that's 32MB, but many distros increase it significantly
-// (i.e. to 256MB).
-//
-// We fetch the maximum value from /proc/sys/kernel/shmmax at runtime and, if
-// we exceed that, then we limit the height of the paint rect in the renderer.
-//
-// These constants are here to ensure that, in the event that we exceed it, we
-// end up with something a little more square. Previously we had 4000x4000, but
-// people's monitor setups are actually exceeding that these days.
-const int kMaxWindowWidth = 10000;
-const int kMaxWindowHeight = 10000;
-
-const GdkColor kBGColor =
-#if defined(NDEBUG)
- { 0, 0xff * 257, 0xff * 257, 0xff * 257 };
-#else
- { 0, 0x00 * 257, 0xff * 257, 0x00 * 257 };
-#endif
-
-// Returns the spinning cursor used for loading state.
-GdkCursor* GetMozSpinningCursor() {
- static GdkCursor* moz_spinning_cursor = NULL;
- if (!moz_spinning_cursor) {
- const GdkColor fg = { 0, 0, 0, 0 };
- const GdkColor bg = { 65535, 65535, 65535, 65535 };
- GdkPixmap* source = gdk_bitmap_create_from_data(
- NULL, reinterpret_cast<const gchar*>(moz_spinning_bits), 32, 32);
- GdkPixmap* mask = gdk_bitmap_create_from_data(
- NULL, reinterpret_cast<const gchar*>(moz_spinning_mask_bits), 32, 32);
- moz_spinning_cursor =
- gdk_cursor_new_from_pixmap(source, mask, &fg, &bg, 2, 2);
- g_object_unref(source);
- g_object_unref(mask);
- }
- return moz_spinning_cursor;
-}
-
-bool MovedToPoint(const blink::WebMouseEvent& mouse_event,
- const gfx::Point& center) {
- return mouse_event.globalX == center.x() &&
- mouse_event.globalY == center.y();
-}
-
-} // namespace
-
-// This class is a simple convenience wrapper for Gtk functions. It has only
-// static methods.
-class RenderWidgetHostViewGtkWidget {
- public:
- static AtkObject* GetAccessible(void* userdata) {
- return (static_cast<RenderWidgetHostViewGtk*>(userdata))->
- GetAccessible();
- }
-
- static GtkWidget* CreateNewWidget(RenderWidgetHostViewGtk* host_view) {
- GtkWidget* widget = gtk_preserve_window_new();
- gtk_widget_set_name(widget, "chrome-render-widget-host-view");
- // We manually double-buffer in Paint() because Paint() may or may not be
- // called in repsonse to an "expose-event" signal.
- gtk_widget_set_double_buffered(widget, FALSE);
- gtk_widget_set_redraw_on_allocate(widget, FALSE);
- gtk_widget_modify_bg(widget, GTK_STATE_NORMAL, &kBGColor);
- // Allow the browser window to be resized freely.
- gtk_widget_set_size_request(widget, 0, 0);
-
- gtk_widget_add_events(widget, GDK_EXPOSURE_MASK |
- GDK_STRUCTURE_MASK |
- GDK_POINTER_MOTION_MASK |
- GDK_BUTTON_PRESS_MASK |
- GDK_BUTTON_RELEASE_MASK |
- GDK_KEY_PRESS_MASK |
- GDK_KEY_RELEASE_MASK |
- GDK_FOCUS_CHANGE_MASK |
- GDK_ENTER_NOTIFY_MASK |
- GDK_LEAVE_NOTIFY_MASK);
- gtk_widget_set_can_focus(widget, TRUE);
-
- g_signal_connect(widget, "expose-event",
- G_CALLBACK(OnExposeEvent), host_view);
- g_signal_connect(widget, "realize",
- G_CALLBACK(OnRealize), host_view);
- g_signal_connect(widget, "configure-event",
- G_CALLBACK(OnConfigureEvent), host_view);
- g_signal_connect(widget, "size-allocate",
- G_CALLBACK(OnSizeAllocate), host_view);
- g_signal_connect(widget, "key-press-event",
- G_CALLBACK(OnKeyPressReleaseEvent), host_view);
- g_signal_connect(widget, "key-release-event",
- G_CALLBACK(OnKeyPressReleaseEvent), host_view);
- g_signal_connect(widget, "focus-in-event",
- G_CALLBACK(OnFocusIn), host_view);
- g_signal_connect(widget, "focus-out-event",
- G_CALLBACK(OnFocusOut), host_view);
- g_signal_connect(widget, "grab-notify",
- G_CALLBACK(OnGrabNotify), host_view);
- g_signal_connect(widget, "button-press-event",
- G_CALLBACK(OnButtonPressReleaseEvent), host_view);
- g_signal_connect(widget, "button-release-event",
- G_CALLBACK(OnButtonPressReleaseEvent), host_view);
- g_signal_connect(widget, "motion-notify-event",
- G_CALLBACK(OnMouseMoveEvent), host_view);
- g_signal_connect(widget, "enter-notify-event",
- G_CALLBACK(OnCrossingEvent), host_view);
- g_signal_connect(widget, "leave-notify-event",
- G_CALLBACK(OnCrossingEvent), host_view);
- g_signal_connect(widget, "client-event",
- G_CALLBACK(OnClientEvent), host_view);
-
-
- // Connect after so that we are called after the handler installed by the
- // WebContentsView which handles zoom events.
- g_signal_connect_after(widget, "scroll-event",
- G_CALLBACK(OnMouseScrollEvent), host_view);
-
- // Route calls to get_accessible to the view.
- gtk_preserve_window_set_accessible_factory(
- GTK_PRESERVE_WINDOW(widget), GetAccessible, host_view);
-
- return widget;
- }
-
- private:
- static gboolean OnExposeEvent(GtkWidget* widget,
- GdkEventExpose* expose,
- RenderWidgetHostViewGtk* host_view) {
- if (host_view->host_->is_hidden())
- return FALSE;
- const gfx::Rect damage_rect(expose->area);
- host_view->Paint(damage_rect);
- return FALSE;
- }
-
- static gboolean OnRealize(GtkWidget* widget,
- RenderWidgetHostViewGtk* host_view) {
- // Use GtkSignalRegistrar to register events on a widget we don't
- // control the lifetime of, auto disconnecting at our end of our life.
- host_view->signals_.Connect(gtk_widget_get_toplevel(widget),
- "configure-event",
- G_CALLBACK(OnConfigureEvent), host_view);
- return FALSE;
- }
-
- static gboolean OnConfigureEvent(GtkWidget* widget,
- GdkEventConfigure* event,
- RenderWidgetHostViewGtk* host_view) {
- host_view->MarkCachedWidgetCenterStale();
- host_view->UpdateScreenInfo(host_view->GetNativeView());
- return FALSE;
- }
-
- static gboolean OnSizeAllocate(GtkWidget* widget,
- GdkRectangle* allocation,
- RenderWidgetHostViewGtk* host_view) {
- if (!host_view->IsPopup() && !host_view->is_fullscreen_)
- host_view->SetSize(gfx::Size(allocation->width, allocation->height));
- return FALSE;
- }
-
- static gboolean OnKeyPressReleaseEvent(GtkWidget* widget,
- GdkEventKey* event,
- RenderWidgetHostViewGtk* host_view) {
- TRACE_EVENT0("browser",
- "RenderWidgetHostViewGtkWidget::OnKeyPressReleaseEvent");
- // Force popups or fullscreen windows to close on Escape so they won't keep
- // the keyboard grabbed or be stuck onscreen if the renderer is hanging.
- bool should_close_on_escape =
- (host_view->IsPopup() && host_view->NeedsInputGrab()) ||
- host_view->is_fullscreen_;
- if (should_close_on_escape && GDK_Escape == event->keyval) {
- host_view->host_->Shutdown();
- } else {
- // Send key event to input method.
- host_view->im_context_->ProcessKeyEvent(event);
- }
-
- // We return TRUE because we did handle the event. If it turns out webkit
- // can't handle the event, we'll deal with it in
- // RenderView::UnhandledKeyboardEvent().
- return TRUE;
- }
-
- static gboolean OnFocusIn(GtkWidget* widget,
- GdkEventFocus* focus,
- RenderWidgetHostViewGtk* host_view) {
- host_view->ShowCurrentCursor();
- RenderWidgetHostImpl* host =
- RenderWidgetHostImpl::From(host_view->GetRenderWidgetHost());
- host->GotFocus();
- host->SetActive(true);
-
- // The only way to enable a GtkIMContext object is to call its focus in
- // handler.
- host_view->im_context_->OnFocusIn();
-
- return TRUE;
- }
-
- static gboolean OnFocusOut(GtkWidget* widget,
- GdkEventFocus* focus,
- RenderWidgetHostViewGtk* host_view) {
- // Whenever we lose focus, set the cursor back to that of our parent window,
- // which should be the default arrow.
- gdk_window_set_cursor(gtk_widget_get_window(widget), NULL);
- // If we are showing a context menu, maintain the illusion that webkit has
- // focus.
- if (!host_view->IsShowingContextMenu()) {
- RenderWidgetHostImpl* host =
- RenderWidgetHostImpl::From(host_view->GetRenderWidgetHost());
- host->SetActive(false);
- host->Blur();
- }
-
- // Prevents us from stealing input context focus in OnGrabNotify() handler.
- host_view->was_imcontext_focused_before_grab_ = false;
-
- // Disable the GtkIMContext object.
- host_view->im_context_->OnFocusOut();
-
- host_view->set_last_mouse_down(NULL);
-
- return TRUE;
- }
-
- // Called when we are shadowed or unshadowed by a keyboard grab (which will
- // occur for activatable popups, such as dropdown menus). Popup windows do not
- // take focus, so we never get a focus out or focus in event when they are
- // shown, and must rely on this signal instead.
- static void OnGrabNotify(GtkWidget* widget, gboolean was_grabbed,
- RenderWidgetHostViewGtk* host_view) {
- if (was_grabbed) {
- if (host_view->was_imcontext_focused_before_grab_)
- host_view->im_context_->OnFocusIn();
- } else {
- host_view->was_imcontext_focused_before_grab_ =
- host_view->im_context_->is_focused();
- if (host_view->was_imcontext_focused_before_grab_) {
- gdk_window_set_cursor(gtk_widget_get_window(widget), NULL);
- host_view->im_context_->OnFocusOut();
- }
- }
- }
-
- static gboolean OnButtonPressReleaseEvent(
- GtkWidget* widget,
- GdkEventButton* event,
- RenderWidgetHostViewGtk* host_view) {
- TRACE_EVENT0("browser",
- "RenderWidgetHostViewGtkWidget::OnButtonPressReleaseEvent");
-
- if (event->type != GDK_BUTTON_RELEASE)
- host_view->set_last_mouse_down(event);
-
- if (!(event->button == 1 || event->button == 2 || event->button == 3))
- return FALSE; // We do not forward any other buttons to the renderer.
- if (event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS)
- return FALSE;
-
- // If we don't have focus already, this mouse click will focus us.
- if (!gtk_widget_is_focus(widget))
- host_view->host_->OnPointerEventActivate();
-
- // Confirm existing composition text on mouse click events, to make sure
- // the input caret won't be moved with an ongoing composition session.
- if (event->type != GDK_BUTTON_RELEASE)
- host_view->im_context_->ConfirmComposition();
-
- // We want to translate the coordinates of events that do not originate
- // from this widget to be relative to the top left of the widget.
- GtkWidget* event_widget = gtk_get_event_widget(
- reinterpret_cast<GdkEvent*>(event));
- if (event_widget != widget) {
- int x = 0;
- int y = 0;
- gtk_widget_get_pointer(widget, &x, &y);
- // If the mouse event happens outside our popup, force the popup to
- // close. We do this so a hung renderer doesn't prevent us from
- // releasing the x pointer grab.
- GtkAllocation allocation;
- gtk_widget_get_allocation(widget, &allocation);
- bool click_in_popup = x >= 0 && y >= 0 && x < allocation.width &&
- y < allocation.height;
- // Only Shutdown on mouse downs. Mouse ups can occur outside the render
- // view if the user drags for DnD or while using the scrollbar on a select
- // dropdown. Don't shutdown if we are not a popup.
- if (event->type != GDK_BUTTON_RELEASE && host_view->IsPopup() &&
- !host_view->is_popup_first_mouse_release_ && !click_in_popup) {
- host_view->host_->Shutdown();
- return FALSE;
- }
- event->x = x;
- event->y = y;
- }
-
- // TODO(evanm): why is this necessary here but not in test shell?
- // This logic is the same as GtkButton.
- if (event->type == GDK_BUTTON_PRESS && !gtk_widget_has_focus(widget))
- gtk_widget_grab_focus(widget);
-
- host_view->is_popup_first_mouse_release_ = false;
- RenderWidgetHostImpl* widget_host =
- RenderWidgetHostImpl::From(host_view->GetRenderWidgetHost());
- if (widget_host)
- widget_host->ForwardMouseEvent(WebMouseEventBuilder::Build(event));
-
- // Although we did handle the mouse event, we need to let other handlers
- // run (in particular the one installed by WebContentsViewGtk).
- return FALSE;
- }
-
- static gboolean OnMouseMoveEvent(GtkWidget* widget,
- GdkEventMotion* event,
- RenderWidgetHostViewGtk* host_view) {
- TRACE_EVENT0("browser",
- "RenderWidgetHostViewGtkWidget::OnMouseMoveEvent");
- // We want to translate the coordinates of events that do not originate
- // from this widget to be relative to the top left of the widget.
- GtkWidget* event_widget = gtk_get_event_widget(
- reinterpret_cast<GdkEvent*>(event));
- if (event_widget != widget) {
- int x = 0;
- int y = 0;
- gtk_widget_get_pointer(widget, &x, &y);
- event->x = x;
- event->y = y;
- }
-
- host_view->ModifyEventForEdgeDragging(widget, event);
-
- blink::WebMouseEvent mouse_event = WebMouseEventBuilder::Build(event);
-
- if (host_view->mouse_locked_) {
- gfx::Point center = host_view->GetWidgetCenter();
-
- bool moved_to_center = MovedToPoint(mouse_event, center);
- if (moved_to_center)
- host_view->mouse_has_been_warped_to_new_center_ = true;
-
- host_view->ModifyEventMovementAndCoords(&mouse_event);
-
- if (!moved_to_center &&
- (mouse_event.movementX || mouse_event.movementY)) {
- GdkDisplay* display = gtk_widget_get_display(widget);
- GdkScreen* screen = gtk_widget_get_screen(widget);
- gdk_display_warp_pointer(display, screen, center.x(), center.y());
- if (host_view->mouse_has_been_warped_to_new_center_)
- RenderWidgetHostImpl::From(
- host_view->GetRenderWidgetHost())->ForwardMouseEvent(mouse_event);
- }
- } else { // Mouse is not locked.
- host_view->ModifyEventMovementAndCoords(&mouse_event);
- // Do not send mouse events while the mouse cursor is being warped back
- // to the unlocked location.
- if (!host_view->mouse_is_being_warped_to_unlocked_position_) {
- RenderWidgetHostImpl::From(
- host_view->GetRenderWidgetHost())->ForwardMouseEvent(mouse_event);
- }
- }
- return FALSE;
- }
-
- static gboolean OnCrossingEvent(GtkWidget* widget,
- GdkEventCrossing* event,
- RenderWidgetHostViewGtk* host_view) {
- TRACE_EVENT0("browser",
- "RenderWidgetHostViewGtkWidget::OnCrossingEvent");
- const int any_button_mask =
- GDK_BUTTON1_MASK |
- GDK_BUTTON2_MASK |
- GDK_BUTTON3_MASK |
- GDK_BUTTON4_MASK |
- GDK_BUTTON5_MASK;
-
- // Only forward crossing events if the mouse button is not down.
- // (When the mouse button is down, the proper events are already being
- // sent by ButtonPressReleaseEvent and MouseMoveEvent, above, and if we
- // additionally send this crossing event with the state indicating the
- // button is down, it causes problems with drag and drop in WebKit.)
- if (!(event->state & any_button_mask)) {
- blink::WebMouseEvent mouse_event = WebMouseEventBuilder::Build(event);
- host_view->ModifyEventMovementAndCoords(&mouse_event);
- // When crossing out and back into a render view the movement values
- // must represent the instantaneous movement of the mouse, not the jump
- // from the exit to re-entry point.
- mouse_event.movementX = 0;
- mouse_event.movementY = 0;
- RenderWidgetHostImpl::From(
- host_view->GetRenderWidgetHost())->ForwardMouseEvent(mouse_event);
- }
-
- return FALSE;
- }
-
- static gboolean OnClientEvent(GtkWidget* widget,
- GdkEventClient* event,
- RenderWidgetHostViewGtk* host_view) {
- VLOG(1) << "client event type: " << event->message_type
- << " data_format: " << event->data_format
- << " data: " << event->data.l;
- return TRUE;
- }
-
- // Return the net up / down (or left / right) distance represented by events
- // in the events will be removed from the queue. We only look at the top of
- // queue...any other type of event will cause us not to look farther.
- // If there is a change to the set of modifier keys or scroll axis
- // in the events we will stop looking as well.
- static int GetPendingScrollDelta(bool vert, guint current_event_state) {
- int num_clicks = 0;
- GdkEvent* event;
- bool event_coalesced = true;
- while ((event = gdk_event_get()) && event_coalesced) {
- event_coalesced = false;
- if (event->type == GDK_SCROLL) {
- GdkEventScroll scroll = event->scroll;
- if (scroll.state & GDK_SHIFT_MASK) {
- if (scroll.direction == GDK_SCROLL_UP)
- scroll.direction = GDK_SCROLL_LEFT;
- else if (scroll.direction == GDK_SCROLL_DOWN)
- scroll.direction = GDK_SCROLL_RIGHT;
- }
- if (vert) {
- if (scroll.direction == GDK_SCROLL_UP ||
- scroll.direction == GDK_SCROLL_DOWN) {
- if (scroll.state == current_event_state) {
- num_clicks += (scroll.direction == GDK_SCROLL_UP ? 1 : -1);
- gdk_event_free(event);
- event_coalesced = true;
- }
- }
- } else {
- if (scroll.direction == GDK_SCROLL_LEFT ||
- scroll.direction == GDK_SCROLL_RIGHT) {
- if (scroll.state == current_event_state) {
- num_clicks += (scroll.direction == GDK_SCROLL_LEFT ? 1 : -1);
- gdk_event_free(event);
- event_coalesced = true;
- }
- }
- }
- }
- }
- // If we have an event left we put it back on the queue.
- if (event) {
- gdk_event_put(event);
- gdk_event_free(event);
- }
- return num_clicks * WebMouseWheelEventBuilder::ScrollbarPixelsPerTick();
- }
-
- static gboolean OnMouseScrollEvent(GtkWidget* widget,
- GdkEventScroll* event,
- RenderWidgetHostViewGtk* host_view) {
- TRACE_EVENT0("browser",
- "RenderWidgetHostViewGtkWidget::OnMouseScrollEvent");
- // If the user is holding shift, translate it into a horizontal scroll. We
- // don't care what other modifiers the user may be holding (zooming is
- // handled at the WebContentsView level).
- if (event->state & GDK_SHIFT_MASK) {
- if (event->direction == GDK_SCROLL_UP)
- event->direction = GDK_SCROLL_LEFT;
- else if (event->direction == GDK_SCROLL_DOWN)
- event->direction = GDK_SCROLL_RIGHT;
- }
-
- WebMouseWheelEvent web_event = WebMouseWheelEventBuilder::Build(event);
- const float pixelsPerTick =
- WebMouseWheelEventBuilder::ScrollbarPixelsPerTick();
- // We peek ahead at the top of the queue to look for additional pending
- // scroll events.
- if (event->direction == GDK_SCROLL_UP ||
- event->direction == GDK_SCROLL_DOWN) {
- if (event->direction == GDK_SCROLL_UP)
- web_event.deltaY = pixelsPerTick;
- else
- web_event.deltaY = -pixelsPerTick;
- web_event.deltaY += GetPendingScrollDelta(true, event->state);
- } else {
- if (event->direction == GDK_SCROLL_LEFT)
- web_event.deltaX = pixelsPerTick;
- else
- web_event.deltaX = -pixelsPerTick;
- web_event.deltaX += GetPendingScrollDelta(false, event->state);
- }
- RenderWidgetHostImpl::From(
- host_view->GetRenderWidgetHost())->ForwardWheelEvent(web_event);
- return FALSE;
- }
-
- DISALLOW_IMPLICIT_CONSTRUCTORS(RenderWidgetHostViewGtkWidget);
-};
-
-RenderWidgetHostViewGtk::RenderWidgetHostViewGtk(RenderWidgetHost* widget_host)
- : host_(RenderWidgetHostImpl::From(widget_host)),
- about_to_validate_and_paint_(false),
- is_loading_(false),
- parent_(NULL),
- is_popup_first_mouse_release_(true),
- was_imcontext_focused_before_grab_(false),
- do_x_grab_(false),
- is_fullscreen_(false),
- made_active_(false),
- mouse_is_being_warped_to_unlocked_position_(false),
- destroy_handler_id_(0),
- dragged_at_horizontal_edge_(0),
- dragged_at_vertical_edge_(0),
- compositing_surface_(gfx::kNullPluginWindow),
- last_mouse_down_(NULL) {
- host_->SetView(this);
-}
-
-RenderWidgetHostViewGtk::~RenderWidgetHostViewGtk() {
- UnlockMouse();
- set_last_mouse_down(NULL);
- view_.Destroy();
-}
-
-bool RenderWidgetHostViewGtk::OnMessageReceived(const IPC::Message& message) {
- bool handled = true;
- IPC_BEGIN_MESSAGE_MAP(RenderWidgetHostViewGtk, message)
- IPC_MESSAGE_HANDLER(ViewHostMsg_CreatePluginContainer,
- OnCreatePluginContainer)
- IPC_MESSAGE_HANDLER(ViewHostMsg_DestroyPluginContainer,
- OnDestroyPluginContainer)
- IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP()
- return handled;
-}
-
-void RenderWidgetHostViewGtk::InitAsChild(
- gfx::NativeView parent_view) {
- DoSharedInit();
- gtk_widget_show(view_.get());
-}
-
-void RenderWidgetHostViewGtk::InitAsPopup(
- RenderWidgetHostView* parent_host_view, const gfx::Rect& pos) {
- // If we aren't a popup, then |window| will be leaked.
- DCHECK(IsPopup());
-
- DoSharedInit();
- parent_ = parent_host_view->GetNativeView();
- GtkWindow* window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_POPUP));
- gtk_container_add(GTK_CONTAINER(window), view_.get());
- DoPopupOrFullscreenInit(window, pos);
-
- // Grab all input for the app. If a click lands outside the bounds of the
- // popup, WebKit will notice and destroy us. The underlying X window needs to
- // be created and mapped by the above code before we can grab the input
- // devices.
- if (NeedsInputGrab()) {
- // If our parent is in a widget hierarchy that ends with a window, add
- // ourselves to the same window group to make sure that our GTK grab
- // covers it.
- GtkWidget* toplevel = gtk_widget_get_toplevel(parent_);
- if (toplevel &&
- GTK_WIDGET_TOPLEVEL(toplevel) &&
- GTK_IS_WINDOW(toplevel)) {
- gtk_window_group_add_window(
- gtk_window_get_group(GTK_WINDOW(toplevel)), window);
- }
-
- // Install an application-level GTK grab to make sure that we receive all of
- // the app's input.
- gtk_grab_add(view_.get());
-
- // We need to install an X grab as well. However if the app already has an X
- // grab (as in the case of extension popup), an app grab will suffice.
- do_x_grab_ = !gdk_pointer_is_grabbed();
- if (do_x_grab_) {
- // Install the grab on behalf our parent window if it and all of its
- // ancestors are mapped; otherwise, just use ourselves (maybe we're being
- // shown on behalf of an inactive tab).
- GdkWindow* grab_window = gtk_widget_get_window(parent_);
- if (!grab_window || !gdk_window_is_viewable(grab_window))
- grab_window = gtk_widget_get_window(view_.get());
-
- gdk_pointer_grab(
- grab_window,
- TRUE, // Only events outside of the window are reported with
- // respect to |parent_->window|.
- static_cast<GdkEventMask>(GDK_BUTTON_PRESS_MASK |
- GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK),
- NULL,
- NULL,
- GDK_CURRENT_TIME);
- // We grab keyboard events too so things like alt+tab are eaten.
- gdk_keyboard_grab(grab_window, TRUE, GDK_CURRENT_TIME);
- }
- }
-}
-
-void RenderWidgetHostViewGtk::InitAsFullscreen(
- RenderWidgetHostView* reference_host_view) {
- DCHECK(reference_host_view);
- DoSharedInit();
-
- is_fullscreen_ = true;
- GtkWindow* window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
- gtk_window_set_decorated(window, FALSE);
- destroy_handler_id_ = g_signal_connect(GTK_WIDGET(window),
- "destroy",
- G_CALLBACK(OnDestroyThunk),
- this);
- gtk_container_add(GTK_CONTAINER(window), view_.get());
-
- // Try to move and resize the window to cover the screen in case the window
- // manager doesn't support _NET_WM_STATE_FULLSCREEN.
- GdkScreen* screen = gtk_window_get_screen(window);
- GdkWindow* ref_gdk_window = gtk_widget_get_window(
- reference_host_view->GetNativeView());
-
- gfx::Rect bounds;
- if (ref_gdk_window) {
- const int monitor_id = gdk_screen_get_monitor_at_window(screen,
- ref_gdk_window);
- GdkRectangle monitor_rect;
- gdk_screen_get_monitor_geometry(screen, monitor_id, &monitor_rect);
- bounds = gfx::Rect(monitor_rect);
- } else {
- bounds = gfx::Rect(
- 0, 0, gdk_screen_get_width(screen), gdk_screen_get_height(screen));
- }
- gtk_window_move(window, bounds.x(), bounds.y());
- gtk_window_resize(window, bounds.width(), bounds.height());
- gtk_window_fullscreen(window);
- DoPopupOrFullscreenInit(window, bounds);
-}
-
-RenderWidgetHost* RenderWidgetHostViewGtk::GetRenderWidgetHost() const {
- return host_;
-}
-
-void RenderWidgetHostViewGtk::WasShown() {
- if (!host_ || !host_->is_hidden())
- return;
-
- if (web_contents_switch_paint_time_.is_null())
- web_contents_switch_paint_time_ = base::TimeTicks::Now();
-
- host_->WasShown();
-}
-
-void RenderWidgetHostViewGtk::WasHidden() {
- if (!host_ || host_->is_hidden())
- return;
-
- // If we have a renderer, then inform it that we are being hidden so it can
- // reduce its resource utilization.
- host_->WasHidden();
-
- web_contents_switch_paint_time_ = base::TimeTicks();
-}
-
-void RenderWidgetHostViewGtk::SetSize(const gfx::Size& size) {
- int width = std::min(size.width(), kMaxWindowWidth);
- int height = std::min(size.height(), kMaxWindowHeight);
- if (IsPopup()) {
- // We're a popup, honor the size request.
- gtk_widget_set_size_request(view_.get(), width, height);
- }
-
- // Update the size of the RWH.
- if (requested_size_.width() != width ||
- requested_size_.height() != height) {
- requested_size_ = gfx::Size(width, height);
- host_->SendScreenRects();
- host_->WasResized();
- }
-}
-
-void RenderWidgetHostViewGtk::SetBounds(const gfx::Rect& rect) {
- // This is called when webkit has sent us a Move message.
- if (IsPopup()) {
- gtk_window_move(GTK_WINDOW(gtk_widget_get_toplevel(view_.get())),
- rect.x(), rect.y());
- }
-
- SetSize(rect.size());
-}
-
-gfx::NativeView RenderWidgetHostViewGtk::GetNativeView() const {
- return view_.get();
-}
-
-gfx::NativeViewId RenderWidgetHostViewGtk::GetNativeViewId() const {
- return GtkNativeViewManager::GetInstance()->GetIdForWidget(view_.get());
-}
-
-gfx::NativeViewAccessible RenderWidgetHostViewGtk::GetNativeViewAccessible() {
- NOTIMPLEMENTED();
- return NULL;
-}
-
-void RenderWidgetHostViewGtk::MovePluginWindows(
- const gfx::Vector2d& scroll_offset,
- const std::vector<WebPluginGeometry>& moves) {
- for (size_t i = 0; i < moves.size(); ++i) {
- plugin_container_manager_.MovePluginContainer(moves[i]);
- }
-}
-
-void RenderWidgetHostViewGtk::Focus() {
- gtk_widget_grab_focus(view_.get());
-}
-
-void RenderWidgetHostViewGtk::Blur() {
- // TODO(estade): We should be clearing native focus as well, but I know of no
- // way to do that without focusing another widget.
- host_->Blur();
-}
-
-bool RenderWidgetHostViewGtk::HasFocus() const {
- return gtk_widget_has_focus(view_.get());
-}
-
-void RenderWidgetHostViewGtk::ActiveWindowChanged(GdkWindow* window) {
- GdkWindow* our_window = gtk_widget_get_parent_window(view_.get());
-
- if (our_window == window)
- made_active_ = true;
-
- // If the window was previously active, but isn't active anymore, shut it
- // down.
- if (is_fullscreen_ && our_window != window && made_active_)
- host_->Shutdown();
-}
-
-bool RenderWidgetHostViewGtk::Send(IPC::Message* message) {
- return host_->Send(message);
-}
-
-bool RenderWidgetHostViewGtk::IsSurfaceAvailableForCopy() const {
- return true;
-}
-
-void RenderWidgetHostViewGtk::Show() {
- gtk_widget_show(view_.get());
- WasShown();
-}
-
-void RenderWidgetHostViewGtk::Hide() {
- gtk_widget_hide(view_.get());
- WasHidden();
-}
-
-bool RenderWidgetHostViewGtk::IsShowing() {
- return gtk_widget_get_visible(view_.get());
-}
-
-gfx::Rect RenderWidgetHostViewGtk::GetViewBounds() const {
- GdkWindow* gdk_window = gtk_widget_get_window(view_.get());
- if (!gdk_window)
- return gfx::Rect(requested_size_);
- GdkRectangle window_rect;
- gdk_window_get_origin(gdk_window, &window_rect.x, &window_rect.y);
- return gfx::Rect(window_rect.x, window_rect.y,
- requested_size_.width(), requested_size_.height());
-}
-
-void RenderWidgetHostViewGtk::UpdateCursor(const WebCursor& cursor) {
- // Optimize the common case, where the cursor hasn't changed.
- // However, we can switch between different pixmaps, so only on the
- // non-pixmap branch.
- if (current_cursor_.GetCursorType() != GDK_CURSOR_IS_PIXMAP &&
- current_cursor_.GetCursorType() == cursor.GetCursorType()) {
- return;
- }
-
- current_cursor_ = cursor;
- ShowCurrentCursor();
-}
-
-void RenderWidgetHostViewGtk::SetIsLoading(bool is_loading) {
- is_loading_ = is_loading;
- // Only call ShowCurrentCursor() when it will actually change the cursor.
- if (current_cursor_.GetCursorType() == GDK_LAST_CURSOR)
- ShowCurrentCursor();
-}
-
-void RenderWidgetHostViewGtk::TextInputTypeChanged(
- ui::TextInputType type,
- ui::TextInputMode input_mode,
- bool can_compose_inline) {
- im_context_->UpdateInputMethodState(type, can_compose_inline);
-}
-
-void RenderWidgetHostViewGtk::ImeCancelComposition() {
- im_context_->CancelComposition();
-}
-
-void RenderWidgetHostViewGtk::DidUpdateBackingStore(
- const gfx::Rect& scroll_rect,
- const gfx::Vector2d& scroll_delta,
- const std::vector<gfx::Rect>& copy_rects,
- const ui::LatencyInfo& latency_info) {
- TRACE_EVENT0("ui::gtk", "RenderWidgetHostViewGtk::DidUpdateBackingStore");
- software_latency_info_.MergeWith(latency_info);
-
- if (host_->is_hidden())
- return;
-
- // TODO(darin): Implement the equivalent of Win32's ScrollWindowEX. Can that
- // be done using XCopyArea? Perhaps similar to
- // BackingStore::ScrollBackingStore?
- if (about_to_validate_and_paint_)
- invalid_rect_.Union(scroll_rect);
- else
- Paint(scroll_rect);
-
- for (size_t i = 0; i < copy_rects.size(); ++i) {
- // Avoid double painting. NOTE: This is only relevant given the call to
- // Paint(scroll_rect) above.
- gfx::Rect rect = gfx::SubtractRects(copy_rects[i], scroll_rect);
- if (rect.IsEmpty())
- continue;
-
- if (about_to_validate_and_paint_)
- invalid_rect_.Union(rect);
- else
- Paint(rect);
- }
-}
-
-void RenderWidgetHostViewGtk::RenderProcessGone(base::TerminationStatus status,
- int error_code) {
- Destroy();
- plugin_container_manager_.set_host_widget(NULL);
-}
-
-void RenderWidgetHostViewGtk::Destroy() {
- if (compositing_surface_ != gfx::kNullPluginWindow) {
- GtkNativeViewManager* manager = GtkNativeViewManager::GetInstance();
- manager->ReleasePermanentXID(compositing_surface_);
- }
-
- if (do_x_grab_) {
- // Undo the X grab.
- GdkDisplay* display = gtk_widget_get_display(parent_);
- gdk_display_pointer_ungrab(display, GDK_CURRENT_TIME);
- gdk_display_keyboard_ungrab(display, GDK_CURRENT_TIME);
- }
-
- if (view_.get()) {
- // If this is a popup or fullscreen widget, then we need to destroy the
- // window that we created to hold it.
- if (IsPopup() || is_fullscreen_) {
- GtkWidget* window = gtk_widget_get_parent(view_.get());
-
- ui::ActiveWindowWatcherX::RemoveObserver(this);
-
- // Disconnect the destroy handler so that we don't try to shutdown twice.
- if (is_fullscreen_)
- g_signal_handler_disconnect(window, destroy_handler_id_);
-
- gtk_widget_destroy(window);
- }
-
- // Remove |view_| from all containers now, so nothing else can hold a
- // reference to |view_|'s widget except possibly a gtk signal handler if
- // this code is currently executing within the context of a gtk signal
- // handler. Note that |view_| is still alive after this call. It will be
- // deallocated in the destructor.
- // See http://crbug.com/11847 for details.
- gtk_widget_destroy(view_.get());
- }
-
- // The RenderWidgetHost's destruction led here, so don't call it.
- host_ = NULL;
-
- base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
-}
-
-void RenderWidgetHostViewGtk::SetTooltipText(
- const base::string16& tooltip_text) {
- // Maximum number of characters we allow in a tooltip.
- const int kMaxTooltipLength = 8 << 10;
- // Clamp the tooltip length to kMaxTooltipLength so that we don't
- // accidentally DOS the user with a mega tooltip (since GTK doesn't do
- // this itself).
- // I filed https://bugzilla.gnome.org/show_bug.cgi?id=604641 upstream.
- const base::string16 clamped_tooltip =
- gfx::TruncateString(tooltip_text, kMaxTooltipLength);
-
- if (clamped_tooltip.empty()) {
- gtk_widget_set_has_tooltip(view_.get(), FALSE);
- } else {
- gtk_widget_set_tooltip_text(view_.get(),
- UTF16ToUTF8(clamped_tooltip).c_str());
- }
-}
-
-void RenderWidgetHostViewGtk::SelectionChanged(const base::string16& text,
- size_t offset,
- const gfx::Range& range) {
- RenderWidgetHostViewBase::SelectionChanged(text, offset, range);
-
- if (text.empty() || range.is_empty())
- return;
- size_t pos = range.GetMin() - offset;
- size_t n = range.length();
-
- DCHECK(pos + n <= text.length()) << "The text can not fully cover range.";
- if (pos >= text.length()) {
- NOTREACHED() << "The text can not cover range.";
- return;
- }
-
- // Set the CLIPBOARD_TYPE SELECTION to the ui::Clipboard.
- ui::ScopedClipboardWriter clipboard_writer(
- ui::Clipboard::GetForCurrentThread(),
- ui::CLIPBOARD_TYPE_SELECTION);
- clipboard_writer.WriteText(text.substr(pos, n));
-}
-
-void RenderWidgetHostViewGtk::SelectionBoundsChanged(
- const ViewHostMsg_SelectionBounds_Params& params) {
- im_context_->UpdateCaretBounds(
- gfx::UnionRects(params.anchor_rect, params.focus_rect));
-}
-
-void RenderWidgetHostViewGtk::ScrollOffsetChanged() {
-}
-
-GdkEventButton* RenderWidgetHostViewGtk::GetLastMouseDown() {
- return last_mouse_down_;
-}
-
-gfx::NativeView RenderWidgetHostViewGtk::BuildInputMethodsGtkMenu() {
- return im_context_->BuildInputMethodsGtkMenu();
-}
-
-void RenderWidgetHostViewGtk::OnDestroy(GtkWidget* widget) {
- DCHECK(is_fullscreen_);
- host_->Shutdown();
-}
-
-bool RenderWidgetHostViewGtk::NeedsInputGrab() {
- return popup_type_ == blink::WebPopupTypeSelect;
-}
-
-bool RenderWidgetHostViewGtk::IsPopup() const {
- return popup_type_ != blink::WebPopupTypeNone;
-}
-
-void RenderWidgetHostViewGtk::DoSharedInit() {
- view_.Own(RenderWidgetHostViewGtkWidget::CreateNewWidget(this));
- im_context_.reset(new GtkIMContextWrapper(this));
- plugin_container_manager_.set_host_widget(view_.get());
- key_bindings_handler_.reset(new GtkKeyBindingsHandler(view_.get()));
-}
-
-void RenderWidgetHostViewGtk::DoPopupOrFullscreenInit(GtkWindow* window,
- const gfx::Rect& bounds) {
- requested_size_.SetSize(std::min(bounds.width(), kMaxWindowWidth),
- std::min(bounds.height(), kMaxWindowHeight));
- host_->WasResized();
-
- ui::ActiveWindowWatcherX::AddObserver(this);
-
- // Don't set the size when we're going fullscreen. This can confuse the
- // window manager into thinking we're resizing a fullscreen window and
- // therefore not fullscreen anymore.
- if (!is_fullscreen_) {
- gtk_widget_set_size_request(
- view_.get(), requested_size_.width(), requested_size_.height());
-
- // Don't allow the window to be resized. This also forces the window to
- // shrink down to the size of its child contents.
- gtk_window_set_resizable(window, FALSE);
- gtk_window_set_default_size(window, -1, -1);
- gtk_window_move(window, bounds.x(), bounds.y());
- }
-
- gtk_widget_show_all(GTK_WIDGET(window));
-}
-
-BackingStore* RenderWidgetHostViewGtk::AllocBackingStore(
- const gfx::Size& size) {
- gint depth = gdk_visual_get_depth(gtk_widget_get_visual(view_.get()));
- return new BackingStoreGtk(host_, size,
- ui::GetVisualFromGtkWidget(view_.get()),
- depth);
-}
-
-// NOTE: |output| is initialized with the size of |src_subrect|, and |dst_size|
-// is ignored on GTK.
-void RenderWidgetHostViewGtk::CopyFromCompositingSurface(
- const gfx::Rect& src_subrect,
- const gfx::Size& /* dst_size */,
- const base::Callback<void(bool, const SkBitmap&)>& callback) {
- // Grab the snapshot from the renderer as that's the only reliable way to
- // readback from the GPU for this platform right now.
- GetRenderWidgetHost()->GetSnapshotFromRenderer(src_subrect, callback);
-}
-
-void RenderWidgetHostViewGtk::CopyFromCompositingSurfaceToVideoFrame(
- const gfx::Rect& src_subrect,
- const scoped_refptr<media::VideoFrame>& target,
- const base::Callback<void(bool)>& callback) {
- NOTIMPLEMENTED();
- callback.Run(false);
-}
-
-bool RenderWidgetHostViewGtk::CanCopyToVideoFrame() const {
- return false;
-}
-
-void RenderWidgetHostViewGtk::AcceleratedSurfaceInitialized(int host_id,
- int route_id) {
-}
-
-void RenderWidgetHostViewGtk::AcceleratedSurfaceBuffersSwapped(
- const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params,
- int gpu_host_id) {
- AcceleratedSurfaceMsg_BufferPresented_Params ack_params;
- ack_params.sync_point = 0;
- RenderWidgetHostImpl::AcknowledgeBufferPresent(
- params.route_id, gpu_host_id, ack_params);
- RenderWidgetHostImpl::CompositorFrameDrawn(params.latency_info);
-}
-
-void RenderWidgetHostViewGtk::AcceleratedSurfacePostSubBuffer(
- const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params,
- int gpu_host_id) {
- AcceleratedSurfaceMsg_BufferPresented_Params ack_params;
- ack_params.sync_point = 0;
- RenderWidgetHostImpl::AcknowledgeBufferPresent(
- params.route_id, gpu_host_id, ack_params);
- RenderWidgetHostImpl::CompositorFrameDrawn(params.latency_info);
-}
-
-void RenderWidgetHostViewGtk::AcceleratedSurfaceSuspend() {
-}
-
-void RenderWidgetHostViewGtk::AcceleratedSurfaceRelease() {
-}
-
-bool RenderWidgetHostViewGtk::HasAcceleratedSurface(
- const gfx::Size& desired_size) {
- // TODO(jbates) Implement this so this view can use GetBackingStore for both
- // software and GPU frames. Defaulting to false just makes GetBackingStore
- // only useable for software frames.
- return false;
-}
-
-void RenderWidgetHostViewGtk::SetBackground(const SkBitmap& background) {
- RenderWidgetHostViewBase::SetBackground(background);
- Send(new ViewMsg_SetBackground(host_->GetRoutingID(), background));
-}
-
-void RenderWidgetHostViewGtk::ModifyEventForEdgeDragging(
- GtkWidget* widget, GdkEventMotion* event) {
- // If the widget is aligned with an edge of the monitor its on and the user
- // attempts to drag past that edge we track the number of times it has
- // occurred, so that we can force the widget to scroll when it otherwise
- // would be unable to, by modifying the (x,y) position in the drag
- // event that we forward on to webkit. If we get a move that's no longer a
- // drag or a drag indicating the user is no longer at that edge we stop
- // altering the drag events.
- int new_dragged_at_horizontal_edge = 0;
- int new_dragged_at_vertical_edge = 0;
- // Used for checking the edges of the monitor. We cache the values to save
- // roundtrips to the X server.
- CR_DEFINE_STATIC_LOCAL(gfx::Size, drag_monitor_size, ());
- if (event->state & GDK_BUTTON1_MASK) {
- if (drag_monitor_size.IsEmpty()) {
- // We can safely cache the monitor size for the duration of a drag.
- GdkScreen* screen = gtk_widget_get_screen(widget);
- int monitor =
- gdk_screen_get_monitor_at_point(screen, event->x_root, event->y_root);
- GdkRectangle geometry;
- gdk_screen_get_monitor_geometry(screen, monitor, &geometry);
- drag_monitor_size.SetSize(geometry.width, geometry.height);
- }
- GtkAllocation allocation;
- gtk_widget_get_allocation(widget, &allocation);
- // Check X and Y independently, as the user could be dragging into a corner.
- if (event->x == 0 && event->x_root == 0) {
- new_dragged_at_horizontal_edge = dragged_at_horizontal_edge_ - 1;
- } else if (allocation.width - 1 == static_cast<gint>(event->x) &&
- drag_monitor_size.width() - 1 == static_cast<gint>(event->x_root)) {
- new_dragged_at_horizontal_edge = dragged_at_horizontal_edge_ + 1;
- }
-
- if (event->y == 0 && event->y_root == 0) {
- new_dragged_at_vertical_edge = dragged_at_vertical_edge_ - 1;
- } else if (allocation.height - 1 == static_cast<gint>(event->y) &&
- drag_monitor_size.height() - 1 == static_cast<gint>(event->y_root)) {
- new_dragged_at_vertical_edge = dragged_at_vertical_edge_ + 1;
- }
-
- event->x_root += new_dragged_at_horizontal_edge;
- event->x += new_dragged_at_horizontal_edge;
- event->y_root += new_dragged_at_vertical_edge;
- event->y += new_dragged_at_vertical_edge;
- } else {
- // Clear whenever we get a non-drag mouse move.
- drag_monitor_size.SetSize(0, 0);
- }
- dragged_at_horizontal_edge_ = new_dragged_at_horizontal_edge;
- dragged_at_vertical_edge_ = new_dragged_at_vertical_edge;
-}
-
-void RenderWidgetHostViewGtk::Paint(const gfx::Rect& damage_rect) {
- TRACE_EVENT0("ui::gtk", "RenderWidgetHostViewGtk::Paint");
-
- // If the GPU process is rendering directly into the View,
- // call the compositor directly.
- RenderWidgetHostImpl* render_widget_host =
- RenderWidgetHostImpl::From(GetRenderWidgetHost());
- if (render_widget_host->is_accelerated_compositing_active()) {
- host_->ScheduleComposite();
- return;
- }
-
- GdkWindow* window = gtk_widget_get_window(view_.get());
- DCHECK(!about_to_validate_and_paint_);
-
- invalid_rect_ = damage_rect;
- about_to_validate_and_paint_ = true;
-
- // If the size of our canvas is (0,0), then we don't want to block here. We
- // are doing one of our first paints and probably have animations going on.
- bool force_create = !host_->empty();
- BackingStoreGtk* backing_store = static_cast<BackingStoreGtk*>(
- host_->GetBackingStore(force_create));
- // Calling GetBackingStore maybe have changed |invalid_rect_|...
- about_to_validate_and_paint_ = false;
-
- gfx::Rect paint_rect = gfx::Rect(0, 0, kMaxWindowWidth, kMaxWindowHeight);
- paint_rect.Intersect(invalid_rect_);
-
- if (backing_store) {
- // Only render the widget if it is attached to a window; there's a short
- // period where this object isn't attached to a window but hasn't been
- // Destroy()ed yet and it receives paint messages...
- if (window) {
- backing_store->XShowRect(gfx::Point(0, 0),
- paint_rect, ui::GetX11WindowFromGtkWidget(view_.get()));
- }
- if (!whiteout_start_time_.is_null()) {
- base::TimeDelta whiteout_duration = base::TimeTicks::Now() -
- whiteout_start_time_;
- UMA_HISTOGRAM_TIMES("MPArch.RWHH_WhiteoutDuration", whiteout_duration);
-
- // Reset the start time to 0 so that we start recording again the next
- // time the backing store is NULL...
- whiteout_start_time_ = base::TimeTicks();
- }
- if (!web_contents_switch_paint_time_.is_null()) {
- base::TimeDelta web_contents_switch_paint_duration =
- base::TimeTicks::Now() - web_contents_switch_paint_time_;
- UMA_HISTOGRAM_TIMES("MPArch.RWH_TabSwitchPaintDuration",
- web_contents_switch_paint_duration);
- // Reset web_contents_switch_paint_time_ to 0 so future tab selections are
- // recorded.
- web_contents_switch_paint_time_ = base::TimeTicks();
- }
- software_latency_info_.AddLatencyNumber(
- ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0);
- render_widget_host->FrameSwapped(software_latency_info_);
- software_latency_info_.Clear();
- } else {
- if (window)
- gdk_window_clear(window);
- if (whiteout_start_time_.is_null())
- whiteout_start_time_ = base::TimeTicks::Now();
- }
-}
-
-void RenderWidgetHostViewGtk::ShowCurrentCursor() {
- // The widget may not have a window. If that's the case, abort mission. This
- // is the same issue as that explained above in Paint().
- if (!gtk_widget_get_window(view_.get()))
- return;
-
- // TODO(port): WebKit bug https://bugs.webkit.org/show_bug.cgi?id=16388 is
- // that calling gdk_window_set_cursor repeatedly is expensive. We should
- // avoid it here where possible.
- GdkCursor* gdk_cursor;
- if (current_cursor_.GetCursorType() == GDK_LAST_CURSOR) {
- // Use MOZ_CURSOR_SPINNING if we are showing the default cursor and
- // the page is loading.
- gdk_cursor = is_loading_ ? GetMozSpinningCursor() : NULL;
- } else {
- gdk_cursor = current_cursor_.GetNativeCursor();
- }
- gdk_window_set_cursor(gtk_widget_get_window(view_.get()), gdk_cursor);
-}
-
-void RenderWidgetHostViewGtk::SetHasHorizontalScrollbar(
- bool has_horizontal_scrollbar) {
-}
-
-void RenderWidgetHostViewGtk::SetScrollOffsetPinning(
- bool is_pinned_to_left, bool is_pinned_to_right) {
-}
-
-
-void RenderWidgetHostViewGtk::OnAcceleratedCompositingStateChange() {
- bool activated = host_->is_accelerated_compositing_active();
- GtkPreserveWindow* widget = reinterpret_cast<GtkPreserveWindow*>(view_.get());
-
- gtk_preserve_window_delegate_resize(widget, activated);
-}
-
-void RenderWidgetHostViewGtk::GetScreenInfo(WebScreenInfo* results) {
- GdkWindow* gdk_window = gtk_widget_get_window(view_.get());
- if (!gdk_window) {
- GdkDisplay* display = gdk_display_get_default();
- gdk_window = gdk_display_get_default_group(display);
- }
- if (!gdk_window)
- return;
- GetScreenInfoFromNativeWindow(gdk_window, results);
-}
-
-gfx::Rect RenderWidgetHostViewGtk::GetBoundsInRootWindow() {
- GtkWidget* toplevel = gtk_widget_get_toplevel(view_.get());
- if (!toplevel)
- return GetViewBounds();
-
- GdkRectangle frame_extents;
- GdkWindow* gdk_window = gtk_widget_get_window(toplevel);
- if (!gdk_window)
- return GetViewBounds();
-
- gdk_window_get_frame_extents(gdk_window, &frame_extents);
- return gfx::Rect(frame_extents.x, frame_extents.y,
- frame_extents.width, frame_extents.height);
-}
-
-gfx::GLSurfaceHandle RenderWidgetHostViewGtk::GetCompositingSurface() {
- if (compositing_surface_ == gfx::kNullPluginWindow) {
- GtkNativeViewManager* manager = GtkNativeViewManager::GetInstance();
- gfx::NativeViewId view_id = GetNativeViewId();
-
- if (!manager->GetPermanentXIDForId(&compositing_surface_, view_id)) {
- DLOG(ERROR) << "Can't find XID for view id " << view_id;
- }
- }
- return gfx::GLSurfaceHandle(compositing_surface_, gfx::NATIVE_TRANSPORT);
-}
-
-void RenderWidgetHostViewGtk::ResizeCompositingSurface(const gfx::Size& size) {
- GtkWidget* widget = view_.get();
- GdkWindow* window = gtk_widget_get_window(widget);
- if (window) {
- Display* display = GDK_WINDOW_XDISPLAY(window);
- gdk_window_resize(window, size.width(), size.height());
- XSync(display, False);
- }
-}
-
-bool RenderWidgetHostViewGtk::LockMouse() {
- if (mouse_locked_)
- return true;
-
- mouse_locked_ = true;
-
- // Release any current grab.
- GtkWidget* current_grab_window = gtk_grab_get_current();
- if (current_grab_window) {
- gtk_grab_remove(current_grab_window);
- LOG(WARNING) << "Locking Mouse with gdk_pointer_grab, "
- << "but had to steal grab from another window";
- }
-
- GtkWidget* widget = view_.get();
- GdkWindow* window = gtk_widget_get_window(widget);
- GdkCursor* cursor = gdk_cursor_new(GDK_BLANK_CURSOR);
-
- GdkGrabStatus grab_status =
- gdk_pointer_grab(window,
- FALSE, // owner_events
- static_cast<GdkEventMask>(
- GDK_POINTER_MOTION_MASK |
- GDK_BUTTON_PRESS_MASK |
- GDK_BUTTON_RELEASE_MASK),
- window, // confine_to
- cursor,
- GDK_CURRENT_TIME);
-
- if (grab_status != GDK_GRAB_SUCCESS) {
- LOG(WARNING) << "Failed to grab pointer for LockMouse. "
- << "gdk_pointer_grab returned: " << grab_status;
- mouse_locked_ = false;
- return false;
- }
-
- // Clear the tooltip window.
- SetTooltipText(base::string16());
-
- // Ensure that the widget center location will be relevant for this mouse
- // lock session. It is updated whenever the window geometry moves
- // but may be out of date due to switching tabs.
- MarkCachedWidgetCenterStale();
-
- // Ensure that if we were previously warping the cursor to a specific point
- // that we no longer track doing so when entering lock. It should be cleared
- // by the cursor moving to the warp point, and this shouldn't be necessary.
- // But, this is a small effort to ensure robustness in the event a warp isn't
- // completed.
- mouse_is_being_warped_to_unlocked_position_ = false;
-
- return true;
-}
-
-void RenderWidgetHostViewGtk::UnlockMouse() {
- if (!mouse_locked_)
- return;
-
- mouse_locked_ = false;
-
- GtkWidget* widget = view_.get();
- GdkDisplay* display = gtk_widget_get_display(widget);
- GdkScreen* screen = gtk_widget_get_screen(widget);
- gdk_display_pointer_ungrab(display, GDK_CURRENT_TIME);
- gdk_display_warp_pointer(display, screen,
- unlocked_global_mouse_position_.x(),
- unlocked_global_mouse_position_.y());
- mouse_is_being_warped_to_unlocked_position_ = true;
-
- if (host_)
- host_->LostMouseLock();
-}
-
-void RenderWidgetHostViewGtk::ForwardKeyboardEvent(
- const NativeWebKeyboardEvent& event) {
- if (!host_)
- return;
-
- EditCommands edit_commands;
- if (!event.skip_in_browser &&
- key_bindings_handler_->Match(event, &edit_commands)) {
- Send(new InputMsg_SetEditCommandsForNextKeyEvent(
- host_->GetRoutingID(), edit_commands));
- NativeWebKeyboardEvent copy_event(event);
- copy_event.match_edit_command = true;
- host_->ForwardKeyboardEvent(copy_event);
- return;
- }
-
- host_->ForwardKeyboardEvent(event);
-}
-
-bool RenderWidgetHostViewGtk::RetrieveSurrounding(std::string* text,
- size_t* cursor_index) {
- if (!selection_range_.IsValid())
- return false;
-
- size_t offset = selection_range_.GetMin() - selection_text_offset_;
- DCHECK(offset <= selection_text_.length());
-
- if (offset == selection_text_.length()) {
- *text = UTF16ToUTF8(selection_text_);
- *cursor_index = text->length();
- return true;
- }
-
- *text = base::UTF16ToUTF8AndAdjustOffset(
- base::StringPiece16(selection_text_), &offset);
- if (offset == base::string16::npos) {
- NOTREACHED() << "Invalid offset in UTF16 string.";
- return false;
- }
- *cursor_index = offset;
- return true;
-}
-
-void RenderWidgetHostViewGtk::set_last_mouse_down(GdkEventButton* event) {
- GdkEventButton* temp = NULL;
- if (event) {
- temp = reinterpret_cast<GdkEventButton*>(
- gdk_event_copy(reinterpret_cast<GdkEvent*>(event)));
- }
-
- if (last_mouse_down_)
- gdk_event_free(reinterpret_cast<GdkEvent*>(last_mouse_down_));
-
- last_mouse_down_ = temp;
-}
-
-void RenderWidgetHostViewGtk::MarkCachedWidgetCenterStale() {
- widget_center_valid_ = false;
- mouse_has_been_warped_to_new_center_ = false;
-}
-
-gfx::Point RenderWidgetHostViewGtk::GetWidgetCenter() {
- if (widget_center_valid_)
- return widget_center_;
-
- GdkWindow* window = gtk_widget_get_window(view_.get());
- gint window_x = 0;
- gint window_y = 0;
- gdk_window_get_origin(window, &window_x, &window_y);
- gint window_w = gdk_window_get_width(window);
- gint window_h = gdk_window_get_height(window);
- widget_center_.SetPoint(window_x + window_w / 2,
- window_y + window_h / 2);
- widget_center_valid_ = true;
- return widget_center_;
-}
-
-void RenderWidgetHostViewGtk::ModifyEventMovementAndCoords(
- blink::WebMouseEvent* event) {
- // Movement is computed by taking the difference of the new cursor position
- // and the previous. Under mouse lock the cursor will be warped back to the
- // center so that we are not limited by clipping boundaries.
- // We do not measure movement as the delta from cursor to center because
- // we may receive more mouse movement events before our warp has taken
- // effect.
- event->movementX = event->globalX - global_mouse_position_.x();
- event->movementY = event->globalY - global_mouse_position_.y();
-
- // While the cursor is being warped back to the unlocked position, suppress
- // the movement member data.
- if (mouse_is_being_warped_to_unlocked_position_) {
- event->movementX = 0;
- event->movementY = 0;
- if (MovedToPoint(*event, unlocked_global_mouse_position_))
- mouse_is_being_warped_to_unlocked_position_ = false;
- }
-
- global_mouse_position_.SetPoint(event->globalX, event->globalY);
-
- // Under mouse lock, coordinates of mouse are locked to what they were when
- // mouse lock was entered.
- if (mouse_locked_) {
- event->x = unlocked_mouse_position_.x();
- event->y = unlocked_mouse_position_.y();
- event->windowX = unlocked_mouse_position_.x();
- event->windowY = unlocked_mouse_position_.y();
- event->globalX = unlocked_global_mouse_position_.x();
- event->globalY = unlocked_global_mouse_position_.y();
- } else if (!mouse_is_being_warped_to_unlocked_position_) {
- unlocked_mouse_position_.SetPoint(event->windowX, event->windowY);
- unlocked_global_mouse_position_.SetPoint(event->globalX, event->globalY);
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// RenderWidgetHostView, public:
-
-// static
-RenderWidgetHostView* RenderWidgetHostView::CreateViewForWidget(
- RenderWidgetHost* widget) {
- return new RenderWidgetHostViewGtk(widget);
-}
-
-// static
-void RenderWidgetHostViewPort::GetDefaultScreenInfo(WebScreenInfo* results) {
- GdkWindow* gdk_window =
- gdk_display_get_default_group(gdk_display_get_default());
- GetScreenInfoFromNativeWindow(gdk_window, results);
-}
-
-void RenderWidgetHostViewGtk::SetAccessibilityFocus(int acc_obj_id) {
- if (!host_)
- return;
-
- host_->AccessibilitySetFocus(acc_obj_id);
-}
-
-void RenderWidgetHostViewGtk::AccessibilityDoDefaultAction(int acc_obj_id) {
- if (!host_)
- return;
-
- host_->AccessibilityDoDefaultAction(acc_obj_id);
-}
-
-void RenderWidgetHostViewGtk::AccessibilityScrollToMakeVisible(
- int acc_obj_id, gfx::Rect subfocus) {
- if (!host_)
- return;
-
- host_->AccessibilityScrollToMakeVisible(acc_obj_id, subfocus);
-}
-
-void RenderWidgetHostViewGtk::AccessibilityScrollToPoint(
- int acc_obj_id, gfx::Point point) {
- if (!host_)
- return;
-
- host_->AccessibilityScrollToPoint(acc_obj_id, point);
-}
-
-void RenderWidgetHostViewGtk::AccessibilitySetTextSelection(
- int acc_obj_id, int start_offset, int end_offset) {
- if (!host_)
- return;
-
- host_->AccessibilitySetTextSelection(acc_obj_id, start_offset, end_offset);
-}
-
-gfx::Point RenderWidgetHostViewGtk::GetLastTouchEventLocation() const {
- // Not needed on Linux.
- return gfx::Point();
-}
-
-void RenderWidgetHostViewGtk::FatalAccessibilityTreeError() {
- if (host_) {
- host_->FatalAccessibilityTreeError();
- SetBrowserAccessibilityManager(NULL);
- } else {
- CHECK(FALSE);
- }
-}
-
-void RenderWidgetHostViewGtk::OnAccessibilityEvents(
- const std::vector<AccessibilityHostMsg_EventParams>& params) {
- if (!GetBrowserAccessibilityManager()) {
- GtkWidget* parent = gtk_widget_get_parent(view_.get());
- SetBrowserAccessibilityManager(
- new BrowserAccessibilityManagerGtk(
- parent,
- BrowserAccessibilityManagerGtk::GetEmptyDocument(),
- this));
- }
- GetBrowserAccessibilityManager()->OnAccessibilityEvents(params);
-}
-
-AtkObject* RenderWidgetHostViewGtk::GetAccessible() {
- if (!GetBrowserAccessibilityManager()) {
- GtkWidget* parent = gtk_widget_get_parent(view_.get());
- SetBrowserAccessibilityManager(
- new BrowserAccessibilityManagerGtk(
- parent,
- BrowserAccessibilityManagerGtk::GetEmptyDocument(),
- this));
- }
- BrowserAccessibilityGtk* root =
- GetBrowserAccessibilityManager()->GetRoot()->ToBrowserAccessibilityGtk();
-
- atk_object_set_role(root->GetAtkObject(), ATK_ROLE_HTML_CONTAINER);
- return root->GetAtkObject();
-}
-
-void RenderWidgetHostViewGtk::OnCreatePluginContainer(
- gfx::PluginWindowHandle id) {
- plugin_container_manager_.CreatePluginContainer(id);
-}
-
-void RenderWidgetHostViewGtk::OnDestroyPluginContainer(
- gfx::PluginWindowHandle id) {
- plugin_container_manager_.DestroyPluginContainer(id);
-}
-
-} // namespace content
diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_gtk.h b/chromium/content/browser/renderer_host/render_widget_host_view_gtk.h
deleted file mode 100644
index e01181ab4cc..00000000000
--- a/chromium/content/browser/renderer_host/render_widget_host_view_gtk.h
+++ /dev/null
@@ -1,340 +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_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_GTK_H_
-#define CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_GTK_H_
-
-#include <gdk/gdk.h>
-
-#include <string>
-#include <vector>
-
-#include "base/memory/scoped_ptr.h"
-#include "base/time/time.h"
-#include "content/browser/accessibility/browser_accessibility_manager.h"
-#include "content/browser/renderer_host/gtk_plugin_container_manager.h"
-#include "content/browser/renderer_host/render_widget_host_view_base.h"
-#include "content/common/content_export.h"
-#include "ipc/ipc_sender.h"
-#include "ui/base/gtk/gtk_signal.h"
-#include "ui/base/gtk/gtk_signal_registrar.h"
-#include "ui/base/gtk/owned_widget_gtk.h"
-#include "ui/base/x/active_window_watcher_x_observer.h"
-#include "ui/gfx/native_widget_types.h"
-#include "ui/gfx/point.h"
-#include "ui/gfx/rect.h"
-#include "webkit/common/cursors/webcursor.h"
-
-typedef struct _GtkClipboard GtkClipboard;
-typedef struct _GtkSelectionData GtkSelectionData;
-
-namespace content {
-class GtkIMContextWrapper;
-class GtkKeyBindingsHandler;
-class RenderWidgetHost;
-class RenderWidgetHostImpl;
-struct NativeWebKeyboardEvent;
-
-// -----------------------------------------------------------------------------
-// See comments in render_widget_host_view.h about this class and its members.
-// -----------------------------------------------------------------------------
-class CONTENT_EXPORT RenderWidgetHostViewGtk
- : public RenderWidgetHostViewBase,
- public BrowserAccessibilityDelegate,
- public ui::ActiveWindowWatcherXObserver,
- public IPC::Sender {
- public:
- virtual ~RenderWidgetHostViewGtk();
-
- // RenderWidgetHostView implementation.
- virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE;
- virtual void InitAsChild(gfx::NativeView parent_view) OVERRIDE;
- virtual RenderWidgetHost* GetRenderWidgetHost() const OVERRIDE;
- virtual void SetSize(const gfx::Size& size) OVERRIDE;
- virtual void SetBounds(const gfx::Rect& rect) OVERRIDE;
- virtual gfx::NativeView GetNativeView() const OVERRIDE;
- virtual gfx::NativeViewId GetNativeViewId() const OVERRIDE;
- virtual gfx::NativeViewAccessible GetNativeViewAccessible() OVERRIDE;
- virtual bool HasFocus() const OVERRIDE;
- virtual bool IsSurfaceAvailableForCopy() const OVERRIDE;
- virtual void Show() OVERRIDE;
- virtual void Hide() OVERRIDE;
- virtual bool IsShowing() OVERRIDE;
- virtual gfx::Rect GetViewBounds() const OVERRIDE;
- virtual GdkEventButton* GetLastMouseDown() OVERRIDE;
- virtual gfx::NativeView BuildInputMethodsGtkMenu() OVERRIDE;
- virtual void SetBackground(const SkBitmap& background) OVERRIDE;
-
- // RenderWidgetHostViewPort implementation.
- virtual void InitAsPopup(RenderWidgetHostView* parent_host_view,
- const gfx::Rect& pos) OVERRIDE;
- virtual void InitAsFullscreen(
- RenderWidgetHostView* reference_host_view) OVERRIDE;
- virtual void WasShown() OVERRIDE;
- virtual void WasHidden() OVERRIDE;
- virtual void MovePluginWindows(
- const gfx::Vector2d& scroll_offset,
- const std::vector<WebPluginGeometry>& moves) OVERRIDE;
- virtual void Focus() OVERRIDE;
- virtual void Blur() OVERRIDE;
- virtual void UpdateCursor(const WebCursor& cursor) OVERRIDE;
- virtual void SetIsLoading(bool is_loading) OVERRIDE;
- virtual void TextInputTypeChanged(ui::TextInputType type,
- ui::TextInputMode input_mode,
- bool can_compose_inline) OVERRIDE;
- virtual void ImeCancelComposition() OVERRIDE;
- virtual void DidUpdateBackingStore(
- const gfx::Rect& scroll_rect,
- const gfx::Vector2d& scroll_delta,
- const std::vector<gfx::Rect>& copy_rects,
- const ui::LatencyInfo& latency_info) OVERRIDE;
- virtual void RenderProcessGone(base::TerminationStatus status,
- int error_code) OVERRIDE;
- virtual void Destroy() OVERRIDE;
- virtual void WillDestroyRenderWidget(RenderWidgetHost* rwh) {}
- virtual void SetTooltipText(const base::string16& tooltip_text) OVERRIDE;
- virtual void SelectionChanged(const base::string16& text,
- size_t offset,
- const gfx::Range& range) OVERRIDE;
- virtual void SelectionBoundsChanged(
- const ViewHostMsg_SelectionBounds_Params& params) OVERRIDE;
- virtual void ScrollOffsetChanged() OVERRIDE;
- virtual BackingStore* AllocBackingStore(const gfx::Size& size) OVERRIDE;
- virtual void CopyFromCompositingSurface(
- const gfx::Rect& src_subrect,
- const gfx::Size& dst_size,
- const base::Callback<void(bool, const SkBitmap&)>& callback) OVERRIDE;
- virtual void CopyFromCompositingSurfaceToVideoFrame(
- const gfx::Rect& src_subrect,
- const scoped_refptr<media::VideoFrame>& target,
- const base::Callback<void(bool)>& callback) OVERRIDE;
- virtual bool CanCopyToVideoFrame() const OVERRIDE;
- virtual void OnAcceleratedCompositingStateChange() OVERRIDE;
- virtual void AcceleratedSurfaceInitialized(int host_id,
- int route_id) OVERRIDE;
- virtual void AcceleratedSurfaceBuffersSwapped(
- const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params,
- int gpu_host_id) OVERRIDE;
- virtual void AcceleratedSurfacePostSubBuffer(
- const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params,
- int gpu_host_id) OVERRIDE;
- virtual void AcceleratedSurfaceSuspend() OVERRIDE;
- virtual void AcceleratedSurfaceRelease() OVERRIDE;
- virtual bool HasAcceleratedSurface(const gfx::Size& desired_size) OVERRIDE;
- virtual void SetHasHorizontalScrollbar(
- bool has_horizontal_scrollbar) OVERRIDE;
- virtual void SetScrollOffsetPinning(
- bool is_pinned_to_left, bool is_pinned_to_right) OVERRIDE;
- virtual void GetScreenInfo(blink::WebScreenInfo* results) OVERRIDE;
- virtual gfx::Rect GetBoundsInRootWindow() OVERRIDE;
- virtual gfx::GLSurfaceHandle GetCompositingSurface() OVERRIDE;
- virtual void ResizeCompositingSurface(const gfx::Size&) OVERRIDE;
- virtual bool LockMouse() OVERRIDE;
- virtual void UnlockMouse() OVERRIDE;
- virtual void OnAccessibilityEvents(
- const std::vector<AccessibilityHostMsg_EventParams>& params)
- OVERRIDE;
-
- // ActiveWindowWatcherXObserver implementation.
- virtual void ActiveWindowChanged(GdkWindow* active_window) OVERRIDE;
-
- // IPC::Sender implementation:
- virtual bool Send(IPC::Message* message) OVERRIDE;
-
- // If the widget is aligned with an edge of the monitor its on and the user
- // attempts to drag past that edge we track the number of times it has
- // occurred, so that we can force the widget to scroll when it otherwise
- // would be unable to.
- void ModifyEventForEdgeDragging(GtkWidget* widget, GdkEventMotion* event);
-
- // Mouse events always provide a movementX/Y which needs to be computed.
- // Also, mouse lock requires knowledge of last unlocked cursor coordinates.
- // State is stored on the host view to do this, and the mouse event modified.
- void ModifyEventMovementAndCoords(blink::WebMouseEvent* event);
-
- void Paint(const gfx::Rect&);
-
- // Called by GtkIMContextWrapper to forward a keyboard event to renderer.
- // On Linux (not ChromeOS):
- // Before calling RenderWidgetHost::ForwardKeyboardEvent(), this method
- // calls GtkKeyBindingsHandler::Match() against the event and send matched
- // edit commands to renderer by calling
- // RenderWidgetHost::ForwardEditCommandsForNextKeyEvent().
- void ForwardKeyboardEvent(const NativeWebKeyboardEvent& event);
-
- bool RetrieveSurrounding(std::string* text, size_t* cursor_index);
-
- // BrowserAccessibilityDelegate implementation.
- virtual void SetAccessibilityFocus(int acc_obj_id) OVERRIDE;
- virtual void AccessibilityDoDefaultAction(int acc_obj_id) OVERRIDE;
- virtual void AccessibilityScrollToMakeVisible(
- int acc_obj_id, gfx::Rect subfocus) OVERRIDE;
- virtual void AccessibilityScrollToPoint(
- int acc_obj_id, gfx::Point point) OVERRIDE;
- virtual void AccessibilitySetTextSelection(
- int acc_obj_id, int start_offset, int end_offset) OVERRIDE;
- virtual gfx::Point GetLastTouchEventLocation() const OVERRIDE;
- virtual void FatalAccessibilityTreeError() OVERRIDE;
-
- // Get the root of the AtkObject* tree for accessibility.
- AtkObject* GetAccessible();
-
- protected:
- friend class RenderWidgetHostView;
-
- // Should construct only via RenderWidgetHostView::CreateViewForWidget.
- explicit RenderWidgetHostViewGtk(RenderWidgetHost* widget);
-
- private:
- friend class RenderWidgetHostViewGtkWidget;
-
- CHROMEGTK_CALLBACK_0(RenderWidgetHostViewGtk,
- void,
- OnDestroy);
-
- // Returns whether the widget needs an input grab (GTK+ and X) to work
- // properly.
- bool NeedsInputGrab();
-
- // Returns whether this render view is a popup (<select> dropdown or
- // autocomplete window).
- bool IsPopup() const;
-
- // Do initialization needed by all InitAs*() methods.
- void DoSharedInit();
-
- // Do initialization needed just by InitAsPopup() and InitAsFullscreen().
- // We move and resize |window| to |bounds| and show it and its contents.
- void DoPopupOrFullscreenInit(GtkWindow* window, const gfx::Rect& bounds);
-
- // Update the display cursor for the render view.
- void ShowCurrentCursor();
-
- void set_last_mouse_down(GdkEventButton* event);
-
- // Cause the next query for the widget center to recompute the cached value.
- void MarkCachedWidgetCenterStale();
-
- void OnCreatePluginContainer(gfx::PluginWindowHandle id);
- void OnDestroyPluginContainer(gfx::PluginWindowHandle id);
-
- gfx::Point GetWidgetCenter();
-
- // The model object.
- RenderWidgetHostImpl* host_;
-
- // The native UI widget.
- ui::OwnedWidgetGtk view_;
-
- // This is true when we are currently painting and thus should handle extra
- // paint requests by expanding the invalid rect rather than actually
- // painting.
- bool about_to_validate_and_paint_;
-
- // This is the rectangle which we'll paint.
- gfx::Rect invalid_rect_;
-
- // Whether we are currently loading.
- bool is_loading_;
-
- // The cursor for the page. This is passed up from the renderer.
- WebCursor current_cursor_;
-
- // The time at which this view started displaying white pixels as a result of
- // not having anything to paint (empty backing store from renderer). This
- // value returns true for is_null() if we are not recording whiteout times.
- base::TimeTicks whiteout_start_time_;
-
- // The time it took after this view was selected for it to be fully painted.
- base::TimeTicks web_contents_switch_paint_time_;
-
- // The native view of our parent widget. Used only for popups.
- GtkWidget* parent_;
-
- // We ignore the first mouse release on popups so the popup will remain open.
- bool is_popup_first_mouse_release_;
-
- // Whether or not this widget's input context was focused before being
- // shadowed by another widget. Used in OnGrabNotify() handler to track the
- // focused state correctly.
- bool was_imcontext_focused_before_grab_;
-
- // True if we are responsible for creating an X grab. This will only be used
- // for <select> dropdowns. It should be true for most such cases, but false
- // for extension popups.
- bool do_x_grab_;
-
- // Is the widget fullscreen?
- bool is_fullscreen_;
-
- // Has the window ever been marked active? Only valid for fullscreen or
- // popup windows.
- bool made_active_;
-
- // Used to record the last position of the mouse.
- // While the mouse is locked, they store the last known position just as mouse
- // lock was entered.
- // Relative to the upper-left corner of the view.
- gfx::Point unlocked_mouse_position_;
- // Relative to the upper-left corner of the screen.
- gfx::Point unlocked_global_mouse_position_;
- // Last hidden cursor position. Relative to screen.
- gfx::Point global_mouse_position_;
- // Indicates when mouse motion is valid after the widget has moved.
- bool mouse_has_been_warped_to_new_center_;
- // Indicates the cursor has been warped to the unlocked position,
- // but a move event has not yet been received for it there.
- bool mouse_is_being_warped_to_unlocked_position_;
-
- // For full-screen windows we have a OnDestroy handler that we need to remove,
- // so we keep it ID here.
- unsigned long destroy_handler_id_;
-
- // A convenience wrapper object for GtkIMContext;
- scoped_ptr<GtkIMContextWrapper> im_context_;
-
- // A convenience object for handling editor key bindings defined in gtk
- // keyboard theme.
- scoped_ptr<GtkKeyBindingsHandler> key_bindings_handler_;
-
- // Helper class that lets us allocate plugin containers and move them.
- GtkPluginContainerManager plugin_container_manager_;
-
- // The size that we want the renderer to be. We keep this in a separate
- // variable because resizing in GTK+ is async.
- gfx::Size requested_size_;
-
- // The latest reported center of the widget, use GetWidgetCenter() to access.
- gfx::Point widget_center_;
- // If the window moves the widget_center will not be valid until we recompute.
- bool widget_center_valid_;
-
- // The number of times the user has dragged against horizontal edge of the
- // monitor (if the widget is aligned with that edge). Negative values
- // indicate the left edge, positive the right.
- int dragged_at_horizontal_edge_;
-
- // The number of times the user has dragged against vertical edge of the
- // monitor (if the widget is aligned with that edge). Negative values
- // indicate the top edge, positive the bottom.
- int dragged_at_vertical_edge_;
-
- gfx::PluginWindowHandle compositing_surface_;
-
- // The event for the last mouse down we handled. We need this for context
- // menus and drags.
- GdkEventButton* last_mouse_down_;
-
- // Instance of accessibility information for the root of the AtkObject
- // tree representation of the WebKit render tree.
- scoped_ptr<BrowserAccessibilityManager> browser_accessibility_manager_;
-
- ui::GtkSignalRegistrar signals_;
-
- ui::LatencyInfo software_latency_info_;
-};
-
-} // namespace content
-
-#endif // CHROME_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_GTK_H_
diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_mac.h b/chromium/content/browser/renderer_host/render_widget_host_view_mac.h
index b6825b07ab5..8fb2e4a5fe8 100644
--- a/chromium/content/browser/renderer_host/render_widget_host_view_mac.h
+++ b/chromium/content/browser/renderer_host/render_widget_host_view_mac.h
@@ -6,6 +6,7 @@
#define CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_MAC_H_
#import <Cocoa/Cocoa.h>
+#include <IOSurface/IOSurfaceAPI.h>
#include <list>
#include <map>
#include <string>
@@ -16,26 +17,40 @@
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
-#include "content/browser/accessibility/browser_accessibility_delegate_mac.h"
+#include "content/browser/compositor/browser_compositor_view_mac.h"
+#include "content/browser/compositor/delegated_frame_host.h"
+#include "content/browser/renderer_host/compositing_iosurface_layer_mac.h"
+#include "content/browser/renderer_host/display_link_mac.h"
#include "content/browser/renderer_host/render_widget_host_view_base.h"
#include "content/browser/renderer_host/software_frame_manager.h"
+#include "content/common/content_export.h"
+#include "content/common/cursors/webcursor.h"
#include "content/common/edit_command.h"
#import "content/public/browser/render_widget_host_view_mac_base.h"
#include "ipc/ipc_sender.h"
#include "third_party/WebKit/public/web/WebCompositionUnderline.h"
#include "ui/base/cocoa/base_view.h"
-#include "webkit/common/cursors/webcursor.h"
+
+struct ViewHostMsg_TextInputState_Params;
namespace content {
class CompositingIOSurfaceMac;
class CompositingIOSurfaceContext;
class RenderWidgetHostViewMac;
class RenderWidgetHostViewMacEditCommandHelper;
+class WebContents;
+}
+
+namespace ui {
+class Compositor;
+class Layer;
}
+@class BrowserCompositorViewMac;
@class CompositingIOSurfaceLayer;
@class FullscreenWindowManager;
@protocol RenderWidgetHostViewMacDelegate;
+@class SoftwareLayer;
@class ToolTip;
@protocol RenderWidgetHostViewMacOwner
@@ -49,11 +64,12 @@ class RenderWidgetHostViewMacEditCommandHelper;
@interface RenderWidgetHostViewCocoa
: BaseView <RenderWidgetHostViewMacBase,
RenderWidgetHostViewMacOwner,
- NSTextInputClient,
- BrowserAccessibilityDelegateCocoa> {
+ NSTextInputClient> {
@private
scoped_ptr<content::RenderWidgetHostViewMac> renderWidgetHostView_;
- NSObject<RenderWidgetHostViewMacDelegate>* delegate_; // weak
+ // This ivar is the cocoa delegate of the NSResponder.
+ base::scoped_nsobject<NSObject<RenderWidgetHostViewMacDelegate>>
+ responderDelegate_;
BOOL canBeKeyView_;
BOOL takesFocusOnlyOnMouseDown_;
BOOL closeOnDeactivate_;
@@ -196,14 +212,24 @@ class RenderWidgetHostImpl;
// references to it must become NULL."
//
// RenderWidgetHostView class hierarchy described in render_widget_host_view.h.
-class RenderWidgetHostViewMac : public RenderWidgetHostViewBase,
- public IPC::Sender,
- public SoftwareFrameManagerClient {
+class CONTENT_EXPORT RenderWidgetHostViewMac
+ : public RenderWidgetHostViewBase,
+ public DelegatedFrameHostClient,
+ public IPC::Sender,
+ public SoftwareFrameManagerClient,
+ public CompositingIOSurfaceLayerClient {
public:
+ // The view will associate itself with the given widget. The native view must
+ // be hooked up immediately to the view hierarchy, or else when it is
+ // deleted it will delete this out from under the caller.
+ explicit RenderWidgetHostViewMac(RenderWidgetHost* widget);
virtual ~RenderWidgetHostViewMac();
RenderWidgetHostViewCocoa* cocoa_view() const { return cocoa_view_; }
+ // |delegate| is used to separate out the logic from the NSResponder delegate.
+ // |delegate| is retained by this class.
+ // |delegate| should be set at most once.
CONTENT_EXPORT void SetDelegate(
NSObject<RenderWidgetHostViewMacDelegate>* delegate);
void SetAllowOverlappingViews(bool overlapping);
@@ -233,9 +259,9 @@ class RenderWidgetHostViewMac : public RenderWidgetHostViewBase,
virtual void SpeakSelection() OVERRIDE;
virtual bool IsSpeaking() const OVERRIDE;
virtual void StopSpeaking() OVERRIDE;
- virtual void SetBackground(const SkBitmap& background) OVERRIDE;
+ virtual void SetBackgroundOpaque(bool opaque) OVERRIDE;
- // Implementation of RenderWidgetHostViewPort.
+ // Implementation of RenderWidgetHostViewBase.
virtual void InitAsPopup(RenderWidgetHostView* parent_host_view,
const gfx::Rect& pos) OVERRIDE;
virtual void InitAsFullscreen(
@@ -243,24 +269,17 @@ class RenderWidgetHostViewMac : public RenderWidgetHostViewBase,
virtual void WasShown() OVERRIDE;
virtual void WasHidden() OVERRIDE;
virtual void MovePluginWindows(
- const gfx::Vector2d& scroll_offset,
const std::vector<WebPluginGeometry>& moves) OVERRIDE;
virtual void Focus() OVERRIDE;
virtual void Blur() OVERRIDE;
virtual void UpdateCursor(const WebCursor& cursor) OVERRIDE;
virtual void SetIsLoading(bool is_loading) OVERRIDE;
- virtual void TextInputTypeChanged(ui::TextInputType type,
- ui::TextInputMode input_mode,
- bool can_compose_inline) OVERRIDE;
+ virtual void TextInputStateChanged(
+ const ViewHostMsg_TextInputState_Params& params) OVERRIDE;
virtual void ImeCancelComposition() OVERRIDE;
virtual void ImeCompositionRangeChanged(
const gfx::Range& range,
const std::vector<gfx::Rect>& character_bounds) OVERRIDE;
- virtual void DidUpdateBackingStore(
- const gfx::Rect& scroll_rect,
- const gfx::Vector2d& scroll_delta,
- const std::vector<gfx::Rect>& copy_rects,
- const ui::LatencyInfo& latency_info) OVERRIDE;
virtual void RenderProcessGone(base::TerminationStatus status,
int error_code) OVERRIDE;
virtual void Destroy() OVERRIDE;
@@ -271,11 +290,11 @@ class RenderWidgetHostViewMac : public RenderWidgetHostViewBase,
virtual void SelectionBoundsChanged(
const ViewHostMsg_SelectionBounds_Params& params) OVERRIDE;
virtual void ScrollOffsetChanged() OVERRIDE;
- virtual BackingStore* AllocBackingStore(const gfx::Size& size) OVERRIDE;
virtual void CopyFromCompositingSurface(
const gfx::Rect& src_subrect,
const gfx::Size& dst_size,
- const base::Callback<void(bool, const SkBitmap&)>& callback) OVERRIDE;
+ const base::Callback<void(bool, const SkBitmap&)>& callback,
+ SkBitmap::Config config) OVERRIDE;
virtual void CopyFromCompositingSurfaceToVideoFrame(
const gfx::Rect& src_subrect,
const scoped_refptr<media::VideoFrame>& target,
@@ -287,12 +306,13 @@ class RenderWidgetHostViewMac : public RenderWidgetHostViewBase,
virtual void EndFrameSubscription() OVERRIDE;
virtual void OnSwapCompositorFrame(
uint32 output_surface_id, scoped_ptr<cc::CompositorFrame> frame) OVERRIDE;
- virtual void OnAcceleratedCompositingStateChange() OVERRIDE;
virtual void AcceleratedSurfaceInitialized(int host_id,
int route_id) OVERRIDE;
- virtual void OnAccessibilityEvents(
- const std::vector<AccessibilityHostMsg_EventParams>& params
- ) OVERRIDE;
+ virtual void CreateBrowserAccessibilityManagerIfNeeded() OVERRIDE;
+ virtual gfx::Point AccessibilityOriginInScreen(const gfx::Rect& bounds)
+ OVERRIDE;
+ virtual void OnAccessibilitySetFocus(int acc_obj_id) OVERRIDE;
+ virtual void AccessibilityShowMenu(int acc_obj_id) OVERRIDE;
virtual bool PostProcessEventForPluginIme(
const NativeWebKeyboardEvent& event) OVERRIDE;
@@ -305,19 +325,14 @@ class RenderWidgetHostViewMac : public RenderWidgetHostViewBase,
virtual void AcceleratedSurfaceSuspend() OVERRIDE;
virtual void AcceleratedSurfaceRelease() OVERRIDE;
virtual bool HasAcceleratedSurface(const gfx::Size& desired_size) OVERRIDE;
- virtual void AboutToWaitForBackingStoreMsg() OVERRIDE;
virtual void GetScreenInfo(blink::WebScreenInfo* results) OVERRIDE;
virtual gfx::Rect GetBoundsInRootWindow() OVERRIDE;
virtual gfx::GLSurfaceHandle GetCompositingSurface() OVERRIDE;
- virtual void SetHasHorizontalScrollbar(
- bool has_horizontal_scrollbar) OVERRIDE;
- virtual void SetScrollOffsetPinning(
- bool is_pinned_to_left, bool is_pinned_to_right) OVERRIDE;
virtual bool LockMouse() OVERRIDE;
virtual void UnlockMouse() OVERRIDE;
- virtual void UnhandledWheelEvent(
- const blink::WebMouseWheelEvent& event) OVERRIDE;
+ virtual void WheelEventAck(const blink::WebMouseWheelEvent& event,
+ InputEventAckState ack_result) OVERRIDE;
// IPC::Sender implementation.
virtual bool Send(IPC::Message* message) OVERRIDE;
@@ -327,6 +342,11 @@ class RenderWidgetHostViewMac : public RenderWidgetHostViewBase,
uint32 output_surface_id, unsigned frame_id) OVERRIDE;
virtual void ReleaseReferencesToSoftwareFrame() OVERRIDE;
+ virtual SkBitmap::Config PreferredReadbackFormat() OVERRIDE;
+
+ // CompositingIOSurfaceLayerClient implementation.
+ virtual void AcceleratedLayerDidDrawFrame(bool succeeded) OVERRIDE;
+
// Forwards the mouse event to the renderer.
void ForwardMouseEvent(const blink::WebMouseEvent& event);
@@ -334,9 +354,6 @@ class RenderWidgetHostViewMac : public RenderWidgetHostViewBase,
void SetTextInputActive(bool active);
- // Change this view to use CoreAnimation to draw.
- void EnableCoreAnimation();
-
// Sends completed plugin IME notification and text back to the renderer.
void PluginImeCompositionCompleted(const base::string16& text, int plugin_id);
@@ -344,15 +361,13 @@ class RenderWidgetHostViewMac : public RenderWidgetHostViewBase,
// Update the IOSurface to be drawn and call setNeedsDisplay on
// |cocoa_view_|.
- void CompositorSwapBuffers(uint64 surface_handle,
+ void CompositorSwapBuffers(IOSurfaceID surface_handle,
const gfx::Size& size,
float scale_factor,
- const ui::LatencyInfo& latency_info);
-
- // Draw the IOSurface by making its context current to this view.
- bool DrawIOSurfaceWithoutCoreAnimation();
+ const std::vector<ui::LatencyInfo>& latency_info);
- // Called when a GPU error is detected. Deletes all compositing state.
+ // Called when a GPU error is detected. Posts a task to destroy all
+ // compositing state.
void GotAcceleratedCompositingError();
// Sets the overlay view, which should be drawn in the same IOSurface
@@ -388,6 +403,8 @@ class RenderWidgetHostViewMac : public RenderWidgetHostViewBase,
gfx::Range ConvertCharacterRangeToCompositionRange(
const gfx::Range& request_range);
+ WebContents* GetWebContents();
+
// These member variables should be private, but the associated ObjC class
// needs access to them and can't be made a friend.
@@ -395,57 +412,35 @@ class RenderWidgetHostViewMac : public RenderWidgetHostViewBase,
// someone (other than superview) has retained |cocoa_view_|.
RenderWidgetHostImpl* render_widget_host_;
- // This is true when we are currently painting and thus should handle extra
- // paint requests by expanding the invalid rect rather than actually painting.
- bool about_to_validate_and_paint_;
-
- // This is true when we have already scheduled a call to
- // |-callSetNeedsDisplayInRect:| but it has not been fulfilled yet. Used to
- // prevent us from scheduling multiple calls.
- bool call_set_needs_display_in_rect_pending_;
-
- // Whether last rendered frame was accelerated.
- bool last_frame_was_accelerated_;
-
- // The invalid rect that needs to be painted by callSetNeedsDisplayInRect.
- // This value is only meaningful when
- // |call_set_needs_display_in_rect_pending_| is true.
- NSRect invalid_rect_;
-
- // The time at which this view started displaying white pixels as a result of
- // not having anything to paint (empty backing store from renderer). This
- // value returns true for is_null() if we are not recording whiteout times.
- base::TimeTicks whiteout_start_time_;
-
- // The time it took after this view was selected for it to be fully painted.
- base::TimeTicks web_contents_switch_paint_time_;
-
// Current text input type.
ui::TextInputType text_input_type_;
bool can_compose_inline_;
- base::scoped_nsobject<CALayer> software_layer_;
+ // The background CoreAnimation layer which is hosted by |cocoa_view_|.
+ // The compositing or software layers will be added as sublayers to this.
+ base::scoped_nsobject<CALayer> background_layer_;
+
+ // The CoreAnimation layer for software compositing. This should be NULL
+ // when software compositing is not in use.
+ base::scoped_nsobject<SoftwareLayer> software_layer_;
// Accelerated compositing structures. These may be dynamically created and
// destroyed together in Create/DestroyCompositedIOSurfaceAndLayer.
base::scoped_nsobject<CompositingIOSurfaceLayer> compositing_iosurface_layer_;
- scoped_ptr<CompositingIOSurfaceMac> compositing_iosurface_;
+ scoped_refptr<CompositingIOSurfaceMac> compositing_iosurface_;
scoped_refptr<CompositingIOSurfaceContext> compositing_iosurface_context_;
+ // Delegated frame management and compositior.
+ base::scoped_nsobject<BrowserCompositorViewMac> browser_compositor_view_;
+ scoped_ptr<DelegatedFrameHost> delegated_frame_host_;
+ scoped_ptr<ui::Layer> root_layer_;
+
// This holds the current software compositing framebuffer, if any.
scoped_ptr<SoftwareFrameManager> software_frame_manager_;
- // This is set when a new software frame is received and un-set when the
- // frame's ack is sent back to the renderer.
- bool software_frame_needs_to_send_ack_;
-
- // Whether to allow overlapping views.
- bool allow_overlapping_views_;
-
- // Whether to use the CoreAnimation path to draw content.
- bool use_core_animation_;
-
- ui::LatencyInfo software_latency_info_;
+ // Latency info to send back when the next frame appears on the
+ // screen.
+ std::vector<ui::LatencyInfo> pending_latency_info_;
NSWindow* pepper_fullscreen_window() const {
return pepper_fullscreen_window_;
@@ -463,21 +458,61 @@ class RenderWidgetHostViewMac : public RenderWidgetHostViewBase,
int window_number() const;
- float scale_factor() const;
+ // The scale factor for the screen that the view is currently on.
+ float ViewScaleFactor() const;
- void FrameSwapped();
+ // Update the scale factor for the backing store and for any CALayers.
+ void UpdateBackingStoreScaleFactor();
+
+ // Ensure that the display link is associated with the correct display.
+ void UpdateDisplayLink();
+
+ // The scale factor of the backing store. Note that this is updated based on
+ // ViewScaleFactor with some delay.
+ float backing_store_scale_factor_;
+
+ void AddPendingLatencyInfo(
+ const std::vector<ui::LatencyInfo>& latency_info);
+ void SendPendingLatencyInfoToHost();
+
+ void SendPendingSwapAck();
+
+ void PauseForPendingResizeOrRepaintsAndDraw();
+
+ // The geometric arrangement of the layers depends on cocoa_view's size, the
+ // compositing IOSurface's rounded size, and the software frame size. Update
+ // all of them using this function when any of those parameters changes. Also
+ // update the scale factor of the layers.
+ void LayoutLayers();
+
+ // DelegatedFrameHostClient implementation.
+ virtual ui::Compositor* GetCompositor() const OVERRIDE;
+ virtual ui::Layer* GetLayer() OVERRIDE;
+ virtual RenderWidgetHostImpl* GetHost() OVERRIDE;
+ virtual void SchedulePaintInRect(
+ const gfx::Rect& damage_rect_in_dip) OVERRIDE;
+ virtual bool IsVisible() OVERRIDE;
+ virtual scoped_ptr<ResizeLock> CreateResizeLock(
+ bool defer_compositor_lock) OVERRIDE;
+ virtual gfx::Size DesiredFrameSize() OVERRIDE;
+ virtual float CurrentDeviceScaleFactor() OVERRIDE;
+ virtual gfx::Size ConvertViewSizeToPixel(const gfx::Size& size) OVERRIDE;
+ virtual DelegatedFrameHost* GetDelegatedFrameHost() const OVERRIDE;
private:
- friend class RenderWidgetHostView;
friend class RenderWidgetHostViewMacTest;
- void GetVSyncParameters(
- base::TimeTicks* timebase, base::TimeDelta* interval);
-
- // The view will associate itself with the given widget. The native view must
- // be hooked up immediately to the view hierarchy, or else when it is
- // deleted it will delete this out from under the caller.
- explicit RenderWidgetHostViewMac(RenderWidgetHost* widget);
+ struct PendingSwapAck {
+ PendingSwapAck(int32 route_id, int gpu_host_id, int32 renderer_id)
+ : route_id(route_id),
+ gpu_host_id(gpu_host_id),
+ renderer_id(renderer_id) {}
+ int32 route_id;
+ int gpu_host_id;
+ int32 renderer_id;
+ };
+ scoped_ptr<PendingSwapAck> pending_swap_ack_;
+ void AddPendingSwapAck(int32 route_id, int gpu_host_id, int32 renderer_id);
// Returns whether this render view is a popup (autocomplete window).
bool IsPopup() const;
@@ -486,17 +521,20 @@ class RenderWidgetHostViewMac : public RenderWidgetHostViewBase,
// invoke it from the message loop.
void ShutdownHost();
- bool CreateCompositedIOSurface();
- bool CreateCompositedIOSurfaceLayer();
- enum DestroyContextBehavior {
- kLeaveContextBoundToView,
- kDestroyContext,
+ void EnsureSoftwareLayer();
+ void DestroySoftwareLayer();
+
+ bool EnsureCompositedIOSurface() WARN_UNUSED_RESULT;
+ void EnsureCompositedIOSurfaceLayer();
+ enum DestroyCompositedIOSurfaceLayerBehavior {
+ kLeaveLayerInHierarchy,
+ kRemoveLayerFromHierarchy,
};
- void DestroyCompositedIOSurfaceAndLayer(DestroyContextBehavior
- destroy_context_behavior);
+ void DestroyCompositedIOSurfaceLayer(
+ DestroyCompositedIOSurfaceLayerBehavior destroy_layer_behavior);
+ void DestroyCompositedIOSurfaceAndLayer();
- // Unbind the GL context (if any) that is bound to |cocoa_view_|.
- void ClearBoundContextDrawable();
+ void DestroyCompositingStateOnError();
// Called when a GPU SwapBuffers is received.
void GotAcceleratedFrame();
@@ -504,32 +542,17 @@ class RenderWidgetHostViewMac : public RenderWidgetHostViewBase,
// Called when a software DIB is received.
void GotSoftwareFrame();
- // Ack pending SwapBuffers requests, if any, to unblock the GPU process. Has
- // no effect if there are no pending requests.
- void AckPendingSwapBuffers();
-
- // Ack pending SwapBuffers requests, but no more frequently than the vsync
- // rate if the renderer is not throttling the swap rate.
- void ThrottledAckPendingSwapBuffers();
-
+ // IPC message handlers.
void OnPluginFocusChanged(bool focused, int plugin_id);
void OnStartPluginIme();
- CONTENT_EXPORT void OnAcceleratedSurfaceSetIOSurface(
- gfx::PluginWindowHandle window,
- int32 width,
- int32 height,
- uint64 mach_port);
- void OnAcceleratedSurfaceSetTransportDIB(gfx::PluginWindowHandle window,
- int32 width,
- int32 height,
- TransportDIB::Handle transport_dib);
- void OnAcceleratedSurfaceBuffersSwapped(gfx::PluginWindowHandle window,
- uint64 surface_handle);
// Convert |rect| from the views coordinate (upper-left origin) into
// the OpenGL coordinate (lower-left origin) and scale for HiDPI displays.
gfx::Rect GetScaledOpenGLPixelRect(const gfx::Rect& rect);
+ // Send updated vsync parameters to the renderer.
+ void SendVSyncParametersToRenderer();
+
// The associated view. This is weak and is inserted into the view hierarchy
// to own this RenderWidgetHostViewMac object. Set to nil at the start of the
// destructor.
@@ -556,39 +579,22 @@ class RenderWidgetHostViewMac : public RenderWidgetHostViewBase,
// Our parent host view, if this is fullscreen. NULL otherwise.
RenderWidgetHostViewMac* fullscreen_parent_host_view_;
- // List of pending swaps for deferred acking:
- // pairs of (route_id, gpu_host_id).
- std::list<std::pair<int32, int32> > pending_swap_buffers_acks_;
-
- // Factory used to cancel outstanding throttled AckPendingSwapBuffers calls.
- base::WeakPtrFactory<RenderWidgetHostViewMac>
- pending_swap_buffers_acks_weak_factory_;
-
// The overlay view which is rendered above this one in the same
// accelerated IOSurface.
// Overlay view has |underlay_view_| set to this view.
base::WeakPtr<RenderWidgetHostViewMac> overlay_view_;
- // Offset at which overlay view should be rendered.
- gfx::Point overlay_view_offset_;
-
// The underlay view which this view is rendered above in the same
// accelerated IOSurface.
// Underlay view has |overlay_view_| set to this view.
base::WeakPtr<RenderWidgetHostViewMac> underlay_view_;
- // Set to true when |underlay_view_| has drawn this view. After that point,
- // this view should not draw again until |underlay_view_| is changed.
- bool underlay_view_has_drawn_;
-
// Factory used to safely reference overlay view set in SetOverlayView.
base::WeakPtrFactory<RenderWidgetHostViewMac>
overlay_view_weak_factory_;
- // The earliest time at which the next swap ack may be sent. Only relevant
- // when swaps are not being throttled by the renderer (when threaded
- // compositing is off).
- base::Time next_swap_ack_time_;
+ // Display link for getting vsync info.
+ scoped_refptr<DisplayLinkMac> display_link_;
// The current composition character range and its bounds.
gfx::Range composition_range_;
diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_mac.mm b/chromium/content/browser/renderer_host/render_widget_host_view_mac.mm
index 23a6e3c5f24..375833cec29 100644
--- a/chromium/content/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/chromium/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -5,6 +5,7 @@
#include "content/browser/renderer_host/render_widget_host_view_mac.h"
#import <objc/runtime.h>
+#include <OpenGL/gl.h>
#include <QuartzCore/QuartzCore.h>
#include "base/basictypes.h"
@@ -27,14 +28,18 @@
#include "base/sys_info.h"
#import "content/browser/accessibility/browser_accessibility_cocoa.h"
#include "content/browser/accessibility/browser_accessibility_manager_mac.h"
-#include "content/browser/renderer_host/backing_store_mac.h"
-#include "content/browser/renderer_host/backing_store_manager.h"
+#include "content/browser/compositor/resize_lock.h"
+#include "content/browser/frame_host/frame_tree.h"
+#include "content/browser/frame_host/frame_tree_node.h"
+#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/browser/renderer_host/compositing_iosurface_context_mac.h"
#include "content/browser/renderer_host/compositing_iosurface_layer_mac.h"
#include "content/browser/renderer_host/compositing_iosurface_mac.h"
+#include "content/browser/renderer_host/render_widget_helper.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#import "content/browser/renderer_host/render_widget_host_view_mac_dictionary_helper.h"
#import "content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper.h"
+#import "content/browser/renderer_host/software_layer_mac.h"
#import "content/browser/renderer_host/text_input_client_mac.h"
#include "content/common/accessibility_messages.h"
#include "content/common/edit_command.h"
@@ -42,12 +47,14 @@
#include "content/common/input_messages.h"
#include "content/common/view_messages.h"
#include "content/common/webplugin_geometry.h"
-#include "content/port/browser/render_widget_host_view_frame_subscriber.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/native_web_keyboard_event.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/render_widget_host_view_frame_subscriber.h"
#import "content/public/browser/render_widget_host_view_mac_delegate.h"
#include "content/public/browser/user_metrics.h"
-#include "content/public/common/content_switches.h"
+#include "content/public/browser/web_contents.h"
#include "skia/ext/platform_canvas.h"
#include "third_party/WebKit/public/platform/WebScreenInfo.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
@@ -58,51 +65,34 @@
#import "ui/base/cocoa/underlay_opengl_hosting_window.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "ui/base/layout.h"
+#include "ui/compositor/compositor.h"
+#include "ui/compositor/layer.h"
#include "ui/gfx/display.h"
#include "ui/gfx/point.h"
#include "ui/gfx/rect_conversions.h"
#include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
#include "ui/gfx/screen.h"
#include "ui/gfx/size_conversions.h"
-#include "ui/gl/io_surface_support_mac.h"
+#include "ui/gl/gl_switches.h"
-using content::BackingStoreMac;
using content::BrowserAccessibility;
using content::BrowserAccessibilityManager;
using content::EditCommand;
+using content::FrameTreeNode;
using content::NativeWebKeyboardEvent;
+using content::RenderFrameHost;
+using content::RenderViewHost;
using content::RenderViewHostImpl;
using content::RenderWidgetHostImpl;
using content::RenderWidgetHostViewMac;
using content::RenderWidgetHostViewMacEditCommandHelper;
using content::TextInputClientMac;
+using content::WebContents;
using blink::WebInputEvent;
using blink::WebInputEventFactory;
using blink::WebMouseEvent;
using blink::WebMouseWheelEvent;
-
-enum CoreAnimationStatus {
- CORE_ANIMATION_DISABLED,
- CORE_ANIMATION_ENABLED_LAZY,
- CORE_ANIMATION_ENABLED_ALWAYS,
-};
-
-static CoreAnimationStatus GetCoreAnimationStatus() {
- // TODO(sail) Remove this.
- if (!CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kUseCoreAnimation)) {
- return CORE_ANIMATION_DISABLED;
- }
- if (CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- switches::kUseCoreAnimation) == "lazy") {
- return CORE_ANIMATION_ENABLED_LAZY;
- }
- if (CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- switches::kUseCoreAnimation) == "disabled") {
- return CORE_ANIMATION_DISABLED;
- }
- return CORE_ANIMATION_ENABLED_ALWAYS;
-}
+using blink::WebGestureEvent;
// These are not documented, so use only after checking -respondsToSelector:.
@interface NSApplication (UndocumentedSpeechMethods)
@@ -114,20 +104,9 @@ static CoreAnimationStatus GetCoreAnimationStatus() {
// Declare things that are part of the 10.7 SDK.
#if !defined(MAC_OS_X_VERSION_10_7) || \
MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
-@interface NSView (NSOpenGLSurfaceResolutionLionAPI)
-- (void)setWantsBestResolutionOpenGLSurface:(BOOL)flag;
-@end
static NSString* const NSWindowDidChangeBackingPropertiesNotification =
@"NSWindowDidChangeBackingPropertiesNotification";
-static NSString* const NSBackingPropertyOldScaleFactorKey =
- @"NSBackingPropertyOldScaleFactorKey";
-// Note: Apple's example code (linked from the comment above
-// -windowDidChangeBackingProperties:) uses
-// @"NSWindowBackingPropertiesChangeOldBackingScaleFactorKey", but that always
-// returns an old scale of 0. @"NSBackingPropertyOldScaleFactorKey" seems to
-// work in practice, and it's what's used in Apple's WebKit port
-// (WebKit/mac/WebView/WebView.mm).
#endif // 10.7
@@ -154,31 +133,23 @@ static BOOL SupportsBackingPropertiesChangedNotification() {
return methodDescription.name != NULL || methodDescription.types != NULL;
}
-static float ScaleFactor(NSView* view) {
- return ui::GetImageScale(ui::GetScaleFactorForNativeView(view));
-}
-
// Private methods:
@interface RenderWidgetHostViewCocoa ()
@property(nonatomic, assign) NSRange selectedRange;
@property(nonatomic, assign) NSRange markedRange;
-@property(nonatomic, assign)
- NSObject<RenderWidgetHostViewMacDelegate>* delegate;
+ (BOOL)shouldAutohideCursorForEvent:(NSEvent*)event;
- (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r;
-- (void)gotUnhandledWheelEvent;
-- (void)scrollOffsetPinnedToLeft:(BOOL)left toRight:(BOOL)right;
-- (void)setHasHorizontalScrollbar:(BOOL)has_horizontal_scrollbar;
+- (void)processedWheelEvent:(const blink::WebMouseWheelEvent&)event
+ consumed:(BOOL)consumed;
+
- (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv;
- (void)windowDidChangeBackingProperties:(NSNotification*)notification;
- (void)windowChangedGlobalFrame:(NSNotification*)notification;
-- (void)drawBackingStore:(BackingStoreMac*)backingStore
- dirtyRect:(CGRect)dirtyRect
- inContext:(CGContextRef)context;
-- (void)updateSoftwareLayerScaleFactor;
- (void)checkForPluginImeCancellation;
-- (void)updateTabBackingStoreScaleFactor;
+- (void)updateScreenProperties;
+- (void)setResponderDelegate:
+ (NSObject<RenderWidgetHostViewMacDelegate>*)delegate;
@end
// A window subclass that allows the fullscreen window to become main and gain
@@ -216,7 +187,6 @@ static float ScaleFactor(NSView* view) {
styleMask:windowStyle
backing:bufferingType
defer:deferCreation]) {
- CHECK_EQ(CORE_ANIMATION_DISABLED, GetCoreAnimationStatus());
[self setOpaque:NO];
[self setBackgroundColor:[NSColor clearColor]];
[self startObservingClicks];
@@ -308,8 +278,12 @@ void ExtractUnderlines(
color = WebColorFromNSColor(
[colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]);
}
- underlines->push_back(blink::WebCompositionUnderline(
- range.location, NSMaxRange(range), color, [style intValue] > 1));
+ underlines->push_back(
+ blink::WebCompositionUnderline(range.location,
+ NSMaxRange(range),
+ color,
+ [style intValue] > 1,
+ SK_ColorTRANSPARENT));
}
i = range.location + range.length;
}
@@ -332,15 +306,35 @@ void DisablePasswordInput() {
TSMRemoveDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag);
}
+// Calls to [NSScreen screens], required by FlipYFromRectToScreen and
+// FlipNSRectToRectScreen, can take several milliseconds. Only re-compute this
+// value when screen info changes.
+// TODO(ccameron): An observer on every RWHVCocoa will set this to false
+// on NSApplicationDidChangeScreenParametersNotification. Only one observer
+// is necessary.
+bool g_screen_info_up_to_date = false;
+
+float FlipYFromRectToScreen(float y, float rect_height) {
+ TRACE_EVENT0("browser", "FlipYFromRectToScreen");
+ static CGFloat screen_zero_height = 0;
+ if (!g_screen_info_up_to_date) {
+ if ([[NSScreen screens] count] > 0) {
+ screen_zero_height =
+ [[[NSScreen screens] objectAtIndex:0] frame].size.height;
+ g_screen_info_up_to_date = true;
+ } else {
+ return y;
+ }
+ }
+ return screen_zero_height - y - rect_height;
+}
+
// Adjusts an NSRect in Cocoa screen coordinates to have an origin in the upper
// left of the primary screen (Carbon coordinates), and stuffs it into a
// gfx::Rect.
gfx::Rect FlipNSRectToRectScreen(const NSRect& rect) {
gfx::Rect new_rect(NSRectToCGRect(rect));
- if ([[NSScreen screens] count] > 0) {
- new_rect.set_y([[[NSScreen screens] objectAtIndex:0] frame].size.height -
- new_rect.y() - new_rect.height());
- }
+ new_rect.set_y(FlipYFromRectToScreen(new_rect.y(), new_rect.height()));
return new_rect;
}
@@ -380,24 +374,76 @@ blink::WebScreenInfo GetWebScreenInfo(NSView* view) {
[[screen colorSpace] colorSpaceModel] == NSGrayColorSpaceModel;
results.rect = display.bounds();
results.availableRect = display.work_area();
+ results.orientationAngle = display.RotationAsDegree();
+
return results;
}
+void RemoveLayerFromSuperlayer(
+ base::scoped_nsobject<CompositingIOSurfaceLayer> layer) {
+ // Disable the fade-out animation as the layer is removed.
+ ScopedCAActionDisabler disabler;
+ [layer removeFromSuperlayer];
+}
+
} // namespace
namespace content {
-///////////////////////////////////////////////////////////////////////////////
-// RenderWidgetHostView, public:
+////////////////////////////////////////////////////////////////////////////////
+// DelegatedFrameHost, public:
-// static
-RenderWidgetHostView* RenderWidgetHostView::CreateViewForWidget(
- RenderWidgetHost* widget) {
- return new RenderWidgetHostViewMac(widget);
+ui::Compositor* RenderWidgetHostViewMac::GetCompositor() const {
+ return [browser_compositor_view_ compositor];
+}
+
+ui::Layer* RenderWidgetHostViewMac::GetLayer() {
+ return root_layer_.get();
+}
+
+RenderWidgetHostImpl* RenderWidgetHostViewMac::GetHost() {
+ return render_widget_host_;
+}
+
+void RenderWidgetHostViewMac::SchedulePaintInRect(
+ const gfx::Rect& damage_rect_in_dip) {
+ [browser_compositor_view_ compositor]->ScheduleFullRedraw();
+}
+
+bool RenderWidgetHostViewMac::IsVisible() {
+ return !render_widget_host_->is_hidden();
+}
+
+gfx::Size RenderWidgetHostViewMac::DesiredFrameSize() {
+ return GetViewBounds().size();
+}
+
+float RenderWidgetHostViewMac::CurrentDeviceScaleFactor() {
+ return ViewScaleFactor();
+}
+
+gfx::Size RenderWidgetHostViewMac::ConvertViewSizeToPixel(
+ const gfx::Size& size) {
+ return gfx::ToEnclosingRect(gfx::ScaleRect(gfx::Rect(size),
+ ViewScaleFactor())).size();
}
+scoped_ptr<ResizeLock> RenderWidgetHostViewMac::CreateResizeLock(
+ bool defer_compositor_lock) {
+ NOTREACHED();
+ ResizeLock* lock = NULL;
+ return scoped_ptr<ResizeLock>(lock);
+}
+
+DelegatedFrameHost* RenderWidgetHostViewMac::GetDelegatedFrameHost() const {
+ return delegated_frame_host_.get();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// RenderWidgetHostViewBase, public:
+
// static
-void RenderWidgetHostViewPort::GetDefaultScreenInfo(
+void RenderWidgetHostViewBase::GetDefaultScreenInfo(
blink::WebScreenInfo* results) {
*results = GetWebScreenInfo(NULL);
}
@@ -407,21 +453,13 @@ void RenderWidgetHostViewPort::GetDefaultScreenInfo(
RenderWidgetHostViewMac::RenderWidgetHostViewMac(RenderWidgetHost* widget)
: render_widget_host_(RenderWidgetHostImpl::From(widget)),
- about_to_validate_and_paint_(false),
- call_set_needs_display_in_rect_pending_(false),
- last_frame_was_accelerated_(false),
text_input_type_(ui::TEXT_INPUT_TYPE_NONE),
can_compose_inline_(true),
- software_frame_needs_to_send_ack_(false),
- allow_overlapping_views_(false),
- use_core_animation_(false),
+ backing_store_scale_factor_(1),
is_loading_(false),
weak_factory_(this),
fullscreen_parent_host_view_(NULL),
- pending_swap_buffers_acks_weak_factory_(this),
- underlay_view_has_drawn_(false),
overlay_view_weak_factory_(this),
- next_swap_ack_time_(base::Time::Now()),
software_frame_weak_ptr_factory_(this) {
software_frame_manager_.reset(new SoftwareFrameManager(
software_frame_weak_ptr_factory_.GetWeakPtr()));
@@ -431,9 +469,11 @@ RenderWidgetHostViewMac::RenderWidgetHostViewMac(RenderWidgetHost* widget)
cocoa_view_ = [[[RenderWidgetHostViewCocoa alloc]
initWithRenderWidgetHostViewMac:this] autorelease];
- if (GetCoreAnimationStatus() == CORE_ANIMATION_ENABLED_ALWAYS) {
- EnableCoreAnimation();
- }
+ background_layer_.reset([[CALayer alloc] init]);
+ [background_layer_
+ setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)];
+ [cocoa_view_ setLayer:background_layer_];
+ [cocoa_view_ setWantsLayer:YES];
render_widget_host_->SetView(this);
}
@@ -443,12 +483,11 @@ RenderWidgetHostViewMac::~RenderWidgetHostViewMac() {
// pointer.
cocoa_view_ = nil;
- AckPendingSwapBuffers();
UnlockMouse();
// Make sure that the layer doesn't reach into the now-invalid object.
- DestroyCompositedIOSurfaceAndLayer(kDestroyContext);
- software_layer_.reset();
+ DestroyCompositedIOSurfaceAndLayer();
+ DestroySoftwareLayer();
// We are owned by RenderWidgetHostViewCocoa, so if we go away before the
// RenderWidgetHost does we need to tell it not to hold a stale pointer to
@@ -459,62 +498,34 @@ RenderWidgetHostViewMac::~RenderWidgetHostViewMac() {
void RenderWidgetHostViewMac::SetDelegate(
NSObject<RenderWidgetHostViewMacDelegate>* delegate) {
- [cocoa_view_ setDelegate:delegate];
+ [cocoa_view_ setResponderDelegate:delegate];
}
void RenderWidgetHostViewMac::SetAllowOverlappingViews(bool overlapping) {
- if (GetCoreAnimationStatus() == CORE_ANIMATION_ENABLED_LAZY) {
- if (overlapping) {
- ScopedCAActionDisabler disabler;
- EnableCoreAnimation();
- return;
- }
- }
-
- if (allow_overlapping_views_ == overlapping)
- return;
- allow_overlapping_views_ = overlapping;
- [cocoa_view_ setNeedsDisplay:YES];
- [[cocoa_view_ window] disableScreenUpdatesUntilFlush];
+ // TODO(ccameron): Remove callers of this function.
}
///////////////////////////////////////////////////////////////////////////////
// RenderWidgetHostViewMac, RenderWidgetHostView implementation:
-void RenderWidgetHostViewMac::EnableCoreAnimation() {
- if (use_core_animation_)
- return;
-
- use_core_animation_ = true;
-
- // Un-bind the GL context from this view because the CoreAnimation path will
- // not use explicit setView and clearDrawable calls.
- ClearBoundContextDrawable();
-
- software_layer_.reset([[CALayer alloc] init]);
- if (!software_layer_)
- LOG(ERROR) << "Failed to create CALayer for software rendering";
- [software_layer_ setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)];
- [software_layer_ setDelegate:cocoa_view_];
- [software_layer_ setContentsGravity:kCAGravityTopLeft];
- [software_layer_ setFrame:NSRectToCGRect([cocoa_view_ bounds])];
- [software_layer_ setNeedsDisplay];
- [cocoa_view_ updateSoftwareLayerScaleFactor];
-
- [cocoa_view_ setLayer:software_layer_];
- [cocoa_view_ setWantsLayer:YES];
-
- if (compositing_iosurface_) {
- if (!CreateCompositedIOSurfaceLayer()) {
- LOG(ERROR) << "Failed to create CALayer for existing IOSurface";
- GotAcceleratedCompositingError();
- return;
- }
+bool RenderWidgetHostViewMac::EnsureCompositedIOSurface() {
+ // If the context or the IOSurface's context has had an error, re-build
+ // everything from scratch.
+ if (compositing_iosurface_context_ &&
+ compositing_iosurface_context_->HasBeenPoisoned()) {
+ LOG(ERROR) << "Failing EnsureCompositedIOSurface because "
+ << "context was poisoned";
+ return false;
+ }
+ if (compositing_iosurface_ &&
+ compositing_iosurface_->HasBeenPoisoned()) {
+ LOG(ERROR) << "Failing EnsureCompositedIOSurface because "
+ << "surface was poisoned";
+ return false;
}
-}
-bool RenderWidgetHostViewMac::CreateCompositedIOSurface() {
- int current_window_number = window_number();
+ int current_window_number =
+ CompositingIOSurfaceContext::kOffscreenContextWindowNumber;
bool new_surface_needed = !compositing_iosurface_;
bool new_context_needed =
!compositing_iosurface_context_ ||
@@ -525,8 +536,6 @@ bool RenderWidgetHostViewMac::CreateCompositedIOSurface() {
if (!new_surface_needed && !new_context_needed)
return true;
- ScopedCAActionDisabler disabler;
-
// Create the GL context and shaders.
if (new_context_needed) {
scoped_refptr<CompositingIOSurfaceContext> new_context =
@@ -535,7 +544,6 @@ bool RenderWidgetHostViewMac::CreateCompositedIOSurface() {
// context. Having two GL contexts bound to a view will result in
// crashes and corruption.
// http://crbug.com/230883
- ClearBoundContextDrawable();
if (!new_context) {
LOG(ERROR) << "Failed to create CompositingIOSurfaceContext";
return false;
@@ -545,7 +553,7 @@ bool RenderWidgetHostViewMac::CreateCompositedIOSurface() {
// Create the IOSurface texture.
if (new_surface_needed) {
- compositing_iosurface_.reset(CompositingIOSurfaceMac::Create());
+ compositing_iosurface_ = CompositingIOSurfaceMac::Create();
if (!compositing_iosurface_) {
LOG(ERROR) << "Failed to create CompositingIOSurface";
return false;
@@ -555,66 +563,68 @@ bool RenderWidgetHostViewMac::CreateCompositedIOSurface() {
return true;
}
-bool RenderWidgetHostViewMac::CreateCompositedIOSurfaceLayer() {
- CHECK(compositing_iosurface_context_ && compositing_iosurface_);
- if (compositing_iosurface_layer_ || !use_core_animation_)
- return true;
+void RenderWidgetHostViewMac::EnsureSoftwareLayer() {
+ TRACE_EVENT0("browser", "RenderWidgetHostViewMac::EnsureSoftwareLayer");
+ if (software_layer_)
+ return;
+ software_layer_.reset([[SoftwareLayer alloc] init]);
+ DCHECK(software_layer_);
+
+ // Disable the fade-in animation as the layer is added.
ScopedCAActionDisabler disabler;
+ [background_layer_ addSublayer:software_layer_];
+}
- // Create the GL CoreAnimation layer.
- if (!compositing_iosurface_layer_) {
- compositing_iosurface_layer_.reset([[CompositingIOSurfaceLayer alloc]
- initWithRenderWidgetHostViewMac:this]);
- if (!compositing_iosurface_layer_) {
- LOG(ERROR) << "Failed to create CALayer for IOSurface";
- return false;
- }
- [software_layer_ addSublayer:compositing_iosurface_layer_];
- }
+void RenderWidgetHostViewMac::DestroySoftwareLayer() {
+ if (!software_layer_)
+ return;
- // Creating the CompositingIOSurfaceLayer may attempt to draw in setLayer,
- // which, if it fails, will promptly tear down everything that was just
- // created. If that happened, return failure.
- return compositing_iosurface_context_ &&
- compositing_iosurface_ &&
- (compositing_iosurface_layer_ || !use_core_animation_);
+ // Disable the fade-out animation as the layer is removed.
+ ScopedCAActionDisabler disabler;
+ [software_layer_ removeFromSuperlayer];
+ software_layer_.reset();
}
-void RenderWidgetHostViewMac::DestroyCompositedIOSurfaceAndLayer(
- DestroyContextBehavior destroy_context_behavior) {
+void RenderWidgetHostViewMac::EnsureCompositedIOSurfaceLayer() {
+ TRACE_EVENT0("browser",
+ "RenderWidgetHostViewMac::EnsureCompositedIOSurfaceLayer");
+ DCHECK(compositing_iosurface_context_);
+ if (compositing_iosurface_layer_)
+ return;
+
+ compositing_iosurface_layer_.reset([[CompositingIOSurfaceLayer alloc]
+ initWithIOSurface:compositing_iosurface_
+ withScaleFactor:compositing_iosurface_->scale_factor()
+ withClient:this]);
+ DCHECK(compositing_iosurface_layer_);
+
+ // Disable the fade-in animation as the layer is added.
ScopedCAActionDisabler disabler;
+ [background_layer_ addSublayer:compositing_iosurface_layer_];
+}
+
+void RenderWidgetHostViewMac::DestroyCompositedIOSurfaceLayer(
+ DestroyCompositedIOSurfaceLayerBehavior destroy_layer_behavior) {
+ if (!compositing_iosurface_layer_)
+ return;
- compositing_iosurface_.reset();
- if (compositing_iosurface_layer_) {
- [software_layer_ setNeedsDisplay];
+ if (destroy_layer_behavior == kRemoveLayerFromHierarchy) {
+ // Disable the fade-out animation as the layer is removed.
+ ScopedCAActionDisabler disabler;
[compositing_iosurface_layer_ removeFromSuperlayer];
- [compositing_iosurface_layer_ disableCompositing];
- compositing_iosurface_layer_.reset();
- }
- switch (destroy_context_behavior) {
- case kLeaveContextBoundToView:
- break;
- case kDestroyContext:
- ClearBoundContextDrawable();
- compositing_iosurface_context_ = NULL;
- break;
- default:
- NOTREACHED();
- break;
}
+ [compositing_iosurface_layer_ resetClient];
+ compositing_iosurface_layer_.reset();
}
-void RenderWidgetHostViewMac::ClearBoundContextDrawable() {
- if (compositing_iosurface_context_ &&
- cocoa_view_ &&
- [[compositing_iosurface_context_->nsgl_context() view]
- isEqual:cocoa_view_]) {
- // Disable screen updates because removing the GL context from below can
- // cause flashes.
- [[cocoa_view_ window] disableScreenUpdatesUntilFlush];
- [compositing_iosurface_context_->nsgl_context() clearDrawable];
- }
+void RenderWidgetHostViewMac::DestroyCompositedIOSurfaceAndLayer() {
+ // Any pending frames will not be displayed, so ack them now.
+ SendPendingSwapAck();
+
+ DestroyCompositedIOSurfaceLayer(kRemoveLayerFromHierarchy);
+ compositing_iosurface_ = NULL;
+ compositing_iosurface_context_ = NULL;
}
bool RenderWidgetHostViewMac::OnMessageReceived(const IPC::Message& message) {
@@ -639,10 +649,7 @@ void RenderWidgetHostViewMac::InitAsPopup(
[cocoa_view_ setCanBeKeyView:activatable ? YES : NO];
NSPoint origin_global = NSPointFromCGPoint(pos.origin().ToCGPoint());
- if ([[NSScreen screens] count] > 0) {
- origin_global.y = [[[NSScreen screens] objectAtIndex:0] frame].size.height -
- pos.height() - origin_global.y;
- }
+ origin_global.y = FlipYFromRectToScreen(origin_global.y, pos.height());
popup_window_.reset([[RenderWidgetPopupWindow alloc]
initWithContentRect:NSMakeRect(origin_global.x, origin_global.y,
@@ -729,8 +736,51 @@ int RenderWidgetHostViewMac::window_number() const {
return [window windowNumber];
}
-float RenderWidgetHostViewMac::scale_factor() const {
- return ScaleFactor(cocoa_view_);
+float RenderWidgetHostViewMac::ViewScaleFactor() const {
+ return ui::GetScaleFactorForNativeView(cocoa_view_);
+}
+
+void RenderWidgetHostViewMac::UpdateDisplayLink() {
+ static bool is_vsync_disabled =
+ CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableGpuVsync);
+ if (is_vsync_disabled)
+ return;
+
+ NSScreen* screen = [[cocoa_view_ window] screen];
+ NSDictionary* screen_description = [screen deviceDescription];
+ NSNumber* screen_number = [screen_description objectForKey:@"NSScreenNumber"];
+ CGDirectDisplayID display_id = [screen_number unsignedIntValue];
+
+ display_link_ = DisplayLinkMac::GetForDisplay(display_id);
+ if (!display_link_) {
+ // Note that on some headless systems, the display link will fail to be
+ // created, so this should not be a fatal error.
+ LOG(ERROR) << "Failed to create display link.";
+ }
+}
+
+void RenderWidgetHostViewMac::SendVSyncParametersToRenderer() {
+ if (!render_widget_host_ || !display_link_)
+ return;
+
+ base::TimeTicks timebase;
+ base::TimeDelta interval;
+ if (!display_link_->GetVSyncParameters(&timebase, &interval))
+ return;
+
+ render_widget_host_->UpdateVSyncParameters(timebase, interval);
+}
+
+void RenderWidgetHostViewMac::UpdateBackingStoreScaleFactor() {
+ if (!render_widget_host_)
+ return;
+
+ float new_scale_factor = ui::GetScaleFactorForNativeView(cocoa_view_);
+ if (new_scale_factor == backing_store_scale_factor_)
+ return;
+ backing_store_scale_factor_ = new_scale_factor;
+
+ render_widget_host_->NotifyScreenInfoChanged();
}
RenderWidgetHost* RenderWidgetHostViewMac::GetRenderWidgetHost() const {
@@ -741,40 +791,34 @@ void RenderWidgetHostViewMac::WasShown() {
if (!render_widget_host_->is_hidden())
return;
- if (web_contents_switch_paint_time_.is_null())
- web_contents_switch_paint_time_ = base::TimeTicks::Now();
render_widget_host_->WasShown();
software_frame_manager_->SetVisibility(true);
-
- // We're messing with the window, so do this to ensure no flashes.
- if (!use_core_animation_)
- [[cocoa_view_ window] disableScreenUpdatesUntilFlush];
-
+ if (delegated_frame_host_)
+ delegated_frame_host_->WasShown();
+
+ // Call setNeedsDisplay before pausing for new frames to come in -- if any
+ // do, and are drawn, then the needsDisplay bit will be cleared.
+ // Workaround for crbug.com/395827
+ if ([compositing_iosurface_layer_ isAsynchronous])
+ [compositing_iosurface_layer_ setAsynchronous:NO];
[compositing_iosurface_layer_ setNeedsDisplay];
+ PauseForPendingResizeOrRepaintsAndDraw();
}
void RenderWidgetHostViewMac::WasHidden() {
if (render_widget_host_->is_hidden())
return;
- // Send ACKs for any pending SwapBuffers (if any) since we won't be displaying
- // them and the GPU process is waiting.
- AckPendingSwapBuffers();
+ // Any pending frames will not be displayed until this is shown again. Ack
+ // them now.
+ SendPendingSwapAck();
// If we have a renderer, then inform it that we are being hidden so it can
// reduce its resource utilization.
render_widget_host_->WasHidden();
software_frame_manager_->SetVisibility(false);
-
- // There can be a transparent flash as this view is removed and the next is
- // added, because of OSX windowing races between displaying the contents of
- // the NSView and its corresponding OpenGL context.
- // disableScreenUpdatesUntilFlush prevents the transparent flash by avoiding
- // screen updates until the next tab draws.
- if (!use_core_animation_)
- [[cocoa_view_ window] disableScreenUpdatesUntilFlush];
-
- web_contents_switch_paint_time_ = base::TimeTicks();
+ if (delegated_frame_host_)
+ delegated_frame_host_->WasHidden();
}
void RenderWidgetHostViewMac::SetSize(const gfx::Size& size) {
@@ -802,23 +846,27 @@ void RenderWidgetHostViewMac::SetBounds(const gfx::Rect& rect) {
// Ignore the position of |rect| for non-popup rwhvs. This is because
// background tabs do not have a window, but the window is required for the
// coordinate conversions. Popups are always for a visible tab.
- if (IsPopup()) {
+ //
+ // Note: If |cocoa_view_| has been removed from the view hierarchy, it's still
+ // valid for resizing to be requested (e.g., during tab capture, to size the
+ // view to screen-capture resolution). In this case, simply treat the view as
+ // relative to the screen.
+ BOOL isRelativeToScreen = IsPopup() ||
+ ![[cocoa_view_ superview] isKindOfClass:[BaseView class]];
+ if (isRelativeToScreen) {
// The position of |rect| is screen coordinate system and we have to
// consider Cocoa coordinate system is upside-down and also multi-screen.
NSPoint origin_global = NSPointFromCGPoint(rect.origin().ToCGPoint());
NSSize size = NSMakeSize(rect.width(), rect.height());
size = [cocoa_view_ convertSize:size toView:nil];
- if ([[NSScreen screens] count] > 0) {
- NSScreen* screen =
- static_cast<NSScreen*>([[NSScreen screens] objectAtIndex:0]);
- origin_global.y =
- NSHeight([screen frame]) - size.height - origin_global.y;
- }
- [popup_window_ setFrame:NSMakeRect(origin_global.x, origin_global.y,
- size.width, size.height)
- display:YES];
+ origin_global.y = FlipYFromRectToScreen(origin_global.y, size.height);
+ NSRect frame = NSMakeRect(origin_global.x, origin_global.y,
+ size.width, size.height);
+ if (IsPopup())
+ [popup_window_ setFrame:frame display:YES];
+ else
+ [cocoa_view_ setFrame:frame];
} else {
- DCHECK([[cocoa_view_ superview] isKindOfClass:[BaseView class]]);
BaseView* superview = static_cast<BaseView*>([cocoa_view_ superview]);
gfx::Rect rect2 = [superview flipNSRectToRect:[cocoa_view_ frame]];
rect2.set_width(rect.width());
@@ -841,12 +889,11 @@ gfx::NativeViewAccessible RenderWidgetHostViewMac::GetNativeViewAccessible() {
}
void RenderWidgetHostViewMac::MovePluginWindows(
- const gfx::Vector2d& scroll_offset,
const std::vector<WebPluginGeometry>& moves) {
// Must be overridden, but unused on this platform. Core Animation
// plugins are drawn by the GPU process (through the compositor),
// and Core Graphics plugins are drawn by the renderer process.
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
void RenderWidgetHostViewMac::Focus() {
@@ -863,9 +910,11 @@ bool RenderWidgetHostViewMac::HasFocus() const {
}
bool RenderWidgetHostViewMac::IsSurfaceAvailableForCopy() const {
+ if (delegated_frame_host_)
+ return delegated_frame_host_->CanCopyToBitmap();
+
return software_frame_manager_->HasCurrentFrame() ||
- (compositing_iosurface_ && compositing_iosurface_->HasIOSurface()) ||
- !!render_widget_host_->GetBackingStore(false);
+ (compositing_iosurface_ && compositing_iosurface_->HasIOSurface());
}
void RenderWidgetHostViewMac::Show() {
@@ -875,10 +924,6 @@ void RenderWidgetHostViewMac::Show() {
}
void RenderWidgetHostViewMac::Hide() {
- // We're messing with the window, so do this to ensure no flashes.
- if (!use_core_animation_)
- [[cocoa_view_ window] disableScreenUpdatesUntilFlush];
-
[cocoa_view_ setHidden:YES];
WasHidden();
@@ -913,14 +958,12 @@ void RenderWidgetHostViewMac::SetIsLoading(bool is_loading) {
// like Chrome does on Windows, call |UpdateCursor()| here.
}
-void RenderWidgetHostViewMac::TextInputTypeChanged(
- ui::TextInputType type,
- ui::TextInputMode input_mode,
- bool can_compose_inline) {
- if (text_input_type_ != type
- || can_compose_inline_ != can_compose_inline) {
- text_input_type_ = type;
- can_compose_inline_ = can_compose_inline;
+void RenderWidgetHostViewMac::TextInputStateChanged(
+ const ViewHostMsg_TextInputState_Params& params) {
+ if (text_input_type_ != params.type ||
+ can_compose_inline_ != params.can_compose_inline) {
+ text_input_type_ = params.type;
+ can_compose_inline_ = params.can_compose_inline;
if (HasFocus()) {
SetTextInputActive(true);
@@ -949,65 +992,12 @@ void RenderWidgetHostViewMac::ImeCompositionRangeChanged(
composition_bounds_ = character_bounds;
}
-void RenderWidgetHostViewMac::DidUpdateBackingStore(
- const gfx::Rect& scroll_rect,
- const gfx::Vector2d& scroll_delta,
- const std::vector<gfx::Rect>& copy_rects,
- const ui::LatencyInfo& latency_info) {
- GotSoftwareFrame();
-
- software_latency_info_.MergeWith(latency_info);
-
- if (render_widget_host_->is_hidden())
- return;
-
- std::vector<gfx::Rect> rects(copy_rects);
-
- // Because the findbar might be open, we cannot use scrollRect:by: here. For
- // now, simply mark all of scroll rect as dirty.
- if (!scroll_rect.IsEmpty())
- rects.push_back(scroll_rect);
-
- for (size_t i = 0; i < rects.size(); ++i) {
- NSRect ns_rect = [cocoa_view_ flipRectToNSRect:rects[i]];
-
- if (about_to_validate_and_paint_) {
- // As much as we'd like to use -setNeedsDisplayInRect: here, we can't.
- // We're in the middle of executing a -drawRect:, and as soon as it
- // returns Cocoa will clear its record of what needs display. We instead
- // use |performSelector:| to call |setNeedsDisplayInRect:| after returning
- // to the main loop, at which point |drawRect:| is no longer on the
- // stack.
- DCHECK([NSThread isMainThread]);
- if (!call_set_needs_display_in_rect_pending_) {
- [cocoa_view_ performSelector:@selector(callSetNeedsDisplayInRect)
- withObject:nil
- afterDelay:0];
- call_set_needs_display_in_rect_pending_ = true;
- invalid_rect_ = ns_rect;
- } else {
- // The old invalid rect is probably invalid now, since the view has most
- // likely been resized, but there's no harm in dirtying the union. In
- // the limit, this becomes equivalent to dirtying the whole view.
- invalid_rect_ = NSUnionRect(invalid_rect_, ns_rect);
- }
- } else {
- [cocoa_view_ setNeedsDisplayInRect:ns_rect];
- }
- }
-
- if (!about_to_validate_and_paint_)
- [cocoa_view_ displayIfNeeded];
-}
-
void RenderWidgetHostViewMac::RenderProcessGone(base::TerminationStatus status,
int error_code) {
Destroy();
}
void RenderWidgetHostViewMac::Destroy() {
- AckPendingSwapBuffers();
-
[[NSNotificationCenter defaultCenter]
removeObserver:cocoa_view_
name:NSWindowWillCloseNotification
@@ -1030,6 +1020,12 @@ void RenderWidgetHostViewMac::Destroy() {
// object needs to survive until the stack unwinds.
pepper_fullscreen_window_.autorelease();
+ // Delete the delegated frame state, which will reach back into
+ // render_widget_host_.
+ [browser_compositor_view_ resetClient];
+ delegated_frame_host_.reset();
+ root_layer_.reset();
+
// We get this call just before |render_widget_host_| deletes
// itself. But we are owned by |cocoa_view_|, which may be retained
// by some other code. Examples are WebContentsViewMac's
@@ -1097,7 +1093,7 @@ void RenderWidgetHostViewMac::SelectionChanged(const base::string16& text,
DCHECK(false) << "The text can not cover range.";
return;
}
- selected_text_ = UTF16ToUTF8(text.substr(pos, n));
+ selected_text_ = base::UTF16ToUTF8(text.substr(pos, n));
}
[cocoa_view_ setSelectedRange:range.ToNSRange()];
@@ -1152,19 +1148,24 @@ bool RenderWidgetHostViewMac::IsPopup() const {
return popup_type_ != blink::WebPopupTypeNone;
}
-BackingStore* RenderWidgetHostViewMac::AllocBackingStore(
- const gfx::Size& size) {
- float scale = ScaleFactor(cocoa_view_);
- return new BackingStoreMac(render_widget_host_, size, scale);
-}
-
void RenderWidgetHostViewMac::CopyFromCompositingSurface(
const gfx::Rect& src_subrect,
const gfx::Size& dst_size,
- const base::Callback<void(bool, const SkBitmap&)>& callback) {
+ const base::Callback<void(bool, const SkBitmap&)>& callback,
+ const SkBitmap::Config config) {
+ if (delegated_frame_host_) {
+ delegated_frame_host_->CopyFromCompositingSurface(
+ src_subrect, dst_size, callback, config);
+ return;
+ }
+
+ if (config != SkBitmap::kARGB_8888_Config) {
+ NOTIMPLEMENTED();
+ callback.Run(false, SkBitmap());
+ }
base::ScopedClosureRunner scoped_callback_runner(
base::Bind(callback, false, SkBitmap()));
- float scale = ScaleFactor(cocoa_view_);
+ float scale = ui::GetScaleFactorForNativeView(cocoa_view_);
gfx::Size dst_pixel_size = gfx::ToFlooredSize(
gfx::ScaleSize(dst_size, scale));
if (compositing_iosurface_ && compositing_iosurface_->HasIOSurface()) {
@@ -1208,6 +1209,8 @@ void RenderWidgetHostViewMac::CopyFromCompositingSurface(
ignore_result(scoped_callback_runner.Release());
callback.Run(true, target_bitmap);
+ } else {
+ callback.Run(false, SkBitmap());
}
}
@@ -1215,10 +1218,14 @@ void RenderWidgetHostViewMac::CopyFromCompositingSurfaceToVideoFrame(
const gfx::Rect& src_subrect,
const scoped_refptr<media::VideoFrame>& target,
const base::Callback<void(bool)>& callback) {
+ if (delegated_frame_host_) {
+ delegated_frame_host_->CopyFromCompositingSurfaceToVideoFrame(
+ src_subrect, target, callback);
+ return;
+ }
+
base::ScopedClosureRunner scoped_callback_runner(base::Bind(callback, false));
- if (!render_widget_host_->is_accelerated_compositing_active() ||
- !compositing_iosurface_ ||
- !compositing_iosurface_->HasIOSurface())
+ if (!compositing_iosurface_ || !compositing_iosurface_->HasIOSurface())
return;
if (!target.get()) {
@@ -1243,23 +1250,36 @@ void RenderWidgetHostViewMac::CopyFromCompositingSurfaceToVideoFrame(
}
bool RenderWidgetHostViewMac::CanCopyToVideoFrame() const {
- return (!render_widget_host_->GetBackingStore(false) &&
- !software_frame_manager_->HasCurrentFrame() &&
- render_widget_host_->is_accelerated_compositing_active() &&
+ if (delegated_frame_host_)
+ return delegated_frame_host_->CanCopyToVideoFrame();
+
+ return (!software_frame_manager_->HasCurrentFrame() &&
compositing_iosurface_ &&
compositing_iosurface_->HasIOSurface());
}
bool RenderWidgetHostViewMac::CanSubscribeFrame() const {
+ if (delegated_frame_host_)
+ return delegated_frame_host_->CanSubscribeFrame();
+
return !software_frame_manager_->HasCurrentFrame();
}
void RenderWidgetHostViewMac::BeginFrameSubscription(
scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) {
+ if (delegated_frame_host_) {
+ delegated_frame_host_->BeginFrameSubscription(subscriber.Pass());
+ return;
+ }
frame_subscriber_ = subscriber.Pass();
}
void RenderWidgetHostViewMac::EndFrameSubscription() {
+ if (delegated_frame_host_) {
+ delegated_frame_host_->EndFrameSubscription();
+ return;
+ }
+
frame_subscriber_.reset();
}
@@ -1309,100 +1329,87 @@ void RenderWidgetHostViewMac::PluginImeCompositionCompleted(
}
void RenderWidgetHostViewMac::CompositorSwapBuffers(
- uint64 surface_handle,
+ IOSurfaceID surface_handle,
const gfx::Size& size,
float surface_scale_factor,
- const ui::LatencyInfo& latency_info) {
+ const std::vector<ui::LatencyInfo>& latency_info) {
+ // Ensure that the frame be acked unless it is explicitly passed to a
+ // display function.
+ base::ScopedClosureRunner scoped_ack(
+ base::Bind(&RenderWidgetHostViewMac::SendPendingSwapAck,
+ weak_factory_.GetWeakPtr()));
+
if (render_widget_host_->is_hidden())
return;
- NSWindow* window = [cocoa_view_ window];
- if (window_number() <= 0) {
- // There is no window to present so capturing during present won't work.
- // We check if frame subscriber wants this frame and capture manually.
- if (compositing_iosurface_ && frame_subscriber_) {
- const base::Time present_time = base::Time::Now();
- scoped_refptr<media::VideoFrame> frame;
- RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback callback;
- if (frame_subscriber_->ShouldCaptureFrame(present_time,
- &frame, &callback)) {
- CGLSetCurrentContext(compositing_iosurface_context_->cgl_context());
- compositing_iosurface_->SetIOSurfaceWithContextCurrent(
- compositing_iosurface_context_, surface_handle, size,
- surface_scale_factor, latency_info);
- compositing_iosurface_->CopyToVideoFrame(
- gfx::Rect(size), frame,
- base::Bind(callback, present_time));
- return;
- }
- }
+ // Ensure that if this function exits before the frame is set up (but not
+ // necessarily drawn) then it is treated as an error.
+ base::ScopedClosureRunner scoped_error(
+ base::Bind(&RenderWidgetHostViewMac::GotAcceleratedCompositingError,
+ weak_factory_.GetWeakPtr()));
- // TODO(shess) If the view does not have a window, or the window
- // does not have backing, the IOSurface will log "invalid drawable"
- // in -setView:. It is not clear how this code is reached with such
- // a case, so record some info into breakpad (some subset of
- // browsers are likely to crash later for unrelated reasons).
- // http://crbug.com/148882
- const char* const kCrashKey = "rwhvm_window";
- if (!window) {
- base::debug::SetCrashKeyValue(kCrashKey, "Missing window");
- } else {
- std::string value =
- base::StringPrintf("window %s delegate %s controller %s",
- object_getClassName(window),
- object_getClassName([window delegate]),
- object_getClassName([window windowController]));
- base::debug::SetCrashKeyValue(kCrashKey, value);
- }
+ AddPendingLatencyInfo(latency_info);
- return;
+ // If compositing_iosurface_ exists and has been poisoned, destroy it
+ // and allow EnsureCompositedIOSurface to recreate it below. Keep a
+ // reference to the destroyed layer around until after the below call
+ // to LayoutLayers, to avoid flickers.
+ base::ScopedClosureRunner scoped_layer_remover;
+ if (compositing_iosurface_context_ &&
+ compositing_iosurface_context_->HasBeenPoisoned()) {
+ scoped_layer_remover.Reset(
+ base::Bind(RemoveLayerFromSuperlayer, compositing_iosurface_layer_));
+ DestroyCompositedIOSurfaceLayer(kLeaveLayerInHierarchy);
+ DestroyCompositedIOSurfaceAndLayer();
}
// Ensure compositing_iosurface_ and compositing_iosurface_context_ be
// allocated.
- if (!CreateCompositedIOSurface()) {
- LOG(ERROR) << "Failed to create CompositingIOSurface";
- GotAcceleratedCompositingError();
+ if (!EnsureCompositedIOSurface()) {
+ LOG(ERROR) << "Failed EnsureCompositingIOSurface";
return;
}
// Make the context current and update the IOSurface with the handle
// passed in by the swap command.
- CGLSetCurrentContext(compositing_iosurface_context_->cgl_context());
- if (!compositing_iosurface_->SetIOSurfaceWithContextCurrent(
- compositing_iosurface_context_, surface_handle, size,
- surface_scale_factor, latency_info)) {
- LOG(ERROR) << "Failed SetIOSurface on CompositingIOSurfaceMac";
- GotAcceleratedCompositingError();
- return;
+ {
+ gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
+ compositing_iosurface_context_->cgl_context());
+ if (!compositing_iosurface_->SetIOSurfaceWithContextCurrent(
+ compositing_iosurface_context_, surface_handle, size,
+ surface_scale_factor)) {
+ LOG(ERROR) << "Failed SetIOSurface on CompositingIOSurfaceMac";
+ return;
+ }
}
// Grab video frames now that the IOSurface has been set up. Note that this
// will be done in an offscreen context, so it is necessary to re-set the
// current context afterward.
+ bool frame_was_captured = false;
if (frame_subscriber_) {
- const base::Time present_time = base::Time::Now();
+ const base::TimeTicks present_time = base::TimeTicks::Now();
scoped_refptr<media::VideoFrame> frame;
RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback callback;
if (frame_subscriber_->ShouldCaptureFrame(present_time,
&frame, &callback)) {
// Flush the context that updated the IOSurface, to ensure that the
// context that does the copy picks up the correct version.
- glFlush();
+ {
+ gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
+ compositing_iosurface_context_->cgl_context());
+ glFlush();
+ }
compositing_iosurface_->CopyToVideoFrame(
gfx::Rect(size), frame,
base::Bind(callback, present_time));
- CGLSetCurrentContext(compositing_iosurface_context_->cgl_context());
+ frame_was_captured = true;
}
}
- // Create the layer for the composited content only after the IOSurface has
- // been initialized.
- if (!CreateCompositedIOSurfaceLayer()) {
- LOG(ERROR) << "Failed to create CompositingIOSurface layer";
- GotAcceleratedCompositingError();
- return;
- }
+ // At this point the surface, its context, and its layer have been set up, so
+ // don't generate an error (one may be generated when drawing).
+ ignore_result(scoped_error.Release());
GotAcceleratedFrame();
@@ -1412,146 +1419,93 @@ void RenderWidgetHostViewMac::CompositorSwapBuffers(
// empty, so ack now and don't bother calling setNeedsDisplay below.
return;
}
+ if (window_number() <= 0) {
+ // It's normal for a backgrounded tab that is being captured to have no
+ // window but not be hidden. Immediately ack the frame, and don't try to
+ // draw it.
+ if (frame_was_captured)
+ return;
+
+ // If this frame was not captured, there is likely some sort of bug. Ack
+ // the frame and hope for the best. Because the IOSurface and layer are
+ // populated, it will likely be displayed when the view is added to a
+ // window's hierarchy.
- // No need to draw the surface if we are inside a drawRect. It will be done
- // later.
- if (!about_to_validate_and_paint_) {
- if (use_core_animation_) {
- DCHECK(compositing_iosurface_layer_);
- [compositing_iosurface_layer_ setNeedsDisplay];
+ // TODO(shess) If the view does not have a window, or the window
+ // does not have backing, the IOSurface will log "invalid drawable"
+ // in -setView:. It is not clear how this code is reached with such
+ // a case, so record some info into breakpad (some subset of
+ // browsers are likely to crash later for unrelated reasons).
+ // http://crbug.com/148882
+ const char* const kCrashKey = "rwhvm_window";
+ NSWindow* window = [cocoa_view_ window];
+ if (!window) {
+ base::debug::SetCrashKeyValue(kCrashKey, "Missing window");
} else {
- if (!DrawIOSurfaceWithoutCoreAnimation()) {
- [cocoa_view_ setNeedsDisplay:YES];
- GotAcceleratedCompositingError();
- return;
- }
+ std::string value =
+ base::StringPrintf("window %s delegate %s controller %s",
+ object_getClassName(window),
+ object_getClassName([window delegate]),
+ object_getClassName([window windowController]));
+ base::debug::SetCrashKeyValue(kCrashKey, value);
}
+ return;
}
-}
-void RenderWidgetHostViewMac::AckPendingSwapBuffers() {
- TRACE_EVENT0("browser", "RenderWidgetHostViewMac::AckPendingSwapBuffers");
-
- // Cancel any outstanding delayed calls to this function.
- pending_swap_buffers_acks_weak_factory_.InvalidateWeakPtrs();
-
- while (!pending_swap_buffers_acks_.empty()) {
- if (pending_swap_buffers_acks_.front().first != 0) {
- AcceleratedSurfaceMsg_BufferPresented_Params ack_params;
- ack_params.sync_point = 0;
- if (compositing_iosurface_)
- ack_params.renderer_id = compositing_iosurface_->GetRendererID();
- RenderWidgetHostImpl::AcknowledgeBufferPresent(
- pending_swap_buffers_acks_.front().first,
- pending_swap_buffers_acks_.front().second,
- ack_params);
- if (render_widget_host_) {
- render_widget_host_->AcknowledgeSwapBuffersToRenderer();
- }
- }
- pending_swap_buffers_acks_.erase(pending_swap_buffers_acks_.begin());
- }
-}
+ // If the window is occluded, then this frame's display call may be severely
+ // throttled. This is a good thing, unless tab capture may be active,
+ // because the broadcast will be inappropriately throttled.
+ // http://crbug.com/350410
+ NSWindow* window = [cocoa_view_ window];
+ if (window && [window respondsToSelector:@selector(occlusionState)]) {
+ bool window_is_occluded =
+ !([window occlusionState] & NSWindowOcclusionStateVisible);
+ // Note that we aggressively ack even if this particular frame is not being
+ // captured.
+ if (window_is_occluded && frame_subscriber_)
+ scoped_ack.Reset();
+ }
+
+ // If we reach here, then the frame will be displayed by a future draw
+ // call, so don't make the callback.
+ ignore_result(scoped_ack.Release());
+ DCHECK(compositing_iosurface_layer_);
+ [compositing_iosurface_layer_ gotNewFrame];
+
+ // Try to finish previous copy requests after draw to get better pipelining.
+ if (compositing_iosurface_)
+ compositing_iosurface_->CheckIfAllCopiesAreFinished(false);
-void RenderWidgetHostViewMac::ThrottledAckPendingSwapBuffers() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- // Send VSync parameters to the renderer's compositor thread.
- base::TimeTicks vsync_timebase;
- base::TimeDelta vsync_interval;
- GetVSyncParameters(&vsync_timebase, &vsync_interval);
- if (render_widget_host_ && compositing_iosurface_)
- render_widget_host_->UpdateVSyncParameters(vsync_timebase, vsync_interval);
-
- // If the render widget host is responsible for throttling swaps to vsync rate
- // then don't ack the swapbuffers until a full vsync has passed since the last
- // ack was sent.
- bool throttle_swap_ack =
- render_widget_host_ &&
- !render_widget_host_->is_threaded_compositing_enabled() &&
- compositing_iosurface_context_ &&
- !compositing_iosurface_context_->is_vsync_disabled();
- base::Time now = base::Time::Now();
- if (throttle_swap_ack && next_swap_ack_time_ > now) {
- base::TimeDelta next_swap_ack_delay = next_swap_ack_time_ - now;
- next_swap_ack_time_ += vsync_interval;
- base::MessageLoop::current()->PostDelayedTask(
- FROM_HERE,
- base::Bind(&RenderWidgetHostViewMac::AckPendingSwapBuffers,
- pending_swap_buffers_acks_weak_factory_.GetWeakPtr()),
- next_swap_ack_delay);
- } else {
- next_swap_ack_time_ = now + vsync_interval;
- AckPendingSwapBuffers();
- }
+ // The IOSurface's size may have changed, so re-layout the layers to take
+ // this into account. This may force an immediate draw.
+ LayoutLayers();
}
-bool RenderWidgetHostViewMac::DrawIOSurfaceWithoutCoreAnimation() {
- CHECK(!use_core_animation_);
- CHECK(compositing_iosurface_);
-
- GLint old_gl_surface_order = 0;
- GLint new_gl_surface_order = allow_overlapping_views_ ? -1 : 1;
- [compositing_iosurface_context_->nsgl_context()
- getValues:&old_gl_surface_order
- forParameter:NSOpenGLCPSurfaceOrder];
- if (old_gl_surface_order != new_gl_surface_order) {
- [compositing_iosurface_context_->nsgl_context()
- setValues:&new_gl_surface_order
- forParameter:NSOpenGLCPSurfaceOrder];
- }
-
- // Instead of drawing, request that underlay view redraws.
- if (underlay_view_ &&
- underlay_view_->compositing_iosurface_ &&
- underlay_view_has_drawn_) {
- [underlay_view_->cocoa_view() setNeedsDisplay:YES];
- return true;
- }
-
- bool has_overlay = overlay_view_ && overlay_view_->compositing_iosurface_;
- if (has_overlay) {
- // Un-bind the overlay view's OpenGL context, since its content will be
- // drawn by this context. Not doing this can result in corruption.
- // http://crbug.com/330701
- overlay_view_->ClearBoundContextDrawable();
- }
- [compositing_iosurface_context_->nsgl_context() setView:cocoa_view_];
-
- gfx::Rect view_rect(NSRectToCGRect([cocoa_view_ frame]));
- if (!compositing_iosurface_->DrawIOSurface(
- compositing_iosurface_context_, view_rect,
- scale_factor(), !has_overlay)) {
- return false;
- }
-
- if (has_overlay) {
- overlay_view_->underlay_view_has_drawn_ = true;
- gfx::Rect overlay_view_rect(
- NSRectToCGRect([overlay_view_->cocoa_view() frame]));
- overlay_view_rect.set_x(overlay_view_offset_.x());
- overlay_view_rect.set_y(view_rect.height() -
- overlay_view_rect.height() -
- overlay_view_offset_.y());
- if (!overlay_view_->compositing_iosurface_->DrawIOSurface(
- compositing_iosurface_context_, overlay_view_rect,
- overlay_view_->scale_factor(), true)) {
- return false;
- }
- }
-
- return true;
+void RenderWidgetHostViewMac::GotAcceleratedCompositingError() {
+ LOG(ERROR) << "Encountered accelerated compositing error";
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&RenderWidgetHostViewMac::DestroyCompositingStateOnError,
+ weak_factory_.GetWeakPtr()));
}
-void RenderWidgetHostViewMac::GotAcceleratedCompositingError() {
- AckPendingSwapBuffers();
- DestroyCompositedIOSurfaceAndLayer(kDestroyContext);
+void RenderWidgetHostViewMac::DestroyCompositingStateOnError() {
+ // This should be called with a clean stack. Make sure that no context is
+ // current.
+ DCHECK(!CGLGetCurrentContext());
+
// The existing GL contexts may be in a bad state, so don't re-use any of the
// existing ones anymore, rather, allocate new ones.
- CompositingIOSurfaceContext::MarkExistingContextsAsNotShareable();
- // Request that a new frame be generated.
+ if (compositing_iosurface_context_)
+ compositing_iosurface_context_->PoisonContextAndSharegroup();
+
+ DestroyCompositedIOSurfaceAndLayer();
+
+ // Request that a new frame be generated and dirty the view.
if (render_widget_host_)
render_widget_host_->ScheduleComposite();
+ [cocoa_view_ setNeedsDisplay:YES];
+
// TODO(ccameron): It may be a good idea to request that the renderer recreate
// its GL context as well, and fall back to software if this happens
// repeatedly.
@@ -1563,12 +1517,7 @@ void RenderWidgetHostViewMac::SetOverlayView(
overlay_view_->underlay_view_.reset();
overlay_view_ = overlay->overlay_view_weak_factory_.GetWeakPtr();
- overlay_view_offset_ = offset;
overlay_view_->underlay_view_ = overlay_view_weak_factory_.GetWeakPtr();
- overlay_view_->underlay_view_has_drawn_ = false;
-
- [cocoa_view_ setNeedsDisplay:YES];
- [[cocoa_view_ window] disableScreenUpdatesUntilFlush];
}
void RenderWidgetHostViewMac::RemoveOverlayView() {
@@ -1576,31 +1525,6 @@ void RenderWidgetHostViewMac::RemoveOverlayView() {
overlay_view_->underlay_view_.reset();
overlay_view_.reset();
}
- [cocoa_view_ setNeedsDisplay:YES];
- [[cocoa_view_ window] disableScreenUpdatesUntilFlush];
-}
-
-void RenderWidgetHostViewMac::GetVSyncParameters(
- base::TimeTicks* timebase, base::TimeDelta* interval) {
- if (compositing_iosurface_) {
- uint32 numerator = 0;
- uint32 denominator = 0;
- compositing_iosurface_->GetVSyncParameters(
- timebase, &numerator, &denominator);
- if (numerator > 0 && denominator > 0) {
- int64 interval_micros =
- 1000000 * static_cast<int64>(numerator) / denominator;
- *interval = base::TimeDelta::FromMicroseconds(interval_micros);
- return;
- }
- }
-
- // Pass reasonable default values if unable to get the actual ones
- // (e.g. CVDisplayLink failed to return them because the display is
- // in sleep mode).
- static const int64 kOneOverSixtyMicroseconds = 16669;
- *timebase = base::TimeTicks::Now(),
- *interval = base::TimeDelta::FromMicroseconds(kOneOverSixtyMicroseconds);
}
bool RenderWidgetHostViewMac::GetLineBreakIndex(
@@ -1688,6 +1612,14 @@ gfx::Range RenderWidgetHostViewMac::ConvertCharacterRangeToCompositionRange(
request_range.end() - composition_range_.start());
}
+WebContents* RenderWidgetHostViewMac::GetWebContents() {
+ if (!render_widget_host_->IsRenderView())
+ return NULL;
+
+ return WebContents::FromRenderViewHost(
+ RenderViewHost::From(render_widget_host_));
+}
+
bool RenderWidgetHostViewMac::GetCachedFirstRectForCharacterRange(
NSRange range,
NSRect* rect,
@@ -1733,17 +1665,18 @@ void RenderWidgetHostViewMac::AcceleratedSurfaceBuffersSwapped(
int gpu_host_id) {
TRACE_EVENT0("browser",
"RenderWidgetHostViewMac::AcceleratedSurfaceBuffersSwapped");
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- pending_swap_buffers_acks_.push_back(std::make_pair(params.route_id,
- gpu_host_id));
-
- CompositorSwapBuffers(params.surface_handle,
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ IOSurfaceID io_surface_handle =
+ static_cast<IOSurfaceID>(params.surface_handle);
+ AddPendingSwapAck(params.route_id,
+ gpu_host_id,
+ compositing_iosurface_ ?
+ compositing_iosurface_->GetRendererID() : 0);
+ CompositorSwapBuffers(io_surface_handle,
params.size,
params.scale_factor,
params.latency_info);
-
- ThrottledAckPendingSwapBuffers();
}
void RenderWidgetHostViewMac::AcceleratedSurfacePostSubBuffer(
@@ -1751,80 +1684,120 @@ void RenderWidgetHostViewMac::AcceleratedSurfacePostSubBuffer(
int gpu_host_id) {
TRACE_EVENT0("browser",
"RenderWidgetHostViewMac::AcceleratedSurfacePostSubBuffer");
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- pending_swap_buffers_acks_.push_back(std::make_pair(params.route_id,
- gpu_host_id));
-
- CompositorSwapBuffers(params.surface_handle,
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ IOSurfaceID io_surface_handle =
+ static_cast<IOSurfaceID>(params.surface_handle);
+ AddPendingSwapAck(params.route_id,
+ gpu_host_id,
+ compositing_iosurface_ ?
+ compositing_iosurface_->GetRendererID() : 0);
+ CompositorSwapBuffers(io_surface_handle,
params.surface_size,
params.surface_scale_factor,
params.latency_info);
-
- ThrottledAckPendingSwapBuffers();
}
void RenderWidgetHostViewMac::AcceleratedSurfaceSuspend() {
- if (compositing_iosurface_)
- compositing_iosurface_->UnrefIOSurface();
+ if (!render_widget_host_->is_hidden())
+ DestroyCompositedIOSurfaceAndLayer();
}
void RenderWidgetHostViewMac::AcceleratedSurfaceRelease() {
- DestroyCompositedIOSurfaceAndLayer(kDestroyContext);
+ DestroyCompositedIOSurfaceAndLayer();
}
bool RenderWidgetHostViewMac::HasAcceleratedSurface(
const gfx::Size& desired_size) {
- if (last_frame_was_accelerated_) {
- return compositing_iosurface_ &&
- compositing_iosurface_->HasIOSurface() &&
+ if (compositing_iosurface_) {
+ return compositing_iosurface_->HasIOSurface() &&
(desired_size.IsEmpty() ||
compositing_iosurface_->dip_io_surface_size() == desired_size);
- } else {
- return (software_frame_manager_->HasCurrentFrame() &&
- (desired_size.IsEmpty() ||
+ }
+ if (software_frame_manager_->HasCurrentFrame()) {
+ return (desired_size.IsEmpty() ||
software_frame_manager_->GetCurrentFrameSizeInDIP() ==
- desired_size));
+ desired_size);
}
return false;
}
-void RenderWidgetHostViewMac::AboutToWaitForBackingStoreMsg() {
- AckPendingSwapBuffers();
-}
-
void RenderWidgetHostViewMac::OnSwapCompositorFrame(
uint32 output_surface_id, scoped_ptr<cc::CompositorFrame> frame) {
- // Only software compositor frames are accepted.
- if (!frame->software_frame_data) {
- DLOG(ERROR) << "Received unexpected frame type.";
- RecordAction(
- UserMetricsAction("BadMessageTerminate_UnexpectedFrameType"));
- render_widget_host_->GetProcess()->ReceivedBadMessage();
- return;
- }
+ TRACE_EVENT0("browser", "RenderWidgetHostViewMac::OnSwapCompositorFrame");
+
+ if (frame->delegated_frame_data) {
+ if (!browser_compositor_view_) {
+ browser_compositor_view_.reset(
+ [[BrowserCompositorViewMac alloc] initWithSuperview:cocoa_view_]);
+ root_layer_.reset(new ui::Layer(ui::LAYER_TEXTURED));
+ delegated_frame_host_.reset(new DelegatedFrameHost(this));
+ [browser_compositor_view_ compositor]->SetRootLayer(root_layer_.get());
+ }
- // Ack any swaps that didn't make it to the display.
- if (software_frame_needs_to_send_ack_)
- FrameSwapped();
+ float scale_factor = frame->metadata.device_scale_factor;
+ gfx::Size dip_size = ToCeiledSize(frame->metadata.viewport_size);
+ gfx::Size pixel_size = ConvertSizeToPixel(
+ scale_factor, dip_size);
+ [browser_compositor_view_ compositor]->SetScaleAndSize(
+ scale_factor, pixel_size);
+ root_layer_->SetBounds(gfx::Rect(dip_size));
+
+ delegated_frame_host_->SwapDelegatedFrame(
+ output_surface_id,
+ frame->delegated_frame_data.Pass(),
+ frame->metadata.device_scale_factor,
+ frame->metadata.latency_info);
+ } else if (frame->software_frame_data) {
+ if (!software_frame_manager_->SwapToNewFrame(
+ output_surface_id,
+ frame->software_frame_data.get(),
+ frame->metadata.device_scale_factor,
+ render_widget_host_->GetProcess()->GetHandle())) {
+ render_widget_host_->GetProcess()->ReceivedBadMessage();
+ return;
+ }
- if (!software_frame_manager_->SwapToNewFrame(
- output_surface_id,
- frame->software_frame_data.get(),
- frame->metadata.device_scale_factor,
- render_widget_host_->GetProcess()->GetHandle())) {
- render_widget_host_->GetProcess()->ReceivedBadMessage();
- return;
- }
+ // Add latency info to report when the frame finishes drawing.
+ AddPendingLatencyInfo(frame->metadata.latency_info);
- GotSoftwareFrame();
- software_latency_info_.MergeWith(frame->metadata.latency_info);
- software_frame_needs_to_send_ack_ = true;
+ const void* pixels = software_frame_manager_->GetCurrentFramePixels();
+ gfx::Size size_in_pixels =
+ software_frame_manager_->GetCurrentFrameSizeInPixels();
- [cocoa_view_ setNeedsDisplay:YES];
-}
+ EnsureSoftwareLayer();
+ [software_layer_ setContentsToData:pixels
+ withRowBytes:4 * size_in_pixels.width()
+ withPixelSize:size_in_pixels
+ withScaleFactor:frame->metadata.device_scale_factor];
+
+ // Send latency information to the host immediately, as there will be no
+ // subsequent draw call in which to do so.
+ SendPendingLatencyInfoToHost();
+
+ GotSoftwareFrame();
+
+ cc::CompositorFrameAck ack;
+ RenderWidgetHostImpl::SendSwapCompositorFrameAck(
+ render_widget_host_->GetRoutingID(),
+ software_frame_manager_->GetCurrentFrameOutputSurfaceId(),
+ render_widget_host_->GetProcess()->GetID(),
+ ack);
+ software_frame_manager_->SwapToNewFrameComplete(
+ !render_widget_host_->is_hidden());
-void RenderWidgetHostViewMac::OnAcceleratedCompositingStateChange() {
+ // Notify observers, tab capture observers in particular, that a new
+ // software frame has come in.
+ NotificationService::current()->Notify(
+ NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE,
+ Source<RenderWidgetHost>(render_widget_host_),
+ NotificationService::NoDetails());
+ } else {
+ DLOG(ERROR) << "Received unexpected frame type.";
+ RecordAction(
+ base::UserMetricsAction("BadMessageTerminate_UnexpectedFrameType"));
+ render_widget_host_->GetProcess()->ReceivedBadMessage();
+ }
}
void RenderWidgetHostViewMac::AcceleratedSurfaceInitialized(int host_id,
@@ -1853,17 +1826,6 @@ gfx::GLSurfaceHandle RenderWidgetHostViewMac::GetCompositingSurface() {
return gfx::GLSurfaceHandle(gfx::kNullPluginWindow, gfx::NATIVE_TRANSPORT);
}
-void RenderWidgetHostViewMac::SetHasHorizontalScrollbar(
- bool has_horizontal_scrollbar) {
- [cocoa_view_ setHasHorizontalScrollbar:has_horizontal_scrollbar];
-}
-
-void RenderWidgetHostViewMac::SetScrollOffsetPinning(
- bool is_pinned_to_left, bool is_pinned_to_right) {
- [cocoa_view_ scrollOffsetPinnedToLeft:is_pinned_to_left
- toRight:is_pinned_to_right];
-}
-
bool RenderWidgetHostViewMac::LockMouse() {
if (mouse_locked_)
return true;
@@ -1893,12 +1855,14 @@ void RenderWidgetHostViewMac::UnlockMouse() {
render_widget_host_->LostMouseLock();
}
-void RenderWidgetHostViewMac::UnhandledWheelEvent(
- const blink::WebMouseWheelEvent& event) {
+void RenderWidgetHostViewMac::WheelEventAck(
+ const blink::WebMouseWheelEvent& event,
+ InputEventAckState ack_result) {
+ bool consumed = ack_result == INPUT_EVENT_ACK_STATE_CONSUMED;
// Only record a wheel event as unhandled if JavaScript handlers got a chance
// to see it (no-op wheel events are ignored by the event dispatcher)
if (event.deltaX || event.deltaY)
- [cocoa_view_ gotUnhandledWheelEvent];
+ [cocoa_view_ processedWheelEvent:event consumed:consumed];
}
bool RenderWidgetHostViewMac::Send(IPC::Message* message) {
@@ -1922,6 +1886,7 @@ void RenderWidgetHostViewMac::SoftwareFrameWasFreed(
}
void RenderWidgetHostViewMac::ReleaseReferencesToSoftwareFrame() {
+ DestroySoftwareLayer();
}
void RenderWidgetHostViewMac::ShutdownHost() {
@@ -1931,49 +1896,30 @@ void RenderWidgetHostViewMac::ShutdownHost() {
}
void RenderWidgetHostViewMac::GotAcceleratedFrame() {
- // Update the scale factor of the layer to match the scale factor of the
- // IOSurface.
- [compositing_iosurface_layer_ updateScaleFactor];
+ EnsureCompositedIOSurfaceLayer();
+ SendVSyncParametersToRenderer();
- if (!last_frame_was_accelerated_) {
- last_frame_was_accelerated_ = true;
-
- if (!use_core_animation_) {
- // Need to wipe the software view with transparency to expose the GL
- // underlay. Invalidate the whole window to do that.
- [cocoa_view_ setNeedsDisplay:YES];
- }
-
- // Delete software backingstore.
- BackingStoreManager::RemoveBackingStore(render_widget_host_);
- software_frame_manager_->DiscardCurrentFrame();
- }
+ // Delete software backingstore and layer.
+ software_frame_manager_->DiscardCurrentFrame();
+ DestroySoftwareLayer();
}
void RenderWidgetHostViewMac::GotSoftwareFrame() {
- if (last_frame_was_accelerated_) {
- last_frame_was_accelerated_ = false;
-
- AckPendingSwapBuffers();
-
- // If overlapping views are allowed, then don't unbind the context
- // from the view (that is, don't call clearDrawble -- just delete the
- // texture and IOSurface). Rather, let it sit behind the software frame
- // that will be put up in front. This will prevent transparent
- // flashes.
- // http://crbug.com/154531
- // Also note that it is necessary that clearDrawable be called if
- // overlapping views are not allowed, e.g, for content shell.
- // http://crbug.com/178408
- // Disable screen updates so that the changes of flashes is minimized.
- // http://crbug.com/279472
- if (!use_core_animation_)
- [[cocoa_view_ window] disableScreenUpdatesUntilFlush];
- if (allow_overlapping_views_)
- DestroyCompositedIOSurfaceAndLayer(kLeaveContextBoundToView);
- else
- DestroyCompositedIOSurfaceAndLayer(kDestroyContext);
- }
+ TRACE_EVENT0("browser", "RenderWidgetHostViewMac::GotSoftwareFrame");
+
+ if (!render_widget_host_)
+ return;
+
+ EnsureSoftwareLayer();
+ LayoutLayers();
+ SendVSyncParametersToRenderer();
+
+ // Draw the contents of the frame immediately. It is critical that this
+ // happen before the frame be acked, otherwise the new frame will likely be
+ // ready before the drawing is complete, thrashing the browser main thread.
+ [software_layer_ displayIfNeeded];
+
+ DestroyCompositedIOSurfaceAndLayer();
}
void RenderWidgetHostViewMac::SetActive(bool active) {
@@ -2007,11 +1953,6 @@ void RenderWidgetHostViewMac::WindowFrameChanged() {
render_widget_host_->GetRoutingID(), GetBoundsInRootWindow(),
GetViewBounds()));
}
-
- if (compositing_iosurface_) {
- // This will migrate the context to the appropriate window.
- CreateCompositedIOSurface();
- }
}
void RenderWidgetHostViewMac::ShowDefinitionForSelection() {
@@ -2019,25 +1960,77 @@ void RenderWidgetHostViewMac::ShowDefinitionForSelection() {
helper.ShowDefinitionForSelection();
}
-void RenderWidgetHostViewMac::SetBackground(const SkBitmap& background) {
- RenderWidgetHostViewBase::SetBackground(background);
+void RenderWidgetHostViewMac::SetBackgroundOpaque(bool opaque) {
+ RenderWidgetHostViewBase::SetBackgroundOpaque(opaque);
if (render_widget_host_)
- render_widget_host_->Send(new ViewMsg_SetBackground(
- render_widget_host_->GetRoutingID(), background));
+ render_widget_host_->SetBackgroundOpaque(opaque);
}
-void RenderWidgetHostViewMac::OnAccessibilityEvents(
- const std::vector<AccessibilityHostMsg_EventParams>& params) {
+void RenderWidgetHostViewMac::CreateBrowserAccessibilityManagerIfNeeded() {
if (!GetBrowserAccessibilityManager()) {
SetBrowserAccessibilityManager(
new BrowserAccessibilityManagerMac(
cocoa_view_,
BrowserAccessibilityManagerMac::GetEmptyDocument(),
- NULL));
+ render_widget_host_));
}
- GetBrowserAccessibilityManager()->OnAccessibilityEvents(params);
}
+gfx::Point RenderWidgetHostViewMac::AccessibilityOriginInScreen(
+ const gfx::Rect& bounds) {
+ NSPoint origin = NSMakePoint(bounds.x(), bounds.y());
+ NSSize size = NSMakeSize(bounds.width(), bounds.height());
+ origin.y = NSHeight([cocoa_view_ bounds]) - origin.y;
+ NSPoint originInWindow = [cocoa_view_ convertPoint:origin toView:nil];
+ NSPoint originInScreen =
+ [[cocoa_view_ window] convertBaseToScreen:originInWindow];
+ originInScreen.y = originInScreen.y - size.height;
+ return gfx::Point(originInScreen.x, originInScreen.y);
+}
+
+void RenderWidgetHostViewMac::OnAccessibilitySetFocus(int accObjId) {
+ // Immediately set the focused item even though we have not officially set
+ // focus on it as VoiceOver expects to get the focused item after this
+ // method returns.
+ BrowserAccessibilityManager* manager = GetBrowserAccessibilityManager();
+ if (manager)
+ manager->SetFocus(manager->GetFromID(accObjId), false);
+}
+
+void RenderWidgetHostViewMac::AccessibilityShowMenu(int accObjId) {
+ BrowserAccessibilityManager* manager = GetBrowserAccessibilityManager();
+ if (!manager)
+ return;
+ BrowserAccessibilityCocoa* obj =
+ manager->GetFromID(accObjId)->ToBrowserAccessibilityCocoa();
+
+ // Performs a right click copying WebKit's
+ // accessibilityPerformShowMenuAction.
+ NSPoint objOrigin = [obj origin];
+ NSSize size = [[obj size] sizeValue];
+ gfx::Point origin = AccessibilityOriginInScreen(
+ gfx::Rect(objOrigin.x, objOrigin.y, size.width, size.height));
+ NSPoint location = NSMakePoint(origin.x(), origin.y());
+ location = [[cocoa_view_ window] convertScreenToBase:location];
+ location.x += size.width/2;
+ location.y += size.height/2;
+
+ NSEvent* fakeRightClick = [NSEvent
+ mouseEventWithType:NSRightMouseDown
+ location:location
+ modifierFlags:0
+ timestamp:0
+ windowNumber:[[cocoa_view_ window] windowNumber]
+ context:[NSGraphicsContext currentContext]
+ eventNumber:0
+ clickCount:1
+ pressure:0];
+
+ [cocoa_view_ mouseEvent:fakeRightClick];
+}
+
+
+
void RenderWidgetHostViewMac::SetTextInputActive(bool active) {
if (active) {
if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD)
@@ -2065,28 +2058,153 @@ gfx::Rect RenderWidgetHostViewMac::GetScaledOpenGLPixelRect(
src_gl_subrect.set_y(GetViewBounds().height() - rect.bottom());
return gfx::ToEnclosingRect(gfx::ScaleRect(src_gl_subrect,
- scale_factor()));
+ ViewScaleFactor()));
}
-void RenderWidgetHostViewMac::FrameSwapped() {
- if (software_frame_needs_to_send_ack_ &&
- software_frame_manager_->HasCurrentFrame()) {
- software_frame_manager_->SwapToNewFrameComplete(
- !render_widget_host_->is_hidden());
+void RenderWidgetHostViewMac::AddPendingLatencyInfo(
+ const std::vector<ui::LatencyInfo>& latency_info) {
+ for (size_t i = 0; i < latency_info.size(); i++) {
+ pending_latency_info_.push_back(latency_info[i]);
+ }
+}
- cc::CompositorFrameAck ack;
- RenderWidgetHostImpl::SendSwapCompositorFrameAck(
- render_widget_host_->GetRoutingID(),
- software_frame_manager_->GetCurrentFrameOutputSurfaceId(),
- render_widget_host_->GetProcess()->GetID(),
- ack);
- software_frame_needs_to_send_ack_ = false;
+void RenderWidgetHostViewMac::SendPendingLatencyInfoToHost() {
+ for (size_t i = 0; i < pending_latency_info_.size(); i++) {
+ pending_latency_info_[i].AddLatencyNumber(
+ ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0);
+ render_widget_host_->FrameSwapped(pending_latency_info_[i]);
+ }
+ pending_latency_info_.clear();
+}
+
+void RenderWidgetHostViewMac::AddPendingSwapAck(
+ int32 route_id, int gpu_host_id, int32 renderer_id) {
+ // Note that multiple un-acked swaps can come in the event of a GPU process
+ // loss. Drop the old acks.
+ pending_swap_ack_.reset(new PendingSwapAck(
+ route_id, gpu_host_id, renderer_id));
+}
+
+void RenderWidgetHostViewMac::SendPendingSwapAck() {
+ if (!pending_swap_ack_)
+ return;
+
+ AcceleratedSurfaceMsg_BufferPresented_Params ack_params;
+ ack_params.sync_point = 0;
+ ack_params.renderer_id = pending_swap_ack_->renderer_id;
+ RenderWidgetHostImpl::AcknowledgeBufferPresent(pending_swap_ack_->route_id,
+ pending_swap_ack_->gpu_host_id,
+ ack_params);
+ pending_swap_ack_.reset();
+}
+
+void RenderWidgetHostViewMac::PauseForPendingResizeOrRepaintsAndDraw() {
+ if (!render_widget_host_ || render_widget_host_->is_hidden())
+ return;
+
+ // Pausing for the overlay/underlay view prevents the other one from receiving
+ // frames. This may lead to large delays, causing overlaps.
+ // See crbug.com/352020.
+ if (underlay_view_ || overlay_view_)
+ return;
+
+ // Ensure that all frames are acked before waiting for a frame to come in.
+ // Note that we will draw a frame at the end of this function, so it is safe
+ // to ack a never-drawn frame here.
+ SendPendingSwapAck();
+
+ // Wait for a frame of the right size to come in.
+ render_widget_host_->PauseForPendingResizeOrRepaints();
+
+ // Immediately draw any frames that haven't been drawn yet. This is necessary
+ // to keep the window and the window's contents in sync.
+ [cocoa_view_ displayIfNeeded];
+ [software_layer_ displayIfNeeded];
+ [compositing_iosurface_layer_ displayIfNeededAndAck];
+}
+
+void RenderWidgetHostViewMac::LayoutLayers() {
+ if (browser_compositor_view_) {
+ [browser_compositor_view_ layoutLayers];
+ return;
+ }
+
+ // Disable animation of the layer's resizing or change in contents scale.
+ ScopedCAActionDisabler disabler;
+
+ CGRect new_background_frame = NSRectToCGRect([cocoa_view() bounds]);
+
+ // Dynamically calling setContentsScale on a CAOpenGLLayer for which
+ // setAsynchronous is dynamically toggled can result in flashes of corrupt
+ // content. Work around this by replacing the entire layer when the scale
+ // factor changes.
+ if (compositing_iosurface_ &&
+ [compositing_iosurface_layer_
+ respondsToSelector:(@selector(contentsScale))]) {
+ if (compositing_iosurface_->scale_factor() !=
+ [compositing_iosurface_layer_ contentsScale]) {
+ DestroyCompositedIOSurfaceLayer(kRemoveLayerFromHierarchy);
+ EnsureCompositedIOSurfaceLayer();
+ }
+ }
+ if (compositing_iosurface_ &&
+ compositing_iosurface_->HasIOSurface() &&
+ compositing_iosurface_layer_) {
+ CGRect layer_bounds = CGRectMake(
+ 0,
+ 0,
+ compositing_iosurface_->dip_io_surface_size().width(),
+ compositing_iosurface_->dip_io_surface_size().height());
+ CGPoint layer_position = CGPointMake(
+ 0,
+ CGRectGetHeight(new_background_frame) - CGRectGetHeight(layer_bounds));
+ bool bounds_changed = !CGRectEqualToRect(
+ layer_bounds, [compositing_iosurface_layer_ bounds]);
+ [compositing_iosurface_layer_ setPosition:layer_position];
+ [compositing_iosurface_layer_ setBounds:layer_bounds];
+
+ // If the bounds changed, then draw the frame immediately, to ensure that
+ // content displayed is in sync with the window size.
+ if (bounds_changed) {
+ // Also, sometimes, especially when infobars are being removed, the
+ // setNeedsDisplay calls are dropped on the floor, and stale content is
+ // displayed. Calling displayIfNeeded will ensure that the right size
+ // frame is drawn to the screen.
+ // http://crbug.com/350817
+ // Workaround for crbug.com/395827
+ if ([compositing_iosurface_layer_ isAsynchronous])
+ [compositing_iosurface_layer_ setAsynchronous:NO];
+ [compositing_iosurface_layer_ setNeedsDisplayAndDisplayAndAck];
+ }
+ }
+
+ // Changing the software layer's bounds and position doesn't always result
+ // in the layer being anchored to the top-left. Set the layer's frame
+ // explicitly, since this is more reliable in practice.
+ if (software_layer_) {
+ bool frame_changed = !CGRectEqualToRect(
+ new_background_frame, [software_layer_ frame]);
+ if (frame_changed) {
+ [software_layer_ setFrame:new_background_frame];
+ }
}
+}
+
+SkBitmap::Config RenderWidgetHostViewMac::PreferredReadbackFormat() {
+ return SkBitmap::kARGB_8888_Config;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CompositingIOSurfaceLayerClient, public:
+
+void RenderWidgetHostViewMac::AcceleratedLayerDidDrawFrame(bool succeeded) {
+ if (!render_widget_host_)
+ return;
- software_latency_info_.AddLatencyNumber(
- ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0);
- render_widget_host_->FrameSwapped(software_latency_info_);
- software_latency_info_.Clear();
+ SendPendingLatencyInfoToHost();
+ SendPendingSwapAck();
+ if (!succeeded)
+ GotAcceleratedCompositingError();
}
} // namespace content
@@ -2097,7 +2215,6 @@ void RenderWidgetHostViewMac::FrameSwapped() {
@synthesize selectedRange = selectedRange_;
@synthesize suppressNextEscapeKeyUp = suppressNextEscapeKeyUp_;
@synthesize markedRange = markedRange_;
-@synthesize delegate = delegate_;
- (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r {
self = [super initWithFrame:NSZeroRect];
@@ -2109,7 +2226,8 @@ void RenderWidgetHostViewMac::FrameSwapped() {
renderWidgetHostView_.reset(r);
canBeKeyView_ = YES;
focusedPluginIdentifier_ = -1;
- deviceScaleFactor_ = ScaleFactor(self);
+ renderWidgetHostView_->backing_store_scale_factor_ =
+ ui::GetScaleFactorForNativeView(self);
// OpenGL support:
if ([self respondsToSelector:
@@ -2119,9 +2237,9 @@ void RenderWidgetHostViewMac::FrameSwapped() {
handlingGlobalFrameDidChange_ = NO;
[[NSNotificationCenter defaultCenter]
addObserver:self
- selector:@selector(globalFrameDidChange:)
- name:NSViewGlobalFrameDidChangeNotification
- object:self];
+ selector:@selector(didChangeScreenParameters:)
+ name:NSApplicationDidChangeScreenParametersNotification
+ object:nil];
}
return self;
}
@@ -2134,39 +2252,36 @@ void RenderWidgetHostViewMac::FrameSwapped() {
if (renderWidgetHostView_)
renderWidgetHostView_->AcceleratedSurfaceRelease();
- if (delegate_ && [delegate_ respondsToSelector:@selector(viewGone:)])
- [delegate_ viewGone:self];
+ if (responderDelegate_ &&
+ [responderDelegate_ respondsToSelector:@selector(viewGone:)])
+ [responderDelegate_ viewGone:self];
+ responderDelegate_.reset();
+
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
-- (void)resetCursorRects {
- if (currentCursor_) {
- [self addCursorRect:[self visibleRect] cursor:currentCursor_];
- [currentCursor_ setOnMouseEntered:YES];
- }
+- (void)didChangeScreenParameters:(NSNotification*)notify {
+ g_screen_info_up_to_date = false;
}
-- (void)gotUnhandledWheelEvent {
- if (delegate_ &&
- [delegate_ respondsToSelector:@selector(gotUnhandledWheelEvent)]) {
- [delegate_ gotUnhandledWheelEvent];
- }
+- (void)setResponderDelegate:
+ (NSObject<RenderWidgetHostViewMacDelegate>*)delegate {
+ DCHECK(!responderDelegate_);
+ responderDelegate_.reset([delegate retain]);
}
-- (void)scrollOffsetPinnedToLeft:(BOOL)left toRight:(BOOL)right {
- if (delegate_ && [delegate_
- respondsToSelector:@selector(scrollOffsetPinnedToLeft:toRight:)]) {
- [delegate_ scrollOffsetPinnedToLeft:left toRight:right];
+- (void)resetCursorRects {
+ if (currentCursor_) {
+ [self addCursorRect:[self visibleRect] cursor:currentCursor_];
+ [currentCursor_ setOnMouseEntered:YES];
}
}
-- (void)setHasHorizontalScrollbar:(BOOL)has_horizontal_scrollbar {
- if (delegate_ &&
- [delegate_ respondsToSelector:@selector(setHasHorizontalScrollbar:)]) {
- [delegate_ setHasHorizontalScrollbar:has_horizontal_scrollbar];
- }
+- (void)processedWheelEvent:(const blink::WebMouseWheelEvent&)event
+ consumed:(BOOL)consumed {
+ [responderDelegate_ rendererHandledWheelEvent:event consumed:consumed];
}
- (BOOL)respondsToSelector:(SEL)selector {
@@ -2176,15 +2291,15 @@ void RenderWidgetHostViewMac::FrameSwapped() {
if ([super respondsToSelector:selector])
return YES;
- if (delegate_)
- return [delegate_ respondsToSelector:selector];
+ if (responderDelegate_)
+ return [responderDelegate_ respondsToSelector:selector];
return NO;
}
- (id)forwardingTargetForSelector:(SEL)selector {
- if ([delegate_ respondsToSelector:selector])
- return delegate_;
+ if ([responderDelegate_ respondsToSelector:selector])
+ return responderDelegate_.get();
return [super forwardingTargetForSelector:selector];
}
@@ -2255,8 +2370,9 @@ void RenderWidgetHostViewMac::FrameSwapped() {
- (void)mouseEvent:(NSEvent*)theEvent {
TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::mouseEvent");
- if (delegate_ && [delegate_ respondsToSelector:@selector(handleEvent:)]) {
- BOOL handled = [delegate_ handleEvent:theEvent];
+ if (responderDelegate_ &&
+ [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
+ BOOL handled = [responderDelegate_ handleEvent:theEvent];
if (handled)
return;
}
@@ -2367,8 +2483,9 @@ void RenderWidgetHostViewMac::FrameSwapped() {
}
- (EventHandled)keyEvent:(NSEvent*)theEvent {
- if (delegate_ && [delegate_ respondsToSelector:@selector(handleEvent:)]) {
- BOOL handled = [delegate_ handleEvent:theEvent];
+ if (responderDelegate_ &&
+ [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
+ BOOL handled = [responderDelegate_ handleEvent:theEvent];
if (handled)
return kEventHandled;
}
@@ -2612,8 +2729,12 @@ void RenderWidgetHostViewMac::FrameSwapped() {
}
if (renderWidgetHostView_->render_widget_host_) {
- const WebMouseWheelEvent& webEvent =
- WebInputEventFactory::mouseWheelEvent(event, self);
+ // History-swiping is not possible if the logic reaches this point.
+ // Allow rubber-banding in both directions.
+ bool canRubberbandLeft = true;
+ bool canRubberbandRight = true;
+ const WebMouseWheelEvent webEvent = WebInputEventFactory::mouseWheelEvent(
+ event, self, canRubberbandLeft, canRubberbandRight);
renderWidgetHostView_->render_widget_host_->ForwardWheelEvent(webEvent);
}
@@ -2624,22 +2745,40 @@ void RenderWidgetHostViewMac::FrameSwapped() {
}
- (void)beginGestureWithEvent:(NSEvent*)event {
- [delegate_ beginGestureWithEvent:event];
+ [responderDelegate_ beginGestureWithEvent:event];
}
- (void)endGestureWithEvent:(NSEvent*)event {
- [delegate_ endGestureWithEvent:event];
+ [responderDelegate_ endGestureWithEvent:event];
}
- (void)touchesMovedWithEvent:(NSEvent*)event {
- [delegate_ touchesMovedWithEvent:event];
+ [responderDelegate_ touchesMovedWithEvent:event];
}
- (void)touchesBeganWithEvent:(NSEvent*)event {
- [delegate_ touchesBeganWithEvent:event];
+ [responderDelegate_ touchesBeganWithEvent:event];
}
- (void)touchesCancelledWithEvent:(NSEvent*)event {
- [delegate_ touchesCancelledWithEvent:event];
+ [responderDelegate_ touchesCancelledWithEvent:event];
}
- (void)touchesEndedWithEvent:(NSEvent*)event {
- [delegate_ touchesEndedWithEvent:event];
+ [responderDelegate_ touchesEndedWithEvent:event];
+}
+
+// This is invoked only on 10.8 or newer when the user taps a word using
+// three fingers.
+- (void)quickLookWithEvent:(NSEvent*)event {
+ NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
+ TextInputClientMac::GetInstance()->GetStringAtPoint(
+ renderWidgetHostView_->render_widget_host_,
+ gfx::Point(point.x, NSHeight([self frame]) - point.y),
+ ^(NSAttributedString* string, NSPoint baselinePoint) {
+ if (string && [string length] > 0) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [self showDefinitionForAttributedString:string
+ atPoint:baselinePoint];
+ });
+ }
+ }
+ );
}
// This method handles 2 different types of hardware events.
@@ -2665,8 +2804,9 @@ void RenderWidgetHostViewMac::FrameSwapped() {
// cancels the gesture, all remaining touches are forwarded to the content
// scroll logic. The user cannot trigger the navigation logic again.
- (void)scrollWheel:(NSEvent*)event {
- if (delegate_ && [delegate_ respondsToSelector:@selector(handleEvent:)]) {
- BOOL handled = [delegate_ handleEvent:event];
+ if (responderDelegate_ &&
+ [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
+ BOOL handled = [responderDelegate_ handleEvent:event];
if (handled)
return;
}
@@ -2687,27 +2827,31 @@ void RenderWidgetHostViewMac::FrameSwapped() {
// This is responsible for content scrolling!
if (renderWidgetHostView_->render_widget_host_) {
- const WebMouseWheelEvent& webEvent =
- WebInputEventFactory::mouseWheelEvent(event, self);
+ BOOL canRubberbandLeft = [responderDelegate_ canRubberbandLeft:self];
+ BOOL canRubberbandRight = [responderDelegate_ canRubberbandRight:self];
+ const WebMouseWheelEvent webEvent = WebInputEventFactory::mouseWheelEvent(
+ event, self, canRubberbandLeft, canRubberbandRight);
renderWidgetHostView_->render_widget_host_->ForwardWheelEvent(webEvent);
}
}
+// Called repeatedly during a pinch gesture, with incremental change values.
+- (void)magnifyWithEvent:(NSEvent*)event {
+ if (renderWidgetHostView_->render_widget_host_) {
+ // Send a GesturePinchUpdate event.
+ // Note that we don't attempt to bracket these by GesturePinchBegin/End (or
+ // GestureSrollBegin/End) as is done for touchscreen. Keeping track of when
+ // a pinch is active would take a little more work here, and we don't need
+ // it for anything yet.
+ const WebGestureEvent& webEvent =
+ WebInputEventFactory::gestureEvent(event, self);
+ renderWidgetHostView_->render_widget_host_->ForwardGestureEvent(webEvent);
+ }
+}
+
- (void)viewWillMoveToWindow:(NSWindow*)newWindow {
NSWindow* oldWindow = [self window];
- // We're messing with the window, so do this to ensure no flashes. This one
- // prevents a flash when the current tab is closed.
- if (!renderWidgetHostView_->use_core_animation_)
- [oldWindow disableScreenUpdatesUntilFlush];
-
- // If the new window for this view is using CoreAnimation then enable
- // CoreAnimation on this view.
- if (GetCoreAnimationStatus() == CORE_ANIMATION_ENABLED_LAZY &&
- [[newWindow contentView] wantsLayer]) {
- renderWidgetHostView_->EnableCoreAnimation();
- }
-
NSNotificationCenter* notificationCenter =
[NSNotificationCenter defaultCenter];
@@ -2753,56 +2897,23 @@ void RenderWidgetHostViewMac::FrameSwapped() {
}
}
-- (void)updateTabBackingStoreScaleFactor {
- if (!renderWidgetHostView_->render_widget_host_)
- return;
-
- float scaleFactor = ScaleFactor(self);
- if (scaleFactor == deviceScaleFactor_)
- return;
- deviceScaleFactor_ = scaleFactor;
-
- BackingStoreMac* backingStore = static_cast<BackingStoreMac*>(
- renderWidgetHostView_->render_widget_host_->GetBackingStore(false));
- if (backingStore) // NULL in hardware path.
- backingStore->ScaleFactorChanged(scaleFactor);
-
- [self updateSoftwareLayerScaleFactor];
- renderWidgetHostView_->render_widget_host_->NotifyScreenInfoChanged();
+- (void)updateScreenProperties{
+ renderWidgetHostView_->UpdateBackingStoreScaleFactor();
+ renderWidgetHostView_->UpdateDisplayLink();
}
// http://developer.apple.com/library/mac/#documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/CapturingScreenContents/CapturingScreenContents.html#//apple_ref/doc/uid/TP40012302-CH10-SW4
- (void)windowDidChangeBackingProperties:(NSNotification*)notification {
- NSWindow* window = (NSWindow*)[notification object];
-
- CGFloat newBackingScaleFactor = [window backingScaleFactor];
- CGFloat oldBackingScaleFactor = [base::mac::ObjCCast<NSNumber>(
- [[notification userInfo] objectForKey:NSBackingPropertyOldScaleFactorKey])
- doubleValue];
- if (newBackingScaleFactor != oldBackingScaleFactor) {
- // Background tabs check if their scale factor changed when they are added
- // to a window.
-
- // Allocating a CGLayerRef with the current scale factor immediately from
- // this handler doesn't work. Schedule the backing store update on the
- // next runloop cycle, then things are read for CGLayerRef allocations to
- // work.
- [self performSelector:@selector(updateTabBackingStoreScaleFactor)
- withObject:nil
- afterDelay:0];
- }
-}
-
-- (void)globalFrameDidChange:(NSNotification*)notification {
- if (handlingGlobalFrameDidChange_)
- return;
+ // Background tabs check if their scale factor or vsync properties changed
+ // when they are added to a window.
- handlingGlobalFrameDidChange_ = YES;
- if (renderWidgetHostView_->compositing_iosurface_context_) {
- [renderWidgetHostView_->compositing_iosurface_context_->nsgl_context()
- update];
- }
- handlingGlobalFrameDidChange_ = NO;
+ // Allocating a CGLayerRef with the current scale factor immediately from
+ // this handler doesn't work. Schedule the backing store update on the
+ // next runloop cycle, then things are read for CGLayerRef allocations to
+ // work.
+ [self performSelector:@selector(updateScreenProperties)
+ withObject:nil
+ afterDelay:0];
}
- (void)windowChangedGlobalFrame:(NSNotification*)notification {
@@ -2816,246 +2927,25 @@ void RenderWidgetHostViewMac::FrameSwapped() {
// NB: -[NSView setFrame:] calls through -setFrameSize:, so overriding
// -setFrame: isn't neccessary.
[super setFrameSize:newSize];
- if (renderWidgetHostView_->render_widget_host_) {
- renderWidgetHostView_->render_widget_host_->SendScreenRects();
- renderWidgetHostView_->render_widget_host_->WasResized();
- }
-
- // This call is necessary to make the window wait for a new frame at the new
- // size to be available before the resize completes. Calling only
- // setLayerContentsRedrawPolicy:NSViewLayerContentsRedrawOnSetNeedsDisplay on
- // this is not sufficient.
- ScopedCAActionDisabler disabler;
- CGRect frame = NSRectToCGRect([renderWidgetHostView_->cocoa_view() bounds]);
- [renderWidgetHostView_->software_layer_ setFrame:frame];
- [renderWidgetHostView_->software_layer_ setNeedsDisplay];
- [renderWidgetHostView_->compositing_iosurface_layer_ setFrame:frame];
- [renderWidgetHostView_->compositing_iosurface_layer_ setNeedsDisplay];
-}
-
-- (void)callSetNeedsDisplayInRect {
- DCHECK([NSThread isMainThread]);
- DCHECK(renderWidgetHostView_->call_set_needs_display_in_rect_pending_);
- [self setNeedsDisplayInRect:renderWidgetHostView_->invalid_rect_];
- renderWidgetHostView_->call_set_needs_display_in_rect_pending_ = false;
- renderWidgetHostView_->invalid_rect_ = NSZeroRect;
-
- if (renderWidgetHostView_->compositing_iosurface_layer_)
- [renderWidgetHostView_->compositing_iosurface_layer_ setNeedsDisplay];
-}
-
-// Fills with white the parts of the area to the right and bottom for |rect|
-// that intersect |damagedRect|.
-- (void)fillBottomRightRemainderOfRect:(gfx::Rect)rect
- dirtyRect:(gfx::Rect)damagedRect
- inContext:(CGContextRef)context {
- if (damagedRect.right() > rect.right()) {
- int x = std::max(rect.right(), damagedRect.x());
- int y = std::min(rect.bottom(), damagedRect.bottom());
- int width = damagedRect.right() - x;
- int height = damagedRect.y() - y;
-
- // Extra fun to get around the fact that gfx::Rects can't have
- // negative sizes.
- if (width < 0) {
- x += width;
- width = -width;
- }
- if (height < 0) {
- y += height;
- height = -height;
- }
- NSRect r = [self flipRectToNSRect:gfx::Rect(x, y, width, height)];
- CGContextSetFillColorWithColor(context,
- CGColorGetConstantColor(kCGColorWhite));
- CGContextFillRect(context, NSRectToCGRect(r));
- }
- if (damagedRect.bottom() > rect.bottom()) {
- int x = damagedRect.x();
- int y = damagedRect.bottom();
- int width = damagedRect.right() - x;
- int height = std::max(rect.bottom(), damagedRect.y()) - y;
-
- // Extra fun to get around the fact that gfx::Rects can't have
- // negative sizes.
- if (width < 0) {
- x += width;
- width = -width;
- }
- if (height < 0) {
- y += height;
- height = -height;
- }
-
- NSRect r = [self flipRectToNSRect:gfx::Rect(x, y, width, height)];
- CGContextSetFillColorWithColor(context,
- CGColorGetConstantColor(kCGColorWhite));
- CGContextFillRect(context, NSRectToCGRect(r));
- }
-}
-
-- (void)drawRect:(NSRect)dirtyRect {
- TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::drawRect");
- CHECK(!renderWidgetHostView_->use_core_animation_);
-
- if (!renderWidgetHostView_->render_widget_host_) {
- // TODO(shess): Consider using something more noticable?
- [[NSColor whiteColor] set];
- NSRectFill(dirtyRect);
+ if (!renderWidgetHostView_->render_widget_host_)
return;
- }
-
- DCHECK(!renderWidgetHostView_->about_to_validate_and_paint_);
-
- // GetBackingStore works for both software and accelerated frames. If a
- // SwapBuffers occurs while GetBackingStore is blocking, we will continue to
- // blit the IOSurface below.
- renderWidgetHostView_->about_to_validate_and_paint_ = true;
- BackingStoreMac* backingStore = static_cast<BackingStoreMac*>(
- renderWidgetHostView_->render_widget_host_->GetBackingStore(true));
- renderWidgetHostView_->about_to_validate_and_paint_ = false;
-
- const gfx::Rect damagedRect([self flipNSRectToRect:dirtyRect]);
-
- if (renderWidgetHostView_->last_frame_was_accelerated_ &&
- renderWidgetHostView_->compositing_iosurface_) {
- if (renderWidgetHostView_->allow_overlapping_views_) {
- CHECK_EQ(CORE_ANIMATION_DISABLED, GetCoreAnimationStatus());
-
- // If overlapping views need to be allowed, punch a hole in the window
- // to expose the GL underlay.
- TRACE_EVENT2("gpu", "NSRectFill clear", "w", damagedRect.width(),
- "h", damagedRect.height());
- // NSRectFill is extremely slow (15ms for a window on a fast MacPro), so
- // this is only done when it's a real invalidation from window damage (not
- // when a BuffersSwapped was received). Note that even a 1x1 NSRectFill
- // can take many milliseconds sometimes (!) so this is skipped completely
- // for drawRects that are triggered by BuffersSwapped messages.
- [[NSColor clearColor] set];
- NSRectFill(dirtyRect);
- }
-
- CGLSetCurrentContext(
- renderWidgetHostView_->compositing_iosurface_context_->cgl_context());
- if (renderWidgetHostView_->DrawIOSurfaceWithoutCoreAnimation())
- return;
-
- // On error, fall back to software and fall through to the non-accelerated
- // drawing path.
- renderWidgetHostView_->GotAcceleratedCompositingError();
- }
-
- CGContextRef context = static_cast<CGContextRef>(
- [[NSGraphicsContext currentContext] graphicsPort]);
- [self drawBackingStore:backingStore
- dirtyRect:NSRectToCGRect(dirtyRect)
- inContext:context];
-}
-
-- (void)drawBackingStore:(BackingStoreMac*)backingStore
- dirtyRect:(CGRect)dirtyRect
- inContext:(CGContextRef)context {
- content::SoftwareFrameManager* software_frame_manager =
- renderWidgetHostView_->software_frame_manager_.get();
- // There should never be both a legacy software and software composited
- // frame.
- DCHECK(!backingStore || !software_frame_manager->HasCurrentFrame());
-
- if (backingStore || software_frame_manager->HasCurrentFrame()) {
- // Note: All coordinates are in view units, not pixels.
- gfx::Rect bitmapRect(
- software_frame_manager->HasCurrentFrame() ?
- software_frame_manager->GetCurrentFrameSizeInDIP() :
- backingStore->size());
-
- // Specify the proper y offset to ensure that the view is rooted to the
- // upper left corner. This can be negative, if the window was resized
- // smaller and the renderer hasn't yet repainted.
- int yOffset = NSHeight([self bounds]) - bitmapRect.height();
-
- NSRect nsDirtyRect = NSRectFromCGRect(dirtyRect);
- const gfx::Rect damagedRect([self flipNSRectToRect:nsDirtyRect]);
-
- gfx::Rect paintRect = gfx::IntersectRects(bitmapRect, damagedRect);
- if (!paintRect.IsEmpty()) {
- if (software_frame_manager->HasCurrentFrame()) {
- // If a software compositor framebuffer is present, draw using that.
- gfx::Size sizeInPixels =
- software_frame_manager->GetCurrentFrameSizeInPixels();
- base::ScopedCFTypeRef<CGDataProviderRef> dataProvider(
- CGDataProviderCreateWithData(
- NULL,
- software_frame_manager->GetCurrentFramePixels(),
- 4 * sizeInPixels.width() * sizeInPixels.height(),
- NULL));
- base::ScopedCFTypeRef<CGImageRef> image(
- CGImageCreate(
- sizeInPixels.width(),
- sizeInPixels.height(),
- 8,
- 32,
- 4 * sizeInPixels.width(),
- base::mac::GetSystemColorSpace(),
- kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host,
- dataProvider,
- NULL,
- false,
- kCGRenderingIntentDefault));
- CGRect imageRect = bitmapRect.ToCGRect();
- imageRect.origin.y = yOffset;
- CGContextDrawImage(context, imageRect, image);
- } else if (backingStore->cg_layer()) {
- // If we have a CGLayer, draw that into the window
- // TODO: add clipping to dirtyRect if it improves drawing performance.
- CGContextDrawLayerAtPoint(context, CGPointMake(0.0, yOffset),
- backingStore->cg_layer());
- } else {
- // If we haven't created a layer yet, draw the cached bitmap into
- // the window. The CGLayer will be created the next time the renderer
- // paints.
- base::ScopedCFTypeRef<CGImageRef> image(
- CGBitmapContextCreateImage(backingStore->cg_bitmap()));
- CGRect imageRect = bitmapRect.ToCGRect();
- imageRect.origin.y = yOffset;
- CGContextDrawImage(context, imageRect, image);
- }
- }
- renderWidgetHostView_->FrameSwapped();
+ // Move the CALayers to their positions in the new view size. Note that
+ // this will not draw anything because the non-background layers' sizes
+ // didn't actually change.
+ renderWidgetHostView_->LayoutLayers();
- // Fill the remaining portion of the damagedRect with white
- [self fillBottomRightRemainderOfRect:bitmapRect
- dirtyRect:damagedRect
- inContext:context];
+ renderWidgetHostView_->render_widget_host_->SendScreenRects();
+ renderWidgetHostView_->render_widget_host_->WasResized();
+ if (renderWidgetHostView_->delegated_frame_host_)
+ renderWidgetHostView_->delegated_frame_host_->WasResized();
- if (!renderWidgetHostView_->whiteout_start_time_.is_null()) {
- base::TimeDelta whiteout_duration = base::TimeTicks::Now() -
- renderWidgetHostView_->whiteout_start_time_;
- UMA_HISTOGRAM_TIMES("MPArch.RWHH_WhiteoutDuration", whiteout_duration);
-
- // Reset the start time to 0 so that we start recording again the next
- // time the backing store is NULL...
- renderWidgetHostView_->whiteout_start_time_ = base::TimeTicks();
- }
- if (!renderWidgetHostView_->web_contents_switch_paint_time_.is_null()) {
- base::TimeDelta web_contents_switch_paint_duration =
- base::TimeTicks::Now() -
- renderWidgetHostView_->web_contents_switch_paint_time_;
- UMA_HISTOGRAM_TIMES("MPArch.RWH_TabSwitchPaintDuration",
- web_contents_switch_paint_duration);
- // Reset contents_switch_paint_time_ to 0 so future tab selections are
- // recorded.
- renderWidgetHostView_->web_contents_switch_paint_time_ =
- base::TimeTicks();
- }
- } else {
- CGContextSetFillColorWithColor(context,
- CGColorGetConstantColor(kCGColorWhite));
- CGContextFillRect(context, dirtyRect);
- if (renderWidgetHostView_->whiteout_start_time_.is_null())
- renderWidgetHostView_->whiteout_start_time_ = base::TimeTicks::Now();
- }
+ // Wait for the frame that WasResize might have requested. If the view is
+ // being made visible at a new size, then this call will have no effect
+ // because the view widget is still hidden, and the pause call in WasShown
+ // will have this effect for us.
+ renderWidgetHostView_->PauseForPendingResizeOrRepaintsAndDraw();
}
- (BOOL)canBecomeKeyView {
@@ -3119,12 +3009,13 @@ void RenderWidgetHostViewMac::FrameSwapped() {
}
- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
- if (delegate_ &&
- [delegate_ respondsToSelector:@selector(validateUserInterfaceItem:
- isValidItem:)]) {
+ if (responderDelegate_ &&
+ [responderDelegate_
+ respondsToSelector:@selector(validateUserInterfaceItem:
+ isValidItem:)]) {
BOOL valid;
- BOOL known = [delegate_ validateUserInterfaceItem:item
- isValidItem:&valid];
+ BOOL known =
+ [responderDelegate_ validateUserInterfaceItem:item isValidItem:&valid];
if (known)
return valid;
}
@@ -3256,72 +3147,6 @@ void RenderWidgetHostViewMac::FrameSwapped() {
return [super accessibilityFocusedUIElement];
}
-- (void)doDefaultAction:(int32)accessibilityObjectId {
- RenderWidgetHostImpl* rwh = renderWidgetHostView_->render_widget_host_;
- rwh->Send(new AccessibilityMsg_DoDefaultAction(
- rwh->GetRoutingID(), accessibilityObjectId));
-}
-
-// VoiceOver uses this method to move the caret to the beginning of the next
-// word in a text field.
-- (void)accessibilitySetTextSelection:(int32)accId
- startOffset:(int32)startOffset
- endOffset:(int32)endOffset {
- RenderWidgetHostImpl* rwh = renderWidgetHostView_->render_widget_host_;
- rwh->AccessibilitySetTextSelection(accId, startOffset, endOffset);
-}
-
-// Convert a web accessibility's location in web coordinates into a cocoa
-// screen coordinate.
-- (NSPoint)accessibilityPointInScreen:(NSPoint)origin
- size:(NSSize)size {
- origin.y = NSHeight([self bounds]) - origin.y;
- NSPoint originInWindow = [self convertPoint:origin toView:nil];
- NSPoint originInScreen = [[self window] convertBaseToScreen:originInWindow];
- originInScreen.y = originInScreen.y - size.height;
- return originInScreen;
-}
-
-- (void)setAccessibilityFocus:(BOOL)focus
- accessibilityId:(int32)accessibilityObjectId {
- if (focus) {
- RenderWidgetHostImpl* rwh = renderWidgetHostView_->render_widget_host_;
- rwh->Send(new AccessibilityMsg_SetFocus(
- rwh->GetRoutingID(), accessibilityObjectId));
-
- // Immediately set the focused item even though we have not officially set
- // focus on it as VoiceOver expects to get the focused item after this
- // method returns.
- BrowserAccessibilityManager* manager =
- renderWidgetHostView_->GetBrowserAccessibilityManager();
- manager->SetFocus(manager->GetFromRendererID(accessibilityObjectId), false);
- }
-}
-
-- (void)performShowMenuAction:(BrowserAccessibilityCocoa*)accessibility {
- // Performs a right click copying WebKit's
- // accessibilityPerformShowMenuAction.
- NSPoint origin = [accessibility origin];
- NSSize size = [[accessibility size] sizeValue];
- NSPoint location = [self accessibilityPointInScreen:origin size:size];
- location = [[self window] convertScreenToBase:location];
- location.x += size.width/2;
- location.y += size.height/2;
-
- NSEvent* fakeRightClick = [NSEvent
- mouseEventWithType:NSRightMouseDown
- location:location
- modifierFlags:0
- timestamp:0
- windowNumber:[[self window] windowNumber]
- context:[NSGraphicsContext currentContext]
- eventNumber:0
- clickCount:1
- pressure:0];
-
- [self mouseEvent:fakeRightClick];
-}
-
// Below is the nasty tooltip stuff -- copied from WebKit's WebHTMLView.mm
// with minor modifications for code style and commenting.
//
@@ -3722,8 +3547,8 @@ extern NSString *NSTextInputReplacementRangeAttributeName;
ExtractUnderlines(string, &underlines_);
} else {
// Use a thin black underline by default.
- underlines_.push_back(
- blink::WebCompositionUnderline(0, length, SK_ColorBLACK, false));
+ underlines_.push_back(blink::WebCompositionUnderline(
+ 0, length, SK_ColorBLACK, false, SK_ColorTRANSPARENT));
}
// If we are handling a key down event, then SetComposition() will be
@@ -3803,7 +3628,7 @@ extern NSString *NSTextInputReplacementRangeAttributeName;
- (void)viewDidMoveToWindow {
if ([self window])
- [self updateTabBackingStoreScaleFactor];
+ [self updateScreenProperties];
if (canBeKeyView_) {
NSWindow* newWindow = [self window];
@@ -3831,62 +3656,48 @@ extern NSString *NSTextInputReplacementRangeAttributeName;
hasOpenMouseDown_ = NO;
}
-
- // Resize the view's layers to match the new window size.
- ScopedCAActionDisabler disabler;
- [renderWidgetHostView_->software_layer_
- setFrame:NSRectToCGRect([self bounds])];
- [renderWidgetHostView_->compositing_iosurface_layer_
- setFrame:NSRectToCGRect([self bounds])];
}
- (void)undo:(id)sender {
- if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
- static_cast<RenderViewHostImpl*>(
- renderWidgetHostView_->render_widget_host_)->Undo();
- }
+ WebContents* web_contents = renderWidgetHostView_->GetWebContents();
+ if (web_contents)
+ web_contents->Undo();
}
- (void)redo:(id)sender {
- if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
- static_cast<RenderViewHostImpl*>(
- renderWidgetHostView_->render_widget_host_)->Redo();
- }
+ WebContents* web_contents = renderWidgetHostView_->GetWebContents();
+ if (web_contents)
+ web_contents->Redo();
}
- (void)cut:(id)sender {
- if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
- static_cast<RenderViewHostImpl*>(
- renderWidgetHostView_->render_widget_host_)->Cut();
- }
+ WebContents* web_contents = renderWidgetHostView_->GetWebContents();
+ if (web_contents)
+ web_contents->Cut();
}
- (void)copy:(id)sender {
- if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
- static_cast<RenderViewHostImpl*>(
- renderWidgetHostView_->render_widget_host_)->Copy();
- }
+ WebContents* web_contents = renderWidgetHostView_->GetWebContents();
+ if (web_contents)
+ web_contents->Copy();
}
- (void)copyToFindPboard:(id)sender {
- if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
- static_cast<RenderViewHostImpl*>(
- renderWidgetHostView_->render_widget_host_)->CopyToFindPboard();
- }
+ WebContents* web_contents = renderWidgetHostView_->GetWebContents();
+ if (web_contents)
+ web_contents->CopyToFindPboard();
}
- (void)paste:(id)sender {
- if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
- static_cast<RenderViewHostImpl*>(
- renderWidgetHostView_->render_widget_host_)->Paste();
- }
+ WebContents* web_contents = renderWidgetHostView_->GetWebContents();
+ if (web_contents)
+ web_contents->Paste();
}
- (void)pasteAndMatchStyle:(id)sender {
- if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
- static_cast<RenderViewHostImpl*>(
- renderWidgetHostView_->render_widget_host_)->PasteAndMatchStyle();
- }
+ WebContents* web_contents = renderWidgetHostView_->GetWebContents();
+ if (web_contents)
+ web_contents->PasteAndMatchStyle();
}
- (void)selectAll:(id)sender {
@@ -3897,10 +3708,9 @@ extern NSString *NSTextInputReplacementRangeAttributeName;
// menu handler, neither is true.
// Explicitly call SelectAll() here to make sure the renderer returns
// selection results.
- if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
- static_cast<RenderViewHostImpl*>(
- renderWidgetHostView_->render_widget_host_)->SelectAll();
- }
+ WebContents* web_contents = renderWidgetHostView_->GetWebContents();
+ if (web_contents)
+ web_contents->SelectAll();
}
- (void)startSpeaking:(id)sender {
@@ -4036,54 +3846,6 @@ extern NSString *NSTextInputReplacementRangeAttributeName;
renderWidgetHostView_->KillSelf();
}
-- (void)updateSoftwareLayerScaleFactor {
- if (![renderWidgetHostView_->software_layer_
- respondsToSelector:@selector(setContentsScale:)])
- return;
-
- ScopedCAActionDisabler disabler;
- [renderWidgetHostView_->software_layer_ setContentsScale:deviceScaleFactor_];
-}
-
-// Delegate methods for the software CALayer
-- (void)drawLayer:(CALayer*)layer
- inContext:(CGContextRef)context {
- TRACE_EVENT0("browser", "CompositingIOSurfaceLayer::drawLayer");
-
- DCHECK(renderWidgetHostView_->use_core_animation_);
- DCHECK([layer isEqual:renderWidgetHostView_->software_layer_]);
-
- CGRect clipRect = CGContextGetClipBoundingBox(context);
-
- if (!renderWidgetHostView_->render_widget_host_ ||
- renderWidgetHostView_->render_widget_host_->is_hidden()) {
- CGContextSetFillColorWithColor(context,
- CGColorGetConstantColor(kCGColorWhite));
- CGContextFillRect(context, clipRect);
- return;
- }
-
- renderWidgetHostView_->about_to_validate_and_paint_ = true;
- BackingStoreMac* backingStore = static_cast<BackingStoreMac*>(
- renderWidgetHostView_->render_widget_host_->GetBackingStore(true));
- renderWidgetHostView_->about_to_validate_and_paint_ = false;
-
- [self drawBackingStore:backingStore
- dirtyRect:clipRect
- inContext:context];
-}
-
-- (void)setNeedsDisplay:(BOOL)flag {
- [renderWidgetHostView_->software_layer_ setNeedsDisplay];
- [super setNeedsDisplay:flag];
-}
-
-- (void)setNeedsDisplayInRect:(NSRect)rect {
- [renderWidgetHostView_->software_layer_
- setNeedsDisplayInRect:NSRectToCGRect(rect)];
- [super setNeedsDisplayInRect:rect];
-}
-
@end
//
@@ -4115,9 +3877,15 @@ extern NSString *NSTextInputReplacementRangeAttributeName;
}
- (BOOL)isOpaque {
- if (renderWidgetHostView_->use_core_animation_)
- return YES;
- return [super isOpaque];
+ return YES;
+}
+
+// "-webkit-app-region: drag | no-drag" is implemented on Mac by excluding
+// regions that are not draggable. (See ControlRegionView in
+// native_app_window_cocoa.mm). This requires the render host view to be
+// draggable by default.
+- (BOOL)mouseDownCanMoveWindow {
+ return YES;
}
@end
diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_mac_dictionary_helper.h b/chromium/content/browser/renderer_host/render_widget_host_view_mac_dictionary_helper.h
index 79ccc4d2b5e..a14d312c776 100644
--- a/chromium/content/browser/renderer_host/render_widget_host_view_mac_dictionary_helper.h
+++ b/chromium/content/browser/renderer_host/render_widget_host_view_mac_dictionary_helper.h
@@ -12,7 +12,6 @@ namespace content {
class RenderWidgetHostView;
class RenderWidgetHostViewMac;
-class RenderWidgetHostViewPort;
// A helper class to bring up definition of word for a RWHV.
//
@@ -21,8 +20,7 @@ class RenderWidgetHostViewPort;
// system settings.
class RenderWidgetHostViewMacDictionaryHelper {
public:
- explicit RenderWidgetHostViewMacDictionaryHelper(
- RenderWidgetHostViewPort* view);
+ explicit RenderWidgetHostViewMacDictionaryHelper(RenderWidgetHostView* view);
// Overrides the view to use to bring up dictionary panel.
// This |target_view| can be different from |view_|, |view_| is used to get
diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_mac_dictionary_helper.mm b/chromium/content/browser/renderer_host/render_widget_host_view_mac_dictionary_helper.mm
index 8402a43267c..bcae7ec4e4e 100644
--- a/chromium/content/browser/renderer_host/render_widget_host_view_mac_dictionary_helper.mm
+++ b/chromium/content/browser/renderer_host/render_widget_host_view_mac_dictionary_helper.mm
@@ -9,7 +9,7 @@
namespace content {
RenderWidgetHostViewMacDictionaryHelper::
- RenderWidgetHostViewMacDictionaryHelper(RenderWidgetHostViewPort* view)
+ RenderWidgetHostViewMacDictionaryHelper(RenderWidgetHostView* view)
: view_(static_cast<RenderWidgetHostViewMac*>(view)),
target_view_(static_cast<RenderWidgetHostViewMac*>(view)) {
}
diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper_unittest.mm b/chromium/content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper_unittest.mm
index ba92843ebf7..94f499e7c32 100644
--- a/chromium/content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper_unittest.mm
+++ b/chromium/content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper_unittest.mm
@@ -6,6 +6,7 @@
#import <Cocoa/Cocoa.h>
+#include "base/mac/scoped_nsautorelease_pool.h"
#include "base/message_loop/message_loop.h"
#include "content/browser/renderer_host/render_widget_host_delegate.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
@@ -15,6 +16,7 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
+#include "ui/base/layout.h"
using content::RenderWidgetHostViewMac;
@@ -98,27 +100,34 @@ class RenderWidgetHostViewMacEditCommandHelperTest : public PlatformTest {
// Tests that editing commands make it through the pipeline all the way to
// RenderWidgetHost.
-// Disabled, http://crbug.com/93286.
TEST_F(RenderWidgetHostViewMacEditCommandHelperTest,
- DISABLED_TestEditingCommandDelivery) {
- RenderWidgetHostViewMacEditCommandHelper helper;
- NSArray* edit_command_strings = helper.GetEditSelectorNames();
-
- // Set up a mock render widget and set expectations.
- base::MessageLoopForUI message_loop;
- TestBrowserContext browser_context;
- MockRenderProcessHost mock_process(&browser_context);
+ TestEditingCommandDelivery) {
MockRenderWidgetHostDelegate delegate;
- RenderWidgetHostEditCommandCounter render_widget(&delegate, &mock_process, 0);
+ TestBrowserContext browser_context;
+ MockRenderProcessHost process_host(&browser_context);
+
+ // Populates |g_supported_scale_factors|.
+ std::vector<ui::ScaleFactor> supported_factors;
+ supported_factors.push_back(ui::SCALE_FACTOR_100P);
+ ui::test::ScopedSetSupportedScaleFactors scoped_supported(supported_factors);
- // RenderWidgetHostViewMac self destructs (RenderWidgetHostViewMacCocoa
- // takes ownership) so no need to delete it ourselves.
- RenderWidgetHostViewMac* rwhvm = static_cast<RenderWidgetHostViewMac*>(
- RenderWidgetHostView::CreateViewForWidget(&render_widget));
+ RenderWidgetHostEditCommandCounter* render_widget =
+ new RenderWidgetHostEditCommandCounter(
+ &delegate, &process_host, MSG_ROUTING_NONE);
+ base::mac::ScopedNSAutoreleasePool pool;
+
+ // Owned by its |cocoa_view()|, i.e. |rwhv_cocoa|.
+ RenderWidgetHostViewMac* rwhv_mac = new RenderWidgetHostViewMac(
+ render_widget);
+ base::scoped_nsobject<RenderWidgetHostViewCocoa> rwhv_cocoa(
+ [rwhv_mac->cocoa_view() retain]);
+
+ RenderWidgetHostViewMacEditCommandHelper helper;
+ NSArray* edit_command_strings = helper.GetEditSelectorNames();
RenderWidgetHostViewMacOwner* rwhwvm_owner =
[[[RenderWidgetHostViewMacOwner alloc]
- initWithRenderWidgetHostViewMac:rwhvm] autorelease];
+ initWithRenderWidgetHostViewMac:rwhv_mac] autorelease];
helper.AddEditingSelectorsToClass([rwhwvm_owner class]);
@@ -128,7 +137,15 @@ TEST_F(RenderWidgetHostViewMacEditCommandHelperTest,
}
size_t num_edit_commands = [edit_command_strings count];
- EXPECT_EQ(render_widget.edit_command_message_count_, num_edit_commands);
+ EXPECT_EQ(render_widget->edit_command_message_count_, num_edit_commands);
+ rwhv_cocoa.reset();
+ pool.Recycle();
+
+ {
+ // The |render_widget|'s process needs to be deleted within |message_loop|.
+ base::MessageLoop message_loop;
+ delete render_widget;
+ }
}
// Test RenderWidgetHostViewMacEditCommandHelper::AddEditingSelectorsToClass
diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm b/chromium/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
index 25154962167..da766eac4ad 100644
--- a/chromium/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
+++ b/chromium/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
@@ -6,6 +6,7 @@
#include "base/mac/mac_util.h"
#include "base/mac/scoped_nsautorelease_pool.h"
+#include "base/mac/sdk_forward_declarations.h"
#include "base/strings/utf_string_conversions.h"
#include "content/browser/browser_thread_impl.h"
#include "content/browser/renderer_host/render_widget_host_delegate.h"
@@ -20,27 +21,8 @@
#include "content/test/test_render_view_host.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/base/test/cocoa_test_event_utils.h"
-#import "ui/base/test/ui_cocoa_test_helper.h"
-
-// Declare things that are part of the 10.7 SDK.
-#if !defined(MAC_OS_X_VERSION_10_7) || \
- MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
-enum {
- NSEventPhaseNone = 0, // event not associated with a phase.
- NSEventPhaseBegan = 0x1 << 0,
- NSEventPhaseStationary = 0x1 << 1,
- NSEventPhaseChanged = 0x1 << 2,
- NSEventPhaseEnded = 0x1 << 3,
- NSEventPhaseCancelled = 0x1 << 4,
-};
-typedef NSUInteger NSEventPhase;
-
-@interface NSEvent (LionAPI)
-- (NSEventPhase)phase;
-@end
-
-#endif // 10.7
+#include "ui/events/test/cocoa_test_event_utils.h"
+#import "ui/gfx/test/ui_cocoa_test_helper.h"
// Helper class with methods used to mock -[NSEvent phase], used by
// |MockScrollWheelEventWithPhase()|.
@@ -73,22 +55,29 @@ typedef NSUInteger NSEventPhase;
@property(nonatomic) BOOL unhandledWheelEventReceived;
-- (void)gotUnhandledWheelEvent;
@end
@implementation MockRenderWidgetHostViewMacDelegate
@synthesize unhandledWheelEventReceived = unhandledWheelEventReceived_;
-- (void)gotUnhandledWheelEvent {
- unhandledWheelEventReceived_ = true;
+- (void)rendererHandledWheelEvent:(const blink::WebMouseWheelEvent&)event
+ consumed:(BOOL)consumed {
+ if (!consumed)
+ unhandledWheelEventReceived_ = true;
+}
+- (void)touchesBeganWithEvent:(NSEvent*)event {}
+- (void)touchesMovedWithEvent:(NSEvent*)event {}
+- (void)touchesCancelledWithEvent:(NSEvent*)event {}
+- (void)touchesEndedWithEvent:(NSEvent*)event {}
+- (void)beginGestureWithEvent:(NSEvent*)event {}
+- (void)endGestureWithEvent:(NSEvent*)event {}
+- (BOOL)canRubberbandLeft:(NSView*)view {
+ return true;
+}
+- (BOOL)canRubberbandRight:(NSView*)view {
+ return true;
}
-- (void)touchesBeganWithEvent:(NSEvent*)event{}
-- (void)touchesMovedWithEvent:(NSEvent*)event{}
-- (void)touchesCancelledWithEvent:(NSEvent*)event{}
-- (void)touchesEndedWithEvent:(NSEvent*)event{}
-- (void)beginGestureWithEvent:(NSEvent*)event{}
-- (void)endGestureWithEvent:(NSEvent*)event{}
@end
@@ -183,8 +172,7 @@ class RenderWidgetHostViewMacTest : public RenderViewHostImplTestHarness {
old_rwhv_ = rvh()->GetView();
// Owned by its |cocoa_view()|, i.e. |rwhv_cocoa_|.
- rwhv_mac_ = static_cast<RenderWidgetHostViewMac*>(
- RenderWidgetHostView::CreateViewForWidget(rvh()));
+ rwhv_mac_ = new RenderWidgetHostViewMac(rvh());
rwhv_cocoa_.reset([rwhv_mac_->cocoa_view() retain]);
}
virtual void TearDown() {
@@ -195,7 +183,7 @@ class RenderWidgetHostViewMacTest : public RenderViewHostImplTestHarness {
pool_.Recycle();
// See comment in SetUp().
- test_rvh()->SetView(old_rwhv_);
+ test_rvh()->SetView(static_cast<RenderWidgetHostViewBase*>(old_rwhv_));
RenderViewHostImplTestHarness::TearDown();
}
@@ -271,8 +259,7 @@ TEST_F(RenderWidgetHostViewMacTest, FullscreenCloseOnEscape) {
// Owned by its |cocoa_view()|.
RenderWidgetHostImpl* rwh = new RenderWidgetHostImpl(
&delegate, process_host, MSG_ROUTING_NONE, false);
- RenderWidgetHostViewMac* view = static_cast<RenderWidgetHostViewMac*>(
- RenderWidgetHostView::CreateViewForWidget(rwh));
+ RenderWidgetHostViewMac* view = new RenderWidgetHostViewMac(rwh);
view->InitAsFullscreen(rwhv_mac_);
@@ -305,8 +292,7 @@ TEST_F(RenderWidgetHostViewMacTest, AcceleratorDestroy) {
// Owned by its |cocoa_view()|.
RenderWidgetHostImpl* rwh = new RenderWidgetHostImpl(
&delegate, process_host, MSG_ROUTING_NONE, false);
- RenderWidgetHostViewMac* view = static_cast<RenderWidgetHostViewMac*>(
- RenderWidgetHostView::CreateViewForWidget(rwh));
+ RenderWidgetHostViewMac* view = new RenderWidgetHostViewMac(rwh);
view->InitAsFullscreen(rwhv_mac_);
@@ -324,7 +310,7 @@ TEST_F(RenderWidgetHostViewMacTest, AcceleratorDestroy) {
}
TEST_F(RenderWidgetHostViewMacTest, GetFirstRectForCharacterRangeCaretCase) {
- const base::string16 kDummyString = UTF8ToUTF16("hogehoge");
+ const base::string16 kDummyString = base::UTF8ToUTF16("hogehoge");
const size_t kDummyOffset = 0;
gfx::Rect caret_rect(10, 11, 0, 10);
@@ -660,8 +646,7 @@ TEST_F(RenderWidgetHostViewMacTest, BlurAndFocusOnSetActive) {
// Owned by its |cocoa_view()|.
MockRenderWidgetHostImpl* rwh = new MockRenderWidgetHostImpl(
&delegate, process_host, MSG_ROUTING_NONE);
- RenderWidgetHostViewMac* view = static_cast<RenderWidgetHostViewMac*>(
- RenderWidgetHostView::CreateViewForWidget(rwh));
+ RenderWidgetHostViewMac* view = new RenderWidgetHostViewMac(rwh);
base::scoped_nsobject<CocoaTestHelperWindow> window(
[[CocoaTestHelperWindow alloc] init]);
@@ -707,8 +692,7 @@ TEST_F(RenderWidgetHostViewMacTest, ScrollWheelEndEventDelivery) {
MockRenderWidgetHostDelegate delegate;
MockRenderWidgetHostImpl* host = new MockRenderWidgetHostImpl(
&delegate, process_host, MSG_ROUTING_NONE);
- RenderWidgetHostViewMac* view = static_cast<RenderWidgetHostViewMac*>(
- RenderWidgetHostView::CreateViewForWidget(host));
+ RenderWidgetHostViewMac* view = new RenderWidgetHostViewMac(host);
// Send an initial wheel event with NSEventPhaseBegan to the view.
NSEvent* event1 = MockScrollWheelEventWithPhase(@selector(phaseBegan), 0);
@@ -716,9 +700,11 @@ TEST_F(RenderWidgetHostViewMacTest, ScrollWheelEndEventDelivery) {
ASSERT_EQ(1U, process_host->sink().message_count());
// Send an ACK for the first wheel event, so that the queue will be flushed.
- scoped_ptr<IPC::Message> response(new InputHostMsg_HandleInputEvent_ACK(
- 0, blink::WebInputEvent::MouseWheel, INPUT_EVENT_ACK_STATE_CONSUMED,
- ui::LatencyInfo()));
+ InputHostMsg_HandleInputEvent_ACK_Params ack;
+ ack.type = blink::WebInputEvent::MouseWheel;
+ ack.state = INPUT_EVENT_ACK_STATE_CONSUMED;
+ scoped_ptr<IPC::Message> response(
+ new InputHostMsg_HandleInputEvent_ACK(0, ack));
host->OnMessageReceived(*response);
// Post the NSEventPhaseEnded wheel event to NSApp and check whether the
@@ -746,8 +732,7 @@ TEST_F(RenderWidgetHostViewMacTest, IgnoreEmptyUnhandledWheelEvent) {
MockRenderWidgetHostDelegate delegate;
MockRenderWidgetHostImpl* host = new MockRenderWidgetHostImpl(
&delegate, process_host, MSG_ROUTING_NONE);
- RenderWidgetHostViewMac* view = static_cast<RenderWidgetHostViewMac*>(
- RenderWidgetHostView::CreateViewForWidget(host));
+ RenderWidgetHostViewMac* view = new RenderWidgetHostViewMac(host);
// Add a delegate to the view.
base::scoped_nsobject<MockRenderWidgetHostViewMacDelegate> view_delegate(
@@ -761,9 +746,11 @@ TEST_F(RenderWidgetHostViewMacTest, IgnoreEmptyUnhandledWheelEvent) {
process_host->sink().ClearMessages();
// Indicate that the wheel event was unhandled.
- scoped_ptr<IPC::Message> response1(new InputHostMsg_HandleInputEvent_ACK(0,
- blink::WebInputEvent::MouseWheel, INPUT_EVENT_ACK_STATE_NOT_CONSUMED,
- ui::LatencyInfo()));
+ InputHostMsg_HandleInputEvent_ACK_Params unhandled_ack;
+ unhandled_ack.type = blink::WebInputEvent::MouseWheel;
+ unhandled_ack.state = INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
+ scoped_ptr<IPC::Message> response1(
+ new InputHostMsg_HandleInputEvent_ACK(0, unhandled_ack));
host->OnMessageReceived(*response1);
// Check that the view delegate got an unhandled wheel event.
@@ -776,9 +763,8 @@ TEST_F(RenderWidgetHostViewMacTest, IgnoreEmptyUnhandledWheelEvent) {
ASSERT_EQ(1U, process_host->sink().message_count());
// Indicate that the wheel event was also unhandled.
- scoped_ptr<IPC::Message> response2(new InputHostMsg_HandleInputEvent_ACK(0,
- blink::WebInputEvent::MouseWheel, INPUT_EVENT_ACK_STATE_NOT_CONSUMED,
- ui::LatencyInfo()));
+ scoped_ptr<IPC::Message> response2(
+ new InputHostMsg_HandleInputEvent_ACK(0, unhandled_ack));
host->OnMessageReceived(*response2);
// Check that the view delegate ignored the empty unhandled wheel event.
diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_win.cc b/chromium/content/browser/renderer_host/render_widget_host_view_win.cc
deleted file mode 100644
index 8238a82b4ca..00000000000
--- a/chromium/content/browser/renderer_host/render_widget_host_view_win.cc
+++ /dev/null
@@ -1,3225 +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/renderer_host/render_widget_host_view_win.h"
-
-#include <InputScope.h>
-#include <wtsapi32.h>
-#pragma comment(lib, "wtsapi32.lib")
-
-#include <algorithm>
-#include <map>
-#include <stack>
-
-#include "base/basictypes.h"
-#include "base/bind.h"
-#include "base/callback_helpers.h"
-#include "base/command_line.h"
-#include "base/debug/trace_event.h"
-#include "base/i18n/rtl.h"
-#include "base/metrics/histogram.h"
-#include "base/threading/thread.h"
-#include "base/win/metro.h"
-#include "base/win/scoped_comptr.h"
-#include "base/win/scoped_gdi_object.h"
-#include "base/win/win_util.h"
-#include "base/win/windows_version.h"
-#include "base/win/wrapped_window_proc.h"
-#include "content/browser/accessibility/browser_accessibility_manager_win.h"
-#include "content/browser/accessibility/browser_accessibility_state_impl.h"
-#include "content/browser/accessibility/browser_accessibility_win.h"
-#include "content/browser/gpu/gpu_data_manager_impl.h"
-#include "content/browser/gpu/gpu_process_host.h"
-#include "content/browser/gpu/gpu_process_host_ui_shim.h"
-#include "content/browser/renderer_host/backing_store.h"
-#include "content/browser/renderer_host/backing_store_win.h"
-#include "content/browser/renderer_host/input/web_input_event_builders_win.h"
-#include "content/browser/renderer_host/render_process_host_impl.h"
-#include "content/browser/renderer_host/render_widget_host_impl.h"
-#include "content/browser/renderer_host/ui_events_helper.h"
-#include "content/common/accessibility_messages.h"
-#include "content/common/gpu/gpu_messages.h"
-#include "content/common/plugin_constants_win.h"
-#include "content/common/view_messages.h"
-#include "content/common/webplugin_geometry.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/child_process_data.h"
-#include "content/public/browser/content_browser_client.h"
-#include "content/public/browser/native_web_keyboard_event.h"
-#include "content/public/browser/notification_service.h"
-#include "content/public/browser/notification_types.h"
-#include "content/public/browser/render_view_host.h"
-#include "content/public/common/content_switches.h"
-#include "content/public/common/page_zoom.h"
-#include "content/public/common/process_type.h"
-#include "skia/ext/skia_utils_win.h"
-#include "third_party/WebKit/public/web/WebCompositionUnderline.h"
-#include "third_party/WebKit/public/web/WebInputEvent.h"
-#include "third_party/skia/include/core/SkRegion.h"
-#include "ui/base/ime/composition_text.h"
-#include "ui/base/ime/win/imm32_manager.h"
-#include "ui/base/ime/win/tsf_input_scope.h"
-#include "ui/base/l10n/l10n_util_win.h"
-#include "ui/base/touch/touch_device.h"
-#include "ui/base/touch/touch_enabled.h"
-#include "ui/base/ui_base_switches.h"
-#include "ui/base/view_prop.h"
-#include "ui/base/win/mouse_wheel_util.h"
-#include "ui/base/win/touch_input.h"
-#include "ui/events/event.h"
-#include "ui/events/event_utils.h"
-#include "ui/gfx/canvas.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/rect_conversions.h"
-#include "ui/gfx/screen.h"
-#include "ui/gfx/sequential_id_generator.h"
-#include "ui/gfx/text_elider.h"
-#include "ui/gfx/win/dpi.h"
-#include "ui/gfx/win/hwnd_util.h"
-#include "webkit/common/cursors/webcursor.h"
-#include "win8/util/win8_util.h"
-
-using base::TimeDelta;
-using base::TimeTicks;
-using ui::ViewProp;
-using blink::WebInputEvent;
-using blink::WebMouseEvent;
-using blink::WebTextDirection;
-
-namespace content {
-namespace {
-
-// Tooltips will wrap after this width. Yes, wrap. Imagine that!
-const int kTooltipMaxWidthPixels = 300;
-
-// Maximum number of characters we allow in a tooltip.
-const int kMaxTooltipLength = 1024;
-
-// A custom MSAA object id used to determine if a screen reader is actively
-// listening for MSAA events.
-const int kIdCustom = 1;
-
-// The delay before the compositor host window is destroyed. This gives the GPU
-// process a grace period to stop referencing it.
-const int kDestroyCompositorHostWindowDelay = 10000;
-
-// In mouse lock mode, we need to prevent the (invisible) cursor from hitting
-// the border of the view, in order to get valid movement information. However,
-// forcing the cursor back to the center of the view after each mouse move
-// doesn't work well. It reduces the frequency of useful WM_MOUSEMOVE messages
-// significantly. Therefore, we move the cursor to the center of the view only
-// if it approaches the border. |kMouseLockBorderPercentage| specifies the width
-// of the border area, in percentage of the corresponding dimension.
-const int kMouseLockBorderPercentage = 15;
-
-// A callback function for EnumThreadWindows to enumerate and dismiss
-// any owned popup windows.
-BOOL CALLBACK DismissOwnedPopups(HWND window, LPARAM arg) {
- const HWND toplevel_hwnd = reinterpret_cast<HWND>(arg);
-
- if (::IsWindowVisible(window)) {
- const HWND owner = ::GetWindow(window, GW_OWNER);
- if (toplevel_hwnd == owner) {
- ::PostMessage(window, WM_CANCELMODE, 0, 0);
- }
- }
-
- return TRUE;
-}
-
-void SendToGpuProcessHost(int gpu_host_id, scoped_ptr<IPC::Message> message) {
- GpuProcessHost* gpu_process_host = GpuProcessHost::FromID(gpu_host_id);
- if (!gpu_process_host)
- return;
-
- gpu_process_host->Send(message.release());
-}
-
-void PostTaskOnIOThread(const tracked_objects::Location& from_here,
- base::Closure task) {
- BrowserThread::PostTask(BrowserThread::IO, from_here, task);
-}
-
-bool DecodeZoomGesture(HWND hwnd, const GESTUREINFO& gi,
- PageZoom* zoom, POINT* zoom_center) {
- static long start = 0;
- static POINT zoom_first;
-
- if (gi.dwFlags == GF_BEGIN) {
- start = gi.ullArguments;
- zoom_first.x = gi.ptsLocation.x;
- zoom_first.y = gi.ptsLocation.y;
- ScreenToClient(hwnd, &zoom_first);
- return false;
- }
-
- if (gi.dwFlags == GF_END)
- return false;
-
- POINT zoom_second = {0};
- zoom_second.x = gi.ptsLocation.x;
- zoom_second.y = gi.ptsLocation.y;
- ScreenToClient(hwnd, &zoom_second);
-
- if (zoom_first.x == zoom_second.x && zoom_first.y == zoom_second.y)
- return false;
-
- zoom_center->x = (zoom_first.x + zoom_second.x) / 2;
- zoom_center->y = (zoom_first.y + zoom_second.y) / 2;
-
- double zoom_factor =
- static_cast<double>(gi.ullArguments)/static_cast<double>(start);
-
- *zoom = zoom_factor >= 1 ? PAGE_ZOOM_IN : PAGE_ZOOM_OUT;
-
- start = gi.ullArguments;
- zoom_first = zoom_second;
- return true;
-}
-
-bool DecodeScrollGesture(const GESTUREINFO& gi,
- POINT* start,
- POINT* delta){
- // Windows gestures are streams of messages with begin/end messages that
- // separate each new gesture. We key off the begin message to reset
- // the static variables.
- static POINT last_pt;
- static POINT start_pt;
-
- if (gi.dwFlags == GF_BEGIN) {
- delta->x = 0;
- delta->y = 0;
- start_pt.x = gi.ptsLocation.x;
- start_pt.y = gi.ptsLocation.y;
- } else {
- delta->x = gi.ptsLocation.x - last_pt.x;
- delta->y = gi.ptsLocation.y - last_pt.y;
- }
- last_pt.x = gi.ptsLocation.x;
- last_pt.y = gi.ptsLocation.y;
- *start = start_pt;
- return true;
-}
-
-blink::WebMouseWheelEvent MakeFakeScrollWheelEvent(HWND hwnd,
- POINT start,
- POINT delta) {
- blink::WebMouseWheelEvent result;
- result.type = WebInputEvent::MouseWheel;
- result.timeStampSeconds = ::GetMessageTime() / 1000.0;
- result.button = WebMouseEvent::ButtonNone;
- result.globalX = start.x;
- result.globalY = start.y;
- // Map to window coordinates.
- POINT client_point = { result.globalX, result.globalY };
- MapWindowPoints(0, hwnd, &client_point, 1);
- result.x = client_point.x;
- result.y = client_point.y;
- result.windowX = result.x;
- result.windowY = result.y;
- // Note that we support diagonal scrolling.
- result.deltaX = static_cast<float>(delta.x);
- result.wheelTicksX = WHEEL_DELTA;
- result.deltaY = static_cast<float>(delta.y);
- result.wheelTicksY = WHEEL_DELTA;
- return result;
-}
-
-static const int kTouchMask = 0x7;
-
-inline int GetTouchType(const TOUCHINPUT& point) {
- return point.dwFlags & kTouchMask;
-}
-
-inline void SetTouchType(TOUCHINPUT* point, int type) {
- point->dwFlags = (point->dwFlags & kTouchMask) | type;
-}
-
-ui::EventType ConvertToUIEvent(blink::WebTouchPoint::State t) {
- switch (t) {
- case blink::WebTouchPoint::StatePressed:
- return ui::ET_TOUCH_PRESSED;
- case blink::WebTouchPoint::StateMoved:
- return ui::ET_TOUCH_MOVED;
- case blink::WebTouchPoint::StateStationary:
- return ui::ET_TOUCH_STATIONARY;
- case blink::WebTouchPoint::StateReleased:
- return ui::ET_TOUCH_RELEASED;
- case blink::WebTouchPoint::StateCancelled:
- return ui::ET_TOUCH_CANCELLED;
- default:
- DCHECK(false) << "Unexpected ui type. " << t;
- return ui::ET_UNKNOWN;
- }
-}
-
-// Creates a WebGestureEvent corresponding to the given |gesture|
-blink::WebGestureEvent CreateWebGestureEvent(HWND hwnd,
- const ui::GestureEvent& gesture) {
- blink::WebGestureEvent gesture_event =
- MakeWebGestureEventFromUIEvent(gesture);
-
- POINT client_point = gesture.location().ToPOINT();
- POINT screen_point = gesture.location().ToPOINT();
- MapWindowPoints(hwnd, HWND_DESKTOP, &screen_point, 1);
-
- gesture_event.x = client_point.x;
- gesture_event.y = client_point.y;
- gesture_event.globalX = screen_point.x;
- gesture_event.globalY = screen_point.y;
-
- return gesture_event;
-}
-
-blink::WebGestureEvent CreateFlingCancelEvent(double time_stamp) {
- blink::WebGestureEvent gesture_event;
- gesture_event.timeStampSeconds = time_stamp;
- gesture_event.type = blink::WebGestureEvent::GestureFlingCancel;
- gesture_event.sourceDevice = blink::WebGestureEvent::Touchscreen;
- return gesture_event;
-}
-
-class TouchEventFromWebTouchPoint : public ui::TouchEvent {
- public:
- TouchEventFromWebTouchPoint(const blink::WebTouchPoint& touch_point,
- base::TimeDelta& timestamp)
- : ui::TouchEvent(ConvertToUIEvent(touch_point.state),
- touch_point.position,
- touch_point.id,
- timestamp) {
- set_radius(touch_point.radiusX, touch_point.radiusY);
- set_rotation_angle(touch_point.rotationAngle);
- set_force(touch_point.force);
- set_flags(ui::GetModifiersFromKeyState());
- }
-
- virtual ~TouchEventFromWebTouchPoint() {}
-
- private:
- DISALLOW_COPY_AND_ASSIGN(TouchEventFromWebTouchPoint);
-};
-
-bool ShouldSendPinchGesture() {
- static bool pinch_allowed =
- CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnablePinch);
- return pinch_allowed;
-}
-
-void GetScreenInfoForWindow(gfx::NativeViewId id,
- blink::WebScreenInfo* results) {
- HWND window = gfx::NativeViewFromId(id);
-
- HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTOPRIMARY);
-
- MONITORINFOEX monitor_info;
- monitor_info.cbSize = sizeof(MONITORINFOEX);
- if (!base::win::GetMonitorInfoWrapper(monitor, &monitor_info))
- return;
-
- DEVMODE dev_mode;
- dev_mode.dmSize = sizeof(dev_mode);
- dev_mode.dmDriverExtra = 0;
- EnumDisplaySettings(monitor_info.szDevice, ENUM_CURRENT_SETTINGS, &dev_mode);
-
- blink::WebScreenInfo screen_info;
- screen_info.depth = dev_mode.dmBitsPerPel;
- screen_info.depthPerComponent = 8;
- screen_info.deviceScaleFactor = gfx::win::GetDeviceScaleFactor();
- screen_info.isMonochrome = dev_mode.dmColor == DMCOLOR_MONOCHROME;
- screen_info.rect = gfx::Rect(monitor_info.rcMonitor);
- screen_info.availableRect = gfx::Rect(monitor_info.rcWork);
-
- *results = screen_info;
-}
-
-} // namespace
-
-const wchar_t kRenderWidgetHostHWNDClass[] = L"Chrome_RenderWidgetHostHWND";
-
-// Wrapper for maintaining touchstate associated with a WebTouchEvent.
-class WebTouchState {
- public:
- explicit WebTouchState(const RenderWidgetHostViewWin* window);
-
- // Updates the current touchpoint state with the supplied touches.
- // Touches will be consumed only if they are of the same type (e.g. down,
- // up, move). Returns the number of consumed touches.
- size_t UpdateTouchPoints(TOUCHINPUT* points, size_t count);
-
- // Marks all active touchpoints as released.
- bool ReleaseTouchPoints();
-
- // The contained WebTouchEvent.
- const blink::WebTouchEvent& touch_event() { return touch_event_; }
-
- // Returns if any touches are modified in the event.
- bool is_changed() { return touch_event_.changedTouchesLength != 0; }
-
- private:
- // Adds a touch point or returns NULL if there's not enough space.
- blink::WebTouchPoint* AddTouchPoint(TOUCHINPUT* touch_input);
-
- // Copy details from a TOUCHINPUT to an existing WebTouchPoint, returning
- // true if the resulting point is a stationary move.
- bool UpdateTouchPoint(blink::WebTouchPoint* touch_point,
- TOUCHINPUT* touch_input);
-
- // Find (or create) a mapping for _os_touch_id_.
- unsigned int GetMappedTouch(unsigned int os_touch_id);
-
- // Remove any mappings that are no longer in use.
- void RemoveExpiredMappings();
-
- blink::WebTouchEvent touch_event_;
- const RenderWidgetHostViewWin* const window_;
-
- ui::SequentialIDGenerator id_generator_;
-
- DISALLOW_COPY_AND_ASSIGN(WebTouchState);
-};
-
-typedef void (*MetroSetFrameWindow)(HWND window);
-typedef void (*MetroCloseFrameWindow)(HWND window);
-
-///////////////////////////////////////////////////////////////////////////////
-// RenderWidgetHostViewWin, public:
-
-RenderWidgetHostViewWin::RenderWidgetHostViewWin(RenderWidgetHost* widget)
- : render_widget_host_(RenderWidgetHostImpl::From(widget)),
- compositor_host_window_(NULL),
- hide_compositor_window_at_next_paint_(false),
- track_mouse_leave_(false),
- imm32_manager_(new ui::IMM32Manager),
- ime_notification_(false),
- capture_enter_key_(false),
- about_to_validate_and_paint_(false),
- close_on_deactivate_(false),
- being_destroyed_(false),
- tooltip_hwnd_(NULL),
- tooltip_showing_(false),
- weak_factory_(this),
- is_loading_(false),
- text_input_type_(ui::TEXT_INPUT_TYPE_NONE),
- text_input_mode_(ui::TEXT_INPUT_MODE_DEFAULT),
- can_compose_inline_(true),
- is_fullscreen_(false),
- ignore_mouse_movement_(true),
- composition_range_(gfx::Range::InvalidRange()),
- touch_state_(new WebTouchState(this)),
- pointer_down_context_(false),
- last_touch_location_(-1, -1),
- touch_events_enabled_(ui::AreTouchEventsEnabled()),
- gesture_recognizer_(ui::GestureRecognizer::Create()) {
- render_widget_host_->SetView(this);
- registrar_.Add(this,
- NOTIFICATION_RENDERER_PROCESS_TERMINATED,
- NotificationService::AllBrowserContextsAndSources());
- gesture_recognizer_->AddGestureEventHelper(this);
-}
-
-RenderWidgetHostViewWin::~RenderWidgetHostViewWin() {
- gesture_recognizer_->RemoveGestureEventHelper(this);
- UnlockMouse();
- ResetTooltip();
-}
-
-void RenderWidgetHostViewWin::CreateWnd(HWND parent) {
- // ATL function to create the window.
- Create(parent);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// RenderWidgetHostViewWin, RenderWidgetHostView implementation:
-
-void RenderWidgetHostViewWin::InitAsChild(
- gfx::NativeView parent_view) {
- CreateWnd(parent_view);
-}
-
-void RenderWidgetHostViewWin::InitAsPopup(
- RenderWidgetHostView* parent_host_view, const gfx::Rect& pos) {
- close_on_deactivate_ = true;
- DoPopupOrFullscreenInit(parent_host_view->GetNativeView(), pos,
- WS_EX_TOOLWINDOW);
-}
-
-void RenderWidgetHostViewWin::InitAsFullscreen(
- RenderWidgetHostView* reference_host_view) {
- gfx::Rect pos = gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(
- reference_host_view->GetNativeView()).bounds();
- is_fullscreen_ = true;
- DoPopupOrFullscreenInit(gfx::GetWindowToParentTo(true), pos, 0);
-}
-
-RenderWidgetHost* RenderWidgetHostViewWin::GetRenderWidgetHost() const {
- return render_widget_host_;
-}
-
-void RenderWidgetHostViewWin::WasShown() {
- // |render_widget_host_| may be NULL if the WebContentsImpl is in the process
- // of closing.
- if (!render_widget_host_)
- return;
-
- if (!render_widget_host_->is_hidden())
- return;
-
- if (web_contents_switch_paint_time_.is_null())
- web_contents_switch_paint_time_ = TimeTicks::Now();
-
- render_widget_host_->WasShown();
-}
-
-void RenderWidgetHostViewWin::WasHidden() {
- // |render_widget_host_| may be NULL if the WebContentsImpl is in the process
- // of closing.
- if (!render_widget_host_)
- return;
-
- if (render_widget_host_->is_hidden())
- return;
-
- ResetTooltip();
-
- // Inform the renderer that we are being hidden so it can reduce its resource
- // utilization.
- render_widget_host_->WasHidden();
-
- if (accelerated_surface_)
- accelerated_surface_->WasHidden();
-
- if (GetBrowserAccessibilityManager())
- GetBrowserAccessibilityManager()->WasHidden();
-
- web_contents_switch_paint_time_ = base::TimeTicks();
-}
-
-void RenderWidgetHostViewWin::SetSize(const gfx::Size& size) {
- SetBounds(gfx::Rect(GetPixelBounds().origin(), size));
-}
-
-void RenderWidgetHostViewWin::SetBounds(const gfx::Rect& rect) {
- if (being_destroyed_)
- return;
-
- // No SWP_NOREDRAW as autofill popups can move and the underneath window
- // should redraw in that case.
- UINT swp_flags = SWP_NOSENDCHANGING | SWP_NOOWNERZORDER | SWP_NOCOPYBITS |
- SWP_NOZORDER | SWP_NOACTIVATE | SWP_DEFERERASE;
-
- // If the style is not popup, you have to convert the point to client
- // coordinate.
- POINT point = { rect.x(), rect.y() };
- if (GetStyle() & WS_CHILD)
- ScreenToClient(&point);
-
- SetWindowPos(NULL, point.x, point.y, rect.width(), rect.height(), swp_flags);
- render_widget_host_->WasResized();
-}
-
-gfx::NativeView RenderWidgetHostViewWin::GetNativeView() const {
- return m_hWnd;
-}
-
-gfx::NativeViewId RenderWidgetHostViewWin::GetNativeViewId() const {
- return reinterpret_cast<gfx::NativeViewId>(m_hWnd);
-}
-
-gfx::NativeViewAccessible
-RenderWidgetHostViewWin::GetNativeViewAccessible() {
- if (render_widget_host_ &&
- !BrowserAccessibilityState::GetInstance()->IsAccessibleBrowser()) {
- // Attempt to detect screen readers by sending an event with our custom id.
- NotifyWinEvent(EVENT_SYSTEM_ALERT, m_hWnd, kIdCustom, CHILDID_SELF);
- }
-
- CreateBrowserAccessibilityManagerIfNeeded();
-
- return GetBrowserAccessibilityManager()->GetRoot()->
- ToBrowserAccessibilityWin();
-}
-
-void RenderWidgetHostViewWin::CreateBrowserAccessibilityManagerIfNeeded() {
- if (GetBrowserAccessibilityManager())
- return;
-
- HRESULT hr = ::CreateStdAccessibleObject(
- m_hWnd, OBJID_WINDOW, IID_IAccessible,
- reinterpret_cast<void **>(&window_iaccessible_));
- DCHECK(SUCCEEDED(hr));
-
- SetBrowserAccessibilityManager(
- new BrowserAccessibilityManagerWin(
- m_hWnd,
- window_iaccessible_.get(),
- BrowserAccessibilityManagerWin::GetEmptyDocument(),
- this));
-}
-
-void RenderWidgetHostViewWin::MovePluginWindows(
- const gfx::Vector2d& scroll_offset,
- const std::vector<WebPluginGeometry>& plugin_window_moves) {
- MovePluginWindowsHelper(m_hWnd, plugin_window_moves);
-}
-
-static BOOL CALLBACK AddChildWindowToVector(HWND hwnd, LPARAM lparam) {
- std::vector<HWND>* vector = reinterpret_cast<std::vector<HWND>*>(lparam);
- vector->push_back(hwnd);
- return TRUE;
-}
-
-void RenderWidgetHostViewWin::CleanupCompositorWindow() {
- if (!compositor_host_window_)
- return;
-
- gfx::SetWindowUserData(compositor_host_window_, NULL);
-
- // Hide the compositor and parent it to the desktop rather than destroying
- // it immediately. The GPU process has a grace period to stop accessing the
- // window. TODO(apatrick): the GPU process should acknowledge that it has
- // finished with the window handle and the browser process should destroy it
- // at that point.
- ::ShowWindow(compositor_host_window_, SW_HIDE);
- ::SetParent(compositor_host_window_, NULL);
-
- BrowserThread::PostDelayedTask(
- BrowserThread::UI,
- FROM_HERE,
- base::Bind(base::IgnoreResult(&::DestroyWindow),
- compositor_host_window_),
- base::TimeDelta::FromMilliseconds(kDestroyCompositorHostWindowDelay));
-
- compositor_host_window_ = NULL;
-}
-
-bool RenderWidgetHostViewWin::IsActivatable() const {
- // Popups should not be activated.
- return popup_type_ == blink::WebPopupTypeNone;
-}
-
-void RenderWidgetHostViewWin::Focus() {
- if (IsWindow())
- SetFocus();
-}
-
-void RenderWidgetHostViewWin::Blur() {
- NOTREACHED();
-}
-
-bool RenderWidgetHostViewWin::HasFocus() const {
- return ::GetFocus() == m_hWnd;
-}
-
-bool RenderWidgetHostViewWin::IsSurfaceAvailableForCopy() const {
- if (render_widget_host_->is_accelerated_compositing_active())
- return accelerated_surface_.get() && accelerated_surface_->IsReadyForCopy();
- else
- return !!render_widget_host_->GetBackingStore(false);
-}
-
-void RenderWidgetHostViewWin::Show() {
- ShowWindow(SW_SHOW);
- WasShown();
-}
-
-void RenderWidgetHostViewWin::Hide() {
- if (!is_fullscreen_ && GetParent() == gfx::GetWindowToParentTo(true)) {
- LOG(WARNING) << "Hide() called twice in a row: " << this << ":"
- << GetParent();
- return;
- }
-
- if (::GetFocus() == m_hWnd)
- ::SetFocus(NULL);
- ShowWindow(SW_HIDE);
-
- WasHidden();
-}
-
-bool RenderWidgetHostViewWin::IsShowing() {
- return !!IsWindowVisible();
-}
-
-gfx::Rect RenderWidgetHostViewWin::GetViewBounds() const {
- return gfx::win::ScreenToDIPRect(GetPixelBounds());
-}
-
-gfx::Rect RenderWidgetHostViewWin::GetPixelBounds() const {
- CRect window_rect;
- GetWindowRect(&window_rect);
- return gfx::Rect(window_rect);
-}
-
-void RenderWidgetHostViewWin::UpdateCursor(const WebCursor& cursor) {
- current_cursor_ = cursor;
- UpdateCursorIfOverSelf();
-}
-
-void RenderWidgetHostViewWin::UpdateCursorIfOverSelf() {
- static HCURSOR kCursorArrow = LoadCursor(NULL, IDC_ARROW);
- static HCURSOR kCursorAppStarting = LoadCursor(NULL, IDC_APPSTARTING);
- static HINSTANCE module_handle = GetModuleHandle(
- GetContentClient()->browser()->GetResourceDllName());
-
- // If the mouse is over our HWND, then update the cursor state immediately.
- CPoint pt;
- GetCursorPos(&pt);
- if (WindowFromPoint(pt) == m_hWnd) {
- // We cannot pass in NULL as the module handle as this would only work for
- // standard win32 cursors. We can also receive cursor types which are
- // defined as webkit resources. We need to specify the module handle of
- // chrome.dll while loading these cursors.
- HCURSOR display_cursor = current_cursor_.GetCursor(module_handle);
-
- // If a page is in the loading state, we want to show the Arrow+Hourglass
- // cursor only when the current cursor is the ARROW cursor. In all other
- // cases we should continue to display the current cursor.
- if (is_loading_ && display_cursor == kCursorArrow)
- display_cursor = kCursorAppStarting;
-
- SetCursor(display_cursor);
- }
-}
-
-void RenderWidgetHostViewWin::SetIsLoading(bool is_loading) {
- is_loading_ = is_loading;
- UpdateCursorIfOverSelf();
-}
-
-void RenderWidgetHostViewWin::TextInputTypeChanged(
- ui::TextInputType type,
- ui::TextInputMode input_mode,
- bool can_compose_inline) {
- if (text_input_type_ != type ||
- text_input_mode_ != input_mode ||
- can_compose_inline_ != can_compose_inline) {
- const bool text_input_type_changed = (text_input_type_ != type) ||
- (text_input_mode_ != input_mode);
- text_input_type_ = type;
- text_input_mode_ = input_mode;
- can_compose_inline_ = can_compose_inline;
- UpdateIMEState();
- if (text_input_type_changed)
- UpdateInputScopeIfNecessary(text_input_type_);
- }
-}
-
-void RenderWidgetHostViewWin::SelectionBoundsChanged(
- const ViewHostMsg_SelectionBounds_Params& params) {
- bool is_enabled = (text_input_type_ != ui::TEXT_INPUT_TYPE_NONE &&
- text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD);
- // Only update caret position if the input method is enabled.
- if (is_enabled) {
- caret_rect_ = gfx::UnionRects(params.anchor_rect, params.focus_rect);
- imm32_manager_->UpdateCaretRect(m_hWnd, caret_rect_);
- }
-}
-
-void RenderWidgetHostViewWin::ScrollOffsetChanged() {
-}
-
-void RenderWidgetHostViewWin::ImeCancelComposition() {
- imm32_manager_->CancelIME(m_hWnd);
-}
-
-void RenderWidgetHostViewWin::ImeCompositionRangeChanged(
- const gfx::Range& range,
- const std::vector<gfx::Rect>& character_bounds) {
- composition_range_ = range;
- composition_character_bounds_ = character_bounds;
-}
-
-void RenderWidgetHostViewWin::Redraw() {
- RECT damage_bounds;
- GetUpdateRect(&damage_bounds, FALSE);
-
- base::win::ScopedGDIObject<HRGN> damage_region(CreateRectRgn(0, 0, 0, 0));
- GetUpdateRgn(damage_region, FALSE);
-
- // Paint the invalid region synchronously. Our caller will not paint again
- // until we return, so by painting to the screen here, we ensure effective
- // rate-limiting of backing store updates. This helps a lot on pages that
- // have animations or fairly expensive layout (e.g., google maps).
- //
- // We paint this window synchronously, however child windows (i.e. plugins)
- // are painted asynchronously. By avoiding synchronous cross-process window
- // message dispatching we allow scrolling to be smooth, and also avoid the
- // browser process locking up if the plugin process is hung.
- //
- RedrawWindow(NULL, damage_region, RDW_UPDATENOW | RDW_NOCHILDREN);
-
- // Send the invalid rect in screen coordinates.
- gfx::Rect invalid_screen_rect(damage_bounds);
- invalid_screen_rect.Offset(GetPixelBounds().OffsetFromOrigin());
-
- PaintPluginWindowsHelper(m_hWnd, invalid_screen_rect);
-}
-
-void RenderWidgetHostViewWin::DidUpdateBackingStore(
- const gfx::Rect& scroll_rect,
- const gfx::Vector2d& scroll_delta,
- const std::vector<gfx::Rect>& copy_rects,
- const ui::LatencyInfo& latency_info) {
- TRACE_EVENT0("content", "RenderWidgetHostViewWin::DidUpdateBackingStore");
- software_latency_info_.MergeWith(latency_info);
- if (render_widget_host_->is_hidden())
- return;
-
- // Schedule invalidations first so that the ScrollWindowEx call is closer to
- // Redraw. That minimizes chances of "flicker" resulting if the screen
- // refreshes before we have a chance to paint the exposed area. Somewhat
- // surprisingly, this ordering matters.
-
- for (size_t i = 0; i < copy_rects.size(); ++i) {
- gfx::Rect pixel_rect = gfx::win::DIPToScreenRect(copy_rects[i]);
- // Damage might not be DIP aligned.
- pixel_rect.Inset(-1, -1);
- RECT bounds = pixel_rect.ToRECT();
- InvalidateRect(&bounds, false);
- }
-
- if (!scroll_rect.IsEmpty()) {
- TRACE_EVENT0("content", "ScrollWindowEx");
- gfx::Rect pixel_rect = gfx::win::DIPToScreenRect(scroll_rect);
- // Damage might not be DIP aligned.
- pixel_rect.Inset(-1, -1);
- RECT clip_rect = pixel_rect.ToRECT();
- float scale = gfx::win::GetDeviceScaleFactor();
- int dx = static_cast<int>(scale * scroll_delta.x());
- int dy = static_cast<int>(scale * scroll_delta.y());
- ScrollWindowEx(dx, dy, NULL, &clip_rect, NULL, NULL, SW_INVALIDATE);
- }
-
- if (!about_to_validate_and_paint_)
- Redraw();
-}
-
-void RenderWidgetHostViewWin::RenderProcessGone(base::TerminationStatus status,
- int error_code) {
- UpdateCursorIfOverSelf();
- Destroy();
-}
-
-bool RenderWidgetHostViewWin::CanSubscribeFrame() const {
- return render_widget_host_ != NULL;
-}
-
-void RenderWidgetHostViewWin::WillWmDestroy() {
- CleanupCompositorWindow();
- if (base::win::IsTSFAwareRequired())
- ui::TSFBridge::GetInstance()->RemoveFocusedClient(this);
-}
-
-void RenderWidgetHostViewWin::Destroy() {
- // We've been told to destroy.
- // By clearing close_on_deactivate_, we prevent further deactivations
- // (caused by windows messages resulting from the DestroyWindow) from
- // triggering further destructions. The deletion of this is handled by
- // OnFinalMessage();
- close_on_deactivate_ = false;
- render_widget_host_ = NULL;
- being_destroyed_ = true;
- CleanupCompositorWindow();
-
- // This releases the resources associated with input scope.
- UpdateInputScopeIfNecessary(ui::TEXT_INPUT_TYPE_NONE);
-
- if (is_fullscreen_ && win8::IsSingleWindowMetroMode()) {
- MetroCloseFrameWindow close_frame_window =
- reinterpret_cast<MetroCloseFrameWindow>(
- ::GetProcAddress(base::win::GetMetroModule(), "CloseFrameWindow"));
- DCHECK(close_frame_window);
- close_frame_window(m_hWnd);
- }
-
- DestroyWindow();
-}
-
-void RenderWidgetHostViewWin::SetTooltipText(
- const base::string16& tooltip_text) {
- if (!render_widget_host_->is_hidden())
- EnsureTooltip();
-
- // Clamp the tooltip length to kMaxTooltipLength so that we don't
- // accidentally DOS the user with a mega tooltip (since Windows doesn't seem
- // to do this itself).
- const base::string16 new_tooltip_text =
- gfx::TruncateString(tooltip_text, kMaxTooltipLength);
-
- if (new_tooltip_text != tooltip_text_) {
- tooltip_text_ = new_tooltip_text;
-
- // Need to check if the tooltip is already showing so that we don't
- // immediately show the tooltip with no delay when we move the mouse from
- // a region with no tooltip to a region with a tooltip.
- if (::IsWindow(tooltip_hwnd_) && tooltip_showing_) {
- ::SendMessage(tooltip_hwnd_, TTM_POP, 0, 0);
- ::SendMessage(tooltip_hwnd_, TTM_POPUP, 0, 0);
- }
- } else {
- // Make sure the tooltip gets closed after TTN_POP gets sent. For some
- // reason this doesn't happen automatically, so moving the mouse around
- // within the same link/image/etc doesn't cause the tooltip to re-appear.
- if (!tooltip_showing_) {
- if (::IsWindow(tooltip_hwnd_))
- ::SendMessage(tooltip_hwnd_, TTM_POP, 0, 0);
- }
- }
-}
-
-BackingStore* RenderWidgetHostViewWin::AllocBackingStore(
- const gfx::Size& size) {
- return new BackingStoreWin(render_widget_host_, size);
-}
-
-void RenderWidgetHostViewWin::CopyFromCompositingSurface(
- const gfx::Rect& src_subrect,
- const gfx::Size& dst_size,
- const base::Callback<void(bool, const SkBitmap&)>& callback) {
- base::ScopedClosureRunner scoped_callback_runner(
- base::Bind(callback, false, SkBitmap()));
- if (!accelerated_surface_)
- return;
-
- if (dst_size.IsEmpty() || src_subrect.IsEmpty())
- return;
-
- ignore_result(scoped_callback_runner.Release());
- accelerated_surface_->AsyncCopyTo(src_subrect, dst_size, callback);
-}
-
-void RenderWidgetHostViewWin::CopyFromCompositingSurfaceToVideoFrame(
- const gfx::Rect& src_subrect,
- const scoped_refptr<media::VideoFrame>& target,
- const base::Callback<void(bool)>& callback) {
- base::ScopedClosureRunner scoped_callback_runner(base::Bind(callback, false));
- if (!accelerated_surface_)
- return;
-
- if (!target || (target->format() != media::VideoFrame::YV12 &&
- target->format() != media::VideoFrame::I420))
- return;
-
- if (src_subrect.IsEmpty())
- return;
-
- ignore_result(scoped_callback_runner.Release());
- accelerated_surface_->AsyncCopyToVideoFrame(src_subrect, target, callback);
-}
-
-bool RenderWidgetHostViewWin::CanCopyToVideoFrame() const {
- return accelerated_surface_.get() && render_widget_host_ &&
- render_widget_host_->is_accelerated_compositing_active();
-}
-
-void RenderWidgetHostViewWin::SetBackground(const SkBitmap& background) {
- RenderWidgetHostViewBase::SetBackground(background);
- render_widget_host_->SetBackground(background);
-}
-
-void RenderWidgetHostViewWin::ProcessAckedTouchEvent(
- const TouchEventWithLatencyInfo& touch, InputEventAckState ack_result) {
- DCHECK(touch_events_enabled_);
-
- ScopedVector<ui::TouchEvent> events;
- if (!MakeUITouchEventsFromWebTouchEvents(touch, &events, LOCAL_COORDINATES))
- return;
-
- ui::EventResult result = (ack_result ==
- INPUT_EVENT_ACK_STATE_CONSUMED) ? ui::ER_HANDLED : ui::ER_UNHANDLED;
- for (ScopedVector<ui::TouchEvent>::iterator iter = events.begin(),
- end = events.end(); iter != end; ++iter) {
- scoped_ptr<ui::GestureRecognizer::Gestures> gestures;
- gestures.reset(gesture_recognizer_->ProcessTouchEventForGesture(
- *(*iter), result, this));
- ProcessGestures(gestures.get());
- }
-}
-
-void RenderWidgetHostViewWin::UpdateDesiredTouchMode() {
- // Make sure that touch events even make sense.
- if (base::win::GetVersion() < base::win::VERSION_WIN7)
- return;
- if (touch_events_enabled_) {
- CHECK(RegisterTouchWindow(m_hWnd, TWF_WANTPALM));
- }
-}
-
-bool RenderWidgetHostViewWin::CanDispatchToConsumer(
- ui::GestureConsumer* consumer) {
- CHECK_EQ(static_cast<RenderWidgetHostViewWin*>(consumer), this);
- return true;
-}
-
-void RenderWidgetHostViewWin::DispatchPostponedGestureEvent(
- ui::GestureEvent* event) {
- ForwardGestureEventToRenderer(event);
-}
-
-void RenderWidgetHostViewWin::DispatchCancelTouchEvent(
- ui::TouchEvent* event) {
- if (!render_widget_host_ || !touch_events_enabled_ ||
- !render_widget_host_->ShouldForwardTouchEvent()) {
- return;
- }
- DCHECK(event->type() == blink::WebInputEvent::TouchCancel);
- blink::WebTouchEvent cancel_event;
- cancel_event.type = blink::WebInputEvent::TouchCancel;
- cancel_event.timeStampSeconds = event->time_stamp().InSecondsF();
- render_widget_host_->ForwardTouchEventWithLatencyInfo(
- cancel_event, *event->latency());
-}
-
-void RenderWidgetHostViewWin::SetHasHorizontalScrollbar(
- bool has_horizontal_scrollbar) {
-}
-
-void RenderWidgetHostViewWin::SetScrollOffsetPinning(
- bool is_pinned_to_left, bool is_pinned_to_right) {
-}
-
-void RenderWidgetHostViewWin::SetCompositionText(
- const ui::CompositionText& composition) {
- if (!base::win::IsTSFAwareRequired()) {
- NOTREACHED();
- return;
- }
- if (!render_widget_host_)
- return;
- // ui::CompositionUnderline should be identical to
- // blink::WebCompositionUnderline, so that we can do reinterpret_cast safely.
- COMPILE_ASSERT(sizeof(ui::CompositionUnderline) ==
- sizeof(blink::WebCompositionUnderline),
- ui_CompositionUnderline__WebKit_WebCompositionUnderline_diff);
- const std::vector<blink::WebCompositionUnderline>& underlines =
- reinterpret_cast<const std::vector<blink::WebCompositionUnderline>&>(
- composition.underlines);
- render_widget_host_->ImeSetComposition(composition.text, underlines,
- composition.selection.end(),
- composition.selection.end());
-}
-
-void RenderWidgetHostViewWin::ConfirmCompositionText() {
- if (!base::win::IsTSFAwareRequired()) {
- NOTREACHED();
- return;
- }
- // TODO(nona): Implement this function.
- NOTIMPLEMENTED();
-}
-
-void RenderWidgetHostViewWin::ClearCompositionText() {
- if (!base::win::IsTSFAwareRequired()) {
- NOTREACHED();
- return;
- }
- // TODO(nona): Implement this function.
- NOTIMPLEMENTED();
-}
-
-void RenderWidgetHostViewWin::InsertText(const base::string16& text) {
- if (!base::win::IsTSFAwareRequired()) {
- NOTREACHED();
- return;
- }
- if (render_widget_host_)
- render_widget_host_->ImeConfirmComposition(text,
- gfx::Range::InvalidRange(),
- false);
-}
-
-void RenderWidgetHostViewWin::InsertChar(char16 ch, int flags) {
- if (!base::win::IsTSFAwareRequired()) {
- NOTREACHED();
- return;
- }
- // TODO(nona): Implement this function.
- NOTIMPLEMENTED();
-}
-
-gfx::NativeWindow RenderWidgetHostViewWin::GetAttachedWindow() const {
- return m_hWnd;
-}
-
-ui::TextInputType RenderWidgetHostViewWin::GetTextInputType() const {
- if (!base::win::IsTSFAwareRequired()) {
- NOTREACHED();
- return ui::TEXT_INPUT_TYPE_NONE;
- }
- return text_input_type_;
-}
-
-ui::TextInputMode RenderWidgetHostViewWin::GetTextInputMode() const {
- if (!base::win::IsTSFAwareRequired()) {
- NOTREACHED();
- return ui::TEXT_INPUT_MODE_DEFAULT;
- }
- return text_input_mode_;
-}
-
-bool RenderWidgetHostViewWin::CanComposeInline() const {
- if (!base::win::IsTSFAwareRequired()) {
- NOTREACHED();
- return false;
- }
- // TODO(nona): Implement this function.
- NOTIMPLEMENTED();
- return false;
-}
-
-gfx::Rect RenderWidgetHostViewWin::GetCaretBounds() const {
- if (!base::win::IsTSFAwareRequired()) {
- NOTREACHED();
- return gfx::Rect(0, 0, 0, 0);
- }
- RECT tmp_rect = caret_rect_.ToRECT();
- ClientToScreen(&tmp_rect);
- return gfx::Rect(tmp_rect);
-}
-
-bool RenderWidgetHostViewWin::GetCompositionCharacterBounds(
- uint32 index, gfx::Rect* rect) const {
- if (!base::win::IsTSFAwareRequired()) {
- NOTREACHED();
- return false;
- }
- DCHECK(rect);
- if (index >= composition_character_bounds_.size())
- return false;
- RECT rec = composition_character_bounds_[index].ToRECT();
- ClientToScreen(&rec);
- *rect = gfx::Rect(rec);
- return true;
-}
-
-bool RenderWidgetHostViewWin::HasCompositionText() const {
- if (!base::win::IsTSFAwareRequired()) {
- NOTREACHED();
- return false;
- }
- // TODO(nona): Implement this function.
- NOTIMPLEMENTED();
- return false;
-}
-
-bool RenderWidgetHostViewWin::GetTextRange(gfx::Range* range) const {
- if (!base::win::IsTSFAwareRequired()) {
- NOTREACHED();
- return false;
- }
- range->set_start(selection_text_offset_);
- range->set_end(selection_text_offset_ + selection_text_.length());
- return false;
-}
-
-bool RenderWidgetHostViewWin::GetCompositionTextRange(gfx::Range* range) const {
- if (!base::win::IsTSFAwareRequired()) {
- NOTREACHED();
- return false;
- }
- // TODO(nona): Implement this function.
- NOTIMPLEMENTED();
- return false;
-}
-
-bool RenderWidgetHostViewWin::GetSelectionRange(gfx::Range* range) const {
- if (!base::win::IsTSFAwareRequired()) {
- NOTREACHED();
- return false;
- }
- range->set_start(selection_range_.start());
- range->set_end(selection_range_.end());
- return false;
-}
-
-bool RenderWidgetHostViewWin::SetSelectionRange(const gfx::Range& range) {
- if (!base::win::IsTSFAwareRequired()) {
- NOTREACHED();
- return false;
- }
- // TODO(nona): Implement this function.
- NOTIMPLEMENTED();
- return false;
-}
-
-bool RenderWidgetHostViewWin::DeleteRange(const gfx::Range& range) {
- if (!base::win::IsTSFAwareRequired()) {
- NOTREACHED();
- return false;
- }
- // TODO(nona): Implement this function.
- NOTIMPLEMENTED();
- return false;
-}
-
-bool RenderWidgetHostViewWin::GetTextFromRange(const gfx::Range& range,
- base::string16* text) const {
- if (!base::win::IsTSFAwareRequired()) {
- NOTREACHED();
- return false;
- }
- gfx::Range selection_text_range(selection_text_offset_,
- selection_text_offset_ + selection_text_.length());
- if (!selection_text_range.Contains(range)) {
- text->clear();
- return false;
- }
- if (selection_text_range.EqualsIgnoringDirection(range)) {
- *text = selection_text_;
- } else {
- *text = selection_text_.substr(
- range.GetMin() - selection_text_offset_,
- range.length());
- }
- return true;
-}
-
-void RenderWidgetHostViewWin::OnInputMethodChanged() {
- if (!base::win::IsTSFAwareRequired()) {
- NOTREACHED();
- return;
- }
- // TODO(nona): Implement this function.
- NOTIMPLEMENTED();
-}
-
-bool RenderWidgetHostViewWin::ChangeTextDirectionAndLayoutAlignment(
- base::i18n::TextDirection direction) {
- if (!base::win::IsTSFAwareRequired()) {
- NOTREACHED();
- return false;
- }
- // TODO(nona): Implement this function.
- NOTIMPLEMENTED();
- return false;
-}
-
-void RenderWidgetHostViewWin::ExtendSelectionAndDelete(
- size_t before,
- size_t after) {
- if (!base::win::IsTSFAwareRequired()) {
- NOTREACHED();
- return;
- }
- if (!render_widget_host_)
- return;
- render_widget_host_->ExtendSelectionAndDelete(before, after);
-}
-
-void RenderWidgetHostViewWin::EnsureCaretInRect(const gfx::Rect& rect) {
- // TODO(nona): Implement this function.
- NOTIMPLEMENTED();
-}
-
-void RenderWidgetHostViewWin::OnCandidateWindowShown() {
-}
-
-void RenderWidgetHostViewWin::OnCandidateWindowUpdated() {
-}
-
-void RenderWidgetHostViewWin::OnCandidateWindowHidden() {
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// RenderWidgetHostViewWin, private:
-
-LRESULT RenderWidgetHostViewWin::OnCreate(CREATESTRUCT* create_struct) {
- TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnCreate");
- // Call the WM_INPUTLANGCHANGE message handler to initialize the input locale
- // of a browser process.
- OnInputLangChange(0, 0);
- // Marks that window as supporting mouse-wheel messages rerouting so it is
- // scrolled when under the mouse pointer even if inactive.
- props_.push_back(ui::SetWindowSupportsRerouteMouseWheel(m_hWnd));
-
- WTSRegisterSessionNotification(m_hWnd, NOTIFY_FOR_THIS_SESSION);
-
- UpdateDesiredTouchMode();
- UpdateIMEState();
-
- return 0;
-}
-
-void RenderWidgetHostViewWin::OnActivate(UINT action, BOOL minimized,
- HWND window) {
- TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnActivate");
- // If the container is a popup, clicking elsewhere on screen should close the
- // popup.
- if (close_on_deactivate_ && action == WA_INACTIVE) {
- // Send a windows message so that any derived classes
- // will get a change to override the default handling
- SendMessage(WM_CANCELMODE);
- }
-}
-
-void RenderWidgetHostViewWin::OnDestroy() {
- TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnDestroy");
- DetachPluginsHelper(m_hWnd);
-
- props_.clear();
-
- if (base::win::GetVersion() >= base::win::VERSION_WIN7 &&
- IsTouchWindow(m_hWnd, NULL)) {
- UnregisterTouchWindow(m_hWnd);
- }
-
- CleanupCompositorWindow();
-
- WTSUnRegisterSessionNotification(m_hWnd);
-
- ResetTooltip();
- TrackMouseLeave(false);
-}
-
-void RenderWidgetHostViewWin::OnPaint(HDC unused_dc) {
- TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnPaint");
-
- // Grab the region to paint before creation of paint_dc since it clears the
- // damage region.
- base::win::ScopedGDIObject<HRGN> damage_region(CreateRectRgn(0, 0, 0, 0));
- GetUpdateRgn(damage_region, FALSE);
-
- CPaintDC paint_dc(m_hWnd);
-
- if (!render_widget_host_)
- return;
-
- DCHECK(render_widget_host_->GetProcess()->HasConnection());
-
- // If the GPU process is rendering to a child window, compositing is
- // already triggered by damage to compositor_host_window_, so all we need to
- // do here is clear borders during resize.
- if (compositor_host_window_ &&
- render_widget_host_->is_accelerated_compositing_active()) {
- RECT host_rect, child_rect;
- GetClientRect(&host_rect);
- if (::GetClientRect(compositor_host_window_, &child_rect) &&
- (child_rect.right < host_rect.right ||
- child_rect.bottom < host_rect.bottom)) {
- paint_dc.FillRect(&host_rect,
- reinterpret_cast<HBRUSH>(GetStockObject(WHITE_BRUSH)));
- }
- return;
- }
-
- if (accelerated_surface_.get() &&
- render_widget_host_->is_accelerated_compositing_active()) {
- AcceleratedPaint(paint_dc.m_hDC);
- return;
- }
-
- about_to_validate_and_paint_ = true;
- BackingStoreWin* backing_store = static_cast<BackingStoreWin*>(
- render_widget_host_->GetBackingStore(true));
-
- // We initialize |paint_dc| (and thus call BeginPaint()) after calling
- // GetBackingStore(), so that if it updates the invalid rect we'll catch the
- // changes and repaint them.
- about_to_validate_and_paint_ = false;
-
- if (compositor_host_window_ && hide_compositor_window_at_next_paint_) {
- ::ShowWindow(compositor_host_window_, SW_HIDE);
- hide_compositor_window_at_next_paint_ = false;
- }
-
- gfx::Rect damaged_rect(paint_dc.m_ps.rcPaint);
- if (damaged_rect.IsEmpty())
- return;
-
- if (backing_store) {
- gfx::Rect bitmap_rect(gfx::Point(),
- gfx::win::DIPToScreenSize(backing_store->size()));
-
- bool manage_colors = BackingStoreWin::ColorManagementEnabled();
- if (manage_colors)
- SetICMMode(paint_dc.m_hDC, ICM_ON);
-
- // Blit only the damaged regions from the backing store.
- DWORD data_size = GetRegionData(damage_region, 0, NULL);
- scoped_ptr<char[]> region_data_buf;
- RGNDATA* region_data = NULL;
- RECT* region_rects = NULL;
-
- if (data_size) {
- region_data_buf.reset(new char[data_size]);
- region_data = reinterpret_cast<RGNDATA*>(region_data_buf.get());
- region_rects = reinterpret_cast<RECT*>(region_data->Buffer);
- data_size = GetRegionData(damage_region, data_size, region_data);
- }
-
- if (!data_size) {
- // Grabbing the damaged regions failed, fake with the whole rect.
- data_size = sizeof(RGNDATAHEADER) + sizeof(RECT);
- region_data_buf.reset(new char[data_size]);
- region_data = reinterpret_cast<RGNDATA*>(region_data_buf.get());
- region_rects = reinterpret_cast<RECT*>(region_data->Buffer);
- region_data->rdh.nCount = 1;
- region_rects[0] = damaged_rect.ToRECT();
- }
-
- for (DWORD i = 0; i < region_data->rdh.nCount; ++i) {
- gfx::Rect paint_rect =
- gfx::IntersectRects(bitmap_rect, gfx::Rect(region_rects[i]));
- if (!paint_rect.IsEmpty()) {
- BitBlt(paint_dc.m_hDC,
- paint_rect.x(),
- paint_rect.y(),
- paint_rect.width(),
- paint_rect.height(),
- backing_store->hdc(),
- paint_rect.x(),
- paint_rect.y(),
- SRCCOPY);
- }
- }
-
- if (manage_colors)
- SetICMMode(paint_dc.m_hDC, ICM_OFF);
-
- // Fill the remaining portion of the damaged_rect with the background
- if (damaged_rect.right() > bitmap_rect.right()) {
- RECT r;
- r.left = std::max(bitmap_rect.right(), damaged_rect.x());
- r.right = damaged_rect.right();
- r.top = damaged_rect.y();
- r.bottom = std::min(bitmap_rect.bottom(), damaged_rect.bottom());
- DrawBackground(r, &paint_dc);
- }
- if (damaged_rect.bottom() > bitmap_rect.bottom()) {
- RECT r;
- r.left = damaged_rect.x();
- r.right = damaged_rect.right();
- r.top = std::max(bitmap_rect.bottom(), damaged_rect.y());
- r.bottom = damaged_rect.bottom();
- DrawBackground(r, &paint_dc);
- }
- if (!whiteout_start_time_.is_null()) {
- TimeDelta whiteout_duration = TimeTicks::Now() - whiteout_start_time_;
- UMA_HISTOGRAM_TIMES("MPArch.RWHH_WhiteoutDuration", whiteout_duration);
-
- // Reset the start time to 0 so that we start recording again the next
- // time the backing store is NULL...
- whiteout_start_time_ = TimeTicks();
- }
- if (!web_contents_switch_paint_time_.is_null()) {
- TimeDelta web_contents_switch_paint_duration = TimeTicks::Now() -
- web_contents_switch_paint_time_;
- UMA_HISTOGRAM_TIMES("MPArch.RWH_TabSwitchPaintDuration",
- web_contents_switch_paint_duration);
- // Reset contents_switch_paint_time_ to 0 so future tab selections are
- // recorded.
- web_contents_switch_paint_time_ = TimeTicks();
- }
-
- software_latency_info_.AddLatencyNumber(
- ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0);
- render_widget_host_->FrameSwapped(software_latency_info_);
- software_latency_info_.Clear();
- } else {
- DrawBackground(paint_dc.m_ps.rcPaint, &paint_dc);
- if (whiteout_start_time_.is_null())
- whiteout_start_time_ = TimeTicks::Now();
- }
-}
-
-void RenderWidgetHostViewWin::DrawBackground(const RECT& dirty_rect,
- CPaintDC* dc) {
- if (!background_.empty()) {
- gfx::Rect dirty_area(dirty_rect);
- gfx::Canvas canvas(dirty_area.size(), 1.0f, true);
- canvas.Translate(-dirty_area.OffsetFromOrigin());
-
- gfx::Rect dc_rect(dc->m_ps.rcPaint);
- // TODO(pkotwicz): Fix |background_| such that it is an ImageSkia.
- canvas.TileImageInt(gfx::ImageSkia::CreateFrom1xBitmap(background_),
- 0, 0, dc_rect.width(), dc_rect.height());
-
- skia::DrawToNativeContext(canvas.sk_canvas(), *dc, dirty_area.x(),
- dirty_area.y(), NULL);
- } else {
- HBRUSH white_brush = reinterpret_cast<HBRUSH>(GetStockObject(WHITE_BRUSH));
- dc->FillRect(&dirty_rect, white_brush);
- }
-}
-
-void RenderWidgetHostViewWin::OnNCPaint(HRGN update_region) {
- TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnNCPaint");
- // Do nothing. This suppresses the resize corner that Windows would
- // otherwise draw for us.
-}
-
-void RenderWidgetHostViewWin::SetClickthroughRegion(SkRegion* region) {
- transparent_region_.reset(region);
-}
-
-LRESULT RenderWidgetHostViewWin::OnNCHitTest(const CPoint& point) {
- TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnNCHitTest");
- RECT rc;
- GetWindowRect(&rc);
- if (transparent_region_.get() &&
- transparent_region_->contains(point.x - rc.left, point.y - rc.top)) {
- SetMsgHandled(TRUE);
- return HTTRANSPARENT;
- }
- SetMsgHandled(FALSE);
- return 0;
-}
-
-LRESULT RenderWidgetHostViewWin::OnEraseBkgnd(HDC dc) {
- TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnEraseBkgnd");
- return 1;
-}
-
-LRESULT RenderWidgetHostViewWin::OnSetCursor(HWND window, UINT hittest_code,
- UINT mouse_message_id) {
- TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnSetCursor");
- UpdateCursorIfOverSelf();
- return 0;
-}
-
-void RenderWidgetHostViewWin::OnSetFocus(HWND window) {
- TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnSetFocus");
- if (!render_widget_host_)
- return;
-
- if (GetBrowserAccessibilityManager())
- GetBrowserAccessibilityManager()->GotFocus(pointer_down_context_);
-
- render_widget_host_->GotFocus();
- render_widget_host_->SetActive(true);
-
- if (base::win::IsTSFAwareRequired())
- ui::TSFBridge::GetInstance()->SetFocusedClient(m_hWnd, this);
-}
-
-void RenderWidgetHostViewWin::OnKillFocus(HWND window) {
- TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnKillFocus");
- if (!render_widget_host_)
- return;
-
- render_widget_host_->SetActive(false);
- render_widget_host_->Blur();
-
- last_touch_location_ = gfx::Point(-1, -1);
-
- if (base::win::IsTSFAwareRequired())
- ui::TSFBridge::GetInstance()->RemoveFocusedClient(this);
-}
-
-void RenderWidgetHostViewWin::OnCaptureChanged(HWND window) {
- TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnCaptureChanged");
- if (render_widget_host_)
- render_widget_host_->LostCapture();
- pointer_down_context_ = false;
-}
-
-void RenderWidgetHostViewWin::OnCancelMode() {
- TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnCancelMode");
- if (render_widget_host_)
- render_widget_host_->LostCapture();
-
- if ((is_fullscreen_ || close_on_deactivate_) &&
- !weak_factory_.HasWeakPtrs()) {
- // Dismiss popups and menus. We do this asynchronously to avoid changing
- // activation within this callstack, which may interfere with another window
- // being activated. We can synchronously hide the window, but we need to
- // not change activation while doing so.
- SetWindowPos(NULL, 0, 0, 0, 0,
- SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE |
- SWP_NOREPOSITION | SWP_NOSIZE | SWP_NOZORDER);
- base::MessageLoop::current()->PostTask(
- FROM_HERE,
- base::Bind(&RenderWidgetHostViewWin::ShutdownHost,
- weak_factory_.GetWeakPtr()));
- }
-}
-
-void RenderWidgetHostViewWin::OnInputLangChange(DWORD character_set,
- HKL input_language_id) {
- TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnInputLangChange");
- // Send the given Locale ID to the IMM32Manager object and retrieves whether
- // or not the current input context has IMEs.
- // If the current input context has IMEs, a browser process has to send a
- // request to a renderer process that it needs status messages about
- // the focused edit control from the renderer process.
- // On the other hand, if the current input context does not have IMEs, the
- // browser process also has to send a request to the renderer process that
- // it does not need the status messages any longer.
- // To minimize the number of this notification request, we should check if
- // the browser process is actually retrieving the status messages (this
- // state is stored in ime_notification_) and send a request only if the
- // browser process has to update this status, its details are listed below:
- // * If a browser process is not retrieving the status messages,
- // (i.e. ime_notification_ == false),
- // send this request only if the input context does have IMEs,
- // (i.e. ime_status == true);
- // When it successfully sends the request, toggle its notification status,
- // (i.e.ime_notification_ = !ime_notification_ = true).
- // * If a browser process is retrieving the status messages
- // (i.e. ime_notification_ == true),
- // send this request only if the input context does not have IMEs,
- // (i.e. ime_status == false).
- // When it successfully sends the request, toggle its notification status,
- // (i.e.ime_notification_ = !ime_notification_ = false).
- // To analyze the above actions, we can optimize them into the ones
- // listed below:
- // 1 Sending a request only if ime_status_ != ime_notification_, and;
- // 2 Copying ime_status to ime_notification_ if it sends the request
- // successfully (because Action 1 shows ime_status = !ime_notification_.)
- bool ime_status = imm32_manager_->SetInputLanguage();
- if (ime_status != ime_notification_) {
- if (render_widget_host_) {
- render_widget_host_->SetInputMethodActive(ime_status);
- ime_notification_ = ime_status;
- }
- }
- // Call DefWindowProc() for consistency with other Chrome windows.
- // TODO(hbono): This is a speculative fix for Bug 36354 and this code may be
- // reverted if it does not fix it.
- SetMsgHandled(FALSE);
-}
-
-void RenderWidgetHostViewWin::OnThemeChanged() {
- TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnThemeChanged");
- if (!render_widget_host_)
- return;
- render_widget_host_->Send(new ViewMsg_ThemeChanged(
- render_widget_host_->GetRoutingID()));
-}
-
-LRESULT RenderWidgetHostViewWin::OnNotify(int w_param, NMHDR* header) {
- TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnNotify");
- if (tooltip_hwnd_ == NULL)
- return 0;
-
- switch (header->code) {
- case TTN_GETDISPINFO: {
- NMTTDISPINFOW* tooltip_info = reinterpret_cast<NMTTDISPINFOW*>(header);
- tooltip_info->szText[0] = L'\0';
- tooltip_info->lpszText = const_cast<WCHAR*>(tooltip_text_.c_str());
- ::SendMessage(
- tooltip_hwnd_, TTM_SETMAXTIPWIDTH, 0, kTooltipMaxWidthPixels);
- SetMsgHandled(TRUE);
- break;
- }
- case TTN_POP:
- tooltip_showing_ = false;
- SetMsgHandled(TRUE);
- break;
- case TTN_SHOW:
- // Tooltip shouldn't be shown when the mouse is locked.
- DCHECK(!mouse_locked_);
- tooltip_showing_ = true;
- SetMsgHandled(TRUE);
- break;
- }
- return 0;
-}
-
-LRESULT RenderWidgetHostViewWin::OnImeSetContext(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) {
- TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnImeSetContext");
- if (!render_widget_host_)
- return 0;
-
- // We need status messages about the focused input control from a
- // renderer process when:
- // * the current input context has IMEs, and;
- // * an application is activated.
- // This seems to tell we should also check if the current input context has
- // IMEs before sending a request, however, this WM_IME_SETCONTEXT is
- // fortunately sent to an application only while the input context has IMEs.
- // Therefore, we just start/stop status messages according to the activation
- // status of this application without checks.
- bool activated = (wparam == TRUE);
- if (render_widget_host_) {
- render_widget_host_->SetInputMethodActive(activated);
- ime_notification_ = activated;
- }
-
- if (ime_notification_)
- imm32_manager_->CreateImeWindow(m_hWnd);
-
- imm32_manager_->CleanupComposition(m_hWnd);
- return imm32_manager_->SetImeWindowStyle(
- m_hWnd, message, wparam, lparam, &handled);
-}
-
-LRESULT RenderWidgetHostViewWin::OnImeStartComposition(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) {
- TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnImeStartComposition");
- if (!render_widget_host_)
- return 0;
-
- // Reset the composition status and create IME windows.
- imm32_manager_->CreateImeWindow(m_hWnd);
- imm32_manager_->ResetComposition(m_hWnd);
- // When the focus is on an element that does not draw composition by itself
- // (i.e., PPAPI plugin not handling IME), let IME to draw the text. Otherwise
- // we have to prevent WTL from calling ::DefWindowProc() because the function
- // calls ::ImmSetCompositionWindow() and ::ImmSetCandidateWindow() to
- // over-write the position of IME windows.
- handled = (can_compose_inline_ ? TRUE : FALSE);
- return 0;
-}
-
-LRESULT RenderWidgetHostViewWin::OnImeComposition(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) {
- TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnImeComposition");
- if (!render_widget_host_)
- return 0;
-
- // At first, update the position of the IME window.
- imm32_manager_->UpdateImeWindow(m_hWnd);
-
- // ui::CompositionUnderline should be identical to
- // blink::WebCompositionUnderline, so that we can do reinterpret_cast safely.
- COMPILE_ASSERT(sizeof(ui::CompositionUnderline) ==
- sizeof(blink::WebCompositionUnderline),
- ui_CompositionUnderline__WebKit_WebCompositionUnderline_diff);
-
- // Retrieve the result string and its attributes of the ongoing composition
- // and send it to a renderer process.
- ui::CompositionText composition;
- if (imm32_manager_->GetResult(m_hWnd, lparam, &composition.text)) {
- render_widget_host_->ImeConfirmComposition(
- composition.text, gfx::Range::InvalidRange(), false);
- imm32_manager_->ResetComposition(m_hWnd);
- // Fall though and try reading the composition string.
- // Japanese IMEs send a message containing both GCS_RESULTSTR and
- // GCS_COMPSTR, which means an ongoing composition has been finished
- // by the start of another composition.
- }
- // Retrieve the composition string and its attributes of the ongoing
- // composition and send it to a renderer process.
- if (imm32_manager_->GetComposition(m_hWnd, lparam, &composition)) {
- // TODO(suzhe): due to a bug of webkit, we can't use selection range with
- // composition string. See: https://bugs.webkit.org/show_bug.cgi?id=37788
- composition.selection = gfx::Range(composition.selection.end());
-
- // TODO(suzhe): convert both renderer_host and renderer to use
- // ui::CompositionText.
- const std::vector<blink::WebCompositionUnderline>& underlines =
- reinterpret_cast<const std::vector<blink::WebCompositionUnderline>&>(
- composition.underlines);
- render_widget_host_->ImeSetComposition(
- composition.text, underlines,
- composition.selection.start(), composition.selection.end());
- }
- // We have to prevent WTL from calling ::DefWindowProc() because we do not
- // want for the IMM (Input Method Manager) to send WM_IME_CHAR messages.
- handled = TRUE;
- if (!can_compose_inline_) {
- // When the focus is on an element that does not draw composition by itself
- // (i.e., PPAPI plugin not handling IME), let IME to draw the text, which
- // is the default behavior of DefWindowProc. Note, however, even in this
- // case we don't want GCS_RESULTSTR to be converted to WM_IME_CHAR messages.
- // Thus we explicitly drop the flag.
- return ::DefWindowProc(m_hWnd, message, wparam, lparam & ~GCS_RESULTSTR);
- }
- return 0;
-}
-
-LRESULT RenderWidgetHostViewWin::OnImeEndComposition(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) {
- TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnImeEndComposition");
- if (!render_widget_host_)
- return 0;
-
- if (imm32_manager_->is_composing()) {
- // A composition has been ended while there is an ongoing composition,
- // i.e. the ongoing composition has been canceled.
- // We need to reset the composition status both of the IMM32Manager object
- // and of the renderer process.
- render_widget_host_->ImeCancelComposition();
- imm32_manager_->ResetComposition(m_hWnd);
- }
- imm32_manager_->DestroyImeWindow(m_hWnd);
- // Let WTL call ::DefWindowProc() and release its resources.
- handled = FALSE;
- return 0;
-}
-
-LRESULT RenderWidgetHostViewWin::OnImeRequest(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) {
- TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnImeRequest");
- if (!render_widget_host_) {
- handled = FALSE;
- return 0;
- }
-
- // Should not receive WM_IME_REQUEST message, if IME is disabled.
- if (text_input_type_ == ui::TEXT_INPUT_TYPE_NONE ||
- text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD) {
- handled = FALSE;
- return 0;
- }
-
- switch (wparam) {
- case IMR_RECONVERTSTRING:
- return OnReconvertString(reinterpret_cast<RECONVERTSTRING*>(lparam));
- case IMR_DOCUMENTFEED:
- return OnDocumentFeed(reinterpret_cast<RECONVERTSTRING*>(lparam));
- case IMR_QUERYCHARPOSITION:
- return OnQueryCharPosition(reinterpret_cast<IMECHARPOSITION*>(lparam));
- default:
- handled = FALSE;
- return 0;
- }
-}
-
-LRESULT RenderWidgetHostViewWin::OnMouseEvent(UINT message, WPARAM wparam,
- LPARAM lparam, BOOL& handled) {
- TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnMouseEvent");
- handled = TRUE;
-
- if (message == WM_MOUSELEAVE)
- ignore_mouse_movement_ = true;
-
- if (mouse_locked_) {
- HandleLockedMouseEvent(message, wparam, lparam);
- MoveCursorToCenterIfNecessary();
- return 0;
- }
-
- if (::IsWindow(tooltip_hwnd_)) {
- // Forward mouse events through to the tooltip window
- MSG msg;
- msg.hwnd = m_hWnd;
- msg.message = message;
- msg.wParam = wparam;
- msg.lParam = lparam;
- SendMessage(tooltip_hwnd_, TTM_RELAYEVENT, NULL,
- reinterpret_cast<LPARAM>(&msg));
- }
-
- // Due to a bug in Windows, the simulated mouse events for a touch event
- // outside our bounds are delivered to us if we were previously focused
- // causing crbug.com/159982. As a workaround, we check if this event is a
- // simulated mouse event outside our bounds, and if so, we send it to the
- // right window.
- if ((message == WM_LBUTTONDOWN || message == WM_LBUTTONUP) &&
- ui::IsMouseEventFromTouch(message)) {
- CPoint cursor_pos(GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam));
- ClientToScreen(&cursor_pos);
- if (!GetPixelBounds().Contains(cursor_pos.x, cursor_pos.y)) {
- HWND window = WindowFromPoint(cursor_pos);
- if (window) {
- LRESULT nc_hit_result = SendMessage(window, WM_NCHITTEST, 0,
- MAKELPARAM(cursor_pos.x, cursor_pos.y));
- const bool in_client_area = (nc_hit_result == HTCLIENT);
- int event_type;
- if (message == WM_LBUTTONDOWN)
- event_type = in_client_area ? WM_LBUTTONDOWN : WM_NCLBUTTONDOWN;
- else
- event_type = in_client_area ? WM_LBUTTONUP : WM_NCLBUTTONUP;
-
- // Convert the coordinates to the target window.
- RECT window_bounds;
- ::GetWindowRect(window, &window_bounds);
- int window_x = cursor_pos.x - window_bounds.left;
- int window_y = cursor_pos.y - window_bounds.top;
- if (in_client_area) {
- ::PostMessage(window, event_type, wparam,
- MAKELPARAM(window_x, window_y));
- } else {
- ::PostMessage(window, event_type, nc_hit_result,
- MAKELPARAM(cursor_pos.x, cursor_pos.y));
- }
- return 0;
- }
- }
- }
-
- // TODO(jcampan): I am not sure if we should forward the message to the
- // WebContentsImpl first in the case of popups. If we do, we would need to
- // convert the click from the popup window coordinates to the WebContentsImpl'
- // window coordinates. For now we don't forward the message in that case to
- // address bug #907474.
- // Note: GetParent() on popup windows returns the top window and not the
- // parent the window was created with (the parent and the owner of the popup
- // is the first non-child view of the view that was specified to the create
- // call). So the WebContentsImpl's window would have to be specified to the
- // RenderViewHostHWND as there is no way to retrieve it from the HWND.
-
- // Don't forward if the container is a popup or fullscreen widget.
- if (!is_fullscreen_ && !close_on_deactivate_) {
- switch (message) {
- case WM_LBUTTONDOWN:
- case WM_MBUTTONDOWN:
- case WM_RBUTTONDOWN:
- // Finish the ongoing composition whenever a mouse click happens.
- // It matches IE's behavior.
- if (base::win::IsTSFAwareRequired()) {
- ui::TSFBridge::GetInstance()->CancelComposition();
- } else {
- imm32_manager_->CleanupComposition(m_hWnd);
- }
- // Fall through.
- case WM_MOUSEMOVE:
- case WM_MOUSELEAVE: {
- // Give the WebContentsImpl first crack at the message. It may want to
- // prevent forwarding to the renderer if some higher level browser
- // functionality is invoked.
- LPARAM parent_msg_lparam = lparam;
- if (message != WM_MOUSELEAVE) {
- // For the messages except WM_MOUSELEAVE, before forwarding them to
- // parent window, we should adjust cursor position from client
- // coordinates in current window to client coordinates in its parent
- // window.
- CPoint cursor_pos(GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam));
- ClientToScreen(&cursor_pos);
- GetParent().ScreenToClient(&cursor_pos);
- parent_msg_lparam = MAKELPARAM(cursor_pos.x, cursor_pos.y);
- }
- if (SendMessage(GetParent(), message, wparam, parent_msg_lparam) != 0) {
- TRACE_EVENT0("browser", "EarlyOut_SentToParent");
- return 1;
- }
- }
- }
- }
-
- if (message == WM_LBUTTONDOWN && pointer_down_context_ &&
- GetBrowserAccessibilityManager()) {
- GetBrowserAccessibilityManager()->GotMouseDown();
- }
-
- if (message == WM_LBUTTONUP && ui::IsMouseEventFromTouch(message) &&
- base::win::IsMetroProcess())
- pointer_down_context_ = false;
-
- ForwardMouseEventToRenderer(message, wparam, lparam);
- return 0;
-}
-
-LRESULT RenderWidgetHostViewWin::OnKeyEvent(UINT message, WPARAM wparam,
- LPARAM lparam, BOOL& handled) {
- TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnKeyEvent");
- handled = TRUE;
-
- // When Escape is pressed, force fullscreen windows to close if necessary.
- if ((message == WM_KEYDOWN || message == WM_KEYUP) && wparam == VK_ESCAPE) {
- if (is_fullscreen_) {
- SendMessage(WM_CANCELMODE);
- return 0;
- }
- }
-
- // If we are a pop-up, forward tab related messages to our parent HWND, so
- // that we are dismissed appropriately and so that the focus advance in our
- // parent.
- // TODO(jcampan): http://b/issue?id=1192881 Could be abstracted in the
- // FocusManager.
- if (close_on_deactivate_ &&
- (((message == WM_KEYDOWN || message == WM_KEYUP) && (wparam == VK_TAB)) ||
- (message == WM_CHAR && wparam == L'\t'))) {
- // First close the pop-up.
- SendMessage(WM_CANCELMODE);
- // Then move the focus by forwarding the tab key to the parent.
- return ::SendMessage(GetParent(), message, wparam, lparam);
- }
-
- if (!render_widget_host_)
- return 0;
-
- // Bug 1845: we need to update the text direction when a user releases
- // either a right-shift key or a right-control key after pressing both of
- // them. So, we just update the text direction while a user is pressing the
- // keys, and we notify the text direction when a user releases either of them.
- // Bug 9718: http://crbug.com/9718 To investigate IE and notepad, this
- // shortcut is enabled only on a PC having RTL keyboard layouts installed.
- // We should emulate them.
- if (ui::IMM32Manager::IsRTLKeyboardLayoutInstalled()) {
- if (message == WM_KEYDOWN) {
- if (wparam == VK_SHIFT) {
- base::i18n::TextDirection dir;
- if (ui::IMM32Manager::IsCtrlShiftPressed(&dir)) {
- render_widget_host_->UpdateTextDirection(
- dir == base::i18n::RIGHT_TO_LEFT ?
- blink::WebTextDirectionRightToLeft :
- blink::WebTextDirectionLeftToRight);
- }
- } else if (wparam != VK_CONTROL) {
- // Bug 9762: http://crbug.com/9762 A user pressed a key except shift
- // and control keys.
- // When a user presses a key while he/she holds control and shift keys,
- // we cancel sending an IPC message in NotifyTextDirection() below and
- // ignore succeeding UpdateTextDirection() calls while we call
- // NotifyTextDirection().
- // To cancel it, this call set a flag that prevents sending an IPC
- // message in NotifyTextDirection() only if we are going to send it.
- // It is harmless to call this function if we aren't going to send it.
- render_widget_host_->CancelUpdateTextDirection();
- }
- } else if (message == WM_KEYUP &&
- (wparam == VK_SHIFT || wparam == VK_CONTROL)) {
- // We send an IPC message only if we need to update the text direction.
- render_widget_host_->NotifyTextDirection();
- }
- }
-
- // Special processing for enter key: When user hits enter in omnibox
- // we change focus to render host after the navigation, so repeat WM_KEYDOWNs
- // and WM_KEYUP are going to render host, despite being initiated in other
- // window. This code filters out these messages.
- bool ignore_keyboard_event = false;
- if (wparam == VK_RETURN) {
- if (message == WM_KEYDOWN || message == WM_SYSKEYDOWN) {
- if (KF_REPEAT & HIWORD(lparam)) {
- // this is a repeated key
- if (!capture_enter_key_)
- ignore_keyboard_event = true;
- } else {
- capture_enter_key_ = true;
- }
- } else if (message == WM_KEYUP || message == WM_SYSKEYUP) {
- if (!capture_enter_key_)
- ignore_keyboard_event = true;
- capture_enter_key_ = false;
- } else {
- // Ignore all other keyboard events for the enter key if not captured.
- if (!capture_enter_key_)
- ignore_keyboard_event = true;
- }
- }
-
- if (render_widget_host_ && !ignore_keyboard_event) {
- MSG msg = { m_hWnd, message, wparam, lparam };
- render_widget_host_->ForwardKeyboardEvent(NativeWebKeyboardEvent(msg));
- }
-
- return 0;
-}
-
-LRESULT RenderWidgetHostViewWin::OnWheelEvent(UINT message, WPARAM wparam,
- LPARAM lparam, BOOL& handled) {
- TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnWheelEvent");
- // Forward the mouse-wheel message to the window under the mouse if it belongs
- // to us.
- if (message == WM_MOUSEWHEEL &&
- ui::RerouteMouseWheel(m_hWnd, wparam, lparam)) {
- handled = TRUE;
- return 0;
- }
-
- // We get mouse wheel/scroll messages even if we are not in the foreground.
- // So here we check if we have any owned popup windows in the foreground and
- // dismiss them.
- if (m_hWnd != GetForegroundWindow()) {
- HWND toplevel_hwnd = ::GetAncestor(m_hWnd, GA_ROOT);
- EnumThreadWindows(
- GetCurrentThreadId(),
- DismissOwnedPopups,
- reinterpret_cast<LPARAM>(toplevel_hwnd));
- }
-
- if (render_widget_host_) {
- blink::WebMouseWheelEvent wheel_event =
- WebMouseWheelEventBuilder::Build(m_hWnd, message, wparam, lparam);
- float scale = gfx::win::GetDeviceScaleFactor();
- wheel_event.x /= scale;
- wheel_event.y /= scale;
- wheel_event.deltaX /= scale;
- wheel_event.deltaY /= scale;
-
- render_widget_host_->ForwardWheelEvent(wheel_event);
- }
- handled = TRUE;
- return 0;
-}
-
-WebTouchState::WebTouchState(const RenderWidgetHostViewWin* window)
- : window_(window),
- id_generator_(0) {
-}
-
-size_t WebTouchState::UpdateTouchPoints(
- TOUCHINPUT* points, size_t count) {
- // First we reset all touch event state. This involves removing any released
- // touchpoints and marking the rest as stationary. After that we go through
- // and alter/add any touchpoints (from the touch input buffer) that we can
- // coalesce into a single message. The return value is the number of consumed
- // input message.
- blink::WebTouchPoint* point = touch_event_.touches;
- blink::WebTouchPoint* end = point + touch_event_.touchesLength;
- while (point < end) {
- if (point->state == blink::WebTouchPoint::StateReleased) {
- *point = *(--end);
- --touch_event_.touchesLength;
- } else {
- point->state = blink::WebTouchPoint::StateStationary;
- point++;
- }
- }
- touch_event_.changedTouchesLength = 0;
- touch_event_.modifiers = content::EventFlagsToWebEventModifiers(
- ui::GetModifiersFromKeyState());
-
- // Consume all events of the same type and add them to the changed list.
- int last_type = 0;
- for (size_t i = 0; i < count; ++i) {
- unsigned int mapped_id = GetMappedTouch(points[i].dwID);
-
- blink::WebTouchPoint* point = NULL;
- for (unsigned j = 0; j < touch_event_.touchesLength; ++j) {
- if (static_cast<DWORD>(touch_event_.touches[j].id) == mapped_id) {
- point = &touch_event_.touches[j];
- break;
- }
- }
-
- // Use a move instead if we see a down on a point we already have.
- int type = GetTouchType(points[i]);
- if (point && type == TOUCHEVENTF_DOWN)
- SetTouchType(&points[i], TOUCHEVENTF_MOVE);
-
- // Stop processing when the event type changes.
- if (touch_event_.changedTouchesLength && type != last_type)
- return i;
-
- touch_event_.timeStampSeconds = points[i].dwTime / 1000.0;
-
- last_type = type;
- switch (type) {
- case TOUCHEVENTF_DOWN: {
- if (!(point = AddTouchPoint(&points[i])))
- continue;
- touch_event_.type = blink::WebInputEvent::TouchStart;
- break;
- }
-
- case TOUCHEVENTF_UP: {
- if (!point) // Just throw away a stray up.
- continue;
- point->state = blink::WebTouchPoint::StateReleased;
- UpdateTouchPoint(point, &points[i]);
- touch_event_.type = blink::WebInputEvent::TouchEnd;
- break;
- }
-
- case TOUCHEVENTF_MOVE: {
- if (point) {
- point->state = blink::WebTouchPoint::StateMoved;
- // Don't update the message if the point didn't really move.
- if (UpdateTouchPoint(point, &points[i]))
- continue;
- touch_event_.type = blink::WebInputEvent::TouchMove;
- } else if (touch_event_.changedTouchesLength) {
- RemoveExpiredMappings();
- // Can't add a point if we're already handling move events.
- return i;
- } else {
- // Treat a move with no existing point as a down.
- if (!(point = AddTouchPoint(&points[i])))
- continue;
- last_type = TOUCHEVENTF_DOWN;
- SetTouchType(&points[i], TOUCHEVENTF_DOWN);
- touch_event_.type = blink::WebInputEvent::TouchStart;
- }
- break;
- }
-
- default:
- NOTREACHED();
- continue;
- }
- touch_event_.changedTouches[touch_event_.changedTouchesLength++] = *point;
- }
-
- RemoveExpiredMappings();
- return count;
-}
-
-void WebTouchState::RemoveExpiredMappings() {
- blink::WebTouchPoint* point = touch_event_.touches;
- blink::WebTouchPoint* end = point + touch_event_.touchesLength;
- for (; point < end; ++point) {
- if (point->state == blink::WebTouchPoint::StateReleased)
- id_generator_.ReleaseGeneratedID(point->id);
- }
-}
-
-
-bool WebTouchState::ReleaseTouchPoints() {
- if (touch_event_.touchesLength == 0)
- return false;
- // Mark every active touchpoint as released.
- touch_event_.type = blink::WebInputEvent::TouchEnd;
- touch_event_.changedTouchesLength = touch_event_.touchesLength;
- for (unsigned int i = 0; i < touch_event_.touchesLength; ++i) {
- touch_event_.touches[i].state = blink::WebTouchPoint::StateReleased;
- touch_event_.changedTouches[i].state =
- blink::WebTouchPoint::StateReleased;
- }
-
- return true;
-}
-
-blink::WebTouchPoint* WebTouchState::AddTouchPoint(
- TOUCHINPUT* touch_input) {
- DCHECK(touch_event_.touchesLength <
- blink::WebTouchEvent::touchesLengthCap);
- if (touch_event_.touchesLength >=
- blink::WebTouchEvent::touchesLengthCap)
- return NULL;
- blink::WebTouchPoint* point =
- &touch_event_.touches[touch_event_.touchesLength++];
- point->state = blink::WebTouchPoint::StatePressed;
- point->id = GetMappedTouch(touch_input->dwID);
- UpdateTouchPoint(point, touch_input);
- return point;
-}
-
-bool WebTouchState::UpdateTouchPoint(
- blink::WebTouchPoint* touch_point,
- TOUCHINPUT* touch_input) {
- CPoint coordinates(
- TOUCH_COORD_TO_PIXEL(touch_input->x) /
- gfx::win::GetUndocumentedDPITouchScale(),
- TOUCH_COORD_TO_PIXEL(touch_input->y) /
- gfx::win::GetUndocumentedDPITouchScale());
- int radius_x = 1;
- int radius_y = 1;
- if (touch_input->dwMask & TOUCHINPUTMASKF_CONTACTAREA) {
- // Some touch drivers send a contact area of "-1", yet flag it as valid.
- radius_x = std::max(1,
- static_cast<int>(TOUCH_COORD_TO_PIXEL(touch_input->cxContact) /
- gfx::win::GetUndocumentedDPITouchScale()));
- radius_y = std::max(1,
- static_cast<int>(TOUCH_COORD_TO_PIXEL(touch_input->cyContact) /
- gfx::win::GetUndocumentedDPITouchScale()));
- }
-
- // Detect and exclude stationary moves.
- if (GetTouchType(*touch_input) == TOUCHEVENTF_MOVE &&
- touch_point->screenPosition.x == coordinates.x &&
- touch_point->screenPosition.y == coordinates.y &&
- touch_point->radiusX == radius_x &&
- touch_point->radiusY == radius_y) {
- touch_point->state = blink::WebTouchPoint::StateStationary;
- return true;
- }
-
- touch_point->screenPosition.x = coordinates.x;
- touch_point->screenPosition.y = coordinates.y;
- window_->ScreenToClient(&coordinates);
- static float scale = gfx::win::GetDeviceScaleFactor();
- touch_point->position.x = coordinates.x / scale;
- touch_point->position.y = coordinates.y / scale;
- touch_point->radiusX = radius_x;
- touch_point->radiusY = radius_y;
- touch_point->force = 0;
- touch_point->rotationAngle = 0;
- return false;
-}
-
-// Find (or create) a mapping for _os_touch_id_.
-unsigned int WebTouchState::GetMappedTouch(unsigned int os_touch_id) {
- return id_generator_.GetGeneratedID(os_touch_id);
-}
-
-LRESULT RenderWidgetHostViewWin::OnTouchEvent(UINT message, WPARAM wparam,
- LPARAM lparam, BOOL& handled) {
- TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnTouchEvent");
- // Finish the ongoing composition whenever a touch event happens.
- // It matches IE's behavior.
- if (base::win::IsTSFAwareRequired()) {
- ui::TSFBridge::GetInstance()->CancelComposition();
- } else {
- imm32_manager_->CleanupComposition(m_hWnd);
- }
-
- // TODO(jschuh): Add support for an arbitrary number of touchpoints.
- size_t total = std::min(static_cast<int>(LOWORD(wparam)),
- static_cast<int>(blink::WebTouchEvent::touchesLengthCap));
- TOUCHINPUT points[blink::WebTouchEvent::touchesLengthCap];
-
- if (!total || !ui::GetTouchInputInfoWrapper((HTOUCHINPUT)lparam, total,
- points, sizeof(TOUCHINPUT))) {
- TRACE_EVENT0("browser", "EarlyOut_NothingToDo");
- return 0;
- }
-
- if (total == 1 && (points[0].dwFlags & TOUCHEVENTF_DOWN)) {
- pointer_down_context_ = true;
- last_touch_location_ = gfx::Point(
- TOUCH_COORD_TO_PIXEL(points[0].x) /
- gfx::win::GetUndocumentedDPITouchScale(),
- TOUCH_COORD_TO_PIXEL(points[0].y) /
- gfx::win::GetUndocumentedDPITouchScale());
- }
-
- bool should_forward = render_widget_host_->ShouldForwardTouchEvent() &&
- touch_events_enabled_;
-
- // Send a copy of the touch events on to the gesture recognizer.
- for (size_t start = 0; start < total;) {
- start += touch_state_->UpdateTouchPoints(points + start, total - start);
- if (should_forward) {
- if (touch_state_->is_changed())
- render_widget_host_->ForwardTouchEventWithLatencyInfo(
- touch_state_->touch_event(), ui::LatencyInfo());
- } else {
- const blink::WebTouchEvent& touch_event = touch_state_->touch_event();
- base::TimeDelta timestamp = base::TimeDelta::FromMilliseconds(
- touch_event.timeStampSeconds * 1000);
- for (size_t i = 0; i < touch_event.touchesLength; ++i) {
- scoped_ptr<ui::GestureRecognizer::Gestures> gestures;
- gestures.reset(gesture_recognizer_->ProcessTouchEventForGesture(
- TouchEventFromWebTouchPoint(touch_event.touches[i], timestamp),
- ui::ER_UNHANDLED, this));
- ProcessGestures(gestures.get());
- }
- }
- }
-
- CloseTouchInputHandle((HTOUCHINPUT)lparam);
-
- return 0;
-}
-
-void RenderWidgetHostViewWin::ProcessGestures(
- ui::GestureRecognizer::Gestures* gestures) {
- if ((gestures == NULL) || gestures->empty())
- return;
- for (ui::GestureRecognizer::Gestures::iterator g_it = gestures->begin();
- g_it != gestures->end();
- ++g_it) {
- ForwardGestureEventToRenderer(*g_it);
- }
-}
-
-LRESULT RenderWidgetHostViewWin::OnMouseActivate(UINT message,
- WPARAM wparam,
- LPARAM lparam,
- BOOL& handled) {
- TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnMouseActivate");
- if (!render_widget_host_)
- return MA_NOACTIVATE;
-
- if (!IsActivatable())
- return MA_NOACTIVATE;
-
- HWND focus_window = GetFocus();
- if (!::IsWindow(focus_window) || !IsChild(focus_window)) {
- // We handle WM_MOUSEACTIVATE to set focus to the underlying plugin
- // child window. This is to ensure that keyboard events are received
- // by the plugin. The correct way to fix this would be send over
- // an event to the renderer which would then eventually send over
- // a setFocus call to the plugin widget. This would ensure that
- // the renderer (webkit) knows about the plugin widget receiving
- // focus.
- // TODO(iyengar) Do the right thing as per the above comment.
- POINT cursor_pos = {0};
- ::GetCursorPos(&cursor_pos);
- ::ScreenToClient(m_hWnd, &cursor_pos);
- HWND child_window = ::RealChildWindowFromPoint(m_hWnd, cursor_pos);
- if (::IsWindow(child_window) && child_window != m_hWnd) {
- if (gfx::GetClassName(child_window) == kWrapperNativeWindowClassName)
- child_window = ::GetWindow(child_window, GW_CHILD);
-
- ::SetFocus(child_window);
- return MA_NOACTIVATE;
- }
- }
- handled = FALSE;
- render_widget_host_->OnPointerEventActivate();
- return MA_ACTIVATE;
-}
-
-LRESULT RenderWidgetHostViewWin::OnGestureEvent(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) {
- TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnGestureEvent");
-
- DCHECK(!touch_events_enabled_);
- handled = FALSE;
-
- GESTUREINFO gi = {sizeof(GESTUREINFO)};
- HGESTUREINFO gi_handle = reinterpret_cast<HGESTUREINFO>(lparam);
- if (!::GetGestureInfo(gi_handle, &gi)) {
- DWORD error = GetLastError();
- NOTREACHED() << "Unable to get gesture info. Error : " << error;
- return 0;
- }
-
- if (gi.dwID == GID_ZOOM) {
- PageZoom zoom = PAGE_ZOOM_RESET;
- POINT zoom_center = {0};
- if (DecodeZoomGesture(m_hWnd, gi, &zoom, &zoom_center)) {
- handled = TRUE;
- Send(new ViewMsg_ZoomFactor(render_widget_host_->GetRoutingID(),
- zoom, zoom_center.x, zoom_center.y));
- }
- } else if (gi.dwID == GID_PAN) {
- // Right now we only decode scroll gestures and we forward to the page
- // as scroll events.
- POINT start;
- POINT delta;
- if (DecodeScrollGesture(gi, &start, &delta)) {
- handled = TRUE;
- render_widget_host_->ForwardWheelEvent(
- MakeFakeScrollWheelEvent(m_hWnd, start, delta));
- }
- }
- ::CloseGestureInfoHandle(gi_handle);
- return 0;
-}
-
-LRESULT RenderWidgetHostViewWin::OnMoveOrSize(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) {
- // Reset the cliping rectangle if the mouse is locked.
- if (mouse_locked_) {
- CRect rect;
- GetWindowRect(&rect);
- ::ClipCursor(&rect);
- }
- return 0;
-}
-
-void RenderWidgetHostViewWin::OnAccessibilityEvents(
- const std::vector<AccessibilityHostMsg_EventParams>& params) {
- CreateBrowserAccessibilityManagerIfNeeded();
- GetBrowserAccessibilityManager()->OnAccessibilityEvents(params);
-}
-
-bool RenderWidgetHostViewWin::LockMouse() {
- if (mouse_locked_)
- return true;
-
- mouse_locked_ = true;
-
- // Hide the tooltip window if it is currently visible. When the mouse is
- // locked, no mouse message is relayed to the tooltip window, so we don't need
- // to worry that it will reappear.
- if (::IsWindow(tooltip_hwnd_) && tooltip_showing_) {
- ::SendMessage(tooltip_hwnd_, TTM_POP, 0, 0);
- // Sending a TTM_POP message doesn't seem to actually hide the tooltip
- // window, although we will receive a TTN_POP notification. As a result,
- // ShowWindow() is explicitly called to hide the window.
- ::ShowWindow(tooltip_hwnd_, SW_HIDE);
- }
-
- // TODO(yzshen): ShowCursor(FALSE) causes SetCursorPos() to be ignored on
- // Remote Desktop.
- ::ShowCursor(FALSE);
-
- move_to_center_request_.pending = false;
- last_mouse_position_.locked_global = last_mouse_position_.unlocked_global;
-
- // Must set the clip rectangle before MoveCursorToCenterIfNecessary()
- // so that if the cursor is moved it uses the clip rect set to the window
- // rect. Otherwise, MoveCursorToCenterIfNecessary() may move the cursor
- // to the center of the screen, and then we would clip to the window
- // rect, thus moving the cursor and causing a movement delta.
- CRect rect;
- GetWindowRect(&rect);
- ::ClipCursor(&rect);
- MoveCursorToCenterIfNecessary();
-
- return true;
-}
-
-void RenderWidgetHostViewWin::UnlockMouse() {
- if (!mouse_locked_)
- return;
-
- mouse_locked_ = false;
-
- ::ClipCursor(NULL);
- ::SetCursorPos(last_mouse_position_.unlocked_global.x(),
- last_mouse_position_.unlocked_global.y());
- ::ShowCursor(TRUE);
-
- if (render_widget_host_)
- render_widget_host_->LostMouseLock();
-}
-
-void RenderWidgetHostViewWin::Observe(
- int type,
- const NotificationSource& source,
- const NotificationDetails& details) {
- DCHECK(type == NOTIFICATION_RENDERER_PROCESS_TERMINATED);
-
- // Get the RenderProcessHost that posted this notification, and exit
- // if it's not the one associated with this host view.
- RenderProcessHost* render_process_host =
- Source<RenderProcessHost>(source).ptr();
- DCHECK(render_process_host);
- if (!render_widget_host_ ||
- render_process_host != render_widget_host_->GetProcess()) {
- return;
- }
-
- // If it was our RenderProcessHost that posted the notification,
- // clear the BrowserAccessibilityManager, because the renderer is
- // dead and any accessibility information we have is now stale.
- SetBrowserAccessibilityManager(NULL);
-}
-
-static void PaintCompositorHostWindow(HWND hWnd) {
- PAINTSTRUCT paint;
- BeginPaint(hWnd, &paint);
-
- RenderWidgetHostViewWin* win = static_cast<RenderWidgetHostViewWin*>(
- gfx::GetWindowUserData(hWnd));
- // Trigger composite to rerender window.
- if (win)
- win->AcceleratedPaint(paint.hdc);
-
- EndPaint(hWnd, &paint);
-}
-
-// WndProc for the compositor host window. We use this instead of Default so
-// we can drop WM_PAINT and WM_ERASEBKGD messages on the floor.
-static LRESULT CALLBACK CompositorHostWindowProc(HWND hWnd, UINT message,
- WPARAM wParam, LPARAM lParam) {
- switch (message) {
- case WM_ERASEBKGND:
- return 0;
- case WM_PAINT:
- PaintCompositorHostWindow(hWnd);
- return 0;
- default:
- return DefWindowProc(hWnd, message, wParam, lParam);
- }
-}
-
-void RenderWidgetHostViewWin::AcceleratedPaint(HDC dc) {
- if (render_widget_host_)
- render_widget_host_->ScheduleComposite();
- if (accelerated_surface_)
- accelerated_surface_->Present(dc);
-}
-
-void RenderWidgetHostViewWin::GetScreenInfo(blink::WebScreenInfo* results) {
- GetScreenInfoForWindow(GetNativeViewId(), results);
-}
-
-gfx::Rect RenderWidgetHostViewWin::GetBoundsInRootWindow() {
- RECT window_rect = {0};
- HWND root_window = GetAncestor(m_hWnd, GA_ROOT);
- ::GetWindowRect(root_window, &window_rect);
- gfx::Rect rect(window_rect);
-
- // Maximized windows are outdented from the work area by the frame thickness
- // even though this "frame" is not painted. This confuses code (and people)
- // that think of a maximized window as corresponding exactly to the work area.
- // Correct for this by subtracting the frame thickness back off.
- if (::IsZoomed(root_window)) {
- rect.Inset(GetSystemMetrics(SM_CXSIZEFRAME),
- GetSystemMetrics(SM_CYSIZEFRAME));
- }
-
- return gfx::win::ScreenToDIPRect(rect);
-}
-
-// Creates a HWND within the RenderWidgetHostView that will serve as a host
-// for a HWND that the GPU process will create. The host window is used
-// to Z-position the GPU's window relative to other plugin windows.
-gfx::GLSurfaceHandle RenderWidgetHostViewWin::GetCompositingSurface() {
- // If the window has been created, don't recreate it a second time
- if (compositor_host_window_)
- return gfx::GLSurfaceHandle(compositor_host_window_, gfx::NATIVE_TRANSPORT);
-
- // On Vista and later we present directly to the view window rather than a
- // child window.
- if (GpuDataManagerImpl::GetInstance()->IsUsingAcceleratedSurface()) {
- if (!accelerated_surface_)
- accelerated_surface_.reset(new AcceleratedSurface(m_hWnd));
- return gfx::GLSurfaceHandle(m_hWnd, gfx::NATIVE_TRANSPORT);
- }
-
- // On XP we need a child window that can be resized independently of the
- // parent.
- static ATOM atom = 0;
- static HMODULE instance = NULL;
- if (!atom) {
- WNDCLASSEX window_class;
- base::win::InitializeWindowClass(
- L"CompositorHostWindowClass",
- &base::win::WrappedWindowProc<CompositorHostWindowProc>,
- 0, 0, 0, NULL, NULL, NULL, NULL, NULL,
- &window_class);
- instance = window_class.hInstance;
- atom = RegisterClassEx(&window_class);
- DCHECK(atom);
- }
-
- RECT currentRect;
- GetClientRect(&currentRect);
-
- // Ensure window does not have zero area because D3D cannot create a zero
- // area swap chain.
- int width = std::max(1,
- static_cast<int>(currentRect.right - currentRect.left));
- int height = std::max(1,
- static_cast<int>(currentRect.bottom - currentRect.top));
-
- compositor_host_window_ = CreateWindowEx(
- WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR,
- MAKEINTATOM(atom), 0,
- WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_DISABLED,
- 0, 0, width, height, m_hWnd, 0, instance, 0);
- gfx::CheckWindowCreated(compositor_host_window_);
-
- gfx::SetWindowUserData(compositor_host_window_, this);
-
- gfx::GLSurfaceHandle surface_handle(compositor_host_window_,
- gfx::NATIVE_TRANSPORT);
- return surface_handle;
-}
-
-void RenderWidgetHostViewWin::ResizeCompositingSurface(const gfx::Size& size) {
- // Ensure window does not have zero area because D3D cannot create a zero
- // area swap chain.
- ::SetWindowPos(compositor_host_window_,
- NULL,
- 0, 0,
- std::max(1, size.width()),
- std::max(1, size.height()),
- SWP_NOSENDCHANGING | SWP_NOCOPYBITS | SWP_NOZORDER |
- SWP_NOACTIVATE | SWP_DEFERERASE | SWP_NOMOVE);
-}
-
-void RenderWidgetHostViewWin::OnAcceleratedCompositingStateChange() {
- bool show = render_widget_host_->is_accelerated_compositing_active();
- // When we first create the compositor, we will get a show request from
- // the renderer before we have gotten the create request from the GPU. In this
- // case, simply ignore the show request.
- if (compositor_host_window_ == NULL)
- return;
-
- if (show) {
- ::ShowWindow(compositor_host_window_, SW_SHOW);
-
- // Get all the child windows of this view, including the compositor window.
- std::vector<HWND> all_child_windows;
- ::EnumChildWindows(m_hWnd, AddChildWindowToVector,
- reinterpret_cast<LPARAM>(&all_child_windows));
-
- // Build a list of just the plugin window handles
- std::vector<HWND> plugin_windows;
- bool compositor_host_window_found = false;
- for (size_t i = 0; i < all_child_windows.size(); ++i) {
- if (all_child_windows[i] != compositor_host_window_)
- plugin_windows.push_back(all_child_windows[i]);
- else
- compositor_host_window_found = true;
- }
- DCHECK(compositor_host_window_found);
-
- // Set all the plugin windows to be "after" the compositor window.
- // When the compositor window is created, gets placed above plugins.
- for (size_t i = 0; i < plugin_windows.size(); ++i) {
- HWND next;
- if (i + 1 < plugin_windows.size())
- next = plugin_windows[i+1];
- else
- next = compositor_host_window_;
- ::SetWindowPos(plugin_windows[i], next, 0, 0, 0, 0,
- SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
- }
- } else {
- // Drop the backing store for the accelerated surface when the accelerated
- // compositor is disabled. Otherwise, a flash of the last presented frame
- // could appear when it is next enabled.
- if (accelerated_surface_)
- accelerated_surface_->Suspend();
- hide_compositor_window_at_next_paint_ = true;
- }
-}
-
-void RenderWidgetHostViewWin::AcceleratedSurfaceInitialized(int host_id,
- int route_id) {
-}
-
-void RenderWidgetHostViewWin::AcceleratedSurfaceBuffersSwapped(
- const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params,
- int gpu_host_id) {
- NOTREACHED();
-}
-
-void RenderWidgetHostViewWin::AcceleratedSurfacePostSubBuffer(
- const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params,
- int gpu_host_id) {
- NOTREACHED();
-}
-
-void RenderWidgetHostViewWin::AcceleratedSurfaceSuspend() {
- if (!accelerated_surface_)
- return;
-
- accelerated_surface_->Suspend();
-}
-
-void RenderWidgetHostViewWin::AcceleratedSurfaceRelease() {
-}
-
-bool RenderWidgetHostViewWin::HasAcceleratedSurface(
- const gfx::Size& desired_size) {
- // TODO(jbates) Implement this so this view can use GetBackingStore for both
- // software and GPU frames. Defaulting to false just makes GetBackingStore
- // only useable for software frames.
- return false;
-}
-
-void RenderWidgetHostViewWin::SetAccessibilityFocus(int acc_obj_id) {
- if (!render_widget_host_)
- return;
-
- render_widget_host_->AccessibilitySetFocus(acc_obj_id);
-}
-
-void RenderWidgetHostViewWin::AccessibilityDoDefaultAction(int acc_obj_id) {
- if (!render_widget_host_)
- return;
-
- render_widget_host_->AccessibilityDoDefaultAction(acc_obj_id);
-}
-
-void RenderWidgetHostViewWin::AccessibilityScrollToMakeVisible(
- int acc_obj_id, gfx::Rect subfocus) {
- if (!render_widget_host_)
- return;
-
- render_widget_host_->AccessibilityScrollToMakeVisible(acc_obj_id, subfocus);
-}
-
-void RenderWidgetHostViewWin::AccessibilityScrollToPoint(
- int acc_obj_id, gfx::Point point) {
- if (!render_widget_host_)
- return;
-
- render_widget_host_->AccessibilityScrollToPoint(acc_obj_id, point);
-}
-
-void RenderWidgetHostViewWin::AccessibilitySetTextSelection(
- int acc_obj_id, int start_offset, int end_offset) {
- if (!render_widget_host_)
- return;
-
- render_widget_host_->AccessibilitySetTextSelection(
- acc_obj_id, start_offset, end_offset);
-}
-
-gfx::Point RenderWidgetHostViewWin::GetLastTouchEventLocation() const {
- return last_touch_location_;
-}
-
-void RenderWidgetHostViewWin::FatalAccessibilityTreeError() {
- render_widget_host_->FatalAccessibilityTreeError();
- SetBrowserAccessibilityManager(NULL);
-}
-
-LRESULT RenderWidgetHostViewWin::OnGetObject(UINT message, WPARAM wparam,
- LPARAM lparam, BOOL& handled) {
- TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnGetObject");
- if (kIdCustom == lparam) {
- // An MSAA client requestes our custom id. Assume that we have detected an
- // active windows screen reader.
- BrowserAccessibilityState::GetInstance()->OnScreenReaderDetected();
- render_widget_host_->SetAccessibilityMode(
- BrowserAccessibilityStateImpl::GetInstance()->accessibility_mode());
-
- // Return with failure.
- return static_cast<LRESULT>(0L);
- }
-
- if (lparam != OBJID_CLIENT) {
- handled = false;
- return static_cast<LRESULT>(0L);
- }
-
- IAccessible* iaccessible = GetNativeViewAccessible();
- if (iaccessible)
- return LresultFromObject(IID_IAccessible, wparam, iaccessible);
-
- handled = false;
- return static_cast<LRESULT>(0L);
-}
-
-LRESULT RenderWidgetHostViewWin::OnParentNotify(UINT message, WPARAM wparam,
- LPARAM lparam, BOOL& handled) {
- TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnParentNotify");
- handled = FALSE;
-
- if (!render_widget_host_)
- return 0;
-
- switch (LOWORD(wparam)) {
- case WM_LBUTTONDOWN:
- case WM_RBUTTONDOWN:
- case WM_MBUTTONDOWN:
- render_widget_host_->StartUserGesture();
- break;
- default:
- break;
- }
- return 0;
-}
-
-void RenderWidgetHostViewWin::OnFinalMessage(HWND window) {
- TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnFinalMessage");
- // When the render widget host is being destroyed, it ends up calling
- // Destroy() which NULLs render_widget_host_.
- // Note: the following bug http://crbug.com/24248 seems to report that
- // OnFinalMessage is called with a deleted |render_widget_host_|. It is not
- // clear how this could happen, hence the NULLing of render_widget_host_
- // above.
- if (!render_widget_host_ && !being_destroyed_) {
- // If you hit this NOTREACHED, please add a comment to report it on
- // http://crbug.com/24248, including what you did when it happened and if
- // you can repro.
- NOTREACHED();
- }
- if (render_widget_host_)
- render_widget_host_->ViewDestroyed();
- if (base::win::IsTSFAwareRequired())
- ui::TSFBridge::GetInstance()->RemoveFocusedClient(this);
- delete this;
-}
-
-LRESULT RenderWidgetHostViewWin::OnSessionChange(UINT message,
- WPARAM wparam,
- LPARAM lparam,
- BOOL& handled) {
- handled = FALSE;
- TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnSessionChange");
-
- if (!accelerated_surface_)
- return 0;
-
- switch (wparam) {
- case WTS_SESSION_LOCK:
- accelerated_surface_->SetIsSessionLocked(true);
- break;
- case WTS_SESSION_UNLOCK:
- // Force a repaint to update the window contents.
- if (!render_widget_host_->is_hidden())
- InvalidateRect(NULL, FALSE);
- accelerated_surface_->SetIsSessionLocked(false);
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-void RenderWidgetHostViewWin::TrackMouseLeave(bool track) {
- if (track == track_mouse_leave_)
- return;
- track_mouse_leave_ = track;
-
- DCHECK(m_hWnd);
-
- TRACKMOUSEEVENT tme;
- tme.cbSize = sizeof(TRACKMOUSEEVENT);
- tme.dwFlags = TME_LEAVE;
- if (!track_mouse_leave_)
- tme.dwFlags |= TME_CANCEL;
- tme.hwndTrack = m_hWnd;
-
- TrackMouseEvent(&tme);
-}
-
-bool RenderWidgetHostViewWin::Send(IPC::Message* message) {
- if (!render_widget_host_)
- return false;
- return render_widget_host_->Send(message);
-}
-
-void RenderWidgetHostViewWin::EnsureTooltip() {
- UINT message = TTM_NEWTOOLRECT;
-
- TOOLINFO ti = {0};
- ti.cbSize = sizeof(ti);
- ti.hwnd = m_hWnd;
- ti.uId = 0;
- if (!::IsWindow(tooltip_hwnd_)) {
- message = TTM_ADDTOOL;
- tooltip_hwnd_ = CreateWindowEx(
- WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(),
- TOOLTIPS_CLASS, NULL, TTS_NOPREFIX, 0, 0, 0, 0, m_hWnd, NULL,
- NULL, NULL);
- if (!tooltip_hwnd_) {
- // Tooltip creation can inexplicably fail. See bug 82913 for details.
- LOG_GETLASTERROR(WARNING) <<
- "Tooltip creation failed, tooltips won't work";
- return;
- }
- ti.uFlags = TTF_TRANSPARENT;
- ti.lpszText = LPSTR_TEXTCALLBACK;
-
- // Ensure web content tooltips are displayed for at least this amount of
- // time, to give users a chance to read longer messages.
- const int kMinimumAutopopDurationMs = 10 * 1000;
- int autopop_duration_ms =
- SendMessage(tooltip_hwnd_, TTM_GETDELAYTIME, TTDT_AUTOPOP, NULL);
- if (autopop_duration_ms < kMinimumAutopopDurationMs) {
- SendMessage(tooltip_hwnd_, TTM_SETDELAYTIME, TTDT_AUTOPOP,
- kMinimumAutopopDurationMs);
- }
- }
-
- CRect cr;
- GetClientRect(&ti.rect);
- SendMessage(tooltip_hwnd_, message, NULL, reinterpret_cast<LPARAM>(&ti));
-}
-
-void RenderWidgetHostViewWin::ResetTooltip() {
- if (::IsWindow(tooltip_hwnd_))
- ::DestroyWindow(tooltip_hwnd_);
- tooltip_hwnd_ = NULL;
-}
-
-bool RenderWidgetHostViewWin::ForwardGestureEventToRenderer(
- ui::GestureEvent* gesture) {
- if (!render_widget_host_)
- return false;
-
- // Pinch gestures are disabled by default on windows desktop. See
- // crbug.com/128477 and crbug.com/148816
- if ((gesture->type() == ui::ET_GESTURE_PINCH_BEGIN ||
- gesture->type() == ui::ET_GESTURE_PINCH_UPDATE ||
- gesture->type() == ui::ET_GESTURE_PINCH_END) &&
- !ShouldSendPinchGesture()) {
- return true;
- }
-
- blink::WebGestureEvent web_gesture = CreateWebGestureEvent(m_hWnd, *gesture);
- if (web_gesture.type == blink::WebGestureEvent::Undefined)
- return false;
- if (web_gesture.type == blink::WebGestureEvent::GestureTapDown) {
- render_widget_host_->ForwardGestureEvent(
- CreateFlingCancelEvent(gesture->time_stamp().InSecondsF()));
- }
- render_widget_host_->ForwardGestureEventWithLatencyInfo(web_gesture,
- *gesture->latency());
- return true;
-}
-
-void RenderWidgetHostViewWin::ForwardMouseEventToRenderer(UINT message,
- WPARAM wparam,
- LPARAM lparam) {
- TRACE_EVENT0("browser",
- "RenderWidgetHostViewWin::ForwardMouseEventToRenderer");
- if (!render_widget_host_) {
- TRACE_EVENT0("browser", "EarlyOut_NoRWH");
- return;
- }
-
- gfx::Point point = gfx::win::ScreenToDIPPoint(
- gfx::Point(static_cast<short>(LOWORD(lparam)),
- static_cast<short>(HIWORD(lparam))));
- lparam = MAKELPARAM(point.x(), point.y());
-
- WebMouseEvent event(
- WebMouseEventBuilder::Build(m_hWnd, message, wparam, lparam));
-
- if (mouse_locked_) {
- event.movementX = event.globalX - last_mouse_position_.locked_global.x();
- event.movementY = event.globalY - last_mouse_position_.locked_global.y();
- last_mouse_position_.locked_global.SetPoint(event.globalX, event.globalY);
-
- event.x = last_mouse_position_.unlocked.x();
- event.y = last_mouse_position_.unlocked.y();
- event.windowX = last_mouse_position_.unlocked.x();
- event.windowY = last_mouse_position_.unlocked.y();
- event.globalX = last_mouse_position_.unlocked_global.x();
- event.globalY = last_mouse_position_.unlocked_global.y();
- } else {
- if (ignore_mouse_movement_) {
- ignore_mouse_movement_ = false;
- event.movementX = 0;
- event.movementY = 0;
- } else {
- event.movementX =
- event.globalX - last_mouse_position_.unlocked_global.x();
- event.movementY =
- event.globalY - last_mouse_position_.unlocked_global.y();
- }
-
- last_mouse_position_.unlocked.SetPoint(event.windowX, event.windowY);
- last_mouse_position_.unlocked_global.SetPoint(event.globalX, event.globalY);
- }
-
- // Windows sends (fake) mouse messages for touch events. Don't send these to
- // the render widget.
- if (!touch_events_enabled_ || !ui::IsMouseEventFromTouch(message)) {
- // Send the event to the renderer before changing mouse capture, so that
- // the capturelost event arrives after mouseup.
- render_widget_host_->ForwardMouseEvent(event);
-
- switch (event.type) {
- case WebInputEvent::MouseMove:
- TrackMouseLeave(true);
- break;
- case WebInputEvent::MouseLeave:
- TrackMouseLeave(false);
- break;
- case WebInputEvent::MouseDown:
- SetCapture();
- break;
- case WebInputEvent::MouseUp:
- if (GetCapture() == m_hWnd)
- ReleaseCapture();
- break;
- }
- }
-
- if (IsActivatable() && event.type == WebInputEvent::MouseDown) {
- // This is a temporary workaround for bug 765011 to get focus when the
- // mouse is clicked. This happens after the mouse down event is sent to
- // the renderer because normally Windows does a WM_SETFOCUS after
- // WM_LBUTTONDOWN.
- SetFocus();
- }
-}
-
-void RenderWidgetHostViewWin::ShutdownHost() {
- weak_factory_.InvalidateWeakPtrs();
- if (render_widget_host_)
- render_widget_host_->Shutdown();
- // Do not touch any members at this point, |this| has been deleted.
-}
-
-void RenderWidgetHostViewWin::DoPopupOrFullscreenInit(HWND parent_hwnd,
- const gfx::Rect& pos,
- DWORD ex_style) {
- Create(parent_hwnd, NULL, NULL, WS_POPUP, ex_style);
- gfx::Rect screen_rect = gfx::win::DIPToScreenRect(pos);
- MoveWindow(screen_rect.x(), screen_rect.y(), screen_rect.width(),
- screen_rect.height(), TRUE);
- ShowWindow(IsActivatable() ? SW_SHOW : SW_SHOWNA);
-
- if (is_fullscreen_ && win8::IsSingleWindowMetroMode()) {
- MetroSetFrameWindow set_frame_window =
- reinterpret_cast<MetroSetFrameWindow>(
- ::GetProcAddress(base::win::GetMetroModule(), "SetFrameWindow"));
- DCHECK(set_frame_window);
- set_frame_window(m_hWnd);
- }
-}
-
-CPoint RenderWidgetHostViewWin::GetClientCenter() const {
- CRect rect;
- GetClientRect(&rect);
- return rect.CenterPoint();
-}
-
-void RenderWidgetHostViewWin::MoveCursorToCenterIfNecessary() {
- DCHECK(mouse_locked_);
-
- CRect rect;
- GetClipCursor(&rect);
- int border_x = rect.Width() * kMouseLockBorderPercentage / 100;
- int border_y = rect.Height() * kMouseLockBorderPercentage / 100;
-
- bool should_move =
- last_mouse_position_.locked_global.x() < rect.left + border_x ||
- last_mouse_position_.locked_global.x() > rect.right - border_x ||
- last_mouse_position_.locked_global.y() < rect.top + border_y ||
- last_mouse_position_.locked_global.y() > rect.bottom - border_y;
-
- if (should_move) {
- move_to_center_request_.pending = true;
- move_to_center_request_.target = rect.CenterPoint();
- if (!::SetCursorPos(move_to_center_request_.target.x(),
- move_to_center_request_.target.y())) {
- LOG_GETLASTERROR(WARNING) << "Failed to set cursor position.";
- }
- }
-}
-
-void RenderWidgetHostViewWin::HandleLockedMouseEvent(UINT message,
- WPARAM wparam,
- LPARAM lparam) {
- TRACE_EVENT0("browser", "RenderWidgetHostViewWin::HandleLockedMouseEvent");
- DCHECK(mouse_locked_);
-
- if (message == WM_MOUSEMOVE && move_to_center_request_.pending) {
- // Ignore WM_MOUSEMOVE messages generated by
- // MoveCursorToCenterIfNecessary().
- CPoint current_position(LOWORD(lparam), HIWORD(lparam));
- ClientToScreen(&current_position);
- if (move_to_center_request_.target.x() == current_position.x &&
- move_to_center_request_.target.y() == current_position.y) {
- move_to_center_request_.pending = false;
- last_mouse_position_.locked_global = move_to_center_request_.target;
- return;
- }
- }
-
- ForwardMouseEventToRenderer(message, wparam, lparam);
-}
-
-LRESULT RenderWidgetHostViewWin::OnDocumentFeed(RECONVERTSTRING* reconv) {
- size_t target_offset;
- size_t target_length;
- bool has_composition;
- if (!composition_range_.is_empty()) {
- target_offset = composition_range_.GetMin();
- target_length = composition_range_.length();
- has_composition = true;
- } else if (selection_range_.IsValid()) {
- target_offset = selection_range_.GetMin();
- target_length = selection_range_.length();
- has_composition = false;
- } else {
- return 0;
- }
-
- size_t len = selection_text_.length();
- size_t need_size = sizeof(RECONVERTSTRING) + len * sizeof(WCHAR);
-
- if (target_offset < selection_text_offset_ ||
- target_offset + target_length > selection_text_offset_ + len) {
- return 0;
- }
-
- if (!reconv)
- return need_size;
-
- if (reconv->dwSize < need_size)
- return 0;
-
- reconv->dwVersion = 0;
- reconv->dwStrLen = len;
- reconv->dwStrOffset = sizeof(RECONVERTSTRING);
- reconv->dwCompStrLen = has_composition ? target_length: 0;
- reconv->dwCompStrOffset =
- (target_offset - selection_text_offset_) * sizeof(WCHAR);
- reconv->dwTargetStrLen = target_length;
- reconv->dwTargetStrOffset = reconv->dwCompStrOffset;
- memcpy(reinterpret_cast<char*>(reconv) + sizeof(RECONVERTSTRING),
- selection_text_.c_str(), len * sizeof(WCHAR));
-
- // According to Microsft API document, IMR_RECONVERTSTRING and
- // IMR_DOCUMENTFEED should return reconv, but some applications return
- // need_size.
- return reinterpret_cast<LRESULT>(reconv);
-}
-
-LRESULT RenderWidgetHostViewWin::OnReconvertString(RECONVERTSTRING* reconv) {
- // If there is a composition string already, we don't allow reconversion.
- if (imm32_manager_->is_composing())
- return 0;
-
- if (selection_range_.is_empty())
- return 0;
-
- if (selection_text_.empty())
- return 0;
-
- if (selection_range_.GetMin() < selection_text_offset_ ||
- selection_range_.GetMax() >
- selection_text_offset_ + selection_text_.length()) {
- return 0;
- }
-
- size_t len = selection_range_.length();
- size_t need_size = sizeof(RECONVERTSTRING) + len * sizeof(WCHAR);
-
- if (!reconv)
- return need_size;
-
- if (reconv->dwSize < need_size)
- return 0;
-
- reconv->dwVersion = 0;
- reconv->dwStrLen = len;
- reconv->dwStrOffset = sizeof(RECONVERTSTRING);
- reconv->dwCompStrLen = len;
- reconv->dwCompStrOffset = 0;
- reconv->dwTargetStrLen = len;
- reconv->dwTargetStrOffset = 0;
-
- size_t offset = selection_range_.GetMin() - selection_text_offset_;
- memcpy(reinterpret_cast<char*>(reconv) + sizeof(RECONVERTSTRING),
- selection_text_.c_str() + offset, len * sizeof(WCHAR));
-
- // According to Microsft API document, IMR_RECONVERTSTRING and
- // IMR_DOCUMENTFEED should return reconv, but some applications return
- // need_size.
- return reinterpret_cast<LRESULT>(reconv);
-}
-
-LRESULT RenderWidgetHostViewWin::OnQueryCharPosition(
- IMECHARPOSITION* position) {
- DCHECK(position);
-
- if (position->dwSize < sizeof(IMECHARPOSITION))
- return 0;
-
- RECT target_rect = {};
- if (imm32_manager_->is_composing() && !composition_range_.is_empty() &&
- position->dwCharPos < composition_character_bounds_.size()) {
- target_rect =
- composition_character_bounds_[position->dwCharPos].ToRECT();
- } else if (position->dwCharPos == 0) {
- // When there is no on-going composition but |position->dwCharPos| is 0,
- // use the caret rect. This behavior is the same to RichEdit. In fact,
- // CUAS (Cicero Unaware Application Support) relies on this behavior to
- // implement ITfContextView::GetTextExt on top of IMM32-based applications.
- target_rect = caret_rect_.ToRECT();
- } else {
- return 0;
- }
- ClientToScreen(&target_rect);
-
- RECT document_rect = GetPixelBounds().ToRECT();
- ClientToScreen(&document_rect);
-
- position->pt.x = target_rect.left;
- position->pt.y = target_rect.top;
- position->cLineHeight = target_rect.bottom - target_rect.top;
- position->rcDocument = document_rect;
- return 1;
-}
-
-void RenderWidgetHostViewWin::UpdateIMEState() {
- if (base::win::IsTSFAwareRequired()) {
- ui::TSFBridge::GetInstance()->OnTextInputTypeChanged(this);
- return;
- }
- if (text_input_type_ != ui::TEXT_INPUT_TYPE_NONE &&
- text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD) {
- imm32_manager_->EnableIME(m_hWnd);
- imm32_manager_->SetUseCompositionWindow(!can_compose_inline_);
- } else {
- imm32_manager_->DisableIME(m_hWnd);
- }
-
- imm32_manager_->SetTextInputMode(m_hWnd, text_input_mode_);
-}
-
-void RenderWidgetHostViewWin::UpdateInputScopeIfNecessary(
- ui::TextInputType text_input_type) {
- // The text store is responsible for handling input scope when TSF-aware is
- // required.
- if (base::win::IsTSFAwareRequired())
- return;
-
- ui::tsf_inputscope::SetInputScopeForTsfUnawareWindow(
- m_hWnd, text_input_type, text_input_mode_);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// RenderWidgetHostView, public:
-
-// static
-RenderWidgetHostView* RenderWidgetHostView::CreateViewForWidget(
- RenderWidgetHost* widget) {
- return new RenderWidgetHostViewWin(widget);
-}
-
-// static
-void RenderWidgetHostViewPort::GetDefaultScreenInfo(
- blink::WebScreenInfo* results) {
- GetScreenInfoForWindow(0, results);
-}
-
-} // namespace content
diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_win.h b/chromium/content/browser/renderer_host/render_widget_host_view_win.h
deleted file mode 100644
index b9a87bf61e8..00000000000
--- a/chromium/content/browser/renderer_host/render_widget_host_view_win.h
+++ /dev/null
@@ -1,616 +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_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_WIN_H_
-#define CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_WIN_H_
-
-#include <atlbase.h>
-#include <atlapp.h>
-#include <atlcrack.h>
-#include <atlmisc.h>
-#include <vector>
-
-#include "base/compiler_specific.h"
-#include "base/gtest_prod_util.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/scoped_vector.h"
-#include "base/memory/weak_ptr.h"
-#include "base/time/time.h"
-#include "base/win/scoped_comptr.h"
-#include "content/browser/accessibility/browser_accessibility_manager.h"
-#include "content/browser/renderer_host/render_widget_host_view_base.h"
-#include "content/common/content_export.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
-#include "ui/base/ime/text_input_client.h"
-#include "ui/base/ime/win/tsf_bridge.h"
-#include "ui/events/gestures/gesture_recognizer.h"
-#include "ui/events/gestures/gesture_types.h"
-#include "ui/gfx/native_widget_types.h"
-#include "ui/gfx/point.h"
-#include "ui/surface/accelerated_surface_win.h"
-#include "webkit/common/cursors/webcursor.h"
-
-class SkRegion;
-
-namespace gfx {
-class Size;
-class Rect;
-}
-
-namespace IPC {
-class Message;
-}
-
-namespace ui {
-class IMM32Manager;
-class ViewProp;
-}
-
-namespace blink {
-struct WebScreenInfo;
-}
-
-namespace content {
-class BackingStore;
-class RenderWidgetHost;
-class WebTouchState;
-
-typedef CWinTraits<WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0>
- RenderWidgetHostHWNDTraits;
-
-CONTENT_EXPORT extern const wchar_t kRenderWidgetHostHWNDClass[];
-
-///////////////////////////////////////////////////////////////////////////////
-// RenderWidgetHostViewWin
-//
-// An object representing the "View" of a rendered web page. This object is
-// responsible for displaying the content of the web page, receiving windows
-// messages, and containing plugins HWNDs. It is the implementation of the
-// RenderWidgetHostView that the cross-platform RenderWidgetHost object uses
-// to display the data.
-//
-// Comment excerpted from render_widget_host.h:
-//
-// "The lifetime of the RenderWidgetHostHWND is tied to the render process.
-// If the render process dies, the RenderWidgetHostHWND goes away and all
-// references to it must become NULL."
-//
-// RenderWidgetHostView class hierarchy described in render_widget_host_view.h.
-class RenderWidgetHostViewWin
- : public CWindowImpl<RenderWidgetHostViewWin,
- CWindow,
- RenderWidgetHostHWNDTraits>,
- public RenderWidgetHostViewBase,
- public NotificationObserver,
- public BrowserAccessibilityDelegate,
- public ui::GestureConsumer,
- public ui::GestureEventHelper,
- public ui::TextInputClient { // for Win8/metro TSF support.
- public:
- virtual ~RenderWidgetHostViewWin();
-
- CONTENT_EXPORT void CreateWnd(HWND parent);
-
- void AcceleratedPaint(HDC dc);
-
- DECLARE_WND_CLASS_EX(kRenderWidgetHostHWNDClass, CS_DBLCLKS, 0);
-
- BEGIN_MSG_MAP(RenderWidgetHostHWND)
- MSG_WM_CREATE(OnCreate)
- MSG_WM_ACTIVATE(OnActivate)
- MSG_WM_DESTROY(OnDestroy)
- MSG_WM_PAINT(OnPaint)
- MSG_WM_NCPAINT(OnNCPaint)
- MSG_WM_NCHITTEST(OnNCHitTest)
- MSG_WM_ERASEBKGND(OnEraseBkgnd)
- MSG_WM_SETCURSOR(OnSetCursor)
- MSG_WM_SETFOCUS(OnSetFocus)
- MSG_WM_KILLFOCUS(OnKillFocus)
- MSG_WM_CAPTURECHANGED(OnCaptureChanged)
- MSG_WM_CANCELMODE(OnCancelMode)
- MSG_WM_INPUTLANGCHANGE(OnInputLangChange)
- MSG_WM_THEMECHANGED(OnThemeChanged)
- MSG_WM_NOTIFY(OnNotify)
- MESSAGE_HANDLER(WM_IME_SETCONTEXT, OnImeSetContext)
- MESSAGE_HANDLER(WM_IME_STARTCOMPOSITION, OnImeStartComposition)
- MESSAGE_HANDLER(WM_IME_COMPOSITION, OnImeComposition)
- MESSAGE_HANDLER(WM_IME_ENDCOMPOSITION, OnImeEndComposition)
- MESSAGE_HANDLER(WM_IME_REQUEST, OnImeRequest)
- MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseEvent)
- MESSAGE_HANDLER(WM_MOUSELEAVE, OnMouseEvent)
- MESSAGE_HANDLER(WM_LBUTTONDOWN, OnMouseEvent)
- MESSAGE_HANDLER(WM_MBUTTONDOWN, OnMouseEvent)
- MESSAGE_HANDLER(WM_RBUTTONDOWN, OnMouseEvent)
- MESSAGE_HANDLER(WM_LBUTTONUP, OnMouseEvent)
- MESSAGE_HANDLER(WM_MBUTTONUP, OnMouseEvent)
- MESSAGE_HANDLER(WM_RBUTTONUP, OnMouseEvent)
- MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnMouseEvent)
- MESSAGE_HANDLER(WM_MBUTTONDBLCLK, OnMouseEvent)
- MESSAGE_HANDLER(WM_RBUTTONDBLCLK, OnMouseEvent)
- MESSAGE_HANDLER(WM_SYSKEYDOWN, OnKeyEvent)
- MESSAGE_HANDLER(WM_SYSKEYUP, OnKeyEvent)
- MESSAGE_HANDLER(WM_KEYDOWN, OnKeyEvent)
- MESSAGE_HANDLER(WM_KEYUP, OnKeyEvent)
- MESSAGE_HANDLER(WM_MOUSEWHEEL, OnWheelEvent)
- MESSAGE_HANDLER(WM_MOUSEHWHEEL, OnWheelEvent)
- MESSAGE_HANDLER(WM_HSCROLL, OnWheelEvent)
- MESSAGE_HANDLER(WM_VSCROLL, OnWheelEvent)
- MESSAGE_HANDLER(WM_CHAR, OnKeyEvent)
- MESSAGE_HANDLER(WM_SYSCHAR, OnKeyEvent)
- MESSAGE_HANDLER(WM_TOUCH, OnTouchEvent)
- MESSAGE_HANDLER(WM_IME_CHAR, OnKeyEvent)
- MESSAGE_HANDLER(WM_MOUSEACTIVATE, OnMouseActivate)
- MESSAGE_HANDLER(WM_GETOBJECT, OnGetObject)
- MESSAGE_HANDLER(WM_PARENTNOTIFY, OnParentNotify)
- MESSAGE_HANDLER(WM_GESTURE, OnGestureEvent)
- MESSAGE_HANDLER(WM_MOVE, OnMoveOrSize)
- MESSAGE_HANDLER(WM_SIZE, OnMoveOrSize)
- MESSAGE_HANDLER(WM_WTSSESSION_CHANGE, OnSessionChange)
- END_MSG_MAP()
-
- // RenderWidgetHostView implementation.
- virtual void InitAsChild(gfx::NativeView parent_view) OVERRIDE;
- virtual RenderWidgetHost* GetRenderWidgetHost() const OVERRIDE;
- virtual void SetSize(const gfx::Size& size) OVERRIDE;
- virtual void SetBounds(const gfx::Rect& rect) OVERRIDE;
- virtual gfx::NativeView GetNativeView() const OVERRIDE;
- virtual gfx::NativeViewId GetNativeViewId() const OVERRIDE;
- virtual gfx::NativeViewAccessible GetNativeViewAccessible() OVERRIDE;
- virtual bool HasFocus() const OVERRIDE;
- virtual bool IsSurfaceAvailableForCopy() const OVERRIDE;
- virtual void Show() OVERRIDE;
- virtual void Hide() OVERRIDE;
- virtual bool IsShowing() OVERRIDE;
- virtual gfx::Rect GetViewBounds() const OVERRIDE;
- virtual void SetBackground(const SkBitmap& background) OVERRIDE;
-
- // Implementation of RenderWidgetHostViewPort.
- virtual void InitAsPopup(RenderWidgetHostView* parent_host_view,
- const gfx::Rect& pos) OVERRIDE;
- virtual void InitAsFullscreen(
- RenderWidgetHostView* reference_host_view) OVERRIDE;
- virtual void WasShown() OVERRIDE;
- virtual void WasHidden() OVERRIDE;
- virtual void MovePluginWindows(
- const gfx::Vector2d& scroll_offset,
- const std::vector<WebPluginGeometry>& moves) OVERRIDE;
- virtual void Focus() OVERRIDE;
- virtual void Blur() OVERRIDE;
- virtual void UpdateCursor(const WebCursor& cursor) OVERRIDE;
- virtual void SetIsLoading(bool is_loading) OVERRIDE;
- virtual void TextInputTypeChanged(ui::TextInputType type,
- ui::TextInputMode input_mode,
- bool can_compose_inline) OVERRIDE;
- virtual void SelectionBoundsChanged(
- const ViewHostMsg_SelectionBounds_Params& params) OVERRIDE;
- virtual void ScrollOffsetChanged() OVERRIDE;
- virtual void ImeCancelComposition() OVERRIDE;
- virtual void ImeCompositionRangeChanged(
- const gfx::Range& range,
- const std::vector<gfx::Rect>& character_bounds) OVERRIDE;
- virtual void DidUpdateBackingStore(
- const gfx::Rect& scroll_rect,
- const gfx::Vector2d& scroll_delta,
- const std::vector<gfx::Rect>& copy_rects,
- const ui::LatencyInfo& latency_info) OVERRIDE;
- virtual void RenderProcessGone(base::TerminationStatus status,
- int error_code) OVERRIDE;
- virtual bool CanSubscribeFrame() const OVERRIDE;
-
- // called by WebContentsImpl before DestroyWindow
- virtual void WillWmDestroy() OVERRIDE;
- virtual void Destroy() OVERRIDE;
- virtual void SetTooltipText(const base::string16& tooltip_text) OVERRIDE;
- virtual BackingStore* AllocBackingStore(const gfx::Size& size) OVERRIDE;
- virtual void CopyFromCompositingSurface(
- const gfx::Rect& src_subrect,
- const gfx::Size& dst_size,
- const base::Callback<void(bool, const SkBitmap&)>& callback) OVERRIDE;
- virtual void CopyFromCompositingSurfaceToVideoFrame(
- const gfx::Rect& src_subrect,
- const scoped_refptr<media::VideoFrame>& target,
- const base::Callback<void(bool)>& callback) OVERRIDE;
- virtual bool CanCopyToVideoFrame() const OVERRIDE;
- virtual void OnAcceleratedCompositingStateChange() OVERRIDE;
- virtual void AcceleratedSurfaceInitialized(int host_id,
- int route_id) OVERRIDE;
- virtual void ProcessAckedTouchEvent(const TouchEventWithLatencyInfo& touch,
- InputEventAckState ack_result) OVERRIDE;
- virtual void SetHasHorizontalScrollbar(
- bool has_horizontal_scrollbar) OVERRIDE;
- virtual void SetScrollOffsetPinning(
- bool is_pinned_to_left, bool is_pinned_to_right) OVERRIDE;
- virtual void GetScreenInfo(blink::WebScreenInfo* results) OVERRIDE;
- virtual gfx::Rect GetBoundsInRootWindow() OVERRIDE;
- virtual gfx::GLSurfaceHandle GetCompositingSurface() OVERRIDE;
- virtual void ResizeCompositingSurface(const gfx::Size&) OVERRIDE;
- virtual void AcceleratedSurfaceBuffersSwapped(
- const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params,
- int gpu_host_id) OVERRIDE;
- virtual void AcceleratedSurfacePostSubBuffer(
- const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params,
- int gpu_host_id) OVERRIDE;
- virtual void AcceleratedSurfaceSuspend() OVERRIDE;
- virtual void AcceleratedSurfaceRelease() OVERRIDE;
- virtual bool HasAcceleratedSurface(const gfx::Size& desired_size) OVERRIDE;
- virtual void OnAccessibilityEvents(
- const std::vector<AccessibilityHostMsg_EventParams>& params
- ) OVERRIDE;
- virtual bool LockMouse() OVERRIDE;
- virtual void UnlockMouse() OVERRIDE;
- virtual void SetClickthroughRegion(SkRegion* region) OVERRIDE;
-
- // Implementation of NotificationObserver:
- virtual void Observe(int type,
- const NotificationSource& source,
- const NotificationDetails& details) OVERRIDE;
-
- // Implementation of BrowserAccessibilityDelegate:
- virtual void SetAccessibilityFocus(int acc_obj_id) OVERRIDE;
- virtual void AccessibilityDoDefaultAction(int acc_obj_id) OVERRIDE;
- virtual void AccessibilityScrollToMakeVisible(
- int acc_obj_id, gfx::Rect subfocus) OVERRIDE;
- virtual void AccessibilityScrollToPoint(
- int acc_obj_id, gfx::Point point) OVERRIDE;
- virtual void AccessibilitySetTextSelection(
- int acc_obj_id, int start_offset, int end_offset) OVERRIDE;
- virtual gfx::Point GetLastTouchEventLocation() const OVERRIDE;
- virtual void FatalAccessibilityTreeError() OVERRIDE;
-
- // Overridden from ui::GestureEventHelper.
- virtual bool CanDispatchToConsumer(ui::GestureConsumer* consumer) OVERRIDE;
- virtual void DispatchPostponedGestureEvent(ui::GestureEvent* event) OVERRIDE;
- virtual void DispatchCancelTouchEvent(ui::TouchEvent* event) OVERRIDE;
-
- // Overridden from ui::TextInputClient for Win8/metro TSF support.
- // Following methods are not used in existing IMM32 related implementation.
- virtual void SetCompositionText(
- const ui::CompositionText& composition) OVERRIDE;
- virtual void ConfirmCompositionText() OVERRIDE;
- virtual void ClearCompositionText() OVERRIDE;
- virtual void InsertText(const base::string16& text) OVERRIDE;
- virtual void InsertChar(char16 ch, int flags) OVERRIDE;
- virtual gfx::NativeWindow GetAttachedWindow() const OVERRIDE;
- virtual ui::TextInputType GetTextInputType() const OVERRIDE;
- virtual ui::TextInputMode GetTextInputMode() const OVERRIDE;
- virtual bool CanComposeInline() const OVERRIDE;
- virtual gfx::Rect GetCaretBounds() const OVERRIDE;
- virtual bool GetCompositionCharacterBounds(uint32 index,
- gfx::Rect* rect) const OVERRIDE;
- virtual bool HasCompositionText() const OVERRIDE;
- virtual bool GetTextRange(gfx::Range* range) const OVERRIDE;
- virtual bool GetCompositionTextRange(gfx::Range* range) const OVERRIDE;
- virtual bool GetSelectionRange(gfx::Range* range) const OVERRIDE;
- virtual bool SetSelectionRange(const gfx::Range& range) OVERRIDE;
- virtual bool DeleteRange(const gfx::Range& range) OVERRIDE;
- virtual bool GetTextFromRange(const gfx::Range& range,
- base::string16* text) const OVERRIDE;
- virtual void OnInputMethodChanged() OVERRIDE;
- virtual bool ChangeTextDirectionAndLayoutAlignment(
- base::i18n::TextDirection direction) OVERRIDE;
- virtual void ExtendSelectionAndDelete(size_t before, size_t after) OVERRIDE;
- virtual void EnsureCaretInRect(const gfx::Rect& rect) OVERRIDE;
- virtual void OnCandidateWindowShown() OVERRIDE;
- virtual void OnCandidateWindowUpdated() OVERRIDE;
- virtual void OnCandidateWindowHidden() OVERRIDE;
-
- protected:
- friend class RenderWidgetHostView;
-
- // Should construct only via RenderWidgetHostView::CreateViewForWidget.
- //
- // The view will associate itself with the given widget.
- explicit RenderWidgetHostViewWin(RenderWidgetHost* widget);
-
- // Windows Message Handlers
- LRESULT OnCreate(CREATESTRUCT* create_struct);
- void OnActivate(UINT, BOOL, HWND);
- void OnDestroy();
- void OnPaint(HDC unused_dc);
- void OnNCPaint(HRGN update_region);
- LRESULT OnNCHitTest(const CPoint& pt);
- LRESULT OnEraseBkgnd(HDC dc);
- LRESULT OnSetCursor(HWND window, UINT hittest_code, UINT mouse_message_id);
- void OnSetFocus(HWND window);
- void OnKillFocus(HWND window);
- void OnCaptureChanged(HWND window);
- void OnCancelMode();
- void OnInputLangChange(DWORD character_set, HKL input_language_id);
- void OnThemeChanged();
- LRESULT OnNotify(int w_param, NMHDR* header);
- LRESULT OnImeSetContext(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled);
- LRESULT OnImeStartComposition(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled);
- LRESULT OnImeComposition(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled);
- LRESULT OnImeEndComposition(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled);
- LRESULT OnImeRequest(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled);
- LRESULT OnMouseEvent(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled);
- LRESULT OnKeyEvent(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled);
- LRESULT OnWheelEvent(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled);
- LRESULT OnTouchEvent(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled);
- LRESULT OnMouseActivate(UINT message,
- WPARAM wparam,
- LPARAM lparam,
- BOOL& handled);
- // Handle MSAA requests for accessibility information.
- LRESULT OnGetObject(UINT message, WPARAM wparam, LPARAM lparam,
- BOOL& handled);
- // Handle vertical scrolling.
- LRESULT OnVScroll(int code, short position, HWND scrollbar_control);
- // Handle horizontal scrolling.
- LRESULT OnHScroll(int code, short position, HWND scrollbar_control);
-
- LRESULT OnParentNotify(UINT message, WPARAM wparam, LPARAM lparam,
- BOOL& handled);
-
- // Handle high-level touch events.
- LRESULT OnGestureEvent(UINT message, WPARAM wparam, LPARAM lparam,
- BOOL& handled);
- LRESULT OnMoveOrSize(UINT message, WPARAM wparam, LPARAM lparam,
- BOOL& handled);
-
- // Handle transitioning in and out of screensaver mode.
- LRESULT OnSessionChange(UINT message,
- WPARAM wparam,
- LPARAM lparam,
- BOOL& handled);
-
- void OnFinalMessage(HWND window);
-
- private:
- FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewWinBrowserTest,
- TextInputTypeChanged);
-
- // Updates the display cursor to the current cursor if the cursor is over this
- // render view.
- void UpdateCursorIfOverSelf();
-
- // Tells Windows that we want to hear about mouse exit messages.
- void TrackMouseLeave(bool start_tracking);
-
- // Sends a message to the RenderView in the renderer process.
- bool Send(IPC::Message* message);
-
- // Set the tooltip region to the size of the window, creating the tooltip
- // hwnd if it has not been created yet.
- void EnsureTooltip();
-
- // Tooltips become invalid when the root ancestor changes. When the View
- // becomes hidden, this method is called to reset the tooltip.
- void ResetTooltip();
-
- // Builds and forwards a WebKitGestureEvent to the renderer.
- bool ForwardGestureEventToRenderer(
- ui::GestureEvent* gesture);
-
- // Process all of the given gestures (passes them on to renderer)
- void ProcessGestures(ui::GestureRecognizer::Gestures* gestures);
-
- // Sends the specified mouse event to the renderer.
- void ForwardMouseEventToRenderer(UINT message, WPARAM wparam, LPARAM lparam);
-
- // Synthesize mouse wheel event.
- LRESULT SynthesizeMouseWheel(bool is_vertical, int scroll_code,
- short scroll_position);
-
- // Shuts down the render_widget_host_. This is a separate function so we can
- // invoke it from the message loop.
- void ShutdownHost();
-
- // Redraws the window synchronously, and any child windows (i.e. plugins)
- // asynchronously.
- void Redraw();
-
- // Draw our background over the given HDC in the given |rect|. The background
- // will be tiled such that it lines up with existing tiles starting from the
- // origin of |dc|.
- void DrawBackground(const RECT& rect, CPaintDC* dc);
-
- // Clean up the compositor window, if needed.
- void CleanupCompositorWindow();
-
- // Whether the window should be activated.
- bool IsActivatable() const;
-
- // Do initialization needed by both InitAsPopup() and InitAsFullscreen().
- void DoPopupOrFullscreenInit(HWND parent_hwnd,
- const gfx::Rect& pos,
- DWORD ex_style);
-
- CPoint GetClientCenter() const;
- // In mouse lock mode, moves the mouse cursor to the center of the view if it
- // is too close to the border.
- void MoveCursorToCenterIfNecessary();
-
- void HandleLockedMouseEvent(UINT message, WPARAM wparam, LPARAM lparam);
-
- LRESULT OnDocumentFeed(RECONVERTSTRING* reconv);
- LRESULT OnReconvertString(RECONVERTSTRING* reconv);
- LRESULT OnQueryCharPosition(IMECHARPOSITION* position);
-
- // Sets the appropriate mode for raw-touches or gestures. Currently touch mode
- // will only take effect on Win7+.
- void UpdateDesiredTouchMode();
-
- // Configures the enable/disable state of |ime_input_| to match with the
- // current |text_input_type_|.
- void UpdateIMEState();
-
- // Returns bounds of the view in pixels.
- gfx::Rect GetPixelBounds() const;
-
- // Sets the appropriate input scope for given |text_input_type| if TSF-aware
- // is not required. Does nothing if TSF-aware is required (and TSF text store
- // is responsible for managing input scope). Currently input scope will only
- // take effect on Vista+.
- void UpdateInputScopeIfNecessary(ui::TextInputType text_input_type);
-
- // Create a BrowserAccessibilityManager with an empty document if it
- // doesn't already exist.
- void CreateBrowserAccessibilityManagerIfNeeded();
-
- // The associated Model. While |this| is being Destroyed,
- // |render_widget_host_| is NULL and the Windows message loop is run one last
- // time. Message handlers must check for a NULL |render_widget_host_|.
- RenderWidgetHostImpl* render_widget_host_;
-
- // When we are doing accelerated compositing
- HWND compositor_host_window_;
-
- // Presents a texture received from another process to the compositing
- // window.
- scoped_ptr<AcceleratedSurface> accelerated_surface_;
-
- // true if the compositor host window must be hidden after the
- // software renderered view is updated.
- bool hide_compositor_window_at_next_paint_;
-
- // The cursor for the page. This is passed up from the renderer.
- WebCursor current_cursor_;
-
- // Indicates if the page is loading.
- bool is_loading_;
-
- // true if we are currently tracking WM_MOUSEEXIT messages.
- bool track_mouse_leave_;
-
- // Wrapper class for IMM32 APIs.
- // (See "ui/base/ime/win/imm32_manager.h" for its details.)
- scoped_ptr<ui::IMM32Manager> imm32_manager_;
-
- // Represents whether or not this browser process is receiving status
- // messages about the focused edit control from a renderer process.
- bool ime_notification_;
-
- // true if Enter was hit when render widget host was in focus.
- bool capture_enter_key_;
-
- // The touch-state. Its touch-points are updated as necessary. A new
- // touch-point is added from an TOUCHEVENTF_DOWN message, and a touch-point
- // is removed from the list on an TOUCHEVENTF_UP message.
- scoped_ptr<WebTouchState> touch_state_;
-
- // True if we're in the midst of a paint operation and should respond to
- // DidPaintRect() notifications by merely invalidating. See comments on
- // render_widget_host_view.h:DidPaintRect().
- bool about_to_validate_and_paint_;
-
- // true if the View should be closed when its HWND is deactivated (used to
- // support SELECT popups which are closed when they are deactivated).
- bool close_on_deactivate_;
-
- // Whether Destroy() has been called. Used to detect a crasher
- // (http://crbug.com/24248) where render_view_host_ has been deleted when
- // OnFinalMessage is called.
- bool being_destroyed_;
-
- // Tooltips
- // The text to be shown in the tooltip, supplied by the renderer.
- base::string16 tooltip_text_;
- // The tooltip control hwnd
- HWND tooltip_hwnd_;
- // Whether or not a tooltip is currently visible. We use this to track
- // whether or not we want to force-close the tooltip when we receive mouse
- // move notifications from the renderer. See comment in OnMsgSetTooltipText.
- bool tooltip_showing_;
-
- // Factory used to safely scope delayed calls to ShutdownHost().
- base::WeakPtrFactory<RenderWidgetHostViewWin> weak_factory_;
-
- // The time at which this view started displaying white pixels as a result of
- // not having anything to paint (empty backing store from renderer). This
- // value returns true for is_null() if we are not recording whiteout times.
- base::TimeTicks whiteout_start_time_;
-
- // The time it took after this view was selected for it to be fully painted.
- base::TimeTicks web_contents_switch_paint_time_;
-
- // Registrar so we can listen to RENDERER_PROCESS_TERMINATED events.
- NotificationRegistrar registrar_;
-
- // Stores the current text input type received by TextInputStateChanged()
- // method.
- ui::TextInputType text_input_type_;
- ui::TextInputMode text_input_mode_;
- bool can_compose_inline_;
-
- ScopedVector<ui::ViewProp> props_;
-
- // Is the widget fullscreen?
- bool is_fullscreen_;
-
- // Used to record the last position of the mouse.
- struct {
- // While the mouse is locked, |unlocked| and |unlocked_global| store the
- // last known position just as mouse lock was entered.
- // Relative to the upper-left corner of the view.
- gfx::Point unlocked;
- // Relative to the upper-left corner of the screen.
- gfx::Point unlocked_global;
-
- // Only valid while the mouse is locked.
- gfx::Point locked_global;
- } last_mouse_position_;
-
- // When the mouse cursor is moved to the center of the view by
- // MoveCursorToCenterIfNecessary(), we ignore the resulting WM_MOUSEMOVE
- // message.
- struct {
- bool pending;
- // Relative to the upper-left corner of the screen.
- gfx::Point target;
- } move_to_center_request_;
-
- // In the case of the mouse being moved away from the view and then moved
- // back, we regard the mouse movement as (0, 0).
- bool ignore_mouse_movement_;
-
- gfx::Range composition_range_;
-
- // The current composition character bounds.
- std::vector<gfx::Rect> composition_character_bounds_;
-
- // A cached latest caret rectangle sent from renderer.
- gfx::Rect caret_rect_;
-
- // TODO(ananta): The pointer and touch related members should be moved to an
- // independent class to reduce the clutter. This includes members
- // pointer_down_context_ and last_touch_location_.
-
- // Set to true if we are in the context of a pointer down message.
- bool pointer_down_context_;
-
- // The global x, y coordinates of the last point a touch event was
- // received, used to determine if it's okay to open the on-screen
- // keyboard. Reset when the window loses focus.
- gfx::Point last_touch_location_;
-
- // Region in which the view will be transparent to clicks.
- scoped_ptr<SkRegion> transparent_region_;
-
- // Are touch events currently enabled?
- bool touch_events_enabled_;
-
- scoped_ptr<ui::GestureRecognizer> gesture_recognizer_;
-
- // The OS-provided default IAccessible instance for our hwnd.
- base::win::ScopedComPtr<IAccessible> window_iaccessible_;
-
- ui::LatencyInfo software_latency_info_;
-
- DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewWin);
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_WIN_H_
diff --git a/chromium/content/browser/renderer_host/renderer_frame_manager.cc b/chromium/content/browser/renderer_host/renderer_frame_manager.cc
index 3d16361828d..0a93e298943 100644
--- a/chromium/content/browser/renderer_host/renderer_frame_manager.cc
+++ b/chromium/content/browser/renderer_host/renderer_frame_manager.cc
@@ -4,8 +4,12 @@
#include "content/browser/renderer_host/renderer_frame_manager.h"
+#include <algorithm>
+
#include "base/logging.h"
+#include "base/memory/shared_memory.h"
#include "base/sys_info.h"
+#include "content/common/host_shared_bitmap_manager.h"
namespace content {
@@ -14,46 +18,80 @@ RendererFrameManager* RendererFrameManager::GetInstance() {
}
void RendererFrameManager::AddFrame(RendererFrameManagerClient* frame,
- bool visible) {
+ bool locked) {
RemoveFrame(frame);
- if (visible)
- visible_frames_.insert(frame);
+ if (locked)
+ locked_frames_[frame] = 1;
else
- hidden_frames_.push_front(frame);
- CullHiddenFrames();
+ unlocked_frames_.push_front(frame);
+ CullUnlockedFrames();
}
void RendererFrameManager::RemoveFrame(RendererFrameManagerClient* frame) {
- visible_frames_.erase(frame);
- hidden_frames_.remove(frame);
+ std::map<RendererFrameManagerClient*, size_t>::iterator locked_iter =
+ locked_frames_.find(frame);
+ if (locked_iter != locked_frames_.end())
+ locked_frames_.erase(locked_iter);
+ unlocked_frames_.remove(frame);
}
-void RendererFrameManager::SetFrameVisibility(RendererFrameManagerClient* frame,
- bool visible) {
- if (visible) {
- hidden_frames_.remove(frame);
- visible_frames_.insert(frame);
+void RendererFrameManager::LockFrame(RendererFrameManagerClient* frame) {
+ std::list<RendererFrameManagerClient*>::iterator unlocked_iter =
+ std::find(unlocked_frames_.begin(), unlocked_frames_.end(), frame);
+ if (unlocked_iter != unlocked_frames_.end()) {
+ DCHECK(locked_frames_.find(frame) == locked_frames_.end());
+ unlocked_frames_.remove(frame);
+ locked_frames_[frame] = 1;
} else {
- visible_frames_.erase(frame);
- hidden_frames_.push_front(frame);
- CullHiddenFrames();
+ DCHECK(locked_frames_.find(frame) != locked_frames_.end());
+ locked_frames_[frame]++;
}
}
-RendererFrameManager::RendererFrameManager()
- : max_number_of_saved_frames_(
- std::min(5, 2 + (base::SysInfo::AmountOfPhysicalMemoryMB() / 256))) {}
+void RendererFrameManager::UnlockFrame(RendererFrameManagerClient* frame) {
+ DCHECK(locked_frames_.find(frame) != locked_frames_.end());
+ size_t locked_count = locked_frames_[frame];
+ DCHECK(locked_count);
+ if (locked_count > 1) {
+ locked_frames_[frame]--;
+ } else {
+ RemoveFrame(frame);
+ unlocked_frames_.push_front(frame);
+ CullUnlockedFrames();
+ }
+}
+
+RendererFrameManager::RendererFrameManager() {
+ max_number_of_saved_frames_ =
+#if defined(OS_ANDROID)
+ 1;
+#else
+ std::min(5, 2 + (base::SysInfo::AmountOfPhysicalMemoryMB() / 256));
+#endif
+ max_handles_ = base::SharedMemory::GetHandleLimit() / 8.0f;
+}
RendererFrameManager::~RendererFrameManager() {}
-void RendererFrameManager::CullHiddenFrames() {
- while (!hidden_frames_.empty() &&
- hidden_frames_.size() + visible_frames_.size() >
- max_number_of_saved_frames()) {
- size_t old_size = hidden_frames_.size();
+void RendererFrameManager::CullUnlockedFrames() {
+ uint32 saved_frame_limit = max_number_of_saved_frames();
+
+ if (unlocked_frames_.size() + locked_frames_.size() > 0) {
+ float handles_per_frame =
+ HostSharedBitmapManager::current()->AllocatedBitmapCount() * 1.0f /
+ (unlocked_frames_.size() + locked_frames_.size());
+
+ saved_frame_limit = std::max(
+ 1,
+ static_cast<int>(std::min(static_cast<float>(saved_frame_limit),
+ max_handles_ / handles_per_frame)));
+ }
+ while (!unlocked_frames_.empty() &&
+ unlocked_frames_.size() + locked_frames_.size() > saved_frame_limit) {
+ size_t old_size = unlocked_frames_.size();
// Should remove self from list.
- hidden_frames_.back()->EvictCurrentFrame();
- DCHECK_EQ(hidden_frames_.size() + 1, old_size);
+ unlocked_frames_.back()->EvictCurrentFrame();
+ DCHECK_EQ(unlocked_frames_.size() + 1, old_size);
}
}
diff --git a/chromium/content/browser/renderer_host/renderer_frame_manager.h b/chromium/content/browser/renderer_host/renderer_frame_manager.h
index 134e1ef6a04..f8d90f476f0 100644
--- a/chromium/content/browser/renderer_host/renderer_frame_manager.h
+++ b/chromium/content/browser/renderer_host/renderer_frame_manager.h
@@ -6,7 +6,7 @@
#define CONTENT_BROWSER_RENDERER_HOST_RENDERER_FRAME_MANAGER_H_
#include <list>
-#include <set>
+#include <map>
#include "base/basictypes.h"
#include "base/memory/singleton.h"
@@ -24,23 +24,29 @@ class CONTENT_EXPORT RendererFrameManager {
public:
static RendererFrameManager* GetInstance();
- void AddFrame(RendererFrameManagerClient*, bool visible);
+ void AddFrame(RendererFrameManagerClient*, bool locked);
void RemoveFrame(RendererFrameManagerClient*);
- void SetFrameVisibility(RendererFrameManagerClient*, bool visible);
+ void LockFrame(RendererFrameManagerClient*);
+ void UnlockFrame(RendererFrameManagerClient*);
size_t max_number_of_saved_frames() const {
return max_number_of_saved_frames_;
}
+ // For testing only
+ void set_max_handles(float max_handles) { max_handles_ = max_handles; }
+
private:
RendererFrameManager();
~RendererFrameManager();
- void CullHiddenFrames();
+ void CullUnlockedFrames();
+
friend struct DefaultSingletonTraits<RendererFrameManager>;
- std::set<RendererFrameManagerClient*> visible_frames_;
- std::list<RendererFrameManagerClient*> hidden_frames_;
+ std::map<RendererFrameManagerClient*, size_t> locked_frames_;
+ std::list<RendererFrameManagerClient*> unlocked_frames_;
size_t max_number_of_saved_frames_;
+ float max_handles_;
DISALLOW_COPY_AND_ASSIGN(RendererFrameManager);
};
diff --git a/chromium/content/browser/renderer_host/sandbox_ipc_linux.cc b/chromium/content/browser/renderer_host/sandbox_ipc_linux.cc
new file mode 100644
index 00000000000..7598bdbcc13
--- /dev/null
+++ b/chromium/content/browser/renderer_host/sandbox_ipc_linux.cc
@@ -0,0 +1,650 @@
+// 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/renderer_host/sandbox_ipc_linux.h"
+
+#include <fcntl.h>
+#include <fontconfig/fontconfig.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include "base/command_line.h"
+#include "base/files/scoped_file.h"
+#include "base/linux_util.h"
+#include "base/memory/scoped_vector.h"
+#include "base/memory/shared_memory.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/posix/unix_domain_socket_linux.h"
+#include "base/process/launch.h"
+#include "base/strings/string_number_conversions.h"
+#include "content/common/font_config_ipc_linux.h"
+#include "content/common/sandbox_linux/sandbox_linux.h"
+#include "content/common/set_process_title.h"
+#include "content/public/common/content_switches.h"
+#include "ppapi/c/trusted/ppb_browser_font_trusted.h"
+#include "third_party/WebKit/public/platform/linux/WebFontInfo.h"
+#include "third_party/WebKit/public/web/WebKit.h"
+#include "third_party/npapi/bindings/npapi_extensions.h"
+#include "third_party/skia/include/ports/SkFontConfigInterface.h"
+#include "ui/gfx/font_render_params_linux.h"
+
+using blink::WebCString;
+using blink::WebFontInfo;
+using blink::WebUChar;
+using blink::WebUChar32;
+
+namespace {
+
+// MSCharSetToFontconfig translates a Microsoft charset identifier to a
+// fontconfig language set by appending to |langset|.
+// Returns true if |langset| is Latin/Greek/Cyrillic.
+bool MSCharSetToFontconfig(FcLangSet* langset, unsigned fdwCharSet) {
+ // We have need to translate raw fdwCharSet values into terms that
+ // fontconfig can understand. (See the description of fdwCharSet in the MSDN
+ // documentation for CreateFont:
+ // http://msdn.microsoft.com/en-us/library/dd183499(VS.85).aspx )
+ //
+ // Although the argument is /called/ 'charset', the actual values conflate
+ // character sets (which are sets of Unicode code points) and character
+ // encodings (which are algorithms for turning a series of bits into a
+ // series of code points.) Sometimes the values will name a language,
+ // sometimes they'll name an encoding. In the latter case I'm assuming that
+ // they mean the set of code points in the domain of that encoding.
+ //
+ // fontconfig deals with ISO 639-1 language codes:
+ // http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
+ //
+ // So, for each of the documented fdwCharSet values I've had to take a
+ // guess at the set of ISO 639-1 languages intended.
+
+ bool is_lgc = false;
+ switch (fdwCharSet) {
+ case NPCharsetAnsi:
+ // These values I don't really know what to do with, so I'm going to map
+ // them to English also.
+ case NPCharsetDefault:
+ case NPCharsetMac:
+ case NPCharsetOEM:
+ case NPCharsetSymbol:
+ is_lgc = true;
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("en"));
+ break;
+ case NPCharsetBaltic:
+ // The three baltic languages.
+ is_lgc = true;
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("et"));
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("lv"));
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("lt"));
+ break;
+ case NPCharsetChineseBIG5:
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("zh-tw"));
+ break;
+ case NPCharsetGB2312:
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("zh-cn"));
+ break;
+ case NPCharsetEastEurope:
+ // A scattering of eastern European languages.
+ is_lgc = true;
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("pl"));
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("cs"));
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("sk"));
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("hu"));
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("hr"));
+ break;
+ case NPCharsetGreek:
+ is_lgc = true;
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("el"));
+ break;
+ case NPCharsetHangul:
+ case NPCharsetJohab:
+ // Korean
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ko"));
+ break;
+ case NPCharsetRussian:
+ is_lgc = true;
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ru"));
+ break;
+ case NPCharsetShiftJIS:
+ // Japanese
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ja"));
+ break;
+ case NPCharsetTurkish:
+ is_lgc = true;
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("tr"));
+ break;
+ case NPCharsetVietnamese:
+ is_lgc = true;
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("vi"));
+ break;
+ case NPCharsetArabic:
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ar"));
+ break;
+ case NPCharsetHebrew:
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("he"));
+ break;
+ case NPCharsetThai:
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("th"));
+ break;
+ // default:
+ // Don't add any languages in that case that we don't recognise the
+ // constant.
+ }
+ return is_lgc;
+}
+
+} // namespace
+
+namespace content {
+
+SandboxIPCHandler::SandboxIPCHandler(int lifeline_fd, int browser_socket)
+ : lifeline_fd_(lifeline_fd), browser_socket_(browser_socket) {
+ // FontConfig doesn't provide a standard property to control subpixel
+ // positioning, so we pass the current setting through to WebKit.
+ WebFontInfo::setSubpixelPositioning(
+ gfx::GetDefaultWebkitSubpixelPositioning());
+}
+
+void SandboxIPCHandler::Run() {
+ struct pollfd pfds[2];
+ pfds[0].fd = lifeline_fd_;
+ pfds[0].events = POLLIN;
+ pfds[1].fd = browser_socket_;
+ pfds[1].events = POLLIN;
+
+ int failed_polls = 0;
+ for (;;) {
+ const int r =
+ HANDLE_EINTR(poll(pfds, arraysize(pfds), -1 /* no timeout */));
+ // '0' is not a possible return value with no timeout.
+ DCHECK_NE(0, r);
+ if (r < 0) {
+ PLOG(WARNING) << "poll";
+ if (failed_polls++ == 3) {
+ LOG(FATAL) << "poll(2) failing. SandboxIPCHandler aborting.";
+ return;
+ }
+ continue;
+ }
+
+ failed_polls = 0;
+
+ // The browser process will close the other end of this pipe on shutdown,
+ // so we should exit.
+ if (pfds[0].revents) {
+ break;
+ }
+
+ // If poll(2) reports an error condition in this fd,
+ // we assume the zygote is gone and we exit the loop.
+ if (pfds[1].revents & (POLLERR | POLLHUP)) {
+ break;
+ }
+
+ if (pfds[1].revents & POLLIN) {
+ HandleRequestFromRenderer(browser_socket_);
+ }
+ }
+
+ VLOG(1) << "SandboxIPCHandler stopping.";
+}
+
+void SandboxIPCHandler::HandleRequestFromRenderer(int fd) {
+ ScopedVector<base::ScopedFD> fds;
+
+ // A FontConfigIPC::METHOD_MATCH message could be kMaxFontFamilyLength
+ // bytes long (this is the largest message type).
+ // 128 bytes padding are necessary so recvmsg() does not return MSG_TRUNC
+ // error for a maximum length message.
+ char buf[FontConfigIPC::kMaxFontFamilyLength + 128];
+
+ const ssize_t len = UnixDomainSocket::RecvMsg(fd, buf, sizeof(buf), &fds);
+ if (len == -1) {
+ // TODO: should send an error reply, or the sender might block forever.
+ NOTREACHED() << "Sandbox host message is larger than kMaxFontFamilyLength";
+ return;
+ }
+ if (fds.empty())
+ return;
+
+ Pickle pickle(buf, len);
+ PickleIterator iter(pickle);
+
+ int kind;
+ if (!pickle.ReadInt(&iter, &kind))
+ return;
+
+ if (kind == FontConfigIPC::METHOD_MATCH) {
+ HandleFontMatchRequest(fd, pickle, iter, fds.get());
+ } else if (kind == FontConfigIPC::METHOD_OPEN) {
+ HandleFontOpenRequest(fd, pickle, iter, fds.get());
+ } else if (kind == LinuxSandbox::METHOD_GET_FALLBACK_FONT_FOR_CHAR) {
+ HandleGetFallbackFontForChar(fd, pickle, iter, fds.get());
+ } else if (kind == LinuxSandbox::METHOD_LOCALTIME) {
+ HandleLocaltime(fd, pickle, iter, fds.get());
+ } else if (kind == LinuxSandbox::METHOD_GET_STYLE_FOR_STRIKE) {
+ HandleGetStyleForStrike(fd, pickle, iter, fds.get());
+ } else if (kind == LinuxSandbox::METHOD_MAKE_SHARED_MEMORY_SEGMENT) {
+ HandleMakeSharedMemorySegment(fd, pickle, iter, fds.get());
+ } else if (kind == LinuxSandbox::METHOD_MATCH_WITH_FALLBACK) {
+ HandleMatchWithFallback(fd, pickle, iter, fds.get());
+ }
+}
+
+int SandboxIPCHandler::FindOrAddPath(const SkString& path) {
+ int count = paths_.count();
+ for (int i = 0; i < count; ++i) {
+ if (path == *paths_[i])
+ return i;
+ }
+ *paths_.append() = new SkString(path);
+ return count;
+}
+
+void SandboxIPCHandler::HandleFontMatchRequest(
+ int fd,
+ const Pickle& pickle,
+ PickleIterator iter,
+ const std::vector<base::ScopedFD*>& fds) {
+ uint32_t requested_style;
+ std::string family;
+ if (!pickle.ReadString(&iter, &family) ||
+ !pickle.ReadUInt32(&iter, &requested_style))
+ return;
+
+ SkFontConfigInterface::FontIdentity result_identity;
+ SkString result_family;
+ SkTypeface::Style result_style;
+ SkFontConfigInterface* fc =
+ SkFontConfigInterface::GetSingletonDirectInterface();
+ const bool r =
+ fc->matchFamilyName(family.c_str(),
+ static_cast<SkTypeface::Style>(requested_style),
+ &result_identity,
+ &result_family,
+ &result_style);
+
+ Pickle reply;
+ if (!r) {
+ reply.WriteBool(false);
+ } else {
+ // Stash away the returned path, so we can give it an ID (index)
+ // which will later be given to us in a request to open the file.
+ int index = FindOrAddPath(result_identity.fString);
+ result_identity.fID = static_cast<uint32_t>(index);
+
+ reply.WriteBool(true);
+ skia::WriteSkString(&reply, result_family);
+ skia::WriteSkFontIdentity(&reply, result_identity);
+ reply.WriteUInt32(result_style);
+ }
+ SendRendererReply(fds, reply, -1);
+}
+
+void SandboxIPCHandler::HandleFontOpenRequest(
+ int fd,
+ const Pickle& pickle,
+ PickleIterator iter,
+ const std::vector<base::ScopedFD*>& fds) {
+ uint32_t index;
+ if (!pickle.ReadUInt32(&iter, &index))
+ return;
+ if (index >= static_cast<uint32_t>(paths_.count()))
+ return;
+ const int result_fd = open(paths_[index]->c_str(), O_RDONLY);
+
+ Pickle reply;
+ if (result_fd == -1) {
+ reply.WriteBool(false);
+ } else {
+ reply.WriteBool(true);
+ }
+
+ // The receiver will have its own access to the file, so we will close it
+ // after this send.
+ SendRendererReply(fds, reply, result_fd);
+
+ if (result_fd >= 0) {
+ int err = IGNORE_EINTR(close(result_fd));
+ DCHECK(!err);
+ }
+}
+
+void SandboxIPCHandler::HandleGetFallbackFontForChar(
+ int fd,
+ const Pickle& pickle,
+ PickleIterator iter,
+ const std::vector<base::ScopedFD*>& fds) {
+ // The other side of this call is
+ // content/common/child_process_sandbox_support_impl_linux.cc
+
+ EnsureWebKitInitialized();
+ WebUChar32 c;
+ if (!pickle.ReadInt(&iter, &c))
+ return;
+
+ std::string preferred_locale;
+ if (!pickle.ReadString(&iter, &preferred_locale))
+ return;
+
+ blink::WebFallbackFont fallbackFont;
+ WebFontInfo::fallbackFontForChar(c, preferred_locale.c_str(), &fallbackFont);
+
+ Pickle reply;
+ if (fallbackFont.name.data()) {
+ reply.WriteString(fallbackFont.name.data());
+ } else {
+ reply.WriteString(std::string());
+ }
+ if (fallbackFont.filename.data()) {
+ reply.WriteString(fallbackFont.filename.data());
+ } else {
+ reply.WriteString(std::string());
+ }
+ reply.WriteInt(fallbackFont.ttcIndex);
+ reply.WriteBool(fallbackFont.isBold);
+ reply.WriteBool(fallbackFont.isItalic);
+ SendRendererReply(fds, reply, -1);
+}
+
+void SandboxIPCHandler::HandleGetStyleForStrike(
+ int fd,
+ const Pickle& pickle,
+ PickleIterator iter,
+ const std::vector<base::ScopedFD*>& fds) {
+ std::string family;
+ int sizeAndStyle;
+
+ if (!pickle.ReadString(&iter, &family) ||
+ !pickle.ReadInt(&iter, &sizeAndStyle)) {
+ return;
+ }
+
+ EnsureWebKitInitialized();
+ blink::WebFontRenderStyle style;
+ WebFontInfo::renderStyleForStrike(family.c_str(), sizeAndStyle, &style);
+
+ Pickle reply;
+ reply.WriteInt(style.useBitmaps);
+ reply.WriteInt(style.useAutoHint);
+ reply.WriteInt(style.useHinting);
+ reply.WriteInt(style.hintStyle);
+ reply.WriteInt(style.useAntiAlias);
+ reply.WriteInt(style.useSubpixelRendering);
+ reply.WriteInt(style.useSubpixelPositioning);
+
+ SendRendererReply(fds, reply, -1);
+}
+
+void SandboxIPCHandler::HandleLocaltime(
+ int fd,
+ const Pickle& pickle,
+ PickleIterator iter,
+ const std::vector<base::ScopedFD*>& fds) {
+ // The other side of this call is in zygote_main_linux.cc
+
+ std::string time_string;
+ if (!pickle.ReadString(&iter, &time_string) ||
+ time_string.size() != sizeof(time_t)) {
+ return;
+ }
+
+ time_t time;
+ memcpy(&time, time_string.data(), sizeof(time));
+ // We use localtime here because we need the tm_zone field to be filled
+ // out. Since we are a single-threaded process, this is safe.
+ const struct tm* expanded_time = localtime(&time);
+
+ std::string result_string;
+ const char* time_zone_string = "";
+ if (expanded_time != NULL) {
+ result_string = std::string(reinterpret_cast<const char*>(expanded_time),
+ sizeof(struct tm));
+ time_zone_string = expanded_time->tm_zone;
+ }
+
+ Pickle reply;
+ reply.WriteString(result_string);
+ reply.WriteString(time_zone_string);
+ SendRendererReply(fds, reply, -1);
+}
+
+void SandboxIPCHandler::HandleMakeSharedMemorySegment(
+ int fd,
+ const Pickle& pickle,
+ PickleIterator iter,
+ const std::vector<base::ScopedFD*>& fds) {
+ base::SharedMemoryCreateOptions options;
+ uint32_t size;
+ if (!pickle.ReadUInt32(&iter, &size))
+ return;
+ options.size = size;
+ if (!pickle.ReadBool(&iter, &options.executable))
+ return;
+ int shm_fd = -1;
+ base::SharedMemory shm;
+ if (shm.Create(options))
+ shm_fd = shm.handle().fd;
+ Pickle reply;
+ SendRendererReply(fds, reply, shm_fd);
+}
+
+void SandboxIPCHandler::HandleMatchWithFallback(
+ int fd,
+ const Pickle& pickle,
+ PickleIterator iter,
+ const std::vector<base::ScopedFD*>& fds) {
+ // Unlike the other calls, for which we are an indirection in front of
+ // WebKit or Skia, this call is always made via this sandbox helper
+ // process. Therefore the fontconfig code goes in here directly.
+
+ std::string face;
+ bool is_bold, is_italic;
+ uint32 charset, fallback_family;
+
+ if (!pickle.ReadString(&iter, &face) || face.empty() ||
+ !pickle.ReadBool(&iter, &is_bold) ||
+ !pickle.ReadBool(&iter, &is_italic) ||
+ !pickle.ReadUInt32(&iter, &charset) ||
+ !pickle.ReadUInt32(&iter, &fallback_family)) {
+ return;
+ }
+
+ FcLangSet* langset = FcLangSetCreate();
+ bool is_lgc = MSCharSetToFontconfig(langset, charset);
+
+ FcPattern* pattern = FcPatternCreate();
+ FcPatternAddString(
+ pattern, FC_FAMILY, reinterpret_cast<const FcChar8*>(face.c_str()));
+
+ // TODO(thestig) Check if we can access Chrome's per-script font preference
+ // here and select better default fonts for non-LGC case.
+ std::string generic_font_name;
+ if (is_lgc) {
+ switch (fallback_family) {
+ case PP_BROWSERFONT_TRUSTED_FAMILY_SERIF:
+ generic_font_name = "Times New Roman";
+ break;
+ case PP_BROWSERFONT_TRUSTED_FAMILY_SANSSERIF:
+ generic_font_name = "Arial";
+ break;
+ case PP_BROWSERFONT_TRUSTED_FAMILY_MONOSPACE:
+ generic_font_name = "Courier New";
+ break;
+ }
+ }
+ if (!generic_font_name.empty()) {
+ const FcChar8* fc_generic_font_name =
+ reinterpret_cast<const FcChar8*>(generic_font_name.c_str());
+ FcPatternAddString(pattern, FC_FAMILY, fc_generic_font_name);
+ }
+
+ if (is_bold)
+ FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
+ if (is_italic)
+ FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
+ FcPatternAddLangSet(pattern, FC_LANG, langset);
+ FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
+ FcConfigSubstitute(NULL, pattern, FcMatchPattern);
+ FcDefaultSubstitute(pattern);
+
+ FcResult result;
+ FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
+ int font_fd = -1;
+ int good_enough_index = -1;
+ bool good_enough_index_set = false;
+
+ if (font_set) {
+ for (int i = 0; i < font_set->nfont; ++i) {
+ FcPattern* current = font_set->fonts[i];
+
+ // Older versions of fontconfig have a bug where they cannot select
+ // only scalable fonts so we have to manually filter the results.
+ FcBool is_scalable;
+ if (FcPatternGetBool(current, FC_SCALABLE, 0, &is_scalable) !=
+ FcResultMatch ||
+ !is_scalable) {
+ continue;
+ }
+
+ FcChar8* c_filename;
+ if (FcPatternGetString(current, FC_FILE, 0, &c_filename) !=
+ FcResultMatch) {
+ continue;
+ }
+
+ // We only want to return sfnt (TrueType) based fonts. We don't have a
+ // very good way of detecting this so we'll filter based on the
+ // filename.
+ bool is_sfnt = false;
+ static const char kSFNTExtensions[][5] = {".ttf", ".otc", ".TTF", ".ttc",
+ ""};
+ const size_t filename_len = strlen(reinterpret_cast<char*>(c_filename));
+ for (unsigned j = 0;; j++) {
+ if (kSFNTExtensions[j][0] == 0) {
+ // None of the extensions matched.
+ break;
+ }
+ const size_t ext_len = strlen(kSFNTExtensions[j]);
+ if (filename_len > ext_len &&
+ memcmp(c_filename + filename_len - ext_len,
+ kSFNTExtensions[j],
+ ext_len) == 0) {
+ is_sfnt = true;
+ break;
+ }
+ }
+
+ if (!is_sfnt)
+ continue;
+
+ // This font is good enough to pass muster, but we might be able to do
+ // better with subsequent ones.
+ if (!good_enough_index_set) {
+ good_enough_index = i;
+ good_enough_index_set = true;
+ }
+
+ FcValue matrix;
+ bool have_matrix = FcPatternGet(current, FC_MATRIX, 0, &matrix) == 0;
+
+ if (is_italic && have_matrix) {
+ // we asked for an italic font, but fontconfig is giving us a
+ // non-italic font with a transformation matrix.
+ continue;
+ }
+
+ FcValue embolden;
+ const bool have_embolden =
+ FcPatternGet(current, FC_EMBOLDEN, 0, &embolden) == 0;
+
+ if (is_bold && have_embolden) {
+ // we asked for a bold font, but fontconfig gave us a non-bold font
+ // and asked us to apply fake bolding.
+ continue;
+ }
+
+ font_fd = open(reinterpret_cast<char*>(c_filename), O_RDONLY);
+ if (font_fd >= 0)
+ break;
+ }
+ }
+
+ if (font_fd == -1 && good_enough_index_set) {
+ // We didn't find a font that we liked, so we fallback to something
+ // acceptable.
+ FcPattern* current = font_set->fonts[good_enough_index];
+ FcChar8* c_filename;
+ FcPatternGetString(current, FC_FILE, 0, &c_filename);
+ font_fd = open(reinterpret_cast<char*>(c_filename), O_RDONLY);
+ }
+
+ if (font_set)
+ FcFontSetDestroy(font_set);
+ FcPatternDestroy(pattern);
+
+ Pickle reply;
+ SendRendererReply(fds, reply, font_fd);
+
+ if (font_fd >= 0) {
+ if (IGNORE_EINTR(close(font_fd)) < 0)
+ PLOG(ERROR) << "close";
+ }
+}
+
+void SandboxIPCHandler::SendRendererReply(
+ const std::vector<base::ScopedFD*>& fds,
+ const Pickle& reply,
+ int reply_fd) {
+ struct msghdr msg;
+ memset(&msg, 0, sizeof(msg));
+ struct iovec iov = {const_cast<void*>(reply.data()), reply.size()};
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ char control_buffer[CMSG_SPACE(sizeof(int))];
+
+ if (reply_fd != -1) {
+ struct stat st;
+ if (fstat(reply_fd, &st) == 0 && S_ISDIR(st.st_mode)) {
+ LOG(FATAL) << "Tried to send a directory descriptor over sandbox IPC";
+ // We must never send directory descriptors to a sandboxed process
+ // because they can use openat with ".." elements in the path in order
+ // to escape the sandbox and reach the real filesystem.
+ }
+
+ struct cmsghdr* cmsg;
+ msg.msg_control = control_buffer;
+ msg.msg_controllen = sizeof(control_buffer);
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ memcpy(CMSG_DATA(cmsg), &reply_fd, sizeof(reply_fd));
+ msg.msg_controllen = cmsg->cmsg_len;
+ }
+
+ if (HANDLE_EINTR(sendmsg(fds[0]->get(), &msg, MSG_DONTWAIT)) < 0)
+ PLOG(ERROR) << "sendmsg";
+}
+
+SandboxIPCHandler::~SandboxIPCHandler() {
+ paths_.deleteAll();
+ if (webkit_platform_support_)
+ blink::shutdownWithoutV8();
+
+ if (IGNORE_EINTR(close(lifeline_fd_)) < 0)
+ PLOG(ERROR) << "close";
+ if (IGNORE_EINTR(close(browser_socket_)) < 0)
+ PLOG(ERROR) << "close";
+}
+
+void SandboxIPCHandler::EnsureWebKitInitialized() {
+ if (webkit_platform_support_)
+ return;
+ webkit_platform_support_.reset(new BlinkPlatformImpl);
+ blink::initializeWithoutV8(webkit_platform_support_.get());
+}
+
+} // namespace content
diff --git a/chromium/content/browser/renderer_host/sandbox_ipc_linux.h b/chromium/content/browser/renderer_host/sandbox_ipc_linux.h
new file mode 100644
index 00000000000..fc986a6189c
--- /dev/null
+++ b/chromium/content/browser/renderer_host/sandbox_ipc_linux.h
@@ -0,0 +1,87 @@
+// 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.
+
+// http://code.google.com/p/chromium/wiki/LinuxSandboxIPC
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_SANDBOX_IPC_H_
+#define CONTENT_BROWSER_RENDERER_HOST_SANDBOX_IPC_H_
+
+#include <vector>
+
+#include "base/files/scoped_file.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/pickle.h"
+#include "base/threading/simple_thread.h"
+#include "content/child/blink_platform_impl.h"
+#include "skia/ext/skia_utils_base.h"
+
+namespace content {
+
+class SandboxIPCHandler : public base::DelegateSimpleThread::Delegate {
+ public:
+ // lifeline_fd: the read end of a pipe which the main thread holds
+ // the other end of.
+ // browser_socket: the browser's end of the sandbox IPC socketpair.
+ SandboxIPCHandler(int lifeline_fd, int browser_socket);
+ virtual ~SandboxIPCHandler();
+
+ virtual void Run() OVERRIDE;
+
+ private:
+ void EnsureWebKitInitialized();
+
+ int FindOrAddPath(const SkString& path);
+
+ void HandleRequestFromRenderer(int fd);
+
+ void HandleFontMatchRequest(int fd,
+ const Pickle& pickle,
+ PickleIterator iter,
+ const std::vector<base::ScopedFD*>& fds);
+
+ void HandleFontOpenRequest(int fd,
+ const Pickle& pickle,
+ PickleIterator iter,
+ const std::vector<base::ScopedFD*>& fds);
+
+ void HandleGetFallbackFontForChar(int fd,
+ const Pickle& pickle,
+ PickleIterator iter,
+ const std::vector<base::ScopedFD*>& fds);
+
+ void HandleGetStyleForStrike(int fd,
+ const Pickle& pickle,
+ PickleIterator iter,
+ const std::vector<base::ScopedFD*>& fds);
+
+ void HandleLocaltime(int fd,
+ const Pickle& pickle,
+ PickleIterator iter,
+ const std::vector<base::ScopedFD*>& fds);
+
+ void HandleMakeSharedMemorySegment(int fd,
+ const Pickle& pickle,
+ PickleIterator iter,
+ const std::vector<base::ScopedFD*>& fds);
+
+ void HandleMatchWithFallback(int fd,
+ const Pickle& pickle,
+ PickleIterator iter,
+ const std::vector<base::ScopedFD*>& fds);
+
+ void SendRendererReply(const std::vector<base::ScopedFD*>& fds,
+ const Pickle& reply,
+ int reply_fd);
+
+ const int lifeline_fd_;
+ const int browser_socket_;
+ scoped_ptr<BlinkPlatformImpl> webkit_platform_support_;
+ SkTDArray<SkString*> paths_;
+
+ DISALLOW_COPY_AND_ASSIGN(SandboxIPCHandler);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_SANDBOX_IPC_H_
diff --git a/chromium/content/browser/renderer_host/socket_stream_dispatcher_host.cc b/chromium/content/browser/renderer_host/socket_stream_dispatcher_host.cc
index 677c281932c..b08b119ed92 100644
--- a/chromium/content/browser/renderer_host/socket_stream_dispatcher_host.cc
+++ b/chromium/content/browser/renderer_host/socket_stream_dispatcher_host.cc
@@ -32,7 +32,8 @@ SocketStreamDispatcherHost::SocketStreamDispatcherHost(
int render_process_id,
const GetRequestContextCallback& request_context_callback,
ResourceContext* resource_context)
- : render_process_id_(render_process_id),
+ : BrowserMessageFilter(SocketStreamMsgStart),
+ render_process_id_(render_process_id),
request_context_callback_(request_context_callback),
resource_context_(resource_context),
weak_ptr_factory_(this),
@@ -40,18 +41,18 @@ SocketStreamDispatcherHost::SocketStreamDispatcherHost(
net::WebSocketJob::EnsureInit();
}
-bool SocketStreamDispatcherHost::OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) {
+bool SocketStreamDispatcherHost::OnMessageReceived(
+ const IPC::Message& message) {
if (on_shutdown_)
return false;
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(SocketStreamDispatcherHost, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(SocketStreamDispatcherHost, message)
IPC_MESSAGE_HANDLER(SocketStreamHostMsg_Connect, OnConnect)
IPC_MESSAGE_HANDLER(SocketStreamHostMsg_SendData, OnSendData)
IPC_MESSAGE_HANDLER(SocketStreamHostMsg_Close, OnCloseReq)
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return handled;
}
@@ -144,22 +145,45 @@ void SocketStreamDispatcherHost::OnSSLCertificateError(
GlobalRequestID request_id(-1, socket_id);
SSLManager::OnSSLCertificateError(
weak_ptr_factory_.GetWeakPtr(), request_id, ResourceType::SUB_RESOURCE,
- socket->url(), render_process_id_, socket_stream_host->render_view_id(),
+ socket->url(), render_process_id_, socket_stream_host->render_frame_id(),
ssl_info, fatal);
}
bool SocketStreamDispatcherHost::CanGetCookies(net::SocketStream* socket,
const GURL& url) {
+ int socket_id = SocketStreamHost::SocketIdFromSocketStream(socket);
+ if (socket_id == kNoSocketId) {
+ return false;
+ }
+ SocketStreamHost* socket_stream_host = hosts_.Lookup(socket_id);
+ DCHECK(socket_stream_host);
return GetContentClient()->browser()->AllowGetCookie(
- url, url, net::CookieList(), resource_context_, 0, MSG_ROUTING_NONE);
+ url,
+ url,
+ net::CookieList(),
+ resource_context_,
+ render_process_id_,
+ socket_stream_host->render_frame_id());
}
bool SocketStreamDispatcherHost::CanSetCookie(net::SocketStream* request,
const GURL& url,
const std::string& cookie_line,
net::CookieOptions* options) {
+ int socket_id = SocketStreamHost::SocketIdFromSocketStream(request);
+ if (socket_id == kNoSocketId) {
+ return false;
+ }
+ SocketStreamHost* socket_stream_host = hosts_.Lookup(socket_id);
+ DCHECK(socket_stream_host);
return GetContentClient()->browser()->AllowSetCookie(
- url, url, cookie_line, resource_context_, 0, MSG_ROUTING_NONE, options);
+ url,
+ url,
+ cookie_line,
+ resource_context_,
+ render_process_id_,
+ socket_stream_host->render_frame_id(),
+ options);
}
void SocketStreamDispatcherHost::CancelSSLRequest(
@@ -190,16 +214,16 @@ void SocketStreamDispatcherHost::ContinueSSLRequest(
}
SocketStreamDispatcherHost::~SocketStreamDispatcherHost() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
Shutdown();
}
// Message handlers called by OnMessageReceived.
-void SocketStreamDispatcherHost::OnConnect(int render_view_id,
+void SocketStreamDispatcherHost::OnConnect(int render_frame_id,
const GURL& url,
int socket_id) {
DVLOG(2) << "SocketStreamDispatcherHost::OnConnect"
- << " render_view_id=" << render_view_id
+ << " render_frame_id=" << render_frame_id
<< " url=" << url
<< " socket_id=" << socket_id;
DCHECK_NE(kNoSocketId, socket_id);
@@ -223,7 +247,8 @@ void SocketStreamDispatcherHost::OnConnect(int render_view_id,
// Note that the SocketStreamHost is responsible for checking that |url|
// is valid.
SocketStreamHost* socket_stream_host =
- new SocketStreamHost(this, render_view_id, socket_id);
+ new SocketStreamHost(this, render_process_id_, render_frame_id,
+ socket_id);
hosts_.AddWithID(socket_stream_host, socket_id);
socket_stream_host->Connect(url, GetURLRequestContext());
DVLOG(2) << "SocketStreamDispatcherHost::OnConnect -> " << socket_id;
@@ -266,7 +291,7 @@ net::URLRequestContext* SocketStreamDispatcherHost::GetURLRequestContext() {
}
void SocketStreamDispatcherHost::Shutdown() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
// TODO(ukai): Implement IDMap::RemoveAll().
for (IDMap<SocketStreamHost>::const_iterator iter(&hosts_);
!iter.IsAtEnd();
diff --git a/chromium/content/browser/renderer_host/socket_stream_dispatcher_host.h b/chromium/content/browser/renderer_host/socket_stream_dispatcher_host.h
index 958eada3a57..c0836138826 100644
--- a/chromium/content/browser/renderer_host/socket_stream_dispatcher_host.h
+++ b/chromium/content/browser/renderer_host/socket_stream_dispatcher_host.h
@@ -40,8 +40,7 @@ class SocketStreamDispatcherHost
ResourceContext* resource_context);
// BrowserMessageFilter:
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
// Make this object inactive.
// Remove all active SocketStreamHost objects.
@@ -76,7 +75,7 @@ class SocketStreamDispatcherHost
private:
// Message handlers called by OnMessageReceived.
- void OnConnect(int render_view_id, const GURL& url, int socket_id);
+ void OnConnect(int render_frame_id, const GURL& url, int socket_id);
void OnSendData(int socket_id, const std::vector<char>& data);
void OnCloseReq(int socket_id);
diff --git a/chromium/content/browser/renderer_host/socket_stream_host.cc b/chromium/content/browser/renderer_host/socket_stream_host.cc
index ecf6ceec80c..364b8d09dde 100644
--- a/chromium/content/browser/renderer_host/socket_stream_host.cc
+++ b/chromium/content/browser/renderer_host/socket_stream_host.cc
@@ -6,6 +6,7 @@
#include "base/logging.h"
#include "content/common/socket_stream.h"
+#include "content/public/browser/content_browser_client.h"
#include "net/socket_stream/socket_stream_job.h"
#include "net/url_request/url_request_context.h"
@@ -28,13 +29,15 @@ class SocketStreamId : public net::SocketStream::UserData {
SocketStreamHost::SocketStreamHost(
net::SocketStream::Delegate* delegate,
- int render_view_id,
+ int child_id,
+ int render_frame_id,
int socket_id)
: delegate_(delegate),
- render_view_id_(render_view_id),
+ child_id_(child_id),
+ render_frame_id_(render_frame_id),
socket_id_(socket_id) {
DCHECK_NE(socket_id_, kNoSocketId);
- VLOG(1) << "SocketStreamHost: render_view_id=" << render_view_id
+ VLOG(1) << "SocketStreamHost: render_frame_id=" << render_frame_id
<< " socket_id=" << socket_id_;
}
@@ -51,7 +54,7 @@ int SocketStreamHost::SocketIdFromSocketStream(
SocketStreamHost::~SocketStreamHost() {
VLOG(1) << "SocketStreamHost destructed socket_id=" << socket_id_;
- job_->set_context(NULL);
+ job_->DetachContext();
job_->DetachDelegate();
}
@@ -60,8 +63,10 @@ void SocketStreamHost::Connect(const GURL& url,
VLOG(1) << "SocketStreamHost::Connect url=" << url;
job_ = net::SocketStreamJob::CreateSocketStreamJob(
url, delegate_, request_context->transport_security_state(),
- request_context->ssl_config_service());
- job_->set_context(request_context);
+ request_context->ssl_config_service(),
+ request_context,
+ GetContentClient()->browser()->OverrideCookieStoreForRenderProcess(
+ child_id_));
job_->SetUserData(kSocketIdKey, new SocketStreamId(socket_id_));
job_->Connect();
}
diff --git a/chromium/content/browser/renderer_host/socket_stream_host.h b/chromium/content/browser/renderer_host/socket_stream_host.h
index 237e915e64c..da4716c00af 100644
--- a/chromium/content/browser/renderer_host/socket_stream_host.h
+++ b/chromium/content/browser/renderer_host/socket_stream_host.h
@@ -29,14 +29,15 @@ namespace content {
class SocketStreamHost {
public:
SocketStreamHost(net::SocketStream::Delegate* delegate,
- int render_view_id,
+ int child_id,
+ int render_frame_id,
int socket_id);
~SocketStreamHost();
// Gets socket_id associated with |socket|.
static int SocketIdFromSocketStream(const net::SocketStream* socket);
- int render_view_id() const { return render_view_id_; }
+ int render_frame_id() const { return render_frame_id_; }
int socket_id() const { return socket_id_; }
// Starts to open connection to |url|.
@@ -66,7 +67,8 @@ class SocketStreamHost {
private:
net::SocketStream::Delegate* delegate_;
- int render_view_id_;
+ int child_id_;
+ int render_frame_id_;
int socket_id_;
scoped_refptr<net::SocketStreamJob> job_;
diff --git a/chromium/content/browser/renderer_host/software_frame_manager.cc b/chromium/content/browser/renderer_host/software_frame_manager.cc
index 147c21eab65..08ac5000a14 100644
--- a/chromium/content/browser/renderer_host/software_frame_manager.cc
+++ b/chromium/content/browser/renderer_host/software_frame_manager.cc
@@ -7,13 +7,16 @@
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/debug/alias.h"
+#include "base/numerics/safe_math.h"
+#include "cc/resources/shared_bitmap.h"
#include "content/browser/renderer_host/dip_util.h"
+#include "content/common/host_shared_bitmap_manager.h"
#include "content/public/browser/user_metrics.h"
namespace {
void ReleaseMailbox(scoped_refptr<content::SoftwareFrame> frame,
- unsigned sync_point,
+ uint32 sync_point,
bool lost_resource) {}
} // namespace
@@ -28,13 +31,12 @@ class CONTENT_EXPORT SoftwareFrame : public base::RefCounted<SoftwareFrame> {
friend class base::RefCounted<SoftwareFrame>;
friend class SoftwareFrameManager;
- SoftwareFrame(
- base::WeakPtr<SoftwareFrameManagerClient> frame_manager_client,
- uint32 output_surface_id,
- unsigned frame_id,
- float frame_device_scale_factor,
- gfx::Size frame_size_pixels,
- scoped_ptr<base::SharedMemory> shared_memory);
+ SoftwareFrame(base::WeakPtr<SoftwareFrameManagerClient> frame_manager_client,
+ uint32 output_surface_id,
+ unsigned frame_id,
+ float frame_device_scale_factor,
+ gfx::Size frame_size_pixels,
+ scoped_ptr<cc::SharedBitmap> shared_bitmap);
~SoftwareFrame();
base::WeakPtr<SoftwareFrameManagerClient> frame_manager_client_;
@@ -42,7 +44,7 @@ class CONTENT_EXPORT SoftwareFrame : public base::RefCounted<SoftwareFrame> {
const unsigned frame_id_;
float frame_device_scale_factor_;
const gfx::Size frame_size_pixels_;
- scoped_ptr<base::SharedMemory> shared_memory_;
+ scoped_ptr<cc::SharedBitmap> shared_bitmap_;
DISALLOW_COPY_AND_ASSIGN(SoftwareFrame);
};
@@ -53,13 +55,13 @@ SoftwareFrame::SoftwareFrame(
unsigned frame_id,
float frame_device_scale_factor,
gfx::Size frame_size_pixels,
- scoped_ptr<base::SharedMemory> shared_memory)
+ scoped_ptr<cc::SharedBitmap> shared_bitmap)
: frame_manager_client_(frame_manager_client),
output_surface_id_(output_surface_id),
frame_id_(frame_id),
frame_device_scale_factor_(frame_device_scale_factor),
frame_size_pixels_(frame_size_pixels),
- shared_memory_(shared_memory.Pass()) {}
+ shared_bitmap_(shared_bitmap.Pass()) {}
SoftwareFrame::~SoftwareFrame() {
if (frame_manager_client_) {
@@ -84,50 +86,23 @@ bool SoftwareFrameManager::SwapToNewFrame(
const cc::SoftwareFrameData* frame_data,
float frame_device_scale_factor,
base::ProcessHandle process_handle) {
-
-#ifdef OS_WIN
- scoped_ptr<base::SharedMemory> shared_memory(
- new base::SharedMemory(frame_data->handle, true,
- process_handle));
-#else
- scoped_ptr<base::SharedMemory> shared_memory(
- new base::SharedMemory(frame_data->handle, true));
-#endif
-
- // The NULL handle is used in testing.
- if (base::SharedMemory::IsHandleValid(shared_memory->handle())) {
- const size_t size_in_bytes = 4 * frame_data->size.GetArea();
-#ifdef OS_WIN
- if (!shared_memory->Map(0)) {
- DLOG(ERROR) << "Unable to map renderer memory.";
- RecordAction(
- UserMetricsAction("BadMessageTerminate_SharedMemoryManager1"));
- return false;
- }
-
- if (shared_memory->mapped_size() < size_in_bytes) {
- DLOG(ERROR) << "Shared memory too small for given rectangle";
- RecordAction(
- UserMetricsAction("BadMessageTerminate_SharedMemoryManager2"));
- return false;
- }
-#else
- if (!shared_memory->Map(size_in_bytes)) {
- DLOG(ERROR) << "Unable to map renderer memory.";
- RecordAction(
- UserMetricsAction("BadMessageTerminate_SharedMemoryManager1"));
- return false;
- }
-#endif
+ scoped_ptr<cc::SharedBitmap> shared_bitmap =
+ HostSharedBitmapManager::current()->GetSharedBitmapFromId(
+ frame_data->size, frame_data->bitmap_id);
+
+ if (!shared_bitmap) {
+ RecordAction(
+ base::UserMetricsAction("BadMessageTerminate_SharedMemoryManager1"));
+ return false;
}
- scoped_refptr<SoftwareFrame> next_frame(new SoftwareFrame(
- client_,
- output_surface_id,
- frame_data->id,
- frame_device_scale_factor,
- frame_data->size,
- shared_memory.Pass()));
+ scoped_refptr<SoftwareFrame> next_frame(
+ new SoftwareFrame(client_,
+ output_surface_id,
+ frame_data->id,
+ frame_device_scale_factor,
+ frame_data->size,
+ shared_bitmap.Pass()));
current_frame_.swap(next_frame);
return true;
}
@@ -150,7 +125,11 @@ void SoftwareFrameManager::SwapToNewFrameComplete(bool visible) {
void SoftwareFrameManager::SetVisibility(bool visible) {
if (HasCurrentFrame()) {
- RendererFrameManager::GetInstance()->SetFrameVisibility(this, visible);
+ if (visible) {
+ RendererFrameManager::GetInstance()->LockFrame(this);
+ } else {
+ RendererFrameManager::GetInstance()->UnlockFrame(this);
+ }
}
}
@@ -163,17 +142,16 @@ void SoftwareFrameManager::GetCurrentFrameMailbox(
cc::TextureMailbox* mailbox,
scoped_ptr<cc::SingleReleaseCallback>* callback) {
DCHECK(HasCurrentFrame());
- *mailbox = cc::TextureMailbox(
- current_frame_->shared_memory_.get(), current_frame_->frame_size_pixels_);
+ *mailbox = cc::TextureMailbox(current_frame_->shared_bitmap_->memory(),
+ current_frame_->frame_size_pixels_);
*callback = cc::SingleReleaseCallback::Create(
base::Bind(ReleaseMailbox, current_frame_));
}
void* SoftwareFrameManager::GetCurrentFramePixels() const {
DCHECK(HasCurrentFrame());
- DCHECK(base::SharedMemory::IsHandleValid(
- current_frame_->shared_memory_->handle()));
- return current_frame_->shared_memory_->memory();
+ DCHECK(current_frame_->shared_bitmap_);
+ return current_frame_->shared_bitmap_->pixels();
}
float SoftwareFrameManager::GetCurrentFrameDeviceScaleFactor() const {
diff --git a/chromium/content/browser/renderer_host/software_frame_manager_unittest.cc b/chromium/content/browser/renderer_host/software_frame_manager_unittest.cc
index e234f57605a..e700f69f155 100644
--- a/chromium/content/browser/renderer_host/software_frame_manager_unittest.cc
+++ b/chromium/content/browser/renderer_host/software_frame_manager_unittest.cc
@@ -6,7 +6,10 @@
#include <vector>
+#include "base/memory/scoped_vector.h"
+#include "base/memory/shared_memory.h"
#include "base/sys_info.h"
+#include "content/common/host_shared_bitmap_manager.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
@@ -18,9 +21,12 @@ class FakeSoftwareFrameManagerClient : public SoftwareFrameManagerClient {
software_frame_manager_.reset(new SoftwareFrameManager(
weak_ptr_factory_.GetWeakPtr()));
}
- virtual ~FakeSoftwareFrameManagerClient() {}
- virtual void SoftwareFrameWasFreed(
- uint32 output_surface_id, unsigned frame_id) OVERRIDE {
+ virtual ~FakeSoftwareFrameManagerClient() {
+ HostSharedBitmapManager::current()->ProcessRemoved(
+ base::GetCurrentProcessHandle());
+ }
+ virtual void SoftwareFrameWasFreed(uint32 output_surface_id,
+ unsigned frame_id) OVERRIDE {
freed_frames_.push_back(std::make_pair(output_surface_id, frame_id));
}
virtual void ReleaseReferencesToSoftwareFrame() OVERRIDE {
@@ -32,7 +38,13 @@ class FakeSoftwareFrameManagerClient : public SoftwareFrameManagerClient {
frame.id = frame_id;
frame.size = gfx::Size(1, 1);
frame.damage_rect = gfx::Rect(frame.size);
- frame.handle = base::SharedMemory::NULLHandle();
+ frame.bitmap_id = cc::SharedBitmap::GenerateId();
+ scoped_ptr<base::SharedMemory> memory =
+ make_scoped_ptr(new base::SharedMemory);
+ memory->CreateAnonymous(4);
+ HostSharedBitmapManager::current()->ChildAllocatedSharedBitmap(
+ 4, memory->handle(), base::GetCurrentProcessHandle(), frame.bitmap_id);
+ allocated_memory_.push_back(memory.release());
return software_frame_manager_->SwapToNewFrame(
output_surface, &frame, 1.0, base::GetCurrentProcessHandle());
}
@@ -46,6 +58,7 @@ class FakeSoftwareFrameManagerClient : public SoftwareFrameManagerClient {
private:
std::vector<std::pair<uint32,unsigned> > freed_frames_;
size_t evicted_count_;
+ ScopedVector<base::SharedMemory> allocated_memory_;
scoped_ptr<SoftwareFrameManager> software_frame_manager_;
base::WeakPtrFactory<FakeSoftwareFrameManagerClient>
diff --git a/chromium/content/browser/renderer_host/software_layer_mac.h b/chromium/content/browser/renderer_host/software_layer_mac.h
new file mode 100644
index 00000000000..687070e500e
--- /dev/null
+++ b/chromium/content/browser/renderer_host/software_layer_mac.h
@@ -0,0 +1,22 @@
+// 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_RENDERER_HOST_SOFTWARE_LAYER_MAC_H_
+#define CONTENT_BROWSER_RENDERER_HOST_SOFTWARE_LAYER_MAC_H_
+
+#import <Cocoa/Cocoa.h>
+
+#include "ui/gfx/geometry/size.h"
+
+@interface SoftwareLayer : CALayer
+- (id)init;
+
+- (void)setContentsToData:(const void *)data
+ withRowBytes:(size_t)rowBytes
+ withPixelSize:(gfx::Size)pixelSize
+ withScaleFactor:(float)scaleFactor;
+@end
+
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_SOFTWARE_LAYER_MAC_H_
diff --git a/chromium/content/browser/renderer_host/software_layer_mac.mm b/chromium/content/browser/renderer_host/software_layer_mac.mm
new file mode 100644
index 00000000000..896e3d25595
--- /dev/null
+++ b/chromium/content/browser/renderer_host/software_layer_mac.mm
@@ -0,0 +1,66 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/software_layer_mac.h"
+
+#include "base/debug/trace_event.h"
+#include "base/mac/mac_util.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/mac/sdk_forward_declarations.h"
+#include "ui/base/cocoa/animation_utils.h"
+
+@implementation SoftwareLayer
+
+- (id)init {
+ if (self = [super init]) {
+ [self setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)];
+ [self setAnchorPoint:CGPointMake(0, 0)];
+ // Setting contents gravity is necessary to prevent the layer from being
+ // scaled during dyanmic resizes (especially with devtools open).
+ [self setContentsGravity:kCAGravityTopLeft];
+ }
+ return self;
+}
+
+- (void)setContentsToData:(const void *)data
+ withRowBytes:(size_t)rowBytes
+ withPixelSize:(gfx::Size)pixelSize
+ withScaleFactor:(float)scaleFactor {
+ TRACE_EVENT0("browser", "-[SoftwareLayer setContentsToData]");
+
+ // Disable animating the contents change or the scale factor change.
+ ScopedCAActionDisabler disabler;
+
+ // Set the contents of the software CALayer to be a CGImage with the provided
+ // pixel data. Make a copy of the data before backing the image with them,
+ // because the same buffer will be reused for the next frame.
+ base::ScopedCFTypeRef<CFDataRef> dataCopy(
+ CFDataCreate(NULL,
+ static_cast<const UInt8 *>(data),
+ rowBytes * pixelSize.height()));
+ base::ScopedCFTypeRef<CGDataProviderRef> dataProvider(
+ CGDataProviderCreateWithCFData(dataCopy));
+ base::ScopedCFTypeRef<CGImageRef> image(
+ CGImageCreate(pixelSize.width(),
+ pixelSize.height(),
+ 8,
+ 32,
+ rowBytes,
+ base::mac::GetSystemColorSpace(),
+ kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host,
+ dataProvider,
+ NULL,
+ false,
+ kCGRenderingIntentDefault));
+ [self setContents:(id)image.get()];
+
+ // Set the contents scale of the software CALayer.
+ if ([self respondsToSelector:(@selector(contentsScale))] &&
+ [self respondsToSelector:(@selector(setContentsScale:))] &&
+ [self contentsScale] != scaleFactor) {
+ [self setContentsScale:scaleFactor];
+ }
+}
+
+@end
diff --git a/chromium/content/browser/renderer_host/text_input_client_mac.h b/chromium/content/browser/renderer_host/text_input_client_mac.h
index 53318f95cb5..6a079471352 100644
--- a/chromium/content/browser/renderer_host/text_input_client_mac.h
+++ b/chromium/content/browser/renderer_host/text_input_client_mac.h
@@ -7,6 +7,7 @@
#import <Cocoa/Cocoa.h>
+#include "base/mac/scoped_block.h"
#include "base/mac/scoped_nsobject.h"
#include "base/synchronization/condition_variable.h"
#include "base/synchronization/lock.h"
@@ -32,6 +33,12 @@ class RenderWidgetHost;
// requires getting information from the renderer synchronously. Rather than
// using an actual sync IPC message, a normal async ViewMsg is used with a lock
// and condition (managed by this service).
+//
+// Mac OS 10.8 introduced -[NSResponder quickLookWithEvent:].
+// We can use it to implement asynchronous dictionary lookup when the user
+// taps a word using three fingers.
+// But currently the "Look Up in Dictionary" context menu item still goes
+// through the above synchronous IPC.
class CONTENT_EXPORT TextInputClientMac {
public:
// Returns the singleton instance.
@@ -61,6 +68,19 @@ class CONTENT_EXPORT TextInputClientMac {
void SetFirstRectAndSignal(NSRect first_rect);
void SetSubstringAndSignal(NSAttributedString* string);
+ // This async method is invoked from RenderWidgetHostViewCocoa's
+ // -quickLookWithEvent:, when the user taps a word using 3 fingers.
+ // The reply callback will be invoked from the IO thread, the caller is
+ // responsible for bouncing to the main thread if necessary.
+ // The callback parameters provide the attributed word under the point and
+ // the lower left baseline point of the text.
+ void GetStringAtPoint(RenderWidgetHost* rwh,
+ gfx::Point point,
+ void (^replyHandler)(NSAttributedString*, NSPoint));
+ // This is called on the IO thread when we get the renderer's reply for
+ // GetStringAtPoint.
+ void GetStringAtPointReply(NSAttributedString*, NSPoint);
+
private:
friend struct DefaultSingletonTraits<TextInputClientMac>;
TextInputClientMac();
@@ -82,6 +102,8 @@ class CONTENT_EXPORT TextInputClientMac {
base::Lock lock_;
base::ConditionVariable condition_;
+ base::mac::ScopedBlock<void(^)(NSAttributedString*, NSPoint)> replyHandler_;
+
DISALLOW_COPY_AND_ASSIGN(TextInputClientMac);
};
diff --git a/chromium/content/browser/renderer_host/text_input_client_mac.mm b/chromium/content/browser/renderer_host/text_input_client_mac.mm
index b9d900a704e..131ab92b1e3 100644
--- a/chromium/content/browser/renderer_host/text_input_client_mac.mm
+++ b/chromium/content/browser/renderer_host/text_input_client_mac.mm
@@ -33,6 +33,24 @@ TextInputClientMac* TextInputClientMac::GetInstance() {
return Singleton<TextInputClientMac>::get();
}
+void TextInputClientMac::GetStringAtPoint(
+ RenderWidgetHost* rwh,
+ gfx::Point point,
+ void (^replyHandler)(NSAttributedString*, NSPoint)) {
+ DCHECK(replyHandler_.get() == nil);
+ replyHandler_.reset(replyHandler, base::scoped_policy::RETAIN);
+ RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(rwh);
+ rwhi->Send(new TextInputClientMsg_StringAtPoint(rwhi->GetRoutingID(), point));
+}
+
+void TextInputClientMac::GetStringAtPointReply(NSAttributedString* string,
+ NSPoint point) {
+ if (replyHandler_.get()) {
+ replyHandler_.get()(string, point);
+ replyHandler_.reset();
+ }
+}
+
NSUInteger TextInputClientMac::GetCharacterIndexAtPoint(RenderWidgetHost* rwh,
gfx::Point point) {
base::TimeTicks start = base::TimeTicks::Now();
diff --git a/chromium/content/browser/renderer_host/text_input_client_mac_unittest.mm b/chromium/content/browser/renderer_host/text_input_client_mac_unittest.mm
index 0993018e8f4..eddff7b3764 100644
--- a/chromium/content/browser/renderer_host/text_input_client_mac_unittest.mm
+++ b/chromium/content/browser/renderer_host/text_input_client_mac_unittest.mm
@@ -35,8 +35,7 @@ class MockRenderWidgetHostDelegate : public RenderWidgetHostDelegate {
class TextInputClientMacTest : public testing::Test {
public:
TextInputClientMacTest()
- : message_loop_(base::MessageLoop::TYPE_UI),
- browser_context_(),
+ : browser_context_(),
process_factory_(),
delegate_(),
widget_(&delegate_,
@@ -74,7 +73,7 @@ class TextInputClientMacTest : public testing::Test {
private:
friend class ScopedTestingThread;
- base::MessageLoop message_loop_;
+ base::MessageLoopForUI message_loop_;
TestBrowserContext browser_context_;
// Gets deleted when the last RWH in the "process" gets destroyed.
@@ -105,9 +104,8 @@ class ScopedTestingThread {
// Adapter for OnMessageReceived to ignore return type so it can be posted
// to a MessageLoop.
void CallOnMessageReceived(scoped_refptr<TextInputClientMessageFilter> filter,
- const IPC::Message& message,
- bool* message_was_ok) {
- filter->OnMessageReceived(message, message_was_ok);
+ const IPC::Message& message) {
+ filter->OnMessageReceived(message);
}
} // namespace
@@ -154,11 +152,10 @@ TEST_F(TextInputClientMacTest, NotFoundCharacterIndex) {
scoped_ptr<IPC::Message> message(
new TextInputClientReplyMsg_GotCharacterIndexForPoint(
widget()->GetRoutingID(), kNotFoundValue));
- bool message_ok = true;
// Set |WTF::notFound| to the index |kTaskDelayMs| after the previous
// setting.
PostTask(FROM_HERE,
- base::Bind(&CallOnMessageReceived, filter, *message, &message_ok),
+ base::Bind(&CallOnMessageReceived, filter, *message),
base::TimeDelta::FromMilliseconds(kTaskDelayMs) * 2);
NSUInteger index = service()->GetCharacterIndexAtPoint(
diff --git a/chromium/content/browser/renderer_host/text_input_client_message_filter.h b/chromium/content/browser/renderer_host/text_input_client_message_filter.h
index 5a1405ae5ff..4706346202e 100644
--- a/chromium/content/browser/renderer_host/text_input_client_message_filter.h
+++ b/chromium/content/browser/renderer_host/text_input_client_message_filter.h
@@ -9,6 +9,7 @@
#include "content/public/browser/browser_message_filter.h"
namespace gfx {
+class Point;
class Range;
class Rect;
}
@@ -24,14 +25,16 @@ class CONTENT_EXPORT TextInputClientMessageFilter
explicit TextInputClientMessageFilter(int child_id);
// BrowserMessageFilter override:
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
protected:
virtual ~TextInputClientMessageFilter();
private:
// IPC Message handlers:
+ void OnGotStringAtPoint(
+ const mac::AttributedStringCoder::EncodedString& encoded_string,
+ const gfx::Point& point);
void OnGotCharacterIndexForPoint(size_t index);
void OnGotFirstRectForRange(const gfx::Rect& rect);
void OnGotStringFromRange(
diff --git a/chromium/content/browser/renderer_host/text_input_client_message_filter.mm b/chromium/content/browser/renderer_host/text_input_client_message_filter.mm
index 7af5a8ff2bd..76507005778 100644
--- a/chromium/content/browser/renderer_host/text_input_client_message_filter.mm
+++ b/chromium/content/browser/renderer_host/text_input_client_message_filter.mm
@@ -10,22 +10,22 @@
#include "content/common/text_input_client_messages.h"
#include "content/public/browser/render_widget_host_view.h"
#include "ipc/ipc_message_macros.h"
+#include "ui/gfx/point.h"
#include "ui/gfx/range/range.h"
-#include "ui/gfx/rect.h"
namespace content {
TextInputClientMessageFilter::TextInputClientMessageFilter(int child_id)
- : BrowserMessageFilter(),
+ : BrowserMessageFilter(TextInputClientMsgStart),
child_process_id_(child_id) {
}
bool TextInputClientMessageFilter::OnMessageReceived(
- const IPC::Message& message,
- bool* message_was_ok) {
+ const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(TextInputClientMessageFilter, message,
- *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(TextInputClientMessageFilter, message)
+ IPC_MESSAGE_HANDLER(TextInputClientReplyMsg_GotStringAtPoint,
+ OnGotStringAtPoint)
IPC_MESSAGE_HANDLER(TextInputClientReplyMsg_GotCharacterIndexForPoint,
OnGotCharacterIndexForPoint)
IPC_MESSAGE_HANDLER(TextInputClientReplyMsg_GotFirstRectForRange,
@@ -33,12 +33,21 @@ bool TextInputClientMessageFilter::OnMessageReceived(
IPC_MESSAGE_HANDLER(TextInputClientReplyMsg_GotStringForRange,
OnGotStringFromRange)
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return handled;
}
TextInputClientMessageFilter::~TextInputClientMessageFilter() {}
+void TextInputClientMessageFilter::OnGotStringAtPoint(
+ const mac::AttributedStringCoder::EncodedString& encoded_string,
+ const gfx::Point& point) {
+ TextInputClientMac* service = TextInputClientMac::GetInstance();
+ NSAttributedString* string =
+ mac::AttributedStringCoder::Decode(&encoded_string);
+ service->GetStringAtPointReply(string, NSPointFromCGPoint(point.ToCGPoint()));
+}
+
void TextInputClientMessageFilter::OnGotCharacterIndexForPoint(size_t index) {
TextInputClientMac* service = TextInputClientMac::GetInstance();
// |index| could be WTF::notFound (-1) and its value is different from
diff --git a/chromium/content/browser/renderer_host/ui_events_helper.cc b/chromium/content/browser/renderer_host/ui_events_helper.cc
index 1dc312db591..c521ee60452 100644
--- a/chromium/content/browser/renderer_host/ui_events_helper.cc
+++ b/chromium/content/browser/renderer_host/ui_events_helper.cc
@@ -4,6 +4,7 @@
#include "content/browser/renderer_host/ui_events_helper.h"
+#include "content/common/input/web_touch_event_traits.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
#include "ui/events/event.h"
#include "ui/events/event_constants.h"
@@ -120,19 +121,12 @@ bool MakeUITouchEventsFromWebTouchEvents(
const blink::WebTouchPoint& point = touch.touches[i];
if (WebTouchPointStateToEventType(point.state) != type)
continue;
- // In aura, the touch-event needs to be in the screen coordinate, since the
- // touch-event is routed to RootWindow first. In Windows, on the other hand,
- // the touch-event is dispatched directly to the gesture-recognizer, so the
- // location needs to be in the local coordinate space.
-#if defined(USE_AURA)
- gfx::Point location;
+ // ui events start in the co-ordinate space of the EventDispatcher.
+ gfx::PointF location;
if (coordinate_system == LOCAL_COORDINATES)
- location = gfx::Point(point.position.x, point.position.y);
+ location = point.position;
else
- location = gfx::Point(point.screenPosition.x, point.screenPosition.y);
-#else
- gfx::Point location(point.position.x, point.position.y);
-#endif
+ location = point.screenPosition;
ui::TouchEvent* uievent = new ui::TouchEvent(type,
location,
flags,
@@ -178,6 +172,10 @@ blink::WebGestureEvent MakeWebGestureEventFromUIEvent(
break;
case ui::ET_GESTURE_SCROLL_BEGIN:
gesture_event.type = blink::WebInputEvent::GestureScrollBegin;
+ gesture_event.data.scrollBegin.deltaXHint =
+ event.details().scroll_x_hint();
+ gesture_event.data.scrollBegin.deltaYHint =
+ event.details().scroll_y_hint();
break;
case ui::ET_GESTURE_SCROLL_UPDATE:
gesture_event.type = blink::WebInputEvent::GestureScrollUpdate;
@@ -228,14 +226,14 @@ blink::WebGestureEvent MakeWebGestureEventFromUIEvent(
break;
case ui::ET_GESTURE_BEGIN:
case ui::ET_GESTURE_END:
- case ui::ET_GESTURE_MULTIFINGER_SWIPE:
+ case ui::ET_GESTURE_SWIPE:
gesture_event.type = blink::WebInputEvent::Undefined;
break;
default:
NOTREACHED() << "Unknown gesture type: " << event.type();
}
- gesture_event.sourceDevice = blink::WebGestureEvent::Touchscreen;
+ gesture_event.sourceDevice = blink::WebGestureDeviceTouchscreen;
gesture_event.modifiers = EventFlagsToWebEventModifiers(event.flags());
gesture_event.timeStampSeconds = event.time_stamp().InSecondsF();
@@ -316,7 +314,7 @@ blink::WebTouchPoint* UpdateWebTouchEventFromUIEvent(
point->position.x = event.x();
point->position.y = event.y();
- const gfx::Point root_point = event.root_location();
+ const gfx::PointF& root_point = event.root_location_f();
point->screenPosition.x = root_point.x();
point->screenPosition.y = root_point.y();
@@ -328,8 +326,9 @@ blink::WebTouchPoint* UpdateWebTouchEventFromUIEvent(
}
// Update the type of the touch event.
- web_event->type = TouchEventTypeFromEvent(event);
- web_event->timeStampSeconds = event.time_stamp().InSecondsF();
+ WebTouchEventTraits::ResetType(TouchEventTypeFromEvent(event),
+ event.time_stamp().InSecondsF(),
+ web_event);
web_event->modifiers = EventFlagsToWebEventModifiers(event.flags());
return point;
diff --git a/chromium/content/browser/renderer_host/ui_events_helper.h b/chromium/content/browser/renderer_host/ui_events_helper.h
index 51fd46f0a50..883ba48cf00 100644
--- a/chromium/content/browser/renderer_host/ui_events_helper.h
+++ b/chromium/content/browser/renderer_host/ui_events_helper.h
@@ -6,8 +6,8 @@
#define CONTENT_BROWSER_RENDERER_HOST_UI_EVENTS_HELPER_H_
#include "base/memory/scoped_vector.h"
+#include "content/browser/renderer_host/event_with_latency_info.h"
#include "content/common/content_export.h"
-#include "content/port/browser/event_with_latency_info.h"
namespace blink {
class WebGestureEvent;
@@ -33,6 +33,10 @@ enum TouchEventCoordinateSystem {
// it is possible to create more than one ui::TouchEvents out of a single
// WebTouchEvent. All the ui::TouchEvent in the list will carry the same
// LatencyInfo the WebTouchEvent carries.
+// |coordinate_system| specifies which fields to use for the co-ordinates,
+// WebTouchPoint.position or WebTouchPoint.screenPosition. Is's up to the
+// caller to do any co-ordinate system mapping (typically to get them into
+// the Aura EventDispatcher co-ordinate system).
CONTENT_EXPORT bool MakeUITouchEventsFromWebTouchEvents(
const TouchEventWithLatencyInfo& touch,
ScopedVector<ui::TouchEvent>* list,
diff --git a/chromium/content/browser/renderer_host/web_input_event_aura.cc b/chromium/content/browser/renderer_host/web_input_event_aura.cc
index f35d37740e5..b754b494c81 100644
--- a/chromium/content/browser/renderer_host/web_input_event_aura.cc
+++ b/chromium/content/browser/renderer_host/web_input_event_aura.cc
@@ -63,20 +63,20 @@ blink::WebUChar GetControlCharacter(int windows_key_code, bool shift) {
#endif
#if defined(OS_WIN)
blink::WebMouseEvent MakeUntranslatedWebMouseEventFromNativeEvent(
- base::NativeEvent native_event);
+ const base::NativeEvent& native_event);
blink::WebMouseWheelEvent MakeUntranslatedWebMouseWheelEventFromNativeEvent(
- base::NativeEvent native_event);
+ const base::NativeEvent& native_event);
blink::WebKeyboardEvent MakeWebKeyboardEventFromNativeEvent(
- base::NativeEvent native_event);
+ const base::NativeEvent& native_event);
blink::WebGestureEvent MakeWebGestureEventFromNativeEvent(
- base::NativeEvent native_event);
+ const base::NativeEvent& native_event);
#elif defined(USE_X11)
blink::WebKeyboardEvent MakeWebKeyboardEventFromAuraEvent(
ui::KeyEvent* event);
#elif defined(USE_OZONE)
blink::WebKeyboardEvent MakeWebKeyboardEventFromAuraEvent(
ui::KeyEvent* event) {
- base::NativeEvent native_event = event->native_event();
+ const base::NativeEvent& native_event = event->native_event();
ui::EventType type = ui::EventTypeFromNative(native_event);
blink::WebKeyboardEvent webkit_event;
@@ -133,18 +133,27 @@ blink::WebMouseWheelEvent MakeWebMouseWheelEventFromAuraEvent(
webkit_event.modifiers = EventFlagsToWebEventModifiers(event->flags());
webkit_event.timeStampSeconds = event->time_stamp().InSecondsF();
webkit_event.hasPreciseScrollingDeltas = true;
- webkit_event.deltaX = event->x_offset();
- if (event->x_offset_ordinal() != 0.f && event->x_offset() != 0.f) {
- webkit_event.accelerationRatioX =
- event->x_offset_ordinal() / event->x_offset();
+
+ float offset_ordinal_x = 0.f;
+ float offset_ordinal_y = 0.f;
+ if ((event->flags() & ui::EF_SHIFT_DOWN) != 0 && event->x_offset() == 0) {
+ webkit_event.deltaX = event->y_offset();
+ webkit_event.deltaY = 0;
+ offset_ordinal_x = event->y_offset_ordinal();
+ offset_ordinal_y = event->x_offset_ordinal();
+ } else {
+ webkit_event.deltaX = event->x_offset();
+ webkit_event.deltaY = event->y_offset();
+ offset_ordinal_x = event->x_offset_ordinal();
+ offset_ordinal_y = event->y_offset_ordinal();
}
+
+ if (offset_ordinal_x != 0.f && webkit_event.deltaX != 0.f)
+ webkit_event.accelerationRatioX = offset_ordinal_x / webkit_event.deltaX;
webkit_event.wheelTicksX = webkit_event.deltaX / kPixelsPerTick;
- webkit_event.deltaY = event->y_offset();
webkit_event.wheelTicksY = webkit_event.deltaY / kPixelsPerTick;
- if (event->y_offset_ordinal() != 0.f && event->y_offset() != 0.f) {
- webkit_event.accelerationRatioY =
- event->y_offset_ordinal() / event->y_offset();
- }
+ if (offset_ordinal_y != 0.f && webkit_event.deltaY != 0.f)
+ webkit_event.accelerationRatioY = offset_ordinal_y / webkit_event.deltaY;
return webkit_event;
}
@@ -168,7 +177,7 @@ blink::WebGestureEvent MakeWebGestureEventFromAuraEvent(
NOTREACHED() << "Unknown gesture type: " << event->type();
}
- webkit_event.sourceDevice = blink::WebGestureEvent::Touchpad;
+ webkit_event.sourceDevice = blink::WebGestureDeviceTouchpad;
webkit_event.modifiers = EventFlagsToWebEventModifiers(event->flags());
webkit_event.timeStampSeconds = event->time_stamp().InSecondsF();
return webkit_event;
@@ -278,6 +287,9 @@ blink::WebMouseWheelEvent MakeWebMouseWheelEvent(ui::ScrollEvent* event) {
}
blink::WebKeyboardEvent MakeWebKeyboardEvent(ui::KeyEvent* event) {
+ if (!event->HasNativeEvent())
+ return blink::WebKeyboardEvent();
+
// Windows can figure out whether or not to construct a RawKeyDown or a Char
// WebInputEvent based on the type of message carried in
// event->native_event(). X11 is not so fortunate, there is no separate
@@ -337,7 +349,7 @@ blink::WebGestureEvent MakeWebGestureEventFlingCancel() {
// All other fields are ignored on a GestureFlingCancel event.
gesture_event.type = blink::WebInputEvent::GestureFlingCancel;
- gesture_event.sourceDevice = blink::WebGestureEvent::Touchpad;
+ gesture_event.sourceDevice = blink::WebGestureDeviceTouchpad;
return gesture_event;
}
@@ -386,8 +398,15 @@ blink::WebMouseWheelEvent MakeWebMouseWheelEventFromAuraEvent(
webkit_event.button = blink::WebMouseEvent::ButtonNone;
webkit_event.modifiers = EventFlagsToWebEventModifiers(event->flags());
webkit_event.timeStampSeconds = event->time_stamp().InSecondsF();
- webkit_event.deltaX = event->x_offset();
- webkit_event.deltaY = event->y_offset();
+
+ if ((event->flags() & ui::EF_SHIFT_DOWN) != 0 && event->x_offset() == 0) {
+ webkit_event.deltaX = event->y_offset();
+ webkit_event.deltaY = 0;
+ } else {
+ webkit_event.deltaX = event->x_offset();
+ webkit_event.deltaY = event->y_offset();
+ }
+
webkit_event.wheelTicksX = webkit_event.deltaX / kPixelsPerTick;
webkit_event.wheelTicksY = webkit_event.deltaY / kPixelsPerTick;
diff --git a/chromium/content/browser/renderer_host/web_input_event_aurawin.cc b/chromium/content/browser/renderer_host/web_input_event_aurawin.cc
index 06e2dbf4592..549e2f2bb1d 100644
--- a/chromium/content/browser/renderer_host/web_input_event_aurawin.cc
+++ b/chromium/content/browser/renderer_host/web_input_event_aurawin.cc
@@ -14,31 +14,34 @@ namespace content {
// construct our pre-translated events.
blink::WebMouseEvent MakeUntranslatedWebMouseEventFromNativeEvent(
- base::NativeEvent native_event) {
+ const base::NativeEvent& native_event) {
return WebMouseEventBuilder::Build(native_event.hwnd,
native_event.message,
native_event.wParam,
- native_event.lParam);
+ native_event.lParam,
+ native_event.time);
}
blink::WebMouseWheelEvent MakeUntranslatedWebMouseWheelEventFromNativeEvent(
- base::NativeEvent native_event) {
+ const base::NativeEvent& native_event) {
return WebMouseWheelEventBuilder::Build(native_event.hwnd,
native_event.message,
native_event.wParam,
- native_event.lParam);
+ native_event.lParam,
+ native_event.time);
}
blink::WebKeyboardEvent MakeWebKeyboardEventFromNativeEvent(
- base::NativeEvent native_event) {
+ const base::NativeEvent& native_event) {
return WebKeyboardEventBuilder::Build(native_event.hwnd,
native_event.message,
native_event.wParam,
- native_event.lParam);
+ native_event.lParam,
+ native_event.time);
}
blink::WebGestureEvent MakeWebGestureEventFromNativeEvent(
- base::NativeEvent native_event) {
+ const base::NativeEvent& native_event) {
// TODO: Create gestures from native event.
NOTIMPLEMENTED();
return blink::WebGestureEvent();
diff --git a/chromium/content/browser/renderer_host/web_input_event_aurax11.cc b/chromium/content/browser/renderer_host/web_input_event_aurax11.cc
index 6bec4934bd7..6a9430a62d6 100644
--- a/chromium/content/browser/renderer_host/web_input_event_aurax11.cc
+++ b/chromium/content/browser/renderer_host/web_input_event_aurax11.cc
@@ -91,7 +91,7 @@ int XKeyEventToWindowsKeyCode(XKeyEvent* event) {
blink::WebKeyboardEvent MakeWebKeyboardEventFromAuraEvent(
ui::KeyEvent* event) {
- base::NativeEvent native_event = event->native_event();
+ const base::NativeEvent& native_event = event->native_event();
blink::WebKeyboardEvent webkit_event;
XKeyEvent* native_key_event = &native_event->xkey;
diff --git a/chromium/content/browser/renderer_host/webmenurunner_mac.h b/chromium/content/browser/renderer_host/webmenurunner_mac.h
index e1ce91a9e43..392ce9a0399 100644
--- a/chromium/content/browser/renderer_host/webmenurunner_mac.h
+++ b/chromium/content/browser/renderer_host/webmenurunner_mac.h
@@ -52,6 +52,9 @@
withBounds:(NSRect)bounds
initialIndex:(int)index;
+// Hides a popup menu if it's visible.
+- (void)hide;
+
// Returns the index of selected menu item, or its initial value (-1) if no item
// was selected.
- (int)indexOfSelectedItem;
diff --git a/chromium/content/browser/renderer_host/webmenurunner_mac.mm b/chromium/content/browser/renderer_host/webmenurunner_mac.mm
index bb33a007aa6..94e77453e91 100644
--- a/chromium/content/browser/renderer_host/webmenurunner_mac.mm
+++ b/chromium/content/browser/renderer_host/webmenurunner_mac.mm
@@ -148,6 +148,10 @@
index_ = [cell indexOfSelectedItem];
}
+- (void)hide {
+ [menu_ cancelTracking];
+}
+
- (int)indexOfSelectedItem {
return index_;
}
diff --git a/chromium/content/browser/renderer_host/websocket_dispatcher_host.cc b/chromium/content/browser/renderer_host/websocket_dispatcher_host.cc
index e7bb0286a81..2af2f0814c8 100644
--- a/chromium/content/browser/renderer_host/websocket_dispatcher_host.cc
+++ b/chromium/content/browser/renderer_host/websocket_dispatcher_host.cc
@@ -9,6 +9,7 @@
#include "base/callback.h"
#include "base/logging.h"
#include "base/stl_util.h"
+#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/renderer_host/websocket_host.h"
#include "content/common/websocket_messages.h"
@@ -24,24 +25,29 @@ typedef WebSocketDispatcherHost::WebSocketHostState WebSocketHostState;
} // namespace
WebSocketDispatcherHost::WebSocketDispatcherHost(
+ int process_id,
const GetRequestContextCallback& get_context_callback)
- : get_context_callback_(get_context_callback),
+ : BrowserMessageFilter(WebSocketMsgStart),
+ process_id_(process_id),
+ get_context_callback_(get_context_callback),
websocket_host_factory_(
base::Bind(&WebSocketDispatcherHost::CreateWebSocketHost,
base::Unretained(this))) {}
WebSocketDispatcherHost::WebSocketDispatcherHost(
+ int process_id,
const GetRequestContextCallback& get_context_callback,
const WebSocketHostFactory& websocket_host_factory)
- : get_context_callback_(get_context_callback),
+ : BrowserMessageFilter(WebSocketMsgStart),
+ process_id_(process_id),
+ get_context_callback_(get_context_callback),
websocket_host_factory_(websocket_host_factory) {}
WebSocketHost* WebSocketDispatcherHost::CreateWebSocketHost(int routing_id) {
return new WebSocketHost(routing_id, this, get_context_callback_.Run());
}
-bool WebSocketDispatcherHost::OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) {
+bool WebSocketDispatcherHost::OnMessageReceived(const IPC::Message& message) {
switch (message.type()) {
case WebSocketHostMsg_AddChannelRequest::ID:
case WebSocketMsg_SendFrame::ID:
@@ -74,7 +80,13 @@ bool WebSocketDispatcherHost::OnMessageReceived(const IPC::Message& message,
<< " from renderer.";
return true; // We handled the message (by ignoring it).
}
- return host->OnMessageReceived(message, message_was_ok);
+ return host->OnMessageReceived(message);
+}
+
+bool WebSocketDispatcherHost::CanReadRawCookies() const {
+ ChildProcessSecurityPolicyImpl* policy =
+ ChildProcessSecurityPolicyImpl::GetInstance();
+ return policy->CanReadRawCookies(process_id_);
}
WebSocketHost* WebSocketDispatcherHost::GetHost(int routing_id) const {
@@ -124,28 +136,39 @@ WebSocketHostState WebSocketDispatcherHost::SendFlowControl(int routing_id,
return SendOrDrop(new WebSocketMsg_FlowControl(routing_id, quota));
}
-WebSocketHostState WebSocketDispatcherHost::SendClosing(int routing_id) {
- // TODO(ricea): Implement the SendClosing IPC.
- return WEBSOCKET_HOST_ALIVE;
+WebSocketHostState WebSocketDispatcherHost::NotifyClosingHandshake(
+ int routing_id) {
+ return SendOrDrop(new WebSocketMsg_NotifyClosing(routing_id));
}
-WebSocketHostState WebSocketDispatcherHost::SendStartOpeningHandshake(
+WebSocketHostState WebSocketDispatcherHost::NotifyStartOpeningHandshake(
int routing_id, const WebSocketHandshakeRequest& request) {
return SendOrDrop(new WebSocketMsg_NotifyStartOpeningHandshake(
routing_id, request));
}
-WebSocketHostState WebSocketDispatcherHost::SendFinishOpeningHandshake(
+WebSocketHostState WebSocketDispatcherHost::NotifyFinishOpeningHandshake(
int routing_id, const WebSocketHandshakeResponse& response) {
return SendOrDrop(new WebSocketMsg_NotifyFinishOpeningHandshake(
routing_id, response));
}
+WebSocketHostState WebSocketDispatcherHost::NotifyFailure(
+ int routing_id,
+ const std::string& message) {
+ if (SendOrDrop(new WebSocketMsg_NotifyFailure(
+ routing_id, message)) == WEBSOCKET_HOST_DELETED) {
+ return WEBSOCKET_HOST_DELETED;
+ }
+ DeleteWebSocketHost(routing_id);
+ return WEBSOCKET_HOST_DELETED;
+}
+
WebSocketHostState WebSocketDispatcherHost::DoDropChannel(
int routing_id,
+ bool was_clean,
uint16 code,
const std::string& reason) {
- bool was_clean = true;
if (SendOrDrop(
new WebSocketMsg_DropChannel(routing_id, was_clean, code, reason)) ==
WEBSOCKET_HOST_DELETED)
diff --git a/chromium/content/browser/renderer_host/websocket_dispatcher_host.h b/chromium/content/browser/renderer_host/websocket_dispatcher_host.h
index 638178ee9b3..4179b71b2e2 100644
--- a/chromium/content/browser/renderer_host/websocket_dispatcher_host.h
+++ b/chromium/content/browser/renderer_host/websocket_dispatcher_host.h
@@ -45,18 +45,18 @@ class CONTENT_EXPORT WebSocketDispatcherHost : public BrowserMessageFilter {
WEBSOCKET_HOST_DELETED
};
- explicit WebSocketDispatcherHost(
+ WebSocketDispatcherHost(
+ int process_id,
const GetRequestContextCallback& get_context_callback);
// For testing. Specify a factory method that creates mock version of
// WebSocketHost.
- WebSocketDispatcherHost(
- const GetRequestContextCallback& get_context_callback,
- const WebSocketHostFactory& websocket_host_factory);
+ WebSocketDispatcherHost(int process_id,
+ const GetRequestContextCallback& get_context_callback,
+ const WebSocketHostFactory& websocket_host_factory);
// BrowserMessageFilter:
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
// The following methods are used by WebSocketHost::EventInterface to send
// IPCs from the browser to the renderer or child process. Any of them may
@@ -81,26 +81,38 @@ class CONTENT_EXPORT WebSocketDispatcherHost : public BrowserMessageFilter {
WebSocketHostState SendFlowControl(int routing_id,
int64 quota) WARN_UNUSED_RESULT;
- // Sends a WebSocketMsg_SendClosing IPC
- WebSocketHostState SendClosing(int routing_id) WARN_UNUSED_RESULT;
+ // Sends a WebSocketMsg_NotifyClosing IPC
+ WebSocketHostState NotifyClosingHandshake(int routing_id) WARN_UNUSED_RESULT;
// Sends a WebSocketMsg_NotifyStartOpeningHandshake IPC.
- WebSocketHostState SendStartOpeningHandshake(
+ WebSocketHostState NotifyStartOpeningHandshake(
int routing_id,
const WebSocketHandshakeRequest& request) WARN_UNUSED_RESULT;
// Sends a WebSocketMsg_NotifyFinishOpeningHandshake IPC.
- WebSocketHostState SendFinishOpeningHandshake(
+ WebSocketHostState NotifyFinishOpeningHandshake(
int routing_id,
const WebSocketHandshakeResponse& response) WARN_UNUSED_RESULT;
+ // Sends a WebSocketMsg_NotifyFailure IPC and deletes and unregisters the
+ // channel.
+ WebSocketHostState NotifyFailure(
+ int routing_id,
+ const std::string& message) WARN_UNUSED_RESULT;
+
// Sends a WebSocketMsg_DropChannel IPC and deletes and unregisters the
// channel.
WebSocketHostState DoDropChannel(
int routing_id,
+ bool was_clean,
uint16 code,
const std::string& reason) WARN_UNUSED_RESULT;
+ // Returns whether the associated renderer process can read raw cookies.
+ bool CanReadRawCookies() const;
+
+ int render_process_id() const { return process_id_; }
+
private:
typedef base::hash_map<int, WebSocketHost*> WebSocketHostTable;
@@ -126,6 +138,9 @@ class CONTENT_EXPORT WebSocketDispatcherHost : public BrowserMessageFilter {
// routing_id.
WebSocketHostTable hosts_;
+ // The the process ID of the associated renderer process.
+ const int process_id_;
+
// A callback which returns the appropriate net::URLRequestContext for us to
// use.
GetRequestContextCallback get_context_callback_;
diff --git a/chromium/content/browser/renderer_host/websocket_dispatcher_host_unittest.cc b/chromium/content/browser/renderer_host/websocket_dispatcher_host_unittest.cc
index e771a2c0002..e1506d99dd1 100644
--- a/chromium/content/browser/renderer_host/websocket_dispatcher_host_unittest.cc
+++ b/chromium/content/browser/renderer_host/websocket_dispatcher_host_unittest.cc
@@ -15,10 +15,14 @@
#include "ipc/ipc_message.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
+#include "url/origin.h"
namespace content {
namespace {
+// This number is unlikely to occur by chance.
+static const int kMagicRenderProcessId = 506116062;
+
// A mock of WebsocketHost which records received messages.
class MockWebSocketHost : public WebSocketHost {
public:
@@ -30,8 +34,7 @@ class MockWebSocketHost : public WebSocketHost {
virtual ~MockWebSocketHost() {}
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE{
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE{
received_messages_.push_back(message);
return true;
}
@@ -42,12 +45,12 @@ class MockWebSocketHost : public WebSocketHost {
class WebSocketDispatcherHostTest : public ::testing::Test {
public:
WebSocketDispatcherHostTest() {
- dispatcher_host_ =
- new WebSocketDispatcherHost(
- base::Bind(&WebSocketDispatcherHostTest::OnGetRequestContext,
- base::Unretained(this)),
- base::Bind(&WebSocketDispatcherHostTest::CreateWebSocketHost,
- base::Unretained(this)));
+ dispatcher_host_ = new WebSocketDispatcherHost(
+ kMagicRenderProcessId,
+ base::Bind(&WebSocketDispatcherHostTest::OnGetRequestContext,
+ base::Unretained(this)),
+ base::Bind(&WebSocketDispatcherHostTest::CreateWebSocketHost,
+ base::Unretained(this)));
}
virtual ~WebSocketDispatcherHostTest() {}
@@ -77,9 +80,12 @@ TEST_F(WebSocketDispatcherHostTest, Construct) {
}
TEST_F(WebSocketDispatcherHostTest, UnrelatedMessage) {
- bool message_was_ok = false;
IPC::Message message;
- EXPECT_FALSE(dispatcher_host_->OnMessageReceived(message, &message_was_ok));
+ EXPECT_FALSE(dispatcher_host_->OnMessageReceived(message));
+}
+
+TEST_F(WebSocketDispatcherHostTest, RenderProcessIdGetter) {
+ EXPECT_EQ(kMagicRenderProcessId, dispatcher_host_->render_process_id());
}
TEST_F(WebSocketDispatcherHostTest, AddChannelRequest) {
@@ -87,12 +93,12 @@ TEST_F(WebSocketDispatcherHostTest, AddChannelRequest) {
GURL socket_url("ws://example.com/test");
std::vector<std::string> requested_protocols;
requested_protocols.push_back("hello");
- GURL origin("http://example.com/test");
+ url::Origin origin("http://example.com/test");
+ int render_frame_id = -2;
WebSocketHostMsg_AddChannelRequest message(
- routing_id, socket_url, requested_protocols, origin);
+ routing_id, socket_url, requested_protocols, origin, render_frame_id);
- bool message_was_ok = false;
- ASSERT_TRUE(dispatcher_host_->OnMessageReceived(message, &message_was_ok));
+ ASSERT_TRUE(dispatcher_host_->OnMessageReceived(message));
ASSERT_EQ(1U, mock_hosts_.size());
MockWebSocketHost* host = mock_hosts_[0];
@@ -109,9 +115,8 @@ TEST_F(WebSocketDispatcherHostTest, SendFrameButNoHostYet) {
WebSocketMsg_SendFrame message(
routing_id, true, WEB_SOCKET_MESSAGE_TYPE_TEXT, data);
- bool message_was_ok = false;
// Expected to be ignored.
- EXPECT_TRUE(dispatcher_host_->OnMessageReceived(message, &message_was_ok));
+ EXPECT_TRUE(dispatcher_host_->OnMessageReceived(message));
EXPECT_EQ(0U, mock_hosts_.size());
}
@@ -122,21 +127,18 @@ TEST_F(WebSocketDispatcherHostTest, SendFrame) {
GURL socket_url("ws://example.com/test");
std::vector<std::string> requested_protocols;
requested_protocols.push_back("hello");
- GURL origin("http://example.com/test");
+ url::Origin origin("http://example.com/test");
+ int render_frame_id = -2;
WebSocketHostMsg_AddChannelRequest add_channel_message(
- routing_id, socket_url, requested_protocols, origin);
-
- bool message_was_ok = false;
+ routing_id, socket_url, requested_protocols, origin, render_frame_id);
- ASSERT_TRUE(dispatcher_host_->OnMessageReceived(
- add_channel_message, &message_was_ok));
+ ASSERT_TRUE(dispatcher_host_->OnMessageReceived(add_channel_message));
std::vector<char> data;
WebSocketMsg_SendFrame send_frame_message(
routing_id, true, WEB_SOCKET_MESSAGE_TYPE_TEXT, data);
- EXPECT_TRUE(dispatcher_host_->OnMessageReceived(
- send_frame_message, &message_was_ok));
+ EXPECT_TRUE(dispatcher_host_->OnMessageReceived(send_frame_message));
ASSERT_EQ(1U, mock_hosts_.size());
MockWebSocketHost* host = mock_hosts_[0];
diff --git a/chromium/content/browser/renderer_host/websocket_host.cc b/chromium/content/browser/renderer_host/websocket_host.cc
index aff172cb869..7f639180372 100644
--- a/chromium/content/browser/renderer_host/websocket_host.cc
+++ b/chromium/content/browser/renderer_host/websocket_host.cc
@@ -5,13 +5,23 @@
#include "content/browser/renderer_host/websocket_host.h"
#include "base/basictypes.h"
+#include "base/memory/weak_ptr.h"
#include "base/strings/string_util.h"
#include "content/browser/renderer_host/websocket_dispatcher_host.h"
+#include "content/browser/ssl/ssl_error_handler.h"
+#include "content/browser/ssl/ssl_manager.h"
#include "content/common/websocket_messages.h"
#include "ipc/ipc_message_macros.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_util.h"
+#include "net/ssl/ssl_info.h"
#include "net/websockets/websocket_channel.h"
#include "net/websockets/websocket_event_interface.h"
#include "net/websockets/websocket_frame.h" // for WebSocketFrameHeader::OpCode
+#include "net/websockets/websocket_handshake_request_info.h"
+#include "net/websockets/websocket_handshake_response_info.h"
+#include "url/origin.h"
namespace content {
@@ -74,36 +84,74 @@ ChannelState StateCast(WebSocketDispatcherHost::WebSocketHostState host_state) {
// renderer or child process via WebSocketDispatcherHost.
class WebSocketEventHandler : public net::WebSocketEventInterface {
public:
- WebSocketEventHandler(WebSocketDispatcherHost* dispatcher, int routing_id);
+ WebSocketEventHandler(WebSocketDispatcherHost* dispatcher,
+ int routing_id,
+ int render_frame_id);
virtual ~WebSocketEventHandler();
// net::WebSocketEventInterface implementation
- // TODO(ricea): Add |extensions| parameter to pass the list of enabled
- // WebSocket extensions through to the renderer to make it visible to
- // Javascript.
virtual ChannelState OnAddChannelResponse(
bool fail,
- const std::string& selected_subprotocol) OVERRIDE;
+ const std::string& selected_subprotocol,
+ const std::string& extensions) OVERRIDE;
virtual ChannelState OnDataFrame(bool fin,
WebSocketMessageType type,
const std::vector<char>& data) OVERRIDE;
virtual ChannelState OnClosingHandshake() OVERRIDE;
virtual ChannelState OnFlowControl(int64 quota) OVERRIDE;
- virtual ChannelState OnDropChannel(uint16 code,
+ virtual ChannelState OnDropChannel(bool was_clean,
+ uint16 code,
const std::string& reason) OVERRIDE;
+ virtual ChannelState OnFailChannel(const std::string& message) OVERRIDE;
+ virtual ChannelState OnStartOpeningHandshake(
+ scoped_ptr<net::WebSocketHandshakeRequestInfo> request) OVERRIDE;
+ virtual ChannelState OnFinishOpeningHandshake(
+ scoped_ptr<net::WebSocketHandshakeResponseInfo> response) OVERRIDE;
+ virtual ChannelState OnSSLCertificateError(
+ scoped_ptr<net::WebSocketEventInterface::SSLErrorCallbacks> callbacks,
+ const GURL& url,
+ const net::SSLInfo& ssl_info,
+ bool fatal) OVERRIDE;
private:
+ class SSLErrorHandlerDelegate : public SSLErrorHandler::Delegate {
+ public:
+ SSLErrorHandlerDelegate(
+ scoped_ptr<net::WebSocketEventInterface::SSLErrorCallbacks> callbacks);
+ virtual ~SSLErrorHandlerDelegate();
+
+ base::WeakPtr<SSLErrorHandler::Delegate> GetWeakPtr();
+
+ // SSLErrorHandler::Delegate methods
+ virtual void CancelSSLRequest(const GlobalRequestID& id,
+ int error,
+ const net::SSLInfo* ssl_info) OVERRIDE;
+ virtual void ContinueSSLRequest(const GlobalRequestID& id) OVERRIDE;
+
+ private:
+ scoped_ptr<net::WebSocketEventInterface::SSLErrorCallbacks> callbacks_;
+ base::WeakPtrFactory<SSLErrorHandlerDelegate> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(SSLErrorHandlerDelegate);
+ };
+
WebSocketDispatcherHost* const dispatcher_;
const int routing_id_;
+ const int render_frame_id_;
+ scoped_ptr<SSLErrorHandlerDelegate> ssl_error_handler_delegate_;
DISALLOW_COPY_AND_ASSIGN(WebSocketEventHandler);
};
WebSocketEventHandler::WebSocketEventHandler(
WebSocketDispatcherHost* dispatcher,
- int routing_id)
- : dispatcher_(dispatcher), routing_id_(routing_id) {}
+ int routing_id,
+ int render_frame_id)
+ : dispatcher_(dispatcher),
+ routing_id_(routing_id),
+ render_frame_id_(render_frame_id) {
+}
WebSocketEventHandler::~WebSocketEventHandler() {
DVLOG(1) << "WebSocketEventHandler destroyed routing_id=" << routing_id_;
@@ -111,12 +159,15 @@ WebSocketEventHandler::~WebSocketEventHandler() {
ChannelState WebSocketEventHandler::OnAddChannelResponse(
bool fail,
- const std::string& selected_protocol) {
+ const std::string& selected_protocol,
+ const std::string& extensions) {
DVLOG(3) << "WebSocketEventHandler::OnAddChannelResponse"
<< " routing_id=" << routing_id_ << " fail=" << fail
- << " selected_protocol=\"" << selected_protocol << "\"";
+ << " selected_protocol=\"" << selected_protocol << "\""
+ << " extensions=\"" << extensions << "\"";
+
return StateCast(dispatcher_->SendAddChannelResponse(
- routing_id_, fail, selected_protocol, std::string()));
+ routing_id_, fail, selected_protocol, extensions));
}
ChannelState WebSocketEventHandler::OnDataFrame(
@@ -126,6 +177,7 @@ ChannelState WebSocketEventHandler::OnDataFrame(
DVLOG(3) << "WebSocketEventHandler::OnDataFrame"
<< " routing_id=" << routing_id_ << " fin=" << fin
<< " type=" << type << " data is " << data.size() << " bytes";
+
return StateCast(dispatcher_->SendFrame(
routing_id_, fin, OpCodeToMessageType(type), data));
}
@@ -133,21 +185,136 @@ ChannelState WebSocketEventHandler::OnDataFrame(
ChannelState WebSocketEventHandler::OnClosingHandshake() {
DVLOG(3) << "WebSocketEventHandler::OnClosingHandshake"
<< " routing_id=" << routing_id_;
- return StateCast(dispatcher_->SendClosing(routing_id_));
+
+ return StateCast(dispatcher_->NotifyClosingHandshake(routing_id_));
}
ChannelState WebSocketEventHandler::OnFlowControl(int64 quota) {
DVLOG(3) << "WebSocketEventHandler::OnFlowControl"
<< " routing_id=" << routing_id_ << " quota=" << quota;
+
return StateCast(dispatcher_->SendFlowControl(routing_id_, quota));
}
-ChannelState WebSocketEventHandler::OnDropChannel(uint16 code,
+ChannelState WebSocketEventHandler::OnDropChannel(bool was_clean,
+ uint16 code,
const std::string& reason) {
DVLOG(3) << "WebSocketEventHandler::OnDropChannel"
- << " routing_id=" << routing_id_ << " code=" << code
- << " reason=\"" << reason << "\"";
- return StateCast(dispatcher_->DoDropChannel(routing_id_, code, reason));
+ << " routing_id=" << routing_id_ << " was_clean=" << was_clean
+ << " code=" << code << " reason=\"" << reason << "\"";
+
+ return StateCast(
+ dispatcher_->DoDropChannel(routing_id_, was_clean, code, reason));
+}
+
+ChannelState WebSocketEventHandler::OnFailChannel(const std::string& message) {
+ DVLOG(3) << "WebSocketEventHandler::OnFailChannel"
+ << " routing_id=" << routing_id_
+ << " message=\"" << message << "\"";
+
+ return StateCast(dispatcher_->NotifyFailure(routing_id_, message));
+}
+
+ChannelState WebSocketEventHandler::OnStartOpeningHandshake(
+ scoped_ptr<net::WebSocketHandshakeRequestInfo> request) {
+ bool should_send = dispatcher_->CanReadRawCookies();
+ DVLOG(3) << "WebSocketEventHandler::OnStartOpeningHandshake "
+ << "should_send=" << should_send;
+
+ if (!should_send)
+ return WebSocketEventInterface::CHANNEL_ALIVE;
+
+ WebSocketHandshakeRequest request_to_pass;
+ request_to_pass.url.Swap(&request->url);
+ net::HttpRequestHeaders::Iterator it(request->headers);
+ while (it.GetNext())
+ request_to_pass.headers.push_back(std::make_pair(it.name(), it.value()));
+ request_to_pass.headers_text =
+ base::StringPrintf("GET %s HTTP/1.1\r\n",
+ request_to_pass.url.spec().c_str()) +
+ request->headers.ToString();
+ request_to_pass.request_time = request->request_time;
+
+ return StateCast(dispatcher_->NotifyStartOpeningHandshake(routing_id_,
+ request_to_pass));
+}
+
+ChannelState WebSocketEventHandler::OnFinishOpeningHandshake(
+ scoped_ptr<net::WebSocketHandshakeResponseInfo> response) {
+ bool should_send = dispatcher_->CanReadRawCookies();
+ DVLOG(3) << "WebSocketEventHandler::OnFinishOpeningHandshake "
+ << "should_send=" << should_send;
+
+ if (!should_send)
+ return WebSocketEventInterface::CHANNEL_ALIVE;
+
+ WebSocketHandshakeResponse response_to_pass;
+ response_to_pass.url.Swap(&response->url);
+ response_to_pass.status_code = response->status_code;
+ response_to_pass.status_text.swap(response->status_text);
+ void* iter = NULL;
+ std::string name, value;
+ while (response->headers->EnumerateHeaderLines(&iter, &name, &value))
+ response_to_pass.headers.push_back(std::make_pair(name, value));
+ response_to_pass.headers_text =
+ net::HttpUtil::ConvertHeadersBackToHTTPResponse(
+ response->headers->raw_headers());
+ response_to_pass.response_time = response->response_time;
+
+ return StateCast(dispatcher_->NotifyFinishOpeningHandshake(routing_id_,
+ response_to_pass));
+}
+
+ChannelState WebSocketEventHandler::OnSSLCertificateError(
+ scoped_ptr<net::WebSocketEventInterface::SSLErrorCallbacks> callbacks,
+ const GURL& url,
+ const net::SSLInfo& ssl_info,
+ bool fatal) {
+ DVLOG(3) << "WebSocketEventHandler::OnSSLCertificateError"
+ << " routing_id=" << routing_id_ << " url=" << url.spec()
+ << " cert_status=" << ssl_info.cert_status << " fatal=" << fatal;
+ ssl_error_handler_delegate_.reset(
+ new SSLErrorHandlerDelegate(callbacks.Pass()));
+ // We don't need request_id to be unique so just make a fake one.
+ GlobalRequestID request_id(-1, -1);
+ SSLManager::OnSSLCertificateError(ssl_error_handler_delegate_->GetWeakPtr(),
+ request_id,
+ ResourceType::SUB_RESOURCE,
+ url,
+ dispatcher_->render_process_id(),
+ render_frame_id_,
+ ssl_info,
+ fatal);
+ // The above method is always asynchronous.
+ return WebSocketEventInterface::CHANNEL_ALIVE;
+}
+
+WebSocketEventHandler::SSLErrorHandlerDelegate::SSLErrorHandlerDelegate(
+ scoped_ptr<net::WebSocketEventInterface::SSLErrorCallbacks> callbacks)
+ : callbacks_(callbacks.Pass()), weak_ptr_factory_(this) {}
+
+WebSocketEventHandler::SSLErrorHandlerDelegate::~SSLErrorHandlerDelegate() {}
+
+base::WeakPtr<SSLErrorHandler::Delegate>
+WebSocketEventHandler::SSLErrorHandlerDelegate::GetWeakPtr() {
+ return weak_ptr_factory_.GetWeakPtr();
+}
+
+void WebSocketEventHandler::SSLErrorHandlerDelegate::CancelSSLRequest(
+ const GlobalRequestID& id,
+ int error,
+ const net::SSLInfo* ssl_info) {
+ DVLOG(3) << "SSLErrorHandlerDelegate::CancelSSLRequest"
+ << " error=" << error
+ << " cert_status=" << (ssl_info ? ssl_info->cert_status
+ : static_cast<net::CertStatus>(-1));
+ callbacks_->CancelSSLRequest(error, ssl_info);
+}
+
+void WebSocketEventHandler::SSLErrorHandlerDelegate::ContinueSSLRequest(
+ const GlobalRequestID& id) {
+ DVLOG(3) << "SSLErrorHandlerDelegate::ContinueSSLRequest";
+ callbacks_->ContinueSSLRequest();
}
} // namespace
@@ -155,39 +322,42 @@ ChannelState WebSocketEventHandler::OnDropChannel(uint16 code,
WebSocketHost::WebSocketHost(int routing_id,
WebSocketDispatcherHost* dispatcher,
net::URLRequestContext* url_request_context)
- : routing_id_(routing_id) {
+ : dispatcher_(dispatcher),
+ url_request_context_(url_request_context),
+ routing_id_(routing_id) {
DVLOG(1) << "WebSocketHost: created routing_id=" << routing_id;
- scoped_ptr<net::WebSocketEventInterface> event_interface(
- new WebSocketEventHandler(dispatcher, routing_id));
- channel_.reset(
- new net::WebSocketChannel(event_interface.Pass(), url_request_context));
}
WebSocketHost::~WebSocketHost() {}
-bool WebSocketHost::OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) {
+bool WebSocketHost::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(WebSocketHost, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(WebSocketHost, message)
IPC_MESSAGE_HANDLER(WebSocketHostMsg_AddChannelRequest, OnAddChannelRequest)
IPC_MESSAGE_HANDLER(WebSocketMsg_SendFrame, OnSendFrame)
IPC_MESSAGE_HANDLER(WebSocketMsg_FlowControl, OnFlowControl)
IPC_MESSAGE_HANDLER(WebSocketMsg_DropChannel, OnDropChannel)
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return handled;
}
void WebSocketHost::OnAddChannelRequest(
const GURL& socket_url,
const std::vector<std::string>& requested_protocols,
- const GURL& origin) {
+ const url::Origin& origin,
+ int render_frame_id) {
DVLOG(3) << "WebSocketHost::OnAddChannelRequest"
<< " routing_id=" << routing_id_ << " socket_url=\"" << socket_url
<< "\" requested_protocols=\""
- << JoinString(requested_protocols, ", ") << "\" origin=\"" << origin
- << "\"";
+ << JoinString(requested_protocols, ", ") << "\" origin=\""
+ << origin.string() << "\"";
+ DCHECK(!channel_);
+ scoped_ptr<net::WebSocketEventInterface> event_interface(
+ new WebSocketEventHandler(dispatcher_, routing_id_, render_frame_id));
+ channel_.reset(
+ new net::WebSocketChannel(event_interface.Pass(), url_request_context_));
channel_->SendAddChannelRequest(socket_url, requested_protocols, origin);
}
@@ -198,6 +368,7 @@ void WebSocketHost::OnSendFrame(bool fin,
<< " routing_id=" << routing_id_ << " fin=" << fin
<< " type=" << type << " data is " << data.size() << " bytes";
+ DCHECK(channel_);
channel_->SendFrame(fin, MessageTypeToOpCode(type), data);
}
@@ -205,6 +376,7 @@ void WebSocketHost::OnFlowControl(int64 quota) {
DVLOG(3) << "WebSocketHost::OnFlowControl"
<< " routing_id=" << routing_id_ << " quota=" << quota;
+ DCHECK(channel_);
channel_->SendFlowControl(quota);
}
@@ -215,9 +387,9 @@ void WebSocketHost::OnDropChannel(bool was_clean,
<< " routing_id=" << routing_id_ << " was_clean=" << was_clean
<< " code=" << code << " reason=\"" << reason << "\"";
+ DCHECK(channel_);
// TODO(yhirano): Handle |was_clean| appropriately.
channel_->StartClosingHandshake(code, reason);
}
-
} // namespace content
diff --git a/chromium/content/browser/renderer_host/websocket_host.h b/chromium/content/browser/renderer_host/websocket_host.h
index ba10f1c3885..21c77dcbbd5 100644
--- a/chromium/content/browser/renderer_host/websocket_host.h
+++ b/chromium/content/browser/renderer_host/websocket_host.h
@@ -14,6 +14,10 @@
class GURL;
+namespace url {
+class Origin;
+} // namespace url
+
namespace net {
class WebSocketChannel;
class URLRequestContext;
@@ -38,8 +42,7 @@ class CONTENT_EXPORT WebSocketHost {
// General message dispatch. WebSocketDispatcherHost::OnMessageReceived
// delegates to this method after looking up the |routing_id|.
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok);
+ virtual bool OnMessageReceived(const IPC::Message& message);
private:
// Handlers for each message type, dispatched by OnMessageReceived(), as
@@ -47,7 +50,8 @@ class CONTENT_EXPORT WebSocketHost {
void OnAddChannelRequest(const GURL& socket_url,
const std::vector<std::string>& requested_protocols,
- const GURL& origin);
+ const url::Origin& origin,
+ int render_frame_id);
void OnSendFrame(bool fin,
WebSocketMessageType type,
@@ -60,8 +64,14 @@ class CONTENT_EXPORT WebSocketHost {
// The channel we use to send events to the network.
scoped_ptr<net::WebSocketChannel> channel_;
+ // The WebSocketHostDispatcher that created this object.
+ WebSocketDispatcherHost* const dispatcher_;
+
+ // The URL request context for the channel.
+ net::URLRequestContext* const url_request_context_;
+
// The ID used to route messages.
- int routing_id_;
+ const int routing_id_;
DISALLOW_COPY_AND_ASSIGN(WebSocketHost);
};
diff --git a/chromium/content/browser/resolve_proxy_msg_helper.cc b/chromium/content/browser/resolve_proxy_msg_helper.cc
index 52d0987edb9..5f0a6cd52d7 100644
--- a/chromium/content/browser/resolve_proxy_msg_helper.cc
+++ b/chromium/content/browser/resolve_proxy_msg_helper.cc
@@ -16,18 +16,19 @@ namespace content {
ResolveProxyMsgHelper::ResolveProxyMsgHelper(
net::URLRequestContextGetter* getter)
- : context_getter_(getter),
+ : BrowserMessageFilter(ViewMsgStart),
+ context_getter_(getter),
proxy_service_(NULL) {
}
ResolveProxyMsgHelper::ResolveProxyMsgHelper(net::ProxyService* proxy_service)
- : proxy_service_(proxy_service) {
+ : BrowserMessageFilter(ViewMsgStart),
+ proxy_service_(proxy_service) {
}
-bool ResolveProxyMsgHelper::OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) {
+bool ResolveProxyMsgHelper::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(ResolveProxyMsgHelper, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(ResolveProxyMsgHelper, message)
IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_ResolveProxy, OnResolveProxy)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
diff --git a/chromium/content/browser/resolve_proxy_msg_helper.h b/chromium/content/browser/resolve_proxy_msg_helper.h
index a55e6698a3f..167d946e545 100644
--- a/chromium/content/browser/resolve_proxy_msg_helper.h
+++ b/chromium/content/browser/resolve_proxy_msg_helper.h
@@ -38,8 +38,7 @@ class CONTENT_EXPORT ResolveProxyMsgHelper : public BrowserMessageFilter {
explicit ResolveProxyMsgHelper(net::ProxyService* proxy_service);
// BrowserMessageFilter implementation
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
void OnResolveProxy(const GURL& url, IPC::Message* reply_msg);
diff --git a/chromium/content/browser/resolve_proxy_msg_helper_unittest.cc b/chromium/content/browser/resolve_proxy_msg_helper_unittest.cc
index 91f0243b2e7..023fe43567a 100644
--- a/chromium/content/browser/resolve_proxy_msg_helper_unittest.cc
+++ b/chromium/content/browser/resolve_proxy_msg_helper_unittest.cc
@@ -63,7 +63,6 @@ class ResolveProxyMsgHelperTest : public testing::Test, public IPC::Listener {
service_(
new net::ProxyService(new MockProxyConfigService, resolver_, NULL)),
helper_(new TestResolveProxyMsgHelper(service_.get(), this)),
- message_loop_(base::MessageLoop::TYPE_IO),
io_thread_(BrowserThread::IO, &message_loop_) {
test_sink_.AddFilter(this);
}
@@ -97,7 +96,7 @@ class ResolveProxyMsgHelperTest : public testing::Test, public IPC::Listener {
return true;
}
- base::MessageLoop message_loop_;
+ base::MessageLoopForIO message_loop_;
BrowserThreadImpl io_thread_;
IPC::TestSink test_sink_;
};
diff --git a/chromium/content/browser/resources/accessibility/accessibility.js b/chromium/content/browser/resources/accessibility/accessibility.js
index 5d98b48c62f..37af8e75d44 100644
--- a/chromium/content/browser/resources/accessibility/accessibility.js
+++ b/chromium/content/browser/resources/accessibility/accessibility.js
@@ -5,6 +5,24 @@
cr.define('accessibility', function() {
'use strict';
+ // Keep in sync with view_message_enums.h
+ var AccessibilityModeFlag = {
+ Platform: 1 << 0,
+ FullTree: 1 << 1
+ }
+
+ var AccessibilityMode = {
+ Off: 0,
+ Complete:
+ AccessibilityModeFlag.Platform | AccessibilityModeFlag.FullTree,
+ EditableTextOnly: AccessibilityModeFlag.Platform,
+ TreeOnly: AccessibilityModeFlag.FullTree
+ }
+
+ function isAccessibilityComplete(mode) {
+ return ((mode & AccessibilityMode.Complete) == AccessibilityMode.Complete);
+ }
+
function requestData() {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'targets-data.json', false);
@@ -60,7 +78,8 @@ cr.define('accessibility', function() {
}
function addGlobalAccessibilityModeToggle(global_a11y_mode) {
- $('toggle_global').textContent = (global_a11y_mode == 0 ? 'off' : 'on');
+ var full_a11y_on = isAccessibilityComplete(global_a11y_mode);
+ $('toggle_global').textContent = (full_a11y_on ? 'on' : 'off');
$('toggle_global').addEventListener('click',
toggleGlobalAccessibility);
}
@@ -92,7 +111,7 @@ cr.define('accessibility', function() {
row.appendChild(formatValue(data, properties[j]));
row.appendChild(createToggleAccessibilityElement(data));
- if (data['a11y_mode'] != 0) {
+ if (isAccessibilityComplete(data['a11y_mode'])) {
row.appendChild(document.createTextNode(' | '));
if ('tree' in data) {
row.appendChild(createShowAccessibilityTreeElement(data, row, true));
@@ -132,8 +151,8 @@ cr.define('accessibility', function() {
function createToggleAccessibilityElement(data) {
var link = document.createElement('a');
link.setAttribute('href', '#');
- var a11y_mode = data['a11y_mode'];
- link.textContent = 'accessibility ' + (a11y_mode == 0 ? 'off' : 'on');
+ var full_a11y_on = isAccessibilityComplete(data['a11y_mode']);
+ link.textContent = 'accessibility ' + (full_a11y_on ? 'on' : 'off');
link.addEventListener('click',
toggleAccessibility.bind(this, data, link));
return link;
diff --git a/chromium/content/browser/resources/devtools/devtools_pinch_cursor.png b/chromium/content/browser/resources/devtools/devtools_pinch_cursor.png
new file mode 100644
index 00000000000..2d82272129f
--- /dev/null
+++ b/chromium/content/browser/resources/devtools/devtools_pinch_cursor.png
Binary files differ
diff --git a/chromium/content/browser/resources/devtools/devtools_pinch_cursor_2x.png b/chromium/content/browser/resources/devtools/devtools_pinch_cursor_2x.png
new file mode 100644
index 00000000000..23dd68db692
--- /dev/null
+++ b/chromium/content/browser/resources/devtools/devtools_pinch_cursor_2x.png
Binary files differ
diff --git a/chromium/content/browser/resources/devtools/devtools_touch_cursor.png b/chromium/content/browser/resources/devtools/devtools_touch_cursor.png
new file mode 100644
index 00000000000..a6e0e80b943
--- /dev/null
+++ b/chromium/content/browser/resources/devtools/devtools_touch_cursor.png
Binary files differ
diff --git a/chromium/content/browser/resources/devtools/devtools_touch_cursor_2x.png b/chromium/content/browser/resources/devtools/devtools_touch_cursor_2x.png
new file mode 100644
index 00000000000..f769f12a9db
--- /dev/null
+++ b/chromium/content/browser/resources/devtools/devtools_touch_cursor_2x.png
Binary files differ
diff --git a/chromium/content/browser/resources/gpu/OWNERS b/chromium/content/browser/resources/gpu/OWNERS
index 93d1471e1ef..2aa356651f1 100644
--- a/chromium/content/browser/resources/gpu/OWNERS
+++ b/chromium/content/browser/resources/gpu/OWNERS
@@ -1 +1,3 @@
nduca@chromium.org
+kbr@chromium.org
+zmo@chromium.org \ No newline at end of file
diff --git a/chromium/content/browser/resources/gpu/gpu_internals.html b/chromium/content/browser/resources/gpu/gpu_internals.html
index ef4def79b32..a54861b404a 100644
--- a/chromium/content/browser/resources/gpu/gpu_internals.html
+++ b/chromium/content/browser/resources/gpu/gpu_internals.html
@@ -6,6 +6,7 @@ Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<head i18n-values="dir:textdirection;">
+<meta name="viewport" content="width=device-width" />
<style>
* {
box-sizing: border-box;
diff --git a/chromium/content/browser/resources/gpu/info_view.css b/chromium/content/browser/resources/gpu/info_view.css
index 867690952cf..b78ab1974a6 100644
--- a/chromium/content/browser/resources/gpu/info_view.css
+++ b/chromium/content/browser/resources/gpu/info_view.css
@@ -34,6 +34,8 @@
#info-view table {
border-collapse: collapse;
cursor: text;
+ table-layout: fixed;
+ width: 100%;
}
#info-view table,
@@ -45,6 +47,10 @@
text-align: top;
}
+#info-view td {
+ overflow-x: auto;
+}
+
#info-view .feature-green {
color: rgb(0, 128, 0);
}
diff --git a/chromium/content/browser/resources/gpu/info_view.html b/chromium/content/browser/resources/gpu/info_view.html
index 5a4d9f4acc9..76b235e2230 100644
--- a/chromium/content/browser/resources/gpu/info_view.html
+++ b/chromium/content/browser/resources/gpu/info_view.html
@@ -9,11 +9,6 @@ found in the LICENSE file.
<ul class="feature-status-list">
</ul>
</div>
- <div class='problems-div'>
- <h3>Problems Detected</h3>
- <ul class="problems-list">
- </ul>
- </div>
<div class='workarounds-div'>
<h3>Driver Bug Workarounds</h3>
@@ -21,6 +16,12 @@ found in the LICENSE file.
</ul>
</div>
+ <div class='problems-div'>
+ <h3>Problems Detected</h3>
+ <ul class="problems-list">
+ </ul>
+ </div>
+
<div>
<h3>Version Information</h3>
<div id="client-info"></div>
@@ -55,6 +56,10 @@ found in the LICENSE file.
<div style="display:none">
<div id="info-view-table-template">
<table id="info-view-table">
+ <colgroup>
+ <col style="width: 25%" />
+ <col style="width: 75%" />
+ </colgroup>
<tr jsselect="value">
<td jsdisplay="!(value instanceof Array)">
<span class="row-title" jscontent="description">title</span>
diff --git a/chromium/content/browser/resources/gpu/info_view.js b/chromium/content/browser/resources/gpu/info_view.js
index e736d8e49a8..2dbe5a174b2 100644
--- a/chromium/content/browser/resources/gpu/info_view.js
+++ b/chromium/content/browser/resources/gpu/info_view.js
@@ -64,8 +64,8 @@ cr.define('gpu', function() {
value: clientInfo.driver_bug_list_version
},
{
- description: 'ANGLE revision',
- value: clientInfo.angle_revision
+ description: 'ANGLE commit id',
+ value: clientInfo.angle_commit_id
},
{
description: '2D graphics backend',
@@ -82,66 +82,73 @@ cr.define('gpu', function() {
// Feature map
var featureLabelMap = {
'2d_canvas': 'Canvas',
- '3d_css': '3D CSS',
- 'css_animation': 'CSS Animation',
- 'compositing': 'Compositing',
+ 'gpu_compositing': 'Compositing',
'webgl': 'WebGL',
'multisampling': 'WebGL multisampling',
- 'flash_3d': 'Flash 3D',
+ 'flash_3d': 'Flash',
'flash_stage3d': 'Flash Stage3D',
'flash_stage3d_baseline': 'Flash Stage3D Baseline profile',
'texture_sharing': 'Texture Sharing',
'video_decode': 'Video Decode',
'video_encode': 'Video Encode',
- 'video': 'Video',
- // GPU Switching
- 'gpu_switching': 'GPU Switching',
'panel_fitting': 'Panel Fitting',
- 'force_compositing_mode': 'Force Compositing Mode',
- 'raster': 'Rasterization',
- };
- var statusLabelMap = {
- 'disabled_software': 'Software only. Hardware acceleration disabled.',
- 'disabled_software_animated': 'Software animated.',
- 'disabled_off': 'Unavailable. Hardware acceleration disabled.',
- 'software': 'Software rendered. Hardware acceleration not enabled.',
- 'unavailable_off': 'Unavailable. Hardware acceleration unavailable',
- 'unavailable_software':
- 'Software only, hardware acceleration unavailable',
- 'enabled_readback': 'Hardware accelerated, but at reduced performance',
- 'enabled_force': 'Hardware accelerated on all pages',
- 'enabled_threaded': 'Hardware accelerated on demand and threaded',
- 'enabled_force_threaded':
- 'Hardware accelerated on all pages and threaded',
- 'enabled': 'Hardware accelerated',
- 'accelerated': 'Accelerated',
- 'accelerated_threaded': 'Accelerated and threaded',
- // GPU Switching
- 'gpu_switching_automatic': 'Automatic switching',
- 'gpu_switching_force_discrete': 'Always on discrete GPU',
- 'gpu_switching_force_integrated': 'Always on integrated GPU',
- 'disabled_software_multithreaded': 'Software only, multi-threaded',
+ 'rasterization': 'Rasterization',
+ 'threaded_rasterization': 'Threaded Rasterization',
};
- var statusClassMap = {
- 'disabled_software': 'feature-yellow',
- 'disabled_software_animated': 'feature-yellow',
- 'disabled_off': 'feature-red',
- 'software': 'feature-yellow',
- 'unavailable_off': 'feature-red',
- 'unavailable_software': 'feature-yellow',
- 'enabled_force': 'feature-green',
- 'enabled_readback': 'feature-yellow',
- 'enabled_threaded': 'feature-green',
- 'enabled_force_threaded': 'feature-green',
- 'enabled': 'feature-green',
- 'accelerated': 'feature-green',
- 'accelerated_threaded': 'feature-green',
- // GPU Switching
- 'gpu_switching_automatic': 'feature-green',
- 'gpu_switching_force_discrete': 'feature-red',
- 'gpu_switching_force_integrated': 'feature-red',
- 'disabled_software_multithreaded': 'feature-yellow',
+ var statusMap = {
+ 'disabled_software': {
+ 'label': 'Software only. Hardware acceleration disabled',
+ 'class': 'feature-yellow'
+ },
+ 'disabled_software_threaded': {
+ 'label': 'Software only, threaded. Hardware acceleration disabled',
+ 'class': 'feature-yellow'
+ },
+ 'disabled_off': {
+ 'label': 'Disabled',
+ 'class': 'feature-red'
+ },
+ 'disabled_off_ok': {
+ 'label': 'Disabled',
+ 'class': 'feature-yellow'
+ },
+ 'unavailable_software': {
+ 'label': 'Software only, hardware acceleration unavailable',
+ 'class': 'feature-yellow'
+ },
+ 'unavailable_software_threaded': {
+ 'label': 'Software only, threaded. Hardware acceleration unavailable',
+ 'class': 'feature-yellow'
+ },
+ 'unavailable_off': {
+ 'label': 'Unavailable',
+ 'class': 'feature-red'
+ },
+ 'unavailable_off_ok': {
+ 'label': 'Unavailable',
+ 'class': 'feature-yellow'
+ },
+ 'enabled_readback': {
+ 'label': 'Hardware accelerated but at reduced performance',
+ 'class': 'feature-yellow'
+ },
+ 'enabled_force': {
+ 'label': 'Hardware accelerated on all pages',
+ 'class': 'feature-green'
+ },
+ 'enabled_threaded': {
+ 'label': 'Hardware accelerated and threaded',
+ 'class': 'feature-green'
+ },
+ 'enabled': {
+ 'label': 'Hardware accelerated',
+ 'class': 'feature-green'
+ },
+ 'enabled_on': {
+ 'label': 'Enabled',
+ 'class': 'feature-green'
+ }
};
// GPU info, basic
@@ -173,12 +180,15 @@ cr.define('gpu', function() {
featureEl.appendChild(nameEl);
var statusEl = document.createElement('span');
- if (!statusLabelMap[featureStatus])
- console.log('Missing statusLabel for', featureStatus);
- if (!statusClassMap[featureStatus])
- console.log('Missing statusClass for', featureStatus);
- statusEl.textContent = statusLabelMap[featureStatus];
- statusEl.className = statusClassMap[featureStatus];
+ var statusInfo = statusMap[featureStatus];
+ if (!statusInfo) {
+ console.log('Missing status for ', featureStatus);
+ statusEl.textContent = 'Unknown';
+ statusEl.className = 'feature-red';
+ } else {
+ statusEl.textContent = statusInfo['label'];
+ statusEl.className = statusInfo['class'];
+ }
featureEl.appendChild(statusEl);
featureStatusList.appendChild(featureEl);
@@ -303,6 +313,35 @@ cr.define('gpu', function() {
nbugs++;
}
+ if (problem.affectedGpuSettings.length > 0) {
+ var brNode = document.createElement('br');
+ problemEl.appendChild(brNode);
+
+ var iNode = document.createElement('i');
+ problemEl.appendChild(iNode);
+
+ var headNode = document.createElement('span');
+ if (problem.tag == 'disabledFeatures')
+ headNode.textContent = 'Disabled Features: ';
+ else // problem.tag == 'workarounds'
+ headNode.textContent = 'Applied Workarounds: ';
+ iNode.appendChild(headNode);
+ for (j = 0; j < problem.affectedGpuSettings.length; ++j) {
+ if (j > 0) {
+ var separateNode = document.createElement('span');
+ separateNode.textContent = ', ';
+ iNode.appendChild(separateNode);
+ }
+ var nameNode = document.createElement('span');
+ if (problem.tag == 'disabledFeatures')
+ nameNode.classList.add('feature-red');
+ else // problem.tag == 'workarounds'
+ nameNode.classList.add('feature-yellow');
+ nameNode.textContent = problem.affectedGpuSettings[j];
+ iNode.appendChild(nameNode);
+ }
+ }
+
return problemEl;
},
diff --git a/chromium/content/browser/resources/indexed_db/indexeddb_internals.css b/chromium/content/browser/resources/indexed_db/indexeddb_internals.css
index c52f5ff9754..774938b3201 100644
--- a/chromium/content/browser/resources/indexed_db/indexeddb_internals.css
+++ b/chromium/content/browser/resources/indexed_db/indexeddb_internals.css
@@ -86,6 +86,9 @@ td.indexeddb-transaction-scope {
.indexeddb-transaction.running {
font-weight: bold;
}
+.indexeddb-transaction.committing {
+ font-weight: bold;
+}
.indexeddb-transaction.blocked {
}
@@ -95,6 +98,9 @@ td.indexeddb-transaction-scope {
.indexeddb-transaction.running .indexeddb-transaction-state {
background-color: rgb(235, 249, 235);
}
+.indexeddb-transaction.committing .indexeddb-transaction-state {
+ background-color: rgb(235, 235, 249);
+}
.indexeddb-transaction.blocked .indexeddb-transaction-state {
background-color: rgb(249, 235, 235);
}
diff --git a/chromium/content/browser/resources/indexed_db/indexeddb_internals.html b/chromium/content/browser/resources/indexed_db/indexeddb_internals.html
index 781fcb0fcec..bf7fa1ce78a 100644
--- a/chromium/content/browser/resources/indexed_db/indexeddb_internals.html
+++ b/chromium/content/browser/resources/indexed_db/indexeddb_internals.html
@@ -152,7 +152,7 @@
</td>
<td class="indexeddb-transaction-age">
- <span jsdisplay="status == 'started' || status == 'running'"
+ <span jsdisplay="status == 'started' || status == 'running' || status == 'committing'"
jscontent="Math.round(runtime)">
</span>
</td>
diff --git a/chromium/content/browser/resources/media/OWNERS b/chromium/content/browser/resources/media/OWNERS
index d132d0e6061..bcb6c2496fd 100644
--- a/chromium/content/browser/resources/media/OWNERS
+++ b/chromium/content/browser/resources/media/OWNERS
@@ -1,7 +1,6 @@
acolwell@chromium.org
dalecurtis@chromium.org
ddorwin@chromium.org
-fischman@chromium.org
scherkus@chromium.org
shadi@chromium.org
tommi@chromium.org
diff --git a/chromium/content/browser/resources/media/data_series.js b/chromium/content/browser/resources/media/data_series.js
index 8947492dbf4..adf4c2a25dc 100644
--- a/chromium/content/browser/resources/media/data_series.js
+++ b/chromium/content/browser/resources/media/data_series.js
@@ -76,6 +76,9 @@ var TimelineDataSeries = (function() {
this.color_ = color;
},
+ getCount: function() {
+ return this.dataPoints_.length;
+ },
/**
* Returns a list containing the values of the data series at |count|
* points, starting at |startTime|, and |stepSize| milliseconds apart.
diff --git a/chromium/content/browser/resources/media/dump_creator.js b/chromium/content/browser/resources/media/dump_creator.js
index af17cf3a4ee..24f6d6a18fa 100644
--- a/chromium/content/browser/resources/media/dump_creator.js
+++ b/chromium/content/browser/resources/media/dump_creator.js
@@ -4,9 +4,7 @@
/**
- * Provides the UI to start and stop RTP recording, forwards the start/stop
- * commands to Chrome, and updates the UI based on dump updates. Also provides
- * creating a file containing all PeerConnection updates and stats.
+ * Provides the UI for dump creation.
*/
var DumpCreator = (function() {
/**
@@ -16,30 +14,6 @@ var DumpCreator = (function() {
*/
function DumpCreator(containerElement) {
/**
- * True if the RTP packets are being recorded.
- * @type {bool}
- * @private
- */
- this.recording_ = false;
-
- /**
- * @type {!Object.<string>}
- * @private
- * @const
- */
- this.StatusStrings_ = {
- NOT_STARTED: 'not started.',
- RECORDING: 'recording...',
- },
-
- /**
- * The status of dump creation.
- * @type {string}
- * @private
- */
- this.status_ = this.StatusStrings_.NOT_STARTED;
-
- /**
* The root element of the dump creation UI.
* @type {Element}
* @private
@@ -51,79 +25,82 @@ var DumpCreator = (function() {
var summary = document.createElement('summary');
this.root_.appendChild(summary);
summary.textContent = 'Create Dump';
- var content = document.createElement('pre');
+ var content = document.createElement('div');
this.root_.appendChild(content);
- content.innerHTML = '<button disabled></button> Status: <span></span>' +
- '<div><a><button>' +
+ content.innerHTML = '<div><a><button>' +
'Download the PeerConnection updates and stats data' +
- '</button></a></div>';
- content.getElementsByTagName('button')[0].addEventListener(
- 'click', this.onRtpToggled_.bind(this));
+ '</button></a></div>' +
+ '<p><label><input type=checkbox>' +
+ 'Enable diagnostic audio recordings.</label></p>' +
+ '<p>A diagnostic audio recording is used for analyzing audio' +
+ ' problems. It contains the audio played out from the speaker and' +
+ ' recorded from the microphone and is saved to the local disk.' +
+ ' Checking this box will enable the recording for ongoing WebRTC' +
+ ' calls and for future WebRTC calls. When the box is unchecked or' +
+ ' this page is closed, all ongoing recordings will be stopped and' +
+ ' this recording functionality will be disabled for future WebRTC' +
+ ' calls. Recordings in multiple tabs is supported as well as multiple' +
+ ' recordings in the same tab. When enabling, you select a base' +
+ ' filename to save the dump(s) to. The base filename will have a' +
+ ' suffix appended to it as &lt;base filename&gt;.&lt;unique ID for' +
+ ' the render process&gt;.&lt;recording ID&gt;. If recordings are' +
+ ' disabled and then enabled using the same base filename, the' +
+ ' file(s) will be appended to and may become invalid. It is' +
+ ' recommended to choose a new base filename each time or move' +
+ ' the resulting files before enabling again. If track processing is' +
+ ' disabled (--disable-audio-track-processing): (1) Only one recording' +
+ ' per render process is supported. (2) When the box is unchecked or' +
+ ' this page is closed, ongoing recordings will continue until the' +
+ ' call ends or the page with the recording is closed.</p>';
+
content.getElementsByTagName('a')[0].addEventListener(
'click', this.onDownloadData_.bind(this));
-
- this.updateDisplay_();
+ content.getElementsByTagName('input')[0].addEventListener(
+ 'click', this.onAecRecordingChanged_.bind(this));
}
DumpCreator.prototype = {
+ // Mark the AEC recording checkbox checked.
+ enableAecRecording: function() {
+ this.root_.getElementsByTagName('input')[0].checked = true;
+ },
+
+ // Mark the AEC recording checkbox unchecked.
+ disableAecRecording: function() {
+ this.root_.getElementsByTagName('input')[0].checked = false;
+ },
+
/**
* Downloads the PeerConnection updates and stats data as a file.
*
* @private
*/
onDownloadData_: function() {
- var textBlob =
- new Blob([JSON.stringify(peerConnectionDataStore, null, ' ')],
- {type: 'octet/stream'});
- var URL = window.webkitURL.createObjectURL(textBlob);
+ var dump_object =
+ {
+ 'getUserMedia': userMediaRequests,
+ 'PeerConnections': peerConnectionDataStore,
+ };
+ var textBlob = new Blob([JSON.stringify(dump_object, null, ' ')],
+ {type: 'octet/stream'});
+ var URL = window.URL.createObjectURL(textBlob);
+
this.root_.getElementsByTagName('a')[0].href = URL;
// The default action of the anchor will download the URL.
},
/**
- * Handles the event of toggling the rtp recording state.
+ * Handles the event of toggling the AEC recording state.
*
* @private
*/
- onRtpToggled_: function() {
- if (this.recording_) {
- this.recording_ = false;
- this.status_ = this.StatusStrings_.NOT_STARTED;
- chrome.send('stopRtpRecording');
+ onAecRecordingChanged_: function() {
+ var enabled = this.root_.getElementsByTagName('input')[0].checked;
+ if (enabled) {
+ chrome.send('enableAecRecording');
} else {
- this.recording_ = true;
- this.status_ = this.StatusStrings_.RECORDING;
- chrome.send('startRtpRecording');
- }
- this.updateDisplay_();
- },
-
- /**
- * Updates the UI based on the recording status.
- *
- * @private
- */
- updateDisplay_: function() {
- if (this.recording_) {
- this.root_.getElementsByTagName('button')[0].textContent =
- 'Stop Recording RTP Packets';
- } else {
- this.root_.getElementsByTagName('button')[0].textContent =
- 'Start Recording RTP Packets';
- }
-
- this.root_.getElementsByTagName('span')[0].textContent = this.status_;
- },
-
- /**
- * Set the status to the content of the update.
- * @param {!Object} update
- */
- onUpdate: function(update) {
- if (this.recording_) {
- this.status_ = JSON.stringify(update);
- this.updateDisplay_();
+ chrome.send('disableAecRecording');
}
},
};
diff --git a/chromium/content/browser/resources/media/stats_graph_helper.js b/chromium/content/browser/resources/media/stats_graph_helper.js
index 1a20666dad6..ad479dcdc55 100644
--- a/chromium/content/browser/resources/media/stats_graph_helper.js
+++ b/chromium/content/browser/resources/media/stats_graph_helper.js
@@ -15,6 +15,11 @@
var STATS_GRAPH_CONTAINER_HEADING_CLASS = 'stats-graph-container-heading';
+var RECEIVED_PROPAGATION_DELTA_LABEL =
+ 'googReceivedPacketGroupPropagationDeltaDebug';
+var RECEIVED_PACKET_GROUP_ARRIVAL_TIME_LABEL =
+ 'googReceivedPacketGroupArrivalTimeDebug';
+
// Specifies which stats should be drawn on the 'bweCompound' graph and how.
var bweCompoundGraphConfig = {
googAvailableSendBandwidth: {color: 'red'},
@@ -110,14 +115,22 @@ function drawSingleReport(peerConnectionElement, report) {
for (var i = 0; i < stats.values.length - 1; i = i + 2) {
var rawLabel = stats.values[i];
+ // Propagation deltas are handled separately.
+ if (rawLabel == RECEIVED_PROPAGATION_DELTA_LABEL) {
+ drawReceivedPropagationDelta(
+ peerConnectionElement, report, stats.values[i + 1]);
+ continue;
+ }
var rawDataSeriesId = reportId + '-' + rawLabel;
var rawValue = getNumberFromValue(rawLabel, stats.values[i + 1]);
if (isNaN(rawValue)) {
// We do not draw non-numerical values, but still want to record it in the
// data series.
- addDataSeriesPoint(peerConnectionElement,
- rawDataSeriesId, stats.timestamp,
- rawLabel, stats.values[i + 1]);
+ addDataSeriesPoints(peerConnectionElement,
+ rawDataSeriesId,
+ rawLabel,
+ [stats.timestamp],
+ [stats.values[i + 1]]);
continue;
}
@@ -127,9 +140,11 @@ function drawSingleReport(peerConnectionElement, report) {
// We need to convert the value if dataConversionConfig[rawLabel] exists.
if (dataConversionConfig[rawLabel]) {
// Updates the original dataSeries before the conversion.
- addDataSeriesPoint(peerConnectionElement,
- rawDataSeriesId, stats.timestamp,
- rawLabel, rawValue);
+ addDataSeriesPoints(peerConnectionElement,
+ rawDataSeriesId,
+ rawLabel,
+ [stats.timestamp],
+ [rawValue]);
// Convert to another value to draw on graph, using the original
// dataSeries as input.
@@ -141,11 +156,11 @@ function drawSingleReport(peerConnectionElement, report) {
}
// Updates the final dataSeries to draw.
- addDataSeriesPoint(peerConnectionElement,
- finalDataSeriesId,
- stats.timestamp,
- finalLabel,
- finalValue);
+ addDataSeriesPoints(peerConnectionElement,
+ finalDataSeriesId,
+ finalLabel,
+ [stats.timestamp],
+ [finalValue]);
// Updates the graph.
var graphType = bweCompoundGraphConfig[finalLabel] ?
@@ -172,9 +187,10 @@ function drawSingleReport(peerConnectionElement, report) {
}
// Makes sure the TimelineDataSeries with id |dataSeriesId| is created,
-// and adds the new data point to it.
-function addDataSeriesPoint(
- peerConnectionElement, dataSeriesId, time, label, value) {
+// and adds the new data points to it. |times| is the list of timestamps for
+// each data point, and |values| is the list of the data point values.
+function addDataSeriesPoints(
+ peerConnectionElement, dataSeriesId, label, times, values) {
var dataSeries =
peerConnectionDataStore[peerConnectionElement.id].getDataSeries(
dataSeriesId);
@@ -186,7 +202,62 @@ function addDataSeriesPoint(
dataSeries.setColor(bweCompoundGraphConfig[label].color);
}
}
- dataSeries.addPoint(time, value);
+ for (var i = 0; i < times.length; ++i)
+ dataSeries.addPoint(times[i], values[i]);
+}
+
+// Draws the received propagation deltas using the packet group arrival time as
+// the x-axis. For example, |report.stats.values| should be like
+// ['googReceivedPacketGroupArrivalTimeDebug', '[123456, 234455, 344566]',
+// 'googReceivedPacketGroupPropagationDeltaDebug', '[23, 45, 56]', ...].
+function drawReceivedPropagationDelta(peerConnectionElement, report, deltas) {
+ var reportId = report.id;
+ var stats = report.stats;
+ var times = null;
+ // Find the packet group arrival times.
+ for (var i = 0; i < stats.values.length - 1; i = i + 2) {
+ if (stats.values[i] == RECEIVED_PACKET_GROUP_ARRIVAL_TIME_LABEL) {
+ times = stats.values[i + 1];
+ break;
+ }
+ }
+ // Unexpected.
+ if (times == null)
+ return;
+
+ // Convert |deltas| and |times| from strings to arrays of numbers.
+ try {
+ deltas = JSON.parse(deltas);
+ times = JSON.parse(times);
+ } catch (e) {
+ console.log(e);
+ return;
+ }
+
+ // Update the data series.
+ var dataSeriesId = reportId + '-' + RECEIVED_PROPAGATION_DELTA_LABEL;
+ addDataSeriesPoints(
+ peerConnectionElement,
+ dataSeriesId,
+ RECEIVED_PROPAGATION_DELTA_LABEL,
+ times,
+ deltas);
+ // Update the graph.
+ var graphViewId = peerConnectionElement.id + '-' + reportId + '-' +
+ RECEIVED_PROPAGATION_DELTA_LABEL;
+ var date = new Date(times[times.length - 1]);
+ if (!graphViews[graphViewId]) {
+ graphViews[graphViewId] = createStatsGraphView(
+ peerConnectionElement,
+ report,
+ RECEIVED_PROPAGATION_DELTA_LABEL);
+ graphViews[graphViewId].setScale(10);
+ graphViews[graphViewId].setDateRange(date, date);
+ var dataSeries = peerConnectionDataStore[peerConnectionElement.id]
+ .getDataSeries(dataSeriesId);
+ graphViews[graphViewId].addDataSeries(dataSeries);
+ }
+ graphViews[graphViewId].updateEndDate(date);
}
// Ensures a div container to hold all stats graphs for one track is created as
diff --git a/chromium/content/browser/resources/media/stats_table.js b/chromium/content/browser/resources/media/stats_table.js
index 313484702e3..418be756c65 100644
--- a/chromium/content/browser/resources/media/stats_table.js
+++ b/chromium/content/browser/resources/media/stats_table.js
@@ -83,6 +83,7 @@ var StatsTable = (function(ssrcInfoManager) {
var container = this.ensureStatsTableContainer_(peerConnectionElement);
var details = document.createElement('details');
container.appendChild(details);
+
var summary = document.createElement('summary');
summary.textContent = report.id;
details.appendChild(summary);
@@ -139,6 +140,10 @@ var StatsTable = (function(ssrcInfoManager) {
trElement.innerHTML = '<td>' + rowName + '</td><td></td>';
}
trElement.cells[1].textContent = value;
+
+ // Highlights the table for the active connection.
+ if (rowName == 'googActiveConnection' && value == 'true')
+ statsTable.parentElement.classList.add('stats-table-active-connection');
}
};
diff --git a/chromium/content/browser/resources/media/tab_view.js b/chromium/content/browser/resources/media/tab_view.js
index 953ec2fae11..6fc8d73410d 100644
--- a/chromium/content/browser/resources/media/tab_view.js
+++ b/chromium/content/browser/resources/media/tab_view.js
@@ -52,7 +52,7 @@ var TabView = (function() {
var head = document.createElement('div');
head.className = this.TAB_HEAD_CLASS_;
- head.textContent = id;
+ head.textContent = title + ' [' + id + ']';
head.title = title;
this.headBar_.appendChild(head);
head.addEventListener('click', this.switchTab_.bind(this, id));
diff --git a/chromium/content/browser/resources/media/timeline_graph_view.js b/chromium/content/browser/resources/media/timeline_graph_view.js
index 89b557e1710..d2b12329da2 100644
--- a/chromium/content/browser/resources/media/timeline_graph_view.js
+++ b/chromium/content/browser/resources/media/timeline_graph_view.js
@@ -8,9 +8,6 @@
var TimelineGraphView = (function() {
'use strict';
- // Default starting scale factor, in terms of milliseconds per pixel.
- var DEFAULT_SCALE = 1000;
-
// Maximum number of labels placed vertically along the sides of the graph.
var MAX_VERTICAL_LABELS = 6;
@@ -31,6 +28,7 @@ var TimelineGraphView = (function() {
var TEXT_COLOR = '#000';
var BACKGROUND_COLOR = '#FFF';
+ var MAX_DECIMAL_PRECISION = 2;
/**
* @constructor
*/
@@ -50,17 +48,24 @@ var TimelineGraphView = (function() {
this.graph_ = null;
+ // Horizontal scale factor, in terms of milliseconds per pixel.
+ this.scale_ = 1000;
+
// Initialize the scrollbar.
this.updateScrollbarRange_(true);
}
TimelineGraphView.prototype = {
+ setScale: function(scale) {
+ this.scale_ = scale;
+ },
+
// Returns the total length of the graph, in pixels.
getLength_: function() {
var timeRange = this.endTime_ - this.startTime_;
// Math.floor is used to ignore the last partial area, of length less
- // than DEFAULT_SCALE.
- return Math.floor(timeRange / DEFAULT_SCALE);
+ // than this.scale_.
+ return Math.floor(timeRange / this.scale_);
},
/**
@@ -113,8 +118,8 @@ var TimelineGraphView = (function() {
* all the way to the right, keeps it all the way to the right. Otherwise,
* leaves the view as-is and doesn't redraw anything.
*/
- updateEndDate: function() {
- this.endTime_ = (new Date()).getTime();
+ updateEndDate: function(opt_date) {
+ this.endTime_ = opt_date || (new Date()).getTime();
this.updateScrollbarRange_(this.graphScrolledToRightEdge_());
},
@@ -181,7 +186,7 @@ var TimelineGraphView = (function() {
// the graph to the end of the time range.
if (this.scrollbar_.range_ == 0)
position = this.getLength_() - this.canvas_.width;
- var visibleStartTime = this.startTime_ + position * DEFAULT_SCALE;
+ var visibleStartTime = this.startTime_ + position * this.scale_;
// Make space at the bottom of the graph for the time labels, and then
// draw the labels.
@@ -196,7 +201,7 @@ var TimelineGraphView = (function() {
if (this.graph_) {
// Layout graph and have them draw their tick marks.
this.graph_.layout(
- width, height, fontHeight, visibleStartTime, DEFAULT_SCALE);
+ width, height, fontHeight, visibleStartTime, this.scale_);
this.graph_.drawTicks(context);
// Draw the lines of all graphs, and then draw their labels.
@@ -228,7 +233,7 @@ var TimelineGraphView = (function() {
// Draw labels and vertical grid lines.
while (true) {
- var x = Math.round((time - startTime) / DEFAULT_SCALE);
+ var x = Math.round((time - startTime) / this.scale_);
if (x >= width)
break;
var text = (new Date(time)).toLocaleTimeString();
@@ -274,8 +279,10 @@ var TimelineGraphView = (function() {
this.startTime_ = 0;
this.scale_ = 0;
- // At least the highest value in the displayed range of the graph.
- // Used for scaling and setting labels. Set in layoutLabels.
+ // The lowest/highest values adjusted by the vertical label step size
+ // in the displayed range of the graph. Used for scaling and setting
+ // labels. Set in layoutLabels.
+ this.min_ = 0;
this.max_ = 0;
// Cached text of equally spaced labels. Set in layoutLabels.
@@ -327,7 +334,7 @@ var TimelineGraphView = (function() {
this.scale_ = scale;
// Find largest value.
- var max = 0;
+ var max = 0, min = 0;
for (var i = 0; i < this.dataSeries_.length; ++i) {
var values = this.getValues(this.dataSeries_[i]);
if (!values)
@@ -335,21 +342,23 @@ var TimelineGraphView = (function() {
for (var j = 0; j < values.length; ++j) {
if (values[j] > max)
max = values[j];
+ else if (values[j] < min)
+ min = values[j];
}
}
- this.layoutLabels_(max);
+ this.layoutLabels_(min, max);
},
/**
- * Lays out labels and sets |max_|, taking the time units into
+ * Lays out labels and sets |max_|/|min_|, taking the time units into
* consideration. |maxValue| is the actual maximum value, and
* |max_| will be set to the value of the largest label, which
- * will be at least |maxValue|.
+ * will be at least |maxValue|. Similar for |min_|.
*/
- layoutLabels_: function(maxValue) {
- if (maxValue < 1024) {
- this.layoutLabelsBasic_(maxValue, 0);
+ layoutLabels_: function(minValue, maxValue) {
+ if (maxValue - minValue < 1024) {
+ this.layoutLabelsBasic_(minValue, maxValue, MAX_DECIMAL_PRECISION);
return;
}
@@ -358,20 +367,23 @@ var TimelineGraphView = (function() {
// Units to use for labels. 0 is '1', 1 is K, etc.
// We start with 1, and work our way up.
var unit = 1;
+ minValue /= 1024;
maxValue /= 1024;
- while (units[unit + 1] && maxValue >= 1024) {
+ while (units[unit + 1] && maxValue - minValue >= 1024) {
+ minValue /= 1024;
maxValue /= 1024;
++unit;
}
// Calculate labels.
- this.layoutLabelsBasic_(maxValue, 1);
+ this.layoutLabelsBasic_(minValue, maxValue, MAX_DECIMAL_PRECISION);
// Append units to labels.
for (var i = 0; i < this.labels_.length; ++i)
this.labels_[i] += ' ' + units[unit];
- // Convert |max_| back to unit '1'.
+ // Convert |min_|/|max_| back to unit '1'.
+ this.min_ *= Math.pow(1024, unit);
this.max_ *= Math.pow(1024, unit);
},
@@ -380,11 +392,12 @@ var TimelineGraphView = (function() {
* maximum number of decimal digits allowed. The minimum allowed
* difference between two adjacent labels is 10^-|maxDecimalDigits|.
*/
- layoutLabelsBasic_: function(maxValue, maxDecimalDigits) {
+ layoutLabelsBasic_: function(minValue, maxValue, maxDecimalDigits) {
this.labels_ = [];
- // No labels if |maxValue| is 0.
- if (maxValue == 0) {
- this.max_ = maxValue;
+ var range = maxValue - minValue;
+ // No labels if the range is 0.
+ if (range == 0) {
+ this.min_ = this.max_ = maxValue;
return;
}
@@ -411,21 +424,21 @@ var TimelineGraphView = (function() {
while (true) {
// If we use a step size of |stepSize| between labels, we'll need:
//
- // Math.ceil(maxValue / stepSize) + 1
+ // Math.ceil(range / stepSize) + 1
//
// labels. The + 1 is because we need labels at both at 0 and at
// the top of the graph.
// Check if we can use steps of size |stepSize|.
- if (Math.ceil(maxValue / stepSize) + 1 <= maxLabels)
+ if (Math.ceil(range / stepSize) + 1 <= maxLabels)
break;
// Check |stepSize| * 2.
- if (Math.ceil(maxValue / (stepSize * 2)) + 1 <= maxLabels) {
+ if (Math.ceil(range / (stepSize * 2)) + 1 <= maxLabels) {
stepSize *= 2;
break;
}
// Check |stepSize| * 5.
- if (Math.ceil(maxValue / (stepSize * 5)) + 1 <= maxLabels) {
+ if (Math.ceil(range / (stepSize * 5)) + 1 <= maxLabels) {
stepSize *= 5;
break;
}
@@ -434,11 +447,12 @@ var TimelineGraphView = (function() {
--stepSizeDecimalDigits;
}
- // Set the max so it's an exact multiple of the chosen step size.
+ // Set the min/max so it's an exact multiple of the chosen step size.
this.max_ = Math.ceil(maxValue / stepSize) * stepSize;
+ this.min_ = Math.floor(minValue / stepSize) * stepSize;
// Create labels.
- for (var label = this.max_; label >= 0; label -= stepSize)
+ for (var label = this.max_; label >= this.min_; label -= stepSize)
this.labels_.push(label.toFixed(stepSizeDecimalDigits));
},
@@ -472,7 +486,7 @@ var TimelineGraphView = (function() {
var scale = 0;
var bottom = this.height_ - 1;
if (this.max_)
- scale = bottom / this.max_;
+ scale = bottom / (this.max_ - this.min_);
// Draw in reverse order, so earlier data series are drawn on top of
// subsequent ones.
@@ -485,7 +499,8 @@ var TimelineGraphView = (function() {
for (var x = 0; x < values.length; ++x) {
// The rounding is needed to avoid ugly 2-pixel wide anti-aliased
// horizontal lines.
- context.lineTo(x, bottom - Math.round(values[x] * scale));
+ context.lineTo(
+ x, bottom - Math.round((values[x] - this.min_) * scale));
}
context.stroke();
}
diff --git a/chromium/content/browser/resources/media/webrtc_internals.css b/chromium/content/browser/resources/media/webrtc_internals.css
index 096a227b523..cc976ebd931 100644
--- a/chromium/content/browser/resources/media/webrtc_internals.css
+++ b/chromium/content/browser/resources/media/webrtc_internals.css
@@ -24,11 +24,6 @@
margin: 0.5em 0 0.5em 0;
}
-.stats-graph-container-heading {
- font-size: 0.8em;
- font-weight: bold;
-}
-
.stats-graph-sub-container {
float: left;
margin: 0.5em;
@@ -56,6 +51,10 @@
padding: 0 0 1em 0;
}
+.stats-table-active-connection {
+ font-weight: bold;
+}
+
body {
font-family: 'Lucida Grande', sans-serif;
}
@@ -85,22 +84,26 @@ th {
.tab-head {
background-color: rgb(220, 220, 220);
- margin: 10px 5px 0 5px;
+ margin: 10px 2px 0 2px;
text-decoration: underline;
cursor: pointer;
display: inline-block;
- width: 32%;
+ word-break: break-all;
+ width: 20em;
}
+
.active-tab-head {
background-color: turquoise;
font-weight: bold;
}
+
.tab-body {
border: 1px solid turquoise;
border-top-width: 3px;
padding: 0 10px 500px 10px;
display: none;
}
+
.active-tab-body {
display: block;
}
diff --git a/chromium/content/browser/resources/media/webrtc_internals.js b/chromium/content/browser/resources/media/webrtc_internals.js
index 6d3cfc16e3a..52ab9f711f4 100644
--- a/chromium/content/browser/resources/media/webrtc_internals.js
+++ b/chromium/content/browser/resources/media/webrtc_internals.js
@@ -9,6 +9,8 @@ var statsTable = null;
var dumpCreator = null;
/** A map from peer connection id to the PeerConnectionRecord. */
var peerConnectionDataStore = {};
+/** A list of getUserMedia requests. */
+var userMediaRequests = [];
/** A simple class to store the updates and stats data for a peer connection. */
var PeerConnectionRecord = (function() {
@@ -94,17 +96,21 @@ function initialize() {
peerConnectionUpdateTable = new PeerConnectionUpdateTable();
statsTable = new StatsTable(ssrcInfoManager);
- chrome.send('getAllUpdates');
+ chrome.send('finishedDOMLoad');
// Requests stats from all peer connections every second.
- window.setInterval(function() {
- if (Object.keys(peerConnectionDataStore).length > 0)
- chrome.send('getAllStats');
- }, 1000);
+ window.setInterval(requestStats, 1000);
}
document.addEventListener('DOMContentLoaded', initialize);
+/** Sends a request to the browser to get peer connection statistics. */
+function requestStats() {
+ if (Object.keys(peerConnectionDataStore).length > 0)
+ chrome.send('getAllStats');
+}
+
+
/**
* A helper function for getting a peer connection element id.
*
@@ -219,6 +225,7 @@ function updateAllPeerConnections(data) {
addPeerConnectionUpdate(peerConnection, log[j]);
}
}
+ requestStats();
}
@@ -245,10 +252,43 @@ function addStats(data) {
/**
- * Delegates to dumpCreator to update the recording status.
- * @param {!Object.<string>} update Key-value pairs describing the status of the
- * RTP recording.
+ * Adds a getUserMedia request.
+ *
+ * @param {!Object} data The object containing rid {number}, pid {number},
+ * origin {string}, audio {string}, video {string}.
+ */
+function addGetUserMedia(data) {
+ // TODO(jiayl): add the getUserMedia info to the tabbed UI.
+ userMediaRequests.push(data);
+}
+
+
+/**
+ * Removes the getUserMedia requests from the specified |rid|.
+ *
+ * @param {!Object} data The object containing rid {number}, the render id.
+ */
+function removeGetUserMediaForRenderer(data) {
+ // TODO(jiayl): remove the getUserMedia info from the tabbed UI.
+ for (var i = userMediaRequests.length - 1; i >= 0; --i) {
+ if (userMediaRequests[i].rid == data.rid)
+ userMediaRequests.splice(i, 1);
+ }
+}
+
+
+/**
+ * Notification that the AEC recording file selection dialog was cancelled,
+ * i.e. AEC has not been enabled.
+ */
+function aecRecordingFileSelectionCancelled() {
+ dumpCreator.disableAecRecording();
+}
+
+
+/**
+ * Set
*/
-function updateDumpStatus(update) {
- dumpCreator.onUpdate(update);
+function enableAecRecording() {
+ dumpCreator.enableAecRecording();
}
diff --git a/chromium/content/browser/resources/service_worker/OWNERS b/chromium/content/browser/resources/service_worker/OWNERS
new file mode 100644
index 00000000000..a143cc76541
--- /dev/null
+++ b/chromium/content/browser/resources/service_worker/OWNERS
@@ -0,0 +1,5 @@
+michaeln@chromium.org
+horo@chromium.org
+
+# may not be available
+kinuko@chromium.org
diff --git a/chromium/content/browser/resources/service_worker/serviceworker_internals.css b/chromium/content/browser/resources/service_worker/serviceworker_internals.css
new file mode 100644
index 00000000000..0ac2528098c
--- /dev/null
+++ b/chromium/content/browser/resources/service_worker/serviceworker_internals.css
@@ -0,0 +1,44 @@
+/* Copyright 2014 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file. */
+
+.serviceworker-summary {
+ background-color: rgb(235, 239, 249);
+ border-top: 1px solid rgb(156, 194, 239);
+ margin-bottom: 6px;
+ margin-top: 12px;
+ padding: 3px;
+ font-weight: bold;
+}
+
+.serviceworker-item {
+ margin-bottom: 15px;
+ margin-top: 6px;
+ position: relative;
+}
+
+.serviceworker-registration {
+ padding: 5px;
+}
+
+.serviceworker-scope {
+ color: rgb(85, 102, 221);
+ display: inline-block;
+ max-width: 500px;
+ overflow: hidden;
+ padding-bottom: 1px;
+ padding-top: 4px;
+ text-decoration: none;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.serviceworker-version {
+ padding-bottom: 3px;
+ padding-left: 10px;
+}
+
+.controls a {
+ -webkit-margin-end: 16px;
+ color: #777;
+}
diff --git a/chromium/content/browser/resources/service_worker/serviceworker_internals.html b/chromium/content/browser/resources/service_worker/serviceworker_internals.html
new file mode 100644
index 00000000000..85e70454664
--- /dev/null
+++ b/chromium/content/browser/resources/service_worker/serviceworker_internals.html
@@ -0,0 +1,141 @@
+<!DOCTYPE html>
+<html i18n-values="dir:textdirection;">
+<head>
+ <meta charset="utf-8">
+ <title>ServiceWorker</title>
+ <link rel="stylesheet" href="chrome://resources/css/tabs.css">
+ <link rel="stylesheet" href="chrome://resources/css/widgets.css">
+ <link rel="stylesheet" href="serviceworker_internals.css">
+</head>
+<body i18n-values=".style.fontFamily:fontfamily;.style.fontSize:fontsize">
+ <!-- templates -->
+ <div style="display:none">
+ <div id="serviceworker-version-template" class="serviceworker-version">
+ <div class="serviceworker-status">
+ <span>Installation Status:</span>
+ <span jscontent="$this.status"></span>
+ </div>
+ <div class="serviceworker-running-status">
+ <span>Running Status:</span>
+ <span jscontent="$this.running_status"></span>
+ </div>
+ <div class="serviceworker-vid">
+ <span>Version ID:</span>
+ <span jscontent="$this.version_id"></span>
+ </div>
+ <div class="serviceworker-pid">
+ <span>Renderer process ID:</span>
+ <span jscontent="$this.process_id"></span>
+ </div>
+ <div class="serviceworker-tid">
+ <span>Renderer thread ID:</span>
+ <span jscontent="$this.thread_id"></span>
+ </div>
+ <div class="serviceworker-rid">
+ <span>DevTools agent route ID:</span>
+ <span jscontent="$this.devtools_agent_route_id"></span>
+ </div>
+ <div>
+ <div>Log:</div>
+ <textarea class="serviceworker-log"
+ jsvalues=".partition_id:$partition_id;.version_id:$this.version_id"
+ rows="3" cols="120" readonly jscontent="$this.log"></textarea>
+ </div>
+ <div class="worker-controls">
+ <button href="#" class="stop"
+ jsvalues=".cmdArgs:{partition_id:$partition_id,version_id:version_id}"
+ jsdisplay="$this.running_status == 'RUNNING'">Stop</button>
+ <button href="#" class="sync"
+ jsvalues=".cmdArgs:{partition_id:$partition_id,version_id:version_id}"
+ jsdisplay="$this.running_status == 'RUNNING'">Sync</button>
+ <button href="#" class="push"
+ jsvalues=".cmdArgs:{partition_id:$partition_id,version_id:version_id}"
+ jsdisplay="$this.running_status == 'RUNNING'">Push</button>
+ <button href="#" class="inspect"
+ jsvalues=".cmdArgs:{process_id:process_id,devtools_agent_route_id:devtools_agent_route_id}"
+ jsdisplay="$this.running_status == 'RUNNING'">Inspect</button>
+ <span class="operation-status" style="display: none">Running...</span>
+ </div>
+ </div>
+ <div id="serviceworker-registration-template"
+ class="serviceworker-registration">
+ <div class="serviceworker-scope">
+ <span>Scope:</span>
+ <span jscontent="scope"></span>
+ </div>
+ <div class="serviceworker-script_url">
+ <span>Script:</span>
+ <span jscontent="script_url"></span>
+ </div>
+ <div class="serviceworker-rid">
+ <span>Registration ID:</span>
+ <span jscontent="registration_id"></span>
+ <span jsdisplay="$this.unregistered">(unregistered)</span>
+ </div>
+ <div jsselect="$this.active">
+ Active worker:
+ <div transclude="serviceworker-version-template"></div>
+ </div>
+ <div jsselect="$this.waiting">
+ Waiting worker:
+ <div transclude="serviceworker-version-template"></div>
+ </div>
+ <div class="registration-controls" jsdisplay="!$this.unregistered">
+ <button href="#" class="unregister"
+ jsvalues=".cmdArgs:{partition_id:$partition_id,scope:scope}">
+ Unregister
+ </button>
+ <button href="#" class="start"
+ jsdisplay="$this.active.running_status != 'RUNNING'"
+ jsvalues=".cmdArgs:{partition_id:$partition_id,scope:scope}">
+ Start
+ </button>
+ <span class="operation-status" style="display: none">Running...</span>
+ </div>
+ </div>
+ <div id="serviceworker-list-template"
+ jsvalues="$partition_id:$this.partition_id;.partition_id:$this.partition_id"
+ jsdisplay="$this.stored_registrations.length + $this.unregistered_registrations.length + $this.unregistered_versions.length > 0">
+ <div class="serviceworker-summary">
+ <span>Registrations in: </span>
+ <span jscontent="$this.partition_path"></span>
+ <span jscontent="'(' + $this.stored_registrations.length + ')'"></span>
+ </div>
+ <div class="serviceworker-item" jsselect="$this.stored_registrations">
+ <div transclude="serviceworker-registration-template"></div>
+ </div>
+ <div class="serviceworker-item"
+ jsselect="$this.unregistered_registrations">
+ <div transclude="serviceworker-registration-template"></div>
+ </div>
+ <div class="serviceworker-item" jsselect="$this.unregistered_versions">
+ Unregistered worker:
+ <div transclude="serviceworker-version-template"></div>
+ </div>
+ </div>
+ <div id="serviceworker-options-template">
+ <div>
+ <span>
+ <input type="checkbox" class="debug_on_start"
+ jsvalues=".checked:$this.debug_on_start">
+ </span>
+ <span>
+ Opens the DevTools window for ServiceWorker on start for debugging.
+ </span>
+ </div>
+ </div>
+ </div>
+ <h1>ServiceWorker</h1>
+ <div class="content">
+ <div id="serviceworker-options"></div>
+ <div id="serviceworker-list"></div>
+ </div>
+ <script src="chrome://resources/js/util.js"></script>
+ <script src="chrome://resources/js/cr.js"></script>
+ <script src="serviceworker_internals.js"></script>
+ <script src="chrome://resources/js/load_time_data.js"></script>
+ <script src="chrome://resources/js/jstemplate_compiled.js"></script>
+ <script src="strings.js"></script>
+ <script src="chrome://resources/js/i18n_template2.js"></script>
+</body>
+</html>
diff --git a/chromium/content/browser/resources/service_worker/serviceworker_internals.js b/chromium/content/browser/resources/service_worker/serviceworker_internals.js
new file mode 100644
index 00000000000..5e698b80d5c
--- /dev/null
+++ b/chromium/content/browser/resources/service_worker/serviceworker_internals.js
@@ -0,0 +1,304 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+cr.define('serviceworker', function() {
+ 'use strict';
+
+ function initialize() {
+ if (window.location.hash == "#iframe") {
+ // This page is loaded from chrome://inspect.
+ window.addEventListener('message', onMessage.bind(this), false);
+ }
+ update();
+ }
+
+ function onMessage(event) {
+ if (event.origin != 'chrome://inspect') {
+ return;
+ }
+ sendCommand(event.data.action, event.data.worker);
+ }
+
+ function update() {
+ chrome.send('GetOptions');
+ chrome.send('getAllRegistrations');
+ }
+
+ function onOptions(options) {
+ var template;
+ var container = $('serviceworker-options');
+ if (container.childNodes) {
+ template = container.childNodes[0];
+ }
+ if (!template) {
+ template = jstGetTemplate('serviceworker-options-template');
+ container.appendChild(template);
+ }
+ jstProcess(new JsEvalContext(options), template);
+ var inputs = container.querySelectorAll('input[type=\'checkbox\']');
+ for (var i = 0; i < inputs.length; ++i) {
+ if (!inputs[i].hasClickEvent) {
+ inputs[i].addEventListener('click', (function(event) {
+ chrome.send('SetOption',
+ [event.target.className, event.target.checked]);
+ }).bind(this), false);
+ inputs[i].hasClickEvent = true;
+ }
+ }
+ }
+
+ function progressNodeFor(link) {
+ return link.parentNode.querySelector('.operation-status');
+ }
+
+ // All commands are completed with 'onOperationComplete'.
+ var COMMANDS = ['stop', 'sync', 'push', 'inspect', 'unregister', 'start'];
+ function commandHandler(command) {
+ return function(event) {
+ var link = event.target;
+ progressNodeFor(link).style.display = 'inline';
+ sendCommand(command, link.cmdArgs, (function(status) {
+ progressNodeFor(link).style.display = 'none';
+ }).bind(null, link));
+ return false;
+ };
+ };
+
+ var commandCallbacks = [];
+ function sendCommand(command, args, callback) {
+ var callbackId = 0;
+ while (callbackId in commandCallbacks) {
+ callbackId++;
+ }
+ commandCallbacks[callbackId] = callback;
+ chrome.send(command, [callbackId, args]);
+ }
+
+ // Fired from the backend after the command call has completed.
+ function onOperationComplete(status, callbackId) {
+ var callback = commandCallbacks[callbackId];
+ delete commandCallbacks[callbackId];
+ if (callback) {
+ callback(status);
+ }
+ update();
+ }
+
+ // Send the active ServiceWorker information to chrome://inspect.
+ function sendToInspectPage(live_registrations,
+ partition_id) {
+ var workers = [];
+ live_registrations.forEach(function(registration) {
+ [registration.active, registration.waiting].forEach(function(version) {
+ if (!version || version.running_status != 'RUNNING') {
+ return;
+ }
+ workers.push({
+ 'scope': registration.scope,
+ 'url': registration.script_url,
+ 'partition_id': partition_id,
+ 'version_id': version.version_id,
+ 'process_id': version.process_id,
+ 'devtools_agent_route_id':
+ version.devtools_agent_route_id
+ });
+ });
+ });
+ window.parent.postMessage(
+ {'partition_id': partition_id, 'workers': workers},
+ 'chrome://inspect');
+ }
+
+ var allLogMessages = {};
+ // Set log for a worker version.
+ function fillLogForVersion(partition_id, version) {
+ if (!version) {
+ return;
+ }
+ if (!(partition_id in allLogMessages)) {
+ allLogMessages[partition_id] = {};
+ }
+ var logMessages = allLogMessages[partition_id];
+ if (version.version_id in logMessages) {
+ version.log = logMessages[version.version_id];
+ } else {
+ version.log = '';
+ }
+ }
+
+ // Get the unregistered workers.
+ // |unregistered_registrations| will be filled with the registrations which
+ // are in |live_registrations| but not in |stored_registrations|.
+ // |unregistered_versions| will be filled with the versions which
+ // are in |live_versions| but not in |stored_registrations| nor in
+ // |live_registrations|.
+ function getUnregisteredWorkers(stored_registrations,
+ live_registrations,
+ live_versions,
+ unregistered_registrations,
+ unregistered_versions) {
+ var registration_id_set = {};
+ var version_id_set = {};
+ stored_registrations.forEach(function(registration) {
+ registration_id_set[registration.registration_id] = true;
+ });
+ [stored_registrations, live_registrations].forEach(function(registrations) {
+ registrations.forEach(function(registration) {
+ [registration.active, registration.waiting].forEach(function(version) {
+ if (version) {
+ version_id_set[version.version_id] = true;
+ }
+ });
+ });
+ });
+ live_registrations.forEach(function(registration) {
+ if (!registration_id_set[registration.registration_id]) {
+ registration.unregistered = true;
+ unregistered_registrations.push(registration);
+ }
+ });
+ live_versions.forEach(function(version) {
+ if (!version_id_set[version.version_id]) {
+ unregistered_versions.push(version);
+ }
+ });
+ }
+
+ // Fired once per partition from the backend.
+ function onPartitionData(live_registrations,
+ live_versions,
+ stored_registrations,
+ partition_id,
+ partition_path) {
+ if (window.location.hash == "#iframe") {
+ // This page is loaded from chrome://inspect.
+ sendToInspectPage(live_registrations, partition_id);
+ return;
+ }
+ var unregistered_registrations = [];
+ var unregistered_versions = [];
+ getUnregisteredWorkers(stored_registrations,
+ live_registrations,
+ live_versions,
+ unregistered_registrations,
+ unregistered_versions);
+ var template;
+ var container = $('serviceworker-list');
+ // Existing templates are keyed by partition_id. This allows
+ // the UI to be updated in-place rather than refreshing the
+ // whole page.
+ for (var i = 0; i < container.childNodes.length; ++i) {
+ if (container.childNodes[i].partition_id == partition_id) {
+ template = container.childNodes[i];
+ }
+ }
+ // This is probably the first time we're loading.
+ if (!template) {
+ template = jstGetTemplate('serviceworker-list-template');
+ container.appendChild(template);
+ }
+ var fillLogFunc = fillLogForVersion.bind(this, partition_id);
+ stored_registrations.forEach(function(registration) {
+ [registration.active, registration.waiting].forEach(fillLogFunc);
+ });
+ unregistered_registrations.forEach(function(registration) {
+ [registration.active, registration.waiting].forEach(fillLogFunc);
+ });
+ unregistered_versions.forEach(fillLogFunc);
+ jstProcess(new JsEvalContext({
+ stored_registrations: stored_registrations,
+ unregistered_registrations: unregistered_registrations,
+ unregistered_versions: unregistered_versions,
+ partition_id: partition_id,
+ partition_path: partition_path}),
+ template);
+ for (var i = 0; i < COMMANDS.length; ++i) {
+ var handler = commandHandler(COMMANDS[i]);
+ var links = container.querySelectorAll('button.' + COMMANDS[i]);
+ for (var j = 0; j < links.length; ++j) {
+ if (!links[j].hasClickEvent) {
+ links[j].addEventListener('click', handler, false);
+ links[j].hasClickEvent = true;
+ }
+ }
+ }
+ }
+
+ function onWorkerStarted(partition_id, version_id, process_id, thread_id) {
+ update();
+ }
+
+ function onWorkerStopped(partition_id, version_id, process_id, thread_id) {
+ update();
+ }
+
+ function onErrorReported(partition_id,
+ version_id,
+ process_id,
+ thread_id,
+ error_info) {
+ outputLogMessage(partition_id,
+ version_id,
+ 'Error: ' + JSON.stringify(error_info) + '\n');
+ }
+
+ function onConsoleMessageReported(partition_id,
+ version_id,
+ process_id,
+ thread_id,
+ message) {
+ outputLogMessage(partition_id,
+ version_id,
+ 'Console: ' + JSON.stringify(message) + '\n');
+ }
+
+ function onVersionStateChanged(partition_id, version_id) {
+ update();
+ }
+
+ function onRegistrationStored(scope) {
+ update();
+ }
+
+ function onRegistrationDeleted(scope) {
+ update();
+ }
+
+ function outputLogMessage(partition_id, version_id, message) {
+ if (!(partition_id in allLogMessages)) {
+ allLogMessages[partition_id] = {};
+ }
+ var logMessages = allLogMessages[partition_id];
+ if (version_id in logMessages) {
+ logMessages[version_id] += message;
+ } else {
+ logMessages[version_id] = message;
+ }
+
+ var logAreas = document.querySelectorAll('textarea.serviceworker-log');
+ for (var i = 0; i < logAreas.length; ++i) {
+ var logArea = logAreas[i];
+ if (logArea.partition_id == partition_id &&
+ logArea.version_id == version_id) {
+ logArea.value += message;
+ }
+ }
+ }
+
+ return {
+ initialize: initialize,
+ onOptions: onOptions,
+ onOperationComplete: onOperationComplete,
+ onPartitionData: onPartitionData,
+ onWorkerStarted: onWorkerStarted,
+ onWorkerStopped: onWorkerStopped,
+ onErrorReported: onErrorReported,
+ onConsoleMessageReported: onConsoleMessageReported,
+ onVersionStateChanged: onVersionStateChanged,
+ onRegistrationStored: onRegistrationStored,
+ onRegistrationDeleted: onRegistrationDeleted,
+ };
+});
+
+document.addEventListener('DOMContentLoaded', serviceworker.initialize);
diff --git a/chromium/content/browser/safe_util_win.cc b/chromium/content/browser/safe_util_win.cc
index ac077f15350..e5843fd2bca 100644
--- a/chromium/content/browser/safe_util_win.cc
+++ b/chromium/content/browser/safe_util_win.cc
@@ -81,7 +81,7 @@ HRESULT AVScanFile(const base::FilePath& full_path,
// Note: SetSource looks like it needs to be called, even if empty.
// Docs say it is optional, but it appears not calling it at all sets
// a zone that is too restrictive.
- hr = attachment_services->SetSource(UTF8ToWide(source_url).c_str());
+ hr = attachment_services->SetSource(base::UTF8ToWide(source_url).c_str());
if (FAILED(hr))
return hr;
diff --git a/chromium/content/browser/screen_orientation/OWNERS b/chromium/content/browser/screen_orientation/OWNERS
new file mode 100644
index 00000000000..2d282460822
--- /dev/null
+++ b/chromium/content/browser/screen_orientation/OWNERS
@@ -0,0 +1 @@
+mlamouri@chromium.org
diff --git a/chromium/content/browser/screen_orientation/screen_orientation_dispatcher_host.cc b/chromium/content/browser/screen_orientation/screen_orientation_dispatcher_host.cc
new file mode 100644
index 00000000000..67174d028ea
--- /dev/null
+++ b/chromium/content/browser/screen_orientation/screen_orientation_dispatcher_host.cc
@@ -0,0 +1,85 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/screen_orientation/screen_orientation_dispatcher_host.h"
+
+#include "content/browser/screen_orientation/screen_orientation_provider.h"
+#include "content/common/screen_orientation_messages.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+
+namespace content {
+
+ScreenOrientationDispatcherHost::ScreenOrientationDispatcherHost(
+ WebContents* web_contents)
+ : WebContentsObserver(web_contents) {
+ if (!provider_.get())
+ provider_.reset(CreateProvider());
+}
+
+ScreenOrientationDispatcherHost::~ScreenOrientationDispatcherHost() {
+}
+
+bool ScreenOrientationDispatcherHost::OnMessageReceived(
+ const IPC::Message& message,
+ RenderFrameHost* render_frame_host) {
+ bool handled = true;
+
+ IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(ScreenOrientationDispatcherHost, message,
+ render_frame_host)
+ IPC_MESSAGE_HANDLER(ScreenOrientationHostMsg_LockRequest, OnLockRequest)
+ IPC_MESSAGE_HANDLER(ScreenOrientationHostMsg_Unlock, OnUnlockRequest)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+
+ return handled;
+}
+
+void ScreenOrientationDispatcherHost::OnOrientationChange(
+ blink::WebScreenOrientationType orientation) {
+ Send(new ScreenOrientationMsg_OrientationChange(orientation));
+}
+
+void ScreenOrientationDispatcherHost::SetProviderForTests(
+ ScreenOrientationProvider* provider) {
+ provider_.reset(provider);
+}
+
+void ScreenOrientationDispatcherHost::OnLockRequest(
+ RenderFrameHost* render_frame_host,
+ blink::WebScreenOrientationLockType orientation,
+ int request_id) {
+ if (!provider_) {
+ render_frame_host->Send(new ScreenOrientationMsg_LockError(
+ render_frame_host->GetRoutingID(),
+ request_id,
+ blink::WebLockOrientationCallback::ErrorTypeNotAvailable));
+ return;
+ }
+
+ // TODO(mlamouri): pass real values.
+ render_frame_host->Send(new ScreenOrientationMsg_LockSuccess(
+ render_frame_host->GetRoutingID(),
+ request_id,
+ 0,
+ blink::WebScreenOrientationPortraitPrimary));
+ provider_->LockOrientation(orientation);
+}
+
+void ScreenOrientationDispatcherHost::OnUnlockRequest(
+ RenderFrameHost* render_frame_host) {
+ if (!provider_.get())
+ return;
+
+ provider_->UnlockOrientation();
+}
+
+#if !defined(OS_ANDROID)
+// static
+ScreenOrientationProvider* ScreenOrientationDispatcherHost::CreateProvider() {
+ return NULL;
+}
+#endif
+
+} // namespace content
diff --git a/chromium/content/browser/screen_orientation/screen_orientation_dispatcher_host.h b/chromium/content/browser/screen_orientation/screen_orientation_dispatcher_host.h
new file mode 100644
index 00000000000..c86854a83ad
--- /dev/null
+++ b/chromium/content/browser/screen_orientation/screen_orientation_dispatcher_host.h
@@ -0,0 +1,50 @@
+// 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_SCREEN_ORIENTATION_SCREEN_ORIENTATION_DISPATCHER_HOST_H_
+#define CONTENT_BROWSER_SCREEN_ORIENTATION_SCREEN_ORIENTATION_DISPATCHER_HOST_H_
+
+#include "content/public/browser/web_contents_observer.h"
+#include "third_party/WebKit/public/platform/WebScreenOrientationLockType.h"
+#include "third_party/WebKit/public/platform/WebScreenOrientationType.h"
+
+namespace content {
+
+class RenderFrameHost;
+class ScreenOrientationProvider;
+class WebContents;
+
+// ScreenOrientationDispatcherHost receives lock and unlock requests from the
+// RenderFrames and dispatch them to the ScreenOrientationProvider. It also
+// make sure that the right RenderFrame get replied for each lock request.
+class CONTENT_EXPORT ScreenOrientationDispatcherHost
+ : public WebContentsObserver {
+ public:
+ explicit ScreenOrientationDispatcherHost(WebContents* web_contents);
+ virtual ~ScreenOrientationDispatcherHost();
+
+ // WebContentsObserver
+ virtual bool OnMessageReceived(const IPC::Message&,
+ RenderFrameHost* render_frame_host) OVERRIDE;
+
+ void OnOrientationChange(blink::WebScreenOrientationType orientation);
+
+ void SetProviderForTests(ScreenOrientationProvider* provider);
+
+ private:
+ void OnLockRequest(RenderFrameHost* render_frame_host,
+ blink::WebScreenOrientationLockType orientation,
+ int request_id);
+ void OnUnlockRequest(RenderFrameHost* render_frame_host);
+
+ static ScreenOrientationProvider* CreateProvider();
+
+ scoped_ptr<ScreenOrientationProvider> provider_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScreenOrientationDispatcherHost);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_SCREEN_ORIENTATION_SCREEN_ORIENTATION_DISPATCHER_HOST_H_
diff --git a/chromium/content/browser/screen_orientation/screen_orientation_provider.h b/chromium/content/browser/screen_orientation/screen_orientation_provider.h
new file mode 100644
index 00000000000..4ae858805b0
--- /dev/null
+++ b/chromium/content/browser/screen_orientation/screen_orientation_provider.h
@@ -0,0 +1,28 @@
+// 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_SCREEN_ORIENTATION_SCREEN_ORIENTATION_PROVIDER_H_
+#define CONTENT_BROWSER_SCREEN_ORIENTATION_SCREEN_ORIENTATION_PROVIDER_H_
+
+#include "third_party/WebKit/public/platform/WebScreenOrientationLockType.h"
+
+namespace content {
+
+// Interface that needs to be implemented by any backend that wants to handle
+// screen orientation lock/unlock.
+class ScreenOrientationProvider {
+ public:
+ // Lock the screen orientation to |orientations|.
+ virtual void LockOrientation(
+ blink::WebScreenOrientationLockType orientations) = 0;
+
+ // Unlock the screen orientation.
+ virtual void UnlockOrientation() = 0;
+
+ virtual ~ScreenOrientationProvider() {}
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_SCREEN_ORIENTATION_SCREEN_ORIENTATION_PROVIDER_H_
diff --git a/chromium/content/browser/screen_orientation/screen_orientation_provider_android.cc b/chromium/content/browser/screen_orientation/screen_orientation_provider_android.cc
new file mode 100644
index 00000000000..e8961bc4726
--- /dev/null
+++ b/chromium/content/browser/screen_orientation/screen_orientation_provider_android.cc
@@ -0,0 +1,51 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/screen_orientation/screen_orientation_provider_android.h"
+
+#include "content/browser/screen_orientation/screen_orientation_dispatcher_host.h"
+#include "jni/ScreenOrientationProvider_jni.h"
+
+namespace content {
+
+ScreenOrientationProviderAndroid::ScreenOrientationProviderAndroid() {
+}
+
+ScreenOrientationProviderAndroid::~ScreenOrientationProviderAndroid() {
+}
+
+// static
+bool ScreenOrientationProviderAndroid::Register(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+void ScreenOrientationProviderAndroid::LockOrientation(
+ blink::WebScreenOrientationLockType orientation) {
+ if (j_screen_orientation_provider_.is_null()) {
+ j_screen_orientation_provider_.Reset(Java_ScreenOrientationProvider_create(
+ base::android::AttachCurrentThread()));
+ }
+
+ Java_ScreenOrientationProvider_lockOrientation(
+ base::android::AttachCurrentThread(),
+ j_screen_orientation_provider_.obj(), orientation);
+}
+
+void ScreenOrientationProviderAndroid::UnlockOrientation() {
+ // j_screen_orientation_provider_ is set when locking. If the screen
+ // orientation was not locked, unlocking should be a no-op.
+ if (j_screen_orientation_provider_.is_null())
+ return;
+
+ Java_ScreenOrientationProvider_unlockOrientation(
+ base::android::AttachCurrentThread(),
+ j_screen_orientation_provider_.obj());
+}
+
+// static
+ScreenOrientationProvider* ScreenOrientationDispatcherHost::CreateProvider() {
+ return new ScreenOrientationProviderAndroid();
+}
+
+} // namespace content
diff --git a/chromium/content/browser/screen_orientation/screen_orientation_provider_android.h b/chromium/content/browser/screen_orientation/screen_orientation_provider_android.h
new file mode 100644
index 00000000000..a526e24596c
--- /dev/null
+++ b/chromium/content/browser/screen_orientation/screen_orientation_provider_android.h
@@ -0,0 +1,34 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_SCREEN_ORIENTATION_SCREEN_ORIENTATION_PROVIDER_ANDROID_H_
+#define CONTENT_BROWSER_SCREEN_ORIENTATION_SCREEN_ORIENTATION_PROVIDER_ANDROID_H_
+
+#include "base/android/jni_android.h"
+#include "base/compiler_specific.h"
+#include "content/browser/screen_orientation/screen_orientation_provider.h"
+
+namespace content {
+
+class ScreenOrientationProviderAndroid : public ScreenOrientationProvider {
+ public:
+ ScreenOrientationProviderAndroid();
+
+ static bool Register(JNIEnv* env);
+
+ // ScreenOrientationProvider
+ virtual void LockOrientation(blink::WebScreenOrientationLockType) OVERRIDE;
+ virtual void UnlockOrientation() OVERRIDE;
+
+ private:
+ virtual ~ScreenOrientationProviderAndroid();
+
+ base::android::ScopedJavaGlobalRef<jobject> j_screen_orientation_provider_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScreenOrientationProviderAndroid);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_SCREEN_ORIENTATION_SCREEN_ORIENTATION_PROVIDER_ANDROID_H_
diff --git a/chromium/content/browser/security_exploit_browsertest.cc b/chromium/content/browser/security_exploit_browsertest.cc
index fc648f0000f..9fadec049a7 100644
--- a/chromium/content/browser/security_exploit_browsertest.cc
+++ b/chromium/content/browser/security_exploit_browsertest.cc
@@ -6,20 +6,19 @@
#include "base/containers/hash_tables.h"
#include "content/browser/dom_storage/dom_storage_context_wrapper.h"
#include "content/browser/dom_storage/session_storage_namespace_impl.h"
+#include "content/browser/frame_host/navigator.h"
#include "content/browser/renderer_host/render_view_host_factory.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/view_messages.h"
#include "content/public/browser/browser_context.h"
-#include "content/public/browser/notification_service.h"
-#include "content/public/browser/notification_types.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/content_switches.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/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
namespace content {
@@ -58,9 +57,9 @@ RenderViewHostImpl* PrepareToDuplicateHosts(Shell* shell,
// Now, simulate a link click coming from the renderer.
GURL extension_url("https://bar.com/files/simple_page.html");
WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell->web_contents());
- wc->RequestOpenURL(
- shell->web_contents()->GetRenderViewHost(), extension_url,
- Referrer(), CURRENT_TAB, wc->GetFrameTree()->root()->frame_id(),
+ wc->GetFrameTree()->root()->navigator()->RequestOpenURL(
+ wc->GetFrameTree()->root()->current_frame_host(), extension_url,
+ Referrer(), CURRENT_TAB,
false, true);
// Since the navigation above requires a cross-process swap, there will be a
@@ -107,9 +106,9 @@ IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTest, SetWebUIProperty) {
EXPECT_EQ(0,
shell()->web_contents()->GetRenderViewHost()->GetEnabledBindings());
- content::WindowedNotificationObserver terminated(
- content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
- content::NotificationService::AllSources());
+ content::RenderProcessHostWatcher terminated(
+ shell()->web_contents(),
+ content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
shell()->web_contents()->GetRenderViewHost()->SetWebUIProperty(
"toolkit", "views");
terminated.Wait();
diff --git a/chromium/content/browser/service_worker/BUILD.gn b/chromium/content/browser/service_worker/BUILD.gn
new file mode 100644
index 00000000000..d9f206615fb
--- /dev/null
+++ b/chromium/content/browser/service_worker/BUILD.gn
@@ -0,0 +1,12 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//third_party/protobuf/proto_library.gni")
+
+proto_library("database_proto") {
+ sources = [
+ "service_worker_database.proto",
+ ]
+}
+
diff --git a/chromium/content/browser/service_worker/DEPS b/chromium/content/browser/service_worker/DEPS
new file mode 100644
index 00000000000..743a2f3baec
--- /dev/null
+++ b/chromium/content/browser/service_worker/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+third_party/leveldatabase",
+]
diff --git a/chromium/content/browser/service_worker/OWNERS b/chromium/content/browser/service_worker/OWNERS
index 633b8a4fd96..f9916062308 100644
--- a/chromium/content/browser/service_worker/OWNERS
+++ b/chromium/content/browser/service_worker/OWNERS
@@ -1,3 +1,12 @@
-alecflett@chromium.org
-kinuko@chromium.org
michaeln@chromium.org
+falken@chromium.org
+
+# may not be available
+kinuko@chromium.org
+
+# per-file owners
+per-file embedded_worker*=horo@chromium.org
+per-file service_worker_process_manager*=horo@chromium.org
+per-file service_worker_internals_ui*=horo@chromium.org
+per-file service_worker_database*=nhiroki@chromium.org
+per-file service_worker_storage*=nhiroki@chromium.org
diff --git a/chromium/content/browser/service_worker/embedded_worker_instance.cc b/chromium/content/browser/service_worker/embedded_worker_instance.cc
index 07998845986..fd2d201a660 100644
--- a/chromium/content/browser/service_worker/embedded_worker_instance.cc
+++ b/chromium/content/browser/service_worker/embedded_worker_instance.cc
@@ -4,40 +4,144 @@
#include "content/browser/service_worker/embedded_worker_instance.h"
+#include "base/bind_helpers.h"
+#include "content/browser/devtools/embedded_worker_devtools_manager.h"
#include "content/browser/service_worker/embedded_worker_registry.h"
+#include "content/browser/service_worker/service_worker_context_core.h"
+#include "content/common/service_worker/embedded_worker_messages.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_process_host.h"
+#include "ipc/ipc_message.h"
#include "url/gurl.h"
namespace content {
-EmbeddedWorkerInstance::~EmbeddedWorkerInstance() {
- registry_->RemoveWorker(embedded_worker_id_);
+namespace {
+
+// Functor to sort by the .second element of a struct.
+struct SecondGreater {
+ template <typename Value>
+ bool operator()(const Value& lhs, const Value& rhs) {
+ return lhs.second > rhs.second;
+ }
+};
+
+void NotifyWorkerContextStarted(int worker_process_id, int worker_route_id) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(
+ NotifyWorkerContextStarted, worker_process_id, worker_route_id));
+ return;
+ }
+ EmbeddedWorkerDevToolsManager::GetInstance()->WorkerContextStarted(
+ worker_process_id, worker_route_id);
}
-bool EmbeddedWorkerInstance::Start(
+void NotifyWorkerDestroyed(int worker_process_id, int worker_route_id) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(NotifyWorkerDestroyed, worker_process_id, worker_route_id));
+ return;
+ }
+ EmbeddedWorkerDevToolsManager::GetInstance()->WorkerDestroyed(
+ worker_process_id, worker_route_id);
+}
+
+void RegisterToWorkerDevToolsManager(
+ int process_id,
+ const ServiceWorkerContextCore* const service_worker_context,
int64 service_worker_version_id,
- const GURL& script_url) {
+ const base::Callback<void(int worker_devtools_agent_route_id,
+ bool pause_on_start)>& callback) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ BrowserThread::PostTask(BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(RegisterToWorkerDevToolsManager,
+ process_id,
+ service_worker_context,
+ service_worker_version_id,
+ callback));
+ return;
+ }
+ int worker_devtools_agent_route_id = MSG_ROUTING_NONE;
+ bool pause_on_start = false;
+ if (RenderProcessHost* rph = RenderProcessHost::FromID(process_id)) {
+ // |rph| may be NULL in unit tests.
+ worker_devtools_agent_route_id = rph->GetNextRoutingID();
+ pause_on_start =
+ EmbeddedWorkerDevToolsManager::GetInstance()->ServiceWorkerCreated(
+ process_id,
+ worker_devtools_agent_route_id,
+ EmbeddedWorkerDevToolsManager::ServiceWorkerIdentifier(
+ service_worker_context, service_worker_version_id));
+ }
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(callback, worker_devtools_agent_route_id, pause_on_start));
+}
+
+} // namespace
+
+EmbeddedWorkerInstance::~EmbeddedWorkerInstance() {
+ if (status_ == STARTING || status_ == RUNNING)
+ Stop();
+ if (worker_devtools_agent_route_id_ != MSG_ROUTING_NONE)
+ NotifyWorkerDestroyed(process_id_, worker_devtools_agent_route_id_);
+ if (context_ && process_id_ != -1)
+ context_->process_manager()->ReleaseWorkerProcess(embedded_worker_id_);
+ registry_->RemoveWorker(process_id_, embedded_worker_id_);
+}
+
+void EmbeddedWorkerInstance::Start(int64 service_worker_version_id,
+ const GURL& scope,
+ const GURL& script_url,
+ const std::vector<int>& possible_process_ids,
+ const StatusCallback& callback) {
+ if (!context_) {
+ callback.Run(SERVICE_WORKER_ERROR_ABORT);
+ return;
+ }
DCHECK(status_ == STOPPED);
- if (!ChooseProcess())
- return false;
status_ = STARTING;
- bool success = registry_->StartWorker(
- process_id_,
+ scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params(
+ new EmbeddedWorkerMsg_StartWorker_Params());
+ params->embedded_worker_id = embedded_worker_id_;
+ params->service_worker_version_id = service_worker_version_id;
+ params->scope = scope;
+ params->script_url = script_url;
+ params->worker_devtools_agent_route_id = MSG_ROUTING_NONE;
+ params->pause_on_start = false;
+ context_->process_manager()->AllocateWorkerProcess(
embedded_worker_id_,
- service_worker_version_id,
- script_url);
- if (!success) {
- status_ = STOPPED;
- process_id_ = -1;
- }
- return success;
+ SortProcesses(possible_process_ids),
+ script_url,
+ base::Bind(&EmbeddedWorkerInstance::RunProcessAllocated,
+ weak_factory_.GetWeakPtr(),
+ context_,
+ base::Passed(&params),
+ callback));
}
-bool EmbeddedWorkerInstance::Stop() {
+ServiceWorkerStatusCode EmbeddedWorkerInstance::Stop() {
DCHECK(status_ == STARTING || status_ == RUNNING);
- const bool success = registry_->StopWorker(process_id_, embedded_worker_id_);
- if (success)
+ ServiceWorkerStatusCode status =
+ registry_->StopWorker(process_id_, embedded_worker_id_);
+ if (status == SERVICE_WORKER_OK)
status_ = STOPPING;
- return success;
+ return status;
+}
+
+ServiceWorkerStatusCode EmbeddedWorkerInstance::SendMessage(
+ const IPC::Message& message) {
+ DCHECK(status_ == RUNNING);
+ return registry_->Send(process_id_,
+ new EmbeddedWorkerContextMsg_MessageToWorker(
+ thread_id_, embedded_worker_id_, message));
}
void EmbeddedWorkerInstance::AddProcessReference(int process_id) {
@@ -58,42 +162,167 @@ void EmbeddedWorkerInstance::ReleaseProcessReference(int process_id) {
}
EmbeddedWorkerInstance::EmbeddedWorkerInstance(
- EmbeddedWorkerRegistry* registry,
+ base::WeakPtr<ServiceWorkerContextCore> context,
int embedded_worker_id)
- : registry_(registry),
+ : context_(context),
+ registry_(context->embedded_worker_registry()),
embedded_worker_id_(embedded_worker_id),
status_(STOPPED),
process_id_(-1),
- thread_id_(-1) {
+ thread_id_(-1),
+ worker_devtools_agent_route_id_(MSG_ROUTING_NONE),
+ weak_factory_(this) {
+}
+
+// static
+void EmbeddedWorkerInstance::RunProcessAllocated(
+ base::WeakPtr<EmbeddedWorkerInstance> instance,
+ base::WeakPtr<ServiceWorkerContextCore> context,
+ scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
+ const EmbeddedWorkerInstance::StatusCallback& callback,
+ ServiceWorkerStatusCode status,
+ int process_id) {
+ if (!context) {
+ callback.Run(SERVICE_WORKER_ERROR_ABORT);
+ return;
+ }
+ if (!instance) {
+ if (status == SERVICE_WORKER_OK) {
+ // We only have a process allocated if the status is OK.
+ context->process_manager()->ReleaseWorkerProcess(
+ params->embedded_worker_id);
+ }
+ callback.Run(SERVICE_WORKER_ERROR_ABORT);
+ return;
+ }
+ instance->ProcessAllocated(params.Pass(), callback, process_id, status);
+}
+
+void EmbeddedWorkerInstance::ProcessAllocated(
+ scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
+ const StatusCallback& callback,
+ int process_id,
+ ServiceWorkerStatusCode status) {
+ DCHECK_EQ(process_id_, -1);
+ if (status != SERVICE_WORKER_OK) {
+ status_ = STOPPED;
+ callback.Run(status);
+ return;
+ }
+ const int64 service_worker_version_id = params->service_worker_version_id;
+ process_id_ = process_id;
+ RegisterToWorkerDevToolsManager(
+ process_id,
+ context_.get(),
+ service_worker_version_id,
+ base::Bind(&EmbeddedWorkerInstance::SendStartWorker,
+ weak_factory_.GetWeakPtr(),
+ base::Passed(&params),
+ callback));
+}
+
+void EmbeddedWorkerInstance::SendStartWorker(
+ scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
+ const StatusCallback& callback,
+ int worker_devtools_agent_route_id,
+ bool pause_on_start) {
+ worker_devtools_agent_route_id_ = worker_devtools_agent_route_id;
+ params->worker_devtools_agent_route_id = worker_devtools_agent_route_id;
+ params->pause_on_start = pause_on_start;
+ registry_->SendStartWorker(params.Pass(), callback, process_id_);
+}
+
+void EmbeddedWorkerInstance::OnScriptLoaded() {
+ if (worker_devtools_agent_route_id_ != MSG_ROUTING_NONE)
+ NotifyWorkerContextStarted(process_id_, worker_devtools_agent_route_id_);
+}
+
+void EmbeddedWorkerInstance::OnScriptLoadFailed() {
}
void EmbeddedWorkerInstance::OnStarted(int thread_id) {
+ // Stop is requested before OnStarted is sent back from the worker.
+ if (status_ == STOPPING)
+ return;
DCHECK(status_ == STARTING);
status_ = RUNNING;
thread_id_ = thread_id;
+ FOR_EACH_OBSERVER(Listener, listener_list_, OnStarted());
}
void EmbeddedWorkerInstance::OnStopped() {
+ if (worker_devtools_agent_route_id_ != MSG_ROUTING_NONE)
+ NotifyWorkerDestroyed(process_id_, worker_devtools_agent_route_id_);
+ if (context_)
+ context_->process_manager()->ReleaseWorkerProcess(embedded_worker_id_);
status_ = STOPPED;
process_id_ = -1;
thread_id_ = -1;
+ worker_devtools_agent_route_id_ = MSG_ROUTING_NONE;
+ FOR_EACH_OBSERVER(Listener, listener_list_, OnStopped());
+}
+
+bool EmbeddedWorkerInstance::OnMessageReceived(const IPC::Message& message) {
+ ListenerList::Iterator it(listener_list_);
+ while (Listener* listener = it.GetNext()) {
+ if (listener->OnMessageReceived(message))
+ return true;
+ }
+ return false;
+}
+
+void EmbeddedWorkerInstance::OnReportException(
+ const base::string16& error_message,
+ int line_number,
+ int column_number,
+ const GURL& source_url) {
+ FOR_EACH_OBSERVER(
+ Listener,
+ listener_list_,
+ OnReportException(error_message, line_number, column_number, source_url));
+}
+
+void EmbeddedWorkerInstance::OnReportConsoleMessage(
+ int source_identifier,
+ int message_level,
+ const base::string16& message,
+ int line_number,
+ const GURL& source_url) {
+ FOR_EACH_OBSERVER(
+ Listener,
+ listener_list_,
+ OnReportConsoleMessage(
+ source_identifier, message_level, message, line_number, source_url));
+}
+
+void EmbeddedWorkerInstance::AddListener(Listener* listener) {
+ listener_list_.AddObserver(listener);
}
-bool EmbeddedWorkerInstance::ChooseProcess() {
- DCHECK_EQ(-1, process_id_);
- // Naive implementation; chooses a process which has the biggest number of
- // associated providers (so that hopefully likely live longer).
- ProcessRefMap::iterator max_ref_iter = process_refs_.end();
- for (ProcessRefMap::iterator iter = process_refs_.begin();
- iter != process_refs_.end(); ++iter) {
- if (max_ref_iter == process_refs_.end() ||
- max_ref_iter->second < iter->second)
- max_ref_iter = iter;
+void EmbeddedWorkerInstance::RemoveListener(Listener* listener) {
+ listener_list_.RemoveObserver(listener);
+}
+
+std::vector<int> EmbeddedWorkerInstance::SortProcesses(
+ const std::vector<int>& possible_process_ids) const {
+ // Add the |possible_process_ids| to the existing process_refs_ since each one
+ // is likely to take a reference once the SW starts up.
+ ProcessRefMap refs_with_new_ids = process_refs_;
+ for (std::vector<int>::const_iterator it = possible_process_ids.begin();
+ it != possible_process_ids.end();
+ ++it) {
+ refs_with_new_ids[*it]++;
}
- if (max_ref_iter == process_refs_.end())
- return false;
- process_id_ = max_ref_iter->first;
- return true;
+
+ std::vector<std::pair<int, int> > counted(refs_with_new_ids.begin(),
+ refs_with_new_ids.end());
+ // Sort descending by the reference count.
+ std::sort(counted.begin(), counted.end(), SecondGreater());
+
+ std::vector<int> result(counted.size());
+ for (size_t i = 0; i < counted.size(); ++i)
+ result[i] = counted[i].first;
+ return result;
}
} // namespace content
diff --git a/chromium/content/browser/service_worker/embedded_worker_instance.h b/chromium/content/browser/service_worker/embedded_worker_instance.h
index fb0fe19bb6c..9e404ff4640 100644
--- a/chromium/content/browser/service_worker/embedded_worker_instance.h
+++ b/chromium/content/browser/service_worker/embedded_worker_instance.h
@@ -6,25 +6,38 @@
#define CONTENT_BROWSER_SERVICE_WORKER_EMBEDDED_WORKER_INSTANCE_H_
#include <map>
+#include <vector>
#include "base/basictypes.h"
#include "base/callback_forward.h"
#include "base/gtest_prod_util.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/strings/string16.h"
#include "content/common/content_export.h"
+#include "content/common/service_worker/service_worker_status_code.h"
+#include "url/gurl.h"
-class GURL;
+struct EmbeddedWorkerMsg_StartWorker_Params;
+
+namespace IPC {
+class Message;
+}
namespace content {
class EmbeddedWorkerRegistry;
+class ServiceWorkerContextCore;
+struct ServiceWorkerFetchRequest;
// This gives an interface to control one EmbeddedWorker instance, which
// may be 'in-waiting' or running in one of the child processes added by
// AddProcessReference().
class CONTENT_EXPORT EmbeddedWorkerInstance {
public:
+ typedef base::Callback<void(ServiceWorkerStatusCode)> StatusCallback;
enum Status {
STOPPED,
STARTING,
@@ -32,44 +45,109 @@ class CONTENT_EXPORT EmbeddedWorkerInstance {
STOPPING,
};
+ class Listener {
+ public:
+ virtual ~Listener() {}
+ virtual void OnStarted() = 0;
+ virtual void OnStopped() = 0;
+ virtual void OnReportException(const base::string16& error_message,
+ int line_number,
+ int column_number,
+ const GURL& source_url) {}
+ virtual void OnReportConsoleMessage(int source_identifier,
+ int message_level,
+ const base::string16& message,
+ int line_number,
+ const GURL& source_url) {}
+ // These should return false if the message is not handled by this
+ // listener. (TODO(kinuko): consider using IPC::Listener interface)
+ // TODO(kinuko): Deprecate OnReplyReceived.
+ virtual bool OnMessageReceived(const IPC::Message& message) = 0;
+ };
+
~EmbeddedWorkerInstance();
- // Starts the worker. It is invalid to call this when the worker is
- // not in STOPPED status.
- // This returns false if starting a worker fails immediately, e.g. when
- // IPC couldn't be sent to the worker or no process was available.
- bool Start(int64 service_worker_version_id,
- const GURL& script_url);
+ // Starts the worker. It is invalid to call this when the worker is not in
+ // STOPPED status. |callback| is invoked when the worker's process is created
+ // if necessary and the IPC to evaluate the worker's script is sent.
+ // Observer::OnStarted() is run when the worker is actually started.
+ void Start(int64 service_worker_version_id,
+ const GURL& scope,
+ const GURL& script_url,
+ const std::vector<int>& possible_process_ids,
+ const StatusCallback& callback);
// Stops the worker. It is invalid to call this when the worker is
// not in STARTING or RUNNING status.
// This returns false if stopping a worker fails immediately, e.g. when
// IPC couldn't be sent to the worker.
- bool Stop();
+ ServiceWorkerStatusCode Stop();
+
+ // Sends |message| to the embedded worker running in the child process.
+ // It is invalid to call this while the worker is not in RUNNING status.
+ ServiceWorkerStatusCode SendMessage(const IPC::Message& message);
// Add or remove |process_id| to the internal process set where this
// worker can be started.
void AddProcessReference(int process_id);
void ReleaseProcessReference(int process_id);
+ bool HasProcessToRun() const { return !process_refs_.empty(); }
int embedded_worker_id() const { return embedded_worker_id_; }
Status status() const { return status_; }
int process_id() const { return process_id_; }
int thread_id() const { return thread_id_; }
+ int worker_devtools_agent_route_id() const {
+ return worker_devtools_agent_route_id_;
+ }
+
+ void AddListener(Listener* listener);
+ void RemoveListener(Listener* listener);
private:
+ typedef ObserverList<Listener> ListenerList;
+
friend class EmbeddedWorkerRegistry;
FRIEND_TEST_ALL_PREFIXES(EmbeddedWorkerInstanceTest, StartAndStop);
+ FRIEND_TEST_ALL_PREFIXES(EmbeddedWorkerInstanceTest, SortProcesses);
typedef std::map<int, int> ProcessRefMap;
// Constructor is called via EmbeddedWorkerRegistry::CreateWorker().
// This instance holds a ref of |registry|.
- EmbeddedWorkerInstance(EmbeddedWorkerRegistry* registry,
+ EmbeddedWorkerInstance(base::WeakPtr<ServiceWorkerContextCore> context,
int embedded_worker_id);
+ // Called back from ServiceWorkerProcessManager after Start() passes control
+ // to the UI thread to acquire a reference to the process.
+ static void RunProcessAllocated(
+ base::WeakPtr<EmbeddedWorkerInstance> instance,
+ base::WeakPtr<ServiceWorkerContextCore> context,
+ scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
+ const EmbeddedWorkerInstance::StatusCallback& callback,
+ ServiceWorkerStatusCode status,
+ int process_id);
+ void ProcessAllocated(scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
+ const StatusCallback& callback,
+ int process_id,
+ ServiceWorkerStatusCode status);
+ // Called back after ProcessAllocated() passes control to the UI thread to
+ // register to WorkerDevToolsManager.
+ void SendStartWorker(scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
+ const StatusCallback& callback,
+ int worker_devtools_agent_route_id,
+ bool pause_on_start);
+
// Called back from Registry when the worker instance has ack'ed that
- // its WorkerGlobalScope is actually started on |thread_id| in the
+ // it finished loading the script.
+ void OnScriptLoaded();
+
+ // Called back from Registry when the worker instance has ack'ed that
+ // it failed to load the script.
+ void OnScriptLoadFailed();
+
+ // Called back from Registry when the worker instance has ack'ed that
+ // its WorkerGlobalScope is actually started and parsed on |thread_id| in the
// child process.
// This will change the internal status from STARTING to RUNNING.
void OnStarted(int thread_id);
@@ -80,10 +158,30 @@ class CONTENT_EXPORT EmbeddedWorkerInstance {
// STOPPED.
void OnStopped();
- // Chooses a process to start this worker and populate process_id_.
- // Returns false when no process is available.
- bool ChooseProcess();
-
+ // Called back from Registry when the worker instance sends message
+ // to the browser (i.e. EmbeddedWorker observers).
+ // Returns false if the message is not handled.
+ bool OnMessageReceived(const IPC::Message& message);
+
+ // Called back from Registry when the worker instance reports the exception.
+ void OnReportException(const base::string16& error_message,
+ int line_number,
+ int column_number,
+ const GURL& source_url);
+
+ // Called back from Registry when the worker instance reports to the console.
+ void OnReportConsoleMessage(int source_identifier,
+ int message_level,
+ const base::string16& message,
+ int line_number,
+ const GURL& source_url);
+
+ // Chooses a list of processes to try to start this worker in, ordered by how
+ // many clients are currently in those processes.
+ std::vector<int> SortProcesses(
+ const std::vector<int>& possible_process_ids) const;
+
+ base::WeakPtr<ServiceWorkerContextCore> context_;
scoped_refptr<EmbeddedWorkerRegistry> registry_;
const int embedded_worker_id_;
Status status_;
@@ -91,8 +189,12 @@ class CONTENT_EXPORT EmbeddedWorkerInstance {
// Current running information. -1 indicates the worker is not running.
int process_id_;
int thread_id_;
+ int worker_devtools_agent_route_id_;
ProcessRefMap process_refs_;
+ ListenerList listener_list_;
+
+ base::WeakPtrFactory<EmbeddedWorkerInstance> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(EmbeddedWorkerInstance);
};
diff --git a/chromium/content/browser/service_worker/embedded_worker_instance_unittest.cc b/chromium/content/browser/service_worker/embedded_worker_instance_unittest.cc
index 45b2f29dc7f..d14ccd22f25 100644
--- a/chromium/content/browser/service_worker/embedded_worker_instance_unittest.cc
+++ b/chromium/content/browser/service_worker/embedded_worker_instance_unittest.cc
@@ -3,43 +3,21 @@
// found in the LICENSE file.
#include "base/basictypes.h"
+#include "base/run_loop.h"
#include "base/stl_util.h"
#include "content/browser/service_worker/embedded_worker_instance.h"
#include "content/browser/service_worker/embedded_worker_registry.h"
+#include "content/browser/service_worker/embedded_worker_test_helper.h"
#include "content/browser/service_worker/service_worker_context_core.h"
-#include "content/common/service_worker_messages.h"
+#include "content/browser/service_worker/service_worker_context_wrapper.h"
+#include "content/common/service_worker/embedded_worker_messages.h"
#include "content/public/test/test_browser_thread_bundle.h"
-#include "ipc/ipc_message.h"
-#include "ipc/ipc_sender.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
-namespace {
-
-typedef std::vector<IPC::Message*> MessageList;
-
-class FakeSender : public IPC::Sender {
- public:
- FakeSender() {}
- virtual ~FakeSender() {
- STLDeleteContainerPointers(sent_messages_.begin(), sent_messages_.end());
- }
-
- // IPC::Sender implementation.
- virtual bool Send(IPC::Message* message) OVERRIDE {
- sent_messages_.push_back(message);
- return true;
- }
-
- const MessageList& sent_messages() { return sent_messages_; }
-
- private:
- MessageList sent_messages_;
- DISALLOW_COPY_AND_ASSIGN(FakeSender);
-};
-
-} // namespace
+static const int kRenderProcessId = 11;
class EmbeddedWorkerInstanceTest : public testing::Test {
protected:
@@ -47,91 +25,143 @@ class EmbeddedWorkerInstanceTest : public testing::Test {
: thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {}
virtual void SetUp() OVERRIDE {
- context_.reset(new ServiceWorkerContextCore(base::FilePath(), NULL));
+ helper_.reset(new EmbeddedWorkerTestHelper(kRenderProcessId));
}
virtual void TearDown() OVERRIDE {
- context_.reset();
+ helper_.reset();
}
+ ServiceWorkerContextCore* context() { return helper_->context(); }
+
EmbeddedWorkerRegistry* embedded_worker_registry() {
- DCHECK(context_);
- return context_->embedded_worker_registry();
+ DCHECK(context());
+ return context()->embedded_worker_registry();
}
+ IPC::TestSink* ipc_sink() { return helper_->ipc_sink(); }
+
TestBrowserThreadBundle thread_bundle_;
- scoped_ptr<ServiceWorkerContextCore> context_;
+ scoped_ptr<EmbeddedWorkerTestHelper> helper_;
DISALLOW_COPY_AND_ASSIGN(EmbeddedWorkerInstanceTest);
};
+static void SaveStatusAndCall(ServiceWorkerStatusCode* out,
+ const base::Closure& callback,
+ ServiceWorkerStatusCode status) {
+ *out = status;
+ callback.Run();
+}
+
TEST_F(EmbeddedWorkerInstanceTest, StartAndStop) {
scoped_ptr<EmbeddedWorkerInstance> worker =
embedded_worker_registry()->CreateWorker();
EXPECT_EQ(EmbeddedWorkerInstance::STOPPED, worker->status());
- FakeSender fake_sender;
- const int process_id = 11;
- const int thread_id = 33;
+ const int embedded_worker_id = worker->embedded_worker_id();
const int64 service_worker_version_id = 55L;
+ const GURL scope("http://example.com/*");
const GURL url("http://example.com/worker.js");
- // This fails as we have no available process yet.
- EXPECT_FALSE(worker->Start(service_worker_version_id, url));
- EXPECT_EQ(EmbeddedWorkerInstance::STOPPED, worker->status());
-
// Simulate adding one process to the worker.
- worker->AddProcessReference(process_id);
- embedded_worker_registry()->AddChildProcessSender(process_id, &fake_sender);
+ helper_->SimulateAddProcessToWorker(embedded_worker_id, kRenderProcessId);
// Start should succeed.
- EXPECT_TRUE(worker->Start(service_worker_version_id, url));
+ ServiceWorkerStatusCode status;
+ base::RunLoop run_loop;
+ worker->Start(
+ service_worker_version_id,
+ scope,
+ url,
+ std::vector<int>(),
+ base::Bind(&SaveStatusAndCall, &status, run_loop.QuitClosure()));
+ run_loop.Run();
+ EXPECT_EQ(SERVICE_WORKER_OK, status);
EXPECT_EQ(EmbeddedWorkerInstance::STARTING, worker->status());
+ base::RunLoop().RunUntilIdle();
- // Simulate an upcall from embedded worker to notify that it's started.
- worker->OnStarted(thread_id);
+ // Worker started message should be notified (by EmbeddedWorkerTestHelper).
EXPECT_EQ(EmbeddedWorkerInstance::RUNNING, worker->status());
- EXPECT_EQ(process_id, worker->process_id());
- EXPECT_EQ(thread_id, worker->thread_id());
+ EXPECT_EQ(kRenderProcessId, worker->process_id());
// Stop the worker.
- EXPECT_TRUE(worker->Stop());
+ EXPECT_EQ(SERVICE_WORKER_OK, worker->Stop());
EXPECT_EQ(EmbeddedWorkerInstance::STOPPING, worker->status());
+ base::RunLoop().RunUntilIdle();
- // Simulate an upcall from embedded worker to notify that it's stopped.
- worker->OnStopped();
+ // Worker stopped message should be notified (by EmbeddedWorkerTestHelper).
EXPECT_EQ(EmbeddedWorkerInstance::STOPPED, worker->status());
// Verify that we've sent two messages to start and terminate the worker.
- const MessageList& messages = fake_sender.sent_messages();
- ASSERT_EQ(2U, messages.size());
- ASSERT_EQ(ServiceWorkerMsg_StartWorker::ID, messages[0]->type());
- ASSERT_EQ(ServiceWorkerMsg_TerminateWorker::ID, messages[1]->type());
+ ASSERT_TRUE(ipc_sink()->GetUniqueMessageMatching(
+ EmbeddedWorkerMsg_StartWorker::ID));
+ ASSERT_TRUE(ipc_sink()->GetUniqueMessageMatching(
+ EmbeddedWorkerMsg_StopWorker::ID));
}
-TEST_F(EmbeddedWorkerInstanceTest, ChooseProcess) {
+TEST_F(EmbeddedWorkerInstanceTest, InstanceDestroyedBeforeStartFinishes) {
scoped_ptr<EmbeddedWorkerInstance> worker =
embedded_worker_registry()->CreateWorker();
EXPECT_EQ(EmbeddedWorkerInstance::STOPPED, worker->status());
- FakeSender fake_sender;
+ const int64 service_worker_version_id = 55L;
+ const GURL scope("http://example.com/*");
+ const GURL url("http://example.com/worker.js");
+
+ ServiceWorkerStatusCode status;
+ base::RunLoop run_loop;
+ // Begin starting the worker.
+ std::vector<int> available_process;
+ available_process.push_back(kRenderProcessId);
+ worker->Start(
+ service_worker_version_id,
+ scope,
+ url,
+ available_process,
+ base::Bind(&SaveStatusAndCall, &status, run_loop.QuitClosure()));
+ // But destroy it before it gets a chance to complete.
+ worker.reset();
+ run_loop.Run();
+ EXPECT_EQ(SERVICE_WORKER_ERROR_ABORT, status);
+
+ // Verify that we didn't send the message to start the worker.
+ ASSERT_FALSE(
+ ipc_sink()->GetUniqueMessageMatching(EmbeddedWorkerMsg_StartWorker::ID));
+}
+
+TEST_F(EmbeddedWorkerInstanceTest, SortProcesses) {
+ scoped_ptr<EmbeddedWorkerInstance> worker =
+ embedded_worker_registry()->CreateWorker();
+ EXPECT_EQ(EmbeddedWorkerInstance::STOPPED, worker->status());
// Simulate adding processes to the worker.
// Process 1 has 1 ref, 2 has 2 refs and 3 has 3 refs.
- worker->AddProcessReference(1);
- worker->AddProcessReference(2);
- worker->AddProcessReference(2);
- worker->AddProcessReference(3);
- worker->AddProcessReference(3);
- worker->AddProcessReference(3);
- embedded_worker_registry()->AddChildProcessSender(1, &fake_sender);
- embedded_worker_registry()->AddChildProcessSender(2, &fake_sender);
- embedded_worker_registry()->AddChildProcessSender(3, &fake_sender);
+ const int embedded_worker_id = worker->embedded_worker_id();
+ helper_->SimulateAddProcessToWorker(embedded_worker_id, 1);
+ helper_->SimulateAddProcessToWorker(embedded_worker_id, 2);
+ helper_->SimulateAddProcessToWorker(embedded_worker_id, 2);
+ helper_->SimulateAddProcessToWorker(embedded_worker_id, 3);
+ helper_->SimulateAddProcessToWorker(embedded_worker_id, 3);
+ helper_->SimulateAddProcessToWorker(embedded_worker_id, 3);
// Process 3 has the biggest # of references and it should be chosen.
- EXPECT_TRUE(worker->Start(1L, GURL("http://example.com/worker.js")));
- EXPECT_EQ(EmbeddedWorkerInstance::STARTING, worker->status());
- EXPECT_EQ(3, worker->process_id());
+ EXPECT_THAT(worker->SortProcesses(std::vector<int>()),
+ testing::ElementsAre(3, 2, 1));
+ EXPECT_EQ(-1, worker->process_id());
+
+ // Argument processes are added to the existing set, but only for a single
+ // call.
+ std::vector<int> registering_processes;
+ registering_processes.push_back(1);
+ registering_processes.push_back(1);
+ registering_processes.push_back(1);
+ registering_processes.push_back(4);
+ EXPECT_THAT(worker->SortProcesses(registering_processes),
+ testing::ElementsAre(1, 3, 2, 4));
+
+ EXPECT_THAT(worker->SortProcesses(std::vector<int>()),
+ testing::ElementsAre(3, 2, 1));
}
} // namespace content
diff --git a/chromium/content/browser/service_worker/embedded_worker_registry.cc b/chromium/content/browser/service_worker/embedded_worker_registry.cc
index f8f0fbe7d5f..988b0563f42 100644
--- a/chromium/content/browser/service_worker/embedded_worker_registry.cc
+++ b/chromium/content/browser/service_worker/embedded_worker_registry.cc
@@ -4,10 +4,14 @@
#include "content/browser/service_worker/embedded_worker_registry.h"
+#include "base/bind_helpers.h"
#include "base/stl_util.h"
+#include "content/browser/renderer_host/render_widget_helper.h"
#include "content/browser/service_worker/embedded_worker_instance.h"
#include "content/browser/service_worker/service_worker_context_core.h"
-#include "content/common/service_worker_messages.h"
+#include "content/browser/service_worker/service_worker_context_wrapper.h"
+#include "content/common/service_worker/embedded_worker_messages.h"
+#include "content/public/browser/browser_thread.h"
#include "ipc/ipc_message.h"
#include "ipc/ipc_sender.h"
@@ -15,56 +19,197 @@ namespace content {
EmbeddedWorkerRegistry::EmbeddedWorkerRegistry(
base::WeakPtr<ServiceWorkerContextCore> context)
- : context_(context),
- next_embedded_worker_id_(0) {}
+ : context_(context), next_embedded_worker_id_(0) {
+}
scoped_ptr<EmbeddedWorkerInstance> EmbeddedWorkerRegistry::CreateWorker() {
scoped_ptr<EmbeddedWorkerInstance> worker(
- new EmbeddedWorkerInstance(this, next_embedded_worker_id_));
+ new EmbeddedWorkerInstance(context_, next_embedded_worker_id_));
worker_map_[next_embedded_worker_id_++] = worker.get();
return worker.Pass();
}
-void EmbeddedWorkerRegistry::RemoveWorker(int embedded_worker_id) {
- DCHECK(ContainsKey(worker_map_, embedded_worker_id));
- worker_map_.erase(embedded_worker_id);
+ServiceWorkerStatusCode EmbeddedWorkerRegistry::StopWorker(
+ int process_id, int embedded_worker_id) {
+ return Send(process_id,
+ new EmbeddedWorkerMsg_StopWorker(embedded_worker_id));
+}
+
+bool EmbeddedWorkerRegistry::OnMessageReceived(const IPC::Message& message) {
+ // TODO(kinuko): Move all EmbeddedWorker message handling from
+ // ServiceWorkerDispatcherHost.
+
+ WorkerInstanceMap::iterator found = worker_map_.find(message.routing_id());
+ if (found == worker_map_.end()) {
+ LOG(ERROR) << "Worker " << message.routing_id() << " not registered";
+ return false;
+ }
+ return found->second->OnMessageReceived(message);
+}
+
+void EmbeddedWorkerRegistry::Shutdown() {
+ for (WorkerInstanceMap::iterator it = worker_map_.begin();
+ it != worker_map_.end();
+ ++it) {
+ it->second->Stop();
+ }
+}
+
+void EmbeddedWorkerRegistry::OnWorkerScriptLoaded(int process_id,
+ int embedded_worker_id) {
+ WorkerInstanceMap::iterator found = worker_map_.find(embedded_worker_id);
+ if (found == worker_map_.end()) {
+ LOG(ERROR) << "Worker " << embedded_worker_id << " not registered";
+ return;
+ }
+ if (found->second->process_id() != process_id) {
+ LOG(ERROR) << "Incorrect embedded_worker_id";
+ return;
+ }
+ found->second->OnScriptLoaded();
+}
+
+void EmbeddedWorkerRegistry::OnWorkerScriptLoadFailed(int process_id,
+ int embedded_worker_id) {
+ WorkerInstanceMap::iterator found = worker_map_.find(embedded_worker_id);
+ if (found == worker_map_.end()) {
+ LOG(ERROR) << "Worker " << embedded_worker_id << " not registered";
+ return;
+ }
+ if (found->second->process_id() != process_id) {
+ LOG(ERROR) << "Incorrect embedded_worker_id";
+ return;
+ }
+ found->second->OnScriptLoadFailed();
+}
+
+void EmbeddedWorkerRegistry::OnWorkerStarted(
+ int process_id, int thread_id, int embedded_worker_id) {
+ DCHECK(!ContainsKey(worker_process_map_, process_id) ||
+ worker_process_map_[process_id].count(embedded_worker_id) == 0);
+ WorkerInstanceMap::iterator found = worker_map_.find(embedded_worker_id);
+ if (found == worker_map_.end()) {
+ LOG(ERROR) << "Worker " << embedded_worker_id << " not registered";
+ return;
+ }
+ if (found->second->process_id() != process_id) {
+ LOG(ERROR) << "Incorrect embedded_worker_id";
+ return;
+ }
+ worker_process_map_[process_id].insert(embedded_worker_id);
+ found->second->OnStarted(thread_id);
}
-bool EmbeddedWorkerRegistry::StartWorker(
- int process_id,
+void EmbeddedWorkerRegistry::OnWorkerStopped(
+ int process_id, int embedded_worker_id) {
+ WorkerInstanceMap::iterator found = worker_map_.find(embedded_worker_id);
+ if (found == worker_map_.end()) {
+ LOG(ERROR) << "Worker " << embedded_worker_id << " not registered";
+ return;
+ }
+ if (found->second->process_id() != process_id) {
+ LOG(ERROR) << "Incorrect embedded_worker_id";
+ return;
+ }
+ worker_process_map_[process_id].erase(embedded_worker_id);
+ found->second->OnStopped();
+}
+
+void EmbeddedWorkerRegistry::OnReportException(
int embedded_worker_id,
- int64 service_worker_version_id,
- const GURL& script_url) {
- return Send(process_id,
- new ServiceWorkerMsg_StartWorker(embedded_worker_id,
- service_worker_version_id,
- script_url));
+ const base::string16& error_message,
+ int line_number,
+ int column_number,
+ const GURL& source_url) {
+ WorkerInstanceMap::iterator found = worker_map_.find(embedded_worker_id);
+ if (found == worker_map_.end()) {
+ LOG(ERROR) << "Worker " << embedded_worker_id << " not registered";
+ return;
+ }
+ found->second->OnReportException(
+ error_message, line_number, column_number, source_url);
}
-bool EmbeddedWorkerRegistry::StopWorker(int process_id,
- int embedded_worker_id) {
- return Send(process_id,
- new ServiceWorkerMsg_TerminateWorker(embedded_worker_id));
+void EmbeddedWorkerRegistry::OnReportConsoleMessage(
+ int embedded_worker_id,
+ int source_identifier,
+ int message_level,
+ const base::string16& message,
+ int line_number,
+ const GURL& source_url) {
+ WorkerInstanceMap::iterator found = worker_map_.find(embedded_worker_id);
+ if (found == worker_map_.end()) {
+ LOG(ERROR) << "Worker " << embedded_worker_id << " not registered";
+ return;
+ }
+ found->second->OnReportConsoleMessage(
+ source_identifier, message_level, message, line_number, source_url);
}
void EmbeddedWorkerRegistry::AddChildProcessSender(
int process_id, IPC::Sender* sender) {
process_sender_map_[process_id] = sender;
+ DCHECK(!ContainsKey(worker_process_map_, process_id));
}
void EmbeddedWorkerRegistry::RemoveChildProcessSender(int process_id) {
process_sender_map_.erase(process_id);
+ std::map<int, std::set<int> >::iterator found =
+ worker_process_map_.find(process_id);
+ if (found != worker_process_map_.end()) {
+ const std::set<int>& worker_set = worker_process_map_[process_id];
+ for (std::set<int>::const_iterator it = worker_set.begin();
+ it != worker_set.end();
+ ++it) {
+ int embedded_worker_id = *it;
+ DCHECK(ContainsKey(worker_map_, embedded_worker_id));
+ worker_map_[embedded_worker_id]->OnStopped();
+ }
+ worker_process_map_.erase(found);
+ }
+}
+
+EmbeddedWorkerInstance* EmbeddedWorkerRegistry::GetWorker(
+ int embedded_worker_id) {
+ WorkerInstanceMap::iterator found = worker_map_.find(embedded_worker_id);
+ if (found == worker_map_.end())
+ return NULL;
+ return found->second;
+}
+
+EmbeddedWorkerRegistry::~EmbeddedWorkerRegistry() {
+ Shutdown();
}
-EmbeddedWorkerRegistry::~EmbeddedWorkerRegistry() {}
+void EmbeddedWorkerRegistry::SendStartWorker(
+ scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
+ const StatusCallback& callback,
+ int process_id) {
+ // The ServiceWorkerDispatcherHost is supposed to be created when the process
+ // is created, and keep an entry in process_sender_map_ for its whole
+ // lifetime.
+ DCHECK(ContainsKey(process_sender_map_, process_id));
+ callback.Run(Send(process_id, new EmbeddedWorkerMsg_StartWorker(*params)));
+}
-bool EmbeddedWorkerRegistry::Send(int process_id, IPC::Message* message) {
+ServiceWorkerStatusCode EmbeddedWorkerRegistry::Send(
+ int process_id, IPC::Message* message_ptr) {
+ scoped_ptr<IPC::Message> message(message_ptr);
if (!context_)
- return false;
+ return SERVICE_WORKER_ERROR_ABORT;
ProcessToSenderMap::iterator found = process_sender_map_.find(process_id);
if (found == process_sender_map_.end())
- return false;
- return found->second->Send(message);
+ return SERVICE_WORKER_ERROR_PROCESS_NOT_FOUND;
+ if (!found->second->Send(message.release()))
+ return SERVICE_WORKER_ERROR_IPC_FAILED;
+ return SERVICE_WORKER_OK;
+}
+
+void EmbeddedWorkerRegistry::RemoveWorker(int process_id,
+ int embedded_worker_id) {
+ DCHECK(ContainsKey(worker_map_, embedded_worker_id));
+ worker_map_.erase(embedded_worker_id);
+ worker_process_map_.erase(process_id);
}
} // namespace content
diff --git a/chromium/content/browser/service_worker/embedded_worker_registry.h b/chromium/content/browser/service_worker/embedded_worker_registry.h
index 8860e374b5c..3df2e238071 100644
--- a/chromium/content/browser/service_worker/embedded_worker_registry.h
+++ b/chromium/content/browser/service_worker/embedded_worker_registry.h
@@ -6,13 +6,18 @@
#define CONTENT_BROWSER_SERVICE_WORKER_EMBEDDED_WORKER_REGISTRY_H_
#include <map>
+#include <set>
+#include <vector>
#include "base/basictypes.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
+#include "base/strings/string16.h"
#include "content/common/content_export.h"
+#include "content/common/service_worker/service_worker_status_code.h"
+struct EmbeddedWorkerMsg_StartWorker_Params;
class GURL;
namespace IPC {
@@ -33,40 +38,76 @@ class ServiceWorkerContextCore;
class CONTENT_EXPORT EmbeddedWorkerRegistry
: public NON_EXPORTED_BASE(base::RefCounted<EmbeddedWorkerRegistry>) {
public:
+ typedef base::Callback<void(ServiceWorkerStatusCode)> StatusCallback;
+
explicit EmbeddedWorkerRegistry(
base::WeakPtr<ServiceWorkerContextCore> context);
+ bool OnMessageReceived(const IPC::Message& message);
+
// Creates and removes a new worker instance entry for bookkeeping.
// This doesn't actually start or stop the worker.
scoped_ptr<EmbeddedWorkerInstance> CreateWorker();
- void RemoveWorker(int embedded_worker_id);
// Called from EmbeddedWorkerInstance, relayed to the child process.
- bool StartWorker(int process_id,
- int embedded_worker_id,
- int64 service_worker_version_id,
- const GURL& script_url);
- bool StopWorker(int process_id,
- int embedded_worker_id);
+ void SendStartWorker(scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
+ const StatusCallback& callback,
+ int process_id);
+ ServiceWorkerStatusCode StopWorker(int process_id,
+ int embedded_worker_id);
+
+ // Stop all active workers, even if they're handling events.
+ void Shutdown();
+
+ // Called back from EmbeddedWorker in the child process, relayed via
+ // ServiceWorkerDispatcherHost.
+ void OnWorkerScriptLoaded(int process_id, int embedded_worker_id);
+ void OnWorkerScriptLoadFailed(int process_id, int embedded_worker_id);
+ void OnWorkerStarted(int process_id, int thread_id, int embedded_worker_id);
+ void OnWorkerStopped(int process_id, int embedded_worker_id);
+ void OnReportException(int embedded_worker_id,
+ const base::string16& error_message,
+ int line_number,
+ int column_number,
+ const GURL& source_url);
+ void OnReportConsoleMessage(int embedded_worker_id,
+ int source_identifier,
+ int message_level,
+ const base::string16& message,
+ int line_number,
+ const GURL& source_url);
// Keeps a map from process_id to sender information.
void AddChildProcessSender(int process_id, IPC::Sender* sender);
void RemoveChildProcessSender(int process_id);
+ // Returns an embedded worker instance for given |embedded_worker_id|.
+ EmbeddedWorkerInstance* GetWorker(int embedded_worker_id);
+
private:
friend class base::RefCounted<EmbeddedWorkerRegistry>;
-
- ~EmbeddedWorkerRegistry();
- bool Send(int process_id, IPC::Message* message);
+ friend class EmbeddedWorkerInstance;
typedef std::map<int, EmbeddedWorkerInstance*> WorkerInstanceMap;
typedef std::map<int, IPC::Sender*> ProcessToSenderMap;
+ ~EmbeddedWorkerRegistry();
+
+ ServiceWorkerStatusCode Send(int process_id, IPC::Message* message);
+
+ // RemoveWorker is called when EmbeddedWorkerInstance is destructed.
+ // |process_id| could be invalid (i.e. -1) if it's not running.
+ void RemoveWorker(int process_id, int embedded_worker_id);
+
base::WeakPtr<ServiceWorkerContextCore> context_;
WorkerInstanceMap worker_map_;
ProcessToSenderMap process_sender_map_;
+ // Map from process_id to embedded_worker_id.
+ // This map only contains running workers.
+ std::map<int, std::set<int> > worker_process_map_;
+
int next_embedded_worker_id_;
DISALLOW_COPY_AND_ASSIGN(EmbeddedWorkerRegistry);
diff --git a/chromium/content/browser/service_worker/embedded_worker_test_helper.cc b/chromium/content/browser/service_worker/embedded_worker_test_helper.cc
new file mode 100644
index 00000000000..82d206142a1
--- /dev/null
+++ b/chromium/content/browser/service_worker/embedded_worker_test_helper.cc
@@ -0,0 +1,241 @@
+// 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/service_worker/embedded_worker_test_helper.h"
+
+#include "base/bind.h"
+#include "content/browser/service_worker/embedded_worker_instance.h"
+#include "content/browser/service_worker/embedded_worker_registry.h"
+#include "content/browser/service_worker/service_worker_context_core.h"
+#include "content/browser/service_worker/service_worker_context_wrapper.h"
+#include "content/common/service_worker/embedded_worker_messages.h"
+#include "content/common/service_worker/service_worker_messages.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+EmbeddedWorkerTestHelper::EmbeddedWorkerTestHelper(int mock_render_process_id)
+ : wrapper_(new ServiceWorkerContextWrapper(NULL)),
+ next_thread_id_(0),
+ weak_factory_(this) {
+ wrapper_->InitInternal(base::FilePath(),
+ base::MessageLoopProxy::current(),
+ base::MessageLoopProxy::current(),
+ NULL);
+ wrapper_->process_manager()->SetProcessIdForTest(mock_render_process_id);
+ registry()->AddChildProcessSender(mock_render_process_id, this);
+}
+
+EmbeddedWorkerTestHelper::~EmbeddedWorkerTestHelper() {
+ if (wrapper_)
+ wrapper_->Shutdown();
+}
+
+void EmbeddedWorkerTestHelper::SimulateAddProcessToWorker(
+ int embedded_worker_id,
+ int process_id) {
+ EmbeddedWorkerInstance* worker = registry()->GetWorker(embedded_worker_id);
+ ASSERT_TRUE(worker);
+ registry()->AddChildProcessSender(process_id, this);
+ worker->AddProcessReference(process_id);
+}
+
+bool EmbeddedWorkerTestHelper::Send(IPC::Message* message) {
+ OnMessageReceived(*message);
+ delete message;
+ return true;
+}
+
+bool EmbeddedWorkerTestHelper::OnMessageReceived(const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(EmbeddedWorkerTestHelper, message)
+ IPC_MESSAGE_HANDLER(EmbeddedWorkerMsg_StartWorker, OnStartWorkerStub)
+ IPC_MESSAGE_HANDLER(EmbeddedWorkerMsg_StopWorker, OnStopWorkerStub)
+ IPC_MESSAGE_HANDLER(EmbeddedWorkerContextMsg_MessageToWorker,
+ OnMessageToWorkerStub)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+
+ // IPC::TestSink only records messages that are not handled by filters,
+ // so we just forward all messages to the separate sink.
+ sink_.OnMessageReceived(message);
+
+ return handled;
+}
+
+ServiceWorkerContextCore* EmbeddedWorkerTestHelper::context() {
+ return wrapper_->context();
+}
+
+void EmbeddedWorkerTestHelper::ShutdownContext() {
+ wrapper_->Shutdown();
+ wrapper_ = NULL;
+}
+
+void EmbeddedWorkerTestHelper::OnStartWorker(
+ int embedded_worker_id,
+ int64 service_worker_version_id,
+ const GURL& scope,
+ const GURL& script_url) {
+ // By default just notify the sender that the worker is started.
+ SimulateWorkerStarted(next_thread_id_++, embedded_worker_id);
+}
+
+void EmbeddedWorkerTestHelper::OnStopWorker(int embedded_worker_id) {
+ // By default just notify the sender that the worker is stopped.
+ SimulateWorkerStopped(embedded_worker_id);
+}
+
+bool EmbeddedWorkerTestHelper::OnMessageToWorker(
+ int thread_id,
+ int embedded_worker_id,
+ const IPC::Message& message) {
+ bool handled = true;
+ current_embedded_worker_id_ = embedded_worker_id;
+ IPC_BEGIN_MESSAGE_MAP(EmbeddedWorkerTestHelper, message)
+ IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ActivateEvent, OnActivateEventStub)
+ IPC_MESSAGE_HANDLER(ServiceWorkerMsg_InstallEvent, OnInstallEventStub)
+ IPC_MESSAGE_HANDLER(ServiceWorkerMsg_FetchEvent, OnFetchEventStub)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ // Record all messages directed to inner script context.
+ inner_sink_.OnMessageReceived(message);
+ return handled;
+}
+
+void EmbeddedWorkerTestHelper::OnActivateEvent(int embedded_worker_id,
+ int request_id) {
+ SimulateSend(
+ new ServiceWorkerHostMsg_ActivateEventFinished(
+ embedded_worker_id, request_id,
+ blink::WebServiceWorkerEventResultCompleted));
+}
+
+void EmbeddedWorkerTestHelper::OnInstallEvent(int embedded_worker_id,
+ int request_id,
+ int active_version_id) {
+ SimulateSend(
+ new ServiceWorkerHostMsg_InstallEventFinished(
+ embedded_worker_id, request_id,
+ blink::WebServiceWorkerEventResultCompleted));
+}
+
+void EmbeddedWorkerTestHelper::OnFetchEvent(
+ int embedded_worker_id,
+ int request_id,
+ const ServiceWorkerFetchRequest& request) {
+ SimulateSend(
+ new ServiceWorkerHostMsg_FetchEventFinished(
+ embedded_worker_id,
+ request_id,
+ SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE,
+ ServiceWorkerResponse(200, "OK",
+ std::map<std::string, std::string>(),
+ std::string())));
+}
+
+void EmbeddedWorkerTestHelper::SimulateWorkerStarted(
+ int thread_id, int embedded_worker_id) {
+ EmbeddedWorkerInstance* worker = registry()->GetWorker(embedded_worker_id);
+ ASSERT_TRUE(worker != NULL);
+ registry()->OnWorkerStarted(
+ worker->process_id(),
+ thread_id,
+ embedded_worker_id);
+}
+
+void EmbeddedWorkerTestHelper::SimulateWorkerStopped(
+ int embedded_worker_id) {
+ EmbeddedWorkerInstance* worker = registry()->GetWorker(embedded_worker_id);
+ if (worker != NULL)
+ registry()->OnWorkerStopped(worker->process_id(), embedded_worker_id);
+}
+
+void EmbeddedWorkerTestHelper::SimulateSend(
+ IPC::Message* message) {
+ registry()->OnMessageReceived(*message);
+ delete message;
+}
+
+void EmbeddedWorkerTestHelper::OnStartWorkerStub(
+ const EmbeddedWorkerMsg_StartWorker_Params& params) {
+ EmbeddedWorkerInstance* worker =
+ registry()->GetWorker(params.embedded_worker_id);
+ ASSERT_TRUE(worker != NULL);
+ EXPECT_EQ(EmbeddedWorkerInstance::STARTING, worker->status());
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&EmbeddedWorkerTestHelper::OnStartWorker,
+ weak_factory_.GetWeakPtr(),
+ params.embedded_worker_id,
+ params.service_worker_version_id,
+ params.scope,
+ params.script_url));
+}
+
+void EmbeddedWorkerTestHelper::OnStopWorkerStub(int embedded_worker_id) {
+ EmbeddedWorkerInstance* worker = registry()->GetWorker(embedded_worker_id);
+ ASSERT_TRUE(worker != NULL);
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&EmbeddedWorkerTestHelper::OnStopWorker,
+ weak_factory_.GetWeakPtr(),
+ embedded_worker_id));
+}
+
+void EmbeddedWorkerTestHelper::OnMessageToWorkerStub(
+ int thread_id,
+ int embedded_worker_id,
+ const IPC::Message& message) {
+ EmbeddedWorkerInstance* worker = registry()->GetWorker(embedded_worker_id);
+ ASSERT_TRUE(worker != NULL);
+ EXPECT_EQ(worker->thread_id(), thread_id);
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(
+ base::IgnoreResult(&EmbeddedWorkerTestHelper::OnMessageToWorker),
+ weak_factory_.GetWeakPtr(),
+ thread_id,
+ embedded_worker_id,
+ message));
+}
+
+void EmbeddedWorkerTestHelper::OnActivateEventStub(int request_id) {
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&EmbeddedWorkerTestHelper::OnActivateEvent,
+ weak_factory_.GetWeakPtr(),
+ current_embedded_worker_id_,
+ request_id));
+}
+
+void EmbeddedWorkerTestHelper::OnInstallEventStub(int request_id,
+ int active_version_id) {
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&EmbeddedWorkerTestHelper::OnInstallEvent,
+ weak_factory_.GetWeakPtr(),
+ current_embedded_worker_id_,
+ request_id,
+ active_version_id));
+}
+
+void EmbeddedWorkerTestHelper::OnFetchEventStub(
+ int request_id,
+ const ServiceWorkerFetchRequest& request) {
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&EmbeddedWorkerTestHelper::OnFetchEvent,
+ weak_factory_.GetWeakPtr(),
+ current_embedded_worker_id_,
+ request_id,
+ request));
+}
+
+EmbeddedWorkerRegistry* EmbeddedWorkerTestHelper::registry() {
+ DCHECK(context());
+ return context()->embedded_worker_registry();
+}
+
+} // namespace content
diff --git a/chromium/content/browser/service_worker/embedded_worker_test_helper.h b/chromium/content/browser/service_worker/embedded_worker_test_helper.h
new file mode 100644
index 00000000000..7cd817df6b9
--- /dev/null
+++ b/chromium/content/browser/service_worker/embedded_worker_test_helper.h
@@ -0,0 +1,135 @@
+// 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_SERVICE_WORKER_EMBEDDED_WORKER_TEST_HELPER_H_
+#define CONTENT_BROWSER_SERVICE_WORKER_EMBEDDED_WORKER_TEST_HELPER_H_
+
+#include <vector>
+
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "ipc/ipc_listener.h"
+#include "ipc/ipc_test_sink.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+struct EmbeddedWorkerMsg_StartWorker_Params;
+class GURL;
+
+namespace content {
+
+class EmbeddedWorkerRegistry;
+class EmbeddedWorkerTestHelper;
+class ServiceWorkerContextCore;
+class ServiceWorkerContextWrapper;
+struct ServiceWorkerFetchRequest;
+
+// In-Process EmbeddedWorker test helper.
+//
+// Usage: create an instance of this class to test browser-side embedded worker
+// code without creating a child process. This class will create a
+// ServiceWorkerContextWrapper and ServiceWorkerContextCore for you.
+//
+// By default this class just notifies back WorkerStarted and WorkerStopped
+// for StartWorker and StopWorker requests. The default implementation
+// also returns success for event messages (e.g. InstallEvent, FetchEvent).
+//
+// Alternatively consumers can subclass this helper and override On*()
+// methods to add their own logic/verification code.
+//
+// See embedded_worker_instance_unittest.cc for example usages.
+//
+class EmbeddedWorkerTestHelper : public IPC::Sender,
+ public IPC::Listener {
+ public:
+ // Initialize this helper for |context|, and enable this as an IPC
+ // sender for |mock_render_process_id|.
+ EmbeddedWorkerTestHelper(int mock_render_process_id);
+ virtual ~EmbeddedWorkerTestHelper();
+
+ // Call this to simulate add/associate a process to a worker.
+ // This also registers this sender for the process.
+ void SimulateAddProcessToWorker(int embedded_worker_id, int process_id);
+
+ // IPC::Sender implementation.
+ virtual bool Send(IPC::Message* message) OVERRIDE;
+
+ // IPC::Listener implementation.
+ virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE;
+
+ // IPC sink for EmbeddedWorker messages.
+ IPC::TestSink* ipc_sink() { return &sink_; }
+ // Inner IPC sink for script context messages sent via EmbeddedWorker.
+ IPC::TestSink* inner_ipc_sink() { return &inner_sink_; }
+
+ ServiceWorkerContextCore* context();
+ ServiceWorkerContextWrapper* context_wrapper() { return wrapper_.get(); }
+ void ShutdownContext();
+
+ protected:
+ // Called when StartWorker, StopWorker and SendMessageToWorker message
+ // is sent to the embedded worker. Override if necessary. By default
+ // they verify given parameters and:
+ // - OnStartWorker calls SimulateWorkerStarted
+ // - OnStopWorker calls SimulateWorkerStoped
+ // - OnSendMessageToWorker calls the message's respective On*Event handler
+ virtual void OnStartWorker(int embedded_worker_id,
+ int64 service_worker_version_id,
+ const GURL& scope,
+ const GURL& script_url);
+ virtual void OnStopWorker(int embedded_worker_id);
+ virtual bool OnMessageToWorker(int thread_id,
+ int embedded_worker_id,
+ const IPC::Message& message);
+
+ // On*Event handlers. Called by the default implementation of
+ // OnMessageToWorker when events are sent to the embedded
+ // worker. By default they just return success via
+ // SimulateSendReplyToBrowser.
+ virtual void OnActivateEvent(int embedded_worker_id, int request_id);
+ virtual void OnInstallEvent(int embedded_worker_id,
+ int request_id,
+ int active_version_id);
+ virtual void OnFetchEvent(int embedded_worker_id,
+ int request_id,
+ const ServiceWorkerFetchRequest& request);
+
+ // These functions simulate sending an EmbeddedHostMsg message to the
+ // browser.
+ void SimulateWorkerStarted(int thread_id, int embedded_worker_id);
+ void SimulateWorkerStopped(int embedded_worker_id);
+ void SimulateSend(IPC::Message* message);
+
+ protected:
+ EmbeddedWorkerRegistry* registry();
+
+ private:
+ void OnStartWorkerStub(const EmbeddedWorkerMsg_StartWorker_Params& params);
+ void OnStopWorkerStub(int embedded_worker_id);
+ void OnMessageToWorkerStub(int thread_id,
+ int embedded_worker_id,
+ const IPC::Message& message);
+ void OnActivateEventStub(int request_id);
+ void OnInstallEventStub(int request_id, int active_version_id);
+ void OnFetchEventStub(int request_id,
+ const ServiceWorkerFetchRequest& request);
+
+ scoped_refptr<ServiceWorkerContextWrapper> wrapper_;
+
+ IPC::TestSink sink_;
+ IPC::TestSink inner_sink_;
+
+ int next_thread_id_;
+
+ // Updated each time MessageToWorker message is received.
+ int current_embedded_worker_id_;
+
+ base::WeakPtrFactory<EmbeddedWorkerTestHelper> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(EmbeddedWorkerTestHelper);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_SERVICE_WORKER_EMBEDDED_WORKER_TEST_HELPER_H_
diff --git a/chromium/content/browser/service_worker/service_worker_browsertest.cc b/chromium/content/browser/service_worker/service_worker_browsertest.cc
new file mode 100644
index 00000000000..8e5b8871f9e
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_browsertest.cc
@@ -0,0 +1,710 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/command_line.h"
+#include "base/run_loop.h"
+#include "content/browser/fileapi/chrome_blob_storage_context.h"
+#include "content/browser/service_worker/embedded_worker_instance.h"
+#include "content/browser/service_worker/embedded_worker_registry.h"
+#include "content/browser/service_worker/service_worker_context_core.h"
+#include "content/browser/service_worker/service_worker_context_wrapper.h"
+#include "content/browser/service_worker/service_worker_registration.h"
+#include "content/browser/service_worker/service_worker_test_utils.h"
+#include "content/browser/service_worker/service_worker_version.h"
+#include "content/common/service_worker/service_worker_messages.h"
+#include "content/common/service_worker/service_worker_status_code.h"
+#include "content/common/service_worker/service_worker_types.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#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/content_switches.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 "net/test/embedded_test_server/embedded_test_server.h"
+#include "webkit/browser/blob/blob_data_handle.h"
+#include "webkit/browser/blob/blob_storage_context.h"
+#include "webkit/common/blob/blob_data.h"
+
+namespace content {
+
+namespace {
+
+struct FetchResult {
+ ServiceWorkerStatusCode status;
+ ServiceWorkerFetchEventResult result;
+ ServiceWorkerResponse response;
+ scoped_ptr<webkit_blob::BlobDataHandle> blob_data_handle;
+};
+
+void RunAndQuit(const base::Closure& closure,
+ const base::Closure& quit,
+ base::MessageLoopProxy* original_message_loop) {
+ closure.Run();
+ original_message_loop->PostTask(FROM_HERE, quit);
+}
+
+void RunOnIOThread(const base::Closure& closure) {
+ base::RunLoop run_loop;
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&RunAndQuit, closure, run_loop.QuitClosure(),
+ base::MessageLoopProxy::current()));
+ run_loop.Run();
+}
+
+void RunOnIOThread(
+ const base::Callback<void(const base::Closure& continuation)>& closure) {
+ base::RunLoop run_loop;
+ base::Closure quit_on_original_thread =
+ base::Bind(base::IgnoreResult(&base::MessageLoopProxy::PostTask),
+ base::MessageLoopProxy::current().get(),
+ FROM_HERE,
+ run_loop.QuitClosure());
+ BrowserThread::PostTask(BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(closure, quit_on_original_thread));
+ run_loop.Run();
+}
+
+// Contrary to the style guide, the output parameter of this function comes
+// before input parameters so Bind can be used on it to create a FetchCallback
+// to pass to DispatchFetchEvent.
+void ReceiveFetchResult(BrowserThread::ID run_quit_thread,
+ const base::Closure& quit,
+ ChromeBlobStorageContext* blob_context,
+ FetchResult* out_result,
+ ServiceWorkerStatusCode actual_status,
+ ServiceWorkerFetchEventResult actual_result,
+ const ServiceWorkerResponse& actual_response) {
+ out_result->status = actual_status;
+ out_result->result = actual_result;
+ out_result->response = actual_response;
+ if (!actual_response.blob_uuid.empty()) {
+ out_result->blob_data_handle =
+ blob_context->context()->GetBlobDataFromUUID(
+ actual_response.blob_uuid);
+ }
+ if (!quit.is_null())
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, quit);
+}
+
+ServiceWorkerVersion::FetchCallback CreateResponseReceiver(
+ BrowserThread::ID run_quit_thread,
+ const base::Closure& quit,
+ ChromeBlobStorageContext* blob_context,
+ FetchResult* result) {
+ return base::Bind(&ReceiveFetchResult, run_quit_thread, quit,
+ make_scoped_refptr<ChromeBlobStorageContext>(blob_context),
+ result);
+}
+
+void ReadResponseBody(std::string* body,
+ webkit_blob::BlobDataHandle* blob_data_handle) {
+ ASSERT_TRUE(blob_data_handle);
+ ASSERT_EQ(1U, blob_data_handle->data()->items().size());
+ *body = std::string(blob_data_handle->data()->items()[0].bytes(),
+ blob_data_handle->data()->items()[0].length());
+}
+
+} // namespace
+
+class ServiceWorkerBrowserTest : public ContentBrowserTest {
+ protected:
+ typedef ServiceWorkerBrowserTest self;
+
+ virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
+ command_line->AppendSwitch(switches::kEnableServiceWorker);
+ }
+
+ virtual void SetUpOnMainThread() OVERRIDE {
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+ StoragePartition* partition = BrowserContext::GetDefaultStoragePartition(
+ shell()->web_contents()->GetBrowserContext());
+ wrapper_ = static_cast<ServiceWorkerContextWrapper*>(
+ partition->GetServiceWorkerContext());
+
+ // Navigate to the page to set up a renderer page (where we can embed
+ // a worker).
+ NavigateToURLBlockUntilNavigationsComplete(
+ shell(),
+ embedded_test_server()->GetURL("/service_worker/empty.html"), 1);
+
+ RunOnIOThread(base::Bind(&self::SetUpOnIOThread, this));
+ }
+
+ virtual void TearDownOnMainThread() OVERRIDE {
+ RunOnIOThread(base::Bind(&self::TearDownOnIOThread, this));
+ wrapper_ = NULL;
+ }
+
+ virtual void SetUpOnIOThread() {}
+ virtual void TearDownOnIOThread() {}
+
+ ServiceWorkerContextWrapper* wrapper() { return wrapper_.get(); }
+ ServiceWorkerContext* public_context() { return wrapper(); }
+
+ void AssociateRendererProcessToWorker(EmbeddedWorkerInstance* worker) {
+ worker->AddProcessReference(
+ shell()->web_contents()->GetRenderProcessHost()->GetID());
+ }
+
+ private:
+ scoped_refptr<ServiceWorkerContextWrapper> wrapper_;
+};
+
+class EmbeddedWorkerBrowserTest : public ServiceWorkerBrowserTest,
+ public EmbeddedWorkerInstance::Listener {
+ public:
+ typedef EmbeddedWorkerBrowserTest self;
+
+ EmbeddedWorkerBrowserTest()
+ : last_worker_status_(EmbeddedWorkerInstance::STOPPED) {}
+ virtual ~EmbeddedWorkerBrowserTest() {}
+
+ virtual void TearDownOnIOThread() OVERRIDE {
+ if (worker_) {
+ worker_->RemoveListener(this);
+ worker_.reset();
+ }
+ }
+
+ void StartOnIOThread() {
+ ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ worker_ = wrapper()->context()->embedded_worker_registry()->CreateWorker();
+ EXPECT_EQ(EmbeddedWorkerInstance::STOPPED, worker_->status());
+ worker_->AddListener(this);
+
+ AssociateRendererProcessToWorker(worker_.get());
+
+ const int64 service_worker_version_id = 33L;
+ const GURL scope = embedded_test_server()->GetURL("/*");
+ const GURL script_url = embedded_test_server()->GetURL(
+ "/service_worker/worker.js");
+ std::vector<int> processes;
+ processes.push_back(
+ shell()->web_contents()->GetRenderProcessHost()->GetID());
+ worker_->Start(
+ service_worker_version_id,
+ scope,
+ script_url,
+ processes,
+ base::Bind(&EmbeddedWorkerBrowserTest::StartOnIOThread2, this));
+ }
+ void StartOnIOThread2(ServiceWorkerStatusCode status) {
+ last_worker_status_ = worker_->status();
+ EXPECT_EQ(SERVICE_WORKER_OK, status);
+ EXPECT_EQ(EmbeddedWorkerInstance::STARTING, last_worker_status_);
+
+ if (status != SERVICE_WORKER_OK && !done_closure_.is_null())
+ done_closure_.Run();
+ }
+
+ void StopOnIOThread() {
+ ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ EXPECT_EQ(EmbeddedWorkerInstance::RUNNING, worker_->status());
+
+ ServiceWorkerStatusCode status = worker_->Stop();
+
+ last_worker_status_ = worker_->status();
+ EXPECT_EQ(SERVICE_WORKER_OK, status);
+ EXPECT_EQ(EmbeddedWorkerInstance::STOPPING, last_worker_status_);
+
+ if (status != SERVICE_WORKER_OK && !done_closure_.is_null())
+ done_closure_.Run();
+ }
+
+ protected:
+ // EmbeddedWorkerInstance::Observer overrides:
+ virtual void OnStarted() OVERRIDE {
+ ASSERT_TRUE(worker_ != NULL);
+ ASSERT_FALSE(done_closure_.is_null());
+ last_worker_status_ = worker_->status();
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, done_closure_);
+ }
+ virtual void OnStopped() OVERRIDE {
+ ASSERT_TRUE(worker_ != NULL);
+ ASSERT_FALSE(done_closure_.is_null());
+ last_worker_status_ = worker_->status();
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, done_closure_);
+ }
+ virtual void OnReportException(const base::string16& error_message,
+ int line_number,
+ int column_number,
+ const GURL& source_url) OVERRIDE {}
+ virtual void OnReportConsoleMessage(int source_identifier,
+ int message_level,
+ const base::string16& message,
+ int line_number,
+ const GURL& source_url) OVERRIDE {}
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
+ return false;
+ }
+
+ scoped_ptr<EmbeddedWorkerInstance> worker_;
+ EmbeddedWorkerInstance::Status last_worker_status_;
+
+ // Called by EmbeddedWorkerInstance::Observer overrides so that
+ // test code can wait for the worker status notifications.
+ base::Closure done_closure_;
+};
+
+class ServiceWorkerVersionBrowserTest : public ServiceWorkerBrowserTest {
+ public:
+ typedef ServiceWorkerVersionBrowserTest self;
+
+ virtual ~ServiceWorkerVersionBrowserTest() {}
+
+ virtual void TearDownOnIOThread() OVERRIDE {
+ registration_ = NULL;
+ version_ = NULL;
+ }
+
+ void InstallTestHelper(const std::string& worker_url,
+ ServiceWorkerStatusCode expected_status) {
+ RunOnIOThread(base::Bind(&self::SetUpRegistrationOnIOThread, this,
+ worker_url));
+
+ // Dispatch install on a worker.
+ ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
+ base::RunLoop install_run_loop;
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+ base::Bind(&self::InstallOnIOThread, this,
+ install_run_loop.QuitClosure(),
+ &status));
+ install_run_loop.Run();
+ ASSERT_EQ(expected_status, status);
+
+ // Stop the worker.
+ status = SERVICE_WORKER_ERROR_FAILED;
+ base::RunLoop stop_run_loop;
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+ base::Bind(&self::StopOnIOThread, this,
+ stop_run_loop.QuitClosure(),
+ &status));
+ stop_run_loop.Run();
+ ASSERT_EQ(SERVICE_WORKER_OK, status);
+ }
+
+ void ActivateTestHelper(
+ const std::string& worker_url,
+ ServiceWorkerStatusCode expected_status) {
+ RunOnIOThread(
+ base::Bind(&self::SetUpRegistrationOnIOThread, this, worker_url));
+ version_->SetStatus(ServiceWorkerVersion::INSTALLED);
+ ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
+ base::RunLoop run_loop;
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(
+ &self::ActivateOnIOThread, this, run_loop.QuitClosure(), &status));
+ run_loop.Run();
+ ASSERT_EQ(expected_status, status);
+ }
+
+ void FetchOnRegisteredWorker(
+ ServiceWorkerFetchEventResult* result,
+ ServiceWorkerResponse* response,
+ scoped_ptr<webkit_blob::BlobDataHandle>* blob_data_handle) {
+ blob_context_ = ChromeBlobStorageContext::GetFor(
+ shell()->web_contents()->GetBrowserContext());
+ FetchResult fetch_result;
+ fetch_result.status = SERVICE_WORKER_ERROR_FAILED;
+ base::RunLoop fetch_run_loop;
+ BrowserThread::PostTask(BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&self::FetchOnIOThread,
+ this,
+ fetch_run_loop.QuitClosure(),
+ &fetch_result));
+ fetch_run_loop.Run();
+ *result = fetch_result.result;
+ *response = fetch_result.response;
+ *blob_data_handle = fetch_result.blob_data_handle.Pass();
+ ASSERT_EQ(SERVICE_WORKER_OK, fetch_result.status);
+ }
+
+ void FetchTestHelper(
+ const std::string& worker_url,
+ ServiceWorkerFetchEventResult* result,
+ ServiceWorkerResponse* response,
+ scoped_ptr<webkit_blob::BlobDataHandle>* blob_data_handle) {
+ RunOnIOThread(
+ base::Bind(&self::SetUpRegistrationOnIOThread, this, worker_url));
+ FetchOnRegisteredWorker(result, response, blob_data_handle);
+ }
+
+ void SetUpRegistrationOnIOThread(const std::string& worker_url) {
+ registration_ = new ServiceWorkerRegistration(
+ embedded_test_server()->GetURL("/*"),
+ embedded_test_server()->GetURL(worker_url),
+ wrapper()->context()->storage()->NewRegistrationId(),
+ wrapper()->context()->AsWeakPtr());
+ version_ = new ServiceWorkerVersion(
+ registration_,
+ wrapper()->context()->storage()->NewVersionId(),
+ wrapper()->context()->AsWeakPtr());
+ AssociateRendererProcessToWorker(version_->embedded_worker());
+ }
+
+ void StartOnIOThread(const base::Closure& done,
+ ServiceWorkerStatusCode* result) {
+ ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ version_->StartWorker(CreateReceiver(BrowserThread::UI, done, result));
+ }
+
+ void InstallOnIOThread(const base::Closure& done,
+ ServiceWorkerStatusCode* result) {
+ ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ version_->DispatchInstallEvent(
+ -1, CreateReceiver(BrowserThread::UI, done, result));
+ }
+
+ void ActivateOnIOThread(const base::Closure& done,
+ ServiceWorkerStatusCode* result) {
+ ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ version_->SetStatus(ServiceWorkerVersion::INSTALLED);
+ version_->DispatchActivateEvent(
+ CreateReceiver(BrowserThread::UI, done, result));
+ }
+
+ void FetchOnIOThread(const base::Closure& done, FetchResult* result) {
+ ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ ServiceWorkerFetchRequest request(
+ embedded_test_server()->GetURL("/service_worker/empty.html"),
+ "GET",
+ std::map<std::string, std::string>());
+ version_->SetStatus(ServiceWorkerVersion::ACTIVE);
+ version_->DispatchFetchEvent(
+ request, CreateResponseReceiver(BrowserThread::UI, done,
+ blob_context_, result));
+ }
+
+ void StopOnIOThread(const base::Closure& done,
+ ServiceWorkerStatusCode* result) {
+ ASSERT_TRUE(version_);
+ version_->StopWorker(CreateReceiver(BrowserThread::UI, done, result));
+ }
+
+ void SyncEventOnIOThread(const base::Closure& done,
+ ServiceWorkerStatusCode* result) {
+ ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ version_->SetStatus(ServiceWorkerVersion::ACTIVE);
+ version_->DispatchSyncEvent(
+ CreateReceiver(BrowserThread::UI, done, result));
+ }
+
+ protected:
+ scoped_refptr<ServiceWorkerRegistration> registration_;
+ scoped_refptr<ServiceWorkerVersion> version_;
+ scoped_refptr<ChromeBlobStorageContext> blob_context_;
+};
+
+IN_PROC_BROWSER_TEST_F(EmbeddedWorkerBrowserTest, StartAndStop) {
+ // Start a worker and wait until OnStarted() is called.
+ base::RunLoop start_run_loop;
+ done_closure_ = start_run_loop.QuitClosure();
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+ base::Bind(&self::StartOnIOThread, this));
+ start_run_loop.Run();
+
+ ASSERT_EQ(EmbeddedWorkerInstance::RUNNING, last_worker_status_);
+
+ // Stop a worker and wait until OnStopped() is called.
+ base::RunLoop stop_run_loop;
+ done_closure_ = stop_run_loop.QuitClosure();
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+ base::Bind(&self::StopOnIOThread, this));
+ stop_run_loop.Run();
+
+ ASSERT_EQ(EmbeddedWorkerInstance::STOPPED, last_worker_status_);
+}
+
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, StartAndStop) {
+ RunOnIOThread(base::Bind(&self::SetUpRegistrationOnIOThread, this,
+ "/service_worker/worker.js"));
+
+ // Start a worker.
+ ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
+ base::RunLoop start_run_loop;
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+ base::Bind(&self::StartOnIOThread, this,
+ start_run_loop.QuitClosure(),
+ &status));
+ start_run_loop.Run();
+ ASSERT_EQ(SERVICE_WORKER_OK, status);
+
+ // Stop the worker.
+ status = SERVICE_WORKER_ERROR_FAILED;
+ base::RunLoop stop_run_loop;
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+ base::Bind(&self::StopOnIOThread, this,
+ stop_run_loop.QuitClosure(),
+ &status));
+ stop_run_loop.Run();
+ ASSERT_EQ(SERVICE_WORKER_OK, status);
+}
+
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, StartNotFound) {
+ RunOnIOThread(base::Bind(&self::SetUpRegistrationOnIOThread, this,
+ "/service_worker/nonexistent.js"));
+
+ // Start a worker for nonexistent URL.
+ ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
+ base::RunLoop start_run_loop;
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+ base::Bind(&self::StartOnIOThread, this,
+ start_run_loop.QuitClosure(),
+ &status));
+ start_run_loop.Run();
+ ASSERT_EQ(SERVICE_WORKER_ERROR_START_WORKER_FAILED, status);
+}
+
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, Install) {
+ InstallTestHelper("/service_worker/worker.js", SERVICE_WORKER_OK);
+}
+
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
+ InstallWithWaitUntil_Fulfilled) {
+ InstallTestHelper("/service_worker/worker_install_fulfilled.js",
+ SERVICE_WORKER_OK);
+}
+
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
+ Activate_NoEventListener) {
+ ActivateTestHelper("/service_worker/worker.js", SERVICE_WORKER_OK);
+ ASSERT_EQ(ServiceWorkerVersion::ACTIVE, version_->status());
+}
+
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, Activate_Rejected) {
+ ActivateTestHelper("/service_worker/worker_activate_rejected.js",
+ SERVICE_WORKER_ERROR_ACTIVATE_WORKER_FAILED);
+}
+
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
+ InstallWithWaitUntil_Rejected) {
+ InstallTestHelper("/service_worker/worker_install_rejected.js",
+ SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED);
+}
+
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, FetchEvent_Response) {
+ ServiceWorkerFetchEventResult result;
+ ServiceWorkerResponse response;
+ scoped_ptr<webkit_blob::BlobDataHandle> blob_data_handle;
+ FetchTestHelper("/service_worker/fetch_event.js",
+ &result, &response, &blob_data_handle);
+ ASSERT_EQ(SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE, result);
+ EXPECT_EQ(301, response.status_code);
+ EXPECT_EQ("Moved Permanently", response.status_text);
+ std::map<std::string, std::string> expected_headers;
+ expected_headers["Content-Language"] = "fi";
+ expected_headers["Content-Type"] = "text/html; charset=UTF-8";
+ EXPECT_EQ(expected_headers, response.headers);
+
+ std::string body;
+ RunOnIOThread(
+ base::Bind(&ReadResponseBody,
+ &body, base::Owned(blob_data_handle.release())));
+ EXPECT_EQ("This resource is gone. Gone, gone, gone.", body);
+}
+
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
+ SyncAbortedWithoutFlag) {
+ RunOnIOThread(base::Bind(
+ &self::SetUpRegistrationOnIOThread, this, "/service_worker/sync.js"));
+
+ // Run the sync event.
+ ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
+ base::RunLoop sync_run_loop;
+ BrowserThread::PostTask(BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&self::SyncEventOnIOThread,
+ this,
+ sync_run_loop.QuitClosure(),
+ &status));
+ sync_run_loop.Run();
+ ASSERT_EQ(SERVICE_WORKER_ERROR_ABORT, status);
+}
+
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, SyncEventHandled) {
+ CommandLine* command_line = CommandLine::ForCurrentProcess();
+ command_line->AppendSwitch(switches::kEnableServiceWorkerSync);
+
+ RunOnIOThread(base::Bind(
+ &self::SetUpRegistrationOnIOThread, this, "/service_worker/sync.js"));
+ ServiceWorkerFetchEventResult result;
+ ServiceWorkerResponse response;
+ scoped_ptr<webkit_blob::BlobDataHandle> blob_data_handle;
+ // Should 404 before sync event.
+ FetchOnRegisteredWorker(&result, &response, &blob_data_handle);
+ EXPECT_EQ(404, response.status_code);
+
+ // Run the sync event.
+ ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
+ base::RunLoop sync_run_loop;
+ BrowserThread::PostTask(BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&self::SyncEventOnIOThread,
+ this,
+ sync_run_loop.QuitClosure(),
+ &status));
+ sync_run_loop.Run();
+ ASSERT_EQ(SERVICE_WORKER_OK, status);
+
+ // Should 200 after sync event.
+ FetchOnRegisteredWorker(&result, &response, &blob_data_handle);
+ EXPECT_EQ(200, response.status_code);
+}
+
+class ServiceWorkerBlackBoxBrowserTest : public ServiceWorkerBrowserTest {
+ public:
+ typedef ServiceWorkerBlackBoxBrowserTest self;
+
+ static void ExpectResultAndRun(bool expected,
+ const base::Closure& continuation,
+ bool actual) {
+ EXPECT_EQ(expected, actual);
+ continuation.Run();
+ }
+
+ void FindRegistrationOnIO(const GURL& document_url,
+ ServiceWorkerStatusCode* status,
+ GURL* script_url,
+ const base::Closure& continuation) {
+ wrapper()->context()->storage()->FindRegistrationForDocument(
+ document_url,
+ base::Bind(&ServiceWorkerBlackBoxBrowserTest::FindRegistrationOnIO2,
+ this,
+ status,
+ script_url,
+ continuation));
+ }
+
+ void FindRegistrationOnIO2(
+ ServiceWorkerStatusCode* out_status,
+ GURL* script_url,
+ const base::Closure& continuation,
+ ServiceWorkerStatusCode status,
+ const scoped_refptr<ServiceWorkerRegistration>& registration) {
+ *out_status = status;
+ if (registration) {
+ *script_url = registration->script_url();
+ } else {
+ EXPECT_NE(SERVICE_WORKER_OK, status);
+ }
+ continuation.Run();
+ }
+};
+
+static int CountRenderProcessHosts() {
+ int result = 0;
+ for (RenderProcessHost::iterator iter(RenderProcessHost::AllHostsIterator());
+ !iter.IsAtEnd();
+ iter.Advance()) {
+ result++;
+ }
+ return result;
+}
+
+// Crashes on Android: http://crbug.com/387045
+#if defined(OS_ANDROID)
+#define MAYBE_Registration DISABLED_Registration
+#else
+#define MAYBE_Registration Registration
+#endif
+IN_PROC_BROWSER_TEST_F(ServiceWorkerBlackBoxBrowserTest, MAYBE_Registration) {
+ // Close the only window to be sure we're not re-using its RenderProcessHost.
+ shell()->Close();
+ EXPECT_EQ(0, CountRenderProcessHosts());
+
+ const std::string kWorkerUrl = "/service_worker/fetch_event.js";
+
+ // Unregistering nothing should return true.
+ {
+ base::RunLoop run_loop;
+ public_context()->UnregisterServiceWorker(
+ embedded_test_server()->GetURL("/*"),
+ base::Bind(&ServiceWorkerBlackBoxBrowserTest::ExpectResultAndRun,
+ true,
+ run_loop.QuitClosure()));
+ run_loop.Run();
+ }
+
+ // If we use a worker URL that doesn't exist, registration fails.
+ {
+ base::RunLoop run_loop;
+ public_context()->RegisterServiceWorker(
+ embedded_test_server()->GetURL("/*"),
+ embedded_test_server()->GetURL("/does/not/exist"),
+ base::Bind(&ServiceWorkerBlackBoxBrowserTest::ExpectResultAndRun,
+ false,
+ run_loop.QuitClosure()));
+ run_loop.Run();
+ }
+ EXPECT_EQ(0, CountRenderProcessHosts());
+
+ // Register returns when the promise would be resolved.
+ {
+ base::RunLoop run_loop;
+ public_context()->RegisterServiceWorker(
+ embedded_test_server()->GetURL("/*"),
+ embedded_test_server()->GetURL(kWorkerUrl),
+ base::Bind(&ServiceWorkerBlackBoxBrowserTest::ExpectResultAndRun,
+ true,
+ run_loop.QuitClosure()));
+ run_loop.Run();
+ }
+ EXPECT_EQ(1, CountRenderProcessHosts());
+
+ // Registering again should succeed, although the algo still
+ // might not be complete.
+ {
+ base::RunLoop run_loop;
+ public_context()->RegisterServiceWorker(
+ embedded_test_server()->GetURL("/*"),
+ embedded_test_server()->GetURL(kWorkerUrl),
+ base::Bind(&ServiceWorkerBlackBoxBrowserTest::ExpectResultAndRun,
+ true,
+ run_loop.QuitClosure()));
+ run_loop.Run();
+ }
+
+ // The registration algo might not be far enough along to have
+ // stored the registration data, so it may not be findable
+ // at this point.
+
+ // Unregistering something should return true.
+ {
+ base::RunLoop run_loop;
+ public_context()->UnregisterServiceWorker(
+ embedded_test_server()->GetURL("/*"),
+ base::Bind(&ServiceWorkerBlackBoxBrowserTest::ExpectResultAndRun,
+ true,
+ run_loop.QuitClosure()));
+ run_loop.Run();
+ }
+ EXPECT_GE(1, CountRenderProcessHosts()) << "Unregistering doesn't stop the "
+ "workers eagerly, so their RPHs "
+ "can still be running.";
+
+ // Should not be able to find it.
+ {
+ ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
+ GURL script_url;
+ RunOnIOThread(
+ base::Bind(&ServiceWorkerBlackBoxBrowserTest::FindRegistrationOnIO,
+ this,
+ embedded_test_server()->GetURL("/service_worker/empty.html"),
+ &status,
+ &script_url));
+ EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND, status);
+ }
+}
+
+} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_context.h b/chromium/content/browser/service_worker/service_worker_context.h
deleted file mode 100644
index d9f51f3165c..00000000000
--- a/chromium/content/browser/service_worker/service_worker_context.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CONTEXT_H_
-#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CONTEXT_H_
-
-#include "base/basictypes.h"
-
-namespace content {
-
-// Represents the per-BrowserContext ServiceWorker data.
-class ServiceWorkerContext {
- public:
- // TODO(michaeln): This class is a place holder for content/public api
- // which will come later. Promote this class when we get there.
-
- protected:
- ServiceWorkerContext() {}
- virtual ~ServiceWorkerContext() {}
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ServiceWorkerContext);
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CONTEXT_H_
diff --git a/chromium/content/browser/service_worker/service_worker_context_core.cc b/chromium/content/browser/service_worker/service_worker_context_core.cc
index 048d33bd836..cdc67460bcc 100644
--- a/chromium/content/browser/service_worker/service_worker_context_core.cc
+++ b/chromium/content/browser/service_worker/service_worker_context_core.cc
@@ -4,27 +4,106 @@
#include "content/browser/service_worker/service_worker_context_core.h"
-#include "base/command_line.h"
#include "base/files/file_path.h"
+#include "base/message_loop/message_loop_proxy.h"
#include "base/strings/string_util.h"
#include "content/browser/service_worker/embedded_worker_registry.h"
+#include "content/browser/service_worker/service_worker_context_observer.h"
+#include "content/browser/service_worker/service_worker_context_wrapper.h"
+#include "content/browser/service_worker/service_worker_info.h"
+#include "content/browser/service_worker/service_worker_job_coordinator.h"
+#include "content/browser/service_worker/service_worker_process_manager.h"
#include "content/browser/service_worker/service_worker_provider_host.h"
#include "content/browser/service_worker/service_worker_register_job.h"
#include "content/browser/service_worker/service_worker_registration.h"
#include "content/browser/service_worker/service_worker_storage.h"
#include "content/public/browser/browser_thread.h"
-#include "content/public/common/content_switches.h"
#include "url/gurl.h"
namespace content {
+ServiceWorkerContextCore::ProviderHostIterator::~ProviderHostIterator() {}
+
+ServiceWorkerProviderHost*
+ServiceWorkerContextCore::ProviderHostIterator::GetProviderHost() {
+ DCHECK(!IsAtEnd());
+ return provider_host_iterator_->GetCurrentValue();
+}
+
+void ServiceWorkerContextCore::ProviderHostIterator::Advance() {
+ DCHECK(!IsAtEnd());
+ DCHECK(!provider_host_iterator_->IsAtEnd());
+ DCHECK(!provider_iterator_->IsAtEnd());
+
+ // Advance the inner iterator. If an element is reached, we're done.
+ provider_host_iterator_->Advance();
+ if (!provider_host_iterator_->IsAtEnd())
+ return;
+
+ // Advance the outer iterator until an element is reached, or end is hit.
+ while (true) {
+ provider_iterator_->Advance();
+ if (provider_iterator_->IsAtEnd())
+ return;
+ ProviderMap* provider_map = provider_iterator_->GetCurrentValue();
+ provider_host_iterator_.reset(new ProviderMap::iterator(provider_map));
+ if (!provider_host_iterator_->IsAtEnd())
+ return;
+ }
+}
+
+bool ServiceWorkerContextCore::ProviderHostIterator::IsAtEnd() {
+ return provider_iterator_->IsAtEnd() &&
+ (!provider_host_iterator_ || provider_host_iterator_->IsAtEnd());
+}
+
+ServiceWorkerContextCore::ProviderHostIterator::ProviderHostIterator(
+ ProcessToProviderMap* map)
+ : map_(map) {
+ DCHECK(map);
+ Initialize();
+}
+
+void ServiceWorkerContextCore::ProviderHostIterator::Initialize() {
+ provider_iterator_.reset(new ProcessToProviderMap::iterator(map_));
+ // Advance to the first element.
+ while (!provider_iterator_->IsAtEnd()) {
+ ProviderMap* provider_map = provider_iterator_->GetCurrentValue();
+ provider_host_iterator_.reset(new ProviderMap::iterator(provider_map));
+ if (!provider_host_iterator_->IsAtEnd())
+ return;
+ provider_iterator_->Advance();
+ }
+}
+
ServiceWorkerContextCore::ServiceWorkerContextCore(
const base::FilePath& path,
- quota::QuotaManagerProxy* quota_manager_proxy)
- : storage_(new ServiceWorkerStorage(path, quota_manager_proxy)),
- embedded_worker_registry_(new EmbeddedWorkerRegistry(AsWeakPtr())) {}
+ base::SequencedTaskRunner* database_task_runner,
+ base::MessageLoopProxy* disk_cache_thread,
+ quota::QuotaManagerProxy* quota_manager_proxy,
+ ObserverListThreadSafe<ServiceWorkerContextObserver>* observer_list,
+ ServiceWorkerContextWrapper* wrapper)
+ : weak_factory_(this),
+ wrapper_(wrapper),
+ storage_(new ServiceWorkerStorage(path,
+ AsWeakPtr(),
+ database_task_runner,
+ disk_cache_thread,
+ quota_manager_proxy)),
+ embedded_worker_registry_(new EmbeddedWorkerRegistry(AsWeakPtr())),
+ job_coordinator_(new ServiceWorkerJobCoordinator(AsWeakPtr())),
+ next_handle_id_(0),
+ observer_list_(observer_list) {
+}
-ServiceWorkerContextCore::~ServiceWorkerContextCore() {}
+ServiceWorkerContextCore::~ServiceWorkerContextCore() {
+ for (VersionMap::iterator it = live_versions_.begin();
+ it != live_versions_.end();
+ ++it) {
+ it->second->RemoveListener(this);
+ }
+ weak_factory_.InvalidateWeakPtrs();
+}
ServiceWorkerProviderHost* ServiceWorkerContextCore::GetProviderHost(
int process_id, int provider_id) {
@@ -58,42 +137,204 @@ void ServiceWorkerContextCore::RemoveAllProviderHostsForProcess(
providers_.Remove(process_id);
}
-bool ServiceWorkerContextCore::IsEnabled() {
- return CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kEnableServiceWorker);
+scoped_ptr<ServiceWorkerContextCore::ProviderHostIterator>
+ServiceWorkerContextCore::GetProviderHostIterator() {
+ return make_scoped_ptr(new ProviderHostIterator(&providers_));
}
void ServiceWorkerContextCore::RegisterServiceWorker(
const GURL& pattern,
const GURL& script_url,
+ int source_process_id,
+ ServiceWorkerProviderHost* provider_host,
const RegistrationCallback& callback) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
- storage_->Register(pattern,
- script_url,
- base::Bind(&ServiceWorkerContextCore::RegistrationComplete,
- AsWeakPtr(),
- callback));
+ // TODO(kinuko): Wire the provider_host so that we can tell which document
+ // is calling .register.
+
+ job_coordinator_->Register(
+ pattern,
+ script_url,
+ source_process_id,
+ base::Bind(&ServiceWorkerContextCore::RegistrationComplete,
+ AsWeakPtr(),
+ pattern,
+ callback));
}
void ServiceWorkerContextCore::UnregisterServiceWorker(
const GURL& pattern,
const UnregistrationCallback& callback) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
- storage_->Unregister(pattern, callback);
+ job_coordinator_->Unregister(
+ pattern,
+ base::Bind(&ServiceWorkerContextCore::UnregistrationComplete,
+ AsWeakPtr(),
+ pattern,
+ callback));
}
void ServiceWorkerContextCore::RegistrationComplete(
+ const GURL& pattern,
const ServiceWorkerContextCore::RegistrationCallback& callback,
- ServiceWorkerRegistrationStatus status,
- const scoped_refptr<ServiceWorkerRegistration>& registration) {
- if (status != REGISTRATION_OK) {
- DCHECK(!registration);
- callback.Run(status, -1L);
+ ServiceWorkerStatusCode status,
+ ServiceWorkerRegistration* registration,
+ ServiceWorkerVersion* version) {
+ if (status != SERVICE_WORKER_OK) {
+ DCHECK(!version);
+ callback.Run(status,
+ kInvalidServiceWorkerRegistrationId,
+ kInvalidServiceWorkerVersionId);
+ return;
+ }
+
+ DCHECK(version);
+ DCHECK_EQ(version->registration_id(), registration->id());
+ callback.Run(status,
+ registration->id(),
+ version->version_id());
+ if (observer_list_) {
+ observer_list_->Notify(&ServiceWorkerContextObserver::OnRegistrationStored,
+ pattern);
+ }
+}
+
+void ServiceWorkerContextCore::UnregistrationComplete(
+ const GURL& pattern,
+ const ServiceWorkerContextCore::UnregistrationCallback& callback,
+ ServiceWorkerStatusCode status) {
+ callback.Run(status);
+ if (observer_list_) {
+ observer_list_->Notify(&ServiceWorkerContextObserver::OnRegistrationDeleted,
+ pattern);
+ }
+}
+
+ServiceWorkerRegistration* ServiceWorkerContextCore::GetLiveRegistration(
+ int64 id) {
+ RegistrationsMap::iterator it = live_registrations_.find(id);
+ return (it != live_registrations_.end()) ? it->second : NULL;
+}
+
+void ServiceWorkerContextCore::AddLiveRegistration(
+ ServiceWorkerRegistration* registration) {
+ DCHECK(!GetLiveRegistration(registration->id()));
+ live_registrations_[registration->id()] = registration;
+}
+
+void ServiceWorkerContextCore::RemoveLiveRegistration(int64 id) {
+ live_registrations_.erase(id);
+}
+
+ServiceWorkerVersion* ServiceWorkerContextCore::GetLiveVersion(
+ int64 id) {
+ VersionMap::iterator it = live_versions_.find(id);
+ return (it != live_versions_.end()) ? it->second : NULL;
+}
+
+void ServiceWorkerContextCore::AddLiveVersion(ServiceWorkerVersion* version) {
+ DCHECK(!GetLiveVersion(version->version_id()));
+ live_versions_[version->version_id()] = version;
+ version->AddListener(this);
+}
+
+void ServiceWorkerContextCore::RemoveLiveVersion(int64 id) {
+ live_versions_.erase(id);
+}
+
+std::vector<ServiceWorkerRegistrationInfo>
+ServiceWorkerContextCore::GetAllLiveRegistrationInfo() {
+ std::vector<ServiceWorkerRegistrationInfo> infos;
+ for (std::map<int64, ServiceWorkerRegistration*>::const_iterator iter =
+ live_registrations_.begin();
+ iter != live_registrations_.end();
+ ++iter) {
+ infos.push_back(iter->second->GetInfo());
}
+ return infos;
+}
+
+std::vector<ServiceWorkerVersionInfo>
+ServiceWorkerContextCore::GetAllLiveVersionInfo() {
+ std::vector<ServiceWorkerVersionInfo> infos;
+ for (std::map<int64, ServiceWorkerVersion*>::const_iterator iter =
+ live_versions_.begin();
+ iter != live_versions_.end();
+ ++iter) {
+ infos.push_back(iter->second->GetInfo());
+ }
+ return infos;
+}
+
+int ServiceWorkerContextCore::GetNewServiceWorkerHandleId() {
+ return next_handle_id_++;
+}
+
+void ServiceWorkerContextCore::OnWorkerStarted(ServiceWorkerVersion* version) {
+ if (!observer_list_)
+ return;
+ observer_list_->Notify(&ServiceWorkerContextObserver::OnWorkerStarted,
+ version->version_id(),
+ version->embedded_worker()->process_id(),
+ version->embedded_worker()->thread_id());
+}
+
+void ServiceWorkerContextCore::OnWorkerStopped(ServiceWorkerVersion* version) {
+ if (!observer_list_)
+ return;
+ observer_list_->Notify(&ServiceWorkerContextObserver::OnWorkerStopped,
+ version->version_id(),
+ version->embedded_worker()->process_id(),
+ version->embedded_worker()->thread_id());
+}
+
+void ServiceWorkerContextCore::OnVersionStateChanged(
+ ServiceWorkerVersion* version) {
+ if (!observer_list_)
+ return;
+ observer_list_->Notify(&ServiceWorkerContextObserver::OnVersionStateChanged,
+ version->version_id());
+}
+
+void ServiceWorkerContextCore::OnErrorReported(
+ ServiceWorkerVersion* version,
+ const base::string16& error_message,
+ int line_number,
+ int column_number,
+ const GURL& source_url) {
+ if (!observer_list_)
+ return;
+ observer_list_->Notify(
+ &ServiceWorkerContextObserver::OnErrorReported,
+ version->version_id(),
+ version->embedded_worker()->process_id(),
+ version->embedded_worker()->thread_id(),
+ ServiceWorkerContextObserver::ErrorInfo(
+ error_message, line_number, column_number, source_url));
+}
+
+void ServiceWorkerContextCore::OnReportConsoleMessage(
+ ServiceWorkerVersion* version,
+ int source_identifier,
+ int message_level,
+ const base::string16& message,
+ int line_number,
+ const GURL& source_url) {
+ if (!observer_list_)
+ return;
+ observer_list_->Notify(
+ &ServiceWorkerContextObserver::OnReportConsoleMessage,
+ version->version_id(),
+ version->embedded_worker()->process_id(),
+ version->embedded_worker()->thread_id(),
+ ServiceWorkerContextObserver::ConsoleMessage(
+ source_identifier, message_level, message, line_number, source_url));
+}
- callback.Run(status, registration->id());
+ServiceWorkerProcessManager* ServiceWorkerContextCore::process_manager() {
+ return wrapper_->process_manager();
}
} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_context_core.h b/chromium/content/browser/service_worker/service_worker_context_core.h
index b7b26537bec..b3f97ae28f3 100644
--- a/chromium/content/browser/service_worker/service_worker_context_core.h
+++ b/chromium/content/browser/service_worker/service_worker_context_core.h
@@ -6,12 +6,16 @@
#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CONTEXT_CORE_H_
#include <map>
+#include <vector>
#include "base/callback.h"
#include "base/files/file_path.h"
#include "base/id_map.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
+#include "base/observer_list_threadsafe.h"
+#include "content/browser/service_worker/service_worker_info.h"
+#include "content/browser/service_worker/service_worker_process_manager.h"
#include "content/browser/service_worker/service_worker_provider_host.h"
#include "content/browser/service_worker/service_worker_registration_status.h"
#include "content/browser/service_worker/service_worker_storage.h"
@@ -21,6 +25,8 @@ class GURL;
namespace base {
class FilePath;
+class MessageLoopProxy;
+class SequencedTaskRunner;
}
namespace quota {
@@ -30,6 +36,10 @@ class QuotaManagerProxy;
namespace content {
class EmbeddedWorkerRegistry;
+class ServiceWorkerContextObserver;
+class ServiceWorkerContextWrapper;
+class ServiceWorkerHandle;
+class ServiceWorkerJobCoordinator;
class ServiceWorkerProviderHost;
class ServiceWorkerRegistration;
class ServiceWorkerStorage;
@@ -40,61 +50,149 @@ class ServiceWorkerStorage;
// is the root of the containment hierarchy for service worker data
// associated with a particular partition.
class CONTENT_EXPORT ServiceWorkerContextCore
- : NON_EXPORTED_BASE(
- public base::SupportsWeakPtr<ServiceWorkerContextCore>) {
+ : public ServiceWorkerVersion::Listener {
public:
- typedef base::Callback<void(ServiceWorkerRegistrationStatus status,
- int64 registration_id)> RegistrationCallback;
+ typedef base::Callback<void(ServiceWorkerStatusCode status,
+ int64 registration_id,
+ int64 version_id)> RegistrationCallback;
typedef base::Callback<
- void(ServiceWorkerRegistrationStatus status)> UnregistrationCallback;
+ void(ServiceWorkerStatusCode status)> UnregistrationCallback;
+ typedef IDMap<ServiceWorkerProviderHost, IDMapOwnPointer> ProviderMap;
+ typedef IDMap<ProviderMap, IDMapOwnPointer> ProcessToProviderMap;
+
+ // Iterates over ServiceWorkerProviderHost objects in a ProcessToProviderMap.
+ class ProviderHostIterator {
+ public:
+ ~ProviderHostIterator();
+ ServiceWorkerProviderHost* GetProviderHost();
+ void Advance();
+ bool IsAtEnd();
+
+ private:
+ friend class ServiceWorkerContextCore;
+ explicit ProviderHostIterator(ProcessToProviderMap* map);
+ void Initialize();
+
+ ProcessToProviderMap* map_;
+ scoped_ptr<ProcessToProviderMap::iterator> provider_iterator_;
+ scoped_ptr<ProviderMap::iterator> provider_host_iterator_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProviderHostIterator);
+ };
// This is owned by the StoragePartition, which will supply it with
// the local path on disk. Given an empty |user_data_directory|,
- // nothing will be stored on disk.
- ServiceWorkerContextCore(const base::FilePath& user_data_directory,
- quota::QuotaManagerProxy* quota_manager_proxy);
- ~ServiceWorkerContextCore();
+ // nothing will be stored on disk. |observer_list| is created in
+ // ServiceWorkerContextWrapper. When Notify() of |observer_list| is called in
+ // ServiceWorkerContextCore, the methods of ServiceWorkerContextObserver will
+ // be called on the thread which called AddObserver() of |observer_list|.
+ ServiceWorkerContextCore(
+ const base::FilePath& user_data_directory,
+ base::SequencedTaskRunner* database_task_runner,
+ base::MessageLoopProxy* disk_cache_thread,
+ quota::QuotaManagerProxy* quota_manager_proxy,
+ ObserverListThreadSafe<ServiceWorkerContextObserver>* observer_list,
+ ServiceWorkerContextWrapper* wrapper);
+ virtual ~ServiceWorkerContextCore();
+
+ // ServiceWorkerVersion::Listener overrides.
+ virtual void OnWorkerStarted(ServiceWorkerVersion* version) OVERRIDE;
+ virtual void OnWorkerStopped(ServiceWorkerVersion* version) OVERRIDE;
+ virtual void OnVersionStateChanged(ServiceWorkerVersion* version) OVERRIDE;
+ virtual void OnErrorReported(ServiceWorkerVersion* version,
+ const base::string16& error_message,
+ int line_number,
+ int column_number,
+ const GURL& source_url) OVERRIDE;
+ virtual void OnReportConsoleMessage(ServiceWorkerVersion* version,
+ int source_identifier,
+ int message_level,
+ const base::string16& message,
+ int line_number,
+ const GURL& source_url) OVERRIDE;
ServiceWorkerStorage* storage() { return storage_.get(); }
+ ServiceWorkerProcessManager* process_manager();
+ EmbeddedWorkerRegistry* embedded_worker_registry() {
+ return embedded_worker_registry_.get();
+ }
+ ServiceWorkerJobCoordinator* job_coordinator() {
+ return job_coordinator_.get();
+ }
// The context class owns the set of ProviderHosts.
ServiceWorkerProviderHost* GetProviderHost(int process_id, int provider_id);
void AddProviderHost(scoped_ptr<ServiceWorkerProviderHost> provider_host);
void RemoveProviderHost(int process_id, int provider_id);
void RemoveAllProviderHostsForProcess(int process_id);
-
- // Checks the cmdline flag.
- bool IsEnabled();
+ scoped_ptr<ProviderHostIterator> GetProviderHostIterator();
// The callback will be called on the IO thread.
+ // A child process of |source_process_id| may be used to run the created
+ // worker for initial installation.
+ // Non-null |provider_host| must be given if this is called from a document,
+ // whose process_id() must match with |source_process_id|.
void RegisterServiceWorker(const GURL& pattern,
const GURL& script_url,
+ int source_process_id,
+ ServiceWorkerProviderHost* provider_host,
const RegistrationCallback& callback);
// The callback will be called on the IO thread.
void UnregisterServiceWorker(const GURL& pattern,
const UnregistrationCallback& callback);
- EmbeddedWorkerRegistry* embedded_worker_registry() {
- return embedded_worker_registry_.get();
+ // This class maintains collections of live instances, this class
+ // does not own these object or influence their lifetime.
+ ServiceWorkerRegistration* GetLiveRegistration(int64 registration_id);
+ void AddLiveRegistration(ServiceWorkerRegistration* registration);
+ void RemoveLiveRegistration(int64 registration_id);
+ ServiceWorkerVersion* GetLiveVersion(int64 version_id);
+ void AddLiveVersion(ServiceWorkerVersion* version);
+ void RemoveLiveVersion(int64 registration_id);
+
+ std::vector<ServiceWorkerRegistrationInfo> GetAllLiveRegistrationInfo();
+ std::vector<ServiceWorkerVersionInfo> GetAllLiveVersionInfo();
+
+ // Returns new context-local unique ID for ServiceWorkerHandle.
+ int GetNewServiceWorkerHandleId();
+
+ base::WeakPtr<ServiceWorkerContextCore> AsWeakPtr() {
+ return weak_factory_.GetWeakPtr();
}
private:
- typedef IDMap<ServiceWorkerProviderHost, IDMapOwnPointer> ProviderMap;
- typedef IDMap<ProviderMap, IDMapOwnPointer> ProcessToProviderMap;
+ typedef std::map<int64, ServiceWorkerRegistration*> RegistrationsMap;
+ typedef std::map<int64, ServiceWorkerVersion*> VersionMap;
ProviderMap* GetProviderMapForProcess(int process_id) {
return providers_.Lookup(process_id);
}
- void RegistrationComplete(
- const RegistrationCallback& callback,
- ServiceWorkerRegistrationStatus status,
- const scoped_refptr<ServiceWorkerRegistration>& registration);
-
+ void RegistrationComplete(const GURL& pattern,
+ const RegistrationCallback& callback,
+ ServiceWorkerStatusCode status,
+ ServiceWorkerRegistration* registration,
+ ServiceWorkerVersion* version);
+
+ void UnregistrationComplete(const GURL& pattern,
+ const UnregistrationCallback& callback,
+ ServiceWorkerStatusCode status);
+
+ base::WeakPtrFactory<ServiceWorkerContextCore> weak_factory_;
+ // It's safe to store a raw pointer instead of a scoped_refptr to |wrapper_|
+ // because the Wrapper::Shutdown call that hops threads to destroy |this| uses
+ // Bind() to hold a reference to |wrapper_| until |this| is fully destroyed.
+ ServiceWorkerContextWrapper* wrapper_;
ProcessToProviderMap providers_;
scoped_ptr<ServiceWorkerStorage> storage_;
scoped_refptr<EmbeddedWorkerRegistry> embedded_worker_registry_;
+ scoped_ptr<ServiceWorkerJobCoordinator> job_coordinator_;
+ std::map<int64, ServiceWorkerRegistration*> live_registrations_;
+ std::map<int64, ServiceWorkerVersion*> live_versions_;
+ int next_handle_id_;
+ scoped_refptr<ObserverListThreadSafe<ServiceWorkerContextObserver> >
+ observer_list_;
DISALLOW_COPY_AND_ASSIGN(ServiceWorkerContextCore);
};
diff --git a/chromium/content/browser/service_worker/service_worker_context_observer.h b/chromium/content/browser/service_worker/service_worker_context_observer.h
new file mode 100644
index 00000000000..8bdaeea394f
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_context_observer.h
@@ -0,0 +1,70 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CONTEXT_OBSERVER_H_
+#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CONTEXT_OBSERVER_H_
+
+#include "base/strings/string16.h"
+#include "url/gurl.h"
+
+namespace content {
+
+class ServiceWorkerContextObserver {
+ public:
+ struct ErrorInfo {
+ ErrorInfo(const base::string16& message,
+ int line,
+ int column,
+ const GURL& url)
+ : error_message(message),
+ line_number(line),
+ column_number(column),
+ source_url(url) {}
+ const base::string16 error_message;
+ const int line_number;
+ const int column_number;
+ const GURL source_url;
+ };
+ struct ConsoleMessage {
+ ConsoleMessage(int source_identifier,
+ int message_level,
+ const base::string16& message,
+ int line_number,
+ const GURL& source_url)
+ : source_identifier(source_identifier),
+ message_level(message_level),
+ message(message),
+ line_number(line_number),
+ source_url(source_url) {}
+ const int source_identifier;
+ const int message_level;
+ const base::string16 message;
+ const int line_number;
+ const GURL source_url;
+ };
+ virtual void OnWorkerStarted(int64 version_id,
+ int process_id,
+ int thread_id) {}
+ virtual void OnWorkerStopped(int64 version_id,
+ int process_id,
+ int thread_id) {}
+ virtual void OnVersionStateChanged(int64 version_id) {}
+ virtual void OnErrorReported(int64 version_id,
+ int process_id,
+ int thread_id,
+ const ErrorInfo& info) {}
+ virtual void OnReportConsoleMessage(int64 version_id,
+ int process_id,
+ int thread_id,
+ const ConsoleMessage& message) {}
+ virtual void OnRegistrationStored(const GURL& pattern) {}
+ virtual void OnRegistrationDeleted(const GURL& pattern) {}
+
+ protected:
+ virtual ~ServiceWorkerContextObserver() {}
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CONTEXT_OBSERVER_H_
diff --git a/chromium/content/browser/service_worker/service_worker_context_request_handler.cc b/chromium/content/browser/service_worker/service_worker_context_request_handler.cc
new file mode 100644
index 00000000000..248587fceb7
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_context_request_handler.cc
@@ -0,0 +1,90 @@
+// 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/service_worker/service_worker_context_request_handler.h"
+
+#include "content/browser/service_worker/service_worker_context_core.h"
+#include "content/browser/service_worker/service_worker_provider_host.h"
+#include "content/browser/service_worker/service_worker_read_from_cache_job.h"
+#include "content/browser/service_worker/service_worker_storage.h"
+#include "content/browser/service_worker/service_worker_version.h"
+#include "content/browser/service_worker/service_worker_write_to_cache_job.h"
+#include "net/url_request/url_request.h"
+
+namespace content {
+
+ServiceWorkerContextRequestHandler::ServiceWorkerContextRequestHandler(
+ base::WeakPtr<ServiceWorkerContextCore> context,
+ base::WeakPtr<ServiceWorkerProviderHost> provider_host,
+ base::WeakPtr<webkit_blob::BlobStorageContext> blob_storage_context,
+ ResourceType::Type resource_type)
+ : ServiceWorkerRequestHandler(context,
+ provider_host,
+ blob_storage_context,
+ resource_type),
+ version_(provider_host_->running_hosted_version()) {
+ DCHECK(provider_host_->IsHostToRunningServiceWorker());
+}
+
+ServiceWorkerContextRequestHandler::~ServiceWorkerContextRequestHandler() {
+}
+
+net::URLRequestJob* ServiceWorkerContextRequestHandler::MaybeCreateJob(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) {
+ if (!provider_host_ || !version_ || !context_)
+ return NULL;
+
+ // We currently have no use case for hijacking a redirected request.
+ if (request->url_chain().size() > 1)
+ return NULL;
+
+ // We only use the script cache for main script loading and
+ // importScripts(), even if a cached script is xhr'd, we don't
+ // retrieve it from the script cache.
+ // TODO(michaeln): Get the desired behavior clarified in the spec,
+ // and make tweak the behavior here to match.
+ if (resource_type_ != ResourceType::SERVICE_WORKER &&
+ resource_type_ != ResourceType::SCRIPT) {
+ return NULL;
+ }
+
+ if (ShouldAddToScriptCache(request->url())) {
+ int64 response_id = context_->storage()->NewResourceId();
+ if (response_id == kInvalidServiceWorkerResponseId)
+ return NULL;
+ return new ServiceWorkerWriteToCacheJob(
+ request, network_delegate, context_, version_, response_id);
+ }
+
+ int64 response_id = kInvalidServiceWorkerResponseId;
+ if (ShouldReadFromScriptCache(request->url(), &response_id)) {
+ return new ServiceWorkerReadFromCacheJob(
+ request, network_delegate, context_, response_id);
+ }
+
+ // NULL means use the network.
+ return NULL;
+}
+
+bool ServiceWorkerContextRequestHandler::ShouldAddToScriptCache(
+ const GURL& url) {
+ // We only write imports that occur during the initial eval.
+ if (version_->status() != ServiceWorkerVersion::NEW)
+ return false;
+ return version_->script_cache_map()->Lookup(url) ==
+ kInvalidServiceWorkerResponseId;
+}
+
+bool ServiceWorkerContextRequestHandler::ShouldReadFromScriptCache(
+ const GURL& url, int64* response_id_out) {
+ // We don't read from the script cache until the version is INSTALLED.
+ if (version_->status() == ServiceWorkerVersion::NEW ||
+ version_->status() == ServiceWorkerVersion::INSTALLING)
+ return false;
+ *response_id_out = version_->script_cache_map()->Lookup(url);
+ return *response_id_out != kInvalidServiceWorkerResponseId;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_context_request_handler.h b/chromium/content/browser/service_worker/service_worker_context_request_handler.h
new file mode 100644
index 00000000000..ca894ce237e
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_context_request_handler.h
@@ -0,0 +1,42 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CONTEXT_REQUEST_HANDLER_H_
+#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CONTEXT_REQUEST_HANDLER_H_
+
+#include "content/browser/service_worker/service_worker_request_handler.h"
+
+namespace content {
+
+class ServiceWorkerVersion;
+
+// A request handler derivative used to handle requests from
+// service workers.
+class CONTENT_EXPORT ServiceWorkerContextRequestHandler
+ : public ServiceWorkerRequestHandler {
+ public:
+ ServiceWorkerContextRequestHandler(
+ base::WeakPtr<ServiceWorkerContextCore> context,
+ base::WeakPtr<ServiceWorkerProviderHost> provider_host,
+ base::WeakPtr<webkit_blob::BlobStorageContext> blob_storage_context,
+ ResourceType::Type resource_type);
+ virtual ~ServiceWorkerContextRequestHandler();
+
+ // Called via custom URLRequestJobFactory.
+ virtual net::URLRequestJob* MaybeCreateJob(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) OVERRIDE;
+
+ private:
+ bool ShouldAddToScriptCache(const GURL& url);
+ bool ShouldReadFromScriptCache(const GURL& url, int64* response_id_out);
+
+ scoped_refptr<ServiceWorkerVersion> version_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServiceWorkerContextRequestHandler);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CONTEXT_REQUEST_HANDLER_H_
diff --git a/chromium/content/browser/service_worker/service_worker_context_unittest.cc b/chromium/content/browser/service_worker/service_worker_context_unittest.cc
index e7c230f93a0..14f54f7cb50 100644
--- a/chromium/content/browser/service_worker/service_worker_context_unittest.cc
+++ b/chromium/content/browser/service_worker/service_worker_context_unittest.cc
@@ -2,14 +2,19 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "content/browser/service_worker/service_worker_context.h"
+#include "content/public/browser/service_worker_context.h"
#include "base/files/scoped_temp_dir.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "content/browser/browser_thread_impl.h"
+#include "content/browser/service_worker/embedded_worker_registry.h"
+#include "content/browser/service_worker/embedded_worker_test_helper.h"
#include "content/browser/service_worker/service_worker_context_core.h"
#include "content/browser/service_worker/service_worker_registration.h"
+#include "content/browser/service_worker/service_worker_storage.h"
+#include "content/common/service_worker/embedded_worker_messages.h"
+#include "content/common/service_worker/service_worker_messages.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "content/public/test/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -19,20 +24,27 @@ namespace content {
namespace {
void SaveResponseCallback(bool* called,
- int64* store_result,
- ServiceWorkerRegistrationStatus status,
- int64 result) {
+ int64* store_registration_id,
+ int64* store_version_id,
+ ServiceWorkerStatusCode status,
+ int64 registration_id,
+ int64 version_id) {
+ EXPECT_EQ(SERVICE_WORKER_OK, status) << ServiceWorkerStatusToString(status);
*called = true;
- *store_result = result;
+ *store_registration_id = registration_id;
+ *store_version_id = version_id;
}
ServiceWorkerContextCore::RegistrationCallback MakeRegisteredCallback(
bool* called,
- int64* store_result) {
- return base::Bind(&SaveResponseCallback, called, store_result);
+ int64* store_registration_id,
+ int64* store_version_id) {
+ return base::Bind(&SaveResponseCallback, called,
+ store_registration_id,
+ store_version_id);
}
-void CallCompletedCallback(bool* called, ServiceWorkerRegistrationStatus) {
+void CallCompletedCallback(bool* called, ServiceWorkerStatusCode) {
*called = true;
}
@@ -41,44 +53,212 @@ ServiceWorkerContextCore::UnregistrationCallback MakeUnregisteredCallback(
return base::Bind(&CallCompletedCallback, called);
}
+void ExpectRegisteredWorkers(
+ ServiceWorkerStatusCode expect_status,
+ int64 expect_version_id,
+ bool expect_waiting,
+ bool expect_active,
+ ServiceWorkerStatusCode status,
+ const scoped_refptr<ServiceWorkerRegistration>& registration) {
+ ASSERT_EQ(expect_status, status);
+ if (status != SERVICE_WORKER_OK) {
+ EXPECT_FALSE(registration);
+ return;
+ }
+
+ if (expect_waiting) {
+ EXPECT_TRUE(registration->waiting_version());
+ EXPECT_EQ(expect_version_id,
+ registration->waiting_version()->version_id());
+ } else {
+ EXPECT_FALSE(registration->waiting_version());
+ }
+
+ if (expect_active) {
+ EXPECT_TRUE(registration->active_version());
+ EXPECT_EQ(expect_version_id,
+ registration->active_version()->version_id());
+ } else {
+ EXPECT_FALSE(registration->active_version());
+ }
+}
+
+class RejectInstallTestHelper : public EmbeddedWorkerTestHelper {
+ public:
+ RejectInstallTestHelper(int mock_render_process_id)
+ : EmbeddedWorkerTestHelper(mock_render_process_id) {}
+
+ virtual void OnInstallEvent(int embedded_worker_id,
+ int request_id,
+ int active_version_id) OVERRIDE {
+ SimulateSend(
+ new ServiceWorkerHostMsg_InstallEventFinished(
+ embedded_worker_id, request_id,
+ blink::WebServiceWorkerEventResultRejected));
+ }
+};
+
+class RejectActivateTestHelper : public EmbeddedWorkerTestHelper {
+ public:
+ RejectActivateTestHelper(int mock_render_process_id)
+ : EmbeddedWorkerTestHelper(mock_render_process_id) {}
+
+ virtual void OnActivateEvent(int embedded_worker_id,
+ int request_id) OVERRIDE {
+ SimulateSend(
+ new ServiceWorkerHostMsg_ActivateEventFinished(
+ embedded_worker_id, request_id,
+ blink::WebServiceWorkerEventResultRejected));
+ }
+};
+
} // namespace
class ServiceWorkerContextTest : public testing::Test {
public:
ServiceWorkerContextTest()
- : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {}
+ : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP),
+ render_process_id_(99) {}
virtual void SetUp() OVERRIDE {
- context_.reset(new ServiceWorkerContextCore(base::FilePath(), NULL));
+ helper_.reset(new EmbeddedWorkerTestHelper(render_process_id_));
+ }
+
+ virtual void TearDown() OVERRIDE {
+ helper_.reset();
}
- virtual void TearDown() OVERRIDE { context_.reset(); }
+ ServiceWorkerContextCore* context() { return helper_->context(); }
protected:
TestBrowserThreadBundle browser_thread_bundle_;
- scoped_ptr<ServiceWorkerContextCore> context_;
+ scoped_ptr<EmbeddedWorkerTestHelper> helper_;
+ const int render_process_id_;
};
-void RegistrationCallback(
- scoped_refptr<ServiceWorkerRegistration>* registration,
- const scoped_refptr<ServiceWorkerRegistration>& result) {
- *registration = result;
-}
-
// Make sure basic registration is working.
TEST_F(ServiceWorkerContextTest, Register) {
- int64 registration_id = -1L;
+ int64 registration_id = kInvalidServiceWorkerRegistrationId;
+ int64 version_id = kInvalidServiceWorkerVersionId;
bool called = false;
- context_->RegisterServiceWorker(
+ context()->RegisterServiceWorker(
GURL("http://www.example.com/*"),
GURL("http://www.example.com/service_worker.js"),
- MakeRegisteredCallback(&called, &registration_id));
+ render_process_id_,
+ NULL,
+ MakeRegisteredCallback(&called, &registration_id, &version_id));
ASSERT_FALSE(called);
base::RunLoop().RunUntilIdle();
- ASSERT_TRUE(called);
+ EXPECT_TRUE(called);
+
+ EXPECT_EQ(4UL, helper_->ipc_sink()->message_count());
+ EXPECT_TRUE(helper_->ipc_sink()->GetUniqueMessageMatching(
+ EmbeddedWorkerMsg_StartWorker::ID));
+ EXPECT_TRUE(helper_->inner_ipc_sink()->GetUniqueMessageMatching(
+ ServiceWorkerMsg_InstallEvent::ID));
+ EXPECT_TRUE(helper_->inner_ipc_sink()->GetUniqueMessageMatching(
+ ServiceWorkerMsg_ActivateEvent::ID));
+ EXPECT_TRUE(helper_->ipc_sink()->GetUniqueMessageMatching(
+ EmbeddedWorkerMsg_StopWorker::ID));
+ EXPECT_NE(kInvalidServiceWorkerRegistrationId, registration_id);
+ EXPECT_NE(kInvalidServiceWorkerVersionId, version_id);
+
+ context()->storage()->FindRegistrationForId(
+ registration_id,
+ GURL("http://www.example.com"),
+ base::Bind(&ExpectRegisteredWorkers,
+ SERVICE_WORKER_OK,
+ version_id,
+ false /* expect_waiting */,
+ true /* expect_active */));
+ base::RunLoop().RunUntilIdle();
+}
+
+// Test registration when the service worker rejects the install event. The
+// registration callback should indicate success, but there should be no waiting
+// or active worker in the registration.
+TEST_F(ServiceWorkerContextTest, Register_RejectInstall) {
+ helper_.reset(); // Make sure the process lookups stay overridden.
+ helper_.reset(new RejectInstallTestHelper(render_process_id_));
+ int64 registration_id = kInvalidServiceWorkerRegistrationId;
+ int64 version_id = kInvalidServiceWorkerVersionId;
+ bool called = false;
+ context()->RegisterServiceWorker(
+ GURL("http://www.example.com/*"),
+ GURL("http://www.example.com/service_worker.js"),
+ render_process_id_,
+ NULL,
+ MakeRegisteredCallback(&called, &registration_id, &version_id));
+
+ ASSERT_FALSE(called);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(called);
+
+ EXPECT_EQ(3UL, helper_->ipc_sink()->message_count());
+ EXPECT_TRUE(helper_->ipc_sink()->GetUniqueMessageMatching(
+ EmbeddedWorkerMsg_StartWorker::ID));
+ EXPECT_TRUE(helper_->inner_ipc_sink()->GetUniqueMessageMatching(
+ ServiceWorkerMsg_InstallEvent::ID));
+ EXPECT_FALSE(helper_->inner_ipc_sink()->GetUniqueMessageMatching(
+ ServiceWorkerMsg_ActivateEvent::ID));
+ EXPECT_TRUE(helper_->ipc_sink()->GetUniqueMessageMatching(
+ EmbeddedWorkerMsg_StopWorker::ID));
+ EXPECT_NE(kInvalidServiceWorkerRegistrationId, registration_id);
+ EXPECT_NE(kInvalidServiceWorkerVersionId, version_id);
+
+ context()->storage()->FindRegistrationForId(
+ registration_id,
+ GURL("http://www.example.com"),
+ base::Bind(&ExpectRegisteredWorkers,
+ SERVICE_WORKER_ERROR_NOT_FOUND,
+ kInvalidServiceWorkerVersionId,
+ false /* expect_waiting */,
+ false /* expect_active */));
+ base::RunLoop().RunUntilIdle();
+}
- ASSERT_NE(-1L, registration_id);
+// Test registration when the service worker rejects the activate event. The
+// registration callback should indicate success, but there should be no waiting
+// or active worker in the registration.
+TEST_F(ServiceWorkerContextTest, Register_RejectActivate) {
+ helper_.reset(); // Make sure the process lookups stay overridden.
+ helper_.reset(new RejectActivateTestHelper(render_process_id_));
+ int64 registration_id = kInvalidServiceWorkerRegistrationId;
+ int64 version_id = kInvalidServiceWorkerVersionId;
+ bool called = false;
+ context()->RegisterServiceWorker(
+ GURL("http://www.example.com/*"),
+ GURL("http://www.example.com/service_worker.js"),
+ render_process_id_,
+ NULL,
+ MakeRegisteredCallback(&called, &registration_id, &version_id));
+
+ ASSERT_FALSE(called);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(called);
+
+ EXPECT_EQ(4UL, helper_->ipc_sink()->message_count());
+ EXPECT_TRUE(helper_->ipc_sink()->GetUniqueMessageMatching(
+ EmbeddedWorkerMsg_StartWorker::ID));
+ EXPECT_TRUE(helper_->inner_ipc_sink()->GetUniqueMessageMatching(
+ ServiceWorkerMsg_InstallEvent::ID));
+ EXPECT_TRUE(helper_->inner_ipc_sink()->GetUniqueMessageMatching(
+ ServiceWorkerMsg_ActivateEvent::ID));
+ EXPECT_TRUE(helper_->ipc_sink()->GetUniqueMessageMatching(
+ EmbeddedWorkerMsg_StopWorker::ID));
+ EXPECT_NE(kInvalidServiceWorkerRegistrationId, registration_id);
+ EXPECT_NE(kInvalidServiceWorkerVersionId, version_id);
+
+ context()->storage()->FindRegistrationForId(
+ registration_id,
+ GURL("http://www.example.com"),
+ base::Bind(&ExpectRegisteredWorkers,
+ SERVICE_WORKER_ERROR_NOT_FOUND,
+ kInvalidServiceWorkerVersionId,
+ false /* expect_waiting */,
+ false /* expect_active */));
+ base::RunLoop().RunUntilIdle();
}
// Make sure registrations are cleaned up when they are unregistered.
@@ -86,22 +266,38 @@ TEST_F(ServiceWorkerContextTest, Unregister) {
GURL pattern("http://www.example.com/*");
bool called = false;
- int64 registration_id = -1L;
- context_->RegisterServiceWorker(
+ int64 registration_id = kInvalidServiceWorkerRegistrationId;
+ int64 version_id = kInvalidServiceWorkerVersionId;
+ context()->RegisterServiceWorker(
pattern,
GURL("http://www.example.com/service_worker.js"),
- MakeRegisteredCallback(&called, &registration_id));
+ render_process_id_,
+ NULL,
+ MakeRegisteredCallback(&called, &registration_id, &version_id));
ASSERT_FALSE(called);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(called);
+ EXPECT_NE(kInvalidServiceWorkerRegistrationId, registration_id);
+ EXPECT_NE(kInvalidServiceWorkerVersionId, version_id);
called = false;
- context_->UnregisterServiceWorker(pattern, MakeUnregisteredCallback(&called));
+ context()->UnregisterServiceWorker(pattern,
+ MakeUnregisteredCallback(&called));
ASSERT_FALSE(called);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(called);
+
+ context()->storage()->FindRegistrationForId(
+ registration_id,
+ pattern.GetOrigin(),
+ base::Bind(&ExpectRegisteredWorkers,
+ SERVICE_WORKER_ERROR_NOT_FOUND,
+ kInvalidServiceWorkerVersionId,
+ false /* expect_waiting */,
+ false /* expect_active */));
+ base::RunLoop().RunUntilIdle();
}
// Make sure that when a new registration replaces an existing
@@ -110,28 +306,41 @@ TEST_F(ServiceWorkerContextTest, RegisterNewScript) {
GURL pattern("http://www.example.com/*");
bool called = false;
- int64 old_registration_id = -1L;
- context_->RegisterServiceWorker(
+ int64 old_registration_id = kInvalidServiceWorkerRegistrationId;
+ int64 old_version_id = kInvalidServiceWorkerVersionId;
+ context()->RegisterServiceWorker(
pattern,
GURL("http://www.example.com/service_worker.js"),
- MakeRegisteredCallback(&called, &old_registration_id));
+ render_process_id_,
+ NULL,
+ MakeRegisteredCallback(&called, &old_registration_id, &old_version_id));
ASSERT_FALSE(called);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(called);
+ EXPECT_NE(kInvalidServiceWorkerRegistrationId, old_registration_id);
+ EXPECT_NE(kInvalidServiceWorkerVersionId, old_version_id);
called = false;
- int64 new_registration_id = -1L;
- context_->RegisterServiceWorker(
+ int64 new_registration_id = kInvalidServiceWorkerRegistrationId;
+ int64 new_version_id = kInvalidServiceWorkerVersionId;
+ context()->RegisterServiceWorker(
pattern,
GURL("http://www.example.com/service_worker_new.js"),
- MakeRegisteredCallback(&called, &new_registration_id));
+ render_process_id_,
+ NULL,
+ MakeRegisteredCallback(&called, &new_registration_id, &new_version_id));
ASSERT_FALSE(called);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(called);
- ASSERT_NE(old_registration_id, new_registration_id);
+ // Returned IDs should be valid, and should differ from the values
+ // returned for the previous registration.
+ EXPECT_NE(kInvalidServiceWorkerRegistrationId, new_registration_id);
+ EXPECT_NE(kInvalidServiceWorkerVersionId, new_version_id);
+ EXPECT_NE(old_registration_id, new_registration_id);
+ EXPECT_NE(old_version_id, new_version_id);
}
// Make sure that when registering a duplicate pattern+script_url
@@ -141,28 +350,36 @@ TEST_F(ServiceWorkerContextTest, RegisterDuplicateScript) {
GURL script_url("http://www.example.com/service_worker.js");
bool called = false;
- int64 old_registration_id = -1L;
- context_->RegisterServiceWorker(
+ int64 old_registration_id = kInvalidServiceWorkerRegistrationId;
+ int64 old_version_id = kInvalidServiceWorkerVersionId;
+ context()->RegisterServiceWorker(
pattern,
script_url,
- MakeRegisteredCallback(&called, &old_registration_id));
+ render_process_id_,
+ NULL,
+ MakeRegisteredCallback(&called, &old_registration_id, &old_version_id));
ASSERT_FALSE(called);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(called);
+ EXPECT_NE(kInvalidServiceWorkerRegistrationId, old_registration_id);
+ EXPECT_NE(kInvalidServiceWorkerVersionId, old_version_id);
called = false;
- int64 new_registration_id = -1L;
- context_->RegisterServiceWorker(
+ int64 new_registration_id = kInvalidServiceWorkerRegistrationId;
+ int64 new_version_id = kInvalidServiceWorkerVersionId;
+ context()->RegisterServiceWorker(
pattern,
script_url,
- MakeRegisteredCallback(&called, &new_registration_id));
+ render_process_id_,
+ NULL,
+ MakeRegisteredCallback(&called, &new_registration_id, &new_version_id));
ASSERT_FALSE(called);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(called);
-
- ASSERT_EQ(old_registration_id, new_registration_id);
+ EXPECT_EQ(old_registration_id, new_registration_id);
+ EXPECT_EQ(old_version_id, new_version_id);
}
} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_context_wrapper.cc b/chromium/content/browser/service_worker/service_worker_context_wrapper.cc
index 49a6842968b..460f6df36e4 100644
--- a/chromium/content/browser/service_worker/service_worker_context_wrapper.cc
+++ b/chromium/content/browser/service_worker/service_worker_context_wrapper.cc
@@ -5,13 +5,20 @@
#include "content/browser/service_worker/service_worker_context_wrapper.h"
#include "base/files/file_path.h"
+#include "base/threading/sequenced_worker_pool.h"
#include "content/browser/service_worker/service_worker_context_core.h"
+#include "content/browser/service_worker/service_worker_context_observer.h"
+#include "content/browser/service_worker/service_worker_process_manager.h"
#include "content/public/browser/browser_thread.h"
-#include "webkit/browser/quota/quota_manager.h"
+#include "webkit/browser/quota/quota_manager_proxy.h"
namespace content {
-ServiceWorkerContextWrapper::ServiceWorkerContextWrapper() {
+ServiceWorkerContextWrapper::ServiceWorkerContextWrapper(
+ BrowserContext* browser_context)
+ : observer_list_(
+ new ObserverListThreadSafe<ServiceWorkerContextObserver>()),
+ process_manager_(new ServiceWorkerProcessManager(browser_context)) {
}
ServiceWorkerContextWrapper::~ServiceWorkerContextWrapper() {
@@ -20,33 +27,140 @@ ServiceWorkerContextWrapper::~ServiceWorkerContextWrapper() {
void ServiceWorkerContextWrapper::Init(
const base::FilePath& user_data_directory,
quota::QuotaManagerProxy* quota_manager_proxy) {
+ scoped_refptr<base::SequencedTaskRunner> database_task_runner =
+ BrowserThread::GetBlockingPool()->
+ GetSequencedTaskRunnerWithShutdownBehavior(
+ BrowserThread::GetBlockingPool()->GetSequenceToken(),
+ base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
+ scoped_refptr<base::MessageLoopProxy> disk_cache_thread =
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE);
+ InitInternal(user_data_directory, database_task_runner,
+ disk_cache_thread, quota_manager_proxy);
+}
+
+void ServiceWorkerContextWrapper::Shutdown() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ process_manager_->Shutdown();
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&ServiceWorkerContextWrapper::ShutdownOnIO, this));
+}
+
+ServiceWorkerContextCore* ServiceWorkerContextWrapper::context() {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ return context_core_.get();
+}
+
+static void FinishRegistrationOnIO(
+ const ServiceWorkerContext::ResultCallback& continuation,
+ ServiceWorkerStatusCode status,
+ int64 registration_id,
+ int64 version_id) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(continuation, status == SERVICE_WORKER_OK));
+}
+
+void ServiceWorkerContextWrapper::RegisterServiceWorker(
+ const GURL& pattern,
+ const GURL& script_url,
+ const ResultCallback& continuation) {
if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::Bind(&ServiceWorkerContextWrapper::Init, this,
- user_data_directory,
- make_scoped_refptr(quota_manager_proxy)));
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&ServiceWorkerContextWrapper::RegisterServiceWorker,
+ this,
+ pattern,
+ script_url,
+ continuation));
return;
}
- DCHECK(!context_core_);
- context_core_.reset(
- new ServiceWorkerContextCore(
- user_data_directory, quota_manager_proxy));
+
+ context()->RegisterServiceWorker(
+ pattern,
+ script_url,
+ -1,
+ NULL /* provider_host */,
+ base::Bind(&FinishRegistrationOnIO, continuation));
}
-void ServiceWorkerContextWrapper::Shutdown() {
+static void FinishUnregistrationOnIO(
+ const ServiceWorkerContext::ResultCallback& continuation,
+ ServiceWorkerStatusCode status) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(continuation, status == SERVICE_WORKER_OK));
+}
+
+void ServiceWorkerContextWrapper::UnregisterServiceWorker(
+ const GURL& pattern,
+ const ResultCallback& continuation) {
if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::Bind(&ServiceWorkerContextWrapper::Shutdown, this));
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&ServiceWorkerContextWrapper::UnregisterServiceWorker,
+ this,
+ pattern,
+ continuation));
return;
}
- context_core_.reset();
+
+ context()->UnregisterServiceWorker(
+ pattern,
+ base::Bind(&FinishUnregistrationOnIO, continuation));
}
-ServiceWorkerContextCore* ServiceWorkerContextWrapper::context() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- return context_core_.get();
+void ServiceWorkerContextWrapper::Terminate() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ process_manager_->Shutdown();
+}
+
+void ServiceWorkerContextWrapper::AddObserver(
+ ServiceWorkerContextObserver* observer) {
+ observer_list_->AddObserver(observer);
+}
+
+void ServiceWorkerContextWrapper::RemoveObserver(
+ ServiceWorkerContextObserver* observer) {
+ observer_list_->RemoveObserver(observer);
+}
+
+void ServiceWorkerContextWrapper::InitInternal(
+ const base::FilePath& user_data_directory,
+ base::SequencedTaskRunner* database_task_runner,
+ base::MessageLoopProxy* disk_cache_thread,
+ quota::QuotaManagerProxy* quota_manager_proxy) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&ServiceWorkerContextWrapper::InitInternal,
+ this,
+ user_data_directory,
+ make_scoped_refptr(database_task_runner),
+ make_scoped_refptr(disk_cache_thread),
+ make_scoped_refptr(quota_manager_proxy)));
+ return;
+ }
+ DCHECK(!context_core_);
+ context_core_.reset(new ServiceWorkerContextCore(user_data_directory,
+ database_task_runner,
+ disk_cache_thread,
+ quota_manager_proxy,
+ observer_list_,
+ this));
+}
+
+void ServiceWorkerContextWrapper::ShutdownOnIO() {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ context_core_.reset();
}
} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_context_wrapper.h b/chromium/content/browser/service_worker/service_worker_context_wrapper.h
index 1b9b6523907..029af6f54ca 100644
--- a/chromium/content/browser/service_worker/service_worker_context_wrapper.h
+++ b/chromium/content/browser/service_worker/service_worker_context_wrapper.h
@@ -5,14 +5,19 @@
#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CONTEXT_WRAPPER_H_
#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CONTEXT_WRAPPER_H_
+#include <vector>
+
#include "base/files/file_path.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
-#include "content/browser/service_worker/service_worker_context.h"
+#include "content/browser/service_worker/service_worker_context_core.h"
#include "content/common/content_export.h"
+#include "content/public/browser/service_worker_context.h"
namespace base {
class FilePath;
+class MessageLoopProxy;
+class SequencedTaskRunner;
}
namespace quota {
@@ -21,7 +26,9 @@ class QuotaManagerProxy;
namespace content {
+class BrowserContext;
class ServiceWorkerContextCore;
+class ServiceWorkerContextObserver;
// A refcounted wrapper class for our core object. Higher level content lib
// classes keep references to this class on mutliple threads. The inner core
@@ -31,7 +38,7 @@ class CONTENT_EXPORT ServiceWorkerContextWrapper
: NON_EXPORTED_BASE(public ServiceWorkerContext),
public base::RefCountedThreadSafe<ServiceWorkerContextWrapper> {
public:
- ServiceWorkerContextWrapper();
+ ServiceWorkerContextWrapper(BrowserContext* browser_context);
// Init and Shutdown are for use on the UI thread when the profile,
// storagepartition is being setup and torn down.
@@ -42,10 +49,40 @@ class CONTENT_EXPORT ServiceWorkerContextWrapper
// The core context is only for use on the IO thread.
ServiceWorkerContextCore* context();
+ // The process manager can be used on either UI or IO.
+ ServiceWorkerProcessManager* process_manager() {
+ return process_manager_.get();
+ }
+
+ // ServiceWorkerContext implementation:
+ virtual void RegisterServiceWorker(
+ const GURL& pattern,
+ const GURL& script_url,
+ const ResultCallback& continuation) OVERRIDE;
+ virtual void UnregisterServiceWorker(const GURL& pattern,
+ const ResultCallback& continuation)
+ OVERRIDE;
+ virtual void Terminate() OVERRIDE;
+
+ void AddObserver(ServiceWorkerContextObserver* observer);
+ void RemoveObserver(ServiceWorkerContextObserver* observer);
+
private:
friend class base::RefCountedThreadSafe<ServiceWorkerContextWrapper>;
+ friend class EmbeddedWorkerTestHelper;
+ friend class ServiceWorkerProcessManager;
virtual ~ServiceWorkerContextWrapper();
+ void InitInternal(const base::FilePath& user_data_directory,
+ base::SequencedTaskRunner* database_task_runner,
+ base::MessageLoopProxy* disk_cache_thread,
+ quota::QuotaManagerProxy* quota_manager_proxy);
+ void ShutdownOnIO();
+
+ const scoped_refptr<ObserverListThreadSafe<ServiceWorkerContextObserver> >
+ observer_list_;
+ const scoped_ptr<ServiceWorkerProcessManager> process_manager_;
+ // Cleared in Shutdown():
scoped_ptr<ServiceWorkerContextCore> context_core_;
};
diff --git a/chromium/content/browser/service_worker/service_worker_controllee_request_handler.cc b/chromium/content/browser/service_worker/service_worker_controllee_request_handler.cc
new file mode 100644
index 00000000000..14a707b62e8
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_controllee_request_handler.cc
@@ -0,0 +1,121 @@
+// 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/service_worker/service_worker_controllee_request_handler.h"
+
+#include "content/browser/service_worker/service_worker_context_core.h"
+#include "content/browser/service_worker/service_worker_provider_host.h"
+#include "content/browser/service_worker/service_worker_registration.h"
+#include "content/browser/service_worker/service_worker_url_request_job.h"
+#include "content/browser/service_worker/service_worker_utils.h"
+#include "content/common/service_worker/service_worker_types.h"
+#include "net/base/net_util.h"
+#include "net/url_request/url_request.h"
+
+namespace content {
+
+ServiceWorkerControlleeRequestHandler::ServiceWorkerControlleeRequestHandler(
+ base::WeakPtr<ServiceWorkerContextCore> context,
+ base::WeakPtr<ServiceWorkerProviderHost> provider_host,
+ base::WeakPtr<webkit_blob::BlobStorageContext> blob_storage_context,
+ ResourceType::Type resource_type)
+ : ServiceWorkerRequestHandler(context,
+ provider_host,
+ blob_storage_context,
+ resource_type),
+ weak_factory_(this) {
+}
+
+ServiceWorkerControlleeRequestHandler::
+ ~ServiceWorkerControlleeRequestHandler() {
+}
+
+net::URLRequestJob* ServiceWorkerControlleeRequestHandler::MaybeCreateJob(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) {
+ if (!context_ || !provider_host_) {
+ // We can't do anything other than to fall back to network.
+ job_ = NULL;
+ return NULL;
+ }
+
+ // This may get called multiple times for original and redirect requests:
+ // A. original request case: job_ is null, no previous location info.
+ // B. redirect or restarted request case:
+ // a) job_ is non-null if the previous location was forwarded to SW.
+ // b) job_ is null if the previous location was fallback.
+ // c) job_ is non-null if additional restart was required to fall back.
+
+ // We've come here by restart, we already have original request and it
+ // tells we should fallback to network. (Case B-c)
+ if (job_.get() && job_->ShouldFallbackToNetwork()) {
+ job_ = NULL;
+ return NULL;
+ }
+
+ // It's for original request (A) or redirect case (B-a or B-b).
+ DCHECK(!job_.get() || job_->ShouldForwardToServiceWorker());
+
+ job_ = new ServiceWorkerURLRequestJob(
+ request, network_delegate, provider_host_, blob_storage_context_);
+ if (ServiceWorkerUtils::IsMainResourceType(resource_type_))
+ PrepareForMainResource(request->url());
+ else
+ PrepareForSubResource();
+
+ if (job_->ShouldFallbackToNetwork()) {
+ // If we know we can fallback to network at this point (in case
+ // the storage lookup returned immediately), just return NULL here to
+ // fallback to network.
+ job_ = NULL;
+ return NULL;
+ }
+
+ return job_.get();
+}
+
+void ServiceWorkerControlleeRequestHandler::PrepareForMainResource(
+ const GURL& url) {
+ DCHECK(job_.get());
+ DCHECK(context_);
+ // The corresponding provider_host may already have associate version in
+ // redirect case, unassociate it now.
+ provider_host_->SetActiveVersion(NULL);
+ provider_host_->SetWaitingVersion(NULL);
+
+ GURL stripped_url = net::SimplifyUrlForRequest(url);
+ provider_host_->SetDocumentUrl(stripped_url);
+ context_->storage()->FindRegistrationForDocument(
+ stripped_url,
+ base::Bind(&self::DidLookupRegistrationForMainResource,
+ weak_factory_.GetWeakPtr()));
+}
+
+void
+ServiceWorkerControlleeRequestHandler::DidLookupRegistrationForMainResource(
+ ServiceWorkerStatusCode status,
+ const scoped_refptr<ServiceWorkerRegistration>& registration) {
+ DCHECK(job_.get());
+ if (status != SERVICE_WORKER_OK || !registration->active_version()) {
+ // No registration, or no active version for the registration is available.
+ job_->FallbackToNetwork();
+ return;
+ }
+ // TODO(michaeln): should SetWaitingVersion() even if no active version so
+ // so the versions in the pipeline (.installing, .waiting) show up in the
+ // attribute values.
+ DCHECK(registration);
+ provider_host_->SetActiveVersion(registration->active_version());
+ provider_host_->SetWaitingVersion(registration->waiting_version());
+ job_->ForwardToServiceWorker();
+}
+
+void ServiceWorkerControlleeRequestHandler::PrepareForSubResource() {
+ DCHECK(job_.get());
+ DCHECK(context_);
+ DCHECK(provider_host_->active_version());
+ job_->ForwardToServiceWorker();
+}
+
+} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_controllee_request_handler.h b/chromium/content/browser/service_worker/service_worker_controllee_request_handler.h
new file mode 100644
index 00000000000..87227cb32dd
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_controllee_request_handler.h
@@ -0,0 +1,57 @@
+// 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_SERVICE_WORKER_SERVICE_WORKER_CONTROLLEE_REQUEST_HANDLER_H_
+#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CONTROLLEE_REQUEST_HANDLER_H_
+
+#include "content/browser/service_worker/service_worker_request_handler.h"
+
+namespace net {
+class NetworkDelegate;
+class URLRequest;
+}
+
+namespace content {
+
+class ServiceWorkerRegistration;
+class ServiceWorkerURLRequestJob;
+
+// A request handler derivative used to handle requests from
+// controlled documents.
+class CONTENT_EXPORT ServiceWorkerControlleeRequestHandler
+ : public ServiceWorkerRequestHandler {
+ public:
+ ServiceWorkerControlleeRequestHandler(
+ base::WeakPtr<ServiceWorkerContextCore> context,
+ base::WeakPtr<ServiceWorkerProviderHost> provider_host,
+ base::WeakPtr<webkit_blob::BlobStorageContext> blob_storage_context,
+ ResourceType::Type resource_type);
+ virtual ~ServiceWorkerControlleeRequestHandler();
+
+ // Called via custom URLRequestJobFactory.
+ virtual net::URLRequestJob* MaybeCreateJob(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) OVERRIDE;
+
+ private:
+ typedef ServiceWorkerControlleeRequestHandler self;
+
+ // For main resource case.
+ void PrepareForMainResource(const GURL& url);
+ void DidLookupRegistrationForMainResource(
+ ServiceWorkerStatusCode status,
+ const scoped_refptr<ServiceWorkerRegistration>& registration);
+
+ // For sub resource case.
+ void PrepareForSubResource();
+
+ scoped_refptr<ServiceWorkerURLRequestJob> job_;
+ base::WeakPtrFactory<ServiceWorkerControlleeRequestHandler> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServiceWorkerControlleeRequestHandler);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CONTROLLEE_REQUEST_HANDLER_H_
diff --git a/chromium/content/browser/service_worker/service_worker_database.cc b/chromium/content/browser/service_worker/service_worker_database.cc
new file mode 100644
index 00000000000..1058acc6165
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_database.cc
@@ -0,0 +1,1113 @@
+// 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/service_worker/service_worker_database.h"
+
+#include <string>
+
+#include "base/file_util.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "content/browser/service_worker/service_worker_database.pb.h"
+#include "content/common/service_worker/service_worker_types.h"
+#include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
+#include "third_party/leveldatabase/src/include/leveldb/db.h"
+#include "third_party/leveldatabase/src/include/leveldb/env.h"
+#include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
+
+// LevelDB database schema
+// =======================
+//
+// NOTE
+// - int64 value is serialized as a string by base::Int64ToString().
+// - GURL value is serialized as a string by GURL::spec().
+//
+// Version 1 (in sorted order)
+// key: "INITDATA_DB_VERSION"
+// value: "1"
+//
+// key: "INITDATA_NEXT_REGISTRATION_ID"
+// value: <int64 'next_available_registration_id'>
+//
+// key: "INITDATA_NEXT_RESOURCE_ID"
+// value: <int64 'next_available_resource_id'>
+//
+// key: "INITDATA_NEXT_VERSION_ID"
+// value: <int64 'next_available_version_id'>
+//
+// key: "INITDATA_UNIQUE_ORIGIN:" + <GURL 'origin'>
+// value: <empty>
+//
+// key: "PRES:" + <int64 'purgeable_resource_id'>
+// value: <empty>
+//
+// key: "REG:" + <GURL 'origin'> + '\x00' + <int64 'registration_id'>
+// (ex. "REG:http://example.com\x00123456")
+// value: <ServiceWorkerRegistrationData serialized as a string>
+//
+// key: "RES:" + <int64 'version_id'> + '\x00' + <int64 'resource_id'>
+// (ex. "RES:123456\x00654321")
+// value: <ServiceWorkerResourceRecord serialized as a string>
+//
+// key: "URES:" + <int64 'uncommitted_resource_id'>
+// value: <empty>
+
+namespace content {
+
+namespace {
+
+const char kDatabaseVersionKey[] = "INITDATA_DB_VERSION";
+const char kNextRegIdKey[] = "INITDATA_NEXT_REGISTRATION_ID";
+const char kNextResIdKey[] = "INITDATA_NEXT_RESOURCE_ID";
+const char kNextVerIdKey[] = "INITDATA_NEXT_VERSION_ID";
+const char kUniqueOriginKey[] = "INITDATA_UNIQUE_ORIGIN:";
+
+const char kRegKeyPrefix[] = "REG:";
+const char kResKeyPrefix[] = "RES:";
+const char kKeySeparator = '\x00';
+
+const char kUncommittedResIdKeyPrefix[] = "URES:";
+const char kPurgeableResIdKeyPrefix[] = "PRES:";
+
+const int64 kCurrentSchemaVersion = 1;
+
+// For histogram.
+const char kOpenResultHistogramLabel[] =
+ "ServiceWorker.Database.OpenResult";
+const char kReadResultHistogramLabel[] =
+ "ServiceWorker.Database.ReadResult";
+const char kWriteResultHistogramLabel[] =
+ "ServiceWorker.Database.WriteResult";
+
+bool RemovePrefix(const std::string& str,
+ const std::string& prefix,
+ std::string* out) {
+ if (!StartsWithASCII(str, prefix, true))
+ return false;
+ if (out)
+ *out = str.substr(prefix.size());
+ return true;
+}
+
+std::string CreateRegistrationKey(int64 registration_id,
+ const GURL& origin) {
+ return base::StringPrintf("%s%s%c%s",
+ kRegKeyPrefix,
+ origin.spec().c_str(),
+ kKeySeparator,
+ base::Int64ToString(registration_id).c_str());
+}
+
+std::string CreateResourceRecordKeyPrefix(int64 version_id) {
+ return base::StringPrintf("%s%s%c",
+ kResKeyPrefix,
+ base::Int64ToString(version_id).c_str(),
+ kKeySeparator);
+}
+
+std::string CreateResourceRecordKey(int64 version_id,
+ int64 resource_id) {
+ return CreateResourceRecordKeyPrefix(version_id).append(
+ base::Int64ToString(resource_id));
+}
+
+std::string CreateUniqueOriginKey(const GURL& origin) {
+ return base::StringPrintf("%s%s", kUniqueOriginKey, origin.spec().c_str());
+}
+
+std::string CreateResourceIdKey(const char* key_prefix, int64 resource_id) {
+ return base::StringPrintf(
+ "%s%s", key_prefix, base::Int64ToString(resource_id).c_str());
+}
+
+void PutRegistrationDataToBatch(
+ const ServiceWorkerDatabase::RegistrationData& input,
+ leveldb::WriteBatch* batch) {
+ DCHECK(batch);
+
+ // Convert RegistrationData to ServiceWorkerRegistrationData.
+ ServiceWorkerRegistrationData data;
+ data.set_registration_id(input.registration_id);
+ data.set_scope_url(input.scope.spec());
+ data.set_script_url(input.script.spec());
+ data.set_version_id(input.version_id);
+ data.set_is_active(input.is_active);
+ data.set_has_fetch_handler(input.has_fetch_handler);
+ data.set_last_update_check_time(input.last_update_check.ToInternalValue());
+
+ std::string value;
+ bool success = data.SerializeToString(&value);
+ DCHECK(success);
+ GURL origin = input.scope.GetOrigin();
+ batch->Put(CreateRegistrationKey(data.registration_id(), origin), value);
+}
+
+void PutResourceRecordToBatch(
+ const ServiceWorkerDatabase::ResourceRecord& input,
+ int64 version_id,
+ leveldb::WriteBatch* batch) {
+ DCHECK(batch);
+
+ // Convert ResourceRecord to ServiceWorkerResourceRecord.
+ ServiceWorkerResourceRecord record;
+ record.set_resource_id(input.resource_id);
+ record.set_url(input.url.spec());
+
+ std::string value;
+ bool success = record.SerializeToString(&value);
+ DCHECK(success);
+ batch->Put(CreateResourceRecordKey(version_id, input.resource_id), value);
+}
+
+void PutUniqueOriginToBatch(const GURL& origin,
+ leveldb::WriteBatch* batch) {
+ // Value should be empty.
+ batch->Put(CreateUniqueOriginKey(origin), "");
+}
+
+void PutPurgeableResourceIdToBatch(int64 resource_id,
+ leveldb::WriteBatch* batch) {
+ // Value should be empty.
+ batch->Put(CreateResourceIdKey(kPurgeableResIdKeyPrefix, resource_id), "");
+}
+
+ServiceWorkerDatabase::Status ParseId(
+ const std::string& serialized,
+ int64* out) {
+ DCHECK(out);
+ int64 id;
+ if (!base::StringToInt64(serialized, &id) || id < 0)
+ return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
+ *out = id;
+ return ServiceWorkerDatabase::STATUS_OK;
+}
+
+ServiceWorkerDatabase::Status ParseDatabaseVersion(
+ const std::string& serialized,
+ int64* out) {
+ DCHECK(out);
+ const int kFirstValidVersion = 1;
+ int64 version;
+ if (!base::StringToInt64(serialized, &version) ||
+ version < kFirstValidVersion) {
+ return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
+ }
+ if (kCurrentSchemaVersion < version) {
+ DLOG(ERROR) << "ServiceWorkerDatabase has newer schema version"
+ << " than the current latest version: "
+ << version << " vs " << kCurrentSchemaVersion;
+ return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
+ }
+ *out = version;
+ return ServiceWorkerDatabase::STATUS_OK;
+}
+
+ServiceWorkerDatabase::Status ParseRegistrationData(
+ const std::string& serialized,
+ ServiceWorkerDatabase::RegistrationData* out) {
+ DCHECK(out);
+ ServiceWorkerRegistrationData data;
+ if (!data.ParseFromString(serialized))
+ return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
+
+ GURL scope_url(data.scope_url());
+ GURL script_url(data.script_url());
+ if (!scope_url.is_valid() ||
+ !script_url.is_valid() ||
+ scope_url.GetOrigin() != script_url.GetOrigin()) {
+ return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
+ }
+
+ // Convert ServiceWorkerRegistrationData to RegistrationData.
+ out->registration_id = data.registration_id();
+ out->scope = scope_url;
+ out->script = script_url;
+ out->version_id = data.version_id();
+ out->is_active = data.is_active();
+ out->has_fetch_handler = data.has_fetch_handler();
+ out->last_update_check =
+ base::Time::FromInternalValue(data.last_update_check_time());
+ return ServiceWorkerDatabase::STATUS_OK;
+}
+
+ServiceWorkerDatabase::Status ParseResourceRecord(
+ const std::string& serialized,
+ ServiceWorkerDatabase::ResourceRecord* out) {
+ DCHECK(out);
+ ServiceWorkerResourceRecord record;
+ if (!record.ParseFromString(serialized))
+ return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
+
+ GURL url(record.url());
+ if (!url.is_valid())
+ return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
+
+ // Convert ServiceWorkerResourceRecord to ResourceRecord.
+ out->resource_id = record.resource_id();
+ out->url = url;
+ return ServiceWorkerDatabase::STATUS_OK;
+}
+
+ServiceWorkerDatabase::Status LevelDBStatusToStatus(
+ const leveldb::Status& status) {
+ if (status.ok())
+ return ServiceWorkerDatabase::STATUS_OK;
+ else if (status.IsNotFound())
+ return ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
+ else if (status.IsIOError())
+ return ServiceWorkerDatabase::STATUS_ERROR_IO_ERROR;
+ else if (status.IsCorruption())
+ return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
+ else
+ return ServiceWorkerDatabase::STATUS_ERROR_FAILED;
+}
+
+const char* StatusToString(ServiceWorkerDatabase::Status status) {
+ switch (status) {
+ case ServiceWorkerDatabase::STATUS_OK:
+ return "Database OK";
+ case ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND:
+ return "Database not found";
+ case ServiceWorkerDatabase::STATUS_ERROR_IO_ERROR:
+ return "Database IO error";
+ case ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED:
+ return "Database corrupted";
+ case ServiceWorkerDatabase::STATUS_ERROR_FAILED:
+ return "Database operation failed";
+ case ServiceWorkerDatabase::STATUS_ERROR_MAX:
+ NOTREACHED();
+ return "Database unknown error";
+ }
+ NOTREACHED();
+ return "Database unknown error";
+}
+
+} // namespace
+
+ServiceWorkerDatabase::RegistrationData::RegistrationData()
+ : registration_id(kInvalidServiceWorkerRegistrationId),
+ version_id(kInvalidServiceWorkerVersionId),
+ is_active(false),
+ has_fetch_handler(false) {
+}
+
+ServiceWorkerDatabase::RegistrationData::~RegistrationData() {
+}
+
+ServiceWorkerDatabase::ServiceWorkerDatabase(const base::FilePath& path)
+ : path_(path),
+ next_avail_registration_id_(0),
+ next_avail_resource_id_(0),
+ next_avail_version_id_(0),
+ state_(UNINITIALIZED) {
+ sequence_checker_.DetachFromSequence();
+}
+
+ServiceWorkerDatabase::~ServiceWorkerDatabase() {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ db_.reset();
+}
+
+ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetNextAvailableIds(
+ int64* next_avail_registration_id,
+ int64* next_avail_version_id,
+ int64* next_avail_resource_id) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ DCHECK(next_avail_registration_id);
+ DCHECK(next_avail_version_id);
+ DCHECK(next_avail_resource_id);
+
+ Status status = LazyOpen(false);
+ if (IsNewOrNonexistentDatabase(status)) {
+ *next_avail_registration_id = 0;
+ *next_avail_version_id = 0;
+ *next_avail_resource_id = 0;
+ return STATUS_OK;
+ }
+ if (status != STATUS_OK)
+ return status;
+
+ status = ReadNextAvailableId(kNextRegIdKey, &next_avail_registration_id_);
+ if (status != STATUS_OK)
+ return status;
+ status = ReadNextAvailableId(kNextVerIdKey, &next_avail_version_id_);
+ if (status != STATUS_OK)
+ return status;
+ status = ReadNextAvailableId(kNextResIdKey, &next_avail_resource_id_);
+ if (status != STATUS_OK)
+ return status;
+
+ *next_avail_registration_id = next_avail_registration_id_;
+ *next_avail_version_id = next_avail_version_id_;
+ *next_avail_resource_id = next_avail_resource_id_;
+ return STATUS_OK;
+}
+
+ServiceWorkerDatabase::Status
+ServiceWorkerDatabase::GetOriginsWithRegistrations(std::set<GURL>* origins) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ DCHECK(origins->empty());
+
+ Status status = LazyOpen(false);
+ if (IsNewOrNonexistentDatabase(status))
+ return STATUS_OK;
+ if (status != STATUS_OK)
+ return status;
+
+ scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
+ for (itr->Seek(kUniqueOriginKey); itr->Valid(); itr->Next()) {
+ status = LevelDBStatusToStatus(itr->status());
+ if (status != STATUS_OK) {
+ HandleReadResult(FROM_HERE, status);
+ origins->clear();
+ return status;
+ }
+
+ std::string origin;
+ if (!RemovePrefix(itr->key().ToString(), kUniqueOriginKey, &origin))
+ break;
+ origins->insert(GURL(origin));
+ }
+
+ HandleReadResult(FROM_HERE, status);
+ return status;
+}
+
+ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetRegistrationsForOrigin(
+ const GURL& origin,
+ std::vector<RegistrationData>* registrations) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ DCHECK(registrations->empty());
+
+ Status status = LazyOpen(false);
+ if (IsNewOrNonexistentDatabase(status))
+ return STATUS_OK;
+ if (status != STATUS_OK)
+ return status;
+
+ // Create a key prefix for registrations.
+ std::string prefix = base::StringPrintf(
+ "%s%s%c", kRegKeyPrefix, origin.spec().c_str(), kKeySeparator);
+
+ scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
+ for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
+ status = LevelDBStatusToStatus(itr->status());
+ if (status != STATUS_OK) {
+ HandleReadResult(FROM_HERE, status);
+ registrations->clear();
+ return status;
+ }
+
+ if (!RemovePrefix(itr->key().ToString(), prefix, NULL))
+ break;
+
+ RegistrationData registration;
+ status = ParseRegistrationData(itr->value().ToString(), &registration);
+ if (status != STATUS_OK) {
+ HandleReadResult(FROM_HERE, status);
+ registrations->clear();
+ return status;
+ }
+ registrations->push_back(registration);
+ }
+
+ HandleReadResult(FROM_HERE, status);
+ return status;
+}
+
+ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetAllRegistrations(
+ std::vector<RegistrationData>* registrations) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ DCHECK(registrations->empty());
+
+ Status status = LazyOpen(false);
+ if (IsNewOrNonexistentDatabase(status))
+ return STATUS_OK;
+ if (status != STATUS_OK)
+ return status;
+
+ scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
+ for (itr->Seek(kRegKeyPrefix); itr->Valid(); itr->Next()) {
+ status = LevelDBStatusToStatus(itr->status());
+ if (status != STATUS_OK) {
+ HandleReadResult(FROM_HERE, status);
+ registrations->clear();
+ return status;
+ }
+
+ if (!RemovePrefix(itr->key().ToString(), kRegKeyPrefix, NULL))
+ break;
+
+ RegistrationData registration;
+ status = ParseRegistrationData(itr->value().ToString(), &registration);
+ if (status != STATUS_OK) {
+ HandleReadResult(FROM_HERE, status);
+ registrations->clear();
+ return status;
+ }
+ registrations->push_back(registration);
+ }
+
+ HandleReadResult(FROM_HERE, status);
+ return status;
+}
+
+ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistration(
+ int64 registration_id,
+ const GURL& origin,
+ RegistrationData* registration,
+ std::vector<ResourceRecord>* resources) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ DCHECK(registration);
+ DCHECK(resources);
+
+ Status status = LazyOpen(false);
+ if (IsNewOrNonexistentDatabase(status) || status != STATUS_OK)
+ return status;
+
+ RegistrationData value;
+ status = ReadRegistrationData(registration_id, origin, &value);
+ if (status != STATUS_OK)
+ return status;
+
+ status = ReadResourceRecords(value.version_id, resources);
+ if (status != STATUS_OK)
+ return status;
+
+ *registration = value;
+ return STATUS_OK;
+}
+
+ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteRegistration(
+ const RegistrationData& registration,
+ const std::vector<ResourceRecord>& resources,
+ std::vector<int64>* newly_purgeable_resources) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ Status status = LazyOpen(true);
+ if (status != STATUS_OK)
+ return status;
+
+ leveldb::WriteBatch batch;
+ BumpNextRegistrationIdIfNeeded(registration.registration_id, &batch);
+ BumpNextVersionIdIfNeeded(registration.version_id, &batch);
+
+ PutUniqueOriginToBatch(registration.scope.GetOrigin(), &batch);
+ PutRegistrationDataToBatch(registration, &batch);
+
+ // Used for avoiding multiple writes for the same resource id or url.
+ std::set<int64> pushed_resources;
+ std::set<GURL> pushed_urls;
+ for (std::vector<ResourceRecord>::const_iterator itr = resources.begin();
+ itr != resources.end(); ++itr) {
+ if (!itr->url.is_valid())
+ return STATUS_ERROR_FAILED;
+
+ // Duplicated resource id or url should not exist.
+ DCHECK(pushed_resources.insert(itr->resource_id).second);
+ DCHECK(pushed_urls.insert(itr->url).second);
+
+ PutResourceRecordToBatch(*itr, registration.version_id, &batch);
+
+ // Delete a resource from the uncommitted list.
+ batch.Delete(CreateResourceIdKey(
+ kUncommittedResIdKeyPrefix, itr->resource_id));
+ }
+
+ // Retrieve a previous version to sweep purgeable resources.
+ RegistrationData old_registration;
+ status = ReadRegistrationData(registration.registration_id,
+ registration.scope.GetOrigin(),
+ &old_registration);
+ if (status != STATUS_OK && status != STATUS_ERROR_NOT_FOUND)
+ return status;
+ if (status == STATUS_OK) {
+ DCHECK_LT(old_registration.version_id, registration.version_id);
+ status = DeleteResourceRecords(
+ old_registration.version_id, newly_purgeable_resources, &batch);
+ if (status != STATUS_OK)
+ return status;
+
+ // Currently resource sharing across versions and registrations is not
+ // supported, so resource ids should not be overlapped between
+ // |registration| and |old_registration|.
+ std::set<int64> deleted_resources(newly_purgeable_resources->begin(),
+ newly_purgeable_resources->end());
+ DCHECK(base::STLSetIntersection<std::set<int64> >(
+ pushed_resources, deleted_resources).empty());
+ }
+
+ return WriteBatch(&batch);
+}
+
+ServiceWorkerDatabase::Status ServiceWorkerDatabase::UpdateVersionToActive(
+ int64 registration_id,
+ const GURL& origin) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ ServiceWorkerDatabase::Status status = LazyOpen(false);
+ if (IsNewOrNonexistentDatabase(status))
+ return STATUS_ERROR_NOT_FOUND;
+ if (status != STATUS_OK)
+ return status;
+ if (!origin.is_valid())
+ return STATUS_ERROR_FAILED;
+
+ RegistrationData registration;
+ status = ReadRegistrationData(registration_id, origin, &registration);
+ if (status != STATUS_OK)
+ return status;
+
+ registration.is_active = true;
+
+ leveldb::WriteBatch batch;
+ PutRegistrationDataToBatch(registration, &batch);
+ return WriteBatch(&batch);
+}
+
+ServiceWorkerDatabase::Status ServiceWorkerDatabase::UpdateLastCheckTime(
+ int64 registration_id,
+ const GURL& origin,
+ const base::Time& time) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ ServiceWorkerDatabase::Status status = LazyOpen(false);
+ if (IsNewOrNonexistentDatabase(status))
+ return STATUS_ERROR_NOT_FOUND;
+ if (status != STATUS_OK)
+ return status;
+ if (!origin.is_valid())
+ return STATUS_ERROR_FAILED;
+
+ RegistrationData registration;
+ status = ReadRegistrationData(registration_id, origin, &registration);
+ if (status != STATUS_OK)
+ return status;
+
+ registration.last_update_check = time;
+
+ leveldb::WriteBatch batch;
+ PutRegistrationDataToBatch(registration, &batch);
+ return WriteBatch(&batch);
+}
+
+ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteRegistration(
+ int64 registration_id,
+ const GURL& origin,
+ std::vector<int64>* newly_purgeable_resources) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ Status status = LazyOpen(false);
+ if (IsNewOrNonexistentDatabase(status))
+ return STATUS_OK;
+ if (status != STATUS_OK)
+ return status;
+ if (!origin.is_valid())
+ return STATUS_ERROR_FAILED;
+
+ leveldb::WriteBatch batch;
+
+ // Remove |origin| from unique origins if a registration specified by
+ // |registration_id| is the only one for |origin|.
+ // TODO(nhiroki): Check the uniqueness by more efficient way.
+ std::vector<RegistrationData> registrations;
+ status = GetRegistrationsForOrigin(origin, &registrations);
+ if (status != STATUS_OK)
+ return status;
+
+ if (registrations.size() == 1 &&
+ registrations[0].registration_id == registration_id) {
+ batch.Delete(CreateUniqueOriginKey(origin));
+ }
+
+ // Delete a registration specified by |registration_id|.
+ batch.Delete(CreateRegistrationKey(registration_id, origin));
+
+ // Delete resource records associated with the registration.
+ for (std::vector<RegistrationData>::const_iterator itr =
+ registrations.begin(); itr != registrations.end(); ++itr) {
+ if (itr->registration_id == registration_id) {
+ status = DeleteResourceRecords(
+ itr->version_id, newly_purgeable_resources, &batch);
+ if (status != STATUS_OK)
+ return status;
+ break;
+ }
+ }
+
+ return WriteBatch(&batch);
+}
+
+ServiceWorkerDatabase::Status
+ServiceWorkerDatabase::GetUncommittedResourceIds(std::set<int64>* ids) {
+ return ReadResourceIds(kUncommittedResIdKeyPrefix, ids);
+}
+
+ServiceWorkerDatabase::Status
+ServiceWorkerDatabase::WriteUncommittedResourceIds(const std::set<int64>& ids) {
+ return WriteResourceIds(kUncommittedResIdKeyPrefix, ids);
+}
+
+ServiceWorkerDatabase::Status
+ServiceWorkerDatabase::ClearUncommittedResourceIds(const std::set<int64>& ids) {
+ return DeleteResourceIds(kUncommittedResIdKeyPrefix, ids);
+}
+
+ServiceWorkerDatabase::Status
+ServiceWorkerDatabase::GetPurgeableResourceIds(std::set<int64>* ids) {
+ return ReadResourceIds(kPurgeableResIdKeyPrefix, ids);
+}
+
+ServiceWorkerDatabase::Status
+ServiceWorkerDatabase::WritePurgeableResourceIds(const std::set<int64>& ids) {
+ return WriteResourceIds(kPurgeableResIdKeyPrefix, ids);
+}
+
+ServiceWorkerDatabase::Status
+ServiceWorkerDatabase::ClearPurgeableResourceIds(const std::set<int64>& ids) {
+ return DeleteResourceIds(kPurgeableResIdKeyPrefix, ids);
+}
+
+ServiceWorkerDatabase::Status
+ServiceWorkerDatabase::PurgeUncommittedResourceIds(
+ const std::set<int64>& ids) {
+ leveldb::WriteBatch batch;
+ Status status = DeleteResourceIdsInBatch(
+ kUncommittedResIdKeyPrefix, ids, &batch);
+ if (status != STATUS_OK)
+ return status;
+ status = WriteResourceIdsInBatch(kPurgeableResIdKeyPrefix, ids, &batch);
+ if (status != STATUS_OK)
+ return status;
+ return WriteBatch(&batch);
+}
+
+ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteAllDataForOrigin(
+ const GURL& origin,
+ std::vector<int64>* newly_purgeable_resources) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ Status status = LazyOpen(false);
+ if (IsNewOrNonexistentDatabase(status))
+ return STATUS_OK;
+ if (status != STATUS_OK)
+ return status;
+ if (!origin.is_valid())
+ return STATUS_ERROR_FAILED;
+
+ leveldb::WriteBatch batch;
+
+ // Delete from the unique origin list.
+ batch.Delete(CreateUniqueOriginKey(origin));
+
+ std::vector<RegistrationData> registrations;
+ status = GetRegistrationsForOrigin(origin, &registrations);
+ if (status != STATUS_OK)
+ return status;
+
+ // Delete registrations and resource records.
+ for (std::vector<RegistrationData>::const_iterator itr =
+ registrations.begin(); itr != registrations.end(); ++itr) {
+ batch.Delete(CreateRegistrationKey(itr->registration_id, origin));
+ status = DeleteResourceRecords(
+ itr->version_id, newly_purgeable_resources, &batch);
+ if (status != STATUS_OK)
+ return status;
+ }
+
+ return WriteBatch(&batch);
+}
+
+ServiceWorkerDatabase::Status ServiceWorkerDatabase::DestroyDatabase() {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ Disable(FROM_HERE, STATUS_OK);
+ return LevelDBStatusToStatus(
+ leveldb::DestroyDB(path_.AsUTF8Unsafe(), leveldb::Options()));
+}
+
+ServiceWorkerDatabase::Status ServiceWorkerDatabase::LazyOpen(
+ bool create_if_missing) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+
+ // Do not try to open a database if we tried and failed once.
+ if (state_ == DISABLED)
+ return STATUS_ERROR_FAILED;
+ if (IsOpen())
+ return STATUS_OK;
+
+ // When |path_| is empty, open a database in-memory.
+ bool use_in_memory_db = path_.empty();
+
+ if (!create_if_missing) {
+ // Avoid opening a database if it does not exist at the |path_|.
+ if (use_in_memory_db ||
+ !base::PathExists(path_) ||
+ base::IsDirectoryEmpty(path_)) {
+ return STATUS_ERROR_NOT_FOUND;
+ }
+ }
+
+ leveldb::Options options;
+ options.create_if_missing = create_if_missing;
+ if (use_in_memory_db) {
+ env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
+ options.env = env_.get();
+ }
+
+ leveldb::DB* db = NULL;
+ Status status = LevelDBStatusToStatus(
+ leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db));
+ HandleOpenResult(FROM_HERE, status);
+ if (status != STATUS_OK) {
+ DCHECK(!db);
+ // TODO(nhiroki): Should we retry to open the database?
+ return status;
+ }
+ db_.reset(db);
+
+ int64 db_version;
+ status = ReadDatabaseVersion(&db_version);
+ if (status != STATUS_OK)
+ return status;
+ DCHECK_LE(0, db_version);
+ if (db_version > 0)
+ state_ = INITIALIZED;
+ return STATUS_OK;
+}
+
+bool ServiceWorkerDatabase::IsNewOrNonexistentDatabase(
+ ServiceWorkerDatabase::Status status) {
+ if (status == STATUS_ERROR_NOT_FOUND)
+ return true;
+ if (status == STATUS_OK && state_ == UNINITIALIZED)
+ return true;
+ return false;
+}
+
+ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadNextAvailableId(
+ const char* id_key,
+ int64* next_avail_id) {
+ DCHECK(id_key);
+ DCHECK(next_avail_id);
+
+ std::string value;
+ Status status = LevelDBStatusToStatus(
+ db_->Get(leveldb::ReadOptions(), id_key, &value));
+ if (status == STATUS_ERROR_NOT_FOUND) {
+ // Nobody has gotten the next resource id for |id_key|.
+ *next_avail_id = 0;
+ HandleReadResult(FROM_HERE, STATUS_OK);
+ return STATUS_OK;
+ } else if (status != STATUS_OK) {
+ HandleReadResult(FROM_HERE, status);
+ return status;
+ }
+
+ status = ParseId(value, next_avail_id);
+ HandleReadResult(FROM_HERE, status);
+ return status;
+}
+
+ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistrationData(
+ int64 registration_id,
+ const GURL& origin,
+ RegistrationData* registration) {
+ DCHECK(registration);
+
+ const std::string key = CreateRegistrationKey(registration_id, origin);
+ std::string value;
+ Status status = LevelDBStatusToStatus(
+ db_->Get(leveldb::ReadOptions(), key, &value));
+ if (status != STATUS_OK) {
+ HandleReadResult(
+ FROM_HERE,
+ status == STATUS_ERROR_NOT_FOUND ? STATUS_OK : status);
+ return status;
+ }
+
+ status = ParseRegistrationData(value, registration);
+ HandleReadResult(FROM_HERE, status);
+ return status;
+}
+
+ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadResourceRecords(
+ int64 version_id,
+ std::vector<ResourceRecord>* resources) {
+ DCHECK(resources->empty());
+
+ Status status = STATUS_OK;
+ const std::string prefix = CreateResourceRecordKeyPrefix(version_id);
+
+ scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
+ for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
+ Status status = LevelDBStatusToStatus(itr->status());
+ if (status != STATUS_OK) {
+ HandleReadResult(FROM_HERE, status);
+ resources->clear();
+ return status;
+ }
+
+ if (!RemovePrefix(itr->key().ToString(), prefix, NULL))
+ break;
+
+ ResourceRecord resource;
+ status = ParseResourceRecord(itr->value().ToString(), &resource);
+ if (status != STATUS_OK) {
+ HandleReadResult(FROM_HERE, status);
+ resources->clear();
+ return status;
+ }
+ resources->push_back(resource);
+ }
+
+ HandleReadResult(FROM_HERE, status);
+ return status;
+}
+
+ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceRecords(
+ int64 version_id,
+ std::vector<int64>* newly_purgeable_resources,
+ leveldb::WriteBatch* batch) {
+ DCHECK(batch);
+
+ Status status = STATUS_OK;
+ const std::string prefix = CreateResourceRecordKeyPrefix(version_id);
+
+ scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
+ for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
+ status = LevelDBStatusToStatus(itr->status());
+ if (status != STATUS_OK) {
+ HandleReadResult(FROM_HERE, status);
+ return status;
+ }
+
+ const std::string key = itr->key().ToString();
+ std::string unprefixed;
+ if (!RemovePrefix(key, prefix, &unprefixed))
+ break;
+
+ int64 resource_id;
+ status = ParseId(unprefixed, &resource_id);
+ if (status != STATUS_OK) {
+ HandleReadResult(FROM_HERE, status);
+ return status;
+ }
+
+ // Remove a resource record.
+ batch->Delete(key);
+
+ // Currently resource sharing across versions and registrations is not
+ // supported, so we can purge this without caring about it.
+ PutPurgeableResourceIdToBatch(resource_id, batch);
+ newly_purgeable_resources->push_back(resource_id);
+ }
+
+ HandleReadResult(FROM_HERE, status);
+ return status;
+}
+
+ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadResourceIds(
+ const char* id_key_prefix,
+ std::set<int64>* ids) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ DCHECK(id_key_prefix);
+ DCHECK(ids->empty());
+
+ Status status = LazyOpen(false);
+ if (IsNewOrNonexistentDatabase(status))
+ return STATUS_OK;
+ if (status != STATUS_OK)
+ return status;
+
+ scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
+ for (itr->Seek(id_key_prefix); itr->Valid(); itr->Next()) {
+ status = LevelDBStatusToStatus(itr->status());
+ if (status != STATUS_OK) {
+ HandleReadResult(FROM_HERE, status);
+ ids->clear();
+ return status;
+ }
+
+ std::string unprefixed;
+ if (!RemovePrefix(itr->key().ToString(), id_key_prefix, &unprefixed))
+ break;
+
+ int64 resource_id;
+ status = ParseId(unprefixed, &resource_id);
+ if (status != STATUS_OK) {
+ HandleReadResult(FROM_HERE, status);
+ ids->clear();
+ return status;
+ }
+ ids->insert(resource_id);
+ }
+
+ HandleReadResult(FROM_HERE, status);
+ return status;
+}
+
+ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteResourceIds(
+ const char* id_key_prefix,
+ const std::set<int64>& ids) {
+ leveldb::WriteBatch batch;
+ Status status = WriteResourceIdsInBatch(id_key_prefix, ids, &batch);
+ if (status != STATUS_OK)
+ return status;
+ return WriteBatch(&batch);
+}
+
+ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteResourceIdsInBatch(
+ const char* id_key_prefix,
+ const std::set<int64>& ids,
+ leveldb::WriteBatch* batch) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ DCHECK(id_key_prefix);
+
+ Status status = LazyOpen(true);
+ if (status != STATUS_OK)
+ return status;
+
+ for (std::set<int64>::const_iterator itr = ids.begin();
+ itr != ids.end(); ++itr) {
+ // Value should be empty.
+ batch->Put(CreateResourceIdKey(id_key_prefix, *itr), "");
+ }
+ return STATUS_OK;
+}
+
+ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceIds(
+ const char* id_key_prefix,
+ const std::set<int64>& ids) {
+ leveldb::WriteBatch batch;
+ Status status = DeleteResourceIdsInBatch(id_key_prefix, ids, &batch);
+ if (status != STATUS_OK)
+ return status;
+ return WriteBatch(&batch);
+}
+
+ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceIdsInBatch(
+ const char* id_key_prefix,
+ const std::set<int64>& ids,
+ leveldb::WriteBatch* batch) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ DCHECK(id_key_prefix);
+
+ Status status = LazyOpen(false);
+ if (IsNewOrNonexistentDatabase(status))
+ return STATUS_OK;
+ if (status != STATUS_OK)
+ return status;
+
+ for (std::set<int64>::const_iterator itr = ids.begin();
+ itr != ids.end(); ++itr) {
+ batch->Delete(CreateResourceIdKey(id_key_prefix, *itr));
+ }
+ return STATUS_OK;
+}
+
+ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadDatabaseVersion(
+ int64* db_version) {
+ std::string value;
+ Status status = LevelDBStatusToStatus(
+ db_->Get(leveldb::ReadOptions(), kDatabaseVersionKey, &value));
+ if (status == STATUS_ERROR_NOT_FOUND) {
+ // The database hasn't been initialized yet.
+ *db_version = 0;
+ HandleReadResult(FROM_HERE, STATUS_OK);
+ return STATUS_OK;
+ }
+
+ if (status != STATUS_OK) {
+ HandleReadResult(FROM_HERE, status);
+ return status;
+ }
+
+ status = ParseDatabaseVersion(value, db_version);
+ HandleReadResult(FROM_HERE, status);
+ return status;
+}
+
+ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteBatch(
+ leveldb::WriteBatch* batch) {
+ DCHECK(batch);
+ DCHECK_NE(DISABLED, state_);
+
+ if (state_ == UNINITIALIZED) {
+ // Write the database schema version.
+ batch->Put(kDatabaseVersionKey, base::Int64ToString(kCurrentSchemaVersion));
+ state_ = INITIALIZED;
+ }
+
+ Status status = LevelDBStatusToStatus(
+ db_->Write(leveldb::WriteOptions(), batch));
+ HandleWriteResult(FROM_HERE, status);
+ return status;
+}
+
+void ServiceWorkerDatabase::BumpNextRegistrationIdIfNeeded(
+ int64 used_id, leveldb::WriteBatch* batch) {
+ DCHECK(batch);
+ if (next_avail_registration_id_ <= used_id) {
+ next_avail_registration_id_ = used_id + 1;
+ batch->Put(kNextRegIdKey, base::Int64ToString(next_avail_registration_id_));
+ }
+}
+
+void ServiceWorkerDatabase::BumpNextVersionIdIfNeeded(
+ int64 used_id, leveldb::WriteBatch* batch) {
+ DCHECK(batch);
+ if (next_avail_version_id_ <= used_id) {
+ next_avail_version_id_ = used_id + 1;
+ batch->Put(kNextVerIdKey, base::Int64ToString(next_avail_version_id_));
+ }
+}
+
+bool ServiceWorkerDatabase::IsOpen() {
+ return db_ != NULL;
+}
+
+void ServiceWorkerDatabase::Disable(
+ const tracked_objects::Location& from_here,
+ Status status) {
+ if (status != STATUS_OK) {
+ DLOG(ERROR) << "Failed at: " << from_here.ToString()
+ << " with error: " << StatusToString(status);
+ DLOG(ERROR) << "ServiceWorkerDatabase is disabled.";
+ }
+ state_ = DISABLED;
+ db_.reset();
+}
+
+void ServiceWorkerDatabase::HandleOpenResult(
+ const tracked_objects::Location& from_here,
+ Status status) {
+ if (status != ServiceWorkerDatabase::STATUS_OK)
+ Disable(from_here, status);
+ UMA_HISTOGRAM_ENUMERATION(kOpenResultHistogramLabel,
+ status,
+ ServiceWorkerDatabase::STATUS_ERROR_MAX);
+}
+
+void ServiceWorkerDatabase::HandleReadResult(
+ const tracked_objects::Location& from_here,
+ Status status) {
+ if (status != ServiceWorkerDatabase::STATUS_OK)
+ Disable(from_here, status);
+ UMA_HISTOGRAM_ENUMERATION(kReadResultHistogramLabel,
+ status,
+ ServiceWorkerDatabase::STATUS_ERROR_MAX);
+}
+
+void ServiceWorkerDatabase::HandleWriteResult(
+ const tracked_objects::Location& from_here,
+ Status status) {
+ if (status != ServiceWorkerDatabase::STATUS_OK)
+ Disable(from_here, status);
+ UMA_HISTOGRAM_ENUMERATION(kWriteResultHistogramLabel,
+ status,
+ ServiceWorkerDatabase::STATUS_ERROR_MAX);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_database.h b/chromium/content/browser/service_worker/service_worker_database.h
new file mode 100644
index 00000000000..6475de04a99
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_database.h
@@ -0,0 +1,324 @@
+// 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_SERVICE_WORKER_SERVICE_WORKER_DATABASE_H_
+#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_DATABASE_H_
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/sequence_checker.h"
+#include "base/time/time.h"
+#include "content/common/content_export.h"
+#include "content/common/service_worker/service_worker_status_code.h"
+#include "url/gurl.h"
+
+namespace leveldb {
+class DB;
+class Env;
+class Status;
+class WriteBatch;
+}
+
+namespace content {
+
+// Class to persist serviceworker registration data in a database.
+// Should NOT be used on the IO thread since this does blocking
+// file io. The ServiceWorkerStorage class owns this class and
+// is responsible for only calling it serially on background
+// non-IO threads (ala SequencedWorkerPool).
+class CONTENT_EXPORT ServiceWorkerDatabase {
+ public:
+ // We do leveldb stuff in |path| or in memory if |path| is empty.
+ explicit ServiceWorkerDatabase(const base::FilePath& path);
+ ~ServiceWorkerDatabase();
+
+ // Used in UMA. A new value must be appended only.
+ enum Status {
+ STATUS_OK,
+ STATUS_ERROR_NOT_FOUND,
+ STATUS_ERROR_IO_ERROR,
+ STATUS_ERROR_CORRUPTED,
+ STATUS_ERROR_FAILED,
+ STATUS_ERROR_MAX,
+ };
+
+ struct CONTENT_EXPORT RegistrationData {
+ // These values are immutable for the life of a registration.
+ int64 registration_id;
+ GURL scope;
+ GURL script;
+
+ // Versions are first stored once they successfully install and become
+ // the waiting version. Then transition to the active version. The stored
+ // version may be in the ACTIVE state or in the INSTALLED state.
+ int64 version_id;
+ bool is_active;
+ bool has_fetch_handler;
+ base::Time last_update_check;
+
+ RegistrationData();
+ ~RegistrationData();
+ };
+
+ struct ResourceRecord {
+ int64 resource_id;
+ GURL url;
+
+ ResourceRecord() {}
+ ResourceRecord(int64 id, GURL url) : resource_id(id), url(url) {}
+ };
+
+ // Reads next available ids from the database. Returns OK if they are
+ // successfully read. Fills the arguments with an initial value and returns
+ // OK if they are not found in the database. Otherwise, returns an error.
+ Status GetNextAvailableIds(
+ int64* next_avail_registration_id,
+ int64* next_avail_version_id,
+ int64* next_avail_resource_id);
+
+ // Reads origins that have one or more than one registration from the
+ // database. Returns OK if they are successfully read or not found.
+ // Otherwise, returns an error.
+ Status GetOriginsWithRegistrations(std::set<GURL>* origins);
+
+ // Reads registrations for |origin| from the database. Returns OK if they are
+ // successfully read or not found. Otherwise, returns an error.
+ Status GetRegistrationsForOrigin(
+ const GURL& origin,
+ std::vector<RegistrationData>* registrations);
+
+ // Reads all registrations from the database. Returns OK if successfully read
+ // or not found. Otherwise, returns an error.
+ Status GetAllRegistrations(std::vector<RegistrationData>* registrations);
+
+ // Saving, retrieving, and updating registration data.
+ // (will bump next_avail_xxxx_ids as needed)
+ // (resource ids will be added/removed from the uncommitted/purgeable
+ // lists as needed)
+
+ // Reads a registration for |registration_id| and resource records associated
+ // with it from the database. Returns OK if they are successfully read.
+ // Otherwise, returns an error.
+ Status ReadRegistration(
+ int64 registration_id,
+ const GURL& origin,
+ RegistrationData* registration,
+ std::vector<ResourceRecord>* resources);
+
+ // Writes |registration| and |resources| into the database and does following
+ // things:
+ // - Deletes an old version of the registration if exists.
+ // - Bumps the next registration id and the next version id if needed.
+ // - Removes |resources| from the uncommitted list if exist.
+ // Returns OK they are successfully written. Otherwise, returns an error.
+ Status WriteRegistration(
+ const RegistrationData& registration,
+ const std::vector<ResourceRecord>& resources,
+ std::vector<int64>* newly_purgeable_resources);
+
+ // Updates a registration for |registration_id| to an active state. Returns OK
+ // if it's successfully updated. Otherwise, returns an error.
+ Status UpdateVersionToActive(
+ int64 registration_id,
+ const GURL& origin);
+
+ // Updates last check time of a registration for |registration_id| by |time|.
+ // Returns OK if it's successfully updated. Otherwise, returns an error.
+ Status UpdateLastCheckTime(
+ int64 registration_id,
+ const GURL& origin,
+ const base::Time& time);
+
+ // Deletes a registration for |registration_id| and moves resource records
+ // associated with it into the purgeable list. Returns OK if it's successfully
+ // deleted or not found in the database. Otherwise, returns an error.
+ Status DeleteRegistration(
+ int64 registration_id,
+ const GURL& origin,
+ std::vector<int64>* newly_purgeable_resources);
+
+ // As new resources are put into the diskcache, they go into an uncommitted
+ // list. When a registration is saved that refers to those ids, they're
+ // removed from that list. When a resource no longer has any registrations or
+ // caches referring to it, it's added to the purgeable list. Periodically,
+ // the purgeable list can be purged from the diskcache. At system startup, all
+ // uncommitted ids are moved to the purgeable list.
+
+ // Reads uncommitted resource ids from the database. Returns OK on success.
+ // Otherwise clears |ids| and returns an error.
+ Status GetUncommittedResourceIds(std::set<int64>* ids);
+
+ // Writes |ids| into the database as uncommitted resources. Returns OK on
+ // success. Otherwise writes nothing and returns an error.
+ Status WriteUncommittedResourceIds(const std::set<int64>& ids);
+
+ // Deletes uncommitted resource ids specified by |ids| from the database.
+ // Returns OK on success. Otherwise deletes nothing and returns an error.
+ Status ClearUncommittedResourceIds(const std::set<int64>& ids);
+
+ // Reads purgeable resource ids from the database. Returns OK on success.
+ // Otherwise clears |ids| and returns an error.
+ Status GetPurgeableResourceIds(std::set<int64>* ids);
+
+ // Writes |ids| into the database as purgeable resources. Returns OK on
+ // success. Otherwise writes nothing and returns an error.
+ Status WritePurgeableResourceIds(const std::set<int64>& ids);
+
+ // Deletes purgeable resource ids specified by |ids| from the database.
+ // Returns OK on success. Otherwise deletes nothing and returns an error.
+ Status ClearPurgeableResourceIds(const std::set<int64>& ids);
+
+ // Moves |ids| from the uncommitted list to the purgeable list.
+ // Returns OK on success. Otherwise deletes nothing and returns an error.
+ Status PurgeUncommittedResourceIds(const std::set<int64>& ids);
+
+ // Deletes all data for |origin|, namely, unique origin, registrations and
+ // resource records. Resources are moved to the purgeable list. Returns OK if
+ // they are successfully deleted or not found in the database. Otherwise,
+ // returns an error.
+ Status DeleteAllDataForOrigin(
+ const GURL& origin,
+ std::vector<int64>* newly_purgeable_resources);
+
+ // Completely deletes the contents of the database.
+ // Be careful using this function.
+ Status DestroyDatabase();
+
+ private:
+ // Opens the database at the |path_|. This is lazily called when the first
+ // database API is called. Returns OK if the database is successfully opened.
+ // Returns NOT_FOUND if the database does not exist and |create_if_missing| is
+ // false. Otherwise, returns an error.
+ Status LazyOpen(bool create_if_missing);
+
+ // Helper for LazyOpen(). |status| must be the return value from LazyOpen()
+ // and this must be called just after LazyOpen() is called. Returns true if
+ // the database is new or nonexistent, that is, it has never been used.
+ bool IsNewOrNonexistentDatabase(Status status);
+
+ // Reads the next available id for |id_key|. Returns OK if it's successfully
+ // read. Fills |next_avail_id| with an initial value and returns OK if it's
+ // not found in the database. Otherwise, returns an error.
+ Status ReadNextAvailableId(
+ const char* id_key,
+ int64* next_avail_id);
+
+ // Reads registration data for |registration_id| from the database. Returns OK
+ // if successfully reads. Otherwise, returns an error.
+ Status ReadRegistrationData(
+ int64 registration_id,
+ const GURL& origin,
+ RegistrationData* registration);
+
+ // Reads resource records for |version_id| from the database. Returns OK if
+ // it's successfully read or not found in the database. Otherwise, returns an
+ // error.
+ Status ReadResourceRecords(
+ int64 version_id,
+ std::vector<ResourceRecord>* resources);
+
+ // Deletes resource records for |version_id| from the database. Returns OK if
+ // they are successfully deleted or not found in the database. Otherwise,
+ // returns an error.
+ Status DeleteResourceRecords(
+ int64 version_id,
+ std::vector<int64>* newly_purgeable_resources,
+ leveldb::WriteBatch* batch);
+
+ // Reads resource ids for |id_key_prefix| from the database. Returns OK if
+ // it's successfully read or not found in the database. Otherwise, returns an
+ // error.
+ Status ReadResourceIds(
+ const char* id_key_prefix,
+ std::set<int64>* ids);
+
+ // Write resource ids for |id_key_prefix| into the database. Returns OK on
+ // success. Otherwise, returns writes nothing and returns an error.
+ Status WriteResourceIds(
+ const char* id_key_prefix,
+ const std::set<int64>& ids);
+ Status WriteResourceIdsInBatch(
+ const char* id_key_prefix,
+ const std::set<int64>& ids,
+ leveldb::WriteBatch* batch);
+
+ // Deletes resource ids for |id_key_prefix| from the database. Returns OK if
+ // it's successfully deleted or not found in the database. Otherwise, returns
+ // an error.
+ Status DeleteResourceIds(
+ const char* id_key_prefix,
+ const std::set<int64>& ids);
+ Status DeleteResourceIdsInBatch(
+ const char* id_key_prefix,
+ const std::set<int64>& ids,
+ leveldb::WriteBatch* batch);
+
+ // Reads the current schema version from the database. If the database hasn't
+ // been written anything yet, sets |db_version| to 0 and returns OK.
+ Status ReadDatabaseVersion(int64* db_version);
+
+ // Writes a batch into the database.
+ // NOTE: You must call this when you want to put something into the database
+ // because this initializes the database if needed.
+ Status WriteBatch(leveldb::WriteBatch* batch);
+
+ // Bumps the next available id if |used_id| is greater than or equal to the
+ // cached one.
+ void BumpNextRegistrationIdIfNeeded(
+ int64 used_id,
+ leveldb::WriteBatch* batch);
+ void BumpNextVersionIdIfNeeded(
+ int64 used_id,
+ leveldb::WriteBatch* batch);
+
+ bool IsOpen();
+
+ void Disable(
+ const tracked_objects::Location& from_here,
+ Status status);
+ void HandleOpenResult(
+ const tracked_objects::Location& from_here,
+ Status status);
+ void HandleReadResult(
+ const tracked_objects::Location& from_here,
+ Status status);
+ void HandleWriteResult(
+ const tracked_objects::Location& from_here,
+ Status status);
+
+ base::FilePath path_;
+ scoped_ptr<leveldb::Env> env_;
+ scoped_ptr<leveldb::DB> db_;
+
+ int64 next_avail_registration_id_;
+ int64 next_avail_resource_id_;
+ int64 next_avail_version_id_;
+
+ enum State {
+ UNINITIALIZED,
+ INITIALIZED,
+ DISABLED,
+ };
+ State state_;
+
+ base::SequenceChecker sequence_checker_;
+
+ FRIEND_TEST_ALL_PREFIXES(ServiceWorkerDatabaseTest, OpenDatabase);
+ FRIEND_TEST_ALL_PREFIXES(ServiceWorkerDatabaseTest, OpenDatabase_InMemory);
+ FRIEND_TEST_ALL_PREFIXES(ServiceWorkerDatabaseTest, DatabaseVersion);
+ FRIEND_TEST_ALL_PREFIXES(ServiceWorkerDatabaseTest, GetNextAvailableIds);
+ FRIEND_TEST_ALL_PREFIXES(ServiceWorkerDatabaseTest, DestroyDatabase);
+
+ DISALLOW_COPY_AND_ASSIGN(ServiceWorkerDatabase);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_DATABASE_H_
diff --git a/chromium/content/browser/service_worker/service_worker_database.proto b/chromium/content/browser/service_worker/service_worker_database.proto
new file mode 100644
index 00000000000..c2223ecdd76
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_database.proto
@@ -0,0 +1,31 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package content;
+
+message ServiceWorkerRegistrationData {
+ required int64 registration_id = 1;
+ required string scope_url = 2;
+ required string script_url = 3;
+
+ // Versions are first stored once they successfully install and become the
+ // waiting version. Then they are updated when they transition to the active
+ // version.
+ required int64 version_id = 4;
+
+ required bool is_active = 5;
+ required bool has_fetch_handler = 6;
+
+ // Serialized by Time::ToInternalValue().
+ required int64 last_update_check_time = 7;
+}
+
+message ServiceWorkerResourceRecord {
+ required int64 resource_id = 1;
+ required string url = 2;
+}
diff --git a/chromium/content/browser/service_worker/service_worker_database_unittest.cc b/chromium/content/browser/service_worker/service_worker_database_unittest.cc
new file mode 100644
index 00000000000..baa2542cbc1
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_database_unittest.cc
@@ -0,0 +1,904 @@
+// 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/service_worker/service_worker_database.h"
+
+#include <string>
+
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/stl_util.h"
+#include "content/browser/service_worker/service_worker_database.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+namespace {
+
+typedef ServiceWorkerDatabase::RegistrationData RegistrationData;
+typedef ServiceWorkerDatabase::ResourceRecord Resource;
+
+struct AvailableIds {
+ int64 reg_id;
+ int64 res_id;
+ int64 ver_id;
+
+ AvailableIds() : reg_id(-1), res_id(-1), ver_id(-1) {}
+ ~AvailableIds() {}
+};
+
+GURL URL(const GURL& origin, const std::string& path) {
+ EXPECT_TRUE(origin.is_valid());
+ EXPECT_EQ(origin, origin.GetOrigin());
+ GURL out(origin.spec() + path);
+ EXPECT_TRUE(out.is_valid());
+ return out;
+}
+
+Resource CreateResource(int64 resource_id, const GURL& url) {
+ EXPECT_TRUE(url.is_valid());
+ Resource resource;
+ resource.resource_id = resource_id;
+ resource.url = url;
+ return resource;
+}
+
+ServiceWorkerDatabase* CreateDatabase(const base::FilePath& path) {
+ return new ServiceWorkerDatabase(path);
+}
+
+ServiceWorkerDatabase* CreateDatabaseInMemory() {
+ return new ServiceWorkerDatabase(base::FilePath());
+}
+
+void VerifyRegistrationData(const RegistrationData& expected,
+ const RegistrationData& actual) {
+ EXPECT_EQ(expected.registration_id, actual.registration_id);
+ EXPECT_EQ(expected.scope, actual.scope);
+ EXPECT_EQ(expected.script, actual.script);
+ EXPECT_EQ(expected.version_id, actual.version_id);
+ EXPECT_EQ(expected.is_active, actual.is_active);
+ EXPECT_EQ(expected.has_fetch_handler, actual.has_fetch_handler);
+ EXPECT_EQ(expected.last_update_check, actual.last_update_check);
+}
+
+void VerifyResourceRecords(const std::vector<Resource>& expected,
+ const std::vector<Resource>& actual) {
+ ASSERT_EQ(expected.size(), actual.size());
+ for (size_t i = 0; i < expected.size(); ++i) {
+ EXPECT_EQ(expected[i].resource_id, actual[i].resource_id);
+ EXPECT_EQ(expected[i].url, actual[i].url);
+ }
+}
+
+} // namespace
+
+TEST(ServiceWorkerDatabaseTest, OpenDatabase) {
+ base::ScopedTempDir database_dir;
+ ASSERT_TRUE(database_dir.CreateUniqueTempDir());
+ scoped_ptr<ServiceWorkerDatabase> database(
+ CreateDatabase(database_dir.path()));
+
+ // Should be false because the database does not exist at the path.
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND,
+ database->LazyOpen(false));
+
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->LazyOpen(true));
+
+ database.reset(CreateDatabase(database_dir.path()));
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->LazyOpen(false));
+}
+
+TEST(ServiceWorkerDatabaseTest, OpenDatabase_InMemory) {
+ scoped_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory());
+
+ // Should be false because the database does not exist in memory.
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND,
+ database->LazyOpen(false));
+
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->LazyOpen(true));
+ database.reset(CreateDatabaseInMemory());
+
+ // Should be false because the database is not persistent.
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND,
+ database->LazyOpen(false));
+}
+
+TEST(ServiceWorkerDatabaseTest, DatabaseVersion) {
+ scoped_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory());
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->LazyOpen(true));
+
+ // Opening a new database does not write anything, so its schema version
+ // should be 0.
+ int64 db_version = -1;
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->ReadDatabaseVersion(&db_version));
+ EXPECT_EQ(0u, db_version);
+
+ // First writing triggers database initialization and bumps the schema
+ // version.
+ std::vector<ServiceWorkerDatabase::ResourceRecord> resources;
+ std::vector<int64> newly_purgeable_resources;
+ ServiceWorkerDatabase::RegistrationData data;
+ ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->WriteRegistration(data, resources,
+ &newly_purgeable_resources));
+
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->ReadDatabaseVersion(&db_version));
+ EXPECT_LT(0, db_version);
+}
+
+TEST(ServiceWorkerDatabaseTest, GetNextAvailableIds) {
+ base::ScopedTempDir database_dir;
+ ASSERT_TRUE(database_dir.CreateUniqueTempDir());
+ scoped_ptr<ServiceWorkerDatabase> database(
+ CreateDatabase(database_dir.path()));
+
+ GURL origin("http://example.com");
+
+ // The database has never been used, so returns initial values.
+ AvailableIds ids;
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->GetNextAvailableIds(
+ &ids.reg_id, &ids.ver_id, &ids.res_id));
+ EXPECT_EQ(0, ids.reg_id);
+ EXPECT_EQ(0, ids.ver_id);
+ EXPECT_EQ(0, ids.res_id);
+
+ ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, database->LazyOpen(true));
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->GetNextAvailableIds(
+ &ids.reg_id, &ids.ver_id, &ids.res_id));
+ EXPECT_EQ(0, ids.reg_id);
+ EXPECT_EQ(0, ids.ver_id);
+ EXPECT_EQ(0, ids.res_id);
+
+ // Writing a registration bumps the next available ids.
+ std::vector<Resource> resources;
+ RegistrationData data1;
+ std::vector<int64> newly_purgeable_resources;
+ data1.registration_id = 100;
+ data1.scope = URL(origin, "/foo");
+ data1.script = URL(origin, "/script1.js");
+ data1.version_id = 200;
+ ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->WriteRegistration(data1, resources,
+ &newly_purgeable_resources));
+
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->GetNextAvailableIds(
+ &ids.reg_id, &ids.ver_id, &ids.res_id));
+ EXPECT_EQ(101, ids.reg_id);
+ EXPECT_EQ(201, ids.ver_id);
+ EXPECT_EQ(0, ids.res_id);
+
+ // Writing a registration whose ids are lower than the stored ones should not
+ // bump the next available ids.
+ RegistrationData data2;
+ data2.registration_id = 10;
+ data2.scope = URL(origin, "/bar");
+ data2.script = URL(origin, "/script2.js");
+ data2.version_id = 20;
+ ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->WriteRegistration(data2, resources,
+ &newly_purgeable_resources));
+
+ // Close and reopen the database to verify the stored values.
+ database.reset(CreateDatabase(database_dir.path()));
+
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->GetNextAvailableIds(
+ &ids.reg_id, &ids.ver_id, &ids.res_id));
+ EXPECT_EQ(101, ids.reg_id);
+ EXPECT_EQ(201, ids.ver_id);
+ EXPECT_EQ(0, ids.res_id);
+}
+
+TEST(ServiceWorkerDatabaseTest, GetOriginsWithRegistrations) {
+ scoped_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory());
+
+ std::set<GURL> origins;
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->GetOriginsWithRegistrations(&origins));
+ EXPECT_TRUE(origins.empty());
+
+ std::vector<Resource> resources;
+ std::vector<int64> newly_purgeable_resources;
+
+ GURL origin1("http://example.com");
+ RegistrationData data1;
+ data1.registration_id = 123;
+ data1.scope = URL(origin1, "/foo");
+ data1.script = URL(origin1, "/script1.js");
+ data1.version_id = 456;
+ ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->WriteRegistration(data1, resources,
+ &newly_purgeable_resources));
+
+ GURL origin2("https://www.example.com");
+ RegistrationData data2;
+ data2.registration_id = 234;
+ data2.scope = URL(origin2, "/bar");
+ data2.script = URL(origin2, "/script2.js");
+ data2.version_id = 567;
+ ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->WriteRegistration(data2, resources,
+ &newly_purgeable_resources));
+
+ GURL origin3("https://example.org");
+ RegistrationData data3;
+ data3.registration_id = 345;
+ data3.scope = URL(origin3, "/hoge");
+ data3.script = URL(origin3, "/script3.js");
+ data3.version_id = 678;
+ ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->WriteRegistration(data3, resources,
+ &newly_purgeable_resources));
+
+ // |origin3| has two registrations.
+ RegistrationData data4;
+ data4.registration_id = 456;
+ data4.scope = URL(origin3, "/fuga");
+ data4.script = URL(origin3, "/script4.js");
+ data4.version_id = 789;
+ ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->WriteRegistration(data4, resources,
+ &newly_purgeable_resources));
+
+ origins.clear();
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->GetOriginsWithRegistrations(&origins));
+ EXPECT_EQ(3U, origins.size());
+ EXPECT_TRUE(ContainsKey(origins, origin1));
+ EXPECT_TRUE(ContainsKey(origins, origin2));
+ EXPECT_TRUE(ContainsKey(origins, origin3));
+
+ // |origin3| has another registration, so should not remove it from the
+ // unique origin list.
+ ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->DeleteRegistration(data4.registration_id, origin3,
+ &newly_purgeable_resources));
+
+ origins.clear();
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->GetOriginsWithRegistrations(&origins));
+ EXPECT_EQ(3U, origins.size());
+ EXPECT_TRUE(ContainsKey(origins, origin1));
+ EXPECT_TRUE(ContainsKey(origins, origin2));
+ EXPECT_TRUE(ContainsKey(origins, origin3));
+
+ // |origin3| should be removed from the unique origin list.
+ ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->DeleteRegistration(data3.registration_id, origin3,
+ &newly_purgeable_resources));
+
+ origins.clear();
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->GetOriginsWithRegistrations(&origins));
+ EXPECT_EQ(2U, origins.size());
+ EXPECT_TRUE(ContainsKey(origins, origin1));
+ EXPECT_TRUE(ContainsKey(origins, origin2));
+}
+
+TEST(ServiceWorkerDatabaseTest, GetRegistrationsForOrigin) {
+ scoped_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory());
+
+ GURL origin1("http://example.com");
+ GURL origin2("https://www.example.com");
+ GURL origin3("https://example.org");
+
+ std::vector<RegistrationData> registrations;
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->GetRegistrationsForOrigin(origin1, &registrations));
+ EXPECT_TRUE(registrations.empty());
+
+ std::vector<Resource> resources;
+ std::vector<int64> newly_purgeable_resources;
+
+ RegistrationData data1;
+ data1.registration_id = 100;
+ data1.scope = URL(origin1, "/foo");
+ data1.script = URL(origin1, "/script1.js");
+ data1.version_id = 1000;
+ ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->WriteRegistration(data1, resources,
+ &newly_purgeable_resources));
+
+ RegistrationData data2;
+ data2.registration_id = 200;
+ data2.scope = URL(origin2, "/bar");
+ data2.script = URL(origin2, "/script2.js");
+ data2.version_id = 2000;
+ ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->WriteRegistration(data2, resources,
+ &newly_purgeable_resources));
+
+ RegistrationData data3;
+ data3.registration_id = 300;
+ data3.scope = URL(origin3, "/hoge");
+ data3.script = URL(origin3, "/script3.js");
+ data3.version_id = 3000;
+ ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->WriteRegistration(data3, resources,
+ &newly_purgeable_resources));
+
+ // |origin3| has two registrations.
+ RegistrationData data4;
+ data4.registration_id = 400;
+ data4.scope = URL(origin3, "/fuga");
+ data4.script = URL(origin3, "/script4.js");
+ data4.version_id = 4000;
+ ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->WriteRegistration(data4, resources,
+ &newly_purgeable_resources));
+
+ registrations.clear();
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->GetRegistrationsForOrigin(origin3, &registrations));
+ EXPECT_EQ(2U, registrations.size());
+ VerifyRegistrationData(data3, registrations[0]);
+ VerifyRegistrationData(data4, registrations[1]);
+}
+
+TEST(ServiceWorkerDatabaseTest, GetAllRegistrations) {
+ scoped_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory());
+
+ std::vector<RegistrationData> registrations;
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->GetAllRegistrations(&registrations));
+ EXPECT_TRUE(registrations.empty());
+
+ std::vector<Resource> resources;
+ std::vector<int64> newly_purgeable_resources;
+
+ GURL origin1("http://www1.example.com");
+ RegistrationData data1;
+ data1.registration_id = 100;
+ data1.scope = URL(origin1, "/foo");
+ data1.script = URL(origin1, "/script1.js");
+ data1.version_id = 1000;
+ ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->WriteRegistration(data1, resources,
+ &newly_purgeable_resources));
+
+ GURL origin2("http://www2.example.com");
+ RegistrationData data2;
+ data2.registration_id = 200;
+ data2.scope = URL(origin2, "/bar");
+ data2.script = URL(origin2, "/script2.js");
+ data2.version_id = 2000;
+ ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->WriteRegistration(data2, resources,
+ &newly_purgeable_resources));
+
+ GURL origin3("http://www3.example.com");
+ RegistrationData data3;
+ data3.registration_id = 300;
+ data3.scope = URL(origin3, "/hoge");
+ data3.script = URL(origin3, "/script3.js");
+ data3.version_id = 3000;
+ ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->WriteRegistration(data3, resources,
+ &newly_purgeable_resources));
+
+ // |origin3| has two registrations.
+ RegistrationData data4;
+ data4.registration_id = 400;
+ data4.scope = URL(origin3, "/fuga");
+ data4.script = URL(origin3, "/script4.js");
+ data4.version_id = 4000;
+ ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->WriteRegistration(data4, resources,
+ &newly_purgeable_resources));
+
+ registrations.clear();
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->GetAllRegistrations(&registrations));
+ EXPECT_EQ(4U, registrations.size());
+ VerifyRegistrationData(data1, registrations[0]);
+ VerifyRegistrationData(data2, registrations[1]);
+ VerifyRegistrationData(data3, registrations[2]);
+ VerifyRegistrationData(data4, registrations[3]);
+}
+
+TEST(ServiceWorkerDatabaseTest, Registration_Basic) {
+ scoped_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory());
+
+ GURL origin("http://example.com");
+ RegistrationData data;
+ data.registration_id = 100;
+ data.scope = URL(origin, "/foo");
+ data.script = URL(origin, "/script.js");
+ data.version_id = 200;
+
+ std::vector<Resource> resources;
+ resources.push_back(CreateResource(1, URL(origin, "/resource1")));
+ resources.push_back(CreateResource(2, URL(origin, "/resource2")));
+
+ // Write a resource to the uncommitted list to make sure that writing
+ // registration removes resource ids associated with the registration from
+ // the uncommitted list.
+ std::set<int64> uncommitted_ids;
+ uncommitted_ids.insert(resources[0].resource_id);
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->WriteUncommittedResourceIds(uncommitted_ids));
+ std::set<int64> uncommitted_ids_out;
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->GetUncommittedResourceIds(&uncommitted_ids_out));
+ EXPECT_EQ(uncommitted_ids, uncommitted_ids_out);
+
+ std::vector<int64> newly_purgeable_resources;
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->WriteRegistration(data, resources,
+ &newly_purgeable_resources));
+
+ // Make sure that the registration and resource records are stored.
+ RegistrationData data_out;
+ std::vector<Resource> resources_out;
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->ReadRegistration(
+ data.registration_id, origin, &data_out, &resources_out));
+ VerifyRegistrationData(data, data_out);
+ VerifyResourceRecords(resources, resources_out);
+
+ // Make sure that the resource is removed from the uncommitted list.
+ uncommitted_ids_out.clear();
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->GetUncommittedResourceIds(&uncommitted_ids_out));
+ EXPECT_TRUE(uncommitted_ids_out.empty());
+
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->DeleteRegistration(data.registration_id, origin,
+ &newly_purgeable_resources));
+ ASSERT_EQ(resources.size(), newly_purgeable_resources.size());
+ for (size_t i = 0; i < resources.size(); ++i)
+ EXPECT_EQ(newly_purgeable_resources[i], resources[i].resource_id);
+
+ // Make sure that the registration and resource records are gone.
+ resources_out.clear();
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND,
+ database->ReadRegistration(
+ data.registration_id, origin, &data_out, &resources_out));
+ EXPECT_TRUE(resources_out.empty());
+
+ // Resources should be purgeable because these are no longer referred.
+ std::set<int64> purgeable_ids_out;
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->GetPurgeableResourceIds(&purgeable_ids_out));
+ EXPECT_EQ(2u, purgeable_ids_out.size());
+ EXPECT_TRUE(ContainsKey(purgeable_ids_out, resources[0].resource_id));
+ EXPECT_TRUE(ContainsKey(purgeable_ids_out, resources[1].resource_id));
+}
+
+TEST(ServiceWorkerDatabaseTest, Registration_Overwrite) {
+ scoped_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory());
+
+ GURL origin("http://example.com");
+ RegistrationData data;
+ data.registration_id = 100;
+ data.scope = URL(origin, "/foo");
+ data.script = URL(origin, "/script.js");
+ data.version_id = 200;
+
+ std::vector<Resource> resources1;
+ resources1.push_back(CreateResource(1, URL(origin, "/resource1")));
+ resources1.push_back(CreateResource(2, URL(origin, "/resource2")));
+ std::vector<int64> newly_purgeable_resources;
+
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->WriteRegistration(data, resources1,
+ &newly_purgeable_resources));
+ EXPECT_TRUE(newly_purgeable_resources.empty());
+
+ // Make sure that the registration and resource records are stored.
+ RegistrationData data_out;
+ std::vector<Resource> resources_out;
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->ReadRegistration(
+ data.registration_id, origin, &data_out, &resources_out));
+ VerifyRegistrationData(data, data_out);
+ VerifyResourceRecords(resources1, resources_out);
+
+ // Update the registration.
+ RegistrationData updated_data = data;
+ updated_data.version_id = data.version_id + 1;
+ std::vector<Resource> resources2;
+ resources2.push_back(CreateResource(3, URL(origin, "/resource3")));
+ resources2.push_back(CreateResource(4, URL(origin, "/resource4")));
+
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->WriteRegistration(updated_data, resources2,
+ &newly_purgeable_resources));
+ ASSERT_EQ(resources1.size(), newly_purgeable_resources.size());
+ for(size_t i = 0; i < resources1.size(); ++i)
+ EXPECT_EQ(newly_purgeable_resources[i], resources1[i].resource_id);
+
+ // Make sure that |updated_data| is stored and resources referred from |data|
+ // is moved to the purgeable list.
+ resources_out.clear();
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->ReadRegistration(
+ updated_data.registration_id, origin, &data_out, &resources_out));
+ VerifyRegistrationData(updated_data, data_out);
+ VerifyResourceRecords(resources2, resources_out);
+
+ std::set<int64> purgeable_ids_out;
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->GetPurgeableResourceIds(&purgeable_ids_out));
+ EXPECT_EQ(2u, purgeable_ids_out.size());
+ EXPECT_TRUE(ContainsKey(purgeable_ids_out, resources1[0].resource_id));
+ EXPECT_TRUE(ContainsKey(purgeable_ids_out, resources1[1].resource_id));
+}
+
+TEST(ServiceWorkerDatabaseTest, Registration_Multiple) {
+ scoped_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory());
+ GURL origin("http://example.com");
+
+ std::vector<int64> newly_purgeable_resources;
+
+ // Add registration1.
+ RegistrationData data1;
+ data1.registration_id = 100;
+ data1.scope = URL(origin, "/foo");
+ data1.script = URL(origin, "/script1.js");
+ data1.version_id = 200;
+
+ std::vector<Resource> resources1;
+ resources1.push_back(CreateResource(1, URL(origin, "/resource1")));
+ resources1.push_back(CreateResource(2, URL(origin, "/resource2")));
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->WriteRegistration(data1, resources1,
+ &newly_purgeable_resources));
+
+ // Add registration2.
+ RegistrationData data2;
+ data2.registration_id = 101;
+ data2.scope = URL(origin, "/bar");
+ data2.script = URL(origin, "/script2.js");
+ data2.version_id = 201;
+
+ std::vector<Resource> resources2;
+ resources2.push_back(CreateResource(3, URL(origin, "/resource3")));
+ resources2.push_back(CreateResource(4, URL(origin, "/resource4")));
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->WriteRegistration(data2, resources2,
+ &newly_purgeable_resources));
+
+ // Make sure that registration1 is stored.
+ RegistrationData data_out;
+ std::vector<Resource> resources_out;
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->ReadRegistration(
+ data1.registration_id, origin, &data_out, &resources_out));
+ VerifyRegistrationData(data1, data_out);
+ VerifyResourceRecords(resources1, resources_out);
+
+ // Make sure that registration2 is also stored.
+ resources_out.clear();
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->ReadRegistration(
+ data2.registration_id, origin, &data_out, &resources_out));
+ VerifyRegistrationData(data2, data_out);
+ VerifyResourceRecords(resources2, resources_out);
+
+ std::set<int64> purgeable_ids_out;
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->GetPurgeableResourceIds(&purgeable_ids_out));
+ EXPECT_TRUE(purgeable_ids_out.empty());
+
+ // Delete registration1.
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->DeleteRegistration(data1.registration_id, origin,
+ &newly_purgeable_resources));
+
+ // Make sure that registration1 is gone.
+ resources_out.clear();
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND,
+ database->ReadRegistration(
+ data1.registration_id, origin, &data_out, &resources_out));
+ EXPECT_TRUE(resources_out.empty());
+
+ purgeable_ids_out.clear();
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->GetPurgeableResourceIds(&purgeable_ids_out));
+ EXPECT_EQ(2u, purgeable_ids_out.size());
+ EXPECT_TRUE(ContainsKey(purgeable_ids_out, resources1[0].resource_id));
+ EXPECT_TRUE(ContainsKey(purgeable_ids_out, resources1[1].resource_id));
+
+ // Make sure that registration2 is still alive.
+ resources_out.clear();
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->ReadRegistration(
+ data2.registration_id, origin, &data_out, &resources_out));
+ VerifyRegistrationData(data2, data_out);
+ VerifyResourceRecords(resources2, resources_out);
+}
+
+TEST(ServiceWorkerDatabaseTest, UpdateVersionToActive) {
+ scoped_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory());
+ GURL origin("http://example.com");
+
+ std::vector<int64> newly_purgeable_resources;
+
+ // Should be false because a registration does not exist.
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND,
+ database->UpdateVersionToActive(0, origin));
+
+ // Add a registration.
+ RegistrationData data;
+ data.registration_id = 100;
+ data.scope = URL(origin, "/foo");
+ data.script = URL(origin, "/script.js");
+ data.version_id = 200;
+ data.is_active = false;
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->WriteRegistration(data, std::vector<Resource>(),
+ &newly_purgeable_resources));
+
+ // Make sure that the registration is stored.
+ RegistrationData data_out;
+ std::vector<Resource> resources_out;
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->ReadRegistration(
+ data.registration_id, origin, &data_out, &resources_out));
+ VerifyRegistrationData(data, data_out);
+ EXPECT_TRUE(resources_out.empty());
+
+ // Activate the registration.
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->UpdateVersionToActive(data.registration_id, origin));
+
+ // Make sure that the registration is activated.
+ resources_out.clear();
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->ReadRegistration(
+ data.registration_id, origin, &data_out, &resources_out));
+ RegistrationData expected_data = data;
+ expected_data.is_active = true;
+ VerifyRegistrationData(expected_data, data_out);
+ EXPECT_TRUE(resources_out.empty());
+
+ // Delete the registration.
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->DeleteRegistration(data.registration_id, origin,
+ &newly_purgeable_resources));
+
+ // Should be false because the registration is gone.
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND,
+ database->UpdateVersionToActive(data.registration_id, origin));
+}
+
+TEST(ServiceWorkerDatabaseTest, UpdateLastCheckTime) {
+ scoped_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory());
+ GURL origin("http://example.com");
+ std::vector<int64> newly_purgeable_resources;
+
+ // Should be false because a registration does not exist.
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND,
+ database->UpdateLastCheckTime(0, origin, base::Time::Now()));
+
+ // Add a registration.
+ RegistrationData data;
+ data.registration_id = 100;
+ data.scope = URL(origin, "/foo");
+ data.script = URL(origin, "/script.js");
+ data.version_id = 200;
+ data.last_update_check = base::Time::Now();
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->WriteRegistration(data, std::vector<Resource>(),
+ &newly_purgeable_resources));
+
+ // Make sure that the registration is stored.
+ RegistrationData data_out;
+ std::vector<Resource> resources_out;
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->ReadRegistration(
+ data.registration_id, origin, &data_out, &resources_out));
+ VerifyRegistrationData(data, data_out);
+ EXPECT_TRUE(resources_out.empty());
+
+ // Update the last check time.
+ base::Time updated_time = base::Time::Now();
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->UpdateLastCheckTime(
+ data.registration_id, origin, updated_time));
+
+ // Make sure that the registration is updated.
+ resources_out.clear();
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->ReadRegistration(
+ data.registration_id, origin, &data_out, &resources_out));
+ RegistrationData expected_data = data;
+ expected_data.last_update_check = updated_time;
+ VerifyRegistrationData(expected_data, data_out);
+ EXPECT_TRUE(resources_out.empty());
+
+ // Delete the registration.
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->DeleteRegistration(data.registration_id, origin,
+ &newly_purgeable_resources));
+
+ // Should be false because the registration is gone.
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND,
+ database->UpdateLastCheckTime(
+ data.registration_id, origin, base::Time::Now()));
+}
+
+TEST(ServiceWorkerDatabaseTest, UncommittedResourceIds) {
+ scoped_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory());
+
+ // Write {1, 2, 3}.
+ std::set<int64> ids1;
+ ids1.insert(1);
+ ids1.insert(2);
+ ids1.insert(3);
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->WriteUncommittedResourceIds(ids1));
+
+ std::set<int64> ids_out;
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->GetUncommittedResourceIds(&ids_out));
+ EXPECT_EQ(ids1, ids_out);
+
+ // Write {2, 4}.
+ std::set<int64> ids2;
+ ids2.insert(2);
+ ids2.insert(4);
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->WriteUncommittedResourceIds(ids2));
+
+ ids_out.clear();
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->GetUncommittedResourceIds(&ids_out));
+ std::set<int64> expected = base::STLSetUnion<std::set<int64> >(ids1, ids2);
+ EXPECT_EQ(expected, ids_out);
+
+ // Delete {2, 3}.
+ std::set<int64> ids3;
+ ids3.insert(2);
+ ids3.insert(3);
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->ClearUncommittedResourceIds(ids3));
+
+ ids_out.clear();
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->GetUncommittedResourceIds(&ids_out));
+ expected = base::STLSetDifference<std::set<int64> >(expected, ids3);
+ EXPECT_EQ(expected, ids_out);
+}
+
+TEST(ServiceWorkerDatabaseTest, PurgeableResourceIds) {
+ scoped_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory());
+
+ // Write {1, 2, 3}.
+ std::set<int64> ids1;
+ ids1.insert(1);
+ ids1.insert(2);
+ ids1.insert(3);
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->WritePurgeableResourceIds(ids1));
+
+ std::set<int64> ids_out;
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->GetPurgeableResourceIds(&ids_out));
+ EXPECT_EQ(ids1, ids_out);
+
+ // Write {2, 4}.
+ std::set<int64> ids2;
+ ids2.insert(2);
+ ids2.insert(4);
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->WritePurgeableResourceIds(ids2));
+
+ ids_out.clear();
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->GetPurgeableResourceIds(&ids_out));
+ std::set<int64> expected = base::STLSetUnion<std::set<int64> >(ids1, ids2);
+ EXPECT_EQ(expected, ids_out);
+
+ // Delete {2, 3}.
+ std::set<int64> ids3;
+ ids3.insert(2);
+ ids3.insert(3);
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->ClearPurgeableResourceIds(ids3));
+
+ ids_out.clear();
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->GetPurgeableResourceIds(&ids_out));
+ expected = base::STLSetDifference<std::set<int64> >(expected, ids3);
+ EXPECT_EQ(expected, ids_out);
+}
+
+TEST(ServiceWorkerDatabaseTest, DeleteAllDataForOrigin) {
+ scoped_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory());
+ std::vector<int64> newly_purgeable_resources;
+
+ // Data associated with |origin1| will be removed.
+ GURL origin1("http://example.com");
+ GURL origin2("http://example.org");
+
+ // |origin1| has two registrations.
+ RegistrationData data1;
+ data1.registration_id = 10;
+ data1.scope = URL(origin1, "/foo");
+ data1.script = URL(origin1, "/script1.js");
+ data1.version_id = 100;
+
+ std::vector<Resource> resources1;
+ resources1.push_back(CreateResource(1, URL(origin1, "/resource1")));
+ resources1.push_back(CreateResource(2, URL(origin1, "/resource2")));
+ ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->WriteRegistration(data1, resources1,
+ &newly_purgeable_resources));
+
+ RegistrationData data2;
+ data2.registration_id = 11;
+ data2.scope = URL(origin1, "/bar");
+ data2.script = URL(origin1, "/script2.js");
+ data2.version_id = 101;
+
+ std::vector<Resource> resources2;
+ resources2.push_back(CreateResource(3, URL(origin1, "/resource3")));
+ resources2.push_back(CreateResource(4, URL(origin1, "/resource4")));
+ ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->WriteRegistration(data2, resources2,
+ &newly_purgeable_resources));
+
+ // |origin2| has one registration.
+ RegistrationData data3;
+ data3.registration_id = 12;
+ data3.scope = URL(origin2, "/hoge");
+ data3.script = URL(origin2, "/script3.js");
+ data3.version_id = 102;
+
+ std::vector<Resource> resources3;
+ resources3.push_back(CreateResource(5, URL(origin2, "/resource5")));
+ resources3.push_back(CreateResource(6, URL(origin2, "/resource6")));
+ ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->WriteRegistration(data3, resources3,
+ &newly_purgeable_resources));
+
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->DeleteAllDataForOrigin(origin1,
+ &newly_purgeable_resources));
+
+ // |origin1| should be removed from the unique origin list.
+ std::set<GURL> unique_origins;
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->GetOriginsWithRegistrations(&unique_origins));
+ EXPECT_EQ(1u, unique_origins.size());
+ EXPECT_TRUE(ContainsKey(unique_origins, origin2));
+
+ // The registrations for |origin1| should be removed.
+ std::vector<RegistrationData> registrations;
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->GetRegistrationsForOrigin(origin1, &registrations));
+ EXPECT_TRUE(registrations.empty());
+
+ // The registration for |origin2| should not be removed.
+ RegistrationData data_out;
+ std::vector<Resource> resources_out;
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->ReadRegistration(
+ data3.registration_id, origin2, &data_out, &resources_out));
+ VerifyRegistrationData(data3, data_out);
+ VerifyResourceRecords(resources3, resources_out);
+
+ // The resources associated with |origin1| should be purgeable.
+ std::set<int64> purgeable_ids_out;
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->GetPurgeableResourceIds(&purgeable_ids_out));
+ EXPECT_EQ(4u, purgeable_ids_out.size());
+ EXPECT_TRUE(ContainsKey(purgeable_ids_out, 1));
+ EXPECT_TRUE(ContainsKey(purgeable_ids_out, 2));
+ EXPECT_TRUE(ContainsKey(purgeable_ids_out, 3));
+ EXPECT_TRUE(ContainsKey(purgeable_ids_out, 4));
+}
+
+TEST(ServiceWorkerDatabaseTest, DestroyDatabase) {
+ base::ScopedTempDir database_dir;
+ ASSERT_TRUE(database_dir.CreateUniqueTempDir());
+ scoped_ptr<ServiceWorkerDatabase> database(
+ CreateDatabase(database_dir.path()));
+
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->LazyOpen(true));
+ ASSERT_TRUE(base::DirectoryExists(database_dir.path()));
+
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->DestroyDatabase());
+ ASSERT_FALSE(base::DirectoryExists(database_dir.path()));
+}
+
+} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_disk_cache.cc b/chromium/content/browser/service_worker/service_worker_disk_cache.cc
new file mode 100644
index 00000000000..a8ca52acae7
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_disk_cache.cc
@@ -0,0 +1,22 @@
+// 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/service_worker/service_worker_disk_cache.h"
+
+namespace content {
+
+ServiceWorkerResponseReader::ServiceWorkerResponseReader(
+ int64 response_id, ServiceWorkerDiskCache* disk_cache)
+ : appcache::AppCacheResponseReader(response_id, 0, disk_cache) {
+}
+
+ServiceWorkerResponseWriter::ServiceWorkerResponseWriter(
+ int64 response_id, ServiceWorkerDiskCache* disk_cache)
+ : appcache::AppCacheResponseWriter(response_id, 0, disk_cache) {
+}
+
+HttpResponseInfoIOBuffer::~HttpResponseInfoIOBuffer() {
+}
+
+} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_disk_cache.h b/chromium/content/browser/service_worker/service_worker_disk_cache.h
new file mode 100644
index 00000000000..83850129aad
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_disk_cache.h
@@ -0,0 +1,55 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_DISK_CACHE_H_
+#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_DISK_CACHE_H_
+
+#include "content/common/content_export.h"
+#include "webkit/browser/appcache/appcache_disk_cache.h"
+
+namespace content {
+
+// Wholesale reusage of the appcache code for response reading,
+// writing, and storage. See the corresponding class in that
+// library for doc comments and other details.
+// TODO(michaeln): If this reuse sticks, refactor/move the
+// resused classes to a more common location.
+
+class CONTENT_EXPORT ServiceWorkerDiskCache
+ : public appcache::AppCacheDiskCache {
+};
+
+class CONTENT_EXPORT ServiceWorkerResponseReader
+ : public appcache::AppCacheResponseReader {
+ protected:
+ // Should only be constructed by the storage class.
+ friend class ServiceWorkerStorage;
+ ServiceWorkerResponseReader(
+ int64 response_id,
+ ServiceWorkerDiskCache* disk_cache);
+};
+
+class CONTENT_EXPORT ServiceWorkerResponseWriter
+ : public appcache::AppCacheResponseWriter {
+ protected:
+ // Should only be constructed by the storage class.
+ friend class ServiceWorkerStorage;
+ ServiceWorkerResponseWriter(
+ int64 response_id,
+ ServiceWorkerDiskCache* disk_cache);
+};
+
+struct CONTENT_EXPORT HttpResponseInfoIOBuffer
+ : public appcache::HttpResponseInfoIOBuffer {
+ public:
+ HttpResponseInfoIOBuffer() : appcache::HttpResponseInfoIOBuffer() {}
+ explicit HttpResponseInfoIOBuffer(net::HttpResponseInfo* info)
+ : appcache::HttpResponseInfoIOBuffer(info) {}
+ protected:
+ virtual ~HttpResponseInfoIOBuffer();
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_DISK_CACHE_H_
diff --git a/chromium/content/browser/service_worker/service_worker_dispatcher_host.cc b/chromium/content/browser/service_worker/service_worker_dispatcher_host.cc
index 76e7ff746ae..964a393f8eb 100644
--- a/chromium/content/browser/service_worker/service_worker_dispatcher_host.cc
+++ b/chromium/content/browser/service_worker/service_worker_dispatcher_host.cc
@@ -4,18 +4,26 @@
#include "content/browser/service_worker/service_worker_dispatcher_host.h"
+#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
+#include "content/browser/message_port_message_filter.h"
+#include "content/browser/message_port_service.h"
#include "content/browser/service_worker/embedded_worker_registry.h"
#include "content/browser/service_worker/service_worker_context_core.h"
#include "content/browser/service_worker/service_worker_context_wrapper.h"
-#include "content/browser/service_worker/service_worker_provider_host.h"
-#include "content/common/service_worker_messages.h"
+#include "content/browser/service_worker/service_worker_handle.h"
+#include "content/browser/service_worker/service_worker_registration.h"
+#include "content/browser/service_worker/service_worker_utils.h"
+#include "content/common/service_worker/embedded_worker_messages.h"
+#include "content/common/service_worker/service_worker_messages.h"
#include "ipc/ipc_message_macros.h"
#include "third_party/WebKit/public/platform/WebServiceWorkerError.h"
#include "url/gurl.h"
using blink::WebServiceWorkerError;
+namespace content {
+
namespace {
const char kDisabledErrorMessage[] =
@@ -23,19 +31,43 @@ const char kDisabledErrorMessage[] =
const char kDomainMismatchErrorMessage[] =
"Scope and scripts do not have the same origin";
-} // namespace
+const uint32 kFilteredMessageClasses[] = {
+ ServiceWorkerMsgStart,
+ EmbeddedWorkerMsgStart,
+};
-namespace content {
+bool CanRegisterServiceWorker(const GURL& document_url,
+ const GURL& pattern,
+ const GURL& script_url) {
+ // TODO: Respect Chrome's content settings, if we add a setting for
+ // controlling whether Service Worker is allowed.
+ return document_url.GetOrigin() == pattern.GetOrigin() &&
+ document_url.GetOrigin() == script_url.GetOrigin();
+}
+
+bool CanUnregisterServiceWorker(const GURL& document_url,
+ const GURL& pattern) {
+ // TODO: Respect Chrome's content settings, if we add a setting for
+ // controlling whether Service Worker is allowed.
+ return document_url.GetOrigin() == pattern.GetOrigin();
+}
+
+} // namespace
ServiceWorkerDispatcherHost::ServiceWorkerDispatcherHost(
- int render_process_id)
- : render_process_id_(render_process_id) {
+ int render_process_id,
+ MessagePortMessageFilter* message_port_message_filter)
+ : BrowserMessageFilter(kFilteredMessageClasses,
+ arraysize(kFilteredMessageClasses)),
+ render_process_id_(render_process_id),
+ message_port_message_filter_(message_port_message_filter),
+ channel_ready_(false) {
}
ServiceWorkerDispatcherHost::~ServiceWorkerDispatcherHost() {
- if (context_) {
- context_->RemoveAllProviderHostsForProcess(render_process_id_);
- context_->embedded_worker_registry()->RemoveChildProcessSender(
+ if (GetContext()) {
+ GetContext()->RemoveAllProviderHostsForProcess(render_process_id_);
+ GetContext()->embedded_worker_registry()->RemoveChildProcessSender(
render_process_id_);
}
}
@@ -46,27 +78,32 @@ void ServiceWorkerDispatcherHost::Init(
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&ServiceWorkerDispatcherHost::Init,
- this, make_scoped_refptr(context_wrapper)));
- return;
+ this, make_scoped_refptr(context_wrapper)));
+ return;
}
- context_ = context_wrapper->context()->AsWeakPtr();
- context_->embedded_worker_registry()->AddChildProcessSender(
+ context_wrapper_ = context_wrapper;
+ GetContext()->embedded_worker_registry()->AddChildProcessSender(
render_process_id_, this);
}
+void ServiceWorkerDispatcherHost::OnFilterAdded(IPC::Sender* sender) {
+ BrowserMessageFilter::OnFilterAdded(sender);
+ channel_ready_ = true;
+ std::vector<IPC::Message*> messages;
+ pending_messages_.release(&messages);
+ for (size_t i = 0; i < messages.size(); ++i) {
+ BrowserMessageFilter::Send(messages[i]);
+ }
+}
+
void ServiceWorkerDispatcherHost::OnDestruct() const {
BrowserThread::DeleteOnIOThread::Destruct(this);
}
bool ServiceWorkerDispatcherHost::OnMessageReceived(
- const IPC::Message& message,
- bool* message_was_ok) {
- if (IPC_MESSAGE_CLASS(message) != ServiceWorkerMsgStart)
- return false;
-
+ const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(
- ServiceWorkerDispatcherHost, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(ServiceWorkerDispatcherHost, message)
IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_RegisterServiceWorker,
OnRegisterServiceWorker)
IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_UnregisterServiceWorker,
@@ -75,41 +112,100 @@ bool ServiceWorkerDispatcherHost::OnMessageReceived(
OnProviderCreated)
IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_ProviderDestroyed,
OnProviderDestroyed)
+ IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_SetVersionId,
+ OnSetHostedVersionId)
+ IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_PostMessageToWorker,
+ OnPostMessageToWorker)
+ IPC_MESSAGE_HANDLER(EmbeddedWorkerHostMsg_WorkerScriptLoaded,
+ OnWorkerScriptLoaded)
+ IPC_MESSAGE_HANDLER(EmbeddedWorkerHostMsg_WorkerScriptLoadFailed,
+ OnWorkerScriptLoadFailed)
+ IPC_MESSAGE_HANDLER(EmbeddedWorkerHostMsg_WorkerStarted,
+ OnWorkerStarted)
+ IPC_MESSAGE_HANDLER(EmbeddedWorkerHostMsg_WorkerStopped,
+ OnWorkerStopped)
+ IPC_MESSAGE_HANDLER(EmbeddedWorkerHostMsg_ReportException,
+ OnReportException)
+ IPC_MESSAGE_HANDLER(EmbeddedWorkerHostMsg_ReportConsoleMessage,
+ OnReportConsoleMessage)
+ IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_IncrementServiceWorkerRefCount,
+ OnIncrementServiceWorkerRefCount)
+ IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_DecrementServiceWorkerRefCount,
+ OnDecrementServiceWorkerRefCount)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
+ if (!handled && GetContext()) {
+ handled =
+ GetContext()->embedded_worker_registry()->OnMessageReceived(message);
+ if (!handled)
+ BadMessageReceived();
+ }
+
return handled;
}
+bool ServiceWorkerDispatcherHost::Send(IPC::Message* message) {
+ if (channel_ready_) {
+ BrowserMessageFilter::Send(message);
+ // Don't bother passing through Send()'s result: it's not reliable.
+ return true;
+ }
+
+ pending_messages_.push_back(message);
+ return true;
+}
+
+void ServiceWorkerDispatcherHost::RegisterServiceWorkerHandle(
+ scoped_ptr<ServiceWorkerHandle> handle) {
+ int handle_id = handle->handle_id();
+ handles_.AddWithID(handle.release(), handle_id);
+}
+
void ServiceWorkerDispatcherHost::OnRegisterServiceWorker(
- int32 thread_id,
- int32 request_id,
+ int thread_id,
+ int request_id,
+ int provider_id,
const GURL& pattern,
const GURL& script_url) {
- if (!context_ || !context_->IsEnabled()) {
+ if (!GetContext() || !ServiceWorkerUtils::IsFeatureEnabled()) {
Send(new ServiceWorkerMsg_ServiceWorkerRegistrationError(
thread_id,
request_id,
- WebServiceWorkerError::DisabledError,
- ASCIIToUTF16(kDisabledErrorMessage)));
+ WebServiceWorkerError::ErrorTypeDisabled,
+ base::ASCIIToUTF16(kDisabledErrorMessage)));
return;
}
- // TODO(alecflett): This check is insufficient for release. Add a
- // ServiceWorker-specific policy query in
- // ChildProcessSecurityImpl. See http://crbug.com/311631.
- if (pattern.GetOrigin() != script_url.GetOrigin()) {
+ ServiceWorkerProviderHost* provider_host = GetContext()->GetProviderHost(
+ render_process_id_, provider_id);
+ if (!provider_host) {
+ BadMessageReceived();
+ return;
+ }
+ if (!provider_host->IsContextAlive()) {
Send(new ServiceWorkerMsg_ServiceWorkerRegistrationError(
thread_id,
request_id,
- WebServiceWorkerError::SecurityError,
- ASCIIToUTF16(kDomainMismatchErrorMessage)));
+ WebServiceWorkerError::ErrorTypeDisabled,
+ base::ASCIIToUTF16(kDisabledErrorMessage)));
return;
}
- context_->RegisterServiceWorker(
+ if (!CanRegisterServiceWorker(
+ provider_host->document_url(), pattern, script_url)) {
+ Send(new ServiceWorkerMsg_ServiceWorkerRegistrationError(
+ thread_id,
+ request_id,
+ WebServiceWorkerError::ErrorTypeSecurity,
+ base::ASCIIToUTF16(kDomainMismatchErrorMessage)));
+ return;
+ }
+ GetContext()->RegisterServiceWorker(
pattern,
script_url,
+ render_process_id_,
+ provider_host,
base::Bind(&ServiceWorkerDispatcherHost::RegistrationComplete,
this,
thread_id,
@@ -117,22 +213,44 @@ void ServiceWorkerDispatcherHost::OnRegisterServiceWorker(
}
void ServiceWorkerDispatcherHost::OnUnregisterServiceWorker(
- int32 thread_id,
- int32 request_id,
+ int thread_id,
+ int request_id,
+ int provider_id,
const GURL& pattern) {
- // TODO(alecflett): This check is insufficient for release. Add a
- // ServiceWorker-specific policy query in
- // ChildProcessSecurityImpl. See http://crbug.com/311631.
- if (!context_ || !context_->IsEnabled()) {
+ if (!GetContext() || !ServiceWorkerUtils::IsFeatureEnabled()) {
Send(new ServiceWorkerMsg_ServiceWorkerRegistrationError(
thread_id,
request_id,
- blink::WebServiceWorkerError::DisabledError,
- ASCIIToUTF16(kDisabledErrorMessage)));
+ blink::WebServiceWorkerError::ErrorTypeDisabled,
+ base::ASCIIToUTF16(kDisabledErrorMessage)));
return;
}
- context_->UnregisterServiceWorker(
+ ServiceWorkerProviderHost* provider_host = GetContext()->GetProviderHost(
+ render_process_id_, provider_id);
+ if (!provider_host) {
+ BadMessageReceived();
+ return;
+ }
+ if (!provider_host->IsContextAlive()) {
+ Send(new ServiceWorkerMsg_ServiceWorkerRegistrationError(
+ thread_id,
+ request_id,
+ blink::WebServiceWorkerError::ErrorTypeDisabled,
+ base::ASCIIToUTF16(kDisabledErrorMessage)));
+ return;
+ }
+
+ if (!CanUnregisterServiceWorker(provider_host->document_url(), pattern)) {
+ Send(new ServiceWorkerMsg_ServiceWorkerRegistrationError(
+ thread_id,
+ request_id,
+ WebServiceWorkerError::ErrorTypeSecurity,
+ base::ASCIIToUTF16(kDomainMismatchErrorMessage)));
+ return;
+ }
+
+ GetContext()->UnregisterServiceWorker(
pattern,
base::Bind(&ServiceWorkerDispatcherHost::UnregistrationComplete,
this,
@@ -140,47 +258,183 @@ void ServiceWorkerDispatcherHost::OnUnregisterServiceWorker(
request_id));
}
+void ServiceWorkerDispatcherHost::OnPostMessageToWorker(
+ int handle_id,
+ const base::string16& message,
+ const std::vector<int>& sent_message_port_ids) {
+ if (!GetContext() || !ServiceWorkerUtils::IsFeatureEnabled())
+ return;
+
+ ServiceWorkerHandle* handle = handles_.Lookup(handle_id);
+ if (!handle) {
+ BadMessageReceived();
+ return;
+ }
+
+ std::vector<int> new_routing_ids;
+ message_port_message_filter_->UpdateMessagePortsWithNewRoutes(
+ sent_message_port_ids, &new_routing_ids);
+ handle->version()->SendMessage(
+ ServiceWorkerMsg_MessageToWorker(message,
+ sent_message_port_ids,
+ new_routing_ids),
+ base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
+}
+
void ServiceWorkerDispatcherHost::OnProviderCreated(int provider_id) {
- if (!context_)
+ if (!GetContext())
return;
- if (context_->GetProviderHost(render_process_id_, provider_id)) {
+ if (GetContext()->GetProviderHost(render_process_id_, provider_id)) {
BadMessageReceived();
return;
}
scoped_ptr<ServiceWorkerProviderHost> provider_host(
- new ServiceWorkerProviderHost(render_process_id_, provider_id));
- context_->AddProviderHost(provider_host.Pass());
+ new ServiceWorkerProviderHost(
+ render_process_id_, provider_id, GetContext()->AsWeakPtr(), this));
+ GetContext()->AddProviderHost(provider_host.Pass());
}
void ServiceWorkerDispatcherHost::OnProviderDestroyed(int provider_id) {
- if (!context_)
+ if (!GetContext())
return;
- if (!context_->GetProviderHost(render_process_id_, provider_id)) {
+ if (!GetContext()->GetProviderHost(render_process_id_, provider_id)) {
BadMessageReceived();
return;
}
- context_->RemoveProviderHost(render_process_id_, provider_id);
+ GetContext()->RemoveProviderHost(render_process_id_, provider_id);
+}
+
+void ServiceWorkerDispatcherHost::OnSetHostedVersionId(
+ int provider_id, int64 version_id) {
+ if (!GetContext())
+ return;
+ ServiceWorkerProviderHost* provider_host =
+ GetContext()->GetProviderHost(render_process_id_, provider_id);
+ if (!provider_host) {
+ BadMessageReceived();
+ return;
+ }
+ if (!provider_host->IsContextAlive())
+ return;
+ if (!provider_host->SetHostedVersionId(version_id))
+ BadMessageReceived();
}
void ServiceWorkerDispatcherHost::RegistrationComplete(
- int32 thread_id,
- int32 request_id,
- ServiceWorkerRegistrationStatus status,
- int64 registration_id) {
- if (status != REGISTRATION_OK) {
+ int thread_id,
+ int request_id,
+ ServiceWorkerStatusCode status,
+ int64 registration_id,
+ int64 version_id) {
+ if (!GetContext())
+ return;
+
+ if (status != SERVICE_WORKER_OK) {
SendRegistrationError(thread_id, request_id, status);
return;
}
+ ServiceWorkerVersion* version = GetContext()->GetLiveVersion(version_id);
+ DCHECK(version);
+ DCHECK_EQ(registration_id, version->registration_id());
+ scoped_ptr<ServiceWorkerHandle> handle =
+ ServiceWorkerHandle::Create(GetContext()->AsWeakPtr(),
+ this, thread_id, version);
Send(new ServiceWorkerMsg_ServiceWorkerRegistered(
- thread_id, request_id, registration_id));
+ thread_id, request_id, handle->GetObjectInfo()));
+ RegisterServiceWorkerHandle(handle.Pass());
+}
+
+// TODO(nhiroki): These message handlers that take |embedded_worker_id| as an
+// input should check if the worker refers to the live context. If the context
+// was deleted, handle the messege gracefully (http://crbug.com/371675).
+void ServiceWorkerDispatcherHost::OnWorkerScriptLoaded(int embedded_worker_id) {
+ if (!GetContext())
+ return;
+ GetContext()->embedded_worker_registry()->OnWorkerScriptLoaded(
+ render_process_id_, embedded_worker_id);
+}
+
+void ServiceWorkerDispatcherHost::OnWorkerScriptLoadFailed(
+ int embedded_worker_id) {
+ if (!GetContext())
+ return;
+ GetContext()->embedded_worker_registry()->OnWorkerScriptLoadFailed(
+ render_process_id_, embedded_worker_id);
+}
+
+void ServiceWorkerDispatcherHost::OnWorkerStarted(
+ int thread_id, int embedded_worker_id) {
+ if (!GetContext())
+ return;
+ GetContext()->embedded_worker_registry()->OnWorkerStarted(
+ render_process_id_, thread_id, embedded_worker_id);
+}
+
+void ServiceWorkerDispatcherHost::OnWorkerStopped(int embedded_worker_id) {
+ if (!GetContext())
+ return;
+ GetContext()->embedded_worker_registry()->OnWorkerStopped(
+ render_process_id_, embedded_worker_id);
+}
+
+void ServiceWorkerDispatcherHost::OnReportException(
+ int embedded_worker_id,
+ const base::string16& error_message,
+ int line_number,
+ int column_number,
+ const GURL& source_url) {
+ if (!GetContext())
+ return;
+ GetContext()->embedded_worker_registry()->OnReportException(
+ embedded_worker_id,
+ error_message,
+ line_number,
+ column_number,
+ source_url);
+}
+
+void ServiceWorkerDispatcherHost::OnReportConsoleMessage(
+ int embedded_worker_id,
+ const EmbeddedWorkerHostMsg_ReportConsoleMessage_Params& params) {
+ if (!GetContext())
+ return;
+ GetContext()->embedded_worker_registry()->OnReportConsoleMessage(
+ embedded_worker_id,
+ params.source_identifier,
+ params.message_level,
+ params.message,
+ params.line_number,
+ params.source_url);
+}
+
+void ServiceWorkerDispatcherHost::OnIncrementServiceWorkerRefCount(
+ int handle_id) {
+ ServiceWorkerHandle* handle = handles_.Lookup(handle_id);
+ if (!handle) {
+ BadMessageReceived();
+ return;
+ }
+ handle->IncrementRefCount();
+}
+
+void ServiceWorkerDispatcherHost::OnDecrementServiceWorkerRefCount(
+ int handle_id) {
+ ServiceWorkerHandle* handle = handles_.Lookup(handle_id);
+ if (!handle) {
+ BadMessageReceived();
+ return;
+ }
+ handle->DecrementRefCount();
+ if (handle->HasNoRefCount())
+ handles_.Remove(handle_id);
}
void ServiceWorkerDispatcherHost::UnregistrationComplete(
- int32 thread_id,
- int32 request_id,
- ServiceWorkerRegistrationStatus status) {
- if (status != REGISTRATION_OK) {
+ int thread_id,
+ int request_id,
+ ServiceWorkerStatusCode status) {
+ if (status != SERVICE_WORKER_OK) {
SendRegistrationError(thread_id, request_id, status);
return;
}
@@ -189,9 +443,9 @@ void ServiceWorkerDispatcherHost::UnregistrationComplete(
}
void ServiceWorkerDispatcherHost::SendRegistrationError(
- int32 thread_id,
- int32 request_id,
- ServiceWorkerRegistrationStatus status) {
+ int thread_id,
+ int request_id,
+ ServiceWorkerStatusCode status) {
base::string16 error_message;
blink::WebServiceWorkerError::ErrorType error_type;
GetServiceWorkerRegistrationStatusResponse(
@@ -200,4 +454,8 @@ void ServiceWorkerDispatcherHost::SendRegistrationError(
thread_id, request_id, error_type, error_message));
}
+ServiceWorkerContextCore* ServiceWorkerDispatcherHost::GetContext() {
+ return context_wrapper_->context();
+}
+
} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_dispatcher_host.h b/chromium/content/browser/service_worker/service_worker_dispatcher_host.h
index be008018ec5..b269311497b 100644
--- a/chromium/content/browser/service_worker/service_worker_dispatcher_host.h
+++ b/chromium/content/browser/service_worker/service_worker_dispatcher_host.h
@@ -5,28 +5,50 @@
#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_DISPATCHER_HOST_H_
#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_DISPATCHER_HOST_H_
+#include "base/id_map.h"
#include "base/memory/weak_ptr.h"
+#include "base/strings/string16.h"
#include "content/browser/service_worker/service_worker_registration_status.h"
#include "content/public/browser/browser_message_filter.h"
class GURL;
+struct EmbeddedWorkerHostMsg_ReportConsoleMessage_Params;
namespace content {
+class MessagePortMessageFilter;
class ServiceWorkerContextCore;
class ServiceWorkerContextWrapper;
+class ServiceWorkerHandle;
class ServiceWorkerProviderHost;
+class ServiceWorkerRegistration;
class CONTENT_EXPORT ServiceWorkerDispatcherHost : public BrowserMessageFilter {
public:
- explicit ServiceWorkerDispatcherHost(int render_process_id);
+ ServiceWorkerDispatcherHost(
+ int render_process_id,
+ MessagePortMessageFilter* message_port_message_filter);
void Init(ServiceWorkerContextWrapper* context_wrapper);
- // BrowserIOMessageFilter implementation
+ // BrowserMessageFilter implementation
+ virtual void OnFilterAdded(IPC::Sender* sender) OVERRIDE;
virtual void OnDestruct() const OVERRIDE;
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+
+ // IPC::Sender implementation
+
+ // Send() queues the message until the underlying sender is ready. This
+ // class assumes that Send() can only fail after that when the renderer
+ // process has terminated, at which point the whole instance will eventually
+ // be destroyed.
+ virtual bool Send(IPC::Message* message) OVERRIDE;
+
+ void RegisterServiceWorkerHandle(scoped_ptr<ServiceWorkerHandle> handle);
+
+ MessagePortMessageFilter* message_port_message_filter() {
+ return message_port_message_filter_;
+ }
protected:
virtual ~ServiceWorkerDispatcherHost();
@@ -37,31 +59,68 @@ class CONTENT_EXPORT ServiceWorkerDispatcherHost : public BrowserMessageFilter {
friend class TestingServiceWorkerDispatcherHost;
// IPC Message handlers
- void OnRegisterServiceWorker(int32 thread_id,
- int32 request_id,
+ void OnRegisterServiceWorker(int thread_id,
+ int request_id,
+ int provider_id,
const GURL& pattern,
const GURL& script_url);
- void OnUnregisterServiceWorker(int32 thread_id,
- int32 request_id,
+ void OnUnregisterServiceWorker(int thread_id,
+ int request_id,
+ int provider_id,
const GURL& pattern);
void OnProviderCreated(int provider_id);
void OnProviderDestroyed(int provider_id);
+ void OnSetHostedVersionId(int provider_id, int64 version_id);
+ void OnWorkerScriptLoaded(int embedded_worker_id);
+ void OnWorkerScriptLoadFailed(int embedded_worker_id);
+ void OnWorkerStarted(int thread_id,
+ int embedded_worker_id);
+ void OnWorkerStopped(int embedded_worker_id);
+ void OnReportException(int embedded_worker_id,
+ const base::string16& error_message,
+ int line_number,
+ int column_number,
+ const GURL& source_url);
+ void OnReportConsoleMessage(
+ int embedded_worker_id,
+ const EmbeddedWorkerHostMsg_ReportConsoleMessage_Params& params);
+ void OnPostMessage(int handle_id,
+ const base::string16& message,
+ const std::vector<int>& sent_message_port_ids);
+ void OnIncrementServiceWorkerRefCount(int handle_id);
+ void OnDecrementServiceWorkerRefCount(int handle_id);
+ void OnPostMessageToWorker(int handle_id,
+ const base::string16& message,
+ const std::vector<int>& sent_message_port_ids);
+ void OnServiceWorkerObjectDestroyed(int handle_id);
// Callbacks from ServiceWorkerContextCore
- void RegistrationComplete(int32 thread_id,
- int32 request_id,
- ServiceWorkerRegistrationStatus status,
- int64 registration_id);
-
- void UnregistrationComplete(int32 thread_id,
- int32 request_id,
- ServiceWorkerRegistrationStatus status);
-
- void SendRegistrationError(int32 thread_id,
- int32 request_id,
- ServiceWorkerRegistrationStatus status);
+ void RegistrationComplete(int thread_id,
+ int request_id,
+ ServiceWorkerStatusCode status,
+ int64 registration_id,
+ int64 version_id);
+
+ void UnregistrationComplete(int thread_id,
+ int request_id,
+ ServiceWorkerStatusCode status);
+
+ void SendRegistrationError(int thread_id,
+ int request_id,
+ ServiceWorkerStatusCode status);
+
+ ServiceWorkerContextCore* GetContext();
+
int render_process_id_;
- base::WeakPtr<ServiceWorkerContextCore> context_;
+ MessagePortMessageFilter* const message_port_message_filter_;
+ scoped_refptr<ServiceWorkerContextWrapper> context_wrapper_;
+
+ IDMap<ServiceWorkerHandle, IDMapOwnPointer> handles_;
+
+ bool channel_ready_; // True after BrowserMessageFilter::sender_ != NULL.
+ ScopedVector<IPC::Message> pending_messages_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServiceWorkerDispatcherHost);
};
} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc b/chromium/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc
index da9e83c8ed8..079c8de65fb 100644
--- a/chromium/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc
+++ b/chromium/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc
@@ -8,108 +8,178 @@
#include "base/files/file_path.h"
#include "base/run_loop.h"
#include "content/browser/browser_thread_impl.h"
+#include "content/browser/service_worker/embedded_worker_instance.h"
+#include "content/browser/service_worker/embedded_worker_registry.h"
+#include "content/browser/service_worker/embedded_worker_test_helper.h"
#include "content/browser/service_worker/service_worker_context_core.h"
#include "content/browser/service_worker/service_worker_context_wrapper.h"
-#include "content/common/service_worker_messages.h"
+#include "content/common/service_worker/embedded_worker_messages.h"
+#include "content/common/service_worker/service_worker_messages.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
-class ServiceWorkerDispatcherHostTest : public testing::Test {
- protected:
- ServiceWorkerDispatcherHostTest()
- : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {}
-
- virtual void SetUp() {
- context_wrapper_ = new ServiceWorkerContextWrapper;
- context_wrapper_->Init(base::FilePath(), NULL);
- }
-
- virtual void TearDown() {
- if (context_wrapper_) {
- context_wrapper_->Shutdown();
- context_wrapper_ = NULL;
- }
- }
-
- ServiceWorkerContextCore* context() { return context_wrapper_->context(); }
-
- TestBrowserThreadBundle browser_thread_bundle_;
- scoped_refptr<ServiceWorkerContextWrapper> context_wrapper_;
-};
-
static const int kRenderProcessId = 1;
class TestingServiceWorkerDispatcherHost : public ServiceWorkerDispatcherHost {
public:
TestingServiceWorkerDispatcherHost(
int process_id,
- ServiceWorkerContextWrapper* context_wrapper)
- : ServiceWorkerDispatcherHost(process_id),
- bad_messages_received_count_(0) {
+ ServiceWorkerContextWrapper* context_wrapper,
+ EmbeddedWorkerTestHelper* helper)
+ : ServiceWorkerDispatcherHost(process_id, NULL),
+ bad_messages_received_count_(0),
+ helper_(helper) {
Init(context_wrapper);
}
virtual bool Send(IPC::Message* message) OVERRIDE {
- sent_messages_.push_back(message);
- return true;
+ return helper_->Send(message);
}
+ IPC::TestSink* ipc_sink() { return helper_->ipc_sink(); }
+
virtual void BadMessageReceived() OVERRIDE {
++bad_messages_received_count_;
}
- ScopedVector<IPC::Message> sent_messages_;
int bad_messages_received_count_;
protected:
+ EmbeddedWorkerTestHelper* helper_;
virtual ~TestingServiceWorkerDispatcherHost() {}
};
+class ServiceWorkerDispatcherHostTest : public testing::Test {
+ protected:
+ ServiceWorkerDispatcherHostTest()
+ : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {}
+
+ virtual void SetUp() {
+ helper_.reset(new EmbeddedWorkerTestHelper(kRenderProcessId));
+ dispatcher_host_ = new TestingServiceWorkerDispatcherHost(
+ kRenderProcessId, context_wrapper(), helper_.get());
+ }
+
+ virtual void TearDown() {
+ helper_.reset();
+ }
+
+ ServiceWorkerContextCore* context() { return helper_->context(); }
+ ServiceWorkerContextWrapper* context_wrapper() {
+ return helper_->context_wrapper();
+ }
+
+ void Register(int64 provider_id,
+ GURL pattern,
+ GURL worker_url,
+ uint32 expected_message) {
+ dispatcher_host_->OnMessageReceived(
+ ServiceWorkerHostMsg_RegisterServiceWorker(
+ -1, -1, provider_id, pattern, worker_url));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(dispatcher_host_->ipc_sink()->GetUniqueMessageMatching(
+ expected_message));
+ dispatcher_host_->ipc_sink()->ClearMessages();
+ }
+
+ void Unregister(int64 provider_id, GURL pattern, uint32 expected_message) {
+ dispatcher_host_->OnMessageReceived(
+ ServiceWorkerHostMsg_UnregisterServiceWorker(
+ -1, -1, provider_id, pattern));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(dispatcher_host_->ipc_sink()->GetUniqueMessageMatching(
+ expected_message));
+ dispatcher_host_->ipc_sink()->ClearMessages();
+ }
+
+ TestBrowserThreadBundle browser_thread_bundle_;
+ scoped_ptr<EmbeddedWorkerTestHelper> helper_;
+ scoped_refptr<TestingServiceWorkerDispatcherHost> dispatcher_host_;
+};
+
TEST_F(ServiceWorkerDispatcherHostTest, DisabledCausesError) {
DCHECK(!CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableServiceWorker));
- scoped_refptr<TestingServiceWorkerDispatcherHost> dispatcher_host =
- new TestingServiceWorkerDispatcherHost(kRenderProcessId,
- context_wrapper_.get());
-
- bool handled;
- dispatcher_host->OnMessageReceived(
- ServiceWorkerHostMsg_RegisterServiceWorker(-1, -1, GURL(), GURL()),
- &handled);
- EXPECT_TRUE(handled);
+ dispatcher_host_->OnMessageReceived(
+ ServiceWorkerHostMsg_RegisterServiceWorker(-1, -1, -1, GURL(), GURL()));
// TODO(alecflett): Pump the message loop when this becomes async.
- ASSERT_EQ(1UL, dispatcher_host->sent_messages_.size());
- EXPECT_EQ(
- static_cast<uint32>(ServiceWorkerMsg_ServiceWorkerRegistrationError::ID),
- dispatcher_host->sent_messages_[0]->type());
+ ASSERT_EQ(1UL, dispatcher_host_->ipc_sink()->message_count());
+ EXPECT_TRUE(dispatcher_host_->ipc_sink()->GetUniqueMessageMatching(
+ ServiceWorkerMsg_ServiceWorkerRegistrationError::ID));
}
-TEST_F(ServiceWorkerDispatcherHostTest, Enabled) {
+// TODO(falken): Enable this test when we remove the
+// --enable-service-worker-flag (see crbug.com/352581)
+TEST_F(ServiceWorkerDispatcherHostTest, DISABLED_RegisterSameOrigin) {
+ const int64 kProviderId = 99; // Dummy value
+ scoped_ptr<ServiceWorkerProviderHost> host(new ServiceWorkerProviderHost(
+ kRenderProcessId, kProviderId, context()->AsWeakPtr(), NULL));
+ host->SetDocumentUrl(GURL("http://www.example.com/foo"));
+ base::WeakPtr<ServiceWorkerProviderHost> provider_host = host->AsWeakPtr();
+ context()->AddProviderHost(host.Pass());
+
+ Register(kProviderId,
+ GURL("http://www.example.com/*"),
+ GURL("http://foo.example.com/bar"),
+ ServiceWorkerMsg_ServiceWorkerRegistrationError::ID);
+ Register(kProviderId,
+ GURL("http://foo.example.com/*"),
+ GURL("http://www.example.com/bar"),
+ ServiceWorkerMsg_ServiceWorkerRegistrationError::ID);
+ Register(kProviderId,
+ GURL("http://foo.example.com/*"),
+ GURL("http://foo.example.com/bar"),
+ ServiceWorkerMsg_ServiceWorkerRegistrationError::ID);
+ Register(kProviderId,
+ GURL("http://www.example.com/*"),
+ GURL("http://www.example.com/bar"),
+ ServiceWorkerMsg_ServiceWorkerRegistered::ID);
+}
+
+// TODO(falken): Enable this test when we remove the
+// --enable-service-worker-flag (see crbug.com/352581)
+TEST_F(ServiceWorkerDispatcherHostTest, DISABLED_UnregisterSameOrigin) {
+ const int64 kProviderId = 99; // Dummy value
+ scoped_ptr<ServiceWorkerProviderHost> host(new ServiceWorkerProviderHost(
+ kRenderProcessId, kProviderId, context()->AsWeakPtr(), NULL));
+ host->SetDocumentUrl(GURL("http://www.example.com/foo"));
+ base::WeakPtr<ServiceWorkerProviderHost> provider_host = host->AsWeakPtr();
+ context()->AddProviderHost(host.Pass());
+
+ Unregister(kProviderId,
+ GURL("http://foo.example.com/*"),
+ ServiceWorkerMsg_ServiceWorkerRegistrationError::ID);
+ Unregister(kProviderId,
+ GURL("http://www.example.com/*"),
+ ServiceWorkerMsg_ServiceWorkerUnregistered::ID);
+}
+
+// Disable this since now we cache command-line switch in
+// ServiceWorkerUtils::IsFeatureEnabled() and this could be flaky depending
+// on testing order. (crbug.com/352581)
+// TODO(kinuko): Just remove DisabledCausesError test above and enable
+// this test when we remove the --enable-service-worker flag.
+TEST_F(ServiceWorkerDispatcherHostTest, DISABLED_Enabled) {
DCHECK(!CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableServiceWorker));
CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kEnableServiceWorker);
- scoped_refptr<TestingServiceWorkerDispatcherHost> dispatcher_host =
- new TestingServiceWorkerDispatcherHost(kRenderProcessId,
- context_wrapper_.get());
-
- bool handled;
- dispatcher_host->OnMessageReceived(
- ServiceWorkerHostMsg_RegisterServiceWorker(-1, -1, GURL(), GURL()),
- &handled);
- EXPECT_TRUE(handled);
+ dispatcher_host_->OnMessageReceived(
+ ServiceWorkerHostMsg_RegisterServiceWorker(-1, -1, -1, GURL(), GURL()));
base::RunLoop().RunUntilIdle();
// TODO(alecflett): Pump the message loop when this becomes async.
- ASSERT_EQ(1UL, dispatcher_host->sent_messages_.size());
- EXPECT_EQ(static_cast<uint32>(ServiceWorkerMsg_ServiceWorkerRegistered::ID),
- dispatcher_host->sent_messages_[0]->type());
+ ASSERT_EQ(2UL, dispatcher_host_->ipc_sink()->message_count());
+ EXPECT_TRUE(dispatcher_host_->ipc_sink()->GetUniqueMessageMatching(
+ EmbeddedWorkerMsg_StartWorker::ID));
+ EXPECT_TRUE(dispatcher_host_->ipc_sink()->GetUniqueMessageMatching(
+ ServiceWorkerMsg_ServiceWorkerRegistered::ID));
}
TEST_F(ServiceWorkerDispatcherHostTest, EarlyContextDeletion) {
@@ -118,72 +188,48 @@ TEST_F(ServiceWorkerDispatcherHostTest, EarlyContextDeletion) {
CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kEnableServiceWorker);
- scoped_refptr<TestingServiceWorkerDispatcherHost> dispatcher_host =
- new TestingServiceWorkerDispatcherHost(kRenderProcessId,
- context_wrapper_.get());
+ helper_->ShutdownContext();
- context_wrapper_->Shutdown();
- context_wrapper_ = NULL;
+ // Let the shutdown reach the simulated IO thread.
+ base::RunLoop().RunUntilIdle();
- bool handled;
- dispatcher_host->OnMessageReceived(
- ServiceWorkerHostMsg_RegisterServiceWorker(-1, -1, GURL(), GURL()),
- &handled);
- EXPECT_TRUE(handled);
+ dispatcher_host_->OnMessageReceived(
+ ServiceWorkerHostMsg_RegisterServiceWorker(-1, -1, -1, GURL(), GURL()));
// TODO(alecflett): Pump the message loop when this becomes async.
- ASSERT_EQ(1UL, dispatcher_host->sent_messages_.size());
- EXPECT_EQ(
- static_cast<uint32>(ServiceWorkerMsg_ServiceWorkerRegistrationError::ID),
- dispatcher_host->sent_messages_[0]->type());
+ ASSERT_EQ(1UL, dispatcher_host_->ipc_sink()->message_count());
+ EXPECT_TRUE(dispatcher_host_->ipc_sink()->GetUniqueMessageMatching(
+ ServiceWorkerMsg_ServiceWorkerRegistrationError::ID));
}
TEST_F(ServiceWorkerDispatcherHostTest, ProviderCreatedAndDestroyed) {
- scoped_refptr<TestingServiceWorkerDispatcherHost> dispatcher_host =
- new TestingServiceWorkerDispatcherHost(kRenderProcessId,
- context_wrapper_.get());
-
const int kProviderId = 1001; // Test with a value != kRenderProcessId.
- bool handled = false;
- dispatcher_host->OnMessageReceived(
- ServiceWorkerHostMsg_ProviderCreated(kProviderId),
- &handled);
- EXPECT_TRUE(handled);
+ dispatcher_host_->OnMessageReceived(
+ ServiceWorkerHostMsg_ProviderCreated(kProviderId));
EXPECT_TRUE(context()->GetProviderHost(kRenderProcessId, kProviderId));
// Two with the same ID should be seen as a bad message.
- handled = false;
- dispatcher_host->OnMessageReceived(
- ServiceWorkerHostMsg_ProviderCreated(kProviderId),
- &handled);
- EXPECT_TRUE(handled);
- EXPECT_EQ(1, dispatcher_host->bad_messages_received_count_);
-
- handled = false;
- dispatcher_host->OnMessageReceived(
- ServiceWorkerHostMsg_ProviderDestroyed(kProviderId),
- &handled);
- EXPECT_TRUE(handled);
+ dispatcher_host_->OnMessageReceived(
+ ServiceWorkerHostMsg_ProviderCreated(kProviderId));
+ EXPECT_EQ(1, dispatcher_host_->bad_messages_received_count_);
+
+ dispatcher_host_->OnMessageReceived(
+ ServiceWorkerHostMsg_ProviderDestroyed(kProviderId));
EXPECT_FALSE(context()->GetProviderHost(kRenderProcessId, kProviderId));
// Destroying an ID that does not exist warrants a bad message.
- handled = false;
- dispatcher_host->OnMessageReceived(
- ServiceWorkerHostMsg_ProviderDestroyed(kProviderId),
- &handled);
- EXPECT_TRUE(handled);
- EXPECT_EQ(2, dispatcher_host->bad_messages_received_count_);
+ dispatcher_host_->OnMessageReceived(
+ ServiceWorkerHostMsg_ProviderDestroyed(kProviderId));
+ EXPECT_EQ(2, dispatcher_host_->bad_messages_received_count_);
// Deletion of the dispatcher_host should cause providers for that
// process to get deleted as well.
- dispatcher_host->OnMessageReceived(
- ServiceWorkerHostMsg_ProviderCreated(kProviderId),
- &handled);
- EXPECT_TRUE(handled);
+ dispatcher_host_->OnMessageReceived(
+ ServiceWorkerHostMsg_ProviderCreated(kProviderId));
EXPECT_TRUE(context()->GetProviderHost(kRenderProcessId, kProviderId));
- EXPECT_TRUE(dispatcher_host->HasOneRef());
- dispatcher_host = NULL;
+ EXPECT_TRUE(dispatcher_host_->HasOneRef());
+ dispatcher_host_ = NULL;
EXPECT_FALSE(context()->GetProviderHost(kRenderProcessId, kProviderId));
}
diff --git a/chromium/content/browser/service_worker/service_worker_fetch_dispatcher.cc b/chromium/content/browser/service_worker/service_worker_fetch_dispatcher.cc
new file mode 100644
index 00000000000..81be9aff2a7
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_fetch_dispatcher.cc
@@ -0,0 +1,78 @@
+// 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/service_worker/service_worker_fetch_dispatcher.h"
+
+#include "base/bind.h"
+#include "content/browser/service_worker/service_worker_version.h"
+#include "net/url_request/url_request.h"
+
+namespace content {
+
+ServiceWorkerFetchDispatcher::ServiceWorkerFetchDispatcher(
+ net::URLRequest* request,
+ ServiceWorkerVersion* version,
+ const FetchCallback& callback)
+ : version_(version),
+ callback_(callback),
+ weak_factory_(this) {
+ request_.url = request->url();
+ request_.method = request->method();
+ const net::HttpRequestHeaders& headers = request->extra_request_headers();
+ for (net::HttpRequestHeaders::Iterator it(headers); it.GetNext();)
+ request_.headers[it.name()] = it.value();
+}
+
+ServiceWorkerFetchDispatcher::~ServiceWorkerFetchDispatcher() {}
+
+void ServiceWorkerFetchDispatcher::Run() {
+ DCHECK(version_->status() == ServiceWorkerVersion::ACTIVATING ||
+ version_->status() == ServiceWorkerVersion::ACTIVE)
+ << version_->status();
+
+ if (version_->status() == ServiceWorkerVersion::ACTIVATING) {
+ version_->RegisterStatusChangeCallback(
+ base::Bind(&ServiceWorkerFetchDispatcher::DidWaitActivation,
+ weak_factory_.GetWeakPtr()));
+ return;
+ }
+ DispatchFetchEvent();
+}
+
+void ServiceWorkerFetchDispatcher::DidWaitActivation() {
+ if (version_->status() != ServiceWorkerVersion::ACTIVE) {
+ DCHECK_EQ(ServiceWorkerVersion::INSTALLED, version_->status());
+ DidFailActivation();
+ return;
+ }
+ DispatchFetchEvent();
+}
+
+void ServiceWorkerFetchDispatcher::DidFailActivation() {
+ // The previous activation seems to have failed, abort the step
+ // with activate error. (The error should be separately reported
+ // to the associated documents and association must be dropped
+ // at this point)
+ DidFinish(SERVICE_WORKER_ERROR_ACTIVATE_WORKER_FAILED,
+ SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK,
+ ServiceWorkerResponse());
+}
+
+void ServiceWorkerFetchDispatcher::DispatchFetchEvent() {
+ version_->DispatchFetchEvent(
+ request_,
+ base::Bind(&ServiceWorkerFetchDispatcher::DidFinish,
+ weak_factory_.GetWeakPtr()));
+}
+
+void ServiceWorkerFetchDispatcher::DidFinish(
+ ServiceWorkerStatusCode status,
+ ServiceWorkerFetchEventResult fetch_result,
+ const ServiceWorkerResponse& response) {
+ DCHECK(!callback_.is_null());
+ FetchCallback callback = callback_;
+ callback.Run(status, fetch_result, response);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_fetch_dispatcher.h b/chromium/content/browser/service_worker/service_worker_fetch_dispatcher.h
new file mode 100644
index 00000000000..9e595ab0870
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_fetch_dispatcher.h
@@ -0,0 +1,57 @@
+// 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_SERVICE_WORKER_SERVICE_WORKER_FETCH_DISPATCHER_H_
+#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_FETCH_DISPATCHER_H_
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "content/common/service_worker/service_worker_status_code.h"
+#include "content/common/service_worker/service_worker_types.h"
+
+namespace net {
+class URLRequest;
+}
+
+namespace content {
+
+class ServiceWorkerVersion;
+
+// A helper class to dispatch fetch event to a service worker.
+class ServiceWorkerFetchDispatcher {
+ public:
+ typedef base::Callback<void(ServiceWorkerStatusCode,
+ ServiceWorkerFetchEventResult,
+ const ServiceWorkerResponse&)> FetchCallback;
+
+ ServiceWorkerFetchDispatcher(
+ net::URLRequest* request,
+ ServiceWorkerVersion* version,
+ const FetchCallback& callback);
+ ~ServiceWorkerFetchDispatcher();
+
+ // Dispatches a fetch event to the |version| given in ctor, and fires
+ // |callback| (also given in ctor) when finishes.
+ void Run();
+
+ private:
+ void DidWaitActivation();
+ void DidFailActivation();
+ void DispatchFetchEvent();
+ void DidFinish(ServiceWorkerStatusCode status,
+ ServiceWorkerFetchEventResult fetch_result,
+ const ServiceWorkerResponse& response);
+
+ scoped_refptr<ServiceWorkerVersion> version_;
+ FetchCallback callback_;
+ ServiceWorkerFetchRequest request_;
+ base::WeakPtrFactory<ServiceWorkerFetchDispatcher> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServiceWorkerFetchDispatcher);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_FETCH_DISPATCHER_H_
diff --git a/chromium/content/browser/service_worker/service_worker_handle.cc b/chromium/content/browser/service_worker/service_worker_handle.cc
new file mode 100644
index 00000000000..a07ab9e9d91
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_handle.cc
@@ -0,0 +1,126 @@
+// 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/service_worker/service_worker_handle.h"
+
+#include "content/browser/service_worker/service_worker_context_core.h"
+#include "content/browser/service_worker/service_worker_registration.h"
+#include "content/common/service_worker/service_worker_messages.h"
+#include "ipc/ipc_sender.h"
+
+namespace content {
+
+namespace {
+
+blink::WebServiceWorkerState
+GetWebServiceWorkerState(ServiceWorkerVersion* version) {
+ DCHECK(version);
+ switch (version->status()) {
+ case ServiceWorkerVersion::NEW:
+ if (version->running_status() == ServiceWorkerVersion::RUNNING)
+ return blink::WebServiceWorkerStateParsed;
+ else
+ return blink::WebServiceWorkerStateUnknown;
+ case ServiceWorkerVersion::INSTALLING:
+ return blink::WebServiceWorkerStateInstalling;
+ case ServiceWorkerVersion::INSTALLED:
+ return blink::WebServiceWorkerStateInstalled;
+ case ServiceWorkerVersion::ACTIVATING:
+ return blink::WebServiceWorkerStateActivating;
+ case ServiceWorkerVersion::ACTIVE:
+ return blink::WebServiceWorkerStateActive;
+ case ServiceWorkerVersion::DEACTIVATED:
+ return blink::WebServiceWorkerStateDeactivated;
+ }
+ NOTREACHED() << version->status();
+ return blink::WebServiceWorkerStateUnknown;
+}
+
+} // namespace
+
+scoped_ptr<ServiceWorkerHandle> ServiceWorkerHandle::Create(
+ base::WeakPtr<ServiceWorkerContextCore> context,
+ IPC::Sender* sender,
+ int thread_id,
+ ServiceWorkerVersion* version) {
+ if (!context || !version)
+ return scoped_ptr<ServiceWorkerHandle>();
+ ServiceWorkerRegistration* registration =
+ context->GetLiveRegistration(version->registration_id());
+ return make_scoped_ptr(
+ new ServiceWorkerHandle(context, sender, thread_id,
+ registration, version));
+}
+
+ServiceWorkerHandle::ServiceWorkerHandle(
+ base::WeakPtr<ServiceWorkerContextCore> context,
+ IPC::Sender* sender,
+ int thread_id,
+ ServiceWorkerRegistration* registration,
+ ServiceWorkerVersion* version)
+ : context_(context),
+ sender_(sender),
+ thread_id_(thread_id),
+ handle_id_(context.get() ? context->GetNewServiceWorkerHandleId() : -1),
+ ref_count_(1),
+ registration_(registration),
+ version_(version) {
+ version_->AddListener(this);
+}
+
+ServiceWorkerHandle::~ServiceWorkerHandle() {
+ version_->RemoveListener(this);
+ // TODO(kinuko): At this point we can discard the registration if
+ // all documents/handles that have a reference to the registration is
+ // closed or freed up, but could also keep it alive in cache
+ // (e.g. in context_) for a while with some timer so that we don't
+ // need to re-load the same registration from disk over and over.
+}
+
+void ServiceWorkerHandle::OnWorkerStarted(ServiceWorkerVersion* version) {
+}
+
+void ServiceWorkerHandle::OnWorkerStopped(ServiceWorkerVersion* version) {
+}
+
+void ServiceWorkerHandle::OnErrorReported(ServiceWorkerVersion* version,
+ const base::string16& error_message,
+ int line_number,
+ int column_number,
+ const GURL& source_url) {
+}
+
+void ServiceWorkerHandle::OnReportConsoleMessage(ServiceWorkerVersion* version,
+ int source_identifier,
+ int message_level,
+ const base::string16& message,
+ int line_number,
+ const GURL& source_url) {
+}
+
+void ServiceWorkerHandle::OnVersionStateChanged(ServiceWorkerVersion* version) {
+ sender_->Send(new ServiceWorkerMsg_ServiceWorkerStateChanged(
+ thread_id_, handle_id_, GetWebServiceWorkerState(version)));
+}
+
+ServiceWorkerObjectInfo ServiceWorkerHandle::GetObjectInfo() {
+ ServiceWorkerObjectInfo info;
+ info.handle_id = handle_id_;
+ info.scope = registration_->pattern();
+ info.url = registration_->script_url();
+ info.state = GetWebServiceWorkerState(version_);
+ return info;
+}
+
+void ServiceWorkerHandle::IncrementRefCount() {
+ DCHECK_GT(ref_count_, 0);
+ ++ref_count_;
+}
+
+void ServiceWorkerHandle::DecrementRefCount() {
+ DCHECK_GE(ref_count_, 0);
+ --ref_count_;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_handle.h b/chromium/content/browser/service_worker/service_worker_handle.h
new file mode 100644
index 00000000000..81d5b3574a2
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_handle.h
@@ -0,0 +1,92 @@
+// 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_SERVICE_WORKER_SERVICE_WORKER_HANDLE_H_
+#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_HANDLE_H_
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "content/browser/service_worker/service_worker_version.h"
+#include "content/common/content_export.h"
+#include "content/common/service_worker/service_worker_types.h"
+
+namespace IPC {
+class Sender;
+}
+
+namespace content {
+
+class ServiceWorkerContextCore;
+class ServiceWorkerRegistration;
+
+// Roughly corresponds to one ServiceWorker object in the renderer process
+// (WebServiceWorkerImpl).
+// Has references to the corresponding ServiceWorkerVersion and
+// ServiceWorkerRegistration (therefore they're guaranteed to be alive while
+// this handle is around).
+class CONTENT_EXPORT ServiceWorkerHandle
+ : public ServiceWorkerVersion::Listener {
+ public:
+ // Creates a handle for a live version. The version's corresponding
+ // registration must be also alive.
+ // This may return NULL if |context|.get() or |version| is NULL.
+ // |sender| and |thread_id| will be used to send messages to the
+ // corresponding WebServiceWorkerImpl (which should live on |thread_id|
+ // in the child process).
+ static scoped_ptr<ServiceWorkerHandle> Create(
+ base::WeakPtr<ServiceWorkerContextCore> context,
+ IPC::Sender* sender,
+ int thread_id,
+ ServiceWorkerVersion* version);
+
+ ServiceWorkerHandle(base::WeakPtr<ServiceWorkerContextCore> context,
+ IPC::Sender* sender,
+ int thread_id,
+ ServiceWorkerRegistration* registration,
+ ServiceWorkerVersion* version);
+ virtual ~ServiceWorkerHandle();
+
+ // ServiceWorkerVersion::Listener overrides.
+ virtual void OnWorkerStarted(ServiceWorkerVersion* version) OVERRIDE;
+ virtual void OnWorkerStopped(ServiceWorkerVersion* version) OVERRIDE;
+ virtual void OnErrorReported(ServiceWorkerVersion* version,
+ const base::string16& error_message,
+ int line_number,
+ int column_number,
+ const GURL& source_url) OVERRIDE;
+ virtual void OnReportConsoleMessage(ServiceWorkerVersion* version,
+ int source_identifier,
+ int message_level,
+ const base::string16& message,
+ int line_number,
+ const GURL& source_url) OVERRIDE;
+ virtual void OnVersionStateChanged(ServiceWorkerVersion* version) OVERRIDE;
+
+ ServiceWorkerObjectInfo GetObjectInfo();
+
+ ServiceWorkerRegistration* registration() { return registration_.get(); }
+ ServiceWorkerVersion* version() { return version_.get(); }
+ int handle_id() const { return handle_id_; }
+
+ bool HasNoRefCount() const { return ref_count_ <= 0; }
+ void IncrementRefCount();
+ void DecrementRefCount();
+
+ private:
+ base::WeakPtr<ServiceWorkerContextCore> context_;
+ IPC::Sender* sender_; // Not owned, it should always outlive this.
+ const int thread_id_;
+ const int handle_id_;
+ int ref_count_; // Created with 1.
+ scoped_refptr<ServiceWorkerRegistration> registration_;
+ scoped_refptr<ServiceWorkerVersion> version_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServiceWorkerHandle);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_HANDLE_H_
diff --git a/chromium/content/browser/service_worker/service_worker_handle_unittest.cc b/chromium/content/browser/service_worker/service_worker_handle_unittest.cc
new file mode 100644
index 00000000000..70c445613c9
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_handle_unittest.cc
@@ -0,0 +1,114 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "base/run_loop.h"
+#include "content/browser/service_worker/embedded_worker_registry.h"
+#include "content/browser/service_worker/embedded_worker_test_helper.h"
+#include "content/browser/service_worker/service_worker_context_core.h"
+#include "content/browser/service_worker/service_worker_handle.h"
+#include "content/browser/service_worker/service_worker_registration.h"
+#include "content/browser/service_worker/service_worker_test_utils.h"
+#include "content/browser/service_worker/service_worker_version.h"
+#include "content/common/service_worker/embedded_worker_messages.h"
+#include "content/common/service_worker/service_worker_messages.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "ipc/ipc_message.h"
+#include "ipc/ipc_test_sink.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/WebServiceWorkerState.h"
+
+namespace content {
+
+namespace {
+
+const int kRenderProcessId = 88; // A dummy ID for testing.
+
+void VerifyStateChangedMessage(int expected_handle_id,
+ blink::WebServiceWorkerState expected_state,
+ const IPC::Message* message) {
+ ASSERT_TRUE(message != NULL);
+ ServiceWorkerMsg_ServiceWorkerStateChanged::Param param;
+ ASSERT_TRUE(ServiceWorkerMsg_ServiceWorkerStateChanged::Read(
+ message, &param));
+ EXPECT_EQ(expected_handle_id, param.b);
+ EXPECT_EQ(expected_state, param.c);
+}
+
+} // namespace
+
+class ServiceWorkerHandleTest : public testing::Test {
+ public:
+ ServiceWorkerHandleTest()
+ : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {}
+
+ virtual void SetUp() OVERRIDE {
+ helper_.reset(new EmbeddedWorkerTestHelper(kRenderProcessId));
+
+ registration_ = new ServiceWorkerRegistration(
+ GURL("http://www.example.com/*"),
+ GURL("http://www.example.com/service_worker.js"),
+ 1L,
+ helper_->context()->AsWeakPtr());
+ version_ = new ServiceWorkerVersion(
+ registration_, 1L, helper_->context()->AsWeakPtr());
+
+ // Simulate adding one process to the worker.
+ int embedded_worker_id = version_->embedded_worker()->embedded_worker_id();
+ helper_->SimulateAddProcessToWorker(embedded_worker_id, kRenderProcessId);
+ }
+
+ virtual void TearDown() OVERRIDE {
+ registration_ = NULL;
+ version_ = NULL;
+ helper_.reset();
+ }
+
+ IPC::TestSink* ipc_sink() { return helper_->ipc_sink(); }
+
+ TestBrowserThreadBundle browser_thread_bundle_;
+ scoped_ptr<EmbeddedWorkerTestHelper> helper_;
+ scoped_refptr<ServiceWorkerRegistration> registration_;
+ scoped_refptr<ServiceWorkerVersion> version_;
+ DISALLOW_COPY_AND_ASSIGN(ServiceWorkerHandleTest);
+};
+
+TEST_F(ServiceWorkerHandleTest, OnVersionStateChanged) {
+ scoped_ptr<ServiceWorkerHandle> handle =
+ ServiceWorkerHandle::Create(helper_->context()->AsWeakPtr(),
+ helper_.get(),
+ 1 /* thread_id */,
+ version_);
+
+ // Start the worker, and then...
+ ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
+ version_->StartWorker(CreateReceiverOnCurrentThread(&status));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(SERVICE_WORKER_OK, status);
+
+ // ...dispatch install event.
+ status = SERVICE_WORKER_ERROR_FAILED;
+ version_->DispatchInstallEvent(-1, CreateReceiverOnCurrentThread(&status));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(SERVICE_WORKER_OK, status);
+
+ ASSERT_EQ(4UL, ipc_sink()->message_count());
+
+ // We should be sending 1. StartWorker,
+ EXPECT_EQ(EmbeddedWorkerMsg_StartWorker::ID,
+ ipc_sink()->GetMessageAt(0)->type());
+ // 2. StateChanged (state == Installing),
+ VerifyStateChangedMessage(handle->handle_id(),
+ blink::WebServiceWorkerStateInstalling,
+ ipc_sink()->GetMessageAt(1));
+ // 3. SendMessageToWorker (to send InstallEvent), and
+ EXPECT_EQ(EmbeddedWorkerContextMsg_MessageToWorker::ID,
+ ipc_sink()->GetMessageAt(2)->type());
+ // 4. StateChanged (state == Installed).
+ VerifyStateChangedMessage(handle->handle_id(),
+ blink::WebServiceWorkerStateInstalled,
+ ipc_sink()->GetMessageAt(3));
+}
+
+} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_histograms.cc b/chromium/content/browser/service_worker/service_worker_histograms.cc
new file mode 100644
index 00000000000..8d1df4bef4b
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_histograms.cc
@@ -0,0 +1,30 @@
+// 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/service_worker/service_worker_histograms.h"
+
+#include "base/metrics/histogram.h"
+
+namespace content {
+
+// static
+void ServiceWorkerHistograms::CountInitDiskCacheResult(bool result) {
+ UMA_HISTOGRAM_BOOLEAN("ServiceWorker.DiskCache.InitResult", result);
+}
+
+// static
+void ServiceWorkerHistograms::CountReadResponseResult(
+ ServiceWorkerHistograms::ReadResponseResult result) {
+ UMA_HISTOGRAM_ENUMERATION("ServiceWorker.DiskCache.ReadResponseResult",
+ result, NUM_READ_RESPONSE_RESULT_TYPES);
+}
+
+// static
+void ServiceWorkerHistograms::CountWriteResponseResult(
+ ServiceWorkerHistograms::WriteResponseResult result) {
+ UMA_HISTOGRAM_ENUMERATION("ServiceWorker.DiskCache.WriteResponseResult",
+ result, NUM_WRITE_RESPONSE_RESULT_TYPES);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_histograms.h b/chromium/content/browser/service_worker/service_worker_histograms.h
new file mode 100644
index 00000000000..a9ad8c6ef06
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_histograms.h
@@ -0,0 +1,38 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_HISTOGRAMS_H_
+#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_HISTOGRAMS_H_
+
+#include "base/macros.h"
+
+namespace content {
+
+class ServiceWorkerHistograms {
+ public:
+ enum ReadResponseResult {
+ READ_OK,
+ READ_HEADERS_ERROR,
+ READ_DATA_ERROR,
+ NUM_READ_RESPONSE_RESULT_TYPES,
+ };
+
+ enum WriteResponseResult {
+ WRITE_OK,
+ WRITE_HEADERS_ERROR,
+ WRITE_DATA_ERROR,
+ NUM_WRITE_RESPONSE_RESULT_TYPES,
+ };
+
+ static void CountInitDiskCacheResult(bool result);
+ static void CountReadResponseResult(ReadResponseResult result);
+ static void CountWriteResponseResult(WriteResponseResult result);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ServiceWorkerHistograms);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_HISTOGRAMS_H_
diff --git a/chromium/content/browser/service_worker/service_worker_info.cc b/chromium/content/browser/service_worker/service_worker_info.cc
new file mode 100644
index 00000000000..8933340c2ff
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_info.cc
@@ -0,0 +1,57 @@
+// 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/service_worker/service_worker_info.h"
+
+#include "content/common/service_worker/service_worker_types.h"
+#include "ipc/ipc_message.h"
+
+namespace content {
+
+ServiceWorkerVersionInfo::ServiceWorkerVersionInfo()
+ : is_null(true),
+ running_status(ServiceWorkerVersion::STOPPED),
+ status(ServiceWorkerVersion::NEW),
+ version_id(kInvalidServiceWorkerVersionId),
+ process_id(-1),
+ thread_id(-1),
+ devtools_agent_route_id(MSG_ROUTING_NONE) {
+}
+
+ServiceWorkerVersionInfo::ServiceWorkerVersionInfo(
+ ServiceWorkerVersion::RunningStatus running_status,
+ ServiceWorkerVersion::Status status,
+ int64 version_id,
+ int process_id,
+ int thread_id,
+ int devtools_agent_route_id)
+ : is_null(false),
+ running_status(running_status),
+ status(status),
+ version_id(version_id),
+ process_id(process_id),
+ thread_id(thread_id),
+ devtools_agent_route_id(devtools_agent_route_id) {
+}
+
+ServiceWorkerVersionInfo::~ServiceWorkerVersionInfo() {}
+
+ServiceWorkerRegistrationInfo::ServiceWorkerRegistrationInfo() {}
+
+ServiceWorkerRegistrationInfo::ServiceWorkerRegistrationInfo(
+ const GURL& script_url,
+ const GURL& pattern,
+ int64 registration_id,
+ const ServiceWorkerVersionInfo& active_version,
+ const ServiceWorkerVersionInfo& waiting_version)
+ : script_url(script_url),
+ pattern(pattern),
+ registration_id(registration_id),
+ active_version(active_version),
+ waiting_version(waiting_version) {
+}
+
+ServiceWorkerRegistrationInfo::~ServiceWorkerRegistrationInfo() {}
+
+} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_info.h b/chromium/content/browser/service_worker/service_worker_info.h
new file mode 100644
index 00000000000..99e374696b0
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_info.h
@@ -0,0 +1,56 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_INFO_H_
+#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_INFO_H_
+
+#include <vector>
+
+#include "content/browser/service_worker/service_worker_version.h"
+#include "content/common/content_export.h"
+#include "url/gurl.h"
+
+namespace content {
+
+class CONTENT_EXPORT ServiceWorkerVersionInfo {
+ public:
+ ServiceWorkerVersionInfo();
+ ServiceWorkerVersionInfo(ServiceWorkerVersion::RunningStatus running_status,
+ ServiceWorkerVersion::Status status,
+ int64 version_id,
+ int process_id,
+ int thread_id,
+ int devtools_agent_route_id);
+ ~ServiceWorkerVersionInfo();
+
+ bool is_null;
+ ServiceWorkerVersion::RunningStatus running_status;
+ ServiceWorkerVersion::Status status;
+ int64 version_id;
+ int process_id;
+ int thread_id;
+ int devtools_agent_route_id;
+};
+
+class CONTENT_EXPORT ServiceWorkerRegistrationInfo {
+ public:
+ ServiceWorkerRegistrationInfo();
+ ServiceWorkerRegistrationInfo(
+ const GURL& script_url,
+ const GURL& pattern,
+ int64 registration_id,
+ const ServiceWorkerVersionInfo& active_version,
+ const ServiceWorkerVersionInfo& waiting_version);
+ ~ServiceWorkerRegistrationInfo();
+
+ GURL script_url;
+ GURL pattern;
+ int64 registration_id;
+ ServiceWorkerVersionInfo active_version;
+ ServiceWorkerVersionInfo waiting_version;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_INFO_H_
diff --git a/chromium/content/browser/service_worker/service_worker_internals_ui.cc b/chromium/content/browser/service_worker/service_worker_internals_ui.cc
new file mode 100644
index 00000000000..e976ad82884
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_internals_ui.cc
@@ -0,0 +1,675 @@
+// 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/service_worker/service_worker_internals_ui.h"
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/memory/scoped_vector.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/values.h"
+#include "content/browser/devtools/devtools_manager_impl.h"
+#include "content/browser/devtools/embedded_worker_devtools_manager.h"
+#include "content/browser/service_worker/service_worker_context_observer.h"
+#include "content/browser/service_worker/service_worker_context_wrapper.h"
+#include "content/browser/service_worker/service_worker_registration.h"
+#include "content/browser/service_worker/service_worker_version.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/common/url_constants.h"
+#include "grit/content_resources.h"
+
+using base::DictionaryValue;
+using base::FundamentalValue;
+using base::ListValue;
+using base::StringValue;
+using base::Value;
+using base::WeakPtr;
+
+namespace content {
+
+namespace {
+
+void OperationCompleteCallback(WeakPtr<ServiceWorkerInternalsUI> internals,
+ int callback_id,
+ ServiceWorkerStatusCode status) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(OperationCompleteCallback, internals, callback_id, status));
+ return;
+ }
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (internals) {
+ internals->web_ui()->CallJavascriptFunction(
+ "serviceworker.onOperationComplete",
+ FundamentalValue(static_cast<int>(status)),
+ FundamentalValue(callback_id));
+ }
+}
+
+void CallServiceWorkerVersionMethodWithVersionID(
+ ServiceWorkerInternalsUI::ServiceWorkerVersionMethod method,
+ scoped_refptr<ServiceWorkerContextWrapper> context,
+ int64 version_id,
+ const ServiceWorkerInternalsUI::StatusCallback& callback) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(CallServiceWorkerVersionMethodWithVersionID,
+ method,
+ context,
+ version_id,
+ callback));
+ return;
+ }
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ scoped_refptr<ServiceWorkerVersion> version =
+ context->context()->GetLiveVersion(version_id);
+ if (!version) {
+ callback.Run(SERVICE_WORKER_ERROR_NOT_FOUND);
+ return;
+ }
+ (*version.*method)(callback);
+}
+
+void DispatchPushEventWithVersionID(
+ scoped_refptr<ServiceWorkerContextWrapper> context,
+ int64 version_id,
+ const ServiceWorkerInternalsUI::StatusCallback& callback) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(DispatchPushEventWithVersionID,
+ context,
+ version_id,
+ callback));
+ return;
+ }
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ scoped_refptr<ServiceWorkerVersion> version =
+ context->context()->GetLiveVersion(version_id);
+ if (!version) {
+ callback.Run(SERVICE_WORKER_ERROR_NOT_FOUND);
+ return;
+ }
+ std::string data = "Test push message from ServiceWorkerInternals.";
+ version->DispatchPushEvent(callback, data);
+}
+
+void UnregisterWithScope(
+ scoped_refptr<ServiceWorkerContextWrapper> context,
+ const GURL& scope,
+ const ServiceWorkerInternalsUI::StatusCallback& callback) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(UnregisterWithScope, context, scope, callback));
+ return;
+ }
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ context->context()->UnregisterServiceWorker(scope, callback);
+}
+
+void WorkerStarted(const scoped_refptr<ServiceWorkerRegistration>& registration,
+ const ServiceWorkerInternalsUI::StatusCallback& callback,
+ ServiceWorkerStatusCode status) {
+ callback.Run(status);
+}
+
+void StartActiveWorker(
+ const ServiceWorkerInternalsUI::StatusCallback& callback,
+ ServiceWorkerStatusCode status,
+ const scoped_refptr<ServiceWorkerRegistration>& registration) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ if (status == SERVICE_WORKER_OK) {
+ // Pass the reference of |registration| to WorkerStarted callback to prevent
+ // it from being deleted while starting the worker. If the refcount of
+ // |registration| is 1, it will be deleted after WorkerStarted is called.
+ registration->active_version()->StartWorker(
+ base::Bind(WorkerStarted, registration, callback));
+ return;
+ }
+ callback.Run(SERVICE_WORKER_ERROR_NOT_FOUND);
+}
+
+void FindRegistrationForPattern(
+ scoped_refptr<ServiceWorkerContextWrapper> context,
+ const GURL& scope,
+ const ServiceWorkerStorage::FindRegistrationCallback callback) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(FindRegistrationForPattern, context, scope, callback));
+ return;
+ }
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ context->context()->storage()->FindRegistrationForPattern(scope, callback);
+}
+
+void UpdateVersionInfo(const ServiceWorkerVersionInfo& version,
+ DictionaryValue* info) {
+ switch (version.running_status) {
+ case ServiceWorkerVersion::STOPPED:
+ info->SetString("running_status", "STOPPED");
+ break;
+ case ServiceWorkerVersion::STARTING:
+ info->SetString("running_status", "STARTING");
+ break;
+ case ServiceWorkerVersion::RUNNING:
+ info->SetString("running_status", "RUNNING");
+ break;
+ case ServiceWorkerVersion::STOPPING:
+ info->SetString("running_status", "STOPPING");
+ break;
+ }
+
+ switch (version.status) {
+ case ServiceWorkerVersion::NEW:
+ info->SetString("status", "NEW");
+ break;
+ case ServiceWorkerVersion::INSTALLING:
+ info->SetString("status", "INSTALLING");
+ break;
+ case ServiceWorkerVersion::INSTALLED:
+ info->SetString("status", "INSTALLED");
+ break;
+ case ServiceWorkerVersion::ACTIVATING:
+ info->SetString("status", "ACTIVATING");
+ break;
+ case ServiceWorkerVersion::ACTIVE:
+ info->SetString("status", "ACTIVE");
+ break;
+ case ServiceWorkerVersion::DEACTIVATED:
+ info->SetString("status", "DEACTIVATED");
+ break;
+ }
+ info->SetString("version_id", base::Int64ToString(version.version_id));
+ info->SetInteger("process_id", version.process_id);
+ info->SetInteger("thread_id", version.thread_id);
+ info->SetInteger("devtools_agent_route_id", version.devtools_agent_route_id);
+}
+
+ListValue* GetRegistrationListValue(
+ const std::vector<ServiceWorkerRegistrationInfo>& registrations) {
+ ListValue* result = new ListValue();
+ for (std::vector<ServiceWorkerRegistrationInfo>::const_iterator it =
+ registrations.begin();
+ it != registrations.end();
+ ++it) {
+ const ServiceWorkerRegistrationInfo& registration = *it;
+ DictionaryValue* registration_info = new DictionaryValue();
+ registration_info->SetString("scope", registration.pattern.spec());
+ registration_info->SetString("script_url", registration.script_url.spec());
+ registration_info->SetString(
+ "registration_id", base::Int64ToString(registration.registration_id));
+
+ if (!registration.active_version.is_null) {
+ DictionaryValue* active_info = new DictionaryValue();
+ UpdateVersionInfo(registration.active_version, active_info);
+ registration_info->Set("active", active_info);
+ }
+
+ if (!registration.waiting_version.is_null) {
+ DictionaryValue* waiting_info = new DictionaryValue();
+ UpdateVersionInfo(registration.waiting_version, waiting_info);
+ registration_info->Set("waiting", waiting_info);
+ }
+
+ result->Append(registration_info);
+ }
+ return result;
+}
+
+ListValue* GetVersionListValue(
+ const std::vector<ServiceWorkerVersionInfo>& versions) {
+ ListValue* result = new ListValue();
+ for (std::vector<ServiceWorkerVersionInfo>::const_iterator it =
+ versions.begin();
+ it != versions.end();
+ ++it) {
+ DictionaryValue* info = new DictionaryValue();
+ UpdateVersionInfo(*it, info);
+ result->Append(info);
+ }
+ return result;
+}
+
+void GetRegistrationsOnIOThread(
+ scoped_refptr<ServiceWorkerContextWrapper> context,
+ base::Callback<void(const std::vector<ServiceWorkerRegistrationInfo>&)>
+ callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ context->context()->storage()->GetAllRegistrations(callback);
+}
+
+void OnStoredRegistrations(
+ scoped_refptr<ServiceWorkerContextWrapper> context,
+ base::Callback<void(const std::vector<ServiceWorkerRegistrationInfo>&,
+ const std::vector<ServiceWorkerVersionInfo>&,
+ const std::vector<ServiceWorkerRegistrationInfo>&)>
+ callback,
+ const std::vector<ServiceWorkerRegistrationInfo>& stored_registrations) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(callback,
+ context->context()->GetAllLiveRegistrationInfo(),
+ context->context()->GetAllLiveVersionInfo(),
+ stored_registrations));
+}
+
+void OnAllRegistrations(
+ WeakPtr<ServiceWorkerInternalsUI> internals,
+ int partition_id,
+ const base::FilePath& context_path,
+ const std::vector<ServiceWorkerRegistrationInfo>& live_registrations,
+ const std::vector<ServiceWorkerVersionInfo>& live_versions,
+ const std::vector<ServiceWorkerRegistrationInfo>& stored_registrations) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (!internals)
+ return;
+
+ ScopedVector<const Value> args;
+ args.push_back(GetRegistrationListValue(live_registrations));
+ args.push_back(GetVersionListValue(live_versions));
+ args.push_back(GetRegistrationListValue(stored_registrations));
+ args.push_back(new FundamentalValue(partition_id));
+ args.push_back(new StringValue(context_path.value()));
+ internals->web_ui()->CallJavascriptFunction("serviceworker.onPartitionData",
+ args.get());
+}
+
+} // namespace
+
+class ServiceWorkerInternalsUI::PartitionObserver
+ : public ServiceWorkerContextObserver {
+ public:
+ PartitionObserver(int partition_id, WebUI* web_ui)
+ : partition_id_(partition_id), web_ui_(web_ui) {}
+ virtual ~PartitionObserver() {}
+ // ServiceWorkerContextObserver overrides:
+ virtual void OnWorkerStarted(int64 version_id,
+ int process_id,
+ int thread_id) OVERRIDE {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ web_ui_->CallJavascriptFunction(
+ "serviceworker.onWorkerStarted",
+ FundamentalValue(partition_id_),
+ StringValue(base::Int64ToString(version_id)),
+ FundamentalValue(process_id),
+ FundamentalValue(thread_id));
+ }
+ virtual void OnWorkerStopped(int64 version_id,
+ int process_id,
+ int thread_id) OVERRIDE {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ web_ui_->CallJavascriptFunction(
+ "serviceworker.onWorkerStopped",
+ FundamentalValue(partition_id_),
+ StringValue(base::Int64ToString(version_id)),
+ FundamentalValue(process_id),
+ FundamentalValue(thread_id));
+ }
+ virtual void OnVersionStateChanged(int64 version_id) OVERRIDE {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ web_ui_->CallJavascriptFunction(
+ "serviceworker.onVersionStateChanged",
+ FundamentalValue(partition_id_),
+ StringValue(base::Int64ToString(version_id)));
+ }
+ virtual void OnErrorReported(int64 version_id,
+ int process_id,
+ int thread_id,
+ const ErrorInfo& info) OVERRIDE {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ ScopedVector<const Value> args;
+ args.push_back(new FundamentalValue(partition_id_));
+ args.push_back(new StringValue(base::Int64ToString(version_id)));
+ args.push_back(new FundamentalValue(process_id));
+ args.push_back(new FundamentalValue(thread_id));
+ scoped_ptr<DictionaryValue> value(new DictionaryValue());
+ value->SetString("message", info.error_message);
+ value->SetInteger("lineNumber", info.line_number);
+ value->SetInteger("columnNumber", info.column_number);
+ value->SetString("sourceURL", info.source_url.spec());
+ args.push_back(value.release());
+ web_ui_->CallJavascriptFunction("serviceworker.onErrorReported",
+ args.get());
+ }
+ virtual void OnReportConsoleMessage(int64 version_id,
+ int process_id,
+ int thread_id,
+ const ConsoleMessage& message) OVERRIDE {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ ScopedVector<const Value> args;
+ args.push_back(new FundamentalValue(partition_id_));
+ args.push_back(new StringValue(base::Int64ToString(version_id)));
+ args.push_back(new FundamentalValue(process_id));
+ args.push_back(new FundamentalValue(thread_id));
+ scoped_ptr<DictionaryValue> value(new DictionaryValue());
+ value->SetInteger("sourceIdentifier", message.source_identifier);
+ value->SetInteger("message_level", message.message_level);
+ value->SetString("message", message.message);
+ value->SetInteger("lineNumber", message.line_number);
+ value->SetString("sourceURL", message.source_url.spec());
+ args.push_back(value.release());
+ web_ui_->CallJavascriptFunction("serviceworker.onConsoleMessageReported",
+ args.get());
+ }
+ virtual void OnRegistrationStored(const GURL& pattern) OVERRIDE {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ web_ui_->CallJavascriptFunction("serviceworker.onRegistrationStored",
+ StringValue(pattern.spec()));
+ }
+ virtual void OnRegistrationDeleted(const GURL& pattern) OVERRIDE {
+ web_ui_->CallJavascriptFunction("serviceworker.onRegistrationDeleted",
+ StringValue(pattern.spec()));
+ }
+ int partition_id() const { return partition_id_; }
+
+ private:
+ const int partition_id_;
+ WebUI* const web_ui_;
+};
+
+ServiceWorkerInternalsUI::ServiceWorkerInternalsUI(WebUI* web_ui)
+ : WebUIController(web_ui), next_partition_id_(0) {
+ WebUIDataSource* source =
+ WebUIDataSource::Create(kChromeUIServiceWorkerInternalsHost);
+ source->SetUseJsonJSFormatV2();
+ source->SetJsonPath("strings.js");
+ source->AddResourcePath("serviceworker_internals.js",
+ IDR_SERVICE_WORKER_INTERNALS_JS);
+ source->AddResourcePath("serviceworker_internals.css",
+ IDR_SERVICE_WORKER_INTERNALS_CSS);
+ source->SetDefaultResource(IDR_SERVICE_WORKER_INTERNALS_HTML);
+ source->DisableDenyXFrameOptions();
+
+ BrowserContext* browser_context =
+ web_ui->GetWebContents()->GetBrowserContext();
+ WebUIDataSource::Add(browser_context, source);
+
+ web_ui->RegisterMessageCallback(
+ "GetOptions",
+ base::Bind(&ServiceWorkerInternalsUI::GetOptions,
+ base::Unretained(this)));
+ web_ui->RegisterMessageCallback(
+ "SetOption",
+ base::Bind(&ServiceWorkerInternalsUI::SetOption, base::Unretained(this)));
+ web_ui->RegisterMessageCallback(
+ "getAllRegistrations",
+ base::Bind(&ServiceWorkerInternalsUI::GetAllRegistrations,
+ base::Unretained(this)));
+ web_ui->RegisterMessageCallback(
+ "stop",
+ base::Bind(&ServiceWorkerInternalsUI::CallServiceWorkerVersionMethod,
+ base::Unretained(this),
+ &ServiceWorkerVersion::StopWorker));
+ web_ui->RegisterMessageCallback(
+ "sync",
+ base::Bind(&ServiceWorkerInternalsUI::CallServiceWorkerVersionMethod,
+ base::Unretained(this),
+ &ServiceWorkerVersion::DispatchSyncEvent));
+ web_ui->RegisterMessageCallback(
+ "push",
+ base::Bind(&ServiceWorkerInternalsUI::DispatchPushEvent,
+ base::Unretained(this)));
+ web_ui->RegisterMessageCallback(
+ "inspect",
+ base::Bind(&ServiceWorkerInternalsUI::InspectWorker,
+ base::Unretained(this)));
+ web_ui->RegisterMessageCallback(
+ "unregister",
+ base::Bind(&ServiceWorkerInternalsUI::Unregister,
+ base::Unretained(this)));
+ web_ui->RegisterMessageCallback(
+ "start",
+ base::Bind(&ServiceWorkerInternalsUI::StartWorker,
+ base::Unretained(this)));
+}
+
+ServiceWorkerInternalsUI::~ServiceWorkerInternalsUI() {
+ BrowserContext* browser_context =
+ web_ui()->GetWebContents()->GetBrowserContext();
+ // Safe to use base::Unretained(this) because
+ // ForEachStoragePartition is synchronous.
+ BrowserContext::StoragePartitionCallback remove_observer_cb =
+ base::Bind(&ServiceWorkerInternalsUI::RemoveObserverFromStoragePartition,
+ base::Unretained(this));
+ BrowserContext::ForEachStoragePartition(browser_context, remove_observer_cb);
+}
+
+void ServiceWorkerInternalsUI::GetOptions(const ListValue* args) {
+ DictionaryValue options;
+ options.SetBoolean("debug_on_start",
+ EmbeddedWorkerDevToolsManager::GetInstance()
+ ->debug_service_worker_on_start());
+ web_ui()->CallJavascriptFunction("serviceworker.onOptions", options);
+}
+
+void ServiceWorkerInternalsUI::SetOption(const ListValue* args) {
+ std::string option_name;
+ bool option_boolean;
+ if (!args->GetString(0, &option_name) || option_name != "debug_on_start" ||
+ !args->GetBoolean(1, &option_boolean)) {
+ return;
+ }
+ EmbeddedWorkerDevToolsManager::GetInstance()
+ ->set_debug_service_worker_on_start(option_boolean);
+}
+
+void ServiceWorkerInternalsUI::GetAllRegistrations(const ListValue* args) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ BrowserContext* browser_context =
+ web_ui()->GetWebContents()->GetBrowserContext();
+ // Safe to use base::Unretained(this) because
+ // ForEachStoragePartition is synchronous.
+ BrowserContext::StoragePartitionCallback add_context_cb =
+ base::Bind(&ServiceWorkerInternalsUI::AddContextFromStoragePartition,
+ base::Unretained(this));
+ BrowserContext::ForEachStoragePartition(browser_context, add_context_cb);
+}
+
+void ServiceWorkerInternalsUI::AddContextFromStoragePartition(
+ StoragePartition* partition) {
+ int partition_id = 0;
+ scoped_refptr<ServiceWorkerContextWrapper> context =
+ static_cast<ServiceWorkerContextWrapper*>(
+ partition->GetServiceWorkerContext());
+ if (PartitionObserver* observer =
+ observers_.get(reinterpret_cast<uintptr_t>(partition))) {
+ partition_id = observer->partition_id();
+ } else {
+ partition_id = next_partition_id_++;
+ scoped_ptr<PartitionObserver> new_observer(
+ new PartitionObserver(partition_id, web_ui()));
+ context->AddObserver(new_observer.get());
+ observers_.set(reinterpret_cast<uintptr_t>(partition), new_observer.Pass());
+ }
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(GetRegistrationsOnIOThread,
+ context,
+ base::Bind(OnStoredRegistrations,
+ context,
+ base::Bind(OnAllRegistrations,
+ AsWeakPtr(),
+ partition_id,
+ partition->GetPath()))));
+}
+
+void ServiceWorkerInternalsUI::RemoveObserverFromStoragePartition(
+ StoragePartition* partition) {
+ scoped_ptr<PartitionObserver> observer(
+ observers_.take_and_erase(reinterpret_cast<uintptr_t>(partition)));
+ if (!observer.get())
+ return;
+ scoped_refptr<ServiceWorkerContextWrapper> context =
+ static_cast<ServiceWorkerContextWrapper*>(
+ partition->GetServiceWorkerContext());
+ context->RemoveObserver(observer.get());
+}
+
+void ServiceWorkerInternalsUI::FindContext(
+ int partition_id,
+ StoragePartition** result_partition,
+ StoragePartition* storage_partition) const {
+ PartitionObserver* observer =
+ observers_.get(reinterpret_cast<uintptr_t>(storage_partition));
+ if (observer && partition_id == observer->partition_id()) {
+ *result_partition = storage_partition;
+ }
+}
+
+bool ServiceWorkerInternalsUI::GetServiceWorkerContext(
+ int partition_id,
+ scoped_refptr<ServiceWorkerContextWrapper>* context) const {
+ BrowserContext* browser_context =
+ web_ui()->GetWebContents()->GetBrowserContext();
+ StoragePartition* result_partition(NULL);
+ BrowserContext::StoragePartitionCallback find_context_cb =
+ base::Bind(&ServiceWorkerInternalsUI::FindContext,
+ base::Unretained(this),
+ partition_id,
+ &result_partition);
+ BrowserContext::ForEachStoragePartition(browser_context, find_context_cb);
+ if (!result_partition)
+ return false;
+ *context = static_cast<ServiceWorkerContextWrapper*>(
+ result_partition->GetServiceWorkerContext());
+ return true;
+}
+
+void ServiceWorkerInternalsUI::CallServiceWorkerVersionMethod(
+ ServiceWorkerVersionMethod method,
+ const ListValue* args) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ int callback_id;
+ int partition_id;
+ int64 version_id;
+ std::string version_id_string;
+ const DictionaryValue* cmd_args = NULL;
+ scoped_refptr<ServiceWorkerContextWrapper> context;
+ if (!args->GetInteger(0, &callback_id) ||
+ !args->GetDictionary(1, &cmd_args) ||
+ !cmd_args->GetInteger("partition_id", &partition_id) ||
+ !GetServiceWorkerContext(partition_id, &context) ||
+ !cmd_args->GetString("version_id", &version_id_string) ||
+ !base::StringToInt64(version_id_string, &version_id)) {
+ return;
+ }
+
+ base::Callback<void(ServiceWorkerStatusCode)> callback =
+ base::Bind(OperationCompleteCallback, AsWeakPtr(), callback_id);
+ CallServiceWorkerVersionMethodWithVersionID(
+ method, context, version_id, callback);
+}
+
+void ServiceWorkerInternalsUI::DispatchPushEvent(
+ const ListValue* args) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ int callback_id;
+ int partition_id;
+ int64 version_id;
+ std::string version_id_string;
+ const DictionaryValue* cmd_args = NULL;
+ scoped_refptr<ServiceWorkerContextWrapper> context;
+ if (!args->GetInteger(0, &callback_id) ||
+ !args->GetDictionary(1, &cmd_args) ||
+ !cmd_args->GetInteger("partition_id", &partition_id) ||
+ !GetServiceWorkerContext(partition_id, &context) ||
+ !cmd_args->GetString("version_id", &version_id_string) ||
+ !base::StringToInt64(version_id_string, &version_id)) {
+ return;
+ }
+
+ base::Callback<void(ServiceWorkerStatusCode)> callback =
+ base::Bind(OperationCompleteCallback, AsWeakPtr(), callback_id);
+ DispatchPushEventWithVersionID(context, version_id, callback);
+}
+
+void ServiceWorkerInternalsUI::InspectWorker(const ListValue* args) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ int callback_id;
+ int process_id;
+ int devtools_agent_route_id;
+ const DictionaryValue* cmd_args = NULL;
+ scoped_refptr<ServiceWorkerContextWrapper> context;
+ if (!args->GetInteger(0, &callback_id) ||
+ !args->GetDictionary(1, &cmd_args) ||
+ !cmd_args->GetInteger("process_id", &process_id) ||
+ !cmd_args->GetInteger("devtools_agent_route_id",
+ &devtools_agent_route_id)) {
+ return;
+ }
+ base::Callback<void(ServiceWorkerStatusCode)> callback =
+ base::Bind(OperationCompleteCallback, AsWeakPtr(), callback_id);
+ scoped_refptr<DevToolsAgentHost> agent_host(
+ EmbeddedWorkerDevToolsManager::GetInstance()
+ ->GetDevToolsAgentHostForWorker(process_id, devtools_agent_route_id));
+ if (!agent_host) {
+ callback.Run(SERVICE_WORKER_ERROR_NOT_FOUND);
+ return;
+ }
+ DevToolsManagerImpl::GetInstance()->Inspect(
+ web_ui()->GetWebContents()->GetBrowserContext(), agent_host.get());
+ callback.Run(SERVICE_WORKER_OK);
+}
+
+void ServiceWorkerInternalsUI::Unregister(const ListValue* args) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ int callback_id;
+ int partition_id;
+ std::string scope_string;
+ const DictionaryValue* cmd_args = NULL;
+ scoped_refptr<ServiceWorkerContextWrapper> context;
+ if (!args->GetInteger(0, &callback_id) ||
+ !args->GetDictionary(1, &cmd_args) ||
+ !cmd_args->GetInteger("partition_id", &partition_id) ||
+ !GetServiceWorkerContext(partition_id, &context) ||
+ !cmd_args->GetString("scope", &scope_string)) {
+ return;
+ }
+
+ base::Callback<void(ServiceWorkerStatusCode)> callback =
+ base::Bind(OperationCompleteCallback, AsWeakPtr(), callback_id);
+ UnregisterWithScope(context, GURL(scope_string), callback);
+}
+
+void ServiceWorkerInternalsUI::StartWorker(const ListValue* args) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ int callback_id;
+ int partition_id;
+ std::string scope_string;
+ const DictionaryValue* cmd_args = NULL;
+ scoped_refptr<ServiceWorkerContextWrapper> context;
+ if (!args->GetInteger(0, &callback_id) ||
+ !args->GetDictionary(1, &cmd_args) ||
+ !cmd_args->GetInteger("partition_id", &partition_id) ||
+ !GetServiceWorkerContext(partition_id, &context) ||
+ !cmd_args->GetString("scope", &scope_string)) {
+ return;
+ }
+
+ base::Callback<void(ServiceWorkerStatusCode)> callback =
+ base::Bind(OperationCompleteCallback, AsWeakPtr(), callback_id);
+ FindRegistrationForPattern(
+ context, GURL(scope_string), base::Bind(StartActiveWorker, callback));
+}
+
+} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_internals_ui.h b/chromium/content/browser/service_worker/service_worker_internals_ui.h
new file mode 100644
index 00000000000..84c1e19204e
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_internals_ui.h
@@ -0,0 +1,74 @@
+// 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_SERVICE_WORKER_SERVICE_WORKER_INTERNALS_UI_H_
+#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_INTERNALS_UI_H_
+
+#include <set>
+
+#include "base/containers/scoped_ptr_hash_map.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "content/browser/service_worker/service_worker_context_observer.h"
+#include "content/common/service_worker/service_worker_status_code.h"
+#include "content/public/browser/web_ui_controller.h"
+
+namespace base {
+class ListValue;
+class DictionaryValue;
+}
+
+namespace content {
+
+class StoragePartition;
+class ServiceWorkerContextWrapper;
+class ServiceWorkerRegistration;
+class ServiceWorkerVersion;
+
+class ServiceWorkerInternalsUI
+ : public WebUIController,
+ public base::SupportsWeakPtr<ServiceWorkerInternalsUI> {
+ public:
+ typedef base::Callback<void(ServiceWorkerStatusCode)> StatusCallback;
+ typedef void (ServiceWorkerVersion::*ServiceWorkerVersionMethod)(
+ const StatusCallback& callback);
+
+ explicit ServiceWorkerInternalsUI(WebUI* web_ui);
+
+ private:
+ class OperationProxy;
+ class PartitionObserver;
+
+ virtual ~ServiceWorkerInternalsUI();
+ void AddContextFromStoragePartition(StoragePartition* partition);
+
+ void RemoveObserverFromStoragePartition(StoragePartition* partition);
+
+ // Called from Javascript.
+ void GetOptions(const base::ListValue* args);
+ void SetOption(const base::ListValue* args);
+ void GetAllRegistrations(const base::ListValue* args);
+ void CallServiceWorkerVersionMethod(ServiceWorkerVersionMethod method,
+ const base::ListValue* args);
+ void DispatchPushEvent(const base::ListValue* args);
+ void InspectWorker(const base::ListValue* args);
+ void Unregister(const base::ListValue* args);
+ void StartWorker(const base::ListValue* args);
+
+ bool GetServiceWorkerContext(
+ int partition_id,
+ scoped_refptr<ServiceWorkerContextWrapper>* context) const;
+ void FindContext(int partition_id,
+ StoragePartition** result_partition,
+ StoragePartition* storage_partition) const;
+
+ base::ScopedPtrHashMap<uintptr_t, PartitionObserver> observers_;
+ int next_partition_id_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_INTERNALS_UI_H_
diff --git a/chromium/content/browser/service_worker/service_worker_job_coordinator.cc b/chromium/content/browser/service_worker/service_worker_job_coordinator.cc
new file mode 100644
index 00000000000..8152b490272
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_job_coordinator.cc
@@ -0,0 +1,112 @@
+// 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/service_worker/service_worker_job_coordinator.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "base/stl_util.h"
+#include "content/browser/service_worker/service_worker_register_job_base.h"
+
+namespace content {
+
+ServiceWorkerJobCoordinator::JobQueue::JobQueue() {}
+
+ServiceWorkerJobCoordinator::JobQueue::~JobQueue() {
+ DCHECK(jobs_.empty()) << "Destroying JobQueue with " << jobs_.size()
+ << " unfinished jobs";
+ STLDeleteElements(&jobs_);
+}
+
+ServiceWorkerRegisterJobBase* ServiceWorkerJobCoordinator::JobQueue::Push(
+ scoped_ptr<ServiceWorkerRegisterJobBase> job) {
+ if (jobs_.empty()) {
+ job->Start();
+ jobs_.push_back(job.release());
+ } else if (!job->Equals(jobs_.back())) {
+ jobs_.push_back(job.release());
+ }
+ // Note we are releasing 'job' here.
+
+ DCHECK(!jobs_.empty());
+ return jobs_.back();
+}
+
+void ServiceWorkerJobCoordinator::JobQueue::Pop(
+ ServiceWorkerRegisterJobBase* job) {
+ DCHECK(job == jobs_.front());
+ jobs_.pop_front();
+ delete job;
+ if (!jobs_.empty())
+ jobs_.front()->Start();
+}
+
+void ServiceWorkerJobCoordinator::JobQueue::AbortAll() {
+ for (size_t i = 0; i < jobs_.size(); ++i)
+ jobs_[i]->Abort();
+ STLDeleteElements(&jobs_);
+}
+
+void ServiceWorkerJobCoordinator::JobQueue::ClearForShutdown() {
+ STLDeleteElements(&jobs_);
+}
+
+ServiceWorkerJobCoordinator::ServiceWorkerJobCoordinator(
+ base::WeakPtr<ServiceWorkerContextCore> context)
+ : context_(context) {
+}
+
+ServiceWorkerJobCoordinator::~ServiceWorkerJobCoordinator() {
+ if (!context_) {
+ for (RegistrationJobMap::iterator it = job_queues_.begin();
+ it != job_queues_.end(); ++it) {
+ it->second.ClearForShutdown();
+ }
+ job_queues_.clear();
+ }
+ DCHECK(job_queues_.empty()) << "Destroying ServiceWorkerJobCoordinator with "
+ << job_queues_.size() << " job queues";
+}
+
+void ServiceWorkerJobCoordinator::Register(
+ const GURL& pattern,
+ const GURL& script_url,
+ int source_process_id,
+ const ServiceWorkerRegisterJob::RegistrationCallback& callback) {
+ scoped_ptr<ServiceWorkerRegisterJobBase> job(
+ new ServiceWorkerRegisterJob(context_, pattern, script_url));
+ ServiceWorkerRegisterJob* queued_job =
+ static_cast<ServiceWorkerRegisterJob*>(
+ job_queues_[pattern].Push(job.Pass()));
+ queued_job->AddCallback(callback, source_process_id);
+}
+
+void ServiceWorkerJobCoordinator::Unregister(
+ const GURL& pattern,
+ const ServiceWorkerUnregisterJob::UnregistrationCallback& callback) {
+ scoped_ptr<ServiceWorkerRegisterJobBase> job(
+ new ServiceWorkerUnregisterJob(context_, pattern));
+ ServiceWorkerUnregisterJob* queued_job =
+ static_cast<ServiceWorkerUnregisterJob*>(
+ job_queues_[pattern].Push(job.Pass()));
+ queued_job->AddCallback(callback);
+}
+
+void ServiceWorkerJobCoordinator::AbortAll() {
+ for (RegistrationJobMap::iterator it = job_queues_.begin();
+ it != job_queues_.end(); ++it) {
+ it->second.AbortAll();
+ }
+ job_queues_.clear();
+}
+
+void ServiceWorkerJobCoordinator::FinishJob(const GURL& pattern,
+ ServiceWorkerRegisterJobBase* job) {
+ RegistrationJobMap::iterator pending_jobs = job_queues_.find(pattern);
+ DCHECK(pending_jobs != job_queues_.end()) << "Deleting non-existent job.";
+ pending_jobs->second.Pop(job);
+ if (pending_jobs->second.empty())
+ job_queues_.erase(pending_jobs);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_job_coordinator.h b/chromium/content/browser/service_worker/service_worker_job_coordinator.h
new file mode 100644
index 00000000000..06a1057d76e
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_job_coordinator.h
@@ -0,0 +1,85 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_JOB_COORDINATOR_H_
+#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_JOB_COORDINATOR_H_
+
+#include <deque>
+#include <map>
+
+#include "content/browser/service_worker/service_worker_register_job.h"
+#include "content/browser/service_worker/service_worker_unregister_job.h"
+#include "content/common/content_export.h"
+#include "url/gurl.h"
+
+namespace content {
+
+class EmbeddedWorkerRegistry;
+class ServiceWorkerRegistration;
+class ServiceWorkerStorage;
+
+// This class manages all in-flight registration or unregistration jobs.
+class CONTENT_EXPORT ServiceWorkerJobCoordinator {
+ public:
+ explicit ServiceWorkerJobCoordinator(
+ base::WeakPtr<ServiceWorkerContextCore> context);
+ ~ServiceWorkerJobCoordinator();
+
+ void Register(const GURL& pattern,
+ const GURL& script_url,
+ int source_process_id,
+ const ServiceWorkerRegisterJob::RegistrationCallback& callback);
+
+ void Unregister(
+ const GURL& pattern,
+ const ServiceWorkerUnregisterJob::UnregistrationCallback& callback);
+
+ // Calls ServiceWorkerRegisterJobBase::Abort() on all jobs and removes them.
+ void AbortAll();
+
+ // Removes the job. A job that was not aborted must call FinishJob when it is
+ // done.
+ void FinishJob(const GURL& pattern, ServiceWorkerRegisterJobBase* job);
+
+ private:
+ class JobQueue {
+ public:
+ JobQueue();
+ ~JobQueue();
+
+ // Adds a job to the queue. If an identical job is already in the queue, no
+ // new job is added. Returns the job in the queue, regardless of whether it
+ // was newly added.
+ ServiceWorkerRegisterJobBase* Push(
+ scoped_ptr<ServiceWorkerRegisterJobBase> job);
+
+ // Removes a job from the queue.
+ void Pop(ServiceWorkerRegisterJobBase* job);
+
+ bool empty() { return jobs_.empty(); }
+
+ // Aborts all jobs in the queue and removes them.
+ void AbortAll();
+
+ // Marks that the browser is shutting down, so jobs may be destroyed before
+ // finishing.
+ void ClearForShutdown();
+
+ private:
+ std::deque<ServiceWorkerRegisterJobBase*> jobs_;
+ };
+
+ typedef std::map<GURL, JobQueue> RegistrationJobMap;
+
+ // The ServiceWorkerContextCore object should always outlive the
+ // job coordinator, the core owns the coordinator.
+ base::WeakPtr<ServiceWorkerContextCore> context_;
+ RegistrationJobMap job_queues_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServiceWorkerJobCoordinator);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_JOB_COORDINATOR_H_
diff --git a/chromium/content/browser/service_worker/service_worker_job_unittest.cc b/chromium/content/browser/service_worker/service_worker_job_unittest.cc
new file mode 100644
index 00000000000..efa8bd1aa1e
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_job_unittest.cc
@@ -0,0 +1,702 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/files/scoped_temp_dir.h"
+#include "base/logging.h"
+#include "base/run_loop.h"
+#include "content/browser/browser_thread_impl.h"
+#include "content/browser/service_worker/embedded_worker_registry.h"
+#include "content/browser/service_worker/embedded_worker_test_helper.h"
+#include "content/browser/service_worker/service_worker_context_core.h"
+#include "content/browser/service_worker/service_worker_job_coordinator.h"
+#include "content/browser/service_worker/service_worker_registration.h"
+#include "content/browser/service_worker/service_worker_registration_status.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "ipc/ipc_test_sink.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Unit tests for testing all job registration tasks.
+namespace content {
+
+namespace {
+
+void SaveRegistrationCallback(
+ ServiceWorkerStatusCode expected_status,
+ bool* called,
+ scoped_refptr<ServiceWorkerRegistration>* registration_out,
+ ServiceWorkerStatusCode status,
+ ServiceWorkerRegistration* registration,
+ ServiceWorkerVersion* version) {
+ ASSERT_TRUE(!version || version->registration_id() == registration->id())
+ << version << " " << registration;
+ EXPECT_EQ(expected_status, status);
+ *called = true;
+ *registration_out = registration;
+}
+
+void SaveFoundRegistrationCallback(
+ ServiceWorkerStatusCode expected_status,
+ bool* called,
+ scoped_refptr<ServiceWorkerRegistration>* registration,
+ ServiceWorkerStatusCode status,
+ const scoped_refptr<ServiceWorkerRegistration>& result) {
+ EXPECT_EQ(expected_status, status);
+ *called = true;
+ *registration = result;
+}
+
+// Creates a callback which both keeps track of if it's been called,
+// as well as the resulting registration. Whent the callback is fired,
+// it ensures that the resulting status matches the expectation.
+// 'called' is useful for making sure a sychronous callback is or
+// isn't called.
+ServiceWorkerRegisterJob::RegistrationCallback SaveRegistration(
+ ServiceWorkerStatusCode expected_status,
+ bool* called,
+ scoped_refptr<ServiceWorkerRegistration>* registration) {
+ *called = false;
+ return base::Bind(
+ &SaveRegistrationCallback, expected_status, called, registration);
+}
+
+ServiceWorkerStorage::FindRegistrationCallback SaveFoundRegistration(
+ ServiceWorkerStatusCode expected_status,
+ bool* called,
+ scoped_refptr<ServiceWorkerRegistration>* registration) {
+ *called = false;
+ return base::Bind(&SaveFoundRegistrationCallback,
+ expected_status,
+ called,
+ registration);
+}
+
+void SaveUnregistrationCallback(ServiceWorkerStatusCode expected_status,
+ bool* called,
+ ServiceWorkerStatusCode status) {
+ EXPECT_EQ(expected_status, status);
+ *called = true;
+}
+
+ServiceWorkerUnregisterJob::UnregistrationCallback SaveUnregistration(
+ ServiceWorkerStatusCode expected_status,
+ bool* called) {
+ *called = false;
+ return base::Bind(&SaveUnregistrationCallback, expected_status, called);
+}
+
+} // namespace
+
+class ServiceWorkerJobTest : public testing::Test {
+ public:
+ ServiceWorkerJobTest()
+ : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP),
+ render_process_id_(88) {}
+
+ virtual void SetUp() OVERRIDE {
+ helper_.reset(new EmbeddedWorkerTestHelper(render_process_id_));
+ }
+
+ virtual void TearDown() OVERRIDE {
+ helper_.reset();
+ }
+
+ ServiceWorkerContextCore* context() const { return helper_->context(); }
+
+ ServiceWorkerJobCoordinator* job_coordinator() const {
+ return context()->job_coordinator();
+ }
+ ServiceWorkerStorage* storage() const { return context()->storage(); }
+
+ protected:
+ TestBrowserThreadBundle browser_thread_bundle_;
+ scoped_ptr<EmbeddedWorkerTestHelper> helper_;
+
+ int render_process_id_;
+};
+
+TEST_F(ServiceWorkerJobTest, SameDocumentSameRegistration) {
+ scoped_refptr<ServiceWorkerRegistration> original_registration;
+ bool called;
+ job_coordinator()->Register(
+ GURL("http://www.example.com/*"),
+ GURL("http://www.example.com/service_worker.js"),
+ render_process_id_,
+ SaveRegistration(SERVICE_WORKER_OK, &called, &original_registration));
+ EXPECT_FALSE(called);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(called);
+
+ scoped_refptr<ServiceWorkerRegistration> registration1;
+ storage()->FindRegistrationForDocument(
+ GURL("http://www.example.com/"),
+ SaveFoundRegistration(SERVICE_WORKER_OK, &called, &registration1));
+ scoped_refptr<ServiceWorkerRegistration> registration2;
+ storage()->FindRegistrationForDocument(
+ GURL("http://www.example.com/"),
+ SaveFoundRegistration(SERVICE_WORKER_OK, &called, &registration2));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(called);
+ ASSERT_TRUE(registration1);
+ ASSERT_EQ(registration1, original_registration);
+ ASSERT_EQ(registration1, registration2);
+}
+
+TEST_F(ServiceWorkerJobTest, SameMatchSameRegistration) {
+ bool called;
+ scoped_refptr<ServiceWorkerRegistration> original_registration;
+ job_coordinator()->Register(
+ GURL("http://www.example.com/*"),
+ GURL("http://www.example.com/service_worker.js"),
+ render_process_id_,
+ SaveRegistration(SERVICE_WORKER_OK, &called, &original_registration));
+ EXPECT_FALSE(called);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(called);
+ ASSERT_NE(static_cast<ServiceWorkerRegistration*>(NULL),
+ original_registration.get());
+
+ scoped_refptr<ServiceWorkerRegistration> registration1;
+ storage()->FindRegistrationForDocument(
+ GURL("http://www.example.com/one"),
+ SaveFoundRegistration(SERVICE_WORKER_OK, &called, &registration1));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(called);
+
+ scoped_refptr<ServiceWorkerRegistration> registration2;
+ storage()->FindRegistrationForDocument(
+ GURL("http://www.example.com/two"),
+ SaveFoundRegistration(SERVICE_WORKER_OK, &called, &registration2));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(called);
+ ASSERT_EQ(registration1, original_registration);
+ ASSERT_EQ(registration1, registration2);
+}
+
+TEST_F(ServiceWorkerJobTest, DifferentMatchDifferentRegistration) {
+ bool called1;
+ scoped_refptr<ServiceWorkerRegistration> original_registration1;
+ job_coordinator()->Register(
+ GURL("http://www.example.com/one/*"),
+ GURL("http://www.example.com/service_worker.js"),
+ render_process_id_,
+ SaveRegistration(SERVICE_WORKER_OK, &called1, &original_registration1));
+
+ bool called2;
+ scoped_refptr<ServiceWorkerRegistration> original_registration2;
+ job_coordinator()->Register(
+ GURL("http://www.example.com/two/*"),
+ GURL("http://www.example.com/service_worker.js"),
+ render_process_id_,
+ SaveRegistration(SERVICE_WORKER_OK, &called2, &original_registration2));
+
+ EXPECT_FALSE(called1);
+ EXPECT_FALSE(called2);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(called2);
+ EXPECT_TRUE(called1);
+
+ scoped_refptr<ServiceWorkerRegistration> registration1;
+ storage()->FindRegistrationForDocument(
+ GURL("http://www.example.com/one/"),
+ SaveFoundRegistration(SERVICE_WORKER_OK, &called1, &registration1));
+ scoped_refptr<ServiceWorkerRegistration> registration2;
+ storage()->FindRegistrationForDocument(
+ GURL("http://www.example.com/two/"),
+ SaveFoundRegistration(SERVICE_WORKER_OK, &called2, &registration2));
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(called2);
+ EXPECT_TRUE(called1);
+ ASSERT_NE(registration1, registration2);
+}
+
+// Make sure basic registration is working.
+TEST_F(ServiceWorkerJobTest, Register) {
+ bool called = false;
+ scoped_refptr<ServiceWorkerRegistration> registration;
+ job_coordinator()->Register(
+ GURL("http://www.example.com/*"),
+ GURL("http://www.example.com/service_worker.js"),
+ render_process_id_,
+ SaveRegistration(SERVICE_WORKER_OK, &called, &registration));
+
+ ASSERT_FALSE(called);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_TRUE(called);
+
+ ASSERT_NE(scoped_refptr<ServiceWorkerRegistration>(NULL), registration);
+}
+
+// Make sure registrations are cleaned up when they are unregistered.
+TEST_F(ServiceWorkerJobTest, Unregister) {
+ GURL pattern("http://www.example.com/*");
+
+ bool called;
+ scoped_refptr<ServiceWorkerRegistration> registration;
+ job_coordinator()->Register(
+ pattern,
+ GURL("http://www.example.com/service_worker.js"),
+ render_process_id_,
+ SaveRegistration(SERVICE_WORKER_OK, &called, &registration));
+
+ ASSERT_FALSE(called);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_TRUE(called);
+
+ job_coordinator()->Unregister(pattern,
+ SaveUnregistration(SERVICE_WORKER_OK, &called));
+
+ ASSERT_FALSE(called);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_TRUE(called);
+
+ ASSERT_TRUE(registration->HasOneRef());
+
+ storage()->FindRegistrationForPattern(
+ pattern,
+ SaveFoundRegistration(SERVICE_WORKER_ERROR_NOT_FOUND,
+ &called, &registration));
+
+ ASSERT_FALSE(called);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_TRUE(called);
+
+ ASSERT_EQ(scoped_refptr<ServiceWorkerRegistration>(NULL), registration);
+}
+
+TEST_F(ServiceWorkerJobTest, Unregister_NothingRegistered) {
+ GURL pattern("http://www.example.com/*");
+
+ bool called;
+ job_coordinator()->Unregister(pattern,
+ SaveUnregistration(SERVICE_WORKER_OK, &called));
+
+ ASSERT_FALSE(called);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_TRUE(called);
+}
+
+// Make sure that when a new registration replaces an existing
+// registration, that the old one is cleaned up.
+TEST_F(ServiceWorkerJobTest, RegisterNewScript) {
+ GURL pattern("http://www.example.com/*");
+
+ bool called;
+ scoped_refptr<ServiceWorkerRegistration> old_registration;
+ job_coordinator()->Register(
+ pattern,
+ GURL("http://www.example.com/service_worker.js"),
+ render_process_id_,
+ SaveRegistration(SERVICE_WORKER_OK, &called, &old_registration));
+
+ ASSERT_FALSE(called);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_TRUE(called);
+
+ scoped_refptr<ServiceWorkerRegistration> old_registration_by_pattern;
+ storage()->FindRegistrationForPattern(
+ pattern,
+ SaveFoundRegistration(
+ SERVICE_WORKER_OK, &called, &old_registration_by_pattern));
+
+ ASSERT_FALSE(called);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_TRUE(called);
+
+ ASSERT_EQ(old_registration, old_registration_by_pattern);
+ old_registration_by_pattern = NULL;
+
+ scoped_refptr<ServiceWorkerRegistration> new_registration;
+ job_coordinator()->Register(
+ pattern,
+ GURL("http://www.example.com/service_worker_new.js"),
+ render_process_id_,
+ SaveRegistration(SERVICE_WORKER_OK, &called, &new_registration));
+
+ ASSERT_FALSE(called);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_TRUE(called);
+
+ ASSERT_TRUE(old_registration->HasOneRef());
+
+ ASSERT_NE(old_registration, new_registration);
+
+ scoped_refptr<ServiceWorkerRegistration> new_registration_by_pattern;
+ storage()->FindRegistrationForPattern(
+ pattern,
+ SaveFoundRegistration(
+ SERVICE_WORKER_OK, &called, &new_registration));
+
+ ASSERT_FALSE(called);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_TRUE(called);
+
+ ASSERT_NE(new_registration_by_pattern, old_registration);
+}
+
+// Make sure that when registering a duplicate pattern+script_url
+// combination, that the same registration is used.
+TEST_F(ServiceWorkerJobTest, RegisterDuplicateScript) {
+ GURL pattern("http://www.example.com/*");
+ GURL script_url("http://www.example.com/service_worker.js");
+
+ bool called;
+ scoped_refptr<ServiceWorkerRegistration> old_registration;
+ job_coordinator()->Register(
+ pattern,
+ script_url,
+ render_process_id_,
+ SaveRegistration(SERVICE_WORKER_OK, &called, &old_registration));
+
+ ASSERT_FALSE(called);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_TRUE(called);
+
+ scoped_refptr<ServiceWorkerRegistration> old_registration_by_pattern;
+ storage()->FindRegistrationForPattern(
+ pattern,
+ SaveFoundRegistration(
+ SERVICE_WORKER_OK, &called, &old_registration_by_pattern));
+ ASSERT_FALSE(called);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_TRUE(called);
+
+ ASSERT_TRUE(old_registration_by_pattern);
+
+ scoped_refptr<ServiceWorkerRegistration> new_registration;
+ job_coordinator()->Register(
+ pattern,
+ script_url,
+ render_process_id_,
+ SaveRegistration(SERVICE_WORKER_OK, &called, &new_registration));
+
+ ASSERT_FALSE(called);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_TRUE(called);
+
+ ASSERT_EQ(old_registration, new_registration);
+
+ ASSERT_FALSE(old_registration->HasOneRef());
+
+ scoped_refptr<ServiceWorkerRegistration> new_registration_by_pattern;
+ storage()->FindRegistrationForPattern(
+ pattern,
+ SaveFoundRegistration(
+ SERVICE_WORKER_OK, &called, &new_registration_by_pattern));
+
+ ASSERT_FALSE(called);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_TRUE(called);
+
+ ASSERT_EQ(new_registration, old_registration);
+}
+
+class FailToStartWorkerTestHelper : public EmbeddedWorkerTestHelper {
+ public:
+ FailToStartWorkerTestHelper(int mock_render_process_id)
+ : EmbeddedWorkerTestHelper(mock_render_process_id) {}
+
+ virtual void OnStartWorker(int embedded_worker_id,
+ int64 service_worker_version_id,
+ const GURL& scope,
+ const GURL& script_url) OVERRIDE {
+ // Simulate failure by sending worker stopped instead of started.
+ EmbeddedWorkerInstance* worker = registry()->GetWorker(embedded_worker_id);
+ registry()->OnWorkerStopped(worker->process_id(), embedded_worker_id);
+ }
+};
+
+TEST_F(ServiceWorkerJobTest, Register_FailToStartWorker) {
+ helper_.reset(new FailToStartWorkerTestHelper(render_process_id_));
+
+ bool called = false;
+ scoped_refptr<ServiceWorkerRegistration> registration;
+ job_coordinator()->Register(
+ GURL("http://www.example.com/*"),
+ GURL("http://www.example.com/service_worker.js"),
+ render_process_id_,
+ SaveRegistration(
+ SERVICE_WORKER_ERROR_START_WORKER_FAILED, &called, &registration));
+
+ ASSERT_FALSE(called);
+ base::RunLoop().RunUntilIdle();
+
+ ASSERT_TRUE(called);
+ ASSERT_EQ(scoped_refptr<ServiceWorkerRegistration>(NULL), registration);
+}
+
+// Register and then unregister the pattern, in parallel. Job coordinator should
+// process jobs until the last job.
+TEST_F(ServiceWorkerJobTest, ParallelRegUnreg) {
+ GURL pattern("http://www.example.com/*");
+ GURL script_url("http://www.example.com/service_worker.js");
+
+ bool registration_called = false;
+ scoped_refptr<ServiceWorkerRegistration> registration;
+ job_coordinator()->Register(
+ pattern,
+ script_url,
+ render_process_id_,
+ SaveRegistration(SERVICE_WORKER_OK, &registration_called, &registration));
+
+ bool unregistration_called = false;
+ job_coordinator()->Unregister(
+ pattern,
+ SaveUnregistration(SERVICE_WORKER_OK, &unregistration_called));
+
+ ASSERT_FALSE(registration_called);
+ ASSERT_FALSE(unregistration_called);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_TRUE(registration_called);
+ ASSERT_TRUE(unregistration_called);
+
+ bool find_called = false;
+ storage()->FindRegistrationForPattern(
+ pattern,
+ SaveFoundRegistration(
+ SERVICE_WORKER_ERROR_NOT_FOUND, &find_called, &registration));
+
+ base::RunLoop().RunUntilIdle();
+
+ ASSERT_EQ(scoped_refptr<ServiceWorkerRegistration>(), registration);
+}
+
+// Register conflicting scripts for the same pattern. The most recent
+// registration should win, and the old registration should have been
+// shutdown.
+TEST_F(ServiceWorkerJobTest, ParallelRegNewScript) {
+ GURL pattern("http://www.example.com/*");
+
+ GURL script_url1("http://www.example.com/service_worker1.js");
+ bool registration1_called = false;
+ scoped_refptr<ServiceWorkerRegistration> registration1;
+ job_coordinator()->Register(
+ pattern,
+ script_url1,
+ render_process_id_,
+ SaveRegistration(
+ SERVICE_WORKER_OK, &registration1_called, &registration1));
+
+ GURL script_url2("http://www.example.com/service_worker2.js");
+ bool registration2_called = false;
+ scoped_refptr<ServiceWorkerRegistration> registration2;
+ job_coordinator()->Register(
+ pattern,
+ script_url2,
+ render_process_id_,
+ SaveRegistration(
+ SERVICE_WORKER_OK, &registration2_called, &registration2));
+
+ ASSERT_FALSE(registration1_called);
+ ASSERT_FALSE(registration2_called);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_TRUE(registration1_called);
+ ASSERT_TRUE(registration2_called);
+
+ scoped_refptr<ServiceWorkerRegistration> registration;
+ bool find_called = false;
+ storage()->FindRegistrationForPattern(
+ pattern,
+ SaveFoundRegistration(
+ SERVICE_WORKER_OK, &find_called, &registration));
+
+ base::RunLoop().RunUntilIdle();
+
+ ASSERT_EQ(registration2, registration);
+}
+
+// Register the exact same pattern + script. Requests should be
+// coalesced such that both callers get the exact same registration
+// object.
+TEST_F(ServiceWorkerJobTest, ParallelRegSameScript) {
+ GURL pattern("http://www.example.com/*");
+
+ GURL script_url("http://www.example.com/service_worker1.js");
+ bool registration1_called = false;
+ scoped_refptr<ServiceWorkerRegistration> registration1;
+ job_coordinator()->Register(
+ pattern,
+ script_url,
+ render_process_id_,
+ SaveRegistration(
+ SERVICE_WORKER_OK, &registration1_called, &registration1));
+
+ bool registration2_called = false;
+ scoped_refptr<ServiceWorkerRegistration> registration2;
+ job_coordinator()->Register(
+ pattern,
+ script_url,
+ render_process_id_,
+ SaveRegistration(
+ SERVICE_WORKER_OK, &registration2_called, &registration2));
+
+ ASSERT_FALSE(registration1_called);
+ ASSERT_FALSE(registration2_called);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_TRUE(registration1_called);
+ ASSERT_TRUE(registration2_called);
+
+ ASSERT_EQ(registration1, registration2);
+
+ scoped_refptr<ServiceWorkerRegistration> registration;
+ bool find_called = false;
+ storage()->FindRegistrationForPattern(
+ pattern,
+ SaveFoundRegistration(
+ SERVICE_WORKER_OK, &find_called, &registration));
+
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(registration, registration1);
+}
+
+// Call simulataneous unregister calls.
+TEST_F(ServiceWorkerJobTest, ParallelUnreg) {
+ GURL pattern("http://www.example.com/*");
+
+ GURL script_url("http://www.example.com/service_worker.js");
+ bool unregistration1_called = false;
+ job_coordinator()->Unregister(
+ pattern,
+ SaveUnregistration(SERVICE_WORKER_OK, &unregistration1_called));
+
+ bool unregistration2_called = false;
+ job_coordinator()->Unregister(
+ pattern, SaveUnregistration(SERVICE_WORKER_OK, &unregistration2_called));
+
+ ASSERT_FALSE(unregistration1_called);
+ ASSERT_FALSE(unregistration2_called);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_TRUE(unregistration1_called);
+ ASSERT_TRUE(unregistration2_called);
+
+ // There isn't really a way to test that they are being coalesced,
+ // but we can make sure they can exist simultaneously without
+ // crashing.
+ scoped_refptr<ServiceWorkerRegistration> registration;
+ bool find_called = false;
+ storage()->FindRegistrationForPattern(
+ pattern,
+ SaveFoundRegistration(
+ SERVICE_WORKER_ERROR_NOT_FOUND, &find_called, &registration));
+
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(scoped_refptr<ServiceWorkerRegistration>(), registration);
+}
+
+TEST_F(ServiceWorkerJobTest, AbortAll_Register) {
+ GURL pattern1("http://www1.example.com/*");
+ GURL pattern2("http://www2.example.com/*");
+ GURL script_url1("http://www1.example.com/service_worker.js");
+ GURL script_url2("http://www2.example.com/service_worker.js");
+
+ bool registration_called1 = false;
+ scoped_refptr<ServiceWorkerRegistration> registration1;
+ job_coordinator()->Register(
+ pattern1,
+ script_url1,
+ render_process_id_,
+ SaveRegistration(SERVICE_WORKER_ERROR_ABORT,
+ &registration_called1, &registration1));
+
+ bool registration_called2 = false;
+ scoped_refptr<ServiceWorkerRegistration> registration2;
+ job_coordinator()->Register(
+ pattern2,
+ script_url2,
+ render_process_id_,
+ SaveRegistration(SERVICE_WORKER_ERROR_ABORT,
+ &registration_called2, &registration2));
+
+ ASSERT_FALSE(registration_called1);
+ ASSERT_FALSE(registration_called2);
+ job_coordinator()->AbortAll();
+
+ base::RunLoop().RunUntilIdle();
+ ASSERT_TRUE(registration_called1);
+ ASSERT_TRUE(registration_called2);
+
+ bool find_called1 = false;
+ storage()->FindRegistrationForPattern(
+ pattern1,
+ SaveFoundRegistration(
+ SERVICE_WORKER_ERROR_NOT_FOUND, &find_called1, &registration1));
+
+ bool find_called2 = false;
+ storage()->FindRegistrationForPattern(
+ pattern2,
+ SaveFoundRegistration(
+ SERVICE_WORKER_ERROR_NOT_FOUND, &find_called2, &registration2));
+
+ base::RunLoop().RunUntilIdle();
+ ASSERT_TRUE(find_called1);
+ ASSERT_TRUE(find_called2);
+ EXPECT_EQ(scoped_refptr<ServiceWorkerRegistration>(), registration1);
+ EXPECT_EQ(scoped_refptr<ServiceWorkerRegistration>(), registration2);
+}
+
+TEST_F(ServiceWorkerJobTest, AbortAll_Unregister) {
+ GURL pattern1("http://www1.example.com/*");
+ GURL pattern2("http://www2.example.com/*");
+
+ bool unregistration_called1 = false;
+ scoped_refptr<ServiceWorkerRegistration> registration1;
+ job_coordinator()->Unregister(
+ pattern1,
+ SaveUnregistration(SERVICE_WORKER_ERROR_ABORT,
+ &unregistration_called1));
+
+ bool unregistration_called2 = false;
+ job_coordinator()->Unregister(
+ pattern2,
+ SaveUnregistration(SERVICE_WORKER_ERROR_ABORT,
+ &unregistration_called2));
+
+ ASSERT_FALSE(unregistration_called1);
+ ASSERT_FALSE(unregistration_called2);
+ job_coordinator()->AbortAll();
+
+ base::RunLoop().RunUntilIdle();
+ ASSERT_TRUE(unregistration_called1);
+ ASSERT_TRUE(unregistration_called2);
+}
+
+TEST_F(ServiceWorkerJobTest, AbortAll_RegUnreg) {
+ GURL pattern("http://www.example.com/*");
+ GURL script_url("http://www.example.com/service_worker.js");
+
+ bool registration_called = false;
+ scoped_refptr<ServiceWorkerRegistration> registration;
+ job_coordinator()->Register(
+ pattern,
+ script_url,
+ render_process_id_,
+ SaveRegistration(SERVICE_WORKER_ERROR_ABORT,
+ &registration_called, &registration));
+
+ bool unregistration_called = false;
+ job_coordinator()->Unregister(
+ pattern,
+ SaveUnregistration(SERVICE_WORKER_ERROR_ABORT,
+ &unregistration_called));
+
+ ASSERT_FALSE(registration_called);
+ ASSERT_FALSE(unregistration_called);
+ job_coordinator()->AbortAll();
+
+ base::RunLoop().RunUntilIdle();
+ ASSERT_TRUE(registration_called);
+ ASSERT_TRUE(unregistration_called);
+
+ bool find_called = false;
+ storage()->FindRegistrationForPattern(
+ pattern,
+ SaveFoundRegistration(
+ SERVICE_WORKER_ERROR_NOT_FOUND, &find_called, &registration));
+
+ base::RunLoop().RunUntilIdle();
+ ASSERT_TRUE(find_called);
+ EXPECT_EQ(scoped_refptr<ServiceWorkerRegistration>(), registration);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_process_manager.cc b/chromium/content/browser/service_worker/service_worker_process_manager.cc
new file mode 100644
index 00000000000..7395e7b7b77
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_process_manager.cc
@@ -0,0 +1,193 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/service_worker/service_worker_process_manager.h"
+
+#include "content/browser/renderer_host/render_process_host_impl.h"
+#include "content/browser/service_worker/service_worker_context_wrapper.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/site_instance.h"
+#include "url/gurl.h"
+
+namespace content {
+
+static bool IncrementWorkerRefCountByPid(int process_id) {
+ RenderProcessHost* rph = RenderProcessHost::FromID(process_id);
+ if (!rph || rph->FastShutdownStarted())
+ return false;
+
+ static_cast<RenderProcessHostImpl*>(rph)->IncrementWorkerRefCount();
+ return true;
+}
+
+ServiceWorkerProcessManager::ProcessInfo::ProcessInfo(
+ const scoped_refptr<SiteInstance>& site_instance)
+ : site_instance(site_instance),
+ process_id(site_instance->GetProcess()->GetID()) {
+}
+
+ServiceWorkerProcessManager::ProcessInfo::ProcessInfo(int process_id)
+ : process_id(process_id) {
+}
+
+ServiceWorkerProcessManager::ProcessInfo::~ProcessInfo() {
+}
+
+ServiceWorkerProcessManager::ServiceWorkerProcessManager(
+ BrowserContext* browser_context)
+ : browser_context_(browser_context),
+ process_id_for_test_(-1),
+ weak_this_factory_(this),
+ weak_this_(weak_this_factory_.GetWeakPtr()) {
+}
+
+ServiceWorkerProcessManager::~ServiceWorkerProcessManager() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(browser_context_ == NULL)
+ << "Call Shutdown() before destroying |this|, so that racing method "
+ << "invocations don't use a destroyed BrowserContext.";
+}
+
+void ServiceWorkerProcessManager::Shutdown() {
+ browser_context_ = NULL;
+ for (std::map<int, ProcessInfo>::const_iterator it = instance_info_.begin();
+ it != instance_info_.end();
+ ++it) {
+ RenderProcessHost* rph = RenderProcessHost::FromID(it->second.process_id);
+ DCHECK(rph);
+ static_cast<RenderProcessHostImpl*>(rph)->DecrementWorkerRefCount();
+ }
+ instance_info_.clear();
+}
+
+void ServiceWorkerProcessManager::AllocateWorkerProcess(
+ int embedded_worker_id,
+ const std::vector<int>& process_ids,
+ const GURL& script_url,
+ const base::Callback<void(ServiceWorkerStatusCode, int process_id)>&
+ callback) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&ServiceWorkerProcessManager::AllocateWorkerProcess,
+ weak_this_,
+ embedded_worker_id,
+ process_ids,
+ script_url,
+ callback));
+ return;
+ }
+
+ if (process_id_for_test_ != -1) {
+ // Let tests specify the returned process ID. Note: We may need to be able
+ // to specify the error code too.
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(callback, SERVICE_WORKER_OK, process_id_for_test_));
+ return;
+ }
+
+ DCHECK(!ContainsKey(instance_info_, embedded_worker_id))
+ << embedded_worker_id << " already has a process allocated";
+
+ for (std::vector<int>::const_iterator it = process_ids.begin();
+ it != process_ids.end();
+ ++it) {
+ if (IncrementWorkerRefCountByPid(*it)) {
+ instance_info_.insert(
+ std::make_pair(embedded_worker_id, ProcessInfo(*it)));
+ BrowserThread::PostTask(BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(callback, SERVICE_WORKER_OK, *it));
+ return;
+ }
+ }
+
+ if (!browser_context_) {
+ // Shutdown has started.
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(callback, SERVICE_WORKER_ERROR_START_WORKER_FAILED, -1));
+ return;
+ }
+ // No existing processes available; start a new one.
+ scoped_refptr<SiteInstance> site_instance =
+ SiteInstance::CreateForURL(browser_context_, script_url);
+ RenderProcessHost* rph = site_instance->GetProcess();
+ // This Init() call posts a task to the IO thread that adds the RPH's
+ // ServiceWorkerDispatcherHost to the
+ // EmbeddedWorkerRegistry::process_sender_map_.
+ if (!rph->Init()) {
+ LOG(ERROR) << "Couldn't start a new process!";
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(callback, SERVICE_WORKER_ERROR_START_WORKER_FAILED, -1));
+ return;
+ }
+
+ instance_info_.insert(
+ std::make_pair(embedded_worker_id, ProcessInfo(site_instance)));
+
+ static_cast<RenderProcessHostImpl*>(rph)->IncrementWorkerRefCount();
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(callback, SERVICE_WORKER_OK, rph->GetID()));
+}
+
+void ServiceWorkerProcessManager::ReleaseWorkerProcess(int embedded_worker_id) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&ServiceWorkerProcessManager::ReleaseWorkerProcess,
+ weak_this_,
+ embedded_worker_id));
+ return;
+ }
+ if (process_id_for_test_ != -1) {
+ // Unittests don't increment or decrement the worker refcount of a
+ // RenderProcessHost.
+ return;
+ }
+ if (browser_context_ == NULL) {
+ // Shutdown already released all instances.
+ DCHECK(instance_info_.empty());
+ return;
+ }
+ std::map<int, ProcessInfo>::iterator info =
+ instance_info_.find(embedded_worker_id);
+ DCHECK(info != instance_info_.end());
+ RenderProcessHost* rph = NULL;
+ if (info->second.site_instance) {
+ rph = info->second.site_instance->GetProcess();
+ DCHECK_EQ(info->second.process_id, rph->GetID())
+ << "A SiteInstance's process shouldn't get destroyed while we're "
+ "holding a reference to it. Was the reference actually held?";
+ } else {
+ rph = RenderProcessHost::FromID(info->second.process_id);
+ DCHECK(rph)
+ << "Process " << info->second.process_id
+ << " was destroyed unexpectedly. Did we actually hold a reference?";
+ }
+ static_cast<RenderProcessHostImpl*>(rph)->DecrementWorkerRefCount();
+ instance_info_.erase(info);
+}
+
+} // namespace content
+
+namespace base {
+// Destroying ServiceWorkerProcessManagers only on the UI thread allows the
+// member WeakPtr to safely guard the object's lifetime when used on that
+// thread.
+void DefaultDeleter<content::ServiceWorkerProcessManager>::operator()(
+ content::ServiceWorkerProcessManager* ptr) const {
+ content::BrowserThread::DeleteSoon(
+ content::BrowserThread::UI, FROM_HERE, ptr);
+}
+} // namespace base
diff --git a/chromium/content/browser/service_worker/service_worker_process_manager.h b/chromium/content/browser/service_worker/service_worker_process_manager.h
new file mode 100644
index 00000000000..85fbcc1c050
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_process_manager.h
@@ -0,0 +1,118 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_PROCESS_MANAGER_H_
+#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_PROCESS_MANAGER_H_
+
+#include <map>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "content/common/service_worker/service_worker_status_code.h"
+
+class GURL;
+
+namespace content {
+
+class BrowserContext;
+class SiteInstance;
+
+// Interacts with the UI thread to keep RenderProcessHosts alive while the
+// ServiceWorker system is using them. Each instance of
+// ServiceWorkerProcessManager is destroyed on the UI thread shortly after its
+// ServiceWorkerContextWrapper is destroyed.
+class CONTENT_EXPORT ServiceWorkerProcessManager {
+ public:
+ // |*this| must be owned by a ServiceWorkerContextWrapper in a
+ // StoragePartition within |browser_context|.
+ explicit ServiceWorkerProcessManager(BrowserContext* browser_context);
+
+ // Shutdown must be called before the ProcessManager is destroyed.
+ ~ServiceWorkerProcessManager();
+
+ // Synchronously prevents new processes from being allocated.
+ // TODO(jyasskin): Drop references to RenderProcessHosts too.
+ void Shutdown();
+
+ // Returns a reference to a running process suitable for starting the Service
+ // Worker at |script_url|. Processes in |process_ids| will be checked in order
+ // for existence, and if none exist, then a new process will be created. Posts
+ // |callback| to the IO thread to indicate whether creation succeeded and the
+ // process ID that has a new reference.
+ //
+ // Allocation can fail with SERVICE_WORKER_ERROR_START_WORKER_FAILED if
+ // RenderProcessHost::Init fails.
+ void AllocateWorkerProcess(
+ int embedded_worker_id,
+ const std::vector<int>& process_ids,
+ const GURL& script_url,
+ const base::Callback<void(ServiceWorkerStatusCode, int process_id)>&
+ callback);
+
+ // Drops a reference to a process that was running a Service Worker, and its
+ // SiteInstance. This must match a call to AllocateWorkerProcess.
+ void ReleaseWorkerProcess(int embedded_worker_id);
+
+ // Sets a single process ID that will be used for all embedded workers. This
+ // bypasses the work of creating a process and managing its worker refcount so
+ // that unittests can run without a BrowserContext. The test is in charge of
+ // making sure this is only called on the same thread as runs the UI message
+ // loop.
+ void SetProcessIdForTest(int process_id) {
+ process_id_for_test_ = process_id;
+ }
+
+ private:
+ // Information about the process for an EmbeddedWorkerInstance.
+ struct ProcessInfo {
+ explicit ProcessInfo(const scoped_refptr<SiteInstance>& site_instance);
+ explicit ProcessInfo(int process_id);
+ ~ProcessInfo();
+
+ // Stores the SiteInstance the Worker lives inside. This needs to outlive
+ // the instance's use of its RPH to uphold assumptions in the
+ // ContentBrowserClient interface.
+ scoped_refptr<SiteInstance> site_instance;
+
+ // In case the process was allocated without using a SiteInstance, we need
+ // to store a process ID to decrement a worker reference on shutdown.
+ // TODO(jyasskin): Implement http://crbug.com/372045 or thread a frame_id in
+ // so all processes can be allocated with a SiteInstance.
+ int process_id;
+ };
+
+ // These fields are only accessed on the UI thread.
+ BrowserContext* browser_context_;
+
+ // Maps the ID of a running EmbeddedWorkerInstance to information about the
+ // process it's running inside. Since the Instances themselves live on the IO
+ // thread, this can be slightly out of date:
+ // * instance_info_ is populated while an Instance is STARTING and before
+ // it's RUNNING.
+ // * instance_info_ is depopulated in a message sent as the Instance becomes
+ // STOPPED.
+ std::map<int, ProcessInfo> instance_info_;
+
+ // In unit tests, this will be returned as the process for all
+ // EmbeddedWorkerInstances.
+ int process_id_for_test_;
+
+ // Used to double-check that we don't access *this after it's destroyed.
+ base::WeakPtrFactory<ServiceWorkerProcessManager> weak_this_factory_;
+ const base::WeakPtr<ServiceWorkerProcessManager> weak_this_;
+};
+
+} // namespace content
+
+namespace base {
+// Specialized to post the deletion to the UI thread.
+template <>
+struct CONTENT_EXPORT DefaultDeleter<content::ServiceWorkerProcessManager> {
+ void operator()(content::ServiceWorkerProcessManager* ptr) const;
+};
+} // namespace base
+
+#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_PROCESS_MANAGER_H_
diff --git a/chromium/content/browser/service_worker/service_worker_proto.gyp b/chromium/content/browser/service_worker/service_worker_proto.gyp
new file mode 100644
index 00000000000..b2588fff6a5
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_proto.gyp
@@ -0,0 +1,17 @@
+{
+ 'targets': [
+ {
+ # GN version: //content/browser/service_worker:database_proto
+ 'target_name': 'database_proto',
+ 'type': 'static_library',
+ 'sources': [
+ 'service_worker_database.proto',
+ ],
+ 'variables': {
+ 'proto_in_dir': '.',
+ 'proto_out_dir': 'content/browser/service_worker',
+ },
+ 'includes': [ '../../../build/protoc.gypi' ]
+ },
+ ],
+}
diff --git a/chromium/content/browser/service_worker/service_worker_provider_host.cc b/chromium/content/browser/service_worker/service_worker_provider_host.cc
index 871343bac69..eeba3736d03 100644
--- a/chromium/content/browser/service_worker/service_worker_provider_host.cc
+++ b/chromium/content/browser/service_worker/service_worker_provider_host.cc
@@ -4,16 +4,177 @@
#include "content/browser/service_worker/service_worker_provider_host.h"
+#include "base/stl_util.h"
+#include "content/browser/message_port_message_filter.h"
+#include "content/browser/service_worker/service_worker_context_core.h"
+#include "content/browser/service_worker/service_worker_context_request_handler.h"
+#include "content/browser/service_worker/service_worker_controllee_request_handler.h"
+#include "content/browser/service_worker/service_worker_dispatcher_host.h"
+#include "content/browser/service_worker/service_worker_handle.h"
+#include "content/browser/service_worker/service_worker_utils.h"
#include "content/browser/service_worker/service_worker_version.h"
+#include "content/common/service_worker/service_worker_messages.h"
namespace content {
+static const int kDocumentMainThreadId = 0;
+
ServiceWorkerProviderHost::ServiceWorkerProviderHost(
- int process_id, int provider_id)
- : process_id_(process_id), provider_id_(provider_id) {
+ int process_id, int provider_id,
+ base::WeakPtr<ServiceWorkerContextCore> context,
+ ServiceWorkerDispatcherHost* dispatcher_host)
+ : process_id_(process_id),
+ provider_id_(provider_id),
+ context_(context),
+ dispatcher_host_(dispatcher_host) {
}
ServiceWorkerProviderHost::~ServiceWorkerProviderHost() {
+ if (active_version_)
+ active_version_->RemoveControllee(this);
+ if (waiting_version_)
+ waiting_version_->RemoveWaitingControllee(this);
+}
+
+void ServiceWorkerProviderHost::SetDocumentUrl(const GURL& url) {
+ DCHECK(!url.has_ref());
+ document_url_ = url;
+}
+
+void ServiceWorkerProviderHost::SetActiveVersion(
+ ServiceWorkerVersion* version) {
+ if (version == active_version_)
+ return;
+ scoped_refptr<ServiceWorkerVersion> previous_version = active_version_;
+ active_version_ = version;
+ if (version)
+ version->AddControllee(this);
+ if (previous_version)
+ previous_version->RemoveControllee(this);
+
+ if (!dispatcher_host_)
+ return; // Could be NULL in some tests.
+
+ dispatcher_host_->Send(new ServiceWorkerMsg_SetCurrentServiceWorker(
+ kDocumentMainThreadId, provider_id(), CreateHandleAndPass(version)));
+}
+
+void ServiceWorkerProviderHost::SetWaitingVersion(
+ ServiceWorkerVersion* version) {
+ DCHECK(ValidateVersionForAssociation(version));
+ if (version == waiting_version_)
+ return;
+ scoped_refptr<ServiceWorkerVersion> previous_version = waiting_version_;
+ waiting_version_ = version;
+ if (version)
+ version->AddWaitingControllee(this);
+ if (previous_version)
+ previous_version->RemoveWaitingControllee(this);
+
+ if (!dispatcher_host_)
+ return; // Could be NULL in some tests.
+
+ dispatcher_host_->Send(new ServiceWorkerMsg_SetWaitingServiceWorker(
+ kDocumentMainThreadId, provider_id(), CreateHandleAndPass(version)));
+}
+
+bool ServiceWorkerProviderHost::SetHostedVersionId(int64 version_id) {
+ if (!context_)
+ return true; // System is shutting down.
+ if (active_version_)
+ return false; // Unexpected bad message.
+
+ ServiceWorkerVersion* live_version = context_->GetLiveVersion(version_id);
+ if (!live_version)
+ return true; // Was deleted before it got started.
+
+ ServiceWorkerVersionInfo info = live_version->GetInfo();
+ if (info.running_status != ServiceWorkerVersion::STARTING ||
+ info.process_id != process_id_) {
+ // If we aren't trying to start this version in our process
+ // something is amiss.
+ return false;
+ }
+
+ running_hosted_version_ = live_version;
+ return true;
+}
+
+scoped_ptr<ServiceWorkerRequestHandler>
+ServiceWorkerProviderHost::CreateRequestHandler(
+ ResourceType::Type resource_type,
+ base::WeakPtr<webkit_blob::BlobStorageContext> blob_storage_context) {
+ if (IsHostToRunningServiceWorker()) {
+ return scoped_ptr<ServiceWorkerRequestHandler>(
+ new ServiceWorkerContextRequestHandler(
+ context_, AsWeakPtr(), blob_storage_context, resource_type));
+ }
+ if (ServiceWorkerUtils::IsMainResourceType(resource_type) ||
+ active_version()) {
+ return scoped_ptr<ServiceWorkerRequestHandler>(
+ new ServiceWorkerControlleeRequestHandler(
+ context_, AsWeakPtr(), blob_storage_context, resource_type));
+ }
+ return scoped_ptr<ServiceWorkerRequestHandler>();
+}
+
+bool ServiceWorkerProviderHost::ValidateVersionForAssociation(
+ ServiceWorkerVersion* version) {
+ if (running_hosted_version_)
+ return false;
+ if (!version)
+ return true;
+
+ // A version to be associated with this provider should have the same
+ // registration (scope) as current active/waiting versions.
+ if (active_version_) {
+ if (active_version_->registration_id() != version->registration_id())
+ return false;
+ DCHECK_EQ(active_version_->scope(), version->scope());
+ }
+ if (waiting_version_) {
+ if (waiting_version_->registration_id() != version->registration_id())
+ return false;
+ DCHECK_EQ(waiting_version_->scope(), version->scope());
+ }
+ return true;
+}
+
+void ServiceWorkerProviderHost::PostMessage(
+ const base::string16& message,
+ const std::vector<int>& sent_message_port_ids) {
+ if (!dispatcher_host_)
+ return; // Could be NULL in some tests.
+
+ std::vector<int> new_routing_ids;
+ dispatcher_host_->message_port_message_filter()->
+ UpdateMessagePortsWithNewRoutes(sent_message_port_ids,
+ &new_routing_ids);
+
+ dispatcher_host_->Send(
+ new ServiceWorkerMsg_MessageToDocument(
+ kDocumentMainThreadId, provider_id(),
+ message,
+ sent_message_port_ids,
+ new_routing_ids));
+}
+
+ServiceWorkerObjectInfo ServiceWorkerProviderHost::CreateHandleAndPass(
+ ServiceWorkerVersion* version) {
+ DCHECK(ValidateVersionForAssociation(version));
+ ServiceWorkerObjectInfo info;
+ if (context_ && version) {
+ scoped_ptr<ServiceWorkerHandle> handle =
+ ServiceWorkerHandle::Create(context_, dispatcher_host_,
+ kDocumentMainThreadId, version);
+ info = handle->GetObjectInfo();
+ dispatcher_host_->RegisterServiceWorkerHandle(handle.Pass());
+ }
+ return info;
+}
+
+bool ServiceWorkerProviderHost::IsContextAlive() {
+ return context_ != NULL;
}
} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_provider_host.h b/chromium/content/browser/service_worker/service_worker_provider_host.h
index 09990b61126..31f4a372b1b 100644
--- a/chromium/content/browser/service_worker/service_worker_provider_host.h
+++ b/chromium/content/browser/service_worker/service_worker_provider_host.h
@@ -5,36 +5,119 @@
#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_PROVIDER_HOST_H_
#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_PROVIDER_HOST_H_
+#include <set>
+#include <vector>
+
#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "content/common/content_export.h"
+#include "content/common/service_worker/service_worker_types.h"
+#include "webkit/common/resource_type.h"
+
+namespace IPC {
+class Sender;
+}
+
+namespace webkit_blob {
+class BlobStorageContext;
+}
namespace content {
+class ServiceWorkerContextCore;
+class ServiceWorkerDispatcherHost;
+class ServiceWorkerRequestHandler;
class ServiceWorkerVersion;
-// This class is the browser-process representation of a serice worker
+// This class is the browser-process representation of a service worker
// provider. There is a provider per document and the lifetime of this
// object is tied to the lifetime of its document in the renderer process.
-// This class holds service worker state this is scoped to an individual
+// This class holds service worker state that is scoped to an individual
// document.
-class ServiceWorkerProviderHost {
+//
+// Note this class can also host a running service worker, in which
+// case it will observe resource loads made directly by the service worker.
+class CONTENT_EXPORT ServiceWorkerProviderHost
+ : public base::SupportsWeakPtr<ServiceWorkerProviderHost> {
public:
ServiceWorkerProviderHost(int process_id,
- int provider_id);
+ int provider_id,
+ base::WeakPtr<ServiceWorkerContextCore> context,
+ ServiceWorkerDispatcherHost* dispatcher_host);
~ServiceWorkerProviderHost();
int process_id() const { return process_id_; }
int provider_id() const { return provider_id_; }
- // The service worker version that corresponds with navigator.serviceWorker
- // for our document.
- ServiceWorkerVersion* associated_version() const {
- return associated_version_.get();
+ bool IsHostToRunningServiceWorker() {
+ return running_hosted_version_ != NULL;
+ }
+
+ // The service worker version that corresponds with
+ // navigator.serviceWorker.active for our document.
+ ServiceWorkerVersion* active_version() const {
+ return active_version_.get();
+ }
+
+ // The service worker version that corresponds with
+ // navigate.serviceWorker.waiting for our document.
+ ServiceWorkerVersion* waiting_version() const {
+ return waiting_version_.get();
+ }
+
+ // The running version, if any, that this provider is providing resource
+ // loads for.
+ ServiceWorkerVersion* running_hosted_version() const {
+ return running_hosted_version_.get();
}
+ void SetDocumentUrl(const GURL& url);
+ const GURL& document_url() const { return document_url_; }
+
+ // Associates |version| to this provider as its '.active' or '.waiting'
+ // version.
+ // Giving NULL to this method will unset the corresponding field.
+ void SetActiveVersion(ServiceWorkerVersion* version);
+ void SetWaitingVersion(ServiceWorkerVersion* version);
+
+ // Returns false if the version is not in the expected STARTING in our
+ // process state. That would be indicative of a bad IPC message.
+ bool SetHostedVersionId(int64 versions_id);
+
+ // Returns a handler for a request, the handler may return NULL if
+ // the request doesn't require special handling.
+ scoped_ptr<ServiceWorkerRequestHandler> CreateRequestHandler(
+ ResourceType::Type resource_type,
+ base::WeakPtr<webkit_blob::BlobStorageContext> blob_storage_context);
+
+ // Returns true if |version| has the same registration as active and waiting
+ // versions.
+ bool ValidateVersionForAssociation(ServiceWorkerVersion* version);
+
+ // Returns true if the context referred to by this host (i.e. |context_|) is
+ // still alive.
+ bool IsContextAlive();
+
+ // Dispatches message event to the document.
+ void PostMessage(const base::string16& message,
+ const std::vector<int>& sent_message_port_ids);
+
private:
+ // Creates a ServiceWorkerHandle to retain |version| and returns a
+ // ServiceWorkerInfo with the handle ID to pass to the provider. The
+ // provider is responsible for releasing the handle.
+ ServiceWorkerObjectInfo CreateHandleAndPass(ServiceWorkerVersion* version);
+
const int process_id_;
const int provider_id_;
- scoped_refptr<ServiceWorkerVersion> associated_version_;
+ GURL document_url_;
+ scoped_refptr<ServiceWorkerVersion> active_version_;
+ scoped_refptr<ServiceWorkerVersion> waiting_version_;
+ scoped_refptr<ServiceWorkerVersion> running_hosted_version_;
+ base::WeakPtr<ServiceWorkerContextCore> context_;
+ ServiceWorkerDispatcherHost* dispatcher_host_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServiceWorkerProviderHost);
};
} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_provider_host_unittest.cc b/chromium/content/browser/service_worker/service_worker_provider_host_unittest.cc
new file mode 100644
index 00000000000..1f52514a603
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_provider_host_unittest.cc
@@ -0,0 +1,283 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "base/memory/weak_ptr.h"
+#include "content/browser/service_worker/service_worker_context_core.h"
+#include "content/browser/service_worker/service_worker_provider_host.h"
+#include "content/browser/service_worker/service_worker_register_job.h"
+#include "content/browser/service_worker/service_worker_registration.h"
+#include "content/browser/service_worker/service_worker_version.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+static const int kRenderProcessId = 33; // Dummy process ID for testing.
+
+class ServiceWorkerProviderHostTest : public testing::Test {
+ protected:
+ ServiceWorkerProviderHostTest()
+ : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {}
+ virtual ~ServiceWorkerProviderHostTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ context_.reset(
+ new ServiceWorkerContextCore(base::FilePath(),
+ base::MessageLoopProxy::current(),
+ base::MessageLoopProxy::current(),
+ NULL,
+ NULL,
+ NULL));
+
+ scope_ = GURL("http://www.example.com/*");
+ script_url_ = GURL("http://www.example.com/service_worker.js");
+ registration_ = new ServiceWorkerRegistration(
+ scope_, script_url_, 1L, context_->AsWeakPtr());
+ version_ = new ServiceWorkerVersion(
+ registration_,
+ 1L, context_->AsWeakPtr());
+
+ // Prepare provider hosts (for the same process).
+ scoped_ptr<ServiceWorkerProviderHost> host1(new ServiceWorkerProviderHost(
+ kRenderProcessId, 1 /* provider_id */,
+ context_->AsWeakPtr(), NULL));
+ scoped_ptr<ServiceWorkerProviderHost> host2(new ServiceWorkerProviderHost(
+ kRenderProcessId, 2 /* provider_id */,
+ context_->AsWeakPtr(), NULL));
+ scoped_ptr<ServiceWorkerProviderHost> host3(new ServiceWorkerProviderHost(
+ kRenderProcessId, 3 /* provider_id */,
+ context_->AsWeakPtr(), NULL));
+ provider_host1_ = host1->AsWeakPtr();
+ provider_host2_ = host2->AsWeakPtr();
+ provider_host3_ = host3->AsWeakPtr();
+ context_->AddProviderHost(make_scoped_ptr(host1.release()));
+ context_->AddProviderHost(make_scoped_ptr(host2.release()));
+ context_->AddProviderHost(make_scoped_ptr(host3.release()));
+ }
+
+ virtual void TearDown() OVERRIDE {
+ version_ = 0;
+ registration_ = 0;
+ context_.reset();
+ }
+
+ content::TestBrowserThreadBundle thread_bundle_;
+ scoped_ptr<ServiceWorkerContextCore> context_;
+ scoped_refptr<ServiceWorkerRegistration> registration_;
+ scoped_refptr<ServiceWorkerVersion> version_;
+ base::WeakPtr<ServiceWorkerProviderHost> provider_host1_;
+ base::WeakPtr<ServiceWorkerProviderHost> provider_host2_;
+ base::WeakPtr<ServiceWorkerProviderHost> provider_host3_;
+ GURL scope_;
+ GURL script_url_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ServiceWorkerProviderHostTest);
+};
+
+TEST_F(ServiceWorkerProviderHostTest, SetActiveVersion_ProcessStatus) {
+ ASSERT_FALSE(version_->HasProcessToRun());
+
+ // Associating version_ to a provider_host's active version will internally
+ // add the provider_host's process ref to the version.
+ provider_host1_->SetActiveVersion(version_);
+ ASSERT_TRUE(version_->HasProcessToRun());
+
+ // Re-associating the same version and provider_host should just work too.
+ provider_host1_->SetActiveVersion(version_);
+ ASSERT_TRUE(version_->HasProcessToRun());
+
+ // Resetting the provider_host's active version should remove process refs
+ // from the version.
+ provider_host1_->SetActiveVersion(NULL);
+ ASSERT_FALSE(version_->HasProcessToRun());
+}
+
+TEST_F(ServiceWorkerProviderHostTest,
+ SetActiveVersion_MultipleHostsForSameProcess) {
+ ASSERT_FALSE(version_->HasProcessToRun());
+
+ // Associating version_ to two providers as active version.
+ provider_host1_->SetActiveVersion(version_);
+ provider_host2_->SetActiveVersion(version_);
+ ASSERT_TRUE(version_->HasProcessToRun());
+
+ // Disassociating one provider_host shouldn't remove all process refs
+ // from the version yet.
+ provider_host1_->SetActiveVersion(NULL);
+ ASSERT_TRUE(version_->HasProcessToRun());
+
+ // Disassociating the other provider_host will remove all process refs.
+ provider_host2_->SetActiveVersion(NULL);
+ ASSERT_FALSE(version_->HasProcessToRun());
+}
+
+TEST_F(ServiceWorkerProviderHostTest, SetWaitingVersion_ProcessStatus) {
+ ASSERT_FALSE(version_->HasProcessToRun());
+
+ // Associating version_ to a provider_host's waiting version will internally
+ // add the provider_host's process ref to the version.
+ provider_host1_->SetWaitingVersion(version_);
+ ASSERT_TRUE(version_->HasProcessToRun());
+
+ // Re-associating the same version and provider_host should just work too.
+ provider_host1_->SetWaitingVersion(version_);
+ ASSERT_TRUE(version_->HasProcessToRun());
+
+ // Resetting the provider_host's waiting version should remove process refs
+ // from the version.
+ provider_host1_->SetWaitingVersion(NULL);
+ ASSERT_FALSE(version_->HasProcessToRun());
+}
+
+TEST_F(ServiceWorkerProviderHostTest,
+ SetWaitingVersion_MultipleHostsForSameProcess) {
+ ASSERT_FALSE(version_->HasProcessToRun());
+
+ // Associating version_ to two providers as active version.
+ provider_host1_->SetWaitingVersion(version_);
+ provider_host2_->SetWaitingVersion(version_);
+ ASSERT_TRUE(version_->HasProcessToRun());
+
+ // Disassociating one provider_host shouldn't remove all process refs
+ // from the version yet.
+ provider_host1_->SetWaitingVersion(NULL);
+ ASSERT_TRUE(version_->HasProcessToRun());
+
+ // Disassociating the other provider_host will remove all process refs.
+ provider_host2_->SetWaitingVersion(NULL);
+ ASSERT_FALSE(version_->HasProcessToRun());
+}
+
+class ServiceWorkerProviderHostWaitingVersionTest : public testing::Test {
+ protected:
+ ServiceWorkerProviderHostWaitingVersionTest()
+ : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP),
+ next_provider_id_(1L) {}
+ virtual ~ServiceWorkerProviderHostWaitingVersionTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ context_.reset(
+ new ServiceWorkerContextCore(base::FilePath(),
+ base::MessageLoopProxy::current(),
+ base::MessageLoopProxy::current(),
+ NULL,
+ NULL,
+ NULL));
+
+ // Prepare provider hosts (for the same process).
+ provider_host1_ = CreateProviderHost(GURL("http://www.example.com/foo"));
+ provider_host2_ = CreateProviderHost(GURL("http://www.example.com/bar"));
+ provider_host3_ = CreateProviderHost(GURL("http://www.example.ca/foo"));
+ }
+
+ base::WeakPtr<ServiceWorkerProviderHost> CreateProviderHost(
+ const GURL& document_url) {
+ scoped_ptr<ServiceWorkerProviderHost> host(new ServiceWorkerProviderHost(
+ kRenderProcessId, next_provider_id_++, context_->AsWeakPtr(), NULL));
+ host->SetDocumentUrl(document_url);
+ base::WeakPtr<ServiceWorkerProviderHost> provider_host = host->AsWeakPtr();
+ context_->AddProviderHost(host.Pass());
+ return provider_host;
+ }
+
+ virtual void TearDown() OVERRIDE {
+ context_.reset();
+ }
+
+ content::TestBrowserThreadBundle thread_bundle_;
+ scoped_ptr<ServiceWorkerContextCore> context_;
+ base::WeakPtr<ServiceWorkerProviderHost> provider_host1_;
+ base::WeakPtr<ServiceWorkerProviderHost> provider_host2_;
+ base::WeakPtr<ServiceWorkerProviderHost> provider_host3_;
+
+ private:
+ int64 next_provider_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServiceWorkerProviderHostWaitingVersionTest);
+};
+
+TEST_F(ServiceWorkerProviderHostWaitingVersionTest,
+ AssociateWaitingVersionToDocuments) {
+ const GURL scope1("http://www.example.com/*");
+ const GURL script_url1("http://www.example.com/service_worker1.js");
+ scoped_refptr<ServiceWorkerRegistration> registration1(
+ new ServiceWorkerRegistration(
+ scope1, script_url1, 1L, context_->AsWeakPtr()));
+ scoped_refptr<ServiceWorkerVersion> version1(
+ new ServiceWorkerVersion(registration1, 1L, context_->AsWeakPtr()));
+
+ ServiceWorkerRegisterJob::AssociateWaitingVersionToDocuments(
+ context_->AsWeakPtr(), version1.get());
+ EXPECT_EQ(version1.get(), provider_host1_->waiting_version());
+ EXPECT_EQ(version1.get(), provider_host2_->waiting_version());
+ EXPECT_EQ(NULL, provider_host3_->waiting_version());
+
+ // Version2 is associated with the same registration as version1, so the
+ // waiting version of host1 and host2 should be replaced.
+ scoped_refptr<ServiceWorkerVersion> version2(
+ new ServiceWorkerVersion(registration1, 2L, context_->AsWeakPtr()));
+ ServiceWorkerRegisterJob::AssociateWaitingVersionToDocuments(
+ context_->AsWeakPtr(), version2.get());
+ EXPECT_EQ(version2.get(), provider_host1_->waiting_version());
+ EXPECT_EQ(version2.get(), provider_host2_->waiting_version());
+ EXPECT_EQ(NULL, provider_host3_->waiting_version());
+
+ const GURL scope3(provider_host1_->document_url());
+ const GURL script_url3("http://www.example.com/service_worker3.js");
+ scoped_refptr<ServiceWorkerRegistration> registration3(
+ new ServiceWorkerRegistration(
+ scope3, script_url3, 3L, context_->AsWeakPtr()));
+ scoped_refptr<ServiceWorkerVersion> version3(
+ new ServiceWorkerVersion(registration3, 3L, context_->AsWeakPtr()));
+
+ // Although version3 can match longer than version2 for host1, it should be
+ // ignored because version3 is associated with a different registration from
+ // version2.
+ ServiceWorkerRegisterJob::AssociateWaitingVersionToDocuments(
+ context_->AsWeakPtr(), version3.get());
+ EXPECT_EQ(version2.get(), provider_host1_->waiting_version());
+ EXPECT_EQ(version2.get(), provider_host2_->waiting_version());
+ EXPECT_EQ(NULL, provider_host3_->waiting_version());
+}
+
+TEST_F(ServiceWorkerProviderHostWaitingVersionTest,
+ DisassociateWaitingVersionFromDocuments) {
+ const GURL scope1("http://www.example.com/*");
+ const GURL script_url1("http://www.example.com/service_worker.js");
+ scoped_refptr<ServiceWorkerRegistration> registration1(
+ new ServiceWorkerRegistration(
+ scope1, script_url1, 1L, context_->AsWeakPtr()));
+ scoped_refptr<ServiceWorkerVersion> version1(
+ new ServiceWorkerVersion(registration1, 1L, context_->AsWeakPtr()));
+
+ const GURL scope2("http://www.example.ca/*");
+ const GURL script_url2("http://www.example.ca/service_worker.js");
+ scoped_refptr<ServiceWorkerRegistration> registration2(
+ new ServiceWorkerRegistration(
+ scope2, script_url2, 2L, context_->AsWeakPtr()));
+ scoped_refptr<ServiceWorkerVersion> version2(
+ new ServiceWorkerVersion(registration2, 2L, context_->AsWeakPtr()));
+
+ ServiceWorkerRegisterJob::AssociateWaitingVersionToDocuments(
+ context_->AsWeakPtr(), version1.get());
+ ServiceWorkerRegisterJob::AssociateWaitingVersionToDocuments(
+ context_->AsWeakPtr(), version2.get());
+
+ // Host1 and host2 are associated with version1 as a waiting version, whereas
+ // host3 is associated with version2.
+ EXPECT_EQ(version1.get(), provider_host1_->waiting_version());
+ EXPECT_EQ(version1.get(), provider_host2_->waiting_version());
+ EXPECT_EQ(version2.get(), provider_host3_->waiting_version());
+
+ // Disassociate version1 from host1 and host2.
+ ServiceWorkerRegisterJob::DisassociateWaitingVersionFromDocuments(
+ context_->AsWeakPtr(), version1->version_id());
+ EXPECT_EQ(NULL, provider_host1_->waiting_version());
+ EXPECT_EQ(NULL, provider_host2_->waiting_version());
+ EXPECT_EQ(version2.get(), provider_host3_->waiting_version());
+}
+
+} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_read_from_cache_job.cc b/chromium/content/browser/service_worker/service_worker_read_from_cache_job.cc
new file mode 100644
index 00000000000..5189a86c2bd
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_read_from_cache_job.cc
@@ -0,0 +1,191 @@
+// 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/service_worker/service_worker_read_from_cache_job.h"
+
+#include "content/browser/service_worker/service_worker_context_core.h"
+#include "content/browser/service_worker/service_worker_disk_cache.h"
+#include "content/browser/service_worker/service_worker_histograms.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_util.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_status.h"
+
+namespace content {
+
+ServiceWorkerReadFromCacheJob::ServiceWorkerReadFromCacheJob(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate,
+ base::WeakPtr<ServiceWorkerContextCore> context,
+ int64 response_id)
+ : net::URLRequestJob(request, network_delegate),
+ context_(context),
+ response_id_(response_id),
+ has_been_killed_(false),
+ weak_factory_(this) {
+}
+
+ServiceWorkerReadFromCacheJob::~ServiceWorkerReadFromCacheJob() {
+}
+
+void ServiceWorkerReadFromCacheJob::Start() {
+ if (!context_) {
+ NotifyStartError(net::URLRequestStatus(
+ net::URLRequestStatus::FAILED, net::ERR_FAILED));
+ return;
+ }
+
+ // Create a response reader and start reading the headers,
+ // we'll continue when thats done.
+ reader_ = context_->storage()->CreateResponseReader(response_id_);
+ http_info_io_buffer_ = new HttpResponseInfoIOBuffer;
+ reader_->ReadInfo(
+ http_info_io_buffer_,
+ base::Bind(&ServiceWorkerReadFromCacheJob::OnReadInfoComplete,
+ weak_factory_.GetWeakPtr()));
+ SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
+}
+
+void ServiceWorkerReadFromCacheJob::Kill() {
+ if (has_been_killed_)
+ return;
+ weak_factory_.InvalidateWeakPtrs();
+ has_been_killed_ = true;
+ reader_.reset();
+ context_.reset();
+ http_info_io_buffer_ = NULL;
+ http_info_.reset();
+ range_response_info_.reset();
+ net::URLRequestJob::Kill();
+}
+
+net::LoadState ServiceWorkerReadFromCacheJob::GetLoadState() const {
+ if (reader_.get() && reader_->IsReadPending())
+ return net::LOAD_STATE_READING_RESPONSE;
+ return net::LOAD_STATE_IDLE;
+}
+
+bool ServiceWorkerReadFromCacheJob::GetCharset(std::string* charset) {
+ if (!http_info())
+ return false;
+ return http_info()->headers->GetCharset(charset);
+}
+
+bool ServiceWorkerReadFromCacheJob::GetMimeType(std::string* mime_type) const {
+ if (!http_info())
+ return false;
+ return http_info()->headers->GetMimeType(mime_type);
+}
+
+void ServiceWorkerReadFromCacheJob::GetResponseInfo(
+ net::HttpResponseInfo* info) {
+ if (!http_info())
+ return;
+ *info = *http_info();
+}
+
+int ServiceWorkerReadFromCacheJob::GetResponseCode() const {
+ if (!http_info())
+ return -1;
+ return http_info()->headers->response_code();
+}
+
+void ServiceWorkerReadFromCacheJob::SetExtraRequestHeaders(
+ const net::HttpRequestHeaders& headers) {
+ std::string value;
+ std::vector<net::HttpByteRange> ranges;
+ if (!headers.GetHeader(net::HttpRequestHeaders::kRange, &value) ||
+ !net::HttpUtil::ParseRangeHeader(value, &ranges)) {
+ return;
+ }
+
+ // If multiple ranges are requested, we play dumb and
+ // return the entire response with 200 OK.
+ if (ranges.size() == 1U)
+ range_requested_ = ranges[0];
+}
+
+bool ServiceWorkerReadFromCacheJob::ReadRawData(
+ net::IOBuffer* buf,
+ int buf_size,
+ int *bytes_read) {
+ DCHECK_NE(buf_size, 0);
+ DCHECK(bytes_read);
+ DCHECK(!reader_->IsReadPending());
+ reader_->ReadData(
+ buf, buf_size, base::Bind(&ServiceWorkerReadFromCacheJob::OnReadComplete,
+ weak_factory_.GetWeakPtr()));
+ SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
+ return false;
+}
+
+const net::HttpResponseInfo* ServiceWorkerReadFromCacheJob::http_info() const {
+ if (!http_info_)
+ return NULL;
+ if (range_response_info_)
+ return range_response_info_.get();
+ return http_info_.get();
+}
+
+void ServiceWorkerReadFromCacheJob::OnReadInfoComplete(int result) {
+ scoped_refptr<ServiceWorkerReadFromCacheJob> protect(this);
+ if (!http_info_io_buffer_->http_info) {
+ DCHECK(result < 0);
+ ServiceWorkerHistograms::CountReadResponseResult(
+ ServiceWorkerHistograms::READ_HEADERS_ERROR);
+ NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, result));
+ return;
+ }
+ DCHECK(result >= 0);
+ SetStatus(net::URLRequestStatus()); // Clear the IO_PENDING status
+ http_info_.reset(http_info_io_buffer_->http_info.release());
+ if (is_range_request())
+ SetupRangeResponse(http_info_io_buffer_->response_data_size);
+ http_info_io_buffer_ = NULL;
+ NotifyHeadersComplete();
+}
+
+void ServiceWorkerReadFromCacheJob::SetupRangeResponse(int resource_size) {
+ DCHECK(is_range_request() && http_info_.get() && reader_.get());
+ if (resource_size < 0 || !range_requested_.ComputeBounds(resource_size)) {
+ range_requested_ = net::HttpByteRange();
+ return;
+ }
+
+ DCHECK(range_requested_.IsValid());
+ int offset = static_cast<int>(range_requested_.first_byte_position());
+ int length = static_cast<int>(range_requested_.last_byte_position() -
+ range_requested_.first_byte_position() + 1);
+
+ // Tell the reader about the range to read.
+ reader_->SetReadRange(offset, length);
+
+ // Make a copy of the full response headers and fix them up
+ // for the range we'll be returning.
+ range_response_info_.reset(new net::HttpResponseInfo(*http_info_));
+ net::HttpResponseHeaders* headers = range_response_info_->headers.get();
+ headers->UpdateWithNewRange(
+ range_requested_, resource_size, true /* replace status line */);
+}
+
+void ServiceWorkerReadFromCacheJob::OnReadComplete(int result) {
+ ServiceWorkerHistograms::ReadResponseResult check_result;
+ if (result == 0) {
+ check_result = ServiceWorkerHistograms::READ_OK;
+ NotifyDone(net::URLRequestStatus());
+ } else if (result < 0) {
+ check_result = ServiceWorkerHistograms::READ_DATA_ERROR;
+ NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, result));
+ } else {
+ check_result = ServiceWorkerHistograms::READ_OK;
+ SetStatus(net::URLRequestStatus()); // Clear the IO_PENDING status
+ }
+ ServiceWorkerHistograms::CountReadResponseResult(check_result);
+ NotifyReadComplete(result);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_read_from_cache_job.h b/chromium/content/browser/service_worker/service_worker_read_from_cache_job.h
new file mode 100644
index 00000000000..5fb74015127
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_read_from_cache_job.h
@@ -0,0 +1,73 @@
+// 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_SERVICE_WORKER_SERVICE_WORKER_READ_FROM_CACHE_JOB_H_
+#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_READ_FROM_CACHE_JOB_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "content/browser/service_worker/service_worker_disk_cache.h"
+#include "content/common/content_export.h"
+#include "net/http/http_byte_range.h"
+#include "net/url_request/url_request_job.h"
+
+namespace content {
+
+class ServiceWorkerContextCore;
+class ServiceWorkerResponseReader;
+
+// A URLRequestJob derivative used to retrieve script resources
+// from the service workers script cache. It uses a response reader
+// and pipes the response to the consumer of this url request job.
+class CONTENT_EXPORT ServiceWorkerReadFromCacheJob
+ : public net::URLRequestJob {
+ public:
+ ServiceWorkerReadFromCacheJob(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate,
+ base::WeakPtr<ServiceWorkerContextCore> context,
+ int64 response_id);
+
+ private:
+ virtual ~ServiceWorkerReadFromCacheJob();
+
+ // net::URLRequestJob overrides
+ virtual void Start() OVERRIDE;
+ virtual void Kill() OVERRIDE;
+ virtual net::LoadState GetLoadState() const OVERRIDE;
+ virtual bool GetCharset(std::string* charset) OVERRIDE;
+ virtual bool GetMimeType(std::string* mime_type) const OVERRIDE;
+ virtual void GetResponseInfo(net::HttpResponseInfo* info) OVERRIDE;
+ virtual int GetResponseCode() const OVERRIDE;
+ virtual void SetExtraRequestHeaders(
+ const net::HttpRequestHeaders& headers) OVERRIDE;
+ virtual bool ReadRawData(net::IOBuffer* buf,
+ int buf_size,
+ int *bytes_read) OVERRIDE;
+
+ // Reader completion callbacks.
+ void OnReadInfoComplete(int result);
+ void OnReadComplete(int result);
+
+ // Helpers
+ const net::HttpResponseInfo* http_info() const;
+ bool is_range_request() const { return range_requested_.IsValid(); }
+ void SetupRangeResponse(int response_data_size);
+
+ base::WeakPtr<ServiceWorkerContextCore> context_;
+ int64 response_id_;
+ scoped_ptr<ServiceWorkerResponseReader> reader_;
+ scoped_refptr<HttpResponseInfoIOBuffer> http_info_io_buffer_;
+ scoped_ptr<net::HttpResponseInfo> http_info_;
+ net::HttpByteRange range_requested_;
+ scoped_ptr<net::HttpResponseInfo> range_response_info_;
+ bool has_been_killed_;
+ base::WeakPtrFactory<ServiceWorkerReadFromCacheJob> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServiceWorkerReadFromCacheJob);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_READ_FROM_CACHE_JOB_H_
diff --git a/chromium/content/browser/service_worker/service_worker_register_job.cc b/chromium/content/browser/service_worker/service_worker_register_job.cc
index 90333595891..098b6b31df8 100644
--- a/chromium/content/browser/service_worker/service_worker_register_job.cc
+++ b/chromium/content/browser/service_worker/service_worker_register_job.cc
@@ -4,114 +4,466 @@
#include "content/browser/service_worker/service_worker_register_job.h"
+#include <vector>
+
+#include "base/message_loop/message_loop.h"
+#include "content/browser/service_worker/service_worker_context_core.h"
+#include "content/browser/service_worker/service_worker_job_coordinator.h"
#include "content/browser/service_worker/service_worker_registration.h"
-#include "content/public/browser/browser_thread.h"
-#include "url/gurl.h"
+#include "content/browser/service_worker/service_worker_storage.h"
+#include "content/browser/service_worker/service_worker_utils.h"
namespace content {
+namespace {
+
+void RunSoon(const base::Closure& closure) {
+ base::MessageLoop::current()->PostTask(FROM_HERE, closure);
+}
+
+}
+
+typedef ServiceWorkerRegisterJobBase::RegistrationJobType RegistrationJobType;
+
ServiceWorkerRegisterJob::ServiceWorkerRegisterJob(
- const base::WeakPtr<ServiceWorkerStorage>& storage,
- const RegistrationCompleteCallback& callback)
- : storage_(storage), callback_(callback), weak_factory_(this) {}
-
-ServiceWorkerRegisterJob::~ServiceWorkerRegisterJob() {}
-
-void ServiceWorkerRegisterJob::StartRegister(const GURL& pattern,
- const GURL& script_url) {
- // Set up a chain of callbacks, in reverse order. Each of these
- // callbacks may be called asynchronously by the previous callback.
- ServiceWorkerStorage::RegistrationCallback finish_registration(base::Bind(
- &ServiceWorkerRegisterJob::RegisterComplete, weak_factory_.GetWeakPtr()));
-
- ServiceWorkerStorage::UnregistrationCallback register_new(
- base::Bind(&ServiceWorkerRegisterJob::RegisterPatternAndContinue,
- weak_factory_.GetWeakPtr(),
- pattern,
- script_url,
- finish_registration));
-
- ServiceWorkerStorage::FindRegistrationCallback unregister_old(
- base::Bind(&ServiceWorkerRegisterJob::UnregisterPatternAndContinue,
- weak_factory_.GetWeakPtr(),
- pattern,
- script_url,
- register_new));
-
- storage_->FindRegistrationForPattern(pattern, unregister_old);
-}
-
-void ServiceWorkerRegisterJob::StartUnregister(const GURL& pattern) {
- // Set up a chain of callbacks, in reverse order. Each of these
- // callbacks may be called asynchronously by the previous callback.
- ServiceWorkerStorage::UnregistrationCallback finish_unregistration(
- base::Bind(&ServiceWorkerRegisterJob::UnregisterComplete,
+ base::WeakPtr<ServiceWorkerContextCore> context,
+ const GURL& pattern,
+ const GURL& script_url)
+ : context_(context),
+ pattern_(pattern),
+ script_url_(script_url),
+ phase_(INITIAL),
+ is_promise_resolved_(false),
+ promise_resolved_status_(SERVICE_WORKER_OK),
+ weak_factory_(this) {}
+
+ServiceWorkerRegisterJob::~ServiceWorkerRegisterJob() {
+ DCHECK(!context_ ||
+ phase_ == INITIAL || phase_ == COMPLETE || phase_ == ABORT)
+ << "Jobs should only be interrupted during shutdown.";
+}
+
+void ServiceWorkerRegisterJob::AddCallback(const RegistrationCallback& callback,
+ int process_id) {
+ if (!is_promise_resolved_) {
+ callbacks_.push_back(callback);
+ if (process_id != -1 && (phase_ < UPDATE || !pending_version()))
+ pending_process_ids_.push_back(process_id);
+ return;
+ }
+ RunSoon(base::Bind(
+ callback, promise_resolved_status_,
+ promise_resolved_registration_, promise_resolved_version_));
+}
+
+void ServiceWorkerRegisterJob::Start() {
+ SetPhase(START);
+ context_->storage()->FindRegistrationForPattern(
+ pattern_,
+ base::Bind(
+ &ServiceWorkerRegisterJob::HandleExistingRegistrationAndContinue,
+ weak_factory_.GetWeakPtr()));
+}
+
+void ServiceWorkerRegisterJob::Abort() {
+ SetPhase(ABORT);
+ CompleteInternal(SERVICE_WORKER_ERROR_ABORT);
+ // Don't have to call FinishJob() because the caller takes care of removing
+ // the jobs from the queue.
+}
+
+bool ServiceWorkerRegisterJob::Equals(ServiceWorkerRegisterJobBase* job) {
+ if (job->GetType() != GetType())
+ return false;
+ ServiceWorkerRegisterJob* register_job =
+ static_cast<ServiceWorkerRegisterJob*>(job);
+ return register_job->pattern_ == pattern_ &&
+ register_job->script_url_ == script_url_;
+}
+
+RegistrationJobType ServiceWorkerRegisterJob::GetType() {
+ return REGISTRATION;
+}
+
+ServiceWorkerRegisterJob::Internal::Internal() {}
+
+ServiceWorkerRegisterJob::Internal::~Internal() {}
+
+void ServiceWorkerRegisterJob::set_registration(
+ ServiceWorkerRegistration* registration) {
+ DCHECK(phase_ == START || phase_ == REGISTER) << phase_;
+ DCHECK(!internal_.registration);
+ internal_.registration = registration;
+}
+
+ServiceWorkerRegistration* ServiceWorkerRegisterJob::registration() {
+ DCHECK(phase_ >= REGISTER) << phase_;
+ return internal_.registration;
+}
+
+void ServiceWorkerRegisterJob::set_pending_version(
+ ServiceWorkerVersion* version) {
+ DCHECK(phase_ == UPDATE) << phase_;
+ DCHECK(!internal_.pending_version);
+ internal_.pending_version = version;
+}
+
+ServiceWorkerVersion* ServiceWorkerRegisterJob::pending_version() {
+ DCHECK(phase_ >= UPDATE) << phase_;
+ return internal_.pending_version;
+}
+
+void ServiceWorkerRegisterJob::SetPhase(Phase phase) {
+ switch (phase) {
+ case INITIAL:
+ NOTREACHED();
+ break;
+ case START:
+ DCHECK(phase_ == INITIAL) << phase_;
+ break;
+ case REGISTER:
+ DCHECK(phase_ == START) << phase_;
+ break;
+ case UPDATE:
+ DCHECK(phase_ == START || phase_ == REGISTER) << phase_;
+ break;
+ case INSTALL:
+ DCHECK(phase_ == UPDATE) << phase_;
+ break;
+ case STORE:
+ DCHECK(phase_ == INSTALL) << phase_;
+ break;
+ case ACTIVATE:
+ DCHECK(phase_ == STORE) << phase_;
+ break;
+ case COMPLETE:
+ DCHECK(phase_ != INITIAL && phase_ != COMPLETE) << phase_;
+ break;
+ case ABORT:
+ break;
+ }
+ phase_ = phase;
+}
+
+// This function corresponds to the steps in Register following
+// "Let serviceWorkerRegistration be _GetRegistration(scope)"
+// |existing_registration| corresponds to serviceWorkerRegistration.
+// Throughout this file, comments in quotes are excerpts from the spec.
+void ServiceWorkerRegisterJob::HandleExistingRegistrationAndContinue(
+ ServiceWorkerStatusCode status,
+ const scoped_refptr<ServiceWorkerRegistration>& existing_registration) {
+ // On unexpected error, abort this registration job.
+ if (status != SERVICE_WORKER_ERROR_NOT_FOUND && status != SERVICE_WORKER_OK) {
+ Complete(status);
+ return;
+ }
+
+ // "If serviceWorkerRegistration is not null and script is equal to
+ // serviceWorkerRegistration.scriptUrl..." resolve with the existing
+ // registration and abort.
+ if (existing_registration.get() &&
+ existing_registration->script_url() == script_url_) {
+ set_registration(existing_registration);
+ // If there's no active version, go ahead to Update (this isn't in the spec
+ // but seems reasonable, and without SoftUpdate implemented we can never
+ // Update otherwise).
+ if (!existing_registration->active_version()) {
+ UpdateAndContinue(status);
+ return;
+ }
+ ResolvePromise(
+ status, existing_registration, existing_registration->active_version());
+ Complete(SERVICE_WORKER_OK);
+ return;
+ }
+
+ // "If serviceWorkerRegistration is null..." create a new registration.
+ if (!existing_registration.get()) {
+ RegisterAndContinue(SERVICE_WORKER_OK);
+ return;
+ }
+
+ // On script URL mismatch, "set serviceWorkerRegistration.scriptUrl to
+ // script." We accomplish this by deleting the existing registration and
+ // registering a new one.
+ // TODO(falken): Match the spec. We now throw away the active_version_ and
+ // waiting_version_ of the existing registration, which isn't in the spec.
+ // TODO(michaeln): Deactivate the live existing_registration object and
+ // eventually call storage->DeleteVersionResources()
+ // when it no longer has any controllees.
+ context_->storage()->DeleteRegistration(
+ existing_registration->id(),
+ existing_registration->script_url().GetOrigin(),
+ base::Bind(&ServiceWorkerRegisterJob::RegisterAndContinue,
weak_factory_.GetWeakPtr()));
+}
- ServiceWorkerStorage::FindRegistrationCallback unregister(
- base::Bind(&ServiceWorkerRegisterJob::UnregisterPatternAndContinue,
- weak_factory_.GetWeakPtr(),
- pattern,
- GURL(),
- finish_unregistration));
+// Creates a new ServiceWorkerRegistration.
+void ServiceWorkerRegisterJob::RegisterAndContinue(
+ ServiceWorkerStatusCode status) {
+ SetPhase(REGISTER);
+ if (status != SERVICE_WORKER_OK) {
+ // Abort this registration job.
+ Complete(status);
+ return;
+ }
- storage_->FindRegistrationForPattern(pattern, unregister);
+ set_registration(new ServiceWorkerRegistration(
+ pattern_, script_url_, context_->storage()->NewRegistrationId(),
+ context_));
+ context_->storage()->NotifyInstallingRegistration(registration());
+ UpdateAndContinue(SERVICE_WORKER_OK);
}
-void ServiceWorkerRegisterJob::RegisterPatternAndContinue(
- const GURL& pattern,
- const GURL& script_url,
- const ServiceWorkerStorage::RegistrationCallback& callback,
- ServiceWorkerRegistrationStatus previous_status) {
- if (previous_status != REGISTRATION_OK) {
- BrowserThread::PostTask(
- BrowserThread::IO,
- FROM_HERE,
- base::Bind(callback,
- previous_status,
- scoped_refptr<ServiceWorkerRegistration>()));
+// This function corresponds to the spec's _Update algorithm.
+void ServiceWorkerRegisterJob::UpdateAndContinue(
+ ServiceWorkerStatusCode status) {
+ SetPhase(UPDATE);
+ if (status != SERVICE_WORKER_OK) {
+ // Abort this registration job.
+ Complete(status);
return;
}
- // TODO: Eventually RegisterInternal will be replaced by an asynchronous
- // operation. Pass its resulting status through 'callback'.
- scoped_refptr<ServiceWorkerRegistration> registration =
- storage_->RegisterInternal(pattern, script_url);
- BrowserThread::PostTask(BrowserThread::IO,
- FROM_HERE,
- base::Bind(callback, REGISTRATION_OK, registration));
+ // TODO(falken): "If serviceWorkerRegistration.installingWorker is not null.."
+ // then terminate the installing worker. It doesn't make sense to implement
+ // yet since we always activate the worker if install completed, so there can
+ // be no installing worker at this point.
+ // TODO(nhiroki): Check 'installing_version()' instead when it's supported.
+ DCHECK(!registration()->waiting_version());
+
+ // "Let serviceWorker be a newly-created ServiceWorker object..." and start
+ // the worker.
+ set_pending_version(new ServiceWorkerVersion(
+ registration(), context_->storage()->NewVersionId(), context_));
+
+ // TODO(michaeln): Start the worker into a paused state where the
+ // script resource is downloaded but not yet evaluated.
+ pending_version()->StartWorkerWithCandidateProcesses(
+ pending_process_ids_,
+ base::Bind(&ServiceWorkerRegisterJob::OnStartWorkerFinished,
+ weak_factory_.GetWeakPtr()));
}
-void ServiceWorkerRegisterJob::UnregisterPatternAndContinue(
- const GURL& pattern,
- const GURL& new_script_url,
- const ServiceWorkerStorage::UnregistrationCallback& callback,
- bool found,
- ServiceWorkerRegistrationStatus previous_status,
- const scoped_refptr<ServiceWorkerRegistration>& previous_registration) {
-
- // The previous registration may not exist, which is ok.
- if (previous_status == REGISTRATION_OK && found &&
- (new_script_url.is_empty() ||
- previous_registration->script_url() != new_script_url)) {
- // TODO: Eventually UnregisterInternal will be replaced by an
- // asynchronous operation. Pass its resulting status though
- // 'callback'.
- storage_->UnregisterInternal(pattern);
- }
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE, base::Bind(callback, previous_status));
-}
-
-void ServiceWorkerRegisterJob::UnregisterComplete(
- ServiceWorkerRegistrationStatus status) {
- callback_.Run(this, status, NULL);
-}
-
-void ServiceWorkerRegisterJob::RegisterComplete(
- ServiceWorkerRegistrationStatus status,
- const scoped_refptr<ServiceWorkerRegistration>& registration) {
- callback_.Run(this, status, registration);
+void ServiceWorkerRegisterJob::OnStartWorkerFinished(
+ ServiceWorkerStatusCode status) {
+ // "If serviceWorker fails to start up..." then reject the promise with an
+ // error and abort.
+ if (status != SERVICE_WORKER_OK) {
+ Complete(status);
+ return;
+ }
+
+ // TODO(michaeln): Compare the old and new script.
+ // If different unpause the worker and continue with
+ // the job. If the same ResolvePromise with the current
+ // version and complete the job, throwing away the new version
+ // since there's nothing new.
+
+ // "Resolve promise with serviceWorker."
+ // Although the spec doesn't set waitingWorker until after resolving the
+ // promise, our system's resolving works by passing ServiceWorkerRegistration
+ // to the callbacks, so waitingWorker must be set first.
+ DCHECK(!registration()->waiting_version());
+ registration()->set_waiting_version(pending_version());
+ ResolvePromise(status, registration(), pending_version());
+
+ AssociateWaitingVersionToDocuments(context_, pending_version());
+
+ InstallAndContinue();
+}
+
+// This function corresponds to the spec's _Install algorithm.
+void ServiceWorkerRegisterJob::InstallAndContinue() {
+ SetPhase(INSTALL);
+ // "Set serviceWorkerRegistration.installingWorker._state to installing."
+ // "Fire install event on the associated ServiceWorkerGlobalScope object."
+ pending_version()->DispatchInstallEvent(
+ -1,
+ base::Bind(&ServiceWorkerRegisterJob::OnInstallFinished,
+ weak_factory_.GetWeakPtr()));
+}
+
+void ServiceWorkerRegisterJob::OnInstallFinished(
+ ServiceWorkerStatusCode status) {
+ // "If any handler called waitUntil()..." and the resulting promise
+ // is rejected, abort.
+ // TODO(kinuko,falken): For some error cases (e.g. ServiceWorker is
+ // unexpectedly terminated) we may want to retry sending the event again.
+ if (status != SERVICE_WORKER_OK) {
+ Complete(status);
+ return;
+ }
+
+ SetPhase(STORE);
+ context_->storage()->StoreRegistration(
+ registration(),
+ pending_version(),
+ base::Bind(&ServiceWorkerRegisterJob::OnStoreRegistrationComplete,
+ weak_factory_.GetWeakPtr()));
+}
+
+void ServiceWorkerRegisterJob::OnStoreRegistrationComplete(
+ ServiceWorkerStatusCode status) {
+ if (status != SERVICE_WORKER_OK) {
+ Complete(status);
+ return;
+ }
+
+ ActivateAndContinue();
+}
+
+// This function corresponds to the spec's _Activate algorithm.
+void ServiceWorkerRegisterJob::ActivateAndContinue() {
+ SetPhase(ACTIVATE);
+
+ // "If existingWorker is not null, then: wait for exitingWorker to finish
+ // handling any in-progress requests."
+ // See if we already have an active_version for the scope and it has
+ // controllee documents (if so activating the new version should wait
+ // until we have no documents controlled by the version).
+ if (registration()->active_version() &&
+ registration()->active_version()->HasControllee()) {
+ // TODO(kinuko,falken): Currently we immediately return if the existing
+ // registration already has an active version, so we shouldn't come
+ // this way.
+ NOTREACHED();
+ // TODO(falken): Register an continuation task to wait for NoControllees
+ // notification so that we can resume activation later (see comments
+ // in ServiceWorkerVersion::RemoveControllee).
+ Complete(SERVICE_WORKER_OK);
+ return;
+ }
+
+ // "Set serviceWorkerRegistration.waitingWorker to null."
+ // "Set serviceWorkerRegistration.activeWorker to activatingWorker."
+ DisassociateWaitingVersionFromDocuments(
+ context_, pending_version()->version_id());
+ registration()->set_waiting_version(NULL);
+ DCHECK(!registration()->active_version());
+ registration()->set_active_version(pending_version());
+
+ // "Set serviceWorkerRegistration.activeWorker._state to activating."
+ // "Fire activate event on the associated ServiceWorkerGlobalScope object."
+ // "Set serviceWorkerRegistration.activeWorker._state to active."
+ pending_version()->DispatchActivateEvent(
+ base::Bind(&ServiceWorkerRegisterJob::OnActivateFinished,
+ weak_factory_.GetWeakPtr()));
+}
+
+void ServiceWorkerRegisterJob::OnActivateFinished(
+ ServiceWorkerStatusCode status) {
+ // "If any handler called waitUntil()..." and the resulting promise
+ // is rejected, abort.
+ // TODO(kinuko,falken): For some error cases (e.g. ServiceWorker is
+ // unexpectedly terminated) we may want to retry sending the event again.
+ if (status != SERVICE_WORKER_OK) {
+ registration()->set_active_version(NULL);
+ Complete(status);
+ return;
+ }
+ context_->storage()->UpdateToActiveState(
+ registration(),
+ base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
+ Complete(SERVICE_WORKER_OK);
+}
+
+void ServiceWorkerRegisterJob::Complete(ServiceWorkerStatusCode status) {
+ CompleteInternal(status);
+ context_->job_coordinator()->FinishJob(pattern_, this);
+}
+
+void ServiceWorkerRegisterJob::CompleteInternal(
+ ServiceWorkerStatusCode status) {
+ SetPhase(COMPLETE);
+ if (status != SERVICE_WORKER_OK) {
+ if (registration() && registration()->waiting_version()) {
+ DisassociateWaitingVersionFromDocuments(
+ context_, registration()->waiting_version()->version_id());
+ registration()->set_waiting_version(NULL);
+ }
+ if (registration() && !registration()->active_version()) {
+ context_->storage()->DeleteRegistration(
+ registration()->id(),
+ registration()->script_url().GetOrigin(),
+ base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
+ }
+ if (!is_promise_resolved_)
+ ResolvePromise(status, NULL, NULL);
+ }
+ DCHECK(callbacks_.empty());
+ if (registration()) {
+ context_->storage()->NotifyDoneInstallingRegistration(
+ registration(), pending_version(), status);
+ }
+}
+
+void ServiceWorkerRegisterJob::ResolvePromise(
+ ServiceWorkerStatusCode status,
+ ServiceWorkerRegistration* registration,
+ ServiceWorkerVersion* version) {
+ DCHECK(!is_promise_resolved_);
+ is_promise_resolved_ = true;
+ promise_resolved_status_ = status;
+ promise_resolved_registration_ = registration;
+ promise_resolved_version_ = version;
+ for (std::vector<RegistrationCallback>::iterator it = callbacks_.begin();
+ it != callbacks_.end();
+ ++it) {
+ it->Run(status, registration, version);
+ }
+ callbacks_.clear();
+}
+
+// static
+void ServiceWorkerRegisterJob::AssociateWaitingVersionToDocuments(
+ base::WeakPtr<ServiceWorkerContextCore> context,
+ ServiceWorkerVersion* version) {
+ DCHECK(context);
+ DCHECK(version);
+
+ for (scoped_ptr<ServiceWorkerContextCore::ProviderHostIterator> it =
+ context->GetProviderHostIterator();
+ !it->IsAtEnd();
+ it->Advance()) {
+ ServiceWorkerProviderHost* host = it->GetProviderHost();
+ if (!host->IsContextAlive())
+ continue;
+ if (ServiceWorkerUtils::ScopeMatches(version->scope(),
+ host->document_url())) {
+ // The spec's _Update algorithm says, "upgrades active version to a new
+ // version for the same URL scope.", so skip if the scope (registration)
+ // of |version| is different from that of the current active/waiting
+ // version.
+ if (!host->ValidateVersionForAssociation(version))
+ continue;
+
+ // TODO(nhiroki): Keep |host->waiting_version()| to be replaced and set
+ // status of them to 'redandunt' after breaking the loop.
+
+ host->SetWaitingVersion(version);
+ // TODO(nhiroki): Set |host|'s installing version to null.
+ }
+ }
+}
+
+// static
+void ServiceWorkerRegisterJob::DisassociateWaitingVersionFromDocuments(
+ base::WeakPtr<ServiceWorkerContextCore> context,
+ int64 version_id) {
+ DCHECK(context);
+ for (scoped_ptr<ServiceWorkerContextCore::ProviderHostIterator> it =
+ context->GetProviderHostIterator();
+ !it->IsAtEnd();
+ it->Advance()) {
+ ServiceWorkerProviderHost* host = it->GetProviderHost();
+ if (!host->IsContextAlive())
+ continue;
+ if (host->waiting_version() &&
+ host->waiting_version()->version_id() == version_id) {
+ host->SetWaitingVersion(NULL);
+ }
+ }
}
} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_register_job.h b/chromium/content/browser/service_worker/service_worker_register_job.h
index 9e12eb51854..db10fa61118 100644
--- a/chromium/content/browser/service_worker/service_worker_register_job.h
+++ b/chromium/content/browser/service_worker/service_worker_register_job.h
@@ -5,76 +5,141 @@
#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_REGISTER_JOB_H_
#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_REGISTER_JOB_H_
+#include <vector>
+
#include "base/memory/weak_ptr.h"
-#include "content/browser/service_worker/service_worker_registration_status.h"
-#include "content/browser/service_worker/service_worker_storage.h"
+#include "content/browser/service_worker/service_worker_register_job_base.h"
+#include "content/browser/service_worker/service_worker_registration.h"
+#include "content/common/service_worker/service_worker_status_code.h"
+#include "url/gurl.h"
namespace content {
-// A ServiceWorkerRegisterJob lives only for the lifetime of a single
-// registration or unregistration.
-class ServiceWorkerRegisterJob {
+class ServiceWorkerJobCoordinator;
+class ServiceWorkerStorage;
+
+// Handles the registration of a Service Worker.
+//
+// The registration flow includes most or all of the following,
+// depending on what is already registered:
+// - creating a ServiceWorkerRegistration instance if there isn't
+// already something registered
+// - creating a ServiceWorkerVersion for the new registration instance.
+// - starting a worker for the ServiceWorkerVersion
+// - telling the Version to evaluate the script
+// - firing the 'install' event at the ServiceWorkerVersion
+// - firing the 'activate' event at the ServiceWorkerVersion
+// - waiting for older ServiceWorkerVersions to deactivate
+// - designating the new version to be the 'active' version
+class ServiceWorkerRegisterJob : public ServiceWorkerRegisterJobBase {
public:
- typedef base::Callback<void(
- ServiceWorkerRegisterJob* job,
- ServiceWorkerRegistrationStatus status,
- ServiceWorkerRegistration* registration)> RegistrationCompleteCallback;
-
- // All type of jobs (Register and Unregister) complete through a
- // single call to this callback on the IO thread.
- ServiceWorkerRegisterJob(const base::WeakPtr<ServiceWorkerStorage>& storage,
- const RegistrationCompleteCallback& callback);
- ~ServiceWorkerRegisterJob();
-
- // The Registration flow includes most or all of the following,
- // depending on what is already registered:
- // - creating a ServiceWorkerRegistration instance if there isn't
- // already something registered
- // - creating a ServiceWorkerVersion for the new registration instance.
- // - starting a worker for the ServiceWorkerVersion
- // - telling the Version to evaluate the script
- // - firing the 'install' event at the ServiceWorkerVersion
- // - firing the 'activate' event at the ServiceWorkerVersion
- // - Waiting for older ServiceWorkerVersions to deactivate
- // - designating the new version to be the 'active' version
- // This method should be called once and only once per job.
- void StartRegister(const GURL& pattern, const GURL& script_url);
-
- // The Unregistration process is primarily cleanup, removing
- // everything that was created during the Registration process,
- // including the ServiceWorkerRegistration itself.
- // This method should be called once and only once per job.
- void StartUnregister(const GURL& pattern);
+ typedef base::Callback<void(ServiceWorkerStatusCode status,
+ ServiceWorkerRegistration* registration,
+ ServiceWorkerVersion* version)>
+ RegistrationCallback;
- private:
- // These are all steps in the registration and unregistration pipeline.
- void RegisterPatternAndContinue(
+ CONTENT_EXPORT ServiceWorkerRegisterJob(
+ base::WeakPtr<ServiceWorkerContextCore> context,
const GURL& pattern,
- const GURL& script_url,
- const ServiceWorkerStorage::RegistrationCallback& callback,
- ServiceWorkerRegistrationStatus previous_status);
+ const GURL& script_url);
+ virtual ~ServiceWorkerRegisterJob();
- void UnregisterPatternAndContinue(
- const GURL& pattern,
- const GURL& script_url,
- const ServiceWorkerStorage::UnregistrationCallback& callback,
- bool found,
- ServiceWorkerRegistrationStatus previous_status,
- const scoped_refptr<ServiceWorkerRegistration>& previous_registration);
-
- // These methods are the last internal callback in the callback
- // chain, and ultimately call callback_.
- void UnregisterComplete(ServiceWorkerRegistrationStatus status);
- void RegisterComplete(
- ServiceWorkerRegistrationStatus status,
+ // Registers a callback to be called when the promise would resolve (whether
+ // successfully or not). Multiple callbacks may be registered. If |process_id|
+ // is not -1, it's added to the existing clients when deciding in which
+ // process to create the Service Worker instance. If there are no existing
+ // clients, a new RenderProcessHost will be created.
+ void AddCallback(const RegistrationCallback& callback, int process_id);
+
+ // ServiceWorkerRegisterJobBase implementation:
+ virtual void Start() OVERRIDE;
+ virtual void Abort() OVERRIDE;
+ virtual bool Equals(ServiceWorkerRegisterJobBase* job) OVERRIDE;
+ virtual RegistrationJobType GetType() OVERRIDE;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(ServiceWorkerProviderHostWaitingVersionTest,
+ AssociateWaitingVersionToDocuments);
+ FRIEND_TEST_ALL_PREFIXES(ServiceWorkerProviderHostWaitingVersionTest,
+ DisassociateWaitingVersionFromDocuments);
+
+ enum Phase {
+ INITIAL,
+ START,
+ REGISTER,
+ UPDATE,
+ INSTALL,
+ STORE,
+ ACTIVATE,
+ COMPLETE,
+ ABORT,
+ };
+
+ // Holds internal state of ServiceWorkerRegistrationJob, to compel use of the
+ // getter/setter functions.
+ struct Internal {
+ Internal();
+ ~Internal();
+ scoped_refptr<ServiceWorkerRegistration> registration;
+
+ // Holds 'installing' or 'waiting' version depending on the phase.
+ scoped_refptr<ServiceWorkerVersion> pending_version;
+ };
+
+ void set_registration(ServiceWorkerRegistration* registration);
+ ServiceWorkerRegistration* registration();
+ void set_pending_version(ServiceWorkerVersion* version);
+ ServiceWorkerVersion* pending_version();
+
+ void SetPhase(Phase phase);
+
+ void HandleExistingRegistrationAndContinue(
+ ServiceWorkerStatusCode status,
const scoped_refptr<ServiceWorkerRegistration>& registration);
+ void RegisterAndContinue(ServiceWorkerStatusCode status);
+ void UpdateAndContinue(ServiceWorkerStatusCode status);
+ void OnStartWorkerFinished(ServiceWorkerStatusCode status);
+ void OnStoreRegistrationComplete(ServiceWorkerStatusCode status);
+ void InstallAndContinue();
+ void OnInstallFinished(ServiceWorkerStatusCode status);
+ void ActivateAndContinue();
+ void OnActivateFinished(ServiceWorkerStatusCode status);
+ void Complete(ServiceWorkerStatusCode status);
+ void CompleteInternal(ServiceWorkerStatusCode status);
+
+ void ResolvePromise(ServiceWorkerStatusCode status,
+ ServiceWorkerRegistration* registration,
+ ServiceWorkerVersion* version);
- const base::WeakPtr<ServiceWorkerStorage> storage_;
- const RegistrationCompleteCallback callback_;
+ // Associates a waiting version to documents matched with a scope of the
+ // version.
+ CONTENT_EXPORT static void AssociateWaitingVersionToDocuments(
+ base::WeakPtr<ServiceWorkerContextCore> context,
+ ServiceWorkerVersion* version);
+
+ // Disassociates a waiting version specified by |version_id| from documents.
+ CONTENT_EXPORT static void DisassociateWaitingVersionFromDocuments(
+ base::WeakPtr<ServiceWorkerContextCore> context,
+ int64 version_id);
+
+ // The ServiceWorkerContextCore object should always outlive this.
+ base::WeakPtr<ServiceWorkerContextCore> context_;
+
+ const GURL pattern_;
+ const GURL script_url_;
+ std::vector<RegistrationCallback> callbacks_;
+ std::vector<int> pending_process_ids_;
+ Phase phase_;
+ Internal internal_;
+ bool is_promise_resolved_;
+ ServiceWorkerStatusCode promise_resolved_status_;
+ scoped_refptr<ServiceWorkerRegistration> promise_resolved_registration_;
+ scoped_refptr<ServiceWorkerVersion> promise_resolved_version_;
base::WeakPtrFactory<ServiceWorkerRegisterJob> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(ServiceWorkerRegisterJob);
};
+
} // namespace content
#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_REGISTER_JOB_H_
diff --git a/chromium/content/browser/service_worker/service_worker_register_job_base.h b/chromium/content/browser/service_worker/service_worker_register_job_base.h
new file mode 100644
index 00000000000..7995adda7c6
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_register_job_base.h
@@ -0,0 +1,37 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_REGISTER_JOB_BASE_H_
+#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_REGISTER_JOB_BASE_H_
+
+namespace content {
+
+// A base class for ServiceWorkerRegisterJob and ServiceWorkerUnregisterJob. A
+// job lives only for the lifetime of a single registration or unregistration.
+class ServiceWorkerRegisterJobBase {
+ public:
+ enum RegistrationJobType { REGISTRATION, UNREGISTRATION, };
+
+ virtual ~ServiceWorkerRegisterJobBase() {}
+
+ // Starts the job. This method should be called once and only once per job.
+ virtual void Start() = 0;
+
+ // Aborts the job. This method should be called once and only once per job.
+ // It can be called regardless of whether Start() was called.
+ virtual void Abort() = 0;
+
+ // Returns true if this job is identical to |job| for the purpose of
+ // collapsing them together in a ServiceWorkerJobCoordinator queue.
+ // Registration jobs are equal if they are for the same pattern and script
+ // URL; unregistration jobs are equal if they are for the same pattern.
+ virtual bool Equals(ServiceWorkerRegisterJobBase* job) = 0;
+
+ // Returns the type of this job.
+ virtual RegistrationJobType GetType() = 0;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_REGISTER_JOB_BASE_H_
diff --git a/chromium/content/browser/service_worker/service_worker_registration.cc b/chromium/content/browser/service_worker/service_worker_registration.cc
index 060de04608f..9e166b6f7b1 100644
--- a/chromium/content/browser/service_worker/service_worker_registration.cc
+++ b/chromium/content/browser/service_worker/service_worker_registration.cc
@@ -4,40 +4,56 @@
#include "content/browser/service_worker/service_worker_registration.h"
+#include "content/browser/service_worker/service_worker_context_core.h"
+#include "content/browser/service_worker/service_worker_info.h"
#include "content/public/browser/browser_thread.h"
namespace content {
-ServiceWorkerRegistration::ServiceWorkerRegistration(const GURL& pattern,
- const GURL& script_url,
- int64 registration_id)
+ServiceWorkerRegistration::ServiceWorkerRegistration(
+ const GURL& pattern,
+ const GURL& script_url,
+ int64 registration_id,
+ base::WeakPtr<ServiceWorkerContextCore> context)
: pattern_(pattern),
script_url_(script_url),
registration_id_(registration_id),
- is_shutdown_(false) {
+ is_shutdown_(false),
+ context_(context) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK(context_);
+ context_->AddLiveRegistration(this);
}
ServiceWorkerRegistration::~ServiceWorkerRegistration() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- DCHECK(is_shutdown_);
+ if (context_)
+ context_->RemoveLiveRegistration(registration_id_);
}
-void ServiceWorkerRegistration::Shutdown() {
- DCHECK(!is_shutdown_);
- if (active_version_)
- active_version_->Shutdown();
- active_version_ = NULL;
- if (pending_version_)
- pending_version_->Shutdown();
- pending_version_ = NULL;
- is_shutdown_ = true;
+ServiceWorkerRegistrationInfo ServiceWorkerRegistration::GetInfo() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ return ServiceWorkerRegistrationInfo(
+ script_url(),
+ pattern(),
+ registration_id_,
+ active_version_ ? active_version_->GetInfo() : ServiceWorkerVersionInfo(),
+ waiting_version_ ? waiting_version_->GetInfo()
+ : ServiceWorkerVersionInfo());
+}
+
+ServiceWorkerVersion* ServiceWorkerRegistration::GetNewestVersion() {
+ if (active_version())
+ return active_version();
+ return waiting_version();
}
-void ServiceWorkerRegistration::ActivatePendingVersion() {
- active_version_->Shutdown();
- active_version_ = pending_version_;
- pending_version_ = NULL;
+void ServiceWorkerRegistration::ActivateWaitingVersion() {
+ active_version_->SetStatus(ServiceWorkerVersion::DEACTIVATED);
+ active_version_ = waiting_version_;
+ // TODO(kinuko): This should be set to ACTIVATING until activation finishes.
+ active_version_->SetStatus(ServiceWorkerVersion::ACTIVE);
+ waiting_version_ = NULL;
}
} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_registration.h b/chromium/content/browser/service_worker/service_worker_registration.h
index 680fcfacbcc..df23dc3d61a 100644
--- a/chromium/content/browser/service_worker/service_worker_registration.h
+++ b/chromium/content/browser/service_worker/service_worker_registration.h
@@ -16,6 +16,7 @@
namespace content {
+class ServiceWorkerRegistrationInfo;
class ServiceWorkerVersion;
// This class manages all persistence of service workers:
@@ -30,16 +31,14 @@ class ServiceWorkerVersion;
//
// This class also manages the state of the upgrade process, which
// includes managing which ServiceWorkerVersion is "active" vs "in
-// waiting" (or "pending")
+// waiting".
class CONTENT_EXPORT ServiceWorkerRegistration
: NON_EXPORTED_BASE(public base::RefCounted<ServiceWorkerRegistration>) {
public:
ServiceWorkerRegistration(const GURL& pattern,
const GURL& script_url,
- int64 registration_id);
-
- void Shutdown();
- bool is_shutdown() const { return is_shutdown_; }
+ int64 registration_id,
+ base::WeakPtr<ServiceWorkerContextCore> context);
int64 id() const { return registration_id_; }
const GURL& script_url() const { return script_url_; }
@@ -50,9 +49,9 @@ class CONTENT_EXPORT ServiceWorkerRegistration
return active_version_.get();
}
- ServiceWorkerVersion* pending_version() const {
+ ServiceWorkerVersion* waiting_version() const {
DCHECK(!is_shutdown_);
- return pending_version_.get();
+ return waiting_version_.get();
}
void set_active_version(ServiceWorkerVersion* version) {
@@ -60,17 +59,23 @@ class CONTENT_EXPORT ServiceWorkerRegistration
active_version_ = version;
}
- void set_pending_version(ServiceWorkerVersion* version) {
+ void set_waiting_version(ServiceWorkerVersion* version) {
DCHECK(!is_shutdown_);
- pending_version_ = version;
+ waiting_version_ = version;
}
+ ServiceWorkerRegistrationInfo GetInfo();
+
+ // Returns the active version, if it is not null; otherwise, returns the
+ // waiting version.
+ ServiceWorkerVersion* GetNewestVersion();
+
// The final synchronous switchover after all events have been
// fired, and the old "active version" is being shut down.
- void ActivatePendingVersion();
+ void ActivateWaitingVersion();
private:
- virtual ~ServiceWorkerRegistration();
+ ~ServiceWorkerRegistration();
friend class base::RefCounted<ServiceWorkerRegistration>;
const GURL pattern_;
@@ -78,9 +83,10 @@ class CONTENT_EXPORT ServiceWorkerRegistration
const int64 registration_id_;
scoped_refptr<ServiceWorkerVersion> active_version_;
- scoped_refptr<ServiceWorkerVersion> pending_version_;
+ scoped_refptr<ServiceWorkerVersion> waiting_version_;
bool is_shutdown_;
+ base::WeakPtr<ServiceWorkerContextCore> context_;
DISALLOW_COPY_AND_ASSIGN(ServiceWorkerRegistration);
};
diff --git a/chromium/content/browser/service_worker/service_worker_registration_status.cc b/chromium/content/browser/service_worker/service_worker_registration_status.cc
index 4556b5f537f..ee5ea906676 100644
--- a/chromium/content/browser/service_worker/service_worker_registration_status.cc
+++ b/chromium/content/browser/service_worker/service_worker_registration_status.cc
@@ -7,35 +7,45 @@
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
-namespace {
-const char kInstallFailedErrorMessage[] = "ServiceWorker failed to install";
-const char kActivateFailedErrorMessage[] = "ServiceWorker failed to activate";
-}
-
namespace content {
using blink::WebServiceWorkerError;
void GetServiceWorkerRegistrationStatusResponse(
- ServiceWorkerRegistrationStatus status,
+ ServiceWorkerStatusCode status,
blink::WebServiceWorkerError::ErrorType* error_type,
base::string16* message) {
+ *error_type = WebServiceWorkerError::ErrorTypeUnknown;
+ *message = base::ASCIIToUTF16(ServiceWorkerStatusToString(status));
switch (status) {
- case REGISTRATION_OK:
- NOTREACHED() << "Consumers should check registration status before "
- "calling this function.";
+ case SERVICE_WORKER_OK:
+ NOTREACHED() << "Calling this when status == OK is not allowed";
+ return;
+
+ case SERVICE_WORKER_ERROR_START_WORKER_FAILED:
+ case SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED:
+ *error_type = WebServiceWorkerError::ErrorTypeInstall;
return;
- case REGISTRATION_INSTALL_FAILED:
- *error_type = WebServiceWorkerError::InstallError;
- *message = ASCIIToUTF16(kInstallFailedErrorMessage);
+ case SERVICE_WORKER_ERROR_ACTIVATE_WORKER_FAILED:
+ *error_type = WebServiceWorkerError::ErrorTypeActivate;
return;
- case REGISTRATION_ACTIVATE_FAILED:
- *error_type = WebServiceWorkerError::ActivateError;
- *message = ASCIIToUTF16(kActivateFailedErrorMessage);
+ case SERVICE_WORKER_ERROR_NOT_FOUND:
+ *error_type = WebServiceWorkerError::ErrorTypeNotFound;
return;
+
+ case SERVICE_WORKER_ERROR_ABORT:
+ case SERVICE_WORKER_ERROR_IPC_FAILED:
+ case SERVICE_WORKER_ERROR_FAILED:
+ case SERVICE_WORKER_ERROR_PROCESS_NOT_FOUND:
+ case SERVICE_WORKER_ERROR_EXISTS:
+ // Unexpected, or should have bailed out before calling this, or we don't
+ // have a corresponding blink error code yet.
+ break; // Fall through to NOTREACHED().
}
- NOTREACHED();
+ NOTREACHED() << "Got unexpected error code: "
+ << status << " " << ServiceWorkerStatusToString(status);
}
+
} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_registration_status.h b/chromium/content/browser/service_worker/service_worker_registration_status.h
index 1676b495cde..8cffd5628e9 100644
--- a/chromium/content/browser/service_worker/service_worker_registration_status.h
+++ b/chromium/content/browser/service_worker/service_worker_registration_status.h
@@ -6,21 +6,14 @@
#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_REGISTRATION_STATUS_H_
#include "base/strings/string16.h"
+#include "content/common/service_worker/service_worker_status_code.h"
#include "third_party/WebKit/public/platform/WebServiceWorkerError.h"
namespace content {
-// This enum describes the reason a registration or unregistration succeeds or
-// fails.
-enum ServiceWorkerRegistrationStatus {
- REGISTRATION_OK,
- REGISTRATION_INSTALL_FAILED,
- REGISTRATION_ACTIVATE_FAILED,
-};
-
-// This should only be called for errors, where status != REGISTRATION_OK.
+// This should only be called for errors, where status != OK.
void GetServiceWorkerRegistrationStatusResponse(
- ServiceWorkerRegistrationStatus status,
+ ServiceWorkerStatusCode status,
blink::WebServiceWorkerError::ErrorType* error_type,
base::string16* message);
diff --git a/chromium/content/browser/service_worker/service_worker_registration_unittest.cc b/chromium/content/browser/service_worker/service_worker_registration_unittest.cc
index 3fb6611a2f3..ccce6417526 100644
--- a/chromium/content/browser/service_worker/service_worker_registration_unittest.cc
+++ b/chromium/content/browser/service_worker/service_worker_registration_unittest.cc
@@ -4,10 +4,13 @@
#include "content/browser/service_worker/service_worker_registration.h"
+
#include "base/files/scoped_temp_dir.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
#include "content/browser/browser_thread_impl.h"
+#include "content/browser/service_worker/service_worker_context_core.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
@@ -18,34 +21,29 @@ class ServiceWorkerRegistrationTest : public testing::Test {
ServiceWorkerRegistrationTest()
: io_thread_(BrowserThread::IO, &message_loop_) {}
- virtual void SetUp() OVERRIDE {}
+ virtual void SetUp() OVERRIDE {
+ context_.reset(
+ new ServiceWorkerContextCore(base::FilePath(),
+ base::MessageLoopProxy::current(),
+ base::MessageLoopProxy::current(),
+ NULL,
+ NULL,
+ NULL));
+ context_ptr_ = context_->AsWeakPtr();
+ }
+
+ virtual void TearDown() OVERRIDE {
+ context_.reset();
+ base::RunLoop().RunUntilIdle();
+ }
protected:
+ scoped_ptr<ServiceWorkerContextCore> context_;
+ base::WeakPtr<ServiceWorkerContextCore> context_ptr_;
base::MessageLoopForIO message_loop_;
BrowserThreadImpl io_thread_;
};
-TEST_F(ServiceWorkerRegistrationTest, Shutdown) {
- const int64 registration_id = -1L;
- const int64 version_id = -1L;
- scoped_refptr<ServiceWorkerRegistration> registration =
- new ServiceWorkerRegistration(
- GURL("http://www.example.com/*"),
- GURL("http://www.example.com/service_worker.js"),
- registration_id);
-
- scoped_refptr<ServiceWorkerVersion> active_version =
- new ServiceWorkerVersion(registration, NULL, version_id);
- registration->set_active_version(active_version);
-
- registration->Shutdown();
-
- DCHECK(registration->is_shutdown());
- DCHECK(active_version->is_shutdown());
- DCHECK(registration->HasOneRef());
- DCHECK(active_version->HasOneRef());
-}
-
// Make sure that activation does not leak
TEST_F(ServiceWorkerRegistrationTest, ActivatePending) {
int64 registration_id = -1L;
@@ -53,33 +51,26 @@ TEST_F(ServiceWorkerRegistrationTest, ActivatePending) {
new ServiceWorkerRegistration(
GURL("http://www.example.com/*"),
GURL("http://www.example.com/service_worker.js"),
- registration_id);
+ registration_id,
+ context_ptr_);
const int64 version_1_id = 1L;
const int64 version_2_id = 2L;
scoped_refptr<ServiceWorkerVersion> version_1 =
- new ServiceWorkerVersion(registration, NULL, version_1_id);
+ new ServiceWorkerVersion(registration, version_1_id, context_ptr_);
+ version_1->SetStatus(ServiceWorkerVersion::ACTIVE);
registration->set_active_version(version_1);
scoped_refptr<ServiceWorkerVersion> version_2 =
- new ServiceWorkerVersion(registration, NULL, version_2_id);
- registration->set_pending_version(version_2);
+ new ServiceWorkerVersion(registration, version_2_id, context_ptr_);
+ registration->set_waiting_version(version_2);
- registration->ActivatePendingVersion();
+ registration->ActivateWaitingVersion();
DCHECK_EQ(version_2, registration->active_version());
- DCHECK(version_1->is_shutdown());
DCHECK(version_1->HasOneRef());
version_1 = NULL;
- DCHECK(!version_2->is_shutdown());
DCHECK(!version_2->HasOneRef());
-
- registration->Shutdown();
-
- DCHECK(registration->is_shutdown());
- DCHECK(version_2->is_shutdown());
- DCHECK(registration->HasOneRef());
- DCHECK(version_2->HasOneRef());
}
} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_request_handler.cc b/chromium/content/browser/service_worker/service_worker_request_handler.cc
new file mode 100644
index 00000000000..a04e0116452
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_request_handler.cc
@@ -0,0 +1,111 @@
+// 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/service_worker/service_worker_request_handler.h"
+
+#include "content/browser/service_worker/service_worker_context_core.h"
+#include "content/browser/service_worker/service_worker_context_wrapper.h"
+#include "content/browser/service_worker/service_worker_provider_host.h"
+#include "content/browser/service_worker/service_worker_registration.h"
+#include "content/browser/service_worker/service_worker_url_request_job.h"
+#include "content/browser/service_worker/service_worker_utils.h"
+#include "content/common/service_worker/service_worker_types.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_interceptor.h"
+#include "webkit/browser/blob/blob_storage_context.h"
+
+namespace content {
+
+namespace {
+
+int kUserDataKey; // Key value is not important.
+
+class ServiceWorkerRequestInterceptor
+ : public net::URLRequestInterceptor {
+ public:
+ ServiceWorkerRequestInterceptor() {}
+ virtual ~ServiceWorkerRequestInterceptor() {}
+ virtual net::URLRequestJob* MaybeInterceptRequest(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) const OVERRIDE {
+ ServiceWorkerRequestHandler* handler =
+ ServiceWorkerRequestHandler::GetHandler(request);
+ if (!handler)
+ return NULL;
+ return handler->MaybeCreateJob(request, network_delegate);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ServiceWorkerRequestInterceptor);
+};
+
+bool IsMethodSupported(const std::string& method) {
+ return (method == "GET") || (method == "HEAD");
+}
+
+bool IsSchemeAndMethodSupported(const net::URLRequest* request) {
+ return request->url().SchemeIsHTTPOrHTTPS() &&
+ IsMethodSupported(request->method());
+}
+
+} // namespace
+
+void ServiceWorkerRequestHandler::InitializeHandler(
+ net::URLRequest* request,
+ ServiceWorkerContextWrapper* context_wrapper,
+ webkit_blob::BlobStorageContext* blob_storage_context,
+ int process_id,
+ int provider_id,
+ ResourceType::Type resource_type) {
+ if (!ServiceWorkerUtils::IsFeatureEnabled() ||
+ !IsSchemeAndMethodSupported(request)) {
+ return;
+ }
+
+ if (!context_wrapper || !context_wrapper->context() ||
+ provider_id == kInvalidServiceWorkerProviderId) {
+ return;
+ }
+
+ ServiceWorkerProviderHost* provider_host =
+ context_wrapper->context()->GetProviderHost(process_id, provider_id);
+ if (!provider_host || !provider_host->IsContextAlive())
+ return;
+
+ scoped_ptr<ServiceWorkerRequestHandler> handler(
+ provider_host->CreateRequestHandler(resource_type,
+ blob_storage_context->AsWeakPtr()));
+ if (!handler)
+ return;
+
+ request->SetUserData(&kUserDataKey, handler.release());
+}
+
+ServiceWorkerRequestHandler* ServiceWorkerRequestHandler::GetHandler(
+ net::URLRequest* request) {
+ return reinterpret_cast<ServiceWorkerRequestHandler*>(
+ request->GetUserData(&kUserDataKey));
+}
+
+scoped_ptr<net::URLRequestInterceptor>
+ServiceWorkerRequestHandler::CreateInterceptor() {
+ return scoped_ptr<net::URLRequestInterceptor>(
+ new ServiceWorkerRequestInterceptor);
+}
+
+ServiceWorkerRequestHandler::~ServiceWorkerRequestHandler() {
+}
+
+ServiceWorkerRequestHandler::ServiceWorkerRequestHandler(
+ base::WeakPtr<ServiceWorkerContextCore> context,
+ base::WeakPtr<ServiceWorkerProviderHost> provider_host,
+ base::WeakPtr<webkit_blob::BlobStorageContext> blob_storage_context,
+ ResourceType::Type resource_type)
+ : context_(context),
+ provider_host_(provider_host),
+ blob_storage_context_(blob_storage_context),
+ resource_type_(resource_type) {
+}
+
+} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_request_handler.h b/chromium/content/browser/service_worker/service_worker_request_handler.h
new file mode 100644
index 00000000000..0c666b548df
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_request_handler.h
@@ -0,0 +1,83 @@
+// 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_SERVICE_WORKER_SERVICE_WORKER_REQUEST_HANDLER_H_
+#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_REQUEST_HANDLER_H_
+
+#include "base/basictypes.h"
+#include "base/memory/weak_ptr.h"
+#include "base/supports_user_data.h"
+#include "content/common/content_export.h"
+#include "content/common/service_worker/service_worker_status_code.h"
+#include "net/url_request/url_request_job_factory.h"
+#include "webkit/common/resource_type.h"
+
+namespace net {
+class NetworkDelegate;
+class URLRequest;
+class URLRequestInterceptor;
+}
+
+namespace webkit_blob {
+class BlobStorageContext;
+}
+
+namespace content {
+
+class ServiceWorkerContextCore;
+class ServiceWorkerContextWrapper;
+class ServiceWorkerProviderHost;
+
+// Abstract base class for routing network requests to ServiceWorkers.
+// Created one per URLRequest and attached to each request.
+class CONTENT_EXPORT ServiceWorkerRequestHandler
+ : public base::SupportsUserData::Data {
+ public:
+ // Attaches a newly created handler if the given |request| needs to
+ // be handled by ServiceWorker.
+ // TODO(kinuko): While utilizing UserData to attach data to URLRequest
+ // has some precedence, it might be better to attach this handler in a more
+ // explicit way within content layer, e.g. have ResourceRequestInfoImpl
+ // own it.
+ static void InitializeHandler(
+ net::URLRequest* request,
+ ServiceWorkerContextWrapper* context_wrapper,
+ webkit_blob::BlobStorageContext* blob_storage_context,
+ int process_id,
+ int provider_id,
+ ResourceType::Type resource_type);
+
+ // Returns the handler attached to |request|. This may return NULL
+ // if no handler is attached.
+ static ServiceWorkerRequestHandler* GetHandler(
+ net::URLRequest* request);
+
+ // Creates a protocol interceptor for ServiceWorker.
+ static scoped_ptr<net::URLRequestInterceptor> CreateInterceptor();
+
+ virtual ~ServiceWorkerRequestHandler();
+
+ // Called via custom URLRequestJobFactory.
+ virtual net::URLRequestJob* MaybeCreateJob(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) = 0;
+
+ protected:
+ ServiceWorkerRequestHandler(
+ base::WeakPtr<ServiceWorkerContextCore> context,
+ base::WeakPtr<ServiceWorkerProviderHost> provider_host,
+ base::WeakPtr<webkit_blob::BlobStorageContext> blob_storage_context,
+ ResourceType::Type resource_type);
+
+ base::WeakPtr<ServiceWorkerContextCore> context_;
+ base::WeakPtr<ServiceWorkerProviderHost> provider_host_;
+ base::WeakPtr<webkit_blob::BlobStorageContext> blob_storage_context_;
+ ResourceType::Type resource_type_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServiceWorkerRequestHandler);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_REQUEST_HANDLER_H_
diff --git a/chromium/content/browser/service_worker/service_worker_script_cache_map.cc b/chromium/content/browser/service_worker/service_worker_script_cache_map.cc
new file mode 100644
index 00000000000..60876b8d716
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_script_cache_map.cc
@@ -0,0 +1,72 @@
+// 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/service_worker/service_worker_script_cache_map.h"
+
+#include "base/logging.h"
+#include "content/browser/service_worker/service_worker_context_core.h"
+#include "content/browser/service_worker/service_worker_storage.h"
+#include "content/browser/service_worker/service_worker_version.h"
+#include "content/common/service_worker/service_worker_types.h"
+
+namespace content {
+
+ServiceWorkerScriptCacheMap::ServiceWorkerScriptCacheMap(
+ ServiceWorkerVersion* owner,
+ base::WeakPtr<ServiceWorkerContextCore> context)
+ : owner_(owner),
+ context_(context),
+ has_error_(false) {
+}
+
+ServiceWorkerScriptCacheMap::~ServiceWorkerScriptCacheMap() {
+}
+
+int64 ServiceWorkerScriptCacheMap::Lookup(const GURL& url) {
+ ResourceIDMap::const_iterator found = resource_ids_.find(url);
+ if (found == resource_ids_.end())
+ return kInvalidServiceWorkerResponseId;
+ return found->second;
+}
+
+void ServiceWorkerScriptCacheMap::NotifyStartedCaching(
+ const GURL& url, int64 resource_id) {
+ DCHECK_EQ(kInvalidServiceWorkerResponseId, Lookup(url));
+ DCHECK(owner_->status() == ServiceWorkerVersion::NEW);
+ resource_ids_[url] = resource_id;
+ context_->storage()->StoreUncommittedReponseId(resource_id);
+}
+
+void ServiceWorkerScriptCacheMap::NotifyFinishedCaching(
+ const GURL& url, bool success) {
+ DCHECK_NE(kInvalidServiceWorkerResponseId, Lookup(url));
+ DCHECK(owner_->status() == ServiceWorkerVersion::NEW);
+ if (!success) {
+ context_->storage()->DoomUncommittedResponse(Lookup(url));
+ has_error_ = true;
+ resource_ids_.erase(url);
+ }
+}
+
+void ServiceWorkerScriptCacheMap::GetResources(
+ std::vector<ServiceWorkerDatabase::ResourceRecord>* resources) {
+ DCHECK(resources->empty());
+ for (ResourceIDMap::const_iterator it = resource_ids_.begin();
+ it != resource_ids_.end(); ++it) {
+ resources->push_back(
+ ServiceWorkerDatabase::ResourceRecord(it->second, it->first));
+ }
+}
+
+void ServiceWorkerScriptCacheMap::SetResources(
+ const std::vector<ServiceWorkerDatabase::ResourceRecord>& resources) {
+ DCHECK(resource_ids_.empty());
+ typedef std::vector<ServiceWorkerDatabase::ResourceRecord> RecordVector;
+ for (RecordVector::const_iterator it = resources.begin();
+ it != resources.end(); ++it) {
+ resource_ids_[it->url] = it->resource_id;
+ }
+}
+
+} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_script_cache_map.h b/chromium/content/browser/service_worker/service_worker_script_cache_map.h
new file mode 100644
index 00000000000..4513f3d9ea7
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_script_cache_map.h
@@ -0,0 +1,62 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_SCRIPT_CACHE_MAP_H_
+#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_SCRIPT_CACHE_MAP_H_
+
+#include <map>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/weak_ptr.h"
+#include "content/browser/service_worker/service_worker_database.h"
+
+class GURL;
+
+namespace content {
+
+class ServiceWorkerContextCore;
+class ServiceWorkerVersion;
+
+// Class that maintains the mapping between urls and a resource id
+// for a particular versions implicit script resources.
+class ServiceWorkerScriptCacheMap {
+ public:
+ int64 Lookup(const GURL& url);
+
+ // Used during the initial run of a new version to build the map
+ // of resources ids.
+ void NotifyStartedCaching(const GURL& url, int64 resource_id);
+ void NotifyFinishedCaching(const GURL& url, bool success);
+
+ // Used to retrieve the results of the initial run of a new version.
+ bool HasError() const { return has_error_; }
+ void GetResources(
+ std::vector<ServiceWorkerDatabase::ResourceRecord>* resources);
+
+ // Used when loading an existing version.
+ void SetResources(
+ const std::vector<ServiceWorkerDatabase::ResourceRecord>& resources);
+
+ private:
+ typedef std::map<GURL, int64> ResourceIDMap;
+
+ // The version objects owns its script cache and provides a rawptr to it.
+ friend class ServiceWorkerVersion;
+ ServiceWorkerScriptCacheMap(
+ ServiceWorkerVersion* owner,
+ base::WeakPtr<ServiceWorkerContextCore> context);
+ ~ServiceWorkerScriptCacheMap();
+
+ ServiceWorkerVersion* owner_;
+ base::WeakPtr<ServiceWorkerContextCore> context_;
+ ResourceIDMap resource_ids_;
+ bool has_error_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServiceWorkerScriptCacheMap);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_SCRIPT_CACHE_MAP_H_
diff --git a/chromium/content/browser/service_worker/service_worker_storage.cc b/chromium/content/browser/service_worker/service_worker_storage.cc
index 16a671e20b8..8a924e0c723 100644
--- a/chromium/content/browser/service_worker/service_worker_storage.cc
+++ b/chromium/content/browser/service_worker/service_worker_storage.cc
@@ -6,189 +6,980 @@
#include <string>
-#include "base/strings/string_util.h"
-#include "content/browser/service_worker/service_worker_register_job.h"
+#include "base/bind_helpers.h"
+#include "base/message_loop/message_loop.h"
+#include "base/sequenced_task_runner.h"
+#include "base/task_runner_util.h"
+#include "content/browser/service_worker/service_worker_context_core.h"
+#include "content/browser/service_worker/service_worker_disk_cache.h"
+#include "content/browser/service_worker/service_worker_histograms.h"
+#include "content/browser/service_worker/service_worker_info.h"
#include "content/browser/service_worker/service_worker_registration.h"
+#include "content/browser/service_worker/service_worker_utils.h"
+#include "content/browser/service_worker/service_worker_version.h"
+#include "content/common/service_worker/service_worker_types.h"
#include "content/public/browser/browser_thread.h"
-#include "webkit/browser/quota/quota_manager.h"
+#include "net/base/net_errors.h"
+#include "webkit/browser/quota/quota_manager_proxy.h"
+
+namespace content {
namespace {
-// This is temporary until we figure out how registration ids will be
-// calculated.
-int64 NextRegistrationId() {
- static int64 worker_id = 0;
- return worker_id++;
+
+void RunSoon(const tracked_objects::Location& from_here,
+ const base::Closure& closure) {
+ base::MessageLoop::current()->PostTask(from_here, closure);
}
-} // namespace
-namespace content {
+void CompleteFindNow(
+ const scoped_refptr<ServiceWorkerRegistration>& registration,
+ ServiceWorkerStatusCode status,
+ const ServiceWorkerStorage::FindRegistrationCallback& callback) {
+ callback.Run(status, registration);
+}
+
+void CompleteFindSoon(
+ const tracked_objects::Location& from_here,
+ const scoped_refptr<ServiceWorkerRegistration>& registration,
+ ServiceWorkerStatusCode status,
+ const ServiceWorkerStorage::FindRegistrationCallback& callback) {
+ RunSoon(from_here, base::Bind(callback, status, registration));
+}
const base::FilePath::CharType kServiceWorkerDirectory[] =
- FILE_PATH_LITERAL("ServiceWorker");
+ FILE_PATH_LITERAL("Service Worker");
+const base::FilePath::CharType kDatabaseName[] =
+ FILE_PATH_LITERAL("Database");
+const base::FilePath::CharType kDiskCacheName[] =
+ FILE_PATH_LITERAL("Cache");
+
+const int kMaxMemDiskCacheSize = 10 * 1024 * 1024;
+const int kMaxDiskCacheSize = 250 * 1024 * 1024;
+
+void EmptyCompletionCallback(int) {}
+
+ServiceWorkerStatusCode DatabaseStatusToStatusCode(
+ ServiceWorkerDatabase::Status status) {
+ switch (status) {
+ case ServiceWorkerDatabase::STATUS_OK:
+ return SERVICE_WORKER_OK;
+ case ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND:
+ return SERVICE_WORKER_ERROR_NOT_FOUND;
+ case ServiceWorkerDatabase::STATUS_ERROR_MAX:
+ NOTREACHED();
+ default:
+ return SERVICE_WORKER_ERROR_FAILED;
+ }
+}
+
+} // namespace
+
+ServiceWorkerStorage::InitialData::InitialData()
+ : next_registration_id(kInvalidServiceWorkerRegistrationId),
+ next_version_id(kInvalidServiceWorkerVersionId),
+ next_resource_id(kInvalidServiceWorkerResourceId) {
+}
+
+ServiceWorkerStorage::InitialData::~InitialData() {
+}
ServiceWorkerStorage::ServiceWorkerStorage(
const base::FilePath& path,
+ base::WeakPtr<ServiceWorkerContextCore> context,
+ base::SequencedTaskRunner* database_task_runner,
+ base::MessageLoopProxy* disk_cache_thread,
quota::QuotaManagerProxy* quota_manager_proxy)
- : quota_manager_proxy_(quota_manager_proxy), weak_factory_(this) {
+ : next_registration_id_(kInvalidServiceWorkerRegistrationId),
+ next_version_id_(kInvalidServiceWorkerVersionId),
+ next_resource_id_(kInvalidServiceWorkerResourceId),
+ state_(UNINITIALIZED),
+ context_(context),
+ database_task_runner_(database_task_runner),
+ disk_cache_thread_(disk_cache_thread),
+ quota_manager_proxy_(quota_manager_proxy),
+ is_purge_pending_(false),
+ weak_factory_(this) {
if (!path.empty())
path_ = path.Append(kServiceWorkerDirectory);
+ database_.reset(new ServiceWorkerDatabase(GetDatabasePath()));
}
ServiceWorkerStorage::~ServiceWorkerStorage() {
- for (PatternToRegistrationMap::const_iterator iter =
- registration_by_pattern_.begin();
- iter != registration_by_pattern_.end();
- ++iter) {
- iter->second->Shutdown();
+ weak_factory_.InvalidateWeakPtrs();
+ database_task_runner_->DeleteSoon(FROM_HERE, database_.release());
+}
+
+void ServiceWorkerStorage::FindRegistrationForDocument(
+ const GURL& document_url,
+ const FindRegistrationCallback& callback) {
+ DCHECK(!document_url.has_ref());
+ if (!LazyInitialize(base::Bind(
+ &ServiceWorkerStorage::FindRegistrationForDocument,
+ weak_factory_.GetWeakPtr(), document_url, callback))) {
+ if (state_ != INITIALIZING || !context_) {
+ CompleteFindNow(scoped_refptr<ServiceWorkerRegistration>(),
+ SERVICE_WORKER_ERROR_FAILED, callback);
+ }
+ return;
+ }
+ DCHECK_EQ(INITIALIZED, state_);
+
+ // See if there are any stored registrations for the origin.
+ if (!ContainsKey(registered_origins_, document_url.GetOrigin())) {
+ // Look for something currently being installed.
+ scoped_refptr<ServiceWorkerRegistration> installing_registration =
+ FindInstallingRegistrationForDocument(document_url);
+ CompleteFindNow(
+ installing_registration,
+ installing_registration ?
+ SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND,
+ callback);
+ return;
}
- registration_by_pattern_.clear();
+
+ database_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &FindForDocumentInDB,
+ database_.get(),
+ base::MessageLoopProxy::current(),
+ document_url,
+ base::Bind(&ServiceWorkerStorage::DidFindRegistrationForDocument,
+ weak_factory_.GetWeakPtr(), document_url, callback)));
}
void ServiceWorkerStorage::FindRegistrationForPattern(
- const GURL& pattern,
+ const GURL& scope,
const FindRegistrationCallback& callback) {
- PatternToRegistrationMap::const_iterator match =
- registration_by_pattern_.find(pattern);
- if (match == registration_by_pattern_.end()) {
- BrowserThread::PostTask(
- BrowserThread::IO,
- FROM_HERE,
- base::Bind(callback,
- false /* found */,
- REGISTRATION_OK,
- scoped_refptr<ServiceWorkerRegistration>()));
+ if (!LazyInitialize(base::Bind(
+ &ServiceWorkerStorage::FindRegistrationForPattern,
+ weak_factory_.GetWeakPtr(), scope, callback))) {
+ if (state_ != INITIALIZING || !context_) {
+ CompleteFindSoon(FROM_HERE, scoped_refptr<ServiceWorkerRegistration>(),
+ SERVICE_WORKER_ERROR_FAILED, callback);
+ }
+ return;
+ }
+ DCHECK_EQ(INITIALIZED, state_);
+
+ // See if there are any stored registrations for the origin.
+ if (!ContainsKey(registered_origins_, scope.GetOrigin())) {
+ // Look for something currently being installed.
+ scoped_refptr<ServiceWorkerRegistration> installing_registration =
+ FindInstallingRegistrationForPattern(scope);
+ CompleteFindSoon(
+ FROM_HERE, installing_registration,
+ installing_registration ?
+ SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND,
+ callback);
return;
}
- BrowserThread::PostTask(
- BrowserThread::IO,
+
+ database_task_runner_->PostTask(
FROM_HERE,
- base::Bind(callback, true /* found */, REGISTRATION_OK, match->second));
+ base::Bind(
+ &FindForPatternInDB,
+ database_.get(),
+ base::MessageLoopProxy::current(),
+ scope,
+ base::Bind(&ServiceWorkerStorage::DidFindRegistrationForPattern,
+ weak_factory_.GetWeakPtr(), scope, callback)));
}
-void ServiceWorkerStorage::FindRegistrationForDocument(
- const GURL& document_url,
+void ServiceWorkerStorage::FindRegistrationForId(
+ int64 registration_id,
+ const GURL& origin,
const FindRegistrationCallback& callback) {
- // TODO(alecflett): This needs to be synchronous in the fast path,
- // but asynchronous in the slow path (when the patterns have to be
- // loaded from disk). For now it is always pessimistically async.
- for (PatternToRegistrationMap::const_iterator it =
- registration_by_pattern_.begin();
- it != registration_by_pattern_.end();
- ++it) {
- if (PatternMatches(it->first, document_url)) {
- BrowserThread::PostTask(
- BrowserThread::IO,
- FROM_HERE,
- base::Bind(callback,
- true /* found */,
- REGISTRATION_OK,
- scoped_refptr<ServiceWorkerRegistration>(it->second)));
- return;
+ if (!LazyInitialize(base::Bind(
+ &ServiceWorkerStorage::FindRegistrationForId,
+ weak_factory_.GetWeakPtr(), registration_id, origin, callback))) {
+ if (state_ != INITIALIZING || !context_) {
+ CompleteFindNow(scoped_refptr<ServiceWorkerRegistration>(),
+ SERVICE_WORKER_ERROR_FAILED, callback);
}
+ return;
+ }
+ DCHECK_EQ(INITIALIZED, state_);
+
+ // See if there are any stored registrations for the origin.
+ if (!ContainsKey(registered_origins_, origin)) {
+ // Look for something currently being installed.
+ scoped_refptr<ServiceWorkerRegistration> installing_registration =
+ FindInstallingRegistrationForId(registration_id);
+ CompleteFindNow(
+ installing_registration,
+ installing_registration ?
+ SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND,
+ callback);
+ return;
}
- BrowserThread::PostTask(
- BrowserThread::IO,
+
+ scoped_refptr<ServiceWorkerRegistration> registration =
+ context_->GetLiveRegistration(registration_id);
+ if (registration) {
+ CompleteFindNow(registration, SERVICE_WORKER_OK, callback);
+ return;
+ }
+
+ database_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&FindForIdInDB,
+ database_.get(),
+ base::MessageLoopProxy::current(),
+ registration_id, origin,
+ base::Bind(&ServiceWorkerStorage::DidFindRegistrationForId,
+ weak_factory_.GetWeakPtr(), callback)));
+}
+
+void ServiceWorkerStorage::GetAllRegistrations(
+ const GetAllRegistrationInfosCallback& callback) {
+ if (!LazyInitialize(base::Bind(
+ &ServiceWorkerStorage::GetAllRegistrations,
+ weak_factory_.GetWeakPtr(), callback))) {
+ if (state_ != INITIALIZING || !context_) {
+ RunSoon(FROM_HERE, base::Bind(
+ callback, std::vector<ServiceWorkerRegistrationInfo>()));
+ }
+ return;
+ }
+ DCHECK_EQ(INITIALIZED, state_);
+
+ RegistrationList* registrations = new RegistrationList;
+ PostTaskAndReplyWithResult(
+ database_task_runner_,
FROM_HERE,
- base::Bind(callback,
- false /* found */,
- REGISTRATION_OK,
- scoped_refptr<ServiceWorkerRegistration>()));
-}
-
-void ServiceWorkerStorage::Register(const GURL& pattern,
- const GURL& script_url,
- const RegistrationCallback& callback) {
- scoped_ptr<ServiceWorkerRegisterJob> job(new ServiceWorkerRegisterJob(
- weak_factory_.GetWeakPtr(),
- base::Bind(&ServiceWorkerStorage::RegisterComplete,
+ base::Bind(&ServiceWorkerDatabase::GetAllRegistrations,
+ base::Unretained(database_.get()),
+ base::Unretained(registrations)),
+ base::Bind(&ServiceWorkerStorage::DidGetAllRegistrations,
weak_factory_.GetWeakPtr(),
- callback)));
- job->StartRegister(pattern, script_url);
- registration_jobs_.push_back(job.release());
+ callback,
+ base::Owned(registrations)));
+}
+
+void ServiceWorkerStorage::StoreRegistration(
+ ServiceWorkerRegistration* registration,
+ ServiceWorkerVersion* version,
+ const StatusCallback& callback) {
+ DCHECK(registration);
+ DCHECK(version);
+
+ DCHECK(state_ == INITIALIZED || state_ == DISABLED);
+ if (state_ != INITIALIZED || !context_) {
+ RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
+ return;
+ }
+
+ ServiceWorkerDatabase::RegistrationData data;
+ data.registration_id = registration->id();
+ data.scope = registration->pattern();
+ data.script = registration->script_url();
+ data.has_fetch_handler = true;
+ data.version_id = version->version_id();
+ data.last_update_check = base::Time::Now();
+ data.is_active = false; // initially stored in the waiting state
+
+ ResourceList resources;
+ version->script_cache_map()->GetResources(&resources);
+
+ database_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&WriteRegistrationInDB,
+ database_.get(),
+ base::MessageLoopProxy::current(),
+ data, resources,
+ base::Bind(&ServiceWorkerStorage::DidStoreRegistration,
+ weak_factory_.GetWeakPtr(),
+ callback)));
}
-void ServiceWorkerStorage::Unregister(const GURL& pattern,
- const UnregistrationCallback& callback) {
- scoped_ptr<ServiceWorkerRegisterJob> job(new ServiceWorkerRegisterJob(
- weak_factory_.GetWeakPtr(),
- base::Bind(&ServiceWorkerStorage::UnregisterComplete,
+void ServiceWorkerStorage::UpdateToActiveState(
+ ServiceWorkerRegistration* registration,
+ const StatusCallback& callback) {
+ DCHECK(registration);
+
+ DCHECK(state_ == INITIALIZED || state_ == DISABLED);
+ if (state_ != INITIALIZED || !context_) {
+ RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
+ return;
+ }
+
+ PostTaskAndReplyWithResult(
+ database_task_runner_,
+ FROM_HERE,
+ base::Bind(&ServiceWorkerDatabase::UpdateVersionToActive,
+ base::Unretained(database_.get()),
+ registration->id(),
+ registration->script_url().GetOrigin()),
+ base::Bind(&ServiceWorkerStorage::DidUpdateToActiveState,
weak_factory_.GetWeakPtr(),
- callback)));
- job->StartUnregister(pattern);
- registration_jobs_.push_back(job.release());
-}
-
-scoped_refptr<ServiceWorkerRegistration> ServiceWorkerStorage::RegisterInternal(
- const GURL& pattern,
- const GURL& script_url) {
-
- PatternToRegistrationMap::const_iterator current(
- registration_by_pattern_.find(pattern));
- DCHECK(current == registration_by_pattern_.end() ||
- current->second->script_url() == script_url);
-
- if (current == registration_by_pattern_.end()) {
- scoped_refptr<ServiceWorkerRegistration> registration(
- new ServiceWorkerRegistration(
- pattern, script_url, NextRegistrationId()));
- // TODO(alecflett): version upgrade path.
- registration_by_pattern_[pattern] = registration;
- return registration;
+ callback));
+}
+
+void ServiceWorkerStorage::DeleteRegistration(
+ int64 registration_id,
+ const GURL& origin,
+ const StatusCallback& callback) {
+ DCHECK(state_ == INITIALIZED || state_ == DISABLED);
+ if (state_ != INITIALIZED || !context_) {
+ RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
+ return;
}
- return current->second;
+ database_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&DeleteRegistrationFromDB,
+ database_.get(),
+ base::MessageLoopProxy::current(),
+ registration_id, origin,
+ base::Bind(&ServiceWorkerStorage::DidDeleteRegistration,
+ weak_factory_.GetWeakPtr(), origin, callback)));
+
+ // TODO(michaeln): Either its instance should also be
+ // removed from liveregistrations map or the live object
+ // should marked as deleted in some way and not 'findable'
+ // thereafter.
+}
+
+scoped_ptr<ServiceWorkerResponseReader>
+ServiceWorkerStorage::CreateResponseReader(int64 response_id) {
+ return make_scoped_ptr(
+ new ServiceWorkerResponseReader(response_id, disk_cache()));
}
-void ServiceWorkerStorage::UnregisterInternal(const GURL& pattern) {
- PatternToRegistrationMap::iterator match =
- registration_by_pattern_.find(pattern);
- if (match != registration_by_pattern_.end()) {
- match->second->Shutdown();
- registration_by_pattern_.erase(match);
+scoped_ptr<ServiceWorkerResponseWriter>
+ServiceWorkerStorage::CreateResponseWriter(int64 response_id) {
+ return make_scoped_ptr(
+ new ServiceWorkerResponseWriter(response_id, disk_cache()));
+}
+
+void ServiceWorkerStorage::StoreUncommittedReponseId(int64 id) {
+ DCHECK_NE(kInvalidServiceWorkerResponseId, id);
+ database_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(base::IgnoreResult(
+ &ServiceWorkerDatabase::WriteUncommittedResourceIds),
+ base::Unretained(database_.get()),
+ std::set<int64>(&id, &id + 1)));
+}
+
+void ServiceWorkerStorage::DoomUncommittedResponse(int64 id) {
+ DCHECK_NE(kInvalidServiceWorkerResponseId, id);
+ database_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(base::IgnoreResult(
+ &ServiceWorkerDatabase::PurgeUncommittedResourceIds),
+ base::Unretained(database_.get()),
+ std::set<int64>(&id, &id + 1)));
+ StartPurgingResources(std::vector<int64>(1, id));
+}
+
+int64 ServiceWorkerStorage::NewRegistrationId() {
+ if (state_ == DISABLED)
+ return kInvalidServiceWorkerRegistrationId;
+ DCHECK_EQ(INITIALIZED, state_);
+ return next_registration_id_++;
+}
+
+int64 ServiceWorkerStorage::NewVersionId() {
+ if (state_ == DISABLED)
+ return kInvalidServiceWorkerVersionId;
+ DCHECK_EQ(INITIALIZED, state_);
+ return next_version_id_++;
+}
+
+int64 ServiceWorkerStorage::NewResourceId() {
+ if (state_ == DISABLED)
+ return kInvalidServiceWorkerResourceId;
+ DCHECK_EQ(INITIALIZED, state_);
+ return next_resource_id_++;
+}
+
+void ServiceWorkerStorage::NotifyInstallingRegistration(
+ ServiceWorkerRegistration* registration) {
+ installing_registrations_[registration->id()] = registration;
+}
+
+void ServiceWorkerStorage::NotifyDoneInstallingRegistration(
+ ServiceWorkerRegistration* registration,
+ ServiceWorkerVersion* version,
+ ServiceWorkerStatusCode status) {
+ installing_registrations_.erase(registration->id());
+ if (status != SERVICE_WORKER_OK && version) {
+ ResourceList resources;
+ version->script_cache_map()->GetResources(&resources);
+
+ std::set<int64> ids;
+ for (size_t i = 0; i < resources.size(); ++i)
+ ids.insert(resources[i].resource_id);
+
+ database_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(base::IgnoreResult(
+ &ServiceWorkerDatabase::PurgeUncommittedResourceIds),
+ base::Unretained(database_.get()),
+ ids));
+
+ StartPurgingResources(resources);
}
}
-bool ServiceWorkerStorage::PatternMatches(const GURL& pattern,
- const GURL& url) {
- // This is a really basic, naive
- // TODO(alecflett): Formalize what pattern matches mean.
- // Temporarily borrowed directly from appcache::Namespace::IsMatch().
- // We have to escape '?' characters since MatchPattern also treats those
- // as wildcards which we don't want here, we only do '*'s.
- std::string pattern_spec(pattern.spec());
- if (pattern.has_query())
- ReplaceSubstringsAfterOffset(&pattern_spec, 0, "?", "\\?");
- return MatchPattern(url.spec(), pattern_spec);
+base::FilePath ServiceWorkerStorage::GetDatabasePath() {
+ if (path_.empty())
+ return base::FilePath();
+ return path_.Append(kDatabaseName);
+}
+
+base::FilePath ServiceWorkerStorage::GetDiskCachePath() {
+ if (path_.empty())
+ return base::FilePath();
+ return path_.Append(kDiskCacheName);
}
-void ServiceWorkerStorage::EraseJob(ServiceWorkerRegisterJob* job) {
- ScopedVector<ServiceWorkerRegisterJob>::iterator job_position =
- registration_jobs_.begin();
- for (; job_position != registration_jobs_.end(); ++job_position) {
- if (*job_position == job) {
- registration_jobs_.erase(job_position);
- return;
+bool ServiceWorkerStorage::LazyInitialize(const base::Closure& callback) {
+ if (!context_)
+ return false;
+
+ switch (state_) {
+ case INITIALIZED:
+ return true;
+ case DISABLED:
+ return false;
+ case INITIALIZING:
+ pending_tasks_.push_back(callback);
+ return false;
+ case UNINITIALIZED:
+ pending_tasks_.push_back(callback);
+ // Fall-through.
+ }
+
+ state_ = INITIALIZING;
+ database_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&ReadInitialDataFromDB,
+ database_.get(),
+ base::MessageLoopProxy::current(),
+ base::Bind(&ServiceWorkerStorage::DidReadInitialData,
+ weak_factory_.GetWeakPtr())));
+ return false;
+}
+
+void ServiceWorkerStorage::DidReadInitialData(
+ InitialData* data,
+ ServiceWorkerDatabase::Status status) {
+ DCHECK(data);
+ DCHECK_EQ(INITIALIZING, state_);
+
+ if (status == ServiceWorkerDatabase::STATUS_OK) {
+ next_registration_id_ = data->next_registration_id;
+ next_version_id_ = data->next_version_id;
+ next_resource_id_ = data->next_resource_id;
+ registered_origins_.swap(data->origins);
+ state_ = INITIALIZED;
+ } else {
+ // TODO(nhiroki): If status==STATUS_ERROR_CORRUPTED, do corruption recovery
+ // (http://crbug.com/371675).
+ DLOG(WARNING) << "Failed to initialize: " << status;
+ state_ = DISABLED;
+ }
+
+ for (std::vector<base::Closure>::const_iterator it = pending_tasks_.begin();
+ it != pending_tasks_.end(); ++it) {
+ RunSoon(FROM_HERE, *it);
+ }
+ pending_tasks_.clear();
+}
+
+void ServiceWorkerStorage::DidFindRegistrationForDocument(
+ const GURL& document_url,
+ const FindRegistrationCallback& callback,
+ const ServiceWorkerDatabase::RegistrationData& data,
+ const ResourceList& resources,
+ ServiceWorkerDatabase::Status status) {
+ if (status == ServiceWorkerDatabase::STATUS_OK) {
+ callback.Run(SERVICE_WORKER_OK, GetOrCreateRegistration(data, resources));
+ return;
+ }
+
+ if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
+ // Look for something currently being installed.
+ scoped_refptr<ServiceWorkerRegistration> installing_registration =
+ FindInstallingRegistrationForDocument(document_url);
+ callback.Run(installing_registration ?
+ SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND,
+ installing_registration);
+ return;
+ }
+
+ // TODO(nhiroki): Handle database error (http://crbug.com/371675).
+ callback.Run(DatabaseStatusToStatusCode(status),
+ scoped_refptr<ServiceWorkerRegistration>());
+}
+
+void ServiceWorkerStorage::DidFindRegistrationForPattern(
+ const GURL& scope,
+ const FindRegistrationCallback& callback,
+ const ServiceWorkerDatabase::RegistrationData& data,
+ const ResourceList& resources,
+ ServiceWorkerDatabase::Status status) {
+ if (status == ServiceWorkerDatabase::STATUS_OK) {
+ callback.Run(SERVICE_WORKER_OK, GetOrCreateRegistration(data, resources));
+ return;
+ }
+
+ if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
+ scoped_refptr<ServiceWorkerRegistration> installing_registration =
+ FindInstallingRegistrationForPattern(scope);
+ callback.Run(installing_registration ?
+ SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND,
+ installing_registration);
+ return;
+ }
+
+ // TODO(nhiroki): Handle database error (http://crbug.com/371675).
+ callback.Run(DatabaseStatusToStatusCode(status),
+ scoped_refptr<ServiceWorkerRegistration>());
+}
+
+void ServiceWorkerStorage::DidFindRegistrationForId(
+ const FindRegistrationCallback& callback,
+ const ServiceWorkerDatabase::RegistrationData& data,
+ const ResourceList& resources,
+ ServiceWorkerDatabase::Status status) {
+ if (status == ServiceWorkerDatabase::STATUS_OK) {
+ callback.Run(SERVICE_WORKER_OK,
+ GetOrCreateRegistration(data, resources));
+ return;
+ }
+ // TODO(nhiroki): Handle database error (http://crbug.com/371675).
+ callback.Run(DatabaseStatusToStatusCode(status),
+ scoped_refptr<ServiceWorkerRegistration>());
+}
+
+void ServiceWorkerStorage::DidGetAllRegistrations(
+ const GetAllRegistrationInfosCallback& callback,
+ RegistrationList* registrations,
+ ServiceWorkerDatabase::Status status) {
+ DCHECK(registrations);
+ if (status != ServiceWorkerDatabase::STATUS_OK) {
+ // TODO(nhiroki): Handle database error (http://crbug.com/371675).
+ callback.Run(std::vector<ServiceWorkerRegistrationInfo>());
+ return;
+ }
+
+ // Add all stored registrations.
+ std::set<int64> pushed_registrations;
+ std::vector<ServiceWorkerRegistrationInfo> infos;
+ for (RegistrationList::const_iterator it = registrations->begin();
+ it != registrations->end(); ++it) {
+ const bool inserted =
+ pushed_registrations.insert(it->registration_id).second;
+ DCHECK(inserted);
+
+ ServiceWorkerRegistration* registration =
+ context_->GetLiveRegistration(it->registration_id);
+ if (registration) {
+ infos.push_back(registration->GetInfo());
+ continue;
+ }
+
+ ServiceWorkerRegistrationInfo info;
+ info.pattern = it->scope;
+ info.script_url = it->script;
+ info.registration_id = it->registration_id;
+ if (ServiceWorkerVersion* version =
+ context_->GetLiveVersion(it->version_id)) {
+ if (it->is_active)
+ info.active_version = version->GetInfo();
+ else
+ info.waiting_version = version->GetInfo();
+ infos.push_back(info);
+ continue;
+ }
+
+ if (it->is_active) {
+ info.active_version.is_null = false;
+ info.active_version.status = ServiceWorkerVersion::ACTIVE;
+ info.active_version.version_id = it->version_id;
+ } else {
+ info.waiting_version.is_null = false;
+ info.waiting_version.status = ServiceWorkerVersion::INSTALLED;
+ info.waiting_version.version_id = it->version_id;
}
+ infos.push_back(info);
}
- NOTREACHED() << "Deleting non-existent job. ";
+
+ // Add unstored registrations that are being installed.
+ for (RegistrationRefsById::const_iterator it =
+ installing_registrations_.begin();
+ it != installing_registrations_.end(); ++it) {
+ if (pushed_registrations.insert(it->first).second)
+ infos.push_back(it->second->GetInfo());
+ }
+
+ callback.Run(infos);
}
-void ServiceWorkerStorage::UnregisterComplete(
- const UnregistrationCallback& callback,
- ServiceWorkerRegisterJob* job,
- ServiceWorkerRegistrationStatus status,
- ServiceWorkerRegistration* previous_registration) {
- callback.Run(status);
- EraseJob(job);
+void ServiceWorkerStorage::DidStoreRegistration(
+ const StatusCallback& callback,
+ const GURL& origin,
+ const std::vector<int64>& newly_purgeable_resources,
+ ServiceWorkerDatabase::Status status) {
+ if (status != ServiceWorkerDatabase::STATUS_OK) {
+ // TODO(nhiroki): Handle database error (http://crbug.com/371675).
+ callback.Run(DatabaseStatusToStatusCode(status));
+ return;
+ }
+ registered_origins_.insert(origin);
+ callback.Run(SERVICE_WORKER_OK);
+ StartPurgingResources(newly_purgeable_resources);
}
-void ServiceWorkerStorage::RegisterComplete(
- const RegistrationCallback& callback,
- ServiceWorkerRegisterJob* job,
- ServiceWorkerRegistrationStatus status,
- ServiceWorkerRegistration* registration) {
- callback.Run(status, registration);
- EraseJob(job);
+void ServiceWorkerStorage::DidUpdateToActiveState(
+ const StatusCallback& callback,
+ ServiceWorkerDatabase::Status status) {
+ // TODO(nhiroki): Handle database error (http://crbug.com/371675).
+ callback.Run(DatabaseStatusToStatusCode(status));
+}
+
+void ServiceWorkerStorage::DidDeleteRegistration(
+ const GURL& origin,
+ const StatusCallback& callback,
+ bool origin_is_deletable,
+ const std::vector<int64>& newly_purgeable_resources,
+ ServiceWorkerDatabase::Status status) {
+ if (status != ServiceWorkerDatabase::STATUS_OK) {
+ // TODO(nhiroki): Handle database error (http://crbug.com/371675).
+ callback.Run(DatabaseStatusToStatusCode(status));
+ return;
+ }
+ if (origin_is_deletable)
+ registered_origins_.erase(origin);
+ callback.Run(SERVICE_WORKER_OK);
+ StartPurgingResources(newly_purgeable_resources);
+}
+
+scoped_refptr<ServiceWorkerRegistration>
+ServiceWorkerStorage::GetOrCreateRegistration(
+ const ServiceWorkerDatabase::RegistrationData& data,
+ const ResourceList& resources) {
+ scoped_refptr<ServiceWorkerRegistration> registration =
+ context_->GetLiveRegistration(data.registration_id);
+ if (registration)
+ return registration;
+
+ registration = new ServiceWorkerRegistration(
+ data.scope, data.script, data.registration_id, context_);
+ scoped_refptr<ServiceWorkerVersion> version =
+ context_->GetLiveVersion(data.version_id);
+ if (!version) {
+ version = new ServiceWorkerVersion(registration, data.version_id, context_);
+ version->SetStatus(data.is_active ?
+ ServiceWorkerVersion::ACTIVE : ServiceWorkerVersion::INSTALLED);
+ version->script_cache_map()->SetResources(resources);
+ }
+
+ if (version->status() == ServiceWorkerVersion::ACTIVE)
+ registration->set_active_version(version);
+ else if (version->status() == ServiceWorkerVersion::INSTALLED)
+ registration->set_waiting_version(version);
+ else
+ NOTREACHED();
+ // TODO(michaeln): Hmmm, what if DeleteReg was invoked after
+ // the Find result we're returning here? NOTREACHED condition?
+ return registration;
+}
+
+ServiceWorkerRegistration*
+ServiceWorkerStorage::FindInstallingRegistrationForDocument(
+ const GURL& document_url) {
+ DCHECK(!document_url.has_ref());
+
+ LongestScopeMatcher matcher(document_url);
+ ServiceWorkerRegistration* match = NULL;
+
+ // TODO(nhiroki): This searches over installing registrations linearly and it
+ // couldn't be scalable. Maybe the regs should be partitioned by origin.
+ for (RegistrationRefsById::const_iterator it =
+ installing_registrations_.begin();
+ it != installing_registrations_.end(); ++it) {
+ if (matcher.MatchLongest(it->second->pattern()))
+ match = it->second;
+ }
+ return match;
+}
+
+ServiceWorkerRegistration*
+ServiceWorkerStorage::FindInstallingRegistrationForPattern(
+ const GURL& scope) {
+ for (RegistrationRefsById::const_iterator it =
+ installing_registrations_.begin();
+ it != installing_registrations_.end(); ++it) {
+ if (it->second->pattern() == scope)
+ return it->second;
+ }
+ return NULL;
+}
+
+ServiceWorkerRegistration*
+ServiceWorkerStorage::FindInstallingRegistrationForId(
+ int64 registration_id) {
+ RegistrationRefsById::const_iterator found =
+ installing_registrations_.find(registration_id);
+ if (found == installing_registrations_.end())
+ return NULL;
+ return found->second;
+}
+
+ServiceWorkerDiskCache* ServiceWorkerStorage::disk_cache() {
+ if (disk_cache_)
+ return disk_cache_.get();
+
+ disk_cache_.reset(new ServiceWorkerDiskCache);
+
+ base::FilePath path = GetDiskCachePath();
+ if (path.empty()) {
+ int rv = disk_cache_->InitWithMemBackend(
+ kMaxMemDiskCacheSize,
+ base::Bind(&EmptyCompletionCallback));
+ DCHECK_EQ(net::OK, rv);
+ return disk_cache_.get();
+ }
+
+ int rv = disk_cache_->InitWithDiskBackend(
+ path, kMaxDiskCacheSize, false,
+ disk_cache_thread_.get(),
+ base::Bind(&ServiceWorkerStorage::OnDiskCacheInitialized,
+ weak_factory_.GetWeakPtr()));
+ if (rv != net::ERR_IO_PENDING)
+ OnDiskCacheInitialized(rv);
+
+ return disk_cache_.get();
+}
+
+void ServiceWorkerStorage::OnDiskCacheInitialized(int rv) {
+ if (rv != net::OK) {
+ LOG(ERROR) << "Failed to open the serviceworker diskcache: "
+ << net::ErrorToString(rv);
+ // TODO(michaeln): DeleteAndStartOver()
+ disk_cache_->Disable();
+ state_ = DISABLED;
+ }
+ ServiceWorkerHistograms::CountInitDiskCacheResult(rv == net::OK);
+}
+
+void ServiceWorkerStorage::StartPurgingResources(
+ const std::vector<int64>& ids) {
+ for (size_t i = 0; i < ids.size(); ++i)
+ purgeable_reource_ids_.push_back(ids[i]);
+ ContinuePurgingResources();
+}
+
+void ServiceWorkerStorage::StartPurgingResources(
+ const ResourceList& resources) {
+ for (size_t i = 0; i < resources.size(); ++i)
+ purgeable_reource_ids_.push_back(resources[i].resource_id);
+ ContinuePurgingResources();
+}
+
+void ServiceWorkerStorage::ContinuePurgingResources() {
+ if (purgeable_reource_ids_.empty() || is_purge_pending_)
+ return;
+
+ // Do one at a time until we're done, use RunSoon to avoid recursion when
+ // DoomEntry returns immediately.
+ is_purge_pending_ = true;
+ int64 id = purgeable_reource_ids_.front();
+ purgeable_reource_ids_.pop_front();
+ RunSoon(FROM_HERE,
+ base::Bind(&ServiceWorkerStorage::PurgeResource,
+ weak_factory_.GetWeakPtr(), id));
+}
+
+void ServiceWorkerStorage::PurgeResource(int64 id) {
+ DCHECK(is_purge_pending_);
+ int rv = disk_cache()->DoomEntry(
+ id, base::Bind(&ServiceWorkerStorage::OnResourcePurged,
+ weak_factory_.GetWeakPtr(), id));
+ if (rv != net::ERR_IO_PENDING)
+ OnResourcePurged(id, rv);
+}
+
+void ServiceWorkerStorage::OnResourcePurged(int64 id, int rv) {
+ DCHECK(is_purge_pending_);
+ is_purge_pending_ = false;
+
+ database_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(base::IgnoreResult(
+ &ServiceWorkerDatabase::ClearPurgeableResourceIds),
+ base::Unretained(database_.get()),
+ std::set<int64>(&id, &id + 1)));
+
+ ContinuePurgingResources();
+}
+
+void ServiceWorkerStorage::ReadInitialDataFromDB(
+ ServiceWorkerDatabase* database,
+ scoped_refptr<base::SequencedTaskRunner> original_task_runner,
+ const InitializeCallback& callback) {
+ DCHECK(database);
+ scoped_ptr<ServiceWorkerStorage::InitialData> data(
+ new ServiceWorkerStorage::InitialData());
+
+ ServiceWorkerDatabase::Status status =
+ database->GetNextAvailableIds(&data->next_registration_id,
+ &data->next_version_id,
+ &data->next_resource_id);
+ if (status != ServiceWorkerDatabase::STATUS_OK) {
+ original_task_runner->PostTask(
+ FROM_HERE, base::Bind(callback, base::Owned(data.release()), status));
+ return;
+ }
+
+ status = database->GetOriginsWithRegistrations(&data->origins);
+ original_task_runner->PostTask(
+ FROM_HERE, base::Bind(callback, base::Owned(data.release()), status));
+}
+
+void ServiceWorkerStorage::DeleteRegistrationFromDB(
+ ServiceWorkerDatabase* database,
+ scoped_refptr<base::SequencedTaskRunner> original_task_runner,
+ int64 registration_id,
+ const GURL& origin,
+ const DeleteRegistrationCallback& callback) {
+ DCHECK(database);
+
+ std::vector<int64> newly_purgeable_resources;
+ ServiceWorkerDatabase::Status status =
+ database->DeleteRegistration(registration_id, origin,
+ &newly_purgeable_resources);
+ if (status != ServiceWorkerDatabase::STATUS_OK) {
+ original_task_runner->PostTask(
+ FROM_HERE, base::Bind(callback, false, std::vector<int64>(), status));
+ return;
+ }
+
+ // TODO(nhiroki): Add convenient method to ServiceWorkerDatabase to check the
+ // unique origin list.
+ std::vector<ServiceWorkerDatabase::RegistrationData> registrations;
+ status = database->GetRegistrationsForOrigin(origin, &registrations);
+ if (status != ServiceWorkerDatabase::STATUS_OK) {
+ original_task_runner->PostTask(
+ FROM_HERE, base::Bind(callback, false, std::vector<int64>(), status));
+ return;
+ }
+
+ bool deletable = registrations.empty();
+ original_task_runner->PostTask(
+ FROM_HERE, base::Bind(callback, deletable,
+ newly_purgeable_resources, status));
+}
+
+void ServiceWorkerStorage::WriteRegistrationInDB(
+ ServiceWorkerDatabase* database,
+ scoped_refptr<base::SequencedTaskRunner> original_task_runner,
+ const ServiceWorkerDatabase::RegistrationData& data,
+ const ResourceList& resources,
+ const WriteRegistrationCallback& callback) {
+ DCHECK(database);
+ std::vector<int64> newly_purgeable_resources;
+ ServiceWorkerDatabase::Status status =
+ database->WriteRegistration(data, resources, &newly_purgeable_resources);
+ original_task_runner->PostTask(
+ FROM_HERE,
+ base::Bind(callback, data.script.GetOrigin(),
+ newly_purgeable_resources, status));
+}
+
+void ServiceWorkerStorage::FindForDocumentInDB(
+ ServiceWorkerDatabase* database,
+ scoped_refptr<base::SequencedTaskRunner> original_task_runner,
+ const GURL& document_url,
+ const FindInDBCallback& callback) {
+ GURL origin = document_url.GetOrigin();
+ RegistrationList registrations;
+ ServiceWorkerDatabase::Status status =
+ database->GetRegistrationsForOrigin(origin, &registrations);
+ if (status != ServiceWorkerDatabase::STATUS_OK) {
+ original_task_runner->PostTask(
+ FROM_HERE,
+ base::Bind(callback,
+ ServiceWorkerDatabase::RegistrationData(),
+ ResourceList(),
+ status));
+ return;
+ }
+
+ ServiceWorkerDatabase::RegistrationData data;
+ ResourceList resources;
+ status = ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
+
+ // Find one with a pattern match.
+ LongestScopeMatcher matcher(document_url);
+ int64 match = kInvalidServiceWorkerRegistrationId;
+ for (size_t i = 0; i < registrations.size(); ++i) {
+ if (matcher.MatchLongest(registrations[i].scope))
+ match = registrations[i].registration_id;
+ }
+
+ if (match != kInvalidServiceWorkerRegistrationId)
+ status = database->ReadRegistration(match, origin, &data, &resources);
+
+ original_task_runner->PostTask(
+ FROM_HERE,
+ base::Bind(callback, data, resources, status));
+}
+
+void ServiceWorkerStorage::FindForPatternInDB(
+ ServiceWorkerDatabase* database,
+ scoped_refptr<base::SequencedTaskRunner> original_task_runner,
+ const GURL& scope,
+ const FindInDBCallback& callback) {
+ GURL origin = scope.GetOrigin();
+ std::vector<ServiceWorkerDatabase::RegistrationData> registrations;
+ ServiceWorkerDatabase::Status status =
+ database->GetRegistrationsForOrigin(origin, &registrations);
+ if (status != ServiceWorkerDatabase::STATUS_OK) {
+ original_task_runner->PostTask(
+ FROM_HERE,
+ base::Bind(callback,
+ ServiceWorkerDatabase::RegistrationData(),
+ ResourceList(),
+ status));
+ return;
+ }
+
+ // Find one with an exact matching scope.
+ ServiceWorkerDatabase::RegistrationData data;
+ ResourceList resources;
+ status = ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
+ for (RegistrationList::const_iterator it = registrations.begin();
+ it != registrations.end(); ++it) {
+ if (scope != it->scope)
+ continue;
+ status = database->ReadRegistration(it->registration_id, origin,
+ &data, &resources);
+ break; // We're done looping.
+ }
+
+ original_task_runner->PostTask(
+ FROM_HERE,
+ base::Bind(callback, data, resources, status));
+}
+
+void ServiceWorkerStorage::FindForIdInDB(
+ ServiceWorkerDatabase* database,
+ scoped_refptr<base::SequencedTaskRunner> original_task_runner,
+ int64 registration_id,
+ const GURL& origin,
+ const FindInDBCallback& callback) {
+ ServiceWorkerDatabase::RegistrationData data;
+ ResourceList resources;
+ ServiceWorkerDatabase::Status status =
+ database->ReadRegistration(registration_id, origin, &data, &resources);
+ original_task_runner->PostTask(
+ FROM_HERE, base::Bind(callback, data, resources, status));
}
} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_storage.h b/chromium/content/browser/service_worker/service_worker_storage.h
index b3f1dbfaf69..5d89864dc21 100644
--- a/chromium/content/browser/service_worker/service_worker_storage.h
+++ b/chromium/content/browser/service_worker/service_worker_storage.h
@@ -5,99 +5,297 @@
#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_STORAGE_H_
#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_STORAGE_H_
+#include <deque>
#include <map>
+#include <set>
+#include <vector>
#include "base/bind.h"
#include "base/files/file_path.h"
#include "base/gtest_prod_util.h"
#include "base/memory/scoped_vector.h"
-#include "content/browser/service_worker/service_worker_registration_status.h"
+#include "base/memory/weak_ptr.h"
+#include "content/browser/service_worker/service_worker_database.h"
#include "content/common/content_export.h"
+#include "content/common/service_worker/service_worker_status_code.h"
#include "url/gurl.h"
+namespace base {
+class MessageLoopProxy;
+class SequencedTaskRunner;
+}
+
namespace quota {
class QuotaManagerProxy;
}
namespace content {
+class ServiceWorkerContextCore;
+class ServiceWorkerDiskCache;
class ServiceWorkerRegistration;
-class ServiceWorkerRegisterJob;
+class ServiceWorkerRegistrationInfo;
+class ServiceWorkerResponseReader;
+class ServiceWorkerResponseWriter;
+class ServiceWorkerVersion;
-// This class provides an interface to load registration data and
-// instantiate ServiceWorkerRegistration objects. Any asynchronous
-// operations are run through instances of ServiceWorkerRegisterJob.
+// This class provides an interface to store and retrieve ServiceWorker
+// registration data.
class CONTENT_EXPORT ServiceWorkerStorage {
public:
- ServiceWorkerStorage(const base::FilePath& path,
- quota::QuotaManagerProxy* quota_manager_proxy);
- ~ServiceWorkerStorage();
-
- typedef base::Callback<void(ServiceWorkerRegistrationStatus status,
+ typedef std::vector<ServiceWorkerDatabase::ResourceRecord> ResourceList;
+ typedef base::Callback<void(ServiceWorkerStatusCode status)> StatusCallback;
+ typedef base::Callback<void(ServiceWorkerStatusCode status,
const scoped_refptr<ServiceWorkerRegistration>&
- registration)> RegistrationCallback;
+ registration)> FindRegistrationCallback;
+ typedef base::Callback<
+ void(const std::vector<ServiceWorkerRegistrationInfo>& registrations)>
+ GetAllRegistrationInfosCallback;
typedef base::Callback<
- void(ServiceWorkerRegistrationStatus status)> UnregistrationCallback;
+ void(ServiceWorkerStatusCode status, int result)>
+ CompareCallback;
- // `found` is only valid if status == REGISTRATION_OK.
- typedef base::Callback<void(bool found,
- ServiceWorkerRegistrationStatus status,
- const scoped_refptr<ServiceWorkerRegistration>&
- registration)> FindRegistrationCallback;
+ ServiceWorkerStorage(const base::FilePath& path,
+ base::WeakPtr<ServiceWorkerContextCore> context,
+ base::SequencedTaskRunner* database_task_runner,
+ base::MessageLoopProxy* disk_cache_thread,
+ quota::QuotaManagerProxy* quota_manager_proxy);
+ ~ServiceWorkerStorage();
+ // Finds registration for |document_url| or |pattern| or |registration_id|.
+ // The Find methods will find stored and initially installing registrations.
+ // Returns SERVICE_WORKER_OK with non-null registration if registration
+ // is found, or returns SERVICE_WORKER_ERROR_NOT_FOUND if no matching
+ // registration is found. The FindRegistrationForPattern method is
+ // guaranteed to return asynchronously. However, the methods to find
+ // for |document_url| or |registration_id| may complete immediately
+ // (the callback may be called prior to the method returning) or
+ // asynchronously.
void FindRegistrationForDocument(const GURL& document_url,
const FindRegistrationCallback& callback);
- void FindRegistrationForPattern(const GURL& pattern,
+ void FindRegistrationForPattern(const GURL& scope,
const FindRegistrationCallback& callback);
+ void FindRegistrationForId(int64 registration_id,
+ const GURL& origin,
+ const FindRegistrationCallback& callback);
+
+ // Returns info about all stored and initially installing registrations.
+ void GetAllRegistrations(const GetAllRegistrationInfosCallback& callback);
+
+ // Commits |registration| with the installed but not activated |version|
+ // to storage, overwritting any pre-existing registration data for the scope.
+ // A pre-existing version's script resources will remain available until
+ // either a browser restart or DeleteVersionResources is called.
+ void StoreRegistration(
+ ServiceWorkerRegistration* registration,
+ ServiceWorkerVersion* version,
+ const StatusCallback& callback);
+
+ // Updates the state of the registration's stored version to active.
+ void UpdateToActiveState(
+ ServiceWorkerRegistration* registration,
+ const StatusCallback& callback);
+
+ // Deletes the registration data for |registration_id|, the
+ // script resources for the registration's stored version
+ // will remain available until either a browser restart or
+ // DeleteVersionResources is called.
+ void DeleteRegistration(int64 registration_id,
+ const GURL& origin,
+ const StatusCallback& callback);
+
+ scoped_ptr<ServiceWorkerResponseReader> CreateResponseReader(
+ int64 response_id);
+ scoped_ptr<ServiceWorkerResponseWriter> CreateResponseWriter(
+ int64 response_id);
+
+ // Adds |id| to the set of resources ids that are in the disk
+ // cache but not yet stored with a registration.
+ void StoreUncommittedReponseId(int64 id);
+
+ // Removes |id| from uncommitted list, adds it to the
+ // purgeable list and purges it.
+ void DoomUncommittedResponse(int64 id);
- void Register(const GURL& pattern,
- const GURL& script_url,
- const RegistrationCallback& callback);
+ // Returns new IDs which are guaranteed to be unique in the storage.
+ int64 NewRegistrationId();
+ int64 NewVersionId();
+ int64 NewResourceId();
- void Unregister(const GURL& pattern, const UnregistrationCallback& callback);
+ // Intended for use only by ServiceWorkerRegisterJob.
+ void NotifyInstallingRegistration(
+ ServiceWorkerRegistration* registration);
+ void NotifyDoneInstallingRegistration(
+ ServiceWorkerRegistration* registration,
+ ServiceWorkerVersion* version,
+ ServiceWorkerStatusCode status);
private:
- friend class ServiceWorkerRegisterJob;
- FRIEND_TEST_ALL_PREFIXES(ServiceWorkerStorageTest, PatternMatches);
-
- typedef std::map<GURL, scoped_refptr<ServiceWorkerRegistration> >
- PatternToRegistrationMap;
- typedef ScopedVector<ServiceWorkerRegisterJob> RegistrationJobList;
-
- // TODO(alecflett): These are temporary internal methods providing
- // synchronous in-memory registration. Eventually these will be
- // replaced by asynchronous methods that persist registration to disk.
- scoped_refptr<ServiceWorkerRegistration> RegisterInternal(
- const GURL& pattern,
- const GURL& script_url);
- void UnregisterInternal(const GURL& pattern);
- static bool PatternMatches(const GURL& pattern, const GURL& script_url);
-
- // Jobs are removed whenever they are finished or canceled.
- void EraseJob(ServiceWorkerRegisterJob* job);
-
- // Called at ServiceWorkerRegisterJob completion.
- void RegisterComplete(const RegistrationCallback& callback,
- ServiceWorkerRegisterJob* job,
- ServiceWorkerRegistrationStatus status,
- ServiceWorkerRegistration* registration);
-
- // Called at ServiceWorkerRegisterJob completion.
- void UnregisterComplete(const UnregistrationCallback& callback,
- ServiceWorkerRegisterJob* job,
- ServiceWorkerRegistrationStatus status,
- ServiceWorkerRegistration* registration);
-
- // This is the in-memory registration. Eventually the registration will be
- // persisted to disk.
- // A list of currently running jobs. This is a temporary structure until we
- // start managing overlapping registrations explicitly.
- RegistrationJobList registration_jobs_;
-
- // in-memory map, to eventually be replaced with persistence
- PatternToRegistrationMap registration_by_pattern_;
- scoped_refptr<quota::QuotaManagerProxy> quota_manager_proxy_;
+ friend class ServiceWorkerStorageTest;
+ FRIEND_TEST_ALL_PREFIXES(ServiceWorkerStorageTest,
+ ResourceIdsAreStoredAndPurged);
+
+ struct InitialData {
+ int64 next_registration_id;
+ int64 next_version_id;
+ int64 next_resource_id;
+ std::set<GURL> origins;
+
+ InitialData();
+ ~InitialData();
+ };
+
+ typedef std::vector<ServiceWorkerDatabase::RegistrationData> RegistrationList;
+ typedef std::map<int64, scoped_refptr<ServiceWorkerRegistration> >
+ RegistrationRefsById;
+ typedef base::Callback<void(
+ InitialData* data,
+ ServiceWorkerDatabase::Status status)> InitializeCallback;
+ typedef base::Callback<void(
+ const GURL& origin,
+ const std::vector<int64>& newly_purgeable_resources,
+ ServiceWorkerDatabase::Status status)> WriteRegistrationCallback;
+ typedef base::Callback<void(
+ bool origin_is_deletable,
+ const std::vector<int64>& newly_purgeable_resources,
+ ServiceWorkerDatabase::Status status)> DeleteRegistrationCallback;
+ typedef base::Callback<void(
+ const ServiceWorkerDatabase::RegistrationData& data,
+ const ResourceList& resources,
+ ServiceWorkerDatabase::Status status)> FindInDBCallback;
+
+ base::FilePath GetDatabasePath();
+ base::FilePath GetDiskCachePath();
+
+ bool LazyInitialize(
+ const base::Closure& callback);
+ void DidReadInitialData(
+ InitialData* data,
+ ServiceWorkerDatabase::Status status);
+ void DidFindRegistrationForDocument(
+ const GURL& document_url,
+ const FindRegistrationCallback& callback,
+ const ServiceWorkerDatabase::RegistrationData& data,
+ const ResourceList& resources,
+ ServiceWorkerDatabase::Status status);
+ void DidFindRegistrationForPattern(
+ const GURL& scope,
+ const FindRegistrationCallback& callback,
+ const ServiceWorkerDatabase::RegistrationData& data,
+ const ResourceList& resources,
+ ServiceWorkerDatabase::Status status);
+ void DidFindRegistrationForId(
+ const FindRegistrationCallback& callback,
+ const ServiceWorkerDatabase::RegistrationData& data,
+ const ResourceList& resources,
+ ServiceWorkerDatabase::Status status);
+ void DidGetAllRegistrations(
+ const GetAllRegistrationInfosCallback& callback,
+ RegistrationList* registrations,
+ ServiceWorkerDatabase::Status status);
+ void DidStoreRegistration(
+ const StatusCallback& callback,
+ const GURL& origin,
+ const std::vector<int64>& newly_purgeable_resources,
+ ServiceWorkerDatabase::Status status);
+ void DidUpdateToActiveState(
+ const StatusCallback& callback,
+ ServiceWorkerDatabase::Status status);
+ void DidDeleteRegistration(
+ const GURL& origin,
+ const StatusCallback& callback,
+ bool origin_is_deletable,
+ const std::vector<int64>& newly_purgeable_resources,
+ ServiceWorkerDatabase::Status status);
+
+ scoped_refptr<ServiceWorkerRegistration> GetOrCreateRegistration(
+ const ServiceWorkerDatabase::RegistrationData& data,
+ const ResourceList& resources);
+ ServiceWorkerRegistration* FindInstallingRegistrationForDocument(
+ const GURL& document_url);
+ ServiceWorkerRegistration* FindInstallingRegistrationForPattern(
+ const GURL& scope);
+ ServiceWorkerRegistration* FindInstallingRegistrationForId(
+ int64 registration_id);
+
+ // Lazy disk_cache getter.
+ ServiceWorkerDiskCache* disk_cache();
+ void OnDiskCacheInitialized(int rv);
+
+ void StartPurgingResources(const std::vector<int64>& ids);
+ void StartPurgingResources(const ResourceList& resources);
+ void ContinuePurgingResources();
+ void PurgeResource(int64 id);
+ void OnResourcePurged(int64 id, int rv);
+
+ // Static cross-thread helpers.
+ static void ReadInitialDataFromDB(
+ ServiceWorkerDatabase* database,
+ scoped_refptr<base::SequencedTaskRunner> original_task_runner,
+ const InitializeCallback& callback);
+ static void DeleteRegistrationFromDB(
+ ServiceWorkerDatabase* database,
+ scoped_refptr<base::SequencedTaskRunner> original_task_runner,
+ int64 registration_id,
+ const GURL& origin,
+ const DeleteRegistrationCallback& callback);
+ static void WriteRegistrationInDB(
+ ServiceWorkerDatabase* database,
+ scoped_refptr<base::SequencedTaskRunner> original_task_runner,
+ const ServiceWorkerDatabase::RegistrationData& registration,
+ const ResourceList& resources,
+ const WriteRegistrationCallback& callback);
+ static void FindForDocumentInDB(
+ ServiceWorkerDatabase* database,
+ scoped_refptr<base::SequencedTaskRunner> original_task_runner,
+ const GURL& document_url,
+ const FindInDBCallback& callback);
+ static void FindForPatternInDB(
+ ServiceWorkerDatabase* database,
+ scoped_refptr<base::SequencedTaskRunner> original_task_runner,
+ const GURL& scope,
+ const FindInDBCallback& callback);
+ static void FindForIdInDB(
+ ServiceWorkerDatabase* database,
+ scoped_refptr<base::SequencedTaskRunner> original_task_runner,
+ int64 registration_id,
+ const GURL& origin,
+ const FindInDBCallback& callback);
+
+ // For finding registrations being installed.
+ RegistrationRefsById installing_registrations_;
+
+ // Origins having registations.
+ std::set<GURL> registered_origins_;
+
+ // Pending database tasks waiting for initialization.
+ std::vector<base::Closure> pending_tasks_;
+
+ int64 next_registration_id_;
+ int64 next_version_id_;
+ int64 next_resource_id_;
+
+ enum State {
+ UNINITIALIZED,
+ INITIALIZING,
+ INITIALIZED,
+ DISABLED,
+ };
+ State state_;
+
base::FilePath path_;
+ base::WeakPtr<ServiceWorkerContextCore> context_;
+
+ // Only accessed on |database_task_runner_|.
+ scoped_ptr<ServiceWorkerDatabase> database_;
+
+ scoped_refptr<base::SequencedTaskRunner> database_task_runner_;
+ scoped_refptr<base::MessageLoopProxy> disk_cache_thread_;
+ scoped_refptr<quota::QuotaManagerProxy> quota_manager_proxy_;
+ scoped_ptr<ServiceWorkerDiskCache> disk_cache_;
+ std::deque<int64> purgeable_reource_ids_;
+ bool is_purge_pending_;
+
base::WeakPtrFactory<ServiceWorkerStorage> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(ServiceWorkerStorage);
diff --git a/chromium/content/browser/service_worker/service_worker_storage_unittest.cc b/chromium/content/browser/service_worker/service_worker_storage_unittest.cc
index bd6d629eb80..417a0b7f237 100644
--- a/chromium/content/browser/service_worker/service_worker_storage_unittest.cc
+++ b/chromium/content/browser/service_worker/service_worker_storage_unittest.cc
@@ -2,383 +2,638 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "content/browser/service_worker/service_worker_storage.h"
+#include <string>
-#include "base/files/scoped_temp_dir.h"
#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "content/browser/browser_thread_impl.h"
+#include "content/browser/service_worker/service_worker_context_core.h"
+#include "content/browser/service_worker/service_worker_disk_cache.h"
#include "content/browser/service_worker/service_worker_registration.h"
+#include "content/browser/service_worker/service_worker_storage.h"
+#include "content/browser/service_worker/service_worker_version.h"
+#include "content/common/service_worker/service_worker_status_code.h"
#include "content/public/test/test_browser_thread_bundle.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_response_headers.h"
#include "testing/gtest/include/gtest/gtest.h"
+using net::IOBuffer;
+using net::WrappedIOBuffer;
+
namespace content {
namespace {
-void SaveRegistrationCallback(
- ServiceWorkerRegistrationStatus expected_status,
- bool* called,
- scoped_refptr<ServiceWorkerRegistration>* registration,
- ServiceWorkerRegistrationStatus status,
- const scoped_refptr<ServiceWorkerRegistration>& result) {
- EXPECT_EQ(expected_status, status);
- *called = true;
- *registration = result;
-}
+typedef ServiceWorkerDatabase::RegistrationData RegistrationData;
+typedef ServiceWorkerDatabase::ResourceRecord ResourceRecord;
-void SaveFoundRegistrationCallback(
- bool expected_found,
- ServiceWorkerRegistrationStatus expected_status,
- bool* called,
- scoped_refptr<ServiceWorkerRegistration>* registration,
- bool found,
- ServiceWorkerRegistrationStatus status,
- const scoped_refptr<ServiceWorkerRegistration>& result) {
- EXPECT_EQ(expected_found, found);
- EXPECT_EQ(expected_status, status);
- *called = true;
- *registration = result;
+void StatusCallback(bool* was_called,
+ ServiceWorkerStatusCode* result,
+ ServiceWorkerStatusCode status) {
+ *was_called = true;
+ *result = status;
}
-// Creates a callback which both keeps track of if it's been called,
-// as well as the resulting registration. Whent the callback is fired,
-// it ensures that the resulting status matches the expectation.
-// 'called' is useful for making sure a sychronous callback is or
-// isn't called.
-ServiceWorkerStorage::RegistrationCallback SaveRegistration(
- ServiceWorkerRegistrationStatus expected_status,
- bool* called,
- scoped_refptr<ServiceWorkerRegistration>* registration) {
- *called = false;
- return base::Bind(
- &SaveRegistrationCallback, expected_status, called, registration);
+ServiceWorkerStorage::StatusCallback MakeStatusCallback(
+ bool* was_called,
+ ServiceWorkerStatusCode* result) {
+ return base::Bind(&StatusCallback, was_called, result);
}
-ServiceWorkerStorage::FindRegistrationCallback SaveFoundRegistration(
- bool expected_found,
- ServiceWorkerRegistrationStatus expected_status,
- bool* called,
- scoped_refptr<ServiceWorkerRegistration>* registration) {
- *called = false;
- return base::Bind(&SaveFoundRegistrationCallback,
- expected_found,
- expected_status,
- called,
- registration);
+void FindCallback(
+ bool* was_called,
+ ServiceWorkerStatusCode* result,
+ scoped_refptr<ServiceWorkerRegistration>* found,
+ ServiceWorkerStatusCode status,
+ const scoped_refptr<ServiceWorkerRegistration>& registration) {
+ *was_called = true;
+ *result = status;
+ *found = registration;
}
-void SaveUnregistrationCallback(ServiceWorkerRegistrationStatus expected_status,
- bool* called,
- ServiceWorkerRegistrationStatus status) {
- EXPECT_EQ(expected_status, status);
- *called = true;
+ServiceWorkerStorage::FindRegistrationCallback MakeFindCallback(
+ bool* was_called,
+ ServiceWorkerStatusCode* result,
+ scoped_refptr<ServiceWorkerRegistration>* found) {
+ return base::Bind(&FindCallback, was_called, result, found);
}
-ServiceWorkerStorage::UnregistrationCallback SaveUnregistration(
- ServiceWorkerRegistrationStatus expected_status,
- bool* called) {
- *called = false;
- return base::Bind(&SaveUnregistrationCallback, expected_status, called);
+void GetAllCallback(
+ bool* was_called,
+ std::vector<ServiceWorkerRegistrationInfo>* all_out,
+ const std::vector<ServiceWorkerRegistrationInfo>& all) {
+ *was_called = true;
+ *all_out = all;
}
-} // namespace
-
-class ServiceWorkerStorageTest : public testing::Test {
- public:
- ServiceWorkerStorageTest()
- : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {}
-
- virtual void SetUp() OVERRIDE {
- storage_.reset(new ServiceWorkerStorage(base::FilePath(), NULL));
- }
-
- virtual void TearDown() OVERRIDE { storage_.reset(); }
-
- protected:
- TestBrowserThreadBundle browser_thread_bundle_;
- scoped_ptr<ServiceWorkerStorage> storage_;
-};
-
-TEST_F(ServiceWorkerStorageTest, PatternMatches) {
- ASSERT_TRUE(ServiceWorkerStorage::PatternMatches(
- GURL("http://www.example.com/*"), GURL("http://www.example.com/")));
- ASSERT_TRUE(ServiceWorkerStorage::PatternMatches(
- GURL("http://www.example.com/*"),
- GURL("http://www.example.com/page.html")));
-
- ASSERT_FALSE(ServiceWorkerStorage::PatternMatches(
- GURL("http://www.example.com/*"), GURL("https://www.example.com/")));
- ASSERT_FALSE(ServiceWorkerStorage::PatternMatches(
- GURL("http://www.example.com/*"),
- GURL("https://www.example.com/page.html")));
-
- ASSERT_FALSE(ServiceWorkerStorage::PatternMatches(
- GURL("http://www.example.com/*"), GURL("http://www.foo.com/")));
- ASSERT_FALSE(ServiceWorkerStorage::PatternMatches(
- GURL("http://www.example.com/*"), GURL("https://www.foo.com/page.html")));
+ServiceWorkerStorage::GetAllRegistrationInfosCallback MakeGetAllCallback(
+ bool* was_called,
+ std::vector<ServiceWorkerRegistrationInfo>* all) {
+ return base::Bind(&GetAllCallback, was_called, all);
}
-TEST_F(ServiceWorkerStorageTest, SameDocumentSameRegistration) {
- scoped_refptr<ServiceWorkerRegistration> original_registration;
- bool called;
- storage_->Register(
- GURL("http://www.example.com/*"),
- GURL("http://www.example.com/service_worker.js"),
- SaveRegistration(REGISTRATION_OK, &called, &original_registration));
- EXPECT_FALSE(called);
- base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(called);
-
- scoped_refptr<ServiceWorkerRegistration> registration1;
- storage_->FindRegistrationForDocument(
- GURL("http://www.example.com/"),
- SaveFoundRegistration(true, REGISTRATION_OK, &called, &registration1));
- scoped_refptr<ServiceWorkerRegistration> registration2;
- storage_->FindRegistrationForDocument(
- GURL("http://www.example.com/"),
- SaveFoundRegistration(true, REGISTRATION_OK, &called, &registration2));
-
- ServiceWorkerRegistration* null_registration(NULL);
- ASSERT_EQ(null_registration, registration1);
- ASSERT_EQ(null_registration, registration2);
- EXPECT_FALSE(called);
- base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(called);
- ASSERT_NE(null_registration, registration1);
- ASSERT_NE(null_registration, registration2);
-
- ASSERT_EQ(registration1, registration2);
+void OnIOComplete(int* rv_out, int rv) {
+ *rv_out = rv;
}
-TEST_F(ServiceWorkerStorageTest, SameMatchSameRegistration) {
- bool called;
- scoped_refptr<ServiceWorkerRegistration> original_registration;
- storage_->Register(
- GURL("http://www.example.com/*"),
- GURL("http://www.example.com/service_worker.js"),
- SaveRegistration(REGISTRATION_OK, &called, &original_registration));
- EXPECT_FALSE(called);
+void WriteBasicResponse(ServiceWorkerStorage* storage, int64 id) {
+ scoped_ptr<ServiceWorkerResponseWriter> writer =
+ storage->CreateResponseWriter(id);
+
+ const char kHttpHeaders[] =
+ "HTTP/1.0 200 HONKYDORY\0Content-Length: 6\0\0";
+ const char kHttpBody[] = "Hello\0";
+ scoped_refptr<IOBuffer> body(new WrappedIOBuffer(kHttpBody));
+ std::string raw_headers(kHttpHeaders, arraysize(kHttpHeaders));
+ scoped_ptr<net::HttpResponseInfo> info(new net::HttpResponseInfo);
+ info->request_time = base::Time::Now();
+ info->response_time = base::Time::Now();
+ info->was_cached = false;
+ info->headers = new net::HttpResponseHeaders(raw_headers);
+ scoped_refptr<HttpResponseInfoIOBuffer> info_buffer =
+ new HttpResponseInfoIOBuffer(info.release());
+
+ int rv = -1234;
+ writer->WriteInfo(info_buffer, base::Bind(&OnIOComplete, &rv));
base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(called);
- ASSERT_NE(static_cast<ServiceWorkerRegistration*>(NULL),
- original_registration.get());
-
- scoped_refptr<ServiceWorkerRegistration> registration1;
- storage_->FindRegistrationForDocument(
- GURL("http://www.example.com/one"),
- SaveFoundRegistration(true, REGISTRATION_OK, &called, &registration1));
+ EXPECT_LT(0, rv);
- EXPECT_FALSE(called);
+ rv = -1234;
+ writer->WriteData(body, arraysize(kHttpBody),
+ base::Bind(&OnIOComplete, &rv));
base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(called);
-
- scoped_refptr<ServiceWorkerRegistration> registration2;
- storage_->FindRegistrationForDocument(
- GURL("http://www.example.com/two"),
- SaveFoundRegistration(true, REGISTRATION_OK, &called, &registration2));
- EXPECT_FALSE(called);
- base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(called);
-
- ASSERT_EQ(registration1, registration2);
+ EXPECT_EQ(static_cast<int>(arraysize(kHttpBody)), rv);
}
-TEST_F(ServiceWorkerStorageTest, DifferentMatchDifferentRegistration) {
- bool called1;
- scoped_refptr<ServiceWorkerRegistration> original_registration1;
- storage_->Register(
- GURL("http://www.example.com/one/*"),
- GURL("http://www.example.com/service_worker.js"),
- SaveRegistration(REGISTRATION_OK, &called1, &original_registration1));
-
- bool called2;
- scoped_refptr<ServiceWorkerRegistration> original_registration2;
- storage_->Register(
- GURL("http://www.example.com/two/*"),
- GURL("http://www.example.com/service_worker.js"),
- SaveRegistration(REGISTRATION_OK, &called2, &original_registration2));
-
- EXPECT_FALSE(called1);
- EXPECT_FALSE(called2);
+bool VerifyBasicResponse(ServiceWorkerStorage* storage, int64 id,
+ bool expected_positive_result) {
+ const char kExpectedHttpBody[] = "Hello\0";
+ scoped_ptr<ServiceWorkerResponseReader> reader =
+ storage->CreateResponseReader(id);
+ scoped_refptr<HttpResponseInfoIOBuffer> info_buffer =
+ new HttpResponseInfoIOBuffer();
+ int rv = -1234;
+ reader->ReadInfo(info_buffer, base::Bind(&OnIOComplete, &rv));
base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(called2);
- EXPECT_TRUE(called1);
-
- scoped_refptr<ServiceWorkerRegistration> registration1;
- storage_->FindRegistrationForDocument(
- GURL("http://www.example.com/one/"),
- SaveFoundRegistration(true, REGISTRATION_OK, &called1, &registration1));
- scoped_refptr<ServiceWorkerRegistration> registration2;
- storage_->FindRegistrationForDocument(
- GURL("http://www.example.com/two/"),
- SaveFoundRegistration(true, REGISTRATION_OK, &called2, &registration2));
-
- EXPECT_FALSE(called1);
- EXPECT_FALSE(called2);
+ if (expected_positive_result)
+ EXPECT_LT(0, rv);
+ if (rv <= 0)
+ return false;
+
+ const int kBigEnough = 512;
+ scoped_refptr<net::IOBuffer> buffer = new IOBuffer(kBigEnough);
+ rv = -1234;
+ reader->ReadData(buffer, kBigEnough, base::Bind(&OnIOComplete, &rv));
base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(called2);
- EXPECT_TRUE(called1);
-
- ASSERT_NE(registration1, registration2);
+ EXPECT_EQ(static_cast<int>(arraysize(kExpectedHttpBody)), rv);
+ if (rv <= 0)
+ return false;
+
+ bool status_match =
+ std::string("HONKYDORY") ==
+ info_buffer->http_info->headers->GetStatusText();
+ bool data_match =
+ std::string(kExpectedHttpBody) == std::string(buffer->data());
+
+ EXPECT_TRUE(status_match);
+ EXPECT_TRUE(data_match);
+ return status_match && data_match;
}
-// Make sure basic registration is working.
-TEST_F(ServiceWorkerStorageTest, Register) {
- bool called = false;
- scoped_refptr<ServiceWorkerRegistration> registration;
- storage_->Register(GURL("http://www.example.com/*"),
- GURL("http://www.example.com/service_worker.js"),
- SaveRegistration(REGISTRATION_OK, &called, &registration));
-
- ASSERT_FALSE(called);
- base::RunLoop().RunUntilIdle();
- ASSERT_TRUE(called);
-
- ASSERT_NE(scoped_refptr<ServiceWorkerRegistration>(NULL), registration);
-}
-
-// Make sure registrations are cleaned up when they are unregistered.
-TEST_F(ServiceWorkerStorageTest, Unregister) {
- GURL pattern("http://www.example.com/*");
-
- bool called;
- scoped_refptr<ServiceWorkerRegistration> registration;
- storage_->Register(pattern,
- GURL("http://www.example.com/service_worker.js"),
- SaveRegistration(REGISTRATION_OK, &called, &registration));
-
- ASSERT_FALSE(called);
- base::RunLoop().RunUntilIdle();
- ASSERT_TRUE(called);
-
- storage_->Unregister(pattern, SaveUnregistration(REGISTRATION_OK, &called));
-
- ASSERT_FALSE(called);
- base::RunLoop().RunUntilIdle();
- ASSERT_TRUE(called);
-
- ASSERT_TRUE(registration->HasOneRef());
-
- storage_->FindRegistrationForPattern(
- pattern,
- SaveFoundRegistration(false, REGISTRATION_OK, &called, &registration));
-
- ASSERT_FALSE(called);
- base::RunLoop().RunUntilIdle();
- ASSERT_TRUE(called);
-
- ASSERT_EQ(scoped_refptr<ServiceWorkerRegistration>(NULL), registration);
-}
+} // namespace
-// Make sure that when a new registration replaces an existing
-// registration, that the old one is cleaned up.
-TEST_F(ServiceWorkerStorageTest, RegisterNewScript) {
- GURL pattern("http://www.example.com/*");
+class ServiceWorkerStorageTest : public testing::Test {
+ public:
+ ServiceWorkerStorageTest()
+ : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {
+ }
- bool called;
- scoped_refptr<ServiceWorkerRegistration> old_registration;
- storage_->Register(
- pattern,
- GURL("http://www.example.com/service_worker.js"),
- SaveRegistration(REGISTRATION_OK, &called, &old_registration));
+ virtual void SetUp() OVERRIDE {
+ context_.reset(
+ new ServiceWorkerContextCore(base::FilePath(),
+ base::MessageLoopProxy::current(),
+ base::MessageLoopProxy::current(),
+ NULL,
+ NULL,
+ NULL));
+ context_ptr_ = context_->AsWeakPtr();
+ }
- ASSERT_FALSE(called);
- base::RunLoop().RunUntilIdle();
- ASSERT_TRUE(called);
+ virtual void TearDown() OVERRIDE {
+ context_.reset();
+ }
- scoped_refptr<ServiceWorkerRegistration> old_registration_by_pattern;
- storage_->FindRegistrationForPattern(
- pattern,
- SaveFoundRegistration(
- true, REGISTRATION_OK, &called, &old_registration_by_pattern));
+ ServiceWorkerStorage* storage() { return context_->storage(); }
+
+ // A static class method for friendliness.
+ static void VerifyPurgeableListStatusCallback(
+ ServiceWorkerDatabase* database,
+ std::set<int64> *purgeable_ids,
+ bool* was_called,
+ ServiceWorkerStatusCode* result,
+ ServiceWorkerStatusCode status) {
+ *was_called = true;
+ *result = status;
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ database->GetPurgeableResourceIds(purgeable_ids));
+ }
- ASSERT_FALSE(called);
- base::RunLoop().RunUntilIdle();
- ASSERT_TRUE(called);
+ protected:
+ ServiceWorkerStatusCode StoreRegistration(
+ scoped_refptr<ServiceWorkerRegistration> registration,
+ scoped_refptr<ServiceWorkerVersion> version) {
+ bool was_called = false;
+ ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_FAILED;
+ storage()->StoreRegistration(
+ registration, version, MakeStatusCallback(&was_called, &result));
+ EXPECT_FALSE(was_called); // always async
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(was_called);
+ return result;
+ }
- ASSERT_EQ(old_registration, old_registration_by_pattern);
- old_registration_by_pattern = NULL;
+ ServiceWorkerStatusCode DeleteRegistration(
+ int64 registration_id,
+ const GURL& origin) {
+ bool was_called = false;
+ ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_FAILED;
+ storage()->DeleteRegistration(
+ registration_id, origin, MakeStatusCallback(&was_called, &result));
+ EXPECT_FALSE(was_called); // always async
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(was_called);
+ return result;
+ }
- scoped_refptr<ServiceWorkerRegistration> new_registration;
- storage_->Register(
- pattern,
- GURL("http://www.example.com/service_worker_new.js"),
- SaveRegistration(REGISTRATION_OK, &called, &new_registration));
+ void GetAllRegistrations(
+ std::vector<ServiceWorkerRegistrationInfo>* registrations) {
+ bool was_called = false;
+ storage()->GetAllRegistrations(
+ MakeGetAllCallback(&was_called, registrations));
+ EXPECT_FALSE(was_called); // always async
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(was_called);
+ }
- ASSERT_FALSE(called);
- base::RunLoop().RunUntilIdle();
- ASSERT_TRUE(called);
+ ServiceWorkerStatusCode UpdateToActiveState(
+ scoped_refptr<ServiceWorkerRegistration> registration) {
+ bool was_called = false;
+ ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_FAILED;
+ storage()->UpdateToActiveState(
+ registration, MakeStatusCallback(&was_called, &result));
+ EXPECT_FALSE(was_called); // always async
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(was_called);
+ return result;
+ }
- ASSERT_TRUE(old_registration->HasOneRef());
+ ServiceWorkerStatusCode FindRegistrationForDocument(
+ const GURL& document_url,
+ scoped_refptr<ServiceWorkerRegistration>* registration) {
+ bool was_called = false;
+ ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_FAILED;
+ storage()->FindRegistrationForDocument(
+ document_url, MakeFindCallback(&was_called, &result, registration));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(was_called);
+ return result;
+ }
- ASSERT_NE(old_registration, new_registration);
+ ServiceWorkerStatusCode FindRegistrationForPattern(
+ const GURL& scope,
+ scoped_refptr<ServiceWorkerRegistration>* registration) {
+ bool was_called = false;
+ ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_FAILED;
+ storage()->FindRegistrationForPattern(
+ scope, MakeFindCallback(&was_called, &result, registration));
+ EXPECT_FALSE(was_called); // always async
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(was_called);
+ return result;
+ }
- scoped_refptr<ServiceWorkerRegistration> new_registration_by_pattern;
- storage_->FindRegistrationForPattern(
- pattern,
- SaveFoundRegistration(true, REGISTRATION_OK, &called, &new_registration));
+ ServiceWorkerStatusCode FindRegistrationForId(
+ int64 registration_id,
+ const GURL& origin,
+ scoped_refptr<ServiceWorkerRegistration>* registration) {
+ bool was_called = false;
+ ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_FAILED;
+ storage()->FindRegistrationForId(
+ registration_id, origin,
+ MakeFindCallback(&was_called, &result, registration));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(was_called);
+ return result;
+ }
- ASSERT_FALSE(called);
- base::RunLoop().RunUntilIdle();
- ASSERT_TRUE(called);
+ scoped_ptr<ServiceWorkerContextCore> context_;
+ base::WeakPtr<ServiceWorkerContextCore> context_ptr_;
+ TestBrowserThreadBundle browser_thread_bundle_;
+};
- ASSERT_NE(new_registration_by_pattern, old_registration);
+TEST_F(ServiceWorkerStorageTest, StoreFindUpdateDeleteRegistration) {
+ const GURL kScope("http://www.test.not/scope/*");
+ const GURL kScript("http://www.test.not/script.js");
+ const GURL kDocumentUrl("http://www.test.not/scope/document.html");
+ const int64 kRegistrationId = 0;
+ const int64 kVersionId = 0;
+
+ scoped_refptr<ServiceWorkerRegistration> found_registration;
+
+ // We shouldn't find anything without having stored anything.
+ EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
+ FindRegistrationForDocument(kDocumentUrl, &found_registration));
+ EXPECT_FALSE(found_registration);
+
+ EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
+ FindRegistrationForPattern(kScope, &found_registration));
+ EXPECT_FALSE(found_registration);
+
+ EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
+ FindRegistrationForId(
+ kRegistrationId, kScope.GetOrigin(), &found_registration));
+ EXPECT_FALSE(found_registration);
+
+ // Store something.
+ scoped_refptr<ServiceWorkerRegistration> live_registration =
+ new ServiceWorkerRegistration(
+ kScope, kScript, kRegistrationId, context_ptr_);
+ scoped_refptr<ServiceWorkerVersion> live_version =
+ new ServiceWorkerVersion(
+ live_registration, kVersionId, context_ptr_);
+ live_version->SetStatus(ServiceWorkerVersion::INSTALLED);
+ live_registration->set_waiting_version(live_version);
+ EXPECT_EQ(SERVICE_WORKER_OK,
+ StoreRegistration(live_registration, live_version));
+
+ // Now we should find it and get the live ptr back immediately.
+ EXPECT_EQ(SERVICE_WORKER_OK,
+ FindRegistrationForDocument(kDocumentUrl, &found_registration));
+ EXPECT_EQ(live_registration, found_registration);
+ found_registration = NULL;
+
+ // But FindRegistrationForPattern is always async.
+ EXPECT_EQ(SERVICE_WORKER_OK,
+ FindRegistrationForPattern(kScope, &found_registration));
+ EXPECT_EQ(live_registration, found_registration);
+ found_registration = NULL;
+
+ // Can be found by id too.
+ EXPECT_EQ(SERVICE_WORKER_OK,
+ FindRegistrationForId(
+ kRegistrationId, kScope.GetOrigin(), &found_registration));
+ ASSERT_TRUE(found_registration);
+ EXPECT_EQ(kRegistrationId, found_registration->id());
+ EXPECT_EQ(live_registration, found_registration);
+ found_registration = NULL;
+
+ // Drop the live registration, but keep the version live.
+ live_registration = NULL;
+
+ // Now FindRegistrationForDocument should be async.
+ EXPECT_EQ(SERVICE_WORKER_OK,
+ FindRegistrationForDocument(kDocumentUrl, &found_registration));
+ ASSERT_TRUE(found_registration);
+ EXPECT_EQ(kRegistrationId, found_registration->id());
+ EXPECT_TRUE(found_registration->HasOneRef());
+ EXPECT_EQ(live_version, found_registration->waiting_version());
+ found_registration = NULL;
+
+ // Drop the live version too.
+ live_version = NULL;
+
+ // And FindRegistrationForPattern is always async.
+ EXPECT_EQ(SERVICE_WORKER_OK,
+ FindRegistrationForPattern(kScope, &found_registration));
+ ASSERT_TRUE(found_registration);
+ EXPECT_EQ(kRegistrationId, found_registration->id());
+ EXPECT_TRUE(found_registration->HasOneRef());
+ EXPECT_FALSE(found_registration->active_version());
+ ASSERT_TRUE(found_registration->waiting_version());
+ EXPECT_EQ(ServiceWorkerVersion::INSTALLED,
+ found_registration->waiting_version()->status());
+
+ // Update to active.
+ scoped_refptr<ServiceWorkerVersion> temp_version =
+ found_registration->waiting_version();
+ found_registration->set_waiting_version(NULL);
+ temp_version->SetStatus(ServiceWorkerVersion::ACTIVE);
+ found_registration->set_active_version(temp_version);
+ temp_version = NULL;
+ EXPECT_EQ(SERVICE_WORKER_OK, UpdateToActiveState(found_registration));
+ found_registration = NULL;
+
+ // Trying to update a unstored registration to active should fail.
+ scoped_refptr<ServiceWorkerRegistration> unstored_registration =
+ new ServiceWorkerRegistration(
+ kScope, kScript, kRegistrationId + 1, context_ptr_);
+ EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
+ UpdateToActiveState(unstored_registration));
+ unstored_registration = NULL;
+
+ // The Find methods should return a registration with an active version.
+ EXPECT_EQ(SERVICE_WORKER_OK,
+ FindRegistrationForDocument(kDocumentUrl, &found_registration));
+ ASSERT_TRUE(found_registration);
+ EXPECT_EQ(kRegistrationId, found_registration->id());
+ EXPECT_TRUE(found_registration->HasOneRef());
+ EXPECT_FALSE(found_registration->waiting_version());
+ ASSERT_TRUE(found_registration->active_version());
+ EXPECT_EQ(ServiceWorkerVersion::ACTIVE,
+ found_registration->active_version()->status());
+
+ // Delete from storage but with a instance still live.
+ EXPECT_TRUE(context_->GetLiveVersion(kRegistrationId));
+ EXPECT_EQ(SERVICE_WORKER_OK,
+ DeleteRegistration(kRegistrationId, kScope.GetOrigin()));
+ EXPECT_TRUE(context_->GetLiveVersion(kRegistrationId));
+
+ // Should no longer be found.
+ EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
+ FindRegistrationForId(
+ kRegistrationId, kScope.GetOrigin(), &found_registration));
+ EXPECT_FALSE(found_registration);
+
+ // Deleting an unstored registration should succeed.
+ EXPECT_EQ(SERVICE_WORKER_OK,
+ DeleteRegistration(kRegistrationId + 1, kScope.GetOrigin()));
}
-// Make sure that when registering a duplicate pattern+script_url
-// combination, that the same registration is used.
-TEST_F(ServiceWorkerStorageTest, RegisterDuplicateScript) {
- GURL pattern("http://www.example.com/*");
- GURL script_url("http://www.example.com/service_worker.js");
-
- bool called;
- scoped_refptr<ServiceWorkerRegistration> old_registration;
- storage_->Register(
- pattern,
- script_url,
- SaveRegistration(REGISTRATION_OK, &called, &old_registration));
-
- ASSERT_FALSE(called);
- base::RunLoop().RunUntilIdle();
- ASSERT_TRUE(called);
-
- scoped_refptr<ServiceWorkerRegistration> old_registration_by_pattern;
- storage_->FindRegistrationForPattern(
- pattern,
- SaveFoundRegistration(
- true, REGISTRATION_OK, &called, &old_registration_by_pattern));
- ASSERT_FALSE(called);
- base::RunLoop().RunUntilIdle();
- ASSERT_TRUE(called);
-
- ASSERT_TRUE(old_registration_by_pattern);
-
- scoped_refptr<ServiceWorkerRegistration> new_registration;
- storage_->Register(
- pattern,
- script_url,
- SaveRegistration(REGISTRATION_OK, &called, &new_registration));
+TEST_F(ServiceWorkerStorageTest, InstallingRegistrationsAreFindable) {
+ const GURL kScope("http://www.test.not/scope/*");
+ const GURL kScript("http://www.test.not/script.js");
+ const GURL kDocumentUrl("http://www.test.not/scope/document.html");
+ const int64 kRegistrationId = 0;
+ const int64 kVersionId = 0;
+
+ scoped_refptr<ServiceWorkerRegistration> found_registration;
+
+ // Create an unstored registration.
+ scoped_refptr<ServiceWorkerRegistration> live_registration =
+ new ServiceWorkerRegistration(
+ kScope, kScript, kRegistrationId, context_ptr_);
+ scoped_refptr<ServiceWorkerVersion> live_version =
+ new ServiceWorkerVersion(
+ live_registration, kVersionId, context_ptr_);
+ live_version->SetStatus(ServiceWorkerVersion::INSTALLING);
+ live_registration->set_waiting_version(live_version);
+
+ // Should not be findable, including by GetAllRegistrations.
+ EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
+ FindRegistrationForId(
+ kRegistrationId, kScope.GetOrigin(), &found_registration));
+ EXPECT_FALSE(found_registration);
+
+ EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
+ FindRegistrationForDocument(kDocumentUrl, &found_registration));
+ EXPECT_FALSE(found_registration);
+
+ EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
+ FindRegistrationForPattern(kScope, &found_registration));
+ EXPECT_FALSE(found_registration);
+
+ std::vector<ServiceWorkerRegistrationInfo> all_registrations;
+ GetAllRegistrations(&all_registrations);
+ EXPECT_TRUE(all_registrations.empty());
+
+ // Notify storage of it being installed.
+ storage()->NotifyInstallingRegistration(live_registration);
+
+ // Now should be findable.
+ EXPECT_EQ(SERVICE_WORKER_OK,
+ FindRegistrationForId(
+ kRegistrationId, kScope.GetOrigin(), &found_registration));
+ EXPECT_EQ(live_registration, found_registration);
+ found_registration = NULL;
+
+ EXPECT_EQ(SERVICE_WORKER_OK,
+ FindRegistrationForDocument(kDocumentUrl, &found_registration));
+ EXPECT_EQ(live_registration, found_registration);
+ found_registration = NULL;
+
+ EXPECT_EQ(SERVICE_WORKER_OK,
+ FindRegistrationForPattern(kScope, &found_registration));
+ EXPECT_EQ(live_registration, found_registration);
+ found_registration = NULL;
+
+ GetAllRegistrations(&all_registrations);
+ EXPECT_EQ(1u, all_registrations.size());
+ all_registrations.clear();
+
+ // Notify storage of installation no longer happening.
+ storage()->NotifyDoneInstallingRegistration(
+ live_registration, NULL, SERVICE_WORKER_OK);
+
+ // Once again, should not be findable.
+ EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
+ FindRegistrationForId(
+ kRegistrationId, kScope.GetOrigin(), &found_registration));
+ EXPECT_FALSE(found_registration);
+
+ EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
+ FindRegistrationForDocument(kDocumentUrl, &found_registration));
+ EXPECT_FALSE(found_registration);
+
+ EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
+ FindRegistrationForPattern(kScope, &found_registration));
+ EXPECT_FALSE(found_registration);
+
+ GetAllRegistrations(&all_registrations);
+ EXPECT_TRUE(all_registrations.empty());
+}
- ASSERT_FALSE(called);
+TEST_F(ServiceWorkerStorageTest, ResourceIdsAreStoredAndPurged) {
+ storage()->LazyInitialize(base::Bind(&base::DoNothing));
base::RunLoop().RunUntilIdle();
- ASSERT_TRUE(called);
-
- ASSERT_EQ(old_registration, new_registration);
-
- ASSERT_FALSE(old_registration->HasOneRef());
-
- scoped_refptr<ServiceWorkerRegistration> new_registration_by_pattern;
- storage_->FindRegistrationForPattern(
- pattern,
- SaveFoundRegistration(
- true, REGISTRATION_OK, &called, &new_registration_by_pattern));
-
- ASSERT_FALSE(called);
+ const GURL kScope("http://www.test.not/scope/*");
+ const GURL kScript("http://www.test.not/script.js");
+ const GURL kImport("http://www.test.not/import.js");
+ const GURL kDocumentUrl("http://www.test.not/scope/document.html");
+ const int64 kRegistrationId = storage()->NewRegistrationId();
+ const int64 kVersionId = storage()->NewVersionId();
+ const int64 kResourceId1 = storage()->NewResourceId();
+ const int64 kResourceId2 = storage()->NewResourceId();
+
+ // Cons up a new registration+version with two script resources.
+ RegistrationData data;
+ data.registration_id = kRegistrationId;
+ data.scope = kScope;
+ data.script = kScript;
+ data.version_id = kVersionId;
+ data.is_active = false;
+ std::vector<ResourceRecord> resources;
+ resources.push_back(ResourceRecord(kResourceId1, kScript));
+ resources.push_back(ResourceRecord(kResourceId2, kImport));
+ scoped_refptr<ServiceWorkerRegistration> registration =
+ storage()->GetOrCreateRegistration(data, resources);
+ registration->waiting_version()->SetStatus(ServiceWorkerVersion::NEW);
+
+ // Add the resources ids to the uncommitted list.
+ std::set<int64> resource_ids;
+ resource_ids.insert(kResourceId1);
+ resource_ids.insert(kResourceId2);
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ storage()->database_->WriteUncommittedResourceIds(resource_ids));
+
+ // And dump something in the disk cache for them.
+ WriteBasicResponse(storage(), kResourceId1);
+ WriteBasicResponse(storage(), kResourceId2);
+ EXPECT_TRUE(VerifyBasicResponse(storage(), kResourceId1, true));
+ EXPECT_TRUE(VerifyBasicResponse(storage(), kResourceId2, true));
+
+ // Storing the registration/version should take the resources ids out
+ // of the uncommitted list.
+ EXPECT_EQ(SERVICE_WORKER_OK,
+ StoreRegistration(registration, registration->waiting_version()));
+ std::set<int64> verify_ids;
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ storage()->database_->GetUncommittedResourceIds(&verify_ids));
+ EXPECT_TRUE(verify_ids.empty());
+
+ // Deleting it should result in the resources being added to the
+ // purgeable list and then doomed in the disk cache and removed from
+ // that list.
+ bool was_called = false;
+ ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_FAILED;
+ verify_ids.clear();
+ storage()->DeleteRegistration(
+ registration->id(), kScope.GetOrigin(),
+ base::Bind(&VerifyPurgeableListStatusCallback,
+ base::Unretained(storage()->database_.get()),
+ &verify_ids, &was_called, &result));
base::RunLoop().RunUntilIdle();
- ASSERT_TRUE(called);
+ ASSERT_TRUE(was_called);
+ EXPECT_EQ(SERVICE_WORKER_OK, result);
+ EXPECT_EQ(2u, verify_ids.size());
+ verify_ids.clear();
+ EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+ storage()->database_->GetPurgeableResourceIds(&verify_ids));
+ EXPECT_TRUE(verify_ids.empty());
+
+ EXPECT_FALSE(VerifyBasicResponse(storage(), kResourceId1, false));
+ EXPECT_FALSE(VerifyBasicResponse(storage(), kResourceId2, false));
+}
- ASSERT_EQ(new_registration, old_registration);
+TEST_F(ServiceWorkerStorageTest, FindRegistration_LongestScopeMatch) {
+ const GURL kDocumentUrl("http://www.example.com/scope/foo");
+ scoped_refptr<ServiceWorkerRegistration> found_registration;
+
+ // Registration for "/scope/*".
+ const GURL kScope1("http://www.example.com/scope/*");
+ const GURL kScript1("http://www.example.com/script1.js");
+ const int64 kRegistrationId1 = 1;
+ const int64 kVersionId1 = 1;
+ scoped_refptr<ServiceWorkerRegistration> live_registration1 =
+ new ServiceWorkerRegistration(
+ kScope1, kScript1, kRegistrationId1, context_ptr_);
+ scoped_refptr<ServiceWorkerVersion> live_version1 =
+ new ServiceWorkerVersion(
+ live_registration1, kVersionId1, context_ptr_);
+ live_version1->SetStatus(ServiceWorkerVersion::INSTALLED);
+ live_registration1->set_waiting_version(live_version1);
+
+ // Registration for "/scope/foo*".
+ const GURL kScope2("http://www.example.com/scope/foo*");
+ const GURL kScript2("http://www.example.com/script2.js");
+ const int64 kRegistrationId2 = 2;
+ const int64 kVersionId2 = 2;
+ scoped_refptr<ServiceWorkerRegistration> live_registration2 =
+ new ServiceWorkerRegistration(
+ kScope2, kScript2, kRegistrationId2, context_ptr_);
+ scoped_refptr<ServiceWorkerVersion> live_version2 =
+ new ServiceWorkerVersion(
+ live_registration2, kVersionId2, context_ptr_);
+ live_version2->SetStatus(ServiceWorkerVersion::INSTALLED);
+ live_registration2->set_waiting_version(live_version2);
+
+ // Registration for "/scope/foo".
+ const GURL kScope3("http://www.example.com/scope/foo");
+ const GURL kScript3("http://www.example.com/script3.js");
+ const int64 kRegistrationId3 = 3;
+ const int64 kVersionId3 = 3;
+ scoped_refptr<ServiceWorkerRegistration> live_registration3 =
+ new ServiceWorkerRegistration(
+ kScope3, kScript3, kRegistrationId3, context_ptr_);
+ scoped_refptr<ServiceWorkerVersion> live_version3 =
+ new ServiceWorkerVersion(
+ live_registration3, kVersionId3, context_ptr_);
+ live_version3->SetStatus(ServiceWorkerVersion::INSTALLED);
+ live_registration3->set_waiting_version(live_version3);
+
+ // Notify storage of they being installed.
+ storage()->NotifyInstallingRegistration(live_registration1);
+ storage()->NotifyInstallingRegistration(live_registration2);
+ storage()->NotifyInstallingRegistration(live_registration3);
+
+ // Find a registration among installing ones.
+ EXPECT_EQ(SERVICE_WORKER_OK,
+ FindRegistrationForDocument(kDocumentUrl, &found_registration));
+ EXPECT_EQ(live_registration2, found_registration);
+ found_registration = NULL;
+
+ // Store registrations.
+ EXPECT_EQ(SERVICE_WORKER_OK,
+ StoreRegistration(live_registration1, live_version1));
+ EXPECT_EQ(SERVICE_WORKER_OK,
+ StoreRegistration(live_registration2, live_version2));
+ EXPECT_EQ(SERVICE_WORKER_OK,
+ StoreRegistration(live_registration3, live_version3));
+
+ // Notify storage of installations no longer happening.
+ storage()->NotifyDoneInstallingRegistration(
+ live_registration1, NULL, SERVICE_WORKER_OK);
+ storage()->NotifyDoneInstallingRegistration(
+ live_registration2, NULL, SERVICE_WORKER_OK);
+ storage()->NotifyDoneInstallingRegistration(
+ live_registration3, NULL, SERVICE_WORKER_OK);
+
+ // Find a registration among installed ones.
+ EXPECT_EQ(SERVICE_WORKER_OK,
+ FindRegistrationForDocument(kDocumentUrl, &found_registration));
+ EXPECT_EQ(live_registration2, found_registration);
}
} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_test_utils.h b/chromium/content/browser/service_worker/service_worker_test_utils.h
new file mode 100644
index 00000000000..5bb72c9e105
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_test_utils.h
@@ -0,0 +1,39 @@
+// 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_SERVICE_WORKER_SERVICE_WORKER_TEST_UTILS_H_
+#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_TEST_UTILS_H_
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace content {
+
+template <typename Arg>
+void ReceiveResult(BrowserThread::ID run_quit_thread,
+ const base::Closure& quit,
+ Arg* out, Arg actual) {
+ *out = actual;
+ if (!quit.is_null())
+ BrowserThread::PostTask(run_quit_thread, FROM_HERE, quit);
+}
+
+template <typename Arg> base::Callback<void(Arg)>
+CreateReceiver(BrowserThread::ID run_quit_thread,
+ const base::Closure& quit, Arg* out) {
+ return base::Bind(&ReceiveResult<Arg>, run_quit_thread, quit, out);
+}
+
+template <typename Arg> base::Callback<void(Arg)>
+CreateReceiverOnCurrentThread(Arg* out) {
+ BrowserThread::ID id;
+ bool ret = BrowserThread::GetCurrentThreadIdentifier(&id);
+ DCHECK(ret);
+ return base::Bind(&ReceiveResult<Arg>, id, base::Closure(), out);
+}
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_TEST_UTILS_H_
diff --git a/chromium/content/browser/service_worker/service_worker_unregister_job.cc b/chromium/content/browser/service_worker/service_worker_unregister_job.cc
new file mode 100644
index 00000000000..dabcd22f108
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_unregister_job.cc
@@ -0,0 +1,90 @@
+// 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/service_worker/service_worker_unregister_job.h"
+
+#include "content/browser/service_worker/service_worker_context_core.h"
+#include "content/browser/service_worker/service_worker_job_coordinator.h"
+#include "content/browser/service_worker/service_worker_registration.h"
+#include "content/browser/service_worker/service_worker_storage.h"
+
+namespace content {
+
+typedef ServiceWorkerRegisterJobBase::RegistrationJobType RegistrationJobType;
+
+ServiceWorkerUnregisterJob::ServiceWorkerUnregisterJob(
+ base::WeakPtr<ServiceWorkerContextCore> context,
+ const GURL& pattern)
+ : context_(context),
+ pattern_(pattern),
+ weak_factory_(this) {}
+
+ServiceWorkerUnregisterJob::~ServiceWorkerUnregisterJob() {}
+
+void ServiceWorkerUnregisterJob::AddCallback(
+ const UnregistrationCallback& callback) {
+ callbacks_.push_back(callback);
+}
+
+void ServiceWorkerUnregisterJob::Start() {
+ context_->storage()->FindRegistrationForPattern(
+ pattern_,
+ base::Bind(&ServiceWorkerUnregisterJob::DeleteExistingRegistration,
+ weak_factory_.GetWeakPtr()));
+}
+
+void ServiceWorkerUnregisterJob::Abort() {
+ CompleteInternal(SERVICE_WORKER_ERROR_ABORT);
+}
+
+bool ServiceWorkerUnregisterJob::Equals(ServiceWorkerRegisterJobBase* job) {
+ if (job->GetType() != GetType())
+ return false;
+ return static_cast<ServiceWorkerUnregisterJob*>(job)->pattern_ == pattern_;
+}
+
+RegistrationJobType ServiceWorkerUnregisterJob::GetType() {
+ return ServiceWorkerRegisterJobBase::UNREGISTRATION;
+}
+
+void ServiceWorkerUnregisterJob::DeleteExistingRegistration(
+ ServiceWorkerStatusCode status,
+ const scoped_refptr<ServiceWorkerRegistration>& registration) {
+ if (status == SERVICE_WORKER_OK) {
+ DCHECK(registration);
+ // TODO(michaeln): Deactivate the live registration object and
+ // eventually call storage->DeleteVersionResources()
+ // when the version no longer has any controllees.
+ context_->storage()->DeleteRegistration(
+ registration->id(),
+ registration->script_url().GetOrigin(),
+ base::Bind(&ServiceWorkerUnregisterJob::Complete,
+ weak_factory_.GetWeakPtr()));
+ return;
+ }
+
+ if (status == SERVICE_WORKER_ERROR_NOT_FOUND) {
+ DCHECK(!registration);
+ Complete(SERVICE_WORKER_OK);
+ return;
+ }
+
+ Complete(status);
+}
+
+void ServiceWorkerUnregisterJob::Complete(ServiceWorkerStatusCode status) {
+ CompleteInternal(status);
+ context_->job_coordinator()->FinishJob(pattern_, this);
+}
+
+void ServiceWorkerUnregisterJob::CompleteInternal(
+ ServiceWorkerStatusCode status) {
+ for (std::vector<UnregistrationCallback>::iterator it = callbacks_.begin();
+ it != callbacks_.end();
+ ++it) {
+ it->Run(status);
+ }
+}
+
+} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_unregister_job.h b/chromium/content/browser/service_worker/service_worker_unregister_job.h
new file mode 100644
index 00000000000..484318136f3
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_unregister_job.h
@@ -0,0 +1,64 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_UNREGISTER_JOB_H_
+#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_UNREGISTER_JOB_H_
+
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "content/browser/service_worker/service_worker_register_job_base.h"
+#include "content/common/service_worker/service_worker_status_code.h"
+#include "url/gurl.h"
+
+namespace content {
+
+class EmbeddedWorkerRegistry;
+class ServiceWorkerContextCore;
+class ServiceWorkerJobCoordinator;
+class ServiceWorkerRegistration;
+class ServiceWorkerStorage;
+
+// Handles the unregistration of a Service Worker.
+//
+// The unregistration process is primarily cleanup, removing everything that was
+// created during the registration process, including the
+// ServiceWorkerRegistration itself.
+class ServiceWorkerUnregisterJob : public ServiceWorkerRegisterJobBase {
+ public:
+ typedef base::Callback<void(ServiceWorkerStatusCode status)>
+ UnregistrationCallback;
+
+ ServiceWorkerUnregisterJob(base::WeakPtr<ServiceWorkerContextCore> context,
+ const GURL& pattern);
+ virtual ~ServiceWorkerUnregisterJob();
+
+ // Registers a callback to be called when the job completes (whether
+ // successfully or not). Multiple callbacks may be registered.
+ void AddCallback(const UnregistrationCallback& callback);
+
+ // ServiceWorkerRegisterJobBase implementation:
+ virtual void Start() OVERRIDE;
+ virtual void Abort() OVERRIDE;
+ virtual bool Equals(ServiceWorkerRegisterJobBase* job) OVERRIDE;
+ virtual RegistrationJobType GetType() OVERRIDE;
+
+ private:
+ void DeleteExistingRegistration(
+ ServiceWorkerStatusCode status,
+ const scoped_refptr<ServiceWorkerRegistration>& registration);
+ void Complete(ServiceWorkerStatusCode status);
+ void CompleteInternal(ServiceWorkerStatusCode status);
+
+ // The ServiceWorkerStorage object should always outlive this.
+ base::WeakPtr<ServiceWorkerContextCore> context_;
+ const GURL pattern_;
+ std::vector<UnregistrationCallback> callbacks_;
+ base::WeakPtrFactory<ServiceWorkerUnregisterJob> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServiceWorkerUnregisterJob);
+};
+} // namespace content
+
+#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_UNREGISTER_JOB_H_
diff --git a/chromium/content/browser/service_worker/service_worker_url_request_job.cc b/chromium/content/browser/service_worker/service_worker_url_request_job.cc
new file mode 100644
index 00000000000..9d75b93ec25
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_url_request_job.cc
@@ -0,0 +1,304 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/service_worker/service_worker_url_request_job.h"
+
+#include "base/bind.h"
+#include "base/strings/stringprintf.h"
+#include "content/browser/service_worker/service_worker_fetch_dispatcher.h"
+#include "content/browser/service_worker/service_worker_provider_host.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_response_info.h"
+#include "net/http/http_util.h"
+#include "webkit/browser/blob/blob_data_handle.h"
+#include "webkit/browser/blob/blob_storage_context.h"
+#include "webkit/browser/blob/blob_url_request_job_factory.h"
+
+namespace content {
+
+ServiceWorkerURLRequestJob::ServiceWorkerURLRequestJob(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate,
+ base::WeakPtr<ServiceWorkerProviderHost> provider_host,
+ base::WeakPtr<webkit_blob::BlobStorageContext> blob_storage_context)
+ : net::URLRequestJob(request, network_delegate),
+ provider_host_(provider_host),
+ response_type_(NOT_DETERMINED),
+ is_started_(false),
+ blob_storage_context_(blob_storage_context),
+ weak_factory_(this) {
+}
+
+void ServiceWorkerURLRequestJob::FallbackToNetwork() {
+ DCHECK_EQ(NOT_DETERMINED, response_type_);
+ response_type_ = FALLBACK_TO_NETWORK;
+ MaybeStartRequest();
+}
+
+void ServiceWorkerURLRequestJob::ForwardToServiceWorker() {
+ DCHECK_EQ(NOT_DETERMINED, response_type_);
+ response_type_ = FORWARD_TO_SERVICE_WORKER;
+ MaybeStartRequest();
+}
+
+void ServiceWorkerURLRequestJob::Start() {
+ is_started_ = true;
+ MaybeStartRequest();
+}
+
+void ServiceWorkerURLRequestJob::Kill() {
+ net::URLRequestJob::Kill();
+ fetch_dispatcher_.reset();
+ blob_request_.reset();
+ weak_factory_.InvalidateWeakPtrs();
+}
+
+net::LoadState ServiceWorkerURLRequestJob::GetLoadState() const {
+ // TODO(kinuko): refine this for better debug.
+ return net::URLRequestJob::GetLoadState();
+}
+
+bool ServiceWorkerURLRequestJob::GetCharset(std::string* charset) {
+ if (!http_info())
+ return false;
+ return http_info()->headers->GetCharset(charset);
+}
+
+bool ServiceWorkerURLRequestJob::GetMimeType(std::string* mime_type) const {
+ if (!http_info())
+ return false;
+ return http_info()->headers->GetMimeType(mime_type);
+}
+
+void ServiceWorkerURLRequestJob::GetResponseInfo(net::HttpResponseInfo* info) {
+ if (!http_info())
+ return;
+ *info = *http_info();
+}
+
+int ServiceWorkerURLRequestJob::GetResponseCode() const {
+ if (!http_info())
+ return -1;
+ return http_info()->headers->response_code();
+}
+
+void ServiceWorkerURLRequestJob::SetExtraRequestHeaders(
+ const net::HttpRequestHeaders& headers) {
+ std::string range_header;
+ std::vector<net::HttpByteRange> ranges;
+ if (!headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header) ||
+ !net::HttpUtil::ParseRangeHeader(range_header, &ranges)) {
+ return;
+ }
+
+ // We don't support multiple range requests in one single URL request.
+ if (ranges.size() == 1U)
+ byte_range_ = ranges[0];
+}
+
+bool ServiceWorkerURLRequestJob::ReadRawData(
+ net::IOBuffer* buf, int buf_size, int *bytes_read) {
+ if (!blob_request_) {
+ *bytes_read = 0;
+ return true;
+ }
+
+ blob_request_->Read(buf, buf_size, bytes_read);
+ net::URLRequestStatus status = blob_request_->status();
+ SetStatus(status);
+ if (status.is_io_pending())
+ return false;
+ return status.is_success();
+}
+
+void ServiceWorkerURLRequestJob::OnReceivedRedirect(net::URLRequest* request,
+ const GURL& new_url,
+ bool* defer_redirect) {
+ NOTREACHED();
+}
+
+void ServiceWorkerURLRequestJob::OnAuthRequired(
+ net::URLRequest* request,
+ net::AuthChallengeInfo* auth_info) {
+ NOTREACHED();
+}
+
+void ServiceWorkerURLRequestJob::OnCertificateRequested(
+ net::URLRequest* request,
+ net::SSLCertRequestInfo* cert_request_info) {
+ NOTREACHED();
+}
+
+void ServiceWorkerURLRequestJob::OnSSLCertificateError(
+ net::URLRequest* request,
+ const net::SSLInfo& ssl_info,
+ bool fatal) {
+ NOTREACHED();
+}
+
+void ServiceWorkerURLRequestJob::OnBeforeNetworkStart(net::URLRequest* request,
+ bool* defer) {
+ NOTREACHED();
+}
+
+void ServiceWorkerURLRequestJob::OnResponseStarted(net::URLRequest* request) {
+ // TODO(falken): Add Content-Length, Content-Type if they were not provided in
+ // the ServiceWorkerResponse.
+ CommitResponseHeader();
+}
+
+void ServiceWorkerURLRequestJob::OnReadCompleted(net::URLRequest* request,
+ int bytes_read) {
+ SetStatus(request->status());
+ if (!request->status().is_success()) {
+ NotifyDone(request->status());
+ return;
+ }
+ NotifyReadComplete(bytes_read);
+ if (bytes_read == 0)
+ NotifyDone(request->status());
+}
+
+const net::HttpResponseInfo* ServiceWorkerURLRequestJob::http_info() const {
+ if (!http_response_info_)
+ return NULL;
+ if (range_response_info_)
+ return range_response_info_.get();
+ return http_response_info_.get();
+}
+
+ServiceWorkerURLRequestJob::~ServiceWorkerURLRequestJob() {
+}
+
+void ServiceWorkerURLRequestJob::MaybeStartRequest() {
+ if (is_started_ && response_type_ != NOT_DETERMINED) {
+ // Start asynchronously.
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&ServiceWorkerURLRequestJob::StartRequest,
+ weak_factory_.GetWeakPtr()));
+ }
+}
+
+void ServiceWorkerURLRequestJob::StartRequest() {
+ switch (response_type_) {
+ case NOT_DETERMINED:
+ NOTREACHED();
+ return;
+
+ case FALLBACK_TO_NETWORK:
+ // Restart the request to create a new job. Our request handler will
+ // return NULL, and the default job (which will hit network) should be
+ // created.
+ NotifyRestartRequired();
+ return;
+
+ case FORWARD_TO_SERVICE_WORKER:
+ DCHECK(provider_host_ && provider_host_->active_version());
+ DCHECK(!fetch_dispatcher_);
+
+ // Send a fetch event to the ServiceWorker associated to the
+ // provider_host.
+ fetch_dispatcher_.reset(new ServiceWorkerFetchDispatcher(
+ request(), provider_host_->active_version(),
+ base::Bind(&ServiceWorkerURLRequestJob::DidDispatchFetchEvent,
+ weak_factory_.GetWeakPtr())));
+ fetch_dispatcher_->Run();
+ return;
+ }
+
+ NOTREACHED();
+}
+
+void ServiceWorkerURLRequestJob::DidDispatchFetchEvent(
+ ServiceWorkerStatusCode status,
+ ServiceWorkerFetchEventResult fetch_result,
+ const ServiceWorkerResponse& response) {
+ fetch_dispatcher_.reset();
+
+ // Check if we're not orphaned.
+ if (!request())
+ return;
+
+ if (status != SERVICE_WORKER_OK) {
+ // Dispatching event has been failed, falling back to the network.
+ // (Tentative behavior described on github)
+ // TODO(kinuko): consider returning error if we've come here because
+ // unexpected worker termination etc (so that we could fix bugs).
+ // TODO(kinuko): Would be nice to log the error case.
+ response_type_ = FALLBACK_TO_NETWORK;
+ NotifyRestartRequired();
+ return;
+ }
+
+ if (fetch_result == SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK) {
+ // Change the response type and restart the request to fallback to
+ // the network.
+ response_type_ = FALLBACK_TO_NETWORK;
+ NotifyRestartRequired();
+ return;
+ }
+
+ // We should have a response now.
+ DCHECK_EQ(SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE, fetch_result);
+
+ // Set up a request for reading the blob.
+ if (!response.blob_uuid.empty() && blob_storage_context_) {
+ scoped_ptr<webkit_blob::BlobDataHandle> blob_data_handle =
+ blob_storage_context_->GetBlobDataFromUUID(response.blob_uuid);
+ if (!blob_data_handle) {
+ // The renderer gave us a bad blob UUID.
+ DeliverErrorResponse();
+ return;
+ }
+ blob_request_ = webkit_blob::BlobProtocolHandler::CreateBlobRequest(
+ blob_data_handle.Pass(), request()->context(), this);
+ blob_request_->Start();
+ }
+
+ CreateResponseHeader(
+ response.status_code, response.status_text, response.headers);
+ if (!blob_request_)
+ CommitResponseHeader();
+}
+
+void ServiceWorkerURLRequestJob::CreateResponseHeader(
+ int status_code,
+ const std::string& status_text,
+ const std::map<std::string, std::string>& headers) {
+ // TODO(kinuko): If the response has an identifier to on-disk cache entry,
+ // pull response header from the disk.
+ std::string status_line(
+ base::StringPrintf("HTTP/1.1 %d %s", status_code, status_text.c_str()));
+ status_line.push_back('\0');
+ http_response_headers_ = new net::HttpResponseHeaders(status_line);
+ for (std::map<std::string, std::string>::const_iterator it = headers.begin();
+ it != headers.end();
+ ++it) {
+ std::string header;
+ header.reserve(it->first.size() + 2 + it->second.size());
+ header.append(it->first);
+ header.append(": ");
+ header.append(it->second);
+ http_response_headers_->AddHeader(header);
+ }
+}
+
+void ServiceWorkerURLRequestJob::CommitResponseHeader() {
+ http_response_info_.reset(new net::HttpResponseInfo());
+ http_response_info_->headers.swap(http_response_headers_);
+ NotifyHeadersComplete();
+}
+
+void ServiceWorkerURLRequestJob::DeliverErrorResponse() {
+ // TODO(falken): Print an error to the console of the ServiceWorker and of
+ // the requesting page.
+ CreateResponseHeader(500,
+ "Service Worker Response Error",
+ std::map<std::string, std::string>());
+ CommitResponseHeader();
+}
+
+} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_url_request_job.h b/chromium/content/browser/service_worker/service_worker_url_request_job.h
new file mode 100644
index 00000000000..498058cffb4
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_url_request_job.h
@@ -0,0 +1,137 @@
+// 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_SERVICE_WORKER_SERVICE_WORKER_URL_REQUEST_JOB_H_
+#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_URL_REQUEST_JOB_H_
+
+#include "base/memory/weak_ptr.h"
+#include "content/common/content_export.h"
+#include "content/common/service_worker/service_worker_status_code.h"
+#include "content/common/service_worker/service_worker_types.h"
+#include "net/http/http_byte_range.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_job.h"
+
+namespace webkit_blob {
+class BlobStorageContext;
+}
+
+namespace content {
+
+class ServiceWorkerContextCore;
+class ServiceWorkerFetchDispatcher;
+class ServiceWorkerProviderHost;
+
+class CONTENT_EXPORT ServiceWorkerURLRequestJob
+ : public net::URLRequestJob,
+ public net::URLRequest::Delegate {
+ public:
+ ServiceWorkerURLRequestJob(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate,
+ base::WeakPtr<ServiceWorkerProviderHost> provider_host,
+ base::WeakPtr<webkit_blob::BlobStorageContext> blob_storage_context);
+
+ // Sets the response type.
+ void FallbackToNetwork();
+ void ForwardToServiceWorker();
+
+ bool ShouldFallbackToNetwork() const {
+ return response_type_ == FALLBACK_TO_NETWORK;
+ }
+ bool ShouldForwardToServiceWorker() const {
+ return response_type_ == FORWARD_TO_SERVICE_WORKER;
+ }
+
+ // net::URLRequestJob overrides:
+ virtual void Start() OVERRIDE;
+ virtual void Kill() OVERRIDE;
+ virtual net::LoadState GetLoadState() const OVERRIDE;
+ virtual bool GetCharset(std::string* charset) OVERRIDE;
+ virtual bool GetMimeType(std::string* mime_type) const OVERRIDE;
+ virtual void GetResponseInfo(net::HttpResponseInfo* info) OVERRIDE;
+ virtual int GetResponseCode() const OVERRIDE;
+ virtual void SetExtraRequestHeaders(
+ const net::HttpRequestHeaders& headers) OVERRIDE;
+ virtual bool ReadRawData(net::IOBuffer* buf,
+ int buf_size,
+ int *bytes_read) OVERRIDE;
+
+ // net::URLRequest::Delegate overrides that read the blob from the
+ // ServiceWorkerFetchResponse.
+ virtual void OnReceivedRedirect(net::URLRequest* request,
+ const GURL& new_url,
+ bool* defer_redirect) OVERRIDE;
+ virtual void OnAuthRequired(net::URLRequest* request,
+ net::AuthChallengeInfo* auth_info) OVERRIDE;
+ virtual void OnCertificateRequested(
+ net::URLRequest* request,
+ net::SSLCertRequestInfo* cert_request_info) OVERRIDE;
+ virtual void OnSSLCertificateError(net::URLRequest* request,
+ const net::SSLInfo& ssl_info,
+ bool fatal) OVERRIDE;
+ virtual void OnBeforeNetworkStart(net::URLRequest* request,
+ bool* defer) OVERRIDE;
+ virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE;
+ virtual void OnReadCompleted(net::URLRequest* request,
+ int bytes_read) OVERRIDE;
+
+ const net::HttpResponseInfo* http_info() const;
+
+ protected:
+ virtual ~ServiceWorkerURLRequestJob();
+
+ private:
+ enum ResponseType {
+ NOT_DETERMINED,
+ FALLBACK_TO_NETWORK,
+ FORWARD_TO_SERVICE_WORKER,
+ };
+
+ // We start processing the request if Start() is called AND response_type_
+ // is determined.
+ void MaybeStartRequest();
+ void StartRequest();
+
+ // For FORWARD_TO_SERVICE_WORKER case.
+ void DidDispatchFetchEvent(ServiceWorkerStatusCode status,
+ ServiceWorkerFetchEventResult fetch_result,
+ const ServiceWorkerResponse& response);
+
+ // Populates |http_response_headers_|.
+ void CreateResponseHeader(int status_code,
+ const std::string& status_text,
+ const std::map<std::string, std::string>& headers);
+
+ // Creates |http_response_info_| using |http_response_headers_| and calls
+ // NotifyHeadersComplete.
+ void CommitResponseHeader();
+
+ // Creates and commits a response header indicating error.
+ void DeliverErrorResponse();
+
+ base::WeakPtr<ServiceWorkerProviderHost> provider_host_;
+
+ ResponseType response_type_;
+ bool is_started_;
+
+ net::HttpByteRange byte_range_;
+ scoped_ptr<net::HttpResponseInfo> range_response_info_;
+ scoped_ptr<net::HttpResponseInfo> http_response_info_;
+ // Headers that have not yet been committed to |http_response_info_|.
+ scoped_refptr<net::HttpResponseHeaders> http_response_headers_;
+
+ // Used when response type is FORWARD_TO_SERVICE_WORKER.
+ scoped_ptr<ServiceWorkerFetchDispatcher> fetch_dispatcher_;
+ base::WeakPtr<webkit_blob::BlobStorageContext> blob_storage_context_;
+ scoped_ptr<net::URLRequest> blob_request_;
+
+ base::WeakPtrFactory<ServiceWorkerURLRequestJob> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServiceWorkerURLRequestJob);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_URL_REQUEST_JOB_H_
diff --git a/chromium/content/browser/service_worker/service_worker_url_request_job_unittest.cc b/chromium/content/browser/service_worker/service_worker_url_request_job_unittest.cc
new file mode 100644
index 00000000000..54a2cc966fc
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_url_request_job_unittest.cc
@@ -0,0 +1,238 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "content/browser/fileapi/chrome_blob_storage_context.h"
+#include "content/browser/fileapi/mock_url_request_delegate.h"
+#include "content/browser/service_worker/embedded_worker_registry.h"
+#include "content/browser/service_worker/embedded_worker_test_helper.h"
+#include "content/browser/service_worker/service_worker_context_core.h"
+#include "content/browser/service_worker/service_worker_provider_host.h"
+#include "content/browser/service_worker/service_worker_registration.h"
+#include "content/browser/service_worker/service_worker_test_utils.h"
+#include "content/browser/service_worker/service_worker_url_request_job.h"
+#include "content/browser/service_worker/service_worker_version.h"
+#include "content/common/service_worker/service_worker_messages.h"
+#include "content/public/browser/blob_handle.h"
+#include "content/public/test/test_browser_context.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "net/base/io_buffer.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_response_headers.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_job_factory_impl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/blob/blob_storage_context.h"
+#include "webkit/browser/blob/blob_url_request_job.h"
+#include "webkit/browser/blob/blob_url_request_job_factory.h"
+#include "webkit/common/blob/blob_data.h"
+
+namespace content {
+
+class ServiceWorkerURLRequestJobTest;
+
+namespace {
+
+const int kProcessID = 1;
+const int kProviderID = 100;
+const char kTestData[] = "Here is sample text for the blob.";
+
+class MockHttpProtocolHandler
+ : public net::URLRequestJobFactory::ProtocolHandler {
+ public:
+ MockHttpProtocolHandler(
+ base::WeakPtr<ServiceWorkerProviderHost> provider_host,
+ base::WeakPtr<webkit_blob::BlobStorageContext> blob_storage_context)
+ : provider_host_(provider_host),
+ blob_storage_context_(blob_storage_context) {}
+ virtual ~MockHttpProtocolHandler() {}
+
+ virtual net::URLRequestJob* MaybeCreateJob(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) const OVERRIDE {
+ ServiceWorkerURLRequestJob* job = new ServiceWorkerURLRequestJob(
+ request, network_delegate, provider_host_, blob_storage_context_);
+ job->ForwardToServiceWorker();
+ return job;
+ }
+
+ private:
+ base::WeakPtr<ServiceWorkerProviderHost> provider_host_;
+ base::WeakPtr<webkit_blob::BlobStorageContext> blob_storage_context_;
+};
+
+// Returns a BlobProtocolHandler that uses |blob_storage_context|. Caller owns
+// the memory.
+webkit_blob::BlobProtocolHandler* CreateMockBlobProtocolHandler(
+ webkit_blob::BlobStorageContext* blob_storage_context) {
+ // The FileSystemContext and MessageLoopProxy are not actually used but a
+ // MessageLoopProxy is needed to avoid a DCHECK in BlobURLRequestJob ctor.
+ return new webkit_blob::BlobProtocolHandler(
+ blob_storage_context, NULL, base::MessageLoopProxy::current().get());
+}
+
+} // namespace
+
+class ServiceWorkerURLRequestJobTest : public testing::Test {
+ protected:
+ ServiceWorkerURLRequestJobTest()
+ : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP),
+ blob_data_(new webkit_blob::BlobData("blob-id:myblob")) {}
+ virtual ~ServiceWorkerURLRequestJobTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ browser_context_.reset(new TestBrowserContext);
+ SetUpWithHelper(new EmbeddedWorkerTestHelper(kProcessID));
+ }
+
+ void SetUpWithHelper(EmbeddedWorkerTestHelper* helper) {
+ helper_.reset(helper);
+
+ registration_ = new ServiceWorkerRegistration(
+ GURL("http://example.com/*"),
+ GURL("http://example.com/service_worker.js"),
+ 1L,
+ helper_->context()->AsWeakPtr());
+ version_ = new ServiceWorkerVersion(
+ registration_, 1L, helper_->context()->AsWeakPtr());
+
+ scoped_ptr<ServiceWorkerProviderHost> provider_host(
+ new ServiceWorkerProviderHost(
+ kProcessID, kProviderID, helper_->context()->AsWeakPtr(), NULL));
+ provider_host->SetActiveVersion(version_.get());
+
+ ChromeBlobStorageContext* chrome_blob_storage_context =
+ ChromeBlobStorageContext::GetFor(browser_context_.get());
+ // Wait for chrome_blob_storage_context to finish initializing.
+ base::RunLoop().RunUntilIdle();
+ webkit_blob::BlobStorageContext* blob_storage_context =
+ chrome_blob_storage_context->context();
+
+ url_request_job_factory_.reset(new net::URLRequestJobFactoryImpl);
+ url_request_job_factory_->SetProtocolHandler(
+ "http",
+ new MockHttpProtocolHandler(provider_host->AsWeakPtr(),
+ blob_storage_context->AsWeakPtr()));
+ url_request_job_factory_->SetProtocolHandler(
+ "blob", CreateMockBlobProtocolHandler(blob_storage_context));
+ url_request_context_.set_job_factory(url_request_job_factory_.get());
+
+ helper_->context()->AddProviderHost(provider_host.Pass());
+ }
+
+ virtual void TearDown() OVERRIDE {
+ version_ = NULL;
+ registration_ = NULL;
+ helper_.reset();
+ }
+
+ void TestRequest(int expected_status_code,
+ const std::string& expected_status_text,
+ const std::string& expected_response) {
+ request_ = url_request_context_.CreateRequest(
+ GURL("http://example.com/foo.html"),
+ net::DEFAULT_PRIORITY,
+ &url_request_delegate_,
+ NULL);
+
+ request_->set_method("GET");
+ request_->Start();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(request_->status().is_success());
+ EXPECT_EQ(expected_status_code,
+ request_->response_headers()->response_code());
+ EXPECT_EQ(expected_status_text,
+ request_->response_headers()->GetStatusText());
+ EXPECT_EQ(expected_response, url_request_delegate_.response_data());
+ }
+
+ TestBrowserThreadBundle thread_bundle_;
+
+ scoped_ptr<TestBrowserContext> browser_context_;
+ scoped_ptr<EmbeddedWorkerTestHelper> helper_;
+ scoped_refptr<ServiceWorkerRegistration> registration_;
+ scoped_refptr<ServiceWorkerVersion> version_;
+
+ scoped_ptr<net::URLRequestJobFactoryImpl> url_request_job_factory_;
+ net::URLRequestContext url_request_context_;
+ MockURLRequestDelegate url_request_delegate_;
+ scoped_ptr<net::URLRequest> request_;
+
+ scoped_refptr<webkit_blob::BlobData> blob_data_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServiceWorkerURLRequestJobTest);
+};
+
+TEST_F(ServiceWorkerURLRequestJobTest, Simple) {
+ version_->SetStatus(ServiceWorkerVersion::ACTIVE);
+ TestRequest(200, "OK", std::string());
+}
+
+TEST_F(ServiceWorkerURLRequestJobTest, WaitForActivation) {
+ ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
+ version_->SetStatus(ServiceWorkerVersion::INSTALLED);
+ version_->DispatchActivateEvent(CreateReceiverOnCurrentThread(&status));
+
+ TestRequest(200, "OK", std::string());
+
+ EXPECT_EQ(SERVICE_WORKER_OK, status);
+}
+
+// Responds to fetch events with a blob.
+class BlobResponder : public EmbeddedWorkerTestHelper {
+ public:
+ BlobResponder(int mock_render_process_id, const std::string& blob_uuid)
+ : EmbeddedWorkerTestHelper(mock_render_process_id),
+ blob_uuid_(blob_uuid) {}
+ virtual ~BlobResponder() {}
+
+ protected:
+ virtual void OnFetchEvent(int embedded_worker_id,
+ int request_id,
+ const ServiceWorkerFetchRequest& request) OVERRIDE {
+ SimulateSend(new ServiceWorkerHostMsg_FetchEventFinished(
+ embedded_worker_id,
+ request_id,
+ SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE,
+ ServiceWorkerResponse(200,
+ "OK",
+ std::map<std::string, std::string>(),
+ blob_uuid_)));
+ }
+
+ std::string blob_uuid_;
+ DISALLOW_COPY_AND_ASSIGN(BlobResponder);
+};
+
+TEST_F(ServiceWorkerURLRequestJobTest, BlobResponse) {
+ ChromeBlobStorageContext* blob_storage_context =
+ ChromeBlobStorageContext::GetFor(browser_context_.get());
+ std::string expected_response;
+ for (int i = 0; i < 1024; ++i) {
+ blob_data_->AppendData(kTestData);
+ expected_response += kTestData;
+ }
+ scoped_ptr<webkit_blob::BlobDataHandle> blob_handle =
+ blob_storage_context->context()->AddFinishedBlob(blob_data_);
+ SetUpWithHelper(new BlobResponder(kProcessID, blob_handle->uuid()));
+
+ version_->SetStatus(ServiceWorkerVersion::ACTIVE);
+ TestRequest(200, "OK", expected_response);
+}
+
+TEST_F(ServiceWorkerURLRequestJobTest, NonExistentBlobUUIDResponse) {
+ SetUpWithHelper(new BlobResponder(kProcessID, "blob-id:nothing-is-here"));
+ version_->SetStatus(ServiceWorkerVersion::ACTIVE);
+ TestRequest(500, "Service Worker Response Error", std::string());
+}
+
+// TODO(kinuko): Add more tests with different response data and also for
+// FallbackToNetwork case.
+
+} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_utils.cc b/chromium/content/browser/service_worker/service_worker_utils.cc
new file mode 100644
index 00000000000..2c3d05b773f
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_utils.cc
@@ -0,0 +1,65 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/service_worker/service_worker_utils.h"
+
+#include <string>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "content/public/common/content_switches.h"
+#include "url/gurl.h"
+
+namespace content {
+
+// static
+bool ServiceWorkerUtils::IsFeatureEnabled() {
+ static bool enabled = CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableServiceWorker);
+ return enabled;
+}
+
+// static
+bool ServiceWorkerUtils::ScopeMatches(const GURL& scope, const GURL& url) {
+ DCHECK(!scope.has_ref());
+ DCHECK(!url.has_ref());
+ const std::string& scope_spec = scope.spec();
+ const std::string& url_spec = url.spec();
+
+ size_t len = scope_spec.size();
+ if (len > 0 && scope_spec[len - 1] == '*')
+ return scope_spec.compare(0, len - 1, url_spec, 0, len - 1) == 0;
+ return scope_spec == url_spec;
+}
+
+bool LongestScopeMatcher::MatchLongest(const GURL& scope) {
+ if (!ServiceWorkerUtils::ScopeMatches(scope, url_))
+ return false;
+ if (match_.is_empty()) {
+ match_ = scope;
+ return true;
+ }
+
+ const std::string match_spec = match_.spec();
+ const std::string scope_spec = scope.spec();
+ if (match_spec.size() < scope_spec.size()) {
+ match_ = scope;
+ return true;
+ }
+
+ // If |scope| has the same length with |match_|, they are compared as strings.
+ // For example:
+ // 1) for a document "/foo", "/foo" is prioritized over "/fo*".
+ // 2) for a document "/f(1)", "/f(1*" is prioritized over "/f(1)".
+ // TODO(nhiroki): This isn't in the spec.
+ // (https://github.com/slightlyoff/ServiceWorker/issues/287)
+ if (match_spec.size() == scope_spec.size() && match_spec < scope_spec) {
+ match_ = scope;
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_utils.h b/chromium/content/browser/service_worker/service_worker_utils.h
new file mode 100644
index 00000000000..25700dee2bd
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_utils.h
@@ -0,0 +1,55 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_UTILS_H_
+#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_UTILS_H_
+
+#include "content/common/content_export.h"
+#include "content/common/service_worker/service_worker_status_code.h"
+#include "webkit/common/resource_type.h"
+
+class GURL;
+
+namespace content {
+
+class ServiceWorkerUtils {
+ public:
+ static bool IsMainResourceType(ResourceType::Type type) {
+ return ResourceType::IsFrame(type) ||
+ ResourceType::IsSharedWorker(type);
+ }
+
+ static bool IsServiceWorkerResourceType(ResourceType::Type type) {
+ return ResourceType::IsServiceWorker(type);
+ }
+
+ // Returns true if the feature is enabled (or not disabled) by command-line
+ // flag.
+ static bool IsFeatureEnabled();
+
+ // A helper for creating a do-nothing status callback.
+ static void NoOpStatusCallback(ServiceWorkerStatusCode status) {}
+
+ // Returns true if |scope| matches |url|.
+ CONTENT_EXPORT static bool ScopeMatches(const GURL& scope, const GURL& url);
+};
+
+class CONTENT_EXPORT LongestScopeMatcher {
+ public:
+ explicit LongestScopeMatcher(const GURL& url) : url_(url) {}
+ virtual ~LongestScopeMatcher() {}
+
+ // Returns true if |scope| matches |url_| longer than |match_|.
+ bool MatchLongest(const GURL& scope);
+
+ private:
+ const GURL url_;
+ GURL match_;
+
+ DISALLOW_COPY_AND_ASSIGN(LongestScopeMatcher);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_UTILS_H_
diff --git a/chromium/content/browser/service_worker/service_worker_utils_unittest.cc b/chromium/content/browser/service_worker/service_worker_utils_unittest.cc
new file mode 100644
index 00000000000..67a74801d6c
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_utils_unittest.cc
@@ -0,0 +1,103 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/service_worker/service_worker_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+TEST(ServiceWorkerUtilsTest, ScopeMatches) {
+ ASSERT_TRUE(ServiceWorkerUtils::ScopeMatches(
+ GURL("http://www.example.com/*"), GURL("http://www.example.com/")));
+ ASSERT_TRUE(ServiceWorkerUtils::ScopeMatches(
+ GURL("http://www.example.com/*"),
+ GURL("http://www.example.com/page.html")));
+
+ ASSERT_FALSE(ServiceWorkerUtils::ScopeMatches(
+ GURL("http://www.example.com/*"), GURL("https://www.example.com/")));
+ ASSERT_FALSE(ServiceWorkerUtils::ScopeMatches(
+ GURL("http://www.example.com/*"),
+ GURL("https://www.example.com/page.html")));
+
+ ASSERT_FALSE(ServiceWorkerUtils::ScopeMatches(
+ GURL("http://www.example.com/*"), GURL("http://www.foo.com/")));
+ ASSERT_FALSE(ServiceWorkerUtils::ScopeMatches(
+ GURL("http://www.example.com/*"), GURL("https://www.foo.com/page.html")));
+
+ ASSERT_TRUE(ServiceWorkerUtils::ScopeMatches(
+ GURL("http://www.example.com/"), GURL("http://www.example.com/")));
+ ASSERT_FALSE(ServiceWorkerUtils::ScopeMatches(
+ GURL("http://www.example.com/"), GURL("http://www.example.com/x")));
+
+ // '?' is not a wildcard.
+ ASSERT_FALSE(ServiceWorkerUtils::ScopeMatches(
+ GURL("http://www.example.com/?"), GURL("http://www.example.com/x")));
+ ASSERT_FALSE(ServiceWorkerUtils::ScopeMatches(
+ GURL("http://www.example.com/?"), GURL("http://www.example.com/")));
+ ASSERT_FALSE(ServiceWorkerUtils::ScopeMatches(
+ GURL("http://www.example.com/?"), GURL("http://www.example.com/xx")));
+ ASSERT_TRUE(ServiceWorkerUtils::ScopeMatches(
+ GURL("http://www.example.com/?"), GURL("http://www.example.com/?")));
+
+ // Query string is part of the resource.
+ ASSERT_TRUE(
+ ServiceWorkerUtils::ScopeMatches(GURL("http://www.example.com/?a=b"),
+ GURL("http://www.example.com/?a=b")));
+ ASSERT_TRUE(
+ ServiceWorkerUtils::ScopeMatches(GURL("http://www.example.com/?a=*"),
+ GURL("http://www.example.com/?a=b")));
+ ASSERT_TRUE(ServiceWorkerUtils::ScopeMatches(
+ GURL("http://www.example.com/*"), GURL("http://www.example.com/?a=b")));
+ ASSERT_FALSE(ServiceWorkerUtils::ScopeMatches(
+ GURL("http://www.example.com/"), GURL("http://www.example.com/?a=b")));
+
+ // '*' only has special meaning in terminal position.
+ ASSERT_TRUE(ServiceWorkerUtils::ScopeMatches(
+ GURL("http://www.example.com/*/x"), GURL("http://www.example.com/*/x")));
+ ASSERT_FALSE(ServiceWorkerUtils::ScopeMatches(
+ GURL("http://www.example.com/*/x"), GURL("http://www.example.com/a/x")));
+ ASSERT_FALSE(
+ ServiceWorkerUtils::ScopeMatches(GURL("http://www.example.com/*/x/*"),
+ GURL("http://www.example.com/a/x/b")));
+ ASSERT_TRUE(
+ ServiceWorkerUtils::ScopeMatches(GURL("http://www.example.com/*/x/*"),
+ GURL("http://www.example.com/*/x/b")));
+
+ // URLs canonicalize \ to / so this is equivalent to "...//*" and "...//x"
+ ASSERT_TRUE(ServiceWorkerUtils::ScopeMatches(
+ GURL("http://www.example.com/\\*"), GURL("http://www.example.com/\\x")));
+}
+
+TEST(ServiceWorkerUtilsTest, FindLongestScopeMatch_Basic) {
+ LongestScopeMatcher matcher(GURL("http://www.example.com/xxx"));
+
+ // "/xx*" should be matched longest.
+ ASSERT_TRUE(matcher.MatchLongest(GURL("http://www.example.com/x*")));
+ ASSERT_FALSE(matcher.MatchLongest(GURL("http://www.example.com/*")));
+ ASSERT_TRUE(matcher.MatchLongest(GURL("http://www.example.com/xx*")));
+
+ // "xxx*" should be matched longer than "/xx*".
+ ASSERT_TRUE(matcher.MatchLongest(GURL("http://www.example.com/xxx*")));
+
+ ASSERT_FALSE(matcher.MatchLongest(GURL("http://www.example.com/xxxx*")));
+}
+
+TEST(ServiceWorkerUtilsTest, FindLongestScopeMatch_SameLength) {
+ LongestScopeMatcher matcher1(GURL("http://www.example.com/xxx"));
+
+ // "/xxx" has the same length with "/xx*", so they are compared as strings
+ // and "/xxx" should win.
+ // TODO(nhiroki): This isn't in the spec (see: service_worker_utils.cc)
+ ASSERT_TRUE(matcher1.MatchLongest(GURL("http://www.example.com/xxx")));
+ ASSERT_FALSE(matcher1.MatchLongest(GURL("http://www.example.com/xx*")));
+
+ LongestScopeMatcher matcher2(GURL("http://www.example.com/x(1)"));
+
+ // "/xx*" should be prioritized over "/x(1)".
+ // TODO(nhiroki): This isn't in the spec (see: service_worker_utils.cc)
+ ASSERT_TRUE(matcher2.MatchLongest(GURL("http://www.example.com/x(1)")));
+ ASSERT_TRUE(matcher2.MatchLongest(GURL("http://www.example.com/x(1*")));
+}
+
+} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_version.cc b/chromium/content/browser/service_worker/service_worker_version.cc
index a91ac6e9aa9..50f24df48b6 100644
--- a/chromium/content/browser/service_worker/service_worker_version.cc
+++ b/chromium/content/browser/service_worker/service_worker_version.cc
@@ -4,53 +4,634 @@
#include "content/browser/service_worker/service_worker_version.h"
+#include "base/command_line.h"
#include "base/stl_util.h"
+#include "base/strings/string16.h"
#include "content/browser/service_worker/embedded_worker_instance.h"
#include "content/browser/service_worker/embedded_worker_registry.h"
#include "content/browser/service_worker/service_worker_context_core.h"
#include "content/browser/service_worker/service_worker_registration.h"
+#include "content/browser/service_worker/service_worker_utils.h"
+#include "content/common/service_worker/service_worker_messages.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/common/content_switches.h"
namespace content {
+typedef ServiceWorkerVersion::StatusCallback StatusCallback;
+typedef ServiceWorkerVersion::MessageCallback MessageCallback;
+
+namespace {
+
+// Default delay to stop the worker context after all documents that
+// are associated to the worker are closed.
+// (Note that if all references to the version is dropped the worker
+// is also stopped without delay)
+const int64 kStopWorkerDelay = 30; // 30 secs.
+
+void RunSoon(const base::Closure& callback) {
+ if (!callback.is_null())
+ base::MessageLoop::current()->PostTask(FROM_HERE, callback);
+}
+
+template <typename CallbackArray, typename Arg>
+void RunCallbacks(ServiceWorkerVersion* version,
+ CallbackArray* callbacks_ptr,
+ const Arg& arg) {
+ CallbackArray callbacks;
+ callbacks.swap(*callbacks_ptr);
+ scoped_refptr<ServiceWorkerVersion> protect(version);
+ for (typename CallbackArray::const_iterator i = callbacks.begin();
+ i != callbacks.end(); ++i)
+ (*i).Run(arg);
+}
+
+template <typename IDMAP, typename Method, typename Params>
+void RunIDMapCallbacks(IDMAP* callbacks, Method method, const Params& params) {
+ typename IDMAP::iterator iter(callbacks);
+ while (!iter.IsAtEnd()) {
+ DispatchToMethod(iter.GetCurrentValue(), method, params);
+ iter.Advance();
+ }
+ callbacks->Clear();
+}
+
+// A callback adapter to start a |task| after StartWorker.
+void RunTaskAfterStartWorker(
+ base::WeakPtr<ServiceWorkerVersion> version,
+ const StatusCallback& error_callback,
+ const base::Closure& task,
+ ServiceWorkerStatusCode status) {
+ if (status != SERVICE_WORKER_OK) {
+ if (!error_callback.is_null())
+ error_callback.Run(status);
+ return;
+ }
+ if (version->running_status() != ServiceWorkerVersion::RUNNING) {
+ // We've tried to start the worker (and it has succeeded), but
+ // it looks it's not running yet.
+ NOTREACHED() << "The worker's not running after successful StartWorker";
+ if (!error_callback.is_null())
+ error_callback.Run(SERVICE_WORKER_ERROR_START_WORKER_FAILED);
+ return;
+ }
+ task.Run();
+}
+
+void RunErrorFetchCallback(const ServiceWorkerVersion::FetchCallback& callback,
+ ServiceWorkerStatusCode status) {
+ callback.Run(status,
+ SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK,
+ ServiceWorkerResponse());
+}
+
+} // namespace
+
ServiceWorkerVersion::ServiceWorkerVersion(
ServiceWorkerRegistration* registration,
- EmbeddedWorkerRegistry* worker_registry,
- int64 version_id)
+ int64 version_id,
+ base::WeakPtr<ServiceWorkerContextCore> context)
: version_id_(version_id),
- is_shutdown_(false),
- registration_(registration) {
- if (worker_registry)
- embedded_worker_ = worker_registry->CreateWorker();
+ registration_id_(kInvalidServiceWorkerVersionId),
+ status_(NEW),
+ context_(context),
+ script_cache_map_(this, context),
+ weak_factory_(this) {
+ DCHECK(context_);
+ DCHECK(registration);
+ if (registration) {
+ registration_id_ = registration->id();
+ script_url_ = registration->script_url();
+ scope_ = registration->pattern();
+ }
+ context_->AddLiveVersion(this);
+ embedded_worker_ = context_->embedded_worker_registry()->CreateWorker();
+ embedded_worker_->AddListener(this);
+}
+
+ServiceWorkerVersion::~ServiceWorkerVersion() {
+ embedded_worker_->RemoveListener(this);
+ if (context_)
+ context_->RemoveLiveVersion(version_id_);
+ // EmbeddedWorker's dtor sends StopWorker if it's still running.
+}
+
+void ServiceWorkerVersion::SetStatus(Status status) {
+ if (status_ == status)
+ return;
+
+ status_ = status;
+
+ std::vector<base::Closure> callbacks;
+ callbacks.swap(status_change_callbacks_);
+ for (std::vector<base::Closure>::const_iterator i = callbacks.begin();
+ i != callbacks.end(); ++i) {
+ (*i).Run();
+ }
+
+ FOR_EACH_OBSERVER(Listener, listeners_, OnVersionStateChanged(this));
+}
+
+void ServiceWorkerVersion::RegisterStatusChangeCallback(
+ const base::Closure& callback) {
+ status_change_callbacks_.push_back(callback);
+}
+
+ServiceWorkerVersionInfo ServiceWorkerVersion::GetInfo() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ return ServiceWorkerVersionInfo(
+ running_status(),
+ status(),
+ version_id(),
+ embedded_worker()->process_id(),
+ embedded_worker()->thread_id(),
+ embedded_worker()->worker_devtools_agent_route_id());
+}
+
+void ServiceWorkerVersion::StartWorker(const StatusCallback& callback) {
+ StartWorkerWithCandidateProcesses(std::vector<int>(), callback);
+}
+
+void ServiceWorkerVersion::StartWorkerWithCandidateProcesses(
+ const std::vector<int>& possible_process_ids,
+ const StatusCallback& callback) {
+ switch (running_status()) {
+ case RUNNING:
+ RunSoon(base::Bind(callback, SERVICE_WORKER_OK));
+ return;
+ case STOPPING:
+ RunSoon(base::Bind(callback, SERVICE_WORKER_ERROR_START_WORKER_FAILED));
+ return;
+ case STOPPED:
+ case STARTING:
+ start_callbacks_.push_back(callback);
+ if (running_status() == STOPPED) {
+ embedded_worker_->Start(
+ version_id_,
+ scope_,
+ script_url_,
+ possible_process_ids,
+ base::Bind(&ServiceWorkerVersion::RunStartWorkerCallbacksOnError,
+ weak_factory_.GetWeakPtr()));
+ }
+ return;
+ }
+}
+
+void ServiceWorkerVersion::StopWorker(const StatusCallback& callback) {
+ if (running_status() == STOPPED) {
+ RunSoon(base::Bind(callback, SERVICE_WORKER_OK));
+ return;
+ }
+ if (stop_callbacks_.empty()) {
+ ServiceWorkerStatusCode status = embedded_worker_->Stop();
+ if (status != SERVICE_WORKER_OK) {
+ RunSoon(base::Bind(callback, status));
+ return;
+ }
+ }
+ stop_callbacks_.push_back(callback);
+}
+
+void ServiceWorkerVersion::SendMessage(
+ const IPC::Message& message, const StatusCallback& callback) {
+ if (running_status() != RUNNING) {
+ // Schedule calling this method after starting the worker.
+ StartWorker(base::Bind(&RunTaskAfterStartWorker,
+ weak_factory_.GetWeakPtr(), callback,
+ base::Bind(&self::SendMessage,
+ weak_factory_.GetWeakPtr(),
+ message, callback)));
+ return;
+ }
+
+ ServiceWorkerStatusCode status = embedded_worker_->SendMessage(message);
+ RunSoon(base::Bind(callback, status));
+}
+
+void ServiceWorkerVersion::DispatchInstallEvent(
+ int active_version_id,
+ const StatusCallback& callback) {
+ DCHECK_EQ(NEW, status()) << status();
+ SetStatus(INSTALLING);
+
+ if (running_status() != RUNNING) {
+ // Schedule calling this method after starting the worker.
+ StartWorker(
+ base::Bind(&RunTaskAfterStartWorker,
+ weak_factory_.GetWeakPtr(),
+ callback,
+ base::Bind(&self::DispatchInstallEventAfterStartWorker,
+ weak_factory_.GetWeakPtr(),
+ active_version_id,
+ callback)));
+ } else {
+ DispatchInstallEventAfterStartWorker(active_version_id, callback);
+ }
+}
+
+void ServiceWorkerVersion::DispatchActivateEvent(
+ const StatusCallback& callback) {
+ DCHECK_EQ(INSTALLED, status()) << status();
+ SetStatus(ACTIVATING);
+
+ if (running_status() != RUNNING) {
+ // Schedule calling this method after starting the worker.
+ StartWorker(
+ base::Bind(&RunTaskAfterStartWorker,
+ weak_factory_.GetWeakPtr(),
+ callback,
+ base::Bind(&self::DispatchActivateEventAfterStartWorker,
+ weak_factory_.GetWeakPtr(),
+ callback)));
+ } else {
+ DispatchActivateEventAfterStartWorker(callback);
+ }
+}
+
+void ServiceWorkerVersion::DispatchFetchEvent(
+ const ServiceWorkerFetchRequest& request,
+ const FetchCallback& callback) {
+ DCHECK_EQ(ACTIVE, status()) << status();
+
+ if (running_status() != RUNNING) {
+ // Schedule calling this method after starting the worker.
+ StartWorker(base::Bind(&RunTaskAfterStartWorker,
+ weak_factory_.GetWeakPtr(),
+ base::Bind(&RunErrorFetchCallback, callback),
+ base::Bind(&self::DispatchFetchEvent,
+ weak_factory_.GetWeakPtr(),
+ request, callback)));
+ return;
+ }
+
+ int request_id = fetch_callbacks_.Add(new FetchCallback(callback));
+ ServiceWorkerStatusCode status = embedded_worker_->SendMessage(
+ ServiceWorkerMsg_FetchEvent(request_id, request));
+ if (status != SERVICE_WORKER_OK) {
+ fetch_callbacks_.Remove(request_id);
+ RunSoon(base::Bind(&RunErrorFetchCallback,
+ callback,
+ SERVICE_WORKER_ERROR_FAILED));
+ }
+}
+
+void ServiceWorkerVersion::DispatchSyncEvent(const StatusCallback& callback) {
+ DCHECK_EQ(ACTIVE, status()) << status();
+
+ if (!CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableServiceWorkerSync)) {
+ callback.Run(SERVICE_WORKER_ERROR_ABORT);
+ return;
+ }
+
+ if (running_status() != RUNNING) {
+ // Schedule calling this method after starting the worker.
+ StartWorker(base::Bind(&RunTaskAfterStartWorker,
+ weak_factory_.GetWeakPtr(), callback,
+ base::Bind(&self::DispatchSyncEvent,
+ weak_factory_.GetWeakPtr(),
+ callback)));
+ return;
+ }
+
+ int request_id = sync_callbacks_.Add(new StatusCallback(callback));
+ ServiceWorkerStatusCode status = embedded_worker_->SendMessage(
+ ServiceWorkerMsg_SyncEvent(request_id));
+ if (status != SERVICE_WORKER_OK) {
+ sync_callbacks_.Remove(request_id);
+ RunSoon(base::Bind(callback, status));
+ }
+}
+
+void ServiceWorkerVersion::DispatchPushEvent(const StatusCallback& callback,
+ const std::string& data) {
+ DCHECK_EQ(ACTIVE, status()) << status();
+
+ if (!CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableExperimentalWebPlatformFeatures)) {
+ callback.Run(SERVICE_WORKER_ERROR_ABORT);
+ return;
+ }
+
+ if (running_status() != RUNNING) {
+ // Schedule calling this method after starting the worker.
+ StartWorker(base::Bind(&RunTaskAfterStartWorker,
+ weak_factory_.GetWeakPtr(), callback,
+ base::Bind(&self::DispatchPushEvent,
+ weak_factory_.GetWeakPtr(),
+ callback, data)));
+ return;
+ }
+
+ int request_id = push_callbacks_.Add(new StatusCallback(callback));
+ ServiceWorkerStatusCode status = embedded_worker_->SendMessage(
+ ServiceWorkerMsg_PushEvent(request_id, data));
+ if (status != SERVICE_WORKER_OK) {
+ push_callbacks_.Remove(request_id);
+ RunSoon(base::Bind(callback, status));
+ }
}
-ServiceWorkerVersion::~ServiceWorkerVersion() { DCHECK(is_shutdown_); }
+void ServiceWorkerVersion::AddProcessToWorker(int process_id) {
+ embedded_worker_->AddProcessReference(process_id);
+}
-void ServiceWorkerVersion::Shutdown() {
- is_shutdown_ = true;
- registration_ = NULL;
- embedded_worker_.reset();
+void ServiceWorkerVersion::RemoveProcessFromWorker(int process_id) {
+ embedded_worker_->ReleaseProcessReference(process_id);
}
-void ServiceWorkerVersion::StartWorker() {
- DCHECK(!is_shutdown_);
- DCHECK(registration_);
- embedded_worker_->Start(version_id_, registration_->script_url());
+bool ServiceWorkerVersion::HasProcessToRun() const {
+ return embedded_worker_->HasProcessToRun();
}
-void ServiceWorkerVersion::StopWorker() {
- DCHECK(!is_shutdown_);
- embedded_worker_->Stop();
+void ServiceWorkerVersion::AddControllee(
+ ServiceWorkerProviderHost* provider_host) {
+ DCHECK(!ContainsKey(controllee_map_, provider_host));
+ int controllee_id = controllee_by_id_.Add(provider_host);
+ controllee_map_[provider_host] = controllee_id;
+ AddProcessToWorker(provider_host->process_id());
+ if (stop_worker_timer_.IsRunning())
+ stop_worker_timer_.Stop();
+}
+
+void ServiceWorkerVersion::RemoveControllee(
+ ServiceWorkerProviderHost* provider_host) {
+ ControlleeMap::iterator found = controllee_map_.find(provider_host);
+ DCHECK(found != controllee_map_.end());
+ controllee_by_id_.Remove(found->second);
+ controllee_map_.erase(found);
+ RemoveProcessFromWorker(provider_host->process_id());
+ if (!HasControllee())
+ ScheduleStopWorker();
+ // TODO(kinuko): Fire NoControllees notification when the # of controllees
+ // reaches 0, so that a new pending version can be activated (which will
+ // deactivate this version).
+ // TODO(michaeln): On no controllees call storage DeleteVersionResources
+ // if this version has been deactivated. Probably storage can listen for
+ // NoControllees for versions that have been deleted.
}
-void ServiceWorkerVersion::OnAssociateProvider(
+void ServiceWorkerVersion::AddWaitingControllee(
ServiceWorkerProviderHost* provider_host) {
- DCHECK(!is_shutdown_);
- embedded_worker_->AddProcessReference(provider_host->process_id());
+ AddProcessToWorker(provider_host->process_id());
}
-void ServiceWorkerVersion::OnUnassociateProvider(
+void ServiceWorkerVersion::RemoveWaitingControllee(
ServiceWorkerProviderHost* provider_host) {
- embedded_worker_->ReleaseProcessReference(provider_host->process_id());
+ RemoveProcessFromWorker(provider_host->process_id());
+}
+
+void ServiceWorkerVersion::AddListener(Listener* listener) {
+ listeners_.AddObserver(listener);
+}
+
+void ServiceWorkerVersion::RemoveListener(Listener* listener) {
+ listeners_.RemoveObserver(listener);
+}
+
+void ServiceWorkerVersion::OnStarted() {
+ DCHECK_EQ(RUNNING, running_status());
+ // Fire all start callbacks.
+ RunCallbacks(this, &start_callbacks_, SERVICE_WORKER_OK);
+ FOR_EACH_OBSERVER(Listener, listeners_, OnWorkerStarted(this));
+}
+
+void ServiceWorkerVersion::OnStopped() {
+ DCHECK_EQ(STOPPED, running_status());
+ scoped_refptr<ServiceWorkerVersion> protect(this);
+
+ // Fire all stop callbacks.
+ RunCallbacks(this, &stop_callbacks_, SERVICE_WORKER_OK);
+
+ // Let all start callbacks fail.
+ RunCallbacks(
+ this, &start_callbacks_, SERVICE_WORKER_ERROR_START_WORKER_FAILED);
+
+ // Let all message callbacks fail (this will also fire and clear all
+ // callbacks for events).
+ // TODO(kinuko): Consider if we want to add queue+resend mechanism here.
+ RunIDMapCallbacks(&activate_callbacks_,
+ &StatusCallback::Run,
+ MakeTuple(SERVICE_WORKER_ERROR_ACTIVATE_WORKER_FAILED));
+ RunIDMapCallbacks(&install_callbacks_,
+ &StatusCallback::Run,
+ MakeTuple(SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED));
+ RunIDMapCallbacks(&fetch_callbacks_,
+ &FetchCallback::Run,
+ MakeTuple(SERVICE_WORKER_ERROR_FAILED,
+ SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK,
+ ServiceWorkerResponse()));
+ RunIDMapCallbacks(&sync_callbacks_,
+ &StatusCallback::Run,
+ MakeTuple(SERVICE_WORKER_ERROR_FAILED));
+ RunIDMapCallbacks(&push_callbacks_,
+ &StatusCallback::Run,
+ MakeTuple(SERVICE_WORKER_ERROR_FAILED));
+
+ FOR_EACH_OBSERVER(Listener, listeners_, OnWorkerStopped(this));
+}
+
+void ServiceWorkerVersion::OnReportException(
+ const base::string16& error_message,
+ int line_number,
+ int column_number,
+ const GURL& source_url) {
+ FOR_EACH_OBSERVER(
+ Listener,
+ listeners_,
+ OnErrorReported(
+ this, error_message, line_number, column_number, source_url));
+}
+
+void ServiceWorkerVersion::OnReportConsoleMessage(int source_identifier,
+ int message_level,
+ const base::string16& message,
+ int line_number,
+ const GURL& source_url) {
+ FOR_EACH_OBSERVER(Listener,
+ listeners_,
+ OnReportConsoleMessage(this,
+ source_identifier,
+ message_level,
+ message,
+ line_number,
+ source_url));
+}
+
+bool ServiceWorkerVersion::OnMessageReceived(const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(ServiceWorkerVersion, message)
+ IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_GetClientDocuments,
+ OnGetClientDocuments)
+ IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_ActivateEventFinished,
+ OnActivateEventFinished)
+ IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_InstallEventFinished,
+ OnInstallEventFinished)
+ IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_FetchEventFinished,
+ OnFetchEventFinished)
+ IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_SyncEventFinished,
+ OnSyncEventFinished)
+ IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_PushEventFinished,
+ OnPushEventFinished)
+ IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_PostMessageToDocument,
+ OnPostMessageToDocument)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void ServiceWorkerVersion::RunStartWorkerCallbacksOnError(
+ ServiceWorkerStatusCode status) {
+ if (status != SERVICE_WORKER_OK)
+ RunCallbacks(this, &start_callbacks_, status);
+}
+
+void ServiceWorkerVersion::DispatchInstallEventAfterStartWorker(
+ int active_version_id,
+ const StatusCallback& callback) {
+ DCHECK_EQ(RUNNING, running_status())
+ << "Worker stopped too soon after it was started.";
+ int request_id = install_callbacks_.Add(new StatusCallback(callback));
+ ServiceWorkerStatusCode status = embedded_worker_->SendMessage(
+ ServiceWorkerMsg_InstallEvent(request_id, active_version_id));
+ if (status != SERVICE_WORKER_OK) {
+ install_callbacks_.Remove(request_id);
+ RunSoon(base::Bind(callback, status));
+ }
+}
+
+void ServiceWorkerVersion::DispatchActivateEventAfterStartWorker(
+ const StatusCallback& callback) {
+ DCHECK_EQ(RUNNING, running_status())
+ << "Worker stopped too soon after it was started.";
+ int request_id = activate_callbacks_.Add(new StatusCallback(callback));
+ ServiceWorkerStatusCode status =
+ embedded_worker_->SendMessage(ServiceWorkerMsg_ActivateEvent(request_id));
+ if (status != SERVICE_WORKER_OK) {
+ activate_callbacks_.Remove(request_id);
+ RunSoon(base::Bind(callback, status));
+ }
+}
+
+void ServiceWorkerVersion::OnGetClientDocuments(int request_id) {
+ std::vector<int> client_ids;
+ ControlleeByIDMap::iterator it(&controllee_by_id_);
+ while (!it.IsAtEnd()) {
+ client_ids.push_back(it.GetCurrentKey());
+ it.Advance();
+ }
+ // Don't bother if it's no longer running.
+ if (running_status() == RUNNING) {
+ embedded_worker_->SendMessage(
+ ServiceWorkerMsg_DidGetClientDocuments(request_id, client_ids));
+ }
+}
+
+void ServiceWorkerVersion::OnActivateEventFinished(
+ int request_id,
+ blink::WebServiceWorkerEventResult result) {
+ StatusCallback* callback = activate_callbacks_.Lookup(request_id);
+ if (!callback) {
+ NOTREACHED() << "Got unexpected message: " << request_id;
+ return;
+ }
+ ServiceWorkerStatusCode status = SERVICE_WORKER_OK;
+ if (result == blink::WebServiceWorkerEventResultRejected)
+ status = SERVICE_WORKER_ERROR_ACTIVATE_WORKER_FAILED;
+ else
+ SetStatus(ACTIVE);
+
+ scoped_refptr<ServiceWorkerVersion> protect(this);
+ callback->Run(status);
+ activate_callbacks_.Remove(request_id);
+}
+
+void ServiceWorkerVersion::OnInstallEventFinished(
+ int request_id,
+ blink::WebServiceWorkerEventResult result) {
+ StatusCallback* callback = install_callbacks_.Lookup(request_id);
+ if (!callback) {
+ NOTREACHED() << "Got unexpected message: " << request_id;
+ return;
+ }
+ ServiceWorkerStatusCode status = SERVICE_WORKER_OK;
+ if (result == blink::WebServiceWorkerEventResultRejected)
+ status = SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED;
+ else
+ SetStatus(INSTALLED);
+
+ scoped_refptr<ServiceWorkerVersion> protect(this);
+ callback->Run(status);
+ install_callbacks_.Remove(request_id);
+}
+
+void ServiceWorkerVersion::OnFetchEventFinished(
+ int request_id,
+ ServiceWorkerFetchEventResult result,
+ const ServiceWorkerResponse& response) {
+ FetchCallback* callback = fetch_callbacks_.Lookup(request_id);
+ if (!callback) {
+ NOTREACHED() << "Got unexpected message: " << request_id;
+ return;
+ }
+
+ scoped_refptr<ServiceWorkerVersion> protect(this);
+ callback->Run(SERVICE_WORKER_OK, result, response);
+ fetch_callbacks_.Remove(request_id);
+}
+
+void ServiceWorkerVersion::OnSyncEventFinished(
+ int request_id) {
+ StatusCallback* callback = sync_callbacks_.Lookup(request_id);
+ if (!callback) {
+ NOTREACHED() << "Got unexpected message: " << request_id;
+ return;
+ }
+
+ scoped_refptr<ServiceWorkerVersion> protect(this);
+ callback->Run(SERVICE_WORKER_OK);
+ sync_callbacks_.Remove(request_id);
+}
+
+void ServiceWorkerVersion::OnPushEventFinished(
+ int request_id) {
+ StatusCallback* callback = push_callbacks_.Lookup(request_id);
+ if (!callback) {
+ NOTREACHED() << "Got unexpected message: " << request_id;
+ return;
+ }
+
+ scoped_refptr<ServiceWorkerVersion> protect(this);
+ callback->Run(SERVICE_WORKER_OK);
+ push_callbacks_.Remove(request_id);
+}
+
+void ServiceWorkerVersion::OnPostMessageToDocument(
+ int client_id,
+ const base::string16& message,
+ const std::vector<int>& sent_message_port_ids) {
+ ServiceWorkerProviderHost* provider_host =
+ controllee_by_id_.Lookup(client_id);
+ if (!provider_host) {
+ // The client may already have been closed, just ignore.
+ return;
+ }
+ provider_host->PostMessage(message, sent_message_port_ids);
+}
+
+void ServiceWorkerVersion::ScheduleStopWorker() {
+ if (running_status() != RUNNING)
+ return;
+ if (stop_worker_timer_.IsRunning()) {
+ stop_worker_timer_.Reset();
+ return;
+ }
+ stop_worker_timer_.Start(
+ FROM_HERE, base::TimeDelta::FromSeconds(kStopWorkerDelay),
+ base::Bind(&ServiceWorkerVersion::StopWorker,
+ weak_factory_.GetWeakPtr(),
+ base::Bind(&ServiceWorkerUtils::NoOpStatusCallback)));
}
} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_version.h b/chromium/content/browser/service_worker/service_worker_version.h
index 46b75bd6866..0c7bda50775 100644
--- a/chromium/content/browser/service_worker/service_worker_version.h
+++ b/chromium/content/browser/service_worker/service_worker_version.h
@@ -5,86 +5,280 @@
#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_VERSION_H_
#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_VERSION_H_
+#include <string>
+#include <vector>
+
#include "base/basictypes.h"
+#include "base/callback.h"
#include "base/gtest_prod_util.h"
+#include "base/id_map.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
+#include "base/observer_list.h"
+#include "base/timer/timer.h"
+#include "content/browser/service_worker/embedded_worker_instance.h"
+#include "content/browser/service_worker/service_worker_script_cache_map.h"
#include "content/common/content_export.h"
+#include "content/common/service_worker/service_worker_status_code.h"
+#include "content/common/service_worker/service_worker_types.h"
+#include "third_party/WebKit/public/platform/WebServiceWorkerEventResult.h"
class GURL;
namespace content {
-class EmbeddedWorkerInstance;
class EmbeddedWorkerRegistry;
+class ServiceWorkerContextCore;
class ServiceWorkerProviderHost;
class ServiceWorkerRegistration;
+class ServiceWorkerVersionInfo;
// This class corresponds to a specific version of a ServiceWorker
// script for a given pattern. When a script is upgraded, there may be
// more than one ServiceWorkerVersion "running" at a time, but only
// one of them is active. This class connects the actual script with a
// running worker.
-// Instances of this class are in one of two install states:
-// - Pending: The script is in the process of being installed. There
-// may be another active script running.
-// - Active: The script is the only worker handling requests for the
-// registration's pattern.
-//
-// In addition, a version has a running state (this is a rough
-// sketch). Since a service worker can be stopped and started at any
-// time, it will transition among these states multiple times during
-// its lifetime.
-// - Stopped: The script is not running
-// - Starting: A request to fire an event against the version has been
-// queued, but the worker is not yet
-// loaded/initialized/etc.
-// - Started: The worker is ready to receive events
-// - Stopping: The worker is returning to the stopped state.
//
-// The worker can "run" in both the Pending and the Active
-// install states above. During the Pending state, the worker is only
-// started in order to fire the 'install' and 'activate'
-// events. During the Active state, it can receive other events such
-// as 'fetch'.
-//
-// And finally, is_shutdown_ is detects the live-ness of the object
-// itself. If the object is shut down, then it is in the process of
-// being deleted from memory. This happens when a version is replaced
-// as well as at browser shutdown.
+// is_shutdown_ detects the live-ness of the object itself. If the object is
+// shut down, then it is in the process of being deleted from memory.
+// This happens when a version is replaced as well as at browser shutdown.
class CONTENT_EXPORT ServiceWorkerVersion
- : NON_EXPORTED_BASE(public base::RefCounted<ServiceWorkerVersion>) {
+ : NON_EXPORTED_BASE(public base::RefCounted<ServiceWorkerVersion>),
+ public EmbeddedWorkerInstance::Listener {
public:
+ typedef base::Callback<void(ServiceWorkerStatusCode)> StatusCallback;
+ typedef base::Callback<void(ServiceWorkerStatusCode,
+ const IPC::Message& message)> MessageCallback;
+ typedef base::Callback<void(ServiceWorkerStatusCode,
+ ServiceWorkerFetchEventResult,
+ const ServiceWorkerResponse&)> FetchCallback;
+
+ enum RunningStatus {
+ STOPPED = EmbeddedWorkerInstance::STOPPED,
+ STARTING = EmbeddedWorkerInstance::STARTING,
+ RUNNING = EmbeddedWorkerInstance::RUNNING,
+ STOPPING = EmbeddedWorkerInstance::STOPPING,
+ };
+
+ // Current version status; some of the status (e.g. INSTALLED and ACTIVE)
+ // should be persisted unlike running status.
+ enum Status {
+ NEW, // The version is just created.
+ INSTALLING, // Install event is dispatched and being handled.
+ INSTALLED, // Install event is finished and is ready to be activated.
+ ACTIVATING, // Activate event is dispatched and being handled.
+ ACTIVE, // Activation is finished and can run as active.
+ DEACTIVATED, // The version is no longer running as active, due to
+ // unregistration or replace. (TODO(kinuko): we may need
+ // different states for different termination sequences)
+ };
+
+ class Listener {
+ public:
+ virtual void OnWorkerStarted(ServiceWorkerVersion* version) = 0;
+ virtual void OnWorkerStopped(ServiceWorkerVersion* version) = 0;
+ virtual void OnVersionStateChanged(ServiceWorkerVersion* version) = 0;
+ virtual void OnErrorReported(ServiceWorkerVersion* version,
+ const base::string16& error_message,
+ int line_number,
+ int column_number,
+ const GURL& source_url) = 0;
+ virtual void OnReportConsoleMessage(ServiceWorkerVersion* version,
+ int source_identifier,
+ int message_level,
+ const base::string16& message,
+ int line_number,
+ const GURL& source_url) = 0;
+ };
+
ServiceWorkerVersion(
ServiceWorkerRegistration* registration,
- EmbeddedWorkerRegistry* worker_registry,
- int64 version_id);
+ int64 version_id,
+ base::WeakPtr<ServiceWorkerContextCore> context);
int64 version_id() const { return version_id_; }
+ int64 registration_id() const { return registration_id_; }
+ const GURL& script_url() const { return script_url_; }
+ const GURL& scope() const { return scope_; }
+ RunningStatus running_status() const {
+ return static_cast<RunningStatus>(embedded_worker_->status());
+ }
+ ServiceWorkerVersionInfo GetInfo();
+ Status status() const { return status_; }
+
+ // This sets the new status and also run status change callbacks
+ // if there're any (see RegisterStatusChangeCallback).
+ void SetStatus(Status status);
+
+ // Registers status change callback. (This is for one-off observation,
+ // the consumer needs to re-register if it wants to continue observing
+ // status changes)
+ void RegisterStatusChangeCallback(const base::Closure& callback);
+
+ // Starts an embedded worker for this version.
+ // This returns OK (success) if the worker is already running.
+ void StartWorker(const StatusCallback& callback);
+
+ // Starts an embedded worker for this version.
+ // |potential_process_ids| is a list of processes in which to start the
+ // worker.
+ // This returns OK (success) if the worker is already running.
+ void StartWorkerWithCandidateProcesses(
+ const std::vector<int>& potential_process_ids,
+ const StatusCallback& callback);
+
+ // Starts an embedded worker for this version.
+ // This returns OK (success) if the worker is already stopped.
+ void StopWorker(const StatusCallback& callback);
+
+ // Sends an IPC message to the worker.
+ // If the worker is not running this first tries to start it by
+ // calling StartWorker internally.
+ // |callback| can be null if the sender does not need to know if the
+ // message is successfully sent or not.
+ void SendMessage(const IPC::Message& message, const StatusCallback& callback);
+
+ // Sends install event to the associated embedded worker and asynchronously
+ // calls |callback| when it errors out or it gets response from the worker
+ // to notify install completion.
+ // |active_version_id| must be a valid positive ID
+ // if there's an active (previous) version running.
+ //
+ // This must be called when the status() is NEW. Calling this changes
+ // the version's status to INSTALLING.
+ // Upon completion, the version's status will be changed to INSTALLED
+ // on success, or back to NEW on failure.
+ void DispatchInstallEvent(int active_version_id,
+ const StatusCallback& callback);
+
+ // Sends activate event to the associated embedded worker and asynchronously
+ // calls |callback| when it errors out or it gets response from the worker
+ // to notify activation completion.
+ //
+ // This must be called when the status() is INSTALLED. Calling this changes
+ // the version's status to ACTIVATING.
+ // Upon completion, the version's status will be changed to ACTIVE
+ // on success, or back to INSTALLED on failure.
+ void DispatchActivateEvent(const StatusCallback& callback);
- void Shutdown();
- bool is_shutdown() const { return is_shutdown_; }
+ // Sends fetch event to the associated embedded worker and calls
+ // |callback| with the response from the worker.
+ //
+ // This must be called when the status() is ACTIVE. Calling this in other
+ // statuses will result in an error SERVICE_WORKER_ERROR_FAILED.
+ void DispatchFetchEvent(const ServiceWorkerFetchRequest& request,
+ const FetchCallback& callback);
- // Starts and stops an embedded worker for this version.
- void StartWorker();
- void StopWorker();
+ // Sends sync event to the associated embedded worker and asynchronously calls
+ // |callback| when it errors out or it gets response from the worker to notify
+ // completion.
+ //
+ // This must be called when the status() is ACTIVE.
+ void DispatchSyncEvent(const StatusCallback& callback);
- // Called when this version is associated to a provider host.
- // Non-null |provider_host| must be given.
- void OnAssociateProvider(ServiceWorkerProviderHost* provider_host);
- void OnUnassociateProvider(ServiceWorkerProviderHost* provider_host);
+ // Sends push event to the associated embedded worker and asynchronously calls
+ // |callback| when it errors out or it gets response from the worker to notify
+ // completion.
+ //
+ // This must be called when the status() is ACTIVE.
+ void DispatchPushEvent(const StatusCallback& callback,
+ const std::string& data);
+
+ // These are expected to be called when a renderer process host for the
+ // same-origin as for this ServiceWorkerVersion is created. The added
+ // processes are used to run an in-renderer embedded worker.
+ void AddProcessToWorker(int process_id);
+ void RemoveProcessFromWorker(int process_id);
+
+ // Returns true if this has at least one process to run.
+ bool HasProcessToRun() const;
+
+ // Adds and removes |provider_host| as a controllee of this ServiceWorker.
+ void AddControllee(ServiceWorkerProviderHost* provider_host);
+ void RemoveControllee(ServiceWorkerProviderHost* provider_host);
+ void AddWaitingControllee(ServiceWorkerProviderHost* provider_host);
+ void RemoveWaitingControllee(ServiceWorkerProviderHost* provider_host);
+
+ // Returns if it has controllee.
+ bool HasControllee() const { return !controllee_map_.empty(); }
+
+ // Adds and removes Listeners.
+ void AddListener(Listener* listener);
+ void RemoveListener(Listener* listener);
+
+ ServiceWorkerScriptCacheMap* script_cache_map() { return &script_cache_map_; }
+ EmbeddedWorkerInstance* embedded_worker() { return embedded_worker_.get(); }
private:
+ typedef ServiceWorkerVersion self;
+ typedef std::map<ServiceWorkerProviderHost*, int> ControlleeMap;
+ typedef IDMap<ServiceWorkerProviderHost> ControlleeByIDMap;
friend class base::RefCounted<ServiceWorkerVersion>;
- ~ServiceWorkerVersion();
+ virtual ~ServiceWorkerVersion();
- const int64 version_id_;
+ // EmbeddedWorkerInstance::Listener overrides:
+ virtual void OnStarted() OVERRIDE;
+ virtual void OnStopped() OVERRIDE;
+ virtual void OnReportException(const base::string16& error_message,
+ int line_number,
+ int column_number,
+ const GURL& source_url) OVERRIDE;
+ virtual void OnReportConsoleMessage(int source_identifier,
+ int message_level,
+ const base::string16& message,
+ int line_number,
+ const GURL& source_url) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
- bool is_shutdown_;
- scoped_refptr<ServiceWorkerRegistration> registration_;
+ void RunStartWorkerCallbacksOnError(ServiceWorkerStatusCode status);
+ void DispatchInstallEventAfterStartWorker(int active_version_id,
+ const StatusCallback& callback);
+ void DispatchActivateEventAfterStartWorker(const StatusCallback& callback);
+
+ // Message handlers.
+ void OnGetClientDocuments(int request_id);
+ void OnActivateEventFinished(int request_id,
+ blink::WebServiceWorkerEventResult result);
+ void OnInstallEventFinished(int request_id,
+ blink::WebServiceWorkerEventResult result);
+ void OnFetchEventFinished(int request_id,
+ ServiceWorkerFetchEventResult result,
+ const ServiceWorkerResponse& response);
+ void OnSyncEventFinished(int request_id);
+ void OnPushEventFinished(int request_id);
+ void OnPostMessageToDocument(int client_id,
+ const base::string16& message,
+ const std::vector<int>& sent_message_port_ids);
+
+ void ScheduleStopWorker();
+
+ const int64 version_id_;
+ int64 registration_id_;
+ GURL script_url_;
+ GURL scope_;
+ Status status_;
scoped_ptr<EmbeddedWorkerInstance> embedded_worker_;
+ std::vector<StatusCallback> start_callbacks_;
+ std::vector<StatusCallback> stop_callbacks_;
+ std::vector<base::Closure> status_change_callbacks_;
+
+ // Message callbacks.
+ IDMap<StatusCallback, IDMapOwnPointer> activate_callbacks_;
+ IDMap<StatusCallback, IDMapOwnPointer> install_callbacks_;
+ IDMap<FetchCallback, IDMapOwnPointer> fetch_callbacks_;
+ IDMap<StatusCallback, IDMapOwnPointer> sync_callbacks_;
+ IDMap<StatusCallback, IDMapOwnPointer> push_callbacks_;
+
+ ControlleeMap controllee_map_;
+ ControlleeByIDMap controllee_by_id_;
+ base::WeakPtr<ServiceWorkerContextCore> context_;
+ ObserverList<Listener> listeners_;
+ ServiceWorkerScriptCacheMap script_cache_map_;
+ base::OneShotTimer<ServiceWorkerVersion> stop_worker_timer_;
+
+ base::WeakPtrFactory<ServiceWorkerVersion> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(ServiceWorkerVersion);
};
diff --git a/chromium/content/browser/service_worker/service_worker_version_unittest.cc b/chromium/content/browser/service_worker/service_worker_version_unittest.cc
new file mode 100644
index 00000000000..6622e5aba83
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_version_unittest.cc
@@ -0,0 +1,355 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "base/run_loop.h"
+#include "content/browser/service_worker/embedded_worker_registry.h"
+#include "content/browser/service_worker/embedded_worker_test_helper.h"
+#include "content/browser/service_worker/service_worker_context_core.h"
+#include "content/browser/service_worker/service_worker_registration.h"
+#include "content/browser/service_worker/service_worker_test_utils.h"
+#include "content/browser/service_worker/service_worker_version.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// IPC messages for testing ---------------------------------------------------
+
+#define IPC_MESSAGE_IMPL
+#include "ipc/ipc_message_macros.h"
+
+#define IPC_MESSAGE_START TestMsgStart
+
+IPC_MESSAGE_CONTROL0(TestMsg_Message);
+IPC_MESSAGE_ROUTED1(TestMsg_MessageFromWorker, int);
+
+// ---------------------------------------------------------------------------
+
+namespace content {
+
+namespace {
+
+static const int kRenderProcessId = 1;
+
+class MessageReceiver : public EmbeddedWorkerTestHelper {
+ public:
+ MessageReceiver()
+ : EmbeddedWorkerTestHelper(kRenderProcessId),
+ current_embedded_worker_id_(0) {}
+ virtual ~MessageReceiver() {}
+
+ virtual bool OnMessageToWorker(int thread_id,
+ int embedded_worker_id,
+ const IPC::Message& message) OVERRIDE {
+ if (EmbeddedWorkerTestHelper::OnMessageToWorker(
+ thread_id, embedded_worker_id, message)) {
+ return true;
+ }
+ current_embedded_worker_id_ = embedded_worker_id;
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(MessageReceiver, message)
+ IPC_MESSAGE_HANDLER(TestMsg_Message, OnMessage)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+ }
+
+ void SimulateSendValueToBrowser(int embedded_worker_id, int value) {
+ SimulateSend(new TestMsg_MessageFromWorker(embedded_worker_id, value));
+ }
+
+ private:
+ void OnMessage() {
+ // Do nothing.
+ }
+
+ int current_embedded_worker_id_;
+ DISALLOW_COPY_AND_ASSIGN(MessageReceiver);
+};
+
+void VerifyCalled(bool* called) {
+ *called = true;
+}
+
+void ObserveStatusChanges(ServiceWorkerVersion* version,
+ std::vector<ServiceWorkerVersion::Status>* statuses) {
+ statuses->push_back(version->status());
+ version->RegisterStatusChangeCallback(
+ base::Bind(&ObserveStatusChanges, base::Unretained(version), statuses));
+}
+
+// A specialized listener class to receive test messages from a worker.
+class MessageReceiverFromWorker : public EmbeddedWorkerInstance::Listener {
+ public:
+ explicit MessageReceiverFromWorker(EmbeddedWorkerInstance* instance)
+ : instance_(instance) {
+ instance_->AddListener(this);
+ }
+ virtual ~MessageReceiverFromWorker() {
+ instance_->RemoveListener(this);
+ }
+
+ virtual void OnStarted() OVERRIDE { NOTREACHED(); }
+ virtual void OnStopped() OVERRIDE { NOTREACHED(); }
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(MessageReceiverFromWorker, message)
+ IPC_MESSAGE_HANDLER(TestMsg_MessageFromWorker, OnMessageFromWorker)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+ }
+
+ void OnMessageFromWorker(int value) { received_values_.push_back(value); }
+ const std::vector<int>& received_values() const { return received_values_; }
+
+ private:
+ EmbeddedWorkerInstance* instance_;
+ std::vector<int> received_values_;
+ DISALLOW_COPY_AND_ASSIGN(MessageReceiverFromWorker);
+};
+
+} // namespace
+
+class ServiceWorkerVersionTest : public testing::Test {
+ protected:
+ ServiceWorkerVersionTest()
+ : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {}
+
+ virtual void SetUp() OVERRIDE {
+ helper_.reset(new MessageReceiver());
+
+ registration_ = new ServiceWorkerRegistration(
+ GURL("http://www.example.com/*"),
+ GURL("http://www.example.com/service_worker.js"),
+ 1L,
+ helper_->context()->AsWeakPtr());
+ version_ = new ServiceWorkerVersion(
+ registration_, 1L, helper_->context()->AsWeakPtr());
+
+ // Simulate adding one process to the worker.
+ int embedded_worker_id = version_->embedded_worker()->embedded_worker_id();
+ helper_->SimulateAddProcessToWorker(embedded_worker_id, kRenderProcessId);
+ ASSERT_TRUE(version_->HasProcessToRun());
+ }
+
+ virtual void TearDown() OVERRIDE {
+ version_ = 0;
+ registration_ = 0;
+ helper_.reset();
+ }
+
+ TestBrowserThreadBundle thread_bundle_;
+ scoped_ptr<MessageReceiver> helper_;
+ scoped_refptr<ServiceWorkerRegistration> registration_;
+ scoped_refptr<ServiceWorkerVersion> version_;
+ DISALLOW_COPY_AND_ASSIGN(ServiceWorkerVersionTest);
+};
+
+TEST_F(ServiceWorkerVersionTest, ConcurrentStartAndStop) {
+ // Call StartWorker() multiple times.
+ ServiceWorkerStatusCode status1 = SERVICE_WORKER_ERROR_FAILED;
+ ServiceWorkerStatusCode status2 = SERVICE_WORKER_ERROR_FAILED;
+ ServiceWorkerStatusCode status3 = SERVICE_WORKER_ERROR_FAILED;
+ version_->StartWorker(CreateReceiverOnCurrentThread(&status1));
+ version_->StartWorker(CreateReceiverOnCurrentThread(&status2));
+
+ EXPECT_EQ(ServiceWorkerVersion::STARTING, version_->running_status());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(ServiceWorkerVersion::RUNNING, version_->running_status());
+
+ // Call StartWorker() after it's started.
+ version_->StartWorker(CreateReceiverOnCurrentThread(&status3));
+ base::RunLoop().RunUntilIdle();
+
+ // All should just succeed.
+ EXPECT_EQ(SERVICE_WORKER_OK, status1);
+ EXPECT_EQ(SERVICE_WORKER_OK, status2);
+ EXPECT_EQ(SERVICE_WORKER_OK, status3);
+
+ // Call StopWorker() multiple times.
+ status1 = SERVICE_WORKER_ERROR_FAILED;
+ status2 = SERVICE_WORKER_ERROR_FAILED;
+ status3 = SERVICE_WORKER_ERROR_FAILED;
+ version_->StopWorker(CreateReceiverOnCurrentThread(&status1));
+ version_->StopWorker(CreateReceiverOnCurrentThread(&status2));
+
+ // Also try calling StartWorker while StopWorker is in queue.
+ version_->StartWorker(CreateReceiverOnCurrentThread(&status3));
+
+ EXPECT_EQ(ServiceWorkerVersion::STOPPING, version_->running_status());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(ServiceWorkerVersion::STOPPED, version_->running_status());
+
+ // All StopWorker should just succeed, while StartWorker fails.
+ EXPECT_EQ(SERVICE_WORKER_OK, status1);
+ EXPECT_EQ(SERVICE_WORKER_OK, status2);
+ EXPECT_EQ(SERVICE_WORKER_ERROR_START_WORKER_FAILED, status3);
+}
+
+TEST_F(ServiceWorkerVersionTest, SendMessage) {
+ EXPECT_EQ(ServiceWorkerVersion::STOPPED, version_->running_status());
+
+ // Send a message without starting the worker.
+ ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
+ version_->SendMessage(TestMsg_Message(),
+ CreateReceiverOnCurrentThread(&status));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(SERVICE_WORKER_OK, status);
+
+ // The worker should be now started.
+ EXPECT_EQ(ServiceWorkerVersion::RUNNING, version_->running_status());
+
+ // Stop the worker, and then send the message immediately.
+ ServiceWorkerStatusCode msg_status = SERVICE_WORKER_ERROR_FAILED;
+ ServiceWorkerStatusCode stop_status = SERVICE_WORKER_ERROR_FAILED;
+ version_->StopWorker(CreateReceiverOnCurrentThread(&stop_status));
+ version_->SendMessage(TestMsg_Message(),
+ CreateReceiverOnCurrentThread(&msg_status));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(SERVICE_WORKER_OK, stop_status);
+
+ // SendMessage should return START_WORKER_FAILED error since it tried to
+ // start a worker while it was stopping.
+ EXPECT_EQ(SERVICE_WORKER_ERROR_START_WORKER_FAILED, msg_status);
+}
+
+TEST_F(ServiceWorkerVersionTest, ReSendMessageAfterStop) {
+ EXPECT_EQ(ServiceWorkerVersion::STOPPED, version_->running_status());
+
+ // Start the worker.
+ ServiceWorkerStatusCode start_status = SERVICE_WORKER_ERROR_FAILED;
+ version_->StartWorker(CreateReceiverOnCurrentThread(&start_status));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(SERVICE_WORKER_OK, start_status);
+ EXPECT_EQ(ServiceWorkerVersion::RUNNING, version_->running_status());
+
+ // Stop the worker, and then send the message immediately.
+ ServiceWorkerStatusCode msg_status = SERVICE_WORKER_ERROR_FAILED;
+ ServiceWorkerStatusCode stop_status = SERVICE_WORKER_ERROR_FAILED;
+ version_->StopWorker(CreateReceiverOnCurrentThread(&stop_status));
+ version_->SendMessage(TestMsg_Message(),
+ CreateReceiverOnCurrentThread(&msg_status));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(SERVICE_WORKER_OK, stop_status);
+
+ // SendMessage should return START_WORKER_FAILED error since it tried to
+ // start a worker while it was stopping.
+ EXPECT_EQ(SERVICE_WORKER_ERROR_START_WORKER_FAILED, msg_status);
+
+ // Resend the message, which should succeed and restart the worker.
+ version_->SendMessage(TestMsg_Message(),
+ CreateReceiverOnCurrentThread(&msg_status));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(SERVICE_WORKER_OK, msg_status);
+ EXPECT_EQ(ServiceWorkerVersion::RUNNING, version_->running_status());
+}
+
+TEST_F(ServiceWorkerVersionTest, ReceiveMessageFromWorker) {
+ MessageReceiverFromWorker receiver(version_->embedded_worker());
+
+ // Simulate sending some dummy values from the worker.
+ helper_->SimulateSendValueToBrowser(
+ version_->embedded_worker()->embedded_worker_id(), 555);
+ helper_->SimulateSendValueToBrowser(
+ version_->embedded_worker()->embedded_worker_id(), 777);
+
+ // Verify the receiver received the values.
+ ASSERT_EQ(2U, receiver.received_values().size());
+ EXPECT_EQ(555, receiver.received_values()[0]);
+ EXPECT_EQ(777, receiver.received_values()[1]);
+}
+
+TEST_F(ServiceWorkerVersionTest, InstallAndWaitCompletion) {
+ EXPECT_EQ(ServiceWorkerVersion::NEW, version_->status());
+
+ // Dispatch an install event.
+ ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
+ version_->DispatchInstallEvent(-1, CreateReceiverOnCurrentThread(&status));
+
+ // Wait for the completion.
+ bool status_change_called = false;
+ version_->RegisterStatusChangeCallback(
+ base::Bind(&VerifyCalled, &status_change_called));
+
+ base::RunLoop().RunUntilIdle();
+
+ // After successful completion, version's status must be changed to
+ // INSTALLED, and status change callback must have been fired.
+ EXPECT_EQ(SERVICE_WORKER_OK, status);
+ EXPECT_TRUE(status_change_called);
+ EXPECT_EQ(ServiceWorkerVersion::INSTALLED, version_->status());
+}
+
+TEST_F(ServiceWorkerVersionTest, ActivateAndWaitCompletion) {
+ version_->SetStatus(ServiceWorkerVersion::INSTALLED);
+ EXPECT_EQ(ServiceWorkerVersion::INSTALLED, version_->status());
+
+ // Dispatch an activate event.
+ ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
+ version_->DispatchActivateEvent(CreateReceiverOnCurrentThread(&status));
+
+ // Wait for the completion.
+ bool status_change_called = false;
+ version_->RegisterStatusChangeCallback(
+ base::Bind(&VerifyCalled, &status_change_called));
+
+ base::RunLoop().RunUntilIdle();
+
+ // After successful completion, version's status must be changed to
+ // ACTIVE, and status change callback must have been fired.
+ EXPECT_EQ(SERVICE_WORKER_OK, status);
+ EXPECT_TRUE(status_change_called);
+ EXPECT_EQ(ServiceWorkerVersion::ACTIVE, version_->status());
+}
+
+TEST_F(ServiceWorkerVersionTest, RepeatedlyObserveStatusChanges) {
+ EXPECT_EQ(ServiceWorkerVersion::NEW, version_->status());
+
+ // Repeatedly observe status changes (the callback re-registers itself).
+ std::vector<ServiceWorkerVersion::Status> statuses;
+ version_->RegisterStatusChangeCallback(
+ base::Bind(&ObserveStatusChanges, version_, &statuses));
+
+ // Dispatch some events.
+ ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
+ version_->DispatchInstallEvent(-1, CreateReceiverOnCurrentThread(&status));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(SERVICE_WORKER_OK, status);
+
+ status = SERVICE_WORKER_ERROR_FAILED;
+ version_->DispatchActivateEvent(CreateReceiverOnCurrentThread(&status));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(SERVICE_WORKER_OK, status);
+
+ // Verify that we could successfully observe repeated status changes.
+ ASSERT_EQ(4U, statuses.size());
+ ASSERT_EQ(ServiceWorkerVersion::INSTALLING, statuses[0]);
+ ASSERT_EQ(ServiceWorkerVersion::INSTALLED, statuses[1]);
+ ASSERT_EQ(ServiceWorkerVersion::ACTIVATING, statuses[2]);
+ ASSERT_EQ(ServiceWorkerVersion::ACTIVE, statuses[3]);
+}
+
+TEST_F(ServiceWorkerVersionTest, AddAndRemoveProcesses) {
+ // Preparation (to reset the process count to 0).
+ ASSERT_TRUE(version_->HasProcessToRun());
+ version_->RemoveProcessFromWorker(kRenderProcessId);
+ ASSERT_FALSE(version_->HasProcessToRun());
+
+ // Add another process to the worker twice, and then remove process once.
+ const int another_process_id = kRenderProcessId + 1;
+ version_->AddProcessToWorker(another_process_id);
+ version_->AddProcessToWorker(another_process_id);
+ version_->RemoveProcessFromWorker(another_process_id);
+
+ // We're ref-counting the process internally, so adding the same process
+ // multiple times should be handled correctly.
+ ASSERT_TRUE(version_->HasProcessToRun());
+
+ // Removing the process again (so that # of AddProcess == # of RemoveProcess
+ // for the process) should remove all process references.
+ version_->RemoveProcessFromWorker(another_process_id);
+ ASSERT_FALSE(version_->HasProcessToRun());
+}
+
+} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_write_to_cache_job.cc b/chromium/content/browser/service_worker/service_worker_write_to_cache_job.cc
new file mode 100644
index 00000000000..bd16a132004
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_write_to_cache_job.cc
@@ -0,0 +1,327 @@
+// 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/service_worker/service_worker_write_to_cache_job.h"
+
+#include "content/browser/service_worker/service_worker_context_core.h"
+#include "content/browser/service_worker/service_worker_disk_cache.h"
+#include "content/browser/service_worker/service_worker_histograms.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_util.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_status.h"
+
+namespace content {
+
+ServiceWorkerWriteToCacheJob::ServiceWorkerWriteToCacheJob(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate,
+ base::WeakPtr<ServiceWorkerContextCore> context,
+ ServiceWorkerVersion* version,
+ int64 response_id)
+ : net::URLRequestJob(request, network_delegate),
+ context_(context),
+ url_(request->url()),
+ response_id_(response_id),
+ version_(version),
+ has_been_killed_(false),
+ did_notify_started_(false),
+ did_notify_finished_(false),
+ weak_factory_(this) {
+ InitNetRequest();
+}
+
+ServiceWorkerWriteToCacheJob::~ServiceWorkerWriteToCacheJob() {
+ DCHECK_EQ(did_notify_started_, did_notify_finished_);
+}
+
+void ServiceWorkerWriteToCacheJob::Start() {
+ if (!context_) {
+ NotifyStartError(net::URLRequestStatus(
+ net::URLRequestStatus::FAILED, net::ERR_FAILED));
+ return;
+ }
+ version_->script_cache_map()->NotifyStartedCaching(
+ url_, response_id_);
+ did_notify_started_ = true;
+ StartNetRequest();
+}
+
+void ServiceWorkerWriteToCacheJob::Kill() {
+ if (has_been_killed_)
+ return;
+ weak_factory_.InvalidateWeakPtrs();
+ has_been_killed_ = true;
+ net_request_.reset();
+ if (did_notify_started_ && !did_notify_finished_) {
+ version_->script_cache_map()->NotifyFinishedCaching(
+ url_, false);
+ did_notify_finished_ = true;
+ }
+ writer_.reset();
+ context_.reset();
+ net::URLRequestJob::Kill();
+}
+
+net::LoadState ServiceWorkerWriteToCacheJob::GetLoadState() const {
+ if (writer_ && writer_->IsWritePending())
+ return net::LOAD_STATE_WAITING_FOR_APPCACHE;
+ if (net_request_)
+ return net_request_->GetLoadState().state;
+ return net::LOAD_STATE_IDLE;
+}
+
+bool ServiceWorkerWriteToCacheJob::GetCharset(std::string* charset) {
+ if (!http_info())
+ return false;
+ return http_info()->headers->GetCharset(charset);
+}
+
+bool ServiceWorkerWriteToCacheJob::GetMimeType(std::string* mime_type) const {
+ if (!http_info())
+ return false;
+ return http_info()->headers->GetMimeType(mime_type);
+}
+
+void ServiceWorkerWriteToCacheJob::GetResponseInfo(
+ net::HttpResponseInfo* info) {
+ if (!http_info())
+ return;
+ *info = *http_info();
+}
+
+int ServiceWorkerWriteToCacheJob::GetResponseCode() const {
+ if (!http_info())
+ return -1;
+ return http_info()->headers->response_code();
+}
+
+void ServiceWorkerWriteToCacheJob::SetExtraRequestHeaders(
+ const net::HttpRequestHeaders& headers) {
+ std::string value;
+ DCHECK(!headers.GetHeader(net::HttpRequestHeaders::kRange, &value));
+ net_request_->SetExtraRequestHeaders(headers);
+}
+
+bool ServiceWorkerWriteToCacheJob::ReadRawData(
+ net::IOBuffer* buf,
+ int buf_size,
+ int *bytes_read) {
+ net::URLRequestStatus status = ReadNetData(buf, buf_size, bytes_read);
+ SetStatus(status);
+ if (status.is_io_pending())
+ return false;
+
+ // No more data to process, the job is complete.
+ io_buffer_ = NULL;
+ version_->script_cache_map()->NotifyFinishedCaching(
+ url_, status.is_success());
+ did_notify_finished_ = true;
+ return status.is_success();
+}
+
+const net::HttpResponseInfo* ServiceWorkerWriteToCacheJob::http_info() const {
+ return http_info_.get();
+}
+
+void ServiceWorkerWriteToCacheJob::InitNetRequest() {
+ DCHECK(request());
+ net_request_ = request()->context()->CreateRequest(
+ request()->url(),
+ request()->priority(),
+ this,
+ this->GetCookieStore());
+ net_request_->set_first_party_for_cookies(
+ request()->first_party_for_cookies());
+ net_request_->SetReferrer(request()->referrer());
+ net_request_->SetExtraRequestHeaders(request()->extra_request_headers());
+}
+
+void ServiceWorkerWriteToCacheJob::StartNetRequest() {
+ net_request_->Start(); // We'll continue in OnResponseStarted.
+}
+
+net::URLRequestStatus ServiceWorkerWriteToCacheJob::ReadNetData(
+ net::IOBuffer* buf,
+ int buf_size,
+ int *bytes_read) {
+ DCHECK_GT(buf_size, 0);
+ DCHECK(bytes_read);
+
+ *bytes_read = 0;
+ io_buffer_ = buf;
+ int net_bytes_read = 0;
+ if (!net_request_->Read(buf, buf_size, &net_bytes_read)) {
+ if (net_request_->status().is_io_pending())
+ return net_request_->status();
+ DCHECK(!net_request_->status().is_success());
+ return net_request_->status();
+ }
+
+ if (net_bytes_read != 0) {
+ WriteDataToCache(net_bytes_read);
+ DCHECK(GetStatus().is_io_pending());
+ return GetStatus();
+ }
+
+ DCHECK(net_request_->status().is_success());
+ return net_request_->status();
+}
+
+void ServiceWorkerWriteToCacheJob::WriteHeadersToCache() {
+ if (!context_) {
+ AsyncNotifyDoneHelper(net::URLRequestStatus(
+ net::URLRequestStatus::FAILED, net::ERR_FAILED));
+ return;
+ }
+ writer_ = context_->storage()->CreateResponseWriter(response_id_);
+ info_buffer_ = new HttpResponseInfoIOBuffer(
+ new net::HttpResponseInfo(net_request_->response_info()));
+ writer_->WriteInfo(
+ info_buffer_,
+ base::Bind(&ServiceWorkerWriteToCacheJob::OnWriteHeadersComplete,
+ weak_factory_.GetWeakPtr()));
+ SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
+}
+
+void ServiceWorkerWriteToCacheJob::OnWriteHeadersComplete(int result) {
+ SetStatus(net::URLRequestStatus()); // Clear the IO_PENDING status
+ if (result < 0) {
+ ServiceWorkerHistograms::CountWriteResponseResult(
+ ServiceWorkerHistograms::WRITE_HEADERS_ERROR);
+ AsyncNotifyDoneHelper(net::URLRequestStatus(
+ net::URLRequestStatus::FAILED, result));
+ return;
+ }
+ http_info_.reset(info_buffer_->http_info.release());
+ info_buffer_ = NULL;
+ NotifyHeadersComplete();
+}
+
+void ServiceWorkerWriteToCacheJob::WriteDataToCache(int amount_to_write) {
+ DCHECK_NE(0, amount_to_write);
+ SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
+ writer_->WriteData(
+ io_buffer_, amount_to_write,
+ base::Bind(&ServiceWorkerWriteToCacheJob::OnWriteDataComplete,
+ weak_factory_.GetWeakPtr()));
+}
+
+void ServiceWorkerWriteToCacheJob::OnWriteDataComplete(int result) {
+ DCHECK_NE(0, result);
+ io_buffer_ = NULL;
+ if (!context_) {
+ AsyncNotifyDoneHelper(net::URLRequestStatus(
+ net::URLRequestStatus::FAILED, net::ERR_FAILED));
+ return;
+ }
+ if (result < 0) {
+ ServiceWorkerHistograms::CountWriteResponseResult(
+ ServiceWorkerHistograms::WRITE_DATA_ERROR);
+ AsyncNotifyDoneHelper(net::URLRequestStatus(
+ net::URLRequestStatus::FAILED, result));
+ return;
+ }
+ ServiceWorkerHistograms::CountWriteResponseResult(
+ ServiceWorkerHistograms::WRITE_OK);
+ SetStatus(net::URLRequestStatus()); // Clear the IO_PENDING status
+ NotifyReadComplete(result);
+}
+
+void ServiceWorkerWriteToCacheJob::OnReceivedRedirect(
+ net::URLRequest* request,
+ const GURL& new_url,
+ bool* defer_redirect) {
+ DCHECK_EQ(net_request_, request);
+ // Script resources can't redirect.
+ AsyncNotifyDoneHelper(net::URLRequestStatus(
+ net::URLRequestStatus::FAILED, net::ERR_FAILED));
+}
+
+void ServiceWorkerWriteToCacheJob::OnAuthRequired(
+ net::URLRequest* request,
+ net::AuthChallengeInfo* auth_info) {
+ DCHECK_EQ(net_request_, request);
+ // TODO(michaeln): Pass this thru to our jobs client.
+ AsyncNotifyDoneHelper(net::URLRequestStatus(
+ net::URLRequestStatus::FAILED, net::ERR_FAILED));
+}
+
+void ServiceWorkerWriteToCacheJob::OnCertificateRequested(
+ net::URLRequest* request,
+ net::SSLCertRequestInfo* cert_request_info) {
+ DCHECK_EQ(net_request_, request);
+ // TODO(michaeln): Pass this thru to our jobs client.
+ // see NotifyCertificateRequested.
+ AsyncNotifyDoneHelper(net::URLRequestStatus(
+ net::URLRequestStatus::FAILED, net::ERR_FAILED));
+}
+
+void ServiceWorkerWriteToCacheJob:: OnSSLCertificateError(
+ net::URLRequest* request,
+ const net::SSLInfo& ssl_info,
+ bool fatal) {
+ DCHECK_EQ(net_request_, request);
+ // TODO(michaeln): Pass this thru to our jobs client,
+ // see NotifySSLCertificateError.
+ AsyncNotifyDoneHelper(net::URLRequestStatus(
+ net::URLRequestStatus::FAILED, net::ERR_FAILED));
+}
+
+void ServiceWorkerWriteToCacheJob::OnBeforeNetworkStart(
+ net::URLRequest* request,
+ bool* defer) {
+ DCHECK_EQ(net_request_, request);
+ NotifyBeforeNetworkStart(defer);
+}
+
+void ServiceWorkerWriteToCacheJob::OnResponseStarted(
+ net::URLRequest* request) {
+ DCHECK_EQ(net_request_, request);
+ if (!request->status().is_success()) {
+ AsyncNotifyDoneHelper(request->status());
+ return;
+ }
+ if (request->GetResponseCode() / 100 != 2) {
+ AsyncNotifyDoneHelper(net::URLRequestStatus(
+ net::URLRequestStatus::FAILED, net::ERR_FAILED));
+ // TODO(michaeln): Instead of error'ing immediately, send the net
+ // response to our consumer, just don't cache it?
+ return;
+ }
+ WriteHeadersToCache();
+}
+
+void ServiceWorkerWriteToCacheJob::OnReadCompleted(
+ net::URLRequest* request,
+ int bytes_read) {
+ DCHECK_EQ(net_request_, request);
+ if (!request->status().is_success()) {
+ AsyncNotifyDoneHelper(request->status());
+ return;
+ }
+ if (bytes_read > 0) {
+ WriteDataToCache(bytes_read);
+ return;
+ }
+ // We're done with all.
+ AsyncNotifyDoneHelper(request->status());
+ return;
+}
+
+void ServiceWorkerWriteToCacheJob::AsyncNotifyDoneHelper(
+ const net::URLRequestStatus& status) {
+ DCHECK(!status.is_io_pending());
+ version_->script_cache_map()->NotifyFinishedCaching(
+ url_, status.is_success());
+ did_notify_finished_ = true;
+ SetStatus(status);
+ NotifyDone(status);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/service_worker/service_worker_write_to_cache_job.h b/chromium/content/browser/service_worker/service_worker_write_to_cache_job.h
new file mode 100644
index 00000000000..040df41b4c6
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_write_to_cache_job.h
@@ -0,0 +1,117 @@
+// 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_SERVICE_WORKER_SERVICE_WORKER_WRITE_TO_CACHE_JOB_H_
+#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_WRITE_TO_CACHE_JOB_H_
+
+#include "base/memory/weak_ptr.h"
+#include "content/browser/service_worker/service_worker_disk_cache.h"
+#include "content/browser/service_worker/service_worker_version.h"
+#include "content/common/content_export.h"
+#include "content/common/service_worker/service_worker_status_code.h"
+#include "content/common/service_worker/service_worker_types.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_job.h"
+
+namespace content {
+
+class ServiceWorkerContextCore;
+class ServiceWorkerResponseWriter;
+class ServiceWorkerVersions;
+
+// A URLRequestJob derivative used to cache the main script
+// and its imports during the initial install of a new version.
+// Another separate URLRequest is started which will perform
+// a network fetch. The response produced for that separate
+// request is written to the service worker script cache and piped
+// to the consumer of the ServiceWorkerWriteToCacheJob for delivery
+// to the renderer process housing the worker.
+class CONTENT_EXPORT ServiceWorkerWriteToCacheJob
+ : public net::URLRequestJob,
+ public net::URLRequest::Delegate {
+ public:
+ ServiceWorkerWriteToCacheJob(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate,
+ base::WeakPtr<ServiceWorkerContextCore> context,
+ ServiceWorkerVersion* version,
+ int64 response_id);
+
+ private:
+ virtual ~ServiceWorkerWriteToCacheJob();
+
+ // net::URLRequestJob overrides
+ virtual void Start() OVERRIDE;
+ virtual void Kill() OVERRIDE;
+ virtual net::LoadState GetLoadState() const OVERRIDE;
+ virtual bool GetCharset(std::string* charset) OVERRIDE;
+ virtual bool GetMimeType(std::string* mime_type) const OVERRIDE;
+ virtual void GetResponseInfo(net::HttpResponseInfo* info) OVERRIDE;
+ virtual int GetResponseCode() const OVERRIDE;
+ virtual void SetExtraRequestHeaders(
+ const net::HttpRequestHeaders& headers) OVERRIDE;
+ virtual bool ReadRawData(net::IOBuffer* buf,
+ int buf_size,
+ int *bytes_read) OVERRIDE;
+
+ const net::HttpResponseInfo* http_info() const;
+
+ // Methods to drive the net request forward and
+ // write data to the disk cache.
+ void InitNetRequest();
+ void StartNetRequest();
+ net::URLRequestStatus ReadNetData(
+ net::IOBuffer* buf,
+ int buf_size,
+ int *bytes_read);
+ void WriteHeadersToCache();
+ void OnWriteHeadersComplete(int result);
+ void WriteDataToCache(int bytes_to_write);
+ void OnWriteDataComplete(int result);
+
+ // net::URLRequest::Delegate overrides that observe the net request.
+ virtual void OnReceivedRedirect(
+ net::URLRequest* request,
+ const GURL& new_url,
+ bool* defer_redirect) OVERRIDE;
+ virtual void OnAuthRequired(
+ net::URLRequest* request,
+ net::AuthChallengeInfo* auth_info) OVERRIDE;
+ virtual void OnCertificateRequested(
+ net::URLRequest* request,
+ net::SSLCertRequestInfo* cert_request_info) OVERRIDE;
+ virtual void OnSSLCertificateError(
+ net::URLRequest* request,
+ const net::SSLInfo& ssl_info,
+ bool fatal) OVERRIDE;
+ virtual void OnBeforeNetworkStart(
+ net::URLRequest* request,
+ bool* defer) OVERRIDE;
+ virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE;
+ virtual void OnReadCompleted(
+ net::URLRequest* request,
+ int bytes_read) OVERRIDE;
+
+ void AsyncNotifyDoneHelper(const net::URLRequestStatus& status);
+
+ scoped_refptr<net::IOBuffer> io_buffer_;
+ scoped_refptr<HttpResponseInfoIOBuffer> info_buffer_;
+ base::WeakPtr<ServiceWorkerContextCore> context_;
+ GURL url_;
+ int64 response_id_;
+ scoped_ptr<net::URLRequest> net_request_;
+ scoped_ptr<net::HttpResponseInfo> http_info_;
+ scoped_ptr<ServiceWorkerResponseWriter> writer_;
+ scoped_refptr<ServiceWorkerVersion> version_;
+ bool has_been_killed_;
+ bool did_notify_started_;
+ bool did_notify_finished_;
+ base::WeakPtrFactory<ServiceWorkerWriteToCacheJob> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServiceWorkerWriteToCacheJob);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_WRITE_TO_CACHE_JOB_H_
diff --git a/chromium/content/browser/session_history_browsertest.cc b/chromium/content/browser/session_history_browsertest.cc
index bec8293a6b8..4465510ec38 100644
--- a/chromium/content/browser/session_history_browsertest.cc
+++ b/chromium/content/browser/session_history_browsertest.cc
@@ -11,10 +11,10 @@
#include "content/public/browser/web_contents.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/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
@@ -52,7 +52,7 @@ class SessionHistoryTest : public ContentBrowserTest {
embedded_test_server()->RegisterRequestHandler(
base::Bind(&HandleEchoTitleRequest, "/echotitle"));
- NavigateToURL(shell(), GURL(kAboutBlankURL));
+ NavigateToURL(shell(), GURL(url::kAboutBlankURL));
}
// Simulate clicking a link. Only works on the frames.html testserver page.
@@ -85,7 +85,7 @@ class SessionHistoryTest : public ContentBrowserTest {
}
std::string GetTabTitle() {
- return UTF16ToASCII(shell()->web_contents()->GetTitle());
+ return base::UTF16ToASCII(shell()->web_contents()->GetTitle());
}
GURL GetTabURL() {
@@ -99,7 +99,7 @@ class SessionHistoryTest : public ContentBrowserTest {
void NavigateAndCheckTitle(const char* filename,
const std::string& expected_title) {
- base::string16 expected_title16(ASCIIToUTF16(expected_title));
+ base::string16 expected_title16(base::ASCIIToUTF16(expected_title));
TitleWatcher title_watcher(shell()->web_contents(), expected_title16);
NavigateToURL(shell(), GetURL(filename));
ASSERT_EQ(expected_title16, title_watcher.WaitAndGetTitle());
@@ -164,10 +164,10 @@ IN_PROC_BROWSER_TEST_F(SessionHistoryTest, BasicBackForward) {
EXPECT_EQ("bot1", GetTabTitle());
GoBack();
- EXPECT_EQ(std::string(kAboutBlankURL), GetTabTitle());
+ EXPECT_EQ(std::string(url::kAboutBlankURL), GetTabTitle());
ASSERT_FALSE(CanGoBack());
- EXPECT_EQ(std::string(kAboutBlankURL), GetTabTitle());
+ EXPECT_EQ(std::string(url::kAboutBlankURL), GetTabTitle());
GoForward();
EXPECT_EQ("bot1", GetTabTitle());
@@ -203,8 +203,8 @@ IN_PROC_BROWSER_TEST_F(SessionHistoryTest, FrameBackForward) {
EXPECT_EQ(frames, GetTabURL());
GoBack();
- EXPECT_EQ(std::string(kAboutBlankURL), GetTabTitle());
- EXPECT_EQ(GURL(kAboutBlankURL), GetTabURL());
+ EXPECT_EQ(std::string(url::kAboutBlankURL), GetTabTitle());
+ EXPECT_EQ(GURL(url::kAboutBlankURL), GetTabURL());
GoForward();
EXPECT_EQ("bot1", GetTabTitle());
@@ -400,10 +400,10 @@ IN_PROC_BROWSER_TEST_F(SessionHistoryTest, JavascriptHistory) {
// history is [blank, bot1, bot2, *bot3]
JavascriptGo("-3");
- EXPECT_EQ(std::string(kAboutBlankURL), GetTabTitle());
+ EXPECT_EQ(std::string(url::kAboutBlankURL), GetTabTitle());
ASSERT_FALSE(CanGoBack());
- EXPECT_EQ(std::string(kAboutBlankURL), GetTabTitle());
+ EXPECT_EQ(std::string(url::kAboutBlankURL), GetTabTitle());
JavascriptGo("1");
EXPECT_EQ("bot1", GetTabTitle());
@@ -419,10 +419,10 @@ IN_PROC_BROWSER_TEST_F(SessionHistoryTest, JavascriptHistory) {
EXPECT_EQ("bot1", GetTabTitle());
JavascriptGo("-1");
- EXPECT_EQ(std::string(kAboutBlankURL), GetTabTitle());
+ EXPECT_EQ(std::string(url::kAboutBlankURL), GetTabTitle());
ASSERT_FALSE(CanGoBack());
- EXPECT_EQ(std::string(kAboutBlankURL), GetTabTitle());
+ EXPECT_EQ(std::string(url::kAboutBlankURL), GetTabTitle());
JavascriptGo("1");
EXPECT_EQ("bot1", GetTabTitle());
diff --git a/chromium/content/browser/shared_worker/OWNERS b/chromium/content/browser/shared_worker/OWNERS
new file mode 100644
index 00000000000..61fc3fd49ea
--- /dev/null
+++ b/chromium/content/browser/shared_worker/OWNERS
@@ -0,0 +1,3 @@
+atwilson@chromium.org
+kinuko@chromium.org
+horo@chromium.org
diff --git a/chromium/content/browser/shared_worker/shared_worker_host.cc b/chromium/content/browser/shared_worker/shared_worker_host.cc
new file mode 100644
index 00000000000..9fc9d67ec3a
--- /dev/null
+++ b/chromium/content/browser/shared_worker/shared_worker_host.cc
@@ -0,0 +1,336 @@
+// 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/shared_worker/shared_worker_host.h"
+
+#include "base/metrics/histogram.h"
+#include "content/browser/devtools/embedded_worker_devtools_manager.h"
+#include "content/browser/frame_host/render_frame_host_delegate.h"
+#include "content/browser/frame_host/render_frame_host_impl.h"
+#include "content/browser/message_port_service.h"
+#include "content/browser/shared_worker/shared_worker_instance.h"
+#include "content/browser/shared_worker/shared_worker_message_filter.h"
+#include "content/browser/shared_worker/shared_worker_service_impl.h"
+#include "content/browser/worker_host/worker_document_set.h"
+#include "content/common/view_messages.h"
+#include "content/common/worker_messages.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"
+
+namespace content {
+namespace {
+
+// Notifies RenderViewHost that one or more worker objects crashed.
+void WorkerCrashCallback(int render_process_unique_id, int render_frame_id) {
+ RenderFrameHostImpl* host =
+ RenderFrameHostImpl::FromID(render_process_unique_id, render_frame_id);
+ if (host)
+ host->delegate()->WorkerCrashed(host);
+}
+
+void NotifyWorkerContextStarted(int worker_process_id, int worker_route_id) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(
+ NotifyWorkerContextStarted, worker_process_id, worker_route_id));
+ return;
+ }
+ EmbeddedWorkerDevToolsManager::GetInstance()->WorkerContextStarted(
+ worker_process_id, worker_route_id);
+}
+
+void NotifyWorkerDestroyed(int worker_process_id, int worker_route_id) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(NotifyWorkerDestroyed, worker_process_id, worker_route_id));
+ return;
+ }
+ EmbeddedWorkerDevToolsManager::GetInstance()->WorkerDestroyed(
+ worker_process_id, worker_route_id);
+}
+
+} // namespace
+
+SharedWorkerHost::SharedWorkerHost(SharedWorkerInstance* instance,
+ SharedWorkerMessageFilter* filter,
+ int worker_route_id)
+ : instance_(instance),
+ worker_document_set_(new WorkerDocumentSet()),
+ container_render_filter_(filter),
+ worker_process_id_(filter->render_process_id()),
+ worker_route_id_(worker_route_id),
+ load_failed_(false),
+ closed_(false),
+ creation_time_(base::TimeTicks::Now()) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+}
+
+SharedWorkerHost::~SharedWorkerHost() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ UMA_HISTOGRAM_LONG_TIMES("SharedWorker.TimeToDeleted",
+ base::TimeTicks::Now() - creation_time_);
+ // If we crashed, tell the RenderViewHosts.
+ if (instance_ && !load_failed_) {
+ const WorkerDocumentSet::DocumentInfoSet& parents =
+ worker_document_set_->documents();
+ for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter =
+ parents.begin();
+ parent_iter != parents.end();
+ ++parent_iter) {
+ BrowserThread::PostTask(BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&WorkerCrashCallback,
+ parent_iter->render_process_id(),
+ parent_iter->render_frame_id()));
+ }
+ }
+ if (!closed_)
+ NotifyWorkerDestroyed(worker_process_id_, worker_route_id_);
+ SharedWorkerServiceImpl::GetInstance()->NotifyWorkerDestroyed(
+ worker_process_id_, worker_route_id_);
+}
+
+bool SharedWorkerHost::Send(IPC::Message* message) {
+ if (!container_render_filter_) {
+ delete message;
+ return false;
+ }
+ return container_render_filter_->Send(message);
+}
+
+void SharedWorkerHost::Start(bool pause_on_start) {
+ WorkerProcessMsg_CreateWorker_Params params;
+ params.url = instance_->url();
+ params.name = instance_->name();
+ params.content_security_policy = instance_->content_security_policy();
+ params.security_policy_type = instance_->security_policy_type();
+ params.pause_on_start = pause_on_start;
+ params.route_id = worker_route_id_;
+ Send(new WorkerProcessMsg_CreateWorker(params));
+
+ for (FilterList::const_iterator i = filters_.begin(); i != filters_.end();
+ ++i) {
+ i->filter()->Send(new ViewMsg_WorkerCreated(i->route_id()));
+ }
+}
+
+bool SharedWorkerHost::FilterMessage(const IPC::Message& message,
+ SharedWorkerMessageFilter* filter) {
+ if (!instance_)
+ return false;
+
+ if (!closed_ && HasFilter(filter, message.routing_id())) {
+ RelayMessage(message, filter);
+ return true;
+ }
+
+ return false;
+}
+
+void SharedWorkerHost::FilterShutdown(SharedWorkerMessageFilter* filter) {
+ if (!instance_)
+ return;
+ RemoveFilters(filter);
+ worker_document_set_->RemoveAll(filter);
+ if (worker_document_set_->IsEmpty()) {
+ // This worker has no more associated documents - shut it down.
+ Send(new WorkerMsg_TerminateWorkerContext(worker_route_id_));
+ }
+}
+
+void SharedWorkerHost::DocumentDetached(SharedWorkerMessageFilter* filter,
+ unsigned long long document_id) {
+ if (!instance_)
+ return;
+ // Walk all instances and remove the document from their document set.
+ worker_document_set_->Remove(filter, document_id);
+ if (worker_document_set_->IsEmpty()) {
+ // This worker has no more associated documents - shut it down.
+ Send(new WorkerMsg_TerminateWorkerContext(worker_route_id_));
+ }
+}
+
+void SharedWorkerHost::WorkerContextClosed() {
+ if (!instance_)
+ return;
+ // Set the closed flag - this will stop any further messages from
+ // being sent to the worker (messages can still be sent from the worker,
+ // for exception reporting, etc).
+ closed_ = true;
+ NotifyWorkerDestroyed(worker_process_id_, worker_route_id_);
+}
+
+void SharedWorkerHost::WorkerContextDestroyed() {
+ if (!instance_)
+ return;
+ instance_.reset();
+ worker_document_set_ = NULL;
+}
+
+void SharedWorkerHost::WorkerScriptLoaded() {
+ UMA_HISTOGRAM_TIMES("SharedWorker.TimeToScriptLoaded",
+ base::TimeTicks::Now() - creation_time_);
+ NotifyWorkerContextStarted(worker_process_id_, worker_route_id_);
+}
+
+void SharedWorkerHost::WorkerScriptLoadFailed() {
+ UMA_HISTOGRAM_TIMES("SharedWorker.TimeToScriptLoadFailed",
+ base::TimeTicks::Now() - creation_time_);
+ if (!instance_)
+ return;
+ load_failed_ = true;
+ for (FilterList::const_iterator i = filters_.begin(); i != filters_.end();
+ ++i) {
+ i->filter()->Send(new ViewMsg_WorkerScriptLoadFailed(i->route_id()));
+ }
+}
+
+void SharedWorkerHost::WorkerConnected(int message_port_id) {
+ if (!instance_)
+ return;
+ for (FilterList::const_iterator i = filters_.begin(); i != filters_.end();
+ ++i) {
+ if (i->message_port_id() != message_port_id)
+ continue;
+ i->filter()->Send(new ViewMsg_WorkerConnected(i->route_id()));
+ return;
+ }
+}
+
+void SharedWorkerHost::AllowDatabase(const GURL& url,
+ const base::string16& name,
+ const base::string16& display_name,
+ unsigned long estimated_size,
+ bool* result) {
+ if (!instance_)
+ return;
+ *result = GetContentClient()->browser()->AllowWorkerDatabase(
+ url,
+ name,
+ display_name,
+ estimated_size,
+ instance_->resource_context(),
+ GetRenderFrameIDsForWorker());
+}
+
+void SharedWorkerHost::AllowFileSystem(const GURL& url,
+ bool* result) {
+ if (!instance_)
+ return;
+ *result = GetContentClient()->browser()->AllowWorkerFileSystem(
+ url, instance_->resource_context(), GetRenderFrameIDsForWorker());
+}
+
+void SharedWorkerHost::AllowIndexedDB(const GURL& url,
+ const base::string16& name,
+ bool* result) {
+ if (!instance_)
+ return;
+ *result = GetContentClient()->browser()->AllowWorkerIndexedDB(
+ url, name, instance_->resource_context(), GetRenderFrameIDsForWorker());
+}
+
+void SharedWorkerHost::RelayMessage(
+ const IPC::Message& message,
+ SharedWorkerMessageFilter* incoming_filter) {
+ if (!instance_)
+ return;
+ if (message.type() == WorkerMsg_Connect::ID) {
+ // Crack the SharedWorker Connect message to setup routing for the port.
+ WorkerMsg_Connect::Param param;
+ if (!WorkerMsg_Connect::Read(&message, &param))
+ return;
+ int sent_message_port_id = param.a;
+ int new_routing_id = param.b;
+
+ DCHECK(container_render_filter_);
+ new_routing_id = container_render_filter_->GetNextRoutingID();
+ MessagePortService::GetInstance()->UpdateMessagePort(
+ sent_message_port_id,
+ container_render_filter_->message_port_message_filter(),
+ new_routing_id);
+ SetMessagePortID(
+ incoming_filter, message.routing_id(), sent_message_port_id);
+ // Resend the message with the new routing id.
+ Send(new WorkerMsg_Connect(
+ worker_route_id_, sent_message_port_id, new_routing_id));
+
+ // Send any queued messages for the sent port.
+ MessagePortService::GetInstance()->SendQueuedMessagesIfPossible(
+ sent_message_port_id);
+ } else {
+ IPC::Message* new_message = new IPC::Message(message);
+ new_message->set_routing_id(worker_route_id_);
+ Send(new_message);
+ return;
+ }
+}
+
+void SharedWorkerHost::TerminateWorker() {
+ Send(new WorkerMsg_TerminateWorkerContext(worker_route_id_));
+}
+
+std::vector<std::pair<int, int> >
+SharedWorkerHost::GetRenderFrameIDsForWorker() {
+ std::vector<std::pair<int, int> > result;
+ if (!instance_)
+ return result;
+ const WorkerDocumentSet::DocumentInfoSet& documents =
+ worker_document_set_->documents();
+ for (WorkerDocumentSet::DocumentInfoSet::const_iterator doc =
+ documents.begin();
+ doc != documents.end();
+ ++doc) {
+ result.push_back(
+ std::make_pair(doc->render_process_id(), doc->render_frame_id()));
+ }
+ return result;
+}
+
+void SharedWorkerHost::AddFilter(SharedWorkerMessageFilter* filter,
+ int route_id) {
+ CHECK(filter);
+ if (!HasFilter(filter, route_id)) {
+ FilterInfo info(filter, route_id);
+ filters_.push_back(info);
+ }
+}
+
+void SharedWorkerHost::RemoveFilters(SharedWorkerMessageFilter* filter) {
+ for (FilterList::iterator i = filters_.begin(); i != filters_.end();) {
+ if (i->filter() == filter)
+ i = filters_.erase(i);
+ else
+ ++i;
+ }
+}
+
+bool SharedWorkerHost::HasFilter(SharedWorkerMessageFilter* filter,
+ int route_id) const {
+ for (FilterList::const_iterator i = filters_.begin(); i != filters_.end();
+ ++i) {
+ if (i->filter() == filter && i->route_id() == route_id)
+ return true;
+ }
+ return false;
+}
+
+void SharedWorkerHost::SetMessagePortID(SharedWorkerMessageFilter* filter,
+ int route_id,
+ int message_port_id) {
+ for (FilterList::iterator i = filters_.begin(); i != filters_.end(); ++i) {
+ if (i->filter() == filter && i->route_id() == route_id) {
+ i->set_message_port_id(message_port_id);
+ return;
+ }
+ }
+}
+
+} // namespace content
diff --git a/chromium/content/browser/shared_worker/shared_worker_host.h b/chromium/content/browser/shared_worker/shared_worker_host.h
new file mode 100644
index 00000000000..53cc218669c
--- /dev/null
+++ b/chromium/content/browser/shared_worker/shared_worker_host.h
@@ -0,0 +1,135 @@
+// 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_SHARED_WORKER_SHARED_WORKER_HOST_H_
+#define CONTENT_BROWSER_SHARED_WORKER_SHARED_WORKER_HOST_H_
+
+#include <list>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string16.h"
+#include "base/time/time.h"
+#include "content/browser/shared_worker/shared_worker_message_filter.h"
+#include "content/browser/worker_host/worker_document_set.h"
+
+class GURL;
+
+namespace IPC {
+class Message;
+}
+
+namespace content {
+class SharedWorkerMessageFilter;
+class SharedWorkerInstance;
+
+// The SharedWorkerHost is the interface that represents the browser side of
+// the browser <-> worker communication channel.
+class SharedWorkerHost {
+ public:
+ SharedWorkerHost(SharedWorkerInstance* instance,
+ SharedWorkerMessageFilter* filter,
+ int worker_route_id);
+ ~SharedWorkerHost();
+
+ // Sends |message| to the SharedWorker.
+ bool Send(IPC::Message* message);
+
+ // Starts the SharedWorker in the renderer process which is associated with
+ // |filter_|.
+ void Start(bool pause_on_start);
+
+ // Returns true iff the given message from a renderer process was forwarded to
+ // the worker.
+ bool FilterMessage(const IPC::Message& message,
+ SharedWorkerMessageFilter* filter);
+
+ // Handles the shutdown of the filter. If the worker has no other client,
+ // sends TerminateWorkerContext message to shut it down.
+ void FilterShutdown(SharedWorkerMessageFilter* filter);
+
+ // Shuts down any shared workers that are no longer referenced by active
+ // documents.
+ void DocumentDetached(SharedWorkerMessageFilter* filter,
+ unsigned long long document_id);
+
+ void WorkerContextClosed();
+ void WorkerScriptLoaded();
+ void WorkerScriptLoadFailed();
+ void WorkerConnected(int message_port_id);
+ void WorkerContextDestroyed();
+ void AllowDatabase(const GURL& url,
+ const base::string16& name,
+ const base::string16& display_name,
+ unsigned long estimated_size,
+ bool* result);
+ void AllowFileSystem(const GURL& url, bool* result);
+ void AllowIndexedDB(const GURL& url,
+ const base::string16& name,
+ bool* result);
+
+ // Terminates the given worker, i.e. based on a UI action.
+ void TerminateWorker();
+
+ void AddFilter(SharedWorkerMessageFilter* filter, int route_id);
+
+ SharedWorkerInstance* instance() { return instance_.get(); }
+ WorkerDocumentSet* worker_document_set() const {
+ return worker_document_set_.get();
+ }
+ SharedWorkerMessageFilter* container_render_filter() const {
+ return container_render_filter_;
+ }
+ int process_id() const { return worker_process_id_; }
+ int worker_route_id() const { return worker_route_id_; }
+ bool load_failed() const { return load_failed_; }
+ bool closed() const { return closed_; }
+
+ private:
+ // Unique identifier for a worker client.
+ class FilterInfo {
+ public:
+ FilterInfo(SharedWorkerMessageFilter* filter, int route_id)
+ : filter_(filter), route_id_(route_id), message_port_id_(0) {}
+ SharedWorkerMessageFilter* filter() const { return filter_; }
+ int route_id() const { return route_id_; }
+ int message_port_id() const { return message_port_id_; }
+ void set_message_port_id(int id) { message_port_id_ = id; }
+
+ private:
+ SharedWorkerMessageFilter* filter_;
+ int route_id_;
+ int message_port_id_;
+ };
+
+ typedef std::list<FilterInfo> FilterList;
+
+ // Relays |message| to the SharedWorker. Takes care of parsing the message if
+ // it contains a message port and sending it a valid route id.
+ void RelayMessage(const IPC::Message& message,
+ SharedWorkerMessageFilter* incoming_filter);
+
+ // Return a vector of all the render process/render frame IDs.
+ std::vector<std::pair<int, int> > GetRenderFrameIDsForWorker();
+
+ void RemoveFilters(SharedWorkerMessageFilter* filter);
+ bool HasFilter(SharedWorkerMessageFilter* filter, int route_id) const;
+ void SetMessagePortID(SharedWorkerMessageFilter* filter,
+ int route_id,
+ int message_port_id);
+
+ scoped_ptr<SharedWorkerInstance> instance_;
+ scoped_refptr<WorkerDocumentSet> worker_document_set_;
+ FilterList filters_;
+ SharedWorkerMessageFilter* container_render_filter_;
+ int worker_process_id_;
+ int worker_route_id_;
+ bool load_failed_;
+ bool closed_;
+ const base::TimeTicks creation_time_;
+ DISALLOW_COPY_AND_ASSIGN(SharedWorkerHost);
+};
+} // namespace content
+
+#endif // CONTENT_BROWSER_SHARED_WORKER_SHARED_WORKER_HOST_H_
diff --git a/chromium/content/browser/shared_worker/shared_worker_instance.cc b/chromium/content/browser/shared_worker/shared_worker_instance.cc
new file mode 100644
index 00000000000..ea8e02db705
--- /dev/null
+++ b/chromium/content/browser/shared_worker/shared_worker_instance.cc
@@ -0,0 +1,68 @@
+// 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/shared_worker/shared_worker_instance.h"
+
+#include "base/logging.h"
+
+namespace content {
+
+SharedWorkerInstance::SharedWorkerInstance(
+ const GURL& url,
+ const base::string16& name,
+ const base::string16& content_security_policy,
+ blink::WebContentSecurityPolicyType security_policy_type,
+ ResourceContext* resource_context,
+ const WorkerStoragePartitionId& partition_id)
+ : url_(url),
+ name_(name),
+ content_security_policy_(content_security_policy),
+ security_policy_type_(security_policy_type),
+ resource_context_(resource_context),
+ partition_id_(partition_id) {
+ DCHECK(resource_context_);
+}
+
+SharedWorkerInstance::SharedWorkerInstance(const SharedWorkerInstance& other)
+ : url_(other.url_),
+ name_(other.name_),
+ content_security_policy_(other.content_security_policy_),
+ security_policy_type_(other.security_policy_type_),
+ resource_context_(other.resource_context_),
+ partition_id_(other.partition_id_) {
+}
+
+SharedWorkerInstance::~SharedWorkerInstance() {}
+
+bool SharedWorkerInstance::Matches(const GURL& match_url,
+ const base::string16& match_name,
+ const WorkerStoragePartitionId& partition_id,
+ ResourceContext* resource_context) const {
+ // ResourceContext equivalence is being used as a proxy to ensure we only
+ // matched shared workers within the same BrowserContext.
+ if (resource_context_ != resource_context)
+ return false;
+
+ // We must be in the same storage partition otherwise sharing will violate
+ // isolation.
+ if (!partition_id_.Equals(partition_id))
+ return false;
+
+ if (url_.GetOrigin() != match_url.GetOrigin())
+ return false;
+
+ if (name_.empty() && match_name.empty())
+ return url_ == match_url;
+
+ return name_ == match_name;
+}
+
+bool SharedWorkerInstance::Matches(const SharedWorkerInstance& other) const {
+ return Matches(other.url(),
+ other.name(),
+ other.partition_id(),
+ other.resource_context());
+}
+
+} // namespace content
diff --git a/chromium/content/browser/shared_worker/shared_worker_instance.h b/chromium/content/browser/shared_worker/shared_worker_instance.h
new file mode 100644
index 00000000000..2785c409d57
--- /dev/null
+++ b/chromium/content/browser/shared_worker/shared_worker_instance.h
@@ -0,0 +1,70 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_SHARED_WORKER_SHARED_WORKER_INSTANCE_H_
+#define CONTENT_BROWSER_SHARED_WORKER_SHARED_WORKER_INSTANCE_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "content/browser/worker_host/worker_storage_partition.h"
+#include "content/common/content_export.h"
+#include "third_party/WebKit/public/web/WebContentSecurityPolicy.h"
+#include "url/gurl.h"
+
+namespace content {
+class ResourceContext;
+
+// SharedWorkerInstance is copyable value-type data type. It could be passed to
+// the UI thread and be used for comparison in EmbeddedWorkerDevToolsManager.
+class CONTENT_EXPORT SharedWorkerInstance {
+ public:
+ SharedWorkerInstance(const GURL& url,
+ const base::string16& name,
+ const base::string16& content_security_policy,
+ blink::WebContentSecurityPolicyType security_policy_type,
+ ResourceContext* resource_context,
+ const WorkerStoragePartitionId& partition_id);
+ SharedWorkerInstance(const SharedWorkerInstance& other);
+ ~SharedWorkerInstance();
+
+ // Checks if this SharedWorkerInstance matches the passed url/name params
+ // based on the algorithm in the WebWorkers spec - an instance matches if the
+ // origins of the URLs match, and:
+ // a) the names are non-empty and equal.
+ // -or-
+ // b) the names are both empty, and the urls are equal.
+ bool Matches(const GURL& url,
+ const base::string16& name,
+ const WorkerStoragePartitionId& partition,
+ ResourceContext* resource_context) const;
+ bool Matches(const SharedWorkerInstance& other) const;
+
+ // Accessors.
+ const GURL& url() const { return url_; }
+ const base::string16 name() const { return name_; }
+ const base::string16 content_security_policy() const {
+ return content_security_policy_;
+ }
+ blink::WebContentSecurityPolicyType security_policy_type() const {
+ return security_policy_type_;
+ }
+ ResourceContext* resource_context() const {
+ return resource_context_;
+ }
+ const WorkerStoragePartitionId& partition_id() const { return partition_id_; }
+
+ private:
+ const GURL url_;
+ const base::string16 name_;
+ const base::string16 content_security_policy_;
+ const blink::WebContentSecurityPolicyType security_policy_type_;
+ ResourceContext* const resource_context_;
+ const WorkerStoragePartitionId partition_id_;
+};
+
+} // namespace content
+
+
+#endif // CONTENT_BROWSER_SHARED_WORKER_SHARED_WORKER_INSTANCE_H_
diff --git a/chromium/content/browser/shared_worker/shared_worker_instance_unittest.cc b/chromium/content/browser/shared_worker/shared_worker_instance_unittest.cc
new file mode 100644
index 00000000000..83e51c171b3
--- /dev/null
+++ b/chromium/content/browser/shared_worker/shared_worker_instance_unittest.cc
@@ -0,0 +1,83 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/browser/shared_worker/shared_worker_instance.h"
+#include "content/browser/worker_host/worker_storage_partition.h"
+#include "content/public/test/test_browser_context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+class SharedWorkerInstanceTest : public testing::Test {
+ protected:
+ SharedWorkerInstanceTest()
+ : browser_context_(new TestBrowserContext()),
+ partition_(
+ new WorkerStoragePartition(browser_context_->GetRequestContext(),
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL)),
+ partition_id_(*partition_.get()) {}
+
+ bool Matches(const SharedWorkerInstance& instance,
+ const std::string& url,
+ const base::StringPiece& name) {
+ return instance.Matches(GURL(url),
+ base::ASCIIToUTF16(name),
+ partition_id_,
+ browser_context_->GetResourceContext());
+ }
+
+ scoped_ptr<TestBrowserContext> browser_context_;
+ scoped_ptr<WorkerStoragePartition> partition_;
+ const WorkerStoragePartitionId partition_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(SharedWorkerInstanceTest);
+};
+
+TEST_F(SharedWorkerInstanceTest, MatchesTest) {
+ SharedWorkerInstance instance1(GURL("http://example.com/w.js"),
+ base::string16(),
+ base::string16(),
+ blink::WebContentSecurityPolicyTypeReport,
+ browser_context_->GetResourceContext(),
+ partition_id_);
+ EXPECT_TRUE(Matches(instance1, "http://example.com/w.js", ""));
+ EXPECT_FALSE(Matches(instance1, "http://example.com/w2.js", ""));
+ EXPECT_FALSE(Matches(instance1, "http://example.net/w.js", ""));
+ EXPECT_FALSE(Matches(instance1, "http://example.net/w2.js", ""));
+ EXPECT_FALSE(Matches(instance1, "http://example.com/w.js", "name"));
+ EXPECT_FALSE(Matches(instance1, "http://example.com/w2.js", "name"));
+ EXPECT_FALSE(Matches(instance1, "http://example.net/w.js", "name"));
+ EXPECT_FALSE(Matches(instance1, "http://example.net/w2.js", "name"));
+
+ SharedWorkerInstance instance2(GURL("http://example.com/w.js"),
+ base::ASCIIToUTF16("name"),
+ base::string16(),
+ blink::WebContentSecurityPolicyTypeReport,
+ browser_context_->GetResourceContext(),
+ partition_id_);
+ EXPECT_FALSE(Matches(instance2, "http://example.com/w.js", ""));
+ EXPECT_FALSE(Matches(instance2, "http://example.com/w2.js", ""));
+ EXPECT_FALSE(Matches(instance2, "http://example.net/w.js", ""));
+ EXPECT_FALSE(Matches(instance2, "http://example.net/w2.js", ""));
+ EXPECT_TRUE(Matches(instance2, "http://example.com/w.js", "name"));
+ EXPECT_TRUE(Matches(instance2, "http://example.com/w2.js", "name"));
+ EXPECT_FALSE(Matches(instance2, "http://example.net/w.js", "name"));
+ EXPECT_FALSE(Matches(instance2, "http://example.net/w2.js", "name"));
+ EXPECT_FALSE(Matches(instance2, "http://example.com/w.js", "name2"));
+ EXPECT_FALSE(Matches(instance2, "http://example.com/w2.js", "name2"));
+ EXPECT_FALSE(Matches(instance2, "http://example.net/w.js", "name2"));
+ EXPECT_FALSE(Matches(instance2, "http://example.net/w2.js", "name2"));
+}
+
+} // namespace content
diff --git a/chromium/content/browser/shared_worker/shared_worker_message_filter.cc b/chromium/content/browser/shared_worker/shared_worker_message_filter.cc
new file mode 100644
index 00000000000..fa5f15b603f
--- /dev/null
+++ b/chromium/content/browser/shared_worker/shared_worker_message_filter.cc
@@ -0,0 +1,168 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/shared_worker/shared_worker_message_filter.h"
+
+#include "content/browser/devtools/worker_devtools_manager.h"
+#include "content/browser/message_port_message_filter.h"
+#include "content/browser/shared_worker/shared_worker_service_impl.h"
+#include "content/common/devtools_messages.h"
+#include "content/common/view_messages.h"
+#include "content/common/worker_messages.h"
+
+namespace content {
+namespace {
+const uint32 kFilteredMessageClasses[] = {
+ ViewMsgStart,
+ WorkerMsgStart,
+};
+} // namespace
+
+SharedWorkerMessageFilter::SharedWorkerMessageFilter(
+ int render_process_id,
+ ResourceContext* resource_context,
+ const WorkerStoragePartition& partition,
+ MessagePortMessageFilter* message_port_message_filter)
+ : BrowserMessageFilter(kFilteredMessageClasses,
+ arraysize(kFilteredMessageClasses)),
+ render_process_id_(render_process_id),
+ resource_context_(resource_context),
+ partition_(partition),
+ message_port_message_filter_(message_port_message_filter) {
+}
+
+SharedWorkerMessageFilter::~SharedWorkerMessageFilter() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+}
+
+void SharedWorkerMessageFilter::OnChannelClosing() {
+ SharedWorkerServiceImpl::GetInstance()->OnSharedWorkerMessageFilterClosing(
+ this);
+}
+
+bool SharedWorkerMessageFilter::OnMessageReceived(const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(SharedWorkerMessageFilter, message)
+ // Only sent from renderer for now, until we have nested workers.
+ IPC_MESSAGE_HANDLER(ViewHostMsg_CreateWorker, OnCreateWorker)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ForwardToWorker, OnForwardToWorker)
+ // Only sent from renderer.
+ IPC_MESSAGE_HANDLER(ViewHostMsg_DocumentDetached, OnDocumentDetached)
+ // Only sent from SharedWorker in renderer.
+ IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerContextClosed,
+ OnWorkerContextClosed)
+ IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerContextDestroyed,
+ OnWorkerContextDestroyed)
+ IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerScriptLoaded,
+ OnWorkerScriptLoaded)
+ IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerScriptLoadFailed,
+ OnWorkerScriptLoadFailed)
+ IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerConnected,
+ OnWorkerConnected)
+ IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_AllowDatabase, OnAllowDatabase)
+ IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_RequestFileSystemAccessSync,
+ OnRequestFileSystemAccessSync)
+ IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_AllowIndexedDB, OnAllowIndexedDB)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+int SharedWorkerMessageFilter::GetNextRoutingID() {
+ return message_port_message_filter_->GetNextRoutingID();
+}
+
+void SharedWorkerMessageFilter::OnCreateWorker(
+ const ViewHostMsg_CreateWorker_Params& params,
+ int* route_id) {
+ bool url_error = false;
+ *route_id = GetNextRoutingID();
+ SharedWorkerServiceImpl::GetInstance()->CreateWorker(
+ params,
+ *route_id,
+ this,
+ resource_context_,
+ WorkerStoragePartitionId(partition_),
+ &url_error);
+ if (url_error)
+ *route_id = MSG_ROUTING_NONE;
+}
+
+void SharedWorkerMessageFilter::OnForwardToWorker(const IPC::Message& message) {
+ SharedWorkerServiceImpl::GetInstance()->ForwardToWorker(message, this);
+}
+
+void SharedWorkerMessageFilter::OnDocumentDetached(
+ unsigned long long document_id) {
+ SharedWorkerServiceImpl::GetInstance()->DocumentDetached(document_id, this);
+}
+
+void SharedWorkerMessageFilter::OnWorkerContextClosed(int worker_route_id) {
+ SharedWorkerServiceImpl::GetInstance()->WorkerContextClosed(worker_route_id,
+ this);
+}
+
+void SharedWorkerMessageFilter::OnWorkerContextDestroyed(int worker_route_id) {
+ SharedWorkerServiceImpl::GetInstance()->WorkerContextDestroyed(
+ worker_route_id,
+ this);
+}
+
+void SharedWorkerMessageFilter::OnWorkerScriptLoaded(int worker_route_id) {
+ SharedWorkerServiceImpl::GetInstance()->WorkerScriptLoaded(worker_route_id,
+ this);
+}
+
+void SharedWorkerMessageFilter::OnWorkerScriptLoadFailed(int worker_route_id) {
+ SharedWorkerServiceImpl::GetInstance()->WorkerScriptLoadFailed(
+ worker_route_id,
+ this);
+}
+
+void SharedWorkerMessageFilter::OnWorkerConnected(int message_port_id,
+ int worker_route_id) {
+ SharedWorkerServiceImpl::GetInstance()->WorkerConnected(
+ message_port_id,
+ worker_route_id,
+ this);
+}
+
+void SharedWorkerMessageFilter::OnAllowDatabase(
+ int worker_route_id,
+ const GURL& url,
+ const base::string16& name,
+ const base::string16& display_name,
+ unsigned long estimated_size,
+ bool* result) {
+ SharedWorkerServiceImpl::GetInstance()->AllowDatabase(worker_route_id,
+ url,
+ name,
+ display_name,
+ estimated_size,
+ result,
+ this);
+}
+
+void SharedWorkerMessageFilter::OnRequestFileSystemAccessSync(
+ int worker_route_id,
+ const GURL& url,
+ bool* result) {
+ SharedWorkerServiceImpl::GetInstance()->AllowFileSystem(worker_route_id,
+ url,
+ result,
+ this);
+}
+
+void SharedWorkerMessageFilter::OnAllowIndexedDB(int worker_route_id,
+ const GURL& url,
+ const base::string16& name,
+ bool* result) {
+ SharedWorkerServiceImpl::GetInstance()->AllowIndexedDB(worker_route_id,
+ url,
+ name,
+ result,
+ this);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/shared_worker/shared_worker_message_filter.h b/chromium/content/browser/shared_worker/shared_worker_message_filter.h
new file mode 100644
index 00000000000..8fb927b805e
--- /dev/null
+++ b/chromium/content/browser/shared_worker/shared_worker_message_filter.h
@@ -0,0 +1,78 @@
+// 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_SHARED_WORKER_SHARED_WORKER_MESSAGE_FILTER_H_
+#define CONTENT_BROWSER_SHARED_WORKER_SHARED_WORKER_MESSAGE_FILTER_H_
+
+#include "content/browser/worker_host/worker_storage_partition.h"
+#include "content/common/content_export.h"
+#include "content/public/browser/browser_message_filter.h"
+
+class GURL;
+struct ViewHostMsg_CreateWorker_Params;
+
+namespace content {
+class MessagePortMessageFilter;
+class ResourceContext;
+
+// If "enable-embedded-shared-worker" is set this class will be used instead of
+// WorkerMessageFilter.
+class CONTENT_EXPORT SharedWorkerMessageFilter : public BrowserMessageFilter {
+ public:
+ SharedWorkerMessageFilter(int render_process_id,
+ ResourceContext* resource_context,
+ const WorkerStoragePartition& partition,
+ MessagePortMessageFilter* message_port_filter);
+
+ // BrowserMessageFilter implementation.
+ virtual void OnChannelClosing() OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+
+ int GetNextRoutingID();
+ int render_process_id() const { return render_process_id_; }
+
+ MessagePortMessageFilter* message_port_message_filter() const {
+ return message_port_message_filter_;
+ }
+
+ protected:
+ // This is protected, so we can define sub classes for testing.
+ virtual ~SharedWorkerMessageFilter();
+
+ private:
+ // Message handlers.
+ void OnCreateWorker(const ViewHostMsg_CreateWorker_Params& params,
+ int* route_id);
+ void OnForwardToWorker(const IPC::Message& message);
+ void OnDocumentDetached(unsigned long long document_id);
+ void OnWorkerContextClosed(int worker_route_id);
+ void OnWorkerContextDestroyed(int worker_route_id);
+ void OnWorkerScriptLoaded(int worker_route_id);
+ void OnWorkerScriptLoadFailed(int worker_route_id);
+ void OnWorkerConnected(int message_port_id, int worker_route_id);
+ void OnAllowDatabase(int worker_route_id,
+ const GURL& url,
+ const base::string16& name,
+ const base::string16& display_name,
+ unsigned long estimated_size,
+ bool* result);
+ void OnRequestFileSystemAccessSync(int worker_route_id,
+ const GURL& url,
+ bool* result);
+ void OnAllowIndexedDB(int worker_route_id,
+ const GURL& url,
+ const base::string16& name,
+ bool* result);
+
+ const int render_process_id_;
+ ResourceContext* const resource_context_;
+ const WorkerStoragePartition partition_;
+ MessagePortMessageFilter* const message_port_message_filter_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(SharedWorkerMessageFilter);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_SHARED_WORKER_SHARED_WORKER_MESSAGE_FILTER_H_
diff --git a/chromium/content/browser/shared_worker/shared_worker_service_impl.cc b/chromium/content/browser/shared_worker/shared_worker_service_impl.cc
new file mode 100644
index 00000000000..4a689fa812f
--- /dev/null
+++ b/chromium/content/browser/shared_worker/shared_worker_service_impl.cc
@@ -0,0 +1,657 @@
+// 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/shared_worker/shared_worker_service_impl.h"
+
+#include <algorithm>
+#include <iterator>
+#include <set>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "content/browser/devtools/embedded_worker_devtools_manager.h"
+#include "content/browser/renderer_host/render_process_host_impl.h"
+#include "content/browser/shared_worker/shared_worker_host.h"
+#include "content/browser/shared_worker/shared_worker_instance.h"
+#include "content/browser/shared_worker/shared_worker_message_filter.h"
+#include "content/browser/worker_host/worker_document_set.h"
+#include "content/common/view_messages.h"
+#include "content/common/worker_messages.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/worker_service_observer.h"
+
+namespace content {
+namespace {
+
+class ScopedWorkerDependencyChecker {
+ public:
+ explicit ScopedWorkerDependencyChecker(SharedWorkerServiceImpl* service)
+ : service_(service) {}
+ ScopedWorkerDependencyChecker(SharedWorkerServiceImpl* service,
+ base::Closure done_closure)
+ : service_(service), done_closure_(done_closure) {}
+ ~ScopedWorkerDependencyChecker() {
+ service_->CheckWorkerDependency();
+ if (!done_closure_.is_null())
+ done_closure_.Run();
+ }
+
+ private:
+ SharedWorkerServiceImpl* service_;
+ base::Closure done_closure_;
+ DISALLOW_COPY_AND_ASSIGN(ScopedWorkerDependencyChecker);
+};
+
+void UpdateWorkerDependencyOnUI(const std::vector<int>& added_ids,
+ const std::vector<int>& removed_ids) {
+ for (size_t i = 0; i < added_ids.size(); ++i) {
+ RenderProcessHostImpl* render_process_host_impl =
+ static_cast<RenderProcessHostImpl*>(
+ RenderProcessHost::FromID(added_ids[i]));
+ if (!render_process_host_impl)
+ continue;
+ render_process_host_impl->IncrementWorkerRefCount();
+ }
+ for (size_t i = 0; i < removed_ids.size(); ++i) {
+ RenderProcessHostImpl* render_process_host_impl =
+ static_cast<RenderProcessHostImpl*>(
+ RenderProcessHost::FromID(removed_ids[i]));
+ if (!render_process_host_impl)
+ continue;
+ render_process_host_impl->DecrementWorkerRefCount();
+ }
+}
+
+void UpdateWorkerDependency(const std::vector<int>& added_ids,
+ const std::vector<int>& removed_ids) {
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&UpdateWorkerDependencyOnUI, added_ids, removed_ids));
+}
+
+void DecrementWorkerRefCount(int process_id) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ BrowserThread::PostTask(BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&DecrementWorkerRefCount, process_id));
+ return;
+ }
+ RenderProcessHostImpl* render_process_host_impl =
+ static_cast<RenderProcessHostImpl*>(
+ RenderProcessHost::FromID(process_id));
+ if (render_process_host_impl)
+ render_process_host_impl->DecrementWorkerRefCount();
+}
+
+bool TryIncrementWorkerRefCount(int worker_process_id) {
+ RenderProcessHostImpl* render_process = static_cast<RenderProcessHostImpl*>(
+ RenderProcessHost::FromID(worker_process_id));
+ if (!render_process || render_process->FastShutdownStarted()) {
+ return false;
+ }
+ render_process->IncrementWorkerRefCount();
+ return true;
+}
+
+} // namespace
+
+class SharedWorkerServiceImpl::SharedWorkerPendingInstance {
+ public:
+ struct SharedWorkerPendingRequest {
+ SharedWorkerPendingRequest(SharedWorkerMessageFilter* filter,
+ int route_id,
+ unsigned long long document_id,
+ int render_process_id,
+ int render_frame_route_id)
+ : filter(filter),
+ route_id(route_id),
+ document_id(document_id),
+ render_process_id(render_process_id),
+ render_frame_route_id(render_frame_route_id) {}
+ SharedWorkerMessageFilter* const filter;
+ const int route_id;
+ const unsigned long long document_id;
+ const int render_process_id;
+ const int render_frame_route_id;
+ };
+
+ typedef ScopedVector<SharedWorkerPendingRequest> SharedWorkerPendingRequests;
+
+ explicit SharedWorkerPendingInstance(
+ scoped_ptr<SharedWorkerInstance> instance)
+ : instance_(instance.Pass()) {}
+ ~SharedWorkerPendingInstance() {}
+ SharedWorkerInstance* instance() { return instance_.get(); }
+ SharedWorkerInstance* release_instance() { return instance_.release(); }
+ SharedWorkerPendingRequests* requests() { return &requests_; }
+ SharedWorkerMessageFilter* FindFilter(int process_id) {
+ for (size_t i = 0; i < requests_.size(); ++i) {
+ if (requests_[i]->render_process_id == process_id)
+ return requests_[i]->filter;
+ }
+ return NULL;
+ }
+ void AddRequest(scoped_ptr<SharedWorkerPendingRequest> request_info) {
+ requests_.push_back(request_info.release());
+ }
+ void RemoveRequest(int process_id) {
+ for (SharedWorkerPendingRequests::iterator request_itr = requests_.begin();
+ request_itr != requests_.end();) {
+ if ((*request_itr)->render_process_id == process_id)
+ request_itr = requests_.erase(request_itr);
+ else
+ ++request_itr;
+ }
+ }
+ void RegisterToSharedWorkerHost(SharedWorkerHost* host) {
+ for (size_t i = 0; i < requests_.size(); ++i) {
+ SharedWorkerPendingRequest* request = requests_[i];
+ host->AddFilter(request->filter, request->route_id);
+ host->worker_document_set()->Add(request->filter,
+ request->document_id,
+ request->render_process_id,
+ request->render_frame_route_id);
+ }
+ }
+ void SendWorkerCreatedMessages() {
+ for (size_t i = 0; i < requests_.size(); ++i) {
+ SharedWorkerPendingRequest* request = requests_[i];
+ request->filter->Send(new ViewMsg_WorkerCreated(request->route_id));
+ }
+ }
+
+ private:
+ scoped_ptr<SharedWorkerInstance> instance_;
+ SharedWorkerPendingRequests requests_;
+ DISALLOW_COPY_AND_ASSIGN(SharedWorkerPendingInstance);
+};
+
+class SharedWorkerServiceImpl::SharedWorkerReserver
+ : public base::RefCountedThreadSafe<SharedWorkerReserver> {
+ public:
+ SharedWorkerReserver(int pending_instance_id,
+ int worker_process_id,
+ int worker_route_id,
+ bool is_new_worker,
+ const SharedWorkerInstance& instance)
+ : worker_process_id_(worker_process_id),
+ worker_route_id_(worker_route_id),
+ is_new_worker_(is_new_worker),
+ instance_(instance) {}
+
+ void TryReserve(const base::Callback<void(bool)>& success_cb,
+ const base::Closure& failure_cb,
+ bool (*try_increment_worker_ref_count)(int)) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (!try_increment_worker_ref_count(worker_process_id_)) {
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, failure_cb);
+ return;
+ }
+ bool pause_on_start = false;
+ if (is_new_worker_) {
+ pause_on_start =
+ EmbeddedWorkerDevToolsManager::GetInstance()->SharedWorkerCreated(
+ worker_process_id_, worker_route_id_, instance_);
+ }
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE, base::Bind(success_cb, pause_on_start));
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<SharedWorkerReserver>;
+ ~SharedWorkerReserver() {}
+
+ const int worker_process_id_;
+ const int worker_route_id_;
+ const bool is_new_worker_;
+ const SharedWorkerInstance instance_;
+};
+
+// static
+bool (*SharedWorkerServiceImpl::s_try_increment_worker_ref_count_)(int) =
+ TryIncrementWorkerRefCount;
+
+SharedWorkerServiceImpl* SharedWorkerServiceImpl::GetInstance() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ return Singleton<SharedWorkerServiceImpl>::get();
+}
+
+SharedWorkerServiceImpl::SharedWorkerServiceImpl()
+ : update_worker_dependency_(UpdateWorkerDependency),
+ next_pending_instance_id_(0) {
+}
+
+SharedWorkerServiceImpl::~SharedWorkerServiceImpl() {}
+
+void SharedWorkerServiceImpl::ResetForTesting() {
+ last_worker_depended_renderers_.clear();
+ worker_hosts_.clear();
+ observers_.Clear();
+ update_worker_dependency_ = UpdateWorkerDependency;
+ s_try_increment_worker_ref_count_ = TryIncrementWorkerRefCount;
+}
+
+bool SharedWorkerServiceImpl::TerminateWorker(int process_id, int route_id) {
+ SharedWorkerHost* host =
+ worker_hosts_.get(std::make_pair(process_id, route_id));
+ if (!host || !host->instance())
+ return false;
+ host->TerminateWorker();
+ return true;
+}
+
+std::vector<WorkerService::WorkerInfo> SharedWorkerServiceImpl::GetWorkers() {
+ std::vector<WorkerService::WorkerInfo> results;
+ for (WorkerHostMap::const_iterator iter = worker_hosts_.begin();
+ iter != worker_hosts_.end();
+ ++iter) {
+ SharedWorkerHost* host = iter->second;
+ const SharedWorkerInstance* instance = host->instance();
+ if (instance) {
+ WorkerService::WorkerInfo info;
+ info.url = instance->url();
+ info.name = instance->name();
+ info.route_id = host->worker_route_id();
+ info.process_id = host->process_id();
+ info.handle = host->container_render_filter()->PeerHandle();
+ results.push_back(info);
+ }
+ }
+ return results;
+}
+
+void SharedWorkerServiceImpl::AddObserver(WorkerServiceObserver* observer) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ observers_.AddObserver(observer);
+}
+
+void SharedWorkerServiceImpl::RemoveObserver(WorkerServiceObserver* observer) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ observers_.RemoveObserver(observer);
+}
+
+void SharedWorkerServiceImpl::CreateWorker(
+ const ViewHostMsg_CreateWorker_Params& params,
+ int route_id,
+ SharedWorkerMessageFilter* filter,
+ ResourceContext* resource_context,
+ const WorkerStoragePartitionId& partition_id,
+ bool* url_mismatch) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ *url_mismatch = false;
+ scoped_ptr<SharedWorkerInstance> instance(
+ new SharedWorkerInstance(params.url,
+ params.name,
+ params.content_security_policy,
+ params.security_policy_type,
+ resource_context,
+ partition_id));
+ scoped_ptr<SharedWorkerPendingInstance::SharedWorkerPendingRequest> request(
+ new SharedWorkerPendingInstance::SharedWorkerPendingRequest(
+ filter,
+ route_id,
+ params.document_id,
+ filter->render_process_id(),
+ params.render_frame_route_id));
+ if (SharedWorkerPendingInstance* pending = FindPendingInstance(*instance)) {
+ if (params.url != pending->instance()->url()) {
+ *url_mismatch = true;
+ return;
+ }
+ pending->AddRequest(request.Pass());
+ return;
+ }
+ scoped_ptr<SharedWorkerPendingInstance> pending_instance(
+ new SharedWorkerPendingInstance(instance.Pass()));
+ pending_instance->AddRequest(request.Pass());
+ ReserveRenderProcessToCreateWorker(pending_instance.Pass(), url_mismatch);
+}
+
+void SharedWorkerServiceImpl::ForwardToWorker(
+ const IPC::Message& message,
+ SharedWorkerMessageFilter* filter) {
+ for (WorkerHostMap::const_iterator iter = worker_hosts_.begin();
+ iter != worker_hosts_.end();
+ ++iter) {
+ if (iter->second->FilterMessage(message, filter))
+ return;
+ }
+}
+
+void SharedWorkerServiceImpl::DocumentDetached(
+ unsigned long long document_id,
+ SharedWorkerMessageFilter* filter) {
+ ScopedWorkerDependencyChecker checker(this);
+ for (WorkerHostMap::const_iterator iter = worker_hosts_.begin();
+ iter != worker_hosts_.end();
+ ++iter) {
+ iter->second->DocumentDetached(filter, document_id);
+ }
+}
+
+void SharedWorkerServiceImpl::WorkerContextClosed(
+ int worker_route_id,
+ SharedWorkerMessageFilter* filter) {
+ ScopedWorkerDependencyChecker checker(this);
+ if (SharedWorkerHost* host = FindSharedWorkerHost(filter, worker_route_id))
+ host->WorkerContextClosed();
+}
+
+void SharedWorkerServiceImpl::WorkerContextDestroyed(
+ int worker_route_id,
+ SharedWorkerMessageFilter* filter) {
+ ScopedWorkerDependencyChecker checker(this);
+ scoped_ptr<SharedWorkerHost> host =
+ worker_hosts_.take_and_erase(std::make_pair(filter->render_process_id(),
+ worker_route_id));
+ if (!host)
+ return;
+ host->WorkerContextDestroyed();
+}
+
+void SharedWorkerServiceImpl::WorkerScriptLoaded(
+ int worker_route_id,
+ SharedWorkerMessageFilter* filter) {
+ if (SharedWorkerHost* host = FindSharedWorkerHost(filter, worker_route_id))
+ host->WorkerScriptLoaded();
+}
+
+void SharedWorkerServiceImpl::WorkerScriptLoadFailed(
+ int worker_route_id,
+ SharedWorkerMessageFilter* filter) {
+ ScopedWorkerDependencyChecker checker(this);
+ scoped_ptr<SharedWorkerHost> host =
+ worker_hosts_.take_and_erase(std::make_pair(filter->render_process_id(),
+ worker_route_id));
+ if (!host)
+ return;
+ host->WorkerScriptLoadFailed();
+}
+
+void SharedWorkerServiceImpl::WorkerConnected(
+ int message_port_id,
+ int worker_route_id,
+ SharedWorkerMessageFilter* filter) {
+ if (SharedWorkerHost* host = FindSharedWorkerHost(filter, worker_route_id))
+ host->WorkerConnected(message_port_id);
+}
+
+void SharedWorkerServiceImpl::AllowDatabase(
+ int worker_route_id,
+ const GURL& url,
+ const base::string16& name,
+ const base::string16& display_name,
+ unsigned long estimated_size,
+ bool* result,
+ SharedWorkerMessageFilter* filter) {
+ if (SharedWorkerHost* host = FindSharedWorkerHost(filter, worker_route_id))
+ host->AllowDatabase(url, name, display_name, estimated_size, result);
+}
+
+void SharedWorkerServiceImpl::AllowFileSystem(
+ int worker_route_id,
+ const GURL& url,
+ bool* result,
+ SharedWorkerMessageFilter* filter) {
+ if (SharedWorkerHost* host = FindSharedWorkerHost(filter, worker_route_id))
+ host->AllowFileSystem(url, result);
+}
+
+void SharedWorkerServiceImpl::AllowIndexedDB(
+ int worker_route_id,
+ const GURL& url,
+ const base::string16& name,
+ bool* result,
+ SharedWorkerMessageFilter* filter) {
+ if (SharedWorkerHost* host = FindSharedWorkerHost(filter, worker_route_id))
+ host->AllowIndexedDB(url, name, result);
+}
+
+void SharedWorkerServiceImpl::OnSharedWorkerMessageFilterClosing(
+ SharedWorkerMessageFilter* filter) {
+ ScopedWorkerDependencyChecker checker(this);
+ std::vector<ProcessRouteIdPair> remove_list;
+ for (WorkerHostMap::iterator iter = worker_hosts_.begin();
+ iter != worker_hosts_.end();
+ ++iter) {
+ iter->second->FilterShutdown(filter);
+ if (iter->first.first == filter->render_process_id())
+ remove_list.push_back(iter->first);
+ }
+ for (size_t i = 0; i < remove_list.size(); ++i) {
+ scoped_ptr<SharedWorkerHost> host =
+ worker_hosts_.take_and_erase(remove_list[i]);
+ }
+
+ std::vector<int> remove_pending_instance_list;
+ for (PendingInstaneMap::iterator iter = pending_instances_.begin();
+ iter != pending_instances_.end();
+ ++iter) {
+ iter->second->RemoveRequest(filter->render_process_id());
+ if (!iter->second->requests()->size())
+ remove_pending_instance_list.push_back(iter->first);
+ }
+ for (size_t i = 0; i < remove_pending_instance_list.size(); ++i)
+ pending_instances_.take_and_erase(remove_pending_instance_list[i]);
+}
+
+void SharedWorkerServiceImpl::NotifyWorkerDestroyed(int worker_process_id,
+ int worker_route_id) {
+ FOR_EACH_OBSERVER(WorkerServiceObserver,
+ observers_,
+ WorkerDestroyed(worker_process_id, worker_route_id));
+}
+
+void SharedWorkerServiceImpl::ReserveRenderProcessToCreateWorker(
+ scoped_ptr<SharedWorkerPendingInstance> pending_instance,
+ bool* url_mismatch) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK(!FindPendingInstance(*pending_instance->instance()));
+ if (url_mismatch)
+ *url_mismatch = false;
+ if (!pending_instance->requests()->size())
+ return;
+ int worker_process_id = -1;
+ int worker_route_id = MSG_ROUTING_NONE;
+ bool is_new_worker = true;
+ SharedWorkerHost* host = FindSharedWorkerHost(*pending_instance->instance());
+ if (host) {
+ if (pending_instance->instance()->url() != host->instance()->url()) {
+ if (url_mismatch)
+ *url_mismatch = true;
+ return;
+ }
+ worker_process_id = host->process_id();
+ worker_route_id = host->worker_route_id();
+ is_new_worker = false;
+ } else {
+ SharedWorkerMessageFilter* first_filter =
+ (*pending_instance->requests()->begin())->filter;
+ worker_process_id = first_filter->render_process_id();
+ worker_route_id = first_filter->GetNextRoutingID();
+ }
+ const int pending_instance_id = next_pending_instance_id_++;
+ scoped_refptr<SharedWorkerReserver> reserver(
+ new SharedWorkerReserver(pending_instance_id,
+ worker_process_id,
+ worker_route_id,
+ is_new_worker,
+ *pending_instance->instance()));
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(
+ &SharedWorkerReserver::TryReserve,
+ reserver,
+ base::Bind(&SharedWorkerServiceImpl::RenderProcessReservedCallback,
+ base::Unretained(this),
+ pending_instance_id,
+ worker_process_id,
+ worker_route_id,
+ is_new_worker),
+ base::Bind(
+ &SharedWorkerServiceImpl::RenderProcessReserveFailedCallback,
+ base::Unretained(this),
+ pending_instance_id,
+ worker_process_id,
+ worker_route_id,
+ is_new_worker),
+ s_try_increment_worker_ref_count_));
+ pending_instances_.set(pending_instance_id, pending_instance.Pass());
+}
+
+void SharedWorkerServiceImpl::RenderProcessReservedCallback(
+ int pending_instance_id,
+ int worker_process_id,
+ int worker_route_id,
+ bool is_new_worker,
+ bool pause_on_start) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ // To offset the TryIncrementWorkerRefCount called for the reservation,
+ // calls DecrementWorkerRefCount after CheckWorkerDependency in
+ // ScopeWorkerDependencyChecker's destructor.
+ ScopedWorkerDependencyChecker checker(
+ this, base::Bind(&DecrementWorkerRefCount, worker_process_id));
+ scoped_ptr<SharedWorkerPendingInstance> pending_instance =
+ pending_instances_.take_and_erase(pending_instance_id);
+ if (!pending_instance)
+ return;
+ if (!is_new_worker) {
+ SharedWorkerHost* existing_host =
+ worker_hosts_.get(std::make_pair(worker_process_id, worker_route_id));
+ if (!existing_host) {
+ // Retry reserving a renderer process if the existed Shared Worker was
+ // destroyed on IO thread while reserving the renderer process on UI
+ // thread.
+ ReserveRenderProcessToCreateWorker(pending_instance.Pass(), NULL);
+ return;
+ }
+ pending_instance->RegisterToSharedWorkerHost(existing_host);
+ pending_instance->SendWorkerCreatedMessages();
+ return;
+ }
+ SharedWorkerMessageFilter* filter =
+ pending_instance->FindFilter(worker_process_id);
+ if (!filter) {
+ pending_instance->RemoveRequest(worker_process_id);
+ // Retry reserving a renderer process if the requested renderer process was
+ // destroyed on IO thread while reserving the renderer process on UI thread.
+ ReserveRenderProcessToCreateWorker(pending_instance.Pass(), NULL);
+ return;
+ }
+ scoped_ptr<SharedWorkerHost> host(new SharedWorkerHost(
+ pending_instance->release_instance(), filter, worker_route_id));
+ pending_instance->RegisterToSharedWorkerHost(host.get());
+ const GURL url = host->instance()->url();
+ const base::string16 name = host->instance()->name();
+ host->Start(pause_on_start);
+ worker_hosts_.set(std::make_pair(worker_process_id, worker_route_id),
+ host.Pass());
+ FOR_EACH_OBSERVER(
+ WorkerServiceObserver,
+ observers_,
+ WorkerCreated(url, name, worker_process_id, worker_route_id));
+}
+
+void SharedWorkerServiceImpl::RenderProcessReserveFailedCallback(
+ int pending_instance_id,
+ int worker_process_id,
+ int worker_route_id,
+ bool is_new_worker) {
+ worker_hosts_.take_and_erase(
+ std::make_pair(worker_process_id, worker_route_id));
+ scoped_ptr<SharedWorkerPendingInstance> pending_instance =
+ pending_instances_.take_and_erase(pending_instance_id);
+ if (!pending_instance)
+ return;
+ pending_instance->RemoveRequest(worker_process_id);
+ // Retry reserving a renderer process.
+ ReserveRenderProcessToCreateWorker(pending_instance.Pass(), NULL);
+}
+
+SharedWorkerHost* SharedWorkerServiceImpl::FindSharedWorkerHost(
+ SharedWorkerMessageFilter* filter,
+ int worker_route_id) {
+ return worker_hosts_.get(std::make_pair(filter->render_process_id(),
+ worker_route_id));
+}
+
+SharedWorkerHost* SharedWorkerServiceImpl::FindSharedWorkerHost(
+ const SharedWorkerInstance& instance) {
+ for (WorkerHostMap::const_iterator iter = worker_hosts_.begin();
+ iter != worker_hosts_.end();
+ ++iter) {
+ SharedWorkerHost* host = iter->second;
+ if (host->instance() && !host->closed() &&
+ host->instance()->Matches(instance)) {
+ return iter->second;
+ }
+ }
+ return NULL;
+}
+
+SharedWorkerServiceImpl::SharedWorkerPendingInstance*
+SharedWorkerServiceImpl::FindPendingInstance(
+ const SharedWorkerInstance& instance) {
+ for (PendingInstaneMap::iterator iter = pending_instances_.begin();
+ iter != pending_instances_.end();
+ ++iter) {
+ if (iter->second->instance()->Matches(instance))
+ return iter->second;
+ }
+ return NULL;
+}
+
+const std::set<int>
+SharedWorkerServiceImpl::GetRenderersWithWorkerDependency() {
+ std::set<int> dependent_renderers;
+ for (WorkerHostMap::iterator host_iter = worker_hosts_.begin();
+ host_iter != worker_hosts_.end();
+ ++host_iter) {
+ const int process_id = host_iter->first.first;
+ if (dependent_renderers.count(process_id))
+ continue;
+ if (host_iter->second->instance() &&
+ host_iter->second->worker_document_set()->ContainsExternalRenderer(
+ process_id)) {
+ dependent_renderers.insert(process_id);
+ }
+ }
+ return dependent_renderers;
+}
+
+void SharedWorkerServiceImpl::CheckWorkerDependency() {
+ const std::set<int> current_worker_depended_renderers =
+ GetRenderersWithWorkerDependency();
+ std::vector<int> added_items;
+ std::vector<int> removed_items;
+ std::set_difference(current_worker_depended_renderers.begin(),
+ current_worker_depended_renderers.end(),
+ last_worker_depended_renderers_.begin(),
+ last_worker_depended_renderers_.end(),
+ std::back_inserter(added_items));
+ std::set_difference(last_worker_depended_renderers_.begin(),
+ last_worker_depended_renderers_.end(),
+ current_worker_depended_renderers.begin(),
+ current_worker_depended_renderers.end(),
+ std::back_inserter(removed_items));
+ if (!added_items.empty() || !removed_items.empty()) {
+ last_worker_depended_renderers_ = current_worker_depended_renderers;
+ update_worker_dependency_(added_items, removed_items);
+ }
+}
+
+void SharedWorkerServiceImpl::ChangeUpdateWorkerDependencyFuncForTesting(
+ UpdateWorkerDependencyFunc new_func) {
+ update_worker_dependency_ = new_func;
+}
+
+void SharedWorkerServiceImpl::ChangeTryIncrementWorkerRefCountFuncForTesting(
+ bool (*new_func)(int)) {
+ s_try_increment_worker_ref_count_ = new_func;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/shared_worker/shared_worker_service_impl.h b/chromium/content/browser/shared_worker/shared_worker_service_impl.h
new file mode 100644
index 00000000000..e97c259493e
--- /dev/null
+++ b/chromium/content/browser/shared_worker/shared_worker_service_impl.h
@@ -0,0 +1,176 @@
+// 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_SHARED_WORKER_SHARED_WORKER_SERVICE_IMPL_H_
+#define CONTENT_BROWSER_SHARED_WORKER_SHARED_WORKER_SERVICE_IMPL_H_
+
+#include <set>
+
+#include "base/compiler_specific.h"
+#include "base/containers/scoped_ptr_hash_map.h"
+#include "base/memory/scoped_vector.h"
+#include "base/memory/singleton.h"
+#include "base/observer_list.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/worker_service.h"
+
+struct ViewHostMsg_CreateWorker_Params;
+
+namespace IPC {
+class Message;
+}
+
+namespace content {
+
+class SharedWorkerInstance;
+class SharedWorkerHost;
+class SharedWorkerMessageFilter;
+class ResourceContext;
+class WorkerServiceObserver;
+class WorkerStoragePartitionId;
+
+// If "enable-embedded-shared-worker" is set this class will be used instead of
+// WorkerServiceImpl.
+// TODO(horo): implement this class.
+class CONTENT_EXPORT SharedWorkerServiceImpl
+ : public NON_EXPORTED_BASE(WorkerService) {
+ public:
+ // Returns the SharedWorkerServiceImpl singleton.
+ static SharedWorkerServiceImpl* GetInstance();
+
+ // WorkerService implementation:
+ virtual bool TerminateWorker(int process_id, int route_id) OVERRIDE;
+ virtual std::vector<WorkerInfo> GetWorkers() OVERRIDE;
+ virtual void AddObserver(WorkerServiceObserver* observer) OVERRIDE;
+ virtual void RemoveObserver(WorkerServiceObserver* observer) OVERRIDE;
+
+ // These methods correspond to worker related IPCs.
+ void CreateWorker(const ViewHostMsg_CreateWorker_Params& params,
+ int route_id,
+ SharedWorkerMessageFilter* filter,
+ ResourceContext* resource_context,
+ const WorkerStoragePartitionId& partition_id,
+ bool* url_mismatch);
+ void ForwardToWorker(const IPC::Message& message,
+ SharedWorkerMessageFilter* filter);
+ void DocumentDetached(unsigned long long document_id,
+ SharedWorkerMessageFilter* filter);
+ void WorkerContextClosed(int worker_route_id,
+ SharedWorkerMessageFilter* filter);
+ void WorkerContextDestroyed(int worker_route_id,
+ SharedWorkerMessageFilter* filter);
+ void WorkerScriptLoaded(int worker_route_id,
+ SharedWorkerMessageFilter* filter);
+ void WorkerScriptLoadFailed(int worker_route_id,
+ SharedWorkerMessageFilter* filter);
+ void WorkerConnected(int message_port_id,
+ int worker_route_id,
+ SharedWorkerMessageFilter* filter);
+ void AllowDatabase(int worker_route_id,
+ const GURL& url,
+ const base::string16& name,
+ const base::string16& display_name,
+ unsigned long estimated_size,
+ bool* result,
+ SharedWorkerMessageFilter* filter);
+ void AllowFileSystem(int worker_route_id,
+ const GURL& url,
+ bool* result,
+ SharedWorkerMessageFilter* filter);
+ void AllowIndexedDB(int worker_route_id,
+ const GURL& url,
+ const base::string16& name,
+ bool* result,
+ SharedWorkerMessageFilter* filter);
+
+ void OnSharedWorkerMessageFilterClosing(
+ SharedWorkerMessageFilter* filter);
+
+ // Checks the worker dependency of renderer processes and calls
+ // IncrementWorkerRefCount and DecrementWorkerRefCount of
+ // RenderProcessHostImpl on UI thread if necessary.
+ void CheckWorkerDependency();
+
+ void NotifyWorkerDestroyed(int worker_process_id, int worker_route_id);
+
+ private:
+ class SharedWorkerPendingInstance;
+ class SharedWorkerReserver;
+
+ friend struct DefaultSingletonTraits<SharedWorkerServiceImpl>;
+ friend class SharedWorkerServiceImplTest;
+
+ typedef void (*UpdateWorkerDependencyFunc)(const std::vector<int>&,
+ const std::vector<int>&);
+ typedef bool (*TryIncrementWorkerRefCountFunc)(bool);
+ // Pair of render_process_id and worker_route_id.
+ typedef std::pair<int, int> ProcessRouteIdPair;
+ typedef base::ScopedPtrHashMap<ProcessRouteIdPair, SharedWorkerHost>
+ WorkerHostMap;
+ typedef base::ScopedPtrHashMap<int, SharedWorkerPendingInstance>
+ PendingInstaneMap;
+
+ SharedWorkerServiceImpl();
+ virtual ~SharedWorkerServiceImpl();
+
+ void ResetForTesting();
+
+ // Reserves the render process to create Shared Worker. This reservation
+ // procedure will be executed on UI thread and
+ // RenderProcessReservedCallback() or RenderProcessReserveFailedCallback()
+ // will be called on IO thread.
+ void ReserveRenderProcessToCreateWorker(
+ scoped_ptr<SharedWorkerPendingInstance> pending_instance,
+ bool* url_mismatch);
+
+ // Called after the render process is reserved to create Shared Worker in it.
+ void RenderProcessReservedCallback(int pending_instance_id,
+ int worker_process_id,
+ int worker_route_id,
+ bool is_new_worker,
+ bool pause_on_start);
+
+ // Called after the fast shutdown is detected while reserving the render
+ // process to create Shared Worker in it.
+ void RenderProcessReserveFailedCallback(int pending_instance_id,
+ int worker_process_id,
+ int worker_route_id,
+ bool is_new_worker);
+
+ SharedWorkerHost* FindSharedWorkerHost(
+ SharedWorkerMessageFilter* filter,
+ int worker_route_id);
+
+ SharedWorkerHost* FindSharedWorkerHost(const SharedWorkerInstance& instance);
+ SharedWorkerPendingInstance* FindPendingInstance(
+ const SharedWorkerInstance& instance);
+
+ // Returns the IDs of the renderer processes which are executing
+ // SharedWorkers connected to other renderer processes.
+ const std::set<int> GetRenderersWithWorkerDependency();
+
+ void ChangeUpdateWorkerDependencyFuncForTesting(
+ UpdateWorkerDependencyFunc new_func);
+ void ChangeTryIncrementWorkerRefCountFuncForTesting(bool (*new_func)(int));
+
+ std::set<int> last_worker_depended_renderers_;
+ // Function ptr to update worker dependency, tests may override this.
+ UpdateWorkerDependencyFunc update_worker_dependency_;
+
+ // Function ptr to increment worker ref count, tests may override this.
+ static bool (*s_try_increment_worker_ref_count_)(int);
+
+ WorkerHostMap worker_hosts_;
+ PendingInstaneMap pending_instances_;
+ int next_pending_instance_id_;
+
+ ObserverList<WorkerServiceObserver> observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(SharedWorkerServiceImpl);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_SHARED_WORKER_SHARED_WORKER_SERVICE_IMPL_H_
diff --git a/chromium/content/browser/shared_worker/shared_worker_service_impl_unittest.cc b/chromium/content/browser/shared_worker/shared_worker_service_impl_unittest.cc
new file mode 100644
index 00000000000..e08da83efb2
--- /dev/null
+++ b/chromium/content/browser/shared_worker/shared_worker_service_impl_unittest.cc
@@ -0,0 +1,905 @@
+// 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 <map>
+#include <set>
+#include <vector>
+
+#include "base/atomic_sequence_num.h"
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/synchronization/lock.h"
+#include "content/browser/message_port_message_filter.h"
+#include "content/browser/shared_worker/shared_worker_message_filter.h"
+#include "content/browser/shared_worker/shared_worker_service_impl.h"
+#include "content/browser/worker_host/worker_storage_partition.h"
+#include "content/common/message_port_messages.h"
+#include "content/common/view_messages.h"
+#include "content/common/worker_messages.h"
+#include "content/public/test/test_browser_context.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "content/public/test/test_utils.h"
+#include "ipc/ipc_sync_message.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+class SharedWorkerServiceImplTest : public testing::Test {
+ public:
+ static void RegisterRunningProcessID(int process_id) {
+ base::AutoLock lock(s_lock_);
+ s_running_process_id_set_.insert(process_id);
+ }
+ static void UnregisterRunningProcessID(int process_id) {
+ base::AutoLock lock(s_lock_);
+ s_running_process_id_set_.erase(process_id);
+ }
+
+ protected:
+ SharedWorkerServiceImplTest()
+ : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP),
+ browser_context_(new TestBrowserContext()),
+ partition_(
+ new WorkerStoragePartition(browser_context_->GetRequestContext(),
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL)) {
+ SharedWorkerServiceImpl::GetInstance()
+ ->ChangeUpdateWorkerDependencyFuncForTesting(
+ &SharedWorkerServiceImplTest::MockUpdateWorkerDependency);
+ SharedWorkerServiceImpl::GetInstance()
+ ->ChangeTryIncrementWorkerRefCountFuncForTesting(
+ &SharedWorkerServiceImplTest::MockTryIncrementWorkerRefCount);
+ }
+
+ virtual void SetUp() OVERRIDE {}
+ virtual void TearDown() OVERRIDE {
+ s_update_worker_dependency_call_count_ = 0;
+ s_worker_dependency_added_ids_.clear();
+ s_worker_dependency_removed_ids_.clear();
+ s_running_process_id_set_.clear();
+ SharedWorkerServiceImpl::GetInstance()->ResetForTesting();
+ }
+ static void MockUpdateWorkerDependency(const std::vector<int>& added_ids,
+ const std::vector<int>& removed_ids) {
+ ++s_update_worker_dependency_call_count_;
+ s_worker_dependency_added_ids_ = added_ids;
+ s_worker_dependency_removed_ids_ = removed_ids;
+ }
+ static bool MockTryIncrementWorkerRefCount(int worker_process_id) {
+ base::AutoLock lock(s_lock_);
+ return s_running_process_id_set_.find(worker_process_id) !=
+ s_running_process_id_set_.end();
+ }
+
+ TestBrowserThreadBundle browser_thread_bundle_;
+ scoped_ptr<TestBrowserContext> browser_context_;
+ scoped_ptr<WorkerStoragePartition> partition_;
+ static int s_update_worker_dependency_call_count_;
+ static std::vector<int> s_worker_dependency_added_ids_;
+ static std::vector<int> s_worker_dependency_removed_ids_;
+ static base::Lock s_lock_;
+ static std::set<int> s_running_process_id_set_;
+ DISALLOW_COPY_AND_ASSIGN(SharedWorkerServiceImplTest);
+};
+
+// static
+int SharedWorkerServiceImplTest::s_update_worker_dependency_call_count_;
+std::vector<int> SharedWorkerServiceImplTest::s_worker_dependency_added_ids_;
+std::vector<int> SharedWorkerServiceImplTest::s_worker_dependency_removed_ids_;
+base::Lock SharedWorkerServiceImplTest::s_lock_;
+std::set<int> SharedWorkerServiceImplTest::s_running_process_id_set_;
+
+namespace {
+
+static const int kProcessIDs[] = {100, 101, 102};
+static const unsigned long long kDocumentIDs[] = {200, 201, 202};
+static const int kRenderFrameRouteIDs[] = {300, 301, 302};
+
+class MockMessagePortMessageFilter : public MessagePortMessageFilter {
+ public:
+ MockMessagePortMessageFilter(const NextRoutingIDCallback& callback,
+ ScopedVector<IPC::Message>* message_queue)
+ : MessagePortMessageFilter(callback), message_queue_(message_queue) {}
+
+ virtual bool Send(IPC::Message* message) OVERRIDE {
+ if (!message_queue_) {
+ delete message;
+ return false;
+ }
+ message_queue_->push_back(message);
+ return true;
+ }
+
+ void Close() {
+ message_queue_ = NULL;
+ OnChannelClosing();
+ }
+
+ private:
+ virtual ~MockMessagePortMessageFilter() {}
+ ScopedVector<IPC::Message>* message_queue_;
+};
+
+class MockSharedWorkerMessageFilter : public SharedWorkerMessageFilter {
+ public:
+ MockSharedWorkerMessageFilter(int render_process_id,
+ ResourceContext* resource_context,
+ const WorkerStoragePartition& partition,
+ MessagePortMessageFilter* message_port_filter,
+ ScopedVector<IPC::Message>* message_queue)
+ : SharedWorkerMessageFilter(render_process_id,
+ resource_context,
+ partition,
+ message_port_filter),
+ message_queue_(message_queue) {}
+
+ virtual bool Send(IPC::Message* message) OVERRIDE {
+ if (!message_queue_) {
+ delete message;
+ return false;
+ }
+ message_queue_->push_back(message);
+ return true;
+ }
+
+ void Close() {
+ message_queue_ = NULL;
+ OnChannelClosing();
+ }
+
+ private:
+ virtual ~MockSharedWorkerMessageFilter() {}
+ ScopedVector<IPC::Message>* message_queue_;
+};
+
+class MockRendererProcessHost {
+ public:
+ MockRendererProcessHost(int process_id,
+ ResourceContext* resource_context,
+ const WorkerStoragePartition& partition)
+ : process_id_(process_id),
+ message_filter_(new MockMessagePortMessageFilter(
+ base::Bind(&base::AtomicSequenceNumber::GetNext,
+ base::Unretained(&next_routing_id_)),
+ &queued_messages_)),
+ worker_filter_(new MockSharedWorkerMessageFilter(process_id,
+ resource_context,
+ partition,
+ message_filter_.get(),
+ &queued_messages_)) {
+ SharedWorkerServiceImplTest::RegisterRunningProcessID(process_id);
+ }
+
+ ~MockRendererProcessHost() {
+ SharedWorkerServiceImplTest::UnregisterRunningProcessID(process_id_);
+ message_filter_->Close();
+ worker_filter_->Close();
+ }
+
+ bool OnMessageReceived(IPC::Message* message) {
+ scoped_ptr<IPC::Message> msg(message);
+ const bool ret = message_filter_->OnMessageReceived(*message) ||
+ worker_filter_->OnMessageReceived(*message);
+ if (message->is_sync()) {
+ CHECK(!queued_messages_.empty());
+ const IPC::Message* response_msg = queued_messages_.back();
+ IPC::SyncMessage* sync_msg = static_cast<IPC::SyncMessage*>(message);
+ scoped_ptr<IPC::MessageReplyDeserializer> reply_serializer(
+ sync_msg->GetReplyDeserializer());
+ bool result = reply_serializer->SerializeOutputParameters(*response_msg);
+ CHECK(result);
+ queued_messages_.pop_back();
+ }
+ return ret;
+ }
+
+ size_t QueuedMessageCount() const { return queued_messages_.size(); }
+
+ scoped_ptr<IPC::Message> PopMessage() {
+ CHECK(queued_messages_.size());
+ scoped_ptr<IPC::Message> msg(*queued_messages_.begin());
+ queued_messages_.weak_erase(queued_messages_.begin());
+ return msg.Pass();
+ }
+
+ void FastShutdownIfPossible() {
+ SharedWorkerServiceImplTest::UnregisterRunningProcessID(process_id_);
+ }
+
+ private:
+ const int process_id_;
+ ScopedVector<IPC::Message> queued_messages_;
+ base::AtomicSequenceNumber next_routing_id_;
+ scoped_refptr<MockMessagePortMessageFilter> message_filter_;
+ scoped_refptr<MockSharedWorkerMessageFilter> worker_filter_;
+};
+
+void CreateMessagePortPair(MockRendererProcessHost* renderer,
+ int* route_1,
+ int* port_1,
+ int* route_2,
+ int* port_2) {
+ EXPECT_TRUE(renderer->OnMessageReceived(
+ new MessagePortHostMsg_CreateMessagePort(route_1, port_1)));
+ EXPECT_TRUE(renderer->OnMessageReceived(
+ new MessagePortHostMsg_CreateMessagePort(route_2, port_2)));
+ EXPECT_TRUE(renderer->OnMessageReceived(
+ new MessagePortHostMsg_Entangle(*port_1, *port_2)));
+ EXPECT_TRUE(renderer->OnMessageReceived(
+ new MessagePortHostMsg_Entangle(*port_2, *port_1)));
+}
+
+void PostCreateWorker(MockRendererProcessHost* renderer,
+ const std::string& url,
+ const std::string& name,
+ unsigned long long document_id,
+ int render_frame_route_id,
+ int* connector_route_id) {
+ ViewHostMsg_CreateWorker_Params params;
+ params.url = GURL(url);
+ params.name = base::ASCIIToUTF16(name);
+ params.content_security_policy = base::string16();
+ params.security_policy_type = blink::WebContentSecurityPolicyTypeReport;
+ params.document_id = document_id;
+ params.render_frame_route_id = render_frame_route_id;
+ EXPECT_TRUE(renderer->OnMessageReceived(
+ new ViewHostMsg_CreateWorker(params, connector_route_id)));
+}
+
+class MockSharedWorkerConnector {
+ public:
+ MockSharedWorkerConnector(MockRendererProcessHost* renderer_host)
+ : renderer_host_(renderer_host),
+ temporary_remote_port_route_id_(0),
+ remote_port_id_(0),
+ local_port_route_id_(0),
+ local_port_id_(0),
+ route_id_(0) {}
+ void Create(const std::string& url,
+ const std::string& name,
+ unsigned long long document_id,
+ int render_frame_route_id) {
+ CreateMessagePortPair(renderer_host_,
+ &temporary_remote_port_route_id_,
+ &remote_port_id_,
+ &local_port_route_id_,
+ &local_port_id_);
+ PostCreateWorker(renderer_host_,
+ url,
+ name,
+ document_id,
+ render_frame_route_id,
+ &route_id_);
+ }
+ void SendQueueMessages() {
+ EXPECT_TRUE(renderer_host_->OnMessageReceived(
+ new MessagePortHostMsg_QueueMessages(remote_port_id_)));
+ }
+ void SendPostMessage(std::string data) {
+ const std::vector<int> empty_ids;
+ EXPECT_TRUE(
+ renderer_host_->OnMessageReceived(new MessagePortHostMsg_PostMessage(
+ local_port_id_, base::ASCIIToUTF16(data), empty_ids)));
+ }
+ void SendConnect() {
+ EXPECT_TRUE(
+ renderer_host_->OnMessageReceived(new ViewHostMsg_ForwardToWorker(
+ WorkerMsg_Connect(route_id_, remote_port_id_, MSG_ROUTING_NONE))));
+ }
+ void SendSendQueuedMessages(
+ const std::vector<QueuedMessage>& queued_messages) {
+ EXPECT_TRUE(renderer_host_->OnMessageReceived(
+ new MessagePortHostMsg_SendQueuedMessages(remote_port_id_,
+ queued_messages)));
+ }
+ int temporary_remote_port_route_id() {
+ return temporary_remote_port_route_id_;
+ }
+ int remote_port_id() { return remote_port_id_; }
+ int local_port_route_id() { return local_port_route_id_; }
+ int local_port_id() { return local_port_id_; }
+ int route_id() { return route_id_; }
+
+ private:
+ MockRendererProcessHost* renderer_host_;
+ int temporary_remote_port_route_id_;
+ int remote_port_id_;
+ int local_port_route_id_;
+ int local_port_id_;
+ int route_id_;
+};
+
+void CheckWorkerProcessMsgCreateWorker(
+ MockRendererProcessHost* renderer_host,
+ const std::string& expected_url,
+ const std::string& expected_name,
+ blink::WebContentSecurityPolicyType expected_security_policy_type,
+ int* route_id) {
+ scoped_ptr<IPC::Message> msg(renderer_host->PopMessage());
+ EXPECT_EQ(WorkerProcessMsg_CreateWorker::ID, msg->type());
+ Tuple1<WorkerProcessMsg_CreateWorker_Params> param;
+ EXPECT_TRUE(WorkerProcessMsg_CreateWorker::Read(msg.get(), &param));
+ EXPECT_EQ(GURL(expected_url), param.a.url);
+ EXPECT_EQ(base::ASCIIToUTF16(expected_name), param.a.name);
+ EXPECT_EQ(expected_security_policy_type, param.a.security_policy_type);
+ *route_id = param.a.route_id;
+}
+
+void CheckViewMsgWorkerCreated(MockRendererProcessHost* renderer_host,
+ MockSharedWorkerConnector* connector) {
+ scoped_ptr<IPC::Message> msg(renderer_host->PopMessage());
+ EXPECT_EQ(ViewMsg_WorkerCreated::ID, msg->type());
+ EXPECT_EQ(connector->route_id(), msg->routing_id());
+}
+
+void CheckMessagePortMsgMessagesQueued(MockRendererProcessHost* renderer_host,
+ MockSharedWorkerConnector* connector) {
+ scoped_ptr<IPC::Message> msg(renderer_host->PopMessage());
+ EXPECT_EQ(MessagePortMsg_MessagesQueued::ID, msg->type());
+ EXPECT_EQ(connector->temporary_remote_port_route_id(), msg->routing_id());
+}
+
+void CheckWorkerMsgConnect(MockRendererProcessHost* renderer_host,
+ int expected_msg_route_id,
+ int expected_sent_message_port_id,
+ int* routing_id) {
+ scoped_ptr<IPC::Message> msg(renderer_host->PopMessage());
+ EXPECT_EQ(WorkerMsg_Connect::ID, msg->type());
+ EXPECT_EQ(expected_msg_route_id, msg->routing_id());
+ WorkerMsg_Connect::Param params;
+ EXPECT_TRUE(WorkerMsg_Connect::Read(msg.get(), &params));
+ int port_id = params.a;
+ *routing_id = params.b;
+ EXPECT_EQ(expected_sent_message_port_id, port_id);
+}
+
+void CheckMessagePortMsgMessage(MockRendererProcessHost* renderer_host,
+ int expected_msg_route_id,
+ std::string expected_data) {
+ scoped_ptr<IPC::Message> msg(renderer_host->PopMessage());
+ EXPECT_EQ(MessagePortMsg_Message::ID, msg->type());
+ EXPECT_EQ(expected_msg_route_id, msg->routing_id());
+ MessagePortMsg_Message::Param params;
+ EXPECT_TRUE(MessagePortMsg_Message::Read(msg.get(), &params));
+ base::string16 data = params.a;
+ EXPECT_EQ(base::ASCIIToUTF16(expected_data), data);
+}
+
+void CheckViewMsgWorkerConnected(MockRendererProcessHost* renderer_host,
+ MockSharedWorkerConnector* connector) {
+ scoped_ptr<IPC::Message> msg(renderer_host->PopMessage());
+ EXPECT_EQ(ViewMsg_WorkerConnected::ID, msg->type());
+ EXPECT_EQ(connector->route_id(), msg->routing_id());
+}
+
+} // namespace
+
+TEST_F(SharedWorkerServiceImplTest, BasicTest) {
+ scoped_ptr<MockRendererProcessHost> renderer_host(
+ new MockRendererProcessHost(kProcessIDs[0],
+ browser_context_->GetResourceContext(),
+ *partition_.get()));
+ scoped_ptr<MockSharedWorkerConnector> connector(
+ new MockSharedWorkerConnector(renderer_host.get()));
+ int worker_route_id;
+ int worker_msg_port_route_id;
+
+ // SharedWorkerConnector creates two message ports and sends
+ // ViewHostMsg_CreateWorker.
+ connector->Create("http://example.com/w.js",
+ "name",
+ kDocumentIDs[0],
+ kRenderFrameRouteIDs[0]);
+ // We need to go to UI thread to call ReserveRenderProcessOnUI().
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ(2U, renderer_host->QueuedMessageCount());
+ // WorkerProcessMsg_CreateWorker should be sent to the renderer in which
+ // SharedWorker will be created.
+ CheckWorkerProcessMsgCreateWorker(renderer_host.get(),
+ "http://example.com/w.js",
+ "name",
+ blink::WebContentSecurityPolicyTypeReport,
+ &worker_route_id);
+ // ViewMsg_WorkerCreated(1) should be sent back to SharedWorkerConnector side.
+ CheckViewMsgWorkerCreated(renderer_host.get(), connector.get());
+
+ // SharedWorkerConnector side sends MessagePortHostMsg_QueueMessages in
+ // WebSharedWorkerProxy::connect.
+ connector->SendQueueMessages();
+ EXPECT_EQ(1U, renderer_host->QueuedMessageCount());
+ // MessagePortMsg_MessagesQueued(2) should be sent back to
+ // SharedWorkerConnector side.
+ CheckMessagePortMsgMessagesQueued(renderer_host.get(), connector.get());
+
+ // When SharedWorkerConnector receives ViewMsg_WorkerCreated(1), it sends
+ // WorkerMsg_Connect wrapped in ViewHostMsg_ForwardToWorker.
+ connector->SendConnect();
+ EXPECT_EQ(1U, renderer_host->QueuedMessageCount());
+ // WorkerMsg_Connect should be sent to SharedWorker side.
+ CheckWorkerMsgConnect(renderer_host.get(),
+ worker_route_id,
+ connector->remote_port_id(),
+ &worker_msg_port_route_id);
+
+ // When SharedWorkerConnector receives MessagePortMsg_MessagesQueued(2), it
+ // sends MessagePortHostMsg_SendQueuedMessages.
+ std::vector<QueuedMessage> empty_messages;
+ connector->SendSendQueuedMessages(empty_messages);
+ EXPECT_EQ(0U, renderer_host->QueuedMessageCount());
+
+ // SharedWorker sends WorkerHostMsg_WorkerScriptLoaded in
+ // EmbeddedSharedWorkerStub::workerScriptLoaded().
+ EXPECT_TRUE(renderer_host->OnMessageReceived(
+ new WorkerHostMsg_WorkerScriptLoaded(worker_route_id)));
+ EXPECT_EQ(0U, renderer_host->QueuedMessageCount());
+
+ // SharedWorker sends WorkerHostMsg_WorkerConnected in
+ // EmbeddedSharedWorkerStub::workerScriptLoaded().
+ EXPECT_TRUE(
+ renderer_host->OnMessageReceived(new WorkerHostMsg_WorkerConnected(
+ connector->remote_port_id(), worker_route_id)));
+ EXPECT_EQ(1U, renderer_host->QueuedMessageCount());
+ // ViewMsg_WorkerConnected should be sent to SharedWorkerConnector side.
+ CheckViewMsgWorkerConnected(renderer_host.get(), connector.get());
+
+ // When SharedWorkerConnector side sends MessagePortHostMsg_PostMessage,
+ // SharedWorker side shuold receive MessagePortMsg_Message.
+ connector->SendPostMessage("test1");
+ EXPECT_EQ(1U, renderer_host->QueuedMessageCount());
+ CheckMessagePortMsgMessage(
+ renderer_host.get(), worker_msg_port_route_id, "test1");
+
+ // When SharedWorker side sends MessagePortHostMsg_PostMessage,
+ // SharedWorkerConnector side shuold receive MessagePortMsg_Message.
+ const std::vector<int> empty_ids;
+ EXPECT_TRUE(renderer_host->OnMessageReceived(
+ new MessagePortHostMsg_PostMessage(connector->remote_port_id(),
+ base::ASCIIToUTF16("test2"),
+ empty_ids)));
+ EXPECT_EQ(1U, renderer_host->QueuedMessageCount());
+ CheckMessagePortMsgMessage(
+ renderer_host.get(), connector->local_port_route_id(), "test2");
+
+ // UpdateWorkerDependency should not be called.
+ EXPECT_EQ(0, s_update_worker_dependency_call_count_);
+}
+
+TEST_F(SharedWorkerServiceImplTest, TwoRendererTest) {
+ // The first renderer host.
+ scoped_ptr<MockRendererProcessHost> renderer_host0(
+ new MockRendererProcessHost(kProcessIDs[0],
+ browser_context_->GetResourceContext(),
+ *partition_.get()));
+ scoped_ptr<MockSharedWorkerConnector> connector0(
+ new MockSharedWorkerConnector(renderer_host0.get()));
+ int worker_route_id;
+ int worker_msg_port_route_id1;
+
+ // SharedWorkerConnector creates two message ports and sends
+ // ViewHostMsg_CreateWorker.
+ connector0->Create("http://example.com/w.js",
+ "name",
+ kDocumentIDs[0],
+ kRenderFrameRouteIDs[0]);
+ // We need to go to UI thread to call ReserveRenderProcessOnUI().
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ(2U, renderer_host0->QueuedMessageCount());
+ // WorkerProcessMsg_CreateWorker should be sent to the renderer in which
+ // SharedWorker will be created.
+ CheckWorkerProcessMsgCreateWorker(renderer_host0.get(),
+ "http://example.com/w.js",
+ "name",
+ blink::WebContentSecurityPolicyTypeReport,
+ &worker_route_id);
+ // ViewMsg_WorkerCreated(1) should be sent back to SharedWorkerConnector side.
+ CheckViewMsgWorkerCreated(renderer_host0.get(), connector0.get());
+
+ // SharedWorkerConnector side sends MessagePortHostMsg_QueueMessages in
+ // WebSharedWorkerProxy::connect.
+ connector0->SendQueueMessages();
+ EXPECT_EQ(1U, renderer_host0->QueuedMessageCount());
+ // MessagePortMsg_MessagesQueued(2) should be sent back to
+ // SharedWorkerConnector side.
+ CheckMessagePortMsgMessagesQueued(renderer_host0.get(), connector0.get());
+
+ // When SharedWorkerConnector receives ViewMsg_WorkerCreated(1), it sends
+ // WorkerMsg_Connect wrapped in ViewHostMsg_ForwardToWorker.
+ connector0->SendConnect();
+ EXPECT_EQ(1U, renderer_host0->QueuedMessageCount());
+ // WorkerMsg_Connect should be sent to SharedWorker side.
+ CheckWorkerMsgConnect(renderer_host0.get(),
+ worker_route_id,
+ connector0->remote_port_id(),
+ &worker_msg_port_route_id1);
+
+ // When SharedWorkerConnector receives MessagePortMsg_MessagesQueued(2), it
+ // sends MessagePortHostMsg_SendQueuedMessages.
+ std::vector<QueuedMessage> empty_messages;
+ connector0->SendSendQueuedMessages(empty_messages);
+ EXPECT_EQ(0U, renderer_host0->QueuedMessageCount());
+
+ // SharedWorker sends WorkerHostMsg_WorkerScriptLoaded in
+ // EmbeddedSharedWorkerStub::workerScriptLoaded().
+ EXPECT_TRUE(renderer_host0->OnMessageReceived(
+ new WorkerHostMsg_WorkerScriptLoaded(worker_route_id)));
+ EXPECT_EQ(0U, renderer_host0->QueuedMessageCount());
+
+ // SharedWorker sends WorkerHostMsg_WorkerConnected in
+ // EmbeddedSharedWorkerStub::workerScriptLoaded().
+ EXPECT_TRUE(
+ renderer_host0->OnMessageReceived(new WorkerHostMsg_WorkerConnected(
+ connector0->remote_port_id(), worker_route_id)));
+ EXPECT_EQ(1U, renderer_host0->QueuedMessageCount());
+ // ViewMsg_WorkerConnected should be sent to SharedWorkerConnector side.
+ CheckViewMsgWorkerConnected(renderer_host0.get(), connector0.get());
+
+ // When SharedWorkerConnector side sends MessagePortHostMsg_PostMessage,
+ // SharedWorker side shuold receive MessagePortMsg_Message.
+ connector0->SendPostMessage("test1");
+ EXPECT_EQ(1U, renderer_host0->QueuedMessageCount());
+ CheckMessagePortMsgMessage(
+ renderer_host0.get(), worker_msg_port_route_id1, "test1");
+
+ // When SharedWorker side sends MessagePortHostMsg_PostMessage,
+ // SharedWorkerConnector side shuold receive MessagePortMsg_Message.
+ const std::vector<int> empty_ids;
+ EXPECT_TRUE(renderer_host0->OnMessageReceived(
+ new MessagePortHostMsg_PostMessage(connector0->remote_port_id(),
+ base::ASCIIToUTF16("test2"),
+ empty_ids)));
+ EXPECT_EQ(1U, renderer_host0->QueuedMessageCount());
+ CheckMessagePortMsgMessage(
+ renderer_host0.get(), connector0->local_port_route_id(), "test2");
+
+ // The second renderer host.
+ scoped_ptr<MockRendererProcessHost> renderer_host1(
+ new MockRendererProcessHost(kProcessIDs[1],
+ browser_context_->GetResourceContext(),
+ *partition_.get()));
+ scoped_ptr<MockSharedWorkerConnector> connector1(
+ new MockSharedWorkerConnector(renderer_host1.get()));
+ int worker_msg_port_route_id2;
+
+ // UpdateWorkerDependency should not be called yet.
+ EXPECT_EQ(0, s_update_worker_dependency_call_count_);
+
+ // SharedWorkerConnector creates two message ports and sends
+ // ViewHostMsg_CreateWorker.
+ connector1->Create("http://example.com/w.js",
+ "name",
+ kDocumentIDs[1],
+ kRenderFrameRouteIDs[1]);
+ // We need to go to UI thread to call ReserveRenderProcessOnUI().
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ(1U, renderer_host1->QueuedMessageCount());
+ // ViewMsg_WorkerCreated(3) should be sent back to SharedWorkerConnector side.
+ CheckViewMsgWorkerCreated(renderer_host1.get(), connector1.get());
+
+ // UpdateWorkerDependency should be called.
+ EXPECT_EQ(1, s_update_worker_dependency_call_count_);
+ EXPECT_EQ(1U, s_worker_dependency_added_ids_.size());
+ EXPECT_EQ(kProcessIDs[0], s_worker_dependency_added_ids_[0]);
+ EXPECT_EQ(0U, s_worker_dependency_removed_ids_.size());
+
+ // SharedWorkerConnector side sends MessagePortHostMsg_QueueMessages in
+ // WebSharedWorkerProxy::connect.
+ connector1->SendQueueMessages();
+ EXPECT_EQ(1U, renderer_host1->QueuedMessageCount());
+ // MessagePortMsg_MessagesQueued(4) should be sent back to
+ // SharedWorkerConnector side.
+ CheckMessagePortMsgMessagesQueued(renderer_host1.get(), connector1.get());
+
+ // When SharedWorkerConnector receives ViewMsg_WorkerCreated(3), it sends
+ // WorkerMsg_Connect wrapped in ViewHostMsg_ForwardToWorker.
+ connector1->SendConnect();
+ EXPECT_EQ(1U, renderer_host0->QueuedMessageCount());
+ // WorkerMsg_Connect should be sent to SharedWorker side.
+ CheckWorkerMsgConnect(renderer_host0.get(),
+ worker_route_id,
+ connector1->remote_port_id(),
+ &worker_msg_port_route_id2);
+
+ // When SharedWorkerConnector receives MessagePortMsg_MessagesQueued(4), it
+ // sends MessagePortHostMsg_SendQueuedMessages.
+ connector1->SendSendQueuedMessages(empty_messages);
+ EXPECT_EQ(0U, renderer_host1->QueuedMessageCount());
+
+ // SharedWorker sends WorkerHostMsg_WorkerConnected in
+ // EmbeddedSharedWorkerStub::OnConnect().
+ EXPECT_TRUE(
+ renderer_host0->OnMessageReceived(new WorkerHostMsg_WorkerConnected(
+ connector1->remote_port_id(), worker_route_id)));
+ EXPECT_EQ(1U, renderer_host1->QueuedMessageCount());
+ // ViewMsg_WorkerConnected should be sent to SharedWorkerConnector side.
+ CheckViewMsgWorkerConnected(renderer_host1.get(), connector1.get());
+
+ // When SharedWorkerConnector side sends MessagePortHostMsg_PostMessage,
+ // SharedWorker side shuold receive MessagePortMsg_Message.
+ connector1->SendPostMessage("test3");
+ EXPECT_EQ(1U, renderer_host0->QueuedMessageCount());
+ CheckMessagePortMsgMessage(
+ renderer_host0.get(), worker_msg_port_route_id2, "test3");
+
+ // When SharedWorker side sends MessagePortHostMsg_PostMessage,
+ // SharedWorkerConnector side shuold receive MessagePortMsg_Message.
+ EXPECT_TRUE(renderer_host0->OnMessageReceived(
+ new MessagePortHostMsg_PostMessage(connector1->remote_port_id(),
+ base::ASCIIToUTF16("test4"),
+ empty_ids)));
+ EXPECT_EQ(1U, renderer_host1->QueuedMessageCount());
+ CheckMessagePortMsgMessage(
+ renderer_host1.get(), connector1->local_port_route_id(), "test4");
+
+ EXPECT_EQ(1, s_update_worker_dependency_call_count_);
+ renderer_host1.reset();
+ // UpdateWorkerDependency should be called.
+ EXPECT_EQ(2, s_update_worker_dependency_call_count_);
+ EXPECT_EQ(0U, s_worker_dependency_added_ids_.size());
+ EXPECT_EQ(1U, s_worker_dependency_removed_ids_.size());
+ EXPECT_EQ(kProcessIDs[0], s_worker_dependency_removed_ids_[0]);
+}
+
+TEST_F(SharedWorkerServiceImplTest, CreateWorkerTest) {
+ // The first renderer host.
+ scoped_ptr<MockRendererProcessHost> renderer_host0(
+ new MockRendererProcessHost(kProcessIDs[0],
+ browser_context_->GetResourceContext(),
+ *partition_.get()));
+ // The second renderer host.
+ scoped_ptr<MockRendererProcessHost> renderer_host1(
+ new MockRendererProcessHost(kProcessIDs[1],
+ browser_context_->GetResourceContext(),
+ *partition_.get()));
+ int worker_route_id;
+
+ // Normal case.
+ {
+ scoped_ptr<MockSharedWorkerConnector> connector0(
+ new MockSharedWorkerConnector(renderer_host0.get()));
+ scoped_ptr<MockSharedWorkerConnector> connector1(
+ new MockSharedWorkerConnector(renderer_host1.get()));
+ connector0->Create("http://example.com/w1.js",
+ "name1",
+ kDocumentIDs[0],
+ kRenderFrameRouteIDs[0]);
+ EXPECT_NE(MSG_ROUTING_NONE, connector0->route_id());
+ EXPECT_EQ(0U, renderer_host0->QueuedMessageCount());
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ(2U, renderer_host0->QueuedMessageCount());
+ CheckWorkerProcessMsgCreateWorker(renderer_host0.get(),
+ "http://example.com/w1.js",
+ "name1",
+ blink::WebContentSecurityPolicyTypeReport,
+ &worker_route_id);
+ CheckViewMsgWorkerCreated(renderer_host0.get(), connector0.get());
+ connector1->Create("http://example.com/w1.js",
+ "name1",
+ kDocumentIDs[1],
+ kRenderFrameRouteIDs[1]);
+ EXPECT_NE(MSG_ROUTING_NONE, connector1->route_id());
+ EXPECT_EQ(0U, renderer_host1->QueuedMessageCount());
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ(1U, renderer_host1->QueuedMessageCount());
+ CheckViewMsgWorkerCreated(renderer_host1.get(), connector1.get());
+ }
+
+ // Normal case (URL mismatch).
+ {
+ scoped_ptr<MockSharedWorkerConnector> connector0(
+ new MockSharedWorkerConnector(renderer_host0.get()));
+ scoped_ptr<MockSharedWorkerConnector> connector1(
+ new MockSharedWorkerConnector(renderer_host1.get()));
+ connector0->Create("http://example.com/w2.js",
+ "name2",
+ kDocumentIDs[0],
+ kRenderFrameRouteIDs[0]);
+ EXPECT_NE(MSG_ROUTING_NONE, connector0->route_id());
+ EXPECT_EQ(0U, renderer_host0->QueuedMessageCount());
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ(2U, renderer_host0->QueuedMessageCount());
+ CheckWorkerProcessMsgCreateWorker(renderer_host0.get(),
+ "http://example.com/w2.js",
+ "name2",
+ blink::WebContentSecurityPolicyTypeReport,
+ &worker_route_id);
+ CheckViewMsgWorkerCreated(renderer_host0.get(), connector0.get());
+ connector1->Create("http://example.com/w2x.js",
+ "name2",
+ kDocumentIDs[1],
+ kRenderFrameRouteIDs[1]);
+ EXPECT_EQ(MSG_ROUTING_NONE, connector1->route_id());
+ EXPECT_EQ(0U, renderer_host1->QueuedMessageCount());
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ(0U, renderer_host1->QueuedMessageCount());
+ }
+
+ // Pending case.
+ {
+ scoped_ptr<MockSharedWorkerConnector> connector0(
+ new MockSharedWorkerConnector(renderer_host0.get()));
+ scoped_ptr<MockSharedWorkerConnector> connector1(
+ new MockSharedWorkerConnector(renderer_host1.get()));
+ connector0->Create("http://example.com/w3.js",
+ "name3",
+ kDocumentIDs[0],
+ kRenderFrameRouteIDs[0]);
+ EXPECT_NE(MSG_ROUTING_NONE, connector0->route_id());
+ EXPECT_EQ(0U, renderer_host0->QueuedMessageCount());
+ connector1->Create("http://example.com/w3.js",
+ "name3",
+ kDocumentIDs[1],
+ kRenderFrameRouteIDs[1]);
+ EXPECT_NE(MSG_ROUTING_NONE, connector1->route_id());
+ EXPECT_EQ(0U, renderer_host1->QueuedMessageCount());
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ(2U, renderer_host0->QueuedMessageCount());
+ CheckWorkerProcessMsgCreateWorker(renderer_host0.get(),
+ "http://example.com/w3.js",
+ "name3",
+ blink::WebContentSecurityPolicyTypeReport,
+ &worker_route_id);
+ CheckViewMsgWorkerCreated(renderer_host0.get(), connector0.get());
+ EXPECT_EQ(1U, renderer_host1->QueuedMessageCount());
+ CheckViewMsgWorkerCreated(renderer_host1.get(), connector1.get());
+ }
+
+ // Pending case (URL mismatch).
+ {
+ scoped_ptr<MockSharedWorkerConnector> connector0(
+ new MockSharedWorkerConnector(renderer_host0.get()));
+ scoped_ptr<MockSharedWorkerConnector> connector1(
+ new MockSharedWorkerConnector(renderer_host1.get()));
+ connector0->Create("http://example.com/w4.js",
+ "name4",
+ kDocumentIDs[0],
+ kRenderFrameRouteIDs[0]);
+ EXPECT_NE(MSG_ROUTING_NONE, connector0->route_id());
+ EXPECT_EQ(0U, renderer_host0->QueuedMessageCount());
+ connector1->Create("http://example.com/w4x.js",
+ "name4",
+ kDocumentIDs[1],
+ kRenderFrameRouteIDs[1]);
+ EXPECT_EQ(MSG_ROUTING_NONE, connector1->route_id());
+ EXPECT_EQ(0U, renderer_host1->QueuedMessageCount());
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ(2U, renderer_host0->QueuedMessageCount());
+ CheckWorkerProcessMsgCreateWorker(renderer_host0.get(),
+ "http://example.com/w4.js",
+ "name4",
+ blink::WebContentSecurityPolicyTypeReport,
+ &worker_route_id);
+ CheckViewMsgWorkerCreated(renderer_host0.get(), connector0.get());
+ EXPECT_EQ(0U, renderer_host1->QueuedMessageCount());
+ }
+}
+
+TEST_F(SharedWorkerServiceImplTest, CreateWorkerRaceTest) {
+ // Create three renderer hosts.
+ scoped_ptr<MockRendererProcessHost> renderer_host0(
+ new MockRendererProcessHost(kProcessIDs[0],
+ browser_context_->GetResourceContext(),
+ *partition_.get()));
+ scoped_ptr<MockRendererProcessHost> renderer_host1(
+ new MockRendererProcessHost(kProcessIDs[1],
+ browser_context_->GetResourceContext(),
+ *partition_.get()));
+ scoped_ptr<MockRendererProcessHost> renderer_host2(
+ new MockRendererProcessHost(kProcessIDs[2],
+ browser_context_->GetResourceContext(),
+ *partition_.get()));
+ int worker_route_id;
+
+ scoped_ptr<MockSharedWorkerConnector> connector0(
+ new MockSharedWorkerConnector(renderer_host0.get()));
+ scoped_ptr<MockSharedWorkerConnector> connector1(
+ new MockSharedWorkerConnector(renderer_host1.get()));
+ scoped_ptr<MockSharedWorkerConnector> connector2(
+ new MockSharedWorkerConnector(renderer_host2.get()));
+ connector0->Create("http://example.com/w1.js",
+ "name1",
+ kDocumentIDs[0],
+ kRenderFrameRouteIDs[0]);
+ EXPECT_NE(MSG_ROUTING_NONE, connector0->route_id());
+ EXPECT_EQ(0U, renderer_host0->QueuedMessageCount());
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ(2U, renderer_host0->QueuedMessageCount());
+ CheckWorkerProcessMsgCreateWorker(renderer_host0.get(),
+ "http://example.com/w1.js",
+ "name1",
+ blink::WebContentSecurityPolicyTypeReport,
+ &worker_route_id);
+ CheckViewMsgWorkerCreated(renderer_host0.get(), connector0.get());
+ renderer_host0->FastShutdownIfPossible();
+
+ connector1->Create("http://example.com/w1.js",
+ "name1",
+ kDocumentIDs[1],
+ kRenderFrameRouteIDs[1]);
+ EXPECT_NE(MSG_ROUTING_NONE, connector1->route_id());
+ EXPECT_EQ(0U, renderer_host1->QueuedMessageCount());
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ(2U, renderer_host1->QueuedMessageCount());
+ CheckWorkerProcessMsgCreateWorker(renderer_host1.get(),
+ "http://example.com/w1.js",
+ "name1",
+ blink::WebContentSecurityPolicyTypeReport,
+ &worker_route_id);
+ CheckViewMsgWorkerCreated(renderer_host1.get(), connector1.get());
+
+ connector2->Create("http://example.com/w1.js",
+ "name1",
+ kDocumentIDs[2],
+ kRenderFrameRouteIDs[2]);
+ EXPECT_NE(MSG_ROUTING_NONE, connector2->route_id());
+ EXPECT_EQ(0U, renderer_host2->QueuedMessageCount());
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ(1U, renderer_host2->QueuedMessageCount());
+ CheckViewMsgWorkerCreated(renderer_host2.get(), connector2.get());
+}
+
+TEST_F(SharedWorkerServiceImplTest, CreateWorkerRaceTest2) {
+ // Create three renderer hosts.
+ scoped_ptr<MockRendererProcessHost> renderer_host0(
+ new MockRendererProcessHost(kProcessIDs[0],
+ browser_context_->GetResourceContext(),
+ *partition_.get()));
+ scoped_ptr<MockRendererProcessHost> renderer_host1(
+ new MockRendererProcessHost(kProcessIDs[1],
+ browser_context_->GetResourceContext(),
+ *partition_.get()));
+ scoped_ptr<MockRendererProcessHost> renderer_host2(
+ new MockRendererProcessHost(kProcessIDs[2],
+ browser_context_->GetResourceContext(),
+ *partition_.get()));
+ int worker_route_id;
+
+ scoped_ptr<MockSharedWorkerConnector> connector0(
+ new MockSharedWorkerConnector(renderer_host0.get()));
+ scoped_ptr<MockSharedWorkerConnector> connector1(
+ new MockSharedWorkerConnector(renderer_host1.get()));
+ scoped_ptr<MockSharedWorkerConnector> connector2(
+ new MockSharedWorkerConnector(renderer_host2.get()));
+ connector0->Create("http://example.com/w1.js",
+ "name1",
+ kDocumentIDs[0],
+ kRenderFrameRouteIDs[0]);
+ EXPECT_NE(MSG_ROUTING_NONE, connector0->route_id());
+ EXPECT_EQ(0U, renderer_host0->QueuedMessageCount());
+ renderer_host0->FastShutdownIfPossible();
+
+ connector1->Create("http://example.com/w1.js",
+ "name1",
+ kDocumentIDs[1],
+ kRenderFrameRouteIDs[1]);
+ EXPECT_NE(MSG_ROUTING_NONE, connector1->route_id());
+ EXPECT_EQ(0U, renderer_host1->QueuedMessageCount());
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ(2U, renderer_host1->QueuedMessageCount());
+ CheckWorkerProcessMsgCreateWorker(renderer_host1.get(),
+ "http://example.com/w1.js",
+ "name1",
+ blink::WebContentSecurityPolicyTypeReport,
+ &worker_route_id);
+ CheckViewMsgWorkerCreated(renderer_host1.get(), connector1.get());
+
+ connector2->Create("http://example.com/w1.js",
+ "name1",
+ kDocumentIDs[2],
+ kRenderFrameRouteIDs[2]);
+ EXPECT_NE(MSG_ROUTING_NONE, connector2->route_id());
+ EXPECT_EQ(0U, renderer_host2->QueuedMessageCount());
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ(1U, renderer_host2->QueuedMessageCount());
+ CheckViewMsgWorkerCreated(renderer_host2.get(), connector2.get());
+}
+
+} // namespace content
diff --git a/chromium/content/browser/site_instance_impl.cc b/chromium/content/browser/site_instance_impl.cc
index c003eb73484..b6e6918c0a4 100644
--- a/chromium/content/browser/site_instance_impl.cc
+++ b/chromium/content/browser/site_instance_impl.cc
@@ -104,11 +104,8 @@ RenderProcessHost* SiteInstanceImpl::GetProcess() {
StoragePartitionImpl* partition =
static_cast<StoragePartitionImpl*>(
BrowserContext::GetStoragePartition(browser_context, this));
- bool supports_browser_plugin = GetContentClient()->browser()->
- SupportsBrowserPlugin(browser_context, site_);
process_ = new RenderProcessHostImpl(browser_context,
partition,
- supports_browser_plugin,
site_.SchemeIs(kGuestScheme));
}
}
@@ -185,6 +182,10 @@ bool SiteInstanceImpl::IsRelatedSiteInstance(const SiteInstance* instance) {
instance)->browsing_instance_.get();
}
+size_t SiteInstanceImpl::GetRelatedActiveContentsCount() {
+ return browsing_instance_->active_contents_count();
+}
+
bool SiteInstanceImpl::HasWrongProcessForURL(const GURL& url) {
// Having no process isn't a problem, since we'll assign it correctly.
// Note that HasProcess() may return true if process_ is null, in
@@ -206,6 +207,14 @@ bool SiteInstanceImpl::HasWrongProcessForURL(const GURL& url) {
GetProcess(), browsing_instance_->browser_context(), site_url);
}
+void SiteInstanceImpl::IncrementRelatedActiveContentsCount() {
+ browsing_instance_->increment_active_contents_count();
+}
+
+void SiteInstanceImpl::DecrementRelatedActiveContentsCount() {
+ browsing_instance_->decrement_active_contents_count();
+}
+
void SiteInstanceImpl::set_render_process_host_factory(
const RenderProcessHostFactory* rph_factory) {
g_render_process_host_factory_ = rph_factory;
@@ -259,7 +268,7 @@ bool SiteInstance::IsSameWebSite(BrowserContext* browser_context,
return net::registry_controlled_domains::SameDomainOrHost(
url1,
url2,
- net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
+ net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
}
/*static*/
@@ -294,7 +303,7 @@ GURL SiteInstance::GetSiteForURL(BrowserContext* browser_context,
std::string domain =
net::registry_controlled_domains::GetDomainAndRegistry(
url,
- net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
+ net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
if (!domain.empty()) {
GURL::Replacements rep;
rep.SetHostStr(domain);
diff --git a/chromium/content/browser/site_instance_impl.h b/chromium/content/browser/site_instance_impl.h
index edaeb3e702d..69197eeeab4 100644
--- a/chromium/content/browser/site_instance_impl.h
+++ b/chromium/content/browser/site_instance_impl.h
@@ -12,6 +12,7 @@
#include "url/gurl.h"
namespace content {
+class BrowsingInstance;
class RenderProcessHostFactory;
class CONTENT_EXPORT SiteInstanceImpl : public SiteInstance,
@@ -21,10 +22,11 @@ class CONTENT_EXPORT SiteInstanceImpl : public SiteInstance,
virtual int32 GetId() OVERRIDE;
virtual bool HasProcess() const OVERRIDE;
virtual RenderProcessHost* GetProcess() OVERRIDE;
+ virtual BrowserContext* GetBrowserContext() const OVERRIDE;
virtual const GURL& GetSiteURL() const OVERRIDE;
virtual SiteInstance* GetRelatedSiteInstance(const GURL& url) OVERRIDE;
virtual bool IsRelatedSiteInstance(const SiteInstance* instance) OVERRIDE;
- virtual BrowserContext* GetBrowserContext() const OVERRIDE;
+ virtual size_t GetRelatedActiveContentsCount() OVERRIDE;
// Set the web site that this SiteInstance is rendering pages for.
// This includes the scheme and registered domain, but not the port. If the
@@ -59,6 +61,14 @@ class CONTENT_EXPORT SiteInstanceImpl : public SiteInstance,
// discarded to save memory.
size_t active_view_count() { return active_view_count_; }
+ // Increase the number of active WebContentses using this SiteInstance. Note
+ // that, unlike active_view_count, this does not count pending RVHs.
+ void IncrementRelatedActiveContentsCount();
+
+ // Decrease the number of active WebContentses using this SiteInstance. Note
+ // that, unlike active_view_count, this does not count pending RVHs.
+ void DecrementRelatedActiveContentsCount();
+
// Sets the global factory used to create new RenderProcessHosts. It may be
// NULL, in which case the default BrowserRenderProcessHost will be created
// (this is the behavior if you don't call this function). The factory must
diff --git a/chromium/content/browser/site_instance_impl_unittest.cc b/chromium/content/browser/site_instance_impl_unittest.cc
index ac3c1927c92..646f4d07957 100644
--- a/chromium/content/browser/site_instance_impl_unittest.cc
+++ b/chromium/content/browser/site_instance_impl_unittest.cc
@@ -92,8 +92,8 @@ class SiteInstanceTest : public testing::Test {
virtual void SetUp() {
old_browser_client_ = SetBrowserClientForTesting(&browser_client_);
- url_util::AddStandardScheme(kPrivilegedScheme);
- url_util::AddStandardScheme(chrome::kChromeUIScheme);
+ url::AddStandardScheme(kPrivilegedScheme);
+ url::AddStandardScheme(kChromeUIScheme);
SiteInstanceImpl::set_render_process_host_factory(&rph_factory_);
}
@@ -557,6 +557,11 @@ static SiteInstanceImpl* CreateSiteInstance(BrowserContext* browser_context,
// Test to ensure that pages that require certain privileges are grouped
// in processes with similar pages.
TEST_F(SiteInstanceTest, ProcessSharingByType) {
+ // This test shouldn't run with --site-per-process mode, since it doesn't
+ // allow render process reuse, which this test explicitly exercises.
+ if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess))
+ return;
+
ChildProcessSecurityPolicyImpl* policy =
ChildProcessSecurityPolicyImpl::GetInstance();
@@ -583,13 +588,12 @@ TEST_F(SiteInstanceTest, ProcessSharingByType) {
// Create some WebUI instances and make sure they share a process.
scoped_refptr<SiteInstanceImpl> webui1_instance(CreateSiteInstance(
- browser_context.get(),
- GURL(chrome::kChromeUIScheme + std::string("://newtab"))));
+ browser_context.get(), GURL(kChromeUIScheme + std::string("://newtab"))));
policy->GrantWebUIBindings(webui1_instance->GetProcess()->GetID());
- scoped_refptr<SiteInstanceImpl> webui2_instance(CreateSiteInstance(
- browser_context.get(),
- GURL(chrome::kChromeUIScheme + std::string("://history"))));
+ scoped_refptr<SiteInstanceImpl> webui2_instance(
+ CreateSiteInstance(browser_context.get(),
+ GURL(kChromeUIScheme + std::string("://history"))));
scoped_ptr<RenderProcessHost> dom_host(webui1_instance->GetProcess());
EXPECT_EQ(webui1_instance->GetProcess(), webui2_instance->GetProcess());
diff --git a/chromium/content/browser/site_per_process_browsertest.cc b/chromium/content/browser/site_per_process_browsertest.cc
index cca1df07c00..3adb455acc2 100644
--- a/chromium/content/browser/site_per_process_browsertest.cc
+++ b/chromium/content/browser/site_per_process_browsertest.cc
@@ -4,26 +4,25 @@
#include "base/command_line.h"
#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
+#include "content/browser/frame_host/cross_process_frame_connector.h"
#include "content/browser/frame_host/frame_tree.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/renderer_host/render_view_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
-#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/content_switches.h"
-#include "content/public/common/url_constants.h"
#include "content/public/test/browser_test_utils.h"
-#include "content/public/test/test_navigation_observer.h"
+#include "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/shell/browser/shell_content_browser_client.h"
-#include "content/test/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
-#include "net/base/escape.h"
+#include "content/test/content_browser_test_utils_internal.h"
#include "net/dns/mock_host_resolver.h"
+#include "url/gurl.h"
namespace content {
@@ -31,9 +30,20 @@ class SitePerProcessWebContentsObserver: public WebContentsObserver {
public:
explicit SitePerProcessWebContentsObserver(WebContents* web_contents)
: WebContentsObserver(web_contents),
- navigation_succeeded_(true) {}
+ navigation_succeeded_(false) {}
virtual ~SitePerProcessWebContentsObserver() {}
+ virtual void DidStartProvisionalLoadForFrame(
+ int64 frame_id,
+ int64 parent_frame_id,
+ bool is_main_frame,
+ const GURL& validated_url,
+ bool is_error_page,
+ bool is_iframe_srcdoc,
+ RenderViewHost* render_view_host) OVERRIDE {
+ navigation_succeeded_ = false;
+ }
+
virtual void DidFailProvisionalLoad(
int64 frame_id,
const base::string16& frame_unique_name,
@@ -149,74 +159,218 @@ void RedirectNotificationObserver::Observe(
}
class SitePerProcessBrowserTest : public ContentBrowserTest {
+ public:
+ SitePerProcessBrowserTest() {}
+
protected:
+ // Start at a data URL so each extra navigation creates a navigation entry.
+ // (The first navigation will silently be classified as AUTO_SUBFRAME.)
+ // TODO(creis): This won't be necessary when we can wait for LOAD_STOP.
+ void StartFrameAtDataURL() {
+ std::string data_url_script =
+ "var iframes = document.getElementById('test');iframes.src="
+ "'data:text/html,dataurl';";
+ ASSERT_TRUE(ExecuteScript(shell()->web_contents(), data_url_script));
+ }
+
bool NavigateIframeToURL(Shell* window,
const GURL& url,
std::string iframe_id) {
+ // TODO(creis): This should wait for LOAD_STOP, but cross-site subframe
+ // navigations generate extra DidStartLoading and DidStopLoading messages.
+ // Until we replace swappedout:// with frame proxies, we need to listen for
+ // something else. For now, we trigger NEW_SUBFRAME navigations and listen
+ // for commit.
std::string script = base::StringPrintf(
- "var iframes = document.getElementById('%s');iframes.src='%s';",
+ "setTimeout(\""
+ "var iframes = document.getElementById('%s');iframes.src='%s';"
+ "\",0)",
iframe_id.c_str(), url.spec().c_str());
WindowedNotificationObserver load_observer(
- NOTIFICATION_LOAD_STOP,
+ NOTIFICATION_NAV_ENTRY_COMMITTED,
Source<NavigationController>(
- &shell()->web_contents()->GetController()));
+ &window->web_contents()->GetController()));
bool result = ExecuteScript(window->web_contents(), script);
load_observer.Wait();
return result;
}
- void NavigateToURLContentInitiated(Shell* window,
- const GURL& url,
- bool should_replace_current_entry) {
- std::string script;
- if (should_replace_current_entry)
- script = base::StringPrintf("location.replace('%s')", url.spec().c_str());
- else
- script = base::StringPrintf("location.href = '%s'", url.spec().c_str());
- TestNavigationObserver load_observer(shell()->web_contents(), 1);
- bool result = ExecuteScript(window->web_contents(), script);
- EXPECT_TRUE(result);
- load_observer.Wait();
- }
-
virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
command_line->AppendSwitch(switches::kSitePerProcess);
}
};
-// TODO(nasko): Disable this test until out-of-process iframes is ready and the
-// security checks are back in place.
-IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, DISABLED_CrossSiteIframe) {
+// Ensure that we can complete a cross-process subframe navigation.
+IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, CrossSiteIframe) {
+ host_resolver()->AddRule("*", "127.0.0.1");
ASSERT_TRUE(test_server()->Start());
- net::SpawnedTestServer https_server(
- net::SpawnedTestServer::TYPE_HTTPS,
- net::SpawnedTestServer::kLocalhost,
- base::FilePath(FILE_PATH_LITERAL("content/test/data")));
- ASSERT_TRUE(https_server.Start());
GURL main_url(test_server()->GetURL("files/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<WebContentsImpl*>(shell()->web_contents())->
+ GetFrameTree()->root();
+
SitePerProcessWebContentsObserver observer(shell()->web_contents());
+
+ // Load same-site page into iframe.
+ FrameTreeNode* child = root->child_at(0);
+ GURL http_url(test_server()->GetURL("files/title1.html"));
+ NavigateFrameToURL(child, http_url);
+ EXPECT_EQ(http_url, observer.navigation_url());
+ EXPECT_TRUE(observer.navigation_succeeded());
{
- // Load same-site page into Iframe.
- GURL http_url(test_server()->GetURL("files/title1.html"));
- EXPECT_TRUE(NavigateIframeToURL(shell(), http_url, "test"));
- EXPECT_EQ(observer.navigation_url(), http_url);
- EXPECT_TRUE(observer.navigation_succeeded());
+ // There should be only one RenderWidgetHost when there are no
+ // cross-process iframes.
+ std::set<RenderWidgetHostView*> views_set =
+ static_cast<WebContentsImpl*>(shell()->web_contents())
+ ->GetRenderWidgetHostViewsInTree();
+ EXPECT_EQ(1U, views_set.size());
}
+ RenderFrameProxyHost* proxy_to_parent =
+ child->render_manager()->GetRenderFrameProxyHost(
+ shell()->web_contents()->GetSiteInstance());
+ EXPECT_FALSE(proxy_to_parent);
+ // These must stay in scope with replace_host.
+ GURL::Replacements replace_host;
+ std::string foo_com("foo.com");
+
+ // Load cross-site page into iframe.
+ GURL cross_site_url(test_server()->GetURL("files/title2.html"));
+ replace_host.SetHostStr(foo_com);
+ cross_site_url = cross_site_url.ReplaceComponents(replace_host);
+ NavigateFrameToURL(root->child_at(0), cross_site_url);
+ EXPECT_EQ(cross_site_url, observer.navigation_url());
+ EXPECT_TRUE(observer.navigation_succeeded());
+
+ // Ensure that we have created a new process for the subframe.
+ ASSERT_EQ(1U, root->child_count());
+ SiteInstance* site_instance = child->current_frame_host()->GetSiteInstance();
+ RenderViewHost* rvh = child->current_frame_host()->render_view_host();
+ RenderProcessHost* rph = child->current_frame_host()->GetProcess();
+ EXPECT_NE(shell()->web_contents()->GetRenderViewHost(), rvh);
+ EXPECT_NE(shell()->web_contents()->GetSiteInstance(), site_instance);
+ EXPECT_NE(shell()->web_contents()->GetRenderProcessHost(), rph);
{
- // Load cross-site page into Iframe.
- GURL https_url(https_server.GetURL("files/title1.html"));
- EXPECT_TRUE(NavigateIframeToURL(shell(), https_url, "test"));
- EXPECT_EQ(observer.navigation_url(), https_url);
- EXPECT_FALSE(observer.navigation_succeeded());
+ // There should be now two RenderWidgetHosts, one for each process
+ // rendering a frame.
+ std::set<RenderWidgetHostView*> views_set =
+ static_cast<WebContentsImpl*>(shell()->web_contents())
+ ->GetRenderWidgetHostViewsInTree();
+ EXPECT_EQ(2U, views_set.size());
}
+ proxy_to_parent = child->render_manager()->GetProxyToParent();
+ EXPECT_TRUE(proxy_to_parent);
+ EXPECT_TRUE(proxy_to_parent->cross_process_frame_connector());
+ EXPECT_EQ(
+ rvh->GetView(),
+ proxy_to_parent->cross_process_frame_connector()->get_view_for_testing());
+
+ // Load another cross-site page into the same iframe.
+ cross_site_url = test_server()->GetURL("files/title3.html");
+ std::string bar_com("bar.com");
+ replace_host.SetHostStr(bar_com);
+ cross_site_url = cross_site_url.ReplaceComponents(replace_host);
+ NavigateFrameToURL(root->child_at(0), cross_site_url);
+ EXPECT_EQ(cross_site_url, observer.navigation_url());
+ EXPECT_TRUE(observer.navigation_succeeded());
+
+ // Check again that a new process is created and is different from the
+ // top level one and the previous one.
+ ASSERT_EQ(1U, root->child_count());
+ child = root->child_at(0);
+ EXPECT_NE(shell()->web_contents()->GetRenderViewHost(),
+ child->current_frame_host()->render_view_host());
+ EXPECT_NE(rvh, child->current_frame_host()->render_view_host());
+ EXPECT_NE(shell()->web_contents()->GetSiteInstance(),
+ child->current_frame_host()->GetSiteInstance());
+ EXPECT_NE(site_instance,
+ child->current_frame_host()->GetSiteInstance());
+ EXPECT_NE(shell()->web_contents()->GetRenderProcessHost(),
+ child->current_frame_host()->GetProcess());
+ EXPECT_NE(rph, child->current_frame_host()->GetProcess());
+ {
+ std::set<RenderWidgetHostView*> views_set =
+ static_cast<WebContentsImpl*>(shell()->web_contents())
+ ->GetRenderWidgetHostViewsInTree();
+ EXPECT_EQ(2U, views_set.size());
+ }
+ EXPECT_EQ(proxy_to_parent, child->render_manager()->GetProxyToParent());
+ EXPECT_TRUE(proxy_to_parent->cross_process_frame_connector());
+ EXPECT_EQ(
+ child->current_frame_host()->render_view_host()->GetView(),
+ proxy_to_parent->cross_process_frame_connector()->get_view_for_testing());
+}
+
+// Crash a subframe and ensures its children are cleared from the FrameTree.
+// See http://crbug.com/338508.
+// TODO(creis): Enable this on Android when we can kill the process there.
+#if defined(OS_ANDROID)
+#define MAYBE_CrashSubframe DISABLED_CrashSubframe
+#else
+#define MAYBE_CrashSubframe CrashSubframe
+#endif
+IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, MAYBE_CrashSubframe) {
+ host_resolver()->AddRule("*", "127.0.0.1");
+ ASSERT_TRUE(test_server()->Start());
+ GURL main_url(test_server()->GetURL("files/site_per_process_main.html"));
+ NavigateToURL(shell(), main_url);
+
+ StartFrameAtDataURL();
+
+ // These must stay in scope with replace_host.
+ GURL::Replacements replace_host;
+ std::string foo_com("foo.com");
+
+ // Load cross-site page into iframe.
+ GURL cross_site_url(test_server()->GetURL("files/title2.html"));
+ replace_host.SetHostStr(foo_com);
+ cross_site_url = cross_site_url.ReplaceComponents(replace_host);
+ EXPECT_TRUE(NavigateIframeToURL(shell(), cross_site_url, "test"));
+
+ // Check the subframe process.
+ FrameTreeNode* root =
+ static_cast<WebContentsImpl*>(shell()->web_contents())->
+ GetFrameTree()->root();
+ ASSERT_EQ(1U, root->child_count());
+ FrameTreeNode* child = root->child_at(0);
+ EXPECT_EQ(main_url, root->current_url());
+ EXPECT_EQ(cross_site_url, child->current_url());
+
+ // Crash the subframe process.
+ RenderProcessHost* root_process = root->current_frame_host()->GetProcess();
+ RenderProcessHost* child_process = child->current_frame_host()->GetProcess();
+ {
+ RenderProcessHostWatcher crash_observer(
+ child_process,
+ RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
+ base::KillProcess(child_process->GetHandle(), 0, false);
+ crash_observer.Wait();
+ }
+
+ // Ensure that the child frame still exists but has been cleared.
+ EXPECT_EQ(1U, root->child_count());
+ EXPECT_EQ(main_url, root->current_url());
+ EXPECT_EQ(GURL(), child->current_url());
+
+ // Now crash the top-level page to clear the child frame.
+ {
+ RenderProcessHostWatcher crash_observer(
+ root_process,
+ RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
+ base::KillProcess(root_process->GetHandle(), 0, false);
+ crash_observer.Wait();
+ }
+ EXPECT_EQ(0U, root->child_count());
+ EXPECT_EQ(GURL(), root->current_url());
}
// TODO(nasko): Disable this test until out-of-process iframes is ready and the
// security checks are back in place.
+// TODO(creis): Replace SpawnedTestServer with host_resolver to get test to run
+// on Android (http://crbug.com/187570).
IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
DISABLED_CrossSiteIframeRedirectOnce) {
ASSERT_TRUE(test_server()->Start());
@@ -341,6 +495,8 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
// TODO(nasko): Disable this test until out-of-process iframes is ready and the
// security checks are back in place.
+// TODO(creis): Replace SpawnedTestServer with host_resolver to get test to run
+// on Android (http://crbug.com/187570).
IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
DISABLED_CrossSiteIframeRedirectTwice) {
ASSERT_TRUE(test_server()->Start());
@@ -421,304 +577,4 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
}
}
-// Ensures FrameTree correctly reflects page structure during navigations.
-IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
- FrameTreeShape) {
- host_resolver()->AddRule("*", "127.0.0.1");
- ASSERT_TRUE(test_server()->Start());
-
- GURL base_url = test_server()->GetURL("files/site_isolation/");
- GURL::Replacements replace_host;
- std::string host_str("A.com"); // Must stay in scope with replace_host.
- replace_host.SetHostStr(host_str);
- base_url = base_url.ReplaceComponents(replace_host);
-
- // Load doc without iframes. Verify FrameTree just has root.
- // Frame tree:
- // Site-A Root
- NavigateToURL(shell(), base_url.Resolve("blank.html"));
- FrameTreeNode* root =
- static_cast<WebContentsImpl*>(shell()->web_contents())->
- GetFrameTree()->root();
- EXPECT_EQ(0U, root->child_count());
-
- // Add 2 same-site frames. Verify 3 nodes in tree with proper names.
- // Frame tree:
- // Site-A Root -- Site-A frame1
- // \-- Site-A frame2
- WindowedNotificationObserver observer1(
- content::NOTIFICATION_LOAD_STOP,
- content::Source<NavigationController>(
- &shell()->web_contents()->GetController()));
- NavigateToURL(shell(), base_url.Resolve("frames-X-X.html"));
- observer1.Wait();
- ASSERT_EQ(2U, root->child_count());
- EXPECT_EQ(0U, root->child_at(0)->child_count());
- EXPECT_EQ(0U, root->child_at(1)->child_count());
-}
-
-// TODO(ajwong): Talk with nasko and merge this functionality with
-// FrameTreeShape.
-IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
- FrameTreeShape2) {
- host_resolver()->AddRule("*", "127.0.0.1");
- ASSERT_TRUE(test_server()->Start());
-
- NavigateToURL(shell(),
- test_server()->GetURL("files/frame_tree/top.html"));
-
- WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
- RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
- wc->GetRenderViewHost());
- FrameTreeNode* root = wc->GetFrameTree()->root();
-
- // Check that the root node is properly created with the frame id of the
- // initial navigation.
- ASSERT_EQ(3UL, root->child_count());
- EXPECT_EQ(std::string(), root->frame_name());
- EXPECT_EQ(rvh->main_frame_id(), root->frame_id());
-
- ASSERT_EQ(2UL, root->child_at(0)->child_count());
- EXPECT_STREQ("1-1-name", root->child_at(0)->frame_name().c_str());
-
- // Verify the deepest node exists and has the right name.
- ASSERT_EQ(2UL, root->child_at(2)->child_count());
- EXPECT_EQ(1UL, root->child_at(2)->child_at(1)->child_count());
- EXPECT_EQ(0UL, root->child_at(2)->child_at(1)->child_at(0)->child_count());
- EXPECT_STREQ("3-1-id",
- root->child_at(2)->child_at(1)->child_at(0)->frame_name().c_str());
-
- // Navigate to about:blank, which should leave only the root node of the frame
- // tree in the browser process.
- NavigateToURL(shell(), test_server()->GetURL("files/title1.html"));
-
- root = wc->GetFrameTree()->root();
- EXPECT_EQ(0UL, root->child_count());
- EXPECT_EQ(std::string(), root->frame_name());
- EXPECT_EQ(rvh->main_frame_id(), root->frame_id());
-}
-
-// Test that we can navigate away if the previous renderer doesn't clean up its
-// child frames.
-IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, FrameTreeAfterCrash) {
- ASSERT_TRUE(test_server()->Start());
- NavigateToURL(shell(),
- test_server()->GetURL("files/frame_tree/top.html"));
-
- // Crash the renderer so that it doesn't send any FrameDetached messages.
- WindowedNotificationObserver crash_observer(
- NOTIFICATION_RENDERER_PROCESS_CLOSED,
- NotificationService::AllSources());
- NavigateToURL(shell(), GURL(kChromeUICrashURL));
- crash_observer.Wait();
-
- // The frame tree should be cleared, and the frame ID should be reset.
- WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
- RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
- wc->GetRenderViewHost());
- FrameTreeNode* root = wc->GetFrameTree()->root();
- EXPECT_EQ(0UL, root->child_count());
- EXPECT_EQ(FrameTreeNode::kInvalidFrameId, root->frame_id());
- EXPECT_EQ(rvh->main_frame_id(), root->frame_id());
-
- // Navigate to a new URL.
- NavigateToURL(shell(), test_server()->GetURL("files/title1.html"));
-
- // The frame ID should now be set.
- EXPECT_EQ(0UL, root->child_count());
- EXPECT_NE(FrameTreeNode::kInvalidFrameId, root->frame_id());
- EXPECT_EQ(rvh->main_frame_id(), root->frame_id());
-}
-
-// Test that we can navigate away if the previous renderer doesn't clean up its
-// child frames.
-IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, NavigateWithLeftoverFrames) {
- host_resolver()->AddRule("*", "127.0.0.1");
- ASSERT_TRUE(test_server()->Start());
-
- GURL base_url = test_server()->GetURL("files/site_isolation/");
- GURL::Replacements replace_host;
- std::string host_str("A.com"); // Must stay in scope with replace_host.
- replace_host.SetHostStr(host_str);
- base_url = base_url.ReplaceComponents(replace_host);
-
- NavigateToURL(shell(),
- test_server()->GetURL("files/frame_tree/top.html"));
-
- // Hang the renderer so that it doesn't send any FrameDetached messages.
- // (This navigation will never complete, so don't wait for it.)
- shell()->LoadURL(GURL(kChromeUIHangURL));
-
- // Check that the frame tree still has children.
- WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
- FrameTreeNode* root = wc->GetFrameTree()->root();
- ASSERT_EQ(3UL, root->child_count());
-
- // Navigate to a new URL. We use LoadURL because NavigateToURL will try to
- // wait for the previous navigation to stop.
- TestNavigationObserver tab_observer(wc, 1);
- shell()->LoadURL(base_url.Resolve("blank.html"));
- tab_observer.Wait();
-
- // The frame tree should now be cleared, and the frame ID should be valid.
- EXPECT_EQ(0UL, root->child_count());
- EXPECT_NE(FrameTreeNode::kInvalidFrameId, root->frame_id());
-}
-
-// Tests that the |should_replace_current_entry| flag persists correctly across
-// request transfers that began with a cross-process navigation.
-IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
- ReplaceEntryCrossProcessThenTranfers) {
- const NavigationController& controller =
- shell()->web_contents()->GetController();
- host_resolver()->AddRule("*", "127.0.0.1");
- ASSERT_TRUE(test_server()->Start());
-
- // These must all stay in scope with replace_host.
- GURL::Replacements replace_host;
- std::string a_com("A.com");
- std::string b_com("B.com");
-
- // Navigate to a starting URL, so there is a history entry to replace.
- GURL url1 = test_server()->GetURL("files/site_isolation/blank.html?1");
- NavigateToURL(shell(), url1);
-
- // Force all future navigations to transfer. Note that this includes same-site
- // navigiations which may cause double process swaps (via OpenURL and then via
- // transfer). This test intentionally exercises that case.
- ShellContentBrowserClient::SetSwapProcessesForRedirect(true);
-
- // Navigate to a page on A.com with entry replacement. This navigation is
- // cross-site, so the renderer will send it to the browser via OpenURL to give
- // to a new process. It will then be transferred into yet another process due
- // to the call above.
- GURL url2 = test_server()->GetURL("files/site_isolation/blank.html?2");
- replace_host.SetHostStr(a_com);
- url2 = url2.ReplaceComponents(replace_host);
- NavigateToURLContentInitiated(shell(), url2, true);
-
- // There should be one history entry. url2 should have replaced url1.
- EXPECT_TRUE(controller.GetPendingEntry() == NULL);
- EXPECT_EQ(1, controller.GetEntryCount());
- EXPECT_EQ(0, controller.GetCurrentEntryIndex());
- EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL());
-
- // Now navigate as before to a page on B.com, but normally (without
- // replacement). This will still perform a double process-swap as above, via
- // OpenURL and then transfer.
- GURL url3 = test_server()->GetURL("files/site_isolation/blank.html?3");
- replace_host.SetHostStr(b_com);
- url3 = url3.ReplaceComponents(replace_host);
- NavigateToURLContentInitiated(shell(), url3, false);
-
- // There should be two history entries. url2 should have replaced url1. url2
- // should not have replaced url3.
- EXPECT_TRUE(controller.GetPendingEntry() == NULL);
- EXPECT_EQ(2, controller.GetEntryCount());
- EXPECT_EQ(1, controller.GetCurrentEntryIndex());
- EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL());
- EXPECT_EQ(url3, controller.GetEntryAtIndex(1)->GetURL());
-}
-
-// Tests that the |should_replace_current_entry| flag persists correctly across
-// request transfers that began with a content-initiated in-process
-// navigation. This test is the same as the test above, except transfering from
-// in-process.
-IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
- ReplaceEntryInProcessThenTranfers) {
- const NavigationController& controller =
- shell()->web_contents()->GetController();
- ASSERT_TRUE(test_server()->Start());
-
- // Navigate to a starting URL, so there is a history entry to replace.
- GURL url = test_server()->GetURL("files/site_isolation/blank.html?1");
- NavigateToURL(shell(), url);
-
- // Force all future navigations to transfer. Note that this includes same-site
- // navigiations which may cause double process swaps (via OpenURL and then via
- // transfer). All navigations in this test are same-site, so it only swaps
- // processes via request transfer.
- ShellContentBrowserClient::SetSwapProcessesForRedirect(true);
-
- // Navigate in-process with entry replacement. It will then be transferred
- // into a new one due to the call above.
- GURL url2 = test_server()->GetURL("files/site_isolation/blank.html?2");
- NavigateToURLContentInitiated(shell(), url2, true);
-
- // There should be one history entry. url2 should have replaced url1.
- EXPECT_TRUE(controller.GetPendingEntry() == NULL);
- EXPECT_EQ(1, controller.GetEntryCount());
- EXPECT_EQ(0, controller.GetCurrentEntryIndex());
- EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL());
-
- // Now navigate as before, but without replacement.
- GURL url3 = test_server()->GetURL("files/site_isolation/blank.html?3");
- NavigateToURLContentInitiated(shell(), url3, false);
-
- // There should be two history entries. url2 should have replaced url1. url2
- // should not have replaced url3.
- EXPECT_TRUE(controller.GetPendingEntry() == NULL);
- EXPECT_EQ(2, controller.GetEntryCount());
- EXPECT_EQ(1, controller.GetCurrentEntryIndex());
- EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL());
- EXPECT_EQ(url3, controller.GetEntryAtIndex(1)->GetURL());
-}
-
-// Tests that the |should_replace_current_entry| flag persists correctly across
-// request transfers that cross processes twice from renderer policy.
-IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
- ReplaceEntryCrossProcessTwice) {
- const NavigationController& controller =
- shell()->web_contents()->GetController();
- host_resolver()->AddRule("*", "127.0.0.1");
- ASSERT_TRUE(test_server()->Start());
-
- // These must all stay in scope with replace_host.
- GURL::Replacements replace_host;
- std::string a_com("A.com");
- std::string b_com("B.com");
-
- // Navigate to a starting URL, so there is a history entry to replace.
- GURL url1 = test_server()->GetURL("files/site_isolation/blank.html?1");
- NavigateToURL(shell(), url1);
-
- // Navigate to a page on A.com which redirects to B.com with entry
- // replacement. This will switch processes via OpenURL twice. First to A.com,
- // and second in response to the server redirect to B.com. The second swap is
- // also renderer-initiated via OpenURL because decidePolicyForNavigation is
- // currently applied on redirects.
- GURL url2b = test_server()->GetURL("files/site_isolation/blank.html?2");
- replace_host.SetHostStr(b_com);
- url2b = url2b.ReplaceComponents(replace_host);
- GURL url2a = test_server()->GetURL(
- "server-redirect?" + net::EscapeQueryParamValue(url2b.spec(), false));
- replace_host.SetHostStr(a_com);
- url2a = url2a.ReplaceComponents(replace_host);
- NavigateToURLContentInitiated(shell(), url2a, true);
-
- // There should be one history entry. url2b should have replaced url1.
- EXPECT_TRUE(controller.GetPendingEntry() == NULL);
- EXPECT_EQ(1, controller.GetEntryCount());
- EXPECT_EQ(0, controller.GetCurrentEntryIndex());
- EXPECT_EQ(url2b, controller.GetEntryAtIndex(0)->GetURL());
-
- // Now repeat without replacement.
- GURL url3b = test_server()->GetURL("files/site_isolation/blank.html?3");
- replace_host.SetHostStr(b_com);
- url3b = url3b.ReplaceComponents(replace_host);
- GURL url3a = test_server()->GetURL(
- "server-redirect?" + net::EscapeQueryParamValue(url3b.spec(), false));
- replace_host.SetHostStr(a_com);
- url3a = url3a.ReplaceComponents(replace_host);
- NavigateToURLContentInitiated(shell(), url3a, false);
-
- // There should be two history entries. url2b should have replaced url1. url2b
- // should not have replaced url3b.
- EXPECT_TRUE(controller.GetPendingEntry() == NULL);
- EXPECT_EQ(2, controller.GetEntryCount());
- EXPECT_EQ(1, controller.GetCurrentEntryIndex());
- EXPECT_EQ(url2b, controller.GetEntryAtIndex(0)->GetURL());
- EXPECT_EQ(url3b, controller.GetEntryAtIndex(1)->GetURL());
-}
-
} // namespace content
diff --git a/chromium/content/browser/speech/endpointer/energy_endpointer.cc b/chromium/content/browser/speech/endpointer/energy_endpointer.cc
index d8d1274994d..30f3770286e 100644
--- a/chromium/content/browser/speech/endpointer/energy_endpointer.cc
+++ b/chromium/content/browser/speech/endpointer/energy_endpointer.cc
@@ -238,7 +238,7 @@ void EnergyEndpointer::ProcessAudioFrame(int64 time_us,
// Check that this is user input audio vs. pre-input adaptation audio.
// Input audio starts when the user indicates start of input, by e.g.
- // pressing push-to-talk. Audio recieved prior to that is used to update
+ // pressing push-to-talk. Audio received prior to that is used to update
// noise and speech level estimates.
if (!estimating_environment_) {
bool decision = false;
diff --git a/chromium/content/browser/speech/google_one_shot_remote_engine.cc b/chromium/content/browser/speech/google_one_shot_remote_engine.cc
index 575dd5fda6d..e52e713deb5 100644
--- a/chromium/content/browser/speech/google_one_shot_remote_engine.cc
+++ b/chromium/content/browser/speech/google_one_shot_remote_engine.cc
@@ -16,6 +16,7 @@
#include "google_apis/google_api_keys.h"
#include "net/base/escape.h"
#include "net/base/load_flags.h"
+#include "net/url_request/http_user_agent_settings.h"
#include "net/url_request/url_fetcher.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
@@ -174,9 +175,12 @@ void GoogleOneShotRemoteEngine::StartRecognition() {
// TODO(pauljensen): GoogleOneShotRemoteEngine should be constructed with
// a reference to the HttpUserAgentSettings rather than accessing the
// accept language through the URLRequestContext.
- std::string accepted_language_list = request_context->GetAcceptLanguage();
- size_t separator = accepted_language_list.find_first_of(",;");
- lang_param = accepted_language_list.substr(0, separator);
+ if (request_context->http_user_agent_settings()) {
+ std::string accepted_language_list =
+ request_context->http_user_agent_settings()->GetAcceptLanguage();
+ size_t separator = accepted_language_list.find_first_of(",;");
+ lang_param = accepted_language_list.substr(0, separator);
+ }
}
if (lang_param.empty())
diff --git a/chromium/content/browser/speech/google_one_shot_remote_engine_unittest.cc b/chromium/content/browser/speech/google_one_shot_remote_engine_unittest.cc
index f7c65615e4e..a16329cadff 100644
--- a/chromium/content/browser/speech/google_one_shot_remote_engine_unittest.cc
+++ b/chromium/content/browser/speech/google_one_shot_remote_engine_unittest.cc
@@ -83,7 +83,7 @@ TEST_F(GoogleOneShotRemoteEngineTest, BasicTest) {
"[{\"utterance\":\"123456\",\"confidence\":0.9}]}");
EXPECT_EQ(error_, SPEECH_RECOGNITION_ERROR_NONE);
EXPECT_EQ(1U, result().hypotheses.size());
- EXPECT_EQ(ASCIIToUTF16("123456"), result().hypotheses[0].utterance);
+ EXPECT_EQ(base::ASCIIToUTF16("123456"), result().hypotheses[0].utterance);
EXPECT_EQ(0.9, result().hypotheses[0].confidence);
// Normal success case with multiple results.
@@ -93,9 +93,9 @@ TEST_F(GoogleOneShotRemoteEngineTest, BasicTest) {
"{\"utterance\":\"123456\",\"confidence\":0.5}]}");
EXPECT_EQ(error_, SPEECH_RECOGNITION_ERROR_NONE);
EXPECT_EQ(2u, result().hypotheses.size());
- EXPECT_EQ(ASCIIToUTF16("hello"), result().hypotheses[0].utterance);
+ EXPECT_EQ(base::ASCIIToUTF16("hello"), result().hypotheses[0].utterance);
EXPECT_EQ(0.9, result().hypotheses[0].confidence);
- EXPECT_EQ(ASCIIToUTF16("123456"), result().hypotheses[1].utterance);
+ EXPECT_EQ(base::ASCIIToUTF16("123456"), result().hypotheses[1].utterance);
EXPECT_EQ(0.5, result().hypotheses[1].confidence);
// Zero results.
diff --git a/chromium/content/browser/speech/google_streaming_remote_engine.cc b/chromium/content/browser/speech/google_streaming_remote_engine.cc
index b6b68d17ced..f9e2e6ea04d 100644
--- a/chromium/content/browser/speech/google_streaming_remote_engine.cc
+++ b/chromium/content/browser/speech/google_streaming_remote_engine.cc
@@ -7,7 +7,6 @@
#include <vector>
#include "base/bind.h"
-#include "base/command_line.h"
#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
@@ -15,12 +14,12 @@
#include "base/time/time.h"
#include "content/browser/speech/audio_buffer.h"
#include "content/browser/speech/proto/google_streaming_api.pb.h"
-#include "content/public/common/content_switches.h"
#include "content/public/common/speech_recognition_error.h"
#include "content/public/common/speech_recognition_result.h"
#include "google_apis/google_api_keys.h"
#include "net/base/escape.h"
#include "net/base/load_flags.h"
+#include "net/url_request/http_user_agent_settings.h"
#include "net/url_request/url_fetcher.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
@@ -68,21 +67,6 @@ void DumpResponse(const std::string& response) {
}
}
-std::string GetAPIKey() {
- const CommandLine& command_line = *CommandLine::ForCurrentProcess();
- if (command_line.HasSwitch(switches::kSpeechRecognitionWebserviceKey)) {
- DVLOG(1) << "GetAPIKey() used key from command-line.";
- return command_line.GetSwitchValueASCII(
- switches::kSpeechRecognitionWebserviceKey);
- }
-
- std::string api_key = google_apis::GetAPIKey();
- if (api_key.empty())
- DVLOG(1) << "GetAPIKey() returned empty string!";
-
- return api_key;
-}
-
} // namespace
const int GoogleStreamingRemoteEngine::kAudioPacketIntervalMs = 100;
@@ -316,7 +300,7 @@ GoogleStreamingRemoteEngine::ConnectBothStreams(const FSMEventArgs&) {
// Setup downstream fetcher.
std::vector<std::string> downstream_args;
downstream_args.push_back(
- "key=" + net::EscapeQueryParamValue(GetAPIKey(), true));
+ "key=" + net::EscapeQueryParamValue(google_apis::GetAPIKey(), true));
downstream_args.push_back("pair=" + request_key);
downstream_args.push_back("output=pb");
GURL downstream_url(std::string(kWebServiceBaseUrl) +
@@ -336,7 +320,7 @@ GoogleStreamingRemoteEngine::ConnectBothStreams(const FSMEventArgs&) {
// TODO(hans): Support for user-selected grammars.
std::vector<std::string> upstream_args;
upstream_args.push_back("key=" +
- net::EscapeQueryParamValue(GetAPIKey(), true));
+ net::EscapeQueryParamValue(google_apis::GetAPIKey(), true));
upstream_args.push_back("pair=" + request_key);
upstream_args.push_back("output=pb");
upstream_args.push_back(
@@ -455,7 +439,7 @@ GoogleStreamingRemoteEngine::ProcessDownstreamResponse(
DCHECK(ws_alternative.has_transcript());
// TODO(hans): Perhaps the transcript should be required in the proto?
if (ws_alternative.has_transcript())
- hypothesis.utterance = UTF8ToUTF16(ws_alternative.transcript());
+ hypothesis.utterance = base::UTF8ToUTF16(ws_alternative.transcript());
result.hypotheses.push_back(hypothesis);
}
@@ -563,10 +547,13 @@ std::string GoogleStreamingRemoteEngine::GetAcceptedLanguages() const {
// TODO(pauljensen): GoogleStreamingRemoteEngine should be constructed with
// a reference to the HttpUserAgentSettings rather than accessing the
// accept language through the URLRequestContext.
- std::string accepted_language_list = request_context->GetAcceptLanguage();
- size_t separator = accepted_language_list.find_first_of(",;");
- if (separator != std::string::npos)
- langs = accepted_language_list.substr(0, separator);
+ if (request_context->http_user_agent_settings()) {
+ std::string accepted_language_list =
+ request_context->http_user_agent_settings()->GetAcceptLanguage();
+ size_t separator = accepted_language_list.find_first_of(",;");
+ if (separator != std::string::npos)
+ langs = accepted_language_list.substr(0, separator);
+ }
}
if (langs.empty())
langs = "en-US";
@@ -575,8 +562,8 @@ std::string GoogleStreamingRemoteEngine::GetAcceptedLanguages() const {
// TODO(primiano): Is there any utility in the codebase that already does this?
std::string GoogleStreamingRemoteEngine::GenerateRequestKey() const {
- const int64 kKeepLowBytes = GG_LONGLONG(0x00000000FFFFFFFF);
- const int64 kKeepHighBytes = GG_LONGLONG(0xFFFFFFFF00000000);
+ const int64 kKeepLowBytes = 0x00000000FFFFFFFFLL;
+ const int64 kKeepHighBytes = 0xFFFFFFFF00000000LL;
// Just keep the least significant bits of timestamp, in order to reduce
// probability of collisions.
diff --git a/chromium/content/browser/speech/google_streaming_remote_engine_unittest.cc b/chromium/content/browser/speech/google_streaming_remote_engine_unittest.cc
index dd0ef89cad9..ab703d7ac04 100644
--- a/chromium/content/browser/speech/google_streaming_remote_engine_unittest.cc
+++ b/chromium/content/browser/speech/google_streaming_remote_engine_unittest.cc
@@ -6,7 +6,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
-#include "base/safe_numerics.h"
+#include "base/numerics/safe_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/sys_byteorder.h"
#include "content/browser/speech/audio_buffer.h"
@@ -20,7 +20,7 @@
#include "testing/gtest/include/gtest/gtest.h"
using base::HostToNet32;
-using base::checked_numeric_cast;
+using base::checked_cast;
using net::URLRequestStatus;
using net::TestURLFetcher;
using net::TestURLFetcherFactory;
@@ -112,9 +112,9 @@ TEST_F(GoogleStreamingRemoteEngineTest, SingleDefinitiveResult) {
SpeechRecognitionResult& result = results.back();
result.is_provisional = false;
result.hypotheses.push_back(
- SpeechRecognitionHypothesis(UTF8ToUTF16("hypothesis 1"), 0.1F));
+ SpeechRecognitionHypothesis(base::UTF8ToUTF16("hypothesis 1"), 0.1F));
result.hypotheses.push_back(
- SpeechRecognitionHypothesis(UTF8ToUTF16("hypothesis 2"), 0.2F));
+ SpeechRecognitionHypothesis(base::UTF8ToUTF16("hypothesis 2"), 0.2F));
ProvideMockResultDownstream(result);
ExpectResultsReceived(results);
@@ -142,8 +142,8 @@ TEST_F(GoogleStreamingRemoteEngineTest, SeveralStreamingResults) {
SpeechRecognitionResult& result = results.back();
result.is_provisional = (i % 2 == 0); // Alternate result types.
float confidence = result.is_provisional ? 0.0F : (i * 0.1F);
- result.hypotheses.push_back(
- SpeechRecognitionHypothesis(UTF8ToUTF16("hypothesis"), confidence));
+ result.hypotheses.push_back(SpeechRecognitionHypothesis(
+ base::UTF8ToUTF16("hypothesis"), confidence));
ProvideMockResultDownstream(result);
ExpectResultsReceived(results);
@@ -161,7 +161,7 @@ TEST_F(GoogleStreamingRemoteEngineTest, SeveralStreamingResults) {
SpeechRecognitionResult& result = results.back();
result.is_provisional = false;
result.hypotheses.push_back(
- SpeechRecognitionHypothesis(UTF8ToUTF16("The final result"), 1.0F));
+ SpeechRecognitionHypothesis(base::UTF8ToUTF16("The final result"), 1.0F));
ProvideMockResultDownstream(result);
ExpectResultsReceived(results);
ASSERT_TRUE(engine_under_test_->IsRecognitionPending());
@@ -188,7 +188,7 @@ TEST_F(GoogleStreamingRemoteEngineTest, NoFinalResultAfterAudioChunksEnded) {
results.push_back(SpeechRecognitionResult());
SpeechRecognitionResult& result = results.back();
result.hypotheses.push_back(
- SpeechRecognitionHypothesis(UTF8ToUTF16("hypothesis"), 1.0F));
+ SpeechRecognitionHypothesis(base::UTF8ToUTF16("hypothesis"), 1.0F));
ProvideMockResultDownstream(result);
ExpectResultsReceived(results);
ASSERT_TRUE(engine_under_test_->IsRecognitionPending());
@@ -228,7 +228,7 @@ TEST_F(GoogleStreamingRemoteEngineTest, NoMatchError) {
SpeechRecognitionResult& result = results.back();
result.is_provisional = true;
result.hypotheses.push_back(
- SpeechRecognitionHypothesis(UTF8ToUTF16("The final result"), 0.0F));
+ SpeechRecognitionHypothesis(base::UTF8ToUTF16("The final result"), 0.0F));
ProvideMockResultDownstream(result);
ExpectResultsReceived(results);
ASSERT_TRUE(engine_under_test_->IsRecognitionPending());
@@ -305,7 +305,7 @@ TEST_F(GoogleStreamingRemoteEngineTest, Stability) {
SpeechRecognitionResult& result = results.back();
result.is_provisional = true;
result.hypotheses.push_back(
- SpeechRecognitionHypothesis(UTF8ToUTF16("foo"), 0.5));
+ SpeechRecognitionHypothesis(base::UTF8ToUTF16("foo"), 0.5));
// Check that the protobuf generated the expected result.
ExpectResultsReceived(results);
@@ -425,7 +425,7 @@ void GoogleStreamingRemoteEngineTest::ProvideMockResultDownstream(
proto_result->add_alternative();
const SpeechRecognitionHypothesis& hypothesis = result.hypotheses[i];
proto_alternative->set_confidence(hypothesis.confidence);
- proto_alternative->set_transcript(UTF16ToUTF8(hypothesis.utterance));
+ proto_alternative->set_transcript(base::UTF16ToUTF8(hypothesis.utterance));
}
ProvideMockProtoResultDownstream(proto_event);
}
@@ -490,7 +490,7 @@ std::string GoogleStreamingRemoteEngineTest::SerializeProtobufResponse(
// Prepend 4 byte prefix length indication to the protobuf message as
// envisaged by the google streaming recognition webservice protocol.
- uint32 prefix = HostToNet32(checked_numeric_cast<uint32>(msg_string.size()));
+ uint32 prefix = HostToNet32(checked_cast<uint32>(msg_string.size()));
msg_string.insert(0, reinterpret_cast<char*>(&prefix), sizeof(prefix));
return msg_string;
diff --git a/chromium/content/browser/speech/input_tag_speech_browsertest.cc b/chromium/content/browser/speech/input_tag_speech_browsertest.cc
deleted file mode 100644
index 10c18785dd9..00000000000
--- a/chromium/content/browser/speech/input_tag_speech_browsertest.cc
+++ /dev/null
@@ -1,136 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/command_line.h"
-#include "base/files/file_path.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/message_loop/message_loop.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/utf_string_conversions.h"
-#include "content/browser/renderer_host/render_view_host_impl.h"
-#include "content/browser/web_contents/web_contents_impl.h"
-#include "content/public/browser/notification_types.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/common/content_switches.h"
-#include "content/public/common/speech_recognition_error.h"
-#include "content/public/common/speech_recognition_result.h"
-#include "content/public/common/url_constants.h"
-#include "content/public/test/fake_speech_recognition_manager.h"
-#include "content/public/test/test_utils.h"
-#include "content/shell/browser/shell.h"
-#include "content/test/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
-#include "third_party/WebKit/public/web/WebInputEvent.h"
-
-namespace content {
-
-class InputTagSpeechBrowserTest : public ContentBrowserTest {
- public:
- // ContentBrowserTest methods
- virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
- EXPECT_TRUE(!command_line->HasSwitch(switches::kDisableSpeechInput));
- }
-
- protected:
- void LoadAndStartSpeechRecognitionTest(const char* filename) {
- // The test page calculates the speech button's coordinate in the page on
- // load & sets that coordinate in the URL fragment. We send mouse down & up
- // events at that coordinate to trigger speech recognition.
- GURL test_url = GetTestUrl("speech", filename);
- NavigateToURL(shell(), test_url);
-
- blink::WebMouseEvent mouse_event;
- mouse_event.type = blink::WebInputEvent::MouseDown;
- mouse_event.button = blink::WebMouseEvent::ButtonLeft;
- mouse_event.x = 0;
- mouse_event.y = 0;
- mouse_event.clickCount = 1;
- WebContents* web_contents = shell()->web_contents();
-
- WindowedNotificationObserver observer(
- NOTIFICATION_LOAD_STOP,
- Source<NavigationController>(&web_contents->GetController()));
- web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event);
- mouse_event.type = blink::WebInputEvent::MouseUp;
- web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event);
- fake_speech_recognition_manager_.WaitForRecognitionStarted();
-
- // We should wait for a navigation event, raised by the test page JS code
- // upon the onwebkitspeechchange event, in all cases except when the
- // speech response is inhibited.
- if (fake_speech_recognition_manager_.should_send_fake_response())
- observer.Wait();
- }
-
- void RunSpeechRecognitionTest(const char* filename) {
- // The fake speech input manager would receive the speech input
- // request and return the test string as recognition result. The test page
- // then sets the URL fragment as 'pass' if it received the expected string.
- LoadAndStartSpeechRecognitionTest(filename);
-
- EXPECT_EQ("pass", shell()->web_contents()->GetLastCommittedURL().ref());
- }
-
- // ContentBrowserTest methods.
- virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
- fake_speech_recognition_manager_.set_should_send_fake_response(true);
- speech_recognition_manager_ = &fake_speech_recognition_manager_;
-
- // Inject the fake manager factory so that the test result is returned to
- // the web page.
- SpeechRecognitionManager::SetManagerForTesting(speech_recognition_manager_);
- }
-
- virtual void TearDownInProcessBrowserTestFixture() OVERRIDE {
- speech_recognition_manager_ = NULL;
- }
-
- FakeSpeechRecognitionManager fake_speech_recognition_manager_;
-
- // This is used by the static |fakeManager|, and it is a pointer rather than a
- // direct instance per the style guide.
- static SpeechRecognitionManager* speech_recognition_manager_;
-};
-
-SpeechRecognitionManager*
- InputTagSpeechBrowserTest::speech_recognition_manager_ = NULL;
-
-// TODO(satish): Once this flakiness has been fixed, add a second test here to
-// check for sending many clicks in succession to the speech button and verify
-// that it doesn't cause any crash but works as expected. This should act as the
-// test for http://crbug.com/59173
-//
-// TODO(satish): Similar to above, once this flakiness has been fixed add
-// another test here to check that when speech recognition is in progress and
-// a renderer crashes, we get a call to
-// SpeechRecognitionManager::CancelAllRequestsWithDelegate.
-IN_PROC_BROWSER_TEST_F(InputTagSpeechBrowserTest, TestBasicRecognition) {
- RunSpeechRecognitionTest("basic_recognition.html");
- EXPECT_TRUE(fake_speech_recognition_manager_.grammar().empty());
-}
-
-IN_PROC_BROWSER_TEST_F(InputTagSpeechBrowserTest, GrammarAttribute) {
- RunSpeechRecognitionTest("grammar_attribute.html");
- EXPECT_EQ("http://example.com/grammar.xml",
- fake_speech_recognition_manager_.grammar());
-}
-
-// Flaky on Linux, Windows and Mac http://crbug.com/140765.
-IN_PROC_BROWSER_TEST_F(InputTagSpeechBrowserTest, DISABLED_TestCancelAll) {
- // The test checks that the cancel-all callback gets issued when a session
- // is pending, so don't send a fake response.
- // We are not expecting a navigation event being raised from the JS of the
- // test page JavaScript in this case.
- fake_speech_recognition_manager_.set_should_send_fake_response(false);
-
- LoadAndStartSpeechRecognitionTest("basic_recognition.html");
-
- // Make the renderer crash. This should trigger
- // InputTagSpeechDispatcherHost to cancel all pending sessions.
- NavigateToURL(shell(), GURL(kChromeUICrashURL));
-
- EXPECT_TRUE(fake_speech_recognition_manager_.did_cancel_all());
-}
-
-} // namespace content
diff --git a/chromium/content/browser/speech/input_tag_speech_dispatcher_host.cc b/chromium/content/browser/speech/input_tag_speech_dispatcher_host.cc
deleted file mode 100644
index 23169c78105..00000000000
--- a/chromium/content/browser/speech/input_tag_speech_dispatcher_host.cc
+++ /dev/null
@@ -1,227 +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/speech/input_tag_speech_dispatcher_host.h"
-
-#include "base/bind.h"
-#include "base/lazy_instance.h"
-#include "content/browser/browser_plugin/browser_plugin_guest.h"
-#include "content/browser/renderer_host/render_view_host_impl.h"
-#include "content/browser/speech/speech_recognition_manager_impl.h"
-#include "content/browser/web_contents/web_contents_impl.h"
-#include "content/common/speech_recognition_messages.h"
-#include "content/public/browser/speech_recognition_manager_delegate.h"
-#include "content/public/browser/speech_recognition_session_config.h"
-#include "content/public/browser/speech_recognition_session_context.h"
-
-namespace {
-const uint32 kMaxHypothesesForSpeechInputTag = 6;
-}
-
-namespace content {
-
-InputTagSpeechDispatcherHost::InputTagSpeechDispatcherHost(
- bool is_guest,
- int render_process_id,
- net::URLRequestContextGetter* url_request_context_getter)
- : is_guest_(is_guest),
- render_process_id_(render_process_id),
- url_request_context_getter_(url_request_context_getter),
- weak_factory_(this) {
- // Do not add any non-trivial initialization here, instead do it lazily when
- // required (e.g. see the method |SpeechRecognitionManager::GetInstance()|) or
- // add an Init() method.
-}
-
-InputTagSpeechDispatcherHost::~InputTagSpeechDispatcherHost() {
- SpeechRecognitionManager::GetInstance()->AbortAllSessionsForRenderProcess(
- render_process_id_);
-}
-
-base::WeakPtr<InputTagSpeechDispatcherHost>
-InputTagSpeechDispatcherHost::AsWeakPtr() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- return weak_factory_.GetWeakPtr();
-}
-
-bool InputTagSpeechDispatcherHost::OnMessageReceived(
- const IPC::Message& message, bool* message_was_ok) {
- bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(InputTagSpeechDispatcherHost, message,
- *message_was_ok)
- IPC_MESSAGE_HANDLER(InputTagSpeechHostMsg_StartRecognition,
- OnStartRecognition)
- IPC_MESSAGE_HANDLER(InputTagSpeechHostMsg_CancelRecognition,
- OnCancelRecognition)
- IPC_MESSAGE_HANDLER(InputTagSpeechHostMsg_StopRecording,
- OnStopRecording)
- IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP()
- return handled;
-}
-
-void InputTagSpeechDispatcherHost::OverrideThreadForMessage(
- const IPC::Message& message,
- BrowserThread::ID* thread) {
- if (message.type() == InputTagSpeechHostMsg_StartRecognition::ID)
- *thread = BrowserThread::UI;
-}
-
-void InputTagSpeechDispatcherHost::OnChannelClosing() {
- weak_factory_.InvalidateWeakPtrs();
-}
-
-void InputTagSpeechDispatcherHost::OnStartRecognition(
- const InputTagSpeechHostMsg_StartRecognition_Params& params) {
- InputTagSpeechHostMsg_StartRecognition_Params input_params(params);
- int render_process_id = render_process_id_;
- // The chrome layer is mostly oblivious to BrowserPlugin guests and so it
- // cannot correctly place the speech bubble relative to a guest. Thus, we
- // set up the speech recognition context relative to the embedder.
- int guest_render_view_id = MSG_ROUTING_NONE;
- if (is_guest_) {
- RenderViewHostImpl* render_view_host =
- RenderViewHostImpl::FromID(render_process_id_, params.render_view_id);
- WebContentsImpl* web_contents = static_cast<WebContentsImpl*>(
- WebContents::FromRenderViewHost(render_view_host));
- BrowserPluginGuest* guest = web_contents->GetBrowserPluginGuest();
- input_params.element_rect.set_origin(
- guest->GetScreenCoordinates(input_params.element_rect.origin()));
- guest_render_view_id = params.render_view_id;
- render_process_id =
- guest->embedder_web_contents()->GetRenderProcessHost()->GetID();
- input_params.render_view_id =
- guest->embedder_web_contents()->GetRoutingID();
- }
- bool filter_profanities =
- SpeechRecognitionManagerImpl::GetInstance() &&
- SpeechRecognitionManagerImpl::GetInstance()->delegate() &&
- SpeechRecognitionManagerImpl::GetInstance()->delegate()->
- FilterProfanities(render_process_id_);
-
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::Bind(
- &InputTagSpeechDispatcherHost::StartRecognitionOnIO,
- this,
- render_process_id,
- guest_render_view_id,
- input_params,
- filter_profanities));
-}
-
-void InputTagSpeechDispatcherHost::StartRecognitionOnIO(
- int render_process_id,
- int guest_render_view_id,
- const InputTagSpeechHostMsg_StartRecognition_Params& params,
- bool filter_profanities) {
- SpeechRecognitionSessionContext context;
- context.render_process_id = render_process_id;
- context.render_view_id = params.render_view_id;
- context.guest_render_view_id = guest_render_view_id;
- // Keep context.embedder_render_process_id and context.embedder_render_view_id
- // unset.
- context.request_id = params.request_id;
- context.element_rect = params.element_rect;
-
- SpeechRecognitionSessionConfig config;
- config.language = params.language;
- if (!params.grammar.empty()) {
- config.grammars.push_back(SpeechRecognitionGrammar(params.grammar));
- }
- config.max_hypotheses = kMaxHypothesesForSpeechInputTag;
- config.origin_url = params.origin_url;
- config.initial_context = context;
- config.url_request_context_getter = url_request_context_getter_.get();
- config.filter_profanities = filter_profanities;
- config.event_listener = AsWeakPtr();
-
- int session_id = SpeechRecognitionManager::GetInstance()->CreateSession(
- config);
- DCHECK_NE(session_id, SpeechRecognitionManager::kSessionIDInvalid);
- SpeechRecognitionManager::GetInstance()->StartSession(session_id);
-}
-
-void InputTagSpeechDispatcherHost::OnCancelRecognition(int render_view_id,
- int request_id) {
- int session_id = SpeechRecognitionManager::GetInstance()->GetSession(
- render_process_id_, render_view_id, request_id);
-
- // The renderer might provide an invalid |request_id| if the session was not
- // started as expected, e.g., due to unsatisfied security requirements.
- if (session_id != SpeechRecognitionManager::kSessionIDInvalid)
- SpeechRecognitionManager::GetInstance()->AbortSession(session_id);
-}
-
-void InputTagSpeechDispatcherHost::OnStopRecording(int render_view_id,
- int request_id) {
- int session_id = SpeechRecognitionManager::GetInstance()->GetSession(
- render_process_id_, render_view_id, request_id);
-
- // The renderer might provide an invalid |request_id| if the session was not
- // started as expected, e.g., due to unsatisfied security requirements.
- if (session_id != SpeechRecognitionManager::kSessionIDInvalid) {
- SpeechRecognitionManager::GetInstance()->StopAudioCaptureForSession(
- session_id);
- }
-}
-
-// -------- SpeechRecognitionEventListener interface implementation -----------
-void InputTagSpeechDispatcherHost::OnRecognitionResults(
- int session_id,
- const SpeechRecognitionResults& results) {
- DVLOG(1) << "InputTagSpeechDispatcherHost::OnRecognitionResults enter";
-
- const SpeechRecognitionSessionContext& context =
- SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id);
-
- int render_view_id =
- context.guest_render_view_id == MSG_ROUTING_NONE ?
- context.render_view_id : context.guest_render_view_id;
- Send(new InputTagSpeechMsg_SetRecognitionResults(
- render_view_id,
- context.request_id,
- results));
- DVLOG(1) << "InputTagSpeechDispatcherHost::OnRecognitionResults exit";
-}
-
-void InputTagSpeechDispatcherHost::OnAudioEnd(int session_id) {
- DVLOG(1) << "InputTagSpeechDispatcherHost::OnAudioEnd enter";
-
- const SpeechRecognitionSessionContext& context =
- SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id);
- int render_view_id =
- context.guest_render_view_id == MSG_ROUTING_NONE ?
- context.render_view_id : context.guest_render_view_id;
- Send(new InputTagSpeechMsg_RecordingComplete(render_view_id,
- context.request_id));
- DVLOG(1) << "InputTagSpeechDispatcherHost::OnAudioEnd exit";
-}
-
-void InputTagSpeechDispatcherHost::OnRecognitionEnd(int session_id) {
- DVLOG(1) << "InputTagSpeechDispatcherHost::OnRecognitionEnd enter";
- const SpeechRecognitionSessionContext& context =
- SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id);
- int render_view_id =
- context.guest_render_view_id == MSG_ROUTING_NONE ?
- context.render_view_id : context.guest_render_view_id;
- Send(new InputTagSpeechMsg_RecognitionComplete(render_view_id,
- context.request_id));
- DVLOG(1) << "InputTagSpeechDispatcherHost::OnRecognitionEnd exit";
-}
-
-// The events below are currently not used by x-webkit-speech implementation.
-void InputTagSpeechDispatcherHost::OnRecognitionStart(int session_id) {}
-void InputTagSpeechDispatcherHost::OnAudioStart(int session_id) {}
-void InputTagSpeechDispatcherHost::OnSoundStart(int session_id) {}
-void InputTagSpeechDispatcherHost::OnSoundEnd(int session_id) {}
-void InputTagSpeechDispatcherHost::OnRecognitionError(
- int session_id,
- const SpeechRecognitionError& error) {}
-void InputTagSpeechDispatcherHost::OnAudioLevelsChange(
- int session_id, float volume, float noise_volume) {}
-void InputTagSpeechDispatcherHost::OnEnvironmentEstimationComplete(
- int session_id) {}
-
-} // namespace content
diff --git a/chromium/content/browser/speech/input_tag_speech_dispatcher_host.h b/chromium/content/browser/speech/input_tag_speech_dispatcher_host.h
deleted file mode 100644
index 4d5eb60bf17..00000000000
--- a/chromium/content/browser/speech/input_tag_speech_dispatcher_host.h
+++ /dev/null
@@ -1,93 +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_SPEECH_INPUT_TAG_SPEECH_DISPATCHER_HOST_H_
-#define CONTENT_BROWSER_SPEECH_INPUT_TAG_SPEECH_DISPATCHER_HOST_H_
-
-#include "base/compiler_specific.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "content/common/content_export.h"
-#include "content/public/browser/browser_message_filter.h"
-#include "content/public/browser/speech_recognition_event_listener.h"
-#include "content/public/common/speech_recognition_result.h"
-#include "net/url_request/url_request_context_getter.h"
-
-struct InputTagSpeechHostMsg_StartRecognition_Params;
-
-namespace content {
-
-class SpeechRecognitionManager;
-
-// InputTagSpeechDispatcherHost is a delegate for Speech API messages used by
-// RenderMessageFilter. Basically it acts as a proxy, relaying the events coming
-// from the SpeechRecognitionManager to IPC messages (and vice versa).
-// It's the complement of SpeechRecognitionDispatcher (owned by RenderView).
-class CONTENT_EXPORT InputTagSpeechDispatcherHost
- : public BrowserMessageFilter,
- public SpeechRecognitionEventListener {
- public:
- InputTagSpeechDispatcherHost(
- bool guest,
- int render_process_id,
- net::URLRequestContextGetter* url_request_context_getter);
-
- base::WeakPtr<InputTagSpeechDispatcherHost> AsWeakPtr();
-
- // SpeechRecognitionEventListener methods.
- virtual void OnRecognitionStart(int session_id) OVERRIDE;
- virtual void OnAudioStart(int session_id) OVERRIDE;
- virtual void OnEnvironmentEstimationComplete(int session_id) OVERRIDE;
- virtual void OnSoundStart(int session_id) OVERRIDE;
- virtual void OnSoundEnd(int session_id) OVERRIDE;
- virtual void OnAudioEnd(int session_id) OVERRIDE;
- virtual void OnRecognitionEnd(int session_id) OVERRIDE;
- virtual void OnRecognitionResults(
- int session_id,
- const SpeechRecognitionResults& results) OVERRIDE;
- virtual void OnRecognitionError(
- int session_id,
- const SpeechRecognitionError& error) OVERRIDE;
- virtual void OnAudioLevelsChange(int session_id,
- float volume,
- float noise_volume) OVERRIDE;
-
- // BrowserMessageFilter implementation.
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
- virtual void OverrideThreadForMessage(
- const IPC::Message& message,
- BrowserThread::ID* thread) OVERRIDE;
-
- virtual void OnChannelClosing() OVERRIDE;
-
- private:
- virtual ~InputTagSpeechDispatcherHost();
-
- void OnStartRecognition(
- const InputTagSpeechHostMsg_StartRecognition_Params& params);
- void OnCancelRecognition(int render_view_id, int request_id);
- void OnStopRecording(int render_view_id, int request_id);
-
- void StartRecognitionOnIO(
- int embedder_render_process_id,
- int embedder_render_view_id,
- const InputTagSpeechHostMsg_StartRecognition_Params& params,
- bool filter_profanities);
-
- bool is_guest_;
- int render_process_id_;
- scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
-
- // Used for posting asynchronous tasks (on the IO thread) without worrying
- // about this class being destroyed in the meanwhile (due to browser shutdown)
- // since tasks pending on a destroyed WeakPtr are automatically discarded.
- base::WeakPtrFactory<InputTagSpeechDispatcherHost> weak_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(InputTagSpeechDispatcherHost);
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_SPEECH_INPUT_TAG_SPEECH_DISPATCHER_HOST_H_
diff --git a/chromium/content/browser/speech/proto/BUILD.gn b/chromium/content/browser/speech/proto/BUILD.gn
new file mode 100644
index 00000000000..fb54fb25906
--- /dev/null
+++ b/chromium/content/browser/speech/proto/BUILD.gn
@@ -0,0 +1,11 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//third_party/protobuf/proto_library.gni")
+
+proto_library("proto") {
+ sources = [
+ "google_streaming_api.proto",
+ ]
+}
diff --git a/chromium/content/browser/speech/proto/speech_proto.gyp b/chromium/content/browser/speech/proto/speech_proto.gyp
index f26021bfe23..47da49926d6 100644
--- a/chromium/content/browser/speech/proto/speech_proto.gyp
+++ b/chromium/content/browser/speech/proto/speech_proto.gyp
@@ -4,7 +4,9 @@
{
'targets': [
- {'target_name': 'speech_proto',
+ {
+ # GN version: //content/browser/speech/proto:proto
+ 'target_name': 'speech_proto',
'type': 'static_library',
'sources': [
'google_streaming_api.proto',
diff --git a/chromium/content/browser/speech/speech_recognition_browsertest.cc b/chromium/content/browser/speech/speech_recognition_browsertest.cc
index 82daf323ff3..5871aa193f8 100644
--- a/chromium/content/browser/speech/speech_recognition_browsertest.cc
+++ b/chromium/content/browser/speech/speech_recognition_browsertest.cc
@@ -15,10 +15,10 @@
#include "content/public/browser/notification_types.h"
#include "content/public/browser/web_contents.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/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
#include "content/test/mock_google_streaming_server.h"
#include "media/audio/mock_audio_manager.h"
#include "media/audio/test_audio_input_controller_factory.h"
@@ -133,6 +133,7 @@ class SpeechRecognitionBrowserTest :
size_t buffer_size,
bool fill_with_noise) {
DCHECK(controller.get());
+ const media::AudioParameters& audio_params = controller->audio_parameters();
scoped_ptr<uint8[]> audio_buffer(new uint8[buffer_size]);
if (fill_with_noise) {
for (size_t i = 0; i < buffer_size; ++i)
@@ -141,9 +142,13 @@ class SpeechRecognitionBrowserTest :
} else {
memset(audio_buffer.get(), 0, buffer_size);
}
- controller->event_handler()->OnData(controller,
- audio_buffer.get(),
- buffer_size);
+
+ scoped_ptr<media::AudioBus> audio_bus =
+ media::AudioBus::Create(audio_params);
+ audio_bus->FromInterleaved(&audio_buffer.get()[0],
+ audio_bus->frames(),
+ audio_params.bits_per_sample() / 8);
+ controller->event_handler()->OnData(controller, audio_bus.get());
}
void FeedAudioController(int duration_ms, bool feed_with_noise) {
@@ -172,7 +177,7 @@ class SpeechRecognitionBrowserTest :
SpeechRecognitionResult GetGoodSpeechResult() {
SpeechRecognitionResult result;
result.hypotheses.push_back(SpeechRecognitionHypothesis(
- UTF8ToUTF16("Pictures of the moon"), 1.0F));
+ base::UTF8ToUTF16("Pictures of the moon"), 1.0F));
return result;
}
diff --git a/chromium/content/browser/speech/speech_recognition_dispatcher_host.cc b/chromium/content/browser/speech/speech_recognition_dispatcher_host.cc
index 5b08dd2529f..05a24527efe 100644
--- a/chromium/content/browser/speech/speech_recognition_dispatcher_host.cc
+++ b/chromium/content/browser/speech/speech_recognition_dispatcher_host.cc
@@ -8,6 +8,7 @@
#include "base/command_line.h"
#include "base/lazy_instance.h"
#include "content/browser/browser_plugin/browser_plugin_guest.h"
+#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/speech/speech_recognition_manager_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
@@ -20,10 +21,9 @@
namespace content {
SpeechRecognitionDispatcherHost::SpeechRecognitionDispatcherHost(
- bool is_guest,
int render_process_id,
net::URLRequestContextGetter* context_getter)
- : is_guest_(is_guest),
+ : BrowserMessageFilter(SpeechRecognitionMsgStart),
render_process_id_(render_process_id),
context_getter_(context_getter),
weak_factory_(this) {
@@ -43,16 +43,17 @@ SpeechRecognitionDispatcherHost::AsWeakPtr() {
}
bool SpeechRecognitionDispatcherHost::OnMessageReceived(
- const IPC::Message& message, bool* message_was_ok) {
+ const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(SpeechRecognitionDispatcherHost, message,
- *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(SpeechRecognitionDispatcherHost, message)
IPC_MESSAGE_HANDLER(SpeechRecognitionHostMsg_StartRequest,
OnStartRequest)
IPC_MESSAGE_HANDLER(SpeechRecognitionHostMsg_AbortRequest,
OnAbortRequest)
IPC_MESSAGE_HANDLER(SpeechRecognitionHostMsg_StopCaptureRequest,
OnStopCaptureRequest)
+ IPC_MESSAGE_HANDLER(SpeechRecognitionHostMsg_AbortAllRequests,
+ OnAbortAllRequests)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
@@ -73,17 +74,26 @@ void SpeechRecognitionDispatcherHost::OnStartRequest(
const SpeechRecognitionHostMsg_StartRequest_Params& params) {
SpeechRecognitionHostMsg_StartRequest_Params input_params(params);
+ // Check that the origin specified by the renderer process is one
+ // that it is allowed to access.
+ if (params.origin_url != "null" &&
+ !ChildProcessSecurityPolicyImpl::GetInstance()->CanRequestURL(
+ render_process_id_, GURL(params.origin_url))) {
+ LOG(ERROR) << "SRDH::OnStartRequest, disallowed origin: "
+ << params.origin_url;
+ return;
+ }
+
int embedder_render_process_id = 0;
int embedder_render_view_id = MSG_ROUTING_NONE;
- if (is_guest_) {
+ RenderViewHostImpl* render_view_host =
+ RenderViewHostImpl::FromID(render_process_id_, params.render_view_id);
+ WebContentsImpl* web_contents = static_cast<WebContentsImpl*>(
+ WebContents::FromRenderViewHost(render_view_host));
+ BrowserPluginGuest* guest = web_contents->GetBrowserPluginGuest();
+ if (guest) {
// If the speech API request was from a guest, save the context of the
// embedder since we will use it to decide permission.
- RenderViewHostImpl* render_view_host =
- RenderViewHostImpl::FromID(render_process_id_, params.render_view_id);
- WebContentsImpl* web_contents = static_cast<WebContentsImpl*>(
- WebContents::FromRenderViewHost(render_view_host));
- BrowserPluginGuest* guest = web_contents->GetBrowserPluginGuest();
-
embedder_render_process_id =
guest->embedder_web_contents()->GetRenderProcessHost()->GetID();
DCHECK_NE(embedder_render_process_id, 0);
@@ -93,8 +103,7 @@ void SpeechRecognitionDispatcherHost::OnStartRequest(
}
// TODO(lazyboy): Check if filter_profanities should use |render_process_id|
- // instead of |render_process_id_|. We are also using the same value in
- // input_tag_dispatcher_host.cc
+ // instead of |render_process_id_|.
bool filter_profanities =
SpeechRecognitionManagerImpl::GetInstance() &&
SpeechRecognitionManagerImpl::GetInstance()->delegate() &&
@@ -126,7 +135,6 @@ void SpeechRecognitionDispatcherHost::OnStartRequestOnIO(
if (embedder_render_process_id)
context.guest_render_view_id = params.render_view_id;
context.request_id = params.request_id;
- context.requested_by_page_element = false;
SpeechRecognitionSessionConfig config;
config.is_legacy_api = false;
@@ -158,6 +166,11 @@ void SpeechRecognitionDispatcherHost::OnAbortRequest(int render_view_id,
SpeechRecognitionManager::GetInstance()->AbortSession(session_id);
}
+void SpeechRecognitionDispatcherHost::OnAbortAllRequests(int render_view_id) {
+ SpeechRecognitionManager::GetInstance()->AbortAllSessionsForRenderView(
+ render_process_id_, render_view_id);
+}
+
void SpeechRecognitionDispatcherHost::OnStopCaptureRequest(
int render_view_id, int request_id) {
int session_id = SpeechRecognitionManager::GetInstance()->GetSession(
diff --git a/chromium/content/browser/speech/speech_recognition_dispatcher_host.h b/chromium/content/browser/speech/speech_recognition_dispatcher_host.h
index e74447139a9..72e7ee147d1 100644
--- a/chromium/content/browser/speech/speech_recognition_dispatcher_host.h
+++ b/chromium/content/browser/speech/speech_recognition_dispatcher_host.h
@@ -28,7 +28,6 @@ class CONTENT_EXPORT SpeechRecognitionDispatcherHost
public SpeechRecognitionEventListener {
public:
SpeechRecognitionDispatcherHost(
- bool is_guest,
int render_process_id,
net::URLRequestContextGetter* context_getter);
@@ -53,8 +52,7 @@ class CONTENT_EXPORT SpeechRecognitionDispatcherHost
float noise_volume) OVERRIDE;
// BrowserMessageFilter implementation.
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
virtual void OverrideThreadForMessage(
const IPC::Message& message,
BrowserThread::ID* thread) OVERRIDE;
@@ -73,8 +71,8 @@ class CONTENT_EXPORT SpeechRecognitionDispatcherHost
bool filter_profanities);
void OnAbortRequest(int render_view_id, int request_id);
void OnStopCaptureRequest(int render_view_id, int request_id);
+ void OnAbortAllRequests(int render_view_id);
- bool is_guest_;
int render_process_id_;
scoped_refptr<net::URLRequestContextGetter> context_getter_;
diff --git a/chromium/content/browser/speech/speech_recognition_manager_impl.cc b/chromium/content/browser/speech/speech_recognition_manager_impl.cc
index c88cc9fca9f..4b0827a6b62 100644
--- a/chromium/content/browser/speech/speech_recognition_manager_impl.cc
+++ b/chromium/content/browser/speech/speech_recognition_manager_impl.cc
@@ -147,7 +147,8 @@ int SpeechRecognitionManagerImpl::CreateSession(
session->recognizer = new SpeechRecognizerImpl(
this,
session_id,
- !config.continuous,
+ config.continuous,
+ config.interim_results,
google_remote_engine);
#else
session->recognizer = new SpeechRecognizerImplAndroid(this, session_id);
@@ -289,8 +290,8 @@ void SpeechRecognitionManagerImpl::StopAudioCaptureForSession(int session_id) {
// Here begins the SpeechRecognitionEventListener interface implementation,
// which will simply relay the events to the proper listener registered for the
-// particular session (most likely InputTagSpeechDispatcherHost) and to the
-// catch-all listener provided by the delegate (if any).
+// particular session and to the catch-all listener provided by the delegate
+// (if any).
void SpeechRecognitionManagerImpl::OnRecognitionStart(int session_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
@@ -300,7 +301,8 @@ void SpeechRecognitionManagerImpl::OnRecognitionStart(int session_id) {
SessionsTable::iterator iter = sessions_.find(session_id);
if (iter->second->ui) {
// Notify the UI that the devices are being used.
- iter->second->ui->OnStarted(base::Closure());
+ iter->second->ui->OnStarted(base::Closure(),
+ MediaStreamUIProxy::WindowIdCallback());
}
DCHECK_EQ(primary_session_id_, session_id);
diff --git a/chromium/content/browser/speech/speech_recognizer_impl.cc b/chromium/content/browser/speech/speech_recognizer_impl.cc
index ecd24ab9d87..e49d30139aa 100644
--- a/chromium/content/browser/speech/speech_recognizer_impl.cc
+++ b/chromium/content/browser/speech/speech_recognizer_impl.cc
@@ -37,10 +37,10 @@ class SpeechRecognizerImpl::OnDataConverter
const AudioParameters& output_params);
virtual ~OnDataConverter();
- // Converts input |data| buffer into an AudioChunk where the input format
+ // Converts input audio |data| bus into an AudioChunk where the input format
// is given by |input_parameters_| and the output format by
// |output_parameters_|.
- scoped_refptr<AudioChunk> Convert(const uint8* data, size_t size);
+ scoped_refptr<AudioChunk> Convert(const AudioBus* data);
private:
// media::AudioConverter::InputCallback implementation.
@@ -132,11 +132,10 @@ SpeechRecognizerImpl::OnDataConverter::~OnDataConverter() {
}
scoped_refptr<AudioChunk> SpeechRecognizerImpl::OnDataConverter::Convert(
- const uint8* data, size_t size) {
- CHECK_EQ(size, static_cast<size_t>(input_parameters_.GetBytesPerBuffer()));
+ const AudioBus* data) {
+ CHECK_EQ(data->frames(), input_parameters_.frames_per_buffer());
- input_bus_->FromInterleaved(
- data, input_bus_->frames(), input_parameters_.bits_per_sample() / 8);
+ data->CopyTo(input_bus_.get());
waiting_for_input_ = true;
audio_converter_.Convert(output_bus_.get());
@@ -173,17 +172,19 @@ double SpeechRecognizerImpl::OnDataConverter::ProvideInput(
SpeechRecognizerImpl::SpeechRecognizerImpl(
SpeechRecognitionEventListener* listener,
int session_id,
- bool is_single_shot,
+ bool continuous,
+ bool provisional_results,
SpeechRecognitionEngine* engine)
: SpeechRecognizer(listener, session_id),
recognition_engine_(engine),
endpointer_(kAudioSampleRate),
is_dispatching_event_(false),
- is_single_shot_(is_single_shot),
+ provisional_results_(provisional_results),
state_(STATE_IDLE) {
DCHECK(recognition_engine_ != NULL);
- if (is_single_shot) {
- // In single shot recognition, the session is automatically ended after:
+ if (!continuous) {
+ // In single shot (non-continous) recognition,
+ // the session is automatically ended after:
// - 0.5 seconds of silence if time < 3 seconds
// - 1 seconds of silence if time >= 3 seconds
endpointer_.set_speech_input_complete_silence_length(
@@ -252,6 +253,7 @@ SpeechRecognizerImpl::recognition_engine() const {
}
SpeechRecognizerImpl::~SpeechRecognizerImpl() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
endpointer_.EndSession();
if (audio_controller_.get()) {
audio_controller_->Close(
@@ -260,7 +262,8 @@ SpeechRecognizerImpl::~SpeechRecognizerImpl() {
}
// Invoked in the audio thread.
-void SpeechRecognizerImpl::OnError(AudioInputController* controller) {
+void SpeechRecognizerImpl::OnError(AudioInputController* controller,
+ media::AudioInputController::ErrorCode error_code) {
FSMEventArgs event_args(EVENT_AUDIO_ERROR);
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&SpeechRecognizerImpl::DispatchEvent,
@@ -268,13 +271,10 @@ void SpeechRecognizerImpl::OnError(AudioInputController* controller) {
}
void SpeechRecognizerImpl::OnData(AudioInputController* controller,
- const uint8* data, uint32 size) {
- if (size == 0) // This could happen when audio capture stops and is normal.
- return;
-
+ const AudioBus* data) {
// Convert audio from native format to fixed format used by WebSpeech.
FSMEventArgs event_args(EVENT_AUDIO_DATA);
- event_args.audio_data = audio_converter_->Convert(data, size);
+ event_args.audio_data = audio_converter_->Convert(data);
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&SpeechRecognizerImpl::DispatchEvent,
@@ -685,10 +685,8 @@ SpeechRecognizerImpl::FSMState SpeechRecognizerImpl::Abort(
SpeechRecognizerImpl::FSMState SpeechRecognizerImpl::ProcessIntermediateResult(
const FSMEventArgs& event_args) {
- // Provisional results can occur only during continuous (non one-shot) mode.
- // If this check is reached it means that a continuous speech recognition
- // engine is being used for a one shot recognition.
- DCHECK_EQ(false, is_single_shot_);
+ // Provisional results can occur only if explicitly enabled in the JS API.
+ DCHECK(provisional_results_);
// In continuous recognition, intermediate results can occur even when we are
// in the ESTIMATING_ENVIRONMENT or WAITING_FOR_SPEECH states (if the
@@ -718,8 +716,8 @@ SpeechRecognizerImpl::ProcessFinalResult(const FSMEventArgs& event_args) {
for (; i != results.end(); ++i) {
const SpeechRecognitionResult& result = *i;
if (result.is_provisional) {
+ DCHECK(provisional_results_);
provisional_results_pending = true;
- DCHECK(!is_single_shot_);
} else if (results_are_empty) {
results_are_empty = result.hypotheses.empty();
}
diff --git a/chromium/content/browser/speech/speech_recognizer_impl.h b/chromium/content/browser/speech/speech_recognizer_impl.h
index c2abb339e65..55e07f04ef6 100644
--- a/chromium/content/browser/speech/speech_recognizer_impl.h
+++ b/chromium/content/browser/speech/speech_recognizer_impl.h
@@ -16,6 +16,7 @@
#include "net/url_request/url_request_context_getter.h"
namespace media {
+class AudioBus;
class AudioManager;
}
@@ -41,7 +42,8 @@ class CONTENT_EXPORT SpeechRecognizerImpl
SpeechRecognizerImpl(SpeechRecognitionEventListener* listener,
int session_id,
- bool is_single_shot,
+ bool continuous,
+ bool provisional_results,
SpeechRecognitionEngine* engine);
virtual void StartRecognition(const std::string& device_id) OVERRIDE;
@@ -128,9 +130,12 @@ class CONTENT_EXPORT SpeechRecognizerImpl
// AudioInputController::EventHandler methods.
virtual void OnCreated(media::AudioInputController* controller) OVERRIDE {}
virtual void OnRecording(media::AudioInputController* controller) OVERRIDE {}
- virtual void OnError(media::AudioInputController* controller) OVERRIDE;
+ virtual void OnError(media::AudioInputController* controller,
+ media::AudioInputController::ErrorCode error_code) OVERRIDE;
virtual void OnData(media::AudioInputController* controller,
- const uint8* data, uint32 size) OVERRIDE;
+ const media::AudioBus* data) OVERRIDE;
+ virtual void OnLog(media::AudioInputController* controller,
+ const std::string& message) OVERRIDE {}
// SpeechRecognitionEngineDelegate methods.
virtual void OnSpeechRecognitionEngineResults(
@@ -146,7 +151,7 @@ class CONTENT_EXPORT SpeechRecognizerImpl
int num_samples_recorded_;
float audio_level_;
bool is_dispatching_event_;
- bool is_single_shot_;
+ bool provisional_results_;
FSMState state_;
std::string device_id_;
diff --git a/chromium/content/browser/speech/speech_recognizer_impl_unittest.cc b/chromium/content/browser/speech/speech_recognizer_impl_unittest.cc
index 54d970906da..13d5eba7dfc 100644
--- a/chromium/content/browser/speech/speech_recognizer_impl_unittest.cc
+++ b/chromium/content/browser/speech/speech_recognizer_impl_unittest.cc
@@ -13,6 +13,7 @@
#include "media/audio/fake_audio_output_stream.h"
#include "media/audio/mock_audio_manager.h"
#include "media/audio/test_audio_input_controller_factory.h"
+#include "media/base/audio_bus.h"
#include "net/base/net_errors.h"
#include "net/url_request/test_url_fetcher_factory.h"
#include "net/url_request/url_request_status.h"
@@ -54,9 +55,8 @@ class SpeechRecognizerImplTest : public SpeechRecognitionEventListener,
sr_engine->SetConfig(config);
const int kTestingSessionId = 1;
- const bool kOneShotMode = true;
recognizer_ = new SpeechRecognizerImpl(
- this, kTestingSessionId, kOneShotMode, sr_engine);
+ this, kTestingSessionId, false, false, sr_engine);
audio_manager_.reset(new media::MockAudioManager(
base::MessageLoop::current()->message_loop_proxy().get()));
recognizer_->SetAudioManagerForTesting(audio_manager_.get());
@@ -67,6 +67,13 @@ class SpeechRecognizerImplTest : public SpeechRecognitionEventListener,
ChannelLayoutToChannelCount(SpeechRecognizerImpl::kChannelLayout) *
SpeechRecognizerImpl::kNumBitsPerAudioSample) / (8 * 1000);
audio_packet_.resize(audio_packet_length_bytes);
+
+ const int channels =
+ ChannelLayoutToChannelCount(SpeechRecognizerImpl::kChannelLayout);
+ bytes_per_sample_ = SpeechRecognizerImpl::kNumBitsPerAudioSample / 8;
+ const int frames = audio_packet_length_bytes / channels / bytes_per_sample_;
+ audio_bus_ = media::AudioBus::Create(channels, frames);
+ audio_bus_->Zero();
}
void CheckEventsConsistency() {
@@ -148,10 +155,17 @@ class SpeechRecognizerImplTest : public SpeechRecognitionEventListener,
AudioInputController::set_factory_for_testing(NULL);
}
+ void CopyPacketToAudioBus() {
+ // Copy the created signal into an audio bus in a deinterleaved format.
+ audio_bus_->FromInterleaved(
+ &audio_packet_[0], audio_bus_->frames(), bytes_per_sample_);
+ }
+
void FillPacketWithTestWaveform() {
// Fill the input with a simple pattern, a 125Hz sawtooth waveform.
for (size_t i = 0; i < audio_packet_.size(); ++i)
audio_packet_[i] = static_cast<uint8>(i);
+ CopyPacketToAudioBus();
}
void FillPacketWithNoise() {
@@ -161,6 +175,7 @@ class SpeechRecognizerImplTest : public SpeechRecognitionEventListener,
value += factor;
audio_packet_[i] = value % 100;
}
+ CopyPacketToAudioBus();
}
protected:
@@ -179,6 +194,8 @@ class SpeechRecognizerImplTest : public SpeechRecognitionEventListener,
net::TestURLFetcherFactory url_fetcher_factory_;
TestAudioInputControllerFactory audio_input_controller_factory_;
std::vector<uint8> audio_packet_;
+ scoped_ptr<media::AudioBus> audio_bus_;
+ int bytes_per_sample_;
float volume_;
float noise_volume_;
};
@@ -223,8 +240,7 @@ TEST_F(SpeechRecognizerImplTest, StopWithData) {
// full recording to complete.
const size_t kNumChunks = 5;
for (size_t i = 0; i < kNumChunks; ++i) {
- controller->event_handler()->OnData(controller, &audio_packet_[0],
- audio_packet_.size());
+ controller->event_handler()->OnData(controller, audio_bus_.get());
base::MessageLoop::current()->RunUntilIdle();
net::TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0);
ASSERT_TRUE(fetcher);
@@ -265,8 +281,7 @@ TEST_F(SpeechRecognizerImplTest, CancelWithData) {
TestAudioInputController* controller =
audio_input_controller_factory_.controller();
ASSERT_TRUE(controller);
- controller->event_handler()->OnData(controller, &audio_packet_[0],
- audio_packet_.size());
+ controller->event_handler()->OnData(controller, audio_bus_.get());
base::MessageLoop::current()->RunUntilIdle();
recognizer_->AbortRecognition();
base::MessageLoop::current()->RunUntilIdle();
@@ -286,8 +301,7 @@ TEST_F(SpeechRecognizerImplTest, ConnectionError) {
TestAudioInputController* controller =
audio_input_controller_factory_.controller();
ASSERT_TRUE(controller);
- controller->event_handler()->OnData(controller, &audio_packet_[0],
- audio_packet_.size());
+ controller->event_handler()->OnData(controller, audio_bus_.get());
base::MessageLoop::current()->RunUntilIdle();
net::TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0);
ASSERT_TRUE(fetcher);
@@ -324,8 +338,7 @@ TEST_F(SpeechRecognizerImplTest, ServerError) {
TestAudioInputController* controller =
audio_input_controller_factory_.controller();
ASSERT_TRUE(controller);
- controller->event_handler()->OnData(controller, &audio_packet_[0],
- audio_packet_.size());
+ controller->event_handler()->OnData(controller, audio_bus_.get());
base::MessageLoop::current()->RunUntilIdle();
net::TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0);
ASSERT_TRUE(fetcher);
@@ -360,7 +373,8 @@ TEST_F(SpeechRecognizerImplTest, AudioControllerErrorNoData) {
TestAudioInputController* controller =
audio_input_controller_factory_.controller();
ASSERT_TRUE(controller);
- controller->event_handler()->OnError(controller);
+ controller->event_handler()->OnError(controller,
+ AudioInputController::UNKNOWN_ERROR);
base::MessageLoop::current()->RunUntilIdle();
EXPECT_TRUE(recognition_started_);
EXPECT_FALSE(audio_started_);
@@ -377,9 +391,9 @@ TEST_F(SpeechRecognizerImplTest, AudioControllerErrorWithData) {
TestAudioInputController* controller =
audio_input_controller_factory_.controller();
ASSERT_TRUE(controller);
- controller->event_handler()->OnData(controller, &audio_packet_[0],
- audio_packet_.size());
- controller->event_handler()->OnError(controller);
+ controller->event_handler()->OnData(controller, audio_bus_.get());
+ controller->event_handler()->OnError(controller,
+ AudioInputController::UNKNOWN_ERROR);
base::MessageLoop::current()->RunUntilIdle();
ASSERT_TRUE(url_fetcher_factory_.GetFetcherByID(0));
EXPECT_TRUE(recognition_started_);
@@ -402,8 +416,7 @@ TEST_F(SpeechRecognizerImplTest, NoSpeechCallbackIssued) {
GoogleOneShotRemoteEngine::kAudioPacketIntervalMs + 1;
// The vector is already filled with zero value samples on create.
for (int i = 0; i < num_packets; ++i) {
- controller->event_handler()->OnData(controller, &audio_packet_[0],
- audio_packet_.size());
+ controller->event_handler()->OnData(controller, audio_bus_.get());
}
base::MessageLoop::current()->RunUntilIdle();
EXPECT_TRUE(recognition_started_);
@@ -431,14 +444,12 @@ TEST_F(SpeechRecognizerImplTest, NoSpeechCallbackNotIssued) {
// The vector is already filled with zero value samples on create.
for (int i = 0; i < num_packets / 2; ++i) {
- controller->event_handler()->OnData(controller, &audio_packet_[0],
- audio_packet_.size());
+ controller->event_handler()->OnData(controller, audio_bus_.get());
}
FillPacketWithTestWaveform();
for (int i = 0; i < num_packets / 2; ++i) {
- controller->event_handler()->OnData(controller, &audio_packet_[0],
- audio_packet_.size());
+ controller->event_handler()->OnData(controller, audio_bus_.get());
}
base::MessageLoop::current()->RunUntilIdle();
@@ -469,21 +480,18 @@ TEST_F(SpeechRecognizerImplTest, SetInputVolumeCallback) {
GoogleOneShotRemoteEngine::kAudioPacketIntervalMs;
FillPacketWithNoise();
for (int i = 0; i < num_packets; ++i) {
- controller->event_handler()->OnData(controller, &audio_packet_[0],
- audio_packet_.size());
+ controller->event_handler()->OnData(controller, audio_bus_.get());
}
base::MessageLoop::current()->RunUntilIdle();
EXPECT_EQ(-1.0f, volume_); // No audio volume set yet.
// The vector is already filled with zero value samples on create.
- controller->event_handler()->OnData(controller, &audio_packet_[0],
- audio_packet_.size());
+ controller->event_handler()->OnData(controller, audio_bus_.get());
base::MessageLoop::current()->RunUntilIdle();
EXPECT_FLOAT_EQ(0.74939233f, volume_);
FillPacketWithTestWaveform();
- controller->event_handler()->OnData(controller, &audio_packet_[0],
- audio_packet_.size());
+ controller->event_handler()->OnData(controller, audio_bus_.get());
base::MessageLoop::current()->RunUntilIdle();
EXPECT_NEAR(0.89926866f, volume_, 0.00001f);
EXPECT_FLOAT_EQ(0.75071919f, noise_volume_);
diff --git a/chromium/content/browser/ssl/ssl_cert_error_handler.cc b/chromium/content/browser/ssl/ssl_cert_error_handler.cc
index b3bdb4100e9..a69e993380a 100644
--- a/chromium/content/browser/ssl/ssl_cert_error_handler.cc
+++ b/chromium/content/browser/ssl/ssl_cert_error_handler.cc
@@ -17,11 +17,11 @@ SSLCertErrorHandler::SSLCertErrorHandler(
ResourceType::Type resource_type,
const GURL& url,
int render_process_id,
- int render_view_id,
+ int render_frame_id,
const net::SSLInfo& ssl_info,
bool fatal)
: SSLErrorHandler(delegate, id, resource_type, url, render_process_id,
- render_view_id),
+ render_frame_id),
ssl_info_(ssl_info),
cert_error_(net::MapCertStatusToNetError(ssl_info.cert_status)),
fatal_(fatal) {
diff --git a/chromium/content/browser/ssl/ssl_cert_error_handler.h b/chromium/content/browser/ssl/ssl_cert_error_handler.h
index 696fb817c8d..6946f42921f 100644
--- a/chromium/content/browser/ssl/ssl_cert_error_handler.h
+++ b/chromium/content/browser/ssl/ssl_cert_error_handler.h
@@ -24,7 +24,7 @@ class SSLCertErrorHandler : public SSLErrorHandler {
ResourceType::Type resource_type,
const GURL& url,
int render_process_id,
- int render_view_id,
+ int render_frame_id,
const net::SSLInfo& ssl_info,
bool fatal);
diff --git a/chromium/content/browser/ssl/ssl_client_auth_handler.cc b/chromium/content/browser/ssl/ssl_client_auth_handler.cc
index 60d57d40c6e..b7fe9f0a7fa 100644
--- a/chromium/content/browser/ssl/ssl_client_auth_handler.cc
+++ b/chromium/content/browser/ssl/ssl_client_auth_handler.cc
@@ -80,10 +80,10 @@ void SSLClientAuthHandler::DidGetClientCerts() {
}
int render_process_host_id;
- int render_view_host_id;
- if (!ResourceRequestInfo::ForRequest(request_)->GetAssociatedRenderView(
+ int render_frame_host_id;
+ if (!ResourceRequestInfo::ForRequest(request_)->GetAssociatedRenderFrame(
&render_process_host_id,
- &render_view_host_id))
+ &render_frame_host_id))
NOTREACHED();
// If the RVH does not exist by the time this task gets run, then the task
@@ -94,7 +94,7 @@ void SSLClientAuthHandler::DidGetClientCerts() {
BrowserThread::UI, FROM_HERE,
base::Bind(
&SSLClientAuthHandler::DoSelectCertificate, this,
- render_process_host_id, render_view_host_id));
+ render_process_host_id, render_frame_host_id));
}
void SSLClientAuthHandler::DoCertificateSelected(net::X509Certificate* cert) {
@@ -113,10 +113,10 @@ void SSLClientAuthHandler::DoCertificateSelected(net::X509Certificate* cert) {
}
void SSLClientAuthHandler::DoSelectCertificate(
- int render_process_host_id, int render_view_host_id) {
+ int render_process_host_id, int render_frame_host_id) {
GetContentClient()->browser()->SelectClientCertificate(
render_process_host_id,
- render_view_host_id,
+ render_frame_host_id,
http_network_session_,
cert_request_info_.get(),
base::Bind(&SSLClientAuthHandler::CertificateSelected, this));
diff --git a/chromium/content/browser/ssl/ssl_client_auth_handler.h b/chromium/content/browser/ssl/ssl_client_auth_handler.h
index bbc77470f66..b848d54d7ff 100644
--- a/chromium/content/browser/ssl/ssl_client_auth_handler.h
+++ b/chromium/content/browser/ssl/ssl_client_auth_handler.h
@@ -66,7 +66,7 @@ class CONTENT_EXPORT SSLClientAuthHandler
// Selects a client certificate on the UI thread.
void DoSelectCertificate(int render_process_host_id,
- int render_view_host_id);
+ int render_frame_host_id);
// The net::URLRequest that triggered this client auth.
net::URLRequest* request_;
diff --git a/chromium/content/browser/ssl/ssl_error_handler.cc b/chromium/content/browser/ssl/ssl_error_handler.cc
index 8a322f7570d..6d120a9e2fb 100644
--- a/chromium/content/browser/ssl/ssl_error_handler.cc
+++ b/chromium/content/browser/ssl/ssl_error_handler.cc
@@ -6,7 +6,7 @@
#include "base/bind.h"
#include "content/browser/frame_host/navigation_controller_impl.h"
-#include "content/browser/renderer_host/render_view_host_impl.h"
+#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/browser/ssl/ssl_cert_error_handler.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/browser_thread.h"
@@ -23,12 +23,12 @@ SSLErrorHandler::SSLErrorHandler(const base::WeakPtr<Delegate>& delegate,
ResourceType::Type resource_type,
const GURL& url,
int render_process_id,
- int render_view_id)
+ int render_frame_id)
: manager_(NULL),
request_id_(id),
delegate_(delegate),
render_process_id_(render_process_id),
- render_view_id_(render_view_id),
+ render_frame_id_(render_frame_id),
request_url_(url),
resource_type_(resource_type),
request_has_been_notified_(false) {
@@ -61,10 +61,9 @@ void SSLErrorHandler::Dispatch() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
WebContents* web_contents = NULL;
- RenderViewHostImpl* render_view_host =
- RenderViewHostImpl::FromID(render_process_id_, render_view_id_);
- if (render_view_host)
- web_contents = render_view_host->GetDelegate()->GetAsWebContents();
+ RenderFrameHost* render_frame_host =
+ RenderFrameHost::FromID(render_process_id_, render_frame_id_);
+ web_contents = WebContents::FromRenderFrameHost(render_frame_host);
if (!web_contents) {
// We arrived on the UI thread, but the tab we're looking for is no longer
diff --git a/chromium/content/browser/ssl/ssl_error_handler.h b/chromium/content/browser/ssl/ssl_error_handler.h
index 5f9e9e59f5a..87bc1da445e 100644
--- a/chromium/content/browser/ssl/ssl_error_handler.h
+++ b/chromium/content/browser/ssl/ssl_error_handler.h
@@ -101,7 +101,7 @@ class SSLErrorHandler : public base::RefCountedThreadSafe<SSLErrorHandler> {
void TakeNoAction();
int render_process_id() const { return render_process_id_; }
- int render_view_id() const { return render_view_id_; }
+ int render_frame_id() const { return render_frame_id_; }
protected:
friend class base::RefCountedThreadSafe<SSLErrorHandler>;
@@ -112,7 +112,7 @@ class SSLErrorHandler : public base::RefCountedThreadSafe<SSLErrorHandler> {
ResourceType::Type resource_type,
const GURL& url,
int render_process_id,
- int render_view_id);
+ int render_frame_id);
virtual ~SSLErrorHandler();
@@ -149,7 +149,7 @@ class SSLErrorHandler : public base::RefCountedThreadSafe<SSLErrorHandler> {
// We use these members to find the correct SSLManager when we arrive on
// the UI thread.
int render_process_id_;
- int render_view_id_;
+ int render_frame_id_;
// The URL that we requested.
// This read-only member can be accessed on any thread.
diff --git a/chromium/content/browser/ssl/ssl_manager.cc b/chromium/content/browser/ssl/ssl_manager.cc
index b1eb51e3207..68906ced1ec 100644
--- a/chromium/content/browser/ssl/ssl_manager.cc
+++ b/chromium/content/browser/ssl/ssl_manager.cc
@@ -53,7 +53,7 @@ void SSLManager::OnSSLCertificateError(
const ResourceType::Type resource_type,
const GURL& url,
int render_process_id,
- int render_view_id,
+ int render_frame_id,
const net::SSLInfo& ssl_info,
bool fatal) {
DCHECK(delegate.get());
@@ -61,7 +61,7 @@ void SSLManager::OnSSLCertificateError(
<< net::MapCertStatusToNetError(ssl_info.cert_status) << " id: "
<< id.child_id << "," << id.request_id << " resource_type: "
<< resource_type << " url: " << url.spec() << " render_process_id: "
- << render_process_id << " render_view_id: " << render_view_id
+ << render_process_id << " render_frame_id: " << render_frame_id
<< " cert_status: " << std::hex << ssl_info.cert_status;
// A certificate error occurred. Construct a SSLCertErrorHandler object and
@@ -74,7 +74,7 @@ void SSLManager::OnSSLCertificateError(
resource_type,
url,
render_process_id,
- render_view_id,
+ render_frame_id,
ssl_info,
fatal)));
}
diff --git a/chromium/content/browser/ssl/ssl_manager.h b/chromium/content/browser/ssl/ssl_manager.h
index 45100a7ea62..c195e981bcb 100644
--- a/chromium/content/browser/ssl/ssl_manager.h
+++ b/chromium/content/browser/ssl/ssl_manager.h
@@ -54,7 +54,7 @@ class SSLManager {
ResourceType::Type resource_type,
const GURL& url,
int render_process_id,
- int render_view_id,
+ int render_frame_id,
const net::SSLInfo& ssl_info,
bool fatal);
diff --git a/chromium/content/browser/ssl/ssl_policy.cc b/chromium/content/browser/ssl/ssl_policy.cc
index ddf9e4cd3a5..731645505b4 100644
--- a/chromium/content/browser/ssl/ssl_policy.cc
+++ b/chromium/content/browser/ssl/ssl_policy.cc
@@ -110,6 +110,9 @@ void SSLPolicy::UpdateEntry(NavigationEntryImpl* entry,
if (!entry->GetURL().SchemeIsSecure())
return;
+ if (!web_contents->DisplayedInsecureContent())
+ entry->GetSSL().content_status &= ~SSLStatus::DISPLAYED_INSECURE_CONTENT;
+
// An HTTPS response may not have a certificate for some reason. When that
// happens, use the unauthenticated (HTTP) rather than the authentication
// broken security style so that we can detect this error condition.
@@ -118,6 +121,9 @@ void SSLPolicy::UpdateEntry(NavigationEntryImpl* entry,
return;
}
+ if (web_contents->DisplayedInsecureContent())
+ entry->GetSSL().content_status |= SSLStatus::DISPLAYED_INSECURE_CONTENT;
+
if (net::IsCertStatusError(entry->GetSSL().cert_status)) {
// Minor errors don't lower the security style to
// SECURITY_STYLE_AUTHENTICATION_BROKEN.
@@ -140,11 +146,6 @@ void SSLPolicy::UpdateEntry(NavigationEntryImpl* entry,
entry->GetSSL().content_status |= SSLStatus::RAN_INSECURE_CONTENT;
return;
}
-
- if (web_contents->DisplayedInsecureContent())
- entry->GetSSL().content_status |= SSLStatus::DISPLAYED_INSECURE_CONTENT;
- else
- entry->GetSSL().content_status &= ~SSLStatus::DISPLAYED_INSECURE_CONTENT;
}
void SSLPolicy::OnAllowCertificate(scoped_refptr<SSLCertErrorHandler> handler,
@@ -187,7 +188,7 @@ void SSLPolicy::OnCertErrorInternal(SSLCertErrorHandler* handler,
CERTIFICATE_REQUEST_RESULT_TYPE_CONTINUE;
GetContentClient()->browser()->AllowCertificateError(
handler->render_process_id(),
- handler->render_view_id(),
+ handler->render_frame_id(),
handler->cert_error(),
handler->ssl_info(),
handler->request_url(),
diff --git a/chromium/content/browser/storage_partition_impl.cc b/chromium/content/browser/storage_partition_impl.cc
index 83e9e96df43..9a876b5cff8 100644
--- a/chromium/content/browser/storage_partition_impl.cc
+++ b/chromium/content/browser/storage_partition_impl.cc
@@ -44,21 +44,21 @@ void ClearCookiesOnIOThread(
const scoped_refptr<net::URLRequestContextGetter>& rq_context,
const base::Time begin,
const base::Time end,
- const GURL& remove_origin,
+ const GURL& storage_origin,
const base::Closure& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
net::CookieStore* cookie_store = rq_context->
GetURLRequestContext()->cookie_store();
- if (remove_origin.is_empty()) {
- cookie_store->GetCookieMonster()->DeleteAllCreatedBetweenAsync(
+ if (storage_origin.is_empty()) {
+ cookie_store->DeleteAllCreatedBetweenAsync(
begin,
end,
base::Bind(&OnClearedCookies, callback));
} else {
- cookie_store->GetCookieMonster()->DeleteAllCreatedBetweenForHostAsync(
+ cookie_store->DeleteAllCreatedBetweenForHostAsync(
begin,
end,
- remove_origin, base::Bind(&OnClearedCookies, callback));
+ storage_origin, base::Bind(&OnClearedCookies, callback));
}
}
@@ -153,18 +153,18 @@ void ClearLocalStorageOnUIThread(
const scoped_refptr<DOMStorageContextWrapper>& dom_storage_context,
const scoped_refptr<quota::SpecialStoragePolicy>& special_storage_policy,
const StoragePartition::OriginMatcherFunction& origin_matcher,
- const GURL& remove_origin,
+ const GURL& storage_origin,
const base::Time begin,
const base::Time end,
const base::Closure& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- if (!remove_origin.is_empty()) {
+ if (!storage_origin.is_empty()) {
bool can_delete = origin_matcher.is_null() ||
- origin_matcher.Run(remove_origin,
+ origin_matcher.Run(storage_origin,
special_storage_policy.get());
if (can_delete)
- dom_storage_context->DeleteLocalStorage(remove_origin);
+ dom_storage_context->DeleteLocalStorage(storage_origin);
callback.Run();
return;
@@ -213,11 +213,11 @@ int StoragePartitionImpl::GenerateQuotaClientMask(uint32 remove_mask) {
struct StoragePartitionImpl::QuotaManagedDataDeletionHelper {
QuotaManagedDataDeletionHelper(uint32 remove_mask,
uint32 quota_storage_remove_mask,
- const GURL& remove_origin,
+ const GURL& storage_origin,
const base::Closure& callback)
: remove_mask(remove_mask),
quota_storage_remove_mask(quota_storage_remove_mask),
- remove_origin(remove_origin),
+ storage_origin(storage_origin),
callback(callback),
task_count(0) {
}
@@ -242,7 +242,7 @@ struct StoragePartitionImpl::QuotaManagedDataDeletionHelper {
// All of these data are accessed on IO thread.
uint32 remove_mask;
uint32 quota_storage_remove_mask;
- GURL remove_origin;
+ GURL storage_origin;
const base::Closure callback;
int task_count;
};
@@ -270,7 +270,7 @@ struct StoragePartitionImpl::DataDeletionHelper {
void IncrementTaskCountOnUI();
void DecrementTaskCountOnUI();
- void ClearDataOnUIThread(const GURL* remove_origin,
+ void ClearDataOnUIThread(const GURL& storage_origin,
const OriginMatcherFunction& origin_matcher,
const base::FilePath& path,
net::URLRequestContextGetter* rq_context,
@@ -284,7 +284,7 @@ struct StoragePartitionImpl::DataDeletionHelper {
void ClearQuotaManagedDataOnIOThread(
const scoped_refptr<quota::QuotaManager>& quota_manager,
const base::Time begin,
- const GURL& remove_origin,
+ const GURL& storage_origin,
const scoped_refptr<quota::SpecialStoragePolicy>& special_storage_policy,
const StoragePartition::OriginMatcherFunction& origin_matcher,
const base::Closure& callback);
@@ -301,7 +301,7 @@ struct StoragePartitionImpl::DataDeletionHelper {
void StoragePartitionImpl::DataDeletionHelper::ClearQuotaManagedDataOnIOThread(
const scoped_refptr<quota::QuotaManager>& quota_manager,
const base::Time begin,
- const GURL& remove_origin,
+ const GURL& storage_origin,
const scoped_refptr<quota::SpecialStoragePolicy>& special_storage_policy,
const StoragePartition::OriginMatcherFunction& origin_matcher,
const base::Closure& callback) {
@@ -311,7 +311,7 @@ void StoragePartitionImpl::DataDeletionHelper::ClearQuotaManagedDataOnIOThread(
new StoragePartitionImpl::QuotaManagedDataDeletionHelper(
remove_mask,
quota_storage_remove_mask,
- remove_origin,
+ storage_origin,
callback);
helper->ClearDataOnIOThread(quota_manager, begin, special_storage_policy,
origin_matcher);
@@ -416,7 +416,7 @@ StoragePartitionImpl* StoragePartitionImpl::Create(
idb_task_runner);
scoped_refptr<ServiceWorkerContextWrapper> service_worker_context =
- new ServiceWorkerContextWrapper();
+ new ServiceWorkerContextWrapper(context);
service_worker_context->Init(path, quota_manager->proxy());
scoped_refptr<ChromeAppCacheService> appcache_service =
@@ -484,7 +484,7 @@ ServiceWorkerContextWrapper* StoragePartitionImpl::GetServiceWorkerContext() {
void StoragePartitionImpl::ClearDataImpl(
uint32 remove_mask,
uint32 quota_storage_remove_mask,
- const GURL* remove_origin,
+ const GURL& storage_origin,
const OriginMatcherFunction& origin_matcher,
net::URLRequestContextGetter* rq_context,
const base::Time begin,
@@ -496,7 +496,7 @@ void StoragePartitionImpl::ClearDataImpl(
callback);
// |helper| deletes itself when done in
// DataDeletionHelper::DecrementTaskCountOnUI().
- helper->ClearDataOnUIThread(remove_origin, origin_matcher, GetPath(),
+ helper->ClearDataOnUIThread(storage_origin, origin_matcher, GetPath(),
rq_context, dom_storage_context_, quota_manager_,
special_storage_policy_.get(),
webrtc_identity_store_, begin, end);
@@ -597,7 +597,7 @@ void StoragePartitionImpl::
for (std::set<GURL>::const_iterator origin = origins.begin();
origin != origins.end(); ++origin) {
// TODO(mkwst): Clean this up, it's slow. http://crbug.com/130746
- if (!remove_origin.is_empty() && origin->GetOrigin() != remove_origin)
+ if (!storage_origin.is_empty() && origin->GetOrigin() != storage_origin)
continue;
if (!origin_matcher.is_null() &&
@@ -640,7 +640,7 @@ void StoragePartitionImpl::DataDeletionHelper::DecrementTaskCountOnUI() {
}
void StoragePartitionImpl::DataDeletionHelper::ClearDataOnUIThread(
- const GURL* remove_origin,
+ const GURL& storage_origin,
const OriginMatcherFunction& origin_matcher,
const base::FilePath& path,
net::URLRequestContextGetter* rq_context,
@@ -657,14 +657,13 @@ void StoragePartitionImpl::DataDeletionHelper::ClearDataOnUIThread(
base::Closure decrement_callback = base::Bind(
&DataDeletionHelper::DecrementTaskCountOnUI, base::Unretained(this));
- GURL origin = remove_origin ? *remove_origin : GURL();
if (remove_mask & REMOVE_DATA_MASK_COOKIES) {
// Handle the cookies.
IncrementTaskCountOnUI();
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&ClearCookiesOnIOThread,
- make_scoped_refptr(rq_context), begin, end, origin,
+ make_scoped_refptr(rq_context), begin, end, storage_origin,
decrement_callback));
}
@@ -679,7 +678,7 @@ void StoragePartitionImpl::DataDeletionHelper::ClearDataOnUIThread(
base::Unretained(this),
make_scoped_refptr(quota_manager),
begin,
- origin,
+ storage_origin,
make_scoped_refptr(special_storage_policy),
origin_matcher,
decrement_callback));
@@ -691,13 +690,13 @@ void StoragePartitionImpl::DataDeletionHelper::ClearDataOnUIThread(
make_scoped_refptr(dom_storage_context),
make_scoped_refptr(special_storage_policy),
origin_matcher,
- origin, begin, end,
+ storage_origin, begin, end,
decrement_callback);
// ClearDataImpl cannot clear session storage data when a particular origin
// is specified. Therefore we ignore clearing session storage in this case.
// TODO(lazyboy): Fix.
- if (origin.is_empty()) {
+ if (storage_origin.is_empty()) {
IncrementTaskCountOnUI();
ClearSessionStorageOnUIThread(
make_scoped_refptr(dom_storage_context),
@@ -737,7 +736,7 @@ void StoragePartitionImpl::ClearDataForOrigin(
const GURL& storage_origin,
net::URLRequestContextGetter* request_context_getter) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- ClearDataImpl(remove_mask, quota_storage_remove_mask, &storage_origin,
+ ClearDataImpl(remove_mask, quota_storage_remove_mask, storage_origin,
OriginMatcherFunction(), request_context_getter,
base::Time(), base::Time::Max(), base::Bind(&base::DoNothing));
}
@@ -745,7 +744,7 @@ void StoragePartitionImpl::ClearDataForOrigin(
void StoragePartitionImpl::ClearData(
uint32 remove_mask,
uint32 quota_storage_remove_mask,
- const GURL* storage_origin,
+ const GURL& storage_origin,
const OriginMatcherFunction& origin_matcher,
const base::Time begin,
const base::Time end,
diff --git a/chromium/content/browser/storage_partition_impl.h b/chromium/content/browser/storage_partition_impl.h
index 4edf47472e8..717038e7afc 100644
--- a/chromium/content/browser/storage_partition_impl.h
+++ b/chromium/content/browser/storage_partition_impl.h
@@ -42,6 +42,7 @@ class StoragePartitionImpl : public StoragePartition {
virtual webkit_database::DatabaseTracker* GetDatabaseTracker() OVERRIDE;
virtual DOMStorageContextWrapper* GetDOMStorageContext() OVERRIDE;
virtual IndexedDBContextImpl* GetIndexedDBContext() OVERRIDE;
+ virtual ServiceWorkerContextWrapper* GetServiceWorkerContext() OVERRIDE;
virtual void ClearDataForOrigin(
uint32 remove_mask,
@@ -50,14 +51,12 @@ class StoragePartitionImpl : public StoragePartition {
net::URLRequestContextGetter* request_context_getter) OVERRIDE;
virtual void ClearData(uint32 remove_mask,
uint32 quota_storage_remove_mask,
- const GURL* storage_origin,
+ const GURL& storage_origin,
const OriginMatcherFunction& origin_matcher,
const base::Time begin,
const base::Time end,
const base::Closure& callback) OVERRIDE;
- ServiceWorkerContextWrapper* GetServiceWorkerContext();
-
WebRTCIdentityStore* GetWebRTCIdentityStore();
struct DataDeletionHelper;
@@ -121,7 +120,7 @@ class StoragePartitionImpl : public StoragePartition {
void ClearDataImpl(uint32 remove_mask,
uint32 quota_storage_remove_mask,
- const GURL* remove_origin,
+ const GURL& remove_origin,
const OriginMatcherFunction& origin_matcher,
net::URLRequestContextGetter* rq_context,
const base::Time begin,
diff --git a/chromium/content/browser/storage_partition_impl_map.cc b/chromium/content/browser/storage_partition_impl_map.cc
index 85934f117b3..fdfb34e3ebd 100644
--- a/chromium/content/browser/storage_partition_impl_map.cc
+++ b/chromium/content/browser/storage_partition_impl_map.cc
@@ -19,6 +19,7 @@
#include "content/browser/fileapi/chrome_blob_storage_context.h"
#include "content/browser/loader/resource_request_info_impl.h"
#include "content/browser/resource_context_impl.h"
+#include "content/browser/service_worker/service_worker_request_handler.h"
#include "content/browser/storage_partition_impl.h"
#include "content/browser/streams/stream.h"
#include "content/browser/streams/stream_context.h"
@@ -39,7 +40,7 @@
#include "webkit/browser/fileapi/file_system_url_request_job_factory.h"
#include "webkit/common/blob/blob_data.h"
-using appcache::AppCacheService;
+using appcache::AppCacheServiceImpl;
using fileapi::FileSystemContext;
using webkit_blob::BlobStorageContext;
@@ -158,7 +159,7 @@ const int kAllFileTypes = base::FileEnumerator::FILES |
base::FilePath GetStoragePartitionDomainPath(
const std::string& partition_domain) {
- CHECK(IsStringUTF8(partition_domain));
+ CHECK(base::IsStringUTF8(partition_domain));
return base::FilePath(kStoragePartitionDirname).Append(kExtensionsDirname)
.Append(base::FilePath::FromUTF8Unsafe(partition_domain));
@@ -265,6 +266,27 @@ void BlockingObliteratePath(
}
}
+// Ensures each path in |active_paths| is a direct child of storage_root.
+void NormalizeActivePaths(const base::FilePath& storage_root,
+ base::hash_set<base::FilePath>* active_paths) {
+ base::hash_set<base::FilePath> normalized_active_paths;
+
+ for (base::hash_set<base::FilePath>::iterator iter = active_paths->begin();
+ iter != active_paths->end(); ++iter) {
+ base::FilePath relative_path;
+ if (!storage_root.AppendRelativePath(*iter, &relative_path))
+ continue;
+
+ std::vector<base::FilePath::StringType> components;
+ relative_path.GetComponents(&components);
+
+ DCHECK(!relative_path.empty());
+ normalized_active_paths.insert(storage_root.Append(components.front()));
+ }
+
+ active_paths->swap(normalized_active_paths);
+}
+
// Deletes all entries inside the |storage_root| that are not in the
// |active_paths|. Deletion is done in 2 steps:
//
@@ -288,6 +310,8 @@ void BlockingGarbageCollect(
scoped_ptr<base::hash_set<base::FilePath> > active_paths) {
CHECK(storage_root.IsAbsolute());
+ NormalizeActivePaths(storage_root, active_paths.get());
+
base::FileEnumerator enumerator(storage_root, false, kAllFileTypes);
base::FilePath trash_directory;
if (!base::CreateTemporaryDirInDir(storage_root, kTrashDirname,
@@ -376,15 +400,16 @@ StoragePartitionImpl* StoragePartitionImplMap::Get(
ChromeBlobStorageContext::GetFor(browser_context_);
StreamContext* stream_context = StreamContext::GetFor(browser_context_);
ProtocolHandlerMap protocol_handlers;
- protocol_handlers[chrome::kBlobScheme] =
+ protocol_handlers[url::kBlobScheme] =
linked_ptr<net::URLRequestJobFactory::ProtocolHandler>(
new BlobProtocolHandler(blob_storage_context,
stream_context,
partition->GetFileSystemContext()));
- protocol_handlers[chrome::kFileSystemScheme] =
+ protocol_handlers[url::kFileSystemScheme] =
linked_ptr<net::URLRequestJobFactory::ProtocolHandler>(
- CreateFileSystemProtocolHandler(partition->GetFileSystemContext()));
- protocol_handlers[chrome::kChromeUIScheme] =
+ CreateFileSystemProtocolHandler(partition_domain,
+ partition->GetFileSystemContext()));
+ protocol_handlers[kChromeUIScheme] =
linked_ptr<net::URLRequestJobFactory::ProtocolHandler>(
URLDataManagerBackend::CreateProtocolHandler(
browser_context_->GetResourceContext(),
@@ -406,22 +431,30 @@ StoragePartitionImpl* StoragePartitionImplMap::Get(
partition->GetAppCacheService(),
blob_storage_context));
}
- protocol_handlers[chrome::kChromeDevToolsScheme] =
+ protocol_handlers[kChromeDevToolsScheme] =
linked_ptr<net::URLRequestJobFactory::ProtocolHandler>(
CreateDevToolsProtocolHandler(browser_context_->GetResourceContext(),
browser_context_->IsOffTheRecord()));
+ URLRequestInterceptorScopedVector request_interceptors;
+ request_interceptors.push_back(
+ ServiceWorkerRequestHandler::CreateInterceptor().release());
+
// These calls must happen after StoragePartitionImpl::Create().
if (partition_domain.empty()) {
partition->SetURLRequestContext(
GetContentClient()->browser()->CreateRequestContext(
browser_context_,
- &protocol_handlers));
+ &protocol_handlers,
+ request_interceptors.Pass()));
} else {
partition->SetURLRequestContext(
GetContentClient()->browser()->CreateRequestContextForStoragePartition(
- browser_context_, partition->GetPath(), in_memory,
- &protocol_handlers));
+ browser_context_,
+ partition->GetPath(),
+ in_memory,
+ &protocol_handlers,
+ request_interceptors.Pass()));
}
partition->SetMediaURLRequestContext(
partition_domain.empty() ?
@@ -465,7 +498,7 @@ void StoragePartitionImplMap::AsyncObliterate(
StoragePartition::REMOVE_DATA_MASK_ALL &
(~StoragePartition::REMOVE_DATA_MASK_SHADER_CACHE),
StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL,
- NULL,
+ GURL(),
StoragePartition::OriginMatcherFunction(),
base::Time(), base::Time::Max(),
base::Bind(&base::DoNothing));
diff --git a/chromium/content/browser/storage_partition_impl_map.h b/chromium/content/browser/storage_partition_impl_map.h
index 972335a33aa..c15fefc3d8c 100644
--- a/chromium/content/browser/storage_partition_impl_map.h
+++ b/chromium/content/browser/storage_partition_impl_map.h
@@ -25,7 +25,8 @@ namespace content {
class BrowserContext;
// A std::string to StoragePartition map for use with SupportsUserData APIs.
-class StoragePartitionImplMap : public base::SupportsUserData::Data {
+class CONTENT_EXPORT StoragePartitionImplMap
+ : public base::SupportsUserData::Data {
public:
explicit StoragePartitionImplMap(BrowserContext* browser_context);
@@ -57,6 +58,7 @@ class StoragePartitionImplMap : public base::SupportsUserData::Data {
private:
FRIEND_TEST_ALL_PREFIXES(StoragePartitionConfigTest, OperatorLess);
+ FRIEND_TEST_ALL_PREFIXES(StoragePartitionImplMapTest, GarbageCollect);
// Each StoragePartition is uniquely identified by which partition domain
// it belongs to (such as an app or the browser itself), the user supplied
diff --git a/chromium/content/browser/storage_partition_impl_map_unittest.cc b/chromium/content/browser/storage_partition_impl_map_unittest.cc
index 04127cce7cb..ae7fa6d43d2 100644
--- a/chromium/content/browser/storage_partition_impl_map_unittest.cc
+++ b/chromium/content/browser/storage_partition_impl_map_unittest.cc
@@ -3,16 +3,17 @@
// found in the LICENSE file.
#include "content/browser/storage_partition_impl_map.h"
+
+#include "base/file_util.h"
+#include "base/run_loop.h"
+#include "content/public/test/test_browser_context.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
-class StoragePartitionConfigTest : public testing::Test {
-};
-
// Test that the Less comparison function is implemented properly to uniquely
// identify storage partitions used as keys in a std::map.
-TEST_F(StoragePartitionConfigTest, OperatorLess) {
+TEST(StoragePartitionConfigTest, OperatorLess) {
StoragePartitionImplMap::StoragePartitionConfig c1(
std::string(), std::string(), false);
StoragePartitionImplMap::StoragePartitionConfig c2(
@@ -60,4 +61,32 @@ TEST_F(StoragePartitionConfigTest, OperatorLess) {
EXPECT_TRUE(!less(c1, c2) && !less(c2, c1));
}
+TEST(StoragePartitionImplMapTest, GarbageCollect) {
+ base::MessageLoop message_loop;
+ TestBrowserContext browser_context;
+ StoragePartitionImplMap storage_partition_impl_map(&browser_context);
+
+ scoped_ptr<base::hash_set<base::FilePath> > active_paths(
+ new base::hash_set<base::FilePath>);
+
+ base::FilePath active_path = browser_context.GetPath().Append(
+ StoragePartitionImplMap::GetStoragePartitionPath(
+ "active", std::string()));
+ ASSERT_TRUE(base::CreateDirectory(active_path));
+ active_paths->insert(active_path);
+
+ base::FilePath inactive_path = browser_context.GetPath().Append(
+ StoragePartitionImplMap::GetStoragePartitionPath(
+ "inactive", std::string()));
+ ASSERT_TRUE(base::CreateDirectory(inactive_path));
+
+ base::RunLoop run_loop;
+ storage_partition_impl_map.GarbageCollect(
+ active_paths.Pass(), run_loop.QuitClosure());
+ run_loop.Run();
+
+ EXPECT_TRUE(base::PathExists(active_path));
+ EXPECT_FALSE(base::PathExists(inactive_path));
+}
+
} // namespace content
diff --git a/chromium/content/browser/storage_partition_impl_unittest.cc b/chromium/content/browser/storage_partition_impl_unittest.cc
index ef3b67912ac..5fbdfbbab88 100644
--- a/chromium/content/browser/storage_partition_impl_unittest.cc
+++ b/chromium/content/browser/storage_partition_impl_unittest.cc
@@ -3,15 +3,16 @@
// found in the LICENSE file.
#include "base/file_util.h"
-#include "base/files/scoped_temp_dir.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/run_loop.h"
#include "base/threading/thread.h"
#include "content/browser/browser_thread_impl.h"
#include "content/browser/gpu/shader_disk_cache.h"
+#include "content/browser/quota/mock_quota_manager.h"
#include "content/browser/storage_partition_impl.h"
#include "content/public/browser/local_storage_usage_info.h"
#include "content/public/browser/storage_partition.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.h"
#include "content/public/test/test_browser_thread_bundle.h"
@@ -20,8 +21,6 @@
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "webkit/browser/quota/mock_quota_manager.h"
-#include "webkit/browser/quota/mock_special_storage_policy.h"
#include "webkit/browser/quota/quota_manager.h"
namespace content {
@@ -61,31 +60,6 @@ const uint32 kAllQuotaRemoveMask =
StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS |
StoragePartition::REMOVE_DATA_MASK_APPCACHE;
-class TestClosureCallback {
- public:
- TestClosureCallback()
- : callback_(base::Bind(
- &TestClosureCallback::StopWaiting, base::Unretained(this))) {
- }
-
- void WaitForResult() {
- wait_run_loop_.reset(new base::RunLoop());
- wait_run_loop_->Run();
- }
-
- const base::Closure& callback() { return callback_; }
-
- private:
- void StopWaiting() {
- wait_run_loop_->Quit();
- }
-
- base::Closure callback_;
- scoped_ptr<base::RunLoop> wait_run_loop_;
-
- DISALLOW_COPY_AND_ASSIGN(TestClosureCallback);
-};
-
class AwaitCompletionHelper {
public:
AwaitCompletionHelper() : start_(false), already_quit_(false) {}
@@ -205,9 +179,9 @@ class RemoveLocalStorageTester {
base::CreateDirectory(storage_path);
// Write some files.
- file_util::WriteFile(storage_path.Append(kDomStorageOrigin1), NULL, 0);
- file_util::WriteFile(storage_path.Append(kDomStorageOrigin2), NULL, 0);
- file_util::WriteFile(storage_path.Append(kDomStorageOrigin3), NULL, 0);
+ base::WriteFile(storage_path.Append(kDomStorageOrigin1), NULL, 0);
+ base::WriteFile(storage_path.Append(kDomStorageOrigin2), NULL, 0);
+ base::WriteFile(storage_path.Append(kDomStorageOrigin3), NULL, 0);
// Tweak their dates.
base::Time now = base::Time::Now();
@@ -270,70 +244,82 @@ bool DoesOriginMatchUnprotected(
return origin.GetOrigin().scheme() != kOriginDevTools.scheme();
}
-void ClearQuotaData(content::StoragePartition* storage_partition,
- const base::Closure& cb) {
- storage_partition->ClearData(
+void ClearQuotaData(content::StoragePartition* partition,
+ base::RunLoop* loop_to_quit) {
+ partition->ClearData(
kAllQuotaRemoveMask,
StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL,
- NULL, StoragePartition::OriginMatcherFunction(),
- base::Time(), base::Time::Max(), cb);
+ GURL(), StoragePartition::OriginMatcherFunction(),
+ base::Time(), base::Time::Max(), loop_to_quit->QuitClosure());
}
void ClearQuotaDataWithOriginMatcher(
- content::StoragePartition* storage_partition,
+ content::StoragePartition* partition,
const GURL& remove_origin,
const StoragePartition::OriginMatcherFunction& origin_matcher,
const base::Time delete_begin,
- const base::Closure& cb) {
- storage_partition->ClearData(kAllQuotaRemoveMask,
- StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL,
- &remove_origin, origin_matcher, delete_begin,
- base::Time::Max(), cb);
+ base::RunLoop* loop_to_quit) {
+ partition->ClearData(kAllQuotaRemoveMask,
+ StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL,
+ remove_origin, origin_matcher, delete_begin,
+ base::Time::Max(), loop_to_quit->QuitClosure());
}
void ClearQuotaDataForOrigin(
- content::StoragePartition* storage_partition,
+ content::StoragePartition* partition,
const GURL& remove_origin,
const base::Time delete_begin,
- const base::Closure& cb) {
+ base::RunLoop* loop_to_quit) {
ClearQuotaDataWithOriginMatcher(
- storage_partition, remove_origin,
- StoragePartition::OriginMatcherFunction(), delete_begin, cb);
+ partition, remove_origin,
+ StoragePartition::OriginMatcherFunction(), delete_begin,
+ loop_to_quit);
}
void ClearQuotaDataForNonPersistent(
- content::StoragePartition* storage_partition,
+ content::StoragePartition* partition,
const base::Time delete_begin,
- const base::Closure& cb) {
+ base::RunLoop* loop_to_quit) {
uint32 quota_storage_remove_mask_no_persistent =
StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL &
~StoragePartition::QUOTA_MANAGED_STORAGE_MASK_PERSISTENT;
- storage_partition->ClearData(
+ partition->ClearData(
kAllQuotaRemoveMask, quota_storage_remove_mask_no_persistent,
- NULL, StoragePartition::OriginMatcherFunction(),
- delete_begin, base::Time::Max(), cb);
+ GURL(), StoragePartition::OriginMatcherFunction(),
+ delete_begin, base::Time::Max(), loop_to_quit->QuitClosure());
}
-void ClearCookies(content::StoragePartition* storage_partition,
+void ClearCookies(content::StoragePartition* partition,
const base::Time delete_begin,
const base::Time delete_end,
- const base::Closure& cb) {
- storage_partition->ClearData(
+ base::RunLoop* run_loop) {
+ partition->ClearData(
StoragePartition::REMOVE_DATA_MASK_COOKIES,
StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL,
- NULL, StoragePartition::OriginMatcherFunction(),
- delete_begin, delete_end, cb);
+ GURL(), StoragePartition::OriginMatcherFunction(),
+ delete_begin, delete_end, run_loop->QuitClosure());
}
void ClearStuff(uint32 remove_mask,
- content::StoragePartition* storage_partition,
+ content::StoragePartition* partition,
const base::Time delete_begin,
const base::Time delete_end,
const StoragePartition::OriginMatcherFunction& origin_matcher,
- const base::Closure& cb) {
- storage_partition->ClearData(
+ base::RunLoop* run_loop) {
+ partition->ClearData(
remove_mask, StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL,
- NULL, origin_matcher, delete_begin, delete_end, cb);
+ GURL(), origin_matcher, delete_begin, delete_end,
+ run_loop->QuitClosure());
+}
+
+void ClearData(content::StoragePartition* partition,
+ base::RunLoop* run_loop) {
+ base::Time time;
+ partition->ClearData(
+ StoragePartition::REMOVE_DATA_MASK_SHADER_CACHE,
+ StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL,
+ GURL(), StoragePartition::OriginMatcherFunction(),
+ time, time, run_loop->QuitClosure());
}
} // namespace
@@ -341,14 +327,13 @@ void ClearStuff(uint32 remove_mask,
class StoragePartitionImplTest : public testing::Test {
public:
StoragePartitionImplTest()
- : browser_context_(new TestBrowserContext()),
- thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {
+ : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
+ browser_context_(new TestBrowserContext()) {
}
- virtual ~StoragePartitionImplTest() {}
- quota::MockQuotaManager* GetMockManager() {
+ MockQuotaManager* GetMockManager() {
if (!quota_manager_.get()) {
- quota_manager_ = new quota::MockQuotaManager(
+ quota_manager_ = new MockQuotaManager(
browser_context_->IsOffTheRecord(),
browser_context_->GetPath(),
BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO).get(),
@@ -358,14 +343,14 @@ class StoragePartitionImplTest : public testing::Test {
return quota_manager_.get();
}
- TestBrowserContext* GetBrowserContext() {
+ TestBrowserContext* browser_context() {
return browser_context_.get();
}
private:
- scoped_ptr<TestBrowserContext> browser_context_;
- scoped_refptr<quota::MockQuotaManager> quota_manager_;
content::TestBrowserThreadBundle thread_bundle_;
+ scoped_ptr<TestBrowserContext> browser_context_;
+ scoped_refptr<MockQuotaManager> quota_manager_;
DISALLOW_COPY_AND_ASSIGN(StoragePartitionImplTest);
};
@@ -373,20 +358,18 @@ class StoragePartitionImplTest : public testing::Test {
class StoragePartitionShaderClearTest : public testing::Test {
public:
StoragePartitionShaderClearTest()
- : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {
+ : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
+ browser_context_(new TestBrowserContext()) {
+ ShaderCacheFactory::GetInstance()->SetCacheInfo(
+ kDefaultClientId,
+ BrowserContext::GetDefaultStoragePartition(
+ browser_context())->GetPath());
+ cache_ = ShaderCacheFactory::GetInstance()->Get(kDefaultClientId);
}
- virtual ~StoragePartitionShaderClearTest() {}
-
- const base::FilePath& cache_path() { return temp_dir_.path(); }
-
- virtual void SetUp() OVERRIDE {
- ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
- ShaderCacheFactory::GetInstance()->SetCacheInfo(kDefaultClientId,
- cache_path());
-
- cache_ = ShaderCacheFactory::GetInstance()->Get(kDefaultClientId);
- ASSERT_TRUE(cache_.get() != NULL);
+ virtual ~StoragePartitionShaderClearTest() {
+ cache_ = NULL;
+ ShaderCacheFactory::GetInstance()->RemoveCacheInfo(kDefaultClientId);
}
void InitCache() {
@@ -405,41 +388,30 @@ class StoragePartitionShaderClearTest : public testing::Test {
size_t Size() { return cache_->Size(); }
- private:
- virtual void TearDown() OVERRIDE {
- cache_ = NULL;
- ShaderCacheFactory::GetInstance()->RemoveCacheInfo(kDefaultClientId);
+ TestBrowserContext* browser_context() {
+ return browser_context_.get();
}
- base::ScopedTempDir temp_dir_;
+ private:
content::TestBrowserThreadBundle thread_bundle_;
+ scoped_ptr<TestBrowserContext> browser_context_;
scoped_refptr<ShaderDiskCache> cache_;
};
-void ClearData(content::StoragePartitionImpl* sp,
- const base::Closure& cb) {
- base::Time time;
- sp->ClearData(
- StoragePartition::REMOVE_DATA_MASK_SHADER_CACHE,
- StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL,
- NULL, StoragePartition::OriginMatcherFunction(),
- time, time, cb);
-}
-
// Tests ---------------------------------------------------------------------
TEST_F(StoragePartitionShaderClearTest, ClearShaderCache) {
InitCache();
EXPECT_EQ(1u, Size());
- TestClosureCallback clear_cb;
- StoragePartitionImpl storage_partition(
- cache_path(), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+ base::RunLoop run_loop;
base::MessageLoop::current()->PostTask(
- FROM_HERE, base::Bind(&ClearData, &storage_partition,
- clear_cb.callback()));
- clear_cb.WaitForResult();
+ FROM_HERE, base::Bind(
+ &ClearData,
+ BrowserContext::GetDefaultStoragePartition(browser_context()),
+ &run_loop));
+ run_loop.Run();
EXPECT_EQ(0u, Size());
}
@@ -467,7 +439,7 @@ TEST_F(StoragePartitionImplTest, QuotaClientMaskGeneration) {
StoragePartition::REMOVE_DATA_MASK_INDEXEDDB));
}
-void PopulateTestQuotaManagedPersistentData(quota::MockQuotaManager* manager) {
+void PopulateTestQuotaManagedPersistentData(MockQuotaManager* manager) {
manager->AddOrigin(kOrigin2, kPersistent, kClientFile, base::Time());
manager->AddOrigin(kOrigin3, kPersistent, kClientFile,
base::Time::Now() - base::TimeDelta::FromDays(1));
@@ -477,7 +449,7 @@ void PopulateTestQuotaManagedPersistentData(quota::MockQuotaManager* manager) {
EXPECT_TRUE(manager->OriginHasData(kOrigin3, kPersistent, kClientFile));
}
-void PopulateTestQuotaManagedTemporaryData(quota::MockQuotaManager* manager) {
+void PopulateTestQuotaManagedTemporaryData(MockQuotaManager* manager) {
manager->AddOrigin(kOrigin1, kTemporary, kClientFile, base::Time::Now());
manager->AddOrigin(kOrigin3, kTemporary, kClientFile,
base::Time::Now() - base::TimeDelta::FromDays(1));
@@ -487,7 +459,7 @@ void PopulateTestQuotaManagedTemporaryData(quota::MockQuotaManager* manager) {
EXPECT_TRUE(manager->OriginHasData(kOrigin3, kTemporary, kClientFile));
}
-void PopulateTestQuotaManagedData(quota::MockQuotaManager* manager) {
+void PopulateTestQuotaManagedData(MockQuotaManager* manager) {
// Set up kOrigin1 with a temporary quota, kOrigin2 with a persistent
// quota, and kOrigin3 with both. kOrigin1 is modified now, kOrigin2
// is modified at the beginning of time, and kOrigin3 is modified one day
@@ -496,7 +468,7 @@ void PopulateTestQuotaManagedData(quota::MockQuotaManager* manager) {
PopulateTestQuotaManagedTemporaryData(manager);
}
-void PopulateTestQuotaManagedNonBrowsingData(quota::MockQuotaManager* manager) {
+void PopulateTestQuotaManagedNonBrowsingData(MockQuotaManager* manager) {
manager->AddOrigin(kOriginDevTools, kTemporary, kClientFile, base::Time());
manager->AddOrigin(kOriginDevTools, kPersistent, kClientFile, base::Time());
}
@@ -504,14 +476,15 @@ void PopulateTestQuotaManagedNonBrowsingData(quota::MockQuotaManager* manager) {
TEST_F(StoragePartitionImplTest, RemoveQuotaManagedDataForeverBoth) {
PopulateTestQuotaManagedData(GetMockManager());
- TestClosureCallback clear_cb;
- StoragePartitionImpl sp(
- base::FilePath(), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
- static_cast<StoragePartitionImpl*>(&sp)->OverrideQuotaManagerForTesting(
+ StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>(
+ BrowserContext::GetDefaultStoragePartition(browser_context()));
+ partition->OverrideQuotaManagerForTesting(
GetMockManager());
+
+ base::RunLoop run_loop;
base::MessageLoop::current()->PostTask(
- FROM_HERE, base::Bind(&ClearQuotaData, &sp, clear_cb.callback()));
- clear_cb.WaitForResult();
+ FROM_HERE, base::Bind(&ClearQuotaData, partition, &run_loop));
+ run_loop.Run();
EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin1, kTemporary,
kClientFile));
@@ -530,14 +503,15 @@ TEST_F(StoragePartitionImplTest, RemoveQuotaManagedDataForeverBoth) {
TEST_F(StoragePartitionImplTest, RemoveQuotaManagedDataForeverOnlyTemporary) {
PopulateTestQuotaManagedTemporaryData(GetMockManager());
- TestClosureCallback clear_cb;
- StoragePartitionImpl sp(
- base::FilePath(), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
- static_cast<StoragePartitionImpl*>(&sp)->OverrideQuotaManagerForTesting(
+ StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>(
+ BrowserContext::GetDefaultStoragePartition(browser_context()));
+ partition->OverrideQuotaManagerForTesting(
GetMockManager());
+
+ base::RunLoop run_loop;
base::MessageLoop::current()->PostTask(
- FROM_HERE, base::Bind(&ClearQuotaData, &sp, clear_cb.callback()));
- clear_cb.WaitForResult();
+ FROM_HERE, base::Bind(&ClearQuotaData, partition, &run_loop));
+ run_loop.Run();
EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin1, kTemporary,
kClientFile));
@@ -556,14 +530,15 @@ TEST_F(StoragePartitionImplTest, RemoveQuotaManagedDataForeverOnlyTemporary) {
TEST_F(StoragePartitionImplTest, RemoveQuotaManagedDataForeverOnlyPersistent) {
PopulateTestQuotaManagedPersistentData(GetMockManager());
- TestClosureCallback clear_cb;
- StoragePartitionImpl sp(
- base::FilePath(), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
- static_cast<StoragePartitionImpl*>(&sp)->OverrideQuotaManagerForTesting(
+ StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>(
+ BrowserContext::GetDefaultStoragePartition(browser_context()));
+ partition->OverrideQuotaManagerForTesting(
GetMockManager());
+
+ base::RunLoop run_loop;
base::MessageLoop::current()->PostTask(
- FROM_HERE, base::Bind(&ClearQuotaData, &sp, clear_cb.callback()));
- clear_cb.WaitForResult();
+ FROM_HERE, base::Bind(&ClearQuotaData, partition, &run_loop));
+ run_loop.Run();
EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin1, kTemporary,
kClientFile));
@@ -580,14 +555,15 @@ TEST_F(StoragePartitionImplTest, RemoveQuotaManagedDataForeverOnlyPersistent) {
}
TEST_F(StoragePartitionImplTest, RemoveQuotaManagedDataForeverNeither) {
- TestClosureCallback clear_cb;
- StoragePartitionImpl sp(
- base::FilePath(), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
- static_cast<StoragePartitionImpl*>(&sp)->OverrideQuotaManagerForTesting(
+ StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>(
+ BrowserContext::GetDefaultStoragePartition(browser_context()));
+ partition->OverrideQuotaManagerForTesting(
GetMockManager());
+
+ base::RunLoop run_loop;
base::MessageLoop::current()->PostTask(
- FROM_HERE, base::Bind(&ClearQuotaData, &sp, clear_cb.callback()));
- clear_cb.WaitForResult();
+ FROM_HERE, base::Bind(&ClearQuotaData, partition, &run_loop));
+ run_loop.Run();
EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin1, kTemporary,
kClientFile));
@@ -606,15 +582,17 @@ TEST_F(StoragePartitionImplTest, RemoveQuotaManagedDataForeverNeither) {
TEST_F(StoragePartitionImplTest, RemoveQuotaManagedDataForeverSpecificOrigin) {
PopulateTestQuotaManagedData(GetMockManager());
- TestClosureCallback clear_cb;
- StoragePartitionImpl sp(
- base::FilePath(), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
- static_cast<StoragePartitionImpl*>(&sp)->OverrideQuotaManagerForTesting(
+ StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>(
+ BrowserContext::GetDefaultStoragePartition(browser_context()));
+ partition->OverrideQuotaManagerForTesting(
GetMockManager());
+
+ base::RunLoop run_loop;
base::MessageLoop::current()->PostTask(
FROM_HERE, base::Bind(&ClearQuotaDataForOrigin,
- &sp, kOrigin1, base::Time(), clear_cb.callback()));
- clear_cb.WaitForResult();
+ partition, kOrigin1, base::Time(),
+ &run_loop));
+ run_loop.Run();
EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin1, kTemporary,
kClientFile));
@@ -633,17 +611,18 @@ TEST_F(StoragePartitionImplTest, RemoveQuotaManagedDataForeverSpecificOrigin) {
TEST_F(StoragePartitionImplTest, RemoveQuotaManagedDataForLastHour) {
PopulateTestQuotaManagedData(GetMockManager());
- TestClosureCallback clear_cb;
- StoragePartitionImpl sp(
- base::FilePath(), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
- static_cast<StoragePartitionImpl*>(&sp)->OverrideQuotaManagerForTesting(
+ StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>(
+ BrowserContext::GetDefaultStoragePartition(browser_context()));
+ partition->OverrideQuotaManagerForTesting(
GetMockManager());
+
+ base::RunLoop run_loop;
base::MessageLoop::current()->PostTask(
FROM_HERE, base::Bind(&ClearQuotaDataForOrigin,
- &sp, GURL(),
+ partition, GURL(),
base::Time::Now() - base::TimeDelta::FromHours(1),
- clear_cb.callback()));
- clear_cb.WaitForResult();
+ &run_loop));
+ run_loop.Run();
EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin1, kTemporary,
kClientFile));
@@ -662,17 +641,17 @@ TEST_F(StoragePartitionImplTest, RemoveQuotaManagedDataForLastHour) {
TEST_F(StoragePartitionImplTest, RemoveQuotaManagedDataForLastWeek) {
PopulateTestQuotaManagedData(GetMockManager());
- TestClosureCallback clear_cb;
- StoragePartitionImpl sp(
- base::FilePath(), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
- static_cast<StoragePartitionImpl*>(&sp)->OverrideQuotaManagerForTesting(
+ base::RunLoop run_loop;
+ StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>(
+ BrowserContext::GetDefaultStoragePartition(browser_context()));
+ partition->OverrideQuotaManagerForTesting(
GetMockManager());
base::MessageLoop::current()->PostTask(
FROM_HERE, base::Bind(&ClearQuotaDataForNonPersistent,
- &sp,
+ partition,
base::Time::Now() - base::TimeDelta::FromDays(7),
- clear_cb.callback()));
- clear_cb.WaitForResult();
+ &run_loop));
+ run_loop.Run();
EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin1, kTemporary,
kClientFile));
@@ -690,25 +669,25 @@ TEST_F(StoragePartitionImplTest, RemoveQuotaManagedDataForLastWeek) {
TEST_F(StoragePartitionImplTest, RemoveQuotaManagedUnprotectedOrigins) {
// Protect kOrigin1.
- scoped_refptr<quota::MockSpecialStoragePolicy> mock_policy =
- new quota::MockSpecialStoragePolicy;
+ scoped_refptr<MockSpecialStoragePolicy> mock_policy =
+ new MockSpecialStoragePolicy;
mock_policy->AddProtected(kOrigin1.GetOrigin());
PopulateTestQuotaManagedData(GetMockManager());
- TestClosureCallback clear_cb;
- StoragePartitionImpl sp(
- base::FilePath(), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
- static_cast<StoragePartitionImpl*>(&sp)->OverrideQuotaManagerForTesting(
+ StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>(
+ BrowserContext::GetDefaultStoragePartition(browser_context()));
+ partition->OverrideQuotaManagerForTesting(
GetMockManager());
- static_cast<StoragePartitionImpl*>(
- &sp)->OverrideSpecialStoragePolicyForTesting(mock_policy);
+ partition->OverrideSpecialStoragePolicyForTesting(mock_policy);
+
+ base::RunLoop run_loop;
base::MessageLoop::current()->PostTask(
FROM_HERE, base::Bind(&ClearQuotaDataWithOriginMatcher,
- &sp, GURL(),
+ partition, GURL(),
base::Bind(&DoesOriginMatchForUnprotectedWeb),
- base::Time(), clear_cb.callback()));
- clear_cb.WaitForResult();
+ base::Time(), &run_loop));
+ run_loop.Run();
EXPECT_TRUE(GetMockManager()->OriginHasData(kOrigin1, kTemporary,
kClientFile));
@@ -726,27 +705,26 @@ TEST_F(StoragePartitionImplTest, RemoveQuotaManagedUnprotectedOrigins) {
TEST_F(StoragePartitionImplTest, RemoveQuotaManagedProtectedSpecificOrigin) {
// Protect kOrigin1.
- scoped_refptr<quota::MockSpecialStoragePolicy> mock_policy =
- new quota::MockSpecialStoragePolicy;
+ scoped_refptr<MockSpecialStoragePolicy> mock_policy =
+ new MockSpecialStoragePolicy;
mock_policy->AddProtected(kOrigin1.GetOrigin());
PopulateTestQuotaManagedData(GetMockManager());
- TestClosureCallback clear_cb;
- StoragePartitionImpl sp(
- base::FilePath(), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
- static_cast<StoragePartitionImpl*>(&sp)->OverrideQuotaManagerForTesting(
+ StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>(
+ BrowserContext::GetDefaultStoragePartition(browser_context()));
+ partition->OverrideQuotaManagerForTesting(
GetMockManager());
- static_cast<StoragePartitionImpl*>(
- &sp)->OverrideSpecialStoragePolicyForTesting(mock_policy);
+ partition->OverrideSpecialStoragePolicyForTesting(mock_policy);
// Try to remove kOrigin1. Expect failure.
+ base::RunLoop run_loop;
base::MessageLoop::current()->PostTask(
FROM_HERE, base::Bind(&ClearQuotaDataWithOriginMatcher,
- &sp, kOrigin1,
+ partition, kOrigin1,
base::Bind(&DoesOriginMatchForUnprotectedWeb),
- base::Time(), clear_cb.callback()));
- clear_cb.WaitForResult();
+ base::Time(), &run_loop));
+ run_loop.Run();
EXPECT_TRUE(GetMockManager()->OriginHasData(kOrigin1, kTemporary,
kClientFile));
@@ -764,27 +742,26 @@ TEST_F(StoragePartitionImplTest, RemoveQuotaManagedProtectedSpecificOrigin) {
TEST_F(StoragePartitionImplTest, RemoveQuotaManagedProtectedOrigins) {
// Protect kOrigin1.
- scoped_refptr<quota::MockSpecialStoragePolicy> mock_policy =
- new quota::MockSpecialStoragePolicy;
+ scoped_refptr<MockSpecialStoragePolicy> mock_policy =
+ new MockSpecialStoragePolicy;
mock_policy->AddProtected(kOrigin1.GetOrigin());
PopulateTestQuotaManagedData(GetMockManager());
// Try to remove kOrigin1. Expect success.
- TestClosureCallback clear_cb;
- StoragePartitionImpl sp(
- base::FilePath(), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
- static_cast<StoragePartitionImpl*>(&sp)->OverrideQuotaManagerForTesting(
+ base::RunLoop run_loop;
+ StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>(
+ BrowserContext::GetDefaultStoragePartition(browser_context()));
+ partition->OverrideQuotaManagerForTesting(
GetMockManager());
- static_cast<StoragePartitionImpl*>(
- &sp)->OverrideSpecialStoragePolicyForTesting(mock_policy);
+ partition->OverrideSpecialStoragePolicyForTesting(mock_policy);
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&ClearQuotaDataWithOriginMatcher,
- &sp, GURL(),
+ partition, GURL(),
base::Bind(&DoesOriginMatchForBothProtectedAndUnprotectedWeb),
- base::Time(), clear_cb.callback()));
- clear_cb.WaitForResult();
+ base::Time(), &run_loop));
+ run_loop.Run();
EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin1, kTemporary,
kClientFile));
@@ -803,17 +780,17 @@ TEST_F(StoragePartitionImplTest, RemoveQuotaManagedProtectedOrigins) {
TEST_F(StoragePartitionImplTest, RemoveQuotaManagedIgnoreDevTools) {
PopulateTestQuotaManagedNonBrowsingData(GetMockManager());
- TestClosureCallback clear_cb;
- StoragePartitionImpl sp(
- base::FilePath(), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
- static_cast<StoragePartitionImpl*>(&sp)->OverrideQuotaManagerForTesting(
+ base::RunLoop run_loop;
+ StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>(
+ BrowserContext::GetDefaultStoragePartition(browser_context()));
+ partition->OverrideQuotaManagerForTesting(
GetMockManager());
base::MessageLoop::current()->PostTask(
FROM_HERE, base::Bind(&ClearQuotaDataWithOriginMatcher,
- &sp, GURL(),
+ partition, GURL(),
base::Bind(&DoesOriginMatchUnprotected),
- base::Time(), clear_cb.callback()));
- clear_cb.WaitForResult();
+ base::Time(), &run_loop));
+ run_loop.Run();
// Check that devtools data isn't removed.
EXPECT_TRUE(GetMockManager()->OriginHasData(kOriginDevTools, kTemporary,
@@ -823,74 +800,72 @@ TEST_F(StoragePartitionImplTest, RemoveQuotaManagedIgnoreDevTools) {
}
TEST_F(StoragePartitionImplTest, RemoveCookieForever) {
- RemoveCookieTester tester(GetBrowserContext());
+ RemoveCookieTester tester(browser_context());
tester.AddCookie();
ASSERT_TRUE(tester.ContainsCookie());
- TestClosureCallback clear_cb;
- StoragePartitionImpl sp(
- base::FilePath(), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
- sp.SetURLRequestContext(GetBrowserContext()->GetRequestContext());
+ StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>(
+ BrowserContext::GetDefaultStoragePartition(browser_context()));
+ partition->SetURLRequestContext(browser_context()->GetRequestContext());
+
+ base::RunLoop run_loop;
base::MessageLoop::current()->PostTask(
FROM_HERE, base::Bind(&ClearCookies,
- &sp, base::Time(), base::Time::Max(),
- clear_cb.callback()));
- clear_cb.WaitForResult();
+ partition, base::Time(), base::Time::Max(),
+ &run_loop));
+ run_loop.Run();
EXPECT_FALSE(tester.ContainsCookie());
}
TEST_F(StoragePartitionImplTest, RemoveCookieLastHour) {
- RemoveCookieTester tester(GetBrowserContext());
+ RemoveCookieTester tester(browser_context());
tester.AddCookie();
ASSERT_TRUE(tester.ContainsCookie());
- TestClosureCallback clear_cb;
- StoragePartitionImpl sp(
- base::FilePath(), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+ StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>(
+ BrowserContext::GetDefaultStoragePartition(browser_context()));
base::Time an_hour_ago = base::Time::Now() - base::TimeDelta::FromHours(1);
- sp.SetURLRequestContext(GetBrowserContext()->GetRequestContext());
+ partition->SetURLRequestContext(browser_context()->GetRequestContext());
+
+ base::RunLoop run_loop;
base::MessageLoop::current()->PostTask(
FROM_HERE, base::Bind(&ClearCookies,
- &sp, an_hour_ago, base::Time::Max(),
- clear_cb.callback()));
- clear_cb.WaitForResult();
+ partition, an_hour_ago, base::Time::Max(),
+ &run_loop));
+ run_loop.Run();
EXPECT_FALSE(tester.ContainsCookie());
}
TEST_F(StoragePartitionImplTest, RemoveUnprotectedLocalStorageForever) {
// Protect kOrigin1.
- scoped_refptr<quota::MockSpecialStoragePolicy> mock_policy =
- new quota::MockSpecialStoragePolicy;
+ scoped_refptr<MockSpecialStoragePolicy> mock_policy =
+ new MockSpecialStoragePolicy;
mock_policy->AddProtected(kOrigin1.GetOrigin());
- RemoveLocalStorageTester tester(GetBrowserContext());
+ RemoveLocalStorageTester tester(browser_context());
tester.AddDOMStorageTestData();
EXPECT_TRUE(tester.DOMStorageExistsForOrigin(kOrigin1));
EXPECT_TRUE(tester.DOMStorageExistsForOrigin(kOrigin2));
EXPECT_TRUE(tester.DOMStorageExistsForOrigin(kOrigin3));
- TestClosureCallback clear_cb;
- DOMStorageContextWrapper* dom_storage_context =
- static_cast<DOMStorageContextWrapper*>(
- content::BrowserContext::GetDefaultStoragePartition(
- GetBrowserContext())->GetDOMStorageContext());
- StoragePartitionImpl sp(base::FilePath(), NULL, NULL, NULL, NULL,
- dom_storage_context, NULL, NULL, NULL, NULL);
- static_cast<StoragePartitionImpl*>(
- &sp)->OverrideSpecialStoragePolicyForTesting(mock_policy);
+ StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>(
+ BrowserContext::GetDefaultStoragePartition(browser_context()));
+ partition->OverrideSpecialStoragePolicyForTesting(mock_policy);
+
+ base::RunLoop run_loop;
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&ClearStuff,
StoragePartitionImpl::REMOVE_DATA_MASK_LOCAL_STORAGE,
- &sp, base::Time(), base::Time::Max(),
+ partition, base::Time(), base::Time::Max(),
base::Bind(&DoesOriginMatchForUnprotectedWeb),
- clear_cb.callback()));
- clear_cb.WaitForResult();
+ &run_loop));
+ run_loop.Run();
EXPECT_TRUE(tester.DOMStorageExistsForOrigin(kOrigin1));
EXPECT_FALSE(tester.DOMStorageExistsForOrigin(kOrigin2));
@@ -899,34 +874,30 @@ TEST_F(StoragePartitionImplTest, RemoveUnprotectedLocalStorageForever) {
TEST_F(StoragePartitionImplTest, RemoveProtectedLocalStorageForever) {
// Protect kOrigin1.
- scoped_refptr<quota::MockSpecialStoragePolicy> mock_policy =
- new quota::MockSpecialStoragePolicy;
+ scoped_refptr<MockSpecialStoragePolicy> mock_policy =
+ new MockSpecialStoragePolicy;
mock_policy->AddProtected(kOrigin1.GetOrigin());
- RemoveLocalStorageTester tester(GetBrowserContext());
+ RemoveLocalStorageTester tester(browser_context());
tester.AddDOMStorageTestData();
EXPECT_TRUE(tester.DOMStorageExistsForOrigin(kOrigin1));
EXPECT_TRUE(tester.DOMStorageExistsForOrigin(kOrigin2));
EXPECT_TRUE(tester.DOMStorageExistsForOrigin(kOrigin3));
- TestClosureCallback clear_cb;
- DOMStorageContextWrapper* dom_storage_context =
- static_cast<DOMStorageContextWrapper*>(
- content::BrowserContext::GetDefaultStoragePartition(
- GetBrowserContext())->GetDOMStorageContext());
- StoragePartitionImpl sp(base::FilePath(), NULL, NULL, NULL, NULL,
- dom_storage_context, NULL, NULL, NULL, NULL);
- static_cast<StoragePartitionImpl*>(
- &sp)->OverrideSpecialStoragePolicyForTesting(mock_policy);
+ StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>(
+ BrowserContext::GetDefaultStoragePartition(browser_context()));
+ partition->OverrideSpecialStoragePolicyForTesting(mock_policy);
+
+ base::RunLoop run_loop;
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&ClearStuff,
StoragePartitionImpl::REMOVE_DATA_MASK_LOCAL_STORAGE,
- &sp, base::Time(), base::Time::Max(),
+ partition, base::Time(), base::Time::Max(),
base::Bind(&DoesOriginMatchForBothProtectedAndUnprotectedWeb),
- clear_cb.callback()));
- clear_cb.WaitForResult();
+ &run_loop));
+ run_loop.Run();
// Even if kOrigin1 is protected, it will be deleted since we specify
// ClearData to delete protected data.
@@ -936,29 +907,26 @@ TEST_F(StoragePartitionImplTest, RemoveProtectedLocalStorageForever) {
}
TEST_F(StoragePartitionImplTest, RemoveLocalStorageForLastWeek) {
- RemoveLocalStorageTester tester(GetBrowserContext());
+ RemoveLocalStorageTester tester(browser_context());
tester.AddDOMStorageTestData();
EXPECT_TRUE(tester.DOMStorageExistsForOrigin(kOrigin1));
EXPECT_TRUE(tester.DOMStorageExistsForOrigin(kOrigin2));
EXPECT_TRUE(tester.DOMStorageExistsForOrigin(kOrigin3));
- TestClosureCallback clear_cb;
- DOMStorageContextWrapper* dom_storage_context =
- static_cast<DOMStorageContextWrapper*>(
- content::BrowserContext::GetDefaultStoragePartition(
- GetBrowserContext())->GetDOMStorageContext());
- StoragePartitionImpl sp(base::FilePath(), NULL, NULL, NULL, NULL,
- dom_storage_context, NULL, NULL, NULL, NULL);
+ StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>(
+ BrowserContext::GetDefaultStoragePartition(browser_context()));
base::Time a_week_ago = base::Time::Now() - base::TimeDelta::FromDays(7);
+
+ base::RunLoop run_loop;
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&ClearStuff,
StoragePartitionImpl::REMOVE_DATA_MASK_LOCAL_STORAGE,
- &sp, a_week_ago, base::Time::Max(),
+ partition, a_week_ago, base::Time::Max(),
base::Bind(&DoesOriginMatchForBothProtectedAndUnprotectedWeb),
- clear_cb.callback()));
- clear_cb.WaitForResult();
+ &run_loop));
+ run_loop.Run();
// kOrigin1 and kOrigin2 do not have age more than a week.
EXPECT_FALSE(tester.DOMStorageExistsForOrigin(kOrigin1));
diff --git a/chromium/content/browser/streams/stream.cc b/chromium/content/browser/streams/stream.cc
index 5d20fe6ab64..d10770c42d2 100644
--- a/chromium/content/browser/streams/stream.cc
+++ b/chromium/content/browser/streams/stream.cc
@@ -7,11 +7,13 @@
#include "base/bind.h"
#include "base/location.h"
#include "base/message_loop/message_loop_proxy.h"
+#include "base/values.h"
#include "content/browser/streams/stream_handle_impl.h"
#include "content/browser/streams/stream_read_observer.h"
#include "content/browser/streams/stream_registry.h"
#include "content/browser/streams/stream_write_observer.h"
#include "net/base/io_buffer.h"
+#include "net/http/http_response_headers.h"
namespace {
// Start throttling the connection at about 1MB.
@@ -158,12 +160,15 @@ Stream::StreamState Stream::ReadRawData(net::IOBuffer* buf,
return STREAM_HAS_DATA;
}
-scoped_ptr<StreamHandle> Stream::CreateHandle(const GURL& original_url,
- const std::string& mime_type) {
+scoped_ptr<StreamHandle> Stream::CreateHandle(
+ const GURL& original_url,
+ const std::string& mime_type,
+ scoped_refptr<net::HttpResponseHeaders> response_headers) {
CHECK(!stream_handle_);
stream_handle_ = new StreamHandleImpl(weak_ptr_factory_.GetWeakPtr(),
original_url,
- mime_type);
+ mime_type,
+ response_headers);
return scoped_ptr<StreamHandle>(stream_handle_).Pass();
}
diff --git a/chromium/content/browser/streams/stream.h b/chromium/content/browser/streams/stream.h
index 3937f5b9e38..e7e9586ca96 100644
--- a/chromium/content/browser/streams/stream.h
+++ b/chromium/content/browser/streams/stream.h
@@ -13,6 +13,7 @@
#include "url/gurl.h"
namespace net {
+class HttpResponseHeaders;
class IOBuffer;
}
@@ -77,8 +78,10 @@ class CONTENT_EXPORT Stream : public base::RefCountedThreadSafe<Stream> {
// and STREAM_COMPLETE if the stream is finalized and all data has been read.
StreamState ReadRawData(net::IOBuffer* buf, int buf_size, int* bytes_read);
- scoped_ptr<StreamHandle> CreateHandle(const GURL& original_url,
- const std::string& mime_type);
+ scoped_ptr<StreamHandle> CreateHandle(
+ const GURL& original_url,
+ const std::string& mime_type,
+ scoped_refptr<net::HttpResponseHeaders> response_headers);
void CloseHandle();
// Indicates whether there is space in the buffer to add more data.
diff --git a/chromium/content/browser/streams/stream_context.cc b/chromium/content/browser/streams/stream_context.cc
index 44ca0f4a1cd..357e68cae58 100644
--- a/chromium/content/browser/streams/stream_context.cc
+++ b/chromium/content/browser/streams/stream_context.cc
@@ -7,6 +7,7 @@
#include "base/bind.h"
#include "content/browser/streams/stream_registry.h"
#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
using base::UserDataAdapter;
diff --git a/chromium/content/browser/streams/stream_context.h b/chromium/content/browser/streams/stream_context.h
index fb6cdc720ff..ad8f65cb638 100644
--- a/chromium/content/browser/streams/stream_context.h
+++ b/chromium/content/browser/streams/stream_context.h
@@ -9,7 +9,6 @@
#include "base/memory/scoped_ptr.h"
#include "base/sequenced_task_runner_helpers.h"
#include "content/common/content_export.h"
-#include "content/public/browser/browser_thread.h"
namespace content {
class BrowserContext;
diff --git a/chromium/content/browser/streams/stream_handle_impl.cc b/chromium/content/browser/streams/stream_handle_impl.cc
index d9f877cbc76..6ff49621b57 100644
--- a/chromium/content/browser/streams/stream_handle_impl.cc
+++ b/chromium/content/browser/streams/stream_handle_impl.cc
@@ -4,23 +4,39 @@
#include "content/browser/streams/stream_handle_impl.h"
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/message_loop/message_loop_proxy.h"
#include "content/browser/streams/stream.h"
-#include "content/public/browser/browser_thread.h"
+#include "net/http/http_response_headers.h"
namespace content {
-StreamHandleImpl::StreamHandleImpl(const base::WeakPtr<Stream>& stream,
- const GURL& original_url,
- const std::string& mime_type)
+namespace {
+
+void RunCloseListeners(const std::vector<base::Closure>& close_listeners) {
+ for (size_t i = 0; i < close_listeners.size(); ++i)
+ close_listeners[i].Run();
+}
+
+} // namespace
+
+StreamHandleImpl::StreamHandleImpl(
+ const base::WeakPtr<Stream>& stream,
+ const GURL& original_url,
+ const std::string& mime_type,
+ scoped_refptr<net::HttpResponseHeaders> response_headers)
: stream_(stream),
url_(stream->url()),
original_url_(original_url),
mime_type_(mime_type),
+ response_headers_(response_headers),
stream_message_loop_(base::MessageLoopProxy::current().get()) {}
StreamHandleImpl::~StreamHandleImpl() {
- stream_message_loop_->PostTask(FROM_HERE,
- base::Bind(&Stream::CloseHandle, stream_));
+ stream_message_loop_->PostTaskAndReply(FROM_HERE,
+ base::Bind(&Stream::CloseHandle, stream_),
+ base::Bind(&RunCloseListeners, close_listeners_));
}
const GURL& StreamHandleImpl::GetURL() {
@@ -35,4 +51,12 @@ const std::string& StreamHandleImpl::GetMimeType() {
return mime_type_;
}
+scoped_refptr<net::HttpResponseHeaders> StreamHandleImpl::GetResponseHeaders() {
+ return response_headers_;
+}
+
+void StreamHandleImpl::AddCloseListener(const base::Closure& callback) {
+ close_listeners_.push_back(callback);
+}
+
} // namespace content
diff --git a/chromium/content/browser/streams/stream_handle_impl.h b/chromium/content/browser/streams/stream_handle_impl.h
index aabd0cb7dfe..dc64dade6da 100644
--- a/chromium/content/browser/streams/stream_handle_impl.h
+++ b/chromium/content/browser/streams/stream_handle_impl.h
@@ -5,11 +5,16 @@
#ifndef CONTENT_BROWSER_STREAMS_STREAM_HANDLE_IMPL_H_
#define CONTENT_BROWSER_STREAMS_STREAM_HANDLE_IMPL_H_
+#include <vector>
+
#include "base/memory/weak_ptr.h"
-#include "base/message_loop/message_loop_proxy.h"
#include "base/synchronization/lock.h"
#include "content/public/browser/stream_handle.h"
+namespace base {
+class MessageLoopProxy;
+}
+
namespace content {
class Stream;
@@ -18,7 +23,8 @@ class StreamHandleImpl : public StreamHandle {
public:
StreamHandleImpl(const base::WeakPtr<Stream>& stream,
const GURL& original_url,
- const std::string& mime_type);
+ const std::string& mime_type,
+ scoped_refptr<net::HttpResponseHeaders> response_headers);
virtual ~StreamHandleImpl();
private:
@@ -26,12 +32,16 @@ class StreamHandleImpl : public StreamHandle {
virtual const GURL& GetURL() OVERRIDE;
virtual const GURL& GetOriginalURL() OVERRIDE;
virtual const std::string& GetMimeType() OVERRIDE;
+ virtual scoped_refptr<net::HttpResponseHeaders> GetResponseHeaders() OVERRIDE;
+ virtual void AddCloseListener(const base::Closure& callback) OVERRIDE;
base::WeakPtr<Stream> stream_;
GURL url_;
GURL original_url_;
std::string mime_type_;
+ scoped_refptr<net::HttpResponseHeaders> response_headers_;
base::MessageLoopProxy* stream_message_loop_;
+ std::vector<base::Closure> close_listeners_;
};
} // namespace content
diff --git a/chromium/content/browser/streams/stream_url_request_job.cc b/chromium/content/browser/streams/stream_url_request_job.cc
index 09b9e6d7815..6d70eefa515 100644
--- a/chromium/content/browser/streams/stream_url_request_job.cc
+++ b/chromium/content/browser/streams/stream_url_request_job.cc
@@ -226,7 +226,7 @@ void StreamURLRequestJob::HeadersCompleted(net::HttpStatusCode status_code) {
if (status_code == net::HTTP_OK) {
std::string content_type_header(net::HttpRequestHeaders::kContentType);
content_type_header.append(": ");
- content_type_header.append("plain/text");
+ content_type_header.append("text/plain");
headers->AddHeader(content_type_header);
}
diff --git a/chromium/content/browser/streams/stream_url_request_job_unittest.cc b/chromium/content/browser/streams/stream_url_request_job_unittest.cc
index d3378032080..035a588bc96 100644
--- a/chromium/content/browser/streams/stream_url_request_job_unittest.cc
+++ b/chromium/content/browser/streams/stream_url_request_job_unittest.cc
@@ -51,7 +51,7 @@ class StreamURLRequestJobTest : public testing::Test {
StreamRegistry* registry_;
};
- StreamURLRequestJobTest() : message_loop_(base::MessageLoop::TYPE_IO) {}
+ StreamURLRequestJobTest() {}
virtual void SetUp() {
registry_.reset(new StreamRegistry());
@@ -76,7 +76,7 @@ class StreamURLRequestJobTest : public testing::Test {
const std::string& expected_response) {
net::TestDelegate delegate;
request_ = url_request_context_.CreateRequest(
- url, net::DEFAULT_PRIORITY, &delegate);
+ url, net::DEFAULT_PRIORITY, &delegate, NULL);
request_->set_method(method);
if (!extra_headers.IsEmpty())
request_->SetExtraRequestHeaders(extra_headers);
@@ -93,7 +93,7 @@ class StreamURLRequestJobTest : public testing::Test {
}
protected:
- base::MessageLoop message_loop_;
+ base::MessageLoopForIO message_loop_;
scoped_ptr<StreamRegistry> registry_;
net::URLRequestContext url_request_context_;
@@ -134,7 +134,7 @@ TEST_F(StreamURLRequestJobTest, TestGetLargeStreamRequest) {
TEST_F(StreamURLRequestJobTest, TestGetNonExistentStreamRequest) {
net::TestDelegate delegate;
request_ = url_request_context_.CreateRequest(
- kStreamURL, net::DEFAULT_PRIORITY, &delegate);
+ kStreamURL, net::DEFAULT_PRIORITY, &delegate, NULL);
request_->set_method("GET");
request_->Start();
diff --git a/chromium/content/browser/theme_helper_mac.h b/chromium/content/browser/theme_helper_mac.h
index ab04337209d..565903bbe3e 100644
--- a/chromium/content/browser/theme_helper_mac.h
+++ b/chromium/content/browser/theme_helper_mac.h
@@ -8,6 +8,7 @@
#include "base/memory/singleton.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
+#include "third_party/WebKit/public/web/mac/WebScrollbarTheme.h"
namespace content {
@@ -17,10 +18,15 @@ class ThemeHelperMac : public NotificationObserver {
// if none.
static ThemeHelperMac* GetInstance();
+ // Returns the value of +[NSScroller preferredScrollStyle] as expressed
+ // as the blink enum value.
+ static blink::ScrollerStyle GetPreferredScrollerStyle();
+
static void SendThemeChangeToAllRenderers(
float initial_button_delay,
float autoscroll_button_delay,
bool jump_on_track_click,
+ blink::ScrollerStyle preferred_scroller_style,
bool redraw);
private:
diff --git a/chromium/content/browser/theme_helper_mac.mm b/chromium/content/browser/theme_helper_mac.mm
index 3b952cc79b8..9f0f86d1a59 100644
--- a/chromium/content/browser/theme_helper_mac.mm
+++ b/chromium/content/browser/theme_helper_mac.mm
@@ -4,13 +4,25 @@
#include "content/browser/theme_helper_mac.h"
-#include <Foundation/Foundation.h>
+#import <Cocoa/Cocoa.h>
+#include "base/command_line.h"
+#include "base/mac/sdk_forward_declarations.h"
#include "content/common/view_messages.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_process_host.h"
+#include "content/public/common/content_switches.h"
+
+// Declare notification names from the 10.7 SDK.
+#if !defined(MAC_OS_X_VERSION_10_7) || \
+ MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
+
+NSString* NSPreferredScrollerStyleDidChangeNotification =
+ @"NSPreferredScrollerStyleDidChangeNotification";
+
+#endif
@interface ScrollbarPrefsObserver : NSObject
@@ -37,6 +49,17 @@ suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately];
name:@"AppleNoRedisplayAppearancePreferenceChanged"
object:nil
suspensionBehavior:NSNotificationSuspensionBehaviorCoalesce];
+
+ // In single-process mode, renderers will catch these notifications
+ // themselves and listening for them here may trigger the DCHECK in Observe().
+ if ([NSScroller respondsToSelector:@selector(preferredScrollerStyle)] &&
+ !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess)) {
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(behaviorPrefsChanged:)
+ name:NSPreferredScrollerStyleDidChangeNotification
+ object:nil];
+ }
}
+ (void)appearancePrefsChanged:(NSNotification*)notification {
@@ -56,6 +79,7 @@ suspensionBehavior:NSNotificationSuspensionBehaviorCoalesce];
[defaults floatForKey:@"NSScrollerButtonDelay"],
[defaults floatForKey:@"NSScrollerButtonPeriod"],
[defaults boolForKey:@"AppleScrollerPagingBehavior"],
+ content::ThemeHelperMac::GetPreferredScrollerStyle(),
redraw);
}
@@ -63,6 +87,38 @@ suspensionBehavior:NSNotificationSuspensionBehaviorCoalesce];
namespace content {
+// static
+ThemeHelperMac* ThemeHelperMac::GetInstance() {
+ return Singleton<ThemeHelperMac,
+ LeakySingletonTraits<ThemeHelperMac> >::get();
+}
+
+// static
+blink::ScrollerStyle ThemeHelperMac::GetPreferredScrollerStyle() {
+ if (![NSScroller respondsToSelector:@selector(preferredScrollerStyle)])
+ return blink::ScrollerStyleLegacy;
+ return static_cast<blink::ScrollerStyle>([NSScroller preferredScrollerStyle]);
+}
+
+// static
+void ThemeHelperMac::SendThemeChangeToAllRenderers(
+ float initial_button_delay,
+ float autoscroll_button_delay,
+ bool jump_on_track_click,
+ blink::ScrollerStyle preferred_scroller_style,
+ bool redraw) {
+ for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator());
+ !it.IsAtEnd();
+ it.Advance()) {
+ it.GetCurrentValue()->Send(new ViewMsg_UpdateScrollbarTheme(
+ initial_button_delay,
+ autoscroll_button_delay,
+ jump_on_track_click,
+ preferred_scroller_style,
+ redraw));
+ }
+}
+
ThemeHelperMac::ThemeHelperMac() {
[ScrollbarPrefsObserver registerAsObserver];
registrar_.Add(this,
@@ -73,13 +129,6 @@ ThemeHelperMac::ThemeHelperMac() {
ThemeHelperMac::~ThemeHelperMac() {
}
-// static
-ThemeHelperMac* ThemeHelperMac::GetInstance() {
- return Singleton<ThemeHelperMac,
- LeakySingletonTraits<ThemeHelperMac> >::get();
-}
-
-
void ThemeHelperMac::Observe(int type,
const NotificationSource& source,
const NotificationDetails& details) {
@@ -94,24 +143,8 @@ void ThemeHelperMac::Observe(int type,
[defaults floatForKey:@"NSScrollerButtonDelay"],
[defaults floatForKey:@"NSScrollerButtonPeriod"],
[defaults boolForKey:@"AppleScrollerPagingBehavior"],
+ GetPreferredScrollerStyle(),
false));
}
-// static
-void ThemeHelperMac::SendThemeChangeToAllRenderers(
- float initial_button_delay,
- float autoscroll_button_delay,
- bool jump_on_track_click,
- bool redraw) {
- for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator());
- !it.IsAtEnd();
- it.Advance()) {
- it.GetCurrentValue()->Send(new ViewMsg_UpdateScrollbarTheme(
- initial_button_delay,
- autoscroll_button_delay,
- jump_on_track_click,
- redraw));
- }
-}
-
} // namespace content
diff --git a/chromium/content/browser/time_zone_monitor.cc b/chromium/content/browser/time_zone_monitor.cc
new file mode 100644
index 00000000000..0c17f8e5206
--- /dev/null
+++ b/chromium/content/browser/time_zone_monitor.cc
@@ -0,0 +1,31 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/time_zone_monitor.h"
+
+#include "base/logging.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_process_host.h"
+
+namespace content {
+
+TimeZoneMonitor::TimeZoneMonitor() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+}
+
+TimeZoneMonitor::~TimeZoneMonitor() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+}
+
+void TimeZoneMonitor::NotifyRenderers() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ for (RenderProcessHost::iterator iterator =
+ RenderProcessHost::AllHostsIterator();
+ !iterator.IsAtEnd();
+ iterator.Advance()) {
+ iterator.GetCurrentValue()->NotifyTimezoneChange();
+ }
+}
+
+} // namespace content
diff --git a/chromium/content/browser/time_zone_monitor.h b/chromium/content/browser/time_zone_monitor.h
new file mode 100644
index 00000000000..fed177f9ae6
--- /dev/null
+++ b/chromium/content/browser/time_zone_monitor.h
@@ -0,0 +1,51 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_TIME_ZONE_MONITOR_H_
+#define CONTENT_BROWSER_TIME_ZONE_MONITOR_H_
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace content {
+
+// TimeZoneMonitor watches the system time zone, and notifies renderers
+// when it changes. Some renderer code caches the system time zone, so
+// this notification is necessary to inform such code that cached
+// timezone data may have become invalid. Due to sandboxing, it is not
+// possible for renderer processes to monitor for system time zone
+// changes themselves, so this must happen in the browser process.
+//
+// Sandboxing also may prevent renderer processes from reading the time
+// zone when it does change, so platforms may have to deal with this in
+// platform-specific ways:
+// - Mac uses a sandbox hole defined in content/renderer/renderer.sb.
+// - Linux-based platforms use ProxyLocaltimeCallToBrowser in
+// content/zygote/zygote_main_linux.cc and HandleLocaltime in
+// content/browser/renderer_host/sandbox_ipc_linux.cc to override
+// localtime in renderer processes with custom code that calls
+// localtime in the browser process via Chrome IPC.
+
+class TimeZoneMonitor {
+ public:
+ // Returns a new TimeZoneMonitor object (likely a subclass) specific to the
+ // platform.
+ static scoped_ptr<TimeZoneMonitor> Create();
+
+ virtual ~TimeZoneMonitor();
+
+ protected:
+ TimeZoneMonitor();
+
+ // Loop over all renderers and notify them that the system time zone may
+ // have changed.
+ void NotifyRenderers();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TimeZoneMonitor);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_TIME_ZONE_MONITOR_H_
diff --git a/chromium/content/browser/time_zone_monitor_android.cc b/chromium/content/browser/time_zone_monitor_android.cc
new file mode 100644
index 00000000000..af57f80a516
--- /dev/null
+++ b/chromium/content/browser/time_zone_monitor_android.cc
@@ -0,0 +1,38 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/time_zone_monitor_android.h"
+
+#include "base/android/jni_android.h"
+#include "jni/TimeZoneMonitor_jni.h"
+
+namespace content {
+
+TimeZoneMonitorAndroid::TimeZoneMonitorAndroid() : TimeZoneMonitor() {
+ impl_.Reset(Java_TimeZoneMonitor_getInstance(
+ base::android::AttachCurrentThread(),
+ base::android::GetApplicationContext(),
+ reinterpret_cast<intptr_t>(this)));
+}
+
+TimeZoneMonitorAndroid::~TimeZoneMonitorAndroid() {
+ Java_TimeZoneMonitor_stop(base::android::AttachCurrentThread(), impl_.obj());
+}
+
+// static
+bool TimeZoneMonitorAndroid::Register(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+void TimeZoneMonitorAndroid::TimeZoneChangedFromJava(JNIEnv* env,
+ jobject caller) {
+ NotifyRenderers();
+}
+
+// static
+scoped_ptr<TimeZoneMonitor> TimeZoneMonitor::Create() {
+ return scoped_ptr<TimeZoneMonitor>(new TimeZoneMonitorAndroid());
+}
+
+} // namespace content
diff --git a/chromium/content/browser/time_zone_monitor_android.h b/chromium/content/browser/time_zone_monitor_android.h
new file mode 100644
index 00000000000..a462eb5a2de
--- /dev/null
+++ b/chromium/content/browser/time_zone_monitor_android.h
@@ -0,0 +1,37 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_TIME_ZONE_MONITOR_ANDROID_H_
+#define CONTENT_BROWSER_TIME_ZONE_MONITOR_ANDROID_H_
+
+#include "content/browser/time_zone_monitor.h"
+
+#include <jni.h>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/basictypes.h"
+
+namespace content {
+
+class TimeZoneMonitorAndroid : public TimeZoneMonitor {
+ public:
+ TimeZoneMonitorAndroid();
+ virtual ~TimeZoneMonitorAndroid();
+
+ // Must be called at startup.
+ static bool Register(JNIEnv* env);
+
+ // Called by the Java implementation when the system time zone changes.
+ void TimeZoneChangedFromJava(JNIEnv* env, jobject caller);
+
+ private:
+ // Java provider of system time zone change notifications.
+ base::android::ScopedJavaGlobalRef<jobject> impl_;
+
+ DISALLOW_COPY_AND_ASSIGN(TimeZoneMonitorAndroid);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_TIME_ZONE_MONITOR_ANDROID_H_
diff --git a/chromium/content/browser/time_zone_monitor_chromeos.cc b/chromium/content/browser/time_zone_monitor_chromeos.cc
new file mode 100644
index 00000000000..6fc885edba0
--- /dev/null
+++ b/chromium/content/browser/time_zone_monitor_chromeos.cc
@@ -0,0 +1,37 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/time_zone_monitor.h"
+
+#include "chromeos/settings/timezone_settings.h"
+
+namespace content {
+
+class TimeZoneMonitorChromeOS
+ : public TimeZoneMonitor,
+ public chromeos::system::TimezoneSettings::Observer {
+ public:
+ TimeZoneMonitorChromeOS() : TimeZoneMonitor() {
+ chromeos::system::TimezoneSettings::GetInstance()->AddObserver(this);
+ }
+
+ virtual ~TimeZoneMonitorChromeOS() {
+ chromeos::system::TimezoneSettings::GetInstance()->RemoveObserver(this);
+ }
+
+ // chromeos::system::TimezoneSettings::Observer implementation.
+ virtual void TimezoneChanged(const icu::TimeZone& time_zone) OVERRIDE {
+ NotifyRenderers();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TimeZoneMonitorChromeOS);
+};
+
+// static
+scoped_ptr<TimeZoneMonitor> TimeZoneMonitor::Create() {
+ return scoped_ptr<TimeZoneMonitor>(new TimeZoneMonitorChromeOS());
+}
+
+} // namespace content
diff --git a/chromium/content/browser/time_zone_monitor_linux.cc b/chromium/content/browser/time_zone_monitor_linux.cc
new file mode 100644
index 00000000000..f8cae0e2430
--- /dev/null
+++ b/chromium/content/browser/time_zone_monitor_linux.cc
@@ -0,0 +1,166 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/time_zone_monitor.h"
+
+#include <stdlib.h>
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/file_path_watcher.h"
+#include "base/memory/ref_counted.h"
+#include "base/stl_util.h"
+#include "content/public/browser/browser_thread.h"
+
+#if !defined(OS_CHROMEOS)
+
+namespace content {
+
+namespace {
+class TimeZoneMonitorLinuxImpl;
+} // namespace
+
+class TimeZoneMonitorLinux : public TimeZoneMonitor {
+ public:
+ TimeZoneMonitorLinux();
+ virtual ~TimeZoneMonitorLinux();
+
+ void NotifyRenderersFromImpl() {
+ NotifyRenderers();
+ }
+
+ private:
+ scoped_refptr<TimeZoneMonitorLinuxImpl> impl_;
+
+ DISALLOW_COPY_AND_ASSIGN(TimeZoneMonitorLinux);
+};
+
+namespace {
+
+// FilePathWatcher needs to run on the FILE thread, but TimeZoneMonitor runs
+// on the UI thread. TimeZoneMonitorLinuxImpl is the bridge between these
+// threads.
+class TimeZoneMonitorLinuxImpl
+ : public base::RefCountedThreadSafe<TimeZoneMonitorLinuxImpl> {
+ public:
+ explicit TimeZoneMonitorLinuxImpl(TimeZoneMonitorLinux* owner)
+ : base::RefCountedThreadSafe<TimeZoneMonitorLinuxImpl>(),
+ file_path_watchers_(),
+ owner_(owner) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ BrowserThread::PostTask(
+ BrowserThread::FILE,
+ FROM_HERE,
+ base::Bind(&TimeZoneMonitorLinuxImpl::StartWatchingOnFileThread, this));
+ }
+
+ void StopWatching() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ owner_ = NULL;
+ BrowserThread::PostTask(
+ BrowserThread::FILE,
+ FROM_HERE,
+ base::Bind(&TimeZoneMonitorLinuxImpl::StopWatchingOnFileThread, this));
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<TimeZoneMonitorLinuxImpl>;
+
+ ~TimeZoneMonitorLinuxImpl() {
+ DCHECK(!owner_);
+ STLDeleteElements(&file_path_watchers_);
+ }
+
+ void StartWatchingOnFileThread() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
+ // There is no true standard for where time zone information is actually
+ // stored. glibc uses /etc/localtime, uClibc uses /etc/TZ, and some older
+ // systems store the name of the time zone file within /usr/share/zoneinfo
+ // in /etc/timezone. Different libraries and custom builds may mean that
+ // still more paths are used. Just watch all three of these paths, because
+ // false positives are harmless, assuming the false positive rate is
+ // reasonable.
+ const char* kFilesToWatch[] = {
+ "/etc/localtime",
+ "/etc/timezone",
+ "/etc/TZ",
+ };
+
+ for (size_t index = 0; index < arraysize(kFilesToWatch); ++index) {
+ file_path_watchers_.push_back(new base::FilePathWatcher());
+ file_path_watchers_.back()->Watch(
+ base::FilePath(kFilesToWatch[index]),
+ false,
+ base::Bind(&TimeZoneMonitorLinuxImpl::OnTimeZoneFileChanged, this));
+ }
+ }
+
+ void StopWatchingOnFileThread() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ STLDeleteElements(&file_path_watchers_);
+ }
+
+ void OnTimeZoneFileChanged(const base::FilePath& path, bool error) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&TimeZoneMonitorLinuxImpl::OnTimeZoneFileChangedOnUIThread,
+ this));
+ }
+
+ void OnTimeZoneFileChangedOnUIThread() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (owner_) {
+ owner_->NotifyRenderersFromImpl();
+ }
+ }
+
+ std::vector<base::FilePathWatcher*> file_path_watchers_;
+ TimeZoneMonitorLinux* owner_;
+
+ DISALLOW_COPY_AND_ASSIGN(TimeZoneMonitorLinuxImpl);
+};
+
+} // namespace
+
+TimeZoneMonitorLinux::TimeZoneMonitorLinux()
+ : TimeZoneMonitor(),
+ impl_() {
+ // If the TZ environment variable is set, its value specifies the time zone
+ // specification, and it's pointless to monitor any files in /etc for
+ // changes because such changes would have no effect on the TZ environment
+ // variable and thus the interpretation of the local time zone in the
+ // or renderer processes.
+ //
+ // The system-specific format for the TZ environment variable beginning with
+ // a colon is implemented by glibc as the path to a time zone data file, and
+ // it would be possible to monitor this file for changes if a TZ variable of
+ // this format was encountered, but this is not necessary: when loading a
+ // time zone specification in this way, glibc does not reload the file when
+ // it changes, so it's pointless to respond to a notification that it has
+ // changed.
+ if (!getenv("TZ")) {
+ impl_ = new TimeZoneMonitorLinuxImpl(this);
+ }
+}
+
+TimeZoneMonitorLinux::~TimeZoneMonitorLinux() {
+ if (impl_) {
+ impl_->StopWatching();
+ }
+}
+
+// static
+scoped_ptr<TimeZoneMonitor> TimeZoneMonitor::Create() {
+ return scoped_ptr<TimeZoneMonitor>(new TimeZoneMonitorLinux());
+}
+
+} // namespace content
+
+#endif // !OS_CHROMEOS
diff --git a/chromium/content/browser/time_zone_monitor_mac.mm b/chromium/content/browser/time_zone_monitor_mac.mm
new file mode 100644
index 00000000000..c435aae818e
--- /dev/null
+++ b/chromium/content/browser/time_zone_monitor_mac.mm
@@ -0,0 +1,40 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/time_zone_monitor.h"
+
+#import <Foundation/Foundation.h>
+
+namespace content {
+
+class TimeZoneMonitorMac : public TimeZoneMonitor {
+ public:
+ TimeZoneMonitorMac() : TimeZoneMonitor() {
+ NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
+ notification_observer_ =
+ [nc addObserverForName:NSSystemTimeZoneDidChangeNotification
+ object:nil
+ queue:nil
+ usingBlock:^(NSNotification* notification) {
+ NotifyRenderers();
+ }];
+ }
+
+ virtual ~TimeZoneMonitorMac() {
+ NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
+ [nc removeObserver:notification_observer_];
+ }
+
+ private:
+ id notification_observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(TimeZoneMonitorMac);
+};
+
+// static
+scoped_ptr<TimeZoneMonitor> TimeZoneMonitor::Create() {
+ return scoped_ptr<TimeZoneMonitor>(new TimeZoneMonitorMac());
+}
+
+} // namespace content
diff --git a/chromium/content/browser/time_zone_monitor_win.cc b/chromium/content/browser/time_zone_monitor_win.cc
new file mode 100644
index 00000000000..6d99b940e41
--- /dev/null
+++ b/chromium/content/browser/time_zone_monitor_win.cc
@@ -0,0 +1,46 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/time_zone_monitor.h"
+
+#include <windows.h>
+
+#include "base/basictypes.h"
+#include "ui/gfx/win/singleton_hwnd.h"
+
+namespace content {
+
+class TimeZoneMonitorWin : public TimeZoneMonitor,
+ public gfx::SingletonHwnd::Observer {
+ public:
+ TimeZoneMonitorWin() : TimeZoneMonitor() {
+ gfx::SingletonHwnd::GetInstance()->AddObserver(this);
+ }
+
+ virtual ~TimeZoneMonitorWin() {
+ gfx::SingletonHwnd::GetInstance()->RemoveObserver(this);
+ }
+
+ // gfx::SingletonHwnd::Observer implementation.
+ virtual void OnWndProc(HWND hwnd,
+ UINT message,
+ WPARAM wparam,
+ LPARAM lparam) OVERRIDE {
+ if (message != WM_TIMECHANGE) {
+ return;
+ }
+
+ NotifyRenderers();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TimeZoneMonitorWin);
+};
+
+// static
+scoped_ptr<TimeZoneMonitor> TimeZoneMonitor::Create() {
+ return scoped_ptr<TimeZoneMonitor>(new TimeZoneMonitorWin());
+}
+
+} // namespace content
diff --git a/chromium/content/browser/tracing/BUILD.gn b/chromium/content/browser/tracing/BUILD.gn
new file mode 100644
index 00000000000..94694ee2fa4
--- /dev/null
+++ b/chromium/content/browser/tracing/BUILD.gn
@@ -0,0 +1,66 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//tools/grit/grit_rule.gni")
+
+# generate_about_tracing puts its files in this directory
+tracing_gen_dir = "$root_gen_dir/content/browser/tracing"
+
+# The script just writes filename with no dirs to the .grd, so we always need
+# this file to be in the same directory as the inputs.
+tracing_grd = "$tracing_gen_dir/tracing_resources.grd"
+
+action("generate_tracing_grd") {
+ visibility = ":resources"
+ script = "generate_trace_viewer_grd.py"
+
+ input_pages = [
+ "$tracing_gen_dir/about_tracing.html",
+ "$tracing_gen_dir/about_tracing.js",
+ ]
+ source_prereqs = input_pages
+ outputs = [ tracing_grd ]
+
+ args = rebase_path(input_pages, target_gen_dir) + [
+ "--output", rebase_path(tracing_grd, root_build_dir),
+ ]
+
+ deps = [
+ "//third_party/trace-viewer:generate_about_tracing",
+ ]
+}
+
+# This can't use the grit template because the grd file is generated at build
+# time, so the trick of using grit_info to get the real inputs/outputs at GYP
+# time isn't possible.
+action("resources") {
+ script = "//tools/grit/grit.py"
+
+ # Get the list of grit script sources.
+ grit_inputs_build_rel =
+ exec_script("//tools/grit/grit_info.py", [ "--inputs" ], "list lines")
+
+ source_prereqs = rebase_path(grit_inputs_build_rel, ".", root_build_dir) + [
+ grit_resource_id_file,
+ ]
+ outputs = [
+ "$target_gen_dir/grit/tracing_resources.h",
+ "$target_gen_dir/tracing_resources.pak",
+ ]
+
+ args = [
+ "-i", rebase_path(tracing_grd, root_build_dir), "build",
+ "-f", rebase_path(grit_resource_id_file, root_build_dir),
+ "-o", rebase_path(target_gen_dir, root_build_dir),
+ # resource_ids has an entry for our .grd file that looks like:
+ # "<(SHARED_INTERMEDIATE_DIR)/content/browser/tracing/tracing_resources.grd"
+ # and what we pass here should make that resolve to our .grd file.
+ "-DSHARED_INTERMEDIATE_DIR=" +
+ rebase_path(root_gen_dir, root_build_dir),
+ ] + grit_defines
+
+ deps = [
+ ":generate_tracing_grd",
+ ]
+}
diff --git a/chromium/content/browser/tracing/etw_system_event_consumer_win.cc b/chromium/content/browser/tracing/etw_system_event_consumer_win.cc
new file mode 100644
index 00000000000..df20bce41d7
--- /dev/null
+++ b/chromium/content/browser/tracing/etw_system_event_consumer_win.cc
@@ -0,0 +1,215 @@
+// 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/tracing/etw_system_event_consumer_win.h"
+
+#include "base/base64.h"
+#include "base/debug/trace_event_impl.h"
+#include "base/json/json_string_value_serializer.h"
+#include "base/lazy_instance.h"
+#include "base/memory/singleton.h"
+#include "base/strings/stringprintf.h"
+#include "base/time/time.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace content {
+
+namespace {
+
+const int kEtwBufferSizeInKBytes = 16;
+const int kEtwBufferFlushTimeoutInSeconds = 1;
+
+std::string GuidToString(const GUID& guid) {
+ return base::StringPrintf("%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
+ guid.Data1, guid.Data2, guid.Data3,
+ guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3],
+ guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
+}
+
+} // namespace
+
+EtwSystemEventConsumer::EtwSystemEventConsumer()
+ : thread_("EtwConsumerThread") {
+}
+
+EtwSystemEventConsumer::~EtwSystemEventConsumer() {
+}
+
+bool EtwSystemEventConsumer::StartSystemTracing() {
+
+ // Activate kernel tracing.
+ if (!StartKernelSessionTracing())
+ return false;
+
+ // Start the consumer thread and start consuming events.
+ thread_.Start();
+ thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&EtwSystemEventConsumer::TraceAndConsumeOnThread,
+ base::Unretained(this)));
+
+ return true;
+}
+
+void EtwSystemEventConsumer::StopSystemTracing(const OutputCallback& callback) {
+ // Deactivate kernel tracing.
+ if (!StopKernelSessionTracing()) {
+ LOG(FATAL) << "Could not stop system tracing.";
+ }
+
+ // Stop consuming and flush events.
+ OutputCallback on_stop_system_tracing_done_callback =
+ base::Bind(&EtwSystemEventConsumer::OnStopSystemTracingDone,
+ base::Unretained(this),
+ callback);
+ thread_.message_loop()->PostTask(FROM_HERE,
+ base::Bind(&EtwSystemEventConsumer::FlushOnThread,
+ base::Unretained(this), on_stop_system_tracing_done_callback));
+}
+
+void EtwSystemEventConsumer::OnStopSystemTracingDone(
+ const OutputCallback& callback,
+ const scoped_refptr<base::RefCountedString>& result) {
+
+ // Stop the consumer thread.
+ thread_.Stop();
+
+ // Pass the serialized events.
+ callback.Run(result);
+}
+
+bool EtwSystemEventConsumer::StartKernelSessionTracing() {
+ // Enabled flags (tracing facilities).
+ uint32 enabled_flags = EVENT_TRACE_FLAG_IMAGE_LOAD |
+ EVENT_TRACE_FLAG_PROCESS |
+ EVENT_TRACE_FLAG_THREAD |
+ EVENT_TRACE_FLAG_CSWITCH;
+
+ EVENT_TRACE_PROPERTIES& p = *properties_.get();
+ p.LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
+ p.FlushTimer = kEtwBufferFlushTimeoutInSeconds;
+ p.BufferSize = kEtwBufferSizeInKBytes;
+ p.LogFileNameOffset = 0;
+ p.EnableFlags = enabled_flags;
+ p.Wnode.ClientContext = 1; // QPC timer accuracy.
+
+ HRESULT hr = base::win::EtwTraceController::Start(
+ KERNEL_LOGGER_NAME, &properties_, &session_handle_);
+ if (FAILED(hr)) {
+ VLOG(1) << "StartRealtimeSession() failed with " << hr << ".";
+ return false;
+ }
+
+ return true;
+}
+
+bool EtwSystemEventConsumer::StopKernelSessionTracing() {
+ HRESULT hr = base::win::EtwTraceController::Stop(
+ KERNEL_LOGGER_NAME, &properties_);
+ return SUCCEEDED(hr);
+}
+
+// static
+EtwSystemEventConsumer* EtwSystemEventConsumer::GetInstance() {
+ return Singleton<EtwSystemEventConsumer>::get();
+}
+
+// static
+void EtwSystemEventConsumer::ProcessEvent(EVENT_TRACE* event) {
+ EtwSystemEventConsumer::GetInstance()->AppendEventToBuffer(event);
+}
+
+void EtwSystemEventConsumer::AddSyncEventToBuffer() {
+ // Sync the clocks.
+ base::Time walltime = base::Time::NowFromSystemTime();
+ base::TimeTicks now = base::TimeTicks::NowFromSystemTraceTime();
+
+ LARGE_INTEGER walltime_in_us;
+ walltime_in_us.QuadPart = walltime.ToInternalValue();
+ LARGE_INTEGER now_in_us;
+ now_in_us.QuadPart = now.ToInternalValue();
+
+ // Add fields to the event.
+ scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue());
+ value->Set("guid", new base::StringValue("ClockSync"));
+ value->Set("walltime", new base::StringValue(
+ base::StringPrintf("%08X%08X",
+ walltime_in_us.HighPart,
+ walltime_in_us.LowPart)));
+ value->Set("tick", new base::StringValue(
+ base::StringPrintf("%08X%08X",
+ now_in_us.HighPart,
+ now_in_us.LowPart)));
+
+ // Append it to the events buffer.
+ events_->Append(value.release());
+}
+
+void EtwSystemEventConsumer::AppendEventToBuffer(EVENT_TRACE* event) {
+ using base::FundamentalValue;
+
+ scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue());
+
+ // Add header fields to the event.
+ LARGE_INTEGER ts_us;
+ ts_us.QuadPart = event->Header.TimeStamp.QuadPart / 10;
+ value->Set("ts", new base::StringValue(
+ base::StringPrintf("%08X%08X", ts_us.HighPart, ts_us.LowPart)));
+
+ value->Set("guid", new base::StringValue(GuidToString(event->Header.Guid)));
+
+ value->Set("op", new FundamentalValue(event->Header.Class.Type));
+ value->Set("ver", new FundamentalValue(event->Header.Class.Version));
+ value->Set("pid",
+ new FundamentalValue(static_cast<int>(event->Header.ProcessId)));
+ value->Set("tid",
+ new FundamentalValue(static_cast<int>(event->Header.ThreadId)));
+ value->Set("cpu", new FundamentalValue(event->BufferContext.ProcessorNumber));
+
+ // Base64 encode the payload bytes.
+ base::StringPiece buffer(static_cast<const char*>(event->MofData),
+ event->MofLength);
+ std::string payload;
+ base::Base64Encode(buffer, &payload);
+ value->Set("payload", new base::StringValue(payload));
+
+ // Append it to the events buffer.
+ events_->Append(value.release());
+}
+
+void EtwSystemEventConsumer::TraceAndConsumeOnThread() {
+ // Create the events buffer.
+ events_.reset(new base::ListValue());
+
+ // Output a clock sync event.
+ AddSyncEventToBuffer();
+
+ HRESULT hr = OpenRealtimeSession(KERNEL_LOGGER_NAME);
+ if (FAILED(hr))
+ return;
+ Consume();
+ Close();
+}
+
+void EtwSystemEventConsumer::FlushOnThread(const OutputCallback& callback) {
+ // Add the header information to the stream.
+ scoped_ptr<base::DictionaryValue> header(new base::DictionaryValue());
+ header->Set("name", base::Value::CreateStringValue("ETW"));
+
+ // Release and pass the events buffer.
+ header->Set("content", events_.release());
+
+ // Serialize the results as a JSon string.
+ std::string output;
+ JSONStringValueSerializer serializer(&output);
+ serializer.Serialize(*header.get());
+
+ // Pass the result to the UI Thread.
+ scoped_refptr<base::RefCountedString> result =
+ base::RefCountedString::TakeString(&output);
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(callback, result));
+}
+
+} // namespace content
diff --git a/chromium/content/browser/tracing/etw_system_event_consumer_win.h b/chromium/content/browser/tracing/etw_system_event_consumer_win.h
new file mode 100644
index 00000000000..8d1764f8de0
--- /dev/null
+++ b/chromium/content/browser/tracing/etw_system_event_consumer_win.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_TRACING_TRACING_CONSUMER_WIN_H_
+#define CONTENT_BROWSER_TRACING_TRACING_CONSUMER_WIN_H_
+
+#include "base/bind.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/threading/thread.h"
+#include "base/values.h"
+#include "base/win/event_trace_consumer.h"
+#include "base/win/event_trace_controller.h"
+
+template <typename Type>
+struct DefaultSingletonTraits;
+
+namespace content {
+
+class EtwSystemEventConsumer :
+ public base::win::EtwTraceConsumerBase<EtwSystemEventConsumer> {
+ public:
+ typedef base::Callback<void(const scoped_refptr<base::RefCountedString>&)>
+ OutputCallback;
+
+ bool StartSystemTracing();
+ void StopSystemTracing(const OutputCallback& callback);
+
+ // Retrieve the ETW consumer instance.
+ static EtwSystemEventConsumer* GetInstance();
+
+ private:
+ // This allows constructor and destructor to be private and usable only
+ // by the Singleton class.
+ friend struct DefaultSingletonTraits<EtwSystemEventConsumer>;
+
+ // Constructor.
+ EtwSystemEventConsumer();
+ virtual ~EtwSystemEventConsumer();
+
+ void AddSyncEventToBuffer();
+ void AppendEventToBuffer(EVENT_TRACE* event);
+
+ // Static override of EtwTraceConsumerBase::ProcessEvent.
+ // @param event the raw ETW event to process.
+ friend class base::win::EtwTraceConsumerBase<EtwSystemEventConsumer>;
+ static void ProcessEvent(EVENT_TRACE* event);
+
+ // Request the ETW trace controller to activate the kernel tracing.
+ // returns true on success, false if the kernel tracing isn't activated.
+ bool StartKernelSessionTracing();
+
+ // Request the ETW trace controller to deactivate the kernel tracing.
+ // @param callback the callback to call with the consumed events.
+ // @returns true on success, false if an error occurred.
+ bool StopKernelSessionTracing();
+
+ void OnStopSystemTracingDone(
+ const OutputCallback& callback,
+ const scoped_refptr<base::RefCountedString>& result);
+
+ void TraceAndConsumeOnThread();
+ void FlushOnThread(const OutputCallback& callback);
+
+ scoped_ptr<base::ListValue> events_;
+ base::Thread thread_;
+ TRACEHANDLE session_handle_;
+ base::win::EtwTraceProperties properties_;
+
+ DISALLOW_COPY_AND_ASSIGN(EtwSystemEventConsumer);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_TRACING_TRACING_CONSUMER_WIN_H_
diff --git a/chromium/content/browser/tracing/generate_trace_viewer_grd.py b/chromium/content/browser/tracing/generate_trace_viewer_grd.py
index 767f5c59746..acc7355cf6e 100755
--- a/chromium/content/browser/tracing/generate_trace_viewer_grd.py
+++ b/chromium/content/browser/tracing/generate_trace_viewer_grd.py
@@ -22,7 +22,6 @@ kGrdTemplate = '''<?xml version="1.0" encoding="UTF-8"?>
<emit emit_type='prepend'></emit>
</output>
<output filename="tracing_resources.pak" type="data_package" />
- <output filename="tracing_resources.rc" type="rc_all" />
</outputs>
<release seq="1">
<includes>
diff --git a/chromium/content/browser/tracing/trace_message_filter.cc b/chromium/content/browser/tracing/trace_message_filter.cc
index 35f23dc1025..ba8ec65de16 100644
--- a/chromium/content/browser/tracing/trace_message_filter.cc
+++ b/chromium/content/browser/tracing/trace_message_filter.cc
@@ -9,11 +9,12 @@
namespace content {
-TraceMessageFilter::TraceMessageFilter() :
- has_child_(false),
- is_awaiting_end_ack_(false),
- is_awaiting_capture_monitoring_snapshot_ack_(false),
- is_awaiting_buffer_percent_full_ack_(false) {
+TraceMessageFilter::TraceMessageFilter()
+ : BrowserMessageFilter(TracingMsgStart),
+ has_child_(false),
+ is_awaiting_end_ack_(false),
+ is_awaiting_capture_monitoring_snapshot_ack_(false),
+ is_awaiting_buffer_percent_full_ack_(false) {
}
TraceMessageFilter::~TraceMessageFilter() {}
@@ -33,11 +34,10 @@ void TraceMessageFilter::OnChannelClosing() {
}
}
-bool TraceMessageFilter::OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) {
+bool TraceMessageFilter::OnMessageReceived(const IPC::Message& message) {
// Always on IO thread (BrowserMessageFilter guarantee).
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(TraceMessageFilter, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(TraceMessageFilter, message)
IPC_MESSAGE_HANDLER(TracingHostMsg_ChildSupportsTracing,
OnChildSupportsTracing)
IPC_MESSAGE_HANDLER(TracingHostMsg_EndTracingAck, OnEndTracingAck)
@@ -52,7 +52,7 @@ bool TraceMessageFilter::OnMessageReceived(const IPC::Message& message,
IPC_MESSAGE_HANDLER(TracingHostMsg_TraceBufferPercentFullReply,
OnTraceBufferPercentFullReply)
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return handled;
}
@@ -121,7 +121,7 @@ void TraceMessageFilter::OnEndTracingAck(
if (is_awaiting_end_ack_) {
is_awaiting_end_ack_ = false;
TracingControllerImpl::GetInstance()->OnDisableRecordingAcked(
- known_categories);
+ this, known_categories);
} else {
NOTREACHED();
}
@@ -132,7 +132,8 @@ void TraceMessageFilter::OnCaptureMonitoringSnapshotAcked() {
// but check in case the child process is compromised.
if (is_awaiting_capture_monitoring_snapshot_ack_) {
is_awaiting_capture_monitoring_snapshot_ack_ = false;
- TracingControllerImpl::GetInstance()->OnCaptureMonitoringSnapshotAcked();
+ TracingControllerImpl::GetInstance()->OnCaptureMonitoringSnapshotAcked(
+ this);
} else {
NOTREACHED();
}
@@ -160,7 +161,7 @@ void TraceMessageFilter::OnTraceBufferPercentFullReply(float percent_full) {
if (is_awaiting_buffer_percent_full_ack_) {
is_awaiting_buffer_percent_full_ack_ = false;
TracingControllerImpl::GetInstance()->OnTraceBufferPercentFullReply(
- percent_full);
+ this, percent_full);
} else {
NOTREACHED();
}
diff --git a/chromium/content/browser/tracing/trace_message_filter.h b/chromium/content/browser/tracing/trace_message_filter.h
index 0f9e18ed9c3..4233fe04480 100644
--- a/chromium/content/browser/tracing/trace_message_filter.h
+++ b/chromium/content/browser/tracing/trace_message_filter.h
@@ -22,8 +22,7 @@ class TraceMessageFilter : public BrowserMessageFilter {
// BrowserMessageFilter implementation.
virtual void OnChannelClosing() OVERRIDE;
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
void SendBeginTracing(const std::string& category_filter_str,
base::debug::TraceLog::Options options);
diff --git a/chromium/content/browser/tracing/tracing_controller_browsertest.cc b/chromium/content/browser/tracing/tracing_controller_browsertest.cc
index bccf90af44a..36f0dce85a8 100644
--- a/chromium/content/browser/tracing/tracing_controller_browsertest.cc
+++ b/chromium/content/browser/tracing/tracing_controller_browsertest.cc
@@ -6,9 +6,9 @@
#include "base/run_loop.h"
#include "content/browser/tracing/tracing_controller_impl.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/shell/browser/shell.h"
-#include "content/test/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
namespace content {
@@ -148,19 +148,47 @@ class TracingControllerTest : public ContentBrowserTest {
TracingController* controller = TracingController::GetInstance();
{
+ bool is_monitoring;
+ std::string category_filter;
+ TracingController::Options options;
+ controller->GetMonitoringStatus(&is_monitoring,
+ &category_filter,
+ &options);
+ EXPECT_FALSE(is_monitoring);
+ EXPECT_EQ("-*Debug,-*Test", category_filter);
+ EXPECT_FALSE(options & TracingController::ENABLE_SYSTRACE);
+ EXPECT_FALSE(options & TracingController::RECORD_CONTINUOUSLY);
+ EXPECT_FALSE(options & TracingController::ENABLE_SAMPLING);
+ }
+
+ {
base::RunLoop run_loop;
TracingController::EnableMonitoringDoneCallback callback =
base::Bind(&TracingControllerTest::EnableMonitoringDoneCallbackTest,
base::Unretained(this),
run_loop.QuitClosure());
bool result = controller->EnableMonitoring(
- "", TracingController::ENABLE_SAMPLING, callback);
+ "*", TracingController::ENABLE_SAMPLING, callback);
ASSERT_TRUE(result);
run_loop.Run();
EXPECT_EQ(enable_monitoring_done_callback_count(), 1);
}
{
+ bool is_monitoring;
+ std::string category_filter;
+ TracingController::Options options;
+ controller->GetMonitoringStatus(&is_monitoring,
+ &category_filter,
+ &options);
+ EXPECT_TRUE(is_monitoring);
+ EXPECT_EQ("*", category_filter);
+ EXPECT_FALSE(options & TracingController::ENABLE_SYSTRACE);
+ EXPECT_FALSE(options & TracingController::RECORD_CONTINUOUSLY);
+ EXPECT_TRUE(options & TracingController::ENABLE_SAMPLING);
+ }
+
+ {
base::RunLoop run_loop;
TracingController::TracingFileResultCallback callback =
base::Bind(&TracingControllerTest::
@@ -184,6 +212,20 @@ class TracingControllerTest : public ContentBrowserTest {
run_loop.Run();
EXPECT_EQ(disable_monitoring_done_callback_count(), 1);
}
+
+ {
+ bool is_monitoring;
+ std::string category_filter;
+ TracingController::Options options;
+ controller->GetMonitoringStatus(&is_monitoring,
+ &category_filter,
+ &options);
+ EXPECT_FALSE(is_monitoring);
+ EXPECT_EQ("", category_filter);
+ EXPECT_FALSE(options & TracingController::ENABLE_SYSTRACE);
+ EXPECT_FALSE(options & TracingController::RECORD_CONTINUOUSLY);
+ EXPECT_FALSE(options & TracingController::ENABLE_SAMPLING);
+ }
}
private:
@@ -257,7 +299,7 @@ IN_PROC_BROWSER_TEST_F(
TracingController* controller = TracingController::GetInstance();
EXPECT_TRUE(controller->EnableMonitoring(
- "", TracingController::ENABLE_SAMPLING,
+ "*", TracingController::ENABLE_SAMPLING,
TracingController::EnableMonitoringDoneCallback()));
controller->CaptureMonitoringSnapshot(
base::FilePath(), TracingController::TracingFileResultCallback());
diff --git a/chromium/content/browser/tracing/tracing_controller_impl.cc b/chromium/content/browser/tracing/tracing_controller_impl.cc
index bc7cee2f930..63a44fcbcbf 100644
--- a/chromium/content/browser/tracing/tracing_controller_impl.cc
+++ b/chromium/content/browser/tracing/tracing_controller_impl.cc
@@ -1,7 +1,6 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-
#include "content/browser/tracing/tracing_controller_impl.h"
#include "base/bind.h"
@@ -10,10 +9,20 @@
#include "base/json/string_escape.h"
#include "base/strings/string_number_conversions.h"
#include "content/browser/tracing/trace_message_filter.h"
+#include "content/browser/tracing/tracing_ui.h"
#include "content/common/child_process_messages.h"
#include "content/public/browser/browser_message_filter.h"
#include "content/public/common/content_switches.h"
+#if defined(OS_CHROMEOS)
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/debug_daemon_client.h"
+#endif
+
+#if defined(OS_WIN)
+#include "content/browser/tracing/etw_system_event_consumer_win.h"
+#endif
+
using base::debug::TraceLog;
namespace content {
@@ -42,16 +51,28 @@ class TracingControllerImpl::ResultFile {
base::Bind(&TracingControllerImpl::ResultFile::CloseTask,
base::Unretained(this), callback));
}
+ void WriteSystemTrace(
+ const scoped_refptr<base::RefCountedString>& events_str_ptr) {
+ BrowserThread::PostTask(
+ BrowserThread::FILE,
+ FROM_HERE,
+ base::Bind(&TracingControllerImpl::ResultFile::WriteSystemTraceTask,
+ base::Unretained(this), events_str_ptr));
+ }
+
const base::FilePath& path() const { return path_; }
private:
void OpenTask();
void WriteTask(const scoped_refptr<base::RefCountedString>& events_str_ptr);
+ void WriteSystemTraceTask(
+ const scoped_refptr<base::RefCountedString>& events_str_ptr);
void CloseTask(const base::Closure& callback);
FILE* file_;
base::FilePath path_;
bool has_at_least_one_result_;
+ scoped_refptr<base::RefCountedString> system_trace_;
DISALLOW_COPY_AND_ASSIGN(ResultFile);
};
@@ -80,10 +101,10 @@ void TracingControllerImpl::ResultFile::OpenTask() {
void TracingControllerImpl::ResultFile::WriteTask(
const scoped_refptr<base::RefCountedString>& events_str_ptr) {
- if (!file_)
+ if (!file_ || !events_str_ptr->data().size())
return;
- // If there is already a result in the file, then put a commma
+ // If there is already a result in the file, then put a comma
// before the next batch of results.
if (has_at_least_one_result_) {
size_t written = fwrite(",", 1, 1, file_);
@@ -96,13 +117,41 @@ void TracingControllerImpl::ResultFile::WriteTask(
DCHECK(written == 1);
}
+void TracingControllerImpl::ResultFile::WriteSystemTraceTask(
+ const scoped_refptr<base::RefCountedString>& events_str_ptr) {
+ system_trace_ = events_str_ptr;
+}
+
void TracingControllerImpl::ResultFile::CloseTask(
const base::Closure& callback) {
if (!file_)
return;
- const char* trailout = "]}";
- size_t written = fwrite(trailout, strlen(trailout), 1, file_);
+ const char* trailevents = "]";
+ size_t written = fwrite(trailevents, strlen(trailevents), 1, file_);
+ DCHECK(written == 1);
+
+ if (system_trace_) {
+#if defined(OS_WIN)
+ // The Windows kernel events are kept into a JSon format stored as string
+ // and must not be escaped.
+ std::string json_string = system_trace_->data();
+#else
+ std::string json_string = base::GetQuotedJSONString(system_trace_->data());
+#endif
+
+ const char* systemTraceHead = ",\n\"systemTraceEvents\": ";
+ written = fwrite(systemTraceHead, strlen(systemTraceHead), 1, file_);
+ DCHECK(written == 1);
+
+ written = fwrite(json_string.data(), json_string.size(), 1, file_);
+ DCHECK(written == 1);
+
+ system_trace_ = NULL;
+ }
+
+ const char* trailout = "}";
+ written = fwrite(trailout, strlen(trailout), 1, file_);
DCHECK(written == 1);
base::CloseFile(file_);
file_ = NULL;
@@ -118,6 +167,9 @@ TracingControllerImpl::TracingControllerImpl() :
maximum_trace_buffer_percent_full_(0),
// Tracing may have been enabled by ContentMainRunner if kTraceStartup
// is specified in command line.
+#if defined(OS_CHROMEOS) || defined(OS_WIN)
+ is_system_tracing_(false),
+#endif
is_recording_(TraceLog::GetInstance()->IsEnabled()),
is_monitoring_(false) {
}
@@ -150,6 +202,28 @@ bool TracingControllerImpl::GetCategories(
return true;
}
+void TracingControllerImpl::SetEnabledOnFileThread(
+ const std::string& category_filter,
+ int mode,
+ int trace_options,
+ const base::Closure& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
+ TraceLog::GetInstance()->SetEnabled(
+ base::debug::CategoryFilter(category_filter),
+ static_cast<TraceLog::Mode>(mode),
+ static_cast<TraceLog::Options>(trace_options));
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback);
+}
+
+void TracingControllerImpl::SetDisabledOnFileThread(
+ const base::Closure& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
+ TraceLog::GetInstance()->SetDisabled();
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback);
+}
+
bool TracingControllerImpl::EnableRecording(
const std::string& category_filter,
TracingController::Options options,
@@ -158,33 +232,63 @@ bool TracingControllerImpl::EnableRecording(
if (!can_enable_recording())
return false;
+ is_recording_ = true;
#if defined(OS_ANDROID)
if (pending_get_categories_done_callback_.is_null())
TraceLog::GetInstance()->AddClockSyncMetadataEvent();
#endif
- TraceLog::Options trace_options = (options & RECORD_CONTINUOUSLY) ?
+ options_ = options;
+ int trace_options = (options & RECORD_CONTINUOUSLY) ?
TraceLog::RECORD_CONTINUOUSLY : TraceLog::RECORD_UNTIL_FULL;
if (options & ENABLE_SAMPLING) {
- trace_options = static_cast<TraceLog::Options>(
- trace_options | TraceLog::ENABLE_SAMPLING);
+ trace_options |= TraceLog::ENABLE_SAMPLING;
+ }
+
+ if (options & ENABLE_SYSTRACE) {
+#if defined(OS_CHROMEOS)
+ DCHECK(!is_system_tracing_);
+ chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->
+ StartSystemTracing();
+ is_system_tracing_ = true;
+#elif defined(OS_WIN)
+ DCHECK(!is_system_tracing_);
+ is_system_tracing_ =
+ EtwSystemEventConsumer::GetInstance()->StartSystemTracing();
+#endif
}
- // TODO(haraken): How to handle ENABLE_SYSTRACE?
- TraceLog::GetInstance()->SetEnabled(
- base::debug::CategoryFilter(category_filter), trace_options);
- is_recording_ = true;
+
+ base::Closure on_enable_recording_done_callback =
+ base::Bind(&TracingControllerImpl::OnEnableRecordingDone,
+ base::Unretained(this),
+ category_filter, trace_options, callback);
+ BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
+ base::Bind(&TracingControllerImpl::SetEnabledOnFileThread,
+ base::Unretained(this),
+ category_filter,
+ base::debug::TraceLog::RECORDING_MODE,
+ trace_options,
+ on_enable_recording_done_callback));
+ return true;
+}
+
+void TracingControllerImpl::OnEnableRecordingDone(
+ const std::string& category_filter,
+ int trace_options,
+ const EnableRecordingDoneCallback& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// Notify all child processes.
- for (TraceMessageFilterMap::iterator it = trace_message_filters_.begin();
+ for (TraceMessageFilterSet::iterator it = trace_message_filters_.begin();
it != trace_message_filters_.end(); ++it) {
- it->get()->SendBeginTracing(category_filter, trace_options);
+ it->get()->SendBeginTracing(category_filter,
+ static_cast<TraceLog::Options>(trace_options));
}
if (!callback.is_null())
callback.Run();
- return true;
}
bool TracingControllerImpl::DisableRecording(
@@ -195,11 +299,26 @@ bool TracingControllerImpl::DisableRecording(
if (!can_disable_recording())
return false;
- pending_disable_recording_done_callback_ = callback;
-
+ options_ = TracingController::Options();
// Disable local trace early to avoid traces during end-tracing process from
// interfering with the process.
- TraceLog::GetInstance()->SetDisabled();
+ base::Closure on_disable_recording_done_callback =
+ base::Bind(&TracingControllerImpl::OnDisableRecordingDone,
+ base::Unretained(this),
+ result_file_path, callback);
+ BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
+ base::Bind(&TracingControllerImpl::SetDisabledOnFileThread,
+ base::Unretained(this),
+ on_disable_recording_done_callback));
+ return true;
+}
+
+void TracingControllerImpl::OnDisableRecordingDone(
+ const base::FilePath& result_file_path,
+ const TracingFileResultCallback& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ pending_disable_recording_done_callback_ = callback;
#if defined(OS_ANDROID)
if (pending_get_categories_done_callback_.is_null())
@@ -212,25 +331,42 @@ bool TracingControllerImpl::DisableRecording(
// Count myself (local trace) in pending_disable_recording_ack_count_,
// acked below.
pending_disable_recording_ack_count_ = trace_message_filters_.size() + 1;
+ pending_disable_recording_filters_ = trace_message_filters_;
+
+#if defined(OS_CHROMEOS) || defined(OS_WIN)
+ if (is_system_tracing_) {
+ // Disable system tracing.
+ is_system_tracing_ = false;
+ ++pending_disable_recording_ack_count_;
+
+#if defined(OS_CHROMEOS)
+ chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->
+ RequestStopSystemTracing(
+ base::Bind(&TracingControllerImpl::OnEndSystemTracingAcked,
+ base::Unretained(this)));
+#elif defined(OS_WIN)
+ EtwSystemEventConsumer::GetInstance()->StopSystemTracing(
+ base::Bind(&TracingControllerImpl::OnEndSystemTracingAcked,
+ base::Unretained(this)));
+#endif
+ }
+#endif // defined(OS_CHROMEOS) || defined(OS_WIN)
- // Handle special case of zero child processes by immediately telling the
- // caller that tracing has ended. Use asynchronous OnDisableRecordingAcked
- // to avoid recursive call back to the caller.
+ // Handle special case of zero child processes by immediately flushing the
+ // trace log. Once the flush has completed the caller will be notified that
+ // tracing has ended.
if (pending_disable_recording_ack_count_ == 1) {
- // Ack asynchronously now, because we don't have any children to wait for.
- std::vector<std::string> category_groups;
- TraceLog::GetInstance()->GetKnownCategoryGroups(&category_groups);
- BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
- base::Bind(&TracingControllerImpl::OnDisableRecordingAcked,
- base::Unretained(this), category_groups));
+ // Flush asynchronously now, because we don't have any children to wait for.
+ TraceLog::GetInstance()->Flush(
+ base::Bind(&TracingControllerImpl::OnLocalTraceDataCollected,
+ base::Unretained(this)));
}
// Notify all child processes.
- for (TraceMessageFilterMap::iterator it = trace_message_filters_.begin();
+ for (TraceMessageFilterSet::iterator it = trace_message_filters_.begin();
it != trace_message_filters_.end(); ++it) {
it->get()->SendEndTracing();
}
- return true;
}
bool TracingControllerImpl::EnableMonitoring(
@@ -241,30 +377,46 @@ bool TracingControllerImpl::EnableMonitoring(
if (!can_enable_monitoring())
return false;
- is_monitoring_ = true;
+ OnMonitoringStateChanged(true);
#if defined(OS_ANDROID)
TraceLog::GetInstance()->AddClockSyncMetadataEvent();
#endif
- int monitoring_tracing_options = 0;
+ options_ = options;
+ int trace_options = 0;
if (options & ENABLE_SAMPLING)
- monitoring_tracing_options |= base::debug::TraceLog::MONITOR_SAMPLING;
+ trace_options |= TraceLog::ENABLE_SAMPLING;
- TraceLog::GetInstance()->SetEnabled(
- base::debug::CategoryFilter(category_filter),
- static_cast<TraceLog::Options>(monitoring_tracing_options));
+ base::Closure on_enable_monitoring_done_callback =
+ base::Bind(&TracingControllerImpl::OnEnableMonitoringDone,
+ base::Unretained(this),
+ category_filter, trace_options, callback);
+ BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
+ base::Bind(&TracingControllerImpl::SetEnabledOnFileThread,
+ base::Unretained(this),
+ category_filter,
+ base::debug::TraceLog::MONITORING_MODE,
+ trace_options,
+ on_enable_monitoring_done_callback));
+ return true;
+}
+
+void TracingControllerImpl::OnEnableMonitoringDone(
+ const std::string& category_filter,
+ int trace_options,
+ const EnableMonitoringDoneCallback& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// Notify all child processes.
- for (TraceMessageFilterMap::iterator it = trace_message_filters_.begin();
+ for (TraceMessageFilterSet::iterator it = trace_message_filters_.begin();
it != trace_message_filters_.end(); ++it) {
it->get()->SendEnableMonitoring(category_filter,
- static_cast<TraceLog::Options>(monitoring_tracing_options));
+ static_cast<TraceLog::Options>(trace_options));
}
if (!callback.is_null())
callback.Run();
- return true;
}
bool TracingControllerImpl::DisableMonitoring(
@@ -273,26 +425,42 @@ bool TracingControllerImpl::DisableMonitoring(
if (!can_disable_monitoring())
return false;
- is_monitoring_ = false;
- TraceLog::GetInstance()->SetDisabled();
+ options_ = TracingController::Options();
+ base::Closure on_disable_monitoring_done_callback =
+ base::Bind(&TracingControllerImpl::OnDisableMonitoringDone,
+ base::Unretained(this), callback);
+ BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
+ base::Bind(&TracingControllerImpl::SetDisabledOnFileThread,
+ base::Unretained(this),
+ on_disable_monitoring_done_callback));
+ return true;
+}
+
+void TracingControllerImpl::OnDisableMonitoringDone(
+ const DisableMonitoringDoneCallback& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ OnMonitoringStateChanged(false);
// Notify all child processes.
- for (TraceMessageFilterMap::iterator it = trace_message_filters_.begin();
+ for (TraceMessageFilterSet::iterator it = trace_message_filters_.begin();
it != trace_message_filters_.end(); ++it) {
it->get()->SendDisableMonitoring();
}
if (!callback.is_null())
callback.Run();
- return true;
}
void TracingControllerImpl::GetMonitoringStatus(
bool* out_enabled,
std::string* out_category_filter,
TracingController::Options* out_options) {
- NOTIMPLEMENTED();
+ *out_enabled = is_monitoring_;
+ *out_category_filter =
+ TraceLog::GetInstance()->GetCurrentCategoryFilter().ToString();
+ *out_options = options_;
}
bool TracingControllerImpl::CaptureMonitoringSnapshot(
@@ -313,20 +481,20 @@ bool TracingControllerImpl::CaptureMonitoringSnapshot(
// acked below.
pending_capture_monitoring_snapshot_ack_count_ =
trace_message_filters_.size() + 1;
+ pending_capture_monitoring_filters_ = trace_message_filters_;
- // Handle special case of zero child processes by immediately telling the
- // caller that capturing snapshot has ended. Use asynchronous
- // OnCaptureMonitoringSnapshotAcked to avoid recursive call back to the
- // caller.
+ // Handle special case of zero child processes by immediately flushing the
+ // trace log. Once the flush has completed the caller will be notified that
+ // the capture snapshot has ended.
if (pending_capture_monitoring_snapshot_ack_count_ == 1) {
- // Ack asynchronously now, because we don't have any children to wait for.
- BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
- base::Bind(&TracingControllerImpl::OnCaptureMonitoringSnapshotAcked,
+ // Flush asynchronously now, because we don't have any children to wait for.
+ TraceLog::GetInstance()->FlushButLeaveBufferIntact(
+ base::Bind(&TracingControllerImpl::OnLocalMonitoringTraceDataCollected,
base::Unretained(this)));
}
// Notify all child processes.
- for (TraceMessageFilterMap::iterator it = trace_message_filters_.begin();
+ for (TraceMessageFilterSet::iterator it = trace_message_filters_.begin();
it != trace_message_filters_.end(); ++it) {
it->get()->SendCaptureMonitoringSnapshot();
}
@@ -350,18 +518,20 @@ bool TracingControllerImpl::GetTraceBufferPercentFull(
// Count myself in pending_trace_buffer_percent_full_ack_count_, acked below.
pending_trace_buffer_percent_full_ack_count_ =
trace_message_filters_.size() + 1;
+ pending_trace_buffer_percent_full_filters_ = trace_message_filters_;
maximum_trace_buffer_percent_full_ = 0;
- // Handle special case of zero child processes.
- if (pending_trace_buffer_percent_full_ack_count_ == 1) {
- BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
- base::Bind(&TracingControllerImpl::OnTraceBufferPercentFullReply,
- base::Unretained(this),
- TraceLog::GetInstance()->GetBufferPercentFull()));
- }
+ // Call OnTraceBufferPercentFullReply unconditionally for the browser process.
+ // This will result in immediate execution of the callback if there are no
+ // child processes.
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(&TracingControllerImpl::OnTraceBufferPercentFullReply,
+ base::Unretained(this),
+ scoped_refptr<TraceMessageFilter>(),
+ TraceLog::GetInstance()->GetBufferPercentFull()));
// Notify all child processes.
- for (TraceMessageFilterMap::iterator it = trace_message_filters_.begin();
+ for (TraceMessageFilterSet::iterator it = trace_message_filters_.begin();
it != trace_message_filters_.end(); ++it) {
it->get()->SendGetTraceBufferPercentFull();
}
@@ -386,7 +556,7 @@ bool TracingControllerImpl::SetWatchEvent(
base::Bind(&TracingControllerImpl::OnWatchEventMatched,
base::Unretained(this)));
- for (TraceMessageFilterMap::iterator it = trace_message_filters_.begin();
+ for (TraceMessageFilterSet::iterator it = trace_message_filters_.begin();
it != trace_message_filters_.end(); ++it) {
it->get()->SendSetWatchEvent(category_name, event_name);
}
@@ -399,7 +569,7 @@ bool TracingControllerImpl::CancelWatchEvent() {
if (!can_cancel_watch_event())
return false;
- for (TraceMessageFilterMap::iterator it = trace_message_filters_.begin();
+ for (TraceMessageFilterSet::iterator it = trace_message_filters_.begin();
it != trace_message_filters_.end(); ++it) {
it->get()->SendCancelWatchEvent();
}
@@ -428,6 +598,11 @@ void TracingControllerImpl::AddTraceMessageFilter(
TraceLog::GetInstance()->GetCurrentCategoryFilter().ToString(),
TraceLog::GetInstance()->trace_options());
}
+ if (can_disable_monitoring()) {
+ trace_message_filter->SendEnableMonitoring(
+ TraceLog::GetInstance()->GetCurrentCategoryFilter().ToString(),
+ TraceLog::GetInstance()->trace_options());
+ }
}
void TracingControllerImpl::RemoveTraceMessageFilter(
@@ -440,15 +615,54 @@ void TracingControllerImpl::RemoveTraceMessageFilter(
return;
}
+ // If a filter is removed while a response from that filter is pending then
+ // simulate the response. Otherwise the response count will be wrong and the
+ // completion callback will never be executed.
+ if (pending_disable_recording_ack_count_ > 0) {
+ TraceMessageFilterSet::const_iterator it =
+ pending_disable_recording_filters_.find(trace_message_filter);
+ if (it != pending_disable_recording_filters_.end()) {
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(&TracingControllerImpl::OnDisableRecordingAcked,
+ base::Unretained(this),
+ make_scoped_refptr(trace_message_filter),
+ std::vector<std::string>()));
+ }
+ }
+ if (pending_capture_monitoring_snapshot_ack_count_ > 0) {
+ TraceMessageFilterSet::const_iterator it =
+ pending_capture_monitoring_filters_.find(trace_message_filter);
+ if (it != pending_capture_monitoring_filters_.end()) {
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(&TracingControllerImpl::OnCaptureMonitoringSnapshotAcked,
+ base::Unretained(this),
+ make_scoped_refptr(trace_message_filter)));
+ }
+ }
+ if (pending_trace_buffer_percent_full_ack_count_ > 0) {
+ TraceMessageFilterSet::const_iterator it =
+ pending_trace_buffer_percent_full_filters_.find(trace_message_filter);
+ if (it != pending_trace_buffer_percent_full_filters_.end()) {
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(&TracingControllerImpl::OnTraceBufferPercentFullReply,
+ base::Unretained(this),
+ make_scoped_refptr(trace_message_filter),
+ 0));
+ }
+ }
+
trace_message_filters_.erase(trace_message_filter);
}
void TracingControllerImpl::OnDisableRecordingAcked(
+ TraceMessageFilter* trace_message_filter,
const std::vector<std::string>& known_category_groups) {
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(&TracingControllerImpl::OnDisableRecordingAcked,
- base::Unretained(this), known_category_groups));
+ base::Unretained(this),
+ make_scoped_refptr(trace_message_filter),
+ known_category_groups));
return;
}
@@ -459,6 +673,12 @@ void TracingControllerImpl::OnDisableRecordingAcked(
if (pending_disable_recording_ack_count_ == 0)
return;
+ if (trace_message_filter &&
+ !pending_disable_recording_filters_.erase(trace_message_filter)) {
+ // The response from the specified message filter has already been received.
+ return;
+ }
+
if (--pending_disable_recording_ack_count_ == 1) {
// All acks from subprocesses have been received. Now flush the local trace.
// During or after this call, our OnLocalTraceDataCollected will be
@@ -472,6 +692,10 @@ void TracingControllerImpl::OnDisableRecordingAcked(
if (pending_disable_recording_ack_count_ != 0)
return;
+ OnDisableRecordingComplete();
+}
+
+void TracingControllerImpl::OnDisableRecordingComplete() {
// All acks (including from the subprocesses and the local trace) have been
// received.
is_recording_ = false;
@@ -500,17 +724,39 @@ void TracingControllerImpl::OnResultFileClosed() {
result_file_.reset();
}
-void TracingControllerImpl::OnCaptureMonitoringSnapshotAcked() {
+#if defined(OS_CHROMEOS) || defined(OS_WIN)
+void TracingControllerImpl::OnEndSystemTracingAcked(
+ const scoped_refptr<base::RefCountedString>& events_str_ptr) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ if (result_file_)
+ result_file_->WriteSystemTrace(events_str_ptr);
+
+ DCHECK(!is_system_tracing_);
+ std::vector<std::string> category_groups;
+ OnDisableRecordingAcked(NULL, category_groups);
+}
+#endif
+
+void TracingControllerImpl::OnCaptureMonitoringSnapshotAcked(
+ TraceMessageFilter* trace_message_filter) {
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(&TracingControllerImpl::OnCaptureMonitoringSnapshotAcked,
- base::Unretained(this)));
+ base::Unretained(this),
+ make_scoped_refptr(trace_message_filter)));
return;
}
if (pending_capture_monitoring_snapshot_ack_count_ == 0)
return;
+ if (trace_message_filter &&
+ !pending_capture_monitoring_filters_.erase(trace_message_filter)) {
+ // The response from the specified message filter has already been received.
+ return;
+ }
+
if (--pending_capture_monitoring_snapshot_ack_count_ == 1) {
// All acks from subprocesses have been received. Now flush the local trace.
// During or after this call, our OnLocalMonitoringTraceDataCollected
@@ -585,7 +831,7 @@ void TracingControllerImpl::OnLocalTraceDataCollected(
// Simulate an DisableRecordingAcked for the local trace.
std::vector<std::string> category_groups;
TraceLog::GetInstance()->GetKnownCategoryGroups(&category_groups);
- OnDisableRecordingAcked(category_groups);
+ OnDisableRecordingAcked(NULL, category_groups);
}
void TracingControllerImpl::OnLocalMonitoringTraceDataCollected(
@@ -598,20 +844,30 @@ void TracingControllerImpl::OnLocalMonitoringTraceDataCollected(
return;
// Simulate an CaptureMonitoringSnapshotAcked for the local trace.
- OnCaptureMonitoringSnapshotAcked();
+ OnCaptureMonitoringSnapshotAcked(NULL);
}
-void TracingControllerImpl::OnTraceBufferPercentFullReply(float percent_full) {
+void TracingControllerImpl::OnTraceBufferPercentFullReply(
+ TraceMessageFilter* trace_message_filter,
+ float percent_full) {
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(&TracingControllerImpl::OnTraceBufferPercentFullReply,
- base::Unretained(this), percent_full));
+ base::Unretained(this),
+ make_scoped_refptr(trace_message_filter),
+ percent_full));
return;
}
if (pending_trace_buffer_percent_full_ack_count_ == 0)
return;
+ if (trace_message_filter &&
+ !pending_trace_buffer_percent_full_filters_.erase(trace_message_filter)) {
+ // The response from the specified message filter has already been received.
+ return;
+ }
+
maximum_trace_buffer_percent_full_ =
std::max(maximum_trace_buffer_percent_full_, percent_full);
@@ -621,15 +877,6 @@ void TracingControllerImpl::OnTraceBufferPercentFullReply(float percent_full) {
maximum_trace_buffer_percent_full_);
pending_trace_buffer_percent_full_callback_.Reset();
}
-
- if (pending_trace_buffer_percent_full_ack_count_ == 1) {
- // The last ack represents local trace, so we need to ack it now. Note that
- // this code only executes if there were child processes.
- BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
- base::Bind(&TracingControllerImpl::OnTraceBufferPercentFullReply,
- base::Unretained(this),
- TraceLog::GetInstance()->GetBufferPercentFull()));
- }
}
void TracingControllerImpl::OnWatchEventMatched() {
@@ -644,4 +891,28 @@ void TracingControllerImpl::OnWatchEventMatched() {
watch_event_callback_.Run();
}
+void TracingControllerImpl::RegisterTracingUI(TracingUI* tracing_ui) {
+ DCHECK(tracing_uis_.find(tracing_ui) == tracing_uis_.end());
+ tracing_uis_.insert(tracing_ui);
+}
+
+void TracingControllerImpl::UnregisterTracingUI(TracingUI* tracing_ui) {
+ std::set<TracingUI*>::iterator it = tracing_uis_.find(tracing_ui);
+ DCHECK(it != tracing_uis_.end());
+ tracing_uis_.erase(it);
+}
+
+void TracingControllerImpl::OnMonitoringStateChanged(bool is_monitoring) {
+ if (is_monitoring_ == is_monitoring)
+ return;
+
+ is_monitoring_ = is_monitoring;
+#if !defined(OS_ANDROID)
+ for (std::set<TracingUI*>::iterator it = tracing_uis_.begin();
+ it != tracing_uis_.end(); it++) {
+ (*it)->OnMonitoringStateChanged(is_monitoring);
+ }
+#endif
+}
+
} // namespace content
diff --git a/chromium/content/browser/tracing/tracing_controller_impl.h b/chromium/content/browser/tracing/tracing_controller_impl.h
index ed3c8fd6519..d4d5722d85b 100644
--- a/chromium/content/browser/tracing/tracing_controller_impl.h
+++ b/chromium/content/browser/tracing/tracing_controller_impl.h
@@ -1,4 +1,4 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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.
@@ -20,6 +20,7 @@ class RefCountedString;
namespace content {
class TraceMessageFilter;
+class TracingUI;
class TracingControllerImpl : public TracingController {
public:
@@ -54,8 +55,11 @@ class TracingControllerImpl : public TracingController {
const WatchEventCallback& callback) OVERRIDE;
virtual bool CancelWatchEvent() OVERRIDE;
+ void RegisterTracingUI(TracingUI* tracing_ui);
+ void UnregisterTracingUI(TracingUI* tracing_ui);
+
private:
- typedef std::set<scoped_refptr<TraceMessageFilter> > TraceMessageFilterMap;
+ typedef std::set<scoped_refptr<TraceMessageFilter> > TraceMessageFilterSet;
class ResultFile;
friend struct base::DefaultLazyInstanceTraits<TracingControllerImpl>;
@@ -107,27 +111,62 @@ class TracingControllerImpl : public TracingController {
bool has_more_events);
void OnDisableRecordingAcked(
+ TraceMessageFilter* trace_message_filter,
const std::vector<std::string>& known_category_groups);
+ void OnDisableRecordingComplete();
void OnResultFileClosed();
- void OnCaptureMonitoringSnapshotAcked();
+#if defined(OS_CHROMEOS) || defined(OS_WIN)
+ void OnEndSystemTracingAcked(
+ const scoped_refptr<base::RefCountedString>& events_str_ptr);
+#endif
+
+ void OnCaptureMonitoringSnapshotAcked(
+ TraceMessageFilter* trace_message_filter);
void OnMonitoringSnapshotFileClosed();
- void OnTraceBufferPercentFullReply(float percent_full);
+ void OnTraceBufferPercentFullReply(
+ TraceMessageFilter* trace_message_filter,
+ float percent_full);
void OnWatchEventMatched();
- TraceMessageFilterMap trace_message_filters_;
+ void SetEnabledOnFileThread(const std::string& category_filter,
+ int mode,
+ int options,
+ const base::Closure& callback);
+ void SetDisabledOnFileThread(const base::Closure& callback);
+ void OnEnableRecordingDone(const std::string& category_filter,
+ int trace_options,
+ const EnableRecordingDoneCallback& callback);
+ void OnDisableRecordingDone(const base::FilePath& result_file_path,
+ const TracingFileResultCallback& callback);
+ void OnEnableMonitoringDone(const std::string& category_filter,
+ int trace_options,
+ const EnableMonitoringDoneCallback& callback);
+ void OnDisableMonitoringDone(const DisableMonitoringDoneCallback& callback);
+
+ void OnMonitoringStateChanged(bool is_monitoring);
+
+ TraceMessageFilterSet trace_message_filters_;
+
// Pending acks for DisableRecording.
int pending_disable_recording_ack_count_;
+ TraceMessageFilterSet pending_disable_recording_filters_;
// Pending acks for CaptureMonitoringSnapshot.
int pending_capture_monitoring_snapshot_ack_count_;
+ TraceMessageFilterSet pending_capture_monitoring_filters_;
// Pending acks for GetTraceBufferPercentFull.
int pending_trace_buffer_percent_full_ack_count_;
+ TraceMessageFilterSet pending_trace_buffer_percent_full_filters_;
float maximum_trace_buffer_percent_full_;
+#if defined(OS_CHROMEOS) || defined(OS_WIN)
+ bool is_system_tracing_;
+#endif
bool is_recording_;
bool is_monitoring_;
+ TracingController::Options options_;
GetCategoriesDoneCallback pending_get_categories_done_callback_;
TracingFileResultCallback pending_disable_recording_done_callback_;
@@ -139,6 +178,7 @@ class TracingControllerImpl : public TracingController {
WatchEventCallback watch_event_callback_;
std::set<std::string> known_category_groups_;
+ std::set<TracingUI*> tracing_uis_;
scoped_ptr<ResultFile> result_file_;
scoped_ptr<ResultFile> monitoring_snapshot_file_;
DISALLOW_COPY_AND_ASSIGN(TracingControllerImpl);
diff --git a/chromium/content/browser/tracing/tracing_resources.gyp b/chromium/content/browser/tracing/tracing_resources.gyp
index f6854b00d35..7ad5b9652cb 100644
--- a/chromium/content/browser/tracing/tracing_resources.gyp
+++ b/chromium/content/browser/tracing/tracing_resources.gyp
@@ -55,6 +55,7 @@
'variables': {
'grit_cmd': ['python', '../../../tools/grit/grit.py'],
'grit_grd_file': '<(SHARED_INTERMEDIATE_DIR)/content/browser/tracing/tracing_resources.grd',
+ 'grit_rc_header_format%': '',
},
'inputs': [
'<(grit_grd_file)',
@@ -63,16 +64,15 @@
'outputs': [
'<(grit_out_dir)/grit/tracing_resources.h',
'<(grit_out_dir)/tracing_resources.pak',
- '<(grit_out_dir)/tracing_resources.rc',
],
'action': ['<@(grit_cmd)',
'-i', '<(grit_grd_file)', 'build',
- '-f', 'GRIT_DIR/../gritsettings/resource_ids',
+ '-f', '<(DEPTH)/tools/gritsettings/resource_ids',
'-o', '<(grit_out_dir)',
'-D', 'SHARED_INTERMEDIATE_DIR=<(SHARED_INTERMEDIATE_DIR)',
- '<@(grit_defines)' ],
+ '<@(grit_defines)',
+ '<@(grit_rc_header_format)'],
'message': 'Generating resources from <(grit_grd_file)',
- 'msvs_cygwin_shell': 1,
}
],
'includes': [ '../../../build/grit_target.gypi' ]
diff --git a/chromium/content/browser/tracing/tracing_ui.cc b/chromium/content/browser/tracing/tracing_ui.cc
index e59f5667cb4..9f3175366eb 100644
--- a/chromium/content/browser/tracing/tracing_ui.cc
+++ b/chromium/content/browser/tracing/tracing_ui.cc
@@ -16,13 +16,14 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/values.h"
+#include "content/browser/tracing/grit/tracing_resources.h"
+#include "content/browser/tracing/tracing_controller_impl.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/tracing_controller.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h"
#include "content/public/common/url_constants.h"
-#include "grit/tracing_resources.h"
namespace content {
namespace {
@@ -41,10 +42,9 @@ void OnGotCategories(const WebUIDataSource::GotDataCallback& callback,
callback.Run(res);
}
-void OnRecordingEnabledAck(const WebUIDataSource::GotDataCallback& callback);
-
-bool OnBeginRecording(const std::string& data64,
- const WebUIDataSource::GotDataCallback& callback) {
+bool GetTracingOptions(const std::string& data64,
+ std::string* category_filter_string,
+ int* tracing_options) {
std::string data;
if (!base::Base64Decode(data64, &data)) {
LOG(ERROR) << "Options were not base64 encoded.";
@@ -62,13 +62,12 @@ bool OnBeginRecording(const std::string& data64,
return false;
}
- std::string category_filter_string;
bool use_system_tracing;
bool use_continuous_tracing;
bool use_sampling;
bool options_ok = true;
- options_ok &= options->GetString("categoryFilter", &category_filter_string);
+ options_ok &= options->GetString("categoryFilter", category_filter_string);
options_ok &= options->GetBoolean("useSystemTracing", &use_system_tracing);
options_ok &= options->GetBoolean("useContinuousTracing",
&use_continuous_tracing);
@@ -78,13 +77,24 @@ bool OnBeginRecording(const std::string& data64,
return false;
}
- int tracing_options = 0;
+ *tracing_options = 0;
if (use_system_tracing)
- tracing_options |= TracingController::ENABLE_SYSTRACE;
+ *tracing_options |= TracingController::ENABLE_SYSTRACE;
if (use_sampling)
- tracing_options |= TracingController::ENABLE_SAMPLING;
+ *tracing_options |= TracingController::ENABLE_SAMPLING;
if (use_continuous_tracing)
- tracing_options |= TracingController::RECORD_CONTINUOUSLY;
+ *tracing_options |= TracingController::RECORD_CONTINUOUSLY;
+ return true;
+}
+
+void OnRecordingEnabledAck(const WebUIDataSource::GotDataCallback& callback);
+
+bool BeginRecording(const std::string& data64,
+ const WebUIDataSource::GotDataCallback& callback) {
+ std::string category_filter_string;
+ int tracing_options = 0;
+ if (!GetTracingOptions(data64, &category_filter_string, &tracing_options))
+ return false;
return TracingController::GetInstance()->EnableRecording(
category_filter_string,
@@ -120,17 +130,87 @@ void BeginReadingRecordingResult(
base::Bind(ReadRecordingResult, callback, path));
}
-bool OnBeginRequest(const std::string& path,
- const WebUIDataSource::GotDataCallback& callback) {
+void OnMonitoringEnabledAck(const WebUIDataSource::GotDataCallback& callback);
+
+bool EnableMonitoring(const std::string& data64,
+ const WebUIDataSource::GotDataCallback& callback) {
+ std::string category_filter_string;
+ int tracing_options = 0;
+ if (!GetTracingOptions(data64, &category_filter_string, &tracing_options))
+ return false;
+
+ return TracingController::GetInstance()->EnableMonitoring(
+ category_filter_string,
+ static_cast<TracingController::Options>(tracing_options),
+ base::Bind(OnMonitoringEnabledAck, callback));
+}
+
+void OnMonitoringEnabledAck(const WebUIDataSource::GotDataCallback& callback) {
+ base::RefCountedString* res = new base::RefCountedString();
+ callback.Run(res);
+}
+
+void OnMonitoringDisabled(const WebUIDataSource::GotDataCallback& callback) {
+ base::RefCountedString* res = new base::RefCountedString();
+ callback.Run(res);
+}
+
+void GetMonitoringStatus(const WebUIDataSource::GotDataCallback& callback) {
+ bool is_monitoring;
+ std::string category_filter;
+ TracingController::Options options;
+ TracingController::GetInstance()->GetMonitoringStatus(
+ &is_monitoring, &category_filter, &options);
+
+ scoped_ptr<base::DictionaryValue>
+ monitoring_options(new base::DictionaryValue());
+ monitoring_options->SetBoolean("isMonitoring", is_monitoring);
+ monitoring_options->SetString("categoryFilter", category_filter);
+ monitoring_options->SetBoolean("useSystemTracing",
+ (options & TracingController::ENABLE_SYSTRACE) != 0);
+ monitoring_options->SetBoolean("useContinuousTracing",
+ (options & TracingController::RECORD_CONTINUOUSLY) != 0);
+ monitoring_options->SetBoolean("useSampling",
+ (options & TracingController::ENABLE_SAMPLING) != 0);
+
+ std::string monitoring_options_json;
+ base::JSONWriter::Write(monitoring_options.get(), &monitoring_options_json);
+
+ base::RefCountedString* monitoring_options_base64 =
+ new base::RefCountedString();
+ base::Base64Encode(monitoring_options_json,
+ &monitoring_options_base64->data());
+ callback.Run(monitoring_options_base64);
+}
+
+void ReadMonitoringSnapshot(const WebUIDataSource::GotDataCallback& callback,
+ const base::FilePath& path) {
+ std::string tmp;
+ if (!base::ReadFileToString(path, &tmp))
+ LOG(ERROR) << "Failed to read file " << path.value();
+ base::DeleteFile(path, false);
+ callback.Run(base::RefCountedString::TakeString(&tmp));
+}
+
+void OnMonitoringSnapshotCaptured(
+ const WebUIDataSource::GotDataCallback& callback,
+ const base::FilePath& path) {
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(ReadMonitoringSnapshot, callback, path));
+}
+
+bool OnBeginJSONRequest(const std::string& path,
+ const WebUIDataSource::GotDataCallback& callback) {
if (path == "json/categories") {
- TracingController::GetInstance()->GetCategories(
+ return TracingController::GetInstance()->GetCategories(
base::Bind(OnGotCategories, callback));
- return true;
}
+
const char* beginRecordingPath = "json/begin_recording?";
- if (path.find(beginRecordingPath) == 0) {
+ if (StartsWithASCII(path, beginRecordingPath, true)) {
std::string data = path.substr(strlen(beginRecordingPath));
- return OnBeginRecording(data, callback);
+ return BeginRecording(data, callback);
}
if (path == "json/get_buffer_percent_full") {
return TracingController::GetInstance()->GetTraceBufferPercentFull(
@@ -140,8 +220,39 @@ bool OnBeginRequest(const std::string& path,
return TracingController::GetInstance()->DisableRecording(
base::FilePath(), base::Bind(BeginReadingRecordingResult, callback));
}
- if (StartsWithASCII(path, "json/", true))
- LOG(ERROR) << "Unhandled request to " << path;
+
+ const char* enableMonitoringPath = "json/begin_monitoring?";
+ if (path.find(enableMonitoringPath) == 0) {
+ std::string data = path.substr(strlen(enableMonitoringPath));
+ return EnableMonitoring(data, callback);
+ }
+ if (path == "json/end_monitoring") {
+ return TracingController::GetInstance()->DisableMonitoring(
+ base::Bind(OnMonitoringDisabled, callback));
+ }
+ if (path == "json/capture_monitoring") {
+ TracingController::GetInstance()->CaptureMonitoringSnapshot(
+ base::FilePath(), base::Bind(OnMonitoringSnapshotCaptured, callback));
+ return true;
+ }
+ if (path == "json/get_monitoring_status") {
+ GetMonitoringStatus(callback);
+ return true;
+ }
+
+ LOG(ERROR) << "Unhandled request to " << path;
+ return false;
+}
+
+bool OnTracingRequest(const std::string& path,
+ const WebUIDataSource::GotDataCallback& callback) {
+ if (StartsWithASCII(path, "json/", true)) {
+ if (!OnBeginJSONRequest(path, callback)) {
+ std::string error("##ERROR##");
+ callback.Run(base::RefCountedString::TakeString(&error));
+ }
+ return true;
+ }
return false;
}
@@ -163,8 +274,18 @@ TracingUI::TracingUI(WebUI* web_ui) : WebUIController(web_ui) {
source->SetJsonPath("strings.js");
source->SetDefaultResource(IDR_TRACING_HTML);
source->AddResourcePath("tracing.js", IDR_TRACING_JS);
- source->SetRequestFilter(base::Bind(OnBeginRequest));
+ source->SetRequestFilter(base::Bind(OnTracingRequest));
WebUIDataSource::Add(browser_context, source);
+ TracingControllerImpl::GetInstance()->RegisterTracingUI(this);
+}
+
+TracingUI::~TracingUI() {
+ TracingControllerImpl::GetInstance()->UnregisterTracingUI(this);
+}
+
+void TracingUI::OnMonitoringStateChanged(bool is_monitoring) {
+ web_ui()->CallJavascriptFunction(
+ "onMonitoringStateChanged", base::FundamentalValue(is_monitoring));
}
} // namespace content
diff --git a/chromium/content/browser/tracing/tracing_ui.h b/chromium/content/browser/tracing/tracing_ui.h
index 85bbce15c2c..40ce0821e2d 100644
--- a/chromium/content/browser/tracing/tracing_ui.h
+++ b/chromium/content/browser/tracing/tracing_ui.h
@@ -10,9 +10,11 @@
namespace content {
// The C++ back-end for the chrome://tracing webui page.
-class TracingUI : public WebUIController {
+class CONTENT_EXPORT TracingUI : public WebUIController {
public:
explicit TracingUI(WebUI* web_ui);
+ virtual ~TracingUI();
+ void OnMonitoringStateChanged(bool is_monitoring);
private:
DISALLOW_COPY_AND_ASSIGN(TracingUI);
diff --git a/chromium/content/browser/user_metrics.cc b/chromium/content/browser/user_metrics.cc
index a1c7819a544..200c851ccb1 100644
--- a/chromium/content/browser/user_metrics.cc
+++ b/chromium/content/browser/user_metrics.cc
@@ -7,56 +7,33 @@
#include <vector>
#include "base/bind.h"
-#include "base/lazy_instance.h"
+#include "base/metrics/user_metrics.h"
#include "content/public/browser/browser_thread.h"
namespace content {
-namespace {
-base::LazyInstance<std::vector<ActionCallback> > g_action_callbacks =
- LAZY_INSTANCE_INITIALIZER;
-
-// Forward declare because of circular dependency.
-void CallRecordOnUI(const std::string& action);
-
-void Record(const char *action) {
+void RecordAction(const base::UserMetricsAction& action) {
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
- base::Bind(&CallRecordOnUI, action));
+ base::Bind(&RecordAction, action));
return;
}
- for (size_t i = 0; i < g_action_callbacks.Get().size(); i++)
- g_action_callbacks.Get()[i].Run(action);
-}
-
-void CallRecordOnUI(const std::string& action) {
- Record(action.c_str());
-}
-
-} // namespace
-
-void RecordAction(const UserMetricsAction& action) {
- Record(action.str_);
+ base::RecordAction(action);
}
void RecordComputedAction(const std::string& action) {
- Record(action.c_str());
-}
-
-void AddActionCallback(const ActionCallback& callback) {
- g_action_callbacks.Get().push_back(callback);
-}
-
-void RemoveActionCallback(const ActionCallback& callback) {
- for (size_t i = 0; i < g_action_callbacks.Get().size(); i++) {
- if (g_action_callbacks.Get()[i].Equals(callback)) {
- g_action_callbacks.Get().erase(g_action_callbacks.Get().begin() + i);
- return;
- }
+ if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&RecordComputedAction, action));
+ return;
}
+
+ base::RecordComputedAction(action);
}
} // namespace content
diff --git a/chromium/content/browser/utility_process_host_impl.cc b/chromium/content/browser/utility_process_host_impl.cc
index ed53fe82c61..ddc41a74131 100644
--- a/chromium/content/browser/utility_process_host_impl.cc
+++ b/chromium/content/browser/utility_process_host_impl.cc
@@ -23,34 +23,65 @@
#include "content/public/browser/utility_process_host_client.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/process_type.h"
+#include "content/public/common/sandboxed_process_launcher_delegate.h"
#include "ipc/ipc_switches.h"
#include "ui/base/ui_base_switches.h"
-#if defined(OS_WIN)
-#include "content/public/common/sandboxed_process_launcher_delegate.h"
-#endif
-
namespace content {
-#if defined(OS_WIN)
// NOTE: changes to this class need to be reviewed by the security team.
class UtilitySandboxedProcessLauncherDelegate
: public SandboxedProcessLauncherDelegate {
public:
- explicit UtilitySandboxedProcessLauncherDelegate(
- const base::FilePath& exposed_dir) : exposed_dir_(exposed_dir) {}
+ UtilitySandboxedProcessLauncherDelegate(const base::FilePath& exposed_dir,
+ bool launch_elevated, bool no_sandbox,
+ base::EnvironmentMap& env,
+ ChildProcessHost* host)
+ : exposed_dir_(exposed_dir),
+#if defined(OS_WIN)
+ launch_elevated_(launch_elevated)
+#elif defined(OS_POSIX)
+ env_(env),
+ no_sandbox_(no_sandbox),
+ ipc_fd_(host->TakeClientFileDescriptor())
+#endif // OS_WIN
+ {}
+
virtual ~UtilitySandboxedProcessLauncherDelegate() {}
+#if defined(OS_WIN)
+ virtual bool ShouldLaunchElevated() OVERRIDE {
+ return launch_elevated_;
+ }
virtual void PreSandbox(bool* disable_default_policy,
base::FilePath* exposed_dir) OVERRIDE {
*exposed_dir = exposed_dir_;
}
+#elif defined(OS_POSIX)
-private:
- base::FilePath exposed_dir_;
-};
-#endif
+ virtual bool ShouldUseZygote() OVERRIDE {
+ return !no_sandbox_ && exposed_dir_.empty();
+ }
+ virtual base::EnvironmentMap GetEnvironment() OVERRIDE {
+ return env_;
+ }
+ virtual int GetIpcFd() OVERRIDE {
+ return ipc_fd_;
+ }
+#endif // OS_WIN
+ private:
+
+ base::FilePath exposed_dir_;
+
+#if defined(OS_WIN)
+ bool launch_elevated_;
+#elif defined(OS_POSIX)
+ base::EnvironmentMap env_;
+ bool no_sandbox_;
+ int ipc_fd_;
+#endif // OS_WIN
+};
UtilityMainThreadFactoryFunction g_utility_main_thread_factory = NULL;
@@ -60,7 +91,7 @@ UtilityProcessHost* UtilityProcessHost::Create(
return new UtilityProcessHostImpl(client, client_task_runner);
}
-void UtilityProcessHost::RegisterUtilityMainThreadFactory(
+void UtilityProcessHostImpl::RegisterUtilityMainThreadFactory(
UtilityMainThreadFactoryFunction create) {
g_utility_main_thread_factory = create;
}
@@ -73,6 +104,7 @@ UtilityProcessHostImpl::UtilityProcessHostImpl(
is_batch_mode_(false),
is_mdns_enabled_(false),
no_sandbox_(false),
+ run_elevated_(false),
#if defined(OS_LINUX)
child_flags_(ChildProcessHost::CHILD_ALLOW_SELF),
#else
@@ -119,6 +151,13 @@ void UtilityProcessHostImpl::DisableSandbox() {
no_sandbox_ = true;
}
+#if defined(OS_WIN)
+void UtilityProcessHostImpl::ElevatePrivileges() {
+ no_sandbox_ = true;
+ run_elevated_ = true;
+}
+#endif
+
const ChildProcessData& UtilityProcessHostImpl::GetData() {
return process_->GetData();
}
@@ -142,7 +181,7 @@ bool UtilityProcessHostImpl::StartProcess() {
// Name must be set or metrics_service will crash in any test which
// launches a UtilityProcessHost.
process_.reset(new BrowserChildProcessHostImpl(PROCESS_TYPE_UTILITY, this));
- process_->SetName(ASCIIToUTF16("utility process"));
+ process_->SetName(base::ASCIIToUTF16("utility process"));
std::string channel_id = process_->GetHost()->CreateChannel();
if (channel_id.empty())
@@ -210,21 +249,17 @@ bool UtilityProcessHostImpl::StartProcess() {
if (is_mdns_enabled_)
cmd_line->AppendSwitch(switches::kUtilityProcessEnableMDns);
- bool use_zygote = false;
-
-#if defined(OS_LINUX)
- // The Linux sandbox does not support granting access to a single directory,
- // so we need to bypass the zygote in that case.
- use_zygote = !no_sandbox_ && exposed_dir_.empty();
+#if defined(OS_WIN)
+ // Let the utility process know if it is intended to be elevated.
+ if (run_elevated_)
+ cmd_line->AppendSwitch(switches::kUtilityProcessRunningElevated);
#endif
process_->Launch(
-#if defined(OS_WIN)
- new UtilitySandboxedProcessLauncherDelegate(exposed_dir_),
-#elif defined(OS_POSIX)
- use_zygote,
- env_,
-#endif
+ new UtilitySandboxedProcessLauncherDelegate(exposed_dir_,
+ run_elevated_,
+ no_sandbox_, env_,
+ process_->GetHost()),
cmd_line);
}
@@ -240,6 +275,13 @@ bool UtilityProcessHostImpl::OnMessageReceived(const IPC::Message& message) {
return true;
}
+void UtilityProcessHostImpl::OnProcessLaunchFailed() {
+ client_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&UtilityProcessHostClient::OnProcessLaunchFailed,
+ client_.get()));
+}
+
void UtilityProcessHostImpl::OnProcessCrashed(int exit_code) {
client_task_runner_->PostTask(
FROM_HERE,
diff --git a/chromium/content/browser/utility_process_host_impl.h b/chromium/content/browser/utility_process_host_impl.h
index 3083b229410..84866551a3b 100644
--- a/chromium/content/browser/utility_process_host_impl.h
+++ b/chromium/content/browser/utility_process_host_impl.h
@@ -25,10 +25,16 @@ class Thread;
namespace content {
class BrowserChildProcessHostImpl;
+typedef base::Thread* (*UtilityMainThreadFactoryFunction)(
+ const std::string& id);
+
class CONTENT_EXPORT UtilityProcessHostImpl
: public NON_EXPORTED_BASE(UtilityProcessHost),
public BrowserChildProcessHostDelegate {
public:
+ static void RegisterUtilityMainThreadFactory(
+ UtilityMainThreadFactoryFunction create);
+
UtilityProcessHostImpl(UtilityProcessHostClient* client,
base::SequencedTaskRunner* client_task_runner);
virtual ~UtilityProcessHostImpl();
@@ -40,6 +46,9 @@ class CONTENT_EXPORT UtilityProcessHostImpl
virtual void SetExposedDir(const base::FilePath& dir) OVERRIDE;
virtual void EnableMDns() OVERRIDE;
virtual void DisableSandbox() OVERRIDE;
+#if defined(OS_WIN)
+ virtual void ElevatePrivileges() OVERRIDE;
+#endif
virtual const ChildProcessData& GetData() OVERRIDE;
#if defined(OS_POSIX)
virtual void SetEnv(const base::EnvironmentMap& env) OVERRIDE;
@@ -54,6 +63,7 @@ class CONTENT_EXPORT UtilityProcessHostImpl
// BrowserChildProcessHost:
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+ virtual void OnProcessLaunchFailed() OVERRIDE;
virtual void OnProcessCrashed(int exit_code) OVERRIDE;
// A pointer to our client interface, who will be informed of progress.
@@ -72,6 +82,9 @@ class CONTENT_EXPORT UtilityProcessHostImpl
// Whether to pass switches::kNoSandbox to the child.
bool no_sandbox_;
+ // Whether to launch the process with elevated privileges.
+ bool run_elevated_;
+
// Flags defined in ChildProcessHost with which to start the process.
int child_flags_;
diff --git a/chromium/content/browser/vibration/vibration_message_filter.cc b/chromium/content/browser/vibration/vibration_message_filter.cc
index 9a6ed0cb2c0..fbc9041c71f 100644
--- a/chromium/content/browser/vibration/vibration_message_filter.cc
+++ b/chromium/content/browser/vibration/vibration_message_filter.cc
@@ -6,10 +6,10 @@
#include <algorithm>
-#include "base/safe_numerics.h"
+#include "base/numerics/safe_conversions.h"
#include "content/common/view_messages.h"
-#include "content/port/browser/vibration_provider.h"
#include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/vibration_provider.h"
#include "content/public/common/content_client.h"
#include "third_party/WebKit/public/platform/WebVibration.h"
@@ -18,7 +18,8 @@ namespace content {
// Minimum duration of a vibration is 1 millisecond.
const int64 kMinimumVibrationDurationMs = 1;
-VibrationMessageFilter::VibrationMessageFilter() {
+VibrationMessageFilter::VibrationMessageFilter()
+ : BrowserMessageFilter(ViewMsgStart) {
provider_.reset(GetContentClient()->browser()->OverrideVibrationProvider());
if (!provider_.get())
provider_.reset(CreateProvider());
@@ -28,16 +29,13 @@ VibrationMessageFilter::~VibrationMessageFilter() {
}
bool VibrationMessageFilter::OnMessageReceived(
- const IPC::Message& message,
- bool* message_was_ok) {
+ const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(VibrationMessageFilter,
- message,
- *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(VibrationMessageFilter, message)
IPC_MESSAGE_HANDLER(ViewHostMsg_Vibrate, OnVibrate)
IPC_MESSAGE_HANDLER(ViewHostMsg_CancelVibration, OnCancelVibration)
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return handled;
}
@@ -48,7 +46,7 @@ void VibrationMessageFilter::OnVibrate(int64 milliseconds) {
// Though the Blink implementation already sanitizes vibration times, don't
// trust any values passed from the renderer.
milliseconds = std::max(kMinimumVibrationDurationMs, std::min(milliseconds,
- base::checked_numeric_cast<int64>(blink::kVibrationDurationMax)));
+ base::checked_cast<int64>(blink::kVibrationDurationMax)));
provider_->Vibrate(milliseconds);
}
diff --git a/chromium/content/browser/vibration/vibration_message_filter.h b/chromium/content/browser/vibration/vibration_message_filter.h
index 086cc562aa8..1d603ee412a 100644
--- a/chromium/content/browser/vibration/vibration_message_filter.h
+++ b/chromium/content/browser/vibration/vibration_message_filter.h
@@ -19,8 +19,7 @@ class VibrationMessageFilter : public BrowserMessageFilter {
private:
virtual ~VibrationMessageFilter();
// BrowserMessageFilter implementation.
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
void OnVibrate(int64 milliseconds);
void OnCancelVibration();
diff --git a/chromium/content/browser/vibration/vibration_provider_android.cc b/chromium/content/browser/vibration/vibration_provider_android.cc
index ba62b159e81..4694f4ea6d0 100644
--- a/chromium/content/browser/vibration/vibration_provider_android.cc
+++ b/chromium/content/browser/vibration/vibration_provider_android.cc
@@ -6,7 +6,6 @@
#include <algorithm>
-#include "base/safe_numerics.h"
#include "content/browser/vibration/vibration_message_filter.h"
#include "content/common/view_messages.h"
#include "jni/VibrationProvider_jni.h"
diff --git a/chromium/content/browser/vibration/vibration_provider_android.h b/chromium/content/browser/vibration/vibration_provider_android.h
index 58ada973730..4d39c748fcd 100644
--- a/chromium/content/browser/vibration/vibration_provider_android.h
+++ b/chromium/content/browser/vibration/vibration_provider_android.h
@@ -6,7 +6,7 @@
#define CONTENT_BROWSER_VIBRATION_VIBRATION_PROVIDER_ANDROID_H_
#include "base/android/jni_android.h"
-#include "content/port/browser/vibration_provider.h"
+#include "content/public/browser/vibration_provider.h"
namespace content {
diff --git a/chromium/content/browser/web_contents/OWNERS b/chromium/content/browser/web_contents/OWNERS
index 4966076abb0..7454076fe49 100644
--- a/chromium/content/browser/web_contents/OWNERS
+++ b/chromium/content/browser/web_contents/OWNERS
@@ -1,2 +1,6 @@
# for *aura*
per-file *aura*=ben@chromium.org
+
+# for *android*
+per-file *android*=sievers@chromium.org
+per-file *android*=tedchoc@chromium.org
diff --git a/chromium/content/browser/web_contents/aura/gesture_nav_simple.cc b/chromium/content/browser/web_contents/aura/gesture_nav_simple.cc
new file mode 100644
index 00000000000..f904fe28cf1
--- /dev/null
+++ b/chromium/content/browser/web_contents/aura/gesture_nav_simple.cc
@@ -0,0 +1,232 @@
+// 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/web_contents/aura/gesture_nav_simple.h"
+
+#include "cc/layers/layer.h"
+#include "content/browser/frame_host/navigation_controller_impl.h"
+#include "content/browser/renderer_host/overscroll_controller.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/browser/web_contents/web_contents_view.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/overscroll_configuration.h"
+#include "content/public/common/content_client.h"
+#include "grit/ui_resources.h"
+#include "ui/aura/window.h"
+#include "ui/compositor/layer.h"
+#include "ui/compositor/layer_animation_observer.h"
+#include "ui/compositor/layer_delegate.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
+#include "ui/gfx/animation/tween.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/image/image.h"
+
+namespace content {
+
+namespace {
+
+const int kArrowHeight = 280;
+const int kArrowWidth = 140;
+const float kMinOpacity = 0.25f;
+
+bool ShouldNavigateForward(const NavigationController& controller,
+ OverscrollMode mode) {
+ return mode == (base::i18n::IsRTL() ? OVERSCROLL_EAST : OVERSCROLL_WEST) &&
+ controller.CanGoForward();
+}
+
+bool ShouldNavigateBack(const NavigationController& controller,
+ OverscrollMode mode) {
+ return mode == (base::i18n::IsRTL() ? OVERSCROLL_WEST : OVERSCROLL_EAST) &&
+ controller.CanGoBack();
+}
+
+// An animation observers that deletes itself and a pointer after the end of the
+// animation.
+template <class T>
+class DeleteAfterAnimation : public ui::ImplicitAnimationObserver {
+ public:
+ explicit DeleteAfterAnimation(scoped_ptr<T> object)
+ : object_(object.Pass()) {}
+
+ private:
+ friend class base::DeleteHelper<DeleteAfterAnimation<T> >;
+
+ virtual ~DeleteAfterAnimation() {}
+
+ // ui::ImplicitAnimationObserver:
+ virtual void OnImplicitAnimationsCompleted() OVERRIDE {
+ // Deleting an observer when a ScopedLayerAnimationSettings is iterating
+ // over them can cause a crash (which can happen during tests). So instead,
+ // schedule this observer to be deleted soon.
+ BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, this);
+ }
+
+ scoped_ptr<T> object_;
+ DISALLOW_COPY_AND_ASSIGN(DeleteAfterAnimation);
+};
+
+} // namespace
+
+// A layer delegate that paints the shield with the arrow in it.
+class ArrowLayerDelegate : public ui::LayerDelegate {
+ public:
+ explicit ArrowLayerDelegate(int resource_id)
+ : image_(GetContentClient()->GetNativeImageNamed(resource_id)),
+ left_arrow_(resource_id == IDR_BACK_ARROW) {
+ CHECK(!image_.IsEmpty());
+ }
+
+ virtual ~ArrowLayerDelegate() {}
+
+ bool left() const { return left_arrow_; }
+
+ private:
+ // ui::LayerDelegate:
+ virtual void OnPaintLayer(gfx::Canvas* canvas) OVERRIDE {
+ SkPaint paint;
+ paint.setColor(SkColorSetARGB(0xa0, 0, 0, 0));
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setAntiAlias(true);
+
+ canvas->DrawCircle(
+ gfx::Point(left_arrow_ ? 0 : kArrowWidth, kArrowHeight / 2),
+ kArrowWidth,
+ paint);
+ canvas->DrawImageInt(*image_.ToImageSkia(),
+ left_arrow_ ? 0 : kArrowWidth - image_.Width(),
+ (kArrowHeight - image_.Height()) / 2);
+ }
+
+ virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE {}
+
+ virtual base::Closure PrepareForLayerBoundsChange() OVERRIDE {
+ return base::Closure();
+ }
+
+ const gfx::Image& image_;
+ const bool left_arrow_;
+
+ DISALLOW_COPY_AND_ASSIGN(ArrowLayerDelegate);
+};
+
+GestureNavSimple::GestureNavSimple(WebContentsImpl* web_contents)
+ : web_contents_(web_contents),
+ completion_threshold_(0.f) {}
+
+GestureNavSimple::~GestureNavSimple() {}
+
+void GestureNavSimple::ApplyEffectsAndDestroy(const gfx::Transform& transform,
+ float opacity) {
+ ui::Layer* layer = arrow_.get();
+ ui::ScopedLayerAnimationSettings settings(arrow_->GetAnimator());
+ settings.AddObserver(
+ new DeleteAfterAnimation<ArrowLayerDelegate>(arrow_delegate_.Pass()));
+ settings.AddObserver(new DeleteAfterAnimation<ui::Layer>(arrow_.Pass()));
+ settings.AddObserver(new DeleteAfterAnimation<ui::Layer>(clip_layer_.Pass()));
+ layer->SetTransform(transform);
+ layer->SetOpacity(opacity);
+}
+
+void GestureNavSimple::AbortGestureAnimation() {
+ if (!arrow_)
+ return;
+ gfx::Transform transform;
+ transform.Translate(arrow_delegate_->left() ? -kArrowWidth : kArrowWidth, 0);
+ ApplyEffectsAndDestroy(transform, kMinOpacity);
+}
+
+void GestureNavSimple::CompleteGestureAnimation() {
+ if (!arrow_)
+ return;
+ // Make sure the fade-out starts from the complete state.
+ ApplyEffectsForDelta(completion_threshold_);
+ ApplyEffectsAndDestroy(arrow_->transform(), 0.f);
+}
+
+void GestureNavSimple::ApplyEffectsForDelta(float delta_x) {
+ if (!arrow_)
+ return;
+ CHECK_GT(completion_threshold_, 0.f);
+ CHECK_GE(delta_x, 0.f);
+ double complete = std::min(1.f, delta_x / completion_threshold_);
+ float translate_x = gfx::Tween::FloatValueBetween(complete, -kArrowWidth, 0);
+ gfx::Transform transform;
+ transform.Translate(arrow_delegate_->left() ? translate_x : -translate_x,
+ 0.f);
+ arrow_->SetTransform(transform);
+ arrow_->SetOpacity(gfx::Tween::FloatValueBetween(complete, kMinOpacity, 1.f));
+}
+
+gfx::Rect GestureNavSimple::GetVisibleBounds() const {
+ return web_contents_->GetNativeView()->bounds();
+}
+
+void GestureNavSimple::OnOverscrollUpdate(float delta_x, float delta_y) {
+ ApplyEffectsForDelta(std::abs(delta_x) + 50.f);
+}
+
+void GestureNavSimple::OnOverscrollComplete(OverscrollMode overscroll_mode) {
+ CompleteGestureAnimation();
+
+ NavigationControllerImpl& controller = web_contents_->GetController();
+ if (ShouldNavigateForward(controller, overscroll_mode))
+ controller.GoForward();
+ else if (ShouldNavigateBack(controller, overscroll_mode))
+ controller.GoBack();
+}
+
+void GestureNavSimple::OnOverscrollModeChange(OverscrollMode old_mode,
+ OverscrollMode new_mode) {
+ NavigationControllerImpl& controller = web_contents_->GetController();
+ if (!ShouldNavigateForward(controller, new_mode) &&
+ !ShouldNavigateBack(controller, new_mode)) {
+ AbortGestureAnimation();
+ return;
+ }
+
+ arrow_.reset(new ui::Layer(ui::LAYER_TEXTURED));
+ // Note that RTL doesn't affect the arrow that should be displayed.
+ int resource_id = 0;
+ if (new_mode == OVERSCROLL_WEST)
+ resource_id = IDR_FORWARD_ARROW;
+ else if (new_mode == OVERSCROLL_EAST)
+ resource_id = IDR_BACK_ARROW;
+ else
+ NOTREACHED();
+
+ arrow_delegate_.reset(new ArrowLayerDelegate(resource_id));
+ arrow_->set_delegate(arrow_delegate_.get());
+ arrow_->SetFillsBoundsOpaquely(false);
+
+ aura::Window* window = web_contents_->GetNativeView();
+ const gfx::Rect& window_bounds = window->bounds();
+ completion_threshold_ = window_bounds.width() *
+ GetOverscrollConfig(OVERSCROLL_CONFIG_HORIZ_THRESHOLD_COMPLETE);
+
+ // Align on the left or right edge.
+ int x = (resource_id == IDR_BACK_ARROW) ? 0 :
+ (window_bounds.width() - kArrowWidth);
+ // Align in the center vertically.
+ int y = std::max(0, (window_bounds.height() - kArrowHeight) / 2);
+ arrow_->SetBounds(gfx::Rect(x, y, kArrowWidth, kArrowHeight));
+ ApplyEffectsForDelta(0.f);
+
+ // Adding the arrow as a child of the content window is not sufficient,
+ // because it is possible for a new layer to be parented on top of the arrow
+ // layer (e.g. when the navigated-to page is displayed while the completion
+ // animation is in progress). So instead, a clip layer (that doesn't paint) is
+ // installed on top of the content window as its sibling, and the arrow layer
+ // is added to that clip layer.
+ clip_layer_.reset(new ui::Layer(ui::LAYER_NOT_DRAWN));
+ clip_layer_->SetBounds(window->layer()->bounds());
+ clip_layer_->SetMasksToBounds(true);
+ clip_layer_->Add(arrow_.get());
+
+ ui::Layer* parent = window->layer()->parent();
+ parent->Add(clip_layer_.get());
+ parent->StackAtTop(clip_layer_.get());
+}
+
+} // namespace content
diff --git a/chromium/content/browser/web_contents/aura/gesture_nav_simple.h b/chromium/content/browser/web_contents/aura/gesture_nav_simple.h
new file mode 100644
index 00000000000..e8466e5e6e2
--- /dev/null
+++ b/chromium/content/browser/web_contents/aura/gesture_nav_simple.h
@@ -0,0 +1,55 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_WEB_CONTENTS_AURA_GESTURE_NAV_SIMPLE_H_
+#define CONTENT_BROWSER_WEB_CONTENTS_AURA_GESTURE_NAV_SIMPLE_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "content/browser/renderer_host/overscroll_controller_delegate.h"
+
+namespace gfx {
+class Transform;
+}
+
+namespace ui {
+class Layer;
+}
+
+namespace content {
+
+class ArrowLayerDelegate;
+class WebContentsImpl;
+
+// A simple delegate for the overscroll controller that paints an arrow on top
+// of the web-contents as a hint for pending navigations from overscroll.
+class GestureNavSimple : public OverscrollControllerDelegate {
+ public:
+ explicit GestureNavSimple(WebContentsImpl* web_contents);
+ virtual ~GestureNavSimple();
+
+ private:
+ void ApplyEffectsAndDestroy(const gfx::Transform& transform, float opacity);
+ void AbortGestureAnimation();
+ void CompleteGestureAnimation();
+ void ApplyEffectsForDelta(float delta_x);
+
+ // OverscrollControllerDelegate:
+ virtual gfx::Rect GetVisibleBounds() const OVERRIDE;
+ virtual void OnOverscrollUpdate(float delta_x, float delta_y) OVERRIDE;
+ virtual void OnOverscrollComplete(OverscrollMode overscroll_mode) OVERRIDE;
+ virtual void OnOverscrollModeChange(OverscrollMode old_mode,
+ OverscrollMode new_mode) OVERRIDE;
+
+ WebContentsImpl* web_contents_;
+ scoped_ptr<ui::Layer> clip_layer_;
+ scoped_ptr<ui::Layer> arrow_;
+ scoped_ptr<ArrowLayerDelegate> arrow_delegate_;
+ float completion_threshold_;
+
+ DISALLOW_COPY_AND_ASSIGN(GestureNavSimple);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_WEB_CONTENTS_AURA_GESTURE_NAV_SIMPLE_H_
diff --git a/chromium/content/browser/web_contents/aura/image_window_delegate.cc b/chromium/content/browser/web_contents/aura/image_window_delegate.cc
index dbb066c894d..0b40c0f0905 100644
--- a/chromium/content/browser/web_contents/aura/image_window_delegate.cc
+++ b/chromium/content/browser/web_contents/aura/image_window_delegate.cc
@@ -77,10 +77,10 @@ void ImageWindowDelegate::OnPaint(gfx::Canvas* canvas) {
void ImageWindowDelegate::OnDeviceScaleFactorChanged(float scale_factor) {
}
-void ImageWindowDelegate::OnWindowDestroying() {
+void ImageWindowDelegate::OnWindowDestroying(aura::Window* window) {
}
-void ImageWindowDelegate::OnWindowDestroyed() {
+void ImageWindowDelegate::OnWindowDestroyed(aura::Window* window) {
delete this;
}
@@ -94,8 +94,4 @@ bool ImageWindowDelegate::HasHitTestMask() const {
void ImageWindowDelegate::GetHitTestMask(gfx::Path* mask) const {
}
-void ImageWindowDelegate::DidRecreateLayer(ui::Layer *old_layer,
- ui::Layer *new_layer) {
-}
-
} // namespace content
diff --git a/chromium/content/browser/web_contents/aura/image_window_delegate.h b/chromium/content/browser/web_contents/aura/image_window_delegate.h
index 561fac8a109..d0f9b061918 100644
--- a/chromium/content/browser/web_contents/aura/image_window_delegate.h
+++ b/chromium/content/browser/web_contents/aura/image_window_delegate.h
@@ -5,6 +5,7 @@
#ifndef CONTENT_BROWSER_WEB_CONTENTS_AURA_IMAGE_WINDOW_DELEGATE_H_
#define CONTENT_BROWSER_WEB_CONTENTS_AURA_IMAGE_WINDOW_DELEGATE_H_
+#include "content/common/content_export.h"
#include "ui/aura/window_delegate.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/size.h"
@@ -13,7 +14,7 @@ namespace content {
// An ImageWindowDelegate paints an image for a Window. The delegate destroys
// itself when the Window is destroyed. The delegate does not consume any event.
-class ImageWindowDelegate : public aura::WindowDelegate {
+class CONTENT_EXPORT ImageWindowDelegate : public aura::WindowDelegate {
public:
ImageWindowDelegate();
@@ -37,13 +38,11 @@ class ImageWindowDelegate : public aura::WindowDelegate {
virtual void OnCaptureLost() OVERRIDE;
virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE;
- virtual void OnWindowDestroying() OVERRIDE;
- virtual void OnWindowDestroyed() OVERRIDE;
+ virtual void OnWindowDestroying(aura::Window* window) OVERRIDE;
+ virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE;
virtual void OnWindowTargetVisibilityChanged(bool visible) OVERRIDE;
virtual bool HasHitTestMask() const OVERRIDE;
virtual void GetHitTestMask(gfx::Path* mask) const OVERRIDE;
- virtual void DidRecreateLayer(ui::Layer* old_layer,
- ui::Layer* new_layer) OVERRIDE;
protected:
gfx::Image image_;
diff --git a/chromium/content/browser/web_contents/aura/overscroll_navigation_overlay.cc b/chromium/content/browser/web_contents/aura/overscroll_navigation_overlay.cc
new file mode 100644
index 00000000000..d2b8b6e7726
--- /dev/null
+++ b/chromium/content/browser/web_contents/aura/overscroll_navigation_overlay.cc
@@ -0,0 +1,302 @@
+// 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/web_contents/aura/overscroll_navigation_overlay.h"
+
+#include "content/browser/frame_host/navigation_entry_impl.h"
+#include "content/browser/renderer_host/render_view_host_impl.h"
+#include "content/browser/web_contents/aura/image_window_delegate.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/common/view_messages.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_widget_host_view.h"
+#include "ui/aura/window.h"
+#include "ui/base/layout.h"
+#include "ui/compositor/layer.h"
+#include "ui/compositor/layer_animation_observer.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/image/image_png_rep.h"
+#include "ui/gfx/image/image_skia.h"
+
+namespace content {
+
+// A LayerDelegate that paints an image for the layer.
+class ImageLayerDelegate : public ui::LayerDelegate {
+ public:
+ ImageLayerDelegate() {}
+
+ virtual ~ImageLayerDelegate() {}
+
+ void SetImage(const gfx::Image& image) {
+ image_ = image;
+ image_size_ = image.AsImageSkia().size();
+ }
+ const gfx::Image& image() const { return image_; }
+
+ private:
+ // Overridden from ui::LayerDelegate:
+ virtual void OnPaintLayer(gfx::Canvas* canvas) OVERRIDE {
+ if (image_.IsEmpty()) {
+ canvas->DrawColor(SK_ColorGRAY);
+ } else {
+ SkISize size = canvas->sk_canvas()->getDeviceSize();
+ if (size.width() != image_size_.width() ||
+ size.height() != image_size_.height()) {
+ canvas->DrawColor(SK_ColorWHITE);
+ }
+ canvas->DrawImageInt(image_.AsImageSkia(), 0, 0);
+ }
+ }
+
+ // Called when the layer's device scale factor has changed.
+ virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE {
+ }
+
+ // Invoked prior to the bounds changing. The returned closured is run after
+ // the bounds change.
+ virtual base::Closure PrepareForLayerBoundsChange() OVERRIDE {
+ return base::Closure();
+ }
+
+ gfx::Image image_;
+ gfx::Size image_size_;
+
+ DISALLOW_COPY_AND_ASSIGN(ImageLayerDelegate);
+};
+
+// Responsible for fading out and deleting the layer of the overlay window.
+class OverlayDismissAnimator
+ : public ui::LayerAnimationObserver {
+ public:
+ // Takes ownership of the layer.
+ explicit OverlayDismissAnimator(scoped_ptr<ui::Layer> layer)
+ : layer_(layer.Pass()) {
+ CHECK(layer_.get());
+ }
+
+ // Starts the fadeout animation on the layer. When the animation finishes,
+ // the object deletes itself along with the layer.
+ void Animate() {
+ DCHECK(layer_.get());
+ ui::LayerAnimator* animator = layer_->GetAnimator();
+ // This makes SetOpacity() animate with default duration (which could be
+ // zero, e.g. when running tests).
+ ui::ScopedLayerAnimationSettings settings(animator);
+ animator->AddObserver(this);
+ layer_->SetOpacity(0);
+ }
+
+ // Overridden from ui::LayerAnimationObserver
+ virtual void OnLayerAnimationEnded(
+ ui::LayerAnimationSequence* sequence) OVERRIDE {
+ delete this;
+ }
+
+ virtual void OnLayerAnimationAborted(
+ ui::LayerAnimationSequence* sequence) OVERRIDE {
+ delete this;
+ }
+
+ virtual void OnLayerAnimationScheduled(
+ ui::LayerAnimationSequence* sequence) OVERRIDE {}
+
+ private:
+ virtual ~OverlayDismissAnimator() {}
+
+ scoped_ptr<ui::Layer> layer_;
+
+ DISALLOW_COPY_AND_ASSIGN(OverlayDismissAnimator);
+};
+
+OverscrollNavigationOverlay::OverscrollNavigationOverlay(
+ WebContentsImpl* web_contents)
+ : web_contents_(web_contents),
+ image_delegate_(NULL),
+ loading_complete_(false),
+ received_paint_update_(false),
+ pending_entry_id_(0),
+ slide_direction_(SLIDE_UNKNOWN) {
+}
+
+OverscrollNavigationOverlay::~OverscrollNavigationOverlay() {
+}
+
+void OverscrollNavigationOverlay::StartObserving() {
+ loading_complete_ = false;
+ received_paint_update_ = false;
+ overlay_dismiss_layer_.reset();
+ pending_entry_id_ = 0;
+ Observe(web_contents_);
+
+ // Make sure the overlay window is on top.
+ if (window_.get() && window_->parent())
+ window_->parent()->StackChildAtTop(window_.get());
+
+ // Assumes the navigation has been initiated.
+ NavigationEntry* pending_entry =
+ web_contents_->GetController().GetPendingEntry();
+ // Save id of the pending entry to identify when it loads and paints later.
+ // Under some circumstances navigation can leave a null pending entry -
+ // see comments in NavigationControllerImpl::NavigateToPendingEntry().
+ pending_entry_id_ = pending_entry ? pending_entry->GetUniqueID() : 0;
+}
+
+void OverscrollNavigationOverlay::SetOverlayWindow(
+ scoped_ptr<aura::Window> window,
+ ImageWindowDelegate* delegate) {
+ window_ = window.Pass();
+ if (window_.get() && window_->parent())
+ window_->parent()->StackChildAtTop(window_.get());
+ image_delegate_ = delegate;
+
+ if (window_.get() && delegate->has_image()) {
+ window_slider_.reset(new WindowSlider(this,
+ window_->parent(),
+ window_.get()));
+ slide_direction_ = SLIDE_UNKNOWN;
+ } else {
+ window_slider_.reset();
+ }
+}
+
+void OverscrollNavigationOverlay::StopObservingIfDone() {
+ // Normally we dismiss the overlay once we receive a paint update, however
+ // for in-page navigations DidFirstVisuallyNonEmptyPaint() does not get
+ // called, and we rely on loading_complete_ for those cases.
+ if (!received_paint_update_ && !loading_complete_)
+ return;
+
+ // If a slide is in progress, then do not destroy the window or the slide.
+ if (window_slider_.get() && window_slider_->IsSlideInProgress())
+ return;
+
+ // The layer to be animated by OverlayDismissAnimator
+ scoped_ptr<ui::Layer> overlay_dismiss_layer;
+ if (overlay_dismiss_layer_)
+ overlay_dismiss_layer = overlay_dismiss_layer_.Pass();
+ else if (window_.get())
+ overlay_dismiss_layer = window_->AcquireLayer();
+ Observe(NULL);
+ window_slider_.reset();
+ window_.reset();
+ image_delegate_ = NULL;
+ if (overlay_dismiss_layer.get()) {
+ // OverlayDismissAnimator deletes overlay_dismiss_layer and itself when the
+ // animation completes.
+ (new OverlayDismissAnimator(overlay_dismiss_layer.Pass()))->Animate();
+ }
+}
+
+ui::Layer* OverscrollNavigationOverlay::CreateSlideLayer(int offset) {
+ const NavigationControllerImpl& controller = web_contents_->GetController();
+ const NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
+ controller.GetEntryAtOffset(offset));
+
+ gfx::Image image;
+ if (entry && entry->screenshot().get()) {
+ std::vector<gfx::ImagePNGRep> image_reps;
+ image_reps.push_back(gfx::ImagePNGRep(entry->screenshot(),
+ ui::GetScaleFactorForNativeView(window_.get())));
+ image = gfx::Image(image_reps);
+ }
+ if (!layer_delegate_)
+ layer_delegate_.reset(new ImageLayerDelegate());
+ layer_delegate_->SetImage(image);
+
+ ui::Layer* layer = new ui::Layer(ui::LAYER_TEXTURED);
+ layer->set_delegate(layer_delegate_.get());
+ return layer;
+}
+
+ui::Layer* OverscrollNavigationOverlay::CreateBackLayer() {
+ if (!web_contents_->GetController().CanGoBack())
+ return NULL;
+ slide_direction_ = SLIDE_BACK;
+ return CreateSlideLayer(-1);
+}
+
+ui::Layer* OverscrollNavigationOverlay::CreateFrontLayer() {
+ if (!web_contents_->GetController().CanGoForward())
+ return NULL;
+ slide_direction_ = SLIDE_FRONT;
+ return CreateSlideLayer(1);
+}
+
+void OverscrollNavigationOverlay::OnWindowSlideCompleting() {
+ if (slide_direction_ == SLIDE_UNKNOWN)
+ return;
+
+ // Perform the navigation.
+ if (slide_direction_ == SLIDE_BACK)
+ web_contents_->GetController().GoBack();
+ else if (slide_direction_ == SLIDE_FRONT)
+ web_contents_->GetController().GoForward();
+ else
+ NOTREACHED();
+
+ // Reset state and wait for the new navigation page to complete
+ // loading/painting.
+ StartObserving();
+}
+
+void OverscrollNavigationOverlay::OnWindowSlideCompleted(
+ scoped_ptr<ui::Layer> layer) {
+ if (slide_direction_ == SLIDE_UNKNOWN) {
+ window_slider_.reset();
+ StopObservingIfDone();
+ return;
+ }
+
+ // Change the image used for the overlay window.
+ image_delegate_->SetImage(layer_delegate_->image());
+ window_->layer()->SetTransform(gfx::Transform());
+ window_->SchedulePaintInRect(gfx::Rect(window_->bounds().size()));
+ slide_direction_ = SLIDE_UNKNOWN;
+ // We may end up dismissing the overlay before it has a chance to repaint, so
+ // set the slider layer to be the one animated by OverlayDismissAnimator.
+ if (layer.get())
+ overlay_dismiss_layer_ = layer.Pass();
+ StopObservingIfDone();
+}
+
+void OverscrollNavigationOverlay::OnWindowSlideAborted() {
+ StopObservingIfDone();
+}
+
+void OverscrollNavigationOverlay::OnWindowSliderDestroyed() {
+ // We only want to take an action here if WindowSlider is being destroyed
+ // outside of OverscrollNavigationOverlay. If window_slider_.get() is NULL,
+ // then OverscrollNavigationOverlay is the one destroying WindowSlider, and
+ // we don't need to do anything.
+ // This check prevents StopObservingIfDone() being called multiple times
+ // (including recursively) for a single event.
+ if (window_slider_.get()) {
+ // The slider has just been destroyed. Release the ownership.
+ WindowSlider* slider ALLOW_UNUSED = window_slider_.release();
+ StopObservingIfDone();
+ }
+}
+
+void OverscrollNavigationOverlay::DidFirstVisuallyNonEmptyPaint() {
+ int visible_entry_id =
+ web_contents_->GetController().GetVisibleEntry()->GetUniqueID();
+ if (visible_entry_id == pending_entry_id_ || !pending_entry_id_) {
+ received_paint_update_ = true;
+ StopObservingIfDone();
+ }
+}
+
+void OverscrollNavigationOverlay::DidStopLoading(RenderViewHost* host) {
+ // Use the last committed entry rather than the active one, in case a
+ // pending entry has been created.
+ int committed_entry_id =
+ web_contents_->GetController().GetLastCommittedEntry()->GetUniqueID();
+ if (committed_entry_id == pending_entry_id_ || !pending_entry_id_) {
+ loading_complete_ = true;
+ StopObservingIfDone();
+ }
+}
+
+} // namespace content
diff --git a/chromium/content/browser/web_contents/aura/overscroll_navigation_overlay.h b/chromium/content/browser/web_contents/aura/overscroll_navigation_overlay.h
new file mode 100644
index 00000000000..e8e917136ca
--- /dev/null
+++ b/chromium/content/browser/web_contents/aura/overscroll_navigation_overlay.h
@@ -0,0 +1,128 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_WEB_CONTENTS_AURA_OVERSCROLL_NAVIGATION_OVERLAY_H_
+#define CONTENT_BROWSER_WEB_CONTENTS_AURA_OVERSCROLL_NAVIGATION_OVERLAY_H_
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "content/browser/web_contents/aura/window_slider.h"
+#include "content/common/content_export.h"
+#include "content/public/browser/web_contents_observer.h"
+
+struct ViewHostMsg_UpdateRect_Params;
+
+namespace content {
+
+class ImageLayerDelegate;
+class ImageWindowDelegate;
+class OverscrollNavigationOverlayTest;
+
+// When a history navigation is triggered at the end of an overscroll
+// navigation, it is necessary to show the history-screenshot until the page is
+// done navigating and painting. This class accomplishes this by showing the
+// screenshot window on top of the page until the page has completed loading and
+// painting.
+class CONTENT_EXPORT OverscrollNavigationOverlay
+ : public WebContentsObserver,
+ public WindowSlider::Delegate {
+ public:
+ explicit OverscrollNavigationOverlay(WebContentsImpl* web_contents);
+ virtual ~OverscrollNavigationOverlay();
+
+ bool has_window() const { return !!window_.get(); }
+
+ // Resets state and starts observing |web_contents_| for page load/paint
+ // updates. This function makes sure that the screenshot window is stacked
+ // on top, so that it hides the content window behind it, and destroys the
+ // screenshot window when the page is done loading/painting.
+ // This should be called immediately after initiating the navigation,
+ // otherwise the overlay may be dismissed prematurely.
+ void StartObserving();
+
+ // Sets the screenshot window and the delegate. This takes ownership of
+ // |window|.
+ // Note that ImageWindowDelegate manages its own lifetime, so this function
+ // does not take ownership of |delegate|.
+ void SetOverlayWindow(scoped_ptr<aura::Window> window,
+ ImageWindowDelegate* delegate);
+
+ private:
+ friend class OverscrollNavigationOverlayTest;
+ FRIEND_TEST_ALL_PREFIXES(OverscrollNavigationOverlayTest,
+ FirstVisuallyNonEmptyPaint_NoImage);
+ FRIEND_TEST_ALL_PREFIXES(OverscrollNavigationOverlayTest,
+ FirstVisuallyNonEmptyPaint_WithImage);
+ FRIEND_TEST_ALL_PREFIXES(OverscrollNavigationOverlayTest,
+ LoadUpdateWithoutNonEmptyPaint);
+ FRIEND_TEST_ALL_PREFIXES(OverscrollNavigationOverlayTest,
+ MultiNavigation_LoadingUpdate);
+ FRIEND_TEST_ALL_PREFIXES(OverscrollNavigationOverlayTest,
+ MultiNavigation_PaintUpdate);
+
+ enum SlideDirection {
+ SLIDE_UNKNOWN,
+ SLIDE_BACK,
+ SLIDE_FRONT
+ };
+
+ // Stop observing the page and start the final overlay fade-out animation if
+ // a window-slide isn't in progress and either the page has been painted or
+ // the page-load has completed.
+ void StopObservingIfDone();
+
+ // Creates a layer to be used for window-slide. |offset| is the offset of the
+ // NavigationEntry for the screenshot image to display.
+ ui::Layer* CreateSlideLayer(int offset);
+
+ // Overridden from WindowSlider::Delegate:
+ virtual ui::Layer* CreateBackLayer() OVERRIDE;
+ virtual ui::Layer* CreateFrontLayer() OVERRIDE;
+ virtual void OnWindowSlideCompleting() OVERRIDE;
+ virtual void OnWindowSlideCompleted(scoped_ptr<ui::Layer> layer) OVERRIDE;
+ virtual void OnWindowSlideAborted() OVERRIDE;
+ virtual void OnWindowSliderDestroyed() OVERRIDE;
+
+ // Overridden from WebContentsObserver:
+ virtual void DidFirstVisuallyNonEmptyPaint() OVERRIDE;
+ virtual void DidStopLoading(RenderViewHost* host) OVERRIDE;
+
+ // The WebContents which is being navigated.
+ WebContentsImpl* web_contents_;
+
+ // The screenshot overlay window.
+ scoped_ptr<aura::Window> window_;
+
+ // This is the WindowDelegate of |window_|. The delegate manages its own
+ // lifetime (destroys itself when |window_| is destroyed).
+ ImageWindowDelegate* image_delegate_;
+
+ bool loading_complete_;
+ bool received_paint_update_;
+
+ // Unique ID of the NavigationEntry we are navigating to. This is needed to
+ // filter on WebContentsObserver callbacks and is used to dismiss the overlay
+ // when the relevant page loads and paints.
+ int pending_entry_id_;
+
+ // The |WindowSlider| that allows sliding history layers while the page is
+ // being reloaded.
+ scoped_ptr<WindowSlider> window_slider_;
+
+ // Layer to be used for the final overlay fadeout animation when the overlay
+ // is being dismissed.
+ scoped_ptr<ui::Layer> overlay_dismiss_layer_;
+
+ // The direction of the in-progress slide (if any).
+ SlideDirection slide_direction_;
+
+ // The LayerDelegate used for the back/front layers during a slide.
+ scoped_ptr<ImageLayerDelegate> layer_delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(OverscrollNavigationOverlay);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_WEB_CONTENTS_AURA_OVERSCROLL_NAVIGATION_OVERLAY_H_
diff --git a/chromium/content/browser/web_contents/aura/overscroll_navigation_overlay_unittest.cc b/chromium/content/browser/web_contents/aura/overscroll_navigation_overlay_unittest.cc
new file mode 100644
index 00000000000..4ff49761095
--- /dev/null
+++ b/chromium/content/browser/web_contents/aura/overscroll_navigation_overlay_unittest.cc
@@ -0,0 +1,197 @@
+// 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/web_contents/aura/overscroll_navigation_overlay.h"
+
+#include "content/browser/frame_host/navigation_entry_impl.h"
+#include "content/browser/web_contents/aura/image_window_delegate.h"
+#include "content/browser/web_contents/web_contents_view.h"
+#include "content/common/view_messages.h"
+#include "content/public/test/mock_render_process_host.h"
+#include "content/test/test_render_view_host.h"
+#include "content/test/test_web_contents.h"
+#include "ui/aura/test/test_windows.h"
+#include "ui/aura/window.h"
+#include "ui/gfx/codec/png_codec.h"
+
+namespace content {
+
+class OverscrollNavigationOverlayTest : public RenderViewHostImplTestHarness {
+ public:
+ OverscrollNavigationOverlayTest() {}
+ virtual ~OverscrollNavigationOverlayTest() {}
+
+ gfx::Image CreateDummyScreenshot() {
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1, 1);
+ bitmap.allocPixels();
+ bitmap.eraseColor(SK_ColorWHITE);
+ return gfx::Image::CreateFrom1xBitmap(bitmap);
+ }
+
+ void SetDummyScreenshotOnNavEntry(NavigationEntry* entry) {
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1, 1);
+ bitmap.allocPixels();
+ bitmap.eraseColor(SK_ColorWHITE);
+ std::vector<unsigned char> png_data;
+ gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, true, &png_data);
+ scoped_refptr<base::RefCountedBytes> png_bytes =
+ base::RefCountedBytes::TakeVector(&png_data);
+ NavigationEntryImpl* entry_impl =
+ NavigationEntryImpl::FromNavigationEntry(entry);
+ entry_impl->SetScreenshotPNGData(png_bytes);
+ }
+
+ void ReceivePaintUpdate() {
+ ViewHostMsg_DidFirstVisuallyNonEmptyPaint msg(test_rvh()->GetRoutingID());
+ RenderViewHostTester::TestOnMessageReceived(test_rvh(), msg);
+ }
+
+ void PerformBackNavigationViaSliderCallbacks() {
+ // Sets slide direction to SLIDE_BACK, sets screenshot from NavEntry at
+ // offset -1 on layer_delegate_.
+ delete GetOverlay()->CreateBackLayer();
+ // Performs BACK navigation, sets image from layer_delegate_ on
+ // image_delegate_.
+ GetOverlay()->OnWindowSlideCompleting();
+ GetOverlay()->OnWindowSlideCompleted(scoped_ptr<ui::Layer>());
+ }
+
+ protected:
+ // RenderViewHostImplTestHarness:
+ virtual void SetUp() OVERRIDE {
+ RenderViewHostImplTestHarness::SetUp();
+
+ const GURL first("https://www.google.com");
+ contents()->NavigateAndCommit(first);
+ EXPECT_TRUE(controller().GetVisibleEntry());
+ EXPECT_FALSE(controller().CanGoBack());
+
+ const GURL second("http://www.chromium.org");
+ contents()->NavigateAndCommit(second);
+ EXPECT_TRUE(controller().CanGoBack());
+
+ // Receive a paint update. This is necessary to make sure the size is set
+ // correctly in RenderWidgetHostImpl.
+ ViewHostMsg_UpdateRect_Params params;
+ memset(&params, 0, sizeof(params));
+ params.view_size = gfx::Size(10, 10);
+ ViewHostMsg_UpdateRect rect(test_rvh()->GetRoutingID(), params);
+ RenderViewHostTester::TestOnMessageReceived(test_rvh(), rect);
+
+ // Reset pending flags for size/paint.
+ test_rvh()->ResetSizeAndRepaintPendingFlags();
+
+ // Create the overlay, and set the contents of the overlay window.
+ overlay_.reset(new OverscrollNavigationOverlay(contents()));
+ ImageWindowDelegate* image_delegate = new ImageWindowDelegate();
+ scoped_ptr<aura::Window> overlay_window(
+ aura::test::CreateTestWindowWithDelegate(
+ image_delegate,
+ 0,
+ gfx::Rect(root_window()->bounds().size()),
+ root_window()));
+
+ overlay_->SetOverlayWindow(overlay_window.Pass(), image_delegate);
+ overlay_->StartObserving();
+
+ EXPECT_TRUE(overlay_->web_contents());
+ EXPECT_FALSE(overlay_->loading_complete_);
+ EXPECT_FALSE(overlay_->received_paint_update_);
+ }
+
+ virtual void TearDown() OVERRIDE {
+ overlay_.reset();
+ RenderViewHostImplTestHarness::TearDown();
+ }
+
+ OverscrollNavigationOverlay* GetOverlay() {
+ return overlay_.get();
+ }
+
+ private:
+ scoped_ptr<OverscrollNavigationOverlay> overlay_;
+
+ DISALLOW_COPY_AND_ASSIGN(OverscrollNavigationOverlayTest);
+};
+
+TEST_F(OverscrollNavigationOverlayTest, FirstVisuallyNonEmptyPaint_NoImage) {
+ ReceivePaintUpdate();
+ EXPECT_TRUE(GetOverlay()->received_paint_update_);
+ EXPECT_FALSE(GetOverlay()->loading_complete_);
+ // The paint update will hide the overlay.
+ EXPECT_FALSE(GetOverlay()->web_contents());
+}
+
+TEST_F(OverscrollNavigationOverlayTest, FirstVisuallyNonEmptyPaint_WithImage) {
+ GetOverlay()->image_delegate_->SetImage(CreateDummyScreenshot());
+
+ ReceivePaintUpdate();
+ EXPECT_TRUE(GetOverlay()->received_paint_update_);
+ EXPECT_FALSE(GetOverlay()->loading_complete_);
+ // The paint update will hide the overlay.
+ EXPECT_FALSE(GetOverlay()->web_contents());
+}
+
+TEST_F(OverscrollNavigationOverlayTest, LoadUpdateWithoutNonEmptyPaint) {
+ GetOverlay()->image_delegate_->SetImage(CreateDummyScreenshot());
+ process()->sink().ClearMessages();
+
+ contents()->TestSetIsLoading(false);
+ EXPECT_TRUE(GetOverlay()->loading_complete_);
+ EXPECT_FALSE(GetOverlay()->received_paint_update_);
+ // The page load should hide the overlay.
+ EXPECT_FALSE(GetOverlay()->web_contents());
+}
+
+TEST_F(OverscrollNavigationOverlayTest, MultiNavigation_PaintUpdate) {
+ GetOverlay()->image_delegate_->SetImage(CreateDummyScreenshot());
+ SetDummyScreenshotOnNavEntry(controller().GetEntryAtOffset(-1));
+
+ PerformBackNavigationViaSliderCallbacks();
+ // Screenshot was set on NavEntry at offset -1.
+ EXPECT_TRUE(GetOverlay()->image_delegate_->has_image());
+ EXPECT_FALSE(GetOverlay()->received_paint_update_);
+
+ ReceivePaintUpdate();
+ // Paint updates until the navigation is committed represent updates
+ // for the previous page, so they shouldn't affect the flag.
+ EXPECT_FALSE(GetOverlay()->received_paint_update_);
+
+ contents()->CommitPendingNavigation();
+ ReceivePaintUpdate();
+ // Navigation was committed and the paint update was received - the flag
+ // should now be updated.
+ EXPECT_TRUE(GetOverlay()->received_paint_update_);
+
+ EXPECT_FALSE(GetOverlay()->web_contents());
+}
+
+TEST_F(OverscrollNavigationOverlayTest, MultiNavigation_LoadingUpdate) {
+ GetOverlay()->image_delegate_->SetImage(CreateDummyScreenshot());
+
+ PerformBackNavigationViaSliderCallbacks();
+ // No screenshot was set on NavEntry at offset -1.
+ EXPECT_FALSE(GetOverlay()->image_delegate_->has_image());
+ // Navigation was started, so the loading status flag should be reset.
+ EXPECT_FALSE(GetOverlay()->loading_complete_);
+
+ // Load updates until the navigation is committed represent updates for the
+ // previous page, so they shouldn't affect the flag.
+ contents()->TestSetIsLoading(true);
+ contents()->TestSetIsLoading(false);
+ EXPECT_FALSE(GetOverlay()->loading_complete_);
+
+ contents()->CommitPendingNavigation();
+ contents()->TestSetIsLoading(true);
+ contents()->TestSetIsLoading(false);
+ // Navigation was committed and the load update was received - the flag
+ // should now be updated.
+ EXPECT_TRUE(GetOverlay()->loading_complete_);
+
+ EXPECT_FALSE(GetOverlay()->web_contents());
+}
+
+} // namespace content
diff --git a/chromium/content/browser/web_contents/aura/shadow_layer_delegate.cc b/chromium/content/browser/web_contents/aura/shadow_layer_delegate.cc
index c8819072b6a..0d0fe934260 100644
--- a/chromium/content/browser/web_contents/aura/shadow_layer_delegate.cc
+++ b/chromium/content/browser/web_contents/aura/shadow_layer_delegate.cc
@@ -42,7 +42,7 @@ void ShadowLayerDelegate::OnPaintLayer(gfx::Canvas* canvas) {
skia::RefPtr<SkShader> shader = skia::AdoptRef(
SkGradientShader::CreateLinear(points, kShadowColors, NULL,
- arraysize(points), SkShader::kRepeat_TileMode, NULL));
+ arraysize(points), SkShader::kRepeat_TileMode));
gfx::Rect paint_rect = gfx::Rect(0, 0, kShadowThick,
layer_->bounds().height());
diff --git a/chromium/content/browser/web_contents/aura/window_slider.cc b/chromium/content/browser/web_contents/aura/window_slider.cc
index c9c3797b54c..8248d6c7318 100644
--- a/chromium/content/browser/web_contents/aura/window_slider.cc
+++ b/chromium/content/browser/web_contents/aura/window_slider.cc
@@ -19,12 +19,6 @@ namespace content {
namespace {
-void DeleteLayerAndShadow(ui::Layer* layer,
- ShadowLayerDelegate* shadow) {
- delete shadow;
- delete layer;
-}
-
// An animation observer that runs a callback at the end of the animation, and
// destroys itself.
class CallbackAnimationObserver : public ui::ImplicitAnimationObserver {
@@ -56,6 +50,7 @@ WindowSlider::WindowSlider(Delegate* delegate,
: delegate_(delegate),
event_window_(event_window),
owner_(owner),
+ active_animator_(NULL),
delta_x_(0.f),
weak_factory_(this),
active_start_threshold_(0.f),
@@ -92,8 +87,8 @@ void WindowSlider::ChangeOwner(aura::Window* new_owner) {
}
bool WindowSlider::IsSlideInProgress() const {
- return fabs(delta_x_) >= active_start_threshold_ || slider_.get() ||
- weak_factory_.HasWeakPtrs();
+ // if active_start_threshold_ is 0, it means that sliding hasn't been started
+ return active_start_threshold_ != 0 && (slider_.get() || active_animator_);
}
void WindowSlider::SetupSliderLayer() {
@@ -108,6 +103,16 @@ void WindowSlider::SetupSliderLayer() {
}
void WindowSlider::UpdateForScroll(float x_offset, float y_offset) {
+ if (active_animator_) {
+ // If there is an active animation, complete it before processing the scroll
+ // so that the callbacks that are invoked on the Delegate are consistent.
+ // Completing the animation may destroy WindowSlider through the animation
+ // callback, so we can't continue processing the scroll event here.
+ delta_x_ += x_offset;
+ CompleteActiveAnimations();
+ return;
+ }
+
float old_delta = delta_x_;
delta_x_ += x_offset;
if (fabs(delta_x_) < active_start_threshold_ && !slider_.get())
@@ -151,100 +156,95 @@ void WindowSlider::UpdateForScroll(float x_offset, float y_offset) {
translate_layer->SetTransform(transform);
}
-void WindowSlider::UpdateForFling(float x_velocity, float y_velocity) {
+void WindowSlider::CompleteOrResetSlide() {
if (!slider_.get())
return;
int width = owner_->bounds().width();
float ratio = (fabs(delta_x_) - active_start_threshold_) / width;
if (ratio < complete_threshold_) {
- ResetScroll();
+ ResetSlide();
return;
}
ui::Layer* sliding = delta_x_ < 0 ? slider_.get() : owner_->layer();
- ui::ScopedLayerAnimationSettings settings(sliding->GetAnimator());
+ active_animator_ = sliding->GetAnimator();
+
+ ui::ScopedLayerAnimationSettings settings(active_animator_);
settings.SetPreemptionStrategy(
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
settings.SetTweenType(gfx::Tween::EASE_OUT);
settings.AddObserver(new CallbackAnimationObserver(
- base::Bind(&WindowSlider::CompleteWindowSlideAfterAnimation,
- weak_factory_.GetWeakPtr())));
+ base::Bind(&WindowSlider::SlideAnimationCompleted,
+ weak_factory_.GetWeakPtr(),
+ base::Passed(&slider_),
+ base::Passed(&shadow_))));
gfx::Transform transform;
transform.Translate(delta_x_ < 0 ? 0 : width, 0);
+ delta_x_ = 0;
+ delegate_->OnWindowSlideCompleting();
sliding->SetTransform(transform);
}
-void WindowSlider::ResetScroll() {
+void WindowSlider::CompleteActiveAnimations() {
+ if (active_animator_)
+ active_animator_->StopAnimating();
+}
+
+void WindowSlider::ResetSlide() {
if (!slider_.get())
return;
- // Do not trigger any callbacks if this animation replaces any in-progress
- // animation.
- weak_factory_.InvalidateWeakPtrs();
-
// Reset the state of the sliding layer.
if (slider_.get()) {
- ui::Layer* layer = slider_.release();
- ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
- settings.SetPreemptionStrategy(
- ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
- settings.SetTweenType(gfx::Tween::EASE_OUT);
-
- // Delete the layer and the shadow at the end of the animation.
- settings.AddObserver(new CallbackAnimationObserver(
- base::Bind(&DeleteLayerAndShadow,
- base::Unretained(layer),
- base::Unretained(shadow_.release()))));
-
+ ui::Layer* translate_layer;
gfx::Transform transform;
- transform.Translate(delta_x_ < 0 ? layer->bounds().width() : 0, 0);
- layer->SetTransform(transform);
- }
-
- // Reset the state of the main layer.
- {
- ui::ScopedLayerAnimationSettings settings(owner_->layer()->GetAnimator());
+ if (delta_x_ < 0) {
+ translate_layer = slider_.get();
+ transform.Translate(translate_layer->bounds().width(), 0);
+ } else {
+ translate_layer = owner_->layer();
+ }
+
+ active_animator_ = translate_layer->GetAnimator();
+ ui::ScopedLayerAnimationSettings settings(active_animator_);
settings.SetPreemptionStrategy(
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
settings.SetTweenType(gfx::Tween::EASE_OUT);
settings.AddObserver(new CallbackAnimationObserver(
- base::Bind(&WindowSlider::AbortWindowSlideAfterAnimation,
- weak_factory_.GetWeakPtr())));
- owner_->layer()->SetTransform(gfx::Transform());
- owner_->layer()->SetLayerBrightness(0.f);
+ base::Bind(&WindowSlider::ResetSlideAnimationCompleted,
+ weak_factory_.GetWeakPtr(),
+ base::Passed(&slider_),
+ base::Passed(&shadow_))));
+ translate_layer->SetTransform(transform);
}
delta_x_ = 0.f;
}
-void WindowSlider::CancelScroll() {
- ResetScroll();
+void WindowSlider::SlideAnimationCompleted(
+ scoped_ptr<ui::Layer> layer, scoped_ptr<ShadowLayerDelegate> shadow) {
+ active_animator_ = NULL;
+ shadow.reset();
+ delegate_->OnWindowSlideCompleted(layer.Pass());
}
-void WindowSlider::CompleteWindowSlideAfterAnimation() {
- weak_factory_.InvalidateWeakPtrs();
- shadow_.reset();
- slider_.reset();
- delta_x_ = 0.f;
-
- delegate_->OnWindowSlideComplete();
-}
-
-void WindowSlider::AbortWindowSlideAfterAnimation() {
- weak_factory_.InvalidateWeakPtrs();
-
+void WindowSlider::ResetSlideAnimationCompleted(
+ scoped_ptr<ui::Layer> layer, scoped_ptr<ShadowLayerDelegate> shadow) {
+ active_animator_ = NULL;
+ shadow.reset();
+ layer.reset();
delegate_->OnWindowSlideAborted();
}
void WindowSlider::OnKeyEvent(ui::KeyEvent* event) {
- CancelScroll();
+ ResetSlide();
}
void WindowSlider::OnMouseEvent(ui::MouseEvent* event) {
if (!(event->flags() & ui::EF_IS_SYNTHESIZED))
- CancelScroll();
+ ResetSlide();
}
void WindowSlider::OnScrollEvent(ui::ScrollEvent* event) {
@@ -252,9 +252,9 @@ void WindowSlider::OnScrollEvent(ui::ScrollEvent* event) {
if (event->type() == ui::ET_SCROLL)
UpdateForScroll(event->x_offset_ordinal(), event->y_offset_ordinal());
else if (event->type() == ui::ET_SCROLL_FLING_START)
- UpdateForFling(event->x_offset_ordinal(), event->y_offset_ordinal());
+ CompleteOrResetSlide();
else
- CancelScroll();
+ ResetSlide();
event->SetHandled();
}
@@ -263,7 +263,7 @@ void WindowSlider::OnGestureEvent(ui::GestureEvent* event) {
const ui::GestureEventDetails& details = event->details();
switch (event->type()) {
case ui::ET_GESTURE_SCROLL_BEGIN:
- ResetScroll();
+ CompleteActiveAnimations();
break;
case ui::ET_GESTURE_SCROLL_UPDATE:
@@ -271,17 +271,17 @@ void WindowSlider::OnGestureEvent(ui::GestureEvent* event) {
break;
case ui::ET_GESTURE_SCROLL_END:
- UpdateForFling(0.f, 0.f);
+ CompleteOrResetSlide();
break;
case ui::ET_SCROLL_FLING_START:
- UpdateForFling(details.velocity_x(), details.velocity_y());
+ CompleteOrResetSlide();
break;
case ui::ET_GESTURE_PINCH_BEGIN:
case ui::ET_GESTURE_PINCH_UPDATE:
case ui::ET_GESTURE_PINCH_END:
- CancelScroll();
+ ResetSlide();
break;
default:
@@ -291,7 +291,8 @@ void WindowSlider::OnGestureEvent(ui::GestureEvent* event) {
event->SetHandled();
}
-void WindowSlider::OnWindowRemovingFromRootWindow(aura::Window* window) {
+void WindowSlider::OnWindowRemovingFromRootWindow(aura::Window* window,
+ aura::Window* new_root) {
if (window == event_window_) {
window->RemoveObserver(this);
window->RemovePreTargetHandler(this);
diff --git a/chromium/content/browser/web_contents/aura/window_slider.h b/chromium/content/browser/web_contents/aura/window_slider.h
index 7100bf5c475..83e56de330a 100644
--- a/chromium/content/browser/web_contents/aura/window_slider.h
+++ b/chromium/content/browser/web_contents/aura/window_slider.h
@@ -10,6 +10,7 @@
#include "base/memory/weak_ptr.h"
#include "content/common/content_export.h"
#include "ui/aura/window_observer.h"
+#include "ui/compositor/layer_animator.h"
#include "ui/events/event_handler.h"
namespace ui {
@@ -24,29 +25,38 @@ class ShadowLayerDelegate;
class CONTENT_EXPORT WindowSlider : public ui::EventHandler,
public aura::WindowObserver {
public:
- class Delegate {
+ class CONTENT_EXPORT Delegate {
public:
virtual ~Delegate() {}
- // Creates a layer to show in the background, as the window-layer slides
- // with the scroll gesture.
+ // Creates a layer to show behind the window-layer. Called when the
+ // window-layer starts sliding out to reveal the layer underneath.
// The WindowSlider takes ownership of the created layer.
virtual ui::Layer* CreateBackLayer() = 0;
- // Creates a layer to slide on top of the window-layer with the scroll
- // gesture.
+ // Creates a layer to show on top of the window-layer. Called when the new
+ // layer needs to start sliding in on top of the window-layer.
// The WindowSlider takes ownership of the created layer.
virtual ui::Layer* CreateFrontLayer() = 0;
- // Called when the slide is complete. Note that at the end of a completed
- // slide, the window-layer may have been transformed. The callback here
- // should reset the transform if necessary.
- virtual void OnWindowSlideComplete() = 0;
-
// Called when the slide is aborted. Note that when the slide is aborted,
// the WindowSlider resets any transform it applied on the window-layer.
virtual void OnWindowSlideAborted() = 0;
+ // Called when the slide is about to be complete. The delegate can take
+ // action with the assumption that slide will complete soon (within the
+ // duration of the final transition animation effect).
+ // This callback is always preceeded by CreateBackLayer() or by
+ // CreateFrontLayer() callback, and is guaranteed to be followed by the
+ // OnWindowSlideCompleted() callback.
+ virtual void OnWindowSlideCompleting() = 0;
+
+ // Called when the window slide completes. Note that at the end the
+ // window-layer may have been transformed. The callback here should reset
+ // the transform if necessary. |layer| is the slider's layer (previously
+ // created via CreateBackLayer() or CreateFrontLayer()).
+ virtual void OnWindowSlideCompleted(scoped_ptr<ui::Layer> layer) = 0;
+
// Called when the slider is destroyed.
virtual void OnWindowSliderDestroyed() = 0;
};
@@ -74,18 +84,25 @@ class CONTENT_EXPORT WindowSlider : public ui::EventHandler,
void UpdateForScroll(float x_offset, float y_offset);
- void UpdateForFling(float x_velocity, float y_velocity);
+ // Completes or resets the slide depending on whether the sliding layer
+ // passed the "complete slide threshold".
+ void CompleteOrResetSlide();
- // Resets any in-progress slide.
- void ResetScroll();
+ // Stops all slider-owned animations, progressing them to their end-points.
+ // Note that depending on the sate of the Delegate and the WindowSlider, this
+ // may destroy the WindowSlider through animation callbacks.
+ void CompleteActiveAnimations();
- // Cancels any scroll/animation in progress.
- void CancelScroll();
+ // Resets in-progress slide if any, and starts the animation of the slidden
+ // window to its original position.
+ void ResetSlide();
// The following callbacks are triggered after an animation.
- void CompleteWindowSlideAfterAnimation();
+ void SlideAnimationCompleted(scoped_ptr<ui::Layer> layer,
+ scoped_ptr<ShadowLayerDelegate> shadow);
- void AbortWindowSlideAfterAnimation();
+ void ResetSlideAnimationCompleted(scoped_ptr<ui::Layer> layer,
+ scoped_ptr<ShadowLayerDelegate> shadow);
// Overridden from ui::EventHandler:
virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE;
@@ -94,7 +111,8 @@ class CONTENT_EXPORT WindowSlider : public ui::EventHandler,
virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE;
// Overridden from aura::WindowObserver:
- virtual void OnWindowRemovingFromRootWindow(aura::Window* window) OVERRIDE;
+ virtual void OnWindowRemovingFromRootWindow(aura::Window* window,
+ aura::Window* new_root) OVERRIDE;
Delegate* delegate_;
@@ -109,6 +127,10 @@ class CONTENT_EXPORT WindowSlider : public ui::EventHandler,
// destroy |owner_|.
aura::Window* owner_;
+ // Set to the Animator of the currently active animation. If no animation is
+ // active, this is set to NULL.
+ ui::LayerAnimator* active_animator_;
+
// The accumulated amount of horizontal scroll.
float delta_x_;
diff --git a/chromium/content/browser/web_contents/aura/window_slider_unittest.cc b/chromium/content/browser/web_contents/aura/window_slider_unittest.cc
index d56d1e77e01..4a2d0a56001 100644
--- a/chromium/content/browser/web_contents/aura/window_slider_unittest.cc
+++ b/chromium/content/browser/web_contents/aura/window_slider_unittest.cc
@@ -5,28 +5,30 @@
#include "content/browser/web_contents/aura/window_slider.h"
#include "base/bind.h"
+#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/aura/root_window.h"
#include "ui/aura/test/aura_test_base.h"
#include "ui/aura/test/event_generator.h"
#include "ui/aura/test/test_window_delegate.h"
#include "ui/aura/window.h"
#include "ui/base/hit_test.h"
+#include "ui/compositor/scoped_animation_duration_scale_mode.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
+#include "ui/compositor/test/layer_animator_test_controller.h"
+#include "ui/events/event_processor.h"
+#include "ui/events/event_utils.h"
+#include "ui/gfx/frame_time.h"
namespace content {
-void DispatchEventDuringScrollCallback(aura::WindowEventDispatcher* dispatcher,
+void DispatchEventDuringScrollCallback(ui::EventProcessor* dispatcher,
ui::Event* event,
ui::EventType type,
const gfx::Vector2dF& delta) {
if (type != ui::ET_GESTURE_SCROLL_UPDATE)
return;
- aura::RootWindowHostDelegate* delegate =
- dispatcher->AsRootWindowHostDelegate();
- if (event->IsMouseEvent())
- delegate->OnHostMouseEvent(static_cast<ui::MouseEvent*>(event));
- else if (event->IsKeyEvent())
- delegate->OnHostKeyEvent(static_cast<ui::KeyEvent*>(event));
+ ui::EventDispatchDetails details = dispatcher->OnEventFromSource(event);
+ CHECK(!details.dispatcher_destroyed);
}
void ChangeSliderOwnerDuringScrollCallback(scoped_ptr<aura::Window>* window,
@@ -36,13 +38,35 @@ void ChangeSliderOwnerDuringScrollCallback(scoped_ptr<aura::Window>* window,
if (type != ui::ET_GESTURE_SCROLL_UPDATE)
return;
aura::Window* new_window = new aura::Window(NULL);
- new_window->Init(ui::LAYER_TEXTURED);
+ new_window->Init(aura::WINDOW_LAYER_TEXTURED);
new_window->Show();
slider->ChangeOwner(new_window);
(*window)->parent()->AddChild(new_window);
window->reset(new_window);
}
+void ConfirmSlideDuringScrollCallback(WindowSlider* slider,
+ ui::EventType type,
+ const gfx::Vector2dF& delta) {
+ static float total_delta_x = 0;
+ if (type == ui::ET_GESTURE_SCROLL_BEGIN)
+ total_delta_x = 0;
+
+ if (type == ui::ET_GESTURE_SCROLL_UPDATE) {
+ total_delta_x += delta.x();
+ if (total_delta_x >= 70)
+ EXPECT_TRUE(slider->IsSlideInProgress());
+ } else {
+ EXPECT_FALSE(slider->IsSlideInProgress());
+ }
+}
+
+void ConfirmNoSlideDuringScrollCallback(WindowSlider* slider,
+ ui::EventType type,
+ const gfx::Vector2dF& delta) {
+ EXPECT_FALSE(slider->IsSlideInProgress());
+}
+
// The window delegate does not receive any events.
class NoEventWindowDelegate : public aura::test::TestWindowDelegate {
public:
@@ -63,16 +87,21 @@ class WindowSliderDelegateTest : public WindowSlider::Delegate {
: can_create_layer_(true),
created_back_layer_(false),
created_front_layer_(false),
+ slide_completing_(false),
slide_completed_(false),
slide_aborted_(false),
slider_destroyed_(false) {
}
- virtual ~WindowSliderDelegateTest() {}
+ virtual ~WindowSliderDelegateTest() {
+ // Make sure slide_completed() gets called if slide_completing() was called.
+ CHECK(!slide_completing_ || slide_completed_);
+ }
void Reset() {
can_create_layer_ = true;
created_back_layer_ = false;
created_front_layer_ = false;
+ slide_completing_ = false;
slide_completed_ = false;
slide_aborted_ = false;
slider_destroyed_ = false;
@@ -84,6 +113,7 @@ class WindowSliderDelegateTest : public WindowSlider::Delegate {
bool created_back_layer() const { return created_back_layer_; }
bool created_front_layer() const { return created_front_layer_; }
+ bool slide_completing() const { return slide_completing_; }
bool slide_completed() const { return slide_completed_; }
bool slide_aborted() const { return slide_aborted_; }
bool slider_destroyed() const { return slider_destroyed_; }
@@ -111,10 +141,14 @@ class WindowSliderDelegateTest : public WindowSlider::Delegate {
return CreateLayerForTest();
}
- virtual void OnWindowSlideComplete() OVERRIDE {
+ virtual void OnWindowSlideCompleted(scoped_ptr<ui::Layer> layer) OVERRIDE {
slide_completed_ = true;
}
+ virtual void OnWindowSlideCompleting() OVERRIDE {
+ slide_completing_ = true;
+ }
+
virtual void OnWindowSlideAborted() OVERRIDE {
slide_aborted_ = true;
}
@@ -127,6 +161,7 @@ class WindowSliderDelegateTest : public WindowSlider::Delegate {
bool can_create_layer_;
bool created_back_layer_;
bool created_front_layer_;
+ bool slide_completing_;
bool slide_completed_;
bool slide_aborted_;
bool slider_destroyed_;
@@ -163,8 +198,8 @@ class WindowSliderDeleteOwnerOnComplete : public WindowSliderDelegateTest {
private:
// Overridden from WindowSlider::Delegate:
- virtual void OnWindowSlideComplete() OVERRIDE {
- WindowSliderDelegateTest::OnWindowSlideComplete();
+ virtual void OnWindowSlideCompleted(scoped_ptr<ui::Layer> layer) OVERRIDE {
+ WindowSliderDelegateTest::OnWindowSlideCompleted(layer.Pass());
delete owner_;
}
@@ -184,11 +219,14 @@ TEST_F(WindowSliderTest, WindowSlideUsingGesture) {
// Generate a horizontal overscroll.
WindowSlider* slider =
new WindowSlider(&slider_delegate, root_window(), window.get());
- generator.GestureScrollSequence(gfx::Point(10, 10),
- gfx::Point(160, 10),
- base::TimeDelta::FromMilliseconds(10),
- 10);
+ generator.GestureScrollSequenceWithCallback(
+ gfx::Point(10, 10),
+ gfx::Point(180, 10),
+ base::TimeDelta::FromMilliseconds(10),
+ 10,
+ base::Bind(&ConfirmSlideDuringScrollCallback, slider));
EXPECT_TRUE(slider_delegate.created_back_layer());
+ EXPECT_TRUE(slider_delegate.slide_completing());
EXPECT_TRUE(slider_delegate.slide_completed());
EXPECT_FALSE(slider_delegate.created_front_layer());
EXPECT_FALSE(slider_delegate.slide_aborted());
@@ -197,12 +235,15 @@ TEST_F(WindowSliderTest, WindowSlideUsingGesture) {
slider_delegate.Reset();
window->SetTransform(gfx::Transform());
- // Generat a horizontal overscroll in the reverse direction.
- generator.GestureScrollSequence(gfx::Point(160, 10),
- gfx::Point(10, 10),
- base::TimeDelta::FromMilliseconds(10),
- 10);
+ // Generate a horizontal overscroll in the reverse direction.
+ generator.GestureScrollSequenceWithCallback(
+ gfx::Point(180, 10),
+ gfx::Point(10, 10),
+ base::TimeDelta::FromMilliseconds(10),
+ 10,
+ base::Bind(&ConfirmSlideDuringScrollCallback, slider));
EXPECT_TRUE(slider_delegate.created_front_layer());
+ EXPECT_TRUE(slider_delegate.slide_completing());
EXPECT_TRUE(slider_delegate.slide_completed());
EXPECT_FALSE(slider_delegate.created_back_layer());
EXPECT_FALSE(slider_delegate.slide_aborted());
@@ -211,11 +252,14 @@ TEST_F(WindowSliderTest, WindowSlideUsingGesture) {
slider_delegate.Reset();
// Generate a vertical overscroll.
- generator.GestureScrollSequence(gfx::Point(10, 10),
- gfx::Point(10, 80),
- base::TimeDelta::FromMilliseconds(10),
- 10);
+ generator.GestureScrollSequenceWithCallback(
+ gfx::Point(10, 10),
+ gfx::Point(10, 80),
+ base::TimeDelta::FromMilliseconds(10),
+ 10,
+ base::Bind(&ConfirmNoSlideDuringScrollCallback, slider));
EXPECT_FALSE(slider_delegate.created_back_layer());
+ EXPECT_FALSE(slider_delegate.slide_completing());
EXPECT_FALSE(slider_delegate.slide_completed());
EXPECT_FALSE(slider_delegate.created_front_layer());
EXPECT_FALSE(slider_delegate.slide_aborted());
@@ -224,13 +268,16 @@ TEST_F(WindowSliderTest, WindowSlideUsingGesture) {
// Generate a horizontal scroll that starts overscroll, but doesn't scroll
// enough to complete it.
- generator.GestureScrollSequence(gfx::Point(10, 10),
- gfx::Point(60, 10),
- base::TimeDelta::FromMilliseconds(10),
- 10);
+ generator.GestureScrollSequenceWithCallback(
+ gfx::Point(10, 10),
+ gfx::Point(80, 10),
+ base::TimeDelta::FromMilliseconds(10),
+ 10,
+ base::Bind(&ConfirmSlideDuringScrollCallback, slider));
EXPECT_TRUE(slider_delegate.created_back_layer());
EXPECT_TRUE(slider_delegate.slide_aborted());
EXPECT_FALSE(slider_delegate.created_front_layer());
+ EXPECT_FALSE(slider_delegate.slide_completing());
EXPECT_FALSE(slider_delegate.slide_completed());
EXPECT_FALSE(slider_delegate.slider_destroyed());
EXPECT_FALSE(slider->IsSlideInProgress());
@@ -241,17 +288,18 @@ TEST_F(WindowSliderTest, WindowSlideUsingGesture) {
EXPECT_TRUE(slider_delegate.slider_destroyed());
}
-// Tests that the window slide is cancelled when a different type of event
+// Tests that the window slide is interrupted when a different type of event
// happens.
TEST_F(WindowSliderTest, WindowSlideIsCancelledOnEvent) {
scoped_ptr<aura::Window> window(CreateNormalWindow(0, root_window(), NULL));
+ window->SetBounds(gfx::Rect(0, 0, 400, 400));
WindowSliderDelegateTest slider_delegate;
ui::Event* events[] = {
new ui::MouseEvent(ui::ET_MOUSE_MOVED,
gfx::Point(55, 10),
gfx::Point(55, 10),
- 0),
+ 0, 0),
new ui::KeyEvent(ui::ET_KEY_PRESSED,
ui::VKEY_A,
0,
@@ -269,11 +317,12 @@ TEST_F(WindowSliderTest, WindowSlideIsCancelledOnEvent) {
base::TimeDelta::FromMilliseconds(10),
1,
base::Bind(&DispatchEventDuringScrollCallback,
- root_window()->GetDispatcher(),
+ root_window()->GetHost()->event_processor(),
base::Owned(events[i])));
EXPECT_TRUE(slider_delegate.created_back_layer());
EXPECT_TRUE(slider_delegate.slide_aborted());
EXPECT_FALSE(slider_delegate.created_front_layer());
+ EXPECT_FALSE(slider_delegate.slide_completing());
EXPECT_FALSE(slider_delegate.slide_completed());
EXPECT_FALSE(slider_delegate.slider_destroyed());
slider_delegate.Reset();
@@ -282,6 +331,111 @@ TEST_F(WindowSliderTest, WindowSlideIsCancelledOnEvent) {
EXPECT_TRUE(slider_delegate.slider_destroyed());
}
+// Tests that the window slide can continue after it is interrupted by another
+// event if the user continues scrolling.
+TEST_F(WindowSliderTest, WindowSlideInterruptedThenContinues) {
+ scoped_ptr<aura::Window> window(CreateNormalWindow(0, root_window(), NULL));
+ window->SetBounds(gfx::Rect(0, 0, 400, 400));
+ WindowSliderDelegateTest slider_delegate;
+
+ ui::ScopedAnimationDurationScaleMode normal_duration_(
+ ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
+ ui::LayerAnimator* animator = window->layer()->GetAnimator();
+ animator->set_disable_timer_for_test(true);
+ ui::LayerAnimatorTestController test_controller(animator);
+
+ WindowSlider* slider =
+ new WindowSlider(&slider_delegate, root_window(), window.get());
+
+ ui::MouseEvent interrupt_event(ui::ET_MOUSE_MOVED,
+ gfx::Point(55, 10),
+ gfx::Point(55, 10),
+ 0, 0);
+
+ aura::test::EventGenerator generator(root_window());
+
+ // Start the scroll sequence. Scroll forward so that |window|'s layer is the
+ // one animating.
+ const int kTouchId = 5;
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED,
+ gfx::Point(10, 10),
+ kTouchId,
+ ui::EventTimeForNow());
+ generator.Dispatch(&press);
+
+ // First scroll event of the sequence.
+ ui::TouchEvent move1(ui::ET_TOUCH_MOVED,
+ gfx::Point(100, 10),
+ kTouchId,
+ ui::EventTimeForNow());
+ generator.Dispatch(&move1);
+ EXPECT_TRUE(slider->IsSlideInProgress());
+ EXPECT_FALSE(animator->is_animating());
+ // Dispatch the event after the first scroll and confirm it interrupts the
+ // scroll and starts the "reset slide" animation.
+ generator.Dispatch(&interrupt_event);
+ EXPECT_TRUE(slider->IsSlideInProgress());
+ EXPECT_TRUE(animator->is_animating());
+ EXPECT_TRUE(slider_delegate.created_back_layer());
+ // slide_aborted() should be false because the 'reset slide' animation
+ // hasn't completed yet.
+ EXPECT_FALSE(slider_delegate.slide_aborted());
+ EXPECT_FALSE(slider_delegate.created_front_layer());
+ EXPECT_FALSE(slider_delegate.slide_completing());
+ EXPECT_FALSE(slider_delegate.slide_completed());
+ EXPECT_FALSE(slider_delegate.slider_destroyed());
+ slider_delegate.Reset();
+
+ // Second scroll event of the sequence.
+ ui::TouchEvent move2(ui::ET_TOUCH_MOVED,
+ gfx::Point(200, 10),
+ kTouchId,
+ ui::EventTimeForNow());
+ generator.Dispatch(&move2);
+ // The second scroll should instantly cause the animation to complete.
+ EXPECT_FALSE(animator->is_animating());
+ EXPECT_FALSE(slider_delegate.created_back_layer());
+ // The ResetScroll() animation was completed, so now slide_aborted()
+ // should be true.
+ EXPECT_TRUE(slider_delegate.slide_aborted());
+
+ // Third scroll event of the sequence.
+ ui::TouchEvent move3(ui::ET_TOUCH_MOVED,
+ gfx::Point(300, 10),
+ kTouchId,
+ ui::EventTimeForNow());
+ generator.Dispatch(&move3);
+ // The third scroll should re-start the sliding.
+ EXPECT_TRUE(slider->IsSlideInProgress());
+ EXPECT_TRUE(slider_delegate.created_back_layer());
+
+ // Generate the release event, finishing the scroll sequence.
+ ui::TouchEvent release(ui::ET_TOUCH_RELEASED,
+ gfx::Point(300, 10),
+ kTouchId,
+ ui::EventTimeForNow());
+ generator.Dispatch(&release);
+ // When the scroll gesture ends, the slide animation should start.
+ EXPECT_TRUE(slider->IsSlideInProgress());
+ EXPECT_TRUE(animator->is_animating());
+ EXPECT_TRUE(slider_delegate.slide_completing());
+ EXPECT_FALSE(slider_delegate.created_front_layer());
+ EXPECT_FALSE(slider_delegate.slide_completed());
+ EXPECT_FALSE(slider_delegate.slider_destroyed());
+
+ // Progress the animator to complete the slide animation.
+ ui::ScopedLayerAnimationSettings settings(animator);
+ base::TimeDelta duration = settings.GetTransitionDuration();
+ test_controller.StartThreadedAnimationsIfNeeded();
+ animator->Step(gfx::FrameTime::Now() + duration);
+
+ EXPECT_TRUE(slider_delegate.slide_completed());
+ EXPECT_FALSE(slider_delegate.slider_destroyed());
+
+ window.reset();
+ EXPECT_TRUE(slider_delegate.slider_destroyed());
+}
+
// Tests that the slide works correctly when the owner of the window changes
// during the duration of the slide.
TEST_F(WindowSliderTest, OwnerWindowChangesDuringWindowSlide) {
@@ -312,28 +466,37 @@ TEST_F(WindowSliderTest, OwnerWindowChangesDuringWindowSlide) {
EXPECT_NE(old_window, new_window);
EXPECT_TRUE(slider_delegate.created_back_layer());
+ EXPECT_TRUE(slider_delegate.slide_completing());
EXPECT_TRUE(slider_delegate.slide_completed());
EXPECT_FALSE(slider_delegate.created_front_layer());
EXPECT_FALSE(slider_delegate.slide_aborted());
EXPECT_FALSE(slider_delegate.slider_destroyed());
}
+// If the delegate doesn't create the layer to show while sliding, WindowSlider
+// shouldn't start the slide or change delegate's state in any way in response
+// to user input.
TEST_F(WindowSliderTest, NoSlideWhenLayerCantBeCreated) {
scoped_ptr<aura::Window> window(CreateNormalWindow(0, root_window(), NULL));
window->SetBounds(gfx::Rect(0, 0, 400, 400));
WindowSliderDelegateTest slider_delegate;
slider_delegate.SetCanCreateLayer(false);
+ WindowSlider* slider =
+ new WindowSlider(&slider_delegate, root_window(), window.get());
aura::test::EventGenerator generator(root_window());
- // Generate a horizontal overscroll.
- scoped_ptr<WindowSlider> slider(
- new WindowSlider(&slider_delegate, root_window(), window.get()));
- generator.GestureScrollSequence(gfx::Point(10, 10),
- gfx::Point(160, 10),
- base::TimeDelta::FromMilliseconds(10),
- 10);
+ // No slide in progress should be reported during scroll since the layer
+ // wasn't created.
+ generator.GestureScrollSequenceWithCallback(
+ gfx::Point(10, 10),
+ gfx::Point(180, 10),
+ base::TimeDelta::FromMilliseconds(10),
+ 1,
+ base::Bind(&ConfirmNoSlideDuringScrollCallback, slider));
+
EXPECT_FALSE(slider_delegate.created_back_layer());
+ EXPECT_FALSE(slider_delegate.slide_completing());
EXPECT_FALSE(slider_delegate.slide_completed());
EXPECT_FALSE(slider_delegate.created_front_layer());
EXPECT_FALSE(slider_delegate.slide_aborted());
@@ -341,15 +504,21 @@ TEST_F(WindowSliderTest, NoSlideWhenLayerCantBeCreated) {
window->SetTransform(gfx::Transform());
slider_delegate.SetCanCreateLayer(true);
- generator.GestureScrollSequence(gfx::Point(10, 10),
- gfx::Point(160, 10),
- base::TimeDelta::FromMilliseconds(10),
- 10);
+ generator.GestureScrollSequenceWithCallback(
+ gfx::Point(10, 10),
+ gfx::Point(180, 10),
+ base::TimeDelta::FromMilliseconds(10),
+ 10,
+ base::Bind(&ConfirmSlideDuringScrollCallback, slider));
EXPECT_TRUE(slider_delegate.created_back_layer());
+ EXPECT_TRUE(slider_delegate.slide_completing());
EXPECT_TRUE(slider_delegate.slide_completed());
EXPECT_FALSE(slider_delegate.created_front_layer());
EXPECT_FALSE(slider_delegate.slide_aborted());
EXPECT_FALSE(slider_delegate.slider_destroyed());
+
+ window.reset();
+ EXPECT_TRUE(slider_delegate.slider_destroyed());
}
// Tests that the owner window can be destroyed from |OnWindowSliderDestroyed()|
@@ -367,10 +536,11 @@ TEST_F(WindowSliderTest, OwnerIsDestroyedOnSliderDestroy) {
scoped_ptr<WindowSlider> slider(
new WindowSlider(&slider_delegate, root_window(), window));
generator.GestureScrollSequence(gfx::Point(10, 10),
- gfx::Point(160, 10),
+ gfx::Point(180, 10),
base::TimeDelta::FromMilliseconds(10),
10);
EXPECT_TRUE(slider_delegate.created_back_layer());
+ EXPECT_TRUE(slider_delegate.slide_completing());
EXPECT_TRUE(slider_delegate.slide_completed());
EXPECT_FALSE(slider_delegate.created_front_layer());
EXPECT_FALSE(slider_delegate.slide_aborted());
@@ -396,10 +566,11 @@ TEST_F(WindowSliderTest, OwnerIsDestroyedOnSlideComplete) {
// Generate a horizontal overscroll.
new WindowSlider(&slider_delegate, root_window(), window);
generator.GestureScrollSequence(gfx::Point(10, 10),
- gfx::Point(160, 10),
+ gfx::Point(180, 10),
base::TimeDelta::FromMilliseconds(10),
10);
EXPECT_TRUE(slider_delegate.created_back_layer());
+ EXPECT_TRUE(slider_delegate.slide_completing());
EXPECT_TRUE(slider_delegate.slide_completed());
EXPECT_FALSE(slider_delegate.created_front_layer());
EXPECT_FALSE(slider_delegate.slide_aborted());
@@ -410,4 +581,70 @@ TEST_F(WindowSliderTest, OwnerIsDestroyedOnSlideComplete) {
EXPECT_EQ(child_windows, root_window()->children().size());
}
+// Test the scenario when two swipe gesture occur quickly one after another so
+// that the second swipe occurs while the transition animation triggered by the
+// first swipe is in progress.
+// The second swipe is supposed to instantly complete the animation caused by
+// the first swipe, ask the delegate to create a new layer, and animate it.
+TEST_F(WindowSliderTest, SwipeDuringSwipeAnimation) {
+ scoped_ptr<aura::Window> window(CreateNormalWindow(0, root_window(), NULL));
+ window->SetBounds(gfx::Rect(0, 0, 400, 400));
+ WindowSliderDelegateTest slider_delegate;
+ new WindowSlider(&slider_delegate, root_window(), window.get());
+
+ ui::ScopedAnimationDurationScaleMode normal_duration_(
+ ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
+ ui::LayerAnimator* animator = window->layer()->GetAnimator();
+ animator->set_disable_timer_for_test(true);
+ ui::LayerAnimatorTestController test_controller(animator);
+
+ aura::test::EventGenerator generator(root_window());
+
+ // Swipe forward so that |window|'s layer is the one animating.
+ generator.GestureScrollSequence(
+ gfx::Point(10, 10),
+ gfx::Point(180, 10),
+ base::TimeDelta::FromMilliseconds(10),
+ 2);
+ EXPECT_TRUE(slider_delegate.created_back_layer());
+ EXPECT_FALSE(slider_delegate.slide_aborted());
+ EXPECT_FALSE(slider_delegate.created_front_layer());
+ EXPECT_TRUE(slider_delegate.slide_completing());
+ EXPECT_FALSE(slider_delegate.slide_completed());
+ EXPECT_FALSE(slider_delegate.slider_destroyed());
+ ui::ScopedLayerAnimationSettings settings(animator);
+ base::TimeDelta duration = settings.GetTransitionDuration();
+ test_controller.StartThreadedAnimationsIfNeeded();
+ base::TimeTicks start_time1 = gfx::FrameTime::Now();
+
+ animator->Step(start_time1 + duration / 2);
+ EXPECT_FALSE(slider_delegate.slide_completed());
+ slider_delegate.Reset();
+ // Generate another horizontal swipe while the animation from the previous
+ // swipe is in progress.
+ generator.GestureScrollSequence(
+ gfx::Point(10, 10),
+ gfx::Point(180, 10),
+ base::TimeDelta::FromMilliseconds(10),
+ 2);
+ // Performing the second swipe should instantly complete the slide started
+ // by the first swipe and create a new layer.
+ EXPECT_TRUE(slider_delegate.created_back_layer());
+ EXPECT_FALSE(slider_delegate.slide_aborted());
+ EXPECT_FALSE(slider_delegate.created_front_layer());
+ EXPECT_TRUE(slider_delegate.slide_completing());
+ EXPECT_TRUE(slider_delegate.slide_completed());
+ EXPECT_FALSE(slider_delegate.slider_destroyed());
+ test_controller.StartThreadedAnimationsIfNeeded();
+ base::TimeTicks start_time2 = gfx::FrameTime::Now();
+ slider_delegate.Reset();
+ animator->Step(start_time2 + duration);
+ // The animation for the second slide should now be completed.
+ EXPECT_TRUE(slider_delegate.slide_completed());
+ slider_delegate.Reset();
+
+ window.reset();
+ EXPECT_TRUE(slider_delegate.slider_destroyed());
+}
+
} // namespace content
diff --git a/chromium/content/browser/web_contents/drag_utils_gtk.cc b/chromium/content/browser/web_contents/drag_utils_gtk.cc
deleted file mode 100644
index 279ae9daa48..00000000000
--- a/chromium/content/browser/web_contents/drag_utils_gtk.cc
+++ /dev/null
@@ -1,38 +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/web_contents/drag_utils_gtk.h"
-
-using blink::WebDragOperationsMask;
-using blink::WebDragOperation;
-using blink::WebDragOperationNone;
-using blink::WebDragOperationCopy;
-using blink::WebDragOperationLink;
-using blink::WebDragOperationMove;
-
-namespace content {
-
-GdkDragAction WebDragOpToGdkDragAction(WebDragOperationsMask op) {
- GdkDragAction action = static_cast<GdkDragAction>(0);
- if (op & WebDragOperationCopy)
- action = static_cast<GdkDragAction>(action | GDK_ACTION_COPY);
- if (op & WebDragOperationLink)
- action = static_cast<GdkDragAction>(action | GDK_ACTION_LINK);
- if (op & WebDragOperationMove)
- action = static_cast<GdkDragAction>(action | GDK_ACTION_MOVE);
- return action;
-}
-
-WebDragOperationsMask GdkDragActionToWebDragOp(GdkDragAction action) {
- WebDragOperationsMask op = WebDragOperationNone;
- if (action & GDK_ACTION_COPY)
- op = static_cast<WebDragOperationsMask>(op | WebDragOperationCopy);
- if (action & GDK_ACTION_LINK)
- op = static_cast<WebDragOperationsMask>(op | WebDragOperationLink);
- if (action & GDK_ACTION_MOVE)
- op = static_cast<WebDragOperationsMask>(op | WebDragOperationMove);
- return op;
-}
-
-} // namespace content
diff --git a/chromium/content/browser/web_contents/drag_utils_gtk.h b/chromium/content/browser/web_contents/drag_utils_gtk.h
deleted file mode 100644
index f6d883924c8..00000000000
--- a/chromium/content/browser/web_contents/drag_utils_gtk.h
+++ /dev/null
@@ -1,24 +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_WEB_CONTENTS_DRAG_UTILS_GTK_H_
-#define CONTENT_BROWSER_WEB_CONTENTS_DRAG_UTILS_GTK_H_
-
-#include <gtk/gtk.h>
-
-#include "content/common/content_export.h"
-#include "third_party/WebKit/public/web/WebDragOperation.h"
-
-namespace content {
-
-// Convenience methods for converting between web drag operations and the GDK
-// equivalent.
-CONTENT_EXPORT GdkDragAction WebDragOpToGdkDragAction(
- blink::WebDragOperationsMask op);
-CONTENT_EXPORT blink::WebDragOperationsMask GdkDragActionToWebDragOp(
- GdkDragAction action);
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_WEB_CONTENTS_DRAG_UTILS_GTK_H_
diff --git a/chromium/content/browser/web_contents/opened_by_dom_browsertest.cc b/chromium/content/browser/web_contents/opened_by_dom_browsertest.cc
new file mode 100644
index 00000000000..1c2d94099ce
--- /dev/null
+++ b/chromium/content/browser/web_contents/opened_by_dom_browsertest.cc
@@ -0,0 +1,139 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/command_line.h"
+#include "base/strings/stringprintf.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_delegate.h"
+#include "content/public/common/content_switches.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_navigation_observer.h"
+#include "content/shell/browser/shell.h"
+#include "net/dns/mock_host_resolver.h"
+#include "url/gurl.h"
+
+namespace content {
+
+namespace {
+
+// A dummy WebContentsDelegate which tracks whether CloseContents() has been
+// called. It refuses the actual close but keeps track of whether the renderer
+// requested it.
+class CloseTrackingDelegate : public WebContentsDelegate {
+ public:
+ CloseTrackingDelegate() : close_contents_called_(false) {}
+
+ bool close_contents_called() const { return close_contents_called_; }
+
+ virtual void CloseContents(WebContents* source) OVERRIDE {
+ close_contents_called_ = true;
+ }
+
+ private:
+ bool close_contents_called_;
+
+ DISALLOW_COPY_AND_ASSIGN(CloseTrackingDelegate);
+};
+
+} // namespace
+
+class OpenedByDOMTest : public ContentBrowserTest {
+ protected:
+ virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
+ // Use --site-per-process to force process swaps on cross-site navigations.
+ command_line->AppendSwitch(switches::kSitePerProcess);
+ }
+
+ bool AttemptCloseFromJavaScript(WebContents* web_contents) {
+ CloseTrackingDelegate close_tracking_delegate;
+ WebContentsDelegate* old_delegate = web_contents->GetDelegate();
+ web_contents->SetDelegate(&close_tracking_delegate);
+
+ const char kCloseWindowScript[] =
+ // Close the window.
+ "window.close();"
+ // Report back after an event loop iteration; the close IPC isn't sent
+ // immediately.
+ "setTimeout(function() {"
+ "window.domAutomationController.send(0);"
+ "});";
+ int dummy;
+ CHECK(ExecuteScriptAndExtractInt(web_contents, kCloseWindowScript, &dummy));
+
+ web_contents->SetDelegate(old_delegate);
+ return close_tracking_delegate.close_contents_called();
+ }
+
+ Shell* OpenWindowFromJavaScript(Shell* shell, const GURL& url) {
+ // Wait for the popup to be created and for it to have navigated.
+ ShellAddedObserver new_shell_observer;
+ TestNavigationObserver nav_observer(NULL);
+ nav_observer.StartWatchingNewWebContents();
+ CHECK(ExecuteScript(
+ shell->web_contents(),
+ base::StringPrintf("window.open('%s')", url.spec().c_str())));
+ nav_observer.Wait();
+ return new_shell_observer.GetShell();
+ }
+};
+
+// Tests that window.close() does not work on a normal window that has navigated
+// a few times.
+IN_PROC_BROWSER_TEST_F(OpenedByDOMTest, NormalWindow) {
+ ASSERT_TRUE(test_server()->Start());
+
+ // window.close is allowed if the window was opened by DOM OR the back/forward
+ // list has only one element. Navigate a bit so the second condition is false.
+ GURL url1 = test_server()->GetURL("files/site_isolation/blank.html?1");
+ GURL url2 = test_server()->GetURL("files/site_isolation/blank.html?2");
+ NavigateToURL(shell(), url1);
+ NavigateToURL(shell(), url2);
+
+ // This window was not opened by DOM, so close does not reach the browser
+ // process.
+ EXPECT_FALSE(AttemptCloseFromJavaScript(shell()->web_contents()));
+}
+
+// Tests that window.close() works in a popup window that has navigated a few
+// times.
+IN_PROC_BROWSER_TEST_F(OpenedByDOMTest, Popup) {
+ ASSERT_TRUE(test_server()->Start());
+
+ GURL url1 = test_server()->GetURL("files/site_isolation/blank.html?1");
+ GURL url2 = test_server()->GetURL("files/site_isolation/blank.html?2");
+ GURL url3 = test_server()->GetURL("files/site_isolation/blank.html?3");
+ NavigateToURL(shell(), url1);
+
+ Shell* popup = OpenWindowFromJavaScript(shell(), url2);
+ NavigateToURL(popup, url3);
+ EXPECT_TRUE(AttemptCloseFromJavaScript(popup->web_contents()));
+}
+
+// Tests that window.close() works in a popup window that has navigated a few
+// times and swapped processes.
+IN_PROC_BROWSER_TEST_F(OpenedByDOMTest, CrossProcessPopup) {
+ host_resolver()->AddRule("*", "127.0.0.1");
+ ASSERT_TRUE(test_server()->Start());
+
+ GURL url1 = test_server()->GetURL("files/site_isolation/blank.html?1");
+
+ GURL url2 = test_server()->GetURL("files/site_isolation/blank.html?2");
+ GURL::Replacements replace_host;
+ std::string foo_com("foo.com");
+ replace_host.SetHostStr(foo_com);
+ url2 = url2.ReplaceComponents(replace_host);
+
+ GURL url3 = test_server()->GetURL("files/site_isolation/blank.html?3");
+ url3 = url3.ReplaceComponents(replace_host);
+
+ NavigateToURL(shell(), url1);
+
+ Shell* popup = OpenWindowFromJavaScript(shell(), url2);
+ NavigateToURL(popup, url3);
+ EXPECT_TRUE(AttemptCloseFromJavaScript(popup->web_contents()));
+}
+
+} // namespace content
diff --git a/chromium/content/browser/web_contents/touch_editable_impl_aura.cc b/chromium/content/browser/web_contents/touch_editable_impl_aura.cc
index ed002c7d9c6..d28fa92deac 100644
--- a/chromium/content/browser/web_contents/touch_editable_impl_aura.cc
+++ b/chromium/content/browser/web_contents/touch_editable_impl_aura.cc
@@ -6,16 +6,18 @@
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_view_aura.h"
+#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/view_messages.h"
+#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host.h"
#include "grit/ui_strings.h"
-#include "ui/aura/client/activation_client.h"
#include "ui/aura/client/screen_position_client.h"
-#include "ui/aura/root_window.h"
#include "ui/aura/window.h"
+#include "ui/aura/window_tree_host.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/ui_base_switches_util.h"
#include "ui/gfx/range/range.h"
+#include "ui/wm/public/activation_client.h"
namespace content {
@@ -49,20 +51,12 @@ void TouchEditableImplAura::UpdateEditingController() {
if (!rwhva_ || !rwhva_->HasFocus())
return;
- // If touch editing handles were not visible, we bring them up only if
- // there is non-zero selection on the page. And the current event is a
- // gesture event (we dont want to show handles if the user is selecting
- // using mouse or keyboard).
- if (selection_gesture_in_process_ && !scroll_in_progress_ &&
- selection_anchor_rect_ != selection_focus_rect_)
- StartTouchEditing();
-
if (text_input_type_ != ui::TEXT_INPUT_TYPE_NONE ||
selection_anchor_rect_ != selection_focus_rect_) {
if (touch_selection_controller_)
touch_selection_controller_->SelectionChanged();
} else {
- EndTouchEditing();
+ EndTouchEditing(false);
}
}
@@ -105,12 +99,15 @@ void TouchEditableImplAura::StartTouchEditing() {
touch_selection_controller_->SelectionChanged();
}
-void TouchEditableImplAura::EndTouchEditing() {
+void TouchEditableImplAura::EndTouchEditing(bool quick) {
if (touch_selection_controller_) {
- if (touch_selection_controller_->IsHandleDragInProgress())
+ if (touch_selection_controller_->IsHandleDragInProgress()) {
touch_selection_controller_->SelectionChanged();
- else
+ } else {
+ selection_gesture_in_process_ = false;
+ touch_selection_controller_->HideHandles(quick);
touch_selection_controller_.reset();
+ }
}
}
@@ -118,6 +115,18 @@ void TouchEditableImplAura::OnSelectionOrCursorChanged(const gfx::Rect& anchor,
const gfx::Rect& focus) {
selection_anchor_rect_ = anchor;
selection_focus_rect_ = focus;
+
+ // If touch editing handles were not visible, we bring them up only if
+ // there is non-zero selection on the page. And the current event is a
+ // gesture event (we dont want to show handles if the user is selecting
+ // using mouse or keyboard).
+ if (selection_gesture_in_process_ && !scroll_in_progress_ &&
+ !overscroll_in_progress_ &&
+ selection_anchor_rect_ != selection_focus_rect_) {
+ StartTouchEditing();
+ selection_gesture_in_process_ = false;
+ }
+
UpdateEditingController();
}
@@ -127,11 +136,9 @@ void TouchEditableImplAura::OnTextInputTypeChanged(ui::TextInputType type) {
bool TouchEditableImplAura::HandleInputEvent(const ui::Event* event) {
DCHECK(rwhva_);
- if (event->IsTouchEvent())
- return false;
-
if (!event->IsGestureEvent()) {
- EndTouchEditing();
+ // Ignore all non-gesture events. Non-gesture events that can deactivate
+ // touch editing are handled in TouchSelectionControllerImpl.
return false;
}
@@ -139,12 +146,10 @@ bool TouchEditableImplAura::HandleInputEvent(const ui::Event* event) {
static_cast<const ui::GestureEvent*>(event);
switch (event->type()) {
case ui::ET_GESTURE_TAP:
- tap_gesture_tap_count_queue_.push(gesture_event->details().tap_count());
- if (gesture_event->details().tap_count() > 1)
- selection_gesture_in_process_ = true;
// When the user taps, we want to show touch editing handles if user
// tapped on selected text.
- if (selection_anchor_rect_ != selection_focus_rect_) {
+ if (gesture_event->details().tap_count() == 1 &&
+ selection_anchor_rect_ != selection_focus_rect_) {
// UnionRects only works for rects with non-zero width.
gfx::Rect anchor(selection_anchor_rect_.origin(),
gfx::Size(1, selection_anchor_rect_.height()));
@@ -158,10 +163,10 @@ bool TouchEditableImplAura::HandleInputEvent(const ui::Event* event) {
}
// For single taps, not inside selected region, we want to show handles
// only when the tap is on an already focused textfield.
- is_tap_on_focused_textfield_ = false;
+ textfield_was_focused_on_tap_ = false;
if (gesture_event->details().tap_count() == 1 &&
text_input_type_ != ui::TEXT_INPUT_TYPE_NONE)
- is_tap_on_focused_textfield_ = true;
+ textfield_was_focused_on_tap_ = true;
break;
case ui::ET_GESTURE_LONG_PRESS:
selection_gesture_in_process_ = true;
@@ -169,13 +174,12 @@ bool TouchEditableImplAura::HandleInputEvent(const ui::Event* event) {
case ui::ET_GESTURE_SCROLL_BEGIN:
// If selection handles are currently visible, we want to get them back up
// when scrolling ends. So we set |handles_hidden_due_to_scroll_| so that
- // we can re-start touch editing when we call |UpdateEditingController()|
- // on scroll end gesture.
+ // we can re-start touch editing on scroll end gesture.
scroll_in_progress_ = true;
handles_hidden_due_to_scroll_ = false;
if (touch_selection_controller_)
handles_hidden_due_to_scroll_ = true;
- EndTouchEditing();
+ EndTouchEditing(true);
break;
case ui::ET_GESTURE_SCROLL_END:
// Scroll has ended, but we might still be in overscroll animation.
@@ -200,18 +204,9 @@ void TouchEditableImplAura::GestureEventAck(int gesture_event_type) {
DCHECK(rwhva_);
if (gesture_event_type == blink::WebInputEvent::GestureTap &&
text_input_type_ != ui::TEXT_INPUT_TYPE_NONE &&
- is_tap_on_focused_textfield_) {
+ textfield_was_focused_on_tap_) {
StartTouchEditing();
- if (touch_selection_controller_)
- touch_selection_controller_->SelectionChanged();
- }
-
- if (gesture_event_type == blink::WebInputEvent::GestureLongPress)
- selection_gesture_in_process_ = false;
- if (gesture_event_type == blink::WebInputEvent::GestureTap) {
- if (tap_gesture_tap_count_queue_.front() > 1)
- selection_gesture_in_process_ = false;
- tap_gesture_tap_count_queue_.pop();
+ UpdateEditingController();
}
}
@@ -224,12 +219,11 @@ void TouchEditableImplAura::OnViewDestroyed() {
void TouchEditableImplAura::SelectRect(const gfx::Point& start,
const gfx::Point& end) {
- if (!rwhva_)
- return;
-
- RenderWidgetHostImpl* host = RenderWidgetHostImpl::From(
- rwhva_->GetRenderWidgetHost());
- host->SelectRange(start, end);
+ RenderWidgetHost* host = rwhva_->GetRenderWidgetHost();
+ RenderViewHost* rvh = RenderViewHost::From(host);
+ WebContentsImpl* wc =
+ static_cast<WebContentsImpl*>(WebContents::FromRenderViewHost(rvh));
+ wc->SelectRange(start, end);
}
void TouchEditableImplAura::MoveCaretTo(const gfx::Point& point) {
@@ -248,11 +242,12 @@ void TouchEditableImplAura::GetSelectionEndPoints(gfx::Rect* p1,
}
gfx::Rect TouchEditableImplAura::GetBounds() {
- return rwhva_ ? rwhva_->GetNativeView()->bounds() : gfx::Rect();
+ return rwhva_ ? gfx::Rect(rwhva_->GetNativeView()->bounds().size()) :
+ gfx::Rect();
}
-gfx::NativeView TouchEditableImplAura::GetNativeView() {
- return rwhva_ ? rwhva_->GetNativeView()->GetRootWindow() : NULL;
+gfx::NativeView TouchEditableImplAura::GetNativeView() const {
+ return rwhva_ ? rwhva_->GetNativeView()->GetToplevelWindow() : NULL;
}
void TouchEditableImplAura::ConvertPointToScreen(gfx::Point* point) {
@@ -286,7 +281,7 @@ void TouchEditableImplAura::OpenContextMenu(const gfx::Point& anchor) {
ConvertPointFromScreen(&point);
RenderWidgetHost* host = rwhva_->GetRenderWidgetHost();
host->Send(new ViewMsg_ShowContextMenu(host->GetRoutingID(), point));
- EndTouchEditing();
+ EndTouchEditing(false);
}
bool TouchEditableImplAura::IsCommandIdChecked(int command_id) const {
@@ -328,30 +323,35 @@ bool TouchEditableImplAura::GetAcceleratorForCommandId(
}
void TouchEditableImplAura::ExecuteCommand(int command_id, int event_flags) {
- if (!rwhva_)
- return;
RenderWidgetHost* host = rwhva_->GetRenderWidgetHost();
+ RenderViewHost* rvh = RenderViewHost::From(host);
+ WebContents* wc = WebContents::FromRenderViewHost(rvh);
+
switch (command_id) {
case IDS_APP_CUT:
- host->Cut();
+ wc->Cut();
break;
case IDS_APP_COPY:
- host->Copy();
+ wc->Copy();
break;
case IDS_APP_PASTE:
- host->Paste();
+ wc->Paste();
break;
case IDS_APP_DELETE:
- host->Delete();
+ wc->Delete();
break;
case IDS_APP_SELECT_ALL:
- host->SelectAll();
+ wc->SelectAll();
break;
default:
NOTREACHED();
break;
}
- EndTouchEditing();
+ EndTouchEditing(false);
+}
+
+void TouchEditableImplAura::DestroyTouchSelection() {
+ EndTouchEditing(false);
}
////////////////////////////////////////////////////////////////////////////////
@@ -364,7 +364,7 @@ TouchEditableImplAura::TouchEditableImplAura()
handles_hidden_due_to_scroll_(false),
scroll_in_progress_(false),
overscroll_in_progress_(false),
- is_tap_on_focused_textfield_(false) {
+ textfield_was_focused_on_tap_(false) {
}
void TouchEditableImplAura::Cleanup() {
@@ -373,7 +373,8 @@ void TouchEditableImplAura::Cleanup() {
rwhva_ = NULL;
}
text_input_type_ = ui::TEXT_INPUT_TYPE_NONE;
- touch_selection_controller_.reset();
+ EndTouchEditing(true);
+ selection_gesture_in_process_ = false;
handles_hidden_due_to_scroll_ = false;
scroll_in_progress_ = false;
overscroll_in_progress_ = false;
diff --git a/chromium/content/browser/web_contents/touch_editable_impl_aura.h b/chromium/content/browser/web_contents/touch_editable_impl_aura.h
index 7f1ea845512..dd19e8e4de6 100644
--- a/chromium/content/browser/web_contents/touch_editable_impl_aura.h
+++ b/chromium/content/browser/web_contents/touch_editable_impl_aura.h
@@ -7,6 +7,7 @@
#include <deque>
#include <map>
+#include <queue>
#include "content/browser/renderer_host/render_widget_host_view_aura.h"
#include "ui/aura/window_observer.h"
@@ -42,7 +43,7 @@ class CONTENT_EXPORT TouchEditableImplAura
// Overridden from RenderWidgetHostViewAura::TouchEditingClient.
virtual void StartTouchEditing() OVERRIDE;
- virtual void EndTouchEditing() OVERRIDE;
+ virtual void EndTouchEditing(bool quick) OVERRIDE;
virtual void OnSelectionOrCursorChanged(const gfx::Rect& anchor,
const gfx::Rect& focus) OVERRIDE;
virtual void OnTextInputTypeChanged(ui::TextInputType type) OVERRIDE;
@@ -56,7 +57,7 @@ class CONTENT_EXPORT TouchEditableImplAura
virtual void MoveCaretTo(const gfx::Point& point) OVERRIDE;
virtual void GetSelectionEndPoints(gfx::Rect* p1, gfx::Rect* p2) OVERRIDE;
virtual gfx::Rect GetBounds() OVERRIDE;
- virtual gfx::NativeView GetNativeView() OVERRIDE;
+ virtual gfx::NativeView GetNativeView() const OVERRIDE;
virtual void ConvertPointToScreen(gfx::Point* point) OVERRIDE;
virtual void ConvertPointFromScreen(gfx::Point* point) OVERRIDE;
virtual bool DrawsHandles() OVERRIDE;
@@ -67,6 +68,7 @@ class CONTENT_EXPORT TouchEditableImplAura
int command_id,
ui::Accelerator* accelerator) OVERRIDE;
virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE;
+ virtual void DestroyTouchSelection() OVERRIDE;
protected:
TouchEditableImplAura();
@@ -100,14 +102,9 @@ class CONTENT_EXPORT TouchEditableImplAura
// Set to true when the page starts an overscroll.
bool overscroll_in_progress_;
- // Used to track if the current tap gesture is on a focused textfield.
- bool is_tap_on_focused_textfield_;
-
- // When we receive ack for a ET_GESTURE_TAP, we do not know if the ack is for
- // a tap or a double tap (we only get the event type in the ack). So we have
- // this queue to keep track of the the tap count so that we can distinguish
- // between double and single tap when we get the ack.
- std::queue<int> tap_gesture_tap_count_queue_;
+ // Used to track if a textfield was focused when the current tap gesture
+ // happened.
+ bool textfield_was_focused_on_tap_;
DISALLOW_COPY_AND_ASSIGN(TouchEditableImplAura);
};
diff --git a/chromium/content/browser/web_contents/touch_editable_impl_aura_browsertest.cc b/chromium/content/browser/web_contents/touch_editable_impl_aura_browsertest.cc
index f8c1f5f91b0..9b4757f9c88 100644
--- a/chromium/content/browser/web_contents/touch_editable_impl_aura_browsertest.cc
+++ b/chromium/content/browser/web_contents/touch_editable_impl_aura_browsertest.cc
@@ -9,23 +9,25 @@
#include "base/strings/utf_string_conversions.h"
#include "base/test/test_timeouts.h"
#include "base/values.h"
-#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/browser/web_contents/web_contents_view_aura.h"
-#include "content/public/browser/web_contents_view.h"
+#include "content/public/browser/render_frame_host.h"
#include "content/public/common/content_switches.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/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
-#include "ui/aura/root_window.h"
+#include "third_party/WebKit/public/web/WebInputEvent.h"
#include "ui/aura/test/event_generator.h"
#include "ui/aura/window.h"
+#include "ui/aura/window_tree_host.h"
#include "ui/base/ui_base_switches.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
#include "ui/events/event_utils.h"
+using blink::WebInputEvent;
+
namespace content {
class TestTouchEditableImplAura : public TouchEditableImplAura {
@@ -33,14 +35,14 @@ class TestTouchEditableImplAura : public TouchEditableImplAura {
TestTouchEditableImplAura()
: selection_changed_callback_arrived_(false),
waiting_for_selection_changed_callback_(false),
- gesture_ack_callback_arrived_(false),
- waiting_for_gesture_ack_callback_(false) {}
+ waiting_for_gesture_ack_type_(WebInputEvent::Undefined),
+ last_gesture_ack_type_(WebInputEvent::Undefined) {}
virtual void Reset() {
selection_changed_callback_arrived_ = false;
waiting_for_selection_changed_callback_ = false;
- gesture_ack_callback_arrived_ = false;
- waiting_for_gesture_ack_callback_ = false;
+ waiting_for_gesture_ack_type_ = WebInputEvent::Undefined;
+ last_gesture_ack_type_ = WebInputEvent::Undefined;
}
virtual void OnSelectionOrCursorChanged(const gfx::Rect& anchor,
@@ -52,9 +54,10 @@ class TestTouchEditableImplAura : public TouchEditableImplAura {
}
virtual void GestureEventAck(int gesture_event_type) OVERRIDE {
- gesture_ack_callback_arrived_ = true;
+ last_gesture_ack_type_ =
+ static_cast<WebInputEvent::Type>(gesture_event_type);
TouchEditableImplAura::GestureEventAck(gesture_event_type);
- if (waiting_for_gesture_ack_callback_)
+ if (waiting_for_gesture_ack_type_ == gesture_event_type)
gesture_ack_wait_run_loop_->Quit();
}
@@ -66,10 +69,10 @@ class TestTouchEditableImplAura : public TouchEditableImplAura {
selection_changed_wait_run_loop_->Run();
}
- virtual void WaitForGestureAck() {
- if (gesture_ack_callback_arrived_)
+ virtual void WaitForGestureAck(WebInputEvent::Type gesture_event_type) {
+ if (last_gesture_ack_type_ == gesture_event_type)
return;
- waiting_for_gesture_ack_callback_ = true;
+ waiting_for_gesture_ack_type_ = gesture_event_type;
gesture_ack_wait_run_loop_.reset(new base::RunLoop());
gesture_ack_wait_run_loop_->Run();
}
@@ -80,52 +83,28 @@ class TestTouchEditableImplAura : public TouchEditableImplAura {
private:
bool selection_changed_callback_arrived_;
bool waiting_for_selection_changed_callback_;
- bool gesture_ack_callback_arrived_;
- bool waiting_for_gesture_ack_callback_;
+ WebInputEvent::Type waiting_for_gesture_ack_type_;
+ WebInputEvent::Type last_gesture_ack_type_;
scoped_ptr<base::RunLoop> selection_changed_wait_run_loop_;
scoped_ptr<base::RunLoop> gesture_ack_wait_run_loop_;
DISALLOW_COPY_AND_ASSIGN(TestTouchEditableImplAura);
};
-// This class ignores mouse-moved, mouse-entered and mouse-exited events
-// without passing them to TouchEditableImplAura. Normally, we should not
-// receive these events; but, sometimes we receive them which breaks the tests
-// and makes them flaky: crbug.com/276992.
-class TestTouchEditableImplAuraIgnoreMouseMovement
- : public TestTouchEditableImplAura {
- public:
- TestTouchEditableImplAuraIgnoreMouseMovement() {}
-
- virtual bool HandleInputEvent(const ui::Event* event) OVERRIDE {
- if (event->type() == ui::ET_MOUSE_ENTERED ||
- event->type() == ui::ET_MOUSE_MOVED ||
- event->type() == ui::ET_MOUSE_EXITED) {
- return false;
- }
- return TestTouchEditableImplAura::HandleInputEvent(event);
- }
-
- protected:
- virtual ~TestTouchEditableImplAuraIgnoreMouseMovement() {}
-
- private:
- DISALLOW_COPY_AND_ASSIGN(TestTouchEditableImplAuraIgnoreMouseMovement);
-};
-
class TouchEditableImplAuraTest : public ContentBrowserTest {
public:
TouchEditableImplAuraTest() {}
+ protected:
virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
command_line->AppendSwitch(switches::kEnableTouchEditing);
}
// Executes the javascript synchronously and makes sure the returned value is
// freed properly.
- void ExecuteSyncJSFunction(RenderViewHost* rvh, const std::string& jscript) {
+ void ExecuteSyncJSFunction(RenderFrameHost* rfh, const std::string& jscript) {
scoped_ptr<base::Value> value =
- content::ExecuteScriptAndGetValue(rvh, jscript);
+ content::ExecuteScriptAndGetValue(rfh, jscript);
}
// Starts the test server and navigates to the given url. Sets a large enough
@@ -135,231 +114,22 @@ class TouchEditableImplAuraTest : public ContentBrowserTest {
ASSERT_TRUE(test_server()->Start());
GURL test_url(test_server()->GetURL(url));
NavigateToURL(shell(), test_url);
- aura::Window* content =
- shell()->web_contents()->GetView()->GetContentNativeView();
- content->GetDispatcher()->SetHostSize(gfx::Size(800, 600));
+ aura::Window* content = shell()->web_contents()->GetContentNativeView();
+ content->GetHost()->SetBounds(gfx::Rect(800, 600));
}
- void TestTouchSelectionOriginatingFromWebpage() {
- ASSERT_NO_FATAL_FAILURE(
- StartTestWithPage("files/touch_selection.html"));
- WebContentsImpl* web_contents =
- static_cast<WebContentsImpl*>(shell()->web_contents());
- RenderViewHostImpl* view_host = static_cast<RenderViewHostImpl*>(
- web_contents->GetRenderViewHost());
- WebContentsViewAura* view_aura = static_cast<WebContentsViewAura*>(
- web_contents->GetView());
- TestTouchEditableImplAura* touch_editable =
- new TestTouchEditableImplAuraIgnoreMouseMovement;
- view_aura->SetTouchEditableForTest(touch_editable);
- RenderWidgetHostViewAura* rwhva = static_cast<RenderWidgetHostViewAura*>(
- web_contents->GetRenderWidgetHostView());
- aura::Window* content = web_contents->GetView()->GetContentNativeView();
- aura::test::EventGenerator generator(content->GetRootWindow(), content);
- gfx::Rect bounds = content->GetBoundsInRootWindow();
-
- touch_editable->Reset();
- ExecuteSyncJSFunction(view_host, "select_all_text()");
- touch_editable->WaitForSelectionChangeCallback();
-
- // Tap inside selection to bring up selection handles.
- generator.GestureTapAt(gfx::Point(bounds.x() + 10, bounds.y() + 10));
- EXPECT_EQ(touch_editable->rwhva_, rwhva);
-
- scoped_ptr<base::Value> value =
- content::ExecuteScriptAndGetValue(view_host, "get_selection()");
- std::string selection;
- value->GetAsString(&selection);
-
- // Check if selection handles are showing.
- EXPECT_TRUE(touch_editable->touch_selection_controller_.get());
- EXPECT_STREQ("Some text we can select", selection.c_str());
-
- // Lets move the handles a bit to modify the selection
- touch_editable->Reset();
- generator.GestureScrollSequence(
- gfx::Point(10, 47),
- gfx::Point(30, 47),
- base::TimeDelta::FromMilliseconds(20),
- 5);
- touch_editable->WaitForSelectionChangeCallback();
-
- EXPECT_TRUE(touch_editable->touch_selection_controller_.get());
- value = content::ExecuteScriptAndGetValue(view_host, "get_selection()");
- value->GetAsString(&selection);
-
- // It is hard to tell what exactly the selection would be now. But it would
- // definitely be less than whatever was selected before.
- EXPECT_GT(std::strlen("Some text we can select"), selection.size());
+ RenderWidgetHostViewAura* GetRenderWidgetHostViewAura(
+ TouchEditableImplAura* touch_editable) {
+ return touch_editable->rwhva_;
}
- void TestTouchSelectionOnLongPress() {
- ASSERT_NO_FATAL_FAILURE(
- StartTestWithPage("files/touch_selection.html"));
- WebContentsImpl* web_contents =
- static_cast<WebContentsImpl*>(shell()->web_contents());
- RenderViewHostImpl* view_host = static_cast<RenderViewHostImpl*>(
- web_contents->GetRenderViewHost());
- WebContentsViewAura* view_aura = static_cast<WebContentsViewAura*>(
- web_contents->GetView());
- TestTouchEditableImplAura* touch_editable = new TestTouchEditableImplAura;
- view_aura->SetTouchEditableForTest(touch_editable);
- RenderWidgetHostViewAura* rwhva = static_cast<RenderWidgetHostViewAura*>(
- web_contents->GetRenderWidgetHostView());
- aura::Window* content = web_contents->GetView()->GetContentNativeView();
- aura::test::EventGenerator generator(content->GetRootWindow(), content);
- gfx::Rect bounds = content->GetBoundsInRootWindow();
- EXPECT_EQ(touch_editable->rwhva_, rwhva);
-
- // Long press to select word.
- ui::GestureEvent long_press(ui::ET_GESTURE_LONG_PRESS,
- 10,
- 10,
- 0,
- ui::EventTimeForNow(),
- ui::GestureEventDetails(
- ui::ET_GESTURE_LONG_PRESS, 0, 0),
- 1);
- touch_editable->Reset();
- rwhva->OnGestureEvent(&long_press);
- touch_editable->WaitForSelectionChangeCallback();
-
- // Check if selection handles are showing.
- ui::TouchSelectionController* controller =
- touch_editable->touch_selection_controller_.get();
- EXPECT_TRUE(controller);
-
- scoped_ptr<base::Value> value =
- content::ExecuteScriptAndGetValue(view_host, "get_selection()");
- std::string selection;
- value->GetAsString(&selection);
- EXPECT_STREQ("Some", selection.c_str());
+ ui::TouchSelectionController* GetTouchSelectionController(
+ TouchEditableImplAura* touch_editable) {
+ return touch_editable->touch_selection_controller_.get();
}
- void TestTouchSelectionHiddenWhenScrolling() {
- ASSERT_NO_FATAL_FAILURE(
- StartTestWithPage("files/touch_selection.html"));
- WebContentsImpl* web_contents =
- static_cast<WebContentsImpl*>(shell()->web_contents());
- RenderViewHostImpl* view_host = static_cast<RenderViewHostImpl*>(
- web_contents->GetRenderViewHost());
- WebContentsViewAura* view_aura = static_cast<WebContentsViewAura*>(
- web_contents->GetView());
- TestTouchEditableImplAura* touch_editable = new TestTouchEditableImplAura;
- view_aura->SetTouchEditableForTest(touch_editable);
- RenderWidgetHostViewAura* rwhva = static_cast<RenderWidgetHostViewAura*>(
- web_contents->GetRenderWidgetHostView());
- aura::Window* content = web_contents->GetView()->GetContentNativeView();
- aura::test::EventGenerator generator(content->GetRootWindow(), content);
- gfx::Rect bounds = content->GetBoundsInRootWindow();
- EXPECT_EQ(touch_editable->rwhva_, rwhva);
-
- // Long press to select word.
- ui::GestureEvent long_press(ui::ET_GESTURE_LONG_PRESS,
- 10,
- 10,
- 0,
- ui::EventTimeForNow(),
- ui::GestureEventDetails(
- ui::ET_GESTURE_LONG_PRESS, 0, 0),
- 1);
- touch_editable->Reset();
- rwhva->OnGestureEvent(&long_press);
- touch_editable->WaitForSelectionChangeCallback();
-
- // Check if selection handles are showing.
- ui::TouchSelectionController* controller =
- touch_editable->touch_selection_controller_.get();
- EXPECT_TRUE(controller);
-
- scoped_ptr<base::Value> value =
- content::ExecuteScriptAndGetValue(view_host, "get_selection()");
- std::string selection;
- value->GetAsString(&selection);
- EXPECT_STREQ("Some", selection.c_str());
-
- // Start scrolling. Handles should get hidden.
- ui::GestureEvent scroll_begin(ui::ET_GESTURE_SCROLL_BEGIN,
- 10,
- 10,
- 0,
- ui::EventTimeForNow(),
- ui::GestureEventDetails(
- ui::ET_GESTURE_LONG_PRESS, 0, 0),
- 1);
- rwhva->OnGestureEvent(&scroll_begin);
- EXPECT_FALSE(touch_editable->touch_selection_controller_.get());
-
- // Handles should come back after scroll ends.
- ui::GestureEvent scroll_end(ui::ET_GESTURE_SCROLL_END,
- 10,
- 10,
- 0,
- ui::EventTimeForNow(),
- ui::GestureEventDetails(
- ui::ET_GESTURE_LONG_PRESS, 0, 0),
- 1);
- rwhva->OnGestureEvent(&scroll_end);
- EXPECT_TRUE(touch_editable->touch_selection_controller_.get());
- }
-
- void TestTouchCursorInTextfield() {
- ASSERT_NO_FATAL_FAILURE(
- StartTestWithPage("files/touch_selection.html"));
- WebContentsImpl* web_contents =
- static_cast<WebContentsImpl*>(shell()->web_contents());
- RenderViewHostImpl* view_host = static_cast<RenderViewHostImpl*>(
- web_contents->GetRenderViewHost());
- WebContentsViewAura* view_aura = static_cast<WebContentsViewAura*>(
- web_contents->GetView());
- TestTouchEditableImplAura* touch_editable =
- new TestTouchEditableImplAuraIgnoreMouseMovement;
- view_aura->SetTouchEditableForTest(touch_editable);
- RenderWidgetHostViewAura* rwhva = static_cast<RenderWidgetHostViewAura*>(
- web_contents->GetRenderWidgetHostView());
- aura::Window* content = web_contents->GetView()->GetContentNativeView();
- aura::test::EventGenerator generator(content->GetRootWindow(), content);
- gfx::Rect bounds = content->GetBoundsInRootWindow();
- EXPECT_EQ(touch_editable->rwhva_, rwhva);
-
- ExecuteSyncJSFunction(view_host, "focus_textfield()");
- touch_editable->WaitForSelectionChangeCallback();
-
- // Tap textfield
- touch_editable->Reset();
- generator.GestureTapAt(gfx::Point(bounds.x() + 50, bounds.y() + 40));
- touch_editable->WaitForSelectionChangeCallback();
- // No Tap Down Ack is coming, it's async.
- touch_editable->Reset();
- touch_editable->WaitForGestureAck(); // Wait for Tap Ack.
-
- // Check if cursor handle is showing.
- ui::TouchSelectionController* controller =
- touch_editable->touch_selection_controller_.get();
- EXPECT_NE(ui::TEXT_INPUT_TYPE_NONE, touch_editable->text_input_type_);
- EXPECT_TRUE(controller);
-
- scoped_ptr<base::Value> value =
- content::ExecuteScriptAndGetValue(view_host, "get_cursor_position()");
- int cursor_pos = -1;
- value->GetAsInteger(&cursor_pos);
- EXPECT_NE(-1, cursor_pos);
-
- // Move the cursor handle.
- generator.GestureScrollSequence(
- gfx::Point(50, 59),
- gfx::Point(10, 59),
- base::TimeDelta::FromMilliseconds(20),
- 1);
- touch_editable->WaitForSelectionChangeCallback();
- EXPECT_TRUE(touch_editable->touch_selection_controller_.get());
- value = content::ExecuteScriptAndGetValue(view_host,
- "get_cursor_position()");
- int new_cursor_pos = -1;
- value->GetAsInteger(&new_cursor_pos);
- EXPECT_NE(-1, new_cursor_pos);
- // Cursor should have moved.
- EXPECT_NE(new_cursor_pos, cursor_pos);
+ ui::TextInputType GetTextInputType(TouchEditableImplAura* touch_editable) {
+ return touch_editable->text_input_type_;
}
private:
@@ -368,22 +138,242 @@ class TouchEditableImplAuraTest : public ContentBrowserTest {
IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest,
TouchSelectionOriginatingFromWebpageTest) {
- TestTouchSelectionOriginatingFromWebpage();
+ ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+ RenderFrameHost* main_frame = web_contents->GetMainFrame();
+ WebContentsViewAura* view_aura = static_cast<WebContentsViewAura*>(
+ web_contents->GetView());
+ TestTouchEditableImplAura* touch_editable = new TestTouchEditableImplAura;
+ view_aura->SetTouchEditableForTest(touch_editable);
+ RenderWidgetHostViewAura* rwhva = static_cast<RenderWidgetHostViewAura*>(
+ web_contents->GetRenderWidgetHostView());
+ aura::Window* content = web_contents->GetContentNativeView();
+ aura::test::EventGenerator generator(content->GetRootWindow(), content);
+ gfx::Rect bounds = content->GetBoundsInRootWindow();
+
+ touch_editable->Reset();
+ ExecuteSyncJSFunction(main_frame, "select_all_text()");
+ touch_editable->WaitForSelectionChangeCallback();
+
+ // Tap inside selection to bring up selection handles.
+ generator.GestureTapAt(gfx::Point(bounds.x() + 10, bounds.y() + 10));
+ EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable), rwhva);
+
+ scoped_ptr<base::Value> value =
+ content::ExecuteScriptAndGetValue(main_frame, "get_selection()");
+ std::string selection;
+ value->GetAsString(&selection);
+
+ // Check if selection handles are showing.
+ EXPECT_TRUE(GetTouchSelectionController(touch_editable));
+ EXPECT_STREQ("Some text we can select", selection.c_str());
+
+ // Lets move the handles a bit to modify the selection
+ touch_editable->Reset();
+ generator.GestureScrollSequence(
+ gfx::Point(10, 47),
+ gfx::Point(30, 47),
+ base::TimeDelta::FromMilliseconds(20),
+ 5);
+ touch_editable->WaitForSelectionChangeCallback();
+
+ EXPECT_TRUE(GetTouchSelectionController(touch_editable));
+ value = content::ExecuteScriptAndGetValue(main_frame, "get_selection()");
+ value->GetAsString(&selection);
+
+ // It is hard to tell what exactly the selection would be now. But it would
+ // definitely be less than whatever was selected before.
+ EXPECT_GT(std::strlen("Some text we can select"), selection.size());
}
IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest,
TestTouchSelectionHiddenWhenScrolling) {
- TestTouchSelectionHiddenWhenScrolling();
+ ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+ RenderFrameHost* main_frame = web_contents->GetMainFrame();
+ WebContentsViewAura* view_aura = static_cast<WebContentsViewAura*>(
+ web_contents->GetView());
+ TestTouchEditableImplAura* touch_editable = new TestTouchEditableImplAura;
+ view_aura->SetTouchEditableForTest(touch_editable);
+ RenderWidgetHostViewAura* rwhva = static_cast<RenderWidgetHostViewAura*>(
+ web_contents->GetRenderWidgetHostView());
+ EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable), rwhva);
+
+ // Long press to select word.
+ ui::GestureEvent long_press(ui::ET_GESTURE_LONG_PRESS,
+ 10,
+ 10,
+ 0,
+ ui::EventTimeForNow(),
+ ui::GestureEventDetails(
+ ui::ET_GESTURE_LONG_PRESS, 0, 0),
+ 1);
+ touch_editable->Reset();
+ rwhva->OnGestureEvent(&long_press);
+ touch_editable->WaitForSelectionChangeCallback();
+
+ // Check if selection handles are showing.
+ EXPECT_TRUE(GetTouchSelectionController(touch_editable));
+
+ scoped_ptr<base::Value> value =
+ content::ExecuteScriptAndGetValue(main_frame, "get_selection()");
+ std::string selection;
+ value->GetAsString(&selection);
+ EXPECT_STREQ("Some", selection.c_str());
+
+ // Start scrolling. Handles should get hidden.
+ ui::GestureEvent scroll_begin(ui::ET_GESTURE_SCROLL_BEGIN,
+ 10,
+ 10,
+ 0,
+ ui::EventTimeForNow(),
+ ui::GestureEventDetails(
+ ui::ET_GESTURE_SCROLL_BEGIN, 0, 0),
+ 1);
+ rwhva->OnGestureEvent(&scroll_begin);
+ EXPECT_FALSE(GetTouchSelectionController(touch_editable));
+
+ // Handles should come back after scroll ends.
+ ui::GestureEvent scroll_end(ui::ET_GESTURE_SCROLL_END,
+ 10,
+ 10,
+ 0,
+ ui::EventTimeForNow(),
+ ui::GestureEventDetails(
+ ui::ET_GESTURE_SCROLL_END, 0, 0),
+ 1);
+ rwhva->OnGestureEvent(&scroll_end);
+ EXPECT_TRUE(GetTouchSelectionController(touch_editable));
}
IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest,
TouchSelectionOnLongPressTest) {
- TestTouchSelectionOnLongPress();
+ ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+ RenderFrameHost* main_frame = web_contents->GetMainFrame();
+ WebContentsViewAura* view_aura = static_cast<WebContentsViewAura*>(
+ web_contents->GetView());
+ TestTouchEditableImplAura* touch_editable = new TestTouchEditableImplAura;
+ view_aura->SetTouchEditableForTest(touch_editable);
+ RenderWidgetHostViewAura* rwhva = static_cast<RenderWidgetHostViewAura*>(
+ web_contents->GetRenderWidgetHostView());
+ EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable), rwhva);
+
+ // Long press to select word.
+ ui::GestureEvent long_press(ui::ET_GESTURE_LONG_PRESS,
+ 10,
+ 10,
+ 0,
+ ui::EventTimeForNow(),
+ ui::GestureEventDetails(
+ ui::ET_GESTURE_LONG_PRESS, 0, 0),
+ 1);
+ touch_editable->Reset();
+ rwhva->OnGestureEvent(&long_press);
+ touch_editable->WaitForSelectionChangeCallback();
+
+ // Check if selection handles are showing.
+ EXPECT_TRUE(GetTouchSelectionController(touch_editable));
+
+ scoped_ptr<base::Value> value =
+ content::ExecuteScriptAndGetValue(main_frame, "get_selection()");
+ std::string selection;
+ value->GetAsString(&selection);
+ EXPECT_STREQ("Some", selection.c_str());
+}
+
+IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest,
+ NoTouchSelectionOnDoubleTapTest) {
+ ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+ RenderFrameHost* main_frame = web_contents->GetMainFrame();
+ WebContentsViewAura* view_aura =
+ static_cast<WebContentsViewAura*>(web_contents->GetView());
+ TestTouchEditableImplAura* touch_editable = new TestTouchEditableImplAura;
+ view_aura->SetTouchEditableForTest(touch_editable);
+ RenderWidgetHostViewAura* rwhva = static_cast<RenderWidgetHostViewAura*>(
+ web_contents->GetRenderWidgetHostView());
+ EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable), rwhva);
+
+ // Double-tap to select word.
+ ui::GestureEvent double_tap(ui::ET_GESTURE_TAP,
+ 10,
+ 10,
+ 0,
+ ui::EventTimeForNow(),
+ ui::GestureEventDetails(ui::ET_GESTURE_TAP, 2, 0),
+ 1);
+ touch_editable->Reset();
+ rwhva->OnGestureEvent(&double_tap);
+ touch_editable->WaitForSelectionChangeCallback();
+
+ // Make sure touch selection handles are not showing.
+ EXPECT_FALSE(GetTouchSelectionController(touch_editable));
+
+ scoped_ptr<base::Value> value =
+ content::ExecuteScriptAndGetValue(main_frame, "get_selection()");
+ std::string selection;
+ value->GetAsString(&selection);
+ EXPECT_STREQ("Some", selection.c_str());
}
IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest,
TouchCursorInTextfieldTest) {
- TestTouchCursorInTextfield();
+ ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+ RenderFrameHost* main_frame = web_contents->GetMainFrame();
+ WebContentsViewAura* view_aura = static_cast<WebContentsViewAura*>(
+ web_contents->GetView());
+ TestTouchEditableImplAura* touch_editable = new TestTouchEditableImplAura;
+ view_aura->SetTouchEditableForTest(touch_editable);
+ RenderWidgetHostViewAura* rwhva = static_cast<RenderWidgetHostViewAura*>(
+ web_contents->GetRenderWidgetHostView());
+ aura::Window* content = web_contents->GetContentNativeView();
+ aura::test::EventGenerator generator(content->GetRootWindow(), content);
+ gfx::Rect bounds = content->GetBoundsInRootWindow();
+ EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable), rwhva);
+
+ ExecuteSyncJSFunction(main_frame, "focus_textfield()");
+ touch_editable->WaitForSelectionChangeCallback();
+
+ // Tap textfield
+ touch_editable->Reset();
+ generator.GestureTapAt(gfx::Point(bounds.x() + 50, bounds.y() + 40));
+ // Tap Down acks are sent synchronously, while Tap acks are asynchronous.
+ touch_editable->WaitForGestureAck(WebInputEvent::GestureTap);
+ touch_editable->WaitForSelectionChangeCallback();
+ touch_editable->Reset();
+
+ // Check if cursor handle is showing.
+ EXPECT_NE(ui::TEXT_INPUT_TYPE_NONE, GetTextInputType(touch_editable));
+ EXPECT_TRUE(GetTouchSelectionController(touch_editable));
+
+ scoped_ptr<base::Value> value =
+ content::ExecuteScriptAndGetValue(main_frame, "get_cursor_position()");
+ int cursor_pos = -1;
+ value->GetAsInteger(&cursor_pos);
+ EXPECT_NE(-1, cursor_pos);
+
+ // Move the cursor handle.
+ generator.GestureScrollSequence(
+ gfx::Point(50, 59),
+ gfx::Point(10, 59),
+ base::TimeDelta::FromMilliseconds(20),
+ 1);
+ touch_editable->WaitForSelectionChangeCallback();
+ EXPECT_TRUE(GetTouchSelectionController(touch_editable));
+ value = content::ExecuteScriptAndGetValue(main_frame,
+ "get_cursor_position()");
+ int new_cursor_pos = -1;
+ value->GetAsInteger(&new_cursor_pos);
+ EXPECT_NE(-1, new_cursor_pos);
+ // Cursor should have moved.
+ EXPECT_NE(new_cursor_pos, cursor_pos);
}
} // namespace content
diff --git a/chromium/content/browser/web_contents/web_contents_android.cc b/chromium/content/browser/web_contents/web_contents_android.cc
index 88922b1922a..fb8c0db8aa5 100644
--- a/chromium/content/browser/web_contents/web_contents_android.cc
+++ b/chromium/content/browser/web_contents/web_contents_android.cc
@@ -5,6 +5,7 @@
#include "content/browser/web_contents/web_contents_android.h"
#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
#include "base/logging.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
@@ -55,4 +56,14 @@ WebContentsAndroid::GetJavaObject() {
return base::android::ScopedJavaLocalRef<jobject>(obj_);
}
+ScopedJavaLocalRef<jstring> WebContentsAndroid::GetTitle(
+ JNIEnv* env, jobject obj) const {
+ return base::android::ConvertUTF16ToJavaString(env,
+ web_contents_->GetTitle());
+}
+
+void WebContentsAndroid::Stop(JNIEnv* env, jobject obj) {
+ web_contents_->Stop();
+}
+
} // namespace content
diff --git a/chromium/content/browser/web_contents/web_contents_android.h b/chromium/content/browser/web_contents/web_contents_android.h
index a9595e2a4dc..eec6127220a 100644
--- a/chromium/content/browser/web_contents/web_contents_android.h
+++ b/chromium/content/browser/web_contents/web_contents_android.h
@@ -33,6 +33,11 @@ class CONTENT_EXPORT WebContentsAndroid
base::android::ScopedJavaLocalRef<jobject> GetJavaObject();
+ // Methods called from Java
+ base::android::ScopedJavaLocalRef<jstring> GetTitle(JNIEnv* env,
+ jobject obj) const;
+ void Stop(JNIEnv* env, jobject obj);
+
private:
WebContents* web_contents_;
NavigationControllerAndroid navigation_controller_;
diff --git a/chromium/content/browser/web_contents/web_contents_drag_win.cc b/chromium/content/browser/web_contents/web_contents_drag_win.cc
deleted file mode 100644
index ffc8713f860..00000000000
--- a/chromium/content/browser/web_contents/web_contents_drag_win.cc
+++ /dev/null
@@ -1,443 +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/web_contents/web_contents_drag_win.h"
-
-#include <windows.h>
-
-#include <string>
-
-#include "base/bind.h"
-#include "base/file_util.h"
-#include "base/files/file_path.h"
-#include "base/message_loop/message_loop.h"
-#include "base/pickle.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/threading/platform_thread.h"
-#include "base/threading/thread.h"
-#include "content/browser/download/drag_download_file.h"
-#include "content/browser/download/drag_download_util.h"
-#include "content/browser/web_contents/web_drag_dest_win.h"
-#include "content/browser/web_contents/web_drag_source_win.h"
-#include "content/browser/web_contents/web_drag_utils_win.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/content_browser_client.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/browser/web_contents_view.h"
-#include "content/public/browser/web_drag_dest_delegate.h"
-#include "content/public/common/drop_data.h"
-#include "net/base/net_util.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "ui/base/clipboard/clipboard.h"
-#include "ui/base/clipboard/custom_data_helper.h"
-#include "ui/base/dragdrop/drag_utils.h"
-#include "ui/base/layout.h"
-#include "ui/base/win/scoped_ole_initializer.h"
-#include "ui/gfx/image/image_skia.h"
-#include "ui/gfx/screen.h"
-#include "ui/gfx/size.h"
-
-using blink::WebDragOperationsMask;
-using blink::WebDragOperationCopy;
-using blink::WebDragOperationLink;
-using blink::WebDragOperationMove;
-
-namespace content {
-namespace {
-
-bool run_do_drag_drop = true;
-
-HHOOK msg_hook = NULL;
-DWORD drag_out_thread_id = 0;
-bool mouse_up_received = false;
-
-LRESULT CALLBACK MsgFilterProc(int code, WPARAM wparam, LPARAM lparam) {
- if (code == base::MessagePumpForUI::kMessageFilterCode &&
- !mouse_up_received) {
- MSG* msg = reinterpret_cast<MSG*>(lparam);
- // We do not care about WM_SYSKEYDOWN and WM_SYSKEYUP because when ALT key
- // is pressed down on drag-and-drop, it means to create a link.
- if (msg->message == WM_MOUSEMOVE || msg->message == WM_LBUTTONUP ||
- msg->message == WM_KEYDOWN || msg->message == WM_KEYUP) {
- // Forward the message from the UI thread to the drag-and-drop thread.
- PostThreadMessage(drag_out_thread_id,
- msg->message,
- msg->wParam,
- msg->lParam);
-
- // If the left button is up, we do not need to forward the message any
- // more.
- if (msg->message == WM_LBUTTONUP || !(GetKeyState(VK_LBUTTON) & 0x8000))
- mouse_up_received = true;
-
- return TRUE;
- }
- }
- return CallNextHookEx(msg_hook, code, wparam, lparam);
-}
-
-void EnableBackgroundDraggingSupport(DWORD thread_id) {
- // Install a hook procedure to monitor the messages so that we can forward
- // the appropriate ones to the background thread.
- drag_out_thread_id = thread_id;
- mouse_up_received = false;
- DCHECK(!msg_hook);
- msg_hook = SetWindowsHookEx(WH_MSGFILTER,
- MsgFilterProc,
- NULL,
- GetCurrentThreadId());
-
- // Attach the input state of the background thread to the UI thread so that
- // SetCursor can work from the background thread.
- AttachThreadInput(drag_out_thread_id, GetCurrentThreadId(), TRUE);
-}
-
-void DisableBackgroundDraggingSupport() {
- DCHECK(msg_hook);
- AttachThreadInput(drag_out_thread_id, GetCurrentThreadId(), FALSE);
- UnhookWindowsHookEx(msg_hook);
- msg_hook = NULL;
-}
-
-bool IsBackgroundDraggingSupportEnabled() {
- return msg_hook != NULL;
-}
-
-} // namespace
-
-class DragDropThread : public base::Thread {
- public:
- explicit DragDropThread(WebContentsDragWin* drag_handler)
- : Thread("Chrome_DragDropThread"),
- drag_handler_(drag_handler) {
- }
-
- virtual ~DragDropThread() {
- Stop();
- }
-
- protected:
- // base::Thread implementations:
- virtual void Init() {
- ole_initializer_.reset(new ui::ScopedOleInitializer());
- }
-
- virtual void CleanUp() {
- ole_initializer_.reset();
- }
-
- private:
- scoped_ptr<ui::ScopedOleInitializer> ole_initializer_;
-
- // Hold a reference count to WebContentsDragWin to make sure that it is always
- // alive in the thread lifetime.
- scoped_refptr<WebContentsDragWin> drag_handler_;
-
- DISALLOW_COPY_AND_ASSIGN(DragDropThread);
-};
-
-WebContentsDragWin::WebContentsDragWin(
- gfx::NativeWindow source_window,
- WebContents* web_contents,
- WebDragDest* drag_dest,
- const base::Callback<void()>& drag_end_callback)
- : drag_drop_thread_id_(0),
- source_window_(source_window),
- web_contents_(web_contents),
- drag_dest_(drag_dest),
- drag_ended_(false),
- drag_end_callback_(drag_end_callback) {
-}
-
-WebContentsDragWin::~WebContentsDragWin() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DCHECK(!drag_drop_thread_.get());
-}
-
-void WebContentsDragWin::StartDragging(const DropData& drop_data,
- WebDragOperationsMask ops,
- const gfx::ImageSkia& image,
- const gfx::Vector2d& image_offset) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- drag_source_ = new WebDragSource(source_window_, web_contents_);
-
- const GURL& page_url = web_contents_->GetLastCommittedURL();
- const std::string& page_encoding = web_contents_->GetEncoding();
-
- // If it is not drag-out, do the drag-and-drop in the current UI thread.
- if (drop_data.download_metadata.empty()) {
- if (DoDragging(drop_data, ops, page_url, page_encoding,
- image, image_offset))
- EndDragging();
- return;
- }
-
- // Start a background thread to do the drag-and-drop.
- DCHECK(!drag_drop_thread_.get());
- drag_drop_thread_.reset(new DragDropThread(this));
- base::Thread::Options options;
- options.message_loop_type = base::MessageLoop::TYPE_UI;
- if (drag_drop_thread_->StartWithOptions(options)) {
- drag_drop_thread_->message_loop()->PostTask(
- FROM_HERE,
- base::Bind(&WebContentsDragWin::StartBackgroundDragging, this,
- drop_data, ops, page_url, page_encoding,
- image, image_offset));
- }
-
- EnableBackgroundDraggingSupport(drag_drop_thread_->thread_id());
-}
-
-void WebContentsDragWin::StartBackgroundDragging(
- const DropData& drop_data,
- WebDragOperationsMask ops,
- const GURL& page_url,
- const std::string& page_encoding,
- const gfx::ImageSkia& image,
- const gfx::Vector2d& image_offset) {
- drag_drop_thread_id_ = base::PlatformThread::CurrentId();
-
- if (DoDragging(drop_data, ops, page_url, page_encoding,
- image, image_offset)) {
- BrowserThread::PostTask(
- BrowserThread::UI,
- FROM_HERE,
- base::Bind(&WebContentsDragWin::EndDragging, this));
- } else {
- // When DoDragging returns false, the contents view window is gone and thus
- // WebContentsViewWin instance becomes invalid though WebContentsDragWin
- // instance is still alive because the task holds a reference count to it.
- // We should not do more than the following cleanup items:
- // 1) Remove the background dragging support. This is safe since it does not
- // access the instance at all.
- // 2) Stop the background thread. This is done in OnDataObjectDisposed.
- // Only drag_drop_thread_ member is accessed.
- BrowserThread::PostTask(
- BrowserThread::UI,
- FROM_HERE,
- base::Bind(&DisableBackgroundDraggingSupport));
- }
-}
-
-void WebContentsDragWin::PrepareDragForDownload(
- const DropData& drop_data,
- ui::OSExchangeData* data,
- const GURL& page_url,
- const std::string& page_encoding) {
- // Parse the download metadata.
- base::string16 mime_type;
- base::FilePath file_name;
- GURL download_url;
- if (!ParseDownloadMetadata(drop_data.download_metadata,
- &mime_type,
- &file_name,
- &download_url))
- return;
-
- // Generate the file name based on both mime type and proposed file name.
- std::string default_name =
- GetContentClient()->browser()->GetDefaultDownloadName();
- base::FilePath generated_download_file_name =
- net::GenerateFileName(download_url,
- std::string(),
- std::string(),
- UTF16ToUTF8(file_name.value()),
- UTF16ToUTF8(mime_type),
- default_name);
- base::FilePath temp_dir_path;
- if (!base::CreateNewTempDirectory(FILE_PATH_LITERAL("chrome_drag"),
- &temp_dir_path))
- return;
- base::FilePath download_path =
- temp_dir_path.Append(generated_download_file_name);
-
- // We cannot know when the target application will be done using the temporary
- // file, so schedule it to be deleted after rebooting.
- base::DeleteFileAfterReboot(download_path);
- base::DeleteFileAfterReboot(temp_dir_path);
-
- // Provide the data as file (CF_HDROP). A temporary download file with the
- // Zone.Identifier ADS (Alternate Data Stream) attached will be created.
- scoped_refptr<DragDownloadFile> download_file =
- new DragDownloadFile(
- download_path,
- scoped_ptr<net::FileStream>(),
- download_url,
- Referrer(page_url, drop_data.referrer_policy),
- page_encoding,
- web_contents_);
- ui::OSExchangeData::DownloadFileInfo file_download(base::FilePath(),
- download_file.get());
- data->SetDownloadFileInfo(file_download);
-
- // Enable asynchronous operation.
- ui::OSExchangeDataProviderWin::GetIAsyncOperation(*data)->SetAsyncMode(TRUE);
-}
-
-void WebContentsDragWin::PrepareDragForFileContents(
- const DropData& drop_data, ui::OSExchangeData* data) {
- static const int kMaxFilenameLength = 255; // FAT and NTFS
- base::FilePath file_name(drop_data.file_description_filename);
-
- // Images without ALT text will only have a file extension so we need to
- // synthesize one from the provided extension and URL.
- if (file_name.BaseName().RemoveExtension().empty()) {
- const base::string16 extension = file_name.Extension();
- // Retrieve the name from the URL.
- file_name = base::FilePath(
- net::GetSuggestedFilename(drop_data.url, "", "", "", "", ""));
- if (file_name.value().size() + extension.size() > kMaxFilenameLength) {
- file_name = base::FilePath(file_name.value().substr(
- 0, kMaxFilenameLength - extension.size()));
- }
- file_name = file_name.ReplaceExtension(extension);
- }
- data->SetFileContents(file_name, drop_data.file_contents);
-}
-
-void WebContentsDragWin::PrepareDragForUrl(const DropData& drop_data,
- ui::OSExchangeData* data) {
- if (drag_dest_->delegate() &&
- drag_dest_->delegate()->AddDragData(drop_data, data)) {
- return;
- }
-
- data->SetURL(drop_data.url, drop_data.url_title);
-}
-
-bool WebContentsDragWin::DoDragging(const DropData& drop_data,
- WebDragOperationsMask ops,
- const GURL& page_url,
- const std::string& page_encoding,
- const gfx::ImageSkia& image,
- const gfx::Vector2d& image_offset) {
- ui::OSExchangeData data;
-
- if (!drop_data.download_metadata.empty()) {
- PrepareDragForDownload(drop_data, &data, page_url, page_encoding);
-
- // Set the observer.
- ui::OSExchangeDataProviderWin::GetDataObjectImpl(data)->set_observer(this);
- }
-
- // We set the file contents before the URL because the URL also sets file
- // contents (to a .URL shortcut). We want to prefer file content data over
- // a shortcut so we add it first.
- if (!drop_data.file_contents.empty())
- PrepareDragForFileContents(drop_data, &data);
- if (!drop_data.html.string().empty())
- data.SetHtml(drop_data.html.string(), drop_data.html_base_url);
- // We set the text contents before the URL because the URL also sets text
- // content.
- if (!drop_data.text.string().empty())
- data.SetString(drop_data.text.string());
- if (drop_data.url.is_valid())
- PrepareDragForUrl(drop_data, &data);
- if (!drop_data.custom_data.empty()) {
- Pickle pickle;
- ui::WriteCustomDataToPickle(drop_data.custom_data, &pickle);
- data.SetPickledData(ui::Clipboard::GetWebCustomDataFormatType(), pickle);
- }
-
- // Set drag image.
- if (!image.isNull()) {
- drag_utils::SetDragImageOnDataObject(image,
- gfx::Size(image.width(), image.height()), image_offset, &data);
- }
-
- // Use a local variable to keep track of the contents view window handle.
- // It might not be safe to access the instance after DoDragDrop returns
- // because the window could be disposed in the nested message loop.
- HWND native_window = web_contents_->GetView()->GetNativeView();
-
- // We need to enable recursive tasks on the message loop so we can get
- // updates while in the system DoDragDrop loop.
- DWORD effect = DROPEFFECT_NONE;
- if (run_do_drag_drop) {
- // Keep a reference count such that |drag_source_| will not get deleted
- // if the contents view window is gone in the nested message loop invoked
- // from DoDragDrop.
- scoped_refptr<WebDragSource> retain_source(drag_source_);
- retain_source->set_data(&data);
- data.SetInDragLoop(true);
-
- base::MessageLoop::ScopedNestableTaskAllower allow(
- base::MessageLoop::current());
- DoDragDrop(ui::OSExchangeDataProviderWin::GetIDataObject(data),
- drag_source_,
- WebDragOpMaskToWinDragOpMask(ops),
- &effect);
- retain_source->set_data(NULL);
- }
-
- // Bail out immediately if the contents view window is gone.
- if (!IsWindow(native_window))
- return false;
-
- // Normally, the drop and dragend events get dispatched in the system
- // DoDragDrop message loop so it'd be too late to set the effect to send back
- // to the renderer here. However, we use PostTask to delay the execution of
- // WebDragSource::OnDragSourceDrop, which means that the delayed dragend
- // callback to the renderer doesn't run until this has been set to the correct
- // value.
- drag_source_->set_effect(effect);
-
- return true;
-}
-
-void WebContentsDragWin::EndDragging() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- if (drag_ended_)
- return;
- drag_ended_ = true;
-
- if (IsBackgroundDraggingSupportEnabled())
- DisableBackgroundDraggingSupport();
-
- drag_end_callback_.Run();
-}
-
-void WebContentsDragWin::CancelDrag() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- drag_source_->CancelDrag();
-}
-
-void WebContentsDragWin::CloseThread() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- drag_drop_thread_.reset();
-}
-
-void WebContentsDragWin::OnWaitForData() {
- DCHECK(drag_drop_thread_id_ == base::PlatformThread::CurrentId());
-
- // When the left button is release and we start to wait for the data, end
- // the dragging before DoDragDrop returns. This makes the page leave the drag
- // mode so that it can start to process the normal input events.
- BrowserThread::PostTask(
- BrowserThread::UI,
- FROM_HERE,
- base::Bind(&WebContentsDragWin::EndDragging, this));
-}
-
-void WebContentsDragWin::OnDataObjectDisposed() {
- DCHECK(drag_drop_thread_id_ == base::PlatformThread::CurrentId());
-
- // The drag-and-drop thread is only closed after OLE is done with
- // DataObjectImpl.
- BrowserThread::PostTask(
- BrowserThread::UI,
- FROM_HERE,
- base::Bind(&WebContentsDragWin::CloseThread, this));
-}
-
-// static
-void WebContentsDragWin::DisableDragDropForTesting() {
- run_do_drag_drop = false;
-}
-
-} // namespace content
diff --git a/chromium/content/browser/web_contents/web_contents_drag_win.h b/chromium/content/browser/web_contents/web_contents_drag_win.h
deleted file mode 100644
index 943cc7d1108..00000000000
--- a/chromium/content/browser/web_contents/web_contents_drag_win.h
+++ /dev/null
@@ -1,122 +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_WEB_CONTENTS_WEB_CONTENTS_DRAG_WIN_H_
-#define CONTENT_BROWSER_WEB_CONTENTS_WEB_CONTENTS_DRAG_WIN_H_
-
-#include "base/callback.h"
-#include "base/compiler_specific.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/threading/platform_thread.h"
-#include "content/common/content_export.h"
-#include "third_party/WebKit/public/web/WebDragOperation.h"
-#include "ui/base/dragdrop/os_exchange_data_provider_win.h"
-#include "ui/gfx/native_widget_types.h"
-#include "ui/gfx/point.h"
-
-class SkBitmap;
-
-namespace gfx {
-class ImageSkia;
-}
-
-namespace content {
-class DragDropThread;
-class WebContents;
-class WebDragDest;
-class WebDragSource;
-struct DropData;
-
-// Windows-specific drag-and-drop handling in WebContentsView.
-// If we are dragging a virtual file out of the browser, we use a background
-// thread to do the drag-and-drop because we do not want to run nested
-// message loop in the UI thread. For all other cases, the drag-and-drop happens
-// in the UI thread.
-class CONTENT_EXPORT WebContentsDragWin
- : NON_EXPORTED_BASE(public ui::DataObjectImpl::Observer),
- public base::RefCountedThreadSafe<WebContentsDragWin> {
- public:
- WebContentsDragWin(gfx::NativeWindow source_window,
- WebContents* web_contents,
- WebDragDest* drag_dest,
- const base::Callback<void()>& drag_end_callback);
- virtual ~WebContentsDragWin();
-
- // Called on UI thread.
- void StartDragging(const DropData& drop_data,
- blink::WebDragOperationsMask ops,
- const gfx::ImageSkia& image,
- const gfx::Vector2d& image_offset);
- void CancelDrag();
-
- // DataObjectImpl::Observer implementation.
- // Called on drag-and-drop thread.
- virtual void OnWaitForData();
- virtual void OnDataObjectDisposed();
-
- // Don't invoke OLE DoDragDrop during tests.
- static void DisableDragDropForTesting();
-
- private:
- // Called on either UI thread or drag-and-drop thread.
- void PrepareDragForDownload(const DropData& drop_data,
- ui::OSExchangeData* data,
- const GURL& page_url,
- const std::string& page_encoding);
- void PrepareDragForFileContents(const DropData& drop_data,
- ui::OSExchangeData* data);
- void PrepareDragForUrl(const DropData& drop_data,
- ui::OSExchangeData* data);
- // Returns false if the source window becomes invalid when the drag ends.
- // This could happen when the window gets destroyed when the drag is still in
- // progress. No further processing should be done beyond this return point
- // because the instance has been destroyed.
- bool DoDragging(const DropData& drop_data,
- blink::WebDragOperationsMask ops,
- const GURL& page_url,
- const std::string& page_encoding,
- const gfx::ImageSkia& image,
- const gfx::Vector2d& image_offset);
-
- // Called on drag-and-drop thread.
- void StartBackgroundDragging(const DropData& drop_data,
- blink::WebDragOperationsMask ops,
- const GURL& page_url,
- const std::string& page_encoding,
- const gfx::ImageSkia& image,
- const gfx::Vector2d& image_offset);
- // Called on UI thread.
- void EndDragging();
- void CloseThread();
-
- // For debug check only. Access only on drag-and-drop thread.
- base::PlatformThreadId drag_drop_thread_id_;
-
- // All the member variables below are accessed on UI thread.
-
- gfx::NativeWindow source_window_;
- WebContents* web_contents_;
- WebDragDest* drag_dest_;
-
- // |drag_source_| is our callback interface passed to the system when we
- // want to initiate a drag and drop operation. We use it to tell if a
- // drag operation is happening.
- scoped_refptr<WebDragSource> drag_source_;
-
- // The thread used by the drag-out download. This is because we want to avoid
- // running nested message loop in main UI thread.
- scoped_ptr<DragDropThread> drag_drop_thread_;
-
- // The flag to guard that EndDragging is not called twice.
- bool drag_ended_;
-
- base::Callback<void()> drag_end_callback_;
-
- DISALLOW_COPY_AND_ASSIGN(WebContentsDragWin);
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_WEB_CONTENTS_WEB_CONTENTS_DRAG_WIN_H_
diff --git a/chromium/content/browser/web_contents/web_contents_impl.cc b/chromium/content/browser/web_contents/web_contents_impl.cc
index 6b6cf00819e..817093a03f6 100644
--- a/chromium/content/browser/web_contents/web_contents_impl.cc
+++ b/chromium/content/browser/web_contents/web_contents_impl.cc
@@ -20,26 +20,32 @@
#include "base/time/time.h"
#include "content/browser/browser_plugin/browser_plugin_embedder.h"
#include "content/browser/browser_plugin/browser_plugin_guest.h"
-#include "content/browser/browser_plugin/browser_plugin_guest_manager.h"
#include "content/browser/child_process_security_policy_impl.h"
-#include "content/browser/devtools/devtools_manager_impl.h"
+#include "content/browser/devtools/render_view_devtools_agent_host.h"
#include "content/browser/dom_storage/dom_storage_context_wrapper.h"
#include "content/browser/dom_storage/session_storage_namespace_impl.h"
#include "content/browser/download/download_stats.h"
#include "content/browser/download/mhtml_generation_manager.h"
#include "content/browser/download/save_package.h"
+#include "content/browser/frame_host/cross_process_frame_connector.h"
#include "content/browser/frame_host/interstitial_page_impl.h"
#include "content/browser/frame_host/navigation_entry_impl.h"
#include "content/browser/frame_host/navigator_impl.h"
#include "content/browser/frame_host/render_frame_host_impl.h"
+#include "content/browser/frame_host/render_widget_host_view_child_frame.h"
+#include "content/browser/geolocation/geolocation_dispatcher_host.h"
#include "content/browser/host_zoom_map_impl.h"
#include "content/browser/loader/resource_dispatcher_host_impl.h"
+#include "content/browser/media/midi_dispatcher_host.h"
#include "content/browser/message_port_message_filter.h"
#include "content/browser/message_port_service.h"
#include "content/browser/power_save_blocker_impl.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
+#include "content/browser/renderer_host/render_view_host_delegate_view.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "content/browser/renderer_host/render_widget_host_view_base.h"
+#include "content/browser/screen_orientation/screen_orientation_dispatcher_host.h"
#include "content/browser/site_instance_impl.h"
#include "content/browser/web_contents/web_contents_view_guest.h"
#include "content/browser/webui/generic_handler.h"
@@ -49,12 +55,12 @@
#include "content/common/browser_plugin/browser_plugin_messages.h"
#include "content/common/frame_messages.h"
#include "content/common/image_messages.h"
+#include "content/common/input_messages.h"
#include "content/common/ssl_status_serialization.h"
#include "content/common/view_messages.h"
-#include "content/port/browser/render_view_host_delegate_view.h"
-#include "content/port/browser/render_widget_host_view_port.h"
+#include "content/public/browser/ax_event_notification_details.h"
#include "content/public/browser/browser_context.h"
-#include "content/public/browser/color_chooser.h"
+#include "content/public/browser/browser_plugin_guest_manager.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/download_manager.h"
@@ -71,7 +77,6 @@
#include "content/public/browser/user_metrics.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/browser/web_contents_observer.h"
-#include "content/public/browser/web_contents_view.h"
#include "content/public/common/bindings_policy.h"
#include "content/public/common/content_constants.h"
#include "content/public/common/content_switches.h"
@@ -93,15 +98,13 @@
#if defined(OS_ANDROID)
#include "content/browser/android/date_time_chooser_android.h"
-#include "content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.h"
+#include "content/browser/media/android/browser_media_player_manager.h"
#include "content/browser/web_contents/web_contents_android.h"
-#include "content/common/java_bridge_messages.h"
#include "content/public/browser/android/content_view_core.h"
#endif
#if defined(OS_MACOSX)
#include "base/mac/foundation_util.h"
-#include "ui/gl/io_surface_support_mac.h"
#endif
// Cross-Site Navigations
@@ -158,6 +161,12 @@
namespace content {
namespace {
+const int kMinimumDelayBetweenLoadingUpdatesMS = 100;
+
+// This matches what Blink's ProgressTracker has traditionally used for a
+// minimum progress value.
+const double kMinimumLoadingProgress = 0.1;
+
const char kDotGoogleDotCom[] = ".google.com";
#if defined(OS_ANDROID)
@@ -167,12 +176,12 @@ const char kWebContentsAndroidKey[] = "web_contents_android";
base::LazyInstance<std::vector<WebContentsImpl::CreatedCallback> >
g_created_callbacks = LAZY_INSTANCE_INITIALIZER;
-static int StartDownload(content::RenderViewHost* rvh,
+static int StartDownload(content::RenderFrameHost* rfh,
const GURL& url,
bool is_favicon,
uint32_t max_bitmap_size) {
static int g_next_image_download_id = 0;
- rvh->Send(new ImageMsg_DownloadImage(rvh->GetRoutingID(),
+ rfh->Send(new ImageMsg_DownloadImage(rfh->GetRoutingID(),
++g_next_image_download_id,
url,
is_favicon,
@@ -180,89 +189,6 @@ static int StartDownload(content::RenderViewHost* rvh,
return g_next_image_download_id;
}
-ViewMsg_Navigate_Type::Value GetNavigationType(
- BrowserContext* browser_context, const NavigationEntryImpl& entry,
- NavigationController::ReloadType reload_type) {
- switch (reload_type) {
- case NavigationControllerImpl::RELOAD:
- return ViewMsg_Navigate_Type::RELOAD;
- case NavigationControllerImpl::RELOAD_IGNORING_CACHE:
- return ViewMsg_Navigate_Type::RELOAD_IGNORING_CACHE;
- case NavigationControllerImpl::RELOAD_ORIGINAL_REQUEST_URL:
- return ViewMsg_Navigate_Type::RELOAD_ORIGINAL_REQUEST_URL;
- case NavigationControllerImpl::NO_RELOAD:
- break; // Fall through to rest of function.
- }
-
- // |RenderViewImpl::PopulateStateFromPendingNavigationParams| differentiates
- // between |RESTORE_WITH_POST| and |RESTORE|.
- if (entry.restore_type() ==
- NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY) {
- if (entry.GetHasPostData())
- return ViewMsg_Navigate_Type::RESTORE_WITH_POST;
- return ViewMsg_Navigate_Type::RESTORE;
- }
-
- return ViewMsg_Navigate_Type::NORMAL;
-}
-
-void MakeNavigateParams(const NavigationEntryImpl& entry,
- const NavigationControllerImpl& controller,
- WebContentsDelegate* delegate,
- NavigationController::ReloadType reload_type,
- ViewMsg_Navigate_Params* params) {
- params->page_id = entry.GetPageID();
- params->should_clear_history_list = entry.should_clear_history_list();
- params->should_replace_current_entry = entry.should_replace_entry();
- if (entry.should_clear_history_list()) {
- // Set the history list related parameters to the same values a
- // NavigationController would return before its first navigation. This will
- // fully clear the RenderView's view of the session history.
- params->pending_history_list_offset = -1;
- params->current_history_list_offset = -1;
- params->current_history_list_length = 0;
- } else {
- params->pending_history_list_offset = controller.GetIndexOfEntry(&entry);
- params->current_history_list_offset =
- controller.GetLastCommittedEntryIndex();
- params->current_history_list_length = controller.GetEntryCount();
- }
- params->url = entry.GetURL();
- if (!entry.GetBaseURLForDataURL().is_empty()) {
- params->base_url_for_data_url = entry.GetBaseURLForDataURL();
- params->history_url_for_data_url = entry.GetVirtualURL();
- }
- params->referrer = entry.GetReferrer();
- params->transition = entry.GetTransitionType();
- params->page_state = entry.GetPageState();
- params->navigation_type =
- GetNavigationType(controller.GetBrowserContext(), entry, reload_type);
- params->request_time = base::Time::Now();
- params->extra_headers = entry.extra_headers();
- params->transferred_request_child_id =
- entry.transferred_global_request_id().child_id;
- params->transferred_request_request_id =
- entry.transferred_global_request_id().request_id;
- params->is_overriding_user_agent = entry.GetIsOverridingUserAgent();
- // Avoid downloading when in view-source mode.
- params->allow_download = !entry.IsViewSourceMode();
- params->is_post = entry.GetHasPostData();
- if (entry.GetBrowserInitiatedPostData()) {
- params->browser_initiated_post_data.assign(
- entry.GetBrowserInitiatedPostData()->front(),
- entry.GetBrowserInitiatedPostData()->front() +
- entry.GetBrowserInitiatedPostData()->size());
- }
-
- params->redirects = entry.redirect_chain();
-
- params->can_load_local_resources = entry.GetCanLoadLocalResources();
- params->frame_to_navigate = entry.GetFrameToNavigate();
-
- if (delegate)
- delegate->AddNavigationHeaders(params->url, &params->extra_headers);
-}
-
void NotifyCacheOnIO(
scoped_refptr<net::URLRequestContextGetter> request_context,
const GURL& url,
@@ -279,6 +205,28 @@ bool CollectSites(BrowserContext* context,
return true;
}
+bool ForEachFrameInternal(
+ const base::Callback<void(RenderFrameHost*)>& on_frame,
+ FrameTreeNode* node) {
+ on_frame.Run(node->current_frame_host());
+ return true;
+}
+
+void SendToAllFramesInternal(IPC::Message* message, RenderFrameHost* rfh) {
+ IPC::Message* message_copy = new IPC::Message(*message);
+ message_copy->set_routing_id(rfh->GetRoutingID());
+ rfh->Send(message_copy);
+}
+
+void AddRenderWidgetHostViewToSet(std::set<RenderWidgetHostView*>* set,
+ RenderFrameHost* rfh) {
+ RenderWidgetHostView* rwhv = static_cast<RenderFrameHostImpl*>(rfh)
+ ->frame_tree_node()
+ ->render_manager()
+ ->GetRenderWidgetHostView();
+ set->insert(rwhv);
+}
+
} // namespace
WebContents* WebContents::Create(const WebContents::CreateParams& params) {
@@ -321,6 +269,13 @@ WebContents* WebContents::FromRenderViewHost(const RenderViewHost* rvh) {
return rvh->GetDelegate()->GetAsWebContents();
}
+WebContents* WebContents::FromRenderFrameHost(RenderFrameHost* rfh) {
+ RenderFrameHostImpl* rfh_impl = static_cast<RenderFrameHostImpl*>(rfh);
+ if (!rfh_impl)
+ return NULL;
+ return rfh_impl->delegate()->GetAsWebContents();
+}
+
// WebContentsImpl::DestructionObserver ----------------------------------------
class WebContentsImpl::DestructionObserver : public WebContentsObserver {
@@ -331,8 +286,9 @@ class WebContentsImpl::DestructionObserver : public WebContentsObserver {
}
// WebContentsObserver:
- virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE {
- owner_->OnWebContentsDestroyed(static_cast<WebContentsImpl*>(web_contents));
+ virtual void WebContentsDestroyed() OVERRIDE {
+ owner_->OnWebContentsDestroyed(
+ static_cast<WebContentsImpl*>(web_contents()));
}
private:
@@ -341,6 +297,19 @@ class WebContentsImpl::DestructionObserver : public WebContentsObserver {
DISALLOW_COPY_AND_ASSIGN(DestructionObserver);
};
+WebContentsImpl::ColorChooserInfo::ColorChooserInfo(int render_process_id,
+ int render_frame_id,
+ ColorChooser* chooser,
+ int identifier)
+ : render_process_id(render_process_id),
+ render_frame_id(render_frame_id),
+ chooser(chooser),
+ identifier(identifier) {
+}
+
+WebContentsImpl::ColorChooserInfo::~ColorChooserInfo() {
+}
+
// WebContentsImpl -------------------------------------------------------------
WebContentsImpl::WebContentsImpl(
@@ -350,32 +319,42 @@ WebContentsImpl::WebContentsImpl(
controller_(this, browser_context),
render_view_host_delegate_view_(NULL),
opener_(opener),
-#if defined(OS_WIN) && defined(USE_AURA)
+ created_with_opener_(!!opener),
+#if defined(OS_WIN)
accessible_parent_(NULL),
#endif
frame_tree_(new NavigatorImpl(&controller_, this),
this, this, this, this),
is_loading_(false),
+ is_load_to_different_document_(false),
crashed_status_(base::TERMINATION_STATUS_STILL_RUNNING),
crashed_error_code_(0),
waiting_for_response_(false),
load_state_(net::LOAD_STATE_IDLE, base::string16()),
+ loading_total_progress_(0.0),
+ loading_weak_factory_(this),
+ loading_frames_in_progress_(0),
upload_size_(0),
upload_position_(0),
displayed_insecure_content_(false),
+ has_accessed_initial_document_(false),
capturer_count_(0),
should_normally_be_visible_(true),
is_being_destroyed_(false),
notify_disconnection_(false),
dialog_manager_(NULL),
is_showing_before_unload_dialog_(false),
+ last_active_time_(base::TimeTicks::Now()),
closed_by_user_gesture_(false),
minimum_zoom_percent_(static_cast<int>(kMinimumZoomFactor * 100)),
maximum_zoom_percent_(static_cast<int>(kMaximumZoomFactor * 100)),
- temporary_zoom_settings_(false),
- color_chooser_identifier_(0),
+ totalPinchGestureAmount_(0),
+ currentPinchZoomStepDelta_(0),
render_view_message_source_(NULL),
- fullscreen_widget_routing_id_(MSG_ROUTING_NONE) {
+ fullscreen_widget_routing_id_(MSG_ROUTING_NONE),
+ is_subframe_(false),
+ touch_emulation_enabled_(false),
+ last_dialog_suppressed_(false) {
for (size_t i = 0; i < g_created_callbacks.Get().size(); i++)
g_created_callbacks.Get().at(i).Run(this);
frame_tree_.SetFrameRemoveListener(
@@ -386,6 +365,11 @@ WebContentsImpl::WebContentsImpl(
WebContentsImpl::~WebContentsImpl() {
is_being_destroyed_ = true;
+ // Delete all RFH pending shutdown, which will lead the corresponding RVH to
+ // shutdown and be deleted as well.
+ frame_tree_.ForEach(
+ base::Bind(&RenderFrameHostManager::ClearRFHsPendingShutdown));
+
ClearAllPowerSaveBlockers();
for (std::set<RenderWidgetHostImpl*>::iterator iter =
@@ -398,8 +382,8 @@ WebContentsImpl::~WebContentsImpl() {
if (dialog_manager_)
dialog_manager_->WebContentsDestroyed(this);
- if (color_chooser_)
- color_chooser_->End();
+ if (color_chooser_info_.get())
+ color_chooser_info_->chooser->End();
NotifyDisconnected();
@@ -409,31 +393,38 @@ WebContentsImpl::~WebContentsImpl() {
Source<WebContents>(this),
NotificationService::NoDetails());
- // TODO(brettw) this should be moved to the view.
-#if defined(OS_WIN) && !defined(USE_AURA)
- // If we still have a window handle, destroy it. GetNativeView can return
- // NULL if this contents was part of a window that closed.
- if (view_->GetNativeView()) {
- RenderViewHost* host = GetRenderViewHost();
- if (host && host->GetView())
- RenderWidgetHostViewPort::FromRWHV(host->GetView())->WillWmDestroy();
+ // Destroy all frame tree nodes except for the root; this notifies observers.
+ frame_tree_.ResetForMainFrameSwap();
+ GetRenderManager()->ResetProxyHosts();
+
+ // Manually call the observer methods for the root frame tree node.
+ RenderFrameHostManager* root = GetRenderManager();
+ if (root->pending_frame_host()) {
+ FOR_EACH_OBSERVER(WebContentsObserver,
+ observers_,
+ RenderFrameDeleted(root->pending_frame_host()));
}
-#endif
+ FOR_EACH_OBSERVER(WebContentsObserver,
+ observers_,
+ RenderFrameDeleted(root->current_frame_host()));
- RenderViewHost* pending_rvh = GetRenderManager()->pending_render_view_host();
- if (pending_rvh) {
+ if (root->pending_render_view_host()) {
FOR_EACH_OBSERVER(WebContentsObserver,
observers_,
- RenderViewDeleted(pending_rvh));
+ RenderViewDeleted(root->pending_render_view_host()));
}
FOR_EACH_OBSERVER(WebContentsObserver,
observers_,
- RenderViewDeleted(GetRenderManager()->current_host()));
+ RenderViewDeleted(root->current_host()));
FOR_EACH_OBSERVER(WebContentsObserver,
observers_,
- WebContentsImplDestroyed());
+ WebContentsDestroyed());
+
+ FOR_EACH_OBSERVER(WebContentsObserver,
+ observers_,
+ ResetWebContents());
SetDelegate(NULL);
@@ -446,36 +437,27 @@ WebContentsImpl* WebContentsImpl::CreateWithOpener(
WebContentsImpl* opener) {
TRACE_EVENT0("browser", "WebContentsImpl::CreateWithOpener");
WebContentsImpl* new_contents = new WebContentsImpl(
- params.browser_context, opener);
+ params.browser_context, params.opener_suppressed ? NULL : opener);
+ if (params.guest_instance_id) {
+ scoped_ptr<base::DictionaryValue> extra_params;
+ if (params.guest_extra_params)
+ extra_params.reset(params.guest_extra_params->DeepCopy());
+ // This makes |new_contents| act as a guest.
+ // For more info, see comment above class BrowserPluginGuest.
+ BrowserPluginGuest::Create(params.guest_instance_id,
+ params.site_instance,
+ new_contents,
+ extra_params.Pass(),
+ opener ? opener->GetBrowserPluginGuest() : NULL);
+ // We are instantiating a WebContents for browser plugin. Set its subframe
+ // bit to true.
+ new_contents->is_subframe_ = true;
+ }
new_contents->Init(params);
return new_contents;
}
-// static
-BrowserPluginGuest* WebContentsImpl::CreateGuest(
- BrowserContext* browser_context,
- SiteInstance* site_instance,
- int guest_instance_id,
- scoped_ptr<base::DictionaryValue> extra_params) {
- WebContentsImpl* new_contents = new WebContentsImpl(browser_context, NULL);
-
- // This makes |new_contents| act as a guest.
- // For more info, see comment above class BrowserPluginGuest.
- BrowserPluginGuest::Create(
- guest_instance_id, site_instance, new_contents, extra_params.Pass());
-
- WebContents::CreateParams create_params(browser_context, site_instance);
- new_contents->Init(create_params);
-
- // We are instantiating a WebContents for browser plugin. Set its subframe bit
- // to true.
- static_cast<RenderViewHostImpl*>(
- new_contents->GetRenderViewHost())->set_is_subframe(true);
-
- return new_contents->browser_plugin_guest_.get();
-}
-
RenderFrameHostManager* WebContentsImpl::GetRenderManagerForTesting() {
return GetRenderManager();
}
@@ -496,59 +478,74 @@ bool WebContentsImpl::OnMessageReceived(RenderViewHost* render_view_host,
ObserverListBase<WebContentsObserver>::Iterator it(observers_);
WebContentsObserver* observer;
- while ((observer = it.GetNext()) != NULL)
- if (observer->OnMessageReceived(message))
- return true;
+ if (render_frame_host) {
+ while ((observer = it.GetNext()) != NULL)
+ if (observer->OnMessageReceived(message, render_frame_host))
+ return true;
+ } else {
+ while ((observer = it.GetNext()) != NULL)
+ if (observer->OnMessageReceived(message))
+ return true;
+ }
+
+ // Message handlers should be aware of which
+ // RenderViewHost/RenderFrameHost sent the message, which is temporarily
+ // stored in render_(view|frame)_message_source_.
+ if (render_frame_host) {
+ if (RenderViewDevToolsAgentHost::DispatchIPCMessage(
+ render_frame_host->GetRenderViewHost(), message))
+ return true;
+ render_frame_message_source_ = render_frame_host;
+ } else {
+ if (RenderViewDevToolsAgentHost::DispatchIPCMessage(
+ render_view_host, message))
+ return true;
+ render_view_message_source_ = render_view_host;
+ }
- // Message handlers should be aware of which RenderViewHost sent the
- // message, which is temporarily stored in render_view_message_source_.
- render_view_message_source_ = render_view_host;
bool handled = true;
- bool message_is_ok = true;
- IPC_BEGIN_MESSAGE_MAP_EX(WebContentsImpl, message, message_is_ok)
+ IPC_BEGIN_MESSAGE_MAP(WebContentsImpl, message)
IPC_MESSAGE_HANDLER(FrameHostMsg_PepperPluginHung, OnPepperPluginHung)
IPC_MESSAGE_HANDLER(FrameHostMsg_PluginCrashed, OnPluginCrashed)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_DomOperationResponse,
+ OnDomOperationResponse)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_DidChangeThemeColor,
+ OnThemeColorChanged)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_DidFinishDocumentLoad,
+ OnDocumentLoadedInFrame)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_DidFinishLoad, OnDidFinishLoad)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_DidStartLoading, OnDidStartLoading)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_DidStopLoading, OnDidStopLoading)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_DidChangeLoadProgress,
+ OnDidChangeLoadProgress)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_OpenColorChooser, OnOpenColorChooser)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_EndColorChooser, OnEndColorChooser)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_SetSelectedColorInColorChooser,
+ OnSetSelectedColorInColorChooser)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_MediaPlayingNotification,
+ OnMediaPlayingNotification)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_MediaPausedNotification,
+ OnMediaPausedNotification)
IPC_MESSAGE_HANDLER(ViewHostMsg_DidLoadResourceFromMemoryCache,
OnDidLoadResourceFromMemoryCache)
IPC_MESSAGE_HANDLER(ViewHostMsg_DidDisplayInsecureContent,
OnDidDisplayInsecureContent)
IPC_MESSAGE_HANDLER(ViewHostMsg_DidRunInsecureContent,
OnDidRunInsecureContent)
- IPC_MESSAGE_HANDLER(ViewHostMsg_DocumentLoadedInFrame,
- OnDocumentLoadedInFrame)
- IPC_MESSAGE_HANDLER(ViewHostMsg_DidFinishLoad, OnDidFinishLoad)
- IPC_MESSAGE_HANDLER(ViewHostMsg_DidFailLoadWithError,
- OnDidFailLoadWithError)
IPC_MESSAGE_HANDLER(ViewHostMsg_GoToEntryAtOffset, OnGoToEntryAtOffset)
IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateZoomLimits, OnUpdateZoomLimits)
IPC_MESSAGE_HANDLER(ViewHostMsg_EnumerateDirectory, OnEnumerateDirectory)
- IPC_MESSAGE_HANDLER(ViewHostMsg_JSOutOfMemory, OnJSOutOfMemory)
IPC_MESSAGE_HANDLER(ViewHostMsg_RegisterProtocolHandler,
OnRegisterProtocolHandler)
IPC_MESSAGE_HANDLER(ViewHostMsg_Find_Reply, OnFindReply)
IPC_MESSAGE_HANDLER(ViewHostMsg_AppCacheAccessed, OnAppCacheAccessed)
- IPC_MESSAGE_HANDLER(ViewHostMsg_OpenColorChooser, OnOpenColorChooser)
- IPC_MESSAGE_HANDLER(ViewHostMsg_EndColorChooser, OnEndColorChooser)
- IPC_MESSAGE_HANDLER(ViewHostMsg_SetSelectedColorInColorChooser,
- OnSetSelectedColorInColorChooser)
IPC_MESSAGE_HANDLER(ViewHostMsg_WebUISend, OnWebUISend)
IPC_MESSAGE_HANDLER(ViewHostMsg_RequestPpapiBrokerPermission,
OnRequestPpapiBrokerPermission)
- IPC_MESSAGE_HANDLER_GENERIC(BrowserPluginHostMsg_AllocateInstanceID,
- OnBrowserPluginMessage(message))
IPC_MESSAGE_HANDLER_GENERIC(BrowserPluginHostMsg_Attach,
OnBrowserPluginMessage(message))
IPC_MESSAGE_HANDLER(ImageHostMsg_DidDownloadImage, OnDidDownloadImage)
IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateFaviconURL, OnUpdateFaviconURL)
-#if defined(OS_ANDROID)
- IPC_MESSAGE_HANDLER(ViewHostMsg_FindMatchRects_Reply,
- OnFindMatchRectsReply)
- IPC_MESSAGE_HANDLER(ViewHostMsg_OpenDateTimeDialog,
- OnOpenDateTimeDialog)
- IPC_MESSAGE_HANDLER_DELAY_REPLY(JavaBridgeHostMsg_GetChannelHandle,
- OnJavaBridgeGetChannelHandle)
-#endif
- IPC_MESSAGE_HANDLER(ViewHostMsg_MediaNotification, OnMediaNotification)
IPC_MESSAGE_HANDLER(ViewHostMsg_DidFirstVisuallyNonEmptyPaint,
OnFirstVisuallyNonEmptyPaint)
IPC_MESSAGE_HANDLER(ViewHostMsg_ShowValidationMessage,
@@ -557,14 +554,16 @@ bool WebContentsImpl::OnMessageReceived(RenderViewHost* render_view_host,
OnHideValidationMessage)
IPC_MESSAGE_HANDLER(ViewHostMsg_MoveValidationMessage,
OnMoveValidationMessage)
+#if defined(OS_ANDROID)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_FindMatchRects_Reply,
+ OnFindMatchRectsReply)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_OpenDateTimeDialog,
+ OnOpenDateTimeDialog)
+#endif
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
render_view_message_source_ = NULL;
-
- if (!message_is_ok) {
- RecordAction(UserMetricsAction("BadMessageTerminate_RVD"));
- GetRenderProcessHost()->ReceivedBadMessage();
- }
+ render_frame_message_source_ = NULL;
return handled;
}
@@ -621,7 +620,7 @@ void WebContentsImpl::SetDelegate(WebContentsDelegate* delegate) {
delegate_->Attach(this);
// Ensure the visible RVH reflects the new delegate's preferences.
if (view_)
- view_->SetOverscrollControllerEnabled(delegate->CanOverscrollContent());
+ view_->SetOverscrollControllerEnabled(CanOverscrollContent());
}
}
@@ -631,36 +630,27 @@ RenderProcessHost* WebContentsImpl::GetRenderProcessHost() const {
}
RenderFrameHost* WebContentsImpl::GetMainFrame() {
- return frame_tree_.root()->render_frame_host();
+ return frame_tree_.root()->current_frame_host();
}
-RenderViewHost* WebContentsImpl::GetRenderViewHost() const {
- return GetRenderManager()->current_host();
+RenderFrameHost* WebContentsImpl::GetFocusedFrame() {
+ if (!frame_tree_.GetFocusedFrame())
+ return NULL;
+ return frame_tree_.GetFocusedFrame()->current_frame_host();
}
-void WebContentsImpl::GetRenderViewHostAtPosition(
- int x,
- int y,
- const base::Callback<void(RenderViewHost*, int, int)>& callback) {
- BrowserPluginEmbedder* embedder = GetBrowserPluginEmbedder();
- if (embedder)
- embedder->GetRenderViewHostAtPosition(x, y, callback);
- else
- callback.Run(GetRenderViewHost(), x, y);
+void WebContentsImpl::ForEachFrame(
+ const base::Callback<void(RenderFrameHost*)>& on_frame) {
+ frame_tree_.ForEach(base::Bind(&ForEachFrameInternal, on_frame));
}
-WebContents* WebContentsImpl::GetEmbedderWebContents() const {
- BrowserPluginGuest* guest = GetBrowserPluginGuest();
- if (guest)
- return guest->embedder_web_contents();
- return NULL;
+void WebContentsImpl::SendToAllFrames(IPC::Message* message) {
+ ForEachFrame(base::Bind(&SendToAllFramesInternal, message));
+ delete message;
}
-int WebContentsImpl::GetEmbeddedInstanceID() const {
- BrowserPluginGuest* guest = GetBrowserPluginGuest();
- if (guest)
- return guest->instance_id();
- return 0;
+RenderViewHost* WebContentsImpl::GetRenderViewHost() const {
+ return GetRenderManager()->current_host();
}
int WebContentsImpl::GetRoutingID() const {
@@ -678,14 +668,6 @@ RenderWidgetHostView* WebContentsImpl::GetRenderWidgetHostView() const {
return GetRenderManager()->GetRenderWidgetHostView();
}
-RenderWidgetHostViewPort* WebContentsImpl::GetRenderWidgetHostViewPort() const {
- BrowserPluginGuest* guest = GetBrowserPluginGuest();
- if (guest && guest->embedder_web_contents()) {
- return guest->embedder_web_contents()->GetRenderWidgetHostViewPort();
- }
- return RenderWidgetHostViewPort::FromRWHV(GetRenderWidgetHostView());
-}
-
RenderWidgetHostView* WebContentsImpl::GetFullscreenRenderWidgetHostView()
const {
RenderWidgetHost* const widget_host =
@@ -746,7 +728,7 @@ const std::string& WebContentsImpl::GetUserAgentOverride() const {
return renderer_preferences_.user_agent_override;
}
-#if defined(OS_WIN) && defined(USE_AURA)
+#if defined(OS_WIN)
void WebContentsImpl::SetParentNativeViewAccessible(
gfx::NativeViewAccessible accessible_parent) {
accessible_parent_ = accessible_parent;
@@ -852,8 +834,12 @@ bool WebContentsImpl::IsLoading() const {
return is_loading_;
}
+bool WebContentsImpl::IsLoadingToDifferentDocument() const {
+ return is_loading_ && is_load_to_different_document_;
+}
+
bool WebContentsImpl::IsWaitingForResponse() const {
- return waiting_for_response_;
+ return waiting_for_response_ && is_load_to_different_document_;
}
const net::LoadStateWithParam& WebContentsImpl::GetLoadState() const {
@@ -874,25 +860,32 @@ uint64 WebContentsImpl::GetUploadPosition() const {
std::set<GURL> WebContentsImpl::GetSitesInTab() const {
std::set<GURL> sites;
- frame_tree_.ForEach(Bind(&CollectSites,
- base::Unretained(GetBrowserContext()),
- base::Unretained(&sites)));
+ frame_tree_.ForEach(base::Bind(&CollectSites,
+ base::Unretained(GetBrowserContext()),
+ base::Unretained(&sites)));
return sites;
}
const std::string& WebContentsImpl::GetEncoding() const {
- return encoding_;
+ return canonical_encoding_;
}
bool WebContentsImpl::DisplayedInsecureContent() const {
return displayed_insecure_content_;
}
-void WebContentsImpl::IncrementCapturerCount() {
+void WebContentsImpl::IncrementCapturerCount(const gfx::Size& capture_size) {
DCHECK(!is_being_destroyed_);
++capturer_count_;
DVLOG(1) << "There are now " << capturer_count_
<< " capturing(s) of WebContentsImpl@" << this;
+
+ // Note: This provides a hint to upstream code to size the views optimally
+ // for quality (e.g., to avoid scaling).
+ if (!capture_size.IsEmpty() && preferred_size_for_capture_.IsEmpty()) {
+ preferred_size_for_capture_ = capture_size;
+ OnPreferredSizeChanged(preferred_size_);
+ }
}
void WebContentsImpl::DecrementCapturerCount() {
@@ -904,6 +897,12 @@ void WebContentsImpl::DecrementCapturerCount() {
if (is_being_destroyed_)
return;
+ if (capturer_count_ == 0) {
+ const gfx::Size old_size = preferred_size_for_capture_;
+ preferred_size_for_capture_ = gfx::Size();
+ OnPreferredSizeChanged(old_size);
+ }
+
if (IsHidden()) {
DVLOG(1) << "Executing delayed WasHidden().";
WasHidden();
@@ -943,22 +942,26 @@ void WebContentsImpl::NotifyNavigationStateChanged(unsigned changed_flags) {
delegate_->NavigationStateChanged(this, changed_flags);
}
-base::TimeTicks WebContentsImpl::GetLastSelectedTime() const {
- return last_selected_time_;
+base::TimeTicks WebContentsImpl::GetLastActiveTime() const {
+ return last_active_time_;
}
void WebContentsImpl::WasShown() {
controller_.SetActive(true);
- RenderWidgetHostViewPort* rwhv =
- RenderWidgetHostViewPort::FromRWHV(GetRenderWidgetHostView());
- if (rwhv) {
- rwhv->Show();
+
+ std::set<RenderWidgetHostView*> widgets = GetRenderWidgetHostViewsInTree();
+ for (std::set<RenderWidgetHostView*>::iterator iter = widgets.begin();
+ iter != widgets.end();
+ iter++) {
+ if (*iter) {
+ (*iter)->Show();
#if defined(OS_MACOSX)
- rwhv->SetActive(true);
+ (*iter)->SetActive(true);
#endif
+ }
}
- last_selected_time_ = base::TimeTicks::Now();
+ last_active_time_ = base::TimeTicks::Now();
// The resize rect might have changed while this was inactive -- send the new
// one to make sure it's up to date.
@@ -983,10 +986,13 @@ void WebContentsImpl::WasHidden() {
// removes the |GetRenderViewHost()|; then when we actually destroy the
// window, OnWindowPosChanged() notices and calls WasHidden() (which
// calls us).
- RenderWidgetHostViewPort* rwhv =
- RenderWidgetHostViewPort::FromRWHV(GetRenderWidgetHostView());
- if (rwhv)
- rwhv->Hide();
+ std::set<RenderWidgetHostView*> widgets = GetRenderWidgetHostViewsInTree();
+ for (std::set<RenderWidgetHostView*>::iterator iter = widgets.begin();
+ iter != widgets.end();
+ iter++) {
+ if (*iter)
+ (*iter)->Hide();
+ }
}
FOR_EACH_OBSERVER(WebContentsObserver, observers_, WasHidden());
@@ -1002,6 +1008,11 @@ bool WebContentsImpl::NeedToFireBeforeUnload() {
GetRenderViewHost())->SuddenTerminationAllowed();
}
+void WebContentsImpl::DispatchBeforeUnload(bool for_cross_site_transition) {
+ static_cast<RenderFrameHostImpl*>(GetMainFrame())->DispatchBeforeUnload(
+ for_cross_site_transition);
+}
+
void WebContentsImpl::Stop() {
GetRenderManager()->Stop();
FOR_EACH_OBSERVER(WebContentsObserver, observers_, NavigationStopped());
@@ -1012,7 +1023,7 @@ WebContents* WebContentsImpl::Clone() {
// We pass our own opener so that the cloned page can access it if it was
// before.
CreateParams create_params(GetBrowserContext(), GetSiteInstance());
- create_params.initial_size = view_->GetContainerSize();
+ create_params.initial_size = GetContainerBounds().size();
WebContentsImpl* tc = CreateWithOpener(create_params, opener_);
tc->GetController().CopyStateFrom(controller_);
FOR_EACH_OBSERVER(WebContentsObserver,
@@ -1055,30 +1066,24 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params) {
params.browser_context, params.site_instance, params.routing_id,
params.main_frame_routing_id);
- view_.reset(GetContentClient()->browser()->
- OverrideCreateWebContentsView(this, &render_view_host_delegate_view_));
- if (view_) {
- CHECK(render_view_host_delegate_view_);
+ WebContentsViewDelegate* delegate =
+ GetContentClient()->browser()->GetWebContentsViewDelegate(this);
+
+ if (browser_plugin_guest_) {
+ scoped_ptr<WebContentsView> platform_view(CreateWebContentsView(
+ this, delegate, &render_view_host_delegate_view_));
+
+ WebContentsViewGuest* rv = new WebContentsViewGuest(
+ this, browser_plugin_guest_.get(), platform_view.Pass(),
+ render_view_host_delegate_view_);
+ render_view_host_delegate_view_ = rv;
+ view_.reset(rv);
} else {
- WebContentsViewDelegate* delegate =
- GetContentClient()->browser()->GetWebContentsViewDelegate(this);
-
- if (browser_plugin_guest_) {
- scoped_ptr<WebContentsViewPort> platform_view(CreateWebContentsView(
- this, delegate, &render_view_host_delegate_view_));
-
- WebContentsViewGuest* rv = new WebContentsViewGuest(
- this, browser_plugin_guest_.get(), platform_view.Pass(),
- render_view_host_delegate_view_);
- render_view_host_delegate_view_ = rv;
- view_.reset(rv);
- } else {
- // Regular WebContentsView.
- view_.reset(CreateWebContentsView(
- this, delegate, &render_view_host_delegate_view_));
- }
- CHECK(render_view_host_delegate_view_);
+ // Regular WebContentsView.
+ view_.reset(CreateWebContentsView(
+ this, delegate, &render_view_host_delegate_view_));
}
+ CHECK(render_view_host_delegate_view_);
CHECK(view_.get());
gfx::Size initial_size = params.initial_size;
@@ -1091,9 +1096,14 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params) {
registrar_.Add(this,
NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
NotificationService::AllBrowserContextsAndSources());
+
+ geolocation_dispatcher_host_.reset(new GeolocationDispatcherHost(this));
+ midi_dispatcher_host_.reset(new MidiDispatcherHost(this));
+
+ screen_orientation_dispatcher_host_.reset(
+ new ScreenOrientationDispatcherHost(this));
+
#if defined(OS_ANDROID)
- java_bridge_dispatcher_host_manager_.reset(
- new JavaBridgeDispatcherHostManager(this));
date_time_chooser_.reset(new DateTimeChooserAndroid());
#endif
}
@@ -1142,6 +1152,18 @@ void WebContentsImpl::RemoveObserver(WebContentsObserver* observer) {
observers_.RemoveObserver(observer);
}
+std::set<RenderWidgetHostView*>
+WebContentsImpl::GetRenderWidgetHostViewsInTree() {
+ std::set<RenderWidgetHostView*> set;
+ if (ShowingInterstitialPage()) {
+ set.insert(GetRenderWidgetHostView());
+ } else {
+ ForEachFrame(
+ base::Bind(&AddRenderWidgetHostViewToSet, base::Unretained(&set)));
+ }
+ return set;
+}
+
void WebContentsImpl::Activate() {
if (delegate_)
delegate_->ActivateContents(this);
@@ -1189,16 +1211,11 @@ bool WebContentsImpl::PreHandleKeyboardEvent(
}
void WebContentsImpl::HandleKeyboardEvent(const NativeWebKeyboardEvent& event) {
- if (browser_plugin_embedder_ &&
- browser_plugin_embedder_->HandleKeyboardEvent(event)) {
- return;
- }
-
if (delegate_)
delegate_->HandleKeyboardEvent(this, event);
}
-bool WebContentsImpl::PreHandleWheelEvent(
+bool WebContentsImpl::HandleWheelEvent(
const blink::WebMouseWheelEvent& event) {
#if !defined(OS_MACOSX)
// On platforms other than Mac, control+mousewheel changes zoom. On Mac, this
@@ -1209,16 +1226,63 @@ bool WebContentsImpl::PreHandleWheelEvent(
// with control key set which isn't what the user wants
if (delegate_ &&
event.wheelTicksY &&
- (event.modifiers & blink::WebInputEvent::ControlKey)) {
+ (event.modifiers & blink::WebInputEvent::ControlKey) &&
+ // Avoid adjusting the zoom in response to two-finger-scrolling touchpad
+ // gestures, which are regrettably easy to trigger accidentally.
+ !event.hasPreciseScrollingDeltas) {
delegate_->ContentsZoomChange(event.wheelTicksY > 0);
return true;
}
#endif
+ return false;
+}
+
+bool WebContentsImpl::PreHandleGestureEvent(
+ const blink::WebGestureEvent& event) {
+ return delegate_ && delegate_->PreHandleGestureEvent(this, event);
+}
+
+bool WebContentsImpl::HandleGestureEvent(
+ const blink::WebGestureEvent& event) {
+ // Some platforms (eg. Mac) send GesturePinch events for trackpad pinch-zoom.
+ // Use them to implement browser zoom, as for HandleWheelEvent above.
+ if (event.type == blink::WebInputEvent::GesturePinchUpdate &&
+ event.sourceDevice == blink::WebGestureDeviceTouchpad) {
+ // The scale difference necessary to trigger a zoom action. Derived from
+ // experimentation to find a value that feels reasonable.
+ const float kZoomStepValue = 0.6f;
+
+ // Find the (absolute) thresholds on either side of the current zoom factor,
+ // then convert those to actual numbers to trigger a zoom in or out.
+ // This logic deliberately makes the range around the starting zoom value
+ // for the gesture twice as large as the other ranges (i.e., the notches are
+ // at ..., -3*step, -2*step, -step, step, 2*step, 3*step, ... but not at 0)
+ // so that it's easier to get back to your starting point than it is to
+ // overshoot.
+ float nextStep = (abs(currentPinchZoomStepDelta_) + 1) * kZoomStepValue;
+ float backStep = abs(currentPinchZoomStepDelta_) * kZoomStepValue;
+ float zoomInThreshold = (currentPinchZoomStepDelta_ >= 0) ? nextStep
+ : -backStep;
+ float zoomOutThreshold = (currentPinchZoomStepDelta_ <= 0) ? -nextStep
+ : backStep;
+
+ totalPinchGestureAmount_ += (event.data.pinchUpdate.scale - 1.0);
+ if (totalPinchGestureAmount_ > zoomInThreshold) {
+ currentPinchZoomStepDelta_++;
+ if (delegate_)
+ delegate_->ContentsZoomChange(true);
+ } else if (totalPinchGestureAmount_ < zoomOutThreshold) {
+ currentPinchZoomStepDelta_--;
+ if (delegate_)
+ delegate_->ContentsZoomChange(false);
+ }
+ return true;
+ }
return false;
}
-#if defined(OS_WIN) && defined(USE_AURA)
+#if defined(OS_WIN)
gfx::NativeViewAccessible WebContentsImpl::GetParentNativeViewAccessible() {
return accessible_parent_;
}
@@ -1259,6 +1323,10 @@ void WebContentsImpl::ToggleFullscreenMode(bool enter_fullscreen) {
if (delegate_)
delegate_->ToggleFullscreenModeForTab(this, enter_fullscreen);
+
+ FOR_EACH_OBSERVER(WebContentsObserver,
+ observers_,
+ DidToggleFullscreenModeForTab(IsFullscreenForCurrentTab()));
}
bool WebContentsImpl::IsFullscreenForCurrentTab() const {
@@ -1289,7 +1357,7 @@ void WebContentsImpl::CreateNewWindow(
// script-related windows), by passing in the current SiteInstance. However,
// if the opener is being suppressed (in a non-guest), we create a new
// SiteInstance in its own BrowsingInstance.
- bool is_guest = GetRenderProcessHost()->IsGuest();
+ bool is_guest = BrowserPluginGuest::IsGuest(this);
// If the opener is to be suppressed, the new window can be in any process.
// Since routing ids are process specific, we must not have one passed in
@@ -1309,7 +1377,7 @@ void WebContentsImpl::CreateNewWindow(
RenderProcessHost::FromID(render_process_id)->GetHandle();
if (process_handle != base::kNullProcessHandle) {
RecordAction(
- UserMetricsAction("Terminate_ProcessMismatch_CreateNewWindow"));
+ base::UserMetricsAction("Terminate_ProcessMismatch_CreateNewWindow"));
base::KillProcess(process_handle, content::RESULT_CODE_KILLED, false);
}
return;
@@ -1338,6 +1406,12 @@ void WebContentsImpl::CreateNewWindow(
params.target_url,
partition_id,
session_storage_namespace)) {
+ if (route_id != MSG_ROUTING_NONE &&
+ !RenderViewHost::FromID(render_process_id, route_id)) {
+ // If the embedder didn't create a WebContents for this route, we need to
+ // delete the RenderView that had already been created.
+ Send(new ViewMsg_Close(route_id));
+ }
GetRenderViewHost()->GetProcess()->ResumeRequestsForView(route_id);
GetRenderViewHost()->GetProcess()->ResumeRequestsForView(
main_frame_route_id);
@@ -1346,39 +1420,33 @@ void WebContentsImpl::CreateNewWindow(
// Create the new web contents. This will automatically create the new
// WebContentsView. In the future, we may want to create the view separately.
- WebContentsImpl* new_contents =
- new WebContentsImpl(GetBrowserContext(),
- params.opener_suppressed ? NULL : this);
-
- new_contents->GetController().SetSessionStorageNamespace(
- partition_id,
- session_storage_namespace);
CreateParams create_params(GetBrowserContext(), site_instance.get());
create_params.routing_id = route_id;
create_params.main_frame_routing_id = main_frame_route_id;
+ create_params.opener = this;
+ create_params.opener_suppressed = params.opener_suppressed;
+ if (params.disposition == NEW_BACKGROUND_TAB)
+ create_params.initially_hidden = true;
+
if (!is_guest) {
create_params.context = view_->GetNativeView();
- create_params.initial_size = view_->GetContainerSize();
+ create_params.initial_size = GetContainerBounds().size();
} else {
- // This makes |new_contents| act as a guest.
- // For more info, see comment above class BrowserPluginGuest.
- int instance_id = GetBrowserPluginGuestManager()->get_next_instance_id();
- WebContentsImpl* new_contents_impl =
- static_cast<WebContentsImpl*>(new_contents);
- BrowserPluginGuest::CreateWithOpener(instance_id,
- new_contents_impl->opener() != NULL,
- new_contents_impl,
- GetBrowserPluginGuest());
+ create_params.guest_instance_id =
+ GetBrowserContext()->GetGuestManager()->GetNextInstanceID();
}
- if (params.disposition == NEW_BACKGROUND_TAB)
- create_params.initially_hidden = true;
- new_contents->Init(create_params);
+ WebContentsImpl* new_contents = static_cast<WebContentsImpl*>(
+ WebContents::Create(create_params));
+ new_contents->GetController().SetSessionStorageNamespace(
+ partition_id,
+ session_storage_namespace);
+ new_contents->RenderViewCreated(new_contents->GetRenderViewHost());
// Save the window for later if we're not suppressing the opener (since it
// will be shown immediately).
if (!params.opener_suppressed) {
if (!is_guest) {
- WebContentsViewPort* new_view = new_contents->view_.get();
+ WebContentsView* new_view = new_contents->view_.get();
// TODO(brettw): It seems bogus that we have to call this function on the
// newly created object and give it one of its own member variables.
@@ -1393,7 +1461,7 @@ void WebContentsImpl::CreateNewWindow(
if (delegate_) {
delegate_->WebContentsCreated(
- this, params.opener_frame_id, params.frame_name,
+ this, params.opener_render_frame_id, params.frame_name,
params.target_url, new_contents);
}
@@ -1443,7 +1511,7 @@ void WebContentsImpl::CreateNewWidget(int render_process_id,
RenderProcessHost::FromID(render_process_id)->GetHandle();
if (process_handle != base::kNullProcessHandle) {
RecordAction(
- UserMetricsAction("Terminate_ProcessMismatch_CreateNewWidget"));
+ base::UserMetricsAction("Terminate_ProcessMismatch_CreateNewWidget"));
base::KillProcess(process_handle, content::RESULT_CODE_KILLED, false);
}
return;
@@ -1453,8 +1521,9 @@ void WebContentsImpl::CreateNewWidget(int render_process_id,
new RenderWidgetHostImpl(this, process, route_id, IsHidden());
created_widgets_.insert(widget_host);
- RenderWidgetHostViewPort* widget_view = RenderWidgetHostViewPort::FromRWHV(
- view_->CreateViewForPopupWidget(widget_host));
+ RenderWidgetHostViewBase* widget_view =
+ static_cast<RenderWidgetHostViewBase*>(
+ view_->CreateViewForPopupWidget(widget_host));
if (!widget_view)
return;
if (!is_fullscreen) {
@@ -1497,13 +1566,19 @@ void WebContentsImpl::ShowCreatedFullscreenWidget(int route_id) {
void WebContentsImpl::ShowCreatedWidget(int route_id,
bool is_fullscreen,
const gfx::Rect& initial_pos) {
- if (delegate_)
- delegate_->RenderWidgetShowing();
-
- RenderWidgetHostViewPort* widget_host_view =
- RenderWidgetHostViewPort::FromRWHV(GetCreatedWidget(route_id));
+ RenderWidgetHostViewBase* widget_host_view =
+ static_cast<RenderWidgetHostViewBase*>(GetCreatedWidget(route_id));
if (!widget_host_view)
return;
+
+ RenderWidgetHostView* view = NULL;
+ BrowserPluginGuest* guest = GetBrowserPluginGuest();
+ if (guest && guest->embedder_web_contents()) {
+ view = guest->embedder_web_contents()->GetRenderWidgetHostView();
+ } else {
+ view = GetRenderWidgetHostView();
+ }
+
if (is_fullscreen) {
DCHECK_EQ(MSG_ROUTING_NONE, fullscreen_widget_routing_id_);
fullscreen_widget_routing_id_ = route_id;
@@ -1511,7 +1586,7 @@ void WebContentsImpl::ShowCreatedWidget(int route_id,
widget_host_view->InitAsChild(GetRenderWidgetHostView()->GetNativeView());
delegate_->ToggleFullscreenModeForTab(this, true);
} else {
- widget_host_view->InitAsFullscreen(GetRenderWidgetHostViewPort());
+ widget_host_view->InitAsFullscreen(view);
}
FOR_EACH_OBSERVER(WebContentsObserver,
observers_,
@@ -1519,7 +1594,7 @@ void WebContentsImpl::ShowCreatedWidget(int route_id,
if (!widget_host_view->HasFocus())
widget_host_view->Focus();
} else {
- widget_host_view->InitAsPopup(GetRenderWidgetHostViewPort(), initial_pos);
+ widget_host_view->InitAsPopup(view, initial_pos);
}
RenderWidgetHostImpl* render_widget_host_impl =
@@ -1551,7 +1626,7 @@ WebContentsImpl* WebContentsImpl::GetCreatedWindow(int route_id) {
RemoveDestructionObserver(new_contents);
// Don't initialize the guest WebContents immediately.
- if (new_contents->GetRenderProcessHost()->IsGuest())
+ if (BrowserPluginGuest::IsGuest(new_contents))
return new_contents;
if (!new_contents->GetRenderProcessHost()->HasConnection() ||
@@ -1582,21 +1657,16 @@ RenderWidgetHostView* WebContentsImpl::GetCreatedWidget(int route_id) {
return widget_host_view;
}
-void WebContentsImpl::ShowContextMenu(const ContextMenuParams& params) {
- // Allow WebContentsDelegates to handle the context menu operation first.
- if (delegate_ && delegate_->HandleContextMenu(params))
- return;
-
- render_view_host_delegate_view_->ShowContextMenu(params);
-}
-
void WebContentsImpl::RequestMediaAccessPermission(
const MediaStreamRequest& request,
const MediaResponseCallback& callback) {
- if (delegate_)
+ if (delegate_) {
delegate_->RequestMediaAccessPermission(this, request, callback);
- else
- callback.Run(MediaStreamDevices(), scoped_ptr<MediaStreamUI>());
+ } else {
+ callback.Run(MediaStreamDevices(),
+ MEDIA_DEVICE_INVALID_STATE,
+ scoped_ptr<MediaStreamUI>());
+ }
}
SessionStorageNamespace* WebContentsImpl::GetSessionStorageNamespace(
@@ -1604,14 +1674,24 @@ SessionStorageNamespace* WebContentsImpl::GetSessionStorageNamespace(
return controller_.GetSessionStorageNamespace(instance);
}
+SessionStorageNamespaceMap WebContentsImpl::GetSessionStorageNamespaceMap() {
+ return controller_.GetSessionStorageNamespaceMap();
+}
+
FrameTree* WebContentsImpl::GetFrameTree() {
return &frame_tree_;
}
+void WebContentsImpl::AccessibilityEventReceived(
+ const std::vector<AXEventNotificationDetails>& details) {
+ FOR_EACH_OBSERVER(
+ WebContentsObserver, observers_, AccessibilityEventReceived(details));
+}
+
void WebContentsImpl::OnShowValidationMessage(
const gfx::Rect& anchor_in_root_view,
- const string16& main_text,
- const string16& sub_text) {
+ const base::string16& main_text,
+ const base::string16& sub_text) {
if (delegate_)
delegate_->ShowValidationMessage(
this, anchor_in_root_view, main_text, sub_text);
@@ -1633,10 +1713,16 @@ void WebContentsImpl::DidSendScreenRects(RenderWidgetHostImpl* rwh) {
browser_plugin_embedder_->DidSendScreenRects();
}
+void WebContentsImpl::OnTouchEmulationEnabled(bool enabled) {
+ touch_emulation_enabled_ = enabled;
+ if (view_)
+ view_->SetOverscrollControllerEnabled(CanOverscrollContent());
+}
+
void WebContentsImpl::UpdatePreferredSize(const gfx::Size& pref_size) {
+ const gfx::Size old_size = GetPreferredSize();
preferred_size_ = pref_size;
- if (delegate_)
- delegate_->UpdatePreferredSize(this, pref_size);
+ OnPreferredSizeChanged(old_size);
}
void WebContentsImpl::ResizeDueToAutoResize(const gfx::Size& new_size) {
@@ -1663,15 +1749,25 @@ bool WebContentsImpl::Send(IPC::Message* message) {
bool WebContentsImpl::NavigateToPendingEntry(
NavigationController::ReloadType reload_type) {
- return NavigateToEntry(
- *NavigationEntryImpl::FromNavigationEntry(controller_.GetPendingEntry()),
- reload_type);
+ FrameTreeNode* node = frame_tree_.root();
+
+ // If we are using --site-per-process, we should navigate in the FrameTreeNode
+ // specified in the pending entry.
+ NavigationEntryImpl* pending_entry =
+ NavigationEntryImpl::FromNavigationEntry(controller_.GetPendingEntry());
+ if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess) &&
+ pending_entry->frame_tree_node_id() != -1) {
+ node = frame_tree_.FindByID(pending_entry->frame_tree_node_id());
+ }
+
+ return node->navigator()->NavigateToPendingEntry(
+ node->current_frame_host(), reload_type);
}
-void WebContentsImpl::RenderViewForInterstitialPageCreated(
- RenderViewHost* render_view_host) {
+void WebContentsImpl::RenderFrameForInterstitialPageCreated(
+ RenderFrameHost* render_frame_host) {
FOR_EACH_OBSERVER(WebContentsObserver, observers_,
- RenderViewForInterstitialPageCreated(render_view_host));
+ RenderFrameForInterstitialPageCreated(render_frame_host));
}
void WebContentsImpl::AttachInterstitialPage(
@@ -1689,82 +1785,12 @@ void WebContentsImpl::AttachInterstitialPage(
}
void WebContentsImpl::DetachInterstitialPage() {
- if (GetInterstitialPage())
+ if (ShowingInterstitialPage())
GetRenderManager()->remove_interstitial_page();
FOR_EACH_OBSERVER(WebContentsObserver, observers_,
DidDetachInterstitialPage());
}
-bool WebContentsImpl::NavigateToEntry(
- const NavigationEntryImpl& entry,
- NavigationController::ReloadType reload_type) {
- TRACE_EVENT0("browser", "WebContentsImpl::NavigateToEntry");
-
- // The renderer will reject IPC messages with URLs longer than
- // this limit, so don't attempt to navigate with a longer URL.
- if (entry.GetURL().spec().size() > GetMaxURLChars()) {
- LOG(WARNING) << "Refusing to load URL as it exceeds " << GetMaxURLChars()
- << " characters.";
- return false;
- }
-
- // TODO(creis): Use entry->frame_tree_node_id() to pick which
- // RenderFrameHostManager to use.
- RenderViewHostImpl* dest_render_view_host =
- static_cast<RenderViewHostImpl*>(GetRenderManager()->Navigate(entry));
- if (!dest_render_view_host)
- return false; // Unable to create the desired render view host.
-
- // For security, we should never send non-Web-UI URLs to a Web UI renderer.
- // Double check that here.
- int enabled_bindings = dest_render_view_host->GetEnabledBindings();
- bool data_urls_allowed = delegate_ && delegate_->CanLoadDataURLsInWebUI();
- bool is_allowed_in_web_ui_renderer =
- WebUIControllerFactoryRegistry::GetInstance()->IsURLAcceptableForWebUI(
- GetBrowserContext(), entry.GetURL(), data_urls_allowed);
- if ((enabled_bindings & BINDINGS_POLICY_WEB_UI) &&
- !is_allowed_in_web_ui_renderer) {
- // Log the URL to help us diagnose any future failures of this CHECK.
- GetContentClient()->SetActiveURL(entry.GetURL());
- CHECK(0);
- }
-
- // Notify observers that we will navigate in this RV.
- FOR_EACH_OBSERVER(WebContentsObserver,
- observers_,
- AboutToNavigateRenderView(dest_render_view_host));
-
- // Used for page load time metrics.
- current_load_start_ = base::TimeTicks::Now();
-
- // Navigate in the desired RenderViewHost.
- ViewMsg_Navigate_Params navigate_params;
- MakeNavigateParams(entry, controller_, delegate_, reload_type,
- &navigate_params);
- dest_render_view_host->Navigate(navigate_params);
-
- if (entry.GetPageID() == -1) {
- // HACK!! This code suppresses javascript: URLs from being added to
- // session history, which is what we want to do for javascript: URLs that
- // do not generate content. What we really need is a message from the
- // renderer telling us that a new page was not created. The same message
- // could be used for mailto: URLs and the like.
- if (entry.GetURL().SchemeIs(kJavaScriptScheme))
- return false;
- }
-
- // Notify observers about navigation.
- FOR_EACH_OBSERVER(
- WebContentsObserver,
- observers_,
- DidStartNavigationToPendingEntry(entry.GetURL(), reload_type));
-
- if (delegate_)
- delegate_->DidNavigateToPendingEntry(this);
-
- return true;
-}
-
void WebContentsImpl::SetHistoryLengthAndPrune(
const SiteInstance* site_instance,
int history_length,
@@ -1789,6 +1815,189 @@ void WebContentsImpl::SetHistoryLengthAndPrune(
minimum_page_id));
}
+void WebContentsImpl::ReloadFocusedFrame(bool ignore_cache) {
+ RenderFrameHost* focused_frame = GetFocusedFrame();
+ if (!focused_frame)
+ return;
+
+ focused_frame->Send(new FrameMsg_Reload(
+ focused_frame->GetRoutingID(), ignore_cache));
+}
+
+void WebContentsImpl::Undo() {
+ RenderFrameHost* focused_frame = GetFocusedFrame();
+ if (!focused_frame)
+ return;
+
+ focused_frame->Send(new InputMsg_Undo(focused_frame->GetRoutingID()));
+ RecordAction(base::UserMetricsAction("Undo"));
+}
+
+void WebContentsImpl::Redo() {
+ RenderFrameHost* focused_frame = GetFocusedFrame();
+ if (!focused_frame)
+ return;
+ focused_frame->Send(new InputMsg_Redo(focused_frame->GetRoutingID()));
+ RecordAction(base::UserMetricsAction("Redo"));
+}
+
+void WebContentsImpl::Cut() {
+ RenderFrameHost* focused_frame = GetFocusedFrame();
+ if (!focused_frame)
+ return;
+
+ focused_frame->Send(new InputMsg_Cut(focused_frame->GetRoutingID()));
+ RecordAction(base::UserMetricsAction("Cut"));
+}
+
+void WebContentsImpl::Copy() {
+ RenderFrameHost* focused_frame = GetFocusedFrame();
+ if (!focused_frame)
+ return;
+
+ focused_frame->Send(new InputMsg_Copy(focused_frame->GetRoutingID()));
+ RecordAction(base::UserMetricsAction("Copy"));
+}
+
+void WebContentsImpl::CopyToFindPboard() {
+#if defined(OS_MACOSX)
+ RenderFrameHost* focused_frame = GetFocusedFrame();
+ if (!focused_frame)
+ return;
+
+ // Windows/Linux don't have the concept of a find pasteboard.
+ focused_frame->Send(
+ new InputMsg_CopyToFindPboard(focused_frame->GetRoutingID()));
+ RecordAction(base::UserMetricsAction("CopyToFindPboard"));
+#endif
+}
+
+void WebContentsImpl::Paste() {
+ RenderFrameHost* focused_frame = GetFocusedFrame();
+ if (!focused_frame)
+ return;
+
+ focused_frame->Send(new InputMsg_Paste(focused_frame->GetRoutingID()));
+ RecordAction(base::UserMetricsAction("Paste"));
+}
+
+void WebContentsImpl::PasteAndMatchStyle() {
+ RenderFrameHost* focused_frame = GetFocusedFrame();
+ if (!focused_frame)
+ return;
+
+ focused_frame->Send(new InputMsg_PasteAndMatchStyle(
+ focused_frame->GetRoutingID()));
+ RecordAction(base::UserMetricsAction("PasteAndMatchStyle"));
+}
+
+void WebContentsImpl::Delete() {
+ RenderFrameHost* focused_frame = GetFocusedFrame();
+ if (!focused_frame)
+ return;
+
+ focused_frame->Send(new InputMsg_Delete(focused_frame->GetRoutingID()));
+ RecordAction(base::UserMetricsAction("DeleteSelection"));
+}
+
+void WebContentsImpl::SelectAll() {
+ RenderFrameHost* focused_frame = GetFocusedFrame();
+ if (!focused_frame)
+ return;
+
+ focused_frame->Send(new InputMsg_SelectAll(focused_frame->GetRoutingID()));
+ RecordAction(base::UserMetricsAction("SelectAll"));
+}
+
+void WebContentsImpl::Unselect() {
+ RenderFrameHost* focused_frame = GetFocusedFrame();
+ if (!focused_frame)
+ return;
+
+ focused_frame->Send(new InputMsg_Unselect(focused_frame->GetRoutingID()));
+ RecordAction(base::UserMetricsAction("Unselect"));
+}
+
+void WebContentsImpl::Replace(const base::string16& word) {
+ RenderFrameHost* focused_frame = GetFocusedFrame();
+ if (!focused_frame)
+ return;
+
+ focused_frame->Send(new InputMsg_Replace(
+ focused_frame->GetRoutingID(), word));
+}
+
+void WebContentsImpl::ReplaceMisspelling(const base::string16& word) {
+ RenderFrameHost* focused_frame = GetFocusedFrame();
+ if (!focused_frame)
+ return;
+
+ focused_frame->Send(new InputMsg_ReplaceMisspelling(
+ focused_frame->GetRoutingID(), word));
+}
+
+void WebContentsImpl::NotifyContextMenuClosed(
+ const CustomContextMenuContext& context) {
+ RenderFrameHost* focused_frame = GetFocusedFrame();
+ if (!focused_frame)
+ return;
+
+ focused_frame->Send(new FrameMsg_ContextMenuClosed(
+ focused_frame->GetRoutingID(), context));
+}
+
+void WebContentsImpl::ExecuteCustomContextMenuCommand(
+ int action, const CustomContextMenuContext& context) {
+ RenderFrameHost* focused_frame = GetFocusedFrame();
+ if (!focused_frame)
+ return;
+
+ focused_frame->Send(new FrameMsg_CustomContextMenuAction(
+ focused_frame->GetRoutingID(), context, action));
+}
+
+gfx::NativeView WebContentsImpl::GetNativeView() {
+ return view_->GetNativeView();
+}
+
+gfx::NativeView WebContentsImpl::GetContentNativeView() {
+ return view_->GetContentNativeView();
+}
+
+gfx::NativeWindow WebContentsImpl::GetTopLevelNativeWindow() {
+ return view_->GetTopLevelNativeWindow();
+}
+
+gfx::Rect WebContentsImpl::GetViewBounds() {
+ return view_->GetViewBounds();
+}
+
+gfx::Rect WebContentsImpl::GetContainerBounds() {
+ gfx::Rect rv;
+ view_->GetContainerBounds(&rv);
+ return rv;
+}
+
+DropData* WebContentsImpl::GetDropData() {
+ return view_->GetDropData();
+}
+
+void WebContentsImpl::Focus() {
+ view_->Focus();
+}
+
+void WebContentsImpl::SetInitialFocus() {
+ view_->SetInitialFocus();
+}
+
+void WebContentsImpl::StoreFocus() {
+ view_->StoreFocus();
+}
+
+void WebContentsImpl::RestoreFocus() {
+ view_->RestoreFocus();
+}
+
void WebContentsImpl::FocusThroughTabTraversal(bool reverse) {
if (ShowingInterstitialPage()) {
GetRenderManager()->interstitial_page()->FocusThroughTabTraversal(reverse);
@@ -1879,15 +2088,6 @@ void WebContentsImpl::GenerateMHTML(
MHTMLGenerationManager::GetInstance()->SaveMHTML(this, file, callback);
}
-// TODO(nasko): Rename this method to IsVisibleEntry.
-bool WebContentsImpl::IsActiveEntry(int32 page_id) {
- NavigationEntryImpl* visible_entry =
- NavigationEntryImpl::FromNavigationEntry(controller_.GetVisibleEntry());
- return (visible_entry != NULL &&
- visible_entry->site_instance() == GetSiteInstance() &&
- visible_entry->GetPageID() == page_id);
-}
-
const std::string& WebContentsImpl::GetContentsMimeType() const {
return contents_mime_type_;
}
@@ -1902,7 +2102,7 @@ void WebContentsImpl::SetOverrideEncoding(const std::string& encoding) {
}
void WebContentsImpl::ResetOverrideEncoding() {
- encoding_.clear();
+ canonical_encoding_.clear();
Send(new ViewMsg_ResetPageEncodingToDefault(GetRoutingID()));
}
@@ -1924,14 +2124,35 @@ void WebContentsImpl::DragSourceEndedAt(int client_x, int client_y,
screen_x, screen_y, operation);
}
-void WebContentsImpl::DragSourceMovedTo(int client_x, int client_y,
- int screen_x, int screen_y) {
- if (browser_plugin_embedder_.get())
- browser_plugin_embedder_->DragSourceMovedTo(client_x, client_y,
- screen_x, screen_y);
- if (GetRenderViewHost())
- GetRenderViewHostImpl()->DragSourceMovedTo(client_x, client_y,
- screen_x, screen_y);
+void WebContentsImpl::DidGetResourceResponseStart(
+ const ResourceRequestDetails& details) {
+ controller_.ssl_manager()->DidStartResourceResponse(details);
+
+ FOR_EACH_OBSERVER(WebContentsObserver, observers_,
+ DidGetResourceResponseStart(details));
+
+ // TODO(avi): Remove. http://crbug.com/170921
+ NotificationService::current()->Notify(
+ NOTIFICATION_RESOURCE_RESPONSE_STARTED,
+ Source<WebContents>(this),
+ Details<const ResourceRequestDetails>(&details));
+}
+
+void WebContentsImpl::DidGetRedirectForResourceRequest(
+ RenderViewHost* render_view_host,
+ const ResourceRedirectDetails& details) {
+ controller_.ssl_manager()->DidReceiveResourceRedirect(details);
+
+ FOR_EACH_OBSERVER(
+ WebContentsObserver,
+ observers_,
+ DidGetRedirectForResourceRequest(render_view_host, details));
+
+ // TODO(avi): Remove. http://crbug.com/170921
+ NotificationService::current()->Notify(
+ NOTIFICATION_RESOURCE_RECEIVED_REDIRECT,
+ Source<WebContents>(this),
+ Details<const ResourceRedirectDetails>(&details));
}
void WebContentsImpl::SystemDragEnded() {
@@ -1955,35 +2176,13 @@ bool WebContentsImpl::GetClosedByUserGesture() const {
return closed_by_user_gesture_;
}
-double WebContentsImpl::GetZoomLevel() const {
- HostZoomMapImpl* zoom_map = static_cast<HostZoomMapImpl*>(
- HostZoomMap::GetForBrowserContext(GetBrowserContext()));
- if (!zoom_map)
- return 0;
-
- double zoom_level;
- if (temporary_zoom_settings_) {
- zoom_level = zoom_map->GetTemporaryZoomLevel(
- GetRenderProcessHost()->GetID(), GetRenderViewHost()->GetRoutingID());
- } else {
- GURL url;
- NavigationEntry* entry = GetController().GetLastCommittedEntry();
- // Since zoom map is updated using rewritten URL, use rewritten URL
- // to get the zoom level.
- url = entry ? entry->GetURL() : GURL::EmptyGURL();
- zoom_level = zoom_map->GetZoomLevelForHostAndScheme(url.scheme(),
- net::GetHostOrSpecFromURL(url));
- }
- return zoom_level;
-}
-
int WebContentsImpl::GetZoomPercent(bool* enable_increment,
bool* enable_decrement) const {
*enable_decrement = *enable_increment = false;
// Calculate the zoom percent from the factor. Round up to the nearest whole
// number.
int percent = static_cast<int>(
- ZoomLevelToZoomFactor(GetZoomLevel()) * 100 + 0.5);
+ ZoomLevelToZoomFactor(HostZoomMap::GetZoomLevel(this)) * 100 + 0.5);
*enable_decrement = percent > minimum_zoom_percent_;
*enable_increment = percent < maximum_zoom_percent_;
return percent;
@@ -2017,10 +2216,13 @@ int WebContentsImpl::GetMaximumZoomPercent() const {
}
gfx::Size WebContentsImpl::GetPreferredSize() const {
- return preferred_size_;
+ return capturer_count_ == 0 ? preferred_size_ : preferred_size_for_capture_;
}
bool WebContentsImpl::GotResponseToLockMouseRequest(bool allowed) {
+ if (GetBrowserPluginGuest())
+ return GetBrowserPluginGuest()->LockMouse(allowed);
+
return GetRenderViewHost() ?
GetRenderViewHostImpl()->GotResponseToLockMouseRequest(allowed) : false;
}
@@ -2030,37 +2232,63 @@ bool WebContentsImpl::HasOpener() const {
}
void WebContentsImpl::DidChooseColorInColorChooser(SkColor color) {
- Send(new ViewMsg_DidChooseColorResponse(
- GetRoutingID(), color_chooser_identifier_, color));
+ if (!color_chooser_info_.get())
+ return;
+ RenderFrameHost* rfh = RenderFrameHost::FromID(
+ color_chooser_info_->render_process_id,
+ color_chooser_info_->render_frame_id);
+ if (!rfh)
+ return;
+
+ rfh->Send(new FrameMsg_DidChooseColorResponse(
+ rfh->GetRoutingID(), color_chooser_info_->identifier, color));
}
void WebContentsImpl::DidEndColorChooser() {
- Send(new ViewMsg_DidEndColorChooser(GetRoutingID(),
- color_chooser_identifier_));
- color_chooser_.reset();
- color_chooser_identifier_ = 0;
+ if (!color_chooser_info_.get())
+ return;
+ RenderFrameHost* rfh = RenderFrameHost::FromID(
+ color_chooser_info_->render_process_id,
+ color_chooser_info_->render_frame_id);
+ if (!rfh)
+ return;
+
+ rfh->Send(new FrameMsg_DidEndColorChooser(
+ rfh->GetRoutingID(), color_chooser_info_->identifier));
+ color_chooser_info_.reset();
}
int WebContentsImpl::DownloadImage(const GURL& url,
bool is_favicon,
uint32_t max_bitmap_size,
const ImageDownloadCallback& callback) {
- RenderViewHost* host = GetRenderViewHost();
- int id = StartDownload(host, url, is_favicon, max_bitmap_size);
+ int id = StartDownload(GetMainFrame(), url, is_favicon, max_bitmap_size);
image_download_map_[id] = callback;
return id;
}
-void WebContentsImpl::SetZoomLevel(double level) {
- Send(new ViewMsg_SetZoomLevel(GetRoutingID(), level));
- BrowserPluginEmbedder* embedder = GetBrowserPluginEmbedder();
- if (embedder)
- embedder->SetZoomLevel(level);
+bool WebContentsImpl::IsSubframe() const {
+ return is_subframe_;
+}
+
+void WebContentsImpl::Find(int request_id,
+ const base::string16& search_text,
+ const blink::WebFindOptions& options) {
+ Send(new ViewMsg_Find(GetRoutingID(), request_id, search_text, options));
+}
+
+void WebContentsImpl::StopFinding(StopFindAction action) {
+ Send(new ViewMsg_StopFinding(GetRoutingID(), action));
+}
+
+void WebContentsImpl::InsertCSS(const std::string& css) {
+ GetMainFrame()->Send(new FrameMsg_CSSInsertRequest(
+ GetMainFrame()->GetRoutingID(), css));
}
bool WebContentsImpl::FocusLocationBarByDefault() {
NavigationEntry* entry = controller_.GetVisibleEntry();
- if (entry && entry->GetURL() == GURL(kAboutBlankURL))
+ if (entry && entry->GetURL() == GURL(url::kAboutBlankURL))
return true;
return delegate_ && delegate_->ShouldFocusLocationBarByDefault(this);
}
@@ -2072,128 +2300,223 @@ void WebContentsImpl::SetFocusToLocationBar(bool select_all) {
void WebContentsImpl::DidStartProvisionalLoad(
RenderFrameHostImpl* render_frame_host,
- int64 frame_id,
- int64 parent_frame_id,
- bool is_main_frame,
+ int parent_routing_id,
const GURL& validated_url,
bool is_error_page,
bool is_iframe_srcdoc) {
- if (is_main_frame)
- DidChangeLoadProgress(0);
+ bool is_main_frame = render_frame_host->frame_tree_node()->IsMainFrame();
// Notify observers about the start of the provisional load.
+ int render_frame_id = render_frame_host->GetRoutingID();
+ RenderViewHost* render_view_host = render_frame_host->render_view_host();
FOR_EACH_OBSERVER(WebContentsObserver, observers_,
- DidStartProvisionalLoadForFrame(frame_id, parent_frame_id,
- is_main_frame, validated_url, is_error_page,
- is_iframe_srcdoc, render_frame_host->render_view_host()));
+ DidStartProvisionalLoadForFrame(
+ render_frame_id, parent_routing_id, is_main_frame,
+ validated_url, is_error_page, is_iframe_srcdoc,
+ render_view_host));
if (is_main_frame) {
FOR_EACH_OBSERVER(
WebContentsObserver,
observers_,
ProvisionalChangeToMainFrameUrl(validated_url,
- render_frame_host->render_view_host()));
+ render_frame_host));
}
}
+void WebContentsImpl::DidFailProvisionalLoadWithError(
+ RenderFrameHostImpl* render_frame_host,
+ const FrameHostMsg_DidFailProvisionalLoadWithError_Params& params) {
+ GURL validated_url(params.url);
+ int render_frame_id = render_frame_host->GetRoutingID();
+ bool is_main_frame = render_frame_host->frame_tree_node()->IsMainFrame();
+ RenderViewHost* render_view_host = render_frame_host->render_view_host();
+ FOR_EACH_OBSERVER(
+ WebContentsObserver,
+ observers_,
+ DidFailProvisionalLoad(render_frame_id,
+ params.frame_unique_name,
+ is_main_frame,
+ validated_url,
+ params.error_code,
+ params.error_description,
+ render_view_host));
+}
+
+void WebContentsImpl::DidFailLoadWithError(
+ RenderFrameHostImpl* render_frame_host,
+ const GURL& url,
+ int error_code,
+ const base::string16& error_description) {
+ int render_frame_id = render_frame_host->GetRoutingID();
+ bool is_main_frame = render_frame_host->frame_tree_node()->IsMainFrame();
+ RenderViewHost* render_view_host = render_frame_host->render_view_host();
+ FOR_EACH_OBSERVER(WebContentsObserver, observers_,
+ DidFailLoad(render_frame_id, url, is_main_frame, error_code,
+ error_description, render_view_host));
+}
+
void WebContentsImpl::NotifyChangedNavigationState(
InvalidateTypes changed_flags) {
NotifyNavigationStateChanged(changed_flags);
}
-void WebContentsImpl::DidRedirectProvisionalLoad(
- RenderViewHost* render_view_host,
- int32 page_id,
- const GURL& source_url,
- const GURL& target_url) {
- // TODO(creis): Remove this method and have the pre-rendering code listen to
- // WebContentsObserver::DidGetRedirectForResourceRequest instead.
- // See http://crbug.com/78512.
- GURL validated_source_url(source_url);
- GURL validated_target_url(target_url);
- RenderProcessHost* render_process_host =
- render_view_host->GetProcess();
- RenderViewHost::FilterURL(render_process_host, false, &validated_source_url);
- RenderViewHost::FilterURL(render_process_host, false, &validated_target_url);
- NavigationEntry* entry;
- if (page_id == -1) {
- entry = controller_.GetPendingEntry();
- } else {
- entry = controller_.GetEntryWithPageID(render_view_host->GetSiteInstance(),
- page_id);
+void WebContentsImpl::AboutToNavigateRenderFrame(
+ RenderFrameHostImpl* render_frame_host) {
+ // Notify observers that we will navigate in this RenderView.
+ RenderViewHost* render_view_host = render_frame_host->render_view_host();
+ FOR_EACH_OBSERVER(
+ WebContentsObserver,
+ observers_,
+ AboutToNavigateRenderView(render_view_host));
+}
+
+void WebContentsImpl::DidStartNavigationToPendingEntry(
+ RenderFrameHostImpl* render_frame_host,
+ const GURL& url,
+ NavigationController::ReloadType reload_type) {
+ // Notify observers about navigation.
+ FOR_EACH_OBSERVER(
+ WebContentsObserver,
+ observers_,
+ DidStartNavigationToPendingEntry(url, reload_type));
+}
+
+void WebContentsImpl::RequestOpenURL(RenderFrameHostImpl* render_frame_host,
+ const OpenURLParams& params) {
+ int source_render_frame_id = render_frame_host->GetRoutingID();
+ WebContents* new_contents = OpenURL(params);
+
+ if (new_contents) {
+ // Notify observers.
+ FOR_EACH_OBSERVER(WebContentsObserver, observers_,
+ DidOpenRequestedURL(new_contents,
+ params.url,
+ params.referrer,
+ params.disposition,
+ params.transition,
+ source_render_frame_id));
}
- if (!entry || entry->GetURL() != validated_source_url)
- return;
+}
+
+bool WebContentsImpl::ShouldPreserveAbortedURLs() {
+ if (!delegate_)
+ return false;
+ return delegate_->ShouldPreserveAbortedURLs(this);
+}
+void WebContentsImpl::DidRedirectProvisionalLoad(
+ RenderFrameHostImpl* render_frame_host,
+ const GURL& validated_target_url) {
// Notify observers about the provisional change in the main frame URL.
- FOR_EACH_OBSERVER(WebContentsObserver, observers_,
- ProvisionalChangeToMainFrameUrl(validated_target_url,
- render_view_host));
+ FOR_EACH_OBSERVER(
+ WebContentsObserver,
+ observers_,
+ ProvisionalChangeToMainFrameUrl(validated_target_url,
+ render_frame_host));
}
-void WebContentsImpl::DidFailProvisionalLoadWithError(
- RenderViewHost* render_view_host,
- const ViewHostMsg_DidFailProvisionalLoadWithError_Params& params) {
- VLOG(1) << "Failed Provisional Load: " << params.url.possibly_invalid_spec()
- << ", error_code: " << params.error_code
- << ", error_description: " << params.error_description
- << ", is_main_frame: " << params.is_main_frame
- << ", showing_repost_interstitial: " <<
- params.showing_repost_interstitial
- << ", frame_id: " << params.frame_id;
- GURL validated_url(params.url);
- RenderProcessHost* render_process_host =
- render_view_host->GetProcess();
- RenderViewHost::FilterURL(render_process_host, false, &validated_url);
-
- if (net::ERR_ABORTED == params.error_code) {
- // EVIL HACK ALERT! Ignore failed loads when we're showing interstitials.
- // This means that the interstitial won't be torn down properly, which is
- // bad. But if we have an interstitial, go back to another tab type, and
- // then load the same interstitial again, we could end up getting the first
- // interstitial's "failed" message (as a result of the cancel) when we're on
- // the second one.
- //
- // We can't tell this apart, so we think we're tearing down the current page
- // which will cause a crash later one. There is also some code in
- // RenderFrameHostManager::RendererAbortedProvisionalLoad that is commented
- // out because of this problem.
- //
- // http://code.google.com/p/chromium/issues/detail?id=2855
- // Because this will not tear down the interstitial properly, if "back" is
- // back to another tab type, the interstitial will still be somewhat alive
- // in the previous tab type. If you navigate somewhere that activates the
- // tab with the interstitial again, you'll see a flash before the new load
- // commits of the interstitial page.
- if (ShowingInterstitialPage()) {
- LOG(WARNING) << "Discarding message during interstitial.";
- return;
- }
+void WebContentsImpl::DidCommitProvisionalLoad(
+ RenderFrameHostImpl* render_frame_host,
+ const base::string16& frame_unique_name,
+ bool is_main_frame,
+ const GURL& url,
+ PageTransition transition_type) {
+ int render_frame_id = render_frame_host->GetRoutingID();
+ RenderViewHost* render_view_host = render_frame_host->render_view_host();
+ // Notify observers about the commit of the provisional load.
+ FOR_EACH_OBSERVER(
+ WebContentsObserver,
+ observers_,
+ DidCommitProvisionalLoadForFrame(render_frame_id,
+ frame_unique_name,
+ is_main_frame,
+ url,
+ transition_type,
+ render_view_host));
+}
+
+void WebContentsImpl::DidNavigateMainFramePreCommit(
+ const FrameHostMsg_DidCommitProvisionalLoad_Params& params) {
+ // Ensure fullscreen mode is exited before committing the navigation to a
+ // different page. The next page will not start out assuming it is in
+ // fullscreen mode.
+ if (controller_.IsURLInPageNavigation(params.url,
+ params.was_within_same_page,
+ NAVIGATION_TYPE_UNKNOWN)) {
+ // No page change? Then, the renderer and browser can remain in fullscreen.
+ return;
+ }
+ if (IsFullscreenForCurrentTab())
+ GetRenderViewHost()->ExitFullscreen();
+ DCHECK(!IsFullscreenForCurrentTab());
+}
- GetRenderManager()->RendererAbortedProvisionalLoad(render_view_host);
+void WebContentsImpl::DidNavigateMainFramePostCommit(
+ const LoadCommittedDetails& details,
+ const FrameHostMsg_DidCommitProvisionalLoad_Params& params) {
+ if (details.is_navigation_to_different_page()) {
+ // Clear the status bubble. This is a workaround for a bug where WebKit
+ // doesn't let us know that the cursor left an element during a
+ // transition (this is also why the mouse cursor remains as a hand after
+ // clicking on a link); see bugs 1184641 and 980803. We don't want to
+ // clear the bubble when a user navigates to a named anchor in the same
+ // page.
+ UpdateTargetURL(details.entry->GetPageID(), GURL());
}
- // Do not usually clear the pending entry if one exists, so that the user's
- // typed URL is not lost when a navigation fails or is aborted. However, in
- // cases that we don't show the pending entry (e.g., renderer-initiated
- // navigations in an existing tab), we don't keep it around. That prevents
- // spoofs on in-page navigations that don't go through
- // DidStartProvisionalLoadForFrame.
- // In general, we allow the view to clear the pending entry and typed URL if
- // the user requests (e.g., hitting Escape with focus in the address bar).
- // Note: don't touch the transient entry, since an interstitial may exist.
- if (controller_.GetPendingEntry() != controller_.GetVisibleEntry())
- controller_.DiscardPendingEntry();
+ if (!details.is_in_page) {
+ // Once the main frame is navigated, we're no longer considered to have
+ // displayed insecure content.
+ displayed_insecure_content_ = false;
+ SSLManager::NotifySSLInternalStateChanged(
+ GetController().GetBrowserContext());
+ }
- FOR_EACH_OBSERVER(WebContentsObserver,
- observers_,
- DidFailProvisionalLoad(params.frame_id,
- params.frame_unique_name,
- params.is_main_frame,
- validated_url,
- params.error_code,
- params.error_description,
- render_view_host));
+ // Notify observers about navigation.
+ FOR_EACH_OBSERVER(WebContentsObserver, observers_,
+ DidNavigateMainFrame(details, params));
+
+ if (delegate_)
+ delegate_->DidNavigateMainFramePostCommit(this);
+ view_->SetOverscrollControllerEnabled(CanOverscrollContent());
+}
+
+void WebContentsImpl::DidNavigateAnyFramePostCommit(
+ RenderFrameHostImpl* render_frame_host,
+ const LoadCommittedDetails& details,
+ const FrameHostMsg_DidCommitProvisionalLoad_Params& params) {
+ // Now that something has committed, we don't need to track whether the
+ // initial page has been accessed.
+ has_accessed_initial_document_ = false;
+
+ // If we navigate off the page, close all JavaScript dialogs.
+ if (dialog_manager_ && !details.is_in_page)
+ dialog_manager_->CancelActiveAndPendingDialogs(this);
+
+ // Notify observers about navigation.
+ FOR_EACH_OBSERVER(WebContentsObserver, observers_,
+ DidNavigateAnyFrame(details, params));
+}
+
+void WebContentsImpl::SetMainFrameMimeType(const std::string& mime_type) {
+ contents_mime_type_ = mime_type;
+}
+
+bool WebContentsImpl::CanOverscrollContent() const {
+ // Disable overscroll when touch emulation is on. See crbug.com/369938.
+ if (touch_emulation_enabled_)
+ return false;
+
+ if (delegate_)
+ return delegate_->CanOverscrollContent();
+
+ return false;
+}
+
+void WebContentsImpl::OnThemeColorChanged(SkColor theme_color) {
+ FOR_EACH_OBSERVER(WebContentsObserver, observers_,
+ DidChangeThemeColor(theme_color));
}
void WebContentsImpl::OnDidLoadResourceFromMemoryCache(
@@ -2239,7 +2562,7 @@ void WebContentsImpl::OnDidLoadResourceFromMemoryCache(
}
void WebContentsImpl::OnDidDisplayInsecureContent() {
- RecordAction(UserMetricsAction("SSL.DisplayedInsecureContent"));
+ RecordAction(base::UserMetricsAction("SSL.DisplayedInsecureContent"));
displayed_insecure_content_ = true;
SSLManager::NotifySSLInternalStateChanged(
GetController().GetBrowserContext());
@@ -2249,81 +2572,145 @@ void WebContentsImpl::OnDidRunInsecureContent(
const std::string& security_origin, const GURL& target_url) {
LOG(WARNING) << security_origin << " ran insecure content from "
<< target_url.possibly_invalid_spec();
- RecordAction(UserMetricsAction("SSL.RanInsecureContent"));
+ RecordAction(base::UserMetricsAction("SSL.RanInsecureContent"));
if (EndsWith(security_origin, kDotGoogleDotCom, false))
- RecordAction(UserMetricsAction("SSL.RanInsecureContentGoogle"));
+ RecordAction(base::UserMetricsAction("SSL.RanInsecureContentGoogle"));
controller_.ssl_manager()->DidRunInsecureContent(security_origin);
displayed_insecure_content_ = true;
SSLManager::NotifySSLInternalStateChanged(
GetController().GetBrowserContext());
}
-void WebContentsImpl::OnDocumentLoadedInFrame(int64 frame_id) {
- FOR_EACH_OBSERVER(
- WebContentsObserver, observers_,
- DocumentLoadedInFrame(frame_id, render_view_message_source_));
+void WebContentsImpl::OnDocumentLoadedInFrame() {
+ CHECK(render_frame_message_source_);
+ CHECK(!render_view_message_source_);
+ RenderFrameHostImpl* rfh =
+ static_cast<RenderFrameHostImpl*>(render_frame_message_source_);
+
+ int render_frame_id = rfh->GetRoutingID();
+ RenderViewHost* render_view_host = rfh->render_view_host();
+ FOR_EACH_OBSERVER(WebContentsObserver,
+ observers_,
+ DocumentLoadedInFrame(render_frame_id, render_view_host));
}
void WebContentsImpl::OnDidFinishLoad(
- int64 frame_id,
- const GURL& url,
- bool is_main_frame) {
- GURL validated_url(url);
- RenderProcessHost* render_process_host =
- render_view_message_source_->GetProcess();
- RenderViewHost::FilterURL(render_process_host, false, &validated_url);
- FOR_EACH_OBSERVER(WebContentsObserver, observers_,
- DidFinishLoad(frame_id, validated_url, is_main_frame,
- render_view_message_source_));
-}
+ const GURL& url) {
+ if (!render_frame_message_source_) {
+ RecordAction(base::UserMetricsAction("BadMessageTerminate_RVD2"));
+ GetRenderProcessHost()->ReceivedBadMessage();
+ return;
+ }
-void WebContentsImpl::OnDidFailLoadWithError(
- int64 frame_id,
- const GURL& url,
- bool is_main_frame,
- int error_code,
- const base::string16& error_description) {
GURL validated_url(url);
RenderProcessHost* render_process_host =
- render_view_message_source_->GetProcess();
- RenderViewHost::FilterURL(render_process_host, false, &validated_url);
+ render_frame_message_source_->GetProcess();
+ render_process_host->FilterURL(false, &validated_url);
+
+ RenderFrameHostImpl* rfh =
+ static_cast<RenderFrameHostImpl*>(render_frame_message_source_);
+ int render_frame_id = rfh->GetRoutingID();
+ RenderViewHost* render_view_host = rfh->render_view_host();
+ bool is_main_frame = rfh->frame_tree_node()->IsMainFrame();
FOR_EACH_OBSERVER(WebContentsObserver, observers_,
- DidFailLoad(frame_id, validated_url, is_main_frame,
- error_code, error_description,
- render_view_message_source_));
+ DidFinishLoad(render_frame_id, validated_url,
+ is_main_frame, render_view_host));
+}
+
+void WebContentsImpl::OnDidStartLoading(bool to_different_document) {
+ RenderFrameHostImpl* rfh =
+ static_cast<RenderFrameHostImpl*>(render_frame_message_source_);
+ int64 render_frame_id = rfh->frame_tree_node()->frame_tree_node_id();
+
+ // It is possible to get multiple calls to OnDidStartLoading that don't have
+ // corresponding calls to OnDidStopLoading:
+ // - With "swappedout://" URLs, this happens when a RenderView gets swapped
+ // out for a cross-process navigation, and it turns into a placeholder for
+ // one being rendered in a different process.
+ // - Also, there might be more than one RenderFrameHost sharing the same
+ // FrameTreeNode (and thus sharing its ID) each sending a start.
+ // - But in the future, once clamy@ moves navigation network requests to the
+ // browser process, there's a good chance that callbacks about starting and
+ // stopping will all be handled by the browser. When that happens, there
+ // should no longer be a start/stop call imbalance. TODO(avi): When this
+ // future arrives, update this code to not allow this case.
+ DCHECK_GE(loading_frames_in_progress_, 0);
+ if (loading_progresses_.find(render_frame_id) == loading_progresses_.end()) {
+ if (loading_frames_in_progress_ == 0)
+ DidStartLoading(rfh, to_different_document);
+ ++loading_frames_in_progress_;
+ }
+
+ loading_progresses_[render_frame_id] = kMinimumLoadingProgress;
+ SendLoadProgressChanged();
+}
+
+void WebContentsImpl::OnDidStopLoading() {
+ RenderFrameHostImpl* rfh =
+ static_cast<RenderFrameHostImpl*>(render_frame_message_source_);
+ int64 render_frame_id = rfh->frame_tree_node()->frame_tree_node_id();
+
+ if (loading_progresses_.find(render_frame_id) != loading_progresses_.end()) {
+ // Load stopped while we were still tracking load. Make sure we update
+ // progress based on this frame's completion.
+ loading_progresses_[render_frame_id] = 1.0;
+ SendLoadProgressChanged();
+ // Then we clean-up our states.
+ if (loading_total_progress_ == 1.0)
+ ResetLoadProgressState();
+ }
+
+ // TODO(japhet): This should be a DCHECK, but the pdf plugin sometimes
+ // calls DidStopLoading() without a matching DidStartLoading().
+ if (loading_frames_in_progress_ == 0)
+ return;
+ --loading_frames_in_progress_;
+ if (loading_frames_in_progress_ == 0)
+ DidStopLoading(rfh);
+}
+
+void WebContentsImpl::OnDidChangeLoadProgress(double load_progress) {
+ RenderFrameHostImpl* rfh =
+ static_cast<RenderFrameHostImpl*>(render_frame_message_source_);
+ int64 render_frame_id = rfh->frame_tree_node()->frame_tree_node_id();
+
+ loading_progresses_[render_frame_id] = load_progress;
+
+ // We notify progress change immediately for the first and last updates.
+ // Also, since the message loop may be pretty busy when a page is loaded, it
+ // might not execute a posted task in a timely manner so we make sure to
+ // immediately send progress report if enough time has passed.
+ base::TimeDelta min_delay =
+ base::TimeDelta::FromMilliseconds(kMinimumDelayBetweenLoadingUpdatesMS);
+ if (load_progress == 1.0 || loading_last_progress_update_.is_null() ||
+ base::TimeTicks::Now() - loading_last_progress_update_ > min_delay) {
+ // If there is a pending task to send progress, it is now obsolete.
+ loading_weak_factory_.InvalidateWeakPtrs();
+ SendLoadProgressChanged();
+ if (loading_total_progress_ == 1.0)
+ ResetLoadProgressState();
+ return;
+ }
+
+ if (loading_weak_factory_.HasWeakPtrs())
+ return;
+
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&WebContentsImpl::SendLoadProgressChanged,
+ loading_weak_factory_.GetWeakPtr()),
+ min_delay);
}
void WebContentsImpl::OnGoToEntryAtOffset(int offset) {
- if (!delegate_ || delegate_->OnGoToEntryOffset(offset)) {
- NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
- controller_.GetEntryAtOffset(offset));
- if (!entry)
- return;
- // Note that we don't call NavigationController::GotToOffset() as we don't
- // want to create a pending navigation entry (it might end up lingering
- // http://crbug.com/51680).
- entry->SetTransitionType(
- PageTransitionFromInt(
- entry->GetTransitionType() |
- PAGE_TRANSITION_FORWARD_BACK));
- NavigateToEntry(*entry, NavigationControllerImpl::NO_RELOAD);
-
- // If the entry is being restored and doesn't have a SiteInstance yet, fill
- // it in now that we know. This allows us to find the entry when it commits.
- if (!entry->site_instance() &&
- entry->restore_type() != NavigationEntryImpl::RESTORE_NONE) {
- entry->set_site_instance(
- static_cast<SiteInstanceImpl*>(GetPendingSiteInstance()));
- }
- }
+ if (!delegate_ || delegate_->OnGoToEntryOffset(offset))
+ controller_.GoToOffset(offset);
}
void WebContentsImpl::OnUpdateZoomLimits(int minimum_percent,
- int maximum_percent,
- bool remember) {
+ int maximum_percent) {
minimum_zoom_percent_ = minimum_percent;
maximum_zoom_percent_ = maximum_percent;
- temporary_zoom_settings_ = !remember;
}
void WebContentsImpl::OnEnumerateDirectory(int request_id,
@@ -2337,11 +2724,6 @@ void WebContentsImpl::OnEnumerateDirectory(int request_id,
delegate_->EnumerateDirectory(this, request_id, path);
}
-void WebContentsImpl::OnJSOutOfMemory() {
- if (delegate_)
- delegate_->JSOutOfMemory(this);
-}
-
void WebContentsImpl::OnRegisterProtocolHandler(const std::string& protocol,
const GURL& url,
const base::string16& title,
@@ -2354,7 +2736,7 @@ void WebContentsImpl::OnRegisterProtocolHandler(const std::string& protocol,
if (policy->IsPseudoScheme(protocol))
return;
- delegate_->RegisterProtocolHandler(this, protocol, url, title, user_gesture);
+ delegate_->RegisterProtocolHandler(this, protocol, url, user_gesture);
}
void WebContentsImpl::OnFindReply(int request_id,
@@ -2389,11 +2771,6 @@ void WebContentsImpl::OnOpenDateTimeDialog(
value.suggestions);
}
-void WebContentsImpl::OnJavaBridgeGetChannelHandle(IPC::Message* reply_msg) {
- java_bridge_dispatcher_host_manager_->OnGetChannelHandle(
- render_view_message_source_, reply_msg);
-}
-
#endif
void WebContentsImpl::OnPepperPluginHung(int plugin_child_id,
@@ -2411,6 +2788,15 @@ void WebContentsImpl::OnPluginCrashed(const base::FilePath& plugin_path,
PluginCrashed(plugin_path, plugin_pid));
}
+void WebContentsImpl::OnDomOperationResponse(const std::string& json_string,
+ int automation_id) {
+ DomOperationNotificationDetails details(json_string, automation_id);
+ NotificationService::current()->Notify(
+ NOTIFICATION_DOM_OPERATION_RESPONSE,
+ Source<WebContents>(this),
+ Details<DomOperationNotificationDetails>(&details));
+}
+
void WebContentsImpl::OnAppCacheAccessed(const GURL& manifest_url,
bool blocked_by_policy) {
// Notify observers about navigation.
@@ -2419,30 +2805,35 @@ void WebContentsImpl::OnAppCacheAccessed(const GURL& manifest_url,
}
void WebContentsImpl::OnOpenColorChooser(
- int color_chooser_id,
- SkColor color,
- const std::vector<ColorSuggestion>& suggestions) {
- ColorChooser* new_color_chooser =
- delegate_->OpenColorChooser(this, color, suggestions);
+ int color_chooser_id,
+ SkColor color,
+ const std::vector<ColorSuggestion>& suggestions) {
+ ColorChooser* new_color_chooser = delegate_ ?
+ delegate_->OpenColorChooser(this, color, suggestions) :
+ NULL;
if (!new_color_chooser)
return;
- if (color_chooser_)
- color_chooser_->End();
- color_chooser_.reset(new_color_chooser);
- color_chooser_identifier_ = color_chooser_id;
+ if (color_chooser_info_.get())
+ color_chooser_info_->chooser->End();
+
+ color_chooser_info_.reset(new ColorChooserInfo(
+ render_frame_message_source_->GetProcess()->GetID(),
+ render_frame_message_source_->GetRoutingID(),
+ new_color_chooser,
+ color_chooser_id));
}
void WebContentsImpl::OnEndColorChooser(int color_chooser_id) {
- if (color_chooser_ &&
- color_chooser_id == color_chooser_identifier_)
- color_chooser_->End();
+ if (color_chooser_info_ &&
+ color_chooser_id == color_chooser_info_->identifier)
+ color_chooser_info_->chooser->End();
}
void WebContentsImpl::OnSetSelectedColorInColorChooser(int color_chooser_id,
SkColor color) {
- if (color_chooser_ &&
- color_chooser_id == color_chooser_identifier_)
- color_chooser_->SetSelectedColor(color);
+ if (color_chooser_info_ &&
+ color_chooser_id == color_chooser_info_->identifier)
+ color_chooser_info_->chooser->SetSelectedColor(color);
}
// This exists for render views that don't have a WebUI, but do have WebUI
@@ -2511,53 +2902,52 @@ void WebContentsImpl::OnDidDownloadImage(
}
void WebContentsImpl::OnUpdateFaviconURL(
- int32 page_id,
const std::vector<FaviconURL>& candidates) {
FOR_EACH_OBSERVER(WebContentsObserver, observers_,
- DidUpdateFaviconURL(page_id, candidates));
+ DidUpdateFaviconURL(candidates));
}
-void WebContentsImpl::OnMediaNotification(int64 player_cookie,
- bool has_video,
- bool has_audio,
- bool is_playing) {
- // Chrome OS does its own detection of audio and video.
+void WebContentsImpl::OnMediaPlayingNotification(int64 player_cookie,
+ bool has_video,
+ bool has_audio) {
+// Chrome OS does its own detection of audio and video.
#if !defined(OS_CHROMEOS)
- if (is_playing) {
- scoped_ptr<PowerSaveBlocker> blocker;
- if (has_video) {
- blocker = PowerSaveBlocker::Create(
- PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep,
- "Playing video");
+ scoped_ptr<PowerSaveBlocker> blocker;
+ if (has_video) {
+ blocker = PowerSaveBlocker::Create(
+ PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep, "Playing video");
#if defined(OS_ANDROID)
- static_cast<PowerSaveBlockerImpl*>(blocker.get())->
- InitDisplaySleepBlocker(GetView()->GetNativeView());
+ static_cast<PowerSaveBlockerImpl*>(blocker.get())
+ ->InitDisplaySleepBlocker(GetView()->GetNativeView());
#endif
- } else if (has_audio) {
- blocker = PowerSaveBlocker::Create(
- PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension,
- "Playing audio");
- }
+ } else if (has_audio) {
+ blocker = PowerSaveBlocker::Create(
+ PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension, "Playing audio");
+ }
- if (blocker) {
- power_save_blockers_[render_view_message_source_][player_cookie] =
- blocker.release();
- }
- } else {
- delete power_save_blockers_[render_view_message_source_][player_cookie];
- power_save_blockers_[render_view_message_source_].erase(player_cookie);
+ if (blocker) {
+ power_save_blockers_[render_frame_message_source_][player_cookie] =
+ blocker.release();
}
#endif // !defined(OS_CHROMEOS)
}
-void WebContentsImpl::OnFirstVisuallyNonEmptyPaint(int32 page_id) {
+void WebContentsImpl::OnMediaPausedNotification(int64 player_cookie) {
+ // Chrome OS does its own detection of audio and video.
+#if !defined(OS_CHROMEOS)
+ delete power_save_blockers_[render_frame_message_source_][player_cookie];
+ power_save_blockers_[render_frame_message_source_].erase(player_cookie);
+#endif // !defined(OS_CHROMEOS)
+}
+
+void WebContentsImpl::OnFirstVisuallyNonEmptyPaint() {
FOR_EACH_OBSERVER(WebContentsObserver, observers_,
- DidFirstVisuallyNonEmptyPaint(page_id));
+ DidFirstVisuallyNonEmptyPaint());
}
void WebContentsImpl::DidChangeVisibleSSLState() {
- FOR_EACH_OBSERVER(WebContentsObserver, observers_,
- DidChangeVisibleSSLState());
+ if (delegate_)
+ delegate_->VisibleSSLStateChanged(this);
}
void WebContentsImpl::NotifyBeforeFormRepostWarningShow() {
@@ -2572,10 +2962,15 @@ void WebContentsImpl::ActivateAndShowRepostFormWarningDialog() {
delegate_->ShowRepostFormWarningDialog(this);
}
+bool WebContentsImpl::HasAccessedInitialDocument() {
+ return has_accessed_initial_document_;
+}
+
// Notifies the RenderWidgetHost instance about the fact that the page is
// loading, or done loading.
void WebContentsImpl::SetIsLoading(RenderViewHost* render_view_host,
bool is_loading,
+ bool to_different_document,
LoadNotificationDetails* details) {
if (is_loading == is_loading_)
return;
@@ -2592,9 +2987,10 @@ void WebContentsImpl::SetIsLoading(RenderViewHost* render_view_host,
is_loading_ = is_loading;
waiting_for_response_ = is_loading;
+ is_load_to_different_document_ = to_different_document;
if (delegate_)
- delegate_->LoadingStateChanged(this);
+ delegate_->LoadingStateChanged(this, to_different_document);
NotifyNavigationStateChanged(INVALIDATE_TYPE_LOAD);
std::string url = (details ? details->url.possibly_invalid_spec() : "NULL");
@@ -2619,54 +3015,14 @@ void WebContentsImpl::SetIsLoading(RenderViewHost* render_view_host,
type, Source<NavigationController>(&controller_), det);
}
-void WebContentsImpl::DidNavigateMainFramePostCommit(
- const LoadCommittedDetails& details,
- const ViewHostMsg_FrameNavigate_Params& params) {
- if (details.is_navigation_to_different_page()) {
- // Clear the status bubble. This is a workaround for a bug where WebKit
- // doesn't let us know that the cursor left an element during a
- // transition (this is also why the mouse cursor remains as a hand after
- // clicking on a link); see bugs 1184641 and 980803. We don't want to
- // clear the bubble when a user navigates to a named anchor in the same
- // page.
- UpdateTargetURL(details.entry->GetPageID(), GURL());
- }
-
- if (!details.is_in_page) {
- // Once the main frame is navigated, we're no longer considered to have
- // displayed insecure content.
- displayed_insecure_content_ = false;
- SSLManager::NotifySSLInternalStateChanged(
- GetController().GetBrowserContext());
- }
-
- // Notify observers about navigation.
- FOR_EACH_OBSERVER(WebContentsObserver, observers_,
- DidNavigateMainFrame(details, params));
-}
-
-void WebContentsImpl::DidNavigateAnyFramePostCommit(
- RenderViewHost* render_view_host,
- const LoadCommittedDetails& details,
- const ViewHostMsg_FrameNavigate_Params& params) {
- // If we navigate off the page, close all JavaScript dialogs.
- if (dialog_manager_ && !details.is_in_page)
- dialog_manager_->CancelActiveAndPendingDialogs(this);
-
- // Notify observers about navigation.
- FOR_EACH_OBSERVER(WebContentsObserver, observers_,
- DidNavigateAnyFrame(details, params));
-}
-
-bool WebContentsImpl::ShouldAssignSiteForURL(const GURL& url) {
- // about:blank should not "use up" a new SiteInstance. The SiteInstance can
- // still be used for a normal web site.
- if (url == GURL(kAboutBlankURL))
- return false;
+void WebContentsImpl::SelectRange(const gfx::Point& start,
+ const gfx::Point& end) {
+ RenderFrameHost* focused_frame = GetFocusedFrame();
+ if (!focused_frame)
+ return;
- // The embedder will then have the opportunity to determine if the URL
- // should "use up" the SiteInstance.
- return GetContentClient()->browser()->ShouldAssignSiteForURL(url);
+ focused_frame->Send(
+ new InputMsg_SelectRange(focused_frame->GetRoutingID(), start, end));
}
void WebContentsImpl::UpdateMaxPageIDIfNecessary(RenderViewHost* rvh) {
@@ -2691,10 +3047,10 @@ bool WebContentsImpl::UpdateTitleForEntry(NavigationEntryImpl* entry,
base::string16 final_title;
bool explicit_set;
if (entry && entry->GetURL().SchemeIsFile() && title.empty()) {
- final_title = UTF8ToUTF16(entry->GetURL().ExtractFileName());
+ final_title = base::UTF8ToUTF16(entry->GetURL().ExtractFileName());
explicit_set = false; // Don't count synthetic titles toward the set limit.
} else {
- TrimWhitespace(title, TRIM_ALL, &final_title);
+ base::TrimWhitespace(title, base::TRIM_ALL, &final_title);
explicit_set = true;
}
@@ -2730,6 +3086,37 @@ bool WebContentsImpl::UpdateTitleForEntry(NavigationEntryImpl* entry,
return true;
}
+void WebContentsImpl::SendLoadProgressChanged() {
+ loading_last_progress_update_ = base::TimeTicks::Now();
+ double progress = 0.0;
+ int frame_count = 0;
+
+ for (LoadingProgressMap::iterator it = loading_progresses_.begin();
+ it != loading_progresses_.end();
+ ++it) {
+ progress += it->second;
+ ++frame_count;
+ }
+ if (frame_count == 0)
+ return;
+ progress /= frame_count;
+ DCHECK(progress <= 1.0);
+
+ if (progress <= loading_total_progress_)
+ return;
+ loading_total_progress_ = progress;
+
+ if (delegate_)
+ delegate_->LoadProgressChanged(this, progress);
+}
+
+void WebContentsImpl::ResetLoadProgressState() {
+ loading_progresses_.clear();
+ loading_total_progress_ = 0.0;
+ loading_weak_factory_.InvalidateWeakPtrs();
+ loading_last_progress_update_ = base::TimeTicks();
+}
+
void WebContentsImpl::NotifySwapped(RenderViewHost* old_host,
RenderViewHost* new_host) {
// After sending out a swap notification, we need to send a disconnect
@@ -2777,6 +3164,10 @@ bool WebContentsImpl::OnMessageReceived(RenderFrameHost* render_frame_host,
return OnMessageReceived(NULL, render_frame_host, message);
}
+const GURL& WebContentsImpl::GetMainFrameLastCommittedURL() const {
+ return GetLastCommittedURL();
+}
+
void WebContentsImpl::RenderFrameCreated(RenderFrameHost* render_frame_host) {
// Note this is only for subframes, the notification for the main frame
// happens in RenderViewCreated.
@@ -2786,29 +3177,134 @@ void WebContentsImpl::RenderFrameCreated(RenderFrameHost* render_frame_host) {
}
void WebContentsImpl::RenderFrameDeleted(RenderFrameHost* render_frame_host) {
+ ClearPowerSaveBlockers(render_frame_host);
FOR_EACH_OBSERVER(WebContentsObserver,
observers_,
RenderFrameDeleted(render_frame_host));
}
-RenderViewHostDelegateView* WebContentsImpl::GetDelegateView() {
- return render_view_host_delegate_view_;
+void WebContentsImpl::WorkerCrashed(RenderFrameHost* render_frame_host) {
+ if (delegate_)
+ delegate_->WorkerCrashed(this);
}
-RenderViewHostDelegate::RendererManagement*
-WebContentsImpl::GetRendererManagementDelegate() {
- return GetRenderManager();
+void WebContentsImpl::ShowContextMenu(RenderFrameHost* render_frame_host,
+ const ContextMenuParams& params) {
+ ContextMenuParams context_menu_params(params);
+ // Allow WebContentsDelegates to handle the context menu operation first.
+ if (GetBrowserPluginGuest()) {
+ WebContentsViewGuest* view_guest =
+ static_cast<WebContentsViewGuest*>(GetView());
+ context_menu_params = view_guest->ConvertContextMenuParams(params);
+ }
+ if (delegate_ && delegate_->HandleContextMenu(context_menu_params))
+ return;
+
+ render_view_host_delegate_view_->ShowContextMenu(render_frame_host,
+ context_menu_params);
}
-RendererPreferences WebContentsImpl::GetRendererPrefs(
- BrowserContext* browser_context) const {
- return renderer_preferences_;
+void WebContentsImpl::RunJavaScriptMessage(
+ RenderFrameHost* render_frame_host,
+ const base::string16& message,
+ const base::string16& default_prompt,
+ const GURL& frame_url,
+ JavaScriptMessageType javascript_message_type,
+ IPC::Message* reply_msg) {
+ // Suppress JavaScript dialogs when requested. Also suppress messages when
+ // showing an interstitial as it's shown over the previous page and we don't
+ // want the hidden page's dialogs to interfere with the interstitial.
+ bool suppress_this_message =
+ static_cast<RenderViewHostImpl*>(render_frame_host->GetRenderViewHost())->
+ IsSwappedOut() ||
+ ShowingInterstitialPage() ||
+ !delegate_ ||
+ delegate_->ShouldSuppressDialogs() ||
+ !delegate_->GetJavaScriptDialogManager();
+
+ if (!suppress_this_message) {
+ std::string accept_lang = GetContentClient()->browser()->
+ GetAcceptLangs(GetBrowserContext());
+ dialog_manager_ = delegate_->GetJavaScriptDialogManager();
+ dialog_manager_->RunJavaScriptDialog(
+ this,
+ frame_url.GetOrigin(),
+ accept_lang,
+ javascript_message_type,
+ message,
+ default_prompt,
+ base::Bind(&WebContentsImpl::OnDialogClosed,
+ base::Unretained(this),
+ render_frame_host->GetProcess()->GetID(),
+ render_frame_host->GetRoutingID(),
+ reply_msg,
+ false),
+ &suppress_this_message);
+ }
+
+ if (suppress_this_message) {
+ // If we are suppressing messages, just reply as if the user immediately
+ // pressed "Cancel", passing true to |dialog_was_suppressed|.
+ OnDialogClosed(render_frame_host->GetProcess()->GetID(),
+ render_frame_host->GetRoutingID(), reply_msg,
+ true, false, base::string16());
+ }
+
+ // OnDialogClosed (two lines up) may have caused deletion of this object (see
+ // http://crbug.com/288961 ). The only safe thing to do here is return.
+}
+
+void WebContentsImpl::RunBeforeUnloadConfirm(
+ RenderFrameHost* render_frame_host,
+ const base::string16& message,
+ bool is_reload,
+ IPC::Message* reply_msg) {
+ RenderFrameHostImpl* rfhi =
+ static_cast<RenderFrameHostImpl*>(render_frame_host);
+ RenderViewHostImpl* rvhi =
+ static_cast<RenderViewHostImpl*>(render_frame_host->GetRenderViewHost());
+ if (delegate_)
+ delegate_->WillRunBeforeUnloadConfirm();
+
+ bool suppress_this_message =
+ rvhi->rvh_state() != RenderViewHostImpl::STATE_DEFAULT ||
+ !delegate_ ||
+ delegate_->ShouldSuppressDialogs() ||
+ !delegate_->GetJavaScriptDialogManager();
+ if (suppress_this_message) {
+ rfhi->JavaScriptDialogClosed(reply_msg, true, base::string16(), true);
+ return;
+ }
+
+ is_showing_before_unload_dialog_ = true;
+ dialog_manager_ = delegate_->GetJavaScriptDialogManager();
+ dialog_manager_->RunBeforeUnloadDialog(
+ this, message, is_reload,
+ base::Bind(&WebContentsImpl::OnDialogClosed, base::Unretained(this),
+ render_frame_host->GetProcess()->GetID(),
+ render_frame_host->GetRoutingID(), reply_msg,
+ false));
}
WebContents* WebContentsImpl::GetAsWebContents() {
return this;
}
+bool WebContentsImpl::IsNeverVisible() {
+ if (!delegate_)
+ return false;
+ return delegate_->IsNeverVisible(this);
+}
+
+RenderViewHostDelegateView* WebContentsImpl::GetDelegateView() {
+ return render_view_host_delegate_view_;
+}
+
+RendererPreferences WebContentsImpl::GetRendererPrefs(
+ BrowserContext* browser_context) const {
+ return renderer_preferences_;
+}
+
gfx::Rect WebContentsImpl::GetRootWindowResizerRect() const {
if (delegate_)
return delegate_->GetRootWindowResizerRect();
@@ -2824,11 +3320,11 @@ void WebContentsImpl::RenderViewCreated(RenderViewHost* render_view_host) {
// Don't send notifications if we are just creating a swapped-out RVH for
// the opener chain. These won't be used for view-source or WebUI, so it's
// ok to return early.
- if (static_cast<RenderViewHostImpl*>(render_view_host)->is_swapped_out())
+ if (static_cast<RenderViewHostImpl*>(render_view_host)->IsSwappedOut())
return;
if (delegate_)
- view_->SetOverscrollControllerEnabled(delegate_->CanOverscrollContent());
+ view_->SetOverscrollControllerEnabled(CanOverscrollContent());
NotificationService::current()->Notify(
NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED,
@@ -2855,7 +3351,7 @@ void WebContentsImpl::RenderViewCreated(RenderViewHost* render_view_host) {
// We tell the observers now instead of when the main RenderFrameHostImpl is
// constructed because otherwise it would be too early (i.e. IPCs sent to the
// frame would be dropped because it's not created yet).
- RenderFrameHost* main_frame = GetMainFrame();
+ RenderFrameHost* main_frame = render_view_host->GetMainFrame();
FOR_EACH_OBSERVER(
WebContentsObserver, observers_, RenderFrameCreated(main_frame));
}
@@ -2895,15 +3391,28 @@ void WebContentsImpl::RenderViewTerminated(RenderViewHost* rvh,
return;
}
+ // Ensure fullscreen mode is exited in the |delegate_| since a crashed
+ // renderer may not have made a clean exit.
+ if (IsFullscreenForCurrentTab())
+ ToggleFullscreenMode(false);
+
// Cancel any visible dialogs so they are not left dangling over the sad tab.
if (dialog_manager_)
dialog_manager_->CancelActiveAndPendingDialogs(this);
- ClearPowerSaveBlockers(rvh);
- SetIsLoading(rvh, false, NULL);
+ if (delegate_)
+ delegate_->HideValidationMessage(this);
+
+ SetIsLoading(rvh, false, true, NULL);
NotifyDisconnected();
SetIsCrashed(status, error_code);
- GetView()->OnTabCrashed(GetCrashedStatus(), crashed_error_code_);
+
+ // Reset the loading progress. TODO(avi): What does it mean to have a
+ // "renderer crash" when there is more than one renderer process serving a
+ // webpage? Once this function is called at a more granular frame level, we
+ // probably will need to more granularly reset the state here.
+ ResetLoadProgressState();
+ loading_frames_in_progress_ = 0;
FOR_EACH_OBSERVER(WebContentsObserver,
observers_,
@@ -2911,145 +3420,21 @@ void WebContentsImpl::RenderViewTerminated(RenderViewHost* rvh,
}
void WebContentsImpl::RenderViewDeleted(RenderViewHost* rvh) {
- ClearPowerSaveBlockers(rvh);
- GetRenderManager()->RenderViewDeleted(rvh);
FOR_EACH_OBSERVER(WebContentsObserver, observers_, RenderViewDeleted(rvh));
}
-void WebContentsImpl::DidGetResourceResponseStart(
- const ResourceRequestDetails& details) {
- controller_.ssl_manager()->DidStartResourceResponse(details);
-
- FOR_EACH_OBSERVER(WebContentsObserver, observers_,
- DidGetResourceResponseStart(details));
-
- // TODO(avi): Remove. http://crbug.com/170921
- NotificationService::current()->Notify(
- NOTIFICATION_RESOURCE_RESPONSE_STARTED,
- Source<WebContents>(this),
- Details<const ResourceRequestDetails>(&details));
-}
-
-void WebContentsImpl::DidGetRedirectForResourceRequest(
- const ResourceRedirectDetails& details) {
- controller_.ssl_manager()->DidReceiveResourceRedirect(details);
-
- FOR_EACH_OBSERVER(WebContentsObserver, observers_,
- DidGetRedirectForResourceRequest(details));
-
- // TODO(avi): Remove. http://crbug.com/170921
- NotificationService::current()->Notify(
- NOTIFICATION_RESOURCE_RECEIVED_REDIRECT,
- Source<WebContents>(this),
- Details<const ResourceRedirectDetails>(&details));
-}
-
-void WebContentsImpl::DidNavigate(
- RenderViewHost* rvh,
- const ViewHostMsg_FrameNavigate_Params& params) {
- if (frame_tree_.IsFirstNavigationAfterSwap()) {
- // First navigation should be a main frame navigation.
- DCHECK(PageTransitionIsMainFrame(params.transition));
- frame_tree_.OnFirstNavigationAfterSwap(params.frame_id);
- }
-
- if (PageTransitionIsMainFrame(params.transition)) {
- // When overscroll navigation gesture is enabled, a screenshot of the page
- // in its current state is taken so that it can be used during the
- // nav-gesture. It is necessary to take the screenshot here, before calling
- // RenderFrameHostManager::DidNavigateMainFrame, because that can change
- // WebContents::GetRenderViewHost to return the new host, instead of the one
- // that may have just been swapped out.
- if (delegate_ && delegate_->CanOverscrollContent())
- controller_.TakeScreenshot();
-
- GetRenderManager()->DidNavigateMainFrame(rvh);
- }
-
- // Update the site of the SiteInstance if it doesn't have one yet, unless
- // assigning a site is not necessary for this URL. In that case, the
- // SiteInstance can still be considered unused until a navigation to a real
- // page.
- if (!static_cast<SiteInstanceImpl*>(GetSiteInstance())->HasSite() &&
- ShouldAssignSiteForURL(params.url)) {
- static_cast<SiteInstanceImpl*>(GetSiteInstance())->SetSite(params.url);
- }
-
- // Need to update MIME type here because it's referred to in
- // UpdateNavigationCommands() called by RendererDidNavigate() to
- // determine whether or not to enable the encoding menu.
- // It's updated only for the main frame. For a subframe,
- // RenderView::UpdateURL does not set params.contents_mime_type.
- // (see http://code.google.com/p/chromium/issues/detail?id=2929 )
- // TODO(jungshik): Add a test for the encoding menu to avoid
- // regressing it again.
- if (PageTransitionIsMainFrame(params.transition))
- contents_mime_type_ = params.contents_mime_type;
-
- LoadCommittedDetails details;
- bool did_navigate = controller_.RendererDidNavigate(params, &details);
-
- // For now, keep track of each frame's URL in its FrameTreeNode. This lets
- // us estimate our process count for implementing OOP iframes.
- // TODO(creis): Remove this when we track which pages commit in each frame.
- frame_tree_.SetFrameUrl(params.frame_id, params.url);
-
- // Send notification about committed provisional loads. This notification is
- // different from the NAV_ENTRY_COMMITTED notification which doesn't include
- // the actual URL navigated to and isn't sent for AUTO_SUBFRAME navigations.
- if (details.type != NAVIGATION_TYPE_NAV_IGNORE) {
- // For AUTO_SUBFRAME navigations, an event for the main frame is generated
- // that is not recorded in the navigation history. For the purpose of
- // tracking navigation events, we treat this event as a sub frame navigation
- // event.
- bool is_main_frame = did_navigate ? details.is_main_frame : false;
- PageTransition transition_type = params.transition;
- // Whether or not a page transition was triggered by going backward or
- // forward in the history is only stored in the navigation controller's
- // entry list.
- if (did_navigate &&
- (controller_.GetLastCommittedEntry()->GetTransitionType() &
- PAGE_TRANSITION_FORWARD_BACK)) {
- transition_type = PageTransitionFromInt(
- params.transition | PAGE_TRANSITION_FORWARD_BACK);
- }
- // Notify observers about the commit of the provisional load.
- FOR_EACH_OBSERVER(WebContentsObserver, observers_,
- DidCommitProvisionalLoadForFrame(
- params.frame_id,
- params.frame_unique_name,
- is_main_frame,
- params.url,
- transition_type,
- rvh));
- }
-
- if (!did_navigate)
- return; // No navigation happened.
-
- // DO NOT ADD MORE STUFF TO THIS FUNCTION! Your component should either listen
- // for the appropriate notification (best) or you can add it to
- // DidNavigateMainFramePostCommit / DidNavigateAnyFramePostCommit (only if
- // necessary, please).
-
- // Run post-commit tasks.
- if (details.is_main_frame) {
- DidNavigateMainFramePostCommit(details, params);
- if (delegate_) {
- delegate_->DidNavigateMainFramePostCommit(this);
- view_->SetOverscrollControllerEnabled(delegate_->CanOverscrollContent());
- }
- }
- DidNavigateAnyFramePostCommit(rvh, details, params);
-}
-
void WebContentsImpl::UpdateState(RenderViewHost* rvh,
int32 page_id,
const PageState& page_state) {
// Ensure that this state update comes from either the active RVH or one of
// the swapped out RVHs. We don't expect to hear from any other RVHs.
- DCHECK(rvh == GetRenderViewHost() ||
- GetRenderManager()->IsOnSwappedOutList(rvh));
+ // TODO(nasko): This should go through RenderFrameHost.
+ // TODO(creis): We can't update state for cross-process subframes until we
+ // have FrameNavigationEntries. Once we do, this should be a DCHECK.
+ if (rvh != GetRenderViewHost() &&
+ !GetRenderManager()->IsRVHOnSwappedOutList(
+ static_cast<RenderViewHostImpl*>(rvh)))
+ return;
// We must be prepared to handle state updates for any page, these occur
// when the user is scrolling and entering form data, as well as when we're
@@ -3069,39 +3454,6 @@ void WebContentsImpl::UpdateState(RenderViewHost* rvh,
controller_.NotifyEntryChanged(entry, entry_index);
}
-void WebContentsImpl::UpdateTitle(RenderViewHost* rvh,
- int32 page_id,
- const base::string16& title,
- base::i18n::TextDirection title_direction) {
- // If we have a title, that's a pretty good indication that we've started
- // getting useful data.
- SetNotWaitingForResponse();
-
- // Try to find the navigation entry, which might not be the current one.
- // For example, it might be from a pending RVH for the pending entry.
- NavigationEntryImpl* entry = controller_.GetEntryWithPageID(
- rvh->GetSiteInstance(), page_id);
-
- // We can handle title updates when we don't have an entry in
- // UpdateTitleForEntry, but only if the update is from the current RVH.
- if (!entry && rvh != GetRenderViewHost())
- return;
-
- // TODO(evan): make use of title_direction.
- // http://code.google.com/p/chromium/issues/detail?id=27094
- if (!UpdateTitleForEntry(entry, title))
- return;
-
- // Broadcast notifications when the UI should be updated.
- if (entry == controller_.GetEntryAtOffset(0))
- NotifyNavigationStateChanged(INVALIDATE_TYPE_TITLE);
-}
-
-void WebContentsImpl::UpdateEncoding(RenderViewHost* render_view_host,
- const std::string& encoding) {
- SetEncoding(encoding);
-}
-
void WebContentsImpl::UpdateTargetURL(int32 page_id, const GURL& url) {
if (delegate_)
delegate_->UpdateTargetURL(this, page_id, url);
@@ -3127,12 +3479,9 @@ void WebContentsImpl::Close(RenderViewHost* rvh) {
delegate_->CloseContents(this);
}
-void WebContentsImpl::SwappedOut(RenderViewHost* rvh) {
- if (delegate_ && rvh == GetRenderViewHost())
+void WebContentsImpl::SwappedOut(RenderFrameHost* rfh) {
+ if (delegate_ && rfh->GetRenderViewHost() == GetRenderViewHost())
delegate_->SwappedOut(this);
-
- // Allow the navigation to proceed.
- GetRenderManager()->SwappedOut(rvh);
}
void WebContentsImpl::RequestMove(const gfx::Rect& new_bounds) {
@@ -3140,21 +3489,25 @@ void WebContentsImpl::RequestMove(const gfx::Rect& new_bounds) {
delegate_->MoveContents(this, new_bounds);
}
-void WebContentsImpl::DidStartLoading(RenderViewHost* render_view_host) {
- SetIsLoading(render_view_host, true, NULL);
+void WebContentsImpl::DidStartLoading(RenderFrameHost* render_frame_host,
+ bool to_different_document) {
+ SetIsLoading(render_frame_host->GetRenderViewHost(), true,
+ to_different_document, NULL);
}
-void WebContentsImpl::DidStopLoading(RenderViewHost* render_view_host) {
+void WebContentsImpl::DidStopLoading(RenderFrameHost* render_frame_host) {
scoped_ptr<LoadNotificationDetails> details;
// Use the last committed entry rather than the active one, in case a
// pending entry has been created.
NavigationEntry* entry = controller_.GetLastCommittedEntry();
+ Navigator* navigator = frame_tree_.root()->navigator();
// An entry may not exist for a stop when loading an initial blank page or
// if an iframe injected by script into a blank page finishes loading.
if (entry) {
- base::TimeDelta elapsed = base::TimeTicks::Now() - current_load_start_;
+ base::TimeDelta elapsed =
+ base::TimeTicks::Now() - navigator->GetCurrentLoadStart();
details.reset(new LoadNotificationDetails(
entry->GetVirtualURL(),
@@ -3164,7 +3517,8 @@ void WebContentsImpl::DidStopLoading(RenderViewHost* render_view_host) {
controller_.GetCurrentEntryIndex()));
}
- SetIsLoading(render_view_host, false, details.get());
+ SetIsLoading(render_frame_host->GetRenderViewHost(), false, true,
+ details.get());
}
void WebContentsImpl::DidCancelLoading() {
@@ -3174,12 +3528,20 @@ void WebContentsImpl::DidCancelLoading() {
NotifyNavigationStateChanged(INVALIDATE_TYPE_URL);
}
-void WebContentsImpl::DidChangeLoadProgress(double progress) {
- if (delegate_)
- delegate_->LoadProgressChanged(this, progress);
+void WebContentsImpl::DidAccessInitialDocument() {
+ has_accessed_initial_document_ = true;
+
+ // We may have left a failed browser-initiated navigation in the address bar
+ // to let the user edit it and try again. Clear it now that content might
+ // show up underneath it.
+ if (!IsLoading() && controller_.GetPendingEntry())
+ controller_.DiscardPendingEntry();
+
+ // Update the URL display.
+ NotifyNavigationStateChanged(content::INVALIDATE_TYPE_URL);
}
-void WebContentsImpl::DidDisownOpener(RenderViewHost* rvh) {
+void WebContentsImpl::DidDisownOpener(RenderFrameHost* render_frame_host) {
if (opener_) {
// Clear our opener so that future cross-process navigations don't have an
// opener assigned.
@@ -3190,116 +3552,62 @@ void WebContentsImpl::DidDisownOpener(RenderViewHost* rvh) {
// Notify all swapped out RenderViewHosts for this tab. This is important
// in case we go back to them, or if another window in those processes tries
// to access window.opener.
- GetRenderManager()->DidDisownOpener(rvh);
+ GetRenderManager()->DidDisownOpener(render_frame_host->GetRenderViewHost());
}
-void WebContentsImpl::DidAccessInitialDocument() {
- // Update the URL display.
- NotifyNavigationStateChanged(content::INVALIDATE_TYPE_URL);
-}
-
-void WebContentsImpl::DocumentAvailableInMainFrame(
- RenderViewHost* render_view_host) {
+void WebContentsImpl::DocumentOnLoadCompleted(
+ RenderFrameHost* render_frame_host) {
FOR_EACH_OBSERVER(WebContentsObserver, observers_,
- DocumentAvailableInMainFrame());
-}
-
-void WebContentsImpl::DocumentOnLoadCompletedInMainFrame(
- RenderViewHost* render_view_host,
- int32 page_id) {
- FOR_EACH_OBSERVER(WebContentsObserver, observers_,
- DocumentOnLoadCompletedInMainFrame(page_id));
+ DocumentOnLoadCompletedInMainFrame());
// TODO(avi): Remove. http://crbug.com/170921
NotificationService::current()->Notify(
NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
Source<WebContents>(this),
- Details<int>(&page_id));
+ NotificationService::NoDetails());
}
-void WebContentsImpl::RequestOpenURL(RenderViewHost* rvh,
- const GURL& url,
- const Referrer& referrer,
- WindowOpenDisposition disposition,
- int64 source_frame_id,
- bool should_replace_current_entry,
- bool user_gesture) {
- // If this came from a swapped out RenderViewHost, we only allow the request
- // if we are still in the same BrowsingInstance.
- if (static_cast<RenderViewHostImpl*>(rvh)->is_swapped_out() &&
- !rvh->GetSiteInstance()->IsRelatedSiteInstance(GetSiteInstance())) {
+void WebContentsImpl::UpdateTitle(RenderFrameHost* render_frame_host,
+ int32 page_id,
+ const base::string16& title,
+ base::i18n::TextDirection title_direction) {
+ RenderViewHost* rvh = render_frame_host->GetRenderViewHost();
+
+ // If we have a title, that's a pretty good indication that we've started
+ // getting useful data.
+ SetNotWaitingForResponse();
+
+ // Try to find the navigation entry, which might not be the current one.
+ // For example, it might be from a pending RVH for the pending entry.
+ NavigationEntryImpl* entry = controller_.GetEntryWithPageID(
+ rvh->GetSiteInstance(), page_id);
+
+ // We can handle title updates when we don't have an entry in
+ // UpdateTitleForEntry, but only if the update is from the current RVH.
+ // TODO(avi): Change to make decisions based on the RenderFrameHost.
+ if (!entry && rvh != GetRenderViewHost())
return;
- }
- // Delegate to RequestTransferURL because this is just the generic
- // case where |old_request_id| is empty.
- // TODO(creis): Pass the redirect_chain into this method to support client
- // redirects. http://crbug.com/311721.
- std::vector<GURL> redirect_chain;
- RequestTransferURL(url, redirect_chain, referrer, PAGE_TRANSITION_LINK,
- disposition, source_frame_id, GlobalRequestID(),
- should_replace_current_entry, user_gesture);
-}
+ // TODO(evan): make use of title_direction.
+ // http://code.google.com/p/chromium/issues/detail?id=27094
+ if (!UpdateTitleForEntry(entry, title))
+ return;
-void WebContentsImpl::RequestTransferURL(
- const GURL& url,
- const std::vector<GURL>& redirect_chain,
- const Referrer& referrer,
- PageTransition page_transition,
- WindowOpenDisposition disposition,
- int64 source_frame_id,
- const GlobalRequestID& old_request_id,
- bool should_replace_current_entry,
- bool user_gesture) {
- WebContents* new_contents = NULL;
- GURL dest_url(url);
- if (!GetContentClient()->browser()->ShouldAllowOpenURL(
- GetSiteInstance(), url))
- dest_url = GURL(kAboutBlankURL);
-
- // TODO(creis): Look up the FrameTreeNode ID corresponding to source_frame_id.
- int frame_tree_node_id = -1;
- OpenURLParams params(dest_url, referrer, source_frame_id,
- frame_tree_node_id, disposition,
- page_transition, true /* is_renderer_initiated */);
- if (redirect_chain.size() > 0)
- params.redirect_chain = redirect_chain;
- params.transferred_global_request_id = old_request_id;
- params.should_replace_current_entry = should_replace_current_entry;
- params.user_gesture = user_gesture;
-
- if (GetRenderManager()->web_ui()) {
- // Web UI pages sometimes want to override the page transition type for
- // link clicks (e.g., so the new tab page can specify AUTO_BOOKMARK for
- // automatically generated suggestions). We don't override other types
- // like TYPED because they have different implications (e.g., autocomplete).
- if (PageTransitionCoreTypeIs(params.transition, PAGE_TRANSITION_LINK))
- params.transition = GetRenderManager()->web_ui()->GetLinkTransitionType();
-
- // Note also that we hide the referrer for Web UI pages. We don't really
- // want web sites to see a referrer of "chrome://blah" (and some
- // chrome: URLs might have search terms or other stuff we don't want to
- // send to the site), so we send no referrer.
- params.referrer = Referrer();
-
- // Navigations in Web UI pages count as browser-initiated navigations.
- params.is_renderer_initiated = false;
- }
-
- new_contents = OpenURL(params);
+ // Broadcast notifications when the UI should be updated.
+ if (entry == controller_.GetEntryAtOffset(0))
+ NotifyNavigationStateChanged(INVALIDATE_TYPE_TITLE);
+}
- if (new_contents) {
- // Notify observers.
- FOR_EACH_OBSERVER(WebContentsObserver, observers_,
- DidOpenRequestedURL(new_contents,
- dest_url,
- referrer,
- disposition,
- params.transition,
- source_frame_id));
- }
+void WebContentsImpl::UpdateEncoding(RenderFrameHost* render_frame_host,
+ const std::string& encoding) {
+ SetEncoding(encoding);
}
+void WebContentsImpl::DocumentAvailableInMainFrame(
+ RenderViewHost* render_view_host) {
+ FOR_EACH_OBSERVER(WebContentsObserver, observers_,
+ DocumentAvailableInMainFrame());
+}
void WebContentsImpl::RouteCloseEvent(RenderViewHost* rvh) {
// Tell the active RenderViewHost to run unload handlers and close, as long
// as the request came from a RenderViewHost in the same BrowsingInstance.
@@ -3330,15 +3638,9 @@ void WebContentsImpl::RouteMessageEvent(
MessagePortMessageFilter* message_port_message_filter =
static_cast<RenderProcessHostImpl*>(GetRenderProcessHost())
->message_port_message_filter();
- std::vector<int> new_routing_ids(params.message_port_ids.size());
- for (size_t i = 0; i < params.message_port_ids.size(); ++i) {
- new_routing_ids[i] = message_port_message_filter->GetNextRoutingID();
- MessagePortService::GetInstance()->UpdateMessagePort(
- params.message_port_ids[i],
- message_port_message_filter,
- new_routing_ids[i]);
- }
- new_params.new_routing_ids = new_routing_ids;
+ message_port_message_filter->UpdateMessagePortsWithNewRoutes(
+ params.message_port_ids,
+ &new_params.new_routing_ids);
}
// If there is a source_routing_id, translate it to the routing ID for
@@ -3378,81 +3680,6 @@ void WebContentsImpl::RouteMessageEvent(
Send(new ViewMsg_PostMessageEvent(GetRoutingID(), new_params));
}
-void WebContentsImpl::RunJavaScriptMessage(
- RenderViewHost* rvh,
- const base::string16& message,
- const base::string16& default_prompt,
- const GURL& frame_url,
- JavaScriptMessageType javascript_message_type,
- IPC::Message* reply_msg,
- bool* did_suppress_message) {
- // Suppress JavaScript dialogs when requested. Also suppress messages when
- // showing an interstitial as it's shown over the previous page and we don't
- // want the hidden page's dialogs to interfere with the interstitial.
- bool suppress_this_message =
- static_cast<RenderViewHostImpl*>(rvh)->is_swapped_out() ||
- ShowingInterstitialPage() ||
- !delegate_ ||
- delegate_->ShouldSuppressDialogs() ||
- !delegate_->GetJavaScriptDialogManager();
-
- if (!suppress_this_message) {
- std::string accept_lang = GetContentClient()->browser()->
- GetAcceptLangs(GetBrowserContext());
- dialog_manager_ = delegate_->GetJavaScriptDialogManager();
- dialog_manager_->RunJavaScriptDialog(
- this,
- frame_url.GetOrigin(),
- accept_lang,
- javascript_message_type,
- message,
- default_prompt,
- base::Bind(&WebContentsImpl::OnDialogClosed,
- base::Unretained(this),
- rvh,
- reply_msg),
- &suppress_this_message);
- }
-
- *did_suppress_message = suppress_this_message;
-
- if (suppress_this_message) {
- // If we are suppressing messages, just reply as if the user immediately
- // pressed "Cancel".
- OnDialogClosed(rvh, reply_msg, false, base::string16());
- }
-
- // OnDialogClosed (two lines up) may have caused deletion of this object (see
- // http://crbug.com/288961 ). The only safe thing to do here is return.
-}
-
-void WebContentsImpl::RunBeforeUnloadConfirm(RenderViewHost* rvh,
- const base::string16& message,
- bool is_reload,
- IPC::Message* reply_msg) {
- RenderViewHostImpl* rvhi = static_cast<RenderViewHostImpl*>(rvh);
- if (delegate_)
- delegate_->WillRunBeforeUnloadConfirm();
-
- bool suppress_this_message =
- rvhi->is_swapped_out() ||
- !delegate_ ||
- delegate_->ShouldSuppressDialogs() ||
- !delegate_->GetJavaScriptDialogManager();
- if (suppress_this_message) {
- // The reply must be sent to the RVH that sent the request.
- rvhi->JavaScriptDialogClosed(reply_msg, true, base::string16());
- return;
- }
-
- is_showing_before_unload_dialog_ = true;
- dialog_manager_ = delegate_->GetJavaScriptDialogManager();
- dialog_manager_->RunBeforeUnloadDialog(
- this, message, is_reload,
- base::Bind(&WebContentsImpl::OnDialogClosed, base::Unretained(this), rvh,
- reply_msg));
-}
-
bool WebContentsImpl::AddMessageToConsole(int32 level,
const base::string16& message,
int32 line_no,
@@ -3476,8 +3703,8 @@ WebPreferences WebContentsImpl::GetWebkitPrefs() {
int WebContentsImpl::CreateSwappedOutRenderView(
SiteInstance* instance) {
- return GetRenderManager()->CreateRenderView(instance, MSG_ROUTING_NONE,
- true, true);
+ return GetRenderManager()->CreateRenderFrame(instance, MSG_ROUTING_NONE,
+ true, true);
}
void WebContentsImpl::OnUserGesture() {
@@ -3524,7 +3751,7 @@ void WebContentsImpl::RendererUnresponsive(RenderViewHost* rvh,
// close. Otherwise, pretend the unload listeners have all fired and close
// the tab.
bool close = true;
- if (is_during_beforeunload) {
+ if (is_during_beforeunload && delegate_) {
delegate_->BeforeUnloadFired(this, true, &close);
}
if (close)
@@ -3562,11 +3789,6 @@ void WebContentsImpl::LoadStateChanged(
}
}
-void WebContentsImpl::WorkerCrashed() {
- if (delegate_)
- delegate_->WorkerCrashed(this);
-}
-
void WebContentsImpl::BeforeUnloadFiredFromRenderManager(
bool proceed, const base::TimeTicks& proceed_time,
bool* proceed_to_fire_unload) {
@@ -3607,7 +3829,7 @@ void WebContentsImpl::NotifySwappedFromRenderManager(RenderViewHost* old_host,
// Make sure the visible RVH reflects the new delegate's preferences.
if (delegate_)
- view_->SetOverscrollControllerEnabled(delegate_->CanOverscrollContent());
+ view_->SetOverscrollControllerEnabled(CanOverscrollContent());
view_->RenderViewSwappedIn(new_host);
}
@@ -3646,8 +3868,8 @@ int WebContentsImpl::CreateOpenerRenderViews(SiteInstance* instance) {
// Create a swapped out RenderView in the given SiteInstance if none exists,
// setting its opener to the given route_id. Return the new view's route_id.
- return GetRenderManager()->CreateRenderView(instance, opener_route_id,
- true, true);
+ return GetRenderManager()->CreateRenderFrame(instance, opener_route_id,
+ true, true);
}
NavigationControllerImpl& WebContentsImpl::GetControllerForRenderManager() {
@@ -3664,10 +3886,24 @@ NavigationEntry*
}
bool WebContentsImpl::CreateRenderViewForRenderManager(
- RenderViewHost* render_view_host, int opener_route_id) {
+ RenderViewHost* render_view_host,
+ int opener_route_id,
+ int proxy_routing_id,
+ bool for_main_frame) {
TRACE_EVENT0("browser", "WebContentsImpl::CreateRenderViewForRenderManager");
// Can be NULL during tests.
- RenderWidgetHostView* rwh_view = view_->CreateViewForWidget(render_view_host);
+ RenderWidgetHostViewBase* rwh_view;
+ // TODO(kenrb): RenderWidgetHostViewChildFrame special casing is temporary
+ // until RenderWidgetHost is attached to RenderFrameHost. We need to special
+ // case this because RWH is still a base class of RenderViewHost, and child
+ // frame RWHVs are unique in that they do not have their own WebContents.
+ if (!for_main_frame) {
+ RenderWidgetHostViewChildFrame* rwh_view_child =
+ new RenderWidgetHostViewChildFrame(render_view_host);
+ rwh_view = rwh_view_child;
+ } else {
+ rwh_view = view_->CreateViewForWidget(render_view_host);
+ }
// Now that the RenderView has been created, we need to tell it its size.
if (rwh_view)
@@ -3681,7 +3917,9 @@ bool WebContentsImpl::CreateRenderViewForRenderManager(
if (!static_cast<RenderViewHostImpl*>(
render_view_host)->CreateRenderView(base::string16(),
opener_route_id,
- max_page_id)) {
+ proxy_routing_id,
+ max_page_id,
+ created_with_opener_)) {
return false;
}
@@ -3698,6 +3936,7 @@ bool WebContentsImpl::CreateRenderViewForRenderManager(
}
#if defined(OS_ANDROID)
+
base::android::ScopedJavaLocalRef<jobject>
WebContentsImpl::GetJavaWebContents() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
@@ -3713,38 +3952,78 @@ WebContentsImpl::GetJavaWebContents() {
bool WebContentsImpl::CreateRenderViewForInitialEmptyDocument() {
return CreateRenderViewForRenderManager(GetRenderViewHost(),
- MSG_ROUTING_NONE);
+ MSG_ROUTING_NONE,
+ MSG_ROUTING_NONE,
+ true);
}
+
+#elif defined(OS_MACOSX)
+
+void WebContentsImpl::SetAllowOverlappingViews(bool overlapping) {
+ view_->SetAllowOverlappingViews(overlapping);
+}
+
+bool WebContentsImpl::GetAllowOverlappingViews() {
+ return view_->GetAllowOverlappingViews();
+}
+
+void WebContentsImpl::SetOverlayView(WebContents* overlay,
+ const gfx::Point& offset) {
+ view_->SetOverlayView(static_cast<WebContentsImpl*>(overlay)->GetView(),
+ offset);
+}
+
+void WebContentsImpl::RemoveOverlayView() {
+ view_->RemoveOverlayView();
+}
+
#endif
-void WebContentsImpl::OnDialogClosed(RenderViewHost* rvh,
+void WebContentsImpl::OnDialogClosed(int render_process_id,
+ int render_frame_id,
IPC::Message* reply_msg,
+ bool dialog_was_suppressed,
bool success,
const base::string16& user_input) {
+ RenderFrameHostImpl* rfh = RenderFrameHostImpl::FromID(render_process_id,
+ render_frame_id);
+ last_dialog_suppressed_ = dialog_was_suppressed;
+
if (is_showing_before_unload_dialog_ && !success) {
// If a beforeunload dialog is canceled, we need to stop the throbber from
// spinning, since we forced it to start spinning in Navigate.
- DidStopLoading(rvh);
+ if (rfh)
+ DidStopLoading(rfh);
controller_.DiscardNonCommittedEntries();
FOR_EACH_OBSERVER(WebContentsObserver, observers_,
BeforeUnloadDialogCancelled());
}
+
is_showing_before_unload_dialog_ = false;
- static_cast<RenderViewHostImpl*>(
- rvh)->JavaScriptDialogClosed(reply_msg, success, user_input);
+ if (rfh) {
+ rfh->JavaScriptDialogClosed(reply_msg, success, user_input,
+ dialog_was_suppressed);
+ } else {
+ // Don't leak the sync IPC reply if the RFH or process is gone.
+ delete reply_msg;
+ }
}
void WebContentsImpl::SetEncoding(const std::string& encoding) {
- encoding_ = GetContentClient()->browser()->
+ if (encoding == last_reported_encoding_)
+ return;
+ last_reported_encoding_ = encoding;
+
+ canonical_encoding_ = GetContentClient()->browser()->
GetCanonicalEncodingNameByAliasName(encoding);
}
void WebContentsImpl::CreateViewAndSetSizeForRVH(RenderViewHost* rvh) {
- RenderWidgetHostView* rwh_view = view_->CreateViewForWidget(rvh);
+ RenderWidgetHostViewBase* rwh_view = view_->CreateViewForWidget(rvh);
// Can be NULL during tests.
if (rwh_view)
- rwh_view->SetSize(GetView()->GetContainerSize());
+ rwh_view->SetSize(GetContainerBounds().size());
}
bool WebContentsImpl::IsHidden() {
@@ -3772,17 +4051,10 @@ BrowserPluginEmbedder* WebContentsImpl::GetBrowserPluginEmbedder() const {
return browser_plugin_embedder_.get();
}
-BrowserPluginGuestManager*
- WebContentsImpl::GetBrowserPluginGuestManager() const {
- return static_cast<BrowserPluginGuestManager*>(
- GetBrowserContext()->GetUserData(
- browser_plugin::kBrowserPluginGuestManagerKeyName));
-}
-
void WebContentsImpl::ClearPowerSaveBlockers(
- RenderViewHost* render_view_host) {
- STLDeleteValues(&power_save_blockers_[render_view_host]);
- power_save_blockers_.erase(render_view_host);
+ RenderFrameHost* render_frame_host) {
+ STLDeleteValues(&power_save_blockers_[render_frame_host]);
+ power_save_blockers_.erase(render_frame_host);
}
void WebContentsImpl::ClearAllPowerSaveBlockers() {
@@ -3792,20 +4064,28 @@ void WebContentsImpl::ClearAllPowerSaveBlockers() {
power_save_blockers_.clear();
}
-gfx::Size WebContentsImpl::GetSizeForNewRenderView() const {
+gfx::Size WebContentsImpl::GetSizeForNewRenderView() {
gfx::Size size;
if (delegate_)
size = delegate_->GetSizeForNewRenderView(this);
if (size.IsEmpty())
- size = view_->GetContainerSize();
+ size = GetContainerBounds().size();
return size;
}
void WebContentsImpl::OnFrameRemoved(
RenderViewHostImpl* render_view_host,
- int64 frame_id) {
+ int frame_routing_id) {
FOR_EACH_OBSERVER(WebContentsObserver, observers_,
- FrameDetached(render_view_host, frame_id));
+ FrameDetached(render_view_host, frame_routing_id));
+}
+
+void WebContentsImpl::OnPreferredSizeChanged(const gfx::Size& old_size) {
+ if (!delegate_)
+ return;
+ const gfx::Size new_size = GetPreferredSize();
+ if (new_size != old_size)
+ delegate_->UpdatePreferredSize(this, new_size);
}
} // namespace content
diff --git a/chromium/content/browser/web_contents/web_contents_impl.h b/chromium/content/browser/web_contents/web_contents_impl.h
index 49202248fa9..ba10e415d14 100644
--- a/chromium/content/browser/web_contents/web_contents_impl.h
+++ b/chromium/content/browser/web_contents/web_contents_impl.h
@@ -24,6 +24,7 @@
#include "content/browser/renderer_host/render_view_host_delegate.h"
#include "content/browser/renderer_host/render_widget_host_delegate.h"
#include "content/common/content_export.h"
+#include "content/public/browser/color_chooser.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/web_contents.h"
@@ -44,34 +45,36 @@ namespace content {
class BrowserPluginEmbedder;
class BrowserPluginGuest;
class BrowserPluginGuestManager;
-class ColorChooser;
class DateTimeChooserAndroid;
class DownloadItem;
+class GeolocationDispatcherHost;
class InterstitialPageImpl;
-class JavaBridgeDispatcherHostManager;
class JavaScriptDialogManager;
+class MidiDispatcherHost;
class PowerSaveBlocker;
class RenderViewHost;
class RenderViewHostDelegateView;
class RenderViewHostImpl;
class RenderWidgetHostImpl;
-class RenderWidgetHostViewPort;
class SavePackage;
-class SessionStorageNamespaceImpl;
+class ScreenOrientationDispatcherHost;
class SiteInstance;
class TestWebContents;
class WebContentsDelegate;
class WebContentsImpl;
class WebContentsObserver;
-class WebContentsViewPort;
+class WebContentsView;
class WebContentsViewDelegate;
+struct AXEventNotificationDetails;
struct ColorSuggestion;
struct FaviconURL;
struct LoadNotificationDetails;
+struct ResourceRedirectDetails;
+struct ResourceRequestDetails;
// Factory function for the implementations that content knows about. Takes
// ownership of |delegate|.
-WebContentsViewPort* CreateWebContentsView(
+WebContentsView* CreateWebContentsView(
WebContentsImpl* web_contents,
WebContentsViewDelegate* delegate,
RenderViewHostDelegateView** render_view_host_delegate_view);
@@ -96,13 +99,6 @@ class CONTENT_EXPORT WebContentsImpl
// opener is closed or the page clears its window.opener.
WebContentsImpl* opener() const { return opener_; }
- // Creates a WebContents to be used as a browser plugin guest.
- static BrowserPluginGuest* CreateGuest(
- BrowserContext* browser_context,
- content::SiteInstance* site_instance,
- int guest_instance_id,
- scoped_ptr<base::DictionaryValue> extra_params);
-
// Creates a swapped out RenderView. This is used by the browser plugin to
// create a swapped out RenderView in the embedder render process for the
// guest, to expose the guest's window object to the embedder.
@@ -117,10 +113,6 @@ class CONTENT_EXPORT WebContentsImpl
SavePackage* save_package() const { return save_package_.get(); }
#if defined(OS_ANDROID)
- JavaBridgeDispatcherHostManager* java_bridge_dispatcher_host_manager() const {
- return java_bridge_dispatcher_host_manager_.get();
- }
-
// In Android WebView, the RenderView needs created even there is no
// navigation entry, this allows Android WebViews to use
// javascript: URLs that load into the DOMWindow before the first page
@@ -130,6 +122,7 @@ class CONTENT_EXPORT WebContentsImpl
#endif
// Expose the render manager for testing.
+ // TODO(creis): Remove this now that we can get to it via FrameTreeNode.
RenderFrameHostManager* GetRenderManagerForTesting();
// Returns guest browser plugin object, or NULL if this WebContents is not a
@@ -145,10 +138,6 @@ class CONTENT_EXPORT WebContentsImpl
// an embedder.
BrowserPluginEmbedder* GetBrowserPluginEmbedder() const;
- // Returns the BrowserPluginGuestManager object, or NULL if this web contents
- // does not have a BrowserPluginGuestManager.
- BrowserPluginGuestManager* GetBrowserPluginGuestManager() const;
-
// Gets the current fullscreen render widget's routing ID. Returns
// MSG_ROUTING_NONE when there is no fullscreen render widget.
int GetFullscreenWidgetRoutingID() const;
@@ -161,10 +150,22 @@ class CONTENT_EXPORT WebContentsImpl
void DragSourceEndedAt(int client_x, int client_y, int screen_x,
int screen_y, blink::WebDragOperation operation);
- // Informs the render view host and the BrowserPluginEmbedder, if present, of
- // a Drag Source Move.
- void DragSourceMovedTo(int client_x, int client_y,
- int screen_x, int screen_y);
+ // A response has been received for a resource request.
+ void DidGetResourceResponseStart(
+ const ResourceRequestDetails& details);
+
+ // A redirect was received while requesting a resource.
+ void DidGetRedirectForResourceRequest(
+ RenderViewHost* render_view_host,
+ const ResourceRedirectDetails& details);
+
+ WebContentsView* GetView() const;
+
+ GeolocationDispatcherHost* geolocation_dispatcher_host() {
+ return geolocation_dispatcher_host_.get();
+ }
+
+ bool should_normally_be_visible() { return should_normally_be_visible_; }
// WebContents ------------------------------------------------------
virtual WebContentsDelegate* GetDelegate() OVERRIDE;
@@ -172,26 +173,26 @@ class CONTENT_EXPORT WebContentsImpl
virtual NavigationControllerImpl& GetController() OVERRIDE;
virtual const NavigationControllerImpl& GetController() const OVERRIDE;
virtual BrowserContext* GetBrowserContext() const OVERRIDE;
+ virtual const GURL& GetURL() const OVERRIDE;
+ virtual const GURL& GetVisibleURL() const OVERRIDE;
+ virtual const GURL& GetLastCommittedURL() const OVERRIDE;
virtual RenderProcessHost* GetRenderProcessHost() const OVERRIDE;
virtual RenderFrameHost* GetMainFrame() OVERRIDE;
+ virtual RenderFrameHost* GetFocusedFrame() OVERRIDE;
+ virtual void ForEachFrame(
+ const base::Callback<void(RenderFrameHost*)>& on_frame) OVERRIDE;
+ virtual void SendToAllFrames(IPC::Message* message) OVERRIDE;
virtual RenderViewHost* GetRenderViewHost() const OVERRIDE;
- virtual void GetRenderViewHostAtPosition(
- int x,
- int y,
- const GetRenderViewHostCallback& callback) OVERRIDE;
- virtual WebContents* GetEmbedderWebContents() const OVERRIDE;
- virtual int GetEmbeddedInstanceID() const OVERRIDE;
virtual int GetRoutingID() const OVERRIDE;
virtual RenderWidgetHostView* GetRenderWidgetHostView() const OVERRIDE;
virtual RenderWidgetHostView* GetFullscreenRenderWidgetHostView() const
OVERRIDE;
- virtual WebContentsView* GetView() const OVERRIDE;
virtual WebUI* CreateWebUI(const GURL& url) OVERRIDE;
virtual WebUI* GetWebUI() const OVERRIDE;
virtual WebUI* GetCommittedWebUI() const OVERRIDE;
virtual void SetUserAgentOverride(const std::string& override) OVERRIDE;
virtual const std::string& GetUserAgentOverride() const OVERRIDE;
-#if defined(OS_WIN) && defined(USE_AURA)
+#if defined(OS_WIN)
virtual void SetParentNativeViewAccessible(
gfx::NativeViewAccessible accessible_parent) OVERRIDE;
#endif
@@ -202,6 +203,7 @@ class CONTENT_EXPORT WebContentsImpl
virtual SiteInstance* GetSiteInstance() const OVERRIDE;
virtual SiteInstance* GetPendingSiteInstance() const OVERRIDE;
virtual bool IsLoading() const OVERRIDE;
+ virtual bool IsLoadingToDifferentDocument() const OVERRIDE;
virtual bool IsWaitingForResponse() const OVERRIDE;
virtual const net::LoadStateWithParam& GetLoadState() const OVERRIDE;
virtual const base::string16& GetLoadStateHost() const OVERRIDE;
@@ -210,7 +212,7 @@ class CONTENT_EXPORT WebContentsImpl
virtual std::set<GURL> GetSitesInTab() const OVERRIDE;
virtual const std::string& GetEncoding() const OVERRIDE;
virtual bool DisplayedInsecureContent() const OVERRIDE;
- virtual void IncrementCapturerCount() OVERRIDE;
+ virtual void IncrementCapturerCount(const gfx::Size& capture_size) OVERRIDE;
virtual void DecrementCapturerCount() OVERRIDE;
virtual int GetCapturerCount() const OVERRIDE;
virtual bool IsCrashed() const OVERRIDE;
@@ -219,12 +221,40 @@ class CONTENT_EXPORT WebContentsImpl
virtual base::TerminationStatus GetCrashedStatus() const OVERRIDE;
virtual bool IsBeingDestroyed() const OVERRIDE;
virtual void NotifyNavigationStateChanged(unsigned changed_flags) OVERRIDE;
- virtual base::TimeTicks GetLastSelectedTime() const OVERRIDE;
+ virtual base::TimeTicks GetLastActiveTime() const OVERRIDE;
virtual void WasShown() OVERRIDE;
virtual void WasHidden() OVERRIDE;
virtual bool NeedToFireBeforeUnload() OVERRIDE;
+ virtual void DispatchBeforeUnload(bool for_cross_site_transition) OVERRIDE;
virtual void Stop() OVERRIDE;
virtual WebContents* Clone() OVERRIDE;
+ virtual void ReloadFocusedFrame(bool ignore_cache) OVERRIDE;
+ virtual void Undo() OVERRIDE;
+ virtual void Redo() OVERRIDE;
+ virtual void Cut() OVERRIDE;
+ virtual void Copy() OVERRIDE;
+ virtual void CopyToFindPboard() OVERRIDE;
+ virtual void Paste() OVERRIDE;
+ virtual void PasteAndMatchStyle() OVERRIDE;
+ virtual void Delete() OVERRIDE;
+ virtual void SelectAll() OVERRIDE;
+ virtual void Unselect() OVERRIDE;
+ virtual void Replace(const base::string16& word) OVERRIDE;
+ virtual void ReplaceMisspelling(const base::string16& word) OVERRIDE;
+ virtual void NotifyContextMenuClosed(
+ const CustomContextMenuContext& context) OVERRIDE;
+ virtual void ExecuteCustomContextMenuCommand(
+ int action, const CustomContextMenuContext& context) OVERRIDE;
+ virtual gfx::NativeView GetNativeView() OVERRIDE;
+ virtual gfx::NativeView GetContentNativeView() OVERRIDE;
+ virtual gfx::NativeWindow GetTopLevelNativeWindow() OVERRIDE;
+ virtual gfx::Rect GetContainerBounds() OVERRIDE;
+ virtual gfx::Rect GetViewBounds() OVERRIDE;
+ virtual DropData* GetDropData() OVERRIDE;
+ virtual void Focus() OVERRIDE;
+ virtual void SetInitialFocus() OVERRIDE;
+ virtual void StoreFocus() OVERRIDE;
+ virtual void RestoreFocus() OVERRIDE;
virtual void FocusThroughTabTraversal(bool reverse) OVERRIDE;
virtual bool ShowingInterstitialPage() const OVERRIDE;
virtual InterstitialPage* GetInterstitialPage() const OVERRIDE;
@@ -239,8 +269,6 @@ class CONTENT_EXPORT WebContentsImpl
const base::FilePath& file,
const base::Callback<void(int64)>& callback)
OVERRIDE;
- virtual bool IsActiveEntry(int32 page_id) OVERRIDE;
-
virtual const std::string& GetContentsMimeType() const OVERRIDE;
virtual bool WillNotifyDisconnection() const OVERRIDE;
virtual void SetOverrideEncoding(const std::string& encoding) OVERRIDE;
@@ -251,7 +279,6 @@ class CONTENT_EXPORT WebContentsImpl
virtual void UserGestureDone() OVERRIDE;
virtual void SetClosedByUserGesture(bool value) OVERRIDE;
virtual bool GetClosedByUserGesture() const OVERRIDE;
- virtual double GetZoomLevel() const OVERRIDE;
virtual int GetZoomPercent(bool* enable_increment,
bool* enable_decrement) const OVERRIDE;
virtual void ViewSource() OVERRIDE;
@@ -268,10 +295,21 @@ class CONTENT_EXPORT WebContentsImpl
bool is_favicon,
uint32_t max_bitmap_size,
const ImageDownloadCallback& callback) OVERRIDE;
- virtual void SetZoomLevel(double level) OVERRIDE;
+ virtual bool IsSubframe() const OVERRIDE;
+ virtual void Find(int request_id,
+ const base::string16& search_text,
+ const blink::WebFindOptions& options) OVERRIDE;
+ virtual void StopFinding(StopFindAction action) OVERRIDE;
+ virtual void InsertCSS(const std::string& css) OVERRIDE;
#if defined(OS_ANDROID)
virtual base::android::ScopedJavaLocalRef<jobject> GetJavaWebContents()
OVERRIDE;
+#elif defined(OS_MACOSX)
+ virtual void SetAllowOverlappingViews(bool overlapping) OVERRIDE;
+ virtual bool GetAllowOverlappingViews() OVERRIDE;
+ virtual void SetOverlayView(WebContents* overlay,
+ const gfx::Point& offset) OVERRIDE;
+ virtual void RemoveOverlayView() OVERRIDE;
#endif
// Implementation of PageNavigator.
@@ -283,19 +321,45 @@ class CONTENT_EXPORT WebContentsImpl
// RenderFrameHostDelegate ---------------------------------------------------
virtual bool OnMessageReceived(RenderFrameHost* render_frame_host,
const IPC::Message& message) OVERRIDE;
+ virtual const GURL& GetMainFrameLastCommittedURL() const OVERRIDE;
virtual void RenderFrameCreated(RenderFrameHost* render_frame_host) OVERRIDE;
virtual void RenderFrameDeleted(RenderFrameHost* render_frame_host) OVERRIDE;
+ virtual void DidStartLoading(RenderFrameHost* render_frame_host,
+ bool to_different_document) OVERRIDE;
+ virtual void SwappedOut(RenderFrameHost* render_frame_host) OVERRIDE;
+ virtual void WorkerCrashed(RenderFrameHost* render_frame_host) OVERRIDE;
+ virtual void ShowContextMenu(RenderFrameHost* render_frame_host,
+ const ContextMenuParams& params) OVERRIDE;
+ virtual void RunJavaScriptMessage(RenderFrameHost* render_frame_host,
+ const base::string16& message,
+ const base::string16& default_prompt,
+ const GURL& frame_url,
+ JavaScriptMessageType type,
+ IPC::Message* reply_msg) OVERRIDE;
+ virtual void RunBeforeUnloadConfirm(RenderFrameHost* render_frame_host,
+ const base::string16& message,
+ bool is_reload,
+ IPC::Message* reply_msg) OVERRIDE;
+ virtual void DidAccessInitialDocument() OVERRIDE;
+ virtual void DidDisownOpener(RenderFrameHost* render_frame_host) OVERRIDE;
+ virtual void DocumentOnLoadCompleted(
+ RenderFrameHost* render_frame_host) OVERRIDE;
+ virtual void UpdateTitle(RenderFrameHost* render_frame_host,
+ int32 page_id,
+ const base::string16& title,
+ base::i18n::TextDirection title_direction) OVERRIDE;
+ virtual void UpdateEncoding(RenderFrameHost* render_frame_host,
+ const std::string& encoding) OVERRIDE;
+ virtual WebContents* GetAsWebContents() OVERRIDE;
+ virtual bool IsNeverVisible() OVERRIDE;
// RenderViewHostDelegate ----------------------------------------------------
virtual RenderViewHostDelegateView* GetDelegateView() OVERRIDE;
- virtual RenderViewHostDelegate::RendererManagement*
- GetRendererManagementDelegate() OVERRIDE;
virtual bool OnMessageReceived(RenderViewHost* render_view_host,
const IPC::Message& message) OVERRIDE;
- virtual const GURL& GetURL() const OVERRIDE;
- virtual const GURL& GetVisibleURL() const OVERRIDE;
- virtual const GURL& GetLastCommittedURL() const OVERRIDE;
- virtual WebContents* GetAsWebContents() OVERRIDE;
+ // RenderFrameHostDelegate has the same method, so list it there because this
+ // interface is going away.
+ // virtual WebContents* GetAsWebContents() OVERRIDE;
virtual gfx::Rect GetRootWindowResizerRect() const OVERRIDE;
virtual void RenderViewCreated(RenderViewHost* render_view_host) OVERRIDE;
virtual void RenderViewReady(RenderViewHost* render_view_host) OVERRIDE;
@@ -303,78 +367,19 @@ class CONTENT_EXPORT WebContentsImpl
base::TerminationStatus status,
int error_code) OVERRIDE;
virtual void RenderViewDeleted(RenderViewHost* render_view_host) OVERRIDE;
- virtual void DidRedirectProvisionalLoad(
- RenderViewHost* render_view_host,
- int32 page_id,
- const GURL& source_url,
- const GURL& target_url) OVERRIDE;
- virtual void DidFailProvisionalLoadWithError(
- RenderViewHost* render_view_host,
- const ViewHostMsg_DidFailProvisionalLoadWithError_Params& params)
- OVERRIDE;
- virtual void DidGetResourceResponseStart(
- const ResourceRequestDetails& details) OVERRIDE;
- virtual void DidGetRedirectForResourceRequest(
- const ResourceRedirectDetails& details) OVERRIDE;
- virtual void DidNavigate(
- RenderViewHost* render_view_host,
- const ViewHostMsg_FrameNavigate_Params& params) OVERRIDE;
virtual void UpdateState(RenderViewHost* render_view_host,
int32 page_id,
const PageState& page_state) OVERRIDE;
- virtual void UpdateTitle(RenderViewHost* render_view_host,
- int32 page_id,
- const base::string16& title,
- base::i18n::TextDirection title_direction) OVERRIDE;
- virtual void UpdateEncoding(RenderViewHost* render_view_host,
- const std::string& encoding) OVERRIDE;
virtual void UpdateTargetURL(int32 page_id, const GURL& url) OVERRIDE;
virtual void Close(RenderViewHost* render_view_host) OVERRIDE;
virtual void RequestMove(const gfx::Rect& new_bounds) OVERRIDE;
- virtual void SwappedOut(RenderViewHost* render_view_host) OVERRIDE;
- virtual void DidStartLoading(RenderViewHost* render_view_host) OVERRIDE;
- virtual void DidStopLoading(RenderViewHost* render_view_host) OVERRIDE;
virtual void DidCancelLoading() OVERRIDE;
- virtual void DidChangeLoadProgress(double progress) OVERRIDE;
- virtual void DidDisownOpener(RenderViewHost* rvh) OVERRIDE;
- virtual void DidAccessInitialDocument() OVERRIDE;
virtual void DocumentAvailableInMainFrame(
RenderViewHost* render_view_host) OVERRIDE;
- virtual void DocumentOnLoadCompletedInMainFrame(
- RenderViewHost* render_view_host,
- int32 page_id) OVERRIDE;
- virtual void RequestOpenURL(RenderViewHost* rvh,
- const GURL& url,
- const Referrer& referrer,
- WindowOpenDisposition disposition,
- int64 source_frame_id,
- bool should_replace_current_entry,
- bool user_gesture) OVERRIDE;
- virtual void RequestTransferURL(
- const GURL& url,
- const std::vector<GURL>& redirect_chain,
- const Referrer& referrer,
- PageTransition page_transition,
- WindowOpenDisposition disposition,
- int64 source_frame_id,
- const GlobalRequestID& transferred_global_request_id,
- bool should_replace_current_entry,
- bool user_gesture) OVERRIDE;
virtual void RouteCloseEvent(RenderViewHost* rvh) OVERRIDE;
virtual void RouteMessageEvent(
RenderViewHost* rvh,
const ViewMsg_PostMessage_Params& params) OVERRIDE;
- virtual void RunJavaScriptMessage(RenderViewHost* rvh,
- const base::string16& message,
- const base::string16& default_prompt,
- const GURL& frame_url,
- JavaScriptMessageType type,
- IPC::Message* reply_msg,
- bool* did_suppress_message) OVERRIDE;
- virtual void RunBeforeUnloadConfirm(RenderViewHost* rvh,
- const base::string16& message,
- bool is_reload,
- IPC::Message* reply_msg) OVERRIDE;
virtual bool AddMessageToConsole(int32 level,
const base::string16& message,
int32 line_no,
@@ -392,7 +397,6 @@ class CONTENT_EXPORT WebContentsImpl
const net::LoadStateWithParam& load_state,
uint64 upload_position,
uint64 upload_size) OVERRIDE;
- virtual void WorkerCrashed() OVERRIDE;
virtual void Activate() OVERRIDE;
virtual void Deactivate() OVERRIDE;
virtual void LostCapture() OVERRIDE;
@@ -429,26 +433,64 @@ class CONTENT_EXPORT WebContentsImpl
virtual void ShowCreatedWidget(int route_id,
const gfx::Rect& initial_pos) OVERRIDE;
virtual void ShowCreatedFullscreenWidget(int route_id) OVERRIDE;
- virtual void ShowContextMenu(const ContextMenuParams& params) OVERRIDE;
virtual void RequestMediaAccessPermission(
const MediaStreamRequest& request,
const MediaResponseCallback& callback) OVERRIDE;
virtual SessionStorageNamespace* GetSessionStorageNamespace(
SiteInstance* instance) OVERRIDE;
+ virtual SessionStorageNamespaceMap GetSessionStorageNamespaceMap() OVERRIDE;
virtual FrameTree* GetFrameTree() OVERRIDE;
+ virtual void AccessibilityEventReceived(
+ const std::vector<AXEventNotificationDetails>& details) OVERRIDE;
// NavigatorDelegate ---------------------------------------------------------
virtual void DidStartProvisionalLoad(
RenderFrameHostImpl* render_frame_host,
- int64 frame_id,
- int64 parent_frame_id,
- bool is_main_frame,
+ int parent_routing_id,
const GURL& validated_url,
bool is_error_page,
bool is_iframe_srcdoc) OVERRIDE;
+ virtual void DidFailProvisionalLoadWithError(
+ RenderFrameHostImpl* render_frame_host,
+ const FrameHostMsg_DidFailProvisionalLoadWithError_Params& params)
+ OVERRIDE;
+ virtual void DidFailLoadWithError(
+ RenderFrameHostImpl* render_frame_host,
+ const GURL& url,
+ int error_code,
+ const base::string16& error_description) OVERRIDE;
+ virtual void DidRedirectProvisionalLoad(
+ RenderFrameHostImpl* render_frame_host,
+ const GURL& validated_target_url) OVERRIDE;
+ virtual void DidCommitProvisionalLoad(
+ RenderFrameHostImpl* render_frame_host,
+ const base::string16& frame_unique_name,
+ bool is_main_frame,
+ const GURL& url,
+ PageTransition transition_type) OVERRIDE;
+ virtual void DidNavigateMainFramePreCommit(
+ const FrameHostMsg_DidCommitProvisionalLoad_Params& params) OVERRIDE;
+ virtual void DidNavigateMainFramePostCommit(
+ const LoadCommittedDetails& details,
+ const FrameHostMsg_DidCommitProvisionalLoad_Params& params) OVERRIDE;
+ virtual void DidNavigateAnyFramePostCommit(
+ RenderFrameHostImpl* render_frame_host,
+ const LoadCommittedDetails& details,
+ const FrameHostMsg_DidCommitProvisionalLoad_Params& params) OVERRIDE;
+ virtual void SetMainFrameMimeType(const std::string& mime_type) OVERRIDE;
+ virtual bool CanOverscrollContent() const OVERRIDE;
virtual void NotifyChangedNavigationState(
InvalidateTypes changed_flags) OVERRIDE;
+ virtual void AboutToNavigateRenderFrame(
+ RenderFrameHostImpl* render_frame_host) OVERRIDE;
+ virtual void DidStartNavigationToPendingEntry(
+ RenderFrameHostImpl* render_frame_host,
+ const GURL& url,
+ NavigationController::ReloadType reload_type) OVERRIDE;
+ virtual void RequestOpenURL(RenderFrameHostImpl* render_frame_host,
+ const OpenURLParams& params) OVERRIDE;
+ virtual bool ShouldPreserveAbortedURLs() OVERRIDE;
// RenderWidgetHostDelegate --------------------------------------------------
@@ -459,17 +501,25 @@ class CONTENT_EXPORT WebContentsImpl
bool* is_keyboard_shortcut) OVERRIDE;
virtual void HandleKeyboardEvent(
const NativeWebKeyboardEvent& event) OVERRIDE;
- virtual bool PreHandleWheelEvent(
+ virtual bool HandleWheelEvent(
const blink::WebMouseWheelEvent& event) OVERRIDE;
+ virtual bool PreHandleGestureEvent(
+ const blink::WebGestureEvent& event) OVERRIDE;
+ virtual bool HandleGestureEvent(
+ const blink::WebGestureEvent& event) OVERRIDE;
virtual void DidSendScreenRects(RenderWidgetHostImpl* rwh) OVERRIDE;
-#if defined(OS_WIN) && defined(USE_AURA)
+ virtual void OnTouchEmulationEnabled(bool enabled) OVERRIDE;
+#if defined(OS_WIN)
virtual gfx::NativeViewAccessible GetParentNativeViewAccessible() OVERRIDE;
#endif
// RenderFrameHostManager::Delegate ------------------------------------------
virtual bool CreateRenderViewForRenderManager(
- RenderViewHost* render_view_host, int opener_route_id) OVERRIDE;
+ RenderViewHost* render_view_host,
+ int opener_route_id,
+ int proxy_routing_id,
+ bool for_main_frame) OVERRIDE;
virtual void BeforeUnloadFiredFromRenderManager(
bool proceed, const base::TimeTicks& proceed_time,
bool* proceed_to_fire_unload) OVERRIDE;
@@ -509,6 +559,11 @@ class CONTENT_EXPORT WebContentsImpl
// Activate this WebContents and show a form repost warning.
virtual void ActivateAndShowRepostFormWarningDialog() OVERRIDE;
+ // Whether the initial empty page of this view has been accessed by another
+ // page, making it unsafe to show the pending URL. Always false after the
+ // first commit.
+ virtual bool HasAccessedInitialDocument() OVERRIDE;
+
// Updates the max page ID for the current SiteInstance in this
// WebContentsImpl to be at least |page_id|.
virtual void UpdateMaxPageID(int32 page_id) OVERRIDE;
@@ -545,9 +600,9 @@ class CONTENT_EXPORT WebContentsImpl
int merge_history_length,
int32 minimum_page_id) OVERRIDE;
- // Called by InterstitialPageImpl when it creates a RenderViewHost.
- virtual void RenderViewForInterstitialPageCreated(
- RenderViewHost* render_view_host) OVERRIDE;
+ // Called by InterstitialPageImpl when it creates a RenderFrameHost.
+ virtual void RenderFrameForInterstitialPageCreated(
+ RenderFrameHost* render_frame_host) OVERRIDE;
// Sets the passed interstitial as the currently showing interstitial.
// No interstitial page should already be attached.
@@ -562,13 +617,18 @@ class CONTENT_EXPORT WebContentsImpl
// (but can be null if not applicable).
virtual void SetIsLoading(RenderViewHost* render_view_host,
bool is_loading,
+ bool to_different_document,
LoadNotificationDetails* details) OVERRIDE;
typedef base::Callback<void(WebContents*)> CreatedCallback;
+ // Requests the renderer to select the region between two points in the
+ // currently focused frame.
+ void SelectRange(const gfx::Point& start, const gfx::Point& end);
+
private:
- friend class NavigationControllerImpl;
friend class TestNavigationObserver;
+ friend class WebContentsAddedObserver;
friend class WebContentsObserver;
friend class WebContents; // To implement factory methods.
@@ -579,9 +639,11 @@ class CONTENT_EXPORT WebContentsImpl
CrossSiteCantPreemptAfterUnload);
FRIEND_TEST_ALL_PREFIXES(WebContentsImplTest, PendingContents);
FRIEND_TEST_ALL_PREFIXES(WebContentsImplTest, FrameTreeShape);
+ FRIEND_TEST_ALL_PREFIXES(WebContentsImplTest, GetLastActiveTime);
FRIEND_TEST_ALL_PREFIXES(FormStructureBrowserTest, HTMLFiles);
FRIEND_TEST_ALL_PREFIXES(NavigationControllerTest, HistoryNavigate);
FRIEND_TEST_ALL_PREFIXES(RenderFrameHostManagerTest, PageDoesBackAndReload);
+ FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest, CrossSiteIframe);
// So InterstitialPageImpl can access SetIsLoading.
friend class InterstitialPageImpl;
@@ -612,9 +674,17 @@ class CONTENT_EXPORT WebContentsImpl
// watching |web_contents|. No-op if there is no such observer.
void RemoveDestructionObserver(WebContentsImpl* web_contents);
- // Callback function when showing JS dialogs.
- void OnDialogClosed(RenderViewHost* rvh,
+ // Traverses all the RenderFrameHosts in the FrameTree and creates a set
+ // all the unique RenderWidgetHostViews.
+ std::set<RenderWidgetHostView*> GetRenderWidgetHostViewsInTree();
+
+ // Callback function when showing JavaScript dialogs. Takes in a routing ID
+ // pair to identify the RenderFrameHost that opened the dialog, because it's
+ // possible for the RenderFrameHost to be deleted by the time this is called.
+ void OnDialogClosed(int render_process_id,
+ int render_frame_id,
IPC::Message* reply_msg,
+ bool dialog_was_suppressed,
bool success,
const base::string16& user_input);
@@ -627,6 +697,7 @@ class CONTENT_EXPORT WebContentsImpl
const IPC::Message& message);
// IPC message handlers.
+ void OnThemeColorChanged(SkColor theme_color);
void OnDidLoadResourceFromMemoryCache(const GURL& url,
const std::string& security_info,
const std::string& http_request,
@@ -635,21 +706,15 @@ class CONTENT_EXPORT WebContentsImpl
void OnDidDisplayInsecureContent();
void OnDidRunInsecureContent(const std::string& security_origin,
const GURL& target_url);
- void OnDocumentLoadedInFrame(int64 frame_id);
- void OnDidFinishLoad(int64 frame_id,
- const GURL& url,
- bool is_main_frame);
- void OnDidFailLoadWithError(int64 frame_id,
- const GURL& url,
- bool is_main_frame,
- int error_code,
- const base::string16& error_description);
+ void OnDocumentLoadedInFrame();
+ void OnDidFinishLoad(const GURL& url);
+ void OnDidStartLoading(bool to_different_document);
+ void OnDidStopLoading();
+ void OnDidChangeLoadProgress(double load_progress);
void OnGoToEntryAtOffset(int offset);
void OnUpdateZoomLimits(int minimum_percent,
- int maximum_percent,
- bool remember);
+ int maximum_percent);
void OnEnumerateDirectory(int request_id, const base::FilePath& path);
- void OnJSOutOfMemory();
void OnRegisterProtocolHandler(const std::string& protocol,
const GURL& url,
@@ -667,13 +732,14 @@ class CONTENT_EXPORT WebContentsImpl
void OnOpenDateTimeDialog(
const ViewHostMsg_DateTimeDialogValue_Params& value);
- void OnJavaBridgeGetChannelHandle(IPC::Message* reply_msg);
#endif
void OnPepperPluginHung(int plugin_child_id,
const base::FilePath& path,
bool is_hung);
void OnPluginCrashed(const base::FilePath& plugin_path,
base::ProcessId plugin_pid);
+ void OnDomOperationResponse(const std::string& json_string,
+ int automation_id);
void OnAppCacheAccessed(const GURL& manifest_url, bool blocked_by_policy);
void OnOpenColorChooser(int color_chooser_id,
SkColor color,
@@ -692,20 +758,18 @@ class CONTENT_EXPORT WebContentsImpl
const GURL& image_url,
const std::vector<SkBitmap>& bitmaps,
const std::vector<gfx::Size>& original_bitmap_sizes);
- void OnUpdateFaviconURL(int32 page_id,
- const std::vector<FaviconURL>& candidates);
- void OnFirstVisuallyNonEmptyPaint(int32 page_id);
- void OnMediaNotification(int64 player_cookie,
- bool has_video,
- bool has_audio,
- bool is_playing);
+ void OnUpdateFaviconURL(const std::vector<FaviconURL>& candidates);
+ void OnFirstVisuallyNonEmptyPaint();
+ void OnMediaPlayingNotification(int64 player_cookie,
+ bool has_video,
+ bool has_audio);
+ void OnMediaPausedNotification(int64 player_cookie);
void OnShowValidationMessage(const gfx::Rect& anchor_in_root_view,
- const string16& main_text,
- const string16& sub_text);
+ const base::string16& main_text,
+ const base::string16& sub_text);
void OnHideValidationMessage();
void OnMoveValidationMessage(const gfx::Rect& anchor_in_root_view);
-
// Called by derived classes to indicate that we're no longer waiting for a
// response. This won't actually update the throbber, but it will get picked
// up at the next animation step if the throbber is going.
@@ -719,17 +783,6 @@ class CONTENT_EXPORT WebContentsImpl
// committed to the navigation controller. Note that the navigation entry is
// not provided since it may be invalid/changed after being committed. The
// current navigation entry is in the NavigationController at this point.
- void DidNavigateMainFramePostCommit(
- const LoadCommittedDetails& details,
- const ViewHostMsg_FrameNavigate_Params& params);
- void DidNavigateAnyFramePostCommit(
- RenderViewHost* render_view_host,
- const LoadCommittedDetails& details,
- const ViewHostMsg_FrameNavigate_Params& params);
-
- // Specifies whether the passed in URL should be assigned as the site of the
- // current SiteInstance, if it does not yet have a site.
- bool ShouldAssignSiteForURL(const GURL& url);
// If our controller was restored, update the max page ID associated with the
// given RenderViewHost to be larger than the number of restored entries.
@@ -748,12 +801,6 @@ class CONTENT_EXPORT WebContentsImpl
bool UpdateTitleForEntry(NavigationEntryImpl* entry,
const base::string16& title);
- // Causes the WebContentsImpl to navigate in the right renderer to |entry|,
- // which must be already part of the entries in the navigation controller.
- // This does not change the NavigationController state.
- bool NavigateToEntry(const NavigationEntryImpl& entry,
- NavigationController::ReloadType reload_type);
-
// Recursively creates swapped out RenderViews for this tab's opener chain
// (including this tab) in the given SiteInstance, allowing other tabs to send
// cross-process JavaScript calls to their opener(s). Returns the route ID of
@@ -780,12 +827,16 @@ class CONTENT_EXPORT WebContentsImpl
// called once as this call also removes it from the internal map.
WebContentsImpl* GetCreatedWindow(int route_id);
- // Returns the RenderWidgetHostView that is associated with a native window
- // and can be used in showing created widgets.
- // If this WebContents belongs to a browser plugin guest, there is no native
- // window 'view' associated with this WebContents. This method returns the
- // 'view' of the embedder instead.
- RenderWidgetHostViewPort* GetRenderWidgetHostViewPort() const;
+ // Tracking loading progress -------------------------------------------------
+
+ // Resets the tracking state of the current load.
+ void ResetLoadProgressState();
+
+ // Calculates the progress of the current load and notifies the delegate.
+ void SendLoadProgressChanged();
+
+ // Called once when the last frame on the page has stopped loading.
+ void DidStopLoading(RenderFrameHost* render_frame_host);
// Misc non-view stuff -------------------------------------------------------
@@ -804,16 +855,22 @@ class CONTENT_EXPORT WebContentsImpl
// Removes browser plugin embedder if there is one.
void RemoveBrowserPluginEmbedder();
- // Clear |render_view_host|'s PowerSaveBlockers.
- void ClearPowerSaveBlockers(RenderViewHost* render_view_host);
+ // Clear |render_frame_host|'s PowerSaveBlockers.
+ void ClearPowerSaveBlockers(RenderFrameHost* render_frame_host);
// Clear all PowerSaveBlockers, leave power_save_blocker_ empty.
void ClearAllPowerSaveBlockers();
// Helper function to invoke WebContentsDelegate::GetSizeForNewRenderView().
- gfx::Size GetSizeForNewRenderView() const;
+ gfx::Size GetSizeForNewRenderView();
+
+ void OnFrameRemoved(RenderViewHostImpl* render_view_host,
+ int frame_routing_id);
- void OnFrameRemoved(RenderViewHostImpl* render_view_host, int64 frame_id);
+ // Helper method that's called whenever |preferred_size_| or
+ // |preferred_size_for_capture_| changes, to propagate the new value to the
+ // |delegate_|.
+ void OnPreferredSizeChanged(const gfx::Size& old_size);
// Adds/removes a callback called on creation of each new WebContents.
// Deprecated, about to remove.
@@ -829,7 +886,7 @@ class CONTENT_EXPORT WebContentsImpl
NavigationControllerImpl controller_;
// The corresponding view.
- scoped_ptr<WebContentsViewPort> view_;
+ scoped_ptr<WebContentsView> view_;
// The view of the RVHD. Usually this is our WebContentsView implementation,
// but if an embedder uses a different WebContentsView, they'll need to
@@ -859,29 +916,26 @@ class CONTENT_EXPORT WebContentsImpl
// is closed.
WebContentsImpl* opener_;
-#if defined(OS_WIN) && defined(USE_AURA)
+ // True if this tab was opened by another tab. This is not unset if the opener
+ // is closed.
+ bool created_with_opener_;
+
+#if defined(OS_WIN)
gfx::NativeViewAccessible accessible_parent_;
#endif
// Helper classes ------------------------------------------------------------
- // Maps the RenderViewHost to its media_player_cookie and PowerSaveBlocker
- // pairs. Key is the RenderViewHost, value is the map which maps player_cookie
- // on to PowerSaveBlocker.
- typedef std::map<RenderViewHost*, std::map<int64, PowerSaveBlocker*> >
+ // Maps the RenderFrameHost to its media_player_cookie and PowerSaveBlocker
+ // pairs. Key is the RenderFrameHost, value is the map which maps
+ // player_cookie on to PowerSaveBlocker.
+ typedef std::map<RenderFrameHost*, std::map<int64, PowerSaveBlocker*> >
PowerSaveBlockerMap;
PowerSaveBlockerMap power_save_blockers_;
// Manages the frame tree of the page and process swaps in each node.
FrameTree frame_tree_;
-#if defined(OS_ANDROID)
- // Manages injecting Java objects into all RenderViewHosts associated with
- // this WebContentsImpl.
- scoped_ptr<JavaBridgeDispatcherHostManager>
- java_bridge_dispatcher_host_manager_;
-#endif
-
// SavePackage, lazily created.
scoped_refptr<SavePackage> save_package_;
@@ -890,6 +944,10 @@ class CONTENT_EXPORT WebContentsImpl
// Indicates whether we're currently loading a resource.
bool is_loading_;
+ // Indicates whether the current load is to a different document. Only valid
+ // if is_loading_ is true.
+ bool is_load_to_different_document_;
+
// Indicates if the tab is considered crashed.
base::TerminationStatus crashed_status_;
int crashed_error_code_;
@@ -904,12 +962,24 @@ class CONTENT_EXPORT WebContentsImpl
// WebContentsImpl.
std::map<int32, int32> max_page_ids_;
- // System time at which the current load was started.
- base::TimeTicks current_load_start_;
-
// The current load state and the URL associated with it.
net::LoadStateWithParam load_state_;
base::string16 load_state_host_;
+
+ // LoadingProgressMap maps FrameTreeNode IDs to a double representing that
+ // frame's completion (from 0 to 1).
+ typedef base::hash_map<int64, double> LoadingProgressMap;
+ LoadingProgressMap loading_progresses_;
+ double loading_total_progress_;
+
+ base::TimeTicks loading_last_progress_update_;
+
+ base::WeakPtrFactory<WebContentsImpl> loading_weak_factory_;
+
+ // Counter to track how many frames have sent start notifications but not
+ // stop notifications.
+ int loading_frames_in_progress_;
+
// Upload progress, for displaying in the status bar.
// Set to zero when there is no significant upload happening.
uint64 upload_size_;
@@ -924,12 +994,20 @@ class CONTENT_EXPORT WebContentsImpl
// used to check whether we can do something for some special contents.
std::string contents_mime_type_;
- // Character encoding.
- std::string encoding_;
+ // The last reported character encoding, not canonicalized.
+ std::string last_reported_encoding_;
+
+ // The canonicalized character encoding.
+ std::string canonical_encoding_;
// True if this is a secure page which displayed insecure content.
bool displayed_insecure_content_;
+ // Whether the initial empty page has been accessed by another page, making it
+ // unsafe to show the pending URL. Usually false unless another window tries
+ // to modify the blank page. Always false after the first commit.
+ bool has_accessed_initial_document_;
+
// Data for misc internal state ----------------------------------------------
// When > 0, the WebContents is currently being captured (e.g., for
@@ -961,8 +1039,9 @@ class CONTENT_EXPORT WebContentsImpl
// Settings that get passed to the renderer process.
RendererPreferences renderer_preferences_;
- // The time that this tab was last selected.
- base::TimeTicks last_selected_time_;
+ // The time that this WebContents was last made active. The initial value is
+ // the WebContents creation time.
+ base::TimeTicks last_active_time_;
// See description above setter.
bool closed_by_user_gesture_;
@@ -970,31 +1049,50 @@ class CONTENT_EXPORT WebContentsImpl
// Minimum/maximum zoom percent.
int minimum_zoom_percent_;
int maximum_zoom_percent_;
- // If true, the default zoom limits have been overriden for this tab, in which
- // case we don't want saved settings to apply to it and we don't want to
- // remember it.
- bool temporary_zoom_settings_;
+
+ // The raw accumulated zoom value and the actual zoom increments made for an
+ // an in-progress pinch gesture.
+ float totalPinchGestureAmount_;
+ int currentPinchZoomStepDelta_;
// The intrinsic size of the page.
gfx::Size preferred_size_;
+ // The preferred size for content screen capture. When |capturer_count_| > 0,
+ // this overrides |preferred_size_|.
+ gfx::Size preferred_size_for_capture_;
+
#if defined(OS_ANDROID)
// Date time chooser opened by this tab.
// Only used in Android since all other platforms use a multi field UI.
scoped_ptr<DateTimeChooserAndroid> date_time_chooser_;
#endif
- // Color chooser that was opened by this tab.
- scoped_ptr<ColorChooser> color_chooser_;
+ // Holds information about a current color chooser dialog, if one is visible.
+ struct ColorChooserInfo {
+ ColorChooserInfo(int render_process_id,
+ int render_frame_id,
+ ColorChooser* chooser,
+ int identifier);
+ ~ColorChooserInfo();
+
+ int render_process_id;
+ int render_frame_id;
+
+ // Color chooser that was opened by this tab.
+ scoped_ptr<ColorChooser> chooser;
+
+ // A unique identifier for the current color chooser. Identifiers are
+ // unique across a renderer process. This avoids race conditions in
+ // synchronizing the browser and renderer processes. For example, if a
+ // renderer closes one chooser and opens another, and simultaneously the
+ // user picks a color in the first chooser, the IDs can be used to drop the
+ // "chose a color" message rather than erroneously tell the renderer that
+ // the user picked a color in the second chooser.
+ int identifier;
+ };
- // A unique identifier for the current color chooser. Identifiers are unique
- // across a renderer process. This avoids race conditions in synchronizing
- // the browser and renderer processes. For example, if a renderer closes one
- // chooser and opens another, and simultaneously the user picks a color in the
- // first chooser, the IDs can be used to drop the "chose a color" message
- // rather than erroneously tell the renderer that the user picked a color in
- // the second chooser.
- int color_chooser_identifier_;
+ scoped_ptr<ColorChooserInfo> color_chooser_info_;
// Manages the embedder state for browser plugins, if this WebContents is an
// embedder; NULL otherwise.
@@ -1007,10 +1105,11 @@ class CONTENT_EXPORT WebContentsImpl
// member variables that are gone.
NotificationRegistrar registrar_;
- // Used during IPC message dispatching from the RenderView so that the
- // handlers can get a pointer to the RVH through which the message was
+ // Used during IPC message dispatching from the RenderView/RenderFrame so that
+ // the handlers can get a pointer to the RVH through which the message was
// received.
RenderViewHost* render_view_message_source_;
+ RenderFrameHost* render_frame_message_source_;
// All live RenderWidgetHostImpls that are created by this object and may
// outlive it.
@@ -1023,6 +1122,23 @@ class CONTENT_EXPORT WebContentsImpl
typedef std::map<int, ImageDownloadCallback> ImageDownloadMap;
ImageDownloadMap image_download_map_;
+ // Whether this WebContents is responsible for displaying a subframe in a
+ // different process from its parent page.
+ bool is_subframe_;
+
+ // Whether touch emulation is enabled in RenderWidgetHost.
+ bool touch_emulation_enabled_;
+
+ // Whether the last JavaScript dialog shown was suppressed. Used for testing.
+ bool last_dialog_suppressed_;
+
+ scoped_ptr<GeolocationDispatcherHost> geolocation_dispatcher_host_;
+
+ scoped_ptr<MidiDispatcherHost> midi_dispatcher_host_;
+
+ scoped_ptr<ScreenOrientationDispatcherHost>
+ screen_orientation_dispatcher_host_;
+
DISALLOW_COPY_AND_ASSIGN(WebContentsImpl);
};
diff --git a/chromium/content/browser/web_contents/web_contents_impl_browsertest.cc b/chromium/content/browser/web_contents/web_contents_impl_browsertest.cc
index b06a62884df..1f0586cd33d 100644
--- a/chromium/content/browser/web_contents/web_contents_impl_browsertest.cc
+++ b/chromium/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -2,9 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "content/browser/frame_host/navigation_entry_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/browser/web_contents/web_contents_view.h"
#include "content/public/browser/load_notification_details.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/notification_details.h"
@@ -13,13 +15,13 @@
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents_observer.h"
-#include "content/public/browser/web_contents_view.h"
#include "content/public/common/content_paths.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/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
+#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
namespace content {
@@ -30,17 +32,17 @@ void ResizeWebContentsView(Shell* shell, const gfx::Size& size,
// works on Win and ChromeOS but not Linux - we need to resize the shell
// window on Linux because if we don't, the next layout of the unchanged shell
// window will resize WebContentsView back to the previous size.
- // The cleaner and shorter SizeContents is preferred as more platforms convert
- // to Aura.
-#if defined(TOOLKIT_GTK) || defined(OS_MACOSX)
+ // SizeContents is a hack and should not be relied on.
+#if defined(OS_MACOSX)
shell->SizeTo(size);
// If |set_start_page| is true, start with blank page to make sure resize
// takes effect.
if (set_start_page)
NavigateToURL(shell, GURL("about://blank"));
#else
- shell->web_contents()->GetView()->SizeContents(size);
-#endif // defined(TOOLKIT_GTK) || defined(OS_MACOSX)
+ static_cast<WebContentsImpl*>(shell->web_contents())->GetView()->
+ SizeContents(size);
+#endif // defined(OS_MACOSX)
}
class WebContentsImplBrowserTest : public ContentBrowserTest {
@@ -95,6 +97,7 @@ class NavigateOnCommitObserver : public WebContentsObserver {
const LoadCommittedDetails& load_details) OVERRIDE {
if (!done_) {
done_ = true;
+ shell_->Stop();
shell_->LoadURL(url_);
}
}
@@ -112,8 +115,8 @@ class RenderViewSizeDelegate : public WebContentsDelegate {
// WebContentsDelegate:
virtual gfx::Size GetSizeForNewRenderView(
- const WebContents* web_contents) const OVERRIDE {
- gfx::Size size(web_contents->GetView()->GetContainerSize());
+ WebContents* web_contents) const OVERRIDE {
+ gfx::Size size(web_contents->GetContainerBounds().size());
size.Enlarge(size_insets_.width(), size_insets_.height());
return size;
}
@@ -149,6 +152,31 @@ class RenderViewSizeObserver : public WebContentsObserver {
gfx::Size rwhv_create_size_;
};
+class LoadingStateChangedDelegate : public WebContentsDelegate {
+ public:
+ LoadingStateChangedDelegate()
+ : loadingStateChangedCount_(0)
+ , loadingStateToDifferentDocumentCount_(0) {
+ }
+
+ // WebContentsDelegate:
+ virtual void LoadingStateChanged(WebContents* contents,
+ bool to_different_document) OVERRIDE {
+ loadingStateChangedCount_++;
+ if (to_different_document)
+ loadingStateToDifferentDocumentCount_++;
+ }
+
+ int loadingStateChangedCount() const { return loadingStateChangedCount_; }
+ int loadingStateToDifferentDocumentCount() const {
+ return loadingStateToDifferentDocumentCount_;
+ }
+
+ private:
+ int loadingStateChangedCount_;
+ int loadingStateToDifferentDocumentCount_;
+};
+
// See: http://crbug.com/298193
#if defined(OS_WIN)
#define MAYBE_DidStopLoadingDetails DISABLED_DidStopLoadingDetails
@@ -185,6 +213,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
MAYBE_DidStopLoadingDetailsWithPending) {
ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+ GURL url("data:text/html,<div>test</div>");
// Listen for the first load to stop.
LoadStopNotificationObserver load_observer(
@@ -194,10 +223,10 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
// is started.
NavigateOnCommitObserver commit_observer(
shell(), embedded_test_server()->GetURL("/title2.html"));
- NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html"));
+ NavigateToURL(shell(), url);
load_observer.Wait();
- EXPECT_EQ("/title1.html", load_observer.url_.path());
+ EXPECT_EQ(url, load_observer.url_);
EXPECT_EQ(0, load_observer.session_index_);
EXPECT_EQ(&shell()->web_contents()->GetController(),
load_observer.controller_);
@@ -230,11 +259,11 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
shell()->web_contents()->GetVisibleURL());
}
-// TODO(sail): enable this for MAC when auto resizing of WebContentsViewCocoa is
-// fixed.
// TODO(shrikant): enable this for Windows when issue with
// force-compositing-mode is resolved (http://crbug.com/281726).
-#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_ANDROID)
+// Also crashes under ThreadSanitizer, http://crbug.com/356758.
+#if defined(OS_WIN) || defined(OS_ANDROID) \
+ || defined(THREAD_SANITIZER)
#define MAYBE_GetSizeForNewRenderView DISABLED_GetSizeForNewRenderView
#else
#define MAYBE_GetSizeForNewRenderView GetSizeForNewRenderView
@@ -256,16 +285,16 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
ASSERT_TRUE(shell()->web_contents()->GetDelegate() == delegate.get());
// When no size is set, RenderWidgetHostView adopts the size of
- // WebContenntsView.
+ // WebContentsView.
NavigateToURL(shell(), embedded_test_server()->GetURL("/title2.html"));
- EXPECT_EQ(shell()->web_contents()->GetView()->GetContainerSize(),
+ EXPECT_EQ(shell()->web_contents()->GetContainerBounds().size(),
shell()->web_contents()->GetRenderWidgetHostView()->GetViewBounds().
size());
// When a size is set, RenderWidgetHostView and WebContentsView honor this
// size.
gfx::Size size(300, 300);
- gfx::Size size_insets(-10, -15);
+ gfx::Size size_insets(10, 15);
ResizeWebContentsView(shell(), size, true);
delegate->set_size_insets(size_insets);
NavigateToURL(shell(), https_server.GetURL("/"));
@@ -273,14 +302,23 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
EXPECT_EQ(size,
shell()->web_contents()->GetRenderWidgetHostView()->GetViewBounds().
size());
- EXPECT_EQ(size, shell()->web_contents()->GetView()->GetContainerSize());
+ // The web_contents size is set by the embedder, and should not depend on the
+ // rwhv size. The behavior is correct on OSX, but incorrect on other
+ // platforms.
+ gfx::Size exp_wcv_size(300, 300);
+#if !defined(OS_MACOSX)
+ exp_wcv_size.Enlarge(size_insets.width(), size_insets.height());
+#endif
+
+ EXPECT_EQ(exp_wcv_size,
+ shell()->web_contents()->GetContainerBounds().size());
// If WebContentsView is resized after RenderWidgetHostView is created but
// before pending navigation entry is committed, both RenderWidgetHostView and
// WebContentsView use the new size of WebContentsView.
gfx::Size init_size(200, 200);
gfx::Size new_size(100, 100);
- size_insets = gfx::Size(-20, -30);
+ size_insets = gfx::Size(20, 30);
ResizeWebContentsView(shell(), init_size, true);
delegate->set_size_insets(size_insets);
RenderViewSizeObserver observer(shell(), new_size);
@@ -288,21 +326,25 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
// RenderWidgetHostView is created at specified size.
init_size.Enlarge(size_insets.width(), size_insets.height());
EXPECT_EQ(init_size, observer.rwhv_create_size());
- // RenderViewSizeObserver resizes WebContentsView in
- // DidStartNavigationToPendingEntry, so both WebContentsView and
- // RenderWidgetHostView adopt this new size.
+
+// Once again, the behavior is correct on OSX. The embedder explicitly sets
+// the size to (100,100) during navigation. Both the wcv and the rwhv should
+// take on that size.
+#if !defined(OS_MACOSX)
new_size.Enlarge(size_insets.width(), size_insets.height());
- EXPECT_EQ(new_size,
- shell()->web_contents()->GetRenderWidgetHostView()->GetViewBounds().
- size());
- EXPECT_EQ(new_size, shell()->web_contents()->GetView()->GetContainerSize());
+#endif
+ gfx::Size actual_size = shell()->web_contents()->GetRenderWidgetHostView()->
+ GetViewBounds().size();
+
+ EXPECT_EQ(new_size, actual_size);
+ EXPECT_EQ(new_size, shell()->web_contents()->GetContainerBounds().size());
}
IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, OpenURLSubframe) {
- // Navigate with source_frame_id 3, FrameTreeNode ID 4.
+ // Navigate with FrameTreeNode ID 4.
const GURL url("http://foo");
- OpenURLParams params(url, Referrer(), 3, 4, CURRENT_TAB, PAGE_TRANSITION_LINK,
+ OpenURLParams params(url, Referrer(), 4, CURRENT_TAB, PAGE_TRANSITION_LINK,
true);
shell()->web_contents()->OpenURL(params);
@@ -313,5 +355,167 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, OpenURLSubframe) {
controller->GetPendingEntry())->frame_tree_node_id());
}
+// Observer class to track the creation of RenderFrameHost objects. It is used
+// in subsequent tests.
+class RenderFrameCreatedObserver : public WebContentsObserver {
+ public:
+ RenderFrameCreatedObserver(Shell* shell)
+ : WebContentsObserver(shell->web_contents()),
+ last_rfh_(NULL) {
+ }
+
+ virtual void RenderFrameCreated(RenderFrameHost* render_frame_host) OVERRIDE {
+ last_rfh_ = render_frame_host;
+ }
+
+ RenderFrameHost* last_rfh() const { return last_rfh_; }
+
+ private:
+ RenderFrameHost* last_rfh_;
+
+ DISALLOW_COPY_AND_ASSIGN(RenderFrameCreatedObserver);
+};
+
+// Test that creation of new RenderFrameHost objects sends the correct object
+// to the WebContentObservers. See http://crbug.com/347339.
+IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
+ RenderFrameCreatedCorrectProcessForObservers) {
+ std::string foo_com("foo.com");
+ GURL::Replacements replace_host;
+ net::HostPortPair foo_host_port;
+ GURL cross_site_url;
+
+ // Setup the server to allow serving separate sites, so we can perform
+ // cross-process navigation.
+ host_resolver()->AddRule("*", "127.0.0.1");
+ ASSERT_TRUE(test_server()->Start());
+
+ foo_host_port = test_server()->host_port_pair();
+ foo_host_port.set_host(foo_com);
+
+ GURL initial_url(test_server()->GetURL("/title1.html"));
+
+ cross_site_url = test_server()->GetURL("/title2.html");
+ replace_host.SetHostStr(foo_com);
+ cross_site_url = cross_site_url.ReplaceComponents(replace_host);
+
+ // Navigate to the initial URL and capture the RenderFrameHost for later
+ // comparison.
+ NavigateToURL(shell(), initial_url);
+ RenderFrameHost* orig_rfh = shell()->web_contents()->GetMainFrame();
+
+ // Install the observer and navigate cross-site.
+ RenderFrameCreatedObserver observer(shell());
+ NavigateToURL(shell(), cross_site_url);
+
+ // The observer should've seen a RenderFrameCreated call for the new frame
+ // and not the old one.
+ EXPECT_NE(observer.last_rfh(), orig_rfh);
+ EXPECT_EQ(observer.last_rfh(), shell()->web_contents()->GetMainFrame());
+}
+
+IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
+ LoadingStateChangedForSameDocumentNavigation) {
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+ scoped_ptr<LoadingStateChangedDelegate> delegate(
+ new LoadingStateChangedDelegate());
+ shell()->web_contents()->SetDelegate(delegate.get());
+
+ LoadStopNotificationObserver load_observer(
+ &shell()->web_contents()->GetController());
+ TitleWatcher title_watcher(shell()->web_contents(),
+ base::ASCIIToUTF16("pushState"));
+ NavigateToURL(shell(), embedded_test_server()->GetURL("/push_state.html"));
+ load_observer.Wait();
+ base::string16 title = title_watcher.WaitAndGetTitle();
+ ASSERT_EQ(title, base::ASCIIToUTF16("pushState"));
+
+ // LoadingStateChanged should be called 4 times: start and stop for the
+ // initial load of push_state.html, and start and stop for the "navigation"
+ // triggered by history.pushState(). However, the start notification for the
+ // history.pushState() navigation should set to_different_document to false.
+ EXPECT_EQ("pushState", shell()->web_contents()->GetURL().ref());
+ EXPECT_EQ(4, delegate->loadingStateChangedCount());
+ EXPECT_EQ(3, delegate->loadingStateToDifferentDocumentCount());
+}
+
+IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
+ RenderViewCreatedForChildWindow) {
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+
+ NavigateToURL(shell(),
+ embedded_test_server()->GetURL("/title1.html"));
+
+ WebContentsAddedObserver new_web_contents_observer;
+ ASSERT_TRUE(ExecuteScript(shell()->web_contents(),
+ "var a = document.createElement('a');"
+ "a.href='./title2.html';"
+ "a.target = '_blank';"
+ "document.body.appendChild(a);"
+ "a.click();"));
+ WebContents* new_web_contents = new_web_contents_observer.GetWebContents();
+ WaitForLoadStop(new_web_contents);
+ EXPECT_TRUE(new_web_contents_observer.RenderViewCreatedCalled());
+}
+
+struct LoadProgressDelegateAndObserver : public WebContentsDelegate,
+ public WebContentsObserver {
+ LoadProgressDelegateAndObserver(Shell* shell)
+ : WebContentsObserver(shell->web_contents()),
+ did_start_loading(false),
+ did_stop_loading(false) {
+ web_contents()->SetDelegate(this);
+ }
+
+ // WebContentsDelegate:
+ virtual void LoadProgressChanged(WebContents* source,
+ double progress) OVERRIDE {
+ EXPECT_TRUE(did_start_loading);
+ EXPECT_FALSE(did_stop_loading);
+ progresses.push_back(progress);
+ }
+
+ // WebContentsObserver:
+ virtual void DidStartLoading(RenderViewHost* render_view_host) OVERRIDE {
+ EXPECT_FALSE(did_start_loading);
+ EXPECT_EQ(0U, progresses.size());
+ EXPECT_FALSE(did_stop_loading);
+ did_start_loading = true;
+ }
+
+ virtual void DidStopLoading(RenderViewHost* render_view_host) OVERRIDE {
+ EXPECT_TRUE(did_start_loading);
+ EXPECT_GE(progresses.size(), 1U);
+ EXPECT_FALSE(did_stop_loading);
+ did_stop_loading = true;
+ }
+
+ bool did_start_loading;
+ std::vector<double> progresses;
+ bool did_stop_loading;
+};
+
+IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, LoadProgress) {
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+ scoped_ptr<LoadProgressDelegateAndObserver> delegate(
+ new LoadProgressDelegateAndObserver(shell()));
+
+ NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html"));
+
+ const std::vector<double>& progresses = delegate->progresses;
+ // All updates should be in order ...
+ if (std::adjacent_find(progresses.begin(),
+ progresses.end(),
+ std::greater<double>()) != progresses.end()) {
+ ADD_FAILURE() << "Progress values should be in order: "
+ << ::testing::PrintToString(progresses);
+ }
+
+ // ... and the last one should be 1.0, meaning complete.
+ ASSERT_GE(progresses.size(), 1U)
+ << "There should be at least one progress update";
+ EXPECT_EQ(1.0, *progresses.rbegin());
+}
} // namespace content
+
diff --git a/chromium/content/browser/web_contents/web_contents_impl_unittest.cc b/chromium/content/browser/web_contents/web_contents_impl_unittest.cc
index 1be4a3c0026..a345598fcf0 100644
--- a/chromium/content/browser/web_contents/web_contents_impl_unittest.cc
+++ b/chromium/content/browser/web_contents/web_contents_impl_unittest.cc
@@ -4,11 +4,14 @@
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
+#include "content/browser/frame_host/cross_site_transferring_request.h"
#include "content/browser/frame_host/interstitial_page_impl.h"
#include "content/browser/frame_host/navigation_entry_impl.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/site_instance_impl.h"
#include "content/browser/webui/web_ui_controller_factory_registry.h"
+#include "content/common/frame_messages.h"
+#include "content/common/input/synthetic_web_input_event_builders.h"
#include "content/common/view_messages.h"
#include "content/public/browser/global_request_id.h"
#include "content/public/browser/interstitial_page_delegate.h"
@@ -16,6 +19,7 @@
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/render_widget_host_view.h"
+#include "content/public/browser/web_contents_delegate.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_ui_controller.h"
#include "content/public/common/bindings_policy.h"
@@ -23,7 +27,6 @@
#include "content/public/common/url_constants.h"
#include "content/public/common/url_utils.h"
#include "content/public/test/mock_render_process_host.h"
-#include "content/public/test/test_browser_thread.h"
#include "content/public/test/test_utils.h"
#include "content/test/test_content_browser_client.h"
#include "content/test/test_content_client.h"
@@ -151,7 +154,7 @@ class TestInterstitialPage : public InterstitialPageImpl {
}
void TestDidNavigate(int page_id, const GURL& url) {
- ViewHostMsg_FrameNavigate_Params params;
+ FrameHostMsg_DidCommitProvisionalLoad_Params params;
InitNavigateParams(&params, page_id, url, PAGE_TRANSITION_TYPED);
DidNavigate(GetRenderViewHostForTesting(), params);
}
@@ -181,12 +184,6 @@ class TestInterstitialPage : public InterstitialPageImpl {
}
protected:
- virtual RenderViewHost* CreateRenderViewHost() OVERRIDE {
- return new TestRenderViewHost(
- SiteInstance::Create(web_contents()->GetBrowserContext()),
- this, this, this, MSG_ROUTING_NONE, MSG_ROUTING_NONE, false);
- }
-
virtual WebContentsView* CreateWebContentsView() OVERRIDE {
return NULL;
}
@@ -298,21 +295,66 @@ class TestWebContentsObserver : public WebContentsObserver {
DISALLOW_COPY_AND_ASSIGN(TestWebContentsObserver);
};
+// Pretends to be a normal browser that receives toggles and transitions to/from
+// a fullscreened state.
+class FakeFullscreenDelegate : public WebContentsDelegate {
+ public:
+ FakeFullscreenDelegate() : fullscreened_contents_(NULL) {}
+ virtual ~FakeFullscreenDelegate() {}
+
+ virtual void ToggleFullscreenModeForTab(WebContents* web_contents,
+ bool enter_fullscreen) OVERRIDE {
+ fullscreened_contents_ = enter_fullscreen ? web_contents : NULL;
+ }
+
+ virtual bool IsFullscreenForTabOrPending(const WebContents* web_contents)
+ const OVERRIDE {
+ return fullscreened_contents_ && web_contents == fullscreened_contents_;
+ }
+
+ private:
+ WebContents* fullscreened_contents_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeFullscreenDelegate);
+};
+
+class FakeValidationMessageDelegate : public WebContentsDelegate {
+ public:
+ FakeValidationMessageDelegate()
+ : hide_validation_message_was_called_(false) {}
+ virtual ~FakeValidationMessageDelegate() {}
+
+ virtual void HideValidationMessage(WebContents* web_contents) OVERRIDE {
+ hide_validation_message_was_called_ = true;
+ }
+
+ bool hide_validation_message_was_called() const {
+ return hide_validation_message_was_called_;
+ }
+
+ private:
+ bool hide_validation_message_was_called_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeValidationMessageDelegate);
+};
+
} // namespace
// Test to make sure that title updates get stripped of whitespace.
TEST_F(WebContentsImplTest, UpdateTitle) {
NavigationControllerImpl& cont =
static_cast<NavigationControllerImpl&>(controller());
- ViewHostMsg_FrameNavigate_Params params;
- InitNavigateParams(&params, 0, GURL(kAboutBlankURL), PAGE_TRANSITION_TYPED);
+ FrameHostMsg_DidCommitProvisionalLoad_Params params;
+ InitNavigateParams(
+ &params, 0, GURL(url::kAboutBlankURL), PAGE_TRANSITION_TYPED);
LoadCommittedDetails details;
- cont.RendererDidNavigate(params, &details);
+ cont.RendererDidNavigate(main_test_rfh(), params, &details);
- contents()->UpdateTitle(rvh(), 0, ASCIIToUTF16(" Lots O' Whitespace\n"),
+ contents()->UpdateTitle(main_test_rfh(), 0,
+ base::ASCIIToUTF16(" Lots O' Whitespace\n"),
base::i18n::LEFT_TO_RIGHT);
- EXPECT_EQ(ASCIIToUTF16("Lots O' Whitespace"), contents()->GetTitle());
+ EXPECT_EQ(base::ASCIIToUTF16("Lots O' Whitespace"), contents()->GetTitle());
}
TEST_F(WebContentsImplTest, DontUseTitleFromPendingEntry) {
@@ -324,7 +366,7 @@ TEST_F(WebContentsImplTest, DontUseTitleFromPendingEntry) {
TEST_F(WebContentsImplTest, UseTitleFromPendingEntryIfSet) {
const GURL kGURL("chrome://blah");
- const base::string16 title = ASCIIToUTF16("My Title");
+ const base::string16 title = base::ASCIIToUTF16("My Title");
controller().LoadURL(
kGURL, Referrer(), PAGE_TRANSITION_TYPED, std::string());
@@ -351,12 +393,12 @@ TEST_F(WebContentsImplTest, NTPViewSource) {
EXPECT_TRUE(process()->sink().GetFirstMessageMatching(
ViewMsg_EnableViewSourceMode::ID));
- ViewHostMsg_FrameNavigate_Params params;
+ FrameHostMsg_DidCommitProvisionalLoad_Params params;
InitNavigateParams(&params, 0, kGURL, PAGE_TRANSITION_TYPED);
LoadCommittedDetails details;
- cont.RendererDidNavigate(params, &details);
+ cont.RendererDidNavigate(main_test_rfh(), params, &details);
// Also check title and url.
- EXPECT_EQ(ASCIIToUTF16(kUrl), contents()->GetTitle());
+ EXPECT_EQ(base::ASCIIToUTF16(kUrl), contents()->GetTitle());
}
// Test to ensure UpdateMaxPageID is working properly.
@@ -427,8 +469,9 @@ TEST_F(WebContentsImplTest, NavigateToExcessivelyLongURL) {
// Test that navigating across a site boundary creates a new RenderViewHost
// with a new SiteInstance. Going back should do the same.
TEST_F(WebContentsImplTest, CrossSiteBoundaries) {
- contents()->transition_cross_site = true;
TestRenderViewHost* orig_rvh = test_rvh();
+ RenderFrameHostImpl* orig_rfh =
+ contents()->GetFrameTree()->root()->current_frame_host();
int orig_rvh_delete_count = 0;
orig_rvh->set_delete_counter(&orig_rvh_delete_count);
SiteInstance* instance1 = contents()->GetSiteInstance();
@@ -461,10 +504,12 @@ TEST_F(WebContentsImplTest, CrossSiteBoundaries) {
static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
int pending_rvh_delete_count = 0;
pending_rvh->set_delete_counter(&pending_rvh_delete_count);
+ RenderFrameHostImpl* pending_rfh = contents()->GetFrameTree()->root()->
+ render_manager()->pending_frame_host();
- // Navigations should be suspended in pending_rvh until ShouldCloseACK.
+ // Navigations should be suspended in pending_rvh until BeforeUnloadACK.
EXPECT_TRUE(pending_rvh->are_navigations_suspended());
- orig_rvh->SendShouldCloseACK(true);
+ orig_rvh->SendBeforeUnloadACK(true);
EXPECT_FALSE(pending_rvh->are_navigations_suspended());
// DidNavigate from the pending page
@@ -484,9 +529,9 @@ TEST_F(WebContentsImplTest, CrossSiteBoundaries) {
EXPECT_EQ(url2, contents()->GetVisibleURL());
EXPECT_NE(instance1, instance2);
EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
- // We keep the original RVH around, swapped out.
+ // We keep the original RFH around, swapped out.
EXPECT_TRUE(contents()->GetRenderManagerForTesting()->IsOnSwappedOutList(
- orig_rvh));
+ orig_rfh));
EXPECT_EQ(orig_rvh_delete_count, 0);
// Going back should switch SiteInstances again. The first SiteInstance is
@@ -498,9 +543,9 @@ TEST_F(WebContentsImplTest, CrossSiteBoundaries) {
EXPECT_EQ(orig_rvh, goback_rvh);
EXPECT_TRUE(contents()->cross_navigation_pending());
- // Navigations should be suspended in goback_rvh until ShouldCloseACK.
+ // Navigations should be suspended in goback_rvh until BeforeUnloadACK.
EXPECT_TRUE(goback_rvh->are_navigations_suspended());
- pending_rvh->SendShouldCloseACK(true);
+ pending_rvh->SendBeforeUnloadACK(true);
EXPECT_FALSE(goback_rvh->are_navigations_suspended());
// DidNavigate from the back action
@@ -509,10 +554,11 @@ TEST_F(WebContentsImplTest, CrossSiteBoundaries) {
EXPECT_FALSE(contents()->cross_navigation_pending());
EXPECT_EQ(goback_rvh, contents()->GetRenderViewHost());
EXPECT_EQ(instance1, contents()->GetSiteInstance());
- // The pending RVH should now be swapped out, not deleted.
+ // The pending RFH should now be swapped out, not deleted.
EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
- IsOnSwappedOutList(pending_rvh));
+ IsOnSwappedOutList(pending_rfh));
EXPECT_EQ(pending_rvh_delete_count, 0);
+ pending_rvh->OnSwappedOut(false);
// Close contents and ensure RVHs are deleted.
DeleteContents();
@@ -523,7 +569,6 @@ TEST_F(WebContentsImplTest, CrossSiteBoundaries) {
// Test that navigating across a site boundary after a crash creates a new
// RVH without requiring a cross-site transition (i.e., PENDING state).
TEST_F(WebContentsImplTest, CrossSiteBoundariesAfterCrash) {
- contents()->transition_cross_site = true;
TestRenderViewHost* orig_rvh = test_rvh();
int orig_rvh_delete_count = 0;
orig_rvh->set_delete_counter(&orig_rvh_delete_count);
@@ -569,7 +614,6 @@ TEST_F(WebContentsImplTest, CrossSiteBoundariesAfterCrash) {
// both contentses to a new site will place both contentses in a single
// SiteInstance.
TEST_F(WebContentsImplTest, NavigateTwoTabsCrossSite) {
- contents()->transition_cross_site = true;
TestRenderViewHost* orig_rvh = test_rvh();
SiteInstance* instance1 = contents()->GetSiteInstance();
@@ -582,7 +626,6 @@ TEST_F(WebContentsImplTest, NavigateTwoTabsCrossSite) {
// Open a new contents with the same SiteInstance, navigated to the same site.
scoped_ptr<TestWebContents> contents2(
TestWebContents::Create(browser_context(), instance1));
- contents2->transition_cross_site = true;
contents2->GetController().LoadURL(url, Referrer(),
PAGE_TRANSITION_TYPED,
std::string());
@@ -595,7 +638,7 @@ TEST_F(WebContentsImplTest, NavigateTwoTabsCrossSite) {
const GURL url2a("http://www.yahoo.com");
controller().LoadURL(
url2a, Referrer(), PAGE_TRANSITION_TYPED, std::string());
- orig_rvh->SendShouldCloseACK(true);
+ orig_rvh->SendBeforeUnloadACK(true);
TestRenderViewHost* pending_rvh_a =
static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
contents()->TestDidNavigate(
@@ -610,7 +653,7 @@ TEST_F(WebContentsImplTest, NavigateTwoTabsCrossSite) {
std::string());
TestRenderViewHost* rvh2 =
static_cast<TestRenderViewHost*>(contents2->GetRenderViewHost());
- rvh2->SendShouldCloseACK(true);
+ rvh2->SendBeforeUnloadACK(true);
TestRenderViewHost* pending_rvh_b =
static_cast<TestRenderViewHost*>(contents2->GetPendingRenderViewHost());
EXPECT_TRUE(pending_rvh_b != NULL);
@@ -632,8 +675,9 @@ TEST_F(WebContentsImplTest, NavigateDoesNotUseUpSiteInstance) {
WebContentsImplTestBrowserClient browser_client;
SetBrowserClientForTesting(&browser_client);
- contents()->transition_cross_site = true;
TestRenderViewHost* orig_rvh = test_rvh();
+ RenderFrameHostImpl* orig_rfh =
+ contents()->GetFrameTree()->root()->current_frame_host();
int orig_rvh_delete_count = 0;
orig_rvh->set_delete_counter(&orig_rvh_delete_count);
SiteInstanceImpl* orig_instance =
@@ -688,9 +732,9 @@ TEST_F(WebContentsImplTest, NavigateDoesNotUseUpSiteInstance) {
int pending_rvh_delete_count = 0;
pending_rvh->set_delete_counter(&pending_rvh_delete_count);
- // Navigations should be suspended in pending_rvh until ShouldCloseACK.
+ // Navigations should be suspended in pending_rvh until BeforeUnloadACK.
EXPECT_TRUE(pending_rvh->are_navigations_suspended());
- orig_rvh->SendShouldCloseACK(true);
+ orig_rvh->SendBeforeUnloadACK(true);
EXPECT_FALSE(pending_rvh->are_navigations_suspended());
// DidNavigate from the pending page.
@@ -704,10 +748,11 @@ TEST_F(WebContentsImplTest, NavigateDoesNotUseUpSiteInstance) {
EXPECT_EQ(url2, contents()->GetVisibleURL());
EXPECT_NE(new_instance, orig_instance);
EXPECT_FALSE(contents()->GetPendingRenderViewHost());
- // We keep the original RVH around, swapped out.
+ // We keep the original RFH around, swapped out.
EXPECT_TRUE(contents()->GetRenderManagerForTesting()->IsOnSwappedOutList(
- orig_rvh));
+ orig_rfh));
EXPECT_EQ(orig_rvh_delete_count, 0);
+ orig_rvh->OnSwappedOut(false);
// Close contents and ensure RVHs are deleted.
DeleteContents();
@@ -718,7 +763,6 @@ TEST_F(WebContentsImplTest, NavigateDoesNotUseUpSiteInstance) {
// Test that we can find an opener RVH even if it's pending.
// http://crbug.com/176252.
TEST_F(WebContentsImplTest, FindOpenerRVHWhenPending) {
- contents()->transition_cross_site = true;
TestRenderViewHost* orig_rvh = test_rvh();
// Navigate to a URL.
@@ -731,7 +775,7 @@ TEST_F(WebContentsImplTest, FindOpenerRVHWhenPending) {
const GURL url2("http://www.yahoo.com");
controller().LoadURL(
url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
- orig_rvh->SendShouldCloseACK(true);
+ orig_rvh->SendBeforeUnloadACK(true);
TestRenderViewHost* pending_rvh =
static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
@@ -748,7 +792,6 @@ TEST_F(WebContentsImplTest, FindOpenerRVHWhenPending) {
// Tests that WebContentsImpl uses the current URL, not the SiteInstance's site,
// to determine whether a navigation is cross-site.
TEST_F(WebContentsImplTest, CrossSiteComparesAgainstCurrentPage) {
- contents()->transition_cross_site = true;
RenderViewHost* orig_rvh = rvh();
SiteInstance* instance1 = contents()->GetSiteInstance();
@@ -762,7 +805,6 @@ TEST_F(WebContentsImplTest, CrossSiteComparesAgainstCurrentPage) {
// Open a related contents to a second site.
scoped_ptr<TestWebContents> contents2(
TestWebContents::Create(browser_context(), instance1));
- contents2->transition_cross_site = true;
const GURL url2("http://www.yahoo.com");
contents2->GetController().LoadURL(url2, Referrer(),
PAGE_TRANSITION_TYPED,
@@ -800,7 +842,6 @@ TEST_F(WebContentsImplTest, CrossSiteComparesAgainstCurrentPage) {
// Test that the onbeforeunload and onunload handlers run when navigating
// across site boundaries.
TEST_F(WebContentsImplTest, CrossSiteUnloadHandlers) {
- contents()->transition_cross_site = true;
TestRenderViewHost* orig_rvh = test_rvh();
SiteInstance* instance1 = contents()->GetSiteInstance();
@@ -818,7 +859,8 @@ TEST_F(WebContentsImplTest, CrossSiteUnloadHandlers) {
url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
base::TimeTicks now = base::TimeTicks::Now();
- orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, false, now, now));
+ orig_rvh->GetMainFrame()->OnMessageReceived(
+ FrameHostMsg_BeforeUnload_ACK(0, false, now, now));
EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
EXPECT_FALSE(contents()->cross_navigation_pending());
EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
@@ -828,7 +870,8 @@ TEST_F(WebContentsImplTest, CrossSiteUnloadHandlers) {
url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
now = base::TimeTicks::Now();
- orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
+ orig_rvh->GetMainFrame()->OnMessageReceived(
+ FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
EXPECT_TRUE(contents()->cross_navigation_pending());
TestRenderViewHost* pending_rvh = static_cast<TestRenderViewHost*>(
@@ -850,7 +893,6 @@ TEST_F(WebContentsImplTest, CrossSiteUnloadHandlers) {
// navigate to a different URL and have it displayed, canceling the slow
// navigation.
TEST_F(WebContentsImplTest, CrossSiteNavigationPreempted) {
- contents()->transition_cross_site = true;
TestRenderViewHost* orig_rvh = test_rvh();
SiteInstance* instance1 = contents()->GetSiteInstance();
@@ -868,7 +910,8 @@ TEST_F(WebContentsImplTest, CrossSiteNavigationPreempted) {
url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
base::TimeTicks now = base::TimeTicks::Now();
- orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
+ orig_rvh->GetMainFrame()->OnMessageReceived(
+ FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
EXPECT_TRUE(contents()->cross_navigation_pending());
// Suppose the original renderer navigates before the new one is ready.
@@ -884,8 +927,6 @@ TEST_F(WebContentsImplTest, CrossSiteNavigationPreempted) {
}
TEST_F(WebContentsImplTest, CrossSiteNavigationBackPreempted) {
- contents()->transition_cross_site = true;
-
// Start with a web ui page, which gets a new RVH with WebUI bindings.
const GURL url1("chrome://blah");
controller().LoadURL(
@@ -913,7 +954,8 @@ TEST_F(WebContentsImplTest, CrossSiteNavigationBackPreempted) {
// Simulate beforeunload approval.
EXPECT_TRUE(ntp_rvh->is_waiting_for_beforeunload_ack());
base::TimeTicks now = base::TimeTicks::Now();
- ntp_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
+ ntp_rvh->GetMainFrame()->OnMessageReceived(
+ FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
// DidNavigate from the pending page.
contents()->TestDidNavigate(
@@ -962,7 +1004,8 @@ TEST_F(WebContentsImplTest, CrossSiteNavigationBackPreempted) {
// Simulate beforeunload approval.
EXPECT_TRUE(google_rvh->is_waiting_for_beforeunload_ack());
now = base::TimeTicks::Now();
- google_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
+ google_rvh->GetMainFrame()->OnMessageReceived(
+ FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
// DidNavigate from the first back. This aborts the second back's pending RVH.
contents()->TestDidNavigate(google_rvh, 1, url2, PAGE_TRANSITION_TYPED);
@@ -986,7 +1029,6 @@ TEST_F(WebContentsImplTest, CrossSiteNavigationBackPreempted) {
// Test that during a slow cross-site navigation, a sub-frame navigation in the
// original renderer will not cancel the slow navigation (bug 42029).
TEST_F(WebContentsImplTest, CrossSiteNavigationNotPreemptedByFrame) {
- contents()->transition_cross_site = true;
TestRenderViewHost* orig_rvh = test_rvh();
// Navigate to URL. First URL should use first RenderViewHost.
@@ -1011,7 +1053,8 @@ TEST_F(WebContentsImplTest, CrossSiteNavigationNotPreemptedByFrame) {
// Now simulate the onbeforeunload approval and verify the navigation is
// not canceled.
base::TimeTicks now = base::TimeTicks::Now();
- orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
+ orig_rvh->GetMainFrame()->OnMessageReceived(
+ FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
EXPECT_TRUE(contents()->cross_navigation_pending());
}
@@ -1021,8 +1064,6 @@ TEST_F(WebContentsImplTest, CrossSiteNavigationNotPreemptedByFrame) {
// We should only preempt the cross-site navigation if the previous renderer
// has started a new navigation. See http://crbug.com/79176.
TEST_F(WebContentsImplTest, CrossSiteNotPreemptedDuringBeforeUnload) {
- contents()->transition_cross_site = true;
-
// Navigate to NTP URL.
const GURL url("chrome://blah");
controller().LoadURL(
@@ -1058,7 +1099,6 @@ TEST_F(WebContentsImplTest, CrossSiteNotPreemptedDuringBeforeUnload) {
// is almost ready to be displayed, and the original renderer is only given a
// short chance to run an unload handler. Prevents regression of bug 23942.
TEST_F(WebContentsImplTest, CrossSiteCantPreemptAfterUnload) {
- contents()->transition_cross_site = true;
TestRenderViewHost* orig_rvh = test_rvh();
SiteInstance* instance1 = contents()->GetSiteInstance();
@@ -1075,7 +1115,8 @@ TEST_F(WebContentsImplTest, CrossSiteCantPreemptAfterUnload) {
controller().LoadURL(
url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
base::TimeTicks now = base::TimeTicks::Now();
- orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
+ orig_rvh->GetMainFrame()->OnMessageReceived(
+ FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
EXPECT_TRUE(contents()->cross_navigation_pending());
TestRenderViewHost* pending_rvh = static_cast<TestRenderViewHost*>(
contents()->GetPendingRenderViewHost());
@@ -1085,14 +1126,15 @@ TEST_F(WebContentsImplTest, CrossSiteCantPreemptAfterUnload) {
std::vector<GURL> url_chain;
url_chain.push_back(GURL());
contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
- pending_rvh, GlobalRequestID(0, 0), false, url_chain, Referrer(),
- PAGE_TRANSITION_TYPED, 1, false);
+ contents()->GetRenderManagerForTesting()->pending_frame_host(),
+ GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
+ url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
// Suppose the original renderer navigates now, while the unload request is in
// flight. We should ignore it, wait for the unload ack, and let the pending
// request continue. Otherwise, the contents may close spontaneously or stop
// responding to navigation requests. (See bug 23942.)
- ViewHostMsg_FrameNavigate_Params params1a;
+ FrameHostMsg_DidCommitProvisionalLoad_Params params1a;
InitNavigateParams(&params1a, 2, GURL("http://www.google.com/foo"),
PAGE_TRANSITION_TYPED);
orig_rvh->SendNavigate(2, GURL("http://www.google.com/foo"));
@@ -1114,7 +1156,6 @@ TEST_F(WebContentsImplTest, CrossSiteCantPreemptAfterUnload) {
// Test that a cross-site navigation that doesn't commit after the unload
// handler doesn't leave the contents in a stuck state. http://crbug.com/88562
TEST_F(WebContentsImplTest, CrossSiteNavigationCanceled) {
- contents()->transition_cross_site = true;
TestRenderViewHost* orig_rvh = test_rvh();
SiteInstance* instance1 = contents()->GetSiteInstance();
@@ -1131,11 +1172,12 @@ TEST_F(WebContentsImplTest, CrossSiteNavigationCanceled) {
controller().LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
base::TimeTicks now = base::TimeTicks::Now();
- orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
+ orig_rvh->GetMainFrame()->OnMessageReceived(
+ FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
EXPECT_TRUE(contents()->cross_navigation_pending());
// Simulate swap out message when the response arrives.
- orig_rvh->set_is_swapped_out(true);
+ orig_rvh->OnSwappedOut(false);
// Suppose the navigation doesn't get a chance to commit, and the user
// navigates in the current RVH's SiteInstance.
@@ -1147,7 +1189,7 @@ TEST_F(WebContentsImplTest, CrossSiteNavigationCanceled) {
SiteInstance* instance2 = contents()->GetSiteInstance();
EXPECT_FALSE(contents()->cross_navigation_pending());
EXPECT_EQ(orig_rvh, rvh());
- EXPECT_FALSE(orig_rvh->is_swapped_out());
+ EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, orig_rvh->rvh_state());
EXPECT_EQ(instance1, instance2);
EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
}
@@ -1194,7 +1236,7 @@ TEST_F(WebContentsImplTest, NavigationEntryContentStateNewWindow) {
// When opening a new window, it is navigated to about:blank internally.
// Currently, this results in two DidNavigate events.
- const GURL url(kAboutBlankURL);
+ const GURL url(url::kAboutBlankURL);
contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
@@ -1221,6 +1263,150 @@ TEST_F(WebContentsImplTest, NavigationEntryContentStateNewWindow) {
EXPECT_TRUE(entry_impl2->site_instance()->HasSite());
}
+// Tests that fullscreen is exited throughout the object hierarchy when
+// navigating to a new page.
+TEST_F(WebContentsImplTest, NavigationExitsFullscreen) {
+ FakeFullscreenDelegate fake_delegate;
+ contents()->SetDelegate(&fake_delegate);
+ TestRenderViewHost* orig_rvh = test_rvh();
+
+ // Navigate to a site.
+ const GURL url("http://www.google.com");
+ controller().LoadURL(
+ url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
+ contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
+ EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
+
+ // Toggle fullscreen mode on (as if initiated via IPC from renderer).
+ EXPECT_FALSE(orig_rvh->IsFullscreen());
+ EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
+ EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
+ orig_rvh->OnMessageReceived(
+ ViewHostMsg_ToggleFullscreen(orig_rvh->GetRoutingID(), true));
+ EXPECT_TRUE(orig_rvh->IsFullscreen());
+ EXPECT_TRUE(contents()->IsFullscreenForCurrentTab());
+ EXPECT_TRUE(fake_delegate.IsFullscreenForTabOrPending(contents()));
+
+ // Navigate to a new site.
+ const GURL url2("http://www.yahoo.com");
+ controller().LoadURL(
+ url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
+ RenderViewHost* const pending_rvh = contents()->GetPendingRenderViewHost();
+ contents()->TestDidNavigate(
+ pending_rvh, 1, url2, PAGE_TRANSITION_TYPED);
+
+ // Confirm fullscreen has exited.
+ EXPECT_FALSE(orig_rvh->IsFullscreen());
+ EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
+ EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
+
+ contents()->SetDelegate(NULL);
+}
+
+// Tests that fullscreen is exited throughout the object hierarchy when
+// instructing NavigationController to GoBack() or GoForward().
+TEST_F(WebContentsImplTest, HistoryNavigationExitsFullscreen) {
+ FakeFullscreenDelegate fake_delegate;
+ contents()->SetDelegate(&fake_delegate);
+ TestRenderViewHost* const orig_rvh = test_rvh();
+
+ // Navigate to a site.
+ const GURL url("http://www.google.com");
+ controller().LoadURL(url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
+ contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
+ EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
+
+ // Now, navigate to another page on the same site.
+ const GURL url2("http://www.google.com/search?q=kittens");
+ controller().LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
+ EXPECT_FALSE(contents()->cross_navigation_pending());
+ contents()->TestDidNavigate(orig_rvh, 2, url2, PAGE_TRANSITION_TYPED);
+ EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
+
+ // Sanity-check: Confirm we're not starting out in fullscreen mode.
+ EXPECT_FALSE(orig_rvh->IsFullscreen());
+ EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
+ EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
+
+ for (int i = 0; i < 2; ++i) {
+ // Toggle fullscreen mode on (as if initiated via IPC from renderer).
+ orig_rvh->OnMessageReceived(
+ ViewHostMsg_ToggleFullscreen(orig_rvh->GetRoutingID(), true));
+ EXPECT_TRUE(orig_rvh->IsFullscreen());
+ EXPECT_TRUE(contents()->IsFullscreenForCurrentTab());
+ EXPECT_TRUE(fake_delegate.IsFullscreenForTabOrPending(contents()));
+
+ // Navigate backward (or forward).
+ if (i == 0)
+ controller().GoBack();
+ else
+ controller().GoForward();
+ EXPECT_FALSE(contents()->cross_navigation_pending());
+ EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
+ contents()->TestDidNavigate(
+ orig_rvh, i + 1, url, PAGE_TRANSITION_FORWARD_BACK);
+
+ // Confirm fullscreen has exited.
+ EXPECT_FALSE(orig_rvh->IsFullscreen());
+ EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
+ EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
+ }
+
+ contents()->SetDelegate(NULL);
+}
+
+TEST_F(WebContentsImplTest, TerminateHidesValidationMessage) {
+ FakeValidationMessageDelegate fake_delegate;
+ contents()->SetDelegate(&fake_delegate);
+ EXPECT_FALSE(fake_delegate.hide_validation_message_was_called());
+
+ // Crash the renderer.
+ test_rvh()->OnMessageReceived(
+ ViewHostMsg_RenderProcessGone(
+ 0, base::TERMINATION_STATUS_PROCESS_CRASHED, -1));
+
+ // Confirm HideValidationMessage was called.
+ EXPECT_TRUE(fake_delegate.hide_validation_message_was_called());
+
+ contents()->SetDelegate(NULL);
+}
+
+// Tests that fullscreen is exited throughout the object hierarchy on a renderer
+// crash.
+TEST_F(WebContentsImplTest, CrashExitsFullscreen) {
+ FakeFullscreenDelegate fake_delegate;
+ contents()->SetDelegate(&fake_delegate);
+
+ // Navigate to a site.
+ const GURL url("http://www.google.com");
+ controller().LoadURL(
+ url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
+ contents()->TestDidNavigate(test_rvh(), 1, url, PAGE_TRANSITION_TYPED);
+ EXPECT_EQ(test_rvh(), contents()->GetRenderViewHost());
+
+ // Toggle fullscreen mode on (as if initiated via IPC from renderer).
+ EXPECT_FALSE(test_rvh()->IsFullscreen());
+ EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
+ EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
+ test_rvh()->OnMessageReceived(
+ ViewHostMsg_ToggleFullscreen(test_rvh()->GetRoutingID(), true));
+ EXPECT_TRUE(test_rvh()->IsFullscreen());
+ EXPECT_TRUE(contents()->IsFullscreenForCurrentTab());
+ EXPECT_TRUE(fake_delegate.IsFullscreenForTabOrPending(contents()));
+
+ // Crash the renderer.
+ test_rvh()->OnMessageReceived(
+ ViewHostMsg_RenderProcessGone(
+ 0, base::TERMINATION_STATUS_PROCESS_CRASHED, -1));
+
+ // Confirm fullscreen has exited.
+ EXPECT_FALSE(test_rvh()->IsFullscreen());
+ EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
+ EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
+
+ contents()->SetDelegate(NULL);
+}
+
////////////////////////////////////////////////////////////////////////////////
// Interstitial Tests
////////////////////////////////////////////////////////////////////////////////
@@ -2035,12 +2221,11 @@ TEST_F(WebContentsImplTest, NoJSMessageOnInterstitials) {
// While the interstitial is showing, let's simulate the hidden page
// attempting to show a JS message.
IPC::Message* dummy_message = new IPC::Message;
- bool did_suppress_message = false;
- contents()->RunJavaScriptMessage(contents()->GetRenderViewHost(),
- ASCIIToUTF16("This is an informative message"), ASCIIToUTF16("OK"),
- kGURL, JAVASCRIPT_MESSAGE_TYPE_ALERT, dummy_message,
- &did_suppress_message);
- EXPECT_TRUE(did_suppress_message);
+ contents()->RunJavaScriptMessage(contents()->GetMainFrame(),
+ base::ASCIIToUTF16("This is an informative message"),
+ base::ASCIIToUTF16("OK"),
+ kGURL, JAVASCRIPT_MESSAGE_TYPE_ALERT, dummy_message);
+ EXPECT_TRUE(contents()->last_dialog_suppressed_);
}
// Makes sure that if the source passed to CopyStateFromAndPrune has an
@@ -2132,7 +2317,7 @@ TEST_F(WebContentsImplTest, FilterURLs) {
// A navigation to about:whatever should always look like a navigation to
// about:blank
- GURL url_normalized(kAboutBlankURL);
+ GURL url_normalized(url::kAboutBlankURL);
GURL url_from_ipc("about:whatever");
// We navigate the test WebContents to about:blank, since NavigateAndCommit
@@ -2141,7 +2326,7 @@ TEST_F(WebContentsImplTest, FilterURLs) {
contents()->NavigateAndCommit(url_normalized);
// Check that an IPC with about:whatever is correctly normalized.
- contents()->TestDidFinishLoad(1, url_from_ipc, true);
+ contents()->TestDidFinishLoad(url_from_ipc);
EXPECT_EQ(url_normalized, observer.last_url());
@@ -2153,7 +2338,7 @@ TEST_F(WebContentsImplTest, FilterURLs) {
// Check that an IPC with about:whatever is correctly normalized.
other_contents->TestDidFailLoadWithError(
- 1, url_from_ipc, true, 1, base::string16());
+ url_from_ipc, 1, base::string16());
EXPECT_EQ(url_normalized, other_observer.last_url());
}
@@ -2168,4 +2353,312 @@ TEST_F(WebContentsImplTest, PendingContents) {
EXPECT_EQ(NULL, contents()->GetCreatedWindow(route_id));
}
+TEST_F(WebContentsImplTest, CapturerOverridesPreferredSize) {
+ const gfx::Size original_preferred_size(1024, 768);
+ contents()->UpdatePreferredSize(original_preferred_size);
+
+ // With no capturers, expect the preferred size to be the one propagated into
+ // WebContentsImpl via the RenderViewHostDelegate interface.
+ EXPECT_EQ(contents()->GetCapturerCount(), 0);
+ EXPECT_EQ(original_preferred_size, contents()->GetPreferredSize());
+
+ // Increment capturer count, but without specifying a capture size. Expect
+ // a "not set" preferred size.
+ contents()->IncrementCapturerCount(gfx::Size());
+ EXPECT_EQ(1, contents()->GetCapturerCount());
+ EXPECT_EQ(gfx::Size(), contents()->GetPreferredSize());
+
+ // Increment capturer count again, but with an overriding capture size.
+ // Expect preferred size to now be overridden to the capture size.
+ const gfx::Size capture_size(1280, 720);
+ contents()->IncrementCapturerCount(capture_size);
+ EXPECT_EQ(2, contents()->GetCapturerCount());
+ EXPECT_EQ(capture_size, contents()->GetPreferredSize());
+
+ // Increment capturer count a third time, but the expect that the preferred
+ // size is still the first capture size.
+ const gfx::Size another_capture_size(720, 480);
+ contents()->IncrementCapturerCount(another_capture_size);
+ EXPECT_EQ(3, contents()->GetCapturerCount());
+ EXPECT_EQ(capture_size, contents()->GetPreferredSize());
+
+ // Decrement capturer count twice, but expect the preferred size to still be
+ // overridden.
+ contents()->DecrementCapturerCount();
+ contents()->DecrementCapturerCount();
+ EXPECT_EQ(1, contents()->GetCapturerCount());
+ EXPECT_EQ(capture_size, contents()->GetPreferredSize());
+
+ // Decrement capturer count, and since the count has dropped to zero, the
+ // original preferred size should be restored.
+ contents()->DecrementCapturerCount();
+ EXPECT_EQ(0, contents()->GetCapturerCount());
+ EXPECT_EQ(original_preferred_size, contents()->GetPreferredSize());
+}
+
+// Tests that GetLastActiveTime starts with a real, non-zero time and updates
+// on activity.
+TEST_F(WebContentsImplTest, GetLastActiveTime) {
+ // The WebContents starts with a valid creation time.
+ EXPECT_FALSE(contents()->GetLastActiveTime().is_null());
+
+ // Reset the last active time to a known-bad value.
+ contents()->last_active_time_ = base::TimeTicks();
+ ASSERT_TRUE(contents()->GetLastActiveTime().is_null());
+
+ // Simulate activating the WebContents. The active time should update.
+ contents()->WasShown();
+ EXPECT_FALSE(contents()->GetLastActiveTime().is_null());
+}
+
+class ContentsZoomChangedDelegate : public WebContentsDelegate {
+ public:
+ ContentsZoomChangedDelegate() :
+ contents_zoom_changed_call_count_(0),
+ last_zoom_in_(false) {
+ }
+
+ int GetAndResetContentsZoomChangedCallCount() {
+ int count = contents_zoom_changed_call_count_;
+ contents_zoom_changed_call_count_ = 0;
+ return count;
+ }
+
+ bool last_zoom_in() const {
+ return last_zoom_in_;
+ }
+
+ // WebContentsDelegate:
+ virtual void ContentsZoomChange(bool zoom_in) OVERRIDE {
+ contents_zoom_changed_call_count_++;
+ last_zoom_in_ = zoom_in;
+ }
+
+ private:
+ int contents_zoom_changed_call_count_;
+ bool last_zoom_in_;
+
+ DISALLOW_COPY_AND_ASSIGN(ContentsZoomChangedDelegate);
+};
+
+// Tests that some mouseehweel events get turned into browser zoom requests.
+TEST_F(WebContentsImplTest, HandleWheelEvent) {
+ using blink::WebInputEvent;
+
+ scoped_ptr<ContentsZoomChangedDelegate> delegate(
+ new ContentsZoomChangedDelegate());
+ contents()->SetDelegate(delegate.get());
+
+ int modifiers = 0;
+ // Verify that normal mouse wheel events do nothing to change the zoom level.
+ blink::WebMouseWheelEvent event =
+ SyntheticWebMouseWheelEventBuilder::Build(0, 1, modifiers, false);
+ EXPECT_FALSE(contents()->HandleWheelEvent(event));
+ EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
+
+ modifiers = WebInputEvent::ShiftKey | WebInputEvent::AltKey;
+ event = SyntheticWebMouseWheelEventBuilder::Build(0, 1, modifiers, false);
+ EXPECT_FALSE(contents()->HandleWheelEvent(event));
+ EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
+
+ // But whenever the ctrl modifier is applied, they can increase/decrease zoom.
+ // Except on MacOS where we never want to adjust zoom with mousewheel.
+ modifiers = WebInputEvent::ControlKey;
+ event = SyntheticWebMouseWheelEventBuilder::Build(0, 1, modifiers, false);
+ bool handled = contents()->HandleWheelEvent(event);
+#if defined(OS_MACOSX)
+ EXPECT_FALSE(handled);
+ EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
+#else
+ EXPECT_TRUE(handled);
+ EXPECT_EQ(1, delegate->GetAndResetContentsZoomChangedCallCount());
+ EXPECT_TRUE(delegate->last_zoom_in());
+#endif
+
+ modifiers = WebInputEvent::ControlKey | WebInputEvent::ShiftKey |
+ WebInputEvent::AltKey;
+ event = SyntheticWebMouseWheelEventBuilder::Build(2, -5, modifiers, false);
+ handled = contents()->HandleWheelEvent(event);
+#if defined(OS_MACOSX)
+ EXPECT_FALSE(handled);
+ EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
+#else
+ EXPECT_TRUE(handled);
+ EXPECT_EQ(1, delegate->GetAndResetContentsZoomChangedCallCount());
+ EXPECT_FALSE(delegate->last_zoom_in());
+#endif
+
+ // Unless there is no vertical movement.
+ event = SyntheticWebMouseWheelEventBuilder::Build(2, 0, modifiers, false);
+ EXPECT_FALSE(contents()->HandleWheelEvent(event));
+ EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
+
+ // Events containing precise scrolling deltas also shouldn't result in the
+ // zoom being adjusted, to avoid accidental adjustments caused by
+ // two-finger-scrolling on a touchpad.
+ modifiers = WebInputEvent::ControlKey;
+ event = SyntheticWebMouseWheelEventBuilder::Build(0, 5, modifiers, true);
+ EXPECT_FALSE(contents()->HandleWheelEvent(event));
+ EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
+
+ // Ensure pointers to the delegate aren't kept beyond its lifetime.
+ contents()->SetDelegate(NULL);
+}
+
+// Tests that trackpad GesturePinchUpdate events get turned into browser zoom.
+TEST_F(WebContentsImplTest, HandleGestureEvent) {
+ using blink::WebGestureEvent;
+ using blink::WebInputEvent;
+
+ scoped_ptr<ContentsZoomChangedDelegate> delegate(
+ new ContentsZoomChangedDelegate());
+ contents()->SetDelegate(delegate.get());
+
+ const float kZoomStepValue = 0.6f;
+ blink::WebGestureEvent event = SyntheticWebGestureEventBuilder::Build(
+ WebInputEvent::GesturePinchUpdate, blink::WebGestureDeviceTouchpad);
+
+ // A pinch less than the step value doesn't change the zoom level.
+ event.data.pinchUpdate.scale = 1.0f + kZoomStepValue * 0.8f;
+ EXPECT_TRUE(contents()->HandleGestureEvent(event));
+ EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
+
+ // But repeating the event so the combined scale is greater does.
+ EXPECT_TRUE(contents()->HandleGestureEvent(event));
+ EXPECT_EQ(1, delegate->GetAndResetContentsZoomChangedCallCount());
+ EXPECT_TRUE(delegate->last_zoom_in());
+
+ // Pinching back out one step goes back to 100%.
+ event.data.pinchUpdate.scale = 1.0f - kZoomStepValue;
+ EXPECT_TRUE(contents()->HandleGestureEvent(event));
+ EXPECT_EQ(1, delegate->GetAndResetContentsZoomChangedCallCount());
+ EXPECT_FALSE(delegate->last_zoom_in());
+
+ // Pinching out again doesn't zoom (step is twice as large around 100%).
+ EXPECT_TRUE(contents()->HandleGestureEvent(event));
+ EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
+
+ // And again now it zooms once per step.
+ EXPECT_TRUE(contents()->HandleGestureEvent(event));
+ EXPECT_EQ(1, delegate->GetAndResetContentsZoomChangedCallCount());
+ EXPECT_FALSE(delegate->last_zoom_in());
+
+ // No other type of gesture event is handled by WebContentsImpl (for example
+ // a touchscreen pinch gesture).
+ event = SyntheticWebGestureEventBuilder::Build(
+ WebInputEvent::GesturePinchUpdate, blink::WebGestureDeviceTouchscreen);
+ event.data.pinchUpdate.scale = 1.0f + kZoomStepValue * 3;
+ EXPECT_FALSE(contents()->HandleGestureEvent(event));
+ EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
+
+ // Ensure pointers to the delegate aren't kept beyond it's lifetime.
+ contents()->SetDelegate(NULL);
+}
+
+// Tests that GetRelatedActiveContentsCount is shared between related
+// SiteInstances and includes WebContents that have not navigated yet.
+TEST_F(WebContentsImplTest, ActiveContentsCountBasic) {
+ scoped_refptr<SiteInstance> instance1(
+ SiteInstance::CreateForURL(browser_context(), GURL("http://a.com")));
+ scoped_refptr<SiteInstance> instance2(
+ instance1->GetRelatedSiteInstance(GURL("http://b.com")));
+
+ EXPECT_EQ(0u, instance1->GetRelatedActiveContentsCount());
+ EXPECT_EQ(0u, instance2->GetRelatedActiveContentsCount());
+
+ scoped_ptr<TestWebContents> contents1(
+ TestWebContents::Create(browser_context(), instance1));
+ EXPECT_EQ(1u, instance1->GetRelatedActiveContentsCount());
+ EXPECT_EQ(1u, instance2->GetRelatedActiveContentsCount());
+
+ scoped_ptr<TestWebContents> contents2(
+ TestWebContents::Create(browser_context(), instance1));
+ EXPECT_EQ(2u, instance1->GetRelatedActiveContentsCount());
+ EXPECT_EQ(2u, instance2->GetRelatedActiveContentsCount());
+
+ contents1.reset();
+ EXPECT_EQ(1u, instance1->GetRelatedActiveContentsCount());
+ EXPECT_EQ(1u, instance2->GetRelatedActiveContentsCount());
+
+ contents2.reset();
+ EXPECT_EQ(0u, instance1->GetRelatedActiveContentsCount());
+ EXPECT_EQ(0u, instance2->GetRelatedActiveContentsCount());
+}
+
+// Tests that GetRelatedActiveContentsCount is preserved correctly across
+// same-site and cross-site navigations.
+TEST_F(WebContentsImplTest, ActiveContentsCountNavigate) {
+ scoped_refptr<SiteInstance> instance(
+ SiteInstance::Create(browser_context()));
+
+ EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
+
+ scoped_ptr<TestWebContents> contents(
+ TestWebContents::Create(browser_context(), instance));
+ EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
+
+ // Navigate to a URL.
+ contents->GetController().LoadURL(
+ GURL("http://a.com/1"), Referrer(), PAGE_TRANSITION_TYPED, std::string());
+ EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
+ contents->CommitPendingNavigation();
+ EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
+
+ // Navigate to a URL in the same site.
+ contents->GetController().LoadURL(
+ GURL("http://a.com/2"), Referrer(), PAGE_TRANSITION_TYPED, std::string());
+ EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
+ contents->CommitPendingNavigation();
+ EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
+
+ // Navigate to a URL in a different site.
+ contents->GetController().LoadURL(
+ GURL("http://b.com"), Referrer(), PAGE_TRANSITION_TYPED, std::string());
+ EXPECT_TRUE(contents->cross_navigation_pending());
+ EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
+ contents->CommitPendingNavigation();
+ EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
+
+ contents.reset();
+ EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
+}
+
+// Tests that GetRelatedActiveContentsCount tracks BrowsingInstance changes
+// from WebUI.
+TEST_F(WebContentsImplTest, ActiveContentsCountChangeBrowsingInstance) {
+ scoped_refptr<SiteInstance> instance(
+ SiteInstance::Create(browser_context()));
+
+ EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
+
+ scoped_ptr<TestWebContents> contents(
+ TestWebContents::Create(browser_context(), instance));
+ EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
+
+ // Navigate to a URL.
+ contents->NavigateAndCommit(GURL("http://a.com"));
+ EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
+
+ // Navigate to a URL with WebUI. This will change BrowsingInstances.
+ contents->GetController().LoadURL(
+ GURL(kTestWebUIUrl), Referrer(), PAGE_TRANSITION_TYPED, std::string());
+ EXPECT_TRUE(contents->cross_navigation_pending());
+ scoped_refptr<SiteInstance> instance_webui(
+ contents->GetPendingRenderViewHost()->GetSiteInstance());
+ EXPECT_FALSE(instance->IsRelatedSiteInstance(instance_webui.get()));
+
+ // At this point, contents still counts for the old BrowsingInstance.
+ EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
+ EXPECT_EQ(0u, instance_webui->GetRelatedActiveContentsCount());
+
+ // Commit and contents counts for the new one.
+ contents->CommitPendingNavigation();
+ EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
+ EXPECT_EQ(1u, instance_webui->GetRelatedActiveContentsCount());
+
+ contents.reset();
+ EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
+ EXPECT_EQ(0u, instance_webui->GetRelatedActiveContentsCount());
+}
+
} // namespace content
diff --git a/chromium/content/browser/web_contents/web_contents_view.h b/chromium/content/browser/web_contents/web_contents_view.h
new file mode 100644
index 00000000000..b2eb5090138
--- /dev/null
+++ b/chromium/content/browser/web_contents/web_contents_view.h
@@ -0,0 +1,137 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_WEB_CONTENTS_WEB_CONTENTS_VIEW_H_
+#define CONTENT_BROWSER_WEB_CONTENTS_WEB_CONTENTS_VIEW_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/strings/string16.h"
+#include "content/common/content_export.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/size.h"
+
+namespace content {
+class RenderViewHost;
+class RenderWidgetHost;
+class RenderWidgetHostViewBase;
+struct DropData;
+
+// The WebContentsView is an interface that is implemented by the platform-
+// dependent web contents views. The WebContents uses this interface to talk to
+// them.
+class WebContentsView {
+ public:
+ virtual ~WebContentsView() {}
+
+ // Returns the native widget that contains the contents of the tab.
+ virtual gfx::NativeView GetNativeView() const = 0;
+
+ // Returns the native widget with the main content of the tab (i.e. the main
+ // render view host, though there may be many popups in the tab as children of
+ // the container).
+ virtual gfx::NativeView GetContentNativeView() const = 0;
+
+ // Returns the outermost native view. This will be used as the parent for
+ // dialog boxes.
+ virtual gfx::NativeWindow GetTopLevelNativeWindow() const = 0;
+
+ // Computes the rectangle for the native widget that contains the contents of
+ // the tab in the screen coordinate system.
+ virtual void GetContainerBounds(gfx::Rect* out) const = 0;
+
+ // TODO(brettw) this is a hack. It's used in two places at the time of this
+ // writing: (1) when render view hosts switch, we need to size the replaced
+ // one to be correct, since it wouldn't have known about sizes that happened
+ // while it was hidden; (2) in constrained windows.
+ //
+ // (1) will be fixed once interstitials are cleaned up. (2) seems like it
+ // should be cleaned up or done some other way, since this works for normal
+ // WebContents without the special code.
+ virtual void SizeContents(const gfx::Size& size) = 0;
+
+ // Sets focus to the native widget for this tab.
+ virtual void Focus() = 0;
+
+ // Sets focus to the appropriate element when the WebContents is shown the
+ // first time.
+ virtual void SetInitialFocus() = 0;
+
+ // Stores the currently focused view.
+ virtual void StoreFocus() = 0;
+
+ // Restores focus to the last focus view. If StoreFocus has not yet been
+ // invoked, SetInitialFocus is invoked.
+ virtual void RestoreFocus() = 0;
+
+ // Returns the current drop data, if any.
+ virtual DropData* GetDropData() const = 0;
+
+ // Get the bounds of the View, relative to the parent.
+ virtual gfx::Rect GetViewBounds() const = 0;
+
+ virtual void CreateView(
+ const gfx::Size& initial_size, gfx::NativeView context) = 0;
+
+ // Sets up the View that holds the rendered web page, receives messages for
+ // it and contains page plugins. The host view should be sized to the current
+ // size of the WebContents.
+ virtual RenderWidgetHostViewBase* CreateViewForWidget(
+ RenderWidgetHost* render_widget_host) = 0;
+
+ // Creates a new View that holds a popup and receives messages for it.
+ virtual RenderWidgetHostViewBase* CreateViewForPopupWidget(
+ RenderWidgetHost* render_widget_host) = 0;
+
+ // Sets the page title for the native widgets corresponding to the view. This
+ // is not strictly necessary and isn't expected to be displayed anywhere, but
+ // can aid certain debugging tools such as Spy++ on Windows where you are
+ // trying to find a specific window.
+ virtual void SetPageTitle(const base::string16& title) = 0;
+
+ // Invoked when the WebContents is notified that the RenderView has been
+ // fully created.
+ virtual void RenderViewCreated(RenderViewHost* host) = 0;
+
+ // Invoked when the WebContents is notified that the RenderView has been
+ // swapped in.
+ virtual void RenderViewSwappedIn(RenderViewHost* host) = 0;
+
+ // Invoked to enable/disable overscroll gesture navigation.
+ virtual void SetOverscrollControllerEnabled(bool enabled) = 0;
+
+#if defined(OS_MACOSX)
+ // The web contents view assumes that its view will never be overlapped by
+ // another view (either partially or fully). This allows it to perform
+ // optimizations. If the view is in a view hierarchy where it might be
+ // overlapped by another view, notify the view by calling this with |true|.
+ virtual void SetAllowOverlappingViews(bool overlapping) = 0;
+
+ // Returns true if overlapping views are allowed, false otherwise.
+ virtual bool GetAllowOverlappingViews() const = 0;
+
+ // To draw two overlapping web contents view, the underlaying one should
+ // know about the overlaying one. Caller must ensure that |overlay| exists
+ // until |RemoveOverlayView| is called.
+ virtual void SetOverlayView(WebContentsView* overlay,
+ const gfx::Point& offset) = 0;
+
+ // Removes the previously set overlay view.
+ virtual void RemoveOverlayView() = 0;
+
+ // If we close the tab while a UI control is in an event-tracking
+ // loop, the control may message freed objects and crash.
+ // WebContents::Close() calls IsEventTracking(), and if it returns
+ // true CloseTabAfterEventTracking() is called and the close is not
+ // completed.
+ virtual bool IsEventTracking() const = 0;
+ virtual void CloseTabAfterEventTracking() = 0;
+#endif
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_WEB_CONTENTS_WEB_CONTENTS_VIEW_H_
diff --git a/chromium/content/browser/web_contents/web_contents_view_android.cc b/chromium/content/browser/web_contents/web_contents_view_android.cc
index 21fcf542bb5..3378595be7e 100644
--- a/chromium/content/browser/web_contents/web_contents_view_android.cc
+++ b/chromium/content/browser/web_contents/web_contents_view_android.cc
@@ -7,7 +7,6 @@
#include "base/logging.h"
#include "content/browser/android/content_view_core_impl.h"
#include "content/browser/frame_host/interstitial_page_impl.h"
-#include "content/browser/media/android/browser_media_player_manager.h"
#include "content/browser/renderer_host/render_widget_host_view_android.h"
#include "content/browser/renderer_host/render_view_host_factory.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
@@ -15,7 +14,7 @@
#include "content/public/browser/web_contents_delegate.h"
namespace content {
-WebContentsViewPort* CreateWebContentsView(
+WebContentsView* CreateWebContentsView(
WebContentsImpl* web_contents,
WebContentsViewDelegate* delegate,
RenderViewHostDelegateView** render_view_host_delegate_view) {
@@ -54,14 +53,6 @@ void WebContentsViewAndroid::SetContentViewCore(
}
}
-#if defined(VIDEO_HOLE)
-void WebContentsViewAndroid::NotifyExternalSurface(
- int player_id, bool is_request, const gfx::RectF& rect) {
- if (content_view_core_)
- content_view_core_->NotifyExternalSurface(player_id, is_request, rect);
-}
-#endif // defined(VIDEO_HOLE)
-
gfx::NativeView WebContentsViewAndroid::GetNativeView() const {
return content_view_core_ ? content_view_core_->GetViewAndroid() : NULL;
}
@@ -75,9 +66,8 @@ gfx::NativeWindow WebContentsViewAndroid::GetTopLevelNativeWindow() const {
}
void WebContentsViewAndroid::GetContainerBounds(gfx::Rect* out) const {
- RenderWidgetHostView* rwhv = web_contents_->GetRenderWidgetHostView();
- if (rwhv)
- *out = rwhv->GetViewBounds();
+ *out = content_view_core_ ? gfx::Rect(content_view_core_->GetViewSize())
+ : gfx::Rect();
}
void WebContentsViewAndroid::SetPageTitle(const base::string16& title) {
@@ -85,16 +75,6 @@ void WebContentsViewAndroid::SetPageTitle(const base::string16& title) {
content_view_core_->SetTitle(title);
}
-void WebContentsViewAndroid::OnTabCrashed(base::TerminationStatus status,
- int error_code) {
- RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
- web_contents_->GetRenderViewHost());
- if (rvh->media_player_manager())
- rvh->media_player_manager()->DestroyAllMediaPlayers();
- if (content_view_core_)
- content_view_core_->OnTabCrashed();
-}
-
void WebContentsViewAndroid::SizeContents(const gfx::Size& size) {
// TODO(klobag): Do we need to do anything else?
RenderWidgetHostView* rwhv = web_contents_->GetRenderWidgetHostView();
@@ -130,18 +110,17 @@ DropData* WebContentsViewAndroid::GetDropData() const {
}
gfx::Rect WebContentsViewAndroid::GetViewBounds() const {
- RenderWidgetHostView* rwhv = web_contents_->GetRenderWidgetHostView();
- if (rwhv)
- return rwhv->GetViewBounds();
- else
- return gfx::Rect();
+ if (content_view_core_)
+ return gfx::Rect(content_view_core_->GetViewSize());
+
+ return gfx::Rect();
}
void WebContentsViewAndroid::CreateView(
const gfx::Size& initial_size, gfx::NativeView context) {
}
-RenderWidgetHostView* WebContentsViewAndroid::CreateViewForWidget(
+RenderWidgetHostViewBase* WebContentsViewAndroid::CreateViewForWidget(
RenderWidgetHost* render_widget_host) {
if (render_widget_host->GetView()) {
// During testing, the view will already be set up in most cases to the
@@ -150,7 +129,8 @@ RenderWidgetHostView* WebContentsViewAndroid::CreateViewForWidget(
// view twice), we check for the RVH Factory, which will be set when we're
// making special ones (which go along with the special views).
DCHECK(RenderViewHostFactory::has_factory());
- return render_widget_host->GetView();
+ return static_cast<RenderWidgetHostViewBase*>(
+ render_widget_host->GetView());
}
// Note that while this instructs the render widget host to reference
// |native_view_|, this has no effect without also instructing the
@@ -158,14 +138,13 @@ RenderWidgetHostView* WebContentsViewAndroid::CreateViewForWidget(
// order to paint it. See ContentView::GetRenderWidgetHostViewAndroid for an
// example of how this is achieved for InterstitialPages.
RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(render_widget_host);
- RenderWidgetHostView* view = new RenderWidgetHostViewAndroid(
- rwhi, content_view_core_);
- return view;
+ return new RenderWidgetHostViewAndroid(rwhi, content_view_core_);
}
-RenderWidgetHostView* WebContentsViewAndroid::CreateViewForPopupWidget(
+RenderWidgetHostViewBase* WebContentsViewAndroid::CreateViewForPopupWidget(
RenderWidgetHost* render_widget_host) {
- return RenderWidgetHostViewPort::CreateViewForWidget(render_widget_host);
+ RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(render_widget_host);
+ return new RenderWidgetHostViewAndroid(rwhi, NULL);
}
void WebContentsViewAndroid::RenderViewCreated(RenderViewHost* host) {
@@ -178,9 +157,9 @@ void WebContentsViewAndroid::SetOverscrollControllerEnabled(bool enabled) {
}
void WebContentsViewAndroid::ShowContextMenu(
- const ContextMenuParams& params) {
+ RenderFrameHost* render_frame_host, const ContextMenuParams& params) {
if (delegate_)
- delegate_->ShowContextMenu(params);
+ delegate_->ShowContextMenu(render_frame_host, params);
}
void WebContentsViewAndroid::ShowPopupMenu(
@@ -193,10 +172,15 @@ void WebContentsViewAndroid::ShowPopupMenu(
bool allow_multiple_selection) {
if (content_view_core_) {
content_view_core_->ShowSelectPopupMenu(
- items, selected_item, allow_multiple_selection);
+ bounds, items, selected_item, allow_multiple_selection);
}
}
+void WebContentsViewAndroid::HidePopupMenu() {
+ if (content_view_core_)
+ content_view_core_->HideSelectPopupMenu();
+}
+
void WebContentsViewAndroid::StartDragging(
const DropData& drop_data,
blink::WebDragOperationsMask allowed_ops,
diff --git a/chromium/content/browser/web_contents/web_contents_view_android.h b/chromium/content/browser/web_contents/web_contents_view_android.h
index 48cfee9b39e..2503595e840 100644
--- a/chromium/content/browser/web_contents/web_contents_view_android.h
+++ b/chromium/content/browser/web_contents/web_contents_view_android.h
@@ -6,18 +6,18 @@
#define CONTENT_BROWSER_WEB_CONTENTS_WEB_CONTENTS_VIEW_ANDROID_H_
#include "base/memory/scoped_ptr.h"
-#include "content/browser/web_contents/web_contents_impl.h"
-#include "content/port/browser/render_view_host_delegate_view.h"
-#include "content/port/browser/web_contents_view_port.h"
+#include "content/browser/renderer_host/render_view_host_delegate_view.h"
+#include "content/browser/web_contents/web_contents_view.h"
#include "content/public/browser/web_contents_view_delegate.h"
#include "content/public/common/context_menu_params.h"
#include "ui/gfx/rect_f.h"
namespace content {
class ContentViewCoreImpl;
+class WebContentsImpl;
// Android-specific implementation of the WebContentsView.
-class WebContentsViewAndroid : public WebContentsViewPort,
+class WebContentsViewAndroid : public WebContentsView,
public RenderViewHostDelegateView {
public:
WebContentsViewAndroid(WebContentsImpl* web_contents,
@@ -29,19 +29,11 @@ class WebContentsViewAndroid : public WebContentsViewPort,
// by the UI frontend.
void SetContentViewCore(ContentViewCoreImpl* content_view_core);
-#if defined(VIDEO_HOLE)
- void NotifyExternalSurface(int player_id,
- bool is_request,
- const gfx::RectF& rect);
-#endif // defined(VIDEO_HOLE)
-
// WebContentsView implementation --------------------------------------------
virtual gfx::NativeView GetNativeView() const OVERRIDE;
virtual gfx::NativeView GetContentNativeView() const OVERRIDE;
virtual gfx::NativeWindow GetTopLevelNativeWindow() const OVERRIDE;
virtual void GetContainerBounds(gfx::Rect* out) const OVERRIDE;
- virtual void OnTabCrashed(base::TerminationStatus status,
- int error_code) OVERRIDE;
virtual void SizeContents(const gfx::Size& size) OVERRIDE;
virtual void Focus() OVERRIDE;
virtual void SetInitialFocus() OVERRIDE;
@@ -49,13 +41,11 @@ class WebContentsViewAndroid : public WebContentsViewPort,
virtual void RestoreFocus() OVERRIDE;
virtual DropData* GetDropData() const OVERRIDE;
virtual gfx::Rect GetViewBounds() const OVERRIDE;
-
- // WebContentsViewPort implementation ----------------------------------------
virtual void CreateView(
const gfx::Size& initial_size, gfx::NativeView context) OVERRIDE;
- virtual RenderWidgetHostView* CreateViewForWidget(
+ virtual RenderWidgetHostViewBase* CreateViewForWidget(
RenderWidgetHost* render_widget_host) OVERRIDE;
- virtual RenderWidgetHostView* CreateViewForPopupWidget(
+ virtual RenderWidgetHostViewBase* CreateViewForPopupWidget(
RenderWidgetHost* render_widget_host) OVERRIDE;
virtual void SetPageTitle(const base::string16& title) OVERRIDE;
virtual void RenderViewCreated(RenderViewHost* host) OVERRIDE;
@@ -63,7 +53,8 @@ class WebContentsViewAndroid : public WebContentsViewPort,
virtual void SetOverscrollControllerEnabled(bool enabled) OVERRIDE;
// Backend implementation of RenderViewHostDelegateView.
- virtual void ShowContextMenu(const ContextMenuParams& params) OVERRIDE;
+ virtual void ShowContextMenu(RenderFrameHost* render_frame_host,
+ const ContextMenuParams& params) OVERRIDE;
virtual void ShowPopupMenu(const gfx::Rect& bounds,
int item_height,
double item_font_size,
@@ -71,6 +62,7 @@ class WebContentsViewAndroid : public WebContentsViewPort,
const std::vector<MenuItem>& items,
bool right_aligned,
bool allow_multiple_selection) OVERRIDE;
+ virtual void HidePopupMenu() OVERRIDE;
virtual void StartDragging(const DropData& drop_data,
blink::WebDragOperationsMask allowed_ops,
const gfx::ImageSkia& image,
diff --git a/chromium/content/browser/web_contents/web_contents_view_aura.cc b/chromium/content/browser/web_contents/web_contents_view_aura.cc
index 484d04b33de..db7b056258f 100644
--- a/chromium/content/browser/web_contents/web_contents_view_aura.cc
+++ b/chromium/content/browser/web_contents/web_contents_view_aura.cc
@@ -9,6 +9,7 @@
#include "base/file_util.h"
#include "base/metrics/histogram.h"
#include "base/strings/utf_string_conversions.h"
+#include "content/browser/browser_plugin/browser_plugin_guest.h"
#include "content/browser/download/drag_download_util.h"
#include "content/browser/frame_host/interstitial_page_impl.h"
#include "content/browser/frame_host/navigation_entry_impl.h"
@@ -18,7 +19,9 @@
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_view_aura.h"
+#include "content/browser/web_contents/aura/gesture_nav_simple.h"
#include "content/browser/web_contents/aura/image_window_delegate.h"
+#include "content/browser/web_contents/aura/overscroll_navigation_overlay.h"
#include "content/browser/web_contents/aura/shadow_layer_delegate.h"
#include "content/browser/web_contents/aura/window_slider.h"
#include "content/browser/web_contents/touch_editable_impl_aura.h"
@@ -39,17 +42,15 @@
#include "content/public/common/content_client.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/drop_data.h"
-#include "net/base/net_util.h"
+#include "net/base/filename_util.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
#include "ui/aura/client/aura_constants.h"
-#include "ui/aura/client/drag_drop_client.h"
-#include "ui/aura/client/drag_drop_delegate.h"
#include "ui/aura/client/window_tree_client.h"
#include "ui/aura/env.h"
-#include "ui/aura/root_window.h"
-#include "ui/aura/root_window_observer.h"
#include "ui/aura/window.h"
#include "ui/aura/window_observer.h"
+#include "ui/aura/window_tree_host.h"
+#include "ui/aura/window_tree_host_observer.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/clipboard/custom_data_helper.h"
#include "ui/base/dragdrop/drag_drop_types.h"
@@ -66,9 +67,11 @@
#include "ui/gfx/image/image_png_rep.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/screen.h"
+#include "ui/wm/public/drag_drop_client.h"
+#include "ui/wm/public/drag_drop_delegate.h"
namespace content {
-WebContentsViewPort* CreateWebContentsView(
+WebContentsView* CreateWebContentsView(
WebContentsImpl* web_contents,
WebContentsViewDelegate* delegate,
RenderViewHostDelegateView** render_view_host_delegate_view) {
@@ -96,13 +99,26 @@ bool ShouldNavigateBack(const NavigationController& controller,
controller.CanGoBack();
}
+// Update the |web contents| to be |visible|.
+void UpdateWebContentsVisibility(WebContentsImpl* web_contents, bool visible) {
+ if (visible) {
+ if (!web_contents->should_normally_be_visible())
+ web_contents->WasShown();
+ } else {
+ if (web_contents->should_normally_be_visible())
+ web_contents->WasHidden();
+ }
+}
+
RenderWidgetHostViewAura* ToRenderWidgetHostViewAura(
RenderWidgetHostView* view) {
if (!view || RenderViewHostFactory::has_factory())
return NULL; // Can't cast to RenderWidgetHostViewAura in unit tests.
- RenderProcessHostImpl* process = static_cast<RenderProcessHostImpl*>(
- view->GetRenderWidgetHost()->GetProcess());
- if (process->IsGuest())
+
+ RenderViewHost* rvh = RenderViewHost::From(view->GetRenderWidgetHost());
+ WebContentsImpl* web_contents = static_cast<WebContentsImpl*>(
+ rvh ? WebContents::FromRenderViewHost(rvh) : NULL);
+ if (BrowserPluginGuest::IsGuest(web_contents))
return NULL;
return static_cast<RenderWidgetHostViewAura*>(view);
}
@@ -130,8 +146,7 @@ class OverscrollWindowDelegate : public ImageWindowDelegate {
if (entry && entry->screenshot().get()) {
std::vector<gfx::ImagePNGRep> image_reps;
image_reps.push_back(gfx::ImagePNGRep(entry->screenshot(),
- ui::GetImageScale(
- ui::GetScaleFactorForNativeView(web_contents_window()))));
+ ui::GetScaleFactorForNativeView(web_contents_window())));
image = gfx::Image(image_reps);
}
SetImage(image);
@@ -157,7 +172,7 @@ class OverscrollWindowDelegate : public ImageWindowDelegate {
web_contents_window()->delegate()->OnGestureEvent(event);
}
- WebContents* web_contents_;
+ WebContentsImpl* web_contents_;
// The window is displayed both during the gesture, and after the gesture
// while the navigation is in progress. During the gesture, it is necessary to
@@ -172,52 +187,20 @@ class OverscrollWindowDelegate : public ImageWindowDelegate {
// Listens to all mouse drag events during a drag and drop and sends them to
// the renderer.
-class WebDragSourceAura : public base::MessageLoopForUI::Observer,
- public NotificationObserver {
+class WebDragSourceAura : public NotificationObserver {
public:
WebDragSourceAura(aura::Window* window, WebContentsImpl* contents)
: window_(window),
contents_(contents) {
- base::MessageLoopForUI::current()->AddObserver(this);
registrar_.Add(this,
NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
Source<WebContents>(contents));
}
virtual ~WebDragSourceAura() {
- base::MessageLoopForUI::current()->RemoveObserver(this);
- }
-
- // MessageLoop::Observer implementation:
- virtual base::EventStatus WillProcessEvent(
- const base::NativeEvent& event) OVERRIDE {
- return base::EVENT_CONTINUE;
- }
- virtual void DidProcessEvent(const base::NativeEvent& event) OVERRIDE {
- if (!contents_)
- return;
- ui::EventType type = ui::EventTypeFromNative(event);
- RenderViewHost* rvh = NULL;
- switch (type) {
- case ui::ET_MOUSE_DRAGGED:
- rvh = contents_->GetRenderViewHost();
- if (rvh) {
- gfx::Point screen_loc_in_pixel = ui::EventLocationFromNative(event);
- gfx::Point screen_loc = ConvertViewPointToDIP(rvh->GetView(),
- screen_loc_in_pixel);
- gfx::Point client_loc = screen_loc;
- aura::Window* window = rvh->GetView()->GetNativeView();
- aura::Window::ConvertPointToTarget(window->GetRootWindow(),
- window, &client_loc);
- contents_->DragSourceMovedTo(client_loc.x(), client_loc.y(),
- screen_loc.x(), screen_loc.y());
- }
- break;
- default:
- break;
- }
}
+ // NotificationObserver:
virtual void Observe(int type,
const NotificationSource& source,
const NotificationDetails& details) OVERRIDE {
@@ -244,23 +227,26 @@ class WebDragSourceAura : public base::MessageLoopForUI::Observer,
DISALLOW_COPY_AND_ASSIGN(WebDragSourceAura);
};
-#if defined(OS_WIN)
+#if (!defined(OS_CHROMEOS) && defined(USE_X11)) || defined(OS_WIN)
// Fill out the OSExchangeData with a file contents, synthesizing a name if
// necessary.
void PrepareDragForFileContents(const DropData& drop_data,
ui::OSExchangeData::Provider* provider) {
- base::FilePath file_name(drop_data.file_description_filename);
+ base::FilePath file_name =
+ base::FilePath::FromUTF16Unsafe(drop_data.file_description_filename);
// Images without ALT text will only have a file extension so we need to
// synthesize one from the provided extension and URL.
if (file_name.BaseName().RemoveExtension().empty()) {
- const base::string16 extension = file_name.Extension();
+ const base::FilePath::StringType extension = file_name.Extension();
// Retrieve the name from the URL.
- file_name = base::FilePath(net::GetSuggestedFilename(
- drop_data.url, "", "", "", "", "")).ReplaceExtension(extension);
+ file_name = net::GenerateFileName(drop_data.url, "", "", "", "", "")
+ .ReplaceExtension(extension);
}
provider->SetFileContents(file_name, drop_data.file_contents);
}
+#endif
+#if defined(OS_WIN)
void PrepareDragForDownload(
const DropData& drop_data,
ui::OSExchangeData::Provider* provider,
@@ -310,7 +296,7 @@ void PrepareDragForDownload(
scoped_refptr<DragDownloadFile> download_file =
new DragDownloadFile(
download_path,
- scoped_ptr<net::FileStream>(),
+ base::File(),
download_url,
Referrer(page_url, drop_data.referrer_policy),
page_encoding,
@@ -319,17 +305,68 @@ void PrepareDragForDownload(
download_file.get());
provider->SetDownloadFileInfo(file_download);
}
-#endif
+#endif // defined(OS_WIN)
+
+// Returns the CustomFormat to store file system files.
+const ui::OSExchangeData::CustomFormat& GetFileSystemFileCustomFormat() {
+ static const char kFormatString[] = "chromium/x-file-system-files";
+ CR_DEFINE_STATIC_LOCAL(ui::OSExchangeData::CustomFormat,
+ format,
+ (ui::Clipboard::GetFormatType(kFormatString)));
+ return format;
+}
+
+// Writes file system files to the pickle.
+void WriteFileSystemFilesToPickle(
+ const std::vector<DropData::FileSystemFileInfo>& file_system_files,
+ Pickle* pickle) {
+ pickle->WriteUInt64(file_system_files.size());
+ for (size_t i = 0; i < file_system_files.size(); ++i) {
+ pickle->WriteString(file_system_files[i].url.spec());
+ pickle->WriteInt64(file_system_files[i].size);
+ }
+}
+
+// Reads file system files from the pickle.
+bool ReadFileSystemFilesFromPickle(
+ const Pickle& pickle,
+ std::vector<DropData::FileSystemFileInfo>* file_system_files) {
+ PickleIterator iter(pickle);
+
+ uint64 num_files = 0;
+ if (!pickle.ReadUInt64(&iter, &num_files))
+ return false;
+ file_system_files->resize(num_files);
+
+ for (uint64 i = 0; i < num_files; ++i) {
+ std::string url_string;
+ int64 size = 0;
+ if (!pickle.ReadString(&iter, &url_string) ||
+ !pickle.ReadInt64(&iter, &size))
+ return false;
+
+ GURL url(url_string);
+ if (!url.is_valid())
+ return false;
+
+ (*file_system_files)[i].url = url;
+ (*file_system_files)[i].size = size;
+ }
+ return true;
+}
// Utility to fill a ui::OSExchangeDataProvider object from DropData.
void PrepareDragData(const DropData& drop_data,
ui::OSExchangeData::Provider* provider,
WebContentsImpl* web_contents) {
+ provider->MarkOriginatedFromRenderer();
#if defined(OS_WIN)
// Put download before file contents to prefer the download of a image over
// its thumbnail link.
if (!drop_data.download_metadata.empty())
PrepareDragForDownload(drop_data, provider, web_contents);
+#endif
+#if (!defined(OS_CHROMEOS) && defined(USE_X11)) || defined(OS_WIN)
// We set the file contents before the URL because the URL also sets file
// contents (to a .URL shortcut). We want to prefer file content data over
// a shortcut so we add it first.
@@ -342,17 +379,12 @@ void PrepareDragData(const DropData& drop_data,
provider->SetURL(drop_data.url, drop_data.url_title);
if (!drop_data.html.string().empty())
provider->SetHtml(drop_data.html.string(), drop_data.html_base_url);
- if (!drop_data.filenames.empty()) {
- std::vector<ui::OSExchangeData::FileInfo> filenames;
- for (std::vector<DropData::FileInfo>::const_iterator it =
- drop_data.filenames.begin();
- it != drop_data.filenames.end(); ++it) {
- filenames.push_back(
- ui::OSExchangeData::FileInfo(
- base::FilePath::FromUTF8Unsafe(UTF16ToUTF8(it->path)),
- base::FilePath::FromUTF8Unsafe(UTF16ToUTF8(it->display_name))));
- }
- provider->SetFilenames(filenames);
+ if (!drop_data.filenames.empty())
+ provider->SetFilenames(drop_data.filenames);
+ if (!drop_data.file_system_files.empty()) {
+ Pickle pickle;
+ WriteFileSystemFilesToPickle(drop_data.file_system_files, &pickle);
+ provider->SetPickledData(GetFileSystemFileCustomFormat(), pickle);
}
if (!drop_data.custom_data.empty()) {
Pickle pickle;
@@ -364,6 +396,8 @@ void PrepareDragData(const DropData& drop_data,
// Utility to fill a DropData object from ui::OSExchangeData.
void PrepareDropData(DropData* drop_data, const ui::OSExchangeData& data) {
+ drop_data->did_originate_from_renderer = data.DidOriginateFromRenderer();
+
base::string16 plain_text;
data.GetString(&plain_text);
if (!plain_text.empty())
@@ -386,18 +420,14 @@ void PrepareDropData(DropData* drop_data, const ui::OSExchangeData& data) {
if (html_base_url.is_valid())
drop_data->html_base_url = html_base_url;
- std::vector<ui::OSExchangeData::FileInfo> files;
- if (data.GetFilenames(&files) && !files.empty()) {
- for (std::vector<ui::OSExchangeData::FileInfo>::const_iterator
- it = files.begin(); it != files.end(); ++it) {
- drop_data->filenames.push_back(
- DropData::FileInfo(
- UTF8ToUTF16(it->path.AsUTF8Unsafe()),
- UTF8ToUTF16(it->display_name.AsUTF8Unsafe())));
- }
- }
+ data.GetFilenames(&drop_data->filenames);
Pickle pickle;
+ std::vector<DropData::FileSystemFileInfo> file_system_files;
+ if (data.GetPickledData(GetFileSystemFileCustomFormat(), &pickle) &&
+ ReadFileSystemFilesFromPickle(pickle, &file_system_files))
+ drop_data->file_system_files = file_system_files;
+
if (data.GetPickledData(ui::Clipboard::GetWebCustomDataFormatType(), &pickle))
ui::ReadCustomDataIntoMap(
pickle.data(), pickle.size(), &drop_data->custom_data);
@@ -440,285 +470,10 @@ int ConvertAuraEventFlagsToWebInputEventModifiers(int aura_event_flags) {
return web_input_event_modifiers;
}
-// A LayerDelegate that paints an image for the layer.
-class ImageLayerDelegate : public ui::LayerDelegate {
- public:
- ImageLayerDelegate() {}
-
- virtual ~ImageLayerDelegate() {}
-
- void SetImage(const gfx::Image& image) {
- image_ = image;
- image_size_ = image.AsImageSkia().size();
- }
- const gfx::Image& image() const { return image_; }
-
- private:
- // Overridden from ui::LayerDelegate:
- virtual void OnPaintLayer(gfx::Canvas* canvas) OVERRIDE {
- if (image_.IsEmpty()) {
- canvas->DrawColor(SK_ColorGRAY);
- } else {
- SkISize size = canvas->sk_canvas()->getDeviceSize();
- if (size.width() != image_size_.width() ||
- size.height() != image_size_.height()) {
- canvas->DrawColor(SK_ColorWHITE);
- }
- canvas->DrawImageInt(image_.AsImageSkia(), 0, 0);
- }
- }
-
- // Called when the layer's device scale factor has changed.
- virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE {
- }
-
- // Invoked prior to the bounds changing. The returned closured is run after
- // the bounds change.
- virtual base::Closure PrepareForLayerBoundsChange() OVERRIDE {
- return base::Closure();
- }
-
- gfx::Image image_;
- gfx::Size image_size_;
-
- DISALLOW_COPY_AND_ASSIGN(ImageLayerDelegate);
-};
-
} // namespace
-// When a history navigation is triggered at the end of an overscroll
-// navigation, it is necessary to show the history-screenshot until the page is
-// done navigating and painting. This class accomplishes this by showing the
-// screenshot window on top of the page until the page has completed loading and
-// painting.
-class OverscrollNavigationOverlay :
- public RenderWidgetHostViewAura::PaintObserver,
- public WindowSlider::Delegate {
- public:
- explicit OverscrollNavigationOverlay(WebContentsImpl* web_contents)
- : web_contents_(web_contents),
- image_delegate_(NULL),
- view_(NULL),
- loading_complete_(false),
- received_paint_update_(false),
- compositor_updated_(false),
- slide_direction_(SLIDE_UNKNOWN),
- need_paint_update_(true) {
- }
-
- virtual ~OverscrollNavigationOverlay() {
- if (view_)
- view_->set_paint_observer(NULL);
- }
-
- bool has_window() const { return !!window_.get(); }
-
- void StartObservingView(RenderWidgetHostViewAura* view) {
- if (view_)
- view_->set_paint_observer(NULL);
-
- loading_complete_ = false;
- received_paint_update_ = false;
- compositor_updated_ = false;
- view_ = view;
- if (view_)
- view_->set_paint_observer(this);
-
- // Make sure the overlay window is on top.
- if (window_.get() && window_->parent())
- window_->parent()->StackChildAtTop(window_.get());
- }
-
- void SetOverlayWindow(scoped_ptr<aura::Window> window,
- ImageWindowDelegate* delegate) {
- window_ = window.Pass();
- if (window_.get() && window_->parent())
- window_->parent()->StackChildAtTop(window_.get());
- image_delegate_ = delegate;
-
- if (window_.get() && delegate->has_image()) {
- window_slider_.reset(new WindowSlider(this,
- window_->parent(),
- window_.get()));
- slide_direction_ = SLIDE_UNKNOWN;
- } else {
- window_slider_.reset();
- }
- }
-
- void SetupForTesting() {
- need_paint_update_ = false;
- }
-
- private:
- // Stop observing the page if the page-load has completed and the page has
- // been painted, and a window-slide isn't in progress.
- void StopObservingIfDone() {
- // If there is a screenshot displayed in the overlay window, then wait for
- // the navigated page to complete loading and some paint update before
- // hiding the overlay.
- // If there is no screenshot in the overlay window, then hide this view
- // as soon as there is any new painting notification.
- if ((need_paint_update_ && !received_paint_update_) ||
- (image_delegate_->has_image() && !loading_complete_)) {
- return;
- }
-
- // If a slide is in progress, then do not destroy the window or the slide.
- if (window_slider_.get() && window_slider_->IsSlideInProgress())
- return;
-
- window_slider_.reset();
- window_.reset();
- image_delegate_ = NULL;
- if (view_) {
- view_->set_paint_observer(NULL);
- view_ = NULL;
- }
- }
-
- // Creates a layer to be used for window-slide. |offset| is the offset of the
- // NavigationEntry for the screenshot image to display.
- ui::Layer* CreateSlideLayer(int offset) {
- const NavigationControllerImpl& controller = web_contents_->GetController();
- const NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
- controller.GetEntryAtOffset(offset));
-
- gfx::Image image;
- if (entry && entry->screenshot().get()) {
- std::vector<gfx::ImagePNGRep> image_reps;
- image_reps.push_back(gfx::ImagePNGRep(entry->screenshot(),
- ui::GetImageScale(
- ui::GetScaleFactorForNativeView(window_.get()))));
- image = gfx::Image(image_reps);
- }
- layer_delegate_.SetImage(image);
-
- ui::Layer* layer = new ui::Layer(ui::LAYER_TEXTURED);
- layer->set_delegate(&layer_delegate_);
- return layer;
- }
-
- // Overridden from WindowSlider::Delegate:
- virtual ui::Layer* CreateBackLayer() OVERRIDE {
- if (!web_contents_->GetController().CanGoBack())
- return NULL;
- slide_direction_ = SLIDE_BACK;
- return CreateSlideLayer(-1);
- }
-
- virtual ui::Layer* CreateFrontLayer() OVERRIDE {
- if (!web_contents_->GetController().CanGoForward())
- return NULL;
- slide_direction_ = SLIDE_FRONT;
- return CreateSlideLayer(1);
- }
-
- virtual void OnWindowSlideComplete() OVERRIDE {
- if (slide_direction_ == SLIDE_UNKNOWN) {
- window_slider_.reset();
- StopObservingIfDone();
- return;
- }
-
- // Change the image used for the overlay window.
- image_delegate_->SetImage(layer_delegate_.image());
- window_->layer()->SetTransform(gfx::Transform());
- window_->SchedulePaintInRect(gfx::Rect(window_->bounds().size()));
-
- SlideDirection direction = slide_direction_;
- slide_direction_ = SLIDE_UNKNOWN;
-
- // Reset state and wait for the new navigation page to complete
- // loading/painting.
- StartObservingView(ToRenderWidgetHostViewAura(
- web_contents_->GetRenderWidgetHostView()));
-
- // Perform the navigation.
- if (direction == SLIDE_BACK)
- web_contents_->GetController().GoBack();
- else if (direction == SLIDE_FRONT)
- web_contents_->GetController().GoForward();
- else
- NOTREACHED();
- }
-
- virtual void OnWindowSlideAborted() OVERRIDE {
- StopObservingIfDone();
- }
-
- virtual void OnWindowSliderDestroyed() OVERRIDE {
- // The slider has just been destroyed. Release the ownership.
- WindowSlider* slider ALLOW_UNUSED = window_slider_.release();
- StopObservingIfDone();
- }
-
- // Overridden from RenderWidgetHostViewAura::PaintObserver:
- virtual void OnPaintComplete() OVERRIDE {
- received_paint_update_ = true;
- StopObservingIfDone();
- }
-
- virtual void OnCompositingComplete() OVERRIDE {
- received_paint_update_ = compositor_updated_;
- StopObservingIfDone();
- }
-
- virtual void OnUpdateCompositorContent() OVERRIDE {
- compositor_updated_ = true;
- }
-
- virtual void OnPageLoadComplete() OVERRIDE {
- loading_complete_ = true;
- StopObservingIfDone();
- }
-
- virtual void OnViewDestroyed() OVERRIDE {
- DCHECK(view_);
- view_->set_paint_observer(NULL);
- view_ = NULL;
- }
-
- // The WebContents which is being navigated.
- WebContentsImpl* web_contents_;
-
- scoped_ptr<aura::Window> window_;
-
- // This is the WindowDelegate of |window_|. The delegate manages its own
- // lifetime (destroys itself when |window_| is destroyed).
- ImageWindowDelegate* image_delegate_;
-
- RenderWidgetHostViewAura* view_;
- bool loading_complete_;
- bool received_paint_update_;
- bool compositor_updated_;
-
- enum SlideDirection {
- SLIDE_UNKNOWN,
- SLIDE_BACK,
- SLIDE_FRONT
- };
-
- // The |WindowSlider| that allows sliding history layers while the page is
- // being reloaded.
- scoped_ptr<WindowSlider> window_slider_;
-
- // The direction of the in-progress slide (if any).
- SlideDirection slide_direction_;
-
- // The LayerDelegate used for the back/front layers during a slide.
- ImageLayerDelegate layer_delegate_;
-
- // During tests, the aura windows don't get any paint updates. So the overlay
- // container keeps waiting for a paint update it never receives, causing a
- // timeout. So during tests, disable the wait for paint updates.
- bool need_paint_update_;
-
- DISALLOW_COPY_AND_ASSIGN(OverscrollNavigationOverlay);
-};
-
class WebContentsViewAura::WindowObserver
- : public aura::WindowObserver, public aura::RootWindowObserver {
+ : public aura::WindowObserver, public aura::WindowTreeHostObserver {
public:
explicit WindowObserver(WebContentsViewAura* view)
: view_(view),
@@ -733,8 +488,8 @@ class WebContentsViewAura::WindowObserver
virtual ~WindowObserver() {
view_->window_->RemoveObserver(this);
- if (view_->window_->GetDispatcher())
- view_->window_->GetDispatcher()->RemoveRootWindowObserver(this);
+ if (view_->window_->GetHost())
+ view_->window_->GetHost()->RemoveObserver(this);
if (parent_)
parent_->RemoveObserver(this);
@@ -764,17 +519,19 @@ class WebContentsViewAura::WindowObserver
// going to be deprecated in a year, this is ok for now. The test for this is
// PrintPreviewTest.WindowedNPAPIPluginHidden.
virtual void OnWindowAdded(aura::Window* new_window) OVERRIDE {
- if (new_window == view_->window_)
- return;
-
- if (new_window == parent_)
- return; // This happens if the parent moves to the root window.
+ if (new_window != view_->window_) {
+ // Skip the case when the parent moves to the root window.
+ if (new_window != parent_) {
+ // Observe sibling windows of the WebContents, or children of the root
+ // window.
+ if (new_window->parent() == parent_ ||
+ new_window->parent() == view_->window_->GetRootWindow()) {
+ new_window->AddObserver(this);
+ }
+ }
+ }
- // Observe sibling windows of the WebContents, or children of the root
- // window.
- if (new_window->parent() == parent_ ||
- new_window->parent() == view_->window_->GetRootWindow()) {
- new_window->AddObserver(this);
+ if (new_window->parent() == parent_) {
UpdateConstrainedWindows(NULL);
}
}
@@ -863,7 +620,7 @@ class WebContentsViewAura::WindowObserver
virtual void OnWindowAddedToRootWindow(aura::Window* window) OVERRIDE {
if (window == view_->window_) {
- window->GetDispatcher()->AddRootWindowObserver(this);
+ window->GetHost()->AddObserver(this);
#if defined(OS_WIN)
if (!window->GetRootWindow()->HasObserver(this))
window->GetRootWindow()->AddObserver(this);
@@ -871,9 +628,10 @@ class WebContentsViewAura::WindowObserver
}
}
- virtual void OnWindowRemovingFromRootWindow(aura::Window* window) OVERRIDE {
+ virtual void OnWindowRemovingFromRootWindow(aura::Window* window,
+ aura::Window* new_root) OVERRIDE {
if (window == view_->window_) {
- window->GetDispatcher()->RemoveRootWindowObserver(this);
+ window->GetHost()->RemoveObserver(this);
#if defined(OS_WIN)
window->GetRootWindow()->RemoveObserver(this);
@@ -887,11 +645,11 @@ class WebContentsViewAura::WindowObserver
}
}
- // Overridden RootWindowObserver:
- virtual void OnRootWindowHostMoved(const aura::RootWindow* root,
- const gfx::Point& new_origin) OVERRIDE {
+ // Overridden WindowTreeHostObserver:
+ virtual void OnHostMoved(const aura::WindowTreeHost* host,
+ const gfx::Point& new_origin) OVERRIDE {
TRACE_EVENT1("ui",
- "WebContentsViewAura::WindowObserver::OnRootWindowHostMoved",
+ "WebContentsViewAura::WindowObserver::OnHostMoved",
"new_origin", new_origin.ToString());
// This is for the desktop case (i.e. Aura desktop).
@@ -973,17 +731,13 @@ WebContentsViewAura::~WebContentsViewAura() {
return;
window_observer_.reset();
+ window_->RemoveObserver(this);
// Window needs a valid delegate during its destructor, so we explicitly
// delete it here.
window_.reset();
}
-void WebContentsViewAura::SetupOverlayWindowForTesting() {
- if (navigation_overlay_)
- navigation_overlay_->SetupForTesting();
-}
-
void WebContentsViewAura::SetTouchEditableForTest(
TouchEditableImplAura* touch_editable) {
touch_editable_.reset(touch_editable);
@@ -1013,6 +767,26 @@ void WebContentsViewAura::EndDrag(blink::WebDragOperationsMask ops) {
screen_loc.x(), screen_loc.y(), ops);
}
+void WebContentsViewAura::InstallOverscrollControllerDelegate(
+ RenderWidgetHostViewAura* view) {
+ const std::string value = CommandLine::ForCurrentProcess()->
+ GetSwitchValueASCII(switches::kOverscrollHistoryNavigation);
+ if (value == "0") {
+ navigation_overlay_.reset();
+ return;
+ }
+ if (value == "2") {
+ navigation_overlay_.reset();
+ if (!gesture_nav_simple_)
+ gesture_nav_simple_.reset(new GestureNavSimple(web_contents_));
+ view->overscroll_controller()->set_delegate(gesture_nav_simple_.get());
+ return;
+ }
+ view->overscroll_controller()->set_delegate(this);
+ if (!navigation_overlay_)
+ navigation_overlay_.reset(new OverscrollNavigationOverlay(web_contents_));
+}
+
void WebContentsViewAura::PrepareOverscrollWindow() {
// If there is an existing |overscroll_window_| which is in the middle of an
// animation, then destroying the window here causes the animation to be
@@ -1029,9 +803,9 @@ void WebContentsViewAura::PrepareOverscrollWindow() {
web_contents_,
current_overscroll_gesture_);
overscroll_window_.reset(new aura::Window(overscroll_delegate));
- overscroll_window_->SetType(aura::client::WINDOW_TYPE_CONTROL);
+ overscroll_window_->SetType(ui::wm::WINDOW_TYPE_CONTROL);
overscroll_window_->SetTransparent(true);
- overscroll_window_->Init(ui::LAYER_TEXTURED);
+ overscroll_window_->Init(aura::WINDOW_LAYER_TEXTURED);
overscroll_window_->layer()->SetMasksToBounds(false);
overscroll_window_->SetName("OverscrollOverlay");
@@ -1158,8 +932,7 @@ void WebContentsViewAura::PrepareOverscrollNavigationOverlay() {
overscroll_window_->SetTransform(gfx::Transform());
navigation_overlay_->SetOverlayWindow(overscroll_window_.Pass(),
delegate);
- navigation_overlay_->StartObservingView(ToRenderWidgetHostViewAura(
- web_contents_->GetRenderWidgetHostView()));
+ navigation_overlay_->StartObserving();
}
void WebContentsViewAura::UpdateOverscrollWindowBrightness(float delta_x) {
@@ -1215,10 +988,6 @@ void WebContentsViewAura::GetContainerBounds(gfx::Rect *out) const {
*out = window_->GetBoundsInScreen();
}
-void WebContentsViewAura::OnTabCrashed(base::TerminationStatus status,
- int error_code) {
-}
-
void WebContentsViewAura::SizeContents(const gfx::Size& size) {
gfx::Rect bounds = window_->bounds();
if (bounds.size() != size) {
@@ -1271,7 +1040,7 @@ gfx::Rect WebContentsViewAura::GetViewBounds() const {
}
////////////////////////////////////////////////////////////////////////////////
-// WebContentsViewAura, WebContentsViewPort implementation:
+// WebContentsViewAura, WebContentsView implementation:
void WebContentsViewAura::CreateView(
const gfx::Size& initial_size, gfx::NativeView context) {
@@ -1279,12 +1048,13 @@ void WebContentsViewAura::CreateView(
// if the bookmark bar is not shown and you create a new tab). The right
// value is set shortly after this, so its safe to ignore.
- aura::Env::CreateInstance();
+ aura::Env::CreateInstance(true);
window_.reset(new aura::Window(this));
window_->set_owned_by_parent(false);
- window_->SetType(aura::client::WINDOW_TYPE_CONTROL);
+ window_->SetType(ui::wm::WINDOW_TYPE_CONTROL);
window_->SetTransparent(false);
- window_->Init(ui::LAYER_NOT_DRAWN);
+ window_->Init(aura::WINDOW_LAYER_NOT_DRAWN);
+ window_->AddObserver(this);
aura::Window* root_window = context ? context->GetRootWindow() : NULL;
if (root_window) {
// There are places where there is no context currently because object
@@ -1308,7 +1078,7 @@ void WebContentsViewAura::CreateView(
// The use cases for WindowObserver do not apply to Browser Plugins:
// 1) guests do not support NPAPI plugins.
// 2) guests' window bounds are supposed to come from its embedder.
- if (!web_contents_->GetRenderProcessHost()->IsGuest())
+ if (!BrowserPluginGuest::IsGuest(web_contents_))
window_observer_.reset(new WindowObserver(this));
// delegate_->GetDragDestDelegate() creates a new delegate on every call.
@@ -1318,7 +1088,7 @@ void WebContentsViewAura::CreateView(
drag_dest_delegate_ = delegate_->GetDragDestDelegate();
}
-RenderWidgetHostView* WebContentsViewAura::CreateViewForWidget(
+RenderWidgetHostViewBase* WebContentsViewAura::CreateViewForWidget(
RenderWidgetHost* render_widget_host) {
if (render_widget_host->GetView()) {
// During testing, the view will already be set up in most cases to the
@@ -1327,16 +1097,17 @@ RenderWidgetHostView* WebContentsViewAura::CreateViewForWidget(
// view twice), we check for the RVH Factory, which will be set when we're
// making special ones (which go along with the special views).
DCHECK(RenderViewHostFactory::has_factory());
- return render_widget_host->GetView();
+ return static_cast<RenderWidgetHostViewBase*>(
+ render_widget_host->GetView());
}
- RenderWidgetHostView* view =
- RenderWidgetHostView::CreateViewForWidget(render_widget_host);
+ RenderWidgetHostViewAura* view =
+ new RenderWidgetHostViewAura(render_widget_host);
view->InitAsChild(NULL);
GetNativeView()->AddChild(view->GetNativeView());
if (navigation_overlay_.get() && navigation_overlay_->has_window()) {
- navigation_overlay_->StartObservingView(ToRenderWidgetHostViewAura(view));
+ navigation_overlay_->StartObserving();
}
RenderWidgetHostImpl* host_impl =
@@ -1348,21 +1119,19 @@ RenderWidgetHostView* WebContentsViewAura::CreateViewForWidget(
// We listen to drag drop events in the newly created view's window.
aura::client::SetDragDropDelegate(view->GetNativeView(), this);
- if (host_impl->overscroll_controller() &&
+ if (view->overscroll_controller() &&
(!web_contents_->GetDelegate() ||
web_contents_->GetDelegate()->CanOverscrollContent())) {
- host_impl->overscroll_controller()->set_delegate(this);
- if (!navigation_overlay_)
- navigation_overlay_.reset(new OverscrollNavigationOverlay(web_contents_));
+ InstallOverscrollControllerDelegate(view);
}
AttachTouchEditableToRenderView();
return view;
}
-RenderWidgetHostView* WebContentsViewAura::CreateViewForPopupWidget(
+RenderWidgetHostViewBase* WebContentsViewAura::CreateViewForPopupWidget(
RenderWidgetHost* render_widget_host) {
- return RenderWidgetHostViewPort::CreateViewForWidget(render_widget_host);
+ return new RenderWidgetHostViewAura(render_widget_host);
}
void WebContentsViewAura::SetPageTitle(const base::string16& title) {
@@ -1373,20 +1142,18 @@ void WebContentsViewAura::RenderViewCreated(RenderViewHost* host) {
}
void WebContentsViewAura::RenderViewSwappedIn(RenderViewHost* host) {
- if (navigation_overlay_.get() && navigation_overlay_->has_window()) {
- navigation_overlay_->StartObservingView(
- ToRenderWidgetHostViewAura(host->GetView()));
- }
+ if (navigation_overlay_.get() && navigation_overlay_->has_window())
+ navigation_overlay_->StartObserving();
AttachTouchEditableToRenderView();
}
void WebContentsViewAura::SetOverscrollControllerEnabled(bool enabled) {
- RenderViewHostImpl* host = static_cast<RenderViewHostImpl*>(
- web_contents_->GetRenderViewHost());
- if (host) {
- host->SetOverscrollControllerEnabled(enabled);
+ RenderWidgetHostViewAura* view =
+ ToRenderWidgetHostViewAura(web_contents_->GetRenderWidgetHostView());
+ if (view) {
+ view->SetOverscrollControllerEnabled(enabled);
if (enabled)
- host->overscroll_controller()->set_delegate(this);
+ InstallOverscrollControllerDelegate(view);
}
if (!enabled)
@@ -1398,26 +1165,17 @@ void WebContentsViewAura::SetOverscrollControllerEnabled(bool enabled) {
////////////////////////////////////////////////////////////////////////////////
// WebContentsViewAura, RenderViewHostDelegateView implementation:
-void WebContentsViewAura::ShowContextMenu(const ContextMenuParams& params) {
- if (touch_editable_)
- touch_editable_->EndTouchEditing();
+void WebContentsViewAura::ShowContextMenu(RenderFrameHost* render_frame_host,
+ const ContextMenuParams& params) {
+ if (touch_editable_) {
+ touch_editable_->EndTouchEditing(false);
+ }
if (delegate_) {
- delegate_->ShowContextMenu(params);
+ delegate_->ShowContextMenu(render_frame_host, params);
// WARNING: we may have been deleted during the call to ShowContextMenu().
}
}
-void WebContentsViewAura::ShowPopupMenu(const gfx::Rect& bounds,
- int item_height,
- double item_font_size,
- int selected_item,
- const std::vector<MenuItem>& items,
- bool right_aligned,
- bool allow_multiple_selection) {
- // External popup menus are only used on Mac and Android.
- NOTIMPLEMENTED();
-}
-
void WebContentsViewAura::StartDragging(
const DropData& drop_data,
blink::WebDragOperationsMask operations,
@@ -1431,17 +1189,15 @@ void WebContentsViewAura::StartDragging(
}
if (touch_editable_)
- touch_editable_->EndTouchEditing();
+ touch_editable_->EndTouchEditing(false);
ui::OSExchangeData::Provider* provider = ui::OSExchangeData::CreateProvider();
PrepareDragData(drop_data, provider, web_contents_);
ui::OSExchangeData data(provider); // takes ownership of |provider|.
- if (!image.isNull()) {
- drag_utils::SetDragImageOnDataObject(image,
- gfx::Size(image.width(), image.height()), image_offset, &data);
- }
+ if (!image.isNull())
+ drag_utils::SetDragImageOnDataObject(image, image_offset, &data);
scoped_ptr<WebDragSourceAura> drag_source(
new WebDragSourceAura(GetNativeView(), web_contents_));
@@ -1509,8 +1265,6 @@ void WebContentsViewAura::OnOverscrollUpdate(float delta_x, float delta_y) {
return;
aura::Window* target = GetWindowToAnimateForOverscroll();
- ui::ScopedLayerAnimationSettings settings(target->layer()->GetAnimator());
- settings.SetPreemptionStrategy(ui::LayerAnimator::IMMEDIATELY_SET_NEW_TARGET);
gfx::Vector2d translate = GetTranslationForOverscroll(delta_x, delta_y);
gfx::Transform transform;
@@ -1580,12 +1334,12 @@ void WebContentsViewAura::OnImplicitAnimationsCompleted() {
if (ShouldNavigateForward(web_contents_->GetController(),
completed_overscroll_gesture_)) {
- PrepareOverscrollNavigationOverlay();
web_contents_->GetController().GoForward();
+ PrepareOverscrollNavigationOverlay();
} else if (ShouldNavigateBack(web_contents_->GetController(),
completed_overscroll_gesture_)) {
- PrepareOverscrollNavigationOverlay();
web_contents_->GetController().GoBack();
+ PrepareOverscrollNavigationOverlay();
} else {
if (touch_editable_)
touch_editable_->OverscrollCompleted();
@@ -1667,7 +1421,7 @@ void WebContentsViewAura::OnDeviceScaleFactorChanged(
float device_scale_factor) {
}
-void WebContentsViewAura::OnWindowDestroying() {
+void WebContentsViewAura::OnWindowDestroying(aura::Window* window) {
// This means the destructor is going to be called soon. If there is an
// overscroll gesture in progress (i.e. |overscroll_window_| is not NULL),
// then destroying it in the WebContentsViewAura destructor can trigger other
@@ -1677,14 +1431,10 @@ void WebContentsViewAura::OnWindowDestroying() {
overscroll_window_.reset();
}
-void WebContentsViewAura::OnWindowDestroyed() {
+void WebContentsViewAura::OnWindowDestroyed(aura::Window* window) {
}
void WebContentsViewAura::OnWindowTargetVisibilityChanged(bool visible) {
- if (visible)
- web_contents_->WasShown();
- else
- web_contents_->WasHidden();
}
bool WebContentsViewAura::HasHitTestMask() const {
@@ -1694,10 +1444,6 @@ bool WebContentsViewAura::HasHitTestMask() const {
void WebContentsViewAura::GetHitTestMask(gfx::Path* mask) const {
}
-void WebContentsViewAura::DidRecreateLayer(ui::Layer *old_layer,
- ui::Layer *new_layer) {
-}
-
////////////////////////////////////////////////////////////////////////////////
// WebContentsViewAura, ui::EventHandler implementation:
@@ -1728,17 +1474,25 @@ void WebContentsViewAura::OnMouseEvent(ui::MouseEvent* event) {
// WebContentsViewAura, aura::client::DragDropDelegate implementation:
void WebContentsViewAura::OnDragEntered(const ui::DropTargetEvent& event) {
- if (drag_dest_delegate_)
- drag_dest_delegate_->DragInitialize(web_contents_);
-
+ current_rvh_for_drag_ = web_contents_->GetRenderViewHost();
current_drop_data_.reset(new DropData());
PrepareDropData(current_drop_data_.get(), event.data());
blink::WebDragOperationsMask op = ConvertToWeb(event.source_operations());
+ // Give the delegate an opportunity to cancel the drag.
+ if (!web_contents_->GetDelegate()->CanDragEnter(web_contents_,
+ *current_drop_data_.get(),
+ op)) {
+ current_drop_data_.reset(NULL);
+ return;
+ }
+
+ if (drag_dest_delegate_)
+ drag_dest_delegate_->DragInitialize(web_contents_);
+
gfx::Point screen_pt =
gfx::Screen::GetScreenFor(GetNativeView())->GetCursorScreenPoint();
- current_rvh_for_drag_ = web_contents_->GetRenderViewHost();
web_contents_->GetRenderViewHost()->DragTargetDragEnter(
*current_drop_data_.get(), event.location(), screen_pt, op,
ConvertAuraEventFlagsToWebInputEventModifiers(event.flags()));
@@ -1754,6 +1508,9 @@ int WebContentsViewAura::OnDragUpdated(const ui::DropTargetEvent& event) {
if (current_rvh_for_drag_ != web_contents_->GetRenderViewHost())
OnDragEntered(event);
+ if (!current_drop_data_)
+ return ui::DragDropTypes::DRAG_NONE;
+
blink::WebDragOperationsMask op = ConvertToWeb(event.source_operations());
gfx::Point screen_pt =
gfx::Screen::GetScreenFor(GetNativeView())->GetCursorScreenPoint();
@@ -1772,6 +1529,9 @@ void WebContentsViewAura::OnDragExited() {
if (current_rvh_for_drag_ != web_contents_->GetRenderViewHost())
return;
+ if (!current_drop_data_)
+ return;
+
web_contents_->GetRenderViewHost()->DragTargetDragLeave();
if (drag_dest_delegate_)
drag_dest_delegate_->OnDragLeave();
@@ -1784,6 +1544,9 @@ int WebContentsViewAura::OnPerformDrop(const ui::DropTargetEvent& event) {
if (current_rvh_for_drag_ != web_contents_->GetRenderViewHost())
OnDragEntered(event);
+ if (!current_drop_data_)
+ return ui::DragDropTypes::DRAG_NONE;
+
web_contents_->GetRenderViewHost()->DragTargetDrop(
event.location(),
gfx::Screen::GetScreenFor(GetNativeView())->GetCursorScreenPoint(),
@@ -1791,7 +1554,25 @@ int WebContentsViewAura::OnPerformDrop(const ui::DropTargetEvent& event) {
if (drag_dest_delegate_)
drag_dest_delegate_->OnDrop();
current_drop_data_.reset();
- return current_drag_op_;
+ return ConvertFromWeb(current_drag_op_);
+}
+
+void WebContentsViewAura::OnWindowParentChanged(aura::Window* window,
+ aura::Window* parent) {
+ // On Windows we will get called with a parent of NULL as part of the shut
+ // down process. As such we do only change the visibility when a parent gets
+ // set.
+ if (parent)
+ UpdateWebContentsVisibility(web_contents_, window->IsVisible());
+}
+
+void WebContentsViewAura::OnWindowVisibilityChanged(aura::Window* window,
+ bool visible) {
+ // Ignore any visibility changes in the hierarchy below.
+ if (window != window_.get() && window_->Contains(window))
+ return;
+
+ UpdateWebContentsVisibility(web_contents_, visible);
}
} // namespace content
diff --git a/chromium/content/browser/web_contents/web_contents_view_aura.h b/chromium/content/browser/web_contents/web_contents_view_aura.h
index a0597d38fa0..71a425a18f8 100644
--- a/chromium/content/browser/web_contents/web_contents_view_aura.h
+++ b/chromium/content/browser/web_contents/web_contents_view_aura.h
@@ -10,12 +10,13 @@
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "content/browser/renderer_host/overscroll_controller_delegate.h"
+#include "content/browser/renderer_host/render_view_host_delegate_view.h"
+#include "content/browser/web_contents/web_contents_view.h"
#include "content/common/content_export.h"
-#include "content/port/browser/render_view_host_delegate_view.h"
-#include "content/port/browser/web_contents_view_port.h"
-#include "ui/aura/client/drag_drop_delegate.h"
#include "ui/aura/window_delegate.h"
+#include "ui/aura/window_observer.h"
#include "ui/compositor/layer_animation_observer.h"
+#include "ui/wm/public/drag_drop_delegate.h"
namespace aura {
class Window;
@@ -26,27 +27,30 @@ class DropTargetEvent;
}
namespace content {
+class GestureNavSimple;
class OverscrollNavigationOverlay;
+class RenderWidgetHostImpl;
+class RenderWidgetHostViewAura;
class ShadowLayerDelegate;
class TouchEditableImplAura;
class WebContentsViewDelegate;
class WebContentsImpl;
class WebDragDestDelegate;
-class CONTENT_EXPORT WebContentsViewAura
- : public WebContentsViewPort,
+class WebContentsViewAura
+ : public WebContentsView,
public RenderViewHostDelegateView,
- NON_EXPORTED_BASE(public OverscrollControllerDelegate),
+ public OverscrollControllerDelegate,
public ui::ImplicitAnimationObserver,
public aura::WindowDelegate,
- public aura::client::DragDropDelegate {
+ public aura::client::DragDropDelegate,
+ public aura::WindowObserver {
public:
WebContentsViewAura(WebContentsImpl* web_contents,
WebContentsViewDelegate* delegate);
- void SetupOverlayWindowForTesting();
-
- void SetTouchEditableForTest(TouchEditableImplAura* touch_editable);
+ CONTENT_EXPORT void SetTouchEditableForTest(
+ TouchEditableImplAura* touch_editable);
private:
class WindowObserver;
@@ -57,6 +61,8 @@ class CONTENT_EXPORT WebContentsViewAura
void EndDrag(blink::WebDragOperationsMask ops);
+ void InstallOverscrollControllerDelegate(RenderWidgetHostViewAura* view);
+
// Creates and sets up the overlay window that will be displayed during the
// overscroll gesture.
void PrepareOverscrollWindow();
@@ -101,8 +107,6 @@ class CONTENT_EXPORT WebContentsViewAura
virtual gfx::NativeView GetContentNativeView() const OVERRIDE;
virtual gfx::NativeWindow GetTopLevelNativeWindow() const OVERRIDE;
virtual void GetContainerBounds(gfx::Rect *out) const OVERRIDE;
- virtual void OnTabCrashed(base::TerminationStatus status,
- int error_code) OVERRIDE;
virtual void SizeContents(const gfx::Size& size) OVERRIDE;
virtual void Focus() OVERRIDE;
virtual void SetInitialFocus() OVERRIDE;
@@ -110,13 +114,11 @@ class CONTENT_EXPORT WebContentsViewAura
virtual void RestoreFocus() OVERRIDE;
virtual DropData* GetDropData() const OVERRIDE;
virtual gfx::Rect GetViewBounds() const OVERRIDE;
-
- // Overridden from WebContentsViewPort:
virtual void CreateView(
const gfx::Size& initial_size, gfx::NativeView context) OVERRIDE;
- virtual RenderWidgetHostView* CreateViewForWidget(
+ virtual RenderWidgetHostViewBase* CreateViewForWidget(
RenderWidgetHost* render_widget_host) OVERRIDE;
- virtual RenderWidgetHostView* CreateViewForPopupWidget(
+ virtual RenderWidgetHostViewBase* CreateViewForPopupWidget(
RenderWidgetHost* render_widget_host) OVERRIDE;
virtual void SetPageTitle(const base::string16& title) OVERRIDE;
virtual void RenderViewCreated(RenderViewHost* host) OVERRIDE;
@@ -124,14 +126,8 @@ class CONTENT_EXPORT WebContentsViewAura
virtual void SetOverscrollControllerEnabled(bool enabled) OVERRIDE;
// Overridden from RenderViewHostDelegateView:
- virtual void ShowContextMenu(const ContextMenuParams& params) OVERRIDE;
- virtual void ShowPopupMenu(const gfx::Rect& bounds,
- int item_height,
- double item_font_size,
- int selected_item,
- const std::vector<MenuItem>& items,
- bool right_aligned,
- bool allow_multiple_selection) OVERRIDE;
+ virtual void ShowContextMenu(RenderFrameHost* render_frame_host,
+ const ContextMenuParams& params) OVERRIDE;
virtual void StartDragging(const DropData& drop_data,
blink::WebDragOperationsMask operations,
const gfx::ImageSkia& image,
@@ -165,13 +161,11 @@ class CONTENT_EXPORT WebContentsViewAura
virtual void OnCaptureLost() OVERRIDE;
virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE;
- virtual void OnWindowDestroying() OVERRIDE;
- virtual void OnWindowDestroyed() OVERRIDE;
+ virtual void OnWindowDestroying(aura::Window* window) OVERRIDE;
+ virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE;
virtual void OnWindowTargetVisibilityChanged(bool visible) OVERRIDE;
virtual bool HasHitTestMask() const OVERRIDE;
virtual void GetHitTestMask(gfx::Path* mask) const OVERRIDE;
- virtual void DidRecreateLayer(ui::Layer* old_layer,
- ui::Layer* new_layer) OVERRIDE;
// Overridden from ui::EventHandler:
virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE;
@@ -183,6 +177,12 @@ class CONTENT_EXPORT WebContentsViewAura
virtual void OnDragExited() OVERRIDE;
virtual int OnPerformDrop(const ui::DropTargetEvent& event) OVERRIDE;
+ // Overridden from aura::WindowObserver:
+ virtual void OnWindowParentChanged(aura::Window* window,
+ aura::Window* parent) OVERRIDE;
+ virtual void OnWindowVisibilityChanged(aura::Window* window,
+ bool visible) OVERRIDE;
+
scoped_ptr<aura::Window> window_;
// The window that shows the screenshot of the history page during an
@@ -224,6 +224,7 @@ class CONTENT_EXPORT WebContentsViewAura
scoped_ptr<ShadowLayerDelegate> overscroll_shadow_;
scoped_ptr<TouchEditableImplAura> touch_editable_;
+ scoped_ptr<GestureNavSimple> gesture_nav_simple_;
DISALLOW_COPY_AND_ASSIGN(WebContentsViewAura);
};
diff --git a/chromium/content/browser/web_contents/web_contents_view_aura_browsertest.cc b/chromium/content/browser/web_contents/web_contents_view_aura_browsertest.cc
index 687ce57606f..41dab4c5c18 100644
--- a/chromium/content/browser/web_contents/web_contents_view_aura_browsertest.cc
+++ b/chromium/content/browser/web_contents/web_contents_view_aura_browsertest.cc
@@ -15,20 +15,24 @@
#include "content/browser/frame_host/navigation_controller_impl.h"
#include "content/browser/frame_host/navigation_entry_impl.h"
#include "content/browser/frame_host/navigation_entry_screenshot_manager.h"
-#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/browser/web_contents/web_contents_view.h"
+#include "content/common/view_messages.h"
+#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents_observer.h"
-#include "content/public/browser/web_contents_view.h"
#include "content/public/common/content_switches.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_renderer_host.h"
#include "content/public/test/test_utils.h"
#include "content/shell/browser/shell.h"
-#include "content/test/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
-#include "ui/aura/root_window.h"
#include "ui/aura/test/event_generator.h"
#include "ui/aura/window.h"
+#include "ui/aura/window_tree_host.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
+#include "ui/events/event_processor.h"
+#include "ui/events/event_utils.h"
namespace content {
@@ -128,17 +132,11 @@ class WebContentsViewAuraTest : public ContentBrowserTest {
: screenshot_manager_(NULL) {
}
- virtual void SetUp() OVERRIDE {
- // TODO(jbauman): Remove this. http://crbug.com/268644
- UseRealGLContexts();
- ContentBrowserTest::SetUp();
- }
-
// Executes the javascript synchronously and makes sure the returned value is
// freed properly.
- void ExecuteSyncJSFunction(RenderViewHost* rvh, const std::string& jscript) {
+ void ExecuteSyncJSFunction(RenderFrameHost* rfh, const std::string& jscript) {
scoped_ptr<base::Value> value =
- content::ExecuteScriptAndGetValue(rvh, jscript);
+ content::ExecuteScriptAndGetValue(rfh, jscript);
}
// Starts the test server and navigates to the given url. Sets a large enough
@@ -163,32 +161,28 @@ class WebContentsViewAuraTest : public ContentBrowserTest {
WebContentsImpl* web_contents =
static_cast<WebContentsImpl*>(shell()->web_contents());
NavigationController& controller = web_contents->GetController();
- RenderViewHostImpl* view_host = static_cast<RenderViewHostImpl*>(
- web_contents->GetRenderViewHost());
- WebContentsViewAura* view_aura = static_cast<WebContentsViewAura*>(
- web_contents->GetView());
- view_aura->SetupOverlayWindowForTesting();
+ RenderFrameHost* main_frame = web_contents->GetMainFrame();
EXPECT_FALSE(controller.CanGoBack());
EXPECT_FALSE(controller.CanGoForward());
int index = -1;
scoped_ptr<base::Value> value =
- content::ExecuteScriptAndGetValue(view_host, "get_current()");
+ content::ExecuteScriptAndGetValue(main_frame, "get_current()");
ASSERT_TRUE(value->GetAsInteger(&index));
EXPECT_EQ(0, index);
if (touch_handler)
- ExecuteSyncJSFunction(view_host, "install_touch_handler()");
+ ExecuteSyncJSFunction(main_frame, "install_touch_handler()");
- ExecuteSyncJSFunction(view_host, "navigate_next()");
- ExecuteSyncJSFunction(view_host, "navigate_next()");
- value = content::ExecuteScriptAndGetValue(view_host, "get_current()");
+ ExecuteSyncJSFunction(main_frame, "navigate_next()");
+ ExecuteSyncJSFunction(main_frame, "navigate_next()");
+ value = content::ExecuteScriptAndGetValue(main_frame, "get_current()");
ASSERT_TRUE(value->GetAsInteger(&index));
EXPECT_EQ(2, index);
EXPECT_TRUE(controller.CanGoBack());
EXPECT_FALSE(controller.CanGoForward());
- aura::Window* content = web_contents->GetView()->GetContentNativeView();
+ aura::Window* content = web_contents->GetContentNativeView();
gfx::Rect bounds = content->GetBoundsInRootWindow();
aura::test::EventGenerator generator(content->GetRootWindow(), content);
const int kScrollDurationMs = 20;
@@ -196,7 +190,7 @@ class WebContentsViewAuraTest : public ContentBrowserTest {
{
// Do a swipe-right now. That should navigate backwards.
- base::string16 expected_title = ASCIIToUTF16("Title: #1");
+ base::string16 expected_title = base::ASCIIToUTF16("Title: #1");
content::TitleWatcher title_watcher(web_contents, expected_title);
generator.GestureScrollSequence(
gfx::Point(bounds.x() + 2, bounds.y() + 10),
@@ -205,7 +199,7 @@ class WebContentsViewAuraTest : public ContentBrowserTest {
kScrollSteps);
base::string16 actual_title = title_watcher.WaitAndGetTitle();
EXPECT_EQ(expected_title, actual_title);
- value = content::ExecuteScriptAndGetValue(view_host, "get_current()");
+ value = content::ExecuteScriptAndGetValue(main_frame, "get_current()");
ASSERT_TRUE(value->GetAsInteger(&index));
EXPECT_EQ(1, index);
EXPECT_TRUE(controller.CanGoBack());
@@ -214,7 +208,7 @@ class WebContentsViewAuraTest : public ContentBrowserTest {
{
// Do a fling-right now. That should navigate backwards.
- base::string16 expected_title = ASCIIToUTF16("Title:");
+ base::string16 expected_title = base::ASCIIToUTF16("Title:");
content::TitleWatcher title_watcher(web_contents, expected_title);
generator.GestureScrollSequence(
gfx::Point(bounds.x() + 2, bounds.y() + 10),
@@ -223,7 +217,7 @@ class WebContentsViewAuraTest : public ContentBrowserTest {
kScrollSteps);
base::string16 actual_title = title_watcher.WaitAndGetTitle();
EXPECT_EQ(expected_title, actual_title);
- value = content::ExecuteScriptAndGetValue(view_host, "get_current()");
+ value = content::ExecuteScriptAndGetValue(main_frame, "get_current()");
ASSERT_TRUE(value->GetAsInteger(&index));
EXPECT_EQ(0, index);
EXPECT_FALSE(controller.CanGoBack());
@@ -232,7 +226,7 @@ class WebContentsViewAuraTest : public ContentBrowserTest {
{
// Do a swipe-left now. That should navigate forward.
- base::string16 expected_title = ASCIIToUTF16("Title: #1");
+ base::string16 expected_title = base::ASCIIToUTF16("Title: #1");
content::TitleWatcher title_watcher(web_contents, expected_title);
generator.GestureScrollSequence(
gfx::Point(bounds.right() - 10, bounds.y() + 10),
@@ -241,7 +235,7 @@ class WebContentsViewAuraTest : public ContentBrowserTest {
kScrollSteps);
base::string16 actual_title = title_watcher.WaitAndGetTitle();
EXPECT_EQ(expected_title, actual_title);
- value = content::ExecuteScriptAndGetValue(view_host, "get_current()");
+ value = content::ExecuteScriptAndGetValue(main_frame, "get_current()");
ASSERT_TRUE(value->GetAsInteger(&index));
EXPECT_EQ(1, index);
EXPECT_TRUE(controller.CanGoBack());
@@ -252,11 +246,10 @@ class WebContentsViewAuraTest : public ContentBrowserTest {
int GetCurrentIndex() {
WebContentsImpl* web_contents =
static_cast<WebContentsImpl*>(shell()->web_contents());
- RenderViewHostImpl* view_host = static_cast<RenderViewHostImpl*>(
- web_contents->GetRenderViewHost());
+ RenderFrameHost* main_frame = web_contents->GetMainFrame();
int index = -1;
scoped_ptr<base::Value> value;
- value = content::ExecuteScriptAndGetValue(view_host, "get_current()");
+ value = content::ExecuteScriptAndGetValue(main_frame, "get_current()");
if (!value->GetAsInteger(&index))
index = -1;
return index;
@@ -274,17 +267,14 @@ class WebContentsViewAuraTest : public ContentBrowserTest {
DISALLOW_COPY_AND_ASSIGN(WebContentsViewAuraTest);
};
-// Flaky on Windows (perhaps just Win-Aura): http://crbug.com/305722
-#if defined(OS_WIN)
-#define MAYBE_OverscrollNavigation DISABLED_OverscrollNavigation
-#else
-#define MAYBE_OverscrollNavigation OverscrollNavigation
-#endif
-IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, MAYBE_OverscrollNavigation) {
+// Flaky on Windows and ChromeOS: http://crbug.com/305722
+IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest,
+ DISABLED_OverscrollNavigation) {
TestOverscrollNavigation(false);
}
-// Flaky on Windows (perhaps just Win-Aura): http://crbug.com/305722
+// Flaky on Windows (might be related to the above test):
+// http://crbug.com/305722
#if defined(OS_WIN)
#define MAYBE_OverscrollNavigationWithTouchHandler \
DISABLED_OverscrollNavigationWithTouchHandler
@@ -311,8 +301,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest,
StartTestWithPage("files/overscroll_navigation.html"));
WebContentsImpl* web_contents =
static_cast<WebContentsImpl*>(shell()->web_contents());
- RenderViewHostImpl* view_host = static_cast<RenderViewHostImpl*>(
- web_contents->GetRenderViewHost());
+ RenderFrameHost* main_frame = web_contents->GetMainFrame();
// This test triggers a large number of animations. Speed them up to ensure
// the test completes within its time limit.
@@ -320,29 +309,31 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest,
ui::ScopedAnimationDurationScaleMode::FAST_DURATION);
// Make sure the page has both back/forward history.
- ExecuteSyncJSFunction(view_host, "navigate_next()");
+ ExecuteSyncJSFunction(main_frame, "navigate_next()");
EXPECT_EQ(1, GetCurrentIndex());
- ExecuteSyncJSFunction(view_host, "navigate_next()");
+ ExecuteSyncJSFunction(main_frame, "navigate_next()");
EXPECT_EQ(2, GetCurrentIndex());
web_contents->GetController().GoBack();
EXPECT_EQ(1, GetCurrentIndex());
- aura::Window* content = web_contents->GetView()->GetContentNativeView();
- aura::WindowEventDispatcher* dispatcher = content->GetDispatcher();
+ aura::Window* content = web_contents->GetContentNativeView();
+ ui::EventProcessor* dispatcher = content->GetHost()->event_processor();
gfx::Rect bounds = content->GetBoundsInRootWindow();
- base::TimeDelta timestamp;
+ base::TimeDelta timestamp = ui::EventTimeForNow();
ui::TouchEvent press(ui::ET_TOUCH_PRESSED,
gfx::Point(bounds.x() + bounds.width() / 2, bounds.y() + 5),
0, timestamp);
- dispatcher->AsRootWindowHostDelegate()->OnHostTouchEvent(&press);
+ ui::EventDispatchDetails details = dispatcher->OnEventFromSource(&press);
+ ASSERT_FALSE(details.dispatcher_destroyed);
EXPECT_EQ(1, GetCurrentIndex());
timestamp += base::TimeDelta::FromMilliseconds(10);
ui::TouchEvent move1(ui::ET_TOUCH_MOVED,
gfx::Point(bounds.right() - 10, bounds.y() + 5),
0, timestamp);
- dispatcher->AsRootWindowHostDelegate()->OnHostTouchEvent(&move1);
+ details = dispatcher->OnEventFromSource(&move1);
+ ASSERT_FALSE(details.dispatcher_destroyed);
EXPECT_EQ(1, GetCurrentIndex());
// Swipe back from the right edge, back to the left edge, back to the right
@@ -353,7 +344,8 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest,
ui::TouchEvent inc(ui::ET_TOUCH_MOVED,
gfx::Point(x, bounds.y() + 5),
0, timestamp);
- dispatcher->AsRootWindowHostDelegate()->OnHostTouchEvent(&inc);
+ details = dispatcher->OnEventFromSource(&inc);
+ ASSERT_FALSE(details.dispatcher_destroyed);
EXPECT_EQ(1, GetCurrentIndex());
}
@@ -362,7 +354,8 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest,
ui::TouchEvent inc(ui::ET_TOUCH_MOVED,
gfx::Point(x, bounds.y() + 5),
0, timestamp);
- dispatcher->AsRootWindowHostDelegate()->OnHostTouchEvent(&inc);
+ details = dispatcher->OnEventFromSource(&inc);
+ ASSERT_FALSE(details.dispatcher_destroyed);
EXPECT_EQ(1, GetCurrentIndex());
}
@@ -371,7 +364,8 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest,
ui::TouchEvent inc(ui::ET_TOUCH_MOVED,
gfx::Point(x, bounds.y() + 5),
0, timestamp);
- dispatcher->AsRootWindowHostDelegate()->OnHostTouchEvent(&inc);
+ details = dispatcher->OnEventFromSource(&inc);
+ ASSERT_FALSE(details.dispatcher_destroyed);
EXPECT_EQ(1, GetCurrentIndex());
}
@@ -383,7 +377,9 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest,
// - interactively, when user does an overscroll gesture
// - interactively, when user navigates in history without the overscroll
// gesture.
-IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, OverscrollScreenshot) {
+// Flaky on Windows and ChromeOS (http://crbug.com/357311). Might be related to
+// OverscrollNavigation test.
+IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, DISABLED_OverscrollScreenshot) {
// Disable the test for WinXP. See http://crbug/294116.
#if defined(OS_WIN)
if (base::win::GetVersion() < base::win::VERSION_VISTA) {
@@ -396,51 +392,47 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, OverscrollScreenshot) {
StartTestWithPage("files/overscroll_navigation.html"));
WebContentsImpl* web_contents =
static_cast<WebContentsImpl*>(shell()->web_contents());
- RenderViewHostImpl* view_host = static_cast<RenderViewHostImpl*>(
- web_contents->GetRenderViewHost());
+ RenderFrameHost* main_frame = web_contents->GetMainFrame();
set_min_screenshot_interval(0);
// Do a few navigations initiated by the page.
- ExecuteSyncJSFunction(view_host, "navigate_next()");
+ // Screenshots should never be captured since these are all in-page
+ // navigations.
+ ExecuteSyncJSFunction(main_frame, "navigate_next()");
EXPECT_EQ(1, GetCurrentIndex());
- ExecuteSyncJSFunction(view_host, "navigate_next()");
+ ExecuteSyncJSFunction(main_frame, "navigate_next()");
EXPECT_EQ(2, GetCurrentIndex());
screenshot_manager()->WaitUntilScreenshotIsReady();
- // The current entry won't have any screenshots. But the entries in the
- // history should now have screenshots.
NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
web_contents->GetController().GetEntryAtIndex(2));
EXPECT_FALSE(entry->screenshot().get());
entry = NavigationEntryImpl::FromNavigationEntry(
web_contents->GetController().GetEntryAtIndex(1));
- EXPECT_TRUE(screenshot_manager()->ScreenshotSetForEntry(entry));
+ EXPECT_FALSE(screenshot_manager()->ScreenshotSetForEntry(entry));
entry = NavigationEntryImpl::FromNavigationEntry(
web_contents->GetController().GetEntryAtIndex(0));
- EXPECT_TRUE(screenshot_manager()->ScreenshotSetForEntry(entry));
+ EXPECT_FALSE(screenshot_manager()->ScreenshotSetForEntry(entry));
- // Navigate again. Index 2 should now have a screenshot.
- ExecuteSyncJSFunction(view_host, "navigate_next()");
- EXPECT_EQ(3, GetCurrentIndex());
+ ExecuteSyncJSFunction(main_frame, "navigate_next()");
screenshot_manager()->WaitUntilScreenshotIsReady();
entry = NavigationEntryImpl::FromNavigationEntry(
web_contents->GetController().GetEntryAtIndex(2));
- EXPECT_TRUE(screenshot_manager()->ScreenshotSetForEntry(entry));
+ EXPECT_FALSE(screenshot_manager()->ScreenshotSetForEntry(entry));
entry = NavigationEntryImpl::FromNavigationEntry(
web_contents->GetController().GetEntryAtIndex(3));
EXPECT_FALSE(entry->screenshot().get());
-
{
// Now, swipe right to navigate backwards. This should navigate away from
- // index 3 to index 2, and index 3 should have a screenshot.
- base::string16 expected_title = ASCIIToUTF16("Title: #2");
+ // index 3 to index 2.
+ base::string16 expected_title = base::ASCIIToUTF16("Title: #2");
content::TitleWatcher title_watcher(web_contents, expected_title);
- aura::Window* content = web_contents->GetView()->GetContentNativeView();
+ aura::Window* content = web_contents->GetContentNativeView();
gfx::Rect bounds = content->GetBoundsInRootWindow();
aura::test::EventGenerator generator(content->GetRootWindow(), content);
generator.GestureScrollSequence(
@@ -454,13 +446,13 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, OverscrollScreenshot) {
screenshot_manager()->WaitUntilScreenshotIsReady();
entry = NavigationEntryImpl::FromNavigationEntry(
web_contents->GetController().GetEntryAtIndex(3));
- EXPECT_TRUE(screenshot_manager()->ScreenshotSetForEntry(entry));
+ EXPECT_FALSE(screenshot_manager()->ScreenshotSetForEntry(entry));
}
// Navigate a couple more times.
- ExecuteSyncJSFunction(view_host, "navigate_next()");
+ ExecuteSyncJSFunction(main_frame, "navigate_next()");
EXPECT_EQ(3, GetCurrentIndex());
- ExecuteSyncJSFunction(view_host, "navigate_next()");
+ ExecuteSyncJSFunction(main_frame, "navigate_next()");
EXPECT_EQ(4, GetCurrentIndex());
screenshot_manager()->WaitUntilScreenshotIsReady();
entry = NavigationEntryImpl::FromNavigationEntry(
@@ -469,7 +461,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, OverscrollScreenshot) {
{
// Navigate back in history.
- base::string16 expected_title = ASCIIToUTF16("Title: #3");
+ base::string16 expected_title = base::ASCIIToUTF16("Title: #3");
content::TitleWatcher title_watcher(web_contents, expected_title);
web_contents->GetController().GoBack();
base::string16 actual_title = title_watcher.WaitAndGetTitle();
@@ -478,14 +470,22 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, OverscrollScreenshot) {
screenshot_manager()->WaitUntilScreenshotIsReady();
entry = NavigationEntryImpl::FromNavigationEntry(
web_contents->GetController().GetEntryAtIndex(4));
- EXPECT_TRUE(screenshot_manager()->ScreenshotSetForEntry(entry));
+ EXPECT_FALSE(screenshot_manager()->ScreenshotSetForEntry(entry));
}
}
+// Crashes under ThreadSanitizer, http://crbug.com/356758.
+#if defined(THREAD_SANITIZER)
+#define MAYBE_ScreenshotForSwappedOutRenderViews \
+ DISABLED_ScreenshotForSwappedOutRenderViews
+#else
+#define MAYBE_ScreenshotForSwappedOutRenderViews \
+ ScreenshotForSwappedOutRenderViews
+#endif
// Tests that screenshot is taken correctly when navigation causes a
// RenderViewHost to be swapped out.
IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest,
- ScreenshotForSwappedOutRenderViews) {
+ MAYBE_ScreenshotForSwappedOutRenderViews) {
ASSERT_NO_FATAL_FAILURE(
StartTestWithPage("files/overscroll_navigation.html"));
// Create a new server with a different site.
@@ -552,6 +552,39 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest,
EXPECT_EQ(NULL, screenshot_manager()->screenshot_taken_for());
}
+// Tests that navigations resulting from reloads, history.replaceState,
+// and history.pushState do not capture screenshots.
+IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, ReplaceStateReloadPushState) {
+ ASSERT_NO_FATAL_FAILURE(
+ StartTestWithPage("files/overscroll_navigation.html"));
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+ RenderFrameHost* main_frame = web_contents->GetMainFrame();
+
+ set_min_screenshot_interval(0);
+ screenshot_manager()->Reset();
+ ExecuteSyncJSFunction(main_frame, "use_replace_state()");
+ screenshot_manager()->WaitUntilScreenshotIsReady();
+ // history.replaceState shouldn't capture a screenshot
+ EXPECT_FALSE(screenshot_manager()->screenshot_taken_for());
+ screenshot_manager()->Reset();
+ web_contents->GetController().Reload(true);
+ WaitForLoadStop(web_contents);
+ // reloading the page shouldn't capture a screenshot
+ // TODO (mfomitchev): currently broken. Uncomment when
+ // FrameHostMsg_DidCommitProvisionalLoad_Params.was_within_same_page
+ // is populated properly when reloading the page.
+ //EXPECT_FALSE(screenshot_manager()->screenshot_taken_for());
+ screenshot_manager()->Reset();
+ ExecuteSyncJSFunction(main_frame, "use_push_state()");
+ screenshot_manager()->WaitUntilScreenshotIsReady();
+ // pushing a state shouldn't capture a screenshot
+ // TODO (mfomitchev): currently broken. Uncomment when
+ // FrameHostMsg_DidCommitProvisionalLoad_Params.was_within_same_page
+ // is populated properly when pushState is used.
+ //EXPECT_FALSE(screenshot_manager()->screenshot_taken_for());
+}
+
// TODO(sadrul): This test is disabled because it reparents in a way the
// FocusController does not support. This code would crash in
// a production build. It only passed prior to this revision
@@ -565,14 +598,14 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest,
StartTestWithPage("files/overscroll_navigation.html"));
scoped_ptr<aura::Window> window(new aura::Window(NULL));
- window->Init(ui::LAYER_NOT_DRAWN);
+ window->Init(aura::WINDOW_LAYER_NOT_DRAWN);
WebContentsImpl* web_contents =
static_cast<WebContentsImpl*>(shell()->web_contents());
- ExecuteSyncJSFunction(web_contents->GetRenderViewHost(), "navigate_next()");
+ ExecuteSyncJSFunction(web_contents->GetMainFrame(), "navigate_next()");
EXPECT_EQ(1, GetCurrentIndex());
- aura::Window* content = web_contents->GetView()->GetContentNativeView();
+ aura::Window* content = web_contents->GetContentNativeView();
gfx::Rect bounds = content->GetBoundsInRootWindow();
aura::test::EventGenerator generator(content->GetRootWindow(), content);
generator.GestureScrollSequence(
@@ -581,20 +614,19 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest,
base::TimeDelta::FromMilliseconds(20),
1);
- window->AddChild(shell()->web_contents()->GetView()->GetContentNativeView());
+ window->AddChild(shell()->web_contents()->GetContentNativeView());
}
-IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest,
- ContentWindowClose) {
+IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, ContentWindowClose) {
ASSERT_NO_FATAL_FAILURE(
StartTestWithPage("files/overscroll_navigation.html"));
WebContentsImpl* web_contents =
static_cast<WebContentsImpl*>(shell()->web_contents());
- ExecuteSyncJSFunction(web_contents->GetRenderViewHost(), "navigate_next()");
+ ExecuteSyncJSFunction(web_contents->GetMainFrame(), "navigate_next()");
EXPECT_EQ(1, GetCurrentIndex());
- aura::Window* content = web_contents->GetView()->GetContentNativeView();
+ aura::Window* content = web_contents->GetContentNativeView();
gfx::Rect bounds = content->GetBoundsInRootWindow();
aura::test::EventGenerator generator(content->GetRootWindow(), content);
generator.GestureScrollSequence(
@@ -603,27 +635,34 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest,
base::TimeDelta::FromMilliseconds(20),
1);
- delete web_contents->GetView()->GetContentNativeView();
+ delete web_contents->GetContentNativeView();
}
+
+#if defined(OS_WIN) || (defined(OS_LINUX) && !defined(OS_CHROMEOS))
+// This appears to be flaky in the same was as the other overscroll
+// tests. Enabling for non-Windows platforms.
+// See http://crbug.com/369871.
+// For linux, see http://crbug.com/381294
+#define MAYBE_RepeatedQuickOverscrollGestures DISABLED_RepeatedQuickOverscrollGestures
+#else
+#define MAYBE_RepeatedQuickOverscrollGestures RepeatedQuickOverscrollGestures
+#endif
+
IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest,
- RepeatedQuickOverscrollGestures) {
+ MAYBE_RepeatedQuickOverscrollGestures) {
ASSERT_NO_FATAL_FAILURE(
StartTestWithPage("files/overscroll_navigation.html"));
WebContentsImpl* web_contents =
static_cast<WebContentsImpl*>(shell()->web_contents());
NavigationController& controller = web_contents->GetController();
- RenderViewHostImpl* view_host = static_cast<RenderViewHostImpl*>(
- web_contents->GetRenderViewHost());
- WebContentsViewAura* view_aura = static_cast<WebContentsViewAura*>(
- web_contents->GetView());
- view_aura->SetupOverlayWindowForTesting();
- ExecuteSyncJSFunction(view_host, "install_touch_handler()");
+ RenderFrameHost* main_frame = web_contents->GetMainFrame();
+ ExecuteSyncJSFunction(main_frame, "install_touch_handler()");
// Navigate twice, then navigate back in history once.
- ExecuteSyncJSFunction(view_host, "navigate_next()");
- ExecuteSyncJSFunction(view_host, "navigate_next()");
+ ExecuteSyncJSFunction(main_frame, "navigate_next()");
+ ExecuteSyncJSFunction(main_frame, "navigate_next()");
EXPECT_EQ(2, GetCurrentIndex());
EXPECT_TRUE(controller.CanGoBack());
EXPECT_FALSE(controller.CanGoForward());
@@ -635,13 +674,13 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest,
EXPECT_TRUE(controller.CanGoBack());
EXPECT_TRUE(controller.CanGoForward());
- aura::Window* content = web_contents->GetView()->GetContentNativeView();
+ aura::Window* content = web_contents->GetContentNativeView();
gfx::Rect bounds = content->GetBoundsInRootWindow();
aura::test::EventGenerator generator(content->GetRootWindow(), content);
// Do a swipe left to start a forward navigation. Then quickly do a swipe
// right.
- base::string16 expected_title = ASCIIToUTF16("Title: #2");
+ base::string16 expected_title = base::ASCIIToUTF16("Title: #2");
content::TitleWatcher title_watcher(web_contents, expected_title);
NavigationWatcher nav_watcher(web_contents);
@@ -665,4 +704,17 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest,
EXPECT_FALSE(controller.CanGoForward());
}
+// Verify that hiding a parent of the renderer will hide the content too.
+IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, HideContentOnParenHide) {
+ ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/title1.html"));
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+ aura::Window* content = web_contents->GetNativeView()->parent();
+ EXPECT_TRUE(web_contents->should_normally_be_visible());
+ content->Hide();
+ EXPECT_FALSE(web_contents->should_normally_be_visible());
+ content->Show();
+ EXPECT_TRUE(web_contents->should_normally_be_visible());
+}
+
} // namespace content
diff --git a/chromium/content/browser/web_contents/web_contents_view_gtk.cc b/chromium/content/browser/web_contents/web_contents_view_gtk.cc
deleted file mode 100644
index bf4dcb7fd3b..00000000000
--- a/chromium/content/browser/web_contents/web_contents_view_gtk.cc
+++ /dev/null
@@ -1,431 +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/web_contents/web_contents_view_gtk.h"
-
-#include <gdk/gdk.h>
-#include <gdk/gdkkeysyms.h>
-#include <gtk/gtk.h>
-
-#include <algorithm>
-
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "build/build_config.h"
-#include "content/browser/frame_host/interstitial_page_impl.h"
-#include "content/browser/renderer_host/render_view_host_factory.h"
-#include "content/browser/renderer_host/render_view_host_impl.h"
-#include "content/browser/renderer_host/render_widget_host_view_gtk.h"
-#include "content/browser/web_contents/web_contents_impl.h"
-#include "content/browser/web_contents/web_drag_dest_gtk.h"
-#include "content/browser/web_contents/web_drag_source_gtk.h"
-#include "content/public/browser/web_contents_delegate.h"
-#include "content/public/browser/web_contents_view_delegate.h"
-#include "content/public/common/drop_data.h"
-#include "ui/base/gtk/gtk_expanded_container.h"
-#include "ui/gfx/image/image_skia.h"
-#include "ui/gfx/point.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/size.h"
-
-using blink::WebDragOperation;
-using blink::WebDragOperationsMask;
-
-namespace content {
-namespace {
-
-// Called when the mouse leaves the widget. We notify our delegate.
-gboolean OnLeaveNotify(GtkWidget* widget, GdkEventCrossing* event,
- WebContentsImpl* web_contents) {
- if (web_contents->GetDelegate())
- web_contents->GetDelegate()->ContentsMouseEvent(
- web_contents, gfx::Point(event->x_root, event->y_root), false);
- return FALSE;
-}
-
-// Called when the mouse moves within the widget. We notify our delegate.
-gboolean OnMouseMove(GtkWidget* widget, GdkEventMotion* event,
- WebContentsImpl* web_contents) {
- if (web_contents->GetDelegate())
- web_contents->GetDelegate()->ContentsMouseEvent(
- web_contents, gfx::Point(event->x_root, event->y_root), true);
- return FALSE;
-}
-
-// See tab_contents_view_views.cc for discussion of mouse scroll zooming.
-gboolean OnMouseScroll(GtkWidget* widget, GdkEventScroll* event,
- WebContentsImpl* web_contents) {
- if ((event->state & gtk_accelerator_get_default_mod_mask()) !=
- GDK_CONTROL_MASK) {
- return FALSE;
- }
-
- WebContentsDelegate* delegate = web_contents->GetDelegate();
- if (!delegate)
- return FALSE;
-
- if (!(event->direction == GDK_SCROLL_DOWN ||
- event->direction == GDK_SCROLL_UP)) {
- return FALSE;
- }
-
- delegate->ContentsZoomChange(event->direction == GDK_SCROLL_UP);
- return TRUE;
-}
-
-} // namespace
-
-WebContentsViewPort* CreateWebContentsView(
- WebContentsImpl* web_contents,
- WebContentsViewDelegate* delegate,
- RenderViewHostDelegateView** render_view_host_delegate_view) {
- WebContentsViewGtk* rv = new WebContentsViewGtk(web_contents, delegate);
- *render_view_host_delegate_view = rv;
- return rv;
-}
-
-WebContentsViewGtk::WebContentsViewGtk(
- WebContentsImpl* web_contents,
- WebContentsViewDelegate* delegate)
- : web_contents_(web_contents),
- expanded_(gtk_expanded_container_new()),
- delegate_(delegate) {
- gtk_widget_set_name(expanded_.get(), "chrome-web-contents-view");
- g_signal_connect(expanded_.get(), "size-allocate",
- G_CALLBACK(OnSizeAllocateThunk), this);
- g_signal_connect(expanded_.get(), "child-size-request",
- G_CALLBACK(OnChildSizeRequestThunk), this);
-
- gtk_widget_show(expanded_.get());
- drag_source_.reset(new WebDragSourceGtk(web_contents));
-
- if (delegate_)
- delegate_->Initialize(expanded_.get(), &focus_store_);
-}
-
-WebContentsViewGtk::~WebContentsViewGtk() {
- expanded_.Destroy();
-}
-
-gfx::NativeView WebContentsViewGtk::GetNativeView() const {
- if (delegate_)
- return delegate_->GetNativeView();
-
- return expanded_.get();
-}
-
-gfx::NativeView WebContentsViewGtk::GetContentNativeView() const {
- RenderWidgetHostView* rwhv = web_contents_->GetRenderWidgetHostView();
- if (!rwhv)
- return NULL;
- return rwhv->GetNativeView();
-}
-
-gfx::NativeWindow WebContentsViewGtk::GetTopLevelNativeWindow() const {
- GtkWidget* window = gtk_widget_get_ancestor(GetNativeView(), GTK_TYPE_WINDOW);
- return window ? GTK_WINDOW(window) : NULL;
-}
-
-void WebContentsViewGtk::GetContainerBounds(gfx::Rect* out) const {
- // This is used for positioning the download shelf arrow animation,
- // as well as sizing some other widgets in Windows. In GTK the size is
- // managed for us, so it appears to be only used for the download shelf
- // animation.
- int x = 0;
- int y = 0;
- GdkWindow* expanded_window = gtk_widget_get_window(expanded_.get());
- if (expanded_window)
- gdk_window_get_origin(expanded_window, &x, &y);
-
- GtkAllocation allocation;
- gtk_widget_get_allocation(expanded_.get(), &allocation);
- out->SetRect(x + allocation.x, y + allocation.y,
- requested_size_.width(), requested_size_.height());
-}
-
-void WebContentsViewGtk::OnTabCrashed(base::TerminationStatus status,
- int error_code) {
-}
-
-void WebContentsViewGtk::Focus() {
- if (web_contents_->ShowingInterstitialPage()) {
- web_contents_->GetInterstitialPage()->Focus();
- } else if (delegate_) {
- delegate_->Focus();
- }
-}
-
-void WebContentsViewGtk::SetInitialFocus() {
- if (web_contents_->FocusLocationBarByDefault())
- web_contents_->SetFocusToLocationBar(false);
- else
- Focus();
-}
-
-void WebContentsViewGtk::StoreFocus() {
- focus_store_.Store(GetNativeView());
-}
-
-void WebContentsViewGtk::RestoreFocus() {
- if (focus_store_.widget())
- gtk_widget_grab_focus(focus_store_.widget());
- else
- SetInitialFocus();
-}
-
-DropData* WebContentsViewGtk::GetDropData() const {
- if (!drag_dest_)
- return NULL;
- return drag_dest_->current_drop_data();
-}
-
-gfx::Rect WebContentsViewGtk::GetViewBounds() const {
- gfx::Rect rect;
- GdkWindow* window = gtk_widget_get_window(GetNativeView());
- if (!window) {
- rect.SetRect(0, 0, requested_size_.width(), requested_size_.height());
- return rect;
- }
- int x = 0, y = 0, w, h;
- gdk_window_get_geometry(window, &x, &y, &w, &h, NULL);
- rect.SetRect(x, y, w, h);
- return rect;
-}
-
-void WebContentsViewGtk::CreateView(
- const gfx::Size& initial_size, gfx::NativeView context) {
- requested_size_ = initial_size;
-}
-
-RenderWidgetHostView* WebContentsViewGtk::CreateViewForWidget(
- RenderWidgetHost* render_widget_host) {
- if (render_widget_host->GetView()) {
- // During testing, the view will already be set up in most cases to the
- // test view, so we don't want to clobber it with a real one. To verify that
- // this actually is happening (and somebody isn't accidentally creating the
- // view twice), we check for the RVH Factory, which will be set when we're
- // making special ones (which go along with the special views).
- DCHECK(RenderViewHostFactory::has_factory());
- return render_widget_host->GetView();
- }
-
- RenderWidgetHostView* view =
- RenderWidgetHostView::CreateViewForWidget(render_widget_host);
- view->InitAsChild(NULL);
- gfx::NativeView content_view = view->GetNativeView();
- g_signal_connect(content_view, "focus", G_CALLBACK(OnFocusThunk), this);
- g_signal_connect(content_view, "leave-notify-event",
- G_CALLBACK(OnLeaveNotify), web_contents_);
- g_signal_connect(content_view, "motion-notify-event",
- G_CALLBACK(OnMouseMove), web_contents_);
- g_signal_connect(content_view, "scroll-event",
- G_CALLBACK(OnMouseScroll), web_contents_);
- gtk_widget_add_events(content_view, GDK_LEAVE_NOTIFY_MASK |
- GDK_POINTER_MOTION_MASK);
- InsertIntoContentArea(content_view);
-
- if (render_widget_host->IsRenderView()) {
- RenderViewHost* rvh = RenderViewHost::From(render_widget_host);
- // If |rvh| is already the current render view host for the web contents, we
- // need to initialize |drag_dest_| for drags to be properly handled.
- // Otherwise, |drag_dest_| will be updated in RenderViewSwappedIn. The
- // reason we can't simply check that this isn't a swapped-out view is
- // because there are navigations that create non-swapped-out views that may
- // never be displayed, e.g. a navigation that becomes a download.
- if (rvh == web_contents_->GetRenderViewHost()) {
- UpdateDragDest(rvh);
- }
- }
-
- return view;
-}
-
-RenderWidgetHostView* WebContentsViewGtk::CreateViewForPopupWidget(
- RenderWidgetHost* render_widget_host) {
- return RenderWidgetHostViewPort::CreateViewForWidget(render_widget_host);
-}
-
-void WebContentsViewGtk::SetPageTitle(const base::string16& title) {
- // Set the window name to include the page title so it's easier to spot
- // when debugging (e.g. via xwininfo -tree).
- gfx::NativeView content_view = GetContentNativeView();
- if (content_view) {
- GdkWindow* content_window = gtk_widget_get_window(content_view);
- if (content_window) {
- gdk_window_set_title(content_window, UTF16ToUTF8(title).c_str());
- }
- }
-}
-
-void WebContentsViewGtk::SizeContents(const gfx::Size& size) {
- // We don't need to manually set the size of of widgets in GTK+, but we do
- // need to pass the sizing information on to the RWHV which will pass the
- // sizing information on to the renderer.
- requested_size_ = size;
- RenderWidgetHostView* rwhv = web_contents_->GetRenderWidgetHostView();
- if (rwhv)
- rwhv->SetSize(size);
-}
-
-void WebContentsViewGtk::RenderViewCreated(RenderViewHost* host) {
-}
-
-void WebContentsViewGtk::RenderViewSwappedIn(RenderViewHost* host) {
- UpdateDragDest(host);
-}
-
-void WebContentsViewGtk::SetOverscrollControllerEnabled(bool enabled) {
-}
-
-WebContents* WebContentsViewGtk::web_contents() {
- return web_contents_;
-}
-
-void WebContentsViewGtk::UpdateDragCursor(WebDragOperation operation) {
- if (!drag_dest_)
- return;
- drag_dest_->UpdateDragStatus(operation);
-}
-
-void WebContentsViewGtk::GotFocus() {
- // This is only used in the views FocusManager stuff but it bleeds through
- // all subclasses. http://crbug.com/21875
-}
-
-// This is called when the renderer asks us to take focus back (i.e., it has
-// iterated past the last focusable element on the page).
-void WebContentsViewGtk::TakeFocus(bool reverse) {
- if (!web_contents_->GetDelegate())
- return;
- if (!web_contents_->GetDelegate()->TakeFocus(web_contents_, reverse) &&
- GetTopLevelNativeWindow()) {
- gtk_widget_child_focus(GTK_WIDGET(GetTopLevelNativeWindow()),
- reverse ? GTK_DIR_TAB_BACKWARD : GTK_DIR_TAB_FORWARD);
- }
-}
-
-void WebContentsViewGtk::InsertIntoContentArea(GtkWidget* widget) {
- gtk_container_add(GTK_CONTAINER(expanded_.get()), widget);
-}
-
-void WebContentsViewGtk::UpdateDragDest(RenderViewHost* host) {
- // Drag-and-drop is entirely managed by BrowserPluginGuest for guest
- // processes in a largely platform independent way. WebDragDestGtk
- // will result in spurious messages being sent to the guest process which
- // will violate assumptions.
- if (host->GetProcess() && host->GetProcess()->IsGuest()) {
- DCHECK(!drag_dest_);
- return;
- }
-
- gfx::NativeView content_view = host->GetView()->GetNativeView();
-
- // If the host is already used by the drag_dest_, there's no point in deleting
- // the old one to create an identical copy.
- if (drag_dest_.get() && drag_dest_->widget() == content_view)
- return;
-
- // Clear the currently connected drag drop signals by deleting the old
- // drag_dest_ before creating the new one.
- drag_dest_.reset();
- // Create the new drag_dest_.
- drag_dest_.reset(new WebDragDestGtk(web_contents_, content_view));
-
- if (delegate_)
- drag_dest_->set_delegate(delegate_->GetDragDestDelegate());
-}
-
-// Called when the content view gtk widget is tabbed to, or after the call to
-// gtk_widget_child_focus() in TakeFocus(). We return true
-// and grab focus if we don't have it. The call to
-// FocusThroughTabTraversal(bool) forwards the "move focus forward" effect to
-// webkit.
-gboolean WebContentsViewGtk::OnFocus(GtkWidget* widget,
- GtkDirectionType focus) {
- // Give our view wrapper first chance at this event.
- if (delegate_) {
- gboolean return_value = FALSE;
- if (delegate_->OnNativeViewFocusEvent(widget, focus, &return_value))
- return return_value;
- }
-
- // If we already have focus, let the next widget have a shot at it. We will
- // reach this situation after the call to gtk_widget_child_focus() in
- // TakeFocus().
- if (gtk_widget_is_focus(widget))
- return FALSE;
-
- gtk_widget_grab_focus(widget);
- bool reverse = focus == GTK_DIR_TAB_BACKWARD;
- web_contents_->FocusThroughTabTraversal(reverse);
- return TRUE;
-}
-
-void WebContentsViewGtk::ShowContextMenu(const ContextMenuParams& params) {
- if (delegate_)
- delegate_->ShowContextMenu(params);
- else
- DLOG(ERROR) << "Cannot show context menus without a delegate.";
-}
-
-void WebContentsViewGtk::ShowPopupMenu(const gfx::Rect& bounds,
- int item_height,
- double item_font_size,
- int selected_item,
- const std::vector<MenuItem>& items,
- bool right_aligned,
- bool allow_multiple_selection) {
- // External popup menus are only used on Mac and Android.
- NOTIMPLEMENTED();
-}
-
-// Render view DnD -------------------------------------------------------------
-
-void WebContentsViewGtk::StartDragging(const DropData& drop_data,
- WebDragOperationsMask ops,
- const gfx::ImageSkia& image,
- const gfx::Vector2d& image_offset,
- const DragEventSourceInfo& event_info) {
- DCHECK(GetContentNativeView());
-
- RenderWidgetHostViewGtk* view_gtk = static_cast<RenderWidgetHostViewGtk*>(
- web_contents_->GetRenderWidgetHostView());
- if (!view_gtk || !view_gtk->GetLastMouseDown() ||
- !drag_source_->StartDragging(drop_data, ops, view_gtk->GetLastMouseDown(),
- *image.bitmap(), image_offset)) {
- web_contents_->SystemDragEnded();
- }
-}
-
-// -----------------------------------------------------------------------------
-
-void WebContentsViewGtk::OnChildSizeRequest(GtkWidget* widget,
- GtkWidget* child,
- GtkRequisition* requisition) {
- if (web_contents_->GetDelegate()) {
- requisition->height +=
- web_contents_->GetDelegate()->GetExtraRenderViewHeight();
- }
-}
-
-void WebContentsViewGtk::OnSizeAllocate(GtkWidget* widget,
- GtkAllocation* allocation) {
- int width = allocation->width;
- int height = allocation->height;
- // |delegate()| can be NULL here during browser teardown.
- if (web_contents_->GetDelegate())
- height += web_contents_->GetDelegate()->GetExtraRenderViewHeight();
- gfx::Size size(width, height);
- requested_size_ = size;
-
- // We manually tell our RWHV to resize the renderer content. This avoids
- // spurious resizes from GTK+.
- RenderWidgetHostView* rwhv = web_contents_->GetRenderWidgetHostView();
- if (rwhv)
- rwhv->SetSize(size);
- if (web_contents_->GetInterstitialPage())
- web_contents_->GetInterstitialPage()->SetSize(size);
-}
-
-} // namespace content
diff --git a/chromium/content/browser/web_contents/web_contents_view_gtk.h b/chromium/content/browser/web_contents/web_contents_view_gtk.h
deleted file mode 100644
index 7a8d25e450a..00000000000
--- a/chromium/content/browser/web_contents/web_contents_view_gtk.h
+++ /dev/null
@@ -1,147 +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_WEB_CONTENTS_WEB_CONTENTS_VIEW_GTK_H_
-#define CONTENT_BROWSER_WEB_CONTENTS_WEB_CONTENTS_VIEW_GTK_H_
-
-#include <gtk/gtk.h>
-
-#include <vector>
-
-#include "base/memory/scoped_ptr.h"
-#include "content/common/content_export.h"
-#include "content/common/drag_event_source_info.h"
-#include "content/port/browser/render_view_host_delegate_view.h"
-#include "content/port/browser/web_contents_view_port.h"
-#include "ui/base/gtk/focus_store_gtk.h"
-#include "ui/base/gtk/gtk_signal.h"
-#include "ui/base/gtk/owned_widget_gtk.h"
-
-namespace content {
-
-class WebContents;
-class WebContentsImpl;
-class WebContentsViewDelegate;
-class WebDragDestDelegate;
-class WebDragDestGtk;
-class WebDragSourceGtk;
-
-class CONTENT_EXPORT WebContentsViewGtk
- : public WebContentsViewPort,
- public RenderViewHostDelegateView {
- public:
- // The corresponding WebContentsImpl is passed in the constructor, and manages
- // our lifetime. This doesn't need to be the case, but is this way currently
- // because that's what was easiest when they were split. We optionally take
- // |wrapper| which creates an intermediary widget layer for features from the
- // Embedding layer that lives with the WebContentsView.
- WebContentsViewGtk(WebContentsImpl* web_contents,
- WebContentsViewDelegate* delegate);
- virtual ~WebContentsViewGtk();
-
- WebContentsViewDelegate* delegate() const { return delegate_.get(); }
- WebContents* web_contents();
-
- // WebContentsView implementation --------------------------------------------
-
- virtual gfx::NativeView GetNativeView() const OVERRIDE;
- virtual gfx::NativeView GetContentNativeView() const OVERRIDE;
- virtual gfx::NativeWindow GetTopLevelNativeWindow() const OVERRIDE;
- virtual void GetContainerBounds(gfx::Rect* out) const OVERRIDE;
- virtual void OnTabCrashed(base::TerminationStatus status,
- int error_code) OVERRIDE;
- virtual void SizeContents(const gfx::Size& size) OVERRIDE;
- virtual void Focus() OVERRIDE;
- virtual void SetInitialFocus() OVERRIDE;
- virtual void StoreFocus() OVERRIDE;
- virtual void RestoreFocus() OVERRIDE;
- virtual DropData* GetDropData() const OVERRIDE;
- virtual gfx::Rect GetViewBounds() const OVERRIDE;
-
- // WebContentsViewPort implementation ----------------------------------------
- virtual void CreateView(
- const gfx::Size& initial_size, gfx::NativeView context) OVERRIDE;
- virtual RenderWidgetHostView* CreateViewForWidget(
- RenderWidgetHost* render_widget_host) OVERRIDE;
- virtual RenderWidgetHostView* CreateViewForPopupWidget(
- RenderWidgetHost* render_widget_host) OVERRIDE;
- virtual void SetPageTitle(const base::string16& title) OVERRIDE;
- virtual void RenderViewCreated(RenderViewHost* host) OVERRIDE;
- virtual void RenderViewSwappedIn(RenderViewHost* host) OVERRIDE;
- virtual void SetOverscrollControllerEnabled(bool enabled) OVERRIDE;
-
- // Backend implementation of RenderViewHostDelegateView.
- virtual void ShowContextMenu(const ContextMenuParams& params) OVERRIDE;
- virtual void ShowPopupMenu(const gfx::Rect& bounds,
- int item_height,
- double item_font_size,
- int selected_item,
- const std::vector<MenuItem>& items,
- bool right_aligned,
- bool allow_multiple_selection) OVERRIDE;
- virtual void StartDragging(const DropData& drop_data,
- blink::WebDragOperationsMask allowed_ops,
- const gfx::ImageSkia& image,
- const gfx::Vector2d& image_offset,
- const DragEventSourceInfo& event_info) OVERRIDE;
- virtual void UpdateDragCursor(blink::WebDragOperation operation) OVERRIDE;
- virtual void GotFocus() OVERRIDE;
- virtual void TakeFocus(bool reverse) OVERRIDE;
-
- private:
- // Insert the given widget into the content area. Should only be used for
- // web pages and the like (including interstitials and sad tab). Note that
- // this will be perfectly happy to insert overlapping render views, so care
- // should be taken that the correct one is hidden/shown.
- void InsertIntoContentArea(GtkWidget* widget);
-
- // Replaces, or updates, the existing WebDragDestGtk with one for |new_host|.
- // This must be called when swapping in, or creating a swapped in, RVH.
- void UpdateDragDest(RenderViewHost* new_host);
-
- // Handle focus traversal on the render widget native view. Can be overridden
- // by subclasses.
- CHROMEGTK_CALLBACK_1(WebContentsViewGtk, gboolean, OnFocus, GtkDirectionType);
-
- // Used to adjust the size of its children when the size of |expanded_| is
- // changed.
- CHROMEGTK_CALLBACK_2(WebContentsViewGtk, void, OnChildSizeRequest,
- GtkWidget*, GtkRequisition*);
-
- // Used to propagate the size change of |expanded_| to our RWHV to resize the
- // renderer content.
- CHROMEGTK_CALLBACK_1(WebContentsViewGtk, void, OnSizeAllocate,
- GtkAllocation*);
-
- // The WebContentsImpl whose contents we display.
- WebContentsImpl* web_contents_;
-
- // This container holds the tab's web page views. It is a GtkExpandedContainer
- // so that we can control the size of the web pages.
- ui::OwnedWidgetGtk expanded_;
-
- ui::FocusStoreGtk focus_store_;
-
- // The helper object that handles drag destination related interactions with
- // GTK.
- scoped_ptr<WebDragDestGtk> drag_dest_;
-
- // Object responsible for handling drags from the page for us.
- scoped_ptr<WebDragSourceGtk> drag_source_;
-
- // Our optional views wrapper. If non-NULL, we return this widget as our
- // GetNativeView() and insert |expanded_| as its child in the GtkWidget
- // hierarchy.
- scoped_ptr<WebContentsViewDelegate> delegate_;
-
- // The size we want the view to be. We keep this in a separate variable
- // because resizing in GTK+ is async.
- gfx::Size requested_size_;
-
- DISALLOW_COPY_AND_ASSIGN(WebContentsViewGtk);
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_WEB_CONTENTS_WEB_CONTENTS_VIEW_GTK_H_
diff --git a/chromium/content/browser/web_contents/web_contents_view_guest.cc b/chromium/content/browser/web_contents/web_contents_view_guest.cc
index cbfb7903da5..14f460077b8 100644
--- a/chromium/content/browser/web_contents/web_contents_view_guest.cc
+++ b/chromium/content/browser/web_contents/web_contents_view_guest.cc
@@ -8,11 +8,12 @@
#include "content/browser/browser_plugin/browser_plugin_embedder.h"
#include "content/browser/browser_plugin/browser_plugin_guest.h"
#include "content/browser/frame_host/interstitial_page_impl.h"
+#include "content/browser/frame_host/render_widget_host_view_guest.h"
#include "content/browser/renderer_host/render_view_host_factory.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
-#include "content/browser/renderer_host/render_widget_host_view_guest.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/drag_messages.h"
+#include "content/public/browser/user_metrics.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/common/context_menu_params.h"
#include "content/public/common/drop_data.h"
@@ -33,7 +34,7 @@ namespace content {
WebContentsViewGuest::WebContentsViewGuest(
WebContentsImpl* web_contents,
BrowserPluginGuest* guest,
- scoped_ptr<WebContentsViewPort> platform_view,
+ scoped_ptr<WebContentsView> platform_view,
RenderViewHostDelegateView* platform_view_delegate_view)
: web_contents_(web_contents),
guest_(guest),
@@ -56,23 +57,42 @@ gfx::NativeView WebContentsViewGuest::GetContentNativeView() const {
}
gfx::NativeWindow WebContentsViewGuest::GetTopLevelNativeWindow() const {
- return guest_->embedder_web_contents()->GetView()->GetTopLevelNativeWindow();
+ return guest_->embedder_web_contents()->GetTopLevelNativeWindow();
}
void WebContentsViewGuest::OnGuestInitialized(WebContentsView* parent_view) {
-#if defined(USE_AURA) || defined(OS_WIN)
- // In aura and windows, ScreenPositionClient doesn't work properly if we do
+#if defined(USE_AURA)
+ // In aura, ScreenPositionClient doesn't work properly if we do
// not have the native view associated with this WebContentsViewGuest in the
// view hierarchy. We add this view as embedder's child here.
// This would go in WebContentsViewGuest::CreateView, but that is too early to
// access embedder_web_contents(). Therefore, we do it here.
-#if defined(USE_AURA)
- // This can be win aura or chromeos.
parent_view->GetNativeView()->AddChild(platform_view_->GetNativeView());
-#elif defined(OS_WIN)
- SetParent(platform_view_->GetNativeView(), parent_view->GetNativeView());
+#endif // defined(USE_AURA)
+}
+
+ContextMenuParams WebContentsViewGuest::ConvertContextMenuParams(
+ const ContextMenuParams& params) const {
+#if defined(USE_AURA)
+ // Context menu uses ScreenPositionClient::ConvertPointToScreen() in aura
+ // to calculate popup position. Guest's native view
+ // (platform_view_->GetNativeView()) is part of the embedder's view hierarchy,
+ // but is placed at (0, 0) w.r.t. the embedder's position. Therefore, |offset|
+ // is added to |params|.
+ gfx::Rect embedder_bounds;
+ guest_->embedder_web_contents()->GetView()->GetContainerBounds(
+ &embedder_bounds);
+ gfx::Rect guest_bounds;
+ GetContainerBounds(&guest_bounds);
+
+ gfx::Vector2d offset = guest_bounds.origin() - embedder_bounds.origin();
+ ContextMenuParams params_in_embedder = params;
+ params_in_embedder.x += offset.x();
+ params_in_embedder.y += offset.y();
+ return params_in_embedder;
+#else
+ return params;
#endif
-#endif // defined(USE_AURA) || defined(OS_WIN)
}
void WebContentsViewGuest::GetContainerBounds(gfx::Rect* out) const {
@@ -123,7 +143,7 @@ void WebContentsViewGuest::CreateView(const gfx::Size& initial_size,
size_ = initial_size;
}
-RenderWidgetHostView* WebContentsViewGuest::CreateViewForWidget(
+RenderWidgetHostViewBase* WebContentsViewGuest::CreateViewForWidget(
RenderWidgetHost* render_widget_host) {
if (render_widget_host->GetView()) {
// During testing, the view will already be set up in most cases to the
@@ -132,13 +152,14 @@ RenderWidgetHostView* WebContentsViewGuest::CreateViewForWidget(
// view twice), we check for the RVH Factory, which will be set when we're
// making special ones (which go along with the special views).
DCHECK(RenderViewHostFactory::has_factory());
- return render_widget_host->GetView();
+ return static_cast<RenderWidgetHostViewBase*>(
+ render_widget_host->GetView());
}
- RenderWidgetHostView* platform_widget = NULL;
- platform_widget = platform_view_->CreateViewForWidget(render_widget_host);
+ RenderWidgetHostViewBase* platform_widget =
+ platform_view_->CreateViewForWidget(render_widget_host);
- RenderWidgetHostView* view = new RenderWidgetHostViewGuest(
+ RenderWidgetHostViewBase* view = new RenderWidgetHostViewGuest(
render_widget_host,
guest_,
platform_widget);
@@ -146,9 +167,9 @@ RenderWidgetHostView* WebContentsViewGuest::CreateViewForWidget(
return view;
}
-RenderWidgetHostView* WebContentsViewGuest::CreateViewForPopupWidget(
+RenderWidgetHostViewBase* WebContentsViewGuest::CreateViewForPopupWidget(
RenderWidgetHost* render_widget_host) {
- return RenderWidgetHostViewPort::CreateViewForWidget(render_widget_host);
+ return platform_view_->CreateViewForPopupWidget(render_widget_host);
}
void WebContentsViewGuest::SetPageTitle(const base::string16& title) {
@@ -183,10 +204,6 @@ void WebContentsViewGuest::RestoreFocus() {
platform_view_->RestoreFocus();
}
-void WebContentsViewGuest::OnTabCrashed(base::TerminationStatus status,
- int error_code) {
-}
-
void WebContentsViewGuest::Focus() {
platform_view_->Focus();
}
@@ -217,38 +234,10 @@ void WebContentsViewGuest::GotFocus() {
void WebContentsViewGuest::TakeFocus(bool reverse) {
}
-void WebContentsViewGuest::ShowContextMenu(const ContextMenuParams& params) {
-#if defined(USE_AURA) || defined(OS_WIN)
- // Context menu uses ScreenPositionClient::ConvertPointToScreen() in aura and
- // windows to calculate popup position. Guest's native view
- // (platform_view_->GetNativeView()) is part of the embedder's view hierarchy,
- // but is placed at (0, 0) w.r.t. the embedder's position. Therefore, |offset|
- // is added to |params|.
- gfx::Rect embedder_bounds;
- guest_->embedder_web_contents()->GetView()->GetContainerBounds(
- &embedder_bounds);
- gfx::Rect guest_bounds;
- GetContainerBounds(&guest_bounds);
-
- gfx::Vector2d offset = guest_bounds.origin() - embedder_bounds.origin();
- ContextMenuParams params_in_embedder = params;
- params_in_embedder.x += offset.x();
- params_in_embedder.y += offset.y();
- platform_view_delegate_view_->ShowContextMenu(params_in_embedder);
-#else
- platform_view_delegate_view_->ShowContextMenu(params);
-#endif // defined(USE_AURA) || defined(OS_WIN)
-}
-
-void WebContentsViewGuest::ShowPopupMenu(const gfx::Rect& bounds,
- int item_height,
- double item_font_size,
- int selected_item,
- const std::vector<MenuItem>& items,
- bool right_aligned,
- bool allow_multiple_selection) {
- // External popup menus are only used on Mac and Android.
- NOTIMPLEMENTED();
+void WebContentsViewGuest::ShowContextMenu(RenderFrameHost* render_frame_host,
+ const ContextMenuParams& params) {
+ platform_view_delegate_view_->ShowContextMenu(
+ render_frame_host, ConvertContextMenuParams(params));
}
void WebContentsViewGuest::StartDragging(
@@ -265,10 +254,12 @@ void WebContentsViewGuest::StartDragging(
CHECK(embedder_render_view_host);
RenderViewHostDelegateView* view =
embedder_render_view_host->GetDelegate()->GetDelegateView();
- if (view)
+ if (view) {
+ RecordAction(base::UserMetricsAction("BrowserPlugin.Guest.StartDrag"));
view->StartDragging(drop_data, ops, image, image_offset, event_info);
- else
+ } else {
embedder_web_contents->SystemDragEnded();
+ }
}
} // namespace content
diff --git a/chromium/content/browser/web_contents/web_contents_view_guest.h b/chromium/content/browser/web_contents/web_contents_view_guest.h
index 68fb473d709..53edd64e47f 100644
--- a/chromium/content/browser/web_contents/web_contents_view_guest.h
+++ b/chromium/content/browser/web_contents/web_contents_view_guest.h
@@ -8,10 +8,10 @@
#include <vector>
#include "base/memory/scoped_ptr.h"
+#include "content/browser/renderer_host/render_view_host_delegate_view.h"
+#include "content/browser/web_contents/web_contents_view.h"
#include "content/common/content_export.h"
#include "content/common/drag_event_source_info.h"
-#include "content/port/browser/render_view_host_delegate_view.h"
-#include "content/port/browser/web_contents_view_port.h"
namespace content {
@@ -19,9 +19,8 @@ class WebContents;
class WebContentsImpl;
class BrowserPluginGuest;
-class CONTENT_EXPORT WebContentsViewGuest
- : public WebContentsViewPort,
- public RenderViewHostDelegateView {
+class WebContentsViewGuest : public WebContentsView,
+ public RenderViewHostDelegateView {
public:
// The corresponding WebContentsImpl is passed in the constructor, and manages
// our lifetime. This doesn't need to be the case, but is this way currently
@@ -30,7 +29,7 @@ class CONTENT_EXPORT WebContentsViewGuest
// |platform_view|.
WebContentsViewGuest(WebContentsImpl* web_contents,
BrowserPluginGuest* guest,
- scoped_ptr<WebContentsViewPort> platform_view,
+ scoped_ptr<WebContentsView> platform_view,
RenderViewHostDelegateView* platform_view_delegate_view);
virtual ~WebContentsViewGuest();
@@ -38,14 +37,16 @@ class CONTENT_EXPORT WebContentsViewGuest
void OnGuestInitialized(WebContentsView* parent_view);
- // WebContentsView implementation --------------------------------------------
+ // Converts the guest specific coordinates in |params| to embedder specific
+ // ones.
+ ContextMenuParams ConvertContextMenuParams(
+ const ContextMenuParams& params) const;
+ // WebContentsView implementation --------------------------------------------
virtual gfx::NativeView GetNativeView() const OVERRIDE;
virtual gfx::NativeView GetContentNativeView() const OVERRIDE;
virtual gfx::NativeWindow GetTopLevelNativeWindow() const OVERRIDE;
virtual void GetContainerBounds(gfx::Rect* out) const OVERRIDE;
- virtual void OnTabCrashed(base::TerminationStatus status,
- int error_code) OVERRIDE;
virtual void SizeContents(const gfx::Size& size) OVERRIDE;
virtual void Focus() OVERRIDE;
virtual void SetInitialFocus() OVERRIDE;
@@ -53,39 +54,29 @@ class CONTENT_EXPORT WebContentsViewGuest
virtual void RestoreFocus() OVERRIDE;
virtual DropData* GetDropData() const OVERRIDE;
virtual gfx::Rect GetViewBounds() const OVERRIDE;
-#if defined(OS_MACOSX)
- virtual void SetAllowOverlappingViews(bool overlapping) OVERRIDE;
- virtual bool GetAllowOverlappingViews() const OVERRIDE;
- virtual void SetOverlayView(WebContentsView* overlay,
- const gfx::Point& offset) OVERRIDE;
- virtual void RemoveOverlayView() OVERRIDE;
-#endif
-
- // WebContentsViewPort implementation ----------------------------------------
virtual void CreateView(const gfx::Size& initial_size,
gfx::NativeView context) OVERRIDE;
- virtual RenderWidgetHostView* CreateViewForWidget(
+ virtual RenderWidgetHostViewBase* CreateViewForWidget(
RenderWidgetHost* render_widget_host) OVERRIDE;
- virtual RenderWidgetHostView* CreateViewForPopupWidget(
+ virtual RenderWidgetHostViewBase* CreateViewForPopupWidget(
RenderWidgetHost* render_widget_host) OVERRIDE;
virtual void SetPageTitle(const base::string16& title) OVERRIDE;
virtual void RenderViewCreated(RenderViewHost* host) OVERRIDE;
virtual void RenderViewSwappedIn(RenderViewHost* host) OVERRIDE;
virtual void SetOverscrollControllerEnabled(bool enabled) OVERRIDE;
#if defined(OS_MACOSX)
+ virtual void SetAllowOverlappingViews(bool overlapping) OVERRIDE;
+ virtual bool GetAllowOverlappingViews() const OVERRIDE;
+ virtual void SetOverlayView(WebContentsView* overlay,
+ const gfx::Point& offset) OVERRIDE;
+ virtual void RemoveOverlayView() OVERRIDE;
virtual bool IsEventTracking() const OVERRIDE;
virtual void CloseTabAfterEventTracking() OVERRIDE;
#endif
// Backend implementation of RenderViewHostDelegateView.
- virtual void ShowContextMenu(const ContextMenuParams& params) OVERRIDE;
- virtual void ShowPopupMenu(const gfx::Rect& bounds,
- int item_height,
- double item_font_size,
- int selected_item,
- const std::vector<MenuItem>& items,
- bool right_aligned,
- bool allow_multiple_selection) OVERRIDE;
+ virtual void ShowContextMenu(RenderFrameHost* render_frame_host,
+ const ContextMenuParams& params) OVERRIDE;
virtual void StartDragging(const DropData& drop_data,
blink::WebDragOperationsMask allowed_ops,
const gfx::ImageSkia& image,
@@ -101,7 +92,7 @@ class CONTENT_EXPORT WebContentsViewGuest
BrowserPluginGuest* guest_;
// The platform dependent view backing this WebContentsView.
// Calls to this WebContentsViewGuest are forwarded to |platform_view_|.
- scoped_ptr<WebContentsViewPort> platform_view_;
+ scoped_ptr<WebContentsView> platform_view_;
gfx::Size size_;
// Delegate view for guest's platform view.
diff --git a/chromium/content/browser/web_contents/web_contents_view_mac.h b/chromium/content/browser/web_contents/web_contents_view_mac.h
index 0dadbad9ebe..c6bc9dac7b8 100644
--- a/chromium/content/browser/web_contents/web_contents_view_mac.h
+++ b/chromium/content/browser/web_contents/web_contents_view_mac.h
@@ -12,10 +12,10 @@
#include "base/mac/scoped_nsobject.h"
#include "base/memory/scoped_ptr.h"
+#include "content/browser/renderer_host/render_view_host_delegate_view.h"
+#include "content/browser/web_contents/web_contents_view.h"
#include "content/common/content_export.h"
#include "content/common/drag_event_source_info.h"
-#include "content/port/browser/render_view_host_delegate_view.h"
-#include "content/port/browser/web_contents_view_port.h"
#include "ui/base/cocoa/base_view.h"
#include "ui/gfx/size.h"
@@ -25,6 +25,7 @@ class SkBitmap;
@class WebDragSource;
namespace content {
+class PopupMenuHelper;
class WebContentsImpl;
class WebContentsViewDelegate;
class WebContentsViewMac;
@@ -54,7 +55,7 @@ namespace content {
// Mac-specific implementation of the WebContentsView. It owns an NSView that
// contains all of the contents of the tab and associated child views.
-class WebContentsViewMac : public WebContentsViewPort,
+class WebContentsViewMac : public WebContentsView,
public RenderViewHostDelegateView {
public:
// The corresponding WebContentsImpl is passed in the constructor, and manages
@@ -69,8 +70,6 @@ class WebContentsViewMac : public WebContentsViewPort,
virtual gfx::NativeView GetContentNativeView() const OVERRIDE;
virtual gfx::NativeWindow GetTopLevelNativeWindow() const OVERRIDE;
virtual void GetContainerBounds(gfx::Rect* out) const OVERRIDE;
- virtual void OnTabCrashed(base::TerminationStatus status,
- int error_code) OVERRIDE;
virtual void SizeContents(const gfx::Size& size) OVERRIDE;
virtual void Focus() OVERRIDE;
virtual void SetInitialFocus() OVERRIDE;
@@ -83,13 +82,11 @@ class WebContentsViewMac : public WebContentsViewPort,
virtual void SetOverlayView(WebContentsView* overlay,
const gfx::Point& offset) OVERRIDE;
virtual void RemoveOverlayView() OVERRIDE;
-
- // WebContentsViewPort implementation ----------------------------------------
virtual void CreateView(
const gfx::Size& initial_size, gfx::NativeView context) OVERRIDE;
- virtual RenderWidgetHostView* CreateViewForWidget(
+ virtual RenderWidgetHostViewBase* CreateViewForWidget(
RenderWidgetHost* render_widget_host) OVERRIDE;
- virtual RenderWidgetHostView* CreateViewForPopupWidget(
+ virtual RenderWidgetHostViewBase* CreateViewForPopupWidget(
RenderWidgetHost* render_widget_host) OVERRIDE;
virtual void SetPageTitle(const base::string16& title) OVERRIDE;
virtual void RenderViewCreated(RenderViewHost* host) OVERRIDE;
@@ -99,7 +96,8 @@ class WebContentsViewMac : public WebContentsViewPort,
virtual void CloseTabAfterEventTracking() OVERRIDE;
// Backend implementation of RenderViewHostDelegateView.
- virtual void ShowContextMenu(const ContextMenuParams& params) OVERRIDE;
+ virtual void ShowContextMenu(content::RenderFrameHost* render_frame_host,
+ const ContextMenuParams& params) OVERRIDE;
virtual void ShowPopupMenu(const gfx::Rect& bounds,
int item_height,
double item_font_size,
@@ -107,6 +105,7 @@ class WebContentsViewMac : public WebContentsViewPort,
const std::vector<MenuItem>& items,
bool right_aligned,
bool allow_multiple_selection) OVERRIDE;
+ virtual void HidePopupMenu() OVERRIDE;
virtual void StartDragging(const DropData& drop_data,
blink::WebDragOperationsMask allowed_operations,
const gfx::ImageSkia& image,
@@ -154,6 +153,8 @@ class WebContentsViewMac : public WebContentsViewPort,
// Underlay view has |overlay_view_| set to this view.
WebContentsViewMac* underlay_view_;
+ scoped_ptr<PopupMenuHelper> popup_menu_helper_;
+
DISALLOW_COPY_AND_ASSIGN(WebContentsViewMac);
};
diff --git a/chromium/content/browser/web_contents/web_contents_view_mac.mm b/chromium/content/browser/web_contents/web_contents_view_mac.mm
index 527ebc024d8..39e579baec2 100644
--- a/chromium/content/browser/web_contents/web_contents_view_mac.mm
+++ b/chromium/content/browser/web_contents/web_contents_view_mac.mm
@@ -68,7 +68,7 @@ COMPILE_ASSERT_MATCHING_ENUM(DragOperationEvery);
@end
namespace content {
-WebContentsViewPort* CreateWebContentsView(
+WebContentsView* CreateWebContentsView(
WebContentsImpl* web_contents,
WebContentsViewDelegate* delegate,
RenderViewHostDelegateView** render_view_host_delegate_view) {
@@ -152,21 +152,11 @@ void WebContentsViewMac::StartDragging(
offset:offset];
}
-void WebContentsViewMac::OnTabCrashed(base::TerminationStatus /* status */,
- int /* error_code */) {
-}
-
void WebContentsViewMac::SizeContents(const gfx::Size& size) {
// TODO(brettw | japhet) This is a hack and should be removed.
// See web_contents_view.h.
- gfx::Rect rect(gfx::Point(), size);
- WebContentsViewCocoa* view = cocoa_view_.get();
-
- NSPoint origin = [view frame].origin;
- NSRect frame = [view flipRectToNSRect:rect];
- frame.origin = NSMakePoint(NSMinX(frame) + origin.x,
- NSMinY(frame) + origin.y);
- [view setFrame:frame];
+ // Note(erikchen): This method has /never/ worked correctly. I've removed the
+ // previous implementation.
}
void WebContentsViewMac::Focus() {
@@ -230,7 +220,9 @@ void WebContentsViewMac::TakeFocus(bool reverse) {
}
}
-void WebContentsViewMac::ShowContextMenu(const ContextMenuParams& params) {
+void WebContentsViewMac::ShowContextMenu(
+ content::RenderFrameHost* render_frame_host,
+ const ContextMenuParams& params) {
// Allow delegates to handle the context menu operation first.
if (web_contents_->GetDelegate() &&
web_contents_->GetDelegate()->HandleContextMenu(params)) {
@@ -238,7 +230,7 @@ void WebContentsViewMac::ShowContextMenu(const ContextMenuParams& params) {
}
if (delegate())
- delegate()->ShowContextMenu(params);
+ delegate()->ShowContextMenu(render_frame_host, params);
else
DLOG(ERROR) << "Cannot show context menus without a delegate.";
}
@@ -252,10 +244,17 @@ void WebContentsViewMac::ShowPopupMenu(
const std::vector<MenuItem>& items,
bool right_aligned,
bool allow_multiple_selection) {
- PopupMenuHelper popup_menu_helper(web_contents_->GetRenderViewHost());
- popup_menu_helper.ShowPopupMenu(bounds, item_height, item_font_size,
- selected_item, items, right_aligned,
- allow_multiple_selection);
+ popup_menu_helper_.reset(
+ new PopupMenuHelper(web_contents_->GetRenderViewHost()));
+ popup_menu_helper_->ShowPopupMenu(bounds, item_height, item_font_size,
+ selected_item, items, right_aligned,
+ allow_multiple_selection);
+ popup_menu_helper_.reset();
+}
+
+void WebContentsViewMac::HidePopupMenu() {
+ if (popup_menu_helper_)
+ popup_menu_helper_->Hide();
}
gfx::Rect WebContentsViewMac::GetViewBounds() const {
@@ -334,7 +333,7 @@ void WebContentsViewMac::CreateView(
cocoa_view_.reset(view);
}
-RenderWidgetHostView* WebContentsViewMac::CreateViewForWidget(
+RenderWidgetHostViewBase* WebContentsViewMac::CreateViewForWidget(
RenderWidgetHost* render_widget_host) {
if (render_widget_host->GetView()) {
// During testing, the view will already be set up in most cases to the
@@ -343,15 +342,18 @@ RenderWidgetHostView* WebContentsViewMac::CreateViewForWidget(
// view twice), we check for the RVH Factory, which will be set when we're
// making special ones (which go along with the special views).
DCHECK(RenderViewHostFactory::has_factory());
- return render_widget_host->GetView();
+ return static_cast<RenderWidgetHostViewBase*>(
+ render_widget_host->GetView());
}
- RenderWidgetHostViewMac* view = static_cast<RenderWidgetHostViewMac*>(
- RenderWidgetHostView::CreateViewForWidget(render_widget_host));
+ RenderWidgetHostViewMac* view = new RenderWidgetHostViewMac(
+ render_widget_host);
if (delegate()) {
- NSObject<RenderWidgetHostViewMacDelegate>* rw_delegate =
- delegate()->CreateRenderWidgetHostViewDelegate(render_widget_host);
- view->SetDelegate(rw_delegate);
+ base::scoped_nsobject<NSObject<RenderWidgetHostViewMacDelegate> >
+ rw_delegate(
+ delegate()->CreateRenderWidgetHostViewDelegate(render_widget_host));
+
+ view->SetDelegate(rw_delegate.get());
}
view->SetAllowOverlappingViews(allow_overlapping_views_);
@@ -377,9 +379,9 @@ RenderWidgetHostView* WebContentsViewMac::CreateViewForWidget(
return view;
}
-RenderWidgetHostView* WebContentsViewMac::CreateViewForPopupWidget(
+RenderWidgetHostViewBase* WebContentsViewMac::CreateViewForPopupWidget(
RenderWidgetHost* render_widget_host) {
- return RenderWidgetHostViewPort::CreateViewForWidget(render_widget_host);
+ return new RenderWidgetHostViewMac(render_widget_host);
}
void WebContentsViewMac::SetPageTitle(const base::string16& title) {
@@ -560,7 +562,6 @@ void WebContentsViewMac::CloseTab() {
// Called when a drag initiated in our view moves.
- (void)draggedImage:(NSImage*)draggedImage movedTo:(NSPoint)screenPoint {
- [dragSource_ moveDragTo:screenPoint];
}
// Called when a file drag is dropped and the promised files need to be written.
@@ -624,4 +625,13 @@ void WebContentsViewMac::CloseTab() {
FocusThroughTabTraversal(direction == NSSelectingPrevious);
}
+// When the subviews require a layout, their size should be reset to the size
+// of this view. (It is possible for the size to get out of sync as an
+// optimization in preparation for an upcoming WebContentsView resize.
+// http://crbug.com/264207)
+- (void)resizeSubviewsWithOldSize:(NSSize)oldBoundsSize {
+ for (NSView* subview in self.subviews)
+ [subview setFrame:self.bounds];
+}
+
@end
diff --git a/chromium/content/browser/web_contents/web_contents_view_mac_unittest.mm b/chromium/content/browser/web_contents/web_contents_view_mac_unittest.mm
index 71f7e48dcc2..8c009eafbd8 100644
--- a/chromium/content/browser/web_contents/web_contents_view_mac_unittest.mm
+++ b/chromium/content/browser/web_contents/web_contents_view_mac_unittest.mm
@@ -7,7 +7,7 @@
#include "base/mac/scoped_nsobject.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
-#import "ui/base/test/ui_cocoa_test_helper.h"
+#import "ui/gfx/test/ui_cocoa_test_helper.h"
namespace {
diff --git a/chromium/content/browser/web_contents/web_contents_view_overscroll_animator_mac.h b/chromium/content/browser/web_contents/web_contents_view_overscroll_animator_mac.h
new file mode 100644
index 00000000000..0603a9b99e3
--- /dev/null
+++ b/chromium/content/browser/web_contents/web_contents_view_overscroll_animator_mac.h
@@ -0,0 +1,61 @@
+// 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_WEB_CONTENTS_WEB_CONTENTS_VIEW_OVERSCROLL_ANIMATOR_MAC_H_
+#define CONTENT_BROWSER_WEB_CONTENTS_WEB_CONTENTS_VIEW_OVERSCROLL_ANIMATOR_MAC_H_
+
+#import <Cocoa/Cocoa.h>
+
+namespace content {
+class WebContentsImpl;
+
+// The direction of the overscroll animations. Backwards means that the user
+// wants to navigate backwards in the navigation history. The opposite applies
+// to forwards.
+enum OverscrollAnimatorDirection {
+ OVERSCROLL_ANIMATOR_DIRECTION_BACKWARDS,
+ OVERSCROLL_ANIMATOR_DIRECTION_FORWARDS,
+};
+} // namespace content
+
+// NSViews that intend to manage the animation associated with an overscroll
+// must implement this protocol.
+@protocol WebContentsOverscrollAnimator
+// Some implementations require the WebContentsView to supply a snapshot of a
+// previous navigation state. This method determines whether the snapshot passed
+// to the overscroll animator is expected to be non-nil.
+- (BOOL)needsNavigationSnapshot;
+
+// Begin an overscroll animation. The method -needsNavigationSnapshot determines
+// whether |snapshot| can be nil.
+- (void)beginOverscrollInDirection:
+ (content::OverscrollAnimatorDirection)direction
+ navigationSnapshot:(NSImage*)snapshot;
+
+// Due to the nature of some of the overscroll animations, implementators of
+// this protocol must have control over the layout of the RenderWidgetHost's
+// NativeView. When there is no overscroll animation in progress, the
+// implementor must guarantee that the frame of the RenderWidgetHost's
+// NativeView in screen coordinates is the same as its own frame in screen
+// coordinates.
+// Due to the odd ownership cycles of the RenderWidgetHost's NativeView, it is
+// important that its presence in the NSView hierarchy is the only strong
+// reference, and that when it gets removed from the NSView hierarchy, it will
+// be dealloc'ed shortly thereafter.
+- (void)addRenderWidgetHostNativeView:(NSView*)view;
+
+// During an overscroll animation, |progress| ranges from 0 to 2, and indicates
+// how close the overscroll is to completing. If the overscroll ends with
+// |progress| >= 1, then the overscroll is considered completed.
+- (void)updateOverscrollProgress:(CGFloat)progress;
+
+// Animate the finish of the overscroll and perform a navigation. The navigation
+// may not happen synchronously, but is guaranteed to eventually occur.
+- (void)completeOverscroll:(content::WebContentsImpl*)webContents;
+
+// Animate the cancellation of the overscroll.
+- (void)cancelOverscroll;
+@end
+
+#endif // CONTENT_BROWSER_WEB_CONTENTS_WEB_CONTENTS_VIEW_OVERSCROLL_ANIMATOR_MAC_H_
diff --git a/chromium/content/browser/web_contents/web_contents_view_overscroll_animator_slider_mac.h b/chromium/content/browser/web_contents/web_contents_view_overscroll_animator_slider_mac.h
new file mode 100644
index 00000000000..2cc87ec0071
--- /dev/null
+++ b/chromium/content/browser/web_contents/web_contents_view_overscroll_animator_slider_mac.h
@@ -0,0 +1,59 @@
+// 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_WEB_CONTENTS_WEB_CONTENTS_VIEW_OVERSCROLL_ANIMATOR_SLIDER_MAC_H_
+#define CONTENT_BROWSER_WEB_CONTENTS_WEB_CONTENTS_VIEW_OVERSCROLL_ANIMATOR_SLIDER_MAC_H_
+
+#import <Cocoa/Cocoa.h>
+
+#include "base/mac/scoped_nsobject.h"
+#include "base/memory/scoped_ptr.h"
+#include "content/browser/web_contents/web_contents_view_overscroll_animator_mac.h"
+
+namespace overscroll_animator {
+class WebContentsPaintObserver;
+} // namespace overscroll_animator
+
+@interface OverscrollAnimatorSliderView
+ : NSView<WebContentsOverscrollAnimator> {
+ // This container view holds the RenderWidgetHost's NativeViews. Most of the
+ // time, its frame in screen coordinates is the same as SliderView's frame in
+ // screen coordinates. During an overscroll animation, it may temporarily be
+ // relocated, but it will return to its original position after the overscroll
+ // animation is finished.
+ base::scoped_nsobject<NSView> middleView_;
+
+ // This view is a sibling of middleView_, and is guaranteed to live below it.
+ // Most of the time, it is hidden. During a backwards overscroll animation,
+ // middleView_ is slid to the right, and bottomView_ peeks out from the
+ // original position of middleView_.
+ base::scoped_nsobject<NSImageView> bottomView_;
+
+ // This view is a sibling of middleView_, and is guaranteed to live above it.
+ // Most of the time, it is hidden. During a forwards overscroll animation,
+ // topView_ is slid to the left from off screen, its final position exactly
+ // covering middleView_.
+ base::scoped_nsobject<NSImageView> topView_;
+
+ // The direction of the current overscroll animation. This property has no
+ // meaning when inOverscroll_ is false.
+ content::OverscrollAnimatorDirection direction_;
+
+ // Indicates that this view is completing or cancelling the overscroll. This
+ // animation cannot be cancelled.
+ BOOL animating_;
+
+ // Reflects whether this view is in the process of handling an overscroll.
+ BOOL inOverscroll_;
+
+ // The most recent value passed to -updateOverscrollProgress:.
+ CGFloat progress_;
+
+ // An observer that reports the first non-empty paint of a WebContents. This
+ // is used when completing an overscroll animation.
+ scoped_ptr<overscroll_animator::WebContentsPaintObserver> observer_;
+}
+@end
+
+#endif // CONTENT_BROWSER_WEB_CONTENTS_WEB_CONTENTS_VIEW_OVERSCROLL_ANIMATOR_SLIDER_MAC_H_
diff --git a/chromium/content/browser/web_contents/web_contents_view_overscroll_animator_slider_mac.mm b/chromium/content/browser/web_contents/web_contents_view_overscroll_animator_slider_mac.mm
new file mode 100644
index 00000000000..29c84c1cdf5
--- /dev/null
+++ b/chromium/content/browser/web_contents/web_contents_view_overscroll_animator_slider_mac.mm
@@ -0,0 +1,259 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <QuartzCore/QuartzCore.h>
+
+#include "content/browser/web_contents/web_contents_view_overscroll_animator_slider_mac.h"
+
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/public/browser/web_contents_observer.h"
+
+namespace {
+// The minimum possible progress of an overscroll animation.
+CGFloat kMinProgress = 0;
+// The maximum possible progress of an overscroll animation.
+CGFloat kMaxProgress = 2.0;
+// The maximum duration of the completion or cancellation animations. The
+// effective maximum is half of this value, since the longest animation is from
+// progress = 1.0 to progress = 2.0;
+CGFloat kMaxAnimationDuration = 0.2;
+} // namespace
+
+// OverscrollAnimatorSliderView Private Category -------------------------------
+
+@interface OverscrollAnimatorSliderView ()
+// Callback from WebContentsPaintObserver.
+- (void)webContentsFinishedNonEmptyPaint;
+
+// Resets overscroll animation state.
+- (void)reset;
+
+// Given a |progress| from 0 to 2, the expected frame origin of the -movingView.
+- (NSPoint)frameOriginWithProgress:(CGFloat)progress;
+
+// The NSView that is moving during the overscroll animation.
+- (NSView*)movingView;
+
+// The expected duration of an animation from progress_ to |progress|
+- (CGFloat)animationDurationForProgress:(CGFloat)progress;
+
+// NSView override. During an overscroll animation, the cursor may no longer
+// rest on the RenderWidgetHost's NativeView, which prevents wheel events from
+// reaching the NativeView. The overscroll animation is driven by wheel events
+// so they must be explicitly forwarded to the NativeView.
+- (void)scrollWheel:(NSEvent*)event;
+@end
+
+// Helper Class (ResizingView) -------------------------------------------------
+
+// This NSView subclass is intended to be the RenderWidgetHost's NativeView's
+// parent NSView. It is possible for the RenderWidgetHost's NativeView's size to
+// become out of sync with its parent NSView. The override of
+// -resizeSubviewsWithOldSize: ensures that the sizes will eventually become
+// consistent.
+// http://crbug.com/264207
+@interface ResizingView : NSView
+@end
+
+@implementation ResizingView
+- (void)resizeSubviewsWithOldSize:(NSSize)oldBoundsSize {
+ for (NSView* subview in self.subviews)
+ [subview setFrame:self.bounds];
+}
+@end
+
+// Helper Class (WebContentsPaintObserver) -------------------------------------
+
+namespace overscroll_animator {
+class WebContentsPaintObserver : public content::WebContentsObserver {
+ public:
+ WebContentsPaintObserver(content::WebContents* web_contents,
+ OverscrollAnimatorSliderView* slider_view)
+ : WebContentsObserver(web_contents), slider_view_(slider_view) {}
+
+ virtual void DidFirstVisuallyNonEmptyPaint() OVERRIDE {
+ [slider_view_ webContentsFinishedNonEmptyPaint];
+ }
+
+ private:
+ OverscrollAnimatorSliderView* slider_view_; // Weak reference.
+};
+} // namespace overscroll_animator
+
+// OverscrollAnimatorSliderView Implementation ---------------------------------
+
+@implementation OverscrollAnimatorSliderView
+
+- (instancetype)initWithFrame:(NSRect)frame {
+ self = [super initWithFrame:frame];
+ if (self) {
+ bottomView_.reset([[NSImageView alloc] initWithFrame:self.bounds]);
+ bottomView_.get().imageScaling = NSImageScaleNone;
+ bottomView_.get().autoresizingMask =
+ NSViewWidthSizable | NSViewHeightSizable;
+ bottomView_.get().imageAlignment = NSImageAlignTop;
+ [self addSubview:bottomView_];
+ middleView_.reset([[ResizingView alloc] initWithFrame:self.bounds]);
+ middleView_.get().autoresizingMask =
+ NSViewWidthSizable | NSViewHeightSizable;
+ [self addSubview:middleView_];
+ topView_.reset([[NSImageView alloc] initWithFrame:self.bounds]);
+ topView_.get().autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
+ topView_.get().imageScaling = NSImageScaleNone;
+ topView_.get().imageAlignment = NSImageAlignTop;
+ [self addSubview:topView_];
+
+ [self reset];
+ }
+ return self;
+}
+
+- (void)webContentsFinishedNonEmptyPaint {
+ observer_.reset();
+ [self reset];
+}
+
+- (void)reset {
+ DCHECK(!animating_);
+ inOverscroll_ = NO;
+ progress_ = kMinProgress;
+
+ [CATransaction begin];
+ [CATransaction setDisableActions:YES];
+ bottomView_.get().hidden = YES;
+ middleView_.get().hidden = NO;
+ topView_.get().hidden = YES;
+
+ [bottomView_ setFrameOrigin:NSMakePoint(0, 0)];
+ [middleView_ setFrameOrigin:NSMakePoint(0, 0)];
+ [topView_ setFrameOrigin:NSMakePoint(0, 0)];
+ [CATransaction commit];
+}
+
+- (NSPoint)frameOriginWithProgress:(CGFloat)progress {
+ if (direction_ == content::OVERSCROLL_ANIMATOR_DIRECTION_BACKWARDS)
+ return NSMakePoint(progress / kMaxProgress * self.bounds.size.width, 0);
+ return NSMakePoint((1 - progress / kMaxProgress) * self.bounds.size.width, 0);
+}
+
+- (NSView*)movingView {
+ if (direction_ == content::OVERSCROLL_ANIMATOR_DIRECTION_BACKWARDS)
+ return middleView_;
+ return topView_;
+}
+
+- (CGFloat)animationDurationForProgress:(CGFloat)progress {
+ CGFloat progressPercentage =
+ fabs(progress_ - progress) / (kMaxProgress - kMinProgress);
+ return progressPercentage * kMaxAnimationDuration;
+}
+
+- (void)scrollWheel:(NSEvent*)event {
+ NSView* latestRenderWidgetHostView = [[middleView_ subviews] lastObject];
+ [latestRenderWidgetHostView scrollWheel:event];
+}
+
+// WebContentsOverscrollAnimator Implementation --------------------------------
+
+- (BOOL)needsNavigationSnapshot {
+ return YES;
+}
+
+- (void)beginOverscrollInDirection:
+ (content::OverscrollAnimatorDirection)direction
+ navigationSnapshot:(NSImage*)snapshot {
+ // TODO(erikchen): If snapshot is nil, need a placeholder.
+ if (animating_ || inOverscroll_)
+ return;
+
+ inOverscroll_ = YES;
+ direction_ = direction;
+ if (direction_ == content::OVERSCROLL_ANIMATOR_DIRECTION_BACKWARDS) {
+ // The middleView_ will slide to the right, revealing bottomView_.
+ bottomView_.get().hidden = NO;
+ [bottomView_ setImage:snapshot];
+ } else {
+ // The topView_ will slide in from the right, concealing middleView_.
+ topView_.get().hidden = NO;
+ [topView_ setFrameOrigin:NSMakePoint(self.bounds.size.width, 0)];
+ [topView_ setImage:snapshot];
+ }
+
+ [self updateOverscrollProgress:kMinProgress];
+}
+
+- (void)addRenderWidgetHostNativeView:(NSView*)view {
+ [middleView_ addSubview:view];
+}
+
+- (void)updateOverscrollProgress:(CGFloat)progress {
+ if (animating_)
+ return;
+ DCHECK_LE(progress, kMaxProgress);
+ DCHECK_GE(progress, kMinProgress);
+ progress_ = progress;
+ [[self movingView] setFrameOrigin:[self frameOriginWithProgress:progress]];
+}
+
+- (void)completeOverscroll:(content::WebContentsImpl*)webContents {
+ if (animating_ || !inOverscroll_)
+ return;
+
+ animating_ = YES;
+
+ NSView* view = [self movingView];
+ [NSAnimationContext beginGrouping];
+ [NSAnimationContext currentContext].duration =
+ [self animationDurationForProgress:kMaxProgress];
+ [[NSAnimationContext currentContext] setCompletionHandler:^{
+ animating_ = NO;
+
+ // Animation is complete. Now perform page load.
+ if (direction_ == content::OVERSCROLL_ANIMATOR_DIRECTION_BACKWARDS)
+ webContents->GetController().GoBack();
+ else
+ webContents->GetController().GoForward();
+
+ // Reset the position of the middleView_, but wait for the page to paint
+ // before showing it.
+ middleView_.get().hidden = YES;
+ [middleView_ setFrameOrigin:NSMakePoint(0, 0)];
+ observer_.reset(
+ new overscroll_animator::WebContentsPaintObserver(webContents, self));
+ }];
+
+ // Animate the moving view to its final position.
+ [[view animator] setFrameOrigin:[self frameOriginWithProgress:kMaxProgress]];
+
+ [NSAnimationContext endGrouping];
+}
+
+- (void)cancelOverscroll {
+ if (animating_)
+ return;
+
+ if (!inOverscroll_) {
+ [self reset];
+ return;
+ }
+
+ animating_ = YES;
+
+ NSView* view = [self movingView];
+ [NSAnimationContext beginGrouping];
+ [NSAnimationContext currentContext].duration =
+ [self animationDurationForProgress:kMinProgress];
+ [[NSAnimationContext currentContext] setCompletionHandler:^{
+ // Animation is complete. Reset the state.
+ animating_ = NO;
+ [self reset];
+ }];
+
+ // Animate the moving view to its initial position.
+ [[view animator] setFrameOrigin:[self frameOriginWithProgress:kMinProgress]];
+
+ [NSAnimationContext endGrouping];
+}
+
+@end
diff --git a/chromium/content/browser/web_contents/web_contents_view_win.cc b/chromium/content/browser/web_contents/web_contents_view_win.cc
deleted file mode 100644
index 8360d8c5f59..00000000000
--- a/chromium/content/browser/web_contents/web_contents_view_win.cc
+++ /dev/null
@@ -1,465 +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/web_contents/web_contents_view_win.h"
-
-#include "base/bind.h"
-#include "base/memory/scoped_vector.h"
-#include "content/browser/frame_host/interstitial_page_impl.h"
-#include "content/browser/renderer_host/render_view_host_factory.h"
-#include "content/browser/renderer_host/render_view_host_impl.h"
-#include "content/browser/renderer_host/render_widget_host_view_win.h"
-#include "content/browser/web_contents/web_contents_drag_win.h"
-#include "content/browser/web_contents/web_contents_impl.h"
-#include "content/browser/web_contents/web_drag_dest_win.h"
-#include "content/public/browser/web_contents_delegate.h"
-#include "content/public/browser/web_contents_view_delegate.h"
-#include "ui/base/win/hidden_window.h"
-#include "ui/base/win/hwnd_subclass.h"
-#include "ui/gfx/screen.h"
-
-namespace content {
-WebContentsViewPort* CreateWebContentsView(
- WebContentsImpl* web_contents,
- WebContentsViewDelegate* delegate,
- RenderViewHostDelegateView** render_view_host_delegate_view) {
- WebContentsViewWin* rv = new WebContentsViewWin(web_contents, delegate);
- *render_view_host_delegate_view = rv;
- return rv;
-}
-
-namespace {
-
-typedef std::map<HWND, WebContentsViewWin*> HwndToWcvMap;
-HwndToWcvMap hwnd_to_wcv_map;
-
-void RemoveHwndToWcvMapEntry(WebContentsViewWin* wcv) {
- HwndToWcvMap::iterator it;
- for (it = hwnd_to_wcv_map.begin(); it != hwnd_to_wcv_map.end();) {
- if (it->second == wcv)
- hwnd_to_wcv_map.erase(it++);
- else
- ++it;
- }
-}
-
-BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lParam) {
- HwndToWcvMap::iterator it = hwnd_to_wcv_map.find(hwnd);
- if (it == hwnd_to_wcv_map.end())
- return TRUE; // must return TRUE to continue enumeration.
- WebContentsViewWin* wcv = it->second;
- RenderWidgetHostViewWin* rwhv = static_cast<RenderWidgetHostViewWin*>(
- wcv->web_contents()->GetRenderWidgetHostView());
- if (rwhv)
- rwhv->UpdateScreenInfo(rwhv->GetNativeView());
-
- return TRUE; // must return TRUE to continue enumeration.
-}
-
-class PositionChangedMessageFilter : public ui::HWNDMessageFilter {
- public:
- PositionChangedMessageFilter() {}
-
- private:
- // Overridden from ui::HWNDMessageFilter:
- virtual bool FilterMessage(HWND hwnd,
- UINT message,
- WPARAM w_param,
- LPARAM l_param,
- LRESULT* l_result) OVERRIDE {
- if (message == WM_WINDOWPOSCHANGED || message == WM_SETTINGCHANGE)
- EnumChildWindows(hwnd, EnumChildProc, 0);
-
- return false;
- }
-
- DISALLOW_COPY_AND_ASSIGN(PositionChangedMessageFilter);
-};
-
-void AddFilterToParentHwndSubclass(HWND hwnd, ui::HWNDMessageFilter* filter) {
- HWND parent = ::GetAncestor(hwnd, GA_ROOT);
- if (parent) {
- ui::HWNDSubclass::RemoveFilterFromAllTargets(filter);
- ui::HWNDSubclass::AddFilterToTarget(parent, filter);
- }
-}
-
-} // namespace namespace
-
-WebContentsViewWin::WebContentsViewWin(WebContentsImpl* web_contents,
- WebContentsViewDelegate* delegate)
- : web_contents_(web_contents),
- delegate_(delegate),
- hwnd_message_filter_(new PositionChangedMessageFilter) {
-}
-
-WebContentsViewWin::~WebContentsViewWin() {
- RemoveHwndToWcvMapEntry(this);
-
- if (IsWindow(hwnd()))
- DestroyWindow(hwnd());
-}
-
-gfx::NativeView WebContentsViewWin::GetNativeView() const {
- return hwnd();
-}
-
-gfx::NativeView WebContentsViewWin::GetContentNativeView() const {
- RenderWidgetHostView* rwhv = web_contents_->GetRenderWidgetHostView();
- return rwhv ? rwhv->GetNativeView() : NULL;
-}
-
-gfx::NativeWindow WebContentsViewWin::GetTopLevelNativeWindow() const {
- return ::GetAncestor(GetNativeView(), GA_ROOT);
-}
-
-void WebContentsViewWin::GetContainerBounds(gfx::Rect *out) const {
- // Copied from NativeWidgetWin::GetClientAreaScreenBounds().
- RECT r;
- GetClientRect(hwnd(), &r);
- POINT point = { r.left, r.top };
- ClientToScreen(hwnd(), &point);
- *out = gfx::Rect(point.x, point.y, r.right - r.left, r.bottom - r.top);
-}
-
-void WebContentsViewWin::OnTabCrashed(base::TerminationStatus status,
- int error_code) {
-}
-
-void WebContentsViewWin::SizeContents(const gfx::Size& size) {
- gfx::Rect bounds;
- GetContainerBounds(&bounds);
- if (bounds.size() != size) {
- SetWindowPos(hwnd(), NULL, 0, 0, size.width(), size.height(),
- SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE);
- } else {
- // Our size matches what we want but the renderers size may not match.
- // Pretend we were resized so that the renderers size is updated too.
- if (web_contents_->GetInterstitialPage())
- web_contents_->GetInterstitialPage()->SetSize(size);
- RenderWidgetHostView* rwhv = web_contents_->GetRenderWidgetHostView();
- if (rwhv)
- rwhv->SetSize(size);
- }
-}
-
-void WebContentsViewWin::CreateView(
- const gfx::Size& initial_size, gfx::NativeView context) {
- initial_size_ = initial_size;
-
- set_window_style(WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
-
- Init(ui::GetHiddenWindow(), gfx::Rect(initial_size_));
-
- // Remove the root view drop target so we can register our own.
- RevokeDragDrop(GetNativeView());
- drag_dest_ = new WebDragDest(hwnd(), web_contents_);
- if (delegate_) {
- WebDragDestDelegate* delegate = delegate_->GetDragDestDelegate();
- if (delegate)
- drag_dest_->set_delegate(delegate);
- }
-}
-
-void WebContentsViewWin::Focus() {
- if (web_contents_->GetInterstitialPage()) {
- web_contents_->GetInterstitialPage()->Focus();
- return;
- }
-
- if (delegate_.get() && delegate_->Focus())
- return;
-
- RenderWidgetHostView* rwhv = web_contents_->GetRenderWidgetHostView();
- if (rwhv)
- rwhv->Focus();
-}
-
-void WebContentsViewWin::SetInitialFocus() {
- if (web_contents_->FocusLocationBarByDefault())
- web_contents_->SetFocusToLocationBar(false);
- else
- Focus();
-}
-
-void WebContentsViewWin::StoreFocus() {
- if (delegate_)
- delegate_->StoreFocus();
-}
-
-void WebContentsViewWin::RestoreFocus() {
- if (delegate_)
- delegate_->RestoreFocus();
-}
-
-DropData* WebContentsViewWin::GetDropData() const {
- return drag_dest_->current_drop_data();
-}
-
-gfx::Rect WebContentsViewWin::GetViewBounds() const {
- RECT r;
- GetWindowRect(hwnd(), &r);
- return gfx::Rect(r);
-}
-
-RenderWidgetHostView* WebContentsViewWin::CreateViewForWidget(
- RenderWidgetHost* render_widget_host) {
- if (render_widget_host->GetView()) {
- // During testing, the view will already be set up in most cases to the
- // test view, so we don't want to clobber it with a real one. To verify that
- // this actually is happening (and somebody isn't accidentally creating the
- // view twice), we check for the RVH Factory, which will be set when we're
- // making special ones (which go along with the special views).
- DCHECK(RenderViewHostFactory::has_factory());
- return render_widget_host->GetView();
- }
-
- RenderWidgetHostViewWin* view = static_cast<RenderWidgetHostViewWin*>(
- RenderWidgetHostView::CreateViewForWidget(render_widget_host));
- view->CreateWnd(GetNativeView());
- view->ShowWindow(SW_SHOW);
- view->SetSize(initial_size_);
- return view;
-}
-
-RenderWidgetHostView* WebContentsViewWin::CreateViewForPopupWidget(
- RenderWidgetHost* render_widget_host) {
- return RenderWidgetHostViewPort::CreateViewForWidget(render_widget_host);
-}
-
-void WebContentsViewWin::SetPageTitle(const base::string16& title) {
- // It's possible to get this after the hwnd has been destroyed.
- if (GetNativeView())
- ::SetWindowText(GetNativeView(), title.c_str());
-}
-
-void WebContentsViewWin::RenderViewCreated(RenderViewHost* host) {
-}
-
-void WebContentsViewWin::RenderViewSwappedIn(RenderViewHost* host) {
-}
-
-void WebContentsViewWin::SetOverscrollControllerEnabled(bool enabled) {
-}
-
-void WebContentsViewWin::ShowContextMenu(const ContextMenuParams& params) {
- if (delegate_)
- delegate_->ShowContextMenu(params);
- // WARNING: this may have been deleted.
-}
-
-void WebContentsViewWin::ShowPopupMenu(const gfx::Rect& bounds,
- int item_height,
- double item_font_size,
- int selected_item,
- const std::vector<MenuItem>& items,
- bool right_aligned,
- bool allow_multiple_selection) {
- // External popup menus are only used on Mac and Android.
- NOTIMPLEMENTED();
-}
-
-void WebContentsViewWin::StartDragging(const DropData& drop_data,
- blink::WebDragOperationsMask operations,
- const gfx::ImageSkia& image,
- const gfx::Vector2d& image_offset,
- const DragEventSourceInfo& event_info) {
- drag_handler_ = new WebContentsDragWin(
- GetNativeView(),
- web_contents_,
- drag_dest_,
- base::Bind(&WebContentsViewWin::EndDragging, base::Unretained(this)));
- drag_handler_->StartDragging(drop_data, operations, image, image_offset);
-}
-
-void WebContentsViewWin::UpdateDragCursor(blink::WebDragOperation operation) {
- drag_dest_->set_drag_cursor(operation);
-}
-
-void WebContentsViewWin::GotFocus() {
- if (web_contents_->GetDelegate())
- web_contents_->GetDelegate()->WebContentsFocused(web_contents_);
-}
-
-void WebContentsViewWin::TakeFocus(bool reverse) {
- if (web_contents_->GetDelegate() &&
- !web_contents_->GetDelegate()->TakeFocus(web_contents_, reverse) &&
- delegate_.get()) {
- delegate_->TakeFocus(reverse);
- }
-}
-
-void WebContentsViewWin::EndDragging() {
- drag_handler_ = NULL;
- web_contents_->SystemDragEnded();
-}
-
-void WebContentsViewWin::CloseTab() {
- RenderViewHost* rvh = web_contents_->GetRenderViewHost();
- rvh->GetDelegate()->Close(rvh);
-}
-
-LRESULT WebContentsViewWin::OnCreate(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) {
- hwnd_to_wcv_map.insert(std::make_pair(hwnd(), this));
- AddFilterToParentHwndSubclass(hwnd(), hwnd_message_filter_.get());
- return 0;
-}
-
-LRESULT WebContentsViewWin::OnDestroy(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) {
- if (drag_dest_) {
- RevokeDragDrop(GetNativeView());
- drag_dest_ = NULL;
- }
- if (drag_handler_) {
- drag_handler_->CancelDrag();
- drag_handler_ = NULL;
- }
- return 0;
-}
-
-LRESULT WebContentsViewWin::OnWindowPosChanged(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) {
-
- // Our parent might have changed. So we re-install our hwnd message filter.
- AddFilterToParentHwndSubclass(hwnd(), hwnd_message_filter_.get());
-
- WINDOWPOS* window_pos = reinterpret_cast<WINDOWPOS*>(lparam);
- if (window_pos->flags & SWP_HIDEWINDOW) {
- web_contents_->WasHidden();
- return 0;
- }
-
- // The WebContents was shown by a means other than the user selecting a
- // Tab, e.g. the window was minimized then restored.
- if (window_pos->flags & SWP_SHOWWINDOW)
- web_contents_->WasShown();
-
- RenderWidgetHostView* rwhv = web_contents_->GetRenderWidgetHostView();
- if (rwhv) {
- RenderWidgetHostViewWin* view = static_cast<RenderWidgetHostViewWin*>(rwhv);
- view->UpdateScreenInfo(view->GetNativeView());
- }
-
- // Unless we were specifically told not to size, cause the renderer to be
- // sized to the new bounds, which forces a repaint. Not required for the
- // simple minimize-restore case described above, for example, since the
- // size hasn't changed.
- if (window_pos->flags & SWP_NOSIZE)
- return 0;
-
- gfx::Size size(window_pos->cx, window_pos->cy);
- if (web_contents_->GetInterstitialPage())
- web_contents_->GetInterstitialPage()->SetSize(size);
- if (rwhv)
- rwhv->SetSize(size);
-
- if (delegate_)
- delegate_->SizeChanged(size);
-
- return 0;
-}
-
-LRESULT WebContentsViewWin::OnMouseDown(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) {
- // Make sure this WebContents is activated when it is clicked on.
- if (web_contents_->GetDelegate())
- web_contents_->GetDelegate()->ActivateContents(web_contents_);
- return 0;
-}
-
-LRESULT WebContentsViewWin::OnMouseMove(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) {
- // Let our delegate know that the mouse moved (useful for resetting status
- // bubble state).
- if (web_contents_->GetDelegate()) {
- web_contents_->GetDelegate()->ContentsMouseEvent(
- web_contents_,
- gfx::Screen::GetNativeScreen()->GetCursorScreenPoint(),
- true);
- }
- return 0;
-}
-
-LRESULT WebContentsViewWin::OnNCCalcSize(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) {
- // Hack for ThinkPad mouse wheel driver. We have set the fake scroll bars
- // to receive scroll messages from ThinkPad touch-pad driver. Suppress
- // painting of scrollbars by returning 0 size for them.
- return 0;
-}
-
-LRESULT WebContentsViewWin::OnNCHitTest(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) {
- return HTTRANSPARENT;
-}
-
-LRESULT WebContentsViewWin::OnScroll(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) {
- int scroll_type = LOWORD(wparam);
- short position = HIWORD(wparam);
- HWND scrollbar = reinterpret_cast<HWND>(lparam);
- // This window can receive scroll events as a result of the ThinkPad's
- // touch-pad scroll wheel emulation.
- // If ctrl is held, zoom the UI. There are three issues with this:
- // 1) Should the event be eaten or forwarded to content? We eat the event,
- // which is like Firefox and unlike IE.
- // 2) Should wheel up zoom in or out? We zoom in (increase font size), which
- // is like IE and Google maps, but unlike Firefox.
- // 3) Should the mouse have to be over the content area? We zoom as long as
- // content has focus, although FF and IE require that the mouse is over
- // content. This is because all events get forwarded when content has
- // focus.
- if (GetAsyncKeyState(VK_CONTROL) & 0x8000) {
- int distance = 0;
- switch (scroll_type) {
- case SB_LINEUP:
- distance = WHEEL_DELTA;
- break;
- case SB_LINEDOWN:
- distance = -WHEEL_DELTA;
- break;
- // TODO(joshia): Handle SB_PAGEUP, SB_PAGEDOWN, SB_THUMBPOSITION,
- // and SB_THUMBTRACK for completeness
- default:
- break;
- }
-
- web_contents_->GetDelegate()->ContentsZoomChange(distance > 0);
- return 0;
- }
-
- // Reflect scroll message to the view() to give it a chance
- // to process scrolling.
- SendMessage(GetContentNativeView(), message, wparam, lparam);
- return 0;
-}
-
-LRESULT WebContentsViewWin::OnSize(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) {
- // NOTE: Because we handle OnWindowPosChanged without calling DefWindowProc,
- // OnSize is NOT called on window resize. This handler is called only once
- // when the window is created.
- // Don't call base class OnSize to avoid useless layout for 0x0 size.
- // We will get OnWindowPosChanged later and layout root view in WasSized.
-
- // Hack for ThinkPad touch-pad driver.
- // Set fake scrollbars so that we can get scroll messages,
- SCROLLINFO si = {0};
- si.cbSize = sizeof(si);
- si.fMask = SIF_ALL;
-
- si.nMin = 1;
- si.nMax = 100;
- si.nPage = 10;
- si.nPos = 50;
-
- ::SetScrollInfo(hwnd(), SB_HORZ, &si, FALSE);
- ::SetScrollInfo(hwnd(), SB_VERT, &si, FALSE);
-
- return 1;
-}
-
-} // namespace content
diff --git a/chromium/content/browser/web_contents/web_contents_view_win.h b/chromium/content/browser/web_contents/web_contents_view_win.h
deleted file mode 100644
index 9b052e20a2b..00000000000
--- a/chromium/content/browser/web_contents/web_contents_view_win.h
+++ /dev/null
@@ -1,147 +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_WEB_CONTENTS_WEB_CONTENTS_VIEW_WIN_H_
-#define CONTENT_BROWSER_WEB_CONTENTS_WEB_CONTENTS_VIEW_WIN_H_
-
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/timer/timer.h"
-#include "base/win/win_util.h"
-#include "content/common/content_export.h"
-#include "content/common/drag_event_source_info.h"
-#include "content/port/browser/render_view_host_delegate_view.h"
-#include "content/port/browser/web_contents_view_port.h"
-#include "ui/gfx/win/window_impl.h"
-
-namespace ui {
-class HWNDMessageFilter;
-}
-
-namespace content {
-class WebContentsDragWin;
-class WebContentsImpl;
-class WebContentsViewDelegate;
-class WebDragDest;
-
-// An implementation of WebContentsView for Windows.
-class CONTENT_EXPORT WebContentsViewWin
- : public WebContentsViewPort,
- public RenderViewHostDelegateView,
- public gfx::WindowImpl {
- public:
- WebContentsViewWin(WebContentsImpl* web_contents,
- WebContentsViewDelegate* delegate);
- virtual ~WebContentsViewWin();
-
- BEGIN_MSG_MAP_EX(WebContentsViewWin)
- MESSAGE_HANDLER(WM_CREATE, OnCreate)
- MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
- MESSAGE_HANDLER(WM_WINDOWPOSCHANGED, OnWindowPosChanged)
- MESSAGE_HANDLER(WM_LBUTTONDOWN, OnMouseDown)
- MESSAGE_HANDLER(WM_MBUTTONDOWN, OnMouseDown)
- MESSAGE_HANDLER(WM_RBUTTONDOWN, OnMouseDown)
- MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
- // Hacks for old ThinkPad touchpads/scroll points.
- MESSAGE_HANDLER(WM_NCCALCSIZE, OnNCCalcSize)
- MESSAGE_HANDLER(WM_NCHITTEST, OnNCHitTest)
- MESSAGE_HANDLER(WM_HSCROLL, OnScroll)
- MESSAGE_HANDLER(WM_VSCROLL, OnScroll)
- MESSAGE_HANDLER(WM_SIZE, OnSize)
- END_MSG_MAP()
-
- // Overridden from WebContentsView:
- virtual gfx::NativeView GetNativeView() const OVERRIDE;
- virtual gfx::NativeView GetContentNativeView() const OVERRIDE;
- virtual gfx::NativeWindow GetTopLevelNativeWindow() const OVERRIDE;
- virtual void GetContainerBounds(gfx::Rect *out) const OVERRIDE;
- virtual void OnTabCrashed(base::TerminationStatus status,
- int error_code) OVERRIDE;
- virtual void SizeContents(const gfx::Size& size) OVERRIDE;
- virtual void Focus() OVERRIDE;
- virtual void SetInitialFocus() OVERRIDE;
- virtual void StoreFocus() OVERRIDE;
- virtual void RestoreFocus() OVERRIDE;
- virtual DropData* GetDropData() const OVERRIDE;
- virtual gfx::Rect GetViewBounds() const OVERRIDE;
-
- // Overridden from WebContentsViewPort:
- virtual void CreateView(
- const gfx::Size& initial_size, gfx::NativeView context) OVERRIDE;
- virtual RenderWidgetHostView* CreateViewForWidget(
- RenderWidgetHost* render_widget_host) OVERRIDE;
- virtual RenderWidgetHostView* CreateViewForPopupWidget(
- RenderWidgetHost* render_widget_host) OVERRIDE;
- virtual void SetPageTitle(const base::string16& title) OVERRIDE;
- virtual void RenderViewCreated(RenderViewHost* host) OVERRIDE;
- virtual void RenderViewSwappedIn(RenderViewHost* host) OVERRIDE;
- virtual void SetOverscrollControllerEnabled(bool enabled) OVERRIDE;
-
- // Implementation of RenderViewHostDelegateView.
- virtual void ShowContextMenu(const ContextMenuParams& params) OVERRIDE;
- virtual void ShowPopupMenu(const gfx::Rect& bounds,
- int item_height,
- double item_font_size,
- int selected_item,
- const std::vector<MenuItem>& items,
- bool right_aligned,
- bool allow_multiple_selection) OVERRIDE;
- virtual void StartDragging(const DropData& drop_data,
- blink::WebDragOperationsMask operations,
- const gfx::ImageSkia& image,
- const gfx::Vector2d& image_offset,
- const DragEventSourceInfo& event_info) OVERRIDE;
- virtual void UpdateDragCursor(blink::WebDragOperation operation) OVERRIDE;
- virtual void GotFocus() OVERRIDE;
- virtual void TakeFocus(bool reverse) OVERRIDE;
-
- WebContentsImpl* web_contents() const { return web_contents_; }
-
- private:
- void EndDragging();
- void CloseTab();
-
- LRESULT OnCreate(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled);
- LRESULT OnDestroy(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled);
- LRESULT OnWindowPosChanged(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled);
- LRESULT OnMouseDown(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled);
- LRESULT OnMouseMove(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled);
- LRESULT OnReflectedMessage(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled);
- LRESULT OnNCCalcSize(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled);
- LRESULT OnNCHitTest(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled);
- LRESULT OnScroll(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled);
- LRESULT OnSize(
- UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled);
-
- gfx::Size initial_size_;
-
- // The WebContentsImpl whose contents we display.
- WebContentsImpl* web_contents_;
-
- scoped_ptr<WebContentsViewDelegate> delegate_;
-
- // The helper object that handles drag destination related interactions with
- // Windows.
- scoped_refptr<WebDragDest> drag_dest_;
-
- // Used to handle the drag-and-drop.
- scoped_refptr<WebContentsDragWin> drag_handler_;
-
- scoped_ptr<ui::HWNDMessageFilter> hwnd_message_filter_;
-
- DISALLOW_COPY_AND_ASSIGN(WebContentsViewWin);
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_WEB_CONTENTS_WEB_CONTENTS_VIEW_WIN_H_
diff --git a/chromium/content/browser/web_contents/web_drag_dest_gtk.cc b/chromium/content/browser/web_contents/web_drag_dest_gtk.cc
deleted file mode 100644
index 738d6b59a80..00000000000
--- a/chromium/content/browser/web_contents/web_drag_dest_gtk.cc
+++ /dev/null
@@ -1,339 +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/web_contents/web_drag_dest_gtk.h"
-
-#include <string>
-
-#include "base/bind.h"
-#include "base/files/file_path.h"
-#include "base/message_loop/message_loop.h"
-#include "base/strings/utf_string_conversions.h"
-#include "content/browser/renderer_host/render_view_host_impl.h"
-#include "content/browser/web_contents/drag_utils_gtk.h"
-#include "content/browser/web_contents/web_contents_impl.h"
-#include "content/public/browser/web_contents_delegate.h"
-#include "content/public/browser/web_drag_dest_delegate.h"
-#include "content/public/common/url_constants.h"
-#include "net/base/net_util.h"
-#include "third_party/WebKit/public/web/WebInputEvent.h"
-#include "ui/base/clipboard/custom_data_helper.h"
-#include "ui/base/dragdrop/gtk_dnd_util.h"
-#include "ui/base/gtk/gtk_screen_util.h"
-
-using blink::WebDragOperation;
-using blink::WebDragOperationNone;
-
-namespace content {
-
-namespace {
-const int kNumGtkHandlers = 5;
-
-int GetModifierFlags(GtkWidget* widget) {
- int modifier_state = 0;
- GdkModifierType state;
- gdk_window_get_pointer(gtk_widget_get_window(widget), NULL, NULL, &state);
-
- if (state & GDK_SHIFT_MASK)
- modifier_state |= blink::WebInputEvent::ShiftKey;
- if (state & GDK_CONTROL_MASK)
- modifier_state |= blink::WebInputEvent::ControlKey;
- if (state & GDK_MOD1_MASK)
- modifier_state |= blink::WebInputEvent::AltKey;
- if (state & GDK_META_MASK)
- modifier_state |= blink::WebInputEvent::MetaKey;
- return modifier_state;
-}
-
-} // namespace
-
-WebDragDestGtk::WebDragDestGtk(WebContents* web_contents, GtkWidget* widget)
- : web_contents_(web_contents),
- widget_(widget),
- context_(NULL),
- data_requests_(0),
- delegate_(NULL),
- canceled_(false),
- method_factory_(this) {
- gtk_drag_dest_set(widget, static_cast<GtkDestDefaults>(0),
- NULL, 0,
- static_cast<GdkDragAction>(GDK_ACTION_COPY |
- GDK_ACTION_LINK |
- GDK_ACTION_MOVE));
-
- // If adding a handler, make sure to update kNumGtkHandlers and add it to the
- // |handlers_| array so that it can be disconnected later on.
- handlers_.reset(new int[kNumGtkHandlers]);
- handlers_.get()[0] = g_signal_connect(
- widget, "drag-motion", G_CALLBACK(OnDragMotionThunk), this);
- handlers_.get()[1] = g_signal_connect(
- widget, "drag-leave", G_CALLBACK(OnDragLeaveThunk), this);
- handlers_.get()[2] = g_signal_connect(
- widget, "drag-drop", G_CALLBACK(OnDragDropThunk), this);
- handlers_.get()[3] = g_signal_connect(
- widget, "drag-data-received", G_CALLBACK(OnDragDataReceivedThunk), this);
- // TODO(tony): Need a drag-data-delete handler for moving content out of
- // the WebContents. http://crbug.com/38989
-
- handlers_.get()[4] = g_signal_connect(
- widget, "destroy", G_CALLBACK(gtk_widget_destroyed), &widget_);
-}
-
-WebDragDestGtk::~WebDragDestGtk() {
- if (widget_) {
- gtk_drag_dest_unset(widget_);
- for (int i = 0; i < kNumGtkHandlers; ++i)
- g_signal_handler_disconnect(widget_, handlers_.get()[i]);
- }
-}
-
-void WebDragDestGtk::UpdateDragStatus(WebDragOperation operation) {
- if (context_) {
- is_drop_target_ = operation != WebDragOperationNone;
- gdk_drag_status(context_, WebDragOpToGdkDragAction(operation),
- drag_over_time_);
- }
-}
-
-void WebDragDestGtk::DragLeave() {
- GetRenderViewHost()->DragTargetDragLeave();
- if (delegate())
- delegate()->OnDragLeave();
-
- drop_data_.reset();
-}
-
-gboolean WebDragDestGtk::OnDragMotion(GtkWidget* sender,
- GdkDragContext* context,
- gint x, gint y,
- guint time) {
- if (context_ != context) {
- context_ = context;
- drop_data_.reset(new DropData);
- is_drop_target_ = false;
-
- if (delegate())
- delegate()->DragInitialize(web_contents_);
-
- // text/plain must come before text/uri-list. This is a hack that works in
- // conjunction with OnDragDataReceived. Since some file managers populate
- // text/plain with file URLs when dragging files, we want to handle
- // text/uri-list after text/plain so that the plain text can be cleared if
- // it's a file drag.
- static int supported_targets[] = {
- ui::TEXT_PLAIN,
- ui::TEXT_URI_LIST,
- ui::TEXT_HTML,
- ui::NETSCAPE_URL,
- ui::CHROME_NAMED_URL,
- // TODO(estade): support image drags?
- ui::CUSTOM_DATA,
- };
-
- // Add the delegate's requested target if applicable. Need to do this here
- // since gtk_drag_get_data will dispatch to our drag-data-received.
- data_requests_ = arraysize(supported_targets) + (delegate() ? 1 : 0);
- for (size_t i = 0; i < arraysize(supported_targets); ++i) {
- gtk_drag_get_data(widget_, context,
- ui::GetAtomForTarget(supported_targets[i]),
- time);
- }
-
- if (delegate()) {
- gtk_drag_get_data(widget_, context, delegate()->GetBookmarkTargetAtom(),
- time);
- }
- } else if (data_requests_ == 0) {
- if (canceled_)
- return FALSE;
-
- GetRenderViewHost()->DragTargetDragOver(
- ui::ClientPoint(widget_),
- ui::ScreenPoint(widget_),
- GdkDragActionToWebDragOp(context->actions),
- GetModifierFlags(widget_));
-
- if (delegate())
- delegate()->OnDragOver();
-
- drag_over_time_ = time;
- }
-
- // Pretend we are a drag destination because we don't want to wait for
- // the renderer to tell us if we really are or not.
- return TRUE;
-}
-
-void WebDragDestGtk::OnDragDataReceived(
- GtkWidget* sender, GdkDragContext* context, gint x, gint y,
- GtkSelectionData* data, guint info, guint time) {
- // We might get the data from an old get_data() request that we no longer
- // care about.
- if (context != context_)
- return;
-
- data_requests_--;
-
- // Decode the data.
- gint data_length = gtk_selection_data_get_length(data);
- const guchar* raw_data = gtk_selection_data_get_data(data);
- GdkAtom target = gtk_selection_data_get_target(data);
- if (raw_data && data_length > 0) {
- // If the source can't provide us with valid data for a requested target,
- // raw_data will be NULL.
- if (target == ui::GetAtomForTarget(ui::TEXT_PLAIN)) {
- guchar* text = gtk_selection_data_get_text(data);
- if (text) {
- drop_data_->text = base::NullableString16(
- UTF8ToUTF16(std::string(reinterpret_cast<const char*>(text))),
- false);
- g_free(text);
- }
- } else if (target == ui::GetAtomForTarget(ui::TEXT_URI_LIST)) {
- gchar** uris = gtk_selection_data_get_uris(data);
- if (uris) {
- drop_data_->url = GURL();
- for (gchar** uri_iter = uris; *uri_iter; uri_iter++) {
- // Most file managers populate text/uri-list with file URLs when
- // dragging files. To avoid exposing file system paths to web content,
- // file URLs are never set as the URL content for the drop.
- // TODO(estade): Can the filenames have a non-UTF8 encoding?
- GURL url(*uri_iter);
- base::FilePath file_path;
- if (url.SchemeIs(chrome::kFileScheme) &&
- net::FileURLToFilePath(url, &file_path)) {
- drop_data_->filenames.push_back(
- DropData::FileInfo(UTF8ToUTF16(file_path.value()),
- base::string16()));
- // This is a hack. Some file managers also populate text/plain with
- // a file URL when dragging files, so we clear it to avoid exposing
- // it to the web content.
- drop_data_->text = base::NullableString16();
- } else if (!drop_data_->url.is_valid()) {
- // Also set the first non-file URL as the URL content for the drop.
- drop_data_->url = url;
- }
- }
- g_strfreev(uris);
- }
- } else if (target == ui::GetAtomForTarget(ui::TEXT_HTML)) {
- // TODO(estade): Can the html have a non-UTF8 encoding?
- drop_data_->html = base::NullableString16(
- UTF8ToUTF16(std::string(reinterpret_cast<const char*>(raw_data),
- data_length)),
- false);
- // We leave the base URL empty.
- } else if (target == ui::GetAtomForTarget(ui::NETSCAPE_URL)) {
- std::string netscape_url(reinterpret_cast<const char*>(raw_data),
- data_length);
- size_t split = netscape_url.find_first_of('\n');
- if (split != std::string::npos) {
- drop_data_->url = GURL(netscape_url.substr(0, split));
- if (split < netscape_url.size() - 1)
- drop_data_->url_title = UTF8ToUTF16(netscape_url.substr(split + 1));
- }
- } else if (target == ui::GetAtomForTarget(ui::CHROME_NAMED_URL)) {
- ui::ExtractNamedURL(data, &drop_data_->url, &drop_data_->url_title);
- } else if (target == ui::GetAtomForTarget(ui::CUSTOM_DATA)) {
- ui::ReadCustomDataIntoMap(
- raw_data, data_length, &drop_data_->custom_data);
- }
- }
-
- if (data_requests_ == 0) {
- // Give the delegate an opportunity to cancel the drag.
- canceled_ = !web_contents_->GetDelegate()->CanDragEnter(
- web_contents_,
- *drop_data_,
- GdkDragActionToWebDragOp(context->actions));
- if (canceled_) {
- drag_over_time_ = time;
- UpdateDragStatus(WebDragOperationNone);
- drop_data_.reset();
- return;
- }
- }
-
- // For CHROME_BOOKMARK_ITEM, we have to handle the case where the drag source
- // doesn't have any data available for us. In this case we try to synthesize a
- // URL bookmark.
- // Note that bookmark drag data is encoded in the same format for both
- // GTK and Views, hence we can share the same logic here.
- if (delegate() && target == delegate()->GetBookmarkTargetAtom()) {
- if (raw_data && data_length > 0) {
- delegate()->OnReceiveDataFromGtk(data);
- } else {
- delegate()->OnReceiveProcessedData(drop_data_->url,
- drop_data_->url_title);
- }
- }
-
- if (data_requests_ == 0) {
- // Tell the renderer about the drag.
- // |x| and |y| are seemingly arbitrary at this point.
- GetRenderViewHost()->DragTargetDragEnter(
- *drop_data_.get(),
- ui::ClientPoint(widget_),
- ui::ScreenPoint(widget_),
- GdkDragActionToWebDragOp(context->actions),
- GetModifierFlags(widget_));
-
- if (delegate())
- delegate()->OnDragEnter();
-
- drag_over_time_ = time;
- }
-}
-
-// The drag has left our widget; forward this information to the renderer.
-void WebDragDestGtk::OnDragLeave(GtkWidget* sender, GdkDragContext* context,
- guint time) {
- // Set |context_| to NULL to make sure we will recognize the next DragMotion
- // as an enter.
- context_ = NULL;
-
- if (canceled_)
- return;
-
- // Sometimes we get a drag-leave event before getting a drag-data-received
- // event. In that case, we don't want to bother the renderer with a
- // DragLeave event.
- if (data_requests_ != 0)
- return;
-
- // When GTK sends us a drag-drop signal, it is shortly (and synchronously)
- // preceded by a drag-leave. The renderer doesn't like getting the signals
- // in this order so delay telling it about the drag-leave till we are sure
- // we are not getting a drop as well.
- base::MessageLoop::current()->PostTask(
- FROM_HERE,
- base::Bind(&WebDragDestGtk::DragLeave, method_factory_.GetWeakPtr()));
-}
-
-// Called by GTK when the user releases the mouse, executing a drop.
-gboolean WebDragDestGtk::OnDragDrop(GtkWidget* sender, GdkDragContext* context,
- gint x, gint y, guint time) {
- // Cancel that drag leave!
- method_factory_.InvalidateWeakPtrs();
-
- GetRenderViewHost()->
- DragTargetDrop(ui::ClientPoint(widget_), ui::ScreenPoint(widget_),
- GetModifierFlags(widget_));
-
- if (delegate())
- delegate()->OnDrop();
-
- // The second parameter is just an educated guess as to whether or not the
- // drag succeeded, but at least we will get the drag-end animation right
- // sometimes.
- gtk_drag_finish(context, is_drop_target_, FALSE, time);
-
- return TRUE;
-}
-
-RenderViewHostImpl* WebDragDestGtk::GetRenderViewHost() const {
- return static_cast<RenderViewHostImpl*>(web_contents_->GetRenderViewHost());
-}
-
-} // namespace content
diff --git a/chromium/content/browser/web_contents/web_drag_dest_gtk.h b/chromium/content/browser/web_contents/web_drag_dest_gtk.h
deleted file mode 100644
index 7258e1bc43c..00000000000
--- a/chromium/content/browser/web_contents/web_drag_dest_gtk.h
+++ /dev/null
@@ -1,114 +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_WEB_CONTENTS_WEB_DRAG_DEST_GTK_H_
-#define CONTENT_BROWSER_WEB_CONTENTS_WEB_DRAG_DEST_GTK_H_
-
-#include <gtk/gtk.h>
-
-#include "base/gtest_prod_util.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/weak_ptr.h"
-#include "content/common/content_export.h"
-#include "content/public/common/drop_data.h"
-#include "third_party/WebKit/public/web/WebDragOperation.h"
-#include "ui/base/gtk/gtk_signal.h"
-
-namespace content {
-
-class RenderViewHostImpl;
-class WebContents;
-class WebDragDestDelegate;
-
-// A helper class that handles DnD for drops in the renderer. In GTK parlance,
-// this handles destination-side DnD, but not source-side DnD.
-class CONTENT_EXPORT WebDragDestGtk {
- public:
- WebDragDestGtk(WebContents* web_contents, GtkWidget* widget);
- ~WebDragDestGtk();
-
- DropData* current_drop_data() const { return drop_data_.get(); }
-
- // This is called when the renderer responds to a drag motion event. We must
- // update the system drag cursor.
- void UpdateDragStatus(blink::WebDragOperation operation);
-
- // Informs the renderer when a system drag has left the render view.
- // See OnDragLeave().
- void DragLeave();
-
- WebDragDestDelegate* delegate() const { return delegate_; }
- void set_delegate(WebDragDestDelegate* delegate) { delegate_ = delegate; }
-
- GtkWidget* widget() const { return widget_; }
-
- private:
- RenderViewHostImpl* GetRenderViewHost() const;
-
- // Called when a system drag crosses over the render view. As there is no drag
- // enter event, we treat it as an enter event (and not a regular motion event)
- // when |context_| is NULL.
- CHROMEGTK_CALLBACK_4(WebDragDestGtk, gboolean, OnDragMotion, GdkDragContext*,
- gint, gint, guint);
-
- // We make a series of requests for the drag data when the drag first enters
- // the render view. This is the callback that is used to give us the data
- // for each individual target. When |data_requests_| reaches 0, we know we
- // have attained all the data, and we can finally tell the renderer about the
- // drag.
- CHROMEGTK_CALLBACK_6(WebDragDestGtk, void, OnDragDataReceived,
- GdkDragContext*, gint, gint, GtkSelectionData*,
- guint, guint);
-
- // The drag has left our widget; forward this information to the renderer.
- CHROMEGTK_CALLBACK_2(WebDragDestGtk, void, OnDragLeave, GdkDragContext*,
- guint);
-
- // Called by GTK when the user releases the mouse, executing a drop.
- CHROMEGTK_CALLBACK_4(WebDragDestGtk, gboolean, OnDragDrop, GdkDragContext*,
- gint, gint, guint);
-
- WebContents* web_contents_;
-
- // The render view.
- GtkWidget* widget_;
-
- // The current drag context for system drags over our render view, or NULL if
- // there is no system drag or the system drag is not over our render view.
- GdkDragContext* context_;
-
- // The data for the current drag, or NULL if |context_| is NULL.
- scoped_ptr<DropData> drop_data_;
-
- // The number of outstanding drag data requests we have sent to the drag
- // source.
- int data_requests_;
-
- // The last time we sent a message to the renderer related to a drag motion.
- gint drag_over_time_;
-
- // Whether the cursor is over a drop target, according to the last message we
- // got from the renderer.
- bool is_drop_target_;
-
- // Stores Handler IDs for the gtk signal handlers. We have to cancel the
- // signal handlers when this WebDragDestGtk is deleted so that if, later on,
- // we re-create the drag dest with the same widget, we don't get callbacks to
- // deleted functions.
- scoped_ptr<int[]> handlers_;
-
- // A delegate that can receive drag information about drag events.
- WebDragDestDelegate* delegate_;
-
- // True if the drag has been canceled.
- bool canceled_;
-
- base::WeakPtrFactory<WebDragDestGtk> method_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(WebDragDestGtk);
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_WEB_CONTENTS_WEB_DRAG_DEST_GTK_H_
diff --git a/chromium/content/browser/web_contents/web_drag_dest_mac.mm b/chromium/content/browser/web_contents/web_drag_dest_mac.mm
index 8768bed657b..b0cb8e2d376 100644
--- a/chromium/content/browser/web_contents/web_drag_dest_mac.mm
+++ b/chromium/content/browser/web_contents/web_drag_dest_mac.mm
@@ -253,6 +253,9 @@ int GetModifierFlags() {
DCHECK(pboard);
NSArray* types = [pboard types];
+ data->did_originate_from_renderer =
+ [types containsObject:ui::kChromeDragDummyPboardType];
+
// Get URL if possible. To avoid exposing file system paths to web content,
// filenames in the drag are not converted to file URLs.
ui::PopulateURLAndTitleFromPasteboard(&data->url,
@@ -288,9 +291,9 @@ int GetModifierFlags() {
BOOL exists = [[NSFileManager defaultManager]
fileExistsAtPath:filename];
if (exists) {
- data->filenames.push_back(
- DropData::FileInfo(
- base::SysNSStringToUTF16(filename), base::string16()));
+ data->filenames.push_back(ui::FileInfo(
+ base::FilePath::FromUTF8Unsafe(base::SysNSStringToUTF8(filename)),
+ base::FilePath()));
}
}
}
diff --git a/chromium/content/browser/web_contents/web_drag_dest_mac_unittest.mm b/chromium/content/browser/web_contents/web_drag_dest_mac_unittest.mm
index a7dcedecb03..90c565c46dd 100644
--- a/chromium/content/browser/web_contents/web_drag_dest_mac_unittest.mm
+++ b/chromium/content/browser/web_contents/web_drag_dest_mac_unittest.mm
@@ -13,7 +13,7 @@
#include "testing/gtest/include/gtest/gtest.h"
#import "third_party/mozilla/NSPasteboard+Utils.h"
#import "ui/base/dragdrop/cocoa_dnd_util.h"
-#import "ui/base/test/ui_cocoa_test_helper.h"
+#import "ui/gfx/test/ui_cocoa_test_helper.h"
using content::DropData;
using content::RenderViewHostImplTestHarness;
@@ -140,7 +140,7 @@ TEST_F(WebDragDestTest, URL) {
EXPECT_TRUE(ui::PopulateURLAndTitleFromPasteboard(
&result_url, &result_title, pboard, YES));
EXPECT_EQ("file://localhost/bin/sh", result_url.spec());
- EXPECT_EQ("sh", UTF16ToUTF8(result_title));
+ EXPECT_EQ("sh", base::UTF16ToUTF8(result_title));
[pboard releaseGlobally];
}
diff --git a/chromium/content/browser/web_contents/web_drag_dest_win.cc b/chromium/content/browser/web_contents/web_drag_dest_win.cc
deleted file mode 100644
index e497f860d65..00000000000
--- a/chromium/content/browser/web_contents/web_drag_dest_win.cc
+++ /dev/null
@@ -1,286 +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/web_contents/web_drag_dest_win.h"
-
-#include <windows.h>
-#include <shlobj.h>
-
-#include "base/win/win_util.h"
-#include "content/browser/renderer_host/render_view_host_impl.h"
-#include "content/browser/web_contents/web_drag_utils_win.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/browser/web_contents_delegate.h"
-#include "content/public/browser/web_drag_dest_delegate.h"
-#include "content/public/common/drop_data.h"
-#include "net/base/net_util.h"
-#include "third_party/WebKit/public/web/WebInputEvent.h"
-#include "ui/base/clipboard/clipboard_util_win.h"
-#include "ui/base/dragdrop/os_exchange_data.h"
-#include "ui/base/dragdrop/os_exchange_data_provider_win.h"
-#include "ui/base/window_open_disposition.h"
-#include "ui/gfx/point.h"
-#include "url/gurl.h"
-
-using blink::WebDragOperationNone;
-using blink::WebDragOperationCopy;
-using blink::WebDragOperationLink;
-using blink::WebDragOperationMove;
-using blink::WebDragOperationGeneric;
-
-namespace content {
-namespace {
-
-const unsigned short kHighBitMaskShort = 0x8000;
-
-// A helper method for getting the preferred drop effect.
-DWORD GetPreferredDropEffect(DWORD effect) {
- if (effect & DROPEFFECT_COPY)
- return DROPEFFECT_COPY;
- if (effect & DROPEFFECT_LINK)
- return DROPEFFECT_LINK;
- if (effect & DROPEFFECT_MOVE)
- return DROPEFFECT_MOVE;
- return DROPEFFECT_NONE;
-}
-
-int GetModifierFlags() {
- int modifier_state = 0;
- if (base::win::IsShiftPressed())
- modifier_state |= blink::WebInputEvent::ShiftKey;
- if (base::win::IsCtrlPressed())
- modifier_state |= blink::WebInputEvent::ControlKey;
- if (base::win::IsAltPressed())
- modifier_state |= blink::WebInputEvent::AltKey;
- if (::GetKeyState(VK_LWIN) & kHighBitMaskShort)
- modifier_state |= blink::WebInputEvent::MetaKey;
- if (::GetKeyState(VK_RWIN) & kHighBitMaskShort)
- modifier_state |= blink::WebInputEvent::MetaKey;
- return modifier_state;
-}
-
-// Helper method for converting Window's specific IDataObject to a DropData
-// object.
-void PopulateDropData(IDataObject* data_object, DropData* drop_data) {
- base::string16 url_str;
- if (ui::ClipboardUtil::GetUrl(
- data_object, &url_str, &drop_data->url_title, false)) {
- GURL test_url(url_str);
- if (test_url.is_valid())
- drop_data->url = test_url;
- }
- std::vector<base::string16> filenames;
- ui::ClipboardUtil::GetFilenames(data_object, &filenames);
- for (size_t i = 0; i < filenames.size(); ++i)
- drop_data->filenames.push_back(
- DropData::FileInfo(filenames[i], base::string16()));
- base::string16 text;
- ui::ClipboardUtil::GetPlainText(data_object, &text);
- if (!text.empty()) {
- drop_data->text = base::NullableString16(text, false);
- }
- base::string16 html;
- std::string html_base_url;
- ui::ClipboardUtil::GetHtml(data_object, &html, &html_base_url);
- if (!html.empty()) {
- drop_data->html = base::NullableString16(html, false);
- }
- if (!html_base_url.empty()) {
- drop_data->html_base_url = GURL(html_base_url);
- }
- ui::ClipboardUtil::GetWebCustomData(data_object, &drop_data->custom_data);
-}
-
-} // namespace
-
-// InterstitialDropTarget is like a ui::DropTargetWin implementation that
-// WebDragDest passes through to if an interstitial is showing. Rather than
-// passing messages on to the renderer, we just check to see if there's a link
-// in the drop data and handle links as navigations.
-class InterstitialDropTarget {
- public:
- explicit InterstitialDropTarget(WebContents* web_contents)
- : web_contents_(web_contents) {}
-
- DWORD OnDragEnter(IDataObject* data_object, DWORD effect) {
- return ui::ClipboardUtil::HasUrl(data_object) ?
- GetPreferredDropEffect(effect) : DROPEFFECT_NONE;
- }
-
- DWORD OnDragOver(IDataObject* data_object, DWORD effect) {
- return ui::ClipboardUtil::HasUrl(data_object) ?
- GetPreferredDropEffect(effect) : DROPEFFECT_NONE;
- }
-
- void OnDragLeave(IDataObject* data_object) {
- }
-
- DWORD OnDrop(IDataObject* data_object, DWORD effect) {
- if (!ui::ClipboardUtil::HasUrl(data_object))
- return DROPEFFECT_NONE;
-
- std::wstring url;
- std::wstring title;
- ui::ClipboardUtil::GetUrl(data_object, &url, &title, true);
- OpenURLParams params(GURL(url), Referrer(), CURRENT_TAB,
- PAGE_TRANSITION_AUTO_BOOKMARK, false);
- web_contents_->OpenURL(params);
- return GetPreferredDropEffect(effect);
- }
-
- private:
- WebContents* web_contents_;
-
- DISALLOW_COPY_AND_ASSIGN(InterstitialDropTarget);
-};
-
-WebDragDest::WebDragDest(HWND source_hwnd, WebContents* web_contents)
- : ui::DropTargetWin(source_hwnd),
- web_contents_(web_contents),
- current_rvh_(NULL),
- drag_cursor_(WebDragOperationNone),
- interstitial_drop_target_(new InterstitialDropTarget(web_contents)),
- delegate_(NULL),
- canceled_(false) {
-}
-
-WebDragDest::~WebDragDest() {
-}
-
-DWORD WebDragDest::OnDragEnter(IDataObject* data_object,
- DWORD key_state,
- POINT cursor_position,
- DWORD effects) {
- current_rvh_ = web_contents_->GetRenderViewHost();
-
- // TODO(tc): PopulateDropData can be slow depending on what is in the
- // IDataObject. Maybe we can do this in a background thread.
- scoped_ptr<DropData> drop_data;
- drop_data.reset(new DropData());
- PopulateDropData(data_object, drop_data.get());
-
- if (drop_data->url.is_empty())
- ui::OSExchangeDataProviderWin::GetPlainTextURL(data_object,
- &drop_data->url);
-
- // Give the delegate an opportunity to cancel the drag.
- canceled_ = !web_contents_->GetDelegate()->CanDragEnter(
- web_contents_,
- *drop_data,
- WinDragOpMaskToWebDragOpMask(effects));
- if (canceled_)
- return DROPEFFECT_NONE;
-
- if (delegate_)
- delegate_->DragInitialize(web_contents_);
-
- // Don't pass messages to the renderer if an interstitial page is showing
- // because we don't want the interstitial page to navigate. Instead,
- // pass the messages on to a separate interstitial DropTarget handler.
- if (web_contents_->ShowingInterstitialPage())
- return interstitial_drop_target_->OnDragEnter(data_object, effects);
-
- drop_data_.swap(drop_data);
- drag_cursor_ = WebDragOperationNone;
-
- POINT client_pt = cursor_position;
- ScreenToClient(GetHWND(), &client_pt);
- web_contents_->GetRenderViewHost()->DragTargetDragEnter(*drop_data_,
- gfx::Point(client_pt.x, client_pt.y),
- gfx::Point(cursor_position.x, cursor_position.y),
- WinDragOpMaskToWebDragOpMask(effects),
- GetModifierFlags());
-
- if (delegate_)
- delegate_->OnDragEnter(data_object);
-
- // We lie here and always return a DROPEFFECT because we don't want to
- // wait for the IPC call to return.
- return WebDragOpToWinDragOp(drag_cursor_);
-}
-
-DWORD WebDragDest::OnDragOver(IDataObject* data_object,
- DWORD key_state,
- POINT cursor_position,
- DWORD effects) {
- DCHECK(current_rvh_);
- if (current_rvh_ != web_contents_->GetRenderViewHost())
- OnDragEnter(data_object, key_state, cursor_position, effects);
-
- if (canceled_)
- return DROPEFFECT_NONE;
-
- if (web_contents_->ShowingInterstitialPage())
- return interstitial_drop_target_->OnDragOver(data_object, effects);
-
- POINT client_pt = cursor_position;
- ScreenToClient(GetHWND(), &client_pt);
- web_contents_->GetRenderViewHost()->DragTargetDragOver(
- gfx::Point(client_pt.x, client_pt.y),
- gfx::Point(cursor_position.x, cursor_position.y),
- WinDragOpMaskToWebDragOpMask(effects),
- GetModifierFlags());
-
- if (delegate_)
- delegate_->OnDragOver(data_object);
-
- return WebDragOpToWinDragOp(drag_cursor_);
-}
-
-void WebDragDest::OnDragLeave(IDataObject* data_object) {
- DCHECK(current_rvh_);
- if (current_rvh_ != web_contents_->GetRenderViewHost())
- return;
-
- if (canceled_)
- return;
-
- if (web_contents_->ShowingInterstitialPage()) {
- interstitial_drop_target_->OnDragLeave(data_object);
- } else {
- web_contents_->GetRenderViewHost()->DragTargetDragLeave();
- }
-
- if (delegate_)
- delegate_->OnDragLeave(data_object);
-
- drop_data_.reset();
-}
-
-DWORD WebDragDest::OnDrop(IDataObject* data_object,
- DWORD key_state,
- POINT cursor_position,
- DWORD effect) {
- DCHECK(current_rvh_);
- if (current_rvh_ != web_contents_->GetRenderViewHost())
- OnDragEnter(data_object, key_state, cursor_position, effect);
-
- if (web_contents_->ShowingInterstitialPage())
- interstitial_drop_target_->OnDragOver(data_object, effect);
-
- if (web_contents_->ShowingInterstitialPage())
- return interstitial_drop_target_->OnDrop(data_object, effect);
-
- POINT client_pt = cursor_position;
- ScreenToClient(GetHWND(), &client_pt);
- web_contents_->GetRenderViewHost()->DragTargetDrop(
- gfx::Point(client_pt.x, client_pt.y),
- gfx::Point(cursor_position.x, cursor_position.y),
- GetModifierFlags());
-
- if (delegate_)
- delegate_->OnDrop(data_object);
-
- current_rvh_ = NULL;
-
- // This isn't always correct, but at least it's a close approximation.
- // For now, we always map a move to a copy to prevent potential data loss.
- DWORD drop_effect = WebDragOpToWinDragOp(drag_cursor_);
- DWORD result = drop_effect != DROPEFFECT_MOVE ? drop_effect : DROPEFFECT_COPY;
-
- drop_data_.reset();
- return result;
-}
-
-} // namespace content
diff --git a/chromium/content/browser/web_contents/web_drag_dest_win.h b/chromium/content/browser/web_contents/web_drag_dest_win.h
deleted file mode 100644
index 9e3ca8daa2e..00000000000
--- a/chromium/content/browser/web_contents/web_drag_dest_win.h
+++ /dev/null
@@ -1,89 +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_WEB_CONTENTS_WEB_DRAG_DEST_WIN_H_
-#define CONTENT_BROWSER_WEB_CONTENTS_WEB_DRAG_DEST_WIN_H_
-
-#include "base/memory/scoped_ptr.h"
-#include "content/common/content_export.h"
-#include "content/public/common/drop_data.h"
-#include "third_party/WebKit/public/web/WebDragOperation.h"
-#include "ui/base/dragdrop/drop_target_win.h"
-
-namespace content {
-class InterstitialDropTarget;
-class RenderViewHost;
-class WebContents;
-class WebDragDestDelegate;
-
-// A helper object that provides drop capabilities to a WebContentsImpl. The
-// DropTarget handles drags that enter the region of the WebContents by
-// passing on the events to the renderer.
-class CONTENT_EXPORT WebDragDest : public ui::DropTargetWin {
- public:
- // Create a new WebDragDest associating it with the given HWND and
- // WebContents.
- WebDragDest(HWND source_hwnd, WebContents* contents);
- virtual ~WebDragDest();
-
- DropData* current_drop_data() const { return drop_data_.get(); }
-
- void set_drag_cursor(blink::WebDragOperation op) {
- drag_cursor_ = op;
- }
-
- WebDragDestDelegate* delegate() const { return delegate_; }
- void set_delegate(WebDragDestDelegate* d) { delegate_ = d; }
-
- protected:
- virtual DWORD OnDragEnter(IDataObject* data_object,
- DWORD key_state,
- POINT cursor_position,
- DWORD effect);
-
- virtual DWORD OnDragOver(IDataObject* data_object,
- DWORD key_state,
- POINT cursor_position,
- DWORD effect);
-
- virtual void OnDragLeave(IDataObject* data_object);
-
- virtual DWORD OnDrop(IDataObject* data_object,
- DWORD key_state,
- POINT cursor_position,
- DWORD effect);
-
- private:
- // Our associated WebContents.
- WebContents* web_contents_;
-
- // We keep track of the render view host we're dragging over. If it changes
- // during a drag, we need to re-send the DragEnter message. WARNING:
- // this pointer should never be dereferenced. We only use it for comparing
- // pointers.
- RenderViewHost* current_rvh_;
-
- // Used to determine what cursor we should display when dragging over web
- // content area. This can be updated async during a drag operation.
- blink::WebDragOperation drag_cursor_;
-
- // A special drop target handler for when we try to d&d while an interstitial
- // page is showing.
- scoped_ptr<InterstitialDropTarget> interstitial_drop_target_;
-
- // A delegate that can receive drag information about drag events.
- WebDragDestDelegate* delegate_;
-
- // The data for the current drag, or NULL if |context_| is NULL.
- scoped_ptr<DropData> drop_data_;
-
- // True if the drag has been canceled.
- bool canceled_;
-
- DISALLOW_COPY_AND_ASSIGN(WebDragDest);
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_WEB_CONTENTS_WEB_DRAG_DEST_WIN_H_
diff --git a/chromium/content/browser/web_contents/web_drag_source_gtk.cc b/chromium/content/browser/web_contents/web_drag_source_gtk.cc
deleted file mode 100644
index 2f9e58707f9..00000000000
--- a/chromium/content/browser/web_contents/web_drag_source_gtk.cc
+++ /dev/null
@@ -1,403 +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/web_contents/web_drag_source_gtk.h"
-
-#include <string>
-
-#include "base/nix/mime_util_xdg.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/threading/thread_restrictions.h"
-#include "content/browser/download/drag_download_file.h"
-#include "content/browser/download/drag_download_util.h"
-#include "content/browser/renderer_host/render_view_host_delegate.h"
-#include "content/browser/renderer_host/render_view_host_impl.h"
-#include "content/browser/web_contents/drag_utils_gtk.h"
-#include "content/browser/web_contents/web_contents_impl.h"
-#include "content/public/browser/content_browser_client.h"
-#include "content/public/browser/web_contents_view.h"
-#include "content/public/common/content_client.h"
-#include "content/public/common/drop_data.h"
-#include "net/base/file_stream.h"
-#include "net/base/net_util.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "ui/base/clipboard/custom_data_helper.h"
-#include "ui/base/dragdrop/gtk_dnd_util.h"
-#include "ui/base/gtk/gtk_screen_util.h"
-#include "ui/gfx/gtk_compat.h"
-#include "ui/gfx/gtk_util.h"
-
-using blink::WebDragOperation;
-using blink::WebDragOperationsMask;
-using blink::WebDragOperationNone;
-
-namespace content {
-
-WebDragSourceGtk::WebDragSourceGtk(WebContents* web_contents)
- : web_contents_(static_cast<WebContentsImpl*>(web_contents)),
- drag_pixbuf_(NULL),
- drag_failed_(false),
- drag_widget_(gtk_invisible_new()),
- drag_context_(NULL),
- drag_icon_(gtk_window_new(GTK_WINDOW_POPUP)) {
- signals_.Connect(drag_widget_, "drag-failed",
- G_CALLBACK(OnDragFailedThunk), this);
- signals_.Connect(drag_widget_, "drag-begin",
- G_CALLBACK(OnDragBeginThunk),
- this);
- signals_.Connect(drag_widget_, "drag-end",
- G_CALLBACK(OnDragEndThunk), this);
- signals_.Connect(drag_widget_, "drag-data-get",
- G_CALLBACK(OnDragDataGetThunk), this);
-
- signals_.Connect(drag_icon_, "expose-event",
- G_CALLBACK(OnDragIconExposeThunk), this);
-}
-
-WebDragSourceGtk::~WebDragSourceGtk() {
- // Break the current drag, if any.
- if (drop_data_) {
- gtk_grab_add(drag_widget_);
- gtk_grab_remove(drag_widget_);
- base::MessageLoopForUI::current()->RemoveObserver(this);
- drop_data_.reset();
- }
-
- gtk_widget_destroy(drag_widget_);
- gtk_widget_destroy(drag_icon_);
-}
-
-bool WebDragSourceGtk::StartDragging(const DropData& drop_data,
- WebDragOperationsMask allowed_ops,
- GdkEventButton* last_mouse_down,
- const SkBitmap& image,
- const gfx::Vector2d& image_offset) {
- // Guard against re-starting before previous drag completed.
- if (drag_context_) {
- NOTREACHED();
- return false;
- }
-
- int targets_mask = 0;
-
- if (!drop_data.text.string().empty())
- targets_mask |= ui::TEXT_PLAIN;
- if (drop_data.url.is_valid()) {
- targets_mask |= ui::TEXT_URI_LIST;
- targets_mask |= ui::CHROME_NAMED_URL;
- targets_mask |= ui::NETSCAPE_URL;
- }
- if (!drop_data.html.string().empty())
- targets_mask |= ui::TEXT_HTML;
- if (!drop_data.file_contents.empty())
- targets_mask |= ui::CHROME_WEBDROP_FILE_CONTENTS;
- if (!drop_data.download_metadata.empty() &&
- ParseDownloadMetadata(drop_data.download_metadata,
- &wide_download_mime_type_,
- &download_file_name_,
- &download_url_)) {
- targets_mask |= ui::DIRECT_SAVE_FILE;
- }
- if (!drop_data.custom_data.empty())
- targets_mask |= ui::CUSTOM_DATA;
-
- // NOTE: Begin a drag even if no targets present. Otherwise, things like
- // draggable list elements will not work.
-
- drop_data_.reset(new DropData(drop_data));
-
- // The image we get from WebKit makes heavy use of alpha-shading. This looks
- // bad on non-compositing WMs. Fall back to the default drag icon.
- if (!image.isNull() && ui::IsScreenComposited())
- drag_pixbuf_ = gfx::GdkPixbufFromSkBitmap(image);
- image_offset_ = image_offset;
-
- GtkTargetList* list = ui::GetTargetListFromCodeMask(targets_mask);
- if (targets_mask & ui::CHROME_WEBDROP_FILE_CONTENTS) {
- // Looking up the mime type can hit the disk. http://crbug.com/84896
- base::ThreadRestrictions::ScopedAllowIO allow_io;
- drag_file_mime_type_ = gdk_atom_intern(
- base::nix::GetDataMimeType(drop_data.file_contents).c_str(), FALSE);
- gtk_target_list_add(list, drag_file_mime_type_,
- 0, ui::CHROME_WEBDROP_FILE_CONTENTS);
- }
-
- drag_failed_ = false;
- // If we don't pass an event, GDK won't know what event time to start grabbing
- // mouse events. Technically it's the mouse motion event and not the mouse
- // down event that causes the drag, but there's no reliable way to know
- // *which* motion event initiated the drag, so this will have to do.
- // TODO(estade): This can sometimes be very far off, e.g. if the user clicks
- // and holds and doesn't start dragging for a long time. I doubt it matters
- // much, but we should probably look into the possibility of getting the
- // initiating event from webkit.
- drag_context_ = gtk_drag_begin(drag_widget_, list,
- WebDragOpToGdkDragAction(allowed_ops),
- 1, // Drags are always initiated by the left button.
- reinterpret_cast<GdkEvent*>(last_mouse_down));
- // The drag adds a ref; let it own the list.
- gtk_target_list_unref(list);
-
- // Sometimes the drag fails to start; |context| will be NULL and we won't
- // get a drag-end signal.
- if (!drag_context_) {
- drag_failed_ = true;
- drop_data_.reset();
- return false;
- }
-
- base::MessageLoopForUI::current()->AddObserver(this);
- return true;
-}
-
-void WebDragSourceGtk::WillProcessEvent(GdkEvent* event) {
- // No-op.
-}
-
-void WebDragSourceGtk::DidProcessEvent(GdkEvent* event) {
- if (event->type != GDK_MOTION_NOTIFY)
- return;
-
- GdkEventMotion* event_motion = reinterpret_cast<GdkEventMotion*>(event);
- gfx::Point client = ui::ClientPoint(GetContentNativeView());
-
- if (web_contents_) {
- web_contents_->DragSourceMovedTo(
- client.x(), client.y(),
- static_cast<int>(event_motion->x_root),
- static_cast<int>(event_motion->y_root));
- }
-}
-
-void WebDragSourceGtk::OnDragDataGet(GtkWidget* sender,
- GdkDragContext* context,
- GtkSelectionData* selection_data,
- guint target_type,
- guint time) {
- const int kBitsPerByte = 8;
-
- switch (target_type) {
- case ui::TEXT_PLAIN: {
- std::string utf8_text = UTF16ToUTF8(drop_data_->text.string());
- gtk_selection_data_set_text(selection_data, utf8_text.c_str(),
- utf8_text.length());
- break;
- }
-
- case ui::TEXT_HTML: {
- // TODO(estade): change relative links to be absolute using
- // |html_base_url|.
- std::string utf8_text = UTF16ToUTF8(drop_data_->html.string());
- gtk_selection_data_set(selection_data,
- ui::GetAtomForTarget(ui::TEXT_HTML),
- kBitsPerByte,
- reinterpret_cast<const guchar*>(utf8_text.c_str()),
- utf8_text.length());
- break;
- }
-
- case ui::TEXT_URI_LIST:
- case ui::CHROME_NAMED_URL:
- case ui::NETSCAPE_URL: {
- ui::WriteURLWithName(selection_data, drop_data_->url,
- drop_data_->url_title, target_type);
- break;
- }
-
- case ui::CHROME_WEBDROP_FILE_CONTENTS: {
- gtk_selection_data_set(
- selection_data,
- drag_file_mime_type_, kBitsPerByte,
- reinterpret_cast<const guchar*>(drop_data_->file_contents.data()),
- drop_data_->file_contents.length());
- break;
- }
-
- case ui::DIRECT_SAVE_FILE: {
- char status_code = 'E';
-
- // Retrieves the full file path (in file URL format) provided by the
- // drop target by reading from the source window's XdndDirectSave0
- // property.
- gint file_url_len = 0;
- guchar* file_url_value = NULL;
- if (gdk_property_get(context->source_window,
- ui::GetAtomForTarget(ui::DIRECT_SAVE_FILE),
- ui::GetAtomForTarget(ui::TEXT_PLAIN_NO_CHARSET),
- 0,
- 1024,
- FALSE,
- NULL,
- NULL,
- &file_url_len,
- &file_url_value) &&
- file_url_value) {
- // Convert from the file url to the file path.
- GURL file_url(std::string(reinterpret_cast<char*>(file_url_value),
- file_url_len));
- g_free(file_url_value);
- base::FilePath file_path;
- if (net::FileURLToFilePath(file_url, &file_path)) {
- // Open the file as a stream.
- scoped_ptr<net::FileStream> file_stream(
- CreateFileStreamForDrop(
- &file_path,
- GetContentClient()->browser()->GetNetLog()));
- if (file_stream) {
- // Start downloading the file to the stream.
- scoped_refptr<DragDownloadFile> drag_file_downloader =
- new DragDownloadFile(
- file_path,
- file_stream.Pass(),
- download_url_,
- Referrer(web_contents_->GetURL(),
- drop_data_->referrer_policy),
- web_contents_->GetEncoding(),
- web_contents_);
- drag_file_downloader->Start(
- new PromiseFileFinalizer(drag_file_downloader.get()));
-
- // Set the status code to success.
- status_code = 'S';
- }
- }
-
- // Return the status code to the file manager.
- gtk_selection_data_set(selection_data,
- gtk_selection_data_get_target(selection_data),
- kBitsPerByte,
- reinterpret_cast<guchar*>(&status_code),
- 1);
- }
- break;
- }
-
- case ui::CUSTOM_DATA: {
- Pickle custom_data;
- ui::WriteCustomDataToPickle(drop_data_->custom_data, &custom_data);
- gtk_selection_data_set(
- selection_data,
- ui::GetAtomForTarget(ui::CUSTOM_DATA),
- kBitsPerByte,
- reinterpret_cast<const guchar*>(custom_data.data()),
- custom_data.size());
- break;
- }
-
- default:
- NOTREACHED();
- }
-}
-
-gboolean WebDragSourceGtk::OnDragFailed(GtkWidget* sender,
- GdkDragContext* context,
- GtkDragResult result) {
- drag_failed_ = true;
-
- gfx::Point root = ui::ScreenPoint(GetContentNativeView());
- gfx::Point client = ui::ClientPoint(GetContentNativeView());
-
- if (web_contents_) {
- web_contents_->DragSourceEndedAt(
- client.x(), client.y(), root.x(), root.y(),
- WebDragOperationNone);
- }
-
- // Let the native failure animation run.
- return FALSE;
-}
-
-void WebDragSourceGtk::OnDragBegin(GtkWidget* sender,
- GdkDragContext* drag_context) {
- if (!download_url_.is_empty()) {
- // Generate the file name based on both mime type and proposed file name.
- std::string default_name =
- GetContentClient()->browser()->GetDefaultDownloadName();
- base::FilePath generated_download_file_name =
- net::GenerateFileName(download_url_,
- std::string(),
- std::string(),
- download_file_name_.value(),
- UTF16ToUTF8(wide_download_mime_type_),
- default_name);
-
- // Pass the file name to the drop target by setting the source window's
- // XdndDirectSave0 property.
- gdk_property_change(drag_context->source_window,
- ui::GetAtomForTarget(ui::DIRECT_SAVE_FILE),
- ui::GetAtomForTarget(ui::TEXT_PLAIN_NO_CHARSET),
- 8,
- GDK_PROP_MODE_REPLACE,
- reinterpret_cast<const guchar*>(
- generated_download_file_name.value().c_str()),
- generated_download_file_name.value().length());
- }
-
- if (drag_pixbuf_) {
- gtk_widget_set_size_request(drag_icon_,
- gdk_pixbuf_get_width(drag_pixbuf_),
- gdk_pixbuf_get_height(drag_pixbuf_));
-
- // We only need to do this once.
- if (!gtk_widget_get_realized(drag_icon_)) {
- GdkScreen* screen = gtk_widget_get_screen(drag_icon_);
- GdkColormap* rgba = gdk_screen_get_rgba_colormap(screen);
- if (rgba)
- gtk_widget_set_colormap(drag_icon_, rgba);
- }
-
- gtk_drag_set_icon_widget(drag_context, drag_icon_,
- image_offset_.x(), image_offset_.y());
- }
-}
-
-void WebDragSourceGtk::OnDragEnd(GtkWidget* sender,
- GdkDragContext* drag_context) {
- if (drag_pixbuf_) {
- g_object_unref(drag_pixbuf_);
- drag_pixbuf_ = NULL;
- }
-
- base::MessageLoopForUI::current()->RemoveObserver(this);
-
- if (!download_url_.is_empty()) {
- gdk_property_delete(drag_context->source_window,
- ui::GetAtomForTarget(ui::DIRECT_SAVE_FILE));
- }
-
- if (!drag_failed_) {
- gfx::Point root = ui::ScreenPoint(GetContentNativeView());
- gfx::Point client = ui::ClientPoint(GetContentNativeView());
-
- if (web_contents_) {
- web_contents_->DragSourceEndedAt(
- client.x(), client.y(), root.x(), root.y(),
- GdkDragActionToWebDragOp(drag_context->action));
- }
- }
-
- web_contents_->SystemDragEnded();
-
- drop_data_.reset();
- drag_context_ = NULL;
-}
-
-gfx::NativeView WebDragSourceGtk::GetContentNativeView() const {
- return web_contents_->GetView()->GetContentNativeView();
-}
-
-gboolean WebDragSourceGtk::OnDragIconExpose(GtkWidget* sender,
- GdkEventExpose* event) {
- cairo_t* cr = gdk_cairo_create(event->window);
- gdk_cairo_rectangle(cr, &event->area);
- cairo_clip(cr);
- cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
- gdk_cairo_set_source_pixbuf(cr, drag_pixbuf_, 0, 0);
- cairo_paint(cr);
- cairo_destroy(cr);
-
- return TRUE;
-}
-
-} // namespace content
diff --git a/chromium/content/browser/web_contents/web_drag_source_gtk.h b/chromium/content/browser/web_contents/web_drag_source_gtk.h
deleted file mode 100644
index 8a7c7b28a9c..00000000000
--- a/chromium/content/browser/web_contents/web_drag_source_gtk.h
+++ /dev/null
@@ -1,115 +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_WEB_CONTENTS_WEB_DRAG_SOURCE_GTK_H_
-#define CONTENT_BROWSER_WEB_CONTENTS_WEB_DRAG_SOURCE_GTK_H_
-
-#include <gtk/gtk.h>
-
-#include "base/basictypes.h"
-#include "base/files/file_path.h"
-#include "base/message_loop/message_loop.h"
-#include "base/strings/string16.h"
-#include "content/common/content_export.h"
-#include "content/public/browser/web_contents.h"
-#include "third_party/WebKit/public/web/WebDragOperation.h"
-#include "ui/base/gtk/gtk_signal.h"
-#include "ui/base/gtk/gtk_signal_registrar.h"
-#include "ui/gfx/native_widget_types.h"
-#include "ui/gfx/vector2d.h"
-#include "url/gurl.h"
-
-class SkBitmap;
-
-namespace content {
-
-class RenderViewHostImpl;
-class WebContentsImpl;
-struct DropData;
-
-// WebDragSourceGtk takes care of managing the drag from a WebContents
-// with Gtk.
-class CONTENT_EXPORT WebDragSourceGtk :
- public base::MessageLoopForUI::Observer {
- public:
- explicit WebDragSourceGtk(WebContents* web_contents);
- virtual ~WebDragSourceGtk();
-
- // Starts a drag for the WebContents this WebDragSourceGtk was created for.
- // Returns false if the drag could not be started.
- bool StartDragging(const DropData& drop_data,
- blink::WebDragOperationsMask allowed_ops,
- GdkEventButton* last_mouse_down,
- const SkBitmap& image,
- const gfx::Vector2d& image_offset);
-
- // MessageLoop::Observer implementation:
- virtual void WillProcessEvent(GdkEvent* event) OVERRIDE;
- virtual void DidProcessEvent(GdkEvent* event) OVERRIDE;
-
- private:
- CHROMEGTK_CALLBACK_2(WebDragSourceGtk, gboolean, OnDragFailed,
- GdkDragContext*, GtkDragResult);
- CHROMEGTK_CALLBACK_1(WebDragSourceGtk, void, OnDragBegin,
- GdkDragContext*);
- CHROMEGTK_CALLBACK_1(WebDragSourceGtk, void, OnDragEnd,
- GdkDragContext*);
- CHROMEGTK_CALLBACK_4(WebDragSourceGtk, void, OnDragDataGet,
- GdkDragContext*, GtkSelectionData*, guint, guint);
- CHROMEGTK_CALLBACK_1(WebDragSourceGtk, gboolean, OnDragIconExpose,
- GdkEventExpose*);
-
- gfx::NativeView GetContentNativeView() const;
-
- // The tab we're manging the drag for.
- WebContentsImpl* web_contents_;
-
- // The drop data for the current drag (for drags that originate in the render
- // view). Non-NULL iff there is a current drag.
- scoped_ptr<DropData> drop_data_;
-
- // The image used for depicting the drag, and the offset between the cursor
- // and the top left pixel.
- GdkPixbuf* drag_pixbuf_;
- gfx::Vector2d image_offset_;
-
- // The mime type for the file contents of the current drag (if any).
- GdkAtom drag_file_mime_type_;
-
- // Whether the current drag has failed. Meaningless if we are not the source
- // for a current drag.
- bool drag_failed_;
-
- // This is the widget we use to initiate drags. Since we don't use the
- // renderer widget, we can persist drags even when our contents is switched
- // out. We can't use an OwnedWidgetGtk because the GtkInvisible widget
- // initialization code sinks the reference.
- GtkWidget* drag_widget_;
-
- // Context created once drag starts. A NULL value indicates that there is
- // no drag currently in progress.
- GdkDragContext* drag_context_;
-
- // The file mime type for a drag-out download.
- base::string16 wide_download_mime_type_;
-
- // The file name to be saved to for a drag-out download.
- base::FilePath download_file_name_;
-
- // The URL to download from for a drag-out download.
- GURL download_url_;
-
- // The widget that provides visual feedback for the drag. We can't use
- // an OwnedWidgetGtk because the GtkWindow initialization code sinks
- // the reference.
- GtkWidget* drag_icon_;
-
- ui::GtkSignalRegistrar signals_;
-
- DISALLOW_COPY_AND_ASSIGN(WebDragSourceGtk);
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_WEB_CONTENTS_WEB_DRAG_SOURCE_GTK_H_
diff --git a/chromium/content/browser/web_contents/web_drag_source_mac.h b/chromium/content/browser/web_contents/web_drag_source_mac.h
index f355728a594..f1ccc4df16b 100644
--- a/chromium/content/browser/web_contents/web_drag_source_mac.h
+++ b/chromium/content/browser/web_contents/web_drag_source_mac.h
@@ -83,9 +83,6 @@ CONTENT_EXPORT
- (void)endDragAt:(NSPoint)screenPoint
operation:(NSDragOperation)operation;
-// Drag moved; hook up to -draggedImage:movedTo:.
-- (void)moveDragTo:(NSPoint)screenPoint;
-
// Call to drag a promised file to the given path (should be called before
// -endDragAt:...); hook up to -namesOfPromisedFilesDroppedAtDestination:.
// Returns the file name (not including path) of the file deposited (or which
diff --git a/chromium/content/browser/web_contents/web_drag_source_mac.mm b/chromium/content/browser/web_contents/web_drag_source_mac.mm
index c66732050c9..b0f21609338 100644
--- a/chromium/content/browser/web_contents/web_drag_source_mac.mm
+++ b/chromium/content/browser/web_contents/web_drag_source_mac.mm
@@ -7,6 +7,7 @@
#include <sys/param.h>
#include "base/bind.h"
+#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/mac/mac_util.h"
#include "base/pickle.h"
@@ -23,15 +24,14 @@
#include "content/public/browser/content_browser_client.h"
#include "content/public/common/content_client.h"
#include "content/public/common/drop_data.h"
-#include "content/public/common/url_constants.h"
#include "grit/ui_resources.h"
#include "net/base/escape.h"
-#include "net/base/file_stream.h"
+#include "net/base/filename_util.h"
#include "net/base/mime_util.h"
-#include "net/base/net_util.h"
#include "ui/base/clipboard/custom_data_helper.h"
#include "ui/base/dragdrop/cocoa_dnd_util.h"
#include "ui/gfx/image/image.h"
+#include "url/url_constants.h"
using base::SysNSStringToUTF8;
using base::SysUTF8ToNSString;
@@ -41,7 +41,6 @@ using content::DragDownloadFile;
using content::DropData;
using content::PromiseFileFinalizer;
using content::RenderViewHostImpl;
-using net::FileStream;
namespace {
@@ -85,9 +84,9 @@ base::FilePath GetFileNameFromDragData(const DropData& drop_data) {
// is responsible for opening the file. It takes the drop data and an open file
// stream.
void PromiseWriterHelper(const DropData& drop_data,
- scoped_ptr<FileStream> file_stream) {
- DCHECK(file_stream);
- file_stream->WriteSync(drop_data.file_contents.data(),
+ base::File file) {
+ DCHECK(file.IsValid());
+ file.WriteAtCurrentPos(drop_data.file_contents.data(),
drop_data.file_contents.length());
}
@@ -147,7 +146,7 @@ void PromiseWriterHelper(const DropData& drop_data,
- (void)lazyWriteToPasteboard:(NSPasteboard*)pboard forType:(NSString*)type {
// NSHTMLPboardType requires the character set to be declared. Otherwise, it
// assumes US-ASCII. Awesome.
- const base::string16 kHtmlHeader = ASCIIToUTF16(
+ const base::string16 kHtmlHeader = base::ASCIIToUTF16(
"<meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\">");
// Be extra paranoid; avoid crashing.
@@ -170,7 +169,7 @@ void PromiseWriterHelper(const DropData& drop_data,
NSURL* url = [NSURL URLWithString:SysUTF8ToNSString(dropData_->url.spec())];
// If NSURL creation failed, check for a badly-escaped JavaScript URL.
// Strip out any existing escapes and then re-escape uniformly.
- if (!url && dropData_->url.SchemeIs(content::kJavaScriptScheme)) {
+ if (!url && dropData_->url.SchemeIs(url::kJavaScriptScheme)) {
net::UnescapeRule::Type unescapeRules =
net::UnescapeRule::SPACES |
net::UnescapeRule::URL_SPECIAL_CHARS |
@@ -292,27 +291,6 @@ void PromiseWriterHelper(const DropData& drop_data,
[pasteboard_ declareTypes:[NSArray array] owner:nil];
}
-- (void)moveDragTo:(NSPoint)screenPoint {
- if (!contents_)
- return;
- RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
- contents_->GetRenderViewHost());
- if (rvh) {
- // Convert |screenPoint| to view coordinates and flip it.
- NSPoint localPoint = NSZeroPoint;
- if ([contentsView_ window])
- localPoint = [self convertScreenPoint:screenPoint];
- NSRect viewFrame = [contentsView_ frame];
- localPoint.y = viewFrame.size.height - localPoint.y;
- // Flip |screenPoint|.
- NSRect screenFrame = [[[contentsView_ window] screen] frame];
- screenPoint.y = screenFrame.size.height - screenPoint.y;
-
- contents_->DragSourceMovedTo(localPoint.x, localPoint.y,
- screenPoint.x, screenPoint.y);
- }
-}
-
- (NSString*)dragPromisedFileTo:(NSString*)path {
// Be extra paranoid; avoid crashing.
if (!dropData_) {
@@ -323,19 +301,18 @@ void PromiseWriterHelper(const DropData& drop_data,
base::FilePath filePath(SysNSStringToUTF8(path));
filePath = filePath.Append(downloadFileName_);
- // CreateFileStreamForDrop() will call base::PathExists(),
+ // CreateFileForDrop() will call base::PathExists(),
// which is blocking. Since this operation is already blocking the
// UI thread on OSX, it should be reasonable to let it happen.
base::ThreadRestrictions::ScopedAllowIO allowIO;
- scoped_ptr<FileStream> fileStream(content::CreateFileStreamForDrop(
- &filePath, content::GetContentClient()->browser()->GetNetLog()));
- if (!fileStream)
+ base::File file(content::CreateFileForDrop(&filePath));
+ if (!file.IsValid())
return nil;
if (downloadURL_.is_valid()) {
scoped_refptr<DragDownloadFile> dragFileDownloader(new DragDownloadFile(
filePath,
- fileStream.Pass(),
+ file.Pass(),
downloadURL_,
content::Referrer(contents_->GetLastCommittedURL(),
dropData_->referrer_policy),
@@ -351,7 +328,7 @@ void PromiseWriterHelper(const DropData& drop_data,
FROM_HERE,
base::Bind(&PromiseWriterHelper,
*dropData_,
- base::Passed(&fileStream)));
+ base::Passed(&file)));
}
// The DragDownloadFile constructor may have altered the value of |filePath|
@@ -397,7 +374,7 @@ void PromiseWriterHelper(const DropData& drop_data,
// name.
std::string defaultName =
content::GetContentClient()->browser()->GetDefaultDownloadName();
- mimeType = UTF16ToUTF8(mimeType16);
+ mimeType = base::UTF16ToUTF8(mimeType16);
downloadFileName_ =
net::GenerateFileName(downloadURL_,
std::string(),
diff --git a/chromium/content/browser/web_contents/web_drag_source_win.cc b/chromium/content/browser/web_contents/web_drag_source_win.cc
deleted file mode 100644
index 6697d73ac2e..00000000000
--- a/chromium/content/browser/web_contents/web_drag_source_win.cc
+++ /dev/null
@@ -1,130 +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/web_contents/web_drag_source_win.h"
-
-#include "base/bind.h"
-#include "content/browser/renderer_host/render_view_host_impl.h"
-#include "content/browser/web_contents/web_contents_impl.h"
-#include "content/browser/web_contents/web_drag_utils_win.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/notification_source.h"
-#include "content/public/browser/notification_types.h"
-#include "ui/base/dragdrop/os_exchange_data.h"
-
-using blink::WebDragOperationNone;
-
-namespace content {
-namespace {
-
-static void GetCursorPositions(gfx::NativeWindow wnd, gfx::Point* client,
- gfx::Point* screen) {
- POINT cursor_pos;
- GetCursorPos(&cursor_pos);
- screen->SetPoint(cursor_pos.x, cursor_pos.y);
- ScreenToClient(wnd, &cursor_pos);
- client->SetPoint(cursor_pos.x, cursor_pos.y);
-}
-
-} // namespace
-
-///////////////////////////////////////////////////////////////////////////////
-// WebDragSource, public:
-
-WebDragSource::WebDragSource(gfx::NativeWindow source_wnd,
- WebContents* web_contents)
- : ui::DragSourceWin(),
- source_wnd_(source_wnd),
- web_contents_(static_cast<WebContentsImpl*>(web_contents)),
- effect_(DROPEFFECT_NONE),
- data_(NULL) {
- registrar_.Add(this, NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
- Source<WebContents>(web_contents));
- registrar_.Add(this, NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
- Source<WebContents>(web_contents));
-}
-
-WebDragSource::~WebDragSource() {
-}
-
-void WebDragSource::OnDragSourceCancel() {
- // Delegate to the UI thread if we do drag-and-drop in the background thread.
- if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
- BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- base::Bind(&WebDragSource::OnDragSourceCancel, this));
- return;
- }
-
- if (!web_contents_)
- return;
-
- gfx::Point client;
- gfx::Point screen;
- GetCursorPositions(source_wnd_, &client, &screen);
- web_contents_->DragSourceEndedAt(client.x(), client.y(),
- screen.x(), screen.y(),
- WebDragOperationNone);
-}
-
-void WebDragSource::OnDragSourceDrop() {
- DCHECK(data_);
- data_->SetInDragLoop(false);
- // On Windows, we check for drag end in IDropSource::QueryContinueDrag which
- // happens before IDropTarget::Drop is called. HTML5 requires the "dragend"
- // event to happen after the "drop" event. Since Windows calls these two
- // directly after each other we can just post a task to handle the
- // OnDragSourceDrop after the current task.
- BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- base::Bind(&WebDragSource::DelayedOnDragSourceDrop, this));
-}
-
-void WebDragSource::DelayedOnDragSourceDrop() {
- if (!web_contents_)
- return;
-
- gfx::Point client;
- gfx::Point screen;
- GetCursorPositions(source_wnd_, &client, &screen);
- web_contents_->DragSourceEndedAt(client.x(), client.y(), screen.x(),
- screen.y(), WinDragOpToWebDragOp(effect_));
-}
-
-void WebDragSource::OnDragSourceMove() {
- // Delegate to the UI thread if we do drag-and-drop in the background thread.
- if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
- BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- base::Bind(&WebDragSource::OnDragSourceMove, this));
- return;
- }
-
- if (!web_contents_)
- return;
-
- gfx::Point client;
- gfx::Point screen;
- GetCursorPositions(source_wnd_, &client, &screen);
- web_contents_->DragSourceMovedTo(client.x(), client.y(),
- screen.x(), screen.y());
-}
-
-void WebDragSource::Observe(int type,
- const NotificationSource& source,
- const NotificationDetails& details) {
- if (type == NOTIFICATION_RENDER_VIEW_HOST_CHANGED) {
- // When the WebContents get swapped, our render view host goes away.
- // That's OK, we can continue the drag, we just can't send messages back to
- // our drag source.
- web_contents_ = NULL;
- } else if (type == NOTIFICATION_WEB_CONTENTS_DISCONNECTED) {
- // This could be possible when we close the tab and the source is still
- // being used in DoDragDrop at the time that the virtual file is being
- // downloaded.
- web_contents_ = NULL;
- }
-}
-
-} // namespace content
diff --git a/chromium/content/browser/web_contents/web_drag_source_win.h b/chromium/content/browser/web_contents/web_drag_source_win.h
deleted file mode 100644
index 72d4a1692a2..00000000000
--- a/chromium/content/browser/web_contents/web_drag_source_win.h
+++ /dev/null
@@ -1,79 +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_WEB_CONTENTS_WEB_DRAG_SOURCE_WIN_H_
-#define CONTENT_BROWSER_WEB_CONTENTS_WEB_DRAG_SOURCE_WIN_H_
-
-#include "base/basictypes.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
-#include "ui/base/dragdrop/drag_source_win.h"
-#include "ui/gfx/native_widget_types.h"
-#include "ui/gfx/point.h"
-
-namespace ui {
-class OSExchangeData;
-} // namespace ui
-
-namespace content {
-class RenderViewHost;
-class WebContents;
-class WebContentsImpl;
-
-// An IDropSource implementation for a WebContentsImpl. Handles notifications
-// sent by an active drag-drop operation as the user mouses over other drop
-// targets on their system. This object tells Windows whether or not the drag
-// should continue, and supplies the appropriate cursors.
-class WebDragSource : public ui::DragSourceWin,
- public NotificationObserver {
- public:
- // Create a new DragSource for a given HWND and WebContents.
- WebDragSource(gfx::NativeWindow source_wnd, WebContents* web_contents);
- virtual ~WebDragSource();
-
- // NotificationObserver implementation.
- virtual void Observe(int type,
- const NotificationSource& source,
- const NotificationDetails& details);
-
- void set_effect(DWORD effect) { effect_ = effect; }
- // Used to set the active data object for the current drag operation. The
- // caller must ensure that |data| is not destroyed before the nested drag loop
- // terminates.
- void set_data(ui::OSExchangeData* data) { data_ = data; }
-
- protected:
- // ui::DragSourceWin
- virtual void OnDragSourceCancel();
- virtual void OnDragSourceDrop();
- virtual void OnDragSourceMove();
-
- private:
- // Cannot construct thusly.
- WebDragSource();
-
- // OnDragSourceDrop schedules its main work to be done after IDropTarget::Drop
- // by posting a task to this function.
- void DelayedOnDragSourceDrop();
-
- // Keep a reference to the window so we can translate the cursor position.
- gfx::NativeWindow source_wnd_;
-
- // We use this as a channel to the renderer to tell it about various drag
- // drop events that it needs to know about (such as when a drag operation it
- // initiated terminates).
- WebContentsImpl* web_contents_;
-
- NotificationRegistrar registrar_;
-
- DWORD effect_;
-
- ui::OSExchangeData* data_;
-
- DISALLOW_COPY_AND_ASSIGN(WebDragSource);
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_WEB_CONTENTS_WEB_DRAG_SOURCE_WIN_H_
diff --git a/chromium/content/browser/webkit_browsertest.cc b/chromium/content/browser/webkit_browsertest.cc
index 5394f5bcc55..4202eecb3ef 100644
--- a/chromium/content/browser/webkit_browsertest.cc
+++ b/chromium/content/browser/webkit_browsertest.cc
@@ -4,9 +4,9 @@
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/web_contents.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/content_browser_test.h"
-#include "content/test/content_browser_test_utils.h"
#include "content/test/net/url_request_abort_on_end_job.h"
namespace content {
@@ -76,19 +76,4 @@ IN_PROC_BROWSER_TEST_F(WebKitBrowserTest, PrerenderNoCrash) {
EXPECT_FALSE(shell()->web_contents()->IsCrashed());
}
-// This is a browser test because DumpRenderTree doesn't run nested message
-// loops. The failure case was that a nested message triggered from an element
-// that has signalled an error but had an open request would receive a body for
-// the request and crash/fail an assertion.
-const char kErrorBodyNoCrash[] =
- "files/error-body-no-crash.html";
-IN_PROC_BROWSER_TEST_F(WebKitBrowserTest, ErrorBodyNoCrash) {
- ASSERT_TRUE(test_server()->Start());
- GURL url = test_server()->GetURL(kErrorBodyNoCrash);
-
- NavigateToURL(shell(), url);
-
- EXPECT_FALSE(shell()->web_contents()->IsCrashed());
-}
-
} // namespace content
diff --git a/chromium/content/browser/webui/content_web_ui_controller_factory.cc b/chromium/content/browser/webui/content_web_ui_controller_factory.cc
index 45a38adb707..7a9263111f3 100644
--- a/chromium/content/browser/webui/content_web_ui_controller_factory.cc
+++ b/chromium/content/browser/webui/content_web_ui_controller_factory.cc
@@ -8,12 +8,17 @@
#include "content/browser/gpu/gpu_internals_ui.h"
#include "content/browser/indexed_db/indexed_db_internals_ui.h"
#include "content/browser/media/media_internals_ui.h"
-#include "content/browser/media/webrtc_internals_ui.h"
+#include "content/browser/service_worker/service_worker_internals_ui.h"
#include "content/browser/tracing/tracing_ui.h"
+#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "content/public/common/url_constants.h"
+#if defined(ENABLE_WEBRTC)
+#include "content/browser/media/webrtc_internals_ui.h"
+#endif
+
namespace content {
WebUI::TypeID ContentWebUIControllerFactory::GetWebUIType(
@@ -25,6 +30,7 @@ WebUI::TypeID ContentWebUIControllerFactory::GetWebUIType(
url.host() == kChromeUIGpuHost ||
url.host() == kChromeUIIndexedDBInternalsHost ||
url.host() == kChromeUIMediaInternalsHost ||
+ url.host() == kChromeUIServiceWorkerInternalsHost ||
url.host() == kChromeUIAccessibilityHost) {
return const_cast<ContentWebUIControllerFactory*>(this);
}
@@ -43,8 +49,6 @@ bool ContentWebUIControllerFactory::UseWebUIBindingsForURL(
WebUIController* ContentWebUIControllerFactory::CreateWebUIControllerForURL(
WebUI* web_ui, const GURL& url) const {
- if (url.host() == kChromeUIWebRTCInternalsHost)
- return new WebRTCInternalsUI(web_ui);
if (url.host() == kChromeUIGpuHost)
return new GpuInternalsUI(web_ui);
if (url.host() == kChromeUIIndexedDBInternalsHost)
@@ -53,11 +57,18 @@ WebUIController* ContentWebUIControllerFactory::CreateWebUIControllerForURL(
return new MediaInternalsUI(web_ui);
if (url.host() == kChromeUIAccessibilityHost)
return new AccessibilityUI(web_ui);
+ if (url.host() == kChromeUIServiceWorkerInternalsHost)
+ return new ServiceWorkerInternalsUI(web_ui);
#if !defined(OS_ANDROID)
if (url.host() == kChromeUITracingHost)
return new TracingUI(web_ui);
#endif
+#if defined(ENABLE_WEBRTC)
+ if (url.host() == kChromeUIWebRTCInternalsHost)
+ return new WebRTCInternalsUI(web_ui);
+#endif
+
return NULL;
}
diff --git a/chromium/content/browser/webui/generic_handler.cc b/chromium/content/browser/webui/generic_handler.cc
index 56b9e0766c8..058382fa8a8 100644
--- a/chromium/content/browser/webui/generic_handler.cc
+++ b/chromium/content/browser/webui/generic_handler.cc
@@ -25,7 +25,7 @@ void GenericHandler::RegisterMessages() {
base::Bind(&GenericHandler::HandleNavigateToUrl, base::Unretained(this)));
}
-void GenericHandler::HandleNavigateToUrl(const ListValue* args) {
+void GenericHandler::HandleNavigateToUrl(const base::ListValue* args) {
std::string url_string;
std::string target_string;
double button;
diff --git a/chromium/content/browser/webui/shared_resources_data_source.cc b/chromium/content/browser/webui/shared_resources_data_source.cc
index 45ddc793a65..aa1dc390100 100644
--- a/chromium/content/browser/webui/shared_resources_data_source.cc
+++ b/chromium/content/browser/webui/shared_resources_data_source.cc
@@ -69,7 +69,7 @@ std::string SharedResourcesDataSource::GetSource() const {
void SharedResourcesDataSource::StartDataRequest(
const std::string& path,
int render_process_id,
- int render_view_id,
+ int render_frame_id,
const content::URLDataSource::GotDataCallback& callback) {
int idr = PathToIDR(path);
DCHECK_NE(-1, idr) << " path: " << path;
diff --git a/chromium/content/browser/webui/shared_resources_data_source.h b/chromium/content/browser/webui/shared_resources_data_source.h
index cf9c24c6eec..9c3a3c464ef 100644
--- a/chromium/content/browser/webui/shared_resources_data_source.h
+++ b/chromium/content/browser/webui/shared_resources_data_source.h
@@ -19,7 +19,7 @@ class SharedResourcesDataSource : public content::URLDataSource {
virtual void StartDataRequest(
const std::string& path,
int render_process_id,
- int render_view_id,
+ int render_frame_id,
const content::URLDataSource::GotDataCallback& callback) OVERRIDE;
virtual std::string GetMimeType(const std::string&) const OVERRIDE;
diff --git a/chromium/content/browser/webui/url_data_manager_backend.cc b/chromium/content/browser/webui/url_data_manager_backend.cc
index 1e378007502..9f661af2f6a 100644
--- a/chromium/content/browser/webui/url_data_manager_backend.cc
+++ b/chromium/content/browser/webui/url_data_manager_backend.cc
@@ -10,6 +10,7 @@
#include "base/bind.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
+#include "base/debug/alias.h"
#include "base/debug/trace_event.h"
#include "base/lazy_instance.h"
#include "base/memory/ref_counted.h"
@@ -18,6 +19,7 @@
#include "base/message_loop/message_loop.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
+#include "content/browser/appcache/view_appcache_internals_job.h"
#include "content/browser/fileapi/chrome_blob_storage_context.h"
#include "content/browser/histogram_internals_request_job.h"
#include "content/browser/net/view_blob_internals_job_factory.h"
@@ -26,6 +28,7 @@
#include "content/browser/tcmalloc_internals_request_job.h"
#include "content/browser/webui/shared_resources_data_source.h"
#include "content/browser/webui/url_data_source_impl.h"
+#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/render_process_host.h"
@@ -40,9 +43,8 @@
#include "net/url_request/url_request_job.h"
#include "net/url_request/url_request_job_factory.h"
#include "url/url_util.h"
-#include "webkit/browser/appcache/view_appcache_internals_job.h"
-using appcache::AppCacheService;
+using appcache::AppCacheServiceImpl;
namespace content {
@@ -55,36 +57,36 @@ const char kChromeURLContentSecurityPolicyHeaderBase[] =
const char kChromeURLXFrameOptionsHeader[] = "X-Frame-Options: DENY";
+const int kNoRenderProcessId = -1;
+
bool SchemeIsInSchemes(const std::string& scheme,
const std::vector<std::string>& schemes) {
return std::find(schemes.begin(), schemes.end(), scheme) != schemes.end();
}
-// Parse a URL into the components used to resolve its request. |source_name|
-// is the hostname and |path| is the remaining portion of the URL.
-void URLToRequest(const GURL& url, std::string* source_name,
- std::string* path) {
+// Returns whether |url| passes some sanity checks and is a valid GURL.
+bool CheckURLIsValid(const GURL& url) {
std::vector<std::string> additional_schemes;
- DCHECK(url.SchemeIs(chrome::kChromeDevToolsScheme) ||
- url.SchemeIs(chrome::kChromeUIScheme) ||
+ DCHECK(url.SchemeIs(kChromeDevToolsScheme) || url.SchemeIs(kChromeUIScheme) ||
(GetContentClient()->browser()->GetAdditionalWebUISchemes(
- &additional_schemes),
+ &additional_schemes),
SchemeIsInSchemes(url.scheme(), additional_schemes)));
if (!url.is_valid()) {
NOTREACHED();
- return;
+ return false;
}
- // Our input looks like: chrome://source_name/extra_bits?foo .
- // So the url's "host" is our source, and everything after the host is
- // the path.
- source_name->assign(url.host());
+ return true;
+}
+// Parse |url| to get the path which will be used to resolve the request. The
+// path is the remaining portion after the scheme and hostname.
+void URLToRequestPath(const GURL& url, std::string* path) {
const std::string& spec = url.possibly_invalid_spec();
- const url_parse::Parsed& parsed = url.parsed_for_possibly_invalid_spec();
+ const url::Parsed& parsed = url.parsed_for_possibly_invalid_spec();
// + 1 to skip the slash at the beginning of the path.
- int offset = parsed.CountCharactersBefore(url_parse::Parsed::PATH, false) + 1;
+ int offset = parsed.CountCharactersBefore(url::Parsed::PATH, false) + 1;
if (offset < static_cast<int>(spec.size()))
path->assign(spec.substr(offset));
@@ -162,7 +164,13 @@ class URLRequestChromeJob : public net::URLRequestJob,
// Helper for Start(), to let us start asynchronously.
// (This pattern is shared by most net::URLRequestJob implementations.)
- void StartAsync();
+ void StartAsync(bool allowed);
+
+ // Called on the UI thread to check if this request is allowed.
+ static void CheckStoragePartitionMatches(
+ int render_process_id,
+ const GURL& url,
+ const base::WeakPtr<URLRequestChromeJob>& job);
// Do the actual copy from data_ (the data we're serving) into |buf|.
// Separate from ReadRawData so we can handle async I/O.
@@ -231,12 +239,16 @@ URLRequestChromeJob::~URLRequestChromeJob() {
}
void URLRequestChromeJob::Start() {
- // Start reading asynchronously so that all error reporting and data
- // callbacks happen as they would for network requests.
- base::MessageLoop::current()->PostTask(
+ int render_process_id, unused;
+ bool is_renderer_request = ResourceRequestInfo::GetRenderFrameForRequest(
+ request_, &render_process_id, &unused);
+ if (!is_renderer_request)
+ render_process_id = kNoRenderProcessId;
+ BrowserThread::PostTask(
+ BrowserThread::UI,
FROM_HERE,
- base::Bind(&URLRequestChromeJob::StartAsync, weak_factory_.GetWeakPtr()));
-
+ base::Bind(&URLRequestChromeJob::CheckStoragePartitionMatches,
+ render_process_id, request_->url(), AsWeakPtr()));
TRACE_EVENT_ASYNC_BEGIN1("browser", "DataManager:Request", this, "URL",
request_->url().possibly_invalid_spec());
}
@@ -330,6 +342,11 @@ bool URLRequestChromeJob::ReadRawData(net::IOBuffer* buf, int buf_size,
void URLRequestChromeJob::CompleteRead(net::IOBuffer* buf, int buf_size,
int* bytes_read) {
+ // http://crbug.com/373841
+ char url_buf[128];
+ base::strlcpy(url_buf, request_->url().spec().c_str(), arraysize(url_buf));
+ base::debug::Alias(url_buf);
+
int remaining = static_cast<int>(data_->size()) - data_offset_;
if (buf_size > remaining)
buf_size = remaining;
@@ -340,11 +357,46 @@ void URLRequestChromeJob::CompleteRead(net::IOBuffer* buf, int buf_size,
*bytes_read = buf_size;
}
-void URLRequestChromeJob::StartAsync() {
+void URLRequestChromeJob::CheckStoragePartitionMatches(
+ int render_process_id,
+ const GURL& url,
+ const base::WeakPtr<URLRequestChromeJob>& job) {
+ // The embedder could put some webui pages in separate storage partition.
+ // RenderProcessHostImpl::IsSuitableHost would guard against top level pages
+ // being in the same process. We do an extra check to guard against an
+ // exploited renderer pretending to add them as a subframe. We skip this check
+ // for resources.
+ bool allowed = false;
+ std::vector<std::string> hosts;
+ GetContentClient()->
+ browser()->GetAdditionalWebUIHostsToIgnoreParititionCheck(&hosts);
+ if (url.SchemeIs(kChromeUIScheme) &&
+ (url.SchemeIs(kChromeUIScheme) ||
+ std::find(hosts.begin(), hosts.end(), url.host()) != hosts.end())) {
+ allowed = true;
+ } else if (render_process_id == kNoRenderProcessId) {
+ // Request was not issued by renderer.
+ allowed = true;
+ } else {
+ RenderProcessHost* process = RenderProcessHost::FromID(render_process_id);
+ if (process) {
+ StoragePartition* partition = BrowserContext::GetStoragePartitionForSite(
+ process->GetBrowserContext(), url);
+ allowed = partition == process->GetStoragePartition();
+ }
+ }
+
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&URLRequestChromeJob::StartAsync, job, allowed));
+}
+
+void URLRequestChromeJob::StartAsync(bool allowed) {
if (!request_)
return;
- if (!backend_->StartRequest(request_, this)) {
+ if (!allowed || !backend_->StartRequest(request_, this)) {
NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED,
net::ERR_INVALID_URL));
}
@@ -376,7 +428,7 @@ class ChromeProtocolHandler
// |is_incognito| should be set for incognito profiles.
ChromeProtocolHandler(ResourceContext* resource_context,
bool is_incognito,
- AppCacheService* appcache_service,
+ AppCacheServiceImpl* appcache_service,
ChromeBlobStorageContext* blob_storage_context)
: resource_context_(resource_context),
is_incognito_(is_incognito),
@@ -395,9 +447,9 @@ class ChromeProtocolHandler
network_delegate);
// Next check for chrome://appcache-internals/, which uses its own job type.
- if (request->url().SchemeIs(chrome::kChromeUIScheme) &&
+ if (request->url().SchemeIs(kChromeUIScheme) &&
request->url().host() == kChromeUIAppCacheInternalsHost) {
- return appcache::ViewAppCacheInternalsJobFactory::CreateJobForRequest(
+ return ViewAppCacheInternalsJobFactory::CreateJobForRequest(
request, network_delegate, appcache_service_);
}
@@ -409,14 +461,14 @@ class ChromeProtocolHandler
#if defined(USE_TCMALLOC)
// Next check for chrome://tcmalloc/, which uses its own job type.
- if (request->url().SchemeIs(chrome::kChromeUIScheme) &&
+ if (request->url().SchemeIs(kChromeUIScheme) &&
request->url().host() == kChromeUITcmallocHost) {
return new TcmallocInternalsRequestJob(request, network_delegate);
}
#endif
// Next check for chrome://histograms/, which uses its own job type.
- if (request->url().SchemeIs(chrome::kChromeUIScheme) &&
+ if (request->url().SchemeIs(kChromeUIScheme) &&
request->url().host() == kChromeUIHistogramHost) {
return new HistogramInternalsRequestJob(request, network_delegate);
}
@@ -437,7 +489,7 @@ class ChromeProtocolHandler
// True when generated from an incognito profile.
const bool is_incognito_;
- AppCacheService* appcache_service_;
+ AppCacheServiceImpl* appcache_service_;
ChromeBlobStorageContext* blob_storage_context_;
DISALLOW_COPY_AND_ASSIGN(ChromeProtocolHandler);
@@ -466,7 +518,7 @@ net::URLRequestJobFactory::ProtocolHandler*
URLDataManagerBackend::CreateProtocolHandler(
content::ResourceContext* resource_context,
bool is_incognito,
- AppCacheService* appcache_service,
+ AppCacheServiceImpl* appcache_service,
ChromeBlobStorageContext* blob_storage_context) {
DCHECK(resource_context);
return new ChromeProtocolHandler(
@@ -498,20 +550,18 @@ bool URLDataManagerBackend::HasPendingJob(
bool URLDataManagerBackend::StartRequest(const net::URLRequest* request,
URLRequestChromeJob* job) {
- // Parse the URL into a request for a source and path.
- std::string source_name;
- std::string path;
- URLToRequest(request->url(), &source_name, &path);
-
- // Look up the data source for the request.
- DataSourceMap::iterator i = data_sources_.find(source_name);
- if (i == data_sources_.end())
+ if (!CheckURLIsValid(request->url()))
return false;
- URLDataSourceImpl* source = i->second.get();
+ URLDataSourceImpl* source = GetDataSourceFromURL(request->url());
+ if (!source)
+ return false;
if (!source->source()->ShouldServiceRequest(request))
return false;
+
+ std::string path;
+ URLToRequestPath(request->url(), &path);
source->source()->WillServiceRequest(request, &path);
// Save this request so we know where to send the data.
@@ -532,10 +582,10 @@ bool URLDataManagerBackend::StartRequest(const net::URLRequest* request,
// Look up additional request info to pass down.
int render_process_id = -1;
- int render_view_id = -1;
- ResourceRequestInfo::GetRenderViewForRequest(request,
- &render_process_id,
- &render_view_id);
+ int render_frame_id = -1;
+ ResourceRequestInfo::GetRenderFrameForRequest(request,
+ &render_process_id,
+ &render_frame_id);
// Forward along the request to the data source.
base::MessageLoop* target_message_loop =
@@ -549,7 +599,7 @@ bool URLDataManagerBackend::StartRequest(const net::URLRequest* request,
// on for this path. Call directly into it from this thread, the IO
// thread.
source->source()->StartDataRequest(
- path, render_process_id, render_view_id,
+ path, render_process_id, render_frame_id,
base::Bind(&URLDataSourceImpl::SendResponse, source, request_id));
} else {
// URLRequestChromeJob should receive mime type before data. This
@@ -568,16 +618,34 @@ bool URLDataManagerBackend::StartRequest(const net::URLRequest* request,
FROM_HERE,
base::Bind(&URLDataManagerBackend::CallStartRequest,
make_scoped_refptr(source), path, render_process_id,
- render_view_id, request_id));
+ render_frame_id, request_id));
}
return true;
}
+URLDataSourceImpl* URLDataManagerBackend::GetDataSourceFromURL(
+ const GURL& url) {
+ // The input usually looks like: chrome://source_name/extra_bits?foo
+ // so do a lookup using the host of the URL.
+ DataSourceMap::iterator i = data_sources_.find(url.host());
+ if (i != data_sources_.end())
+ return i->second.get();
+
+ // No match using the host of the URL, so do a lookup using the scheme for
+ // URLs on the form source_name://extra_bits/foo .
+ i = data_sources_.find(url.scheme() + "://");
+ if (i != data_sources_.end())
+ return i->second.get();
+
+ // No matches found, so give up.
+ return NULL;
+}
+
void URLDataManagerBackend::CallStartRequest(
scoped_refptr<URLDataSourceImpl> source,
const std::string& path,
int render_process_id,
- int render_view_id,
+ int render_frame_id,
int request_id) {
if (BrowserThread::CurrentlyOn(BrowserThread::UI) &&
render_process_id != -1 &&
@@ -591,7 +659,7 @@ void URLDataManagerBackend::CallStartRequest(
source->source()->StartDataRequest(
path,
render_process_id,
- render_view_id,
+ render_frame_id,
base::Bind(&URLDataSourceImpl::SendResponse, source, request_id));
}
diff --git a/chromium/content/browser/webui/url_data_manager_backend.h b/chromium/content/browser/webui/url_data_manager_backend.h
index e38fe1ee3f9..a307cdbfa10 100644
--- a/chromium/content/browser/webui/url_data_manager_backend.h
+++ b/chromium/content/browser/webui/url_data_manager_backend.h
@@ -19,7 +19,7 @@
class GURL;
namespace appcache {
-class AppCacheService;
+class AppCacheServiceImpl;
}
namespace base {
@@ -48,7 +48,7 @@ class URLDataManagerBackend : public base::SupportsUserData::Data {
static net::URLRequestJobFactory::ProtocolHandler* CreateProtocolHandler(
content::ResourceContext* resource_context,
bool is_incognito,
- appcache::AppCacheService* appcache_service,
+ appcache::AppCacheServiceImpl* appcache_service,
ChromeBlobStorageContext* blob_storage_context);
// Adds a DataSource to the collection of data sources.
@@ -77,7 +77,7 @@ class URLDataManagerBackend : public base::SupportsUserData::Data {
static void CallStartRequest(scoped_refptr<URLDataSourceImpl> source,
const std::string& path,
int render_process_id,
- int render_view_id,
+ int render_frame_id,
int request_id);
// Remove a request from the list of pending requests.
@@ -88,6 +88,10 @@ class URLDataManagerBackend : public base::SupportsUserData::Data {
// up to date.
bool HasPendingJob(URLRequestChromeJob* job) const;
+ // Look up the data source for the request. Returns the source if it is found,
+ // else NULL.
+ URLDataSourceImpl* GetDataSourceFromURL(const GURL& url);
+
// Custom sources of data, keyed by source path (e.g. "favicon").
DataSourceMap data_sources_;
diff --git a/chromium/content/browser/webui/web_ui_controller_factory_registry.cc b/chromium/content/browser/webui/web_ui_controller_factory_registry.cc
index ede404d9939..a80f52b8760 100644
--- a/chromium/content/browser/webui/web_ui_controller_factory_registry.cc
+++ b/chromium/content/browser/webui/web_ui_controller_factory_registry.cc
@@ -5,6 +5,7 @@
#include "content/browser/webui/web_ui_controller_factory_registry.h"
#include "base/lazy_instance.h"
+#include "content/browser/frame_host/debug_urls.h"
#include "content/public/common/url_constants.h"
#include "url/gurl.h"
@@ -78,23 +79,13 @@ bool WebUIControllerFactoryRegistry::UseWebUIBindingsForURL(
bool WebUIControllerFactoryRegistry::IsURLAcceptableForWebUI(
BrowserContext* browser_context,
- const GURL& url,
- bool data_urls_allowed) const {
+ const GURL& url) const {
return UseWebUIForURL(browser_context, url) ||
- // javascript: URLs are allowed to run in Web UI pages.
- url.SchemeIs(kJavaScriptScheme) ||
// It's possible to load about:blank in a Web UI renderer.
// See http://crbug.com/42547
- url.spec() == kAboutBlankURL ||
- // Chrome URLs crash, kill, hang, and shorthang are allowed.
- url == GURL(kChromeUICrashURL) ||
- url == GURL(kChromeUIKillURL) ||
- url == GURL(kChromeUIHangURL) ||
- url == GURL(kChromeUIShorthangURL) ||
- // Data URLs are usually not allowed in WebUI for security reasons.
- // BalloonHosts are one exception needed by ChromeOS, and are safe because
- // they cannot be scripted by other pages.
- (data_urls_allowed && url.SchemeIs(chrome::kDataScheme));
+ url.spec() == url::kAboutBlankURL ||
+ // javascript: and debug URLs like chrome://kill are allowed.
+ IsRendererDebugURL(url);
}
WebUIControllerFactoryRegistry::WebUIControllerFactoryRegistry() {
diff --git a/chromium/content/browser/webui/web_ui_controller_factory_registry.h b/chromium/content/browser/webui/web_ui_controller_factory_registry.h
index c2dd9bb7fab..6d0da82edeb 100644
--- a/chromium/content/browser/webui/web_ui_controller_factory_registry.h
+++ b/chromium/content/browser/webui/web_ui_controller_factory_registry.h
@@ -32,8 +32,7 @@ class CONTENT_EXPORT WebUIControllerFactoryRegistry
// URLs that UseWebUIForURL returns true for, and also URLs that can be loaded
// by normal tabs such as javascript: URLs or about:hang.
bool IsURLAcceptableForWebUI(BrowserContext* browser_context,
- const GURL& url,
- bool data_urls_allowed) const;
+ const GURL& url) const;
private:
friend struct DefaultSingletonTraits<WebUIControllerFactoryRegistry>;
diff --git a/chromium/content/browser/webui/web_ui_data_source_impl.cc b/chromium/content/browser/webui/web_ui_data_source_impl.cc
index 6775eb739da..052396746fe 100644
--- a/chromium/content/browser/webui/web_ui_data_source_impl.cc
+++ b/chromium/content/browser/webui/web_ui_data_source_impl.cc
@@ -10,15 +10,41 @@
#include "base/memory/ref_counted_memory.h"
#include "base/strings/string_util.h"
#include "content/public/common/content_client.h"
+#include "grit/content_resources.h"
+#include "mojo/public/js/bindings/constants.h"
#include "ui/base/webui/jstemplate_builder.h"
#include "ui/base/webui/web_ui_util.h"
namespace content {
+// static
WebUIDataSource* WebUIDataSource::Create(const std::string& source_name) {
return new WebUIDataSourceImpl(source_name);
}
+// static
+WebUIDataSource* WebUIDataSource::AddMojoDataSource(
+ BrowserContext* browser_context) {
+ WebUIDataSource* mojo_source = Create("mojo");
+
+ static const struct {
+ const char* path;
+ int id;
+ } resources[] = {
+ { mojo::kCodecModuleName, IDR_MOJO_CODEC_JS },
+ { mojo::kConnectionModuleName, IDR_MOJO_CONNECTION_JS },
+ { mojo::kConnectorModuleName, IDR_MOJO_CONNECTOR_JS },
+ { mojo::kRouterModuleName, IDR_MOJO_ROUTER_JS },
+ { mojo::kUnicodeModuleName, IDR_MOJO_UNICODE_JS },
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(resources); ++i)
+ mojo_source->AddResourcePath(resources[i].path, resources[i].id);
+
+ URLDataManager::AddWebUIDataSource(browser_context, mojo_source);
+ return mojo_source;
+}
+
+// static
void WebUIDataSource::Add(BrowserContext* browser_context,
WebUIDataSource* source) {
URLDataManager::AddWebUIDataSource(browser_context, source);
@@ -44,9 +70,9 @@ class WebUIDataSourceImpl::InternalDataSource : public URLDataSource {
virtual void StartDataRequest(
const std::string& path,
int render_process_id,
- int render_view_id,
+ int render_frame_id,
const URLDataSource::GotDataCallback& callback) OVERRIDE {
- return parent_->StartDataRequest(path, render_process_id, render_view_id,
+ return parent_->StartDataRequest(path, render_process_id, render_frame_id,
callback);
}
virtual bool ShouldReplaceExistingSource() const OVERRIDE {
@@ -188,7 +214,7 @@ std::string WebUIDataSourceImpl::GetMimeType(const std::string& path) const {
void WebUIDataSourceImpl::StartDataRequest(
const std::string& path,
int render_process_id,
- int render_view_id,
+ int render_frame_id,
const URLDataSource::GotDataCallback& callback) {
if (!filter_callback_.is_null() &&
filter_callback_.Run(path, callback)) {
diff --git a/chromium/content/browser/webui/web_ui_data_source_impl.h b/chromium/content/browser/webui/web_ui_data_source_impl.h
index 91ec087cfc0..9a015899c1a 100644
--- a/chromium/content/browser/webui/web_ui_data_source_impl.h
+++ b/chromium/content/browser/webui/web_ui_data_source_impl.h
@@ -76,7 +76,7 @@ class CONTENT_EXPORT WebUIDataSourceImpl
void StartDataRequest(
const std::string& path,
int render_process_id,
- int render_view_id,
+ int render_frame_id,
const URLDataSource::GotDataCallback& callback);
void disable_set_font_strings_for_testing() {
diff --git a/chromium/content/browser/webui/web_ui_data_source_unittest.cc b/chromium/content/browser/webui/web_ui_data_source_unittest.cc
index a5da7eb8211..97fa85ec82f 100644
--- a/chromium/content/browser/webui/web_ui_data_source_unittest.cc
+++ b/chromium/content/browser/webui/web_ui_data_source_unittest.cc
@@ -27,7 +27,7 @@ class TestClient : public TestContentClient {
virtual base::string16 GetLocalizedString(int message_id) const OVERRIDE {
if (message_id == kDummyStringId)
- return UTF8ToUTF16(kDummyString);
+ return base::UTF8ToUTF16(kDummyString);
return base::string16();
}
@@ -37,12 +37,10 @@ class TestClient : public TestContentClient {
base::RefCountedStaticMemory* bytes = NULL;
if (resource_id == kDummyDefaultResourceId) {
bytes = new base::RefCountedStaticMemory(
- reinterpret_cast<const unsigned char*>(kDummyDefaultResource),
- arraysize(kDummyDefaultResource));
+ kDummyDefaultResource, arraysize(kDummyDefaultResource));
} else if (resource_id == kDummyResourceId) {
bytes = new base::RefCountedStaticMemory(
- reinterpret_cast<const unsigned char*>(kDummytResource),
- arraysize(kDummytResource));
+ kDummytResource, arraysize(kDummytResource));
}
return bytes;
}
@@ -92,19 +90,17 @@ class WebUIDataSourceTest : public testing::Test {
TEST_F(WebUIDataSourceTest, EmptyStrings) {
source()->SetJsonPath("strings.js");
StartDataRequest("strings.js");
- std::string result(reinterpret_cast<const char*>(
- result_data_->front()), result_data_->size());
+ std::string result(result_data_->front_as<char>(), result_data_->size());
EXPECT_NE(result.find("var templateData = {"), std::string::npos);
EXPECT_NE(result.find("};"), std::string::npos);
}
TEST_F(WebUIDataSourceTest, SomeStrings) {
source()->SetJsonPath("strings.js");
- source()->AddString("planet", ASCIIToUTF16("pluto"));
+ source()->AddString("planet", base::ASCIIToUTF16("pluto"));
source()->AddLocalizedString("button", kDummyStringId);
StartDataRequest("strings.js");
- std::string result(reinterpret_cast<const char*>(
- result_data_->front()), result_data_->size());
+ std::string result(result_data_->front_as<char>(), result_data_->size());
EXPECT_NE(result.find("\"planet\":\"pluto\""), std::string::npos);
EXPECT_NE(result.find("\"button\":\"foo\""), std::string::npos);
}
@@ -112,14 +108,10 @@ TEST_F(WebUIDataSourceTest, SomeStrings) {
TEST_F(WebUIDataSourceTest, DefaultResource) {
source()->SetDefaultResource(kDummyDefaultResourceId);
StartDataRequest("foobar" );
- std::string result(
- reinterpret_cast<const char*>(result_data_->front()),
- result_data_->size());
+ std::string result(result_data_->front_as<char>(), result_data_->size());
EXPECT_NE(result.find(kDummyDefaultResource), std::string::npos);
StartDataRequest("strings.js");
- result = std::string(
- reinterpret_cast<const char*>(result_data_->front()),
- result_data_->size());
+ result = std::string(result_data_->front_as<char>(), result_data_->size());
EXPECT_NE(result.find(kDummyDefaultResource), std::string::npos);
}
@@ -127,14 +119,10 @@ TEST_F(WebUIDataSourceTest, NamedResource) {
source()->SetDefaultResource(kDummyDefaultResourceId);
source()->AddResourcePath("foobar", kDummyResourceId);
StartDataRequest("foobar");
- std::string result(
- reinterpret_cast<const char*>(result_data_->front()),
- result_data_->size());
+ std::string result(result_data_->front_as<char>(), result_data_->size());
EXPECT_NE(result.find(kDummytResource), std::string::npos);
StartDataRequest("strings.js");
- result = std::string(
- reinterpret_cast<const char*>(result_data_->front()),
- result_data_->size());
+ result = std::string(result_data_->front_as<char>(), result_data_->size());
EXPECT_NE(result.find(kDummyDefaultResource), std::string::npos);
}
diff --git a/chromium/content/browser/webui/web_ui_impl.cc b/chromium/content/browser/webui/web_ui_impl.cc
index 4774e4f17ac..340a047b398 100644
--- a/chromium/content/browser/webui/web_ui_impl.cc
+++ b/chromium/content/browser/webui/web_ui_impl.cc
@@ -10,13 +10,13 @@
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/renderer_host/dip_util.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
-#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/browser/web_contents/web_contents_view.h"
#include "content/browser/webui/web_ui_controller_factory_registry.h"
#include "content/common/view_messages.h"
#include "content/public/browser/content_browser_client.h"
-#include "content/public/browser/web_contents_delegate.h"
-#include "content/public/browser/web_contents_view.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_ui_controller.h"
#include "content/public/browser/web_ui_message_handler.h"
#include "content/public/common/bindings_policy.h"
@@ -29,18 +29,18 @@ const WebUI::TypeID WebUI::kNoWebUI = NULL;
// static
base::string16 WebUI::GetJavascriptCall(
const std::string& function_name,
- const std::vector<const Value*>& arg_list) {
+ const std::vector<const base::Value*>& arg_list) {
base::string16 parameters;
std::string json;
for (size_t i = 0; i < arg_list.size(); ++i) {
if (i > 0)
- parameters += char16(',');
+ parameters += base::char16(',');
base::JSONWriter::Write(arg_list[i], &json);
- parameters += UTF8ToUTF16(json);
+ parameters += base::UTF8ToUTF16(json);
}
- return ASCIIToUTF16(function_name) +
- char16('(') + parameters + char16(')') + char16(';');
+ return base::ASCIIToUTF16(function_name) +
+ base::char16('(') + parameters + base::char16(')') + base::char16(';');
}
WebUIImpl::WebUIImpl(WebContents* contents)
@@ -69,13 +69,11 @@ bool WebUIImpl::OnMessageReceived(const IPC::Message& message) {
void WebUIImpl::OnWebUISend(const GURL& source_url,
const std::string& message,
- const ListValue& args) {
- WebContentsDelegate* delegate = web_contents_->GetDelegate();
- bool data_urls_allowed = delegate && delegate->CanLoadDataURLsInWebUI();
+ const base::ListValue& args) {
if (!ChildProcessSecurityPolicyImpl::GetInstance()->
HasWebUIBindings(web_contents_->GetRenderProcessHost()->GetID()) ||
!WebUIControllerFactoryRegistry::GetInstance()->IsURLAcceptableForWebUI(
- web_contents_->GetBrowserContext(), source_url, data_urls_allowed)) {
+ web_contents_->GetBrowserContext(), source_url)) {
NOTREACHED() << "Blocked unauthorized use of WebUIBindings.";
return;
}
@@ -93,8 +91,6 @@ void WebUIImpl::RenderViewCreated(RenderViewHost* render_view_host) {
#if defined(TOOLKIT_VIEWS)
render_view_host->SetWebUIProperty("toolkit", "views");
-#elif defined(TOOLKIT_GTK)
- render_view_host->SetWebUIProperty("toolkit", "GTK");
#endif // defined(TOOLKIT_VIEWS)
}
@@ -102,7 +98,7 @@ WebContents* WebUIImpl::GetWebContents() const {
return web_contents_;
}
-ui::ScaleFactor WebUIImpl::GetDeviceScaleFactor() const {
+float WebUIImpl::GetDeviceScaleFactor() const {
return GetScaleFactorForView(web_contents_->GetRenderWidgetHostView());
}
@@ -130,8 +126,8 @@ void WebUIImpl::SetBindings(int bindings) {
bindings_ = bindings;
}
-void WebUIImpl::SetFrameXPath(const std::string& xpath) {
- frame_xpath_ = xpath;
+void WebUIImpl::OverrideJavaScriptFrame(const std::string& frame_name) {
+ frame_name_ = frame_name;
}
WebUIController* WebUIImpl::GetController() const {
@@ -143,24 +139,24 @@ void WebUIImpl::SetController(WebUIController* controller) {
}
void WebUIImpl::CallJavascriptFunction(const std::string& function_name) {
- DCHECK(IsStringASCII(function_name));
- base::string16 javascript = ASCIIToUTF16(function_name + "();");
+ DCHECK(base::IsStringASCII(function_name));
+ base::string16 javascript = base::ASCIIToUTF16(function_name + "();");
ExecuteJavascript(javascript);
}
void WebUIImpl::CallJavascriptFunction(const std::string& function_name,
- const Value& arg) {
- DCHECK(IsStringASCII(function_name));
- std::vector<const Value*> args;
+ const base::Value& arg) {
+ DCHECK(base::IsStringASCII(function_name));
+ std::vector<const base::Value*> args;
args.push_back(&arg);
ExecuteJavascript(GetJavascriptCall(function_name, args));
}
void WebUIImpl::CallJavascriptFunction(
const std::string& function_name,
- const Value& arg1, const Value& arg2) {
- DCHECK(IsStringASCII(function_name));
- std::vector<const Value*> args;
+ const base::Value& arg1, const base::Value& arg2) {
+ DCHECK(base::IsStringASCII(function_name));
+ std::vector<const base::Value*> args;
args.push_back(&arg1);
args.push_back(&arg2);
ExecuteJavascript(GetJavascriptCall(function_name, args));
@@ -168,9 +164,9 @@ void WebUIImpl::CallJavascriptFunction(
void WebUIImpl::CallJavascriptFunction(
const std::string& function_name,
- const Value& arg1, const Value& arg2, const Value& arg3) {
- DCHECK(IsStringASCII(function_name));
- std::vector<const Value*> args;
+ const base::Value& arg1, const base::Value& arg2, const base::Value& arg3) {
+ DCHECK(base::IsStringASCII(function_name));
+ std::vector<const base::Value*> args;
args.push_back(&arg1);
args.push_back(&arg2);
args.push_back(&arg3);
@@ -179,12 +175,12 @@ void WebUIImpl::CallJavascriptFunction(
void WebUIImpl::CallJavascriptFunction(
const std::string& function_name,
- const Value& arg1,
- const Value& arg2,
- const Value& arg3,
- const Value& arg4) {
- DCHECK(IsStringASCII(function_name));
- std::vector<const Value*> args;
+ const base::Value& arg1,
+ const base::Value& arg2,
+ const base::Value& arg3,
+ const base::Value& arg4) {
+ DCHECK(base::IsStringASCII(function_name));
+ std::vector<const base::Value*> args;
args.push_back(&arg1);
args.push_back(&arg2);
args.push_back(&arg3);
@@ -194,8 +190,8 @@ void WebUIImpl::CallJavascriptFunction(
void WebUIImpl::CallJavascriptFunction(
const std::string& function_name,
- const std::vector<const Value*>& args) {
- DCHECK(IsStringASCII(function_name));
+ const std::vector<const base::Value*>& args) {
+ DCHECK(base::IsStringASCII(function_name));
ExecuteJavascript(GetJavascriptCall(function_name, args));
}
@@ -231,9 +227,33 @@ void WebUIImpl::AddMessageHandler(WebUIMessageHandler* handler) {
}
void WebUIImpl::ExecuteJavascript(const base::string16& javascript) {
- static_cast<RenderViewHostImpl*>(
- web_contents_->GetRenderViewHost())->ExecuteJavascriptInWebFrame(
- ASCIIToUTF16(frame_xpath_), javascript);
+ RenderFrameHost* target_frame = TargetFrame();
+ if (target_frame)
+ target_frame->ExecuteJavaScript(javascript);
+}
+
+RenderFrameHost* WebUIImpl::TargetFrame() {
+ if (frame_name_.empty())
+ return web_contents_->GetMainFrame();
+
+ std::set<RenderFrameHost*> frame_set;
+ web_contents_->ForEachFrame(base::Bind(&WebUIImpl::AddToSetIfFrameNameMatches,
+ base::Unretained(this),
+ &frame_set));
+
+ // It happens that some sub-pages attempt to send JavaScript messages before
+ // their frames are loaded.
+ DCHECK_GE(1U, frame_set.size());
+ if (frame_set.empty())
+ return NULL;
+ return *frame_set.begin();
+}
+
+void WebUIImpl::AddToSetIfFrameNameMatches(
+ std::set<RenderFrameHost*>* frame_set,
+ RenderFrameHost* host) {
+ if (host->GetFrameName() == frame_name_)
+ frame_set->insert(host);
}
} // namespace content
diff --git a/chromium/content/browser/webui/web_ui_impl.h b/chromium/content/browser/webui/web_ui_impl.h
index 8715e43e226..40cb559c1f6 100644
--- a/chromium/content/browser/webui/web_ui_impl.h
+++ b/chromium/content/browser/webui/web_ui_impl.h
@@ -6,6 +6,7 @@
#define CONTENT_BROWSER_WEBUI_WEB_UI_IMPL_H_
#include <map>
+#include <set>
#include "base/compiler_specific.h"
#include "base/memory/scoped_vector.h"
@@ -14,6 +15,7 @@
#include "ipc/ipc_listener.h"
namespace content {
+class RenderFrameHost;
class RenderViewHost;
class CONTENT_EXPORT WebUIImpl : public WebUI,
@@ -32,14 +34,14 @@ class CONTENT_EXPORT WebUIImpl : public WebUI,
virtual WebContents* GetWebContents() const OVERRIDE;
virtual WebUIController* GetController() const OVERRIDE;
virtual void SetController(WebUIController* controller) OVERRIDE;
- virtual ui::ScaleFactor GetDeviceScaleFactor() const OVERRIDE;
+ virtual float GetDeviceScaleFactor() const OVERRIDE;
virtual const base::string16& GetOverriddenTitle() const OVERRIDE;
virtual void OverrideTitle(const base::string16& title) OVERRIDE;
virtual PageTransition GetLinkTransitionType() const OVERRIDE;
virtual void SetLinkTransitionType(PageTransition type) OVERRIDE;
virtual int GetBindings() const OVERRIDE;
virtual void SetBindings(int bindings) OVERRIDE;
- virtual void SetFrameXPath(const std::string& xpath) OVERRIDE;
+ virtual void OverrideJavaScriptFrame(const std::string& frame_name) OVERRIDE;
virtual void AddMessageHandler(WebUIMessageHandler* handler) OVERRIDE;
typedef base::Callback<void(const base::ListValue*)> MessageCallback;
virtual void RegisterMessageCallback(
@@ -77,9 +79,19 @@ class CONTENT_EXPORT WebUIImpl : public WebUI,
const std::string& message,
const base::ListValue& args);
- // Execute a string of raw Javascript on the page.
+ // Execute a string of raw JavaScript on the page.
void ExecuteJavascript(const base::string16& javascript);
+ // Finds the frame in which to execute JavaScript (the one specified by
+ // OverrideJavaScriptFrame). May return NULL if no frame of the specified name
+ // exists in the page.
+ RenderFrameHost* TargetFrame();
+
+ // A helper function for TargetFrame; adds a frame to the specified set if its
+ // name matches frame_name_.
+ void AddToSetIfFrameNameMatches(std::set<RenderFrameHost*>* frame_set,
+ RenderFrameHost* host);
+
// A map of message name -> message handling callback.
typedef std::map<std::string, MessageCallback> MessageCallbackMap;
MessageCallbackMap message_callbacks_;
@@ -97,9 +109,9 @@ class CONTENT_EXPORT WebUIImpl : public WebUI,
// Non-owning pointer to the WebContents this WebUI is associated with.
WebContents* web_contents_;
- // The path for the iframe this WebUI is embedded in (empty if not in an
- // iframe).
- std::string frame_xpath_;
+ // The name of the iframe this WebUI is embedded in (empty if not explicitly
+ // overridden with OverrideJavaScriptFrame).
+ std::string frame_name_;
scoped_ptr<WebUIController> controller_;
diff --git a/chromium/content/browser/webui/web_ui_message_handler.cc b/chromium/content/browser/webui/web_ui_message_handler.cc
index 57b2c46b86d..44f065d44ed 100644
--- a/chromium/content/browser/webui/web_ui_message_handler.cc
+++ b/chromium/content/browser/webui/web_ui_message_handler.cc
@@ -11,7 +11,7 @@
namespace content {
-bool WebUIMessageHandler::ExtractIntegerValue(const ListValue* value,
+bool WebUIMessageHandler::ExtractIntegerValue(const base::ListValue* value,
int* out_int) {
std::string string_value;
if (value->GetString(0, &string_value))
@@ -25,7 +25,7 @@ bool WebUIMessageHandler::ExtractIntegerValue(const ListValue* value,
return false;
}
-bool WebUIMessageHandler::ExtractDoubleValue(const ListValue* value,
+bool WebUIMessageHandler::ExtractDoubleValue(const base::ListValue* value,
double* out_value) {
std::string string_value;
if (value->GetString(0, &string_value))
@@ -36,7 +36,8 @@ bool WebUIMessageHandler::ExtractDoubleValue(const ListValue* value,
return false;
}
-base::string16 WebUIMessageHandler::ExtractStringValue(const ListValue* value) {
+base::string16 WebUIMessageHandler::ExtractStringValue(
+ const base::ListValue* value) {
base::string16 string16_value;
if (value->GetString(0, &string16_value))
return string16_value;
diff --git a/chromium/content/browser/webui/web_ui_message_handler_unittest.cc b/chromium/content/browser/webui/web_ui_message_handler_unittest.cc
index 33cdb61fb11..cd12a3e8d61 100644
--- a/chromium/content/browser/webui/web_ui_message_handler_unittest.cc
+++ b/chromium/content/browser/webui/web_ui_message_handler_unittest.cc
@@ -12,11 +12,11 @@
namespace content {
TEST(WebUIMessageHandlerTest, ExtractIntegerValue) {
- ListValue list;
+ base::ListValue list;
int value, zero_value = 0, neg_value = -1234, pos_value = 1234;
- base::string16 zero_string(UTF8ToUTF16("0"));
- base::string16 neg_string(UTF8ToUTF16("-1234"));
- base::string16 pos_string(UTF8ToUTF16("1234"));
+ base::string16 zero_string(base::UTF8ToUTF16("0"));
+ base::string16 neg_string(base::UTF8ToUTF16("-1234"));
+ base::string16 pos_string(base::UTF8ToUTF16("1234"));
list.Append(new base::FundamentalValue(zero_value));
EXPECT_TRUE(WebUIMessageHandler::ExtractIntegerValue(&list, &value));
@@ -51,9 +51,9 @@ TEST(WebUIMessageHandlerTest, ExtractIntegerValue) {
TEST(WebUIMessageHandlerTest, ExtractDoubleValue) {
base::ListValue list;
double value, zero_value = 0.0, neg_value = -1234.5, pos_value = 1234.5;
- base::string16 zero_string(UTF8ToUTF16("0"));
- base::string16 neg_string(UTF8ToUTF16("-1234.5"));
- base::string16 pos_string(UTF8ToUTF16("1234.5"));
+ base::string16 zero_string(base::UTF8ToUTF16("0"));
+ base::string16 neg_string(base::UTF8ToUTF16("-1234.5"));
+ base::string16 pos_string(base::UTF8ToUTF16("1234.5"));
list.Append(new base::FundamentalValue(zero_value));
EXPECT_TRUE(WebUIMessageHandler::ExtractDoubleValue(&list, &value));
@@ -87,7 +87,7 @@ TEST(WebUIMessageHandlerTest, ExtractDoubleValue) {
TEST(WebUIMessageHandlerTest, ExtractStringValue) {
base::ListValue list;
- base::string16 in_string(UTF8ToUTF16(
+ base::string16 in_string(base::UTF8ToUTF16(
"The facts, though interesting, are irrelevant."));
list.Append(new base::StringValue(in_string));
base::string16 out_string = WebUIMessageHandler::ExtractStringValue(&list);
diff --git a/chromium/content/browser/webui/web_ui_mojo_browsertest.cc b/chromium/content/browser/webui/web_ui_mojo_browsertest.cc
new file mode 100644
index 00000000000..5007a8da6a0
--- /dev/null
+++ b/chromium/content/browser/webui/web_ui_mojo_browsertest.cc
@@ -0,0 +1,218 @@
+// 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 <limits>
+
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "base/strings/string_util.h"
+#include "content/browser/webui/web_ui_controller_factory_registry.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/common/content_paths.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/common/url_utils.h"
+#include "content/public/test/content_browser_test.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/test/data/web_ui_test_mojo_bindings.mojom.h"
+#include "grit/content_resources.h"
+#include "mojo/common/test/test_utils.h"
+#include "mojo/public/js/bindings/constants.h"
+
+namespace content {
+namespace {
+
+bool got_message = false;
+
+// The bindings for the page are generated from a .mojom file. This code looks
+// up the generated file from disk and returns it.
+bool GetResource(const std::string& id,
+ const WebUIDataSource::GotDataCallback& callback) {
+ // These are handled by the WebUIDataSource that AddMojoDataSource() creates.
+ if (id == mojo::kCodecModuleName ||
+ id == mojo::kConnectionModuleName ||
+ id == mojo::kConnectorModuleName ||
+ id == mojo::kUnicodeModuleName ||
+ id == mojo::kRouterModuleName)
+ return false;
+
+ std::string contents;
+ CHECK(base::ReadFileToString(mojo::test::GetFilePathForJSResource(id),
+ &contents,
+ std::string::npos)) << id;
+ base::RefCountedString* ref_contents = new base::RefCountedString;
+ ref_contents->data() = contents;
+ callback.Run(ref_contents);
+ return true;
+}
+
+class BrowserTargetImpl : public BrowserTarget {
+ public:
+ BrowserTargetImpl(mojo::ScopedMessagePipeHandle handle,
+ base::RunLoop* run_loop)
+ : run_loop_(run_loop) {
+ renderer_.Bind(handle.Pass());
+ renderer_.set_client(this);
+ }
+
+ virtual ~BrowserTargetImpl() {}
+
+ // BrowserTarget overrides:
+ virtual void PingResponse() OVERRIDE {
+ NOTREACHED();
+ }
+
+ protected:
+ RendererTargetPtr renderer_;
+ base::RunLoop* run_loop_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BrowserTargetImpl);
+};
+
+class PingBrowserTargetImpl : public BrowserTargetImpl {
+ public:
+ PingBrowserTargetImpl(mojo::ScopedMessagePipeHandle handle,
+ base::RunLoop* run_loop)
+ : BrowserTargetImpl(handle.Pass(), run_loop) {
+ renderer_->Ping();
+ }
+
+ virtual ~PingBrowserTargetImpl() {}
+
+ // BrowserTarget overrides:
+ // Quit the RunLoop when called.
+ virtual void PingResponse() OVERRIDE {
+ got_message = true;
+ run_loop_->Quit();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PingBrowserTargetImpl);
+};
+
+// WebUIController that sets up mojo bindings.
+class TestWebUIController : public WebUIController {
+ public:
+ TestWebUIController(WebUI* web_ui, base::RunLoop* run_loop)
+ : WebUIController(web_ui),
+ run_loop_(run_loop) {
+ content::WebUIDataSource* data_source =
+ WebUIDataSource::AddMojoDataSource(
+ web_ui->GetWebContents()->GetBrowserContext());
+ data_source->SetRequestFilter(base::Bind(&GetResource));
+ }
+
+ protected:
+ base::RunLoop* run_loop_;
+ scoped_ptr<BrowserTargetImpl> browser_target_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestWebUIController);
+};
+
+// TestWebUIController that additionally creates the ping test BrowserTarget
+// implementation at the right time.
+class PingTestWebUIController : public TestWebUIController {
+ public:
+ PingTestWebUIController(WebUI* web_ui, base::RunLoop* run_loop)
+ : TestWebUIController(web_ui, run_loop) {
+ }
+
+ // WebUIController overrides:
+ virtual void RenderViewCreated(RenderViewHost* render_view_host) OVERRIDE {
+ mojo::MessagePipe pipe;
+ browser_target_.reset(
+ new PingBrowserTargetImpl(pipe.handle0.Pass(), run_loop_));
+ render_view_host->SetWebUIHandle(pipe.handle1.Pass());
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PingTestWebUIController);
+};
+
+// WebUIControllerFactory that creates TestWebUIController.
+class TestWebUIControllerFactory : public WebUIControllerFactory {
+ public:
+ TestWebUIControllerFactory() : run_loop_(NULL) {}
+
+ void set_run_loop(base::RunLoop* run_loop) { run_loop_ = run_loop; }
+
+ virtual WebUIController* CreateWebUIControllerForURL(
+ WebUI* web_ui, const GURL& url) const OVERRIDE {
+ if (url.query() == "ping")
+ return new PingTestWebUIController(web_ui, run_loop_);
+ return NULL;
+ }
+ virtual WebUI::TypeID GetWebUIType(BrowserContext* browser_context,
+ const GURL& url) const OVERRIDE {
+ return reinterpret_cast<WebUI::TypeID>(1);
+ }
+ virtual bool UseWebUIForURL(BrowserContext* browser_context,
+ const GURL& url) const OVERRIDE {
+ return true;
+ }
+ virtual bool UseWebUIBindingsForURL(BrowserContext* browser_context,
+ const GURL& url) const OVERRIDE {
+ return true;
+ }
+
+ private:
+ base::RunLoop* run_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestWebUIControllerFactory);
+};
+
+class WebUIMojoTest : public ContentBrowserTest {
+ public:
+ WebUIMojoTest() {
+ WebUIControllerFactory::RegisterFactory(&factory_);
+ }
+
+ virtual ~WebUIMojoTest() {
+ WebUIControllerFactory::UnregisterFactoryForTesting(&factory_);
+ }
+
+ TestWebUIControllerFactory* factory() { return &factory_; }
+
+ private:
+ TestWebUIControllerFactory factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebUIMojoTest);
+};
+
+// Loads a webui page that contains mojo bindings and verifies a message makes
+// it from the browser to the page and back.
+IN_PROC_BROWSER_TEST_F(WebUIMojoTest, EndToEndPing) {
+ // Currently there is no way to have a generated file included in the isolate
+ // files. If the bindings file doesn't exist assume we're on such a bot and
+ // pass.
+ // TODO(sky): remove this conditional when isolates support copying from gen.
+ const base::FilePath test_file_path(
+ mojo::test::GetFilePathForJSResource(
+ "content/test/data/web_ui_test_mojo_bindings.mojom"));
+ if (!base::PathExists(test_file_path)) {
+ LOG(WARNING) << " mojom binding file doesn't exist, assuming on isolate";
+ return;
+ }
+
+ got_message = false;
+ ASSERT_TRUE(test_server()->Start());
+ base::RunLoop run_loop;
+ factory()->set_run_loop(&run_loop);
+ GURL test_url(test_server()->GetURL("files/web_ui_mojo.html?ping"));
+ NavigateToURL(shell(), test_url);
+ // RunLoop is quit when message received from page.
+ run_loop.Run();
+ EXPECT_TRUE(got_message);
+}
+
+} // namespace
+} // namespace content
diff --git a/chromium/content/browser/worker_host/OWNERS b/chromium/content/browser/worker_host/OWNERS
index 3e63b46257a..61fc3fd49ea 100644
--- a/chromium/content/browser/worker_host/OWNERS
+++ b/chromium/content/browser/worker_host/OWNERS
@@ -1 +1,3 @@
atwilson@chromium.org
+kinuko@chromium.org
+horo@chromium.org
diff --git a/chromium/content/browser/worker_host/worker_document_set.cc b/chromium/content/browser/worker_host/worker_document_set.cc
index 48eaf867c23..3031493783e 100644
--- a/chromium/content/browser/worker_host/worker_document_set.cc
+++ b/chromium/content/browser/worker_host/worker_document_set.cc
@@ -11,15 +11,15 @@ namespace content {
WorkerDocumentSet::WorkerDocumentSet() {
}
-void WorkerDocumentSet::Add(WorkerMessageFilter* parent,
+void WorkerDocumentSet::Add(BrowserMessageFilter* parent,
unsigned long long document_id,
int render_process_id,
- int render_view_id) {
- DocumentInfo info(parent, document_id, render_process_id, render_view_id);
+ int render_frame_id) {
+ DocumentInfo info(parent, document_id, render_process_id, render_frame_id);
document_set_.insert(info);
}
-bool WorkerDocumentSet::Contains(WorkerMessageFilter* parent,
+bool WorkerDocumentSet::Contains(BrowserMessageFilter* parent,
unsigned long long document_id) const {
for (DocumentInfoSet::const_iterator i = document_set_.begin();
i != document_set_.end(); ++i) {
@@ -29,7 +29,17 @@ bool WorkerDocumentSet::Contains(WorkerMessageFilter* parent,
return false;
}
-void WorkerDocumentSet::Remove(WorkerMessageFilter* parent,
+bool WorkerDocumentSet::ContainsExternalRenderer(
+ int worker_process_id) const {
+ for (DocumentInfoSet::const_iterator i = document_set_.begin();
+ i != document_set_.end(); ++i) {
+ if (i->render_process_id() != worker_process_id)
+ return true;
+ }
+ return false;
+}
+
+void WorkerDocumentSet::Remove(BrowserMessageFilter* parent,
unsigned long long document_id) {
for (DocumentInfoSet::iterator i = document_set_.begin();
i != document_set_.end(); i++) {
@@ -42,7 +52,7 @@ void WorkerDocumentSet::Remove(WorkerMessageFilter* parent,
DCHECK(!Contains(parent, document_id));
}
-void WorkerDocumentSet::RemoveAll(WorkerMessageFilter* parent) {
+void WorkerDocumentSet::RemoveAll(BrowserMessageFilter* parent) {
for (DocumentInfoSet::iterator i = document_set_.begin();
i != document_set_.end();) {
@@ -59,12 +69,12 @@ void WorkerDocumentSet::RemoveAll(WorkerMessageFilter* parent) {
}
WorkerDocumentSet::DocumentInfo::DocumentInfo(
- WorkerMessageFilter* filter, unsigned long long document_id,
- int render_process_id, int render_view_id)
+ BrowserMessageFilter* filter, unsigned long long document_id,
+ int render_process_id, int render_frame_id)
: filter_(filter),
document_id_(document_id),
render_process_id_(render_process_id),
- render_view_id_(render_view_id) {
+ render_frame_id_(render_frame_id) {
}
WorkerDocumentSet::~WorkerDocumentSet() {
diff --git a/chromium/content/browser/worker_host/worker_document_set.h b/chromium/content/browser/worker_host/worker_document_set.h
index a4b35ae5ce8..e497aa80e53 100644
--- a/chromium/content/browser/worker_host/worker_document_set.h
+++ b/chromium/content/browser/worker_host/worker_document_set.h
@@ -11,7 +11,7 @@
#include "base/memory/ref_counted.h"
namespace content {
-class WorkerMessageFilter;
+class BrowserMessageFilter;
// The WorkerDocumentSet tracks all of the DOM documents associated with a
// set of workers. With nested workers, multiple workers can share the same
@@ -24,12 +24,12 @@ class WorkerDocumentSet : public base::RefCounted<WorkerDocumentSet> {
// The information we track for each document
class DocumentInfo {
public:
- DocumentInfo(WorkerMessageFilter* filter, unsigned long long document_id,
- int renderer_process_id, int render_view_id);
- WorkerMessageFilter* filter() const { return filter_; }
+ DocumentInfo(BrowserMessageFilter* filter, unsigned long long document_id,
+ int renderer_process_id, int render_frame_id);
+ BrowserMessageFilter* filter() const { return filter_; }
unsigned long long document_id() const { return document_id_; }
int render_process_id() const { return render_process_id_; }
- int render_view_id() const { return render_view_id_; }
+ int render_frame_id() const { return render_frame_id_; }
// Define operator "<", which is used to determine uniqueness within
// the set.
@@ -46,31 +46,35 @@ class WorkerDocumentSet : public base::RefCounted<WorkerDocumentSet> {
}
private:
- WorkerMessageFilter* filter_;
+ BrowserMessageFilter* filter_;
unsigned long long document_id_;
int render_process_id_;
- int render_view_id_;
+ int render_frame_id_;
};
// Adds a document to a shared worker's document set. Also includes the
// associated render_process_id the document is associated with, to enable
// communication with the parent tab for things like http auth dialogs.
- void Add(WorkerMessageFilter* parent,
+ void Add(BrowserMessageFilter* parent,
unsigned long long document_id,
int render_process_id,
- int render_view_id);
+ int render_frame_id);
// Checks to see if a document is in a shared worker's document set.
- bool Contains(WorkerMessageFilter* parent,
+ bool Contains(BrowserMessageFilter* parent,
unsigned long long document_id) const;
+ // Checks to see if the document set contains any documents which is
+ // associated with other renderer process than worker_process_id.
+ bool ContainsExternalRenderer(int worker_process_id) const;
+
// Removes a specific document from a worker's document set when that document
// is detached.
- void Remove(WorkerMessageFilter* parent, unsigned long long document_id);
+ void Remove(BrowserMessageFilter* parent, unsigned long long document_id);
// Invoked when a render process exits, to remove all associated documents
// from a worker's document set.
- void RemoveAll(WorkerMessageFilter* parent);
+ void RemoveAll(BrowserMessageFilter* parent);
bool IsEmpty() const { return document_set_.empty(); }
diff --git a/chromium/content/browser/worker_host/worker_message_filter.cc b/chromium/content/browser/worker_host/worker_message_filter.cc
index 49fe7f6f3e8..62348b5ff0b 100644
--- a/chromium/content/browser/worker_host/worker_message_filter.cc
+++ b/chromium/content/browser/worker_host/worker_message_filter.cc
@@ -17,7 +17,8 @@ WorkerMessageFilter::WorkerMessageFilter(
ResourceContext* resource_context,
const WorkerStoragePartition& partition,
MessagePortMessageFilter* message_port_message_filter)
- : render_process_id_(render_process_id),
+ : BrowserMessageFilter(ViewMsgStart),
+ render_process_id_(render_process_id),
resource_context_(resource_context),
partition_(partition),
message_port_message_filter_(message_port_message_filter) {
@@ -33,20 +34,17 @@ void WorkerMessageFilter::OnChannelClosing() {
WorkerServiceImpl::GetInstance()->OnWorkerMessageFilterClosing(this);
}
-bool WorkerMessageFilter::OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) {
+bool WorkerMessageFilter::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(WorkerMessageFilter, message, *message_was_ok)
+ IPC_BEGIN_MESSAGE_MAP(WorkerMessageFilter, message)
// Worker messages.
// Only sent from renderer for now, until we have nested workers.
IPC_MESSAGE_HANDLER(ViewHostMsg_CreateWorker, OnCreateWorker)
- // Only sent from renderer for now, until we have nested workers.
- IPC_MESSAGE_HANDLER(ViewHostMsg_LookupSharedWorker, OnLookupSharedWorker)
IPC_MESSAGE_HANDLER(ViewHostMsg_ForwardToWorker, OnForwardToWorker)
// Only sent from renderer.
IPC_MESSAGE_HANDLER(ViewHostMsg_DocumentDetached, OnDocumentDetached)
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
+ IPC_END_MESSAGE_MAP()
return handled;
}
@@ -58,22 +56,12 @@ int WorkerMessageFilter::GetNextRoutingID() {
void WorkerMessageFilter::OnCreateWorker(
const ViewHostMsg_CreateWorker_Params& params,
int* route_id) {
- *route_id = params.route_id != MSG_ROUTING_NONE ?
- params.route_id : GetNextRoutingID();
- WorkerServiceImpl::GetInstance()->CreateWorker(
- params, *route_id, this, resource_context_, partition_);
-}
-
-void WorkerMessageFilter::OnLookupSharedWorker(
- const ViewHostMsg_CreateWorker_Params& params,
- bool* exists,
- int* route_id,
- bool* url_error) {
+ bool url_error = false;
*route_id = GetNextRoutingID();
-
- WorkerServiceImpl::GetInstance()->LookupSharedWorker(
- params, *route_id, this, resource_context_, partition_, exists,
- url_error);
+ WorkerServiceImpl::GetInstance()->CreateWorker(
+ params, *route_id, this, resource_context_, partition_, &url_error);
+ if (url_error)
+ *route_id = MSG_ROUTING_NONE;
}
void WorkerMessageFilter::OnForwardToWorker(const IPC::Message& message) {
diff --git a/chromium/content/browser/worker_host/worker_message_filter.h b/chromium/content/browser/worker_host/worker_message_filter.h
index 0d495fae302..ab9bade8e64 100644
--- a/chromium/content/browser/worker_host/worker_message_filter.h
+++ b/chromium/content/browser/worker_host/worker_message_filter.h
@@ -25,8 +25,7 @@ class WorkerMessageFilter : public BrowserMessageFilter {
// BrowserMessageFilter implementation.
virtual void OnChannelClosing() OVERRIDE;
- virtual bool OnMessageReceived(const IPC::Message& message,
- bool* message_was_ok) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
int GetNextRoutingID();
int render_process_id() const { return render_process_id_; }
@@ -41,13 +40,8 @@ class WorkerMessageFilter : public BrowserMessageFilter {
// Message handlers.
void OnCreateWorker(const ViewHostMsg_CreateWorker_Params& params,
int* route_id);
- void OnLookupSharedWorker(const ViewHostMsg_CreateWorker_Params& params,
- bool* exists,
- int* route_id,
- bool* url_error);
void OnForwardToWorker(const IPC::Message& message);
void OnDocumentDetached(unsigned long long document_id);
- void OnCreateMessagePort(int* route_id, int* message_port_id);
int render_process_id_;
ResourceContext* const resource_context_;
diff --git a/chromium/content/browser/worker_host/worker_process_host.cc b/chromium/content/browser/worker_host/worker_process_host.cc
index fae589bf476..81645f66732 100644
--- a/chromium/content/browser/worker_host/worker_process_host.cc
+++ b/chromium/content/browser/worker_host/worker_process_host.cc
@@ -23,6 +23,8 @@
#include "content/browser/devtools/worker_devtools_manager.h"
#include "content/browser/devtools/worker_devtools_message_filter.h"
#include "content/browser/fileapi/fileapi_message_filter.h"
+#include "content/browser/frame_host/render_frame_host_delegate.h"
+#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/browser/indexed_db/indexed_db_dispatcher_host.h"
#include "content/browser/loader/resource_message_filter.h"
#include "content/browser/message_port_message_filter.h"
@@ -34,6 +36,7 @@
#include "content/browser/renderer_host/render_view_host_delegate.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/renderer_host/socket_stream_dispatcher_host.h"
+#include "content/browser/renderer_host/websocket_dispatcher_host.h"
#include "content/browser/resource_context_impl.h"
#include "content/browser/worker_host/worker_message_filter.h"
#include "content/browser/worker_host/worker_service_impl.h"
@@ -45,6 +48,7 @@
#include "content/public/browser/user_metrics.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/result_codes.h"
+#include "content/public/common/sandboxed_process_launcher_delegate.h"
#include "ipc/ipc_switches.h"
#include "net/base/mime_util.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
@@ -56,37 +60,82 @@
#if defined(OS_WIN)
#include "content/common/sandbox_win.h"
-#include "content/public/common/sandboxed_process_launcher_delegate.h"
#endif
namespace content {
namespace {
-#if defined(OS_WIN)
// NOTE: changes to this class need to be reviewed by the security team.
class WorkerSandboxedProcessLauncherDelegate
: public content::SandboxedProcessLauncherDelegate {
public:
- WorkerSandboxedProcessLauncherDelegate() {}
+ WorkerSandboxedProcessLauncherDelegate(ChildProcessHost* host,
+ bool debugging_child)
+#if defined(OS_POSIX)
+ : ipc_fd_(host->TakeClientFileDescriptor()),
+ debugging_child_(debugging_child)
+#endif // OS_POSIX
+ {}
+
virtual ~WorkerSandboxedProcessLauncherDelegate() {}
+#if defined(OS_WIN)
virtual void PreSpawnTarget(sandbox::TargetPolicy* policy,
bool* success) {
AddBaseHandleClosePolicy(policy);
}
-};
+#elif defined(OS_POSIX)
+ virtual bool ShouldUseZygote() OVERRIDE {
+ return !debugging_child_;
+ }
+ virtual int GetIpcFd() OVERRIDE {
+ return ipc_fd_;
+ }
#endif // OS_WIN
-} // namespace
+ private:
+#if defined(OS_POSIX)
+ int ipc_fd_;
+ bool debugging_child_;
+#endif // OS_POSIX
+};
// Notifies RenderViewHost that one or more worker objects crashed.
-void WorkerCrashCallback(int render_process_unique_id, int render_view_id) {
- RenderViewHostImpl* host =
- RenderViewHostImpl::FromID(render_process_unique_id, render_view_id);
+void WorkerCrashCallback(int render_process_unique_id, int render_frame_id) {
+ RenderFrameHostImpl* host =
+ RenderFrameHostImpl::FromID(render_process_unique_id, render_frame_id);
if (host)
- host->GetDelegate()->WorkerCrashed();
+ host->delegate()->WorkerCrashed(host);
}
+void WorkerCreatedCallback(int render_process_id,
+ int render_frame_id,
+ int worker_process_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ RenderFrameHost* render_frame_host =
+ RenderFrameHost::FromID(render_process_id, render_frame_id);
+ if (!render_frame_host)
+ return;
+ SiteInstance* site_instance = render_frame_host->GetSiteInstance();
+ GetContentClient()->browser()->WorkerProcessCreated(site_instance,
+ worker_process_id);
+}
+
+void WorkerTerminatedCallback(int render_process_id,
+ int render_frame_id,
+ int worker_process_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ RenderFrameHost* render_frame_host =
+ RenderFrameHost::FromID(render_process_id, render_frame_id);
+ if (!render_frame_host)
+ return;
+ SiteInstance* site_instance = render_frame_host->GetSiteInstance();
+ GetContentClient()->browser()->WorkerProcessTerminated(site_instance,
+ worker_process_id);
+}
+
+} // namespace
+
WorkerProcessHost::WorkerProcessHost(
ResourceContext* resource_context,
const WorkerStoragePartition& partition)
@@ -102,14 +151,16 @@ WorkerProcessHost::WorkerProcessHost(
WorkerProcessHost::~WorkerProcessHost() {
// If we crashed, tell the RenderViewHosts.
for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
- const WorkerDocumentSet::DocumentInfoSet& parents =
- i->worker_document_set()->documents();
- for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter =
- parents.begin(); parent_iter != parents.end(); ++parent_iter) {
- BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- base::Bind(&WorkerCrashCallback, parent_iter->render_process_id(),
- parent_iter->render_view_id()));
+ if (!i->load_failed()) {
+ const WorkerDocumentSet::DocumentInfoSet& parents =
+ i->worker_document_set()->documents();
+ for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter =
+ parents.begin(); parent_iter != parents.end(); ++parent_iter) {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&WorkerCrashCallback, parent_iter->render_process_id(),
+ parent_iter->render_frame_id()));
+ }
}
WorkerServiceImpl::GetInstance()->NotifyWorkerDestroyed(
this, i->worker_route_id());
@@ -123,7 +174,7 @@ bool WorkerProcessHost::Send(IPC::Message* message) {
return process_->Send(message);
}
-bool WorkerProcessHost::Init(int render_process_id) {
+bool WorkerProcessHost::Init(int render_process_id, int render_frame_id) {
std::string channel_id = process_->GetHost()->CreateChannel();
if (channel_id.empty())
return false;
@@ -153,18 +204,19 @@ bool WorkerProcessHost::Init(int render_process_id) {
switches::kDisableFileSystem,
switches::kDisableSeccompFilterSandbox,
switches::kEnableExperimentalWebPlatformFeatures,
+ switches::kEnablePreciseMemoryInfo,
switches::kEnableServiceWorker,
#if defined(OS_MACOSX)
switches::kEnableSandboxLogging,
#endif
- switches::kJavaScriptFlags
+ switches::kJavaScriptFlags,
+ switches::kNoSandbox
};
cmd_line->CopySwitchesFrom(*CommandLine::ForCurrentProcess(), kSwitchNames,
arraysize(kSwitchNames));
+bool debugging_child = false;
#if defined(OS_POSIX)
- bool use_zygote = true;
-
if (CommandLine::ForCurrentProcess()->HasSwitch(
switches::kWaitForDebuggerChildren)) {
// Look to pass-on the kWaitForDebugger flag.
@@ -172,36 +224,26 @@ bool WorkerProcessHost::Init(int render_process_id) {
switches::kWaitForDebuggerChildren);
if (value.empty() || value == switches::kWorkerProcess) {
cmd_line->AppendSwitch(switches::kWaitForDebugger);
- use_zygote = false;
- }
- }
-
- if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDebugChildren)) {
- // Look to pass-on the kDebugOnStart flag.
- std::string value = CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- switches::kDebugChildren);
- if (value.empty() || value == switches::kWorkerProcess) {
- // launches a new xterm, and runs the worker process in gdb, reading
- // optional commands from gdb_chrome file in the working directory.
- cmd_line->PrependWrapper("xterm -e gdb -x gdb_chrome --args");
- use_zygote = false;
+ debugging_child = true;
}
}
#endif
process_->Launch(
-#if defined(OS_WIN)
- new WorkerSandboxedProcessLauncherDelegate,
-#elif defined(OS_POSIX)
- use_zygote,
- base::EnvironmentMap(),
-#endif
+ new WorkerSandboxedProcessLauncherDelegate(process_->GetHost(),
+ debugging_child),
cmd_line);
ChildProcessSecurityPolicyImpl::GetInstance()->AddWorker(
process_->GetData().id, render_process_id);
CreateMessageFilters(render_process_id);
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&WorkerCreatedCallback,
+ render_process_id,
+ render_frame_id,
+ process_->GetData().id));
return true;
}
@@ -223,6 +265,7 @@ void WorkerProcessHost::CreateMessageFilters(int render_process_id) {
partition_.appcache_service(),
blob_storage_context,
partition_.filesystem_context(),
+ partition_.service_worker_context(),
get_contexts_callback);
process_->AddFilter(resource_message_filter);
@@ -265,12 +308,26 @@ void WorkerProcessHost::CreateMessageFilters(int render_process_id) {
resource_context_);
socket_stream_dispatcher_host_ = socket_stream_dispatcher_host;
process_->AddFilter(socket_stream_dispatcher_host);
+
+ WebSocketDispatcherHost::GetRequestContextCallback
+ websocket_request_context_callback(
+ base::Bind(&WorkerProcessHost::GetRequestContext,
+ base::Unretained(this),
+ ResourceType::SUB_RESOURCE));
+
+ process_->AddFilter(new WebSocketDispatcherHost(
+ render_process_id, websocket_request_context_callback));
+
process_->AddFilter(new WorkerDevToolsMessageFilter(process_->GetData().id));
process_->AddFilter(
- new IndexedDBDispatcherHost(partition_.indexed_db_context()));
+ new IndexedDBDispatcherHost(process_->GetData().id,
+ url_request_context,
+ partition_.indexed_db_context(),
+ blob_storage_context));
}
-void WorkerProcessHost::CreateWorker(const WorkerInstance& instance) {
+void WorkerProcessHost::CreateWorker(const WorkerInstance& instance,
+ bool pause_on_start) {
ChildProcessSecurityPolicyImpl::GetInstance()->GrantRequestURL(
process_->GetData().id, instance.url());
@@ -279,9 +336,10 @@ void WorkerProcessHost::CreateWorker(const WorkerInstance& instance) {
WorkerProcessMsg_CreateWorker_Params params;
params.url = instance.url();
params.name = instance.name();
+ params.content_security_policy = instance.content_security_policy();
+ params.security_policy_type = instance.security_policy_type();
+ params.pause_on_start = pause_on_start;
params.route_id = instance.worker_route_id();
- params.creator_process_id = instance.parent_process_id();
- params.shared_worker_appcache_id = instance.main_resource_appcache_id();
Send(new WorkerProcessMsg_CreateWorker(params));
UpdateTitle();
@@ -292,8 +350,7 @@ void WorkerProcessHost::CreateWorker(const WorkerInstance& instance) {
for (WorkerInstance::FilterList::const_iterator i =
instance.filters().begin();
i != instance.filters().end(); ++i) {
- CHECK(i->first);
- i->first->Send(new ViewMsg_WorkerCreated(i->second));
+ i->filter()->Send(new ViewMsg_WorkerCreated(i->route_id()));
}
}
@@ -301,7 +358,7 @@ bool WorkerProcessHost::FilterMessage(const IPC::Message& message,
WorkerMessageFilter* filter) {
for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
if (!i->closed() && i->HasFilter(filter, message.routing_id())) {
- RelayMessage(message, worker_message_filter_.get(), i->worker_route_id());
+ RelayMessage(message, filter, &(*i));
return true;
}
}
@@ -316,44 +373,28 @@ void WorkerProcessHost::OnProcessLaunched() {
}
bool WorkerProcessHost::OnMessageReceived(const IPC::Message& message) {
- bool msg_is_ok = true;
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(WorkerProcessHost, message, msg_is_ok)
+ IPC_BEGIN_MESSAGE_MAP(WorkerProcessHost, message)
IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerContextClosed,
OnWorkerContextClosed)
+ IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerContextDestroyed,
+ OnWorkerContextDestroyed)
+ IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerScriptLoaded,
+ OnWorkerScriptLoaded)
+ IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerScriptLoadFailed,
+ OnWorkerScriptLoadFailed)
+ IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerConnected,
+ OnWorkerConnected)
IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_AllowDatabase, OnAllowDatabase)
- IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_AllowFileSystem, OnAllowFileSystem)
+ IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_RequestFileSystemAccessSync,
+ OnRequestFileSystemAccessSync)
IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_AllowIndexedDB, OnAllowIndexedDB)
IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_ForceKillWorker,
OnForceKillWorkerProcess)
IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP_EX()
-
- if (!msg_is_ok) {
- NOTREACHED();
- RecordAction(UserMetricsAction("BadMessageTerminate_WPH"));
- base::KillProcess(
- process_->GetData().handle, RESULT_CODE_KILLED_BAD_MESSAGE, false);
- }
-
- if (handled)
- return true;
-
- if (message.type() == WorkerHostMsg_WorkerContextDestroyed::ID) {
- WorkerServiceImpl::GetInstance()->NotifyWorkerDestroyed(
- this, message.routing_id());
- }
+ IPC_END_MESSAGE_MAP()
- for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
- if (i->worker_route_id() == message.routing_id()) {
- if (message.type() == WorkerHostMsg_WorkerContextDestroyed::ID) {
- instances_.erase(i);
- UpdateTitle();
- }
- return true;
- }
- }
- return false;
+ return handled;
}
// Sent to notify the browser process when a worker context invokes close(), so
@@ -370,6 +411,57 @@ void WorkerProcessHost::OnWorkerContextClosed(int worker_route_id) {
}
}
+void WorkerProcessHost::OnWorkerContextDestroyed(int worker_route_id) {
+ WorkerServiceImpl::GetInstance()->NotifyWorkerDestroyed(
+ this, worker_route_id);
+ for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
+ if (i->worker_route_id() == worker_route_id) {
+ instances_.erase(i);
+ UpdateTitle();
+ return;
+ }
+ }
+}
+
+void WorkerProcessHost::OnWorkerScriptLoaded(int worker_route_id) {
+ WorkerDevToolsManager::GetInstance()->WorkerContextStarted(this,
+ worker_route_id);
+}
+
+void WorkerProcessHost::OnWorkerScriptLoadFailed(int worker_route_id) {
+ bool shutdown = true;
+ for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
+ if (i->worker_route_id() != worker_route_id) {
+ shutdown = false;
+ continue;
+ }
+ i->set_load_failed(true);
+ for (WorkerInstance::FilterList::const_iterator j = i->filters().begin();
+ j != i->filters().end(); ++j) {
+ j->filter()->Send(new ViewMsg_WorkerScriptLoadFailed(j->route_id()));
+ }
+ }
+ if (shutdown) {
+ base::KillProcess(
+ process_->GetData().handle, RESULT_CODE_NORMAL_EXIT, false);
+ }
+}
+
+void WorkerProcessHost::OnWorkerConnected(int message_port_id,
+ int worker_route_id) {
+ for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
+ if (i->worker_route_id() != worker_route_id)
+ continue;
+ for (WorkerInstance::FilterList::const_iterator j = i->filters().begin();
+ j != i->filters().end(); ++j) {
+ if (j->message_port_id() != message_port_id)
+ continue;
+ j->filter()->Send(new ViewMsg_WorkerConnected(j->route_id()));
+ return;
+ }
+ }
+}
+
void WorkerProcessHost::OnAllowDatabase(int worker_route_id,
const GURL& url,
const base::string16& name,
@@ -378,14 +470,14 @@ void WorkerProcessHost::OnAllowDatabase(int worker_route_id,
bool* result) {
*result = GetContentClient()->browser()->AllowWorkerDatabase(
url, name, display_name, estimated_size, resource_context_,
- GetRenderViewIDsForWorker(worker_route_id));
+ GetRenderFrameIDsForWorker(worker_route_id));
}
-void WorkerProcessHost::OnAllowFileSystem(int worker_route_id,
- const GURL& url,
- bool* result) {
+void WorkerProcessHost::OnRequestFileSystemAccessSync(int worker_route_id,
+ const GURL& url,
+ bool* result) {
*result = GetContentClient()->browser()->AllowWorkerFileSystem(
- url, resource_context_, GetRenderViewIDsForWorker(worker_route_id));
+ url, resource_context_, GetRenderFrameIDsForWorker(worker_route_id));
}
void WorkerProcessHost::OnAllowIndexedDB(int worker_route_id,
@@ -393,7 +485,8 @@ void WorkerProcessHost::OnAllowIndexedDB(int worker_route_id,
const base::string16& name,
bool* result) {
*result = GetContentClient()->browser()->AllowWorkerIndexedDB(
- url, name, resource_context_, GetRenderViewIDsForWorker(worker_route_id));
+ url, name, resource_context_,
+ GetRenderFrameIDsForWorker(worker_route_id));
}
void WorkerProcessHost::OnForceKillWorkerProcess() {
@@ -401,42 +494,41 @@ void WorkerProcessHost::OnForceKillWorkerProcess() {
base::KillProcess(
process_->GetData().handle, RESULT_CODE_NORMAL_EXIT, false);
else
- RecordAction(UserMetricsAction("WorkerProcess_BadProcessToKill"));
+ RecordAction(base::UserMetricsAction("WorkerProcess_BadProcessToKill"));
}
void WorkerProcessHost::RelayMessage(
const IPC::Message& message,
- WorkerMessageFilter* filter,
- int route_id) {
+ WorkerMessageFilter* incoming_filter,
+ WorkerInstance* instance) {
if (message.type() == WorkerMsg_Connect::ID) {
// Crack the SharedWorker Connect message to setup routing for the port.
- int sent_message_port_id;
- int new_routing_id;
- if (!WorkerMsg_Connect::Read(
- &message, &sent_message_port_id, &new_routing_id)) {
+ WorkerMsg_Connect::Param params;
+ if (!WorkerMsg_Connect::Read(&message, &params))
return;
- }
- new_routing_id = filter->GetNextRoutingID();
+
+ int sent_message_port_id = params.a;
+ int new_routing_id = params.b;
+ new_routing_id = worker_message_filter_->GetNextRoutingID();
MessagePortService::GetInstance()->UpdateMessagePort(
sent_message_port_id,
- filter->message_port_message_filter(),
+ worker_message_filter_->message_port_message_filter(),
new_routing_id);
+ instance->SetMessagePortID(incoming_filter,
+ message.routing_id(),
+ sent_message_port_id);
// Resend the message with the new routing id.
- filter->Send(new WorkerMsg_Connect(
- route_id, sent_message_port_id, new_routing_id));
+ worker_message_filter_->Send(new WorkerMsg_Connect(
+ instance->worker_route_id(), sent_message_port_id, new_routing_id));
// Send any queued messages for the sent port.
MessagePortService::GetInstance()->SendQueuedMessagesIfPossible(
sent_message_port_id);
} else {
IPC::Message* new_message = new IPC::Message(message);
- new_message->set_routing_id(route_id);
- filter->Send(new_message);
- if (message.type() == WorkerMsg_StartWorkerContext::ID) {
- WorkerDevToolsManager::GetInstance()->WorkerContextStarted(
- this, route_id);
- }
+ new_message->set_routing_id(instance->worker_route_id());
+ worker_message_filter_->Send(new_message);
return;
}
}
@@ -459,11 +551,27 @@ void WorkerProcessHost::FilterShutdown(WorkerMessageFilter* filter) {
bool shutdown = false;
i->RemoveFilters(filter);
+ int render_frame_id = 0;
+ const WorkerDocumentSet::DocumentInfoSet& documents =
+ i->worker_document_set()->documents();
+ for (WorkerDocumentSet::DocumentInfoSet::const_iterator doc =
+ documents.begin(); doc != documents.end(); ++doc) {
+ if (doc->filter() == filter) {
+ render_frame_id = doc->render_frame_id();
+ break;
+ }
+ }
i->worker_document_set()->RemoveAll(filter);
if (i->worker_document_set()->IsEmpty()) {
shutdown = true;
}
if (shutdown) {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&WorkerTerminatedCallback,
+ filter->render_process_id(),
+ render_frame_id,
+ process_->GetData().id));
Send(new WorkerMsg_TerminateWorkerContext(i->worker_route_id()));
i = instances_.erase(i);
} else {
@@ -487,7 +595,7 @@ void WorkerProcessHost::UpdateTitle() {
if (title.empty()) {
title = net::registry_controlled_domains::GetDomainAndRegistry(
i->url(),
- net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
+ net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
}
// Use the host name if the domain is empty, i.e. localhost or IP address.
@@ -508,15 +616,31 @@ void WorkerProcessHost::UpdateTitle() {
display_title += *i;
}
- process_->SetName(UTF8ToUTF16(display_title));
+ process_->SetName(base::UTF8ToUTF16(display_title));
}
void WorkerProcessHost::DocumentDetached(WorkerMessageFilter* filter,
unsigned long long document_id) {
// Walk all instances and remove the document from their document set.
for (Instances::iterator i = instances_.begin(); i != instances_.end();) {
+ int render_frame_id = 0;
+ const WorkerDocumentSet::DocumentInfoSet& documents =
+ i->worker_document_set()->documents();
+ for (WorkerDocumentSet::DocumentInfoSet::const_iterator doc =
+ documents.begin(); doc != documents.end(); ++doc) {
+ if (doc->filter() == filter && doc->document_id() == document_id) {
+ render_frame_id = doc->render_frame_id();
+ break;
+ }
+ }
i->worker_document_set()->Remove(filter, document_id);
if (i->worker_document_set()->IsEmpty()) {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&WorkerTerminatedCallback,
+ filter->render_process_id(),
+ render_frame_id,
+ process_->GetData().id));
// This worker has no more associated documents - shut it down.
Send(new WorkerMsg_TerminateWorkerContext(i->worker_route_id()));
i = instances_.erase(i);
@@ -539,7 +663,7 @@ const ChildProcessData& WorkerProcessHost::GetData() {
return process_->GetData();
}
-std::vector<std::pair<int, int> > WorkerProcessHost::GetRenderViewIDsForWorker(
+std::vector<std::pair<int, int> > WorkerProcessHost::GetRenderFrameIDsForWorker(
int worker_route_id) {
std::vector<std::pair<int, int> > result;
WorkerProcessHost::Instances::const_iterator i;
@@ -551,7 +675,7 @@ std::vector<std::pair<int, int> > WorkerProcessHost::GetRenderViewIDsForWorker(
for (WorkerDocumentSet::DocumentInfoSet::const_iterator doc =
documents.begin(); doc != documents.end(); ++doc) {
result.push_back(
- std::make_pair(doc->render_process_id(), doc->render_view_id()));
+ std::make_pair(doc->render_process_id(), doc->render_frame_id()));
}
break;
}
@@ -573,42 +697,39 @@ net::URLRequestContext* WorkerProcessHost::GetRequestContext(
WorkerProcessHost::WorkerInstance::WorkerInstance(
const GURL& url,
const base::string16& name,
+ const base::string16& content_security_policy,
+ blink::WebContentSecurityPolicyType security_policy_type,
int worker_route_id,
- int parent_process_id,
- int64 main_resource_appcache_id,
+ int render_frame_id,
ResourceContext* resource_context,
const WorkerStoragePartition& partition)
: url_(url),
closed_(false),
name_(name),
+ content_security_policy_(content_security_policy),
+ security_policy_type_(security_policy_type),
worker_route_id_(worker_route_id),
- parent_process_id_(parent_process_id),
- main_resource_appcache_id_(main_resource_appcache_id),
+ render_frame_id_(render_frame_id),
worker_document_set_(new WorkerDocumentSet()),
resource_context_(resource_context),
- partition_(partition) {
+ partition_(partition),
+ load_failed_(false) {
DCHECK(resource_context_);
}
-WorkerProcessHost::WorkerInstance::WorkerInstance(
- const GURL& url,
- bool shared,
- const base::string16& name,
- ResourceContext* resource_context,
- const WorkerStoragePartition& partition)
- : url_(url),
- closed_(false),
- name_(name),
- worker_route_id_(MSG_ROUTING_NONE),
- parent_process_id_(0),
- main_resource_appcache_id_(0),
- worker_document_set_(new WorkerDocumentSet()),
- resource_context_(resource_context),
- partition_(partition) {
- DCHECK(resource_context_);
+WorkerProcessHost::WorkerInstance::~WorkerInstance() {
}
-WorkerProcessHost::WorkerInstance::~WorkerInstance() {
+void WorkerProcessHost::WorkerInstance::SetMessagePortID(
+ WorkerMessageFilter* filter,
+ int route_id,
+ int message_port_id) {
+ for (FilterList::iterator i = filters_.begin(); i != filters_.end(); ++i) {
+ if (i->filter() == filter && i->route_id() == route_id) {
+ i->set_message_port_id(message_port_id);
+ return;
+ }
+ }
}
// Compares an instance based on the algorithm in the WebWorkers spec - an
@@ -656,7 +777,7 @@ void WorkerProcessHost::WorkerInstance::AddFilter(WorkerMessageFilter* filter,
void WorkerProcessHost::WorkerInstance::RemoveFilter(
WorkerMessageFilter* filter, int route_id) {
for (FilterList::iterator i = filters_.begin(); i != filters_.end();) {
- if (i->first == filter && i->second == route_id)
+ if (i->filter() == filter && i->route_id() == route_id)
i = filters_.erase(i);
else
++i;
@@ -668,7 +789,7 @@ void WorkerProcessHost::WorkerInstance::RemoveFilter(
void WorkerProcessHost::WorkerInstance::RemoveFilters(
WorkerMessageFilter* filter) {
for (FilterList::iterator i = filters_.begin(); i != filters_.end();) {
- if (i->first == filter)
+ if (i->filter() == filter)
i = filters_.erase(i);
else
++i;
@@ -679,21 +800,21 @@ bool WorkerProcessHost::WorkerInstance::HasFilter(
WorkerMessageFilter* filter, int route_id) const {
for (FilterList::const_iterator i = filters_.begin(); i != filters_.end();
++i) {
- if (i->first == filter && i->second == route_id)
+ if (i->filter() == filter && i->route_id() == route_id)
return true;
}
return false;
}
-bool WorkerProcessHost::WorkerInstance::RendererIsParent(
- int render_process_id, int render_view_id) const {
+bool WorkerProcessHost::WorkerInstance::FrameIsParent(
+ int render_process_id, int render_frame_id) const {
const WorkerDocumentSet::DocumentInfoSet& parents =
worker_document_set()->documents();
for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter =
parents.begin();
parent_iter != parents.end(); ++parent_iter) {
if (parent_iter->render_process_id() == render_process_id &&
- parent_iter->render_view_id() == render_view_id) {
+ parent_iter->render_frame_id() == render_frame_id) {
return true;
}
}
diff --git a/chromium/content/browser/worker_host/worker_process_host.h b/chromium/content/browser/worker_host/worker_process_host.h
index c3fa64d6d74..150153139fe 100644
--- a/chromium/content/browser/worker_host/worker_process_host.h
+++ b/chromium/content/browser/worker_host/worker_process_host.h
@@ -19,6 +19,7 @@
#include "content/public/browser/browser_child_process_host_iterator.h"
#include "content/public/common/process_type.h"
#include "ipc/ipc_sender.h"
+#include "third_party/WebKit/public/web/WebContentSecurityPolicy.h"
#include "url/gurl.h"
#include "webkit/common/resource_type.h"
@@ -41,6 +42,7 @@ class BrowserChildProcessHostImpl;
class IndexedDBContextImpl;
class ResourceContext;
class SocketStreamDispatcherHost;
+class WorkerMessageFilter;
class WorkerServiceImpl;
// The WorkerProcessHost is the interface that represents the browser side of
@@ -58,29 +60,40 @@ class WorkerProcessHost : public BrowserChildProcessHostDelegate,
public:
WorkerInstance(const GURL& url,
const base::string16& name,
+ const base::string16& content_security_policy,
+ blink::WebContentSecurityPolicyType security_policy_type,
int worker_route_id,
- int parent_process_id,
- int64 main_resource_appcache_id,
- ResourceContext* resource_context,
- const WorkerStoragePartition& partition);
- // Used for pending instances. Rest of the parameters are ignored.
- WorkerInstance(const GURL& url,
- bool shared,
- const base::string16& name,
+ int render_frame_id,
ResourceContext* resource_context,
const WorkerStoragePartition& partition);
~WorkerInstance();
// Unique identifier for a worker client.
- typedef std::pair<WorkerMessageFilter*, int> FilterInfo;
+ class FilterInfo {
+ public:
+ FilterInfo(WorkerMessageFilter* filter, int route_id)
+ : filter_(filter), route_id_(route_id), message_port_id_(0) { }
+ WorkerMessageFilter* filter() const { return filter_; }
+ int route_id() const { return route_id_; }
+ int message_port_id() const { return message_port_id_; }
+ void set_message_port_id(int id) { message_port_id_ = id; }
+
+ private:
+ WorkerMessageFilter* filter_;
+ int route_id_;
+ int message_port_id_;
+ };
// APIs to manage the filter list for a given instance.
void AddFilter(WorkerMessageFilter* filter, int route_id);
void RemoveFilter(WorkerMessageFilter* filter, int route_id);
void RemoveFilters(WorkerMessageFilter* filter);
bool HasFilter(WorkerMessageFilter* filter, int route_id) const;
- bool RendererIsParent(int render_process_id, int render_view_id) const;
+ bool FrameIsParent(int render_process_id, int render_frame_id) const;
int NumFilters() const { return filters_.size(); }
+ void SetMessagePortID(WorkerMessageFilter* filter,
+ int route_id,
+ int message_port_id);
// Returns the single filter (must only be one).
FilterInfo GetFilter() const;
@@ -108,11 +121,14 @@ class WorkerProcessHost : public BrowserChildProcessHostDelegate,
void set_closed(bool closed) { closed_ = closed; }
const GURL& url() const { return url_; }
const base::string16 name() const { return name_; }
- int worker_route_id() const { return worker_route_id_; }
- int parent_process_id() const { return parent_process_id_; }
- int64 main_resource_appcache_id() const {
- return main_resource_appcache_id_;
+ const base::string16 content_security_policy() const {
+ return content_security_policy_;
}
+ blink::WebContentSecurityPolicyType security_policy_type() const {
+ return security_policy_type_;
+ }
+ int worker_route_id() const { return worker_route_id_; }
+ int render_frame_id() const { return render_frame_id_; }
WorkerDocumentSet* worker_document_set() const {
return worker_document_set_.get();
}
@@ -122,19 +138,23 @@ class WorkerProcessHost : public BrowserChildProcessHostDelegate,
const WorkerStoragePartition& partition() const {
return partition_;
}
+ void set_load_failed(bool failed) { load_failed_ = failed; }
+ bool load_failed() { return load_failed_; }
private:
// Set of all filters (clients) associated with this worker.
GURL url_;
bool closed_;
base::string16 name_;
+ base::string16 content_security_policy_;
+ blink::WebContentSecurityPolicyType security_policy_type_;
int worker_route_id_;
- int parent_process_id_;
- int64 main_resource_appcache_id_;
+ int render_frame_id_;
FilterList filters_;
scoped_refptr<WorkerDocumentSet> worker_document_set_;
ResourceContext* const resource_context_;
WorkerStoragePartition partition_;
+ bool load_failed_;
};
WorkerProcessHost(ResourceContext* resource_context,
@@ -145,12 +165,12 @@ class WorkerProcessHost : public BrowserChildProcessHostDelegate,
virtual bool Send(IPC::Message* message) OVERRIDE;
// Starts the process. Returns true iff it succeeded.
- // |render_process_id| is the renderer process responsible for starting this
- // worker.
- bool Init(int render_process_id);
+ // |render_process_id| and |render_frame_id| are the renderer process and the
+ // renderer frame responsible for starting this worker.
+ bool Init(int render_process_id, int render_frame_id);
// Creates a worker object in the process.
- void CreateWorker(const WorkerInstance& instance);
+ void CreateWorker(const WorkerInstance& instance, bool pause_on_start);
// Returns true iff the given message from a renderer process was forwarded to
// the worker.
@@ -194,15 +214,19 @@ class WorkerProcessHost : public BrowserChildProcessHostDelegate,
void CreateMessageFilters(int render_process_id);
void OnWorkerContextClosed(int worker_route_id);
+ void OnWorkerContextDestroyed(int worker_route_id);
+ void OnWorkerScriptLoaded(int worker_route_id);
+ void OnWorkerScriptLoadFailed(int worker_route_id);
+ void OnWorkerConnected(int message_port_id, int worker_route_id);
void OnAllowDatabase(int worker_route_id,
const GURL& url,
const base::string16& name,
const base::string16& display_name,
unsigned long estimated_size,
bool* result);
- void OnAllowFileSystem(int worker_route_id,
- const GURL& url,
- bool* result);
+ void OnRequestFileSystemAccessSync(int worker_route_id,
+ const GURL& url,
+ bool* result);
void OnAllowIndexedDB(int worker_route_id,
const GURL& url,
const base::string16& name,
@@ -212,8 +236,8 @@ class WorkerProcessHost : public BrowserChildProcessHostDelegate,
// Relays a message to the given endpoint. Takes care of parsing the message
// if it contains a message port and sending it a valid route id.
void RelayMessage(const IPC::Message& message,
- WorkerMessageFilter* filter,
- int route_id);
+ WorkerMessageFilter* incoming_filter,
+ WorkerInstance* instance);
void ShutdownSocketStreamDispatcherHostIfNecessary();
@@ -222,9 +246,9 @@ class WorkerProcessHost : public BrowserChildProcessHostDelegate,
// Updates the title shown in the task manager.
void UpdateTitle();
- // Return a vector of all the render process/render view IDs that use the
+ // Return a vector of all the render process/render frame IDs that use the
// given worker.
- std::vector<std::pair<int, int> > GetRenderViewIDsForWorker(int route_id);
+ std::vector<std::pair<int, int> > GetRenderFrameIDsForWorker(int route_id);
// Callbacks for ResourceMessageFilter and SocketStreamDispatcherHost.
void GetContexts(const ResourceHostMsg_Request& request,
diff --git a/chromium/content/browser/worker_host/worker_service_impl.cc b/chromium/content/browser/worker_host/worker_service_impl.cc
index 3ebab469ff7..4636777e1ce 100644
--- a/chromium/content/browser/worker_host/worker_service_impl.cc
+++ b/chromium/content/browser/worker_host/worker_service_impl.cc
@@ -11,6 +11,7 @@
#include "base/threading/thread.h"
#include "content/browser/devtools/worker_devtools_manager.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "content/browser/shared_worker/shared_worker_service_impl.h"
#include "content/browser/worker_host/worker_message_filter.h"
#include "content/browser/worker_host/worker_process_host.h"
#include "content/common/view_messages.h"
@@ -18,6 +19,7 @@
#include "content/public/browser/child_process_data.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
+#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host.h"
@@ -31,8 +33,17 @@
namespace content {
+namespace {
+void AddRenderFrameID(std::set<std::pair<int, int> >* visible_frame_ids,
+ RenderFrameHost* rfh) {
+ visible_frame_ids->insert(
+ std::pair<int, int>(rfh->GetProcess()->GetID(),
+ rfh->GetRoutingID()));
+}
+}
+
const int WorkerServiceImpl::kMaxWorkersWhenSeparate = 64;
-const int WorkerServiceImpl::kMaxWorkersPerTabWhenSeparate = 16;
+const int WorkerServiceImpl::kMaxWorkersPerFrameWhenSeparate = 16;
class WorkerPrioritySetter
: public NotificationObserver,
@@ -64,8 +75,8 @@ class WorkerPrioritySetter
// widgets being shown.
void RegisterObserver();
- // Sets priorities for shared workers given a set of visible tabs (as a
- // std::set of std::pair<render_process, render_view> ids.
+ // Sets priorities for shared workers given a set of visible frames (as a
+ // std::set of std::pair<render_process, render_frame> ids.
void UpdateWorkerPrioritiesFromVisibleSet(
const std::set<std::pair<int, int> >* visible);
@@ -107,7 +118,7 @@ void WorkerPrioritySetter::PostTaskToGatherAndUpdateWorkerPriorities() {
void WorkerPrioritySetter::GatherVisibleIDsAndUpdateWorkerPriorities() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- std::set<std::pair<int, int> >* visible_renderer_ids =
+ std::set<std::pair<int, int> >* visible_frame_ids =
new std::set<std::pair<int, int> >();
// Gather up all the visible renderer process/view pairs
@@ -116,23 +127,28 @@ void WorkerPrioritySetter::GatherVisibleIDsAndUpdateWorkerPriorities() {
while (RenderWidgetHost* widget = widgets->GetNextHost()) {
if (widget->GetProcess()->VisibleWidgetCount() == 0)
continue;
+ if (!widget->IsRenderView())
+ continue;
- RenderWidgetHostView* render_view = widget->GetView();
- if (render_view && render_view->IsShowing()) {
- visible_renderer_ids->insert(
- std::pair<int, int>(widget->GetProcess()->GetID(),
- widget->GetRoutingID()));
- }
+ RenderWidgetHostView* widget_view = widget->GetView();
+ if (!widget_view || !widget_view->IsShowing())
+ continue;
+ RenderViewHost* rvh = RenderViewHost::From(widget);
+ WebContents* web_contents = WebContents::FromRenderViewHost(rvh);
+ if (!web_contents)
+ continue;
+ web_contents->ForEachFrame(
+ base::Bind(&AddRenderFrameID, visible_frame_ids));
}
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&WorkerPrioritySetter::UpdateWorkerPrioritiesFromVisibleSet,
- this, base::Owned(visible_renderer_ids)));
+ this, base::Owned(visible_frame_ids)));
}
void WorkerPrioritySetter::UpdateWorkerPrioritiesFromVisibleSet(
- const std::set<std::pair<int, int> >* visible_renderer_ids) {
+ const std::set<std::pair<int, int> >* visible_frame_ids) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) {
@@ -156,8 +172,8 @@ void WorkerPrioritySetter::UpdateWorkerPrioritiesFromVisibleSet(
for (; info != first_instance->worker_document_set()->documents().end();
++info) {
std::pair<int, int> id(
- info->render_process_id(), info->render_view_id());
- if (visible_renderer_ids->find(id) != visible_renderer_ids->end()) {
+ info->render_process_id(), info->render_frame_id());
+ if (visible_frame_ids->find(id) != visible_frame_ids->end()) {
throttle = false;
break;
}
@@ -175,11 +191,11 @@ void WorkerPrioritySetter::UpdateWorkerPrioritiesFromVisibleSet(
void WorkerPrioritySetter::OnRenderWidgetVisibilityChanged(
std::pair<int, int> id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- std::set<std::pair<int, int> > visible_renderer_ids;
+ std::set<std::pair<int, int> > visible_frame_ids;
- visible_renderer_ids.insert(id);
+ visible_frame_ids.insert(id);
- UpdateWorkerPrioritiesFromVisibleSet(&visible_renderer_ids);
+ UpdateWorkerPrioritiesFromVisibleSet(&visible_frame_ids);
}
void WorkerPrioritySetter::RegisterObserver() {
@@ -213,7 +229,16 @@ void WorkerPrioritySetter::Observe(int type,
}
WorkerService* WorkerService::GetInstance() {
- return WorkerServiceImpl::GetInstance();
+ if (EmbeddedSharedWorkerEnabled())
+ return SharedWorkerServiceImpl::GetInstance();
+ else
+ return WorkerServiceImpl::GetInstance();
+}
+
+bool WorkerService::EmbeddedSharedWorkerEnabled() {
+ static bool disabled = CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableEmbeddedSharedWorker);
+ return !disabled;
}
WorkerServiceImpl* WorkerServiceImpl::GetInstance() {
@@ -253,29 +278,6 @@ void WorkerServiceImpl::OnWorkerMessageFilterClosing(
}
}
- for (WorkerProcessHost::Instances::iterator i =
- pending_shared_workers_.begin();
- i != pending_shared_workers_.end(); ) {
- i->RemoveFilters(filter);
- if (i->NumFilters() == 0) {
- i = pending_shared_workers_.erase(i);
- } else {
- ++i;
- }
- }
-
- // Also, see if that process had any pending shared workers.
- for (WorkerProcessHost::Instances::iterator iter =
- pending_shared_workers_.begin();
- iter != pending_shared_workers_.end(); ) {
- iter->worker_document_set()->RemoveAll(filter);
- if (iter->worker_document_set()->IsEmpty()) {
- iter = pending_shared_workers_.erase(iter);
- } else {
- ++iter;
- }
- }
-
// Either a worker proceess has shut down, in which case we can start one of
// the queued workers, or a renderer has shut down, in which case it doesn't
// affect anything. We call this function in both scenarios because then we
@@ -288,8 +290,38 @@ void WorkerServiceImpl::CreateWorker(
int route_id,
WorkerMessageFilter* filter,
ResourceContext* resource_context,
- const WorkerStoragePartition& partition) {
+ const WorkerStoragePartition& partition,
+ bool* url_mismatch) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ *url_mismatch = false;
+ WorkerProcessHost::WorkerInstance* existing_instance =
+ FindSharedWorkerInstance(
+ params.url, params.name, partition, resource_context);
+ if (existing_instance) {
+ if (params.url != existing_instance->url()) {
+ *url_mismatch = true;
+ return;
+ }
+ if (existing_instance->load_failed()) {
+ filter->Send(new ViewMsg_WorkerScriptLoadFailed(route_id));
+ return;
+ }
+ existing_instance->AddFilter(filter, route_id);
+ existing_instance->worker_document_set()->Add(
+ filter, params.document_id, filter->render_process_id(),
+ params.render_frame_route_id);
+ filter->Send(new ViewMsg_WorkerCreated(route_id));
+ return;
+ }
+ for (WorkerProcessHost::Instances::iterator i = queued_workers_.begin();
+ i != queued_workers_.end(); ++i) {
+ if (i->Matches(params.url, params.name, partition, resource_context) &&
+ params.url != i->url()) {
+ *url_mismatch = true;
+ return;
+ }
+ }
+
// Generate a unique route id for the browser-worker communication that's
// unique among all worker processes. That way when the worker process sends
// a wrapped IPC message through us, we know which WorkerProcessHost to give
@@ -297,63 +329,20 @@ void WorkerServiceImpl::CreateWorker(
WorkerProcessHost::WorkerInstance instance(
params.url,
params.name,
+ params.content_security_policy,
+ params.security_policy_type,
next_worker_route_id(),
- 0,
- params.script_resource_appcache_id,
+ params.render_frame_route_id,
resource_context,
partition);
instance.AddFilter(filter, route_id);
instance.worker_document_set()->Add(
filter, params.document_id, filter->render_process_id(),
- params.render_view_route_id);
+ params.render_frame_route_id);
CreateWorkerFromInstance(instance);
}
-void WorkerServiceImpl::LookupSharedWorker(
- const ViewHostMsg_CreateWorker_Params& params,
- int route_id,
- WorkerMessageFilter* filter,
- ResourceContext* resource_context,
- const WorkerStoragePartition& partition,
- bool* exists,
- bool* url_mismatch) {
- *exists = true;
- WorkerProcessHost::WorkerInstance* instance = FindSharedWorkerInstance(
- params.url, params.name, partition, resource_context);
-
- if (!instance) {
- // If no worker instance currently exists, we need to create a pending
- // instance - this is to make sure that any subsequent lookups passing a
- // mismatched URL get the appropriate url_mismatch error at lookup time.
- // Having named shared workers was a Really Bad Idea due to details like
- // this.
- instance = CreatePendingInstance(params.url, params.name,
- resource_context, partition);
- *exists = false;
- }
-
- // Make sure the passed-in instance matches the URL - if not, return an
- // error.
- if (params.url != instance->url()) {
- *url_mismatch = true;
- *exists = false;
- } else {
- *url_mismatch = false;
- // Add our route ID to the existing instance so we can send messages to it.
- instance->AddFilter(filter, route_id);
-
- // Add the passed filter/document_id to the worker instance.
- // TODO(atwilson): This won't work if the message is from a worker process.
- // We don't support that yet though (this message is only sent from
- // renderers) but when we do, we'll need to add code to pass in the current
- // worker's document set for nested workers.
- instance->worker_document_set()->Add(
- filter, params.document_id, filter->render_process_id(),
- params.render_view_route_id);
- }
-}
-
void WorkerServiceImpl::ForwardToWorker(const IPC::Message& message,
WorkerMessageFilter* filter) {
for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) {
@@ -381,18 +370,6 @@ void WorkerServiceImpl::DocumentDetached(unsigned long long document_id,
}
++iter;
}
-
- // Remove the document from any pending shared workers.
- for (WorkerProcessHost::Instances::iterator iter =
- pending_shared_workers_.begin();
- iter != pending_shared_workers_.end(); ) {
- iter->worker_document_set()->Remove(filter, document_id);
- if (iter->worker_document_set()->IsEmpty()) {
- iter = pending_shared_workers_.erase(iter);
- } else {
- ++iter;
- }
- }
}
bool WorkerServiceImpl::CreateWorkerFromInstance(
@@ -402,54 +379,6 @@ bool WorkerServiceImpl::CreateWorkerFromInstance(
return true;
}
- // Check to see if this shared worker is already running (two pages may have
- // tried to start up the worker simultaneously).
- // See if a worker with this name already exists.
- WorkerProcessHost::WorkerInstance* existing_instance =
- FindSharedWorkerInstance(
- instance.url(), instance.name(), instance.partition(),
- instance.resource_context());
- WorkerProcessHost::WorkerInstance::FilterInfo filter_info =
- instance.GetFilter();
- // If this worker is already running, no need to create a new copy. Just
- // inform the caller that the worker has been created.
- if (existing_instance) {
- // Walk the worker's filter list to see if this client is listed. If not,
- // then it means that the worker started by the client already exited so
- // we should not attach to this new one (http://crbug.com/29243).
- if (!existing_instance->HasFilter(filter_info.first, filter_info.second))
- return false;
- filter_info.first->Send(new ViewMsg_WorkerCreated(filter_info.second));
- return true;
- }
-
- // Look to see if there's a pending instance.
- WorkerProcessHost::WorkerInstance* pending = FindPendingInstance(
- instance.url(), instance.name(), instance.partition(),
- instance.resource_context());
- // If there's no instance *and* no pending instance (or there is a pending
- // instance but it does not contain our filter info), then it means the
- // worker started up and exited already. Log a warning because this should
- // be a very rare occurrence and is probably a bug, but it *can* happen so
- // handle it gracefully.
- if (!pending ||
- !pending->HasFilter(filter_info.first, filter_info.second)) {
- DLOG(WARNING) << "Pending worker already exited";
- return false;
- }
-
- // Assign the accumulated document set and filter list for this pending
- // worker to the new instance.
- DCHECK(!pending->worker_document_set()->IsEmpty());
- instance.ShareDocumentSet(*pending);
- for (WorkerProcessHost::WorkerInstance::FilterList::const_iterator i =
- pending->filters().begin();
- i != pending->filters().end(); ++i) {
- instance.AddFilter(i->first, i->second);
- }
- RemovePendingInstances(instance.url(), instance.name(),
- instance.partition(), instance.resource_context());
-
// Remove any queued instances of this worker and copy over the filter to
// this instance.
for (WorkerProcessHost::Instances::iterator iter = queued_workers_.begin();
@@ -457,33 +386,36 @@ bool WorkerServiceImpl::CreateWorkerFromInstance(
if (iter->Matches(instance.url(), instance.name(),
instance.partition(), instance.resource_context())) {
DCHECK(iter->NumFilters() == 1);
+ DCHECK_EQ(instance.url(), iter->url());
WorkerProcessHost::WorkerInstance::FilterInfo filter_info =
iter->GetFilter();
- instance.AddFilter(filter_info.first, filter_info.second);
+ instance.AddFilter(filter_info.filter(), filter_info.route_id());
iter = queued_workers_.erase(iter);
} else {
++iter;
}
}
- WorkerMessageFilter* first_filter = instance.filters().begin()->first;
+ WorkerMessageFilter* first_filter = instance.filters().begin()->filter();
WorkerProcessHost* worker = new WorkerProcessHost(
instance.resource_context(), instance.partition());
// TODO(atwilson): This won't work if the message is from a worker process.
// We don't support that yet though (this message is only sent from
// renderers) but when we do, we'll need to add code to pass in the current
// worker's document set for nested workers.
- if (!worker->Init(first_filter->render_process_id())) {
+ if (!worker->Init(first_filter->render_process_id(),
+ instance.render_frame_id())) {
delete worker;
return false;
}
- worker->CreateWorker(instance);
+ worker->CreateWorker(
+ instance,
+ WorkerDevToolsManager::GetInstance()->WorkerCreated(worker, instance));
FOR_EACH_OBSERVER(
WorkerServiceObserver, observers_,
WorkerCreated(instance.url(), instance.name(), worker->GetData().id,
instance.worker_route_id()));
- WorkerDevToolsManager::GetInstance()->WorkerCreated(worker, instance);
return true;
}
@@ -497,9 +429,9 @@ bool WorkerServiceImpl::CanCreateWorkerProcess(
parents.begin();
parent_iter != parents.end(); ++parent_iter) {
bool hit_total_worker_limit = false;
- if (TabCanCreateWorkerProcess(parent_iter->render_process_id(),
- parent_iter->render_view_id(),
- &hit_total_worker_limit)) {
+ if (FrameCanCreateWorkerProcess(parent_iter->render_process_id(),
+ parent_iter->render_frame_id(),
+ &hit_total_worker_limit)) {
return true;
}
// Return false if already at the global worker limit (no need to continue
@@ -512,9 +444,9 @@ bool WorkerServiceImpl::CanCreateWorkerProcess(
return false;
}
-bool WorkerServiceImpl::TabCanCreateWorkerProcess(
+bool WorkerServiceImpl::FrameCanCreateWorkerProcess(
int render_process_id,
- int render_view_id,
+ int render_frame_id,
bool* hit_total_worker_limit) {
int total_workers = 0;
int workers_per_tab = 0;
@@ -528,9 +460,9 @@ bool WorkerServiceImpl::TabCanCreateWorkerProcess(
*hit_total_worker_limit = true;
return false;
}
- if (cur_instance->RendererIsParent(render_process_id, render_view_id)) {
+ if (cur_instance->FrameIsParent(render_process_id, render_frame_id)) {
workers_per_tab++;
- if (workers_per_tab >= kMaxWorkersPerTabWhenSeparate)
+ if (workers_per_tab >= kMaxWorkersPerFrameWhenSeparate)
return false;
}
}
@@ -564,7 +496,7 @@ void WorkerServiceImpl::TryStartingQueuedWorker() {
bool WorkerServiceImpl::GetRendererForWorker(int worker_process_id,
int* render_process_id,
- int* render_view_id) const {
+ int* render_frame_id) const {
for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) {
if (iter.GetData().id != worker_process_id)
continue;
@@ -578,7 +510,7 @@ bool WorkerServiceImpl::GetRendererForWorker(int worker_process_id,
WorkerDocumentSet::DocumentInfoSet::const_iterator info =
first_instance->worker_document_set()->documents().begin();
*render_process_id = info->render_process_id();
- *render_view_id = info->render_view_id();
+ *render_frame_id = info->render_frame_id();
return true;
}
return false;
@@ -665,56 +597,4 @@ WorkerProcessHost::WorkerInstance* WorkerServiceImpl::FindSharedWorkerInstance(
return NULL;
}
-WorkerProcessHost::WorkerInstance* WorkerServiceImpl::FindPendingInstance(
- const GURL& url,
- const base::string16& name,
- const WorkerStoragePartition& partition,
- ResourceContext* resource_context) {
- // Walk the pending instances looking for a matching pending worker.
- for (WorkerProcessHost::Instances::iterator iter =
- pending_shared_workers_.begin();
- iter != pending_shared_workers_.end();
- ++iter) {
- if (iter->Matches(url, name, partition, resource_context))
- return &(*iter);
- }
- return NULL;
-}
-
-
-void WorkerServiceImpl::RemovePendingInstances(
- const GURL& url,
- const base::string16& name,
- const WorkerStoragePartition& partition,
- ResourceContext* resource_context) {
- // Walk the pending instances looking for a matching pending worker.
- for (WorkerProcessHost::Instances::iterator iter =
- pending_shared_workers_.begin();
- iter != pending_shared_workers_.end(); ) {
- if (iter->Matches(url, name, partition, resource_context)) {
- iter = pending_shared_workers_.erase(iter);
- } else {
- ++iter;
- }
- }
-}
-
-WorkerProcessHost::WorkerInstance* WorkerServiceImpl::CreatePendingInstance(
- const GURL& url,
- const base::string16& name,
- ResourceContext* resource_context,
- const WorkerStoragePartition& partition) {
- // Look for an existing pending shared worker.
- WorkerProcessHost::WorkerInstance* instance =
- FindPendingInstance(url, name, partition, resource_context);
- if (instance)
- return instance;
-
- // No existing pending worker - create a new one.
- WorkerProcessHost::WorkerInstance pending(
- url, true, name, resource_context, partition);
- pending_shared_workers_.push_back(pending);
- return &pending_shared_workers_.back();
-}
-
} // namespace content
diff --git a/chromium/content/browser/worker_host/worker_service_impl.h b/chromium/content/browser/worker_host/worker_service_impl.h
index 4d5127aec25..133c0a38e1a 100644
--- a/chromium/content/browser/worker_host/worker_service_impl.h
+++ b/chromium/content/browser/worker_host/worker_service_impl.h
@@ -44,14 +44,8 @@ class CONTENT_EXPORT WorkerServiceImpl
int route_id,
WorkerMessageFilter* filter,
ResourceContext* resource_context,
- const WorkerStoragePartition& worker_partition);
- void LookupSharedWorker(const ViewHostMsg_CreateWorker_Params& params,
- int route_id,
- WorkerMessageFilter* filter,
- ResourceContext* resource_context,
- const WorkerStoragePartition& worker_partition,
- bool* exists,
- bool* url_error);
+ const WorkerStoragePartition& worker_partition,
+ bool* url_mismatch);
void ForwardToWorker(const IPC::Message& message,
WorkerMessageFilter* filter);
void DocumentDetached(unsigned long long document_id,
@@ -62,13 +56,13 @@ class CONTENT_EXPORT WorkerServiceImpl
int next_worker_route_id() { return ++next_worker_route_id_; }
// Given a worker's process id, return the IDs of the renderer process and
- // render view that created it. For shared workers, this returns the first
+ // render frame that created it. For shared workers, this returns the first
// parent.
// TODO(dimich): This code assumes there is 1 worker per worker process, which
// is how it is today until V8 can run in separate threads.
bool GetRendererForWorker(int worker_process_id,
int* render_process_id,
- int* render_view_id) const;
+ int* render_frame_id) const;
const WorkerProcessHost::WorkerInstance* FindWorkerInstance(
int worker_process_id);
@@ -80,7 +74,7 @@ class CONTENT_EXPORT WorkerServiceImpl
// Used when we run each worker in a separate process.
static const int kMaxWorkersWhenSeparate;
- static const int kMaxWorkersPerTabWhenSeparate;
+ static const int kMaxWorkersPerFrameWhenSeparate;
private:
friend struct DefaultSingletonTraits<WorkerServiceImpl>;
@@ -96,32 +90,15 @@ class CONTENT_EXPORT WorkerServiceImpl
bool CanCreateWorkerProcess(
const WorkerProcessHost::WorkerInstance& instance);
- // Checks if the tab associated with the passed RenderView can create a
+ // Checks if the frame associated with the passed RenderFrame can create a
// worker process based on the process limit when we're using a strategy of
// one worker per process.
- bool TabCanCreateWorkerProcess(
- int render_process_id, int render_route_id, bool* hit_total_worker_limit);
+ bool FrameCanCreateWorkerProcess(
+ int render_process_id, int render_frame_id, bool* hit_total_worker_limit);
// Tries to see if any of the queued workers can be created.
void TryStartingQueuedWorker();
- // APIs for manipulating our set of pending shared worker instances.
- WorkerProcessHost::WorkerInstance* CreatePendingInstance(
- const GURL& url,
- const base::string16& name,
- ResourceContext* resource_context,
- const WorkerStoragePartition& worker_partition);
- WorkerProcessHost::WorkerInstance* FindPendingInstance(
- const GURL& url,
- const base::string16& name,
- const WorkerStoragePartition& worker_partition,
- ResourceContext* resource_context);
- void RemovePendingInstances(
- const GURL& url,
- const base::string16& name,
- const WorkerStoragePartition& worker_partition,
- ResourceContext* resource_context);
-
WorkerProcessHost::WorkerInstance* FindSharedWorkerInstance(
const GURL& url,
const base::string16& name,
@@ -134,11 +111,6 @@ class CONTENT_EXPORT WorkerServiceImpl
WorkerProcessHost::Instances queued_workers_;
- // These are shared workers that have been looked up, but not created yet.
- // We need to keep a list of these to synchronously detect shared worker
- // URL mismatches when two pages launch shared workers simultaneously.
- WorkerProcessHost::Instances pending_shared_workers_;
-
ObserverList<WorkerServiceObserver> observers_;
DISALLOW_COPY_AND_ASSIGN(WorkerServiceImpl);
diff --git a/chromium/content/browser/worker_host/worker_storage_partition.cc b/chromium/content/browser/worker_host/worker_storage_partition.cc
index 2c9105721f3..e7a5f40660c 100644
--- a/chromium/content/browser/worker_host/worker_storage_partition.cc
+++ b/chromium/content/browser/worker_host/worker_storage_partition.cc
@@ -8,6 +8,7 @@
#include "content/browser/appcache/chrome_appcache_service.h"
#include "content/browser/indexed_db/indexed_db_context_impl.h"
+#include "content/browser/service_worker/service_worker_context_wrapper.h"
#include "net/url_request/url_request_context_getter.h"
#include "webkit/browser/database/database_tracker.h"
#include "webkit/browser/fileapi/file_system_context.h"
@@ -22,14 +23,16 @@ WorkerStoragePartition::WorkerStoragePartition(
quota::QuotaManager* quota_manager,
fileapi::FileSystemContext* filesystem_context,
webkit_database::DatabaseTracker* database_tracker,
- IndexedDBContextImpl* indexed_db_context)
+ IndexedDBContextImpl* indexed_db_context,
+ ServiceWorkerContextWrapper* service_worker_context)
: url_request_context_(url_request_context),
media_url_request_context_(media_url_request_context),
appcache_service_(appcache_service),
quota_manager_(quota_manager),
filesystem_context_(filesystem_context),
database_tracker_(database_tracker),
- indexed_db_context_(indexed_db_context) {
+ indexed_db_context_(indexed_db_context),
+ service_worker_context_(service_worker_context) {
}
WorkerStoragePartition::WorkerStoragePartition(
@@ -52,7 +55,8 @@ bool WorkerStoragePartition::Equals(
quota_manager_.get() == other.quota_manager_.get() &&
filesystem_context_.get() == other.filesystem_context_.get() &&
database_tracker_.get() == other.database_tracker_.get() &&
- indexed_db_context_.get() == other.indexed_db_context_.get();
+ indexed_db_context_.get() == other.indexed_db_context_.get() &&
+ service_worker_context_.get() == other.service_worker_context_.get();
}
WorkerStoragePartition::~WorkerStoragePartition() {
@@ -66,6 +70,34 @@ void WorkerStoragePartition::Copy(const WorkerStoragePartition& other) {
filesystem_context_ = other.filesystem_context_;
database_tracker_ = other.database_tracker_;
indexed_db_context_ = other.indexed_db_context_;
+ service_worker_context_ = other.service_worker_context_;
+}
+
+WorkerStoragePartitionId::WorkerStoragePartitionId(
+ const WorkerStoragePartition& partition)
+ : url_request_context_(partition.url_request_context()),
+ media_url_request_context_(partition.media_url_request_context()),
+ appcache_service_(partition.appcache_service()),
+ quota_manager_(partition.quota_manager()),
+ filesystem_context_(partition.filesystem_context()),
+ database_tracker_(partition.database_tracker()),
+ indexed_db_context_(partition.indexed_db_context()),
+ service_worker_context_(partition.service_worker_context()) {
+}
+
+WorkerStoragePartitionId::~WorkerStoragePartitionId() {
+}
+
+bool WorkerStoragePartitionId::Equals(
+ const WorkerStoragePartitionId& other) const {
+ return url_request_context_ == other.url_request_context_ &&
+ media_url_request_context_ == other.media_url_request_context_ &&
+ appcache_service_ == other.appcache_service_ &&
+ quota_manager_ == other.quota_manager_ &&
+ filesystem_context_ == other.filesystem_context_ &&
+ database_tracker_ == other.database_tracker_ &&
+ indexed_db_context_ == other.indexed_db_context_ &&
+ service_worker_context_ == other.service_worker_context_;
}
} // namespace content
diff --git a/chromium/content/browser/worker_host/worker_storage_partition.h b/chromium/content/browser/worker_host/worker_storage_partition.h
index 49c2deae248..aebd683c3f5 100644
--- a/chromium/content/browser/worker_host/worker_storage_partition.h
+++ b/chromium/content/browser/worker_host/worker_storage_partition.h
@@ -6,6 +6,7 @@
#define CONTENT_BROWSER_WORKER_HOST_WORKER_STORAGE_PARTITION_H_
#include "base/memory/ref_counted.h"
+#include "content/common/content_export.h"
namespace quota {
class QuotaManager;
@@ -26,6 +27,7 @@ class DatabaseTracker;
namespace content {
class ChromeAppCacheService;
class IndexedDBContextImpl;
+class ServiceWorkerContextWrapper;
// Contains the data from StoragePartition for use by Worker APIs.
//
@@ -39,7 +41,7 @@ class IndexedDBContextImpl;
// This class is effectively a struct, but we make it a class because we want to
// define copy constructors, assignment operators, and an Equals() function for
// it which makes it look awkward as a struct.
-class WorkerStoragePartition {
+class CONTENT_EXPORT WorkerStoragePartition {
public:
WorkerStoragePartition(
net::URLRequestContextGetter* url_request_context,
@@ -48,7 +50,8 @@ class WorkerStoragePartition {
quota::QuotaManager* quota_manager,
fileapi::FileSystemContext* filesystem_context,
webkit_database::DatabaseTracker* database_tracker,
- IndexedDBContextImpl* indexed_db_context);
+ IndexedDBContextImpl* indexed_db_context,
+ ServiceWorkerContextWrapper* service_worker_context);
~WorkerStoragePartition();
// Declaring so these don't get inlined which has the unfortunate effect of
@@ -87,6 +90,10 @@ class WorkerStoragePartition {
return indexed_db_context_.get();
}
+ ServiceWorkerContextWrapper* service_worker_context() const {
+ return service_worker_context_.get();
+ }
+
private:
void Copy(const WorkerStoragePartition& other);
@@ -97,6 +104,29 @@ class WorkerStoragePartition {
scoped_refptr<fileapi::FileSystemContext> filesystem_context_;
scoped_refptr<webkit_database::DatabaseTracker> database_tracker_;
scoped_refptr<IndexedDBContextImpl> indexed_db_context_;
+ scoped_refptr<ServiceWorkerContextWrapper> service_worker_context_;
+};
+
+// WorkerStoragePartitionId can be used to identify each
+// WorkerStoragePartitions. We can hold WorkerStoragePartitionId without
+// extending the lifetime of all objects in the WorkerStoragePartition.
+// That means that holding a WorkerStoragePartitionId doesn't mean the
+// corresponding partition and its members are kept alive.
+class CONTENT_EXPORT WorkerStoragePartitionId {
+ public:
+ explicit WorkerStoragePartitionId(const WorkerStoragePartition& partition);
+ ~WorkerStoragePartitionId();
+ bool Equals(const WorkerStoragePartitionId& other) const;
+
+ private:
+ net::URLRequestContextGetter* url_request_context_;
+ net::URLRequestContextGetter* media_url_request_context_;
+ ChromeAppCacheService* appcache_service_;
+ quota::QuotaManager* quota_manager_;
+ fileapi::FileSystemContext* filesystem_context_;
+ webkit_database::DatabaseTracker* database_tracker_;
+ IndexedDBContextImpl* indexed_db_context_;
+ ServiceWorkerContextWrapper* service_worker_context_;
};
} // namespace content
diff --git a/chromium/content/browser/zygote_host/zygote_host_impl_linux.cc b/chromium/content/browser/zygote_host/zygote_host_impl_linux.cc
index 18e9b693094..81d075688d7 100644
--- a/chromium/content/browser/zygote_host/zygote_host_impl_linux.cc
+++ b/chromium/content/browser/zygote_host/zygote_host_impl_linux.cc
@@ -4,6 +4,7 @@
#include "content/browser/zygote_host/zygote_host_impl_linux.h"
+#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -14,16 +15,19 @@
#include "base/environment.h"
#include "base/file_util.h"
#include "base/files/file_enumerator.h"
+#include "base/files/scoped_file.h"
#include "base/linux_util.h"
#include "base/logging.h"
#include "base/memory/linked_ptr.h"
#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
#include "base/metrics/histogram.h"
#include "base/path_service.h"
#include "base/posix/eintr_wrapper.h"
#include "base/posix/unix_domain_socket_linux.h"
#include "base/process/launch.h"
#include "base/process/memory.h"
+#include "base/process/process_handle.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
@@ -45,6 +49,26 @@
namespace content {
+// Receive a fixed message on fd and return the sender's PID.
+// Returns true if the message received matches the expected message.
+static bool ReceiveFixedMessage(int fd,
+ const char* expect_msg,
+ size_t expect_len,
+ base::ProcessId* sender_pid) {
+ char buf[expect_len + 1];
+ ScopedVector<base::ScopedFD> fds_vec;
+
+ const ssize_t len = UnixDomainSocket::RecvMsgWithPid(
+ fd, buf, sizeof(buf), &fds_vec, sender_pid);
+ if (static_cast<size_t>(len) != expect_len)
+ return false;
+ if (memcmp(buf, expect_msg, expect_len) != 0)
+ return false;
+ if (!fds_vec.empty())
+ return false;
+ return true;
+}
+
// static
ZygoteHost* ZygoteHost::GetInstance() {
return ZygoteHostImpl::GetInstance();
@@ -81,17 +105,12 @@ void ZygoteHostImpl::Init(const std::string& sandbox_cmd) {
cmd_line.AppendSwitchASCII(switches::kProcessType, switches::kZygoteProcess);
int fds[2];
-#if defined(OS_FREEBSD) || defined(OS_OPENBSD)
- // The BSDs often don't support SOCK_SEQPACKET yet, so fall back to
- // SOCK_DGRAM if necessary.
- if (socketpair(PF_UNIX, SOCK_SEQPACKET, 0, fds) != 0)
- CHECK(socketpair(PF_UNIX, SOCK_DGRAM, 0, fds) == 0);
-#else
- CHECK(socketpair(PF_UNIX, SOCK_SEQPACKET, 0, fds) == 0);
-#endif
+ CHECK(socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds) == 0);
+ CHECK(UnixDomainSocket::EnableReceiveProcessId(fds[0]));
base::FileHandleMappingVector fds_to_map;
fds_to_map.push_back(std::make_pair(fds[1], kZygoteSocketPairFd));
+ base::LaunchOptions options;
const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
if (browser_command_line.HasSwitch(switches::kZygoteCmdPrefix)) {
cmd_line.PrependWrapper(
@@ -112,7 +131,6 @@ void ZygoteHostImpl::Init(const std::string& sandbox_cmd) {
// Zygote process needs to know what resources to have loaded when it
// becomes a renderer process.
switches::kForceDeviceScaleFactor,
- switches::kTouchOptimizedUI,
switches::kNoSandbox,
};
@@ -126,79 +144,49 @@ void ZygoteHostImpl::Init(const std::string& sandbox_cmd) {
// A non empty sandbox_cmd means we want a SUID sandbox.
using_suid_sandbox_ = !sandbox_cmd.empty();
- if (using_suid_sandbox_) {
- struct stat st;
- if (stat(sandbox_binary_.c_str(), &st) != 0) {
- LOG(FATAL) << "The SUID sandbox helper binary is missing: "
- << sandbox_binary_ << " Aborting now.";
- }
-
- if (access(sandbox_binary_.c_str(), X_OK) == 0 &&
- (st.st_uid == 0) &&
- (st.st_mode & S_ISUID) &&
- (st.st_mode & S_IXOTH)) {
- cmd_line.PrependWrapper(sandbox_binary_);
-
- scoped_ptr<sandbox::SetuidSandboxClient>
- sandbox_client(sandbox::SetuidSandboxClient::Create());
- sandbox_client->SetupLaunchEnvironment();
- } else {
- LOG(FATAL) << "The SUID sandbox helper binary was found, but is not "
- "configured correctly. Rather than run without sandboxing "
- "I'm aborting now. You need to make sure that "
- << sandbox_binary_ << " is owned by root and has mode 4755.";
- }
- }
-
// Start up the sandbox host process and get the file descriptor for the
// renderers to talk to it.
const int sfd = RenderSandboxHostLinux::GetInstance()->GetRendererSocket();
fds_to_map.push_back(std::make_pair(sfd, GetSandboxFD()));
- int dummy_fd = -1;
+ base::ScopedFD dummy_fd;
if (using_suid_sandbox_) {
- dummy_fd = socket(PF_UNIX, SOCK_DGRAM, 0);
- CHECK(dummy_fd >= 0);
- fds_to_map.push_back(std::make_pair(dummy_fd, kZygoteIdFd));
+ scoped_ptr<sandbox::SetuidSandboxClient>
+ sandbox_client(sandbox::SetuidSandboxClient::Create());
+ sandbox_client->PrependWrapper(&cmd_line);
+ sandbox_client->SetupLaunchOptions(&options, &fds_to_map, &dummy_fd);
+ sandbox_client->SetupLaunchEnvironment();
}
base::ProcessHandle process = -1;
- base::LaunchOptions options;
options.fds_to_remap = &fds_to_map;
base::LaunchProcess(cmd_line.argv(), options, &process);
CHECK(process != -1) << "Failed to launch zygote process";
+ dummy_fd.reset();
if (using_suid_sandbox_) {
- // In the SUID sandbox, the real zygote is forked from the sandbox.
- // We need to look for it.
- // But first, wait for the zygote to tell us it's running.
- // The sending code is in content/browser/zygote_main_linux.cc.
- std::vector<int> fds_vec;
- const int kExpectedLength = sizeof(kZygoteHelloMessage);
- char buf[kExpectedLength];
- const ssize_t len = UnixDomainSocket::RecvMsg(fds[0], buf, sizeof(buf),
- &fds_vec);
- CHECK(len == kExpectedLength) << "Incorrect zygote magic length";
- CHECK(0 == strcmp(buf, kZygoteHelloMessage))
- << "Incorrect zygote hello";
-
- std::string inode_output;
- ino_t inode = 0;
- // Figure out the inode for |dummy_fd|, close |dummy_fd| on our end,
- // and find the zygote process holding |dummy_fd|.
- if (base::FileDescriptorGetInode(&inode, dummy_fd)) {
- close(dummy_fd);
- std::vector<std::string> get_inode_cmdline;
- get_inode_cmdline.push_back(sandbox_binary_);
- get_inode_cmdline.push_back(base::kFindInodeSwitch);
- get_inode_cmdline.push_back(base::Int64ToString(inode));
- CommandLine get_inode_cmd(get_inode_cmdline);
- if (base::GetAppOutput(get_inode_cmd, &inode_output)) {
- base::StringToInt(inode_output, &pid_);
- }
- }
- CHECK(pid_ > 0) << "Did not find zygote process (using sandbox binary "
- << sandbox_binary_ << ")";
+ // The SUID sandbox will execute the zygote in a new PID namespace, and
+ // the main zygote process will then fork from there. Watch now our
+ // elaborate dance to find and validate the zygote's PID.
+
+ // First we receive a message from the zygote boot process.
+ base::ProcessId boot_pid;
+ CHECK(ReceiveFixedMessage(
+ fds[0], kZygoteBootMessage, sizeof(kZygoteBootMessage), &boot_pid));
+
+ // Within the PID namespace, the zygote boot process thinks it's PID 1,
+ // but its real PID can never be 1. This gives us a reliable test that
+ // the kernel is translating the sender's PID to our namespace.
+ CHECK_GT(boot_pid, 1)
+ << "Received invalid process ID for zygote; kernel might be too old? "
+ "See crbug.com/357670 or try using --"
+ << switches::kDisableSetuidSandbox << " to workaround.";
+
+ // Now receive the message that the zygote's ready to go, along with the
+ // main zygote process's ID.
+ CHECK(ReceiveFixedMessage(
+ fds[0], kZygoteHelloMessage, sizeof(kZygoteHelloMessage), &pid_));
+ CHECK_GT(pid_, 1);
if (process != pid_) {
// Reap the sandbox.
@@ -306,6 +294,12 @@ pid_t ZygoteHostImpl::ForkRequest(
DCHECK(init_);
Pickle pickle;
+ int raw_socks[2];
+ PCHECK(0 == socketpair(AF_UNIX, SOCK_SEQPACKET, 0, raw_socks));
+ base::ScopedFD my_sock(raw_socks[0]);
+ base::ScopedFD peer_sock(raw_socks[1]);
+ CHECK(UnixDomainSocket::EnableReceiveProcessId(my_sock.get()));
+
pickle.WriteInt(kZygoteCommandFork);
pickle.WriteString(process_type);
pickle.WriteInt(argv.size());
@@ -313,12 +307,19 @@ pid_t ZygoteHostImpl::ForkRequest(
i = argv.begin(); i != argv.end(); ++i)
pickle.WriteString(*i);
- pickle.WriteInt(mapping.size());
+ // Fork requests contain one file descriptor for the PID oracle, and one
+ // more for each file descriptor mapping for the child process.
+ const size_t num_fds_to_send = 1 + mapping.size();
+ pickle.WriteInt(num_fds_to_send);
std::vector<int> fds;
- // Scoped pointers cannot be stored in containers, so we have to use a
- // linked_ptr.
- std::vector<linked_ptr<file_util::ScopedFD> > autodelete_fds;
+ ScopedVector<base::ScopedFD> autoclose_fds;
+
+ // First FD to send is peer_sock.
+ fds.push_back(peer_sock.get());
+ autoclose_fds.push_back(new base::ScopedFD(peer_sock.Pass()));
+
+ // The rest come from mapping.
for (std::vector<FileDescriptorInfo>::const_iterator
i = mapping.begin(); i != mapping.end(); ++i) {
pickle.WriteUInt32(i->id);
@@ -326,17 +327,46 @@ pid_t ZygoteHostImpl::ForkRequest(
if (i->fd.auto_close) {
// Auto-close means we need to close the FDs after they have been passed
// to the other process.
- linked_ptr<file_util::ScopedFD> ptr(
- new file_util::ScopedFD(&(fds.back())));
- autodelete_fds.push_back(ptr);
+ autoclose_fds.push_back(new base::ScopedFD(i->fd.fd));
}
}
+ // Sanity check that we've populated |fds| correctly.
+ DCHECK_EQ(num_fds_to_send, fds.size());
+
pid_t pid;
{
base::AutoLock lock(control_lock_);
if (!SendMessage(pickle, &fds))
return base::kNullProcessHandle;
+ autoclose_fds.clear();
+
+ {
+ char buf[sizeof(kZygoteChildPingMessage) + 1];
+ ScopedVector<base::ScopedFD> recv_fds;
+ base::ProcessId real_pid;
+
+ ssize_t n = UnixDomainSocket::RecvMsgWithPid(
+ my_sock.get(), buf, sizeof(buf), &recv_fds, &real_pid);
+ if (n != sizeof(kZygoteChildPingMessage) ||
+ 0 != memcmp(buf,
+ kZygoteChildPingMessage,
+ sizeof(kZygoteChildPingMessage))) {
+ // Zygote children should still be trustworthy when they're supposed to
+ // ping us, so something's broken if we don't receive a valid ping.
+ LOG(ERROR) << "Did not receive ping from zygote child";
+ NOTREACHED();
+ real_pid = -1;
+ }
+ my_sock.reset();
+
+ // Always send PID back to zygote.
+ Pickle pid_pickle;
+ pid_pickle.WriteInt(kZygoteCommandForkRealPID);
+ pid_pickle.WriteInt(real_pid);
+ if (!SendMessage(pid_pickle, NULL))
+ return base::kNullProcessHandle;
+ }
// Read the reply, which pickles the PID and an optional UMA enumeration.
static const unsigned kMaxReplyLength = 2048;
@@ -448,7 +478,12 @@ void ZygoteHostImpl::AdjustRendererOOMScore(base::ProcessHandle pid,
adj_oom_score_cmdline.push_back(base::IntToString(score));
base::ProcessHandle sandbox_helper_process;
- if (base::LaunchProcess(adj_oom_score_cmdline, base::LaunchOptions(),
+ base::LaunchOptions options;
+
+ // sandbox_helper_process is a setuid binary.
+ options.allow_new_privs = true;
+
+ if (base::LaunchProcess(adj_oom_score_cmdline, options,
&sandbox_helper_process)) {
base::EnsureProcessGetsReaped(sandbox_helper_process);
}
@@ -524,10 +559,6 @@ pid_t ZygoteHostImpl::GetPid() const {
return pid_;
}
-pid_t ZygoteHostImpl::GetSandboxHelperPid() const {
- return RenderSandboxHostLinux::GetInstance()->pid();
-}
-
int ZygoteHostImpl::GetSandboxStatus() const {
if (have_read_sandbox_status_word_)
return sandbox_status_;
diff --git a/chromium/content/browser/zygote_host/zygote_host_impl_linux.h b/chromium/content/browser/zygote_host/zygote_host_impl_linux.h
index 08044fa5a88..c4435469849 100644
--- a/chromium/content/browser/zygote_host/zygote_host_impl_linux.h
+++ b/chromium/content/browser/zygote_host/zygote_host_impl_linux.h
@@ -55,7 +55,6 @@ class CONTENT_EXPORT ZygoteHostImpl : public ZygoteHost {
// ZygoteHost implementation:
virtual pid_t GetPid() const OVERRIDE;
- virtual pid_t GetSandboxHelperPid() const OVERRIDE;
virtual int GetSandboxStatus() const OVERRIDE;
virtual void AdjustRendererOOMScore(base::ProcessHandle process_handle,
int score) OVERRIDE;